# 训练意图和实体

意图 代表一句话的主要意思,实体 则代表了一句话中特定的元素,我们看下面这个订机票的场景:

I want to book a flight from Paris to Montreal

上例中的意图是订一张从巴黎到蒙特利尔的机票,我们把这个意图称为 book_flight,而巴黎和蒙特利尔则是实体,我们把他们分别称作 fromto

下面是更多的语料:

Is there a flight from Rome to London tomorrow?
I wanna fly from The big apple to the city of light

在 Botfront 中这样处理:

把上面的语料训练生成模型之后再看,你会发现无论你在 User says... 那个输入框里面输什么,只要是跟上面类似的句子都会自动识别出来 fromto 实体。

注意

因为这个例子中只有一个意图,所以你只看到一个,如果要用到意图分类器你至少需要两个或以上意图让分类器工作。

# 实体同义词

上例中如果我们想把订票意图传给一个订票网站或者比价服务,我们可能需要用到机场编码,实体同义词可以帮我们处理这个问题。比如我们把上例中提到的 the city of light 同义词设成 CDG, The big apple 同义词设成 JFK,重新训练后我们得到了两个实体的返回分别变成了 CDGJFK

提示

事实上,手动添加同义词的方式并不常见,与之对应的,你应该训练实体抽取器去把各种意图和这些意图的同义词自动抽取出来,可以通过添加更多的、包含同义词的语料来实现。

截至目前,我们假设我们的用户不会手误输入错误的单词,而且同义词的方式也无法帮助模型识别诸如: the big aple 或者 citi of lite 这种情况。

gazette 可以解决这个问题!

# Gazettes

Gazettes 在你希望获得的实体的值是在一个可控范围内时特别有用,尤其是当你希望规范化用户的输入的时候。比如在输入颜色、品牌或者城市等这些内容的场景,下例中我们希望会话实体中提取的值是一种合理的颜色,比如 red 或者 blue,我们希望确保两件事情:

  1. 如果实体抽取器 CRFEntityExtractor 获取的实体值是不在白名单中的颜色, yellow,这种情况我们不希望 NLU 把它作为 color 的实体值抽取、返回。
  2. 如果用户输入的颜色在白名单中,但可能有点输入错误(在可辨识范围中),这种情况我们希望 NLU 把它作为实体值抽取、返回。

只要简单的创建一个输入错误容忍表(其实拼错 Paris 或者 New York 的方式也没多少)就可以了, spelling latitude 通过这张表计算一个模糊度,100分是说对拼写错误 0 容忍, 0 是无限量容忍,也就是说无论用户输入什么,哪怕输入的是一个风马牛不相及的东西,系统都会返回一个实体的值。

注意

当实体抽取器遇到 citi of lite 这种情况时,它会拿这个跟 Gazette 列表中所有的词比较模糊度,发现最匹配的一项是 the city of light ,于是走到同义词处理流程,发现 CDG 是对应的同义词值,然后就返回这个 CDG 作为实体的值。但如果实体抽取器获取到的词的模糊度指标低于整个 Gazette 列表的最低分时,也就意味着这个值是不在该范畴,所以也就没有针对这个实体的实体值返回。

# 复合实体

Duckling 是由 Facebook 开源的一个用来抽取结构化实体的包,在处理比如数字、钱数、电子邮件、日期等场景非常有用。它可以跟 Rasa 配合使用,Botfront 已经把它打包在了 docker-compose 里面(当你用 Botfront 的命令行启动 Botfront 的时候,有一个 Duckling 的容器也以服务的方式被启动了)。

作为一个解析器,Duckling 只能通过模式识别的方式识别一句话中的固定模式,但无法识别对应实体的属性描述,比如下面这个例子:

I want 2 beers and 3 cokes

Duckling 会返回两个 number 实体的实例,但不会识别出来具体是那种饮料的数量。

幸运的是,Botfront 内置了一个专门的实体抽取器来处理这种场景,这个抽取器会把 2 识别为 beers_count 实体,把 3 识别为 cokes_count 实体。

像下面这样把这个组件加到 CRFEntityExtractor 管道下面(注意顺序)就好:

- name: CRFEntityExtractor
  ...
- name: rasa_addons.nlu.components.duckling_http_extractor.DucklingHTTPExtractor
  url: http://duckling
  dimensions:
  - "number"
  
- name: rasa_addons.nlu.components.duckling_crf_merger.DucklingCrfMerger
  entities:
    beers_count: ["number"]
    cokes_count: ["number"]

像这样,beers_countcokes_count 就会被正确的识别出来,然后其他部分的逻辑就可以用这两个值来做进一步的处理。如果 CRFEntityExtractor 没有正确的抽取出来一个 number,那么后面这些处理逻辑也就不会发送。

顺序,很重要!

  • 对话中的实体首先是由 ner_crf 组件抽取出来的。
  • 接下来 Duckling 负责抽取数字。
  • 然后 DucklingCrfMerge 组件负责把上面得到的数字组合在一起,得到 beers_count 是 2。

# 配置管道

为了上面的例子能正常的工作,我们需要确保管道按照正确的顺序执行:

  1. 首先 CRFEntityExtractor 组件从会话中抽取 citi of lite 实体。
  2. Gazette 组件用 city of the light 实体替换掉 citi of lite 实体。
  3. EntitySynonymMapper 组件把实体 the city of light 映射到 CDG 实体。

# 多样化的语料

语料多样化是创建一个高可用性模型的关键。

正面例子

I want to book a flight from Paris to Montreal
Is there a flight from Rome to London tomorrow?
I wanna fly from The big apple to the city of light

而下面的例子则会影响你模型的泛化能力。

负面示例

I want to book a flight from Paris to Montreal
I want to book a flight from Rome to London tomorrow?
I want to book a flight from The big apple to the city of light

# 拼写错误

拼写错误不仅会影响实体抽取,也会影响意图分类。上面我们演示了 gazette 是如何帮我们在实体抽取过程中处理拼写错误的,其实在意图分类场景中它也可以起到同样的作用,即便是数据量较小的场景也是如此。

提示

你的数据集分布必须非常接近实际用户输入内容的分布,也就是说如果实际场景中用户输入有很大比例是有输入错误的,那么你的训练集里面也应该有对应比例的笔误数据。