人工智能-智能創意平台架構成長之路(三)--機器學習算法工程服務化


人工智能-智能創意平台架構成長之路(一)--長篇開篇

人工智能-智能創意平台架構成長之路(二)--大數據架構篇

人工智能-智能創意平台架構成長之路(三)--機器學習算法工程服務化

人工智能-智能創意平台架構成長之路(四)-豐富多彩的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")
}

  

未完待續...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM