CNN网路结构可视化

简介

可视化方法

  • 传统CNN可视化
    • 对kernel可视化
    • 对中间feature map可视化
    • 对全连接weight可视化
  • 反卷积网络 可视化

传统CNN可视化

单层sparse autoencoder在的可视化

对autoencoder的参数W进行可视化。(训练数据STL-10)

通过可视化可以看到,autoencoder学习到的参数类似边缘检测器(a set of edge detectors)
不同的隐藏单元学会了在图像的不同位置和方向进行边缘检测。

以上来自ufldl

这里sparse autoencoder是在图片88的patch上学习得到的,学到**参数W类似CNN中的88的卷积核**。因此UFLDL的教程中直接把w当做cnn的卷积核,效果也不错。

cnn的可视化

  • 传统CNN可视化的缺陷:
    • 传统CNN可视化的一个缺陷是,不能够还原图片,只能单向可视化。例如,给定一个hidden feature。

反卷积网络 可视化

反卷积网络与传统CNN网络采用同样的结构,只不过训练方法不同。反卷积网络采用非监督学习,类似autoencoder的思想。

这样

deep dream 详解

见参考中的deep dream源码

#

todo

  • 可视化梯度消失、梯度爆炸
  • 可视化ReLU对梯度消失的影响
  • 可视化LSTM对梯度消失的影响

借鉴deep-dream,可视化lstm

参考

paper

博客

code

反卷积 逆卷积(Deconvolution) - CNN的逆过程

背景

众所周知,神经网络分为有向网络无向网络。典型的无向网络有CRF、Autoencoder、RBM等。由于Autoencoder和RBM是层级结构,也叫双向网络。这种是专门设计的双向网络,具有很好的理论依据(特别是RBM)。用的更多的网络,比如NN、CNN等都是单向网络。

PGM

理论好,实践弱

概率图模型,又称有向图模型。
实际上我们是可以通过后验概率,计算图模型的反向。

但是

  1. PGM中的后验概率计算一般都很复杂,特别是对于深层PGM,要算很多层叠的微积分。没有
  2. 模型的求解难
    1. 深层模型要用EM算法,
    2. 由于后验求不出来,大家就采用很多近似方法(比如变分)以及基于采样的方法。

得益于漂亮的理论基础,PGM也曾大放异彩,占领学术界和工业界高地。比如曾经风靡的HMM,LDA。涌现了Jordan Blei Daffni Ng一大批牛人。
但是PGM的深层扩展性较差,常见的比较深层的PGM就是Blei大神的LDA了。然而神经网络已经动不动上百层了。
随着大数据来临,PGM越来越力不从心,渐渐被神经网络取代。

RBM

理论漂亮,实践,大家还是用autoencoder,而不用RBM。

复杂性

CRF

条件随机场,又叫概率无向图,

NN

首先我们来看一下,RBM和autoencoder是怎样可逆的?

autoencoder

强制学习双向的全连接参数W

deconv

神奇的CNN逆过程。

在我的世界观里,CNN是用来特征学习的,网络是单向的,特别是卷积层和pooling层是不可逆的。
然而竟然听说有deconv network。

实际上就是类似autoencoder的思想。

反池化过程

反激活

在Alexnet中,relu函数是用于保证每层输出的激活值都是正数,因此对于反向过程,我们同样需要保证每层的特征图为正值,也就是说这个反激活过程和激活过程没有什么差别,都是直接采用relu函数。

反卷积

信号里的卷积是不可逆的,比如高通滤波,难道还能把高频还原?显然是行不通的。

这里只是采用autoencoder的思想,强制学习双向的卷积kernel

反全连接

应用

deconvolutional network相关文章,其实deconv的用处还挺广的,涉及到visualization/pixel-wiseprediction/unsupervised learning/image generation都会用到deconv的结构。比如Deconvolutional Network[1][2]做图片的unsupervised feature learning,ZF-Net论文中的卷积网络可视化[3],FCN网络中的upsampling[4],GAN中的Generative图片生成[5]。

参考

Adaptive deconvolutional networks for mid and high level feature learning

图像风格迁移-Image Style Transfer

简介

图片1中的内容 + 图片2中的风格 = 新图片

难点

  1. 怎样提取出content信息?
  2. 怎样提取出style信息?
  3. 怎样融合content和style?

大佬们围绕这几个点,绞尽脑汁,各显神通。

核心思想

分解

我们先从简单问题的入手,问题1看上去好像比较简单。

怎样提取出content信息?

怎样提取出style信息?

这几年CNN挺火,据说CNN的不同层能学习到不同级别的特征。是不是也能学习到style信息呢?带着这个疑问,我们先来看看CNN学到的是什么。

https://distill.pub/2017/feature-visualization/

细节

网络的高层特征一般是关于输入图像的物体和布局等信息低层特征一般表达输入图像的像素信息。也就是说在提取content特征时,不同层的表达效果是不一样的,本文在后面提取图像的content特征时采用高层特征。

参考文献 | 发展史

2015 A Neural Algorithm of Artistic Style
2016年的CVPR Image Style Transfer Using Convolutional Neural Networks

mean field

mean field method,一句话,就是后验概率用fully factorized approximation,即后验概率的每个维度都相互独立。见图MLAPP P740

但是这一个假设太强,很多时候近似效果不好,因此采用structured mean field

分词

简介 分句 用户词典 NER 新词识别 模型/算法 开源
结巴分词 开源
哈工大LTP 根据中文标点里的句号、问号、感叹号、分号、省略号。 C++
中科院NLPIR
计算所ICTCLAS CHMM(层叠形马尔可夫模型)。进行原子切分,然后在此基础上进行N-最短路径粗切分,找出前N个最符合的切分结果,生成二元分词表,然后生成分词结果,接着进行词性标注并完成主要分词步骤 C/C++
IKAnalyzer 轻量级 结合词典分词和文法分析算法 java

参考

https://www.zhihu.com/question/19578687

前后端渲染之争

简介

前端渲染是在客户端完成字符串替换,后端渲染当然在服务器完成

-「后端渲染」指传统的 ASP、Java 或 PHP 的渲染机制;以及最初的CGI
-「前端渲染」指使用 JS 来渲染页面大部分内容,代表是现在流行的 SPA 单页面应用;

前端渲染

前端渲染的优势

  • 局部刷新。无需每次都进行完整页面请求
  • 懒加载。如在页面初始时只加载可视区域内的数据,滚动后rp加载其它数据,可以通过 react-lazyload 实现
  • 富交互。使用 JS 实现各种酷炫效果
  • 节约服务器成本。省电省钱,JS 支持 CDN 部署,且部署极其简单,只需要服务器支持静态文件即可
  • 天生的关注分离设计。服务器来访问数据库提供接口,JS 只关注数据获取和展现
  • JS 一次学习,到处使用。可以用来开发 Web、Serve、Mobile、Desktop 类型的应用

坏处:

  • 占用(一部分、少部分)客户端运算资源(解析模板)。前端代码多点,毕竟包含模板代码了么。脚本是不是首次下就慢点了(看你在意不在意这个毕竟能304和CDN啥的)。可能造成前后两份模板的情况,总归要后端吐出个首屏啥的先让用户看见吧。那这部分页面模板不就是后端拼好了吐出来的么
  • mathjax.js
  • angualr
  • vue
  • bootstrap也是吧

后端渲染

后端渲染的优势

  • 服务端渲染不需要先下载一堆 js 和 css 后才能看到页面(首屏性能)
  • SEO
  • 服务端渲染不用关心浏览器兼容性问题(随着浏览器发展,这个优点逐渐消失)
  • 对于电量不给力的手机或平板,减少在客户端的电量消耗很重要
  • python flask,django
  • java jsp
    -

参考

深度学习中的梯度爆炸 梯度消失

梯度消失

随着网络深度的加深,梯度消失问题会愈加明显。无论cnn,rnn

什么形式会爆炸

f_{t+1}

累加的形式不消失
$f_{t+1} = w * f_{t} + w f_{t-1} + …$

CNN

。设输入数据为 $x$ ,对 $x$ 的卷积操作就可以看做是 $Wx+b$

我们设第一层卷积的参数为$W_1, b_1$ ,第二层卷积的参数是$W_2, b_2$,依次类推。又设激活函数为$f$ ,每一层卷积在经过激活函数前的值为$a_i$,经过激活函数后的值为$f_i$。

按照上面的表示,在CNN中,输入$x$ ,第一层的输出就是$f_1=f[W_1x+b_1]$,第二层的输出就是$f_2= f[W_2f[W_1x+b_1]+b_2]$ ,第三层的输出就是$f_3= f[W_3f[W_2f[W_1x+b_1]+b_2]+b_3]$。设最终损失为$L$ ,我们来尝试从第三层开始,用BP算法推导一下损失对参数$W_1$的偏导数,看看会发生什么。

为了简洁起见,略过求导过程,最后的结果为
$\frac{\partial{L}}{\partial{W_1}}=\frac{\partial{L}}{\partial{f_3}}\frac{\partial{f_3}}{\partial{a_3}}W_3\frac{\partial{f_2}}{\partial{a_2}}W_2\frac{\partial{f_1}}{\partial{a_1}}\frac{\partial{a_1}}{\partial{W_1}}$。
我们常常说原始神经网络的梯度消失问题,这里的$\frac{\partial{f_3}}{\partial{a_3}}$、$\frac{\partial{f_2}}{\partial{a_2}}$
就是梯度消失的“罪魁祸首”
例如sigmoid的函数,它的导数的取值范围是(0, 0.25],也就是说对于导数中的每一个元素,我们都有$0<\frac{\partial{f_3}}{\partial{a_3}}\le0.25$,$0<\frac{\partial{f_2}}{\partial{a_2}}\le0.25$,小于1的数乘在一起,必然是越乘越小的。这才仅仅是3层,如果10层的话, 根据$0.25^{10}\approx0.000000954$,第10层的误差相对第一层卷积的参数 $W_1$的梯度将是一个非常小的值,这就是所谓的“梯度消失”。

ReLU函数的改进就是它使得 $\frac{\partial{f_3}}{\partial{a_3}}\in{0,1}$ , $\frac{\partial{f_2}}{\partial{a_2}}\in{0,1}$ , $\frac{\partial{f_1}}{\partial{a_1}}\in{0,1}$ ,这样的话只要一条路径上的导数都是1,无论神经网络是多少层,这一部分的乘积都始终为1,因此深层的梯度也可以传递到浅层中。

那为什么同样的方法在RNN中不奏效呢?其实这一点Hinton在它的IRNN论文里面(arxiv:[1504.00941] A Simple Way to Initialize Recurrent Networks of Rectified Linear Units)是很明确的提到的:

也就是说在RNN中直接把激活函数换成ReLU会导致非常大的输出值。为了讲清楚这一点,我们先用同上面相似的符号把原始的RNN表示出来:

$$a_i=Wf_{i-1}+Ux_{i}+b_i$$,
$$f_i=f[a_i]$$

在这个表示中,RNN每个阶段的输入是 $x_i$,和CNN每一层使用独立的参数$W_i$不同,原始的
RNN在每个阶段都共享一个参数 $W$。如果我们假设从某一层开始输入 $x_i$ 和偏置 $b_i$ 都为0,
那么最后得到的输出就是 $f[W…[Wf[Wf[Wf_i]]]]$ ,这在某种程度上相当于对参数矩阵 $W$ 作连乘,
很显然,只要 $W$ 有一个大于1的特征值,在经过若干次连乘后都会导致结果是一个数值非常庞大的矩阵。

另外一方面,将激活函数换成ReLU也不能解决梯度在长程上传递的问题。同样考虑 $f_3$ 对 $W$ 的导数。在CNN中,每一层的参数 $W_1,W_2,W_3……$ 是互相独立的,然而RNN中 W 参与了每个时间段的运算,因此 $f_3$ 对 $W$ 导数更复杂,写出来是

$\frac{\partial{f_3}}{\partial{W_1}}=\frac{\partial{f_3}}{\partial{a_3}}f_2+\frac{\partial{f_3}}{\partial{a_3}}W\frac{\partial{f_2}}{\partial{a_2}}f_1+\frac{\partial{f_3}}{\partial{a_3}}W\frac{\partial{f_2}}{\partial{a_2}}W\frac{\partial{f_1}}{\partial{a_1}}\frac{\partial{a_1}}{\partial{W_1}}$ 。我们可以看下最后 $\frac{\partial{f_3}}{\partial{a_3}}W\frac{\partial{f_2}}{\partial{a_2}}W\frac{\partial{f_1}}{\partial{a_1}}\frac{\partial{a_1}}{\partial{W_1}}$
这部分,使用ReLU后,当梯度可以传递时,有
$\frac{\partial{f_3}}{\partial{a_3}}=\frac{\partial{f_2}}{\partial{a_2}}=\frac{\partial{f_3}}{\partial{a_1}}=1$ ,但这个式子中还是会有两个 $W$ 的连乘。在更长程上,就会有更多 $W$ 的连乘。对于CNN来说,这个部分是 $W_1,W_2,W_3…..$ 进行连乘,一方面它们都是稀疏矩阵,另一方面 $W_1,W_2,W_3….$ 互不相同,很大程度上都能抵消掉梯度爆炸的影响。

最后,IRNN在RNN上使用了ReLU,取得了比较好的结果,其中的原因在于,它对 $W$ 和 $b_i$ 取了比较特殊的初值: $W=I$ , $b_i=0$ 。
这样在梯度的式子 $\frac{\partial{f_3}}{\partial{a_3}}W\frac{\partial{f_2}}{\partial{a_2}}W\frac{\partial{f_1}}{\partial{a_1}}\frac{\partial{a_1}}{\partial{W_1}}$ 中W尽管会连乘,但是会类似于单位矩阵 $I$ 的连乘,不会引起太明显的梯度数值变化。另外一方面,也不会引起非常大的输出值

RNN

LSTM

梯度爆炸 策略

梯度爆炸问题其实不是什么麻烦。

因为现在大家都会做某种形式的Gradient clipping(也就是限定一下梯度绝对值的上限,超过就截断)来避免梯度爆炸。
觉得Gradient clipping很糙?其实一点都不糙,因为用SGD训练深度模型数学上本身就已经糙的不能再糙了。觉得LSTM不需要这种东西?No。如果查查主流工具包,或者看看比较实际的LSTM应用论文,应该都至少这么做了。

SGD为什么糙?只有GD才不糙?一切近似算法都糙?

saizheng: relu确实容易explode,除非加大很tricky的clipping,因为clipping多了,优化就做不好了
为什么??

saizheng: 普通rnn做超长memory一般就用quoc le的那个trick,identity init,我猜你看过那个paper,但是那个东西的tradeoff就是损失了短时间段内fit复杂nonlinearty的能力

参考

java的正则表达式

正则表达式实例

Java 正则表达式和 Perl 的是最为相似的。

java.util.regex 包主要包括以下三个类:

Pattern 类

pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。

Matcher 类

Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。

PatternSyntaxException:
PatternSyntaxException 是一个非强制异常类,它表示一个正则表达式模式中的语法错误。

参考