深入浅出深度学习

  • 深度学习的本质是通过多层神经网络对数据进行特征提取和转换,从而实现对数据的理解和预测。它的核心是神经网络,神经网络是一种模拟人脑神经元结构和功能的计算模型,它由大量的神经元(节点)和连接(权重)组成,通过对输入数据进行加权求和和非线性变换,实现对数据的表示和处理。
  • 深度学习的优势在于它能够自动地从数据中学习特征,而不需要人工设计特征,这使得它在处理复杂数据(如图像、语音、文本等)时表现出色。它的应用范围非常广泛,包括计算机视觉、自然语言处理、语音识别、推荐系统等领域。
  • 本文将对深度学习的基本原理进行介绍,并使用 PyTorchPyTorch 进行构建和训练深度学习模型。

张量

几何代数中定义的张量是基于向量和矩阵的推广,比如可以将标量视为零阶张量,矢量可以视为一阶张量,矩阵就是二阶张量。

张量维度 代表含义
0维张量 代表的是标量(数字)
1维张量 代表的是向量
2维张量 代表的是矩阵
3维张量 时间序列数据 股价 文本数据 单张彩色图片(RGB)

张量是现代机器学习的基础。它的核心是一个数据容器,多数情况下,它包含数字,有时候它也包含字符串,但这种情况比较少。因此可以把它想象成一个数字的水桶。

这里有一些存储在各种类型张量的公用数据集类型:

  • 3维 = 时间序列
  • 4维 = 图像
  • 5维 = 视频

创建张量

函数 功能
Tensor(sizes) 基础构造函数
tensor(data) 类似于np.array
ones(sizes) 全1
zeros(sizes) 全0
eye(sizes) 对角为1,其余为0
arange(s,e,step) 从s到e,步长为step
linspace(s,e,steps) 从s到e,均匀分成step份
rand/randn(sizes) rand是[0,1)均匀分布;randn是服从N(0,1)的正态分布
normal(mean,std) 正态分布(均值为mean,标准差是std)
randperm(m) 随机排列

操作张量

PyTorch中的 Tensor 支持超过一百种操作,包括转置、索引、切片、数学运算、线性代数、随机数等等,具体使用方法可参考官方文档。

神经网络

在 PyTorch 中,神经网络是通过继承 torch.nn.Module 类来定义的,自定义神经网络是,通常会创建一个继承自 nn.Module 的类,并在 __init__ 方法中定义网络的层,在 forward 方法中定义前向传播的计算过程。

  1. __init(self)__: 构造函数,用于初始化网络的层(如卷积层、线性层等)、激活函数(如ReLU、Sigmoid等)以及其他需要的组件。
  2. forward(self, input_data): 前向传播函数,定义了输入数据如何通过 __init__ 中定义的层进行计算,最终输出结果。这个方法会在调用网络实例时自动执行。接收一个或多个输入张量,并返回一个或多个输出张量。

网络结构

torch.nn 提供了很多不同类型的网络结构,它们可以在 nn.Module 子类中组合起来,以构建复杂的深度学习模型,其中线性层、卷积层、循环层代表了针对不同数据和任务的基本操作。

线性层(nn.Linear)

线性层(Linear Layer)也被称为全连接层(Fully Connected Layer),它是神经网络中最基本的层之一。线性层的主要功能是对输入数据进行线性变换,即通过权重矩阵和偏置向量将输入映射到输出空间。在数学上,线性层的计算可以表示为:y=Wx+by = Wx + b,其中 xx 是输入向量,WW 是权重矩阵,bb 是偏置向量,yy 是输出向量。

线性层是许多架构中的基本组成部分,包括简单的多层感知机(MLP),并且在像 CNN 和 RNN 这样更复杂的模型中,它们通常作为最终的分类或回归头部。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import torch
import torch.nn as nn

# 示例:输入特征大小为 20,输出特征大小为 30
linear_layer = nn.Linear(in_features=20, out_features=30)

# 创建一个示例输入张量(批大小 64,20 个特征)
input_tensor = torch.randn(64, 20)

# 将输入通过该层
output_tensor = linear_layer(input_tensor)

print(f"Input shape: {input_tensor.shape}")
print(f"Output shape: {output_tensor.shape}")
# 预期输出:
# Input shape: torch.Size([64, 20])
# Output shape: torch.Size([64, 30])

# 检查层的参数(自动创建)
print(f"\nWeight shape: {linear_layer.weight.shape}")
print(f"Bias shape: {linear_layer.bias.shape}")
# 预期输出:
# Weight shape: torch.Size([30, 20])
# Bias shape: torch.Size([30])

卷积层(nn.Conv2d)

卷积层是现代计算机视觉任务中最常用的层之一。它通过卷积操作提取输入数据(通常是图像)的局部特征。它的核心工作原理是在输入空间维度(高和宽)上滑动小型滤波器,对于每个位置计算局部区域的加权和,从而生成特征图(feature map)。这个过程可以帮助模型捕捉图像中的边缘、纹理、角点和其他重要特征。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 示例:处理一批 16 张图像,3 通道(RGB),32x32 像素
# 应用 6 个滤波器(输出通道),每个大小为 5x5
conv_layer = nn.Conv2d(in_channels=3, out_channels=6, kernel_size=5)

# 创建一个示例输入张量(批大小,通道,高,宽)
# PyTorch 通常期望通道优先的格式 (N, C, H, W)
input_image_batch = torch.randn(16, 3, 32, 32)

# 将输入通过卷积层
output_feature_maps = conv_layer(input_image_batch)

print(f"Input shape: {input_image_batch.shape}")
print(f"Output shape: {output_feature_maps.shape}")
# 没有填充/步幅时,输出大小会减小:32 - 5 + 1 = 28
# 预期输出:
# Input shape: torch.Size([16, 3, 32, 32])
# Output shape: torch.Size([16, 6, 28, 28])

# 检查参数
print(f"\nWeight (filter) shape: {conv_layer.weight.shape}") # (输出通道,输入通道,卷积核高,卷积核宽)
print(f"Bias shape: {conv_layer.bias.shape}") # (输出通道)
# 预期输出:
# Weight (filter) shape: torch.Size([6, 3, 5, 5])
# Bias shape: torch.Size([6])

循环层(nn.RNN)

循环神经网络(RNN)是一类用于处理序列数据的神经网络。它们通过在时间步之间共享参数来捕捉序列中的时间依赖关系。RNN 的核心思想是使用隐藏状态(hidden state)来记忆先前时间步的信息,从而在处理当前时间步时能够参考过去的输入。比如文本、音频信号和时间序列等数据都可以使用 RNN 来建模。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 示例:处理一批 10 个序列,每个序列长 20 步,每步有 5 个特征。
# 使用大小为 30 的隐藏状态。
# 设置 batch_first=True 以便更方便地处理数据。
rnn_layer = nn.RNN(input_size=5, hidden_size=30, batch_first=True)

# 创建一个示例输入张量(批,序列长度,输入特征)
input_sequence_batch = torch.randn(10, 20, 5)

# 初始化隐藏状态(层数,批,隐藏状态大小)
# 如果未提供,默认为零。
initial_hidden_state = torch.randn(1, 10, 30) # 层数=1

# 将输入序列和初始隐藏状态通过 RNN
# 输出包含所有时间步的输出
# final_hidden_state 包含最后一个时间步的隐藏状态
output_sequence, final_hidden_state = rnn_layer(input_sequence_batch, initial_hidden_state)

print(f"Input shape: {input_sequence_batch.shape}")
print(f"Initial hidden state shape: {initial_hidden_state.shape}")
print(f"Output sequence shape: {output_sequence.shape}") # (批,序列长度,隐藏状态大小)
print(f"Final hidden state shape: {final_hidden_state.shape}") # (层数,批,隐藏状态大小)
# 预期输出:
# Input shape: torch.Size([10, 20, 5])
# Initial hidden state shape: torch.Size([1, 10, 30])
# Output sequence shape: torch.Size([10, 20, 30])
# Final hidden state shape: torch.Size([1, 10, 30])

虽然 nn.RNN 展示了基本思想,但简单的 RNN 通常因梯度消失而难以处理长序列。在实际应用中,通常更偏好 nn.LSTM(长短期记忆)和 nn.GRU(门控循环单元 (GRU))等更高级的循环层,因为它们包含门控机制,能更好地管理长距离依赖中的信息流。这些将在第 7 章再次提及。

激活函数

神经网络的表示能力很大程度上得益于在层之间引入非线性。如果只是简单地堆叠线性变换而没有任何介入函数,整个网络将简化为一个单一的等效线性变换。无论网络有多少层,它都只能学习输入与输出之间的线性关系。

激活函数是引入这些重要非线性的组成部分。它们逐元素应用于层的输出,在将值传递给下一层之前对其进行转换。PyTorch 在 torch.nn 模块中提供了各种各样的激活函数,通常通过将它们实例化为层在模型定义中使用,这里介绍最常见的三种:ReLU、Sigmoid 和 Tanh。

修正线性单元(ReLU)

ReLU(x)=max(0,x)ReLU(x) = max(0, x)

含义:是一种非线性激活函数,它将输入小于零的值置为零,而将输入大于零的值保持不变。

优点:

  • 计算简单,收敛速度快。
  • 在正区间内具有线性特性,有助于缓解梯度消失问题。
  • 由于负区间的输出为零,ReLU 具有稀疏激活的特性,有助于提高模型的表达能力。

缺点:

  • 对于输入小于零的神经元,梯度为零,可能导致“死亡神经元”问题,即这些神经元在训练过程中可能永远不会被激活。
  • 在某些情况下,ReLU 可能会导致梯度爆炸问题,尤其是在深层网络中。
  • 输出范围为 [0,+∞)[0, +\infty),可能导致输出值过大,需要注意梯度裁剪或归一化。

逻辑函数(Sigmoid)

Sigmoid(x)=11+e−xSigmoid(x) = \frac{1}{1 + e^{-x}}

含义:将任意实数平滑映射到 (0, 1) 的范围内,常用于二分类问题的输出层,输出代表一个概率值。

优点:

  • 输出范围在 (0, 1) 之间,适合表示概率。
  • 函数连续且可微,便于梯度计算。

缺点:

  • 在输入值较大或较小时,梯度接近零,容易导致梯度消失问题,影响深层网络的训练。
  • 输出不是零中心的,可能导致梯度更新方向不一致,影响收敛速度。
  • 计算指数函数相对较慢,尤其在大规模网络中可能影响训练效率。

双曲正切函数(Tanh)

Tanh(x)=ex−e−xex+e−xTanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}}

含义:将任意实数平滑映射到 (-1, 1) 的范围内,常用于隐藏层的激活函数。

优点:

  • 输出范围在 (-1, 1) 之间,具有零中心特性,有助于梯度更新方向的一致性,有助于模型在训练过程中更快地收敛。
  • 函数连续且可微,便于梯度计算。

缺点:

  • 在输入值较大或较小时,梯度接近零,容易导致梯度消失问题,影响深层网络的训练。
  • 计算指数函数相对较慢,尤其在大规模网络中可能影响训练效率。

损失函数

神经网络由堆叠的层和激活函数构建而成,每一层都可以看作是一个函数,它将输入映射到输出。神经网络的训练目标是通过调整网络的参数,使得网络的输出尽可能接近真实标签。为了衡量网络输出与真实标签之间的差距,引入了损失函数(Loss Function)。

损失函数接收网络的输出和真实标签作为输入,计算出一个标量值,表示网络预测的误差。这个误差值越小,说明网络的预测结果越接近真实标签。训练过程中,神经网络通过反向传播算法(Backpropagation)计算损失函数对网络参数的梯度,并使用优化算法(如梯度下降)更新参数,以最小化损失函数。

均方误差(MSE)

均方差误差(Mean Squared Error, MSE)是回归问题中最常用的损失函数之一。它计算预测值与真实值之间差异的平方的平均值。MSE 的公式如下:

MSE=1n∑i=1n(yi−y^i)2MSE = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2

其中 nn 是样本数量,yiy_i 是第 ii 个样本的真实值,y^i\hat{y}_i 是第 ii 个样本的预测值。

平均绝对误差(MAE)

平均绝对误差(Mean Absolute Error, MAE)是回归问题中另一种常用的损失函数。它计算预测值与真实值之间差异的绝对值的平均值。MAE 的公式如下:

MAE=1n∑i=1n∣yi−y^i∣MAE = \frac{1}{n} \sum_{i=1}^{n} |y_i - \hat{y}_i|

交叉熵损失(Cross-Entropy Loss)

交叉熵损失(Cross-Entropy Loss)是分类问题中最常用的损失函数之一。它衡量了预测概率分布与真实标签分布之间的差异。对于二分类问题,交叉熵损失的公式如下:

CE=−1n∑i=1n[yilog⁡(y^i)+(1−yi)log⁡(1−y^i)]CE = -\frac{1}{n} \sum_{i=1}^{n} [y_i \log(\hat{y}_i) + (1 - y_i) \log(1 - \hat{y}_i)]

参考文档