• [技术干货] WeAutomate Studio【控件】【UI自动化—基于OCR的自动化—本地OCR】【Tesseract】
    一、【使用官方ocr模型】1. 自行下载Tesseract-OCR,安装包下载地址:https://digi.bib.uni-mannheim.de/tesseract/?C=M;O=D ,类似'tesseract-ocr-w64-setup-v5.1.0.20220510.exe';见图1;图12. 安装目录设置环境变量,比如Path中新增‘D:/Tesseract-OCR’;见图2;图23. 设置系统变量名TESSDATA_PREFIX,变量值为'D:/Tesseract-OCR/tessdata';见图3;图34. 默认只有英文语言包,所以下载需要的语言包放入到第3点中的目录下,语言包下载地址:https://github.com/tesseract-ocr/tessdata ,例如‘chi_sim.traineddata’就是中文语言包;见图4;图45. 若出现”no module named 'pytesseract'“,请到studio安装目录下的python文件夹下,进入cmd命令行黑窗口,输入python -m pip install pytesseract。见图5图5二、【自行训练模型】:1. 配置java环境,至少jdk-8及以上,下载地址:https://www.oracle.com/java/technologies/downloads/#jdk18-windows ;见图6;图62. 下载jTessBoxEditor软件:https://sourceforge.net/projects/vietocr/files/jTessBoxEditor/ ;见图7;图73.  修改环境变量:例如,需要将【使用官方ocr模型】的第2、3点中的路径分别改为‘D:\jTessBoxEditor\tesseract-ocr’和‘D:\jTessBoxEditor\tesseract-ocr\tessdata’;见图8、9;图8图94.  在安装目录中找到train.bat双击打开jTessBoxEditor;见图10;图105.  在jTessBoxEditor界面上的Trainer标签页,见图11;图11   (1)Tesseract Executables设置为例如‘D:\jTessBoxEditor\tesseract-ocr\tesseract.exe’;   (2)Training Data设置为需要识别的图片,png或jpg或tif等;   (3)Language自定义,代表训练的模型包名,例如‘chi_sim_new’,即chi_sim_new.traineddata文件名;   (4)Bootstrap Language指用什么语言模型来识别你的图片,例如此处为官方的‘chi_sim’中文训练模型名;            注意:默认只含有英文包eng.traineddata,需要自行下载中文包chi_sim.traineddata,请看上面第一大点【使用官方ocr模型】中第4点讲解。   (5)找到‘RTL’右侧的下拉框,选择‘Make Box File’,之后点击‘Run’;6. 在jTessBoxEditor界面上的Box Editor标签页,见图12;图12  (1)点击‘Open’打开需要识别的图片,即第5点(2)中设置的;  (2)在Box Editor标签下的Box View标签下,结合右侧操作界面,修改识别错误的字体。其中Character为待修改的字体,修改完记得回车;X,Y,W,H分別为字体周围绿色框的横纵坐标,宽高;Merge,Split,Insert,Delete分别代表合并、分离、插入、删除字体框;  (3)修改完后,点击‘Save’,并点击‘Reload’;7. 回到jTessBoxEditor界面上的Trainer标签页,见图13;图13(1)只需选择第5点(5)中的下拉框的值为‘Train with Existing Box’,再点击‘Run’(2)找到同图片路径下生成的tessdata文件夹下的.traineddata(自己训练的模型),例如此次设置的chi_sim_new.traineddata,把此文件放入此次假设的jTessBoxEditor软件安装目录相关路径中:‘D:\jTessBoxEditor\tesseract-ocr\tessdata’8. 控件中的‘识别语言类型’参数,输入此次训练的模型名,例如chi_sim_new,若想结合官方提供的训练模型一起识别图片,直接用+号连接,比如中文模型chi_sim,则可在参数中填入chi_sim+chi_sim_new,见图14;       【简单建议】:直接输入你自己训练的模型名,不要联合官方的,正确率更高,除非你训练模型的图片够多,够好;       【深入理由解释】:联合官方的模型,会导致你的模型过拟合,除非你训练模型的数据集够大够好。图14
  • [其他] 浅谈常见聚类算法
    K-means 聚类算法K-means 聚类算法 可能是大家最为熟悉的聚类算法。它在许多的工业级数据科学和机器学习课程中都有被讲解。并且容易理解和实现相应功能的代码 。    我们先确定要聚类的数量,并随机初始化它们各自的中心点。为了确定要聚类的数量,最好快速查看数据并尝试识别任何不同的分组。中心点是与每个数据点向量长度相同的向量,是上图中的“x”。通过计算当前点与每个组中心之间的距离,对每个数据点进行分类,然后归到与距离最近的中心的组中。基于迭代后的结果,计算每一类内,所有点的平均值,作为新簇中心。迭代重复这些步骤,或者直到组中心在迭代之间变化不大。您还可以选择随机初始化组中心几次,然后选择看起来提供最佳结果。Mean-Shift 聚类Mean-shift 聚类是一个基于滑窗的算法,尝试找到数据点密集的区域。它是一个基于质心的算法,也就是说他的目标是通过更新中心点候选者定位每个组或类的中心点,将中心点候选者更新为滑窗内点的均值。这些候选滑窗之后会在后处理阶段被过滤,来减少临近的重复点,最后形成了中心点的集合和他们对应的组。DBSCAN 笑脸聚类    DBSCAN 从一个任意的还没有被访问过的启动数据点开始。用一个距离 epsilon ε 将这个点的邻域提取出来(所有再距离 ε 内的点都视为邻居点)。如果在邻域内有足够数量的点(根据 minPoints) ,那么聚类过程开始,并且当前数据点变成新集群中的第一个点。否则,该点将被标记为噪声(之后这个噪声点可能会变成集群中的一部分)。在这两种情况中的点都被标记为”已访问“。对于这个新集群中的第一个点,在它 ε 距离邻域内的点已将变成相同集群中的一部分。这个让所有在 ε 邻域内的点都属于相同集群的过程在之后会一直被重复做,直到所有新点都被加进集群分组中。第 2,3 步的过程会一直重复直到集群内所有点都被确定,即所有在 ε 邻域内的点都被访问且被打上标签。我们在当前集群做完这些,一个新的未被访问的点会被提取并处理,从而会接着发现下一个集群或噪声。这个过程反复进行直到所有的点都被编辑为已访问。既然在最后所有的点都被访问,那么每个点都被标记为属于一个集群或者是噪声。基于高斯混合模型(GMM)的期望最大化(EM)聚类k-means的一个主要缺点是它简单地使用了集群中心的平均值。通过下面的图片,我们可以看到为什么这不是最好的方式。在左手边,人眼可以很明显地看到,有两个半径不同的圆形星团以相同的平均值为中心。k-means不能处理这个问题,因为不同簇的平均值非常接近。当簇不是圆形时,k均值也会失效,这也是将均值用作簇中心的后果。使用GMMs的EM聚类我们首先设定聚类簇的数量(如k-means),然后随机初始化每个集群的高斯分布参数。我们也可以通过快速查看数据来为初始参数提供一个很好的猜测。正如上图所示,这不是100%必要的,因为高斯操作开始时候是非常差的,但很快优化。给定每个簇的高斯分布,计算每个数据点属于特定簇的概率。一个点越靠近高斯中心,它就越可能属于该簇。这应该是直观的,因为对于高斯分布,我们假设大多数数据都靠近集群的中心。基于这些概率,我们为高斯分布计算了一组新的参数,这样我们就可以最大化群集中数据点的概率。我们使用数据点位置的加权和计算这些新参数,其中权重是属于特定集群的数据点的概率。为了以可视化的方式解释这一点,我们可以查看上面的图形,特别是以黄色集群为例。在第一次迭代中,分布是随机开始的,但是我们可以看到大多数黄点都在分布的右边。当我们计算一个由概率加权的和时,即使在中心附近有一些点,但大多数都在右边。因此,分布的平均值很自然地移近这些点集。我们还可以看到,大多数点是“从右上到左下”。因此,标准偏差会发生变化,以创建一个更适合这些点的椭圆,以便最大化概率加权的和。第2步和第3步重复进行,直到收敛,也就是在收敛过程中,迭代变化不大。凝聚层次聚类凝聚层次聚类算法实际上分为 2 类:自上而下或自下而上。自下而上算法在一开始将每个数据点当作一个单个集群对待,然后逐步的合并(或凝聚)成对的集群,直到所有的集群被合并到一个集群中,这个集群包含所有的点。自下而上层次聚类因此被叫做层次凝聚的聚类或者 HAC。这个聚类的层次被表示为一棵树(或者树状图)。树根是唯一的集群,他聚集了所有的样本,叶子是只有一个样本的集群。
  • [干货汇总] 基于华为云ModelArts的水表读数识别开发实践
    >【摘要】 这是水表读数识别项目,实现了如何端到端完成水表读数识别项目。涉及领域包括图像分类、语义分割、OCR文本检测、OCR文本识别。 本文分享自华为云社区《[基于华为云ModelArts的水表读数识别开发实践【华为云至简致远】](https://bbs.huaweicloud.com/blogs/358446)》,作者: Tianyi_Li 。 # 水表读数识别 **项目简介:** 这里实现了如何端到端完成水表读数识别项目。涉及领域包括图像分类、语义分割、OCR文本检测、OCR文本识别。 本案例提供的方法较多,涉及多个模型,但不需要运行所有的模型。如果都测试一遍,默认创建的5GB磁盘规格可能不够用。 **磁盘空间不够时可以将不需要的数据和模型文件删除,腾出空间;或者创建NoteBook时将磁盘规格增加到10GB。** **解决方案流程:** 一、使用语义分割或者OCR文本检测算法识别水表读数所在的四边形区域,并对四边形进行仿射变换转换成矩形,保存成新的图片数据。 二、如果文本区域的图片翻转严重,那第一步抠图生成的数据可能会有180度的翻转。因为文本识别的算法对翻转180度的场景效果不佳,所以再训练一个识别文本翻转的分类模型(本案例图片旋转角度微小,不需要此步骤,内容可供参考)。 三、利用步骤一中检测并抠图出来的文本数据训练OCR文本识别算法,识别图片中的文本内容,即数字。 数据集为华为云AI Gallery上提供的数据集。详情可参见本案例的关联资产。 **下载该项目依赖脚本:** ``` !wget --no-check-certificate https://modelarts-cnnorth4-market.obs.cn-north-4.myhuaweicloud.com/moxing-apps/notebooks/WaterMeter_Identification/deps.zip -O deps.zip !unzip -qo deps.zip !rm -f deps.zip !ls ``` ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654828290640769180.png) 该案例效果图如下 ``` import cv2 import matplotlib.pyplot as plt plt.figure(figsize=(10, 10)) plt.imshow(cv2.imread('./imgs/result1.PNG')) plt.show() ``` ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654828313147600490.png) # 1.准备环境 **请在本页面的右上角确认您所选择的kernel是PyTorch-1.4,选择的规格是GPU,如下图所示:** ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654830674303482240.png) 如果没有V100,也可以选择P100,如图所示,看看配置: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654830683253159783.png) **运行时依赖** - moxing>=2.0.1 - torch>=1.4 - 1.4.0>mmcv-full>=1.3.8 (如果环境中没有mmcv-full,安装可能会消耗10几分钟) - tensorboard - 将环境中的pycocotools替换成mmpycocotools - 其他依赖安装 - lanms安装 ``` !pip uninstall -y moxing !pip install --upgrade pip !pip install "mmcv-full>=1.3.8,=1.4.0" -I !wget --no-check-certificate https://modelarts-cnnorth1-market-dataset.obs.cn-north-1.myhuaweicloud.com/moxing-apps/packages/moxing-2.0.1.rc0.7ce21509-py2.py3-none-any.whl -O moxing-2.0.1rc0-py2.py3-none-any.whl !pip install moxing-2.0.1rc0-py2.py3-none-any.whl !pip install future tensorboard !pip uninstall -y pycocotools !pip install mmpycocotools !pip install --upgrade opencv-python !pip install --ignore-installed imageio !pip install imgaug !pip install prettytable lmdb Polygon3 rapidfuzz pyclipper iopath yacs submitit ``` ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654830718296605112.png) lanms安装: ``` !wget http://repo.myhuaweicloud.com/repository/pypi/packages/96/c0/50dc2c857ed060e907adaef31184413a7706e475c322236d346382e45195/lanms-1.0.2.tar.gz !tar -zxf lanms-1.0.2.tar.gz # 解压到lanms-1.0.2文件夹 %cd lanms-1.0.2 #将Makefile文件第一行的内容修改为:CXXFLAGS = -I include -std=c++11 -O3 -I/home/ma-user/anaconda3/include/python3.6m/ with open('Makefile', "r+") as f: read_data = f.read() f.seek(0) f.truncate() #清空文件 f.write(read_data.replace('$(shell python3-config --cflags)', '-I/home/ma-user/anaconda3/include/python3.6m/')) !python setup.py install import sys sys.path.insert(0, '/home/ma-user/work/lanms-1.0.2') %cd ../ ``` # 2.准备数据 ## 2.1.数据下载 可以登录https://www.datafountain.cn/competitions/480/datasets进行下载(需要报名参赛才可以下载)。 或者使用备用下载地址下载并解压: ``` !wget https://modelarts-cnnorth1-market-dataset.obs.cn-north-1.myhuaweicloud.com/dataset-apps/water_meter/water_meter_from_DataFountain.zip -O ./data.zip !mkdir -p ./data/raw !unzip -qo data.zip -d ./data/raw !rm -f data.zip !ls ./data/raw ``` 数据格式如下(如果数据格式不一致,请处理成一致的格式,放入./data/raw目录下): ``` ./data/raw ├── train_imgs ------------------------ 训练集图片目录 │ ├── train_1.jpg │ ├── ... │ ├── train_1000.jpg ├── train_labels ---------------------- 训练集图片标签目录 │ ├── label说明.docx │ ├── labels │ │ ├── train_1.txt │ │ ├── ... │ │ ├── train_1000.txt ├── test_imgs -------------------------- 测试集图片目录 │ ├── test_1.jpg │ ├── ... │ ├── test_500.jpg ``` train_labels中的txt标签文件说明可参考./data/raw/train_labels/label说明.docx,如果notebook无法打开可以下载到本地再打开。 以其中一个文件内容为例: ``` 95 423 286 319 314 366 126 472 00093 00092 ``` 标签中前八个值,为水表表盘的四个角点(x1, y1, x2, y2, x3, y3, x4, y4),分别为:左上、右上、右下、左下。后面的值为表盘的数值,由于最后一位出现半字符的情况,所以有两个数值,如果没有半字符则只有一个数值。参考如下图片: ``` import cv2 import matplotlib.pyplot as plt plt.figure(figsize=(10, 10)) plt.imshow(cv2.imread('./imgs/label.png')) plt.show() ``` ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654830818840877236.png) ## 2.2.数据切分 为了方便训练模型时观察模型的训练效果,将数据集切分成训练集和验证集,默认切分比例 训练集:验证集=9:1,保存在./data/train.txt和./data/eval.txt。 ``` from util import split_data img_dir = './data/raw/train_imgs' # 原始图片路径 save_dir = './data/' # 切分结果保存路径 split_data(img_dir, save_dir) # 开始切分 print('Finished!') ``` ``` !ls ./data ``` # 3.文本检测 本案例尝试了两种算法识别度数区域。分别是语义分割算法deeplabv3和文本检测算法dbnet。对比之后分割算法结果更佳。 **分割算法和文本检测算法两者选择其一即可,推荐分割算法。** ## 3.1.分割算法(推荐) **与文本检测算法二选一即可。** ### 3.1.1.数据准备 分割算法的数据标签通常为单通道的PNG图片。所以我们需要通过原始的txt标签文件生成PNG图片。保存在./data/segmentation/labels目录下。 ``` from segmentation.data_util import create_labels img_dir = './data/raw/train_imgs/' # 原始图片路径 anno_dir = './data/raw/train_labels/labels' # 原始标注文件路径 save_dir = './data/segmentation/labels' # 分割标签保存路径 create_labels(img_dir, anno_dir, save_dir) # 生成标签 print('Finished!') ``` 标签可视化: ``` import cv2 import numpy as np import matplotlib.pyplot as plt def show(img_path, label_path): # 读取原始图片 img = cv2.imread(img_path) # 读取标签图片 label = cv2.imread(label_path) # 标注结果图片的背景像素值为0,物体的像素值为1,均显示为黑色。 # 为了可视化效果明显,突出物体的标注区域,这里将物体的像素值1(黑色)替换为255(白色)。 label[label==1] = 255 # 原始图片和标签图片像素融合 merge = img * 0.5 + label * 0.5 merge = merge.astype(np.uint8) images_to_observe = [img, label, merge] titles = ['原始图片','标签图片','原始图片+标签图片融合'] # 结果可视化 fig = plt.figure(figsize=(30, 30)) for i in range(len(images_to_observe)): fig.add_subplot(1, len(images_to_observe), i + 1).set_title(titles[i], fontsize=18, color='r') imgplot = plt.imshow(images_to_observe[i]) plt.show() if __name__ == '__main__': # 以其中一张图片为例 img_path = './data/raw/train_imgs/train_10.jpg' label_path = './data/segmentation/labels/train_10.png' show(img_path, label_path) ``` ### 3.1.2.下载预训练模型 执行以下命令下载预训练模型并保存到./pretrained_model/deeplabv3plus_r50-d8.pth: ``` !mkdir -p ./pretrained_model !wget --no-check-certificate https://download.openmmlab.com/mmsegmentation/v0.5/deeplabv3plus/deeplabv3plus_r50-d8_512x512_80k_ade20k/deeplabv3plus_r50-d8_512x512_80k_ade20k_20200614_185028-bf1400d8.pth -O ./pretrained_model/deeplabv3plus_r50-d8.pth ``` ### 3.1.3.训练 训练时加载预训练模型: ``` ./pretrained_model/deeplabv3plus_r50-d8.pth ``` 为了节省训练时间,默认训练1000步,每一步训练8个样本,每200步验证一次精度,每200步保存一次模型。如果要进一步提升精度,可以尝试修改./segmentation/train.py脚本中的配置参数,将训练步数增大。训练好的模型保存在./train_url/segmentation目录下。 训练时默认加载./data/train.txt和./data/eval.txt中保存的样本作为训练集和验证集。请参考./segmentation/train.py。 **单卡训练:** 单卡大约耗时15分钟左右。 ``` !python ./segmentation/train.py \ --num_classes=2 \ --data_url=./data/ \ --eval_data_url=./data/ \ --img_dir=raw/train_imgs \ --ann_dir=segmentation/labels \ --work_dir=./train_url/segmentation \ --load_from=./pretrained_model/deeplabv3plus_r50-d8.pth # num_classes:数据类别数 # data_url:训练集根目录 # eval_data_url:验证集根目录 # img_dir:根目录下存放图片的目录名称 # ann_dir:根目录下存放标签的目录名称 # work_dir:保存输出模型的路径 # load_from:预置模型文件路径 ``` 多卡训练: NoteBook创建多卡环境时可使用,用来加速。 ``` !python -m torch.distributed.launch \ --nproc_per_node=$(/usr/local/nvidia/bin/nvidia-smi -L | wc -l) \ --master_port=7000 \ ./segmentation/train.py \ --launcher=pytorch \ --num_classes=2 \ --data_url=./data \ --eval_data_url=./data \ --img_dir=raw/train_imgs \ --ann_dir=segmentation/labels \ --work_dir=./train_url/segmentation \ --load_from=./pretrained_model/deeplabv3plus_r50-d8.pth # nproc_per_node:每个节点启动的进程数(GPU数) # launcher:分布式启动方式 # num_classes:数据类别数 # data_url:训练集根目录 # eval_data_url:验证集根目录 # img_dir:根目录下存放图片的目录名称 # ann_dir:根目录下存放标签的目录名称 # work_dir:保存输出模型的路径 # load_from:预置模型文件路径 ``` ### 3.1.4.推理+四边形定位+矫正 训练完成后,使用训练好的分割模型对图片进行推理,分割模型返回的推理结果是个2维数组,保存了预测图片每个像素点的标签值。 然后对推理结果做四边形的定位,再将四边形矫正成矩形,最后将矩形区域保存成新的图片数据。这部分数据用来训练后面的文本识别算法。保存在./data/textrecog/images_from_seg路径下。 对原始图片./data/raw/train_imgs进行推理: ``` !python ./segmentation/infer_and_crop.py \ --num_classes=2 \ --img_path=./data/raw/train_imgs \ --checkpoint=./train_url/segmentation/latest.pth \ --save_dir=./data/textrecog/images_from_seg # num_classes:类别数 # img_path:要推理的图片目录 # checkpoint:模型文件 # save_path:推理结果保存路径 ``` 推理结果可视化: ``` from util import show_pair img_dir = './data/raw/train_imgs' # 训练集原始图片 result_dir = './data/textrecog/images_from_seg' # 训练集推理结果 show_pair(img_dir, result_dir, show_num=2) ``` ## 3.2.文本检测算法 与分割算法二选一即可。 ### 3.2.1.数据准备 将数据的元信息保存在json文件中。文本检测的标签默认只有一类text,只要对文本区域进行标注并训练,识别出文本区域即可,和分割算法一样,不需要识别文本中的内容。 训练文件保存在./data/textdet/instances_training.json,验证文件保存在./data/textdet/instances_test.json。 ``` import os from textdet.data_util import create_water_json base_dir = './data' annotation_dir = os.path.join(base_dir, 'raw/train_labels/labels') img_dir = os.path.join(base_dir, 'raw/train_imgs') # train create_water_json(img_dir=img_dir, # 图片路径 anno_dir=annotation_dir, # 标注文件路径 save_file=os.path.join(base_dir, 'textdet/instances_training.json'), # 训练文件保存路径 split=os.path.join(base_dir, 'train.txt')) # 保存训练样本的文件 # eval create_water_json(img_dir=img_dir, # 图片路径 anno_dir=annotation_dir, # 标注文件路径 save_file=os.path.join(base_dir, 'textdet/instances_test.json'), # 验证文件保存路径 split=os.path.join(base_dir, 'eval.txt')) # 保存验证样本的文件 ``` 生成的json文件格式如下: ``` { 'images': [{'file_name': 'train_1.jpg', 'height': 960, 'width': 540, 'segm_file': None, 'id': 0}, {'file_name': 'train_2.jpg', 'height': 540, 'width': 960, 'segm_file': None, 'id': 1}, ...]}, 'categories': [{'id': 1, 'name': 'text'}], 'annotations': [{'iscrowd': 0, 'category_id': 1, 'bbox': [126, 283, 241, 147], 'area': 13363, 'segmentation': [[126, 379, 345, 283, 367, 333, 144, 430]], 'image_id': 0, 'id': 0}, {'iscrowd': 0, 'category_id': 1, 'bbox': [430, 159, 204, 67], 'area': 10316, 'segmentation': [[445, 159, 634, 170, 630, 226, 430, 205]], 'image_id': 1, 'id': 1}, ...] } ``` images: - file_name:图片名称 - height: 图片的高 - width: 图片的宽 - id:图片的索引 categories: - id:标签的索引 - name:标签值 annotations: - iscrowd:默认值为0,segmentation使用polygon格式,。 - area: 四边形的面积。 - segmentation:四边形的四个角点的坐标。 - image_id:图片的索引。 - id:文本框的索引 ### 3.2.2.下载预训练模型 ``` !wget --no-check-certificate https://download.pytorch.org/models/resnet50-19c8e357.pth -O ./pretrained_model/resnet50-19c8e357.pth !wget --no-check-certificate https://download.openmmlab.com/mmocr/textdet/dbnet/dbnet_r50dcnv2_fpnc_sbn_2e_synthtext_20210325-aa96e477.pth -O ./pretrained_model/dbnet_r50dcnv2_fpnc_sbn_2e_synthtext.pth ``` ### 3.2.3.训练 训练的数据集、模型、优化器等均已保存在配置文件./textdet/water_meter_textdet_config.py和./textdet/base_config.py中。 base_config.py文件是基础配置,可不必改动。 water_meter_textdet_config.py文件是针对该案例的配置,例如数据路径、预训练模型路径、epoch数、学习率等可直接在此文件中修改。 为了节省时间,默认训练10个epoch,每2个epoch验证一次,保存一次模型。如果精度不够,可以修改water_meter_textdet_config.py中的训练参数。将epoch数调大,可调至30-100之间,epoch越大,训练时间越久。训练的模型保存在./train_url/textdet路径下。 **单卡训练:** 单卡训练较慢,大约耗时15分钟左右。 ``` !python textdet/train.py ./textdet/water_meter_textdet_config.py --work-dir=./train_url/textdet ``` 多卡训练: NoteBook创建多卡环境时可使用,可加速训练。 ``` !python -m torch.distributed.launch \ --nproc_per_node=$(/usr/local/nvidia/bin/nvidia-smi -L | wc -l) \ --master_port=7000 \ textdet/train.py \ ./textdet/water_meter_textdet_config.py \ --launcher=pytorch \ --work-dir=./train_url/textdet # nproc_per_node:每个节点启动的进程数(GPU数) # launcher:分布式启动方式 # work-dir:模型保存的路径 ``` ### 3.2.4.推理 推理生成的结果保存在./data/textdet/results路径下,该路径下包含两个文件夹out_vis_dir和out_txt_dir。 out_vis_dir目录下存放每张图片推理的可视化结果,保存为jpg文件。out_txt_dir目录下存放每张图片推理出的四边形区域的坐标值,保存为txt文件。 ``` !python textdet/infer.py \ ./data/raw/train_imgs \ ./textdet/water_meter_textdet_config.py \ ./train_url/textdet/latest.pth \ --out-dir=./data/textdet/results # 第1个参数: 推理的图片路径 # 第2个参数: 模型的配置文件 # 第3个参数: 训练好的模型文件 # 第4个参数: 推理结果保存路径 ``` out_txt_dir目录下txt文件的内容如下: ``` 393,180,615,177,616,241,394,244,1 ``` 393,180,615,177,616,241,394,244:代表4个角点的坐标,分别是左上角,右上角,右下角,左下角。 1:标签索引,1代表是文本区域。 out_vis_dir目录下为图片,可直接进入./data/textdet/results/out_vis_dir目录下打开图片查看,或者使用下面的可视化接口查看: ``` from util import show img_dir = './data/textdet/results/out_vis_dir' show(img_dir, show_num=3) ``` ### 3.2.5.抠图 根据上面的推理结果,将文本区域抠出来保存成新的图片,用于训练文本识别模型。保存路径为./data/textrecog/images_from_textdet。 ``` from textdet.data_util import cutout img_dir = './data/raw/train_imgs' # 推理时的原始图片路径 cutout(img_dir, ann_dir='./data/textdet/results/out_txt_dir/', # 推理结果txt文件 save_dir='./data/textrecog/images_from_textdet' # 抠图结果保存路径 ) print('Finished') ``` 抠图结果可视化: ``` from util import show_pair img_dir = './data/raw/train_imgs' # 训练集原始图片 result_dir = './data/textrecog/images_from_textdet' # 训练集推理结果 show_pair(img_dir, result_dir, show_num=2) ``` # 4.文本翻转检测(可选) 本案例数据集物体倾斜角度较小,不需要进行翻转识别,如果遇到随机翻转严重的数据集可以参考这部分代码训练一个识别翻转的模型。 文本检测算法中已经将需要识别的文本区域抠图并保存。接下来利用这部分数据训练一个文本识别算法。因为文本区域做了角度的矫正。但是无法区分翻转0度和180度的图片,而文本识别的算法对翻转180度的场景效果不佳。所以我们又训练了一个专门识别翻转的模型。用来做180度翻转的矫正。 ## 4.1.数据准备 可将文本检测抠出来的图片做180度翻转的离线扩充。根据文本检测抠出的图片以及离线扩充后的数据进行人工标注正常或翻转。 **但是本案例图片倾斜角度较小,抠图的结果均为正向,不需要人工标注。原始图片均为正向0度翻转,离线扩充进行180度翻转后的均为180度翻转的图片。** **离线扩充:** 分割模型的精度较高,结果较准确,我们使用分割模型的推理结果进行离线扩充 ``` import os from PIL import Image import moxing as mox def flip_upside_down(img_dir, save_dir): mox.file.make_dirs(save_dir) imgs_list = os.listdir(img_dir) for img_name in imgs_list: img = Image.open(os.path.join(img_dir, img_name)) img.rotate(180).save(os.path.join(save_dir, img_name)) # 图片旋转180度并保存 if __name__ == '__main__': img_dir = './data/textrecog/images_from_seg' flip_save_dir = './data/detect_180/flip_180' # 翻转数据的保存路径,保存在./data/detect_180目录下 flip_upside_down(img_dir, flip_save_dir) # 进行翻转处理 mox.file.copy_parallel(img_dir, './data/detect_180/normal') # 将正常数据拷贝一份到./data/detect_180目录下,用于训练翻转模型 print('Finished!') ``` 离线结果可视化: ``` from util import show img_dir = './data/detect_180/flip_180' show(img_dir, show_num=5) ``` ## 4.2.下载预训练模型 执行以下命令下载模型,模型保存到./pretrained_model/mobilenet_v2.pth。 ``` !wget --no-check-certificate https://download.openmmlab.com/mmclassification/v0/mobilenet_v2/mobilenet_v2_batch256_imagenet_20200708-3b2dc3af.pth -O ./pretrained_model/mobilenet_v2.pth ``` ## 4.3.训练 识别图片翻转可以使用小点的模型,这里我们以mobilenet_v2为例,如果精度不够可以换稍大点的模型。 使用前面下载的预训练模型,./pretrained_model/mobilenet_v2.pth。 为了节省时间,默认训练25个epoch,每一步训练64个样本,每2个epoch验证一次精度,每2个epoch保存一次模型。大约耗时2-3分钟左右。如果想要更高的精度,可以修改./code/detect_180/train.py脚本中的配置参数。或者换大点的模型。模型较小,使用单卡训练即可。多卡训练可参考其他模型的多卡训练。 这里没有将数据切分成训练集和验证集,可自行切分,指定eval_data_url。 ``` !python ./detect_180/train.py \ --num_classes=2 \ --data_url=./data/detect_180 \ --work_dir=./train_url/detect_180 \ --load_from=./pretrained_model/mobilenet_v2.pth #--eval_data_url=./data/detect_180 # num_classes:数据类别数 # data_url:训练集存放路径 # eval_data_url:验证集存放路径 # work_dir:保存输出模型的路径 # load_from:预置模型文件路径 ``` ## 4.4.推理 训练的模型保存在./train_url/detect_180路径下,加载该路径下的模型进行推理。就以训练集路径下的翻转图片为例。 ``` !python ./detect_180/infer.py \ --num_classes=2 \ --img_path=./data/detect_180/flip_180 \ --checkpoint=./train_url/detect_180/latest.pth ``` # 5.文本识别 ## 5.1.数据准备 根据文本检测后的抠图数据和原始的txt标注文件创建文本内容识别的标签文件。 水表中有的度数转到一半,会同时出现两个数字。原始标注文件中也标注了多个label,从实际情况出发,我们选择符合实际情况的标签(如果转到一半,选择小的那个度数)。有一些特殊的图片,需要人工过滤一下:例如train_19.jpg这张图片。 ``` import cv2 import matplotlib.pyplot as plt imgplot = plt.imshow(cv2.imread('./data/raw/train_imgs/train_19.jpg')) plt.show() ``` 该图片的原始标签有四个['00069', '00070', '00060', '00079'],但从实际情况出发,符合条件的应该是00069和 00070。我们选择度数小的那个作为标签值,即00069。 还有几张图片的标注格式与其他标注文件不同,例如train_140.jpg这张图片标注的标签格式是00470.00479,其他文件都是空格分隔00470 00479。这种图片只有几张我们也进行人工过滤。 下面根据原始的标注文件生成文本识别需要的标注文件格式。 ``` from textrecog.data_util import gen_label_file ann_dir = './data/raw/train_labels/labels' # train train_split_path = './data/train.txt' train_save_path = './data/textrecog/train_label.txt' gen_label_file(train_split_path, ann_dir, train_save_path) # eval eval_split_path = './data/eval.txt' eval_save_path = './data/textrecog/eval_label.txt' gen_label_file(eval_split_path, ann_dir, eval_save_path) print('Finished!') print('标注文件内容如下,文本图片名称+文本数字内容') !head -n 5 ./data/textrecog/train_label.txt ``` ## 5.2.下载预训练模型 执行以下命令下载模型,模型保存到./pretrained_model/crnn_academic-a723a1c5.pth。 ``` !wget --no-check-certificate https://download.openmmlab.com/mmocr/textrecog/crnn/crnn_academic-a723a1c5.pth -O ./pretrained_model/crnn_academic-a723a1c5.pth ``` ## 5.3.训练 训练的数据集、模型、优化器等均已保存在配置文件./textrecog/water_meter_textrecog_config.py和./textrecog/base_config.py中。 base_config.py文件是基础配置,可不必改动。 water_meter_textrecog_config.py文件是针对该案例的配置,例如数据路径、预训练模型路径、epoch数、学习率等可直接在此文件中修改。 默认训练50个epoch,精度不够,可以修改water_meter_textrecog_config.py中的训练参数进行调参。 训练生成的模型保存在./train_url/textrecog路径下。大约耗时2-3分钟左右: ``` !python ./textrecog/train.py \ ./textrecog/water_meter_textrecog_config.py \ --work-dir=./train_url/textrecog ``` ## 5.4.推理 生成推理文件列表: 将需要推理的文件名称保存在txt文件中。 ``` import os img_root_path = './data/textrecog/images_from_seg' with open('./data/textrecog/infer_imgs_list.txt', 'w') as f: f.write('\n'.join(os.listdir(img_root_path))) !head -n 5 ./data/textrecog/infer_imgs_list.txt # 查看生成文件的前5行样本 ``` 推理: 利用训练好的模型进行推理并保存推理结果。结果保存在./data/textrecog/infer_results目录下。该目录下会有多个文件和目录生成。我们只需查看out_vis_dir和result.txt即可。 ``` !mkdir ./data/textrecog/infer_results # 创建推理结果的保存路径 !python ./textrecog/infer.py \ ./data/textrecog/images_from_seg \ ./data/textrecog/infer_imgs_list.txt \ ./textrecog/water_meter_textrecog_config.py \ ./train_url/textrecog/latest.pth \ --out_dir=./data/textrecog/infer_results # 第1个参数: 推理的图片根目录 # 第2个参数: 图片根目录下的推理文件列表 # 第3个参数: 模型配置文件 # 第4个参数: 训练好的模型文件 # 第5个参数: 推理结果保存路径 ``` 推理结果可视化: ``` from util import show img_dir = './data/textrecog/infer_results/out_vis_dir' show(img_dir, show_num=5) ``` # 6.端到端识别 以上我们已经完成了水表识别的整个流程。现在我们将上述算法串联在一起,实现端到端的预测。输入数据为原始拍摄的图片。输出为水表的度数。 ## 6.1分割算法 与文本识别算法算法二选一。 如果文本检测部分使用的是分割算法,参考infer_end2end_with_seg.py脚本,模型路径,数据路径都配置在了该脚本中,可修改该脚本。 如果前面的模型或者数据有改动部分,该脚本请同步修改。 ``` !python infer_end2end_with_seg.py \ --num_classes=2 \ --img_path=./data/raw/test_imgs \ --out=./outputs_with_seg \ --detect_180=False # num_classes:类别数 # img_path:推理图片路径 # out:推理结果保存路径 # detect_180:如果训练了识别翻转的模型,设置detect_180=True即可,默认值为False ``` 推理结果可视化: ``` from util import show_pair img_dir = './data/raw/test_imgs' res_dir = './outputs_with_seg' show_pair(img_dir, res_dir, show_num=2) ``` ## 6.2文本识别算法 与分割算法算法二选一。 如果文本检测部分使用的是文本识别算法,参考infer_end2end_with_textdet.py脚本,模型路径,数据路径都配置在了该脚本中,可修改该脚本。 如果前面的模型或者数据有改动部分,该脚本请同步修改。 ``` !python infer_end2end_with_textdet.py \ --num_classes=2 \ --img_path=./data/raw/test_imgs \ --out=./outputs_with_textdet \ --detect_180=False # num_classes:类别数 # img_path:推理图片路径 # out:推理结果保存路径 # detect_180:如果训练了识别翻转的模型,设置detect_180=True即可,默认值为False ``` 推理结果可视化: ``` from util import show_pair img_dir = './data/raw/test_imgs' res_dir = './outputs_with_textdet' show_pair(img_dir, res_dir, show_num=2) ```
  • [其他] 小数据也拥有巨大人工智能潜力
    传统观点认为,尖端人工智能依赖于大量数据,一个国家(或企业)能够获取的数据量是其人工智能进展的关键指标。当前很多人工智能系统确实使用了大量数据,然而,并非所有人工智能系统都需要海量数据作为支撑,小数据也拥有巨大人工智能潜力。在没有大型预标记数据集的情况下,可以利用这些方法训练人工智能系统。 1. 迁移学习(Transfer learning)是一种机器学习方法,可以在数据丰富的环境中学习执行任务,而后将所学知识“迁移”到可用数据少的任务中。这一方法对于解决关联问题标记数据丰富但所研究问题数据不足的情况有很大价值。 2. 数据标记(Data labeling)方法,即从有限的标记数据和大量无标记数据开始,使用一系列方法来理解可用的未标记数据。例如自动生成标记(自动标记)或识别标记重要数据点(主动学习)。 3. 人工数据生成(Artificial data generation)方法,旨在通过创建新数据点或其他相关技术,最大限度地从少量数据中提取更多信息。该方法可以通过对现有数据的小幅更改(如图像分类数据集中裁剪或旋转图像)或其他更复杂的方法,推断可用数据的基础结构并从中进行推测。 4. 贝叶斯方法(Bayesian methods)是机器学习和统计学的一种大类方法,有两个共同特点。首先,该方法明确地将问题先验信息纳入其解决问题的方法中,而其他方法则更倾向于对研究问题做出最少的假设。贝叶斯方法会在数据进一步改进之前合并这些“先验”信息,因此更适合某些数据较为缺乏,但可以采取实用数学形式写出问题相关信息的环境。其次,贝叶斯方法侧重于对其预测的不确定性进行良好校准后的估计。该方法可以更容易地识别数据点,从而极大减少不确定性,在可用数据有限的情况下能发挥很大作用。 5. 强化学习(Reinforcement learning)是一个关于机器学习方法的广义术语,在强化学习中,计算机系统通过反复试验来学习如何与环境进行交互。强化学习通常用于训练游戏系统、机器人和自动驾驶汽车。
  • [其他] 免费体验AI全流程开发
    在使用ModelArts完成AI全流程开发时,端到端过程中,提供免费规格,让您免费体验ModelArts的全流程开发。单击此处进入ModelArts管理控制台,参考如下操作指导体验免费规格的使用。使用场景在AI全流程开发过程中,分为如下几个步骤:数据管理:ModelArts数据管理功能目前免费开放,但是由于数据需存储在OBS中,存储的数据按OBS规则进行计费。算法开发或预置算法:ModelArts官方发布在AI Gallery的算法,均可免费使用。如果您自己在本地开发算法,则不涉及费用。模型训练:在创建训练作业时,可选择免费规格,完成模型训练。导入模型:ModelArts提供的模型导入及管理功能,不收费。部署上线:将导入的模型部署为在线服务时,可选择免费规格(CPU或GPU)的资源,将模型部署为服务。针对之前的ModelArts,如果您想端到端体验AI全流程开发,在“模型训练”和“部署上线”时,都需要付费体验。当前ModelArts提供免费规格后,整个AI开发过程,可端到端免费体验。注意:由于免费规格仅针对ModelArts服务,在使用过程中,需使用OBS存储数据或模型时,会根据OBS计费规则进行计费。免费规格声明(模型训练)免费规格用于使用体验,训练作业会在1小时后自动停止,因此建议设置最大训练时长为1小时。限时免费的规格,性能有限,如果您的数据量较大,或者训练时长会超过1小时,建议选择收费的计算规格用于模型训练。免费规格的资源是有限的,当使用人数较多时,会出现长时间的排队。如果希望获得更佳的体验,请选择付费规格。训练作业功能,仅提供了GPU类型的免费规格。同一个帐号只能在1个训练作业中使用免费规格。等训练作业结束后,其他训练作业可重新使用免费规格。“训练管理”中,只有“创建训练作业”功能支持使用免费规格,“可视化作业”暂时无法使用免费规格进行体验。仅在“华北-北京四”区域提供了免费规格,请注意您使用的区域。免费规格声明(部署上线)免费规格用于使用体验,部署的服务会在1小时后自动停止。如果您还需要使用免费规格继续运行,可重新启动服务,可再运行1小时,1小时后仍然会自动停止。72小时内没有再次启动,会释放资源,请注意文件备份。免费规格的资源是有限的,当使用人数较多时,会出现长时间的排队。如果希望获得更佳的体验,请选择付费规格。使用“部署上线”功能时,可用的免费规格有2种,1个免费CPU规格,1个免费GPU规格。但是两种规格不能同时在一个服务中使用。同一个帐号下,任意一种免费规格只能在1个服务中使用免费规格。如果一个部署上线已使用了一种免费规格,不管是运行中还是停止状态,其他部署上线任务都无法再使用这个免费规格。只有部署为“在线服务”时,支持使用免费规格。“批量服务”和“边缘服务”暂时无法使用免费规格进行体验。使用免费规格的服务,可以通过修改操作,将资源规格修改为收费的规格,也可以修改为另一种免费规格。但是已经在使用收费规格的服务,无法修改为免费规格。仅在“华北-北京四”区域提供了免费规格,请注意您使用的区域。免费体验AI全流程开发如下免费使用的教程,以使用预置算法创建训练作业为例,其他类型的训练作业,操作步骤类似,不再赘述。在开始使用如下端到端流程前,请参考准备工作,完成帐号注册、全局配置等操作。本文重点指导用户如何免费使用,如需自动学习详细操作步骤,可参见快速入门。登录ModelArts管理控制台。参考创建数据集(旧版),创建一个“图像分类”类型的数据集。同时,将需要进行标注的数据,上传至对应的OBS中。您可以使用花卉数据集样例完成体验。参考图像分类,将上传的数据全部完成标注。参考发布数据集,将标注好的数据集发布为可用版本。参考使用已有算法训练模型,上传一个可应用于图像分类的算法,如“ResNet_v1_50”算法,创建训练作业。在“规格”右侧的下拉框中,选择带有“限时免费”标识的规格,用于免费体验。然后单击“下一步”,根据界面提示完成训练作业的创建。选用免费规格时,其对应的计算节点个数,仅支持使用1个节点,无法修改。图2 选择限时免费的规格等待训练作业运行结束,当训练作业的状态变为“运行成功”时,表示模型训练完成。参考从训练中选择元模型,将训练后得到的模型导入ModelArts进行管理。参考部署为在线服务,将导入的模型部署为在线服务。在“计算节点规格”中,选择带有“免费规格”标识的规格,部署在线服务。图3 选择免费规格部署为在线服务在服务管理列表中,使用免费规格创建的服务,名称右侧带“免费”标识,等待服务部署完成,当状态变为“运行中”时,表示服务已部署完成。您可以单击服务名称进入服务详情页面,在“调用指南”查看服务的URL接口,或者进入“预测”页签,上传一张图片,进行预测识别。使用免费规格的服务,将在1个小时后自动停止,如果您还需要使用此服务,可在“在线服务”页面,单击“启动”,系统将使用之前选择的免费规格启动服务。图4 使用免费规格运行的服务
  • [DevKit] 2022鲲鹏DevKit训练营2022/6/2签到
  • [干货汇总] 儿童节,和 AI 一起通关 “超级马里奥兄弟”
    >摘要:六一儿童节,快来训练一款自己的游戏 AI,用代码让马里奥从大反派酷霸王的魔掌里救回桃花公主。 本文分享自华为云社区《[儿童节,和 AI 一起通关 “超级马里奥兄弟”](https://bbs.huaweicloud.com/blogs/357378?utm_source=csdn&utm_medium=bbs-ex&utm_campaign=other&utm_content=content)》,作者:华为云社区精选。 在蘑菇王国,流传着这样一个故事: 某天,操纵着强力魔法的大乌龟酷霸王一族侵略了蘑菇们居住的和平王国。蘑菇一族都被酷霸王变成了岩石、砖块等形状,蘑菇王国即将灭亡。 只有蘑菇王国的桃花公主,才能解开魔法,让蘑菇们苏醒。 然而,她却被大魔王酷霸王所捉住。 为了打倒乌龟一族、救出桃花公主、给蘑菇王国带回和平,水管工马里奥决定站出来,向酷霸王发起挑战。 是的,这就是童年游戏《超级马里奥》的故事。 你是不是仍旧对马里奥这个游戏记忆犹新,是不是仍旧对过关焦头烂额,**六一儿童节,快来训练一款自己的游戏 AI,用代码让马里奥从大反派酷霸王的魔掌里救回桃花公主。** # 当 AI 玩起超级马里奥 基于华为云一站式AI开发平台ModelArts,利用强化学习中的 PPO 算法来玩超级马里奥,对于绝大部分关卡,训练出来的 AI 智能体都可以在 1500 个 episode 内学会过关。 ModelArts 是面向开发者的一站式 AI 平台,支持海量数据预处理及交互式智能标注、大规模分布式训练、自动化模型生成,及端 - 边 - 云模型按需部署能力,可以让 AI 应用开发到商用部署缩短为分钟级别。 就算不懂代码,也可以按照教程案例,通过简单的调参,一步步实现游戏 AI 开发,成为超级马里奥闯关王者。 话不多说,先来看看实际的效果: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/2/1654130948865385270.png) 超级马里奥游戏 AI 的整体开发流程为:**创建马里奥环境 -> 构建 PPO 算法 -> 训练 -> 推理 -> 可视化效果,目前可以在AI Gallery上免费体验。** AI Gallery 是在 ModelArts 的基础上构建的开发者生态社区, 支持算法、模型、数据集、Notebook 案例和技术文章的共享。 下面,童年回忆杀走起。 # PPO 算法科普 因为这个游戏 AI 是基于 PPO 算法来训练的,所以先简单科普一下强化学习算法。PPO 算法有两种主要形式:PPO-Penalty 和 PPO-Clip (PPO2)。在这里,我们讨论 PPO-Clip(OpenAI 使用的主要形式)。 PPO 的主要特点如下: - PPO 属于 on-policy 算法 - PPO 同时适用于离散和连续的动作空间 - 损失函数 PPO-Clip 算法最精髓的地方就是加入了一项比例用以描绘新老策略的差异,通过超参数 ϵ 限制策略的更新步长: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/2/1654131008940762157.png) - 更新策略: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/2/1654131027784192021.png) - 探索策略 PPO 采用随机探索策略。 - 优势函数 表示在状态 s 下采取动作 a,相较于其他动作有多少优势,如果 > 0, 则当前动作比平均动作好,反之,则差 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/2/1654131041320134520.png) 算法主要流程大致如下: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/2/1654131050336952994.png) 看起来有点复杂,不用担心,**即便你不懂这些算法,有了华为云 ModelArts,可以跟着下面的步骤轻松实现超级马里奥游戏 AI 的强化学习。** # 开发步骤 本案例运行环境为 Pytorch-1.0.0,且需使用 GPU 运行,开始之前一定要选择对应的硬件规格。在 ModelArts Jupyter 中,只要点击代码前面的箭头,就能自动运行。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/2/1654131071006400555.png) ## 1. 程序初始化 ### 第 1 步:安装基础依赖 ``` !pip install -U pip !pip install gym==0.19.0 !pip install tqdm==4.48.0 !pip install nes-py==8.1.0 !pip install gym-super-mario-bros==7.3.2 ``` ### 第 2 步:导入相关的库 ``` import os import shutil import subprocess as sp from collections import deque import numpy as np import torch import torch.nn as nn import torch.nn.functional as F import torch.multiprocessing as _mp from torch.distributions import Categorical import torch.multiprocessing as mp from nes_py.wrappers import JoypadSpace import gym_super_mario_bros from gym.spaces import Box from gym import Wrapper from gym_super_mario_bros.actions import SIMPLE_MOVEMENT, COMPLEX_MOVEMENT, RIGHT_ONLY import cv2 import matplotlib.pyplot as plt from IPython import display import moxing as mox ``` ## 2. 训练参数初始化 此处划重点,该部分参数可以自己调整,以训练出更好的效果。 ``` opt={ "world": 1, # 可选大关:1,2,3,4,5,6,7,8 "stage": 1, # 可选小关:1,2,3,4 "action_type": "simple", # 动作类别:"simple","right_only", "complex" 'lr': 1e-4, # 建议学习率:1e-3,1e-4, 1e-5,7e-5 'gamma': 0.9, # 奖励折扣 'tau': 1.0, # GAE参数 'beta': 0.01, # 熵系数 'epsilon': 0.2, # PPO的Clip系数 'batch_size': 16, # 经验回放的batch_size 'max_episode':10, # 最大训练局数 'num_epochs': 10, # 每条经验回放次数 "num_local_steps": 512, # 每局的最大步数 "num_processes": 8, # 训练进程数,一般等于训练机核心数 "save_interval": 5, # 每{}局保存一次模型 "log_path": "./log", # 日志保存路径 "saved_path": "./model", # 训练模型保存路径 "pretrain_model": True, # 是否加载预训练模型,目前只提供1-1关卡的预训练模型,其他需要从零开始训练 "episode":5 } ``` 如果你想选择其他关卡时,记得调整参数 world 和 stage ,这里默认的是第一关。 ## 3. 创建环境 **结束标志:** - 胜利:mario 到达本关终点 - 失败:mario 受到敌人的伤害、坠入悬崖或者时间用完 **奖励函数:** - 得分:收集金币、踩扁敌人、结束时夺旗 - 扣分:受到敌人伤害、掉落悬崖、结束时未夺旗 ``` #创建环境 def create_train_env(world, stage, actions, output_path=None): # 创建基础环境 env = gym_super_mario_bros.make("SuperMarioBros-{}-{}-v0".format(world, stage)) env = JoypadSpace(env, actions) # 对环境自定义 env = CustomReward(env, world, stage, monitor=None) env = CustomSkipFrame(env) return env # 对原始环境进行修改,以获得更好的训练效果 class CustomReward(Wrapper): def __init__(self, env=None, world=None, stage=None, monitor=None): super(CustomReward, self).__init__(env) self.observation_space = Box(low=0, high=255, shape=(1, 84, 84)) self.curr_score = 0 self.current_x = 40 self.world = world self.stage = stage if monitor: self.monitor = monitor else: self.monitor = None def step(self, action): state, reward, done, info = self.env.step(action) if self.monitor: self.monitor.record(state) state = process_frame(state) reward += (info["score"] - self.curr_score) / 40. self.curr_score = info["score"] if done: if info["flag_get"]: reward += 50 else: reward -= 50 if self.world == 7 and self.stage == 4: if (506 = info["x_pos"] = 832 and info["y_pos"] > 127) or ( 832 info["x_pos"] = 1064 and info["y_pos"] 80) or ( 1113 info["x_pos"] = 1464 and info["y_pos"] 191) or ( 1579 info["x_pos"] = 1943 and info["y_pos"] 191) or ( 1946 info["x_pos"] = 1964 and info["y_pos"] >= 191) or ( 1984 info["x_pos"] = 2060 and (info["y_pos"] >= 191 or info["y_pos"] 127)) or ( 2114 info["x_pos"] 2440 and info["y_pos"] 191) or info["x_pos"] self.current_x - 500: reward -= 50 done = True if self.world == 4 and self.stage == 4: if (info["x_pos"] = 1500 and info["y_pos"] 127) or ( 1588 = info["x_pos"] 2380 and info["y_pos"] >= 127): reward = -50 done = True self.current_x = info["x_pos"] return state, reward / 10., done, info def reset(self): self.curr_score = 0 self.current_x = 40 return process_frame(self.env.reset()) class MultipleEnvironments: def __init__(self, world, stage, action_type, num_envs, output_path=None): self.agent_conns, self.env_conns = zip(*[mp.Pipe() for _ in range(num_envs)]) if action_type == "right_only": actions = RIGHT_ONLY elif action_type == "simple": actions = SIMPLE_MOVEMENT else: actions = COMPLEX_MOVEMENT self.envs = [create_train_env(world, stage, actions, output_path=output_path) for _ in range(num_envs)] self.num_states = self.envs[0].observation_space.shape[0] self.num_actions = len(actions) for index in range(num_envs): process = mp.Process(target=self.run, args=(index,)) process.start() self.env_conns[index].close() def run(self, index): self.agent_conns[index].close() while True: request, action = self.env_conns[index].recv() if request == "step": self.env_conns[index].send(self.envs[index].step(action.item())) elif request == "reset": self.env_conns[index].send(self.envs[index].reset()) else: raise NotImplementedError def process_frame(frame): if frame is not None: frame = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY) frame = cv2.resize(frame, (84, 84))[None, :, :] / 255. return frame else: return np.zeros((1, 84, 84)) class CustomSkipFrame(Wrapper): def __init__(self, env, skip=4): super(CustomSkipFrame, self).__init__(env) self.observation_space = Box(low=0, high=255, shape=(skip, 84, 84)) self.skip = skip self.states = np.zeros((skip, 84, 84), dtype=np.float32) def step(self, action): total_reward = 0 last_states = [] for i in range(self.skip): state, reward, done, info = self.env.step(action) total_reward += reward if i >= self.skip / 2: last_states.append(state) if done: self.reset() return self.states[None, :, :, :].astype(np.float32), total_reward, done, info max_state = np.max(np.concatenate(last_states, 0), 0) self.states[:-1] = self.states[1:] self.states[-1] = max_state return self.states[None, :, :, :].astype(np.float32), total_reward, done, info def reset(self): state = self.env.reset() self.states = np.concatenate([state for _ in range(self.skip)], 0) return self.states[None, :, :, :].astype(np.float32) ``` ## 4. 定义神经网络 神经网络结构包含四层卷积网络和一层全连接网络,提取的特征输入 critic 层和 actor 层,分别输出 value 值和动作概率分布。 ``` class Net(nn.Module): def __init__(self, num_inputs, num_actions): super(Net, self).__init__() self.conv1 = nn.Conv2d(num_inputs, 32, 3, stride=2, padding=1) self.conv2 = nn.Conv2d(32, 32, 3, stride=2, padding=1) self.conv3 = nn.Conv2d(32, 32, 3, stride=2, padding=1) self.conv4 = nn.Conv2d(32, 32, 3, stride=2, padding=1) self.linear = nn.Linear(32 * 6 * 6, 512) self.critic_linear = nn.Linear(512, 1) self.actor_linear = nn.Linear(512, num_actions) self._initialize_weights() def _initialize_weights(self): for module in self.modules(): if isinstance(module, nn.Conv2d) or isinstance(module, nn.Linear): nn.init.orthogonal_(module.weight, nn.init.calculate_gain('relu')) nn.init.constant_(module.bias, 0) def forward(self, x): x = F.relu(self.conv1(x)) x = F.relu(self.conv2(x)) x = F.relu(self.conv3(x)) x = F.relu(self.conv4(x)) x = self.linear(x.view(x.size(0), -1)) return self.actor_linear(x), self.critic_linear(x) ``` ## 5. 定义 PPO 算法 ``` def evaluation(opt, global_model, num_states, num_actions,curr_episode): print('start evalution !') torch.manual_seed(123) if opt['action_type'] == "right": actions = RIGHT_ONLY elif opt['action_type'] == "simple": actions = SIMPLE_MOVEMENT else: actions = COMPLEX_MOVEMENT env = create_train_env(opt['world'], opt['stage'], actions) local_model = Net(num_states, num_actions) if torch.cuda.is_available(): local_model.cuda() local_model.eval() state = torch.from_numpy(env.reset()) if torch.cuda.is_available(): state = state.cuda() plt.figure(figsize=(10,10)) img = plt.imshow(env.render(mode='rgb_array')) done=False local_model.load_state_dict(global_model.state_dict()) #加载网络参数\ while not done: if torch.cuda.is_available(): state = state.cuda() logits, value = local_model(state) policy = F.softmax(logits, dim=1) action = torch.argmax(policy).item() state, reward, done, info = env.step(action) state = torch.from_numpy(state) img.set_data(env.render(mode='rgb_array')) # just update the data display.display(plt.gcf()) display.clear_output(wait=True) if info["flag_get"]: print("flag getted in episode:{}!".format(curr_episode)) torch.save(local_model.state_dict(), "{}/ppo_super_mario_bros_{}_{}_{}".format(opt['saved_path'], opt['world'], opt['stage'],curr_episode)) opt.update({'episode':curr_episode}) env.close() return True return False def train(opt): #判断cuda是否可用 if torch.cuda.is_available(): torch.cuda.manual_seed(123) else: torch.manual_seed(123) if os.path.isdir(opt['log_path']): shutil.rmtree(opt['log_path']) os.makedirs(opt['log_path']) if not os.path.isdir(opt['saved_path']): os.makedirs(opt['saved_path']) mp = _mp.get_context("spawn") #创建环境 envs = MultipleEnvironments(opt['world'], opt['stage'], opt['action_type'], opt['num_processes']) #创建模型 model = Net(envs.num_states, envs.num_actions) if opt['pretrain_model']: print('加载预训练模型') if not os.path.exists("ppo_super_mario_bros_1_1_0"): mox.file.copy_parallel( "obs://modelarts-labs-bj4/course/modelarts/zjc_team/reinforcement_learning/ppo_mario/ppo_super_mario_bros_1_1_0", "ppo_super_mario_bros_1_1_0") if torch.cuda.is_available(): model.load_state_dict(torch.load("ppo_super_mario_bros_1_1_0")) model.cuda() else: model.load_state_dict(torch.load("ppo_super_mario_bros_1_1_0",torch.device('cpu'))) else: model.cuda() model.share_memory() optimizer = torch.optim.Adam(model.parameters(), lr=opt['lr']) #环境重置 [agent_conn.send(("reset", None)) for agent_conn in envs.agent_conns] #接收当前状态 curr_states = [agent_conn.recv() for agent_conn in envs.agent_conns] curr_states = torch.from_numpy(np.concatenate(curr_states, 0)) if torch.cuda.is_available(): curr_states = curr_states.cuda() curr_episode = 0 #在最大局数内训练 while curr_episode0 and curr_episode > 0: torch.save(model.state_dict(), "{}/ppo_super_mario_bros_{}_{}_{}".format(opt['saved_path'], opt['world'], opt['stage'], curr_episode)) curr_episode += 1 old_log_policies = [] actions = [] values = [] states = [] rewards = [] dones = [] #一局内最大步数 for _ in range(opt['num_local_steps']): states.append(curr_states) logits, value = model(curr_states) values.append(value.squeeze()) policy = F.softmax(logits, dim=1) old_m = Categorical(policy) action = old_m.sample() actions.append(action) old_log_policy = old_m.log_prob(action) old_log_policies.append(old_log_policy) #执行action if torch.cuda.is_available(): [agent_conn.send(("step", act)) for agent_conn, act in zip(envs.agent_conns, action.cpu())] else: [agent_conn.send(("step", act)) for agent_conn, act in zip(envs.agent_conns, action)] state, reward, done, info = zip(*[agent_conn.recv() for agent_conn in envs.agent_conns]) state = torch.from_numpy(np.concatenate(state, 0)) if torch.cuda.is_available(): state = state.cuda() reward = torch.cuda.FloatTensor(reward) done = torch.cuda.FloatTensor(done) else: reward = torch.FloatTensor(reward) done = torch.FloatTensor(done) rewards.append(reward) dones.append(done) curr_states = state _, next_value, = model(curr_states) next_value = next_value.squeeze() old_log_policies = torch.cat(old_log_policies).detach() actions = torch.cat(actions) values = torch.cat(values).detach() states = torch.cat(states) gae = 0 R = [] #gae计算 for value, reward, done in list(zip(values, rewards, dones))[::-1]: gae = gae * opt['gamma'] * opt['tau'] gae = gae + reward + opt['gamma'] * next_value.detach() * (1 - done) - value.detach() next_value = value R.append(gae + value) R = R[::-1] R = torch.cat(R).detach() advantages = R - values #策略更新 for i in range(opt['num_epochs']): indice = torch.randperm(opt['num_local_steps'] * opt['num_processes']) for j in range(opt['batch_size']): batch_indices = indice[ int(j * (opt['num_local_steps'] * opt['num_processes'] / opt['batch_size'])): int((j + 1) * ( opt['num_local_steps'] * opt['num_processes'] / opt['batch_size']))] logits, value = model(states[batch_indices]) new_policy = F.softmax(logits, dim=1) new_m = Categorical(new_policy) new_log_policy = new_m.log_prob(actions[batch_indices]) ratio = torch.exp(new_log_policy - old_log_policies[batch_indices]) actor_loss = -torch.mean(torch.min(ratio * advantages[batch_indices], torch.clamp(ratio, 1.0 - opt['epsilon'], 1.0 + opt['epsilon']) * advantages[ batch_indices])) critic_loss = F.smooth_l1_loss(R[batch_indices], value.squeeze()) entropy_loss = torch.mean(new_m.entropy()) #损失函数包含三个部分:actor损失,critic损失,和动作entropy损失 total_loss = actor_loss + critic_loss - opt['beta'] * entropy_loss optimizer.zero_grad() total_loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), 0.5) optimizer.step() print("Episode: {}. Total loss: {}".format(curr_episode, total_loss)) finish=False for i in range(opt["num_processes"]): if info[i]["flag_get"]: finish=evaluation(opt, model,envs.num_states, envs.num_actions,curr_episode) if finish: break if finish: break ``` ## 6. 训练模型 训练 10 Episode,耗时约 5 分钟 ``` train(opt) ``` ## 7. 使用模型推理游戏 定义推理函数 ``` def infer(opt): if torch.cuda.is_available(): torch.cuda.manual_seed(123) else: torch.manual_seed(123) if opt['action_type'] == "right": actions = RIGHT_ONLY elif opt['action_type'] == "simple": actions = SIMPLE_MOVEMENT else: actions = COMPLEX_MOVEMENT env = create_train_env(opt['world'], opt['stage'], actions) model = Net(env.observation_space.shape[0], len(actions)) if torch.cuda.is_available(): model.load_state_dict(torch.load("{}/ppo_super_mario_bros_{}_{}_{}".format(opt['saved_path'],opt['world'], opt['stage'],opt['episode']))) model.cuda() else: model.load_state_dict(torch.load("{}/ppo_super_mario_bros_{}_{}_{}".format(opt['saved_path'], opt['world'], opt['stage'],opt['episode']), map_location=torch.device('cpu'))) model.eval() state = torch.from_numpy(env.reset()) plt.figure(figsize=(10,10)) img = plt.imshow(env.render(mode='rgb_array')) while True: if torch.cuda.is_available(): state = state.cuda() logits, value = model(state) policy = F.softmax(logits, dim=1) action = torch.argmax(policy).item() state, reward, done, info = env.step(action) state = torch.from_numpy(state) img.set_data(env.render(mode='rgb_array')) # just update the data display.display(plt.gcf()) display.clear_output(wait=True) if info["flag_get"]: print("World {} stage {} completed".format(opt['world'], opt['stage'])) break if done and info["flag_get"] is False: print('Game Failed') break infer(opt) World 1 stage 1 completed ``` ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/2/1654131275998974944.png) # 最后 六一儿童节,快来华为云 AI Gallery 上体验 AI 闯关超级马里奥,无需考虑计算资源,环境的搭建,在 ModelArts 里跟着运行代码,[点击链接](https://developer.huaweicloud.com/develop/aigallery/usecase/detail?id=3390253c-a00f-4c2a-92b6-08384ec31693)在ModelArts里运行简单几行代码,手把手带你5分钟速成游戏王。
  • [其他] 目标检测的性能上界讨论
    以下文章来源于Smarter ,作者ChenJoyaAbstract  (1)在作者的计算方式下,PASCAL VOC (test2007) 的上界可以达到 91.6%,COCO (val2017) 可以达到 78.2%, OpenImages V4 (val) 可以达到 58.9%,它们的上界离现在所能达到的最佳的性能仍有很大的差距;(2)发现分类错误(混淆/漏检)比定位错误和重复检测错误更加重要;(3)分析了一些数据增强的措施对检测器的影响。分析   1. 实验细节(1)平台:主流检测框架 mmdetection,detectron2 ;(2)模型:R-CNN 系列(FasterRCNN, MaskRCNN, GridRCNN, LibraRCNN, CascadeRCNN, MaskScoringRCNN, GAFasterRCNN, HTC),one-stage/anchor-free系列 (RetinaNet,SSD, FCOS, CenterNet [objects as points]);(3)数据集:PASCAL VOC (训练 07+12, 测试 07test), MS COCO (训练 train2017, 测试 val2017), OpenImages V4 (as Kaggle Competition), Fashion dataset (作者自个的);(4)评价标准:COCO-style AP;2. 如何估计性能上界(1)假定性能上界是由 best object classifier 能达到的,即认为定位问题已经解决;(2)探究实验,利用大网络 ResNet152 对目标进行分类,能得到多少的准确率(top-1 accuracy),发现将 object 单独分离出来效果是最好的;(3)准备基于 recognition accuracy 来估计 AP 上界。设计一种生成 candidate box 的机制,用于生成不同 IoU 的 bounding-boxes 来计算 AP,并且基于这些 bounding-boxes 重新训练 ResNet-152 分类器;(4)对于每一个 bounding-box 分配 ResNet-152 的 top-1 score 所对应的 label;3. 各数据集的上界(1) PASCAL VOC(2)COCO(3)OpenImages V4(4)AP 与识别 accuracy 的关联不同类别识别精度与对应 AP 的关联4. Error Diagnosis(1)定义四种错误类型:(2)对于每种错误,人为地消除后观察其提升,没有了分类方面的错误后,性能提升最明显:5. Invariance Analysis做各种数据处理,观察性能变化。总的来说,FCOS,RetinaNet 属于最优秀的那一档。结论   (1)现在检测器的性能还没有离上界还有一定差距(但似乎在 IoU@0.5 时已经十分接近上界了);(2)检测器的瓶颈在目标识别(recognition)上;(3)检测器缺乏鲁棒性;(4)并没有发现明显的证据说明 context 对于分类目标是有效的。
  • [其他] 适合新手的深度学习综述(7)--训练和优化技术
    本文转载自机器之心。在本节中,我们将简要概述一些主要的技术,用于正则化和优化深度神经网络 (DNN)。7.1 DropoutSrivastava 等人 (2014) 提出 Dropout,以防止神经网络过拟合。Dropout 是一种神经网络模型平均正则化方法,通过增加噪声到其隐藏单元。在训练过程中,它会从神经网络中随机抽取出单元和连接。Dropout 可以用于像 RBM (Srivastava et al.,2014) 这样的图形模型中,也可以用于任何类型的神经网络。最近提出的一个关于 Dropout 的改进是 Fraternal Dropout,用于循环神经网络 (RNN)。7.2 MaxoutGoodfellow 等人 (2013) 提出 Maxout,一种新的激活函数,用于 Dropout。Maxout 的输出是一组输入的最大值,有利于 Dropout 的模型平均。7.3 ZoneoutKrueger 等人 (2016) 提出了循环神经网络 (RNN) 的正则化方法 Zoneout。Zoneout 在训练中随机使用噪音,类似于 Dropout,但保留了隐藏的单元而不是丢弃。7.4 深度残差学习He 等人 (2015) 提出了深度残差学习框架,该框架被称为低训练误差的 ResNet。7.5 批归一化Ioffe 和 Szegedy(2015) 提出了批归一化,通过减少内部协变量移位来加速深度神经网络训练的方法。Ioffe(2017) 提出批重归一化,扩展了以前的方法。7.6 DistillationHinton 等人 (2015) 提出了将知识从高度正则化模型的集合 (即神经网络) 转化为压缩小模型的方法。7.7 层归一化Ba 等人 (2016) 提出了层归一化,特别是针对 RNN 的深度神经网络加速训练,解决了批归一化的局限性。
  • [其他] 适合新手的深度学习综述(1)
    本文转载自机器之心。1. 引言 「深度学习」(DL)一词最初在 1986 被引入机器学习(ML),后来在 2000 年时被用于人工神经网络(ANN)。深度学习方法由多个层组成,以学习具有多个抽象层次的数据特征。DL 方法允许计算机通过相对简单的概念来学习复杂的概念。对于人工神经网络(ANN),深度学习(DL)(也称为分层学习(Hierarchical Learning))是指在多个计算阶段中精确地分配信用,以转换网络中的聚合激活。为了学习复杂的功能,深度架构被用于多个抽象层次,即非线性操作;例如 ANNs,具有许多隐藏层。用准确的话总结就是,深度学习是机器学习的一个子领域,它使用了多层次的非线性信息处理和抽象,用于有监督或无监督的特征学习、表示、分类和模式识别。深度学习即表征学习是机器学习的一个分支或子领域,大多数人认为近代深度学习方法是从 2006 开始发展起来的。本文是关于最新的深度学习技术的综述,主要推荐给即将涉足该领域的研究者。本文包括 DL 的基本思想、主要方法、最新进展以及应用。综述论文是非常有益的,特别是对某一特定领域的新研究人员。一个研究领域如果在不久的将来及相关应用领域中有很大的价值,那通常很难被实时跟踪到最新进展。现在,科学研究是一个很有吸引力的职业,因为知识和教育比以往任何时候都更容易分享和获得。对于一种技术研究的趋势来说,唯一正常的假设是它会在各个方面有很多的改进。几年前对某个领域的概述,现在可能已经过时了。考虑到近年来深度学习的普及和推广,我们简要概述了深度学习和神经网络(NN),以及它的主要进展和几年来的重大突破。我们希望这篇文章将帮助许多新手研究者在这一领域全面了解最近的深度学习的研究和技术,并引导他们以正确的方式开始。同时,我们希望通过这项工作,向这个时代的顶级 DL 和 ANN 研究者们致敬:Geoffrey Hinton(Hinton)、Juergen Schmidhuber(Schmidhuber)、Yann LeCun(LeCun)、Yoshua Bengio(Bengio)和许多其他研究学者,他们的研究构建了现代人工智能(AI)。跟进他们的工作,以追踪当前最佳的 DL 和 ML 研究进展对我们来说也至关重要。在本论文中,我们首先简述过去的研究论文,对深度学习的模型和方法进行研究。然后,我们将开始描述这一领域的最新进展。我们将讨论深度学习(DL)方法、深度架构(即深度神经网络(DNN))和深度生成模型(DGM),其次是重要的正则化和优化方法。此外,用两个简短的部分对于开源的 DL 框架和重要的 DL 应用进行总结。我们将在最后两个章节(即讨论和结论)中讨论深入学习的现状和未来。
  • [其他] 目标检测 | Anchor free之CenterNet深度解析(3)
    文章来源于AI算法修炼营 ,作者周威Encode 前面提到过Encode的过程是将ground-truth bounding box信息映射为类似网络输出的格式。这样可以加速求解损失函数的计算。我们知道在CornerNet中将检测框的左上角点和右下角点映射到heatmap上的过程,并不是简单的一一对应关系的(也就是将原图中的某关键点映射到heatmap中的某一关键点中),而是将原图中的某关键点(在CenterNet中为检测框的中点)映射到heatmap中的某一高斯核区域内。如下图4所示,为每个检测框中心点的高斯核区域显示。又或者借用https://zhuanlan.zhihu.com/p/66048276中的图,为某一中心点在heatmap的映射可视化。可以直观地感受其呈现二维高斯分布。那么根据获得的heatmap,我们可以将ground-truth bbox的偏移信息和宽高信息按照该映射关系,等同地映射到前面提到的Offset特征图和Height&Width特征图中,实现整个encode的过程 4.损失函数的设置 实现了encode过程后,设定损失函数就变得非常简单了。4.1 focal loss原论文中令为网络输出的heatmap,为ground_truth信息,即heatmap的标签/监督信息。类似CornerNet使用focal loss进行损失函数设定,实现过程如下这里的和为focal loss的超参数,N是图片中关键点的个数。4.2 offset loss为了弥补由于stride的原因造成的偏移误差,论文中设定了一个关于偏移的损失函数,使得训练后的网络能够有效计算offset值,从而修正检测框的位置。不妨这里引用一下论文中的offset loss公式。这里的p是检测框中心点(原图中)的真实坐标,p/R是理论上该中心点映射到特征图的准确位置区域(很可能是浮点型)。但是我们知道在特征图中,所有的点的位置都是整型的(即不存在某一个点的位置为(1.1,2.9)的),所以实际上,原图中坐标为p的点映射到特征图后的位置应该是是p向下取整的结果,所以这里就造成了误差了,那么这个误差就是公式中的是网络的offset输出特征图。那么这个指的是关键点实际落入的区域。说明该offset loss只关注在关键点区域的offset输出。
  • [其他] 目标检测 | Anchor free之CenterNet深度解析(2)
    文章来源于AI算法修炼营 ,作者周威.检测框获取Decode 前面我们已经知道了CenterNet网络有三个输出,分别为(1) HeatMap,大小为(W/4,H/4,80),输出不同类别(80个类别)物体中心点的位置(2) Offset,大小为(W/4,H/4,2),对HeatMap的输出进行精炼,提高定位准确度(3) Height&Width,大小为(W/4,H/4,2),预测以关键点为中心的检测框的宽高那么如何将这些输出转为直观的检测框信息呢?在目标检测领域,通常将这一过程称为decode,就是根据网络的输出获取直观的检测框信息。那么encode就是将检测框信息(通常为ground-truth bounding box的坐标、宽高信息)转化为形为网络输出的信息,便于网络损失函数的求解。代码中实现decode这一过程的代码如下def _ctdet_decode(hm, reg, wh, k=100, output_stride=4): """将网络的输出转换为标准的检测框信息""" hm = K.sigmoid(hm) hm = _nms(hm) hm_shape = K.shape(hm) reg_shape = K.shape(reg) wh_shape = K.shape(wh) # cat为通道数 batch, width, cat = hm_shape[0], hm_shape[2], hm_shape[3] # 对输出的特征图进行铺平 hm_flat = K.reshape(hm, (batch, -1)) reg_flat = K.reshape(reg, (reg_shape[0], -1, reg_shape[-1])) wh_flat = K.reshape(wh, (wh_shape[0], -1, wh_shape[-1])) def _process_sample(args): _hm, _reg, _wh = args _scores, _inds = tf.math.top_k(_hm, k=k, sorted=True) # 寻找前k个heatmap的值 _classes = K.cast(_inds % cat, 'float32') #获取索引对应的类别 _inds = K.cast(_inds / cat, 'int32') #在某一类别中的位置(最大长度为 width*width),一维的 # 一维位置转二维坐标 _xs = K.cast(_inds % width, 'float32') #二维坐标中的横坐标 _ys = K.cast(K.cast(_inds / width, 'int32'), 'float32') #二维坐标的纵坐标 _wh = K.gather(_wh, _inds) #根据索引获得宽高数据 _reg = K.gather(_reg, _inds) #根据坐标获得offset _xs = _xs + _reg[..., 0] _ys = _ys + _reg[..., 1] _x1 = _xs - _wh[..., 0] / 2 _y1 = _ys - _wh[..., 1] / 2 _x2 = _xs + _wh[..., 0] / 2 _y2 = _ys + _wh[..., 1] / 2 # rescale to image coordinates _x1 = output_stride * _x1 _y1 = output_stride * _y1 _x2 = output_stride * _x2 _y2 = output_stride * _y2 _detection = K.stack([_x1, _y1, _x2, _y2, _scores, _classes], -1) return _detection detections = K.map_fn(_process_sample, [hm_flat, reg_flat, wh_flat], dtype=K.floatx()) return detections主要通过非极大值抑制(NMS)后在heatmap上寻找topk个最大值,即可能为物体中心的索引。然后根据这topk个中心点,寻找其对应的类别、宽高和offset信息。这里的NMS并不像Anchor-free中的NMS(即利用检测框的IOU为距离基准求解极大值,抑制非极大值)。而CenterNet的NMS,是寻找某点与其周围的八个点之间最大值,作为其NMS的极大值。那么该操作可以使用最简单的3x3的MaxPooling实现。实现代码如下:def _nms(heat, kernel=3): hmax = K.pool2d(heat, (kernel, kernel), padding='same', pool_mode='max') keep = K.cast(K.equal(hmax, heat), K.floatx()) return heat * keep貌似该keras代码中,并没有实现训练CenterNet的过程。所以我们没办法结合代码进行训练过程的解析,包括(1)损失函数设定(2)将ground-truth bounding box信息映射为类似网络输出的格式,被称为encode。那么下面直接结合论文进行损失函数与encode的解析。
  • [其他] 我在华为云学院学习:机器学习(随机森林的优缺点)
    随机森林指的是利用多棵树对样本进行训练并预测的一种分类器。该分类器最早由Leo Breiman和Adele Cutler提出,并被注册成了商标。在机器学习中,随机森林是一个包含多个决策树的分类器, 并且其输出的类别是由个别树输出的类别的众数而定。 Leo Breiman和Adele Cutler发展出推论出随机森林的算法。 而 "Random Forests" 是他们的商标。 这个术语是1995年由贝尔实验室的Tin Kam Ho所提出的随机决策森林(random decision forests)而来的。这个方法则是结合 Breimans 的 "Bootstrap aggregating" 想法和 Ho 的"random subspace method"以建造决策树的集合。随机森林的优缺点优点:1.可以用来解决分类和回归问题:随机森林可以同时处理分类和数值特征2.抗过拟合能力:通过平均决策树,降低过拟合的风险性3.只有在半数以上的基分类器出现差错时才会做出错误的预测:随机森林非常稳定,即使数据集中出现了一个新的数据点,整个算法也不会受到过多影响,它只会影响到一颗决策树,很难对所有决策树产生影响缺点:1.据观测,如果一些分类/回归问题的训练数据中存在噪音,随机森林中的数据集会出现过拟合的现象2.比决策树算法更复杂,计算成本更高3.由于其本身的复杂性,它们比其他类似的算法需要更多的时间来训练非常推荐这个学习视频https://education.huaweicloud.com/courses/course-v1:HuaweiX+CBUCNXE086+Self-paced/courseware/45c5a9b65ee348719ddc4f4c3801ad0f/b67e40f52a7447f79364a2de6f2b9398/相关概念1.分裂:在决策树的训练过程中,需要一次次的将训练数据集分裂成两个子数据集,这个过程就叫做分裂。2.特征:在分类问题中,输入到分类器中的数据叫做特征。以上面的股票涨跌预测问题为例,特征就是前一天的交易量和收盘价。3.待选特征:在决策树的构建过程中,需要按照一定的次序从全部的特征中选取特征。待选特征就是在步骤之前还没有被选择的特征的集合。例如,全部的特征是 ABCDE,第一步的时候,待选特征就是ABCDE,第一步选择了C,那么第二步的时候,待选特征就是ABDE。4.分裂特征:接待选特征的定义,每一次选取的特征就是分裂特征,例如,在上面的例子中,第一步的分裂特征就是C。因为选出的这些特征将数据集分成了一个个不相交的部分,所以叫它们分裂特征。
  • [技术干货] 详解TensorFlow训练网络两种方式(转载)
    TensorFlow训练网络有两种方式,一种是基于tensor(array),另外一种是迭代器两种方式区别是:第一种是要加载全部数据形成一个tensor,然后调用model.fit()然后指定参数batch_size进行将所有数据进行分批训练第二种是自己先将数据分批形成一个迭代器,然后遍历这个迭代器,分别训练每个批次的数据方式一:通过迭代器1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283IMAGE_SIZE = 1000 # step1:加载数据集(train_images, train_labels), (val_images, val_labels) = tf.keras.datasets.mnist.load_data() # step2:将图像归一化train_images, val_images = train_images / 255.0, val_images / 255.0 # step3:设置训练集大小train_images = train_images[:IMAGE_SIZE]val_images = val_images[:IMAGE_SIZE]train_labels = train_labels[:IMAGE_SIZE]val_labels = val_labels[:IMAGE_SIZE] # step4:将图像的维度变为(IMAGE_SIZE,28,28,1)train_images = tf.expand_dims(train_images, axis=3)val_images = tf.expand_dims(val_images, axis=3) # step5:将图像的尺寸变为(32,32)train_images = tf.image.resize(train_images, [32, 32])val_images = tf.image.resize(val_images, [32, 32]) # step6:将数据变为迭代器train_loader = tf.data.Dataset.from_tensor_slices((train_images, train_labels)).batch(32)val_loader = tf.data.Dataset.from_tensor_slices((val_images, val_labels)).batch(IMAGE_SIZE) # step5:导入模型model = LeNet5() # 让模型知道输入数据的形式model.build(input_shape=(1, 32, 32, 1)) # 结局Output Shape为 multiplemodel.call(Input(shape=(32, 32, 1))) # step6:编译模型model.compile(optimizer='adam',              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),              metrics=['accuracy']) # 权重保存路径checkpoint_path = "./weight/cp.ckpt" # 回调函数,用户保存权重save_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_path,                                                   save_best_only=True,                                                   save_weights_only=True,                                                   monitor='val_loss',                                                   verbose=0) EPOCHS = 11 for epoch in range(1, EPOCHS):    # 每个批次训练集误差    train_epoch_loss_avg = tf.keras.metrics.Mean()    # 每个批次训练集精度    train_epoch_accuracy = tf.keras.metrics.SparseCategoricalAccuracy()    # 每个批次验证集误差    val_epoch_loss_avg = tf.keras.metrics.Mean()    # 每个批次验证集精度    val_epoch_accuracy = tf.keras.metrics.SparseCategoricalAccuracy()     for x, y in train_loader:        history = model.fit(x,                            y,                            validation_data=val_loader,                            callbacks=[save_callback],                            verbose=0)         # 更新误差,保留上次        train_epoch_loss_avg.update_state(history.history['loss'][0])        # 更新精度,保留上次        train_epoch_accuracy.update_state(y, model(x, training=True))         val_epoch_loss_avg.update_state(history.history['val_loss'][0])        val_epoch_accuracy.update_state(next(iter(val_loader))[1], model(next(iter(val_loader))[0], training=True))     # 使用.result()计算每个批次的误差和精度结果    print("Epoch {:d}: trainLoss: {:.3f}, trainAccuracy: {:.3%} valLoss: {:.3f}, valAccuracy: {:.3%}".format(epoch,                                                                                                             train_epoch_loss_avg.result(),                                                                                                             train_epoch_accuracy.result(),                                                                                                             val_epoch_loss_avg.result(),                                                                                                             val_epoch_accuracy.result()))方式二:适用model.fit()进行分批训练123456789101112131415161718192021222324252627282930313233343536373839404142434445464748import model_sequential (train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data() # step2:将图像归一化train_images, test_images = train_images / 255.0, test_images / 255.0 # step3:将图像的维度变为(60000,28,28,1)train_images = tf.expand_dims(train_images, axis=3)test_images = tf.expand_dims(test_images, axis=3) # step4:将图像尺寸改为(60000,32,32,1)train_images = tf.image.resize(train_images, [32, 32])test_images = tf.image.resize(test_images, [32, 32]) # step5:导入模型# history = LeNet5()history = model_sequential.LeNet() # 让模型知道输入数据的形式history.build(input_shape=(1, 32, 32, 1))# history(tf.zeros([1, 32, 32, 1])) # 结局Output Shape为 multiplehistory.call(Input(shape=(32, 32, 1)))history.summary() # step6:编译模型history.compile(optimizer='adam',                loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),                metrics=['accuracy']) # 权重保存路径checkpoint_path = "./weight/cp.ckpt" # 回调函数,用户保存权重save_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_path,                                                   save_best_only=True,                                                   save_weights_only=True,                                                   monitor='val_loss',                                                   verbose=1)# step7:训练模型history = history.fit(train_images,                      train_labels,                      epochs=10,                      batch_size=32,                      validation_data=(test_images, test_labels),                      callbacks=[save_callback])
  • [其他] 从训练中选择元模型
    在ModelArts中创建训练作业,并完成模型训练,在得到满意的模型后,可以将训练后得到的模型导入至模型管理,方便统一管理,同时支持将模型快速部署上线为服务。背景信息如果使用ModelArts训练作业生成的模型,请确保训练作业已运行成功,且模型已存储至对应OBS目录下。针对使用订阅算法的训练作业,无需推理代码和配置文件,其生成的模型可直接导入ModelArts。针对使用常用框架或自定义镜像创建的训练作业,需根据模型包规范介绍,将推理代码和配置文件上传至模型的存储目录中。确保您使用的OBS目录与ModelArts在同一区域。创建AI应用操作步骤登录ModelArts管理控制台,在左侧导航栏中选择“AI应用管理 > AI应用”,进入AI应用列表页面。单击左上角的“创建”,进入“创建AI应用”页面。在“创建AI应用”页面,填写相关参数。写元模型来源及其相关参数。当“元模型来源”选择“从训练中选择”时,其相关的参数配置如下确认信息填写无误,单击“立即创建”,完成AI应用的创建。在AI应用列表中,您可以查看刚创建的AI应用及其对应的版本。当AI应用状态变更为“正常”时,表示AI应用导入成功。在此页面,您还可以创建新版本、快速部署服务、发布AI应用等操作。后续操作部署服务:在“AI应用列表”中,单击AI应用名称左侧的小三角,打开此AI应用下的所有版本。在对应版本所在行,单击“操作”列的“部署”,在下拉框中选择部署类型,可以将AI应用部署上线为创建AI应用时所选择的部署类型。
总条数:5195 到第
上滑加载中