在前一章中,我们花了时间将聊天机器人的语音合成标记语言(SSML)输出连接到基于云的文本到语音引擎,以使聊天机器人尽可能像人一样说话。我们使用的 Bing 语音 API 就是一个被统称为认知服务的例子。这些典型的服务支持与应用进行更自然的、类似人类的交互。最初,微软称之为牛津项目。 1 如今,这套 API 如今被打上了 Azure 认知服务的烙印。
在更高的技术层面上,这些服务允许轻松访问执行认知类型任务的机器学习(ML)算法,例如,语音识别、语音合成、拼写检查、自动纠正、推荐引擎、决策引擎和视觉对象识别。LUIS 是 Azure 认知服务的另一个例子,我们在第 3 章 3 中对此进行了深入探讨。微软显然不是这个领域的唯一玩家。IBM 在其 Watson 旗下有许多类似的服务。Google 的云平台在 Google stack 上包含了类似的服务。
这种 ML 即服务的方法对于许多任务来说非常方便。虽然从延迟和成本角度来看,它可能不适合所有工作负载,但对于许多工作负载来说,它只适用于原型开发、试点和生产部署。在这一章中,我们将探索一些微软的 Azure 认知服务。这并不意味着对该主题的详尽论述,而是对聊天机器人开发者可能感兴趣的服务类型的介绍。
无论哪种情况,都值得探索这些服务,以了解提供了什么,了解哪些类型的技术可以应用于我们的业务问题应用,最重要的是,使我们的聊天机器人具有一些相关的智能。
在我们开始之前,请注意所有的认知服务都可以通过使用位于 https://portal.azure.com 的 Azure 门户来提供。将所需的服务资源添加到资源组将允许我们获得访问密钥。例如,当我们试图将一个“bing 拼写检查”资源添加到“图书测试”资源组中时,我们可以选择 Bing 拼写检查 v7 API(图 10-1 )。
图 10-1
在 Azure 中添加 Bing 拼写检查 v7 API
在我们给服务命名并选择定价层之后(图 10-2 ,我们可以看到访问键。通常有两个访问键可供我们使用(图 10-3 )。拥有两把钥匙便于钥匙旋转。
图 10-3
查找 Bing 拼写检查 v7 API 资源的访问键
图 10-2
创建 Bing 拼写检查 v7 API 资源
对于其余的服务,这个过程类似地工作;入门不需要高级的门户知识。
当这些服务第一次在公开预览中开发时,大多数都是免费提供的。随着服务从预览阶段进入全面普及阶段,分层定价模式也随之建立起来。幸运的是,大多数服务仍然有一个允许大量使用的免费层。例如,LUIS 允许我们每月免费呼叫端点 10,000 次。我们可以使用 Translator Text API 每月免费翻译 200 万个字符。您可以在 https://azure.microsoft.com/en-us/pricing/details/cognitive-services/ 找到所有服务的更多定价详情。
任何处理用户生成的文本输入的应用都有一个特性就是拼写检查。我们希望有一个灵活的引擎,可以处理常见的拼写问题,如处理俚语,处理上下文中的专有名称错误,找出单词中断,并找出同音词的错误。此外,引擎应该不断更新新的实体,如品牌和流行文化表达。这是一个不小的壮举,然而微软提供了拼写检查 API 来做这件事。
微软的 Bing 拼写检查 API 提供了两种拼写检查模式:校对和拼写。 Proof 设计用于文档拼写检查,包括大写和标点符号建议,以帮助文档创作(这种类型的拼写检查可以在 Microsoft Word 中找到)。拼写是为纠正网络搜索中的拼写而设计的。微软声称拼写模式更具侵略性,因为它旨在优化搜索结果。 2 聊天机器人的上下文更接近于网页搜索,而不是起草长文档,因此拼写可能是更好的选择。
我们将从基础开始,传递模式、我们想要进行拼写检查的文化(称为市场)以及文本本身。我们还可以选择在输入文本的前后添加上下文。在许多情况下,上下文对于拼写检查器来说可能是重要的和相关的。您可以在 API 参考文档中找到更多详细信息。T33
为了演示 API 的用法,我们将创建一个基本的聊天机器人,它简单地将用户输入传递给拼写检查器,并通过修改用户输入做出响应,给出分数高于 0.5 的改进建议。机器人将首先提示用户选择拼写检查模式。此时,任何输入都将被发送到使用所选模式的拼写检查 API。最后,我们可以随时发送消息“退出”以返回主菜单并再次选择模式。这是基本的,但是它将说明与 API 的交互。你可以在本书的 GitHub repo 的chapter10-spell-check-bot文件夹下找到这个机器人的代码。
我们首先在 Azure 中创建 Bing 拼写检查 v7 API 资源,这样我们就可以获得一个密钥。虽然我们可以编写自己的客户端库来使用该服务,但我们将使用一个名为cognitive-services4的 Node.js 包,其中包含了大多数微软认知服务的客户端实现。
npm install cognitive-services --save
const cognitiveServices = require('cognitive-services');我们像往常一样建立了我们的宇宙机器人。我们将拼写检查 API 密钥添加到我们的.env文件中,并将字段称为 SC_KEY 。
const welcomeMsg = 'Say \'proof\' or \'spell\' to select spell check mode';
const bot = new builder.UniversalBot(connector, [
(session, arg, next) => {
if (session.message.text === 'proof') {
session.beginDialog('spell-check-dialog', { mode: 'proof' });
} else if (session.message.text === 'spell') {
session.beginDialog('spell-check-dialog', { mode: 'spell' });
} else {
session.send(welcomeMsg);
}
},
session => {
session.send(welcomeMsg);
}
]);
const inMemoryStorage = new builder.MemoryBotStorage();
bot.set('storage', inMemoryStorage);接下来,我们创建一个名为拼写检查对话框的对话框。在这段代码中,每当用户发送新消息时,我们都会向拼写检查 API 发送一个请求。当我们收到结果时,我们用分数大于或等于 0.5 的建议修正替换标记为有问题的片段。为什么是 0.5?这有点武断,建议修改分数阈值和输入选项,以找到最适合您的应用的值。
bot.dialog('spell-check-dialog', [
(session, arg) => {
session.dialogData.mode = arg.mode;
builder.Prompts.text(session, 'Enter your input text. Say \'exit\' to reconfigure mode.');
},
(session, arg) => {
session.sendTyping();
const text = arg.response;
if (text === 'exit') {
session.endDialog('ok, done.');
return;
}
spellCheck(text, session.dialogData.mode).then(resultText => {
session.send(resultText);
session.replaceDialog('spell-check-dialog', { mode: session.dialogData.mode });
});
}
]);我们定义了拼写检查函数来调用 Bing 拼写检查 API,并用建议的更正替换拼写错误的单词。
function spellCheck(text, mode) {
const parameters = {
mkt: 'en-US',
mode: mode,
text: text
};
const spellCheckClient = new cognitiveServices.bingSpellCheckV7({
apiKey: process.env.SC_KEY
})
return spellCheckClient.spellCheck({
parameters
}).then(response => {
console.log(response); // we do this so we can easily inspect the resulting object
const resultText = applySpellCheck(text, response.flaggedTokens);
return resultText;
});
}
function applySpellCheck(originalText, possibleProblems) {
let tempText = originalText;
let diff = 0;
for (let i = 0; i < possibleProblems.length; i++) {
const problemToken = possibleProblems[i];
const offset = problemToken.offset;
const originalTokenLength = problemToken.token.length;
const suggestionObj = problemToken.suggestions[0];
if (suggestionObj.score < .5) {
continue;
}
const suggestion = suggestionObj.suggestion;
const lengthDiff = suggestion.length - originalTokenLength;
tempText = tempText.substring(0, offset + diff) + suggestion + tempText.substring(offset + diff + originalTokenLength);
diff += lengthDiff;
}
return tempText;
}图 10-4 显示了结果对话。
图 10-4
运行中的拼写检查机器人
效果很好!另一种方法是总是在输入到达对话框堆栈之前通过拼写检查器运行输入。我们可以通过在 bot 中安装定制的中间件来做到这一点。中间件背后的想法是能够将逻辑添加到 Bot Builder 用来处理每个传入和传出消息的管道中。中间件对象的结构如下。方法 bot.use 将中间件对象添加到 Bot Builder 的管道中。
bot.use({
receive: function (event, next) {
logicOnIncoming(event);
next();
},
send: function (event, next) {
logicOnOutgoing(event);
next();
}
});我们可以使用之前定义的代码创建以下中间件。我们对输入进行拼写检查,用自动更正的文本覆盖输入。我们不定义任何传出消息的逻辑。
bot.use({
receive: function (event, next) {
if (event.type === 'message') {
spellCheck(event.text, 'spell').then(resultText => {
event.text = resultText;
next();
});
}
},
send: function (event, next) {
next();
}
});就这样!现在我们的对话可以简单得多了!
bot.dialog('middleware-dialog', [
(session, arg) => {
let text = session.message.text;
session.send(text);
}
]);最终的对话看起来如图 10-5 所示。
图 10-5
使用中间件方法进行拼写检查
在第 3 章中,我们探讨了语言理解智能服务(LUIS)提供的拼写检查选项。如前所述,LUIS 是微软的另一个认知服务;这是一个 NLU 系统,它允许我们对意图进行分类并提取命名实体。它可以完成的任务之一是集成 Bing 拼写检查 API,并通过 NLU 模型运行拼写检查查询(相对于原始输入)。这种方法的好处是,我们的 LUIS 应用不需要用拼写错误的单词进行训练。缺点是我们的场景可能包含特定领域的语言,拼写检查器无法识别,但 LUIS 模型可以识别。
我们不推荐使用中间件来完全改变用户的输入,让机器人永远看不到原始输入。至少,我们应该记录原始输入和原始输出。如果对 LUIS 启用拼写检查本身会在我们的模型中产生有问题的行为,我们可以将一些逻辑移到我们的 bot 中。一种选择是将 LUIS 识别器包装在自定义拼写检查 LUIS 识别器周围。在这个自定义识别器中,您将有逻辑来确保拼写检查器永远不会修改某个词汇子集。实际上,我们将执行部分拼写检查。
在第一章中,我们演示了一个机器人,它可以响应它从用户输入中检测到的情绪(图 10-6 )。使用“好”和“坏”单词的查找可以简单地实现基本的情感分析。
图 10-6
一个能对情绪做出反应的机器人
显然,这种方法有局限性,比如不考虑单词的上下文。如果我们要开发自己的查找,我们需要确保列表随着文化规范的变化而不断更新。更高级的方法使用机器学习分类技术来创建情感函数,以对话语的情感进行评分。微软提供了一种基于大量预先标注了情感的文本的 ML 算法。
微软的情感分析是其文本分析 API 的一部分。该服务提供三个主要功能:情感分析、关键短语提取和语言检测。我们将首先关注情感分析。
API 允许我们发送一个或多个文本字符串,并接收一个或多个介于 0 和 1 之间的数字分数的响应,其中 0 表示负面情绪,1 表示正面情绪。这里有一个例子(你可以用这个例子告诉我儿子太早叫醒我了):
{
"documents": [
{
"id": "1",
"language": "en",
"text": "i hate early mornings"
}
]
}结果如下:
{
"documents": [
{
"score": 0.073260486125946045,
"id": "1"
}
],
"errors": []
}情感分析在聊天机器人领域有一些有趣的应用。我们可以利用分析报告中的事后数据来了解哪些功能对用户的挑战最大。或者,我们可以利用实时情绪得分将对话自动转移到人工代理,以立即解决用户的问题或挫折。
在聊天机器人中支持多种语言本身就是一个复杂的话题,我们无法在本书的范围内完全涵盖。然而,我们演示了如何通过使用文本分析和翻译器 API 来更新我们在整本书中一直致力于的日历机器人,以支持多种语言。代码可以在该书的 GitHub repo 的chapter10-calendar-bot文件夹下找到。我们将按如下方式完成这项任务:
-
每当用户向机器人发送消息时,我们的聊天机器人将使用文本分析 API 来识别用户的语言。
-
如果语言是英语,请照常继续。如果不是,把这句话翻译成英语。
-
请把这个英语短语读给路易斯听。
-
在退出时,如果用户的语言是英语,继续正常操作。否则,在发送给用户之前,将机器人的响应翻译成用户的语言。
实质上,我们使用英语作为中介语言来为 LUIS 提供支持。这种方法不是万无一失的。LUIS 支持多种文化是有原因的,比如语言中的许多细微差别和文化差异。没有额外上下文的直接直译可能没有意义。事实上,我们可能希望支持用一种语言和用英语表达完全不同的方式。解决问题的正确方法是为我们希望提供一流支持的每种文化开发详细的 LUIS 应用,使用基于语言检测的那些应用,并且仅在我们没有 LUIS 对语言的支持时使用翻译器 API 和中介英语。或者,我们甚至完全避免使用翻译 API,因为翻译可能会有问题。
虽然我们在下面的例子中没有使用这种方法,但是由于我们可以控制机器人的文本输出,我们可以提供那些跨我们想要支持的所有语言本地化的静态字符串(而不是使用翻译服务)。我们可以依靠自动翻译来翻译任何没有明确写下来的东西。
从技术角度来看,我们必须选择何时进行翻译。比如是识别器的作用还是对话框的作用?还是要加中间件把输入翻译成英文?对于这个例子,我们将利用中间件方法,因为我们在传入和传出内容上都利用了翻译服务,并希望它对机器人的其余部分尽可能透明。如果我们有一组特定于文化的 LUIS 应用和本地化的输出字符串,我们可以混合使用识别器和对话逻辑。
- 在我们开始之前,确保您已经在 Azure 门户中创建了文本分析 API 和翻译文本 API 资源,就像我们创建 Bing 拼写检查 v7 API 资源一样。这两个 API 都有一个免费的定价层,所以一定要选择它。请注意,文本分析 API 要求我们选择一个区域。所有与 Bing 无关的认知服务都要求这样设置。这显然对可用性和延迟有影响,超出了本书的范围。创建之后,我们必须将密钥保存到
.env文件中。将文本分析键命名为 TA_KEY ,将翻译键命名为 TRANSLATOR_KEY 。此外,认知服务包要求指定端点。端点映射到地区,因此如果我们选择 West US 作为文本分析服务地区,端点值就是 westus.api.cognitive.microsoft.com。 5 将此设置为.env文件中的 TA_ 端点键。
我们将使用cognitive-servicesnode . js 包与文本分析 API 进行交互;然而,翻译器 API 是这个包不支持的服务之一。我们可以安装 mstranslator Node.js 包。
npm install mstranslator --save
const translator = require('mstranslator');接下来,我们可以创建一个包含翻译逻辑的中间件模块,这样我们就可以轻松地将此功能应用于任何机器人。
const TranslatorMiddleware = require('./translatorMiddleware').TranslatorMiddleware;
bot.use(new TranslatorMiddleware());中间件代码本身将依赖于使用文本分析和翻译 API。
const textAnalytics = new cognitiveServices.textAnalytics({
apiKey: process.env.TA_KEY,
endpoint: process.env.TA_ENDPOINT
});
const translatorApi = new translator({ api_key: process.env.TRANSLATOR_KEY }, true); // the second parameter ensures that the token is autogenerated之后,我们创建一个 TranslatorMiddleware 类,它带有一个映射,告诉我们哪些用户使用哪种语言。这需要存储用户的输入语言,以便输出逻辑能够从英语翻译回英语。
const userLanguageMap = {};
class TranslatorMiddleware {
...
}接收逻辑跳过任何不是消息的内容。如果我们有一个消息,用户的语言被检测。如果语言是英语,我们继续;否则,我们将消息翻译成英语,将消息文本重置为英语版本(从而丢失原始语言输入),然后继续。如果在我们翻译收到的信息时出现错误,我们只需假定是英语。
receive(event, next) {
if (event.type !== 'message') { next(); return; }
if (event.text == null || event.text.length == 0) {
// if there is not input and we already have a language, leave as is, otherwise set to English
userLanguageMap[event.user.id] = userLanguageMap[event.user.id] || 'en';
next();
return;
}
textAnalytics.detectLanguage({
body: {
documents: [
{
id: "1",
text: event.text
}
]
}
}).then(result => {
const languageOptions = _.find(result.documents, p => p.id === "1").detectedLanguages;
let lang = 'en';
if (languageOptions && languageOptions.length > 0) {
lang = languageOptions[0].iso6391Name;
}
this.userLanguageMap[event.user.id] = lang;
if (lang === 'en') next();
else {
translatorApi.translate({
text: event.text,
from: languageOptions[0].iso6391Name,
to: 'en'
}, function (err, result) {
if (err) {
console.error(err);
lang = 'en';
userLanguageMap[event.user.id] = lang;
next();
}
else {
event.text = result;
next();
}
});
}
});
}在出去的路上,我们只需弄清楚用户的语言,然后将发出的消息翻译成那种语言。如果用户的语言是英语,我们跳过翻译步骤。
send(event, next) {
if (event.type === 'message') {
const userLang = this.userLanguageMap[event.address.user.id] || 'en';
if (userLang === 'en') { next(); }
else {
translatorApi.translate({
text: event.text,
from: 'en',
to: userLang
}, (err, result) => {
if (err) {
console.error(err);
next();
}
else {
event.text = result;
next();
}
});
}
}
else {
next();
}
}图 10-7 显示了不同语言对问候语的回应。
图 10-7
不同语言的机器人响应
恭喜,我们现在有了一个天真的多语言聊天机器人!基本的请求和响应看起来不错,但是在收集数据方面存在一些问题。例如,机器人似乎中途切换语言(图 10-8 )。
图 10-8
用西班牙语创建约会流,带有一个标志
问题是, café 这个词在英语和西班牙语中都有效。这可能需要在对话中进行某种语言锁定。“什么时候开会?”听起来也不对。cual这个词翻译过来就是的*,而不是时的。我们可以通过提供静态本地化输出字符串来解决这个问题。*
实现一个生产级多语言机器人还有更多的事情要做,但这是一个很好的概念证明,展示了我们如何使用 Azure 认知服务来检测和翻译语言。
机器人的一个常见用例是为用户提供一个 FAQ,以获取关于某个主题、品牌或产品的信息。通常,这类似于 web FAQ,但更适合于对话式交互。一种典型的方法是创建一个问答对数据库,并提供某种模糊匹配算法来搜索给定用户输入的数据集。
一种实现方法是将所有问答数据加载到 Lucene 之类的搜索引擎中,并使用其模糊搜索算法来搜索正确的配对。在微软 Azure 中,相当于将数据加载到诸如 Cosmos DB 之类的存储库中,并使用 Azure Search 来创建数据的搜索索引。
出于我们的目的,我们将使用一个更简单的选项,称为 QnA Maker,这是我们可以使用的另一种认知服务。QnA Maker ( https://qnamaker.ai/ )于 2018 年 5 月正式上市。该系统很简单:我们将一组问答对输入知识库,训练该系统并将其作为 API 发布。然后,模糊逻辑匹配通过我们在 Azure 应用服务计划中托管的 API 变得可用,因此我们可以根据需要调整其性能。
我们必须首先登录 Azure 门户并创建一个新的 QnA Maker 实例(图 10-9 )。UI 将从我们这里收集一些数据。我们输入一个名称,管理服务定价层(免费定价!)、资源组、搜索服务定价层(还是免费的!),搜索服务位置,服务位置,以及我们是否要包含应用洞察。如果您启用或禁用 Application Insights,该服务也能正常工作。保持启用状态,您可以查看用户向 QnA Maker 提问的日志。
图 10-9
创建新的 QnA 制造商服务
在 Azure 门户完成它的工作后,我们最终得到了几个资源。搜索服务托管搜索索引,应用服务托管我们将调用的 API,应用洞察提供关于我们的服务使用的分析。请确保将应用服务计划定价层更改为免费!
此时,我们可以进入 QnA Maker 门户网站。使用您在 Azure 上使用的同一帐户登录 https://www.qnamaker.ai 。点击创建一个知识 库。您将看到图 10-10 中的屏幕。从您的 Azure 订阅中选择 QnA 服务,并命名您的知识库。有几个选项来填充内容:您可以提供一个带有常见问题的 URL,上传一个包含数据的 TSV 文件、一个 PDF 文件,或者手动输入数据。这些都是非常有趣的选择,我们建议你自己去探索。
图 10-10
创建新的 QnA 知识库
出于我们的目的,我们将使用手动界面。输入服务名后,点击创建新的知识库。我们遇到了一个丰富的界面,允许我们编辑知识库中的内容,并保存、重新培训或发布它(图 10-11 )。我们使用右上角的 +添加新的 QnA 对链接添加几对。
图 10-11
向我们的知识库添加更多 QnA 对
我们现在可以点击保存和训练,然后点击发布。点击发布按钮会将知识库移动到 Azure 门户中创建的 Azure 搜索实例中。一旦发布,我们将会看到如何调用 API 的细节(见图 10-12 )。请注意,URL 对应于我们在 Azure 门户中创建的应用服务。
图 10-12
我们发布了一个 QnA Maker KB!
让我们使用 curl 来看看 API 的运行情况。我们将尝试一些我们没有明确训练过的东西,比如“你叫什么名字?”请注意,我们可以包含 top 参数,以向 QnA Maker 表明我们愿意处理多少结果。如果 QnA Maker 找到多个分数足够接近的可能候选答案,它将返回 top options 的值。
curl -X POST
-H "Authorization: EndpointKey f3c15268-40c1-4e66-8790-392c29f2f704"
-H "Content-Type: application/json" "https://booktestqna.azurewebsites.net/qnamaker/knowledgebases/ce45743a-62e5-42b1-a572-f912ea6836f9/generateAnswer"
-d '{ "question": "whats your name?", "top": 5 }'回应如下:
{
"answers": [
{
"questions": [
"what is your name?"
],
"answer": "Szymon",
"score": 60.98,
"id": 3,
"source": "Editorial",
"metadata": []
}
]
}反响看起来不错。如果我们问一个我们没有训练过的问题,我们会得到“在知识库中没有找到好的匹配”的响应。
curl -X POST
-H "Authorization: EndpointKey f3c15268-40c1-4e66-8790-392c29f2f704"
-H "Content-Type: application/json" "https://booktestqna.azurewebsites.net/qnamaker/knowledgebases/ce45743a-62e5-42b1-a572-f912ea6836f9/generateAnswer"
-d '{ "question": "when are you going to give me your bitcoin?", "top": 5 }'
{
"answers": [
{
"questions": [],
"answer": "No good match found in KB.",
"score": 0.0,
"id": -1,
"metadata": []
}
]
}结果正如我们所料:不匹配。用户界面还提供了一个测试功能,让我们在发布到公共 API 之前,用不同的措辞询问知识库问题,看看模型返回什么。如果算法选择了错误的答案,我们可以将它指向正确的答案。您还可以轻松地添加备选问题措辞(图 10-13 )。
图 10-13
QnA Maker 测试界面,添加新问题短语和新问题对的强大方法
微软提供了 QnA Maker 识别器和对话框,作为其bot builder-cognitive services6node . js 包的一部分。如果我们希望我们的聊天机器人同时利用 QnA Maker 和 LUIS,我们可以使用一个自定义识别器来查询这两个服务,并根据这两个服务的结果选择正确的操作过程。
与 QnA Maker 集成
本练习的目标是向现有聊天机器人添加问答功能。
-
创建一个简单的 QnA Maker 知识库,其中包含关于您的一些问题的答案。姓名、出生日期和兄弟姐妹的数量是一些可能性。
-
创建一个聊天,利用bot builder-cognitive servicesnode . js 包连接到您的 QnA Maker 服务。
-
将 QnA Maker 对话框和识别器集成到一个也连接到 LUIS 的机器人中。可以用第 7 章的日历机器人举例。框架是否擅长区分 LUIS 查询和 QnA 查询?
-
试着用类似于你训练 LUIS 模型的话语来训练 QnA Maker。机器人的行为如何?如果我们改变识别器注册的顺序,行为会改变吗?
在本练习中,您探索了如何将 QnA Maker 集成到聊天机器人中。您还探索了混合使用 QnA Maker 和 LUIS 识别器,这对于 Bot Builder 机制和可能的排序陷阱都是一个很好的练习。
到目前为止,我们探索的所有认知服务都以某种形式明显应用于聊天机器人。拼写检查、情感分析、翻译和语言检测以及模糊输入匹配都明显适用于我们日常的机器人交互。另一方面,有许多机器学习任务对机器人的适用性并不清楚。计算机视觉就是这样一个例子。
微软的 Azure 认知服务包括提供多种功能的计算机视觉系列服务。例如,有一个检测和分析人脸的服务,还有一个分析人的情绪的服务。有一种内容调节服务和一种允许您定制现有计算机视觉模型以适应我们的用例的服务(想象一下,试图让一种算法变得擅长识别不同类型的树)。还有一种更通用的服务叫做计算机视觉,它返回一组带有置信度得分的图像标签。它还可以创建图像的文本摘要,并确定图像是否色情或包含成人内容,以及其他任务。
因为我对那些唯一的任务就是确定一张照片是否是热狗的移动应用的无休止的娱乐,我们将研究一个机器人的代码,它可以判断用户发送的图像是否是热狗。代码可以在该书的 GitHub repo 的chapter10-hot-dog-or-not-hot-dog-bot文件夹下找到。
原则上,我们将使用模拟器来练习这个机器人,以确保我们可以在本地开发。当用户通过任何通道发送图像时,机器人通常会收到图像的 URL。我们可以将该 URL 发送给服务,但是由于模拟器发送的是本地主机地址,所以这是行不通的。我们的代码需要做的是将所述图像下载到临时目录,然后将其上传到计算机视觉 API。我们将使用这段代码和使用 request Node.js 包下载图像。
const getImage = function (uri, filename) {
return new Promise((resolve, reject) => {
request.head(uri, function (err, res, body) {
request(uri).pipe(fs.createWriteStream(filename))
.on('error', () => { reject(); })
.on('close', () => {
resolve();
});
});
});
};然后,我们创建一个简单的对话框,它接受任何输入并在服务中运行,以判断热狗是否被识别。
bot.dialog('hot-dog-or-not-hot-dog', [
(session, arg) => {
if (session.message.attachments == null || session.message.attachments.length == 0 || session.message.attachments[0].contentType.indexOf('image') < 0) {
session.send('Not supported. Require an image to be sent!');
return;
}
// let them know we're thinking....
session.sendTyping();
const id = uuid();
const dirName = 'images';
if (!fs.existsSync(dirName)) {
fs.mkdirSync(dirName);
}
const imagePath = dirName + '/' + id;
const imageUrl = session.message.attachments[0].contentUrl;
getImage(imageUrl, imagePath).then(() => {
const cv = new cognitiveServices.computerVision({ apiKey: process.env.CV_KEY, endpoint: process.env.CV_ENDPOINT });
return cv.describeImage({
headers: { 'Content-Type': 'application/octet-stream' },
body: fs.readFileSync(imagePath)
});
}).then((analysis) => {
// let's look at the raw object
console.log(JSON.stringify(analysis));
if (analysis.description.tags && ) {
if (_.find(analysis.description.tags, p => p === 'hotdog')) {
session.send('HOT DOG!');
}
else {
session.send('not hot dog');
}
}
else {
session.send('not hot dog');
}
fs.unlinkSync(imagePath);
});
}
]);如果我们上传这张漂亮的热狗图片(图 10-14 ,我们会得到下面的 JSON 结果。
图 10-14
普通的老热狗
{
"description": {
"tags": [
"sitting", "food", "paper", "hot",
"piece", "bun", "table", "orange",
"top", "dog", "laying", "hotdog",
"sandwich", "yellow", "close", "plate",
"cake", "phone"
],
"captions": [
{
"text": "a close up of a hot dog on a bun",
"confidence": 0.5577123828705269
}
]
},
"requestId": "4fa77b1a-1b27-491c-b895-8640d6a196fd",
"metadata": {
"width": 1200,
"height": 586,
"format": "Png"
}
}如果我们上传这张索诺兰热狗照片(图 10-15 ),不管那是什么,我们仍然会得到不错的结果。
图 10-15
另一种热狗?
{
"description": {
"tags": [
"food", "sandwich", "dish", "box",
"dog", "table", "hot", "sitting",
"piece", "top", "square", "toppings",
"paper", "slice", "close", "different",
"hotdog", "holding", "pizza", "plate",
"laying"
],
"captions": [
{
"text": "a close up of a hot dog",
"confidence": 0.9727350601423388
}
]
},
"requestId": "11a12305-d36a-4db0-aca0-2a1870a8b9e7",
"metadata": {
"width": 1280,
"height": 960,
"format": "Jpeg"
}
}我不知道索诺兰热狗是什么,但看过之后,听起来真的很好吃。我有点觉得好笑,服务可以正确地确定它是一个热狗。更让我觉得有趣的是,它还给图片贴上了标签披萨和不同。这将是一个有趣的练习,看看如何疯狂的热狗需要完全欺骗这个模型。
我们可以通过图像检测和分析做很多有趣的事情,尽管热狗或不热狗是一个愚蠢的例子,但应该清楚这种通用图像描述生成可以有多强大。当然,更具体的应用需求可能意味着微软或其他提供商提供的通用模型是不够的,定制模型更合适。定制视觉服务 7 涵盖了那些用例。在这两种情况下,使用易于使用的 REST API 快速构建这些函数原型的能力都是不可低估的。
探索计算机视觉
计算机视觉允许我们做一些事情,而不仅仅是获取标签。我们可以用 API 做的一个更引人注目的动作是光学字符识别(OCR)。
-
通过使用 Azure 门户获取计算机视觉 API 的访问密钥。这个过程和其他认知服务是一样的。
-
创建一个聊天机器人,接受照片并从照片中提取文本信息。像我们在热狗聊天机器人中一样处理图片上传。
-
试着在一张纸上写一些文字,然后通过你的聊天机器人运行它。它能正确识别你的笔迹吗?
-
在 OCR 努力识别文本之前,图像中的对比度可以有多差,或者您的书写可以有多差?
现在,您已经练习了计算机视觉 API,并以特别的方式测试了它的 OCR 算法的性能。
这个世界在机器学习算法的准确性方面取得了很大的进步,以至于很多功能已经通过 REST APIs 向开发人员公开。通过一个简单的 REST 端点访问其中一些算法的能力,而不需要学习新的环境和语言(如 Anaconda、Python 和 scikit-learn),刺激了一大批开发人员尝试新的想法,并在他们的应用中包含 AI 功能。大型科技公司提供的一些服务可能不像定制开发和策划的模型那样高效、经济或准确,但它们的易用性以及随着时间的推移不断提高的准确性和成本效益是生产场景中考虑的催化剂。
作为聊天机器人领域的专业人士,我们应该对可以帮助我们的聊天机器人开发的认知产品类型有所了解。使用所有这些强大的功能可以极大地改善对话体验。
Footnotes [1](#Fn1_source)关于必应拼写检查 API 的更多信息: https://azure.microsoft.com/en-us/services/cognitive-services/spell-check/
Bing 拼写检查 API V7 API 文档: https://docs.microsoft.com/en-us/rest/api/cognitiveservices/bing-spell-check-api-v7-reference
Node.js 认知服务包和认知服务 API 支持列表: https://www.npmjs.com/package/cognitive-services
我们可以在 Node.js 包代码中找到所有其他可能的端点值; https://github.com/joshbalfour/node-cognitive-services/blob/master/src/language/textAnalytics.js见。
js 包提供了访问 QnA Maker 的助手。代码可以在 GitHub 的 https://github.com/Microsoft/BotBuilder-CognitiveServices/tree/master/Node 找到。
定制视觉服务允许我们用我们的特定应用图像增强现有的计算机视觉模型: https://azure.microsoft.com/en-us/services/cognitive-services/custom-vision-service/














