深度学习入门与快速实践
深度學習介紹
以深度學習為主要力量的AI浪潮徐徐展開
我們正處在一個巨變的時代,人工智能已經成為了這個時代的主題。人工智能成為第四次工業革命的核心驅動力,并將像機械化、電氣化、信息化一樣,最終會滲透到每一個行業的每一個角落。人工智能將重塑全球制造業競爭新格局、引爆新一輪產業革命。以1956年的達特茅斯會議(Dartmouth Conference)為起點,人工智能經過了60余年的發展,經歷了邏輯推理理論和專家系統的兩次繁榮,也經歷過隨之而來的兩次寒冬。在最近的十年間,人工智能技術再次出現了前所未有的爆發性增長和繁榮期。機器學習技術尤其是深度學習技術的興起是本次人工智能繁榮大潮的重要推動力。
近年來深度學習發展神速,在語音、圖像、自然語言處理等技術上取得了前所未有的突破。本輪人工智能的快速發展離不開三大關鍵要素:算力、數據和算法。
首先,大規模、高性能的云端計算硬件集群是人工智能發展的強勁引擎。目前深度學習算法模型結構復雜,規模大、參數多;模型的開發門檻很高。面對這些挑戰,高性能的硬件,如GPU/FPGA/ASIC等,尤其是面向人工智能應用的專用芯片的發展,讓機器學習的訓練速度數倍提升。
第二,數據是推動人工智能發展的燃料,機器學習技術需要大量標注數據來作訓練模型,在海量數據樣本的基礎上挖掘信息,得到有用的知識,從而做出決策。移動互聯網、IoT物聯網的發展,不止讓手機這樣的移動設備接入互聯網,每一個音箱、每一個電視、每一個冰箱都可以接入云,獲得人工智能的能力和服務。用戶通過這些設備感受到AI帶來的便利,實際上背后是云上提供的服務。云是AI的container,如果將AI比作電的話,云就是核電站,為各行各業提供源源不斷的智能核動力。
第三,是不斷推陳出新的人工智能算法突破,從卷積神經網絡(Convolutional neural network,簡稱CNN)到遞歸神經網絡(Recurrent neural network,簡稱RNN),從生成式對抗網絡(Generative adversarial networks,簡稱GANs)到遷移學習(Transfer learning)。每一次新算法的提出或改進,帶來了應用效果的大幅提升。人工智能技術在語音、圖像、自然語言等眾多領域的使用,推動這次人工智能大潮的持續發展。[1]
深度學習基本概念
機器善于規則、人類善于感知
機器善于計算。只要問題能夠被一系列的數學規則形式化描述,那么通過編程的方法就可以進行運算,例如求解線性方程組,求解行星的運動軌跡。如果這些讓人類手算的話會非常痛苦而且容易出錯。為了盡量模擬人類的行為,人們開始研究機器學習。然而傳統人工智能依賴科學家輸入的規則模型,它只有在解決一些規則比較清楚的問題時才比較有效,比如擊敗卡斯帕羅夫的“深藍”就是這樣一種“人工智能”。但是對于直覺性問題,例如分辨圖片中的是貓還是狗,從不同的口音中聽出對方的實際想表達的意思,通過語氣來表達自己的情緒,這些對于人來說非常容易的事情,對于傳統人工智能來說就非常困難。因為這種認知類問題只有一個模糊的概念,沒有清楚簡單的規則。
[2]
人工智能的目標就是去解決人容易執行,但是很難形式化描述的任務。相對于傳統的機器學習方法,深度學習方法是用來解決直覺問題的好方法。深度神經網絡大大優化了機器學習的效果,使人工智能技術獲得了突破性進展。在此基礎上,圖像識別、語音識別、機器翻譯等都取得了長足進步。語音輸入比打字快得多,機器翻譯讓我們基本可以看懂一篇外文資訊,圖像識別則早已可以憑借一張少年時期的照片就在一堆成人照片中準確找到這個人,甚至可以把很模糊的照片恢復成清晰且準確的照片。神經網絡的特點就是它不需要人類提前告知規則,它會自己從海量的基礎數據里識別模式(規則)。[3]
深度學習與神經網絡
深度學習技術是構建在傳統神經網絡技術基礎之上的。可以粗略的認為當傳統神經網絡夠“深”的時候就是深度學習了。圖中的網絡的層次比較少,也就是這個網絡比較淺,可以想象當網絡層次達到幾十層上百層的時候,那么這就是一個深層次網絡了。用深層次網絡解決問題就是深度學習技術。
人工神經網絡是受到生物神經網絡的啟發而發明的。生物神經網絡是由無數個神經元相互連接進而組成的一個復雜網絡。人工神經網絡模擬了這個過程,在邏輯上我們模擬出很多的神經元,然后讓這些神經元聯系成網絡。在深度學習領域,通常用神經網絡這個詞來指代人工神經網絡。這里要強調一下:人工神經網絡和生物神經網絡僅僅是啟發關系,內部原理是不同的,因為我們人類目前還沒有把生物神經網絡的內在全部機制完全探明。
復雜的神經系統是由簡單的神經元相連組成。神經元就像圖中這樣,枝枝杈杈很多,看上去一邊比較粗大一邊比較纖細。粗大的這邊的結構叫做軸突,纖細這邊叫做樹突。信號就是由一個細胞的軸突通過突觸將信號傳遞給另一個細胞的樹突。當無數個這樣的神經元聯系在一起的時候就構成了神經網絡。
神經元
生物神經網絡的最小單元是神經元,而人工神經網絡的單元是感知機。生物的神經元是一端接受化學信號,經過加工從另一端釋放出來。而感知機是一端接受一個向量作為輸入,經過一定加工,另一端釋放出去一個標量。
而一個神經元內部事實上經歷了兩個運算過程,線性運算和非線性運算。這兩個運算的復合就構成了一個神經元的運算過程。線性運算就是最簡單的加權求和,而非線性運算所使用的函數也被稱作激活函數或者激勵函數。關于激活函數在后面還會繼續討論。注意,在線性運算中的向量 w 和 偏置 b 就是該網絡的參數。其中,w 表示權重, b 表示偏置。
輸入層、輸出層、隱藏層、全連接網絡
通常一個深度學習網絡就是一個多層的神經網絡,那么很自然就會想到,多深算“深”呢?這個度量標準目前還沒有定論,有的學者認為超過3層的為深層網絡,有的學者認為超過5層為深層網絡。無論多深,網絡所具有的基本結構的是一致的。輸入數據的那層別稱作輸入層,輸出預測結果的一層(也就是網絡的最后一層)被稱作輸出層,而夾在兩層之間的層次被稱作隱藏層。輸入層通常不算做網絡的一部分。如果網絡中的每一個神經元都和上層的所有神經元相連也和下一層的所有神經元相連,那么這樣的網絡就被稱作全連接網絡。有時,我們也把全連接網絡認為是一種經典的深度神經網絡(Deep Neural Networks,DNN)。通常情況下,談及DNN就是指全連接網絡(除了DNN網絡還有CNN、RNN等網絡結構)。下圖就是一個4層的全連接網絡。
神經網絡運算三部曲
神經網絡運算由三步過程組成:正向傳播、反向傳播和梯度下降。
正向傳播的主要目的:計算預測值。反向傳播的主要目的:得到參數的偏導數(也就是 w 和 b 的偏導數)。梯度下降的主要目的:更新參數,進而將損失函數的函數值盡量降低。
正向傳播
主要過程為:采集客觀數據 x。將 x 輸入網絡,從輸入層開始一層一層的計算到輸出層,這一路下來會得到預測值 y^\hat{y}y^? 。
反向傳播
主要過程為:在正向傳播得到預測值 y^\hat{y}y^? 后,用該值與采集回來的標簽值 y 做比較,進而度量預測值的準確性,如果 y^\hat{y}y^? 與 y 的差距很小,那么說明預測的較為精準,如果 y^\hat{y}y^? 與 y 的差距很大,說明預測的不準確。這個用來度量 y^\hat{y}y^? 與 y 的差距的函數稱為損失函數。關于損失函數,后面會繼續討論。由損失函數做為起點,沿著網絡反向傳播求得每個參數(w和b)的偏導數的過程,稱為反向傳播過程。
梯度下降
主要過程為:在得到了所有參數的偏導數后,也就得到了梯度,沿著梯度反方向前進一小步,也就是向損失函數最小值的方向更靠近了一步。這個最小化的過程也被稱作優化。具體的優化策略有很多種,這里不再展開詳述。
激活函數
一個神經元中包含兩個運算過程:線性運算和非線性運算。線性運算就是固定的(寫死的)的加權求和,而非線性運算過程是用戶可配置的。常用的線性激活函數有三種:sigmoid、tanh 和 relu。下面分別介紹一下這三種激活激活函數。
Sigmoid 函數主要作用就是把某實數映射到區間(0,1)內,觀察圖像會發現Sigmoid函數可以很好的完成這個工作,當值較大時,趨近于1,當值較小時,趨近于0。sigmoid早期最流行的激活函數,激活函數的代名詞
Tanh 函數 也稱為雙切正切函數,范圍在-1到1之間,隨著x的增大或減小,函數趨于平緩,導函數趨近于0。sigmoid 函數 和 tanh 函數都可以用來做二分類問題的輸出層的激活函數。可以用做網絡內部神經元的激活函數。
Relu 函數。由于 sigmoid 函數 和 tanh 函數的都有飽和區(導數很小區域 0 的位置成為飽和區,觀察函數圖像可以很容易發現,當 x 大于 5 函數的導數就趨于 0 了)。所以,在反向傳播的時候很容發生偏導數非常小的現象,那么進而會導致在梯度下降的時候,參數(w和b)更新速度過于緩慢的現象。為了克服這種現象,于是有了 Relu 函數。事實上,relu 函數是目前使用最多的激活函數。如果開發者不知道該配置什么樣的激活函數比較好,那么就可以嘗試配置 relu 函數。
標準 Relu 函數也不是完美的,可以觀察到 Relu 在拐點處不可導,并且在 x 小于 0 的時候函數值為0,這樣在反向傳播的時候,就會把信息丟失。于是就又有了 Relu 的變種:LeakyRelu、參數化Relu、隨機化Relu等,這里不再展開,但是目前在工業上用的最多的還是標準 Relu。
損失函數
需要定義一個損失函數(Loss function or Error function)用于對參數 w 和 b 進行優化,而損失函數的選擇需要具體問題具體分析,在不同問題場景下采用不同的函數。常見的損失函數為均方誤差損失函數和交叉熵損失函數。
均方誤差損失函數:
回歸主要解決的是對具體數值的預測,解決回歸問題的神經網絡一般只有一個輸出節點,這個節點的輸出值就是預測值。對于回歸問題,最常用的損失函數是均方誤差(MSE),定義為:
交叉熵損失函數:
交叉熵是一個信息論中的概念,它原來是用來估算平均編碼長度的。給定兩個概率分布 p 和 q,通過 q 來表示 p 的交叉熵為:
注意,交叉熵刻畫的是兩個概率分布之間的距離,或可以說它刻畫的是通過概率分布q來表達概率分布p的困難程度,p代表正確答案,q代表的是預測值,交叉熵越小,兩個概率的分布約接近。[4]
通常交叉熵損失函數是和激活函數 softmax 共同使用(也就是將 softmax 設置為輸出層的激活函數)。關于 softmax 函數相關內容這里就不再展開。
PaddlePaddle 工具介紹
做深度學習是一個復雜的過程。理論上,開發者可以自己使用 Python 的 numpy 庫,自己從頭開始編寫網絡結構、正向傳播過程、反向傳播過程、梯度下降的優化過程、以及對GPU、FPGA、NPU的底層適配優化等工作。但是,自己來做這些事情實在工作量太過龐大,會把自己寶貴的時間浪費在底層代碼的編寫。作為開發者,首要任務是完成自己的業務邏輯,讓自己的公司的業務盡快上線盡早盡好的為客戶提供服務。所以,很多大廠和前輩編寫很多非常棒的深度學習框架,使得開發者能夠聚焦于自己的業務。目前主流的框架有 PaddlePaddle、TensorFlow、Pythorch 等,其中 PaddlePaddle 是一個易學、易用的開源深度學習框架,能夠讓開發者和企業安全、高效地實現自己的AI想法[5]。PaddlePaddle 是百度開發、開放、開源的。
使用深度學習實現房價預測----數據準備
數據集介紹
我們使用從UCI Housing Data Set(http://paddlemodels.bj.bcebos.com/uci_housing/housing.data)獲得的波士頓房價數據集進行模型的訓練和預測。下面的散點圖展示了使用模型對部分房屋價格進行的預測。其中,每個點的橫坐標表示同一類房屋真實價格的中位數,縱坐標表示線性回歸模型根據特征預測的結果,當二者值完全相等的時候就會落在虛線上。所以模型預測得越準確,則點離虛線越近。
這份數據集共506行,每行包含了波士頓郊區的一類房屋的相關信息及該類房屋價格的中位數。其各維屬性的意義如下:
| CRIM | 該鎮的人均犯罪率 | 連續值 |
| ZN | 占地面積超過25,000平方呎的住宅用地比例 | 連續值 |
| INDUS | 非零售商業用地比例 | 連續值 |
| CHAS | 是否鄰近 Charles River | 離散值,1=鄰近;0=不鄰近 |
| NOX | 一氧化氮濃度 | 連續值 |
| RM | 每棟房屋的平均客房數 | 連續值 |
| AGE | 1940年之前建成的自用單位比例 | 連續值 |
| DIS | 到波士頓5個就業中心的加權距離 | 連續值 |
| RAD | 到徑向公路的可達性指數 | 連續值 |
| TAX | 全值財產稅率 | 連續值 |
| PTRATIO | 學生與教師的比例 | 連續值 |
| B | 1000(BK - 0.63)^2,其中BK為黑人占比 | 連續值 |
| LSTAT | 低收入人群占比 | 連續值 |
| MEDV | 同類房屋價格的中位數 | 連續值 |
數據預處理
連續值與離散值
觀察一下數據,我們的第一個發現是:所有的13維屬性中,有12維的連續值和1維的離散值(CHAS)。離散值雖然也常使用類似0、1、2這樣的數字表示,但是其含義與連續值是不同的,因為這里的差值沒有實際意義。例如,我們用0、1、2來分別表示紅色、綠色和藍色的話,我們并不能因此說“藍色和紅色”比“綠色和紅色”的距離更遠。所以通常對一個有dd個可能取值的離散屬性,我們會將它們轉為dd個取值為0或1的二值屬性或者將每個可能取值映射為一個多維向量。不過就這里而言,因為CHAS本身就是一個二值屬性,就省去了這個麻煩。
屬性的歸一化
另外一個稍加觀察即可發現的事實是,各維屬性的取值范圍差別很大(例如下圖)。
例如,屬性B的取值范圍是[0.32, 396.90],而屬性NOX的取值范圍是[0.3850, 0.8170]。這里就要用到一個常見的操作-歸一化(normalization)了。歸一化的目標是把各位屬性的取值范圍放縮到差不多的區間,例如[-0.5,0.5]。這里我們使用一種很常見的操作方法:減掉均值,然后除以原取值范圍。
做歸一化至少有以下3個理由:
劃分訓練集與測試集
通常情況下,開發者在對數據集進行過必要的預處理之后,就需要講數據集分為兩部分:訓練集和測試集。
- 訓練集:用于調整模型的參數,即進行模型的訓練。
- 測試集:用來測試,查看模型效果,用來模擬如果模型上線后的效果。
那么有多少數據應該放入訓練集呢,又有多少數據應該放入測試集呢?分割數據的比例要考慮到兩個因素:更多的訓練數據會降低參數估計的方差,從而得到更可信的模型;而更多的測試數據會降低測試誤差的方差,從而得到更可信的測試誤差。在數據集的量不大的情況下,這個比例通常是7:3或者8:2。在這個例子中我們設置的分割比例為8:2。
在更復雜的模型訓練過程中,我們往往還會多使用一種數據集:驗證集。因為復雜的模型中常常還有一些超參數(Hyperparameter)需要調節,所以我們會嘗試多種超參數的組合來分別訓練多個模型,然后對比它們在驗證集上的表現選擇相對最好的一組超參數,最后才使用這組參數下訓練的模型在測試集上評估測試誤差。由于本章訓練的模型比較簡單,我們暫且忽略掉這個過程。
訓練誤差與測試誤差
此外,訓練誤差和測試誤差也是開發者必須了解的概念:
- 模型在訓練集上的誤差被稱為訓練誤差
- 模型在測試集上的誤差被稱為測試誤差
我們訓練模型的目的是為了通過從訓練數據中找到規律來預測未知的新數據,所以測試誤差是更能反映模型表現的指標。
使用深度學習實現房價預測----上代碼
代碼概覽
深度學習過程事實上可以劃分為兩個過程:訓練和預測(預測也稱作推理)。通常這兩個過程不是在同一臺機器上完成更不會在同一個進程中完成,但是,為了講解方便代碼簡便,我們下面的代碼是在同一個進程中順序執行。
在本例中,我們將代碼分為了3個部分:通用內容準備、訓練過程代碼、預測過程代碼
通用內容準備
我們通過 uci_housing 模塊引入了數據集合 UCI Housing Data Set
其中,在uci_housing模塊中封裝了:
數據下載的過程。下載數據保存在~/.cache/paddle/dataset/uci_housing/housing.data。
數據預處理的過程。
接下來我們定義了用于訓練的數據提供器。提供器每次讀入一個大小為BATCH_SIZE的數據批次。如果用戶希望加一些隨機性,它可以同時定義一個批次大小和一個緩存大小。這樣的話,每次數據提供器會從緩存中隨機讀取批次大小那么多的數據。
訓練程序的目的是定義一個訓練模型的網絡結構。對于線性回歸來講,它就是一個從輸入到輸出的簡單的全連接層。更加復雜的結構,比如卷積神經網絡,遞歸神經網絡這里不再展開。
main_program = fluid.default_main_program() startup_program = fluid.default_startup_program()這兩句的作用是獲得 default_main_program 和 default_startup_program 的 Python 級別的引用。default_startup_program 的主要作用是初始化參數,而 default_main_program 的主要作用是管理所有的變量和算子。作為初學者理解到這一層已經足夠,PaddlePaddle 內部的機制非常復雜,該框架對開發者僅僅暴露了很少的部分,復雜的機制都包裹在后臺。由于 PaddlePaddle 是開源的,有興趣深入了解其框架底層實現的開發者可以訪問其 github (https://github.com/PaddlePaddle/Paddle)。
cost = fluid.layers.square_error_cost(input=y_predict, label=y) avg_loss = fluid.layers.mean(cost)訓練程序必須返回平均損失作為第一個返回值,因為它會被后面反向傳播算法所用到。
這里使用了隨機梯度下降(SGD)作為優化器 ,learning_rate 是訓練的速度,與網絡的訓練收斂速度有關系。test_program=main_program.clone(for_test=True) 的作用是為后面邊訓練邊測試做準備(把環境拷貝了一份出來)
我們可以定義運算是發生在CPU還是GPU
# Plot data from paddle.utils.plot import Plotertrain_prompt = "Train cost" test_prompt = "Test cost" plot_prompt = Ploter(train_prompt, test_prompt)展現運算過程有兩種方式:1.打印 log 的方式 2.繪制圖像的方式。相比較來說,繪制圖像的方式更加直觀。上面的代碼就是為繪制圖像做準備。
訓練過程代碼
訓練需要有一個訓練程序和一些必要參數,并構建了一個獲取訓練過程中測試誤差的函數
PaddlePaddle 提供了讀取數據者發生器機制來讀取訓練數據。讀取數據者會一次提供多列數據,因此我們需要一個 Python 的 list 來定義讀取順序。我們構建一個循環來進行訓練,直到訓練結果足夠好或者循環次數足夠多。 如果訓練順利,可以把訓練參數保存到 params_dirname。
預測過程代碼
需要構建一個使用訓練好的參數來進行預測的程序,訓練好的參數位置在params_dirname
類似于訓練過程,預測器需要一個預測程序來做預測。我們可以稍加修改我們的訓練程序來把預測值包含進來。
通過fluid.io.load_inference_model,預測器會從params_dirname中讀取已經訓練好的模型,來對從未遇見過的數據進行預測。
完整代碼請參考:
https://github.com/PaddlePaddle/book/blob/develop/01.fit_a_line/train.py
更多 PaddlePaddle 示例請參考:
https://github.com/PaddlePaddle/book/
參考資料
[1]《PaddlePaddle深度學習實戰》 劉祥龍等著 2018
[2]《白話深度學習與TensorFlow》 高揚等著,萬娟 繪 2017
[3]《深度學習實戰》楊云等著 2017
[4] https://en.wikipedia.org/wiki/Cross_entropy
[5] http://www.paddlepaddle.org/zh
圖片部分來源于網絡,侵刪
總結
以上是生活随笔為你收集整理的深度学习入门与快速实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: “被提拔3个月,我离职了”:给想做管理的
- 下一篇: powershell操作excel