机器学习的构建和部署通常需要非常多的工作与努力,这对于软件开发者和入门者造成了很多困难。本文介绍了如何使用软件库 Lore 快速而高效地构建机器学习模型,并从数据预处理到模型部署等七个步骤介绍构建的经验。
一般问题
Python 或 SQL 等高级语言编写代码时,模型性能很容易出现瓶颈。
代码复杂性在增长,因为有价值的模型需要通过许多次迭代才能得到。当代码以非结构化的方式演化时,难以保证与传达最初的想法。
对数据和函数库的依赖不断变化,从而导致性能再现受到影响。
当人们试图理解最新的论文、软件包、特征和问题时,信息过载使得人们很容易错过唾手可得的成果,这一问题对于刚入行的人来说更为严重。
为了解决这些问题,我们标准化了 Lore 中的机器学习方法,并使用 Lore 开发新的机器学习模型。此外,我们 Instacart 也在产品中运行着十几个 Lore 模型。
如果您想在没有上下文的情况下看一下快速的演示,可以从 GitHub 上复制 my_app。如果您想看到完整工程介绍,请跳至大纲。
$ pip3 install lore
$ git clone https://github.com/montanalow/my_app.git
$ cd my_app
$ lore install # caching all dependencies locally takes a few minutes the first time
$ lore server &
$ curl "http://localhost:5000/product_popularity.Keras/predict.json?product_name=Banana&department=produce"
特征说明
了解该项目最好的方法是在十五分钟内开始我们自己的深度学习项目。如果你想在开始新项目前了解本文所述模型的特性,请参阅以下简要概述:
模型支持使用估计器搜索超参数,它们将采用几个不同的策略有效地利用多个 GPU(如果条件允许的话),因此可以分布式地搜索超参数分布。
支持使用多个软件库的估计器,包括 Keras、XGBoost 和 SciKit Learn 等。这些包都可以通过构建、拟合或预测进行分类,完整地覆盖了用户的算法和架构类型。
数据处理流程避免了信息在训练集和测试集间泄露的风险,且一条流程允许许多不同的估计器进行试验。如果您在实验过程中超出了可用 RAM,那么您可以使用基于磁盘处理流程。
转换标准化的高级特征工程。例如,使用美国人口普查数据可以将美国人的姓转换为年龄或性别的统计学特征;或是从任意格式的电话号字符串中提取地域编码。此外,pandas 包可以支持一般的数据、时间和字符串的转化操作。
编码器则为您的评估器提供鲁棒性的输入,并能避免常见的缺失和长尾问题。
对流行的(非)关系型数据库而言,IO 连接在应用程序中以一种标准的方式进行配置和汇集。这种方式对批量数据进行任务管理和读写优化,而非传统的 ORM 单行操作。IO 连接除了用加密的 S3 buckets 分配模型和数据集外,还共享了可配置的查询缓存。
对每个独立开发中的 APP 而言,依赖项管理都可以将对应包完整地复制到产品中去。因此我们无需了解 venv、pyenv、pyvenv、virtualenv、virtualenvwrapper、pipenv 和 conda 依赖项管理工具。
这样的工作流程可以让您选择是使用命令行、python 控制台、jupyter notebook 或者是其它 IDE。每个环境都可以在产品开发和配置过程中生成可读的日志记录。
15 分钟开启机器学习
您只需基础的 python 知识即可开始。如果您的模型没有开始进行学习,那您可以用省出来的时间继续探索机器学习的复杂性。
1. 创建一个新的 app(3 分钟)
2. 设计一个模型(1 分钟)
3. 生成架构(2 分钟)
4. 铺设流程(5 分钟)
5. 测试代码(1 分钟)
6. 训练模型(1 分钟)
7. 部署产品(2 分钟)
上述时间有些言过其实,只用作博客宣传。没有一个机器学习研究人员可以只用一分钟就设计出一个模型,但是一旦你开始跟着学,并且将过程中得到的一切都做上笔记,那么你也可以在 15 分钟内高效地构建一个自定义的 AI 项目,在你的朋友和同事中一鸣惊人。
1. 创建一个新的 APP
Lore 独立管理每个项目,这是为了避免与系统中的 python 或其他项目发生冲突。我们可以将 Lore 作为一个标准的 pip 包安装:
# On Linux
$ pip install lore
# On OS X use homebrew python 2 or 3
$ brew install python3 && pip3 install lore
当你无法复制他人环境时,是很难复现他人的工作。Lore 保护系统中 python 项目的方式可以避免依赖项错误和项目冲突。每一个 Lore 的应用程序都有自己的字典和安装目录,它特定需要的依赖库会锁定在文件 runtime.txt 和 requirements.txt 中。这使 Lore 的应用程序共享起来更加高效,也让我们离复现这个机器学习项目更近一步。
在安装 Lore 之后,我们可以在阅读本文后创建一个新的深度学习项目的 app。Lore 默认是模块化的,所以我们需要指定——keras 来为这个项目安装深度学习的依赖项。
$ lore init my_app --python-version=3.6.4 --keras
2. 设计一个模型
为了演示,我们将建立一个预测模型,这个模型将基于产品名字及其所在部门,预测出 Instacart 网站的产品能有多流行。世界各地的制造商零售时会在不同的人群间测试产品名字,看什么样的名字能得到最多的关注,我们这个简单的 AI 项目也可以提供相同的服务。
机器学习中最难的一部分是获得良好的数据。幸运的是,Instacart 以匿名的方式公布了 300 万份杂货订单。基于此,我们可以将问题调整为建立一个有监督的回归模型,该模型可以基于两个特点预测年均销量:产品名称和产品类别。实际上,该模型的表现并不好,因此后文会继续讨论更加强大的模型。
3. 生成架构
$ cd my_app
$ lore generate scaffold product_popularity --keras --regression --holdout
每一个 Lore 模型都包含一条用于加载数据和编码数据的流程,还包含一个可以实现特定机器学习算法的估计器。模型最有趣的部分在于类别生成中的实现细节。
流程从左侧的原始数据开始,将原始数据编码为右侧所需格式。估计器可以用编码数据训练模型,并根据验证集的性能确定是否终止训练,最后再用测试集评估。所有内容都可以被序列化存在模型存储区,然后用一个单线程再次加载进行部署。
4. 铺设流程
得到很适合机器学习算法的原始数据是很难的。我们通常会从一个数据集中加载数据或是下载 CSV 文件,将其编码为适合算法的格式,然后再将其分割为训练集和测试集。lore.pipelines 将这一预处理逻辑封装起来,成为标准的工作流程。
lore.pipelines.holdout.Base 可以将数据分为训练集、验证集和测试集,并可以编码数据,使数据适用于我们的机器学习算法。我们的子类将定义三种方法:get_data、get_encoders 和 get_output_encoder。
Instacart 公布的数据分为了多个 csv 文件,如下表所示。
get_data 可以下载 Instacart 的原始数据,使用 pandas 可以将带有所需特征(product_name,department)的数据加入 DataFrame,还可将预测量(sales)合并在一起。如下所示:
下列代码展示了如何实现 get_data :
# my_app/pipelines/product_popularity.py part 1
import os
from lore.encoders import Token, Unique, Norm
import lore.io
import lore.pipelines
import lore.env
import pandas
class Holdout(lore.pipelines.holdout.Base):
# You can inspect the source data csv's yourself from the command line with:
# $ wget https://s3.amazonaws.com/instacart-datasets/instacart_online_grocery_shopping_2017_05_01.tar.gz
# $ tar -xzvf instacart_online_grocery_shopping_2017_05_01.tar.gz
def get_data(self):
url = 'https://s3.amazonaws.com/instacart-datasets/instacart_online_grocery_shopping_2017_05_01.tar.gz'
# Lore will extract and cache files in lore.env.data_dir by default
lore.io.download(url, cache=True, extract=True)
# Defined to DRY up paths to 3rd party file hierarchy
def read_csv(name):
path = os.path.join(
lore.env.data_dir,
'instacart_2017_05_01',
name + '.csv')
return pandas.read_csv(path, encoding='utf8')
# Published order data was split into irrelevant prior/train
# sets, so we will combine them to re-purpose all the data.
orders = read_csv('order_products__prior')
orders = orders.append(read_csv('order_products__train'))
# count how many times each product_id was ordered
data = orders.groupby('product_id').size().to_frame('sales')
# add product names and department ids to ordered product ids
products = read_csv('products').set_index('product_id')
data = data.join(products)
# add department names to the department ids
departments = read_csv('departments').set_index('department_id')
data = data.set_index('department_id').join(departments)
# Only return the columns we need for training
data = data.reset_index()
return data[['product_name', 'department', 'sales']]
接下来,我们需要为每一列指定一个编码器。计算机科学家可能认为编码器是使机器学习更高效的方法。一些产品的名字太长,所以我们将其名称限定为前 15 个单词。
# my_app/pipelines/product_popularity.py part 2
def get_encoders(self):
return (
# An encoder to tokenize product names into max 15 tokens that
# occur in the corpus at least 10 times. We also want the
# estimator to spend 5x as many resources on name vs department
# since there are so many more words in english than there are
# grocery store departments.
Token('product_name', sequence_length=15, minimum_occurrences=10, embed_scale=5),
# An encoder to translate department names into unique
# identifiers that occur at least 50 times
Unique('department', minimum_occurrences=50)
)
def get_output_encoder(self):
# Sales is floating point which we could Pass encode directly to the
# estimator, but Norm will bring it to small values around 0,
# which are more amenable to deep learning.
return Norm('sales')
这就是预处理流程。我们起初用的评估器是 lore.estimators.keras.Regression 的一个简单子类,可以用默认值实现简单的深度学习架构。
# my_app/estimators/product_popularity.py
import lore.estimators.keras
class Keras(lore.estimators.keras.Regression):
pass
最后,我们的模型可以通过将其委托给估计器来改变深度学习架构的高级属性,还可从我们构建的处理流程中提取数据。
# my_app/models/product_popularity.py
import lore.models.keras
import my_app.pipelines.product_popularity
import my_app.estimators.product_popularity
class Keras(lore.models.keras.Base):
def __init__(self, pipeline=None, estimator=None):
super(Keras, self).__init__(
my_app.pipelines.product_popularity.Holdout(),
my_app.estimators.product_popularity.Keras(
hidden_layers=2,
embed_size=4,
hidden_width=256,
batch_size=1024,
sequence_embedding='lstm',
)
)
5. 测试代码
当搭建架构时模型会自动运行通烟测试(smoke test),第一次运行时会花一些时间下载一个 200 MB 的数据集进行测试。您还可以对缓存在 ./tests/data 中的文件进行修改,将其移入回收站以加快测试速度。
$ lore test tests.unit.test_product_popularity
6. 训练模型
训练模型需要 ./data 中的缓存数据,并将工作保存在 ./models 中。
$ lore fit my_app.models.product_popularity.Keras --test --score
查阅终端日志可以了解 Lore 的时间都花在了哪里。
$ tail -f logs/development.log
尝试添加更多隐藏层以了解这一操作是否有助于提高您模型的性能。您可以编辑模型文件或者直接通过命令行调用合适的属性,如 --hidden_layers=5。
检验模型特性
您可以在 lore 环境下运行 jupyter notebooks。Lore 将安装一个可自定义的 jupyter 内核,该内核将为 lore notebook 和 lore console 提供应用程序虚拟环境。
$ lore notebook
浏览 notebooks/product_popularity/features.ipynb 或「运行所有」可以了解您模型的可视化分析。
「生产」类被编码到「20」,这是很大的销售量了。
汇总特定特征时您就可以了解到模型的预测结果(蓝色)和测试结果(黄色)有多一致。在本例中,有 21 类重合程度相当高。「生产」类是例外,模型没有充分说明其逸出值。
您还可以通过运行 notebooks/product_popularity/architecture.ipynb 中的笔记了解生成的深层学习架构。
缩减为 15 个字符的名字通过左边的 LSTM 运行,类名输入到右边的嵌入中,然后一起通过隐藏层。
发布模型服务
Lore 的应用程序可以作为 HTTP API 在本地运行。默认情况下模型会通过 HTTP GET 端点公开其「预测」方法。
$ lore server &
$ curl "http://localhost:5000/product_popularity.Keras/predict.json?product_name=Banana&department=produce"
$ curl "http://localhost:5000/product_popularity.Keras/predict.json?product_name=Organic%20Banana&department=produce"
$ curl "http://localhost:5000/product_popularity.Keras/predict.json?product_name=Green%20Banana&department=produce"
$ curl "http://localhost:5000/product_popularity.Keras/predict.json?product_name=Brown%20Banana&department=produce"
结果表明,在「香蕉」中加入「有机」,将会在「生产」类卖出超过两倍的水果。预测得出,「绿香蕉」比「棕香蕉」销量更差。如果您需要果汁的记录,则应该选择「有机黄香蕉」。
7. 部署产品
Lore 的应用程序可以通过任何支持 Heroku buildpack 的基础架构进行部署。Buildpacks 将 runtime.txt 和 requirements.txt 的依赖项在容器中安装以供模型部署。
您可以在 ./models/my_app.models.product_popularity/Keras/ 中了解每一次运行 lore fit 指令的结果。这个字典和 ./data/ 都默认在 .gitignore 中,您的代码随时可以重建路径。您可以在发布前检查要发布的模型版本,这是一个简单的部署策略:
$ git init .
$ git add .
$ git add -f models/my_app.models.product_popularity/Keras/1 # or your preferred fitting number to deploy
$ git commit -m "My first lore app!"
Heroku 使发布一个应用程序变得非常简单,您可以点击链接浏览其入门介绍:https://devcenter.heroku.com/articles/getting-started-with-python#introduction。
下面是「太长不看」版:
$ heroku login
$ heroku create
$ heroku config:set LORE_PROJECT=my_app
$ heroku config:set LORE_ENV=production
$ git push heroku master
$ heroku open
$ curl “`heroku info -s | grep web_url | cut -d= -f2`product_popularity.Keras/predict.json?product_name=Banana&department=produce”
现在您可以用 heroku 应用的名字替代 http://localhost:5000/,并在任何地方获取预测结果。
原文链接:https://tech.instacart.com/how-to-build-a-deep-learning-model-in-15-minutes-a3684c6f71e