第2章 预训练网络
讨论3种常用的预训练模型:
1、根据内容对图像进行标记(识别)
2、从真实图像中生成新图像(GAN)
3、使用正确的英语句子来描述图像内容(自然语言)
2.1 获取一个预训练好的网络用于图像识别
ImageNet数据集,用于大规模视觉识别挑战赛。
所有预训练好的模型都在TorchVision中。
2.1.1 导入已有的模型
所有模型都在torchvison的models中。导入并查看。
from torchvision import models
dir(models)
输出的是所有torchvison里面集成的模型框架。其中首字母大写的是一些流行的模型。小写的名字是快捷函数,返回实例化模型函数。
1.1.1 AlexNet模型
实例化AlexNet。
alexnet=models.AlexNet()
alexnet
可以像函数一样调用它。给alexnet输入数据,就会通过正向传播(forward pass)得到输出。比如output=alexnet(input)。由于网络没有初始化,没有经过训练。所以一般先要将模型从头训练或者加载训练好的网络。然后再调用。
1.1.2 Resnet模型
(1)加载在ImageNet数据集上训练好的权重,来实例化ResNet101
resnet=models.resnet101(pretrained=True)
resnet
然后就开始下载,下载完成后查看resnet101的结构。
神经网络由许多模块构成,包含过滤器和非线性函数,fc层结束,输出每个类的分数。
预训练好的模型可以跟函数一样调用,并输入图片实现预测。
(2)定义预处理函数:
from torchvision import transforms
preprocess = transforms.Compose([transforms.Resize(256),transforms.CenterCrop(224),transforms.ToTensor(),transforms.Normalize(mean=[0.485,0.456,0.406],std=[0.229,0.224,0.225])
])
预处理包括:图像缩放到256*256像素,围绕中心裁剪到224*224像素,转为张量,归一化处理,使用定义的均值和标准差。
(3)导入图片并进行预处理
导入一张狗的照片并显示
from PIL import Image
img = Image.open('bobby.jpg')
img
待用预处理函数对图片进行预处理。
img_t=preprocess(img)
img_t.shape
输出为一个3维的张量。
给张量前面再增加一个维度。
import torch
batch_t = torch.unsqueeze(img_t,0)#加一个维度,数字0代表增加在第0维前面,如果为1就代表维度1前面
batch_t.shape
输出在第0维前面增加了一个1.
(4)运行模型
在新数据上运行训练过的模型的过程被称为推理(inference),为了推理需要先将网络放到eval模式。执行代码:
resnet.eval()
进行推理:
out=resnet(batch_t)
out
产生了一个1000分类的向量,每个ImageNet对应一个分数。
(5)查看预测结果
加载定义好的ImageNet标签。
with open('imagenet_classes.txt') as f:labels = [line.strip() for line in f.readlines()]
需要找出out输出在labels标签中的索引。可以利用max()函数输出张量中最大值以及最大值的索引。代码如下:
_,index=torch.max(out,1)
index
输出的索引不是一个数字,而是一个一维张量。
使用index[0]获得实际的数字作为标签列表的索引,用torch.nn.functional.softmax()将输出归一化到[0,1],然后除以总和。可以求出模型在预测中的置信度。
代码:
percentage = torch.nn.functional.softmax(out,dim=1)[0]*100
labels[index[0]],percentage[index[0]].item()
输出:('golden retriever', 96.29335021972656)
分类结果维金毛犬,置信度为96%。
也可以对预测结果的其它值进行排序输出。比如输出前5个。
_,indices = torch.sort(out,descending=True)
[(labels[idx],percentage[idx].item()) for idx in indices[0][:5]]
输出:
2.2 一个足以以假乱真的预训练模型
GAN是生成式对抗网络(generative adversarial network)的缩写。
cycleGAN是循环生成式对抗网络的缩写,可以将一个领域的图像转换为另一个领域的图像。
2.2.1 将马变为斑马的网络
CycleGAN从ImageNet数据集中提取的马和斑马的数据集进行训练。该网络学习获取一匹或多匹马的图像,并将它们全部变成斑马,图像的其余部分尽可能不被修改。
使用预训练好的CycleGAN将使我们有机会更进一步了解网络是如何实现的,对于本例就是生成器。
(1)以ResNet为例,定义一个ResNetGenerator类。
import torch
import torch.nn as nnclass ResNetBlock(nn.Module): # <1>def __init__(self, dim):super(ResNetBlock, self).__init__()self.conv_block = self.build_conv_block(dim)def build_conv_block(self, dim):conv_block = []conv_block += [nn.ReflectionPad2d(1)]conv_block += [nn.Conv2d(dim, dim, kernel_size=3, padding=0, bias=True),nn.InstanceNorm2d(dim),nn.ReLU(True)]conv_block += [nn.ReflectionPad2d(1)]conv_block += [nn.Conv2d(dim, dim, kernel_size=3, padding=0, bias=True),nn.InstanceNorm2d(dim)]return nn.Sequential(*conv_block)def forward(self, x):out = x + self.conv_block(x) # <2>return outclass ResNetGenerator(nn.Module):def __init__(self, input_nc=3, output_nc=3, ngf=64, n_blocks=9): # <3> assert(n_blocks >= 0)super(ResNetGenerator, self).__init__()self.input_nc = input_ncself.output_nc = output_ncself.ngf = ngfmodel = [nn.ReflectionPad2d(3),nn.Conv2d(input_nc, ngf, kernel_size=7, padding=0, bias=True),nn.InstanceNorm2d(ngf),nn.ReLU(True)]n_downsampling = 2for i in range(n_downsampling):mult = 2**imodel += [nn.Conv2d(ngf * mult, ngf * mult * 2, kernel_size=3,stride=2, padding=1, bias=True),nn.InstanceNorm2d(ngf * mult * 2),nn.ReLU(True)]mult = 2**n_downsamplingfor i in range(n_blocks):model += [ResNetBlock(ngf * mult)]for i in range(n_downsampling):mult = 2**(n_downsampling - i)model += [nn.ConvTranspose2d(ngf * mult, int(ngf * mult / 2),kernel_size=3, stride=2,padding=1, output_padding=1,bias=True),nn.InstanceNorm2d(int(ngf * mult / 2)),nn.ReLU(True)]model += [nn.ReflectionPad2d(3)]model += [nn.Conv2d(ngf, output_nc, kernel_size=7, padding=0)]model += [nn.Tanh()]self.model = nn.Sequential(*model)def forward(self, input): # <3>return self.model(input)
(2)实例化
netG = ResNetGenerator()
权重为随机权重。
(3)将预训练好的权重添加到ReNet Generator中。
model_path='horse2zebra_0.4.0.pth'
model_data = torch.load(model_path)
netG.load_state_dict(model_data)
执行后,netG就获得了训练中需要的所有知识。
(4)推理
netG.eval()
输出:
程序是将一匹或多匹马逐像素修改。
导入随机马的图像进行测试。
导入需要的库:
from PIL import Image
from torchvision import transforms
定义预处理函数:
preprocess = transforms.Compose([transforms.Resize(256),transforms.ToTensor()
])
导入马的图片:
img = Image.open('horse.jpg')
img
对图片预处理:
img_t = preprocess(img)
batch_t = torch.unsqueeze(img_t,0)
将变量传递给模型:
batch_out = netG(batch_t)
将生成器的输出转换为图像。
out_t = (batch_out.data.squeeze()+1.0)/2.0
out_img = transforms.ToPILImage()(out_t)
out_img
2.6 练习题
1.将金毛猎犬的图像输入马-斑马模型中。
参考资料:
1. 预训练网络 · 深度学习与PyTorch(中文版) (paper2fox.github.io)
4. PyTorch深度学习 Deep Learning with PyTorch ch.2, p2_哔哩哔哩_bilibili