深度前馈网络(Deep Feedforward Network),也叫做前馈神经网络(Feedforward Neural Network)或者多层感知机(Multilayer Perception,MLP),典型的深度学习模型。卷积神经网络是一种专门的前馈网络。包含网络层、隐藏层和输出层。

  花书的理论性很强,在看完相关的理论后,感觉太过于抽象,于是结合实验进一步理解,在搜索了相关的实验例子中,选取了手写数字图像识别这一示例进行学习与推导。在总结相关理论之前,先演示手写数字图像识别的完整代码实现过程。

One-hot编码

  One-hot编码(独热编码)使用N位表示N种状态,任意时候只有一位有效。举例:

1
2
3
4
5
6
7
性别:
[0, 1] 代表女
[1, 0] 代表男

数字0-9
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0] 代表1
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] 代表9

  One-hot编码优点:

  • 能处理非连续性的数值特征
  • 在神经网络中,One-hot具有较强的容错性,如神经网络输出结果 [0, 0.1, 0.2, 0.7, 0, 0, 0, 0, 0, 0],可将最大值变为1,表示为数字3。

mnist数据集

  mnist[1][2]是入门级计算机视觉数据集,包含各种手写数字图片,主要用于手写数字识别,标签大小为10的数组,用One-hot编码表示;该数据集包含四个部分:

文件 数据集 类型 数量
train-images-idx3-ubyte.gz 训练集 图片 6w数据,55000训练,5000验证
train-labels-idx1-ubyte.gz 训练集 标签 6w
t10k-images-idx3-ubyte.gz 测试集 图片 1w
t10k-labels-idx1-ubyte.gz 测试集 标签 1w

  每张数据集每张图片大小为28*28像素,可以得到一个28*28维度的向量组成数组,每个向量值介于[0,1]之间。例如在数据集中的第4张图片,坐标为4,label值为[0. 1. 0. 0. 0. 0. 0. 0. 0. 0.],根据One-hot编码可知数字为:1 。图片1的矩阵表示为:

代码实现

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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import input_data
mnist = input_data.read_data_sets("../data/mnist/", one_hot=True)

# train dataset size
print(mnist.train.images.shape)
# (55000, 784)
print(mnist.train.labels.shape)
# (55000, 10)

# validation dataset size
print(mnist.validation.images.shape)
# (5000, 784)
print(mnist.validation.labels.shape)
# (5000, 10)

# test dataset size
print(mnist.test.images.shape)
# (10000, 784)
print(mnist.test.labels.shape)
# (10000, 10)

# vector of image 4
print(mnist.train.images[0, :])
# output
[0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0.60784316 0.96470594 0.19607845 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0.48235297 0.9960785
0.34117648 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0.3647059 0.9960785 0.70980394 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0.3647059 0.9960785 0.6901961 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0.68235296 0.9960785
0.34117648 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0.19215688 0.97647065 0.9215687 0.03529412 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.21568629
0.9960785 0.91372555 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0.5411765 0.9960785 0.91372555
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0.6392157 0.9960785 0.91372555 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.6392157
0.9960785 0.91372555 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0.02352941 0.7686275 0.9960785 0.7568628
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0.06666667 0.9960785 0.9960785 0.4901961 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0.16470589 0.9960785
0.9960785 0.20784315 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0.654902 0.9960785 0.7019608 0.01176471
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0.9176471 0.9960785 0.63529414 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0.09019608 0.93725497 1.
0.63529414 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0.69411767 0.9960785 0.9960785 0.28235295 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0.10980393 0.8941177
0.9960785 0.73333335 0.00392157 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0.19607845 0.9960785 0.9960785 0.36078432
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0.19607845 0.9960785 0.7607844 0.05490196 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. ]

# label of image 4
print(mnist.train.labels[0, :])
# [0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]

构建全连接网络

手动推算

代码实现

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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# tensorflow 1.4 python 3.9
#import tensorflow as tf
#from tensorflow.examples.tutorials.mnist import input_data

# tensorflow 2.8 python 3.9
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()

# https://tensorflow.googlesource.com/tensorflow/+/master/tensorflow/g3doc/tutorials/mnist/input_data.py
import input_data

FLAGS = tf.app.flags.FLAGS
tf.app.flags.DEFINE_integer("is_train", 1, "train or predict")

# 单层全连接神经网络
def one_layer_full_connected():

mnist = input_data.read_data_sets("../data/mnist/", one_hot=True)

# 1. 建立数据占位符 x[None, 784] y_true [None, 10]
with tf.variable_scope("data"):
x = tf.placeholder(tf.float32, [None, 784])
y_true = tf.placeholder(tf.int32, [None, 10])

# 2. 建立一个全连接层的神经网络 w[784, 10]
with tf.variable_scope("fc_model"):
# 随机初始化权重和偏置
weight = tf.Variable(tf.random_normal([784, 10], mean=0.0, stddev=1.0), name="w")

bias = tf.Variable(tf.constant(0.0, shape=[10]))

# 预测None个样本的输出结果matrix [None, 784] * [784, 10] + [10] = [None, 10]
y_predict = tf.matmul(x, weight) + bias

# 3. 求出所有样本的损失,求平均值
with tf.variable_scope("soft_cross"):
# 求平均交叉熵损失
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=y_true, logits=y_predict))

# 4. 梯度下降求出损失
with tf.variable_scope("optimizer"):
train_op = tf.train.GradientDescentOptimizer(0.1).minimize(loss)

# 5. 计算准确率
with tf.variable_scope("acc"):
equal_list = tf.equal(tf.argmax(y_true, 1), tf.argmax(y_predict, 1))
accuracy = tf.reduce_mean(tf.cast(equal_list, tf.float32))

# 收集变量
tf.summary.scalar("losses", loss)
tf.summary.scalar("acc", accuracy)

# 高维度变量收集
tf.summary.histogram("weights", weight)
tf.summary.histogram("biases", bias)

# 定义初始化变量
init_op = tf.global_variables_initializer()

# 定义一个合并变量
merged = tf.summary.merge_all()

# 创建一个saver
saver = tf.train.Saver()

# 开启会话去训练
with tf.Session() as sess:
# 初始化变量
sess.run(init_op)

# 建立events文件,然后写入
filewriter = tf.summary.FileWriter("./tmp/summary/test/", graph=sess.graph)

if FLAGS.is_train == 1:
# 迭代步数去训练,更新参数预测
for i in range(2000):
# 取出真实存在的特征值和目标值
mnist_x, mnist_y = mnist.train.next_batch(50)
# 运行训练
sess.run(train_op, feed_dict={x: mnist_x, y_true:mnist_y})

# 写入每步训练的值
summary = sess.run(merged, feed_dict={x: mnist_x, y_true: mnist_y})
filewriter.add_summary(summary, i)

print("训练第%d步,当前loss为:%f,准确率为:%f" % (i, sess.run(loss, feed_dict={x: mnist_x, y_true:mnist_y}), sess.run(accuracy, feed_dict={x: mnist_x, y_true:mnist_y})))

# 保存模型
saver.save(sess, "./tmp/fnn/model")
else:
# 加载模型
saver.restore(sess, "tmp/fnn/model")

for i in range(100):
x_test, y_test = mnist.test.next_batch(1)

print("第%d张图片,手写数字目标为:%d,实际预测为:%d" % (
i,
tf.argmax(y_test, 1).eval(),
tf.argmax(sess.run(y_predict, feed_dict={x: x_test, y_true: y_test}), 1).eval()
))
return None


if __name__ == "__main__":
one_layer_full_connected()

# output
# python3 fnn.py --is_train=1
训练第0步,当前loss为:10.366982,准确率为:0.180000
训练第1步,当前loss为:10.819915,准确率为:0.100000
训练第2步,当前loss为:12.081343,准确率为:0.120000
训练第3步,当前loss为:12.595780,准确率为:0.060000
训练第4步,当前loss为:13.353249,准确率为:0.040000
训练第5步,当前loss为:11.786127,准确率为:0.180000
训练第6步,当前loss为:11.357050,准确率为:0.140000
训练第7步,当前loss为:11.209402,准确率为:0.100000
训练第8步,当前loss为:8.908377,准确率为:0.160000
训练第9步,当前loss为:12.215146,准确率为:0.040000
训练第10步,当前loss为:10.103148,准确率为:0.060000
训练第11步,当前loss为:7.380269,准确率为:0.120000
......
训练第1981步,当前loss为:1.074039,准确率为:0.740000
训练第1982步,当前loss为:0.721144,准确率为:0.820000
训练第1983步,当前loss为:0.222450,准确率为:0.920000
训练第1984步,当前loss为:0.728902,准确率为:0.820000
训练第1985步,当前loss为:0.307183,准确率为:0.940000
训练第1986步,当前loss为:0.683472,准确率为:0.800000
训练第1987步,当前loss为:1.009890,准确率为:0.820000
训练第1988步,当前loss为:0.600786,准确率为:0.880000
训练第1989步,当前loss为:1.032702,准确率为:0.820000
训练第1990步,当前loss为:0.296450,准确率为:0.860000
训练第1991步,当前loss为:0.838521,准确率为:0.800000
训练第1992步,当前loss为:0.657953,准确率为:0.820000
训练第1993步,当前loss为:0.815459,准确率为:0.780000
训练第1994步,当前loss为:0.471784,准确率为:0.860000
训练第1995步,当前loss为:0.387192,准确率为:0.820000
训练第1996步,当前loss为:0.734302,准确率为:0.780000
训练第1997步,当前loss为:1.062341,准确率为:0.920000
训练第1998步,当前loss为:1.087465,准确率为:0.760000
训练第1999步,当前loss为:0.488956,准确率为:0.800000


# python3 fnn.py --is_train=0
0张图片,手写数字目标为:7,实际预测为:7
1张图片,手写数字目标为:2,实际预测为:2
2张图片,手写数字目标为:1,实际预测为:1
3张图片,手写数字目标为:0,实际预测为:0
4张图片,手写数字目标为:4,实际预测为:4
5张图片,手写数字目标为:1,实际预测为:1
6张图片,手写数字目标为:4,实际预测为:4
7张图片,手写数字目标为:9,实际预测为:9
8张图片,手写数字目标为:5,实际预测为:6
9张图片,手写数字目标为:9,实际预测为:9
10张图片,手写数字目标为:0,实际预测为:0
11张图片,手写数字目标为:6,实际预测为:8
12张图片,手写数字目标为:9,实际预测为:9
13张图片,手写数字目标为:0,实际预测为:0
14张图片,手写数字目标为:1,实际预测为:1
15张图片,手写数字目标为:5,实际预测为:5
16张图片,手写数字目标为:9,实际预测为:9
17张图片,手写数字目标为:7,实际预测为:7
18张图片,手写数字目标为:3,实际预测为:3
19张图片,手写数字目标为:4,实际预测为:4
20张图片,手写数字目标为:9,实际预测为:9
21张图片,手写数字目标为:6,实际预测为:6
22张图片,手写数字目标为:6,实际预测为:6
23张图片,手写数字目标为:5,实际预测为:5
24张图片,手写数字目标为:4,实际预测为:4
25张图片,手写数字目标为:0,实际预测为:0
26张图片,手写数字目标为:7,实际预测为:7
27张图片,手写数字目标为:4,实际预测为:4
28张图片,手写数字目标为:0,实际预测为:0
29张图片,手写数字目标为:1,实际预测为:1
30张图片,手写数字目标为:3,实际预测为:3
31张图片,手写数字目标为:1,实际预测为:1
32张图片,手写数字目标为:3,实际预测为:3
33张图片,手写数字目标为:4,实际预测为:6
34张图片,手写数字目标为:7,实际预测为:7
35张图片,手写数字目标为:2,实际预测为:2
36张图片,手写数字目标为:7,实际预测为:7
37张图片,手写数字目标为:1,实际预测为:1
38张图片,手写数字目标为:2,实际预测为:2
39张图片,手写数字目标为:1,实际预测为:1
40张图片,手写数字目标为:1,实际预测为:1
41张图片,手写数字目标为:7,实际预测为:7
42张图片,手写数字目标为:4,实际预测为:4
43张图片,手写数字目标为:2,实际预测为:8
44张图片,手写数字目标为:3,实际预测为:3
45张图片,手写数字目标为:5,实际预测为:5
46张图片,手写数字目标为:1,实际预测为:3
47张图片,手写数字目标为:2,实际预测为:6
48张图片,手写数字目标为:4,实际预测为:4
49张图片,手写数字目标为:4,实际预测为:4
50张图片,手写数字目标为:6,实际预测为:6
51张图片,手写数字目标为:3,实际预测为:3
52张图片,手写数字目标为:5,实际预测为:5
53张图片,手写数字目标为:5,实际预测为:8
54张图片,手写数字目标为:6,实际预测为:2
55张图片,手写数字目标为:0,实际预测为:0
56张图片,手写数字目标为:4,实际预测为:4
57张图片,手写数字目标为:1,实际预测为:1
58张图片,手写数字目标为:9,实际预测为:9
59张图片,手写数字目标为:5,实际预测为:7
60张图片,手写数字目标为:7,实际预测为:7
61张图片,手写数字目标为:8,实际预测为:8
62张图片,手写数字目标为:9,实际预测为:9
63张图片,手写数字目标为:3,实际预测为:2
64张图片,手写数字目标为:7,实际预测为:7
65张图片,手写数字目标为:4,实际预测为:5
66张图片,手写数字目标为:6,实际预测为:2
67张图片,手写数字目标为:4,实际预测为:4
68张图片,手写数字目标为:3,实际预测为:3
69张图片,手写数字目标为:0,实际预测为:0
70张图片,手写数字目标为:7,实际预测为:7
71张图片,手写数字目标为:0,实际预测为:0
72张图片,手写数字目标为:2,实际预测为:2
73张图片,手写数字目标为:9,实际预测为:9
74张图片,手写数字目标为:1,实际预测为:1
75张图片,手写数字目标为:7,实际预测为:7
76张图片,手写数字目标为:3,实际预测为:3
77张图片,手写数字目标为:2,实际预测为:7
78张图片,手写数字目标为:9,实际预测为:9
79张图片,手写数字目标为:7,实际预测为:7
80张图片,手写数字目标为:7,实际预测为:7
81张图片,手写数字目标为:6,实际预测为:6
82张图片,手写数字目标为:2,实际预测为:2
83张图片,手写数字目标为:7,实际预测为:7
84张图片,手写数字目标为:8,实际预测为:5
85张图片,手写数字目标为:4,实际预测为:4
86张图片,手写数字目标为:7,实际预测为:7
87张图片,手写数字目标为:3,实际预测为:3
88张图片,手写数字目标为:6,实际预测为:6
89张图片,手写数字目标为:1,实际预测为:1
90张图片,手写数字目标为:3,实际预测为:3
91张图片,手写数字目标为:6,实际预测为:6
92张图片,手写数字目标为:9,实际预测为:8
93张图片,手写数字目标为:3,实际预测为:3
94张图片,手写数字目标为:1,实际预测为:1
95张图片,手写数字目标为:4,实际预测为:4
96张图片,手写数字目标为:1,实际预测为:1
97张图片,手写数字目标为:7,实际预测为:7
98张图片,手写数字目标为:6,实际预测为:6
99张图片,手写数字目标为:9,实际预测为:9


  模型的准确率维持在80%左右,可以看到测试集中前100张图片,第8、11、33、43、46、47、53、54、59、63、65、66、84图片识别错误。准确率与损失如图所示。

  程序参考相关深度学习视频教程,由于目前大部分资料都是基于TensorFlow1.x版本进行实验,而相较于TensorFlow2.x版本有很大差异,故在2.x基础上兼容了1.x版本的代码。为提高模型的准确率,后续会在此版本上进行升级,采用卷积神经网络对模型进行优化。

卷积神经网络优化:Deeeplearning框架-PyTorch#项目二:MNIST手写字体识别