Press "Enter" to skip to content

从0开始 TensorFlow

“一个计算图是被组织到图节点上的一系列 TF 计算”。—— TensorFlow Manual

参考文献:

https://jacobbuckman.com/post/tensorflow-the-confusing-parts-1/

http://www.easy-tensorflow.com

https://www.cnblogs.com/weizhen/p/6751792.html

1.1组成要素

  1. 计算图 (Graph)
    1. 通过节点链接构造的有向图来构造出数据流图。
    2. 在节点与链接之间,传递的是多维数组。
    3. 节点与变量
      1. 节点代表各种不同的操作。
      2. 每个分枝则作为底层的变量输入。
  2. 会话 (Session)
    1. 实例化计算图,并运行计算图的方法。

1.2变量构成

TF种的变量类型由如下三种形式构成:

  1. 常量(Constant)定义后维度与数值皆不可变。一般用于存取超参数或其他结构信息。

  2. 变量(Variable)定义后维度不可变而数值可变。一般用于存取权重值或其他相关信息的矩阵。

  3. 占位符(PlaceHolder)不需要初始值,只分配必要的内存。

1.3结构流程

创建图,实施算。

即,先定义“计算图”,再进行图的“计算”。

2方法实现

Anaconda3-5.1.0-Linux-x86_64, Python3.6.4为版本环境。

2.1简单实现

直接定义 a,b 两个节点,c 作为操作符(add):

1 importtensorflow as tf2 a = 23 b = 34 c = tf.add(a, b, name='Add')5 print(c)

结果为:“Tensor("Add:0", shape=(), dtype=int32)”

运行 session 并进行计算:

1 sess =tf.Session()2 print("按c的定义形式确定的tf.add计算结果:{0}".format(sess.run(c)))3 sess.close()

结果为:“按c的定义形式确定的tf.add计算结果:5”

2.2变量定义与实现

2.2.1常量

采用如下的形式进行声明:

    #Create a constant.
c = tf.constant(value, dtype=None, shape=None, name='Const', verify_shape=False)

定义两个常量:aa,bb,并开展 cc=aa+bb 的运算:

1 aa=tf.constant(100)2 bb=tf.constant(200)4 cc = aa +bb5 #launch the graph in a session6 with tf.Session() as sess:7     print(sess.run(cc))

结果为:“300”

2.2.2变量

采用如下的形式进行声明:

    #Create a variable.
w = tf.Variable(<initial-value>, name=<optional-name>)

复制代码

1 #如定义一个 2x3 的服从标准差为 1 正态分布的随机矩阵:2 w1=tf.Variable(tf.random_normal([2,3],stddev=1,seed=1))3 with tf.Session() as sess:4     #now let's evaluate their value5 sess.run(tf.global_variables_initializer())6     ## 注意 tf.global_variables_initializer() 对于 tf.Variable() 的使用是必须的,下同7     print(sess.run(w1))

复制代码

结果为:“[[-0.8113182 1.4845988 0.06532937] [-2.4427042 0.0992484 0.5912243 ]]”

(因是随机数,不同的机器结果或不同)

2.2.3占位符

占位符只占分配内存,并通过 feed_dict 馈送数据:

    # Create a placeHolder.
b = tf.placeholder(<value-type>, shape=<value-shape>, name=<optional-name>)

复制代码

1 #如定义一个常量(aaa)和一个占位符(bbb),并进行相加运算:2 aaa = tf.constant([5, 5, 5], tf.float32)#, name='A') # 如果对名字空间不严格,name 用不用都可以3 bbb = tf.placeholder(tf.float32, shape=[3])#, name='B')4 ccc = tf.add(aaa, bbb)#, name="Add") # 如果对名字空间不严格,name 用不用都可以5 6 with tf.Session() as sess:7     #create a dictionary:8     #注意这里对于 placeHolder 的字典型定义:9     fb_4_bbb = {bbb: [1, 2, 6]}10     #feed it to the placeholder11     #注意这里 sess.run 中,对于 ddd 的 feed_dict 调用:12     print(sess.run(ccc, feed_dict=fb_4_bbb))

复制代码

结果为:“[ 6. 7. 11.]”

2.2.4tf.get_varaible 声明方式

新的声明方式 get_varialble 被用于对变量进行声明及定义。

该方法的声明方式:

    tf.get_variable(name,
shape=None,
dtype=None,
initializer=None,
regularizer=None,
trainable=True,
collections=None,
caching_device=None,
partitioner=None,
validate_shape=True,
use_resource=None,
custom_getter=None,
constraint=None)

以下尝试将“常量”、“变量”、“占位符” 均在一起进行声明及使用:复制代码

1 #使用 get_variable 重复上面的过程:2 3 ## 声明“常量”:4 5 #get_variable() 中,如果没有 shape 参数,则报错:6 #注意,后面的 add 操作需要 aGV 与 bGV 数据类型一致,因此在 constant_init 中 给出为 20.0,且dtype=‘float’7 aGV = tf.get_variable(name="var_1", shape=[1], dtype='float', initializer=tf.constant_initializer(20.0))8 9 #tf.get_variable 的使用在同一个环境下、第二次使用时,将会报错:“Variable xxx already exists, disallowed.”

复制代码

继续声明其他变量:

复制代码

1 ## 声明“变量”:2 #tf.get_variable 的使用在同一个环境下、第二次使用时,将会报错:“Variable xxx already exists, disallowed.”3 bGV = tf.get_variable(name="var_2", shape=[2,3], initializer=tf.random_normal_initializer(mean=0, stddev=1))4 5 ## 声明“占位符”:6 #get_variable 中没有tf.placeholder_initializer 之类的,因此沿用之前的 tf.placeholder()7 phrGV = tf.placeholder(tf.float32, shape=[2,3])8 9 #注意 tf.add 中的各个参数类型(如 int 或 float)必须一致10 cGV = tf.add(aGV, bGV)#, name="Add1")  ## 同样的,name="Add1" 在名字空间不敏感时也可不要

复制代码

声明变量完成后,进行TF运行,即 sess.run():

复制代码

1 #launch the graph in a session2 with tf.Session() as sess:3     #now let's evaluate their value4     sess.run(tf.global_variables_initializer()) ## tf.global_variables_initializer 对于 tf.Variable 必须5     print("`aGV` is : n {}".format(sess.run(aGV)))6     print("`bGV` is : n {}".format(sess.run(bGV)))7     print("`aGV+bGV` is: n {}".format(sess.run(cGV)))8     9     #用 placeholder 的值进行加合10     print("n Next is Placeholder: n")11     #按 placeholder 的使用方法,采用字典方式进行声明,并在 sess.run中使用 feed_dic
t进行赋值12     fb_4_phrGV = {phrGV: [[2,4,6],[1,3,5]]}13     print("`phrGV` is : n {}".format(sess.run(phrGV, feed_dict=fb_4_phrGV)))14     15     #cGV 与 phrGV 相加16     cAddphr = tf.add(cGV, phrGV)#, name="Add2") ## 同样的,name="Add2" 在名字空间不敏感时也可不要17     ## 上句与此句等价 --->>> cAddphr = tf.add(tf.add(aGV, bGV), phrGV)18     19     #注意,上面的 sess.run(xx, feed_dict=yyy) 结束后,placeholder 仍置空20     #因此此句仍须在 sess.run() 中进行 feed,注意此处是作用在 cAddphr 中:21     print("`aGV+bGV+phrGV` is : n {}".format(sess.run(cAddphr, feed_dict=fb_4_phrGV)))

复制代码

结果为:(因部分变量为随机数,因此不同电脑结果或不同)

`aGV` is :  [20.]`bGV` is :  [[-0.26086393  1.0050383  -0.22036408] [-1.6718161   1.1273892   1.616446  ]]`aGV+bGV` is:  [[19.739136 21.00504  19.779636] [18.328184 21.12739  21.616446]] Next is Placeholder: `phrGV` is :  [[2. 4. 6.] [1. 3. 5.]]`aGV+bGV+phrGV` is :  [[21.739136 25.00504  25.779636] [19.328184 24.12739  26.616446]]

3应用案例

参考前述链接的代码如下:

复制代码

1 importtensorflow as tf2 from numpy.random importRandomState3 4 batch_size=105 #生成一个 [2,3] 的正态随机矩阵,第一层权重系数 w1:6 w1=tf.Variable(tf.random_normal([2,3],stddev=1,seed=1))7 #生成一个 [3,1] 的正态随机矩阵,第一层权重系数 w2:8 w2=tf.Variable(tf.random_normal([3,1],stddev=1,seed=1))9 10 #None 可以根据 batch 大小确定维度,在shape的一个维度上使用None11 ### 构造 x, y ,x应是输入,y应是输出12 x=tf.placeholder(tf.float32,shape=[None,2])13 y=tf.placeholder(tf.float32,shape=[None,1])14 15 #激活函数使用 ReLU (tf.nn.relu())16 ### 构造了两层网络  (矩阵乘法使用 tf.matmul)17 a=tf.nn.relu(tf.matmul(x,w1)) ### 第一层计算:x*w1 = a18 yhat=tf.nn.relu(tf.matmul(a,w2)) ### 第二层计算: a*w2 = x*w1*w2 = y19 20 #定义交叉熵为损失函数,训练过程使用Adam算法最小化交叉熵21 ### 使用 tf.clip_by_value 将 yhat 的最大最小值限定在 [1e-10, 1.0] 之间,越过区间的设定为上下限,22 ### 再使用 tf.log 取 log_e 值,之后与 y 相乘,再取平均值,以此作为交叉熵 (cross_entropy)23 cross_entropy=-tf.reduce_mean(y*tf.log(tf.clip_by_value(yhat,1e-10,1.0)))24 #使用 tf.train.AdamOptimizer (选择参数为 0.001) 进行训练,并最小化交叉熵25 ### 注意 tf.train 中 AdamOptimizer 与 minimize 的用法26 train_step=tf.train.AdamOptimizer(0.001).minimize(cross_entropy)27 28 rdm=RandomState(1)29 data_size=51630 31 ### 定义 输入变量 X 及目标值 Y32 #生成两个特征,共data_size个样本33 X=rdm.rand(data_size,2)34 #定义规则给出样本标签,所有x1+x2<1的样本认为是正样本,其他为负样本。35 ### x1+x2 满足 <1 的条件时,Y 为1,即为正样本36 Y = [[int(x1+x2 < 1)] for (x1, x2) inX]37 38 with tf.Session() as sess:39     sess.run(tf.global_variables_initializer()) ### 生成之前使用 tf 定义的所有变量40     print(sess.run(w1))41     print(sess.run(w2))42     steps=1100043     for i inrange(steps):44 45         #选定每一个批量读取的首尾位置,确保在1个epoch内采样训练46         start = i * batch_size %data_size47         end = min(start +batch_size,data_size)48         ### 注意此处 placeholder 的 feed_dict 用法,49         ### 直接使用函数 train_step 中嵌套使用的 cross_entropy 函数中的 x,y 变量的字典定义,50         ### 亦即,即使在几层函数的嵌套使用中,sess.run(xxx, feed_dict = {..}) 依然有效51         sess.run(train_step,feed_dict={x:X[start:end],y:Y[start:end]})52         if i % 1000 ==0:53             training_loss= sess.run(cross_entropy,feed_dict={x:X,y:Y})54             print("在迭代 %d 次后,训练损失为 %g"%(i,training_loss))

复制代码

结果为:(因部分变量为随机数,因此不同电脑结果或不同)

[[-0.8113182   1.4845988   0.06532937] [-2.4427042   0.0992484   0.5912243 ]][[-0.8113182 ] [ 1.4845988 ] [ 0.06532937]]在迭代 0 次后,训练损失为 0.309702在迭代 1000 次后,训练损失为 0.0393322在迭代 2000 次后,训练损失为 0.0173816在迭代 3000 次后,训练损失为 0.0102881在迭代 4000 次后,训练损失为 0.00676341在迭代 5000 次后,训练损失为 0.00446996在迭代 6000 次后,训练损失为 0.00297459在迭代 7000 次后,训练损失为 0.0021837在迭代 8000 次后,训练损失为 0.00179786在迭代 9000 次后,训练损失为 0.00132818在迭代 10000 次后,训练损失为 0.000957028

相关含义在其中进行了具体注释(“#”为原代码注释,“###”为本人添加注释)综合来说,以上代码实际上做了如下几件事情。

总结:

  1. 网络为双层。w1/w2 声明为两层网络的权重,x为输入,维度[Nx2]的2列多行向量,y为输出,维度[Nx1]的1列多行向量。
  2. 网络的计算结构为: y=x.w1.w2, 相应的维度关系是:[N,2]x[2,3]x[3,1] = [N,1] ,因此得到 y 的值。
  3. 程序的整体运行方式是:

    a. 定义各个变量(x,y,w1,w2);

    b. 定义相互的计算关系,即 Net 的结构(a=x.w1, y=a.w2);

    c. 定义优化目标,即 交叉熵:cross_entropy;

    d. 定义优化方法,即 train_step,其中具体实现采用: tf.train.AdamOptimizer.minimize(交叉熵);

    e. 进行数据整理,并运行 TF,即 sess.run()。

Be First to Comment

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注