作业内容翻译:@胡杨([email protected]) && @面包君 && Fantzy同学
校正与调整:寒小阳 && 龙心尘
时间:2016年7月
出处:http://blog.csdn.net/han_xiaoyang/article/details/51815683
http://blog.csdn.net/longxinchen_ml/article/details/51814343
说明:本文为斯坦福大学CS224d课程的中文版内容笔记,已得到斯坦福大学课程@Richard Socher教授的授权翻译
原本打算把作业和答案做个编排,一起发出来给大家看,无奈代码量有点大,贴上来以后文章篇幅过长,于是乎题目的代码解答放到了百度云盘,欢迎自行下载和运行或者调整。
在本题中,我们会执行一个线性分类器,并会用到下面的交叉熵损失函数:
a.(4分)
在脚本q1_softmax.py
中,通过TensorFlow来构造softmax 函数。softmax的公式如下:
请注意,你不能使用Tensorflow内建的tf.nn.softmax
函数或者其他相关内建函数。这道题目做好的标准是你能够通过运行python q1_softmax.py
命令将这个函数跑起来(需要跑通一个测试用例。当然,不要求完全详尽的测试。)
b. (4分)
在脚本q1_softmax.py
中,通过TensorFlow来构造交叉熵损失函数(Cross Entropy Loss)。交叉熵损失函数的公式长这个样子:
在这里 y∈ℝ5 是一个one-hot标签向量, Nc 是所有类目的数量。请注意,你不能使用Tensorflow内建的cross-entropy
函数或者其他相关内建函数。这道题目做好的标准是你能够通过运行Python q1_softmax.py
脚本将这个函数跑起来(需要写一个测试用例。当然,这同样不要求完全详尽的测试。)
c. (4分)
请仔细学习model.py
脚本中的model
类。并简要解释一下其中占位符变量 (place holder vaiables)和填充字典(feed dictionaries)函数的目的. 在q1_classifier.py
中填充好add_palceholders
和creat_feed_dict
这两个函数.
提示: 请注意配置变量(configuration variables)在config
类中已经储存好了,而在代码中你会用到它们。
答案:在Tensorflow 计算图中,占位符变量是作为其输入节点而存在的。而填充字典函数说明了在计算的时候怎么给这些占位符(或者其他)变量赋值。
d. (4分)
在脚本ql_classifier.py
中的add_model
函数里完成一个用softmax进行分类的基本流程。并在同一个脚本中的函数add_loss_op
中补充好交叉熵损失函数。 请注意,使用本题之前提供的函数,而不是 Tensorflow 内建函数。
e. (4分)
在脚本ql_classifier.py
中填充完成add_training_op
函数。 并解释为什么Tensorflow 的自动微分功能能省去我们直接求梯度的工作。你可以在我们提供的数据上顺利运行python ql_classifier.py
命令,并且确保能够跑通测试用例。这样你才能保证你的模型是合适的。
提示:请一定要使用在config
类中定义的学习率.
答案:只要正确定义好了计算图,Tensorflow就能自动应用反向传播算法来计算梯度。
这一节中,我们会练习反向传播算法和训练深度神经网络,通过它们来实现命名实体识别。命名实体识别是指识别文本中具有特定意义的实体,主要包括人名、地名、机构名、专有名词等。
这其实就是一个5类分类问题(5选1的单选题)。这5类包括上面这4类和1个非实体类——也就是不代表任何实体的类(大部词语都属于这类)。
这个模型是一个单隐藏层神经网络,它有一个类似我们在word2vec中看到的表示层。 这里我们不需要求平均值或者抽样,而是明确地将上下文定义为一个“窗口”,这个“窗口”包含目标词和它左右紧邻的词,是一个3d维度的行向量:
(part a)
(5分)请计算损失函数 J(θ) 在下列模型各个参数上的梯度
值得注意的是,损失函数在模型各参数上的梯度应该化简到可以使用矩阵运算的形式。(通过作业1,相信您已经掌握的很熟练了:))
答案:
由提示公式可得:
(part b)(5分)
神经网络的训练过程往往不是一帆风顺的,许多小问题都会影响模型的性能,例如神经网络中参数过多会导致训练太慢,神经元之间相关性太强会导致模型陷入局部最优。为了避免上述两种情况的发生,我们在(part a)损失函数的基础上增加一个高斯先验。可别小看这个高斯先验,它能使我们的模型参数/权重不会有那么夸张的幅度(更向0值靠拢),同时又能保证大部分参数都有取值(不会对起作用的特征产生太大影响)
,从而保证了模型在实际场景中的可用性(泛化能力强一些)
。混合后的损失函数就长这样。
(part c)(5分)
在part b中我们了解到,如果神经元之间的相关性太强,模型就容易陷入局部最优,从而降低了模型的泛化能力。对此,我们的解决方法是使用L2正则化项对模型参数加以限制。在本题中,我们提供另外一种方法,叫做“参数随机初始化”。在众多参数随机初始化的方法中,我们使用最多的是Xavier方法。
Xavier方法的原理是这样的:给定一个 m×n 的矩阵 A 和一个区间[ −ϵ,ϵ ],从该范围中进行均匀采样作为 Aij ,其中
q2_initialization.py
的
xavier_weight_init
中,用代码来实现一下吧。
(part d)(20分)
在q2_NER.py中,我们实现了一个命名实体窗口(NER Window)模型。模型的输入是sections(小编注:sections在特定情况下可以看作是一句查询,每个section由一些tokens组成,即分词),输出就是命名实体的标签。您可以看一下代码,您刚刚推导的反向传播过程在代码中已经被实现了,是不是很神奇!?
以下工作需要您来完成:
q2_NER.py
中实现命名实体窗口模型部分的代码,我们会根据算法正确性和程序是否可执行来进行打分。q2_test.predicted
文件中,格式为一行一个label,我们会根据真实结果来评估您模型的泛化能力。max_epchs
设为1,并且将load_data
中debug
参数设为True
。在这一节,你将首次实现一个用于建立语言模型的递归神经网络。
语言模型的建立是NLP技术的核心部分,语言模型被广泛使用于语音识别,机器翻译以及其他各类系统。给出一串连续的词 x1,…,xt ,关于预测其后面紧跟的词 xt+1 的建模方式是:
你的任务是实现一个递归神经网络,此网络利用隐层中的反馈信息对“历史记录” xt,xt−1,…,x1 进行建模。关于 t=1,…,n−1 形式化的表示如文献[3]所描述:
输出向量 ŷ (t)∈ℝ|V| 是面向整个词库的概率分布,我们需要最优化交叉熵(非正则化的)的损失率:
(a) (5分)
通常地,我们使用困惑度来评估语言模型的性能,其定义形式如下:
解答:使用的样例 y(t) 为one-hot模型,且假定 y(t)i 是 y(t) 中唯一的非零元素。于是,记:
(b) (5分)
正如注释[2]所描述的操作,在时刻 t 关于单点全部模型参数的梯度计算如下:
此外,还要计算代表前趋隐层权值的导数:
解答:调用函数 ddzsigmoid(z)=sigmoid(z)(1−sigmoid(z)) 。
(c) (5分)
下面为一步迭代的网络框图:
绘制下面三次迭代所“展开”的网络,并且计算迭代时后向传播的梯度:
最好参考讲义[5]所描述的后向传播原理去将这些梯度表达成残差的形式:
注意一个训练样本的实际梯度是需要我们将直到 t=0 时刻的整条后向路径都使用后向传播算法迭代完成后才能得到的。在练习中,我们通常只需截取固定数值\tau\approx3-5 τ≈3−5 的后向传播步骤即可。
解答:
还是延续前一节的条件
\left.\frac{\partial{J^{(t)}}}{\partial{b_1}}\right|_{(t-1)}=\delta^{(t-1)}\odot\mathrm{sigmoid}^{'}(h^{(t-2)}H+e^{(t-1)}I+b_1)
(d) (3分)
对于给定的 h(t−1) ,执行一轮前向传播计算 J(t)(θ) 需要多少操作?执行一轮后向传播又会如何呢?执行 τ 轮呢?对于维度参数组 d,Dh 和 |V| ,使用符号“大写-O”来表示你的答案(如公式14)。是否该慎重考虑这一步?
回忆各个参数的设置: h(t−1) 大小为 Dh , e(t) 大小为 d ,还有 ŷ (t) 的大小为 |V| 。计算 e(t) 花费的时间 O(d) 视矩阵 L 而定。计算 h(t) 花费的时间 O(D2h+dDh) ,计算 ŷ (t) 花费时间 O(Dh|V|) 。后向传播和前向传播算法具有相同的时间复杂度,于是,计算 τ 轮的前向或后向传播复杂度仅通过 τ 与先前得到的复杂度值相乘即可。由于 |V| 比较大的原因,计算 ŷ (t) 的“较慢的一步”计算需花费时长 O(Dh|V|) 。
(e) (20分)
在代码q3_RNNLM.py
中实现以上的模型。其中已经实现了数据加载器和其它的初始化功能代码。顺着已有代码的指引来补充缺失的代码。执行命令行python q3_RNNLM.py
将运行该模型。注意,在运行过程中你不能用到tensorflow库的内置函数,如rnn_cell
模块。
在ptb-train
数据集中训练该模型,其中包含Penn Treebank中WSJ语料集的前20节语料。正如在Q 2部分中所说的,你应该最大限度地提高该模型在实际生产中数据集上的泛化性能(即最小化交叉熵损失率)。关于模型的性能,我们将通过未知但类似的句子集来进行评估。
撰写实验报告的一些要求:
- 在你实验报告中,应包括最优超参数(如训练框架、迭代次数、学习率、反馈频率)以及在
ptb-dev
数据集上的困惑度。正常情况应当保持困惑度值低于175。- 在报告中还应当包含最优模型的相关参数;方便我们测试你的模型。
- 提示:在调试过程中把参数
max_epochs
的值设为1。在_init_
方法中通过设置参数关键字debug=True
来开启对load_data
方法的调用。- 提示:该模型代码在GPU上运行很快(低于30分钟),而在CPU上将耗时多达4小时。
(f) (7分)
作业1和该作业中的q2部分所示的神经网络是判别模型:他们接收数据之后,对其做出预测。前面你实现的RNNLM模型是一个生成式模型,因为它建模了数据序列 x1,…,xn 的分布状况。这就意味着我们不仅能用它去评估句子生成概率,而且还能通过它生成概率最优的语句!
训练完成后,在代码q3 RNNLM.py
中实现函数generate_text()
。该函数首先运行RNN前向算法,在起始标记<eos>
处建立索引,然后从每轮迭代的 ŷ (t) 分布对新词 xt+1 进行采样。然后把该词作为下一步的输入,重复该操作直至该模型遇到一个结束标志(结束标志记为</eos>
)。
撰写实验报告的一些要求:
- 在你的实验报告中至少包含2-3条的生成语句。看看你是否能产生一些妙趣横生的东西!
一些参考建议:
如果你想用语言模型继续实验,非常欢迎你上传自己的文档和对其训练成果——有时会是一个令人惊喜的实验结果!(可从网站http://kingjamesprogramming.tumblr.com/ 上获取以纪念版《圣经》和书籍《计算机程序设计与实现》为混合语料进行训练的伟大成果。)
[1]Optional (not graded): The interested reader should prove that this is indeed the maximum-likelihood objective when we let Wij∼N(0,1/λ) for all i,j .
[2]This is also referred to as Glorot initialization and was initially described in http://jmlr.org/proceedings/papers/v9/glorot10a/glorot10a.pdf
[3]这个模型可以参考Toma Mikolov的论文, 发表于2010年: http://www.fit.vutbr.cz/research/groups/speech/publi/2010/mikolov_interspeech2010_IS100722.pdf
[4]我们使用Tensorflow库函数计算此处的损失率。
[5]http://cs224d.stanford.edu/lectures/CS224d-Lecture5.pdf