Tensorflow 学习1:从入门到搭建初级的神经网络
读书笔记
*** 参考书籍:《Tensorflow 实战 Google 深度学习框架》***
1.tensorflow本身运算是张量,没有具体的值
2.tensorflow 可以用Session来将张量变成具体的数值
tf.constant()
用来产生具体的数字或者向量
相应的:
tf.Variable()
是程序产生相应的矩阵变量
tf.Session.run()
session 用来执行定义好的运算并管理运行时的所有资源
sess = tf.Session()
value = sess.run() #获取值
sess.close()
相当于
with tf.Session() as sess
sess.run()
with 开头所启用的上下文的范围就是sess的存活范围
分类问题中,拿零件来举例,可以用零件的长度和质量来描述,这样来说一个物理意义上的零件就可以被转化成长度和质量这两个数字,机器学习中,所有用于描述实体的数字的组合就是一个实体的特征向量(feature vector),这样可以把物理实体转化成平面上的一个点,比如长度和质量代表的是零件,那么零件就是二维平面上的一个点
特征向量就是神经网络的输入
神经网络解决问题分为四个步骤:
1. 提取问题中的实体特征作为神经网络的输入
2. 定义神经网络的结构,并定义如何从神经网络的输入得到输出
3. 通过训练数据来调整神经网络中的参数取值,这就是训练神经网络的过程
4.使用训练好的神经网络来预测未知的数据
Tensorflow 用于神经网络搭建
前向传播算法
a = tf.matmul(x,w1)
b = tf.matmul(a,w2)
其中,x 和 a 分别是两个不同的层;w1 和 w2 分别是进入不同层的权重的矩阵
tf.Variable()
tf.Variable 的作用就是保存和更新神经网络中的参数。
tensorflow中的变量也要指定初始值,一般用随机数给tensorflow中的变量初始化
weights = tf.Variable(tf.random_normal([2,3],stddev = 2))
tf.random_normal
生成一个随机的正态分布的二维矩阵,标准差为2
tf.truncated_normal
正态分布,但如果随机出来的值偏离平均值超过两个标准差,那么这个数会被重新随机产生出来
tf.random_uniform
均匀分布
tf.random_gamma
Gamma 分布
weights = tf.Variable(shape, mean=0.0, stddev = 1.0, dtype = tf.float32, seed = None, name = None)
- shape: 输出张量的形状,必选
- mean: 正太分布的均值,默认为0
- stddev: 正太分布的标准差,默认为1.0
- dtype: 输出的类型,默认为tf.float32
- seed: 输出随机数种子,是一个整数,设置之后下一次生成的随机数和本次一样
- name: 操作的名称
tensorflow 还可以通过常数来初始化一个变量:
tf.zeros
: 产生全零的数组
tf.ones
: 产生全一的数组
tf.fill
: 产生一个全部为给定数字的数组
tf.constant
: 产生一个给定值的常量
神经网络中偏置顶 bias 通常会用常数来设置初始值:
biases = tf.Variable(tf.zeros([3])
一个变量在被使用之前,这个变量的初始化过程需要被明确地调用
Tensorflow 前向传播的过程
import tensorflow as tf
#weights = tf.Variable(tf.random_normal([2,3], stddev=2))
# w1 = tf.Variable(weights.initialized_value())
# w2 = tf.Variable(weights.initialized_value()*2.0)
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是一个1×2的矩阵
x = tf.constant([[0.7,0.9]])
#通过前向传播算法获得神经网络的输出:
a = tf.matmul(x,w1)
y = tf.matmul(a,w2)
sess = tf.Session()
#!这里不能简单的用sess.run()来获取y的取值
#因为w1 和 w2 都还没有运行初始化的过程。下面两行分别初始化了w1 和 w2 两个变量
sess.run(w1.initializer) #初始化w1
sess.run(w2.initializer) #初始化w2
print(sess.run(y))
sess.close()
每次运行代码之前要将变量都进行初始化,initializer,但是也可以把全局变量初始化
tf.initialize_all_variables()
init_all = tf.initialize_all_variables()
sess.run(init_all)
import tensorflow as tf
#weights = tf.Variable(tf.random_normal([2,3], stddev=2))
# w1 = tf.Variable(weights.initialized_value())
# w2 = tf.Variable(weights.initialized_value()*2.0)
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是一个1×2的矩阵
x = tf.constant([[0.7,0.9]])
#通过前向传播算法获得神经网络的输出:
a = tf.matmul(x,w1)
y = tf.matmul(a,w2)
sess = tf.Session()
#!这里不能简单的用sess.run()来获取y的取值
#因为w1 和 w2 都还没有运行初始化的过程。下面两行分别初始化了w1 和 w2 两个变量
#sess.run(w1.initializer) #初始化w1
#sess.run(w2.initializer) #初始化w2
init_all = tf.initialize_all_variables() 分别初始化变量可以通过这种全局变量初始化方式来替换
sess.run(init_all) #sess.run()是必须执行的,而且必须对于每一个变量都要sess.run但是我们同时也可以先将全部的变量初始化之后,对全局初始化的变量init_all进行sess.run
print(sess.run(y))
sess.close()
‘’‘值得注意的是,如果想要得到w1和w2节点中的数据,还是要分别sess.run各个节点
比如 `print (sess.run(w1))` `print(sess.run(w2))`
如果只是 `print(w1)`或者w2 还是只会输出张量信息’‘’
tf.all_variables
可以获得此时计算图上所有的变量
tf.trainable_variables
可以获得所有需要优化的参数
张量中维度(shape)和类型(type)是最重要的属性,变量的类型是不可改变的;一个变量在构建之后,他的类型就不能再改变了
tf.assign(w1,w2)
是将w2的值传到w1里面去; 其作用相当于w1.assign(w2)
但是如果w1和w2 维度不匹配,比如:
w1 = tf.Variable(tf.random_normal([2,3], stddev=1), name = "w1")
w2 = tf.Variable(tf.random_normal([2,2], stddev=1), name = "w2")
w3 = tf.assign(w1, w2)
那么会报错误:
ValueError: Dimension 1 in both shapes must be equal, but are 3 and 2. Shapes are [2,3] and [2,2]. for 'Assign' (op: 'Assign') with input shapes: [2,3], [2,2]
但是可以强行改正维度:
w1 = tf.Variable(tf.random_normal([2,3], stddev=1), name = "w1")
w2 = tf.Variable(tf.random_normal([2,2], stddev=1), name = "w2")
w3 = tf.assign(w1, w2, validate_shape=False)
w1 就会跟 w2 的数组变成一样的; 无论后面的数组是维度高一些还是低一些,合并后,都会直接复制后面w2的内容; 也就是说:w3 永远和 w2 保持一致
通过Tensorflow训练网络模型
上面提到了随机产生参数矩阵的方式,但是在使用神经网络解决问题的过程中需要更加慎重地设置参数来解决回归和分类问题 ; 设置神经网络参数的过程就是神经网络的训练过程
监督学习最重要的思想就是在已知答案的标注数据集上,模型给出的结果尽可能地接近真实的答案。通过调整神经网络中的参数对训练数据进行拟合,可以使得模型对于未知的样本提供预测的能力。
在神经网络优化算法中,常用的是反向传播算法(backpropagation)
反向传播算法简介
- 每次迭代开始,首先选取一小部分数据(batch)
- 这个batch的样例通过前向传播算法得到神经网络模型的预测结果
- 用这个batch算出当前神经网络模型和预测答案之间的差距
- 根据这个差距可以用来更新神经网络参数的取值,使在这个batch上神经网络的预测结果更加准确
如果每一轮迭代中选取的数据都用常量来表示,那么tensorflow的计算图会非常大,所以tensorflo w提供了 placeholder 机制用于提供输入数据。这样在程序中就不需要生成大量的常量来提供数据,和其他的张量一样,placeholder的类型也是不可以改变的。
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_all = tf.initialize_all_variables()
sess.run(init_all)
print(sess.run(y, feed_dict={x: [[0.7,0.9]]})) #计算前向传播结果时候,需要一个feed_dict来指定x的取值
# 0.7 0.9 是同一列 所以这个数据是一行两列
feed_dict是一个字典(map),在字典中需要给出每一个placeholder的值,如果某个需要的placeholder没有被指定取值,那么程序在运行时将会报错
上面的程序只给出了一个样例前向传播的结果,而在训练神经网络时需要每次提供batch的训练样例; 在上面的样例程序中,如果将输入的12的矩阵改为n2的矩阵,那么就可以得到n个样例的前向传播结果了,如果n = 3 如下:
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=(3,2), name = "input")
a = tf.matmul(x,w1)
y = tf.matmul(a,w2)
sess = tf.Session()
init_all = tf.initialize_all_variables()
sess.run(init_all)
print(sess.run(y, feed_dict={x: [[0.7,0.9], [0.6, 0.4], [0.3, 0.5]]})) #计算前向传播结果时候,需要一个feed_dict来指定x的取值
# 0.7 0.9 是同一 所以这个数据是一行两列
需要注意到的是 feed_dict 永远都是矩阵而不是数组,所以形式永远都是[[ ]]
两层中括号
即使是[0.7, 0.9]
也要表示成 [[0.7, 0.9]]
损失函数
得到一个batch的前向传播之后,需要定义一个损失函数来刻画当前的预测值和真实答案之间的差距。然后通过反向传播算法来调整神经网络参数的取值使得差距可以被缩小,下面展示简单的损失函数:
cross_entropy = -tf.reduce_mean(y_ * tf.clip_by_value(y,1e-10, 1.0))
learning rate = 0.001
train_step = tf.train.AdamOptimizer(learning_rate).minimize(cross_entropy)
tf.clip_by_value:
可以将数值限制在一个范围之内,可以避免类似于log0这样的错误或者大于1的概率
y_:
准确值
y:
预测值
*:
矩阵对应位置直接相乘
tf.matmul:
两个矩阵做矩阵相乘
反向传播优化算法 train_step
常用的优化算法:
- tf.train.GradientDescentOptimizer
- tf.train.AdamOptimizer
- tf.trian.MometumOptimizer
定义了反向传播算法之后,只要通过sess.run(train_step)就可以对所有变量进行优化,使得当前batch下的损失函数更小
*** 完整的网络样例程序***
import tensorflow as tf
# from numpy.random import RandomState
import numpy as np
batch_size = 8
learning_rate = 0.001
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.placeholder(tf.float32, shape=(None, 2), name='input1')
y_ = tf.placeholder(tf.float32,shape=(None, 1), name='input2')
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
y = tf.sigmoid(y)
cross_entropy = -tf.reduce_mean(y_*tf.log(tf.clip_by_value(y, 1e-10, 1.0)))
train_step = tf.train.AdamOptimizer(learning_rate).minimize(cross_entropy)
#通过随机数生成一个模拟数据集
# rdm = RandomState(1)
data_size = 128
X = np.random.rand(data_size, 2)
Y = [[int(x1+x2 < 1)]for (x1, x2) in X] #给label,所有的x1+x2<1的样例被认为是正样本,其余的是负样本
# X = rdm.rand(data_size,2)
with tf.Session() as sess:
init_all = tf.initialize_all_variables()
sess.run(init_all)
print(sess.run(w1))
print(sess.run(w2))
STEPS = 5000
for i in range(STEPS):
start = (i*batch_size) % data_size
end = min(start + batch_size, data_size)
# print(len(Y))
# print(start, end)
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(i,total_cross_entropy)
print(sess.run(w1))
print(sess.run(w2))
训练神经网络可以分为以下步骤:
- 定义神经网络的结构和前向传播的输出结果
- 定义损失函数和选择反向传播优化的算法
- 生成会话并在训练数据上反复运行反向传播优化算法