函数计算部署机器学习遇到的问题和解法

本文涉及的产品
函数计算FC,每月15万CU 3个月
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
简介: 随着 Serverless 的流行,将应用迁移到云上已经成了一种必然的趋势。我们今天来看一下如何将机器学习应用迁移到函数计算上。 1. 本地开发 首先我们看一下本地开发机器学习应用的步骤。我们大概可以将本地开发概括为三个步骤,分别是代码编写,安装依赖,运行调试。

随着 Serverless 的流行,将应用迁移到云上已经成了一种必然的趋势。
我们今天来看一下如何将机器学习应用迁移到[函数计算]上。

1. 本地开发

首先我们看一下本地开发机器学习应用的步骤。我们大概可以将本地开发概括为三个步骤,分别是代码编写,安装依赖,运行调试。我们分别来看一下。

1.1 代码编写

假定我们的项目结构为:

project root
├── index.py
├── model_data
│   ├── checkpoint
│   ├── model.data-00000-of-00001
│   ├── model.index
│   └── model.met
└── pic
    └── e2.jpg

其中 index.py 存放了机器学习相关的代码,model_data 存放了数据模型,pic 中存放了要进行测试的图片。

index.py 的内容为(代码参考了这篇文章):

# -*- coding:utf-8 -*-   
import os
import sys
import cv2  
import numpy as np
import tensorflow as tf  

saver = None

def reversePic(src):
    for i in range(src.shape[0]):
        for j in range(src.shape[1]):
            src[i, j] = 255 - src[i, j]
    return src 

def main():
    sess = tf.Session()  

    saver = tf.train.import_meta_graph('model_data/model.meta')

    saver.restore(sess, 'model_data/model')
    graph = tf.get_default_graph()

    input_x = sess.graph.get_tensor_by_name("Mul:0")
    y_conv2 = sess.graph.get_tensor_by_name("final_result:0")

    path="pic/e2.jpg"  
    im = cv2.imread(path, cv2.IMREAD_GRAYSCALE)

    im = reversePic(im)

    im = cv2.resize(im, (28, 28), interpolation=cv2.INTER_CUBIC)  

    x_img = np.reshape(im , [-1 , 784])  
    output = sess.run(y_conv2 , feed_dict={
   input_x:x_img})  
    print 'the predict is %d' % (np.argmax(output)) 

    sess.close()

if __name__ == '__main__':  
    main()

1.2 安装依赖

在运行应用前,需要先安装应用依赖的模块,这里主要依赖了 opencv 以及 tensorflow,安装方法很简单:

pip install opencv-python
pip install tensorflow

执行完这两条命令后,opencv-python 以及 tensorflow 就被安装到系统目录里。Linux 下默认为 /usr/local/lib/pythonX.Y/site-packages。

1.3 运行

运行时,python 会自动在配置的路径下查找相关模块并进行加载。

$ python index.py
the predict is 8

经过这三个步骤,我们就完成了本地机器学习应用的开发,我们接下来看下如何迁移应用到函数计算。

2. 迁移函数计算

2.1 本地开发与函数计算开发对比

首先,我们需要做一些准备工作。让我们来思考下函数计算应用开发方式与本地应用应用的开发方式有什么不同呢?

  1. 代码入口。本地开发时,代码可以省略 main 函数,也可以提供 main 函数作为程序入口,但在函数计算中,函数入口是固定的,非 Http 触发器的函数入口必须是一个包含了两个参数的函数,比如:def handler(event, context)。

  2. 模块依赖。本地开发时,项目依赖的模块通常会被安装到系统的某个目录。比如我们上面执行的 pip install tensorflow。而对于函数计算,由于为了能够最大限度的对应用进行优化,开放给用户的操作空间通常是比较小的。因此,对于函数计算,目前还无法做到安装项目依赖到运行环境。我们只能通过将自定义模块一同打包的方式。参考

  3. 运行。本地开发时,需要使用 python 命令或者 IDE 来运行代码。而在函数计算,我们需要首先部署应用到函数计算,再通过触发器或者控制台手动触发执行。

接下来我们针对这三点开发方式的不同对代码进行改造。

2.2 改造代码

2.2.1 代码入口改造

这个比较简单,只需要将

def main():

修改为

def handler(event, context):

并删除下面代码:

if __name__ == '__main__':  
    main()

2.2.2. 模块依赖

这一块稍微复杂些。不同的语言因为模块加载机制的不同,这里的处理逻辑也会有差异。比如对于 java,无论是使用 maven,还是 gradle,都可以很容易的一键将代码以及依赖打包成 jar。但遗憾的是 python 目前没有这种机制。

我们先根据场景对 python 依赖的模块做个简单的分类。

应用依赖: 对于本例中使用 pip 安装的模块,比如 pip install tensorflow,我们暂且将其称为应用依赖。
系统依赖: 在某些场景下,python 安装的库仅仅是对底层 c、c++ 库调用的封装,例如使用 zbar 时,除了使用 pip install zbar,还要在系统中安装相应的库:apt-get install -y libzbar-dev。我们暂且把像 libzbar-dev 一样需要使用系统软件包管理器安装的库称为系统依赖。
资源依赖: 对于一些应用,比如机器学习,启动后还需要加载数据模型,数据模型需要在程序启动时准备好,我们暂且将这种依赖称为资源依赖。资源依赖比较特殊,它是我们的应用逻辑所需要的,通常体积比较大。

对于应用依赖,我们可以通过 pip 的 -t 参数改变其安装位置,比如 pip install -t $(pwd) tensorflow。并且可以通过 sys.path 改变加载行为,使得可以从指定目录加载模块。

对于系统依赖,我们可以通过 apt-get 下载 deb 包,再利用 deb 包安装到指定目录。

apt-get install -y -d -o=dir::cache=$(pwd) libzbar-dev
for f in $(ls $(pwd)/archives/*deb); do dpkg -x $f $(pwd); done
rm -r archives

对于系统依赖包含的链接库,可以通过 LD_LIBRARY_PATH 变量改变其加载行为。

对于资源依赖,因为控制权在我们的代码里,因此只需要改变代码的处理逻辑就可以了。

根据上面的描述,我们可以整理成下面的表格:

类别 定义 安装方法举例 指定位置安装方法举例 影响加载的因素
应用依赖 pip 安装的模块 pip install tensorflow pip install -t $(pwd) tensorflow sys.path
系统依赖 系统软件包管理器安装的依赖 apt-get install -y libzbar-dev apt-get install -y -d -o=dir::cache=$(pwd) libzbar-dev

for f in $(ls $(pwd)/archives/*deb); do dpkg -x $f $(pwd); done
rm -r archives
LD_LIBRARY_PATH
资源依赖 代码依赖的资源,比如数据模型 \ \ 由应用代码控制

2.2.3 下载依赖的逻辑

对于我们的 demo 应用,存在两种依赖,一种是应用依赖,另一种是资源依赖。而需要我们特别处理的只有应用依赖。我们需要在项目目录下创建一个名为 applib 的目录,并下载应用依赖到该目录。这里需要注意的是如果引用的模块使用 C / C++ / go 编译出来的可执行文件或者库文件,那么推荐使用 fcli 的 sbox 进行下载,使用方法为:

mkdir applib 
fcli shell
sbox -d applib -t python2.7
pip install -t $(pwd) tensorflow
pip install -t $(pwd) opencv-python

执行完毕后,就会发现 applib 中就包含了项目所需要的应用依赖。

2.2.4 打包依赖上传到 OSS

机器学习的应用依赖、资源依赖通常比较大,会很容易超过函数计算对代码包的限制(50M)。为了避开这个问题,我们需要将这些依赖上传到 OSS:

cd applib && zip -r applib.zip * && mv applib.zip ../ ; cd ..

执行完毕后,项目会多出一个名为 applib.zip 的压缩包,上传到 oss 即可。

同样的,对资源依赖进行相同的操作:

cd model_data && zip -r model_data.zip * && mv model_data.zip ../ ; cd ..

2.2.5 初始化依赖

这里我们提供一个模板代码,负责在函数第一次启动时,从 OSS 下载资源到本地、解压,并配置好相应的环境变量。我们可以在项目中创建一个名为 loader.py 文件,内容为:

# -*- coding:utf-8 -*-   
import sys
import zipfile
import os
import oss2
import imp
import time

app_lib_object = os.environ['AppLibObject']
app_lib_dir = os.environ['AppLibDir']

model_object = os.environ['ModelObject']
model_dir = os.environ['ModelDir']

local = bool(os.getenv('local', ""))

print 'local running: ' + str(local)

initialized = False

def download_and_unzip_if_not_exist(objectKey, path, context):

    creds = context.credentials

    if (local):
        print 'thank you for running function in local!!!!!!'
        auth = oss2.Auth(creds.access_key_id,
                         creds.access_key_secret)
    else:
        auth = oss2.StsAuth(creds.access_key_id,
                            creds.access_key_secret,
                            creds.security_token)

    endpoint = os.environ['Endpoint']
    bucket = os.environ['Bucket']

    print 'objectKey: ' + objectKey
    print 'path: ' + path
    print 'endpoint: ' + endpoint
    print 'bucket: ' + bucket

    bucket = oss2.Bucket(auth, endpoint, bucket) 

    zipName = '/tmp/tmp.zip'

    print 'before downloading ' + objectKey + ' ...'
    start_download_time = time.time()
    bucket.get_object_to_file(objectKey, zipName)
    print 'after downloading, used %s seconds...' % (time.time() - start_download_time)

    if not os.path.exists(path):
        os.mkdir(path)

    print 'before unzipping ' + objectKey + ' ...'
    start_unzip_time = time.time()
    with zipfile.ZipFile(zipName, "r") as z:
        z.extractall(path)
    print 'unzipping done, used %s seconds...' % (time.time() - start_unzip_time)

def handler(event, context):
    global initialized
    if not initialized:
        if ( not local ):
            download_and_unzip_if_not_exist(app_lib_object, app_lib_dir, context) 
            download_and_unzip_if_not_exist(model_object, model_dir, context)
        sys.path.insert(1, app_lib_dir)
        print sys.path
        initialized = True

    file_handle, desc = None, None

    fn, modulePath, desc = imp.find_module('index')
    mod = imp.load_module('index', fn, modulePath, desc)

    request_handler = getattr(mod, 'handler')
    return request_handler(event, context)

这段代码会首先读取 AppLibObject 环境变量,用于从 OSS 下载应用依赖,并解压到 AppLibDir 这个环境变量所代表的目录。

其次会读取 ModelObject 环境变量,用于从 OSS 下载资源依赖,并解压到 ModelDir 这个环境变量所代表的目录。

最后,当依赖准备妥当后,会调用 index.py 中的 handler 函数。

理论上,这个代码可以用于其它任何需要下载应用依赖、资源依赖的场景。而我们的 index.py 需要修改的,只有将原先的获取模型依赖的固定的路径,修改为利用 ModelDir 获取路径即可。

2.3 本地运行调试

代码编写完成后,我们的目录结构调整为:

project root
├── code
│   ├── index.py
│   ├── loader.py
│   └── pic
│       └── e2.jpg
├── applib
│.  └── *
└── model_data
    ├── checkpoint
    ├── model.data-00000-of-00001
    ├── model.index
    └── model.met

我们本地运行看下效果,这里我们借助于函数计算推出的 fc-dcoker 工具。

为了避免本地每次调试做无谓的下载,我们取下巧,将应用依赖、资源依赖挂载到 fc-docker 中,并开启 local 的标识:

docker run --rm \
    -e local=true \
    -e AppLibObject=applib.zip \
    -e AppLibDir=/tmp/applib \
    -e ModelObject=model_data.zip \
    -e ModelDir=/tmp/model \
    -v $(pwd)/code:/code \
    -v $(pwd)/applib:/tmp/applib \
    -v $(pwd)/model_data:/tmp/model \
    aliyunfc/runtime-python2.7 \
   --handler loader.handler

得到结果:

2018-08-28 17:44:16.043564: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supportsinstructions that this TensorFlow binary was not compiled to use: AVX2 FMA
FunctionCompute python runtime inited.
FC Invoke Start RequestId: f3ea930e-d7e2-4173-9726-453cdd89f18c
local running: True
['/code', '/tmp/applib', '/var/fc/runtime/python2.7/src', '/usr/local/lib/python27.zip', '/usr/local/lib/python2.7', '/usr/local/lib/python2.7/plat-linux2', '/usr/local/lib/python2.7/lib-tk', '/usr/local/lib/python2.7/lib-old', '/usr/local/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/site-packages']
2018-08-28T17:44:16.154Z f3ea930e-d7e2-4173-9726-453cdd89f18c [INFO] Restoring parameters from /tmp/model/model
the predict is 8

RequestId: f3ea930e-d7e2-4173-9726-453cdd89f18c          Billed Duration: 9954 ms        Memory Size: 1998 MB       Max Memory Used: 239 MB

2.4 部署

本地开发完成,接下来,我们就需要部署应用到线上了。这里我们借助函数计算推出的 Fun 工具。

Fun 工具使用步骤如下:

  1. release 页面对应平台的 binary 版本,解压就可以使用。或者使用 npm install @alicloud/fun -g 也可以直接使用。
  2. 使用 fun config 配置 ak、region 等信息。
  3. 编写 template.yml
  4. fun deploy 部署

是的,不需要登录控制台进行繁琐的配置,仅仅在项目下提供一个 template.yml 即可:

ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
  tensorflow: # 服务名
    Type: 'Aliyun::Serverless::Service'
    Properties:
      Description: 'tensorflow demo'
      Policies: 
        - AliyunOSSReadOnlyAccess
    test: # 函数名
      Type: 'Aliyun::Serverless::Function'
      Properties:
        Handler: utils.handler
        CodeUri: ./code/
        Description: 'tensorflow application!'
        Runtime: python2.7
        MemorySize: 1024
        Timeout: 300
        EnvironmentVariables:
          Bucket: just-fc-test # 替换为自己的 oss bucket
          Endpoint: 'https://oss-cn-shanghai-internal.aliyuncs.com' # 替换掉 OSS Endpoint
          AppLibObject: applib.zip
          AppLibDir: /tmp/applib
          ModelObject: model_data.zip
          ModelDir: /tmp/model

至此,我们的项目中又多了一个 template.yml,结构为

project root
├── code
│   ├── index.py
│   ├── loader.py
│   └── pic
│       └── e2.jpg
├── applib
│   └── *
├── template.yml
└── model_data
    ├── checkpoint
    ├── model.data-00000-of-00001
    ├── model.index
    └── model.met

通过这一个 template.yml,执行 fun deploy 后即可创建好相应的服务、函数,并配置好函数的环境变量。

$ fun deploy
Waiting for service tensorflow to be deployed...
        Waiting for function test to be deployed...
                Waiting for packaging function test code...
                package function test code done
        function test deploy success
service tensorflow deploy success

即使修改了代码,只要重复执行 fun deploy 即可。

接下来,打开 https://fc.console.aliyun.com/ 控制台,依次找到创建好的服务、函数,点击执行,即可得到与本地一致的输出:

image

2.5 补充

在上面的例子中,我们只列出了应用依赖、资源依赖的情况。对于系统依赖的处理逻辑是比较简单的,比如我们拿 zbar 举例。除了需要在 applib 中通过 pip 安装 zbar,还要在 code 目录下新建一个 lib 目录,并通过 sandbox 在这个目录中执行:

apt-get install -y -d -o=dir::cache=$(pwd) libzbar-dev
for f in $(ls $(pwd)/archives/*deb); do dpkg -x $f $(pwd); done
rm -r archives

执行完成后,目录结构变化为:

project root
├── code
│   ├── lib
│   │   └── usr/lib
│   │       └── *

就像上面提到的,我们需要修改 LD_LIBRARY_PATH 来改变系统依赖的加载行为,因此我们需要在 template.yaml 中的 EnvironmentVariables 下添加一行:

LD_LIBRARY_PATH: /code/lib/usr/lib:/code:/code/lib:/usr/local/lib

至此,就可以直接在 index.py 等文件中直接使用 zbar 了。

3. 总结

结果一番努力,我们终于将机器学习应用上线到函数计算了。回顾上面的所有操作可以发现,其实大部分的改造工作都是可以通过工具解决的。无论是 template.yml,还是 loader.py 都是直接拿来就能用的。而真正需要开发者操作的也就只有下载依赖、修改对资源依赖的引用路径了。

如果您在迁移过程中遇到了困难,请加入我们的钉钉群,同时,我们还会为您提供针对平台优化的 tensorflow 给您:

image

4. 代码链接

https://github.com/awesome-fc/tensorflow-fc-demo

5. 参考

函数计算使用自定义的模块
函数计算安装依赖库方法小结
Tensorflow MINIST数据模型的训练,保存,恢复和手写字体识别
fc-docker
Fun

相关实践学习
【文生图】一键部署Stable Diffusion基于函数计算
本实验教你如何在函数计算FC上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。函数计算提供一定的免费额度供用户使用。本实验答疑钉钉群:29290019867
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
目录
相关文章
|
1月前
|
人工智能 Java Serverless
阿里云函数计算助力AI大模型快速部署
随着人工智能技术的快速发展,AI大模型已经成为企业数字化转型的重要工具。然而,对于许多业务人员、开发者以及企业来说,探索和利用AI大模型仍然面临诸多挑战。业务人员可能缺乏编程技能,难以快速上手AI模型;开发者可能受限于GPU资源,无法高效构建和部署AI应用;企业则希望简化技术门槛,以更低的成本和更高的效率利用AI大模型。
155 12
|
6天前
|
机器学习/深度学习 安全 PyTorch
FastAPI + ONNX 部署机器学习模型最佳实践
本文介绍了如何结合FastAPI和ONNX实现机器学习模型的高效部署。面对模型兼容性、性能瓶颈、服务稳定性和安全性等挑战,FastAPI与ONNX提供了高性能、易于开发维护、跨框架支持和活跃社区的优势。通过将模型转换为ONNX格式、构建FastAPI应用、进行性能优化及考虑安全性,可以简化部署流程,提升推理性能,确保服务的可靠性与安全性。最后,以手写数字识别模型为例,展示了完整的部署过程,帮助读者更好地理解和应用这些技术。
43 18
|
10天前
|
机器学习/深度学习 人工智能 自然语言处理
云上一键部署 DeepSeek-V3 模型,阿里云 PAI-Model Gallery 最佳实践
本文介绍了如何在阿里云 PAI 平台上一键部署 DeepSeek-V3 模型,通过这一过程,用户能够轻松地利用 DeepSeek-V3 模型进行实时交互和 API 推理,从而加速 AI 应用的开发和部署。
|
5天前
|
人工智能 JSON 自然语言处理
一键生成毛茸萌宠形象,基于函数计算极速部署 ComfyUI 生图系统
本次方案将帮助大家实现使用阿里云产品函数计算FC,只需简单操作,就可以快速配置ComfyUI大模型,创建出你的专属毛茸茸萌宠形象。内置基础大模型+常用插件+部分 Lora,以风格化图像生成只需用户让体验键配置简单方便,后续您可以根据自己的需要更换需要的模型、Lora、增加插件。
|
25天前
|
JSON 人工智能 Serverless
一键生成毛茸萌宠形象,基于函数计算极速部署ComfyUI生图系统
通过阿里云函数计算FC 和文件存储NAS,用户体验 ComfyUI 和预置工作流文件,用户可以快速生成毛茸茸萌宠等高质量图像。
一键生成毛茸萌宠形象,基于函数计算极速部署ComfyUI生图系统
|
20天前
|
人工智能 JSON 算法
魔搭支持在阿里云人工智能平台PAI上进行模型训练、部署了!
现在,魔搭上的众多模型支持在阿里云人工智能平台PAI-Model Gallery上使用阿里云算力资源进行模型训练和部署啦!
|
2月前
|
人工智能 JSON 算法
Qwen2.5-Coder 系列模型在 PAI-QuickStart 的训练、评测、压缩及部署实践
阿里云的人工智能平台 PAI,作为一站式、 AI Native 的大模型与 AIGC 工程平台,为开发者和企业客户提供了 Qwen2.5-Coder 系列模型的全链路最佳实践。本文以Qwen2.5-Coder-32B为例,详细介绍在 PAI-QuickStart 完成 Qwen2.5-Coder 的训练、评测和快速部署。
Qwen2.5-Coder 系列模型在 PAI-QuickStart 的训练、评测、压缩及部署实践
|
14天前
|
存储 人工智能 安全
函数计算助您 7 分钟极速部署开源对话大模型
本方案利用函数计算的无服务器架构,您可以在函数计算控制台选择魔搭(ModelScope)开源大模型应用模板;同时,我们将利用文件存储 NAS ,为应用服务所需的大模型和相关文件提供一个安全的存储环境;最终通过访问提供的域名进行模型的调用与验证。仅需三步,即可玩转目前热门 AI 大模型。
|
2月前
|
算法 Serverless
通过函数计算部署ComfyUI以实现一个AIGC图像生成系统
快来报名参与ComfyUI活动,体验一键部署模型,生成粘土风格作品赢取体脂秤。活动时间从即日起至2024年12月13日24:00:00,每个工作日限量50个,先到先得!访问活动页面了解更多详情。
175 54
|
14天前
|
存储 人工智能 Serverless
7分钟玩转 AI 应用,函数计算一键部署 AI 生图大模型
人工智能生成图像(AI 生图)的领域中,Stable Diffusion WebUI 以其强大的算法和稳定的输出质量而闻名。它能够快速地从文本描述中生成高质量的图像,为用户提供了一个直观且高效的创作平台。而 ComfyUI 则以其用户友好的界面和高度定制化的选项所受到欢迎。ComfyUI 的灵活性和直观性使得即使是没有技术背景的用户也能轻松上手。本次技术解决方案通过函数计算一键部署热门 AI 生图大模型,凭借其按量付费、卓越弹性、快速交付能力的特点,完美实现低成本,免运维。

相关产品

  • 函数计算