服务器之家

服务器之家 > 正文

PyTorch 迁移学习实践(几分钟即可训练好自己的模型)

时间:2021-09-28 09:01     来源/作者:YXHPY

前言

如果你认为深度学习非常的吃GPU,或者说非常的耗时间,训练一个模型要非常久,但是你如果了解了迁移学习那你的模型可能只需要几分钟,而且准确率不比你自己训练的模型准确率低,本节我们将会介绍两种方法来实现迁移学习

迁移学习方法介绍

  • 微调网络的方法实现迁移学习,更改最后一层全连接,并且微调训练网络
  • 将模型看成特征提取器,如果一个模型的预训练模型非常的好,那完全就把前面的层看成特征提取器,冻结所有层并且更改最后一层,只训练最后一层,这样我们只训练了最后一层,训练会非常的快速

PyTorch 迁移学习实践(几分钟即可训练好自己的模型)

迁移基本步骤

  • 数据的准备
  • 选择数据增广的方式
  • 选择合适的模型
  • 更换最后一层全连接
  • 冻结层,开始训练
  • 选择预测结果最好的模型保存

需要导入的包

  1. import zipfile # 解压文件
  2. import torchvision
  3. from torchvision import datasets, transforms, models
  4. import torch
  5. from torch.utils.data import DataLoader, Dataset
  6. import os
  7. import cv2
  8. import numpy as np
  9. import matplotlib.pyplot as plt
  10. from PIL import Image
  11. import copy

数据准备

本次实验的数据到这里下载
首先按照上一章节讲的数据读取方法来准备数据

  1. # 解压数据到指定文件
  2. def unzip(filename, dst_dir):
  3. z = zipfile.ZipFile(filename)
  4. z.extractall(dst_dir)
  5. unzip('./data/hymenoptera_data.zip', './data/')
  6. # 实现自己的Dataset方法,主要实现两个方法__len__和__getitem__
  7. class MyDataset(Dataset):
  8. def __init__(self, dirname, transform=None):
  9. super(MyDataset, self).__init__()
  10. self.classes = os.listdir(dirname)
  11. self.images = []
  12. self.transform = transform
  13. for i, classes in enumerate(self.classes):
  14. classes_path = os.path.join(dirname, classes)
  15. for image_name in os.listdir(classes_path):
  16. self.images.append((os.path.join(classes_path, image_name), i))
  17. def __len__(self):
  18. return len(self.images)
  19. def __getitem__(self, idx):
  20. image_name, classes = self.images[idx]
  21. image = Image.open(image_name)
  22. if self.transform:
  23. image = self.transform(image)
  24. return image, classes
  25. def get_claesses(self):
  26. return self.classes
  27. # 分布实现训练和预测的transform
  28. train_transform = transforms.Compose([
  29. transforms.Grayscale(3),
  30. transforms.RandomResizedCrop(224), #随机裁剪一个area然后再resize
  31. transforms.RandomHorizontalFlip(), #随机水平翻转
  32. transforms.Resize(size=(256, 256)),
  33. transforms.ToTensor(),
  34. transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
  35. ])
  36. val_transform = transforms.Compose([
  37. transforms.Grayscale(3),
  38. transforms.Resize(size=(256, 256)),
  39. transforms.CenterCrop(224),
  40. transforms.ToTensor(),
  41. transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
  42. ])
  43. # 分别实现loader
  44. train_dataset = MyDataset('./data/hymenoptera_data/train/', train_transform)
  45. train_loader = DataLoader(train_dataset, shuffle=True, batch_size=32)
  46. val_dataset = MyDataset('./data/hymenoptera_data/val/', val_transform)
  47. val_loader = DataLoader(val_dataset, shuffle=True, batch_size=32)

选择预训练的模型

这里我们选择了resnet18在ImageNet 1000类上进行了预训练的

  1. model = models.resnet18(pretrained=True) # 使用预训练

使用model.buffers查看网络基本结构

  1. <bound method Module.buffers of ResNet(
  2. (conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)
  3. (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  4. (relu): ReLU(inplace=True)
  5. (maxpool): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)
  6. (layer1): Sequential(
  7. (0): BasicBlock(
  8. (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
  9. (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  10. (relu): ReLU(inplace=True)
  11. (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
  12. (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  13. )
  14. (1): BasicBlock(
  15. (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
  16. (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  17. (relu): ReLU(inplace=True)
  18. (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
  19. (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  20. )
  21. )
  22. (layer2): Sequential(
  23. (0): BasicBlock(
  24. (conv1): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
  25. (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  26. (relu): ReLU(inplace=True)
  27. (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
  28. (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  29. (downsample): Sequential(
  30. (0): Conv2d(64, 128, kernel_size=(1, 1), stride=(2, 2), bias=False)
  31. (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  32. )
  33. )
  34. (1): BasicBlock(
  35. (conv1): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
  36. (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  37. (relu): ReLU(inplace=True)
  38. (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
  39. (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  40. )
  41. )
  42. (layer3): Sequential(
  43. (0): BasicBlock(
  44. (conv1): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
  45. (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  46. (relu): ReLU(inplace=True)
  47. (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
  48. (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  49. (downsample): Sequential(
  50. (0): Conv2d(128, 256, kernel_size=(1, 1), stride=(2, 2), bias=False)
  51. (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  52. )
  53. )
  54. (1): BasicBlock(
  55. (conv1): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
  56. (bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  57. (relu): ReLU(inplace=True)
  58. (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
  59. (bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  60. )
  61. )
  62. (layer4): Sequential(
  63. (0): BasicBlock(
  64. (conv1): Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
  65. (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  66. (relu): ReLU(inplace=True)
  67. (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
  68. (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  69. (downsample): Sequential(
  70. (0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False)
  71. (1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  72. )
  73. )
  74. (1): BasicBlock(
  75. (conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
  76. (bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  77. (relu): ReLU(inplace=True)
  78. (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
  79. (bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  80. )
  81. )
  82. (avgpool): AdaptiveAvgPool2d(output_size=(1, 1))
  83. (fc): Linear(in_features=512, out_features=1000, bias=True)
  84. )>

我们现在需要做的就是将最后一层进行替换

  1. only_train_fc = True
  2. if only_train_fc:
  3. for param in model.parameters():
  4. param.requires_grad_(False)
  5. fc_in_features = model.fc.in_features
  6. model.fc = torch.nn.Linear(fc_in_features, 2, bias=True)

注释:only_train_fc如果我们设置为True那么就只训练最后的fc层
现在观察一下可导的参数有那些(在只训练最后一层的情况下)

  1. for i in model.parameters():
  2. if i.requires_grad:
  3. print(i)
  1. Parameter containing:
  2. tensor([[ 0.0342, -0.0336, 0.0279, ..., -0.0428, 0.0421, 0.0366],
  3. [-0.0162, 0.0286, -0.0379, ..., -0.0203, -0.0016, -0.0440]],
  4. requires_grad=True)
  5. Parameter containing:
  6. tensor([-0.0120, -0.0086], requires_grad=True)

注释:由于最后一层使用了bias因此我们会多加两个参数

训练主体的实现

  1. epochs = 50
  2. loss_fn = torch.nn.CrossEntropyLoss()
  3. opt = torch.optim.SGD(lr=0.01, params=model.parameters())
  4. device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
  5. # device = torch.device('cpu')
  6. model.to(device)
  7. opt_step = torch.optim.lr_scheduler.StepLR(opt, step_size=20, gamma=0.1)
  8. max_acc = 0
  9. epoch_acc = []
  10. epoch_loss = []
  11. for epoch in range(epochs):
  12. for type_id, loader in enumerate([train_loader, val_loader]):
  13. mean_loss = []
  14. mean_acc = []
  15. for images, labels in loader:
  16. if type_id == 0:
  17. # opt_step.step()
  18. model.train()
  19. else:
  20. model.eval()
  21. images = images.to(device)
  22. labels = labels.to(device).long()
  23. opt.zero_grad()
  24. with torch.set_grad_enabled(type_id==0):
  25. outputs = model(images)
  26. _, pre_labels = torch.max(outputs, 1)
  27. loss = loss_fn(outputs, labels)
  28. if type_id == 0:
  29. loss.backward()
  30. opt.step()
  31. acc = torch.sum(pre_labels==labels) / torch.tensor(labels.shape[0], dtype=torch.float32)
  32. mean_loss.append(loss.cpu().detach().numpy())
  33. mean_acc.append(acc.cpu().detach().numpy())
  34. if type_id == 1:
  35. epoch_acc.append(np.mean(mean_acc))
  36. epoch_loss.append(np.mean(mean_loss))
  37. if max_acc < np.mean(mean_acc):
  38. max_acc = np.mean(mean_acc)
  39. print(type_id, np.mean(mean_loss),np.mean(mean_acc))
  40. print(max_acc)

在使用cpu训练的情况,也能快速得到较好的结果,这里训练了50次,其实很快的就已经得到了很好的结果了

PyTorch 迁移学习实践(几分钟即可训练好自己的模型)

总结

本节我们使用了预训练模型,发现大概10个epoch就可以很快的得到较好的结果了,即使在使用cpu情况下训练,这也是迁移学习为什么这么受欢迎的原因之一了,如果读者有兴趣可以自己试一试在不冻结层的情况下,使用方法一能否得到更好的结果

到此这篇关于PyTorch 迁移学习实践(几分钟即可训练好自己的模型)的文章就介绍到这了,更多相关PyTorch 迁移内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/weixin_42263486/article/details/108302350

标签:

相关文章

热门资讯

yue是什么意思 网络流行语yue了是什么梗
yue是什么意思 网络流行语yue了是什么梗 2020-10-11
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
背刺什么意思 网络词语背刺是什么梗
背刺什么意思 网络词语背刺是什么梗 2020-05-22
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总 2020-11-13
2021年耽改剧名单 2021要播出的59部耽改剧列表
2021年耽改剧名单 2021要播出的59部耽改剧列表 2021-03-05
返回顶部