PS:要转载请注明出处,本人版权所有。

PS: 这个只是基于《我自己》的理解,

如果和你的原则及想法相冲突,请谅解,勿喷。

前置说明

  本文作为本人csdn blog的主站的备份。(BlogID=113)

环境说明
  • Ubuntu 18.04
  • MLU270 加速卡一张
  • 寒武纪Pytorch-Docker移植环境

前言


  阅读本文前,请务必须知以下前置文章概念:

  经过了前面两篇文章的介绍,我们也对寒武纪加速平台有了一个朴实的了解。为了加深我们对寒武纪平台的理解,我这里将会使用一个分割网络的实例来展示寒武纪平台整个模型移植和部署过程。

  若文中引用部分存在侵权,请及时联系我删除。

实例基本介绍


  这里对这个简单的分割网络做一个简介,这里训练使用的是CamVid数据集。输入是1*3*480*480。输出是480*480。

  这里最终的效果就是分割出输入图片里面的汽车。最终网络效果测试如下图:

  这个时候,我们也得到了一个可以用于移植和测试的pth模型文件。

移植模型基本步骤


  其实Pytorch的模型移植还是比较简单的,按照一定流程进行测试即可。我总结的基本流程如下:

  • 在docker里面,跑cpu版本的模型推理代码。
  • 在docker里面,跑cpu版本的量化模型生成代码,同时进行量化模型的测试。
  • 在docker里面,将量化模型转换为离线模型。
在Docker里运行cpu推理代码

  至今为止,根据寒武纪官方文档描述,现在的docker环境里面存在的是pytorch1.3环境,这个可能和主流模型支持的pytorch 1.7+有差异。所以,为了后续工作的顺利展开,我们不要一上来就开始量化模型,先保证模型能够在pytorch 1.3环境能够正常工作。

  当我们训练好模型后,得到pth文件,然后在训练环境里面还会做一个测试pth文件的脚本,判断模型的效果。同理,我们应该将此测试脚本放到移植环境里面再跑一次,一般来说都会多多少少出点问题。

  至今为止,我们遇到过两大类问题,一类为pytorch1.3某些算子不支持,可以更换为其他类似算子,或者自己实现这个算子。第二类为一些版本问题,比如模型保存的格式在pytorch1.6后使用的是zip格式(详情见torch.save api说明注释里面),旧版本要加载模型,需要使用_use_new_zipfile_serialization=False重新存储一下模型文件。

  一般来说,大致的模型转换代码如下:

# 存在一个模型test.pth(zip格式)
# 存在一个获取的模型网络结构类:TestModel
import torch model = TestModel()
state_dict = torch.load('test.pth', map_location=torch.device('cpu'))
model.load_state_dict(state_dict, strict=True) torch.save(model, 'new_test.pth', _use_new_zipfile_serialization=False)
# 得到了旧版本的pth文件。方便pytorch 1.6以下进行加载
在Docker里处理量化模型

  这里有两个步骤,首先是使用寒武纪的pytorch接口生成量化模型,然后对量化模型进行测试。注意,这里生成的量化模型有两种,一种是INT8,一种是INT16,具体怎么选择,根据实际情况。一般来说,分类、分割算法可以尝试直接使用INT8,目标检测需要测试再下结论。此外INT8由于运算量的减少,也意味着推理速度的提升。如果不特殊说明,后续默认采用的是INT8模式。

  此外,还需要说明的是,量化一般是量化卷积、全连接等这些参数量较大的层,其他的模型参数依旧是FP16或者FP32存在。

  首先生成量化模型:

# 存在一个模型new_test.pth(非zip格式)
# 存在一个获取的模型网络结构类:TestModel
import torch
import torch_mlu.core.mlu_quantize as mlu_quantize model = TestModel()
state_dict = torch.load('new_test.pth', map_location=torch.device('cpu'))
model.load_state_dict(state_dict, False)
mean=[]
std=[]
# 注意此接口,这里不使用firstconv优化,它的作用是将归一化放到第一层去一起加速做,但是有些模型的前处理是不需要这样做的,具体信息,请参考寒武纪官方文档。
net_quantization = mlu_quantize.quantize_dynamic_mlu(model, {'mean':mean, 'std':std, 'firstconv':False}, dtype='int8', gen_quant=True)
torch.save(net_quantization.state_dict(), 'test_quantization.pth') # 得到了INT8的量化模型文件test_quantization.pth

  然后在量化模型上测试,此步骤的内容是验证模型量化之后,使用寒武纪定制的pytorch量化算子能否正常得到结果:

# 存在一个INT8的量化模型文件test_quantization.pth
# 存在一个获取的模型网络结构类:TestModel
import torch_mlu
import torch_mlu.core.mlu_model as ct
import torch_mlu.core.mlu_quantize as mlu_quantize model = TestModel() # step 1
net = mlu_quantize.quantize_dynamic_mlu(model)
# step 2
net.load_state_dict(torch.load('test_quantization.pth'))
# 这里是
input_data=torch.randn((1,3,480,480))
# step 3
net_mlu = net.to(ct.mlu_device())
input_mlu = input_data.to(ct.mlu_device())
# step 4
output=net_mlu(input_mlu)
print(output.cpu())
# output的shape是480*480

  如果这里量化之后的推理结果都是准确无误的,那么基本证明了模型移植成功了。其实从这里可以看出,这里的mlu其实就可以类比cuda,就可以大致猜想mlu是什么样的存在了。

在Docker里生成离线模型

  在之前的基础上,其实我们很快很方便的就生成了离线模型,不过这里的离线模型同样也有两种,还记得前文说的量化只会量化一些特殊层的参数,而模型中的其他层也是用的FP16或者,FP32,因此,离线模型也具备两种,一种是FP16,一种是FP32。通常来说,一个INT8版本的FP16离线模型是最佳的离线模型。

  生成MLU220离线模型:

# 存在一个INT8的量化模型文件test_quantization.pth
# 存在一个获取的模型网络结构类:TestModel
import torch_mlu
import torch_mlu.core.mlu_model as ct
import torch_mlu.core.mlu_quantize as mlu_quantize model = TestModel() # step 1
net = mlu_quantize.quantize_dynamic_mlu(model)
# step 2
net.load_state_dict(torch.load('test_quantization.pth'))
#
input_data=torch.randn((1,3,480,480))
# step 3
net_mlu = net.to(ct.mlu_device())
input_mlu = input_data.to(ct.mlu_device()) # 详细查看文档,一般4
core_number = 4
ct.set_core_number(core_number)
ct.set_core_version('MLU220')
# torch_mlu.core.mlu_model.set_input_format(input_format)
ct.save_as_cambricon('test') net_trace = torch.jit.trace(net_mlu, input_mlu, check_trace=False) net_trace(input_mlu) torch_mlu.core.mlu_model.save_as_cambricon("") # 最终,我们得到了test.cambricon 和 test.cambricon_twins。test.cambricon_twins是离线模型的说明文件,包含输入数据格式通道等信息,也包含输出相关的信息。

  到此,我们已经得到了离线模型,也完成了我们模型移植的前面一半的工作。

  此外,如果想得到MLU270的离线模型,也可以将set_core_version参数改为MLU270。如果将模型和输入tensor调用half()之后,就会得到fp16的模型格式,具体参考寒武纪官方文档。

  .cambricon_twins文件有一个重要的作用就是描述离线模型网络是输入输出格式及通道,毕竟我们的网络一般结果对不上,很大的原因都是预处理和后处理的毛病。下面我会给出.cambricon_twins的两个实例,一个是INT8FP32,一个是INT8FP16。

后记


  模型的移植流程,基本上都是固定的,一旦熟悉之后,其实就不会改了。

  最终一般有6个模型生成,两个INT8和INT16的量化模型。4个离线模型,INT8-FP32,INT8-FP16,INT16-FP32,INT16-FP16。不同的模型对应不同的速度和精度,根据模型实际情况酌情选择。

  注意,寒武纪除了支持Pytorch模型移植外,还支持caffe和tensorflow,因此如果需要转换这些模型,请查看对应文档。

参考文献


打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)

PS: 请尊重原创,不喜勿喷。

PS: 要转载请注明出处,本人版权所有。

PS: 有问题请留言,看到后我会第一时间回复。

寒武纪加速平台(MLU200系列) 摸鱼指南(三)--- 模型移植-分割网络实例的更多相关文章

  1. 寒武纪加速平台(MLU200系列) 摸鱼指南(四)--- 边缘端实例程序分析

    PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明   本文作为本人csdn blog的主站的备份.(Bl ...

  2. 寒武纪加速平台(MLU200系列) 摸鱼指南(二)--- 模型移植-环境搭建

    PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明   本文作为本人csdn blog的主站的备份.(Bl ...

  3. 寒武纪加速平台(MLU200系列) 摸鱼指南(一)--- 基本概念及相关介绍

    PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明   本文作为本人csdn blog的主站的备份.(Bl ...

  4. 春节前“摸鱼”指南——SCA命令行工具助你快速构建FaaS服务

    春节将至,身在公司的你是不是已经完全丧失了工作的斗志? 但俗话说得好:"只要心中有沙,办公室也能是马尔代夫." 职场人如何才能做到最大效能地带薪"摸鱼",成为了 ...

  5. 【转】让Chrome化身成为摸鱼神器,利用Chorme运行布卡漫画以及其他安卓APK应用教程

    下周就是十一了,无论是学生党还是工作党,大家的大概都会有点心不在焉,为了让大家更好的心不在焉,更好的在十一前最后一周愉快的摸鱼,今天就写一个如何让Chrome(google浏览器)运行安卓APK应用的 ...

  6. Thief-Book 上班摸鱼神器

    Thief-Book 上班摸鱼神器 介绍 Thief-Book 是一款真正的摸鱼神器,可以更加隐秘性大胆的看小说. 隐蔽性 自定义透明背景,随意调整大小,完美融入各种软件界面 快捷性 三个快捷键,实现 ...

  7. AI解决方案:边缘计算和GPU加速平台

    AI解决方案:边缘计算和GPU加速平台 一.适用于边缘 AI 的解决方案 AI 在边缘蓬勃发展.AI 和云原生应用程序.物联网及其数十亿的传感器以及 5G 网络现已使得在边缘大规模部署 AI 成为可能 ...

  8. 菜鸡学C语言之摸鱼村村长

    题目描述 摸鱼村要选村长了! 选村长的规则是村里每个人进行一次投票,票数大于人数一半的成为村长. 然鹅摸鱼村的人都比较懒,你能帮他们写一个程序来找出谁当选村长吗? (每名村民的编号都是一个int范围内 ...

  9. [摸鱼]cdq分治 && 学习笔记

    待我玩会游戏整理下思绪(分明是想摸鱼 cdq分治是一种用于降维和处理对不同子区间有贡献的离线分治算法 对于常见的操作查询题目而言,时间总是有序的,而cdq分治则是耗费\(O(logq)\)的代价使动态 ...

  10. HNOI2018 摸鱼记

    HNOI2018 摸鱼记 今天我又来记流水账啦 Day 0 颓废的一天. 我,球爷和杜教在颓膜膜.io ych看起来在搓碧蓝 鬼知道哥达鸭干了什么 学习氛围只局限在机房的一角 后来全体Oier开会,5 ...

随机推荐

  1. Golang中make和new的区别

    1. 相同点 都是内建函数,都是在堆上分配内存,都需要传递类型参数 2. 不同点 传递的参数不一样,new函数只接收一个参数,make函数可以接收一个以上的参数 package main import ...

  2. 双端队列(deque)--python

    Python中的双端队列(deque)是一种特殊的数据结构,它允许在队列的两端进行插入和删除操作12.双端队列可以看成栈和队列的结合3.在Python中,我们可以使用collections模块中的de ...

  3. 手动实现apply、call、bind

    手动实现apply.call.bind 每个Function对象都存在apply().call().bind()方法,其作用都是可以在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函 ...

  4. Java设计模式-命令模式Command

    介绍 命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收 者是谁,也不知道被请求的操作是哪个, 我们只需在程序运行时指定具体的请求接收者即 ...

  5. 网络上收集的C++常见面试题

    1. 进程与线程的关系,图解 进程简单理解就是我们平常使用的程序,进程拥有自己独立的内存空间地址,拥有一个以上的线程. 线程可以理解为轻量级的进程,是程序执行的最小单元.在某个进程启动后,会默认产生一 ...

  6. win32-localtime的使用

    下面的例子用于反映本地系统的日期格式变化 // locale test #include <stdio.h> #include <locale.h> #include < ...

  7. timeit测试函数执行时间

    def list_append(): l = [] for i in range(5000): l.append(i) def list_insert(): l = [] for i in range ...

  8. HTML学习---day01

    1.head标签 <!DOCTYPE html> <!--文档声明H5 html--> <html lang="en"> <head> ...

  9. 解析Spring中的循环依赖问题:初探三级缓存

    什么是循环依赖? 这个情况很简单,即A对象依赖B对象,同时B对象也依赖A对象,让我们来简单看一下. // A依赖了B class A{ public B b; } // B依赖了A class B{ ...

  10. 【LeetCode贪心#07】分糖果(两个维度)

    发糖果 力扣题目链接(opens new window) 老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分. 你需要按照以下要求,帮助老师给这些孩子分发糖 ...