一.概述
????????在文章【機器學習】一個例子帶你了解神經網絡是什么中,我們大致了解神經網絡的正向信息傳導、反向傳導以及學習過程的大致流程,現在我們正式開始進行代碼的實現,首先我們來實現第一步的運算過程模擬講解:正向傳導。本次代碼實現將以“手寫數字識別”為例子。
二.測試訓練數據集的獲取
? ? ? ? 首先我們需要通過官網獲取到手寫數字識別數據集,數據集一共分為四個部分,分別是訓練集的圖片(六萬張)、訓練集的標簽、測試集的圖片(一萬張)以及測試集的標簽。所以我們在代碼中可以使用鍵值表示對應的key-value:
url_base = 'http://yann.lecun.com/exdb/mnist/'
key_file = {'train_img':'train-images-idx3-ubyte.gz','train_label':'train-labels-idx1-ubyte.gz','test_img':'t10k-images-idx3-ubyte.gz','test_label':'t10k-labels-idx1-ubyte.gz'
}
? ? ? ? 同時,我們需要將下載的文件保存到與代碼同一級目錄下:
dataset_dir = os.path.dirname(os.path.abspath(__file__))
? ? ? ? 下載部分十分簡單么,就不在此贅述,需要注意的是代碼使用了python的urlretrieve函數,該函數需要使用頭文件urllib.request,需要自行下載:
def download_mnist():for filename in key_file.values():file_path = dataset_dir + "/" + filenameif os.path.exists(file_path):returnprint("Downloading " + filename + " ... ")urllib.request.urlretrieve(url_base + filename, file_path)print("Done")
三.測試訓練數據集的加載
? ? ? ? 下載完數據集后,我們需要將其加載到我們的程序中以供后續的使用,首先是判斷一下我們是否已經下載過數據集,如果沒有下載,則先進行下載操作,再執行其他步驟:
if not os.path.exists(save_file) :download_mnist()dataset = _convert_numpy()print("Creating pickle file ...")with open(save_file, 'wb') as f:pickle.dump(dataset, f, -1)print("Done!")
? ? ? ? 以上代碼有個需要注意的地方,因為下載完數據集之后無法直接給到python使用,所以還需要對數據進行格式處理,處理成python可以識別的格式,這一步交由函數_convert_numpy實現:
def _convert_numpy(): dataset = {}dataset['train_img'] = _load_img(key_file['train_img'])dataset['train_label'] = _load_label(key_file['train_label'])dataset['test_img'] = _load_img(key_file['test_img'])dataset['test_label'] = _load_label(key_file['test_label'])return dataset
? ? ? ?其中,_load_img函數負責處理圖片數據:
def _load_img(file_name):file_path = dataset_dir + "\\MNIST\\" + file_nameprint("Converting " + file_name + " to NumPy Array ...")with gzip.open(file_path, 'rb') as f:data = np.frombuffer(f.read(), np.uint8, offset=16)data = data.reshape(-1, img_size)print("Done")return data
????????其中,_load_label函數負責處理標簽數據:
def _load_label(file_name):file_path = dataset_dir + "\\MNIST\\" + file_nameprint("Converting " + file_name + " to NumPy Array ...")with gzip.open(file_path, 'rb') as f:labels = np.frombuffer(f.read(), np.uint8, offset=8)print("Done")return labels
? ? ? ? 函數中使用到的都是一些python常用的函數,所以具體作用不在贅述,可自行查詢。介紹完_convert_numpy函數,我們繼續回到數據集加載函數本身,為了方便后續數據集的批量調用等操作,我們需要在加載數據后對其進行進一步的數據清洗整理等預處理,分別為數據歸一化(normalize)、圖像展開(flatten)以及圖像標簽對應(one_hot_label),先將三個功能代碼貼上,然后我們再詳細講解各個功能的具體作用:
with open(save_file,'rb') as f:dataset = pickle.load(f)if normalize:for key in ['train_img','test_img']:dataset[key] = dataset[key].astype(np.float32)if not flatten:for key in ('train_img', 'test_img'):dataset[key] = dataset[key].reshape(-1, 1, 28, 28)if one_hot_label:dataset['train_label'] = _change_one_hot_label(dataset['train_label'])dataset['test_label'] = _change_one_hot_label(dataset['test_label'])
3.1.數據歸一化(normalize)
? ? ? ? 數據歸一化normalize如果設置為True,可以將輸入圖像歸一化為0.0~1.0 的值。如果將該參數設置為False,則輸入圖像的像素會保持原來的0~255。函數實現是使用了python函數中的astype功能將數據,用于將數據集指定字段的數據轉換為?float32
?類型,常見于深度學習模型輸入前的數據預處理。
dataset[key] = dataset[key].astype(np.float32)
3.2.圖像展開(flatten)
? ? ? ? 圖像展開flatten用于設置是否展開輸入圖像使其變成一維數組。如果將該參數設置為False,則輸入圖像為1 × 28 × 28 的三維數組;若設置為True,則輸入圖像會保存為由784 個元素構成的一維數組。函數實現也只是使用到深度學習中常用的reshape函數:
dataset[key] = dataset[key].reshape(-1, 1, 28, 28)
3.3.圖像標簽對應(one_hot_label)
????????圖像標簽對應one_hot_label用于設置是否將標簽保存為onehot表示(one-hot representation)。one-hot 表示是僅正確解標簽為1,其余皆為0 的數組,就像[0,0,1,0,0,0,0,0,0,0]這樣。當one_hot_label為False時,就是像7、2這樣簡單保存正確解標簽,函數_change_one_hot_label的實現如下:
def _change_one_hot_label(X):T = np.zeros((X.size, 10))for idx, row in enumerate(T):row[X[idx]] = 1return T
? ? ? ? 以上即為測試訓練數據集加載函數的全部內容,我們將在下面正式調用一下看看是否能夠正常工作,在此貼上函數全文:
ef load_mnist(normalize=True, flatten=True, one_hot_label=False):if not os.path.exists(save_file) :download_mnist()dataset = _convert_numpy()print("Creating pickle file ...")with open(save_file, 'wb') as f:pickle.dump(dataset, f, -1)print("Done!")with open(save_file,'rb') as f:dataset = pickle.load(f)if normalize:for key in ['train_img','test_img']:dataset[key] = dataset[key].astype(np.float32)if not flatten:for key in ('train_img', 'test_img'):dataset[key] = dataset[key].reshape(-1, 1, 28, 28)if one_hot_label:dataset['train_label'] = _change_one_hot_label(dataset['train_label'])dataset['test_label'] = _change_one_hot_label(dataset['test_label'])return (dataset['train_img'],dataset['train_label']),(dataset['test_img'],dataset['test_label'])
四.測試訓練數據集的使用測試
? ? ? ? 我們可以加載數據集并且查看到各個數據集的形狀:
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True,normalize=False)
# 輸出各個數據的形狀
print(x_train.shape) # (60000, 784)
print(t_train.shape) # (60000,)
print(x_test.shape) # (10000, 784)
print(t_test.shape) # (10000,)
? ? ? ? 根據輸出我們可以看到,訓練集圖片有六萬張,每張圖片有784各像素(28*28),訓練集標簽和照片數量一樣(那是肯定的),測試集圖片和標簽數量比訓練集的少,主要用來驗證模型學習后的正確性。
? ? ? ? 我們甚至還能隨機從數據集中抽取一張照片查看一下實際樣子,具體實現如下:
def img_show(img):
pil_img = Image.fromarray(np.uint8(img))
pil_img.show()
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True,normalize=False)
img = x_train[0]
label = t_train[0]
print(label) # 5
print(img.shape) # (784,)
img = img.reshape(28, 28) # 把圖像的形狀變成原來的尺寸
print(img.shape) # (28, 28)
img_show(img)
? ? ? ? 輸出的圖片如圖下所示:
? ? ? ? 在后面的文章中,我們將開始正式步入主題,講解神經網絡如何學習,各層次之間如何傳遞數值,如何反向傳導,計算損失,又在重新學習,最終實現傳入一張手寫數字就能自動識別出具體的數字的。