concat层,数据结构中concat
论文阅读 | CVPR2017(Best Paper) | Densely Connected Convolutional Networks
大名鼎鼎的DenseNet,17年CVPR的best paper(当然有争议是后话),不得不读。黄高博士的扛鼎之作,之前在读他的Snapshot-Ensembles时感觉就很舒服,整个文章逻辑很清楚,实验对比做的也十分全面,相信这篇best paper更是没有问题,会给读者一种爽的感觉。
2019.2.20 2852次。绝对值很高,但相比其他经典网络,ResNet,GoogLeNet之类,有些差距。
本篇在16年8月挂到arXiv上,中了2017年CVPR,是继16年何大神的ResNet之后,第二个华人的best paper, 这里 有个作者本尊的talk,现场讲解。一作Gao Huang(黄高)05年北航的本科生(GPA第一),15年清华博士毕业(读了6年。。),后来在康奈尔待了3年做博后,此刻在清华作青椒,本篇是在康奈尔时的工作。二作刘壮(同等贡献)也是碉堡,现在在伯克利做博士生,之前是清华姚班的(13级),发这篇文章时还在清华,也就是说 本科生 。。。最近以一作的身份新发了一篇《Rethinking the Value of Network Pruning》,中了19年的ICLR,同时也是18年NIPS的best paper award。。这个世界太疯狂了,这都不是潜力股了,而是才华横溢溢的不行了。
官方实现在这里:
黄高个人主页在这里:
刘壮个人主页在这里:
先前的研究中说明只要网络包含短路连接,基本上就能更深,更准确,更有效的训练。本文基于这个观察,引入了密集卷积网络(DenseNet),它以前馈方式将每个层连接到所有层。传统的卷积网络L层有L个连接,而DenseNet有 个直接连接。对于每一层,它前面所有层的特征图都当作输入,而其本身的特征图作为所有后面层的输入(短路连接被发挥到极致,网络中每两层都相连)。DenseNet具有几个引入注目的优点: 可以缓解梯度消失问题,加强特征传播,鼓励特征重用,并大幅减少参数数量。
随着CNN变得越来越深,一个新的研究问题出现了:随着输入信息或梯度通过多层,它在到达网络结尾(或开始)处就消失了。ResNets和Highway Networks通过恒等连接将信号从一层传输到下一层。Stochastic depth通过在训练期间随机丢弃层来缩短ResNets,以得到更好的信息和梯度流。FractalNets重复组合几个并行层序列和不同数量的卷积块,以获得较深的标准深度,同时在网络中保持许多短路径。尽管上述方法的网络结构都有所不同,但它们有一个共同特征:创建从早期层到后期层的短路径。
本文提出一个简单的连接模式:为了确保网络中各层之间的最大信息流, 将所有层(匹配特征图大小)直接相互连接 。为了保持前向传播性质,每个层从所有前面的层获得附加输入,并将其自身特征图传递给所有后续层。
至关重要的是,与ResNets相比,在传递给下一层之前, 不是通过求和来合并特征,而是通过concat来合并特征 。因此, 层有 个输入,包括所有先前卷积块的特征图。其特征图被传递到后续所有 层。这在L层网络中引入了 个连接,而不是传统架构的L个连接。正是因为这种密集连接模式,所以称本文方法为密集连接网络( Dense Convolutional Network DenseNet)。
相比传统卷积网络,这种密集连接模式有有一点可能违反直觉的是,它需要更少的参数,因为无需重新学习冗余的特征图。本文提出的DenseNet架构显式区分了添加到网络的信息和保留的信息。DenseNet的层非常窄(如每层只有12个滤波器),只给网络的"集体知识"增加一小组特征图,并保持其余的特征图不变。
除了更好的参数利用率之外,DenseNet的一大优势是它改善了整个网络中的信息流和梯度,使得网络更易于训练。每层都可以直接访问损失函数和原始输入信号的梯度( 我屮,这不就是GoogLeNet当时为解决梯度消失而在中间层引入分类器那种ugly办法的替代吗 ),从而导致隐式的深度监督。这有助于训练更深的网络。
与DenseNet相似的级联结构早在1989年就提出来了。。Adanet的提出差不多是与DenseNet并行的,跨层连接也相似(话说竞争真激烈。。)
本文作者提出的另一个网络Stochastic depth说明并非所有层都需要,在深度残差网络中存在大量冗余的层。本文的部分灵感也来源于此。
相比从极深或极宽的架构中提取表示能力,DenseNet是通过 特征重用 来利用网络的潜力,得到易于训练和高参数效率的压缩模型。相比从不同层拼接特征的Inception网络,DenseNet更简单有效(看来Inception因其结构复杂性没少被批判)。
定义 为单张输入图像,网络由 层组成,每一层实现非线性变换 ,其中 为层的索引号。 可以是BN,ReLU,Pooling,Conv等操作的复合函数,定义 层的输出为 。
传统的层连接: 。ResNets增加了跳跃连接: 。ResNets的一个优势是梯度可以通过恒等函数直接从后面的层流向前面的层。然而,恒等函数和 的输出通过加法合并,有可能会阻碍网络的信息流。
本文引入与ResNets不同的连接模式:从任意层到所有后续层的直接连接(图1)。结果就是,第 层接收所有之前层的特征图作为输入: 。为了便于实现,concat 的多个输入为单一张量。
受ResNet v2启发,定义 为三个连续运算的复合函数:BN,ReLU,3 x 3 Conv
当特征图的大小改变时,concat运算是不可能的,然鹅,卷积网络的一个关键组成部分就是下采样层,通过它可以改变特征图大小。为了便于在架构中进行下采样,将网络划分为多个密集连接的密集块(dense blocks),如图2所示。
将密集块之间的层称为过渡层(transition layers),它们进行卷积和池化。本文实验中的过渡层由BN,1 x 1卷积和 2 x 2平均池化组成。
如果每个函数 生成 个特征图,它后面跟着的 层有 个输入特征图,其中 是输入层的通道数。DenseNet和现有网络架构的一个重要区别是DenseNet可以有非常窄的层,如 。本文将超参数 定义为网络的成长率(growth rate)。对此的一种解释是,每一层都可以访问其块中所有前面的特征图,即,网络的『集体知识』。可以将特征图视为网络的全局状态。每一层增加自己的 个特征图到这个状态。成长率反映了每层由多少新信息对全局状态有贡献。全局状态一旦写入,就可以被网络中的任何地方访问,而不像传统网络那样,无需从一层复制到另一层。(全文精华应该就是这一段了)
1x1 conv非常有用(提升计算效率),本文也大用特用。本文定义DenseNet-B的 为 BN-ReLU-Conv(1x1)-BN-ReLU-Conv(3x3)
为了使模型更紧凑,可以减少过渡层的特征图数量。如果密集块包含 个特征图,定义接下来的过渡层生成 个特征图,其中 表示压缩率。定义 的DenseNet为DenseNet-C,本位实验中设置为 。当同时使用瓶颈层和压缩过渡层时,定义模型为DenseNet-BC。
非ImageNet数据集采用同一个架构,由3个密集块构成。ImageNet的架构如表1所示
CIFAR SVHN ImageNet
所有网络都用SGD。
CIFAR和SVHN的batch size为64,epoch分别为300和40,初始学习率为0.1,在50%和75%的epoch时分别除10。
ImageNet的batch size为256,90个epoch,初始学习率为0.1,在30和60epoch时分别除10。
weight decay为 ,动量为0.9。用He初始化。
对于CIFAR和SVHN,还在每个卷积层后接了dropout层(除第一个卷积层外),丢失率为0.2。
看表2的最后一行
DenseNet可以利用更大更深模型表示能力的增长。
如图4所示
主要用DenseNet-BC和ResNet作比较。
表面上看,DenseNets和ResNets没什么不同,两个式子的差别仅仅是输入从加法变为concat,然而,这种看似很小的修改导致两种网络架构的行为明显不同。
因为鼓励特征重用,所以得到更紧凑的模型。
如图4所示。
对DenseNets准确率提升的一种解释是各个层通过短路连接从损失函数接收额外的监督(某种深度监督)。DenseNets用隐式的方式执行相似的深度监督:网络顶部的单个分类器通过最多两到三个过渡层为所有层提供直接监督。 然而,由于在所有层之间共享相同的损失函数,因此DenseNets的损失函数和梯度基本上不那么复杂。
和随机深度的对比,随机深度有点类似DenseNet:如果所有中间层都随机丢弃,那么在相同的池化层之间的任意两层都有可能直接连接。
DenseNet就是好,就是好啊就是好。在遵循简单的连接规则的同时,DenseNets自然地整合了恒等映射,深度监督和多样化深度的属性。
又是一篇没有什么数学公式的paper,越来越感觉深度学习像物理,很多结果都是基于做实验得到的。通过对实验的观察对比分析,找出实验中的缺陷不足,从而去改进,然后发paper。黄高博士的写作套路还是非常讨喜的,特别是开头的地方,娓娓道来,一步一步告诉你为什么要这么做,为什么要引入这一步。此外,DenseNets和作者本人的工作『随机深度』也有千丝万缕的关系,看来功夫做扎实了,沿着一条道路是可以出一系列成果的。
这是个好问题。。是要进一步衍生ResNet吗?
提出密集连接结构,将ResNet的跳跃连接发扬光大为两两连接
效果比ResNet还好,通过减少滤波器个数(文中称作成长率),参数量也下来了
感觉效果提升并没有那么明显,被后续出来的ResNeXt超过了
各种网络结构的实现:
黄高本人视频讲解:
作者本人的解答: CVPR 2017最佳论文作者解读:DenseNet 的“what”、“why”和“how”
DenseNet的3个优势:
问下这句sql 中间的两个concat怎么理解
NAME LIKE CONCAT(CONCAT('%', #{pd.keywords}),'%')
两层CONCAT拼接了两次,把它拆开来看,第一个CONCAT是‘CONCAT('%', #{pd.keywords})’,假设'#{pd.keywords}'得出的值是'name',那么拼出的结果是'%name',第二个是用第一个的值再拼一个%,那么就是CONCAT('%name','%') ,得到的结果就是'%name%'。
如何在Caffe中配置每一个层的结构
何Caffe配置每层结构 近刚电脑装Caffe由于神经中国络同层结构同类型层同参数所根据Caffe官中国说明文档做简单总结 1. Vision Layers 1.1 卷积层(Convolution) 类型:CONVOLUTION 例 layers { name: "conv1" type: CONVOLUTION bottom: "data" top: "conv1" blobs_lr: 1 # learning rate multiplier for the filters blobs_lr: 2 # learning rate multiplier for the biases weight_decay: 1 # weight decay multiplier for the filters weight_decay: 0 # weight decay multiplier for the biases convolution_param { num_output: 96 # learn 96 filters kernel_size: 11 # each filter is 11x11 stride: 4 # step 4 pixels between each filter application weight_filler { type: "gaussian" # initialize the filters from a Gaussian std: 0.01 # distribution with stdev 0.01 (default mean: 0) } bias_filler { type: "constant" # initialize the biases to zero (0) value: 0 } } } blobs_lr: 习率调整参数面例设置权重习率运行求解器给习率同偏置习率权重两倍 weight_decay: 卷积层重要参数 必须参数: num_output (c_o):滤器数 kernel_size (or kernel_h and kernel_w):滤器 选参数: weight_filler [default type: 'constant' value: 0]:参数初始化 bias_filler:偏置初始化 bias_term [default true]:指定否否启偏置项 pad (or pad_h and pad_w) [default 0]:指定输入每边加少像素 stride (or stride_h and stride_w) [default 1]:指定滤器步 group (g) [default 1]: If g 1, we restrict the connectivityof each filter to a subset of the input. Specifically, the input and outputchannels are separated into g groups, and the ith output group channels will beonly connected to the ith input group channels. 通卷积变化: 输入:n * c_i * h_i * w_i 输:n * c_o * h_o * w_o其h_o = (h_i + 2 * pad_h - kernel_h) /stride_h + 1w_o通同计算 1.2 池化层(Pooling) 类型:POOLING 例 layers { name: "pool1" type: POOLING bottom: "conv1" top: "pool1" pooling_param { pool: MAX kernel_size: 3 # pool over a 3x3 region stride: 2 # step two pixels (in the bottom blob) between pooling regions } } 卷积层重要参数 必需参数: kernel_size (or kernel_h and kernel_w):滤器 选参数: pool [default MAX]:pooling目前MAX, AVE, STOCHASTIC三种 pad (or pad_h and pad_w) [default 0]:指定输入每遍加少像素 stride (or stride_h and stride_w) [default1]:指定滤器步 通池化变化: 输入:n * c_i * h_i * w_i 输:n * c_o * h_o * w_o其h_o = (h_i + 2 * pad_h - kernel_h) /stride_h + 1w_o通同计算 1.3 Local Response Normalization (LRN) 类型:LRN Local ResponseNormalization局部输入区域进行归化(激a加归化权重(母部)新激b)两种同形式种输入区域相邻channels(cross channel LRN)另种同channel内空间区域(within channel LRN) 计算公式:每输入除 选参数: local_size [default 5]:于cross channel LRN需要求邻近channel数量;于within channel LRN需要求空间区域边 alpha [default 1]:scaling参数 beta [default 5]:指数 norm_region [default ACROSS_CHANNELS]: 选择哪种LRNACROSS_CHANNELS 或者WITHIN_CHANNEL 2. Loss Layers 深度习通化输目标Loss驱习 2.1 Softmax 类型: SOFTMAX_LOSS 2.2 Sum-of-Squares / Euclidean 类型: EUCLIDEAN_LOSS 2.3 Hinge / Margin 类型: HINGE_LOSS 例: # L1 Norm layers { name: "loss" type: HINGE_LOSS bottom: "pred" bottom: "label" } # L2 Norm layers { name: "loss" type: HINGE_LOSS bottom: "pred" bottom: "label" top: "loss" hinge_loss_param { norm: L2 } } 选参数: norm [default L1]: 选择L1或者 L2范数 输入: n * c * h * wPredictions n * 1 * 1 * 1Labels 输 1 * 1 * 1 * 1Computed Loss 2.4 Sigmoid Cross-Entropy 类型:SIGMOID_CROSS_ENTROPY_LOSS 2.5 Infogain 类型:INFOGAIN_LOSS 2.6 Accuracy and Top-k 类型:ACCURACY 用计算输目标确率事实loss且没backward步 3. 激励层(Activation / Neuron Layers) 般说激励层element-wise操作输入输相同般情况非线性函数 3.1 ReLU / Rectified-Linear and Leaky-ReLU 类型: RELU 例: layers { name: "relu1" type: RELU bottom: "conv1" top: "conv1" } 选参数: negative_slope [default 0]:指定输入值于零输 ReLU目前使用做激励函数主要其收敛更快并且能保持同效 标准ReLU函数max(x, 0)般x 0输xx = 0输negative_slopeRELU层支持in-place计算意味着bottom输输入相同避免内存消耗 3.2 Sigmoid 类型: SIGMOID 例: layers { name: "encode1neuron" bottom: "encode1" top: "encode1neuron" type: SIGMOID } SIGMOID 层通 sigmoid(x) 计算每输入x输函数图 3.3 TanH / Hyperbolic Tangent 类型: TANH 例: layers { name: "encode1neuron" bottom: "encode1" top: "encode1neuron" type: SIGMOID } TANH层通 tanh(x) 计算每输入x输函数图 3.3 Absolute Value 类型: ABSVAL 例: layers { name: "layer" bottom: "in" top: "out" type: ABSVAL } ABSVAL层通 abs(x) 计算每输入x输 3.4 Power 类型: POWER 例: layers { name: "layer" bottom: "in" top: "out" type: POWER power_param { power: 1 scale: 1 shift: 0 } } 选参数: power [default 1] scale [default 1] shift [default 0] POWER层通 (shift + scale * x) ^ power计算每输入x输 3.5 BNLL 类型: BNLL 例: layers { name: "layer" bottom: "in" top: "out" type: BNLL } BNLL (binomial normal log likelihood) 层通 log(1 + exp(x)) 计算每输入x输 4. 数据层(Data Layers) 数据通数据层进入Caffe数据层整中国络底部数据自高效数据库(LevelDB 或者 LMDB)直接自内存追求高效性HDF5或者般图像格式硬盘读取数据 4.1 Database 类型:DATA 必须参数: source:包含数据目录名称 batch_size:处理输入数量 选参数: rand_skip:始候输入跳数值异步随机梯度降(SGD)候非用 backend [default LEVELDB]: 选择使用 LEVELDB 或者 LMDB 4.2 In-Memory 类型: MEMORY_DATA 必需参数: batch_size, channels, height, width: 指定内存读取数据 The memory data layer reads data directly from memory, without copying it. In order to use it, one must call MemoryDataLayer::Reset (from C++) or Net.set_input_arrays (from Python) in order to specify a source of contiguous data (as 4D row major array), which is read one batch-sized chunk at a time. 4.3 HDF5 Input 类型: HDF5_DATA 必要参数: source:需要读取文件名 batch_size:处理输入数量 4.4 HDF5 Output 类型: HDF5_OUTPUT 必要参数: file_name: 输文件名 HDF5作用节其层输入blobs写硬盘 4.5 Images 类型: IMAGE_DATA 必要参数: source: text文件名字每行给张图片文件名label batch_size: batch图片数量 选参数: rand_skip:始候输入跳数值异步随机梯度降(SGD)候非用 shuffle [default false] new_height, new_width: 所图像resize 4.6 Windows 类型:WINDOW_DATA 4.7 Dummy 类型:DUMMY_DATA Dummy 层用于development debugging具体参数DummyDataParameter 5. 般层(Common Layers) 5.1 全连接层Inner Product 类型:INNER_PRODUCT 例: layers { name: "fc8" type: INNER_PRODUCT blobs_lr: 1 # learning rate multiplier for the filters blobs_lr: 2 # learning rate multiplier for the biases weight_decay: 1 # weight decay multiplier for the filters weight_decay: 0 # weight decay multiplier for the biases inner_product_param { num_output: 1000 weight_filler { type: "gaussian" std: 0.01 } bias_filler { type: "constant" value: 0 } } bottom: "fc7" top: "fc8" } 必要参数: num_output (c_o):滤器数 选参数: weight_filler [default type: 'constant' value: 0]:参数初始化 bias_filler:偏置初始化 bias_term [default true]:指定否否启偏置项 通全连接层变化: 输入:n * c_i * h_i * w_i 输:n * c_o * 1 *1 5.2 Splitting 类型:SPLIT Splitting层输入blob离输blobs用需要blob输入输层候 5.3 Flattening 类型:FLATTEN Flattening输入n * c * h * w变简单向量其 n * (c*h*w) * 1 * 1 5.4 Concatenation 类型:CONCAT 例: layers { name: "concat" bottom: "in1" bottom: "in2" top: "out" type: CONCAT concat_param { concat_dim: 1 } } 选参数: concat_dim [default 1]:0代表链接num1代表链接channels 通全连接层变化: 输入:1K每blobn_i * c_i * h * w 输: concat_dim = 0: (n_1 + n_2 + + n_K) *c_1 * h * w需要保证所输入c_i 相同 concat_dim = 1: n_1 * (c_1 + c_2 + +c_K) * h * w需要保证所输入n_i 相同 通Concatenation层blobs链接blob 5.5 Slicing The SLICE layer is a utility layer that slices an input layer to multiple output layers along a given dimension (currently num or channel only) with given slice indices. 5.6 Elementwise Operations 类型:ELTWISE 5.7 Argmax 类型:ARGMAX 5.8 Softmax 类型:SOFTMAX 5.9 Mean-Variance Normalization 类型:MVN 6. 参考 Caffe
java语言:String类的concat方法与StringBuffer类的append方法区别?内存状态?
首先我们先看几个概念:
1:在java内部是对+进行了重载,在处理String的过程中要创建一个StringBuffer对象,用StringBuffer对象的append方法对字符串进行连接,最后调用toString方法返回String字符串。
2: +和concat操作,是先开辟一个要拼接的字符串的空间,在和老字符串一起拼接成一个新的字符串,所以在堆内存中是创建了三块空间的;
然后先来说1和2的区别:line1: 用的是+,+在底层是通过StringBuffer对象的append方法对字符串进行连接,但是他也并不是直接添加的,我们看看他开辟了几块空间?“abc”“def”“ghi”,刚开始开辟了三块堆内存空间,执行一次+,“abcdef”这是第四块内存空间,最后是最终结果“abcdefghi”开辟了第五块堆内存空间,然后其余的被回收。
line2:同样也是开辟了五块堆内存空间,concat()和+号的区别我们可以看下concat()源代码:
public?String?concat(String?str)?{
????int?otherLen?=?str.length();
????if?(otherLen?==?0)?{
????????return?this;
????}
????int?len?=?value.length;
????/*copyOf数组复制,copyOf()的第二个自变量指定要建立的新数组长度,
????如果新数组的长度超过原数组的长度,则保留为默认值null或0*/
????char?buf[]?=?Arrays.copyOf(value,?len?+?otherLen);
????//将字符从此字符串复制到目标字符数组,len为数组中的起始偏移量
????str.getChars(buf,?len);
????return?new?String(buf,?true);
}
我们可以看到concat()方法是通过copyOf(),和getChars();两个方法来拼接数组的。+在底层是通过StringBuffer对象的append方法对字符串进行连接。
最后是StringBuffer:StringBuffer使用时,只会开辟一块内存空间,使用append添加或delete删除其内容时,也是在这一块内存空间中并不会生成多余的空间。所以速度是比较快的而String 每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,对速度的影响一定是相当大的。
【前端面试:手写js系列】flat---数组扁平化
数组扁平化定义:在前端项目开发过程中,偶尔会出现层叠数据结构的数组,需要把多层数组转换为一级数组(即提取嵌套数组元素最终合并为一个数组),使其内容合并并且展开。
遍历数组的方案:
for循环
for...of
for...in
forEach()
entries()
keys()
values()
reduce()
map()
判断元素是否是数组的方案:
instanceof
constructor
object.prototype.toString.call
isArray
将数组元素进行展开一层的方案:
扩展运算法 + concat(concat() 方法用于合并两个或多个数组,在拼接的过程中加上扩展运算符会展开一层数组)
concat + apply(主要是利用 apply 在绑定作用域时,传入的第二个参数是一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。也就是在调用 apply 函数的过程中,会将传入的数组一个一个的传入到要执行的函数中,也就是相当对数组进行了一层的展开。)
toString + split(不推荐使用 toString + split 方法,因为操作字符串是很危险的事情,如果数组中的元素所有都是数字的话,toString + split 是可行的,并且是一步搞定。)
最终手写的flat()方法为:
参考博客如下,注明一下出处,感谢大神们,希望自己可以多多练习,多多回顾:
三元博客
JS数组reduce()方法详解及高级技巧
数组flat方法实现