# 【CV知识学习】神经网络梯度与归一化问题总结+highway network、ResNet的思考

======================

RNN网络简单来说，就是把上层的hidden state与输入数据一同输入到神经元中进行处理（如左图），它是与序列相关的。如果把网络按照时间序列展开，可以得到右图

贴一个RNN的代码，有注释，很容易看明白，来自这里

  1 import copy, numpy as np
2 np.random.seed(0)
3
4 # compute sigmoid nonlinearity
5 def sigmoid(x):
6     output = 1/(1+np.exp(-x))
7     return output
8
9 # convert output of sigmoid function to its derivative
10 def sigmoid_output_to_derivative(output):
11     return output*(1-output)
12
13
14 # training dataset generation
15 int2binary = {}
16 binary_dim = 8
17
18 largest_number = pow(2,binary_dim)
19 binary = np.unpackbits(
20     np.array([range(largest_number)],dtype=np.uint8).T,axis=1)
21 for i in range(largest_number):
22     int2binary[i] = binary[i]
23
24
25 # input variables
26 alpha = 0.1
27 input_dim = 2
28 hidden_dim = 16
29 output_dim = 1
30
31
32 # initialize neural network weights
33 synapse_0 = 2*np.random.random((input_dim,hidden_dim)) - 1
34 synapse_1 = 2*np.random.random((hidden_dim,output_dim)) - 1
35 synapse_h = 2*np.random.random((hidden_dim,hidden_dim)) - 1
36
37 synapse_0_update = np.zeros_like(synapse_0)
38 synapse_1_update = np.zeros_like(synapse_1)
39 synapse_h_update = np.zeros_like(synapse_h)
40
41 # training logic
42 for j in range(10000):
43
44     # generate a simple addition problem (a + b = c)
45     a_int = np.random.randint(largest_number/2) # int version
46     a = int2binary[a_int] # binary encoding
47
48     b_int = np.random.randint(largest_number/2) # int version
49     b = int2binary[b_int] # binary encoding
50
52     c_int = a_int + b_int
53     c = int2binary[c_int]
54
55     # where we'll store our best guess (binary encoded)
56     d = np.zeros_like(c)
57
58     overallError = 0
59
60     layer_2_deltas = list()
61     layer_1_values = list()
62     layer_1_values.append(np.zeros(hidden_dim))
63
64     # moving along the positions in the binary encoding
65     for position in range(binary_dim):
66
67         # generate input and output
68         X = np.array([[a[binary_dim - position - 1],b[binary_dim - position - 1]]])
69         y = np.array([[c[binary_dim - position - 1]]]).T
70
71         # hidden layer (input ~+ prev_hidden)
72         layer_1 = sigmoid(np.dot(X,synapse_0) + np.dot(layer_1_values[-1],synapse_h))
73
74         # output layer (new binary representation)
75         layer_2 = sigmoid(np.dot(layer_1,synapse_1))
76
77         # did we miss?... if so, by how much?
78         layer_2_error = y - layer_2
79         layer_2_deltas.append((layer_2_error)*sigmoid_output_to_derivative(layer_2))
80         overallError += np.abs(layer_2_error[0])
81
82         # decode estimate so we can print it out
83         d[binary_dim - position - 1] = np.round(layer_2[0][0])
84
85         # store hidden layer so we can use it in the next timestep
86         layer_1_values.append(copy.deepcopy(layer_1))
87
88     future_layer_1_delta = np.zeros(hidden_dim)
89
90     for position in range(binary_dim):
91
92         X = np.array([[a[position],b[position]]])
93         layer_1 = layer_1_values[-position-1]
94         prev_layer_1 = layer_1_values[-position-2]
95
96         # error at output layer
97         layer_2_delta = layer_2_deltas[-position-1]
98         # error at hidden layer
99         layer_1_delta = (future_layer_1_delta.dot(synapse_h.T) + layer_2_delta.dot(synapse_1.T)) * sigmoid_output_to_derivative(layer_1)
100
101         # let's update all our weights so we can try again
102         synapse_1_update += np.atleast_2d(layer_1).T.dot(layer_2_delta)
103         synapse_h_update += np.atleast_2d(prev_layer_1).T.dot(layer_1_delta)
104         synapse_0_update += X.T.dot(layer_1_delta)
105
106         future_layer_1_delta = layer_1_delta
107
108
109     synapse_0 += synapse_0_update * alpha
110     synapse_1 += synapse_1_update * alpha
111     synapse_h += synapse_h_update * alpha
112
113     synapse_0_update *= 0
114     synapse_1_update *= 0
115     synapse_h_update *= 0
116
117     # print out progress
118     if(j % 1000 == 0):
119         print "Error:" + str(overallError)
120         print "Pred:" + str(d)
121         print "True:" + str(c)
122         out = 0
123         for index,x in enumerate(reversed(d)):
124             out += x*pow(2,index)
125         print str(a_int) + " + " + str(b_int) + " = " + str(out)
126         print "------------"
127
128         
RNN

激活函数有很多其他的选择，常看见的有ReLU、Leaky ReLU函数。下面就说一说这两个函数，对于ReLU，函数为，观察一下它的函数图像

这里记录的是Batch Normalization。主要参考（1）（2）（3）写的总结，可怜我只是个搬运工啊。

先说一说BN解决的问题，论文说要解决 Internal covariate shift 的问题，covariate shift 是指源空间与目标空间中条件概率一致，但是边缘概率不同。在深度网络中，越深的网络对特征的扭曲就越厉害（应该是这样说吧……），但是特征本身对于类别的标记是不变的，所以符合这样的定义。BN通过把输出层的数据归一化到mean = 0， var = 1的分布中，可以让边缘概率大致相同吧（知乎魏大牛说不可以完全解决，因为均值方差相同不代表分布相同~~他应该是对的），所以题目说是reducing。

那么在哪里可以使用这个BN？很明显，它应该使用在激活函数之前。而在此处还提到一个优点就是加入BN可以不使用dropout，为什么呢？dropout它是用来正则化增强网络的泛化能力的，减少过拟合，而BN是用来提升精度的，之所以说有这样的作用，可能有两方面的原因（1）过拟合一般发生在数据边缘的噪声位置，而BN把它归一化了（2）归一化的数据引入了噪声，这在训练时一定程度有正则化的效果。对于大的数据集，BN的提升精度会显得更重要，这两者是可以结合起来使用的。

为了让信息可以更好的在网络中流动（不一定是梯度消失的问题），可以使用xavier的初始化方法。主要可以看知乎专栏。为了不重复别人的工作，我简单总结一下算了。注意一个问题，xavier的初始化方法的前提假设是，激活函数是线性的（其实归一化后，可能把数据集中在了一处，就好像将BN的那张图一样）。

解决RNN的问题，提出了一种LSTM的结构，但我对LSTM还不是太熟悉，就不装逼了。主要是总结最近看的两篇文章《Training Very Deep Networks》和《Deep Residual Learning for Image Recognition》。

Highway Network

Highway Network主要解决的问题是，网络深度加深，梯度信息回流受阻造成网络训练困难的问题。先看下面的一张对比图片，分别是没有highway 和有highway的。

Residual Network

ResNet的结构与Highway很类似，如果把Highway的网络变一下形会得到，而在ResNet中，直接把门函数T(x)去掉，就得到一个残差函数，而且会得到一个恒等的映射 x ，对的，这叫残差网络，它解决的问题与Highway一样，都是网络加深导致的训练困难且精度下降的问题。残差网络的一个block如下：

posted @ 2016-08-25 22:19 chenjunjie1994 阅读(...) 评论(...) 编辑 收藏