如何理解tensorflow 中国中的tensor

TensorFlow - Tensor理解与使用
如何理解TensorFlow中的tensor
tensor 张量
英 [‘tens?; -s?:]
美 [‘t?ns?]
What is a Tensor?
Tensors are simply mathematical objects that can be used to describe
physical properties, just like scalars and vectors. In fact tensors
are merely a generalisation of a scalar is a zero
rank tensor, and a vector is a first rank tensor.
张量是简单的数学对象,可以用来描述物理性质,就像标量和向量一样。实际上,张量仅仅是标量和向量的一般化;标量是零阶张量,而向量是一阶张量。( rank 与 order通用 )
The rank (or order) of a tensor is defined by the number of directions
(and hence the dimensionality of the array) required to describe it.
For example, properties that require one direction (first rank) can be
fully described by a 3×1 column vector, and properties that require
two directions (second rank tensors), can be described by 9 numbers,
as a 3×3 matrix. As such, in general an nth rank tensor can be
described by 3n coefficients.
一个张量的秩(或阶)是由方向数(以及该数组的维数)来定义的。例如,属性,需要一个方向(第一阶)可以完全描述由一个3×1列向量,属性需要两个方向(二阶张量),可以由9数字,描述为一个3×3的矩阵。因此,一般来说,第n阶张量可以用3的n次方个系数来描述。
以下引用自《TensorFlow:实战Google深度学习框架》
从TensorFlow的名字就可以看出张量(tensor)是一个很重的概念。在TensorFlow程序中所有的数据都通过张量的形式来表示。从功能的角度看,张量可以被理解为多维数组。其中零阶张量表示标量(scalar)也就是一个数;一阶张量为向量,也就是一维数组;n阶张量可以理解为一个n维数组。但张量的实现并不是直接采用数组的形式,它只是对TensorFlow中运算结果的引用。在张量中并没有保存数字,它保存的是如何得到这些数字的计算过程。
向量加法代码说明
import tensorflow as tf
a=tf.constant([1.0,2.0],name='a')
b=tf.constant([2.0,3.0],name='b')
result=tf.add(a,b,name='add') print(result
输出 Tensor(“add:0”, shape=(2,), dtype=float32)
一个张量主要保存三个属性:名字(name)、维度(shape)和类型(type)。
张量的命名就可以通过“node:str_output”的形式来给出。其中node为节点的名称,str_output表示当前张量来自节点的第几个输出。比如上面代码打出来的“add:0”就说明result这个张量是计算节点”add”输出的第一个结果(编号从零开始)。
张量的第二个属性是张量的维度(shape)。这个属性描述了一个张量的维度信息。 张量的第三个属性是类型(type),每个张量会有唯一的类型。
张量的使用分两类。
1 对中间计算结果的引用。当一个计算包含很多计算结果时,使用张量可以很大的提高代码可读性
a=tf.constant([1.0,2.0],name='a')
b=tf.constant([2.0,3.0],name='b')
result=a+b
result=tf.constant([1.0,2.0],name='a')+ tf.constant([2.0,3.0],name='b')
2当计算图构造完成之后,张量可以来获得计算结果,也就是得到真实的数字。虽然张量本身没有存储具体的数字,但可以通过会话session得到这些具体的数字。
C++使用 Tensor的一些示例
class Tensor的源码目录是
\tensorflow\core\framework\tensor.h
python里边的tensor是numpy ndarry,C++里面使用了第三方库Eigen库的封装-class Tensor
eigen\unsupported\Eigen\CXX11\src\Tensor
Eigen库的unsupported部分没有官方支持。
CHECK fails表示在编译时就已经发现了错误
typedef float T;
Tensor T1(DT_FLOAT, TensorShape({}));
Tensor my_mat(DT_FLOAT, TensorShape({ 3, 5 }));
auto mat1 = my_mat.matrix&T&();
auto mat2 = my_mat.tensor&T, 2&();
Tensor my_ten(DT_FLOAT, TensorShape({4, 3, 5}));
auto flat = my_ten.flat&T&();
auto inner = my_ten.flat_inner_dims&T&();
auto outer1 = my_ten.shaped&T, 2&({ 4, 15 });
auto weird = my_ten.shaped&T, 3&({ 6, 5, 2 });
本文已收录于以下专栏:
相关文章推荐
import tensorflow as tf
sequence = [2,-4, 7]
a = tf.abs(sequence)
print(a)这种方式只能打印出 a 的 shape,不能打印出 ...
简单粗暴解释TensorFlow:TensorFlow即是一个实现机器学习算法的接口,同时也是执行机器学习算法的框架!!
举个例子:使用神经网络解决分类问题。
使用神经网络解决分类问题可以分成以下...
tf.train.slice_input_producer(tensor_list, num_epochs=None, shuffle=True, seed=None, capacity=32, sh...
# 2-D tensor `a`
a = tf.constant([1, 2, 3, 4, 5, 6], shape=[2, 3]) => [[1. 2. 3.]
在学习TensorFlow的过程中,我们需要知道某个tensor的值是什么,这个很重要,尤其是在debug的时候。也许你会说,这个很容易啊,直接print就可以了。其实不然,print只能打印输出sh...
1. 从标量到矢量:携带更丰富的信息
矢,是箭的意思,突出的特点是其指向性。袋子里有几个球? 3 个,magnitude(幅度,没有单位);
从这到你家多远?3 km(denominate),3 ...
Tensorflow入门代码(基本框架)# 简化调用库名
import tensorflow as tf
import numpy as np# 模拟生成100对数据对, 对应的函数为...
介绍TensorFlow(TM) 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。TensorFlow 最初由Google大脑小组(隶属于Google机器智能研究机构)的研...
最近致力于深度学习,希望在移动领域能够找出更多的应用点.其中TensorFlow作为目前的一个热点值得我们重点关注....
矩阵操作#对于2-D
#所有的reduce_...,如果不加axis的话,都是对整个矩阵进行运算
tf.reduce_sum(a, 1) #对axis1
tf.reduce_mean(a,0) #每列...
他的最新文章
讲师:李江龙
讲师:司徒正美
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)后使用快捷导航没有帐号?
入门级解读:小白也能看懂的TensorFlow介绍
查看: 16493|
评论: 0|原作者: Soon Hin Khor|来自: 机器之心
摘要: 任何预测都依赖于多个特征,于是我们从单特征的线性回归进阶到 带有两个特征的线性回归;之所以选择两个特征,是为了让可视化和理解简明些,但这个思想可以推广到带有任何数量特征的线性回归。我们引进一个新的特征 ...
矩阵和多特征线性回归快速回顾之前文章的前提是:给定特征——任何房屋面积(sqm),我们需要预测结果,也就是对应房价($)。为了做到这一点,我们:我们找到一条「最拟合」所有数据点的直线(线性回归)。「最拟合」是当线性回归线确保实际数据点(灰色点)和预测值(内插在直线上的灰色点)之间的差异最小,即最小化多个蓝线之和。使用这条直线,我们可以预测任何房屋的价格。使用单一特征线性回归进行预测多特征线性回归概述实际上,任何预测都依赖于多个特征,于是我们从单特征的线性回归进阶到 带有两个特征的线性回归;之所以选择两个特征,是为了让可视化和理解简明些,但这个思想可以推广到带有任何数量特征的线性回归。我们引进一个新的特征——房间数量。当收集数据点时,现在我们需要在现有特征「房屋面积」之上收集新特征「房间数」的值,以及相应的结果「房屋价格」。我们的图表变成了 3 维的。结果「房屋价格」以及 2 个特征(「房间数」,「房屋面积」)的数据点空间然后,我们的目标变成:给定「房间数」和「房屋面积」,预测「房屋价格」(见下图)。由于缺少数据点,有时无法对给定的 2 个特征进行预测在单一特征的情形中,当没有数据点时,我们需要使用线性回归来创建一条直线,以帮助我们预测结果房屋价格。在 2 个特征的情形中,我们也可以使用线性回归,但是需要创建一个平面(而不是直线),以帮助我们预测(见下图)。使用线性回归在 2 个特征空间中的创建一个平面来做预测多特征线性回归模型回忆单一特征的线性回归(见下图左边),线性回归模型结果为 y,权重为 W,房屋大面积为 x,偏差为 b。对于 2 个特征的回归(参见下图右侧),我们引入另一个权重 W2,另一个自变量 x2 来代表房间数的特征值。单特征 vs. 2 个特征的线性回归方程如之前讨论的那样,当我们执行线性回归时,梯度下降能帮助学习系数 W、W2 和 b 的值。Tensorflow 的多特征线性回归1.快速回顾单特征线性回归的 TF 代码由 3 部分组成(见下图):构建模型(蓝色部分)基于模型构建成本函数(红色部分)使用梯度下降(绿色部分)最小化成本函数用于单特征线性回归的 Tensorflow 代码2.Tensorflow 的 2 个特征的线性回归TF 代码中 2 个特征的线性回归方程(如上所述)的变化(相比单特征)用红色显示。注意,增加新特征的这种方式效率低;随着特征数量的增长,所需的变量系数和自变量的数量会增加。实际的模型有更多的特征,这恶化了这个问题。那么,如何能有效地表示特征呢?解决方法:矩阵首先,让我们将表征两个特征的模型推广到表征 n 个特征的模型:复杂的 n 特征公式可以用矩阵简化,矩阵被内置于 TF 中,这是因为:数据可以用多维表示,这契合我们表征具有 n 个特征的数据点(左下方,也称为特征矩阵)以及具有 n 个权重模型(右下,也称为权重矩阵)的方式单个数据点的 n 个特征与模型的矩阵形式的 n 个权重在 TF 中,它们将被写为:x = tf.placeholder(tf.float,[1,n])W = tf.Variable(tf.zeros [n,1])注意:对于 W,我们使用 tf.zeros,它将所有 W1,W2,...,Wn 初始化为零。在数学上,矩阵乘法是向量乘法的加总;因此自然地,特征(中间的一个)和权重(右边的)矩阵之间的矩阵乘法给出(左边的)结果,即等于 n 个特征的线性回归公式的第一部分(如上所述),没有截距项。特征和权重矩阵之间的矩阵乘法给出结果(未添加截距项)在 TF 中,这种乘法将表示为:y = tf.matmul(x, W)多行特征矩阵(每行表示数据点的 n 个特征)之间的矩阵乘法返回多行结果,每行代表每个数据点的结果/预测(没有加入截距项);因此一个矩阵乘法就可以将线性回归公式应用于多个数据点,并对应地产生多个预测(每个数据点对应一个结果)(见下文)注意:特征矩阵中的 x 表示变的更复杂,即我们使用 x1.1、x1.2,而不是 x1、x2 等,因为特征矩阵(中间矩阵)从表示 n 个特征(1 行 x,n 列)的单个数据点扩展到表示具有 n 个特征(m 行 x,n 列)的 m 个数据点。因此,我们扩展 x &n&(如 x1)到 x &m &.&n&(如 x1.1),其中,n 是特征数,m 是数据点的数量。具有模型权重的多行矩阵乘法产生矩阵的多个行结果在 TF 中,它们将被写为:x = tf.placeholder(tf.float,[m,n])W = tf.Variable(tf.zeros [n,1])y = tf.matmul(x,W)最后,向结果矩阵添加常数,也就是将常数添加到矩阵中的每一行在 TF 中,用矩阵表示 x 和 W,无论模型的特征数量或要处理的数据点数量,矩阵都可以简化为:b = tf.Variable(tf.zeros[1])y = tf.matmul(x, W) + bTensorflow 的多特征备忘单我们做一个从单一特征到多特征的线性回归的变化的并行比较:Tensorflow 中的单特征与 n 个特征的线性回归模型总结在本文中,我们介绍了多特征线性回归的概念,并展示了我们如何将模型和 TF 代码从单特征的线性回归模型扩展到 2 个特征的线性回归模型,并可以推广到 n 特征线性回归模型。最后我们为多特征的 TF 线性回归模型提供了一张备忘单。逻辑回归逻辑回归综述我们已经学会了如何使用 Tensorflow(TF)去实现线性回归以预测标量值得结果,例如给定一组特征,如住房大小,预测房价。然而,有时我们需要对事物分类(classify)而不是去预测一个具体的数值,例如给定一张含有数字(0-9 十个数字中的一个)的图片,我们需要将其分类为 0,1,2,3,4,5,6,7,8,9 十类。或者,我们需要将一首歌曲进行归类,如归类为流行,摇滚,说唱等。集合 [0,1,2,...,9]、[流行,摇滚,说唱,等等] 中的每一个元素都可以表示一个类。在计算机中,我们通常用数字对抽象名词进行表示,比如,pop = 0, rock = 1, 等等。为了实现分类,我们使用 TF 来实现逻辑回归。在本文中,我们将使用逻辑回归将数字图片归类为 0,1,2,3,4,5,6,7,8,9 这十类。逻辑回归的细节线性回归中的许多概念仍然用于逻辑回归之中。我们可以再次使用公式 y = W.x + b,但是有一些不同的地方。让我们看看线性回归和逻辑回归的公式:线性回归与逻辑回归的区别与相似区别:结果(y):对于线性回归,结果是一个标量值(可以是任意一个符合实际的数值),例如 5 等;对于逻辑回归,结果是一个整数(表示不同类的整数,是离散的),例如 0,1,2,... 9。特征(x):对于线性回归,特征都表示为一个列向量;对于涉及二维图像的逻辑回归,特征是一个二维矩阵,矩阵的每个元素表示图像的像素值,每个像素值是属于 0 到 255 之间的整数,其中 0 表示黑色,255 表示白色,其他值表示具有某些灰度阴影。成本函数(成本):对于线性回归,成本函数是表示每个预测值与其预期结果之间的聚合差异的某些函数;对于逻辑回归,是计算每次预测的正确或错误的某些函数。相似性:训练:线性回归和逻辑回归的训练目标都是去学习权重(W)和偏置(b)值。结果:线性回归与逻辑回归的目标都是利用学习到的权重和偏置值去预测/分类结果。协调逻辑回归与线性回归为了使逻辑回归利用 y = W.b + x,我们需要做出一些改变以协调上述差异。1.特征变换,x我们可以将二维的图片特征(假设二维特征有 X 行,Y 列)转换成一维的行向量:将第一行以外的其它行数值依顺序放在第一行后面。转换图像特征以适用于逻辑回归公式2.预测结果转换,y对于逻辑回归,y 不能作为标量,因为预测可能最终为 2.3 或 11,这不在可能的类 [0,1,...,9] 中。为了解决这个问题,y 应该被转换成列向量,该向量的每个元素代表逻辑回归模型认为属于某个特定类的得分。在下面的示例中,预测结果为类'1',因为它具有较高得分。每个类的分数和具有较高分数的类成为被预测的类对于给定的图片,为求这个分数向量,每个像素都会贡献一组分数(针对每一类),分数表示系统认为这张图片属于某类的可能性,每个像素分数之和成为预测向量。每个像素提供一个分数向量;每个类别有一个分数,最后变成预测向量。所有预测向量的总和变成最终预测。3.成本函数的变换涉及到预测结果和实际结果之间数值距离的任何函数都不能作为成本函数。对于数字图片「1」,这样的成本函数将使预测值「7」(7-1=6)更严重地惩罚预测值「2」(2-1=1),尽管两个预测结果都是错误的。我们即将使用的成本函数,交叉熵(H),用以下几个步骤实现:1. 将实际图片的类向量(y')转化成 one-hot 向量,这是一个概率分布。2. 将预测类 (y) 转化成概率分布。3. 使用交叉熵函数去计算成本函数,这表示的是两个概率分布函数之间的差异。第一步:One-hot 向量由于我们已经将预测 (y) 转换成分数向量,因此,我们也应该将实际图片类(y』)转换成相同维数的向量;one-hot 向量是将对应于实际类的的元素为设为 1,其它元素为 0。下面,我们展示表示 0-9 十个类中一个类的 one-hot 向量。图片类和它们的 one-hot 向量表示假设实际图像上是数字「1」(y'),它的 one-hot 向量是 [0,1,0,0,0,0,0,0,0,0],假设其预测向量 (y) [1.3, 33, 2, 1.2, 3.2, 0.5, 3, 9.2, 1],绘制比较如下:真实图片 one—hot 向量(顶)预测类别概率第二步:用 softmax 实现概率分布为了在数学上比较这两个「图」的相似性,交叉熵是一个好方法。(这里是一个很棒但比较长的解释,如果你对细节感兴趣的话。https://colah.github.io/posts/2015-09-Visual-Information/)然而,为了利用交叉熵,我们需要将实际结果向量(y')和预测结果向量(y)转换为「概率分布」,「概率分布」意味着:每个类的概率/分数值在 0-1 之间;所以类的概率/分数和必须是 1;实际结果向量(y')如果是 one-hot 向量,满足了上述限制。为预测结果向量(y), 使用 softmax 将其转换为概率分布:softmax 函数,这里 i 是表示 0, 1, 2, …, 9 十类这个过程只需要简单的两步,预测向量(y)中的每个分量是 exp(y_i) 除以所有分量的 exp() 的和。注意:softmax(y)图形在形状上与 prediction (y) 相似,但是仅仅有较大的较大值和较小的最小值使用 softmax 前后预测(y)曲线第三步:交叉熵现在,我们将预测向量分数概率分布(y')和实际向量分数概率分布 (y) 运用交叉熵。交叉熵公式:交叉熵作为我们想最小化的成本函数为了快速理解这个复杂的公式,我们将其分为 3 部分(见下文)。注意,本文中的符号,我们使用 y_i 表示 y 的第 i 个分量。交叉熵(H)公式可视为三个部分:红,蓝,绿蓝:实际图像类(y')对应的 one-hot 图,参看 one-hot 向量部分:红:由预测向量元素(y)经过softmax(y),-og(softmax(y)一系列变化而来:绿:每一图片类别 i,其中,i = 0, 1, 2, …, 9, 红蓝部分相乘的结果以下图例会进一步简化理解。蓝色制图只是真实图片类别(y')one-hot 向量。每个预测向量元素,y,转换成 -log(softmax(y),就得到红图:预测类别向量(y)一系列转换后,得到红图如果你想完全地理解第二个变换 -log(softmax(y)) 与 softmax(y) 为什么成反比,请点击 video or slides(参见文末资源部分).交叉熵(H),这个绿色的部分是每个类别的蓝色值和红色值的乘积和,然后将它们做如下相加:交叉熵是每个图像类的蓝色值和红色值的乘积之和。由于这张蓝色图片对应一个 one-hot 向量,one-hot 向量仅仅有一个元素是 1,它对应一个正确的图片类,交叉熵的其它所有元素乘积为 0,交叉熵简化为:将所有部分放到一起有了三个转换后,现在,我们就可以将用于线性回归的技术用于逻辑回归。下面的代码片段展示的是本系列文章第三部分线性回归代码和代码适用逻辑回归所需要的变化之间的对比。逻辑回归的目标是最小化交叉熵(H),这意味着我们只需要最小化 -log(softmax(y_i)项;因为该项与 softmax(y_i)成反比,所以我们实际上是较大化该项。使用反向传播去最小化交叉熵 (H ) 将改变逻辑回归的权重 W 和偏置 b。因此,每张图片的像素值将会给出对应图片类较高分数/概率!(较高分数/概率对应于正确的图片类)将线性回归方法用于逻辑回归之中,「total_class」是欲分类问题的总类数量,例如,在上文手写数字体识别例子中,total_class=10。1. 将特征变换成一维特征;2. 将预测结果向量、实际结果向量变化成 one-hot 向量;3. 将成本函数从平方误差函数变化到交叉熵。总结线性回归对基于给定特征的预测(数值)是有帮助的,逻辑回归根据输入特征实现分类是有帮助的。我们展示了如何调整线性回归 y = W.x + b 实现逻辑回归:(1)转换特征向量;2)转换预测/结果向量;(3)转换成本函数。当你掌握了 one-hot 向量,softmax,交叉熵的知识,你就可以处理谷歌上针对「初学者」的图片分类问题。资源:针对初学者的图像识别的谷歌代码:/tensorflow/tensorflow/blob/master/tensorflow/examples/tutorials/mnist/mnist_softmax.pyslideshare 上的幻灯片:http://www.slideshare.net/KhorSoonHin/gentlest-introduction-to-tensorflow-part-3油管上的视频:/watch?v=F8g_6TXKlxw原文链接:/all-of-us-are-belong-to-machines/gentlest-intro-to-tensorflow-4-logistic-regression-2afd0cabc54#.glculhxzi欢迎加入本站公开兴趣群商业智能与数据分析群兴趣范围包括各种让数据产生价值的办法,实际应用案例分享与讨论,分析工具,ETL工具,数据仓库,数据挖掘工具,报表系统等全方位知识QQ群:
上一篇:下一篇:解读tensorflow之rnn
我的图书馆
解读tensorflow之rnn
from: http://lan2720.github.io//%E8%A7%A3%E8%AF%BBtensorflow%E4%B9%8Brnn/
这两天想搞清楚用tensorflow来实现rnn/lstm如何做,但是google了半天,发现tf在rnn方面的实现代码或者教程都太少了,仅有的几个教程讲的又过于简单。没办法,只能亲自动手一步步研究官方给出的代码了。
本文研究的代码主体来自官方源码。但是,如果你直接运行这个代码,可以看到warning:
WARNING:tensorflow:: Using a concatenated state is slower and will soon be deprecated. Use state_is_tuple=True.
于是根据这个warning,找到了一个相关的issue:
回答中有人给出了对应的,加入了state_is_tuple=True,笔者就是基于这段代码学习的。
tf的代码看多了之后就知道其实官方代码的这个结构并不好:
graph的构建和训练部分放在了一个文件中,至少也应该分开成model.py和train.py两个文件,model.py中只有一个PTBModel类graph的构建部分全部放在了PTBModel类的constructor中
恰好看到了一篇专门讲如何构建tensorflow模型代码的,值得学习,来重构自己的代码吧。
值得学习的地方
虽说官方给出的代码结构上有点小缺陷,但是毕竟都是大神们写出来的,值得我们学习的地方很多,来总结一下:
(1) 设置is_training这个标志
这个很有必要,因为training阶段和valid/test阶段参数设置上会有小小的区别,比如test时不进行dropout
(2) 将必要的各类参数都写在config类中独立管理
这个的好处就是各类参数的配置工作和model类解耦了,不需要将大量的参数设置写在model中,那样可读性不仅差,还不容易看清究竟设置了哪些超参数
placeholder
两个,分别命名为self._input_data和self._target,只是注意一下,由于我们现在要训练的模型是language model,也就是给一个word,预测最有可能的下一个word,因此可以看出来,input和output是同型的。并且,placeholder只存储一个batch的data,input接收的是个word在vocabulary中对应的index【后续会将index转成dense embedding】,每次接收一个seq长度的words,那么,input shape=[batch_size,
num_steps]
在很多用到rnn的paper中我们会看到类似的图:
这其中的每个小长方形就表示一个cell。每个cell中又是一个略复杂的结构,如下图:
图中的context就是一个cell结构,可以看到它接受的输入有input(t),context(t-1),然后输出output(t),比如像我们这个任务中,用到多层堆叠的rnn cell的话,也就是当前层的cell的output还要作为下一层cell的输入,因此可推出每个cell的输入和输出的shape是一样。如果输入的shape=(None, n),加上context(t-1)同时作为输入部分,因此可以知道W
的shape=(2n, n)。
说了这么多,其实我只是想表达一个重点,就是
别小看那一个小小的cell,它并不是只有1个neuron unit,而是n个hidden units
因此,我们注意到tensorflow中定义一个cell(BasicRNNCell/BasicLSTMCell/GRUCell/RNNCell/LSTMCell)结构的时候需要提供的一个参数就是hidden_units_size。
弄明白这个之后,再看tensorflow中定义cell的代码就无比简单了:
lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(size, forget_bias=0.0, state_is_tuple=True)
if is_training and config.keep_prob & 1:
lstm_cell = tf.nn.rnn_cell.DropoutWrapper(
lstm_cell, output_keep_prob=config.keep_prob)
cell = tf.nn.rnn_cell.MultiRNNCell([lstm_cell] * config.num_layers, state_is_tuple=True)
首先,定义一个最小的cell单元,也就是小长方形,BasicLSTMCell。
问题1:为什么是BasicLSTMCell
你肯定会问,这个类和LSTMCell有什么区别呢?good question,文档给出的解释是这样的:
划一下重点就是倒数第二句话,意思是说这个类没有实现clipping,projection layer,peep-hole等一些lstm的高级变种,仅作为一个基本的basicline结构存在,如果要使用这些高级variant要用LSTMCell这个类。
因为我们现在只是想搭建一个基本的lstm-language model模型,能够训练出一定的结果就行了,因此现阶段BasicLSTMCell够用。这就是为什么这里用的是BasicLSTMCell这个类而不是别的什么。
问题2:state_is_tuple=True是什么
(此图偷自)
可以看到,每个lstm cell在t时刻都会产生两个内部状态ct
,都是在t-1时刻计算要用到的。这两个状态在tensorflow中都要记录,记住这个就好理解了。
来看官方对这个的解释:
意思是说,如果state_is_tuple=True,那么上面我们讲到的状态ct
就是分开记录,放在一个tuple中,如果这个参数没有设定或设置成False,两个状态就按列连接起来,成为[batch, 2n](n是hidden units个数)返回。官方说这种形式马上就要被deprecated了,所有我们在使用LSTM的时候要加上state_is_tuple=True
问题3:forget_bias是什么
暂时还没管这个参数的含义
DropoutWrapper
dropout是一种非常efficient的regularization方法,在rnn中如何使用dropout和cnn不同,推荐大家去把看一遍。我在这里仅讲结论,
对于rnn的部分不进行dropout,也就是说从t-1时候的状态传递到t时刻进行计算时,这个中间不进行memory的dropout;仅在同一个t时刻中,多层cell之间传递信息的时候进行dropout
上图中,xt?2
时刻的输入首先传入第一层cell,这个过程有dropout,但是从t?2时刻的第一层cell传到t?1,t,t+1的第一层cell这个中间都不进行dropout。再从t+1
时候的第一层cell向同一时刻内后续的cell传递时,这之间又有dropout了。
因此,我们在代码中定义完cell之后,在cell外部包裹上dropout,这个类叫DropoutWrapper,这样我们的cell就有了dropout功能!
可以从官方文档中看到,它有input_keep_prob和output_keep_prob,也就是说裹上这个DropoutWrapper之后,如果我希望是input传入这个cell时dropout掉一部分input信息的话,就设置input_keep_prob,那么传入到cell的就是部分input;如果我希望这个cell的output只部分作为下一层cell的input的话,就定义output_keep_prob。不要太方便。
根据Zaremba在paper中的描述,这里应该给cell设置output_keep_prob。
if is_training and config.keep_prob & 1:
lstm_cell = tf.nn.rnn_cell.DropoutWrapper(
lstm_cell, output_keep_prob=config.keep_prob)
Stack MultiCell
现在我们定义了一个lstm cell,这个cell仅是整个图中的一个小长方形,我们希望整个网络能更deep的话,应该stack多个这样的lstm cell,tensorflow给我们提供了MultiRNNCell(注意:multi只有这一个类,并没有MultiLSTMCell之类的),因此堆叠多层只生成这个类即可。
cell = tf.nn.rnn_cell.MultiRNNCell([lstm_cell] * config.num_layers, state_is_tuple=True)
我们还是看看官方文档,
我们可以从描述中看出,tensorflow并不是简单的堆叠了多个single cell,而是将这些cell stack之后当成了一个完整的独立的cell,每个小cell的中间状态还是保存下来了,按n_tuple存储,但是输出output只用最后那个cell的输出。
这样,我们就定义好了每个t时刻的整体cell,接下来只要每个时刻传入不同的输入,再在时间上展开,就能得到上图多个时间上unroll graph。
initial states
接下来就需要给我们的multi lstm cell进行状态初始化。怎么做呢?Zaremba已经告诉我们了
We initialize the hidden states to zero. We then use the
final hidden states of the current minibatch as the initial hidden state of the subsequent minibatch
(successive minibatches sequentially traverse the training set).
也就是初始时全部赋值为0状态。
那么就需要有一个self._initial_state来保存我们生成的全0状态,最后直接调用MultiRNNCell的zero_state()方法即可。
self._initial_state = cell.zero_state(batch_size, tf.float32)
注意:这里传入的是batch_size,我一开始没看懂为什么,那就看文档的解释吧!
state_size是我们在定义MultiRNNCell的时就设置好了的,只是我们的输入input shape=[batch_size, num_steps],我们刚刚定义好的cell会依次接收num_steps个输入然后产生最后的state(n-tuple,n表示堆叠的层数)但是一个batch内有batch_size这样的seq,因此就需要[batch_size,s]来存储整个batch每个seq的状态。
embedding input
我们预处理了数据之后得到的是一个二维array,每个位置的元素表示这个word在vocabulary中的index。
但是传入graph的数据不能讲word用index来表示,这样词和词之间的关系就没法刻画了。我们需要将word用dense vector表示,这也就是广为人知的word embedding。
paper中并没有使用预训练的word embedding,所有的embedding都是随机初始化,然后在训练过程中不断更新embedding矩阵的值。
with tf.device("/cpu:0"):
embedding = tf.get_variable("embedding", [vocab_size, size])
inputs = tf.nn.embedding_lookup(embedding, self._input_data)
首先要明确几点:
既然我们要在训练过程中不断更新embedding矩阵,那么embedding必须是tf.Variable并且trainable=True(default)目前tensorflow对于lookup embedding的操作只能再cpu上进行embedding矩阵的大小是多少:每个word都需要有对应的embedding vector,总共就是vocab_size那么多个embedding,每个word embed成多少维的vector呢?因为我们input embedding后的结果就直接输入给了第一层cell,刚才我们知道cell的hidden units size,因此这个embedding dim要和hidden units size对应上(这也才能和内部的各种门的W和b完美相乘)。因此,我们就确定下来embedding matrix
shape=[vocab_size, hidden_units_size]
最后生成真正的inputs节点,也就是从embedding_lookup之后得到的结果,这个tensor的shape=
input data dropout
刚才我们定义了每个cell的输出要wrap一个dropout,但是根据paper中讲到的,
We can see that the information is corrupted by the dropout operator exactly L + 1 times
We use the activations
to predict yt
is the number of layers
in our deep LSTM.
cell的层数一共定义了L层,为什么dropout要进行L+1次呢?就是因为输入这个地方要进行1次dropout。比如,我们设置cell的hidden units size=200的话,input embbeding dim=200维度较高,dropout一部分,防止overfitting。
if is_training and config.keep_prob & 1:
inputs = tf.nn.dropout(inputs, config.keep_prob)
和上面的DropoutWrapper一样,都是在is_training and config.keep_prob & 1的条件下才进行dropout。
由于这个仅对tensor进行dropout(而非rnn_cell进行wrap),因此调用的是tf.nn.dropout。
RNN循环起来!
到上面这一步,我们的基本单元multi cell和inputs算是全部准备好啦,接下来就是在time上进行recurrent,得到num_steps每一时刻的output和states。
那么很自然的我们可以猜测output的shape=[batch_size, num_steps, size],states的shape=[batch_size, n(LSTMStateTuple)]【state是整个seq输入完之后得到的每层的state
outputs = []
state = self._initial_state
with tf.variable_scope("RNN"):
for time_step in range(num_steps):
if time_step & 0: tf.get_variable_scope().reuse_variables()
(cell_output, state) = cell(inputs[:, time_step, :], state)
outputs.append(cell_output)
以上这是官方给出的代码,个人觉得不是太好。怎么办,查文档。
可以看到,有四个函数可以用来构建rnn,我们一个个的讲。
(1) dynamic rnn
这个方法给rnn()很类似,只是它的inputs不是list of tensors,而是一整个tensor,num_steps是inputs的一个维度。这个方法的输出是一个pair,
由于我们preprocessing之后得到的input shape=[batch_size, num_steps, size]因此,time_major=False。
最后的到的这个pair的shape正如我们猜测的输出是一样的。
sequence_length: (optional) An int32/int64 vector sized [batch_size].表示的是batch中每行sequence的长度。
调用方法是:
outputs, state = tf.nn.dynamic_rnn(cell, inputs, sequence_length=..., initial_state=state)
state是final state,如果有n layer,则是final state也有n个元素,对应每一层的state。
(2)tf.nn.rnn
这个函数和dynamic_rnn的区别就在于,这个需要的inputs是a list of tensor,这个list的长度是num_steps,也就是将每一个时刻的输入切分出来了,tensor的shape=[batch_size, input_size]【这里的input每一个都是word embedding,因此input_size=hidden_units_size】
除了输出inputs是list之外,输出稍有差别。
可以看到,输出也是一个长度为T(num_steps)的list,每一个output对应一个t时刻的input(batch_size, hidden_units_size),output shape=[batch_size, hidden_units_size]
(3)state_saving_rnn
这个方法可以接收一个state saver对象,这是和以上两个方法不同之处,另外其inputs和outputs也都是list of tensors。
(4)bidirectional_rnn
等研究bi-rnn网络的时候再讲。
以上介绍了四种rnn的构建方式,这里选择dynamic_rnn.因为inputs中的第2个维度已经是num_steps了。
得到output之后传到下一层softmax layer
既然我们用的是dynamic_rnn,那么outputs shape=[batch_size, num_steps, size],而接下来需要将output传入到softmax层,softmax层并没有显式地使用tf.nn.softmax函数,而是只是计算了wx+b得到logits(实际上是一样的,softmax函数仅仅只是将logits再rescale到0-1之间)
得到logits后,用到了nn.seq2seq.sequence_loss_by_example函数来计算“所谓的softmax层”的loss。这个loss是整个batch上累加的loss,需要除上batch_size,得到平均下来的loss,也就是self._cost。
loss = tf.nn.seq2seq.sequence_loss_by_example(
[tf.reshape(self._targets, [-1])],
[tf.ones([batch_size * num_steps])])
self._cost = cost = tf.reduce_sum(loss) / batch_size
self._final_state = state
求导,定义train_op
如果is_training=False,也就是仅valid or test的话,计算出loss这一步也就终止了。之所以要求导,就是train的过程。所以这个地方对is_training进行一个判断。
if not is_training:
如果想在训练过程中调节learning rate的话,生成一个lr的variable,但是trainable=False,也就是不进行求导。
self._lr = tf.Variable(0.0, trainable=False)
gradient在backpropagate过程中,很容易出现vanish&explode现象,尤其是rnn这种back很多个time step的结构。
因此都要使用clip来对gradient值进行调节。
既然要调节了就不能简单的调用optimizer.minimize(loss),而是需要显式的计算gradients,然后进行clip,将clip后的gradient进行apply。
官方文档说明了这种操作:
并给出了一个例子:
# Create an optimizer.
opt = GradientDescentOptimizer(learning_rate=0.1)
# Compute the gradients for a list of variables.
grads_and_vars = pute_gradients(loss, &list of variables&)
# grads_and_vars is a list of tuples (gradient, variable).
Do whatever you
# need to the 'gradient' part, for example cap them, etc.
capped_grads_and_vars = [(MyCapper(gv[0]), gv[1]) for gv in grads_and_vars]
# Ask the optimizer to apply the capped gradients.
opt.apply_gradients(capped_grads_and_vars)
模仿这个代码,我们可以写出如下的伪代码:
optimizer = tf.train.AdamOptimizer(learning_rate=self._lr)
# gradients: return A list of sum(dy/dx) for each x in xs.
grads = optimizer.gradients(self._cost, &list of variables&)
clipped_grads = tf.clip_by_global_norm(grads, config.max_grad_norm)
# accept: List of (gradient, variable) pairs, so zip() is needed
self._train_op = optimizer.apply_gradients(zip(grads, &list of variables&))
可以看到,此时就差一个&list of variables&不知道了,也就是需要对哪些variables进行求导。
答案是:trainable variables
因此,我们得到
tvars = tf.trainable_variables()
用tvars带入上面的代码中即可。
how to change Variable value
使用tf.assign(ref, value)函数。ref应该是个variable node,这个assign是个operation,因此需要在sess.run()中进行才能生效。这样之后再调用ref的值就发现改变成新值了。
在这个模型中用于改变learning rate这个variable的值。
def assign_lr(self, session, lr_value):
session.run(tf.assign(self.lr, lr_value))
run_epoch()
Tensor.eval()
比如定义了一个tensor x,x.eval(feed_dict={xxx})就可以得到x的值,而不用sess.run(x, feed_dict={xxx})。返回值是一个numpy array。
state = m.initial_state.eval()
for step, (x, y) in enumerate(reader.ptb_iterator(data, m.batch_size,
m.num_steps)):
cost, state, _ = session.run([m.cost, m.final_state, eval_op],
{m.input_data: x,
m.targets: y,
m.initial_state: state})
为什么feed_dict中还需要传入initial_statel?
TA的最新馆藏
喜欢该文的人也喜欢

我要回帖

更多关于 tensorflow源码理解 的文章

 

随机推荐