0 前言
代码参考了知乎上“10分钟快速入门PyTorch”系列,并且附上了详细的注释和函数讲解。从今天这篇博文开始,我将和大家一起踏上Pytorch的学习道路,希望有问题可以指出!代码可以直接复制粘贴后运行。
1 数据读入
torchvision.datasets里面有很多数据类型,里面有官网处理好的数据,比如我们要使用的MNIST数据集(手写数字数据集),可以通过torchvision.datasets.MNIST()来得到:
import torchfrom torchimport nnfrom torch.utils.dataimport DataLoaderfrom torchvisionimport datasets, transforms# 定义超参数
batch_size=64
learning_rate=1e-2
num_epochs=5# 训练次数# 判断GPU是否可用
use_gpu= torch.cuda.is_available()# 下载训练集 MNIST 手写数字训练集# 数据是datasets类型的
train_dataset= datasets.FashionMNIST(
root='../datasets', train=True, transform=transforms.ToTensor(), download=True)
test_dataset= datasets.FashionMNIST(
root='../datasets', train=False, transform=transforms.ToTensor())# 将数据处理成 DataLoader
train_loader= DataLoader(train_dataset, batch_size=batch_size, shuffle=True)# 选择打乱数据
test_loader= DataLoader(test_dataset, batch_size=batch_size, shuffle=False)# 选择不打乱数据
- Pytorch的数据读取主要包括3个类:(1)Dataset (2)DataLoader (3)DataLoaderIter,这三者的大致是依次封装的关系,1被装进2,2被装进3。
- Dataset是一个包装类,负责表示数据集,它可以每次使用
__getitem__
返回一个样本。我们再使用DataLoader这个类来更加快捷的对数据进行操作。Dataset被封装在了Dataloader中。
def__getitem__(self,index):
img_path,label=self.data[index].img_path,self.data[index].label
img=Image.open(img_path)return img,label
可见每次会返回:数据+标签
- DataLoader是一个比较重要的类,它为我们提供的常用操作有:batch_size(每个batch的大小), shuffle(是否进行shuffle操作), num_workers(加载数据的时候使用几个子进程)。
- Dataset、Dataloader和DataLoaderIter是层层封装的关系,最终在内部使用DataLoaderIter进行迭代。
2 模型搭建
这里给出一个通用的模型框架:
# 基本的网络构建类模板classnet_name(nn.Module):def__init__(self):super(net_name, self).__init__()# 可以添加各种网络层
self.conv1= nn.Conv2d(3,10,3)# 具体每种层的参数可以去查看文档defforward(self, x):# 定义向前传播
out= self.conv1(x)return out
由上述框架搭建我们的神经网络:
# 定义简单的前馈神经网络classneuralNetwork(nn.Module):def__init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):super(neuralNetwork, self).__init__()# super() 函数是用于调用父类(超类)的一个方法# Sequential()表示将一个有序的模块写在一起,也就相当于将神经网络的层按顺序放在一起,这样可以方便结构显示
self.layer1= nn.Sequential(
nn.Linear(in_dim, n_hidden_1),
nn.ReLU(True))# 表示使用ReLU激活函数
self.layer2= nn.Sequential(
nn.Linear(n_hidden_1, n_hidden_2),
nn.ReLU(True))
self.layer3= nn.Sequential(
nn.Linear(n_hidden_2, out_dim),
nn.ReLU(True))# 定义向前传播defforward(self, x):
x= self.layer1(x)
x= self.layer2(x)
x= self.layer3(x)return x# 图片大小是28*28,中间定义了两个隐藏层大小分别为300和100,最后输出层为10,10分类问题
model= neuralNetwork(28*28,300,100,10)if use_gpu:
model= model.cuda()# 现在可以在GPU上跑代码了
criterion= nn.CrossEntropyLoss()# 定义损失函数类型,使用交叉熵
optimizer= torch.optim.SGD(model.parameters(), lr=learning_rate)# 定义优化器,使用随机梯度下降
- nn.Module 是所有神经网络单元(neural network modules)的基类。pytorch在nn.Module中,实现了__call__方法,而在__call__方法中调用了forward函数。 所以forward函数中依次调用添加到self._module中的子模块,最后输出经过所有神经网络层的结果。
- 最后的forward函数中的x只是重复使用了,是由上往下层层传递的。
3 模型训练
第一个循环表示每个epoch,接着开始前向传播,然后计算loss,然后反向传播,接着优化参数,特别注意的是在每次反向传播的时候需要将参数的梯度归零:
# 开始模型训练for epochinrange(num_epochs):print('*'*10)print(f'epoch {epoch+1}')
running_loss=0.0# 初始值
running_acc=0.0for i, datainenumerate(train_loader,1):# 枚举函数enumerate返回下标和值
img, label= data
img= img.view(img.size(0),-1)# 将图片展开为28*28# 使用GPU?if use_gpu:
img= img.cuda()
label= label.cuda()# 向前传播
out= model(img)# 前向传播
loss= criterion(out, label)# 计算loss
running_loss+= loss.item()# loss求和
_, pred= torch.max(out,1)
running_acc+=(pred== label).float().mean()# 向后传播
optimizer.zero_grad()# 梯度归零
loss.backward()# 后向传播
optimizer.step()# 更新参数if i%300==0:print(f'[{epoch+1}/{num_epochs}] Loss: {running_loss/i:.6f}, Acc: {running_acc/i:.6f}')print(f'Finish {epoch+1} epoch, Loss: {running_loss/i:.6f}, Acc: {running_acc/i:.6f}')
4 模型测试
特别注意的是需要用 model.eval(),让model变成测试模式,这主要是对dropout和batch:
## 模型测试
model.eval()# 让模型变成测试模式
eval_loss=0.
eval_acc=0.for datain test_loader:
img, label= data
img= img.view(img.size(0),-1)if use_gpu:
img= img.cuda()
label= label.cuda()with torch.no_grad():
out= model(img)
loss= criterion(out, label)
eval_loss+= loss.item()
_, pred= torch.max(out,1)
eval_acc+=(pred== label).float().mean()print(f'Test Loss: {eval_loss/len(test_loader):.6f}, Acc: {eval_acc/len(test_loader):.6f}\n')
1.在pytorch中的view()函数就是用来改变tensor的形状的,例如将2行3列的tensor变为1行6列, view( )相当于numpy中resize()的功能,但是用法可能不太一样。
2. 参数中的-1就代表这个位置由其他位置的数字来推断,比如a tensor的数据个数是6个,如果view(1,-1),我们就可以根据tensor的元素个数推断出-1代表6。
5 模型保存
# 保存模型
torch.save(model.state_dict(),'./neural_network.pth')
部分运行结果:
6 参考博客
https://www.zhihu.com/column/c_94953554
https://blog.csdn.net/gdymind/article/details/82226509
https://blog.csdn.net/jzwong/article/details/113308158
https://blog.csdn.net/qq_38929105/article/details/106438045
https://blog.csdn.net/york1996/article/details/81949843
https://blog.csdn.net/york1996/article/details/81949843