天天看點

計算神經網絡:資料讀取與完成訓練初始化參數b初始化參數w初始化參數方法預測函數與上文優化d_tanh有關,将矩陣乘法化為數組乘以矩陣

來源商業新知網,原标題:手工計算神經網絡第三期:資料讀取與完成訓練

資料集介紹

資料集采用著名的MNIST的手寫資料集。根據官網介紹,這個資料集有70000個樣本,包括60000個訓練樣本,10000個測試樣本。

資料集下載下傳下來之後,檔案分為4個部分,分别是:訓練集圖檔、訓練集标簽、測試集圖檔、測試集标簽。這些資料以二進制的格式儲存。

計算神經網絡:資料讀取與完成訓練初始化參數b初始化參數w初始化參數方法預測函數與上文優化d_tanh有關,将矩陣乘法化為數組乘以矩陣

其中,訓練集圖檔檔案的前16個位元組是儲存了圖檔的個數,行數以及列數等。訓練集标簽檔案前8個位元組儲存了圖檔标簽的個數等。測試集的兩個檔案同理。

計算神經網絡:資料讀取與完成訓練初始化參數b初始化參數w初始化參數方法預測函數與上文優化d_tanh有關,将矩陣乘法化為數組乘以矩陣

文摘菌下載下傳好的檔案存儲位址

讀取資料

train_img_path=r'C:UsersDellMNISTtrain-images.idx3-ubyte'

train_lab_path=r'C:UsersDellMNISTtrain-labels.idx1-ubyte'

test_img_path=r'C:UsersDellMNISTt10k-images.idx3-ubyte'

test_lab_path=r'C:UsersDellMNISTt10k-labels.idx1-ubyte'

根據檔案在本地解壓後的儲存位址,生成四個位址,上面代碼中‘r’是轉義字元,因為在Python中有特殊的用法,是以需用轉義字元明确檔案位址。

為了讓後面的模型表現更好,我們将訓練集拆分,拆成50000個訓練集和10000個驗證集。

注:驗證集 是模型訓練過程中單獨留出的樣本集,它可以用于調整模型的超參數和用于對模型的能力進行初步評估。

import struct

train_num=50000

valid_num=10000

test_num=10000

with open(train_img_path,'rb') as f:

struct.unpack('>4i',f.read(16))

tmp_img=np.fromfile(f,dtype=np.uint8).reshape(-1,28*28)

train_img=tmp_img[:train_num] #前五萬個資料是訓練集

valid_img=tmp_img[train_num:] #第五萬到第六萬個資料是測試集

with open(test_img_path,'rb') as f:

test_img=np.fromfile(f,dtype=np.uint8).reshape(-1,28*28)

with open(train_lab_path,'rb') as f:

struct.unpack('>2i',f.read(8))

tmp_lab=np.fromfile(f,dtype=np.uint8)

train_lab=tmp_lab[:train_num]

valid_lab=tmp_lab[train_num:]

with open(test_lab_path,'rb') as f:

test_lab=np.fromfile(f,dtype=np.uint8)
           

因為,檔案是以二進制的格式儲存,是以資料讀取方式是‘rb’。又因為我們需要資料以阿拉伯數字的方式顯示。是以這裡用到了Python的struct包。 struct.unpack('>4i',f.read(16)) 中的>号代表位元組存儲的方向,i是整數,4代表需要前4個整數。f.read(16)是指讀取16個位元組,即4個整數,因為一個整數等于4個位元組。

reshape(-1,28*28) :如果參數中存在-1,表示該參數由其他參數來決定.-1是将一維數組轉換為二維的矩陣,并且第二個參數是表示每一行數的個數。

注:fromfile的用法 np.fromfile (frame, dtype=np.float, count=‐1, sep=''),其中:frame : 檔案、字元串。dtype :讀取的資料類型。count : 讀入元素個數,‐1表示讀入整個檔案。sep : 資料分割字元串。

檔案讀取完成,接下來按照用圖檔的方式顯示資料。

import matplotlib.pyplot as plt

def show_train(index):

plt.imshow(train_img[index].reshape(28,28),cmap='gray')

print('label:{}'.format(train_lab[index]))

def show_test(index):

print('label:{}'.format(test_lab[index]))

def valid_train(index):

plt.imshow(valid_img[index].reshape(28,28),cmap='gray')

print('label:{}'.format(valid_lab[index]))

注意,如果不定義 cmap='gray' ,圖檔的底色會非常奇怪。

計算神經網絡:資料讀取與完成訓練初始化參數b初始化參數w初始化參數方法預測函數與上文優化d_tanh有關,将矩陣乘法化為數組乘以矩陣

測試一下,定義完函數之後,顯示的是這樣的~

資料顯示和讀取完成,接下來開始訓練參數。

訓練資料

在開始之前,為了能夠上下銜接,我們把第一次課程的代碼貼上來~

def tanh(x):

return np.tanh(x)

def softmax(x):

exp = np.exp(x-x.max())

return exp/exp.sum()

dimensions = [28*28,10]

activation = [tanh,softmax]

distribution=[

{

'b':[0,0]

},{

'b':[0,0],

'w':[-math.sqrt(6/(dimensions[0]+dimensions[1])),math.sqrt(6/(dimensions[0]+dimensions[1]))]

}]

初始化參數b

def init_parameters_b(layer):

dist = distributionlayer

return np.random.rand(dimensions[layer])*(dist[1]-dist[0])+dist[0]

初始化參數w

def init_parameters_w(layer):

return np.random.rand(dimensions[layer-1],dimensions[layer])*(dist[1]-dist[0])+dist[0]

初始化參數方法

def init_parameters():

parameter=[]

for i in range(len(distribution)):

layer_parameter={}

for j in distribution[i].keys():

if j=='b':

layer_parameter['b'] = init_parameters_b(i)

continue;

if j=='w':

layer_parameter['w'] = init_parameters_w(i)

continue

parameter.append(layer_parameter)

return parameter

預測函數

def predict(img,init_parameters):

l0_in = img+parameters0

l0_out = activation

l0_in

l1_in = np.dot(l0_out,parameters1)+parameters1

l1_out = activation

1

return l1_out

先定義兩個激活函數的導數,導數的具體推到過程在這裡不呈現,感興趣的同學可以自行搜尋。

def d_softmax(data):

sm = softmax(data)

return np.diag(sm)-np.outer(sm,sm)

def d_tanh(data):

return 1/(np.cosh(data))**2

differential = {softmax:d_softmax,tanh:d_tanh}

其中tanh的導數 是 np.diag(1/(np.cosh(data))2) ,進行優化後的結果是 1/(np.cosh(data))2

注:diag生成對角矩陣 ,outer函數的作用是第一個參數挨個乘以第二個參數得到矩陣

然後定義一個字典,并将數解析為某一位置為1的一維矩陣

onehot = np.identity(dimensions[-1])

求平方差函數,其中parameters是我們在第一次課程定義的那個初始化的參數,在訓練的過程中,會自動更新。

def sqr_loss(img,lab,parameters):

y_pred = predict(img,parameters)

y = onehot[lab]

diff = y-y_pred

return np.dot(diff,diff)

計算梯度

def grad_parameters(img,lab,init_parameters):

l0_in

diff = onehot[lab]-l1_out

act1 = np.dot(differential[activation[1]](l1_in),diff)

grad_b1 = -2*act1

grad_w1 = -2*np.outer(l0_out,act1)

與上文優化d_tanh有關,将矩陣乘法化為數組乘以矩陣

grad_b0 = -2differential[activation[0]](l0_in)np.dot(parameters1,act1)

return {'b1':grad_b1,'w1':grad_w1,'b0':grad_b0}

這次的梯度計算公式用到了公式:(y_predict-y)^2,根據複合函數求導,是以有-2(y_prdict-y)乘以相關的導數,這也是grad_b1後面-2的來曆。

按理說應該更加導數的定義[f(x+h)-f(x)]/h驗證下我們的梯度求的對不對,為了照顧新手同學對神經網絡的了解過程,這一步在這兒省略了哈。

下面進入訓練環節,我們将資料以batch的方式輸入,每個batch定位包含100個圖檔。 batch_size=100 。梯度的擷取是用平均求得的,代碼展現在: grad_accu[key]/=batch_size。

def train_batch(current_batch,parameters):

grad_accu = grad_parameters(train_img[current_batchbatch_size+0],train_lab[current_batchbatch_size+0],parameters)

for img_i in range(1,batch_size):

grad_tmp = grad_parameters(train_img[current_batchbatch_size+img_i],train_lab[current_batchbatch_size+img_i],parameters)

for key in grad_accu.keys():

grad_accu[key] += grad_tmp[key]

grad_accu[key]/=batch_size

return grad_accu

import copy

def combine_parameters(parameters,grad,learn_rate):

parameter_tmp = copy.deepcopy(parameters)

parameter_tmp0 -= learn_rate*grad['b0']

parameter_tmp1 -= learn_rate*grad['b1']

parameter_tmp1 -= learn_rate*grad['w1']

return parameter_tmp

采用copy機制,是避免parameters變化影響全局的訓練, copy.deepcopy 可以重新拷貝不影響原來的資料。

并且這裡用到了公式:

計算神經網絡:資料讀取與完成訓練初始化參數b初始化參數w初始化參數方法預測函數與上文優化d_tanh有關,将矩陣乘法化為數組乘以矩陣

然後定義學習率:

def learn_self(learn_rate):

for i in range(train_num//batch_size):

if i%100 == 99:

print("running batch {}/{}".format(i+1,train_num//batch_size))

grad_tmp = train_batch(i,parameters)

global parameters

parameters = combine_parameters(parameters,grad_tmp,learn_rate)

裡面的if語句可以讓我們看到神經網絡訓練的進度。

計算神經網絡:資料讀取與完成訓練初始化參數b初始化參數w初始化參數方法預測函數與上文優化d_tanh有關,将矩陣乘法化為數組乘以矩陣

到這裡,我們就完成了神經網絡的一次訓練,為了驗證準确度如何,我們可以用驗證集看看準确度如何。

定義驗證集的損失:

def valid_loss(parameters):

loss_accu = 0

for img_i in range(valid_num):

loss_accu+=sqr_loss(valid_img[img_i],valid_lab[img_i],parameters)

return loss_accu

計算準确度:

def valid_accuracy(parameters):

correct = [predict(valid_img[img_i],parameters).argmax()==valid_lab[img_i] for img_i in range(valid_num) ]

print("validation accuracy:{}".format(correct.count(True)/len(correct)))

最後得到結果:

計算神經網絡:資料讀取與完成訓練初始化參數b初始化參數w初始化參數方法預測函數與上文優化d_tanh有關,将矩陣乘法化為數組乘以矩陣

有90%的準确度哎~結果還好,還好,畢竟沒有怎麼調學習率以及解決過拟合。

好了,這一期的内容就到這了,内容有些多大家多多消化,下一期我們講講怎麼調節學習率以及看看更複雜的神經網絡。

*注:此篇文章受B站up主大野喵渣的啟發,并參考了其代碼,感興趣的同學可以去B站觀看他關于神經網絡的教學視訊,以及到他的Github位址逛逛。

視訊位址與Github:

https://www.bilibili.com/video/av51197008 https://github.com/YQGong

繼續閱讀