1. 程式人生 > 實用技巧 >Tensorflow2(一)深度學習基礎和tf.keras

Tensorflow2(一)深度學習基礎和tf.keras

程式碼和其他資料在 github

一、tf.keras概述

首先利用tf.keras實現一個簡單的線性迴歸,如 \(f(x) = ax + b\),其中 \(x\) 代表學歷,\(f(x)\) 代表收入,分別代表輸入特徵和輸出值。為了描述預測目標與真實值之間的整體誤差最小,需要定義一個損失函式,數學描述為\((f(x) - y)^2\),即預測值與真實值差值的平方的均值。優化的目標是求解引數 \(a,b\) 使其損失函式最小。

import tensorflow as tf
import pandas as pd
import matplotlib.pyplot as plt
data = pd.read_csv('../../data/Income1.csv')
plt.scatter(data.Education,data.Income)

輸出如下所示:

從輸出的散點圖可以看出,該資料中收入與受教育年限具有線性關係,可以用線性迴歸擬合。下面開始構建訓練模型。

x = data.Education
y = data.Income
model = tf.keras.Sequential() #順序模型,一層一層的搭建
model.add(tf.keras.layers.Dense(1,input_shape = (1,))) # 新增網路層

tf.keras.layers.Dense(1,input_shape = (1,)) 中的第一個引數1代表輸出的維度,因為是線性迴歸,不是二分類或者多分類,輸出只有一個節點,即1維。input_shape代表輸入,也是1維,這裡用元組的形式輸入。

model.summary() 

輸出引數的詳情如下:

Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 1)                 2         
=================================================================
Total params: 2
Trainable params: 2
Non-trainable params: 0
_________________________________________________________________

(None,1) 第一個維度代表樣本的個數,不需要考慮,第二個引數代表輸出的維度。Param代表引數的個數,這裡為2,即\(a,b\)。下面進行模型訓練和預測。

model.compile(optimizer='adam',loss = 'mse')# loss引數設為mse(Mean-Squared),代表均方方差
history = model.fit(x,y,epochs = 5000)
model.predict(x)
model.predict(pd.Series([20]))

二、多層感知機(multilayer perceptron,MLP)

2.1 隱藏層

多層感知機在單層神經網路的基礎上引入了一到多個隱藏層(hidden layer)。隱藏層位於輸入層和輸出層之間,如下圖所示。

2.2 啟用函式
  • relu函式

    ReLU(rectified linear unit)函式提供了一個很簡單的非線性變換。給定元素\(x\),該函式定義為

    \[\text{ReLU}(x) = \max(x, 0) \]

    可以看出,ReLU函式只保留正數元素,並將負數元素清零。如下圖所示。

  • sigmoid函式

    sigmoid函式可以將元素的值變換到0和1之間:

    \[\text{sigmoid}(x) = \frac{1}{1 + \exp(-x)} \]

    如下圖所示:

  • tanh函式

    tanh(雙曲正切)函式可以將元素的值變換到-1和1之間:

    \[\text{tanh}(x) = \frac{1 - \exp(-2x)}{1 + \exp(-2x)} \]

    我們接著繪製tanh函式。當輸入接近0時,tanh函式接近線性變換。雖然該函式的形狀和sigmoid函式的形狀很像,但tanh函式在座標系的原點上對稱。

例子程式碼如下:

import tensorflow as tf
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
data = pd.read_csv('../../data/Advertising.csv')
#data.head()
#plt.scatter(data.TV,data.sales)
#plt.scatter(data.radio,data.sales)
#plt.scatter(data.newspaper,data.sales)
x = data.iloc[:,1:-1] # 第一列到倒數第二列為特徵值
y = data.iloc[:,-1] # 最後一列為輸出
model = tf.keras.Sequential([tf.keras.layers.Dense(10,input_shape(3,),activation='relu'), tf.keras.layers.Dense(1)])
model.compile(optimizer = 'adam',loss = 'mse')
model.fit(x,y,epochs = 100)
test = data.iloc[:10,1:-1]
model.predict(test)

三、邏輯迴歸與交叉熵

線性迴歸預測的是一個連續的值,而邏輯迴歸卻只有“是”或“否”的回答。sigmoid函式是一個概率分佈函式,給定某個輸入,它將輸出為一個概率值 。如下圖所示。

交叉熵損失函式描述的是實際輸出(概率)與期望輸出(概率)的距離,也就是說交叉熵的值越小,兩個概率分佈越接近。假設概率分佈 $p$ 為期望輸出,$q$ 為實際輸出,$H(p,q)$ 為交叉熵,則 $H(p,q) = -\sum\limits_xp(x)\log q(x)$。 程式碼如下:
import tensorflow as tf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
data = pd.read_csv('../../data/credit-a.csv',header = None)
#預設第一行為表頭,因為從第一行開始就是資料,所以需要把第一行改為不是表頭
data.iloc[:,-1].value_counts() # 可以檢視-1和1各有多少個 
x = data.iloc[:,:-1]
y = data.iloc[:,-1].replace(-1,0) # 將-1全部替換成0
model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(4,input_shape=(15,),activation = 'relu'))
model.add(tf.keras.layers.Dense(4,activation = 'relu'))
model.add(tf.keras.layers.Dense(1,activation = 'sigmoid'))
model.compile(optimizer='adam',loss = 'binary_crossentropy', metrics = ['acc'])
history = model.fit(x,y,epochs=100)
history.history.keys()
plt.plot(history.epoch,history.history.get('loss'))
plt.plot(history.epoch,history.history.get('acc'))

輸出圖如下:

四、softmax多分類

神經網路的原始輸出不是一個概率值,實質上只是輸入的數值做了複雜的加權和與非線性處理之後的一個值而已 ,而softmax就是將輸出變成概率分佈。即

\[\sigma(z)_j = \frac{e^{z_j}}{\sum_{k=1}^Ke^{z_k}} \]

softmax要求每個樣本必須屬於某個類別,所有可能的樣本均被覆蓋,且概率之和為1。

程式碼如下:

import tensorflow as tf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
(train_image,train_label),(test_image,test_label) = tf.keras.datasets.fashion_mnist.load_data()
# 歸一化處理
train_image = train_image / 255
test_image = test_image / 255
model = tf.keras.Sequential()
#使其扁平為28*28長的向量,因為圖片大小為28*28畫素
model.add(tf.keras.layers.Flatten(input_shape = (28,28))) 
model.add(tf.keras.layers.Dense(128,activation='relu'))
model.add(tf.keras.layers.Dense(10,activation='softmax'))
#數字編碼則為sparse_categorical_crossentropy
model.compile(optimizer = 'adam',loss = 'sparse_categorical_crossentropy',metrics = ['acc'])
model.fit(train_image,train_label,epochs=5)
model.evaluate(test_image,test_label)

注意:在第五行獲取資料的時候,常常會下載失敗,需要下載離線版資料(網上搜索 fashion_mnist 就可以找到),放到C:\Users\使用者名稱\.keras\datasets\fashion-mnist 目錄下(沒有資料夾,需要手動建立)。

當然我們可以把label轉化成 one-hot 編碼,即獨熱編碼,什麼是獨熱編碼,可以參考這篇博文

train_label_onehot = tf.keras.utils.to_categorical(train_label)
test_label_onehot = tf.keras.utils.to_categorical(test_label)
model = tf.keras.Sequential()
model.add(tf.keras.layers.Flatten(input_shape = (28,28)))
model.add(tf.keras.layers.Dense(128,activation='relu'))
model.add(tf.keras.layers.Dense(10,activation='softmax'))
#獨熱編碼categorical_crossentropy
model.compile(optimizer = 'adam',loss = 'categorical_crossentropy',metrics = ['acc'])
model.fit(train_image,train_label_onehot,epochs=5)
predict = model.predict(test_image)

五、優化演算法,學習速率與反向傳播演算法

  • 學習速率

    梯度就是表明損失函式相對引數的變化率,對梯度進行縮放的引數被稱為學習速率(learning rate)。學習速率是一種超引數或對模型的一種手工可配置的設定需要為它指定正確的值。如果學習速率太小,則找到損失函式極小值點時可能需要許多輪迭代;如果太大,則演算法可能會“跳過”極小值點並且因週期性的“跳躍”而永遠無法找到極小值點。

    在具體實踐中,可通過檢視損失函式值隨時間的變化曲線,來判斷學習速率的選取是合適的 合適的學習速率,損失函式隨時間下降,直到一個底部,不合適的學習速率,損失函式可能會發生震盪 。

    在調整學習速率時,既需要使其足夠小,保證不至於發生超調,也要保證它足夠大,以使損失函式能夠儘快下降,從而可通過較少次數的迭代更快地完成學習 。

  • 反向傳播演算法

    反向傳播演算法是一種高效計算資料流圖中梯度的技術每一層的導數都是後一層的導數與前一層輸出之積,這正是鏈式法則的奇妙之處,誤差反向傳播演算法利用的正是這一特點。 前饋時,從輸入開始,逐一計算每個隱含層的輸出,直到輸出層。然後開始計算導數,並從輸出層經各隱含層逐一反向傳播。為了減少計算量,還需對所有已完成計算的元素進行復用。這便是反向傳播演算法名稱的由來。

  • 優化函式

    SGD:隨機梯度下降優化器。隨機梯度下降優化器SGD和min-batch是同一個意思,抽取m個小批量(獨立同分布)樣本,通過計算他們平梯度均值。

    RMSprop:經驗上, RMSProp被證明有效且實用的深度學習網路優化演算法。RMSProp增加了一個衰減係數來控制歷史資訊的獲取多少,RMSProp會對學習率進行衰減。這個優化器通常是訓練迴圈神經網路RNN的不錯選擇 。

    Adam優化器:Adam演算法可以看做是修正後的Momentum+RMSProp演算法. Adam通常被認為對超引數的選擇相當魯棒(不敏感) ,學習率建議為0.001。

六、超引數選擇原則

所謂超引數,也就是搭建神經網路中,需要我們自己如選擇(不是通過梯度下降演算法去優化)的那些引數。比如,中間層的神經元個數、學習速率 。理想的模型是剛好在欠擬合和過擬合的界線上,也就是正好擬合數據。超引數的選擇是一個經驗與不斷測試的結果。

七、Dropout

深度學習模型常常使用丟棄法(dropout)來應對過擬合問題。即以一定的概率丟棄一些隱藏層的神經元。

model = tf.keras.Sequential()
model.add(tf.keras.layers.Flatten(input_shape = (28,28)))
model.add(tf.keras.layers.Dense(128,activation='relu'))
model.add(tf.keras.layers.Dropout(0.5)) #以0.5的概率丟棄
model.add(tf.keras.layers.Dense(128,activation='relu'))
model.add(tf.keras.layers.Dropout(0.5))
model.add(tf.keras.layers.Dense(10,activation='softmax'))
model.compile(optimizer = tf.keras.optimizers.Adam(learning_rate = 0.001),loss = 'sparse_categorical_crossentropy',metrics = ['acc'])
history = model.fit(train_image,train_label,epochs=10,validation_data=(test_image,test_label))

八、函式式API

import tensorflow as tf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
(train_image,train_label),(test_image,test_label) = tf.keras.datasets.fashion_mnist.load_data()
train_image = train_image / 255
test_image = test_image / 255
input = tf.keras.Input(shape = (28,28))
x = tf.keras.layers.Flatten()(input)
x = tf.keras.layers.Dense(32,activation = 'relu')(x)
x = tf.keras.layers.Dropout(0.5)(x)
x = tf.keras.layers.Dense(32,activation = 'relu')(x)
output = tf.keras.layers.Dense(32,activation = 'softmax')(x)
model = tf.keras.Model(inputs = input,outputs = output)
model.compile(optimizer = tf.keras.optimizers.Adam(learning_rate = 0.001),loss = 'sparse_categorical_crossentropy',metrics = ['acc'])
history = model.fit(train_image,train_label,epochs=10,validation_data=(test_image,test_label))

可以看出構建模型的時候,可以像函式一樣,有引數輸入,比如 x = tf.keras.layers.Flatten()(input) 中的 input ,這樣自己可以根據自己的需求,自定義模型。