0%

神经网络笔记Ⅱ

神经网络笔记Ⅰ

从CNN到RNN再到LSTM,深入了解三种神经网络。其中,CNN主要用于图像识别与处理,RNN处理具有动态时序行为的任务,而LSTM则在此基础上添加了记忆单元,更适合处理时间序列中间隔和延迟非常长的事件。仅靠前向传播已不满足要求,反向传播也变得重要。

CNN(卷积神经网络)

零基础入门深度学习(4) - 卷积神经网络

CNN(Convolutional Neural Netword,卷积神经网络)是一种前馈神经网络,人工神经元可以响应周围单元,可以大型图像处理。卷积神经网络包括卷积层和池化层。

基础结构

  • 特征提取层,每个神经元的输入与前一层的局部接受域相连,并提取该局部的特征。一旦该局部特征被提取后,它与其他特征间的位置关系也随之确定下来
  • 特征映射层,网络的每个计算层由多个特征映射组成,每个特征映射是一个平面,平面上所有的神经元的权值相等。采用影响函数较小的\(sigmoid\)函数作为卷积网络的激活函数,使得特征映射具有位移不变性

CNN主要用来识别位移、缩放以及其他形式扭曲不变性的二维图形。由于CNN的特征检测层通过训练数据进行学习,所以在使用CNN时,避免了显示的特征抽取,从而隐式地从训练数据中进行学习。由于同一特征映射面上的神经元权值相同,所以网络可以并行学习。

对于图像识别任务

全连接网络

  1. 参数数量过多
  2. 像素间的位置信息
  3. 网络层数限制

解决方案:

  1. 局部连接——不是全连接,减少参数数量
  2. 权值共享——一组连接可以共享共一个权重,再一次减少参数数量
  3. 下采样——使用池化(Pooling)来减少每层的样本数,进一步减少参数数量,提供鲁棒性

P.S.

鲁棒是Robust的音译,也就是健壮和强壮的意思。它是在异常和危险情况下系统生存的关键。比如说,计算机软件在输入错误、磁盘故障、网络过载或有意攻击情况下,能否不死机、不崩溃,就是该软件的鲁棒性。所谓“鲁棒性”,是指控制系统在一定(结构,大小)的参数摄动下,维持其它某些性能的特性。根据对性能的不同定义,可分为稳定鲁棒性和性能鲁棒性。以闭环系统的鲁棒性作为目标设计得到的固定控制器称为鲁棒控制器。

卷积神经网络:卷积层、Pooling层(池化层)、全连接层

INPUT -> [[CONV]*N -> POOL]*M -> [FC]*K

N个卷积层叠加再加上一个池化层,重复M次,加上K个全连接层。

概念介绍和计算方式请看下面这篇博客

【深度学习系列】卷积神经网络CNN原理详解(一)——基本原理

总结:

  1. 池化方式:
    1. MaxPooling:取滑动窗口里最大的值
    2. AveragePooling:取滑动窗口内所有值的平均值
    3. StochasticPooling,介于两者之间,通过对像素点按照数值大小赋予概率,再按照概率进行亚采样;
  2. Zero Padding(补零):保留原始图片的尺寸

卷积

对于一个\(l_1*l_2\)的图像,使用\(f_1*f_2\)的filter进行卷积(后者小于前者),得到一个\(f_3*f_4\)的feature map。

\[ a_{ij}=f(\sum\limits_{m=0}^{f_1-1}\sum\limits_{n=0}^{f_2-1}w_{mn}x_{(m+i)(n+j)}+w_b) \]

\(a_{ij}\)表示的是feature map的元素,\(w_{mn}\)表示对应元素,\(f\)函数表示激活函数,\(x_{(m+i)(n+j)}\)表示图像的元素,\(w_b\)表示偏置项,这里从0开始计数,所以需要-1。

这里简单起见,步幅设置为1。

图像大小、步幅和卷积后的Feature Map大小是有关系的。

\(W_2=(W_1-F+2P)/S+1\)

\(H_2=(H_1-F+2P)/S+1\)

\(W_2\)是卷积后Feature Map的宽度;\(W_1\)是卷积前图像的宽度;\(F\)是filter的宽度;\(P\)Zero Padding数量,Zero Padding是指在原始图像周围补几圈0,如果的值是1,那么就补1圈0;\(S\)步幅\(H_2\)是卷积后Feature Map的高度;\(H_1\)是卷积前图像的宽度。

二维卷积公式,已知矩阵\(A\)\(B\),行列数分别用\(m\)\(n\)表示。卷积公式如下

\(C_{st}=\sum\limits_0^{m_A-1}\sum\limits_0^{n_A-1}A_{mn}B_{(s-m)(t-n)}\)

\(0\le s<m_A+m_B-1\)

\(0\le t<n_A+n_B-1\)

也可以写成\(C=A*B\)

数学中的卷积与卷积神经网络中的卷积是一定区别的。可以把神经网络中的卷积称为互相关(cross-correlation)。这两种操作转换方式,可以通过把矩阵\(A\)翻转180度,再交换\(A\)\(B\)运算位置,即可以将卷积变为互相关。

RNN(循环神经网络)

RNN(Recurrent neural Network,循环神经网络/多层反馈网络)是一种节点定向连接成环的人工神经网络。这种网络的内部状态可以展示动态时序行为。不同于前馈神经网络的是,RNN可以利用它内部的记忆来处理任意时序的输入序列,这让它可以更容易处理,如不分段的手写识别、语音识别等。

RNN网络的本质特征是处理单元之间既有内部的反馈连接又有前馈连接。从系统的观点看,它是一个反馈动力系统,在计算过程中体现过程动态特性,比前馈神经网络具有更强的动态行为和计算能力。

参考文章

Talk is cheap. Show me the code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# coding: utf-8

import numpy as np
import copy


def sigmoid(x, deriv=False):
# sigmoid激活函数
if (deriv == True):
return x * (1 - x)
return 1 / (1 + np.exp(-x))


# 存储10进制转2进制的字典
int2binary = {}
# 二进制数最大长度
binary_dim = 8
# 最大10进制数
larget_number = 2 ** binary_dim
binary = np.unpackbits(
np.array([range(larget_number)], dtype=np.uint8).T, axis=1)
for i in range(larget_number):
int2binary[i] = binary[i]

# 梯度
alpha = 0.1
# 输入维度,两个数
input_dim = 2
# 隐单元维度
hidden_dim = 16
# 输出维度,一个数
output_dim = 1

# 两层神经网络。每层权重,[-1, 1]
syn_0 = 2 * np.random.random((input_dim, hidden_dim)) - 1
syn_1 = 2 * np.random.random((hidden_dim, output_dim)) - 1
# 神经网络连接前一个隐藏层的权重
syn_h = 2 * np.random.random((hidden_dim, hidden_dim)) - 1

# 存储权重的更新
syn_0_update = np.zeros_like(syn_0)
syn_1_update = np.zeros_like(syn_1)
syn_h_update = np.zeros_like(syn_h)


for _ in range(10000):
# 一个简单的加法问题,c=a+b
# 在0,128(避免相加超过256)之间一个随机数a,十进制表示
a_int = np.random.randint(larget_number / 2)
# 二进制表示a
a = int2binary[a_int]

b_int = np.random.randint(larget_number / 2)
b = int2binary[b_int]
# 正确答案c
c_int = a_int + b_int
c = int2binary[c_int]

# 神经网络计算的答案
d = np.zeros_like(c)
# 计算的误差
overallError = 0
# 记录l2_delta、l1
layer_2_deltas = []
layer_1_values = []
# l1初始值全为0
layer_1_values.append(np.zeros(hidden_dim))

# 前向传播。特征是二进制数维度。
for position in range(binary_dim):
# X是1*2的矩阵,[[m, n]],m、n是二进制数a、b在每个位置的01值。从最后一维开始遍历
X = np.array(
[[a[binary_dim - position - 1], b[binary_dim - position - 1]]])
# y是1*1的矩阵。[[t]],t是二进制数c在每个位置的01值
y = np.array([[c[binary_dim - position - 1]]]).T

# 计算输出层,np.dot(X,syn_0)是正常操作:从输入层到隐藏层;np.dot(layer_1_values[-1],syn_h):从前一层隐藏层到这一层隐藏层
layer_1 = sigmoid(np.dot(X, syn_0) + np.dot(layer_1_values[-1], syn_h))
layer_2 = sigmoid(np.dot(layer_1, syn_1))
# 计算l2偏差
layer_2_error = y - layer_2
# 保存偏差加权导数l2_delta
layer_2_deltas.append(layer_2_error * sigmoid(layer_2, deriv=True))
# 误差总和
overallError += np.abs(layer_2_error[0])
# 神经网络每个维度的计算的01值
d[binary_dim - position - 1] = np.round(layer_2[0][0])
# 保存l1层,在下次迭代时计算下次传播的隐藏层
# layer_1_values.append(copy.deepcopy(layer_1))
layer_1_values.append(layer_1.copy())

# 存储反向传播的l1_delta
futuer_layer_1_delta = np.zeros(hidden_dim)
# 反向传播.(input + prev_input) -> hidden -> output
for position in range(binary_dim):
# 从第一维开始遍历
X = np.array([[a[position], b[position]]])
# 选出前面计算得到的l1作为当前层,反向计算
layer_1 = layer_1_values[-position - 1]
# 选择l1前一层的隐藏层
prev_layer_1 = layer_1_values[-position - 2]
# 选择出l2,当前层输出的误差
layer_2_delta = layer_2_deltas[-position - 1]
# 计算当前隐藏层误差,计算前一层的l1_delta到当前层,加上正常操作。
layer_1_delta = (np.dot(futuer_layer_1_delta, syn_h.T) +
layer_2_delta.dot(syn_1.T)) * sigmoid(layer_1, deriv=True)

# 更新权重更新的参数。将l1转换为二维矩阵
syn_1_update += np.atleast_2d(layer_1).T.dot(layer_2_delta)
syn_h_update += np.atleast_2d(prev_layer_1).T.dot(layer_1_delta)
# X已经是二维矩阵,不需要转换
syn_0_update += X.T.dot(layer_1_delta)
# 替换futuer_l1
futuer_layer_1_delta = layer_1_delta

# 调整每层权重
syn_0 += syn_0_update * alpha
syn_1 += syn_1_update * alpha
syn_h += syn_h_update * alpha
# 归零更新权重参数
syn_0_update *= 0
syn_1_update *= 0
syn_h_update *= 0

if (_ % 1000 == 0):
print("Error: " + str(overallError))
print("Pred: " + str(d))
print("True: " + str(c))
# 二进制转十进制
out = 0
for index, x in enumerate(reversed(d)):
out += x * (2**index)
print(str(a_int) + " + " + str(b_int) + "=" + str(out))
print("-------------")

将前向传播比喻为字母表顺序读出,反向传播就是字母表反序读出。

LSTM(时间递归神经网络)

长短期记忆(英语:Long Short-Term Memory,LSTM)是一种时间递归神经网络(RNN),论文首次发表于1997年。由于独特的设计结构,LSTM适合于处理和预测时间序列中间隔和延迟非常长的重要事件。

LSTM的表现通常比时间递归神经网络及隐马尔科夫模型(HMM)更好,比如用在不分段连续手写识别上。作为非线性模型,LSTM可作为复杂的非线性单元用于构造更大型深度神经网络。