用简单方法学会----参数估计

4.6 参数估计

参数估计(parameter estimation)是根据从总体中抽取的样本估计总体分布中包含的未知参数的方法。人们常常需要根据手中的数据,分析或推断数据反映的本质规律。即根据样本数据如何选择统计量去推断总体的分布或数字特征等。统计推断是数理统计研究的核心问题。所谓统计推断是指根据样本对总体分布或分布的数字特征等作出合理的推断。它是统计推断的一种基本形式,是数理统计学的一个重要分支。
参数估计有最大似然估计和EM算法,他们都是在已知样本数据下,如何确定未知参数的值,才能使样本值最大。而他们的区别是EM是带隐变量的似然估计,当有部分数据缺失或者无法观察到时,EM算法提供了一个高效的迭代程序用来计算这些数据的最大似然估计。

4.6.1极大似然估计

4.6.1.1极大似然估计简介

极大似然估计是一种参数估计的方法。
先验概率是 知因求果,后验概率是 知果求因,极大似然是 知果求最可能的原因。
即它的核心思想是:找到参数 θ的一个估计值,使得当前样本出现的可能性最大。

例如,当其他条件一样时,抽烟者患肺癌的概率是不抽烟者的 5 倍,那么当我们已知现在有个人是肺癌患者,问这个人是抽烟还是不抽烟?大多数人都会选择抽烟,因为这个答案是“最有可能”得到“肺癌”这样的结果。

4.6.1.2 为什么要有参数估计

当模型已定,但是参数未知时。
例如我们知道全国人民的身高服从正态分布,这样就可以通过采样,观察其结果,然后再用样本数据的结果推出正态分布的均值与方差的大概率值,就可以得到全国人民的身高分布的函数。

4.6.1.3为什么要用似然函数取最大

极大似然估计是频率学派最经典的方法之一,认为真实发生的结果的概率应该是最大的,那么相应的参数,也应该是能让这个状态发生的概率最大的参数。

4.6.1.4何为似然函数

统计学中,似然(likelihood)函数是一种关于统计模型参数的函数。给定输出x时,关于参数θ的似然函数L(θ|x)(在数值上)等于给定参数θ后变量X的概率:L(θ|x)=P(X=x|θ)。
如果有n个样本,似然函数为:
设总体X服从分布P(x;θ)(当X是连续型随机变量时为概率密度,当X为离散型随机变量时为概率分布),θ为待估参数,X1,X2,…Xn是来自于总体X的样本,x1,x2…xn为样本X1,X2,…Xn的一个观察值,则样本的联合分布(当X是连续型随机变量时为概率密度,当X为离散型随机变量时为概率分布) L(θ)=L(x1,x2,…,xn;θ)=ΠP(xi;θ)称为似然函数.

4.6.1.5极大似然估计的计算过程

(1)写出似然函数:

其中 x1,x2,..,xn 为样本,θ 为要估计的参数。

(2) 一般对似然函数取对数

为何要两边取对数?因为 f(xi|θ) 一般比较小,n 比较大,如果连乘容易造成浮点运算下溢。
(3) 求出使得对数似然函数取最大值的参数值
对对数似然函数求导,令导数为0,得出似然方程,
求解似然方程,得到的参数就是对概率模型中参数值的极大似然估计。
(4)示例
假如一个罐子里有黑白两种颜色的球,数目和比例都不知道。
假设进行一百次有放回地随机采样,每次取一个球,有七十次是白球。
问题是要求得罐中白球和黑球的比例?

假设罐中白球的比例是 p,那么黑球的比例就是 1-p。
第1步:定义似然函数:

第2步:对似然函数对数化

第3步:求似然方程
即对参数p求导,并令导数为0。

第4步 解方程
最后求得 p=0.7

4.6.2 EM算法

EM(Expectation Maximization) 算法是 Dempster,Laind,Rubin 于 1977 年提出的求参数极大似然估计的一种方法,它可以从非完整数据集中对参数进行 MLE 估计,是一种非常简单实用的学习算法。这种方法可以广泛地应用于处理缺损数据,截尾数据,带有噪声等所谓的不完全数据。
EM的主要原理可以用一个比较形象的比喻说法。比如说食堂的大师傅炒了一份菜,要等分成两份给两个人吃,显然没有必要拿来天平一点的精确的去称分量,最简单的办法是先随意的把菜分到两个碗中,然后观察是否一样多,把比较多的那一份取出一点放到另一个碗中,这个过程一直迭代地执行下去,直到大家看不出两个碗所容纳的菜有什么分量上的不同为止。
EM算法就是这样,假设我们估计知道A和B两个参数,在开始状态下二者都是未知的,并且知道了A的信息就可以得到B的信息,反过来知道了B也就得到了A。可以考虑首先赋予A某种初值,以此得到B的估计值,然后从B的当前值出发,重新估计A的取值,这个过程一直持续到收敛为止。

4.6.2.1简单回顾最大似然估计

极大似然估计,只是一种概率论在统计学的应用,它是参数估计的方法之一。说的是已知某个随机样本满足某种概率分布,但是其中具体的参数不清楚,参数估计就是通过若干次试验,观察其结果,利用结果推出参数的大概值。最大似然估计是建立在这样的思想上:已知某个参数能使这个样本出现的概率最大,我们当然不会再去选择其他小概率的样本,所以干脆就把这个参数作为估计的真实值。

4.6.2.2求最大似然函数估计值的一般步骤

(1)写出似然函数;
(2)对似然函数取对数,并整理;
(3)求导数,令导数为0,得到似然方程;
(4)解似然方程,得到的参数即为所求;

4.6.2.3生活中EM原型

在上面那个身高分布估计中。男生的身高服从高斯分布、女生身高也服从高斯分布,只是他们的参数可能不相同,那么通过抽取得到的那100个男生的身高和已知的其身高服从高斯分布,我们通过最大化其似然函数,就可以得到了对应高斯分布的参数θ=[u, ∂]T了。那么,对于我们学校的女生的身高分布也可以用同样的方法得到了。
上面这个例子,我们知道抽取的男生还是女生,即知道其分布(都是高斯分布,虽然不知分布的具体参数),现在假如我们从这200个人中,这200人,由于化妆的原因,我们无法确定这个人(的身高)是男生(的身高)还是女生(的身高)。也就是说你不知道抽取的那200个人里面的每一个人到底是从男生的那个身高分布里面抽取的,还是女生的那个身高分布抽取的。用数学的语言就是,抽取得到的每个样本都不知道是从哪个分布抽取的。
这个时候,对于每一个样本或者你抽取到的人,就有两个东西需要猜测或者估计的了,一是这个人是男的还是女的?二是男生和女生对应的身高的高斯分布的参数是多少?
只有当我们知道了哪些人属于同一个高斯分布的时候,我们才能够对这个分布的参数作出靠谱的预测,例如刚开始的最大似然所说的,但现在两种高斯分布的人混在一块了,我们又不知道哪些人属于第一个高斯分布,哪些属于第二个,所以就没法估计这两个分布的参数。反过来,只有当我们对这两个分布的参数作出了准确的估计的时候,才能知道到底哪些人属于第一个分布,那些人属于第二个分布。
如何这类互相依赖问题呢?这有点像先有鸡还是先有蛋的问题了。鸡说,没有我,谁把你生出来的啊。蛋不服,说,没有我,你从哪蹦出来啊。为了解决这个你依赖我,我依赖你的循环依赖问题,总得有一方要先打破僵局,说,不管了,我先随便整一个值出来,看你怎么变,然后我再根据你的变化调整我的变化,然后如此迭代着不断互相推导,最终就会收敛到一个解。这就是EM算法的基本思想了。
EM的意思是“Expectation Maximization”,在我们上面这个问题里面,我们是先随便猜一下男生(身高)的正态分布的参数:如均值和方差是多少。例如男生的均值是1米7,方差是0.1米(当然了,刚开始肯定没那么准),然后计算出每个人更可能属于第一个还是第二个正态分布中的(例如,这个人的身高是1米8,那很明显,他最大可能属于男生的那个分布),这个是属于Expectation一步。有了每个人的归属,或者说我们已经大概地按上面的方法将这200个人分为男生和女生两部分,我们就可以根据之前说的最大似然那样,通过这些被大概分为男生的n个人来重新估计第一个分布的参数,女生的那个分布同样方法重新估计。这个是Maximization。然后,当我们更新了这两个分布的时候,每一个属于这两个分布的概率又变了,那么我们就再需要调整E步……如此往复,直到参数基本不再发生变化为止。
这里把每个人(样本)的完整描述看做是三元组yi={xi,zi1,zi2},其中,xi是第i个样本的观测值,也就是对应的这个人的身高,是可以观测到的值。zi1和zi2表示男生和女生这两个高斯分布中哪个被用来产生值xi,就是说这两个值标记这个人到底是男生还是女生(的身高分布产生的)。这两个值我们是不知道的,是隐含变量。确切的说,zij在xi由第j个高斯分布产生时值为1,否则为0。例如一个样本的观测值为1.8,然后他来自男生的那个高斯分布,那么我们可以将这个样本表示为{1.8, 1, 0}。如果zi1和zi2的值已知,也就是说每个人我已经标记为男生或者女生了,那么我们就可以利用上面说的最大似然算法来估计他们各自高斯分布的参数。但是它们未知,因此我们只能用EM算法。
咱们现在不是因为那个恶心的隐含变量(抽取得到的每个样本都不知道是从哪个分布抽取的)使得本来简单的可以求解的问题变复杂了,求解不了吗。那怎么办呢?人类解决问题的思路都是想能否把复杂的问题简单化。好,那么现在把这个复杂的问题逆回来,我假设已经知道这个隐含变量了,哎,那么求解那个分布的参数是不是很容易了,直接按上面说的最大似然估计就好了。那你就问我了,这个隐含变量是未知的,你怎么就来一个假设说已知呢?你这种假设是没有根据的。呵呵,我知道,所以我们可以先给这个给分布弄一个初始值,然后求这个隐含变量的期望,当成是这个隐含变量的已知值,那么现在就可以用最大似然求解那个分布的参数了吧,那假设这个参数比之前的那个随机的参数要好,它更能表达真实的分布,那么我们再通过这个参数确定的分布去求这个隐含变量的期望,然后再最大化,得到另一个更优的参数,……迭代,就能得到一个皆大欢喜的结果了。
这时候你就不服了,说你老迭代迭代的,你咋知道新的参数的估计就比原来的好啊?为什么这种方法行得通呢?有没有失效的时候呢?什么时候失效呢?用到这个方法需要注意什么问题呢?呵呵,一下子抛出那么多问题,搞得我适应不过来了,不过这证明了你有很好的搞研究的潜质啊。呵呵,其实这些问题就是数学家需要解决的问题。在数学上是可以稳当的证明的或者得出结论的。那咱们用数学来把上面的问题重新描述下。(在这里可以知道,不管多么复杂或者简单的物理世界的思想,都需要通过数学工具进行建模抽象才得以使用并发挥其强大的作用,而且,这里面蕴含的数学往往能带给你更多想象不到的东西,这就是数学的精妙所在啊)。

4.6.2.4 EM算法推导

假设我们有一个样本集{x(1),…,x(m)},包含m个独立的样本。但每个样本i对应的类别z(i)是未知的(如,不知道它属于哪种分布),也即隐含变量。故我们需要估计概率模型p(x,z)的参数θ,但是由于里面包含隐含变量z,所以很难用最大似然求解,但如果z知道了,那我们就很容易求解了。
对于参数估计,我们本质上还是想获得一个使似然函数最大化的那个参数θ,现在与最大似然不同的只是似然函数式中多了一个未知的变量z,见下式(1)。也就是说我们的目标是找到适合的θ和z让L(θ)最大。那我们也许会想,你就是多了一个未知的变量而已啊,我也可以分别对未知的θ和z分别求偏导,再令其等于0,求解出来不也一样吗?

本质上我们是需要最大化(1)式(对(1)式,我们回忆下联合概率密度下某个变量的边缘概率密度函数的求解,注意这里z也是随机变量。对每一个样本i的所有可能类别z求等式右边的联合概率密度函数和,也就得到等式左边为随机变量x的边缘概率密度),也就是似然函数,但是可以看到里面有“和的对数”,求导后形式会非常复杂(自己可以想象下log(f1(x)+ f2(x)+ f3(x)+…)复合函数的求导),所以很难求解得到未知参数z和θ。那OK,我们可否对(1)式做一些改变呢?我们看(2)式,(2)式只是分子分母同乘以一个相等的函数,还是有“和的对数”啊,还是求解不了,那为什么要这么做呢?咱们先不管,看(3)式,发现(3)式变成了“对数的和”,那这样求导就容易了。我们注意点,还发现等号变成了不等号,为什么能这么变呢?这就是Jensen不等式的大显神威的地方。

4.6.2.5Jensen不等式

设f是定义域为实数的函数,如果对于所有的实数x。如果对于所有的实数x,f(x)的二次导数大于等于0,那么f是凸函数。当x是向量时,如果其hessian矩阵H是半正定的,那么f是凸函数。如果只大于0,不等于0,那么称f是严格凸函数。
Jensen不等式表述如下:
如果f是凸函数,X是随机变量,那么:E[f(X)]>=f(E[X])
特别地,如果f是严格凸函数,当且仅当X是常量时,上式取等号。
如果用图表示会很清晰:

图中,实线f是凸函数,X是随机变量,有0.5的概率是a,有0.5的概率是b。(就像掷硬币一样)。X的期望值就是a和b的中值了,图中可以看到E[f(X)]>=f(E[X])成立。
当f是(严格)凹函数当且仅当-f是(严格)凸函数。
Jensen不等式应用于凹函数时,不等号方向反向。
回到公式(2),因为f(x)=log x为凹函数(其二次导数为-1/x2<0)。(2)式中

的期望,(考虑到E(X)=∑x*p(x),f(X)是X的函数,则E(f(X))=∑f(x)*p(x)),又 ,所以就可以得到公式(3)的不等式了。

4.6.2.6 EM算法运算步骤4.6.2.7代码实现

4.6.2.7.1 双硬币模型

假设有两枚硬币A、B,以相同的概率随机选择一个硬币,进行如下的抛硬币实验:共做5次实验,每次实验独立的抛十次,结果如图中a所示,例如某次实验产生了H、T、T、T、H、H、T、H、T、H,H代表正面朝上。
假设试验数据记录员可能是实习生,业务不一定熟悉,造成a和b两种情况
a表示实习生记录了详细的试验数据,我们可以观测到试验数据中每次选择的是A还是B
b表示实习生忘了记录每次试验选择的是A还是B,我们无法观测实验数据中选择的硬币是哪个。问在两种情况下分别如何估计两个硬币正面出现的概率?

这是实习生a记录的情况,由于这里数据的背后参考模型已知(已分好类),因此用极大似然估计方法就能分别求出θ ̂_A和θ ̂_B的概率来。与上文第一节中的例子完全类似。


上图为实习生b记录的情况,令人遗憾的是数据的背后参考模型混淆在了一起,我们无法得知它们这几组实验数据是由A抛的还是由B抛的,因为这里隐含了一个该组数据是A还是B的分类问题。抛开数学对隐含变量的复杂求解过程,我们可以先给出一个思路来解决上述问题。
第一,既然我们不清楚是A类还是B类,但假定我们初始化了A类硬币抛正面的概率和B类硬币抛正面的概率,这两者的概率是随意定的,但由于两者概率可以存在差异,假设P(y=H;θA)>P(y=H;θB),那么一个明显的特征就是,由于能观察到10次硬币中有几次是成功的,我们可以基于这次观察,给出P(z=A|Y;θA,θB)的概率,上式的含义是可以根据两个参数的初值求出,在给定观察序列的情况下,它属于A类还是B类的概率。用公式可以表示为:

其中,z表示单个观察到的随机变量,此处z=A or B(属于分类问题),Y表示观察序列,即Y=(y1,y2,...,y10)T。由此,给定观察序列后,我们可以算出属于A类的概率和属于B类的概率,那么很显然CoinA 和CoinB 不再是属于你有我没有,你没有我有的敌对关系,因为我自己都还不是很清楚是不是A类,由此10个硬币,我们就根据概率进行一次平均分配咯,这样CoinA 和CoinB 在一次观察结果时,都能得到属于自己的那一份,非常的和谐。这一部分便是求期望的过程,即对于第一个观察序列中,10次抛硬币过程中5次为正面朝上,令yj=5,由此可以得到关于隐含变量的数学期望E(z)=0.45*5+0.55*5,其中0.45*5表示CoinA的分配; 0.55*5表示CoinB的分配。分配的份额根据z函数的分布给定,z函数的分布规则根据缺失的信息进行建模,解由初始参数求出。
因此分类问题,给出了每个CoinA 和CoinB 的分配额,有了所有观察值CoinA和CoinB的分配额,我们就可以单独的对CoinA和CoinB进行最大似然估计方法。求出来的新参数,再求z函数,求期望,求参数,如此迭代下去,直到收敛到某一结果。

4.6.2.7.2导入必要的库

4.6.2.7.2编写伯努利分布函数

在双硬币模型中,对某个种类硬币投掷10次中成功n次概率模型

符合伯努利分布

可视化伯努利分布

运行结果如下:

这是在p=0.2的情况下的伯努利分布函数,代回双硬币模型中去,当观察到10次实验中只有2次成功了,那么该θ参数便是0.2。因为只有当θ=0.2时,10次实验中出现成功次数为2次的概率最大

4.6.2.7.3 定义观察矩阵

由数据可得观察矩阵为

有实习生a记录的信息可知,实际每组观察数据属于A类,B类的隐藏状态为:

那么在观察数组中,属于A类的数组为:

运行结果为:
array([[1, 1, 1, 1, 0, 1, 1, 1, 1, 1],
[1, 0, 1, 1, 1, 1, 1, 0, 1, 1],
[0, 1, 1, 1, 0, 1, 1, 1, 0, 1]])
在所有属于A类的数组中,总的实验成功次数为:

运行结果为:24
所以说,针对属于A类的硬币,它的参数θ_A:

运行结果为:0.80000000000000004

同理,对于属于B类的硬币,它的参数为θ_B:

运行结果为:0.45000000000000001

4.6.2.7.4 EM算法步骤

(1)首先来看看,针对第一组观察数据,每一步的数据是如何求出的。
# 对于实验成功率为0.6的情况,10次中成功5次的概率

(2)单步EM算法,迭代一次算法实现步骤。

(3)迭代一次输出结果为:

运行结果为:[0.71301223540051617, 0.58133930831366265]

4.6.2.7.5 EM主循环

给定循环的两个终止条件:模型参数变化小于阈值;循环达到最大次数,就可以写出EM算法的主循环了。

最终结果为:

运行结果为:
[[0.79678875938310978, 0.51958393567528027], 14]
我们可以改变初值,试验初值对EM算法的影响。

运行结果为:
[[0.79678843908109542, 0.51957953211429142], 11]
EM算法对于参数的改变还是有一定的健壮性的。

最终实习生b的EM算法得出的结果,跟实习生a得出的参数还是非常相近的,但EM算法跟初始值的设置有着很大的关系,不信,修改[06,0.5]多尝试尝试。

运行结果为:
[[0.51958345063012845, 0.79678895444393927], 15]

参考:
http://blog.csdn.net/u014688145/article/details/53073266
http://blog.csdn.net/zouxy09/article/details/8537620
http://blog.csdn.net/lilynothing/article/details/64443563

一种全新的读取数据方式 Dataset API

第8章 一种全新的读取数据方式 Dataset API

8.1 Dataset API简介

Dataset API可以用简单复用的方式构建复杂的Input Pipeline。例如:一个图片模型的Pipeline可能会聚合在一个分布式文件系统中的多个文件,对每个图片进行随机扰动(random perturbations),接着将随机选中的图片合并到一个training batch中。一个文本模型的Pipeline可能涉及到:从原始文本数据中抽取特征,并通过一个转换(Transformation)将不同的长度序列batch在一起。Dataset API可以很方便地以不同的数据格式处理大量的数据,以及处理复杂的转换。
Dataset API是TensorFlow 1.3版本中引入的一个新的模块,主要用于数据读取,构建输入数据的pipeline等。之前,在TensorFlow中读取数据一般有两种方法:
 使用placeholder读内存中的数据
 使用queue读硬盘中的数据
Dataset API同时支持从内存和硬盘的读取,相比之前的两种方法在语法上更加简洁易懂。
此外,如果想要使用TensorFlow新出的Eager模式,就必须要使用Dataset API来读取数据。
Dataset API的导入,在TensorFlow 1.3中,Dataset API是放在contrib包中的:
tf.contrib.data.Dataset,而在TensorFlow 1.4中,Dataset API已经从contrib包中移除,变成了核心API的一员:tf.data.Dataset

8.2 Dataset API 架构

图1 Dataset API架构图

Dataset API引入了两个新的抽象类到Tensorflow中:

 tf.data.Dataset
表示一串元素(elements),其中每个元素包含了一或多个Tensor对象。例如:在一个图片pipeline中,一个元素可以是单个训练样本,它们带有一个表示图片数据的tensors和一个label组成的pair。有两种不同的方式创建一个dataset:
(1)创建一个source (例如:Dataset.from_tensor_slices()), 从一或多个tf.Tensor对象中构建一个dataset
(2)应用一个transformation(例如:Dataset.batch()),从一或多个tf.contrib.data.Dataset对象上构建一个dataset
 tf.data.Iterator
它提供了主要的方式来从一个dataset中抽取元素。通过Iterator.get_next() 返回的该操作会yields出Datasets中的下一个元素,作为输入pipeline和模型间的接口使用。最简单的iterator是一个“one-shot iterator”,它与一个指定的Dataset相关联,通过它来进行迭代。对于更复杂的使用,Iterator.initializer操作可以使用不同的datasets重新初始化(reinitialize)和参数化(parameterize)一个iterator ,例如,在同一个程序中通过training data和validation data迭代多次。
以下为生成Dataset的常用方法:
(1)tf.data.Dataset.from_tensor_slices()
利用tf.data.Dataset.from_tensor_slices()从一或多个tf.Tensor对象中构建一个dataset,
其tf.Tensor对象中包括数组、矩阵、字典、元组等,具体实例如下:

import tensorflow as tf
import numpy as np

arry1=np.array([1.0, 2.0, 3.0, 4.0, 5.0])
dataset = tf.data.Dataset.from_tensor_slices(arry1)
#生成实例
iterator = dataset.make_one_shot_iterator()
#从iterator里取出一个元素
one_element = iterator.get_next()
with tf.Session() as sess:
for i in range(len(arry1)):
print(sess.run(one_element))

运行结果为:1,2,3,4,5

(2)Dataset的转换(transformations)
支持任何结构的datasets当使用Dataset.map(),Dataset.flat_map(),以及Dataset.filter()
Dataset支持一类特殊的操作:Transformation。一个Dataset通过Transformation变成一个新的Dataset。通常我们可以通过Transformation完成数据变换,常用的Transformation有:
map()、flat_map()、filter()、filter()、shuffle()、repeat()、tf.py_func()等等。以下是一些简单示例:

import tensorflow as tf
import numpy as np

a1=np.array([1.0, 2.0, 3.0, 4.0, 5.0])
dataset = tf.data.Dataset.from_tensor_slices(a1)
dataset = dataset.map(lambda x: x * 2) # 2.0, 3.0, 4.0, 5.0, 6.0
iterator = dataset.make_one_shot_iterator()
#从iterator里取出一个元素
one_element = iterator.get_next()
with tf.Session() as sess:
for i in range(len(a1)):
print(sess.run(one_element))

flat_map()、filter()等的使用

#使用<code>Dataset.flat_map()</code>将每个文件转换为一个单独的嵌套数据集
#然后将它们的内容顺序连接成一个单一的“扁平”数据集
#跳过第一行(标题行)
#滤除以“#”开头的行(注释)
filenames = ["/var/data/file1.txt", "/var/data/file2.txt"]

dataset = tf.data.Dataset.from_tensor_slices(filenames)

dataset = dataset.flat_map(
lambda filename: (
tf.data.TextLineDataset(filename)
.skip(1)
.filter(lambda line: tf.not_equal(tf.substr(line, 0, 1), "#"))))

batch()、shuffle()、repeat()

filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.shuffle(buffer_size=10000)
dataset = dataset.batch(32)
dataset = dataset.repeat(4)

(3)tf.data.TextLineDataset()
很多数据集是一个或多个文本文件。tf.contrib.data.TextLineDataset提供了一种简单的方式来提取这些文件的每一行。给定一个或多个文件名,TextLineDataset会对这些文件的每行生成一个值为字符串的元素。TextLineDataset也可以接受tf.Tensor作为filenames,所以你可以传递一个tf.placeholder(tf.string)作为参数。这个函数的输入是一个文件的列表,输出是一个dataset。dataset中的每一个元素就对应了文件中的一行。可以使用这个函数来读入CSV文件

filenames = ["/var/data/file1.txt", "/var/data/file2.txt"]
dataset = tf.data.TextLineDataset(filenames)

默认下,TextLineDataset生成每个文件中的每一行,这可能不是你所需要的,例如文件中有标题行,或包含注释。可以使用Dataset.skip()和Dataset.filter()来剔除这些行。为了对每个文件都各自应用这些变换,使用Dataset.flat_map()来对每个文件创建一个嵌套的Dataset。

filenames = ["/var/data/file1.txt", "/var/data/file2.txt"]
dataset = tf.contrib.data.Dataset.from_tensor_slices(filenames)
# Use <code>Dataset.flat_map()</code> to transform each file as a separate nested dataset,
# and then concatenate their contents sequentially into a single "flat" dataset.
# * Skip the first line (header row).
# * Filter out lines beginning with "#" (comments).
dataset = dataset.flat_map(
lambda filename: (
tf.contrib.data.TextLineDataset(filename)
.skip(1)
.filter(lambda line: tf.not_equal(tf.substr(line, 0, 1), "#"))))

(4)tf.data.FixedLengthRecordDataset():
这个函数的输入是一个文件的列表和一个record_bytes,之后dataset的每一个元素就是文件中固定字节数record_bytes的内容。通常用来读取以二进制形式保存的文件,如CIFAR10数据集就是这种形式。

(5)tf.data.TFRecordDataset():
TFRecord是一种面向记录的二进制文件,很多TensorFlow应用使用它作为训练数据。tf.contrib.data.TFRecordDataset能够使TFRecord文件作为输入管道的输入流。

filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
dataset = tf.data.TFRecordDataset(filenames)

传递给TFRecordDataset的参数filenames可以是字符串,字符串列表或tf.Tensor类型的字符串。因此,如果有两组文件分别作为训练和验证,可以使用tf.placeholder(tf.string)来表示文件名,使用适当的文件名来初始化一个迭代器。

filenames = tf.placeholder(tf.string, shape=[None])
dataset = tf.data.TFRecordDataset(filenames)
iterator = dataset.make_initializable_iterator()

training_filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
with tf.Session() as sess:
sess.run(iterator.initializer, feed_dict={filenames: training_filenames})
# Initialize <code>iterator</code> with validation data.
validation_filenames = ["/var/data/validation1.tfrecord", ...]
sess.run(iterator.initializer, feed_dict={filenames: validation_filenames})

8.3 使用Dataset Tensor实例

以上我们通过一个实例来介绍Dataset API的具体使用,实例内容用MNIST数据集为原数据,使用卷积神经网络,对手写0-9十个数字进行识别。
环境配置信息为:Python3.6,Tensorflow1.3,使用CPU
具体步骤如下:
 定义获取、预处理数据集的代码
 加载数据
 创建Dataset Tensor
 创建卷积神经网络
 训练及评估模型

8.3.1.导入需要的库

import os
import struct
import numpy as np
import tensorflow as tf

8.3.2.定义一个把标签变为热编码(one-hot)的函数

def dense_to_one_hot(labels_dense, num_classes=10):
"""Convert class labels from scalars to one-hot vectors."""
num_labels = labels_dense.shape[0]
index_offset = np.arange(num_labels) * num_classes
labels_one_hot = np.zeros((num_labels, num_classes))
labels_one_hot.flat[index_offset + labels_dense.ravel()] = 1
return labels_one_hot

8.3.3.定义加载数据函数

def load_mnist(path, kind='train'):
"""Load MNIST data from path"""
labels_path = os.path.join(path, '%s-labels-idx1-ubyte' % kind)
images_path = os.path.join(path, '%s-images-idx3-ubyte' % kind)

with open(labels_path, 'rb') as lbpath:
magic, n = struct.unpack('>II',lbpath.read(8))
labels = np.fromfile(lbpath, dtype=np.uint8)
labels=dense_to_one_hot(labels)

with open(images_path, 'rb') as imgpath:
magic, num, rows, cols = struct.unpack(">IIII",imgpath.read(16))
images = np.fromfile(imgpath, dtype=np.uint8).reshape(len(labels), 784)
#images = np.fromfile(imgpath, dtype=np.float32).reshape(len(labels), 784)

return images, labels

8.3.4 加载数据

import matplotlib.pyplot as plt
%matplotlib inline

X_train, y_train = load_mnist('./data/mnist/', kind='train')
print('Rows: %d, columns: %d' % (X_train.shape[0], X_train.shape[1]))
print('Rows: %d, columns: %d' % ( y_train.shape[0], y_train.shape[1]))

X_test, y_test = load_mnist('./data/mnist/', kind='t10k')
print('Rows: %d, columns: %d' % (X_test.shape[0], X_test.shape[1]))

运行结果:
Rows: 60000, columns: 784
Rows: 60000, columns: 10
Rows: 10000, columns: 784

8.3.5 定义参数

# Parameters
learning_rate = 0.001
num_steps = 2000
batch_size = 128
display_step = 100

# Network Parameters
n_input = 784 # MNIST data input (img shape: 28*28)
n_classes = 10 # MNIST total classes (0-9 digits)
dropout = 0.75 # Dropout, probability to keep units

8.3.6 创建Dataset Tensor

sess = tf.Session()

# Create a dataset tensor from the images and the labels
dataset = tf.contrib.data.Dataset.from_tensor_slices(
(X_train.astype(np.float32),y_train.astype(np.float32)))
# Create batches of data
dataset = dataset.batch(batch_size)
# Create an iterator, to go over the dataset
iterator = dataset.make_initializable_iterator()
# It is better to use 2 placeholders, to avoid to load all data into memory,
# and avoid the 2Gb restriction length of a tensor.
_data = tf.placeholder(tf.float32, [None, n_input])
_labels = tf.placeholder(tf.float32, [None, n_classes])
# Initialize the iterator
sess.run(iterator.initializer, feed_dict={_data: X_train.astype(np.float32),
_labels: y_train.astype(np.float32)})

# Neural Net Input
X, Y = iterator.get_next()

8.3.7 创建卷积神经网络模型

# Create model
def conv_net(x, n_classes, dropout, reuse, is_training):
# Define a scope for reusing the variables
with tf.variable_scope('ConvNet', reuse=reuse):
# MNIST data input is a 1-D vector of 784 features (28*28 pixels)
# Reshape to match picture format [Height x Width x Channel]
# Tensor input become 4-D: [Batch Size, Height, Width, Channel]
x = tf.reshape(x, shape=[-1, 28, 28, 1])

# Convolution Layer with 32 filters and a kernel size of 5
conv1 = tf.layers.conv2d(x, 32, 5, activation=tf.nn.relu)
# Max Pooling (down-sampling) with strides of 2 and kernel size of 2
conv1 = tf.layers.max_pooling2d(conv1, 2, 2)

# Convolution Layer with 32 filters and a kernel size of 5
conv2 = tf.layers.conv2d(conv1, 64, 3, activation=tf.nn.relu)
# Max Pooling (down-sampling) with strides of 2 and kernel size of 2
conv2 = tf.layers.max_pooling2d(conv2, 2, 2)

# Flatten the data to a 1-D vector for the fully connected layer
fc1 = tf.contrib.layers.flatten(conv2)

# Fully connected layer (in contrib folder for now)
fc1 = tf.layers.dense(fc1, 1024)
# Apply Dropout (if is_training is False, dropout is not applied)
fc1 = tf.layers.dropout(fc1, rate=dropout, training=is_training)

# Output layer, class prediction
out = tf.layers.dense(fc1, n_classes)
# Because 'softmax_cross_entropy_with_logits' already apply softmax,
# we only apply softmax to testing network
out = tf.nn.softmax(out) if not is_training else out

return out

8.3.8 训练及评估模型

# Because Dropout have different behavior at training and prediction time, we
# need to create 2 distinct computation graphs that share the same weights.

# Create a graph for training
logits_train = conv_net(X, n_classes, dropout, reuse=False, is_training=True)
# Create another graph for testing that reuse the same weights, but has
# different behavior for 'dropout' (not applied).
logits_test = conv_net(X, n_classes, dropout, reuse=True, is_training=False)

# Define loss and optimizer (with train logits, for dropout to take effect)
loss_op = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(
logits=logits_train, labels=Y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
train_op = optimizer.minimize(loss_op)

# Evaluate model (with test logits, for dropout to be disabled)
correct_pred = tf.equal(tf.argmax(logits_test, 1), tf.argmax(Y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))

# Initialize the variables (i.e. assign their default value)
init = tf.global_variables_initializer()

sess.run(init)

for step in range(1, num_steps + 1):

try:
# Run optimization
sess.run(train_op)
except tf.errors.OutOfRangeError:
# Reload the iterator when it reaches the end of the dataset
sess.run(iterator.initializer,
feed_dict={_data: X_train.astype(np.float32),
_labels: y_train.astype(np.float32)})
sess.run(train_op)

if step % display_step == 0 or step == 1:
# Calculate batch loss and accuracy
# (note that this consume a new batch of data)
loss, acc = sess.run([loss_op, accuracy])
print("Step " + str(step) + ", Minibatch Loss= " + \
"{:.4f}".format(loss) + ", Training Accuracy= " + \
"{:.3f}".format(acc))

print("Optimization Finished!")

运行结果如下:
Step 1, Minibatch Loss= 182.4177, Training Accuracy= 0.258
Step 100, Minibatch Loss= 0.6034, Training Accuracy= 0.891
Step 200, Minibatch Loss= 0.4140, Training Accuracy= 0.930
Step 300, Minibatch Loss= 0.0813, Training Accuracy= 0.977
Step 400, Minibatch Loss= 0.1380, Training Accuracy= 0.969
Step 500, Minibatch Loss= 0.1193, Training Accuracy= 0.945
Step 600, Minibatch Loss= 0.3291, Training Accuracy= 0.953
Step 700, Minibatch Loss= 0.2158, Training Accuracy= 0.969
Step 800, Minibatch Loss= 0.1293, Training Accuracy= 0.977
Step 900, Minibatch Loss= 0.1323, Training Accuracy= 0.977
Step 1000, Minibatch Loss= 0.2017, Training Accuracy= 0.961
Step 1100, Minibatch Loss= 0.1555, Training Accuracy= 0.961
Step 1200, Minibatch Loss= 0.0744, Training Accuracy= 0.992
Step 1300, Minibatch Loss= 0.1331, Training Accuracy= 0.969
Step 1400, Minibatch Loss= 0.1279, Training Accuracy= 0.977
Step 1500, Minibatch Loss= 0.0733, Training Accuracy= 0.984
Step 1600, Minibatch Loss= 0.1529, Training Accuracy= 0.969
Step 1700, Minibatch Loss= 0.1223, Training Accuracy= 0.977
Step 1800, Minibatch Loss= 0.0503, Training Accuracy= 0.992
Step 1900, Minibatch Loss= 0.1077, Training Accuracy= 0.977
Step 2000, Minibatch Loss= 0.0344, Training Accuracy= 0.992
Optimization Finished!

参考博客:
https://www.leiphone.com/news/201711/zV7yM5W1dFrzs8W5.html
http://d0evi1.com/tensorflow/datasets/

TensorFlow-GPU安装测试

第6章 安装TensorFlow

这里主要介绍基于Linux下的TensorFlow安装,TensorFlow的安装又分为CPU版和GPU版的。使用CPU的相对简单一些,无需安装GPU相关驱动及CUDA、cuDNN等。不过无论哪种安装,我们推荐使用Anaconda作为Python环境,因为这样可以避免大量的兼容性问题,而且使用其中的conda进行后续程序更新非常方便。这里使用Python3.6,TensorFlow为1.4。

6.1 TensorFlow CPU版的安装

TensorFlow的CPU版安装比较简单,可以利用编译好的版本或使用源码安装,推荐使用编译好的进行安装,如果用户环境比较特殊,如gcc高于6版本或不支持编译好的版本,才推荐采用源码安装。采用编译好的进行安装,具体步骤如下:
(1)从Anaconda的官网(https://www.anaconda.com/)下载Anaconda3的最新版本,如Anaconda3-5.0.1-Linux-x86_64.sh,建议使用3系列,3系列代表未来发展。 另外,下载时根据自己环境,选择操作系统、对应版本64位版本。
(2)在Anaconda所在目录,执行如下命令:

bash Anaconda3-5.0.1-Linux-x86_64.sh

(3)接下来根据会看到安装提示,直接按回车即可,然后,会提示选择安装路径,如果没有特殊要求,可以按回车使用默认路径,然后就开始安装。
(4)安装完成后,程序提示我们是否把anaconda3的binary路径加入到当前用户的.bashrc配置文件中,建议添加,添加以后,就可以使用python、ipython命令时自动使用Anaconda3的python3.6环境。
(5)使用conda进行安装

conda install tensorflow

(6)验证安装是否成功

6.2 TensorFlow GPU版的安装

TensorFlow的GPU版本安装相对步骤更多一些,这里采用一种比较简洁的方法。目前TensorFlow对CUDA支持比较好,所以要安装GPU版本的首先需要一块或多块GPU显卡,显卡一般采用NVIDIA显卡,AMD的显卡只能使用实验性支持的OpenCL,效果不是很好。
接下来我们需要安装:
 显卡驱动
 CUDA
 cuDNN
其中CUDA(Compute Unified Device Architecture),是英伟达公司推出的一种基于新的并行编程模型和指令集架构的通用计算架构,它能利用英伟达GPU的并行计算引擎,比CPU更高效的解决许多复杂计算任务。NVIDIA cuDNN是用于深度神经网络的GPU加速库。它强调性能、易用性和低内存开销。NVIDIA cuDNN可以集成到更高级别的机器学习框架中,其插入式设计可以让开发人员专注于设计和实现神经网络模型,而不是调整性能,同时还可以在GPU上实现高性能现代并行计算,目前大部分深度学习框架使用cuDNN来驱动GPU计算。以下为在ubuntu16.04版本上安装TensorFlow1.4版本的具体步骤。
(1)首先安装显卡驱动,首先看显卡信息。

lspci | grep -i vga

(2)查是否已安装驱动

lsmod | grep -i nvidia

(3)更新apt-get

sudo apt-get update

(4)安装一些依赖库

sudo apt-get install openjdk-8-jdk git python-dev python3-dev python-numpy python3-numpy build-essential python-pip python3-pip python-virtualenv swig python-wheel libcurl3-dev

(5)安装 nvidia 驱动

curl -O http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/cuda-repo-ubuntu1604_8.0.61-1_amd64.deb
sudo dpkg -i ./cuda-repo-ubuntu1604_8.0.61-1_amd64.deb
sudo apt-get update
sudo apt-get install cuda -y

(6)检查驱动安装是否成功

nvidia-smi

运行结果如下

说明驱动安装成功。

(7)安装cuda toolkit(在提示是否安装驱动时,选择 n,即不安装)

(8)安装cudnn

(9)把以下内容添加到~/.bashrc

export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/cuda/lib64:/usr/local/cuda/extras/CUPTI/lib64"
export CUDA_HOME=/usr/local/cuda

(10)使环境变量立即生效

source ~/.bashrc

(11)安装Anaconda
先从Anaconda官网下载最新版本,然后,在文件所在目录运行如下命令。

bash Anaconda3-5.0.1-Linux-x86_64.sh

Anaconda的详细安装可参考TensorFlow CPU版本的说明。
(12)使环境变量立即生效

source ~/.bashrc

(13)创建 conda 环境为安装tensorflow

conda create -n tensorflow
# press y a few times

(14)激活环境

source activate tensorflow

(15)安装tensorflow -GPU

pip install tensorflow-gpu

(16)验证安装是否成功

import tensorflow as tf

hello = tf.constant('Hello, TensorFlow!')
sess = tf.Session()
print(sess.run(hello))

6.3远程访问Jupyter Notebook

(1)生成配置文件

jupyter notebook --generate-config

(2)生成当前用户登录jupyter密码
打开ipython, 创建一个密文密码

In [1]: from notebook.auth import passwd
In [2]: passwd()
Enter password:
Verify password:

(3)修改配置文件

vim ~/.jupyter/jupyter_notebook_config.py

进行如下修改:

c.NotebookApp.ip='*' # 就是设置所有ip皆可访问
c.NotebookApp.password = u'sha:ce...刚才复制的那个密文'
c.NotebookApp.open_browser = False # 禁止自动打开浏览器
c.NotebookApp.port =8888 #这是缺省端口,也可指定其它端口

(4)启动jupyter notebook

#后台启动jupyter:不记日志:
nohup jupyter notebook >/dev/null 2>&1 &

然在浏览器上,输入IP:port,即可看到如下类似界面。

然后,我们就可以在浏览器进行开发调试Python或Tensorflow程序了。

6.4比较CPU与GPU 性能

(1)设备设为GPU

2.50004e+11

Shape: (10000, 10000) Device: /gpu:0
Time taken: 0:00:02.605461
(2)把设备改为CPU
把设备改为cpu,运行以上代码,其结果如下:
2.50199e+11

Shape: (10000, 10000) Device: /cpu:0
Time taken: 0:00:07.232871

这个实例运算较简单,但即使简单,GPU也是CPU的近3倍。GPU还是非常不错的。

6.5单GPU与多GPU 性能比较

这里比较使用一个GPU与使用多个(如两个)GPU的性能比较。