前面兩節,講述了梯度下降和方向傳播的原理,這裏我通過mnist訓練來講述下python的實現方法
頭文件
numpy用於矩陣運算,random用於數據集的shuffle,mnist_loader 用於加載數據集
import numpy as np
import random
import mnist_loader
初始化網絡
其中參數是sizes 格式是[784,30,10] 代表每層神經元的個數,self.weights 格式[34*784],[10*34],self.biases格式是[34*1]和[10*1] 前者代表每兩層神經元之間連線的權重,後者代表每個神經元上的偏向值。
def __init__(self,sizes):
self.layers = len(sizes)
self.weights = [np.random.randn(x,y) for (x,y) in zip(sizes[1:],sizes[:-1])]
self.biases = [np.random.randn(x,1) for x in sizes[1:]]
SGD
原理部分第二節已經說過了,我們來解讀一下參數
train_data:我們輸入的訓練集可以抽象成[(x,y),(x1,y1)....(xn,yn)]
epoches:訓練的輪數由我們自己指定
mini_size:每次隨機抽樣訓練的訓練樣本數量
eta():表示learning rate 學習率,可以理解爲步長
test_data:格式與train_data相同,用於測試(注意本實例中沒有介紹驗證集)
程序中先對數據全集進行shuffle打亂操作,之後按mini_size 進行分堆,下面分別對每個mini_batch 進行訓練,也就是隨機抽樣的過程,這樣做極大的加快了學習效率。
def SGD(self,train_data,epoches,mini_size,eta,test_data):
if test_data: n_test = len(test_data)
n = len(train_data) #表示 訓練樣本數量
for epoch in range(epoches):
random.shuffle(train_data)
mini_batches = [train_data[k:k+mini_size] for k in xrange(0,n,mini_size)] #xrange 返回的是生成器
for mini_batch in mini_batches:
self.update_minibatch(mini_batch,eta)
if test_data:
print "epoches {0} acc {1}/{2} ".format(epoch,self.cal_acc(test_data),n_test)
else:
print "no test data"
update_minibatch 主要完成的操作是,求出樣本集中每個實例的偏導數累加求和,在根據下面的公式進行公式對weights和biases進行更新
def update_minibatch(self,mini_batch,eta):
nabla_b = [np.zeros(b.shape) for b in self.biases] #隨機梯度下降算法,用於存儲我們指定的minibatch中的數據的bias的總和
nabla_w = [np.zeros(w.shape) for w in self.weights]
for x,y in mini_batch:
new_nabla_b ,new_nabla_w = self.back(x,y)
nabla_w = [nw+w for nw,w in zip(new_nabla_w,nabla_w)]
nabla_b = [nb+b for nb,b in zip(new_nabla_b,nabla_b)]
self.weights = [w-((eta/len(mini_batch))*nabla_w) for w,nabla_w in zip(self.weights,nabla_w)]
self.biases = [b-((eta/len(mini_batch))*nabla_b) for b,nabla_b in zip(self.biases,nabla_b)]
反向傳播
梯度下降和反向傳播的核心就是在於學習參數的偏導數計算,下圖是反向傳播求偏導數的過程。
1,首先初始化各層的參數爲zero 和Pytorch 中optimizer.zero_grad() 操作大體相似,避免之前梯度的累加,用於存儲各個層偏導數結果
nabla_b = [np.zeros(b.shape) for b in self.biases] #隨機梯度下降算法,用於存儲我們指定的minibatch中的數據的bias的總和
nabla_w = [np.zeros(w.shape) for w in self.weights]
2,z 在原始的公式中相當於 多個 的累加和,待激活的值, 第一層默認等於原始輸入的圖片數據的tensor值
z = np.dot(w,activation)+b
activation=,都是和神經元的個數一致。
3,進行前向傳播,計算所有的z和activation的值
for w,b in zip(self.weights,self.biases):
# print w.shape,b.shape
z = np.dot(w,activation)+b
zs.append(z)
activation = sigmoid(z)
activations.append(activation)
4,根據公式計算最後一層的error
delta = cost(activations[-1],y)*sigmoid_prime(zs[-1])
5,更新最後一層的bias和weight ,其中,bias等於最後一層的error值,weight是最後一層的error值與前一層激活值的轉置乘積
nabla_b[-1] = delta
#delta 目前可能是10*1 的向量,而activation[-2]可能是784*1 無法直接點乘 需要轉置後者 最終是10個784維向量 內部相加輸出 10*1
nabla_w[-1] = np.dot(delta,activations[-2].transpose())
6,根據鏈式求導法則 ,反向更新各層的weight值與bias值 ,並最終以(nabla_w,nable_w)形式返回給隨機梯度下降算法(update_mini_batch)的進行,全局weights和biases的更新
for l in range(2,self.layers):
delta = np.dot(self.weights[-l+1].transpose(),delta)*sigmoid_prime(zs[-l])
nabla_b[-l] =delta
nabla_w[-l] = np.dot(delta,activations[-l-1].transpose())
return (nabla_b,nabla_w)
準確率計算
測試集是10000個(x,y),首先進行前向傳播和softmax,獲取最終預測的label值,並計算預測值與真實值的數量來計算準確率。
def cal_acc(self,test_data):
test_set = [(self.feedforward(x),y) for x,y in test_data]
return np.sum(int(np.argmax(x))==y for (x,y) in test_set)
def feedforward(self,x):
for w,b in zip(self.weights,self.biases):
x = np.dot(w,x)+b
x = sigmoid(x) #一定不能忘記激活
下面是工具方法,包含了的導數:
def sigmoid_prime(z):
return sigmoid(z)*(1-sigmoid(z))
激活函數 :
def sigmoid(z):
return 1/(1+np.exp(-z))
源代碼請移步 堅持一件事或許很難,但堅持下來一定很酷!^_^