- 教程
- 人工智能
- 机器学习
- 深度学习
【免费下载链接】AI-For-Beginners
12 Weeks, 24 Lessons, AI for All!
本指南围绕 AI-For-Beginners 课程第 3 章「神经网络入门」展开,系统讲解如何用数据训练"人造大脑":从神经元数学模型与机器学习基本问题出发,逐步深入感知机、梯度下降、多层网络与反向传播,最终落到 TensorFlow 与 PyTorch 等现代框架与过拟合问题。读完你将掌握从零实现可运行神经网络训练循环的完整技术路径,并理解现代深度学习框架背后的核心原理。
AI-For-Beginners 课程第 3 章「神经网络入门」内容总览(丹麦语译版封面 doodle 图)
为什么要训练一个"人造大脑"
在前序章节的导论中我们谈到,实现智能的一条途径是训练一个计算机模型,也就是一个人造大脑。自 20 世纪中叶以来,研究者尝试过多种数学模型,直到近年这一方向被证明极其成功。这类大脑的数学模型被称为神经网络。
有时神经网络被称为人工神经网络(Artificial Neural Networks, ANN),意在强调我们讨论的是数学模型,而非真实的神经元网络。
在这一章中,我们只看神经网络模型本身,不覆盖经典机器学习内容。如果你希望系统学习经典机器学习,可以单独参考仓库中对应的入门课程(本仓库聚焦 AI,两者互补)。
机器学习的基本设定:特征、标签、分类与回归
神经网络隶属于一门更大的学科——机器学习(Machine Learning),其目标是用数据训练出能够解决问题的计算机模型。机器学习构成了人工智能的很大一部分,但本课程不涉及经典 ML 的算法细节。
在机器学习中,我们假设存在一个样例数据集X以及对应的输出值Y:
- 样例通常是N 维向量,由若干特征(features)组成;
- 输出被称为标签(labels);
- 当输入与输出都用张量表示时,输入数据集是一个大小为M×N的矩阵,其中 M 是样本数量、N 是特征数量;输出标签 Y 是大小为 M 的向量。
本章重点讨论机器学习里最常见的两类问题:
| 问题类型 | 目标 | 典型场景 |
|---|---|---|
| 分类(Classification) | 将输入对象划分到两个或更多类别 | 肿瘤良恶性判定、手写数字识别 |
| 回归(Regression) | 为每个输入样本预测一个数值 | 房价预测、温度预测 |
神经元数学模型:从生物神经元到激活函数
从生物学我们知道,人脑由神经细胞(神经元)组成,每个神经元有多个"输入"(树突)和一个"输出"(轴突)。树突与轴突都能传导电信号,而它们之间的连接——突触——具有可变的传导度,由神经递质调节。
| 真实神经元(图片来自 Wikipedia) | 人工神经元(作者绘制) |
由此得到最简单的神经元数学模型:包含若干输入 X₁, ..., X_N 和一个输出 Y,以及一系列权重 W₁, ..., W_N。输出按如下公式计算:
其中f 是一个非线性激活函数(activation function)。
早期神经元模型由 Warren McCulloch 与 Walter Pitts 在 1943 年发表于经典论文《A logical calculus of the ideas immanent in nervous activity》;Donald Hebb 在其著作《The Organization of Behavior: A Neuropsychological Theory》中提出了这类网络的训练方式(赫布学习规则)。
感知机:最早的二分类神经网络
对现代神经网络最早的成功尝试之一,是 Frank Rosenblatt 1957 年在康奈尔航空实验室完成的实现。它是一个名为Mark-1的硬件设备,用于识别三角形、正方形、圆形等原始几何图形。
当时输入图像由20×20 的光电池阵列表示,因此网络有 400 个输入和 1 个二进制输出。这个简单网络只含一个神经元,也称为阈值逻辑单元(threshold logic unit)。网络的权重在训练阶段像电位器一样需要手动调节。
✅ 电位器(potentiometer)是一种允许用户调节电路电阻的装置。
感知机模型
假设模型有 N 个特征,输入向量即为大小为 N 的向量。感知机是一个二分类模型,即它只能区分两类输入数据。约定对每个输入向量 x,感知机输出 +1 或 -1,取决于样本属于哪个类别。输出计算公式为:
y(x) = f(wᵀx)其中 f 是阶跃激活函数:
f(x) = { +1 (x ≥ 0) { -1 (x < 0)用感知机准则训练:梯度下降的雏形
训练感知机需要找到权重向量w,使绝大多数样本被正确分类,即让误差最小。这个误差 E 由**感知机准则(perceptron criterion)**定义:
E(w) = -Σ wᵀxᵢtᵢ其中:
- 求和只针对那些被错误分类的训练数据点 i;
- xᵢ 是输入数据,tᵢ 对正例取 +1、对负例取 -1。
该准则被视为权重 w 的函数,我们需要最小化它。常用的方法是梯度下降(gradient descent):从某个初始权重 w⁽⁰⁾ 出发,每一步按下式更新权重:
w⁽ᵗ⁺¹⁾ = w⁽ᵗ⁾ - η∇E(w)这里 η 是学习率(learning rate),∇E(w) 是 E 的梯度(gradient)。计算梯度后得到:
w⁽ᵗ⁺¹⁾ = w⁽ᵗ⁾ + Σ ηxᵢtᵢ用 Python 实现的感知机训练算法如下(摘自 感知机章节):
def train(positive_examples, negative_examples, num_iterations = 100, eta = 1): weights = [0,0,0] # 初始化权重(几乎是随机:) for i in range(num_iterations): pos = random.choice(positive_examples) neg = random.choice(negative_examples) z = np.dot(pos, weights) # 计算感知机输出 if z < 0: # 正例被误判为负例 weights = weights + eta*weights.shape z = np.dot(neg, weights) if z >= 0: # 负例被误判为正例 weights = weights - eta*weights.shape return weights配套 Notebook 中的完整可运行版本
在仓库的 Perceptron.ipynb 中,上述算法被扩展为完整可运行的版本,其中包含几个重要的工程细节:
- 消除偏置项:通用线性模型形如 y=f(wᵀx+b)。为简化模型,可以在输入特征上额外加一维恒为 1 的维度(代码中每个样本变为
[t[0], t[1], 1]),从而把偏置吸收进权重向量。 - 学习率的可调性:notebook 中
train的默认学习率为0.01,并明确指出学习率的影响规律:- 较大的学习率(如
1.0)收敛更快,但可能越过最优解; - 较小的学习率(如
0.001)收敛较慢,但可能更精准; - 可尝试调用
train(pos_examples, neg_examples, learning_rate=0.1)观察差异。
- 较大的学习率(如
- 训练过程可视化:notebook 周期性打印每 10 次迭代的正/负例正确率,并用
plot_boundary把分类边界直线 w₀x₀ + w₁x₁ + w₂ = 0 画出来。实际运行输出显示:初始正确率约 50%,随后迅速提升到接近 90%。
动手实验:如果你想从零构建自己的感知机并挑战手写数字分类,可完成 lab/README.md 的实验,参考 Notebook PerceptronMultiClass.ipynb——在上一课中我们用感知机做了二分类并区分两个手写数字,该实验要求你完整解决数字分类问题,即判定给定图像最可能对应哪个数字。
多层感知机与反向传播:从单层到灵活框架
感知机虽经典,却只是线性二分类模型。为了让网络更强大,04-OwnFramework 章节 将其扩展为更灵活的框架,从而支持:
- 在二分类之外进行多分类;
- 在分类之外解决回归问题;
- 分离非线性可分的类别。
同时,我们将在 Python 中开发自己的模块化框架,以构建不同的神经网络架构。
机器学习的正式化:损失函数
假设有带标签Y的训练数据集X,需要构建模型 f 以做出最准确的预测。预测质量用损失函数(Loss function)L衡量。常用损失函数如下:
- 回归问题(需要预测数值):可使用绝对误差Σᵢ|f(x⁽ⁱ⁾)-y⁽ⁱ⁾|,或平方误差Σᵢ(f(x⁽ⁱ⁾)-y⁽ⁱ⁾)²;
- 分类问题:可使用0-1 损失(本质上等同于模型准确率),或对数损失(logistic loss)。
对于单层感知机,函数 f 定义为线性函数 f(x)=wx+b(w 为权重矩阵,x 为输入特征向量,b 为偏置向量)。对于不同架构的神经网络,f 可以呈现更复杂的形式。
在分类问题中,通常希望网络输出的是各类别的概率。为了把任意数值转换成概率(即归一化输出),我们常使用softmax 函数σ,此时 f 变为 f(x)=σ(wx+b)。
在上述定义中,w 和 b 被称为参数θ=⟨w,b⟩。给定数据集 ⟨X,Y⟩,可以把整个数据集上的总体误差视为参数 θ 的函数。
✅神经网络训练的目标,就是通过改变参数 θ 来最小化误差。
梯度下降与随机梯度下降(SGD)
梯度下降是著名的函数优化方法:计算损失函数对参数的导数(多维情形下称为梯度),然后以让误差减小的方式调整参数。其形式化流程为:
- 用随机值初始化参数 w⁽⁰⁾, b⁽⁰⁾;
- 反复执行如下更新步骤:
- w⁽ⁱ⁺¹⁾ = w⁽ⁱ⁾ - η∂L/∂w
- b⁽ⁱ⁺¹⁾ = b⁽ⁱ⁾ - η∂L/∂b
理论上,训练中的优化步骤应基于整个数据集计算(损失是全部训练样本的加和)。但在现实中,我们取数据的一小部分——小批量(minibatches),基于子集计算梯度。由于每次随机选取子集,该方法被称为随机梯度下降(Stochastic Gradient Descent, SGD)。
多层网络的数学形式
单层网络只能分类线性可分的类别。要构建更丰富的模型,可以把若干网络层组合起来。数学上,函数 f 的形式更复杂,分几步计算:
- z₁ = w₁x + b₁
- z₂ = w₂α(z₁) + b₂
- f = σ(z₂)
其中 α 是非线性激活函数,σ 是 softmax 函数,参数 θ=⟨w₁,b₁,w₂,b₂⟩。
梯度下降算法保持不变,但计算梯度变得更困难。借助链式法则,可以这样计算导数:
- ∂L/∂w₂ = (∂L/∂σ)(∂σ/∂z₂)(∂z₂/∂w₂)
- ∂L/∂w₁ = (∂L/∂σ)(∂σ/∂z₂)(∂z₂/∂α)(∂α/∂z₁)(∂z₁/∂w₁)
✅ 链式法则(chain rule)用于计算损失函数对参数的导数。
注意:这些表达式中最左边的部分都相同,因此我们可以从损失函数出发,沿着计算图(computational graph)"向后"高效地计算导数。这种训练多层感知机的方法因此被称为反向传播(backpropagation),简称 backprop。
亲手构建自己的神经网络框架
OwnFramework.ipynb 是本章的实战核心:它用纯 NumPy 逐步搭建一个能处理多分类与回归的小型深度学习框架,让我们看清现代神经网络内部到底发生了什么。
第一步:Linear 层
首先定义最基础的线性层,用随机正态分布初始化权重(方差取 1/√nin 以便稳定信号传播):
class Linear: def __init__(self, nin, nout): self.W = np.random.normal(0, 1.0/np.sqrt(nin), (nout, nin)) self.b = np.zeros((1, nout)) def forward(self, x): return np.dot(x, self.W.T) + self.b因为使用 NumPy 运算,可以一次传入一批输入向量,网络会一次性输出对应的输出向量。
第二步:Softmax——把输出变成概率
线性层的输出不是概率,可以是任意值。要用softmax在所有类别上归一化:
σ(z_c) = e^{z_c} / Σ_j e^{z_j} (c ∈ 1..|C|)实现时先减去最大值zmax以提升数值稳定性:
class Softmax: def forward(self, z): zmax = z.max(axis=1, keepdims=True) expz = np.exp(z - zmax) Z = expz.sum(axis=1, keepdims=True) return expz / Z网络输出 σ(z) 可以解释为类别集合 C 上的概率分布:q = σ(z_c) = p̂(c|x)。
当类别数超过 2 时,softmax 会在所有类别上归一化概率。
第三步:交叉熵损失
分类问题的损失函数通常是逻辑函数,可推广为交叉熵损失(cross-entropy loss)——它度量两个任意概率分布之间的相似度。在我们的场景中,第一个分布是网络输出的概率分布,第二个是one-hot分布(指定类别 c 概率为 1,其余为 0)。此时交叉熵损失可简化为-log p_c,其中 c 是期望类别,p_c 是网络给出的该类别概率。
若网络对期望类别输出概率 1,交叉熵损失为 0;实际类别概率越接近 0,交叉熵损失越大(可以趋于无穷大!)。
代码实现(注意对整批样本取平均,使损失返回一个标量):
class CrossEntropyLoss: def forward(self, p, y): self.p = p self.y = y p_of_y = p[np.arange(len(y)), y] log_prob = np.log(p_of_y) return -log_prob.mean() # 对所有输入样本取平均重要:损失函数返回一个数字,表示网络表现好坏的度量。它应当对整批数据(或部分数据)返回一个数值,因此计算每个样本的交叉熵后需要用
.mean()求平均。
第四步:把层组合成计算图并训练
上述层的组合可以表示为计算图。一次训练过程分为两个阶段:
- 前向传播(forward pass):对给定输入小批量计算损失函数的值;
- 反向传播(backward pass):把误差沿计算图"分布"回模型参数,从而最小化误差。
为每个层实现backward函数是框架的关键。以线性层 z = x×W + b 为例,偏导数为 ∂z/∂W = x、∂z/∂b = 1;要补偿层输出端的误差 Δz,需要相应更新权重:
Δx = Δz × W ΔW = (∂z/∂W)Δz = Δz × x Δb = (∂z/∂b)Δz = Δz重要:计算并非针对单个训练样本,而是针对整个小批量。所需的参数更新 ΔW 与 Δb 在整个 mini-batch 上计算,对应向量的维度为 x ∈ R^(minibatch × nclass)。
带反向传播的完整 Linear 层如下:
class Linear: def __init__(self, nin, nout): self.W = np.random.normal(0, 1.0/np.sqrt(nin), (nout, nin)) self.b = np.zeros((1, nout)) self.dW = np.zeros_like(self.W) self.db = np.zeros_like(self.b) def forward(self, x): self.x = x return np.dot(x, self.W.T) + self.b def backward(self, dz): dx = np.dot(dz, self.W) dW = np.dot(dz.T, self.x) db = dz.sum(axis=0) self.dW = dW self.db = db return dx def update(self, lr): self.W -= lr * self.dW self.b -= lr * self.dbSoftmax 与 CrossEntropyLoss 也以同样方式定义各自的backward。
第五步:训练循环与 Net 类
一次完整的数据集遍历通常被称为一个 epoch。手写训练循环如下(摘自 notebook,批大小 4、学习率 0.1):
lin = Linear(2, 2) softmax = Softmax() cross_ent_loss = CrossEntropyLoss() learning_rate = 0.1 for i in range(0, len(train_x), batch_size): xb = train_x[i:i+batch_size] yb = train_labels[i:i+batch_size] # 前向传播 z = lin.forward(xb) p = softmax.forward(z) loss = cross_ent_loss.forward(p, yb) # 反向传播 dp = cross_ent_loss.backward(loss) dz = softmax.backward(dp) dx = lin.backward(dz) lin.update(learning_rate)运行结果验证了学习有效性:初始准确率约 0.725,一个 epoch 后提升到约 0.825(notebook 中注释为"一个 epoch 就能把准确率从约 50% 提升到约 80%")。
为简化"层堆叠",框架还提供了Net类:内部维护层列表,add()追加层,forward顺序执行各层前向、backward逆序执行反向、update为所有带update方法的层更新参数:
class Net: def __init__(self): self.layers = [] def add(self, l): self.layers.append(l) def forward(self, x): for l in self.layers: x = l.forward(x) return x def backward(self, z): for l in self.layers[::-1]: z = l.backward(z) return z def update(self, lr): for l in self.layers: if 'update' in l.__dir__(): l.update(lr)借助Net,模型定义与训练代码变得整洁:
net = Net() net.add(Linear(2, 2)) net.add(Softmax()) loss = CrossEntropyLoss() def train_epoch(net, train_x, train_labels, loss=CrossEntropyLoss(), batch_size=4, lr=0.1): for i in range(0, len(train_x), batch_size): xb = train_x[i:i+batch_size] yb = train_labels[i:i+batch_size] p = net.forward(xb) l = loss.forward(p, yb) dp = loss.backward(l) dx = net.backward(dp) net.update(lr)实际输出示例:初始 loss=0.6212、准确率 0.6875;训练后 loss=0.4437、准确率 0.8;测试集 loss=0.4768、准确率 0.85。notebook 还提供train_and_plot函数,用等高线图(decision boundary)实时可视化网络在每个 epoch 后的分类边界变化。
练习:使用本节搭建的框架解决 MNIST 手写数字分类,见 lab/README.md 与 Notebook MyFW_MNIST.ipynb。
神经网络框架:从自研到 TensorFlow 与 PyTorch
前面自研框架证明了一个事实:高效训练神经网络需要两件事——张量运算(乘法、加法、sigmoid/softmax 等函数)和自动求梯度(以执行梯度下降)。NumPy 能完成第一件事,但梯度需要机制来计算。在我们自研框架里,所有导数函数都要手工编程进backward方法;理想的框架应能对任意表达式自动计算梯度。
另一个关键能力是在 GPU 或其他专用计算单元(如 TPU)上执行计算。深度神经网络训练需要海量计算,能在 GPU 上并行化这些计算至关重要。
✅ "并行化(parallelize)"指把计算分布到多个设备上执行。
两大框架与两层 API
目前最流行的两个神经网络框架是TensorFlow与PyTorch。两者都提供低层 API 在 CPU 与 GPU 上操作张量,低层 API 之上还有高层 API——分别对应Keras与PyTorch Lightning。
| API 层级 | TensorFlow | PyTorch |
|---|---|---|
| 低层 API | TensorFlow | PyTorch |
| 高层 API | Keras | PyTorch Lightning |
- 低层 API:允许构建所谓的计算图。该图定义给定输入参数时如何计算输出(通常是损失函数),若有 GPU 可把计算推到 GPU 上执行;同时提供对计算图求导的功能,所得梯度用于优化模型参数。
- 高层 API:基本把神经网络视为层的序列,让构建大多数神经网络变得更容易。训练模型通常只需准备数据然后调用
fit函数即可。
高层 API 让你无需操心大量细节就能快速构建典型神经网络;低层 API 则对训练过程提供更多控制,因此在研究新网络架构时被大量使用。两种 API 可以混用:比如用低层 API 开发自定义层,再嵌入高层 API 构造并训练的大网络;或者用高层 API 按层序列定义网络,再用自己的低层训练循环做优化。两者共享相同的基础概念,设计上可以协同工作。
本课程的 Notebook 配套
本课程大部分内容同时提供 PyTorch 与 TensorFlow 版本,可任选其一。对应练习 Notebook:
| API 层级 | Notebook |
|---|---|
| 低层 API | IntroKerasTF.ipynb、IntroPyTorch.ipynb |
| 高层 API | IntroKeras.ipynb |
在课程设计中,能用高层 API 的地方就优先用高层 API 以保持简洁;但理解神经网络如何从底层运作同样重要,因此入门阶段先从低层 API 和张量开始。如果你只想快速上手,也可以直接跳到高层 API 的 Notebook。
过拟合:训练中最关键的陷阱
掌握框架之后,本课程还专门强调一个极其重要的概念——过拟合(Overfitting)。考虑用模型逼近 5 个点的经典例子(图出自 05-Frameworks 章节 的 images 目录):
| 线性模型(2 个参数) | 非线性模型(7 个参数) |
|---|---|
| 训练误差 = 5.3 | 训练误差 = 0 |
| 验证误差 = 5.1 | 验证误差 = 20 |
- 左侧是好的直线近似:参数数量适中,模型正确把握了点分布的规律;
- 右侧模型过于强大:只有 5 个点却有 7 个参数,模型可以调整到穿过所有点,使训练误差为 0,但这妨碍模型理解数据背后的正确模式,因此验证误差非常高。
在模型丰富度(参数数量)与训练样本数量之间找到正确平衡,至关重要。
过拟合为何发生
- 训练数据不足;
- 模型过于强大;
- 输入数据噪声过大。
如何检测过拟合
从上面的图表可以看出,过拟合的典型信号是训练误差极低、验证误差很高。正常训练过程中训练与验证误差都会先下降,然后在某个时刻验证误差可能停止下降并开始上升——这就是过拟合的标志,提示我们应该在此处停止训练(或至少给模型做一次快照)。
如何预防过拟合
发现过拟合时,可以采取以下措施之一:
- 增加训练数据量;
- 降低模型复杂度;
- 使用**正则化(regularization)**技术,例如 Dropout(在后续课程中会讲到)。
过拟合与偏差-方差权衡
过拟合实际上是统计学中更普遍的**偏差-方差权衡(Bias-Variance Tradeoff)**问题的一个实例。考虑模型误差的可能来源,可以看到两类误差:
- 偏差误差(Bias errors):由算法无法正确捕捉训练数据间的关系造成,可能源于模型能力不足(欠拟合,underfitting);
- 方差误差(Variance errors):由模型近似输入数据中的噪声而非有意义的关系造成(过拟合)。
训练过程中,偏差误差会下降(模型学会逼近数据),方差误差会上升。因此重要的是在合适时机停止训练——无论是手动(检测到过拟合时)还是自动(引入正则化)——以防止过拟合。
章节小结与练习路径
本章从神经元数学模型出发,完整走通了"单层感知机 → 梯度下降 → 多层网络与反向传播 → 自研 NumPy 框架 → TensorFlow/PyTorch 框架 → 过拟合"的神经网络学习路径。实践环节(03-Perceptron → 04-OwnFramework → 05-Frameworks)对应的三个实验依次挑战:完整手写数字二分类、用自研框架解决 MNIST 多分类、以及用 PyTorch 或 TensorFlow 解决单层/多层全连接网络的分类问题。完成这三级练习,你就同时掌握了神经网络的底层原理与工业级框架的使用能力。
说明:本文基于 translations/da/lessons/3-NeuralNetworks/README.md(丹麦语译版)编写,并以仓库英文原版 lessons/3-NeuralNetworks/README.md 及其配套 Notebook 作为事实依据;该翻译文档由 AI 翻译服务生成,如需精确内容请以仓库英文原版为准。
- 教程
- 人工智能
- 机器学习
- 深度学习
【免费下载链接】AI-For-Beginners
12 Weeks, 24 Lessons, AI for All!
相关推荐
AI-For-Beginners 神经网络入门指南:从感知机到现代深度学习框架
AI For Beginners 神经网络入门指南:从感知机到现代深度学习框架 本指南以 AI For Beginners 课程第三部分"神经网络"( less
教程人工智能机器学习深度学习AI-For-Beginners 神经网络课程导读:从感知机到深度学习框架
AI For Beginners 神经网络课程导读:从感知机到深度学习框架 本节内容是开源课程 AI For Beginners https://link.gi
教程人工智能机器学习深度学习AI-For-Beginners 神经网络入门:从感知机到 PyTorch 的完整学习路线
AI For Beginners 神经网络入门:从感知机到 PyTorch 的完整学习路线 本篇文章基于 AI For Beginners 开源课程中"神经网络
教程人工智能机器学习深度学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考