文章目录[隐藏]
经过了一年的休整,终于博客也要恢复原先坑着的系列了,《简明机器学习教程》也会恢复更新。说实在的,第二篇的原稿我其实在第一篇之后一星期就写出来了,但是后来因为原稿遗失与学业繁忙就一直拖了下来。历经一年,我对机器学习与这系列教程又有了些新的思考,所以我决定做出些许调整。首先,本系列不再单独分理论、实践篇,而是采用交织在一起的形式。其次,将matlab更换为tensorflow(python)。教程的定位依旧是面向初学者,所以会加入大篇幅的前置介绍。这篇就是为了之后内容而对tensorflow进行先行的介绍。
安装(Windows)
安装CUDA和cuDNN
如果有支持CUDA的显卡(仿佛听到农企在哭泣),那建议安装CUDA。这样tensorflow就可以调用GPU而不是CPU进行计算,这会大大提升计算效率。不过并不是所有显卡都可以适用tensorflow,一些算力过差的显卡依旧是不能使用的。这里牙膏厂采用了运算能力(capability)来衡量显卡的算力,在这里可以查看这个表格,tensorflow官方要求capability必须大于3.0。如果不满足这个条件,建议跳过安装CUDA,直接安装cpu版本的tensorflow。当然解决方案也是有的,参见StackOverflow,不过需要自己编译tensorflow。由于当前版本的tensorflow仅支持CUDA9.0,所以只能到官网下载9.0版本。选择版本之后可以下载network或local,这里建议选择local(network老是提示下载失败)。安装成功后,在命令行输入nvcc -V查看安装的版本,若有图示的信息则说明安装成功。
cuDNN可以在此处下载,不过需要注意的是,只有登录才能下载。选择适合CUDA9.0的版本即可。下载后解压至“安装路径\NVIDIA GPU Computing Toolkit\CUDA\v9.0”即可。
安装Anaconda
Anaconda集成了大量有关科学计算的包,而且自带了个非常棒的开发环境。当然,安装tensorflow时,Anaconda并不是必要的,但是还是很推荐安装。在官网就可以下载其安装包,如果无法下载或下载失败,也可以选择清华的镜像。安装完之后,打开Anaconda Navigator就可以看到jupyter notebook了。这里建议顺手装个jupyterlab,虽然用的人不是很多,但是jupyterlab极大的扩充了jupyter notebook的功能。
然后我们来做下准备工作,首先创建一个开发环境,并激活:
conda create -n tensorflow pip python=3.5 activate tensorflow
如果左侧出现“(tensorflow)”字样,则说明已经切换至此环境了。
安装Tensorflow
终于来到重头戏了。对于安装了Anaconda的情况,装了CUDA的话执行:
pip install --ignore-installed --upgrade tensorflow-gpu
注意要在刚刚创建的环境下执行。(左侧应该有”(tensorflow)”)没安装CUDA则需要执行:
pip install --ignore-installed --upgrade tensorflow
对于没安装Anaconda的情形,若安装了CUDA就执行:
pip3 install --upgrade tensorflow-gpu
若没有安装则执行:
pip3 install --upgrade tensorflow
至此为止,我们就完成了tensorflow的安装。
验证安装
打开jupyter notebook或者直接在shell键入python,然后依此输入以下命令:
>>> import tensorflow as tf >>> hello = tf.constant('Hello, TensorFlow!') >>> sess = tf.Session() >>> print(sess.run(hello))
若能成功打印“Hello, TensorFlow!”则说明安装正常。如果报错,请看官网给出的常见问题。
安装(Linux)
这部分先坑着,过段时间没那么懒的时候写。
简介
图
Tensorflow与其说是一个机器学习库,更应该看作其官网宣称的数值计算库。当然,机器学习才是其设计用途。Tensorflow最核心的原理与思想就是数据流图。数据流图是一张图(graph),这里的图指的是图论里的图。一张图通常包含了一些节点和连接这些节点的边,而数据流图就是这样一张图,进一步讲,是一张有向图(directed graph)。每个结点就代表了一个运算操作(operation,比如加减乘除),而数据就在途中定向的“走动”。例如,我们把(1+3)*2转换为数据流图,那么它会长这样:
可以看到,1、3两个数字先“流”向了“Add(加)”这个结点,然后和2“流”向了“Multiply(乘)”这个结点。这样我们应该能更清楚的理解所谓的结点,每个结点都代表了处理若干数据的过程,它可以是函数或若干个步骤的计算。同时,这些结点也会给出一个“结果”。而这些“1”、“2”、“3”的标量,与矢量、矩阵之类的数据都统称为张量(Tensor)。因为这些张量在图中由一个结点“流(flow)”向另一个结点,所以才取名为TensorFlow。下面这张官方给出的动图就很能说明这个性质:
#使用数据流图的优点
张量
在数学中,有很多不同形式的量,比如标量(数量)、矢量(向量)、矩阵等。这些量都具有不同的维数,比如标量是0维的,矢量是1维的,矩阵是2维的。在tensorflow中,这些量都算张量,而维数就是它们的阶(rank,和矩阵的阶不同)。而如矢量、矩阵这类1阶以上的张量,它们还有不同的形状。比如: \begin{bmatrix}1 & 2\\ 3 & 4\\ 5 & 6\end{bmatrix}。这是一个3行2列的矩阵,而(3,2)就是它的形状。通过将所有数据都统一为具一定形状的张量,数据流图才得以个简单的结构。
开始
我们先引入tensorflow。之后的代码中,我们将使用别名tf来指代tensorflow。
import tensorflow as tf
从张量开始
之前已经介绍了张量,那我们就来看看张量在tensorflow中的具体实现。“在编写 TensorFlow 程序时,操控和传递的主要目标是 tf.Tensor。”而tf.Tensor具有数据类型和形状两个类型,我们先来看数据类型。到目前为止,tensorflow支持的数据类型如下表:
#tensorflow的数据类型
然后我们来看看张量的阶:
[table width=”95%” th=”1″]
阶
数学实例
o
标量(只有大小)
1
矢量(大小和方向)
2
矩阵(数据表)
3
3 阶张量(数据立体)
n
n 阶张量(自行想象)
[/table]
由于之前的介绍已经简单的讲解了阶的定义,那我们就直接来看如何创建张量。以下是0-2阶张量的一些创建示例:
# 0阶张量,标量 t_r0 = tf.constant(1, tf.int16) t_s_r0 = tf.constant("Elephant", tf.string) # 1阶张量,矢量 t_r1 = tf.constant([1, 2, 3], tf.int16) # 2阶张量,矩阵 t_r2 = tf.constant([ [1, 2], [3, 4] ], tf.int32)
同时,tensorflow还提供了一些自建函数以供张量创建:
# 2阶张量,2x3矩阵,元素皆为0 t_zero = tf.zeros((2, 3)) # 2阶张量,3x3矩阵,元素为[0,1)间均匀分布 t_rand = tf.random_uniform((3, 3), minval=0, maxval=1)
这些(2, 3)、(3, 3)就是这些张量的形状了。其实,创建张量的方法也远不止这些。事实上,Python原生类型、NumPy数组都可以直接传入tensorflow指令(tensorflow operation,下称“指令”)。并且在传入时也会被自动转化为对应的张量。
数据流图
还记得上面例子中的数据流图吗?本节我们就将学习如何创建这样一个数据流图。我们先来解析下这张数据流图的代码。
a = tf.constant(1, name="a") b = tf.constant(3, name="b") c = tf.constant(2, name="c") op_add = tf.add(a, b, name="Add") op_mul = tf.multiply(op_add, c, name="Multiply")
a、b、c是常量张量,op_add、op_mul是两个算符,对应于图中的Add、Multiply结点。而tf.add与tf.multiply就是构造这两个结点的函数。等等,op_add如果是指令,那为什么能直接传给tf.multiply呢?我们不妨试一试打印type(op_add),结果出人意料的是“<class ‘tensorflow.python.framework.ops.Tensor’>”。没错,op_add和op_mul都是张量!还记得那句话吗?“在编写 TensorFlow 程序时,操控和传递的主要目标是 tf.Tensor。”这些创建结点的函数并不直接返回结点本身,而是返回结点计算结果的张量,以便进行接下来的运算。得益于python支持算符重载的语言特性,我们可以把上面的代码改的更加简便。
op_add = a+b op_mul = op_add*c
当运算不是很复杂时,数据流图的结构在代码中体现的还是很清楚的。但是一旦运算复杂,数据流图的结构就不是很清楚了。复杂的代码结构,重复的结点命名无疑都会给代码维护增加难度。为了让代码结构更加清楚,tensorflow加入了tf.name_scope的设计。name_scope可以“向特定上下文中创建的所有指令添加名称范围前缀。”看官方文档里的示例。
c_0 = tf.constant(0, name="c") # => 指令名为 "c" # 已被使用过的名字将会被"唯一化". c_1 = tf.constant(2, name="c") # => 指令名为 "c_1" # name_scope给在相同上下文里的所有指令添加了前缀 with tf.name_scope("outer"): c_2 = tf.constant(2, name="c") # => 指令名为 "outer/c" # name_scope的嵌套就像分层文件系统的路径 with tf.name_scope("inner"): c_3 = tf.constant(3, name="c") # => 指令名为 "outer/inner/c" # 离开一个name_scope上下文时,指令名将会返回使用上一个前缀 c_4 = tf.constant(4, name="c") # => 指令名为 "outer/c_1" # 已被使用过的name_scope名将会被"唯一化". with tf.name_scope("inner"): c_5 = tf.constant(5, name="c") # => 指令名为 "outer/inner_1/c"
结合python的缩进,这样代码的结构就会清楚不少。而且在数据流图的可视化时,使用name_scope也可以适当的隐藏细节,以使整个计算结构更加清楚(如上文中的GIF)。
不知各位有没有发现,讲了那么久的数据流图,然而我们的代码中似乎都没有出现一个明确的数据流图声明。我们的代码之所以能正确运作,其实是因为tensorflow会自行创建一张默认数据流图,而我们创建的指令都会被自动加入其中。我们可以通过tf.get_default_graph来获取默认图的引用。如果我们需要自己创建数据流图,可以调用其构造函数tf.Graph。以下是新建一个图并在其中加入一个指令的代码。
g = tf.Graph() with g.as_default(): op_g_mul = tf.multiply(3,4)
会话
如果你尝试过打印之前的Tensor,你会发现你并不能获得你想要的运算结果。事实上,tensorflow的底层都是由C++进行编写的,而Python部分只是承担“指示操作”的工作。而会话(Session)就相当于Python(当然也有其他语言)和C++运行时(C++ Runtime)之间的桥梁,所以数据图必须传入会话执行(有时,在编写基于其他API的代码时,例如使用tf.estimator.Estimator时我们并不需要显式的创建会话,事实上这些API本身已经实现了会话的创建和管理)。调用tf.Session我们便可以创建一个会话。
sess = tf.Session()
而调用sess.run函数,我们就可以计算一个Tensor对象的结果了。例如,我们希望计算前文图中的结果,可以如是编写。
result = sess.run(op_mul) print(result)
输出为8。不过当我们运行sess.run(op_g_mul)时却遇到了问题,程序抛出了“ValueError: Tensor Tensor(“Mul:0”, shape=(), dtype=int32) is not an element of this graph”。其实,虽然我们没有直接传入,但是在创建会话的过程中,默认的数据流图已经被隐式传入了。而op_g_mul并不在默认图中,所以就抛出了错误。我们可以在创建会话时显式的指定图来解决这个问题。
sess = tf.Session(graph=g) result = sess.run(op_g_mul) print(result)
输出为12。事实上,会话的构造函数还接受许多其它的参数。不过这些参数大多与我们当前的情形无关,所以暂且不进行介绍,有兴趣的朋友可以查阅官方文档[2]。tf.Session.run方法接受一个参数fetches,此外还有三个可选参数:feed_dict、options、run_metadata。后面两个参数我们目前用不到,所以暂且不提。而前两个参数正好可以与两种特殊的张量结合,那我们就在下一节讲解。
fetches
fetches接受一个或多个tf.Tensor或tf.Operation,并会执行之。之所以能接受多个,是指这些对象可以以列表或是字典值的形式传入,而返回时也会保持这种形式。传入tf.Tensor的例子之前已经有了,不过tf.Operation是什么?之前不是说创建结点的函数会返回张量么,那tf.Operation从何而来?这里我们讲一个重要的函数tf.global_variables_initializer,因为这个tf.Variable有关所以我们来讲讲变量。
变量
根据上一篇的教程我们知道,在学习时有一些量是会随着迭代而被更新的。而这些特殊的,会改变的张量在tensorflow中以tf.Variable的形式存在。创建变量的方法很简单,我们直接调用tf.Variable即可。
v_int = tf.Variable(1, name="var_int") v_mat = tf.Variable(tf.zeros((2,3)), name="var_mat")
可以看到,张量对象也是可以被传入的。但是和之前相同,这些张量对象不会立刻被计算,所以我们就需要一些方法来初始化这些变量。tf.global_variables_initializer函数就是初始化当前图中所有变量的函数,不过它也不会立刻被执行。事实上,tf.global_variables_initializer会返回一个tf.Operation对象,换句话说,初始化变量也是一个指令。所以我们可以通过sess.run(tf.global_variables_initializer())来初始化所有变量。tensorflow还提供了tf.initialize_variables来初始化指定变量,它接受一个变量列表。tf.Variable还接受一个可选参数trainable,如果他为否,那tensorflow自带的训练方法就不能改变它的值,只能由下文提到的方法改变。
有创建自然有修改。tf.Variable.assign就是用于赋值的方法,它接受一个新值。它接受的基本和变量声明时接受的相同,不过值得注意的是,输入张量的形状要和声明时相同。这个方法最重要的还是其返回值,和其他指令一样tf.Variable.assign返回的是一个值为变量修改后值的张量。tf.Variable还提供了assign_add和assign_sub方法以供自增自减,用法类似于tf.Variable.assign。这里给出一段代码,大家可以通过猜测输出来测试下自己对变量的理解是否正确。
v_int = tf.Variable(1, name="var_int") v_int2 = v_int.assign(2) v_int3 = v_int.assign_add(1) sess.run(tf.global_variables_initializer()) print(sess.run(v_int)) print(sess.run(v_int2)) print(sess.run(v_int3)) print(sess.run(v_int3)) print(sess.run(v_int))
#答案
feed_dict与占位符
在训练过程中,我们需要将一些训练样本传入以进行计算。而当模型训练完毕之后,我们也需要传入一些数据以供模型进行预测。很明显,我们需要一个传入数据的方法,而占位符(placeholder)就是为此设计的。我们可以通过tf.placeholder来创建一个占位符。
p_int = tf.placeholder(tf.int32, name="input_int")
tf.placeholder接受一个参数dtype和两个可选参数shape、name。dtype即数据类型,shape指定了占位符的形状,它默认为None,即可接受任意形状的张量。name指定了占位符在图中的名称。
可以看出,占位符的创建中并没有给占位符赋值。而给占位符以数据的方式,是在tf.Session.run的方法调用时传入feed_dict。feed_dict的键是一个张量对象,即创建占位符返回的张量对象,而值就是需要传入的张量。我们可以将上面的图进行一些修改。
a = tf.placeholder(tf.int32, name="a") b = tf.placeholder(tf.int32, name="b") c = tf.placeholder(tf.int32, name="c") op_add = tf.add(a, b, name="Add") op_mul = tf.multiply(op_add, c, name="Multiply") sess = tf.Session() inputs = { a: 1, b: 3, c: 2 } result = sess.run(op_mul, feed_dict=inputs) print(result)
输出为8。当然,我们也可以通过tf.placeholder_with_default函数创造一个带默认值的占位符,它接受两个参数input、shape,一个可选参数name。input即默认值,其他与tf.placeholder相仿。比如,我们可以给上述图中的占位符c以默认值2。
c = tf.placeholder_with_default(2, shape=None, name="c")
那么,当我们的feed_dict中没有给c指定值时,它的值就会是默认值2了。
实践
经过上面的介绍,相信你对tensorflow已经有了一个基本的了解,那我们就以上篇教程中的感知机为例,简单介绍下在tensorflow中如何进行机器学习。
以感知机为例
还记得感知机吗?如果不记得,赶紧去看上一篇。我们先声明预测函数和代价函数。线性函数和判断函数是为了后续编码方便而声明的。
# 线性函数 def linear(x, w, b): return tf.matmul(x, tf.transpose(w)) + b # 判断函数 def judge_func(x, w, b, y): return linear(x, w, b) * y # 预测函数 def predict(x, w, b): return tf.sign(linear(x, w, b)) # 代价函数 def cost_func(x, w, b, y): return -tf.reduce_sum(judge_func(x, w, b, y))
然后我们创建变量w和b,并创建用于传入数据的占位符。
var_w = tf.Variable(tf.ones((1, n)), dtype=tf.float32, name="Weight") var_b = tf.Variable(1, dtype=tf.float32, name="Bias") p_X = tf.placeholder(tf.float32, shape=(None, n), name="input_X") p_y = tf.placeholder(tf.float32, shape=(None, 1), name="input_y")
然后就是训练了。首先声明Session,初始化变量。
init = tf.global_variables_initializer() sess.run(init)
下面来计算代价函数,并使用tf.gradients函数给代价求导(偏导),然后根据求得的梯度(grad_w和grad_b)更新两个参数。
cost = cost_func(p_X, var_w, var_b, p_y) grad_w, grad_b = tf.gradients(cost, [var_w, var_b]) t_w = var_w.assign_sub(alpha * grad_w) t_b = var_b.assign_sub(alpha * grad_b) train_step = [t_w, t_b]
到此为止,我们已经完成了计算图的搭建,那么接下来我们就来编写迭代更新的代码。首先我们需要获得当前所有的误分类点,这里运用了之前的判断函数并结合python的列表解析进行筛选。d_X和d_y代表数据集。
inputs = {p_X: d_X, p_y: d_y} judge = sess.run(judge_func(p_X, var_w, var_b, p_y), feed_dict=inputs) wrong_idx = [i for i in range(m) if judge[i][0]<0] wrong_X = [d_X[i] for i in wrong_idx] wrong_y = [d_y[i] for i in wrong_idx]
如果有误分类点,那我们就随机选择一个误分类点并更新参数。np是Numpy库的别名,当然也可以使用python的random库,即将其改为random.randint(0, len(wrong_idx) – 1)。
# 随机取一个误分类点 idx = np.random.choice(len(wrong_idx)) X = [wrong_X[idx]] y = [wrong_y[idx]] # 更新参数 inputs = {p_X: X, p_y: y} w, b = sess.run(train_step, feed_dict=inputs)
这里需要注意,如果直接传入wrong_X[idx]的话是不行的。因为wrong_X[idx]是列表,被视为一阶的张量,但是占位符p_X却是二阶的。
然后计算当前的损失并打印。
# 计算损失 inputs = {p_X: wrong_X, p_y: wrong_y} loss = sess.run(cost, feed_dict=inputs) # 打印 print("Step %d/%d, w = %s, b = %f, loss = %f." % (i, steps, str(w), b, loss))
这样我们便完成了一次迭代,之后重复进行筛选-更新的操作即可。运行之后打印如下。
Step 0/100, w = [[0.75 0.9 ]], b = 0.950000, loss = 11.350000. Step 1/100, w = [[0.55 0.84999996]], b = 0.900000, loss = 9.299999. Step 2/100, w = [[0.3 0.74999994]], b = 0.850000, loss = 6.650000. Step 3/100, w = [[0.05000001 0.6499999 ]], b = 0.800000, loss = 4.000000. Step 4/100, w = [[-0.19999999 0.5499999 ]], b = 0.750000, loss = 1.350000. Step 5/100, w = [[-0.39999998 0.49999988]], b = 0.700000, loss = -0.700000.
从图中我们也可以看到,这个超平面(此处是直线)确实将数据集分为了两块。
使用Optimizer训练模型
除了自行使用梯度进行训练,tensorflow其实本身就提供了很多算法以供模型训练。在tensorflow里这些算法都以Optimizer的形式存在,而这里我们使用tf.train.GradientDescentOptimizer来进行代价函数的最小化。tf.train.GradientDescentOptimizer接受一个参数learning_rate即学习率,也就是alpha。除此之外,它还接受两个可选参数use_locking和name,后者和其他name一样,前者暂时不进行介绍。那么创建了一个Optimizer之后,我们只要调用其minimize方法(返回一个tf.Operation对象)并传入代价函数的张量就可以顺利的进行训练了。对上面的代码进行微小的调整即可,构建图可以简化为。
cost = cost_func(p_X, var_w, var_b, p_y) train_step = tf.train.GradientDescentOptimizer(alpha).minimize(cost)
同时,参数更新需要更改为。
# 更新参数 inputs = {p_X: wrong_X, p_y: wrong_y} sess.run(train_step, feed_dict=inputs) w, b = sess.run([var_w, var_b])
运行之后打印如下。
Step 0/50, w = [[0.55 0.85]], b = 0.900000, loss = 9.300000. Step 1/50, w = [[0.10000001 0.70000005]], b = 0.800000, loss = 4.600000. Step 2/50, w = [[-0.35 0.5500001]], b = 0.700000, loss = -0.100000. Step 3/50, w = [[-0.6 0.45000008]], b = 0.650000, loss = -1.450000.
从训练结果图中我们可以看出,经过训练的模型可以正确分类。
可视化:Tensorboard
Tensorflow还自带了一个很棒的可视化工具——Tensorboard,那么我们就来看看如何使用这个工具。首先我们要修改我们的代码,以记录一些能供Tensorboard显示的数据。这里我们需要使用tf.summary.FileWriter,我们来创建一个FileWriter。我们需要传入一个目录以存储相应数据,如果需要将图可视化也可以传入相应的图。
writer = tf.summary.FileWriter('./perceptron_logs', sess.graph)
能在Tensorboard中显示的数据的组织形式是summary,它可以记录各种类型的数据。暂且只讲解标量(scalar)和图像(image)的记录。可以通过调用tf.summary.scalar来记录一个标量,它接受标量的名称和一个张量。
tf.summary.scalar('loss', cost)
这个函数会返回一个tf.Operation,而只有我们手动在会话中执行这个指令,标量才会被记录。随着需要记录的数据增多,手动逐个调用是非常繁琐的,所以tensorflow就提供了一个方法tf.summary.merge_all来把所有summry指令合并为一个指令,所以我们暂且不需要记录它的返回值。
记录图的操作会稍微复杂一点。因为记录图像的初衷是为了调试能生成图像的一些模型,所以记录的图像是以张量的形式存储的。而要记录Matplot库绘制的图像,我们首先要将其转换为张量形式。这个函数可以将当前绘制的图像转为张量并返回。
def gen_plot(): buf = io.BytesIO() plt.savefig(buf, format='png') buf.seek(0) image = tf.image.decode_png(buf.getvalue(), channels=4) image = tf.expand_dims(image, 0) return image
我们声明一个新函数以绘制图像,最后调用上述函数返回一个张量。
def draw_image(): """绘制图像""" plt.close('all') # 绘制过程略 return gen_plot()
这样,我们就可以通过调用draw_image函数来生成一张图片,下面我们来记录它。如果要直接记录这张图片,可以直接在最后执行如下代码。
op_summary = tf.summary.image("Train result", draw_image()) summary = sess.run(op_summary) writer.add_summary(summary)
不过如果我们希望记录训练过程中的图像,就不能这么写了。因为这样记录的图像并不会依照迭代次数归类,而是会依单张的形式存在,一旦图像较多,那么Tensorboard内就会非常混乱。所以我们需要用一个变量来存储这张图。
var_img = tf.Variable(tf.ones((1,480,640,4), dtype=tf.uint8), dtype=tf.uint8, name="Plot")
这里的张量形状和图片大小有关,暂不深入讲解。下面,我们需要记录这张图。
tf.summary.image("Train result", var_img)
每次迭代我们都需要生成并更新变量以记录新图片。
sess.run(var_img.assign(draw_image()))
这样我们就能记录一系列图片了。
记录完数据之后,我们在对应文件夹下打开控制台,并键入tensorboard –logdir=perceptron_logs。等待片刻后复制地址就可以在浏览器中打开了。
打开网页之后,就可以点击上方的标签页来查看相关数据了。
左侧的工具栏也可以对页面内容进行调整。可以看到,使用Tensorboard可以大大降低数据可视化的难度。
SL大法:保存检查点
当我们成功训练了一个模型之后,我们可能会希望保存下这个模型中的变量,以供之后预测。除此之外,在训练一个复杂的模型的过程中,定时保存当前的训练结果也是很重要的,这样一旦发生意外,也可以从就近的检查点(checkpoint)进行恢复。在tensorflow中,我们需要通过tf.train.Saver来进行储存。tf.train.Saver的构造方法并不要求传入任何参数,但是接受许多可选参数,感兴趣的朋友可以查看相关文档[6]。创建saver对象之后,我们可以调用tf.train.Saver.save方法来储存一个检查点。它接受sess和save_path两个参数,前者接受一个tf.Session对象,后者接受一个路径以存储文件。它同样具有很多可选参数,不过其中global_step比较实用,如果传入,他就会在文件名后加上当前的步数。比如“filepath”=>“filepath-1”。
saver = tf.train.Saver() saver.save(sess, './percetron')
复原时,只需要调用tf.train.Saver.restore。他只接受sess和save_path两个参数,并且没有可选参数,用法与tf.train.Saver.save相同。不过需要注意的是,tf.train.Saver.restore并不能指定global_step,所以要恢复相应的检查点,只能通过手动的加上“-步数”。
代码汇总
这里是上述代码的汇总。通过更改train为train_with_optimizer,你可以使用tensorflow的Optimizer进行训练。train_with_optimizer没有加入summary的存储,你可以自行加入。Gist点击此处。
#代码汇总
后记
感谢你阅读这篇博客。我写这篇文章的初衷是想以一篇简明扼要的文章快速介绍Tensorflow这个机器学习框架的,但是没想到,随着各种知识的扩充,这篇文章最后都快接近两万字了。其实现在流行的机器学习框架很多,光是支持Python的同类框架就还有MXNet、Theano、PyTorch等等。其中尤其是PyTorch,编写出来的代码短小精悍,而且各方面完全没有比tesorflow差。但是我最后还是选择了tensorflow,原因就是tensorflow更广。诚然,可视化PyTorch有Visdom,各类缺失的函数也有第三方库予以补足,但是tensorflow基本上都以一套很完整的体系囊括进来了。甚至把训练好的模型迁移到手机端APP也非常容易,这样的广度,目前所有能做到的且支持Python的框架,我想很难找到第二个了。当然,调试时tensorflow可能会显得速度较慢,这里推荐一个库miniflow。它在基本兼容tensorflow API的基础上大大提升了运行速度,可以说很适合调试。
本文在匆忙中写就,而且由于作者水平问题,文章难免有很多疏漏。希望各位大神能及时指出,直接评论即可。如有疑问,可以发邮件至admin@kaaass.net,能力范围内的问题我会择日回答。再次感谢你能耐心的读完这么长的博文。
Reference
- 在 Windows 上安装 TensorFlow (https://www.tensorflow.org/install/install_windows)
- 图和会话 (https://www.tensorflow.org/programmers_guide/graphs)
- 张量的阶、形状、数据类型 (http://wiki.jikexueyuan.com/project/tensorflow-zh/resources/dims_types.html)
- Tensor Ranks, Shapes, and Types (https://www.tensorflow.org/versions/r1.1/programmers_guide/dims_types)
- Tensorflow: How to Display Custom Images in Tensorboard (e.g. Matplotlib Plots) (https://stackoverflow.com/questions/38543850/tensorflow-how-to-display-custom-images-in-tensorboard-e-g-matplotlib-plots)
- tf.train.Saver (https://www.tensorflow.org/api_docs/python/tf/train/Saver)
感谢分享!已推荐到《开发者头条》:https://toutiao.io/posts/abli33 欢迎点赞支持!使用开发者头条 App 搜索 69380 即可订阅《KAAAsS Blog》