使用Java语言开发AI项目

1. 现实的困境
数字化时代,众多的商业项目都具备了AI的属性。短视频推荐、景区人流量预测等各种应用的背后是一个个运行了AI算法的系统在提供支撑。

各个企业中,开发上述这些AI系统的工程师通常会划分为以下团队,前端团队,后端团队,算法团队。这样的工种划分传递到了校园招聘中,甚至传导到了高校的学生队伍中。从学生时代就会区分,某同学志在将来要从事应用系统开发,那么他就会有意无意的离算法远一些。立志从事算法研究的同学又会疏于应用系统开发所需繁杂知识的积累。学生毕业进入企业后,由于工种的划分,应用与算法的鸿沟进一步被拉大。

如果是纯算法研究项目或者研发一些演示项目,这样的划分并无不可。但是对于商用项目,这样的划分会导致很多问题:
1)后端研发人员不清楚算法实现逻辑,将所有涉及数值计算的特性都划分到算法,系统的应用微服务和算法微服务之间调用频繁,增加了业务的复杂度。后端研发人员没有深入业务,对系统的认识较为肤浅,个人成长也受限。

2)算法研发人员只关注算法功能的实现,对于算法在商用环境下的健壮性,可维护性考虑甚少。

3)java语言是强类型语言,很多问题在开发阶段就会暴露出来。同时,java语言具备强大的生态,各种代码静态检查工具能够检测出很多潜在的缺陷;各种开源的库几乎能解决你所有遇到的问题。
相比java语言,python语言的语法较为简洁,在机器学习和深度学习方面的生态更是java语言所无法比拟。但是,由于其本身的弱类型特征,很多问题在运行阶段才会暴露出来,导致系统的稳定性和安全性较差。

对于一款商业软件,软件开发完成部署上线的时间在整个软件生命周期中只占很小的比重。因此软件架构要做到高内聚、低耦合。选择的开发语言/框架要易于理解、生态完善、易于维护。

软件生命周期.png

2. 你永远不是第一个遇到问题的人
在软件开发领域,始终要相信你永远不是第一个遇到问题的人。困扰你的问题也会困扰别人(除非你的项目特别偏门),或许别人已经有了比较好的解决方案。

回到AI系统,在AI开发领域AWS构建了Deep Java Library (DJL),一个为 Java 开发者定制的开源深度学习框架。看下官网的介绍:

很长时间以来,Java 一直是一个很受企业欢迎的编程语言。得益于丰富的生态以及完善维护的包和框架,Java 拥有着庞大的开发者社区。尽管深度学习应用的不断演进和落地,提供给Java开发者的框架和库却十分短缺。现今主要流行的深度学习模型都是用 python 编译和训练的。对于 Java 开发者而言,如果要进军深度学习界,就需要重新学习并接受一门新的编程语言同时还要学习深度学习的复杂知识。这使得大部分Java开发者学习和转型深度学习开发变得困难重重。

为了减少 Java 开发者学习深度学习的成本,AWS构建了Deep Java Library (DJL),一个为 Java 开发者定制的开源深度学习框架。它为 Java 开发者对接主流深度学习框架提供了一个桥梁。DJL 同时对 Apache MXNet,PyTorch 和 TensorFlow 最新版本的支持,使得开发者可以轻松使用Java构建训练和推理任务。

3. DJL和Tablesaw
DJL设计的目标是在既有的深度学习框架之上提供了一套基于Java的API,屏蔽各种深度学习框架TensorFlow、PyTorch、Mxnet的差异,降低java开发者的学习成本。

DJL架构.png

看下官方example中目标检测的一段代码(看不懂没关系,看下大概的API就好):

public static DetectedObjects predict() throws IOException, ModelException, TranslateException {
        Path imageFile = Paths.get("src/test/resources/dog_bike_car.jpg");
        Image img = ImageFactory.getInstance().fromFile(imageFile);

        String modelUrl =
                "http://download.tensorflow.org/models/object_detection/tf2/20200711/ssd_mobilenet_v2_320x320_coco17_tpu-8.tar.gz";

        Criteria<Image, DetectedObjects> criteria =
                Criteria.builder()
                        .optApplication(Application.CV.OBJECT_DETECTION)
                        .setTypes(Image.class, DetectedObjects.class)
                        .optModelUrls(modelUrl)
          // saved_model.pb file is in the subfolder of the model archive file
                        .optModelName("ssd_mobilenet_v2_320x320_coco17_tpu-8/saved_model")
                        .optTranslator(new MyTranslator())
                        .optProgress(new ProgressBar())
                        .build();

        try (ZooModel<Image, DetectedObjects> model = ModelZoo.loadModel(criteria);
                Predictor<Image, DetectedObjects> predictor = model.newPredictor()) {
            DetectedObjects detection = predictor.predict(img);
            saveBoundingBoxImage(img, detection);
            return detection;
        }
    }

在数值计算方面,DJL提供了NDArray。使用NDArray,用和Numpy同样简单的语法,达到同样的效果。
例如:

使用numpy,

nd = np.ones((2, 3))
/*
[[1. 1. 1.]
[1. 1. 1.]]
*/

使用DJL提供的NDArray,

NDArray nd = manager.ones(new Shape(2, 3));
/*
ND: (2, 3) cpu() float32
[[1., 1., 1.],
[1., 1., 1.],
]
*/

很遗憾,DJL还没有提供python数值计算库pandas类似的API。但是,你永远不是第一个遇到问题的人,java语言的pandas已经有开源实现了。那就是Tablesaw库。(还有其他的库,Tablesaw较优)

创建Table(类比pandas中的dataframe),
String[] animals = {"bear", "cat", "giraffe"};
double[] cuteness = {90.1, 84.3, 99.7};

Table cuteAnimals =
    Table.create("Cute Animals")
        .addColumns(
            StringColumn.create("Animal types", animals),
            DoubleColumn.create("rating", cuteness));

导入文件:
Table bushTable = Table.read().csv("../data/bush.csv");

数据探索:
System.out.println(bushTable.structure())

          Structure of bush.csv          
 Index  |  Column Name  |  Column Type  |
-----------------------------------------
     0  |         date  |   LOCAL_DATE  |
     1  |     approval  |      INTEGER  |
     2  |          who  |       STRING  |

4.落地建议
项目中最近遇到的一个问题,后台程序需要对用户输入的一个文件进行分析计算,分别按照按15分钟和小时粒度汇总,然后计算得出各个粒度的阈值。算法开发人员已经用python代码实现了这个算法。解决这个问题有以下方案:

1)实现一个python服务,封装该算法。
这种方案的问题在于多了一个服务,多了一个服务要考虑的事情非常多。多实例,高可靠,日志等等。(其实这是典型的AWS Lambda的应用场景)
2)后台java服务用Spark提交计算任务
这种方案更复杂,为了调用一个python算法,部署一套spark环境,性价比太低。
3)java程序本地调用python代码
这种方案较为简单,但是需要java程序运行环境中同时具备python脚本的运行环境。docker基础镜像的体积增大不说,也提高了运维的复杂度。
4)使用Tablesaw库实现算法。
这种方案几乎没什么缺点,代价就是java程序员要把python算法再实现一遍。如果算法不是十分复杂,这个难度也不大。

5.项目边界划分
当前的项目边界划分,只要涉及略微复杂的数值计算都用python算法实现。

当前界限.png

后续DJL框架逐步稳定后,可以按照如下的界限划分:


未来界限.png

甚至部分模型训练的工作也可以放在java服务中。

6.人员能力终究有限

很多人可能会想,如果算法程序员开发的算法健壮性足够高,是否上述的问题都不存在了?这种想法可能有点理想化:
1)优秀的程序员很少,所以要借助各种语言特性保证我们的代码质量,否则世界上只有一门C语言就够了;
2)工程实践和工程素养都需要时间磨练

或许,DJL框架对于只写增删改查逻辑的程序员还是有一些门槛。一方面DJL在不断地降低门槛,另一方面很多时候难度来自自我设限。程序员群体基本都是大学本科以上学历,掌握API层面的AI知识还是能够做到的。况且,做电商软件的java程序员要学习电商业务,做财务软件的java程序员要学习财务知识,做AI的java程序员为何不能学习AI领域的知识呢?

对于springboot项目,aws提供了配置自动配置环境(底层算法引擎)和寻找模型

<dependency>
  <groupId>ai.djl.spring</groupId>
  <artifactId>djl-spring-boot-starter-autoconfigure</artifactId>
  <version>${djl.starter.version}</version>
</dependency>

同时,在jupyter notebook中也提供了入口(基于JDK9+版本的JShell技术实现,让我头一次认识到了JShell的价值所在):

jupyter_java.png

jupyter_java_demo.png

参考资料:
1.https://github.com/deepjavalibrary/djl(Deep Java Library (DJL))
2.https://djl.ai/website/demo.html(DJL Block Runner)
3.https://aws.amazon.com/cn/blogs/china/start-training-deep-learning-models-with-java/(动手用 Java 训练深度学习模型)
4.https://github.com/deepjavalibrary/djl/blob/master/jupyter/README.md(安装jupyter-java)
5.https://jtablesaw.github.io/tablesaw/gettingstarted(Java dataframe and visualization library)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容