10、basic CNN
import torch
from torch import nn  # 导入神经网络模块
from torchvision import transforms  # 导入数据集模块
from torchvision import datasets  # 导入数据集模块
from torch.utils.data import DataLoader  # 导入数据加载器
import torch.nn.functional as F  # 导入函数式接口(如ReLU、sigmoid等)
import torch.optim as optim  # 导入优化器模块

batch_size = 64  # 设置批次大小,每次训练使用64个样本
# 定义数据预处理管道
transform = transforms.Compose([transforms.ToTensor(),  # 将PIL图像或numpy数组转为Tensor,并归一化到[0,1]
                                transforms.Normalize((0.1307,), (0.3081,))])  # 标准化:(x-mean)/std
# 加载MNIST训练集
train_dataset = datasets.MNIST(root='../dataset/mnist/', # 数据集存储路径
                               train=True,  # 使用训练集
                               transform=transform,  # 应用上面定义的数据变换
                               download=True)  # 如果本地没有则下载
# 创建训练数据加载器
train_loader = DataLoader(train_dataset,  # 训练数据集
                          batch_size=batch_size,  # 批次大小64
                          shuffle=True)  # 每个epoch打乱数据顺序,防止过拟合
# 加载MNIST测试集
test_dataset = datasets.MNIST(root='../dataset/mnist/',  # 同训练集路径
                              train=False,  # 使用测试集
                              transform=transform,  # 应用相同的变换
                              download=True)  # 如果本地没有则下载
test_loader = DataLoader(test_dataset,  # 测试数据集
                         batch_size=batch_size,  # 批次大小64
                         shuffle=False)  # 测试集不需要打乱,保持固定顺序

class Net(torch.nn.Module):  # 继承nn.Module基类
    def __init__(self):
        super(Net, self).__init__()  # 调用父类初始化
        self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5)  # 卷积层1:输入1通道,输出10通道,5x5卷积核
        self.conv2 = torch.nn.Conv2d(10, 20, kernel_size=5)  # 卷积层2:输入10通道,输出20通道,5x5卷积
        self.pooling = torch.nn.MaxPool2d(2)  # 2x2最大池化层
        self.fc = torch.nn.Linear(320, 10)  # 全连接层:320维输入,10维输出(10个类别)

    def forward(self, x):  # 定义前向传播
        batch_size = x.size(0)  # 获取批次大小
        # 卷积 → 池化 → ReLU激活
        x = F.relu(self.pooling(self.conv1(x)))  # 第一层:[batch,1,28,28] → [batch,10,12,12]
        x = F.relu(self.pooling(self.conv2(x)))  # 第二层:[batch,10,12,12] → [batch,20,4,4]
        x = x.view(batch_size, -1)  # 展平:[batch,20,4,4] → [batch,320]
        x = self.fc(x)  # 全连接层:[batch,320] → [batch,10]
        return x

model = Net()  # 实例化神经网络模型
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")  # 设备选择:优先使用GPU,否则使用CPU
model.to(device)  # 将模型移动到指定设备
criterion = torch.nn.CrossEntropyLoss()  # 定义损失函数(适用于多分类问题,内部包含LogSoftmax)
optimizer = torch.optim.SGD(model.parameters(),  # 需要优化的参数(模型所有权重和偏置)
                            lr=0.01,  # 学习率
                            momentum=0.5)  # 动量,加速收敛并减少震荡

def train(epoch):
    running_loss = 0.0  # 累计损失,用于计算平均损失
    # 遍历所有训练批次
    for batch_idx, data in enumerate(train_loader, 0):
        inputs, target = data  # 解包:inputs是图像,target是标签
        inputs, target = inputs.to(device), target.to(device)  # 将数据移动到GPU/CPU
        outputs = model(inputs)  # 前向传播:输入→模型→输出
        loss = criterion(outputs, target)  # 计算损失:预测值 vs 真实值
        optimizer.zero_grad()  # 清空梯度缓存,防止梯度累积
        loss.backward()  # 反向传播:计算所有参数的梯度
        optimizer.step()  # 参数更新:根据梯度更新模型参数
        running_loss += loss.item()  # 累加当前批次损失
        if batch_idx % 300 == 299:  # 每300个批次打印一次平均损失
            print('[%d, %5d] loss: %.3f' % (epoch + 1, batch_idx + 1, running_loss / 300))
            running_loss = 0.0  # 重置累计损失

def test():
    correct = 0  # 正确预测的样本数
    total = 0    # 总样本数
    with torch.no_grad():  # 测试时不计算梯度,节省内存和计算
        for data in test_loader:  # 遍历所有测试批次
            inputs, target = data  # 解包
            inputs, target = inputs.to(device), target.to(device)  # 将数据移动到GPU/CPU
            outputs = model(inputs)  # 前向传播获取预测值
            # torch.max返回两个值:最大值和最大值索引
            # dim=1表示在类别维度(第1维)找最大值
            # predicted是预测的类别索引(0-9)
            _, predicted = torch.max(outputs.data, dim=1)
            total += target.size(0)  # 累加当前批次样本数
            correct += (predicted == target).sum().item()  # 累加正确预测数
    # 计算并打印准确率
    print('Accuracy of the test set: %d %%' % (100 * correct / total))

if __name__ == '__main__':  # 主程序入口,防止多进程问题
    for epoch in range(10):  # 训练10个epoch
        train(epoch)  # 训练一个epoch
        test()        # 测试当前模型性能

11.1、Adcanced CNN

import torch
from torch import nn  # 导入神经网络模块
from torchvision import transforms  # 导入数据集模块
from torchvision import datasets  # 导入数据集模块
from torch.utils.data import DataLoader  # 导入数据加载器
import torch.nn.functional as F  # 导入函数式接口(如ReLU、sigmoid等)
import torch.optim as optim  # 导入优化器模块

batch_size = 64  # 设置批次大小,每次训练使用64个样本
# 定义数据预处理管道
transform = transforms.Compose([transforms.ToTensor(),  # 将PIL图像或numpy数组转为Tensor,并归一化到[0,1]
                                transforms.Normalize((0.1307,), (0.3081,))])  # 标准化:(x-mean)/std
# 加载MNIST训练集
train_dataset = datasets.MNIST(root='../dataset/mnist/', # 数据集存储路径
                               train=True,  # 使用训练集
                               transform=transform,  # 应用上面定义的数据变换
                               download=True)  # 如果本地没有则下载
# 创建训练数据加载器
train_loader = DataLoader(train_dataset,  # 训练数据集
                          batch_size=batch_size,  # 批次大小64
                          shuffle=True)  # 每个epoch打乱数据顺序,防止过拟合
# 加载MNIST测试集
test_dataset = datasets.MNIST(root='../dataset/mnist/',  # 同训练集路径
                              train=False,  # 使用测试集
                              transform=transform,  # 应用相同的变换
                              download=True)  # 如果本地没有则下载
test_loader = DataLoader(test_dataset,  # 测试数据集
                         batch_size=batch_size,  # 批次大小64
                         shuffle=False)  # 测试集不需要打乱,保持固定顺序

class InceptionA(nn.Module):
    def __init__(self, in_channels):
        super(InceptionA, self).__init__()  # 调用父类nn.Module的初始化方法
        # 1x1卷积分支,输入通道数为in_channels,输出通道数为16,卷积核大小为1x1。
        self.branch1x1 = nn.Conv2d(in_channels, 16, kernel_size=1)
        # 5x5卷积分支的第一层:1x1卷积,用于降维,减少计算量。输入通道数in_channels,输出通道数16。
        self.branch5x5_1 = nn.Conv2d(in_channels, 16, kernel_size=1)
        # 5x5卷积分支的第二层:5x5卷积,输入通道数16(来自上一层输出),输出通道数24。
        # padding=2:填充2圈0,使得5x5卷积后特征图尺寸不变(假设步长为1)。
        self.branch5x5_2 = nn.Conv2d(16, 24,  kernel_size=5, padding=2)
        # 3x3卷积分支的第一层:1x1卷积,用于降维。输入通道数in_channels,输出通道数16。
        self.branch3x3_1 = nn.Conv2d(in_channels, 16, kernel_size=1)
        # 3x3卷积分支的第二层:3x3卷积,输入通道数16,输出通道数24,padding=1保持尺寸不变。
        self.branch3x3_2 = nn.Conv2d(16, 24,  kernel_size=3, padding=1)
        # 3x3卷积分支的第三层:3x3卷积,输入通道数24,输出通道数24,padding=1保持尺寸不变。
        self.branch3x3_3 = nn.Conv2d(24, 24,  kernel_size=3, padding=1)
        # 池化分支后的1x1卷积,输入通道数in_channels,输出通道数24。
        self.branch_pool = nn.Conv2d(in_channels, 24, kernel_size=1)

    def forward(self, x):
        # 1x1卷积分支的前向计算
        branch1x1 = self.branch1x1(x)
        # 5x5卷积分支:先经过1x1卷积,再经过5x5卷积
        branch5x5 = self.branch5x5_1(x)
        branch5x5 = self.branch5x5_2(branch5x5)
        # 3x3卷积分支:先1x1卷积,再两个3x3卷积
        branch3x3 = self.branch3x3_1(x)
        branch3x3 = self.branch3x3_2(branch3x3)
        branch3x3 = self.branch3x3_3(branch3x3)
        # 平均池化:使用3x3的池化窗口,步长为1,填充为1,因此输出尺寸不变。
        # 注意:这里使用函数式接口F.avg_pool2d,与nn.AvgPool2d效果相同,但无需在__init__中定义层。
        branch_pool = F.avg_pool2d(x, kernel_size=3, stride=1, padding=1)
        branch_pool = self.branch_pool(branch_pool)
        # 将池化后的特征图通过1x1卷积,调整通道数
        outputs = [branch1x1, branch5x5, branch3x3, branch_pool]
        # 将四个分支的输出在通道维度(dim=1)上拼接起来。
        # 假设每个分支的输出形状为[N, C, H, W],拼接后通道数变为各分支通道数之和16 + 24 + 24 + 24 = 88。
        # 空间尺寸不变:H×W不变
        return torch.cat(outputs, 1)

class Net(nn.Module):  # 继承nn.Module基类
    def __init__(self):
        super(Net, self).__init__()  # 调用父类初始化
        # 第一层卷积:从1通道(灰度)→10通道
        # kernel_size=5, stride默认=1, padding默认=0
        # 输出尺寸计算:(28-5+1)=24 → 输出:[batch, 10, 24, 24]
        self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
        # 第二层卷积:88通道→20通道(信息压缩,筛选重要特征)
        # 输出尺寸:(24-5+1)=20 → [batch, 20, 20, 20]
        self.conv2 = nn.Conv2d(88, 20, kernel_size=5)  # 卷积层2:输入10通道,输出20通道,5x5卷积
        # 第一层Inception模块:10通道→88通道
        # InceptionA输出通道=88,输入输出尺寸不变(24×24)
        self.incep1 = InceptionA(in_channels=10)
        # 第二层Inception模块:20通道→88通道
        self.incep2 = InceptionA(in_channels=20)
        # 最大池化层:2x2池化,stride默认=2
        # 作用:下采样,减少计算量,增加感受野,提供平移不变性,输出尺寸减半
        self.mp = nn.MaxPool2d(2)
        # 全连接层:1408维输入→10维输出(10个类别)
        # 输入:28x28 → conv1+pool: 28→24→12 → incep1: 12不变 → conv2+pool: 12→8→4 → incep2: 4不变
        # 最终特征图:88个通道 × 4×4 = 88×16 = 1408
        self.fc = nn.Linear(1408, 10)

    def forward(self, x):  # 定义前向传播
        # 获取批次大小,用于后续view操作
        in_size = x.size(0)
        # 第一层:卷积conv1(28→24)→mp(stride=2,24→12)池化→ReLU激活
        x = F.relu(self.mp(self.conv1(x)))
        # 第一层:Inception模块,尺寸不变:[batch, 88, 12, 12]
        x = self.incep1(x)
        # 第二层:卷积conv1(12→8)→mp(stride=2,8→4)池化→ReLU激活
        x = F.relu(self.mp(self.conv2(x)))
        # 第二层:Inception模块,尺寸不变:[batch, 88, 4, 4】
        x = self.incep2(x)
        # 展平操作:将4D特征图展平为2D矩阵 [batch, 88×4×4] = [batch, 1408]
        # -1表示自动计算该维度大小,保持总元素数不变
        x = x.view(in_size, -1)
        # 全连接层:1408维→10维 输出:[batch, 10]
        # 交叉熵损失函数内部包含LogSoftmax,因此这里不需要额外Softmax
        x = self.fc(x)
        return x

model = Net()  # 实例化神经网络模型
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")  # 设备选择:优先使用GPU,否则使用CPU
model.to(device)  # 将模型移动到指定设备
criterion = torch.nn.CrossEntropyLoss()  # 定义损失函数(适用于多分类问题,内部包含LogSoftmax)
optimizer = torch.optim.SGD(model.parameters(),  # 需要优化的参数(模型所有权重和偏置)
                            lr=0.01,  # 学习率
                            momentum=0.5)  # 动量,加速收敛并减少震荡

def train(epoch):
    running_loss = 0.0  # 累计损失,用于计算平均损失
    # 遍历所有训练批次
    for batch_idx, data in enumerate(train_loader, 0):
        inputs, target = data  # 解包:inputs是图像,target是标签
        inputs, target = inputs.to(device), target.to(device)  # 将数据移动到GPU/CPU
        outputs = model(inputs)  # 前向传播:输入→模型→输出
        loss = criterion(outputs, target)  # 计算损失:预测值 vs 真实值
        optimizer.zero_grad()  # 清空梯度缓存,防止梯度累积
        loss.backward()  # 反向传播:计算所有参数的梯度
        optimizer.step()  # 参数更新:根据梯度更新模型参数
        running_loss += loss.item()  # 累加当前批次损失
        if batch_idx % 300 == 299:  # 每300个批次打印一次平均损失
            print('[%d, %5d] loss: %.3f' % (epoch + 1, batch_idx + 1, running_loss / 300))
            running_loss = 0.0  # 重置累计损失

def test():
    correct = 0  # 正确预测的样本数
    total = 0    # 总样本数
    with torch.no_grad():  # 测试时不计算梯度,节省内存和计算
        for data in test_loader:  # 遍历所有测试批次
            inputs, target = data  # 解包
            inputs, target = inputs.to(device), target.to(device)  # 将数据移动到GPU/CPU
            outputs = model(inputs)  # 前向传播获取预测值
            # torch.max返回两个值:最大值和最大值索引
            # dim=1表示在类别维度(第1维)找最大值
            # predicted是预测的类别索引(0-9)
            _, predicted = torch.max(outputs.data, dim=1)
            total += target.size(0)  # 累加当前批次样本数
            correct += (predicted == target).sum().item()  # 累加正确预测数
    # 计算并打印准确率
    print('Accuracy of the test set: %d %%' % (100 * correct / total))

if __name__ == '__main__':  # 主程序入口,防止多进程问题
    for epoch in range(10):  # 训练10个epoch
        train(epoch)  # 训练一个epoch
        test()        # 测试当前模型性能

11.2、Simple Residual Network

import torch
from torch import nn  # 导入神经网络模块
from torchvision import transforms  # 导入数据集模块
from torchvision import datasets  # 导入数据集模块
from torch.utils.data import DataLoader  # 导入数据加载器
import torch.nn.functional as F  # 导入函数式接口(如ReLU、sigmoid等)
import torch.optim as optim  # 导入优化器模块

batch_size = 64  # 设置批次大小,每次训练使用64个样本
# 定义数据预处理管道
transform = transforms.Compose([transforms.ToTensor(),  # 将PIL图像或numpy数组转为Tensor,并归一化到[0,1]
                                transforms.Normalize((0.1307,), (0.3081,))])  # 标准化:(x-mean)/std
# 加载MNIST训练集
train_dataset = datasets.MNIST(root='../dataset/mnist/', # 数据集存储路径
                               train=True,  # 使用训练集
                               transform=transform,  # 应用上面定义的数据变换
                               download=True)  # 如果本地没有则下载
# 创建训练数据加载器
train_loader = DataLoader(train_dataset,  # 训练数据集
                          batch_size=batch_size,  # 批次大小64
                          shuffle=True)  # 每个epoch打乱数据顺序,防止过拟合
# 加载MNIST测试集
test_dataset = datasets.MNIST(root='../dataset/mnist/',  # 同训练集路径
                              train=False,  # 使用测试集
                              transform=transform,  # 应用相同的变换
                              download=True)  # 如果本地没有则下载
test_loader = DataLoader(test_dataset,  # 测试数据集
                         batch_size=batch_size,  # 批次大小64
                         shuffle=False)  # 测试集不需要打乱,保持固定顺序

class InceptionA(nn.Module):
    def __init__(self, in_channels):
        super(InceptionA, self).__init__()  # 调用父类nn.Module的初始化方法
        # 1x1卷积分支,输入通道数为in_channels,输出通道数为16,卷积核大小为1x1。
        self.branch1x1 = nn.Conv2d(in_channels, 16, kernel_size=1)
        # 5x5卷积分支的第一层:1x1卷积,用于降维,减少计算量。输入通道数in_channels,输出通道数16。
        self.branch5x5_1 = nn.Conv2d(in_channels, 16, kernel_size=1)
        # 5x5卷积分支的第二层:5x5卷积,输入通道数16(来自上一层输出),输出通道数24。
        # padding=2:填充2圈0,使得5x5卷积后特征图尺寸不变(假设步长为1)。
        self.branch5x5_2 = nn.Conv2d(16, 24,  kernel_size=5, padding=2)
        # 3x3卷积分支的第一层:1x1卷积,用于降维。输入通道数in_channels,输出通道数16。
        self.branch3x3_1 = nn.Conv2d(in_channels, 16, kernel_size=1)
        # 3x3卷积分支的第二层:3x3卷积,输入通道数16,输出通道数24,padding=1保持尺寸不变。
        self.branch3x3_2 = nn.Conv2d(16, 24,  kernel_size=3, padding=1)
        # 3x3卷积分支的第三层:3x3卷积,输入通道数24,输出通道数24,padding=1保持尺寸不变。
        self.branch3x3_3 = nn.Conv2d(24, 24,  kernel_size=3, padding=1)
        # 池化分支后的1x1卷积,输入通道数in_channels,输出通道数24。
        self.branch_pool = nn.Conv2d(in_channels, 24, kernel_size=1)

    def forward(self, x):
        # 1x1卷积分支的前向计算
        branch1x1 = self.branch1x1(x)
        # 5x5卷积分支:先经过1x1卷积,再经过5x5卷积
        branch5x5 = self.branch5x5_1(x)
        branch5x5 = self.branch5x5_2(branch5x5)
        # 3x3卷积分支:先1x1卷积,再两个3x3卷积
        branch3x3 = self.branch3x3_1(x)
        branch3x3 = self.branch3x3_2(branch3x3)
        branch3x3 = self.branch3x3_3(branch3x3)
        # 平均池化:使用3x3的池化窗口,步长为1,填充为1,因此输出尺寸不变。
        # 注意:这里使用函数式接口F.avg_pool2d,与nn.AvgPool2d效果相同,但无需在__init__中定义层。
        branch_pool = F.avg_pool2d(x, kernel_size=3, stride=1, padding=1)
        branch_pool = self.branch_pool(branch_pool)
        # 将池化后的特征图通过1x1卷积,调整通道数
        outputs = [branch1x1, branch5x5, branch3x3, branch_pool]
        # 将四个分支的输出在通道维度(dim=1)上拼接起来。
        # 假设每个分支的输出形状为[N, C, H, W],拼接后通道数变为各分支通道数之和16 + 24 + 24 + 24 = 88。
        # 空间尺寸不变:H×W不变
        return torch.cat(outputs, 1)

class Net(nn.Module):  # 继承nn.Module基类
    def __init__(self):
        super(Net, self).__init__()  # 调用父类初始化
        # 第一层卷积:从1通道(灰度)→10通道
        # kernel_size=5, stride默认=1, padding默认=0
        # 输出尺寸计算:(28-5+1)=24 → 输出:[batch, 10, 24, 24]
        self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
        # 第二层卷积:88通道→20通道(信息压缩,筛选重要特征)
        # 输出尺寸:(24-5+1)=20 → [batch, 20, 20, 20]
        self.conv2 = nn.Conv2d(88, 20, kernel_size=5)  # 卷积层2:输入10通道,输出20通道,5x5卷积
        # 第一层Inception模块:10通道→88通道
        # InceptionA输出通道=88,输入输出尺寸不变(24×24)
        self.incep1 = InceptionA(in_channels=10)
        # 第二层Inception模块:20通道→88通道
        self.incep2 = InceptionA(in_channels=20)
        # 最大池化层:2x2池化,stride默认=2
        # 作用:下采样,减少计算量,增加感受野,提供平移不变性,输出尺寸减半
        self.mp = nn.MaxPool2d(2)
        # 全连接层:1408维输入→10维输出(10个类别)
        # 输入:28x28 → conv1+pool: 28→24→12 → incep1: 12不变 → conv2+pool: 12→8→4 → incep2: 4不变
        # 最终特征图:88个通道 × 4×4 = 88×16 = 1408
        self.fc = nn.Linear(1408, 10)

    def forward(self, x):  # 定义前向传播
        # 获取批次大小,用于后续view操作
        in_size = x.size(0)
        # 第一层:卷积conv1(28→24)→mp(stride=2,24→12)池化→ReLU激活
        x = F.relu(self.mp(self.conv1(x)))
        # 第一层:Inception模块,尺寸不变:[batch, 88, 12, 12]
        x = self.incep1(x)
        # 第二层:卷积conv1(12→8)→mp(stride=2,8→4)池化→ReLU激活
        x = F.relu(self.mp(self.conv2(x)))
        # 第二层:Inception模块,尺寸不变:[batch, 88, 4, 4】
        x = self.incep2(x)
        # 展平操作:将4D特征图展平为2D矩阵 [batch, 88×4×4] = [batch, 1408]
        # -1表示自动计算该维度大小,保持总元素数不变
        x = x.view(in_size, -1)
        # 全连接层:1408维→10维 输出:[batch, 10]
        # 交叉熵损失函数内部包含LogSoftmax,因此这里不需要额外Softmax
        x = self.fc(x)
        return x

model = Net()  # 实例化神经网络模型
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")  # 设备选择:优先使用GPU,否则使用CPU
model.to(device)  # 将模型移动到指定设备
criterion = torch.nn.CrossEntropyLoss()  # 定义损失函数(适用于多分类问题,内部包含LogSoftmax)
optimizer = torch.optim.SGD(model.parameters(),  # 需要优化的参数(模型所有权重和偏置)
                            lr=0.01,  # 学习率
                            momentum=0.5)  # 动量,加速收敛并减少震荡

def train(epoch):
    running_loss = 0.0  # 累计损失,用于计算平均损失
    # 遍历所有训练批次
    for batch_idx, data in enumerate(train_loader, 0):
        inputs, target = data  # 解包:inputs是图像,target是标签
        inputs, target = inputs.to(device), target.to(device)  # 将数据移动到GPU/CPU
        outputs = model(inputs)  # 前向传播:输入→模型→输出
        loss = criterion(outputs, target)  # 计算损失:预测值 vs 真实值
        optimizer.zero_grad()  # 清空梯度缓存,防止梯度累积
        loss.backward()  # 反向传播:计算所有参数的梯度
        optimizer.step()  # 参数更新:根据梯度更新模型参数
        running_loss += loss.item()  # 累加当前批次损失
        if batch_idx % 300 == 299:  # 每300个批次打印一次平均损失
            print('[%d, %5d] loss: %.3f' % (epoch + 1, batch_idx + 1, running_loss / 300))
            running_loss = 0.0  # 重置累计损失

def test():
    correct = 0  # 正确预测的样本数
    total = 0    # 总样本数
    with torch.no_grad():  # 测试时不计算梯度,节省内存和计算
        for data in test_loader:  # 遍历所有测试批次
            inputs, target = data  # 解包
            inputs, target = inputs.to(device), target.to(device)  # 将数据移动到GPU/CPU
            outputs = model(inputs)  # 前向传播获取预测值
            # torch.max返回两个值:最大值和最大值索引
            # dim=1表示在类别维度(第1维)找最大值
            # predicted是预测的类别索引(0-9)
            _, predicted = torch.max(outputs.data, dim=1)
            total += target.size(0)  # 累加当前批次样本数
            correct += (predicted == target).sum().item()  # 累加正确预测数
    # 计算并打印准确率
    print('Accuracy of the test set: %d %%' % (100 * correct / total))

if __name__ == '__main__':  # 主程序入口,防止多进程问题
    for epoch in range(10):  # 训练10个epoch
        train(epoch)  # 训练一个epoch
        test()        # 测试当前模型性能

更多推荐