1. 程式人生 > >入門級神經網路之權重訓練

入門級神經網路之權重訓練

沒有使用任何框架,最能靠近二分類機器學習本質的一段微程式碼

參考: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)

迭代迴圈