脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服务器之家 - 脚本之家 - Python - PyTorch 迁移学习实践(几分钟即可训练好自己的模型)

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

2021-09-28 09:01YXHPY Python

这篇文章主要介绍了PyTorch 迁移学习实践(几分钟即可训练好自己的模型),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言

如果你认为深度学习非常的吃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

延伸 · 阅读

精彩推荐