30 分钟快速构建翻译器应用
- 注册 / 登录 亚马逊云科技账户
- 了解 Python 基础知识
30 分钟,似乎不足以创建一个重要的项目,但完全足以在亚马逊云科技上构建和测试一个语言应用程序。目前,有数百种翻译器应用可以帮助我们了解不同的文化、与不同的人交流、学习全球 7000 多种语言。然而,构建自己的应用会获得一手实践经验。一点一点地自行创建可以真正地学习:关键是通过实践可以获得新技能和提高能力。
在这篇文章中,您将了解如何构建一个翻译器应用。只需完成几个步骤,您就可以创建一个不仅能够识别输入语言、翻译多种语言,还能够生成发音正确的音频文件的翻译应用程序。我将逐步指导您如何结合使用多种亚马逊云科技服务,实现并测试翻译应用程序。
学习内容
- 如何使用人工智能服务检测文本中的主要语言。
- 如何使用人工智能服务翻译文本。
- 如何使用人工智能服务将文本转换为逼真的语音。
- 如何创建人工智能对话机器人来处理翻译请求。
解决方案概述
在本教程试验过程中,您将创建一个翻译聊天机器人,并使用 Amazon Lex 处理前端与用户的交互,使用适用于 Python 的 Amazon SDK (Boto3) 和 Amazon Lambda 函数处理后端业务,其中将使用以下亚马逊云科技服务:
- Amazon Comprehend 负责检测输入的语言。
- Amazon Translate 负责翻译成目标语言。
- Amazon Polly 负责提供发音正确的音频。
图 1.翻译聊天机器人应用流程图
构建步骤
- 第 1 步 - 创建检测语言并翻译成目标语言的函数。
- 第 2 步 - 创建将文本转换为语音的函数。
- 第 3 步 - 使用 Amazon Lex 配置聊天机器人界面。
- 第 4 步 - 构建后端和前端之间的接口。
- 第 5 步 - 将后端与前端集成。
- 第 6 步 - 配置并测试翻译器应用。
- 第 7 步 - 部署翻译器应用。
您可能会怀疑这么多步骤不能在 30 分钟内完成。但是,继续读下去,您会发现您可能不需要 30 分钟就能完成。
让我们开始吧!
第 1 步 - 创建检测语言并翻译成目标语言的函数
这一步骤中,您需要使用两个全托管人工智能服务:Amazon Translate 和 Amazon Comprehend。在 Amazon Translate 中,将使用 Boto3 Translate 客户端调用 TranslateText 来翻译非结构化的常见语言文本 (UTF-8) 内容,或者构建支持多种语言的应用程序;在 Amazon Comprehend 中,将使用 Boto3 Comprehend 客户端调用 DetectDominantLanguage API 检测要翻译的文本的主要语言。
也可以使用 Amazon Translate 确定文本的源语言,即通过内部调用 Amazon Comprehend 确定源语言。后面,我将介绍如何操作。
TranslateText API 的请求参数
- Text:String,要翻译的文本。
- SourceLanguageCode:String,源文本的语言代码。如果指定为 auto,Amazon Translate 将调用 Amazon Comprehend 来确定源语言 ✅。
- TargetLanguageCode:String,目标语言代码。
调用 TranslateText 并执行翻译的函数
import boto3
translate_client = boto3.client('translate')
def TranslateText (text,language):
response = translate_client.translate_text(
Text=text,
SourceLanguageCode="auto",
TargetLanguageCode=language
)
text_ready = response['TranslatedText']
return text_ready
第 2 步 - 创建将文本转换为语音的函数
要创建文本转语音函数,您将使用 Amazon Polly。Amazon Polly 是一种人工智能服务,使用先进的深度学习技术合成自然发音的人类语音。开发人员可以在应用中集成该服务,以便将文本转换为逼真的语音。
这一步中,您将使用 Boto3 Polly 客户端调用 StartSpeechSynthesisTask API 启动语音合成任务,并且调用 GetSpeechSynthesisTask API 根据 TaskID 检索语音合成任务的信息。GetSpeechSynthesisTask 接口调用将返回任务状态和存放任务输出的 Amazon S3 存储桶的地址。
图 2.文本转语音
Amazon Polly API 参数
StartSpeechSynthesisTask 请求参数 | GetSpeechSynthesisTask 请求参数 |
---|---|
|
Amazon Polly 支持多种语言和语音,使合成语音听起来像自然逼真的人声。为了生成优质的音频,必须为每种语言选择合适的语音。在 Python 中使用以下字典:
#Match the language code from Amazon Translate with the right voice from Amazon Polly.
def get_target_voice(language):
to_polly_voice = dict( [ ('en', 'Amy'), ('es', 'Conchita'), ('fr', 'Chantal'), ('pt-PT', 'Cristiano'),('it', 'Giorgio'),("sr","Carmen"),("zh","Hiujin") ] )
target_voice = to_polly_voice[language]
调用 Amazon Polly API 的函数
- StartSpeechSynthesisTask:
import boto3
polly_client = boto3.client('polly')
def start_taskID(target_voice,bucket_name,text):
response = polly_client.start_speech_synthesis_task(
VoiceId=target_voice,
OutputS3BucketName = bucket_name,
OutputFormat= "mp3",
Text= text,
Engine= "standard")
task_id = response['SynthesisTask']['TaskId']
object_name = response['SynthesisTask']['OutputUri'].split("/")[-1]
return task_id, object_name
- GetSpeechSynthesisTask:
import time
def get_speech_synthesis(task_id):
max_time = time.time() + 2
while time.time() < max_time:
response_task = polly_client.get_speech_synthesis_task(
TaskId=task_id
)
status = response_task['SynthesisTask']['TaskStatus']
print("Polly SynthesisTask: {}".format(status))
if status == "completed" or status == "failed":
if status == "failed":
TaskStatusReason = response_task['SynthesisTask']['TaskStatusReason']
print("TaskStatusReason: {}".format(TaskStatusReason))
else:
value= response_task['SynthesisTask']['OutputUri']
print("OutputUri: {}".format(value))
break
time.sleep(2)
return status
注意:此程序不会等待语音合成任务完成,因为任务的持续时间取决于文本的长度。GetSpeechSynthesisTask 只根据任务 ID 返回任务的状态。
生成音频文件的预签名 URL
s3_client = boto3.client("s3")
def create_presigned_url(bucket_name, object_name, expiration=3600):
value = object_name.split("/")[-1]
response = s3_client.generate_presigned_url('get_object',
Params={'Bucket': bucket_name,
'Key': value},
ExpiresIn=expiration)
return response
第 3 步 - 使用 Amazon Lex 配置聊天机器人界面
Amazon Lex 可用于在应用中构建基于语音和文本的对话界面。它提供了自然语言理解 (NLU) 和自动语音识别 (ASR) 的深度功能和灵活性,并简化了构建自然对话体验的过程。使用 Amazon Lex,您无需具有专业的人工智能/机器学习技术。它还可以与移动应用程序、Web 应用程序、联络中心、消息平台以及 Amazon Lambda 函数等亚马逊云科技服务集成。
Amazon Lex 包含以下组件:
组件 | 描述 | 示例值 |
---|---|---|
Language(语言) | 可以选择 Amazon Lex V2 支持的任何语言。 | English (US) |
Intent(意图) | 意图表示用户想要执行的操作 | TranslateIntent |
Slot types(槽位类型) | 允许 Amazon Lex 机器人在对话流期间动态收集用户的数据,以便完成操作和提供自定义响应。槽位类型有两种,分别是内置槽位类型和自定义槽位类型。本试验中将创建自定义槽位。 |
|
Utterances(话语) | 表明意图激活聊天的话语。应采用聊天机器人的语言 |
|
创建 Amazon Lex 机器人
执行以下步骤,在控制台上设置 Amazon Lex:
- 登录亚马逊云科技管理控制台,然后进入 Amazon Lex 控制台
- 在 Creation method(创建方法)字段,选择 Create a blank bot(创建空白机器人),并完成其他设置。具体设置方法,请参见创建 Amazon Lex V2 机器人(控制台)和向机器人添加语言。
下一步是使用组件的信息创建对话流。
上一步中,已经选择了语言,因此在 Intent details(意图详情)-> Intent Name(意图名称)中更改意图的名称,然后点击 Save Intent(保存意图)。下一步创建槽位类型(text_to_translate 和 language)。
创建槽位类型
- 在左侧菜单中,选择 Slot types(槽位类型)。然后选择 Add slot type(添加槽位类型) > Add blank slot type(添加空白槽位类型)。
- 为每种槽位类型设置以下参数:
参数 | language | text_to_translate |
---|---|---|
Slot type name(槽位类型名称) | language | text_to_translate |
Slot value resolution(槽位值解析) | 内置槽位类型 | 取决于槽位值 |
Slot type values(槽位类型值) | [0-9][a-z][A-Z] | 值:语言代码 - 新值:语言(查找语言代码和语言值) - 添加语言 |
- 结果图
配置意图
配置以下意图参数,用于识别用户的翻译请求意图:
- 示例话语:输入 Utterances 的值
- 槽位:选择 Add Slot(添加槽位),并填写以下信息:
参数 | language | text_to_translate |
---|---|---|
名称 | language | text_to_translate |
槽位类型 | language | 取决于槽位值 |
提示语 | 您要翻译什么语言? | 您要翻译什么内容?请直接输入要翻译的内容,我会自动识别语言。 |
- 结果图
重要说明:添加槽位的顺序非常重要。请确保第一个槽位类型是language。
机器人将调用 Lambda 函数作为对话代码挂钩,检验用户输入和实现用户意图。因此,勾选 Use a Lambda function for initialization and validation(使用 Lambda 函数进行初始化和检验)(图 7)。
图 7.Use a Lambda function for initialization and validation(使用 Lambda 函数进行初始化和检验)
点击 Save intent(保存意图),然后点击左上角的 Build(构建),创建聊天机器人。
注意:当您构建一个 Amazon Lex 机器人时,您使用更新的配置和逻辑重新训练机器人,使其能够从新的参数中学习。
恭喜您创建了自己的机器人!请按照此链接中的说明进行测试。
第 4 步 - 构建后端和前端之间的接口
从后端到前端的交互将根据 DialogAction 指定的特定状态进行处理。DialogAction 描述了机器人在与用户交互时必须执行的下一个操作。可选值包括:
- ConfirmIntent:下一个操作是询问用户是否已完成意图设置并准备实现。这是一个是/否问题,例如“下订单吗?”
- Close:不需要用户响应。例如,语句“您的订单已生成”不需要响应。
- Delegate:下一个操作由 Amazon Lex 决定。
- ElicitIntent:下一个操作是确定用户想要实现的意图。
- ElicitSlot:下一个操作是从用户提供的信息中获取槽位值。
对话流的三种主要状态
图 8.对话流
- 用户启动意图,触发一直在侦听的 Lambda 函数。Lambda 没有收到预期的 language 值,因此它将使用 Delegate 委派 Amazon Lex 继续处理对话,获取 language 槽位。
- 用户指定了语言,Amazon Lex 将该值解析为语言代码。Lambda 函数收到 language 值,然后使用 ElicitSlot 要求 Amazon Lex 提供 text_to_translate。
- 用户提供要翻译的文本。由于文本不固定且不可预测,Amazon Lex 无法将文本内容解析为 text_to_translate 值。因此,由 Lambda 函数解析文本,并开始翻译和文本转语音的过程。最后,Lambda 函数将翻译后的文本和预签名的链接通过 ElicitIntent 回复给 Amazon Lex。
要将后端和前端集成,Lambda 函数需要解析 Amazon Lex 的输出数据格式,因为 Amazon Lex 的输出数据是传递给 Lambda 函数的输入数据。如需了解更多详细信息,请参阅输入格式解析开发指南。下面,我们将学习如何从输入数据中提取运行翻译应用程序所需的必要信息。
首先,Lambda 函数必须提取 interpretations 的值,并尽可能保证与用户的话语一致:
def get_intent(intent_request):
interpretations = intent_request['interpretations'];
if len(interpretations) >0:
return interpretations[0]['intent']
else:
return None;
interpretation 是一个包含槽位值和对话状态的数组。
使用以下函数提取 Amazon Lex 解析出的槽位值:
def get_slot(slotname, intent, **kwargs):
try:
slot = intent['slots'].get(slotname)
if not slot:
return None
slotvalue = slot.get('value')
if slotvalue:
interpretedValue = slotvalue.get('interpretedValue')
originalValue = slotvalue.get('originalValue')
if kwargs.get('preference') == 'interpretedValue':
return interpretedValue
elif kwargs.get('preference') == 'originalValue':
return originalValue
# where there is no preference
elif interpretedValue:
return interpretedValue
else:
return originalValue
else:
return None
except:
return None
要保持 Lambda 函数和 Lex 之间的对话,必须了解用户在会话状态 (sessionState) 中使用的上下文 (activeContexts)。使用以下函数获取上下文:
def get_active_contexts(event):
try:
return event['sessionState'].get('activeContexts')
except:
return []
您需要特定于会话的上下文信息 sessionAttributes:
def get_session_attributes(event):
try:
return event['sessionState']['sessionAttributes']
except:
return {}
使用以下函数定义发送 DialogAction 状态:
DialogueAction 状态
Delegate
函数定义
def remove_inactive_context(context_list):
if not context_list:
return context_list
new_context = []
for context in context_list:
time_to_live = context.get('timeToLive')
if time_to_live and time_to_live.get('turnsToLive') != 0:
new_context.append(context)
return new_context
def delegate(active_contexts, session_attributes, intent):
print ('delegate!')
active_contexts = remove_inactive_context(active_contexts)
return {
'sessionState': {
'activeContexts': active_contexts,
'sessionAttributes': session_attributes,
'dialogAction': {
'type': 'Delegate'
},
'intent': intent,
'state': 'ReadyForFulfillment'
},
}
ElicitSlot
def elicit_slot(slotToElicit, active_contexts, session_attributes, intent, messages):
intent['state'] = 'InProgress'
active_contexts = remove_inactive_context(active_contexts)
if not session_attributes:
session_attributes = {}
session_attributes['previous_message'] = json.dumps(messages)
session_attributes['previous_dialog_action_type'] = 'ElicitSlot'
session_attributes['previous_slot_to_elicit'] = slotToElicit
return {
'sessionState': {
'sessionAttributes': session_attributes,
'activeContexts': active_contexts,
'dialogAction': {
'type': 'ElicitSlot',
'slotToElicit': slotToElicit
},
'intent': intent
},
}
ElicitIntent
def elicit_intent(active_contexts, session_attributes, intent, messages):
intent['state'] = 'Fulfilled'
active_contexts = remove_inactive_context(active_contexts)
if not session_attributes:
session_attributes = {}
session_attributes['previous_message'] = json.dumps(messages)
session_attributes['previous_dialog_action_type'] = 'ElicitIntent'
session_attributes['previous_slot_to_elicit'] = None
session_attributes['previous_intent'] = intent['name']
return {
'sessionState': {
'sessionAttributes': session_attributes,
'activeContexts': active_contexts,
'dialogAction': {
'type': 'ElicitIntent'
},
"state": "Fulfilled"
},
'requestAttributes': {},
'messages': messages
}
构建后端和前端函数后,便可以将它们集成在一起了!
第 5 步 - 将后端与前端集成
使用前面步骤中构建的所有代码,按照如下方式组装 Lambda Handler 程序。
Lambda Handler 代码
def lambda_handler(event, context):
print(event)
#Lambda Function Input Event and Response Format
interpretations = event['interpretations']
intent_name = interpretations[0]['intent']['name']
intent = get_intent(event)
#need it to Response Format
active_contexts = get_active_contexts(event)
session_attributes = get_session_attributes(event)
previous_slot_to_elicit = session_attributes.get("previous_slot_to_elicit") #to find out when Amazon Lex is asking for text_to_translate and join the conversation.
print(session_attributes)
if intent_name == 'TranslateIntent':
print(intent_name)
print(intent)
language = get_slot('language',intent)
text_to_translate = get_slot("text_to_translate",intent)
print(language,text_to_translate)
if language == None:
print(language,text_to_translate)
return delegate(active_contexts, session_attributes, intent)
if (text_to_translate == None) and (language != None) and (previous_slot_to_elicit != "text_to_translate"):
print(language,text_to_translate)
response = "What text do you want to translate?"
messages = [{'contentType': 'PlainText', 'content': response}]
print(elicit_slot("text_to_translate", active_contexts, session_attributes, intent, messages))
return elicit_slot("text_to_translate", active_contexts, session_attributes, intent, messages)
if previous_slot_to_elicit == "text_to_translate":
print("diferente a none")
text_to_translate = event["inputTranscript"]
text_ready = TranslateText(text_to_translate,language)
target_voice = get_target_voice(language)
object_name,task_id = start_taskID(target_voice,bucket_name,text_ready)
url_short = create_presigned_url(bucket_name, object_name, expiration=3600)
print ("text_ready: ", text_ready)
status = get_speech_synthesis(task_id)
response = f"The translate text is: {text_ready}. Hear the pronunciation here {url_short} "
messages = [{'contentType': 'PlainText', 'content': response}]
print(elicit_intent(active_contexts, session_attributes, intent, messages))
return elicit_intent(active_contexts, session_attributes, intent, messages)
重要说明:导入必要的库,指定存储桶名称,并根据亚马逊云科技服务初始化 Boto3 的客户端,然后部署。
Lambda 函数权限
要允许 Lambda 函数调用您的亚马逊云科技服务和资源,必须先创建具有所需权限的执行角色。执行以下步骤创建角色:
- 进入 Lambda 控制台的函数页面,然后选择函数的名称。
- 选择 Configuration(配置),选择 Permissions(权限),然后点击角色名称(图 9)。
图 9.角色名称
- 在 Identity and Access Management (IAM) 控制台中,前往 Add Permision(添加权限)--> Create inline policy(创建内联策略)
- 在策略编辑器中,选择 JSON。复制以下 JSON:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"polly:SynthesizeSpeech",
"polly:StartSpeechSynthesisTask",
"polly:GetSpeechSynthesisTask",
"comprehend:DetectDominantLanguage",
"translate:TranslateText"
],
"Resource": "*"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::YOU-BUCKET-NAME/*",
"arn:aws:s3:::YOU-BUCKET-NAME"
]
}
]
}
重要说明:请将 YOU-BUCKET-NAME 替换为您的存储桶名称。
- 点击 Next(下一步),输入策略名称,然后点击 Create policy(创建策略)。
第 6 步 - 配置并测试翻译器应用
将 Lambda 函数与机器人别名绑定
为了在用户与 Amazon Lex 交互时触发 Lambda 函数,您需要将函数与机器人别名绑定。更多详细信息,请参见创建 Lambda 函数并与 Amazon Lex 机器人绑定。
- 打开 Amazon Lex 控制台,选择在第 2 步中创建的机器人的名称。
- 在左侧面板中,点击 Aliases(别名),然后勾选目标机器人的别名(图 10)。
图 12.测试机器人
第 7 步 - 部署翻译器应用
现在,您已经使用 Amazon Lex 快速构建并测试了具有文本转语音功能的翻译对话机器人。但是,目前为止,您只能通过控制台进行访问机器人,并且这只是草稿版。
草稿版是您的机器人的工作副本。在发布正式版之前,更新也只更新草稿版本。
您需要创建不可变版本,才能将机器人投入生产环境。版本是工作机器人的编号快照。发布版本后,便可在工作流中的不同阶段中使用,例如开发、测试版部署和生产。
别名是指向机器人特定版本的指针。使用别名,可以轻松更新客户端应用中正在使用的版本,而无需更改代码。使用别名,可以根据需要将流量无缝定向到不同的版本。
下面,我们将学习如何创建机器人版本以及如何将别名指向版本。
创建版本
- 前往您的机器人页面,然后在左侧面板中,选择 Bot version(机器人版本),然后点击 Create version(创建版本)(图 13)。
图 13.创建新的机器人版本
- 设置机器人版本,然后点击 Create(创建)。
- 选择 Deployment(部署)--> Aliases(别名),然后点击 Create Alias(创建别名)。
- 命名别名并将别名与版本关联。选择新版本(图 14),然后点击 Create(创建)。
图 14.选择关联版本
将机器人与应用程序集成
您已经做好了将机器人与消息平台、移动应用程序和网站进行集成所需的一切准备,请参见以下说明构建应用程序:
总结
使用亚马逊云科技服务构建一个多语言应用对您来说也许是一次新奇的尝试。使用 Amazon Comprehend、Amazon Translate、Amazon Polly 和 Amazon Lex,您能够在短时间内创建一个具有文本转语音功能的强大翻译工具。
该过程展示了通过 Amazon Lambda 函数集成亚马逊云科技人工智能的简易性。只需略懂编码知识,任何人都可以构建复杂的应用,如语言翻译和语音合成应用。
实践是获得技能的最佳方式。尽管市面上已经有很多翻译应用,但创建自己的解决方案可以推动您学习更多知识。不管是否已有现成的应用可用,自己动手做总是更有意义。
继续学习
若想了解有关 [Amazon Polly 示例代码] 的更多信息,请访问 (https://docs.aws.amazon.com/polly/latest/dg/sample-code-overall.html)。