[Deep-Learning-with-Python]神經網絡的數學基礎
理解深度學習需要熟悉一些簡單的數學概念:Tensors(張量)、Tensor operations 張量操作、differentiation微分、gradient descent 梯度下降等等。
“Hello World”----MNIST 手寫數字識別
#coding:utf8 import keras from keras.datasets import mnist from keras import models from keras import layers from keras.utils import to_categorical # 加載MNIST數據集 (train_images,train_labels),(test_images,test_labels) = mnist.load_data() # 定義網絡架構 network = models.Sequential() network.add(layers.Dense(512,activation="relu",input_shape=(28*28,))) network.add(layers.Dense(10,activation="softmax")) # 定義網絡優化:優化算法、損失函數以及評價指標 network.compile(optimizer=‘rmsprop‘,loss="categorical_crossentropy",metrics=[‘accuracy‘]) # 數據預處理:images 縮放到[0,1]之間 train_images = train_images.reshape(60000,28*28) train_images = train_images.astype(‘float32‘) / 255 test_images = test_images.reshape(test_images.shape[0],28*28) test_images = test_images.astype(‘float32‘) / 255 # 數據預處理:labels:one-hot 編碼 train_labels = to_categorical(train_labels) test_labels = to_categorical(test_labels) # 模型訓練 network.fit(train_images,train_labels,epochs=5,batch_size=128) # 模型測試 test_loss, test_acc = network.evaluate(test_images,test_labels) print(‘test accuracy:‘,test_acc) # test accuracy: 0.9727
由上面的程序,我們了解了如何構建網絡以及如何進行網絡訓練來識別手寫字體
神經網絡的數據表示
當下幾乎所有的機器學習框架都使用tensors張量作為基本的數據結構。Tensor本質上是一個數據容器,大多數為數值型數據,也就是說tensor是存儲數字的容器。矩陣是二維的張量,張量是任意維數的矩陣的推廣(tensor的一個維度通常稱為一個軸axis,而不是dimension)。
Scalars(0D tensors)標量--0維張量
只包含一個數字的張量tensor叫做標量scaler(或者0D tensor). 在numpy中,一個float32,或float64類型的數字是一個標量。可以通過tensor的ndim屬性查看tensor的維度;張量的維度為0,同時維度也稱為秩rank。
>>> import numpy as np
>>> x = np.array(12)
>>> x
array(12)
>>> x.ndim
0
向量(一維張量 1D)
一維數組稱為向量,或一維張量。一維張量有一個軸axis;
>>> x = np.array([13, 31, 7, 14])
>>> x
array([13, 31, 7, 14])
>>> x.ndim
1
上述向量有5個條目,因此稱為5維向量。5維向量和5維張量並不相同。5維向量指一個軸5個元素。5維張量有5個軸。
矩陣(二維張量 2D)
向量數組為一個矩陣,即二維張量。一個矩陣有二個軸。
>>> x = np.array([[5, 78, 2, 34, 0],
[6, 79, 3, 35, 1],
[7, 80, 4, 36, 2]])
>>> x.ndim
2
三維張量以及更高維張量
矩陣數組稱為三維張量,可以看做是數字的立方體。
>>> x = np.array([[[5, 78, 2, 34, 0],
[6, 79, 3, 35, 1],
[7, 80, 4, 36, 2]],
[[5, 78, 2, 34, 0],
[6, 79, 3, 35, 1],
[7, 80, 4, 36, 2]],
[[5, 78, 2, 34, 0],
[6, 79, 3, 35, 1],
[7, 80, 4, 36, 2]]])
>>> x.ndim
3
3維張量數組形成一個4維張量,以此類推。深度學習中,一般操作0D~4D的張量。
核心屬性
tensor張量由3個重要的屬性:
- Number of axes軸的個數(秩)。3D tensor有3個軸。可以通過tensor的ndim屬性查看軸的個數。
- Shape形狀:數字元組,描述張量各個軸上的維度。張量維度為(),向量維度為(5,),2D張量維度(3,5),3D張量維度(3,3,5).
- Data type數據類型(dtype屬性):張量中數字的數據類型,如float32,uint8,float64等等。
數據批量data batches
深度學習中數據張量的第一軸(axis 0)通常是樣本軸(樣本維度)---表示樣本量的數目。MNIST數據集中,樣本是數字圖片。
此外,深度學習處理數據過程中並不一次性對整個數據集進行處理,通常會將數據集劃分成若幹個批量batches。比如:MNIST中128的小批量樣本:
batch = train_images[:128]
生活中遇到的數據張量
- 向量型數據vector data--2維張量 ,形狀(samples,features)
- 時間序列數據或序列型數據--3維張量,形狀(samples,timesteps, features)
- 圖片--4維張量,形狀(samples, height, width, channels)或者(samples, channels, height, width)
- 視頻--5維張量。形狀(samples. frames, height, width, channels) 或者(samples, frames, channels, height, width)
Tensors 操作
所有的計算機程序最終都簡化為二進制輸入上的二進制操作(AND, OR, NOR 等),同時,深度學習網絡中所有的轉換也可以簡化為數據張量上的張量操作,如 加、乘等。
逐元素操作element-wise operations
relu操作和加法運算是逐元素操作:獨立應用於待計算張量中的每個條目。
比如加法運算的for-loop實現:
def naive_add(x, y):
assert len(x.shape) == 2
assert x.shape == y.shape
x = x.copy()
for i in range(x.shape[0]):
for j in range(x.shape[1]):
x[i, j] += y[i, j]
return x
廣播broadcasting
上面實現的naive_add加法運算僅支持兩個形狀相同的二維張量。如果兩個加法運算的張量形狀不相同會發生什麽?小張量會廣播匹配到大張量上。廣播由兩步組成:
- 小張量會添加axes廣播軸,以匹配大張量的ndim軸維度。
- 小張量在新添加的軸方向上重復以匹配大張量的形狀。
舉例來說,張量X形狀為(32, 10),張量y形狀為(10, ).兩個張量相加。首先,添加一個新軸到張量y上,形狀變成(1, 10);然後,在新軸方向上重復y32次,最終張量Y形狀為(32,10),X、Y形狀相同,可以進行加法運算。
但實際過程中並不會創建新的二維張量,影響計算效率。
def naive_add_matrix_and_vector(x, y):
assert len(x.shape) == 2
assert len(y.shape) == 1
assert x.shape[1] == y.shape[0]
x = x.copy()
for i in range(x.shape[0]):
for j in range(x.shape[1]):
x[i, j] += y[j]
return x
張量點積運算 Dot
dot點積操作最常用、最有用的張量操作。與逐元素操作相反,點積整合輸入張量的所有條目。
def naive_vector_dot(x, y):
assert len(x.shape) == 1
assert len(y.shape) == 1
assert x.shape[0] == y.shape[0]
z = 0.
for i in range(x.shape[0]):
z += x[i] * y[i]
return z
tensor reshaping
reshape意味著重新排列張量tensor的行和列以滿足特定的形狀。Reshape之後的tensor與初始tensor包含的系數數目相同。
>>> x = np.array([[0., 1.],
[2., 3.],
[4., 5.]])
>>> print(x.shape)
(3, 2)
>>> x = x.reshape((6, 1))
>>> x
array([[ 0.],
[ 1.],
[ 2.],
[ 3.],
[ 4.],
[ 5.]])
基於梯度的優化算法
神經網絡層對輸入進行的數學轉換為:
\(output = relu(dot(W, input) + b)\)
張量\(W\)和張量\(b\) 是網絡層的參數,被稱為網絡層的權重系數或者可訓練參數。這些權重系數包含著網絡從訓練數據中學到的信息。
起始這些權重參數用小的隨機數賦值(稱為隨機初始化)。隨後,基於反饋信號逐漸調整權重系數。調整過程稱為訓練過程。
訓練過程通常需要反復進行:
- 獲得訓練數據X,y的一個batch 批量;
- 前向傳播得到批量X上的預測值y_pred;
- 計算當前批量下的損失值:計算y_pred和y之間的差異度;
- 在損失函數減小的方向上更新權重系數。
隨機梯度下降
一個可微分函數,理論上能夠找到它的最小值:最小值點導數為0,所以需要找到所有導數為0的點,然後相互比較找到最小值。
神經網絡中,意味著找到一組權重值,使損失函數最小。
mini-batch SGD可以描述為以下四步:
- 獲得訓練數據X,y的一個batch 批量;
- 前向傳播得到批量X上的預測值y_pred;
- 計算當前批量下的損失值:計算y_pred和y之間的差異度;
- 沿著梯度反方向移動權重系數--例如:\(W -= step * gradient\),損失函數也因此減小。
隨機是指每個小批量batch是隨機在數據中挑選的。
小批量隨機梯度下降的一種極端情況是隨機梯度下降算法---全部數據形成一個批量,計算結果更準確,但效率比較低。
小結
- 學習指在訓練數據上找到一組權重值使得損失函數最小;
- 學習過程:在小批量數據上計算損失函數對應權重系數的梯度值;之後權重系數沿著梯度的反方向移動;
- 學習過程的可能性是基於神經網絡是一系列張量操作,因此能夠使用導數的鏈式法則計算損失函數對應權重系數的梯度值;
- 兩個重要的概念:損失函數和優化方法(需要在數據送到網絡之前定義);
- 損失函數:在訓練過程中最小化的函數,可以用來評估模型的好壞(越小越好,最小為0);
- 優化方法:計算梯度的具體方法,之後更新權重系數;比如有:RMSProp、SGD、Momentum等等。
[Deep-Learning-with-Python]神經網絡的數學基礎