容器化的数据科学与工程——第二部分:容器化的数据科学

lisongp11 8年前
   <p>(这是有关容器化世界里的数据科学与工程系列博客文章的第二部分,点 <a href="/misc/goto?guid=4959673897354879071" rel="nofollow,noindex">此</a> 看第一部分)。</p>    <p>首先要承认,数据科学家正在设计一些非常有意思(而且或许很有价值的)的模型、优化以及虚拟化等。不幸的是,由于很多模型不能被产业化,它们将永远也不会被使用。事实上,很多工业界正在发生的“数据科学”也同步而孤立的发生在数据科学家的笔记本上。而且,在数据科学的应用被实际部署的场景中,它们经常被部署为python/R脚本,上传到AWS并作为一个cron任务来运行。</p>    <p>正如下面所言,这是数据科学用于工业界的一个非常大的问题和障碍:</p>    <p>"只有一个问题——我所有的工作都是在本地机器的R中完成的。人们欣赏我的努力,但是由于它没有被“产品化”且框架不能和本地模型通信,他们不知道如何使用我的模型。非常大的教训!"—— <a href="/misc/goto?guid=4959673897449451252" rel="nofollow,noindex">推ter</a> 的数据科学家 <a href="/misc/goto?guid=4959673897527989957" rel="nofollow,noindex">Robert Chang</a> 。</p>    <p>“数据工程师经常抱怨:数据科学家缩写的代码效率低、风格差;他们很少考虑想法产品化后的维护代价;他们经常要求一些努力很多、受益很小的不切现实的特性。类似的抱怨还有很多,但你已经知道要点在哪了。”——数据平台 <a href="/misc/goto?guid=4959673897607640536" rel="nofollow,noindex">Stitchfix</a> 的经理 <a href="/misc/goto?guid=4959673897689675398" rel="nofollow,noindex">Jeff Magnusson</a> 。</p>    <p>但是,请不要担心!有一个更好的方法: 容器化你的数据科学应用,以方便部署、可移植以及框架内的集成 。</p>    <h2>数据科学家应该关心Docker的原因</h2>    <p>该问题的简单回答就是:数据科学家想让他们的模型、仪表盘、优化等等被实际使用。为了让数据科学的应用被使用并带来价值,它们需要走出笔记本电脑,并被实际部署。它们还需要能够与现有的架构兼容,并易于升级和迭代。</p>    <p>一个Docker化的数据科学应用是如何提供以下好处的呢?</p>    <ul>     <li><em>无论应用如何部署、部署在何处,你无需担心依赖问题。</em> 部署数据科学的应用的一个难点就是,搞清楚机器上复杂的依赖关系(numpy、scipy、pandas、scikit-learn和statsmodels等)。通过将这些应用容器化,你可以在不管依赖关系、部署机器上的操作系统类型以及现有包/库版本的情况下,利用一行命令轻易完成部署。</li>     <li><em>随着公司框架的扩展或你需要扩展你的应用,你可以轻易移植或创建更多实例。</em> 大家经常会在没有全面考虑服务最终部署位置、服务能力的实际需求等问题的情况下开发一个模型或应用。但是,当你将数据科学的应用容器化以后,你可以轻易的根据需求将它从AWS移植到Azure。或者,你可以根据负载情况,创建更多的应用实例。</li>     <li><em>你,作为一个数据科学家,可以保持公司的现代化架构。</em> 替代在与4个不同的数据库直接交互的机器上的cron任务,容器化的数据应用可以利用JSON API和消息队列来与框架的其他部分进行交互。而且更让工程师觉得开心的是,当架构改变或升级时,应用也可以正常工作。你还可以将数据科学的工作和其他工程团队的CI/CD流水线集成在一起。(观众中的数据科学家不要担心:这并不难,而且我们会在下面给出一个例子)。</li>    </ul>    <h2>容器化数据科学应用的一个简单例子</h2>    <p>接下来,让我们从一个python脚本开始了解容器化的数据科学应用。接下来,我会给出容器化数据科学应用的一个简单例子:</p>    <ol>     <li>利用绝大部分数据科学家熟悉的技术( <a href="/misc/goto?guid=4958832508293054196" rel="nofollow,noindex">python</a> 和 <a href="/misc/goto?guid=4958824676420386745" rel="nofollow,noindex">scikit-learn</a> ).</li>     <li>被容器化(也就是说,可以被编译为一个 <a href="/misc/goto?guid=4958839306020721371" rel="nofollow,noindex">Docker</a> 镜像)。</li>     <li>通过JSON API与 <a href="/misc/goto?guid=4958839306020721371" rel="nofollow,noindex">Docker</a> 容器以外的组件进行交互。</li>    </ol>    <h3>一个做预测的简单模型</h3>    <p>这里,我们将利用著名的 <a href="/misc/goto?guid=4959673897871533133" rel="nofollow,noindex">Iris数据集</a> 来构架一个k-NN分类模型(带 <a href="/misc/goto?guid=4958824676420386745" rel="nofollow,noindex">scikit-learn</a> ):</p>    <pre>  <code class="language-python">from sklearn import datasets    from sklearn.neighbors import KNeighborsClassifier    def predict(inputFeatures):       iris = datasets.load_iris()       knn = KNeighborsClassifier()     knn.fit(iris.data, iris.target)       predictInt = knn.predict(inputFeatures)     if predictInt[0] == 0:         predictString = 'setosa'     elif predictInt[0] == 1:         predictString = 'versicolor'     elif predictInt[0] == 2:         predictString = 'virginica'     else:         predictString = 'null'       return predictString</code></pre>    <p>该预测函数将基于输入特征 `inputFeatures (sepal length、sepal width、petal length和petal width)返回一种Iris。在本例中,用于训练模型的数据集是静态的(也就是说,从scikit-learn数据集中加载)。然而,你可以很轻易的想到如何从一个数据集或利用消息、API和数据库交互所聚合的值中动态加载。</p>    <h3>传递预测的JSON API</h3>    <p>接下来,我们需要将这些预测传递到其他组件。为此,我将开发自己作为简单JSON API的应用。对于很多使用微服务架构的工程团队而言,这种应用只是一种普通的练习。而且它可以使得数据应用与其他现存的服务更好的协同工作。</p>    <p>这里,我们将在API中使用 flashk-restful ,你可以使用 <a href="/misc/goto?guid=4959673897971758434" rel="nofollow,noindex">twisted</a> 或其他任何架构:</p>    <pre>  <code class="language-python">from flask import Flask    from flask_restful import Resource, Api    from flask_restful import reqparse    from utils import makeprediction    app = Flask(__name__)    api = Api(app)    class Prediction(Resource):       def get(self):           parser = reqparse.RequestParser()         parser.add_argument('slength', type=float,                   help='slength cannot be converted')         parser.add_argument('swidth', type=float,                   help='swidth cannot be converted')         parser.add_argument('plength', type=float,                   help='plength cannot be converted')         parser.add_argument('pwidth', type=float,                   help='pwidth cannot be converted')         args = parser.parse_args()           prediction = makeprediction.predict([                 args['slength'],                  args['swidth'],                  args['plength'],                  args['pwidth']             ])           print "THE PREDICTION IS: " + str(prediction)           return {                 'slength': args['slength'],                 'swidth': args['swidth'],                 'plength': args['plength'],                 'pwidth': args['pwidth'],                 'species': prediction                }    api.add_resource(Prediction, '/prediction')    if __name__ == '__main__':       app.run(debug=False)</code></pre>    <p>那么,我就得到了一个 GET 端点,使得我们可以利用其来获得针对一个特征集的预测。例如,路径</p>    <p>http://<host>:5000/prediction?slength=1.5&swidth=0.7&plength=1.3&pwidth=0.3</p>    <p>将返回:</p>    <pre>  <code class="language-python">{    "pwidth": 0.3,     "plength": 1.3,     "slength": 1.5,     "species": "setosa",     "swidth": 0.7  }  </code></pre>    <p>其中,在响应JSON中的 species 表示基于输入特征预测的种类。</p>    <h3>构建Docker镜像的Dockerfile</h3>    <p>为了构建一个我们数据科学应用的“Docker镜像”,我们西药一个 Dockerfile 。该 Dockerfile 将呆在repo的root中,并包含Docker镜像中的所有必须的文件和依赖关系。当我们运行Docker镜像时,运行我们所选择的一个命令:</p>    <pre>  <code class="language-python">FROM ubuntu:12.04    # get up pip, vim, etc.  RUN apt-get -y update --fix-missing    RUN apt-get install -y python-pip python-dev libev4 libev-dev gcc libxslt-dev libxml2-dev libffi-dev vim curl    RUN pip install --upgrade pip    # get numpy, scipy, scikit-learn and flask  RUN apt-get install -y python-numpy python-scipy    RUN pip install scikit-learn    RUN pip install flask-restful    # add our project  ADD . /    # expose the port for the API  EXPOSE 5000    # run the API   CMD [ "python", "/api.py" ]  </code></pre>    <h3>准备完毕,开始部署应用</h3>    <p>以上就是构建第一个容器化的数据科学应用所需要的所有步骤(对于Docker的安装指令,参看 <a href="/misc/goto?guid=4958839306020721371" rel="nofollow,noindex">Docker网站</a> )。现在,让我们构建应用的“Docker镜像”:</p>    <p>docker build --force-rm=true -t pythoniris</p>    <p>该命令将构建一个名为 pythoniris 的Docker镜像。我们可以根据需要标记该镜像(例如, pythoniris:latest ),或将其和 <a href="/misc/goto?guid=4958849364237785306" rel="nofollow,noindex">Docker Hub</a> 上的用户/账号(例如, dwhitena/pythoniris )关联起来(Docker Hub是一个专门存储Docker镜像的公开仓库,类似于Docker镜像的Github)。</p>    <p>如果你将镜像上传到Docker Hub(或一个私有仓库),部署就像运行引用Docker Hub或仓库中的用户名/镜像名的Docker镜像一样容易。然而,假设你想首先在本地进行这些尝试,你可以通过如下命令来运行Docker镜像:</p>    <pre>  <code class="language-python">docker run --net host -d --name myiris pythoniris   </code></pre>    <p>该命令将运行Docker镜像运行为一个名为 myiris 的容器、一个守护进程(-d),并使用与本地主机相同的网络接口( --net host )。现在,你的JOSN API就可以通过 localhost:5000 端口进行访问了。</p>    <p>可以看的出来,从python脚本到容器化的数据应用只需要一点点的付出。现在,请继续向前——研究数据科学、容器化数据科学和部署你的数据科学吧。</p>    <p>以上代码可以在 <a href="/misc/goto?guid=4959673898088292736" rel="nofollow,noindex">Github</a> 中下载。</p>    <p>来自: <a href="/misc/goto?guid=4959673898168562113" rel="nofollow">http://www.infoq.com/cn/articles/container-data-science-and-engineering-part02</a></p>    <p> </p>