1. 这不是“云上点点鼠标”的幻觉ModelArts到底在解决什么真问题“华为云ModelArts模型训练与部署零基础考点精讲”——这个标题里藏着一个巨大的认知陷阱。很多人看到“零基础”三个字下意识就以为这是个教你怎么在网页上拖拽几个模块、点几下“开始训练”按钮然后坐等模型跑出来的保姆级教程。我必须先泼一盆冷水ModelArts不是魔法盒子它是一套精密的工业流水线调度系统而“零基础”指的是你不需要从头手写分布式训练框架但你必须理解这条流水线上的每一个工位在干什么、为什么这么排布、哪个环节卡住了整个产线。我带过几十个从Java后端、前端、甚至财务转行过来的学员他们最大的挫败感从来不是写不出PyTorch代码而是当训练任务在ModelArts控制台显示“运行中”长达8小时日志里只有一行“Waiting for resource...”却完全不知道该去哪个页面、看哪项指标、调哪个参数来破局。这背后是ModelArts对底层资源GPU卡、内存、网络带宽的智能编排逻辑是它把“模型训练”这个原本需要博士团队协作的复杂工程封装成可被业务人员理解的“数据集-算法-超参-输出”四要素闭环。所以这篇精讲的“考点”不是考你记住控制台按钮的位置而是考你能否在任务卡死时像一个老练的产线工程师一样快速定位是“原料”数据集格式错误、“模具”镜像环境缺失torchvision、“工人”worker节点OOM还是“调度指令”预估资源不足出了问题。它面向的“零基础”是那些没碰过GPU集群、没配过CUDA环境、甚至分不清NVIDIA Driver和CUDA Toolkit区别的开发者但它要求的“基础”是你必须建立起对“计算资源-数据流-模型代码”三者耦合关系的直觉。比如当你上传一个50GB的视频帧序列数据集ModelArts会自动将其切片并分发到多个Worker节点但如果你的训练脚本里用的是cv2.imread()这种单线程IO操作整个集群90%的GPU算力就会被IO瓶颈锁死——这个“考点”不会出现在任何官方文档的FAQ里却是我在华为云客户现场踩了三次坑才总结出的血泪经验。2. 模型训练从“本地能跑”到“云上稳训”的生死跨越2.1 训练任务启动前的“三道安检”数据、代码、环境缺一不可在ModelArts上启动一个训练任务表面看只是填写几个表单但背后有三道硬性安检任何一道不过任务连“排队”资格都没有。这三道关卡就是所有初学者最容易栽跟头的“考点”。第一道安检数据集的“户籍证明”。ModelArts不接受你本地硬盘上随意命名的文件夹。它要求数据必须以特定结构存入OBS对象存储服务且路径必须符合规范。比如你要训练一个图像分类模型标准结构必须是obs://my-bucket/dataset/train/ ├── class_a/ │ ├── img_001.jpg │ └── img_002.jpg ├── class_b/ │ ├── img_010.jpg └── ... obs://my-bucket/dataset/val/ ├── class_a/ └── class_b/注意train/和val/目录名是固定的不能写成training/或test/子目录名class_a,class_b就是你的类别标签会被自动识别为label。我见过太多人把数据直接打包成data.zip上传指望ModelArts自动解压识别——结果任务直接失败报错Dataset path not valid。正确做法是用华为云OBS Browser工具将本地已整理好的文件夹逐层创建同名目录再把图片拖进去。这里有个关键细节OBS Browser上传时默认开启“断点续传”但如果网络抖动它可能只上传了部分文件而控制台显示“上传成功”。务必在OBS控制台里手动点开每个子目录确认图片数量与本地一致。这是第一个“考点”数据集不是“放上去就行”而是“结构对、数量准、路径清”三者缺一不可。第二道安检训练脚本的“自证清白”。你的train.py脚本在本地用python train.py能跑通不代表在ModelArts上就能跑。ModelArts的执行环境是纯净的Docker容器它没有你本地Anaconda里装的pandas1.3.5也没有你~/.bashrc里配置的PYTHONPATH。脚本里所有依赖必须显式声明。最稳妥的方式是在项目根目录下创建一个requirements.txt文件里面写明torch1.12.1cu113 torchvision0.13.1cu113 numpy1.21.6 # 注意不要写 torch1.12ModelArts会因版本冲突直接报错更关键的是脚本开头必须加上对OBS路径的适配。ModelArts会把OBS数据集路径通过环境变量$DATASET_PATH注入容器你的代码不能写死/home/data/train而要这样写import os dataset_path os.getenv(DATASET_PATH, /home/ma-user/work/dataset) train_dir os.path.join(dataset_path, train) val_dir os.path.join(dataset_path, val)我曾帮一个客户排查他们的脚本在本地跑得好好的上云后一直报FileNotFoundError: [Errno 2] No such file or directory: train。最后发现是因为脚本里用了相对路径./train而ModelArts的工作目录是/home/ma-user/work./train指向的是空目录。这是第二个“考点”代码必须是“环境无关”的所有路径、依赖、配置都要通过环境变量或配置文件注入而非硬编码。第三道安检镜像环境的“精准匹配”。ModelArts提供了大量预置镜像如tensorflow-2.8.0-py38-cuda11.2、pytorch-1.12.1-py38-cuda11.3。选错镜像后果很直接任务启动失败日志里满屏ImportError: libcudnn.so.8: cannot open shared object file。这里的“考点”在于理解版本依赖链。比如你用的PyTorch 1.12.1它编译时链接的是CUDA 11.3和cuDNN 8.2。如果你选了cuda11.2的镜像cuDNN版本就不匹配。如何查证打开华为云ModelArts文档找到“预置镜像列表”里面有一张大表格明确列出了每个镜像对应的PyTorch/TensorFlow版本、Python版本、CUDA版本、cuDNN版本。别嫌麻烦每次新建任务前花30秒对照这张表。还有一个隐藏坑有些预训练模型如ResNet的权重文件是用特定版本的PyTorch保存的。如果你用PyTorch 1.13加载1.12保存的.pth文件可能会报_IncompatibleKeys警告虽不影响训练但会干扰日志分析。所以镜像选择不是“越新越好”而是“与你的代码、数据、预训练权重三者严格对齐”。这是第三个也是最易被忽视的“考点”。2.2 训练过程中的“心跳监测”读懂日志才是真本事当任务状态变成“运行中”真正的考验才开始。ModelArts控制台的“日志”页签不是让你看热闹的它是你诊断问题的“心电图”。新手常犯的错误是只盯着最后几行看到Epoch 1/100就以为万事大吉。其实关键信息全在开头和中间。日志的黄金三段论第一段启动阶段查看是否成功挂载OBS数据集。正常日志会有类似Successfully mounted OBS bucket my-bucket to /home/ma-user/work/dataset的提示。如果这里报错比如Failed to list objects in obs://my-bucket/dataset/train那一定是OBS权限没配好或者路径写错了。这是最基础的排查点。第二段初始化阶段关注模型加载和数据加载器构建。这里会出现Loading pre-trained ResNet50 weights...或Creating DataLoader with batch_size32。如果卡在这里超过2分钟大概率是数据集IO瓶颈。此时你需要看两个指标一是OBS的“请求速率”监控在OBS控制台里如果QPS长期低于50说明数据读取慢二是训练日志里是否有WARNING: DataLoader is slow。解决方案不是加batch_size而是改用num_workers4ModelArts默认是0并确保你的数据集是TFRecord或LMDB格式而非原始JPEG——后者在高并发读取时小文件过多会导致严重IO争抢。第三段训练阶段这是“考点”密集区。除了看loss是否下降更要盯住GPU Memory Usage。ModelArts会在日志里周期性打印GPU 0: 98%。如果这个数字长期在95%以上恭喜你你的模型已经快把显存撑爆了。下一步你会看到CUDA out of memory任务直接OOM退出。此时你的应对不是换更大显卡成本高而是做三件事1降低batch_size最直接2在PyTorch里启用torch.cuda.amp.autocast()混合精度训练能省30%显存3检查模型里有没有torch.cat()拼接了超大tensor改成torch.stack()。我有个学员他的YOLOv5模型在V100上batch_size16就OOM启用AMP后轻松跑到32训练速度还快了15%。这就是“考点”显存不是固定值而是可以通过代码优化动态释放的资源池。提示ModelArts的日志是实时流式的但有时会延迟10-20秒。如果想立刻看到最新日志不要狂点“刷新”而是关闭日志页签重新打开。这是个鲜为人知的UI小技巧能避免你误判任务状态。2.3 “报NaN”不是玄学从梯度爆炸到学习率失配的归因树loss: nan这是深度学习训练中最令人头皮发麻的报错。它像幽灵一样可能在第1个epoch就出现也可能在第99个epoch突然降临。在ModelArts上它往往伴随着Gradient overflow的警告。很多初学者第一反应是“重跑”结果十次八次都是nan。这背后是一棵清晰的归因树。归因树第一层数据污染这是最简单也最容易被忽略的原因。检查你的数据集特别是val/目录下是否混入了非图像文件如.DS_Store、Thumbs.db或损坏的图片文件头缺失。ModelArts的数据加载器遇到这种文件不会报错而是返回一个全零tensor。当这个零tensor进入网络经过ReLU后还是零再经过后续层梯度就消失了最终loss计算时出现0/0得到nan。解决方案在训练脚本开头加一段数据清洗代码from PIL import Image import os def validate_image(path): try: img Image.open(path) img.verify() # 验证图片完整性 return True except: return False # 遍历val目录删除所有验证失败的文件 for root, _, files in os.walk(val_dir): for f in files: if not validate_image(os.path.join(root, f)): os.remove(os.path.join(root, f))归因树第二层学习率失配这是最典型的“云上特有”问题。你在本地用RTX 3090训练lr0.01很稳但上云后ModelArts可能给你分配了8卡A100总batch_size变成了原来的8倍。如果你没按线性缩放法则调整学习率lr_new lr_old * (batch_size_new / batch_size_old)那么巨大的batch_size会让梯度更新幅度过猛直接冲出损失函数的“安全谷底”导致nan。华为云ICT大赛云赛道的冠军方案里就明确提到他们用lr0.1在8卡A100上训练ResNet50就是因为做了严格的线性缩放。这是核心“考点”学习率不是超参而是与硬件资源强耦合的系统参数。归因树第三层数值不稳定当以上两点都排除后nan往往源于FP32计算的固有缺陷。比如Softmax函数在输入值过大时exp(x)会溢出。解决方案是使用PyTorch内置的稳定版# 错误手动实现不稳定 probs torch.exp(logits) / torch.sum(torch.exp(logits)) # 正确使用内置自动减去最大值 probs torch.nn.functional.softmax(logits, dim-1)或者在模型的关键层如最后一层全连接后加一个torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0)给梯度加个“安全阀”。我在部署一个RoBERTa中文预训练模型时就因为没加梯度裁剪在微调阶段反复nan加上后问题迎刃而解。3. 模型部署从“训练完事”到“服务可用”的惊险一跃3.1 部署前的“灵魂拷问”你的模型真的ready了吗在ModelArts上点击“部署为在线服务”看起来只是一次鼠标操作。但在我经手的上百个部署案例中超过60%的失败根源不在部署步骤本身而在于训练产出的模型文件根本就没做好“上岗准备”。这就像你造了一辆法拉利却没装方向盘和刹车然后怪4S店不会交车。灵魂拷问一模型格式是“推理友好型”吗ModelArts在线服务支持三种主流格式SavedModelTensorFlow、TorchScriptPyTorch、ONNX。但你的训练脚本默认保存的往往是checkpoint.pthPyTorch或model.h5Keras这些是“训练态”格式包含了优化器状态、梯度历史等冗余信息体积大、加载慢、且无法直接用于推理。正确的做法是在训练结束后显式导出为推理格式。以PyTorch为例# 训练完成后加载最佳模型 model.load_state_dict(torch.load(best_model.pth)) model.eval() # 切换到评估模式关闭dropout等 # 创建一个dummy input尺寸要和你实际推理时一致 dummy_input torch.randn(1, 3, 224, 224) # batch1, RGB, 224x224 # 导出为TorchScript traced_model torch.jit.trace(model, dummy_input) traced_model.save(model.pt) # 这才是ModelArts要的格式注意dummy_input的尺寸必须和你未来API请求的图片尺寸严格一致否则部署后调用会报size mismatch。这是第一个“考点”部署不是“扔个文件过去”而是“生产一个轻量、确定、无状态的推理引擎”。灵魂拷问二预处理逻辑固化了吗你的训练脚本里图片预处理可能是这样的transform transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])这段代码在训练时没问题但部署时它必须和模型一起“打包”进服务镜像。否则用户传一张原始JPG过来服务端用cv2.imread()读取后直接喂给模型缺少了归一化结果必然错乱。ModelArts的解决方案是“自定义推理代码”。你需要创建一个customize_service.py文件里面定义preprocess()和postprocess()函数def preprocess(input_data): # input_data是base64解码后的bytes import numpy as np from PIL import Image import io img Image.open(io.BytesIO(input_data)).convert(RGB) img img.resize((256, 256)) img img.crop((16, 16, 240, 240)) # CenterCrop(224) img np.array(img) / 255.0 img (img - [0.485, 0.456, 0.406]) / [0.229, 0.224, 0.225] img np.transpose(img, (2, 0, 1)) # HWC - CHW return img.astype(np.float32) def postprocess(output_data): # output_data是模型输出的numpy array import numpy as np probs np.exp(output_data) / np.sum(np.exp(output_data)) return {probabilities: probs.tolist()}这个文件连同你的model.pt一起打包成model.tar.gz上传。这才是ModelArts在线服务真正运行的“大脑”。很多学员部署后调用返回500 Internal Error查日志发现NameError: name transforms is not defined就是因为忘了把transforms相关的逻辑写进customize_service.py。这是第二个也是最关键的“考点”服务端的预处理必须是独立、完整、可复现的代码块不能依赖任何外部库的全局状态。3.2 在线服务的“三重门”规格、弹性、鉴权一个都不能少部署一个在线服务ModelArts控制台会让你填三个核心参数计算规格、初始实例数、弹性伸缩策略。这看似简单却是决定服务是“丝滑流畅”还是“卡顿掉线”的分水岭。计算规格不是越大越好而是“够用即最优”ModelArts提供多种GPU规格如gpu.p1.2xlarge1张P4卡、gpu.p2.8xlarge8张V100卡。新手常陷入误区认为“反正公司报销选最大的”。结果呢一个简单的ResNet50分类服务配了8卡V100单次推理耗时20ms但每秒只能处理50个请求因为大部分GPU时间都在等网络IO和CPU预处理。而换成1卡P4单次耗时40ms但每秒能处理200个请求资源利用率高达85%。为什么因为P4是专为推理优化的低功耗卡它的Tensor Core在小batch场景下效率远超V100。华为云官方文档里有一张《不同GPU卡推理性能对比表》明确指出对于batch_size1的图像分类P4的吞吐量是V100的2.3倍。所以“考点”在于根据你的QPS每秒查询率和平均请求大小反向推算所需的最小规格。公式很简单所需GPU卡数 ≈ (峰值QPS * 单次推理耗时) / 1000。比如你预计峰值QPS是100单次耗时50ms那么100*50/1000 5意味着你需要至少5个“毫秒级处理单元”一张P4卡刚好提供约200个一张V100提供约150个所以1张P4就绰绰有余。初始实例数冷启动的“救命稻草”这个参数决定了服务刚创建时后台会预先启动几个容器实例。设为0意味着第一次请求来时要花30-60秒去拉镜像、加载模型、初始化环境用户看到的就是漫长的等待和超时。设为1首次请求几乎无感。但设得太高比如10又会造成资源浪费。我的经验是初始实例数 日常最低QPS 1。比如你服务平时QPS是5那就设为6。这样即使突发流量来了也有缓冲。弹性伸缩策略自动扩缩的“智慧大脑”ModelArts允许你设置基于CPU/GPU利用率的自动扩缩。但这里有个致命陷阱如果你只监控CPU利用率当GPU算力打满100%而CPU只有30%时系统不会扩容导致请求排队。所以必须同时监控GPU利用率并将其作为首要扩缩指标。在配置里勾选“GPU Utilization”设置“扩容阈值”为70%“缩容阈值”为30%。这样当GPU使用率持续5分钟超过70%系统会自动增加实例当低于30%持续10分钟自动减少。这是我给所有企业客户的标配建议它让服务像呼吸一样自然既保证了SLA服务等级协议又节省了30%以上的资源成本。注意弹性伸缩不是瞬间完成的。从触发扩容到新实例Ready通常需要90-120秒。所以如果你的服务有可预测的流量高峰比如每天上午9点最好提前10分钟手动扩容而不是完全依赖自动策略。这是个实操中必须掌握的“时间差”技巧。3.3 调用服务的“最后一公里”从curl到生产级SDK的平滑过渡服务部署成功拿到一个https://xxx.modelarts.ai/xxxx的Endpoint你以为就结束了不这才是“最后一公里”的开始。很多学员用curl测试成功就以为万事大吉结果集成到自己的App里各种401 Unauthorized、400 Bad Request。这是因为ModelArts的在线服务为了安全默认启用了IAM鉴权。第一步获取访问密钥Access Key这不是在ModelArts控制台里找而是在华为云“统一身份认证服务IAM”里。你需要创建一个“用户”并为其授予ModelArts FullAccess权限然后为该用户生成一对Access Key ID和Secret Access Key。这个过程比在AWS上创建IAM用户要多两步1必须在IAM里为该用户“启用编程访问”2生成AK/SK后要手动下载CSV文件因为网页上只显示一次。这是第一个“考点”AK/SK是最高权限凭证必须像保管银行卡密码一样保管绝不能硬编码在前端代码或Git仓库里。第二步构造签名请求SignatureModelArts要求所有请求头里必须包含X-Sdk-Date当前UTC时间戳和Authorization一个复杂的签名字符串。手动构造这个签名是场噩梦。幸运的是华为云提供了官方SDK。以Python为例安装huaweicloudsdkcore和huaweicloudsdkmodelartspip install huaweicloudsdkcore huaweicloudsdkmodelarts然后用几行代码搞定鉴权from huaweicloudsdkcore.auth.credentials import BasicCredentials from huaweicloudsdkcore.http.http_config import HttpConfig from huaweicloudsdkmodelarts.v1 import ModelArtsClient, RunModelRequest, RunModelRequestBody # 初始化凭证 credentials BasicCredentials(your_ak, your_sk, your_project_id) config HttpConfig.get_default_config() client ModelArtsClient.new_builder() \ .with_credentials(credentials) \ .with_http_config(config) \ .with_endpoint(https://modelarts.cn-north-4.myhuaweicloud.com) \ .build() # 构造请求体base64编码的图片 import base64 with open(test.jpg, rb) as f: image_bytes f.read() image_base64 base64.b64encode(image_bytes).decode(utf-8) request_body RunModelRequestBody( dataimage_base64, content_typeimage/jpeg ) request RunModelRequest( service_namemy-classifier, bodyrequest_body ) response client.run_model(request) print(response.to_json_string())这段代码自动处理了时间戳、签名、重试、超时等所有底层细节。相比自己手写curl命令它把“调用服务”的复杂度从“博士级”降到了“高中生级”。这也是华为云生态的优势它不强迫你成为密码学专家而是用成熟的SDK把安全的重担扛在自己肩上。所以“考点”不是考你是否会算HMAC-SHA256而是考你是否知道并会用这个SDK。4. 零基础实战用ModelArts 30分钟完成一个OCR服务的端到端交付4.1 项目背景与目标为什么选EasyOCR作为教学样本我们不选ResNet、不选YOLO而是选EasyOCR原因有三第一它开箱即用无需从头训练完美契合“零基础”定位第二它是一个典型的“多模型串联”服务检测识别能覆盖ModelArts部署的全部技术点第三它的预训练模型zh中文模型效果足够好能让你在30分钟内就看到一个能准确识别中文发票、表格的服务上线。这比花3小时训练一个准确率只有70%的自研模型更能建立信心。我们的交付目标输入一张手机拍摄的、带中文文字的图片如超市小票输出一个JSON包含所有识别出的文字及其坐标性能单次请求响应时间 1.5秒支持并发10 QPS4.2 数据准备与模型导出从GitHub到OBS的标准化流程EasyOCR的官方模型托管在GitHub上。但ModelArts无法直接从GitHub拉取我们必须把它“搬”到OBS。这不是简单的下载上传而是一套标准化流程。步骤1下载并解压模型访问EasyOCR的GitHub Release页面https://github.com/JaidedAI/EasyOCR/releases找到最新版如v1.7.0下载easyocr-1.7.0-models.zip。解压后你会看到两个关键文件夹detector包含文本检测模型craft_mlt_25k.pthrecognizer包含文本识别模型zh_sim_g2.pth步骤2创建ModelArts兼容的模型包ModelArts要求模型包是一个tar.gz文件内部结构必须是model/ ├── detector/ │ └── craft_mlt_25k.pth ├── recognizer/ │ └── zh_sim_g2.pth └── config.json # 必须存在告诉服务如何加载config.json内容如下{ model_type: ocr, detector: craft_mlt_25k.pth, recognizer: zh_sim_g2.pth, language: zh }步骤3上传至OBS用OBS Browser将整个model/文件夹上传到你的OBS桶路径为obs://my-bucket/easyocr-model/。注意是上传model/文件夹本身不是里面的文件。上传完成后在OBS控制台确认路径是easyocr-model/model/detector/craft_mlt_25k.pth。实操心得EasyOCR的模型文件很大zh_sim_g2.pth约1.2GBOBS Browser上传时务必勾选“分段上传”并把“分段大小”设为100MB。否则网络稍有波动整个1.2GB就得重传。这是我用华为云DDNS同步NAS数据时总结出的通用大文件上传技巧。4.3 自定义推理代码编写让EasyOCR在云上“活”起来EasyOCR的原生代码是为本地Python环境设计的直接扔到ModelArts上会报一堆ModuleNotFoundError。我们需要一个“胶水层”把它包装成ModelArts能理解的服务。创建customize_service.pyimport os import json import base64 import numpy as np from PIL import Image import io import torch from easyocr import Reader # 全局变量只在服务启动时加载一次避免每次请求都重复加载 _reader None def init(): global _reader # ModelArts会把OBS模型路径注入环境变量 model_path os.getenv(MODEL_PATH, /home/ma-user/work/model) # 初始化EasyOCR Reader指定模型路径和语言 _reader Reader([zh], gpuTrue, model_storage_directorymodel_path) def preprocess(input_data): # input_data是base64字符串 try: image_bytes base64.b64decode(input_data) image Image.open(io.BytesIO(image_bytes)).convert(RGB) # 转为numpy array供EasyOCR使用 image_np np.array(image) return image_np except Exception as e: raise ValueError(fPreprocess failed: {str(e)}) def postprocess(output_data): # output_data是EasyOCR的result list如[([x1,y1,x2,y2], 文字)] result [] for box, text in output_data: # EasyOCR的box是[[x1,y1], [x2,y2], ...]我们简化为[x1,y1,x2,y2] x_coords [p[0] for p in box] y_coords [p[1] for p in box] bbox [min(x_coords), min(y_coords), max(x_coords), max(y_coords)] result.append({ text: text, bbox: bbox }) return {results: result} def inference(input_data): global _reader if _reader is None: init() # 调用EasyOCR进行识别 results _reader.readtext(input_data, detail1, paragraphFalse) return results关键点解析init()函数利用ModelArts的“懒加载”特性只在第一次请求时初始化Reader后续请求直接复用极大缩短冷启动时间。preprocess()严格遵循ModelArts的输入规范把base64转为numpy array。inference()这是核心它调用_reader.readtext()并将detail1返回坐标和paragraphFalse不合并段落作为固定参数保证输出格式稳定。postprocess()把EasyOCR的原始输出转换为我们定义的、易于前端解析的JSON结构。4.4 服务部署与压测从“Hello World”到“生产就绪”部署步骤在ModelArts控制台进入“部署管理” “在线服务” “创建服务”。服务名称easyocr-zh-service。模型来源选择“从OBS导入”路径填obs://my-bucket/easyocr-model/。推理代码上传我们写好的customize_service.py。计算规格选择gpu.p1.2xlarge1张P4卡。理由EasyOCR是计算密集型P4的INT8推理性能足够应付10 QPS。初始实例数填2日常最低QPS是11留作缓冲。弹性伸缩启用GPU利用率扩容阈值70%缩容阈值30%。点击“创建”。压测验证服务状态变成“运行中”后不要急着写业务代码先用abApache Bench做一轮压测# 安装ab sudo apt-get install apache2-utils # 对服务发起100个并发总共1000次请求 ab -n 1000 -c 100 -H Content-Type: application/json \ -p test_payload.json \ https://xxx.modelarts.ai/v1/{project_id}/services/easyocr-zh-service/invocations其中test_payload.json内容为{ data: base64_encoded_image_string_here, content_type: image/jpeg }压测结果中重点关注Time per request平均响应时间和Failed requests失败请求数。理想结果是平均响应时间 1500ms失败请求数0。如果失败率高大概率是GPU显存不足此时应降低-c并发数或升级到更高规格。如果响应时间长则需检查customize_service.py里是否有多余的print()语句——这些IO操作在高并发下会成为性能瓶颈。常见问题速查表现象可能原因解决方案服务创建失败日志报No module named easyocrrequirements.txt未上传或内容错误创建requirements.txt内容为easyocr1.7.0上传并重新部署调用返回500日志报CUDA out of memoryP4卡显存8GB被EasyOCR的detector模型占满在customize_service.py的init()函数里添加torch.cuda.empty_cache()返回结果为空数组[]图片中文字太小或背景太复杂在in
阅读完成 · 觉得有帮助?