深度学习:卷积层的实现
文章目录
- 卷积层的数据shape和普通层的数据shape差别:
- 卷积层实现
- 实现池化层
- 实现 CNN 中的特殊层结构
- 实现 LayerFactory
- 网络结构
卷积层的数据shape和普通层的数据shape差别:
针对一般图像数据shape: Npq,图像就是二维浮点数据,N为数据个数,p,q为图像的维度。
卷积层的中间层数据shape: Npq*r,r为channels。
数据的shape必须非常清楚,因为假如自己处理卷积层就需要用到shape
卷积层实现
1、卷积层自身多了 Kernel 这个属性并因此带来了诸如 Stride、Padding 等属性,不过与此同时、卷积层之间没有权值矩阵,
2、卷积层和普通层的shape属性记录的东西不同,具体而言:
普通层的shape记录着上个 Layer 和该 Layer 所含神经元的个数
卷积层的shape记录着上个卷积层的输出和该卷积层的 Kernel 的信息(注意卷积层的上一层必定还是卷积层)
3、卷积填充有2种方式,tesorflow支持两种方式:一是不填充VALID,二是全部填充SAME,没有部分填充的方式,假如需要实现部分填充,就需要在数据预处理填充0,然后使用VALID方式卷积。
padding 可以为VALID,可以为SAME,也可以为int整型数,为int整型数时就是自填充数据。
class ConvLayer(Layer):"""初始化结构self.shape:记录着上个卷积层的输出和该Layer的Kernel的信息,具体而言:self.shape[0] = 上个卷积层的输出的形状(频道数×高×宽)常简记为self.shape[0] =(c,h_old,w_old)self.shape[1] = 该卷积层Kernel的信息(Kernel数×高×宽)常简记为self.shape[1] =(f,h_new,w_new)self.stride、self.padding:记录Stride、Padding的属性self.parent:记录父层的属性"""def __init__(self, shape, stride=1, padding="SAME", parent=None):if parent is not None:_parent = parent.root if parent.is_sub_layer else parentshape = _parent.shapeLayer.__init__(self, shape)self.stride = stride# 利用Tensorflow里面对Padding功能的封装、定义self.padding属性if isinstance(padding, str):# "VALID"意味着输出的高、宽会受Kernel的高、宽影响,具体公式后面会说if padding.upper() == "VALID":self.padding = 0self.pad_flag = "VALID"# "SAME"意味着输出的高、宽与Kernel的高、宽无关、只受Stride的影响else:self.padding = self.pad_flag = "SAME"# 如果输入了一个整数、那么就按照VALID情形设置Padding相关的属性else:self.padding = int(padding)self.pad_flag = "VALID"self.parent = parentif len(shape) == 1:self.n_channels = self.n_filters = self.out_h = self.out_w = Noneelse:self.feed_shape(shape)# 定义一个处理shape属性的方法def feed_shape(self, shape):self.shape = shapeself.n_channels, height, width = shape[0]self.n_filters, filter_height, filter_width = shape[1]# 根据Padding的相关信息、计算输出的高、宽if self.pad_flag == "VALID":self.out_h = ceil((height - filter_height + 1) / self.stride)self.out_w = ceil((width - filter_width + 1) / self.stride)else:self.out_h = ceil(height / self.stride)self.out_w = ceil(width / self.stride)class ConvLayerMeta(type):def __new__(mcs, *args, **kwargs):name, bases, attr = args[:3]# 规定继承的顺序为ConvLayer→Layerconv_layer, layer = basesdef __init__(self, shape, stride=1, padding="SAME"):conv_layer.__init__(self, shape, stride, padding)# 利用Tensorflow的相应函数定义计算卷积的方法def _conv(self, x, w):return tf.nn.conv2d(x, w, strides=[self.stride] * 4, padding=self.pad_flag)# 依次进行卷积、激活的步骤def _activate(self, x, w, bias, predict):res = self._conv(x, w) + biasreturn layer._activate(self, res, predict)# 在正式进行前向传导算法之前、先要利用Tensorflow相应函数进行Paddingdef activate(self, x, w, bias=None, predict=False):if self.pad_flag == "VALID" and self.padding > 0:_pad = [self.padding] * 2x = tf.pad(x, [[0, 0], _pad, _pad, [0, 0]], "CONSTANT")return _activate(self, x, w, bias, predict)# 将打包好的类返回for key, value in locals().items():if str(value).find("function") >= 0:attr[key] = valuereturn type(name, bases, attr)class ConvReLU(ConvLayer, ReLU, metaclass=ConvLayerMeta):pass
实现池化层
对于最常见的两种池化——极大池化和平均池化,Kernel 个数从数值上来说与输出频道个数一致,所以对于池化层的实现而言、我们应该直接用输入频道数来赋值 Kernel 数,因为池化不会改变数据的频道数。
class ConvPoolLayer(ConvLayer):def feed_shape(self, shape):shape = (shape[0], (shape[0][0], *shape[1]))ConvLayer.feed_shape(self, shape)def activate(self, x, w, bias=None, predict=False):pool_height, pool_width = self.shape[1][1:]# 处理Paddingif self.pad_flag == "VALID" and self.padding > 0:_pad = [self.padding] * 2x = tf.pad(x, [[0, 0], _pad, _pad, [0, 0]], "CONSTANT")# 利用self._activate方法进行池化return self._activate(None)(x, ksize=[1, pool_height, pool_width, 1],strides=[1, self.stride, self.stride, 1], padding=self.pad_flag)def _activate(self, x, *args):pass# 实现极大池化
class MaxPool(ConvPoolLayer):def _activate(self, x, *args):return tf.nn.max_pool# 实现平均池化
class AvgPool(ConvPoolLayer):def _activate(self, x, *args):return tf.nn.avg_pool
实现 CNN 中的特殊层结构
在 CNN 中同样有着 Dropout 和 Normalize 这两种特殊层结构,CNN 则通常是N×p×q×r的、其中r是当前数据的频道数。将 CNN 中r个频道的数据放在一起并视为 NN 中的一个神经元,这样做的话就能通过简易的封装来直接利用上我们对 NN 定义的特殊层结构。
# 定义作为封装的元类
class ConvSubLayerMeta(type):def __new__(mcs, *args, **kwargs):name, bases, attr = args[:3]conv_layer, sub_layer = basesdef __init__(self, parent, shape, *_args, **_kwargs):conv_layer.__init__(self, None, parent=parent)# 与池化层类似、特殊层输出数据的形状应保持与输入数据的形状一致self.out_h, self.out_w = parent.out_h, parent.out_wsub_layer.__init__(self, parent, shape, *_args, **_kwargs)self.shape = ((shape[0][0], self.out_h, self.out_w), shape[0])# 如果是CNN中的Normalize、则要提前初始化好γ、βif name == "ConvNorm":self.tf_gamma = tf.Variable(tf.ones(self.n_filters), name="norm_scale")self.tf_beta = tf.Variable(tf.zeros(self.n_filters), name="norm_beta")# 利用NN中的特殊层结构的相应方法获得结果def _activate(self, x, predict):return sub_layer._activate(self, x, predict)def activate(self, x, w, bias=None, predict=False):return _activate(self, x, predict)# 将打包好的类返回for key, value in locals().items():if str(value).find("function") >= 0 or str(value).find("property"):attr[key] = valuereturn type(name, bases, attr)# 定义CNN中的Dropout,注意继承顺序
class ConvDrop(ConvLayer, Dropout, metaclass=ConvSubLayerMeta):pass# 定义CNN中的Normalize,注意继承顺序
class ConvNorm(ConvLayer, Normalize, metaclass=ConvSubLayerMeta):pass
实现 LayerFactory
集合所有的layer,这样就可以通过字符串索引到对应的layer
class LayerFactory:# 使用一个字典记录下所有的Root Layeravailable_root_layers = {"Tanh": Tanh, "Sigmoid": Sigmoid,"ELU": ELU, "ReLU": ReLU, "Softplus": Softplus,"Identical": Identical,"CrossEntropy": CrossEntropy, "MSE": MSE,"ConvTanh": ConvTanh, "ConvSigmoid": ConvSigmoid,"ConvELU": ConvELU, "ConvReLU": ConvReLU, "ConvSoftplus": ConvSoftplus,"ConvIdentical": ConvIdentical,"MaxPool": MaxPool, "AvgPool": AvgPool}# 使用一个字典记录下所有特殊层available_special_layers = {"Dropout": Dropout,"Normalize": Normalize,"ConvDrop": ConvDrop,"ConvNorm": ConvNorm}# 使用一个字典记录下所有特殊层的默认参数special_layer_default_params = {"Dropout": (0.5,),"Normalize": ("Identical", 1e-8, 0.9),"ConvDrop": (0.5,),"ConvNorm": ("Identical", 1e-8, 0.9)}# 定义根据“名字”获取(Root)Layer的方法def get_root_layer_by_name(self, name, *args, **kwargs):# 根据字典判断输入的名字是否是Root Layer的名字if name in self.available_root_layers:# 若是、则返回相应的Root Layerlayer = self.available_root_layers[name]return layer(*args, **kwargs)# 否则返回Nonereturn None# 定义根据“名字”获取(任何)Layer的方法def get_layer_by_name(self, name, parent, current_dimension, *args, **kwargs):# 先看输入的是否是Root Layer_layer = self.get_root_layer_by_name(name, *args, **kwargs)# 若是、直接返回相应的Root Layerif _layer:return _layer, None# 否则就根据父层和相应字典进行初始化后、返回相应的特殊层_current, _next = parent.shape[1], current_dimensionlayer_param = self.special_layer_default_params[name]_layer = self.available_special_layers[name]if args or kwargs:_layer = _layer(parent, (_current, _next), *args, **kwargs)else:_layer = _layer(parent, (_current, _next), *layer_param)return _layer, (_current, _next)
网络结构
class NN(ClassifierBase):def __init__(self):super(NN, self).__init__()self._layers = []self._optimizer = Noneself._current_dimension = 0self._available_metrics = {key: value for key, value in zip(["acc", "f1-score"], [NN.acc, NN.f1_score])}self.verbose = 0self._metrics, self._metric_names, self._logs = [], [], {}self._layer_factory = LayerFactory()# 定义Tensorflow中的相应变量self._tfx = self._tfy = None # 记录每个Batch的样本、标签的属性self._tf_weights, self._tf_bias = [], [] # 记录w、b的属性self._cost = self._y_pred = None # 记录损失值、输出值的属性self._train_step = None # 记录“参数更新步骤”的属性self._sess = tf.Session() # 记录Tensorflow Session的属性# 利用Tensorflow相应函数初始化参数@staticmethoddef _get_w(shape):initial = tf.truncated_normal(shape, stddev=0.1)return tf.Variable(initial, name="w")@staticmethoddef _get_b(shape):return tf.Variable(np.zeros(shape, dtype=np.float32) + 0.1, name="b")# 做一个初始化参数的封装,要注意兼容CNNdef _add_params(self, shape, conv_channel=None, fc_shape=None, apply_bias=True):# 如果是FC的话、就要根据铺平后数据的形状来初始化数据if fc_shape is not None:w_shape = (fc_shape, shape[1])b_shape = shape[1],# 如果是卷积层的话、就要定义Kernel而非权值矩阵elif conv_channel is not None:if len(shape[1]) <= 2:w_shape = shape[1][0], shape[1][1], conv_channel, conv_channelelse:w_shape = (shape[1][1], shape[1][2], conv_channel, shape[1][0])b_shape = shape[1][0],# 其余情况和普通NN无异else:w_shape = shapeb_shape = shape[1],self._tf_weights.append(self._get_w(w_shape))if apply_bias:self._tf_bias.append(self._get_b(b_shape))else:self._tf_bias.append(None)# 由于特殊层不会用到w和b、所以要定义一个生成占位符的方法def _add_param_placeholder(self):self._tf_weights.append(tf.constant([.0]))self._tf_bias.append(tf.constant([.0]))
深度学习:卷积层的实现相关推荐
- 深度学习——卷积层+填充和步幅(笔记)
一 卷积层 1.了解二维交叉相关:具体做法是 对应数字 相乘后相加 Output具体的运算过程: 2.二维卷积层 * ①输入X: (输入高为h,宽为w的矩阵) 如3*3 ②卷积核W: ③偏 ...
- 动手学深度学习——卷积层里的填充和步幅
1.填充 填充( padding )是指在输⼊⾼和宽的两侧填充元素(通常是 0 元素). 给定(32x32)输入图像: 应用5x5大小的卷积核,第一层得到输出大小28x28,第七层得到输出大小4x4: ...
- 动手学深度学习——卷积层
从全连接到卷积 1.简单例子:分类猫和狗的图片 使用一个还不错的相机采集图片(12M像素) RGB图片有36M元素 使用100大小的单隐藏层MLP,模型有3.6B元素,远多于世界上所有猫和狗总数(90 ...
- 深度学习 卷积层与全连接层权重参数个数的计算
1.卷积网络实例分析 构建卷积网络如下: from tensorflow.python.keras import datasets, models, layers class CNN(object): ...
- 想知道深度学习卷积在GPU上如何优化吗?“大神”赵开勇带你深入浅出
想知道深度学习卷积在GPU上如何优化吗?"大神"赵开勇带你深入浅出 2016-08-19 11:54 转载 陈杨英杰 0条评论 雷锋网(搜索"雷锋网"公众号关注 ...
- 深度学习 | BN层原理浅谈
深度学习 | BN层原理浅谈 文章目录 深度学习 | BN层原理浅谈 一. 背景 二. BN层作用 三. 计算原理 四. 注意事项 为什么BN层一般用在线性层和卷积层的后面,而不是放在激活函数后 为什 ...
- 毕设 深度学习卷积神经网络的花卉识别
文章目录 0 前言 1 项目背景 2 花卉识别的基本原理 3 算法实现 3.1 预处理 3.2 特征提取和选择 3.3 分类器设计和决策 3.4 卷积神经网络基本原理 4 算法实现 4.1 花卉图像数 ...
- 深度学习 卷积神经网络原理
深度学习 卷积神经网络原理 一.前言 二.全连接层的局限性 三.卷积层 3.1 如何进行卷积运算? 3.2 偏置 3.3 填充 3.4 步长 3.5 卷积运算是如何保留图片特征的? 3.6 三维卷积 ...
- 毕业设计 - 题目:基于深度学习卷积神经网络的花卉识别 - 深度学习 机器视觉
文章目录 0 前言 1 项目背景 2 花卉识别的基本原理 3 算法实现 3.1 预处理 3.2 特征提取和选择 3.3 分类器设计和决策 3.4 卷积神经网络基本原理 4 算法实现 4.1 花卉图像数 ...
- 深度学习--卷积神经网络CNN
主要内容 1. 神经网络 1.1 感知器 1.2 Sigmoid神经元 1.3 神经网络 2. 卷积神经网络CNN 2.1 卷积神经网络结构 2.2 数据输入层 2.3 卷积层 2.3.1 局部感知( ...
最新文章
- js——全选框 checkbox
- linux怎么增加cpu负载,Linux下的CPU平均负载
- EXPLAIN字段详解
- Spark on YARN的部署
- 防止xss(脚本攻击)的方法之过滤器
- Intel IPP密码库 IPPCP 2018 开发笔记与总结(全)
- 服务器网卡多路径配置文件,IPSAN(五)IPSAN多路径设置(客户端)
- linux下好看的中文字体,推荐一款 Linux 上比较漂亮的字体(转)
- 使用WIFIPR跑握手包,破解wifi密码
- 高性能服务器架构拓扑图,topology: 开源、易扩展、方便集成的在线绘图(微服务架构图、网络拓扑图、流程图等)工具...
- 如何使用分布式管理工具:Git
- 父亲母亲-山里老房子
- 40岁销售被裁员后抑郁了,学Python是他最后的希望
- MATLAB线形规划函数linprog、intlinprog与二次规划函数quadprog
- 基于MATLAB的远程声控小车的系统设计与仿真
- Retrofit @Multipart@PartMap@Part组合的一种用法
- 【转】设置右键显示/隐藏系统文件
- gt2怎么与微信连接不上服务器,华为WATCH GT2收不到通知消息怎么办?收不到短信微信消息的解决方...
- DAT的算法原理及实现
- 【unity shader】高级光照 --- 薄膜干涉
热门文章
- 小程序支付 PHP
- html 按下和松开事件,利用JQuery实现一个键盘按下与松开触发事件
- c语言中freopen函数,fopen和freopen_C中freopen和fopen的区别(用法+详解+区别)
- windows下mysql(解压版)安装教程
- php监控系统,php 系统监控 | 学步园
- springboot starter工作原理_springboot基础知识集结,你get到了吗
- 错误:未在本地计算机上注册“Microsoft.Ace.OleDb.12.0”提供程序
- Spring Cloud Data Flow手动安装
- Linux 下修改hosts文件
- c语言中热河输入空格,承德市2020年(春秋版)小学英语六年级上册期中考试模拟试卷(1)C卷...