28 KiB
理解语言
手绘笔记由 Nitya Narasimhan 提供。点击图片查看更大版本。
课前测验
简介
在上一课中,你将语音转换为文本。为了将其用于编写智能计时器程序,你的代码需要理解所说的内容。你可以假设用户会说固定短语,例如“设置一个3分钟的计时器”,并解析该表达式以确定计时器的时长,但这对用户来说并不友好。如果用户说“设置一个计时器,时间为3分钟”,你或我可以理解他们的意思,但你的代码却无法理解,因为它期待的是固定短语。
这就是语言理解的作用所在,它使用AI模型来解释文本并返回所需的细节。例如,它可以同时理解“设置一个3分钟的计时器”和“设置一个计时器,时间为3分钟”,并知道需要设置一个3分钟的计时器。
在本课中,你将学习语言理解模型,如何创建、训练它们,以及如何在代码中使用它们。
本课内容包括:
语言理解
人类已经使用语言进行交流数十万年。我们通过词语、声音或动作进行交流,并理解所说的内容,包括词语、声音或动作的含义以及它们的上下文。我们能够理解真诚和讽刺,使得相同的词语在不同的语气下表达不同的意思。
✅ 想一想你最近的一些对话。哪些部分的对话对计算机来说可能很难理解,因为它需要上下文?
语言理解,也称为自然语言理解,是人工智能领域的一部分,属于自然语言处理(NLP)。它涉及阅读理解,试图理解词语或句子的细节。如果你使用过语音助手,例如Alexa或Siri,那么你已经使用过语言理解服务。这些服务是幕后AI服务,将“Alexa,播放Taylor Swift的最新专辑”转换为我女儿在客厅里随着她最喜欢的音乐跳舞。
💁 尽管计算机取得了许多进步,但它们在真正理解文本方面仍有很长的路要走。当我们提到计算机的语言理解时,并不是指任何接近人类交流的高级能力,而是指从一些词语中提取关键细节。
作为人类,我们无需思考就能理解语言。如果我让另一个人“播放Taylor Swift的最新专辑”,他们会本能地知道我的意思。对于计算机来说,这就困难得多。它需要将词语从语音转换为文本,并解析以下信息:
- 需要播放音乐
- 音乐是由艺术家Taylor Swift创作的
- 具体的音乐是一整张专辑,包含多首按顺序排列的曲目
- Taylor Swift有许多专辑,因此需要按时间顺序排序,最新发布的专辑是所需的
✅ 想一想你在提出请求时说过的一些句子,例如点咖啡或让家人递给你某样东西。试着将这些句子分解为计算机需要提取的信息。
语言理解模型是AI模型,旨在从语言中提取特定细节,然后通过迁移学习针对特定任务进行训练,就像你使用一小组图像训练自定义视觉模型一样。你可以使用一个模型,然后通过你希望它理解的文本对其进行训练。
创建语言理解模型
你可以使用LUIS(Language Understanding Intelligent Service)创建语言理解模型,这是微软认知服务的一部分。
任务 - 创建一个创作资源
要使用LUIS,你需要创建一个创作资源。
-
使用以下命令在你的
smart-timer
资源组中创建一个创作资源:az cognitiveservices account create --name smart-timer-luis-authoring \ --resource-group smart-timer \ --kind LUIS.Authoring \ --sku F0 \ --yes \ --location <location>
将
<location>
替换为创建资源组时使用的位置。⚠️ LUIS并非在所有地区都可用,如果你收到以下错误:
InvalidApiSetId: The account type 'LUIS.Authoring' is either invalid or unavailable in given region.
请选择其他地区。
这将创建一个免费级别的LUIS创作资源。
任务 - 创建一个语言理解应用
-
在浏览器中打开LUIS门户 luis.ai,并使用你用于Azure的同一账户登录。
-
按照对话框中的说明选择你的Azure订阅,然后选择刚刚创建的
smart-timer-luis-authoring
资源。 -
从对话应用列表中,选择新建应用按钮以创建一个新应用。将新应用命名为
smart-timer
,并将文化设置为你的语言。💁 有一个预测资源字段。你可以创建一个单独的资源用于预测,但免费创作资源每月允许1000次预测,足够开发使用,因此可以留空。
-
阅读创建应用后出现的指南,了解训练语言理解模型所需的步骤。完成后关闭该指南。
意图和实体
语言理解基于意图和实体。意图是词语的目的,例如播放音乐、设置计时器或点餐。实体是意图所指的内容,例如专辑、计时器的时长或食物类型。模型解释的每个句子至少应有一个意图,并可选地包含一个或多个实体。
一些示例:
句子 | 意图 | 实体 |
---|---|---|
“播放Taylor Swift的最新专辑” | 播放音乐 | Taylor Swift的最新专辑 |
“设置一个3分钟的计时器” | 设置计时器 | 3分钟 |
“取消我的计时器” | 取消计时器 | 无 |
“点3个大号菠萝披萨和一个凯撒沙拉” | 点餐 | 3个大号菠萝披萨,凯撒沙拉 |
✅ 回想你之前想到的句子,这些句子的意图和实体是什么?
要训练LUIS,首先需要设置实体。这些实体可以是固定术语列表,也可以从文本中学习。例如,你可以提供菜单中可用食物的固定列表,并为每个词提供变体(或同义词),例如茄子和eggplant作为茄子的变体。LUIS还提供了可用的预定义实体,例如数字和位置。
对于设置计时器,你可以使用预定义的数字实体为时间创建一个实体,并为单位(如分钟和秒)创建另一个实体。每个单位可以有多个变体以涵盖单数和复数形式,例如minute和minutes。
定义实体后,你需要创建意图。这些意图是通过你提供的示例句子(称为话语)由模型学习的。例如,对于设置计时器意图,你可以提供以下句子:
设置一个1秒计时器
设置一个计时器,时间为1分12秒
设置一个3分钟计时器
设置一个9分30秒计时器
然后你需要告诉LUIS这些句子的哪些部分映射到实体:
句子设置一个计时器,时间为1分12秒
的意图是设置计时器
。它还有2个实体,每个实体有2个值:
时间 | 单位 | |
---|---|---|
1分钟 | 1 | 分钟 |
12秒 | 12 | 秒 |
为了训练一个好的模型,你需要提供一系列不同的示例句子,以涵盖人们可能提出相同请求的多种方式。
💁 与任何AI模型一样,训练时使用的数据越多且越准确,模型效果越好。
✅ 想一想你可能用不同方式提出相同请求并期望人类理解的情况。
任务 - 向语言理解模型添加实体
对于计时器,你需要添加2个实体——一个用于时间单位(分钟或秒),另一个用于分钟或秒的数量。
你可以在微软文档的快速入门:在LUIS门户中构建应用中找到使用LUIS门户的说明。
-
在LUIS门户中,选择实体选项卡并通过选择添加预定义实体按钮添加数字预定义实体,然后从列表中选择数字。
-
使用创建按钮创建一个新的实体。将实体命名为
时间单位
,并将类型设置为列表。在标准化值列表中添加分钟
和秒
,并在同义词列表中添加单数和复数形式。每添加一个同义词后按回车
键将其添加到列表中。标准化值 同义词 分钟 分钟,分钟们 秒 秒,秒们
任务 - 向语言理解模型添加意图
-
在意图选项卡中,选择创建按钮以创建一个新意图。将此意图命名为
设置计时器
。 -
在示例中,输入不同方式设置计时器的句子,包括分钟、秒以及分钟和秒的组合。示例可以包括:
设置一个1秒计时器
设置一个4分钟计时器
设置一个四分钟六秒计时器
设置一个9分30秒计时器
设置一个计时器,时间为1分12秒
设置一个计时器,时间为3分钟
设置一个计时器,时间为3分1秒
设置一个计时器,时间为三分一秒
设置一个计时器,时间为1分1秒
设置一个计时器,时间为30秒
设置一个计时器,时间为1秒
混合使用数字的文字形式和数字形式,以便模型学习处理两者。
-
每输入一个示例,LUIS会开始检测实体,并会下划线并标记它找到的任何实体。
任务 - 训练和测试模型
-
配置好实体和意图后,可以使用顶部菜单中的训练按钮训练模型。选择此按钮,模型应在几秒钟内完成训练。训练期间按钮会变灰,完成后会重新启用。
-
从顶部菜单中选择测试按钮以测试语言理解模型。输入文本,例如
设置一个计时器,时间为5分4秒
,然后按回车键。句子会出现在你输入的文本框下方的一个框中,下面会显示最高意图,即检测到的概率最高的意图。此意图应为设置计时器
。意图名称后会显示检测到正确意图的概率。 -
选择检查选项以查看结果的详细信息。你会看到最高得分意图及其百分比概率,以及检测到的实体列表。
-
完成测试后关闭测试窗格。
任务 - 发布模型
要从代码中使用此模型,你需要发布它。从LUIS发布时,可以发布到测试环境或生产环境。在本课中,测试环境即可。
-
在LUIS门户中,选择顶部菜单中的发布按钮。
-
确保选择了测试槽,然后选择完成。发布应用后会看到通知。
-
你可以使用curl进行测试。要构建curl命令,你需要三个值——端点、应用ID(App ID)和API密钥。这些值可以从顶部菜单中选择的管理选项卡中访问。
- 从设置部分复制应用ID
-
在 Azure Resources 部分中,选择 Authoring Resource,然后复制 Primary Key 和 Endpoint URL。
-
在命令提示符或终端中运行以下 curl 命令:
curl "<endpoint url>/luis/prediction/v3.0/apps/<app id>/slots/staging/predict" \ --request GET \ --get \ --data "subscription-key=<primary key>" \ --data "verbose=false" \ --data "show-all-intents=true" \ --data-urlencode "query=<sentence>"
将
<endpoint url>
替换为 Azure Resources 部分中的 Endpoint URL。将
<app id>
替换为 Settings 部分中的 App ID。将
<primary key>
替换为 Azure Resources 部分中的 Primary Key。将
<sentence>
替换为您想要测试的句子。 -
此调用的输出将是一个 JSON 文档,其中详细说明了查询、最高意图以及按类型分类的实体列表。
{ "query": "set a timer for 45 minutes and 12 seconds", "prediction": { "topIntent": "set timer", "intents": { "set timer": { "score": 0.97031575 }, "None": { "score": 0.02205793 } }, "entities": { "number": [ 45, 12 ], "time-unit": [ [ "minute" ], [ "second" ] ] } } }
上述 JSON 是通过查询
set a timer for 45 minutes and 12 seconds
得到的:set timer
是最高意图,概率为 97%。- 检测到两个 number 实体,分别是
45
和12
。 - 检测到两个 time-unit 实体,分别是
minute
和second
。
使用语言理解模型
一旦发布,LUIS 模型可以通过代码调用。在之前的课程中,您使用了 IoT Hub 来处理与云服务的通信,发送遥测数据并监听命令。这种方式是非常异步的——一旦发送了遥测数据,代码不会等待响应,如果云服务宕机,您也不会知道。
对于智能计时器,我们希望立即获得响应,以便告诉用户计时器已设置,或者提醒他们云服务不可用。为此,我们的 IoT 设备将直接调用一个 Web 端点,而不是依赖 IoT Hub。
与其从 IoT 设备直接调用 LUIS,您可以使用无服务器代码并采用不同类型的触发器——HTTP 触发器。这允许您的函数应用监听 REST 请求并对其作出响应。这个函数将成为您的设备可以调用的 REST 端点。
💁 尽管您可以直接从 IoT 设备调用 LUIS,但使用类似无服务器代码的方式更好。这样,当您想更换调用的 LUIS 应用(例如训练了更好的模型或不同语言的模型)时,您只需更新云端代码,而无需重新部署到可能成千上万甚至数百万的 IoT 设备上。
任务 - 创建无服务器函数应用
-
创建一个名为
smart-timer-trigger
的 Azure Functions 应用,并在 VS Code 中打开它。 -
在此应用中添加一个名为
speech-trigger
的 HTTP 触发器,使用以下命令从 VS Code 终端中运行:func new --name text-to-timer --template "HTTP trigger"
这将创建一个名为
text-to-timer
的 HTTP 触发器。 -
通过运行函数应用来测试 HTTP 触发器。当它运行时,您将在输出中看到列出的端点:
Functions: text-to-timer: [GET,POST] http://localhost:7071/api/text-to-timer
通过在浏览器中加载 http://localhost:7071/api/text-to-timer URL 进行测试。
This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.
任务 - 使用语言理解模型
-
LUIS 的 SDK 可通过 Pip 包获取。在
requirements.txt
文件中添加以下行以添加对该包的依赖:azure-cognitiveservices-language-luis
-
确保 VS Code 终端已激活虚拟环境,并运行以下命令安装 Pip 包:
pip install -r requirements.txt
💁 如果遇到错误,您可能需要使用以下命令升级 pip:
pip install --upgrade pip
-
在
local.settings.json
文件中为您的 LUIS API Key、Endpoint URL 和 App ID 添加新条目,这些信息可以从 LUIS 门户的 MANAGE 选项卡中获取:"LUIS_KEY": "<primary key>", "LUIS_ENDPOINT_URL": "<endpoint url>", "LUIS_APP_ID": "<app id>"
将
<endpoint url>
替换为 MANAGE 选项卡中 Azure Resources 部分的 Endpoint URL。格式为https://<location>.api.cognitive.microsoft.com/
。将
<app id>
替换为 MANAGE 选项卡中 Settings 部分的 App ID。将
<primary key>
替换为 MANAGE 选项卡中 Azure Resources 部分的 Primary Key。 -
在
__init__.py
文件中添加以下导入:import json import os from azure.cognitiveservices.language.luis.runtime import LUISRuntimeClient from msrest.authentication import CognitiveServicesCredentials
这将导入一些系统库以及与 LUIS 交互的库。
-
删除
main
方法的内容,并添加以下代码:luis_key = os.environ['LUIS_KEY'] endpoint_url = os.environ['LUIS_ENDPOINT_URL'] app_id = os.environ['LUIS_APP_ID'] credentials = CognitiveServicesCredentials(luis_key) client = LUISRuntimeClient(endpoint=endpoint_url, credentials=credentials)
这将加载您在
local.settings.json
文件中为 LUIS 应用添加的值,创建一个带有 API Key 的凭据对象,然后创建一个 LUIS 客户端对象以与您的 LUIS 应用交互。 -
这个 HTTP 触发器将通过 JSON 格式传递要理解的文本,文本位于名为
text
的属性中。以下代码从 HTTP 请求的正文中提取该值,并将其记录到控制台中。将此代码添加到main
函数中:req_body = req.get_json() text = req_body['text'] logging.info(f'Request - {text}')
-
通过发送预测请求(包含要预测的文本的 JSON 文档)向 LUIS 请求预测。使用以下代码创建此请求:
prediction_request = { 'query' : text }
-
然后可以将此请求发送到 LUIS,使用您的应用发布到的 staging 插槽:
prediction_response = client.prediction.get_slot_prediction(app_id, 'Staging', prediction_request)
-
预测响应包含最高意图(预测分数最高的意图)以及实体。如果最高意图是
set timer
,则可以读取实体以获取计时器所需的时间:if prediction_response.prediction.top_intent == 'set timer': numbers = prediction_response.prediction.entities['number'] time_units = prediction_response.prediction.entities['time unit'] total_seconds = 0
number
实体将是一个数字数组。例如,如果您说 "Set a four minute 17 second timer.",那么number
数组将包含两个整数 - 4 和 17。time unit
实体将是一个字符串数组的数组,每个时间单位作为数组中的一个字符串。例如,如果您说 "Set a four minute 17 second timer.",那么time unit
数组将包含两个数组,每个数组中有一个值 -['minute']
和['second']
。"Set a four minute 17 second timer." 的这些实体的 JSON 版本为:
{ "number": [4, 17], "time unit": [ ["minute"], ["second"] ] }
此代码还定义了一个计时器总时间(以秒为单位)的计数器。该计数器将由实体中的值填充。
-
实体之间没有直接关联,但我们可以对它们做一些假设。它们将按照语音中的顺序排列,因此可以使用数组中的位置来确定哪个数字对应哪个时间单位。例如:
- "Set a 30 second timer" - 这将有一个数字
30
和一个时间单位second
,因此单个数字将匹配单个时间单位。 - "Set a 2 minute and 30 second timer" - 这将有两个数字
2
和30
,以及两个时间单位minute
和second
,因此第一个数字对应第一个时间单位(2 分钟),第二个数字对应第二个时间单位(30 秒)。
以下代码获取数字实体中的项目数量,并使用该数量从每个数组中提取第一个项目,然后是第二个,依此类推。将此代码添加到
if
块中。for i in range(0, len(numbers)): number = numbers[i] time_unit = time_units[i][0]
对于 "Set a four minute 17 second timer.",这将循环两次,给出以下值:
循环计数 number
time_unit
0 4 minute 1 17 second - "Set a 30 second timer" - 这将有一个数字
-
在此循环中,使用数字和时间单位计算计时器的总时间,为每分钟添加 60 秒,为每秒添加相应的秒数。
if time_unit == 'minute': total_seconds += number * 60 else: total_seconds += number
-
在遍历实体的循环之外,记录计时器的总时间:
logging.info(f'Timer required for {total_seconds} seconds')
-
需要将秒数作为 HTTP 响应从函数返回。在
if
块的末尾添加以下内容:payload = { 'seconds': total_seconds } return func.HttpResponse(json.dumps(payload), status_code=200)
此代码创建一个包含计时器总秒数的有效负载,将其转换为 JSON 字符串,并以状态码 200(表示调用成功)作为 HTTP 结果返回。
-
最后,在
if
块之外,处理未识别意图的情况,返回错误代码:return func.HttpResponse(status_code=404)
404 是 未找到 的状态码。
-
运行函数应用并使用 curl 进行测试。
curl --request POST 'http://localhost:7071/api/text-to-timer' \ --header 'Content-Type: application/json' \ --include \ --data '{"text":"<text>"}'
将
<text>
替换为您的请求文本,例如set a 2 minutes 27 second timer
。您将看到函数应用的以下输出:
Functions: text-to-timer: [GET,POST] http://localhost:7071/api/text-to-timer For detailed output, run func with --verbose flag. [2021-06-26T19:45:14.502Z] Worker process started and initialized. [2021-06-26T19:45:19.338Z] Host lock lease acquired by instance ID '000000000000000000000000951CAE4E'. [2021-06-26T19:45:52.059Z] Executing 'Functions.text-to-timer' (Reason='This function was programmatically called via the host APIs.', Id=f68bfb90-30e4-47a5-99da-126b66218e81) [2021-06-26T19:45:53.577Z] Timer required for 147 seconds [2021-06-26T19:45:53.746Z] Executed 'Functions.text-to-timer' (Succeeded, Id=f68bfb90-30e4-47a5-99da-126b66218e81, Duration=1750ms)
curl 调用将返回以下内容:
HTTP/1.1 200 OK Date: Tue, 29 Jun 2021 01:14:11 GMT Content-Type: text/plain; charset=utf-8 Server: Kestrel Transfer-Encoding: chunked {"seconds": 147}
计时器的秒数在
"seconds"
值中。
💁 您可以在 code/functions 文件夹中找到此代码。
任务 - 使您的函数可供 IoT 设备使用
-
为了让您的 IoT 设备调用 REST 端点,它需要知道 URL。当您之前访问它时,您使用了
localhost
,这是一个访问本地机器上 REST 端点的快捷方式。为了让 IoT 设备能够访问,您需要将其发布到云端,或者获取您的 IP 地址以便本地访问。⚠️ 如果您使用的是 Wio Terminal,建议本地运行函数应用,因为会有一些依赖库,导致无法像之前那样部署函数应用。如果您确实想部署到云端,后续课程将提供相关信息。
-
发布 Functions 应用 - 按照之前课程中的说明将函数应用发布到云端。发布后,URL 将为
https://<APP_NAME>.azurewebsites.net/api/text-to-timer
,其中<APP_NAME>
是您的函数应用名称。确保同时发布本地设置。使用 HTTP 触发器时,它们默认通过函数应用密钥进行保护。要获取此密钥,请运行以下命令:
az functionapp keys list --resource-group smart-timer \ --name <APP_NAME>
从
functionKeys
部分复制default
条目的值。{ "functionKeys": { "default": "sQO1LQaeK9N1qYD6SXeb/TctCmwQEkToLJU6Dw8TthNeUH8VA45hlA==" }, "masterKey": "RSKOAIlyvvQEQt9dfpabJT018scaLpQu9p1poHIMCxx5LYrIQZyQ/g==", "systemKeys": {} }
此密钥需要作为查询参数添加到 URL 中,因此最终的 URL 将为
https://<APP_NAME>.azurewebsites.net/api/text-to-timer?code=<FUNCTION_KEY>
,其中<APP_NAME>
是您的函数应用名称,<FUNCTION_KEY>
是您的默认函数密钥。💁 您可以通过修改
function.json
文件中的authlevel
设置更改 HTTP 触发器的授权类型。您可以在 Microsoft 文档中 Azure Functions HTTP 触发器文档的配置部分 中阅读更多内容。 -
本地运行函数应用并通过 IP 地址访问 - 您可以获取计算机在本地网络中的 IP 地址,并使用该地址构建 URL。
查找您的 IP 地址:
- 在 Windows 10 上,按照 查找 IP 地址指南。
- 在 macOS 上,按照 如何在 Mac 上查找 IP 地址指南。
- 在 Linux 上,按照 如何在 Linux 中查找 IP 地址指南 中查找私有 IP 地址的部分。
一旦您获取了 IP 地址,您将能够通过
http://
<IP地址> :7071/api/text-to-timer,其中
<IP_ADDRESS>是你的IP地址,例如
http://192.168.1.10:7071/api/text-to-timer`。💁 请注意,这里使用的是端口7071,因此在IP地址后需要加上
:7071
。💁 这仅在你的IoT设备与电脑处于同一网络时有效。
-
-
使用curl测试该端点。
🚀 挑战
设置一个计时器有很多种请求方式。想一想不同的表达方式,并将它们作为示例添加到你的LUIS应用中。测试这些表达方式,看看你的模型能否处理多种请求计时器的方式。
课后测验
复习与自学
- 在 Microsoft文档上的语言理解(LUIS)页面 阅读更多关于LUIS及其功能的信息
- 在 维基百科的自然语言理解页面 阅读更多关于语言理解的信息
- 在 Microsoft文档上的Azure Functions HTTP触发器页面 阅读更多关于HTTP触发器的信息
作业
免责声明:
本文档使用AI翻译服务 Co-op Translator 进行翻译。尽管我们努力确保翻译的准确性,但请注意,自动翻译可能包含错误或不准确之处。应以原文档的原始语言版本为权威来源。对于关键信息,建议使用专业人工翻译。我们对因使用此翻译而引起的任何误解或误读不承担责任。