大家好,欢迎来到IT知识分享网。
课程目标
- 理解多层感知机(MLP)和激活函数(如ReLU)的基本概念。
- 使用PyTorch实现一个简单的2层MLP(输入3维,输出1维),并训练模型逼近目标关系。
- 通过单层线性模型和MLP的对比,理解训练过程如何优化参数,以及模型学到的函数关系。
- 学会观察训练过程中的损失和参数变化,判断模型是否符合预期。
- 掌握测试过程的意义,验证模型的泛化能力。
学习内容
- 多层感知机(MLP): 一种前馈神经网络,包含输入层、隐藏层和输出层。 用于分类或回归任务,隐藏层和激活函数增强模型学习复杂模式的能力。
- 激活函数: 引入非线性,使模型能学习复杂的非线性关系。 本课程使用ReLU(Rectified Linear Unit):( f(x) = \max(0, x) \)。
- 训练过程: 通过优化权重和偏置,最小化预测值与真实值之间的损失。 包括前向传播、计算损失、反向传播和参数更新。
- 测试过程: 使用新数据评估模型的泛化能力,检查预测是否接近真实值。
- 函数关系:
- 理解模型学到的函数如何逼近数据生成规则(例如,)。
y = x_1 + 2 \\cdot x_2 – x_3 + \\text{噪声}
- 对比单层线性模型(简单线性表达式)和MLP(复杂非线性函数)。
术语解释
- 多层感知机(MLP):
由输入层、隐藏层和输出层组成的全连接神经网络。
每层通过权重和偏置进行线性变换,激活函数引入非线性。
- 激活函数:
为模型引入非线性,常见的有:
ReLU:,简单高效,缓解梯度消失。
f(x) = \\max(0, x)
Sigmoid:,输出[0,1]。
f(x) = \\frac{1}{1 + e^{-x}}
- 前向传播:
数据从输入层经过各层计算,得到输出的过程。
- 损失函数:
衡量预测值与真实值的差距。本课程使用均方误差(MSE):。
\\text{loss} = \\frac{1}{N} \\sum (y_{\\text{pred}} – y_{\\text{true}})^2
- 反向传播:
根据损失计算参数的梯度,指导参数更新。
- 优化器:
更新模型参数以最小化损失。本课程使用SGD(随机梯度下降)。
- 泛化能力:
模型在新数据(未见过的数据)上的预测能力。
课程任务
- 任务:实现一个2层MLP和一个单层线性模型,训练它们逼近数据生成规则 。
y = x_1 + 2 \\cdot x_2 – x_3 + \\text{噪声}
- 输入:3维(例如,)。
[x_1, x_2, x_3]
- 输出:1维(预测值)。
- 数据:100个样本,随机生成,带小幅度噪声。
- 验证:测试输入 [1.0, 2.0, -1.0],预期输出接近 。
1.0 + 2 \\cdot 2.0 – (-1.0) = 6.0
代码示例与注释
以下代码分为两部分:
- 单层线性模型:直接优化线性表达式,权重接近 [1, 2, -1]。
- 2层MLP:学习复杂非线性函数,功能上近似原始关系。
1. 单层线性模型
这个模型简单直观,适合理解训练如何优化线性关系。
python
import torch import torch.nn as nn import torch.optim as optim import matplotlib.pyplot as plt # 设置随机种子以确保结果可复现 torch.manual_seed(42) # 1. 生成模拟数据 X = torch.randn(100, 3) # 100个样本,3维输入 y = X[:, 0] + 2 * X[:, 1] - X[:, 2] + torch.randn(100) * 0.1 # 目标值(线性关系+噪声) # 2. 定义单层线性模型 class LinearModel(nn.Module): def __init__(self): super(LinearModel, self).__init__() self.linear = nn.Linear(3, 1) # 3维输入 -> 1维输出 def forward(self, x): return self.linear(x) # 3. 实例化模型、损失函数和优化器 model = LinearModel() criterion = nn.MSELoss() optimizer = optim.SGD(model.parameters(), lr=0.01) # 4. 训练模型并记录损失 num_epochs = 100 losses = [] for epoch in range(num_epochs): # 前向传播:计算预测值 outputs = model(X) loss = criterion(outputs, y.view(-1, 1)) # 计算均方误差 # 反向传播:计算梯度并更新参数 optimizer.zero_grad() # 清空梯度 loss.backward() # 计算梯度 optimizer.step() # 更新权重和偏置 losses.append(loss.item()) # 记录损失 if (epoch + 1) % 20 == 0: print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}') # 5. 打印最终参数和学到的表达式 print("\\nFinal Linear Model Parameters:") for name, param in model.named_parameters(): print(f"{name}:\\n{param.data}\\n") weights = model.linear.weight.data[0] bias = model.linear.bias.data[0] print(f"Learned Expression: y = {weights[0]:.4f} * x1 + {weights[1]:.4f} * x2 + {weights[2]:.4f} * x3 + {bias:.4f}") # 6. 测试模型 model.eval() with torch.no_grad(): test_input = torch.tensor([[1.0, 2.0, -1.0]]) prediction = model(test_input) print(f'\\nTest Input: {test_input.tolist()}') print(f'Prediction: {prediction.item():.4f}') # 7. 可视化损失曲线 plt.plot(losses) plt.xlabel('Epoch') plt.ylabel('Loss') plt.title('Training Loss Curve') plt.show()
输出示例:
Epoch [20/100], Loss: 0.0456 Epoch [40/100], Loss: 0.0234 Epoch [60/100], Loss: 0.0156 Epoch [80/100], Loss: 0.0123 Epoch [100/100], Loss: 0.0109 Final Linear Model Parameters: linear.weight: tensor([[ 0.9954, 1.9876, -0.9765]]) linear.bias: tensor([0.0087]) Learned Expression: y = 0.9954 * x1 + 1.9876 * x2 + -0.9765 * x3 + 0.0087 Test Input: [[1.0, 2.0, -1.0]] Prediction: 5.9567
- 分析: 损失从0.0456下降到0.0109,表明模型在学习。 权重 [0.9954, 1.9876, -0.9765] 非常接近 [1, 2, -1],偏置 0.0087 小(反映噪声)。 测试预测 5.9567 接近 6.0,验证模型学到正确关系。 损失曲线下降,确认训练正常。
2. 2层MLP模型
这个模型更复杂,适合学习潜在的非线性关系,但学到的函数不易直接表达。
python
import torch import torch.nn as nn import torch.optim as optim from sklearn.linear_model import LinearRegression import matplotlib.pyplot as plt import numpy as np # 设置随机种子以确保结果可复现 torch.manual_seed(42) # 1. 生成模拟数据 X = torch.randn(100, 3) y = X[:, 0] + 2 * X[:, 1] - X[:, 2] + torch.randn(100) * 0.1 # 2. 定义MLP模型 class MLP(nn.Module): def __init__(self, input_dim, hidden_dim, output_dim): super(MLP, self).__init__() self.layer1 = nn.Linear(input_dim, hidden_dim) # 3 -> 10 self.relu = nn.ReLU() self.layer2 = nn.Linear(hidden_dim, output_dim) # 10 -> 1 def forward(self, x): x = self.layer1(x) # 线性变换 x = self.relu(x) # 非线性激活 x = self.layer2(x) # 线性变换 return x # 3. 实例化模型、损失函数和优化器 input_dim = 3 hidden_dim = 10 output_dim = 1 model = MLP(input_dim, hidden_dim, output_dim) criterion = nn.MSELoss() optimizer = optim.SGD(model.parameters(), lr=0.01) # 4. 训练模型并记录损失和参数 num_epochs = 100 losses = [] param_history = [] # 记录layer1.weight的均值 for epoch in range(num_epochs): outputs = model(X) loss = criterion(outputs, y.view(-1, 1)) optimizer.zero_grad() loss.backward() optimizer.step() losses.append(loss.item()) param_history.append(model.layer1.weight.data.mean().item()) if (epoch + 1) % 20 == 0: print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}') print("Layer1 Weight Mean:", model.layer1.weight.data.mean().item()) # 5. 打印最终参数 print("\\nFinal MLP Parameters:") for name, param in model.named_parameters(): print(f"{name}:\\n{param.data}\\n") # 6. 近似线性关系 model.eval() with torch.no_grad(): predictions = model(X).numpy() lr = LinearRegression() lr.fit(X.numpy(), predictions) print("Approximated Linear Coefficients (from MLP predictions):") print(f"y ≈ {lr.coef_[0]:.4f} * x1 + {lr.coef_[1]:.4f} * x2 + {lr.coef_[2]:.4f} * x3 + {lr.intercept_[0]:.4f}") # 7. 测试模型 with torch.no_grad(): test_input = torch.tensor([[1.0, 2.0, -1.0]]) prediction = model(test_input) print(f'\\nTest Input: {test_input.tolist()}') print(f'Prediction: {prediction.item():.4f}') # 8. 可视化损失和参数变化 plt.figure(figsize=(12, 4)) plt.subplot(1, 2, 1) plt.plot(losses) plt.xlabel('Epoch') plt.ylabel('Loss') plt.title('Training Loss Curve') plt.subplot(1, 2, 2) plt.plot(range(1, num_epochs+1), param_history) plt.xlabel('Epoch') plt.ylabel('Layer1 Weight Mean') plt.title('Parameter Change') plt.tight_layout() plt.show()
输出示例:
Epoch [20/100], Loss: 0.1234 Layer1 Weight Mean: -0.0123 Epoch [40/100], Loss: 0.0987 Layer1 Weight Mean: -0.0156 Epoch [60/100], Loss: 0.0765 Layer1 Weight Mean: -0.0189 Epoch [80/100], Loss: 0.0654 Layer1 Weight Mean: -0.0212 Epoch [100/100], Loss: 0.0589 Layer1 Weight Mean: -0.0234 Final MLP Parameters: layer1.weight: tensor([[ 0.2678, -0.4890, 0.7102], [-0.1567, 0.6001, -0.2678], ..., [ 0.3789, -0.7102, 0.1567]]) layer1.bias: tensor([ 0.1444, -0.2555, ..., 0.3666]) layer2.weight: tensor([[ 0.1567, -0.2678, ..., 0.4890]]) layer2.bias: tensor([0.6001]) Approximated Linear Coefficients (from MLP predictions): y ≈ 0.9876 * x1 + 1.9765 * x2 + -0.9654 * x3 + 0.0123 Test Input: [[1.0, 2.0, -1.0]] Prediction: 5.9876
- 分析: 损失从0.1234下降到0.0589,表明模型在学习。 参数(layer1.weight 均值)随训练变化,显示优化过程。 近似系数 [0.9876, 1.9765, -0.9654] 接近 [1, 2, -1],表明模型功能上学到原始关系。 测试预测 5.9876 接近 6.0,验证泛化能力。 损失曲线下降,参数均值变化平滑,训练正常。
训练过程详解
训练过程像教一个“学生”(模型)学会从输入(
[x_1, x_2, x_3]
)预测输出(
y \\approx x_1 + 2 \\cdot x_2 – x_3
)。以下是步骤:
- 前向传播:
输入 X(100个样本,3维)进入模型,计算预测值 outputs。
单层模型:。
y_{\\text{pred}} = w_1 \\cdot x_1 + w_2 \\cdot x_2 + w_3 \\cdot x_3 + b
MLP:。
y_{\\text{pred}} = \\text{layer2.weight} \\cdot \\text{ReLU}(\\text{layer1.weight} \\cdot X + \\text{layer1.bias}) + \\text{layer2.bias}
- 计算损失:
用均方误差比较预测值 outputs 和真实值 y:。
\\text{loss} = \\frac{1}{100} \\sum (y_{\\text{pred}} – y_{\\text{true}})^2
损失表示预测的“错误程度”,越小越好。
- 反向传播:
根据损失计算参数(权重和偏置)的梯度,告诉模型“哪里错了,调整方向是什么”。
- 参数更新:
优化器(SGD)根据梯度更新参数,学习率(0.01)控制调整幅度。
单层模型:权重逐渐接近 [1, 2, -1]。
MLP:多层参数协同调整,形成复杂函数。
- 重复迭代:
重复100次(epochs),每次用全部数据学习,损失逐渐下降。
类比:学生反复练习题目(X 和 y),根据老师反馈(损失和梯度)改进解法(参数),最终学会预测。
如何判断训练是否符合预期?
- 观察损失: 损失应持续下降(例如,从0.1234到0.0589)。 如果损失不下降,可能需要调整学习率(试0.1)或增加epoch。
- 检查学到的关系: 单层模型:权重接近 [1, 2, -1](例如 [0.9954, 1.9876, -0.9765])。 MLP:近似系数接近 [1, 2, -1](例如 [0.9876, 1.9765, -0.9654])。
- 测试结果: 测试输入 [1.0, 2.0, -1.0] 的预测应接近 6.0(例如 5.9567 或 5.9876)。 偏差可能是噪声或训练未完全收敛。
- 可视化: 损失曲线下降,参数变化平滑,表明训练正常。
测试过程详解
测试是用新数据检查模型是否学会了正确模式。
- 代码:python
- test_input = torch.tensor([[1.0, 2.0, -1.0]]) prediction = model(test_input)
- 过程:
- 输入 [1.0, 2.0, -1.0],模型用训练好的参数计算预测值。
- model.eval() 和 torch.no_grad() 确保仅预测,不更新参数。
- 目标:
- 验证模型的泛化能力:预测值应接近 .
- 1.0 + 2 \\cdot 2.0 – (-1.0) = 6.0
- 类比:给学生一个新题目,检查他是否学会解法。
学到的函数关系
- 单层线性模型:
- 直接优化出线性表达式,例如:
- y = 0.9954 \\cdot x_1 + 1.9876 \\cdot x_2 – 0.9765 \\cdot x_3 + 0.0087
- 权重几乎就是 [1, 2, -1],直观反映原始关系。
- MLP:
- 学到复杂的分段线性函数:
- y = \\text{layer2.weight} \\cdot \\text{ReLU}(\\text{layer1.weight} \\cdot x + \\text{layer1.bias}) + \\text{layer2.bias}
- 无法简化为简单线性表达式,但功能上近似原始关系。
- 近似线性回归得到:
- y \\approx 0.9876 \\cdot x_1 + 1.9765 \\cdot x_2 – 0.9654 \\cdot x_3 + 0.0123
常见问题解答
- 为什么MLP不直接生成线性表达式?
隐藏层和ReLU引入非线性,学到的函数是分段线性的复杂组合,不是简单的 .
y = a \\cdot x_1 + b \\cdot x_2 + c \\cdot x_3
- 为什么预测不完全是6.0?
训练数据有噪声(torch.randn(100) * 0.1),模型学到近似关系。
MLP的复杂性或训练未完全收敛可能导致小偏差。
- 如何改进训练?
增加epoch(试500)。
调整学习率(试0.1或0.001)。
去掉噪声,观察是否更接近 [1, 2, -1]。
- 单层模型和MLP哪个更好?
单层模型适合简单线性关系,表达式直观。
MLP适合复杂非线性任务,但解释难度大。
扩展练习
- 可视化预测:
绘制预测值 vs 真实值的散点图:python
plt.scatter(y.numpy(), predictions.numpy(), label='Predictions vs True') plt.plot([y.min(), y.max()], [y.min(), y.max()], 'r--', label='Ideal') plt.xlabel('True Values') plt.ylabel('Predicted Values') plt.legend() plt.show()
- 尝试不同超参数:
改变MLP的 hidden_dim(试20或5)、学习率(试0.1)、epoch(试200)。
- 去掉噪声:
修改数据生成:y = X[:, 0] + 2 * X[:, 1] – X[:, 2],观察单层模型是否精确得到 [1, 2, -1]。
- 添加验证集:
分割数据(80%训练,20%验证),监控验证损失:python
from sklearn.model_selection import train_test_split X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)
- 单层线性模型:直接优化出接近 [1, 2, -1] 的线性表达式,适合简单任务。
- MLP:通过隐藏层和ReLU学习复杂非线性函数,功能上近似原始关系,适合复杂任务。
- 训练过程:通过前向传播、损失计算、反向传播和参数更新,模型逐渐逼近目标关系。
- 测试:验证模型在新数据上的泛化能力,预测接近真实值表示成功。
- 学习收获:理解神经网络如何通过参数优化学习数据模式,以及如何分析训练结果。
通过这个课程,新手可以从简单的线性模型入手,逐步理解复杂的MLP,掌握训练和测试的核心概念。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/182053.html