ML笔记 01:TensorFlow相关基本概念和基于符号的编程范式

最近学习机器学习相关的东西,理论看了一部分后,想跑几个TensorFlow的小例子,在实践中体会一下,发下总是不能够很好的理解TensorFlow的代码。然后各种查资料,看了下TensorFlow的入门知识,才了解到TensorFlow的编程范式是一种典型的基于符号的编程范式,计算过程是延迟计算,典型的惰性求值。

TensorFlow的基本概念

Tensor 张量

Tensor是张量,Flow流动。从功能上看,tensor 张量可以简单理解为多维数据。零阶张量表示标量(scalar),即一个数;一阶张量可以理解为向量(vector),也就是一维数组;n阶张量可以理解为多维数组。Tensor 在 TensorFlow 中并不直接计算,保存的计算过程,可以理解为符号编程延迟计算。

Tensor类型的三个重要属性:名字(name)、维度(shape)和类型(dtype):

  • name,tensor的唯一标志,给出了tenor如何计算出来的, 命名可通过 node:src_output形式给出;
  • shape,tensor的维度信息,如 shape=(2,) 表示一个一维数组,长度为2;
  • type,tensor的唯一类型,如 tf.float32、tf.int64等;

通过 python 的 list 看 tensor,左边多少中括号,就是几维向量,如:

1                               #维度为0, 标量
[1,2,3]                         #维度为1, 一维向量
[[1,2],[3,4]]                   #维度为2, 二维矩阵
[[[1,2],[3,4]],[[1,2],[3,4]]]   #维度为3, 三维空间矩阵

张量的形状以[D0, D1, … Dn-1]的形式表示,D0 到 Dn 是任意的正整数。如形状[3,4]表示第一维有3个元素,第二维有4个元素。举例:

import tensorflow as tf
a = tf.constant([[[1, 2], [3, 4]], [[1, 2], [3, 4]]])
print(a.shape)  # (2, 2, 2)
print(a.name)   # Const:0
print(a.dtype)  # <dtype: 'int32'>

计算图

在TensorFlow程序中,会将定义的计算转化为计算图上的节点。一般系统会自动维护一个默认计算图。计算图是一个有向无环图,可以通过 tf.get_default_graph 函数获取。除了默认的计算图,TensorFlow支持通过 tf.Graph 函数来生成新的计算图。计算图主要作用有:

  • 隔离张量和计算;
  • 提供管理张量和计算的机制;
  • 整理TensorFlow中的资源;

计算图可通过tf.Graph.device函数制定运行计算的设备,比如GPU。最后,还可以使用 TensorBoard 读取日志文件,可视化查看计算图。

tf.placeholder 与 tf.Variable

tf.Variable:主要在于一些可训练变量(trainable variables),比如模型的权重(weights,W)或者偏执值(bias),声明时,必须提供初始值;

tf.placeholder:用于得到传递进来的真实的训练样本,不必指定初始值,可在运行时,通过 Session.run 的函数的 feed_dict 参数指定;

TensorFlow 的惰性求值

以前看Lisp的时候,接触过惰性求值这个概念,当时自己还记了篇笔记:从 Racket 入门函数式编程。惰性求值(Lazy Evaluation),说白了就是某些中间结果不需要被求出来,求出来反而不利于后面的计算也浪费了时间。惰性求值是一个计算机编程中的一个概念,它的目的是要最小化计算机要做的工作。惰性计算的最重要的好处是它可以构造一个无限的数据类型。使用惰性求值的时候,表达式不在它被绑定到变量之后就立即求值,而是在该值被取用的时候求值。语句如 x:=expression; (把一个表达式的结果赋值给一个变量)明显的调用这个表达式并把计算并把结果放置到 x 中,但是先不管实际在 x 中的是什么,直到通过后面的表达式中到 x 的引用而有了对它的值的需求的时候,而后面表达式自身的求值也可以被延迟,最终为了生成让外界看到的某个符号而计算这个快速增长的依赖树。

Tensorflow两个的重要概念就是 Graph 和 Session,Graph定义计算,Session执行计算。TensorFlow计算的单位是OP(operation),它表示了某种抽象计算。OP的输入/输出以 Tensor 的形式存在,在系统初始化时,系统实现对所有OP进行扫描注册。

比如定义一个运算:f(x)= 3*x^2 + 2*x + 1, 使用TensorFlow编程,就是先定义出这个计算式子,然后计算,如下:

import tensorflow as tf

# 定义计算
x = tf.placeholder("float")
cal = 3 * x * x + 2 * x + 1     # 重载了运算符
print(type(cal))    # cal 是 Tensor 的一个实例

# 执行计算
init = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init)
    print(sess.run(cal, {x: 2}))

# 写log, 用TensorBoard查看计算图
writer = tf.summary.FileWriter("test_log", tf.get_default_graph())
writer.close()

# 运行TensorBoard,并将日志的地址指向上面程序输出的地址
# tensorboard --logdir=./test_log
# 然后 localhost 访问即可

TensorFlow 的体系结构

为什么定义的计算需要在 session 里 run?这是TensorFlow的体系结构决定的。TensorFlow 的系统结构以 C API 为界,将整个系统分为前端和后端两个子系统。前端系统扮演了 Client 的角色,完成计算图的构造,通过转发 Protobuf 格式的 GraphDef 给后端系统的 Master,并启动计算图的执行过程。Tensorflow 前端支持 C++,Python,Go,Java,后端使用 C++,CUDA。

  • 前端系统:提供编程模型,负责构造计算图;
  • 后端系统:提供运行时环境,负责执行计算图;
    这里写图片描述

线性回归例子

有了上面的概念,基本就可以看懂这个经典的线性回归的例子了。配合matplot做的图,可以很方便查看最后的结果,代码:

# python 3.6, TensorFlow 1.9.0

import tensorflow as tf
import numpy
import matplotlib.pyplot as plt

rng = numpy.random

# Parameters
learning_rate = 0.01
training_epochs = 2000
display_step = 50

# Training Data
train_X = numpy.asarray(
    [3.3, 4.4, 5.5, 6.71, 6.93, 4.168, 9.779, 6.182, 7.59,
     2.167, 7.042, 10.791, 5.313, 7.997, 5.654, 9.27, 3.1])
train_Y = numpy.asarray(
    [1.7, 2.76, 2.09, 3.19, 1.694, 1.573, 3.366, 2.596, 2.53,
     1.221, 2.827, 3.465, 1.65, 2.904, 2.42, 2.94, 1.3])
n_samples = train_X.shape[0]

# tf Graph Input
X = tf.placeholder("float")
Y = tf.placeholder("float")

# Create Model

# Set model weights
W = tf.Variable(rng.randn(), name="weight")
b = tf.Variable(rng.randn(), name="bias")

# Construct a linear model,define calculate graph
activation = tf.add(tf.multiply(X, W), b)

# Minimize the squared errors
cost = tf.reduce_sum(tf.pow(activation - Y, 2)) / (2 * n_samples)  # L2 loss
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)

# Initializing the variables
init = tf.global_variables_initializer()

# Launch the graph
with tf.Session() as sess:
    sess.run(init)

    # Fit all training data
    for epoch in range(training_epochs):
        for (x, y) in zip(train_X, train_Y):
            sess.run(optimizer, feed_dict={X: x, Y: y})

        # Display logs per epoch step
        if epoch % display_step == 0:
            print("Epoch:", '%04d' % (epoch + 1), "cost=",
                  "{:.9f}".format(sess.run(cost, feed_dict={X: train_X, Y: train_Y})),
                  "W=", sess.run(W), "b=", sess.run(b))

    print("Optimization Finished!")
    print("cost=", sess.run(cost, feed_dict={X: train_X, Y: train_Y}),
          "W=", sess.run(W), "b=", sess.run(b))

    # Graphic display
    plt.plot(train_X, train_Y, 'ro', label='Original data')
    plt.plot(train_X, sess.run(W) * train_X + sess.run(b), label='Fitted line')
    plt.legend()
    plt.show()

上面例子中,还有一个 epoch 概念:

  • batchsize:批大小。在深度学习中,一般采用SGD训练,即每次训练在训练集中取batchsize个样本训练;
  • iteration:1个iteration等于使用batchsize个样本训练一次;
  • epoch:1个epoch等于使用训练集中的全部样本训练一次;

举个例子,训练集有1000个样本,batchsize=10,那么:训练完整个样本集需要:100次iteration,1次epoch。

参考:

猜你喜欢

转载自blog.csdn.net/thisinnocence/article/details/81608954
今日推荐