本篇博客是学习B站霹雳吧啦Wz教学视频的总结
本节所用到的程序和教学视频链接:
程序结构
- 模型搭建
- 训练,train
- 预测,predict
1 模型搭建:
import torch.nn as nn
import torch.nn.functional as F
class LeNet(nn.Module):
#初始化函数,网络层结构
def __init__(self):
super(LeNet, self).__init__()#
self.conv1 = nn.Conv2d(3, 16, 5)
self.pool1 = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(16, 32, 5)
self.pool2 = nn.MaxPool2d(2, 2)
self.fc1 = nn.Linear(32*5*5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
#正向传播
def forward(self, x):
x = F.relu(self.conv1(x)) # input(3, 32, 32) output(16, 28, 28)
x = self.pool1(x) # output(16, 14, 14)
x = F.relu(self.conv2(x)) # output(32, 10, 10)
x = self.pool2(x) # output(32, 5, 5)
x = x.view(-1, 32*5*5) # output(32*5*5)
x = F.relu(self.fc1(x)) # output(120)
x = F.relu(self.fc2(x)) # output(84)
x = self.fc3(x) # output(10)
return x
- super:类函数继承
- Conv2d: 卷积函数
- MaxPool2d: 池化层
- Linear:全连接层
- x:输入图片集;按照[batch,channel,height,weight]的顺序排列
- view:重构张量的维度, -1代表自动推理
- 输出层没有使用softmax的原因是:在计算交叉熵函数时内部已经有softmax的计算
2 训练
import torch
import torchvision
import torch.nn as nn
from model import LeNet
import torch.optim as optim
import torchvision.transforms as transforms
def main():
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
# 50000张训练图片
# 第一次使用时要将download设置为True才会自动去下载数据集
train_set = torchvision.datasets.CIFAR10(root='./data', train=True,
download=False, transform=transform)
train_loader = torch.utils.data.DataLoader(train_set, batch_size=36,
shuffle=True, num_workers=0)
# 10000张验证图片
# 第一次使用时要将download设置为True才会自动去下载数据集
val_set = torchvision.datasets.CIFAR10(root='./data', train=False,
download=False, transform=transform)
val_loader = torch.utils.data.DataLoader(val_set, batch_size=5000,
shuffle=False, num_workers=0)
val_data_iter = iter(val_loader)
val_image, val_label = val_data_iter.next()
# classes = ('plane', 'car', 'bird', 'cat',
# 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
net = LeNet()
loss_function = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), lr=0.001)
for epoch in range(5): # loop over the dataset multiple times
running_loss = 0.0
for step, data in enumerate(train_loader, start=0):
# get the inputs; data is a list of [inputs, labels]
inputs, labels = data
# zero the parameter gradients
optimizer.zero_grad()
# forward + backward + optimize
outputs = net(inputs)
loss = loss_function(outputs, labels)
loss.backward()
optimizer.step()
# print statistics
running_loss += loss.item()
if step % 500 == 499: # print every 500 mini-batches
with torch.no_grad():
outputs = net(val_image) # [batch, 10]
predict_y = torch.max(outputs, dim=1)[1]#搜索网络输出结果的最大值,dim=1表示在channel上进行搜索,[1]表示只需要获取索引
accuracy = torch.eq(predict_y, val_label).sum().item() / val_label.size(0)# .sum()后获取的是一个tensor并不是数值,使用item()来获取数值
print('[%d, %5d] train_loss: %.3f test_accuracy: %.3f' %
(epoch + 1, step + 1, running_loss / 500, accuracy))
running_loss = 0.0
print('Finished Training')
save_path = './Lenet.pth'
torch.save(net.state_dict(), save_path)
if __name__ == '__main__':
main()
- train_set = torchvision.datasets.CIFAR10(root=’./data’, train=True,download=False, transform=transform)
- 下载训练集
- root:下载的数据集存放在哪里 ./data 当前目录的data文件夹下
- train: 如果为true就会导入训练集样本,False就会导入测试训练集
- download :为True会自动下载数据集,如果数据集已经下载好了,改为False
- transform:对数据集进行预处理,通过ToTensor讲数据集样本的图片变为[通道数,高度,宽度]的排列顺序,并且将[0-255]的数据范围归一化到0~1;Normalize:使用均值和标准差标准化tensor
- train_loader = torch.utils.data.DataLoader(train_set, batch_size=36,
shuffle=True, num_workers=0)- 将上句下载的数据集分成批次(batch_size=36)导入到程序中来
- shuffle:True是否随机提取数据
- num_workers:载入数据的线程数,在Linux系统下可以自定义,在win下只能设置为0
- val_data_iter = iter(val_loader)
- 将val_loader转化为可迭代的迭代器
- 使用 val_image, val_label = val_data_iter.next() 来获取一批迭代器,它包含了图片和对应的标签值
- classes 是一个元组类型,它的值是不能改变的,内容就是标签值
- nn.CrossEntropyLoss():交叉熵损失函数
- optm.Adam(net.parameters(),lr=0.001):优化器,net.parameters()我们所需要的训练参数,这里所有的可训练参数都会进行训练,lr是学习率
- optimizer.zero_grad() 将历史损失梯度清零:详细解释
- loss.backward() 误差反向传播
- optimizer.step() 参数更新
- running_loss+= loss.item() 将计算的损失函数累加到running_loss当中
- with torch.no_grad() 节省内存资源不会计算误差梯度
- net.state_dict() 网络训练的权重结果
3. 预测
import torch
import torchvision.transforms as transforms
from PIL import Image
from model import LeNet
def main():
transform = transforms.Compose(
[transforms.Resize((32, 32)),
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
classes = ('plane', 'car', 'bird', 'cat',
'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
net = LeNet()
net.load_state_dict(torch.load('Lenet.pth'))
im = Image.open('1.jpg')
im = transform(im) # [C, H, W] 更改图片矩阵维度顺序
im = torch.unsqueeze(im, dim=0) # [N, C, H, W] 扩充图像维度
with torch.no_grad():
outputs = net(im)
predict = torch.max(outputs, dim=1)[1].data.numpy()
print(classes[int(predict)])
if __name__ == '__main__':
main()
附录
1.super
https://bramblexu.com/posts/3adca41/
super()函数可以隐式地将子类(sublass)里的method,与父类(superclass)里的method进行关联。这样的好处在于我们不用在子类里显式地重新创建父类method里的属性。
上述例子中,类LeNet()继承了类Module(),Module形式如下图所示:
当我们用super(LeNet, self).init()后,就意味着类Module()中的所有初始化参数在LeNet()中都不用再重新定义
2.Conv2d
2.1定义:
-
2.1.1初始化函数:
def __init__( self, in_channels: int, out_channels: int, kernel_size: _size_2_t, stride: _size_2_t = 1, padding: _size_2_t = 0, dilation: _size_2_t = 1, groups: int = 1, bias: bool = True, padding_mode: str = 'zeros' # TODO: refine this type ):
- in_channels: 输入通道数
- out_channels: 使用卷积核的个数,使用X个卷积核就会生成深度为x维的特征矩阵
- kernel_size: 对应卷积核的大小
- stride: 步长,默认为1
- padding: 在四周进行补全的层数
- dilation
- groups
- bias: 偏执,默认使用
- padding_mode: 补全的模式,默认为0
-
2.1.2公式:
-
2.1.3卷积输出尺寸的计算方法:
N = ( W − F + 2 P ) / S + 1 N=(W-F+2P)/S+1 N=(W−F+2P)/S+1
- 输入图片大小为 W × W W\times W W×W
- Filter 大小为 F × F F\times F F×F
- 步长为S
- padding的像素数为P
3.MaxPool2d
-
3.1初始化函数
def __init__(self, kernel_size: _size_1_t, stride: Optional[_size_1_t] = None, padding: _size_1_t = 0) -> None: super(MaxUnpool1d, self).__init__() self.kernel_size = _single(kernel_size) self.stride = _single(stride if (stride is not None) else kernel_size) self.padding = _single(padding)
- kernel_size: 池化层大小
- stride: 步长,如果不指定步长,就会按照kenel_size的大小执行
4.torch.no_grad()
在使用pytorch时,并不是所有的操作都需要进行计算图1的生成(计算过程的构建,以便梯度反向传播等操作)。而对于tensor的计算操作,默认是要进行计算图的构建的,在这种情况下,可以使用 with torch.no_grad():,强制之后的内容不进行计算图构建2。