文章目录
使用神经网络解决分类问题的步骤:
- 提取问题中实体的特征向量作为神经网络的输入。
- 定义神经网络的结构,并定义如何从神经网络的输入得到输出。—>这个过程是神经网络的前向传播算法。
- 通过训练数据来调整神经网络中参数的取值。—>训练神经网络的过程。
- 使用训练好的神经网络来预测数据。
TensorFlow 如何进行前向传播(基于全连接网络结构)
神经元
神经元是构成一个神经网络的最小单元。
- 一个神经元有多个输入和一个输出。
- 每个神经元的输入既可以是其他神经元的输入,也可以是整个神经网络的输入。
- 神经网络的结构就是指不同神经元之间的连接结构。
最简单的神经元结构的输出就是所有输入的加权和,不同输入的权重就是神经元的参数。神经网络的优化过程就是优化神经元中参数取值的过程。
前向传播计算过程
计算神经网络的前向传播结果需要三部分的信息:
- 神经网络的输入:从实体中提取的特征向量。
- 神经网络的连接结构。
- 每个神经元的参数。(即边:权重)
下图展示了一个完整的前向传播的计算过程:
这个前向传播算法可以表示为矩阵的乘法:
输入可以表示成一个 1 x 2 的矩阵:
x
=
[
x
1
,
x
2
]
x = [x_1,x_2]
x=[x1,x2]
W
(
1
)
W^{(1)}
W(1)可表示成一个 2 x 3 的矩阵:
W
(
1
)
=
[
W
1
,
1
(
1
)
W
1
,
2
(
1
)
W
1
,
3
(
1
)
W
2
,
1
(
1
)
W
2
,
2
(
1
)
W
2
,
3
(
1
)
]
W^{(1)}=\left[ \begin{matrix} W^{(1)}_{1,1} & W^{(1)}_{1,2} & W^{(1)}_{1,3} \\ W^{(1)}_{2,1} & W^{(1)}_{2,2} & W^{(1)}_{2,3} \end{matrix} \right]
W(1)=[W1,1(1)W2,1(1)W1,2(1)W2,2(1)W1,3(1)W2,3(1)]
通过矩阵乘法就可以得到隐藏层三个节点所组成的向量取值:
a
(
1
)
=
[
a
11
,
a
12
,
a
13
]
=
x
W
(
1
)
=
[
x
1
,
x
2
]
[
W
1
,
1
(
1
)
W
1
,
2
(
1
)
W
1
,
3
(
1
)
W
2
,
1
(
1
)
W
2
,
2
(
1
)
W
2
,
3
(
1
)
]
=
[
W
1
,
1
(
1
)
x
1
+
W
2
,
1
(
1
)
x
2
,
W
1
,
2
(
1
)
x
1
+
W
2
,
2
(
1
)
x
2
,
W
1
,
3
(
1
)
x
1
+
W
2
,
3
(
1
)
x
2
]
a^{(1)}=[a_{11},a_{12},a_{13}]=xW^{(1)}=[x_1,x_2] \left[ \begin{matrix} W^{(1)}_{1,1} & W^{(1)}_{1,2} & W^{(1)}_{1,3} \\ W^{(1)}_{2,1} & W^{(1)}_{2,2} & W^{(1)}_{2,3} \end{matrix} \right] \\ =[W^{(1)}_{1,1}x_1+W^{(1)}_{2,1}x_2,W^{(1)}_{1,2}x_1+W^{(1)}_{2,2}x_2,W^{(1)}_{1,3}x_1+W^{(1)}_{2,3}x_2]
a(1)=[a11,a12,a13]=xW(1)=[x1,x2][W1,1(1)W2,1(1)W1,2(1)W2,2(1)W1,3(1)W2,3(1)]=[W1,1(1)x1+W2,1(1)x2,W1,2(1)x1+W2,2(1)x2,W1,3(1)x1+W2,3(1)x2]
类似的输出层可表示为:
[
y
]
=
a
(
1
)
W
(
2
)
=
[
a
11
,
a
12
,
a
13
]
[
W
1
,
1
(
2
)
W
2
,
1
(
2
)
W
3
,
1
(
2
)
]
=
[
W
1
,
1
(
2
)
a
11
,
W
2
,
1
(
2
)
a
12
,
W
3
,
1
(
2
)
a
13
]
[y]=a^{(1)}W^{(2)}=[a_{11},a_{12},a_{13}] \left[ \begin{matrix} W^{(2)}_{1,1} \\ W^{(2)}_{2,1} \\ W^{(2)}_{3,1} \end{matrix} \right] =[W^{(2)}_{1,1}a_{11},W^{(2)}_{2,1}a_{12},W^{(2)}_{3,1}a_{13}]
[y]=a(1)W(2)=[a11,a12,a13]⎣⎢⎡W1,1(2)W2,1(2)W3,1(2)⎦⎥⎤=[W1,1(2)a11,W2,1(2)a12,W3,1(2)a13]
这样就通过矩阵乘法将前向传播算法表示出来了。在 TensorFlow 中矩阵乘法的表示方式:
a = tf.matmul(x,w1)
y = tf.matmul(a,w2)
TensorFlow 如何设置神经网络参数
神经网络中实现分类或者回归中的重要部分:神经网络参数。
tf.Variable
作用:保存和更新神经网络中的参数。
1、TensorFlow 中的变量需要指定初始值。
2、在神经网络中比较常见的是给参数赋予随机初始值,所以一般使用随机数给 TensorFlow 中的变量初始化。
# 在 TensorFlow 中声明一个 2 x 3 矩阵的方法,矩阵中元素是均值为 0,标准差为 2 的随机数。
weights = tf.Variable(tf.random_normal([2,3], stddev=2))
# tf.random_normal 通过参数 mean 来指定平均值,没有指定时默认为 0。
随机数生成函数
函数名称 | 随机数分布 | 主要参数 |
---|---|---|
tf.random_normal | 正太分布 | 平均值、标准差、取值类型 |
tf.truncated_normal | 正太分布,如果随机出来的值偏离平均值超过 2 个标准差,那么这个数将会被重新随机 | 平均值、标准差、取值类型 |
tf.random_uniform | 平均分布 | 最小、最大取值,取值类型 |
tf.random_gamma | Gamma分布 | 形状参数 alpha、尺度参数 beta、取值类型 |
常数生成函数
函数名称 | 功能 | 样例 |
---|---|---|
tf.zeros | 产生全 0 的数组 | tf.zeros([2,3],int32)-->[[0,0,0],[0,0,0]] |
tf.ones | 产生全 1 的数组 | tf.ones([2,3],int32)-->[[1,1,1],[1,1,1]] |
tf.fill | 产生一个全部为给定数字的数组 | tf.fill([2,3],9)-->[[9,9,9],[9,9,9]] |
tf.constant | 产生一个给定值的常量 | tf.constant([1,2,3])-->[1,2,3] |
# 在神经网络中,偏置项(bias)通常会使用常数来设置初始值
bias = tf.Variable(tf.zeros([3]))
通过其他变量的初始值来初始化新变量
# w2 的初始值被设置为与 weights 变量相同。
w2 = tf.Variable(weights.initialized_value())
# w3 的初始值被设置为 weights 初始值的 2 倍
w3 = tf.Variable(weights.initialized_value() * 2.0)
样例介绍神经网络参数设置及前向传播过程
import tensorflow as tf
# 声明 w1,w2 两个变量。
# 通过 seed 参数设定了随机种子:可以保证每次运行得到的结果是一样的。
w1 = tf.Variable(tf.random_normal([2,3],stddev=1,seed=1))
w2 = tf.Variable(tf.random_normal([3,1],stddev=1,seed=1))
# 暂时将输入的特征定义为一个常量
x = tf.constant([[0.7,0.9]])
# 通过最初介绍的前向传播算法获得神经网络的输出
a = tf.matmul(x,w1)
y = tf.matmul(a,w2)
sess = tf.Session()
# 这里不能直接通过 sess.run(y) 来获取 y 的值,因为 w1,w2 还没有运行初始化过程。
sess.run(w1.initializer) # 初始化 w1
sess.run(w2.initializer) # 初始化 w2
#输出 [[3.957578]]
print(sess.run(y))
sess.close();
TensorFlow 如何训练神经网络模型
- 使用监督学习的方式能更合理的设置参数取值 。
- 设置神经网络参数的过程就是神经网络的训练过程。
监督学习
重要思想:在已知答案的标注数据集上,模型给出的预测结果要尽量接近真实的答案。
通过调整神经网络中的参数对训练数据进行拟合,可以使得模型对未知的样本提供预测的能力。
训练神经网络的整体流程
下图展示了训练神经网络的流程图。
神经网络模型的训练是一个迭代的过程:
- 每次迭代开始时,先选取一小部分训练数据。(一个batch)
- 这个 batch 的样例会通过前向传播算法得到神经网络模型的预测结果。因为训练数据都是有正确答案标注的,所以可以计算出当前神经网络模型的预测答案与正确答案之间的差距。
- 基于预测值与真实值之间的差距,通过反向传播算法更新神经网络参数的取值,使得这个 batch 上神经网络模型的预测结果与真实答案更加接近。
使用 TensorFlow 实现反向传播算法
神经网络的优化算法中,最常用的是反向传播算法。
第一步:使用 TensorFlow 表达一个 batch 的数据
前面使用常量表达过一个样例:
x = tf.constant([[0.7,0.9]])
但是每生成一个常量,TensorFlow 都会在计算图中增加一个节点。如果每轮迭代中选取的数据都要通过常量来表示,那么 TensorFlow 的计算图将会很大,而且利用率很低。
解决方法:
TensorFlow 提供了 placeholder 机制用于提供输入数据。
placeholder 相当于定义了一个位置,这个位置中的数据在程序运行时再指定。这样在程序中不需要生成大量常量来提供输入数据,只需要将数据通过 placeholder 传入 TensorFlow 计算图。
# 通过 placeholder 实现前向传播算法
import tensorflow as tf
w1 = tf.Variable(tf.random_normal([2,3],stddev=1))
w2 = tf.Variable(tf.random_normal([3,1],stddev=1))
# 定义 placeholder 作为存放输入数据的地方。这里维度也不一定要定义。
# 但如果维度是确定的,那么给出维度可以降低出错的概率。
x = tf.placeholder(tf.float32, shape=(1,2), name="input")
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
sess = tf.Session()
init_op = tf.global_variables_initializer()
sess.run(init_op)
# 这一行会报错,因为没有给 placeholder 指定取值
print(sess.run(y))
# feed_dict 是一个字典(map),在字典中需要给出每个用到的 placeholder 的取值。
print(sess.run(y, feed_dict={x:[[0.7,0.9]]}))
上面程序只计算了一个样例的前向传播结果。在训练神经网络时需要提供一个 batch 的训练样例。
# 如果将输入的 1 x 2 矩阵改为 n x 2 的矩阵,那么就可以得到 n 个样例的前向传播结果。
x = tf.placeholder(tf.float32, shape=(3,2), name="input")
....
# 因为 x 在定义时指定了 n 为 3,所以在运行前向传播过程时需要提供 3 个样例数据。
print(sess.run(y, feed_dict={x:[[0.7,0.9],[0.1,0.4],[0.5,0.8]]}))
第二步:定义一个损失函数来刻画当前的预测值和真实答案之间的差距
第三步:通过反向传播算法来调整神经网络参数的取值使得差距可以被缩小。
# 以下代码定义了一个简单的损失函数,并通过 TensorFlow 定义了反向传播算法。
# 定义损失函数来刻画预测值与真实值的差距。
cross_entropy = -tf.reduce_mean(
y_ * tf.log(tf.clip_by_value(y, le-10, 1.0)))
# 定义学习率
learning_rate = 0.001
# 定义反向传播算法来优化神经网络中的参数
train_step = tf.train.AdamOptimizer(learning_rate).minimize(cross_entropy)
- cross_entropy 定义了真实值和预测值之间的交叉熵(cross entropy)–>分类问题中常用的损失函数。
- train_step 定义了反向传播的优化方法。比较常用的优化方法有三种:
– tf.train.GradientDescentOptimizer
– tf.train.AdamOptimizer
– tf.train.MomentumOptimizer
TensorFlow 实现完整神经网络样例
下面为一个解决二分类问题的神经网络:
import tensorflow as tf
# Numpy 是一个科学计算的工具包,这里通过 Numpy 工具包生成模拟数据集
from numpy.random import RandomState
# 定义训练数据集的 batch 大小
batch_size = 8
# 定义神经网络的参数
w1 = tf.Variable(tf.random_normal([2,3], stddev=1, seed=1))
w2 = tf.Variable(tf.random_normal([3,1], stddev=1, seed=1))
# 在 shape 的一个维度上使用 None 可以方便使用不大的 batch 大小。
# 在训练时需要把数据分成比较小的 batch,但在测试时可以一次性使用全部的数据。
# 当数据集比较小时这样比较方便测试,但数据集比较大时,将大量数据放入一个 batch 可能会导致内存溢出。
x = tf.placeholder(tf.float32, shape=(None,2), name="x-input")
y_ = tf.placeholder(tf.float32, shape=(None,1), name="y-input")
# 定义神经网络前向传播过程
a = tf.matmul(x,w1)
y = tf.matmul(a,w2)
# 定义损失函数和反向传播算法
cross_entropy = -tf.reduce_mean(
y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0)))
train_step = tf.train.AdamOptimizer(0.001).minimize(cross_entropy)
# 通过随机数生成一个模拟数据集
rdm = RandomState(1)
dataset_size = 128
X = rdm.rand(dataset_size, 2)
# 定义规则来给出样本的标签,在这里所有 x1+x2<1 的样例被认为是正样本,而其他为负样本。
# 使用 0 来表示负样本,1 表示正样本。
Y = [[int(x1+x2 < 1)] for (x1,x2) in X]
# 创建一个会话来运行 TensorFlow 程序
with tf.Session() as sess:
init_op = tf.global_variables_initializer()
# 初始化变量
sess.run(init_op)
print(sess.run(w1))
print(sess.run(w2))
'''
在训练之前神经网络参数的值:
w1 = [[-0.8113182 1.4845988 0.06532937]
[-2.4427042 0.0992484 0.5912243 ]]
w2 = [[-0.8113182 ]
[ 1.4845988 ]
[ 0.06532937]]
'''
# 设定训练的轮数
STEPS = 5000
for i in range(STEPS):
# 每次选取 batch_size 个样本进行训练
start = (i * batch_size) % dataset_size
end = min(start + batch_size,dataset_size)
# 通过选取的样本训练神经网络并更新参数
sess.run(train_step, feed_dict={x:X[start:end], y_:Y[start:end]})
if i % 1000 == 0:
# 每隔一段时间计算在所有数据上的交叉熵并输出
total_cross_entropy = sess.run(cross_entropy, feed_dict={x:X, y_:Y})
print("训练 %d 次后,全部数据的交叉熵为 %g" % (i, total_cross_entropy))
'''
输出结果:
训练 0 次后,全部数据的交叉熵为 0.0674925
训练 1000 次后,全部数据的交叉熵为 0.0163385
训练 2000 次后,全部数据的交叉熵为 0.00907547
训练 3000 次后,全部数据的交叉熵为 0.00714436
训练 4000 次后,全部数据的交叉熵为 0.00578471
可以发现随着训练的进行,交叉熵逐渐变小。
交叉熵越小说明预测的结果和真实的结果差距越小。
'''
print(sess.run(w1))
print(sess.run(w2))
'''
在训练之后神经网络参数的值:
w1 = [[-1.9618274 2.582354 1.6820378]
[-3.4681718 1.0698233 2.11789 ]]
w2 = [[-1.8247149]
[ 2.6854665]
[ 1.4181951]]
可以看到这两个参数的取值已经有变化了,这个变化就是训练的结果。
它使得这个神经网络能更好的拟合提供的训练数据。
'''
总结
训练神经网络的过程:
- 定义神经网络的结构和前向传播的输出结果。
- 定义损失函数以及选择反向传播优化的算法。
- 生成会话(tf.Session)并且在训练数据上反复运行反向传播优化算法。