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

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

前面我们讲了很多都是创意平台应用层的设计,但是其实在人工智能中,最重要的是算法,关于算法的框架很多,这就会导致底层算法的实现语言也会非常多,我们最常用的语言是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++很多也是采用的这种方式,操作的步骤如下:

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、通过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解释器的规范集成进去就可以。

未完待续...

猜你喜欢

转载自www.cnblogs.com/laoqing/p/11364435.html
今日推荐