一、计算图
图中的每一个节点都是一个运算,而每一条边代表了计算之间的依赖关系。如果个运算的输入依赖于另一个运算的输出,那么这两个运算有依赖关系。在图中,a和b这两个常量不依赖任何其他计算。而add计算则依赖读取两个常量的取值。于是在图中可以看到有一条从a到add的边和一条从b到add的边。在图中,没有任何计算依赖add的结果,于是代表加法的节点ad没有任何指向其他节点的边。所有 Tensorflow的程序都可以通过类似图所示的计算图的形式来表示,这就是 Tensorflow的基本计算。
二、张量
从 Tensorflow的名字就可以看出张量( tensor)是一个很重要的概念。在 Tensorflow程序中,所有的数据都通过张量的形式来表示。从功能的角度上看,张量可以被简单理解为多维数组。其中零阶张量表示标量(scar),也就是一个数第一阶张量为向量( vector)也就是一个一维数组;第n阶张量可以理解为一个n维数组。但张量在 Tensorflow中的实现并不是直接采用数组的形式,它只是对 Tensorelow中运算结果的引用。在张量中并没有真正保存数字,它保存的是如何得到这些数字的计算过程。还是以向量加法为例
import tensorflow as tf
a=tf.constant([1,2],name="a")
b=tf.constant([2,3],name="b")
c=tf.constant([4,5],name="c")
result=tf.add(a,b,name="addycx")
result1=tf.add(a+b,c,name="add2")
print(result)
print(result1)
需要在tensorflow环境中运行该段代码
结果为:
Tensor("addycx:0", shape=(2,), dtype=int32)
Tensor("add2:0", shape=(2,), dtype=int32)
从上面的代码可以看出 Tensorflow中的张量和 Numpy中的数组不同, Tensorflow计算的结果不是一个具体的数字,而且一个张量的结构。
从上面代码的运行结果可以看出,个张量中主要保存了三个属性:名字(name)、维度( shape)和类型(type)。张量的第一个属性名字不仅是一个张量的唯一标识符,它同样也给出了这个张量是如何计算出来的。Tensorelow的计算都可以通过计算图的模型来建立,而计算图上的每一个节点代表了一个计算,计算的结果就保存在张量之中。所以张量和计算图上节点所代表的计算结果是对应的。这样张量的命名就可以通过“ node: src_ output”的形式来给出。其中node为节点的名称, src output表示当前张量来自节点的第几个输出。比如上面代码打出来的“addycx:0”就说明了 result这个张量是计算节点“addycx”输出的第一个结果(编号从0开始)。张量的第二个属性是张量的维度( shape)。这个属性描述了一个张量的维度信息。如上面样例中 shape=(2,)说明了张量 result是一个一维数组,这个数组的长度为2。第三个参数是类型,每一个张量对应一个类型。
三、会话
会话拥有并管理 Tensorflow程序运行时的所有资源。当所有计算完成之后需要关闭会话来帮助系统回收资源,否则就可能出现资源泄漏的问题。会话的使用过程如下:
import tensorflow as tf
a=tf.constant([1,2],name="a")
b=tf.constant([2,3],name="b")
c=tf.constant([4,5],name="c")
result=tf.add(a,b,name="addycx")
result1=tf.add(a+b,c,name="add2")
print(result)
print(result1)
with tf.Session() as sess:
print(sess.run(result)) #计算取值方法1
print(result1.eval(session=sess)) #计算取值方法2
结果:
Tensor("addycx:0", shape=(2,), dtype=int32)
Tensor("add2:0", shape=(2,), dtype=int32)
[3 5]
[ 7 10]
tensorflow游乐场:http://playground.tensorflow.org
神经网络:前向传播算法
一个神经元有多个输入和一个输出。每个神经元的输入既可以是其他神经元的输出,也可以是整个神经网络的输入。所谓神经网络的结构就是指的不同神经元之间的连接结构。如图所示,一个最简单的神经元结构的输出就是所有输入的加权和,而不同输入的权重就是神经元的参数。神经网络的优化过程就是优化神经元中参数取值的过程。图给出了一个简单的判断零件是否合格的三层全连接神经网络(全连接神经网络是因为相邻两层之间任意两个节点之间都有连接)。
图给出了输入层的取值x1=0.7和x2=0.9。从输入层开始一层一层地使用向前传播算法。首先隐藏层中有3个节点,每一个节点的取值都是输入层取值的加权和。
下面给出了a11取值的详细计算过程
a11=0.7×0.2+0.9×0.3=0.14+0.27=0.41
a12和a13也可以通过类似的方法计算得到,图3-6中也给出了具体的计算公式。在得到第一层节点的取值之后,可以进一步推导得到输出层的取值。类似的,输出层中节点的取值就是第一层的加权和:y=W2a11+W2a12+W2a13=0.41×06+(-0.38)x0.1+0.46x(-02)0.246+(-0.038)+(0.092)=0.116因为这个输出值大于阅值0,所以在这个样例中最后给出的答案又是:这个产品是合格的。这就是前向传播算法。
简单代码实现
单个样例:
import tensorflow as tf
#w1:会产生一个2*3的矩阵,矩阵元素均值为1,标准差为1
# 2*3为例子中输入层到隐藏层的矩阵
w1= tf.Variable(tf.random_normal([2,3],stddev=1,seed=1))
#w2:会产生一个3*1的矩阵,矩阵元素均值为0,标准差为2
#3*1为例子中隐藏层到输出层的矩阵
w2= tf.Variable(tf.random_normal([3,1],stddev=2,seed=0))
#特征向量的常量,代表例子中的零件长度0.7和零件质量0.9
x=tf.constant([[0.7,0.9]])
#前向传播算法获得神经网络的输出
#第一层
a=tf.matmul(x,w1)
#第二层
y=tf.matmul(a,w2)
#打开会话
sess =tf.Session()
#进行初始化
sess.run(w1.initializer)
sess.run(w2.initializer)
#输出
hg=sess.run(y)
print(hg)
if hg>=0:
print("合格")
else:
print("不合格")
sess.close()
w1= tf.Variable(tf.random_normal([2,3],stddev=1,seed=1))这句代码使用了tensorflow自带的两个函数。
结果:
[[7.156026]]
合格
随机数生成函数tf.random_normal()和常数生成函数tf.Variable()
随机数生成函数:
常数生成函数:
-------------2019.7.15更新
多个样例:
import tensorflow as tf
#w1:会产生一个2*3的矩阵,矩阵元素均值为1,标准差为1
# 2*3为例子中输入层到隐藏层的矩阵
w1= tf.Variable(tf.random_normal([2,3],stddev=1,seed=0))
#w2:会产生一个3*1的矩阵,矩阵元素均值为0,标准差为2
#3*1为例子中隐藏层到输出层的矩阵
w2= tf.Variable(tf.random_normal([3,1],stddev=2,seed=0))
#定义placeholder机制作为输入数据的地方,维度为可选项(可以降低出错的概率)
x=tf.placeholder(tf.float32,shape=(3,2),name="input")
#前向传播算法获得神经网络的输出
#第一层
a=tf.matmul(x,w1)
#第二层
y=tf.matmul(a,w2)
#打开会话
sess =tf.Session()
#initialize_all_variables()方法实现所有变量初始化
init_op=tf.initialize_all_variables()
sess.run(init_op)
#输出(feed_dict是一个字典,提供它来指定x的取值)
print(sess.run(y,feed_dict={x:[[0.7,0.9],[0.5,0.6],[0.9,0.2]]}))
#关闭会话
sess.close()
结果:
[[-2.9782143]
[-1.6776594]
[ 6.212757 ]]
完整的神经网络训练程序
import tensorflow as tf
from numpy.random import RandomState
#w1:会产生一个2*3的矩阵,矩阵元素均值为0,标准差为1
# 2*3为例子中输入层到隐藏层的矩阵
w1= tf.Variable(tf.random_normal([2,3],stddev=1,seed=0))
#w2:会产生一个3*1的矩阵,矩阵元素均值为0,标准差为1
#3*1为例子中隐藏层到输出层的矩阵
w2= tf.Variable(tf.random_normal([3,1],stddev=1,seed=0))
#定义训练数据batch的大小
batch_size=8
#定义placeholder机制作为输入数据的地方,维度为可选项(可以降低出错的概率)
#在shape上使用None可以使用不大的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.AdadeltaOptimizer(0.001).minimize(cross_entropy)
#生成一个模拟数据集
rdm=RandomState(1)
dataset_size=128
#生成128行2列的随机数组
X=rdm.rand(dataset_size,2)
#使用链路表达式来定义样本规则
Y=[[int(x1+x2<1)] for (x1,x2) in X]
#打开会话
sess =tf.Session()
#initialize_all_variables()方法实现所有变量初始化
init_op=tf.initialize_all_variables()
sess.run(init_op)
w1b=sess.run(w1)
w2b=sess.run(w2)
#设定训练轮数
step=10000
for i in range(step):
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))
print("训练前W1:%s"%w1b)
print("训练后W1:%s"%(sess.run(w1)))
print("训练前W2:%s"%w2b)
print("训练后W2:%s"%(sess.run(w2)))
#关闭会话
sess.close()
结果:
第0次训练后,它的交叉熵为7.09188
第1000次训练后,它的交叉熵为7.09183
第2000次训练后,它的交叉熵为7.09178
第3000次训练后,它的交叉熵为7.09173
第4000次训练后,它的交叉熵为7.09169
第5000次训练后,它的交叉熵为7.09164
第6000次训练后,它的交叉熵为7.09159
第7000次训练后,它的交叉熵为7.09154
第8000次训练后,它的交叉熵为7.09149
第9000次训练后,它的交叉熵为7.09145
训练前W1:[[-0.3991576 2.1044393 0.17107224]
[ 0.54651815 -2.4234028 0.422554 ]]
训练后W1:[[-0.40063456 2.1060846 0.1725517 ]
[ 0.5455042 -2.422502 0.42362133]]
训练前W2:[[-0.3991576 ]
[ 2.1044393 ]
[ 0.17107224]]
训练后W2:[[-0.4009118 ]
[ 2.1072705 ]
[ 0.17226876]]
通过结果可以看出经过训练后,交叉熵逐渐变小,说明预测结果与真实结果差距变小。并且可以看出w1和w2的取值发生了变化,这就是神经网络训练的结果。
训练神经网络的全部过程,可以分为三步:
1.定义神经网络结构和前向传播的输出结果。
2.定义损失函数以及选择反向传播优化的算法。
3.生成会话并训练。
总的来说,tensorflow的三个基本概念为:计算图、张量、会话,分别代表计算模型、数据模型、运算模型。