《深度学习之Pytorch实战计算机视觉》阅读笔记

第7章:迁移学习

1. 迁移学习

  • 通过对一个训练好的模型进行细微调整,将其应用到相似的问题中取得很好的效果
  • 迁移学习也能有效解决原始数据较少的问题
  • 在使用迁移学习的过程中,有时会导致迁移模型出现负迁移

2. 数据及处理

数据集:Kaggle Dogs vs Cats
其Test测试集中图片无需混杂,没有对应的标签,所以从Train训练集中抽出部分数据代替

os.path

  • os.path.dirname:返回一个目录的目录名
  • os.path.exists:测试输入参数指定的文件是否存在
  • os.path.isdir:测试输入的参数是否是目录名
  • os.path.isfile:用于测试输入参数是否是一个文件
  • os.path.samefile:用于测试两个输入的路径参数是否指向同一个文件
  • os.path.split:返回输入参数中目录的分割元组,目录名+文件名

数据集目录

  1. # datasets.ImageFolder函数
  2. # ImageFolder加载图片目录的一般形式为 root/分类标签/分类文件
  3. --- train
  4. --- cat
  5. --- ***.jpg
  6. --- ...
  7. --- dog
  8. --- ***.jpg
  9. --- ...
  10. --- test
  11. --- cat
  12. --- ***.jpg
  13. --- ...
  14. --- dog
  15. --- ***.jpg
  16. --- ...

加载数据

  1. data_dir = "./data/DogsAndCats"
  2. data_transform = {
  3. x: transforms.Compose([transforms.Resize([64, 64]),
  4. transforms.ToTensor()])
  5. for x in ["train", "test"]
  6. }
  7. image_datasets = {
  8. x: datasets.ImageFolder(root=os.path.join(data_dir, x),
  9. transform=data_transform[x])
  10. for x in ["train", "test"]
  11. }
  12. print(image_datasets)
  13. dataloader = {
  14. x: torch.utils.data.DataLoader(dataset=image_datasets[x],
  15. batch_size=16,
  16. shuffle=True)
  17. for x in ["train", "test"]
  18. }

DataLoader

  1. {'train': Dataset ImageFolder
  2. Number of datapoints: 25000
  3. Root location: ./data/DogsAndCats/train,
  4. 'test': Dataset ImageFolder
  5. Number of datapoints: 25000
  6. Root location: ./data/DogsAndCats/test}

3. 自定义网络

  • 网络模型

    1. class Model(torch.nn.Module):
    2. def __init__(self):
    3. super(Model, self).__init__()
    4. self.Conv = torch.nn.Sequential(
    5. torch.nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1),
    6. torch.nn.ReLU(),
    7. torch.nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1),
    8. torch.nn.ReLU(),
    9. torch.nn.MaxPool2d(kernel_size=2, stride=2),
    10. torch.nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
    11. torch.nn.ReLU(),
    12. torch.nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1),
    13. torch.nn.ReLU(),
    14. torch.nn.MaxPool2d(kernel_size=2, stride=2),
    15. torch.nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1),
    16. torch.nn.ReLU(),
    17. torch.nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1),
    18. torch.nn.ReLU(),
    19. torch.nn.MaxPool2d(kernel_size=2, stride=2),
    20. torch.nn.Conv2d(256, 512, kernel_size=3, stride=1, padding=1),
    21. torch.nn.ReLU(),
    22. torch.nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1),
    23. torch.nn.ReLU(),
    24. torch.nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1),
    25. torch.nn.ReLU(),
    26. torch.nn.MaxPool2d(kernel_size=2, stride=2))
    27. self.Classes = torch.nn.Sequential(torch.nn.Linear(4 * 4 * 512, 1024),
    28. torch.nn.ReLU(),
    29. torch.nn.Dropout(p=0.5),
    30. torch.nn.Linear(1024, 1024),
    31. torch.nn.ReLU(),
    32. torch.nn.Dropout(p=0.5),
    33. torch.nn.Linear(1024, 2))
    34. def forward(self, input):
    35. x = self.Conv(input)
    36. x = x.view(-1, 4 * 4 * 512)
    37. x = self.Classes(x)
    38. return x
  • 损失函数
    loss_f = torch.nn.CrossEntropyLoss()

  • 优化函数
    optimizer = torch.optim.Adam(model.parameters(), lr=0.00001)
  • 改为GPU计算

    1. # 迁移模型
    2. if (cuda):
    3. model = model.cuda()
    4. # 迁移变量
    5. if (cuda):
    6. x, y = Variable(x.cuda()), Variable(y.cuda())
    7. else:
    8. x, y = Variable(x), Variable(y)
  • 训练结果
    识别准确率大概是76%

4. 迁移到VGG16网络

  • 引入VGG16网络

    1. from torchvision import datasets, models, transforms
    2. # 已经具备最有参数
    3. model = models.vgg16(pretrained=True)
  • 冻结卷积神经网络中的参数
    VGG16网络已自带最优参数,无需进行梯度更新。

    1. for param in model.parameters():
    2. param.requires_grad = False
  • VGG16的全连接层(分类器)
    7.Pytorch实战之卷积神经网络结构 - 图1

VGG16分类器输出结果有1000个参数,对于实际分类结果来看只需要两个参数,因此必须更换全连接层。

  • 更换分类器

    1. model.classifier = torch.nn.Sequential(
    2. torch.nn.Linear(25088,4096),
    3. torch.nn.ReLU(),
    4. torch.nn.Dropout(p=0.5),
    5. torch.nn.Linear(4096, 4096),
    6. torch.nn.ReLU(),
    7. torch.nn.Dropout(p=0.5),
    8. torch.nn.Linear(4096, 2)
    9. )


    此时分类器中的参数**require_grad**默认重置为**True**,即只更新全连接层的梯度
    参数优化
    optimizer = torch.optim.Adam(model.classifier.parameters(), lr=0.00001)

  • 训练结果
    识别准确率大概在91%

5. 迁移到ResNet网络

  • 类似VGG16引入过程,以及冻结参数、参数优化
  • ResNet网络的全连接层
    7.Pytorch实战之卷积神经网络结构 - 图2
    ResNet全连接层输出结果有1000个参数
  • 更换全连接层
    model.fc=torch.nn.Linear(2048,2)
  • 训练结果
    识别准确率大概在97%
    在加载数据时,一定要进行正则化操作,不然识别准确率大概只有75%

6. ResNet迁移完整代码

  1. import torch
  2. import torchvision
  3. from torchvision import datasets, models, transforms
  4. import os
  5. from torch.autograd import Variable
  6. import time
  7. data_dir = "./data/DogsAndCats"
  8. data_transform = {
  9. x: transforms.Compose([transforms.CenterCrop(224),
  10. transforms.ToTensor(),
  11. transforms.Normalize(mean=[0.5,0.5,0.5],std=[0.5,0.5,0.5])])
  12. for x in ["train", "test"]
  13. }
  14. image_datasets = {
  15. x: datasets.ImageFolder(root=os.path.join(data_dir, x),
  16. transform=data_transform[x])
  17. for x in ["train", "test"]
  18. }
  19. dataloader = {
  20. x: torch.utils.data.DataLoader(dataset=image_datasets[x],
  21. batch_size=16,
  22. shuffle=True)
  23. for x in ["train", "test"]
  24. }
  25. model = models.resnet50(pretrained=True)
  26. print(model)
  27. for param in model.parameters():
  28. param.requires_grad = False
  29. model.fc=torch.nn.Linear(2048,2)
  30. loss_f = torch.nn.CrossEntropyLoss()
  31. optimizer = torch.optim.Adam(model.fc.parameters(), lr=0.00001)
  32. epoch_n = 5
  33. time_open = time.time()
  34. cuda = torch.cuda.is_available()
  35. if (cuda):
  36. model = model.cuda()
  37. for epoch in range(epoch_n):
  38. print("Epoch {}/{}".format(epoch, epoch_n - 1))
  39. print("-" * 10)
  40. for phase in ["train", "test"]:
  41. if phase == "train":
  42. print("Training...")
  43. model.train(True)
  44. else:
  45. print("Testing")
  46. model.train(False)
  47. running_loss = 0.0
  48. running_corrects = 0
  49. for batch, data in enumerate(dataloader[phase], 1):
  50. x, y = data
  51. if (cuda):
  52. x, y = Variable(x.cuda()), Variable(y.cuda())
  53. else:
  54. x, y = Variable(x), Variable(y)
  55. y_pred = model(x)
  56. _, pred = torch.max(y_pred.data, 1)
  57. optimizer.zero_grad()
  58. loss = loss_f(y_pred, y)
  59. if phase == "train":
  60. loss.backward()
  61. optimizer.step()
  62. running_loss += loss.item()
  63. running_corrects += torch.sum(pred == y.data)
  64. if batch & 500 == 0 and phase == "train":
  65. print("Batch {},Train Loss:{:.4f},Train Acc{:.4f}".format(
  66. batch, running_loss / batch,
  67. 100 * running_corrects / (16 * batch)))
  68. epoch_loss = running_loss * 16 / len(image_datasets[phase])
  69. epoch_acc = 100 * running_corrects / len(image_datasets[phase])
  70. print("{} Loss:{:.4f} Acc:{:.4f}%".format(phase, epoch_loss,
  71. epoch_acc))
  72. time_end = time.time() - time_open
  73. print(time_end)