學(xué)習(xí)隨筆:部署機(jī)器學(xué)習(xí)的Web APP

Ch9:Embedding a Machine Learning Model into a Web Application

前面的ML模型都是在本地運(yùn)行計(jì)算的,此chapter介紹如何將模型應(yīng)用在Web app上以獲得實(shí)時學(xué)習(xí)耿导、應(yīng)用
這一章主要偏機(jī)器學(xué)習(xí)功能Web的簡單開發(fā)龄寞,與算法關(guān)系不大宏赘。沒有跳過反而收獲不少。不得不佩服作者在構(gòu)思’算法+系統(tǒng)‘時所作出的為讀者充分考慮的思想

本章主要實(shí)現(xiàn)下列功能:(將情感分析學(xué)習(xí)器上線Web,使之能即時搜集新數(shù)據(jù)难菌、學(xué)習(xí)新數(shù)據(jù)竞惋、更新學(xué)習(xí)器柜去、做出預(yù)測。最后作者還提供了一種上傳到公共Web空間的一種簡便方式)

  • Saving the current state of a trained machine learning model
  • Using databases for data storage
  • Developing a web application using the popular Flask web framework
  • Deploying a machine learning application to a public web server

Serializing fitted scikit-learn estimators

這里應(yīng)用的是 sentiment analysis 中的out_of_core.py中訓(xùn)練的線上模型為例拆宛,關(guān)于這個學(xué)習(xí)模型主要是一個one_line的學(xué)習(xí)模式嗓奢,就是所有數(shù)據(jù)不在本地內(nèi)存中。

即時學(xué)習(xí)的方法

fit 是全部學(xué)習(xí)所有的數(shù)據(jù)胰挑,而estimator的part_fit(X_new)方法可以在原來的學(xué)習(xí)器基礎(chǔ)上學(xué)習(xí)新數(shù)據(jù)蔓罚,更新新性能。

序列化

序列化將本地變量(如estimator)存儲為二進(jìn)制pkl文件瞻颂,當(dāng)大型的estimator需要在網(wǎng)絡(luò)空間使用時直接dump出pkl,這一過程的速度回很快。(想想上章節(jié)中本地memory學(xué)習(xí)器學(xué)習(xí)完需要40min,而on_line訓(xùn)練完成也需要3min,這放在Web服務(wù)器上是不可能的郑象,所以序列化是必要的)

import pickle
import os
dest = os.path.join('movieclassifier', 'pkl_objects')
if not os.path.exists(dest):
    os.makedirs(dest)
# 為了方便贡这,將停用詞也序列化
pickle.dump(stop,open(os.path.join(dest, 'stopwords.pkl'),'wb'),protocol=4)
pickle.dump(clf,open(os.path.join(dest, 'classifier.pkl'), 'wb'),protocol=4)

數(shù)據(jù)庫

作者用的是Sqlite,emmmmmm一言難盡啊

Sqlite 厂榛,對就是Django初始化使用的輕量級數(shù)據(jù)庫盖矫,不需要額外的配置接口什么的,可以像訪問一個文件一樣地訪問他击奶。辈双。。 多么厲害友好柜砾!

所以湃望,我決定換掉它,用Mysql

至于為啥痰驱,習(xí)慣吧...... 作者的初衷是好的证芭,因?yàn)檫@樣免去了學(xué)習(xí)新數(shù)據(jù)庫的成本和連接耽誤的風(fēng)險(xiǎn),作者面向的對象是沒有Web開發(fā)經(jīng)驗(yàn)的人担映,所以這一點(diǎn)是值得肯定的废士。

Flask框架

Why Flask?蝇完?作者是這樣回答的:

Flask is also known as a microframework, which means that its core is kept lean
and simple but can be easily extended with other libraries. Although the learning
curve of the lightweight Flask API is not nearly as steep as those of other popular
Python web frameworks, such as Django, I encourage you to take a look at the
official Flask documentation at http://flask.pocoo.org/docs/0.12/ to learn more about
its functionality.

哦官硝,你說Flask比Django簡單,好吧那就用Flask吧短蜕!

控制層

本質(zhì)上講 Flask也是用的MVC理論氢架。

Flask最主要的就是app.py文件 相當(dāng)于Django的views.py ,不同的是他還負(fù)責(zé)路徑和數(shù)據(jù)庫(Flask數(shù)據(jù)庫靠依賴三方庫,所以還需要開發(fā)者自行編寫SQL語句)的問題忿危。而在Django中路徑需要有專門的配置文件达箍,數(shù)據(jù)庫可以用ORM。

這么一對比Django的好處是數(shù)據(jù)庫可以用映射關(guān)系避免寫sql,模塊功能更加清楚分明铺厨。

但Flask也一定有很多比Django好很多的地方缎玫,剛接觸Flask希望在未來慢慢體驗(yàn)吧

app.py代碼硬纤,重要地方我做了注釋:

from flask import Flask, render_template, request
from wtforms import Form, TextAreaField, validators
import pickle
import pymysql
import os
import numpy as np

# import HashingVectorizer from local dir
from vectorizer import vect

app = Flask(__name__)

######## Preparing the Classifier 必要的函數(shù)準(zhǔn)備
cur_dir = os.path.dirname(__file__)
clf = pickle.load(open(os.path.join(cur_dir,
                 'pkl_objects',
                 'classifier.pkl'), 'rb'))
# db = os.path.join(cur_dir, 'reviews.sqlite')

def classify(document):
    label = {0: 'negative', 1: 'positive'}
    # 轉(zhuǎn)化詞袋模型
    X = vect.transform([document])
    # 得到預(yù)測結(jié)果
    y = clf.predict(X)[0]
    # 得到預(yù)測結(jié)果的準(zhǔn)確率
    proba = np.max(clf.predict_proba(X))
    return label[y], proba

def train(document, y):
    X = vect.transform([document])
    # 即時學(xué)習(xí)新的數(shù)據(jù)
    clf.partial_fit(X, [y])

def mysql_entry(document, y):
    # 這個模塊不解釋,添加新影評進(jìn)入數(shù)據(jù)庫
    conn = pymysql.connect(host = 'yzk.mysql.pythonanywhere-services.com',user = 'yzk',password = '',db = 'ML')
    c = conn.cursor()
    sql = 'insert into movie_text values(%s,%s)'
    c.execute(sql,(document,y))
    # c.execute("INSERT INTO review_db (review, sentiment, date)"\
    # " VALUES (?, ?, DATETIME('now'))", (document, y))
    conn.commit()
    conn.close()

######## 從這里Flask部分開始赃磨,下面會調(diào)用上面的函數(shù)
class ReviewForm(Form):
    moviereview = TextAreaField('',
                                [validators.DataRequired(),
                                validators.length(min=15)])
# Flask用裝飾器控制路徑和方法
# 用戶輸入評論模塊
 @app.route('/')
def results():
    form = ReviewForm(request.form)
    if request.method == 'POST' and form.validate():
        # 得到評論 這個movieriew名稱是怎么來的筝家,下面我會在前端做下解釋
        review = request.form['moviereview']
        y, proba = classify(review)
        # 在前端渲染結(jié)果
        return render_template('results.html',
                                content=review,
                                prediction=y,
                                probability=round(proba*100, 2))
    return render_template('reviewform.html', form=form)
# 反饋功能模塊,用戶點(diǎn)擊是否情感判斷正確邻辉,將用戶給的正確標(biāo)簽和評論添加到數(shù)據(jù)庫中溪王,并使學(xué)習(xí)器學(xué)習(xí)
@app.route('/thanks', methods=['POST'])
def feedback():
    # 這些名稱同樣我在下面將前端時講述
    feedback = request.form['feedback_button']
    review = request.form['review']
    prediction = request.form['prediction']

    inv_label = {'negative': 0, 'positive': 1}
    y = inv_label[prediction]
    if feedback == 'Incorrect':
        y = int(not(y))
    train(review, y)
    mysql_entry(review, y)
    return render_template('thanks.html')

if __name__ == '__main__':
    app.run(debug=True)

前端

Flask的前端跟Django的理念差不多,渲染模板值骇。這里講一下上段代碼中Form 在前端的表現(xiàn)問題:

關(guān)于輸入文本框沒有那么簡單莹菱,還需要很多額外的功能,如限定最大最小字符數(shù)吱瘩,錯誤返回道伟,樣式等等一系列問題,這些再去編寫相關(guān)css\js麻煩使碾,F(xiàn)lask和Django都會用到一些如Jinja的成熟模板:

<!-- _formhelpers.html是引入Jinja -->
{% from "_formhelpers.html" import render_field %}

<form method=post action="/results">
  <dl>
     <!-- 這里傳入了樣式參數(shù) 蜜徽,表單名稱就是從這里來的-->
    {{ render_field(form.moviereview, cols='30', rows='10') }}
  </dl>
  <div>
      <input type=submit value='Submit review' name='submit_btn'>
  </div>
</form>

前端與后端 參數(shù)傳遞 也跟Django的模式差不多。

在本地運(yùn)行起來吧票摇!

輸入影評

image

得到結(jié)果

image

可以看到得出了高概率的消極結(jié)果,....What??? 這里先對模型準(zhǔn)確性不做判述拘鞋;用戶點(diǎn)擊正確或者不正確可以添加新數(shù)據(jù)到數(shù)據(jù)庫中,然后學(xué)習(xí)器及時學(xué)習(xí)他矢门。

循環(huán)測試了四次盆色,發(fā)現(xiàn)此分類器對于短一些的情感色彩不那么明顯的評論是無能為力的

打開本地Mysql后臺,測試一下是否添加新數(shù)據(jù):

image

嗯成功添加了颅和。

上傳到公共空間吧傅事!

其實(shí)作者寫道這里是畫蛇添足了,沒必要繼續(xù)下去了峡扩,本地運(yùn)行成功的學(xué)習(xí)成本已經(jīng)很大了蹭越。不過他就寫了兩頁,我看完之后打開了新世界教届。响鹃。。

作者推薦了pythonAnywhere平臺案训,跟google\amozon之類的云平臺不同买置,他主要面對python的Web服務(wù),在這里你可以免費(fèi)\付費(fèi) 地部署自己的Python - Web 應(yīng)用强霎,F(xiàn)lask,Django都可以忿项。因?yàn)閷iT針對Python,所以對于開發(fā)者來說極大的便利就是:不需要再搭建環(huán)境!轩触,只需要吧我們上面本地做好的文件上傳就可以訪問我們的機(jī)器學(xué)習(xí)應(yīng)用啦寞酿!

點(diǎn)這里訪問上線的情感分析程序
就像這樣:

image

然后再Web中創(chuàng)建自己的應(yīng)用:

image

簡直不能再方便了,上圖中可以看到:通用網(wǎng)關(guān)脱柱、日志文件伐弹、靜態(tài)文件路徑都給你放好了...... 真的省了一堆麻煩.

等等!榨为,數(shù)據(jù)庫咋辦惨好? 總不能局域網(wǎng)訪問吧?随闺?(看到這里對作者為何使用Sqlite恍然大悟)

這個問題pythonAnywhere當(dāng)然想到了日川,你可以在他們云端免費(fèi)申請一個Mysql或者Postgres數(shù)據(jù)庫。

image

上圖可知板壮,簡直不需要你再做什么了逗鸣,唯一需要做的就是,他沒有給你數(shù)據(jù)庫的GUI(如果這個再有的話绰精,emmmmmm一言難盡)

你需要自己打開數(shù)據(jù)庫的命令行來創(chuàng)建表:

image

免費(fèi)的用戶不能使用SSH鏈接,也就意味著免費(fèi)試用者智能在Web端訪問他們的bash透葛,數(shù)據(jù)庫和下面將要說道的服務(wù)器bash都是一樣的笨使。 這樣的缺點(diǎn)是網(wǎng)頁的console特別慢。僚害。硫椰。。萨蚕。

數(shù)據(jù)庫搞定靶草,訪問提供的免費(fèi)域名,emmmm出錯了,不過錯誤日志很方便查看:

image

emmmm 沒有pymysql岳遥,前面剛說了不用再搭環(huán)境了奕翔,這一秒打臉?浩蓉?派继?

好吧,好在官網(wǎng)給出了解決方案捻艳,就是在網(wǎng)頁打開服務(wù)器bash驾窟,你自己手動裝一個....pip就OK...

好了 這下可以了:

image

小結(jié)

說說作者的這套技術(shù)路線吧

算法 + 系統(tǒng) 也是我的專業(yè)最好的結(jié)合方式,這里作者給出了一個初步的web解決方案认轨,只使用一種語言绅络、一種工具。總的來講是很棒的恩急。但是有一個大問題就是學(xué)習(xí)器的加速杉畜,整個框架文件學(xué)習(xí)器體積最大,io效率最慢假栓。如果要更變參數(shù)又得重新學(xué)習(xí)寻行,時間成本很大。如果未來機(jī)器學(xué)習(xí)算法程序真的要面向用戶匾荆,我認(rèn)為作者提出的此套技術(shù)路徑是不太適合大型項(xiàng)目的拌蜘。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市牙丽,隨后出現(xiàn)的幾起案子简卧,更是在濱河造成了極大的恐慌,老刑警劉巖烤芦,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件举娩,死亡現(xiàn)場離奇詭異,居然都是意外死亡构罗,警方通過查閱死者的電腦和手機(jī)铜涉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遂唧,“玉大人芙代,你說我怎么就攤上這事「桥恚” “怎么了纹烹?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長召边。 經(jīng)常有香客問我铺呵,道長,這世上最難降的妖魔是什么隧熙? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任片挂,我火速辦了婚禮,結(jié)果婚禮上贱鼻,老公的妹妹穿的比我還像新娘宴卖。我一直安慰自己,他們只是感情好邻悬,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布症昏。 她就那樣靜靜地躺著,像睡著了一般父丰。 火紅的嫁衣襯著肌膚如雪肝谭。 梳的紋絲不亂的頭發(fā)上掘宪,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天,我揣著相機(jī)與錄音攘烛,去河邊找鬼魏滚。 笑死,一個胖子當(dāng)著我的面吹牛坟漱,可吹牛的內(nèi)容都是我干的鼠次。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼芋齿,長吁一口氣:“原來是場噩夢啊……” “哼腥寇!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起觅捆,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤赦役,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后栅炒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體掂摔,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年赢赊,在試婚紗的時候發(fā)現(xiàn)自己被綠了乙漓。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡释移,死狀恐怖簇秒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情秀鞭,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布扛禽,位于F島的核電站锋边,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏编曼。R本人自食惡果不足惜豆巨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望掐场。 院中可真熱鬧往扔,春花似錦、人聲如沸熊户。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嚷堡。三九已至蝗罗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背串塑。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工沼琉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人桩匪。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓打瘪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親傻昙。 傳聞我的和親對象是個殘疾皇子闺骚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容