Data Representation in PyTorch

The Tensor in PyTorch is actually a multidimensional array

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import torch
import numpy as np
# 类比于C++中的include
data = [[1, 2], [3, 4]]
# []代表多维(2x2)数组
x_data = torch.tensor(data)
# 转换成pytorch的tensor,将Python列表转换为PyTorch张量,数据类型根据输入自动推断
np_array = np.array(data)
# 将Python列表转换为NumPy数组,方便与NumPy库进行后续操作
x_np = torch.from_numpy(np_array)
# 将NumPy数组转换为PyTorch张量,这样可以利用NumPy的操作,且数据存储方式相同
x_rand = torch.rand_like(x_data, dtype=torch.float)
# 创建一个与x_data形状相同的张量,初始化,生成浮点数的噪音
x_ones = torch.ones_like(x_data)
# 创建一个与x_data形状相同的张量,所有元素值为1,数据类型与x_data相同
tensor = tensor.to('cuda')
# 把cpu储存编程gpu储存

Load Data

CiFAR dataset: training – 50k, testing – 10k, 10 categories

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import torch
import torchvision
batch_size = 4
# 定义批量大小为4,用于每次加载4张图片

# 定义数据预处理操作:首先将图像转换为张量,然后对其进行标准化处理
transform = torchvision.transforms.Compose(
[transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
# 将PIL图像或NumPy数组转换为PyTorch张量
# 0-1之间,先进行归一化,再转换成tensor

# 加载 CIFAR-10 训练集,并对其应用预处理操作
trainset = torchvision.datasets.CIFAR10(
root='./data', train=True, download=True, transform=transform)

# 使用 DataLoader 以小批量的方式加载数据
trainloader = torch.utils.data.DataLoader(
trainset, batch_size=batch_size, shuffle=True, num_workers=2)
# get some random training images
dataiter = iter(trainloader)
# 创建一个可迭代的训练数据加载器
images, labels = next(dataiter)
# 获取下一个批次的图像和标签

Implement LeNet

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
27
28
29
30
31
32
33
34
35
class LeNet(torch.nn.Module):  
# 定义一个继承自torch.nn.Module的LeNet类,代表LeNet神经网络
def __init__(self):
# self可以类比于C++中的this
# 构造函数,用于初始化网络的各层
super().__init__()
# 调用父类的初始化函数
self.conv1 = nn.Conv2d(3, 6, 5)
# 定义第一个卷积层,输入通道数为3(RGB图像),输出通道数为6,卷积核大小为5x5
self.pool = nn.MaxPool2d(2, 2)
# 定义一个2x2的最大池化层,用于下采样,步长为2
self.conv2 = nn.Conv2d(6, 16, 5)
# 定义第二个卷积层,输入通道数为6,输出通道数为16,卷积核大小为5x5
self.fc1 = nn.Linear(16 * 5 * 5, 120)
# 定义第一个全连接层,输入大小为16*5*5(经过卷积和池化后的特征图展平),输出大小为120
self.fc2 = nn.Linear(120, 84)
# 定义第二个全连接层,输入大小为120,输出大小为84
self.fc3 = nn.Linear(84, 10)
# 定义第三个全连接层,输入大小为84,输出大小为10(对应10个分类)
def forward(self, x):
# 前向传播函数,定义数据如何经过每一层
x = self.pool(F.relu(self.conv1(x)))
# 输入x经过第一个卷积层,然后通过ReLU激活函数,再经过池化层
x = self.pool(F.relu(self.conv2(x)))
# 输入x经过第二个卷积层,然后通过ReLU激活函数,再经过池化层
x = torch.flatten(x, 1)
# 将x展平成一维向量,跳过第一个维度(批次维度),从第一个维度开始展平,拉成一个长数组
x = F.relu(self.fc1(x))
# 经过第一个全连接层,再通过ReLU激活函数
x = F.relu(self.fc2(x))
# 经过第二个全连接层,再通过ReLU激活函数
x = self.fc3(x)
# 经过第三个全连接层,得到最终输出,不再经过激活函数
return x
# 返回前向传播的输出

Auto-Differentiation in PyTorch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
net = Net()  
# 实例化自定义的神经网络 `Net`(可以假设 `Net` 是 LeNet 的实现类)
print(net)
# 打印网络的结构和参数,帮助我们查看网络的各层以及参数数量
input = torch.randn(1, 1, 32, 32)
# 生成一个形状为 (1, 1, 32, 32) 的随机输入张量,表示批量大小为1,单通道的32x32大小图像
out = net(input)
# 将输入张量 `input` 传递给网络 `net`,执行前向传播,得到网络的输出 `out`
params = list(net.parameters())
# list可以类比成C++中的vector
# 将网络的所有可学习参数(权重和偏置)转换为一个列表,并存储在 `params` 中
criterion = nn.CrossEntropyLoss()
# 定义交叉熵损失函数,用于计算分类任务中的损失。通常用于多分类问题
loss = criterion(out, target)
# 计算网络输出 `out` 与目标 `target` 之间的损失值,`target` 是正确的类别标签
net.zero_grad()
# 将网络中所有参数的梯度清零,避免梯度累积,因为 PyTorch 默认会累积梯度
loss.backward(torch.randn(1, 10))
# 计算损失函数的梯度,并执行反向传播,`torch.randn(1, 10)` 表示自定义的梯度值

Optimization in PyTorch

Implement the gradient decent in python

1
2
3
4
5
6
7
8
9
learning_rate = 0.001  
# 定义学习率,表示每次更新权重时的步长为0.001,或者0.0005也常用
for weights in net.parameters():
# 遍历神经网络 `net` 中所有可学习的参数(权重和偏置)
grad = weights.grad + 2 * weights
# 计算梯度更新公式,其中 `weights.grad` 是当前权重的梯度,`2 * weights` 是L2正则化的项
weights.data = weights.data - grad * learning_rate
# 更新权重,使用梯度下降法:新权重 = 旧权重 - 学习率 * 梯度
# `weights.data` 访问权重的实际值,并进行就地更新

Parameters: Tensors with gradients

1
2
3
4
5
6
7
8
9
10
11
12
x = torch.rand(5, 5)  
# 生成一个 5x5 的随机张量 `x`,所有值均在 [0, 1) 范围内,默认不需要计算梯度
y = torch.rand((5, 5), requires_grad=True)
# 生成一个 5x5 的随机张量 `y`,并设置 `requires_grad=True`,表示该张量在计算图中会追踪梯度,对函数求梯度
a = x + y
# 进行张量 `x` 和 `y` 的逐元素相加操作,得到结果 `a`,这一步操作会被记录在计算图中
out = a.sum()
# 将张量 `a` 中所有元素求和,得到标量 `out`,因为 `a` 包含了 `y` 的操作,所以 `out` 会依赖于 `y`
out.backward()
# 计算标量 `out` 对 `y` 的梯度,进行反向传播操作,PyTorch 会根据计算图自动计算梯度
print(y.grad)
# 打印张量 `y` 的梯度,这里 `y.grad` 包含了 `out` 对 `y` 的偏导数(梯度)
1
2
3
4
5
6
7
>>> y.grad
tensor([[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.]])