• [技术干货] 第二讲:鲲鹏代码迁移工具基础知识
    一.代码迁移工具是什么       处理器所支持的指令集不同,意味着开发者可能需要对代码进行跨!平台的迁移。通常,代码迁移是件复杂又繁琐的工作,需要花费开发者大量精力对软件包、源代码、依赖库文件进行人工分析、检查和识别,手动修正不同指令集之间的相关差异,这些差异主要涉及语法、指令、函数和库文件支持情况。为了解决用户代码可迁移性人工排查困难、移植经验欠缺、反复依赖编译调错定位等,投入工作量大,整体效率低的问题,我们推出Kunpeng Porting Advisor鲲鹏代码迁移工具。        鲲鹏代码迁移工具是一款可以简化应用迁移到鲲鹏架构服务器过程的工具。主要面向鲲鹏平台的开发者、用户和第三方待迁移软件提供方开发工程师,对海量代码进行快速地自动化扫描和分析,提供专业的迁移指导报告。同时能够自动分析出需修改的代码内容,并指导如何修改,帮助开发者实现高效的代码迁移。同时支持原生开发代码亲和检查等能力。二.代码迁移工具功能三.功能特性代码迁移工具可以作为独立软件提供给鲲鹏生态用户安装使用,支持的功能特性如下:•当前工具支持五组功能,支持的功能特性分别如下:      •软件迁移评估            •检查用户提供的软件安装包,识别依赖关系,并提供鲲鹏兼容版本的so依赖库对应rpm的包OS发行版官方下载链接。            •检查用户在x86服务器上已安装的软件,识别依赖关系,并提供鲲鹏兼容版本的so依赖库对应rpm的包OS发行版官方下载链接。      •源码迁移            •检查用户C/C++/汇编/Fortran代码软件构建工程文件,并指导用户如何迁移该文件。            •检查用户C/C++/汇编/Fortran/python/Java/Scala/Go软件构建工程文件使用的链接库,并提供可迁移性信息。            •检查用户C/C++/汇编/Fortran软件源码,并指导用户如何迁移源文件。      •软件包重构,分析用户x86软件包构成,重构为适用于鲲鹏平台的软件包。      •专项软件迁移,基于我们丰富的软件迁移经验,帮助用户快速迁移Web、数据库、大数据、高性能计算四大解决方案类软件。      •鲲鹏亲和分析,目前含64位运行模式检查、结构体字节对齐检查、缓存行字节对齐检查、内存一致性检查、构建亲和检查。•用户通过安全传输协议上传软件源码、软件包、二进制文件等资源到工作空间,也可以从Web页面上直接对工作目录下的文件进行删除清理或者覆盖重命名。•支持命令行方式、Web、IDE插件三种工作模式,后两种方式下支持多用户并发扫描。•特别说明:工具不支持迁移windows环境下的软件或者IOS/Mac OS平台应用至Kunpeng平台Linux环境下。四.应用场景对最常用的x86汇编指令进行识别并转换•最常用的1000+条汇编指令自动翻译。•针对受支持的嵌入式汇编指令,提供修改指导;针对完全由受支持的完整汇编指令构成的文件,支持自动转换为结果汇编文件; 支持对尚无法指导修改和转换的汇编指令的识别、提醒。五.源码迁移•识别C/C++/Fortran/汇编源代码,提供修改建议;Make、CMake、Automake编译选项、编译宏的解析及迁移建议•支持100%Intrinsic函数转换(6000+个),包括MMX、SSE及AVX Intrinsic等•支持Fortran内联函数和语法特性以及编译选项的识别•支持Go语言迁移,对go程序使用cgo编译部分中的编译选项、宏定义提供兼容性检查,给出修改建议支持python、Java、Scala语言,对程序中的动态链接库提供兼容性检查,给出修改建议软件迁移评估•识别检查x86平台软件安装包或已安装软件中使用的动态链接库、静态链接库和可执行文件,并将检查出的文件与工具内置的依赖文件列表进行比较匹配,从而为用户提供迁移建议•Jar、war包扫描增强,识别鲲鹏平台已经支持的依赖,提供更精准的依赖兼容替换建议
  • [优秀实践] 鲲鹏众智性能分析工具IntelliJ插件实现
    很荣幸参加《鲲鹏性能分析工具IntelliJ插件实现》项目,我们的项目主要是做Inellij插件开发实现性能分析工具功能。性能分析插件安装卸载等功能可以通过密码认证或密钥认证,下面记录一下通过密钥认证连接的方式①首先打开本地cmd②执行ssh-keygen -m PEM -t rsa -b 2048命令生成公私钥对,生成的公私钥默认保存在C:\Users\username/.ssh目录,id_rsa为私钥,id_rsa.pub为公钥③登录服务器并上传本地生成的公钥文件;普通用户上传至/home/用户名/.ssh目录下;root上传至/root/.ssh目录下上传后将公钥文件id_rsa.pub名字改为authorized_keys④使用chmod 600 authorized_keys命令修改authorized_keys文件权限⑤使用cat /etc/ssh/sshd_config命令查看服务器ssh配置文件。检查如下开关为yes,如果不是,改为yes后保存配置文件,并重启sshd服务:PubkeyAuthentication yesRSAAuthentication yes以上步骤成功后,进入登录页面选择密钥认证,导入本地私钥,其它输入框内容填写正确,点击检测连接,检测成功后就可以开始安装部署了。
  • [技术干货] 使用PyCharm ToolKit工具快速实现模型训练和部署
    这里提供了使用MXNet实现手写数字图像识别应用的示例,帮助您使用ModelArts提供的PyCharm ToolKit工具,在本地快速完成模型的训练和部署。更多关于PyCharm ToolKit工具的描述,请参见《工具指南》。MNIST是一个手写体数字识别数据集,常被用作深度学习的入门样例。本示例将针对MNIST数据集,使用MXNet原生接口编写的模型训练脚本(ModelArts默认提供),在ModelArts PyCharm ToolKit中完成模型训练,并将此模型部署为在线服务。部署完成后,用户可通过在线服务识别输入图片中的数字。开始使用样例前,请仔细阅读准备工作罗列的要求,提前完成准备工作。本示例模型构建的步骤如下所示:准备工作本地已安装PyCharm 2019.2或以上版本,推荐Windows版本,Community或Professional均可,请单击PyCharm工具下载地址获取工具并在本地完成安装。使用PyCharm ToolKit远程连接Notebook开发环境,仅限PyCharm专业版。使用PyCharm ToolKit提交训练作业,社区版和专业版都支持,但仅限于提交旧版训练作业,暂不支持新版训练作业。已注册华为云帐号,且在使用ModelArts前检查帐号状态,帐号不能处于欠费或冻结状态。在ModelArts管理控制台中,当前帐号已完成访问授权的配置。如果已完成,此操作可跳过。如未完成,请参考使用委托授权。针对之前使用访问密钥授权的用户,建议清空授权,然后使用委托进行授权。已在OBS服务中创建桶和文件夹,用于存放样例数据集以及模型。如下示例中,请创建命名为“test-modelarts”的桶,并创建如表1所示的文件夹。创建OBS桶和文件夹的操作指导请参见创建桶和新建文件夹。确保您使用的OBS目录与ModelArts在同一区域。表1 文件夹列表文件夹名称用途“dataset-mnist”用于存储数据集。“mnist-output”用于存储训练输出的模型和日志文件。步骤1:安装PyCharm ToolKit,并添加访问密钥获取PyCharm ToolKit工具安装包,单击ToolKit工具的下载地址,获得工具包。打开本地PyCharm工具。在PyCharm工具中,选择菜单栏的“File > Settings”,弹出“Settings”对话框。在“Settings”对话框中,首先单击左侧导航栏中的“Plugins”,然后单击右侧的设置图标,选择“Install Plugin from Disk”,弹出文件选择对话框。图1 选择从本地安装插件在弹出的对话框中,从本地目录选择ToolKit的工具zip包,然后单击“OK”。图2 选择插件文件单击“Restart IDE”重启PyCharm。在弹出的确认对话框中,单击“Restart”开始重启。图3 重启PyCharm重启成功后,打开一个Project,当PyCharm工具栏出现“ModelArts”页签,表示ToolKit工具已安装完成。图4 安装成功单击工具栏上的“ModelArts > Edit Credential”,在PyCharm中添加访问密钥。获取此帐号的访问密钥(“AK/SK”),详细操作请参见获取访问密钥。PyCharm ToolKit安装后,在ToolKit工具中添加访问密钥,详细操作请参见使用访问秘钥登录。图5 填写区域和访问密钥查看认证结果。在右下角Event Log区域中,当提示如下类似信息时,表示访问密钥添加成功。16:01 Validate Credential Success: The HUAWEI CLOUD credential is valid.步骤2:准备数据ModelArts在公共OBS桶中提供了MNIST数据集,命名为“Mnist-Data-Set”,因此,本文的操作示例使用此数据集进行模型构建。您需要执行如下操作,将数据集上传至您的OBS目录下,即准备工作中您创建的OBS目录“test-modelarts/dataset-mnist”。单击数据集下载链接,将“Mnist-Data-Set”数据集下载至本地。在本地,将“Mnist-Data-Set.zip”压缩包解压。例如,解压至本地“Mnist-Data-Set”文件夹下。参考上传文件,使用批量上传方式将“Mnist-Data-Set”文件夹下的所有文件上传至“test-modelarts/dataset-mnist”OBS路径下。“Mnist-Data-Set”数据集包含的内容如下所示,其中“.gz”为对应的压缩包。“t10k-images-idx3-ubyte”:验证集,共包含10000个样本。“t10k-images-idx3-ubyte.gz”:验证集的压缩包文件。“t10k-labels-idx1-ubyte”:验证集标签,共包含10000个样本的类别标签。“t10k-labels-idx1-ubyte.gz”:验证集标签的压缩包文件。“train-images-idx3-ubyte”:训练集,共包含60000个样本。“train-images-idx3-ubyte.gz”:训练集的压缩包文件。“train-labels-idx1-ubyte”:训练集标签,共包含60000个样本的类别标签。“train-labels-idx1-ubyte.gz”:训练集标签的压缩包文件。步骤3:编写训练代码ModelArts提供了本示例需要使用的训练代码,请获取并在PyCharm工程中打开。在gitee的ModelArts-Lab工程中,单击“克隆/下载”,然后单击“下载ZIP”,下载工程。下载完成后,解压缩“ModelArts-Lab-master.zip”文件,然后在“\ModelArts-Lab-master\official_examples\Using_MXNet_to_Create_a_MNIST_Dataset_Recognition_Application\codes”目录中获取到训练代码文件“train_mnist.py”。打开PyCharm工具,单击“File > New Project”创建新工程, 在工程目录下创建“src”文件夹,并将训练代码文件“train_mnist.py”拷贝到“src”文件夹下。图6 将训练代码拷贝至src目录步骤4:训练模型数据和代码准备完成后,您可以创建一个训练作业,选用MXNet引擎,基于本地的train_mnist.py训练脚本,并最终生成一个可用的模型。本示例已提供编码完成的脚本(基于MXNet引擎的原生接口),如果使用自行编写的代码,请使用ModelArts支持的引擎类型及其对应版本的接口,并满足其要求。在PyCharm工具栏中,选择“ModelArts > Edit Training Job Configuration”。在弹出的对话框中,按照如下示例配置训练参数。“Job Name”:自动生成,首次提交训练作业时,该名称也可以自己指定。“AI Engine”:选择“MXNet”,版本为“MXNet-1.2.1-python3.6”。“Algorithm Source”:此处选择“Frequently-used”,代表常用框架。Custom表示自定义镜像,基于自定义镜像的训练作业案例请参见使用自定义镜像创建训练作业。“Specifications”:选择GPU规格。“OBS Path”:填写准备工作中创建的输出路径,用于存储训练输出模型和日志文件。“Data Path in OBS”:填写步骤2:准备数据中数据上传的OBS目录。此处需完整OBS路径,需包含OBS桶名称。此示例填写的值如图所示,请务必修改为您自己的OBS桶及路径。“Boot File Path”:选择本地的训练脚本“train_mnist.py”。“Code Directory”:选择启动脚本所在“src”目录。“Running Parameters”:是训练脚本所需要的输入参数,本样例中没有参数,无需填写。填写完成后,单击“Apply and Run”提交训练作业到云上ModelArts。说明:由于MNIST数据集数据较多,为提升训练效率,操作示例选择GPU训练。但是,GPU的费用高于CPU,请根据实际情况选择可用的资源池。图7 配置训练作业训练作业提交后,可在下方查看训练日志。当训练日志中出现“Current training job status: Successful”类似信息时,表示训练作业运行成功。图8 查看训练日志其中, “ModelArts Event Log”栏为工具打印的日志,“ModelArts Training Log”为训练脚本代码打印的日志。从日志可以看到,工具会先将本地工程的代码自动上传至云上OBS,然后自动提交一个训练作业。作业提交成功后,工具会实时从云上的训练环境获取日志并展示在“ModelArts Training Log”窗口,直至作业运行结束。在PyCharm的左边菜单栏,单击“ModelArts Explorer”,选择刚才提交的作业,双击版本号“V0001”,可以查看作业详情。图9 选择对应的训练作业及版本图10 训练作业详情步骤5:编写推理代码和配置文件,并上传至模型所在路径ModelArts提供了本示例需要使用的推理代码“customize_service.py”和配置文件“config.json”,文件路径和下载的git工程中的训练代码在同一目录。此推理代码和配置文件是ModelArts提供的示例。在步骤4:训练模型中,训练作业生成的模型存储在“test-modelarts/mnist-output/MA-mnist-11-30-16/output/V0001”路径下(其中MA-mnist-11-30-16是Job Name,如果配置时填写了自定义Job Name,则路径中就是自定义的Job Name),且系统将自动生成“model”文件夹,模型相关的文件存储在此目录下。将获取的“customize_service.py”和“config.json”文件,上传至OBS的“model”文件夹下。说明:如果训练作业运行多次,将在“训练输出位置”生成不同的版本,即“mnist-output”目录下将生成多种版本的目录,如“V0001”、“V0002”,请基于训练作业的版本,将文件上传至对应版本下的“model”文件夹内。前往OBS管理控制台,找到“test-modelarts”桶,然后进入“test-modelarts/mnist-output/MA-mnist-11-30-16/output/V001/model”路径,执行“上传对象”的操作。OBS的上传操作指导,请参见上传文件。步骤6:部署在线服务训练完成的模型还是存储在OBS路径中,您可以将此模型导入到ModelArts中并部署为在线服务。在训练作业版本号上单击鼠标右键,选择“Deploy to Service”。图11 部署为在线服务在弹出的对话框中,按照如下说明填写部署为在线服务的参数。“Service Name”:自动生成,也可以自定义。“Auto Stop”:勾选Auto Stop,表示启动自动停止功能,服务会在指定时间后自动停止。“Model Path”:自动填写,无需自己配置。与您选择的训练作业及其版本一致。“Environment Variables”:填写运行参数,设置为“input_data_name=images;input_data_shape=0,1,28,28;output_data_shape=0,10”。“input_data_name”:参数值必须是images。针对自己开发训练脚本的场景,此参数是必须设置的。您可以在导入模型时设置参数,也可以写到推理代码中。“input_data_shape”:代表NCHW。本示例的数值为“0,1,28,28”,表示设置了输入图片的规则,其中,输入预测图片大小必须是“28px*28px”。“output_data_shape”:置信度。本示例指定范围为“0~10”。表示输出结果会显示“0~9”中10个类别,属于每一个类别的概率。信息填写完成后,单击“OK”,开始服务部署。图12 部署为在线服务可以在最下方的日志栏查看服务部署进度。图13 查看部署进度模型部署上线需要花费一些时间,请耐心等待几分钟。当出现类似“Service status is running”信息时,表示服务部署成功。服务部署成功后,将展示在线服务的链接,单击链接可以进入ModelArts控制台部署上线 >在线服务的界面。说明:首次进入需要输入华为云帐号密码登录。图14 完成部署步骤7:测试服务在线服务部署成功后,您可以进入在线服务,发起预测请求进行测试。部署成功后,单击提供的链接进入ModelArts控制台的“部署上线 >在线服务”界面。找到刚部署的在线服务,单击服务名称,进入在线服务详情页面中,单击“预测”页签,进入预测页面。在“选择预测图片文件”右侧,单击“上传”按钮,上传一张黑底白字的图片,然后单击“预测”。预测完成后,预测结果显示区域将展示预测结果,根据预测结果内容,可识别出此图片的数字是“2”的概率为“1”。说明:由于推理代码和配置文件中已指定图片要求,用于预测的图片,大小必须为“28px*28px”,且图片必须是黑底白字。建议不要使用数据中自带的图片,可以使用Windows自带的画图工具绘制一张。图15 预测结果步骤8:清除相应资源,避免产生费用为避免产生不必要的费用,在完成试用后,建议您删除相关资源,如在线服务、训练作业及其OBS目录。进入ModelArts管理控制台,删除在线服务:在“在线服务”页面,单击操作列的“更多>删除”。在PyCharm菜单栏中,选择“ModelArts > Stop Training Job”停止此训练作业。或者进入ModelArts管理控制台,在“训练作业旧版”页面,单击操作列的“删除”,删除此训练作业。进入OBS管理控制台,删除数据准备阶段创建的OBS桶。先逐个删除桶内文件夹和文件,再执行删除桶的操作。
  • [经验分享] 使用MindStudio进行Pytorch模型迁移以及推理
    1. MindStudio环境搭建    MindStudio是一套基于IntelliJ框架的开发工具链平台,提供了应用开发,调试,Profiling工具,模型转换,模型可视化等功能,以及对开发者来说至关重要的精度对比,自定义算子等重要功能,同时还提供了网络移植,优化和分析功能,方便用户开发应用程序。    按照平台安装教程安装MindStudio即可完成本部分的环境搭建。2.模型简介    本次实验我们的目标是在MindStudio上使用ResNet进行图像分类任务。ResNet曾在ILSVRC2015比赛中获得冠军,在图像分类上具有很大的潜力。ResNet的主要思想是在网络中增加了直连通道,使得原始输入信息能够直接传入到后面的层中,ResNet的结构如下图所示。ResNet会将残差块串联,形成多层网络。一般常见的ResNet根据层数的大小有ResNet-18, ResNet-34, ResNet-50, ResNet-101等。3. 创建推理工程    打开MindStudio,点击New Project,进入新建工程界面。选择Ascend App。填入项目名resNext101_32x8d。首次新建训练工程时,需要配置CANN的版本。点击Change。点击 + 配置远程连接,然后根据选项填入自己服务器的IP地址、端口号、用户名和密码等。配置Remote CANN location。该参数需要填入ascend-toolkit在服务器上的路径地址。在这里,我们的toolkit路径如下/usr/local/Ascend/ascend-toolkit/5.1.RC1。点击finishing进行配置。点击Next,选择Pytorch Project。点击Finish,完成工程创建,进入工程主界面。4. 将环境部署到远端Mind Studio可以支持实时的将本地文件夹内容同步到服务器,可以达到像自己电脑上执行模型推理一样的效果,此过程需要两个步骤。点击File->Settings->Tools,即可看到两个设置的入口。配置ssh                首先点击SSH Configuration,再点击 + 配置远程连接,然后根据选项填入自己的服务器ip地址,端口号,用户名和密码。部署本地文件夹        相当于将远端服务器的路径与本地文件夹形成一个对应关系。Deployment配置能够更精准地连接远程服务,他需要选择一个SSH连接来定位远程主机,然后设置本地与远程项目目录的对应关系。首先选择连接哪SSH服务,然后测试连接是否可用。下一步,设置项目目录映射关系,点击Mappings,然后选择本地的项目目录和远程的项目目录(最好提前创建文件夹),接下来跑代码的时候MindStudio会保证这两个目录文件的同步。配置好Mappings之后,建议配置Excluded Paths, 因为MindStudio默认行为会把Mappings目录的文件保持完全相同,这意味着只存在于远端服务器的数据集文件会被删除(如果本地没有数据集)。再次,我配置了几个需要排除的文件夹目录。5. 配置Python环境    点击File -> Project Structures -> SDKs可以看到如图所示的界面。点击+号,可以新增python SDK和JDK。    点击Add Python SDK后,将弹出下图所示界面,点击左侧的SSH Interpreter,如下图所示,首先选择使用哪一个Deployment,这是刚刚我们配置过的。然后选择python解释器的路径。图中的路径是MindStudio自动检测出来的,但一般需要我们找自己的Python环境安装目录。然后点击Project,选择刚才添加到MindStudio中的Python解释器,将其作为本项目所使用的解释器。6. 数据预处理   首先执行数据预处理脚本image_torch_preprocess.py, 将原生的图片数据转换为bin格式的输入文件。第一个参数为原始数据集(.jpeg)所在路径,第二个参数为输出的二进制文件(.bin)所在的路径。每个图片对应生成一个二进制文件。在预处理获得bin文件之后,再执行get_info.py提取输入信息。7. 模型转换    模型转换主要分为两步,第一步是将原有的pth模型转换为通用的onnx模型,第二步是将onnx模型转换为npu适用的om模型。    首先调用python脚本将pth模型转换为onnx模型。再使用ATC工具将onnx模型导出为om模型。MindStudio提供了atc工具的窗口化应用,他会根据用户选择自动生成atc指令。Model Converter的入口如图所示。选择onnx路径,模型输出名称,目标设备,输入格式和batchsize等信息。本实验需要跑到不同的batchsize,这里只需要修改shape参数即可对应生成不同batch size的om模型。MindStudio自动生成如下atc指令,用户可以在此做最后的校验,点击Finish即可开始进行模型转换。转换成功得到结果如下:8. 离线推理    使用benchmark工具进行离线推理,可以创建如图所示的应用:点击 + 号,再点击Ascend App,按照自己的模型相关信息进行填写即可。在不同机器上执行效率对比如下表格ModelBatch Size310p310 (FPS/Card)T4 (FPS/Card)310p/310310p/T4ResNeXt1011594.884594.696254.2521.0002.340ResNeXt1014566.742674.64486.6560.8401.160ResNeXt1018937.698669.232531.9961.4011.763ResNeXt101161086.34670.3570.8671.6211.903ResNeXt101321191.32429.944543.2932.7712.193ResNeXt10164781.172427.304539.9241.8281.447最优batch32/8/161191.32669.232570.8671.7662.0879. 精度对比    推理结束之后,可以调用imagenet_acc_eval.py脚本和原有标签数据进行对比,得到top5的结果。最后的精度表现如下:top1准确率为79.3%,top5准确率为94.52%。
  • [最佳实践] 使用IntelliJ IDEA Java创建和使用DLI表
    操作场景DLI支持用户编写代码创建Spark作业来创建数据库、创建DLI表或OBS表和插入表数据等操作。本示例完整的演示通过编写java代码、使用Spark作业创建数据库、创建表和插入表数据的详细操作,帮助您在DLI上进行作业开发。约束限制不支持的场景:在SQL作业中创建了数据库(database),编写程序代码指定在该数据库下创建表。例如在DLI的SQL编辑器中的某SQL队列下,创建了数据库testdb。后续通过编写程序代码在testdb下创建表testTable,编译打包后提交的Spark Jar作业则会运行失败。不支持创建加密的DLI表,即不支持创建DLI表时设置encryption=true。例如,如下建表语句不支持:CREATE TABLE tb1(id int) using parquet options(encryption=true)支持的场景在SQL作业中创建数据库(database),表(table) , 通过SQL或Spark程序作业读取插入数据。在Spark程序作业中创建数据库(database),表(table), 通过SQL或Spark程序作业读取插入数据。环境准备在进行Spark 作业访问DLI元数据开发前,请准备以下开发环境。表1 Spark Jar作业开发环境准备项说明操作系统Windows系统,支持Windows7以上版本。安装JDKJDK使用1.8版本。安装和配置IntelliJ IDEAIntelliJ IDEA为进行应用开发的工具,版本要求使用2019.1或其他兼容版本。安装Maven开发环境的基本配置。用于项目管理,贯穿软件开发生命周期。开发流程DLI进行Spark作业访问DLI元数据开发流程参考如下:图1 Spark作业访问DLI元数据开发流程表2 开发流程说明序号阶段操作界面说明1创建DLI通用队列DLI控制台创建作业运行的DLI队列。2OBS桶文件配置OBS控制台如果是创建OBS表,则需要上传文件数据到OBS桶下。配置Spark创建表的元数据信息“spark.sql.warehouse.dir”的存储路径。3新建Maven工程,配置pom文件IntelliJ IDEA参考样例代码说明,编写程序代码创建DLI表或OBS表。4编写程序代码5调试,编译代码并导出Jar包6上传Jar包到OBS和DLIOBS控制台将生成的Spark Jar包文件上传到OBS目录下和DLI程序包中。7创建Spark Jar作业DLI控制台在DLI控制台创建Spark Jar作业并提交运行作业。8查看作业运行结果DLI控制台查看作业运行状态和作业运行日志。步骤1:创建DLI通用队列第一次提交Spark作业,需要先创建队列,例如创建名为“sparktest”的队列,队列类型选择为“通用队列”。在DLI管理控制台的左侧导航栏中,选择“队列管理”。单击“队列管理”页面右上角“购买队列”进行创建队列。创建名为“sparktest”的队列,队列类型选择为“通用队列”。创建队列详细介绍请参考创建队列。单击“立即购买”,确认配置。配置确认无误,单击“提交”完成队列创建。步骤2:OBS桶文件配置如果需要创建OBS表,则需要先上传数据到OBS桶目录下。本次演示的样例代码创建了OBS表,测试数据内容参考如下示例,创建名为的testdata.csv文件。12,Michael 27,Andy 30,Justin进入OBS管理控制台,在“桶列表”下,单击已创建的OBS桶名称,本示例桶名为“dli-test-obs01”,进入“概览”页面。单击左侧列表中的“对象”,选择“上传对象”,将testdata.csv文件上传到OBS桶根目录下。在OBS桶根目录下,单击“新建文件夹”,创建名为“warehousepath”的文件夹。该文件夹路径用来存储Spark创建表的元数据信息“spark.sql.warehouse.dir”。步骤3:新建Maven工程,配置pom依赖以下通过IntelliJ IDEA 2020.2工具操作演示。打开IntelliJ IDEA,选择“File > New > Project”。图2 新建Project选择Maven,Project SDK选择1.8,单击“Next”。定义样例工程名和配置样例工程存储路径,单击“Finish”完成工程创建。如上图所示,本示例创建Maven工程名为:SparkJarMetadata,Maven工程路径为:“D:\DLITest\SparkJarMetadata”。在pom.xml文件中添加如下配置。<dependencies> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-sql_2.11</artifactId> <version>2.3.2</version> </dependency> </dependencies>图3 修改pom.xml文件在工程路径的“src > main > java”文件夹上鼠标右键,选择“New > Package”,新建Package和类文件。Package根据需要定义,本示例定义为:“com.huawei.dli.demo”,完成后回车。在包路径下新建Java Class文件,本示例定义为:DliCatalogTest。步骤4:编写代码编写DliCatalogTest程序创建数据库、DLI表和OBS表。完整的样例请参考Java样例代码,样例代码分段说明如下:导入依赖的包。import org.apache.spark.sql.SparkSession;创建SparkSession会话。创建SparkSession会话时需要指定Spark参数:"spark.sql.session.state.builder"、"spark.sql.catalog.class"和"spark.sql.extensions",按照样例配置即可。SparkSession spark = SparkSession .builder() .config("spark.sql.session.state.builder", "org.apache.spark.sql.hive.UQueryHiveACLSessionStateBuilder") .config("spark.sql.catalog.class", "org.apache.spark.sql.hive.UQueryHiveACLExternalCatalog") .config("spark.sql.extensions","org.apache.spark.sql.CarbonInternalExtensions"+","+"org.apache.spark.sql.DliSparkExtension") .appName("java_spark_demo") .getOrCreate();创建数据库。如下样例代码演示,创建名为test_sparkapp的数据库。spark.sql("create database if not exists test_sparkapp").collect();创建DLI表并插入测试数据。spark.sql("drop table if exists test_sparkapp.dli_testtable").collect(); spark.sql("create table test_sparkapp.dli_testtable(id INT, name STRING)").collect(); spark.sql("insert into test_sparkapp.dli_testtable VALUES (123,'jason')").collect(); spark.sql("insert into test_sparkapp.dli_testtable VALUES (456,'merry')").collect();创建OBS表。如下示例中的OBS路径需要根据步骤2:OBS桶文件配置中的实际数据路径修改。spark.sql("drop table if exists test_sparkapp.dli_testobstable").collect(); spark.sql("create table test_sparkapp.dli_testobstable(age INT, name STRING) using csv options (path 'obs://dli-test-obs01/testdata.csv')").collect();关闭SparkSession会话spark。spark.stop();步骤5:调试、编译代码并导出Jar包单击IntelliJ IDEA工具右侧的“Maven”,参考下图分别单击“clean”、“compile”对代码进行编译。编译成功后,单击“package”对代码进行打包。打包成功后,生成的Jar包会放到target目录下,以备后用。本示例将会生成到:“D:\DLITest\SparkJarMetadata\target”下名为“SparkJarMetadata-1.0-SNAPSHOT.jar”。步骤6:上传Jar包到OBS和DLI下登录OBS控制台,将生成的“SparkJarMetadata-1.0-SNAPSHOT.jar”Jar包文件上传到OBS路径下。将Jar包文件上传到DLI的程序包管理中,方便后续统一管理。登录DLI管理控制台,单击“数据管理 > 程序包管理”。在“程序包管理”页面,单击右上角的“创建”创建程序包。在“创建程序包”对话框,配置以下参数。包类型:选择“JAR”。OBS路径:程序包所在的OBS路径。分组设置和组名称根据情况选择设置,方便后续识别和管理程序包。图4 创建程序包单击“确定”,完成创建程序包。步骤7:创建Spark Jar作业登录DLI控制台,单击“作业管理 > Spark作业”。在“Spark作业”管理界面,单击“创建作业”。在作业创建界面,配置对应作业运行参数。具体说明如下:表3 Spark Jar作业参数填写参数名参数值所属队列选择已创建的DLI通用队列。例如当前选择步骤1:创建DLI通用队列创建的通用队列“sparktest”。作业名称(--name)自定义Spark Jar作业运行的名称。当前定义为:SparkTestMeta。应用程序选择步骤6:上传Jar包到OBS和DLI下中上传到DLI程序包。例如当前选择为:“SparkJarObs-1.0-SNAPSHOT.jar”。主类格式为:程序包名+类名。例如当前为:com.huawei.dli.demo.DliCatalogTest。Spark参数(--conf)spark.dli.metaAccess.enable=truespark.sql.warehouse.dir=obs://dli-test-obs01/warehousepath说明:spark.sql.warehouse.dir参数的OBS路径为步骤2:OBS桶文件配置中配置创建。访问元数据选择:是其他参数保持默认值即可。图5 创建Spark Jar作业单击“执行”,提交该Spark Jar作业。在Spark作业管理界面显示已提交的作业运行状态。查看作业运行结果在Spark作业管理界面显示已提交的作业运行状态。初始状态显示为“启动中”。如果作业运行成功则作业状态显示为“已成功”,通过以下操作查看创建的数据库和表。可以在DLI控制台,左侧导航栏,单击“SQL编辑器”。在“数据库”中已显示创建的数据库“test_sparkapp”。图6 查看创建的数据库双击数据库名,可以在数据库下查看已创建成功的DLI和OBS表。图7 查看表双击DLI表名dli_testtable,单击“执行”查询DLI表数据。图8 查询DLI表数据注释掉DLI表查询语句,双击OBS表名dli_testobstable,单击“执行”查询OBS表数据。图9 查询OBS表数据如果作业运行失败则作业状态显示为“已失败”,单击“操作”列“更多”下的“Driver日志”,显示当前作业运行的日志,分析报错原因。图10 查看Driver日志原因定位解决后,可以在作业“操作”列,单击“编辑”,修改作业相关参数后,单击“执行”重新运行该作业即可。后续指引如果您想通过Spark Jar作业访问其他数据源,请参考《使用Spark作业跨源访问数据源》。创建DLI表的语法请参考创建DLI表,创建OBS表的语法请参考创建OBS表。如果是通过API接口调用提交该作业请参考以下操作说明:调用创建批处理作业接口,参考以下请求参数说明。详细的API参数说明请参考《数据湖探索API参考》>《创建批处理作业》。将请求参数中的“catalog_name”参数设置为“dli”。conf 中需要增加"spark.dli.metaAccess.enable":"true"。如果需要执行DDL,则还要在conf中配置"spark.sql.warehouse.dir": "obs://bucket/warehousepath"。完整的API请求参数可以参考如下示例说明。{ "queue":"citest", "file":"SparkJarMetadata-1.0-SNAPSHOT.jar", "className":"DliCatalogTest", "conf":{"spark.sql.warehouse.dir": "obs://bucket/warehousepath", "spark.dli.metaAccess.enable":"true"}, "sc_type":"A", "executorCores":1, "numExecutors":6, "executorMemory":"4G", "driverCores":2, "driverMemory":"7G", "catalog_name": "dli" }Java样例代码本示例操作步骤采用Java进行编码,具体完整的样例代码参考如下:package com.huawei.dli.demo; import org.apache.spark.sql.SparkSession; public class DliCatalogTest { public static void main(String[] args) { SparkSession spark = SparkSession .builder() .config("spark.sql.session.state.builder", "org.apache.spark.sql.hive.UQueryHiveACLSessionStateBuilder") .config("spark.sql.catalog.class", "org.apache.spark.sql.hive.UQueryHiveACLExternalCatalog") .config("spark.sql.extensions","org.apache.spark.sql.CarbonInternalExtensions"+","+"org.apache.spark.sql.DliSparkExtension") .appName("java_spark_demo") .getOrCreate(); spark.sql("create database if not exists test_sparkapp").collect(); spark.sql("drop table if exists test_sparkapp.dli_testtable").collect(); spark.sql("create table test_sparkapp.dli_testtable(id INT, name STRING)").collect(); spark.sql("insert into test_sparkapp.dli_testtable VALUES (123,'jason')").collect(); spark.sql("insert into test_sparkapp.dli_testtable VALUES (456,'merry')").collect(); spark.sql("drop table if exists test_sparkapp.dli_testobstable").collect(); spark.sql("create table test_sparkapp.dli_testobstable(age INT, name STRING) using csv options (path 'obs://dli-test-obs01/testdata.csv')").collect(); spark.stop(); } }scala样例代码scala样例代码object DliCatalogTest { def main(args:Array[String]): Unit = { val sql = args(0) val runDdl = Try(args(1).toBoolean).getOrElse(true) System.out.println(s"sql is $sql runDdl is $runDdl") val sparkConf = new SparkConf(true) sparkConf .set("spark.sql.session.state.builder", "org.apache.spark.sql.hive.UQueryHiveACLSessionStateBuilder") .set("spark.sql.catalog.class", "org.apache.spark.sql.hive.UQueryHiveACLExternalCatalog") sparkConf.setAppName("dlicatalogtester") val spark = SparkSession.builder .config(sparkConf) .enableHiveSupport() .config("spark.sql.extensions", Seq("org.apache.spark.sql.CarbonInternalExtensions", "org.apache.spark.sql.DliSparkExtension").mkString(",")) .appName("SparkTest") .getOrCreate() System.out.println("catalog is " + spark.sessionState.catalog.toString) if (runDdl) { val df = spark.sql(sql).collect() } else { spark.sql(sql).show() } spark.close() } }Python样例代码Python样例代码#!/usr/bin/python # -*- coding: UTF-8 -*- from __future__ import print_function import sys from pyspark.sql import SparkSession if __name__ == "__main__": url = sys.argv[1] creatTbl = "CREATE TABLE test_sparkapp.dli_rds USING JDBC OPTIONS ('url'='jdbc:mysql://%s'," \ "'driver'='com.mysql.jdbc.Driver','dbtable'='test.test'," \ " 'passwdauth' = 'DatasourceRDSTest_pwd','encryption' = 'true')" % url spark = SparkSession \ .builder \ .enableHiveSupport() \ .config("spark.sql.session.state.builder","org.apache.spark.sql.hive.UQueryHiveACLSessionStateBuilder") \ .config("spark.sql.catalog.class", "org.apache.spark.sql.hive.UQueryHiveACLExternalCatalog") \ .config("spark.sql.extensions",','.join(["org.apache.spark.sql.CarbonInternalExtensions","org.apache.spark.sql.DliSparkExtension"])) \ .appName("python Spark test catalog") \ .getOrCreate() spark.sql("CREATE database if not exists test_sparkapp").collect() spark.sql("drop table if exists test_sparkapp.dli_rds").collect() spark.sql(creatTbl).collect() spark.sql("select * from test_sparkapp.dli_rds").show() spark.sql("insert into table test_sparkapp.dli_rds select 12,'aaa'").collect() spark.sql("select * from test_sparkapp.dli_rds").show() spark.sql("insert overwrite table test_sparkapp.dli_rds select 1111,'asasasa'").collect() spark.sql("select * from test_sparkapp.dli_rds").show() spark.sql("drop table test_sparkapp.dli_rds").collect() spark.stop()
  • [最佳实践] 使用IntelliJ IDEA Java创建和使用Spark UDTF
    操作场景DLI支持用户使用Hive UDTF(User-Defined Table-Generating Functions)自定义表值函数,UDTF用于解决一进多出业务场景,即其输入与输出是一对多的关系,读入一行数据,输出多个值。约束限制在DLI Console上执行UDTF相关操作时,需要使用自建的SQL队列。跨账号使用UDTF时,除了创建UDTF函数的用户,其他用户如果需要使用时,需要先进行授权才可使用对应的UDTF函数。授权操作参考如下:登录DLI管理控制台,选择“ 数据管理 > 程序包管理”页面,选择对应的UDTF Jar包,单击“操作”列中的“权限管理”,进入权限管理页面,单击右上角“授权”,勾选对应权限。自定义函数中引用static类或接口时,必须要加上“try catch”异常捕获,否则可能会造成包冲突,导致函数功能异常。环境准备在进行UDTF开发前,请准备以下开发环境。表1 UDTF开发环境准备项说明操作系统Windows系统,支持Windows7以上版本。安装JDKJDK使用1.8版本。安装和配置IntelliJ IDEAIntelliJ IDEA为进行应用开发的工具,版本要求使用2019.1或其他兼容版本。安装Maven开发环境的基本配置。用于项目管理,贯穿软件开发生命周期。开发流程DLI下UDTF函数开发流程参考如下:图1 UDTF开发流程表2 开发流程说明序号阶段操作界面说明1新建Maven工程,配置pom文件IntelliJ IDEA参考操作步骤说明,编写UDTF函数代码。2编写UDTF函数代码3调试,编译代码并导出Jar包4上传Jar包到OBSOBS控制台将生成的UDTF函数Jar包文件上传到OBS目录下。5创建DLI的UDTF函数DLI控制台在DLI控制台的SQL作业管理界面创建使用的UDTF函数。6验证和使用DLI的UDTF函数DLI控制台在DLI作业中使用创建的UDTF函数。操作步骤新建Maven工程,配置pom文件。以下通过IntelliJ IDEA 2020.2工具操作演示。打开IntelliJ IDEA,选择“File > New > Project”。图2 新建Project选择Maven,Project SDK选择1.8,单击“Next”。定义样例工程名和配置样例工程存储路径,单击“Finish”完成工程创建。在pom.xml文件中添加如下配置。<dependencies> <dependency> <groupId>org.apache.hive</groupId> <artifactId>hive-exec</artifactId> <version>1.2.1</version> </dependency> </dependencies>图3 pom文件中添加配置在工程路径的“src > main > java”文件夹上鼠标右键,选择“New > Package”,新建Package和类文件。Package根据需要定义,本示例定义为:“com.huawei.demo”,完成后回车。在包路径下新建Java Class文件,本示例定义为:UDTFSplit。编写UDTF函数代码。完整样例代码请参考样例代码。UDTF的类需要继承“org.apache.hadoop.hive.ql.udf.generic.GenericUDTF”,实现initialize,process,close三个方法。UDTF首先会调用initialize方法,此方法返回UDTF的返回行的信息,如,返回个数,类型等。初始化完成后,会调用process方法,真正处理在process函数中,在process中,每一次forward()调用产生一行。如果产生多列可以将多个列的值放在一个数组中,然后将该数组传入到forward()函数。public void process(Object[] args) throws HiveException { // TODO Auto-generated method stub if(args.length == 0){ return; } String input = args[0].toString(); if(StringUtils.isEmpty(input)){ return; } String[] test = input.split(";"); for (int i = 0; i < test.length; i++) { try { String[] result = test[i].split(":"); forward(result); } catch (Exception e) { continue; } } }最后调用close方法,对需要清理的方法进行清理。编写调试完成代码后,通过IntelliJ IDEA工具编译代码并导出Jar包。单击工具右侧的“Maven”,参考下图分别单击“clean”、“compile”对代码进行编译。编译成功后,单击“package”对代码进行打包。图4 编译打包打包成功后,生成的Jar包会放到target目录下,以备后用。本示例将会生成到:“D:\MyUDTF\target”下名为“MyUDTF-1.0-SNAPSHOT.jar”。登录OBS控制台,将生成的Jar包文件上传到OBS路径下。说明:Jar包文件上传的OBS桶所在的区域需与DLI的队列区域相同,不可跨区域执行操作。(可选)可以将Jar包文件上传到DLI的程序包管理中,方便后续统一管理。登录DLI管理控制台,单击“数据管理 > 程序包管理”。在“程序包管理”页面,单击右上角的“创建”创建程序包。在“创建程序包”对话框,配置以下参数。包类型:选择“JAR”。OBS路径:程序包所在的OBS路径。分组设置和组名称根据情况选择设置,方便后续识别和管理程序包。单击“确定”,完成创建程序包。创建DLI的UDTF函数。登录DLI管理控制台,单击“SQL编辑器”,执行引擎选择“spark”,选择已创建的SQL队列和数据库。图5 选择队列和数据库在SQL编辑区域输入下列命令创建UDTF函数,单击“执行”提交创建。CREATE FUNCTION mytestsplit AS 'com.huawei.demo.UDTFSplit' using jar 'obs://dli-test-obs01/MyUDTF-1.0-SNAPSHOT.jar';重启原有SQL队列,使得创建的UDTF函数生效。登录数据湖探索管理控制台,选择“队列管理”,在对应“SQL队列”类型作业的“操作”列,单击“重启”。在“重启队列”界面,选择“确定”完成队列重启。验证和使用创建的UDTF函数。在查询语句中使用6中创建的UDTF函数,如:select mytestsplit('abc:123\;efd:567\;utf:890');图6 执行结果(可选)删除UDTF函数。如果不再使用该Function,可执行以下语句删除UDTF函数:Drop FUNCTION mytestsplit;样例代码UDTFSplit.java完整的样例代码参考如下所示:package com.huawei.demo; import java.util.ArrayList; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.hive.ql.exec.UDFArgumentException; import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException; import org.apache.hadoop.hive.ql.metadata.HiveException; import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF; import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector; import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory; import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector; import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory; public class UDTFSplit extends GenericUDTF { @Override public void close() throws HiveException { // TODO Auto-generated method stub } @Override public void process(Object[] args) throws HiveException { // TODO Auto-generated method stub if(args.length == 0){ return; } String input = args[0].toString(); if(StringUtils.isEmpty(input)){ return; } String[] test = input.split(";"); for (int i = 0; i < test.length; i++) { try { String[] result = test[i].split(":"); forward(result); } catch (Exception e) { continue; } } } @Override public StructObjectInspector initialize(ObjectInspector[] args) throws UDFArgumentException { if (args.length != 1) { throw new UDFArgumentLengthException("ExplodeMap takes only one argument"); } if (args[0].getCategory() != ObjectInspector.Category.PRIMITIVE) { throw new UDFArgumentException("ExplodeMap takes string as a parameter"); } ArrayList<String> fieldNames = new ArrayList<String>(); ArrayList<ObjectInspector> fieldOIs = new ArrayList<ObjectInspector>(); fieldNames.add("col1"); fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector); fieldNames.add("col2"); fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector); return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs); } }
  • [最佳实践] 使用IntelliJ IDEA Java创建和使用Spark UDF
    操作场景DLI支持用户使用Hive UDF(User Defined Function,用户定义函数)进行数据查询等操作,UDF只对单行数据产生作用,适用于一进一出的场景。约束限制在DLI Console上执行UDF相关操作时,需要使用自建的SQL队列。跨账号使用UDF时,除了创建UDF函数的用户,其他用户如果需要使用时,需要先进行授权才可使用对应的UDF函数。授权操作参考如下:登录DLI管理控制台,选择“ 数据管理 > 程序包管理”页面,选择对应的UDF Jar包,单击“操作”列中的“权限管理”,进入权限管理页面,单击右上角“授权”,勾选对应权限。自定义函数中引用static类或接口时,必须要加上“try catch”异常捕获,否则可能会造成包冲突,导致函数功能异常。环境准备在进行UDF开发前,请准备以下开发环境。表1 UDF开发环境准备项说明操作系统Windows系统,支持Windows7以上版本。安装JDKJDK使用1.8版本。安装和配置IntelliJ IDEAIntelliJ IDEA为进行应用开发的工具,版本要求使用2019.1或其他兼容版本。安装Maven开发环境的基本配置。用于项目管理,贯穿软件开发生命周期。开发流程DLI下UDF函数开发流程参考如下:图1 开发流程表2 开发流程说明序号阶段操作界面说明1新建Maven工程,配置pom文件IntelliJ IDEA参考操作步骤说明,编写UDF函数代码。2编写UDF函数代码3调试,编译代码并导出Jar包4上传Jar包到OBSOBS控制台将生成的UDF函数Jar包文件上传到OBS目录下。5创建DLI的UDF函数DLI控制台在DLI控制台的SQL作业管理界面创建使用的UDF函数。6验证和使用DLI的UDF函数DLI控制台在DLI作业中使用创建的UDF函数。操作步骤新建Maven工程,配置pom文件。以下通过IntelliJ IDEA 2020.2工具操作演示。打开IntelliJ IDEA,选择“File > New > Project”。图2 新建Project选择Maven,Project SDK选择1.8,单击“Next”。定义样例工程名和配置样例工程存储路径,单击“Finish”完成工程创建。在pom.xml文件中添加如下配置。<dependencies> <dependency> <groupId>org.apache.hive</groupId> <artifactId>hive-exec</artifactId> <version>1.2.1</version> </dependency> </dependencies>图3 pom文件中添加配置在工程路径的“src > main > java”文件夹上鼠标右键,选择“New > Package”,新建Package和类文件。Package根据需要定义,本示例定义为:“com.huawei.demo”,完成后回车。在包路径下新建Java Class文件,本示例定义为:SumUdfDemo。编写UDF函数代码。UDF函数实现,主要注意以下几点:自定义UDF需要继承org.apache.hadoop.hive.ql.UDF。需要实现evaluate函数,evaluate函数支持重载。详细UDF函数实现,可以参考如下样例代码:package com.huawei.demo; import org.apache.hadoop.hive.ql.exec.UDF; public class SumUdfDemo extends UDF { public int evaluate(int a, int b) { return a + b; } }编写调试完成代码后,通过IntelliJ IDEA工具编译代码并导出Jar包。单击工具右侧的“Maven”,参考下图分别单击“clean”、“compile”对代码进行编译。编译成功后,单击“package”对代码进行打包。打包成功后,生成的Jar包会放到target目录下,以备后用。本示例将会生成到:“D:\DLITest\MyUDF\target”下名为“MyUDF-1.0-SNAPSHOT.jar”。登录OBS控制台,将生成的Jar包文件上传到OBS路径下。说明:Jar包文件上传的OBS桶所在的区域需与DLI的队列区域相同,不可跨区域执行操作。(可选)可以将Jar包文件上传到DLI的程序包管理中,方便后续统一管理。登录DLI管理控制台,单击“数据管理 > 程序包管理”。在“程序包管理”页面,单击右上角的“创建”创建程序包。在“创建程序包”对话框,配置以下参数。包类型:选择“JAR”。OBS路径:程序包所在的OBS路径。分组设置和组名称根据情况选择设置,方便后续识别和管理程序包。单击“确定”,完成创建程序包。创建UDF函数。登录DLI管理控制台,单击“SQL编辑器”,执行引擎选择“spark”,选择已创建的SQL队列和数据库。图4 选择队列和数据库在SQL编辑区域输入下列命令创建UDF函数,单击“执行”提交创建。CREATE FUNCTION TestSumUDF AS 'com.huawei.demo.SumUdfDemo' using jar 'obs://dli-test-obs01/MyUDF-1.0-SNAPSHOT.jar';重启原有SQL队列,使得创建的Function生效。登录数据湖探索管理控制台,选择“队列管理”,在对应“SQL队列”类型作业的“操作”列,单击“重启”。在“重启队列”界面,选择“确定”完成队列重启。使用UDF函数。在查询语句中使用6中创建的UDF函数:select TestSumUDF(1,2);图5 执行结果(可选)删除UDF函数。如果不再使用UDF函数,可执行以下语句删除该函数:Drop FUNCTION TestSumUDF;
  • [最佳实践] 使用Spark Jar作业读取和查询OBS数据
    操作场景DLI完全兼容开源的Apache Spark,支持用户开发应用程序代码来进行作业数据的导入、查询以及分析处理。本示例从编写Spark程序代码读取和查询OBS数据、编译打包到提交Spark Jar作业等完整的操作步骤说明来帮助您在DLI上进行作业开发。环境准备在进行Spark Jar作业开发前,请准备以下开发环境。表1 Spark Jar作业开发环境准备项说明操作系统Windows系统,支持Windows7以上版本。安装JDKJDK使用1.8版本。安装和配置IntelliJ IDEAIntelliJ IDEA为进行应用开发的工具,版本要求使用2019.1或其他兼容版本。安装Maven开发环境的基本配置。用于项目管理,贯穿软件开发生命周期。开发流程DLI进行Spark Jar作业开发流程参考如下:图1 Spark Jar作业开发流程表2 开发流程说明序号阶段操作界面说明1创建DLI通用队列DLI控制台创建作业运行的DLI队列。2上传数据到OBS桶OBS控制台将测试数据上传到OBS桶下。3新建Maven工程,配置pom文件IntelliJ IDEA参考样例代码说明,编写程序代码读取OBS数据。4编写程序代码5调试,编译代码并导出Jar包6上传Jar包到OBS和DLIOBS控制台将生成的Spark Jar包文件上传到OBS目录下和DLI程序包中。7创建Spark Jar作业DLI控制台在DLI控制台创建Spark Jar作业并提交运行作业。8查看作业运行结果DLI控制台查看作业运行状态和作业运行日志。步骤1:创建DLI通用队列第一次提交Spark作业,需要先创建队列,例如创建名为“sparktest”的队列,队列类型选择为“通用队列”。在DLI管理控制台的左侧导航栏中,选择“队列管理”。单击“队列管理”页面右上角“购买队列”进行创建队列。创建名为“sparktest”的队列,队列类型选择为“通用队列”。创建队列详细介绍请参考创建队列。单击“立即购买”,确认配置。配置确认无误,单击“提交”完成队列创建。步骤2:上传数据到OBS桶根据如下数据,创建people.json文件。{"name":"Michael"} {"name":"Andy", "age":30} {"name":"Justin", "age":19}进入OBS管理控制台,在“桶列表”下,单击已创建的OBS桶名称,本示例桶名为“dli-test-obs01”,进入“概览”页面。单击左侧列表中的“对象”,选择“上传对象”,将people.json文件上传到OBS桶根目录下。在OBS桶根目录下,单击“新建文件夹”,创建名为“result”的文件夹。单击“result”的文件夹,在“result”下单击“新建文件夹”,创建名为“parquet”的文件夹。步骤3:新建Maven工程,配置pom依赖以下通过IntelliJ IDEA 2020.2工具操作演示。打开IntelliJ IDEA,选择“File > New > Project”。图2 新建Project选择Maven,Project SDK选择1.8,单击“Next”。定义样例工程名和配置样例工程存储路径,单击“Finish”完成工程创建。如上图所示,本示例创建Maven工程名为:SparkJarObs,Maven工程路径为:“D:\DLITest\SparkJarObs”。在pom.xml文件中添加如下配置。<dependencies> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-sql_2.11</artifactId> <version>2.3.2</version> </dependency> </dependencies>图3 修改pom.xml文件在工程路径的“src > main > java”文件夹上鼠标右键,选择“New > Package”,新建Package和类文件。Package根据需要定义,本示例定义为:“com.huawei.dli.demo”,完成后回车。在包路径下新建Java Class文件,本示例定义为:SparkDemoObs。步骤4:编写代码编写SparkDemoObs程序读取OBS桶下的1的“people.json”文件,并创建和查询临时表“people”。完整的样例请参考完整样例代码参考,样例代码分段说明如下:导入依赖的包。import org.apache.spark.sql.Dataset; import org.apache.spark.sql.Row; import org.apache.spark.sql.SaveMode; import org.apache.spark.sql.SparkSession; import static org.apache.spark.sql.functions.col;通过当前帐号的AK和SK创建SparkSession会话spark 。SparkSession spark = SparkSession .builder() .config("spark.hadoop.fs.obs.access.key", "xxx") .config("spark.hadoop.fs.obs.secret.key", "yyy") .appName("java_spark_demo") .getOrCreate();"spark.hadoop.fs.obs.access.key"参数对应的值"xxx"需要替换为帐号的AK值。"spark.hadoop.fs.obs.secret.key"参数对应的值“yyy”需要替换为帐号的SK值。AK和SK值获取请参考:如何获取AK和SK。读取OBS桶中的“people.json”文件数据。其中“dli-test-obs01”为演示的OBS桶名,请根据实际的OBS桶名替换。Dataset<Row> df = spark.read().json("obs://dli-test-obs01/people.json"); df.printSchema();通过创建临时表“people”读取文件数据。df.createOrReplaceTempView("people");查询表“people”数据。Dataset<Row> sqlDF = spark.sql("SELECT * FROM people"); sqlDF.show();将表“people”数据以parquet格式输出到OBS桶的“result/parquet”目录下。sqlDF.write().mode(SaveMode.Overwrite).parquet("obs://dli-test-obs01/result/parquet"); spark.read().parquet("obs://dli-test-obs01/result/parquet").show();关闭SparkSession会话spark。spark.stop();步骤5:调试、编译代码并导出Jar包单击IntelliJ IDEA工具右侧的“Maven”,参考下图分别单击“clean”、“compile”对代码进行编译。编译成功后,单击“package”对代码进行打包。打包成功后,生成的Jar包会放到target目录下,以备后用。本示例将会生成到:“D:\DLITest\SparkJarObs\target”下名为“SparkJarObs-1.0-SNAPSHOT.jar”。步骤6:上传Jar包到OBS和DLI下登录OBS控制台,将生成的“SparkJarObs-1.0-SNAPSHOT.jar”Jar包文件上传到OBS路径下。将Jar包文件上传到DLI的程序包管理中,方便后续统一管理。登录DLI管理控制台,单击“数据管理 > 程序包管理”。在“程序包管理”页面,单击右上角的“创建”创建程序包。在“创建程序包”对话框,配置以下参数。包类型:选择“JAR”。OBS路径:程序包所在的OBS路径。分组设置和组名称根据情况选择设置,方便后续识别和管理程序包。单击“确定”,完成创建程序包。步骤7:创建Spark Jar作业登录DLI控制台,单击“作业管理 > Spark作业”。在“Spark作业”管理界面,单击“创建作业”。在作业创建界面,配置对应作业运行参数。具体说明如下:所属队列:选择已创建的DLI通用队列。例如当前选择步骤1:创建DLI通用队列创建的通用队列“sparktest”。作业名称(--name):自定义Spark Jar作业运行的名称。当前定义为:SparkTestObs。应用程序:选择步骤6:上传Jar包到OBS和DLI下中上传到DLI程序包。例如当前选择为:“SparkJarObs-1.0-SNAPSHOT.jar”。主类:格式为:程序包名+类名。例如当前为:com.huawei.dli.demo.SparkDemoObs。其他参数可暂不选择,想了解更多Spark Jar作业提交说明可以参考创建Spark作业。图4 创建Spark Jar作业单击“执行”,提交该Spark Jar作业。在Spark作业管理界面显示已提交的作业运行状态。步骤8:查看作业运行结果在Spark作业管理界面显示已提交的作业运行状态。初始状态显示为“启动中”。如果作业运行成功则作业状态显示为“已成功”,单击“操作”列“更多”下的“Driver日志”,显示当前作业运行的日志。图5 “Driver日志”中的作业执行日志如果作业运行成功,本示例进入OBS桶下的“result/parquet”目录,查看已生成预期的parquet文件。如果作业运行失败,单击“操作”列“更多”下的“Driver日志”,显示具体的报错日志信息,根据报错信息定位问题原因。例如,如下截图信息因为创建Spark Jar作业时主类名没有包含包路径,报找不到类名“SparkDemoObs”。可以在“操作”列,单击“编辑”,修改“主类”参数为正确的:com.huawei.dli.demo.SparkDemoObs,单击“执行”重新运行该作业即可。后续指引如果您想通过Spark Jar作业访问其他数据源,请参考《使用Spark作业跨源访问数据源》。如果您想通过Spark Jar作业在DLI创建数据库和表,请参考《使用Spark作业访问DLI元数据》。完整样例代码参考package com.huawei.dli.demo; import org.apache.spark.sql.Dataset; import org.apache.spark.sql.Row; import org.apache.spark.sql.SaveMode; import org.apache.spark.sql.SparkSession; import static org.apache.spark.sql.functions.col; public class SparkDemoObs { public static void main(String[] args) { SparkSession spark = SparkSession .builder() .config("spark.hadoop.fs.obs.access.key", "xxx") .config("spark.hadoop.fs.obs.secret.key", "yyy") .appName("java_spark_demo") .getOrCreate(); // can also be used --conf to set the ak sk when submit the app // test json data: // {"name":"Michael"} // {"name":"Andy", "age":30} // {"name":"Justin", "age":19} Dataset<Row> df = spark.read().json("obs://dli-test-obs01/people.json"); df.printSchema(); // root // |-- age: long (nullable = true) // |-- name: string (nullable = true) // Displays the content of the DataFrame to stdout df.show(); // +----+-------+ // | age| name| // +----+-------+ // |null|Michael| // | 30| Andy| // | 19| Justin| // +----+-------+ // Select only the "name" column df.select("name").show(); // +-------+ // | name| // +-------+ // |Michael| // | Andy| // | Justin| // +-------+ // Select people older than 21 df.filter(col("age").gt(21)).show(); // +---+----+ // |age|name| // +---+----+ // | 30|Andy| // +---+----+ // Count people by age df.groupBy("age").count().show(); // +----+-----+ // | age|count| // +----+-----+ // | 19| 1| // |null| 1| // | 30| 1| // +----+-----+ // Register the DataFrame as a SQL temporary view df.createOrReplaceTempView("people"); Dataset<Row> sqlDF = spark.sql("SELECT * FROM people"); sqlDF.show(); // +----+-------+ // | age| name| // +----+-------+ // |null|Michael| // | 30| Andy| // | 19| Justin| // +----+-------+ sqlDF.write().mode(SaveMode.Overwrite).parquet("obs://dli-test-obs01/result/parquet"); spark.read().parquet("obs://dli-test-obs01/result/parquet").show(); spark.stop(); } }
  • [问题求助] IDEA本地连接用Spark连接HIVE的问题,求助!!~
    首先:集群规模是健康的。连接的点是集群外。使用hive的beeline和spark的spark-beeline都能正常连接,和操作。但是用IDEA spark开发就出现问题了代码如下:报错如下:2022-05-27 23:29:21,610 [main] ERROR [org.apache.thrift.transport.TSaslTransport] - SASL negotiation failurejavax.security.sasl.SaslException: GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)]    at com.sun.security.sasl.gsskerb.GssKrb5Client.evaluateChallenge(GssKrb5Client.java:211)    at org.apache.thrift.transport.TSaslClientTransport.handleSaslStartMessage(TSaslClientTransport.java:94)。。。Caused by: GSSException: No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)    at sun.security.jgss.krb5.Krb5InitCredential.getInstance(Krb5InitCredential.java:147)。。。2022-05-27 23:29:21,631 [main] ERROR [org.apache.thrift.transport.TSaslTransport] - SASL negotiation failurejavax.security.sasl.SaslException: GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)]    at com.sun.security.sasl.gsskerb.GssKrb5Client.evaluateChallenge(GssKrb5Client.java:211)    at org.apache.thrift.transport.TSaslClientTransport.handleSaslStartMessage(TSaslClientTransport.java:94)。。。Exception in thread "main" org.apache.spark.sql.AnalysisException: java.lang.RuntimeException: java.lang.RuntimeException: Unable to instantiate org.apache.hadoop.hive.ql.metadata.SessionHiveMetaStoreClient;    at org.apache.spark.sql.hive.HiveExternalCatalog.withClient(HiveExternalCatalog.scala:107)    at org.apache.spark.sql.hive.HiveExternalCatalog.databaseExists(HiveExternalCatalog.scala:215)。。。Caused by: java.lang.RuntimeException: java.lang.RuntimeException: Unable to instantiate org.apache.hadoop.hive.ql.metadata.SessionHiveMetaStoreClient    at org.apache.hadoop.hive.ql.session.SessionState.start(SessionState.java:522)    at org.apache.spark.sql.hive.client.HiveClientImpl.newState(HiveClientImpl.scala:185)。。Caused by: java.lang.RuntimeException: Unable to instantiate org.apache.hadoop.hive.ql.metadata.SessionHiveMetaStoreClient    at org.apache.hadoop.hive.metastore.MetaStoreUtils.newInstance(MetaStoreUtils.java:1523)    at org.apache.hadoop.hive.metastore.RetryingMetaStoreClient.<init>(RetryingMetaStoreClient.java:86)。。。Caused by: java.lang.reflect.InvocationTargetException    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)。。。Caused by: MetaException(message:Could not connect to meta store using any of the URIs provided. Most recent failure: org.apache.thrift.transport.TTransportException: GSS initiate failed    at org.apache.thrift.transport.TSaslTransport.sendAndThrowMessage(TSaslTransport.java:232)    at org.apache.thrift.transport.TSaslTransport.open(TSaslTransport.java:316)    at org.apache.thrift.transport.TSaslClientTransport.open(TSaslClientTransport.java:37)。。。。
  • [其他] ModelArts Notebook亮点
    亮点特性1:远程开发 - 支持本地IDE远程访问Notebook新版Notebook提供了远程开发功能,通过开启SSH连接,用户本地IDE可以远程连接到ModelArts的Notebook开发环境中,调试和运行代码。对于使用本地IDE的开发者,由于本地资源限制,运行和调试环境大多使用团队公共搭建的CPU或GPU服务器,并且是多人共用,这带来一定的环境搭建和维护成本。而ModelArts的Notebook的优势是即开即用,它预先装好了不同的AI引擎,并且提供了非常多的可选规格,用户可以独占一个容器环境,不受其他人的干扰。只需简单配置,用户即可通过本地IDE连接到该环境进行运行和调试。图1 本地IDE远程访问Notebook开发环境ModelArts的Notebook可以视作是本地PC的延伸,均视作本地开发环境,其读取数据、训练、保存文件等操作与常规的本地训练一致。对于习惯使用本地IDE的开发者,使用远程开发方式,不影响用户的编码习惯,并且可以方便快捷的使用云上的Notebook开发环境。本地IDE当前支持VSCode、PyCharm、SSH工具。还有专门的插件PyCharm Toolkit和VSCode Toolkit,方便将云上资源作为本地的一个扩展。亮点特性2:预置镜像 - 即开即用,优化配置,支持主流AI引擎每个镜像预置的AI引擎和版本是固定的,在创建Notebook实例时明确AI引擎和版本,包括适配的芯片。亮点特性3:提供在线的交互式开发调试工具JupyterLabModelArts集成了基于开源的JupyterLab,可为您提供在线的交互式开发调试。您无需关注安装配置,在ModelArts管理控制台直接使用Notebook,编写和调测模型训练代码,然后基于该代码进行模型的训练。JupyterLab是一个交互式的开发环境,是Jupyter Notebook的下一代产品,可以使用它编写Notebook、操作终端、编辑MarkDown文本、打开交互模式、查看csv文件及图片等功能。
  • [经验分享] TBE DSL开发方式实现Tensorflow BatchNorm算子开发全流程(下)
    ## TBE DSL开发方式实现Tensorflow BatchNorm算子开发全流程(下) TBE DSL开发方式实现Tensorflow BatchNorm算子开发全流程(上) https://bbs.huaweicloud.com/forumreview/thread-187864-1-1.html TBE DSL开发方式实现Tensorflow BatchNorm算子开发全流程(下) https://bbs.huaweicloud.com/forum/thread-187872-1-1.html bilibili视频链接 https://www.bilibili.com/video/BV17U4y1m7G4 - 编译配置 ```python if is_training: tensor_list = [x_input, scale_input, offset_input] + list(res) else: tensor_list = [x_input, scale_input, offset_input, mean_input, variance_input] + list(res) config = {"name": kernel_name, "tensor_list": tensor_list} tbe.cce_build_code(sch, config) ``` ### 5.5 算子原型文件 ​ 算子原型定义规定了在昇腾AI处理器上可运行算子的约束,主要体现算子的数学含义,包含定义算子输入、输出和属性信息,基本参数的校验和shape的推导,原型定义的信息会被注册到GE的算子原型库中。网络模型生成时,GE会调用算子原型库的校验接口进行基本参数的校验,校验通过后,会根据原型库中的推导函数推导每个节点的输出shape与dtype,进行输出tensor的静态内存的分配。 ​ 算子的IR用于进行算子的描述,包括算子输入输出信息,属性信息等,用于把算子注册到算子原型库中,需要在算子的工程目录的/op_proto/算子名称\.h 和 /op_proto/算子名称.cc文件中进行实现。 **算子IR头文件.h** ```C /*宏定义*/ #ifndef BATCH_NORM_H #define BATCH_NORM_H /*包含头文件*/ #include "graph/operator_reg.h" /*原型注册*/ /*INPUT与.OUTPUT分别为算子的输入、输出Tensor的名称与数据类型,输入输出的顺序需要与算子代码实现函数形参顺序以及算子信息定义中参数的顺序保持一致。*/ namespace ge { REG_OP(BatchNorm) /*与插件适配文件中的算子类型保存一致,BatchNorm*/ .INPUT(x, TensorType({DT_FLOAT16, DT_FLOAT})) .INPUT(scale, TensorType({DT_FLOAT})) .INPUT(offset, TensorType({DT_FLOAT})) .OPTIONAL_INPUT(mean, TensorType({DT_FLOAT})) .OPTIONAL_INPUT(variance, TensorType({DT_FLOAT})) .OUTPUT(y, TensorType({DT_FLOAT16, DT_FLOAT})) .OUTPUT(batch_mean, TensorType({DT_FLOAT})) .OUTPUT(batch_variance, TensorType({DT_FLOAT})) .OUTPUT(reserve_space_1, TensorType({DT_FLOAT})) .OUTPUT(reserve_space_2, TensorType({DT_FLOAT})) .OUTPUT(reserve_space_3, TensorType({DT_FLOAT})) .ATTR(epsilon, Float, 0.0001) .ATTR(data_format, String, "NHWC") .ATTR(is_training, Bool, true) .OP_END_FACTORY_REG(BatchNorm) } // namespace ge /*结束条件编译*/ #endif // BATCH_NORM_H ``` **算子IR定义的.cc文件** ​ IR实现的cc文件中主要实现如下两个功能: - 算子参数的校验,实现程序健壮性并提高定位效率,对应Verify函数。 - 根据算子的输入张量描述、算子逻辑及算子属性,推理出算子的输出张量描述,包括张量的形状、数据类型及数据排布格式等信息。这样算子构图准备阶段就可以为所有的张量静态分配内存,避免动态内存分配带来的开销,对应InferShape函数。 ​ Verify函数主要校验算子内在关联关系,例如对于多输入算子,多个tensor的dtype需要保持一致,此时需要校验多个输入的dtype,其他情况dtype不需要校验。 ​ 实现Verify函数 ```c IMPLEMT_VERIFIER(BatchNorm, BatchNormVerify) { if (!CheckTwoInputDtypeSame(op, "scale", "offset")) { return GRAPH_FAILED; } return GRAPH_SUCCESS; } ``` ​ InferShape流程负责推导TensorDesc中的dtype与shape,只要全图所有首节点的TensorDesc确定了,就可以逐个向下传播,再由算子自身实现的Shape推导能力,就可以将全图所有OP的输入输出TensorDesc推导出来,推导结束后,全图的dtype与shape的规格就完全连续了。InferShape函数详细可参见算子原型定义。 ​ 实现InferShape函数 ```c IMPLEMT_INFERFUNC(BatchNorm, BatchNormInferShape) { std::string data_format; if (op.GetAttr("data_format", data_format) == GRAPH_SUCCESS) { if (data_format != "NHWC" && data_format != "NCHW") { string expected_format_list = ConcatString("NHWC, NCHW"); std::string err_msg = GetInputFormatNotSupportErrMsg("data_format", expected_format_list, data_format); VECTOR_INFER_SHAPE_INNER_ERR_REPORT(op.GetName(), err_msg); return GRAPH_FAILED; } } if (!OneInOneOutDynamicInfer(op, "x", {"y"})) { return GRAPH_FAILED; } if (!OneInOneOutDynamicInfer(op, "scale", {"batch_mean", "batch_variance", "reserve_space_1", "reserve_space_2"})) { return GRAPH_FAILED; } std::vector<int64_t> oShapeVector; auto op_info = OpDescUtils::GetOpDescFromOperator(op); auto output_desc = op_info->MutableOutputDesc("reserve_space_3"); if (output_desc != nullptr) { output_desc->SetShape(GeShape(oShapeVector)); output_desc->SetDataType(DT_FLOAT); } return GRAPH_SUCCESS; } ``` ​ 注册infershape方法和Verify方法 ```c INFER_FUNC_REG(BatchNorm, BatchNormInferShape); VERIFY_FUNC_REG(BatchNorm, BatchNormVerify); ``` ### 5.6 算子信息库文件 ​ 算子信息库作为算子开发的交付件之一,主要体现算子在昇腾AI处理器上的具体实现规格,算子开发者需要通过配置算子信息库文件,将算子在昇腾AI处理器上相关实现信息注册到算子信息库中,包括算子支持输入输出type、format以及输入shape等信息。网络运行时,FE会根据算子信息库中的算子信息做基本校验,选择dtype,format等信息,并根据算子信息库中信息找到对应的算子实现文件进行编译,用于生成算子二进制文件。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/16/1652685486841687985.png) 图17 算子信息库文件 ​ 参数说明请参见文档中的[表1](https://support.huaweicloud.com/usermanual-mindstudio302/atlasms_02_0151.html#ZH-CN_TOPIC_0000001134636812__zh-cn_topic_0238860099_zh-cn_topic_0213377578_table108272271412)。 ## 6. BatchNorm算子编译 ### 6.1 基本概念 ​ 算子交付件开发完成后,需要对算子工程进行编译,生成自定义算子安装包*.run,详细的编译操作包括: - 将TBE算子信息库定义文件*.ini编译成aic-{soc version}-ops-info.json。 - 将原型定义文件*.h**与*.cc编译成libcust_op_proto.so。 - 将TensorFlow/Caffe/Onnx算子的适配插件实现文件*.h**与*.cc编译成libcust*{tf|caffe|onnx}*parsers.so。 ### 6.2 编译操作 ​ MindStudio界面顶部,点击Build -> Edit Build Configurations。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/16/1652685515399241558.png) 图18 打开算子编译配置窗口 ​ 进入编译配置窗口,以远程编译为例,若未配置远程连接,则先在Deployment后面点击“+“进行配置,并在Envrionment Variables处进行如下配置,用户需将*/home/xxx/Ascend/ascend-toolkit/latest/*替换为CANN实际安装路径。 ```python ASCEND_TENSOR_COMPILER_INCLUDE=/home/xxx/Ascend/ascend-toolkit/latest/include ``` ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/16/1652685534193748835.png) 图19 算子编译配置 表1 算子编译配置参数说明 | 参数 | 说明 | | --------------------- | :----------------------------------------------------------- | | Build Configuration | 编译配置名称,默认为Build-Configuration | | Build Mode | 编译方式。Remote Build:远端编译。Local Build:本地编译。 | | Deployment | Remote Build模式下显示该配置。可以将指定项目中的文件、文件夹同步到远程指定机器的指定目录。 | | Environment variables | Remote Build模式下显示该配置。配置环境变量。 | | Target OS | Local Build模式下显示该配置。针对Ascend EP:选择昇腾AI处理器所在硬件环境的Host侧的操作系统。针对Ascend RC:选择板端环境的操作系统。 | | Target Architecture | Local Build模式下显示该配置。选择Target OS的操作系统架构。 | ​ 点击Build进行工程编译。编译完成后,显示远程编译完成 Information:build remotely finished。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/16/1652685581209869696.png) 图20 算子编译完成输出日志 ​ 在算子工程的cmake-build目录下生成了编译后的BacthNorm算子安装包,包含了操作系统及对应系统架构信息,如下图所示。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/16/1652685594940239372.png) 图21 算子编译生成安装包.run文件 ## 7. BatchNorm算子部署 ### 7.1 基本概念 ​ 算子部署指将算子编译生成的自定义算子安装包(*.run)部署到OPP算子库中。 - 推理场景下,自定义算子直接部署到开发环境的OPP算子库。 - 训练场景下,自定义算子安装包需要部署到运行环境的OPP算子库中。 ### 7.2 部署操作 ​ 在MindStudio工程界面,选中算子工程,点击顶部的Ascend > Operator Deployment,进入算子打包部署界面 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/16/1652685671812219732.png) 图22 打开算子部署配置窗口 ​ 如下算子部署界面,Operator Package即前面编译后生成的自定义算子安装包 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/16/1652685687145739273.png) 图23 算子部署配置 ​ 在Environment Variables中直接输入ASCEND_OPP_PATH=*/home/xxx/Ascend/ascend-toolkit/latest/opp。* */home/xxx/Ascend/ascend-toolkit/latest*为OPP组件(算子库)的安装路径,请根据实际情况配置。 ​ 也可以点击文本框后的图标,在弹出的对话框中填写。 - 在Name中输入环境变量名称:ASCEND_OPP_PATH。 - 在Value中输入环境变量值:*home/xxx/Ascend/ascend-toolkit/latest/opp*。 表2 算子部署配置参数说明 | 参数 | 说明 | | --------------------- | :----------------------------------------------------------- | | Build Configuration | 编译配置名称,默认为Build-Configuration | | Deployment | Remote Build模式下显示该配置。可以将指定项目中的文件、文件夹同步到远程指定机器的指定目录。 | | Environment variables | Remote Build模式下显示该配置。配置环境变量。 | ​ 点击Operator deploy进行算子部署,部署成功会出现以下输出日志。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/16/1652685711926780528.png) 图24 算子部署完成输出日志 ## 8. BatchNorm算子UT ### 8.1 基本概念 ​ 基于MindStudio进行算子开发的场景下,用户可基于MindStudio进行算子的UT,UT(Unit Test:单元测试)是开发人员进行算子代码验证的手段之一,主要目的是: - 测试算子代码的正确性,验证输入输出结果与设计的一致性。 - UT侧重于保证算子程序能够跑通,选取的场景组合应能覆盖算子代码的所有分支(一般来说覆盖率要达到100%),从而降低不同场景下算子代码的编译失败率。 ### 8.2 UT操作 ​ 开发人员可以执行当前工程中所有算子的UT用例,也可以执行单个算子的UT用例: - 右键单击“testcases/ut/ops_test”文件夹,选择Run TBE Operator'All'UT Impl with coverage,执行整个文件夹下算子实现代码的测试用例。 - 右键单击“testcases/ut/ops_test/*算子名称*”文件夹,选择Run TBE Operator'算子名称'UT Impl with coverage,执行单个算子实现代码的测试用例。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/16/1652685760344283999.png) 图25 第一次打开UT配置窗口 ​ 第一次运行时弹出UT配置窗口,进行如下配置后,点击OK执行算子UT。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/16/1652685812825725165.png) 图26 UT配置 表3 算子UT配置参数说明 | 参数 | 说明 | | ------------- | ------------------------------------------------------------ | | Name | 运行配置名称,用户可以自定义。 | | Compute Unit | 选择计算单元 | | SoC Version | 下拉选择当前版本的昇腾AI处理器类型。 | | Target | 运行环境。Simulator_Function:功能仿真环境。Simulator_TMModel:快速展示算子执行的调度流水线,不进行实际算子计算。 | | Operator Name | 选择运行的测试用例。 | | CANN Machine | CANN工具所在设备的deployment信息。 | | Case Names | 勾选需要运行的测试用例,即算子实现代码的UT Python测试用例。 | ​ 第二次执行UT文件不弹出运行配置,若需改配置,在顶部选择更改的文件,点击Edit Configurations即可修改。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/16/1652685836864375076.png) 图27 重新打开UT配置窗口 ​ 在Target选择Simulator_TMModel时,可以查看执行流水线,如下图所示图。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/16/1652685851174210467.png) 图28 UT流水线 ​ 点击Run,会有一个用例测试结果的url。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/16/1652685873123860112.png) 图29 UT完成输出日志url ​ 点击该url,查看UT的具体执行结果。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/16/1652685905458540416.png) 图30 UT测试结果详情 ​ 可点击页面中对应算子,进入UT用例覆盖率详情页面,通过绿色和红色标签区分是否覆盖。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/16/1652685935929754972.png) 图31 UT代码覆盖 ## 9. BatchNorm算子ST ### 9.1 基本概念 ​ 自定义算子部署到算子库(OPP)后,可进行ST(System Test),在真实的硬件环境中,验证算 子功能的正确性。 ​ ST的主要功能是: - 基于算子测试用例定义文件*.json生成单算子的om文件; - 使用AscendCL接口加载并执行单算子om文件,验证算子执行结果的正确性。ST会覆盖算子实现文件,算子原型定义与算子信息库,不会对算子适配插件进行测试。 ### 9.2 ST操作 ​ ST用例的创建与UT用例的创建类似: - 右键单击算子工程根目录,选择New Cases > ST Case。 - 右键单击算子信息定义文件:{工程名} /tbe /op_info_cfg/ai_core/SoC version} /xx\.ini,选择New Cases > ST Case。若已经存在了对应算子的ST Case,可以右键单击testcases目录,或者testcases > st目录,选择New Cases > ST Case,追加ST测试用例。 **执行ST** ​ 在工程目录testcases -> st -> batch_norm -> {SoC Version} -> xxx.json,找到保存的ST用例定义文件,右键单击选择Run ST Case ‘xxx.json',首次执行时,会弹出配置窗口中进行ST用例运行配置。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/16/1652685978330294571.png) 图32 第一次打开ST配置窗口 ​ 执行ST用例,其中在Advanced options下勾选Enable Profiling,可获取算子在昇腾AI处理器上的性能数据,该功能需要将运行环境中的msprof工具所在路径配置到PATH环境变量中,可在.bashrc文件中添加如下语句: ``` export PATH="$PATH:/usr/local/Ascend/ascend-toolkit/latest/tools/profiler/bin/" ``` ​ 参考如下配置,其中环境变量配置如表4 表4 ST环境变量配置说明 | Name | Value | | ------------------ | ------------------------------------------------------------ | | ASCEND_DRIVER_PATH | /usr/local/Ascend/driver | | ASCEND_HOME | /usr/local/Ascend//ascend-toolkit/latest | | LD_LIBRARY_PATH | \${ASCEND_DRIVER_PATH}/lib64:\${ASCEND_HOME}/lib64:$LD_LIBRARY_PATH | ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/16/1652686009083506815.png) 图33 ST配置 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/16/1652686024451963882.png) 图34 ST性能数据日志 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/16/1652686051628816038.png) 图35 ST完成输出日志 ​ 在’*算子工程根目录*/testcases/st/out/‘目录下生成测试数据和测试代码,并编译出可执行文件,在指定的硬件设备上执行测试用例。在’*算子工程根目录*/testcases/st/out/‘目录下生成的st_report.json文件记录了测试情况,可参考MindStudio文档中的ST测试执行结果表。 ## 10. 关于BatchNorm算子开发的FAQ 1. 创建的BatchNorm算子工程,代码的头文件或者导入的依赖显示为红色。 ​ 首先检查是否参考MindStudio的安装指南安装好对应的依赖和环境,其次算子工程是在服务器端的 CANN框架下运行的,但是工程文件保存在Windows端本地,本地没CANN环境会显示为红色,但是不会影响远端工程的运行。 2. 算子流程中的编译、部署和ST测试中如何设置相应的环境变量。 ​ 参考MindStudio用户手册中的用户指南,在自定义算子开发章节下的TBE算子开发(TensorFlow/Caffe)小节中,有详细讲解如何设置环境变量。 3. 算子UT测试中无法生成流水线图。 ​ 在UT测试的配置框中,Target选择Stimulator_TMModel,UT执行完成后可生成流水线图。 4. ST测试中,勾选了Enable Profiling后的ST日志里,没有出现性能数据,并提示msprof command未找到。 ​ 进入Linux服务器,在.bashrc文件最底部中添加export PATH="$PATH:/usr/local/Ascend/ascend-toolkit/latest/tools/profiler/bin/",其中路径需配置到msprof工具实际所在bin目录下。配置完成后,则无该提示并出现性能数据。 5. 如何打开UT测试和ST测试的运行配置窗口。 ​ 第一次进行UT测试和ST测试时,运行前会自动打开配置窗口让用户进行配置,第二次开始就会按照第一次的默认配置进行执行,只需在MindStudio界面上方的绿色锤子旁边先选择UT或ST的配置文件,再点击Edit Configurations即可再次打开配置窗口。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/16/1652686103458137124.png) 图36 再次打开UT、ST配置窗口
  • [经验分享] PyTorch框架使用DSL进行TBE算子开发全流程(上)
    PyTorch框架使用DSL进行TBE算子开发全流程(上)PyTorch框架使用DSL进行TBE算子开发全流程(上) [https://bbs.huaweicloud.com/forumreview/thread-187817-1-1.html](https://bbs.huaweicloud.com/forumreview/thread-187817-1-1.html) PyTorch框架使用DSL进行TBE算子开发全流程(下) [https://bbs.huaweicloud.com/forum/thread-187818-1-1.html](https://bbs.huaweicloud.com/forum/thread-187818-1-1.html) bilibili视频链接 [https://www.bilibili.com/video/BV1WZ4y1a74i](https://www.bilibili.com/video/BV1WZ4y1a74i) ## 1. DSL算子基本概念介绍 ### 1.1 什么是算子 深度学习算法由一个个计算单元组成,我们称这些计算单元为算子(Operator,简称 OP)。在网络模型中,算子对应层中的计算逻辑,例如:卷积层(Convolution Layer)是一个算子;全连接层(Fully-connected Layer, FC layer)中的权值求和过 程,是一个算子。 对每一个独立的算子,用户需要编写算子描述文件,描述算子的整体逻辑、计算步骤以及相关硬件平台信息等。然后用深度学习编译器对算子描述文件进行编译,生成可在特定硬件平台上运行的二进制文件后,将待处理数据作为输入,运行算子即可得到期望输出。将神经网络所有被拆分后的算子都按照上述过程处理后, 再按照输入输出关系串联起来即可得到整网运行结果。 ![图1:算子全流程执行](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/15/1652619658388940256.png)图1:算子全流程执行### 1.2 什么是TBE算子 TBE(Tensor Boost Engine)提供了基于开源深度学习编译栈TVM框架的自定义算子开发能力,通过TBE提供的API可以完成相应神经网络算子的开发。 一个完整的TBE算子包含四部分:算子原型定义、对应开源框架的算子适配插件、算子信息库定义和算子实现。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/15/1652619678959225788.png)图2:TBE算子全流程执行昇腾AI软件栈提供了TBE(Tensor Boost Engine:张量加速引擎)算子开发框架,开发者可以基于此框架使用Python语言开发自定义算子,通过TBE进行算子开发有以下几种方式: * DSL(Domain-Specific Language)开发 * TIK(Tensor Iterator Kernel)开发 ### 1.3 什么是DSL算子 为了方便开发者进行自定义算子开发,TBE(Tensor Boost Engine)提供了一套计算接口供开发者用于组装算子的计算逻辑,使得70%以上的算子可以基于这些接口进行开发,极大的降低自定义算子的开发难度。TBE提供的这套计算接口,称之为DSL(Domain-Specific Language)。基于DSL开发的算子,可以直接使用TBE提供的Auto Schedule机制,自动完成调度过程,省去最复杂的调度编写过程。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/15/1652619698309720196.png)图3:DSL算子介绍#### 1.3.1 DSL功能框架 1. 开发者调用TBE提供的DSL接口进行计算逻辑的描述,指明算子的计算方法和步骤。 2. 计算逻辑开发完成后,开发者可调用Auto Schedule接口,启动自动调度,自动调度时TBE会根据计算类型自动选择合适的调度模板,完成数据切块和数据流向的划分,确保在硬件执行上达到最优。 3. Auto Schedule调度完成后,会生成类似于TVM的I R(Intermediate Representation)的中间表示。 4. 编译优化(Pass)会对算子生成的IR进行编译优化,优化的方式有双缓冲(Double Buffer)、流水线(Pipeline)同步、内存分配管理、指令映射、分块适配矩阵计算单元等。 5. 算子经Pass处理后,由CodeGen生成类C代码的临时文件,这个临时代码文件可通过编译器生成算子的实现文件,可被网络模型直接加载调用。 #### 1.3.2 DSL计算接口 TBE DSL提供的计算接口主要涵盖向量运算,包括Math、NN、Reduce、卷积、矩阵计算等接口。 ## 2. 算子开发流程介绍 算子开发通常包括以下步骤: * 环境准备:配置MindStudio算子开发环境 * 算子分析:明确算子的功能及数学表达式,选择算子开发方式 * 工程创建:利用MindStudio创建算子工程 * 算子原型定义:规定在昇腾AI处理器上可运行算子的约束 * 算子代码实现:使用DSL或TIK实现算子 * 算子信息库定义:规定算子在昇腾AI处理器上的具体实现规格 * 算子UT测试:测试算子代码的正确性 * 算子工程编译:编译出可直接安装的自定义算子run包 * 算子工程部署:将自定义算子部署到opp算子库 * 算子ST测试:在真实的硬件环境中,使用离线模型验证算子功能的正确性 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/15/1652619751270326605.png)图4:算子开发流程介绍## 3.算子分析 进行算子开发前,开发者应首先进行算子分析,算子分析包含:明确算子的功能及数学表达式,选择算子开发方式(DSL方式或者TIK方式),最后细化并明确算子规格,分析流程如下所示: ![算子分析](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/15/1652619416688121833.png)图5:创建工程1#### 3.1分析算子算法原理,提取算子的数学表达式 当$x=\left[x_{1}, x_{2}, \cdots, x_{n}\right]^{\mathrm{T}}$时那么$x$的$p$范数为 $$ \|x\|_{p}=\left(\left|x_{1}\right|^{p}+\left|x_{2}\right|^{p}+\cdots+\left|x_{n}\right|^{p}\right)^{\frac{1}{p}} $$ 当$p$取0时,表示向量中非零元素的个数(即为其稀疏度) 当$p$取1,2,$\infty$时分别是以下几种最简单的情形 $$ \|x\|_{1}=\left|x_{1}\right|+\left|x_{2}\right|+\ldots+\left|x_{n}\right| $$ $$ \|x\|_{2}=\left(\left|x_{1}\right|^{2}+\left|x_{2}\right|^{2}+\cdots+\left|x_{n}\right|^{2}\right)^{\frac{1}{2}} $$ $$ \|x\|_{\infin}=max(\left|x_{1}\right|,\left|x_{2}\right|,\ldots,\left|x_{n}\right|) $$ #### 3.2明确算子开发方式及使用的计算接口 lp_norm 用到的算子有 + ,幂次方,取最大值,取绝对值 * +:可通过tbe.dsl.vadd接口实现。 * 取最大值:可以通过tbe.dsl.reduce_max接口实现。 * 取绝对值:可以通过tbe.dsl.vabs接口实现 * 幂次方:可以通过tbe.dsl.vmuls, tbe.dsl.exp , tbe.dsl.log 接口实现 通过如上分析,可看出TBE DSL接口满足lp_norm算子的实现要求,所以可优先选择TBE DSL方式进行lp_norm算子的实现。 ## 4.创建Pytorch TBE DSL算子工程 ### 4.1安装和配置环境 按照[MindStudio用户手册](https://www.hiascend.com/document/detail/zh/mindstudio/304/progressiveknowledge/index.html)中的安装指南-->安装操作来安装MindStudio。 除了手册中要求的环境依赖,也需要安装tensorflow,推荐1.15版本 如果采用windows和linux 的公开发环境,需要在本地和远端均配置好所需环境。 ### 4.2创建工程 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/15/1652619797098226319.png)图6:创建工程1![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/15/1652619804874706832.png)图7:创建工程2![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/15/1652619821616839895.png)图8:创建工程3* 创建工程成功 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/15/1652619844224122169.png)图9:创建工程成功* 设置python SDK ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/15/1652619857900113079.png)图10:设置python SDK## 5.算子工程结构和交付件介绍 ``` lp_norm: ├─.idea ├─build //编译生成的中间文件 ├─cmake ├─cmake-build ├─framework ├─log ├─metadef ├─op_proto //算子IR定义文件夹 │ ├─lp_norm.cc //算子IR定义 │ ├─lp_norm.h //算子IR定义 ├─op_tiling ├─out ├─profiling ├─scripts ├─tbe │ ├─impl │ │ ├─dynamic //dynamic operator │ │ ├─util //工具类 │ │ └─lp_norm.py //算子python实现 │ └─op_info_cfg │ └─ai_core │ ├─ascend310 │ ├─ascend710 │ └─ascend910 │ └─lp_norm.ini //ascend910算子信息库文件 ├─testcases //UT、ST测试相关目录 │ ├─st //ST测试相关目录 │ │ ├─lp_norm │ │ │ └─ascend310 │ │ │ └─LpNorm_case_20210527144255.json //ST测试用例 │ └─ut //UT测试相关目录 │ └─ops_test │ └─lp_norm │ └─test_lp_norm_impl.py //UT测试入口及测试用例 ├─third_party //工程第三方依赖 └─CMakeLists.txt //编译规则文件 ``` ### 5.1 lp_norm算子原型 lp_norm算子的原型为pytorch中的torch.norm ```python torch.norm(input, p='fro', dim=None, keepdim=False, out=None, dtype=None) ``` ### 5.2 算子python实现代码 ```python def lp_norm(x, y, p=2, axes=None, keepdim=False, epsilon=1e-12, kernel_name="lp_norm"): # 算子校验 para_check.check_kernel_name(kernel_name) xtype_list = ["float16", "float32"] x_type = x.get("dtype").lower() x_shape = x.get("shape") para_check.check_dtype(x_type, xtype_list) para_check.check_shape(x_shape) p_inf_list = ("inf", "-inf") no_shape = len(x_shape) if isinstance(axes, int): axes = [axes] if axes is None: axes = [i for i in range(no_shape)] if len(axes) == 0: axes = [i for i in range(no_shape)] input_data = tvm.placeholder(x_shape, dtype=x_type, name="input_data") abs_data = tbe.vabs(input_data) #对所有输入取绝对值 # 对p分情况 if (p in p_inf_list) or (p == _CONST_INF) or (p == -_CONST_INF - 1): res = lp_norm_inf_compute(abs_data, x_type, y, p, axes, keepdim, kernel_name) elif p == 0: res = lp_norm0_compute(abs_data, x_type, y, axes, keepdim, kernel_name) elif p == 1: res = lp_norm1_compute(abs_data, x_type, y, axes, keepdim, kernel_name) elif p == 2: res = lp_norm2_compute(abs_data, x_type, y, axes, keepdim, kernel_name) else: res = lp_norm_compute(abs_data, x_type, y, p, axes, keepdim, kernel_name) if x_type == "float16" and float(epsilon) <= _CONST_EPSILON_FP16: std_no = tvm.const(_CONST_EPSILON_FP16, dtype=x_type) else: std_no = tvm.const(float(epsilon), dtype=x_type) res = tbe.vmaxs(res, std_no) # 自动调度 with tvm.target.cce(): schedule = tbe.auto_schedule(res) # 算子编译 config = {"name": kernel_name, "tensor_list": [input_data, res]} tbe.cce_build_code(schedule, config) ``` * 无穷范数的计算 $$ \|x\|_{\infin}=max(\left|x_{1}\right|,\left|x_{2}\right|,\ldots,\left|x_{n}\right|) $$ ```python if (p == "inf") or (p == _CONST_INF): res = tbe.reduce_max(abs_x, axis=axes, keepdims=keepdim, priority_flag=True) #对所有值取最大值 else: # p is "-inf" res = tbe.reduce_min(abs_x, axis=axes, keepdims=keepdim, priority_flag=True) #当p为-inf对所有值取最小值 ``` * 0范数的计算 * 0范数表示向量中非零元素的个数(即为其稀疏度) ```python zero_tensor = tbe.vmuls(abs_x, tvm.const(0, dtype=abs_x.dtype)) one_tensor = tbe.vadds(zero_tensor, tvm.const(1, dtype=abs_x.dtype)) ele_tensor = tbe.vcmpsel(abs_x, zero_tensor, 'ne', one_tensor, zero_tensor) res = tbe.sum(ele_tensor, axis=axes, keepdims=keepdim) ``` * 1范数的计算 $$ \|x\|_{1}=\left|x_{1}\right|+\left|x_{2}\right|+\ldots+\left|x_{n}\right| $$ ```python res = tbe.sum(abs_x, axis=axes, keepdims=keepdim) #对所有值求和 ``` * 2范数的计算 $$ \|x\|_{2}=\left(\left|x_{1}\right|^{2}+\left|x_{2}\right|^{2}+\cdots+\left|x_{n}\right|^{2}\right)^{\frac{1}{2}} $$ ```python pow_x = tbe.vmul(abs_x, abs_x) #求平方 sum_pow = tbe.sum(pow_x, axis=axes, keepdims=keepdim) #求和 res = tbe.vsqrt(sum_pow, priority_flag=1) #求1/2次方 ``` * p范数的计算 $$ \|x\|_{p}=\left(\left|x_{1}\right|^{p}+\left|x_{2}\right|^{p}+\cdots+\left|x_{n}\right|^{p}\right)^{\frac{1}{p}} $$ ```python prod_x = abs_x for p_ix in range(1, p): #求p次方 prod_x = tbe.vmul(prod_x, abs_x) sum_prod_x = tbe.sum(prod_x, axis=axes, keepdims=keepdim) #求和 # 将x的p次方转化 x^p --> exp(log(x)/p) if "910" in _CCE_PLAT: log_sum_x = tbe.vlog(sum_prod_x, priority_flag=1) #取对数 else: log_sum_x = tbe.vlog(sum_prod_x) zero_tensor = tbe.vmuls(log_sum_x, tvm.const(0, dtype=log_sum_x.dtype)) p_tensor = tbe.vadds(zero_tensor, tvm.const(p, dtype=log_sum_x.dtype)) div_log_x = tbe.vdiv(log_sum_x, p_tensor) #除以p exp_div_x = tbe.vexp(div_log_x) #e的幂次方 ``` ## 6.介绍算子原型库、算子信息库代码 算子原型定义规定了在昇腾AI处理器上可运行算子的约束,主要体现算子的数学含义,包含定义算子输入、输出和属性信息,基本参数的校验和shape的推导,原型定义的信息会被注册到GE的算子原型库中。网络模型生成时,GE会调用算子原型库的校验接口进行基本参数的校验,校验通过后,会根据原型库中的推导函数推导每个节点的输出shape与dtype,进行输出tensor的静态内存的分配。 算子的IR用于进行算子的描述,包括算子输入输出信息,属性信息等,用于把算子注册到算子原型库中。 算子的IR需要在算子的工程目录的/op_proto/算子名称.h 和 /op_proto/算子名称.cc文件中进行实现。 ### 6.1算子 IR 头文件.h 注册代码实现 ```c #ifndef GE_OP_LP_NORM_H #define GE_OP_LP_NORM_H //头文件 #include "graph/operator_reg.h" #include "graph/operator.h" namespace ge { // 原型注册 REG_OP(LpNorm) .INPUT(x, TensorType({DT_FLOAT16, DT_FLOAT})) .OUTPUT(y, TensorType({DT_FLOAT16, DT_FLOAT})) .ATTR(p, Int, 2) .ATTR(axes, ListInt, {}) .ATTR(keepdim, Bool, false) .ATTR(epsilon, Float, 1e-12) .OP_END_FACTORY_REG(LpNorm) } // namespace ge #endif // GE_OP_LP_NORM_H ``` ### 6.2 算子IR定义的 .cc 注册代码实现 IR实现的cc文件中主要实现如下两个功能: * 算子参数的校验,实现程序健壮性并提高定位效率。 * 根据算子的输入张量描述、算子逻辑及算子属性,推理出算子的输出张量描述,包括张量的形状、数据类型及数据排布格式等信息。这样算子构图准备阶段就可以为所有的张量静态分配内存,避免动态内存分配带来的开销。 ##### 6.2.1实现InferShape方法 InferShape流程负责推导TensorDesc中的dtype与shape,推导结束后,全图的dtype与shape的规格就完全连续了。 如果生成网络模型时产生的GE Dump图“ge_proto_000000xx_after_infershape.txt”中存在dtype与shape规格不连续的情况,说明InferShape处理有错误。 ```c //实现inferShape方法 IMPLEMT_COMMON_INFERFUNC(LpNormInfer) { auto tensor_input = op.GetInputDesc("x"); Shape x_shape = tensor_input.GetShape(); DataType x_type = tensor_input.GetDataType(); Format x_format = tensor_input.GetFormat(); size_t dim_num = op.GetInputDesc("x").GetShape().GetDimNum(); std::vector<int64_t> x_axes = {}; std::vector<int64_t> new_axes = {}; std::vector<int64_t> y_vec = {}; std::vector<int64_t> x_dim_members = x_shape.GetDims(); bool keep_dim = false; int32_t indice; (void)op.GetAttr("keepdim", keep_dim); if (x_axes.empty()) { for (int32_t i = 0; i < dim_num; i++) { new_axes.push_back(i); } } else { for (int32_t i = 0; i < x_axes.size(); i++) { indice = (x_axes<i> < 0) ? (x_axes<i> + dim_num) : x_axes<i>; new_axes.push_back(indice); } } for (int32_t i = 0; i < x_shape.GetDimNum(); i++) { if (find(new_axes.begin(), new_axes.end(), i) != new_axes.end()) { if (keep_dim == true) { y_vec.push_back(1); } } else { y_vec.push_back(x_dim_members<i>); } } ge::Shape output_shape(y_vec); // update output desc ge::TensorDesc output_desc = op.GetOutputDesc("y"); output_desc.SetShape(output_shape); output_desc.SetDataType(x_type); output_desc.SetFormat(x_format); (void)op.UpdateOutputDesc("y", output_desc); return GRAPH_SUCCESS; } ``` ##### 6.2.2实现verify方法 Verify函数主要校验算子内在关联关系,例如对于多输入算子,多个tensor的dtype需要保持一致,此时需要校验多个输入的dtype,其他情况dtype不需要校验。 ```c // 实现verify方法 IMPLEMT_VERIFIER(LpNorm, LpNormVerify) { return GRAPH_SUCCESS; } 注册InferShape方法与Verify方法 ``` ```c // 注册infershape方法和Verify方法 COMMON_INFER_FUNC_REG(LpNorm, LpNormInfer); VERIFY_FUNC_REG(LpNorm, LpNormVerify); ``` ### 6.3算子信息库 算子信息库作为算子开发的交付件之一,主要体现算子在昇腾AI处理器上的具体实现规格,包括算子支持输入输出dtype、format以及输入shape等信息。网络运行时,FE会根据算子信息库中的算子信息做基本校验,选择dtype,format等信息,并根据算子信息库中信息找到对应的算子实现文件进行编译,用于生成算子二进制文件。 如图为Ascend910的算子信息库文件 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/15/1652619895986837868.png)图11:Ascend910的算子信息库文件## 7.算子编译 算子交付件开发完成后,需要对算子工程进行编译,生成自定义算子安装包*.run,详细的编译操作包括: * 将TBE算子信息库定义文件*.ini编译成aic-{soc version}-ops-info.json * 将原型定义文件*.h与*.cc编译成libcust_op_proto.so。 * 将TensorFlow/Caffe/Onnx算子的适配插件实现文件*.h与*.cc编译成libcust_{tf|caffe|onnx}_parsers.so。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/15/1652619910425190594.png)图12:算子编译* 选择Remote Build 进行远程部署 * 配置环境变量ASCEND_TENSOR_COMPILER_INCLUDE=/usr/local/Ascend/ascend-toolkit/latest/include * 点击执行Build ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/15/1652619930192768196.png)图13:算子编译设置| 参数 | 说明 | | --------------------- | :----------------------------------------------------------- | | Build Configuration | 编译配置名称,默认为Build-Configuration | | Build Mode | 编译方式。Remote Build:远端编译。Local Build:本地编译。 | | Deployment | Remote Build模式下显示该配置。可以将指定项目中的文件、文件夹同步到远程指定机器的指定目录。 | | Environment variables | Remote Build模式下显示该配置。配置环境变量。 | | Target OS | Local Build模式下显示该配置。针对Ascend EP:选择昇腾AI处理器所在硬件环境的Host侧的操作系统。针对Ascend RC:选择板端环境的操作系统。 | | Target Architecture | Local Build模式下显示该配置。选择Target OS的操作系统架构。 | 编译成功之后会在在cmake-build目录下生成自定义算子安装包*.run ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/15/1652619990255727776.png)图14:算子编译成功## 8.算子部署 算子部署指将算子编译生成的自定义算子安装包(*.run)部署到OPP算子库中。在推理场景下,自定义算子直接部署到开发环境的OPP算子库。在训练场景下,自定义算子安装包需要部署到运行环境的OPP算子库中。 * 选择Ascend->Operator Deployment * 选择Operator Deploy Remotely 进行远程部署 * 设置Environment Variables * ASCEND_OPP_PATH=/usr/local/Ascend/ascend-toolkit/latest/opp * 点击Operator deploy进行部署 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/15/1652620006445338984.png)图15:算子部署参数设置| 参数 | 说明 | | --------------------- | :----------------------------------------------------------- | | Build Configuration | 编译配置名称,默认为Build-Configuration | | Deployment | Remote Build模式下显示该配置。可以将指定项目中的文件、文件夹同步到远程指定机器的指定目录。 | | Environment variables | Remote Build模式下显示该配置。配置环境变量。 | ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/15/1652620018912463269.png)图16:算子部署成功
  • [经验分享] PyTorch 框架 AICPU 算子开发全流程(下)
    PyTorch 框架 AICPU 算子开发全流程(上):https://bbs.huaweicloud.com/forum/forum.php?mod=viewthread&tid=187229&page=1&authorid=&replytype=&extra=#pid1461187 PyTorch 框架 AICPU 算子开发全流程(中):https://bbs.huaweicloud.com/forum/thread-187231-1-1.html PyTorch 框架 AICPU 算子开发全流程(下):https://bbs.huaweicloud.com/forumreview/thread-187233-1-1.html 详情指路视频专栏:https://www.bilibili.com/video/BV1nS4y1b7Km ## 八.LogSpace算子UT测试 1. **基本概念** MindStudio提供了基于gtest框架的新的UT测试方案,简化了开发者开发UT测试用例的复杂度。UT(Unit Test:单元测试)是开发人员进行单算子运行验证的手段之一,主要目的是: - 测试算子代码的正确性,验证输入输出结果与设计的一致性。 - UT侧重于保证算子程序能够跑通,选取的场景组合应能覆盖算子代码的所有分支(一般来说覆盖率要达到100%),从而降低不同场景下算子代码的编译失败率。 2. **前提条件** - 需完成自定义算子的开发,包括算子实现代码和算子原型定义。 - 安装CMake,要求版本为3.14及以上,若CMake版本不满足要求,请升级CMake版本。 - MindStudio已连接硬件设备。 3. **操作步骤** - 创建UT测试用例,有以下三种方式。 1. 右键单击算子工程根目录,选择“New Cases > AI CPU UT Case”。 2. 若已经存在了算子的UT测试用例,可以右键单击“testcases”目录。 3. 或者“testcases > ut”目录,选择“New Cases > AI CPU UT Case”,创建UT测试用例。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/9/1652078803695547959.png) - 在弹出的算子选择界面,选择需要创建UT测试用例的算子,单击OK。创建完成后,会在算子工程根目录下生成testcases文件夹,并生成ut测试用例。 - 编写算子实现代码的UT C++测试用例。 在“testcases/ut/aicpu_test/reshape_cust/test_log_space_impl.cc”文件中,编写算子实现代码的UT C++测试用例,计算出算子执行结果,并取回结果和预期结果进行比较,来测试算子逻辑的正确性。 ``` TEST_F(LogSpaceTest, log_space_test_case_1) { vector> shapes = {{1}, {1}, {1, 10}, {1}}; vector data_types = {DT_FLOAT, DT_FLOAT, DT_FLOAT}; //输入输出数据定义 float input1[1] = {2}; float input2[1] = {10}; float output[10] = {(float)0}; float expect_out[10] = {4.0, 7.4069977, 13.715903, 25.398417, 47.031506, 87.09056, 161.2699, 298.63144, 552.9906, 1024.0}; vector datas = { (void *)input1, (void *)input2, (void *)output }; CREATE_NODEDEF(shapes, data_types, datas); CpuKernelContext ctx(DEVICE); EXPECT_EQ(ctx.Init(node_def.get()), 0); //定义算子 LogSpaceCpuKernel cpuKernel; //计算输入数据 cpuKernel.Compute(ctx); //对比计算输出是否正确 bool compare1 = CompareResult(output, expect_out, 6); EXPECT_EQ(compare1, true); } ``` - 编写算子原型定义的UT C++测试用例。在“testcases/ut/aicpu_test/reshape_cust/test_log_space_proto.cc”文件中,编写算子原型定义的UT C++测试用例,用于定义算子实例、更新算子输入输出并调用InferShapeAndType函数,最后验证InferShapeAndType函数执行过程及结果的正确性。 ``` TEST_F(LogSpaceTest, logspace_test_end_failed) { ge::op::LogSpace LogSpace_op; std::int64_t attr_value = 1; LogSpace_op.SetAttr("dtype", attr_value); std::int64_t num_steps_value = 4; LogSpace_op.SetAttr("steps", num_steps_value); ge::TensorDesc tensorStartDesc; ge::TensorDesc tensorEndDesc; ge::Shape shape0({1}); tensorStartDesc.SetDataType(ge::DT_FLOAT16); tensorStartDesc.SetShape(shape0); tensorStartDesc.SetOriginShape(shape0); LogSpace_op.UpdateInputDesc("start", tensorStartDesc); ge::Shape shape({2,3}); tensorEndDesc.SetDataType(ge::DT_FLOAT16); tensorEndDesc.SetShape(shape); tensorEndDesc.SetOriginShape(shape); LogSpace_op.UpdateInputDesc("end", tensorEndDesc); auto ret = LogSpace_op.VerifyAllAttr(true); EXPECT_EQ(ret, ge::GRAPH_FAILED); } ``` - 运行算子实现文件的UT测试用例。 开发人员可以执行当前工程中所有算子的UT测试用例,也可以执行单个算子的UT测试用例。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/9/1652078831696896868.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/9/1652078849266187820.png) - 右键单击“**testcases/ut/aicpu_test**”文件夹,选择Run AI CPU Operator‘All’UT Impl with coverage,运行整个文件夹下算子实现代码的测试用例。 - 右键单击“**testcases/ut/aicpu_test**算子名称”文件夹,选择Run AI CPU Operator ‘算子名称’ UT Impl with coverage,运行单个算子实现代码的测试用例。 第一次运行时会弹出运行配置页面,请参考配置,然后单击Run。后续如需修改运行配置,请参考修改运行配置。 - **查看运行结果**。 运行完成后,通过界面下方的日志打印窗口,查看运行结果。结果中展示此次一共运行几个用例,如下图。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/9/1652078886866477245.png)表6 UT测试参数配置信息| **参数** | **说明** | | -------------- | ------------------------------------------------------------ | | Name | 运行配置名称,用户可以自定义。 | | Test Type | 选择ut_impl。 | | Compute Unit | 选择计算单元:l AI Core/Vector Core;l AI CPU。选择不同的计算单元可以实现AI Core/Vector Core和AICPU_UT测试配置界面的切换。 | | Operator Name | 选择运行的测试用例。all表示运行所有用例。其他表示运行某个算子下的测试用例。 | | Case Names | 勾选需要运行的测试用例,即算子实现代码的UT C++测试用例。支持全选和全不选所有测试用例。 | ## 九.LogSpace算子ST测试 1. **概述** MindStudio提供了新的ST(System Test)测试框架,可以自动生成测试用例,在真实的硬件环境中,验证算子功能的正确性和计算结果准确性,并生成运行测试报告,包括: - 基于算子信息库生成算子测试用例定义文件。 - 基于算子测试用例定义文件生成不同shape、dtype的测试数据和基于AscendCL的测试用例。 - 编译算子工程并将算子部署到算子库,最后在硬件环境中执行测试用例,验证算子运行的正确性。 - 自动生成运行报表(st_report.json)功能,报表记录了测试用例信息及各阶段运行情况。 - 根据用户定义并配置的算子期望数据生成函数,回显期望算子输出和实际算子输出的对比测试结果,验证计算结果的准确性。 2. **前提条件** - 完成自定义算子的开发,请参见算子代码实现、算子原型定义、算子信息库定义。 - lMindStudio已连接硬件设备。 3. **生成ST测试用例定义文件** - 创建ST测试用例。有以下三种入口: 1) 右键单击算子工程根目录,选择“**New Cases > ST Case**”。 2) 右键单击算子信息定义文件:{工程名}**/cpukernel/op_info_cfg/aicpu_kernel/ xx.ini**,选择“**New Cases > ST Case**”。 3) 若已经存在了对应算子的ST Case,可以右键单击“testcases”目录,或者“**testcases > st**”目录,选择“**New Cases > ST Case**”,追加ST测试用例。 - 在弹出的“Create ST Cases for an Operator”界面中选择需要创建ST测试用例的算子。如下图所示。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/9/1652078947676797661.png) 1. Operator Name:下拉选择算子名称。 2. SoC Version:下拉选择昇腾AI处理器的类型。若对AI CPU算子进行ST测试,默认选择aicpu_kernel。 3. 单击“OK”后,工具会自动根据首层shape信息dump出选择算子的shape信息,生成对应的算子测 试用例定义文件。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/9/1652079084124462040.png) - 配置算子测试用例定义文件。在算子测试用例文件中配置算子期望数据生成函数。有两种场景,分别为Design视图和Text视图下进行配置。 - 若在Design视图下,可以手动编写输入输出设置,以及属性的设置。 - 若在Text视图下,可以以编写json文件的方式,设置输入输出和属性的设置 2.修改Case信息后,单击“Save”,修改会保存到算子测试用例定义文件。算子测试用例定义文件存储目录为算子工程根目录下的“ testcases/st/OpType/ aicpu_kernel”文件夹下,命名为LogSpace_case_20220415185113.json ``` { "case_name":"Test_Cast_002", "op":"Cast", "calc_expect_func_file":"", "gui_calc_expect_func_file":"", "input_desc":[ { "name":"x_tensor", "format":[ "ND" ], "type":[ "int32" ], "shape":[ 2, 3 ], "value_range":[ [ 0.1, 10.0 ] ], "data_distribute":[ "uniform" ], "value":[ [ 2, 3, 5 ], [ 6, 80, 20 ] ] } ], "output_desc":[ { "format":[ "ND" ], "type":[ "int64" ], "shape":[ 2, 3 ] } ], "attr":[ { "name":"dst_type", "value":9, "type":"int" } ] }, ``` 6. **运行ST测试用例。**右键单击生成ST测试用例定义文件中生成的ST测试用例定义文件(路径:“**testcases> st > add > aicpu_kernel >xxxx.json**”),选择“**Run ST Case 'xxx**'”。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/9/1652079046153354756.png)表7运行配置信息| **参数** | **说明** | | ---------------------------- | ------------------------------------------------------------ | | **Name** | 运行配置名称,用户可以自定义。 | | **Test Type** | 选择st_cases 。 | | **Execute Mode** | Remote Execute 远程执行测试Local Execute 本地执行测试说明:Local Execute不支持Windows操作系统。 | | **Deployment** | 当Execute Mode选择Remote Execute时deployment功能,详细请参见[Deployment](https://www.hiascend.com/document/detail/zh/mindstudio/304/atlasms_02_0371.html),可以将指定项目中的文件、文件夹同步到远程指定机器的指定目录。 | | **CANN Machine** | CANN工具所在设备deployment信息。说明:该参数仅支持Windows操作系统。 | | **Environment Variables** | 在文本框中添加环境变量:PATH_1=路径1;PATH_2=路径2多个环境变量用英文分号隔开。也可以点击文本框后的图标,在弹出的对话框中填写。在Name中输入环境变量名称:PATH_1。在Value中输入环境变量值:路径1。勾选Instead system environment variables可以显示系统环境变量。 | | **Operator Name** | 选择需要运行的算子。 | | **SoC Version** | 配置 昇腾AI处理器的类型。 | | **Product Form** | 配置产品形态。若SoC Version配置为Ascend310时显示该配置项。EPRC | | **Executable File Name** | 下拉选择需要执行的测试用例定义文件。若对AI CPU算子进行ST测试,测试用例文件前有(AI CPU)标识。 | | **Target OS** | 针对Ascend EP:选择昇腾AI处理器所在硬件环境的Host侧的操作系统。针对Ascend RC:选择板端环境的操作系统。 | | **Target Architecture** | 选择Target OS的操作系统架构。 | | **Case Names** | 选择运行的Case Name。说明:默认全选所有用例,可以去除勾选部分不需要运行的用例。 | | **Enable Auto Build&Deploy** | 选择是否在ST运行过程中执行编译和部署。 | | **Advanced Options** | 高级选项 | | **ATC Log Level** | 选择ATC 日志级别INFODEBUGWARNINGERRORNULL | | **Precision Mode** | 精度模式force_fp16allow_mix_precisionallow_fp32_to_fp16must_keep_origin_dtype | | **Device Id** | 设备ID,设置运行ST测试的芯片ID。用户根据使用的AI芯片ID填写。 | | **Error Threshold** | 配置自定义精度标准,取值为含两个元素的列表:[val1,val2]val1:算子输出结果与标杆数据误差阈值,若误差大于该值则记为误差数据。val2:误差数据在全部数据占比阈值。若误差数据在全部数据占比小于该值,则精度达标,否则精度不达标。取值范围为[0.0,1.0]。 | | **Compile Settings** | 编译设置按钮,单击按钮后弹出对话框,可以在对话框中配置以下内容。Include Directories:第三方库的头文件路径,支持多路径输入,中间用英文冒号分隔。Link Directories:库路径,支持多路径输入,中间用英文冒号分隔。Link Libraries:库,支持多文件输入,中间用英文冒号分隔。须知:该出的配置项会添加到算子工程目录下“cpukernel/CMakeLists.txt”文件中。最新的配项会覆盖上一次配置项。第三方库在Ascend310和Ascend910处理器上需用Hccl编译器生成,在RC场景需用交叉编译器生成。仅算子实现文件编译时使用该配置。 | 7. **运行后会输出测试用例总数**,成功用例总数,失败用例总数,错误用例总数。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/9/1652079120563866250.png) ## 十.经验总结 1. 编写算子信息库文件**cpukernel/op_info_cfg/aicpu_kernel/log_space.ini**时,要确保文件没有多余的空行或者其他符号,否则编译会报如下错误。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/9/1652079307121373753.png) 2. 编译算子工程前,先检查一下配置参数,如果缺少环境变量直接编译会报如下错误 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/9/1652079329012882040.png) 点击Build > Edit Build Configuration...进入编译配置页面。检查Environment Variables配置是否配置完整,点击输入框右边文档图标,查看环境变量配置,确保对应参数路径正确,如图: ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/9/1652079352119367642.png) 3. 算子部署前需要先编译成功,生成custom_opp_××××.run,部署的时候会自动检测Operator Package是否存在,否则Operator Package检测不到,会导致如下图所示 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/9/1652079367551634587.png) 这时候直接运行会导致如下错误。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/9/1652079391891421476.png) ## 十一.关于MindStudio更多的内容 如果需要了解关于MindStudio更多的信息,请查阅昇腾社区中MindStudio的[用户手册](https://www.hiascend.com/document/detail/zh/mindstudio/304/progressiveknowledge/index.html),里面有算子开发、模型开发等各种使用操作的详细介绍。 如果在使用MindStudio过程中遇到任何问题,也可以在昇腾社区中的[昇腾论坛](https://www.huaweicloud.com/s/JU1pbmRTdHVkaW_mkK3lu7ol/t_60_p_1)进行提问,会有华为内部技术人员对其进行解答,如下图。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20225/9/1652079407640754655.png)
  • [问题求助] FI产品 使用idea本地调式spark sql 如何配置
    【功能模块】【操作步骤&问题现象】1、我在本机idea上 执行sparksql 查询hive库的数据 报错2、已经hive的相关配置文件放在resources下、 jar包也是从集群上的spark下的lib下导出来的,请问还需要配置说明东西  谢谢【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [技术干货] MindStudio开发算子指导
    ### 一、概述 算子开发通常包括以下步骤: - 算子分析 - 创建算子工程 - 算子实现 - 编译 - UT测试 - ST测试 - 部署 下面介绍使用MindStudio完成一个简单TBE算子的整个开发过程。我们可以从 _[这里](https://www.hiascend.com/software/mindstudio/download)_ 获取MindStudio的版本,CANN的版本需要跟MindStudio配套。 注意:如果图片看不见,可以直接到[这里](https://gitee.com/ascend/mindstudio-samples/wikis/%E4%B8%9A%E5%8A%A1%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/%E7%AE%97%E5%AD%90%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC)查看原文; ### 二、算子分析 进行算子开发前,开发者应首先进行算子分析,算子分析包含:明确算子的功能及数学表达式,选择算子开发方式(DSL方式或者TIK方式),最后细化并明确算子规格,分析流程如下所示: 图4-1 算子分析流程 ![算子分析流程](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/14/1647261874857822428.png) 如何分析可以参考[算子分析](https://support.huawei.com/enterprise/zh/doc/EDOC1100234139/f43ef24b) 这里我们举例开发一个支持计算两个输入tensor的和的算子Add。 ### 三、创建算子工程 1.启动MindStudio,在欢迎界面点击New Project按钮,弹出创建工程的界面; ![输入图片说明](https://images.gitee.com/uploads/images/2022/0313/224103_b94d1976_7662180.png "屏幕截图.png")![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/14/1647261897244534576.png) 2.左边的选项卡选择Ascend Operator,右边界面中Name编辑框填写Add,点击Next按钮; ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/14/1647261913576156846.png) 3.填写相关参数,New Operator选择Empty Template,表示创建一个空的工程模板;Operator Type填Add;Plugin Framework选择TensorFlow,表明该算子是原始框架为TensorFlow的自定义算子;Unit Type根据实际情况选择,如果算子要支持运行在Ascend310芯片上,那么就选择Ascend310-AI Core,可以多选其他芯片类型,这里使用训练服务器,所以勾选Ascend910A-AI Core;点击Finish按钮,会生成一个模板工程。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/14/1647261927215780123.png) 工程目录下主要包括算子原型文件、实现文件、信息库文件和插件适配文件。算子的实现开发主要是针对这四类文件进行开发。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/14/1647261937502453774.png) ### 四、算子实现 1. 算子原型定义 算子原型定义规定了在昇腾AI处理器上可运行算子的约束,主要体现算子的数学含义,包含定义算子输入、输出、属性和取值范围,基本参数的校验和shape的推导,原型定义的信息会被注册到GE的算子原型库中。离线模型转换时,GE会调用算子原型库的校验接口进行基本参数的校验,校验通过后,会根据原型库中的推导函数推导每个节点的输出shape与dtype,进行输出tensor的静态内存的分配。 Add算子原型头文件add.h: ``` /** * Copyright (c) Huawei Technologies Co., Ltd. 2019-2020. All rights reserved. * This program is free software; you can redistribute it and/or modify * it under the terms of the Apache License Version 2.0.You may not use this file except in compliance with the License. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Apache License for more details at * http://www.apache.org/licenses/LICENSE-2.0 * * Description: Huawei Code * * Author: Huawei * * Create: 2020-01-01 * */ #ifndef GE_OPS_OP_PROTO_ADD_H_ #define GE_OPS_OP_PROTO_ADD_H_ #include "graph/operator_reg.h" namespace ge { REG_OP(Add) .INPUT(x1, TensorType({DT_FLOAT, DT_INT32, DT_INT64, DT_FLOAT16, DT_INT16, DT_INT8, DT_UINT8, DT_DOUBLE, DT_COMPLEX128, DT_COMPLEX64, DT_STRING})) .INPUT(x2, TensorType({DT_FLOAT, DT_INT32, DT_INT64, DT_FLOAT16, DT_INT16, DT_INT8, DT_UINT8, DT_DOUBLE, DT_COMPLEX128, DT_COMPLEX64, DT_STRING})) .OUTPUT(y, TensorType({DT_FLOAT, DT_INT32, DT_INT64, DT_FLOAT16, DT_INT16, DT_INT8, DT_UINT8, DT_DOUBLE, DT_COMPLEX128, DT_COMPLEX64, DT_STRING})) .OP_END_FACTORY_REG(Add) } #endif //GE_OPS_OP_PROTO_ADD_H_ ``` Add算子原型源文件add.cc: ``` /** * Copyright (c) Huawei Technologies Co., Ltd. 2019-2020. All rights reserved. * This program is free software; you can redistribute it and/or modify * it under the terms of the Apache License Version 2.0.You may not use this file except in compliance with the License. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Apache License for more details at * http://www.apache.org/licenses/LICENSE-2.0 * * Description: Huawei Code * * Author: Huawei * * Create: 2020-01-01 * */ #include "./add.h" #include #include namespace ge { bool InferShapeAndTypeAdd(Operator &op, const string &inputName1, const string &inputName2, const string &outputName) { TensorDesc vOutputDesc = op.GetOutputDescByName(outputName.c_str()); DataType inputDtype = op.GetInputDescByName(inputName1.c_str()).GetDataType(); Format inputFormat = op.GetInputDescByName(inputName1.c_str()).GetFormat(); // 针对shape维度大小进行交换 ge::Shape shapeX = op.GetInputDescByName(inputName1.c_str()).GetShape(); ge::Shape shapeY = op.GetInputDescByName(inputName2.c_str()).GetShape(); std::vector dimsX = shapeX.GetDims(); std::vector dimsY = shapeY.GetDims(); if (dimsX.size() dimsY.size()) { std::vector dimsTmp = dimsX; dimsX = dimsY; dimsY = dimsTmp; } // 对小的shape进行1补齐 if (dimsX.size() != dimsY.size()) { int dec = dimsX.size() - dimsY.size(); for (int i = 0; i dec; i++) { dimsY.insert(dimsY.begin(), (int64_t)1); } } // 设置输出的shape维度 std::vector dimVec; for (size_t i = 0; i dimsX.size(); i++) { if ((dimsX[i] != dimsY[i]) && (dimsX[i] != 1) && (dimsY[i] != 1)) { return false; } int64_t dims = dimsX[i] > dimsY[i] ? dimsX[i] : dimsY[i]; dimVec.push_back(dims); } ge::Shape outputShape = ge::Shape(dimVec); vOutputDesc.SetShape(outputShape); vOutputDesc.SetDataType(inputDtype); vOutputDesc.SetFormat(inputFormat); op.UpdateOutputDesc(outputName.c_str(), vOutputDesc); return true; } // ----------------Add------------------- IMPLEMT_VERIFIER(Add, AddVerify) { if (op.GetInputDescByName("x1").GetDataType() != op.GetInputDescByName("x2").GetDataType()) { return GRAPH_FAILED; } return GRAPH_SUCCESS; } // Obtains the processing function of the output tensor description. IMPLEMT_COMMON_INFERFUNC(AddInferShape) { if (InferShapeAndTypeAdd(op, "x1", "x2", "y")) { return GRAPH_SUCCESS; } return GRAPH_FAILED; } // Registered inferfunction COMMON_INFER_FUNC_REG(Add, AddInferShape); // Registered verify function VERIFY_FUNC_REG(Add, AddVerify); // ----------------Add------------------- } // namespace ge ``` 更多可以参考[算子原型定义](https://support.huawei.com/enterprise/zh/doc/EDOC1100234139?section=j00s) 2. 算子代码实现 算子计算逻辑及调度的实现。 Add算子实现文件add.py: ``` #!/usr/bin/env python # -*- coding:utf-8 -*- """ Copyright (C) 2019. Huawei Technologies Co., Ltd. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the Apache License Version 2.0.You may not use this file except in compliance with the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Apache License for more details at http://www.apache.org/licenses/LICENSE-2.0 add """ from __future__ import absolute_import import tbe.dsl as tbe from functools import reduce from tbe import tvm from tbe.common.register import register_op_compute from tbe.common.utils import para_check from tbe.common.utils import shape_util # General limitation of the reduce size for input shape: 2**31 SHAPE_SIZE_LIMIT = 2147483648 # pylint: disable=locally-disabled,too-many-arguments,unused-argument @register_op_compute("Add", op_mode="dynamic", support_fusion=True) def add_compute(input_x, input_y, output_z, kernel_name="add"): """ calculating data's add, c = a + b Parameters ---------- input_x: TVM tensor the placeholder of first input data input_y: TVM tensor the placeholder of second input data output_data: dict shape and dtype of output, should be broadcast shape and type as input kernel_name: str cce kernel name, default value is add Returns ------- res : output of the data's add """ shape_x = shape_util.shape_to_list(input_x.shape) shape_y = shape_util.shape_to_list(input_y.shape) shape_x, shape_y, shape_max = \ shape_util.broadcast_shapes(shape_x, shape_y, param_name_input1="input_x", param_name_input2="input_y") shape_size = reduce(lambda x, y: x * y, shape_max[:]) if shape_size > SHAPE_SIZE_LIMIT: raise RuntimeError("the shape is too large to calculate") input_x = tbe.broadcast(input_x, shape_max) input_y = tbe.broadcast(input_y, shape_max) res = tbe.vadd(input_x, input_y) return res @para_check.check_op_params(para_check.REQUIRED_INPUT, para_check.REQUIRED_INPUT, para_check.REQUIRED_OUTPUT, para_check.KERNEL_NAME) def add(input_x, input_y, output_z, kernel_name="add"): """ algorithm: add calculating data's add, c = a + b Parameters ---------- input_x : dict shape and dtype of first input, only support float16, float32, int32 input_y : dict shape and dtype of second input, only support float16, float32, int32 output_z: dict shape and dtype of output, should be broadcast shape and type as input kernel_name : str cce kernel name, default value is add Returns ------- None """ shape_x = input_x.get("shape") shape_y = input_y.get("shape") check_tuple = ("float16", "float32", "int32") input_data_type = input_x.get("dtype").lower() para_check.check_dtype(input_data_type, check_tuple, param_name="input_x") shape_x, shape_y, shape_max = \ shape_util.broadcast_shapes(shape_x, shape_y, param_name_input1="input_x", param_name_input2="input_y") if shape_x[-1] == 1 and shape_y[-1] == 1 and shape_max[-1] == 1: shape_x = shape_x if len(shape_x) == 1 else shape_x[:-1] shape_y = shape_y if len(shape_y) == 1 else shape_y[:-1] shape_max = shape_max if len(shape_max) == 1 else shape_max[:-1] data_x = tvm.placeholder(shape_x, name="data_1", dtype=input_data_type) data_y = tvm.placeholder(shape_y, name="data_2", dtype=input_data_type) res = add_compute(data_x, data_y, output_z, kernel_name) with tvm.target.cce(): schedule = tbe.auto_schedule(res) config = {"print_ir": False, "name": kernel_name, "tensor_list": (data_x, data_y, res)} tbe.build(schedule, config) ``` 3. 算子信息库定义 算子信息库文件用于将算子的相关信息注册到算子信息库中,包括算子的输入输出dtype、format以及输入shape信息。离线模型转换时,FE会根据算子信息库中的算子信息做基本校验,判断是否需要为算子插入合适的转换节点,并根据算子信息库中信息找到对应的算子实现文件进行编译,生成算子二进制文件进行执行。 算子信息库文件add.ini: ``` [Add] input0.name=x1 input0.dtype=float16,float16,float16,float16,float,float,float,float,int32,int32,int32,int32 input0.shape=all input0.paramType=required input0.format=NCHW,NC1HWC0,NHWC,ND,NCHW,NC1HWC0,NHWC,ND,NCHW,NC1HWC0,NHWC,ND input1.name=x2 input1.dtype=float16,float16,float16,float16,float,float,float,float,int32,int32,int32,int32 input1.shape=all input1.paramType=required input1.format=NCHW,NC1HWC0,NHWC,ND,NCHW,NC1HWC0,NHWC,ND,NCHW,NC1HWC0,NHWC,ND output0.name=y output0.dtype=float16,float16,float16,float16,float,float,float,float,int32,int32,int32,int32 output0.shape=all output0.paramType=required output0.format=NCHW,NC1HWC0,NHWC,ND,NCHW,NC1HWC0,NHWC,ND,NCHW,NC1HWC0,NHWC,ND opFile.value=add opInterface.value=add ``` 更多参考[算子信息库定义](https://support.huawei.com/enterprise/zh/doc/EDOC1100234139?section=j01i) 4. 算子适配 基于第三方框架(TensorFlow/Caffe)进行自定义算子开发的场景,开发人员需要进行插件的开发,将基于第三方框架的算子属性映射成适配昇腾AI处理器的算子属性。 Add算子插件适配文件tensorflow_add_plugin.cc: ``` /** * Copyright (c) Huawei Technologies Co., Ltd. 2019-2020. All rights reserved. * This program is free software; you can redistribute it and/or modify * it under the terms of the Apache License Version 2.0.You may not use this file except in compliance with the License. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Apache License for more details at * http://www.apache.org/licenses/LICENSE-2.0 * * Description: Huawei Code * * Author: Huawei * * Create: 2020-01-01 * */ #include "register/register.h" namespace domi { REGISTER_CUSTOM_OP("Add") .FrameworkType(TENSORFLOW) .OriginOpType({ge::AscendString("Add")}) .ParseParamsByOperatorFn(AutoMappingByOpFn) .ImplyType(ImplyType::TVM); } // namespace domi ``` 更多参考[算子适配插件开发](https://support.huawei.com/enterprise/zh/doc/EDOC1100234139?section=j01k) ### 五、编译 点击Build->Edit Build Configuration..菜单,弹出的对话框中选择Local Build,Target Architecture选择AI服务器的架构,我们这里的服务器是鲲鹏的训练服务器,所以选aarch64(需要提前安装好对应版本的CANN),点击Build按钮。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/14/1647261961459399328.png) ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/14/1647262062811359536.png) 查看输出窗口,看到编译成功,且在cmake-build目录下生成了自定义算子Run包custom_opp_Linux_x86_64.run,该run包可以单独拿到其他开发或者运行环境部署。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/14/1647262055188728167.png) ### 六、UT测试 我们可以对算子的实现进行UT单元测试,验证算子实现是否正确。算子的UT是通过在开发机器上将算子运行在昇腾芯片的仿真器来验证的。 在工程树的根目录鼠标右键,弹出的菜单中选择New Cases->TBE UT Case,弹出的窗口中Operator Name选择add算子,点击OK,创建UT文件模板。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/14/1647262073807224748.png) ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/14/1647262084959770579.png) ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/14/1647262089473979599.png) 针对test_add_impl.py文件,我们可以修改这个文件增加算子实现的UT测试用例代码。 test_add_impl.py内容: ``` #!/usr/bin/env python # -*- coding:utf-8 -*- """ Copyright (C) 2020. Huawei Technologies Co., Ltd. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the Apache License Version 2.0.You may not use this file except in compliance with the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Apache License for more details at http://www.apache.org/licenses/LICENSE-2.0 test_add_impl """ from op_test_frame.ut import BroadcastOpUT ut_case = BroadcastOpUT("Add") def calc_expect_func(input_x, input_y, output_z): """ get expect output :param input_x:tensor x :param input_y: tensor y :param output_z: output placeholder :return:output tensor """ res = input_x["value"] + input_y["value"] return [res, ] ut_case.add_precision_case("all", { "params": [{"dtype": "float16", "format": "ND", "ori_format": "ND", "ori_shape": (32,), "shape": (32,), "param_type": "input"}, {"dtype": "float16", "format": "ND", "ori_format": "ND", "ori_shape": (32,), "shape": (32,), "param_type": "input"}, {"dtype": "float16", "format": "ND", "ori_format": "ND", "ori_shape": (32,), "shape": (32,), "param_type": "output"}], "calc_expect_func": calc_expect_func }) ``` 修改完成后,鼠标右键点击test_add_impl.py文件,弹出的菜单中选择Run TBE Operator,弹出配置窗口,SoC Version选择Ascend910,Target 选择Simulator_TMModel,Operator Name选择add,Case Names中全选用例,然后点击Run。查看输出窗口,可以看到用例是否都跑成功了。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/14/1647262102579948712.png) 算子原型也可以进行UT测试,修改test_add_proto.cpp如下: ``` #include gtest.h> #include #include "add.h" class AddTest : public testing::Test { protected: static void SetUpTestCase() { std::cout "add test SetUp" std::endl; } static void TearDownTestCase() { std::cout "add test TearDown" std::endl; } }; TEST_F(AddTest, add_test_case_1) { // define op ge::op::Add addOp; ge::TensorDesc tensorDesc; ge::Shape shape({2, 3, 4}); tensorDesc.SetDataType(ge::DT_FLOAT16); tensorDesc.SetShape(shape); tensorDesc.SetOriginShape(shape); // update op input addOp.UpdateInputDesc("x1", tensorDesc); addOp.UpdateInputDesc("x2", tensorDesc); // call InferShapeAndType function auto ret = addOp.InferShapeAndType(); EXPECT_EQ(ret, ge::GRAPH_SUCCESS); // compare dtype and shape of op output auto outputDesc = addOp.GetOutputDescByName("y"); EXPECT_EQ(outputDesc.GetDataType(), ge::DT_FLOAT16); std::vector expectedOutputShape = {2, 3, 4}; EXPECT_EQ(outputDesc.GetShape().GetDims(), expectedOutputShape); } ``` ### 七、ST测试 在工程树的根目录鼠标右键,弹出的菜单中选择New Cases->ST Case,弹出的窗口中Operator Name选择add算子,SoC Version选择ascend910,点击OK。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/14/1647262113878341098.png) 创建ST用例json文件。 默认生成了一个ST用例,修改输入输出的shape为[8,16,8,16]。点击Run按钮,弹出运行配置窗口,选择Local Execute,勾选用例,点击Run按钮。在最下方的Run窗口,可以看到运行结果,所有的用例是否都运行通过了。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/14/1647262123644480536.png) ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/14/1647262128541940971.png) ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/14/1647262133329353662.png) ### 八、部署 点击Ascend->Operator Deployment菜单,弹出的窗口中选择Operator Deploy Locally,Operator Package默认是我们刚才编译出来的算子Run包,点击Operator deploy按钮,算子相关交付件会部署到CANN的opp的自定义算子目录下。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20223/14/1647262141438190557.png)
总条数:133 到第
上滑加载中