作者的原创文章,转载须注明出处。原创文章归作者所有,欢迎转载,但是保留版权。对于转载了博主的原创文章,不标注出处的,作者将依法追究版权,请尊重作者的成果。

人工智能-智能创意平台架构成长之路(三)--机器学习算法工程服务化

人工智能-智能创意平台架构成长之路(一)--长篇开篇

人工智能-智能创意平台架构成长之路(二)--大数据架构篇

人工智能-智能创意平台架构成长之路(三)--机器学习算法工程服务化

人工智能-智能创意平台架构成长之路(四)-丰富多彩的banner图生成解密第一部分(对标阿里鹿班的设计)

我们接着 人工智能-智能创意平台架构成长之路(二)--大数据架构篇 继续

前面我们讲了很多都是创意平台应用层的设计,但是其实在人工智能中,最重要的是算法,关于算法的框架很多,这就会导致底层算法的实现语言也会非常多,我们最常用的语言是python,其次是C或者C++,还有go语言实现的算法,下表我们列举了常用的机器学习框架以及他们支持的开发语言。

 

那么如何对这些语言实现的算法做工程化服务包装呢?总不能提供一堆的算法函数给平台应用层去使用吧,而应用层平台一般都是java语言来实现的,那么应用层平台如何来跨语言调用算法呢?而且一般的研发队伍中,都是java人员居多,那么java开发人员如何来把研究算法的博士们写的算法函数给包装成服务呢?

1、 python算法的服务化

针对这种情况,应该是比较简单的,因为基于python的web框架非常多,我们很容易的就可以把python的算法代码封装为一个服务,最常用的框架有flask和Django,这里我们以flask为例,看一个示例代码的实现。

# -*- coding: utf-8 -*-
from flask import Flask, request, Response
import json
app = Flask(__name__)
class Algorithm(object):
…
@app.route('/getSyncCrawlSjqqResult',methods = ['GET'])
def getAlgorithm Result():
  …
return Response(json.dumps(Algorithm.parser(request.args.get("para"))),mimetype="application/json")
if __name__ == '__main__':
    app.run(port=3001,host='0.0.0.0',threaded=True)

  

2、 C和C++的服务化

A、 使用java JNI 的接口方式调用C/C++,JNI是Java Native Interface的缩写,通过使用 Java本地接口书写程序,可以确保代码在不同的平台上方便移植。   从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。安卓(Android)调用C/C++很多也是采用的这种方式,而且这种方式使得c或者C++的算法可以跑在storm或者spark上,OpenCV的java版本的包也是通过这种方式来实现,OpenCV的java库是通过 java走jni的方式调用C++编写的OpenCV,

java调用OpenCV的操作的步骤如下:

1)、编写带有native声明的方法的java类

public class AlgorithmExample {
    public static native String callAlgorithm();//所有native关键词修饰的都是对本地的声明
    static {
        System.loadLibrary("Algorithm.so");//载入本地算法库
    }
    public static void main(String[] args) {
        System.out.println(AlgorithmExample. callAlgorithm())
    }
}

在这段代码中,最终的是在类初始化时,需要通过 System.loadLibrary("Algorithm.so")去加载算法的so库包,这里的算法so库包就是一个动态链接库。

2)、java的代码写完后,我们就需要把java代码编译生成class文件

javac AlgorithmExample.java

3)、生成扩展名为h的头文件,可以执行javah AlgorithmExample

jni HelloWorld 头文件的内容:

/*DO NOT EDIT THIS FILE - it is machine generated*/
#include <jni.h>
/*Header for class AlgorithmExample */
 
#ifndef _Included_ AlgorithmExample
#define _Included_ AlgorithmExample
#ifdef __cplusplus
extern "C" {
#endif
/*
 *Class: AlgorithmExample
 *Method: callAlgorithm
 *Signature:()V
 */
JNIEXPORT String JNICALL
Java_ AlgorithmExample_ callAlgorithm(JNIEnv*, jobject);
 
#ifdef __cplusplus
}
#endif
#endif

 

4)、 编写本地方法实现和由javah命令生成的头文件里面声明的方法名相同的方法

#include "jni.h"
#include " AlgorithmExample.h"
 
//#include otherheaders
 
JNIEXPORT String JNICALL
Java_ AlgorithmExample_ callAlgorithm(JNIEnv *env, jobject obj)
{
    printf("Helloworld!\n");
    return ‘Helloworld’;
}

5)、生成动态链接库

gcc -Wall -D_JNI_IMPLEMENTATION_ -Wl,--kill-at -Id:/java/include –Id:/java/include/win32 -shared -o (输出的dll文件名,如AlgorithmExample.dll) (输入的c/c++源文件,如abc.c)

6)、使用时,需要将生成的dll文件(windows环境下)或者so文件(linux文件下)放到项目的classpath目录下。

B、C/C++回调java,也就是C/C++代码也可以通过JNI的方式调用java代码中的方法,但是这种使用方式不是很常见,具体使用方式可以参考博客园中的这篇文档:https://www.cnblogs.com/jiangjh/p/10991365.html

不管是C/C++通过JNI的方式调用java 还是 java 通过JNI的方式调用C/C++,在实际情况中很容易出现一些问题,尤其是java通过JNI的方式调用C/C++。

l  内存泄露问题:

C/C++自身开辟的内存,JVM虚拟机的GC回收器无法帮助其自动回收,如果C/C++中没有及时的free 内存,那么会造成内存泄露,而且这种内存泄露通过jmap获取heap dump来查看内存使用快照时是看不到这块的内存使用的。

l  JNI自身存在的一些问题:

笔者就曾经遇到direct ByteBuffer内存无法回收,通过jni在虚拟机外内存中分配的direct ByteBuffer,在JVM的默认启动时是没有做大小限制的,direct ByteBuffer可以通过-XX:MaxDirectMemorySize来设置,此参数的含义是当Direct ByteBuffer分配的堆外内存到达指定大小后,即触发Full GC。注意该值是有上限的,默认是64M,最大为sun.misc.VM.maxDirectMemory(),在程序中中可以获得-XX:MaxDirectMemorySize的设置的值,而且这块的direct ByteBuffer通过jmap无法查看该快内存的使用情况。也只能通过top来看它的内存使用情况。关于这块可以参考笔者的另一篇博文:https://www.cnblogs.com/laoqing/p/10380536.html

我们再说一个安卓上人脸识别的算法例子,大部分的人脸识别的底层算法都是基于C或者C++来实现的,然后安卓上怎么用呢,我们都知道安卓提供了SDK,也提供了NDK。 NDK 可以用来编译C或者C++的代码,然后会生成so包,最后通过SDK,走java的JNI方式来调用so包。

 

 

C、通过python 调用C/C++,然后通过python来把算法封装成服务

(作者的原创文章,转载须注明出处。原创文章归作者所有,欢迎转载,但是保留版权。对于转载了博主的原创文章,不标注出处的,作者将依法追究版权,请尊重作者的成果。请注明出处:https://www.cnblogs.com/laoqing/p/11364435.html)

Python中提供了ctypes,使用ctypes 可以很方便的调用C语言代码,ctypes模块提供了和C语言兼容的数据类型和函数来加载dll或so文件。

如下C的代码中,提供了两个函数,一个是两个int类型的数相加,一个是两个float类型的数相加,然后我们用python的ctypes模块来调用这个代码

#include <stdio.h>int add_int(int, int);
float add_float(float, float);
 
int add_int(int numA, int numB)
{
    return numA + numB;
}
 
float add_float(float numA, float numB)
{
    return numA + numB;

}
…

使用gcc -shared -Wl,-soname,adder -o adderExample.so -fPIC addExample.c 来生成Linux下的so文件,将so包文件放到python 工程中。

然后我们就可以写一段python代码来调用了so包了

import ctypes

adderExample = ctypes.cdll.LoadLibrary('./adderExample.so ')
res_int = adderExample.add_int(10,5)
print("10 + 5 等于 " + str(res_int))
a = ctypes.c_float(7.2)
b = ctypes.c_float(5.3)
add_float = adderExample.add_float
add_float.restype = ctypes.c_float
print("7.2 + 5.3 等于 " + str(add_float(a, b)))

使用ctypes会有很大的局限性,对于其他类似布尔型和浮点型这样的类型,必须要使用正确的ctype类型才可以,但是调用C中的对象时,就很难做到。

由于python的解释器本身就是用C语言来实现,那么其实只要我们用C写的算法代码能够按照python解释器的规范集成进去就可以。

D、通过C/C++语言自己来包装服务

我们知道C和C++其实相对于Java来说,是更偏底层一点的语言,而且C是面向过程的语言,在很多公司的团队中,基本都很少有C或者C++的开发人员,而且用C语言实现一个http 服务比用java或者python实现一个http服务其实有时候需要写更多的代码。但是C/C++肯定是可以用自己的语言来包装实现服务。

3、Go的服务化

使用go语言来创建一个http服务,大致会有两个过程,首先需要使用go来注册一个路由,提供url模式和handler函数的映射。其次就是需要实例化一个server对象,并开启对客户端的请求监听。

package example
import (
        "io"
        "net/http"
        "log"
)
func main () {
       // 设置请求路由
       http.HandleFunc("/algorithm", algorithm)
       // 路由做注册,开启监听
       err := http.ListenAndServe(":3000", nil)
       if err != nil {
            log.Fatal(err)
       }    
}
func algorithm (response http.ResponseWriter, request *http.Request) {
        io.WriteString(response, "this is algorithm")
}

  

未完待续...

posted @ 2019-08-16 15:53  张永清  阅读(2768)  评论(0编辑  收藏  举报
作者的原创文章,转载须注明出处。原创文章归作者所有,欢迎转载,但是保留版权。对于转载了博主的原创文章,不标注出处的,作者将依法追究版权,请尊重作者的成果。