当前位置:   article > 正文

以大语言模型ChatGLM2-6B为后台,打造个人语音交互机器人Demo_chatglm 语音

chatglm 语音

引言

随着以ChatGLM2-6B为代表的开源大型语言模型的兴起,人工智能革命正席卷全球……

ChatGLM2-6B这一代表性的开源大型模型,以其易于部署、适度的参数量和强大的中文处理能力,为个人用户提供了在个人显卡上部署大型模型的便捷途径。

然而,在大型语言模型领域,人机交互仍然主要以传统的文字输入为主,这种方式难以满足人们对实时性和高效率的需求。在许多情景下,人们更期望能够直接与一个语音交互的智能助手互动。

本文将结合STT(自动语音识别)、大型模型和TTS(文本到语音合成)等人工智能技术,创建一个具备语音交互功能的智能机器人演示。

环境准备

  1. 在开始之前,我们需要准备好开发环境。本文的代码主要采用Python语言编写,建议使用Python版本3.9以上。作者在Windows10操作系统上使用Python 3.9进行了测试。
  2. 请安装一些python库,主要有:
  • 录音库:Pyaudio,及相关音频处理库wave。
  • TTS库:edge-tte,一款免费的TTS库。
  • 音频播放库:pygame,实现代码播放音频文件。
  • openai:调用大语言模型API需要用到。
  • uuid:用于生成唯一的文件名。

      请参考以下Python库的导入列表,并根据需要使用pip进行安装。

  1. import pyaudio
  2. import wave
  3. import requests
  4. import json
  5. import base64
  6. import os
  7. import edge_tts
  8. import asyncio
  9. import pygame
  10. import openai
  11. import uuid # 用于生成唯一的文件名

        3. 在百度AI开发平台开通短语识别标准版服务,可以领取免费额度,赠送15万次调用,很香。

短语音识别标准版_短语音识别-百度AI开放平台 (baidu.com)

          按照官网给出的操作指引一步一步来就OK了。

          提醒:调用百度语音识别API之前一定要先创建应用。

        4. 一台可以跑ChatGLM2-6B的服务器或个人电脑。模型的下载、部署这里不做赘述,可以参考网络上的教程。命令行运行ChatGLM2-6B-main目录下面的openai_api.py。

python openai_api.py

 运行成功会给出一串地址,后面需要在调用方的主机上ssh这个地址。

具体操作是:

  1. Win+R 打开cmd
  2. 输入命令:
  3. ssh -L 8000:0.0.0.0:8000 <你的服务器用户名>@<你的服务器的IP地址>
  4. 回车
  5. 输入密码:
  6. <你的服务器密码>

 核心代码

1.录音模块

我们首先使用PyAudio库来录制音频,将其保存为.wav文件。这个步骤包括设置音频参数,如采样频率、数据流块等。录制完成后,音频文件将被保存在当前目录下。代码内有详细注释,请结合注释进一步理解代码。
 

  1. #1.录音
  2. #用Pyaudio录制音频(生成wav文件)
  3. def audio_record(rec_time,filename):
  4. """
  5. :param rec_time : 音频录制时间
  6. :param filename : 输出音频文件
  7. :返回值:在当前目录输出一个音频文件
  8. """
  9. CHUNK=1024 #定义数据流块
  10. FORMAT = pyaudio.paInt16 #16bit编码格式
  11. CHANNELS = 1 #单声道
  12. RATE = 16000 #16000采样频率
  13. #创建一个音频对象
  14. p = pyaudio.PyAudio()
  15. #创建音频数据流
  16. stream = p.open(format=FORMAT,
  17. channels=CHANNELS,
  18. rate=RATE,
  19. input=True,
  20. frames_per_buffer=CHUNK)
  21. print('Start recording...')
  22. frames=list() #空列表用于保存录制的音频流
  23. #录制音频数据
  24. for i in range(0,int(RATE/CHUNK*rec_time)):
  25. data=stream.read(CHUNK)
  26. frames.append(data)
  27. #录制完成
  28. # print(frames)
  29. #停止数据流
  30. stream.stop_stream()
  31. stream.close()
  32. #关闭pyaudio
  33. p.terminate()
  34. print('recording done...')
  35. #保存音频文件
  36. with wave.open(filename,'wb') as f:
  37. f.setnchannels(CHANNELS) #设置音频声道数
  38. f.setsampwidth(p.get_sample_size(FORMAT)) #以字节为样本返回样本宽度
  39. f.setframerate(RATE) #设置采样频率
  40. f.writeframes(b''.join(frames))
  41. f.close()

 2.获取百度ASR access token

为了进行语音识别,我们需要获取百度语音识别的Access Token。这个Token用于访问百度的语音识别API。您需要提供API_KEY和SECRET_KEY,然后使用这些密钥生成Access Token。

如何查看自己的API_KEY和SECRET_KEY?登录百度AI开放平台,选择应用列表即可查看。

 

 此函数向百度服务器发送API_KEY和SECRET_KEY,返回access token,用于识别对应的服务和用户。 

  1. API_KEY = "XXX" # 这里请替换为你的API_KEY
  2. SECRET_KEY = "XXX" # 这里请替换为你的SECRET_KEY
  3. def get_access_token():
  4. """
  5. 使用 AK,SK 生成鉴权签名(Access Token)
  6. :return: access_token,或是None(如果错误)
  7. """
  8. url = "https://aip.baidubce.com/oauth/2.0/token"
  9. params = {"grant_type": "client_credentials", "client_id": API_KEY, "client_secret": SECRET_KEY}
  10. return str(requests.post(url, params=params).json().get("access_token"))

 3.调用百度ASR API,上传录音文件,获得转换后的文本。

使用获取的Access Token,我们将录音文件上传到百度语音识别API,以将音频转换为文本。这里需要设置一些参数,如采样频率、格式等。最终,我们将获得从录音中识别出的文本。

  1. # 3.上传录音文件
  2. def BaiduYuYin(file_url,token):
  3. """
  4. :param file_url: 录音文件路径
  5. :param token: 获取的access token
  6. :return: 录音识别出来的文本
  7. """
  8. try:
  9. RATE='16000'
  10. FORMAT='wav'
  11. CUID='rvs7K414cquxm4f62jtasIRi6iNRNXR6'
  12. DEV_PID='1536' # 普通话,支持简单的英文识别
  13. file_url=file_url
  14. token=token
  15. #以字节格式读取文件之后进行编码
  16. with open(file_url,'rb') as f:
  17. speech=base64.b64encode(f.read()).decode('utf-8')
  18. size = os.path.getsize(file_url)# 语音文件的字节数
  19. headers={'Content-Type':'application/json',
  20. 'Accept':'application/json'} # json格式post上传本地文件
  21. url='https://vop.baidu.com/server_api'
  22. data={
  23. "format":FORMAT,#格式
  24. "rate":RATE,#取样频率,固定值16000
  25. "dev_pid":DEV_PID,#语音识别类型
  26. "speech":speech,#本地语音文件的二进制数据,需要进行base64编码
  27. "cuid":CUID,#用户唯一标识,用来区分用户 建议填写能区分用户的机器MAC地址或IMEI码,长度为60字符以内。
  28. "len":size,#语音文件的字节数
  29. "channel":1,#声道数,仅支持单声道,固定值为1
  30. "token":token,
  31. }
  32. req=requests.request("POST",url,data=json.dumps(data),headers=headers) #request.post 改为requests.request("POST"……)
  33. data_dict=json.loads(req.text)
  34. # print(data_dict['result'][0])
  35. return data_dict['result'][0] # 返回文本
  36. except:
  37. return '识别不清楚'

 4.调用大语言模型的API,实现问答。

我们使用ChatGLM2-6B大语言模型来生成回复文本。在这一步中,我们向模型提供之前识别的文本,然后等待模型生成回复。

  1. # 4.接入大语言模型
  2. from dotenv import load_dotenv, find_dotenv
  3. _ = load_dotenv(find_dotenv()) # read local .env file
  4. os.environ['OPENAI_API_KEY'] = 'EMPTY'
  5. os.environ['OPENAI_API_BASE'] = 'http://localhost:8000/v1'
  6. openai.api_key = 'none'
  7. openai.api_base = 'http://localhost:8000/v1'
  8. def get_completion(prompt, model="gpt-3.5-turbo"):
  9. """
  10. :param prompt:输入提示词
  11. :param model:模型名称(使用默认参数即可)
  12. :return: 大模型的回复文本
  13. """
  14. messages = [{"role": "user", "content": prompt}]
  15. response = openai.ChatCompletion.create(
  16. model=model,
  17. messages=messages,
  18. temperature=0,
  19. )
  20. return response.choices[0].message["content"]

5. 文本转语音TTS

将生成的文本转换为语音,我们使用edge_tts库。这个库可以将文本转换为语音文件(.mp3格式)。可以选择不同的语音和参数,以获得不同风格的语音。

  1. # 5.文本转语音TTS:edge-tts
  2. async def generate_audio_from_text(text,file_url):
  3. """
  4. :param text:需要进行转换的文本
  5. :file_url:转换后输出的音频文件地址
  6. :return:无
  7. """
  8. voice = 'zh-CN-YunxiNeural'
  9. output = file_url
  10. rate='-4%'
  11. volume = '+0%'
  12. tts = edge_tts.Communicate(text=text,voice=voice,rate=rate,volume=volume)
  13. await tts.save(output)

注意,调用此函数时,要使用asyncio.run:

  1. #调用示例
  2. asyncio.run(generate_audio_from_text(model_response,filename))

6.播放音频文件

最后,我们使用pygame库来播放生成的语音文件。这使得大模型的回复能够以声音的方式呈现给用户。

  1. # 6.播放音频文件:pygame
  2. def play_mp3(mp3_file):
  3. """
  4. :param mp3_file:需要播放的录音文件地址
  5. :return:无
  6. """
  7. pygame.init() # 初始化pygame
  8. pygame.mixer.init() # 初始化音频混合器
  9. pygame.mixer.music.load(mp3_file) # 加载指定MP3文件
  10. pygame.mixer.music.play() # 播放
  11. clock = pygame.time.Clock()
  12. while pygame.mixer.music.get_busy(): # 使用一个循环来等待音频播放完毕,保证程序不会在播放结束前退出
  13. clock.tick(3)

 7.整体的函数调度顺序

如何运行?

要运行这个语音交互demo,只需运行main()函数。等待您的发言,然后进行录音、语音识别、文本生成、语音合成和播放,最后询问是否继续对话或退出。

  1. def main():
  2. while True:
  3. # 1. 提示用户发言
  4. print('请发言,谢谢!')
  5. # 2. 录制音频
  6. audio_record(5, 'user_audio.wav')
  7. print('Audio recording complete.')
  8. # 3. 获取百度语音识别的access token
  9. baidu_token = get_access_token()
  10. print('Baidu access token obtained.')
  11. # 4. 上传录音文件并进行语音识别
  12. baidu_result = BaiduYuYin('user_audio.wav', baidu_token)
  13. print('Baidu speech recognition result:', baidu_result)
  14. # 5. 调用大语言模型进行文本生成
  15. model_response = get_completion(baidu_result)
  16. print('Model response:', model_response)
  17. # 6. 将文本转换为语音,保存到唯一的文件名
  18. unique_audio_filename = str(uuid.uuid4()) + '.mp3' # 保存为不同的文件名以避免访问冲突
  19. asyncio.run(generate_audio_from_text(model_response,unique_audio_filename))
  20. # 7. 播放生成的语音
  21. play_mp3(unique_audio_filename)
  22. # 8. 提示用户继续对话或退出
  23. user_input = input('继续对话或输入"退出"退出: ')
  24. if user_input == '退出':
  25. break

注意,为了避免多轮对话产生的文件访问冲突,请为TTS转换后的音频文件设置不同的文件名,这里使用了uuid库为每个音频文件生成唯一的文件名。

 运行结果:

 完整代码:

  1. import pyaudio
  2. import wave
  3. import requests
  4. import json
  5. import base64
  6. import os
  7. import edge_tts
  8. import asyncio
  9. import pygame
  10. import openai
  11. import uuid # 用于生成唯一的文件名
  12. #1.录音
  13. #用Pyaudio录制音频(生成wav文件)
  14. def audio_record(rec_time,filename):
  15. """
  16. :param rec_time : 音频录制时间
  17. :param filename : 输出音频文件
  18. :返回值:在当前目录输出一个音频文件
  19. """
  20. CHUNK=1024 #定义数据流块
  21. FORMAT = pyaudio.paInt16 #16bit编码格式
  22. CHANNELS = 1 #单声道
  23. RATE = 16000 #16000采样频率
  24. #创建一个音频对象
  25. p = pyaudio.PyAudio()
  26. #创建音频数据流
  27. stream = p.open(format=FORMAT,
  28. channels=CHANNELS,
  29. rate=RATE,
  30. input=True,
  31. frames_per_buffer=CHUNK)
  32. print('Start recording...')
  33. frames=list() #空列表用于保存录制的音频流
  34. #录制音频数据
  35. for i in range(0,int(RATE/CHUNK*rec_time)):
  36. data=stream.read(CHUNK)
  37. frames.append(data)
  38. #录制完成
  39. # print(frames)
  40. #停止数据流
  41. stream.stop_stream()
  42. stream.close()
  43. #关闭pyaudio
  44. p.terminate()
  45. print('recording done...')
  46. #保存音频文件
  47. with wave.open(filename,'wb') as f:
  48. f.setnchannels(CHANNELS) #设置音频声道数
  49. f.setsampwidth(p.get_sample_size(FORMAT)) #以字节为样本返回样本宽度
  50. f.setframerate(RATE) #设置采样频率
  51. f.writeframes(b''.join(frames))
  52. f.close()
  53. #2 获取token
  54. API_KEY = "XXX" # 这里请替换为你的API_KEY
  55. SECRET_KEY = "XXX" # 这里请替换为你的SECRET_KEY
  56. def get_access_token():
  57. """
  58. 使用 AK,SK 生成鉴权签名(Access Token)
  59. :return: access_token,或是None(如果错误)
  60. """
  61. url = "https://aip.baidubce.com/oauth/2.0/token"
  62. params = {"grant_type": "client_credentials", "client_id": API_KEY, "client_secret": SECRET_KEY}
  63. return str(requests.post(url, params=params).json().get("access_token"))
  64. # 3.上传录音文件
  65. def BaiduYuYin(file_url,token):
  66. """
  67. :param file_url: 录音文件路径
  68. :param token: 获取的access token
  69. :return: 录音识别出来的文本
  70. """
  71. try:
  72. RATE='16000'
  73. FORMAT='wav'
  74. CUID='rvs7K414cquxm4f62jtasIRi6iNRNXR6'
  75. DEV_PID='1536' # 普通话,支持简单的英文识别
  76. file_url=file_url
  77. token=token
  78. #以字节格式读取文件之后进行编码
  79. with open(file_url,'rb') as f:
  80. speech=base64.b64encode(f.read()).decode('utf-8')
  81. size = os.path.getsize(file_url)# 语音文件的字节数
  82. headers={'Content-Type':'application/json',
  83. 'Accept':'application/json'} # json格式post上传本地文件
  84. url='https://vop.baidu.com/server_api'
  85. data={
  86. "format":FORMAT,#格式
  87. "rate":RATE,#取样频率,固定值16000
  88. "dev_pid":DEV_PID,#语音识别类型
  89. "speech":speech,#本地语音文件的二进制数据,需要进行base64编码
  90. "cuid":CUID,#用户唯一标识,用来区分用户 建议填写能区分用户的机器MAC地址或IMEI码,长度为60字符以内。
  91. "len":size,#语音文件的字节数
  92. "channel":1,#声道数,仅支持单声道,固定值为1
  93. "token":token,
  94. }
  95. req=requests.request("POST",url,data=json.dumps(data),headers=headers) #request.post 改为requests.request("POST"……)
  96. data_dict=json.loads(req.text)
  97. # print(data_dict['result'][0])
  98. return data_dict['result'][0] # 返回文本
  99. except:
  100. return '识别不清楚'
  101. # 4.接入大语言模型
  102. from dotenv import load_dotenv, find_dotenv
  103. _ = load_dotenv(find_dotenv()) # read local .env file
  104. os.environ['OPENAI_API_KEY'] = 'EMPTY'
  105. os.environ['OPENAI_API_BASE'] = 'http://localhost:8000/v1'
  106. openai.api_key = 'none'
  107. openai.api_base = 'http://localhost:8000/v1'
  108. def get_completion(prompt, model="gpt-3.5-turbo"):
  109. """
  110. :param prompt:输入提示词
  111. :param model:模型名称(使用默认参数即可)
  112. :return: 大模型的回复文本
  113. """
  114. messages = [{"role": "user", "content": prompt}]
  115. response = openai.ChatCompletion.create(
  116. model=model,
  117. messages=messages,
  118. temperature=0,
  119. )
  120. return response.choices[0].message["content"]
  121. # 5.文本转语音TTS:edge-tts
  122. async def generate_audio_from_text(text,file_url):
  123. """
  124. :param text:需要进行转换的文本
  125. :file_url:转换后输出的音频文件地址
  126. :return:无
  127. """
  128. voice = 'zh-CN-YunxiNeural'
  129. output = file_url
  130. rate='-4%'
  131. volume = '+0%'
  132. tts = edge_tts.Communicate(text=text,voice=voice,rate=rate,volume=volume)
  133. await tts.save(output)
  134. # 6.播放音频文件:pygame
  135. def play_mp3(mp3_file):
  136. """
  137. :param mp3_file:需要播放的录音文件地址
  138. :return:无
  139. """
  140. pygame.init() # 初始化pygame
  141. pygame.mixer.init() # 初始化音频混合器
  142. pygame.mixer.music.load(mp3_file) # 加载指定MP3文件
  143. pygame.mixer.music.play() # 播放
  144. clock = pygame.time.Clock()
  145. while pygame.mixer.music.get_busy(): # 使用一个循环来等待音频播放完毕,保证程序不会在播放结束前退出
  146. clock.tick(3)
  147. def main():
  148. while True:
  149. # 1. 提示用户发言
  150. print('请发言,谢谢!')
  151. # 2. 录制音频
  152. audio_record(5, 'user_audio.wav')
  153. print('Audio recording complete.')
  154. # 3. 获取百度语音识别的access token
  155. baidu_token = get_access_token()
  156. print('Baidu access token obtained.')
  157. # 4. 上传录音文件并进行语音识别
  158. baidu_result = BaiduYuYin('user_audio.wav', baidu_token)
  159. print('Baidu speech recognition result:', baidu_result)
  160. # 5. 调用大语言模型进行文本生成
  161. model_response = get_completion(baidu_result)
  162. print('Model response:', model_response)
  163. # 6. 将文本转换为语音,保存到唯一的文件名
  164. unique_audio_filename = str(uuid.uuid4()) + '.mp3' # 保存为不同的文件名以避免访问冲突
  165. asyncio.run(generate_audio_from_text(model_response,unique_audio_filename))
  166. # 7. 播放生成的语音
  167. play_mp3(unique_audio_filename)
  168. # 8. 提示用户继续对话或退出
  169. user_input = input('继续对话或输入"退出"退出: ')
  170. if user_input == '退出':
  171. break
  172. if __name__ == "__main__":
  173. main()

 局限性

  1. ChatGLM2-6B的api存在局限性,调用此api只能一问一答,没有记忆性。
  2. api不能部署到公网上,只能本地访问。运行上面的代码之前,一定要先ssh到运行大模型的服务器上。

结语

看到了这里,你一定是个热爱学习编程的极客,令人钦佩。在这个知识无边界的时代,你的点赞和收藏是我创作的最大动力。让我们携手前行,探索更多的学习和创新,为共同的热爱努力,因为在知识的海洋里,我们永不止步,共同谱写着学习的精彩篇章。感谢你的支持!点赞、收藏!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/678473
推荐阅读
相关标签