DJL + ONNX 实战:在Java生态中高效部署AI模型

在AI浪潮席卷各行各业的今天,大多数Java开发者面临着一个尴尬的困境:明明身在Java技术栈,却不得不为了AI能力去学习Python、维护一套独立的Python服务、处理跨语言调用的各种麻烦。
如果你也有类似的困扰,那么这篇文章正是为你准备的。本文将带你认识Deep Java Library(DJL)—— Java生态中目前唯一真正意义上的AI框架,并阐述为什么“Python训练 + ONNX + DJL部署”是当下Java团队接入AI能力的最优路径。
一、背景:DJL是什么?它从何而来?
1.1 DJL介绍
Deep Java Library(DJL)是亚马逊在2019年12月的AWS re:Invent大会上正式宣布开源的一个深度学习开发包。它是一个引擎无关(Engine-Agnostic)的深度学习框架,使用原生Java概念构建,为Java开发者提供了训练和部署深度学习模型的能力。
简单来说,DJL不是一个“重新发明轮子”的深度学习框架——它不做底层的计算内核,而是做工程胶水。它在上层提供统一的 NDArray、Model、Predictor 等Java风格API,底层可以无缝切换PyTorch、TensorFlow、ONNX Runtime等多种引擎。
1.2 发展历程:从AWS实验室到独立社区
DJL的发展历程颇具代表性。它于2019年在AWS re:Invent上开源,最初托管在awslabs组织下。随着社区的迅速壮大,2021年5月,DJL从awslabs迁移到了一个独立的GitHub组织deepjavalibrary。这一举措标志着DJL不再仅仅是AWS内部的一个实验性项目,而成长为一个真正独立的Java深度学习社区。
截至目前,DJL在GitHub上已获得超过4800颗Star、750+ fork,社区持续活跃。版本迭代从未停歇——从2021年的0.11.0到2025年底的0.36.0,DJL保持着稳定的发布节奏。每一次版本更新都在不断扩展引擎支持、优化性能和增强稳定性。
1.3 为什么说DJL是“Java界唯一的AI框架”?
在DJL出现之前,Java开发者想搞AI,选择非常有限:
-
Deeplearning4j(DL4J) :2014年诞生,是JVM生态的老牌深度学习库。但它与主流Python生态的模型格式兼容性较差,社区活跃度也远不如Python阵营。
-
TensorFlow Java API:长期停留在1.x时代,对现代模型(如Transformer、LSTM等)的支持残缺不全。
-
ONNX Runtime Java API:提供了推理能力,但缺乏完整的模型生命周期管理、预处理/后处理管道等高层抽象。
DJL的出现填补了这个空白。它既兼容主流Python生态训练出的模型(PyTorch、TensorFlow、ONNX等),又提供了Java开发者熟悉的API风格和完整的工程化能力。这正是它能在短短几年内成为Java AI部署事实标准的核心原因。
二、为什么说“Python训练 + DJL部署”是最佳实践?
2.1 认清现实:Python在训练端不可替代
我们必须承认一个事实:Python在AI研究和模型训练领域的主导地位短期内不会改变。PyTorch、TensorFlow、Hugging Face Transformers等工具链已经形成了极其完善的生态,算法工程师用Python训练模型是最高效的方式。
但这不意味着Java开发者必须全程使用Python。训练和部署是两个完全不同的场景,它们对技术栈的要求截然不同:
-
训练:需要灵活的调试环境、丰富的实验工具、快速迭代的能力 → Python是王者
-
部署:需要高并发、低延迟、稳定可靠、与现有系统无缝集成 → Java是王者
强行要求算法团队用Java写训练代码,或强行要求Java团队用Python写生产服务,都是不经济的。
2.2 Java部署的独特优势
为什么说Java是AI部署的更优选择?数据会说话:
-
Java/JVM承载了全球68%的企业级应用。绝大多数后端系统、大数据平台、微服务架构都运行在Java生态中。
-
Netflix使用DJL进行分布式深度学习推理,处理日志数据时单事件延迟仅7ms。
-
某头部互联网公司将CTR模型从TensorFlow转为ONNX + DJL后,推理延迟从22ms降至15ms。
-
Amazon Search将TensorFlow模型转为ONNX + DJL后,延迟从12ms降至4ms。
-
国内某头部银行将OCR模型转为ONNX + DJL后,延迟从1s降至400ms。
这些数据揭示了一个清晰的趋势:在推理部署场景中,Java + ONNX的组合往往能带来显著的性能提升。原因在于Java成熟的JIT编译、多线程优化和内存管理能力,以及ONNX Runtime底层C++算子的高效执行。
2.3 ONNX:连接Python训练与Java部署的桥梁
既然训练用Python、部署用Java,那么两者之间如何衔接?答案就是ONNX(Open Neural Network Exchange)。
ONNX是AI领域的开放标准格式,PyTorch、TensorFlow、PaddlePaddle等主流框架都已支持导出ONNX模型。这意味着:
-
算法团队用熟悉的Python框架训练模型
-
将模型导出为ONNX格式(一条命令的事)
-
Java团队用DJL加载ONNX模型直接推理
整个过程无需任何Python-to-Java的代码翻译,无需部署额外的Python服务,无需操心跨语言调用的网络开销和稳定性问题。
2.4 DJL在部署端的核心优势
具体到DJL这个工具,它在部署端的优势可以归纳为以下几点:
① 原生Java体验,学习成本极低
DJL的API设计遵循Java惯用法,使用起来就像调用任何一个普通的Java库。对于熟悉Java的开发者来说,几乎没有学习曲线。
② 引擎无关,一次编写到处运行
你只需要写一套代码,底层可以在PyTorch、TensorFlow、ONNX Runtime之间自由切换。今天用ONNX部署,明天想换TensorRT获得更高性能?改一行配置就行。
③ 生产级性能
DJL原生支持多线程推理,在单个JVM中运行多线程推理时,吞吐量甚至超过了市面上大多数基于C++的模型服务器。它内置了NDManager自动管理张量内存生命周期,避免内存泄漏。
2.5 落地分工策略
| 环节 | 技术选型 | 负责团队 |
|---|---|---|
| 模型训练 | Python + PaddlePaddle/PyTorch/TensorFlow | 算法团队 |
| 模型导出 | ONNX | 算法团队 |
| 模型部署 | Java + DJL + ONNX Runtime | Java开发团队 |
三、代码实战
聊完背景和优势,干货环节正式开启。O(∩_∩)O 既然 DJL 被说得这么好,那么真正上手如何呢? 我可以告诉你:So Easy!接下来跟着我的步骤,保你不踩坑🕳,一次性上手。
本次实战的环境说明:
- Java:JDK 17 (请使用 Azul Zulu 发行版)
- 包管理工具:maven
- 推理设备:CPU
- AI模型:使用 Paddle 端到端主体检测模型 picodet_lcnet_x2_5_640_mainbody
请大家按照如上环境配置,进行如下代码实战。尤其,在 Windows 平台下,不要使用 Oracle JDK(有Bug,会报错),一定使用 Azul Zulu 发行版。(这里是坑一)
第 1 步:配置 maven pom
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<djl.version>0.36.0</djl.version>
<onnxruntime.version>1.29.0</onnxruntime.version>
</properties>
<dependencies>
<!-- 1. DJL 核心 API -->
<dependency>
<groupId>ai.djl</groupId>
<artifactId>api</artifactId>
<version>${djl.version}</version>
</dependency>
<!-- 2. ONNX Runtime 引擎(主推理,高性能) -->
<dependency>
<groupId>ai.djl.onnxruntime</groupId>
<artifactId>onnxruntime-engine</artifactId>
<version>${djl.version}</version>
</dependency>
<dependency>
<groupId>com.microsoft.onnxruntime</groupId>
<artifactId>onnxruntime</artifactId>
<version>${onnxruntime.version}</version>
</dependency>
<!-- 3. PyTorch 引擎(辅助,负责复杂 NDArray 预处理/后处理) -->
<dependency>
<groupId>ai.djl.pytorch</groupId>
<artifactId>pytorch-engine</artifactId>
<version>${djl.version}</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>2.0.17</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.32</version>
</dependency>
</dependencies>
第 2 步:日志配置
logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 日志格式 -->
<property name="LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<charset>UTF-8</charset>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 根日志级别 -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
<!-- 可选:调试DJL时放开注释 -->
<logger name="ai.djl" level="DEBUG"/>
<logger name="ai.onnxruntime" level="DEBUG"/>
</configuration>
第 3 步:Java 代码编写
- 模型加载与推理:PicodetLcnetMainbodyTest.java
import ai.djl.inference.Predictor;
import ai.djl.modality.cv.Image;
import ai.djl.modality.cv.ImageFactory;
import ai.djl.modality.cv.output.DetectedObjects;
import ai.djl.onnxruntime.engine.OrtEngine;
import ai.djl.repository.zoo.Criteria;
import ai.djl.repository.zoo.ZooModel;
import ai.djl.training.util.ProgressBar;
import com.ty.translator.PicodetLcnetMainbodyTranslator;
import com.ty.utils.ImageUtil;
import com.ty.utils.LogUtil;
import com.ty.utils.OnnxUtil;
import org.apache.commons.io.FilenameUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.file.Paths;
import java.util.List;
/**
* Paddle 主体检测模型 picodet_lcnet_x2_5_640_mainbody 测试
*
* @Author Tommy
* @Date 2026/8/18
*/
public class PicodetLcnetMainbodyTest {
static String ONNX_PATH = "/path/to/picodet_lcnet_x2_5_640_mainbody/inference.onnx";
static String IMG_PATH = "/path/to/drink_dataset_v2.0/test_images/005.jpeg";
static double THRESHOLD = 0.2;
static {
OnnxUtil.config();
LogUtil.enabel_jul();
}
public static void main(String[] args) {
// 1. 使用 Criteria 构建模型加载器
Criteria<Image, DetectedObjects> criteria = Criteria.builder()
.optEngine(OrtEngine.ENGINE_NAME)
.optModelPath(Paths.get(ONNX_PATH)) // 指定ONNX模型文件路径
.setTypes(Image.class, DetectedObjects.class)
.optTranslator(new PicodetLcnetMainbodyTranslator()) // 前置处理与后置处理的Translator
.optModelName("PicodetLcnetMainbodyModel")
.optProgress(new ProgressBar())
.build();
// 2. 加载模型
try (ZooModel<Image,DetectedObjects> model = criteria.loadModel()) {
System.out.printf("成功加载 ONNX 模型:%s%n", ONNX_PATH);
// 3. 创建 Predictor(推理对象)
try (Predictor<Image, DetectedObjects> predictor = model.newPredictor()) {
// 4. 载入图片
Image inputImage = ImageFactory.getInstance().fromFile(Paths.get(IMG_PATH));
// 5. 推理
DetectedObjects results = predictor.predict(inputImage);
// 6. 根据阈值, 过滤结果
List<DetectedObjects.DetectedObject> resultList = results.topK(5).stream().map(r -> (DetectedObjects.DetectedObject) r).filter(r -> r.getProbability() >= THRESHOLD).toList();
System.out.println("推理结果:\n " + resultList);
// 7. 将结果绘制到图片
ImageUtil.drawBoundingBoxes(inputImage, resultList);
// 8. 保存绘制后的图片
File outputFile = new File("result_" + FilenameUtils.getBaseName(IMG_PATH) + ".png");
inputImage.save(new FileOutputStream(outputFile), "png");
System.out.println("推理结果图片保存至:" + outputFile.getAbsolutePath());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
- DJL Translator 前处理与后处理:PicodetLcnetMainbodyTranslator.java
import ai.djl.modality.cv.Image;
import ai.djl.modality.cv.output.BoundingBox;
import ai.djl.modality.cv.output.DetectedObjects;
import ai.djl.modality.cv.output.Rectangle;
import ai.djl.modality.cv.util.NDImageUtils;
import ai.djl.ndarray.NDArray;
import ai.djl.ndarray.NDList;
import ai.djl.ndarray.NDManager;
import ai.djl.ndarray.types.DataType;
import ai.djl.ndarray.types.Shape;
import ai.djl.translate.NoBatchifyTranslator;
import ai.djl.translate.TranslatorContext;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
/**
* Paddle 主体检测模型 前置处理与后置处理的Translator
*
* @Author Tommy
* @Date 2026/8/18
*/
public class PicodetLcnetMainbodyTranslator implements NoBatchifyTranslator<Image, DetectedObjects> {
private final float[] mean = {0.485f, 0.456f, 0.406f};
private final float[] std = {0.229f, 0.224f, 0.225f};
/**
* 前置处理
*/
@Override
public NDList processInput(TranslatorContext ctx, Image input) throws Exception {
long begin = System.currentTimeMillis();
int origH = input.getHeight();
int origW = input.getWidth();
NDManager manager = ctx.getNDManager();
NDArray img = input.toNDArray(manager);
// 1. Resize 到 640x640,使用 INTER_CUBIC
int targetSize = 640;
img = NDImageUtils.resize(img, targetSize, targetSize, Image.Interpolation.BICUBIC);
// 2. 归一化:缩放至 [0,1] 并 z-score标准化
img = img.toType(DataType.FLOAT32, false);
NDArray meanArray = manager.create(mean, new Shape(1, 1, 3));
NDArray stdArray = manager.create(std, new Shape(1, 1, 3));
img = img.div(255.0f);
img = img.sub(meanArray).div(stdArray);
// 3. 转置为 CHW 并增加 batch 维度
img = img.transpose(2, 0, 1).expandDims(0);
// 4. 计算缩放比
float im_scale_y = (float) targetSize / origH;
float im_scale_x = (float) targetSize / origW;
NDArray scale_factor = manager.create(new float[] {im_scale_y, im_scale_x}, new Shape(1, 2));
long end = System.currentTimeMillis();
System.out.printf("前置处理耗时:%d ms %n", end - begin);
// 5.按照 ONNX模型 输入顺序构造 NDList:image 在前,scale_factor 在后
img.setName("image");
scale_factor.setName("scale_factor");
return new NDList(img, scale_factor);
}
/**
* 后置处理
*/
@Override
public DetectedObjects processOutput(TranslatorContext ctx, NDList list) throws Exception {
NDArray output = list.get(0);
Shape shape = output.getShape();
int rows = (int) shape.get(0); // 100
int cols = (int) shape.get(1); // 6
System.out.println(list);
System.out.println(output.toDebugString((int) shape.size(), shape.dimension(), rows, cols, true));
// 按行分组,每行6个元素
float[] data = output.toFloatArray(); // 一次性拷贝 NDArray 全部数据
float[][] rowsArray = IntStream.range(0, rows)
.mapToObj(i -> Arrays.copyOfRange(data, (i * cols), ((i + 1) * cols)))
.toArray(float[][]::new);
// 封装推理结果
List<String> classNames = new ArrayList<>(rows);
List<Double> probabilities = new ArrayList<>(rows);
List<BoundingBox> boundingBoxes = new ArrayList<>(rows);
for (int i = 0; i < rows; i++) {
String id = String.valueOf((int)rowsArray[i][0]); // 第一个值:分类ID
double score = rowsArray[i][1]; // 第二个值:置信度
// 3~6:矩形框4个坐标
float x1 = rowsArray[i][2];
float y1 = rowsArray[i][3];
float x2 = rowsArray[i][4];
float y2 = rowsArray[i][5];
classNames.add(id + "_foreground," + Math.round(score * 100) / 100f);
probabilities.add(score);
boundingBoxes.add(new Rectangle(x1, y1, (x2 - x1), (y2 - y1)));
}
return new DetectedObjects(classNames, probabilities, boundingBoxes);
}
}
- Onnx Util(可选):OnnxUtil.java
import java.nio.file.Paths;
/**
* Onnx Util
*
* @Author Tommy
* @Date 2026/8/18
*/
public class OnnxUtil {
public static final String ONNX_ENGINE_PATH = Paths.get(System.getProperty("user.home"), ".djl.ai", "onnx", "win-x64").toString();
public static void config() {
/*
* 以下操作解决此问题:ONNX Runtime 每运行一次,都会将 onnxruntime.dll onnxruntime4j_jni.dll onnxruntime_providers_shared.dll 解压到一个临时目录
* OnnxRuntime 源码中,finally 执行了 cleanUp (通过 File.deleteOnExit 实现),但它只能删除非空目录。因此,会造成大量垃圾数据。
*/
// 设置ONNX引擎路径(可选,但建议设置)
System.setProperty("onnxruntime.native.path", ONNX_ENGINE_PATH);
System.out.printf("ONNX引擎路径:%s%n", ONNX_ENGINE_PATH);
// 彻底杜绝任何网络请求(包括引擎 native 库的下载),可以设置离线模式
//System.setProperty("ai.djl.offline", "true");
}
}
说明:ONNX_ENGINE 相关 dll,全部在 onnxruntime.jar 中,可以从这里copy出来。对于初次使用的同学,可以在 PicodetLcnetMainbodyTest 中先注释掉 OnnxUtil.config() 这句代码。优先体验DJL,后续再关注DJL优化事宜。
- Log Util 将 onnxruntime 的jul日志流转到slf4j,便于观察DJL运行,有利于深入了解
import org.slf4j.bridge.SLF4JBridgeHandler;
import java.util.logging.Level;
import java.util.logging.LogManager;
/**
* Log Util
*
* @Author Tommy
* @Date 2026/8/18
*/
public class LogUtil {
public static void enabel_jul() {
// 1. (可选) 重置 JUL 配置,移除所有已有的 Handler
LogManager.getLogManager().reset();
// 2. 移除 JUL 根 Logger 上可能存在的旧 Handler
SLF4JBridgeHandler.removeHandlersForRootLogger();
// 3. 安装 SLF4J 桥接器,将 JUL 日志转发给 SLF4J
SLF4JBridgeHandler.install();
// 4. 重要:设置 JUL 根 Logger 的级别,确保日志能传递到 BridgeHandler
// JUL 默认级别是 INFO,如果需要 DEBUG 级别的日志,必须显式设置
java.util.logging.Logger.getLogger("").setLevel(Level.FINEST); // FINEST 对应 TRACE
}
}
- 绘图工具类Image Util,用于可视化推理结果
import ai.djl.modality.cv.Image;
import ai.djl.modality.cv.output.BoundingBox;
import ai.djl.modality.cv.output.DetectedObjects;
import ai.djl.modality.cv.output.DetectedObjects.DetectedObject;
import ai.djl.modality.cv.output.Rectangle;
import java.util.ArrayList;
import java.util.List;
/**
* Image 工具类
*
* @Author Tommy
* @Date 2026/8/18
*/
public class ImageUtil {
/**
* Draws the bounding boxes on the image.
*
* @param image DJL 图像
* @param list 推理结果集合
*/
public static void drawBoundingBoxes(Image image, List<DetectedObject> list) {
int w = image.getWidth();
int h = image.getHeight();
int size = list.size();
List<String> classNames = new ArrayList<>(size);
List<Double> probabilities = new ArrayList<>(size);
List<BoundingBox> boundingBoxes = new ArrayList<>(size);
for (DetectedObject r : list) {
classNames.add(r.getClassName());
probabilities.add(r.getProbability());
// DJL绘图 默认使用归一化坐标
Rectangle box = r.getBoundingBox().getBounds();
boundingBoxes.add(new Rectangle(box.getX() / w, box.getY() / h, box.getWidth() / w, box.getHeight()/ h));
}
image.drawBoundingBoxes(new DetectedObjects(classNames, probabilities, boundingBoxes));
}
}
四、测试
4.1 分步日志解析
- 运行 main 函数后,首先,看到如下日志,说明模型加载成功。
ONNX引擎路径:C:\Users\Tommy\.djl.ai\onnx\win-x64
2026-08-18 16:36:27.834 [main] DEBUG a.d.repository.RepositoryFactoryImpl - mount-s3 is not supported on Windows
2026-08-18 16:36:27.837 [main] DEBUG a.d.repository.RepositoryFactoryImpl - gcsfuse is not supported on Windows
2026-08-18 16:36:27.840 [main] DEBUG a.djl.repository.zoo.DefaultModelZoo - Scanning models in repo: class ai.djl.repository.SimpleRepository, file:/D:/models/picodet_lcnet_x2_5_640_mainbody.onnx
2026-08-18 16:36:27.843 [main] DEBUG ai.djl.repository.zoo.ModelZoo - Loading model with Criteria:
Application: UNDEFINED
Input: interface ai.djl.modality.cv.Image
Output: class ai.djl.modality.cv.output.DetectedObjects
Engine: OnnxRuntime
ModelZoo: ai.djl.localmodelzoo
2026-08-18 16:36:27.844 [main] DEBUG ai.djl.repository.zoo.ModelZoo - Searching model in specified model zoo: ai.djl.localmodelzoo
2026-08-18 16:36:27.846 [main] DEBUG ai.djl.engine.Engine - Registering EngineProvider: RPC
2026-08-18 16:36:27.847 [main] DEBUG ai.djl.engine.Engine - Registering EngineProvider: OnnxRuntime
2026-08-18 16:36:27.848 [main] DEBUG ai.djl.engine.Engine - Registering EngineProvider: PyTorch
2026-08-18 16:36:27.849 [main] DEBUG ai.djl.engine.Engine - Found default engine: PyTorch
2026-08-18 16:36:31.902 [main] DEBUG ai.djl.repository.zoo.ModelZoo - Checking ModelLoader: djl://ai.djl.localmodelzoo/picodet_lcnet_x2_5_640_mainbody.onnx [
ai.djl.localmodelzoo/inference.onnx/picodet_lcnet_x2_5_640_mainbody.onnx {}
]
2026-08-18 16:36:31.911 [main] DEBUG ai.djl.repository.MRL - Preparing artifact: file:/D:/models/picodet_lcnet_x2_5_640_mainbody.onnx, ai.djl.localmodelzoo/inference.onnx/inference.onnx {}
2026-08-18 16:36:31.911 [main] DEBUG ai.djl.repository.SimpleRepository - Skip prepare for local repository.
Loading: 100% |========================================|
2026-08-18 16:36:32.048 [main] DEBUG ai.onnxruntime.OnnxRuntime - Attempting to load native library 'onnxruntime' from specified path: C:\Users\Tommy\.djl.ai\onnx\win-x64
2026-08-18 16:36:32.079 [main] DEBUG ai.onnxruntime.OnnxRuntime - Loaded native library 'onnxruntime' from specified path
2026-08-18 16:36:32.079 [main] DEBUG ai.onnxruntime.OnnxRuntime - Attempting to load native library 'onnxruntime4j_jni' from specified path: C:\Users\Tommy\.djl.ai\onnx\win-x64
2026-08-18 16:36:32.082 [main] DEBUG ai.onnxruntime.OnnxRuntime - Loaded native library 'onnxruntime4j_jni' from specified path
2026-08-18 16:36:32.088 [main] DEBUG ai.onnxruntime.OnnxRuntime - Deleting C:\Users\Tommy\AppData\Local\Temp\onnxruntime-java16321184285320631155 on exit
2026-08-18 16:36:33.101 [main] DEBUG ai.djl.pytorch.jni.LibUtils - Loading native library: C:\Users\Tommy\.djl.ai\pytorch\2.7.1-cpu-win-x86_64\asmjit.dll
2026-08-18 16:36:33.106 [main] DEBUG ai.djl.pytorch.jni.LibUtils - Loading native library: C:\Users\Tommy\.djl.ai\pytorch\2.7.1-cpu-win-x86_64\c10.dll
2026-08-18 16:36:33.123 [main] DEBUG ai.djl.pytorch.jni.LibUtils - Loading native library: C:\Users\Tommy\.djl.ai\pytorch\2.7.1-cpu-win-x86_64\libiomp5md.dll
2026-08-18 16:36:33.144 [main] DEBUG ai.djl.pytorch.jni.LibUtils - Loading native library: C:\Users\Tommy\.djl.ai\pytorch\2.7.1-cpu-win-x86_64\libiompstubs5md.dll
2026-08-18 16:36:33.146 [main] DEBUG ai.djl.pytorch.jni.LibUtils - Loading native library: C:\Users\Tommy\.djl.ai\pytorch\2.7.1-cpu-win-x86_64\uv.dll
2026-08-18 16:36:33.153 [main] DEBUG ai.djl.pytorch.jni.LibUtils - Loading native library: C:\Users\Tommy\.djl.ai\pytorch\2.7.1-cpu-win-x86_64\fbgemm.dll
2026-08-18 16:36:33.168 [main] DEBUG ai.djl.pytorch.jni.LibUtils - Loading native library: C:\Users\Tommy\.djl.ai\pytorch\2.7.1-cpu-win-x86_64\torch_cpu.dll
2026-08-18 16:36:33.745 [main] DEBUG ai.djl.pytorch.jni.LibUtils - Loading native library: C:\Users\Tommy\.djl.ai\pytorch\2.7.1-cpu-win-x86_64\torch.dll
2026-08-18 16:36:33.748 [main] DEBUG ai.djl.pytorch.jni.LibUtils - Loading native library: C:\Users\Tommy\.djl.ai\pytorch\2.7.1-cpu-win-x86_64\0.36.0-djl_torch.dll
2026-08-18 16:36:33.780 [main] INFO ai.djl.pytorch.engine.PtEngine - PyTorch graph executor optimizer is enabled, this may impact your inference latency and throughput. See: https://docs.djl.ai/master/docs/development/inference_performance_optimization.html#graph-executor-optimization
2026-08-18 16:36:33.800 [main] INFO ai.djl.pytorch.engine.PtEngine - Number of inter-op threads is 4
2026-08-18 16:36:33.800 [main] INFO ai.djl.pytorch.engine.PtEngine - Number of intra-op threads is 4
2026-08-18 16:36:33.804 [main] INFO ai.djl.onnxruntime.engine.OrtModel - Onnx extension not found in classpath.
成功加载 ONNX 模型:D:/models/picodet_lcnet_x2_5_640_mainbody.onnx
- 看到如下日志,说明推理成功
前置处理耗时:97 ms
NDList size: 2
0 save_infer_model/scale_0.tmp_0: (100, 6) float32
1 save_infer_model/scale_1.tmp_0: (1) int32
save_infer_model/scale_0.tmp_0: (100, 6) cpu() float32
[[ 0.00000000e+00, 9.21961546e-01, 2.41165359e+02, 8.38219604e+01, 4.98690582e+02, 7.30696533e+02],
[ 0.00000000e+00, 7.77122736e-01, 5.43242798e+02, 6.08345276e+02, 7.49417236e+02, 7.34613770e+02],
[ 0.00000000e+00, 4.39203382e-01, 1.90857053e+00, 2.14971649e+02, 2.38050629e+02, 6.23279297e+02],
[ 0.00000000e+00, 3.82271945e-01, 5.32699707e+02, 4.70653290e+02, 7.49765076e+02, 6.40688049e+02],
[ 0.00000000e+00, 3.41555685e-01, 5.01894951e-01, 5.98395874e+02, 7.57524780e+02, 7.42105713e+02],
...
[ 0.00000000e+00, 5.28227687e-02, -7.56917119e-01, 1.27942177e+02, 5.74728088e+01, 1.86737061e+02],
[ 0.00000000e+00, 5.26817739e-02, 6.61091492e+02, 4.84884766e+02, 6.96175537e+02, 5.59596191e+02],
[ 0.00000000e+00, 5.26706576e-02, 4.16414413e+01, 5.93630615e+02, 9.62088852e+01, 6.32613831e+02],
]
推理结果:
[{"className": "0_foreground,0.92", "probability": 0.92196, "boundingBox": {"rect":[241.1653594970703,83.82196044921875,498.69056701660156,730.696533203125]}}, {"className": "0_foreground,0.78", "probability": 0.77712, "boundingBox": {"rect":[543.2427978515625,608.3452758789062,749.417236328125,734.61376953125]}}, {"className": "0_foreground,0.44", "probability": 0.43920, "boundingBox": {"rect":[1.9085705280303955,214.97164916992188,238.05062985420227,623.279296875]}}, {"className": "0_foreground,0.38", "probability": 0.38227, "boundingBox": {"rect":[532.69970703125,470.6532897949219,749.7650756835938,640.6880493164062]}}, {"className": "0_foreground,0.34", "probability": 0.34155, "boundingBox": {"rect":[0.5018949508666992,598.3958740234375,757.5247831344604,742.105712890625]}}]
推理结果图片保存至:D:\result_005.png
4.2 测试图片

4.3 测试结果对比
- Python 推理结果

- Java DJL 推理结果

结论:从推理结果进行对比,Java 与 Python 一致。
至此,Java 部署 AI 模型,全部结束,是不是 So so Easy 呀!O(∩_∩)O
最后
“Python训练 + ONNX导出 + DJL部署”是一条经过大规模生产验证的务实路径。它让算法团队做他们最擅长的事(训练模型),让Java团队做他们最擅长的事(构建高可用的生产系统)——各司其职,各取所长。
希望这篇文章能让更多Java开发者了解DJL、使用DJL,让Java生态在AI时代继续发光发热。
附录
- picodet_lcnet_x2_5_640_mainbody 模型下载地址:端到端主体检测ONNX模型picodet_lcnet_x2_5_640_mainbody_ONNX_Apache License 2.0-飞桨AI Studio星河社区---license: Apache License 2.0inference_framework: ONNX---## 端到端主体检测 ONNX 模型 —— picodet_lcnet_x2_5_640_mainbody### 一、模型简介`picodet_lcnet_x2_5_640_mainbody.onnx` 是基于 PaddleDetection 官方主体检测模型(picodet_lcn...
https://aistudio.baidu.com/modelsdetail/52734/space
更多推荐
所有评论(0)