• pytorch-实现运动鞋品牌识别


    我的环境

    🍺要求:

    1. 了解如何设置动态学习率(重点)(✔)
    1. 调整代码使测试集accuracy到达84%。(✔)

    🍻拔高(可选):

    1. 保存训练过程中的最佳模型权重(✔)
    1. 调整代码使测试集accuracy到达86%。(✔)

    目录

    一 前期工作

    二 数据预处理

    数据格式设置

    设置dataset

    检查数据格式 

    三 搭建网络

    四 训练模型

    1.设置学习率

    2.模型训练

    五 模型评估

    1.Loss和Accuracy图

    2.对结果进行预测

     3.总结


    一 前期工作

    环境:python3.6,1080ti,pytorch1.10(实验室服务器的环境😂😂)

    1. import torch
    2. import torch.nn as nn
    3. import matplotlib.pyplot as plt
    4. import torchvision
    5. device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    6. device

     2.导入数据

    1. import os,PIL,random,pathlib
    2. data_dir = './46-data/'
    3. data_dir = pathlib.Path(data_dir)
    4. data_paths = list(data_dir.glob('*'))
    5. classeNames = [str(path).split("/")[1] for path in data_paths]
    6. classeNames

    数据预处理

    数据格式设置

    1. # 关于transforms.Compose的更多介绍可以参考:https://blog.csdn.net/qq_38251616/article/details/124878863
    2. train_transforms = transforms.Compose([
    3. transforms.Resize([224, 224]), # 将输入图片resize成统一尺寸
    4. # transforms.RandomHorizontalFlip(), # 随机水平翻转
    5. transforms.ToTensor(), # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
    6. transforms.Normalize( # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛
    7. mean=[0.485, 0.456, 0.406],
    8. std=[0.229, 0.224, 0.225]) # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
    9. ])
    10. test_transform = transforms.Compose([
    11. transforms.Resize([224, 224]), # 将输入图片resize成统一尺寸
    12. transforms.ToTensor(), # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
    13. transforms.Normalize( # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛
    14. mean=[0.485, 0.456, 0.406],
    15. std=[0.229, 0.224, 0.225]) # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
    16. ])
    17. train_dataset = datasets.ImageFolder("./46-data/train/",transform=train_transforms)
    18. test_dataset = datasets.ImageFolder("./46-data/test/",transform=train_transforms)

    设置dataset

    1. batch_size = 32
    2. train_dl = torch.utils.data.DataLoader(train_dataset,
    3. batch_size=batch_size,
    4. shuffle=True,
    5. num_workers=1)
    6. test_dl = torch.utils.data.DataLoader(test_dataset,
    7. batch_size=batch_size,
    8. shuffle=True,
    9. num_workers=1)

    检查数据格式 

    1. for X, y in test_dl:
    2. print("Shape of X [N, C, H, W]: ", X.shape)
    3. print("Shape of y: ", y.shape, y.dtype)
    4. break

    三 搭建网络

     

    1. import torch.nn.functional as F
    2. class Model(nn.Module):
    3. def __init__(self):
    4. super(Model, self).__init__()
    5. self.conv1=nn.Sequential(
    6. nn.Conv2d(3, 12, kernel_size=5, padding=0), # 12*220*220
    7. nn.BatchNorm2d(12),
    8. nn.ReLU())
    9. self.conv2=nn.Sequential(
    10. nn.Conv2d(12, 12, kernel_size=5, padding=0), # 12*216*216
    11. nn.BatchNorm2d(12),
    12. nn.ReLU())
    13. self.pool3=nn.Sequential(
    14. nn.MaxPool2d(2)) # 12*108*108
    15. self.conv4=nn.Sequential(
    16. nn.Conv2d(12, 24, kernel_size=5, padding=0), # 24*104*104
    17. nn.BatchNorm2d(24),
    18. nn.ReLU())
    19. self.conv4_1=nn.Sequential(
    20. nn.Conv2d(24, 48, kernel_size=5, padding=0), # 24*100*100
    21. nn.BatchNorm2d(48),
    22. nn.ReLU())
    23. self.conv5=nn.Sequential(
    24. nn.Conv2d(48, 24, kernel_size=5, padding=0), # 24*96*96
    25. nn.BatchNorm2d(24),
    26. nn.ReLU())
    27. self.pool6=nn.Sequential(
    28. nn.MaxPool2d(2)) # 24*48*48
    29. self.dropout = nn.Sequential(
    30. nn.Dropout(0.2))
    31. self.fc=nn.Sequential(
    32. nn.Linear(24*48*48, len(classeNames)))
    33. def forward(self, x):
    34. batch_size = x.size(0)
    35. x = self.conv1(x) # 卷积-BN-激活
    36. x = self.conv2(x) # 卷积-BN-激活
    37. x = self.pool3(x) # 池化
    38. x = self.conv4(x) # 卷积-BN-激活
    39. x = self.conv4_1(x)
    40. x = self.conv5(x) # 卷积-BN-激活
    41. x = self.pool6(x) # 池化
    42. x = self.dropout(x)
    43. x = x.view(batch_size, -1) # flatten 变成全连接网络需要的输入 (batch, 24*50*50) ==> (batch, -1), -1 此处自动算出的是24*50*50
    44. x = self.fc(x)
    45. return x
    46. device = "cuda" if torch.cuda.is_available() else "cpu"
    47. print("Using {} device".format(device))
    48. model = Model().to(device)
    49. model

    打印网络结构

    四 训练模型

    1.设置学习率

    1. def adjust_learning_rate(optimizer, epoch, start_lr):
    2. # 每 2 个epoch衰减到原来的 0.98
    3. lr = start_lr * (0.92 ** (epoch // 2))
    4. for param_group in optimizer.param_groups:
    5. param_group['lr'] = lr
    6. learn_rate = 1e-4 # 初始学习率
    7. optimizer = torch.optim.SGD(model.parameters(), lr=learn_rate)

    2.模型训练

    训练函数

    1. # 训练循环
    2. def train(dataloader, model, loss_fn, optimizer):
    3. size = len(dataloader.dataset) # 训练集的大小
    4. num_batches = len(dataloader) # 批次数目, (size/batch_size,向上取整)
    5. train_loss, train_acc = 0, 0 # 初始化训练损失和正确率
    6. for X, y in dataloader: # 获取图片及其标签
    7. X, y = X.to(device), y.to(device)
    8. # 计算预测误差
    9. pred = model(X) # 网络输出
    10. loss = loss_fn(pred, y) # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失
    11. # 反向传播
    12. optimizer.zero_grad() # grad属性归零
    13. loss.backward() # 反向传播
    14. optimizer.step() # 每一步自动更新
    15. # 记录acc与loss
    16. train_acc += (pred.argmax(1) == y).type(torch.float).sum().item()
    17. train_loss += loss.item()
    18. train_acc /= size
    19. train_loss /= num_batches
    20. return train_acc, train_loss

    测试函数

    1. def test (dataloader, model, loss_fn):
    2. size = len(dataloader.dataset) # 测试集的大小
    3. num_batches = len(dataloader) # 批次数目, (size/batch_size,向上取整)
    4. test_loss, test_acc = 0, 0
    5. # 当不进行训练时,停止梯度更新,节省计算内存消耗
    6. with torch.no_grad():
    7. for imgs, target in dataloader:
    8. imgs, target = imgs.to(device), target.to(device)
    9. # 计算loss
    10. target_pred = model(imgs)
    11. loss = loss_fn(target_pred, target)
    12. test_loss += loss.item()
    13. test_acc += (target_pred.argmax(1) == target).type(torch.float).sum().item()
    14. test_acc /= size
    15. test_loss /= num_batches
    16. return test_acc, test_loss

    具体训练代码 

    1. loss_fn = nn.CrossEntropyLoss() # 创建损失函数
    2. epochs = 40
    3. min_acc = 0
    4. train_loss = []
    5. train_acc = []
    6. test_loss = []
    7. test_acc = []
    8. for epoch in range(epochs):
    9. # 更新学习率(使用自定义学习率时使用)
    10. adjust_learning_rate(optimizer, epoch, learn_rate)
    11. model.train()
    12. epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, optimizer)
    13. # scheduler.step() # 更新学习率(调用官方动态学习率接口时使用)
    14. model.eval()
    15. epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)
    16. if epoch_test_acc > min_acc :
    17. min_acc = epoch_test_acc
    18. print("save model")
    19. # 保存模型语句
    20. PATH = './bestmodel'+'%d'%epoch+'.pth' # 保存的参数文件名
    21. torch.save(model.state_dict(), PATH )
    22. else :
    23. print("不能保存")
    24. train_acc.append(epoch_train_acc)
    25. train_loss.append(epoch_train_loss)
    26. test_acc.append(epoch_test_acc)
    27. test_loss.append(epoch_test_loss)
    28. # 获取当前的学习率
    29. lr = optimizer.state_dict()['param_groups'][0]['lr']
    30. template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%, Test_loss:{:.3f}, Lr:{:.2E}')
    31. print(template.format(epoch+1, epoch_train_acc*100, epoch_train_loss,
    32. epoch_test_acc*100, epoch_test_loss, lr))
    33. print('Done')

     

    五 模型评估

    1.Loss和Accuracy图

    1. import matplotlib.pyplot as plt
    2. #隐藏警告
    3. import warnings
    4. warnings.filterwarnings("ignore") #忽略警告信息
    5. plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
    6. plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
    7. plt.rcParams['figure.dpi'] = 100 #分辨率
    8. epochs_range = range(epochs)
    9. plt.figure(figsize=(12, 3))
    10. plt.subplot(1, 2, 1)
    11. plt.plot(epochs_range, train_acc, label='Training Accuracy')
    12. plt.plot(epochs_range, test_acc, label='Test Accuracy')
    13. plt.legend(loc='lower right')
    14. plt.title('Training and Validation Accuracy')
    15. plt.subplot(1, 2, 2)
    16. plt.plot(epochs_range, train_loss, label='Training Loss')
    17. plt.plot(epochs_range, test_loss, label='Test Loss')
    18. plt.legend(loc='upper right')
    19. plt.title('Training and Validation Loss')
    20. plt.show()

     

    2.对结果进行预测

    1. from PIL import Image
    2. classes = list(train_dataset.class_to_idx)
    3. def predict_one_image(image_path, model, transform, classes):
    4. test_img = Image.open(image_path).convert('RGB')
    5. # plt.imshow(test_img) # 展示预测的图片
    6. test_img = transform(test_img)
    7. img = test_img.to(device).unsqueeze(0)
    8. model.eval()
    9. output = model(img)
    10. _,pred = torch.max(output,1)
    11. pred_class = classes[pred]
    12. print(f'预测结果是:{pred_class}')
    1. # 预测训练集中的某张照片
    2. predict_one_image(image_path='./46-data/test/adidas/1.jpg',
    3. model=model,
    4. transform=train_transforms,
    5. classes=classes)

     3.总结

    1.本次保留了上次所写根据准确率大小选择是否要保存当前模型

    2.本次加入了k导的动态学习率设置

  • 相关阅读:
    基于微信小程序电影交流平台源码成品(微信小程序毕业设计)
    Vue+element ui实现好看的个人中心
    北京大学肖臻老师《区块链技术与应用》公开课笔记:以太坊原理(二):GHOST、挖矿算法、难度调整、权益证明
    面试官问:Go 中的参数传递是值传递还是引用传递?
    零基础小白该如何学黑客?
    部门新来了个腾讯拿30k跳槽出来的,让我见识到了跳槽天花板
    27、商户查询缓存(添加商户缓存)
    HTTP发起请求与收到响应的大致过程
    信息学奥赛一本通:2048:【例5.18】串排序
    每日一题(LeetCode)----链表--分隔链表
  • 原文地址:https://blog.csdn.net/m0_60524373/article/details/127752401