构建模型
模版搭建
# 定义一个AlexNet模型类
def __init__(self):
# 调用父类的构造函数(如果继承自nn.Module的话)
super(AlexNet, self).__init__()
# ReLU激活函数
self.ReLU = nn.ReLU()
# 卷积层1:输入1个通道(灰度图),输出96个通道,卷积核大小11x11,步幅4
self.c1 = nn.Conv2d(in_channels=1, out_channels=96, kernel_size=11, stride=4)
# 最大池化层2:池化窗口大小为3x3,步幅为2
self.s2 = nn.MaxPool2d(kernel_size=3, stride=2)
# 卷积层2:输入96个通道,输出256个通道,卷积核大小为5x5,使用padding=2保持输出尺寸
self.c3 = nn.Conv2d(in_channels=96, out_channels=256, kernel_size=5, padding=2)
# 最大池化层4:池化窗口大小为3x3,步幅为2
self.s4 = nn.MaxPool2d(kernel_size=3, stride=2)
# 卷积层3:输入256个通道,输出384个通道,卷积核大小为3x3,使用padding=1保持输出尺寸
self.c5 = nn.Conv2d(in_channels=256, out_channels=384, kernel_size=3, padding=1)
# 卷积层4:输入384个通道,输出384个通道,卷积核大小为3x3,使用padding=1保持输出尺寸
self.c6 = nn.Conv2d(in_channels=384, out_channels=384, kernel_size=3, padding=1)
# 卷积层5:输入384个通道,输出256个通道,卷积核大小为3x3,使用padding=1保持输出尺寸
self.c7 = nn.Conv2d(in_channels=384, out_channels=256, kernel_size=3, padding=1)
# 最大池化层8:池化窗口大小为3x3,步幅为2
self.s8 = nn.MaxPool2d(kernel_size=3, stride=2)
# 展平层:将卷积输出的特征图展平为一维向量,供全连接层处理
self.flatten = nn.Flatten()
# 全连接层1:输入维度为6*6*256(假设输入图片尺寸为224x224,经过卷积和池化后的输出尺寸),输出4096维
self.f1 = nn.Linear(6 * 6 * 256, 4096)
# 全连接层2:输入4096维,输出4096维
self.f2 = nn.Linear(4096, 4096)
# 全连接层3:输入4096维,输出10维(表示10个类别的分类输出)
self.f3 = nn.Linear(4096, 10)
forward
def forward(self, x):
# 第1步:通过卷积层 c1 提取特征,并通过 ReLU 激活函数处理
x = self.ReLU(self.c1(x))
# 第2步:通过最大池化层 s2 进行池化操作,减小特征图尺寸
x = self.s2(x)
# 第3步:通过卷积层 c3 提取更高层次的特征,并通过 ReLU 激活函数处理
x = self.ReLU(self.c3(x))
# 第4步:通过最大池化层 s4 进行池化操作
x = self.s4(x)
# 第5步:通过卷积层 c5 提取特征,并通过 ReLU 激活函数处理
x = self.ReLU(self.c5(x))
# 第6步:通过卷积层 c6 提取更多特征,并通过 ReLU 激活函数处理
x = self.ReLU(self.c6(x))
# 第7步:通过卷积层 c7 提取更多特征,并通过 ReLU 激活函数处理
x = self.ReLU(self.c7(x))
# 第8步:通过最大池化层 s8 进行池化操作,进一步减小特征图尺寸
x = self.s8(x)
# 第9步:展平层,将卷积输出的特征图展平成一维向量,准备输入全连接层
x = self.flatten(x)
# 第10步:通过全连接层 f1 提取高层次特征,并通过 ReLU 激活函数处理
x = self.ReLU(self.f1(x))
# 第11步:在全连接层 f1 后应用 dropout,防止过拟合,dropout率为 50%
x = F.dropout(x, 0.5)
# 第12步:通过全连接层 f2 提取更多特征,并通过 ReLU 激活函数处理
x = self.ReLU(self.f2(x))
# 第13步:在全连接层 f2 后应用 dropout,防止过拟合,dropout率为 50%
x = F.dropout(x, 0.5)
# 第14步:通过最后一个全连接层 f3 得到最终的分类输出(10个类别)
x = self.f3(x)
# 返回输出
return x
训练模型
整体代码与LeNet类似,详细分析复习LeNet实践文章
代码实现
def train_val_data_process():
train_data = FashionMNIST(root='./data',
train=True,
transform=transforms.Compose([transforms.Resize(size=227), transforms.ToTensor()]),
download=True)
train_data, val_data = Data.random_split(train_data, [round(0.8*len(train_data)), round(0.2*len(train_data))])
train_dataloader = Data.DataLoader(dataset=train_data,
batch_size=16,
shuffle=True,
num_workers=2)
val_dataloader = Data.DataLoader(dataset=val_data,
batch_size=16,
shuffle=True,
num_workers=2)
return train_dataloader, val_dataloader
def train_model_process(model, train_dataloader, val_dataloader, num_epochs):
# 设定训练所用到的设备,有GPU用GPU没有GPU用CPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# device = torch.device("cpu")
# device = torch.device("cuda")
# 使用Adam优化器,学习率为0.001
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# 损失函数为交叉熵函数
criterion = nn.CrossEntropyLoss()
# 将模型放入到训练设备中
model = model.to(device)
# 复制当前模型的参数
best_model_wts = copy.deepcopy(model.state_dict())
# 初始化参数
# 最高准确度
best_acc = 0.0
# 训练集损失列表
train_loss_all = []
# 验证集损失列表
val_loss_all = []
# 训练集准确度列表
train_acc_all = []
# 验证集准确度列表
val_acc_all = []
# 当前时间
since = time.time()
for epoch in range(num_epochs):
print("Epoch {}/{}".format(epoch, num_epochs-1))
print("-"*10)
# 初始化参数
# 训练集损失函数
train_loss = 0.0
# 训练集准确度
train_corrects = 0
# 验证集损失函数
val_loss = 0.0
# 验证集准确度
val_corrects = 0
# 训练集样本数量
train_num = 0
# 验证集样本数量
val_num = 0
# 对每一个mini-batch训练和计算
for step, (b_x, b_y) in enumerate(train_dataloader):
# 将特征放入到训练设备中
b_x = b_x.to(device)
# 将标签放入到训练设备中
b_y = b_y.to(device)
# 设置模型为训练模式
model.train()
# 前向传播过程,输入为一个batch,输出为一个batch中对应的预测
output = model(b_x)
# 查找每一行中最大值对应的行标
pre_lab = torch.argmax(output, dim=1)
# 计算每一个batch的损失函数
loss = criterion(output, b_y)
# 将梯度初始化为0
optimizer.zero_grad()
# 反向传播计算
loss.backward()
# 根据网络反向传播的梯度信息来更新网络的参数,以起到降低loss函数计算值的作用
optimizer.step()
# 对损失函数进行累加
train_loss += loss.item() * b_x.size(0)
# 如果预测正确,则准确度train_corrects加1
train_corrects += torch.sum(pre_lab == b_y.data)
# 当前用于训练的样本数量
train_num += b_x.size(0)
for step, (b_x, b_y) in enumerate(val_dataloader):
# 将特征放入到验证设备中
b_x = b_x.to(device)
# 将标签放入到验证设备中
b_y = b_y.to(device)
# 设置模型为评估模式
model.eval()
# 前向传播过程,输入为一个batch,输出为一个batch中对应的预测
output = model(b_x)
# 查找每一行中最大值对应的行标
pre_lab = torch.argmax(output, dim=1)
# 计算每一个batch的损失函数
loss = criterion(output, b_y)
# 对损失函数进行累加
val_loss += loss.item() * b_x.size(0)
# 如果预测正确,则准确度train_corrects加1
val_corrects += torch.sum(pre_lab == b_y.data)
# 当前用于验证的样本数量
val_num += b_x.size(0)
# 计算并保存每一次迭代的loss值和准确率
# 计算并保存训练集的loss值
train_loss_all.append(train_loss / train_num)
# 计算并保存训练集的准确率
train_acc_all.append(train_corrects.double().item() / train_num)
# 计算并保存验证集的loss值
val_loss_all.append(val_loss / val_num)
# 计算并保存验证集的准确率
val_acc_all.append(val_corrects.double().item() / val_num)
print("{} train loss:{:.4f} train acc: {:.4f}".format(epoch, train_loss_all[-1], train_acc_all[-1]))
print("{} val loss:{:.4f} val acc: {:.4f}".format(epoch, val_loss_all[-1], val_acc_all[-1]))
if val_acc_all[-1] > best_acc:
# 保存当前最高准确度
best_acc = val_acc_all[-1]
# 保存当前最高准确度的模型参数
best_model_wts = copy.deepcopy(model.state_dict())
# 计算训练和验证的耗时
time_use = time.time() - since
print("训练和验证耗费的时间{:.0f}m{:.0f}s".format(time_use//60, time_use%60))
# 选择最优参数,保存最优参数的模型
model.load_state_dict(best_model_wts)
# torch.save(model.load_state_dict(best_model_wts), "C:/Users/86159/Desktop/LeNet/best_model.pth")
torch.save(best_model_wts, "E:\秋招就业\CNN卷积神经网络\测试用例\AlexNet\\best_model.pth")
train_process = pd.DataFrame(data={"epoch":range(num_epochs),
"train_loss_all":train_loss_all,
"val_loss_all":val_loss_all,
"train_acc_all":train_acc_all,
"val_acc_all":val_acc_all,})
return train_process
def matplot_acc_loss(train_process):
# 显示每一次迭代后的训练集和验证集的损失函数和准确率
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(train_process['epoch'], train_process.train_loss_all, "ro-", label="Train loss")
plt.plot(train_process['epoch'], train_process.val_loss_all, "bs-", label="Val loss")
plt.legend()
plt.xlabel("epoch")
plt.ylabel("Loss")
plt.subplot(1, 2, 2)
plt.plot(train_process['epoch'], train_process.train_acc_all, "ro-", label="Train acc")
plt.plot(train_process['epoch'], train_process.val_acc_all, "bs-", label="Val acc")
plt.xlabel("epoch")
plt.ylabel("acc")
plt.legend()
plt.show()
if __name__ == '__main__':
# 加载需要的模型
AlexNet = AlexNet()
# 加载数据集
train_data, val_data = train_val_data_process()
# 利用现有的模型进行模型的训练
train_process = train_model_process(AlexNet, train_data, val_data, num_epochs=8)
matplot_acc_loss(train_process)
训练结果
poch 0/19
----------
0 train loss:0.5552 train acc: 0.7938
0 val loss:0.3909 val acc: 0.8588
训练和验证耗费的时间3m36s
Epoch 1/19
----------
1 train loss:0.3775 train acc: 0.8642
1 val loss:0.3952 val acc: 0.8555
训练和验证耗费的时间7m6s
Epoch 2/19
----------
2 train loss:0.3436 train acc: 0.8736
2 val loss:0.3405 val acc: 0.8824
训练和验证耗费的时间10m36s
Epoch 3/19
----------
3 train loss:0.3290 train acc: 0.8820
3 val loss:0.3438 val acc: 0.8778
训练和验证耗费的时间14m7s
Epoch 4/19
----------
4 train loss:0.3233 train acc: 0.8843
4 val loss:0.3224 val acc: 0.8826
训练和验证耗费的时间17m37s
Epoch 5/19
----------
5 train loss:0.3158 train acc: 0.8863
5 val loss:0.3457 val acc: 0.8764
训练和验证耗费的时间21m7s
Epoch 6/19
----------
6 train loss:0.3136 train acc: 0.8851
6 val loss:0.3431 val acc: 0.8812
训练和验证耗费的时间24m38s
Epoch 7/19
----------
7 train loss:0.3103 train acc: 0.8873
7 val loss:0.3542 val acc: 0.8716
训练和验证耗费的时间28m7s
Epoch 8/19
----------
8 train loss:0.3022 train acc: 0.8919
8 val loss:0.3463 val acc: 0.8759
训练和验证耗费的时间31m37s
Epoch 9/19
----------
9 train loss:0.2981 train acc: 0.8919
9 val loss:0.3462 val acc: 0.8792
训练和验证耗费的时间35m7s
Epoch 10/19
----------
10 train loss:0.3009 train acc: 0.8909
10 val loss:0.3683 val acc: 0.8682
训练和验证耗费的时间38m37s
Epoch 11/19
----------
11 train loss:0.2968 train acc: 0.8923
11 val loss:0.5043 val acc: 0.8171
训练和验证耗费的时间42m8s
Epoch 12/19
----------
12 train loss:0.3037 train acc: 0.8888
12 val loss:0.3076 val acc: 0.8921
训练和验证耗费的时间45m38s
Epoch 13/19
----------
13 train loss:0.2950 train acc: 0.8932
13 val loss:0.3680 val acc: 0.8776
训练和验证耗费的时间49m8s
Epoch 14/19
----------
14 train loss:0.3002 train acc: 0.8925
14 val loss:0.3667 val acc: 0.8712
训练和验证耗费的时间52m38s
Epoch 15/19
----------
15 train loss:0.2911 train acc: 0.8945
15 val loss:0.3753 val acc: 0.8758
训练和验证耗费的时间56m9s
Epoch 16/19
----------
16 train loss:0.2958 train acc: 0.8937
16 val loss:0.3534 val acc: 0.8778
训练和验证耗费的时间59m39s
Epoch 17/19
----------
17 train loss:0.2981 train acc: 0.8918
17 val loss:0.3350 val acc: 0.8905
训练和验证耗费的时间63m9s
Epoch 18/19
----------
18 train loss:0.2876 train acc: 0.8968
18 val loss:0.3525 val acc: 0.8891
训练和验证耗费的时间66m39s
Epoch 19/19
----------
19 train loss:0.2937 train acc: 0.8941
19 val loss:0.3477 val acc: 0.8778
训练和验证耗费的时间70m9s
测试模型
测试代码
def test_data_process():
test_data = FashionMNIST(root='./data',
train=False,
transform=transforms.Compose([transforms.Resize(size=227), transforms.ToTensor()]),
download=True)
test_dataloader = Data.DataLoader(dataset=test_data,
batch_size=1,
shuffle=True,
num_workers=0)
return test_dataloader
def test_model_process(model, test_dataloader):
# 设定测试所用到的设备,有GPU用GPU没有GPU用CPU
device = "cuda" if torch.cuda.is_available() else 'cpu'
# 讲模型放入到训练设备中
model = model.to(device)
# 初始化参数
test_corrects = 0.0
test_num = 0
# 只进行前向传播计算,不计算梯度,从而节省内存,加快运行速度
with torch.no_grad():
for test_data_x, test_data_y in test_dataloader:
# 将特征放入到测试设备中
test_data_x = test_data_x.to(device)
# 将标签放入到测试设备中
test_data_y = test_data_y.to(device)
# 设置模型为评估模式
model.eval()
# 前向传播过程,输入为测试数据集,输出为对每个样本的预测值
output= model(test_data_x)
# 查找每一行中最大值对应的行标
pre_lab = torch.argmax(output, dim=1)
# 如果预测正确,则准确度test_corrects加1
test_corrects += torch.sum(pre_lab == test_data_y.data)
# 将所有的测试样本进行累加
test_num += test_data_x.size(0)
# 计算测试准确率
test_acc = test_corrects.double().item() / test_num
print("测试的准确率为:", test_acc)
if __name__ == "__main__":
# 加载模型
model = AlexNet()
model.load_state_dict(torch.load('best_model.pth'))
# # 利用现有的模型进行模型的测试
test_dataloader = test_data_process()
test_model_process(model, test_dataloader)
# 设定测试所用到的设备,有GPU用GPU没有GPU用CPU
device = "cuda" if torch.cuda.is_available() else 'cpu'
model = model.to(device)
classes = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']
with torch.no_grad():
for b_x, b_y in test_dataloader:
b_x = b_x.to(device)
b_y = b_y.to(device)
# 设置模型为验证模型
model.eval()
output = model(b_x)
pre_lab = torch.argmax(output, dim=1)
result = pre_lab.item()
label = b_y.item()
print("预测值:", classes[result], "------", "真实值:", classes[label])
测试结果
测试的准确率为: 0.8871
预测值: Ankle boot ------ 真实值: Ankle boot
预测值: Pullover ------ 真实值: Pullover
预测值: Shirt ------ 真实值: T-shirt/top
预测值: Dress ------ 真实值: Dress
预测值: T-shirt/top ------ 真实值: T-shirt/top
预测值: Shirt ------ 真实值: Pullover
预测值: Dress ------ 真实值: Dress
预测值: Coat ------ 真实值: Coat
预测值: Sneaker ------ 真实值: Sneaker
预测值: Trouser ------ 真实值: Trouser
预测值: Shirt ------ 真实值: Shirt
预测值: Pullover ------ 真实值: Pullover
预测值: Ankle boot ------ 真实值: Ankle boot
预测值: Ankle boot ------ 真实值: Ankle boot
预测值: Coat ------ 真实值: Coat
预测值: Coat ------ 真实值: Coat
预测值: T-shirt/top ------ 真实值: T-shirt/top
预测值: T-shirt/top ------ 真实值: T-shirt/top
预测值: T-shirt/top ------ 真实值: T-shirt/top
预测值: Dress ------ 真实值: Shirt
预测值: Shirt ------ 真实值: Shirt
预测值: Trouser ------ 真实值: Trouser
预测值: Dress ------ 真实值: Dress
...............................................