【深度学习】使用PyTorch实现图像分类+所有代码+详细注释 |
您所在的位置:网站首页 › 亚马逊上传库存文件选哪个类别好 › 【深度学习】使用PyTorch实现图像分类+所有代码+详细注释 |
使用PyTorch实现图像分类
本文将介绍如何使用PyTorch实现利用神经网络在图像数据集上进行训练和如何利用训练好的模型对图像进行分类 创建文件夹,用于保存训练好的网络 import os if not os.path.exists("./save_model_rs_dataset"): os.mkdir("./save_model_rs_dataset") 复制代码 1. 定义模型 1.1 一个小的神经网络==说明:自己的数据集结构应该和下面一致(val可以不用),每个文件夹下是各个类别的图像,文件夹名即为类别==
图中数据集可以自行下载:Satellite Image Classification数据集 设置设备 # 如果GPU可用,利用GPU进行训练 device = torch.device("cuda" if torch.cuda.is_available() else "cpu") 复制代码创建网络 # 实例化网络 net = MyNet(num_classes=len(classes)).to(device) 复制代码 3. 定义训练参数 from torch.optim import lr_scheduler # 4. 损失函数 loss_fn = nn.CrossEntropyLoss() # 学习率 learning_rate = 0.001 # 5. 优化器 # 定义优化器(SGD:随机梯度下降) # optimizer = torch.optim.SGD(net.parameters(), lr=learning_rate) optimizer = torch.optim.Adam(net.parameters(), lr=learning_rate) # 学习率衰减⽅法:学习率每隔 step_size 个 epoch 变为原来的 gamma lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=15, gamma=0.1) # 训练轮数 epoch = 100 # 保存训练过程中的loss和精度 train_acc_lst, test_acc_lst = [], [] train_loss_lst, tset_loss_lst = [], [] # 记录训练过程中最大的精度 max_train_acc = 0 max_test_acc = 0 复制代码通道转换函数 import numpy as np # 单通道转为三通道 def transfer_channel(image): image = np.array(image) image = image.transpose((1, 0, 2, 3)) # array 转置 image = np.concatenate((image, image, image), axis=0) image = image.transpose((1, 0, 2, 3)) # array 转置回来 image = torch.tensor(image) # 将 numpy 数据格式转为 tensor return image 复制代码计算精度和loss函数 def compute_accuracy_and_loss(model, dataset, data_loader, device): correct, total = .0, .0 for i, (features, targets) in enumerate(data_loader): # 通道转换 if features.size(1) == 1: features = transfer_channel(features) features = features.to(device) targets = targets.to(device) output = model(features) currnet_loss = loss_fn(output, targets) # 求预测结果精确度之和 # argmax:求最大值的下标,1按行求,0按列求 # correct += (output.argmax(1) == targets).sum() _, predicted_labels = torch.max(output, 1) correct += (predicted_labels == targets).sum() # 更新混淆矩阵数据 for idx in range(len(targets)): cnf_matrix[targets[idx]][predicted_labels[idx]] += 1 total += targets.size(0) return float(correct) * 100 / len(dataset), currnet_loss.item() 复制代码 4. 训练 import time start_time = time.time() print(net) for i in range(epoch): print("---------开始第{}轮训练,本轮学习率为:{}---------".format((i + 1), lr_scheduler.get_last_lr())) # 记录每轮训练批次数,每100次进行一次输出 count_train = 0 # 训练步骤开始 net.train() # 将网络设置为训练模式,当网络包含 Dropout, BatchNorm时必须设置,其他时候无所谓 for (features, targets) in train_dataloader: # 通道转换 if features.size(1) == 1: features = transfer_channel(features) # 将图像和标签移动到指定设备上 features = features.to(device) targets = targets.to(device) # 梯度清零,也就是把loss关于weight的导数变成0. # 进⾏下⼀次batch梯度计算的时候,前⼀个batch的梯度计算结果,没有保留的必要了。所以在下⼀次梯度更新的时候,先使⽤optimizer.zero_grad把梯度信息设置为0。 optimizer.zero_grad() # 获取网络输出 output = net(features) # 获取损失 loss = loss_fn(output, targets) # 反向传播 loss.backward() # 训练 optimizer.step() # 纪录训练次数 count_train += 1 # item()函数会直接输出值,比如tensor(5),会输出5 if count_train % 100 == 0: # 记录时间 end_time = time.time() print(f"训练批次{count_train}/{len(train_dataloader)},loss:{loss.item():.3f},用时:{(end_time - start_time):.2f}" ) # 将网络设置为测试模式,当网络包含 Dropout, BatchNorm时必须设置,其他时候无所谓 net.eval() with torch.no_grad(): # 计算训练精度 train_accuracy, train_loss = compute_accuracy_and_loss(net, train_dataset, train_dataloader, device=device) # 更新最高精度 if train_accuracy > max_train_acc[1]: max_train_acc[0] = i max_train_acc[1] = train_accuracy # 计算测试精度 test_accuracy, test_loss = compute_accuracy_and_loss(net, test_dataset, test_dataloader, device=device) # 更新最高精度 if test_accuracy > max_test_acc[1]: max_test_acc[0] = i max_test_acc[1] = test_accuracy # 收集训练过程精度和loss train_loss_lst.append(train_loss) train_acc_lst.append(train_accuracy) tset_loss_lst.append(test_loss) test_acc_lst.append(test_accuracy) print(f'Epoch: {i + 1:03d}/{epoch:03d}') print(f'Train Loss.: {train_loss:.2f}' f' | Validation Loss.: {test_loss:.2f}') print(f'Train Acc.: {train_accuracy:.2f}%' f' | Validation Acc.: {test_accuracy:.2f}%') # 训练计时 elapsed = (time.time() - start_time) / 60 print(f'本轮训练累计用时: {elapsed:.2f} min') # 保存达标的训练的模型 if test_accuracy > 80: torch.save(net.state_dict(), "save_model_rs_dataset/train_model_{}.pth".format(i)) print("第{}次训练模型已保存".format(i + 1)) # 更新学习率 lr_scheduler.step() print('DONE!') 复制代码部分内容参考自:VGG16识别MNIST数据集(Pytorch实战) 输出(以下均以AlexNet为例) # 网络结构 MyNet( (feature_extraction): Sequential( (0): Conv2d(3, 96, kernel_size=(11, 11), stride=(4, 4), padding=(2, 2), bias=False) (1): ReLU(inplace=True) (2): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False) (3): Conv2d(96, 192, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2), bias=False) (4): ReLU(inplace=True) (5): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False) (6): Conv2d(192, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (7): ReLU(inplace=True) (8): Conv2d(384, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (9): ReLU(inplace=True) (10): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) (11): ReLU(inplace=True) (12): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False) ) (classifier): Sequential( (0): Dropout(p=0.5, inplace=False) (1): Linear(in_features=9216, out_features=4096, bias=True) (2): ReLU(inplace=True) (3): Dropout(p=0.5, inplace=False) (4): Linear(in_features=4096, out_features=4096, bias=True) (5): ReLU(inplace=True) (6): Linear(in_features=4096, out_features=4, bias=True) ) ) 复制代码训练过程输出 ---------开始第1轮训练,本轮学习率为:[0.001]--------- Epoch: 001/050 Train Loss.: 0.64 | Validation Loss.: 0.60 Train Acc.: 62.09% | Validation Acc.: 63.93% 本轮训练累计用时: 0.61 min ---------开始第2轮训练,本轮学习率为:[0.001]--------- Epoch: 002/050 Train Loss.: 0.76 | Validation Loss.: 0.64 Train Acc.: 66.24% | Validation Acc.: 66.79% 本轮训练累计用时: 1.03 min ---------开始第3轮训练,本轮学习率为:[0.001]--------- Epoch: 003/050 Train Loss.: 0.63 | Validation Loss.: 0.68 Train Acc.: 57.81% | Validation Acc.: 60.71% 本轮训练累计用时: 1.44 min ...... 复制代码 5. 显示Loss和Acc 5.1 使用plot import matplotlib.pyplot as plt plt.figure(dpi=480,figsize=(12,5)) # 训练损失和测试损失关系图 plt.plot(range(1, epoch + 1), train_loss_lst, label='Training loss') plt.plot(range(1, epoch + 1), tset_loss_lst, label='Validation loss') plt.legend(loc='upper right') plt.ylabel('Cross entropy') plt.xlabel('Epoch') plt.show() plt.figure(dpi=480,figsize=(12,5)) # 训练精度和测试精度关系图 plt.plot(range(1, epoch + 1), train_acc_lst, label='Training accuracy') plt.plot(range(1, epoch + 1), test_acc_lst, label='Validation accuracy') plt.legend(loc='upper left') plt.ylabel('Accuracy') plt.xlabel('Epoch') plt.show() print("最大训练精度为:", max_train_acc) print("最大测试精度为:", max_test_acc) 复制代码部分内容参考自:VGG16识别MNIST数据集(Pytorch实战) 最大训练精度: [48, 87.82165039929015] 最大测试精度: [28, 89.64285714285714] 5.2 使用混淆矩阵 import itertools import matplotlib.pyplot as plt import numpy as np # 绘制混淆矩阵 def plot_confusion_matrix(cm, classes, normalize=False, title='Confusion matrix', cmap=plt.cm.Blues): """ - cm : 计算出的混淆矩阵的值 - classes : 混淆矩阵中每一行每一列对应的列 - normalize : True:显示百分比, False:显示个数 """ if normalize: cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis] # print("显示百分比:") np.set_printoptions(formatter={'float': '{: 0.2f}'.format}) # print(cm) # else: # print('显示具体数字:') # print(cm) plt.figure(dpi=320,figsize=(16,16)) plt.imshow(cm, interpolation='nearest', cmap=cmap) plt.title(title) plt.colorbar() tick_marks = np.arange(len(classes)) plt.xticks(tick_marks, classes, rotation=45) plt.yticks(tick_marks, classes) # matplotlib版本问题,如果不加下面这行代码,则绘制的混淆矩阵上下只能显示一半,有的版本的matplotlib不需要下面的代码,分别试一下即可 plt.ylim(len(classes) - 0.5, -0.5) # fmt = '.2f' if normalize else 'd' fmt = '.2f' thresh = cm.max() / 2. for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])): plt.text(j, i, format(cm[i, j], fmt), horizontalalignment="center", color="white" if cm[i, j] > thresh else "black") plt.tight_layout() plt.ylabel('True label') plt.xlabel('Predicted label') plt.show() # 第一种情况:显示百分比 plot_confusion_matrix(cnf_matrix, classes=classes, normalize=True, title='Normalized confusion matrix') # 第二种情况:显示数字 plot_confusion_matrix(cnf_matrix, classes=classes, normalize=False, title='Normalized confusion matrix') 复制代码参考自:Matplotlib绘制混淆矩阵 输出 加载上述训练过程中效果较好的一个网络进行验证 # 时间 : 2022/5/14 19:59 # 作者 : 冷芝士鸭 from PIL import features from torch.utils.data import DataLoader import torch import torchvision from torchvision import datasets from torchvision.transforms import transforms import matplotlib.pyplot as plt # 对图像进行尺寸变换,因为网络要求的输入是64*64,并且是tensor类型 custom_transform = transforms.Compose([transforms.Resize([224, 224]), transforms.ToTensor()]) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model = torchvision.models.vgg16().to(device) # map_location:指定设备,cpu或者GPU model.load_state_dict(torch.load("./save_model_rs_dataset/vgg16_train_model_38.pth", map_location="cpu")) val_dataset = datasets.ImageFolder( root=r'E:\machine learning\Deep_learning\deep_learning\PyTorch\code\some_models\vgg-demo\VGG16\satelite\Satellite_Image_Classification\val', transform=custom_transform ) classes = val_dataset.classes val_loader = DataLoader(dataset=val_dataset, batch_size=16, shuffle=True) for features, targets in val_loader: predictions = model.forward(features.to(device)) predictions = torch.argmax(predictions, dim=1) plt.figure(figsize=(15, 15)) # 设置窗口大小 for i in range(len(features)): plt.subplot(4, 4, i + 1) plt.title("Prediction:{}\nTarget:{}".format(classes[predictions[i]], classes[targets[i]])) # 解决报错:Invalid shape (3, 224, 224) for image data # 问题产生的原因是由于matplotlib.pyplot 使用时传入的数组型或Tensor型参数应为 img=(224,224,3)这种类型。 # 其中img[0],img[1]为数组或张量的长与宽,img[2]为维度,如‘RPG’为3 img = features[i].swapaxes(0, 1) img = img.swapaxes(1, 2) plt.imshow(img) # 关闭坐标轴 plt.axis('off') plt.show() break 复制代码验证结果
引自:CNN02:Pytorch实现VGG16的CIFAR10分类 一直以来进入了一个误区,一直以为数据图像的大小要匹配/适应网络的输入大小。在LeNet中,网络输入大小为32x32,而MNIST数据集中的图像大小为28x28,当时认为要使两者的大小匹配,将padding设置为2即解决了这个问题。然而,当用VGG训练CIFAR10数据集时,网络输入大小为224x224,而数据大小是32x32,这两者该怎么匹配呢?试过将32用padding的方法填充到224x224,但是运行之后显示内存不足 (笑哭.jpg)。也百度到将数据图像resize成224x224。 这个问题一直困扰了好久,看着代码里没有改动数据尺寸和网络的尺寸,不知道是怎么解决的这个匹配/适应的问题。最后一步步调试才发现在第一个全连接处报错,全连接的输入尺寸和设定的尺寸不一致,再回过头去一步步推数据的尺寸变化,发现原来的VGG网络输入是224x224的,由于卷积层不改变图像的大小,只有池化层才使图像大小缩小一半,所以经过5层卷积池化之后,图像大小缩小为原来的1/32。卷积层的最终输出是7x7x512=25088,所以全连接层的输入设为25088。 当输入图像大小为32x32时,经过5层卷积之后,图像大小缩小为1x1x512,全连接的输入大小就变为了512,所以不匹配的地方在这里,而不是网络的输入处。所以输入的训练图像的大小不必要与网络原始的输入大小一致,只需要计算经过卷积池化后最终的输出(也即全连接层的输入),然后改以下全连接的输入即可。 7.2 将图像数据划分为训练集、测试集、验证集现有数据集如下图,但是没有划分为训练集和测试集,使用下面代码可以进行数据集划分
|
今日新闻 |
点击排行 |
|
推荐新闻 |
图片新闻 |
|
专题文章 |
CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭 |