
掌握一门新语言最有效的方法是什么?就是说它!但我们都知道,在他人面前尝试新词汇和短语可能会让人感到害怕。如果你有一个耐心和理解的朋友可以一起练习,不受评判,没有羞耻感,那该多好呀!
你正在寻找的那个有耐心、理解的朋友,也许就是由LLMs提供支持的虚拟语言导师!这可能是一种改变游戏规则的方式,可以在你自己的空间里掌握一门语言。
最近,大型语言模型已经出现,它们正在改变我们的做事方式。这些强大的工具已经创建了可以像人类一样回应的聊天机器人,并迅速融入到我们生活的各个方面,被用于数百种不同的方式。其中一个特别有趣的用途是语言学习,特别是在口语练习方面。
当我一段时间前搬到德国时,我意识到学习一门新语言并找到练习口语的机会是多么具有挑战性。上课和语言学习小组可能很昂贵,或者很难安排在繁忙的日程中。作为一个面临这些挑战的人,我有一个想法:为什么不利用聊天机器人进行口语练习呢?但仅仅通过发短信是不够的,因为语言学习不仅涉及写作。因此,通过将AI驱动的聊天机器人与语音转文本和文本转语音技术相结合,我成功地创建了一种与真人对话类似的学习体验。
在本文中,我将分享我选择的工具,解释整个过程,并介绍通过语音命令和语音回应与AI聊天机器人进行口语练习的概念。该项目的流程包括三个主要部分:语音转文本、使用语言模型和文本转语音转换。以下将对这三个部分进行解释。
1. 语音转文本
语音识别对我的语言导师来说起到了桥梁作用,它将用户的口语输入与AI的文本理解连接起来生成回应。这是一个至关重要的组成部分,它实现了基于语音的交互,为更沉浸式和有效的语言学习体验做出了贡献。
准确的转录对于与聊天机器人的流畅交互至关重要,尤其是在语言学习环境中,发音、口音和语法是关键因素。有许多语音识别工具可以用于Python中的口语转录,如OpenAI的Whisper和Google Cloud的语音转文本。
在选择语言导师项目的语音识别工具时,应考虑准确性、语言支持、成本以及是否需要离线解决方案等因素。
谷歌有一个需要互联网连接的Python API,每月免费提供60分钟的转录时间。与谷歌不同,OpenAI发布了他们的Whisper模型,只要有足够的计算能力,你就可以在本地运行它,而不依赖于互联网速度。这就是为什么我选择Whisper,以尽可能减少转录的延迟。
2. 语言模型
语言模型是这个项目的核心。由于我已经非常熟悉ChatGPT及其API,所以我决定在这个项目中继续使用它。然而,如果你有足够的计算能力,你也可以在本地部署Llama,这将是免费的。ChatGPT的成本稍微高一些,但更加方便,你只需要几行代码就可以运行它。
为了增加回应的一致性并使其符合特定的模板,你还可以对语言模型进行微调(例如如何在你的用例中微调ChatGPT)。你需要生成示例句子和相应的最佳回应,并将它们输入到微调训练中。然而,我想构建的基本导师不需要微调,我将在项目中使用通用的GPT3.5-turbo。
下面我将提供一个通过其API在Python中促进用户与ChatGPT之间对话的API调用示例。首先,如果你还没有一个OpenAI账户,你需要打开一个,并设置一个API密钥以与ChatGPT进行交互。具体说明请参见此处。
一旦你设置好了API密钥,你就可以开始使用openai.ChatCompletion.create方法生成文本。该方法需要两个参数:model参数,用于指定通过API访问的特定GPT模型,以及messages参数,其中包括与ChatGPT对话的结构。 messages参数由两个关键组件组成:role和content。
以下是一个代码片段来说明这个过程:
“`python
# 初始化消息并设置行为
messages = [{“role”: “system”, “content”: “在这里输入行为。”}]
# 开始一个无限循环以与用户继续对话
while True:
# 获取用户的输入作为回应
content = input(“用户: “)
messages.append({“role”: “user”, “content”: content}) # 将用户的输入添加到消息中
# 使用 OpenAI GPT-3.5 模型生成对用户输入的回应
completion = openai.ChatCompletion.create(
model=”gpt-3.5-turbo”,
messages=messages
)
chat_response = completion.choices[0].message.content # 从 API 响应中提取聊天回应
print(f’ChatGPT: {chat_response}’) # 打印回应
# 将回应添加到消息中,角色为 “assistant”,以记录对话历史
messages.append({“role”: “assistant”, “content”: chat_response})
“`
- `system` 角色用于确定 ChatGPT 的行为,可以通过在消息列表的开头添加指示来实现。
- 在对话过程中,通过提供用户的语音输入来获取来自用户的 `user` 消息,以从 ChatGPT 获取回应。
- 最后,将 ChatGPT 的回应以 `assistant` 角色追加到消息列表中,以记录对话历史。
3. 文本转语音
在语音转文本的部分,我解释了用户如何使用语音命令来模拟与真人对话的体验。为了进一步增强这种感觉,创造一个更动态和交互式的学习体验,下一步是使用类似 gTTS 的文本转语音工具将 GPT 的文本输出转换为可听的语音。这不仅有助于创建更具吸引力和易于跟随的体验,还解决了语言学习的一个关键方面:通过听而不是阅读来理解。通过整合这种听觉组件,我们提供了一种更全面的实践,更接近于真实世界的语言使用。
有各种各样的 TTS 工具可用,例如 Google 的文本到语音 (gTTS) 和 IBM Watson 的语音到文本。在这个项目中,我选择了 gTTS,因为它非常易于使用,声音质量自然,而且完全免费。要使用 gTTS 库,您需要有一个互联网连接,因为该库需要访问 Google 的服务器以将文本转换为语音。
管道的详细解释
在深入了解管道之前,您可能想要查看一下我的 Github 页面上的完整代码,因为我将引用其中的一些部分。
下图解释了 AI 助手虚拟语言教师的工作流程,旨在建立实时的以语音为基础的对话式学习体验:

- 用户通过按下和持住空格键开始对话录制,并且在释放空格键时停止录制。下面的 Python 代码片段解释了启用此按下和说话功能的部分。
以下全局变量用于管理录制过程的状态:
recording = False # 表示系统当前是否正在录制音频
done_recording = False # 表示用户已完成录制语音命令
stop_recording = False # 表示用户想要退出对话
`listen_for_keys` 函数用于检测按键按下和释放。根据空格键和 Esc 键的状态设置全局变量。
def listen_for_keys():
# 监听按键以控制录制
global recording, done_recording, stop_recording
while True:
if keyboard.is_pressed('space'): # 空格键按下时开始录制
stop_recording = False
recording = True
done_recording = False
elif keyboard.is_pressed('esc'): # 按下 Esc 键时停止录制
stop_recording = True
break
elif recording: # 松开空格键时停止录制
recording = False
done_recording = True
break
time.sleep(0.01)
callback函数用于处理录音时的音频数据。它检查recording标志来确定是否记录传入的音频数据。
def callback(indata, frames, time, status): # 每个音频块在录音期间调用的函数 if recording: if status: print(status, file=sys.stderr) q.put(indata.copy())
press2record函数是负责在用户按住空格键时处理语音录制的主要函数。
它初始化全局变量来管理录制状态,并确定采样率,还创建一个临时文件来存储录制的音频。
然后,该函数打开一个SoundFile对象来写入音频数据,并打开一个InputStream对象来从麦克风捕获音频,使用之前提到的callback函数。启动一个线程来监听键盘按键,特别是用于录制的空格键和用于停止的’esc’键。在循环内部,函数检查录制标志并将音频数据写入文件(如果录制处于活动状态)。如果录制停止,函数返回-1;否则,返回录制音频的文件名。
def press2record(filename, subtype, channels, samplerate): # 当按键被按下时处理录制的函数 global recording, done_recording, stop_recording stop_recording = False recording = False done_recording = False try: # 如果未提供采样率,则确定采样率 if samplerate is None: device_info = sd.query_devices(None, 'input') samplerate = int(device_info['default_samplerate']) print(int(device_info['default_samplerate'])) # 如果未提供文件名,则创建一个临时文件名 if filename is None: filename = tempfile.mktemp(prefix='captured_audio', suffix='.wav', dir='') # 打开音频文件以进行写入 with sf.SoundFile(filename, mode='x', samplerate=samplerate, channels=channels, subtype=subtype) as file: with sd.InputStream(samplerate=samplerate, device=None, channels=channels, callback=callback, blocksize=4096) as stream: print('按下空格键开始录制,松开停止,或按Esc退出') listener_thread = threading.Thread(target=listen_for_keys) # 在单独的线程上启动监听器 listener_thread.start() # 将录制的音频写入文件 while not done_recording and not stop_recording: while recording and not q.empty(): file.write(q.get()) # 如果录制停止,则返回-1 if stop_recording: return -1 except KeyboardInterrupt: print('用户中断') return filename
最后,get_voice_command函数调用press2record来录制用户的语音命令。
def get_voice_command(): # ... saved_file = press2record(filename="input_to_gpt.wav", subtype=args.subtype, channels=args.channels, samplerate=args.samplerate) # ...
- 在临时.wav文件中捕获和保存语音命令后,我们进入转录阶段。在这个阶段,使用Whisper将录制的音频转换为文本。下面是简单运行.wav文件的转录任务的相应脚本:
def get_voice_command(): # ... result = audio_model.transcribe(saved_file, fp16=torch.cuda.is_available()) # ...
此方法接受两个参数:记录的音频文件路径saved_file,以及一个可选的标志,如果CUDA可用,则使用FP16精度以提高性能。它简单地返回转录的文本。
- 然后,将转录的文本发送给ChatGPT,以生成适当的响应在
interact_with_tutor()函数中。相应的代码片段如下:
def interact_with_tutor(): # 定义系统角色以设置聊天助手的行为 messages = [ {"role": "system", "content" : "Du bist Anna, meine deutsche Lernpartnerin. Du wirst mit mir chatten. Ihre Antworten werden kurz sein. Mein Niveau ist B1, stell deine Satzkomplexität auf mein Niveau ein. Versuche immer, mich zum Reden zu bringen, indem du Fragen stellst, und vertiefe den Chat immer."} ] while True: # 获取用户的语音命令 command = get_voice_command() if command == -1: # 如果录制停止,则保存聊天记录并退出 save_response_to_pkl(messages) return "聊天已停止。" # 将用户的命令添加到消息历史记录中 messages.append({"role": "user", "content": command}) # 从聊天助手生成回复 completion = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=messages ) # 从完成中提取响应 chat_response = completion.choices[0].message.content # 从完成中提取响应 print(f'ChatGPT: {chat_response} \n') # 打印助手的响应 messages.append({"role": "assistant", "content": chat_response}) # 将助手的响应添加到消息历史记录中 # ...
函数interact_with_tutor首先通过定义ChatGPT的系统角色来塑造其在对话中的行为。由于我的目标是练习德语,所以我相应地设置了系统角色。我将我的虚拟导师称为“Anna”,并设置了我的语言熟练程度,以便她调整她的回答。此外,我还指示她通过提问保持对话的互动性。
接下来,用户的转录语音命令以“用户”角色附加到消息列表中。然后将该消息发送给ChatGPT。在while循环中继续对话时,用户命令和GPT回答的整个历史记录在消息列表中被记录下来。
- 在ChatGPT的每个回答之后,我们使用gTTS将文本消息转换为语音。
def interact_with_tutor(): # ... # 将文本回答转换为语音 speech_object = gTTS(text=messages[-1]['content'],tld="de", lang=language, slow=False) speech_object.save("GPT_response.wav") current_dir = os.getcwd() audio_file = "GPT_response.wav" # 播放语音回答 play_wav_once(audio_file, args.samplerate, 1.0) os.remove(audio_file) # 删除临时音频文件
gTTS()函数有4个参数:text、tld、lang和slow。将text参数分配为messages列表中最后一条消息的内容(由[-1]表示),你想将其转换为语音。tld参数指定了Google翻译服务的顶级域名。将其设置为"de"意味着使用德语域名,这对于确保发音和语调适合德语非常重要。lang参数指定要以哪种语言朗读文本。在这段代码中,language变量设置为'de',表示文本将以德语朗读。slow=False:slow参数控制语音的速度。将其设置为False意味着语音将以正常速度播放。如果设置为True,语音将会放慢播放速度。
- ChatGPT回答的转换后的语音被保存为临时的.wav文件,然后播放给用户,最后被删除。
interact_with_tutor函数将在用户通过再次按下空格键继续对话时反复运行。- 如果用户按下“esc”键,对话结束,并且整个对话被保存到一个pickle文件
chat_log.pkl中。你可以以后用它进行一些分析。
命令行使用
要运行脚本,只需在终端中运行以下Python代码:
sudo python chat.py
需要使用sudo,因为脚本需要访问麦克风并利用键盘库。如果你使用Anaconda,你也可以通过“以管理员身份运行”启动Anaconda终端,以便获得完全访问权限。
下面是一个演示视频,展示了代码在我的笔记本电脑上的运行情况。你可以对性能有所了解:
作者制作的演示视频
最后的话
我通过简单设置ChatGPT的系统角色并调整gTTs函数中的参数与德语语言保持一致,将导师的语言设定为德语。然而,你可以轻松将其切换到其他语言。仅需几秒钟即可针对目标语言进行配置。
如果你想聊特定的话题,你还可以在ChatGPT的系统角色中添加它。例如,与它一起练习面试可能是一个不错的用例。你还可以指定你的语言水平来调整它的回答。
一个重要的注意事项是聊天的整体速度取决于你的互联网连接(由于ChatGPT API和gTTS)以及你的硬件(由于Whisper的本地部署)。在我的情况下,我输入后的整体响应时间在4-10秒之间。