从chatGPT到语音回答雏形的python实现

Posted lys1335

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从chatGPT到语音回答雏形的python实现相关的知识,希望对你有一定的参考价值。

从玩玩chatGPT说起

起因

最近,哦,已经不是最近了,挺长一段时间了,chatGPT火了。出于好奇,注册了账号,而且尝试与其文字对话,询问问题,还有在工作中让其帮忙翻译以及进行语言学习,发现----chatGPTj是一位好老师。虽然这个老师有时候会一本正经地胡说八道,但综合下来,百分之九十以上可以给出比较靠谱回答的。而且其回答的方式很亲和,即使出错,也可以引导其给出正确回答,确实是目前为止非常高级的AI了。不满足于只是文字交流,在想是否可以通过语音交流呢?(由于本人一直活在小世界孤陋寡闻应该早已有此实现,见谅!)恰好又刷 到一个视频,大致内容是某位大神通过树莓派实现了自己的语音助手。于是有此次尝试,希望在没有硬件的前提下考虑实现。

想法

想法其实很简单,如视频中大神的想法。语音唤醒——语音识别——发送识别文本到openai的API并取得文字回答内容——文本转语音播报。本身各部分实现都有现成的接口了应该,考虑胶水语言python完成。

先文本转语音播报

第一步想到的先从文本转语音播放开始。为什么从此步开始而不是语音识别呢?主要是我觉得这一步最简单吧。具体实现代码呢,拼拼凑凑,不会的就问chatGPT,再次赞赏一下这个好老师。完成函数方法实现。

# 该函数用于实现语音转文字并播报
def say_txt_as_audio(text):
    directory = "./chat-content"
    if not os.path.exists(directory):
        os.makedirs(directory)

    text = text.strip()
    if len(text) > 0:
        print("AI:" + text)
        # 将参数传送过来的txt内容转换为语音保存为mp3文件
        now = datetime.now()
        save_name = now.strftime("%Y%m%d%H%M%S.mp3")
        tts = gTTS(text, lang='zh-cn')
        tts.save("chat-content/" + save_name)

        # 通过pygame模块播放保存的mp3文件
        pygame.init()
        pygame.mixer.music.load("chat-content/" + save_name)
        pygame.mixer.music.play()
        while pygame.mixer.music.get_busy():
            pygame.time.wait(1000)
        pygame.quit()

这部分代码有使用pygame,所以不能忘了import pygame。另外由于执行的时候会有部分无关的文字输出到控制台,所以可能会像我一样修改环境设定。主要就是在import pygame之前有如下两行。

from os import environ
environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1'  # pygame导入前此参数设定用于限制pygame的无关内容输出

经执行确认,可以正常将指定文字内容转成mp3文件,并即时播报。
mp3文件名为年月日进分秒。
对了,还有gtts的导入。from gtts import gTTS。

再来语音转文本

这一步就是把上边功能的文本变量的值的来源变成我们自己说的话,只要把我们说话的语音变成文本赋给这个变量就可以衔接上边功能了。嗯,就是这样。感谢google及chatGPT,不费时的就完成了。

# 该函数用于实现将语音的输入转换为文字
def convert_audio_to_text():
    # 创建麦克风的识别器对象
    r = sr.Recognizer()

    # 打开麦克风并录制音频
    with sr.Microphone() as source:
        print("我在听,你现在可以开始说话啦!~~")
        audio = r.listen(source)

    # 识别语音
    text = ""
    try:
        text = r.recognize_google(audio, language='zh-CN', show_all=False)
        print("你:" + text)
        with open("chatHistory.txt", "a", encoding="utf-8") as file:
            file.write("你:" + text)
    except Exception as e:
        print("没听到,无法识别:" + str(e))
    return text

至于代码中的sr当然是speech_recognition了,所以import部分记得添加

import speech_recognition as sr 

就好了。由于想要将对话内容输出打印到控制台,且保存到文本文档中。所以代码中有print与write动作。

连接AI接口

了解到目前chatGPT并未开放API,所以只能使用opanai的API,貌似连接的是达芬奇3机器人,后来尝试确实不如在线与chagGPT沟通来得舒服。
首先要取得API key,可以在网站上点击获得。代码中有用到。

# 将指定内容发送给AI并取得回答返回
def get_questions_ai_answer(question):
    ai_answer_content = ""
    if len(question) > 0:
        openai.api_key = ""
        with open("OpenAiApiKey", 'r') as f:
            api_key = f.read().strip()
            openai.api_key = api_key

        response = openai.Completion.create(
            model="text-davinci-003",
            prompt=question,
            temperature=0.9,
            max_tokens=2048, #150 OpenAI的API实现中,max_tokens参数的上限值为4096。
            top_p=1,
            frequency_penalty=0.0,
            presence_penalty=0.6,
            stop=[" Human:", " AI:"]
        )
        response_result_text = response.get("choices")[0].get("text")
        ai_answer_content = response_result_text
        with open("chatHistory.txt", "a", encoding="utf-8") as file:
            file.write("\\nAI:" + ai_answer_content.strip() + "\\n")
    return ai_answer_content

此部分用于连接前边的两步实现。即接收语音转文字的结果,发给openai再将回答结果传给文字转语音的实现部分。
开始询问chatGPT给出的示例代码中max_tokens设置为150。这样的情况下,当AI回答你问题的输出内容过多时,会中断,也就是说半句话就戛然而止了,经询问chatGPT,可调引参数,了解到上限是4096,所以改150到了2048。
至于OpenAiApiKey文件中存放的内容,就是在openai网站上取得的key值了。不公开了。

再来添加唤醒

上面三步串起来,基本可以完成从说话到ai语音回答了。但是每次都是单个python文件执行才能开始说一回。所以想到还是得加上语音唤醒才可以。不表经过,选了pocketsphinx,网上说要什么sphinxbase就是这一步折腾了挺长时间,不过后来发现纯粹是语音模型配置的路径不对而已。所以,我猜现在导入pocketsphinx库就可以了,当然swig库也加入到工程了,以防万一。

import os
import openai
import speech_recognition as sr
from gtts import gTTS
from datetime import datetime, timedelta
from os import environ

environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1'  # pygame导入前此参数设定用于限制pygame的无关内容输出
import pygame
from pocketsphinx import LiveSpeech, get_model_path

model_path = get_model_path()

# 创建语音识别对象,用于监听说话,以备唤醒
speech = LiveSpeech(
    verbose=False,
    sampling_rate=16000,
    buffer_size=2048,
    no_search=False,
    full_utt=False,
    #hmm=os.path.join(model_path, 'en-us', 'en-us'),
    #lm=os.path.join(model_path, 'en-us', 'en-us.lm.bin'),
    #dic=os.path.join(model_path, 'en-us', 'cmudict-en-us.dict')
    # 此处切用网上下来的中文语音模型
    hmm = os.path.join(model_path, 'cmusphinx-zh-cn-5.2', 'zh_cn.cd_cont_5000'),
    lm = os.path.join(model_path, 'cmusphinx-zh-cn-5.2', 'zh_cn.lm.bin'),
    dic = os.path.join(model_path, 'cmusphinx-zh-cn-5.2', 'zh_cn.dic')
)

上面代码的Import部分,包括前面几步的import方便就一齐粘过来了。主要是构建了speech对象,但是还没使用,所以还要有下面一部分

# 开始语音识别
for phrase in speech:
    print("识别到的语音内容:", phrase)
    # 这里再执行唤醒后的动作

空间考虑

考虑到MP3文件存放大小的问题,又是以年月日时分秒命名的mp3文件,所以干脆每次问答的时候都删除几分种以前的文件好了。所以添加了一个文件删除的方法。

# 为了节省磁盘占用空间,删除指定分钟数以前的mp3文件。在每次执行前开始算起。
def delete_mp3_files_several_minutes_ago(minute):
    now = datetime.now()
    several_minutes_ago = now - timedelta(minutes=minute)
    several_minutes_ago_str = several_minutes_ago.strftime("%Y%m%d%H%M%S")

    for filename in os.listdir("chat-content/"):  # 遍历该路径下的所有文件
        if not filename.endswith('.mp3'):  # 如果不是mp3文件,跳过
            continue
        else:
            file_time = filename[:-4]  # 取出文件名即时间
            if file_time < several_minutes_ago_str:
                # 如果该文件是指定分钟数以前的,删除
                os.remove(os.path.join("chat-content/", filename))

至于保存的txt文档一直在变大的问题,其实也可以判断大小清除就好啦,毕竟是文本内容,暂不考虑,暂不实现。

串成线

前面几个基本可以串起来了,在语音唤醒之后的动作部分把几个函数方法体串起来就可以了。

# 开始语音识别
for phrase in speech:
    print("识别到的语音内容:", phrase)
    word_heared = str(phrase)
    if word_heared == "如果":
        print("我被唤醒了,您请说话!")
        # 语音识别人类说话
        you_said_txt = convert_audio_to_text()
        # 调用AI的API将语音识别内容发送,并将回答取回
        ai_answer_text = get_questions_ai_answer(you_said_txt)
        # 语音播报AI的回答
        say_txt_as_audio(ai_answer_text)
        # 为了不长期占用磁盘空间,只保存当前时间算起几分钟以内的mp3音频文件,其他生成的mp3文件进行删除
        delete_mp3_files_several_minutes_ago(3)

上面把关键词设成了【如果】,至于为什么设成如果,是因为这个模型的错误率实在太高了,试了几个词这个词相对能听清,测试嘛,就用它了。整体执行下来还可以。

问题与想法

整体跑起来,主要有两点问题,一个是响应速度,在询问问题后到播放出语音来间隔时间有点长,再琢磨吧。再有一个问题就是这个唤醒的错误率太高了,我说了好多次【如果】它才有一次识别对了。这个貌似可以通过更换模型来解决。可模型查了查,不好搞,暂先搁置吧。
还有一点想法,可以整出一个应用界面来,就一个钮,点击可以问ai问题,看文字回答并收听语音朗读。或者还可以进行设置,达到实时监听唤醒词。当然可能会考虑更多东西才可以。这两天摸鱼,搞了这么个东西,聊以记录一下。
PS:翻出了几年前获赠的google home mini,原来只当个小音箱,现在忽然想想是否可以让google assistant 连openai呢。由于openai是预训练模型能不能实时与assistant切换呢。在Actions on Google上可以搞定的么,有空再研究一下。

初探人工智能ChatGPT2雏形开始长成

(【初探人工智能ChatGPT】2、雏形开始长成)

【初探人工智能ChatGPT】2、雏形开始长成

这篇文章主要介绍如何打造一个基于Web的人工智能对话环境。

安装Flask

Flask是一个Python编写的Web 微框架,让我们可以使用Python语言快速实现一个网站或Web服务。我们可以通过Flask将聊天功能封装成Web接口对外发布。

要使用Flask,需要先安装,执行命令:

pip install flask

安装过程:

(OpenAI) wux_labs@wux-labs-vm:~$ pip install flask
Collecting flask
  Downloading Flask-2.2.3-py3-none-any.whl (101 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 101.8/101.8 kB 1.2 MB/s eta 0:00:00
Collecting click>=8.0
  Downloading click-8.1.3-py3-none-any.whl (96 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 96.6/96.6 kB 2.7 MB/s eta 0:00:00
Collecting Jinja2>=3.0
  Downloading Jinja2-3.1.2-py3-none-any.whl (133 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 133.1/133.1 kB 9.7 MB/s eta 0:00:00
Collecting importlib-metadata>=3.6.0
  Downloading importlib_metadata-6.0.0-py3-none-any.whl (21 kB)
Collecting Werkzeug>=2.2.2
  Downloading Werkzeug-2.2.3-py3-none-any.whl (233 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 233.6/233.6 kB 8.3 MB/s eta 0:00:00
Collecting itsdangerous>=2.0
  Downloading itsdangerous-2.1.2-py3-none-any.whl (15 kB)
Collecting zipp>=0.5
  Using cached zipp-3.13.0-py3-none-any.whl (6.7 kB)
Collecting MarkupSafe>=2.0
  Downloading MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (25 kB)
Installing collected packages: zipp, MarkupSafe, itsdangerous, click, Werkzeug, Jinja2, importlib-metadata, flask
Successfully installed Jinja2-3.1.2 MarkupSafe-2.1.2 Werkzeug-2.2.3 click-8.1.3 flask-2.2.3 importlib-metadata-6.0.0 itsdangerous-2.1.2 zipp-3.13.0
(OpenAI) wux_labs@wux-labs-vm:~$ 

安装好Flask之后,写一个脚本server.py验证一下。

from flask import Flask
app = Flask(__name__)

@app.route(/)
def hello_world():
   return Hello World

if __name__ == __main__:
   app.run()

启动服务:

python server.py

验证一下:

但是这样只能本地访问,无法外网访问。Flask类的run()方法可以指定参数,让服务按照我们的预期运行,这里需要指定外网可以访问。修改一下代码:

from flask import Flask
app = Flask(__name__)

@app.route(/)
def hello_world():
   return Hello World

if __name__ == __main__:
   app.run(host=0.0.0.0)

重新启动后,通过浏览器访问。

这样就可以通过外网访问了。

封装Web接口雏形

现在,我们可以将自己的功能封装成Web接口了,修改脚本:

from flask import Flask
import os
import openai

openai.api_key = os.getenv("openai_key")

app = Flask(__name__)

@app.route(/chatgpt)
def chatgpt():
    response = openai.Completion.create(
        model="text-davinci-003", # 最强大的GPT-3模型,This models maximum context length is 4097 tokens
        prompt="介绍一下机器学习算法",
        temperature=0.8,
        max_tokens=3000,
        top_p=1.0,
        frequency_penalty=0.5,
        presence_penalty=0.0
    )
    return response.choices[0].text

if __name__ == __main__:
    app.run(host=0.0.0.0)

重新启动之后,通过浏览器访问一下:

这样,我们就可以通过Web接口来调用相应的API了。

设置接收参数

接下来,修改我们的Web接口,让它可以接收用户参数。

from flask import Flask, request
import os, json
import openai

openai.api_key = os.getenv("openai_key")

app = Flask(__name__)

@app.route(/chatgpt, methods=[post])
def chatgpt():
    get_data = request.get_data()
    get_data = json.loads(get_data)

    response = openai.Completion.create(
        model=get_data["model"],
        prompt=get_data["prompt"],
        temperature=get_data["temperature"],
        max_tokens=get_data["max_tokens"],
        top_p=get_data["top_p"],
        frequency_penalty=get_data["frequency_penalty"],
        presence_penalty=get_data["presence_penalty"],
    )
    return response.choices[0].text

if __name__ == __main__:
    app.run(host=0.0.0.0)

功能验证

由于我们将接口改成了接收POST请求的,所以不能直接通过浏览器访问了,需要借助客户端工具,比如Postman、PyCharm中的Http Request插件等。

聊天

基于上述代码,发起POST请求,使用text-davinci-003模型,得到响应如下。

写代码

使用text-davinci-003模型,让机器人生成一段代码试试。

代码补全

尝试一下其他模型,比如code-davinci-002,该模型可用于补全代码,不过当前处于beta阶段。发起POST请求,补全一段Python代码中的测试用例代码,输出的内容为:

test_sum_numbers():
assert sum_numbers(2, 3) == 5
assert sum_numbers(1, -1) == 0
assert sum_numbers(10.5, 2) == 12.5
test_sum_numbers()
# 测试错误的函数:
def test_sum_numbers():
assert sum_numbers(2, 3) == 6 # 这个测试会失败
test_sum_numbers()


# 单元测试中的断言函数:assertEqual()、assertTrue()、assertFalse()……以及方法还有很多。你可以在文档中查看所有的断言函数。

上述代码只有openai生成的部分。

如果是在交互式环境下,真实场景应该是在代码后面进行补全:

生成图片

修改一下脚本,在代码中添加生成图片的接口:

from flask import Flask, request
import os, json
import openai

openai.api_key = os.getenv("openai_key")

app = Flask(__name__)

@app.route(/chat, methods=[post])
def chat():
    get_data = request.get_data()
    get_data = json.loads(get_data)

    response = openai.Completion.create(
        model=get_data["model"],
        prompt=get_data["prompt"],
        temperature=get_data["temperature"],
        max_tokens=get_data["max_tokens"],
        top_p=get_data["top_p"],
        frequency_penalty=get_data["frequency_penalty"],
        presence_penalty=get_data["presence_penalty"],
    )
    return response.choices[0].text

@app.route("/image", methods=[post])
def image():
    get_data = request.get_data()
    get_data = json.loads(get_data)

    response = openai.Image.create(
        prompt=get_data["prompt"],
        n=1,
        size="1024x1024"
    )
    return response[data][0][url]

if __name__ == __main__:
    app.run(host=0.0.0.0)

重启服务后发送POST请求,生成的图片结果如下。

写在后面

至此,我们的机器人就具备了一些基本的功能了,后续做好用户界面就可以了。

以上是关于从chatGPT到语音回答雏形的python实现的主要内容,如果未能解决你的问题,请参考以下文章

初探人工智能ChatGPT2雏形开始长成

初探人工智能ChatGPT2雏形开始长成

逐句回答,流式返回,ChatGPT采用的Server-sent events后端实时推送协议Python3.10实现,基于Tornado6.1

使用 Open AI 的 ChatGPTWhisperPython 和 Gradio 创建智能语音助手 | Python项目

ChatGPT-语音助手

ChatGPT爆火!它如何回答 Python 相关问题