赞
踩
场景:
从图像中识别出不同种类的椅子,然后将购买链接推荐给用户。
方案1: 找出100种常见的椅子,为每种椅子拍摄1000张不同角度的图像,并训练模型。
缺点: 数据集看似庞大,样本类型和数据集仍不足;适用于更大规模数据集的模型,可能会在该数据集上过拟合。
方案2: 收集更多的数据。
缺点: 收集和标注的成本巨大。
方案3: 应用迁移学习 (transfer learning):将从源数据集学到的知识迁移到目标数据集。例如,虽然ImageNet数据集的图像大多和椅子无关,但是该数据集上训练的模型可以抽取较通用的图像特征,从而帮助识别边缘、纹理、形状和物体组成等。这些类似的特征对于识别椅子也可能同样有效。
微调 (fine tuning)是迁移学习的一种常用技术,分为以下四步,具体如下图:
1)在源数据集上训练一个神经网络模型,即源模型;
2)创建一个新的神经网络模型,即目标模型:它复制了源模型上除输出层外的所有模型设计及其参数;
3)为目标模型添加一个输出大小为目标数据集类别个数的输出层,并随机初始化该层的模型参数;
4)在目标数据集上训练目标模型。我们将从新训练输出层,其余层的参数则是微调获得。
该方案的假设如下:
1)源模型参数包含了源数据集上学到的知识,并能够适用于目标数据集;
2)源模型的输出层与源数据集的标签紧密相关,因此在目标模型中不予采用。
优点:
当目标数据集远小于源数据集时,微调有助于提升模型的泛化能力。
本例基于一个小数据集对在ImageNet数据集上训练好的ResNet模型进行微调。该小数据集包含数千张包含热狗和不包含热狗的图像的图像。最终将判断一个图像中是否包含热狗。
数据集的说明和载入可以参照:
https://blog.csdn.net/weixin_44575152/article/details/118901491
训练: 从图像中随机裁剪出随机大小和随机高宽比的一块区域,并将该区域缩放为
224
×
224
224 \times 224
224×224。
测试: 将图像的高和宽缩放为
256
×
256
256 \times 256
256×256,并裁剪出
224
×
224
224 \times 224
224×224的中心区域。
RGB标准化: 每个通道的数值减去当前通道所有数值的平均值,再除以该通道所有数值的标准差作为输出。
import torch from torch import nn, optim from torch.utils.data import DataLoader from torchvision import transforms, models from torchvision.datasets import ImageFolder def get_format_image(save_home="D:/Data/Image/hotdog/"): """ 图像载入 """ # 训练集和测试集 normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) aug_tr = transforms.Compose([ transforms.RandomResizedCrop(size=224), transforms.RandomHorizontalFlip(), transforms.ToTensor(), normalize, ]) aug_te = transforms.Compose([ transforms.Resize(size=256), transforms.CenterCrop(size=224), transforms.ToTensor(), normalize, ]) image_tr = DataLoader(ImageFolder(save_home + "train", transform=aug_tr), batch_size=128, shuffle=True) image_te = DataLoader(ImageFolder(save_home + "train", transform=aug_te), batch_size=128) return image_tr, image_te
对于预训练的模型,可以设置较小的学习率进行微调;而新加入的用于适应输出的全连接层,学习率则设置为较大值:
def get_model(lr=0.01):
"""获取预训练模型"""
net_pretrained = models.resnet18(pretrained=True)
# fc是一个全连接层,通过设置可以适应指定数据集,例如二分类
net_pretrained.fc = nn.Linear(512, 2)
params_output = list(map(id, net_pretrained.fc.parameters()))
params_feature = filter(lambda p: id(p) not in params_output, net_pretrained.parameters())
# 这里fc层是从头学,所以其学习率设置的较大,而预训练层的则相对较小
optimizer = optim.SGD([
{"params": params_feature},
{"params": net_pretrained.fc.parameters(),
"lr": lr * 10}],
lr=lr, weight_decay=0.001,
)
return net_pretrained, optimizer
def train(train_iter, test_iter, net, loss, optimizer, num_epochs): import time device = torch.device("cuda" if torch.cuda.is_available() else "cpu") net = net.to(device) print("training on ", device) batch_count = 0 for epoch in range(num_epochs): train_l_sum, train_acc_sum, n, start = 0.0, 0.0, 0, time.time() for X, y in train_iter: X = X.to(device) y = y.to(device) y_hat = net(X) l = loss(y_hat, y) optimizer.zero_grad() l.backward() optimizer.step() train_l_sum += l.cpu().item() train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item() n += y.shape[0] batch_count += 1 test_acc = evaluate_accuracy(test_iter, net) print("epoch %d, loss %.4f, train acc %.3f, test acc %.3f, time %.1f sec" % (epoch + 1, train_l_sum / batch_count, train_acc_sum / n, test_acc, time.time() - start)) def evaluate_accuracy(data_iter, net, device=None): if device is None and isinstance(net, torch.nn.Module): # 如果没指定device就使用net的device device = list(net.parameters())[0].device acc_sum, n = 0.0, 0 with torch.no_grad(): for X, y in data_iter: if isinstance(net, torch.nn.Module): net.eval() # 评估模式, 这会关闭dropout acc_sum += (net(X.to(device)).argmax(dim=1) == y.to(device)).float().sum().cpu().item() net.train() # 改回训练模式 else: # 自定义的模型, 3.13节之后不会用到, 不考虑GPU if ('is_training' in net.__code__.co_varnames): # 如果有is_training这个参数 # 将is_training设置成False acc_sum += (net(X, is_training=False).argmax(dim=1) == y).float().sum().item() else: acc_sum += (net(X).argmax(dim=1) == y).float().sum().item() n += y.shape[0] return acc_sum / n def train_fine_tuning(): """模型微调测试""" net, optimizer = get_model() image_tr, image_te = get_format_image() loss = torch.nn.CrossEntropyLoss() train(image_tr, image_te, net, loss, optimizer, num_epochs=5) if __name__ == '__main__': train_fine_tuning()
感谢李沐、Aston Zhang等老师的这本《动手学深度学习》一书,为鄙人学习深度学习提供了很大的帮助。本文一系列关于深度学习的博客均无侵权之意,只为记录自己的深度学习历程。
项目Github地址:https://github.com/ShusenTang/Dive-into-DL-PyTorch
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。