第1章:引言——AI入门的“Hello, World”

在人工智能的广阔世界中,神经网络是其核心驱动力之一。当讨论神经网络时,许多人首先联想到的是其复杂而精巧的结构,这种结构的设计灵感正源于我们最熟悉的事物——人脑。就像人脑通过无数神经圆相互连接、传递电信号来处理信息、做出决策一样,人工神经网络也由称为“节点”的软件模块(人工神经元)构成,他们协同工作以解决复杂问题

对于初学者而言,直接面对复杂的神经网络架构可能会感到无所适从。因此,找到一个合适的切入点至关重要。本文将聚焦于一种最基础、最经典的神经网络架构——全连接网络(Fully Connected Network, FCN),并将其视为人工智能学习旅程中的“Hello, World”程序。全连接网络以其直观的层次结构和清晰的运作原理,为理解更复杂的模型(如卷积神经网络和循环神经网络)奠定了坚实基础。

本报告旨在通过一个经典的计算机视觉任务——手写数字识别,带领读者从零开始,逐步掌握全连接网络的基本概念、工作原理,并通过PyTorch框架实现一个功能完整的AI模型。本文将不仅仅停留在理论层面,更会提供详尽的代码示例和图示,确保读者能够亲自动手,构建出自己的第一个AI“大脑”,并对其中的每一个知识点都能有透彻的理解。

第2章:庖丁解牛——全连接网络的“骨骼”与“肌肉”

理解全连接网络,需先从其最基本的组成元素——神经元开始。一个完整的全连接网络就像一个由层层相连的神经元构成的复杂系统,数据的处理过程则如同信息在这些层级间的高速流动。本章将详细剖析这一网络架构的几个核心组件,并阐明它们如何协同工作。

2.1 神经元:一个“迷你计算器”

神经元是构成神经网络的基本处理单元 [2]。在人工神经网络中,一个神经元可以被看作一个“迷你计算器”,它接收来自上一层的多个输入信号,对这些输入进行处理,然后产生一个输出信号,并将其传递给下一层的神经元 [3]。这个处理过程通常包含三个关键步骤:加权、求和与激活 [2, 3]。每个神经元都与其上一层的所有神经元相连接,这种“全连接”的特性正是该网络名称的由来 [3]

2.2 权重(Weight)与偏置(Bias):学习的“魔法”

在神经元接收到的多个输入信号中,并非所有信号都同等重要。为了反映这种重要性的差异,神经网络引入了**权重(Weight)**的概念 [2]。每个输入信号都附带一个权重值,这个数值可以放大、缩小或反转输入信号,以决定其对神经元总和的影响力 [2]。在训练过程中,模型正是通过不断调整这些权重,来“学习”输入数据中不同特征的重要性。

除了权重,每个神经元还带有一个偏置(Bias) [2]。偏置是一个独立的数值,它被加到所有加权输入之和上 [2]。偏置的作用可以被形象地理解为神经元的“微调旋钮”或“底线”。它允许神经元在没有接收任何有效输入时仍然产生一个输出,或者在面对微弱输入时更容易被激活。这为模型提供了额外的自由度,使其能够更灵活地拟合数据。一个神经元的计算过程可以用以下公式表示 [4]

output=activation(i∑​wi​xi​+bias)

目录

全连接网络:从零开始,用代码构建你的第一个AI“大脑”

第1章:引言——AI入门的“Hello, World”

第2章:庖丁解牛——全连接网络的“骨骼”与“肌肉”

2.1 神经元:一个“迷你计算器”

2.2 权重(Weight)与偏置(Bias):学习的“魔法”

2.3 激活函数(Activation Function):决策的“开关”

2.4 前向传播:数据在网络中的旅程

第3章:动手实践——用PyTorch构建手写数字识别器

3.1 准备数据:MNIST的魔力

3.2 模型构建:层层叠叠的神经元

3.3 训练与评估:让模型“学习”起来

3.4 成果展示:看看你的模型表现如何!

第4章:深度剖析——全连接网络:是起点,不是终点

4.1 局限性:为何它不适用于处理图像?

4.2 全连接网络 vs. 卷积网络 vs. 循环网络

第5章:总结与展望——你的AI学习之路才刚刚开始


2.3 激活函数(Activation Function):决策的“开关”

在神经元完成加权求和并加上偏置后,其结果会传递给激活函数(Activation Function) [2]。激活函数的作用是决定神经元的最终输出值 [2],它就像一个“开关”,决定了神经元是否应该被“激活”并向下一层传递数据 [4]

激活函数最核心的作用在于引入非线性。如果一个神经网络中所有神经元都只进行线性计算,那么无论网络有多少层,其最终输出都只是输入的一个线性组合。这样的网络无法学习和处理复杂的非线性问题,例如图像分类、语音识别等。激活函数通过对线性输出进行非线性变换,赋予了神经网络学习复杂模式的“超能力” [5]

常见的激活函数有:

  • ReLU(Rectified Linear Unit):当输入值小于0时,输出为0;当输入值大于0时,输出等于输入值。其简单高效的特性使其成为目前最常用的激活函数之一 [5]

  • Softmax:常用于输出层,将多个神经元的输出值转换成概率分布,确保所有输出值的和为1,这对于多类别分类任务至关重要 [6]

2.4 前向传播:数据在网络中的旅程

将上述所有组件串联起来,数据在全连接网络中的旅程被称为前向传播(Forward Propagation) [3]。这个过程是一个单向的数据流,从网络的输入层开始,逐层向前传递,直到输出层产生最终结果。

  1. 输入层:数据(例如一张手写数字图片)进入网络,由输入层的神经元接收 ``。

  2. 隐藏层:输入信号被传递到第一层隐藏层。每个隐藏层中的神经元都对来自上一层的所有输入信号进行加权求和,然后通过激活函数处理 ``。处理后的结果作为新的输入,继续传递给下一层。

  3. 输出层:数据流经所有隐藏层后,最终到达输出层 ``。输出层的神经元会给出网络对输入数据的最终预测结果。例如,在数字识别任务中,输出层可能有10个神经元,分别对应0到9这10个数字,输出值最高的神经元代表了网络的预测结果。

第3章:动手实践——用PyTorch构建手写数字识别器

本章将通过一个完整的代码示例,带领读者使用强大的深度学习框架PyTorch,从零开始构建一个可以识别手写数字的全连接网络。

3.1 准备数据:MNIST的魔力

在动手写代码之前,选择一个合适的数据集至关重要。MNIST手写数字数据集被誉为深度学习领域的“Hello, World”,它包含了6万张28x28像素的训练图像和1万张测试图像 [7]。这个数据集之所以成为初学者的首选,不仅在于其易于获取和处理 [8],更因为它是一个典型的“低维”数据,没有复杂的空间结构,非常适合全连接网络作为入门案例来学习,让学习者可以将精力集中在理解网络本身,而非复杂的数据预处理上。

下面是加载和预处理MNIST数据集的Python代码:

import torch
import torch.nn as nn
from torchvision import datasets, transforms
import matplotlib.pyplot as plt

# 定义数据转换:将图像转换为张量并归一化到
transform = transforms.ToTensor()

# 加载MNIST数据集
train_dataset = datasets.MNIST(root='./data', train=True, transform=transform, download=True)
test_dataset = datasets.MNIST(root='./data', train=False, transform=transform, download=True)

# 创建数据加载器,用于批量处理数据
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=64, shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=64, shuffle=False)

# 可视化部分数据样本
fig, axes = plt.subplots(5, 5, figsize=(8, 8))
for i, ax in enumerate(axes.flat):
    image, label = train_dataset[i]
    ax.imshow(image.squeeze(), cmap='gray')
    ax.set_title(f"Label: {label}")
    ax.axis('off')
plt.tight_layout()
plt.show()

3.2 模型构建:层层叠叠的神经元

全连接网络通常被称为多层感知机(MLP),它由多个全连接层堆叠而成。在PyTorch中,可以使用nn.Sequential容器来快速构建一个全连接网络 [9]

下面是构建用于MNIST分类的全连接网络模型的代码:

Python

# 确定设备,优先使用GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using device: {device}")

# 定义全连接网络模型
model = nn.Sequential(
    # nn.Flatten()层将输入的28x28像素图像展平为1x784的向量。
    # 这是一个关键步骤,因为全连接层nn.Linear()的输入必须是一维向量。
    nn.Flatten(),
    # 第一个全连接层(隐藏层):将784个输入神经元连接到128个隐藏神经元。
    nn.Linear(28*28, 128),
    # 使用ReLU激活函数引入非线性。
    nn.ReLU(),
    # 第二个全连接层(隐藏层):将128个神经元连接到64个隐藏神经元。
    nn.Linear(128, 64),
    # 再次使用ReLU激活函数。
    nn.ReLU(),
    # 输出层:将64个隐藏神经元连接到10个输出神经元,分别对应数字0-9。
    nn.Linear(64, 10)
).to(device)

print(model)

3.3 训练与评估:让模型“学习”起来

模型的训练是一个迭代优化的过程,其核心思想是“试错学习” ``。这个过程通常包含三个关键部分:

  • 损失函数(Loss Function):衡量模型预测结果与真实标签之间的误差。误差越大,损失值越高。

  • 优化器(Optimizer):根据损失函数计算出的误差,反向传播(Backpropagation) [4],调整模型的权重和偏置,以减少误差。

  • 训练循环:反复执行前向传播、损失计算、反向传播和参数更新,直到模型收敛或达到预设的训练周期 ``。

下面是完整的训练和评估代码:

Python

# 导入所需的库
import torch.optim as optim
from tqdm import tqdm

# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 定义训练函数
def train_model(model, train_loader, criterion, optimizer, num_epochs=10):
    model.train() # 设置模型为训练模式
    for epoch in range(num_epochs):
        running_loss = 0.0
        for i, (images, labels) in tqdm(enumerate(train_loader), total=len(train_loader), desc=f"Epoch {epoch+1}"):
            # 将数据移动到指定设备
            images, labels = images.to(device), labels.to(device)

            # 前向传播:计算预测值
            outputs = model(images)
            # 计算损失
            loss = criterion(outputs, labels)

            # 反向传播和参数更新
            optimizer.zero_grad() # 梯度清零
            loss.backward()      # 反向传播,计算梯度
            optimizer.step()     # 更新参数

            running_loss += loss.item()
        
        # 打印每个周期的平均损失
        print(f"Epoch {epoch+1}, Loss: {running_loss / len(train_loader):.4f}")

# 定义评估函数
def evaluate_model(model, test_loader):
    model.eval() # 设置模型为评估模式
    correct = 0
    total = 0
    with torch.no_grad(): # 在评估时,无需计算梯度
        for images, labels in test_loader:
            images, labels = images.to(device), labels.to(device)
            outputs = model(images)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()

    accuracy = 100 * correct / total
    print(f"Accuracy on test set: {accuracy:.2f}%")
    return accuracy

# 训练并评估模型
train_model(model, train_loader, criterion, optimizer, num_epochs=10)
evaluate_model(model, test_loader)

3.4 成果展示:看看你的模型表现如何!

在模型训练完成后,我们可以编写代码来随机抽取测试集中的一些图片,并展示模型的预测结果,从而直观地检验模型的学习效果。

Python

import random
import numpy as np

# 从测试集中随机选择9张图片
test_images_to_show =
test_labels_to_show =
for i in random.sample(range(len(test_dataset)), 9):
    test_images_to_show.append(test_dataset[i])
    test_labels_to_show.append(test_dataset[i])

# 将图像张量堆叠成一个批次
stacked_images = torch.stack(test_images_to_show).to(device)

# 进行预测
model.eval()
with torch.no_grad():
    outputs = model(stacked_images)
    _, predicted_labels = torch.max(outputs.data, 1)

# 可视化预测结果
fig, axes = plt.subplots(3, 3, figsize=(8, 8))
for i, ax in enumerate(axes.flat):
    image = test_images_to_show[i]
    label = test_labels_to_show[i]
    predicted_label = predicted_labels[i].item()
    
    ax.imshow(image.squeeze().cpu(), cmap='gray')
    title_color = 'green' if predicted_label == label else 'red'
    ax.set_title(f"True: {label}\nPred: {predicted_label}", color=title_color)
    ax.axis('off')

plt.tight_layout()
plt.show()

第4章:深度剖析——全连接网络:是起点,不是终点

尽管全连接网络在手写数字识别等简单任务中表现出色,但其在更复杂的应用场景中存在显著的局限性。客观地认识这些局限性,有助于理解为何在特定领域需要更专业的网络架构。

4.1 局限性:为何它不适用于处理图像?

全连接网络在处理高分辨率图像时,会面临一个严重的**“参数爆炸”**问题 [10]。一张百万像素的图片,其输入维度高达 106。如果将其输入到全连接网络,即使隐藏层只有1000个神经元,第一层网络所需的参数数量就高达 106×103=109 个 [10]

如此庞大的参数量带来了两个致命问题:

  1. 计算与存储的巨大开销:训练和部署这样一个模型需要海量的计算资源和存储空间,在实际应用中几乎不可行。

  2. 过拟合风险:参数越多,模型拟合训练数据的能力越强,但泛化能力越差。这意味着模型会过度“记忆”训练样本的特征,但在面对未知的测试数据时表现不佳。

正是由于这些局限性,在处理图像、视频这类具有空间结构的数据时,全连接网络被更高效的架构所取代,例如卷积神经网络(CNN)。

4.2 全连接网络 vs. 卷积网络 vs. 循环网络

为了更清晰地理解全连接网络在整个神经网络家族中的位置,下表对其与两种主流网络架构——卷积网络(CNN)和循环网络(RNN)进行了对比 [11, 12]

值得注意的是,“全连接网络”这一术语在不同语境下可能指代不同概念。在基础理论中,它通常指多层感知机(MLP)。而在计算机视觉领域,另一个相似的术语**“全卷积网络”(Fully Convolutional Network, FCN)** [13, 14] 指的是一种特殊的卷积网络,其特点是用卷积层取代了传统CNN末端的全连接层 [14],使其能够处理任意尺寸的图像,并实现像素级的分类(语义分割)。这恰好是全连接网络无法高效完成的任务。

特性 全连接网络 (MLP) 卷积网络 (CNN) 循环网络 (RNN)
典型应用 表格数据分类、简单回归 图像分类、物体检测、语义分割 文本生成、语音识别、时间序列预测
核心特点 层内全连接,每个神经元与下一层所有神经元相连。 局部连接、参数共享,通过卷积核提取空间特征 [10] 拥有内部记忆(反馈循环),可处理可变长度的序列 [11]
适用数据 向量化的表格数据、简单一维数据。 图像、视频等网格化数据,擅长捕获空间特征。 文本、语音、时间序列,擅长处理时序依赖 [11]
主要优点 概念简单,是所有神经网络的基石。 参数少,计算高效,能自动学习空间特征 [10] 能处理任意长度序列,具有记忆能力 [11]
主要局限 参数多,不擅长处理高维图像。 无法处理可变长度的序列数据 [11] 训练效率低,难以捕获长期依赖 [15]

第5章:总结与展望——你的AI学习之路才刚刚开始

通过本文的学习,读者已经迈出了人工智能学习之旅的第一步。我们从神经元、权重、偏置和激活函数等基本概念入手,深入理解了全连接网络的工作原理。随后,我们通过一个完整的、可运行的PyTorch代码示例,亲手实现了一个手写数字识别器。最后,我们对全连接网络的优缺点进行了客观评估,并将其与卷积网络和循环网络进行了对比,从而建立了一个更宏观的认知框架。

全连接网络是所有神经网络的基石。它简单、直观,是理解更复杂模型的起点。正如《MNIST数据集预测》一文中所述,MNIST是“机器学习的‘Hello World’程序” [8]。通过完成这个项目,读者已经掌握了神经网络的核心思想和基本的实践流程,这为未来探索更广阔的AI领域奠定了坚实的基础。

学习之路才刚刚开始。在掌握全连接网络之后,可以进一步探索以下方向:

  • 卷积神经网络(CNN):专为处理图像而设计,了解其如何通过卷积层和池化层高效地提取空间特征。

  • 循环神经网络(RNN):专为处理序列数据而设计,了解其如何通过记忆单元处理文本、语音等时序信息。

  • 更深、更复杂的模型:如ResNet、Transformer等,它们是现代AI领域的基石。

每一次代码的敲击,每一次模型的训练,都将是在AI世界里的一次新的探索。愿本文成为您的起点,引领您走向更深邃、更精彩的人工智能世界。

Logo

纵情码海钱塘涌,杭州开发者创新动! 属于杭州的开发者社区!致力于为杭州地区的开发者提供学习、合作和成长的机会;同时也为企业交流招聘提供舞台!

更多推荐