容器化的數據科學與工程——第二部分:容器化的數據科學
(這是有關容器化世界里的數據科學與工程系列博客文章的第二部分,點 此 看第一部分)。
首先要承認,數據科學家正在設計一些非常有意思(而且或許很有價值的)的模型、優化以及虛擬化等。不幸的是,由于很多模型不能被產業化,它們將永遠也不會被使用。事實上,很多工業界正在發生的“數據科學”也同步而孤立的發生在數據科學家的筆記本上。而且,在數據科學的應用被實際部署的場景中,它們經常被部署為python/R腳本,上傳到AWS并作為一個cron任務來運行。
正如下面所言,這是數據科學用于工業界的一個非常大的問題和障礙:
"只有一個問題——我所有的工作都是在本地機器的R中完成的。人們欣賞我的努力,但是由于它沒有被“產品化”且框架不能和本地模型通信,他們不知道如何使用我的模型。非常大的教訓!"—— 推ter 的數據科學家 Robert Chang 。
“數據工程師經常抱怨:數據科學家縮寫的代碼效率低、風格差;他們很少考慮想法產品化后的維護代價;他們經常要求一些努力很多、受益很小的不切現實的特性。類似的抱怨還有很多,但你已經知道要點在哪了。”——數據平臺 Stitchfix 的經理 Jeff Magnusson 。
但是,請不要擔心!有一個更好的方法: 容器化你的數據科學應用,以方便部署、可移植以及框架內的集成 。
數據科學家應該關心Docker的原因
該問題的簡單回答就是:數據科學家想讓他們的模型、儀表盤、優化等等被實際使用。為了讓數據科學的應用被使用并帶來價值,它們需要走出筆記本電腦,并被實際部署。它們還需要能夠與現有的架構兼容,并易于升級和迭代。
一個Docker化的數據科學應用是如何提供以下好處的呢?
- 無論應用如何部署、部署在何處,你無需擔心依賴問題。 部署數據科學的應用的一個難點就是,搞清楚機器上復雜的依賴關系(numpy、scipy、pandas、scikit-learn和statsmodels等)。通過將這些應用容器化,你可以在不管依賴關系、部署機器上的操作系統類型以及現有包/庫版本的情況下,利用一行命令輕易完成部署。
- 隨著公司框架的擴展或你需要擴展你的應用,你可以輕易移植或創建更多實例。 大家經常會在沒有全面考慮服務最終部署位置、服務能力的實際需求等問題的情況下開發一個模型或應用。但是,當你將數據科學的應用容器化以后,你可以輕易的根據需求將它從AWS移植到Azure。或者,你可以根據負載情況,創建更多的應用實例。
- 你,作為一個數據科學家,可以保持公司的現代化架構。 替代在與4個不同的數據庫直接交互的機器上的cron任務,容器化的數據應用可以利用JSON API和消息隊列來與框架的其他部分進行交互。而且更讓工程師覺得開心的是,當架構改變或升級時,應用也可以正常工作。你還可以將數據科學的工作和其他工程團隊的CI/CD流水線集成在一起。(觀眾中的數據科學家不要擔心:這并不難,而且我們會在下面給出一個例子)。
容器化數據科學應用的一個簡單例子
接下來,讓我們從一個python腳本開始了解容器化的數據科學應用。接下來,我會給出容器化數據科學應用的一個簡單例子:
- 利用絕大部分數據科學家熟悉的技術( python 和 scikit-learn ).
- 被容器化(也就是說,可以被編譯為一個 Docker 鏡像)。
- 通過JSON API與 Docker 容器以外的組件進行交互。
一個做預測的簡單模型
這里,我們將利用著名的 Iris數據集 來構架一個k-NN分類模型(帶 scikit-learn ):
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
該預測函數將基于輸入特征 `inputFeatures (sepal length、sepal width、petal length和petal width)返回一種Iris。在本例中,用于訓練模型的數據集是靜態的(也就是說,從scikit-learn數據集中加載)。然而,你可以很輕易的想到如何從一個數據集或利用消息、API和數據庫交互所聚合的值中動態加載。
傳遞預測的JSON API
接下來,我們需要將這些預測傳遞到其他組件。為此,我將開發自己作為簡單JSON API的應用。對于很多使用微服務架構的工程團隊而言,這種應用只是一種普通的練習。而且它可以使得數據應用與其他現存的服務更好的協同工作。
這里,我們將在API中使用 flashk-restful ,你可以使用 twisted 或其他任何架構:
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)
那么,我就得到了一個 GET 端點,使得我們可以利用其來獲得針對一個特征集的預測。例如,路徑
http://<host>:5000/prediction?slength=1.5&swidth=0.7&plength=1.3&pwidth=0.3
將返回:
{
"pwidth": 0.3,
"plength": 1.3,
"slength": 1.5,
"species": "setosa",
"swidth": 0.7
}
其中,在響應JSON中的 species 表示基于輸入特征預測的種類。
構建Docker鏡像的Dockerfile
為了構建一個我們數據科學應用的“Docker鏡像”,我們西藥一個 Dockerfile 。該 Dockerfile 將呆在repo的root中,并包含Docker鏡像中的所有必須的文件和依賴關系。當我們運行Docker鏡像時,運行我們所選擇的一個命令:
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" ]
準備完畢,開始部署應用
以上就是構建第一個容器化的數據科學應用所需要的所有步驟(對于Docker的安裝指令,參看 Docker網站 )。現在,讓我們構建應用的“Docker鏡像”:
docker build --force-rm=true -t pythoniris
該命令將構建一個名為 pythoniris 的Docker鏡像。我們可以根據需要標記該鏡像(例如, pythoniris:latest ),或將其和 Docker Hub 上的用戶/賬號(例如, dwhitena/pythoniris )關聯起來(Docker Hub是一個專門存儲Docker鏡像的公開倉庫,類似于Docker鏡像的Github)。
如果你將鏡像上傳到Docker Hub(或一個私有倉庫),部署就像運行引用Docker Hub或倉庫中的用戶名/鏡像名的Docker鏡像一樣容易。然而,假設你想首先在本地進行這些嘗試,你可以通過如下命令來運行Docker鏡像:
docker run --net host -d --name myiris pythoniris
該命令將運行Docker鏡像運行為一個名為 myiris 的容器、一個守護進程(-d),并使用與本地主機相同的網絡接口( --net host )。現在,你的JOSN API就可以通過 localhost:5000 端口進行訪問了。
可以看的出來,從python腳本到容器化的數據應用只需要一點點的付出。現在,請繼續向前——研究數據科學、容器化數據科學和部署你的數據科學吧。
以上代碼可以在 Github 中下載。
來自: http://www.infoq.com/cn/articles/container-data-science-and-engineering-part02