热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

卷积层(1)

卷积层(1)前面聊了3期全连接层,下面先扔下它,看看卷积神经网络的另外一个重量级组成部分——卷积层。 关于卷积层的具体计算方式在这里就不多说了,和全连接层类似,由线性部分和非线性部

卷积层(1)

前面聊了3期全连接层,下面先扔下它,看看卷积神经网络的另外一个重量级组成部分——卷积层

 

关于卷积层的具体计算方式在这里就不多说了,和全连接层类似,由线性部分和非线性部分组成,一会儿直接看代码就好。关于卷积层的计算方法,现在一般来说大家的实现方式都是用“相关”这个操作来进行的,为什么呢?当然是为了计算方便,减少一次把卷积核转一圈的计算。

以下是“卷积层”操作的基本代码,我们后面会做进一步地“升级”的:

import numpy as np
import matplotlib.pyplot as plt
def conv2(X, k):
   x_row, x_col = X.shape
   k_row, k_col = k.shape
   ret_row, ret_col = x_row - k_row + 1, x_col - k_col + 1
   ret = np.empty((ret_row, ret_col))
   for y in range(ret_row):
       for x in range(ret_col):
           sub = X[y : y + k_row, x : x + k_col]
           ret[y,x] = np.sum(sub * k)
   return ret

class ConvLayer:
   def __init__(self, in_channel, out_channel, kernel_size):
       self.w = np.random.randn(in_channel, out_channel, kernel_size, kernel_size)
       self.b = np.zeros((out_channel))
   def _relu(self, x):
       x[x <0] = 0
       return x
   def forward(self, in_data):
       # assume the first index is channel index
       in_channel, in_row, in_col = in_data.shape
       out_channel, kernel_row, kernel_col = self.w.shape[1], self.w.shape[2], self.w.shape[3]
       self.top_val = np.zeros((out_channel, in_row - kernel_row + 1, in_col - kernel_col + 1))
       for j in range(out_channel):
           for i in range(in_channel):
               self.top_val[j] += conv2(in_data[i], self.w[i, j])
           self.top_val[j] += self.b[j]
           self.top_val[j] = self._relu(self.topval[j])
       return self.top_val

 

到这里卷积层就介绍完了,谢谢……

卷积层内心OS:玩儿去,怎么可以戏份这么少……

其实在卷积神经网络这个模型大火之前,卷积这个概念早已深入许多图像处理的小伙伴的内心了。在图像处理中,我们有大批图像滤波算法,其中很多都是像卷积层这样的线性卷积核。为了更清楚地介绍这些核,我们拿一张OCR的训练数据做例子(特别说明,本文字来自wikipedia,侵权删图):

 

import cv2

mat = cv2.imread('conv1.png',0)

row,col = mat.shape

in_data = mat.reshape(1,row,col)

in_data = in_data.astype(np.float) / 255

plt.imshow(in_data[0], cmap='Greys_r')

 

这个字显示出来是这个样子:

卷积层(1)

 

首先是mean kernel,也就是均值滤波:

meanCOnv= ConvLayer(1,1,5)

meanConv.w[0,0] = np.ones((5,5)) / (5 * 5)

mean_out = meanConv.forward(in_data)

plt.imshow(mean_out[0], cmap='Greys_r')

 

结果如下所示:

卷积层(1)

 

均值滤波在图像处理中可以起到对模糊图像的作用,当然由于卷积核比较小,所以效果不是很明显。

然后是梯度计算,请上Sobel kernel,我们定义一个计算纵向梯度的核,如果一个像素点的纵向梯度非常大,那么这个点的结果会非常大:

sobelCOnv= ConvLayer(1,1,3)

sobelConv.w[0,0] = np.array([[-1,-2,-1],[0,0,0],[1,2,1]])

sobel_out = sobelConv.forward(in_data)

plt.imshow(sobel_out[0], cmap='Greys_r')

 

图像结果如下所示:

卷积层(1)

从结果来看,文字中横向比划的上端被保留了下来。

 

最后来一张Gabor filter的效果,现在的一些论文里面都宣称自己的模型能够学出Gabor filter的参数来(Gabor filter代码来自wikipedia):

 

def gabor_fn(sigma, theta, Lambda, psi, gamma):
   sigma_x = sigma
   sigma_y = float(sigma) / gamma
   (y, x) = np.meshgrid(np.arange(-1,2), np.arange(-1,2))
   # Rotation 
   x_theta = x * np.cos(theta) + y * np.sin(theta)
   y_theta = -x * np.sin(theta) + y * np.cos(theta)
   gb = np.exp(-.5 * (x_theta ** 2 / sigma_x ** 2 + y_theta ** 2 / sigma_y ** 2)) * np.cos(2 * np.pi / Lambda * x_theta + psi)
   return gb
print gabor_fn(2, 0, 0.3, 0, 2)
gaborCOnv= ConvLayer(1,1,3)
gaborConv.w[0,0] = gabor_fn(2, 0, 0.3, 1, 2)
gabor_out = gaborConv.forward(in_data)
plt.imshow(gabor_out[0], cmap='Greys_r')

[[-0.26763071 -0.44124845 -0.26763071]
[ 0.60653066  1.          0.60653066]
[-0.26763071 -0.44124845 -0.26763071]]

卷积层(1)

 

从结果可以看出,Gabor filter同样可以做到边缘提取的作用,这和它本身的功能是相符的。

 

好了,以上三种处理完成之后,我们可以看出,不同的滤波核确实能起到不同的效果。实际上用于图像滤波的卷积核还有很多。

 

下面要试图做一些讲道理的事情,也就是说明:

 

  • 为什么要发明卷积层这种神经层?能不能用全连接层代替卷积层?

 

很显然,用全连接层代替卷积层是完全没有问题的,但是这样做的代价实在太大了。原始图像的维度相对而言比较大,如果采用全连接的话参数将会有爆炸式的增长,参数的数量可能对于现在的电脑来说是一个灾难。试想一下对于MNist的数据,如果第一层是全连接层,即使把1*28*28数据映射到1*1024这样的输出上,其中的参数已经达到800万。而曾经的经典模型Lenet呢?

卷积层(1)

 

从上面的图可以看出(此处):

 

第一层卷积的参数数量为:1*6*(5*5+1)=156,却可以得到6*28*28的输出。

 

两者的参数数量差距几万倍,而实际上两者效果绝对不会有如此大的差距。

 

既然如此,参数数量如此少的卷积层为什么可以有用?识别这样的问题总体来说是一种比较宏观的问题。像MNist这样的问题,每张图片上有784个像素,理论上可以有

 

种组合,而实际的类别只有10种,基本上可以断定提供的信息是远远多于满足搜索需求的数量的。那么多出来的像素信息是以什么样的形式展现呢?在图像处理上,有一个词叫“局部相关性”就是指这样的问题。

 

我们是如何识别出一个数字的呢?当然是因为这个数字不同于别的数字的特点,对于像MNist这样的数据,特点自然来自于明暗交界的地方。一片黑的区域不会告诉我们任何有用的信息,同样地,一片白的区域也不会告诉我们任何有用的信息。同样,数字的笔画粗细对我们的识别也没有太大的作用。这些都是我们识别过程中会遇到的问题,而其中临近像素之间有规律地出现相似的状态就是局部相关性。

 

那么如何消除这些局部相关性呢,使我们的特征变得少而精呢?卷积就是一种很好地方法。它只考虑附近一块区域的内容,分析这一小片区域的特点,这样针对小区域的算法可以很容易地分析出区域内的内容是否相似。如果再加上Pooling层(可以理解为汇集,聚集的意思,后面不做翻译),从附近的卷积结果中再采样选择一些高价值的合成信息,丢弃一些重复低质量的合成信息,就可以做到对特征信息的进一步处理过滤,让特征向少而精的方向前进。

 

卷积的另一种解释

 

熟悉图像处理的童鞋一定都知道传说中的卷积定理,这个定理涉及到图像处理的一大黑科技——傅立叶变换

 

关于傅立叶的高质量科普文章已经很多,这里就不多说了。套用之前看过的一个有赞的解释:如果把我们看到的图像比作一道做好的菜,那么傅立叶变换就是告诉我们这道菜具体的配料以及各种配料的用量。这个方法的神奇之处在于不管这道是如何做的(按类别码放还是大乱炖),它都能将配料清晰分出来。在图像处理中,这个配料被成为频率。其中有些信息被称作低频信息,有些被成为高频信息。

 

低频信息一般被我们是看作是整幅图像的基础,有点像一道菜中的主料。如果说这道菜是番茄炒蛋,那低频信息可以看作是番茄和蛋。高频信息一般是指那些变动比较大的,表达图像特点的信息,有点像一道菜的配料或者调料。对于番茄炒蛋,像是菜中调料和点缀的材料。

 

回到卷积中,卷积定理中说,两个矩阵的卷积的结果,等于两个矩阵在经过傅立叶变换后,进行元素级别的乘法(element-wise multiplication),然后再对结果进行反向傅立叶处理。如果我们用FFT表示傅立叶变换,IFFT表示反向傅立叶变换,那么下面两个过程结果是相同的(考虑到浮点数近似,可以认为相近就是相同):

 

A = np.array()

B = np.array() # matrix

# method1

C = conv2(A,B)

#method2

FFT_A = FFT(A)

FFT_B = FFT(B)

C = IFFT(FFT_A * FFT_B)

 

真实的代码不会像上面这样简单,但是大体来说结构也是相同的。看上去第二种方法比第一种方法啰嗦了不少,那么它的优势在哪里?

 

那我们就来看看上面提到的一些kernel在经过傅立叶变换后的样子:

 

首先是mean kernel:

然后是Sobel kernel:

最后是Gabor kernel:

 

我们前面说过,傅立叶变换可以帮助分析出高低频信息。在上面的图像中,(经过fft_shift的)图像的正中心是低频信息,越靠近边缘频率越高。由于最终要进行元素级的乘法,如果kernel在某个频率的数据比较低,经过乘法后输入数据在这个频率的数据也会变小。

 

如果我们把上面的图片想象成中心为原点的直角坐标系图,所以我们可以看出:

 

  • mean kernel会把中心附近和坐标轴附近信息进行保留;

  • Sobel kernel会保留y轴上下的信息,丢弃中间的信息和x轴两边的信息;

  • Gabor kernel和Sobel kernel类似,但是保留的内容会更少些,更倾向于保留远离中心的像素。

 

所以从这个角度来分析结果,sobel kernel和gabor kernel更倾向于保留高频信息而弱化低频信息,而mean kernel则是弱化高频信息而保留低频信息。所以mean kernel和另外两个kernel的作用不同。

 

另外,对于一些文章中提到利用可视化的方法发现了自己的参数像gabor filter,也说明了它的卷积核作用是保留高频牺牲低频。

 

那么从这个角度分析,卷积层意义在哪里?如果我们任务是给我们一盘番茄炒蛋,问我们这盘菜是哪位师傅做的(记得小时候在饭店吃饭的时候,菜盘边上经常会有一张小纸条写着“XX号厨师为您服务”)。那么盘子里的数量众多的番茄和鸡蛋不一定能帮助我们找到厨师,而其中佐料的分量多少更有助于我们找出厨师的做菜风格。当然这里我们假设厨师有自己的风格,且正常发挥没有失误了。而这个思路也是卷积层采取的一种策略。

 

关于傅立叶变换以及频域信息分析显然没有这么简单,更多的细节还需要更多的分析,这里只是抛砖作个引子。


推荐阅读
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • 开源Keras Faster RCNN模型介绍及代码结构解析
    本文介绍了开源Keras Faster RCNN模型的环境需求和代码结构,包括FasterRCNN源码解析、RPN与classifier定义、data_generators.py文件的功能以及损失计算。同时提供了该模型的开源地址和安装所需的库。 ... [详细]
  • Python使用Pillow包生成验证码图片的方法
    本文介绍了使用Python中的Pillow包生成验证码图片的方法。通过随机生成数字和符号,并添加干扰象素,生成一幅验证码图片。需要配置好Python环境,并安装Pillow库。代码实现包括导入Pillow包和随机模块,定义随机生成字母、数字和字体颜色的函数。 ... [详细]
  • 关于如何快速定义自己的数据集,可以参考我的前一篇文章PyTorch中快速加载自定义数据(入门)_晨曦473的博客-CSDN博客刚开始学习P ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 也就是|小窗_卷积的特征提取与参数计算
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了卷积的特征提取与参数计算相关的知识,希望对你有一定的参考价值。Dense和Conv2D根本区别在于,Den ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • 【论文】ICLR 2020 九篇满分论文!!!
    点击上方,选择星标或置顶,每天给你送干货!阅读大概需要11分钟跟随小博主,每天进步一丢丢来自:深度学习技术前沿 ... [详细]
  • 很多时候在注册一些比较重要的帐号,或者使用一些比较重要的接口的时候,需要使用到随机字符串,为了方便,我们设计这个脚本需要注意 ... [详细]
  • Python 可视化 | Seaborn5 分钟入门 (六)——heatmap 热力图
    微信公众号:「Python读财」如有问题或建议,请公众号留言Seaborn是基于matplotlib的Python可视化库。它提供了一个高级界面来绘制有吸引力的统计图形。Seabo ... [详细]
  • YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程
    本文介绍了关于人工智能、神经网络和深度学习的知识点,并提供了YOLOv7基于自己的数据集从零构建模型完整训练、推理计算的详细教程。文章还提到了郑州最低生活保障的话题。对于从事目标检测任务的人来说,YOLO是一个熟悉的模型。文章还提到了yolov4和yolov6的相关内容,以及选择模型的优化思路。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
author-avatar
白变小樱啊
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有