机器学习入门(19)— 卷积网络 CNN 的简单实现
之前的章節已經實現了卷積層和池化層,現在來組合這些層,搭建進行手寫數字識別的 CNN 。這里要實現如圖7-23 所示的 CNN 。
class SimpleConvNet:"""簡單的ConvNetconv - relu - pool - affine - relu - affine - softmaxParameters----------? input_dim―輸入數據的維度:(通道,高,長)? conv_param―卷積層的超參數(字典)。字典的關鍵字如下:filter_num―濾波器的數量filter_size―濾波器的大小stride―步幅pad―填充? hidden_size―隱藏層(全連接)的神經元數量? output_size―輸出層(全連接)的神經元數量? weitght_int_std―初始化時權重的標準差"""def __init__(self, input_dim=(1, 28, 28), conv_param={'filter_num':30, 'filter_size':5, 'pad':0, 'stride':1},hidden_size=100, output_size=10, weight_init_std=0.01):filter_num = conv_param['filter_num']filter_size = conv_param['filter_size']filter_pad = conv_param['pad']filter_stride = conv_param['stride']# 計算卷積層的輸出大小input_size = input_dim[1]conv_output_size = (input_size - filter_size + 2*filter_pad) / filter_stride + 1pool_output_size = int(filter_num * (conv_output_size/2) * (conv_output_size/2))
卷積層的超參數通過名為 conv_param 的字典傳入。我們設想它會像 {'filter_num':30,'filter_size':5, 'pad':0, 'stride':1} 這樣,保存必要的超參數值。
# 初始化權重self.params = {}self.params['W1'] = weight_init_std * \np.random.randn(filter_num, input_dim[0], filter_size, filter_size)self.params['b1'] = np.zeros(filter_num)self.params['W2'] = weight_init_std * \np.random.randn(pool_output_size, hidden_size)self.params['b2'] = np.zeros(hidden_size)self.params['W3'] = weight_init_std * \np.random.randn(hidden_size, output_size)self.params['b3'] = np.zeros(output_size)
學習所需的參數是第1 層的卷積層和剩余兩個全連接層的權重和偏置。將這些參數保存在實例變量的 params 字典中。
將第 1 層的卷積層的權重設為關鍵字 W1 ,偏置設為關鍵字 b1 。同樣,分別用關鍵字 W2 、b2 和關鍵字W3 、b3 來保存第 2 個和第 3 個全連接層的權重和偏置。
# 生成層self.layers = OrderedDict()self.layers['Conv1'] = Convolution(self.params['W1'], self.params['b1'],conv_param['stride'], conv_param['pad'])self.layers['Relu1'] = Relu()self.layers['Pool1'] = Pooling(pool_h=2, pool_w=2, stride=2)self.layers['Affine1'] = Affine(self.params['W2'], self.params['b2'])self.layers['Relu2'] = Relu()self.layers['Affine2'] = Affine(self.params['W3'], self.params['b3'])self.last_layer = SoftmaxWithLoss()
從最前面開始按順序向有序字典(OrderedDict)的 layers 中添加層。只有最后的 SoftmaxWithLoss 層被添加到別的變量 lastLayer 中。
像這樣初始化后,進行推理的 predict 方法和求損失函數值的 loss 方法就可以像下面這樣實現。
def predict(self, x):for layer in self.layers.values():x = layer.forward(x)return xdef loss(self, x, t):"""求損失函數參數x是輸入數據、t是教師標簽"""y = self.predict(x)return self.last_layer.forward(y, t)
參數 x 是輸入數據,t是教師標簽。用于推理的 predict 方法從頭開始依次調用已添加的層,并將結果傳遞給下一層。
在求損失函數的 loss 方法中,除了使用 predict 方法進行的 forward 處理之外,還會繼續進行
forward 處理,直到到達最后的 SoftmaxWithLoss 層。
def accuracy(self, x, t, batch_size=100):if t.ndim != 1 : t = np.argmax(t, axis=1)acc = 0.0for i in range(int(x.shape[0] / batch_size)):tx = x[i*batch_size:(i+1)*batch_size]tt = t[i*batch_size:(i+1)*batch_size]y = self.predict(tx)y = np.argmax(y, axis=1)acc += np.sum(y == tt) return acc / x.shape[0]def numerical_gradient(self, x, t):"""求梯度(數值微分)Parameters----------x : 輸入數據t : 教師標簽Returns-------具有各層的梯度的字典變量grads['W1']、grads['W2']、...是各層的權重grads['b1']、grads['b2']、...是各層的偏置"""loss_w = lambda w: self.loss(x, t)grads = {}for idx in (1, 2, 3):grads['W' + str(idx)] = numerical_gradient(loss_w, self.params['W' + str(idx)])grads['b' + str(idx)] = numerical_gradient(loss_w, self.params['b' + str(idx)])return gradsdef gradient(self, x, t):"""求梯度(誤差反向傳播法)Parameters----------x : 輸入數據t : 教師標簽Returns-------具有各層的梯度的字典變量grads['W1']、grads['W2']、...是各層的權重grads['b1']、grads['b2']、...是各層的偏置"""# forwardself.loss(x, t)# backwarddout = 1dout = self.last_layer.backward(dout)layers = list(self.layers.values())layers.reverse()for layer in layers:dout = layer.backward(dout)# 設定grads = {}grads['W1'], grads['b1'] = self.layers['Conv1'].dW, self.layers['Conv1'].dbgrads['W2'], grads['b2'] = self.layers['Affine1'].dW, self.layers['Affine1'].dbgrads['W3'], grads['b3'] = self.layers['Affine2'].dW, self.layers['Affine2'].dbreturn grads
參數的梯度通過誤差反向傳播法(反向傳播)求出,通過把正向傳播和反向傳播組裝在一起來完成。因為已經在各層正確實現了正向傳播和反向傳播的功能,所以這里只需要以合適的順序調用即可。最后,把各個權重參數的梯度保存到 grads 字典中。這就是 SimpleConvNet 的實現。
def save_params(self, file_name="params.pkl"):params = {}for key, val in self.params.items():params[key] = valwith open(file_name, 'wb') as f:pickle.dump(params, f)def load_params(self, file_name="params.pkl"):with open(file_name, 'rb') as f:params = pickle.load(f)for key, val in params.items():self.params[key] = valfor i, key in enumerate(['Conv1', 'Affine1', 'Affine2']):self.layers[key].W = self.params['W' + str(i+1)]self.layers[key].b = self.params['b' + str(i+1)]
總結
以上是生活随笔為你收集整理的机器学习入门(19)— 卷积网络 CNN 的简单实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 找一部小时候看过的电视剧,谢谢大家帮忙了
- 下一篇: 微信网名好看的符号