問題:如何實現數字“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數據集,這些里面后兩部分還得在后續的學習中繼續補充。
一定要加油啊,向目標前進~~~