DJL 之 Java 玩转多维数组,就像 NumPy 一样
本文适合有 Java 基础的人群

作者:DJL-Lanking
HelloGitHub 推出的《讲解开源项目》系列。有幸邀请到了亚马逊 + Apache 的工程师:Lanking( https://github.com/lanking520 ),为我们讲解 DJL —— 完全由 Java 构建的深度学习平台,本文为系列的第二篇。
一、前言

随着数据科学在生产中的应用逐步增加,使用 N维数组 灵活的表达数据变得愈发重要。我们可以将过去数据科学运算中的多维循环嵌套运算简化为简单几行。由于进一步释放了计算并行能力,这几行简单的代码运算速度也会比传统多维循环快很多。
这种数学计算的包已经成为数据科学、图形学以及机器学习领域的标准。同时它的影响力还在不断的扩大到其他领域。
在 Python 的世界,调用 NDArray(N维数组)的标准包叫做 NumPy。但是如今在 Java 领域中,并没有与之同样标准的库。为了给 Java 开发者创造同一种使用环境,亚马逊云服务开源了 DJL 一个基于 Java 的深度学习库。
尽管它包含了深度学习模块,但是它最核心的 NDArray 系统可以被用作 N维数组 的标准。它具备优良的可扩展性、全平台支持以及强大的后端引擎支持 (TensorFlow、PyTorch、Apache MXNet)。无论是 CPU 还是 GPU、PC 还是安卓,DJL 都可以轻而易举的完成任务。
在这个文章中,我们将带你了解 NDArray,并且教你如何写与 Numpy 同样简单的 Java 代码以及如何将 NDArray 使用在现实中的应用之中。
二、安装 DJL
可以通过下方的配置来配置你的 gradle 项目。或者你也可以跳过设置直接使用我们在线 JShell 。
在线 JShell 链接: https://djl.ai/website/demo.html#jshell
plugins {
id 'java'
}
repositories {
jcenter()
}
dependencies {
implementation "ai.djl:api:0.6.0"
// PyTorch
runtimeOnly "ai.djl.pytorch:pytorch-engine:0.6.0"
runtimeOnly "ai.djl.pytorch:pytorch-native-auto:1.5.0"
}
然后,我们就可以开始上手写代码了。
三、基本操作
我们首先尝试建立一个 try block 来包含我们的代码(如果使用在线 JShell 可跳过此步):
try(NDManager manager = NDManager.newBaseManager()) {
}
NDManager 是 DJL 中的一个 class 可以帮助管理 NDArray 的内存使用。通过创建 NDManager 我们可以更及时的对内存进行清理。当这个 block 里的任务运行完成时,内部产生的 NDArray 都会被清理掉。这个设计保证了我们在大规模使用 NDArray 的过程中,可以通过清理其中的 NDManager 来更高效的利用内存。
为了做对比,我们可以参考 NumPy 在 Python 之中的应用。
import numpy as np
3.1 创建 NDArray
ones 是一个创建全是1的N维数组操作.
Python (Numpy)
nd = np.ones((2, 3))
"""
[[1. 1. 1.]
[1. 1. 1.]]
"""
Java (DJL NDArray)
NDArray nd = manager.ones(new Shape(2, 3));
/*
ND: (2, 3) cpu() float32
[[1., 1., 1.],
[1., 1., 1.],
]
*/
你也可以尝试生成随机数。比如我们需要生成一些从 0 到 1 的随机数:
Python (Numpy)
nd = np.random.uniform(0, 1, (1, 1, 4))
# [[[0.7034806 0.85115891 0.63903668 0.39386125]]]
Java (DJL NDArray)
NDArray nd = manager.randomUniform(0, 1, new Shape(1, 1, 4));
/*
ND: (1, 1, 4) cpu() float32
[[[0.932 , 0.7686, 0.2031, 0.7468],
],
]
*/
这只是简单演示一些常用功能。现在 NDManager 支持多达 20 种在 NumPy 中 NDArray 创建的方法。
3.2 数学运算
你可以使用 NDArray 进行一系列的数学操作。假设你想做对数据做一个转置操作,然后对所有数据加一个数的操作。你可以参考如下的实现:
Python (Numpy)
nd = np.arange(1, 10).reshape(3, 3)
nd = nd.transpose()
nd = nd + 10
"""
[[11 14 17]
[12 15 18]
[13 16 19]]
"""
Java (DJL NDArray)
NDArray nd = manager.arange(1, 10).reshape(3, 3);
nd = nd.transpose();
nd = nd.add(10);
/*
ND: (3, 3) cpu() int32
[[11, 14, 17],
[12, 15, 18],
[13, 16, 19],
]
*/
DJL 现在支持 60 多种不同的 NumPy 数学运算,基本涵盖了大部分的应用场景。
3.3 Get 和 Set
其中一个对于 NDArray 最重要的亮点就是它轻松简单的数据设置/获取功能。我们参考了 NumPy 的设计,将 Java 过去对于数据表达中的困难做了精简化处理。
假设我们想筛选一个N维数组所有小于10的数:
Python (Numpy)
nd = np.arange(5, 14)
nd = nd[nd >= 10]
# [10 11 12 13]
Java (DJL NDArray)
NDArray nd = manager.arange(5, 14);
nd = nd.get(nd.gte(10));
/*
ND: (4) cpu() int32
[10, 11, 12, 13]
*/
是不是非常简单?接下来,我们看一下一个稍微复杂一些的应用场景。假设我们现在有一个3x3的矩阵,然后我们想把第二列的数据都乘以2:
Python (Numpy)
nd = np.arange(1, 10).reshape(3, 3)
nd[:, 1] *= 2
"""
[[ 1 4 3]
[ 4 10 6]
[ 7 16 9]]
"""
Java (DJL NDArray)
NDArray nd = manager.arange(1, 10).reshape(3, 3);
nd.set(new NDIndex(":, 1"), array -> array.mul(2));
/*
ND: (3, 3) cpu() int32
[[ 1, 4, 3],
[ 4, 10, 6],
[ 7, 16, 9],
]
*/
在上面的案例中,我们在 Java 引入了一个 NDIndex 的 class。它复刻了大部分在 NumPy 中对于 NDArray 支持的 get/set 操作。只需要简单的放进去一个字符串表达式,开发者在 Java 中可以轻松玩转各种数组的操作。
四、现实中的应用场景
上述的操作对于庞大的数据集是十分有帮助的。现在我们来看一下这个应用场景:基于单词的分类系统训练。在这个场景中,开发者想要利用从用户中获取的数据来进行情感分析预测。
NDArray 被应用在了对于数据进行前后处理的工作中。
4.1 分词操作
在输入到 NDArray 数据前,我们需要对于输入的字符串进行分词操作并编码成数字。下面代码中看到的 tokenizer 是一个 Map<String, Integer>,它是一个单词到字典位置的映射。
String text = "The rabbit cross the street and kick the fox";
String[] tokens = text.toLowerCase().split(" ");
int[] vector = new int[tokens.length];
/*
String[9] { "the", "rabbit", "cross", "the", "street",
"and", "kick", "the", "fox" }
*/
for (int i = 0; i < tokens.length; i++) {
vector[i] = tokenizer.get(tokens[i]);
}
vector
/*
int[9] { 1, 6, 5, 1, 3, 2, 8, 1, 12 }
*/
4.2 NDArray 处理
经过了编码操作后,我们创建了 NDArray 之后,我们需要转化数据的结构:
NDArray array = manager.create(vector);
array = array.reshape(new Shape(vector.length, 1)); // form a batch
array = array.div(10.0);
/*
ND: (9, 1) cpu() float64
[[0.1],
[0.6],
[0.5],
[0.1],
[0.3],
[0.2],
[0.8],
[0.1],
[1.2],
]
*/
最后,我们将数据传入深度学习模型中。如果使用 Java 要达到这些需要更多的工作量:如果我们需要实现类似于 reshape 的方法,我们需要创建一个N维数组:List<List<List<...List<Float>...>>> 来保证不同维度的可操作性。同时我们需要能够支持插入新的 List<Float> 来创建最终的数据格式。
五、NDArray 的实现过程
你也许会好奇 NDArray 究竟是如何在 DJL 之中构建的呢?接下来,我们会讲解一下 NDArray 在 DJL 内部中的架构。架构图如下:

如上图所示 NDArray 有三个关键的层。
界面层 (Interface) 包含了你所用到的 NDArray ,它只是一个 Java 的界面并定义了 NDArray 的输入输出结构。我们很仔细的分析了每一个方式的使用方法以便尽可能的将它们和用户的应用场景统一以及便于使用。
在引擎提供者层 (EngineProvider),是 DJL 各种深度学习引擎为 NDArray 界面开发的包。这个层把原生的深度学习引擎算子表达映射在 NumPy 之上。这样经过这样一层转译,我们在不同引擎上看到 NDArray 的表现都是一致的而且同时兼顾了 NumPy 的表现。
在 C++ 层,为了更便于 Java 使用,我们构建了 JNI 和 JNA 暴露出 C/C++ 的等方法,它可以保证我们有足够的方法来构建 NDArray 所需要的功能。同时 C++ 与 Java 的直接调用也可以保证 NDArray 拥有最好的性能。
六、为什么应该使用 NDArray 呢?
经过了这个教程,你应该获得了基本的 NDArray 在 Java 中的使用体验。但是这仍然只是表象,它的很多内在价值只有在生产环境中才能体现出来。总结一下 NDArray 具有如下几个优点:
- 易如反掌:轻松使用超过 60+ 个在 Java 中的方式实现与 NumPy 相同的结果。
- 快如闪电:具备各路深度学习框架加持,DJL NDArray 具备了各种硬件平台的加速,比如在 CPU 上的 MKLDNN 加速以及 GPU 上的 CUDA 加速,无论多大的数据集都可以轻松应对。
- 深度学习:同时具备高维数组、离散数组支持。你可以轻松的将 DJL 与其他大数据或者流数据平台结合起来应用:比如分布式处理的 Apache Spark 平台以及 Apache Flink 流数据平台。为你现有的方案构建一层深度学习的中间件。
NDArray 的到来帮助 DJL 成功转变为 Java 在深度学习领域中最好的工具。它具备平台自检测机制,无需任何额外设置,便可以在应用中构建基于 CPU/GPU 的代码。感兴趣的小伙伴快跟着教程感受下吧!
更多详情尽在 NDArray 文档:https://javadoc.io/doc/ai.djl/api/latest/ai/djl/ndarray/NDArray.html
关于 DJL

Deep Java Library (DJL) 是一个基于 Java 的深度学习框架,同时支持训练以及推理。 DJL 博取众长,构建在多个深度学习框架之上 (TenserFlow、PyTorch、MXNet 等) 也同时具备多个框架的优良特性。你可以轻松使用 DJL 来进行训练然后部署你的模型。
它同时拥有着强大的模型库支持:只需一行便可以轻松读取各种预训练的模型。现在 DJL 的模型库同时支持高达 70 个来自 GluonCV、 HuggingFace、TorchHub 以及 Keras 的模型。
在最新的版本中 DJL 0.6.0 添加了对于 MXNet 1.7.0、PyTorch 1.5.0、TensorFlow 2.2.0 的支持。我们同时也添加了 ONNXRuntime 以及 PyTorch 在安卓平台的支持。

关注 HelloGitHub 公众号
DJL 之 Java 玩转多维数组,就像 NumPy 一样的更多相关文章
- JAVA生成一个二维数组,使中间元素不与相邻的9个元素相等,并限制每一个元素的个数
JAVA生成一个二维数组,使中间元素不与相邻的9个元素相等,并限制每一个元素的个数 示例如下 至少需要九个元素:"A","B","C",&q ...
- Java中的二维数组
Java 中的二维数组 所谓二维数组,可以简单的理解为是一种"特殊"的一维数组,它的每个数组空间中保存的是一个一维数组. 那么如何使用二维数组呢,步骤如下: 1. 声明数组并分配空 ...
- java学习之二维数组
java当中的二维数组,存储一组比较特殊的对象.他存储一个数组,同时存储的数组当中又存储着元素. java二维数组的声明方式一: class Arr2Demo { public static void ...
- Java基础教程——二维数组
二维数组 Java里的二维数组其实是数组的数组,即每个数组元素都是一个数组. 每个数组的长度不要求一致,但最好一致. // 同样有两种风格的定义方法 int[][] _arr21_推荐 = { { 1 ...
- Java一维与二维数组的拷贝与排序
Java一维与二维数组的拷贝与排序 目录 Java一维与二维数组的拷贝与排序 Arrays.sort() 一维数组升序排序 二维数组按行升序排序 二维数组按列升序排序 Java中的数组 Java中数组 ...
- Java如何设定二维数组的上限?
在Java中,如何设定二维数组的上限? 以下示例中,使用arrayname.length来确定二维数组的上限(元素数目). package com.yiibai; public class Dimen ...
- 【转】java中定义二维数组的几种写法
原文链接 注:以下的 type[][] var 也可以这样申明 type var[][] type为数组的类型,var为变量名 写法一:行列固定的数组 //定义二维数组写法1 class Test { ...
- java基础编程——二维数组中的查找
题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数 ...
- Java实验——输出二维数组连续二维子数组的最大和
该算法思路,根据我博客里面一维子数组求和的思路,可以用一个新的二维数组对该二维区域的数组进行求和,例如新的二维数组的第5个位置,就代表从1到5斜对角线的块状区域的和,即1,2,4,5这4个数的和,x个 ...
随机推荐
- LVS-DR:搭建HTTP和HTTPS负载均衡集群
目录 LVS-DR实战:搭建HTTP和HTTPS负载均衡集群 1. 搭建lvs-dr模式的http负载集群 1.1 LVS上配置IP 1.2 RS上配置arp内核参数 1.3 RS上配置VIP 1.4 ...
- Spring事务专题(三)事务的基本概念,Mysql事务处理原理
前言 本专题大纲: 我重新整理了大纲,思考了很久,决定单独将MySQL的事务实现原理跟Spring中的事务示例分为两篇文章,因为二者毕竟没有什么实际关系,实际上如果你对MySQL的事务原理不感兴趣也可 ...
- 【NOIP2017】跳房子 题解(单调队列优化线性DP)
前言:把鸽了1个月的博客补上 ----------------- 题目链接 题目大意:机器人的灵敏性为$d$.每次可以花费$g$个金币来改造机器人,那么机器人向右跳的范围为$[min(d-g,1),m ...
- 一篇文章教会你用Python爬取淘宝评论数据(写在记事本)
[一.项目简介] 本文主要目标是采集淘宝的评价,找出客户所需要的功能.统计客户评价上面夸哪个功能多,比如防水,容量大,好看等等. 很多人学习python,不知道从何学起.很多人学习python,掌握了 ...
- 线程安全&Java内存模型
目录 Java内存模型 关于线程安全 Volatile关键字 Synchronized锁 重入锁 Lock锁 死锁 乐观锁与悲观锁 乐观锁(适合多读场景) 悲观锁(适合多写场景) Java内存模型 J ...
- springboot多环境配置文件
一.关于springboot的配置文件 springboot的配置文件主要有两种:properties文件和yml文件,我们只要选择一种使用就可以了.我们通过properties文件介绍一下配置的方式 ...
- 计算vtable的大小
在ClassFileParser::parseClassFile()函数中会计算vtable和itable所需要的大小,因为vtable和itable是内嵌在Klass中的,parseClassFil ...
- PHP文件包含学习笔记
看完下面的几篇文章,然后从第8行开始以后的内容可以忽略!此文是个笔记梳理,是对大佬文章简单的COPY记录,方便以后查看,自己只复现了其中的例子 参考文章: PHP文件包含漏洞利用思路与Bypass总结 ...
- 基于OpenSIPS 实现分机注册服务服务器
呼叫中心平台中坐席是不可或缺的一环,而坐席打电话自然需要使用办公分机.通常情况下我们通过软交换平台FreeSWITCH.Asterisk即可搭建分机注册服务. 但单台FreeSWITCH或Asteri ...
- Redis设计与实现——多机数据库的实现
复制 旧版Redis的复制功能分为同步(sync)和命令传播两个操作. sync:是一个非常耗费资源的操作 命令传播 ...