PyTorch框架实战系列(2)——图像分类器优化

接上篇:PyTorch框架实战系列(1)——CNN图像分类器

对PyTorch教程图像分类器进行优化:(不涉及GPU训练,所以没写可GPU训练的代码)

1、CNN(卷积神经网络)增加了网络深度,卷积层逐层对特征进行提取,从微小特征总结为较大特征,最后由全连接层进行仿射变换。以下是模型架构:

Model(
  (block): Sequential(
    (0): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): ReLU()
    (2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (3): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (4): ReLU()
    (5): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (6): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (7): ReLU()
    (8): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (9): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (10): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (11): ReLU()
    (12): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (classer): Sequential(
    (0): Dropout(p=0.5, inplace=False)
    (1): Linear(in_features=512, out_features=10, bias=True)
  )
)

2、对训练过程进行优化,增加训练次数,并且每在一定数量样本训练后,将模型在测试集上进行验证,当测试集损失值不再收敛时自动停止训练。

3、增加模型测试评估报告,包括准确率、混淆矩阵和各类别的精确度、召回率及F1评分。

CNN.py

import torch.nn as nn


class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.block = nn.Sequential(
            nn.Conv2d(3, 32, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2),

            nn.Conv2d(32, 64, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2),

            nn.Conv2d(64, 64, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2),

            nn.Conv2d(64, 128, kernel_size=3, padding=1),
            nn.BatchNorm2d(128, affine=True),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2),
        )

        self.classer = nn.Sequential(
            nn.Dropout(0.5),
            nn.Linear(in_features=512, out_features=10),
        )

    def forward(self, x):
        x = self.block(x)   # [1, 128, 2, 2]
        x = x.view(x.size()[0], -1) # [1, 512]
        x = self.classer(x) # [1, 10]
        # print(x.size())
        return x


if __name__ == '__main__':
    import torch
    from torch.autograd import Variable

    x = Variable(torch.rand(1, 3, 32, 32))
    model = Model()
    print(model)
    y = model(x)
    print(y)

train.py

# -*- coding: utf-8 -*-
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import torch.optim as optim
import torch.nn as nn
from CNN import Model
import torch
import numpy as np
from sklearn import metrics


def train(save_path, model, trainloader, testloader):
    # 训练模式
    model.train()
    # 指定损失函数和优化器,学习率0.001
    criterion = nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters(), lr=0.001)

    total_batch = 0  # 记录进行到多少batch
    dev_best_loss = float('inf')  # 记录验证集最佳损失率
    last_improve = 0  # 记录上次验证集loss下降的batch数
    flag = False  # 记录是否很久没有效果提升
    # 批次训练
    for epoch in range(20):
        print('Epoch [{}/{}]'.format(epoch + 1, 20))
        # 从迭代器中按mini-batch训练
        for trains, labels in trainloader:
            outputs = model(trains)
            # 模型梯度归零
            model.zero_grad()
            # 损失函数反馈传递
            loss = criterion(outputs, labels)
            loss.backward()
            # 执行优化
            optimizer.step()
            # 每多少轮在测试集上查看训练的效果
            if total_batch % 100 == 0:
                # 获得训练集准确率
                true = labels.data
                predic = torch.max(outputs.data, 1)[1]
                train_acc = metrics.accuracy_score(true, predic)
                # 如果验证集上继续收敛则保存模型参数
                dev_acc, dev_loss = evaluate(model, testloader)
                if dev_loss < dev_best_loss:
                    dev_best_loss = dev_loss
                    torch.save(model.state_dict(), save_path)
                    improve = '*'
                    last_improve = total_batch
                else:
                    improve = ''
                # 训练成果
                msg = 'Iter: {0:>6},  Train Loss: {1:>5.2},  Train Acc: {2:>6.2%},  Val Loss: {3:>5.2},  Val Acc: {4:>6.2%}  {5}'
                print(msg.format(total_batch, loss.item(), train_acc, dev_loss, dev_acc, improve))
                # 恢复训练
                model.train()
            total_batch += 1
            # 验证集loss超过多少batch没下降,结束训练
            if total_batch - last_improve > 500:
                print("Finished Training...")
                # torch.save(model.state_dict(), save_path)
                flag = True
                break
        if flag:
            break
    # 使用测试集测试评估模型
    model_test(save_path, model, testloader)

# 验证模型
def evaluate(model, dataloader, test=False):
    class_list = ['plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
    # 模型预测模式
    model.eval()
    loss_total = 0
    predict_all = np.array([], dtype=int)
    labels_all = np.array([], dtype=int)
    loss_func = nn.CrossEntropyLoss()
    # 不记录模型梯度
    with torch.no_grad():
        for texts, labels in dataloader:
            outputs = model(texts)
            loss = loss_func(outputs, labels)
            loss_total += loss
            labels = labels.data.numpy()
            predic = torch.max(outputs.data, 1)[1].numpy()
            labels_all = np.append(labels_all, labels)
            predict_all = np.append(predict_all, predic)
    # 验证集准确度
    acc = metrics.accuracy_score(labels_all, predict_all)
    # 给出模型测试结果,评估报告和混淆矩阵
    if test:
        report = metrics.classification_report(labels_all, predict_all, target_names=class_list, digits=4)
        confusion = metrics.confusion_matrix(labels_all, predict_all)
        return acc, loss_total / len(dataloader), report, confusion
    else:
        return acc, loss_total / len(dataloader)

# 测试模型
def model_test(save_path, model, testloader):
    # 加载模型参数
    model.load_state_dict(torch.load(save_path))
    # 预测模式
    model.eval()
    # 模型测试评估
    test_acc, test_loss, test_report, test_confusion = evaluate(model, testloader, test=True)
    msg = 'Test Loss: {0:>5.2},  Test Acc: {1:>6.2%}'
    print(msg.format(test_loss, test_acc))
    print("混淆矩阵...")
    print(test_confusion)
    print("评估报告...")
    print(test_report)


if __name__ == '__main__':
    data_path = './data'    # 数据集
    save_path = './cnn_model.pth'   # 模型保存路径

    # 数据集转化为张量,并标准化
    # input[channel] = (input[channel] - mean[channel]) / std[channel]
    transform = transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
    ])
    # transform = None
    # 下载数据集
    trainset = datasets.CIFAR10(root=data_path, train=True, download=True, transform=transform)
    testset = datasets.CIFAR10(root=data_path, train=False, download=True, transform=transform)

    # 查看数据集大小
    print('trainset', len(trainset))
    print('testset', len(testset))

    batch_size = 100    # mini-batch
    # 构造迭代器
    trainloader = DataLoader(dataset=trainset, batch_size=batch_size, shuffle=True)
    testloader = DataLoader(dataset=testset, batch_size=batch_size, shuffle=True)

    # 迭代器输出的张量
    for sample, label in trainloader:
        print(sample.size(), label.size())
        break

    # 模型实例化
    model = Model()
    train(save_path, model, trainloader, testloader)

输出结果如下:

Files already downloaded and verified
Files already downloaded and verified
trainset 50000
testset 10000
torch.Size([100, 3, 32, 32]) torch.Size([100])
Epoch [1/20]
Iter:      0,  Train Loss:   2.5,  Train Acc: 14.00%,  Val Loss:   2.3,  Val Acc: 13.20%  *
Iter:    100,  Train Loss:   1.5,  Train Acc: 42.00%,  Val Loss:   1.5,  Val Acc: 44.82%  *
Iter:    200,  Train Loss:   1.4,  Train Acc: 47.00%,  Val Loss:   1.3,  Val Acc: 52.96%  *
Iter:    300,  Train Loss:   1.2,  Train Acc: 57.00%,  Val Loss:   1.3,  Val Acc: 53.33%  
Iter:    400,  Train Loss:   1.1,  Train Acc: 61.00%,  Val Loss:   1.1,  Val Acc: 60.16%  *
Epoch [2/20]
Iter:    500,  Train Loss:  0.95,  Train Acc: 60.00%,  Val Loss:   1.0,  Val Acc: 63.07%  *
Iter:    600,  Train Loss:   1.0,  Train Acc: 63.00%,  Val Loss:   1.0,  Val Acc: 62.91%  
Iter:    700,  Train Loss:  0.77,  Train Acc: 69.00%,  Val Loss:   1.0,  Val Acc: 64.62%  *
Iter:    800,  Train Loss:  0.94,  Train Acc: 63.00%,  Val Loss:   1.1,  Val Acc: 62.55%  
Iter:    900,  Train Loss:  0.93,  Train Acc: 63.00%,  Val Loss:  0.96,  Val Acc: 65.69%  *
Epoch [3/20]
Iter:   1000,  Train Loss:  0.83,  Train Acc: 68.00%,  Val Loss:  0.97,  Val Acc: 65.84%  
Iter:   1100,  Train Loss:  0.77,  Train Acc: 71.00%,  Val Loss:  0.94,  Val Acc: 67.48%  *
Iter:   1200,  Train Loss:   1.0,  Train Acc: 65.00%,  Val Loss:  0.86,  Val Acc: 69.97%  *
Iter:   1300,  Train Loss:  0.77,  Train Acc: 74.00%,  Val Loss:  0.86,  Val Acc: 70.56%  
Iter:   1400,  Train Loss:  0.91,  Train Acc: 69.00%,  Val Loss:  0.87,  Val Acc: 69.61%  
Epoch [4/20]
Iter:   1500,  Train Loss:  0.56,  Train Acc: 79.00%,  Val Loss:  0.85,  Val Acc: 70.40%  *
Iter:   1600,  Train Loss:  0.62,  Train Acc: 74.00%,  Val Loss:  0.76,  Val Acc: 73.41%  *
Iter:   1700,  Train Loss:   0.7,  Train Acc: 78.00%,  Val Loss:  0.78,  Val Acc: 73.04%  
Iter:   1800,  Train Loss:  0.75,  Train Acc: 80.00%,  Val Loss:  0.78,  Val Acc: 72.59%  
Iter:   1900,  Train Loss:  0.89,  Train Acc: 63.00%,  Val Loss:  0.77,  Val Acc: 72.92%  
Epoch [5/20]
Iter:   2000,  Train Loss:  0.58,  Train Acc: 81.00%,  Val Loss:  0.75,  Val Acc: 73.93%  *
Iter:   2100,  Train Loss:   0.7,  Train Acc: 75.00%,  Val Loss:  0.77,  Val Acc: 73.61%  
Iter:   2200,  Train Loss:  0.85,  Train Acc: 66.00%,  Val Loss:   0.8,  Val Acc: 72.10%  
Iter:   2300,  Train Loss:  0.67,  Train Acc: 78.00%,  Val Loss:  0.74,  Val Acc: 74.53%  *
Iter:   2400,  Train Loss:  0.86,  Train Acc: 76.00%,  Val Loss:  0.75,  Val Acc: 73.82%  
Epoch [6/20]
Iter:   2500,  Train Loss:  0.78,  Train Acc: 72.00%,  Val Loss:   0.8,  Val Acc: 72.36%  
Iter:   2600,  Train Loss:  0.65,  Train Acc: 76.00%,  Val Loss:  0.75,  Val Acc: 74.33%  
Iter:   2700,  Train Loss:  0.66,  Train Acc: 81.00%,  Val Loss:  0.74,  Val Acc: 74.53%  
Iter:   2800,  Train Loss:  0.66,  Train Acc: 75.00%,  Val Loss:  0.72,  Val Acc: 75.29%  *
Iter:   2900,  Train Loss:  0.75,  Train Acc: 74.00%,  Val Loss:  0.87,  Val Acc: 70.47%  
Epoch [7/20]
Iter:   3000,  Train Loss:   0.6,  Train Acc: 77.00%,  Val Loss:  0.69,  Val Acc: 75.98%  *
Iter:   3100,  Train Loss:  0.57,  Train Acc: 83.00%,  Val Loss:   0.7,  Val Acc: 76.36%  
Iter:   3200,  Train Loss:  0.54,  Train Acc: 78.00%,  Val Loss:  0.72,  Val Acc: 75.94%  
Iter:   3300,  Train Loss:  0.55,  Train Acc: 81.00%,  Val Loss:  0.67,  Val Acc: 77.06%  *
Iter:   3400,  Train Loss:  0.58,  Train Acc: 76.00%,  Val Loss:   0.7,  Val Acc: 75.97%  
Epoch [8/20]
Iter:   3500,  Train Loss:  0.47,  Train Acc: 85.00%,  Val Loss:  0.69,  Val Acc: 76.08%  
Iter:   3600,  Train Loss:  0.69,  Train Acc: 78.00%,  Val Loss:  0.74,  Val Acc: 74.58%  
Iter:   3700,  Train Loss:  0.68,  Train Acc: 83.00%,  Val Loss:  0.73,  Val Acc: 75.18%  
Iter:   3800,  Train Loss:  0.88,  Train Acc: 70.00%,  Val Loss:  0.69,  Val Acc: 76.80%  
Finished Training...
Test Loss:  0.67,  Test Acc: 77.06%
混淆矩阵...
[[794  31  43  10   9   5   8  15  49  36]
 [ 11 918   2   4   3   3   1   0  10  48]
 [ 65   6 706  39  71  46  35  18   5   9]
 [ 21  10  83 533  60 175  60  32  11  15]
 [ 21   2  81  38 747  34  19  51   6   1]
 [ 13   4  54 103  43 717  18  38   2   8]
 [  5   5  77  47  38  24 790   6   6   2]
 [  7   3  41  22  50  54   2 809   2  10]
 [ 56  41  14  12   5   1   2   2 837  30]
 [ 18  85   6   6   4   3   2   8  13 855]]
评估报告...
              precision    recall  f1-score   support

       plane     0.7854    0.7940    0.7897      1000
         car     0.8308    0.9180    0.8722      1000
        bird     0.6378    0.7060    0.6701      1000
         cat     0.6548    0.5330    0.5877      1000
        deer     0.7252    0.7470    0.7360      1000
         dog     0.6751    0.7170    0.6954      1000
        frog     0.8431    0.7900    0.8157      1000
       horse     0.8264    0.8090    0.8176      1000
        ship     0.8895    0.8370    0.8624      1000
       truck     0.8432    0.8550    0.8491      1000

   micro avg     0.7706    0.7706    0.7706     10000
   macro avg     0.7711    0.7706    0.7696     10000
weighted avg     0.7711    0.7706    0.7696     10000

可以看到,准确率从55%提升到了77%,优化效果明显。

 

还可以使用学习率衰减的方法,应该能够让模型更快收敛。

对比项目实践中图像分类的准确率,本例的准确率还是比较低的。主要因为这个数据集32*32的分辨率还是太小了,很多细微特征无法提取。

你可能感兴趣的:(深度学习,深度学习,图像识别)