c++读取图片_手工计算神经网络第三期:数据读取与完成训练
大數據文摘出品
作者:蔣寶尚
小伙伴們大家好呀~~用Numpy搭建神經網絡,我們已經來到第三期了。第一期文摘菌教大家如何用Numpy搭建一個簡單的神經網絡,完成了前饋部分。第二期為大家帶來了梯度下降相關的知識點。
這一期,教大家如何讀取數據集,以及將數據集用于神經網絡的訓練,和上兩期一樣,這次依然用Numpy實現。在開始代碼之前,文摘菌先帶大家看看今天我們使用的數據集。
數據集介紹
數據集采用著名的MNIST的手寫數據集。根據官網介紹,這個數據集有70000個樣本,包括60000個訓練樣本,10000個測試樣本。
數據集下載下來之后,文件分為4個部分,分別是:訓練集圖片、訓練集標簽、測試集圖片、測試集標簽。這些數據以二進制的格式儲存。
? ? ?
其中,訓練集圖片文件的前16個字節是儲存了圖片的個數,行數以及列數等。訓練集標簽文件前8個字節儲存了圖片標簽的個數等。測試集的兩個文件同理。
文摘菌下載好的文件存儲地址
讀取數據
train_img_path=r'C:\Users\Dell\MNIST\train-images.idx3-ubyte'train_lab_path=r'C:\Users\Dell\MNIST\train-labels.idx1-ubyte'test_img_path=r'C:\Users\Dell\MNIST\t10k-images.idx3-ubyte'test_lab_path=r'C:\Users\Dell\MNIST\t10k-labels.idx1-ubyte'根據文件在本地解壓后的儲存地址,生成四個地址,上面代碼中‘r’是轉義字符,因為\在Python中有特殊的用法,所以需用轉義字符明確文件地址。
為了讓后面的模型表現更好,我們將訓練集拆分,拆成50000個訓練集和10000個驗證集。
注:驗證集是模型訓練過程中單獨留出的樣本集,它可以用于調整模型的超參數和用于對模型的能力進行初步評估。
import?structtrain_num=50000valid_num=10000test_num=10000with 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: struct.unpack('>4i',f.read(16)) 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: struct.unpack('>2i',f.read(8))????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 pltdef show_train(index): plt.imshow(train_img[index].reshape(28,28),cmap='gray') print('label:{}'.format(train_lab[index]))def show_test(index): plt.imshow(train_img[index].reshape(28,28),cmap='gray') 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',圖片的底色會非常奇怪。
? ? ? ?? ? ? ?
測試一下,定義完函數之后,顯示的是這樣的~
數據顯示和讀取完成,接下來開始訓練參數。
訓練數據
在開始之前,為了能夠上下銜接,我們把第一次課程的代碼貼上來~
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]))]}]# 初始化參數bdef init_parameters_b(layer): dist = distribution[layer]['b'] return np.random.rand(dimensions[layer])*(dist[1]-dist[0])+dist[0]# 初始化參數wdef init_parameters_w(layer): dist = distribution[layer]['w'] 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+parameters[0]['b'] l0_out = activation[0](l0_in) l1_in = np.dot(l0_out,parameters[1]['w'])+parameters[1]['b'] l1_out = activation[1](l1_in)??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))**2differential = {softmax:d_softmax,tanh:d_tanh}其中tanh的導數 是np.diag(1/(np.cosh(data))**2),進行優化后的結果是1/(np.cosh(data))**2
注:diag生成對角矩陣?,outer函數的作用是第一個參數挨個乘以第二個參數得到矩陣
然后定義一個字典,并將數解析為某一位置為1的一維矩陣
differential?=?{softmax:d_softmax,tanh:d_tanh}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 = img+parameters[0]['b'] l0_out = activation[0](l0_in) l1_in = np.dot(l0_out,parameters[1]['w'])+parameters[1]['b'] l1_out = activation[1](l1_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?=?-2*differential[activation[0]](l0_in)*np.dot(parameters[1]['w'],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_batch*batch_size+0],train_lab[current_batch*batch_size+0],parameters) for img_i in range(1,batch_size): grad_tmp = grad_parameters(train_img[current_batch*batch_size+img_i],train_lab[current_batch*batch_size+img_i],parameters) for key in grad_accu.keys(): grad_accu[key] += grad_tmp[key] for key in grad_accu.keys(): grad_accu[key]/=batch_size return grad_accuimport copydef combine_parameters(parameters,grad,learn_rate): parameter_tmp = copy.deepcopy(parameters) parameter_tmp[0]['b'] -= learn_rate*grad['b0'] parameter_tmp[1]['b'] -= learn_rate*grad['b1'] parameter_tmp[1]['w'] -= learn_rate*grad['w1'] return parameter_tmp采用copy機制,是避免parameters變化影響全局的訓練,copy.deepcopy可以重新拷貝不影響原來的數據。
并且這里用到了公式:
? ? ? ? ? ? ?
然后定義學習率:
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語句可以讓我們看到神經網絡訓練的進度。
? ? ? ? ? ? ?
到這里,我們就完成了神經網絡的一次訓練,為了驗證準確度如何,我們可以用驗證集看看準確度如何。
定義驗證集的損失:
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)))最后得到結果:
? ? ? ? ? ? ?
有90%的準確度哎~結果還好,還好,畢竟沒有怎么調學習率以及解決過擬合。
好了,這一期的內容就到這了,內容有些多大家多多消化,下一期我們講講怎么調節學習率以及看看更復雜的神經網絡。
*注:此篇文章受B站up主大野喵渣的啟發,并參考了其代碼,感興趣的同學可以去B站觀看他關于神經網絡的教學視頻,以及到他的Github地址逛逛。
視頻地址與Github:
https://www.bilibili.com/video/av51197008
https://github.com/YQGong
實習/全職編輯記者招聘ing
加入我們,親身體驗一家專業科技媒體采寫的每個細節,在最有前景的行業,和一群遍布全球最優秀的人一起成長。坐標北京·清華東門,在大數據文摘主頁對話頁回復“招聘”了解詳情。簡歷請直接發送至zz@bigdatadigest.cn
點「在看」的人都變好看了哦
總結
以上是生活随笔為你收集整理的c++读取图片_手工计算神经网络第三期:数据读取与完成训练的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 版是什么版本的教材_acca教材有哪些版
- 下一篇: tictoc正方形网络模型_反卷积:可视