入門級神經網路之權重訓練
沒有使用任何框架,最能靠近二分類機器學習本質的一段微程式碼
參考:python.jobbole.com/82758
初識神經網路之二層
樣本資料
程式碼全文
import numpy as np import tensorflow as tf # sigmoid function def nonlin(x,deriv=False): if(deriv==True): return x*(1-x) return 1/(1+np.exp(-x)) # input dataset X = np.array([ [0,0,1], [1,1,1], [1,0,1], [0,1,1] ]) # output dataset y = np.array([[0,1,1,0]]).T # seed random numbers to make calculation # deterministic (just a good practice) np.random.seed(1) # initialize weights randomly with mean 0 syn0 = tf.Variable(tf.zeros([3,1])) #向tensorboard新增資料 tf.summary.histogram('syn0[0]',syn0[0]) tf.summary.histogram('syn0[1]',syn0[1]) tf.summary.histogram('syn0[2]',syn0[2]) #將所有的summary全部儲存磁碟 merged = tf.summary.merge_all() init = tf.global_variables_initializer() sess = tf.Session() sess.run(init) #tensorboard所需資料寫入檔案 writer = tf.summary.FileWriter('./tensorflow/',sess.graph) for iter in range(600): # forward propagation l0 = X l1 = nonlin(np.dot(l0,sess.run(syn0))) # how much did we miss? l1_error = y - l1 # multiply how much we missed by the # slope of the sigmoid at the values in l1 l1_delta = l1_error * nonlin(l1,True) # update weights sess.run(tf.assign(syn0, sess.run(syn0) + np.dot(l0.T,l1_delta))) #向tensorboard新增資料 rs = sess.run(merged) writer.add_summary(rs,iter) print("Output After Training:") print(l1)
列印結果
觀察tensorboard
相比原參考連結,我們這裡添加了tersorboard功能,用以檢視權重值syn0變化趨勢,我這裡訓練了600次,可以看到有部分曲線還沒有打到‘完美’,說明需要繼續訓練。
更為詳盡的tensorboard參考連結:https://www.cnblogs.com/maskerk/p/9973664.html
額外解析
為什麼叫額外解析呢,我這裡只說我認為比較關鍵的地方,與原作者形成補充。
1.從訓練資料這裡看起,l0為輸入層,l1為輸出層,這裡只有兩層,就沒有隱藏層了。
2.首先,這裡l0點乘syn0(權重係數)得到l1,這裡就像線性方程
l1 = syn0*l0(y = W * x)
nonlin
函式,這個函式就是啟用函式
,在啟用函式中執行1/(1+np.exp(-x))
,這樣執行的後果是什麼呢?就是不管你之前送進去的值是多少,現在出來的結果都是0~1之間了。函式影象如下圖。
其實這樣 l1
就得到了,也就是結果,但這是訓練的第一次的第一步,基本上距離正確結果很遠(除非中彩)。
3.
l1_error = y - l1
然後計算誤差,我們知道要麼是1要麼是0,l1
的結果又在0~1之間,所以如果樣本值為1 , 1 - l1 的值為正值 ,樣本值如果為0 , 0 - l1的值為負值,這樣,下一步l1
應該向左走還是向右走,就有方向 了。4.
l1_delta = l1_error * nonlin(l1,True)
這裡計算的l1_delta
,實際上就是l1_error
乘上一個變化係數,這裡的變化係數就是函式sigmoid
的斜率,從函式的影象可以看到,y軸在0.5處斜率最大,靠近0或者1斜率也會逐漸變小,這裡乘上這個斜率,能夠使得預測值在距離0或者1較遠時能儘快向0或1靠攏。同時斜率一直為正數,這樣相乘不會影響我們上一段說的變化方向。5.
syn0 += np.dot(l0.T,l1_delta)
這裡學習的過程,其實就是不斷修改權重係數syn0
使其不斷靠近正確值得過程。這裡l0.T*l1_delta,這樣就是每一列中的元素去乘l1_delta ,這樣就是第一列的元素分別乘以誤差然後相加,表現的是每一列與誤差的關係。其實仔細看一下就能發現,我們的第一列的數值和最終結果是相符合的。
神經網路之三層
樣本資料
程式碼全文
import numpy as np
def nonlin(x,deriv=False):
if(deriv==True):
return x*(1-x)
return 1/(1+np.exp(-x))
X = np.array([[0,0,1],
[0,1,1],
[1,0,1],
[1,1,1]])
y = np.array([[0],
[1],
[1],
[0]])
np.random.seed(1)
# randomly initialize our weights with mean 0
syn0 = 2*np.random.random((3,4)) - 1
syn1 = 2*np.random.random((4,1)) - 1
for j in xrange(60000):
# Feed forward through layers 0, 1, and 2
l0 = X
l1 = nonlin(np.dot(l0,syn0))
l2 = nonlin(np.dot(l1,syn1))
# how much did we miss the target value?
l2_error = y - l2
if (j% 10000) == 0:
print "Error:" + str(np.mean(np.abs(l2_error)))
# in what direction is the target value?
# were we really sure? if so, don't change too much.
l2_delta = l2_error*nonlin(l2,deriv=True)
# how much did each l1 value contribute to the l2 error (according to the weights)?
l1_error = l2_delta.dot(syn1.T)
# in what direction is the target l1?
# were we really sure? if so, don't change too much.
l1_delta = l1_error * nonlin(l1,deriv=True)
syn1 += l1.T.dot(l2_delta)
syn0 += l0.T.dot(l1_delta)
列印結果
隱藏層示例
額外解析
① 這裡首先一個問題就是,輸出結果不是和某一列直接相關了 ,這是和第一個demo最大的差別。這裡使用了三層神經網路,即 l0輸入層, l1 隱藏層,l2 輸出層
② 這裡將 syn0
定義為 三行四列,這裡我沒懂 為什麼,這也是一個關鍵的地方。如果之後弄明白了再回來補充。
syn1
定義為四行一列很明白,就是為了資料結果是為了四行一列。
③
l1 = nonlin(np.dot(l0,syn0))
l2 = nonlin(np.dot(l1,syn1))
這裡是前向傳遞 。輸入層權重0 得到隱藏層, 隱藏層權重1 得到輸出層,沒什麼好說的
④
l2_error = y - l2
l2_delta = l2_error*nonlin(l2,deriv=True)
l1_error = l2_delta.dot(syn1.T)
l1_delta = l1_error * nonlin(l1,deriv=True)
syn1 += l1.T.dot(l2_delta)
syn0 += l0.T.dot(l1_delta)
這裡是反向傳遞 ,計算誤差然後從輸出層傳遞迴輸入層。
第一步。l2誤差為樣本值-輸出層值,l2_delta = l2的計算誤差方式與第一個demo的l1計算誤差方式相同。
第二步。方式和第一個demo也相同。 l2(輸出層)的誤差 * 變化係數(激勵函式的導數值)
第三步。這裡就開始不同了。l1(隱藏層)的誤差 = l2_delta * l1層的權重係數。按照慣例,是 l1 - l1樣本值,但是這裡沒有樣本值,l2的輸出誤差又和l1的權重係數有直接聯絡,所以這裡就用了這麼個式子,用以鉗制 l1權重值。
第四步。方式和第二步類似。
第五步。因為是反向傳遞,所以權重值在修改的時候,先改的後邊的權重值syn1 += l1.T.dot(l2_delta)
迭代迴圈