书籍:《深度学习——基于Python语言和TensorFlow平台》
三好学生问题的引入:
学校要评选三好学生,根据德育分、智育分和体育分3项分数来计算一个总分,然后根据总分来确定谁能够被评为三好学生。现有两位孩子的家长,知道自己孩子的3项分数及总分,但是学校并没有告诉家长们计算出总分的规则,我们用人工智能中神经网络的方法来帮家长们推算出这3个权重分别是多少。
代码实现过程:
import tensorflow as tf
# 定义3个输入节点,把德育、智育和体育3个分数分别作为x1,x2,x3的数值输入
# 这种等待模型运行时才会输入的节点,在Tensorflow中要把它定义为placeholder(占位符)
# 所谓占位符,就是在编写程序的时候还不确定输入什么数,而是在程序运行的时候才会输入,编程时仅仅把这个节点定义好,先“占个位子”
# placehold在定义时调用了一个参数,这是一种高级的参数写法,叫做命名参数。
# 一般计算机语言的参数只支持按照顺序来写,不需要给每个参数命名,但这样就不允许顺序发生错误。
# 而python支持命名参数,可以通过指定参数的名称来赋值,避免只用顺序来区别参数造成错乱的情况,也可以适应有时候只需要某几个参数的情况。
# dtype是data type的缩写,表示占位符所代表的数值的类型
# tf.float32是Tensorflow中的32位浮点数类型,所谓32位浮点数,指的是用32位二进制数字来代表1个小数。
x1 = tf.placeholder(dtype=tf.float32)
x2 = tf.placeholder(dtype=tf.float32)
x3 = tf.placeholder(dtype=tf.float32)
# yTrain用来在训练时传入针对每一组输入数据期待的对应计算结果值,简称为目标计算结果或目标值
yTrain = tf.placeholder(dtype=tf.float32)
# 在神经网络中,类似权重这种会在训练过程中经常性地变化的神经元参数,Tensorflow中把它们叫做变量,这与我们一般大多数计算机语言中变量的含义还是有一些区别的
# 为避免混淆,把它们叫做神经网络的可变参数
# 除了用dtype参数来指定数值类型,还传入了另一个初始值参数,这个参数没有用命名参数的形式
# 这是因为tf.Variable函数规定第一个参数是用于指定可变参数的初始值
w1 = tf.Variable(0.1, dtype=tf.float32)
w2 = tf.Variable(0.1, dtype=tf.float32)
w3 = tf.Variable(0.1, dtype=tf.float32)
# 定义隐藏层的3个节点
n1 = x1 * w1
n2 = x2 * w2
n3 = x3 * w3
# 输出层的定义
y = n1 + n2 + n3
loss = tf.abs(y - yTrain)
# 定义一个优化器变量optimizer,所谓优化器,就是用来调整神经网络可变参数的对象
# Tensorflow中有很多种优化器,这次选用的就是AlphaGo使用的优化器RMSProOptimizer,通过tf.train.RMSProOptimizer来获得
# 参数0.001是这个优化器的学习率,学习率决定了优化器每次调整参数的幅度大小,先赋一个常用的数值0.001
optimizer = tf.train.RMSPropOptimizer(0.001)
# 定义一个训练对象train,train对象代表了准备如何来训练这个神经网络
# 把train对象定义为optimizer.minimize(loss),就是要求优化器按照把loss最小化的原则来调整可变参数
train = optimizer.minimize(loss)
# 定义了一个sess变量,它包含一个Tensorflow的会话对象,不必深究会话是什么,可以简单地把会话理解成管理神经网络运行的一个对象
# 有了会话对象,神经网络就可以正式运转了
# 在每次定义完神经网络模型后,在准备运行前都要定义一个会话对象,才能开始训练这个模型或者用训练好的模型去进行预测计算
sess = tf.Session()
# 会话对象管理神经网络的第一步,一般是要把所有的可变参数初始化,也就是给所有可变参数一个各自的初始值
# 首先让变量init等于tf.global_variables_initializer这个函数的返回值,它返回的是一个专门用于初始化可变参数的对象
# 然后调用会话对象sess的成员函数run(),带上init变量作为参数,就可以实现对之前定义的神经网络模型中所有可变参数的初始化
init = tf.global_variables_initializer()
# 就是在sess会话中运行初始化这个函数,具体给每个可变参数赋什么样的初值,是由刚才定义w1,w2,w3时的第一个函数参数来决定的
sess.run(init)
# 这两条语句也可以合起来写成sess.run(tf.global_variables_initializer())
# 执行一次神经网络的计算
# sess.run函数的第一个参数为一个数组,代表我们需要查看哪些结果项
# 为了查看结果更清除,除了最终输出层的结果y,还把输入层的x1,x2,x3和隐藏层的可变参数w1,w2,w3都获取出来,以便对比参照
# sess.run韩式的另一个参数识别个命名参数feed_dict,代表要输入的数据
# feed在英语中有喂的意思,所以有时候也称为给神经网络喂数据
# dict是dictionary的简写,代表着参数要求输入的是前面介绍过的字典类型的数值,所以要按照字典类型数值的写法
# 把sess.run函数的执行结果存到了result变量中,这是一个包含了x1,x2,x3,w1,w2,w3和y的具体数值在内的数组
# 在结果数组中有train对象,意味着要求程序要执行train对象所包含的训练过程,在这个过程中,y、loss等计算结果自然也会被计算出来
# 所以在结果数组中即使只写一个train,其他的结果也会被计算出来,只不过看不到而已
# 在结果数组中加上了yTrain和loss,以便对照
# 另外,只有在结果数组中加上了训练对象,这次sess.run函数的执行才能被称为一个训练,否则只是运行一个神经网络或者说是用神经网络运行一次计算
for i in range(2):
result = sess.run([train, x1, x2, x3, w1, w2, w3, y, yTrain, loss], feed_dict={x1: 90, x2: 80, x3: 70, yTrain: 85})
print(result)
result = sess.run([train, x1, x2, x3, w1, w2, w3, y, yTrain, loss], feed_dict={x1: 98, x2: 95, x3: 87, yTrain: 96})
print(result)
运行结果: