當前位置:
首頁 > 最新 > 人人都可以做深度學習應用:入門篇

人人都可以做深度學習應用:入門篇

更多騰訊海量技術文章,請關注云加社區:https://cloud.tencent.com/developer/column

作者:CSDN技術頭條

一、人工智慧和新科技革命

2017年圍棋界發生了一件比較重要事,Master(Alphago)以60連勝橫掃天下,擊敗各路世界冠軍,人工智慧以氣勢如虹的姿態出現在我們人類的面前。圍棋曾經一度被稱為「人類智慧的堡壘」,如今,這座堡壘也隨之成為過去。從2016年三月份AlphaGo擊敗李世石開始,AI全面進入我們大眾的視野,對於它的討論變得更為火熱起來,整個業界普遍認為,它很可能帶來下一次科技革命,並且,在未來可預見的10多年裡,深刻得改變我們的生活。

其實,AI除了可以做我們熟知的人臉、語音等識別之外,它可以做蠻多有趣的事情。

例如,讓AI學習大量古詩之後寫古詩,並且可以寫出質量非常不錯的古詩。

又或者,將兩部設計造型不同的汽車進行融合,形成全新一種設計風格的新汽車造型。

還有,之前大家在朋友圈裡可能看過的,將相片轉換成對應的藝術風格的畫作。

當前,人工智慧已經在圖像、語音等多個領域的技術上,取得了全面的突破。與此同時,另外一個問題隨之而來,如果這一輪的AI浪潮真的將會掀起新的科技革命,那麼在可預見的未來,我們整個互聯網都將發生翻天覆地的變化,深刻影響我們的生活。那麼作為普通業務開發工程師的我,又應該以何種態度和方式應對這場時代洪流的衝擊呢?

在回答這個問題之前,我們先一起看看上一輪由計算機信息技術引領的科技革命中,過去30多年中國程序員的角色變化:

通過上圖可以簡總結:編程技術在不斷地發展並且走向普及,從最開始掌握在科學家和專家學者手中的技能,逐漸發展為一門大眾技能。換而言之,我們公司內很多資深的工程師,如果帶著今天對編程和計算機的理解和理念回到1980年,那麼他無疑就是那個時代的計算機專家。

如果這一輪AI浪潮真的會帶來新的一輪科技革命,那麼我們相信,它也會遵循類似的發展軌跡,逐步發展和走向普及。如果基於這個理解,或許,我們可以通過積極學習,爭取成為第一代AI工程師。

二、深度學習技術

這一輪AI的技術突破,主要源於深度學習技術,而關於AI和深度學習的發展歷史我們這裡不重複講述,可自行查閱。我用了一個多月的業務時間,去了解和學習了深度學習技術,在這裡,我嘗試以一名業務開發工程師的視角,以盡量容易讓大家理解的方式一起探討下深度學習的原理,儘管,受限於我個人的技術水平和掌握程度,未必完全準確。

1. 人的智能和神經元

人類智能最重要的部分是大腦,大腦雖然複雜,它的組成單元卻是相對簡單的,大腦皮層以及整個神經系統,是由神經元細胞組成的。而一個神經元細胞,由樹突和軸突組成,它們分別代表輸入和輸出。連在細胞膜上的分叉結構叫樹突,是輸入,那根長長的「尾巴」叫軸突,是輸出。神經元輸出的有電信號和化學信號,最主要的是沿著軸突細胞膜表面傳播的一個電脈衝。忽略掉各種細節,神經元,就是一個積累了足夠的輸入,就產生一次輸出(興奮)的相對簡單的裝置。

樹突和軸突都有大量的分支,軸突的末端通常連接到其他細胞的樹突上,連接點上是一個叫「突觸」的結構。一個神經元的輸出通過突觸傳遞給成千上萬個下游的神經元,神經元可以調整突觸的結合強度,並且,有的突觸是促進下游細胞的興奮,有的是則是抑制。一個神經元有成千上萬個上游神經元,積累它們的輸入,產生輸出。

人腦有1000億個神經元,1000萬億個突觸,它們組成人腦中龐大的神經網路,最終產生的結果即是人的智能。

2. 人工神經元和神經網路

一個神經元的結構相對來說是比較簡單的,於是,科學家們就思考,我們的AI是否可以從中獲得借鑒?神經元接受激勵,輸出一個響應的方式,同計算機中的輸入輸出非常類似,看起來簡直就是量身定做的,剛好可以用一個函數來模擬。

通過借鑒和參考神經元的機制,科學家們模擬出了人工神經元和人工神經網路。當然,通過上述這個抽象的描述和圖,比較難讓大家理解它的機制和原理。我們以「房屋價格測算」作為例子,一起來看看:

一套房子的價格,會受到很多因素的影響,例如地段、朝向、房齡、面積、銀行利率等等,這些因素如果細分,可能會有幾十個。一般在深度學習模型里,這些影響結果的因素我們稱之為特徵。我們先假設一種極端的場景,例如影響價格的特徵只有一種,就是房子面積。於是我們收集一批相關的數據,例如,50平米50萬、93平米95萬等一系列樣本數據,如果將這些樣本數據放到而為坐標里看,則如下圖:

然後,正如我們前面所說的,我們嘗試用一個「函數」去擬合這個輸入(面積x)和輸出(價格y),簡而言之,我們就是要通過一條直線或者曲線將這些點「擬合」起來。

假設情況也比較極端,這些點剛好可以用一條「直線」擬合(真實情況通常不會是直線),如下圖:

那麼我們的函數是一個一次元方程f(x) = ax +b,當然,如果是曲線的話,我們得到的將是多次元方程。我們獲得這個f(x) = ax +b的函數之後,接下來就可以做房價「預測」,例如,我們可以計算一個我們從未看見的面積案例81.5平方米,它究竟是多少錢?

這個新的樣本案例,可以通過直線找到對應的點(黃色的點),如圖下:

粗略的理解,上面就是AI的概括性的運作方式。這一切似乎顯得過於簡單了?當然不會,因為,我們前面提到,影響房價其實遠不止一個特徵,而是有幾十個,這樣問題就比較複雜了,接下來,這裡則要繼續介紹深度學習模型的訓練方式。這部分內容相對複雜一點,我盡量以業務工程師的視角來做一個粗略而簡單的闡述。

3. 深度學習模型的訓練方式

當有好幾十個特徵共同影響價格的時候,自然就會涉及權重分配的問題,例如有一些對房價是主要正權重的,例如地段、面積等,也有一些是負權重的,例如房齡等。

(1)初始化權重計算

那麼,第一個步其實是給這些特徵加一個權重值,但是,最開始我們根本不知道這些權重值是多少?怎麼辦呢?不管那麼多了,先給它們隨機賦值吧。隨機賦值,最終計算出來的估算房價肯定是不準確的,例如,它可能將價值100萬的房子,計算成了10萬。

(2)損失函數

因為現在模型的估值和實際估值差距比較大,於是,我們需要引入一個評估「不準確」程度的衡量角色,也就是損失(loss)函數,它是衡量模型估算值和真實值差距的標準,損失函數越小,則模型的估算值和真實值的察覺越小,而我們的根本目的,就是降低這個損失函數。讓剛剛的房子特徵的模型估算值,逼近100萬的估算結果。

(3)模型調整

通過梯度下降和反向傳播,計算出朝著降低損失函數的方向調整權重參數。舉一個不恰當的比喻,我們給面積增加一些權重,然後給房子朝向減少一些權重(實際計算方式,並非針對單個個例特徵的調整),然後損失函數就變小了。

(4)循環迭代

調整了模型的權重之後,就可以又重新取一批新的樣本數據,重複前面的步驟,經過幾十萬次甚至更多的訓練次數,最終估算模型的估算值逼近了真實值結果,這個模型的則是我們要的「函數」。

為了讓大家更容易理解和直觀,採用的例子比較粗略,並且講述深度學習模型的訓練過程,中間省略了比較多的細節。講完了原理,那麼我們就開始講講如何學習和搭建demo。

三、深度學習環境搭建

在2個月前,人工智慧對我來說,只是一個高大上的概念。但是,經過一個多月的業餘時間的認真學習,我發現還是能夠學到一些東西,並且跑一些demo和應用出來的。

1. 學習的提前準備

(1)部分數學內容的複習,高中數學、概率、線性代數等部分內容。(累計花費了10個小時,挑了關鍵的點看了下,其實還是不太夠,只能讓自己看公式的時候,相對沒有那麼懵)

(2)Python基礎語法學習。(花費了3個小時左右,我以前從未寫過Python,因為後面Google的TensorFlow框架的使用是基於Python的)

(3)Google的TensorFlow深度學習開源框架。(花費了10多個小時去看)

數學基礎好或者前期先不關注原理的同學,數學部分不看也可以開始做,全憑個人選擇。

2. Google的TensorFlow開源深度學習框架

深度學習框架,我們可以粗略的理解為是一個「數學函數」集合和AI訓練學習的執行框架。通過它,我們能夠更好的將AI的模型運行和維護起來。

深度學習的框架有各種各樣的版本(Caffe、Torch、Theano等等),我只接觸了Google的TensorFlow,因此,後面的內容都是基於TensorFlow展開的,它的詳細介紹這裡不展開講述,建議直接進入官網查看。非常令人慶幸的是TensorFlow比較早就有中文社區了,儘管裡面的內容有一點老,搭建環境方面有一些坑,但是已經屬於為數不多的中文文檔了,大家且看且珍惜。

TensorFlow的中文社區:

TensorFlow的英文社區:

https://www.tensorflow.org/

3. TensorFlow環境搭建

環境搭建本身並不複雜,主要解決相關的依賴。但是,基礎庫的依賴可以帶來很多問題,因此,建議盡量一步到位,會簡單很多。

(1)操作系統

我搭建環境使用的機器是騰訊雲上的機器,軟體環境如下:

操作系統:CentOS 7.2 64位(GCC 4.8.5)

因為這個框架依賴於python2.7和glibc 2.17。比較舊的版本的CentOS一般都是python2.6以及版本比較低的glibc,會產生比較的多基礎庫依賴問題。而且,glibc作為Linux的底層庫,牽一髮動全身,直接對它升級是比較複雜,很可能會帶來更多的環境異常問題。

(2)軟體環境

我目前安裝的Python版本是python-2.7.5,建議可以採用yum install python的方式安裝相關的原來軟體。然後,再安裝 python內的組件包管理器pip,安裝好pip之後,接下來的其他軟體的安裝就相對比較簡單了。

例如安裝TensorFlow,可通過如下一句命令完成(它會自動幫忙解決一些庫依賴問題):

pip install -U tensorflow

這裡需要特別注意的是,不要按照TensorFlow的中文社區的指引去安裝,因為它會安裝一個非常老的版本(0.5.0),用這個版本跑很多demo都會遇到問題的。而實際上,目前通過上述提供的命令安裝,是tensorflow (1.0.0)的版本了。

Python(2.7.5)下的其他需要安裝的關鍵組件:

tensorflow (0.12.1),深度學習的核心框架

image (1.5.5),圖像處理相關,部分例子會用到

PIL (1.1.7),圖像處理相關,部分例子會用到

除此之後,當然還有另外的一些依賴組件,通過pip list命令可以查看我們安裝的python組件:

appdirs (1.4.0)

backports.ssl-match-hostname (3.4.0.2)

chardet (2.2.1)

configobj (4.7.2)

decorator (3.4.0)

Django (1.10.4)

funcsigs (1.0.2)

image (1.5.5)

iniparse (0.4)

kitchen (1.1.1)

langtable (0.0.31)

mock (2.0.0)

numpy (1.12.0)

packaging (16.8)

pbr (1.10.0)

perf (0.1)

PIL (1.1.7)

Pillow (3.4.2)

pip (9.0.1)

protobuf (3.2.0)

pycurl (7.19.0)

pygobject (3.14.0)

pygpgme (0.3)

pyliblzma (0.5.3)

pyparsing (2.1.10)

python-augeas (0.5.0)

python-dmidecode (3.10.13)

pyudev (0.15)

pyxattr (0.5.1)

setuptools (34.2.0)

six (1.10.0)

slip (0.4.0)

slip.dbus (0.4.0)

tensorflow (1.0.0)

urlgrabber (3.10)

wheel (0.29.0)

yum-langpacks (0.4.2)

yum-metadata-parser (1.1.4)

按照上述提供的來搭建系統,可以規避不少的環境問題。

搭建環境的過程中,我遇到不少問題。例如:在跑官方的例子時的某個報錯,AttributeError: "module" object has no attribute "gfile",就是因為安裝的TensorFlow的版本比較老,缺少gfile模塊導致的。而且,還有各種各樣的。(不要問我是怎麼知道的,說多了都是淚啊~)

更詳細的安裝說明:

https://www.tensorflow.org/install/install_linux

(3)TensorFlow環境測試運行

測試是否安裝成功,可以採用官方的提供的一個短小的例子,demo生成了一些三維數據, 然後用一個平面擬合它們(官網的例子採用的初始化變數的函數是initialize_all_variables,該函數在新版本里已經被廢棄了):

#!/usr/bin/python

#coding=utf-8

import tensorflow as tf

import numpy as np

# 使用 NumPy 生成假數據(phony data), 總共 100 個點.

y_data = np.dot([0.100, 0.200], x_data) + 0.300

# 構造一個線性模型

#

b = tf.Variable(tf.zeros([1]))

W = tf.Variable(tf.random_uniform([1, 2], -1.0, 1.0))

y = tf.matmul(W, x_data) + b

# 最小化方差

loss = tf.reduce_mean(tf.square(y - y_data))

optimizer = tf.train.GradientDescentOptimizer(0.5)

train = optimizer.minimize(loss)

# 初始化變數,舊函數(initialize_all_variables)已經被廢棄,替換為新函數

init = tf.global_variables_initializer()

# 啟動圖 (graph)

sess = tf.Session()

sess.run(init)

# 擬合平面

for step in xrange(0, 201):

sess.run(train)

if step % 20 == 0:

print step, sess.run(W), sess.run(b)

# 得到最佳擬合結果 W: [[0.100 0.200]], b: [0.300]

運行的結果類似如下:

經過200次的訓練,模型的參數逐漸逼近最佳擬合的結果(W: [[0.100 0.200]], b: [0.300]),另外,我們也可以從代碼的「風格」中,了解到框架樣本訓練的基本運行方式。雖然,官方的教程後續會涉及越來越多更複雜的例子,但從整體上看,也是類似的模式。

步驟劃分:

準備數據:獲得有標籤的樣本數據(帶標籤的訓練數據稱為有監督學習);

設置模型:先構建好需要使用的訓練模型,可供選擇的機器學習方法其實也挺多的,換而言之就是一堆數學函數的集合;

損失函數和優化方式:衡量模型計算結果和真實標籤值的差距;

真實訓練運算:訓練之前構造好的模型,讓程序通過循環訓練和學習,獲得最終我們需要的結果「參數」;

驗證結果:採用之前模型沒有訓練過的測試集數據,去驗證模型的準確率。

其中,TensorFlow為了基於python實現高效的數學計算,通常會使用到一些基礎的函數庫,例如Numpy(採用外部底層語言實現),但是,從外部計算切回到python也是存在開銷的,尤其是在幾萬幾十萬次的訓練過程。因此,Tensorflow不單獨地運行單一的函數計算,而是先用圖描述一系列可交互的計算操作流程,然後全部一次性提交到外部運行(在其他機器學習的庫里,也是類似的實現)。所以,上述流程圖中,藍色部分都只是設置了「計算操作流程」,而綠色部分開始才是真正的提交數據給到底層庫進行實際運算,而且,每次訓練一般是批量執行一批數據的。

四、經典入門demo:識別手寫數字(MNIST)

常規的編程入門有「Hello world」程序,而深度學習的入門程序則是MNIST,一個識別28*28像素的圖片中的手寫數字的程序。

MNIST的數據和官網:

http://yann.lecun.com/exdb/mnist/

深度學習的內容,其背後會涉及比較多的數學原理,作為一個初學者,受限於我個人的數學和技術水平,也許並不足以準確講述相關的數學原理,因此,本文會更多的關注「應用層面」,不對背後的數學原理進行展開,感謝諒解。

1. 載入數據

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

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

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

Demo中的關鍵代碼(讀取並且載入數據到數組對象中,方便後面使用):

2. 構建模型

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則是基於前面的數據進行計算得到。

3. 損失函數和優化設置

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

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

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

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

對應的關鍵代碼如下:

備註內容:

交叉熵:http://colah.github.io/posts/2015-09-Visual-Information/

反向傳播:http://colah.github.io/posts/2015-08-Backprop/

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

例如表示數字3的標籤值:

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

4. 訓練運算和模型準確度測試

通過前面的實現,我們已經設置好了整個模型的計算「流程圖」,它們都成為TensorFlow框架的一部分。於是,我們就可以啟動我們的訓練程序,下面的代碼的含義是,循環訓練我們的模型500次,每次批量取50個訓練樣本。

其訓練過程,其實就是TensorFlow框架的啟動訓練過程,在這個過程中,python批量地將數據交給底層庫進行處理。

我在官方的demo里追加了兩行代碼,每隔50次則額外計算一次當前模型的識別準確率。它並非必要的代碼,僅僅用於方便觀察整個模型的識別準確率逐步變化的過程。

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

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

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

5. 實時查看參數的數值的方法

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

Tensor("Equal:0", shape=(?,), dtype=bool)

既然它是佔位符,那麼我們就必須喂一些數據給它,它才能將真實內容展示出來。因此,正確的方法是,在列印時通常需要加上當前的輸入數據給它。

例如,查看y的概率數據:

print(sess.run(y, feed_dict=))

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

print(W.eval())

總的來說,92%的識別準確率是比較令人失望,因此,官方的MNIST其實也有多種模型的不同版本,其中比較適合圖片處理的CNN(卷積神經網路)的版本,可以獲得99%以上的準確率,當然,它的執行耗時也是比較長的。

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

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

分享在微雲上的數據和源碼:

http://url.cn/44aZOpP

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

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

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

1. 數據準備

我將一個現網的電影票活動的用戶參與數據,包括點擊過哪些按鈕、手機平台、IP地址、參與時間等信息抓取了出來。其實這些數據當中是隱含了用戶的身份信息的,例如,某些禮包的必須是超級會員身份才能領取,如果這個按鈕用戶點擊領取成功,則可以證明該用戶的身份肯定是超級會員身份。當然,我只是將這些不知道相不相關的數據特徵直觀的整理出來,作為我們的樣本數據,然後對應的標籤為超級會員身份。

用於訓練的樣本數據格式如下:

第一列是QQ號碼,只做認知標識的,第二列表示是否超級會員身份,作為訓練的標籤值,後面的就是IP地址,平台標誌位以及參與活動的參與記錄(0是未成功參與,1表示成功參與)。則獲得一個擁有11個特徵的數組(經過一些轉化和映射,將特別大的數變小):

對應的是否是超級數據格式如下,作為監督學習的標籤:

超級會員:[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值問題一度深深地困擾著我(往事不堪回首-__-!!),特別放到這裡,避免入門的同學踩坑。

2. 執行結果

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

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

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

六、其他模型

1. CIFAR-10識別圖片分類的demo(官方)

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

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

更詳細的介紹內容:

http://www.cs.toronto.edu/~kriz/cifar.html

http://tensorfly.cn/tfdoc/tutorials/deep_cnn.html

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

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

cifar10_train.py用於訓練:

cifar10_eval.py用於檢驗結果:

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

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

源碼下載:http://url.cn/44mRzBh

微雲盤裡,不含訓練集和測試集的圖片數據,但是,程序如果檢測到這些圖片不存在,會自行下載:

2. 是否大於5歲的測試demo

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

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

我隨機構造一個只有2個特徵緯度的樣本數據,[year, 1],其中year隨機取值0-10,數字1是放進去作為干擾。

如果year大於5歲,則標籤設置為:[0, 0, 1];

否則,標籤設置為:[0, 1, 0]。

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

微雲下載(源碼下載):

http://url.cn/44mKFNK

3. 基於RNN的古詩學習

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

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

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

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

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

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

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

楚田餘絕宇氤氳,細雨洲頭萬里涼。百葉長看如不盡,水東春夜足殘峰。湖頭風浪斜暾鼓,北闕別罹初里村。山在四天三顧客,轆轤爭養抵丹墀。

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

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

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

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

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

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

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

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

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

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

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

4. 中文文檔比較少,英文文檔也不多,開源社區一直在快速更新,文檔的內容過時也比較快。因此,入門學習時遇到的問題會比較多,並且缺乏成型的文檔。

八、小結

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


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 雲加社區 的精彩文章:

一個基於 Docker 的負載均衡實例
多層感知器神經網路速成課

TAG:雲加社區 |