【luxuriance19】TF学习笔记总结-week6暂时完结,不定期回补

人工智能
数据科学
机器学习

#1

1、 监督学习,非监督学习,应用场景:

监督学习:

数据集的目标值有标注,也就是说输入数据和输出数据之间有对应的关系。数据集的关系是(x,y)。监督学习包括回归,分类。

监督学习的算法以及相关的应用场景:

高斯朴素贝叶斯(一般用于分类):

  • 优点:可以处理连续数据,当数据符合高斯分布时,模型的效果好。当训练样本少或者是分布已知的情况下,能够学习联合分布的概率,来获得不错的预估。
  • 缺点:由于高斯朴素贝叶斯使用的是概率分布估计的方法,所以不是和大数据集,容易发生underfitting。所以,在数据集大或者是分布并不符合高斯分布情况下,表现不好。

线性判别分析(分类):降维的一种方法
降低数据的维度,通过把高维样本映射到低维空间中。如果分为两类可以投射到一维。

  • 优点:以标签,类别衡量差异性的有监督的降维方式,相对于PCA的模糊性,目地更加明确,更能反映样本间的差异。
  • 缺点: 局限性大,受样本种类限制,投影空间的维度最多为样本数量N-1维。

决策树:

  • 优点:模型容易解释。
  • 缺点:不支持在线学习,针对新样本进入需要重新学习,容易过拟合。

KNN:

  • 优点: 不需要进行参数估计,适合多分类的问题,在多分类问题有时表现比SVM(更适合二分类)好。
  • 缺点: 计算量大,需要计算预测样本与所有训练样本的距离。在有类的偏斜的情况下,容易出错。

Adaboost:

  • 优点:能改变错误的权重,所以针对样本有调节能力,因而精度比较高。通过弱分类器来构建分类模型,通过弱回归模型来构建回归模型。不容易发生过拟合。
  • 缺点: 对异常样本敏感, 异常样本在迭代中可能会获得较高的权重,影响最终预测的准确性。

SVM:

  • 优点:由于可以使用核函数进行空间变换,所以在解决小样本、非线性以及高维模式识别有很多优势。而且SVM的泛化误差和支持向量数量有关,和转变的维度没有关系,所以,泛化能力比较好。
  • 缺点:计算复杂度比较高, 多分类问题准确性不够高。

Logistic Regression

  • 优点:计算量小,易于实现,可以采用随机梯度下降实现。
  • 缺点:当特征空间较大,性能不佳;容易产生欠拟合,线性模型的准确度不够高。一般处理近似线性可分的问题。

神经网络:

  • 优点: 当数据的数量足够大,学习模型的准确性会比较高。
  • 缺点: 由于学习参数多,所以容易过拟合。计算量大。

有监督学习的学习步骤: 1、决定输入的数据范例的形态,收集训练资料。针对这个训练资料设计相关的输入特征。
2、决定模型,也就是线性模型或者是非线性模型,模型策略,也就是学习的损失函数,以及模型的优化算法,例如随机梯度下降或者是Adam。
3、模型实现,针对现有的模型调节参数,达到最优的学习的效果。

应用场景:手写文字识别、声音处理、图像处理、垃圾邮件分类与拦截、网页检索、基因诊断及股票预测等。
典型任务:预测数值型数据的回归、预测分类标签的分类、预测顺序的排序。

无监督学习:

目的是对原始资料进行分类,了解资料的内部结构。也就是计算机在互联网中自动收集信息,并从中获得有用的信息。 无监督学习不仅仅局限于解决像监督学习一样有明确答案的问题。

相关算法:

异常检测:

  • 局部异常因子: 遵循预先制定的规则(偏离大部分正常值的数据认为是异常值),寻找异常值的无监督的异常检测算法。利用了KNN的算法类似,寻找第K近的值
    :距离x第k近的样本。
    局部可达密度:
    局部异常因子:

LOF值大,x就会被看为异常。

  • 支持向量机的异常检测:
  • 基于密度比的异常检测: KL散度法检测。

无监督降维:

  • PCA
  • 局部投影保持(Locality Preserving Projections) 将核函数利用到LPP当中,就是laplace特征映射。

聚类:

  • KMeans: k值的选择困难,因为目标类别不定
  • 谱聚类。

应用场景: 人造卫星故障诊断、视频分析、社交网站解析和声音信号解析,数据可视化等。
典型任务:聚类、异常检测。

半监督学习:

利用少量的标注样本和大量的标注样本进行机器学习,从概率学习角度理解为利用训练样本的输入边缘概率P(x)和条件输出概率P(y|x)的联系设计具有良好性能的分类器。这种联系的存在是建立在假设的基础上的,如聚类假设和流形假设。

算法:

transductive SVM(与inductive SVM相对,严格来说并不是半监督学习方法):

  • 强调一点,根据Vapnik的Statistical Learning Theory中提出统计学习中考虑两种不同类型的推理:归纳推理(Inductive inference)和转导推理(Transductive inference).转导推理的目的是估计某一未知预测函数在给定兴趣点上的值(而不是在该函数的全部定义域上的值).关键是,通过求解要求较低的问题,可以得到更精确的解.
  • 传统的推理方法是归纳-演绎方法,人们首先根据用已有的信息定义一个一般规则,然后用这个规则来推断所需要的答案.也就是说,首先从特殊到一般,然后从一般到特殊.但是在转导模式中,我们进行直接的从特殊到特殊的推理,避免了推理中的不适定部分.
  • inductive 是用训练和测试的数据来训练w,并把w用到测试集里面,也就是说总结出来一般的规律,然后将这种一般的规律用到特殊的情况(也就是新的范式当中)。Inductive 是用label points和unlabeled points一起来训练分类器的。
  • transductive 是先学习训练护具,然后用没有标注的数据来做test数据,也就是先用train数据训练出来一个classifier,然后用于test set, 如果test set靠近训练数据集,那么这个测试住居就会被标记为测试集的标注。
    image
    如上图所示,有颜色的是训练集,数目量小,可以通过KNN来将其他点标记为某个种类, transductive只是用于标记没有标记的数据,并没有很好的预测作用。

算法流程(transductive):

  • 首先用traning set来训练SVM
  • 调整参数C(如果C = 0,那么忽略没有标注的数据),最开始让C值很小。
  • 基于分类预测,为某些未标记的数据添加标签。
  • 缓慢增加C(松弛变量)
  • 继续标记没有标签的数据,并且重新训练这个分类器。(C应该是对模型可以误分类的约束。)
  • 优点: 只需要少量的测试集就可以训练模型;训练的次数快,计算量空间占有率低。
  • 缺点: 不能称为预测模型,只能呢个网对测试集进行分类, 因为没有训练出一个参数,所以每次有新的测试集进来,需要重新计算。

co-training(协同训练):在训练条件独立的情况下分别训练训练器,然后这些训练器对最终结果进行投票。但是协同训练的基本前提是:训练集是需要在Y的情况下独立,也就是conditional independence。 基于这个原因有新的算法: Co-random forest

  • 主要算法步骤:

输入:有标记的训练集合L, 没有标记的训练集合U: 过程:在数据集U中随机抽取u个样本放入到创建的当中:
循环K次:
利用L训练一个分类器 ,只考虑x中 的比例
利用L训练一个分类器 ,只考虑x中 的比例
标记p个正样本和n个负样本
标记p个正样本和n个负样本
将这些自己标记好的样本添加到L当中
随机从U中抽取2p+2n个样本放到

* 其中<img src="http://chart.googleapis.com/chart?cht=tx&chl=\ x_{1}" style="border:none;">  ,<img src="http://chart.googleapis.com/chart?cht=tx&chl=\ x_{2}" style="border:none;">  是训练集中的两个视图,也就是对训练集从两个不同的角度对其进行分类。这个方法要求就是这两个分类器的差异性。  

得到最终的分类器,然后对最终样本进行分类。

co-forest:不容与Tri-Training,这个利用随机森林来保证各分类之间的差异性。随机森林是一个若干分类决策树的组合。它采用Bagging方法产生各异的训练集同时使用分类树作为元分类器。

Co-forest: 分类器: (1) 训练n颗CART树,并且调整参数到最优。 (2) 对于每棵树:
加入unlabel的数据,选出在置信度区间theta里面数据并且加进树里面。
不断加入unlabel 数据,终止条件是这一次的迭代错误率大于上一次的就终止。
(3)把放进unlabeldata 的数据重新再生成random forest。

Label Propogation:基于图论的方法。利用所有的数据建立一个图,然后利用随机游走的理论来给label data附近的点一个初始的概率,用最小切分(min-cut)方法判断这个点是否属于label附近的点。类似于KNN的方法。

应用场景:只有少量的标签数据和大量的为标记的数据的情况下。

强化学习:

强化学习是针对没有标注数据集的情况而言的,但是在学习的过程当中能够从周围的环境中获取信息。

应用场景: 在机器人的自动控制、计算机游戏中的人工智能、市场战略的最优化等方面都有广泛的应用。 应用的算法: 回归,分类,聚类和降维。

2、做机器学习项目,有哪些环节?

1、针对问题的目的,做特征工程;
2、选择合适的模型,以及模型的策略,也就是损失函数;
3、模型的训练与评估;
4、模型的应用。
(数据准备,模型建立,测试与评估以及应用部署)。

3、深度学习,目前有哪些应用领域:

图像识别,自然语言处理,语音识别,以及预测建模这方面都有应用。

4、数据预处理,需要注意的地方:

1、特征数据的有偏性,一般可以通过box-cox转换;在分类问题中,最终的样本是否有偏。
2、特征的共线性,也就是相关性。
3、数据中的异常值。
4、数据缺失值的处理。
5、是否需要对数据做离散化的处理。
6、特征向量的输入形式。
7、特征维度的处理。

5、 tensorflow运行原理当中,架构有哪些核心点:

1、 graph:用图的连接,来表示数据的传递以及数据的计算。
2、 tensor:利用tensor来表达任何的数据。
3、 variable:通过变量来维护状态,通过placeholder可以为表量保留位置。
4、 session: 在session执行之前,所有的变量都只是operation而不是一个变量值。 所以图的计算需要在session中运行。在运行图之前,必须对所有的变量进行初始化。
5、 summary:通过这个功能来对模型进行记录。
6、 estimator: 定义需要训练的模型的结构。

6、学习过程中的知识点和收获记录:

重新查看了半监督学习的概念,一直没有动手时间,理论知识无法获得很好的补充。实现了简单的tensorflow的计算。明天需要亲自动手实现一个tensorflow的小程序。


import tensorflow as tf
x = tf.constant([2])
y = tf.constant([3])
z = tf.constant([7])
A = tf.constant([[3.0,3.0]])
B = tf.constant([[2.0],[2.0]])

sess = tf.Session()

res = sess.run(x*y+z)

product = sess.run(tf.matmul(A,B))

sess.close()

print(res)
print(product)

疑问,关于week3的LeNet实现。
#2

这个图片没有显示出来 :wink:


#3

期待你的新模型 :grin:


#4

谢谢,图片已经修改好了,但是不知道为什么公式修改预览可以用,发布之后就看不到了。


#5

1、back propagation算法原理理解?

BP算法有两个计算过程,前向计算过程和后向传播的过程。
算法流程:
1、 初始化每一层网络的权重变量,将样本内误差Ein=0和每层的梯度置为0。
2、for 每个样本(xn,yn),n = 1,…,N,做:
计算每层的X(前向过程)。 计算每一层的delta(反向传播过程)。
将这个样本的误差加入样本误差Ein。
for 每层,计算:
梯度更新值。
更新梯度。

BP算法的核心在与梯度的计算的过程,前向的过程当中,只需要按照初始化的权重来计算最后网络层的输出。然后再根据网络层输出的误差进行针对每一层的权重进行求导,根据 求得的导数更新权重的值。在这里,每一层的delta函数,例如第l层,也就是误差函数对l层的激活函数的导数(这里包含了多层的求导的递推过程)。 反向传播算法的一大优点是可以通过图来计算。详细参见Computing Gradient。

sigmoid函数、tanh函数、ReLU函数的区别?各自的优缺点?对应的tf函数是?

这三个函数都是激活函数,是为了引入非线性的因素,主要原因是因为线性模型的表达能力不够。

从PLA模型可知,在二维平面当中,只能够完全区分三个点的情况,但是二维平面取值可以有四组,即线性情况下无法可可分。
实例: XOR模型 输入和输出的关系 [0,0],0; [0,1],1;[1,0],1;[1,1],0。根据这个问题,以最小化MSE为训练目标训练一个线性模型,出来的结果是y=0.5 所以我们需要一个非线性模型来转化这些特征 这就是为什么引入被称为激活函数的固定非线性函数来实现这个目标, 经过放射变换以后在进行非线性变换。 上面这个例子当中,可以先根据z = XW+C, W为2×2的矩阵,元素都为1, c=[0,-1]为2x1的列变量,然后经过max{0,z}的激活函数(非线性变化)最后得到数据为 [0,0],0; [0,1],1;[1,0],1;[2,1],0, 这个时候就可以根据异或进行运算了。

通俗的理解:
如果在神经网络当中没有非线性的激活函数,实际上神经网络每一层的变化都可以看成是一个放射变化,那么不管经过多少层神经网络的变化,输出都是输入的线性组合, 实际上这还是PLA实现的功能,也就是无法分开大于d+1维的数量的数据。 为了让数据可分,引入了非线性变化,然数据进行非线性的映射直至最终可以完全分开。

激活函数的特点:

  • 非线性: 当激活函数是非线性的时候,两层的神经网络能够包含的目标集合就已经基本上是所有的函数了。但是如果不加激活函数,那么两层实际上还是线性变换,MLP实际上也就相当于单层的射精网络。
  • 可微性: 根据神经网络的BP算法,可知神经网络需要通过梯度求导来求解最优的算法。所以只要是最终的优化方法是根据梯度求解的时候,这个性质是激活函数必须的。
  • 单调性: 当激活函数是单调的时候,单层的神经网络可以保证是凸函数。(这个解释有点问题,需要进一步求证,这个条件不一定需要满足)
  • f(x) 約等于 x: 当激活函数满足这个细腻改制的时候,如果参数的初始化是随机设置很小的值,神经网络的训练会比较高校,如果不满足这个性质,就需要用心设置。
  • 输出值的方位: 当激活函数的输出值是有限的时候,基于梯度的优化方法会更加稳定,因为特征的表示受有限全值的影响;当激活函数输出是无限的时候,模型的训练会更加的高效,不会出现梯度消失的情况,不过此时对learning_rate的设计需要更加小心(需要更小的值).

激活函数: ReLU(Recitified linear unit): max(0,z) image (整流线性单元 max(0,z)):

  • 优点: * 易于优化,只要整流处于激活状态,导数都保持比较大并且一致,易于训练学习。相较于tanh和sigmoid对于随机梯度下降的收敛有巨大的加速作用。 因为是由于它的非线性, 非饱和的公式。 * sigmoid和tanh需要计算指数等,计算复杂度高,ReLU只需要一个阈值就可以得到激活值。计算量比较小。
  • 缺点: 不能通过基于梯度学习的方法学习那些使他们激活函数变为0的样本。 image
    (详细版本)ReLU 在训练的时候很”脆弱”,容易导致神经元”坏死”。举个例子:由于 ReLU 在 x<0 时梯度为 0,这样就导致负的梯度在这个 ReLU 被置零,而且这个神经元有可能再也不会被任何数据激活。实际操作中,如果你的学习率 很大,那么很有可能你网络中的40%的神经元都坏死了。 当然,如果你设置了一个合适的较小的学习率,这个问题发生的情况其实也不会太频繁。

tf.nn.relu(features, name=None)

基于这个原因下面有三个线性整流单元的扩展 max(0,z) + alpha*min(0,z) 如果alpha=-1就是 绝对值整流(absolute value rectification) , 对图像中的对象识别,也就是寻找在输入照明极性反转下不变的特征有意义。 alpha=0.01这类的比较小的值是 渗漏整流线性单元(Leaky ReLU) 。 将alpha作为一个学习参数是 参数化整流线性单元(parametric ReLU)
Leaky ReLU
是为解决“ReLU死亡”问题的尝试。ReLU中当x<0时,函数值为0。而Leaky ReLU则是给出一个很小的负数梯度值,比如0.01。所以其函数公式为f(x)=1(x<0)(\alpha x)+1(x>=0)(x)其中\alpha是一个小的常量。有些研究者的论文指出这个激活函数表现很不错,但是其效果并不是很稳定。Kaiming He等人在2015年发布的论文Delving Deep into Rectifiers中介绍了一种新方法PReLU,把负区间上的斜率当做每个神经元中的一个参数。然而该激活函数在在不同任务中均有益处的一致性并没有特别清晰。

整流线性一个特别的扩展(可以自己训练的激活函数)根据选择的k组值来训练激活函数
maxout单元 将z分为魅族具有k个值的组,输出每组中最大的元素。 image

  • 优点: 因为可以学习多达k段的分段线性的凸函数,所以可以视为学习激活函数本身而不仅仅是短圆之间的关系,使用足够大的k,可以通过任意的精确度来近似任何凸函数。具有两块地maxout层可以学习实现和传统层相同地输入x的函数。
  • 缺点: 因为需要k个权重向量参数化,所以需要比整流线性单元更多的正则化。如果训练集很大,每个单元的块数保持比较低,那么可以再没有正则的情况下工作不错。

tf.contrib.layers.maxout(inputs, num_units,axis=-1,scope=None) image ** Maxout** (需加深理解) Maxout 是对 ReLU 和 Leaky ReLU 的一般化归纳,它的函数是:max(w1Tx+b1,w2Tx+b2)。Maxout 神经元就拥有 ReLU 单元的所有优点(线性操作和不饱和),而没有它的缺点(死亡的 ReLU 单元)。Maxout 的拟合能力是非常强的,它可以拟合任意的的凸函数。缺点是参数 Double 了,也就是参数加倍了。
Maxout是通过分组,然后选择每组中的最大值输出,每个组有多少个元素是事先定义的,也就是上面说的K个值的参数。 每个组里面的K个值都是通过wx+b线性训练得出的,maxout实际上就是多段线性函数迭加,所以可以实现任何的ReLU的功能。(详细查看Tips of DNN) image

重点介绍: 渗漏整流线性单元(Leaky ReLU) Leaky ReLU 是为解决 “ReLU死亡” 问题的尝试。ReLU 中当x<0 时,函数值为0。而="" leaky="" relu="" 则是给出一个很小的负数梯度值,比如="" 0.01。这样,既修正了数据分布,又保留了一些负轴的值,使得负轴信息不会全部丢失。="" 公式:f(x)=“α"x(x=”"<="“0),=”" f(x)=“x(x”>=0),α 是一个小常数。 扩展:* PReLU. 对于 Leaky ReLU 中的 α,通常都是通过先验知识人工赋值的。Parametric ReLU 是将 α 作为参数训练,取得的效果还不错。 * Randomized Leaky ReLU. 其是 Leaky ReLU 的 random 版本,在训练过程中,α 是从一个高斯分布中随机出来的,然后再在测试过程中进行修正。

tf.nn.leaky_relu(features,alpha=0.2, name=None)

sigmoid函数:

  • 优点: 输入任意实属值,将其变换到(0,1)区间,大的负数映射为0,大的正数映射为1。
  • 缺点:
    • sigmoid函数存在饱和区间,所以容易出现梯度消失的问题。 当输入非常大或者非常小的时候, 根据sigmoid函数的图片可以看出,神经元的梯度是接近0的,这 样权重基本上不会更新。 所以如果初始化权重过大, 那么大多数神经元将会饱和,导致神经网络几乎不学习。 所以不鼓励sigmoid作为前馈神经网络中的隐藏单元, 在循环网络,许多概率模型以及一些自编码器当中,因为不能够使用分段线性激活函数,所以sigmoid的使用比较常见,尽管它存在饱和的问题
    • sigmoid的输出不是0均值的, 这回导致后层的神经元的输入不是0均值的信号,这会对后面梯度的计算产生影响。 例如:假设后面的神经元的输入都是正值, 那么对权值w的局部求导就都是正值,那么反向传播的过程当中,w都会朝着一个方向更新,也就是说要么正向更新,要么负向更新,使得收敛比较缓慢。 (在这里,由于针对权值求导得到的导数是x,所以如果说前面的输入都是经过sigmoid的,那么神经网络当中的隐含层输入就在(0,1)之间,那么对梯度的方向不会改变,所以根据chain-rule,关于w的梯度在反向传播的过程中,要么全部都是正的,要么全部都是负的(具体按照整个表达式f决定)(这里说的要么都是正的要么都是负的,个人理解是不同层之间权重的梯度,因为后面的输入都只能够在(0,1)之间)。这样的结果就会让梯度下降权重更新时出现z字下降。但是如果采用批量梯度下降,对于权重的最终更新就会有不同的正负,这样,就从一定程度上面减轻了这个问题。所以这个问题相对与上面的神经元饱和的问题来说只是小麻烦,没有那么严重)
    • 幂计算相对而言比较耗时。

tf函数: y = 1/(1+exp(-x)) tf.sigmoid(x,name=None) /tf.nn.sigmoid

tanh函数(双曲正切函数 hyperbolic tangent): image

  • 优点: 解决了sigmoid输出不是零均值的问题。
  • 缺点: 仍然具有饱和性问题。

tf函数: y = 1/(1+exp(-x)) tf.sigmoid(x,name=None) /tf.nn.sigmoid

ELU
f(x) = x (x>0); f(x) =alpha*(exp(x)-1) (x<=0)

  • 优点: 所有的ReLU的优点;接近与0尽脑汁的输出, 相对与Leaky ReLU的负饱和区间增加了对噪声样本的稳健性。
  • 缺点: 幂计算的复杂性。

SELU
将输入自动normalization到均值为零方差为1。

  • 优点:不同的特征的维度的尺度都是一样的,这样在梯度计算的时候,不会出现不同维度特征差别大导致梯度忽大忽小出现zig-zag的情况(出现这种情况的化,收敛会比较慢),所以会比较容易训练。也改进了会出现梯度消失的问题。
  • 缺点:暂时未知,一般在SELU的输入前面会归一化处理。 selu(x) = lambda*x (x>0)
    selu(x) = lambda*(alpha*exp(x) - alpha) (x<=0)
    lambda = 1.050700; alpha = 1.67326

tf.nn.selu(features, name=None)

<转自 https://zhuanlan.zhihu.com/p/21741716> 一句话:“那么该用那种呢?”用ReLU非线性函数。注意设置好学习率,或许可以监控你的网络中死亡的神经元占的比例。如果单元死亡问题困扰你,就试试Leaky ReLU或者Maxout,不要再用sigmoid了。也可以试试tanh,但是其效果应该不如ReLU或者Maxout。

softmax和cross_entropy原理解释?

  • softmax: 在LR二分类问题当中,可以利用sigmoid函数将输入仿射空间映射到(0,1)的范围区间,从而得到预测类别的概率。将这个问题推广到多分类的问题当中,就可以使用softmax函数。对输出的值进行归一化处理。

假设模型在softamx前面已经已经输出一个C维的向量,也就是预测的类别为C,如果输出层是全连接层,定义输出为a1,a2,…,ac。
针对每个样本,它属于类别i的概率为:
yi = exp(ai)/\sum_{k = 1}^{C} exp(ak)
这样的处理保证了所有输出的概率和为1,也就是每个类别的和为1。
对输出yi对aj求导:i = j 为yi(1-yi), i != j 为 yi*yi image

这个里面解决了为什么自己写的sigmoid_cross_entropy_with_logit的问题
因为在math.exp(x)有计算范围,当x的值过大或者过小的时候,输出都会是nan。
所以我们可以针对这个问题给ai加一个常数F。 exp(ai+F) = Eexp(ai); E = exp(F),针对每个ai都加上F,yi的输出值不会改变。
这个想法是想让所有的输入exp的值都在x的附近,所以F的取值可以是-max(a1,a2,…,aC)。这个做法的前提是假设所有的输入ai的值都比较接近,否则差异过大也只是变了符号而已。
为了避免numpy移除的问题,可以采用bigfloat; eg: bigfloat.exp(x), 避免了exp(x)计算的溢出。

  • cross_entropy

相对熵,相对熵也就是KL散度,用来衡量两个分布之间的距离: image
从上图当中可以看出,交叉熵和相对熵相差一个H§,而当p已知的时候,H§是个常数,所以交叉熵和相对熵在这里是等价的,反映了分布p和q之间的相似程度。 回到我们多分类的问题上,真实的类标签可以看作是分布,对某个样本属于哪个类别可以用One-hot的编码方式,是一个维度为C的向量,比如在5个类别的分类中,[0, 1, 0, 0, 0]表示该样本属于第二个类,其概率值为1。我们把真实的类标签分布记为p,该分布中,t_i = 1当i属于它的真实类别c。同时,分类模型经过softmax函数之后,也是一个概率分布,因为\sum_{i = 1}^{C}{y_i} = 1,所以我们把模型的输出的分布记为q,它也是一个维度为C的向量,如[0.1, 0.8, 0.05, 0.05, 0]。 对一个样本来说,真实类标签分布与模型预测的类标签分布可以用交叉熵来表示:

l_{CE} = -\sum_{i = 1}^{C}t_i log(y_i)

可以看出,该等式于上面对数似然函数的形式一样!

最终,对所有的样本,我们有以下loss function:

L = -\sum_{k = 1}^{n}\sum_{i = 1}^{C}t_{ki} log(y_{ki})

其中t_{ki}是样本k属于类别i的概率,y_{ki}是模型对样本k预测为属于类别i的概率。
image

tf.placeholder(),tf.constant(),tf.Variable()的区别?

  • tf.placeholder():

顾名思义,这个是占位符号,用来传递训练样本给模型。当数据的维度不确定的时候,可以用None来确定。tf.placeholder()不需要指定初始值,但是需要指定初始的类型。tf.placeholder()不会出现在计算图上面,所以这是模型在训练的时候必须要传递给sess.run()的feed_dict()的值。

  • tf.constant(value, dtype=None, shape=None, name=‘Const’, verify_shape=False):

定义常量,如果定义了传递value的形状,也就是shape参数,value的shape不能够超过shape的大小,如果说value的长度小于shape定义的参数,那么多余的由传递的value的最后一个值填充。

  • tf.Variable():

通过构造一个Variable类的实例在图中添加一个变量,主要作用在一些可训练的变量的定义上面,例如权重或者是截距偏置的值。 Variable类的构造函数要求一个variable的实例必须要有一个初始值,这个初始值可以是任意的形状和类型。这个变量的形状和类型由这个初始值确定。 变量的值可以被改变。

tf.Graph()概念理解?

因为神经网络大部分都是靠梯度来训练的,而BP算法的梯度计算可以通过图形来计算。
tf.Graph()实际上就是定义了一个计算的流程图。一个节点代表一个操作,一条边代表了一个张量。通过边的连接决定了这些操作的流程的走向。
通过这个图形的构建,可以定义最后变量的计算的走向,来优化需要优化的变量。

tf.name_scope和tf.variable_scope()的理解?

  • tf.name_scope(name,default_name=None,values=None) 是用户用来定义一个Python操作的内容管理器。这个内容管理器是用在图形内部的。在图形可视化的时候,同一个name_scope的操作会建立一个群组(group)来减少graph显示的复杂度。如果说在一个name_scope中定义同一个名字,那么tensorflow会自动补后缀。
  • tf.variable_scope()
    是为了定义多个操作创建变量层的一个内容管理器。
    variable scope允许我们创建一个新的变量并且提供检查的功能。变量的作用域让我们在创建和使用变量的时候控制变量的重用,并且还允许我们用分层的方式来给变量命名。
    比如我们如果想每层都创建权重值w和偏置b,这个时候,如果说我们写了一个函数,经过不同的输入我们想要调用两次,程序执行就会出错。
    因为这个函数在第一次执行的时候,就会创建这个函数里面的变量,在第二次调用函数的时候,程序会不知道我们是想要创建新的变量还是使用援用的变量。所以函数就会起冲突。 变量域就是在这个时候起作用:我们通过不同的变量域,然后调用这个函数。

     ## 1:write a function to create a convolutional/relu layer:'
     def conv_relu(input,kernel_shape,bias_shape):
         #Create variable named "weights"
         weights = tf.get_variable("weights", kernel_shape, 
                                   initializer=tf.random_normal_initializer())
         # Create variable named "biases"
         biases = tf.get_variable("biases", bias_shape,
                                 initializer=tf.constant_initializer(0.0))
         conv = tf.nn.conv2d(input,weights,strides=[1,1,1,1],padding='SAME')
         return tf.nn.relu(conv+biases)

     input1 = tf.random_normal([1,10,10,32])
     input2 = tf.random_normal([1,20,20,32])
     x = conv_relu(input1, kernel_shape=[5,5,32,32],bias_shape=[32])
     x = conv_relu(input2, kernel_shape=[5,5,32,32],bias_shape=[32])# This fails
     # 第二句fail的原因,因为weights,biases变量已经存在,程序不知道我们当前是想使用
     # 原有的变量还是重新建立一个新的变量,所有操作冲突,无法执行。

     # 但是通过不同的scopes来调用conv_relu,就会明确我们是想要创建新的变量,那么程序就会work
     # this is working when we call conv_relu in different scope
     def my_image_filter(input_images):
         with tf.variable_scope("conv1"):
             # Variables created here will be named "conv1/weights", "conv1/biases"
             relu1 = conv_relu(input_images, [5, 5, 32, 32], [32])
         with tf.variable_scope("conv2"):
             # Variables created here will be named "conv2/weights", "conv2/biases".
             return conv_relu(relu1, [5, 5, 32, 32], [32])

tf.variable_scope() 和tf.get_variable()的理解?

tf.get_variable()是创建变量最好的方式,这个函数要求你对创建的变量进行定义。这个名称将被其他副本用来访问同一个变量,以及检验和导出模型时命名这个变量的值。

利用tf.get_variable()的方式创建变量只需要提供变量的名称和形状就可以了。在定义变量的时候,可以利用initializer参数对其进行初始化。这也时变量初始化最好的方式。

tf.Variable()和tf.get_variable()异同: 相同点:都是定义一个变量。 不同点:tf.get_variable()通过变量名字来定义一个变量,创建变量的时候会由initializer的定义,可以直接初始化。tf.Variable()在定义的时候变量的名字可以不选择,通过变量的初始值来定义一个变量,返回一个initializer的操作。

tf.global_variables_initializer() 什么时候使用?

在变量使用之前,必须要被初始化。 所以在模型的训练之前,就需要通过tf.global_variable_initializer()来初始化变量,这个函数会返回一个操作来初始化所有的变量。但是这个操作会忽略变量之间的相关性。所以如果由变量的依赖关系的时候,最后利用变量的initial_value()来初始化所有变量,避免报错。

学习中知识点的收获:

softmax的求导第一次计算,感受到了其中的神奇。tensorflow的一些操作还是有一点疑问,需要从实践中获取。曾经翻译过的tensorflow的API也算是派上了一点用场,然而赶不上google的更新速度,年前还没有中文翻译,现在中文翻译只是tutorial没有了。自己要加油学习。

参考文献: https://blog.csdn.net/cyh_24/article/details/50593400


http://www.cnblogs.com/tornadomeet/p/3428843.html

实践:


# logistic regression implementation in tensorflow
# data reference https://archive.ics.uci.edu/ml/datasets/Adult
# task prediction task is to determine whether a person makes over 50K a year
# data can download from https://ntumlta.github.io/2017fall-ml-hw2/
# categorical variables:
# workclass:(9包含未知项) Private, Self-emp-not-inc, Self-emp-inc, Federal-gov, Local-gov, State-gov, Without-pay, Never-worked. 
# education:(16)[' 10th', ' 11th', ' 12th', ' 1st-4th', ' 5th-6th', ' 7th-8th',' 9th', ' Assoc-acdm', ' Assoc-voc', ' Bachelors', ' Doctorate',' HS-grad', ' Masters', ' Preschool', ' Prof-school',' Some-college']
# marital_status:(7)[' Divorced', ' Married-AF-spouse', ' Married-civ-spouse',' Married-spouse-absent', ' Never-married', ' Separated',' Widowed']
# occupation:(15包含未知项)[' ?', ' Adm-clerical', ' Armed-Forces', ' Craft-repair',' Exec-managerial', ' Farming-fishing', ' Handlers-cleaners',' Machine-op-inspct', ' Other-service', ' Priv-house-serv',' Prof-specialty', ' Protective-serv', ' Sales', ' Tech-support',' Transport-moving']
# relationship:(6)[' Husband', ' Not-in-family', ' Other-relative', ' Own-child',' Unmarried', ' Wife']
# race:(5)[' Amer-Indian-Eskimo', ' Asian-Pac-Islander', ' Black', ' Other', ' White']
# sex:(2)
# native_country:(42包含未知项)[' ?', ' Cambodia', ' Canada', ' China', ' Columbia', ' Cuba',' Dominican-Republic', ' Ecuador', ' El-Salvador', ' England',' France', ' Germany', ' Greece', ' Guatemala', ' Haiti',' Holand-Netherlands', ' Honduras', ' Hong', ' Hungary', ' India',' Iran', ' Ireland', ' Italy', ' Jamaica', ' Japan', ' Laos',' Mexico', ' Nicaragua', ' Outlying-US(Guam-USVI-etc)', ' Peru',' Philippines', ' Poland', ' Portugal', ' Puerto-Rico',' Scotland', ' South', ' Taiwan', ' Thailand', ' Trinadad&Tobago', ' United-States', ' Vietnam', ' Yugoslavia']
# 1,3,5,6,7,8,13 #不对sex做one-hot-encoding
# continuous:
# age,fnlwgt,education_num,capital_gain,capital_loss,hours_per_week //education_num与education一一对应

import tensorflow as tf
from sklearn.cross_validation import train_test_split
import pandas as pd
import numpy as np
import pickle
import matplotlib.pyplot as plt

train = pd.read_csv("./UCI/train.csv")
print(train.columns,test.shape)
test = pd.read_csv("./UCI/test.csv")
print(test.columns,test.shape)

X_train = train[train.columns[:14]]
y_train = train[["income"]]
X_test = test

y_test = pd.read_csv("UCI/correct_answer.csv")

y_test = y_test[['label']].values

y_test.shape

# 对分类类别进行编码
def labelEncoder(mapColumn,data):
    mapping_digits = {label: idx for idx, label in enumerate(np.unique(data[mapColumn]))}
    data[mapColumn] = data[mapColumn].map(mapping_digits)

for columnName in ["workclass","education","marital_status","occupation","relationship","race","sex","native_country"]:
    labelEncoder(columnName,X_train)

labelEncoder("income",y_train)

for columnName in ["workclass","education","marital_status","occupation","relationship","race","sex","native_country"]:
    labelEncoder(columnName,X_test)

# transformed.education_num
# X_train.education_num
np.unique(X_train.education_num).shape
X_train[['education_num','education']].groupby(['education_num','education']).count()
educations = X_train[['education_num','education']].copy()
educations['count'] = 1
educations
educations.pivot_table(values = 'count', index = ['education_num','education'],aggfunc = np.sum)

#由上面分析可知 education与education_num是完全相关的,所以drop education_num这个变量

X_train.drop(['education_num'],axis = 1, inplace=True)
X_test.drop(['education_num'],axis = 1, inplace=True)

from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder

## 重新对上面的数据进行one_hot_encoding:
enc = OneHotEncoder(categorical_features=np.array([False,True,False,True,True,True,True,True,False,False,False,False,True]))
enc.fit(X_train)
X_train_one_hot = enc.transform(X_train).toarray()
X_test_one_hot = enc.transform(X_test).toarray()
y_train = y_train.values

def _shuffle(X,y):
    random_ind = np.arange(X.shape[0]) #或者是len(X)
    np.random.shuffle(random_ind)
    return (X[random_ind],y[random_ind])

# 这里利用了train的数据的scale的参数对test数据做调整,将他们一起归一化会有data snooping的风险
def _normalization(X_train, X_test):
    n_samples = len(X_train)
    mu = sum(X_train)/n_samples 
    sigma = np.std(X_train,axis=0)
    mu_train = np.tile(mu,(n_samples ,1))
    sigma_train = np.tile(sigma,(n_samples ,1))
    mu_test = np.tile(mu,(len(X_test),1))
    sigma_test = np.tile(sigma,(len(X_test),1))
    X_train_normed = (X_train-mu_train)/sigma_train
    
    X_test_normed = (X_test-mu_test)/sigma_test
    
    return X_train_normed,X_test_normed

def _train_valid_split(X,y,percentage):
    n_samples = len(X)
    train_samples = percentage*n_samples
    
    X,y = _shuffle(X,y)
    
    X_train,y_train = X[0:train_samples],y[0:train_samples]
    X_valid,y_valid = X[train_samples:],y[train_samples:]
    return X_train, y_train, X_valid, y_valid

X_train_one_hot_normed,X_test_one_hot_normed = _normalization(X_train_one_hot, X_test_one_hot)

# Tensorflow Implementation
# tf.Graph 输入
# 给输入的分区不指定样本的数量,便于调整batch normalization的值
# 输入的数据是float类型
n_samples = len(X_train_one_hot_normed)
n_features = X_train_one_hot_normed.shape[1]
x = tf.placeholder(tf.float32 ,[None, n_features])
y = tf.placeholder(tf.float32 ,[None, 1]) #在这里因为是二分类问题就没有定义多个输出

# 模型待估计参数
W = tf.Variable(tf.zeros([n_features,1]))
b = tf.Variable(tf.zeros([1]))

## hyperparameter
learning_rate = 0.02
train_epochs = 40
batch_size = 100
display_step = 1

# 定义模型的输出
pred = tf.matmul(x, W) + b

# 定义模型的损失函数:逻辑回归采用交叉熵
cost = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits = pred,labels = y))
# 这里的损失函数不能够直接按照第二个自己定义的,可能原因是log(1+x),当x很小的时候可能返回的参数是零,
# 在tensorflow的实现当中使用了log1p(),这个函数在numpy中的精度比直接计算log(1+x)要高(可能原因)
cost1 = tf.reduce_mean(-y*tf.log(tf.nn.sigmoid(pred))-(1-y)*tf.log(1-tf.nn.sigmoid(pred)))

# 梯度下降
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)

# 初始化参数
# 注意在计算开始之前必须初始化参数,而且注意当参数初始化有执行顺序的时候,需要自己的定义初始化过程
# init = tf.global_varibales_initializer()(这个方法已经被废弃的)
# init = tf.initializers.global_variables()
init = tf.initialize_all_variables()

# 定义模型的准确性
# 如果说输出是多个节点,那么用tf.argmax(input, axis=None),这里采用tf.argmax(input,axis=1)
predLabel = tf.round(tf.sigmoid(pred))
correct = tf.equal(predLabel, y)
accuracy = tf.reduce_mean(tf.cast(correct, dtype=tf.float32))

# 模型训练
# 注意会话窗口的正常关闭,如果不采用以下上下文结构的化,否则程序会有溢出的报错
# 在tensorflow的结构下面,如果会话没有执行,基本上所有的函数返回的都只是操作,必须在会话中执行之后才会有返回值
with tf.Session() as sess:
    # 初始化参数
    sess.run(init)
    
    # 按照epoch训练,一个epoch就遍历所有的训练集
    for epoch in range(train_epochs):
        X_train_one_hot_normed, y_train = _shuffle(X_train_one_hot_normed,y_train)
        avg_cost = 0
        if n_samples % batch_size == 0:
            batches = n_samples//batch_size
        else:
            batches = n_samples//batch_size+1
        for i in range(batches):
            # sess.run()对应返回的是fetches的参数
            if (i+1)*batch_size <= n_samples:
                _, cos,cos1 = sess.run([optimizer, cost,cost1], 
                                  feed_dict ={x:X_train_one_hot_normed[i*batch_size:(i+1)*batch_size],
                                             y:y_train[i*batch_size:(i+1)*batch_size]})
            else:
                _, cos,cos1 = sess.run([optimizer, cost,cost1], 
                                  feed_dict ={x:X_train_one_hot_normed[i*batch_size:],
                                             y:y_train[i*batch_size:]})
            # 计算损失函数
            # print(cos,cos1) cos1经常无法计算
            avg_cost += cos/batches
        
        plt.plot(epoch+1, avg_cost, 'co')
        
        #每display_step时间显示日志
        if (epoch+1)%display_step == 0:
            print("Epoch:", "%04d"%(epoch+1), "cost=","{:.9f}".format(avg_cost))
            
    print("Optimization Finished")
    
    # 测试模型
    # eval()也是启动计算的一种方式,与session.run()有同样的作用,也就是说输入的是字典里面的参数,返回accuracy
    # accuracy.eval({})就是 sess.run(accuracy, feed_dict = {})
    print("Train Accuracy", accuracy.eval({x:X_train_one_hot_normed,y:y_train}))
    print("Test Accuracy", accuracy.eval({x:X_test_one_hot_normed,y:y_test}))
    plt.xlabel("Epoch")
    plt.ylabel("Cost")
    plt.show()


#6

Batch Normalzation原理解释和优点?

Batch normalization 是一种解决深度神经网络层数太多, 而没办法有效前向传递(forward propagate)的问题. 因为每一层的输出值都会有不同的 均值(mean) 和 方差(deviation), 所以输出数据的分布也不一样, 如下图, 从左到右是每一层的输入数据分布, 上排的没有 Batch normalization, 下排的有 Batch normalization.

** 数据标准化的原因:**
在神经网络中,数据分布对训练都会产生影响。(原因:在神经网络中存在激活函数这种非线性变化的部分)。利用tanh为例;当某个权重W的初始值为0.1,x=1,这样后层神经元的计算结果就是Wx=0.1;如果说x=20;那么Wx=2。 当经过激活函数的时候,输出的值接近为0.1和1。当输出为1的时候已经进入了神经元的饱和状态了,这个时候,激活函数的导数为0,根据chain-rule可知导数会为零。当x增大,tanh的激活函数的输出还是1,那这个时候这些大于20的数据对神经网络权重的调整已经没有效果了。也就是说这些样本对整个网络的训练已经失效了。 所以项通过normalization的处理,让x变化范围不会太大,让输入值经过激励函数的敏感的部分。

在神经网络的输入层和隐含层都会出现有上述的问题, 但是对于隐含层不能直接做normalization的处理,所以,我们采用batch normalization来处理这种情况。

batch normalization添加在激活函数前一层,也就是将一个训练的batch数据在前向传播的时候,对每层都进行normalization的处理,然后再传递到激励函数(在这个部分,暂时我看到的是说将BN添加到全连接层和激励函数之间;补充:论文中说BN可以用于CNN和全连接层,可以运用到网络中的任意激活层)。

** BN算法 ** 我们引入一些 batch normalization 的公式. 这三步就是我们在刚刚一直说的 normalization 工序, 但是公式的后面还有一个反向操作, 将 normalize 后的数据再扩展和平移. 原来这是为了让神经网络自己去学着使用和修改这个扩展参数 gamma, 和 平移参数 β, 这样神经网络就能自己慢慢琢磨出前面的 normalization 操作到底有没有起到优化的作用, 如果没有起到作用, 我就使用 gamma 和 beta 来抵消一些 normalization 的操作.这里gamma和beta是可以选择的值,所以网络可以根据自己的需要来恢复这个normalization的参数。 image 最后我们来看看一张神经网络训练到最后, 代表了每层输出值的结果的分布图. 这样我们就能一眼看出 Batch normalization 的功效啦. 让每一层的值在有效的范围内传递下去. image

** 论文摘要 **
BN可以让网络有更大的learning_rates(原因:避免了让训练数据陷在非线性变换的饱和区间)。BN会让参数增长能够稳定下来。

BN对模型有正则效果:这个点子由来与mini-batch的训练。模型的泛化能力会变好。

加速BN网络的方法:

  • 增大learning_rate,这个做法没有副作用。
  • 移除Dropout:因为BN与Dropout有同样的功效。在BN-Inception网络当中,移除Dropout训练更快,并且不会过拟合。
  • 减少二阶范数的正则: BN-Inception中权重减小为5.
  • 加速learning_rate的衰减,利用指数衰减。相交Inception训练速度快。
  • 移除local response normalization。
    (LRN局部归一化:对局部神经元的活性创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强模型的泛化能力 tf.nn.lrn。实质上是对模型做了平滑处理,让识别率提高了)。
    $$ b_{x,y}^{i} = a_{x,y}^{i}/(k+\alpha\sum_{j = max(0,i-n/2)}^{min(N-1,i+n/2)}(a_{x,y}^{j})^{2})^{\beta}$$ 这个公式当中,a表示第i个核在位置(x,y)运用ReLu等非线性神经元输出,n是同一位置上面临近的kernel map的数目,N是kernel的总数。 k = 2, n = 5, alpha=1e-4, beta=0.75,利用不同的核的平滑(在2015年也有提出LRN在大规模的图像识别中没有效果)
  • 在训练的过程中每次对训练的样本进行洗牌。
  • 减小图片的distortion(photometric distortion)。为了防止过拟合,一般我们会对数据进行增强处理,这个时候可能会对数据进行反转之类的操作,在这里,我们需要减少这种操作,让图片大部分保持为原来的状态。

下面的实验尝试的过程当中,BN对tanh的改善好于ReLU,但是这个数据集不代表所有数据。

** 优点 **

  1. 提高梯度在网络中的流动。Normalization能够使特征全部缩放到[0,1],这样在反向传播时候的梯度都是在1左右,避免了梯度消失现象。
  2. 提升学习速率。归一化后的数据能够快速的达到收敛。
  3. 减少模型训练对初始化的依赖。

参考文献:


https://arxiv.org/pdf/1502.03167.pdf(原始论文地址)
https://zhuanlan.zhihu.com/p/23906526



# BN实践

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

ACTIVATION = tf.nn.tanh #每一层的激活函数都使用relu
N_LAYERS = 7 #设置7层网络
N_HIDDEN_UNITS = 30 # 隐含层都设置为30

def plot_his(inputs, inputs_norm):
    # 唯美一层的输入画直方图
    for j, all_inputs in enumerate([inputs,inputs_norm]):
        for i, inPut in enumerate(all_inputs):
            # 这里的数据是网络的输入层和7层网络的输出,所以总共有8个子图
            plt.subplot(2, len(all_inputs), j*len(all_inputs)+(i+1))
            plt.cla()
            if i == 0:
                the_range = (-7,10)
            else:
                the_range = (-1,1)
            plt.hist(inPut.ravel(), bins=15, range = the_range, color = 'lightblue')
            plt.yticks(())
            if j == 1:
                plt.xticks(the_range)
            else:
                plt.xticks(())
            ax = plt.gca()
            ax.spines['right'].set_color('none')
            ax.spines['top'].set_color('none')
        plt.title("%s normalizing"%("Without" if j == 0 else "With"))
    plt.draw()
    plt.pause(0.01)
    
def fix_seed(seed = 1):
    '''
    可重复性
    '''
    np.random.seed(seed)
    tf.set_random_seed(seed)
    
    
def add_layer(inputs, in_size, out_size, activation_function=None,norm=False,on_train=True):
    '''
    增添网络层
    '''
    # xs[n_samples,n_features],w:[n_features,n_outputs],b:[1,n_output]
    Weights = tf.Variable(tf.random_normal([in_size, out_size], mean=0.0,stddev=1.0))
    biases = tf.Variable(tf.zeros([1, out_size])+0.1)
    Wx_plus_b = tf.matmul(inputs, Weights) + biases
    
    # Batch Normalization
    if norm:
        # 利用momments来做batch_normalization;tf.nn.moments(x,axes,shift,name,keep_dims),返回x的均值和方差。
        # 利用卷积核做global normalization,拥有的shape[batch, height, width, depth],传递参数axes[0,1,2]
        # 简单的batch normalization就直接传递参数axes[0]
        # 想要 normalize 的维度, [0] 代表 batch 维度
        # 如果是图像数据, 可以传入 [0, 1, 2], 相当于求[batch, height, width] 的均值/方差, 注意不要加入 channel 维度
        # 公式: scale*(x-mu)/(sigma+epsilon) + shift
        fc_mean, fc_var = tf.nn.moments(Wx_plus_b,axes=[0])
        scale = tf.Variable(tf.ones([out_size])) # 尺度
        shift = tf.Variable(tf.zeros([out_size])) # 偏移量
        epsilon = 0.001 # 防止被零除
        '''
        由于使用batch进行每次的更新,所以每个batch的mean/var都不一样,所以使用滑动平均的方法来提升训练的稳定度
        '''
        ema = tf.train.ExponentialMovingAverage(decay=0.5)
        def mean_var_with_update():
            ema_apply_op = ema.apply([fc_mean, fc_var])
            with tf.control_dependencies([ema_apply_op]):# ema_apply_op必须在return操作前jinxing计算
                return tf.identity(fc_mean), tf.identity(fc_var)
            # 如果直接赋值的化会失效,这里必须要用tf.identity(),因为对与control_dependecies这个管理器,只有当里面的操作是一个op时候才会生效。
            # 也就是线执行传入参数的op,再窒息你该context中的op,但是如果直接return这个值的化就不是定义的op,在图中不会形成一个节点,也就会失效。
            # tf.identity是返回一个一模一样的新的tensor的op,这样就会增加一个新的节点到graph当中,这是的cotrol——dependecies就会生效。
            # 注意"="在tensorflow当中只是一个简单赋值,并不是定义的op,所以不会加到图中,例如mean=fc_mean,这个操作不会添加到图中。
        
        # 根据新的 batch 数据, 记录并稍微修改之前的 mean/var
        mean,var = mean_var_with_update() 
        '''
        由于在test和train阶段的情况不一样,所以添加on_train参数,将on_train定义成全局变量表示在训练阶段
        
        # tf.cond(pred, true_fn, false_fn)
        #True, 更新mean/var# False,返回之前fc_mean//fc_var的moving average
        #bool类型,True/False
        mean,var = tf.cond(on_train, 
                          mean_var_with_update,
                          lambda:(ema.average(fc_mean), ema.average(fc_var)))
        '''
        Wx_plus_b = tf.nn.batch_normalization(Wx_plus_b, mean, var, shift, scale, epsilon)
        # 上面那一步, 在做如下事情:
        # Wx_plus_b = (Wx_plus_b - fc_mean) / tf.sqrt(fc_var + 0.001)
        # Wx_plus_b = Wx_plus_b * scale + shift
        # 如果你已经看不懂了, 请去我最上面学习资料里的链接 (我制作的 Batch normalization 简介视频)
        
    if activation_function is None:
        outputs = Wx_plus_b
    else:
        outputs = activation_function(Wx_plus_b)
    return outputs
    
def build_net(xs, ys, norm):
    '''
    搭建神经网络
    '''
    fix_seed(1)
    # 对第一层采用BN
    if norm:
        fc_mean,fc_var = tf.nn.moments(xs, axes=[0])
        scale = tf.Variable(tf.ones([1]))
        shift = tf.Variable(tf.zeros([1]))
        epsilon = 0.01
        ema = tf.train.ExponentialMovingAverage(decay=0.5)
        
        def mean_var_with_update():
            ema_apply_op = ema.apply([fc_mean, fc_var])
            with tf.control_dependencies([ema_apply_op]):
                return tf.identity(fc_mean), tf.identity(fc_var)
        
        mean, var = mean_var_with_update()
        xs = tf.nn.batch_normalization(xs, mean, var, shift, scale, epsilon)
      
    # 每一层记录输入
    # 利用一个list来存放每一层的输入
    layers_inputs = [xs]
    
    for l_n in range(N_LAYERS):
        layer_input = layers_inputs[l_n]
        in_size = layers_inputs[l_n].get_shape()[1].value
        
        output = add_layer(layer_input, in_size, N_HIDDEN_UNITS, ACTIVATION, norm)
        layers_inputs.append(output)
        
    # 建立输出层
    prediction = add_layer(layers_inputs[-1],N_HIDDEN_UNITS,1,activation_function=None)
    
    cost = tf.reduce_mean(tf.reduce_sum(tf.square(ys-prediction),reduction_indices=[1]))
    train_op = tf.train.GradientDescentOptimizer(.001).minimize(cost)
    return [train_op, cost, layers_inputs]

# 创建数据
fix_seed(1)

x_data = np.linspace(-7,10,2500)[:,np.newaxis]
np.random.shuffle(x_data)
noise = np.random.normal(0,8,x_data.shape)
y_data = np.square(x_data) - 5 + noise

# plot inputdata
plt.scatter(x_data, y_data)
plt.show()

xs = tf.placeholder(tf.float32, [None, 1])
ys = tf.placeholder(tf.float32, [None, 1])

train_op, cost, layers_inputs = build_net(xs,ys,norm=False)
train_op_norm, cost_norm, layers_inputs_norm = build_net(xs,ys,norm=True)

sess = tf.Session()
if int((tf.__version__).split('.')[1]) <12 and int((tf.__version__).split('.')[0]) < 1:
    init = tf.initialize_all_variables()
else:
    init = tf.global_variables_initializer()
sess.run(init)

# record cost
cost_his = []
cost_his_norm = []
record_step = 5

plt.ion()
plt.figure(figsize=(7,3))
for i in range(250):
    if i%50 == 0:
        # 画直方图
        all_inputs, all_inputs_norm = sess.run([layers_inputs, layers_inputs_norm], feed_dict={xs:x_data,ys:y_data})
        # print(len(all_inputs), len(all_inputs_norm))
        plot_his(all_inputs, all_inputs_norm)
        
    # train on batch
    sess.run([train_op, train_op_norm], feed_dict={xs:x_data[i*10:i*10+10],ys:y_data[i*10:i*10+10]})
    
    if i % record_step == 0:
        cost_his.append(sess.run(cost, feed_dict={xs:x_data,ys:y_data}))
        cost_his_norm.append(sess.run(cost_norm, feed_dict = {xs:x_data,ys:y_data}))
        
plt.ioff()
plt.figure()
# 这里我运行出来最后cost_his的计算出现溢出的问题
plt.plot(np.arange(len(cost_his))*record_step, np.array(cost_his),label='no BN') # no norm
plt.plot(np.arange(len(cost_his_norm))*record_step, np.array(cost_his_norm), label='BN')
plt.legend()
plt.show()
sess.close()

如何理解卷积?

卷积的计算实际上就是针对图片不同的位置进行相同的权重相乘。
首先定义一个卷积核的大小,以及卷积核中间的参数。每一个固定的filter相当与一个固定的目标的特征量。也就是我们想要通过这个filter查看这个图片是否具有我们所需要提取的特征。针对每一个filter我们采用平移的方式对这个图片的各个未知进行Wx+b的计算,最终得到卷积值。这个卷积值代表的就是这个图片的这个区域被当前的filter激活的情况。通过不同的stride的设置,可以实现图片的下采样。 针对一个filter,当前层输出的是一个通道的filter map,代表的是这个图片针对当前的filter的激活的程度,也就是是否具有当前的这个filter特征的程度。最后当前卷积层输出的通道数目对应的就是filter的个数。
卷积一般都是采用的方形的filter。定一个图片的大小是N*N, filter的大小是F*F,如果说想要卷积后输出的图片的大小不变,可以先对图片做zero-padding,zero-padding是将图片外围填充宽度为P的0值。 这样填充之后的图片的大小是(N+2P)*(N+2P)。假设卷积的步长是stride,那么卷积之后,输出的图片的宽度是:
floor((N+2P-F)/stride) + 1,可以根据这个公式计算P的值的大小。

** 针对卷积的理解就是设计不同的filter提取不同的特征,也就是卷积是做特征提取的过程。**


如何理解卷积神经网络的卷积(Convolution Kernel)和池化(Pooling),有哪些池化?

  • kernel也就是上面所提到的filter。一个kernel可以设计一个想要提取的特征,这个kernel针对当前图片的输出就是feature map,也就是特征图片。
  • Pooling:实际上是降采样的过程,这个操作的目地是为了减少参数的数量,达到减轻过拟合的目标。这一过程其实在CNN中通过stride的设计也可以得到。现在在实践中也会这么做。一般Pooling会采取max-pooling,主要原因是我们想要寻求这个图片当中被这个kernel激活得最厉害的区域。所以我们一般采用max-pooling。

池化作用补充:

  • 池化引入了输入近似不变的特性。 就是当输入做少量平移的时候,经过池化之后的输出大部分不会改变。 局部的平移不变是一个非常有用的性质, 尤其是当我们关心某个特征是否出现而不关心它的具体的未知的时候。
    池化可以看作是一个无限增强的先验:这一层学得的函数必须具有对少量平移的不变性。当这个假设成立,pooling可以极大提高网络的统计效率。(这个特征也就是增加了鲁棒性)。
  • 处理多任务中,池化对于处理不同的输入也具有重要的作用。如果说想对不同大小的图像进行分类,分类层的输入必须是固定的大小,通常通过调整池化区域的偏置大小来实现。这样池化之后即使输入不同也会收到相同数量的统计特征。

pooling的种类:常见的池化函数包括最大池化,相邻矩形区域内的平均值,L2范数,以及基于据中文像素距离的加权平均数,一般最常采用max-pooling


learning_rate应该如何选取,过大或者过小的影响?

learning_rate应当根据当前的梯度以及迭代情况来决定大小,不合适的权重对训练的影响都不好。 image
从上图当中可以看出,如果说learning_rate设置过大,那么可能已经超过了最优质,但是如果learning_rate设置得过小,那么学习的效率就会很慢,需要迭代的次数就会更多。所以要合理设置合适大小的值。根据这个原因,提出了多种更改learning_rate的学习的办法,详细查看下面的不同的梯度下降的算法。


各种梯度下降算法的优缺点(也就是优化方法)。

  • SGD 随机梯度下降算法。就是针对训练数据,我们不一次全部计算所有的梯度,而是在一个epoch当中,训练完所有的参数,每一次只选择一个样本进行训练。这样训练在梯度下降当中加入了随机性,让训练过程当中避免陷入局部最小值,但是这个方法的缺点是因为每一次梯度更新只和当前的样本相关,随机性太强,因此很不稳定。mini-batch的训练就是基于SGD的基础上,但是mini-batch由于每一次的参数个数相较于一个样本会多,所以说mini-batch的训练会比单个样本的训练稍微稳定一点。在GPU中采用mini-batch的训练可以对训练进行提速,因为GPU的并行计算能力,不同的batch是同时计算的,但是batch_size的设置不能过大,因为过大的batch_size会超过GPU的内存,会导致运行出错。

这个算法的关键参数是学习率

  • momentum 动量梯度下降:这个算法的来源于牛顿定律,基本思想加入惯性的作用,如果说陷入了局部的最小值,可以由于这个惯性的作用跳出这个局部最小值。

tf.train.MomentumOptimizer()
动量的主要目的是为了解决两个问题,一个是Hessian矩阵的病态条件,一个是随机梯度的方差。当数据的维度尺寸不一样的时候,梯度下降可能会出现zig-zag路线。(Hessian矩阵的病态条件也就是不同的尺度的二次导数的尺度不一样,也就是这个原因导致了zig-zag路线)。

  • Adagrad(adaptive gradient optimization): 根据前面累计的梯度的值的均方值(平方和的平均值开根号),以及相关的基于时间的learning_rate的衰减,更新现在的learning_rate。Adagrad因为引入了时间衰减项(因为上下分子分母抵消,所以无法看出来),导致了训练到后面的速度会非常的慢,因为在达到最优解之前,learning_rate就已经衰减得非常小了。

tf.train.AdagradOptimizer()

  • Adam(adaptive moments): 属于现在比较稳定的梯度下降的办法,结合了RMSProp梯度下降的算法,并且加入了梯度的一阶矩(动量项)和二阶距的估计(非中心项)。

tf.train.AdamOptimizer()

  • RMSProp:
    基于Adagrad的基础上修改,为了让梯度的算法在非凸的情况下更好(大多数情况下,神经网络都不是严格的凸函数)。这里将梯度的累计变为指数加权移动平均。拥有二阶项,但缺少优化银子

tf.train.RMSPropOptimizer()


机器学习中,有哪些正则化的方法?目的是什么?Dropout的好处是?

机器学习中,一般采用的正则的方法有L1范数,L2范数,一般采用L2的范数其实相当于是Weight decay也就是权重衰减。L1范数的方法具有特征选择的功能,但是L2范数只能够减少权重的值,让权重值的大小基本上接近0,而不能等于0。实际L2范数也可以看成是weight decay也就是权重的衰减。
在树的方法中中,正则的方法是剪枝,也就是保证树的叶节点的个数不会过多,以此达到正则的目地。

正则的目的:
由于当选用的模型过于复杂的时候,这样的假设集合目标群体太大,泛化能力会变差,容易导致过拟合(也就是说容易拟合噪声)。加入正则就是降低目标集合群,防止过拟合,提升模型的泛化能力。

Dropout:

  1. 由于每次用输入网络的样本进行权值更新时,隐含节点都是以一定概率随机出现,因此不能保证每2个隐含节点每次都同时出现,这样权值的更新不再依赖于有固定关系隐含节点的共同作用,阻止了某些特征仅仅在其它特定特征下才有效果的情况。
  2. 可以将dropout看作是模型平均的一种。对于每次输入到网络中的样本(可能是一个样本,也可能是一个batch的样本),其对应的网络结构都是不同的,但所有的这些不同的网络结构又同时share隐含节点的权值。这样不同的样本就对应不同的模型,是bagging的一种极端情况。个人感觉这个解释稍微靠谱些,和bagging,boosting理论有点像,但又不完全相同。
  3. native bayes是dropout的一个特例。Native bayes有个错误的前提,即假设各个特征之间相互独立,这样在训练样本比较少的情况下,单独对每个特征进行学习,测试时将所有的特征都相乘,且在实际应用时效果还不错。而Droput每次不是训练一个特征,而是一部分隐含层特征。
  4. 还有一个比较有意思的解释是,Dropout类似于性别在生物进化中的角色,物种为了使适应不断变化的环境,性别的出现有效的阻止了过拟合,即避免环境改变时物种可能面临的灭亡。

Dropout的好处就是防止过拟合,由于dropout可以看作是模型的平均,所以选择dropout这个trick的时候,一般用的激活函数都是偏向于线性的激活函数。

在神经网络当中采用的正则的方法和机器学习中不一样,L2正则实际上对神经网络起到的作用比较小,所以神经网络的正则有以下方法:

  1. Batch Normalization(实际上,一般会首先采用这种正则的方法,一般最开始只用着一种,不够才会再加dropout)。
  2. Dropout(在全连接层是采用随机的某些单元置零,在CNN网络层一般是将某些的整个的feature map置零),测试的时候利用所有测试的数据的结果的期望。
  3. DropConnect(这里类似Dropout,但是Dropout是把激活函数置零,这里是将权重置零,结果类似)。
  4. Fractional Max Pooling:不常用,就是在每次池化的时候随机选择池化的区域,最后的在测试的时候取平均,降低随机性。
  5. 数据增强。
  6. 随机神经网络的深度:就是在训练的时候,随机省略某些层不训练,最终结果是使用整个网络。

参考文献:
http://www.cnblogs.com/tornadomeet/p/3258122.html


如何理解“过拟合 overfitting”? 神经网络训练,如何避免overfitting?

过拟合:模型在训练集上能够得到很好的结果,但是这个模型在测试集上的表现很差,说明发生了过拟合现象。所以神经网络的结果不好不一定是过拟合,有可能是根本没有训练起来,在训练机上的效果就表现得很差。针对神经网络的过拟合的判断一定要查看训练集的效果。
如何避免:

  1. Early stopping:将训练集的正确率与测试集的正确率进行对比,由于随着训练加深,训练集的loss 值会不断提高,但是会发生过拟合现象,导致测试集的loss值先下降再上升。一般的做法是分出一个validation set,将训练的模型在这个数据集上进行预测,找到在validation set中loss值最低模型,而不是训练集中loss值最低的模型。
  2. 在loss函数里面添加正则项,也就是增添L1,L2范数。(正则化)
  3. Dropout
  4. data augment(数据增强)这样可以训练模型抵抗噪声的能力,实际上我觉着可以防止过拟合。这里有人提出实际上是一种增加样本集合的办法。
  5. 增加样本数量。
  6. 降低模型的复杂性。
    参考文献:
    http://www.cnblogs.com/unclelin/p/6384608.html

如何理解Momentum?weight decay?他们在神经网络中训练的作用是什么?

Momentum:实际商是共轭梯度法的近似,是通过历史搜索方法对当前梯度方向的修正来抵消在ill-conditioned问题上的来回震荡,也就是ill-Hessian矩阵出现的zig-zag的问题。可以加快网络的训练的速度。

参考文献:
https://www.zhihu.com/question/24529483

weight decay:是权重衰减,实际上是参数正则的过程,在损失函数添加L2范数实际就是做了weight decay。在神经网络的训练当中可以防止过拟合,增加泛化能力。


Covariate Shift 和 Internal Covariate Shift如何理解?

Covariate shift: 训练集和测试集中X的概率密度不一样。
一般用于迁移学习的领域。假设x是属于特征空间的某一样本点,y是标签。covariate这个词,其实就是指这里的x,那么covariate shift可以直接根据字面意思去理解:样本点x的变化。
假设q1(x)是测试集中一个样本点的概率密度,q0(x)是训练集中一个样本点的概率密度。最终我们估计一个条件概率密度p(y|x,θ),它由x和一组参数θ={θ1,θ2…θm}所决定。对于一组参数来说,对应loss(θ)函数评估性能的好坏 综上,当我们找出在q0(x)分布上最优的一组θ’时,能否保证q1(x)上测试时也最好呢? 传统机器学习假设训练集和测试集是独立同分布的,即q0(x)=q1(x),所以可以推出最优θ’依然可以保证q1(x)最优。但现实当中这个假设往往不成立,伴随新数据产生,老数据会过时,当q0(x)不再等于q1(x)时,就被称作covariate shift

二、怎么解决covariate shift?
以上已经知道一个样本点分别在训练集和测试集上的概率密度q0(x)和q1(x),实际当中的解决方案是附加一个由x决定的权值。通过这个附加的权值,拟合出新的数据。

使得在训练过程当中对于q1(x)很大或者q0(x)很小的样本视作“重要”样本,这样的样本是有益于测试集预测的,我们应该尽量把它分类正确。而对于q1(x)很小或者q0(x)很大的样本,它只是被时代遗弃的“老数据”,这些样本对于模型训练的意义也是无关紧要了.


Internal Convariate Shift: 这个概念在Batch Normalization这篇论文中有提到。论文中数由于深度神经网络每层输入的分布在训练的过程会发生变化,因为前面层数会发生变化,就通过设置比较小的learning rate并且对参数的初始化进行好的调节来减慢训练,但是由于神经网络还具有饱和的非线性激活函数,这样子模型的训练就非常的困难。由于网络参数的变化,将internal convariate shift定义为网络激活分布的变化。 为了改善训练,寻求减少内部协变量的转移。
在训练过程中,每一层的激活值的分布随着网络训练时参数的变化而变化,当输入值为一个很大的正数或者负数时,会导致激活值的分布趋于非线性饱和区,如 sigmoid函数两头梯度几乎为0,这就会导致梯度消失。可以用Batch Normalization的方式来解决。

参考文献:
https://www.google.com.hk/search?client=ubuntu&channel=fs&q=covariate+shift&ie=utf-8&oe=utf-8&gws_rd=cr,ssl
http://noahsnail.com/2017/09/04/2017-9-4-Batch%20Normalization论文翻译——中文版/


LeNet5模型的详细解释

C1层:paper作者,选择6个特征卷积核,然后卷积核大小选择5*5,这样我们可以得到6个特征图,然后每个特征图的大小为32-5+1=28,也就是神经元的个数为6*28*28=784。

S2层:这就是下采样层,也就是使用最大池化进行下采样,池化的size,选择(2,2),也就是相当于对C1层28*28的图片,进行分块,每个块的大小为2*2,这样我们可以得到14*14个块,然后我们统计每个块中,最大的值作为下采样的新像素,因此我们可以得到S1结果为:14*14大小的图片,共有6个这样的图片。经过sigmoid函数进行映射。

C3层:卷积层,这一层我们选择卷积核的大小依旧为5*5,据此我们可以得到新的图片大小为14-5+1=10,然后我们希望可以得到16张特征图。那么问题来了?这一层是最难理解的,我们知道S2包含:6张14*14大小的图片,我们希望这一层得到的结果是:16张10*10的图片。

S4层:下采样层,比较简单,也是知己对C3的16张10*10的图片进行最大池化,池化块的大小为2*2。因此最后S4层为16张大小为5*5的图片。至此我们的神经元个数已经减少为:16*5*5=400。激活函数是sigmoid。

C5层:我们继续用5*5的卷积核进行卷积,然后我们希望得到120个特征图。这样C5层图片的大小为5-5+1=1,也就是相当于1个神经元,120个特征图,因此最后只剩下120个神经元了。这个时候,神经元的个数已经够少的了,后面我们就可以直接利用全连接神经网络,进行这120个神经元的后续处理,后面具体要怎么搞,只要懂多层感知器的都懂了,不解释。

F6 全连接层, 含有84个隐层节点, 激活函数sigmoid

F7 dense layer: output 10类,softmax回归。

总结:

  • Lenet5 含有三个卷积层,两个池化层,再加上1个全连接层。

  • 每个卷积层都包含: 卷积、池化、非线性激活函数映射

参考文献:
https://blog.csdn.net/d5224/article/details/68928083
http://www.cnblogs.com/hejunlin1992/p/8449449.html

心得:
注意不要把op的名字和赋值的参数名字弄成一样,这样子tensorflow会报错。
调参魅力无穷啊,adam的learning_rate从0.001调整到0.0001,正确率提升了80%,可怕。 (这里我针对下面的implemetation有个疑问,我的train和test一模一样,我这样训练的设置是不是有问题呢?)



import os
import struct
import numpy as np

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)

    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)

    return images, labels

import tensorflow as tf
import numpy as np

# implementaion of LeNet5
# 构建7层网络结构, conv1,max_pooling1,conv2,max_pooling2, conv3,dense_layer1, dense_layer2(softmax)

def conv_and_pooling(input_layer,filters, kernel_size, pool_size, strides_conv,strides_pool, padding_conv="valid",padding_pool = "valid"):
    # Convolutional Layer #1
    conv1 = tf.layers.conv2d(inputs = input_layer,strides=strides_conv, filters = filters, kernel_size=kernel_size, padding=padding_conv)
    pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size= pool_size, padding=padding_pool,strides=strides_pool)
    output = tf.nn.relu(pool1)
    return output

def LeNet_model_fn(x, y, rate):
    '''
    mode : TRAIN, EVAL, PREDICT
    '''
    # Input layer
    input_layer = tf.reshape(x, [-1,28,28,1])
    c_and_p1 = conv_and_pooling(input_layer,6,[5,5],[2,2],1,2,padding_conv='same')
    c_and_p2 = conv_and_pooling(c_and_p1,16,[5,5],[2,2],1,2)
    conv3 = tf.layers.conv2d(inputs = c_and_p2, filters = 120, strides = 1,kernel_size=[5,5], padding="valid")
    # dense layer
    conv3_flat = tf.reshape(conv3, [-1, 1*1*120])
    dense = tf.layers.dense(inputs=conv3_flat, units=84, activation=tf.nn.relu)
    dropout = tf.layers.dropout(inputs = dense, rate=rate)
    logits = tf.layers.dense(inputs=dropout, units=10)
    
    y = tf.one_hot(indices = tf.cast(y, tf.int32), depth=10)
    
    predictions = {
        "classes" : tf.argmax(logits, axis=1),
        "probabilities" : tf.nn.softmax(logits, name="softmax_tensor")
    }
    loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits = predictions["probabilities"],labels = y))
    loss1 = tf.reduce_mean(tf.reduce_sum(-y*tf.log(predictions["probabilities"]),axis=1))
    accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(y,1),predictions["classes"]), tf.float32))
    train_op = tf.train.AdamOptimizer(0.0001,0.9,0.999,1e-8).minimize(loss)
    
    return [loss,loss1,accuracy,predictions,train_op]
    
    """
    这里调用了tensorflow子集定义的estimator类
    if mode == tf.estimator.ModeKeys.PREDICT:
        return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions)
    
    loss = tf.loss.sparse_softmax_cross_entropy(labels=y, logits=logits)
    
    if mode == tf.estimator.ModeKeys.TRAIN:
        optimizer = tf.train.GradientDesentOptimizer(learning_rate=0.001)
        train_op = optimizer.minimize(loss=loss, global_step = tf.train.get_global_step())
        return tf.estimator.EstimatorSpec(mode=mode, loss=loss, train_op=train_op)
    
    if mode == tf.estimator.ModeKeys.EVAL:
        eval_metric_ops = {
            "accuracy" :tf.metrics.accuracy(labels=y, predictions=predictions["classes"])
        }
        return tf.estimator.EstimatorSpec(mode=mode, loss=loss, eval_metric_ops=eval_metric_ops)
    """

if __name__ == "__main__": 
    train_epochs = 30
    batch_size = 100
    display_step = 1
    
    images,y_train = load_mnist('/home/lily/Datageek/MNIST_data','train')
    X_test,y_test = load_mnist('MNIST_data','t10k')
    
    x = tf.placeholder(tf.float32, [None, 784])
    y = tf.placeholder(tf.float32, [None])
    rate = tf.placeholder(tf.float32)
    [loss,loss1,accuracy,predictions,train_op] = LeNet_model_fn(x,y,rate)
    init = tf.global_variables_initializer()
    #n_samples = len(images)
    with tf.Session() as sess:
        sess.run(init)
        
        for epoch in range(train_epochs):
            avg_loss = 0
            avg_loss1 = 0
            if  len(images) % batch_size == 0:
                batches = len(images) // batch_size
            else:
                batches = len(images) // batch_size + 1
            for i in range(batches):
                if (i+1)*batch_size <= len(images):
                    # 注意前面los的名称不能和后面的loss相同,否则会报错
                    _, los,los1 = sess.run([train_op, loss,loss1], feed_dict={x:images[i*batch_size:(i+1)*batch_size],
                                                         y:y_train[i*batch_size:(i+1)*batch_size], rate:0.5})
                else:
                    _, los,los1 = sess.run([train_op, loss, loss1], feed_dict={x:images[i*batch_size:],
                                                         y:y_train[i*batch_size:], rate:0.5})
                avg_loss += los/batches
                avg_loss1 += los1/batches
            
            if (epoch+1) % display_step == 0:
                print("epoch %d, avg loss %g"%(epoch+1, avg_loss))
               #  print("epoch %d, avg loss %g"%(epoch+1, avg_loss1))
    
        train_accuracy = accuracy.eval(feed_dict={x:images, y:y_train, rate:1})
        print("training accuracy %g"%(train_accuracy))
        test_accuracy = accuracy.eval(feed_dict={x:X_test, y:y_test,  rate:1})                
        print("test accuracy %g"%(train_accuracy))

#7

ResNet, GoogleNet, VGG16网络结构原理解释:

AlexNet的网络结构如下图所示:

AlexNet改进版本:

VGGNet的网络结构如下图所示:

VGGNet:(有16层和19层两种规格)

在VGGNet中采用比较小的3×3的filter:因为3×3的filter叠加三层,它对原始图片的有效的接受域(effective receptive field)就是7×7的filter的接受域,但是参数是7×7的filter的 3×3×3/(7×7),可以减少训练参数,而且这样做的化网络更深了,添加了更多的非线性。(7×7的filter考虑的图片之间的特征相关的区域比较大,这是为什么我们需要更大的图形的接受域的区间的原因。)

GoogleNet(inception)
网络结构如下所示:

inception:


naive的perception中间是没有叠加1×1的conv的,这样导致的结果是经过inception之后,特征map的数量会很多,造成的参数会很大,计算复杂度就会很高。为了减少计算复杂度,添加了bottleneck层,也就是添加1×1的conv层,减少filter的个数,这样输出的feature map的个数就会减少,参数的个数就会减少接近一半。

GoogleNet的结构:
首先是简单conv-pool-2×conv-pool的原始的网络
然后就是inception的网络堆叠
最后是分类输出。

由于在GoogleNet提出的时候没有BN,为了训练GoogleNet,增加了一些小的辅助分类器来训练,如下图所示:

目的是为了在前面的网络层里面注入一些额外的梯度,增加网络训练的能力(感觉是为了减少梯度消失,否则可能前面的网络层的参数无法得到更新。)

ResNet:

这里是在不同的网络层里面加入了直接连接线,让网络在训练的过程中会有快捷通道,所以可以让网络可以更好的更新参数。
在原始的网络层中,某几层网络的输出是H(X),输入是X,但是在网络变更之后,因为这个网络后面会接这些网络之前的输入,所以相当于现在这几层网络只需要学习原来需要输出的H(X)和输入X的差,也就是F(X), 这个F(X) = H(X) - X,也就是残差。
因为深层的模型应该至少比比较少层数的模型表现更好,所以就想要通过将shallower model训练的layers直接映射到更深层的模型当中。这就是中间连接线的作用。

ResNet将residual blocks叠加,每个residual block都有两个3×3的conv层。 并且周期性的将filter的个数乘以2, 利用stride2来降采样。 在residual blocks层前面有额外的卷积层,除了最后的softmax层没有FC layer。

ResNet网络的深度是32, 50, 101, 152层。 对于50层深度之后的网络,也采用了bottleneck的处理方式,也就是1×1conv层。

ResNet一些改进版本:

LSTM/GRU原理解释,tf中对应的参数?

原始RNN:

LSTM:
提出是为了减少梯度消失和梯度爆炸

tf.contrib.rnn.LSTMCell(num_units, use_peepholes, cell_clip…)

GRU:

tf.contrib.nn.GRUCell(num_units, activation, reuse,…)
reuse: 表示是否在这个scope中重复利用这些给定的变量,如果说不是True,如果原来在这个scope中已经存在了给定的variables,熊就会报错。

tf.contrib.rnn.MultiRNNCell(cells, state_is_tuple=True): state_is_tuple:True: 接受并且返回n-tuples的状态位,n = len(cells),cells的个数。
False:装提啊为会依照column axis连接,后面的这个动作会被deprecated,所以默认为True。

tf.nn.dynamic_rnn(cell, inputs, sequence_length= None,…)
创建一个RNN通过特定的cell。
返回:(outputs, state)


  rnn_layers = [tf.nn.rnn_cell.LSTMCell(size) for size in [128, 256]]
  
  # 构建多层的LSTM网络
  multi_rnn_cell = tf.nn.rnn_cell.MultiRNNCell(rnn_layers)
  
  # 使用dynamic_rnn函数,告诉tf构建多层LSTM网络,并且定义该层的输出
  #  这里outputs的shape\[batch_size, max_time, 256]
  # state是N-tuple, N = # LSTMCells
  outputs, state = tf.nn.dynamic_rnn(cell = multi_rnn_cell, inputs = data, dtype = tf.float32)

简单的RNN示例

分类示例,dataset MNIST


# RNN实际操作
# 分类
import os
import struct
import numpy as np
import tensorflow as tf

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)

    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)

    return images, labels

from sklearn.preprocessing import OneHotEncoder
X_train,y_train = load_mnist('/home/lily/Datageek/MNIST_data','train')
X_test,y_test = load_mnist('MNIST_data','t10k')
enc = OneHotEncoder()
y_train = enc.fit_transform(y_train.reshape(-1,1)).toarray()

y_test = enc.transform(y_test.reshape(-1,1)).toarray()

X_train = X_train.reshape((-1,28,28))
X_test = X_test.reshape((-1,28,28))

#  超参数
lr = 0.001
training_iters = 100000
batch_size = 128
train_epochs = 20
display_step = 1

n_inputs = 28 #图片长
n_steps = 28 #图片高
n_hidden_units = 128
n_classes = 10

tf.reset_default_graph()
x = tf.placeholder(tf.float32, [None, n_steps, n_inputs])
y = tf.placeholder(tf.float32, [None, n_classes])
init_batch_size = tf.placeholder(tf.int32,[])

weights = {'in': tf.Variable(tf.random_normal([n_inputs, n_hidden_units])),
          'out': tf.Variable(tf.random_normal([n_hidden_units, n_classes]))}
biases = {'in': tf.Variable(tf.constant(0.1, shape=[n_hidden_units,])),
         'out': tf.Variable(tf.constant(0.1, shape=[n_classes,]))}

def RNN(X,init_batch_size,weights, biases):
    
    # 原始X的shape是[batches, n_steps, n_inputs]
    # X = (128batch*28step,28inputs)
    X = tf.reshape(X, [-1, n_inputs])
    
    # X_in==>(128batch*28step,128)
    X_in = tf.matmul(X, weights['in']) + biases['in']
    
    # X_in ==> (128batch, 28steps, 128hidden)
    X_in = tf.reshape(X_in, [-1,n_steps, n_hidden_units])
    
    with tf.variable_scope("lstm"):
        # cell
        lstm_cell = tf.contrib.rnn.BasicLSTMCell(n_hidden_units,forget_bias=1.0,state_is_tuple=True)
        # lstm cell有两个状态的输出(c_state, m_state),一个是c_state,一个是m_state,生成下面的的初始的状态的时候会包括一个元组,包括这两个元素
        _init_state = lstm_cell.zero_state(init_batch_size, dtype=tf.float32)

        # 选择RNN如何运算
        # 用LSTM,state有两个(c_state, m_state),这个输出的state是最后一步state的输出,这个ouputs是每一步的ouputs的输出
        # time_major表示输出时间的维度是不是在第一个维度上面(主要的维度)
        # 如果说输入为(steps,batches,inputs)那么time_major=True,(batches,steps,inputs),time_major=False
        ouputs, states = tf.nn.dynamic_rnn(lstm_cell, X_in, initial_state=_init_state, time_major=False)

        # hidden layer for ouput as the final results
        results = tf.matmul(states[1], weights['out'])+biases['out']

        '''
        #另外一种解法,将output转换成一个list,将它的维度转换成[batch, output]*steps 
        # outputs = tf.unpack(tf.transpose(outputs,[1,0,2])) 
        outputs = tf.unstack(tf.transpose(outputs,[1,0,2])) 
        results = tf.matmul(outputs[-1],weights['out']) + biases['out']
        '''
        return results

pred = RNN(x,init_batch_size, weights,biases) 
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=pred, labels=y))
with tf.variable_scope("train"):
    train_op = tf.train.AdamOptimizer(lr).minimize(cost)

correct_pred = tf.equal(tf.argmax(pred,1), tf.argmax(y,1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))

with tf.Session() as sess:
    # tf.reset_default_graph()
    # tf.reset_default_graph()
    init = tf.global_variables_initializer()
    sess.run(init)
    step = 0
    for epoch in range(train_epochs):
            if  len(X_train) % batch_size == 0:
                batches = len(X_train) // batch_size
            else:
                batches = len(X_train) // batch_size + 1
            for i in range(batches):
                if (i+1)*batch_size <= len(X_train):
                    batch_xs = X_train[i*batch_size:(i+1)*batch_size]
                    batch_ys = y_train[i*batch_size:(i+1)*batch_size]
                    this_batch = batch_size
                    
                else:
                    batch_xs = X_train[i*batch_size:]
                    batch_ys = y_train[i*batch_size:]
                    this_batch = len(X_train)-i*batch_size
               
                sess.run([train_op], feed_dict={x:batch_xs,
                                               y:batch_ys,init_batch_size:this_batch})
                # print("done")
                
            if epoch % display_step == 0:
                # print("yes")
                print(accuracy.eval({x:X_train,y:y_train,init_batch_size:len(X_train)}))
                print(accuracy.eval({x:X_test,y:y_test,init_batch_size:len(X_test)}))

回归示例


# LSTM回归的例子
# (https://github.com/MorvanZhou/tutorials/blob/master/tensorflowTUT/tf20_RNN2.2/full_code.py)
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt


BATCH_START = 0
TIME_STEPS = 20
BATCH_SIZE = 50
INPUT_SIZE = 1
OUTPUT_SIZE = 1
CELL_SIZE = 10
LR = 0.006

tf.reset_default_graph()
def get_batch():
    global BATCH_START,TIME_STEPS
    xs = np.arange(BATCH_START,BATCH_START+TIME_STEPS*BATCH_SIZE).reshape((BATCH_SIZE,TIME_STEPS))/(10*np.pi)
    seq = np.sin(xs)
    res = np.cos(xs)
    BATCH_START += TIME_STEPS
    return [seq[:,:,np.newaxis], res[:,:,np.newaxis],xs]

class LSTMRNN(object):
    def __init__(self, n_steps, input_size, output_size, cell_size, batch_size):
        self.n_steps = n_steps
        self.input_size = input_size
        self.output_size = output_size
        self.cell_size = cell_size
        self.batch_size = batch_size
        with tf.name_scope("input"):
            self.xs = tf.placeholder(tf.float32, [None, n_steps, input_size], name="xs")
            self.ys = tf.placeholder(tf.float32, [None, n_steps, output_size], name="ys")
        with tf.variable_scope("in_hidden"):
            self.add_input_layer()
        with tf.variable_scope("LSTM_cell"):
            self.add_cell()
        with tf.variable_scope("out_hidden"):
            self.add_output_layer()
        with tf.name_scope("cost"):
            self.compute_cost()
        with tf.name_scope("train"):
            self.train_op = tf.train.AdamOptimizer(LR).minimize(self.cost)
            
    def add_input_layer(self):
        l_in_x = tf.reshape(self.xs, [-1, self.input_size], name="2_2D")
        
        Ws_in = self._weight_variable([self.input_size, self.cell_size])
        
        bs_in = self._bias_variable([self.cell_size,])
        
        with tf.name_scope("Wx_plus_b"):
            l_in_y = tf.matmul(l_in_x, Ws_in) + bs_in
            
        self.l_in_y = tf.reshape(l_in_y, [-1, self.n_steps, self.cell_size], name="2_3D")
        
    def add_cell(self):
        lstm_cell = tf.contrib.rnn.BasicLSTMCell(self.cell_size, forget_bias=1.0, state_is_tuple=True)
        with tf.name_scope("init_state"):
            self.cell_init_state = lstm_cell.zero_state(self.batch_size, dtype=tf.float32)
        self.cell_outputs, self.cell_final_state = tf.nn.dynamic_rnn(
            lstm_cell, self.l_in_y, initial_state = self.cell_init_state, time_major=False)
        
    def add_output_layer(self):
        # shape = (batch * steps, cell_size)
        l_out_x = tf.reshape(self.cell_outputs, [-1, self.cell_size], name='2_2D')
        Ws_out = self._weight_variable([self.cell_size, self.output_size])
        bs_out = self._bias_variable([self.output_size, ])
        # shape = (batch * steps, output_size)
        with tf.name_scope('Wx_plus_b'):
            self.pred = tf.matmul(l_out_x, Ws_out) + bs_out
        
    def compute_cost(self):
        #tf.contrib.legacy_seq2seq.sequence_loss_by_example(logits, targets, weights)
        losses = tf.contrib.legacy_seq2seq.sequence_loss_by_example(
            [tf.reshape(self.pred, [-1], name='reshape_pred')],
            [tf.reshape(self.ys, [-1], name='reshape_target')],
            [tf.ones([self.batch_size * self.n_steps], dtype=tf.float32)],
            average_across_timesteps=True, # 除以所有的权重
            softmax_loss_function=self.ms_error,
            name='losses'
        )
        with tf.name_scope('average_cost'):
            self.cost = tf.div(
                tf.reduce_sum(losses, name='losses_sum'),
                self.batch_size,
                name='average_cost')
            tf.summary.scalar('cost', self.cost)

    @staticmethod
    def ms_error(labels, logits):
        return tf.square(tf.subtract(labels, logits))

    def _weight_variable(self, shape, name='weights'):
        initializer = tf.random_normal_initializer(mean=0., stddev=1.,)
        return tf.get_variable(shape=shape, initializer=initializer, name=name)

    def _bias_variable(self, shape, name='biases'):
        initializer = tf.constant_initializer(0.1)
        return tf.get_variable(name=name, shape=shape, initializer=initializer)
    
    
if __name__ == '__main__':
    model = LSTMRNN(TIME_STEPS, INPUT_SIZE, OUTPUT_SIZE, CELL_SIZE, BATCH_SIZE)
    sess = tf.Session()
    merged = tf.summary.merge_all()
    writer = tf.summary.FileWriter("logs", sess.graph)
    # tf.initialize_all_variables() no long valid from
    # 2017-03-02 if using tensorflow >= 0.12
    if int((tf.__version__).split('.')[1]) < 12 and int((tf.__version__).split('.')[0]) < 1:
        init = tf.initialize_all_variables()
    else:
        init = tf.global_variables_initializer()
    sess.run(init)
    # relocate to the local dir and run this line to view it on Chrome (http://0.0.0.0:6006/):
    # $ tensorboard --logdir='logs'

    plt.ion() # 开启matplotlib的交互模式
    plt.show()
    for i in range(200):
        seq, res, xs = get_batch()
        if i == 0:
            feed_dict = {
                    model.xs: seq,
                    model.ys: res,
                    # create initial state
            }
        else:
            feed_dict = {
                model.xs: seq,
                model.ys: res,
                model.cell_init_state: state    # use last state as the initial state for this run
            }

        _, cost, state, pred = sess.run(
            [model.train_op, model.cost, model.cell_final_state, model.pred],
            feed_dict=feed_dict)

        # plotting
        plt.plot(xs[0, :], res[0].flatten(), 'r', xs[0, :], pred.flatten()[:TIME_STEPS], 'b--')
        plt.ylim((-1.2, 1.2))
        plt.draw()
        plt.pause(0.3)

        if i % 20 == 0:
            print('cost: ', round(cost, 4))
            result = sess.run(merged, feed_dict)
            writer.add_summary(result, i)
    plt.ioff()

word2vec和GloVe原理,各自的异同:

word2vec:
word2vec是利用center word和窗口词汇中最大化这些共同出现的词向量之间的点乘的概率,来获得词汇的向量,学习词汇中的语义和语法的信息。word2vec需要遍历整个语料库里面所有的词,计算center word出现的条件下窗口词汇中出现的概率,所以训练的时间比较就,词汇量比较大的时候比较难训练。但是能够学习词汇之间的一些信息,能够提升后续的(downstream model)的一些性能。

GloVe:
这个也是学习词汇的word vector,但是在这里,会提前统计词料库总词汇共同出现的次数,利用这个词汇出现的次数的matrix和共同出现的词向量之间的点乘的做差,想要最小化差值,也就是最大化共同出现的词汇之间的相似度。这里结合了直接统计计算的word counts的方法和word2vec的方法,不需要像word2vec那样遍历整个语料库里面所有的窗口信息,也降低了统计计算词汇之间共同出现次数矩阵的最后表现的词汇的维度以及稀疏的特性。


相同:
都是为了得到最后词的向量的表达形式。

不同:
训练的目标函数不一样,收敛速度不一样,GloVe整体上而言优于word2vec。

word2vec的应用:

  • 推荐系统:可以生成用户之间的特征或者是物品之间的特征,,利用协同过滤的算法,族谱为推荐系统。 (社交网络中的推荐系统)
  • 计算物品之间的相似度,可以做竞品推荐,在计算广告中,可以根据用户的广告点击序列将每个广告训练成一个向量,利用这个向量生成特征融入到rank的模型当中。
  • 作为另外模型的输入:(不适用于语义情感分析的模型的输入,因为word2vec只是根据语料库学习的,是没有标签的非监督学习,在语料库中会同时包含正向和负向的情感,所以word2vec的输入与random vector的输入情况可能差不多)
    可以做语法分析,语义分析等等。
  • 向量快速检索:将一个文档训练成一个向量,利用相似性来实现topk的文章。但是如果文档更新速度快,这个更方法就不合适了。
    参考文献:
    http://x-algo.cn/index.php/2016/03/12/281/

实践 word2vec


## 参考cs224n 课程7
## 在这个实作里面使用的是ML2017中间homework6中间的all_sents.txt的文本,
## 在这里利用jieba中文分词对这个文件进行分词。
import jieba
import pickle
import collections
import random
import tqdm
import os
jieba.set_dictionary('/home/lily/Datageek/dict.txt.big')

def read_data(filename="all_sents.txt"):
    all_sents = []
    with open(filename,'r') as f:
        for line in f.readlines():
            sent = jieba.lcut(line.strip())
            all_sents.extend(sent)
    pickle.dump(all_sents,open('sliced_sents.pkl','wb'))
    return all_sents

all_sents = read_data("all_sents.txt")
print("word length", len(all_sents))
vocabulary_size = 50000

def build_dataset(all_sents):
    count = [['UNK', -1]]
    count.extend(collections.Counter(all_sents).most_common(vocabulary_size-1))
    dictionary = {}
    for word,_ in count:
        dictionary[word] = len(dictionary)
    data = []
    unk_count = 0
    for word in all_sents:
        if word in dictionary:
            index = dictionary[word]
        else:
            index = 0
            unk_count += 1
        data.append(index)
    count[0][1] = unk_count
    reverse_dictionary = dict(zip(dictionary.values(), dictionary.keys()))
    return data, count, dictionary, reverse_dictionary

data, count, dictionary, reverse_dictionary = build_dataset(all_sents)
del all_sents

data_index = 0
def generate_batch(batch_size, num_skips, skip_window):
    global data_index
    assert batch_size % num_skips == 0
    assert num_skips <= 2* skip_window
    batch = np.ndarray(shape=(batch_size), dtype=np.int32)
    labels = np.ndarray(shape=(batch_size, 1), dtype=np.int32)
    span = 2*skip_window+1
    buffer = collections.deque(maxlen=span)
    for _ in range(span):
        buffer.append(data[data_index])
        data_index = (data_index+1)%len(data)
    for i in range(batch_size//num_skips):
        target = skip_window
        targets_to_avoid = [skip_window]
        for j in range(num_skips):
            while target in targets_to_avoid:
                target = random.randint(0,span-1)
            targets_to_avoid.append(target)
            batch[i*num_skips+j] = buffer[skip_window]
            labels[i*num_skips+j,0] = buffer[target]
        buffer.append(data[data_index])
        data_index = (data_index+1)%len(data)
    return batch, labels

batch_size = 128
skip_window = 1
num_skips = 2

valid_size = 16
valid_window = 100
valid_examples = np.random.choice(valid_window, valid_size, replace=False)

num_steps = 30000
training_data = []

for step in tqdm(range(num_steps)):
    batch_inputs, batch_labels = generate_batch(batch_size, num_skips, skip_window)
    training_data.append((batch_inputs, batch_labels))
    
data_folder = 'data1'

if not os.path.exists(data_folder):
    os.makedirs(data_folder)
print("Saving training data...")
pickle.dump(training_data,open("./data1/train.pkl","wb"))
print("Saving validation data...")
pickle.dump(valid_examples,open("./data1/val.pkl","wb"))
print("Saving reverse_dictionary...")
pickle.dump(reverse_dictionary,open("data1/reverse_dictionary.pkl","wb"))
            

# utils
 # uitls.py
    
import matplotlib
import matplotlib.pyplot as plt
import pickle
import numpy

from sklearn.manifold import TSNE

import matplotlib.font_manager as fm
myfont = fm.FontProperties(fname="/home/lily/tensorflow/lib/python3.5/site-packages/matplotlib/mpl-data/fonts/ttf/SimHei.ttf")


def load_data():
    train_data_path = 'data1/train.pkl'
    val_data_path = 'data1/val.pkl'
    reverse_dictionary_path = 'data1/reverse_dictionary.pkl'
    
    train_data = pickle.load(open(train_data_path,'rb'))
    print("Loaded train data")
    val_data = pickle.load(open(val_data_path,'rb'))
    print("Loaded train data")
    reverse_dictionary = pickle.load(open(reverse_dictionary_path,'rb'))
    print("Loaded train data")
    return train_data, val_data, reverse_dictionary

def print_close_words(val_index, nearest, reverse_dictionary):
    val_word = reverse_dictionary[val_index]
    log_str = "Nearest to %s"%(val_word)
    for k in range(len(nearest)):
        close_word = reverse_dictionary[nearest[k]]
        log_str = "%s %s,"%(log_str, close_word)
    print(log_str)
    
def plot_with_labels(low_dim_embs, labels, filename="tsne.png"):
    assert low_dim_embs.shape[0] >= len(labels)
    plt.figure(figsize=(18,18))
    for i, label in enumerate(labels):
        x, y = low_dim_embs[i,:]
        plt.scatter(x, y)
        plt.annotate(label, xy=(x,y), xytext=(5,2), textcoords="offset points",
                    ha = "right", va="bottom",fontproperties=myfont)
    plt.savefig(filename)
    
def visualize_embeddings(final_embeddings, reverse_dictionary):
    tsne = TSNE(perplexity=30, n_components=2, init="pca", n_iter=5000)
    plot_only = 300
    low_dim_embs = tsne.fit_transform(final_embeddings[:plot_only,:])
    labels = [reverse_dictionary[i] for i in range(plot_only)]
    plot_with_labels(low_dim_embs, labels)

# word2vec
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import math
import numpy as np
import tensorflow as tf

from utils import *

batch_size = 128
vocabulary_size = 50000
embedding_size = 128
num_sampled = 64

train_data, val_data, reverse_dictionary=load_data()
print("number of training examples:", len(train_data)*batch_size)
print("number of validation examples:", len(val_data))

def skipgram():
    batch_inputs = tf.placeholder(tf.int32, shape = [batch_size,]) # or shape=[batch_size]
    batch_labels = tf.placeholder(tf.int32, shape = [batch_size,1])
    val_dataset = tf.constant(val_data, dtype = tf.int32)
    
    with tf.variable_scope("word2vec") as scope:
        embeddings = tf.Variable(tf.random_uniform([vocabulary_size, 
                                                    embedding_size], 
                                                    -1.0, 1.0)) # 注意这里的范围是-1.0,1.0
        batch_embeddings = tf.nn.embedding_lookup(embeddings, batch_inputs)

        weights = tf.Variable(tf.truncated_normal([vocabulary_size, 
                                                   embedding_size],
                                                   stddev=1.0/math.sqrt(embedding_size)))
        biases = tf.Variable(tf.zeros([vocabulary_size]))

        # This objective is maximized when the model assigns high probabilities
        # to the real words, and low probabilities to noise words.
        loss = tf.reduce_mean(tf.nn.nce_loss(weights=weights, 
                                             biases=biases,
                                             labels=batch_labels,
                                             inputs=batch_embeddings,
                                             num_sampled=num_sampled,
                                             num_classes=vocabulary_size))

        norm = tf.sqrt(tf.reduce_mean(tf.square(embeddings),1,keep_dims=True))
        normalized_embeddings = embeddings/norm

        val_embeddings = tf.nn.embedding_lookup(normalized_embeddings, val_dataset)
        similarity = tf.matmul(val_embeddings, normalized_embeddings, transpose_b=True)
        
    return batch_inputs, batch_labels, normalized_embeddings, loss, similarity
    
def run():
    batch_inputs, batch_labels, normalized_embeddings, loss, similarity = skipgram()
    optimizer = tf.train.GradientDescentOptimizer(1.0).minimize(loss)
    
    init = tf.global_variables_initializer()
    with tf.Session() as sess:
        sess.run(init)
        
        average_loss = 0.0
        for step, batch_data in enumerate(train_data):
            inputs, labels = batch_data
            feed_dict = {batch_inputs:inputs, batch_labels:labels}
            
            _, loss_val = sess.run([optimizer, loss], feed_dict)
            average_loss += loss_val
            
            if step % 1000 == 0:
                if step > 0:
                    average_loss /= 1000
                print("loss at iter", step, ":", average_loss)
                average_loss = 0
                
            if step % 5000 == 0:
                sim = similarity.eval()
                for i in range(len(val_data)):
                    top_k = 8
                    nearest = (sim[i,:]).argsort()[1:top_k+1]
                    print_close_words(val_data[i], nearest, reverse_dictionary)
                    
        final_embeddings = normalized_embeddings.eval()
        return final_embeddings
    
final_embeddings = run()

visualize_embeddings(final_embeddings,reverse_dictionary)
            


感觉应该去掉高频停用词,能从图中看到一些近似的关系。


#8

CIFAR10 AlexNet 实战

终于可以更新ALexNet在cifar10上面的了,代码还没有做优化,看了很多LRN并起不到很多作用的,之前基于tensorflow的官方教程API改过官方的实现的4,5层。这里基于别人代码的基础上修改了数据输入的类。啥都不说了。 代码如下。感觉看到的哪个fintune的blog写得挺好的,大家也可以去看看,代码上面提供了地址,哈哈。

下面的代码是基于官方的cifar10代码,以及两个人的blog代码修改的,代码中提供有原来代码的地址。

 
# cifar10_train.py

# -*- coding: utf-8 -*-
import numpy as np
import tensorflow as tf
from datetime import datetime
import os

from cifar10_config import get_conf
from cifar10_input import ImageDataGenerator
from alexnet import AlexNet

conf = get_conf()

IMAGE_SIZE = int(conf.get('data', 'image_size'))
TRAIN_SET_SIZE = int(conf.get('data', 'train_set'))
VALIDATION_SET_SIZE = int(conf.get('data', 'validation_set'))
TEST_SET_SIZE = int(conf.get('data', 'test_set'))
BATCH_SIZE = int(conf.get('train', 'batch_size'))
DECAY_STEP = float(conf.get('train', 'decay_step'))
INITIAL_LEARNING_RATE = float(conf.get('train', 'initial_learning_rate'))
LEARNING_RATE_DECAY_FACTOR = float(conf.get('train', 'learning_rate_decay_factor'))
MOVING_AVERAGE_DECAY = float(conf.get('train','moving_average_decay'))

MOMENTUM = float(conf.get('train', 'momentum'))
EPOCH_NUM = int(conf.get('train', 'epoch_num'))

DROPOUT = float(conf.get('train', 'dropout'))
NUM_CLASSES = int(conf.get('train','num_classes'))

DISPLAY_STEP = int(conf.get('train', 'display_step'))

# learning_rate = .001
# num_epochs = 10
#  batch_size = 128

# dropout_rate = 0.5
# num_classes = 2
train_layers = ['fc8','fc7'] #这里这个参数的设置没有意义

# display_step = 1


# tf.summary.FileWriter to store model checkpoints
filewriter_path = './AlexNet_cifar10'
checkpoint_path = './AlexNet_cifar10_check'

if not os.path.isdir(checkpoint_path):
    os.mkdir(checkpoint_path)
    
x = tf.placeholder(tf.float32, [BATCH_SIZE, 32,32,3])
y = tf.placeholder(tf.float32, [None, NUM_CLASSES])

x = tf.get_variable('x', shape=[BATCH_SIZE, IMAGE_SIZE,IMAGE_SIZE,3], trainable=False)
y = tf.get_variable('y', shape=[BATCH_SIZE, NUM_CLASSES], trainable=False)
keep_prob = tf.placeholder(tf.float32)

#x = tf.Variable(tf.random_normal(shape=[BATCH_SIZE, IMAGE_SIZE, IMAGE_SIZE,3]), name='x', trainable=False)
#y = tf.Variable(tf.random_normal(shape=[BATCH_SIZE, NUM_CLASSES]), name='y',trainable=False)

# 初始化模型
model = AlexNet(x, keep_prob, NUM_CLASSES, train_layers)

# 将变量作为模型的输出,模型的最后一层输出就是类的字段的fc8
score = model.fc8

# 训练网络所需要的所有的操作

# 网络所需要训练层的变量的list
# tf.trainable_variables(scope=None) 返回所有trainable=True的变量
# Variable()构造函数会直接将新的变量添加到计算涂的GraphKeys.TRAINABLE_VARIABLES
# var_list = [v for v in tf.trainable_variables() if v.name.split('/')[0] in train_layers]
# 这边没有训练好的模型,进行修改
var_list = [v for v in tf.trainable_variables()]

with tf.name_scope("cross_ent"):
    loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits = score, labels = y))
    
    # 滑动平均
    tf.add_to_collection('losses', loss)
   
        
    
with tf.name_scope("train"):
    # tf.gradients(xs,ys)依照在xs中的x计算ys之和的微分
    # 返回的是长度是len(xs)的tensor,中间每个单元是[sum([dy/dx for y in ys]) for x in xs]
    gradients = tf.gradients(loss, var_list)
    gradients = list(zip(gradients, var_list))
    
    # tf.train.get_or_create_global_step(graph=None) # 返回或者创建一个global step tensor
    global_step = tf.train.get_or_create_global_step() # 这个和下面这个创建变量的方法都可以,但是注意需要在apply_gradients里面加入global_step,否则不会变化
    # global_step = tf.Variable(0,trainable=False)
    # tf.train.exponential_decay: 将exponential decay用到learning rate,返回decayed_learning_rate = learning_rate*decay_rate^(global_step/decay_step)
    # staircase = True, 那么global_step/decay_step取整数值
    lr = tf.train.exponential_decay(INITIAL_LEARNING_RATE, global_step, DECAY_STEP, LEARNING_RATE_DECAY_FACTOR,staircase=True)
    
    # add_global = global_step.assign_add(1)
    
    optimizer = tf.train.GradientDescentOptimizer(lr)
    # 这一步是minimize()的第第二步,返回gradients的操作,第一步就是compute_gradients()
    # 也就是说minimize()==step 1:compute_gradients()返回gradients和var_list作为参数到step2:apply_gradients
    # tf.compute_gradients()与tf.gradients()基本一致,前一个函数调用tf.gradients计算梯度
    # 计算完梯度在apply之前可以对梯度做clipping,这个在RNN中间比较常用
    # clipped_grads, glob_norm(梯度的模,二阶范数和开方) = tf.clip_by_global_norm(gradients,clip_norm = config.max_grad_norm),梯度最大不超过clip_norm
    # 计算公式 gradients * clip_norm / max(glob_norm, clip_norm)
    # 注意这一部分非常重要,否则不会自动加1,必须要传入这个参数,否则lr不会衰减
    train_op = optimizer.apply_gradients(grads_and_vars = gradients, global_step = global_step) # train_op
    
    '''
    # 这里添加loss的梯度下降的方法:
    # 在tensorflow的官方教程里面
    # 'losses'这个collections当中添加了3,4层权重值的tf.nn.l2_loss(t)(sum(t**2)/2)作为losses,这里的implementation的原因未给。
    totalLoss = tf.add_n(tf.get_collection('losses'), name='total_loss')
    ema_loss = tf.train.ExponentialMovingAverage(0.9, name='avg')
    losses = tf.get_collection('losses')
    loss_average_op = ema_loss.apply(losses+[loss])
    
    for l in losses + [loss]:
        tf.summary.scalar(l.op.name+ '(raw)',l)
        tf.summary.scalar(l.op.name,ema_loss.average(l))
    
    with control_dependencies([loss_average_op]):
        gradients = tf.gradients(loss, var_list)
        gradients = list(zip(gradients, var_list))
        # tf.train.get_or_create_global_step(graph=None) # 返回或者创建一个global step tensor
        global_step = tf.train.get_or_create_global_step()
        # tf.train.exponential_decay: 将exponential decay用到learning rate,返回decayed_learning_rate = learning_rate*decay_rate^(global_step/decay_step)
        # staircase = True, 那么global_step/decay_step取正值
        lr = tf.train.exponential_decay(INITIAL_LEARNING_RATE, global_step, DECAY_STEP, LEARNING_RATE_DECAY_FACTOR,staircase=True)
        optimizer = tf.train.GradientDescentOptimizer(lr)
        # 这一步是minimize()的第第二步,返回gradients的操作,第一步就是compute_gradients()
        # 也就是说minimize()==step 1:compute_gradients()返回gradients和var_list作为参数到step2:apply_gradients
        # tf.compute_gradients()与tf.gradients()基本一致,前一个函数调用tf.gradients计算梯度
        # 计算完梯度在apply之前可以对梯度做clipping,这个在RNN中间比较常用
        # clipped_grads, glob_norm(梯度的模,二阶范数和开方) = tf.clip_by_global_norm(gradients,clip_norm = config.max_grad_norm),梯度最大不超过clip_norm
        # 计算公式 gradients * clip_norm / max(glob_norm, clip_norm)
        train_op = optimizer.apply_gradients(grads_and_vars = gradients) # train_op
    '''
    
    # 增加滑动平均
    ema = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
    with tf.control_dependencies([train_op]):
        variable_ema_op = ema.apply(tf.trainable_variables())

# 添加learning_rate到summary当中:
tf.summary.scalar('learning_rate', lr)

# 观察global_step值
tf.summary.scalar('global_step', global_step)

# 将gradients添加到summary当中
for gradient, var in gradients:
    # tf.summary.histogram(name, values, collections = None, family = None),输出是直方图
    tf.summary.histogram(var.name + '/gradient',gradient)

# 将训练的变量添加到summary当中    
for var in var_list:
    tf.summary.histogram(var.name, var)
    
# 添加loss到summary:
tf.summary.scalar('cross_entropy', loss)
    
# 模型评价:Evaluation op: Accuracy of the model
with tf.name_scope("accuracy"):
    correct_pred = tf.equal(tf.argmax(score, 1), tf.argmax(y, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
    
# 将所有的summary整合:
merged_summary = tf.summary.merge_all()

# 初始化FileWriter
writer = tf.summary.FileWriter(filewriter_path)

# 初始化存储model checkpoints的saver
saver = tf.train.Saver()

data_generator = ImageDataGenerator()

# 每个epoch的训练次数
train_batches_per_epoch = np.floor(TRAIN_SET_SIZE/BATCH_SIZE).astype(np.int32)
val_batches_per_epoch = np.floor(VALIDATION_SET_SIZE/BATCH_SIZE).astype(np.int32)
test_batches_per_epoch = np.floor(TEST_SET_SIZE/BATCH_SIZE).astype(np.int32)

with tf.Session() as sess:
    try:
        epochBef = 5
        ckpt = os.path.join(checkpoint_path, 'model_epoch'+str(epochBef)+'.ckpt')
        saver.restore(sess, ckpt)
        print("loaded the model")
    except:
        print("first time running")
        sess.run(tf.global_variables_initializer())
    
    # 将模型涂加到Tensorboard中
    writer.add_graph(sess.graph)
    
    print("{} Start training...".format(datetime.now()))
    print("{} Open Tensorboard at --logdir {}".format(datetime.now(), filewriter_path))
    
    f = open(filewriter_path+'result',mode='a+')
    
    for epoch in range(EPOCH_NUM):
        print("{} Epoch number: {}".format(datetime.now(), epoch+1))
        step = 1
        while step < train_batches_per_epoch:
            batch_xs, batch_ys = data_generator.get_train_batch_data(test=False,batch_size=BATCH_SIZE)
            sess.run([train_op], feed_dict={x:batch_xs, y:batch_ys, keep_prob:DROPOUT})
            # 这里运行指数滑动平均
            # sess.run(variable_ema_op, feed_dict={ keep_prob:DROPOUT})
            
            if step % DISPLAY_STEP == 0:
                s = sess.run(merged_summary, feed_dict={x:batch_xs, y:batch_ys, keep_prob:1.0})
                writer.add_summary(s, epoch*train_batches_per_epoch + step)
                
            step += 1
        
        print("{} Start validation".format(datetime.now()))
        val_acc = 0
        val_count = 0
        for _ in range(val_batches_per_epoch):
            batch_vx, batch_vy = data_generator.get_valid_batch_data(test=False, batch_size=BATCH_SIZE)
            acc = sess.run(accuracy, feed_dict={x:batch_vx, y:batch_vy, keep_prob:1.0})
            
            val_acc += acc
            val_count += 1
        val_acc /= val_count
        print("{}Validation Accuracy = {:.4f}".format(datetime.now(), val_acc))
        
        f.write("%.5f\n"%val_acc)
        f.flush()
        
        data_generator.reset_trainpointer(shuffle=True)
        data_generator.reset_validpointer()
        
        # 保存checkpoint
        print("{} Saving checkpoint of model...".format(datetime.now()))
        checkpoint_name = os.path.join(checkpoint_path, 'model_epoch'+str(epoch)+'.ckpt')
        save_path = saver.save(sess, checkpoint_name)
        print("{} Model checkpoint saved at {}".format(datetime.now(), checkpoint_name))
    
    print("{} Start test".format(datetime.now())) 
    test_acc = 0
    test_count = 0
    for _ in range(test_batches_per_epoch):
        batch_tx, batch_ty = data_generator.get_test_batch_data(test=True, batch_size=BATCH_SIZE)
        acc = sess.run(accuracy, feed_dict={x: batch_tx, y: batch_ty, keep_prob:1.0})
        
        test_acc += acc
        test_count += 1
    test_acc /= test_count
    print("{}test Accuracy = {:.4f}".format(datetime.now(), test_acc))
    
    f.write("%.5f\n"%test_acc)
    f.flush()
    f.close()  
 
# alexnet.py 请忽略中间那段关于python装饰器的。

# -*- coding: utf-8 -*-
import tensorflow as tf
import numpy as np

class AlexNet(object):

    def __init__(self, x, keep_prob, num_classes, skip_layer, weights_path="DEFAULT"):
        """
        x: tf.placeholder, input images
        keep_prob: tf.placeholder, dropout_rate
        num_classes: int, number of classes of the new dataset
        skip_layer: list of strings, names of the layers you want to reinitialize
        weights_path: path string, path to the pretrained_weights(如果参数文件不在同一个文件夹当中,指定参数的位置和文件名的string)
        """
        
        self.X = x
        self.NUM_CLASSES = num_classes
        self.KEEP_PROB = keep_prob
        self.SKIP_LAYER = skip_layer
        # self.IS_TRAINING = is_train 在这里根本没有这个参数?
        
        if weights_path == 'DEFAULT':
            self.WEIGHTS_PATH = 'bvlc_alexnet.npy' # 参数文件
        else:
            self.WEIGHTS_PATH = weights_path
            
        self.create()
    
    def create(self):
        '''
        build the computational graph for alexnet
        '''
        '''
        # 1st layer: Conv(w ReLU) -> Pool -> Lrn
        # 11*11*96 filter, strides=4, padding='VALID'
        conv1 = conv(self.X, 11, 11, 96, 4, 4, padding='VALID',name='conv1')
        norm1 = lrn(conv1, 2, 1e-05, 0.75, name='norm1')
        pool1 = max_pool(norm1, 3, 3, 2, 2, padding='VALID', name='pool1')
        
        # 2nd layer, Conv(w ReLU) -> Pool -> Lrn  groups=2
        conv2 = conv(pool1, 5, 5, 256, 1, 1, groups=2, name='conv2')
        norm2 = lrn(conv2, 2, 1e-05, 0.75, name='norm2')
        pool2 = max_pool(norm2, 3, 3, 2, 2, padding='VALID', name='pool2')
        
        # 3rd layer: Conv(w ReLU)
        conv3 = conv(pool2, 3, 3, 384, 1, 1, name='conv3')
        
        # 4th layer: Conv(w ReLU) 分为2个groups
        conv4 = conv(conv3, 3, 3, 384, 1, 1, groups = 2, name='conv4')
        
        # 5th layer: Conv(w ReLU) 分为两组
        conv5 = conv(conv4, 3, 3, 256, 1, 1, groups=2, name='conv5')
        pool5 = max_pool(conv5, 3, 3, 2, 2, padding='VALID', name='pool5')
        
        # 6th layer;Flatten -> FC (w ReLu) -> Dropout
        flattened = tf.reshape(pool5, [-1, 6*6*256])
        fc6 = fc(flattened, 6*6*256, 4096, name='fc6')
        dropout6 = dropout(fc6, self.KEEP_PROB)
        
        # 7th layer: FC (w ReLu) -> Dropout
        fc7 = fc(dropout6, 4096, 4096, name='fc7')
        dropout7 = dropout(fc7, self.KEEP_PROB)
        
        self.fc8 = fc(dropout7, 4096, self.NUM_CLASSES, relu=False, name='fc8')
        '''
        # 1st layer: Conv(w ReLU) -> Pool -> Lrn
        # 11*11*96 filter, strides=4, padding='VALID'
        conv1 = conv(self.X, 3, 3, 64, 1, 1, padding='VALID',name='conv1')
        norm1 = lrn(conv1, 2, 1e-05, 0.75, name='norm1')
        pool1 = max_pool(norm1, 3, 3, 2, 2, padding='VALID', name='pool1')
        
        # 2nd layer, Conv(w ReLU) -> Pool -> Lrn  groups=2
        conv2 = conv(pool1, 3, 3, 64, 1, 1, groups=2, name='conv2')
        norm2 = lrn(conv2, 2, 1e-05, 0.75, name='norm2')
        pool2 = max_pool(norm2, 3, 3, 2, 2, padding='VALID', name='pool2')
        
        # 3rd layer: Conv(w ReLU)
        conv3 = conv(pool2, 3, 3, 384, 1, 1, name='conv3')
        
        # 4th layer: Conv(w ReLU) 分为2个groups
        conv4 = conv(conv3, 3, 3, 384, 1, 1, groups = 2, name='conv4')
        
        # 5th layer: Conv(w ReLU) 分为两组
        conv5 = conv(conv4, 3, 3, 256, 1, 1, groups=2, name='conv5')
        pool5 = max_pool(conv5, 3, 3, 2, 2, padding='VALID', name='pool5')
        
        # 6th layer;Flatten -> FC (w ReLu) -> Dropout
        flattened = tf.reshape(pool5, [-1, 1*1*256])
        fc6 = fc(flattened, 1*1*256, 1024, name='fc6')
        dropout6 = dropout(fc6, self.KEEP_PROB)
        
        # 7th layer: FC (w ReLu) -> Dropout
        fc7 = fc(dropout6, 1024, 1024, name='fc7')
        dropout7 = dropout(fc7, self.KEEP_PROB)

        
        self.fc8 = fc(dropout7, 1024, self.NUM_CLASSES, relu=False, name='fc8')
        
    
    def load_initial_weights(self, session):
        weights_dict = np.load(self.WEIGHTS_PATH, encoding='bytes').item()
        
        for op_name in weights_dict:
            if op_name not in self.SKIP_LAYER:
                with tf.variable_scope(op_name, reuse=True):
                    for data in weights_dict[op_name]:
                        if len(data.shape) == 1:
                            var = tf.get_variable('biases', trainable=False)
                            session.run(var.assign(data)) # tf.assign(var, data)
                        else:
                            var = tf.get_variable('weights', trainable=False)
                            session.run(var.assign(data))   
        
    
    
    # @classmethod是类的方法,也可以不用实例化访问,但是要求第一个参数表示类,也就是def fun(cls,),cls表示这个类本身,而不是类的实例
    # 当子类继承上面定义了类的方法的类,子类就拥有这个类的方法。
    # @classmethod可以看作是方法的重载
    '''
    class Date(object):

        def __init__(self, day=0, month=0, year=0):
            self.day = day
            self.month = month
            self.year = year

        @classmethod
        def from_string(cls, date_as_string):
            day, month, year = map(int, date_as_string.split('-'))
            date1 = cls(day, month, year)
            return date1 #返回类,可以查看类的字段(field)

        @staticmethod
        def is_date_valid(date_as_string):
            day, month, year = map(int, date_as_string.split('-'))
            return day <= 31 and month <= 12 and year <= 3999

    date2 = Date.from_string('11-09-2012')
    is_date = Date.is_date_valid('11-09-2012')
    '''
    # 上面的两种都可以直接通过类调用,而不用通过类的实例化对象访问。
    # @property 是定义类的字段的getter的方法, @XX(getter的方法名字).setter()是定义类的字段的setter()方法。   
def conv(x, filter_height, filter_width, num_filters, stride_y, stride_x, name, padding='SAME', groups=1):
    input_channels= int(x.get_shape()[-1])
    # tf.nn.conv2d(input, filter, strides, padding)
    # filter: a tensor 和输入的type一样 ,4Dshape,[filter_height, filter_width, in_channels, out_channels],out_channels就是filter的个数
    convolve = lambda i, k: tf.nn.conv2d(i,k,strides = [1, stride_y, stride_x, 1],padding = padding)
        
    with tf.variable_scope(name) as scope:
        weights = tf.get_variable('weights', shape = [filter_height, filter_width, input_channels/groups, num_filters])
        biases = tf.get_variable('biases',shape = [num_filters])
        
        # 这里的group convs是把channel分组的方式,因为当时GPU内存不够
    if groups == 1:
        conv = convolve(x, weights)
    else:
        #tf.split(value,num_or_size_splits,axis=0), num_or_size_splits是scalar的时候均分,如果是tensor的时候就是代表需要分的axis要分段的长度大小
        input_groups = tf.split(value = x, num_or_size_splits=groups, axis=3)
        weight_groups = tf.split(value = weights, num_or_size_splits=groups, axis=3)
        output_groups = [convolve(i, k) for i, k in zip(input_groups, weight_groups)]
                
        conv = tf.concat(values = output_groups, axis = 3)
                
        #tf.nn.bias_add(value, bias) 将bias加到value上    
    bias = tf.reshape(tf.nn.bias_add(conv,biases), conv.get_shape().as_list())
            
    relu = tf.nn.relu(bias, name = scope.name)
    return relu
        
def fc(x, num_in, num_out, name, relu = True):
    with tf.variable_scope(name) as scope:
        weights = tf.get_variable('weights', [num_in, num_out], trainable=True) # Trainable默认为True
        biases = tf.get_variable('biases', [num_out], trainable=True)
            
        act = tf.nn.xw_plus_b(x, weights, biases, name=scope.name)
            
    if relu == True:
        relu = tf.nn.relu(act)
        return relu
    else:
        return act
    
def max_pool(x, filter_height, filter_width, stride_y, stride_x, name, padding='SAME'):
    return tf.nn.max_pool(x, ksize=[1,filter_height, filter_width, 1], strides = [1, stride_y, stride_x, 1],
                              padding = padding, name = name)
                              
def lrn(x, radius, alpha, beta, name, bias=1.0):
    return tf.nn.local_response_normalization(x, depth_radius=radius, alpha=alpha, beta=beta, bias = bias, name=name)
        
def dropout(x, keep_prob):
    return tf.nn.dropout(x, keep_prob)
        
 
# cifar10_input.py
# -*- coding: utf-8 -*-
# 修改自https://github.com/diudiuzhi/TensorAlexnet/blob/master/input.py
# https://zhuanlan.zhihu.com/p/32713815
import pickle
import numpy as np
import tensorflow as tf
from cifar10_config import get_conf
import random

class ImageDataGenerator():
    # 模型参数
    conf = get_conf()
    DATA_DIR = conf.get('data', 'dir')
    TRAIN_SET_SIZE = int(conf.get('data', 'train_set'))
    VALIDATION_SET_SIZE = int(conf.get('data', 'validation_set'))
    TEST_SET_SIZE = int(conf.get('data', 'test_set'))
    IMAGE_SIZE = int(conf.get('data', 'image_size'))
    NUM_CLASSES = int(conf.get('train','num_classes'))

    
    def __init__(self, test = False, shuffle = False):
        self.test = test
        self.shuffle = shuffle
        
        self.trainpointer = 0 
        self.validpointer = 0
        self.testpointer = 0
        self.get_data_from_file()
        self.TRAIN_IMAGES = []
        self.TRAIN_LABLES = []

        self.VALIDATION_IMAGES = []
        self.VALIDATION_LABELS = []

        self.TEST_IMAGES = []
        self.TEST_LABELS = []
        
        if self.shuffle:
            self.shuffle_data()
            
        self.get_data_from_file()
        
        
    def unpickle(self, data_dir):
        with open(data_dir, 'rb') as f:
            dict = pickle.load(f, encoding='latin1')# 8bit字符集,也可以encoding='bytes'
        return dict
        
        
    def get_data_from_file(self):
        #train and valid data
        t_v_datas, t_v_labels = self._get_train_and_validation_data()
    
        # test data 
        t_datas, t_labels = self._get_test_data()
    
        # calc mean
        train_mean = np.mean(t_v_datas[:ImageDataGenerator.TRAIN_SET_SIZE], axis=0)
        validation_mean = np.mean(t_v_datas[ImageDataGenerator.TRAIN_SET_SIZE:], axis=0)
        test_mean = np.mean(t_datas, axis=0)
    
        # get train dataset
        self.TRAIN_IMAGES = t_v_datas[:ImageDataGenerator.TRAIN_SET_SIZE] - train_mean
        self.TRAIN_LABELS = t_v_labels[:ImageDataGenerator.TRAIN_SET_SIZE]
    
        # get validation dataset
        self.VALIDATION_IMAGES = t_v_datas[ImageDataGenerator.TRAIN_SET_SIZE:] - validation_mean
        self.VALIDATION_LABELS = t_v_labels[ImageDataGenerator.TRAIN_SET_SIZE:]

        # get test dataset
        self.TEST_IMAGES = t_datas[:ImageDataGenerator.TEST_SET_SIZE] - test_mean
        self.TEST_LABELS = t_labels[:ImageDataGenerator.TEST_SET_SIZE]
    
    def _get_train_and_validation_data(self):
        datas = self.unpickle(ImageDataGenerator.DATA_DIR+'data_batch_1')
        t_v_datas = datas['data']
        t_v_labels = datas['labels']
        
        for i in range(2,6):
            datas = self.unpickle(ImageDataGenerator.DATA_DIR+'data_batch_'+str(i))
            images = datas['data']
            labels = datas['labels']
            
            t_v_datas = np.vstack((t_v_datas,images)) #按照第一个维度stack,垂直堆叠,row wise
            t_v_labels = t_v_labels + labels
            
        return t_v_datas, t_v_labels
        
    def _get_test_data(self):
        datas = self.unpickle(ImageDataGenerator.DATA_DIR+'test_batch')
        images = datas['data']
        labels = datas['labels']
    
        return images, labels
        
    def shuffle_data(self):
        # 原有的数据集,IMAGES使用ndarray存储的,LABELS是使用list存储的
        idx = np.arange(ImageDataGenerator.TRAIN_SET_SIZE)
        np.random.shuffle(idx)
        images = self.TRAIN_IMAGES.copy()
        labels = self.TRAIN_LABELS.copy()
        for i,j in enumerate(idx):
            self.TRAIN_IMAGES[i] = images[j]
            self.TRAIN_LABELS[i] = labels[j]
            
        del images
        del labels

    def reset_trainpointer(self, shuffle=True):
        self.shuffle=shuffle
        self.trainpointer = 0
        
        if self.shuffle:
            self.shuffle_data()
            
    def reset_validpointer(self):
        self.validpointer = 0
        
    def reset_testpointer(self):
        self.testpointer = 0         
        
    def _distorted_image(self, image):
        height = ImageDataGenerator.IMAGE_SIZE
        width = ImageDataGenerator.IMAGE_SIZE
        
        # print(image.shape)
        image_height = image.shape[0]
        image_width = image.shape[1]
        
        nh = random.randint(0, image_height - height)
        nw = random.randint(0, image_width - width)
        
        reshape_image = image.astype(float)
        distorted_image = reshape_image[nh: nh+height, nw: nw+height]
        
        if not self.test:
            # max_pixel = np.max(distorted_image)
            # min_pixel = np.min(distorted_image)
            if np.random.random() < 0.5:
                distorted_image = np.flip(distorted_image, axis=0)
            # 以下部分没有进行亮度和对比度的改变,因为图片的像素已经中心化了,所以对像素,不知道是不是要clipping到
            # [-225/2,225/2]中间,暂时不做处理
            '''
            # 改变亮度,按照tf.random_brightness(image, max_delta, seed=None)源码来的,亮度就是增加一个常量值?
            detla = 63
            rand_delta = np.random.uniform(-delta, delta ,size = [])
            distorted_image = distorted_image+rand_delta # 这里操作之后不满足要求clipping
            
            # 没有找到implementation的源码
            # 调整图片对比度 tf.random_contrast(image, lower, upper, seed=None)
            lower = .2
            upper = 1.8
            contrast_factor = np.random.uniform(lower, upper, size=[])
            min_pixel = np.min(distorted_image)
            diff = np.max(distorted_image) - np.min(distorted_image)
            distorted_image = (distorted_image - min_pixel)/diff * contrast_factor
            '''
        return distorted_image
    
    def get_train_batch_data(self, test, batch_size):
        self.test = test
    
        images = self.TRAIN_IMAGES[self.trainpointer:self.trainpointer+batch_size]
        labels = self.TRAIN_LABELS[self.trainpointer:self.trainpointer+batch_size]
        self.trainpointer += batch_size
        
        return_images = np.ndarray([batch_size,ImageDataGenerator.IMAGE_SIZE,ImageDataGenerator.IMAGE_SIZE,3]) 
        for i,image in enumerate(images):
            image = image.reshape(3,32,32)
            image = image.transpose([1,2,0])
            image = self._distorted_image(image)
            return_images[i] = image
            
        one_hot_labels = np.zeros((batch_size, ImageDataGenerator.NUM_CLASSES))
        for i in range(batch_size):
            one_hot_labels[i][labels[i]] = 1
        
        del images
        del labels
            
        return return_images, one_hot_labels        
        
    def get_valid_batch_data(self, test, batch_size):
        self.test = test
    
        images = self.VALIDATION_IMAGES[self.validpointer:self.validpointer+batch_size]
        labels = self.VALIDATION_LABELS[self.validpointer:self.validpointer+batch_size]
        self.validpointer += batch_size
        
        return_images = np.ndarray([batch_size,ImageDataGenerator.IMAGE_SIZE,ImageDataGenerator.IMAGE_SIZE,3]) 
        for i,image in enumerate(images):
            image = image.reshape(3,32,32)
            image = image.transpose([1,2,0])
            image = self._distorted_image(image)
            return_images[i] = image
            
        one_hot_labels = np.zeros((batch_size, ImageDataGenerator.NUM_CLASSES))
        for i in range(batch_size):
            one_hot_labels[i][labels[i]] = 1
            
        del images
        del labels
            
        return return_images, one_hot_labels 
        
    def get_test_batch_data(self, test, batch_size):
        self.test = test
    
        images = self.TEST_IMAGES[self.testpointer:self.testpointer+batch_size]
        labels = self.TEST_LABELS[self.testpointer:self.testpointer+batch_size]
        self.testpointer += batch_size
        
        return_images = np.ndarray([batch_size,ImageDataGenerator.IMAGE_SIZE,ImageDataGenerator.IMAGE_SIZE,3]) 
        for i,image in enumerate(images):
            image = image.reshape(3,32,32)
            image = image.transpose([1,2,0])
            image = self._distorted_image(image)
            return_images[i] = image
            
        one_hot_labels = np.zeros((batch_size, ImageDataGenerator.NUM_CLASSES))
        for i in range(batch_size):
            one_hot_labels[i][labels[i]] = 1
            
        del images
        del labels
            
        return return_images, one_hot_labels 
        

 
# conf.ini
# 这里别人用了momentum,我没有采用,但是没有删除配置

[data]
dir = cifar-10-batches-py/

# Total of train set and valiation set is 50000
train_set = 45000
validation_set = 5000
test_set = 10000

image_size = 24

[train]
batch_size = 128
epoch_num = 1000

# Learning rate
decay_step =10
initial_learning_rate = 0.01
learning_rate_decay_factor = 0.9997
moving_average_decay = 0.9999
num_classes = 10

momentum = 0.9
dropout = 0.5

display_step = 1


# cifar10_config.py

import configparser # 读取配置文件

def get_conf(): # 这里定义一个配置文件config.ini; 以后可以从配置文件当中修改训练的参数
    cf = configparser.ConfigParser()
    cf.read("config.ini")
    return cf

#9

超过可以更新长度了,所以在这里添加fintune微调的代码。


# 这里是只更新网络某些部分的参数,适合迁移学习类的,原博文链接在下面,欢迎大家阅读。这个代码主体就是基于这个博文修改的
# finetune.py
# 以下代码未经运行,可能有些许错误
'''
code from 
https://github.com/kratzert/finetune_alexnet_with_tensorflow/tree/5d751d62eb4d7149f4e3fd465febf8f07d4cea9d
blog:
https://kratzert.github.io/2017/02/24/finetuning-alexnet-with-tensorflow.html#finetune
'''
import numpy as np
import tensorflow as tf
from datetime import datetime
from alexnet import AlexNet
from datagenerator import ImageDataGenerator


train_file = 
val_file = 

learning_rate = .001
num_epochs = 10
batch_size = 128

dropout_rate = 0.5
num_classes = 2
train_layers = ['fc8','fc7']

display_step = 1


# tf.summary.FileWriter to store model checkpoints
filewriter_path = 'AlexNet_cifar10'
checkpoint_path = 'AlexNet_cifar10_check'

if not os.path.isdir(checkpoint_path):
    os.mkdir(checkpoint_path)
    
x = tf.placeholder(tf.float32, [batch_size, 227,227,3])
y = tf.placeholder(tf.float32, [None, num_classes])
keep_prob = tf.placeholder(tf.float32)

# 初始化模型
model = AlexNet(x, keep_prob, num_classes, train_layers)

# 将变量作为模型的输出
score = model.fc8

# 训练网络所需要的所有的操作

# 网络所需要训练层的变量的list
# tf.trainable_variables(scope=None) 返回所有trainable=True的变量
# Variable()构造函数会直接将新的变量添加到计算涂的GraphKeys.TRAINABLE_VARIABLES
var_list = [v for v in tf.trainable_variables() if v.name.split('/')[0] in train_layers]

with tf.name_scope("cross_ent"):
    loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits = score, labels = y))
    
with tf.name_scope("train"):
    # tf.gradients(xs,ys)依照在xs中的x计算ys之和的微分
    # 返回的是长度是len(xs)的tensor,中间每个单元是[sum([dy/dx for y in ys]) for x in xs]
    gradients = tf.gradients(loss, var_list)
    gradients = list(zip(gradients, var_list))
    
    optimizer = tf.train.GradientDescentOptimizer(learning_rate)
    # 这一步是minimize()的第第二步,返回gradients的操作,第一步就是compute_gradients()
    # 也就是说minimize()==step 1:compute_gradients()返回gradients和var_list作为参数到step2:apply_gradients
    # tf.compute_gradients()与tf.gradients()基本一致,前一个函数调用tf.gradients计算梯度
    # 计算完梯度在apply之前可以对梯度做clipping,这个在RNN中间比较常用
    # clipped_grads, glob_norm(梯度的模,二阶范数和开方) = tf.clip_by_global_norm(gradients,clip_norm = config.max_grad_norm),梯度最大不超过clip_norm
    # 计算公式 gradients * clip_norm / max(glob_norm, clip_norm)
    train_op = optimizer.apply_gradients(grads_and_vars = gradients)

# 将gradients添加到summary当中
for gradient, var in gradients:
    # tf.summary.histogram(name, values, collections = None, family = None),输出是直方图
    tf.summary.histogram(var.name + '/gradient',gradient)

# 将训练的变量添加到summary当中    
for var in var_list:
    tf.summary.histogram(var.name, var)
    
# 添加loss到summary:
tf.summary.scalar('cross_emtropy', loss)
    

# 模型评价:Evaluation op: Accuracy of the model
with tf.name_scope("accuracy"):
    correct_pred = tf.equal(tf.argmax(score, 1), tf.argmax(y, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
    
# 将所有的summary整合:
merged_summary = tf.summary.merge_all()

# 初始化FileWriter
writer = tf.summary.FileWriter(filewriter_path)

# 初始化存储model checkpoints的saver
saver = tf.train.Saver()

# 初始化数据迭代器
train_generator = ImageDataGenerator(train_file, horizontal_flip = True, shuffle=True)
val_generator = ImageDataGenerator(val_file, shuffle = False)

# 每个epoch的训练次数
train_batches_per_epoch = np.floor(train_generator.data_size/batch_size).astype(np.int32)
val_batches_per_epoch = np.floor(val_generator.data_size/batch_size).astype(np.int32)

# 
with tf.Session() as sess:
    sess.run(tf.global_variables_initalizer())
    
    # 将模型涂加到Tensorboard中
    writer.add_graph(sess.graph)
    
    model.load_initial_weights(sess)
    
    print("{} Start training...".format(datetime.now()))
    print("{} Open Tensorboard at --logdir {}".format(datetime.now(), epoch+1))
    
    
    for epoch in range(num_epochs):
        print("{} Epoch number: {}".format(datetime.now(), epoch+1))
        
        step = 1
        while step < train_batches_per_epoch:
            batch_xs, batch_ys = train_generator.next_batch(batch_size)
            sess.run(train_op, feed_dict={x:bach_xs, y: batch_ys, keep_prob:dropout_rate})
        
            if step % display_step == 0:
                s = sess.run(merged_summary, feed_dict={x:batch_xs, y:batch_ys, keep_prob:1.0})
                writer.add_summary(s, epoch*train_batches_per_epoch + step)
            
            step += 1
        
        print("{} Start validation".format(datetime.now()))
        test_acc = 0
        test_count = 0
        for _ in range(val_batches_per_epoch):
            batch_tx, batch_ty = val_generator.next_batch(batch_siz)
            acc = sess.run(accuracy, feed_dict={x:batch_tx, y:batch_ty, keep_prob:1.0})
        
            test_acc += acc
            test_count += 1
        test_acc /= test_count
        print("Validation Accuracy = {:.4f}".format(datetime.now(), test_acc))
    
        val_generator.reset_pointer()
        train_generator.reset_pointer()
    
        # 保存checkpoint
        print("{} Saving checkpoint of model...".format(datetime.now()))
        checkpoint_name = os.path.join(checkpoint_path, 'model_epoch'+str(epoch)+'.ckpt')
        save_path = saver.save(sess, checkpoint_name)
        print("{} Model checkpoint saved at {}".format(datetime.now(), checkpoint_name))

#10

在这里,模型融合类,mark一下:
参考文献:


#11

RCNN

region RCNN

https://arxiv.org/abs/1311.2524

RCNN目标实现流程:

  1. 在图像中确定1000-2000个候选框;
  2. 对与每个候选框内图像块,使用深度网络提取特征;
  3. 对候选框提取出来的特征使用SVM分类器判别是否属于同一个类别,也就是是不是属于当前的类别;
  4. 使用回归器精细修正候选框的位置。

候选区域(region proposal)生成方法:(原论文中提及了多种方法,实际上也可以采用任意算法,因为候选区域生成和后续步骤相对独立
采用了Selective Search的方法从一张图像中生成1000~2000个候选区域。


算法如下:

  1. 实现用efficient graph-based image segementation得到初始化的特征区域R = {r1,r2,…,rn}。首先将相似集合置为空集S。
  2. 对任意的两个相邻的区域,计算他们的相似性s(ri,rj),放入相似集合S当中。
  3. 当S集合不为空时,执行下面的操作:
    从相似集合S当中获取最大的相似性的两个区域,合并这里那个区域生成rt,完成后将这两个区域相关的相似度从相似集合当中移除。
    计算rt和它相邻区域的相似度,将这个相似度放入相似集合当中,将rt并入当特征区域R当中。
  4. 从所有的在R的region里面获取object location boxes L。

合并的规则: 优先合并以下四种区域:

  1. 颜色(颜色直方图)相近的
  2. 纹理(梯度直方度)相近的
  3. 合并后总面积小(保证合并操作的尺度较为均匀,避免大区域吃掉小区域)
  4. 合并后,总面集在bbox中所占比例大。(保证合并和的形状规则)

RCNN网络结构如下:
image

参考文献:
https://blog.csdn.net/shenxiaolu1984/article/details/51066975

Fast-RCNN


解决RCNN的三个问题:

  1. 测试时速度慢,因为需要对所有的region proposal做计算。 RCNN一整图像内的候选框有大量重叠的地方,提取特征操作荣誉。所以在Fast-RCNN上面直接将整张图片归一化之后直接送入网络当中。在卷积完之后的feature map上面加入候选框信息。在后面的全连接层才对每个候选框进行前向计算。

  2. 训练时速度慢:因为针对不同的区域都要单独训练,所以训练的时间长。
    这里由于是在卷积层之后的特征map上面选择候选框(region proposal),所以前面的卷积计算的过程当中不需要重复计算这些特征区域,降低了时间。

  3. 训练所需要的客空间大:因为这里的特征数目大,对每张图片都需要事先提取出region proposal区域,对每个区域使用独立的分类器和回归器训练,需要大量的硬盘的存储空间。
    在fast-RCNN里面,直接用一个网络实现,不需要再利用其他的存储的空间。

网络中的RoI(region of interest):是一种特殊的金字塔池化(用于SPPnets) 是池化层,利用最大池化将任意的有效的region of interest转换到一个比较小的固定尺寸的feature map。 ROI是一个长方形,通过一个元组(r,c,h,w)来定义,topleft是(r,c),高和宽是(h,w)。

后向传播的过程 普通的 max pooling层。设xi为输入层的节点,yj为输出层的节点。 $$\frac{\partial L}{\partial x_i}=\begin{cases}0&\delta(i,j)=false\ \frac{\partial L}{\partial y_j} & \delta(i,j)=true\end{cases}$$ 其中判决函数δ(i,j)表示i节点是否被j节点选为最大值输出。不被选中有两种可能:xi不在yj范围内,或者xi不是最大值。
roi max pooling,一个输入节点可能和多个输出节点相连。设xi为输入层的节点,yrj为第r个候选区域的第j个输出节点。
$$\frac{\partial L}{\partial x_i}=\Sigma_{r,j}\delta(i,r,j)\frac{\partial L}{\partial y_{rj}}$$

image

有两层输出,一个是每个RoI的概率,(K+1)维, 一个是box值,用于回归。

全连接层这里对权重W进行了SVD分解:
$ W=U\Sigma V^T\approx U(:,1:t) \cdot \Sigma(1:t,1:t) \cdot V(:,1:t)^T$ 只取前t个变量,所以原来的前向传播的过程变为两步:
$ y=Wx = U\cdot (\Sigma \cdot V^T) \cdot x = U \cdot z $ 这样计算复杂度由原来的u×v变为了u×t+v×t。
计算可视化如下:

基本网络结构如下:

这个网络利用ImageNet的数据训练网络的初始化参数,训练的网络结构如下:

在全连接层中执行的两个过程如下所示:

参考文献:(以上除了三个问题的修改之外,基本图片都来源于这个博客)
https://blog.csdn.net/shenxiaolu1984/article/details/51036677

Faster_RCNN

加入了PRN(region proposal network)这里神经网络自己学习从features中提取候选区域(proposal)。 PRN:输入一张图片,输出一系列的矩形目标候选区,每一个都有目标分数(关于目标类别集合的)。这个过程通过全卷积网络完成。(实现中提取特征的网络用ZF或者VGG-16完成)。 Anchor
在这里,共享的特征提取的feature map层用一个比较小的fiter进行卷积,stride=1。对于这个滑动窗口的未知,同时预测多个region proposal,最大的候选区域的数目是K,所以regression(reg)层有4k层, box-classification(cls)层有2k个分数表明对每个候选区域是这个object和不是这个object的概率。K个候选区域的参数与k个reference boxes相关,这些reference boxes被称为anchors。 anchors是以filter,也就是当前的滑动窗口的中心为中心点的。 默认情况下我们用三个尺度和三个纵横比(aspect ratio),这样就可以获得k=9个anchors在每个滑动的未知。也就是说对应一张featuremap的大小是W*H的,总共会哟你WHk个anchor。

特征可以看做一个尺度51*39的256通道图像,对于该图像的每一个位置,考虑9个可能的候选窗口:三种面积{1282,2562,5122}×三种比例{1:1,1:2,2:1}。这些候选窗口称为anchors。下图示出51*39个anchor中心,以及9种anchor示例。 image

在这里Faster-RCNN中有四个loss:

  1. PRN是否是目标
  2. PRNbox坐标的回归loss
  3. 最后一层分类分数
  4. 最后一个box的坐标回归的loss。

以下内容转载自: https://blog.csdn.net/shenxiaolu1984/article/details/51152614

从RCNN到fast RCNN,再到本文的faster RCNN,目标检测的四个基本步骤(候选区域生成,特征提取,分类,位置精修)终于被统一到一个深度网络框架之内。所有计算没有重复,完全在GPU中完成,大大提高了运行速度。

窗口分类和位置精修

分类层(cls_score)输出每一个位置上,9个anchor属于前景和背景的概率;窗口回归层(bbox_pred)输出每一个位置上,9个anchor对应窗口应该平移缩放的参数。 对于每一个位置来说,分类层从256维特征中输出属于前景和背景的概率;窗口回归层从256维特征中输出4个平移缩放参数。

就局部来说,这两层是全连接网络;就全局来说,由于网络在所有位置(共51*39个)的参数相同,所以实际用尺寸为1×1的卷积网络实现。

实际代码中,将51*39*9个候选位置根据得分排序,选择最高的一部分,再经过Non-Maximum Suppression获得2000个候选结果。之后才送入分类器和回归器。
所以Faster-RCNN和RCNN, Fast-RCNN一样,属于2-stage的检测算法。

区域生成网络:训练

样本

考察训练集中的每张图像: a. 对每个标定的真值候选区域,与其重叠比例最大的anchor记为前景样本 b. 对a)剩余的anchor,如果其与某个标定重叠比例大于0.7,记为前景样本;如果其与任意一个标定的重叠比例都小于0.3,记为背景样本 c. 对a),b)剩余的anchor,弃去不用。 d. 跨越图像边界的anchor弃去不用


#12

以下内容转载自: http://www.caffecn.cn/?/question/255

先上Paper列表:

大体思路:

  • Inception v1的网络,将1x1,3x3,5x5的conv和3x3的pooling,stack在一起,一方面增加了网络的width,另一方面增加了网络对尺度的适应性;
  • v2的网络在v1的基础上,进行了改进,一方面了加入了BN层,减少了Internal Covariate Shift(内部neuron的数据分布发生变化),使每一层的输出都规范化到一个N(0, 1)的高斯,另外一方面学习VGG用2个3x3的conv替代inception模块中的5x5,既降低了参数数量,也加速计算;
  • v3一个最重要的改进是分解(Factorization),将7x7分解成两个一维的卷积(1x7,7x1),3x3也是一样(1x3,3x1),这样的好处,既可以加速计算(多余的计算能力可以用来加深网络),又可以将1个conv拆成2个conv,使得网络深度进一步增加,增加了网络的非线性,还有值得注意的地方是网络输入从224x224变为了299x299,更加精细设计了35x35/17x17/8x8的模块;
  • v4研究了Inception模块结合Residual Connection能不能有改进?发现ResNet的结构可以极大地加速训练,同时性能也有提升,得到一个Inception-ResNet v2网络,同时还设计了一个更深更优化的Inception v4模型,能达到与Inception-ResNet v2相媲美的性能。

更多的细节可以参考论文中的描述

Inception V1:


image
一、inception v1的主要贡献

1、提出inception architecture并对其优化
2、取消全连层
3、运用auxiliary classifiers加速网络converge

在week3 当中已经有过介绍,请查看上面内容

Inception V2:


主要改进是加入了BN层
参考下面博文的内容,博文中写了添加gamma和beta是为了让输出不完全在sigmoid的线性部分。在CS231n中提到的是为了让网络自己学习参数,能够子集决定数据的缩放区间。
https://blog.csdn.net/happynear/article/details/44238541

Inception V3:(在inceptionv3中提出的v2)

image
下面这幅图片中把5×5的卷积换成了两个3×3的卷积,参数变少了(小型化,多层化):
image
下面这个是将n*n的卷积转换为1*n和n*1的模型
image
image
image
image
论文做出的贡献主要有4个:

1、分解大filters,使其小型化、多层化,其中有个“非对称卷积”很新颖
2、优化inception v1的auxiliary classifiers
3、提出一种缩小特征图大小的方法,说白了就是一种新的、更复杂的pooling层
4、Label smooth,“标签平滑”,很难用中文说清楚的一种方法

Szegedy还把一段时间内的科研心得总结了一下,在论文里写了4项网络设计基本原则:

1、尽量避免representational bottlenecks,这种情况一般发生在pooling层,字面意思是,pooling后特征图变小了,但有用信息不能丢,不能因为网络的漏斗形结构而产生表达瓶颈,解决办法是上面提到的贡献3
2、采用更高维的表示方法能够更容易的处理网络的局部信息,我承认前面那句话是我硬翻译的,principle 2我确实不太明白
3、把大的filters拆成几个小filters叠加,不会降低网络的识别能力,对应上面的贡献1
4、把握好网络深度和宽度的平衡,这个原则说了等于没说

一、基本规则

规则1:要防止出现特征描述的瓶颈(representational bottleneck)。所谓特征描述的瓶颈就是中间某层对特征在空间维度进行较大比例的压缩(比如使用pooling时),导致很多特征丢失。虽然Pooling是CNN结构中必须的功能,但我们可以通过一些优化方法来减少Pooling造成的损失。

规则2:特征的数目越多收敛的越快。相互独立的特征越多,输入的信息就被分解的越彻底,分解的子特征间相关性低,子特征内部相关性高,把相关性强的聚集在了一起会更容易收敛。这点就是Hebbin原理:fire together, wire together。规则2和规则1可以组合在一起理解,特征越多能加快收敛速度,但是无法弥补Pooling造成的特征损失,Pooling造成的representational bottleneck要靠其他方法来解决。

规则3:可以压缩特征维度数,来减少计算量。inception-v1中提出的用1x1卷积先降维再作特征提取就是利用这点。不同维度的信息有相关性,降维可以理解成一种无损或低损压缩,即使维度降低了,仍然可以利用相关性恢复出原有的信息。

规则4:整个网络结构的深度和宽度(特征维度数)要做到平衡。只有等比例的增大深度和维度才能最大限度的提升网络的性能。

改进: (1)7*7的卷积层替换成了3个3*3的卷积 (2)第一块Inception变成了3个(同BN-Inception) (3)第一块Inception是传统的 第二块Inception是 Fig.5. 5*\5替换成两个3*3 第三块Inception是Fig.6. 1*n 和n*1 v3一个最重要的改进是分解(Factorization),将7x7分解成两个一维的卷积(1x7,7x1),3x3也是一样(1x3,3x1),这第一个样的好处,既可以加速计算(多余的计算能力可以用来加深网络),又可以将1个conv拆成2个conv,使得网络深度进一步增加,增加了网络的非线性,还有值得注意的地方是网络输入从224x224变为了299x299,更加精细设计了35x35/17x17/8x8的模块。
v3的性能展示
image

参考文献:
https://blog.csdn.net/yuanchheneducn/article/details/53045551

Inception V4:(这篇论文基本上就是网络结构图了)

Google Research从DistBelief过渡到TensorFlow之后,不用再顾虑分布式训练时要做模型的分割。Inception-v4模型设计的可以更简洁,计算量也更小。 1、在Inception v3的基础上发明了Inception v4,v4比v3更加复杂,复杂到不可思议 2、结合ResNet与GoogLeNet,发明了Inception-ResNet-v1、Inception-ResNet-v2,其中Inception-ResNet-v2效果非常好,但相比ResNet,Inception-ResNet-v2的复杂度非常惊人,跟Inception v4差不多 3、加入了Residual Connections以后,网络的训练速度加快了 4、在网络复杂度相近的情况下,Inception-ResNet-v2略优于Inception-v4 5、Residual Connections貌似只能加速网络收敛,真正提高网络精度的是“更大的网络规模”.

v4的网络结构:


v4的inceptionA结构如下图所示:
image

inceptionResNet网络结构

inceptionResNet的inception结构如下图所示,就是把intention的网络加到了ResNet当中
image

参考文献:


#13

先完成week6的理论部分了,动手list week5和week6待补充。
这里没有切分那么明显,融合了CS229和CS231还有李宏毅的部分笔记。
下面的图片笔记首先介绍了CS229中MDP的内容以及Policy gradient的相关部分内容 然后介绍了Q-learning和DQN的内容,个人感觉DQN就是讲Q-learning用神经网络实现。
前面看了VAE(感觉就是神经网络实现EM算法),有一种神奇的感觉。

上面部分是CS229部分的MDP和PG
Q-learning就是基于MDP中间的value估计加了一个action所以是(state,action) pair的估计,这里是连续状态空间的情况,所以我觉着算法就是类似于之前的fitted value iteration。但是在DQN的实现中间,不是类似于fitted value iteration中间先做多次模拟,记录数据,再采样,这里是利用了一个经验缓冲空间(experienced replay)来存储数据,每一次迭代的过程进行更新,再在里面抽样。 以下是CS231和李宏毅部分


#14

看到你的学习进度,给你手动点个赞👍,如果你有需要帮助的请联系我,包括实习或者就业的事情~加油💪


#15

好的,那有需要我就会来打扰了,嘿嘿嘿~~~又干劲满满啦~~~


#16

加油,:grinning:


#17

TextSum 实践踩坑:

这里直接用的源码当中的data里面的toy data来做的实践。 报错报的是 a byte-like object is required, not ‘str’

然而实际过程中我觉着需要的是str类型,而不是byte-like的对象,所以说要对ex.features.feature[key].bytes_list.value[0]进行转义,decode(’utf-8’)
这里需要修改是针对data.py中的以下代码段,我暂时记得的处理,也许还有其他部分针对性的处理没看到。


# 因为这里的text是将其视为text操作的,所以一定要转义成字符串类型。
def SnippetGen(text, start_tok, end_tok, inclusive=True):
  """将text切分为指定开头和结尾的片段"""
  """Generates consecutive snippets between start and end tokens.

  Args:
    text: a string
    start_tok: a string denoting the start of snippets
    end_tok: a string denoting the end of snippets
    inclusive: Whether include the tokens in the returned snippets.(是否包含tokens在返回的片段当中)

  Yields:
    String snippets
  """
  cur = 0
  while True:
    try:
      start_p = text.index(start_tok, cur) # str.index(str1,beg=0,end=len(string))
      end_p = text.index(end_tok, start_p + 1) # 返回str1在str中的位置,找不到str1,抛出异常
      cur = end_p + len(end_tok)
      if inclusive:
        yield text[start_p:cur]
      else:
        yield text[start_p+len(start_tok):end_p]
    except ValueError as e:
      raise StopIteration('no more snippets in text: %s' % e)

def _GetExFeatureText(self, ex, key):
    """Extract text for a feature from td.Example.

    Args:
      ex: tf.Example.
      key: key of the feature to be extracted.
    Returns:
      feature: a feature text extracted.
    """
    # return ex.features.feature[key].bytes_list.value[0]
    return ex.features.feature[key].bytes_list.value[0].decode('utf-8')

在data.py中也有相应部分代码,若需要调用这部分的相关代码,应该也需要做上述的修改


# 实际上感觉有奇怪,因为data中间就是用字节单位存储的,直接这样读取出来的是字节类型,decode到utf-8才是str类型,感觉修改是逆向走的感觉
import pickle
import struct
from tensorflow.core.example import example_pb2

with open('data/data','rb') as f:
    byte = f.read(8)
    str_len = struct.unpack('q',byte)[0]
    example_str = struct.unpack("%ds"%str_len,f.read(str_len))[0]

ex = example_pb2.Example.FromString(example_str)
article = ex.features.feature['article'].bytes_list.value[0]
print(type(article)) # class 'bytes'
print(type(article.decode('utf-8'))) # class 'str'

# 注: 在python3中,字节类型的string在字符串之前都有一个b表示,例如这里的example_str的内容如下
'''
b"\n\xc6\n\n\xd5\t\n\x07article\x12\xc9\t\n\xc6\t\n\xc3\t 

the sri lankan government on wednesday announced the closure of government schools with immediate effect as a military campaign against tamil separatists escalated in the north of the country . the cabinet wednesday decided to advance the december holidays by one month because of a threat from the liberation tigers of tamil eelam -lrb- ltte -rrb- against school children , a government official said . `` there are intelligence reports that the tigers may try to kill a lot of children to provoke a backlash against tamils in colombo . `` if that happens , troops will have to be withdrawn from the north to maintain law and order here , '' a police official said . he said education minister richard pathirana visited several government schools wednesday before the closure decision was taken . the government will make alternate arrangements to hold end of term examinations , officials said . earlier wednesday , president chandrika kumaratunga said the ltte may step up their attacks in the capital to seek revenge for the ongoing military offensive which she described as the biggest ever drive to take the tiger town of jaffna . .

\nV\n\x08abstract\x12J\nH\nF

sri lanka closes schools as war escalates .

\n\x14\n\tpublisher\x12\x07\n\x05\n\x03AFP" '''

在这里数据的padding,在article中padding的是<pad>,在decode和target中padding的是<\s>。target和decode_inputs的区别就在于结束符号的部分

测试embedding代码部分


check = [[i*5+j for j in range(5)] for i in range(3)] #[3,5]
embedding = [[i*10+j for j in range(10)] for i in range(15)]

with tf.Session() as sess:
    a = tf.unstack(tf.transpose(check))
    print(tf.Print(a,[a]).eval()) #5*3
    #embed = tf.convert_to_tensor(embedding)
    b = [tf.nn.embedding_lookup(tf.convert_to_tensor(embedding),x) for x in a]
    print(tf.Print(b,[b]).eval()) #5*3*10

code部分解释注明 seqe2seq_attention_model.py


# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================

"""Sequence-to-Sequence with attention model for text summarization.
"""
from collections import namedtuple

import numpy as np
import seq2seq_lib
from six.moves import xrange
import tensorflow as tf

HParams = namedtuple('HParams',
                     'mode, min_lr, lr, batch_size, '
                     'enc_layers, enc_timesteps, dec_timesteps, '
                     'min_input_len, num_hidden, emb_dim, max_grad_norm, '
                     'num_softmax_samples')

##embedding:[vsize,emb_dim],output_projection[0]:[num_hidden,vsize],output_projection[1]:[vsize]
def _extract_argmax_and_embed(embedding, output_projection=None,
                              update_embedding=True):
  """Get a loop_function that extracts the previous symbol and embeds it.

  Args:
    embedding: embedding tensor for symbols.
    output_projection: None or a pair (W, B). If provided, each fed previous
      output will first be multiplied by W and added B.
    update_embedding: Boolean; if False, the gradients will not propagate
      through the embeddings.

  Returns:
    A loop function.
  """
  def loop_function(prev, _):
    """function that feed previous model output rather than ground truth."""
    if output_projection is not None:
      prev = tf.nn.xw_plus_b(
          prev, output_projection[0], output_projection[1])
    prev_symbol = tf.argmax(prev, 1) #找出对应的词的位置
    # Note that gradients will not propagate through the second parameter of
    # embedding_lookup.
    emb_prev = tf.nn.embedding_lookup(embedding, prev_symbol)
    if not update_embedding:
      emb_prev = tf.stop_gradient(emb_prev) #停止梯度计算,当在graph中执行时,这个op输出和输入的tensor是一样的,加入这个op,这些emb_prev对与gradients的计算不会有任何的帮助
    return emb_prev
  return loop_function


class Seq2SeqAttentionModel(object):
  """Wrapper for Tensorflow model graph for text sum vectors."""

  def __init__(self, hps, vocab, num_gpus=0):
    self._hps = hps
    self._vocab = vocab
    self._num_gpus = num_gpus
    self._cur_gpu = 0

  def run_train_step(self, sess, article_batch, abstract_batch, targets,
                     article_lens, abstract_lens, loss_weights):
    to_return = [self._train_op, self._summaries, self._loss, self.global_step]
    return sess.run(to_return,
                    feed_dict={self._articles: article_batch,
                               self._abstracts: abstract_batch,
                               self._targets: targets,
                               self._article_lens: article_lens,
                               self._abstract_lens: abstract_lens,
                               self._loss_weights: loss_weights})

  def run_eval_step(self, sess, article_batch, abstract_batch, targets,
                    article_lens, abstract_lens, loss_weights):
    to_return = [self._summaries, self._loss, self.global_step]
    return sess.run(to_return,
                    feed_dict={self._articles: article_batch,
                               self._abstracts: abstract_batch,
                               self._targets: targets,
                               self._article_lens: article_lens,
                               self._abstract_lens: abstract_lens,
                               self._loss_weights: loss_weights})

  def run_decode_step(self, sess, article_batch, abstract_batch, targets,
                      article_lens, abstract_lens, loss_weights):
    to_return = [self._outputs, self.global_step]
    return sess.run(to_return,
                    feed_dict={self._articles: article_batch,
                               self._abstracts: abstract_batch,
                               self._targets: targets,
                               self._article_lens: article_lens,
                               self._abstract_lens: abstract_lens,
                               self._loss_weights: loss_weights})

  def _next_device(self):
    """Round robin the gpu device. (Reserve last gpu for expensive op)."""
    if self._num_gpus == 0:
      return ''
    dev = '/gpu:%d' % self._cur_gpu
    if self._num_gpus > 1:
      self._cur_gpu = (self._cur_gpu + 1) % (self._num_gpus-1)
    return dev

  def _get_gpu(self, gpu_id):
    if self._num_gpus <= 0 or gpu_id >= self._num_gpus:
      return ''
    return '/gpu:%d' % gpu_id

  def _add_placeholders(self):
    """Inputs to be fed to the graph."""
    hps = self._hps
    self._articles = tf.placeholder(tf.int32,
                                    [hps.batch_size, hps.enc_timesteps],
                                    name='articles')
    self._abstracts = tf.placeholder(tf.int32,
                                     [hps.batch_size, hps.dec_timesteps],
                                     name='abstracts')
    self._targets = tf.placeholder(tf.int32,
                                   [hps.batch_size, hps.dec_timesteps],
                                   name='targets')
    self._article_lens = tf.placeholder(tf.int32, [hps.batch_size],
                                        name='article_lens')
    self._abstract_lens = tf.placeholder(tf.int32, [hps.batch_size],
                                         name='abstract_lens')
    self._loss_weights = tf.placeholder(tf.float32,
                                        [hps.batch_size, hps.dec_timesteps],
                                        name='loss_weights')

  def _add_seq2seq(self):
    hps = self._hps
    vsize = self._vocab.NumIds()

    with tf.variable_scope('seq2seq'):
      #tf.unstack(value, num=None, axis=0, name='unstack')
      #将self._articles转置,然后再按照维度0解开,返回的是list包含所有的解开的值
      # 这里axis=0是dec_timesteps的维度,unstack的化,list的顺序就是每一个timestep的顺序,长度是batch_size的长度[[batch_size],[batch_size]],也就是[timestep,batch_size]
      encoder_inputs = tf.unstack(tf.transpose(self._articles))
      decoder_inputs = tf.unstack(tf.transpose(self._abstracts))
      targets = tf.unstack(tf.transpose(self._targets))
      loss_weights = tf.unstack(tf.transpose(self._loss_weights))
      article_lens = self._article_lens

      # Embedding shared by the input and outputs.
      with tf.variable_scope('embedding'), tf.device('/cpu:0'):
        embedding = tf.get_variable(
            'embedding', [vsize, hps.emb_dim], dtype=tf.float32,
            initializer=tf.truncated_normal_initializer(stddev=1e-4))
        emb_encoder_inputs = [tf.nn.embedding_lookup(embedding, x)
                              for x in encoder_inputs] # list的大小是[timesteps,batch_size,emb_dim]
        emb_decoder_inputs = [tf.nn.embedding_lookup(embedding, x)
                              for x in decoder_inputs]

      for layer_i in xrange(hps.enc_layers): #hps.enc_layers:这里设置了4层,所以RNN的架构中是用4层双向的RNN
      # 多层双向的RNN的实现
        with tf.variable_scope('encoder%d'%layer_i), tf.device(
            self._next_device()):
          # cell_fw:是RNNCell实例,用来前向传播
          cell_fw = tf.contrib.rnn.LSTMCell(
              hps.num_hidden,
              initializer=tf.random_uniform_initializer(-0.1, 0.1, seed=123),
              state_is_tuple=False) # state_is_tuple:True,返回(c_state,m_state),False:这两个沿着column axis拼接
          # cell_bw:是RNNcell示例,用来后向传播的方向
          cell_bw = tf.contrib.rnn.LSTMCell(
              hps.num_hidden,
              initializer=tf.random_uniform_initializer(-0.1, 0.1, seed=113),
              state_is_tuple=False)
          # tf.contrib.rnn.static_bidirectional_rnn(cell_fw, cell_bw, inputs, initial_state_fw=None, initial_state_bw=None, dtype=None, sequence_length=None, scope=None)
          # inputs: 长度为T的list,每一个tenosr的shape是[batch_size, input_size]
          # sequence_length,大小是[batch_size],包含每个sequence的真实的长度,所以这里是每一个article的真实的长度
          # 这个函数用来创建双向的递归神经网络
          # 函数返回为(outputs,output_state_fw,output_state_bw), outputs是T个长度的list,每一个output针对每一个input的输出,将前向和后向的输出depth_concatenated,output_state_fw: forward rnn的final state,同理output_state_bw也是最后一个state,每一个tensor是[c_state,m_state]
          (emb_encoder_inputs, fw_state, _) = tf.contrib.rnn.static_bidirectional_rnn(
              cell_fw, cell_bw, emb_encoder_inputs, dtype=tf.float32,
              sequence_length=article_lens)
      encoder_outputs = emb_encoder_inputs

      with tf.variable_scope('output_projection'):
        w = tf.get_variable(
            'w', [hps.num_hidden, vsize], dtype=tf.float32,
            initializer=tf.truncated_normal_initializer(stddev=1e-4))
        w_t = tf.transpose(w)
        v = tf.get_variable(
            'v', [vsize], dtype=tf.float32,
            initializer=tf.truncated_normal_initializer(stddev=1e-4))

      with tf.variable_scope('decoder'), tf.device(self._next_device()):
        # When decoding, use model output from the previous step
        # for the next step.
        loop_function = None
        if hps.mode == 'decode':
          loop_function = _extract_argmax_and_embed(#embedding:[vsize,emb_dim],w:[num_hidden,vsize],v:[vsize]
              embedding, (w, v), update_embedding=False)

        cell = tf.contrib.rnn.LSTMCell(
            hps.num_hidden,
            initializer=tf.random_uniform_initializer(-0.1, 0.1, seed=113),
            state_is_tuple=False)

        encoder_outputs = [tf.reshape(x, [hps.batch_size, 1, 2*hps.num_hidden])
                           for x in encoder_outputs] #[timesteps,batchsize,1,2*num_hiddens]
        self._enc_top_states = tf.concat(axis=1, values=encoder_outputs) # 自身concat维度需要大于3,tf.concat和np.concatenate()使用方法基本一致。_enc_top_states最后输出的为[batchsize,timesteps,2*num_hiddens] 
        
        self._dec_in_state = fw_state #这边输出的结构是,每一个cell输出是两个状态[batch_size, 2*num_hiddens]
        # During decoding, follow up _dec_in_state are fed from beam_search.
        # dec_out_state are stored by beam_search for next step feeding.
        initial_state_attention = (hps.mode == 'decode') #这里在decode的过程中从inital state和attention states初始化attentions,否则初始化为0
        
        # API里面说这个被弃用了。。。但是没有说使用什么来代替,蜜汁尴尬
        # 用来创建sequence-to-sequence models的
        # tf.contib.legacy_seq2seq.attention_decoder()是decoder with attention
        # 参数 decoder_inputs:list of [batch_size,input_size],所以tensor的大小是  [timesteps,batch_size,emb_dim]
        # initial_state:[batch_size,cell.state_size]
        # attention_states:[batch_size, attn_length,attn,size]
        # cell:tf.nn.rnn_cell.RNNCell
        # output_size:[output_size](可以为None)
        # num_heads:number of attention_size
        # loop_function:这个function是为了利用ith output产生i+1-th input,并且decoder_inputs会被忽略,除了第一个元素。可以被用来decoding,也可以用来模拟 loop_function(pre,i)=next, pre:[batch_size,output_size],i:integer,next:[batch_size,input_size]
        # 所以在这里,第ith个step的输入是pre,然后loop_finction的输出作为下一个step的输入,这里实际上就是预测的过程。
        decoder_outputs, self._dec_out_state = tf.contrib.legacy_seq2seq.attention_decoder(
            emb_decoder_inputs, self._dec_in_state, self._enc_top_states,
            cell, num_heads=1, loop_function=loop_function,
            initial_state_attention=initial_state_attention)
        # 上面的输出是(outputs,state)
        # outputs是一个和decoder_inputs长度一样的list,每个元素是一个2Dtensor[batch_size,output_size],这里每一次都有一个attention的计算
        # state: 最后一个time_step的每一个decoder的state,[batch_size,cell.state_size]     

      with tf.variable_scope('output'), tf.device(self._next_device()):
        model_outputs = []
        for i in xrange(len(decoder_outputs)):
          if i > 0:
            tf.get_variable_scope().reuse_variables()
          model_outputs.append(
              tf.nn.xw_plus_b(decoder_outputs[i], w, v)) # 输出的列都是vsize的,对应vocab中的词,所以model_output的list的每个维度是[timesteps,batch_size,v_size]

      if hps.mode == 'decode':
        with tf.variable_scope('decode_output'), tf.device('/cpu:0'):
          best_outputs = [tf.argmax(x, 1) for x in model_outputs]
          tf.logging.info('best_outputs%s', best_outputs[0].get_shape())
          self._outputs = tf.concat(
              axis=1, values=[tf.reshape(x, [hps.batch_size, 1]) for x in best_outputs]) #将[time_steps,batch_size,1]按照axis=1合并为[batch_size,time_steps]

	      #tf.nn.top_k(input,k=1):在输入的最后一维找到k个最大的entries的值和indices,返回values和indices
          self._topk_log_probs, self._topk_ids = tf.nn.top_k(
              tf.log(tf.nn.softmax(model_outputs[-1])), hps.batch_size*2) # model_outputs[-1]最后一个timestep的输出[batch_size,v_size]

      with tf.variable_scope('loss'), tf.device(self._next_device()):
        def sampled_loss_func(inputs, labels):
          with tf.device('/cpu:0'):  # Try gpu.
            labels = tf.reshape(labels, [-1, 1])
            # 通过采样快速估计loss,计算的交叉熵的loss
            return tf.nn.sampled_softmax_loss(
                weights=w_t, biases=v, labels=labels, inputs=inputs,
                num_sampled=hps.num_softmax_samples, num_classes=vsize)

        # 这里可以采用采样的办法来估计loss,在train的时候快速训练
        # 采用的loss是交叉熵,通过权重的均值做估计
        if hps.num_softmax_samples != 0 and hps.mode == 'train':
          self._loss = seq2seq_lib.sampled_sequence_loss(
              decoder_outputs, targets, loss_weights, sampled_loss_func)
        else:
          # weighted_cross_entropy with logits
          self._loss = tf.contrib.legacy_seq2seq.sequence_loss(
              model_outputs, targets, loss_weights)
        # 存储loss的值,最大值只存到12,?
        tf.summary.scalar('loss', tf.minimum(12.0, self._loss))

  def _add_train_op(self):
    """Sets self._train_op, op to run for training."""
    hps = self._hps

    self._lr_rate = tf.maximum(
        hps.min_lr,  # min_lr_rate.
        tf.train.exponential_decay(hps.lr, self.global_step, 30000, 0.98))

    tvars = tf.trainable_variables()
    with tf.device(self._get_gpu(self._num_gpus-1)):
      grads, global_norm = tf.clip_by_global_norm(
          tf.gradients(self._loss, tvars), hps.max_grad_norm)
    tf.summary.scalar('global_norm', global_norm)
    optimizer = tf.train.GradientDescentOptimizer(self._lr_rate)
    tf.summary.scalar('learning rate', self._lr_rate)
    self._train_op = optimizer.apply_gradients(
        zip(grads, tvars), global_step=self.global_step, name='train_step')

  def encode_top_state(self, sess, enc_inputs, enc_len):
    """Return the top states from encoder for decoder.

    Args:
      sess: tensorflow session.
      enc_inputs: encoder inputs of shape [batch_size, enc_timesteps].
      enc_len: encoder input length of shape [batch_size]
    Returns:
      enc_top_states: The top level encoder states.
      dec_in_state: The decoder layer initial state.
    """
    results = sess.run([self._enc_top_states, self._dec_in_state],
                       feed_dict={self._articles: enc_inputs,
                                  self._article_lens: enc_len})
    # print(results[0])
    # print(results[1])
    # print(results[0].shape) # (8,120,512)
    # print(results[1][0].shape) # (8,512)
    return results[0], results[1][0]

  def decode_topk(self, sess, latest_tokens, enc_top_states, dec_init_states):
    """Return the topK results and new decoder states."""
    feed = {
        self._enc_top_states: enc_top_states,
        self._dec_in_state:
            np.squeeze(np.array(dec_init_states)),
        self._abstracts:
            np.transpose(np.array([latest_tokens])),
        self._abstract_lens: np.ones([len(dec_init_states)], np.int32)}

    results = sess.run(
        [self._topk_ids, self._topk_log_probs, self._dec_out_state],
        feed_dict=feed)

    ids, probs, states = results[0], results[1], results[2]
    new_states = [s for s in states]
    return ids, probs, new_states

  def build_graph(self):
    self._add_placeholders()
    self._add_seq2seq()
    self.global_step = tf.Variable(0, name='global_step', trainable=False)
    if self._hps.mode == 'train':
      self._add_train_op()
    self._summaries = tf.summary.merge_all()

decode部分
seq2seq_attention_decode.py


# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================

"""Module for decoding."""

import os
import time

import beam_search
import data
from six.moves import xrange
import tensorflow as tf

FLAGS = tf.app.flags.FLAGS
tf.app.flags.DEFINE_integer('max_decode_steps', 1000000,
                            'Number of decoding steps.')
tf.app.flags.DEFINE_integer('decode_batches_per_ckpt', 8000,
                            'Number of batches to decode before restoring next '
                            'checkpoint')

DECODE_LOOP_DELAY_SECS = 60
DECODE_IO_FLUSH_INTERVAL = 100


class DecodeIO(object):
  """Writes the decoded and references to RKV files for Rouge score.

    See nlp/common/utils/internal/rkv_parser.py for detail about rkv file.
  """

  def __init__(self, outdir):
    self._cnt = 0
    self._outdir = outdir
    if not os.path.exists(self._outdir):
      os.mkdir(self._outdir)
    self._ref_file = None
    self._decode_file = None

  def Write(self, reference, decode):
    """Writes the reference and decoded outputs to RKV files.

    Args:
      reference: The human (correct) result.
      decode: The machine-generated result
    """
    self._ref_file.write('output=%s\n' % reference)
    self._decode_file.write('output=%s\n' % decode)
    self._cnt += 1
    if self._cnt % DECODE_IO_FLUSH_INTERVAL == 0:
      self._ref_file.flush()
      self._decode_file.flush()

  def ResetFiles(self):
    """Resets the output files. Must be called once before Write()."""
    if self._ref_file: self._ref_file.close()
    if self._decode_file: self._decode_file.close()
    timestamp = int(time.time())
    self._ref_file = open(
        os.path.join(self._outdir, 'ref%d'%timestamp), 'w')
    self._decode_file = open(
        os.path.join(self._outdir, 'decode%d'%timestamp), 'w')


class BSDecoder(object):
  """Beam search decoder."""

  def __init__(self, model, batch_reader, hps, vocab):
    """Beam search decoding.

    Args:
      model: The seq2seq attentional model.
      batch_reader: The batch data reader.
      hps: Hyperparamters.
      vocab: Vocabulary
    """
    self._model = model
    self._model.build_graph()
    self._batch_reader = batch_reader
    self._hps = hps
    self._vocab = vocab
    self._saver = tf.train.Saver()
    self._decode_io = DecodeIO(FLAGS.decode_dir)

  def DecodeLoop(self):
    """Decoding loop for long running process."""
    sess = tf.Session(config=tf.ConfigProto(allow_soft_placement=True)) #allow_soft_placement=True表示指定设备不存在,那么tensorflow自动选择受支持设备来运行指令。
    step = 0
    while step < FLAGS.max_decode_steps:
      time.sleep(DECODE_LOOP_DELAY_SECS)
      if not self._Decode(self._saver, sess):
        continue
      step += 1

  def _Decode(self, saver, sess):
    """Restore a checkpoint and decode it.

    Args:
      saver: Tensorflow checkpoint saver.
      sess: Tensorflow session.
    Returns:
      If success, returns true, otherwise, false.
    """
    #tf.train.get_checkpoint_state(checkpoint_dir,latest_filename=None) 返回CheckpointState
    #ckpt_state.model_checkpoint_path:表示模型存储的未知,它会查看checkpoint文件(二进制文件),查看最新的model的名字
    ckpt_state = tf.train.get_checkpoint_state(FLAGS.log_root)
    if not (ckpt_state and ckpt_state.model_checkpoint_path):
      tf.logging.info('No model to decode yet at %s', FLAGS.log_root)
      return False

    tf.logging.info('checkpoint path %s', ckpt_state.model_checkpoint_path)
    # os.path.basename(path),返回path中间最后\\或者/后面的数,如果说是"C:/"则返回空
    ckpt_path = os.path.join(
        FLAGS.log_root, os.path.basename(ckpt_state.model_checkpoint_path))
    tf.logging.info('renamed checkpoint path %s', ckpt_path)
    saver.restore(sess, ckpt_path)

    self._decode_io.ResetFiles()
    for _ in xrange(FLAGS.decode_batches_per_ckpt):
      (article_batch, _, _, article_lens, _, _, origin_articles,
       origin_abstracts) = self._batch_reader.NextBatch()
      for i in xrange(self._hps.batch_size):
        bs = beam_search.BeamSearch(
            self._model, self._hps.batch_size,
            self._vocab.WordToId(data.SENTENCE_START),
            self._vocab.WordToId(data.SENTENCE_END),
            self._hps.dec_timesteps)

        article_batch_cp = article_batch.copy()
        article_batch_cp[:] = article_batch[i:i+1] # 这里article_batch_cp里面所有行元素都换成了article_batch[i:i+1]
        
        # print(article_batch_cp)
        # print(article_batch_cp.shape)
        
        article_lens_cp = article_lens.copy()
        article_lens_cp[:] = article_lens[i:i+1]
        best_beam = bs.BeamSearch(sess, article_batch_cp, article_lens_cp)[0]
        decode_output = [int(t) for t in best_beam.tokens[1:]]
        self._DecodeBatch(
            origin_articles[i], origin_abstracts[i], decode_output)
    return True

  def _DecodeBatch(self, article, abstract, output_ids):
    """Convert id to words and writing results.

    Args:
      article: The original article string.
      abstract: The human (correct) abstract string.
      output_ids: The abstract word ids output by machine.
    """
    decoded_output = ' '.join(data.Ids2Words(output_ids, self._vocab))
    end_p = decoded_output.find(data.SENTENCE_END, 0)
    if end_p != -1:
      decoded_output = decoded_output[:end_p]
    tf.logging.info('article:  %s', article)
    tf.logging.info('abstract: %s', abstract)
    tf.logging.info('decoded:  %s', decoded_output)
    self._decode_io.Write(abstract, decoded_output.strip())

#18

beam_search.py
这里由于算法的计算复杂度问题,转义文件不能采取维特比算法


# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================

"""Beam search module.
beam search是一种贪婪算法,这里不用viterbi算法的原因是因为,每一次寻找所有的词汇,这里的预测下一个词的概率是基于窗口大小为C的y值和x值来预测,所以计算复杂度为O(NV^C),由于V很大,所以不使用,相比较而言,beamsearch是在每一个词汇中寻找k个最有的词,所以计算复杂度是O(KVN)

Beam search takes the top K results from the model, predicts the K results for
each of the previous K result, getting K*K results. Pick the top K results from
K*K results, and start over again until certain number of results are fully
decoded.
"""

from six.moves import xrange
import tensorflow as tf

FLAGS = tf.flags.FLAGS
tf.flags.DEFINE_bool('normalize_by_length', True, 'Whether to normalize')


class Hypothesis(object):
  """Defines a hypothesis during beam search."""

  def __init__(self, tokens, log_prob, state):
    """Hypothesis constructor.

    Args:
      tokens: start tokens for decoding.
      log_prob: log prob of the start tokens, usually 1.
      state: decoder initial states.
    """
    self.tokens = tokens
    self.log_prob = log_prob
    self.state = state

  def Extend(self, token, log_prob, new_state):
    """Extend the hypothesis with result from latest step.

    Args:
      token: latest token from decoding.
      log_prob: log prob of the latest decoded tokens.
      new_state: decoder output state. Fed to the decoder for next step.
    Returns:
      New Hypothesis with the results from latest step.
    """
    return Hypothesis(self.tokens + [token], self.log_prob + log_prob,
                      new_state)

  @property #getter属性
  def latest_token(self):
    return self.tokens[-1]

  def __str__(self):
    return ('Hypothesis(log prob = %.4f, tokens = %s)' % (self.log_prob,
                                                          self.tokens))


class BeamSearch(object):
  """Beam search."""

  def __init__(self, model, beam_size, start_token, end_token, max_steps):
    """Creates BeamSearch object.

    Args:
      model: Seq2SeqAttentionModel.
      beam_size: int.
      start_token: int, id of the token to start decoding with
      end_token: int, id of the token that completes an hypothesis
      max_steps: int, upper limit on the size of the hypothesis
    """
    self._model = model
    self._beam_size = beam_size
    self._start_token = start_token
    self._end_token = end_token
    self._max_steps = max_steps

  def BeamSearch(self, sess, enc_inputs, enc_seqlen):
    """Performs beam search for decoding.

    Args:
      sess: tf.Session, session
      这里enc_inputs:shape (batch_size, timesteps)
      enc_seqlen shape (batch_size)
      与原代码备注不相符,但是在这里,每一个enc_inputs的行元素都是一样的
      enc_inputs: ndarray of shape (enc_length, 1), the document ids to encode
      enc_seqlen: ndarray of shape (1), the length of the sequence

    Returns:
      hyps: list of Hypothesis, the best hypotheses found by beam search,
          ordered by score
    """
    # print(enc_inputs.shape)
    # print(enc_seqlen.shape)

    # Run the encoder and extract the outputs and final state.
    enc_top_states, dec_in_state = self._model.encode_top_state(
        sess, enc_inputs, enc_seqlen)
    # Replicate the initial states K times for the first step.
    hyps = [Hypothesis([self._start_token], 0.0, dec_in_state)
           ] * self._beam_size
    results = []

    steps = 0
    while steps < self._max_steps and len(results) < self._beam_size:
      latest_tokens = [h.latest_token for h in hyps]
      states = [h.state for h in hyps]

      topk_ids, topk_log_probs, new_states = self._model.decode_topk(
          sess, latest_tokens, enc_top_states, states)
      # Extend each hypothesis.
      all_hyps = []
      # 这里是第一层选出K个结果,但是在后面层中间需要每个结果都挑选除最优的K个结果,所以从k*k个结果中选取k个最优的结果作为下一层的输入
      # The first step takes the best K results from first hyps. Following
      # steps take the best K results from K*K hyps.
      num_beam_source = 1 if steps == 0 else len(hyps)# len(hyps)==self._beam_size
      for i in xrange(num_beam_source):
        h, ns = hyps[i], new_states[i]
        # 这里为什么要*2
        # 这里和下面的BestHyps对应,因为下面for里面退出最长是2*beam_size,所以需要保证all_hyps的长度至少是2*beam_size
        for j in xrange(self._beam_size*2): 
          all_hyps.append(h.Extend(topk_ids[i, j], topk_log_probs[i, j], ns))

      # Filter and collect any hypotheses that have the end token.
      hyps = []
      for h in self._BestHyps(all_hyps):
        if h.latest_token == self._end_token:
          # Pull the hypothesis off the beam if the end token is reached.
          # 当将结尾对应的id是结尾符号,那么就是一句完整的话
          results.append(h)
        else:
          # Otherwise continue to the extend the hypothesis.
          # 结果对应的不是id符号,还未生成完整,需要继续生成
          hyps.append(h)
        # 当假设集合中有beam_size个假设都没有生成有结尾的数据,
        # 或者生成句子的长度已经达到了beam_size个生成的结果,跳出for循环
        if len(hyps) == self._beam_size or len(results) == self._beam_size:
          break

      steps += 1

    if steps == self._max_steps:
      results.extend(hyps)

    return self._BestHyps(results)

  def _BestHyps(self, hyps):
    """Sort the hyps based on log probs and length.

    Args:
      hyps: A list of hypothesis.
    Returns:
      hyps: A list of sorted hypothesis in reverse log_prob order.
    """
    # This length normalization is only effective for the final results.
    # 这里为什么说长度normalization会导致结果有差别呢,后续代码看完需要回答
   # 这个点没有理解
    if FLAGS.normalize_by_length:
      return sorted(hyps, key=lambda h: h.log_prob/len(h.tokens), reverse=True)
    else:
      return sorted(hyps, key=lambda h: h.log_prob, reverse=True)

以下部分为使用中文预料训练的data_process.py以及data_convert.py部分。
对搜狗的预料进行处理
这一部分参考了week4的预料处理,以及知乎相关修改,还有pavel surmenok关于textsum博客部分。 参考文献:
http://pavel.surmenok.com/2016/10/15/how-to-run-text-summarization-with-tensorflow/
这里由于格式的问题,所以我在所有的<>里面都添加了“",代码运行时注意需要自行去掉。


# !/usr/bin/ python3
# coding=utf-8

import numpy as np
import re
import os
import jieba
import codecs
import collections
jieba.set_dictionary('/home/lily/Datageek/dict.txt.big')

import sys
sys.path.append('/home/lily/projects/textsummarization/textsum')

def clean_and_segmentate_data(raw_data_list, data_parent_path):
    for raw_data_file in raw_data_list:
        out_data_file = "clean." + raw_data_file
        out_data_file = data_parent_path + out_data_file
        raw_data_file = data_parent_path + raw_data_file
        # print(out_data_file, end=' ')

        # sogou encoding in GB18030
        with codecs.open(raw_data_file, 'r', encoding='GB18030') as inf:
            with open(out_data_file, 'w', encoding='utf-8') as outf:
                abstract = ""
                for i, line in enumerate(inf):
                    # every six line is a doc
                    i %= 6

                    # filter punctuation(full_width and half_width) except full_width comma(,)
                    # separate sentences using comma
                    # contenttitle and content
                    if 3 <= i <= 4:
                        # full_width brackets
                        line = re.sub(r'(.*)','',line)
                        # exclude the brackets
                        line = re.sub(r'\(.*\)','',line)
                        line = re.sub("[\s+.\!\/_,$%^*(+\"\']+|[+——!。?、~@#¥%……&*()""“”]+",'',line)

                    # contenttitle,exclude
                    if i == 3:
                        line = " ".join(jieba.cut(line[14:-15], HMM=True))
                        abstract = "abstract=<\d> <\p> <\s> %s <\/s> <\/p> <\/d>" % line

                    elif i == 4:
                        line = ' '.join(jieba.cut(line[9:-10], HMM=True))

                        # filter article length < 32
                        # and long article may cause wrong
                        if len(line) > 32:
                            article = "article=<\d> <\p> <\s> %s <\/s> <\/p> <\/d>" % line[:256].replace(',', '<\/s> <\s> ')
                            temp = "publisher=AFP\t%s\t%s\n"%(abstract, article)
                            print(temp, end='')
                            outf.write(temp)
        #print(out_data_file)
        print("done")

def fullToHalf(ustring):
    rstring = ""
    for uchar in ustring:
        code = ord(uchar)
        if code == 12288: #(full-width spacekey)
            code = 32
        elif 65281 <= code <= 65374: # (ascii)
            code -= 65248
        rstring += chr(code)
    return rstring

def data_segmentation(raw_data_list, data_parent_path):
    # abstract: "abstract=<\d> <\p> <\s> %s <\/s> <\/p> <\/d>"
    # article: "article=<\d> <\p> <\s> %s <\/s> <\/p> <\/d>"
    # write: "publisher=AFP\t%s\t%s\n"
    for raw_data_file in raw_data_list:
        out_data_file = "segmentation." + raw_data_file
        out_data_file = data_parent_path + out_data_file
        raw_data_file = data_parent_path + raw_data_file
        print(out_data_file, end=' ')

        # sogou encoding in GB18030
        with codecs.open(raw_data_file, 'r', encoding='GB18030') as inf:
            with open(out_data_file, 'w', encoding='utf-8') as outf:
                for line in inf:
                    contenttitle = re.findall(".*(.*).*", line)
                    content = re.findall(".*(.*).*", line)
                    # print(contenttitle)
                    # print(content)
                    if contenttitle:
                       #  print(contenttitle)
                        summary = fullToHalf(contenttitle[0].strip()).replace(" ", ",")
                        summary = re.sub(r'\([^\)]*\)', "", summary)
                        summary = re.sub(r'[,.!?\s]+', "", summary)
                        contenttitle = " ".join(jieba.cut(summary))
                        # print(contenttitle)
                        abstract = "abstract=<\d> <\p> <\s> %s <\/s> <\/p> <\/d>"%contenttitle
                    elif content:
                        # print(len(content[0]))
                        summary = fullToHalf(content[0].strip()).replace(" ", ",")
                        summary = re.sub(r"\([^\)]*\)", "", summary)
                        # print("after",summary)
                        summary = re.sub(r'[\s]+', " ", summary)
                        summary = " ".join(jieba.cut(summary))
                        # print(summary)
                        content = re.sub(r'[,.!?]+', "<\/s> <\s>", summary)
                        # print(content)

                        if len(content) > 20: # some have abstract but no article
                            article = "article=<\d> <\p> <\s> %s <\/s> <\/p> <\/d>" % content[:512]
                            temp = "publisher=AFP\t%s\t%s\n" % (abstract, article)
                            print(temp, end='')
                            outf.write(temp)
        print("done")


def get_chinese_vocab(data_list, data_parent_path):
    vocab = {}
    for data_file in data_list:
        data_file = data_parent_path + 'segmentation.' + data_file
        print(data_file)

    with open(data_file,'r',encoding='utf-8') as file:
        line = file.readline()
        while line:
            for word in line.split():
                try:
                    vocab[word] += 1
                except:
                    vocab[word] = 1
            line = file.readline()

    #vocab = np.array(list(vocab.items()))
    #vocab_keys = vocab[:,1].astype(int)
    #vocab = vocab[np.argsort(vocab_keys)]
    # sort dict
    vocab = sorted(vocab.items(),key = lambda x:x[1],reverse=True)

    with open(data_parent_path+'vocab1', 'w', encoding='utf-8') as f:
        for line in vocab:
            # filter frequency <= 16, chinese r'[\u4e00-\u9fa5]'
            # print(line)
            # print(line[1] > 16)
            # print(re.findall(r'[\u4e00-\u9fa5]+', line[0]))
            if line[1] >= 0 and re.findall(r'[\u4e00-\u9fa5]+', line[0]):
                print(line)
                f.write(line[0]+" "+str(line[1])+'\n')
        f.write('<\s> 0\n')
        f.write('<\/s> 0\n')
        f.write('<\p> 0\n')
        f.write('<\/p> 0\n')
        f.write('<\d> 0\n')
        f.write('<\/d> 0\n')
        f.write('<\UNK> 0\n')
        f.write('<\PAD> 0\n')


data_segmentation(['news_tensite_xml.smarty.dat'],'/home/lily/projects/textsummarization/data/')
#clean_and_segmentate_data(['news_tensite_xml.smarty.dat'],'/home/lily/projects/textsummarization/data/')
get_chinese_vocab(['news_tensite_xml.smarty.dat'],'/home/lily/projects/textsummarization/data/')

data_convert_example.py 生成二进制文件,本来想要利用bazel-bin进行编译,也把这个文件加到build文件中了,重新编译了,但是无法通过bazel-bin进行编译,所以对原文件的tf.app.flags改回取了argparser的库。有直接用bazel-bin编译的求指导。


"""Example of Converting TextSum model data.
Usage:
python data_convert_example.py --command binary_to_text --in_file data/data --out_file data/text_data
python data_convert_example.py --command text_to_binary --in_file data/text_data --out_file data/binary_data
python data_convert_example.py --command binary_to_text --in_file data/binary_data --out_file data/text_data2
diff data/text_data2 data/text_data
"""

import struct
import sys
import argparse

import tensorflow as tf
from tensorflow.core.example import example_pb2

parser = argparse.ArgumentParser(description="data_convert")
parser.add_argument("--command", default="", help="Either binary_to_text or text_to_binary.")
parser.add_argument("--in_file",default="",help="path to in file")
parser.add_argument("--out_file",default="",help="path to out file")
args = parser.parse_args()

def _binary_to_text():
  reader = open(args.in_file, 'rb')
  writer = open(args.out_file, 'w')
  while True:
    len_bytes = reader.read(8)
    if not len_bytes:
      sys.stderr.write('Done reading\n')
      return
    str_len = struct.unpack('q', len_bytes)[0]
    tf_example_str = struct.unpack('%ds' % str_len, reader.read(str_len))[0]
    tf_example = example_pb2.Example.FromString(tf_example_str)
    examples = []
    for key in tf_example.features.feature:
      examples.append('%s=%s' % (key, tf_example.features.feature[key].bytes_list.value[0]))
    writer.write('%s\n' % '\t'.join(examples))
  reader.close()
  writer.close()


def _text_to_binary():
  inputs = open(args.in_file, 'r',encoding='utf-8').readlines()
  writer = open(args.out_file, 'wb')
  for inp in inputs:
    tf_example = example_pb2.Example()
    for feature in inp.strip().split('\t'):
      # print(inp)
      (k, v) = feature.split('=')
      k = str.encode(k)
      v = str.encode(v)
      # print(k)
      # print(v)
      tf_example.features.feature[k].bytes_list.value.extend([v])
    tf_example_str = tf_example.SerializeToString()
    # print(tf_example_str)
    str_len = len(tf_example_str)
    writer.write(struct.pack('q', str_len))
    writer.write(struct.pack('%ds' % str_len, tf_example_str))
  writer.close()


def main():
  assert args.command and args.in_file and args.out_file
  if args.command == 'binary_to_text':
    _binary_to_text()
  elif args.command == 'text_to_binary':
    _text_to_binary()


if __name__ == '__main__':
  main()

#19

Textsum实践心得:

由于训练的时间比较短,所以loss还是在5左右,然后decode出来的句子基本上不能看的感觉。利用toy data(也就是自带的数据)运行程序,最后生成的文档基本上都是<\UNK>。这个是vocab的问题,需要自行修改。这里最大的问题是注意语料输入格式需要相符。详情可以参见上面提到parvel的博客。直接搜索textsum和这个名字就可以了。
这里补充一部分理论

data_convert_example.py

转换文件的执行文件,可以实现binary to text或者text to binary


data.py

word2Id和Id2word,实现数据的word和id的相互转换,读入的是list,传回的也是list


batch_reader.py

进程之间是相互独立的,主进程的代码运行结束,守护进程随即终止。 守护进程内无法再开启子进程,否则会抛出异常。

在这里因为用到了守护线程,所以这里把守护线程的概念梳理一下: 守护××会等待主××运行完毕之后被销毁 对于主进程来说,运行完毕是指住进成代码执行完毕 对于主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程全部都运行完毕,主线程才算运行完毕。

详细解释: 主进程在代码结束之后就已经算运行完毕了(守护进程在这里就会被回收),主进程会一直等待非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束。 主线程在其他非守护进程运行完毕才算运行完毕(守护线程在此时就会被回收)。主线程的结束意味着进程的结束,进程整体的资源都会被回收,而进程必须保证非守护线程都运行完毕后才能够算结束。

在这里通过线程利用six.moves.queue来实现数据的batch传输, 这里six.moves.queue是线程安全的

batch_reader: 在这个数据的处理当中,将<\d><\p><\s></s></p></d>元素已经过滤掉 以下的enc_inputs,dec_inputs都有输入句子长度的限制:hyperpaprameter,所以输入一般都是部分文本内容 enc_inputs:是list of article句子的word的id的元素,所以list每个元素是每句话对应的id dec_inputs: list, 每个元素对应的是abstract中间句子的word的id targets: 去除dec_inputs的开头,加上结尾的标识 enc_input_len: 每个article句子的数量 dec_input_len: abstract 句子的数量 最后的: article:对应所有article的word的id abstract:对应所有的abstract word的id


seq2seq_lib.py:

这个文件对应的是loss的计算,是通过对sequence中间的某些词做采样进行loss的估计。(这里面的linear函数没有用到)


seq2seq_attention_model.py:

模型为encoder和decoder部分。 首先是seq2seeq部分,通过encoder输入和输出学习所有词汇的embedding的参数。原始的模型参数当中设置了四层的双向RNN,这里(双向RNN的层数是一个可调参数 enc_layers) 通过一个词汇的映射,将decoder_inputs里面的词汇代替成embedding vector作为decoder的输入。 在这里,decoder是一层的加了attention机制的RNN。


seq2seq_attention.py

执行文件,有train,eval和decode三个模式。 train,eval调用的基本上是seq2seq_attention_model.py

在decode模式,调用的是seq2seq_attention_model.py,还有seq2seq_attention _decode.py,然后在这个文件中调用beam_search.py利用beam_search算法减少计算复杂度来获得次优的句子。


#20

object_detection 项目小记


我运行的版本应该是没有进行过更新之前的版本,所以可以用protoc2来进行编译。 但是现在tensorflow上面的版本是基于tensorflow1.5以上的版本,而且需要源码安装protoc3来编译protocol文件,暂时没有更新tensorflow的版本,所以mask-rcnn没有更新成功。 注意在这里,虽然说cuda8.0支持tensorflow1.5,但是看了别人在github上面的问答,有人更新了tensorflow1.5的版本,但是运行object_detection依旧不支持,而且更新到1.5之后,由于函数不匹配的问题,所以还会持续报错,有人说要更到1.7, google官方说会尽快fix这个问题,不过这个问题我没有遇到,所以不知道是否是解决了,因为当时的帖子是3月份发出的。


下载了faster_rcnn_inception_v2_coco_2018_01_28作为pretrained model,作为猫狗训练的初始化模型的参数。 项目在本地执行的。

在项目中的收获:
解析xml格式的文件:

from lxml import etree

def recursive_parse_xml_to_dict(xml):
    #if not xml: # 因为这里显示可能__bool__这个函数会被废弃,查找了下面的语句作为替代品,经过测试,结果一致
    if xml.text is not None:
        # print(xml.text)
        return {xml.tag: xml.text}
    result = {}
    for child in xml:
        child_result = recursive_parse_xml_to_dict(child)
        if child.tag != 'object':
            result[child.tag] = child_result[child.tag]
        else:
            if child.tag not in result:
                result[child.tag] = []
            result[child.tag].append(child_result[child.tag])
    return {xml.tag: result

这边图片处理的方式。

import io
import PIL
import PIL.Image
import numpy as np
import hashlib

with open('/home/lily/projects/research/images/shiba_inu_73.jpg','rb') as f:
    encode = f.read()
    print(encode)
    print(len(b'\xff'))
    print(len(encode))
    encode_io = io.BytesIO(encode)
    print(encode_io)
    image = PIL.Image.open(encode_io)
    print(image.format)
    print(image.size)
    print(image.mode)
    key = hashlib.sha256(encode).hexdigest()
    mask_np = np.asarray(image)
    print(key)
    print(mask_np.shape)
    non_background_indices_x = np.any(mask_np != 2, axis=0)
    non_background_indices_y = np.any(mask_np != 2, axis=1)
    nonzero_x_indices = np.where(non_background_indices_x)
    nonzero_y_indices = np.where(non_background_indices_y)
    print(non_background_indices_x)
    print(non_background_indices_y)
    print(nonzero_x_indices)
    print(nonzero_y_indices)

off-the-shelf inference:

这里通过tf.GraphDef()载入模型

detection_graph = tf.Graph()
with detection_graph.as_default(): # 这里把想要载入的Graph设置为default graph
    old_graph_def = tf.GraphDef() # 利用tf.GraphDef 或者 tf.Graph.as_graph_def 取得GraphDef物件
    with tf.gfile.GFile(PATH_TO_CKPT,'rb') as fid: # 读取生成的图,也就是pb文件中的内容(binary file)
        serialized_graph = fid.read()
        old_graph_def.ParseFromString(Serialized_graph) # 利用GraphDef.ParseFromString解析读取的内容
        tf.import_graph_def(old_graph_def, name='') #  这里把解析的图利用 tf.import_graph_def()导入到default graph当中。

这里下载到本地的model inference部分有一点不一样。
从下面的两个实现中可以看出,如果tf.Session().run()里面的参数是dict, 那么输出的就是dict。

# 网站上的文档:
from utils import ops as utils_ops

def run_inference_for_single_image(image,graph):
    with graph.as_default():
        with tf.Session() as sess:
            # get handles to input and output_tensor
            ops = tf.get_default_graph().get_operations()
            all_tensor_names = {output.name for op in ops for output in op.outputs}
            tensor_dict = {}
            for key in ['num_detections','detection_boxes','detection_scores',
                        'detection_classes', 'detection_masks']:
                tensor_name = key +':0'
                if tensor_name in all_tensor_names:
                    tensor_dict[key] = tf.get_default_graph().get_tensor_by_name(tensor_name)
            if 'detection_masks' in tensor_dict:
                # the following processing is only for single image
                detection_boxes = tf.squeeze(tensor_dict['detection_boxes'],[0])
                detection_masks = tf.squeeze(tensor_dict['detection_masks'],[0])
                # reframe 要求将box坐标转换成image坐标来适合image的尺寸。
                real_num_detection = tf.cast(tensor_dict['num_detections'][0], tf.int32)
                detection_boxes = tf.slice(detection_boxes, [0,0], [real_num_detecction, -1, -1])
                detection_masks_reframed = utils_ops.reframe_box_masks_to_image_masks(detection_masks, detection_boxes,
                                                                                      image.shape[0], image.shape[1])
                detection_masks_reframed = tf.cast(tf.greater(detection_masks_reframed, 0.5),tf.uint8)
                # 将batch dimension添加回来
                tensor_dict['detection_masks'] = tf.expand_dims(detection_masks_reframed, 0)
            
            image_tensor = tf.get_default_graph().get_tensor_by_name('image_tensor:0')
            
            # inference
            output_dict = sess.run(tensor_dict, feed_dict={image_tensor: np.expand_dims(image, 0)})
            
            # 将输出的types都转成合适的类型
            print(output_dict)
            output_dict['num_detections'] = int(output_dict['num_detections'][0])
            output_dict['detection_classes'] = output_dict['detection_classes'][0].astype(np.uint8)
            output_dict['detection_boxes'] = output_dict['detection_boxes'][0]
            output_dict['detection_scores'] = output_dict['detection_scores'][0]
            if 'detection_masks' in output_dict:
                output_dict['detection_masks'] = output_dict['detection_masks'][0]
    
    return output_dict

for image_path in TEST_IMAGE_PATHS:
  image = Image.open(image_path)
  # the array based representation of the image will be used later in order to prepare the
  # result image with boxes and labels on it.
  image_np = load_image_into_numpy_array(image)
  # Expand dimensions since the model expects images to have shape: [1, None, None, 3]
  image_np_expanded = np.expand_dims(image_np, axis=0)
  # Actual detection.
  output_dict = run_inference_for_single_image(image_np, detection_graph)
  # Visualization of the results of a detection.
  vis_util.visualize_boxes_and_labels_on_image_array(
      image_np,
      output_dict['detection_boxes'],
      output_dict['detection_classes'],
      output_dict['detection_scores'],
      category_index,
      instance_masks=output_dict.get('detection_masks'),
      use_normalized_coordinates=True,
      line_thickness=8)
  plt.figure(figsize=IMAGE_SIZE)
  plt.imshow(image_np)

with detection_graph.as_default():
  with tf.Session(graph=detection_graph) as sess:
    # Definite input and output Tensors for detection_graph
    image_tensor = detection_graph.get_tensor_by_name('image_tensor:0')
    # Each box represents a part of the image where a particular object was detected.
    detection_boxes = detection_graph.get_tensor_by_name('detection_boxes:0')
    # Each score represent how level of confidence for each of the objects.
    # Score is shown on the result image, together with the class label.
    detection_scores = detection_graph.get_tensor_by_name('detection_scores:0')
    detection_classes = detection_graph.get_tensor_by_name('detection_classes:0')
    num_detections = detection_graph.get_tensor_by_name('num_detections:0')
    for image_path in TEST_IMAGE_PATHS:
      image = Image.open(image_path)
      # the array based representation of the image will be used later in order to prepare the
      # result image with boxes and labels on it.
      image_np = load_image_into_numpy_array(image)
      # Expand dimensions since the model expects images to have shape: [1, None, None, 3]
      image_np_expanded = np.expand_dims(image_np, axis=0) # 这里扩展应该是为了匹配模型当中预留的位置
      # Actual detection.
      (boxes, scores, classes, num) = sess.run(
          [detection_boxes, detection_scores, detection_classes, num_detections],
          feed_dict={image_tensor: image_np_expanded})
      # Visualization of the results of a detection.
      vis_util.visualize_boxes_and_labels_on_image_array(
          image_np,
          np.squeeze(boxes),
          np.squeeze(classes).astype(np.int32),
          np.squeeze(scores),
          category_index,
          use_normalized_coordinates=True,
          line_thickness=8)
      plt.figure(figsize=IMAGE_SIZE)
      plt.imshow(image_np)

参考文献:
tf.GraphDef()载入模型
https://medium.com/@dboyliao/簡介-tensorflow-serialization-3be0aa39d585

protobuf


这里tensorflow有很多基于某些数据集已经训练好的model :


可以做为pretrainmodel

这里基于COCO数据集的与训练的model有: ssd_mobilenet_v1,v2, ssdlite_mobilenet_v2, ssd_inception_v2, fast_RCNN基于resetnet,inception的,详情参见上面的链接

kitti数据集只有一个与训练模型 基于resnet101的faster_RCNN的model

open_image模型” faster_rcnn de inception和resnet的集合

还有ava-model(这一部分不知道是什么) 基于resnet101的faster_RCNN的model

对于这个文档的某些部分进行了相关的翻译,但是实际觉着没啥必要,还是附上链接:
https://www.cnblogs.com/luxuriance-lily/p/9095801.html


object_detection体量比较大,后续应该还会跑一下分割的模型,暂时week5的更新到这里结束。