• 第64步 深度学习图像识别:多分类建模误判病例分析(Pytorch)


    基于WIN10的64位系统演示

    一、写在前面

    上期我们基于TensorFlow环境介绍了多分类建模的误判病例分析。

    本期以健康组、肺结核组、COVID-19组、细菌性(病毒性)肺炎组为数据集,基于Pytorch环境,构建SqueezeNet多分类模型,分析误判病例,因为它建模速度快。

    同样,基于GPT-4辅助编程。

    二、误判病例分析实战

    使用胸片的数据集:肺结核病人和健康人的胸片的识别。其中,健康人900张,肺结核病人700张,COVID-19病人549张、细菌性(病毒性)肺炎组900张,分别存入单独的文件夹中。

    直接分享代码:

    1. ######################################导入包###################################
    2. import copy
    3. import torch
    4. import torchvision
    5. import torchvision.transforms as transforms
    6. from torchvision import models
    7. from torch.utils.data import DataLoader
    8. from torch import optim, nn
    9. from torch.optim import lr_scheduler
    10. import os
    11. import matplotlib.pyplot as plt
    12. import warnings
    13. import numpy as np
    14. warnings.filterwarnings("ignore")
    15. plt.rcParams['font.sans-serif'] = ['SimHei']
    16. plt.rcParams['axes.unicode_minus'] = False
    17. # 设置GPU
    18. device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    19. ################################导入数据集#####################################
    20. from torchvision import datasets, transforms
    21. from torch.nn.functional import softmax
    22. from PIL import Image
    23. import pandas as pd
    24. import torch.nn as nn
    25. import timm
    26. from torch.optim import lr_scheduler
    27. # 自定义的数据集类
    28. class ImageFolderWithPaths(datasets.ImageFolder):
    29. def __getitem__(self, index):
    30. original_tuple = super(ImageFolderWithPaths, self).__getitem__(index)
    31. path = self.imgs[index][0]
    32. tuple_with_path = (original_tuple + (path,))
    33. return tuple_with_path
    34. # 数据集路径
    35. data_dir = "./MTB-1"
    36. # 图像的大小
    37. img_height = 256
    38. img_width = 256
    39. # 数据预处理
    40. data_transforms = {
    41. 'train': transforms.Compose([
    42. transforms.RandomResizedCrop(img_height),
    43. transforms.RandomHorizontalFlip(),
    44. transforms.RandomVerticalFlip(),
    45. transforms.RandomRotation(0.2),
    46. transforms.ToTensor(),
    47. transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    48. ]),
    49. 'val': transforms.Compose([
    50. transforms.Resize((img_height, img_width)),
    51. transforms.ToTensor(),
    52. transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    53. ]),
    54. }
    55. # 加载数据集
    56. full_dataset = ImageFolderWithPaths(data_dir, transform=data_transforms['train'])
    57. # 获取数据集的大小
    58. full_size = len(full_dataset)
    59. train_size = int(0.8 * full_size) # 假设训练集占70%
    60. val_size = full_size - train_size # 验证集的大小
    61. # 随机分割数据集
    62. torch.manual_seed(0) # 设置随机种子以确保结果可重复
    63. train_dataset, val_dataset = torch.utils.data.random_split(full_dataset, [train_size, val_size])
    64. # 应用数据增强到训练集和验证集
    65. train_dataset.dataset.transform = data_transforms['train']
    66. val_dataset.dataset.transform = data_transforms['val']
    67. # 创建数据加载器
    68. batch_size = 8
    69. train_dataloader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=0)
    70. val_dataloader = torch.utils.data.DataLoader(val_dataset, batch_size=batch_size, shuffle=True, num_workers=0)
    71. dataloaders = {'train': train_dataloader, 'val': val_dataloader}
    72. dataset_sizes = {'train': len(train_dataset), 'val': len(val_dataset)}
    73. class_names = full_dataset.classes
    74. # 获取数据集的类别
    75. class_names = full_dataset.classes
    76. # 保存预测结果的列表
    77. results = []
    78. ###############################定义SqueezeNet模型################################
    79. # 定义SqueezeNet模型
    80. model = models.squeezenet1_1(pretrained=True) # 这里以SqueezeNet 1.1版本为例
    81. num_ftrs = model.classifier[1].in_channels
    82. # 根据分类任务修改最后一层
    83. # 这里我们改变模型的输出层为4,因为我们做的是四分类
    84. model.classifier[1] = nn.Conv2d(num_ftrs, 4, kernel_size=(1,1))
    85. # 修改模型最后的输出层为我们需要的类别数
    86. model.num_classes = 4
    87. model = model.to(device)
    88. # 打印模型摘要
    89. print(model)
    90. #############################编译模型#########################################
    91. # 定义损失函数
    92. criterion = nn.CrossEntropyLoss()
    93. # 定义优化器
    94. optimizer = torch.optim.Adam(model.parameters())
    95. # 定义学习率调度器
    96. exp_lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)
    97. # 开始训练模型
    98. num_epochs = 20
    99. # 初始化记录器
    100. train_loss_history = []
    101. train_acc_history = []
    102. val_loss_history = []
    103. val_acc_history = []
    104. for epoch in range(num_epochs):
    105. print('Epoch {}/{}'.format(epoch, num_epochs - 1))
    106. print('-' * 10)
    107. # 每个epoch都有一个训练和验证阶段
    108. for phase in ['train', 'val']:
    109. if phase == 'train':
    110. model.train() # 设置模型为训练模式
    111. else:
    112. model.eval() # 设置模型为评估模式
    113. running_loss = 0.0
    114. running_corrects = 0
    115. # 遍历数据
    116. for inputs, labels, paths in dataloaders[phase]:
    117. inputs = inputs.to(device)
    118. labels = labels.to(device)
    119. # 零参数梯度
    120. optimizer.zero_grad()
    121. # 前向
    122. with torch.set_grad_enabled(phase == 'train'):
    123. outputs = model(inputs)
    124. _, preds = torch.max(outputs, 1)
    125. loss = criterion(outputs, labels)
    126. # 只在训练模式下进行反向和优化
    127. if phase == 'train':
    128. loss.backward()
    129. optimizer.step()
    130. # 统计
    131. running_loss += loss.item() * inputs.size(0)
    132. running_corrects += torch.sum(preds == labels.data)
    133. epoch_loss = running_loss / dataset_sizes[phase]
    134. epoch_acc = (running_corrects.double() / dataset_sizes[phase]).item()
    135. # 记录每个epoch的loss和accuracy
    136. if phase == 'train':
    137. train_loss_history.append(epoch_loss)
    138. train_acc_history.append(epoch_acc)
    139. else:
    140. val_loss_history.append(epoch_loss)
    141. val_acc_history.append(epoch_acc)
    142. print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))
    143. print()
    144. # 保存模型
    145. torch.save(model.state_dict(), 'SqueezeNet_model-m-s.pth')
    146. # 加载最佳模型权重
    147. #model.load_state_dict(best_model_wts)
    148. #torch.save(model, 'shufflenet_best_model.pth')
    149. #print("The trained model has been saved.")
    150. ###########################误判病例分析#################################
    151. import os
    152. import pandas as pd
    153. from collections import defaultdict
    154. # 判定组别的字典
    155. group_dict = {
    156. ("COVID-19", "Normal"): "B",
    157. ("COVID-19", "Pneumonia"): "C",
    158. ("COVID-19", "Tuberculosis"): "D",
    159. ("Normal", "COVID-19"): "E",
    160. ("Normal", "Pneumonia"): "F",
    161. ("Normal", "Tuberculosis"): "G",
    162. ("Pneumonia", "COVID-19"): "H",
    163. ("Pneumonia", "Normal"): "I",
    164. ("Pneumonia", "Tuberculosis"): "J",
    165. ("Tuberculosis", "COVID-19"): "K",
    166. ("Tuberculosis", "Normal"): "L",
    167. ("Tuberculosis", "Pneumonia"): "M",
    168. }
    169. # 创建一个字典来保存所有的图片信息
    170. image_predictions = {}
    171. # 循环遍历所有数据集(训练集和验证集)
    172. for phase in ['train', 'val']:
    173. # 设置模型的状态
    174. model.eval()
    175. # 遍历数据
    176. for inputs, labels, paths in dataloaders[phase]:
    177. inputs = inputs.to(device)
    178. labels = labels.to(device)
    179. # 计算模型的输出
    180. with torch.no_grad():
    181. outputs = model(inputs)
    182. _, preds = torch.max(outputs, 1)
    183. # 循环遍历每一个批次的结果
    184. for path, pred in zip(paths, preds):
    185. # 提取图片的类别
    186. actual_class = os.path.split(os.path.dirname(path))[-1]
    187. # 提取图片的名称
    188. image_name = os.path.basename(path)
    189. # 获取预测的类别
    190. predicted_class = class_names[pred]
    191. # 判断预测的分组类型
    192. if actual_class == predicted_class:
    193. group_type = 'A'
    194. elif (actual_class, predicted_class) in group_dict:
    195. group_type = group_dict[(actual_class, predicted_class)]
    196. else:
    197. group_type = 'Other' # 如果没有匹配的条件,可以归类为其他
    198. # 保存到字典中
    199. image_predictions[image_name] = [phase, actual_class, predicted_class, group_type]
    200. # 将字典转换为DataFrame
    201. df = pd.DataFrame.from_dict(image_predictions, orient='index', columns=['Dataset Type', 'Actual Class', 'Predicted Class', 'Group Type'])
    202. # 保存到CSV文件中
    203. df.to_csv('result-m-s.csv')

    四、改写过程

    先说策略:首先,先把二分类的误判病例分析代码改成四分类的;其次,用咒语让GPT-4帮我们续写代码已达到误判病例分析。

    提供咒语如下:

    ①改写{代码1},改变成4分类的建模。代码1为:{XXX};

    在{代码1}的基础上改写代码,达到下面要求:

    (1)首先,提取出所有图片的“原始图片的名称”、“属于训练集还是验证集”、“预测为分组类型”;文件的路劲格式为:例如,“MTB-1\Normal\XXX.png”属于Normal,“MTB-1\COVID-19\XXX.jpg”属于COVID-19,“MTB-1\Pneumonia\XXX.jpeg”属于Pneumonia,“MTB-1\Tuberculosis\XXX.png”属于Tuberculosis;

    (2)其次,根据样本预测结果,把样本分为以下若干组:(a)预测正确的图片,全部判定为A组;(b)本来就是COVID-19的图片,预测为Normal,判定为B组;(c)本来就是COVID-19的图片,预测为Pneumonia,判定为C组;(d)本来就是COVID-19的图片,预测为Tuberculosis,判定为D组;(e)本来就是Normal的图片,预测为COVID-19,判定为E组;(f)本来就是Normal的图片,预测为Pneumonia,判定为F组;(g)本来就是Normal的图片,预测为Tuberculosis,判定为G组;(h)本来就是Pneumonia的图片,预测为COVID-19,判定为H组;(i)本来就是Pneumonia的图片,预测为Normal,判定为I组;(j)本来就是Pneumonia的图片,预测为Tuberculosis,判定为J组;(k)本来就是Tuberculosis的图片,预测为COVID-19,判定为H组;(l)本来就是Tuberculosis的图片,预测为Normal,判定为I组;(m)本来就是Tuberculosis的图片,预测为Pneumonia,判定为J组;

    (3)居于以上计算的结果,生成一个名为result-m.csv表格文件。列名分别为:“原始图片的名称”、“属于训练集还是验证集”、“预测为分组类型”、“判定的组别”。其中,“原始图片的名称”为所有图片的图片名称;“属于训练集还是验证集”为这个图片属于训练集还是验证集;“预测为分组类型”为模型预测该样本是哪一个分组;“判定的组别”为根据步骤(2)判定的组别,从A到J一共十组选择一个。

    (4)需要把所有的图片都进行上面操作,注意是所有图片,而不只是一个批次的图片。

    代码1为:{XXX}

    ③还需要根据报错做一些调整即可,自行调整。

    最后,看看结果:

    模型只运行了2次,所以效果很差哈,全部是预测成了COVID-19。

    四、数据

    链接:https://pan.baidu.com/s/1rqu15KAUxjNBaWYfEmPwgQ?pwd=xfyn

    提取码:xfyn

    五、结语

    深度学习图像分类的教程到此结束,洋洋洒洒29篇,涉及到的算法和技巧也够发一篇SCI了。当然,图像识别还有图像分割和目标识别两块内容,就放到最后再说了。下一趴,我们来介绍时间序列建模!!!

  • 相关阅读:
    Unable to connect to the server: x509: certificate is valid for问题解决
    Demo 题记
    设计模式--代理模式
    【ubuntu】修改系统及硬件时间
    JS对闭包的理解
    Linux网络编程(高并发服务器)
    设计问卷调查有哪些技巧?
    GraalVM入门教程
    java高级:注解
    【计算机网络仿真实验-实验2.6】带交换机的RIP路由协议
  • 原文地址:https://blog.csdn.net/qq_30452897/article/details/132620208