python: MNIST-神經網絡的自主學習


 

問題:如何實現數字“5”的識別?O(∩_∩)O~

                                 手寫數字“5”的例子:寫法因人而異,五花八門

方案: 從圖像中提取特征量-----用及其學習技術學習這些特征量的模式

神經網絡的學習中所用到的指標稱為損失函數

可以用作損失函數的函數很多,最有名的是均方誤差(mean squared error)。

  均方誤差的表達式:

    

    yk  -----------神經網絡輸出

    tk  -----------監督數據,one-hot表示

  這里的神經網絡的輸出y是softmax函數的輸出。softmax函數的輸出可以理解為概率

  用python實現均方誤差:   

def mean_squared_error(y-t): return 0.5*np.sum((y-t)**2)

除了均方誤差,交叉熵誤差(cross entropy error)也經常被用作損失函數。
  交叉熵誤差的表達式:

    

    比如,假設正確解標簽的索引是“2”,與之對應的神經網絡的輸出是 0.6,則交叉熵誤差是 -log 0.6 = 0.51;若“2”對應的輸出是 0.1,則交叉熵誤差為 -log 0.1 = 2.30。也就是說,交叉熵誤差的值是由正確解標簽所對應的輸出結果決定的

 

 

自然對數的圖像如圖所示:

 1 import matplotlib.pyplot as plt
 2 import numpy as np
 3 
 4 #生成數據
 5 x=np.arange(0.01,1.01,0.01)
 6 y=np.log(x)
 7 
 8 #繪制圖像
 9 plt.plot(x,y)
10 plt.xlabel('x')
11 plt.ylabel('y')
12 plt.show()

代碼實現交叉熵誤差:

def cross_entropy_error(y,t):
    delta=1e-7
    return -np.sum(t*np.log(y+delta))

 

如果計算所有訓練數據的損失函數的綜合,則公式為:

  

上述的損失函數都是針對單個數據,加入訓練數據一共有6000個,那我們每次拿出100個當做是6000的近似計算一次,這樣計算得到的結果就是mini-batch學習的結果。

在之前的筆記中已經知道了讀入MNIST數據集的代碼,即:

import sys, os
sys.path.append(os.pardir)
import numpy as np
from dataset.mnist import load_mnist

(x_train, t_train), (x_test, t_test) = \
    load_mnist(normalize=True, one_hot_label=True)

print(x_train.shape) # (60000, 784)
print(t_train.shape) # (60000, 10)

mnist數據下載的代碼mnist.py如下:

  1 # coding: utf-8
  2 try:
  3     import urllib.request
  4 except ImportError:
  5     raise ImportError('You should use Python 3.x')
  6 import os.path
  7 import gzip
  8 import pickle
  9 import os
 10 import numpy as np
 11 
 12 
 13 url_base = 'http://yann.lecun.com/exdb/mnist/'
 14 key_file = {
 15     'train_img':'train-images-idx3-ubyte.gz',
 16     'train_label':'train-labels-idx1-ubyte.gz',
 17     'test_img':'t10k-images-idx3-ubyte.gz',
 18     'test_label':'t10k-labels-idx1-ubyte.gz'
 19 }
 20 
 21 dataset_dir = os.path.dirname(os.path.abspath(__file__))
 22 save_file = dataset_dir + "/mnist.pkl"
 23 
 24 train_num = 60000
 25 test_num = 10000
 26 img_dim = (1, 28, 28)
 27 img_size = 784
 28 
 29 
 30 def _download(file_name):
 31     file_path = dataset_dir + "/" + file_name
 32     
 33     if os.path.exists(file_path):
 34         return
 35 
 36     print("Downloading " + file_name + " ... ")
 37     urllib.request.urlretrieve(url_base + file_name, file_path)
 38     print("Done")
 39     
 40 def download_mnist():
 41     for v in key_file.values():
 42        _download(v)
 43         
 44 def _load_label(file_name):
 45     file_path = dataset_dir + "/" + file_name
 46     
 47     print("Converting " + file_name + " to NumPy Array ...")
 48     with gzip.open(file_path, 'rb') as f:
 49             labels = np.frombuffer(f.read(), np.uint8, offset=8)
 50     print("Done")
 51     
 52     return labels
 53 
 54 def _load_img(file_name):
 55     file_path = dataset_dir + "/" + file_name
 56     
 57     print("Converting " + file_name + " to NumPy Array ...")    
 58     with gzip.open(file_path, 'rb') as f:
 59             data = np.frombuffer(f.read(), np.uint8, offset=16)
 60     data = data.reshape(-1, img_size)
 61     print("Done")
 62     
 63     return data
 64     
 65 def _convert_numpy():
 66     dataset = {}
 67     dataset['train_img'] =  _load_img(key_file['train_img'])
 68     dataset['train_label'] = _load_label(key_file['train_label'])    
 69     dataset['test_img'] = _load_img(key_file['test_img'])
 70     dataset['test_label'] = _load_label(key_file['test_label'])
 71     
 72     return dataset
 73 
 74 def init_mnist():
 75     download_mnist()
 76     dataset = _convert_numpy()
 77     print("Creating pickle file ...")
 78     with open(save_file, 'wb') as f:
 79         pickle.dump(dataset, f, -1)
 80     print("Done!")
 81 
 82 def _change_one_hot_label(X):
 83     T = np.zeros((X.size, 10))
 84     for idx, row in enumerate(T):
 85         row[X[idx]] = 1
 86         
 87     return T
 88     
 89 
 90 def load_mnist(normalize=True, flatten=True, one_hot_label=False):
 91     """讀入MNIST數據集
 92     
 93     Parameters
 94     ----------
 95     normalize : 將圖像的像素值正規化為0.0~1.0
 96     one_hot_label : 
 97         one_hot_label為True的情況下,標簽作為one-hot數組返回
 98         one-hot數組是指[0,0,1,0,0,0,0,0,0,0]這樣的數組
 99     flatten : 是否將圖像展開為一維數組
100     
101     Returns
102     -------
103     (訓練圖像, 訓練標簽), (測試圖像, 測試標簽)
104     """
105     if not os.path.exists(save_file):
106         init_mnist()
107         
108     with open(save_file, 'rb') as f:
109         dataset = pickle.load(f)
110     
111     if normalize:
112         for key in ('train_img', 'test_img'):
113             dataset[key] = dataset[key].astype(np.float32)
114             dataset[key] /= 255.0
115             
116     if one_hot_label:
117         dataset['train_label'] = _change_one_hot_label(dataset['train_label'])
118         dataset['test_label'] = _change_one_hot_label(dataset['test_label'])
119     
120     if not flatten:
121          for key in ('train_img', 'test_img'):
122             dataset[key] = dataset[key].reshape(-1, 1, 28, 28)
123 
124     return (dataset['train_img'], dataset['train_label']), (dataset['test_img'], dataset['test_label']) 
125 
126 
127 if __name__ == '__main__':
128     init_mnist()

minst.py在文件夾dataset中,數據在dataset下的mnist.pkl文件中。

目前我還是自己寫不出來這樣的代碼,還是得分析透才能學會,這里先留一下,下次來分析......\

 

那么,如何從訓練數據中隨機抽取10筆數據呢?可以使用NumPy的no.random.choice(),攜程如下形式:

train_size=x_train.shape[0]
batch_size=10
batch_mask=np.random.choice(train_size,batch_size)
x_batch=x_train[batch_mask]
t_batch=t_train[batch_mask]


mini_batch交叉熵的實現:

def cross_entropy_error(y,t):
    if y.ndim==1:
        t=t.reshape(1,t.size)
        y=y.reshape(1,y.size)
        
    batch_size=y.shape[0]
    return -np.sum(t*np.log(y+1e-7))/batch_size

這里,y 是神經網絡的輸出,t 是監督數據。y 的維度為 1 時,即求單個數據的交叉熵誤差時,需要改變數據的形狀。並且,當輸入為 mini-batch 時,要用 batch 的個數進行正規化,計算單個數據的平均交叉熵誤差。

 

當監督數據是標簽形式而不是one-hot表示時,交叉熵代碼如下:

def cross_entropy_error(y,t):
    if y.ndim==1:
        t=t.reshape(1,t.size)
        y=y.reshape(1,y.size)
        
    batch_size=y.shape[0]
    return -np.sum(np.log(y[np.arange(batch_size),t]+1e-7))/batch_size

實現的要點是,由於 one-hot 表示中 t 為 0 的元素的交叉熵誤差也為 0,因此針對這些元素的計算可以忽略。換言之,如果可以獲得神經網絡在正確解標簽處的輸出,就可以計算交叉熵誤差。因此,t 為 one-hot 表示時通過 t * np.log(y) 計算的地方,在 t 為標簽形式時,可用 np.log( y[np.arange (batch_size), t] ) 實現相同的處理(為了便於觀察,這里省略了微小值1e-7)。

作為參考,簡單介紹一下np.log( y[np.arange(batch_size), t] )np.arange (batch_size) 會生成一個從 0 到 batch_size-1 的數組。比如當 batch_size 為 5 時,np.arange(batch_size) 會生成一個 NumPy 數組 [0, 1, 2, 3, 4]。因為 t 中標簽是以 [2, 7, 0, 9, 4] 的形式存儲的,所以 y[np.arange(batch_size), t] 能抽出各個數據的正確解標簽對應的神經網絡的輸出(在這個例子中,y[np.arange(batch_size), t] 會生成 NumPy 數組 [y[0,2], y[1,7], y[2,0], y[3,9], y[4,4]])。

 

 

這部分理解起來並不是很容易,還是得多回顧才能真正掌握。均方誤差、交叉熵誤差以及下載mnist數據集,這些里面后兩部分還得在后續的學習中繼續補充。

 

一定要加油啊,向目標前進~~~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM