深度學習之TensorFlow構建神經網絡層
基本法
深度神經網絡是一個多層次的網絡模型,包含了:輸入層,隱藏層和輸出層,其中隱藏層是最重要也是深度最多的,通過TensorFlow,python代碼可以構建神經網絡層函數,比如我們稱之為add_layer()函數,由於神經網絡層的工作原理是一層的神經元處理完成后得到一個結果,然后傳遞給下一個神經元,這就類似於函數的return與參數變量,所以最終代碼的模型應該如下圖所示:
通過add_layer的層層嵌套,實現上一個add_layer的結果返回給下一個add_layer作為參數變量。
激勵函數
在深度神經網絡中,激勵函數的概念非常重要,TensorFlow已經包含了許多的激勵函數。
什么是激勵函數呢?
在神經網絡中,隱層和輸出層節點的輸入和輸出之間具有函數關系,這個函數稱為激勵函數。常見的激勵函數有:線性激勵函數、閾值或階躍激勵函數、S形激勵函數、雙曲正切激勵函數和高斯激勵函數等。(百度百科)
簡而言之:隱藏層在處理后得到一個值,這個值要么直接傳遞給下一層,要么先處理一下再傳遞下一層,這個處理的過程就是激勵函數。從數學角度說,激勵就是上一層輸出和下一層輸入存在一種映射關系,即:
F (上一層結果->下一層輸入)
為何需要激勵函數?
想起上一回中介紹了使用TensorFlow求一次函數的系數與偏量的方法,那並沒有用到激勵函數,是因為輸入與輸出的數據呈現為線性關系,也就是y=kx+b這樣的簡單關系,但實際的問題中輸入數據與輸出數據可能會存在某種非線性關系。
如圖要區分藍紅點,可以使用線性函數y>kx+b或者kx+b>y讓神經網絡快速學會分類
如圖再要區分藍紅點,使用線性函數是做不到的,當然可以選擇直接使用非線性函數來處理,在TensorFlow中亦可給出非線性計算的訓練單元,但是另一種更為取巧的辦法就是給線性函數嵌套一個非線性函數,使得原來的線性關系變成非線性關系,這就使得隱藏層處理的關注點放在了權重k與偏量b上,這就是激勵函數的作用!他把本來復雜的問題簡化為只調整兩個簡單的參數上來。
為什么需要分層,深度對學習能力有什么影響?
首先既然說到深度學習,隱藏層應該是多層才算“深”,分層就是為了提高學習能力嘛!那么分層如何做到了學習能力提升?
假定如下場景,我們有三層隱藏層,每個層都有一個神經元,我們對神經元在進行簡化,就說他是一個函數y=kx+b,我們得出下圖:
圖中每一層都會產生出一個不同的f(x)=kx+b函數,而且不同層的函數的系數和偏量都是不一樣的,通過神經元連接將這些f(x)串聯起來,這有點像PHP里的鏈式操作,而且圖中只是簡單的線性關系(正如前面所述這些y函數外層還可以嵌套一個激勵函數做非線性化轉換)。
神經網絡存在一個“逆向工程”,就是當最終輸出神經元發現與真實值有差距時就會反向傳遞,通過調整每一層里的函數來糾正,調整的就是系數k和偏量b,調整出來的k,b在不同層中也是不一樣的,具體如何調整這就是個黑盒,人類無法掌控,但他最后就是會到達一個合適的值,這個專業名詞叫做:擬合!
綜上所述:層數越多,k,b可能性越多,函數越多,那么所能代表的可能性越多,從數學上說,無窮多的可能性可以表達宇宙一切信息。
Show me the code!
Talking is cheap, show me the code!
以下我們給出一個實際的案例,實現上述所述的神經網絡設計和構建過程:
import tensorflow as tf
import numpy as np
#the function to create layer
def add_layer(inputs,in_size,out_size,activation_function=None):
w = tf.Variable(tf.random_normal([in_size,out_size]))
b = tf.Variable(tf.zeros([1,out_size])+0.1)
f = tf.matmul(inputs,w) + b
if activation_function is None:
outputs = f
else:
outputs = activation_function(f)
return outputs
#create test data(as real data)
x_data = np.linspace(-1,1,300,dtype=np.float32)[:,np.newaxis]
noise = np.random.normal(0,0.05,x_data.shape)
y_data = np.square(x_data)-0.5+noise
#give tensorflow input placeholder
xs = tf.placeholder(tf.float32,[None,1])
ys = tf.placeholder(tf.float32,[None,1])
l1 = add_layer(x_data,1,10,activation_function=tf.nn.relu)
prediction = add_layer(l1,10,1,activation_function=None)
loss = tf.reduce_mean(tf.reduce_sum(tf.square(y_data-prediction),reduction_indices=[1]))
train = tf.train.GradientDescentOptimizer(0.1).minimize(loss)
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)
for i in range(1000):
sess.run(train,feed_dict={xs:x_data,ys:y_data})
if(i % 50 == 0):
print sess.run(loss,feed_dict={xs:x_data,ys:y_data})
額,不要在意代碼中我垃圾的英語注釋。
我們簡單分析一下代碼的含義,其實如果讀懂了代碼前所述的理論概念,代碼就能一下理解了。
首先,我們開頭定義了一個create_layer函數,這個函數作用是創建一個神經網絡層!參數變量是:輸入值,輸入值的矩陣行數,矩陣列數,激勵函數。這里為什么是矩陣?因為實際處理的現實問題所攜帶的數據是多個維度的,人類一般能理解到三維,三維以上計算機可以理解。不用過於在意這個細節,以后結合實際問題解釋為什么是矩陣的y=kx+b運算,現在要記住x是個多維矩陣就好了,不是簡單的數。
create_layer內部做了什么?
函數內部首先是使用numpy給出了隨機數,這個隨機數也是個矩陣,代表了系數k與偏量b,tf.Varible()讓TensorFlow學習過程中改變這兩個變量(叫做權重,偏量調整),然后tf.matmul進行了矩陣的運算,給出這一層的輸出值,if—else語句負責判斷是否要給輸出值套上一個激勵函數,將其非線性化。
placeholder干什么的?
由於神經網絡的學習要求我們輸入測試的數據,placeholder的作用是提供輸入數據的空位,實際問題產生的原始數據丟入到placeholder中,然后被一個叫feed_dict的對象收集到TensorFlow當中,再交給神經網絡去處理得到一個預測結果。placeholder就是是原始數據的入口。
loss是什么?
損失值,用於優化訓練過程,很簡單,我們要讓學習結果和真實結果接近,就要求他們之間的差越來越小,這個值就代表了他們的“差”,這個差可不是數學上的減減就行的,可以看到我們用到了:
tf.reduce_sum(tf.square(y_data-prediction),reduction_indices=[1])
這么長的計算方式,實際值和真實值的差的平方,然后還累加了矩陣中的差平方值,很復雜呢。反正這個值越小越好就對了。
控制台輸出結果
運行以上python代碼,得到的是損失值,這是為了驗證TensorFlow在學習過程中是不是真的在優化自己,loss越來越小就是優化了:
確認:損失值減小,學習優化了!
題外話
個人覺得現在的深度學習的人工智能和傳統的人工智能對比,就好像物理上的“量子力學”與“相對論”,怎么說?傳統的人工智能認為機器學習過程是正向的,人類為機器設定好規則,在代碼中呈現為if then if then......的特點,而深度學習是只給出輸入值,讓機器去“推理”出一個結果,與正確結果對比,錯了就逆向去重新修改“推理”的邏輯,這就是一個無窮多可能的事情,這有點像“量子力學”中只有當觀察量子世界中某一個現象時,才能得到一個確定的狀態(結果),不觀察時,這個魔盒中的可能性是無窮多的,答案落在任何一塊區域中,怎么樣,是不是很神奇?再通俗比喻一下,這就像武功的最高境界“無招勝有招”!只要能贏就是厲害!通過不斷的輸,修改招式,最后取得勝利,成為大師的時候固定招式已經不復存在了。