拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

神经网络入门指南:从感知机到深度学习框架(AI-For-Beginners 第 3 章解析)

神经网络入门指南:从感知机到深度学习框架(AI-For-Beginners 第 3 章解析)
  • 教程
  • 人工智能
  • 机器学习
  • 深度学习

【免费下载链接】AI-For-Beginners

12 Weeks, 24 Lessons, AI for All!

项目地址:https://gitcode.com/GitHub_Trending/ai/AI-For-Beginners
点击查看免费下载

本指南围绕 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 中,上述算法被扩展为完整可运行的版本,其中包含几个重要的工程细节:

  1. 消除偏置项:通用线性模型形如 y=f(wᵀx+b)。为简化模型,可以在输入特征上额外加一维恒为 1 的维度(代码中每个样本变为[t[0], t[1], 1]),从而把偏置吸收进权重向量。
  2. 学习率的可调性:notebook 中train的默认学习率为0.01,并明确指出学习率的影响规律:
    • 较大的学习率(如1.0)收敛更快,但可能越过最优解;
    • 较小的学习率(如0.001)收敛较慢,但可能更精准;
    • 可尝试调用train(pos_examples, neg_examples, learning_rate=0.1)观察差异。
  3. 训练过程可视化: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)

梯度下降是著名的函数优化方法:计算损失函数对参数的导数(多维情形下称为梯度),然后以让误差减小的方式调整参数。其形式化流程为:

  1. 用随机值初始化参数 w⁽⁰⁾, b⁽⁰⁾;
  2. 反复执行如下更新步骤:
    • 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.db

Softmax 与 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 层级TensorFlowPyTorch
低层 APITensorFlowPyTorch
高层 APIKerasPyTorch Lightning
  • 低层 API:允许构建所谓的计算图。该图定义给定输入参数时如何计算输出(通常是损失函数),若有 GPU 可把计算推到 GPU 上执行;同时提供对计算图求导的功能,所得梯度用于优化模型参数。
  • 高层 API:基本把神经网络视为层的序列,让构建大多数神经网络变得更容易。训练模型通常只需准备数据然后调用fit函数即可。

高层 API 让你无需操心大量细节就能快速构建典型神经网络;低层 API 则对训练过程提供更多控制,因此在研究新网络架构时被大量使用。两种 API 可以混用:比如用低层 API 开发自定义层,再嵌入高层 API 构造并训练的大网络;或者用高层 API 按层序列定义网络,再用自己的低层训练循环做优化。两者共享相同的基础概念,设计上可以协同工作。

本课程的 Notebook 配套

本课程大部分内容同时提供 PyTorch 与 TensorFlow 版本,可任选其一。对应练习 Notebook:

API 层级Notebook
低层 APIIntroKerasTF.ipynb、IntroPyTorch.ipynb
高层 APIIntroKeras.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!

项目地址:https://gitcode.com/GitHub_Trending/ai/AI-For-Beginners
点击查看免费下载

相关推荐

上一篇:抖音批量下载神器:5分钟搞定无水印视频保存终极指南
下一篇:抖音下载终极神器:douyin-downloader一键批量保存去水印工具全解析

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表