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


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

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

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

    人工智能-智能创意平台架构成长之路(四)-丰富多彩的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!
    ");
        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")
    }
    

      

    未完待续...

  • 相关阅读:
    [LeetCode] 146. LRU Cache(LRU 缓存)
    [LeetCode] 55. Jump Game(跳跃游戏)
    [LeetCode] 33. Search in Rotated Sorted Array(搜索旋转有序数组)
    [LeetCode] 19. Remove Nth Node From End of List(从单链表中移除倒数第 n 个节点)
    [LeetCode] 79. Word Search(单词查找)
    [LeetCode] 322. Coin Change(换硬币)
    [LeetCode] 34. Find First and Last Position of Elements in Sorted Array(在有序数组中寻找某个元素第一次和最后一次出现的位置)
    第04组 Alpha冲刺(2/6)
    第04组 Alpha冲刺(1/6)
    第04组 团队Git现场编程实战
  • 原文地址:https://www.cnblogs.com/laoqing/p/11364435.html
Copyright © 2020-2023  润新知