这篇博客瞄准的是 pytorch 官方教程中 Learning PyTorch
章节的 Deep Learning with PyTorch: A 60 Minute Blitz
部分,
- 官网链接:https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html
完整网盘链接: https://pan.baidu.com/s/1L9PVZ-KRDGVER-AJnXOvlQ?pwd=aa2m 提取码: aa2m
主要由以下四个部分组成:
- Tensor
- A Gentle Introduction to torch.autograd
- Neural Networks
- Training a Classifier
这个章节的教程打开后通常不会直接看到代码,而是需要往下拉看到类似于下图中红框部分的东西,点开后就是完整的示例:
这篇教程中包含了四个部分,为了保证笔记的结构与官网教程的一致,这里会压缩到一篇文章中。
Tensors
- 官网链接:https://pytorch.org/tutorials/beginner/blitz/tensor_tutorial.html
第一部分仍然是关于Tensor的一些基本操作,这一小节的内容与之前的笔记 Pytorch学习笔记(二)Learn the Basics - Tensors 中部分是重复的,如果你已经很熟练了可以选择性跳过一段。
- 导入必要的包:
import torch
import numpy as np
Tensor 构造与初始化
- 直接通过python数据类型初始化
data = [[1,2], [3,4]]
x_data = torch.tensor(data)
- 通过其他tensor初始化
x_ones = torch.ones_like(x_data)
print(f"Ones Tensor:\n{x_ones}")x_rand = torch.rand_like(x_data, dtype=torch.float)
print(f"Rand Tensor:\n{x_rand}")
- 指定shape的变量与常量初始化:
shape = (2,3,)
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)print(f"Random Tensor:\n{rand_tensor}")
print(f"Ones Tensor:\n{ones_tensor}")
print(f"Zeros Tensor:\n{zeros_tensor}")
Tensor 的三大属性
tensor对象最常用的三个属性为 shape
、dtype
、device
:
tensor = torch.rand(3, 4)print(f"Shape: {tensor.shape}")
print(f"Dtype: {tensor.dtype}")
print(f"Device: {tensor.device}")
Tensor 的操作
- 将tensor移动到cuda上
if torch.cuda.is_available():tensor = tensor.to('cuda')print(f"Device tensor is stored on : {tensor.device}")
- 类似numpy形式的切片使用
tensor = torch.ones(4,4)
tensor[:,1] = 0
print(tensor)
- 将两个tensor按照指定维度拼接
t1 = torch.cat([tensor, tensor], dim=1)
print(t1)
- tensor按照元素 点乘
print(f"Tensor.mul:\n{tensor.mul(tensor)}")
print(f"Tensor *:\n{tensor * tensor}")
- tensor 叉乘
print(f"Tensor.matmul:\n{tensor.matmul(tensor.T)}")
print(f"Tensor @:\n{tensor @ tensor.T}")
- 就地操作:
print(f"Tensor:\n{tensor}")
tensor.add_(5)
print(f"Tensor:\n{tensor}")
Bridge with NumPy
- Tensor -> Numpy
t = torch.ones(5)
print(f"t:\n{t}")
n = t.numpy()
print(f"n:\n{n}")
- Numpy -> Tensor
n = np.array((2,4))
print(f"n:\n{n}")
t = torch.from_numpy(n)
print(f"t:\n{t}")
A Gentle Introduction to torch.autograd
- 官网链接: https://pytorch.org/tutorials/beginner/blitz/autograd_tutorial.html
这是该章节的第二部分,主要介绍了pytorch的自动梯度机制。torch.autograd
是 PyTorch 的自动微分引擎,为神经网络训练提供支持。
背景知识
神经网络 (NN) 是一些嵌套函数的集合,这些函数由参数(权重+偏置值)定义,参数存储在Tensor中。
训练 NN 分为两个步骤:
- 前向传播:NN 对输出做出推理,通过每个函数运行输入数据以做出此猜测;
- 反向传播:NN 根据推理中的误差调整参数,它通过从输出向后遍历、收集误差相对于函数参数的导数(梯度)并使用梯度下降优化来调整参数;
梯度传播演示
这里加载了一个 ResNet18 的参数并定义了一对无意义的sample用来掩饰pytorch是如进行梯度传播,运行后会自动下载 ResNet18 模型参数:
import torch
from torchvision.models import resnet18, ResNet18_Weights
加载模型及参数
model = resnet18(weights=ResNet18_Weights.DEFAULT)
data = torch.rand(1,3,64,64)
labels = torch.rand(1,1000)
执行推理:
prediction = model(data)
计算loss并反向传播
loss = (prediction - labels).sum()
loss.backward()
定义优化器并执行梯度优化
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)
optimizer.step()
具体梯度计算
这部分主要介绍了pytorch如和计算梯度,包含了一些数据概念,本质上就是对变量进行求导。
定义两个Tensor
a = torch.tensor([2.0, 3.0], requires_grad=True)
b = torch.tensor([6.0, 4.0], requires_grad=True)
定义另外一个Tensor为 Q = 3 a 3 − b 2 Q=3a^{3}-b^{2} Q=3a3−b2:
Q = 3*a**3 - b**2
手动计算Tensor Q的梯度:
external_grad = torch.tensor([1.0, 1.0])
Q.backward(gradient=external_grad)print(9*a**2 == a.grad)
print(-2*b == b.grad)
Neural Networks
- 官网链接:https://pytorch.org/tutorials/beginner/blitz/neural_networks_tutorial.html#neural-networks
可以使用 torch.nn
构建神经网络。nn
依赖 autograd
来定义模型并对其进行区分。以经典的Alex Net模型为例:
神经网络的典型训练过程如下:
- 定义具有一些可学习参数(或权重)的神经网络;
- 迭代输入数据集;
- 模型处理输入;
- 计算损失(输出与正确的差值);
- 将梯度传播回网络参数;
- 更新网络权重,通常使用简单的更新规则:
权重 = 权重 - 学习率 * 梯度
;
定义模型
导入依赖包
import torch
import torch.nn as nn
import torch.nn.functional as F
定义模型结构
class Net(nn.Module):def __init__(self):super().__init__()self.conv1 = nn.Conv2d(1,6,5)self.conv2 = nn.Conv2d(6,16,5)self.fc1 = nn.Linear(16*5*5, 120)self.fc2 = nn.Linear(120, 84)self.fc3 = nn.Linear(84, 10)def forward(self, x):c1 = F.relu(self.conv1(x))s2 = F.max_pool2d(c1, (2,2))c3 = F.relu(self.conv2(s2))s4 = F.max_pool2d(c3, 2)s4 = torch.flatten(s4, 1)f5 = F.relu(self.fc1(s4))f6 = F.relu(self.fc2(f5))output = self.fc3(f6)return output
实例化模型
net = Net()
print(net)
只需定义 前向函数 和 反向函数(计算梯度的地方),autograd 会自动生成匹配的参数。
params = list(net.parameters())
print(len(params))
print(params[0].size())
模型计对随机数进行推理
input = torch.rand(1,1,32,32)
out = net(input)
print(out)
清空模型梯度并用随机数填充输出Tensor的梯度
net.zero_grad()
out.backward(torch.randn(1,10))
定义损失函数
损失函数采用(输出,目标)输入对,并计算一个值来估计输出与目标之间的差。nn
包下有几种不同的损失函数。最简单的损失函数是:nn.MSELoss
计算输出和目标之间的均方误差。
output = net(input)
target = torch.rand(10)
target = target.view(1, -1)
criterion = nn.MSELoss()
计算损失值:
loss = criterion(output, target)
print(loss)
查看部分层的损失梯度:
print(loss.grad_fn)
print(loss.grad_fn.next_functions[0][0])
print(loss.grad_fn.next_functions[0][0].next_functions[0][0])
只需执行 loss.backward()
就可以反向传播loss。但首先要清除现有梯度,否则梯度将累积到现有梯度中。
net.zero_grad()
print("conv1.bias.grad before backward")
print(net.conv1.bias.grad)loss.backward()print("conv1.bias.grad after backward")
print(net.conv1.bias.grad)
更新模型参数
手动对模型中的值进行一次修改
learning_rate = 0.01
for f in net.parameters():f.data.sub_(f.grad.data * learning_rate)
定义优化器
optimizer = torch.optim.SGD(net.parameters(), lr=0.01)
优化器根据loss值更新模型参数
optimizer.zero_grad()
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step()
Training a Classifier
- 官网链接: https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html
当需要处理图像、文本、音频或视频数据时,可以将数据加载到 numpy 数组中的标准 python 包,然后转换为 torch.*Tensor
。
- 对于图像:Pillow、OpenCV 等包;
- 对于音频:scipy 和 librosa 等包;
- 对于文本:原始 Python 或 Cython 的加载,或 NLTK 和 SpaCy 等包;
pytorch为于视觉提供了一个名为 torchvision 的包,内嵌了常见数据集(如 ImageNet、CIFAR10、MNIST 等)加载器,以及用于图像的数据转换器,即 torchvision.datasets
和 torch.utils.data.DataLoader
。
在这里将使用 CIFAR10
数据集。它有以下类别:‘airplane’, ‘automobile’, ‘bird’, ‘cat’, ‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’. 。CIFAR-10
中的图像大小为 3x32x32
,即 32x32
像素大小的 3
通道彩色图像。
按照下面的步骤训练图像分类神经网络:
- 使用
torchvision
加载并规范化CIFAR10
训练和测试数据集; - 定义卷积神经网络;
- 定义损失函数;
- 在训练数据上训练网络;
- 在测试数据上测试网络;
数据预处理
导入依赖包
import torch
import torchvision
import torchvision.transforms as transforms
定义图像数据转化器
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]
)
加载训练集与测试集
train_set = torchvision.datasets.CIFAR10(root="data", train=True, download=True, transform=transform)
test_set = torchvision.datasets.CIFAR10(root="data", train=True, download=True, transform=transform)classes = ('plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck')
定义训练集与测试集加载器
batch_size = 4train_loader = torch.utils.data.DataLoader(train_set, batch_size=batch_size, shuffle=True, num_workers=2)
test_loader = torch.utils.data.DataLoader(test_set, batch_size=batch_size, shuffle=True, num_workers=2)
抽看四张图像
import matplotlib.pyplot as plt
import numpy as npdef imshow(img):img = img/2 + 0.5npimg = img.numpy()plt.imshow(np.transpose(npimg, (1,2,0)))plt.show()dataiter = iter(train_loader)
images, labels = next(dataiter)imshow(torchvision.utils.make_grid(images))
print(' '.join(f'{classes[labels[j]]:5s}' for j in range(batch_size)))
定义神经网络
import torch.nn as nn
import torch.nn.functional as Fclass Net(nn.Module):def __init__(self):super().__init__()self.conv1 = nn.Conv2d(3, 6, 5)self.pool = nn.MaxPool2d(2, 2)self.conv2 = nn.Conv2d(6, 16, 5)self.fc1 = nn.Linear(16*5*5, 120)self.fc2 = nn.Linear(120, 84)self.fc3 = nn.Linear(84, 10)def forward(self, x):x = self.pool(F.relu(self.conv1(x)))x = self.pool(F.relu(self.conv2(x)))x = torch.flatten(x,1)x = F.relu(self.fc1(x))x = F.relu(self.fc2(x))x = self.fc3(x)return xnet = Net()
定义损失函数与优化器
import torch.optim as optimcriterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
训练模型
for epoch in range(2):running_loss = 0.0for i, data in enumerate(train_loader, 0):inputs, labels = dataoptimzer.zero_grad()outputs = net(inputs)loss = criterion(outputs, labels)loss.backward()optimzer.step()running_loss += loss.item()if i % 2000 == 1999:print(f"Epoch [{epoch+1}]: {i+1:5d} loss:{running_loss / 2000:3f}")runnling_loss = 0.0print("Done")
在测试集上测试
dataiter = iter(test_loader)
images, labels = next(dataiter)imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join(f'{classes[labels[j]]:5s}' for j in range(4)))outputs = net(images)_, predicted = torch.max(outputs, 1)print('Predicted: ', ' '.join(f'{classes[predicted[j]]:5s}'for j in range(4)))
对整个测试集进行测试
correct = 0
total = 0with torch.no_grad():for data in test_loader:images, labels = dataoutputs = net(images)_, predicted = torch.max(outputs, 1)total += labels.size(0)correct += (predict == labels).sum().item()print(f'Accuracy of the network on the 10000 test images: {100 * correct // total} %')