关于人工智能:MindStudio训练营第一季基于MindX的UNet网络的工业质检实践作业

3次阅读

共计 13640 个字符,预计需要花费 35 分钟才能阅读完成。

1.1 作业介绍 1.1.1 作业背景随着新一轮科技反动和产业改革的减速演进,寰球各国都在借助新技术推动制造业降级,从工业 2.0 自动化开始衰亡,到工业 3.0 信息化遍及,现在正迈向工业 4.0 智能化。借助 IoT、工业大数据、人工智能等先进技术实现从低端劳动密集型产业向高端科技型产业的制作降级。在利用人工智能技术之前,局部场景下已呈现传统机器视觉进行质检的案例。然而因为产品整机简单、光源多样等因素的限度,更多场景还是依赖于人工质检。而人工智能技术的交融可进一步晋升检测精度,很多实际已证实 AI 算法可实现高达 99% 以上检测精度,能够利用在绝大多数工业质检场景中。从 AI 算法到工业制作场景化利用还有很远,算法开发、利用开发、业务部署是妨碍 AI 利用进入工业生产的三大鸿沟。为此,华为昇腾计算秉承“硬件凋谢、软件开源”的理念,打造了昇腾智能制作使能平台,致力于推动制作行业转型降级。在硬件方面,华为提供从模组 / 板卡到服务器 / 集群的 Atlas 系列化硬件。Atlas 200 AI 减速模块具备极致性能、超低功耗的特点,能够在端侧实现物体辨认、图像分类等;Atlas 300I 推理卡提供超强 AI 推理性能,以超强算力减速利用,可广泛应用于推理场景。在软件方面,为了帮忙开发者逾越 AI 利用落地制作行业的三大鸿沟,华为提供了全栈软件平台与工具。特地是昇腾利用使能 MindX,帮忙宽广利用开发者疾速开发 AI 利用,让 AI 进入制作行业。据介绍,MindX 中蕴含了“2+1+X”,其中“2”是深度学习使能 MindX DL 和智能边缘使能 MindX Edge,帮忙开发者疾速搭建深度学习和边缘推理的根底平台;“1”是优选模型库 ModelZoo,为开发者提供了各个场景下通过调优的模型,开发者只需依据本身场景须要,按需下载即可;最初是面向行业利用的 SDK,华为曾经在昇腾社区公布了面向智能制作场景的 mxManufacture SDK 和 mxVision SDK,聚焦于工业质检场景,可能以很少的代码量、甚至于零代码实现制作行业 AI 利用开发。1.1.2 作业目标本作业应用工业质检场景中的模仿数据集,采纳 MindSpore 深度学习框架构建 U -Net 网络,在华为云平台的 ModelArts 上创立基于昇腾 910 处理器的训练环境,启动训练并失去图像宰割的模型;之后在华为云平台的 ECS 弹性云服务器上创立基于昇腾 310 处理器的推理环境,将该模型转换成离线模型,应用 MindX SDK mxVision 执行推理工作。本作业目标:• 了解工业质检的背景。• 把握 MindSpore 的根底应用。• 相熟 U -Net 网络和图像宰割的原理常识。• 把握华为云 ModelArts 和 ECS 的环境搭建。• 相熟昇腾 910 和昇腾 310 处理器的应用。• 把握离线模型的转换方法。• 相熟 MindX SDK mxVision 的应用。思考到本次以 MindStudio 为根底,重视推理,因而次要介绍在昇腾 310 上模型推理,训练局部省略。1.1.3 模型介绍 U -Net 介绍:U-Net 模型基于二维图像宰割。在 2015 年 ISBI 细胞跟踪比赛中,U-Net 取得了许多最佳奖项。论文中提出了一种用于医学图像宰割的网络模型和数据加强办法,无效利用标注数据来解决医学畛域标注数据有余的问题。U 型网络结构也用于提取上下文和地位信息。

• –data_url:数据集输出门路。• –run_eval:True 示意训练过程中同时进行验证。训练日志:============== Starting Training ==============

img shape: (1800, 1800, 3) mask shape (1800, 1800)

step: 1, loss is 2.0795505, fps is 0.01587404091683707

step: 2, loss is 2.07847, fps is 25.585164851923018

step: 3, loss is 2.0777602, fps is 34.00713498086528

step: 4, loss is 2.0772874, fps is 35.27263247302604

step: 5, loss is 2.0767062, fps is 36.21961624151569

step: 236, loss is 0.03733757, fps is 35.12729160908855

step: 237, loss is 0.027148828, fps is 35.399451407351144

step: 238, loss is 0.030170249, fps is 35.72904456862478

step: 239, loss is 0.049450595, fps is 36.01899576631429

step: 240, loss is 0.031540662, fps is 35.64675386485131

epoch: 5, avg loss:0.0373, total cost: 138.932 s, per step fps:1.727

epoch: 5, dice_coeff: 0.9967254781174716

End training, the best dice_coeff is: 0.9980154385637104, the best dice_coeff epoch is 1

============== End Training ==============
通过 5 轮的训练,图像宰割模型已趋近收敛,并已找到最优 Dice 系数(Dice coefficient),阐明模型预测值和标签的最高类似度达到 0.9980。*Dice 系数是一种度量汇合类似度的函数,通常用于计算两个样本的类似度(取值范畴为[0,1])。1.1.4 模型保留如果想在昇腾 AI 处理器上执行推理,能够通过网络定义和 CheckPoint 生成 AIR 格局模型文件。export.py 文件内容如下,可依据理论开发状况进行批改。import argparse

import numpy as np
from mindspore import Tensor, export, load_checkpoint, load_param_into_net, context
from src.unet_medical.unet_model import UNetMedical

from src.unet_nested import NestedUNet, UNet

from src.config import cfg_unet as cfg

from src.utils import UnetEval
parser = argparse.ArgumentParser(description=’unet export’)

parser.add_argument(“–device_id”, type=int, default=0, help=”Device id”)

parser.add_argument(“–batch_size”, type=int, default=1, help=”batch size”)

parser.add_argument(“–ckpt_file”, type=str, required=True, help=”Checkpoint file path.”)

parser.add_argument(‘–width’, type=int, default=572, help=’input width’)

parser.add_argument(‘–height’, type=int, default=572, help=’input height’)

parser.add_argument(“–file_name”, type=str, default=”unet”, help=”output file name.”)

parser.add_argument(‘–file_format’, type=str, choices=[“AIR”, “ONNX”, “MINDIR”], default=’AIR’, help=’file format’)

parser.add_argument(“–device_target”, type=str, choices=[“Ascend”, “GPU”, “CPU”], default=”Ascend”,

help=”device target”)

args = parser.parse_args()
context.set_context(mode=context.GRAPH_MODE, device_target=args.device_target)

if args.device_target == “Ascend”:

context.set_context(device_id=args.device_id)
if name == “__main__”:

if cfg[‘model’] == ‘unet_medical’:

net = UNetMedical(n_channels=cfg[‘num_channels’], n_classes=cfg[‘num_classes’])

elif cfg[‘model’] == ‘unet_nested’:

net = NestedUNet(in_channel=cfg[‘num_channels’], n_class=cfg[‘num_classes’], use_deconv=cfg[‘use_deconv’],

use_bn=cfg[‘use_bn’], use_ds=False)

elif cfg[‘model’] == ‘unet_simple’:

net = UNet(in_channel=cfg[‘num_channels’], n_class=cfg[‘num_classes’])

else:

raise ValueError(“Unsupported model: {}”.format(cfg[‘model’]))

return a parameter dict for model

param_dict = load_checkpoint(args.ckpt_file)

load the parameter into net

load_param_into_net(net, param_dict)

net = UnetEval(net)

input_data = Tensor(np.ones([args.batch_size, cfg[“num_channels”], args.height, args.width]).astype(np.float32))

export(net, input_data, file_name=args.file_name, file_format=args.file_format)
终端运行示例:python export.py –ckpt_file=”./ckpt_0/best.ckpt” –width=960 –height=960 –file_name=”out_model/unet_hw960_bs1″ –file_format=”AIR”
• ckpt_file:ckpt 门路。• width:模型输出尺寸。• height:模型输出尺寸。• file_name:输入文件名。• file_format:输入格局,必须为 [“ONNX”,“AIR”,“MINDIR”]。输入后果:out_model/unet_hw960_bs1.air
将 unet_hw960_bs1.air 模型下载至本地,供后续 MindX SDK 推理试验应用。* 揭示:本阶段完结后请及时进行 Notebook 训练作业,防止资源节约。1.2 MindX SDK 推理 1.2.1 环境筹备基于训练营共享的 CANN6.0.RC1_MindX_Vision3.0.RC3 镜像,购买 ECS,留神该镜像是针对 root 用户配置,咱们的操作根本都在 root 用户下执行。咱们还要批改 bash,具体命令和后果如下。

1.2.2 我的项目介绍本我的项目反对 MindStudio 运行和终端运行。下载我的项目代码 https://alexed.obs.cn-north-4… 将我的项目文件 unet_sdk.zip 上传至华为云 ECS 弹性云服务器 /root/ 目录下,并解压;或者下载到本地电脑,用 MindStudio 关上。将之前 unet_hw960_bs1.air 模型放到 /unet_sdk/model/ 目录下。我的项目文件构造├── unet_sdk

├── README.md

├── data // 数据集

│ ├── 1

│ │ ├──image.png // 图片

│ │ ├──mask.png // 标签

│ …

├── model

│ ├──air2om.sh // air 模型转 om 脚本

│ ├──xxx.air //air 模型

│ ├──xxx.om //om 模型

│ ├──aipp_unet_simple_opencv.cfg // aipp 文件

├── pipeline

│ ├──unet_simple_opencv.pipeline // pipeline 文件

├── main.py // 推理文件

├── run.sh // 执行文件

├── requirements.txt // 须要的三方库
1.2.3 模型转换将 unet_hw960_bs1.air 模型转为昇腾 AI 处理器反对的.om 格局离线模型,此处模型转换须要用到 ATC 工具。昇腾张量编译器(Ascend Tensor Compiler,简称 ATC)是昇腾 CANN 架构体系下的模型转换工具,它能够将开源框架的网络模型或 Ascend IR 定义的单算子形容文件(json 格局)转换为昇腾 AI 处理器反对的.om 格局离线模型。模型转换过程中能够实现算子调度的优化、权值数据重排、内存应用优化等,能够脱离设施实现模型的预处理。ATC 参数概览:https://support.huaweicloud.c…

运行脚本:cd unet_sdk/model/ # 切换至模型存储目录

atc –framework=1 –model=unet_hw960_bs1.air –output=unet_hw960_bs1 –input_format=NCHW –soc_version=Ascend310 –log=error –insert_op_conf=aipp_unet_simple_opencv.cfg
留神 air 模型转 om 只反对动态 batch,这里 batchsize=1。参数阐明:• framework:原始框架类型。• model:原始模型文件门路与文件名。• output:转换后的离线模型的门路以及文件名。• input_format:输出数据格式。• soc_version:模型转换时指定芯片版本。• log:显示日志的级别。• insert_op_conf:插入算子的配置文件门路与文件名,这里应用 AIPP 预处理配置文件,用于图像数据预处理。输入后果:ATC run success,示意模型转换胜利,失去 unet_hw960_bs1.om 模型。

模型转换胜利之后,能够应用 MindX SDK mxVision 运行脚本,在 Ascend 310 上进行推理。1.2.4 MindX SDK mxVision 执行推理 MindX SDK 文档请参考:https://support.huaweicloud.c… SDK 执行推理的业务流程:通过 stream 配置文件,Stream manager 可辨认须要构建的 element 以及 element 之间的连贯关系,并启动业务流程。Stream manager 对外提供接口,用于向 stream 发送数据和获取后果,帮忙用户实现业务对接。plugin 示意业务流程中的根底模块,通过 element 的串接构建成一个 stream。buffer 用于外部挂载解码前后的视频、图像数据,是 element 之间传递的数据结构,同时也容许用户挂载元数据(Metadata),用于寄存结构化数据(如指标检测后果)或过程数据(如缩放后的图像)。

MindX SDK 根底概念介绍:

MindX SDK 根底插件

MindX SDK 业务流程编排:Stream 配置文件以 json 格局编写,用户必须指定业务流名称、元件名称和插件名称,并依据须要,补充元件属性和上游元件名称信息。以下表格为本试验 pipeline/unet_simple_opencv.pipeline 文件及其对应的名称及形容:

pipeline/unet_simple_opencv.pipeline 文件内容如下,可依据理论开发状况进行批改。{

"unet_mindspore": {        
    "stream_config": {"deviceId": "0"},
    "appsrc0": {
        "props": {"blocksize": "4096000"},
        "factory": "appsrc",
        "next": "mxpi_imagedecoder0"
    },
    "mxpi_imagedecoder0": {
        "props": {
            "cvProcessor": "opencv",
            "outputDataFormat": "BGR"
        },
        "factory": "mxpi_imagedecoder",
        "next": "mxpi_imagecrop0"
    },
    "mxpi_imagecrop0": {
        "props": {
            "cvProcessor": "opencv",
            "dataSource": "ExternalObjects"
        },
        "factory": "mxpi_imagecrop",
        "next": "mxpi_imageresize0"
    },
    "mxpi_imageresize0": {
        "props": {
            "handleMethod": "opencv",
            "resizeType": "Resizer_Stretch",
            "resizeHeight": "960",
            "resizeWidth": "960"
        },
        "factory": "mxpi_imageresize",
        "next": "mxpi_tensorinfer0"
    },
    "mxpi_tensorinfer0": {
        "props": {
            "dataSource": "mxpi_imageresize0",
            "modelPath": "model/unet_hw960_bs1_AIPP.om"
        },
        "factory": "mxpi_tensorinfer",
        "next": "mxpi_dumpdata0"
    },
    "mxpi_dumpdata0": {
        "props": {"requiredMetaDataKeys": "mxpi_tensorinfer0"},
        "factory": "mxpi_dumpdata",
        "next": "appsink0"
    },
    "appsink0": {
        "props": {"blocksize": "4096000"},
        "factory": "appsink"
    }
}

}
批改 modelPath 关上 pipeline/unet_simple_opencv.pipeline 文件,将 ”mxpi_tensorinfer0″ 元件的属性 ”modelPath”(模型导入门路)批改为模型转换后保留的 om 模型 ”model/unet_hw960_bs1.om”。批改后果:”modelPath”: “model/unet_hw960_bs1.om”
modelPath 批改实现之后,保留 pipeline/unet_simple_opencv.pipeline 文件。StreamManagerApi:StreamManagerApi 文档请参考:https://support.huaweicloud.c… 用于对 Stream 流程的根本治理:加载流程配置、创立流程、向流程发送数据、取得执行后果、销毁流程。本试验用到的 StreamManagerApi 有:• InitManager:初始化一个 StreamManagerApi。• CreateMultipleStreams:依据指定的配置创立多个 Stream。• SendData:向指定 Stream 上的输出元件发送数据(appsrc)。• GetResult:取得 Stream 上的输入元件的后果(appsink)。• DestroyAllStreams:销毁所有的流数据。main.py 文件内容如下,可依据理论开发状况进行批改。import argparse
import base64
import json
import os

import cv2
import numpy as np
from StreamManagerApi import *
import MxpiDataType_pb2 as MxpiDataType

x0 = 2200 # w:2200~4000; h:1000~2800
y0 = 1000
x1 = 4000
y1 = 2800
ori_w = x1 – x0
ori_h = y1 – y0

def _parse_arg():

parser = argparse.ArgumentParser(description="SDK infer")
parser.add_argument("-d", "--dataset", type=str, default="data/",
                    help="Specify the directory of dataset")
parser.add_argument("-p", "--pipeline", type=str,
                    default="pipeline/unet_simple_opencv.pipeline",
                    help="Specify the path of pipeline file")
return parser.parse_args()

def _get_dataset(dataset_dir):

img_ids = sorted(next(os.walk(dataset_dir))[1])
for img_id in img_ids:
    img_path = os.path.join(dataset_dir, img_id)
    yield img_path

def _process_mask(mask_path):

# 手动裁剪
mask = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE)[y0:y1, x0:x1]
return mask

def _get_stream_manager(pipeline_path):

stream_mgr_api = StreamManagerApi()
ret = stream_mgr_api.InitManager()  #初始化 stream
if ret != 0:
    print(f"Failed to init Stream manager, ret={ret}")
    exit(1)

with open(pipeline_path, 'rb') as f:
    pipeline_content = f.read()

ret = stream_mgr_api.CreateMultipleStreams(pipeline_content)  # 创立 stream
if ret != 0:
    print(f"Failed to create stream, ret={ret}")
    exit(1)
return stream_mgr_api

def _do_infer_image(stream_mgr_api, image_path):

stream_name = b'unet_mindspore'  # 与 pipeline 中 stream name 统一
data_input = MxDataInput()
with open(image_path, 'rb') as f:
    data_input.data = f.read()

# 插入抠图的性能,扣 1800*1800 大小
roiVector = RoiBoxVector()
roi = RoiBox()
roi.x0 = x0
roi.y0 = y0
roi.x1 = x1
roi.y1 = y1
roiVector.push_back(roi)
data_input.roiBoxs = roiVector

unique_id = stream_mgr_api.SendData(stream_name, 0, data_input)  # 向指定 Stream 上的输出元件发送数据(appsrc)
if unique_id < 0:
    print("Failed to send data to stream.")
    exit(1)

infer_result = stream_mgr_api.GetResult(stream_name, unique_id)  # 取得 Stream 上的输入元件的后果(appsink)if infer_result.errorCode != 0:
    print(f"GetResult error. errorCode={infer_result.errorCode},"
          f"errorMsg={infer_result.data.decode()}")
    exit(1)
# 用 dumpdata 获取数据
infer_result_data = json.loads(infer_result.data.decode())
content = json.loads(infer_result_data['metaData'][0]['content'])

tensor_vec = content['tensorPackageVec'][0]['tensorVec'][1]  # 1 是 argmax 后果
data_str = tensor_vec['dataStr']
tensor_shape = tensor_vec['tensorShape']
argmax_res = np.frombuffer(base64.b64decode(data_str), dtype=np.float32).reshape(tensor_shape)
np.save("argmax_result.npy", argmax_res)

tensor_vec = content['tensorPackageVec'][0]['tensorVec'][0]  # 0 是 softmax 后果
data_str = tensor_vec['dataStr']
tensor_shape = tensor_vec['tensorShape']
softmax_res = np.frombuffer(base64.b64decode(data_str), dtype=np.float32).reshape(tensor_shape)
np.save("softmax_result.npy", softmax_res)

return softmax_res  # ndarray

自定义 dice 系数和 iou 函数

def _calculate_accuracy(infer_image, mask_image):

mask_image = cv2.resize(mask_image, infer_image.shape[1:3])
mask_image = mask_image / 255.0
mask_image = (mask_image > 0.5).astype(np.int)
mask_image = (np.arange(2) == mask_image[..., None]).astype(np.int)

infer_image = np.squeeze(infer_image, axis=0)
inter = np.dot(infer_image.flatten(), mask_image.flatten())
union = np.dot(infer_image.flatten(), infer_image.flatten()) + \
    np.dot(mask_image.flatten(), mask_image.flatten())

single_dice = 2 * float(inter) / float(union + 1e-6)
single_iou = single_dice / (2 - single_dice)
return single_dice, single_iou

def main(_args):

dice_sum = 0.0
iou_sum = 0.0
cnt = 0
stream_mgr_api = _get_stream_manager(_args.pipeline)
for image_path in _get_dataset(_args.dataset):
    infer_image = _do_infer_image(stream_mgr_api, os.path.join(image_path, 'image.png'))  # 抠图并且 reshape 后的 shape,1hw
    mask_image = _process_mask(os.path.join(image_path, 'mask.png'))  # 抠图后的 shape,hw
    dice, iou = _calculate_accuracy(infer_image, mask_image)
    dice_sum += dice
    iou_sum += iou
    cnt += 1
    print(f"image: {image_path}, dice: {dice}, iou: {iou}")
print(f"========== Cross Valid dice coeff is: {dice_sum / cnt}")
print(f"========== Cross Valid IOU is: {iou_sum / cnt}")
stream_mgr_api.DestroyAllStreams()  # 销毁 stream

if name == “__main__”:

args = _parse_arg()
main(args)

run.sh 文件内容如下,可依据理论开发状况进行批改。参考 SDK 软件包 sample 脚本,须要依照理论门路批改各个环境变量门路。set -e

CUR_PATH=$(cd “$(dirname “$0″)” || {warn “Failed to check path/to/run.sh” ; exit ;} ; pwd)

Simple log helper functions

info() { echo -e “\0331;34m[INFO $1\033[1;37m” ;}
warn() { echo >&2 -e “\0331;31m[WARN $1\033[1;37m” ;}

export MX_SDK_HOME=${CUR_PATH}/../../..

export LD_LIBRARY_PATH=${MX_SDK_HOME}/lib:${MX_SDK_HOME}/opensource/lib:${MX_SDK_HOME}/opensource/lib64:/usr/local/Ascend/ascend-toolkit/latest/acllib/lib64:${LD_LIBRARY_PATH}
export GST_PLUGIN_SCANNER=${MX_SDK_HOME}/opensource/libexec/gstreamer-1.0/gst-plugin-scanner
export GST_PLUGIN_PATH=${MX_SDK_HOME}/opensource/lib/gstreamer-1.0:${MX_SDK_HOME}/lib/plugins

to set PYTHONPATH, import the StreamManagerApi.py

export PYTHONPATH=$PYTHONPATH:${MX_SDK_HOME}/python

python3 main.py
exit 0
运行脚本激活 mxVision 环境变量(本作业无需此步骤):. /root/mxVision/set_env.sh
运行脚本:cd /root/unet_sdk/ # 切换至推理脚本目录
bash run.sh
运行截图如下:

通过 MindStudio 运行,会主动上传代码到预设门路,并执行,运行后果如下:

注意事项:因为 MindX SDK 默认日志级别为 debug,此日志级别下,dump_data 插件会将所有内容打印至终端,影响日志查看。因而能够通过批改日志级别的形式,防止打印不必要的信息。批改日志:vi $MX_SDK_HOME/config/logging.conf
按“i”键批改 Line23 行,日志级别可改为 0 或 1 或 2。# Log level: -1-debug, 0-info, 1-warn, 2-error, 3-fatal
global_level=0
输出“:wq”,保留并退出。* 揭示:试验完结后请及时敞开 / 删除 ECS 弹性云服务器,防止资源节约。1.3 作业总结本作业次要介绍如何应用 MindSpore 框架构建 U -Net 网络模型,应用线上昇腾算力在工业质检的模仿数据集上进行训练,并将保留的模型编译生成适配昇腾 AI 处理器的离线模型,部署在华为云 ECS 上,应用 MindX SDK mxVision 进行推理,从而实现图像宰割的工作。咱们心愿把握 MindSpore 的根底应用,相熟 U -Net 网络和图像宰割的原理常识,把握华为云 ModelArts 和 ECS 的环境搭建,相熟昇腾 910 和昇腾 310 处理器的应用,把握离线模型的转换方法,以及相熟 MindX SDK mxVision 的应用。此外,在应用中发现即便应用 Windows 版本的 MindStudio 近程连贯服务器,依然有卡顿,通过查看工作管理器,发现其占用的 CPU、内存、硬盘和网络资源远超其余利用,思考到我的电脑能够晦涩运行 PyCharm、Intellij IDEA、Clion 等相似软件,且为本地运行,相比之下,近程连贯的 MindStudio 反而卡顿,集体认为应该还是 MindStudio 本身资源占用大,优化有余而导致的安置,这一点,从宏大的安装包也能窥探一二。期待 MindStudio 将来能有更好的优化。此外,倡议 MindStudio 能将 ssh 近程连贯服务器放在菜单栏醒目地位,不便查找。

正文完
 0