第4章:理解神经网络

🎉摘要:本文用大白话解释神经网络核心概念:全连接层如何加权求和,ReLU激活函数如何引入非线性,前向传播是什么。附带PyTorch代码演示,适合初学者理解神经网络基础。

这一章的目标

  • 理解"神经网络"到底是个什么东西(大白话版)

  • 搞清楚层(Layer)激活函数(Activation)前向传播(Forward) 三个核心概念

  • 用 PyTorch 搭一个最简单的单层网络,看看它怎么 "算" 出一个结果

⚠️ 这章会比较 "干"

因为要一次性讲好几个基础概念。但相信我,理解了这章之后,后面所有代码你都能看懂了。如果一遍没完全懂,跑完代码再回头读一遍就好。

神经网络是什么?—— 一个直觉类比

先忘掉那些复杂的数学公式。我们用一个生活例子理解:

你到一家陌生的餐厅点菜,想知道某道菜好不好吃。你会怎么判断?

  • 你可能会看:颜色(输入 1)、香味(输入 2)、摆盘(输入 3)

  • 大脑里有个模糊的判断规则:颜色好看 +0.5 分,香味浓郁 +0.8 分,摆盘精致 +0.3 分

  • 最终得出一个 "好吃指数":0.5×颜色 + 0.8×香味 + 0.3×摆盘

神经网络本质上就是做了这件事:接收输入 → 加权求和 → 输出结果

只不过:

  • "输入"是图片的像素值(28×28 = 784 个数字)

  • "权重"是网络要的东西(一开始瞎猜,通过训练慢慢调准)

  • "输出"是 0~9 每个数字的"得分"

全连接层(Linear Layer / Fully Connected Layer)

想象一个场景:识别手写数字图片(0‑9)。

一张 MNIST 图片是 28×28 像素,一共 28×28 = 784 个小光点。每个光点有明暗数值。

什么是全连接层?

全连接层指前一层的每一个点,全部都和后一层的每一个点相连。如下图:

举例子:输入 784 个光点,输出 10 个数字分数(代表是 0、1、2…9 的可能性)。

输入:784 个小光点(784 个小圆点)

输出:10 个结果点(分别代表数字 0 到 9 的打分)

“全连接” 含义:

784 个输入点,每一个,都连到后面 10 个输出点上面。

总共:784 × 10 = 7840 根连接线。

每一根连线上面,都有一个权重(weight),可以理解成这根线的“重要程度”。

  • 线的数字大 = 这个输入对结果影响大;

  • 数字小 = 影响小;

  • 负数 = 起反作用;

每个输出节点,还要再加一个小偏移数字,叫偏置 bias。

计算过程(大白话):

拿输出“数字 0 的分数” 举例:

  1. 784 个像素,分别乘上各自连线的权重

  2. 全部加在一起

  3. 再加一个偏移 bias

  4. 最后得到一个分数。

分数越高,模型认为这张图片越有可能是数字 0。

数字 1~9 的分数,也是用一模一样的方式算出来。

全连接层是神经网络最基本的组件。它做的事就是上面说的 "加权求和":

image.png

用人话翻译:

  • x(输入):比如一张图展开成 784 个像素值

  • W(权重/Weight):每个输入有多重要,是网络要学习的核心参数

  • b(偏置/Bias):一个"基础分",比如某道菜天生就难吃,基础分扣 5 分

  • y(输出):加权后的结果

📌 术语:参数(Parameter)

模型里需要通过训练来调整的变量,就是参数。Weight 和 Bias 是最主要的参数
训练的本质就是不断调整 W 和 b,让模型的输出越来越接近正确答案。

用代码演示一下:

import torch
# 导入神经网络模块,里面包含各种网络层、激活函数
# nn = neural network,PyTorch 的神经网络模块
import torch.nn as nn  
# 定义一个最简单的全连接层:784个输入 → 10个输出(数字0~9各一个得分)
layer = nn.Linear(in_features=784, out_features=10)

# 随便造一个"假图片"输入(形状:1张图 × 784像素)
dummy_input = torch.randn(1, 784)

# 前向传播:输入流过这一层,得到输出
output = layer(dummy_input)

print(f"输入形状: {dummy_input.shape}")  # → (1, 784)
print(f"输出形状: {output.shape}")       # → (1, 10)

print(f"10个输出值: ")
# range(10) 等价 range(0,10),遍历 0,1,2...9 全部 10 个类别
for i in range(10):
    # output[0,i]:第0张样本,第i类的输出分数
    print(f"{i} = {output[0, i].item():.4f}")

运行代码,输出如下:

输入形状: torch.Size([1, 784])
输出形状: torch.Size([1, 10])
10个输出值: 
0 = -0.3233
1 = -0.5019
2 = -0.1596
3 = -1.1589
4 = 0.7955
5 = 0.4612
6 = 0.4138
7 = -0.5528
8 = -0.0840
9 = 0.0490

上面的 10 个输出值就是模型对 "这张图是数字 0~9 各几分" 的初步判断。值越大,模型越倾向于是那个数字。

激活函数(Activation Function)

光有全连接层不够。因为全连接层只做线性运算(乘法和加法),无论堆多少层,最终还是线性的,表达能力有限。

激活函数的作用就是引入"非线性",让网络能学习更复杂的模式。

📌 术语:线性 vs 非线性

线性:y = 2x + 1,画出来是一条直线

非线性:y = x²,画出来是条曲线;或者 y = max(0, x),折线

现实世界的问题几乎没有纯线性的,所以必须引入非线性。

如下图:

最常用的 ReLU

ReLU 全称 Rectified Linear Unit(修正线性单元),听起来吓人,函数超简单:

image.png

翻译成人话:负数变成 0,正数保持不变。就这。

import torch
# 导入神经网络模块,里面包含各种网络层、激活函数
import torch.nn as nn

# 实例化ReLU激活函数对象
# ReLU作用:负数全部变成0,正数保持不变;给神经网络引入非线性能力
relu = nn.ReLU()

# 创建一维张量,包含正负、零,用来测试 ReLU 效果
x = torch.tensor([-2.0, -1.0, 0.0, 1.0, 2.0])
# 将张量 x 送入 ReLU 做计算,得到输出y
y = relu(x)

print(x)  # → tensor([-2., -1.,  0.,  1.,  2.])
print(y)  # → tensor([0., 0., 0., 1., 2.])

为什么这么简单的东西能有用?因为它给了网络 "选择忽略" 某些信息的能力。输出是 0,意味着后面的层就收不到这个信号了。

其他常见激活函数:

名称特点什么时候用
ReLU负数清零,正数不变隐藏层默认首选
Sigmoid输出压缩到 (0, 1)二分类的最后一层
Softmax输出变成概率分布(所有值加起来=1)多分类的最后一层

对于 MNIST 的 10 分类问题,最后一层输出后跟一个 Softmax,把 10 个 "得分" 转成 10 个 "概率"。

前向传播(Forward Propagation)

前向传播 = 数据从输入层开始,一层一层往前算,直到输出层得到结果。

这名字听起来很深奥,其实就是 "数据从左到右流一遍" 而已。如下图:

用代码模拟一个简单的 3 层网络的前向传播:

import torch
import torch.nn as nn

# 定义网络结构,继承 nn.Module,所有神经网络都要继承这个基类
class SimpleNet(nn.Module):
    def __init__(self):
        super().__init__()   # 必须调用父类构造函数,初始化网络基础功能
        self.fc1 = nn.Linear(784, 128)   # 第1层全连接:784输入 → 128输出神经元
        self.fc2 = nn.Linear(128, 64)    # 第2层全连接:128输入 → 64输出神经元
        self.fc3 = nn.Linear(64, 10)     # 第3层全连接:64输入 → 10输出(0‑9类别得分)

    def forward(self, x):
        # 这就是"前向传播":一层接一层往下计算数据
        x = self.fc1(x)       # 数据流过第1层全连接
        x = torch.relu(x)     # ReLU激活函数,引入非线性,网络才能学习复杂特征
        
        x = self.fc2(x)       # 数据流过第2层全连接
        x = torch.relu(x)     # ReLU激活函数
        
        x = self.fc3(x)       # 数据流过第3层全连接,得到10个类别的原始得分
        return x              # 返回网络输出


# SimpleNet 使用示例
# 1. 创建网络实例,实例化神经网络对象
model = SimpleNet()

# 2. 构造模拟输入数据:batch_size=2,每张图片摊平为784维向量(28x28)
# 模拟2张 MNIST 图片,形状 [批次大小,784]
dummy_input = torch.randn(2, 784)
print(f"模拟输入张量形状:{dummy_input.shape}")

# 3. 执行前向传播,把数据喂给模型;自动调用 forward 函数
output = model(dummy_input)

# 4. 打印输出信息
print(f"模型输出张量形状:{output.shape}")   # [2,10],2个样本,每个样本10个类别得分
print("\n第0号样本10个类别得分:")
for cls_idx in range(10):
    print(f"类别 {cls_idx} 得分: {output[0, cls_idx].item():.4f}")

print("\n第1号样本10个类别得分:")
for cls_idx in range(10):
    print(f"类别 {cls_idx} 得分: {output[1, cls_idx].item():.4f}")

# 5. 获取预测类别:得分最大的下标就是模型预测的数字
# 张量 output 的形状 [2,10]
pred = torch.argmax(output, dim=1)
print(f"\n模型预测类别:{pred.tolist()}")

代码中,torch.argmax(output, dim=1) 用于在维度为 1上,找出最大值对应的下标索引,不是返回最大值本身,返回最大值的位置编号。

运行代码,输出如下:

模拟输入张量形状:torch.Size([2, 784])
模型输出张量形状:torch.Size([2, 10])

第0号样本10个类别得分:
类别 0 得分: 0.2391
类别 1 得分: 0.1663
类别 2 得分: 0.0741
类别 3 得分: 0.0821
类别 4 得分: -0.0421
类别 5 得分: -0.1602
类别 6 得分: 0.0773
类别 7 得分: -0.2579
类别 8 得分: -0.0625
类别 9 得分: 0.0067

第1号样本10个类别得分:
类别 0 得分: 0.1215
类别 1 得分: 0.1625
类别 2 得分: 0.0418
类别 3 得分: 0.0115
类别 4 得分: -0.0278
类别 5 得分: -0.1786
类别 6 得分: 0.2596
类别 7 得分: -0.2208
类别 8 得分: -0.0045
类别 9 得分: 0.0191

模型预测类别:[0, 6]

记住一个规律:每个 fc 层之后必须跟一个激活函数(除了最后一层可以不加,等后面用 Softmax 处理)。否则堆多少层都没意义(线性堆叠还是线性)。

nn.Module 是什么?

你注意到上面的代码有个 class SimpleNet(nn.Module)。nn.Module 是 PyTorch 里所有神经网络的父类。继承它之后,你的网络就自动获得了:

  • 管理参数的能力(model.parameters() 能列出所有要训练的参数)

  • 保存/加载模型的能力(torch.save / torch.load)

  • 搬到 GPU 的能力(model.to('cuda'))

所以你只需要在 __init__ 里定义有哪些层,在 forward 里写怎么计算,剩下的 PyTorch 自动帮你搞定。

本章小结

  • 全连接层(nn.Linear):做 y = x·W + b 的加权求和

  • 激活函数:引入非线性,让网络能学复杂模式。隐藏层用 ReLU,分类输出层用 Softmax

  • 前向传播:数据从输入层流到输出层的过程,就是 forward() 方法里写的

  • nn.Module:PyTorch 网络的基类,继承它能省很多事

点击查看完整的 搭一个简单网络试试前向传播 示例代码。

  

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号