zoukankan      html  css  js  c++  java
  • 从0开始 TensorFlow

    在此记录TensorFlow(TF)的基本概念、使用方法,以及用一段别人写好的代码展示其应用。

    1  背景知识

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

     

    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 import tensorflow as tf
    2 a = 2
    3 b = 3
    4 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 + bb
    5 # launch the graph in a session
    6 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 value
    5     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 placeholder
    11     # 注意这里 sess.run 中,对于 ddd 的 feed_dict 调用:
    12     print(sess.run(ccc, feed_dict=fb_4_bbb))

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

     

    2.2.4  tf.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 session
     2 with tf.Session() as sess:
     3     # now let's evaluate their value
     4     sess.run(tf.global_variables_initializer()) ## tf.global_variables_initializer 对于 tf.Variable 必须
     5     print("`aGV` is : 
     {}".format(sess.run(aGV)))
     6     print("`bGV` is : 
     {}".format(sess.run(bGV)))
     7     print("`aGV+bGV` is: 
     {}".format(sess.run(cGV)))
     8     
     9     # 用 placeholder 的值进行加合
    10     print("
     Next is Placeholder: 
    ")
    11     # 按 placeholder 的使用方法,采用字典方式进行声明,并在 sess.run中使用 feed_dict进行赋值
    12     fb_4_phrGV = {phrGV: [[2,4,6],[1,3,5]]}
    13     print("`phrGV` is : 
     {}".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 : 
     {}".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 import tensorflow as tf
     2 from numpy.random import RandomState
     3 
     4 batch_size=10
     5 # 生成一个 [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的一个维度上使用None
    11 ### 构造 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 = a
    18 yhat=tf.nn.relu(tf.matmul(a,w2)) ### 第二层计算: a*w2 = x*w1*w2 = y
    19 
    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=516
    30 
    31 ### 定义 输入变量 X 及目标值 Y
    32 #生成两个特征,共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) in X]
    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=11000
    43     for i in range(steps):
    44 
    45         #选定每一个批量读取的首尾位置,确保在1个epoch内采样训练
    46         start = i * batch_size % data_size
    47         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()。

     
  • 相关阅读:
    “猫癣”集团借IE7新漏洞再掀风浪 狼人:
    研究人员在黑帽安全大会演示SSL攻击 狼人:
    猫癣病毒“躲猫猫” 移师广东东莞月入百万 狼人:
    Adobe两款软件存在缺陷 黑客可控制用户PC 狼人:
    安全观点:遭遇数据泄露破坏 损失的不只是金钱 狼人:
    McAfee报告称七成手机制造商认为手机安全至关重要 狼人:
    微软表示本月将发布五个Windows 7更新 狼人:
    Gmail电子邮件曝全球性故障 谷歌向用户道歉 狼人:
    Google Talk被黑客利用 发动钓鱼攻击 狼人:
    谷歌GMail邮件服务出现故障 部分服务已恢复 狼人:
  • 原文地址:https://www.cnblogs.com/ruich/p/9397865.html
Copyright © 2011-2022 走看看