一句话概括项目
使用华为atlas 200dk开发板部署一个yolov4的口罩检测模型。这个项目的难点有两个,第一个是需要训练一个口罩检测模型,第二个难点是需要将模型转换成开发板可以加速推理的格式。我使用了pytoch训练了一个yolov4模型,数据集是从网上下载的开源口罩数据集,(yolov4的主干网络是CSPDarknet53,头部网络由SPP和PAN模块组成。)在训练得到了.pth格式的模型文件后,我先是将其转换为了.onnx格式的模型文件,之后在开发板上使用ATC工具将其转换为了.om格式文件,ATC名为昇腾张量编译器,是华为研发的将主流开源框架模型转化为昇腾芯片支持格式的工具。转化后的om格式文件可以在昇腾芯片上得到加速。
感悟
作为开发板的使用者,在使用时的主要困难是开发板无法支持最新的算子,ATC工具能够支持的模型有限,想要能够使用ATC工具转换新算子需要算法工程师还要学会ATC转换代码的编写,毫无疑问这个学习成本比较高。这本质上是生态问题,如果华为的模型部署工程师足够多,或者华为开发板生态圈的人数多,能够快速增加ATC工具能转换的算子数量,或许能够解决这个问题。
其实在很多领域中国都面临这个问题,面对西方技术的先发优势,国产技术的生态圈会被抑制,国外的工具因为先发优势在短时间内比国内技术好,许多国内的技术人员会不得已去用国外的技术(比如深度学习多使用Nvidia的显卡)这样会使国内技术生态圈的发展受到影响。
不能等到国外掐脖子、封锁技术的时候才努力追赶,那样转变的阵痛太大,必须尽早实现可替代。要从两方面入手,一方面是使用技术的人,通过政策来推进国内技术人员使用国产技术;另一方面大力支持国内技术的发展。
开发板环境配置
- SD卡烧录环境
- 共享网络
训练模型
VOC格式数据集
VOC
├─Annotations
│ ├─img0001.xml
│ ├─img0002.xml
│ ├─img0003.xml
│ ├─img0004.xml
│ ├─img0005.xml
│ └─img0006.xml
│
├─ImageSets
│ └─Main
│ ├─test.txt
│ ├─train.txt
│ ├─trainval.txt
│ └─val.txt
│
└─JPEGImages
├─img0001.jpg
├─img0002.jpg
├─img0003.jpg
├─img0004.jpg
├─img0005.jpg
└─img0006.jpg
模型结构
YOLOv4 的网络结构主要由两个部分组成:主干网络和头部网络。主干网络使用 CSPDarknet53 架构,它是一种基于 Darknet53 的改进版网络结构,具有更好的特征提取能力和更快的速度。头部网络主要由 SPP 和 PAN 模块组成,用于进一步提取特征和跨尺度特征融合。此外,YOLOv4 还采用了许多技巧来提高网络的准确度和速度,包括 Mish 激活函数、CmBN 和 DropBlock 正则化等。
模型格式转换
- 利用Pytorch框架自带的pth模型转onnx模型的API接口torch.onnx.export进行第一次模型转换,在该API中需填入原始模型路径、输入大小(1x3x416x416)、输入输出端口名等参数。
- 使用昇腾张量编译器(ATC)工具将onnx模型转换为昇腾AI处理器支持的模型文件,即om格式的模型。模型转换过程中,ATC会进行算子调度优化、权重数据重排、内存使用优化等操作,对开源框架的网络模型做进一步调优,使其高效地在昇腾AI处理器上执行
如果Pytorch模型中使用了ATC不支持的算子则无法支持转化。
跑通推理代码
使用ACL(Ascend Computing Language,昇腾计算语言)提供的API来进行模型推理加速
def main(): if detect=="video": if (len(sys.argv) != 3): print("Please input video path") exit(1) #ACL resource initialization acl_resource = AclLiteResource() acl_resource.init() #load model model = AclLiteModel(MODEL_PATH) #open video video_path = sys.argv[1] print("open video ", video_path) cap = cv.VideoCapture(video_path) fps = cap.get(cv.CAP_PROP_FPS) Width = int(cap.get(cv.CAP_PROP_FRAME_WIDTH)) Height = int(cap.get(cv.CAP_PROP_FRAME_HEIGHT)) #create output directory if not os.path.exists(OUTPUT_DIR): os.mkdir(OUTPUT_DIR) output_Video = os.path.basename(video_path) output_Video = os.path.join(OUTPUT_DIR, output_Video) fourcc = cv.VideoWriter_fourcc(*'mp4v') # DIVX, XVID, MJPG, X264, WMV1, WMV2 outVideo = cv.VideoWriter(output_Video, fourcc, fps, (Width, Height)) # Read until video is completed while (cap.isOpened()): ret, frame = cap.read() if ret == True: #preprocess data, orig = preprocess_video(frame) #Send into model inference result_list = model.execute([data,]) #Process inference results result_return = post_process(result_list, orig) print("result = ", result_return) #Process lane line for i in range(len(result_return['detection_classes'])): box = result_return['detection_boxes'][i] class_name = result_return['detection_classes'][i] confidence = result_return['detection_scores'][i] label_dis = '{} {:.2f}'.format('confidence:', confidence) cv.putText(frame, label_dis, (int(box[1]) + 10, int(box[2]) + 15), cv.FONT_HERSHEY_SIMPLEX, 1, colors[i % 6], 2) cv.rectangle(frame, (int(box[1]), int(box[0])), (int(box[3]), int(box[2])), colors[i % 6],2) p3 = (max(int(box[1]), 15), max(int(box[0]), 15)) out_label = class_name cv.putText(frame, out_label, p3, cv.FONT_HERSHEY_SIMPLEX, 1, colors[i % 6], 2) outVideo.write(frame) # Break the loop else: break cap.release() outVideo.release() print("Execute end") elif detect=='picture': if not os.path.exists(OUTPUT_DIR): os.mkdir(OUTPUT_DIR) #ACL resource initialization acl_resource = AclLiteResource() acl_resource.init() #load model model = AclLiteModel(MODEL_PATH) images_list = [os.path.join(INPUT_DIR, img) for img in os.listdir(INPUT_DIR) if os.path.splitext(img)[1] in const.IMG_EXT] #Read images from the data directory one by one for reasoning for pic in images_list: #read image bgr_img = cv.imread(pic) #preprocess data, orig = preprocess_img(pic) #Send into model inference result_list = model.execute([data,]) #Process inference results result_return = post_process(result_list, orig) print("result = ", result_return) for i in range(len(result_return['detection_classes'])): box = result_return['detection_boxes'][i] class_name = result_return['detection_classes'][i] confidence = result_return['detection_scores'][i] label_dis = '{} {:.2f}'.format('confidence:', confidence) cv.putText(bgr_img, label_dis, (int(box[1]) + 10, int(box[2]) + 15), cv.FONT_HERSHEY_SIMPLEX, 1, colors[i % 6], 2) cv.rectangle(bgr_img, (int(box[1]), int(box[0])), (int(box[3]), int(box[2])), colors[i % 6],2) p3 = (max(int(box[1]), 15), max(int(box[0]), 15)) out_label = class_name cv.putText(bgr_img, out_label, p3, cv.FONT_HERSHEY_SIMPLEX, 1, colors[i % 6], 2) output_file = os.path.join(OUTPUT_DIR, "out_" + os.path.basename(pic)) print("output:%s" % output_file) cv.imwrite(output_file, bgr_img) print("Execute end") else: print("please choose correct \"detect\"") if __name__ == '__main__': main()