• AI项目十七:ResNet50训练部署教程


    若该文为原创文章,转载请注明原文出处。

    ResNet50训练主要还是想部署到RK3568开发板上,先记录下训练和转成ONNX模型过程。

    一、 Resnet50简介

           ResNet50网络是2015年由微软实验室的何恺明提出,获得ILSVRC2015图像分类竞赛第一名。在ResNet网络提出之前,传统的卷积神经网络都是将一系列的卷积层和池化层堆叠得到的,但当网络堆叠到一定深度时,就会出现退化问题。 残差网络的特点是容易优化,并且能够通过增加相当的深度来提高准确率。其内部的残差块使用了跳跃连接,缓解了在深度神经网络中增加深度带来的梯度消失问题。

    二、数据集下载

           本教程以车辆分类算法为例,数据集的百度网盘下载链接为:

    https://pan.baidu.com/s/1pkYm9AA3s3WDM7GecShlbQ 提取码:6666

           解压完成后得到以下两个文件夹:

           打开可以看到一共10类汽车:

    三、环境搭建

    1、创建虚拟环境

    conda create -n Resnet50_env python=3.8 -y

    2、激活环境

    conda activate Resnet50_env

    注意:使用的是CPU版本,电脑无GPU

    3、安装环境

    1. pip install numpy
    2. pip install torch
    3. pip install torchvision
    4. pip install matplotlib

    至此,环境安装完成,开始训练

    四、 ResNet50图像分类训练

    直接上源码:train.py

    1. # -#-coding:utf-8 -*-
    2. import os
    3. import numpy as np
    4. import torch
    5. import torch.nn as nn
    6. import torch.nn.functional as F
    7. import torch.optim as optim
    8. import torchvision
    9. from torch.autograd.variable import Variable
    10. from torch.utils.data import DataLoader
    11. from torchvision import datasets, transforms
    12. import matplotlib.pyplot as plt
    13. from PIL import ImageFile
    14. ImageFile.LOAD_TRUNCATED_IMAGES = True
    15. # 2.定义超参数
    16. BATCH_SIZE = 16 # 每批处理的数据
    17. DEVICE = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') # 放在cuda或者cpu上训练
    18. EPOCHS = 15 # 训练数据集的轮次
    19. modellr = 1e-3
    20. # 3.构建pipeline,对图像做处理
    21. pipeline = transforms.Compose([
    22. # 分辨率重置为256
    23. transforms.Resize(256),
    24. # 对加载的图像作归一化处理, 并裁剪为[224x224x3]大小的图像(因为这图片像素不一致直接统一)
    25. transforms.CenterCrop(224),
    26. # 将图片转成tensor
    27. transforms.ToTensor(),
    28. # 正则化,模型出现过拟合现象时,降低模型复杂度
    29. transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
    30. ])
    31. # 图片路径(训练图片和测试图片的)
    32. base_dir_train = 'G:/enpei_Project_Code/22_Resnet50_bus/1.data/datasets/train'
    33. base_dir_val = 'G:/enpei_Project_Code/22_Resnet50_bus/1.data/datasets/val'
    34. # 4. 加载数据集
    35. train_dataset = datasets.ImageFolder(root=base_dir_train, transform=pipeline)
    36. print("train_dataset=" + repr(train_dataset[1][0].size()))
    37. print("train_dataset.class_to_idx=" + repr(train_dataset.class_to_idx))
    38. # 创建训练集的可迭代对象,一个batch_size地读取数据,shuffle设为True表示随机打乱顺序读取
    39. train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True)
    40. # 测试集
    41. val_dataset = datasets.ImageFolder(root=base_dir_val, transform=pipeline)
    42. print(val_dataset)
    43. print("val_dataset=" + repr(val_dataset[1][0].size()))
    44. print("val_dataset.class_to_idx=" + repr(val_dataset.class_to_idx))
    45. # 创建测试集的可迭代对象,一个batch_size地读取数据
    46. val_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=True)
    47. # 获得一批测试集的数据
    48. images, labels = next(iter(val_loader))
    49. print(images.shape)
    50. print(labels.shape)
    51. # 损失函数,交叉熵损失函数
    52. criterion = nn.CrossEntropyLoss()
    53. # 使用预训练模型
    54. resnet_model = torchvision.models.resnet50(pretrained=True)
    55. num_ftrs = resnet_model.fc.in_features
    56. resnet_model.fc = nn.Linear(num_ftrs, 10)
    57. resnet_model.to(DEVICE)
    58. # 选择简单暴力的Adam优化器,学习率调低
    59. optimizer = optim.Adam(resnet_model.parameters(), lr=modellr)
    60. #optimizer = optim.SGD(net.parameters(), lr = 0.01)
    61. train_loss_list = []
    62. train_accuracy_list = []
    63. test_loss_list = []
    64. test_accuracy_list = []
    65. train_iteration_list = []
    66. test_iteration_list = []
    67. best_val_acc = 0
    68. # 定义训练方法
    69. def train(model, device, train_loader, optimizer, epoch):
    70. iteration = 0
    71. train_correct = 0.0
    72. model.train()
    73. sum_loss = 0.0
    74. total_num = len(train_loader.dataset)
    75. print(total_num, len(train_loader))
    76. for batch_idx, (data, target) in enumerate(train_loader):
    77. # 获取数据与标签
    78. data, target = Variable(data).to(device), Variable(target).to(device)
    79. # 梯度清零
    80. optimizer.zero_grad()
    81. # 计算损失
    82. output = model(data)
    83. loss = criterion(output, target)
    84. #反向传播
    85. loss.backward()
    86. #更新参数
    87. optimizer.step()
    88. print_loss = loss.data.item()
    89. sum_loss += print_loss
    90. _, train_predict = torch.max(output.data, 1)
    91. if torch.cuda.is_available():
    92. train_correct += (train_predict.cuda() == target.cuda()).sum()
    93. else:
    94. train_correct += (train_predict == target).sum()
    95. accuracy = (train_correct / total_num) * 100
    96. print("Epoch: %d , Batch: %3d , Loss : %.8f,train_correct:%d , train_total:%d , accuracy:%.6f" % (
    97. epoch + 1, batch_idx + 1, loss.item(), train_correct, total_num, accuracy))
    98. # 存在集合画图
    99. if (epoch + 1) == EPOCHS: # 只画出最后一个epoch时候的准确度变化曲线
    100. iteration += 1
    101. train_loss_list.append(loss.item())
    102. train_iteration_list.append(iteration)
    103. train_accuracy_list.append(accuracy)
    104. # 定义验证方法
    105. def val(model, device, val_loader, epoch):
    106. print("=====================预测开始=================================")
    107. iteration = 0
    108. model.eval()
    109. test_loss = 0.0
    110. correct = 0.0
    111. total_num = len(val_loader.dataset)
    112. print(total_num, len(val_loader))
    113. with torch.no_grad():
    114. for data, target in val_loader:
    115. data, target = Variable(data).to(device), Variable(target).to(device)
    116. output = model(data)
    117. loss = criterion(output, target)
    118. _, pred = torch.max(output.data, 1)
    119. if torch.cuda.is_available():
    120. correct += torch.sum(pred.cuda() == target.cuda())
    121. else:
    122. correct += torch.sum(pred == target)
    123. print_loss = loss.data.item()
    124. test_loss += print_loss
    125. acc = correct / total_num * 100
    126. avg_loss = test_loss / len(val_loader)
    127. """
    128. 因为调用这个方法的时候就是每次结束训练一次之后调用
    129. """
    130. # iteration += 1
    131. # 存入集合准备画图
    132. test_loss_list.append(avg_loss)
    133. test_accuracy_list.append(acc)
    134. test_iteration_list.append(epoch)
    135. print('\nVal set: Average loss: {:.4f}, Accuracy: {}/{} ({:.6f}%)\n'.format(
    136. avg_loss, correct, len(val_loader.dataset), acc))
    137. global best_val_acc
    138. if acc > best_val_acc:
    139. best_val_acc = acc
    140. print("Best Accuracy:{:.6f}%".format(best_val_acc))
    141. torch.save(resnet_model.state_dict(), 'best-{:.6f}.model.pth'.format(best_val_acc)) # 保存模型
    142. # 训练
    143. for epoch in range(EPOCHS):
    144. train(resnet_model, DEVICE, train_loader, optimizer, epoch)
    145. val(resnet_model, DEVICE, val_loader, epoch)
    146. #torch.save(resnet_model, 'model.pth') # 保存模型
    147. # 可视化测试机的loss和accuracy
    148. plt.figure(1)
    149. plt.plot(test_iteration_list, test_loss_list)
    150. plt.title("ResNet50 test loss")
    151. plt.ylabel("loss")
    152. plt.xlabel("Number of test iteration")
    153. plt.show()
    154. plt.figure(2)
    155. plt.plot(test_iteration_list, test_accuracy_list)
    156. plt.title("ResNet50 test accuracy")
    157. plt.xlabel("Number of test iteration")
    158. plt.ylabel("accuracy")
    159. plt.show()
    160. # 可视化训练集loss和accuracy
    161. plt.figure(3)
    162. plt.plot(train_iteration_list, train_loss_list)
    163. plt.title("ResNet50 train loss")
    164. plt.xlabel("Number of train iteration")
    165. plt.ylabel("accuracy")
    166. plt.show()
    167. plt.figure(4)
    168. plt.plot(train_iteration_list, train_accuracy_list)
    169. plt.title("ResNet50 train accuracy")
    170. plt.xlabel("Number of train iteration")
    171. plt.ylabel("accuracy")
    172. plt.show()

    代码需要注意的是数据集路径,用的是绝对路径,自行修改。

    代码训练的epoch是15,等待一段时间吧!

    五、测试模型

    测试模型脚本predict.py

    1. import os
    2. from PIL import Image
    3. import cv2
    4. import torch
    5. import torch.nn as nn
    6. from torch.autograd.variable import Variable
    7. import torchvision
    8. from torchvision import transforms
    9. # 0-SUV, 1-BUS, 2-family sedan, 3-fire engine, 4-heavy truck,
    10. # 5-jeep, 6-mini bus, 7-racing car, 8-taxi, 9-truck
    11. def predict_single_image():
    12. MODEL_SAVE_FILE = 'best-82.000000.model.pth'
    13. device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
    14. model = torchvision.models.resnet50()
    15. num_ftrs = model.fc.in_features
    16. model.fc = nn.Linear(num_ftrs, 10)
    17. model.to(device)
    18. model.load_state_dict(torch.load(MODEL_SAVE_FILE,map_location='cpu'))
    19. model = torch.nn.DataParallel(model,device_ids=[0])
    20. model.eval()
    21. img = cv2.imread("test.jpg")
    22. img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    23. image = Image.fromarray(img)
    24. pipeline = transforms.Compose([
    25. # 分辨率重置为256
    26. transforms.Resize(256),
    27. # 对加载的图像作归一化处理, 并裁剪为[224x224x3]大小的图像(因为这图片像素不一致直接统一)
    28. transforms.CenterCrop(224),
    29. # 将图片转成tensor
    30. transforms.ToTensor(),
    31. # 正则化,模型出现过拟合现象时,降低模型复杂度
    32. transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
    33. ])
    34. image = pipeline(image)
    35. image = image.unsqueeze(0)
    36. print(image.shape)
    37. input_var = Variable(image).float().to(device)
    38. output = model(input_var)
    39. print("output:", output)
    40. print("output.shape:", output.shape)
    41. soft_output = torch.softmax(output, dim=-1)
    42. print("soft_output:", soft_output)
    43. percent, predicted = torch.max(soft_output.data, 1)
    44. print("percent:", percent)
    45. print("predicted:", predicted)
    46. '''
    47. USE_GPU = torch.cuda.is_available()
    48. if USE_GPU:
    49. inputs = inputs.cuda()
    50. if not os.path.exists(MODEL_SAVE_FILE):
    51. print('can not find model save file.')
    52. exit()
    53. else:
    54. if USE_GPU:
    55. model.load_state_dict(torch.load(MODEL_SAVE_FILE))
    56. else:
    57. model.load_state_dict(torch.load(MODEL_SAVE_FILE, map_location=lambda storage, loc: storage))
    58. outputs = model(inputs)
    59. _, prediction_tensor = torch.max(outputs.data, 1)
    60. if USE_GPU:
    61. prediction = prediction_tensor.cpu().numpy()[0][0]
    62. print('predict: ', prediction)
    63. print('this is {}'.format(classes_name[prediction]))
    64. else:
    65. prediction = prediction_tensor.numpy()[0][0]
    66. print('predict: ', prediction)
    67. print('this is {}'.format(classes_name[prediction]))
    68. '''
    69. predict_single_image()

    运行

    python predict.py

    六、模型转换

    1、转成onnx模型

    pth_to_onnx.py

    1. import torch
    2. import torch.nn as nn
    3. import torchvision
    4. from torch.autograd.variable import Variable
    5. MODEL_SAVE_FILE = 'best-82.000000.model.pth'
    6. device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
    7. model = torchvision.models.resnet50()
    8. num_ftrs = model.fc.in_features
    9. model.fc = nn.Linear(num_ftrs, 10)
    10. model.to(device)
    11. model.load_state_dict(torch.load(MODEL_SAVE_FILE,map_location='cpu'))
    12. batch_size = 1 #批处理大小
    13. # #set the model to inference mode
    14. model.eval()
    15. d_input = Variable(torch.randn(1, 3, 224, 224))
    16. export_onnx_file = "10class_ResNet50.onnx" # 目的ONNX文件名
    17. torch.onnx.export(model, d_input, export_onnx_file, opset_version=12,verbose=True)

    这里需要注意的 是opset_version算子,rk3568用12

    python pth_to_onnx.py

    onnx模型是我需要的,打算部署到rk3568,需要把onnx模型转成rknn模型,后续测试

    2、转成pt模型

    pth_to_pt.py

    1. import torch
    2. import torch.nn as nn
    3. import torchvision
    4. MODEL_SAVE_FILE = 'best-82.000000.model.pth'
    5. device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
    6. model = torchvision.models.resnet50()
    7. num_ftrs = model.fc.in_features
    8. model.fc = nn.Linear(num_ftrs, 10)
    9. model.to(device)
    10. model.load_state_dict(torch.load(MODEL_SAVE_FILE,map_location='cpu'))
    11. model.eval()
    12. example = torch.rand(1,3,224,224).to(device)
    13. traced_script_module = torch.jit.trace(model, example)
    14. traced_script_module.save('./10class_ResNet50.pt')

    运行转换:

    python pth_to_pt.py

    如有侵权,或需要完整代码,请及时联系博主。

  • 相关阅读:
    【Qt】Unicode编码作用 ,以及在Qt中的理解
    java毕业设计民宿管理平台mybatis+源码+调试部署+系统+数据库+lw
    第30章 市场营销
    R语言实战应用案例:论文篇(二)-特殊盒须图绘制
    spring框架漏洞整理(Spring Data漏洞)
    Redis整理
    SpringBoot集成Mybatis-Plus
    TCP协议
    日更【系统架构设计师知识总结2】指令系统(结合真题)
    使用Python进行钻石价格分析
  • 原文地址:https://blog.csdn.net/weixin_38807927/article/details/133915602