一、作业题目
必做题:
(1) 把模型改为resnet18,加载相应的模型权重(Lesson2的物料包中有),跑一下0.jpg和 1.jpg,看一下输出结果。官方 torchvision 训练 mobilenet 和训练 resnet 的方式是一样的,所以数据预处理和数据后处理部分完全相同。
(2) 自己找2张其他图,用resnet18做下推理。
思考题:
(1) 以ResNet18为例,用time模块和for循环,对”./images/0.jpg”连续推理100次,统计时间开销,比如:
model_classify=ModelPipline()
import time image=cv2.imread("./images/0.jpg") t_all=0
for i in range(100):
t_start=time.time() result=model_classify.predict(image) t_end=time.time() t_all+=t_end-t_start
print(t_all)
有 CUDA 的同学,改下代码:self.device=torch.device('cuda')。用上述相同方法测试时间开销。
(2) 在数据预处理和数据后处理的代码实现中,到处在用 numpy, opencv, torch 对数组做 相应变换,大家至少要把课程中出现的函数们给理解。
二、实现情况
1、基本类定义(只需要将get_model中的模型文件进行替换就可以变为resnet18的模型,不得不说这段代码写的真的无敌,新手入门必备属于是)
import torch
import torchvision.models as models
import numpy as np
import cv2
class ModelPipline(object):
def __init__(self):
#进入模型的图片大小:为数据预处理和后处理做准备
self.inputs_size=(224,224)
#CPU or CUDA:为数据预处理和模型加载做准备
self.device=torch.device('cpu')
#载入模型结构和模型权重
self.model=self.get_model()
#载入标签,为数据后处理做准备
label_names=open('./labels/imagenet_label.txt','r').readlines()
self.label_names=[line.strip('\n') for line in label_names]
def predict(self, image):
#数据预处理
inputs=self.preprocess(image)
#数据进网络
outputs=self.model(inputs)
#数据后处理
results=self.postprocess(outputs)
return results
def get_model(self):
#上一节课的内容
model = models.mobilenet_v2(num_classes=1000)
pretrained_state_dict=torch.load('./weights/mobilenet_v2-b0353104.pth', map_location=lambda storage, loc: storage)
model.load_state_dict(pretrained_state_dict, strict=True)
model.to(self.device)
model.eval()
return model
def preprocess(self, image):
#opencv默认读入是BGR,需要转为RGB,和训练时保持一致
image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
#resize成模型输入的大小,和训练时保持一致
image=cv2.resize(image, dsize=self.inputs_size)
#归一化和标准化,和训练时保持一致
inputs=image/255
inputs=(inputs-np.array([0.485, 0.456, 0.406]))/np.array([0.229, 0.224, 0.225])
##以下是图像任务的通用处理
#(H,W,C) ——> (C,H,W)
inputs=inputs.transpose(2,0,1)
#(C,H,W) ——> (1,C,H,W)
inputs=inputs[np.newaxis,:,:,:]
#NumpyArray ——> Tensor
inputs=torch.from_numpy(inputs)
#dtype float32
inputs=inputs.type(torch.float32)
#与self.model放在相同硬件上
inputs=inputs.to(self.device)
return inputs
def postprocess(self, outputs):
#取softmax得到每个类别的置信度
outputs=torch.softmax(outputs,dim=1)
#取最高置信度的类别和分数
score, label_id = torch.max(outputs, dim=1)
#Tensor ——> float
score, label_id =score.item(), label_id.item()
#查找标签名称
label_name=self.label_names[label_id]
return label_name, score
2、问题结果
必做题:
(1)实现的main函数如下:
if __name__=='__main__':
model_classify=ModelPipline()
image=cv2.imread('./images/0.jpg')
result=model_classify.predict(image)
print(result)
image=cv2.imread('./images/1.jpg')
result=model_classify.predict(image)
print(result)
素材如下:
推理结果如下,可以看出识别率其实已经较高(Resnet,当前的神):
(2)该题只需修改main函数中的文件名就行:
if __name__=='__main__':
model_classify=ModelPipline()
image=cv2.imread('./images/Anime_girl.jpeg')
result=model_classify.predict(image)
print(result)
image=cv2.imread('./images/y.Innocence.jpg')
result=model_classify.predict(image)
print(result)
下面是我我随便找的一张色图素材和最喜欢的选手(第一张就不发了,大家知道是不明所以的冻鳗小人就行):
图1 y队:被指到的人de一天的bug
由于两张图中的人都不是真实情况下的人,其中选手人像经过了灰度化的处理,并有包括队标、签名等噪声干扰,而二次元小人更不是正常的人类(不过居然识别出来了comic book),因此在resnet18下的识别情况非常惨烈:
于是我一气之下,就去换了一个resnet50的网络和权重,再跑了一次,这次的结果出乎我意料:
好家伙,你去识别我的二次元小人就识别出一个brassiere。。。不知道该说你准确还是说机器已经知道了宅男们喜欢什么。。。而选手这次识别出了一个杠铃就更让我感到离谱。(直到我想起来官方的1000个分类里面好像没有人像或者是人脸。。。)
于是不甘心的我换了一张上周想来我家蹭饭结果被我关在外面的猫猫:
同时用resnet50和resnet18去识别它,结果比较出人意料,两者都认为这个图像的主要目标是整个窗户(猫猫,我的猫猫),并且识别率有着惊人的反差,看来卷积层越多不一定能有越好的效果 。
思考题:
(1)识别速度:
由于mac并不能装NVIDIA的显卡,因此不能安装cuda,目前先做一个CPU版本,GPU测试时间明天补上。
CPU运行的速度比想象的快很多,在2015年的imac上面跑100次resnet18仅用了10S,比我以前用学校的某个服务器进行CPU推理快多了,不过确实网络也比之前要小很多。
不过其实我不确定这个速度会不会和python中pycache的某种特殊保留方式有关,也许跑100张不一样的图可能结果会不一样?
GPU和CPU在代码上的差距就是将送入的device改为cuda,这个可以直接在类型的初始化中更改。更改后的类如下:
class ModelPipline(object):
def __init__(self,model_path,model,labels_path,device_string='cpu'):
#进入模型的图片大小:为数据预处理和后处理做准备
self.inputs_size=(224,224)
#CPU or CUDA:为数据预处理和模型加载做准备
self.device=torch.device(device_string)
#载入模型结构和模型权重
self.model=self.get_model(model_path,model)
#载入标签,为数据后处理做准备
label_names=open(labels_path,'r').readlines()
self.label_names=[line.strip('\n') for line in label_names]
其中使用的设备在缺省的情况下为CPU,只有在构造的时候进行如下设定,才会使用GPU。
#最后设定参数为'cuda'
model_classify2=ModelPipline('./weights/resnet18-5c106cde.pth',models.resnet18(num_classes=1000),'./labels/imagenet_label.txt','cuda')
cuda使用时间明天补上。