【CV】MNIST 手写数字识别

MNIST:手写数字
50000张训练图片
10000张测试图片
图像大小:28x28
10个类别(0-9)

23 经典卷积神经网络 LeNet【动手学深度学习v2】

手把手实战PyTorch手写数据集MNIST识别项目全流程

pytorch读取MNIST数据集并显示

理论

数据读取和预处理
CUDA:显卡驱动,有了CUDA显卡才能进行复杂运算
数据读取 DataLoader 和 图像预处理 transforms 详解(这篇值得反复阅读)
深度学习 | 三个概念:Epoch, Batch, Iteration
使用 transforms.Compose() 将图像变换方法整合在一起
torchvision.transforms 对有限的图片数据进行各种变换,如缩小或者放大图片的大小、对图片进行水平或者垂直翻转等,这些都是数据增强的方法。
使用sklearn实现对数据集划分,从而进行交叉验证

可视化工具包
以进度条形式可视化迭代器运行的包:tqdm

李沐 Softmax 函数

对 softmax 回归的感性理解:
线性回归模型多输入,单输出,而 softmax 多输入,多输出。输出结果为输入的向量所属类别,由于类别有多个,所以多输出。输出概率最大的为所属类别。但由于直接输出的概率值 总和不一定为1,且可能有负值,所以对多个输出结果进行 softmax 方法的计算,从而使得输出结果为 总和为1,且均为正值的概率值。

实现

# 导入库
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from sklearn.model_selection._split import KFold
# 定义超参数
BATCH_SIZE = 128  # 每批处理的数据量
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")  # 用CPU还是GPU训练
EPOCHS = 10  # 定义总训练次数
k_split_value = 5  # 定义 5 折交叉验证
# 定义图像处理方法
tranform = transforms.Compose([
    transforms.ToTensor(),  # 将图片转换成Tensor
    transforms.Normalize((0.1307,), (0.3081,))  # 进行数据归一化处理
])
# 下载、加载数据集
from torch.utils.data import DataLoader

train_data = datasets.MNIST(root="./MNIST",
                            train=True,
                            transform=tranform,
                            download=False)  # 注,如果已经下载了一遍,也需要用该命令设置data的路径

test_data = datasets.MNIST(root="./MNIST",
                           train=False,
                           transform=tranform,
                           download=False)

# 将测试集和训练集合并,以便后续对数据集进行分割
dataFold = torch.utils.data.ConcatDataset([train_data, test_data])
# 构建网络模型,使用 AlexNet
class AlexNet(nn.Module):
    def __init__(self):
        super(AlexNet, self).__init__()

        # 由于MNIST为28x28, 而最初AlexNet的输入图片是227x227的。所以网络层数和参数需要调节
        self.conv1 = nn.Conv2d(1, 32, kernel_size=3, padding=1)  # AlexCONV1(3,96, k=11,s=4,p=0)
        self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)  # AlexPool1(k=3, s=2)
        self.relu1 = nn.ReLU()

        # self.conv2 = nn.Conv2d(96, 256, kernel_size=5,stride=1,padding=2)
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)  # AlexCONV2(96, 256,k=5,s=1,p=2)
        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)  # AlexPool2(k=3,s=2)
        self.relu2 = nn.ReLU()

        self.conv3 = nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1)  # AlexCONV3(256,384,k=3,s=1,p=1)
        # self.conv4 = nn.Conv2d(384, 384, kernel_size=3, stride=1, padding=1)
        self.conv4 = nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1)  # AlexCONV4(384, 384, k=3,s=1,p=1)
        self.conv5 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)  # AlexCONV5(384, 256, k=3, s=1,p=1)
        self.pool3 = nn.MaxPool2d(kernel_size=2, stride=2)  # AlexPool3(k=3,s=2)
        self.relu3 = nn.ReLU()

        self.fc6 = nn.Linear(256 * 3 * 3, 1024)  # AlexFC6(256*6*6, 4096)
        self.fc7 = nn.Linear(1024, 512)  # AlexFC6(4096,4096)
        self.fc8 = nn.Linear(512, 10)  # AlexFC6(4096,1000)
        self.dropout = nn.Dropout(p=0.5)

    def forward(self, x):
        x = self.conv1(x)
        x = self.pool1(x)
        x = self.relu1(x)
        x = self.conv2(x)
        x = self.pool2(x)
        x = self.relu2(x)
        x = self.conv3(x)
        x = self.conv4(x)
        x = self.conv5(x)
        x = self.pool3(x)
        x = self.relu3(x)
        x = x.view(-1, 256 * 3 * 3)  # Alex: x = x.view(-1, 256*6*6)
        x = self.fc6(x)
        x = self.dropout(x)
        x = F.relu(x)
        x = self.fc7(x)
        x = self.dropout(x)
        x = F.relu(x)
        x = self.fc8(x)
        return x
# 定义优化器
model = AlexNet().to(DEVICE)
optimizer = optim.Adam(model.parameters())  # 使用 Adam 优化器
# 定义训练方法
def train_model(model, device, train_loader, optimizer, epoch):
    model.train()  # PyTorch 提供的训练方法
    for batch_index, (data, label) in enumerate(train_loader):
        # 部署到DEVICE
        data, label = data.to(device), label.to(device)
        # 梯度初始化为0
        optimizer.zero_grad()
        # 训练后的结果
        output = model(data)
        # 计算损失(针对多分类任务交叉熵,二分类用sigmoid)
        loss = F.cross_entropy(output, label)
        # 找到最大概率的下标
        pred = output.argmax(dim=1)
        # 反向传播Backpropagation
        loss.backward()
        # 参数的优化
        optimizer.step()
        if batch_index % 3000 == 0:
            print("Train Epoch : {} \t Loss : {:.6f}".format(epoch, loss.item()))
# 定义测试方法
def test_model(model, device, test_loader):
    # 模型验证
    model.eval()
    # 统计正确率
    correct = 0.0
    # 测试损失
    test_loss = 0.0
    with torch.no_grad():  # 不计算梯度,不反向传播
        for data, label in test_loader:
            data, label = data.to(device), label.to(device)
            # 测试数据
            output = model(data)
            # 计算测试损失
            test_loss += F.cross_entropy(output, label).item()
            # 找到概率值最大的下标
            pred = output.argmax(dim=1)
            # 累计正确率
            correct += pred.eq(label.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)))
# 对数据进行 K 折交叉划分并且调用前面的方法训练模型
def KFold_and_Train(k_split_value):
    counter = 1  # 自定义一个交叉验证计数器
    kf = KFold(n_splits=k_split_value, shuffle=True, random_state=0)  # 定义K折交叉验证的方法
    for train_index, test_index in kf.split(dataFold):  # 使用kf方法将dataFold分成测试集和验证集
        print(f"第{counter}次交叉验证")
        counter += 1  # 计数器自增1

        # get train, val
        train_fold = torch.utils.data.dataset.Subset(dataFold, train_index)
        test_fold = torch.utils.data.dataset.Subset(dataFold, test_index)

        # package type of DataLoader
        train_loader = torch.utils.data.DataLoader(dataset=train_fold, batch_size=BATCH_SIZE, shuffle=True)
        test_loader = torch.utils.data.DataLoader(dataset=test_fold, batch_size=BATCH_SIZE, shuffle=True)

        # training and test
        for epoch in range(EPOCHS):
            train_model(model, DEVICE, train_loader, optimizer, epoch)
            test_model(model, DEVICE, test_loader)
# 调用函数
KFold_and_Train(k_split_value)
赞赏