天天看點

深度學習應用:入門篇(下)

四、經典入門demo:識别手寫數字(mnist)

正常的程式設計入門有“hello world”程式,而深度學習的入門程式則是mnist,一個識别28*28像素的圖檔中的手寫數字的程式。

mnist的資料和官網:

<a href="http://yann.lecun.com/exdb/mnist/" target="_blank">http://yann.lecun.com/exdb/mnist/</a>

深度學習的内容,其背後會涉及比較多的數學原理,作為一個初學者,受限于我個人的數學和技術水準,也許并不足以準确講述相關的數學原理,是以,本文會更多的關注“應用層面”,不對背後的數學原理進行展開,感謝諒解。

加載資料

程式執行的第一步當然是加載資料,根據我們之前獲得的資料集主要包括兩部分:60000的訓練資料集(mnist.train)和10000的測試資料集(mnist.test)。裡面每一行,是一個2828=784的數組,數組的本質就是将2828像素的圖檔,轉化成對應的像素點陣。

例如手寫字1的圖檔轉換出來的對應矩陣表示如下:

之前我們經常聽說,圖檔方面的深度學習需要大量的計算能力,甚至需要采用昂貴、專業的gpu(nvidia的gpu),從上述轉化的案例我們就已經可以獲得一些答案了。一張784像素的圖檔,對學習模型來說,就有784個特征,而我們實際的相片和圖檔動辄幾十萬、百萬級别,則對應的基礎特征數也是這個數量級,基于這樣數量級的數組進行大規模運算,沒有強大的計算能力支援,确實寸步難行。當然,這個入門的mnist的demo還是可以比較快速的跑完。

demo中的關鍵代碼(讀取并且加載資料到數組對象中,友善後面使用):

構模組化型

mnist的每一張圖檔都表示一個數字,從0到9。而模型最終期望獲得的是:給定一張圖檔,獲得代表每個數字的機率。比如說,模型可能推測一張數字9的圖檔代表數字9的機率是80%但是判斷它是8的機率是5%(因為8和9都有上半部分的小圓),然後給予它代表其他數字的機率更小的值。

mnist的入門例子,采用的是softmax回歸(softmax regression),softmax模型可以用來給不同的對象配置設定機率。

為了得到一張給定圖檔屬于某個特定數字類的證據(evidence),我們對圖檔的784個特征(點陣裡的各個像素值)進行權重求和。如果某個特征(像素值)具有很強的證據說明這張圖檔不屬于該類,那麼相應的權重值為負數,相反如果某個特征(像素值)擁有有利的證據支援這張圖檔屬于這個類,那麼權重值是正數。類似前面提到的房價估算例子,對每一個像素點作出了一個權重配置設定。

假設我們獲得一張圖檔,需要計算它是8的機率,轉化成數學公式則如下:

公式中的i代表需要預測的數字(8),代表預測數字為8的情況下,784個特征的不同權重值,代表8的偏置量(bias),x則是該圖檔784個特征的值。通過上述計算,我們則可以獲得證明該圖檔是8的證據(evidence)的總和,softmax函數可以把這些證據轉換成機率 y。(softmax的數學原理,辛苦各位查詢相關資料哈)

将前面的過程概括成一張圖(來自官方)則如下:

不同的特征x和對應不同數字的權重進行相乘和求和,則獲得在各個數字的分布機率,取機率最大的值,則認為是我們的圖檔預測結果。

将上述過程寫成一個等式,則如下:

該等式在矩陣乘法裡可以非常簡單地表示,則等價為:

不展開裡面的具體數值,則可以簡化為:

如果我們對線性代數中矩陣相關内容有适當學習,其實,就會明白矩陣表達在一些問題上,更易于了解。如果對矩陣内容不太記得了,也沒有關系,後面我會附加上線性代數的視訊。

雖然前面講述了這麼多,其實關鍵代碼就四行:

上述代碼都是類似變量占位符,先設定好模型計算方式,在真實訓練流程中,需要批量讀取源資料,不斷給它們填充資料,模型計算才會真實跑起來。tf.zeros則表示,先給它們統一指派為0占位。x資料是從資料檔案中讀取的,而w、b是在訓練過程中不斷變化和更新的,y則是基于前面的資料進行計算得到。

損失函數和優化設定

為了訓練我們的模型,我們首先需要定義一個名額來衡量這個模型是好還是壞。這個名額稱為成本(cost)或損失(loss),然後盡量最小化這個名額。簡單的說,就是我們需要最小化loss的值,loss的值越小,則我們的模型越逼近标簽的真實結果。

demo中使用的損失函數是“交叉熵”(cross-entropy),它的公式如下:

y 是我們預測的機率分布, y' 是實際的分布(我們輸入的),交叉熵是用來衡量我們的預測結果的不準确性。tensorflow擁有一張描述各個計算單元的圖,也就是整個模型的計算流程,它可以自動地使用反向傳播算法(backpropagation algorithm),來确定我們的權重等變量是如何影響我們想要最小化的那個loss值的。然後,tensorflow會用我們設定好的優化算法來不斷修改變量以降低loss值。

其中,demo采用梯度下降算法(gradient descent algorithm)以0.01的學習速率最小化交叉熵。梯度下降算法是一個簡單的學習過程,tensorflow隻需将每個變量一點點地往使loss值不斷降低的方向更新。

對應的關鍵代碼如下:

備注内容:

在代碼中會看見one-hot vector的概念和變量名,其實這個是個非常簡單的東西,就是設定一個10個元素的數組,其中隻有一個是1,其他都是0,以此表示數字的标簽結果。

例如表示數字3的标簽值:

[0,0,0,1,0,0,0,0,0,0]

訓練運算和模型準确度測試

通過前面的實作,我們已經設定好了整個模型的計算“流程圖”,它們都成為tensorflow架構的一部分。于是,我們就可以啟動我們的訓練程式,下面的代碼的含義是,循環訓練我們的模型500次,每次批量取50個訓練樣本。

其訓練過程,其實就是tensorflow架構的啟動訓練過程,在這個過程中,python批量地将資料交給底層庫進行處理。

我在官方的demo裡追加了兩行代碼,每隔50次則額外計算一次目前模型的識别準确率。它并非必要的代碼,僅僅用于友善觀察整個模型的識别準确率逐漸變化的過程。

當然,裡面涉及的accuracy(預測準确率)等變量,需要在前面的地方定義占位:

當我們訓練完畢,則到了驗證我們的模型準确率的時候,和前面相同:

我的demo跑出來的結果如下(softmax回歸的例子運作速度還是比較快的),目前的準确率是0.9252:

實時檢視參數的數值的方法

剛開始跑官方的demo的時候,我們總想将相關變量的值列印出來看看,是怎樣一種格式和狀态。從demo的代碼中,我們可以看見很多的tensor變量對象,而實際上這些變量對象都是無法直接輸出檢視,粗略地了解,有些隻是占位符,直接輸出的話,會獲得類似如下的一個對象:

tensor("equal:0", shape=(?,), dtype=bool)

既然它是占位符,那麼我們就必須喂一些資料給它,它才能将真實内容展示出來。是以,正确的方法是,在列印時通常需要加上目前的輸入資料給它。

例如,檢視y的機率資料:

print(sess.run(y, feed_dict={x: batchxs, y: batch_ys}))

部分非占位符的變量還可以這樣輸出來:

print(w.eval())

總的來說,92%的識别準确率是比較令人失望,是以,官方的mnist其實也有多種模型的不同版本,其中比較适合圖檔處理的cnn(卷積神經網絡)的版本,可以獲得99%以上的準确率,當然,它的執行耗時也是比較長的。

(備注:cnn_mnist.py就是卷積神經網絡版本的,後面有附帶微雲網盤的下載下傳url)

前饋神經網絡(feed-forward neural network)版本的mnist,可達到97%:

分享在微雲上的資料和源碼:

<a href="http://url.cn/44azopp" target="_blank">http://url.cn/44azopp</a>

(備注:國外網站下載下傳都比較慢,我這份下載下傳相對會快一些,在環境已經搭建完畢的情況下,執行裡面的run.py即可)

五、和業務場景結合的demo:預測使用者是否是超級會員身份

根據前面的内容,我們對上述基于softmax隻是三層(輸入、處理、輸出)的神經網絡模型已經比較熟悉,那麼,這個模型是否可以應用到我們具體的業務場景中,其中的難度大嗎?為了驗證這一點,我拿了一些現網的資料來做了這個試驗。

資料準備

我将一個現網的電影票活動的使用者參與資料,包括點選過哪些按鈕、手機平台、ip位址、參與時間等資訊抓取了出來。其實這些資料當中是隐含了使用者的身份資訊的,例如,某些禮包的必須是超級會員身份才能領取,如果這個按鈕使用者點選領取成功,則可以證明該使用者的身份肯定是超級會員身份。當然,我隻是将這些不知道相不相關的資料特征直覺的整理出來,作為我們的樣本資料,然後對應的标簽為超級會員身份。

用于訓練的樣本資料格式如下:

第一列是qq号碼,隻做認知辨別的,第二清單示是否超級會員身份,作為訓練的标簽值,後面的就是ip位址,平台标志位以及參與活動的參與記錄(0是未成功參與,1表示成功參與)。則獲得一個擁有11個特征的數組(經過一些轉化和映射,将特别大的數變小):

[0.9166666666666666, 0.4392156862745098, 0.984313725490196, 0.7411764705882353, 0.2196078431372549, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]

對應的是否是超級資料格式如下,作為監督學習的标簽:

超級會員:[0, 1]

非超級會員:[1, 0]

這裡需要專門解釋下,在實際應用中需要做資料轉換的原因。一方面,将這些資料做一個映射轉化,有助于簡化資料模型。另一方面,是為了規避nan的問題,當數值過大,在一些數學指數和除法的浮點數運算中,有可能得到一個無窮大的數值,或者其他溢出的情形,在python裡會變為nan類型,這個類型會破壞掉後續全部計算結果,導緻計算異常。

例如下圖,就是特征數值過大,在訓練過程中,導緻中間某些參數累計越來越大,最終導緻産生nan值,後續的計算結果全部被破壞掉:

而導緻nan的原因在複雜的數學計算裡,會産生無窮大或者無窮小。例如,在我們的這個demo中,産生nan的原因,主要是因為softmax的計算導緻。

runtimewarning: divide by zero encountered in log

剛開始做實際的業務應用,就發現經常跑出極奇怪異的結果(遇到nan問題,我發現程式也能繼續走下去),幾經排查才發現是nan值問題,是非常令人沮喪的。當然,經過仔細分析問題,發現也并非沒有排查的方式。因為,nan值是個奇特的類型,可以采用下述編碼方式nan != nan來檢測自己的訓練過程中,是否出現的nan。

關鍵程式代碼如下:

我采用上述方法,非常順利地找到自己的深度學習程式,在學習到哪一批資料時産生的nan。是以,很多原始資料我們都會做一個除以某個值,讓數值變小的操作。例如官方的mnist也是這樣做的,将256的像素顔色的數值統一除以255,讓它們都變成一個小于1的浮點數。

mnist在處理原始圖檔像素特征資料時,也對特征資料進行了變小處理:

nan值問題一度深深地困擾着我(往事不堪回首-__-!!),特别放到這裡,避免入門的同學踩坑。

執行結果

我準備的訓練集(6700)和測試集(1000)資料并不多,不過,超級會員身份的預測準确率最終可以達到87%。雖然,預測準确率是不高,這個可能和我的訓練集資料比較少有關系,不過,整個模型也沒有花費多少時間,從整理資料、編碼、訓練到最終跑出結果,隻用了2個晚上的時間。

下圖是兩個實際的測試例子,例如,該模型預測第一個qq使用者有82%的機率是非超級會員使用者,17.9%的機率為超級會員使用者(該預測是準确的)。

通過上面的這個例子,我們會發覺其實對于某些比較簡單的場景下應用,我們是可以比較容易就實作的。

六、其他模型

cifar-10識别圖檔分類的demo(官方)

cifar-10資料集的分類是機器學習中一個公開的基準測試問題,它任務是對一組32x32rgb的圖像進行分類,這些圖像涵蓋了10個類别:飛機, 汽車, 鳥, 貓, 鹿, 狗, 青蛙, 馬, 船和卡車。

這也是官方的重要demo之一。

更詳細的介紹内容:

<a href="http://www.cs.toronto.edu/~kriz/cifar.html" target="_blank">http://www.cs.toronto.edu/~kriz/cifar.html</a>

<a href="http://tensorfly.cn/tfdoc/tutorials/deep_cnn.html" target="_blank">http://tensorfly.cn/tfdoc/tutorials/deep_cnn.html</a>

該例子執行的過程比較長,需要耐心等待。

我在機器上的執行過程和結果:

cifar10_train.py用于訓練:

cifar10_eval.py用于檢驗結果:

識别率不高是因為該官方模型的識别率本來就不高:

另外,官方的例子我首次在1月5日跑的時候,還是有一些小問題的,無法跑起來(最新的官方可能已經修正),建議可以直接使用我放到微雲上的版本(代碼裡面的log和讀取檔案的路徑,需要調整一下)。

微雲盤裡,不含訓練集和測試集的圖檔資料,但是,程式如果檢測到這些圖檔不存在,會自行下載下傳:

是否大于5歲的測試demo

為了檢驗softma回歸模型是否能夠學習到一些我自己設定好的規則,我做了一個小demo來測試。我通過随機數生成的方式構造了一系列的資料,讓前面的softmax回歸模型去學習,最終看看模型能否通過訓練集的學習,最終100%預測這個樣本資料是否大于5歲。

模型和資料本身都比較簡單,構造的資料的方式:

我随機構造一個隻有2個特征緯度的樣本資料,[year, 1],其中year随機取值0-10,數字1是放進去作為幹擾。

如果year大于5歲,則标簽設定為:[0, 0, 1];

否則,标簽設定為:[0, 1, 0]。

生成了6000條假訓練集去訓練該模型,最終它能做到100%成功預測準确:

微雲下載下傳(源碼下載下傳):

<a href="http://url.cn/44mkfnk" target="_blank">http://url.cn/44mkfnk</a>

基于rnn的古詩學習

最開頭的ai寫古詩,非常令人感到驚豔,那個demo是美國的一個研究者做出來的,能夠根據主題生成不能的古詩,而且古詩的品質還比較高。于是,我也嘗試在自己的機器上也跑一個能夠寫古詩的模型,後來我找到的是一個基于rnn的模型。rnn循環神經網絡(recurrent neural networks),是非常常用的深度學習模型之一。我基于一個外部的demo,進行一些調整後跑起一個能夠學習古詩和寫古詩的比較簡單的程式。

執行寫詩(讓它寫了十首):

抑滴留居潋罅斜,二川還羨五侯家。古劉稱士身相染,桃李栽林欲稱家。回首二毛相喘日,萬當仙性盡甘無。如何羽馬嘶來淚,不信紅峰一寸西。

廢寺松陰月似空,垂楊風起晚光催。烏心不把嫌香徑,出定滄洲幾好清。蘭逐白頭鄰斧蝶,蒼蒼歸路自清埃。漁樵若欲斜陽羨,桂苑西河碧朔來。

遙天花落甚巫山,鳳珮飛馳不騁莊。翠初才象飲毫勢,上月朱爐一重牛。香催戍渚同虛客,石勢填樓取蕊紅。佳句舊清箱畔意,剪顔相激菊花繁。

江上蕭條第一取,名長經起月還遊。數尺溫臯雲戰遠,放船鄉鬼蘸雲多。相逢檻上西風動,莫聽風煙認釣魚。堤費禽雛應昨夢,去朝從此滿玄塵。

避命抛醺背暮時,見川誰哭夢知年。卻随筵裡腥消極,不遇嘉唐兩帶春。大歲秘魔窺石稅,鶴成應聽白雲中。朝浮到岸鸱巇恨,不向青青聽徑長。

楚田馀絕宇氤氲,細雨洲頭萬裡涼。百葉長看如不盡,水東春夜足殘峰。湖頭風浪斜暾鼓,北阙别罹初裡村。山在四天三顧客,辘轳争養抵丹墀。

九日重門攜手時,吟疑須渴辭金香。釣來猶繞結茶酒,衣上敬亭甯強燒。自明不肯疑恩日,琴館寒霖急暮霜。劃口濡于孤姹末,出謝空卿寄銀機。蓮龛不足厭絲屦,華騎敷砧出釣矶。

為到席中逢舊木,容華道路不能休。時閑客後多時石,暗水天邊暖人說。風弄霜花嗥明鏡,犀成磨逐乍牽腸。何勞相聽真行侍,石石班場古政蹄。

聽巾邑外見朱蘭,雜時臨廂北滿香。門外玉壇花府古,香牌風出即升登。陵橋翠黛銷仙妙,曉接紅樓疊影聞。敢把苦謠金字表,應從科劍獨頻行。

昨日榮枯桃李慶,紫骝堅黠自何侵。險知河在皆降月,漢縣煙波白發來。仍省封身明月閣,不知吹水洽誰非。更拟慚送風痕去,隻怕鲸雛是後仙。

另外,我抽取其中一些個人認為寫得比較好的詩句(以前跑出來的,不在上圖中):

該模型比較簡單,寫詩的水準不如最前面我介紹的美國研究者demo,但是,所采用的基本方法應該是類似的,隻是他做的更為複雜。

另外,這是一個通用模型,可以學習不同的内容(古詩、現代詩、宋詞或者英文詩等),就可以生成對應的結果。

七、深度學習的入門學習體會

人工智能和深度學習技術并不神秘,更像是一個新型的工具,通過喂資料給它,然後,它能發現這些資料背後的規律,并為我們所用。

數學基礎比較重要,這樣有助于了解模型背後的數學原理,不過,從純應用角度來說,并不一定需要完全掌握數學,也可以提前開始做一些嘗試和學習。

我深深地感到計算資源非常缺乏,每次調整程式的參數或訓練資料後,跑完一次訓練集經常要很多個小時,部分場景不跑多一些訓練集資料,看不出差别,例如寫詩的案例。個人感覺,這個是制約ai發展的重要問題,它直接讓程式的“調試”效率非常低下。

中文文檔比較少,英文文檔也不多,開源社群一直在快速更新,文檔的内容過時也比較快。是以,入門學習時遇到的問題會比較多,并且缺乏成型的文檔。

八、小結

我不知道人工智能的時代是否真的會來臨,也不知道它将要走向何方,但是,毫無疑問,它是一種全新的技術思維模式。更好的探索和學習這種新技術,然後在業務應用場景尋求結合點,最終達到幫助我們的業務獲得更好的成果,一直以來,就是我們工程師的核心宗旨。另一方面,對發展有重大推動作用的新技術,通常會快速的發展并且走向普及,就如同我們的程式設計一樣,是以,人人都可以做深度學習應用,并非隻是一句噱頭。

參考文檔:

<a href="http://www.tensorfly.cn/" target="_blank">http://www.tensorfly.cn/</a>

<a href="https://www.tensorflow.org/" target="_blank">https://www.tensorflow.org/</a>

繼續閱讀