🚀 原文地址:https://rasa.com/docs/rasa/nlu-training-data

NLU 训练数据存储的是用户信息的结构化信息。

NLU(自然语言理解) 的目标是从用户信息中提取结构化信息,通常包含用户的意图以及实体。我们也可以在训练数据中添加额外的信息,如正则表达式和查找表,来帮助模型正确地识别意图和实体。

1. 训练样例

NLU 训练数据由经过意图分类的用户话术的样例组成,为了更方便地使用意图,我们通常对意图的命令与期望用户完成的事情非常相关。注意:保持意图名称小写,避免空格和特殊符号。

💡 / 符号作为分隔符被保留,其目的是将检索意图与文本响应标识区分开。实际场景中,确保不要以意图的名称来使用它。

2. 实体

实体是用户消息中结构化的信息,如果想要实体起作用,你需要通过训练数据来训练一个机器学习模型,或者你可以使用基于字符模式的 RegexEntityExtractor ,定义正则表达式来提取实体。

在决定需要提取哪些实体时,考虑一下机器人需要什么信息来实现用户的目标。因为用户可能会提供任何其他用户目标无关信息,所以我们不需要提取这些作为实体。

3. 同义词

同义词是将所提取的实体映射到文字中提取出来的其他值,在用户以多种方式描述同一事物时,我们可以使用同义词。考虑到提取实体的最终目标,我们应该找出那些等效的词语。

假设已经有一个 account 实体,它用于查询用户的余额,一种可能的账户类型就是 credit。用户也可能将 credit 账号描述成 credit account 或者 credit card account

在下面的案例中,我们将 credit accountcredit card account 定义成 credit 的同义词。

  1. nlu:
  2. - synonym: credit
  3. examples: |
  4. - credit card account
  5. - credit account

如果上述短语中任意一个被提取为实体时,都会被映射到 credit

:::info 💡 提供训练数据
————————
同义词仅仅在提取实体后发生,这表示为了让模型能识别出实体并将它们替换为 credit,训练示例中应该包括同义词样本(credit accountcredit card account )。 :::

4. 正则表达式

你可以在管道中使用 RegexFeatuizerRegexEntityExtractor 组件,使用正则表达式来改进意图分类和实体提取。

4.1 意图分类的正则表达式

你可以通过在管道中启用 RegexFeatuizer 组件,并使用正则表达式来提升意图分类。当使用 RegexFeaturizer 时,正则表达式并不会作为意图分类的规则,它仅仅提供在进行意图分类时,分类器将会使用 RegexFeaturizer 进行模式学习的功能。目前,所有的意图分类器都可利用正则特性。

正则表达式的命名应该遵循可读性高的要求,这样做的目的是能让你记住该正则用于做什么,比将之作为对应模式特性的标题。正则表达式不必匹配任何意图或实体, help 正则请求如下所示:

  1. nlu:
  2. - regex: help
  3. examples: |
  4. - \bhelp\b

上述正则匹配的意图可以是 greethelp_meassistance 或者其他东西。

在创建正则表达式时,请采用尽可能少的单词的方式。例如,使用 \bhelp\b 来替代 help.*,因为后面一种会匹配整个消息,而前面一个只会匹配一个单词。

:::info 💡 提供训练样本
————————
RegexFeaturizer 组件给意图分类器提供特征,但它并不直接用作意图预测。为了意图分类器可以学习到使用正则表达式的特性,我们应该在训练数据中提供足够多包含正则表示的样本。 :::

也就是说,Rasa 并没有直接将正则表达式进行应用,而是通过学习样例来获得使用正则表达式的能力。下面我们来看一个示例:

  1. nlu:
  2. - regex: regex_001
  3. examples: |
  4. - 奥{1,4}
  5. - 啊{1,4}
  6. - 哦{1,4}
  7. - 哎{1,4}
  8. - 唉{1,4}
  9. - 呃{1,2}
  10. - 哈{2,3}
  11. - 呵呵
  12. - 嘿嘿
  13. - intent: intent001
  14. examples: |
  15. - [呵呵](regex_001)
  16. - [嘿嘿](regex_001)
  17. - regex: regex_002
  18. examples: |
  19. - \b认识啊\b
  20. - intent: intent002
  21. examples: |
  22. - [认识么](regex_002)
  23. - [认识啊](regex_002)

作为用户,如果输入“啊”是可以击中 intent_001 的。但如果输入 “啊啊啊啊”,虽然 regex_001 正则中有“啊啊啊啊”匹配模式,居然击中的是 intent_002。在 DEBUG 模式中,可以看到“啊啊啊啊”确实被 regex_001 击中了,但是intent_001intent_002 都被击中了,但是 intent_002 的置信度更高,所以 Rasa 会给出 intent_002 的响应。

我们希望匹配上 intent_001 意图,那么我们需要添加样本 - [啊啊啊啊](regex_001)。模型重新训练之后,就可以击中所期望的 intent_001 意图了。

4.2 提取实体的正则表达式

如果实体具备确定性的结构,我们可以通过以下两种方式来使用正则表达式:

  • 将正则表达式作为特征
  • 将正则表达式用作基于规则下的实体提取

    1)将正则表达式用作特征

    我们可以基于 NLU 管道中 RegexFeaturizer 组件使用正则表达式来创建特征。基于 RegexFeaturizer 组件使用正则表达式时,正则名称是无关紧要的,并且它可以提供一个特性帮助模型学习意图(或者实体)与符合正则的输入之间的关联。

目前用于实体提取的正则表达式功能仅仅支持 CRFEntityExtractorDIETClassifier 组件,对于其他实体提取器(例如 MitieEntityExtractor 或者 SpacyEntityExtractor),并不会使用生成的特征,正则表达式也不会帮助这些提取器提高实体识别的性能。

2)将正则表达式用作基于规则下的实体提取

在 NLU 管道中启用 RegexEntityExtractor 组件后,我们可以将正则表达式用作基于规则的实体提取。

当使用 RegexEntityExtractor 组件时,正则的名称应该与所要提取的实体名称相匹配。例如,通过在训练数据中包含以下表达式(至少需要 2 个标注样本),就可以从账号中提取出 10-12 位数字。

  1. nlu:
  2. - regex: account_number
  3. examples: |
  4. - \d{10,12}
  5. - intent: inform
  6. examples: |
  7. - my account number is [1234567891](account_number)
  8. - This is my account number [1234567891](account_number)

上述示例表明,每当用户信息中包含 10-12 位数字序列时,它将被提取为 account_number 实体。值得一提的是,RegexEntityExtractor 组件并不需要训练样本去学习提取实体,但是你必需给出至少两个带标注的实体样本,以便模型在训练时可以将其注册为实体。

5. 查找表

查找表用于生成不区分大小写正则表达式的单词列表,使用的方式与正则表达式相同,并且它还可以与管道中 RegexFeaturizerRegexEntityExtractor 结合使用。

在已知实体的一组可能值时,我们可以使用查找表来进行实体提取,在定义查找表时尽可能具体些。例如,为了提取国家名,我们可以添加一个所有国家的查找表:

  1. nlu:
  2. - lookup: country
  3. examples: |
  4. - Afghanistan
  5. - Albania
  6. - ...
  7. - Zambia
  8. - Zimbabwe
  • 当查找表与 RegexFeaturizer 结合使用时,为了模型可以学习使用生成的正则表达式作为特征,我们需要给所需匹配的实体和意图提供足够多的样本。
  • 当查找表与 RegexEntityExtractor 结合使用时,至少提供两个带标注的实体示例,以便模型在训练时将其注册为实体。

    6. 实体角色和组

    :::info 🔔 New in 2.8
    ———————
    在 2.8 版本中,实体角色和组不再是实验性功能,该特性将永久保留。 :::

在将单词标注为自定义实体时,允许在训练数据中定义确定的概念。例如,我们可以通过标注来识别以下城市:

  1. I want to fly from [Berlin]{"entity": "city"} to [San Francisco]{"entity": "city"} .

但是,很多时候我们还想要给实体添加一些额外的信息。

例如,当构建预定航班的机器人时,它需要知道航班设涉及到上述示例中哪两个城市,以及哪个是出发城市,哪个是目的地。BerlinSan Francisco 都是城市,但它们在消息中的角色却是不同的。为了区分不同的角色,在添加实体标签的基础上,我们还需要分配角色标签。

  1. - I want to fly from [Berlin]{"entity": "city", "role": "departure"} to [San Francisco]{"entity": "city", "role": "destination"}.

同样地,我们还可以通过 group 标签对不同实体进行分组。例如,分组标签可以用来定义不同订单。在以下的示例中,分组标签指定条料搭配何种披萨以及披萨的大小。

  1. Give me a [small]{"entity": "size", "group": "1"} pizza with [mushrooms]{"entity": "topping", "group": "1"} and a [large]{"entity": "size", "group": "2"} [pepperoni]{"entity": "topping", "group": "2"}

提取器将返回包含检测到的角色/分组标签的实体对象:

  1. {
  2. "text": "Book a flight from Berlin to SF",
  3. "intent": "book_flight",
  4. "entities": [
  5. {
  6. "start": 19,
  7. "end": 25,
  8. "value": "Berlin",
  9. "entity": "city",
  10. "role": "departure",
  11. "extractor": "DIETClassifier",
  12. },
  13. {
  14. "start": 29,
  15. "end": 31,
  16. "value": "San Francisco",
  17. "entity": "city",
  18. "role": "destination",
  19. "extractor": "DIETClassifier",
  20. }
  21. ]
  22. }

💡 实体角色和分组目前仅仅支持 DIETClassifierCRFEntityExtractor

为了正确训练带有角色和分组的模型,对于每种实体和角色(或分组)的组合,请确保包含足够多的训练样本。为了提高模型的泛化性,确保在训练数据中保留一些变化样本。例如,对于 fly FROM x TO y 样本,还可以添加 fly TO y FROM x 样本。

如果要对带有特定角色/分组的实体进行槽值的填充,你需要使用表单定义自定义插槽映射,或者使用自定义动作直接从跟踪器中提取相关实体。

6.1 影响对话预测带角色和分组的实体

:::info 💡 如果想通过角色或者分组来影响对话的预测,我们需要在故事包含期望的角色或分组表签。同时,我们还需要在域文件中列出实体相关的角色和分组。 :::

假设我们想要根据用户为位置输出不用的句子,例如,用户刚从伦敦来,我们可以问问去伦敦的行程,但是如果用户正在前往马德里的路上,机器人可能会祝福用户玩得愉快。你可以通过以下两个故事来完成此功能:

  1. stories:
  2. - story: The user just arrived from another city.
  3. steps:
  4. - intent: greet
  5. - action: utter_greet
  6. - intent: inform_location
  7. entities:
  8. - city: London
  9. role: from
  10. - action: utter_ask_about_trip
  11. - story: The user is going to another city.
  12. steps:
  13. - intent: greet
  14. - action: utter_greet
  15. - intent: inform_location
  16. entities:
  17. - city: Madrid
  18. role: to
  19. - action: utter_wish_pleasant_stay

7. BILOU 实体标记

DIETClassifierCRFEntityExtractor 都有一个 BILOU_flag 的选项,它表示在处理实体时,机器学习模型可以使用标注模式。BILOU 是 Beginning、Inside、Last、Outside 和 Unit-length 的缩写。

  1. [Alex]{"entity": "person"} is going with [Marty A. Rick]{"entity": "person"} to [Los Angeles]{"entity": "location"}.

上面示例中,训练样本首先被拆分为 token 的列表,然后机器学习模型将根据 BILOU_flag 的值,应用下面对应的标注模式。

token BILOU_flag = true BILOU_flag = false
alex U-person person
is O O
going O O
with O O
marty B-person person
a I-person person
rick L-person person
to O O
los B-location location
angeles L-location location

与普通的标记架构相比,BILOU 所含信息将更丰富。在机器学习预测实体时,基于此可以提高性能。

:::info 💡 不一致的 BILOU 标签
———————————
BILOU_flag 设置为 True 时,模型可能预测出不一致的标签,例如 B-person、I-location、L-person。Rasa 使用启发式来清理不一致的 BILOU 标签。例如,B-person、I-location、L-person 将会变为 B-person、I-person、L-person。 :::