近实时智能应答 2D 数字人搭建

news2024/9/29 9:31:51

b8495694281d8a6711042ecfdf825d47.gif

背景

早在大语言模型如 GPT-3.5 等的兴起和被日渐广泛地采用之前,教育行业已经在 AI 辅助教学领域有过各种各样的尝试。在教育行业,人工智能技术的采用帮助教育行业更好地实现教学目标、提高教学质量、提高学习效率、提高学习体验、提高学习成果。例如,人工智能技术可以帮助教师更好地管理课堂、更好地识别学生的学习需求、更好地提供个性化的学习内容、更好地评估学生的学习成果、更好地提供学习支持。此外,人工智能技术还可以帮助教育行业更好地实现自动化,提高教育行业的效率和效果。总之,人工智能技术在教育行业的采用将会带来巨大的变化,为教育行业带来更多的发展机遇。

亚马逊云科技也一直致力于提供更方便快捷,功能更强大的 AI 服务来支持教育行业客户的技术创新和业务创新。特别是 Amazon Transcribe, Amazon Polly, Amazon Textract, Amazon Translate, Amazon Personalize, Amazon Rekognition, Amazon SageMaker 等产品分别从自然语言处理,图形图像处理,模型研发部署等方面为教育行业提供了强有力的技术支持。

本文结合 Amazon Transcribe, Amazon Polly,以及 OpenAI 的大语言模型和 D-ID.com 公司的 2D 数字人生成技术,介绍了一个演示用的可语音对话的智能 2D 数字人设计的服务,和具体的实现过程。

方案架构

8f4315d340e180ada53c7c21526aeca6.png

为了能在一个统一的用户界面呈现语音输入,语音输出,以及 2D 数字人视频播放的整体效果,本方案选择 Gradio 框架实现 WebUI 的功能。呈现的 WebUI 如下:

646c5632b3214b8d8eca0f6c9dc32239.jpeg

用户可以通过直接输入文字内容或者使用麦克风输入语音,文字内容会使用 Langchain 附加上一定的上下文后送给 OpenAI 的 GPT 接口调用,语音输入会先调用 Amazon Transcribe 服务进行语音到文字的转换。经过 GPT 接口返回的文字内容,会调用 Amazon Polly 形成语音文件,同时语音文件会作为 D-ID.com 提供的 API 渲染出 2D 的动态视频在前端自动展示和播放。

本方案中语音输入,语音输出,文字响应生成,以及数字人视频生成的功能都可以做自由的组合和替换。特别是对于 OpenAI 接口的调用可以置换为对自部署的大语言模型的调用,同时 2D 数字人视频的生成也可以考虑其他类似服务,如 Heygen 等。

具体实现

语音输入部分

Amazon Transcribe 支持实时转录语音(流式传输),也可以转录 Amazon S3 存储桶中的语音文件(批处理)。Transcribe 支持多达几十种的不同国家的语言。

Transcribe 的实时转录能力非常的强大,处理流数据的同时,不断的利用之前的上下文进行结果的实时矫正,你可以通过下面这个截图看到 Transcribe 实时转录输出的效果:

fbe50bdbe8cccc494cb35b8c95cb6f85.png

在本方案中,我们使用批处理的方式处理输入的语音转录,具体代码如下:

def transcribe_func_old(audio):
    audio_file = open(audio, "rb")
    file_name = audio_file.name
    print("audio_file: "+file_name)
    
    # Set up the job parameters
    job_name = "ai-bot-demo"
    text_output_bucket = 'ai-bot-text-material' #this bucket is in us-west-1
    text_output_key = 'transcriptions/question.json'
    text_output_key = 'transcriptions/'+job_name+'.json'
    language_code = 'zh-CN'


    # Upload the file to an S3 bucket
    audio_input_bucket_name = "ai-bot-audio-material"
    audio_input_s3_key = "questions/tmp-question-from-huggingface.wav"
    
    s3.upload_file(file_name, audio_input_bucket_name, audio_input_s3_key)
    
    # Construct the S3 bucket URI
    s3_uri = f"s3://{audio_input_bucket_name}/{audio_input_s3_key}"


    response = transcribe.list_transcription_jobs()
    
    # Iterate through the jobs and print their names
    for job in response['TranscriptionJobSummaries']:
        print(job['TranscriptionJobName'])
        if job['TranscriptionJobName'] == job_name:
            response = transcribe.delete_transcription_job(TranscriptionJobName=job_name)
            print("delete transcribe job response:"+str(response))


    # Create the transcription job
    response = transcribe.start_transcription_job(
        TranscriptionJobName=job_name,
        Media={'MediaFileUri': s3_uri},
        MediaFormat='wav',
        LanguageCode=language_code,
        OutputBucketName=text_output_bucket,
        OutputKey=text_output_key
    )
    
    print("start transcribe job response:"+str(response))
    job_name = response["TranscriptionJob"]["TranscriptionJobName"]
    
    # Wait for the transcription job to complete
    while True:
        status = transcribe.get_transcription_job(TranscriptionJobName=job_name)['TranscriptionJob']['TranscriptionJobStatus']
        if status in ['COMPLETED', 'FAILED']:
            break
        print("Transcription job still in progress...")
        time.sleep(1)
    
    # Get the transcript
    #transcript = transcribe.get_transcription_job(TranscriptionJobName=job_name)
    transcript_uri = transcribe.get_transcription_job(TranscriptionJobName=job_name)['TranscriptionJob']['Transcript']['TranscriptFileUri']
    print("transcript uri: " + str(transcript_uri))
    
    transcript_file_content = s3.get_object(Bucket=text_output_bucket, Key=text_output_key)['Body'].read().decode('utf-8')
    print(transcript_file_content)
    json_data = json.loads(transcript_file_content)


    # Extract the transcript value
    transcript_text = json_data['results']['transcripts'][0]['transcript']
    return transcript_text

左滑查看更多

以上代码主要完成了几个步骤的工作:

  1. 将要处理的语音文件上传到 Amazon S3

  2. 创建 Amazon Transcribe 的工作任务并轮询检查任务状态

  3. 从 Amazon S3 获取 Amazon Transcribe 已完成任务的解析结果

应答内容生成部分

在本方案里,应答内容的生成借助 Langchain 这个开源框架,调用基于 OpenAI 的 coversation 接口,同时使用 memory 库对对话的上下文做了 5 轮保存。在实际的客户场景里,可以考虑更丰富的方式来规范回复的内容的有效性和客观性。

比如可以使用 Langchain 的对话模版来对大模型的角色进行预设,或者使用 Amazon Kendra,Amazon Opensearch 这样的知识库构建和检索引擎,来进一步限制大模型应答的内容范围,本方案中和语言大模型相关的代码如下:

memory = ConversationBufferWindowMemory(k=5) 
conversation = ConversationChain(     
    llm=OpenAI(streaming=True, callbacks=[StreamingStdOutCallbackHandler()], max_tokens=2048, temperature=0.5),      
     memory=memory, 
)

左滑查看更多

语音输出部分

Amazon Polly 可以将文本转化为逼真的语音。它支持多种语言并且包含各种逼真的声音模拟,也包含中文普通话语音的模拟。

你可以构建支持语音并能用于各种位置的应用程序,并选择适合客户的声音。Amazon Polly 也支持语音合成标记语言(SSML),它是一种基于 XML 的 W3C 标准标记语言,适用于语音合成应用程序,且支持使用通用 SSML 标签进行断句、重音和语调。自定义 Amazon SSML 标签提供了独特的选项,例如,能够以新闻播音员说话风格发出某些声音。这种灵活性能够帮助您创建逼真的语音,从而吸引并维持听众的注意力。

在本方案中,我们使用 Polly 的实时语音生成接口,使用了中文普通话发音的 VoiceID:Zhiyu,同时对特定的字符的发音做了定制化,这也是 Polly 一个非常有用的功能(Lexion)。

def polly_text_to_audio(audio_file_name, text, audio_format):


    if os.path.exists(audio_file_name):
        os.remove(audio_file_name)
        print("output mp3 file deleted successfully.")
    else:
        print("output mp3 file does not exist.")


    polly_response = polly.synthesize_speech(
        Text=text,
        OutputFormat=audio_format,  
        SampleRate='16000',
        VoiceId='Zhiyu',
        LanguageCode='cmn-CN',
        Engine='neural',
        LexiconNames=['xxxxCN']
    )   
    
    # Access the audio stream from the response
    if "AudioStream" in polly_response:
        # Note: Closing the stream is important because the service throttles on the
        # number of parallel connections. Here we are using contextlib.closing to
        # ensure the close method of the stream object will be called automatically
        # at the end of the with statement's scope.
            with closing(polly_response["AudioStream"]) as stream:
               try:
                # Open a file for writing the output as a binary stream
                    with open(audio_file_name, "wb") as file:
                       file.write(stream.read())
               except IOError as error:
                  # Could not write to file, exit gracefully
                  print(error)
                  sys.exit(-1)


    else:
        # The response didn't contain audio data, exit gracefully
        print("Could not stream audio")
        sys.exit(-1)

左滑查看更多

2D 数字人视频的生成部分

这里我们使用了一个外部第三方的 SaaS 服务。该服务由 D-ID.com 公司提供,对应的 API 可以直接接收文本输入和一张人脸图片来生成对应的动态播报视频,也可以接受语音文件加图片作为输入。

当你输入文本的时候,该 API 接口可以选择制定 Amazon 的 Polly 服务中的不同的 Voice ID 来自动为你合成语音。

在本方案中,我们想体现中文的语音输出的效果,但是 D-ID 的 API 接口中暂时无法直接为中文文本指定中文的 Voice ID。所以我们选择了先用 Polly 的 API 生成语音,再把语音和图片传送给 D-ID 的接口生成视频,具体的代码如下:

def generate_talk_with_audio(input, avatar_url, api_key = did_api_key):
    url = "https://api.d-id.com/talks"
    payload = {
        "script": {
            "type": "audio",
            "audio_url": input
        },
        "config": {
            "auto_match": "true",
            "result_format": "mp4"
        },
        "source_url": avatar_url
    }
    headers = {
        "accept": "application/json",
        "content-type": "application/json",
        "authorization": "Basic " + api_key
    }


    response = requests.post(url, json=payload, headers=headers)
    return response.json()




def get_a_talk(id, api_key = os.environ.get('DID_API_KEY')):
    url = "https://api.d-id.com/talks/" + id
    headers = {
        "accept": "application/json",
        "authorization": "Basic "+api_key
    }
    response = requests.get(url, headers=headers)
    return response.json()


def get_mp4_video(input, avatar_url=avatar_url):
    response = generate_talk_with_audio(input=input, avatar_url=avatar_url)
    print("DID response: "+str(response))
    talk = get_a_talk(response['id'])
    video_url = ""
    index = 0
    while index < 30:
        index += 1
        if 'result_url' in talk:    
            video_url = talk['result_url']
            return video_url
        else:
            time.sleep(1)
            talk = get_a_talk(response['id'])
    return video_url

左滑查看更多

在实际应用中,如果希望 Amazon Polly 实现近乎实时的文本转语音,可以结合大语言模型的流式输出进行实时处理,示例代码如下:

response = generate_response(prompt)
# create variables to collect the stream of events
collected_events = []
completion_text = ''
sentance_to_polly = ''
separators = ['?','。',',','!']
already_polly_processed = ''
# iterate through the stream of events
for event in response:
    collected_events.append(event)  # save the event response
    event_text = event['choices'][0]['text']  # extract the text
    if event_text in separators:
        sentance_to_polly = completion_text.replace(already_polly_processed,'') 
        #print("sentance_to_polly: "+sentance_to_polly)
        polly_text_to_audio(response_audio_filename, sentance_to_polly, 'mp3')
        already_polly_processed = completion_text
    completion_text += event_text  # append the text
    print(event_text, end='', flush=True)  # print the delay and text

左滑查看更多

以上这段代码,利用了 Amazon Polly 的实时处理能力。根据返回的流文本数据,在发现‘?’,‘。’,‘,’,‘!’等分割标点的时候,立刻调用 Amazon Polly 进行最近的一段文字的语音生成,然后附加到当前的视频文件的最后。等文字流接收完毕,语音也基本上转换完毕了。

本方案目前托管在 huggingface 网站提供的工作空间:https://huggingface.co/spaces/xdstone1/ai-bot-demo,相应的代码也可以通过以下渠道来获取:

git lfs install
git clone https://huggingface.co/spaces/xdstone1/ai-bot-demo

左滑查看更多

方案的演示视频可以通过以下链接观看:

https://d3g7d7eldf7i0r.cloudfront.net/。

总结

今年是 AIGC 爆发的一年,也是教育行业所在的客户看到行业拐点的一年。在这个关键的历史性节点上,亚马逊云科技愿意和客户一起面对这些新的机会和挑战,以客户的需求为导向,帮助客户抓住 AI 浪潮带来的红利。

目前除了本文展示的 2D 数字人方案,亚马逊云科技也可以帮助客户提供基于 3D 数字人或者其他 3D 数字形象的直播、互动等方案。同时我们也会引入更多的技术合作伙伴如跃迁引擎(https://www.warpengine.cc/)来丰富整个数字人、数字形象直播、点播、互动等场景的解决方案,助力更多教育行业客户加速 AI 技术的采用和落地。

本篇作者

ca987e33899dee354fef4f00d31158c6.jpeg

薛东

亚马逊云科技解决方案架构师,负责基于亚马逊云平台的解决方案咨询和设计,目前在亚马逊云科技大中华区服务教育行业客户。专注于无服务,安全等技术方向。

30dbcc876fa86a09978c1e4854fd8e25.gif

8a4836c092695d8fd8c4697037080772.gif

听说,点完下面4个按钮

就不会碰到bug了!

1159c7bb319dfd3bdf3b0d709d67d456.gif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/906425.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

动态内存开辟

动态内存开辟 1.动态内存开辟相关试题 题目1&#xff1a; void GetMemory(char *p) {p (char *)malloc(100); } void Test(void) {char *str NULL;GetMemory(str);strcpy(str, "hello world");printf(str); }解释&#xff1a;这里在Test函数中&#xff0c;只是将…

FTP“方便”又“便宜”,为什么有必要替代?

FTP作为全世界第一款文件传输协议&#xff0c;在全球范围内得到大量应用&#xff0c;它为特定场景下的专业传输需求提供了解决方案&#xff0c;被各个行业和领域采用。 FTP使用普遍&#xff0c;主要得益于FTP的经济成本低&#xff0c;且使用方便。目前&#xff0c;开源FTP软件有…

sh脚本函数 数组 expect免交互

1、函数 在编写脚本时&#xff0c;有些脚本可以反复使用&#xff0c;可以调用函数来解决 语句块定义成函数约等于别名 1.1、设置函数 1.2、删除函数 unset 函数名 1.3、函数的传参数 函数变量的作用范围&#xff1a; 函数在shell脚本中仅在当前的shell环境中有效 shell脚…

day 28 | ● 122.买卖股票的最佳时机II ● 55. 跳跃游戏 ● 45.跳跃游戏II

122.买卖股票的最佳时机II 由于卖出没有限制条件&#xff0c;所以可以将一段时间的整体收益分割成每天零碎的收益&#xff0c;然后加起来那些高的即可。 func maxProfit(prices []int) int {sum : 0for i : 1; i < len(prices);i{if prices[i] - prices[i -1] > 0{sum …

Vue入门-特性、常用指令、生命周期、组件

Vue vue简介 ​ Vue (发音为 /vjuː/&#xff0c;类似 view) 是一款用于构建用户界面的JavaScript框架。它基于标准HTML、CSS和JavaScript构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助开发者高效地开发用户界面。 [7] Vue特征 解耦视图与数据M…

Meta-SR: A Magnification-Arbitrary Network for Super-Resolution整理

目录 说明摘要引言相关工作SISRMeta-Learning 本文的方法Meta-Upscale方法Location ProjectionWeight PredictionFeature Mapping 实验细节总结实现代码参考链接 说明 作为一个读者&#xff0c;在阅读这篇文章后&#xff0c;按照自己的理解对其中内容做以总结&#xff08;不然总…

ffmpeg,nginx,vlc把rtsp流转hls

ffmpeg:rtsp>hls流; nginx 托管hls流服务; vlc测试hls流服务; 参考了很多相关文档和资料,由于比较乱就不在一一引用介绍了&#xff0c;下面的是实操OK的例子&#xff1b; 1&#xff09;ffmpeg (ffmpeg-4.4.1-full_build)&#xff0c;要用full版本&#xff0c;否则会缺某些…

【从零学习python 】56. 异常处理在程序设计中的重要性与应用

文章目录 异常的概念读取文件异常try...except语句try...else语句try...finally语句 进阶案例 异常的概念 在程序运行过程中&#xff0c;由于编码不规范或其他客观原因&#xff0c;可能会导致程序无法继续运行&#xff0c;此时就会出现异常。如果不对异常进行处理&#xff0c;…

电工-什么是电功?及电功单位与计算公式讲解

什么是电功&#xff1f;及电功单位与计算公式讲解 电能是有其他形式的能量&#xff08;如机械能、热能、化学能、核能&#xff09;转换而来的一种能量&#xff0c;而电能又可以转换成为其他形式的能。比如当电能的具体体现&#xff1a;电流&#xff0c;其通过电灯泡发光就是将…

代码详解——可变形卷积(DCNv3)

文章目录 概述dcnv3.pyto_channels_firstto_channels_lastbuild_norm_layerbuild_act_layer_is_power_of_2CenterFeatureScaleModuleDCNv3_pytorchDCNv3 dcnv3_func.pyDCNv3Functiondcnv3_core_pytorch_get_reference_points_generate_dilation_grids 可变形卷积DCNv1 & DC…

华为OD机试 - 出错的或电路 - 二进制 - (Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#…

Vue2.0+webpack 引入字体文件(eot,ttf,woff)

webpack.base.config.js 需要配置 {test:/\/(woff2?|eot|ttf|otf)(\?.*)?$/,loader: url-loader,options: {limit: 10000,name: utils.assetsPath(fonts/[name].[hash:7].[ext])}} 如果 Vue2.0webpack3.6引入字体文件&#xff08;eot&#xff0c;ttf&#xff0c;woff&…

成都爱尔林江院长解析离焦眼镜为何与众不同

近视是影响我国国民尤其是青少年眼健康的重大公共卫生问题。因病因不明确&#xff0c;且尚无有效的治疗方法&#xff0c;如何有效控制近视发生和增长备受关注。国家出台了儿童近视防控方案&#xff0c;社会上也出现了各种近视防控方法及策略。周边离焦技术&#xff0c;算得上近…

快速上手Linux核心命令:Linux的文本编辑器vi和vim

前言 上一篇中已经预告&#xff0c;我们这篇主要说Linux中vi/vim 编辑器。它是我们使用Linux系统不可缺少的工具&#xff0c;学会了&#xff0c;你就可以在Linux世界里畅通无阻&#xff0c;学废了&#xff0c;常用操作你也会了&#xff0c;也是够用了&#xff0c;O(∩_∩)O 简…

javascript初学者可以做些什么小东西或者项目来练手?

前言 可以试一下面的一些项目&#xff0c;可能有一些比较复杂&#xff0c;可以学习一下代码的结构思路&#xff0c;希望对你有帮助~ 实用工具向 1.Exchart Star&#xff1a;55.6k Exchart提供了大量精美的图表&#xff0c;只有你想不到&#xff0c;没有你在它上面找不到的&…

了解 JSON 格式

一、JSON 基础 JSON&#xff08;JavaScript Object Notation&#xff0c;JavaScript 对象表示法&#xff09;是一种轻量级的数据交换格式&#xff0c;JSON 的设计目的是使得数据的存储和交换变得简单。 JSON 易于人的阅读和书写&#xff0c;同时也易于机器的解析和生成。尽管 J…

8月17日上课内容 LVS+Keepalived群集

本章结构 Keepalived概述 keepalived工作原理 Keepalived 是一个基于VRRP协议来实现的LVS服务高可用方案&#xff0c;可以解决静态路由出现的单点故障问题 在一个LVS服务集群中通常有主服务器 (MASTER)和备份服务器(BACKUP)两种角色的服务器&#xff0c;但是对外表现为一个虚拟…

为什么选择elasticsearch分布式搜索引擎

文章目录 &#x1f52d;什么是elasticsearch&#x1f320;ELK技术栈&#x1f320;elasticsearch和lucene&#x1f320;为什么不是其他搜索技术&#xff1f; &#x1f52d;总结 &#x1f52d;什么是elasticsearch elasticsearch是一款非常强大的开源搜索引擎&#xff0c;具备非常…

mini木马实践和防护方法

一、实验目的&#xff1a; 1.了解木马的实现原理 2.了解基本的防护方法 二、预备知识&#xff1a; 木马通常有两个可执行程序&#xff1a;一个是客户端&#xff0c;即控制端&#xff1b;另一个是服务端&#xff0c;即被控制端。植入被种者电脑的是“服务器”部分&#xff0c;而…

linux如何使用keepalived配置VIP

VIP常用于负载均衡的高可用&#xff0c;使用VIP可以给多个主机绑定一个IP&#xff0c;这样&#xff0c;当某个负载应用挂了之后&#xff0c;可以自动切到另一个负载。 我这里是在k8s环境中做的测试&#xff0c;集群中有6个节点&#xff0c;我给140和141两个节点配置VIP。 1. 安…