书接上文:【2021.03.03】使用卷积神经网络实现MNIST(上)

本次的学习来源:https://www.bilibili.com/video/BV1HK4y1s7j3

定义优化器

# 定义优化器
model = Digit().to(DEVICE)
optimizer = optim.Adam(model.parameters())

这里使用的是Adam优化器,用于更新模型的参数,使得训练测试得到的数值优化

定义训练函数

# 定义训练函数
def train_model(model,device,train_loader,optimizer,epoch):
    # 模型训练
    model.train() # 调用方法开始训练
    for batch_index, (data,target) in enumerate(train_loader): # data是数据,target是标签,enumerate用于遍历
        # 部署到DEVICE上去
        data, target = data.to(device),target.to(device)
        # 训练开始前,梯度初始化为0,准备开始预测
        optimizer.zero_grad()
        # 训练后的结果
        output = model(data)
        # 计算损失,这里使用的是交叉熵损失
        loss = F.cross_entropy(output, target) # 传入交叉熵的是输出值和便签值
        # 找到最大的概率值
        pred = output.max(1,keepdim=True)
        # 反向传播,拿预测值和标签值进行比较,目的是为了找到更好的权重值,使得损失函数的值得到尽量小
        loss.backward()
        # 参数优化更新
        optimizer.step()
        if batch_index % 3000 == 0: # 每三千个循环打印一个结果
            print("Train Epoch :{} \t Loss :{:.6f}".format(epoch,loss.item())) # .item()是必须的,用于取出标量数值,
        

梯度下降:

梯度下降指的是损失函数的值下降,具体可以看:【2021.02.17】线性模型、梯度下降算法

【2021.03.04】使用卷积神经网络实现MNIST(下)

因此在每一次的训练开始前,梯度要初始化为0

enumerate()函数

函数语法:https://www.runoob.com/python/python-func-enumerate.html

enumerate(sequence, [start=0])

start默认为0,可以不传参

.item()

用于将张量Tensor提取出标量来,

计算损失的部分

loss = F.cross_entropy(output, target)

因为在这里不用打印/进行计算,所以这里可以不用使用.item()

(也可以在计算损失的时候使用该函数,这样子loss的定义就是一个标量而不是张量,在下面打印的时候就不用用到该函数

但是特别要注意的是,loss.backward()这里用到的loss是张量,标量是无法用来计算的反向传播的

因此在训练函数中,要输出时再使用.item()

定义测试方法

同理训练方法,不用考虑优化方法,要算出正确率值,损失值

# 定义测试方法
def test_model(model, device, test_loader):
    # 模型验证
    model.eval()
    
    # 正确率
    correct = 0.0
    # 测试损失
    test_loss = 0.0
    
    with torch.no_grad():#已经拥有模型,不用计算梯度、反向传播,而是进行测试
        for data, target in test_loader:
            # 部署到device上
            data, target = data.to(device),target.to(device)
            # 测试数据
            output = model(data)
            # 计算测试损失
            test_loss += F.cross_entropy(output, target).item() #得到的数据是张量,需要用item()提取出标量
            # 找到概率值最大的下标
            pred = output.max(1,keepdim=True)[1] #数据结构为值、索引,我们要获取的是下标,因此是[1]
            # 其他写法
            # pred=torch.max(output, dim=1)
            # pred=output.argmax(dim=1)
            
            #累计正确率,官方写法
            correct += pred.eq(target.view_as(pred)).sum().item()
        test_loss /= len(test_loader.dataset)
        print("Test -- Average loss :{:4f}, Accuracy : {:.3f}\n".format(test_loss,100.0 * correct/ len(test_loader.dataset)))

torch.no_grad()

这里是测试集,不用计算梯度,已经拥有模型,

不用考虑计算梯度、反向传播(这两个在训练集中的作用都是优化)

调用测试方法

每一次训练完后都进行一次测试

# 调用方法,输出结果
for epoch in range(1, EPOCHS + 1):
    train_model(model,DEVICE,train_loader,optimizer,epoch)
    test_model(model,DEVICE,test_loader)

输出结果

随着轮数的增加,训练集的正确率在曲线上升,损失函数的值在不断趋近于0

测试集也没有出现过拟合的现象,损失函数值也在不断趋近于0

【2021.03.04】使用卷积神经网络实现MNIST(下)