import numpy as np
import pandas as pd
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
import matplotlib.pyplot as plt
import seaborn as sns
import copy
import time
import torch
import torch.nn as nn
from torch.optim import Adam
import torch.utils.data as Data
from torchvision import transforms
from torchvision.datasets import FashionMNIST
##使用FashionMNIST数据,准备训练数据集
train_data = FashionMNIST(
root = "./data/FashionMNIST",
train = True,
transform = transforms.ToTensor(),
download = True
)
##准备dataloader
train_loader = Data.DataLoader(
dataset = train_data,
batch_size = 64,
shuffle=False,
num_workers=2
)
print("train_loader的batch数量为:", len(train_loader))
train_loader的batch数量为: 938
##获得一个batch的图像
for step, (b_x,b_y) in enumerate(train_loader):
if step > 0:
break
##可视化一个batch的图像
batch_x = b_x.squeeze().numpy()
batch_y = b_y.numpy()
class_label = train_data.classes
class_label[0] = "T-shirt"
plt.figure(figsize=(12,5))
for ii in np.arange(len(batch_y)):
plt.subplot(4,16,ii+1)
plt.imshow(batch_x[ii,:,:],cmap=plt.cm.gray)
plt.title(class_label[batch_y[ii]], size = 9)
plt.axis("off")
plt.subplots_adjust(wspace = 0.05)
##对测试集进行处理
test_data = FashionMNIST(
root="./data/FashionMNIST",
train=False,
download=False
)
# 直接从数据集中获取数据
test_data_x = test_data.data.type(torch.FloatTensor) / 255.0
test_data_x = test_data_x.unsqueeze(1)
test_data_y = test_data.targets
print("test_data_x.shape:", test_data_x.shape)
print("test_data_y.shape:", test_data_y.shape)
test_data_x.shape: torch.Size([10000, 1, 28, 28])
test_data_y.shape: torch.Size([10000])
class MyConvnet(nn.Module):
def __init__(self):
super(MyConvnet, self).__init__()
self.conv1 = nn.Sequential(
nn.Conv2d(1, 16, 3, 1 ,1),
nn.ReLU(),
nn.AvgPool2d(2,2)
)
self.conv2 = nn.Sequential(
nn.Conv2d(16, 32, 3, 1, 0),
nn.ReLU(),
nn.AvgPool2d(2,2)
)
self.classifier = nn.Sequential(
nn.Linear(32*6*6, 256), # 正确的输入特征数量
nn.ReLU(),
nn.Linear(256, 128),
nn.ReLU(),
nn.Linear(128, 10)
)
def forward(self, x):
x = self.conv1(x)
x = self.conv2(x)
x = x.view(x.size(0), -1) # 展平特征图以匹配全连接层的输入
output = self.classifier(x)
return output
myconvnet = MyConvnet()
print(myconvnet)
MyConvnet(
(conv1): Sequential(
(0): Conv2d(1, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): ReLU()
(2): AvgPool2d(kernel_size=2, stride=2, padding=0)
)
(conv2): Sequential(
(0): Conv2d(16, 32, kernel_size=(3, 3), stride=(1, 1))
(1): ReLU()
(2): AvgPool2d(kernel_size=2, stride=2, padding=0)
)
(classifier): Sequential(
(0): Linear(in_features=1152, out_features=256, bias=True)
(1): ReLU()
(2): Linear(in_features=256, out_features=128, bias=True)
(3): ReLU()
(4): Linear(in_features=128, out_features=10, bias=True)
)
)
def train_model(model,traindataloader,train_rate,
criterion,optimizer,num_epochs=2):
##计算训练使用的batch数量
batch_num = len(traindataloader)
train_batch_num = round(batch_num * train_rate)
##复制模型的参数
best_model_wts = copy.deepcopy(model.state_dict())
best_acc = 0.0
train_loss_all = []
train_acc_all = []
val_loss_all = []
val_acc_all = []
since = time.time()
for epoch in range(num_epochs):
print('Epoch {}/{}'.format(epoch, num_epochs - 1))
print('-' * 10)
##每个epoch有两个训练阶段
train_loss = 0.0
train_corrects = 0
train_num = 0
val_loss = 0.0
val_corrects = 0
val_num = 0
for step, (b_x,b_y) in enumerate(traindataloader):
if step < train_batch_num:
model.train()##设置为训练模式
output = model(b_x)
pre_lab = torch.argmax(output,1)
loss = criterion(output, b_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_loss += loss.item() * b_x.size(0)
train_corrects += torch.sum(pre_lab == b_y.data)
train_num += b_x.size(0)
else:
model.eval()##设置为评估模式
output = model(b_x)
pre_lab = torch.argmax(output,1)
loss = criterion(output,b_y)
val_loss += loss.item() * b_x.size(0)
val_corrects += torch.sum(pre_lab == b_y.data)
val_num += b_x.size(0)
##计算一个epoch再训练集和验证集上的损失和精度
train_loss_all.append(train_loss / train_num)
train_acc_all.append(train_corrects.double().item()/train_num)
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("Train and val complete in {:.0f}m {:.0f}s".format(time_use // 60, time_use % 60))
##使用最好模型的参数
model.load_state_dict(best_model_wts)
# 确保DataFrame的列长度一致
for lst in [train_loss_all, train_acc_all,
val_loss_all, val_acc_all]:
while len(lst) < num_epochs:
lst.append(None)
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 model,train_process
##对模型进行训练
optimizer = torch.optim.Adam(myconvnet.parameters(), lr=0.0003)
criterion = nn.CrossEntropyLoss() ##损失函数
myconvnet, train_process = train_model(myconvnet, train_loader, 0.8,
criterion, optimizer, num_epochs=25)
Epoch 0/24
----------
0 train_loss: 0.1649 Train Acc: 0.9414
0 Val Loss: 0.2487 val Acc: 0.9133
Train and val complete in 0m 21s
Epoch 1/24
----------
1 train_loss: 0.1597 Train Acc: 0.9430
1 Val Loss: 0.2483 val Acc: 0.9129
Train and val complete in 0m 40s
Epoch 2/24
----------
2 train_loss: 0.1549 Train Acc: 0.9446
2 Val Loss: 0.2500 val Acc: 0.9130
Train and val complete in 0m 59s
Epoch 3/24
----------
3 train_loss: 0.1504 Train Acc: 0.9467
3 Val Loss: 0.2526 val Acc: 0.9134
Train and val complete in 1m 17s
Epoch 4/24
----------
4 train_loss: 0.1459 Train Acc: 0.9480
4 Val Loss: 0.2543 val Acc: 0.9140
Train and val complete in 1m 36s
Epoch 5/24
----------
......
----------
23 train_loss: 0.0689 Train Acc: 0.9771
23 Val Loss: 0.3662 val Acc: 0.9060
Train and val complete in 7m 49s
Epoch 24/24
----------
24 train_loss: 0.0665 Train Acc: 0.9775
24 Val Loss: 0.3762 val Acc: 0.9056
Train and val complete in 8m 8s
##可视化模型
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()
##对测试机进行预测,并可视化预测效果
myconvnet.eval()
output = myconvnet(test_data_x)
pre_lab = torch.argmax(output,1)
acc = accuracy_score(test_data_y, pre_lab)
print("在测试集上的预测精度为:",acc)
在测试集上的预测精度为: 0.9103
##计算混淆矩阵并可视化
conf_mat = confusion_matrix(test_data_y,pre_lab)
df_cm = pd.DataFrame(conf_mat, index=class_label,columns=class_label)
heatmap = sns.heatmap(df_cm, annot=True,fmt="d", cmap="YlGnBu")
heatmap.yaxis.set_ticklabels(heatmap.yaxis.get_ticklabels(),rotation=0
,ha='right')
heatmap.xaxis.set_ticklabels(heatmap.xaxis.get_ticklabels(),rotation=45
,ha='right')
plt.ylabel('True label')
plt.xlabel('Predictied lable')
plt.show()
class MyConvdilaNet(nn.Module):
def __init__(self):
super(MyConvdilaNet,self)
self.conv1 = nn.Sequential(
nn.Conv2d(1,16,3,1,1,dilation=2),
nn.ReLU(),
nn.AvgPool2d(2,2)
)
self.conv2 = nn.Sequential(
nn.Conv2d(16,32,3,1,0,dilation=2),
nn.ReLU(),
nn.AvgPool2d(2,2)
)
self.classifier = nn.Sequential(
nn.Linear(32*4*4,256),
nn.ReLU(),
nn.Linear(256,128),
nn.ReLU(),
nn.Linear(128,10)
)
##定义前向传播路径
def forward(self, x):
x = self.conv1(x)
x = self.conv2(x)
x = x.view(x.size(0), -1)
output = self.classifier(x)
return output
myconvdilanet = MyConvdilaNet()
##对模型进行训练
optimizer = torch.optim.Adam(myconvdilanet.parameters(), lr=0.0003)
criterion = nn.CrossEntropyLoss() ##损失函数
myconvdilanet, train_process = train_model(myconvdilanet, train_loader, 0.8,
criterion, optimizer, num_epochs=25)
##可视化模型
plt.figure(figsize=(12,4))
plt.subplot(1,2,1)
plt.plot(train_process.epoch, train_process.train_loss_all,
"ro-",lable = "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()
##对测试机进行预测,并可视化预测效果
myconvnet.eval()
output = myconvnet(test_data_x)
pre_lab = torch.argmax(output,1)
acc = accuracy_score(test_data_y, pre_lab)
print("在测试集上的预测精度为:",acc)