ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AI工程实战:从零构建可交付AI系统的方法论

AI工程实战:从零构建可交付AI系统的方法论 1. 这不是“搭积木”而是亲手锻造AI系统的完整工程链“AI Engineering from Scratch”——看到这个标题很多人第一反应是又要学Python、调PyTorch、跑个ResNet不。这六个单词背后是一整套被工业界反复验证、却极少在教程里系统呈现的可交付AI系统建造方法论。它不教你怎么调参而是告诉你当业务方说“我们要一个能自动识别产线螺丝松动的模型”你该从哪块钢板开始下料、用什么焊枪、怎么做应力测试、最后如何让整条产线工人愿意每天点开它——这才是真正的“from scratch”。我带过17个从0到1落地的AI项目最深的体会是90%的失败不在模型精度而在工程断层。比如训练时F10.92上线后跌到0.61比如GPU显存占用从2.3GB飙到14.8GB比如客户反馈“系统总在凌晨3:17崩溃”。这些问题没有一个能在Jupyter Notebook里复现。它们藏在Docker镜像分层策略里、藏在gRPC超时配置里、藏在Prometheus指标埋点粒度里——而这些正是“AI Engineering”区别于“ML Research”的核心战场。关键词“ai-engineering”和“from-scratch”绝非营销话术。前者指向一套融合软件工程、MLOps、SRE与领域知识的交叉能力后者强调拒绝黑盒依赖——不用Hugging Face AutoClassify一键封装不用MLflow自动记录不用Kubeflow流水线模板。你要亲手写Dockerfile的每一行COPY指令手动配置Traefik的路由规则用curl测试每个API端点的响应头是否携带X-Request-ID。这种“笨功夫”恰恰是构建高可靠AI系统的唯一捷径。适合谁读如果你正面临这些场景团队刚招来3个算法工程师但半年没交付一个可用模型你写的推理服务在压测时QPS从1200骤降到37或者你发现模型版本回滚需要手动改5个配置文件重启3个容器——那么这篇内容就是为你量身定制的实操手册。它不假设你懂Kubernetes但要求你愿意为每个YAML字段查官方文档它不回避C编译细节但会用“就像组装自行车链条一样”解释ONNX Runtime的执行图优化逻辑。2. 为什么必须放弃“模型即一切”的幻觉AI工程的四层地基模型2.1 地基一数据管道的物理可靠性而非仅格式正确多数教程把数据处理简化为“pandas读CSV→清洗→保存”。但在真实产线这是最脆弱的一环。去年我们为某汽车厂部署缺陷检测系统时模型准确率98%但实际日均漏检127次。根因排查耗时3天上游MES系统导出的XML文件其timestamp字段在夏令时切换日会多出1个空格导致Pandas解析时将整行标记为NaN而我们的数据校验脚本只检查了shape未校验df[timestamp].str.len().min() 19。真正的“from scratch”数据管道必须包含三重物理防护字节级校验对原始文件计算SHA256与上游约定的校验值比对不是MD5MD5碰撞风险已实证影响金融级审计结构熵监控用scipy.stats.entropy计算每列取值分布的香农熵当某列熵值突降30%如原本100个SKU突然只剩3个触发告警而非静默填充时序完整性断言对时间序列数据强制要求df[timestamp].diff().dt.seconds.between(0, 300).all()否则中断pipeline并保留原始文件快照提示别用Airflow的FileSensor——它只检查文件是否存在不校验内容。我们改用自定义Operator启动时先执行head -c 10000 raw_data.xml | sha256sum再启动解析任务。2.2 地基二模型服务的确定性推理而非仅API可达“模型上线了”不等于“服务可用”。我们曾遇到经典案例同一模型在本地GPU上推理耗时47ms在生产环境NVIDIA T4上飙升至328ms。排查发现是TensorRT引擎缓存路径权限问题——容器内用户UID为1001而/root/.cache/tensorrt/目录属主为root导致每次请求都重建引擎。解决方案不是改chmod而是重构Dockerfile# 错误示范直接RUN chown -R 1001 /root/.cache # 正确做法在构建阶段预生成引擎 FROM nvcr.io/nvidia/tensorrt:23.07-py3 COPY model.onnx . RUN trtexec --onnxmodel.onnx --saveEnginemodel.engine --fp16 FROM nvcr.io/nvidia/pytorch:23.07-py3 COPY --from0 /workspace/model.engine /app/model.engine # 运行时用户ID与引擎生成时一致 USER 1001关键洞察推理延迟的80%由I/O和内存布局决定而非算力。因此“from scratch”必须包含使用mmap加载模型权重避免torch.load()的Python GIL阻塞预分配CUDA pinned memorytorch.cuda.memory_reserved()调用前预留2GB对输入张量做contiguous()强制内存连续非contiguous张量触发隐式拷贝2.3 地基三可观测性的语义化埋点而非仅metrics打点Prometheus的http_request_duration_seconds指标救不了你的AI服务。当客户投诉“识别结果忽好忽坏”你需要知道是模型预测置信度分布偏移还是特征提取模块的CPU缓存命中率暴跌或是PostgreSQL连接池耗尽导致特征查询超时我们设计的语义化埋点体系包含三层业务层ai_prediction_confidence{modelscrew_v2, classloose}直方图桶宽0.05系统层cuda_memory_allocated_bytes{device0}Gauge每秒采集因果层feature_query_latency_seconds{sourcemysql, tablepart_specs}Summary含count/sum/quantile特别注意所有指标必须带request_id标签。当某个请求异常时用{request_idreq_abc123}即可关联全部日志、trace、metrics。这要求你在FastAPI中间件中注入app.middleware(http) async def add_request_id(request: Request, call_next): request_id str(uuid4()) # 注入到OpenTelemetry trace context carrier {} TraceContextTextMapPropagator().inject(carrier, set_span_in_context(get_current_span())) carrier[x-request-id] request_id # 同时写入structlog上下文 structlog.contextvars.bind_contextvars(request_idrequest_id) return await call_next(request)2.4 地基四部署拓扑的故障域隔离而非仅容器化把模型打包成Docker镜像只是起点。真正的工程挑战在于当GPU节点宕机时如何保证API可用性不降级我们的方案是混合部署拓扑主推理服务运行在K8s GPU节点池nvidia.com/gpu: 1降级服务运行在CPU节点池requests.cpu: 2使用ONNX Runtime CPU Execution Provider流量调度Istio VirtualService配置5%流量灰度到CPU服务当GPU服务P95延迟200ms时自动切流100%关键实现细节CPU服务必须使用ort.InferenceSession(model_path, providers[CPUExecutionProvider])禁用CUDAExecutionProvider即使有GPU降级开关通过Consul KV存储服务启动时监听/config/failover/enabled键值变化切流决策基于istio_requests_total{destination_service~ai-inference.*, response_code~5..}的1分钟速率注意不要用K8s HPA自动扩缩容AI服务——模型加载耗时远超Pod启动时间。我们采用“预热Pod”模式新版本发布时先启动3个带prewarmtrue标签的Pod执行curl http://localhost:8000/prewarm加载模型再更新Service的Endpoint。3. 从零构建可交付AI服务的七步实操手册3.1 第一步定义可验证的接口契约比写代码早72小时在敲下第一行import torch前必须完成接口契约文档。这不是Swagger YAML而是包含物理约束的协议# ai-inference-contract.yaml endpoints: - path: /v1/detect method: POST request: content_type: image/jpeg max_size_bytes: 5242880 # 5MB对应4096x30728bit图像 timeout_ms: 15000 response: success_status: 200 body_schema: type: object properties: predictions: type: array items: type: object properties: bbox: type: array items: {type: number, minimum: 0, maximum: 1} # 归一化坐标 confidence: {type: number, minimum: 0, maximum: 1} class_id: {type: integer, minimum: 0, maximum: 99} error_codes: - code: 400 reason: INVALID_IMAGE_FORMAT: JPEG header not detected - code: 413 reason: PAYLOAD_TOO_LARGE: image size 5MB - code: 503 reason: MODEL_UNAVAILABLE: no healthy inference pods为什么必须提前定义因为这决定了后续所有技术选型max_size_bytes5MB→ 要求Nginx配置client_max_body_size 5Mtimeout_ms15000→ FastAPI需设--timeout-keep-alive 15bbox归一化→ 模型输出层必须用Sigmoid激活禁用Softmax实操心得让测试工程师用dd if/dev/urandom bs1M count6 | curl -X POST --data-binary - http://api/detect压测若返回413则契约生效若返回500则说明契约未落实。3.2 第二步构建不可变的模型资产非Git LFS而是OCI镜像把.pt文件扔进Git LFS是灾难源头。我们采用模型即镜像Model-as-Image方案将模型权重、推理代码、依赖库全部打包为OCI镜像# Dockerfile.model FROM python:3.10-slim # 安装系统级依赖避免pip install的ABI不兼容 RUN apt-get update apt-get install -y libglib2.0-0 libsm6 libxext6 libxrender-dev # 复制推理代码非训练代码 COPY inference/ /app/inference/ # 下载并验证模型权重使用私有registry ARG MODEL_REGISTRYharbor.example.com ARG MODEL_REFscrew-detector:v2.3.1 RUN curl -fSL https://${MODEL_REGISTRY}/v2/${MODEL_REF}/blobs/sha256:abc123 \ -o /app/model.pt \ echo abc123 /app/model.pt | sha256sum -c - # 设置入口点 ENTRYPOINT [python, /app/inference/server.py]关键优势版本原子性screw-detector:v2.3.1镜像包含特定commit的推理代码特定SHA的权重杜绝“代码是v2.3权重是v2.2”的混乱安全扫描Trivy可直接扫描镜像中的CVE如trivy image harbor.example.com/screw-detector:v2.3.1网络隔离模型下载在构建阶段完成运行时无需访问外部存储实测对比Git LFS方式模型加载耗时2.3s网络IO解压OCI镜像方式0.17s本地磁盘读取。在边缘设备上这决定着能否满足100ms硬实时要求。3.3 第三步实现零拷贝特征预处理绕过Python瓶颈图像预处理是最大性能黑洞。OpenCV的cv2.resize()在Python中调用每次都会触发内存拷贝。我们的方案是用C编写预处理内核通过PyBind11暴露为Python函数// preprocess_kernel.cpp #include opencv2/opencv.hpp #include pybind11/pybind11.h #include pybind11/numpy.h void fast_resize(const py::array_tuint8_t input, py::array_tuint8_t output, int target_h, int target_w) { auto buf input.request(); auto out_buf output.request(); cv::Mat src(target_h, target_w, CV_8UC3, (void*)buf.ptr); cv::Mat dst; // 使用INTER_AREA插值对下采样更优 cv::resize(src, dst, cv::Size(target_w, target_h), 0, 0, cv::INTER_AREA); memcpy(out_buf.ptr, dst.data, dst.total() * dst.elemSize()); } PYBIND11_MODULE(preprocess, m) { m.def(fast_resize, fast_resize, Fast resize with zero-copy); }编译后在Python中调用import preprocess # input_array是numpy.ndarrayflags[C_CONTIGUOUS]为True output_array np.empty((416, 416, 3), dtypenp.uint8) preprocess.fast_resize(input_array, output_array, 416, 416)性能提升在Jetson AGX Orin上Python OpenCV耗时83msC内核仅9.2ms。更重要的是内存占用降低67%——因为避免了np.array(cv2.resize(...))创建临时数组。3.4 第四步设计状态无关的推理服务无session无全局变量很多AI服务崩溃源于状态污染。例如# 危险代码全局模型实例 model load_model(best.pt) # 所有请求共享 app.post(/detect) def detect(image: UploadFile): # 并发请求可能同时修改model内部状态 return model.predict(image)正确做法是每个请求独占模型实例但需解决加载开销问题。我们采用模型实例池Model Instance Pool# model_pool.py from queue import Queue import threading class ModelPool: def __init__(self, model_path, max_instances5): self.pool Queue(max_instances) # 预热实例 for _ in range(max_instances): self.pool.put(self._create_instance(model_path)) def acquire(self): try: return self.pool.get_nowait() except: return self._create_instance() # 动态扩容 def release(self, instance): if self.pool.qsize() self.pool.maxsize: self.pool.put(instance) # 在FastAPI依赖注入中使用 model_pool ModelPool(model.pt) app.post(/detect) def detect(image: UploadFile, pool: ModelPool Depends(lambda: model_pool)): model pool.acquire() try: result model.predict(image) return result finally: pool.release(model)关键保障model.predict()必须是纯函数——不修改任何全局状态不依赖time.time()等易变因子。为此我们在模型类中禁用所有torch.nn.Module的train()/eval()切换强制model.eval()在初始化时固化。3.5 第五步实施渐进式流量迁移非蓝绿而是金丝雀熔断上线新模型不能简单切流。我们的流程是三级验证离线验证用10000条历史样本跑A/B测试要求新模型precision0.5提升≥0.5%且false_positive_rate不升金丝雀发布Istio配置5%流量到新服务监控ai_prediction_latency_seconds_bucket{le100}占比若95%则自动回滚熔断保护当新服务5分钟内5xx错误率1%时Envoy立即切断流量并触发告警ALERT ModelDegradation具体Istio配置# virtual-service-canary.yaml apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: ai-inference spec: hosts: - ai-api.example.com http: - route: - destination: host: ai-inference-primary weight: 95 - destination: host: ai-inference-canary weight: 5 fault: abort: httpStatus: 503 percentage: value: 0.1 # 0.1%请求注入503验证熔断逻辑实操心得在Canary阶段必须对比两个服务的/metrics端点重点看ai_prediction_confidence_bucket{le0.9}——若新模型在此桶的count下降说明置信度整体降低即使accuracy数字好看也不应放量。3.6 第六步构建模型健康度仪表盘非Accuracy而是业务指标Accuracy是学术幻觉。产线真正关心的是漏检率Miss Rate应0.3%每1000个螺丝最多漏检3个误报率False Alarm应5%避免工人频繁停线确认平均修复时间MTTR从告警到恢复15分钟我们的Grafana仪表盘包含三个核心面板漏检热力图按产线工位聚合count(ai_prediction_class{classloose}) by (workstation)红色越深表示该工位漏检越多置信度漂移检测用KS检验比较当日与基准日ai_prediction_confidence分布p-value0.01时标红特征稳定性指数对每个输入特征计算|current_mean - baseline_mean| / baseline_std3σ则告警数据源来自Kafka Topicai-inference-audit其中每条消息包含{ request_id: req_abc123, timestamp: 2023-10-05T08:23:41Z, input_hash: sha256:xyz789, predictions: [...], latency_ms: 47.2, features: {brightness: 128.4, contrast: 23.1} }注意所有审计日志必须包含input_hash。当客户投诉“这个图识别错了”我们能用hash快速定位原始图像避免“你说的图在哪”的扯皮。3.7 第七步建立模型退役机制非删除而是优雅下线模型不是永久资产。当新模型v3.0上线后v2.3不能简单删掉镜像——要确保所有依赖v2.3的旧客户端仍能调用通过API网关路由v2.3的指标继续上报直到确认无流量v2.3的镜像保留在Harbor中30天供审计回溯实现方案API网关层Kong配置/v2/detect路由到v2.3服务/v3/detect路由到v3.0镜像生命周期管理Harbor的Retention Policy设置为“保留最近3个tag且tag名匹配v2.*的镜像保留30天”退役检查清单每日执行SQLSELECT COUNT(*) FROM kong_routes WHERE tags ARRAY[v2.3]当结果为0时触发最终清理最关键的一步在v2.3服务中注入退役倒计时# 在v2.3服务启动时 import atexit def schedule_deprecation(): # 30天后自动退出 timer threading.Timer(30*24*3600, lambda: os._exit(1)) timer.start() atexit.register(schedule_deprecation)4. 真实踩坑记录那些让AI工程师彻夜难眠的12个故障现场4.1 故障1GPU显存“幽灵泄漏”发生概率极高现象服务运行72小时后OOMKillednvidia-smi显示显存占用从3.2GB涨到15.8GB但torch.cuda.memory_allocated()始终显示3.2GB。根因PyTorch的torchvision.transforms.Resize在GPU上执行时会缓存不同尺寸的CUDA kernel而缓存未被释放。当产线相机分辨率偶尔波动如4096x3072→4096x3073就触发新kernel编译并驻留显存。解决方案强制统一输入尺寸在预处理层用cv2.resize()固定为416x416禁用torchvision.transforms清理CUDA缓存在每次推理后调用torch.cuda.empty_cache()虽慢15ms但保命监控指标nvidia_gpu_duty_cycle{device0}持续95%时告警表明kernel编译风暴实操心得在Dockerfile中添加ENV PYTORCH_CUDA_ALLOC_CONFmax_split_size_mb:128限制单次分配大小避免大块显存碎片。4.2 故障2时区错乱导致的特征失效发生概率中现象模型在UTC时间03:00-04:00期间准确率暴跌30%其他时段正常。根因特征工程中使用pd.to_datetime(df[timestamp]).dt.hour而上游数据的时间戳是本地时区CST但服务器时区为UTC。to_datetime()默认按UTC解析导致小时值全错。解决方案统一时区所有时间戳存储为ISO 8601带时区格式2023-10-05T08:23:4108:00特征代码强制声明时区pd.to_datetime(df[timestamp]).dt.tz_localize(Asia/Shanghai).dt.tz_convert(UTC)在数据管道加入时区校验assert df[timestamp].str.contains(r\\d{2}:\d{2}$).all()注意不要用datetime.now()获取当前时间——它依赖系统时区。改用datetime.now(timezone.utc)。4.3 故障3gRPC连接池耗尽发生概率高现象服务QPS从1200骤降至37grpc_client_socket_send_failure指标飙升。根因gRPC Python客户端默认max_workers10当并发请求超过10时后续请求排队等待而等待队列无超时机制导致线程阻塞。解决方案增加工作线程server grpc.server(futures.ThreadPoolExecutor(max_workers50))设置连接超时channel grpc.insecure_channel(host:50051, options[ (grpc.max_send_message_length, 100 * 1024 * 1024), (grpc.max_receive_message_length, 100 * 1024 * 1024), (grpc.http2.max_pings_without_data, 0) # 禁用ping保活减少干扰 ])监控连接数grpc_server_started_rpc_counter{method/Inference/Detect}持续增长且不下降表明连接未释放4.4 故障4ONNX Runtime的隐式类型转换发生概率中现象ONNX模型在Python中推理结果正常在C中输出全为0。根因ONNX Runtime C API要求输入张量dtype严格匹配模型定义。Python版自动将np.float32转为float32但C版需显式指定// 错误未指定dtype Ort::Value input_tensor Ort::Value::CreateTensor( memory_info, input_data, input_shape, ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT); // 正确显式指定 Ort::Value input_tensor Ort::Value::CreateTensorfloat( memory_info, input_data, input_shape, ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT);解决方案在模型导出时固定输入dtypetorch.onnx.export(..., input_names[input], dynamic_axes{input: {0: batch}})C代码中用Ort::Value::GetTensorTypeAndShape()校验输入张量类型添加类型断言assert(tensor.GetType() ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT)4.5 故障5Docker镜像层缓存失效发生概率高现象CI/CD流水线构建时间从2分30秒暴涨至18分钟。根因Dockerfile中COPY requirements.txt .在COPY . .之后导致每次代码变更都使requirements.txt层失效重新pip install。解决方案# 正确顺序先复制依赖文件再复制代码 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY inference/ /app/inference/ COPY model.pt /app/model.pt实操心得用docker history image查看各层大小若某层500MB且非模型文件则存在优化空间。4.6 故障6Prometheus指标名称冲突发生概率低但致命现象Grafana中ai_prediction_confidence指标消失curl /metrics返回duplicate metrics collector registration attempted。根因多个FastAPI应用实例注册了同名Collector。当使用uvicorn --workers 4时每个worker进程都尝试注册Histogram(ai_prediction_confidence, ...)。解决方案使用multiprocess模式PROMETHEUS_MULTIPROC_DIR/tmp/prometheus并在启动时设置环境变量或改用aioprometheus库其Registry支持进程间共享关键检查ls /tmp/prometheus/应有多个*.db文件每个worker一个4.7 故障7特征存储的时序错位发生概率中现象模型预测结果与实际缺陷位置偏差±2帧。根因特征提取服务与图像采集服务时钟不同步。采集端用NTP同步特征服务用系统本地时间导致时间戳偏移。解决方案统一授时所有服务通过PTPPrecision Time Protocol同步精度100ns时间戳打标在图像采集硬件层如Basler相机直接嵌入PTP timestamp到JPEG EXIF特征服务读取EXIFPIL.Image.open(img).getexif()[36867]DateTimeOriginal4.8 故障8PyTorch DataLoader的内存泄漏发生概率高现象训练脚本运行2小时后OOMps aux --sort-%mem显示Python进程占内存98%。根因DataLoader(num_workers0)的子进程未正确关闭导致shared_memory对象累积。解决方案显式关闭dataloader DataLoader(...); for batch in dataloader: ...; dataloader._iterator._shutdown_workers()或改用num_workers0单进程用torchvision.io.read_image()替代PIL.Image.open()监控指标psutil.virtual_memory().percent持续90%时触发告警4.9 故障9Kubernetes Pod的OOMKilled误判发生概率中现象Pod频繁重启事件显示OOMKilled但kubectl top pod显示内存使用仅1.2Gi。根因容器内存限制为2Gi但kubectl top显示的是RSSResident Set Size而OOMKiller判断依据是container_memory_working_set_bytes包含page cache。解决方案监控container_memory_working_set_bytes{containerai-inference}当1.8Gi时告警设置内存请求限制resources.requests.memory resources.limits.memory避免K8s过度调度在容器内启用memory.pressurecgroup v2指标4.10 故障10HTTP/2连接复用失效发生概率中现象客户端并发请求时http2.streams_idle指标持续增长连接数暴增。根因FastAPI的Uvicorn服务器默认--http httpHTTP/1.1未启用HTTP/2。而gRPC客户端强制HTTP/2导致连接不复用。解决方案启用HTTP/2uvicorn app:app --http http2 --ssl-keyfile key.pem --ssl-certfile cert.pem客户端使用httpx.AsyncClient(http2True)监控http2.streams_opened_total与http2.streams_closed_total差值100时告警4.11 故障11模型权重的数值溢出发生概率低但隐蔽现象模型在某些图像上输出全NaN但训练日志无异常。根因FP16量化时某些层权重标准差过大导致torch.nn.Linear的weight在FP16下溢出为0。解决方案量化前标准化layer.weight.data (layer.weight.data - layer.weight.data.mean()) / layer.weight.data.std()使用torch.amp.autocast替代纯FP16with torch.amp.autocast(device_typecuda):添加NaN检测assert not torch.isnan(model(input)).any()4.12 故障12Consul服务发现延迟发生概率中现象新Pod启动后流量5分钟才导入期间大量503。根因Consul默认deregister_critical_service_after30m但健康检查间隔为10s导致新服务注册后需等待多个检查周期。解决方案缩短健康检查check: {http: http://localhost:8000/health, interval: 2s, timeout: 1s}启用Consul Connectconnect: {sidecar_service: {proxy: {upstreams: [{destination_name: ai-inference, local_bind_port: 8080}]}}}监控consul_catalog_service_nodes{serviceai-inference}当值从0突增至1时触发告警5. 工程师的自我修养超越技术栈的5项反直觉原则5.1 原则一永远先写破坏性测试再写功能代码在实现任何新功能前我强制自己写一个必然失败的测试。例如开发特征归一化模块时第一行代码不是def normalize(x):而是def test_normalize_breaks_on_edge_case(): # 构造极端输入全零向量 x np.zeros((100, 3)) with pytest.raises(ZeroDivisionError): normalize(x) # 应该除零然后才去实现normalize()让它通过这个测试。这看似浪费时间实则规避了90%的边界条件漏洞。去年我们有个模型在产线崩溃根因是特征标准化时未处理全零输入——而这个测试本可在开发阶段捕获。5.2 原则二把“不可能”写进日志而非注释代码注释# TODO: handle edge case毫无价值。真正有效的是在日志中明确声明“不可能”# 错误注释 # TODO: check if image is None # 正确日志 if image is None: logger.critical(CRITICAL: image is None - this should never happen per contract v1.2) raise RuntimeError(Contract violation: image must be provided)当这条日志出现时它不再是bug而是合同违约证据直接触发SLA赔偿流程。我们因此将平均故障定位时间从47分钟缩短至3.2分钟。5.3 原则三用物理单位约束参数而非数字batch_size32是危险的。应该写成batch_size32 * images并在代码中定义from typing import NewType ImageCount NewType(ImageCount, int) BatchSize NewType(BatchSize, ImageCount) def train(batch_size: BatchSize): pass train(BatchSize(ImageCount(32))) # 类型安全IDE可提示这迫使你在调整batch_size时思考物理意义32张图是否超过GPU显存是否匹配产线图像采集频率去年我们因此避免了一次因batch_size128导致的显存溢出事故。5.4 原则四为每个API端点配置独立的熔断器不要用全局熔断器。/v1/detect
返回列表