神经网络经常被包装成很复杂的东西,但从入门角度看,可以先把它理解成一种可训练的函数组合:每一层做一次变换,多层连起来,就能表达更复杂的关系。
这篇文章从感知机讲起,逐步解释神经元、权重、激活函数、前向传播和反向传播的基本直觉。目标不是推完所有公式,而是让你能读懂神经网络训练代码的大体结构。
读这篇时可以先抓住一个主线:神经网络先用当前参数做预测,再根据损失反向更新参数。
一、从一个神经元开始
一个最简单的神经元可以写成:
z = w1 * x1 + w2 * x2 + ... + b
output = activation(z)
这里:
x是输入特征w是权重b是偏置activation是激活函数
如果没有激活函数,多层线性变换叠在一起仍然可以合并成一个线性变换。激活函数的意义是给网络加入非线性表达能力。
二、感知机能做什么
感知机可以看成早期的简单神经网络。它根据输入特征加权求和,再通过阈值得到分类结果。
if w1 * x1 + w2 * x2 + b > 0:
predict 1
else:
predict 0
这个模型能解决线性可分问题,也就是可以用一条线、一个平面或更高维超平面分开的数据。
但现实中的关系往往不是线性的,所以我们需要多层网络和非线性激活函数。
三、层是什么
神经网络里的一层,就是把一组输入同时送入多个神经元,得到一组输出。常见结构包括:
- 输入层:接收原始特征
- 隐藏层:中间的特征变换层
- 输出层:输出分类概率或预测数值
一个简单的多层网络可以表示成:
输入特征 -> 隐藏层 1 -> 隐藏层 2 -> 输出层
每一层都有自己的权重和偏置。训练时,模型会同时调整这些参数。
四、前向传播是什么
前向传播就是从输入开始,一层一层算到输出。
x -> layer1 -> activation -> layer2 -> activation -> output
在代码里,前向传播通常对应模型的 forward 函数。它回答的是:
给定当前参数和一批输入,模型会预测什么?
训练和预测都会用到前向传播。区别是训练时还要根据预测误差更新参数。
五、反向传播的直觉
反向传播用于计算每个参数对损失的影响。直觉上,它要回答:
如果这个权重稍微变大或变小,最终损失会怎么变化?
有了这个信息,优化器就可以更新参数,让损失下降。
预测输出 -> 计算损失 -> 反向传播梯度 -> 更新参数
你不需要一开始手写反向传播。PyTorch、TensorFlow 这类框架会自动完成梯度计算。但你需要知道训练循环为什么会有 loss.backward() 和 optimizer.step() 这类步骤。
六、一个典型训练循环
用伪代码表示,神经网络训练通常长这样:
for epoch in range(num_epochs):
for X_batch, y_batch in train_loader:
y_pred = model(X_batch)
loss = loss_fn(y_pred, y_batch)
optimizer.zero_grad()
loss.backward()
optimizer.step()
这段代码可以拆成五步:
- 取一批训练数据
- 前向传播得到预测
- 计算预测和真实标签之间的损失
- 反向传播计算梯度
- 优化器根据梯度更新参数
七、深度学习为什么需要更多数据和算力
神经网络的表达能力很强,但代价也很明显:
- 参数多,容易过拟合
- 训练通常需要更多数据
- 调参空间更大
- 训练速度更依赖硬件
这也是为什么入门时建议先学传统机器学习流程。你先掌握数据、特征、训练和评估,再进入神经网络,会更容易判断模型到底有没有学到可泛化的规律。
八、神经网络和大模型的关系
大语言模型、图像生成模型、语音识别模型,本质上都属于深度学习系统。它们通常使用更复杂的网络结构、更大的数据集和更长的训练过程。
但无论模型多大,基础问题仍然相似:
- 输入如何表示成数字
- 模型结构如何把输入变成输出
- 损失函数如何衡量预测错误
- 训练过程如何更新参数
- 评估方式是否能反映真实使用效果
所以,理解神经网络基础不是为了马上训练大模型,而是为了看懂现代 AI 系统背后的共同语言。
九、初学者容易误解的点
刚接触神经网络时,下面几个误解很常见:
- 以为层数越多一定越好,忽略数据量、过拟合和训练成本
- 把激活函数当成可有可无的细节,没有理解非线性的作用
- 只关注模型结构,不关注损失函数和评估指标
- 看到训练集 loss 下降就认为模型已经可靠
神经网络的强大来自表达能力,但可靠性仍然要靠数据划分、评估和错误分析来确认。
把一次前向和反向手算一遍
看再多描述,不如把最小的例子算一遍。下面这个网络只有两个输入、一个隐藏神经元、一个输出,用平方误差做损失,所有数字都可以口算验证。
输入 x = [1.0, 2.0] 真实标签 y = 1.0
隐藏层 w = [0.3, -0.1], b = 0.2 激活用 ReLU
输出层 v = 0.5, c = 0.1 不加激活
前向:
z = 0.3×1.0 + (-0.1)×2.0 + 0.2 = 0.3
a = ReLU(0.3) = 0.3
ŷ = 0.5×0.3 + 0.1 = 0.25
L = (ŷ - y)² = (0.25 - 1.0)² = 0.5625
反向:从损失开始,一层层往回乘导数。
∂L/∂ŷ = 2(ŷ - y) = 2 × (-0.75) = -1.5
∂L/∂v = ∂L/∂ŷ × a = -1.5 × 0.3 = -0.45
∂L/∂c = ∂L/∂ŷ × 1 = -1.5
∂L/∂a = ∂L/∂ŷ × v = -1.5 × 0.5 = -0.75
∂L/∂z = ∂L/∂a × ReLU'(0.3) = -0.75 × 1 = -0.75 ← z>0 所以导数为 1
∂L/∂w₁ = ∂L/∂z × x₁ = -0.75 × 1.0 = -0.75
∂L/∂w₂ = ∂L/∂z × x₂ = -0.75 × 2.0 = -1.5
∂L/∂b = ∂L/∂z × 1 = -0.75
梯度全是负的,说明这些参数都应该增大才能降低损失——符合直觉,因为预测值 0.25 低于目标 1.0。用学习率 0.1 把所有参数各更新一次(例如 w₁ = 0.3 + 0.1×0.75 = 0.375),重算前向得到 ŷ = 0.6588,损失从 0.5625 降到 0.1165。
注意这里所有参数是同时更新的,所以损失的下降幅度比只动某一个参数大得多。如果只把 w₁ 改成 0.375、其余不动,ŷ 只会从 0.25 走到 0.2875。这也解释了训练循环里 optimizer.step() 为什么是一次性作用于全部参数,而不是逐个调整。
有两个细节值得注意。第一,∂L/∂w₂ 是 ∂L/∂w₁ 的两倍,因为 x₂ 是 x₁ 的两倍——输入越大的特征,梯度也越大。这正是特征需要归一化的原因:量纲差异悬殊时,不同参数的更新步长会差好几个数量级。
第二,如果 z 恰好是负数,ReLU'(z) = 0,那么这条路径上所有梯度都会变成 0,这个神经元这一步完全不更新。大量神经元长期处于这种状态就是所谓的「神经元死亡」,也是 Leaky ReLU 之类变体想解决的问题。
建议自己动手把上面的数字重算一遍。这个例子里出现的每个乘法,在真实的百层网络里都是同样的操作,只是数量不同。
为什么权重不能初始化成全零
知道了训练循环之后,有一个具体问题值得单独想清楚,因为它能把「层」「梯度」「更新」这几件事串起来:如果把所有权重都初始化成 0,会发生什么?
直觉上这看起来很”中立”,但结果是网络完全学不动。原因是对称性:同一层里的所有神经元,如果初始权重相同,那么它们收到的输入相同、算出的输出相同、反向传播分到的梯度也相同,于是更新之后权重仍然相同。
这一层无论有多少个神经元,行为上永远等价于一个神经元。训练确实会让 loss 下降一点(因为偏置和层间还是能学),但整个网络的表达能力被压缩到了极小。
所以初始化必须打破对称性——用随机值。而随机值的大小也不能随便取:太小则信号逐层衰减,传到深层几乎为零;太大则逐层放大,很快溢出。常用的做法是让每层输出的方差与输入的方差大致相等,按扇入扇出的规模来缩放随机数(Xavier、He 初始化就是在做这件事)。
# PyTorch 的 nn.Linear 默认已经做了合理初始化,
# 但自定义参数时需要自己处理
w = torch.empty(out_features, in_features)
nn.init.kaiming_uniform_(w, nonlinearity='relu') # 配 ReLU
b = torch.zeros(out_features) # 偏置可以是 0
注意偏置初始化成 0 是没问题的——对称性是由权重打破的,偏置不需要承担这个职责。这个细节能说明”全零不行”的真正原因是对称性,而不是”零这个数值有问题”。
梯度消失:现代网络结构为什么长成那样
上面提到「层数越多不一定越好」。除了过拟合,还有一个更硬的技术原因,理解它之后你会发现现代架构里很多看起来奇怪的设计其实都在解决同一个问题。
反向传播的本质是链式法则:一个参数对最终损失的影响,等于它到输出这条路径上所有局部导数的连乘。
∂L/∂w₁ = (∂L/∂a_n) × (∂a_n/∂a_{n-1}) × ... × (∂a₂/∂a₁) × (∂a₁/∂w₁)
└────────── n 个因子相乘 ──────────┘
连乘的性质是指数级的。如果每个因子平均是 0.5,10 层之后是 0.5¹⁰ ≈ 0.001,50 层之后是 10⁻¹⁵——浅层参数的梯度小到浮点数都表示不了,它们几乎完全停止学习。这就是梯度消失。反过来如果因子平均大于 1,就是梯度爆炸,loss 会直接变成 NaN。
知道这个机制之后,几个常见设计的动机就清楚了,它们都在让那串因子不要偏离 1 太远:
- ReLU 取代 Sigmoid。Sigmoid 的导数最大只有 0.25,每层至少衰减到四分之一,天然就在制造梯度消失。ReLU 在正半轴导数恒为 1,连乘时不衰减。
- 归一化层(BatchNorm / LayerNorm)。把每层的输出重新拉回一个稳定的分布,间接把导数控制在合理范围内。
- 残差连接。这是最直接的——
y = f(x) + x让梯度多出一条不经过任何变换的通路,导数为 1,无论网络多深都能传到底。这正是几十层甚至上百层的网络能训起来的关键,也是残差结构几乎出现在所有现代架构里的原因。
所以「深度学习」能变深,不是因为堆层数本身有魔力,而是因为这一串工程手段解决了深层网络训不动的问题。入门时不需要记住每个技巧的细节,但记住它们要解决的是同一个问题,比分别背下来有用得多。
十、下一步读什么
上一篇是 模型训练与评估入门。如果你想把前面几篇串成一次完整练习,可以继续读 Python 人工智能小实战。
十一、用二维分类理解隐藏层
理解隐藏层时,可以先想象一个二维分类问题:输入只有 x1 和 x2,输出判断样本属于 A 类还是 B 类。单个线性分类器只能画出一条直线边界;如果数据呈现环形、月牙形或多个局部区域,一条直线就不够用了。
隐藏层的作用可以理解为重新组织特征空间。第一层神经元各自学习不同方向的分割,激活函数保留其中一部分响应;下一层再把这些响应组合起来,形成更复杂的决策边界。这个解释不等于完整数学证明,但足够帮助你理解为什么“多层 + 非线性”会比单层线性模型表达能力更强。
十二、训练日志应该怎么看
训练神经网络时,不要只盯着最后一个准确率。更可靠的做法是同时观察训练损失、验证损失和验证指标的变化趋势。
| 日志现象 | 可能含义 | 下一步检查 |
|---|---|---|
| 训练 loss 下降,验证 loss 上升 | 可能过拟合 | 增加正则化、减少模型规模或检查数据划分 |
| 训练和验证 loss 都不下降 | 学习率、特征或模型结构可能不合适 | 检查输入归一化、标签编码和学习率 |
| 准确率高但某些类别很差 | 类别不平衡或少数类特征不足 | 查看混淆矩阵和每类 precision/recall |
这也是为什么前面反复强调评估:神经网络可以很快把训练集拟合得很好,但只有验证集和错误分析才能说明它是否真的学到了可泛化规律。