个人博客同步更新,界面更美观呀
Introduction
近来安装了MindSpore框架,便想测试一下它和Pytorch等其他深度学习框架有什么区别。首先便从模型的训练耗时和测试耗时来测试,MindSpore官方提供了使用LeNet解决MNIST手写数字识别的Demo,此时还需要一个用Pytorch编写的作为对比,为了使参数、模型等各方面尽可能一致,便有了照猫画虎自己编写的需求。
Coding
为了能总览模型编写训练流程,我决定从__main__函数入手,用到什么写什么。
parser
首先要用的就是一个命令行代码的解析工具,使用方法概括如下:
# 导包
import argparse
# 定义一个解析器
parser = argparse.ArgumentParser(description="")
# 给解析器添加需要从命令行读入的参数
parser.add_argument('--device_target', type=str, default="CPU", choices=['GPU', 'CPU'])
# 调用解析函数生成参数对象
args = parser.parse_args()
dataset
准备数据集,最好单独封装为一个处理函数,提供路径加载和临时下载两种方式。
super parameter
超参数的设置,其实也可以放到第一步命令行参数解析里面。
模型的定义
在__main__
函数中实例化一个模型:
model = LeNet()
在前面预先定义模型类:
class LeNet(nn.Module):
"""lenet network structure."""
def __init__(self, num_channel=1, num_class=10):
self.conv1 = nn.Conv2d(in_channels=num_channel, out_channels=6,
kernel_size=5, stride=1, padding=2)
self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5)
self.fc1 = nn.Linear(16*5*5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, num_class)
self.relu = nn.ReLU()
self.max_pool2d = nn.MaxPool2d(2, 2)
def forward(self, x):
x = self.max_pool2d(self.relu(self.conv1(x)))
x = self.max_pool2d(self.relu(self.conv2(x)))
# 展开为一维
x = x.view(x.size()[0], -1)
x = self.relu(self.fc1(x))
x = self.relu(self.fc2(x))
x = self.fc3(x)
return x
模型的训练
训练的过程就是送入数据执行计算过程,得到输出结果,与标准值进行误差计算,将误差进行梯度反向传播更新模型参数。然后重复此过程,训练结束条件可以为设定的epoch次数,或者给定一个收敛条件(误差的缩小程度已经不值得继续训练了)。
**损失函数:**此例采用交叉熵损失函数,多用于分类问题。
loss_function = nn.CrossEntropyLoss()
**优化器:**即计算得到的损失以怎样的方式作用到模型参数上,此例采用随机梯度下降(SGD)。
optimizer = optim.SGD(model.parameters(), lr=lr, momentum=momentum)
训练循环:
第一层循环一般为epoch维度的循环,简单说就是在这个数据集上训练几次,epoch=2即利用这个数据集进行两次训练过程。第二层循环就是在batch上的训练了,对所有数据分批进行前向计算和反向传播。
训练体一般有如下流程:
- 对得到的数据x和标签label进行device上的转换,比如使用了GPU。
- 前向计算:pred=model(x)
- 计算损失(看看算的对不对):loss=loss_function(pred, label)
- 反向传播(不对的话如何改正):先要对梯度进行一个清零操作optimizer.zero_grad(),然后进行反向传播loss.backward(),执行一个优化步optimizer.step()。
至此一个训练过程就结束了,有需要的话可以在不同逻辑层进行损失和准确率等参数的打印输出,方便监测训练状况。
for epoch in range(1, epoch_size+1):
# data loop
for batch_idx, (img, label) in enumerate(train_loader):
img = img.to(device)
label = label.to(device)
img, label = Variable(img), Variable(label)
# 前向传播
pred = model(img)
# 计算损失
loss = loss_function(pred, label)
# 梯度清零
optimizer.zero_grad()
# 误差反向传播
loss.backward()
# 优化步
optimizer.step()
# 为了和MindSpore版本保持一致 每次都打印一遍
print("epoch: {} step: {}, loss id {}".format(epoch, batch_idx, loss.item()))
模型的测试
测试过程即对一些样本逐个进行预测并记录是否正确,最终输出准确率、召回率等参数指标。
eval_acc = 0
for img, label in test_loader:
img = img.to(device)
label = label.to(device)
img, label = Variable(img), Variable(label)
# 预测
output = model(img)
_, pred = torch.max(output, 1)
num_cortect = (pred == label).sum()
eval_acc += num_cortect.item()
acc = eval_acc / (len(test_loader.dataset))
Experiment
对于添加了argparse的训练脚本来说,执行命令一般为:
python lenet.py --device_target=GPU --other=otherValue
需要通过命令行传入的参数以如上方式进行匹配赋值。
运行无误的话就能得到预期的结果反馈,有错误就根据提示Debug啦。
执行命令一般为:
python lenet.py --device_target=GPU --other=otherValue
需要通过命令行传入的参数以如上方式进行匹配赋值。
运行无误的话就能得到预期的结果反馈,有错误就根据提示Debug啦。