回顾上章:MLP 在引入非线性后,可以形成弯曲的边界 ,但问题:它是怎么知道应该弯成什么样?**答案:通过 损失函数衡量差距 + 反向传播更新参数。
二、损失函数(Loss Function)的角色
• 定义:衡量模型预测与真实结果的差异。
• 分类任务常用:
• 二分类 → 二元交叉熵 (Binary Cross Entropy, BCE)
• 多分类 → 交叉熵 (Cross Entropy)
• 数学公式示例(二元交叉熵):
• 直观类比:像导航地图里的“距离差”,我们要不断缩短它。
三、梯度与参数更新
• 目标:找到能让损失函数最小的参数。
• 梯度:损失函数对参数的偏导,告诉我们“往哪边调”。
• 参数更新公式(梯度下降):
其中:
• \(\theta\) = 参数 (weights, bias)
• \(\eta\) = 学习率 (learning rate)
• \(\frac{\partial L}{\partial \theta}\) = 梯度
• 类比:像在山谷里往下走,梯度是“坡度”,学习率是“步长”。
四、反向传播(Backpropagation)原理
• 前向传播:输入 → 线性变换 → 激活函数 → 输出 → 损失。
• 反向传播:从损失往回推,链式法则分解每一层的梯度。
• 链式法则公式:
• 直观解释:像接力赛,误差从最后一层“传递”回去。
五、PyTorch Demo 实践
👉 用之前的圆形数据集继续演示,增加 loss 曲线可视化,展示训练过程中:
1. 损失值逐渐下降
2. 决策边界逐渐变弯曲
# -*- coding: utf-8 -*-
# 反向传播与梯度下降:神经网络如何真正学会分类import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt# 1) 生成非线性可分数据:同心圆
def make_concentric_circles(n_samples=300, inner_r=0.5, outer_r=1.0, noise=0.08, seed=42):rng = np.random.default_rng(seed)n_inner = n_samples // 2n_outer = n_samples - n_inner# 内圈(类0)angles_inner = rng.uniform(0, 2*np.pi, size=n_inner)r_inner = rng.normal(inner_r, noise, size=n_inner)x_inner = np.stack([r_inner * np.cos(angles_inner), r_inner * np.sin(angles_inner)], axis=1)y_inner = np.zeros((n_inner, 1), dtype=np.float32)# 外环(类1)angles_outer = rng.uniform(0, 2*np.pi, size=n_outer)r_outer = rng.normal(outer_r, noise, size=n_outer)x_outer = np.stack([r_outer * np.cos(angles_outer), r_outer * np.sin(angles_outer)], axis=1)y_outer = np.ones((n_outer, 1), dtype=np.float32)X = np.vstack([x_inner, x_outer]).astype(np.float32)y = np.vstack([y_inner, y_outer]).astype(np.float32)# 打乱idx = rng.permutation(n_samples)return X[idx], y[idx]# 数据准备
X_np, y_np = make_concentric_circles(n_samples=300, inner_r=0.45, outer_r=1.0, noise=0.07, seed=7
)
X = torch.tensor(X_np)
y = torch.tensor(y_np)# 2) 定义一个 MLP 模型
model = nn.Sequential(nn.Linear(2, 8),nn.ReLU(),nn.Linear(8, 1),nn.Sigmoid()
)# 3) 定义损失函数和优化器
criterion = nn.BCELoss() # 二元交叉熵
optimizer = optim.SGD(model.parameters(), lr=0.1) # 梯度下降# 4) 训练过程
losses = []
for epoch in range(200):optimizer.zero_grad() # 清空梯度y_pred = model(X) # 前向传播loss = criterion(y_pred, y) # 计算损失loss.backward() # 反向传播optimizer.step() # 参数更新losses.append(loss.item())# 5) 可视化 Loss 曲线
plt.plot(losses)
plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.title("Loss Curve during Training")
plt.show()
通过上面的计算和图形我们得到的结论是在定义好model,这个model我们可以理解为函数建模,也就是输入和输出之间建立了函数之间的关系,函数的关系通过y = mx + b也就是偏置和权重以及隐藏的网络层不断的进行计算,这个model会给权重和偏置一个随机的初始化值,我们输入好x之后得到的一个y_pred的值,然后使用这个值和真实的值y做个对比,通过调整偏置和权重值让其不断的缩小,最好找到了根据数据训练出来的表现最好的一组函数关系,然后就可以使用这组函数关系对后续的数据进行预测,这就是机器学习的核心。
结论和机器学习专业术语的对应关系:
1. 模型 = 函数建模
• 我们定义的 model(例如 MLP)其实就是一个复杂函数 \(f_\theta(x)\),其中 \(\theta\) 代表参数(权重 \(w\) 和偏置 \(b\))。
• 这相当于我们在寻找一个能描述 输入 → 输出 关系的函数。
2. 参数初始化 = 起点
• 在训练开始时,权重和偏置是 随机初始化 的。
• 随机意味着一开始模型的预测几乎是“瞎猜”,没有实际规律。
3. 前向传播 = 函数计算
• 输入 \(x\) 经过网络层层计算(线性变换 + 激活函数),得到预测值 \(\hat{y}\)(即 y_pred)。
• 数学类比:像是把原始输入放进“函数工厂”,得到一个输出。
4. 损失函数 = 误差度量
• 将预测值 \(\hat{y}\) 与真实值 \(y\) 对比,得到误差(loss)。
• 这个误差告诉我们:当前这组函数参数 \(\theta\) 表现好不好。
5. 反向传播 + 参数更新 = 学习过程
• 通过反向传播算法,计算损失对每个参数的梯度。
• 然后用梯度下降调整参数:
• 直观类比:在“山谷”中顺着斜坡往下走,直到找到最低点。
6. 收敛后的参数 = 最优函数关系
• 训练过程中,权重和偏置不断更新,最终收敛到一个较优解。
• 这组参数对应的就是一个能 泛化数据规律 的函数。
• 后续遇到新数据 $x_{\text{new}}$,只需套入这个函数 $f_\theta$,就能预测输出 $\hat{y}_{\text{new}}$。
⸻
🌱 一句话总结
机器学习的核心就是:用数据来“调教”函数的参数,让这个函数能尽可能准确地映射输入与输出的关系。
换个直白的类比:
• 模型(MLP)像是一台“函数机器”,
• 权重和偏置是机器的“旋钮”,
• 损失函数是“性能检测仪”,
• 梯度下降是“调节旋钮的方法”。
• 训练完成后,这台机器就能根据新输入给出合理预测。