三层神经网络实现手写数字图像分类
數據集采用MNIST。MNIST 數據集包含 4 個文件,分別是訓練集圖像、訓練集 標記、測試集圖像、測試集標記。每個樣本都由灰度圖像(即單通道圖像)及其 標記組成,圖像大小為 28 × 28。
完整代碼見:
鏈接:https://pan.baidu.com/s/1t40bza30W8R58xZW5FbvVw?
提取碼:cbmm
一、整體框架
設置五大模塊,模塊化,便于迭代。
1) 數據加載模塊:從文件中讀取數據,并進行預處理,其中預處理包括歸一化、維度變換等處理。如果需要人為對數據進行隨機數據擴增,則數據擴增處理也在數據加載模塊中實現。
2) 基本單元模塊:實現神經網絡中不同類型的網絡層的定義、前向傳播計算、反向傳播計算等功能。
3) 網絡結構模塊:利用基本單元模塊建立一個完整的神經網絡。
4) 網絡訓練(training)模塊:該模塊實現用訓練集進行神經網絡訓練的功能。在已建立的神經網絡結構基礎上,實現神經網絡的前向傳播、神經網絡的反向傳播、對神經網絡進行參數更新、保存神經網絡參數等基本操作,以及訓練函數主體。
5) 網絡推斷(inference)模塊:該模塊實現使用訓練得到的網絡模型,對測試樣本進行預測的過程。
二、數據加載模塊
首先根據MNIST的IDX文件格式進行數據的讀取
MNIST_DIR = "../mnist_data"TRAIN_DATA = "train-images-idx3-ubyte"TRAIN_LABEL = "train-labels-idx1-ubyte"TEST_DATA = "t10k-images-idx3-ubyte"TEST_LABEL = "t10k-labels-idx1-ubyte"def load_mnist(self, file_dir, is_images = 'True'):# Read binary databin_file = open(file_dir, 'rb')bin_data = bin_file.read()bin_file.close()# Analysis file headerif is_images:# 讀取圖像數據fmt_header = '>iiii'magic, num_images, num_rows, num_cols = struct.unpack_from(fmt_header, bin_data, 0)else:# 讀取標記數據fmt_header = '>ii'magic, num_images = struct.unpack_from(fmt_header, bin_data, 0)num_rows, num_cols = 1, 1data_size = num_images * num_rows * num_colsmat_data = struct.unpack_from('>' + str(data_size) + 'B', bin_data, struct.calcsize(fmt_header))mat_data = np.reshape(mat_data, [num_images, num_rows * num_cols])print('Load images from %s, number: %d, data shape: %s' % (file_dir, num_images, str(mat_data.shape)))return mat_datadef load_data(self):# TODO: 調用函數 load_mnist 讀取和預處理 MNIST 中訓練數據和測試數據的圖像和標記print('Loading MNIST data from files...')train_images = self.load_mnist(os.path.join(MNIST_DIR, TRAIN_DATA), True)train_labels = self.load_mnist(os.path.join(MNIST_DIR, TRAIN_LABEL), False)test_images = self.load_mnist(os.path.join(MNIST_DIR, TEST_DATA), True)test_labels = self.load_mnist(os.path.join(MNIST_DIR, TEST_LABEL), False)self.train_data = np.append(train_images, train_labels, axis=1)self.test_data = np.append(test_images, test_labels, axis=1)三、基本單元模塊
?實現全連接、激活函數等層的定義、前向傳播、反向傳播等具體操作。
(1)采用三層神經網絡,主體是三個全連接層。
(2)在前兩個全連接層之后使用 ReLU 激活函數層引入非線性變換。
(3)在神經網絡最后添加Softmax 層計算交叉熵損失。
同類型的層用一個類來定義,多個同類型的層用類的實例來實現,層中的計算用類的成員函數來定義(計算包括初始化、前向計算、反向計算、參數更新等)。
?3.1 全連接層
(1)層的初始化:輸入神經元數量、輸出神經元數量
(2)參數初始化:權重W和偏置b。在對權重和偏置進行初始化時,通常利用高斯隨機數初始化權重的值,而將偏置的所有值初始化為0。
(3)前向計算:Y = XW + b
(4)反向計算:
關于幾個式子可能有人會有疑問,到底是誰左乘誰,為什么莫名奇妙出了轉置?
這個地方給一個我常用的分析辦法:W矩陣是mxn,求導后依然為mxn,X為1Xm的,L對y的偏導為1xn的,因此使用X^T乘以L對y的偏導。
分析辦法詳細見:反向傳播算法的矩陣維度分析 - 知乎
詳細的推導見:神經網絡的反向傳播算法中矩陣的求導方法(矩陣求導總結)_ASR_THU的博客-CSDN博客_神經網絡矩陣求導
?(5)參數更新:采用梯度下降法進行更新即可。
class FullyConnectedLayer(object):def __init__(self, num_input, num_output): # 全連接層初始化self.num_input = num_inputself.num_output = num_outputprint('\tFully connected layer with input %d, output %d.' % (self.num_input, self.num_output))def init_param(self, std=0.01): # 參數初始化self.weight = np.random.normal(loc=0.0, scale=std, size=(self.num_input, self.num_output))self.bias = np.zeros([1, self.num_output])def forward(self, input): # 前向傳播計算start_time = time.time()self.input = input# TODO:全連接層的前向傳播,計算輸出結果self.output = np.matmul(self.input, self.weight) + self.biasreturn self.outputdef backward(self, top_diff): # 反向傳播的計算# TODO:全連接層的反向傳播,計算參數梯度和本層損失self.d_weight = np.dot(self.input.T, top_diff)self.d_bias = np.sum(top_diff, axis=0)bottom_diff = np.dot(top_diff, self.weight.T)return bottom_diffdef update_param(self, lr): # 參數更新# TODO:對全連接層參數利用參數進行更新self.weight = self.weight - lr * self.d_weightself.bias = self.bias - lr * self.d_biasdef load_param(self, weight, bias): # 參數加載assert self.weight.shape == weight.shapeassert self.bias.shape == bias.shapeself.weight = weightself.bias = biasdef save_param(self): # 參數保存return self.weight, self.bias3.2 ReLu激活函數
前向傳播時,大于0則為x不變,小于0則為0。
反向傳播求導為1。
class ReLULayer(object):def __init__(self):print('\tReLU layer.')def forward(self, input): # 前向傳播的計算start_time = time.time()self.input = input# TODO:ReLU層的前向傳播,計算輸出結果output = np.maximum(0, self.input)return outputdef backward(self, top_diff): # 反向傳播的計算# TODO:ReLU層的反向傳播,計算本層損失bottom_diff = top_diffbottom_diff[self.input < 0] = 0return bottom_diff3.3 SoftMax層
Softmax常用于多分類問題。假設 Softmax 損失層的輸入為向量 x,維度為 k。其中 k 對應分類的類別數,如對手寫數字 0 至 9 進行分類時,類別數 k = 10。
(1)前向傳播:
在前向傳播的計算過程中,首先對 x 計算 e 指數并進行行歸一化,從而得 到 Softmax 分類概率。計算公式為:
在實際的實現中使用批量隨機梯度下降算法,假設選擇的樣本量為 p,Softmax 損失層 的輸入變為二維矩陣 X,維度為 p × k,X 的每個行向量代表一個樣本。則對每個樣本的激活值計算 e 指數并進行行歸一化得到:
其中 X(i, j) 代表 X 中對應第 i 樣本 j 位置的值。當 X(i, j) 數值較大時,求 e 指數可能會出現數值上溢的問題。因此在實際工程實現時,為確保數值穩定性,會在求 e 指數前先進行減最大值處理,此時計算公式變為:
在前向計算時,對 Softmax 分類概率取最大概率對應的類別作為預測的分類類別。
(2)損失函數:
損失函數層在計算前向傳播時還需要根據真實值y計算總的損失函數值。在分類任務中, y 通常表示為一個維度為 k 的 one-hot 向量,該向量中對應真實類別的分量值為 1,其他值為 0。?
采用交叉熵損失函數:
?由于此時y是一個one-hot向量,上式應為:
(3)反向傳播:
在反向傳播的計算過程中,可直接利用標記數據和損失函數層的輸出計算本層輸入的損失(損失 是所有樣本的平均損失,因此對樣本數量 p 取平均):
class SoftmaxLossLayer(object):def __init__(self):print('\tSoftmax loss layer.')def forward(self, input): # 前向傳播的計算# TODO:softmax 損失層的前向傳播,計算輸出結果input_max = np.max(input, axis=1, keepdims=True)input_exp = np.exp(input - input_max)self.prob = input_exp / np.sum(input_exp, axis=1, keepdims=True)return self.probdef get_loss(self, label): # 計算損失self.batch_size = self.prob.shape[0]self.label_onehot = np.zeros_like(self.prob)self.label_onehot[np.arange(self.batch_size), label] = 1.0loss = -np.sum(np.log(self.prob) * self.label_onehot) / self.batch_sizereturn lossdef backward(self): # 反向傳播的計算# TODO:softmax 損失層的反向傳播,計算本層損失bottom_diff = (self.prob - self.label_onehot) / self.batch_sizereturn bottom_diff四、網絡結構模塊
4.1 神經網絡初始化
確定神經網絡相關的超參數,例如網絡中每個隱層的神經元個數。
def __init__(self, batch_size=100, input_size=784, hidden1=32, hidden2=16, out_classes=10, lr=0.01, max_epoch=1, print_iter=100):self.batch_size = batch_sizeself.input_size = input_sizeself.hidden1 = hidden1self.hidden2 = hidden2self.out_classes = out_classesself.lr = lrself.max_epoch = max_epochself.print_iter = print_iter4.2 建立網絡結構
定義整個神經網絡的拓撲結構,實例化基本單元模塊中定義的層并將這些層進行堆疊。本實驗使用的三層神經網絡包含三個全連接層,并且在前兩個全連接層后跟隨有 ReLU 層,神經網絡的最后使用了 Softmax 損失層。
def build_model(self): # 建立網絡結構# TODO:建立三層神經網絡結構print('Building multi-layer perception model...')self.fc1 = FullyConnectedLayer(self.input_size, self.hidden1)self.relu1 = ReLULayer()self.fc2 = FullyConnectedLayer(self.hidden1, self.hidden2)self.relu2 = ReLULayer()self.fc3 = FullyConnectedLayer(self.hidden2, self.out_classes)self.softmax = SoftmaxLossLayer()self.update_layer_list = [self.fc1, self.fc2, self.fc3]4.3 神經網絡參數初始化
對于神經網絡中包含參數的層,依次調用這些層的參數初始化函數,從而完成整個神經網絡的參數初始化。本實驗使用的三層神經網絡中,只有三個全連接層包含參數,依次調用其參數初始化函數即可.
def init_model(self):print('Initializing parameters of each layer in MLP...')for layer in self.update_layer_list:layer.init_param()五、網絡訓練模塊
神經網絡的訓練模塊通常拆解為若干步驟,包括神經網絡的前向傳播、神經網絡的反向傳播、神經 網絡參數更新、神經網絡參數保存等基本操作。
按照神經網絡的結構依次進行實現即可。
六、網絡推斷模塊
調用訓練好的網絡模型,對測試數據進行預測,以評估模型的精度。
?運行結果:
總結
以上是生活随笔為你收集整理的三层神经网络实现手写数字图像分类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 西威变频器使用说明书_西威变频器说明书西
- 下一篇: JAD文件各属性解释