前言
原项目Theano的github地址:https://github.com/julianser/hed-dlg-truncated
论文地址: https://arxiv.org/abs/1605.06069
首先是对模型ADEM介绍以及对评估模型的学习。 ADEM,short for “automatic dialogue evaluation model”,对话自动评估模型。模型在利用RNN(recurrent neural network,循环神经网络)半监督式(semi-supervised)训练下来评分。
有关RNN的学习就不浪费时间整理已有的东西,重点精力放在我在此项目的总结与感受。有关RNN的开篇学习,我参考了这篇https://blog.csdn.net/zhaojc1995/article/details/80572098,markdown有关数学公式的编辑参考了这篇简书https://www.jianshu.com/p/e74eb43960a1
一 The aim of the project
NLP中重要一环是对“dialogue”的评价,而dialogue中包括reference(人工回复作为“参考”)和candidate(模型给出的答复),基本思路是利用一些评估标准来对比candidate和reference,并给出一个评分。本项目就是基于原有的评估思路上,给定一系列responses,得到一个准确的人工评分。responses要有一定的多样性,包含相关、不相关的,也需要包含连贯性强和连贯性较弱的内容。
为了达到上述的“多样化的responses”,研究人员们采用了4种模型产生的candidate responses。
- TF-IDF retrieval-based model
- Dual Encoder(DE)
- responses generated using the hierarchical recurrent encoder-decoder (HRED) model
- human-generated models
评估方式
Word-Overlap Metrics
利用词语的覆盖程度来计算。其中一个最广为使用的当属BLEU和METEOR,两者广泛应用于评价机器翻译的方法当中。但是机器翻译指标与人类评估结果还存在较大差异,尤其在对话系统中,尽管一个对话的responses可以跟reference词语重叠很少(导致Word-Overlap评分很低),但可以被人类评价为一个“好的回复”。
BLEU尽管可以采用n-gram方法来优化评价方式,但在对话评价上有自己的局限性。机器翻译工作中词语选来选去大多不离那些candidate,所以bleu可以很好的被应用。但是在对话当中,与reference词语重叠程度小,也可以是好的回复,这里如果仅以BLEU评分作为评估就显得不合理了。而且回复也需要考虑前后的语境(context)。
ADEM
Automatic Dialogue Evaluation Model是为了弥补word-overlap metrics的缺陷。而且(1)能够捕捉到语义相似性 (2)挖掘语境和参考回答对于评分模型的作用。
二 train.py 中的小细节
argparse模块使用。
argparse是python用于解析命令行参数和选项的标准模块,类似于linux中的ls指令,后面可以跟着不同的参数选项以实现不同的功能,argparse就可以解析命令行然后执行相应的操作。
使用时需要三步(步骤总结来自于python中argparse模块使用):
- 创建 ArgumentParser() 对象
- 调用 add_argument() 方法添加参数
- 使用 parse_args() 解析添加的参数
1 | def parse_args(): |
os.path模块
os.path.exists()
判断括号里的文件是否存在的意思,括号内的可以是文件路径。而且返回值是Boolean类型。1
2
3def create_experiment(config):
if not os.path.exists(config['exp_folder']):
os.makedirs(config['exp_folder'])
eval函数
eval()
返回括号里表达式的值
三 python中的下划线
多学了python后对python当中的下划线很感兴趣。不同于c或者java中局限于“命名作用”作用的下划线,python中的下划线在表示特殊含义方面上有着重要作用。
在创建类的时候,也会较易发现def __init__():
,只要有自动补全的IDE,就会默认出现下划线,前后都有。而不同的函数下划线长度还不同(长度为1或为2,前后出现表意也不同)。这边引起了我的好奇。
参考文章:《Python中下划线的5种含义》和The Meaning of Underscores in Python
- 单前导下划线: _var
- 单末尾下划线:var_
- 双前导下划线:__var
- 双前导和末尾下划线:var
- 单下划线:_
单前导下划线 _var
Intended for internal use. It is defined there. 但是python并不强制要求,而且不像Java,Python对“公共”和“私有”的区分并不那么明显。就像一种提示:
嘿,这其实不是这个类的一个公共接口,最好别管它。
举例如下:
1
2
3
4 class Test:
def __init__(self):
self.foo = 11
self._bar = 23
实例化之后会发生啥呢?
1 | t = Test() |
所以单前导下划线并没有阻止我们访问到方法_bar
但是如果使用通配符从模块中导入名称的话,python不会导入带有单前导下划线名称的方法。(我不确定在python中叫“方法”是否合适,欢迎指摘。)
比如,命名一个my_modelue
有一下代码:1
2
3
4
5
6
7# This is my_module.py:
def external_func():
return 23
def _internal_func():
return 42
此时,如果用通配符*导入所有名称,则Python不会导入有单前导下划线的名称(除非模块定义了覆盖此行为的all列表)1
2
3
4
5from my_module import *
external_func()
23
_internal_func()
NameError: "name '_internal_func' is not defined"
而如果常规导入则不会受此影响:1
2
3
4
5import my_module
my_module.external_func()
23
my_module._internal_func()
42
单末尾下划线 var_
有时候名称被一个keyword占用了,命名的时候就不得不避开这些Python认定的关键字,下面的例子是用class_
代替class
来表示传递给方法的参数。1
2
3
4
5def make_object(name, class):
SyntaxError: "invalid syntax"
def make_object(name, class_):
pass
其实就是为了避免冲突而采用的。
双前导下划线: __var
A double underscore prefix causes the Python interpreter to rewrite the attribute name in order to avoid naming conflicts in subclasses.
这将导致Python解释器重写属性名称避免在子类当中冲突。
这其实也称作 name mangling. 解释器改变变量名称以使得后续扩展类的时候更不容易发生冲突。
例子:1
2
3
4
5class Test:
def __init__(self):
self.foo = 11
self._bar = 23
self.__baz = 23
使用内置的(built-in) dir() 来查看:
1 | t = Test() |
查看列表,查找我们的原始变量foo
,_bar
和 __baz
我们注意到:
- self.foo变量在属性列表中显示为未修改为foo
- self._bar的行为方式相同 - 它以_bar的形式显示在类上。 就像我之前说过的,在这种情况下,前导下划线仅仅是一个约定。 给程序员一个提示而已。
- 然而对于
self.__baz
而言,情况看来不同。列表中并没有__baz
这个名字的变量
 仔细观察,发现有个_Test__baz
的属性。这就是Python解释器所做的名称修饰。 它这样做是为了防止变量在子类中被重写。
而如果创建另一个扩展Test类的类,并尝试重写构造函数中添加的现有属性:
1 | class ExtendedTest(Test): |
foo, _bar, 和 __baz的值会出现在这个ExtendedTest
类的实例上吗? Let’s take a look.
1 | t2 = ExtendedTest() |
得到的AttributeError
? 名称修饰被再次触发了!事实上,这个对象没有__baz属性:
我们可以看到_baz变成_ExtendedTest__baz以防止意外修改:1
2 t2._ExtendedTest__baz
'overridden'
但原来的_Test__baz还在:1
2 t2._Test__baz
42
 双下划线名称修饰对程序员完全透明。下面例子证实:1
2
3
4
5
6
7
8
9
10
11class ManglingTest:
def __init__(self):
self.__mangled = 'hello'
def get_mangled(self):
return self.__mangled
ManglingTest().get_mangled()
'hello'
ManglingTest().__mangled
AttributeError: "'ManglingTest' object has no attribute '__mangled'"
还有另一个例子:1
2
3
4
5
6
7
8
_MangledGlobal__mangled = 23
class MangledGlobal:
def test(self):
return __mangled
MangledGlobal().test()
在这里声明了一个名为_MangledGlobal__mangled
的全局变量。然后在名为MangledGlobal的类的上下文中访问变量。由于名称修饰,我们能够在类的test()方法内,以mangled来引用_MangledGlobalmangled全局变量。
Python解释器自动将名称mangled扩展为_MangledGlobalmangled,因为它以两个下划线字符开头。这表明名称修饰不是专门与类属性关联的。它适用于在类上下文中使用的两个下划线字符开头的任何名称。
双前导和双末尾下划线 var
如果一个名字同时以双下划线开始和结束,则不会应用名称修饰。 由双下划线前缀和后缀包围的变量不会被Python解释器修改:1
2
3
4
5
6class PrefixPostfixTest:
def __init__(self):
self.__bam__ = 42
PrefixPostfixTest().__bam__
42
但是,Python保留了有双前导和双末尾下划线的名称,用于特殊用途。 这样的例子有,init对象构造函数,或call — 它使得一个对象可以被调用。
但是,Python保留了有双前导和双末尾下划线的名称,用于特殊用途。 这样的例子有,init对象构造函数,或call — 它使得一个对象可以被调用。
单下划线 _
有时候独立下划线是用作一个名字,来表示某个变量是临时的或无关紧要的。在下面循环中,我们不需要访问正在运行的索引,可以使用_
来表示它只是一个临时变量1
2
3
for _ in range(32):
'Hello, World.') print(
也可以在拆分(unpacking)表达式中将单个下划线用作“不关心的”变量,以忽略特定的值。 同样,这个含义只是“依照约定”,并不会在Python解释器中触发特殊的行为。 单个下划线仅仅是一个有效的变量名称,会有这个用途而已。
在下面的代码示例中,我们将汽车元组拆分为单独的变量,但我只对颜色和里程值感兴趣。 但是,为了使拆分表达式成功运行,需要将包含在元组中的所有值分配给变量。 在这种情况下,_
作为占位符变量可以派上用场:
1 | 'red', 'auto', 12, 3812.4) car = ( |
除了用作临时变量之外,“_”是大多数Python REPL中的一个特殊变量,它表示由解释器评估的最近一个表达式的结果。
这样比较方便,可以在一个解释器会话中访问先前计算的结果,或者,你是在动态构建多个对象并与它们交互,无需事先给这些对象分配名字:1
2
3
4
5
6
7
8
9
10
11
12
13
1420 + 3
23
_
23
print(_)
23
list()
[]
1) _.append(
2) _.append(
3) _.append(
_
[1, 2, 3]
tensorflow-gpu版本使用错误
看到网上一个语言生成模型要使用tensorflow,出了一堆错误。其实是我个人在解释器interpreter里面没有选择对应的tensorflow版本,但乱乱学着别人弄浪费了很多时间。
首先重新安装numpy时出现了
Could not find a version that satisfies the requirement numpy(from versions:) No matching distribution found for numpy
我使用的指令是’ pip install numpy’, 改用 ‘pip3 install numpy’.
Linux
所有命令都找不到了
解决办法:重新设置环境变量PATH
export PATH=/usr/sbin:/usr/bin:/usr/local/bin:/usr/local/sbin:/bin:/sbin
再 source /etc/profile