1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > 【DL学习笔记06】深度学习入门——基于Python的理论与实现(ch07: 卷积神经网络 CNN)

【DL学习笔记06】深度学习入门——基于Python的理论与实现(ch07: 卷积神经网络 CNN)

时间:2020-10-16 05:15:30

相关推荐

【DL学习笔记06】深度学习入门——基于Python的理论与实现(ch07: 卷积神经网络 CNN)

目录

1. 整体结构

2. 卷积层

全连接层存在的问题

卷积运算

3维数据的卷积运算

批处理

3. 池化层

4. 卷积层和池化层的实现

卷积层

池化层的实现

5. CNN的实现

6. CNN的可视化

第1层权重的可视化

基于分层结构的信息提取

7. 具有代表性的CNN

LeNet

AlexNet

CNN被用于图像识别、语音识别等各种场景

1. 整体结构

CNN通过组装层来构建,新出现了卷积层(Convolution层)和池化层(Pooling层)全连接(fully-connected):相邻层的所有神经元之间都有连接,用Affine层实现基于全连接层(Affine层):Affine - ReLU(Sigmoid)基于CNN网络:Convolution - ReLU - Pooling

2. 卷积层

全连接层存在的问题

在全连接层中,相邻的神经元全部连接在一起,输出的数量可以任意决定向全连接层输入时,会将3维数据拉平为1维数据,因此数据的形状会被“忽视”卷积层可以保持形状不变,会以3维数据的形式接收输入数据,并同样传输至下一层CNN中,卷积层的输入数据称为输入特征图,输出为输出特征图,统称为特征图

卷积运算

卷积层进行的处理就是卷积运算,相当于图像处理中的“滤波器运算”

对于输入数据,卷积运算以一定间隔滑动滤波器的窗口并应用

偏置:向应用了滤波器的元素加上某个固定值

填充:在进行卷积层的处理之前,向输入数据的周围填入固定的数据(比如0),为了调整输出的大小,避免反复进行卷积运算时输出不断缩小到1,导致无法进行卷积运算

步幅:应用滤波器的位置间隔

3维数据的卷积运算

和2维数据相比,纵深方向(通道方向)上增加了特征图通道方向上有多个特征图时,会按通道进行输入数据和滤波器的卷积运算,并将结果相加,从而得到输出滤波器的通道数只能设定为和输入数据的通道数相同的值结合方块思考 3维数据:(channel,height,width)(通道数,高度,长度)滤波器 1个滤波器:(channel,height,width),数据输出是1张特征图n个滤波器:(output_channel,input_channel,height,width)(滤波器个数,通道数,高度,长度),数据输出是n张特征图

批处理

需要将在各层间传递的数据保存为4维数据,(batch_num,channel,height,width)

3. 池化层

池化层是缩小高、长方向上的空间的运算,在图像识别领域,主要使用Max池化,取出目标区域的最大值,此外还有Average池化等。一般来说,池化的窗口大小会和步幅设定成相同的值

特征: 没有要学习的参数通道数不发生变化对微小的位置变化具有健壮性

4. 卷积层和池化层的实现

NumPy中存在使用for语句后处理变慢的缺点。因此卷积运算我们不适用for语句,而是使用im2col这个函数实现

im2col函数(image to column)可以将输入数据展开以适合滤波器(权重)。会在所有应用滤波器的地方进行这个展开处理

函数的原理和实现可以参考im2col的原理和实现_dwyane12138的博客-CSDN博客_im2col

def im2col(input_data, filter_h, filter_w, stride=1, pad=0):"""Parameters----------input_data : 由(数据量, 通道, 高, 长)的4维数组构成的输入数据filter_h : 滤波器的高filter_w : 滤波器的长stride : 步幅pad : 填充Returns-------col : 2维数组"""N, C, H, W = input_data.shapeout_h = (H + 2*pad - filter_h)//stride + 1out_w = (W + 2*pad - filter_w)//stride + 1img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant')col = np.zeros((N, C, filter_h, filter_w, out_h, out_w))for y in range(filter_h):y_max = y + stride*out_hfor x in range(filter_w):x_max = x + stride*out_wcol[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N*out_h*out_w, -1)return col

# 用于反向传播的逆处理def col2im(col, input_shape, filter_h, filter_w, stride=1, pad=0):"""Parameters----------col :input_shape : 输入数据的形状(例:(10, 1, 28, 28))filter_h :filter_wstridepadReturns-------"""N, C, H, W = input_shapeout_h = (H + 2*pad - filter_h)//stride + 1out_w = (W + 2*pad - filter_w)//stride + 1col = col.reshape(N, out_h, out_w, C, filter_h, filter_w).transpose(0, 3, 4, 5, 1, 2)img = np.zeros((N, C, H + 2*pad + stride - 1, W + 2*pad + stride - 1))for y in range(filter_h):y_max = y + stride*out_hfor x in range(filter_w):x_max = x + stride*out_wimg[:, :, y:y_max:stride, x:x_max:stride] += col[:, :, y, x, :, :]return img[:, :, pad:H + pad, pad:W + pad]

使用im2col展开输入数据后,再将卷积层的滤波器纵向展开为1列,并计算2个矩阵的乘积即可

卷积层

先举个例子!

x1 = np.random.rand(1, 3, 7, 7)col1 = im2col(x1, 5, 5, stride=1, pad=0)print(col1.shape) # (9,75)x2 = np.random.rand(10, 3, 7, 7)col2 = im2col(x2, 5, 5, stride=1, pad=0)print(col2.shape) # (90,75)

第一个是批大小为1,通道为3的7 x 7的数据,第二个是批大小改为10第2维的元素个数均为75,是滤波器的元素个数的总和

实现

class Convolution:# 初始化,接收滤波器(权重)、偏置、步幅、填充def __init__(self, W, b, stride=1, pad=0):self.W = Wself.b = bself.stride = strideself.pad = paddef forward(self, x):# Filter Number(滤波器数量),Channel,Filter Height,Filter WidthFN, C, FH, FW = self.W.shapeN, C, H, W = x.shape# 用于还原转换被展开的数据out_h = int(1 + (H + 2*self.pad - FH) / self.stride)out_w = int(1 + (W + 2*self.pad - FW) / self.stride)# 用im2col函数展开输入数据,用reshape将滤波器展开为2维数组# 这里用了reshape(FN, -1)表示有FN行,然后-1会自动计算有几列col = im2col(x, FH, FW, self.strde, self.pad)col_W = self.W.reshape(FN, -1).Tout = np.dot(col, col_W) + self.b# transpose会更改多维数组的轴的顺序# 将原来的形状(N, H, W, C) 还原为 (N, C, H, W)out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)return outdef backward(self, dout):FN, C, FH, FW = self.W.shapedout = dout.transpose(0, 2, 3, 1).reshape(-1, FN)self.db = np.sum(dout, axis=0)self.dW = np.dot(self.col.T, dout)self.dW = self.dW.transpose(1, 0).reshape(FN, C, FH, FW)dcol = np.dot(dout, self.col_W.T)# im2col的逆处理->col2imdx = col2im(dcol, self.x.shape, FH, FW, self.stride, self.pad)return dx

池化层

池化层和卷积层相同,都使用im2col展开输入数据,不过池化层在通道方向上是独立的

class Pooling:def __init__(self, pool_h, pool_w, stride=1, pad=0):self.pool_h = pool_hself.pool_w = pool_wself.stride = strideself.pad = paddef forward(self, x):N, C, H, W = x.shapeout_h = int(1 + (H - self.pool_h) / self.stride)out_w = int(1 + (W - self.pool_w) / self.stride)# 展开,第二步是为了实现池化的应用区域按通道单独展开col = im2col(x, self.pool_h, self.pool_w, self.stride, self.pad)col = col.reshape(-1, self.pool_h*self.pool_w)# 最大值out = np.max(col, axis=1)# 转换out = out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)return outdef backward(self, dout):dout = dout.transpose(0, 2, 3, 1)pool_size = self.pool_h * self.pool_wdmax = np.zeros((dout.size, pool_size))dmax[np.arange(self.arg_max.size), self.arg_max.flatten()] = dout.flatten()dmax = dmax.reshape(dout.shape + (pool_size,))dcol = dmax.reshape(dmax.shape[0] * dmax.shape[1] * dmax.shape[2], -1)dx = col2im(dcol, self.x.shape, self.pool_h, self.pool_w, self.stride, self.pad)return dx

5. CNN的实现

这里我们搭建进行手写数字识别的CNN

网络构成:Convolution - ReLU - Pooling - Affine - ReLU - Affine - Softmax

初始化部分:

class SimpleConvNet:def __init__(self, input_dim=(1, 28, 28), conv_param=None,hidden_size=100, output_size=10, weight_init_std=0.01):""":param input_dim:输入数据的维度:(通道,高,长):param conv_param:卷积层的超参数(字典)。字典的关键字如下:filter_num - 滤波器数量;filter_size - 滤波器大小stride - 步幅;pad - 填充:param hidden_size:隐藏层(全连接)的神经元数量:param output_size:输出层(全连接)的神经元数量:param weight_init_std:初始化时权重的标准差"""# 将超参数从字典中取出来,计算卷积层的输出大小if conv_param is None:conv_param = {'filter_num': 30, 'filter_size': 5, 'pad': 0, 'stride': 1}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))# 权重参数的初始化# 包括第1层的卷积层和剩余两个全连接层的权重和偏置self.params = {'W1': weight_init_std * np.random.randn(filter_num, input_dim[0], filter_size, filter_size),'b1': np.zeros(filter_num),'W2': weight_init_std * np.random.randn(pool_output_size, hidden_size),'b2': np.zeros(hidden_size),'W3': weight_init_std * np.random.randn(hidden_size, output_size),'b3': np.zeros(output_size)}

推理和求损失函数值:

# 推理def predict(self, x):for layer in self.layers.values():x = layer.forward()return x# 求损失函数值def loss(self, x, t):y = self.predict(x)return self.last_layer.forward(y, t)

误差反向传播法求梯度

def gradient(self, x, t):# forwardself.loss(x, t)# backwarddout = 1dout = self.lastLayer.backward(dout)layers = list(self.layers.values())layers.reverse()for layer in layers:dout = layer.backward(dout)# 设定grads = {'W1': self.layers['Conv1'].dW, 'b1': self.layers['Conv1'].db,'W2': self.layers['Affine1'].dW, 'b2': self.layers['Affine1'].db,'W3': self.layers['Affine2'].dW, 'b3': self.layers['Affine2'].db}return grads

6. CNN的可视化

第1层权重的可视化

上述对MNIST数据集的学习中,第1层的卷积层的权重形状是(30,1,5,5),意味着滤波器可以可视化为1通道的灰度图像

可以发现,学习前的滤波器是随即进行初始化的,学习后的滤波器变成了有规律的图像,比如从白变到黑的滤波器、含有块状区域(blob)的滤波器等有规律的滤波器在“观察”边缘(颜色变化的分界线)和斑块(局部的块状区域)等卷积层的滤波器会提取边缘或斑块等原始信息,而实现的CNN会将这些原始信息传递给后面的层

基于分层结构的信息提取

第1层的卷积层中提取了边缘或斑块等“低级”信息,但如果堆叠了多层卷积层,随着层次加深,提取的信息也愈加复杂、抽象。最开始的层对简单的边缘有响应,接下来的层对纹理有响应,再后面的层对更加复杂的物体部件有响应下一节会介绍堆叠了多层卷积层和池化层最后经过全连接层输出结果的AlexNet

7. 具有代表性的CNN

LeNet

是进行手写数字识别的网络

AlexNet

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。