天天看點

(2編寫網絡)自己動手,編寫神經網絡程式,解決Mnist問題,并網絡化部署

基于《神經網絡和深度學習》這本絕好的教材提供的相關資料和代碼,我們自己動手編寫“随機取樣的梯度下降神經網絡”。為了更好地說明問題,我們先從簡單的開始:1、sigmod函數,基本上就是基于定義的;

#########helper函數########

#計算sigmoid,這個函數來自定義

def sigmoid(z):

return 1.0/(1.0+np.exp(-z))

#計算sigmoid的導數,這個函數可以被證明

def sigmoid_prime(z):

return sigmoid(z)*(1 - sigmoid(z))

2、構造函數

###########Main函數########

#使用例子 net = GoNetwork([2, 3, 1])

class GoNetwork(object):

def __init__(self, sizes):#構造函數

self.num_layers = len(sizes)#層數

self.sizes = sizes #每層size

#随機生成子節點

self.biases= [np.random.randn(y, 1) for y in sizes[1:]]

# net.weights[1] 是一個存儲着連接配接第二層和第三層神經元權重的 Numpy 矩陣。

self.weights = [np.random.randn(y, x)

for x, y in zip(sizes[:-1], sizes[1:])]

這個地方有以下幾個地方,一個是在Python中類和類的構造函數是這樣定義的;二個是Python如何展現出其強大的資料處理能力的。

這裡,如果

sizes = [2,  3,  1]

則sizes [1:] = [3,1]

numpy.random.randn(d0, d1, ..., dn)

這個函數的作用就是從标準正态分布中傳回一個或多個樣本值,比如

bbb = [np.random.randn(3, 2)]

表示的是生成3X2的随機序列,可以這樣來使用,就是加上偏置了

2.5 * np.random.randn(2, 4) + 3

傳回:

array([[ 4.128****53,  1.764****44 ,  2.732****92,  2.90839231],

      [ 0.174****86,  4.92026887,  1.574****66, -0.4305991 ]])

aaa =[ np.random.randn(y, 1) for y in sizes[1:]]

這是一種Python的連寫方法,這裡就是對[3,1]分别生成随機序列。這個随機是用來幹什麼的?就是随機的權值。

描述 zip() 函數用于将可疊代的對象作為參數,将對象中對應的元素打包成一個個元組,然後傳回由這些元組組成的清單

這裡

zip(sizes[:-1], sizes[1:])

表示的是将第1、2層之間,2、3層之間的全連接配接生成随機權值。

3、前向網絡,主要用于測試目前網絡

def feedforward(self,a):

for b,w in zip(self.biases,self.weights):

a = sigmoid(np.dot(w,a)+b)

return a

非常直接的按照定義,進行上一層到下一層的前向計算,注意這裡得到的a也是x行1列的一個矩陣

4、評價函數,基本上也是按照定義進行設定的

def evaluate(self, test_data):

test_results = [(np.argmax(self.feedforward(x)), y)#這裡需要注意feedforward的參數x,實際上它是一個in/out參數。

for (x, y) in test_data]

return sum(int(x == y) for (x, y) in test_results)#做出了正确的預測

這個地方調用了feedforward(x),并且和y進行比較,得到準确比對有哪些。應該說代碼非常精簡。

5、代價函數

#cost代價函數

def cost_derivative(self, output_activations, y):

return (output_activations-y)

以上幾項都是非常好了解的,基本上你看到的立刻就能夠了解,需要補充的知識并不是很多。結合上一課的相關知識,我們這裡提出的所謂随機,就是提取很小的一塊資料,而後進行計算梯度下降參數,更新網絡的權重和偏置

def update_mini_batch(self, mini_batch, eta):

nabla_b = [np.zeros(b.shape) for b in self.biases]#生成b和w形狀的以0填充的矩陣

nabla_w = [np.zeros(w.shape) for w in self.weights]

for x, y in mini_batch:

delta_nabla_b, delta_nabla_w = self.backprop(x, y)#了解反向傳播就是一種快速計算梯度的方法

nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]

nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]

self.weights = [w-(eta/len(mini_batch))*nw

for w, nw in zip(self.weights, nabla_w)]

self.biases = [b-(eta/len(mini_batch))*nb

for b, nb in zip(self.biases, nabla_b)]

其中

   nabla_b = [np.zeros(b.shape) for b in self.biases]

   nabla_w = [np.zeros(w.shape) for w in self.weights]

生成b和w形狀的以0填充的矩陣,這裡就是用來填充原始資料的。

在這個小循環裡面,我們可以以“黑箱”的形式來了解backprop函數,就是一種用來計算最快下降梯度的方法。

 for x, y in mini_batch:

            delta_nabla_b, delta_nabla_w = self.backprop(x, y)

            nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]

            nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]

在這裡,我們便曆所有的mini_batch,注意在上面這行代碼中,

而後,引入eta,以這個梯度作為delta_nabla_b, delta_nabla_w 的初始值都為空.

這樣,我們按照定義進行了一次小資料的更新。其能夠完成,是因為backprop為我們成功計算了代價函數的兩個梯度。

6、後向傳播函數,其目的是進行梯度下降計算,是最為複雜的部分

#反向傳播就是一種快速計算代價函數梯度的方法,也就是計算delta的一種方法

def backprop(self, x, y):

#都以空矩陣來進行初始化

nabla_b = [np.zeros(b.shape) for b in self.biases]

# feedforward

activation = x

activations = [x] # list to store all the activations, layer by layer

zs = [] # list to store all the z vectors, layer by layer

for b, w in zip(self.biases, self.weights):

z = np.dot(w, activation)+b #前向傳播

zs.append(z)

activation = sigmoid(z)

activations.append(activation)

# backward pass

delta = self.cost_derivative(activations[-1], y) * \

sigmoid_prime(zs[-1])

nabla_b[-1] = delta

nabla_w[-1] = np.dot(delta, activations[-2].transpose())

for l in range(2, self.num_layers):

z = zs[-l]

sp = sigmoid_prime(z)

delta = np.dot(self.weights[-l+1].transpose(), delta) * sp

nabla_b[-l] = delta

nabla_w[-l] = np.dot(delta, activations[-l-1].transpose())

return (nabla_b, nabla_w)

其中内容比較複雜,一條一條進行解釋 

nabla_b = [np.zeros(b.shape) for b in self.biases]

nabla_w = [np.zeros(w.shape) for w in self.weights]

生成空矩陣

# feedforward

前向計算,儲存所有b、w和 z。後面的幾行代碼,主要都是和4個公式嚴格對應的

delta = self.cost_derivative(activations[-1], y) * sigmoid_prime(zs[-1])

對應BP1

nabla_b[-1] = delta

nabla_w[-1] = np.dot(delta, activations[-2].transpose())

分别對應BP3和BP4,就是最後來計算具體的梯度值

delta = np.dot(self.weights[-l+1].transpose(), delta) * sp

對應BP2,反向計算。

7、随機梯度下降算法,到了這裡也就是将上面的合起來

#随機梯度下降算法

def SGD(self, training_data, epochs, mini_batch_size, eta,test_data=None):

training_data = list(training_data)

n = len(training_data)

if test_data:

test_data = list(test_data)

n_test = len(test_data)

#⾸先随機地将訓練資料打亂

for j in range(epochs):

random.shuffle(training_data)

#再将它分成多個适當⼤⼩的⼩批量資料

mini_batches = [

training_data[k:k+mini_batch_size]

for k in range(0, n, mini_batch_size)]

for mini_batch in mini_batches:#最主要的一行代碼

self.update_mini_batch(mini_batch, eta)

if test_data:

print("Epoch {} : {} / {}".format(j,self.evaluate(test_data),n_test))

else:

print("Epoch {} complete".format(j))

主要優化的地方,就是将原較大的資料集分成多個部分,而後周遊所有的部分,進行梯度下降運算,并且列印比較的結果。應該說再次展現了Python強大的內建編碼能力。

來自為知筆記(Wiz)

目前方向:圖像拼接融合、圖像識别

聯系方式:[email protected]