当前位置:   article > 正文

【PyTorch】图像二分类项目-部署

【PyTorch】图像二分类项目-部署

【PyTorch】图像二分类项目

【PyTorch】图像二分类项目-部署

在独立于训练脚本的新脚本中部署用于推理的模型,需要构造一个模型类的对象,并将权重加载到模型中。操作流程为:定义模型--加载权重--在验证和测试数据集上部署模型。

  1. import torch.nn as nn
  2. import numpy as np
  3. # 设置随机种子
  4. np.random.seed(0)
  1. import torch.nn as nn
  2. import torch.nn.functional as F
  3. # 定义一个函数,用于计算卷积层的输出形状
  4. def findConv2dOutShape(H_in,W_in,conv,pool=2):
  5. # 获取卷积核的大小
  6. kernel_size=conv.kernel_size
  7. # 获取卷积的步长
  8. stride=conv.stride
  9. # 获取卷积的填充
  10. padding=conv.padding
  11. # 获取卷积的扩张
  12. dilation=conv.dilation
  13. # 计算卷积后的高度
  14. H_out=np.floor((H_in+2*padding[0]-dilation[0]*(kernel_size[0]-1)-1)/stride[0]+1)
  15. # 计算卷积后的宽度
  16. W_out=np.floor((W_in+2*padding[1]-dilation[1]*(kernel_size[1]-1)-1)/stride[1]+1)
  17. # 如果pool不为空
  18. if pool:
  19. # 将H_out除以pool
  20. H_out/=pool
  21. W_out/=pool
  22. # 返回H_out和W_out的整数形式
  23. return int(H_out),int(W_out)
  24. class Net(nn.Module):
  25. def __init__(self, params):
  26. super(Net, self).__init__()
  27. # 获取输入形状
  28. C_in,H_in,W_in=params["input_shape"]
  29. # 获取初始滤波器数量
  30. init_f=params["initial_filters"]
  31. # 获取第一个全连接层神经元数量
  32. num_fc1=params["num_fc1"]
  33. # 获取类别数量
  34. num_classes=params["num_classes"]
  35. # 获取模型的dropout率,是01间的浮点数
  36. # Dropout是一种正则化技术,随机关闭部分神经元(输出设为0),防止过拟合,提高泛化能力
  37. self.dropout_rate=params["dropout_rate"]
  38. # 定义第一个卷积层
  39. self.conv1 = nn.Conv2d(C_in, init_f, kernel_size=3)
  40. # 计算第一个卷积层的输出形状
  41. h,w=findConv2dOutShape(H_in,W_in,self.conv1)
  42. self.conv2 = nn.Conv2d(init_f, 2*init_f, kernel_size=3)
  43. h,w=findConv2dOutShape(h,w,self.conv2)
  44. self.conv3 = nn.Conv2d(2*init_f, 4*init_f, kernel_size=3)
  45. h,w=findConv2dOutShape(h,w,self.conv3)
  46. self.conv4 = nn.Conv2d(4*init_f, 8*init_f, kernel_size=3)
  47. h,w=findConv2dOutShape(h,w,self.conv4)
  48. # 计算全连接层的输入形状
  49. self.num_flatten=h*w*8*init_f
  50. # 定义第一个全连接层
  51. self.fc1 = nn.Linear(self.num_flatten, num_fc1)
  52. self.fc2 = nn.Linear(num_fc1, num_classes)
  53. # 定义前向传播函数,接收输入x
  54. def forward(self, x):
  55. # 第一个卷积层
  56. x = F.relu(self.conv1(x))
  57. # 第一个池化层
  58. x = F.max_pool2d(x, 2, 2)
  59. x = F.relu(self.conv2(x))
  60. x = F.max_pool2d(x, 2, 2)
  61. x = F.relu(self.conv3(x))
  62. x = F.max_pool2d(x, 2, 2)
  63. x = F.relu(self.conv4(x))
  64. x = F.max_pool2d(x, 2, 2)
  65. # 将卷积层的输出展平
  66. x = x.view(-1, self.num_flatten)
  67. # 第一个全连接层
  68. x = F.relu(self.fc1(x))
  69. # Dropout层
  70. x=F.dropout(x, self.dropout_rate)
  71. # 第二个全连接层
  72. x = self.fc2(x)
  73. # 返回输入x应用对数软最大变换后的输出
  74. # log-softmax对数软最大值函数,常用于计算交叉熵损失函数(cross-entropy loss),因为交叉熵损失函数需要计算概率的对数。
  75. # dim参数指定了在哪个维度上应用log-softmax。例如,如果dim=1,则对每一行应用log-softmax。
  76. return F.log_softmax(x, dim=1)
  77. # 定义模型参数
  78. params_model={
  79. # 输入形状
  80. "input_shape": (3,96,96),
  81. # 初始过滤器数量
  82. "initial_filters": 8,
  83. # 全连接层1的神经元数量
  84. "num_fc1": 100,
  85. # Dropout率
  86. "dropout_rate": 0.25,
  87. # 类别数量
  88. "num_classes": 2,
  89. }
  90. # 创建一个CNN模型,参数为params_model
  91. cnn_model = Net(params_model)
  92. import torch
  93. # 权重文件路径
  94. path2weights="./models/weights.pt"
  95. # 加载权重文件
  96. cnn_model.load_state_dict(torch.load(path2weights))
  97. # 进入评估模式
  98. cnn_model.eval()

  1. # 移动模型至cuda设备
  2. if torch.cuda.is_available():
  3. device = torch.device("cuda")
  4. cnn_model=cnn_model.to(device)
  1. import time
  2. # 定义一个函数,用于部署模型
  3. def deploy_model(model,dataset,device, num_classes=2,sanity_check=False):
  4. # num_classes:类别数,默认为2
  5. # sanity_check:是否进行完整性检查,默认为False
  6. pass
  7. # 获取数据集长度
  8. len_data=len(dataset)
  9. # 初始化输出张量
  10. y_out=torch.zeros(len_data,num_classes)
  11. # 初始化真实标签张量
  12. y_gt=np.zeros((len_data),dtype="uint8")
  13. # 将模型移动到指定设备
  14. model=model.to(device)
  15. # 存储每次推理的时间
  16. elapsed_times=[]
  17. # 在不计算梯度的情况下执行以下代码
  18. with torch.no_grad():
  19. for i in range(len_data):
  20. # 获取数据集中的一个样本
  21. x,y=dataset[i]
  22. # 将真实标签存储到张量中
  23. y_gt[i]=y
  24. # 记录开始时间
  25. start=time.time()
  26. # 进行推理
  27. y_out[i]=model(x.unsqueeze(0).to(device))
  28. # 计算推理时间
  29. elapsed=time.time()-start
  30. # 将推理时间存储到列表中
  31. elapsed_times.append(elapsed)
  32. # 如果进行完整性检查,则只进行一次推理
  33. if sanity_check is True:
  34. break
  35. # 计算平均推理时间
  36. inference_time=np.mean(elapsed_times)*1000
  37. # 打印平均推理时间
  38. print("average inference time per image on %s: %.2f ms " %(device,inference_time))
  39. # 返回推理结果和真实标签
  40. return y_out.numpy(),y_gt
  41. import torch
  42. from PIL import Image
  43. from torch.utils.data import Dataset
  44. import pandas as pd
  45. import torchvision.transforms as transforms
  46. import os
  47. # 设置随机种子,使得每次运行代码时生成的随机数相同
  48. torch.manual_seed(0)
  49. class histoCancerDataset(Dataset):
  50. def __init__(self, data_dir, transform,data_type="train"):
  51. # 获取数据目录
  52. path2data=os.path.join(data_dir,data_type)
  53. # 获取数据目录下的所有文件名
  54. self.filenames = os.listdir(path2data)
  55. # 获取数据目录下的所有文件的完整路径
  56. self.full_filenames = [os.path.join(path2data, f) for f in self.filenames]
  57. # 获取标签文件名
  58. csv_filename=data_type+"_labels.csv"
  59. # 获取标签文件的完整路径
  60. path2csvLabels=os.path.join(data_dir,csv_filename)
  61. # 读取标签文件
  62. labels_df=pd.read_csv(path2csvLabels)
  63. # 将标签文件的索引设置为文件名
  64. labels_df.set_index("id", inplace=True)
  65. # 获取每个文件的标签
  66. self.labels = [labels_df.loc[filename[:-4]].values[0] for filename in self.filenames]
  67. # 获取数据转换函数
  68. self.transform = transform
  69. def __len__(self):
  70. # 返回数据集的长度
  71. return len(self.full_filenames)
  72. def __getitem__(self, idx):
  73. # 根据索引获取图像
  74. image = Image.open(self.full_filenames[idx])
  75. # 对图像进行转换
  76. image = self.transform(image)
  77. # 返回图像和标签
  78. return image, self.labels[idx]
  79. import torchvision.transforms as transforms
  80. # 创建一个数据转换器,将数据转换为张量
  81. data_transformer = transforms.Compose([transforms.ToTensor()])
  82. data_dir = "./data/"
  83. # 传入数据目录、数据转换器和数据集类型
  84. histo_dataset = histoCancerDataset(data_dir, data_transformer, "train")
  85. # 打印数据集的长度
  86. print(len(histo_dataset))

  1. from torch.utils.data import random_split
  2. # 获取数据集的长度
  3. len_histo=len(histo_dataset)
  4. # 训练集取数据集的80%
  5. len_train=int(0.8*len_histo)
  6. # 验证集取数据集的20%
  7. len_val=len_histo-len_train
  8. # 将数据集随机分割为训练集和验证集
  9. train_ds,val_ds=random_split(histo_dataset,[len_train,len_val])
  10. # 打印训练集和验证集的长度
  11. print("train dataset length:", len(train_ds))
  12. print("validation dataset length:", len(val_ds))

 

  1. # 部署模型
  2. y_out,y_gt=deploy_model(cnn_model,val_ds,device=device,sanity_check=False)
  3. # 打印输出和真实值的形状
  4. print(y_out.shape,y_gt.shape)

使用预测输出计算模型在验证数据集上的精度

  1. from sklearn.metrics import accuracy_score
  2. # 获取预测
  3. y_pred = np.argmax(y_out,axis=1)
  4. print(y_pred.shape,y_gt.shape)
  5. # 计算精度
  6. acc=accuracy_score(y_pred,y_gt)
  7. print("accuracy: %.2f" %acc)

 

  1. # 部署在CPU上
  2. device_cpu = torch.device("cpu")
  3. y_out,y_gt=deploy_model(cnn_model,val_ds,device=device_cpu,sanity_check=False)
  4. print(y_out.shape,y_gt.shape)

复制data文件夹中的sample_submission.csv文件并命名为test_labels.csv

  1. path2csv="./data/test_labels.csv"
  2. # 读取csv文件,并存储到DataFrame中
  3. labels_df=pd.read_csv(path2csv)
  4. # 显示DataFrame的前几行
  5. labels_df.head()

  1. data_dir = "./data/"
  2. # 创建测试数据集
  3. histo_test = histoCancerDataset(data_dir, data_transformer,data_type="test")
  4. # 打印测试数据集的长度
  5. print(len(histo_test))

 

  1. # 用测试数据集部署
  2. y_test_out,_=deploy_model(cnn_model,histo_test, device, sanity_check=False)
  3. # 使用np.argmax函数对y_test_out进行操作,得到y_test_pred
  4. y_test_pred=np.argmax(y_test_out,axis=1)
  5. # 打印y_test_pred的形状
  6. print(y_test_pred.shape)

  1. from torchvision import utils
  2. import numpy as np
  3. import matplotlib.pyplot as plt
  4. %matplotlib inline
  5. np.random.seed(0)
  6. # 定义一个函数,用于显示图像和标签
  7. def show(img,y,color=True):
  8. # 将图像转换为numpy数组
  9. npimg = img.numpy()
  10. # 将图像的维度从(C,H,W)转换为(H,W,C)
  11. npimg_tr=np.transpose(npimg, (1,2,0))
  12. # 如果color为False,则将图像转换为灰度图像
  13. if color==False:
  14. npimg_tr=npimg_tr[:,:,0]
  15. plt.imshow(npimg_tr,interpolation='nearest',cmap="gray")
  16. else:
  17. # 否则,直接显示图像
  18. plt.imshow(npimg_tr,interpolation='nearest')
  19. # 显示图像的标签
  20. plt.title("label: "+str(y))
  21. # 定义一个网格大小
  22. grid_size=4
  23. # 随机选择grid_size个图像的索引
  24. rnd_inds=np.random.randint(0,len(histo_test),grid_size)
  25. print("image indices:",rnd_inds)
  26. # 从histo_test中获取grid_size个图像
  27. x_grid_test=[histo_test[i][0] for i in range(grid_size)]
  28. # 从y_test_pred中获取grid_size个标签
  29. y_grid_test=[y_test_pred[i] for i in range(grid_size)]
  30. # 将grid_size个图像组合成一个网格
  31. x_grid_test=utils.make_grid(x_grid_test, nrow=4, padding=2)
  32. print(x_grid_test.shape)
  33. # 设置图像的大小
  34. plt.rcParams['figure.figsize'] = (10.0, 5)
  35. # 显示图像和标签
  36. show(x_grid_test,y_grid_test)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码探险家/article/detail/860488
推荐阅读
相关标签
  

闽ICP备14008679号