TensorFlow入门教程

这篇文章是TensorFlow的入门教程。在开始阅读本文之前,请确保你会Python,并且对矩阵有一定的了解,除此之外,最好能懂一点机器学习的知识,不过如果你对机器学习一无所知也没关系,你可以从阅读这篇文章开始学起。

TensorFlow提供了丰富的接口供调用。TensorFlow的内核尽可能开放了最完备的接口,它允许你在此基础上从最底层开始开发。我们建议一般开发者可以不用从这么底层开始开发,这些底层接口更适合科研人员。TensorFlow的上层接口都是在此基础上搭建的。上层接口比底层更容易使用。像tf.contrib.learn这样的高层接口帮助你去管理数据集、估算、训练和推理。请注意,有一些高层的接口的方法名包含contrib,这些是正在开发的接口,它们在接下来的版本当中,有可能会被修改甚至删掉。

这篇文章会先讲一下TensorFloat的基础知识,然后,我们会带着大家一起学习一下如何用tf.contrib.learn实现之前提到的模型。

Tensors

TensorFlow的核心就是tensor,tensor是由一系列任意维度的矩阵构成。

3 # 这是一个维度为0的tensor,这是一个标量,它的大小是[]。
[1. ,2., 3.] # 这是一个维度为1的tensor,这是一个矢量,它的大小是[3]
[[1., 2., 3.], [4., 5., 6.]] # 这是一个维度为2的tensor。这是一个矩阵,它的大小是[2, 3]
[[[1., 2., 3.]], [[7., 8., 9.]]] # 这是一个维度为3的tensor,它的大小是[2, 1, 3]

TensorFlow的内核

导入TensorFlow

TensorFlow的导入方式如下:

import tensorflow as tf

导入了TensorFlow了之后,就可以通过Python来访问TensorFlow的里面的类、方法和符号。后续所有的代码执行前都必须先导入TensorFlow。

算法图

TensorFlow的内核分成两部分:构建算法图和运行算法图。
算法图是由一系列算法作为节点形成的一幅图。让我们一起构建一个简单的算法图。每一个节点都有任意数量的tensor作为输入,一个tensor作为输出。常量是一个特殊的节点。所有的常量都没有输入,它的输出来自内部存储的数据。如果想要创建两个浮点数类型的tensor,比如node1和node2,我们可以这样写:

node1 = tf.constant(3.0, dtype=tf.float32)
node2 = tf.constant(4.0) # 隐式指定了tensor内部数据的类型是tf.float32
print(node1, node2)

最后一行打印出来的结果是:

Tensor("Const:0", shape=(), dtype=float32) Tensor("Const_1:0", shape=(), dtype=float32)

请注意,这里并没有像我们期望的那样打印出3.0和4.0。这是因为node1和node2是节点,只有运算的时候,才会分别生成3.0和4.0。想要对这两个节点进行运算,我们必须启动一个会话(Session)。

下面的代码创建 了一个会话,并且调用了它的run方法来执行这个算法图。求出了node1和node2的值。

sess = tf.Session()
print(sess.run([node1, node2]))

这样我们就可以看到期望的结果:

[3.0, 4.0]

我们还可以构建更加复杂的图。比如,我们可以把刚才的两个节点加起来生成一个新的节点:

node3 = tf.add(node1, node2)
print("node3: ", node3)
print("sess.run(node3): ",sess.run(node3))

最后两行打印的代码打印出来的结果如下:

node3:  Tensor("Add:0", shape=(), dtype=float32)
sess.run(node3):  7.0

TensorFlow提供了一个工具,叫TensorBoard,可以用来查看算法图的结构。这就是刚才的代码对应的算法图。

这张图非常简单,因为我们的算法产生的永远是一个定值。一张图如果想要有外部输入,我们就需要用到占位符(placeholder)。一个占位符表示一定会提供一个输入。

a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
adder_node = a + b  # 这里面的加号和调用tf.add(a, b)等效

上面的三行像是一个函数。定义了两个输入参数a和b。然后把它们加了起来。我们要执行这个算法图,就必须要传参数:

print(sess.run(adder_node, {a: 3, b:4.5}))
print(sess.run(adder_node, {a: [1,3], b: [2, 4]}))

运行的结果是:

7.5
[ 3.  7.]

这张算法图是这样的:

我们还可以把图变得更复杂,比如我们可以再添加一个节点:

add_and_triple = adder_node * 3.
print(sess.run(add_and_triple, {a: 3, b:4.5}))

执行的结果是:

22.5

对应的图是这样的:

在机器学习中,我们希望一个模型可以接受任何参数。为了让模型可以被训练,我们希望可以通过修改图,使得同样的输入会得到新的输出。变量(Variables)允许我们把一个可以训练的参数加入到图中。创建变量的时候,需要指定它们的类型和初始值。

W = tf.Variable([.3], dtype=tf.float32)
b = tf.Variable([-.3], dtype=tf.float32)
x = tf.placeholder(tf.float32)
linear_model = W * x + b

当你调用tf.constant的时候,常量就被初始化了,它的值永远不会改变。但是当你调用tf.Variable的时候,变量并没有被初始化,要初始化变量,你必须显式地执行如下操作:

init = tf.global_variables_initializer()
sess.run(init)

有一点很重要,init是一个引用。它引用的是一个子图。这个子图初始化了所有全局变量。一直到执行sess.run的时候,这些变量才真正被初始化。

因为x是一个占位符,所以我们可以一次性计算醋linear_model的四个值。

print(sess.run(linear_model, {x:[1,2,3,4]}))

运行的结果是:

[ 0.          0.30000001  0.60000002  0.90000004]

我们创建了一个模型,但是我们不知道这个模型好不好。想要对这个模型做一个评估,我们需要y占位符去提供想要的值,然后我们需要写一个损失函数(loss function)。
损失函数表征了当前模型和所提供的数据之间的差别。我们将会用一个标准的损失函数来做线性回归。线性回归就是把所求的值和所提供数据的差的平方加起来。linear_model - y是一个向量,向量的值就是所求的值和提供的数据之间的误差。我们调用tf.square去把这个值求平方。然后我们用tf.reduce_sum把所有的值加起来,得到一个数字,通过这个方法得到一个衡量这个样本错误的数据。

y = tf.placeholder(tf.float32)
squared_deltas = tf.square(linear_model - y)
loss = tf.reduce_sum(squared_deltas)
print(sess.run(loss, {x:[1,2,3,4], y:[0,-1,-2,-3]}))

求出的损耗是:

23.66

我们可以把W和b设置为正确答案-1和1。一个变量可以用tf.Variable来初始化,如果要修改它的值,也可以用tf.assign。比如,W = -1 并且 b = 1 就是我们这个模型最理想的参数:

fixW = tf.assign(W, [-1.])
fixb = tf.assign(b, [1.])
sess.run([fixW, fixb])
print(sess.run(loss, {x:[1,2,3,4], y:[0,-1,-2,-3]}))

当我们这么设置了之后,我们发现损耗就变成了:

0.0

我们这里是事先知道W和b的正确值,而我们现在研究机器学习的目标是要机器自己找到这个合适的参数。接下来,我们就来训练我们的机器找到这个参数。

tf.train

TensorFlow提供了一个优化器,缓慢的改变每个变量,来最小化损耗。最简单的优化器就是gradient descent。这个优化器调整参数的方式是根据变量和损耗之间的导数的大小。简单起见,一般都帮你代劳。

optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)

sess.run(init) # reset values to incorrect defaults.
for i in range(1000):
  sess.run(train, {x:[1,2,3,4], y:[0,-1,-2,-3]})

print(sess.run([W, b]))

最终求得模型的参数是:

[array([-0.9999969], dtype=float32), array([ 0.99999082],
 dtype=float32)]

到此为止,我们让机器完成了一次学习的过程。尽管这仅仅是一个简单的线性回归的问题,根本不需要用TensorFlow大费周章的来实现。TensorFlow为常见的设计模式,数据结构和功能提供了很好的抽象。我们将在下节开始学习怎么使用TensorFlow的这些特性。

完整的代码

一个完整的训练线性回归模型的代码如下:

import numpy as np
import tensorflow as tf

# Model parameters
W = tf.Variable([.3], dtype=tf.float32)
b = tf.Variable([-.3], dtype=tf.float32)
# Model input and output
x = tf.placeholder(tf.float32)
linear_model = W * x + b
y = tf.placeholder(tf.float32)
# loss
loss = tf.reduce_sum(tf.square(linear_model - y)) # sum of the squares
# optimizer
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
# training data
x_train = [1,2,3,4]
y_train = [0,-1,-2,-3]
# training loop
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init) # reset values to wrong
for i in range(1000):
  sess.run(train, {x:x_train, y:y_train})

# evaluate training accuracy
curr_W, curr_b, curr_loss = sess.run([W, b, loss], {x:x_train, y:y_train})
print("W: %s b: %s loss: %s"%(curr_W, curr_b, curr_loss))

运行的结果是:

W: [-0.9999969] b: [ 0.99999082] loss: 5.69997e-11

注意这里的损耗是一个非常接近于0的数字,如果你运行同样的代码,得到的结果不一定和这个一模一样,因为我们是用随机值来训练这个模型的。
最后给出这个算法图的图形:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容