函数计算部署机器学习遇到的问题和解法
摘要: 随着 Serverless 的流行,将应用迁移到云上已经成了一种必然的趋势。我们今天来看一下如何将机器学习应用迁移到函数计算上。 1. 本地开发 首先我们看一下本地开发机器学习应用的步骤。我们大概可以将本地开发概括为三个步骤,分别是代码编写,安装依赖,运行调试。
随着 Serverless 的流行,将应用迁移到云上已经成了一种必然的趋势。
我们今天来看一下如何将机器学习应用迁移到函数计算上。
1. 本地开发
首先我们看一下本地开发机器学习应用的步骤。我们大概可以将本地开发概括为三个步骤,分别是代码编写,安装依赖,运行调试。我们分别来看一下。
1.1 代码编写
假定我们的项目结构为:
其中 index.py 存放了机器学习相关的代码,model_data 存放了数据模型,pic 中存放了要进行测试的图片。
index.py 的内容为(代码参考了这篇文章):
1.2 安装依赖
在运行应用前,需要先安装应用依赖的模块,这里主要依赖了 opencv 以及 tensorflow,安装方法很简单:
执行完这两条命令后,opencv-python 以及 tensorflow 就被安装到系统目录里。Linux 下默认为 /usr/local/lib/pythonX.Y/site-packages。
1.3 运行
运行时,python 会自动在配置的路径下查找相关模块并进行加载。
经过这三个步骤,我们就完成了本地机器学习应用的开发,我们接下来看下如何迁移应用到函数计算。
2. 迁移函数计算
2.1 本地开发与函数计算开发对比
首先,我们需要做一些准备工作。让我们来思考下函数计算应用开发方式与本地应用应用的开发方式有什么不同呢?
1. 代码入口。本地开发时,代码可以省略 main 函数,也可以提供 main 函数作为程序入口,但在函数计算中,函数入口是固定的,非 Http 触发器的函数入口必须是一个包含了两个参数的函数,比如:def handler(event, context)。
2. 模块依赖。本地开发时,项目依赖的模块通常会被安装到系统的某个目录。比如我们上面执行的 pip install tensorflow。而对于函数计算,由于为了能够最大限度的对应用进行优化,开放给用户的操作空间通常是比较小的。因此,对于函数计算,目前还无法做到安装项目依赖到运行环境。我们只能通过将自定义模块一同打包的方式。参考。
3. 运行。本地开发时,需要使用 python 命令或者 IDE 来运行代码。而在函数计算,我们需要首先部署应用到函数计算,再通过触发器或者控制台手动触发执行。
接下来我们针对这三点开发方式的不同对代码进行改造。
2.2 改造代码
2.2.1 代码入口改造
这个比较简单,只需要将
修改为
并删除下面代码:
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 包安装到指定目录。
对于系统依赖包含的链接库,可以通过 LD_LIBRARY_PATH 变量改变其加载行为。
对于资源依赖,因为控制权在我们的代码里,因此只需要改变代码的处理逻辑就可以了。
根据上面的描述,我们可以整理成下面的表格:
2.2.3 下载依赖的逻辑
对于我们的 demo 应用,存在两种依赖,一种是应用依赖,另一种是资源依赖。而需要我们特别处理的只有应用依赖。我们需要在项目目录下创建一个名为 applib 的目录,并下载应用依赖到该目录。这里需要注意的是如果引用的模块使用 C / C++ / go 编译出来的可执行文件或者库文件,那么推荐使用 fcli 的 sbox 进行下载,使用方法为:
执行完毕后,就会发现 applib 中就包含了项目所需要的应用依赖。
2.2.4 打包依赖上传到 OSS
机器学习的应用依赖、资源依赖通常比较大,会很容易超过函数计算对代码包的限制(50M)。为了避开这个问题,我们需要将这些依赖上传到 OSS
执行完毕后,项目会多出一个名为 applib.zip 的压缩包,上传到 oss 即可。
同样的,对资源依赖进行相同的操作:
2.2.5 初始化依赖
这里我们提供一个模板代码,负责在函数第一次启动时,从 OSS 下载资源到本地、解压,并配置好相应的环境变量。我们可以在项目中创建一个名为 loader.py 文件,内容为:
这段代码会首先读取 AppLibObject 环境变量,用于从 OSS 下载应用依赖,并解压到 AppLibDir 这个环境变量所代表的目录。
其次会读取 ModelObject 环境变量,用于从 OSS 下载资源依赖,并解压到 ModelDir 这个环境变量所代表的目录。
最后,当依赖准备妥当后,会调用 index.py 中的 handler 函数。
理论上,这个代码可以用于其它任何需要下载应用依赖、资源依赖的场景。而我们的 index.py 需要修改的,只有将原先的获取模型依赖的固定的路径,修改为利用 ModelDir 获取路径即可。
2.3 本地运行调试
代码编写完成后,我们的目录结构调整为:
我们本地运行看下效果,这里我们借助于函数计算推出的 fc-dcoker 工具。
为了避免本地每次调试做无谓的下载,我们取下巧,将应用依赖、资源依赖挂载到 fc-docker 中,并开启 local 的标识:
得到结果:
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 即可:
至此,我们的项目中又多了一个 template.yml,结构为
通过这一个 template.yml,执行 fun deploy 后即可创建好相应的服务、函数,并配置好函数的环境变量。
即使修改了代码,只要重复执行 fun deploy 即可。
接下来,打开 https://fc.console.aliyun.com/ 控制台,依次找到创建好的服务、函数,点击执行,即可得到与本地一致的输出:
2.5 补充
在上面的例子中,我们只列出了应用依赖、资源依赖的情况。对于系统依赖的处理逻辑是比较简单的,比如我们拿 zbar 举例。除了需要在 applib 中通过 pip 安装 zbar,还要在 code 目录下新建一个 lib 目录,并通过 sandbox 在这个目录中执行:
执行完成后,目录结构变化为:
就像上面提到的,我们需要修改 LD_LIBRARY_PATH 来改变系统依赖的加载行为,因此我们需要在 template.yaml 中的 EnvironmentVariables 下添加一行:
至此,就可以直接在 index.py 等文件中直接使用 zbar 了。
3. 总结
结果一番努力,我们终于将机器学习应用上线到函数计算了。回顾上面的所有操作可以发现,其实大部分的改造工作都是可以通过工具解决的。无论是 template.yml,还是 loader.py 都是直接拿来就能用的。而真正需要开发者操作的也就只有下载依赖、修改对资源依赖的引用路径了。
本文作者:tanhe123
本文为云栖社区原创内容,未经允许不得转载。