虚拟女友初步调研总结2024-0212(转载)
发布日期:2024/2/19 18:58:15 浏览量:
虚拟女友初步调研总结2024-0212(转载)
转自:https://zhuanlan.zhihu.com/p/682012849
整个AI生成领域的变化,已经非常恐怖了,呈现出一副勃勃生机,万物竞发的状态。
- 文本生成和对话领域,有Gemini-PRO,GPT4为代表的大语言模型,相当于有了大脑;
- 语音识别领域,有openai的whisper、阿里的funasr为代表的(Speech Recontion)语音识别模型,有了听觉。
- 视觉识别这块,有Gemini-PRO的免费api可以白嫖,可以直接处理文本+图片信息,可以说是 开天眼了。
- 再然后就是前段时间看到花儿不哭大佬的新作品:GPT-sovits,这东西可以只用一分钟的音频,做few-shot训练,然后就能直接拿到参考音频的音色,毫不夸张的说,只需要一分钟的录音,就能偷掉别人的嗓子!霍金来了都得点赞。
- 最后是数字人视频生成,这个对我来说是一个全新的领域,我也只是浅尝了一下,快速做一个记录,和大家一起学习。我主要使用的是Linly-Talker。
- demo视频:【赛博鹰酱给您拜年了】 赛博鹰酱给您拜年了_哔哩哔哩_bilibili
趁着过年这段时间比较浮躁,快速体验了一下这个功能,希望为后面的智能小车,以及最终的虚拟女友,做一个技术铺垫。
由于文本生成和视觉识别都是调用api,所以接下来主要介绍语音识别、声音复刻和数字人生成这三个部分。
1. 语音识别:whisper or FunASR?
openai的whisper
后面使用的是openai 开源的whisper,网上大家都说它是最强语音识别模型。whisper有三种调用方式,做一下总结:
- api调用:对于临时使用、长文本、不用即时对话的场景,用免费的3.5api,就可以直接调用,国内延迟比较高,差不多得3-18秒,才能完成一次识别,非常不稳定,不适合实时对话。另外中文识别效果一言难尽,得字正腔圆的普通话才行,甚至于有时候还会生成繁体字。不知道氪金用户,或者用azure的api会不会好些。
- 官网开源的whisper:官网的whisper本地部署,这个要求你得有至少8Gb的显存,我自己用的4060显卡,勉强可以用medium,推理速度比较稳定,大概是2秒左右,识别2秒的音频。缺点是一样的,中文识别效果不是很好,错字、标点乱打、繁体。
- fast-whisper:这个项目我得贴一下链接:https://github.com/SYSTRAN/faster-whisper, 它大大降低了对显存的要求!
另外,需要提一句的是,large-v2的识别效果一般会比v3更好,所以如果大家要是用whisper的格式,推荐使用fast-whisper:large-v2。
1.1 关于语音识别的自动分段:
在连续对话的逻辑中,不得不提的是一个技术是"语音活性检测 (Voice activity detection,VAD)",即当一个正在录音的循环中,怎么判断有人声,怎么判断,这段话说完了,开始进入对话的逻辑?
好消息是fast whisper仓库自带vad检测模块,因此,只需要做一个集成封装就好。我的主要工作集中在:
- 采集音频信号
- 判断是否有人声,调用vad模块,可以直接实时判断当前音频是否有人声。
- 判断是否是一个完整的对话,这里的思路比较抽象
- 提取完整输入音频之后,保存到本地,然后调用本地的whisper模型,进行语音识别,获取文本;
- 将文本+Prompt,输入给GPT3.5,拿到对话文本;
- 将对话文本输入给GPT3.5的tts模块,拿到回答音频文件;
- 播放回复音频。
这里贴一下我的调用示例代码,也算是开源了,如果有帮助,恳请帮忙给帖子点个赞吧:
# 需要在fast-whipsper工作目录中运行。
import pyaudio
import wave
import time
import tenacity
import openai
import random
import re
from openai import OpenAI
import openai
import numpy as np
from faster_whisper import WhisperModel
import pyaudio # 导入PyAudio库,用于音频录制和播放
import pygame
from faster_whisper.vad import get_vad_model
chatpaper_keys = """
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
"""
chatpaper_keys = chatpaper_keys.strip()
chatpaper_keys_list = re.findall(r’sk-\w+’, chatpaper_keys)
def play_mp3(file_path):
pygame.mixer.init()
pygame.mixer.music.load(file_path)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy(): # Wait for music to finish playing
pygame.time.Clock().tick(10)
# 停止混音器
pygame.mixer.music.stop()
# 卸载当前加载的音乐流,释放资源
pygame.mixer.music.unload()
# 退出混音器
pygame.mixer.quit()
def get_tts(client, input_text, output_path="output.mp3"):
response = client.audio.speech.create(
model="tts-1",
voice="nova",
input=input_text,
)
st = time.time()
response.stream_to_file(output_path)
print("save audio time:", time.time()-st)
@tenacity.retry(wait=tenacity.wait_exponential(multiplier=1, min=4, max=10),
stop=tenacity.stop_after_attempt(8),
reraise=True)
def get_response(client, robot_state=’’, user_task=’’):
openai.api_key = random.choice(chatpaper_keys_list)
st = time.time()
# 限制info和prompts的长度
robot_state = robot_state[:400]
user_task = str(user_task)[:600]
messages = [
{"role": "system", "content": "你是一个对话机器人,你需要根据用户的问题回答."},
{"role": "assistant", "content": ""},
{"role": "user", "content": f"""
根据用户的命令,简洁回答问题。
robot_state: {robot_state},
user_task: {user_task}.
output format:
你好主人:xxx(具体回复).
"""},
]
api = "gpt-3.5-turbo"
response = client.chat.completions.create(
model=api,
messages=messages,
temperature=0.0,
)
result = ’’
for choice in response.choices:
result += choice.message.content
return result
class VoiceRecorder:
def __init__(self, audio_path=’mic_output.wav’):
# 音频参数
self.format = pyaudio.paInt16
self.channels = 1
self.audio = pyaudio.PyAudio()
self.frames = []
self.audio_path = audio_path
# Run on GPU with FP16
model_size = "large-v2"
self.model = WhisperModel(model_size, device="cuda", compute_type="float16")
self.history = []
def start_recording(self):
# 打开录音流
self.stream = self.audio.open(format=self.format, channels=self.channels,
rate=self.rate, input=True,
frames_per_buffer=self.chunk)
self.frames = []
print("Recording...")
def stop_recording(self):
# 停止录音
self.stream.stop_stream()
self.stream.close()
print("Finished recording.")
def play_txt(self,):
OPENAI_API_KEY = np.random.choice(chatpaper_keys_list)
client = OpenAI(api_key=OPENAI_API_KEY)
# 语音识别:
st = time.time()
segments, info = self.model.transcribe(self.audio_path, beam_size=5,
initial_prompt="你是一个对话机器人,你需要根据用户的问题,回答对应的问题.",)
print("Detected language ’%s’ with probability %f" % (info.language, info.language_probability))
for segment in segments:
print("[%.2fs -> %.2fs] %s" % (segment.start, segment.end, segment.text))
print("time:", time.time()-st)
st = time.time()
robot_state = ’默认状态’
user_task = segment.text
robot_response = get_response(client, robot_state, user_task)
print("robot_response: ", robot_response)
print("robot_response Time taken: ", time.time() - st)
# 语音合成
st = time.time()
output_path = ’output.mp3’
get_tts(client, robot_response, output_path=output_path)
print("get_tts Time taken: ", time.time() - st)
# 播放MP3文件
st = time.time()
play_mp3(output_path)
print("Time taken: ", time.time() - st)
def save_recording(self, file_name):
# 保存录音到文件
wave_file = wave.open(file_name, ’wb’)
wave_file.setnchannels(self.channels)
wave_file.setsampwidth(self.audio.get_sample_size(self.format))
wave_file.setframerate(self.rate)
wave_file.writeframes(b’’.join(self.frames))
wave_file.close()
print(f"Recording saved as {file_name}")
def find_indices(self, speech_list, time_num=2):
# 初始化索引
first_index = None
last_index = None
lst = [0 if sp < 0.5 else 1 for sp in speech_list]
# 遍历列表,找到第一个0到1的索引
for i in range(len(lst) - 1):
if lst[i] == 0 and lst[i + 1] == 1:
first_index = i + 1
break
# 如果没有找到0到1的转变,返回None
if first_index is None:
return None
# 从找到的第一个1开始,遍历列表,找到符合条件的最后一个1到0的转变
for i in range(first_index, len(lst) - 1):
if lst[i] == 1 and lst[i + 1] == 0:
# 检查是否有至少time_num个连续的0
zero_count = 0
for j in range(i + 1, len(lst)):
if lst[j] == 0:
zero_count += 1
else:
break
if zero_count >= time_num:
last_index = i
break # 可以提前结束循环,因为我们只需要最后一个符合条件的索引
# 如果没有找到符合条件的1到0的转变,或者只有0没有1,返回None
if last_index is None:
return None
# 返回第一个0到1的索引和最后一个符合条件的1到0的索引
return (first_index, last_index)
def run(self):
# 开始运行录音程序
temp = []
self.audio_rate = 16000
self.rate = 16000
self.chunk = 1024
self.stream = self.audio.open(format=self.format, channels=self.channels,
rate=self.rate, input=True,
frames_per_buffer=self.chunk)
self.vad_model = get_vad_model()
self.vad_state = self.vad_model.get_initial_state(batch_size=1)
self.frames = []
self.speech_list = []
time_sum = 0
time_count = 0
time_th = 1.0
print("Recording...")
while True:
# 进入无限录音模式:
st = time.time()
# 获取当前帧的数据,chunk=1024
data = self.stream.read(self.chunk)
audio_int16 = np.frombuffer(data, np.int16)
audio_float32 = np.array(audio_int16).astype(np.float32)/32768.0 #(num_samples,)
# 利用vad检测是否有人声的概率
speech_prob, self.vad_state = self.vad_model(audio_float32, self.vad_state, self.audio_rate)
temp.append(audio_float32)
# 判断当前帧是否有人声
is_speech = speech_prob[0][0] > 0.5
# 把所有的数据和人声判断都存到列表中。
self.frames.append(data)
self.speech_list.append(is_speech)
time_sum += time.time() - st
time_count += 1
time_mean = time_sum / time_count
time_num = time_th/(time_mean+0.0001)
# 根据人声判断来送筛选是否有人声,且人声的索引。
res = self.find_indices(self.speech_list, time_num=time_num)
if res is not None:
self.frames = self.frames[res[0]:res[1]]
file_name = self.audio_path
# 将人声对应的数据存到本地
self.save_recording(file_name)
# 和gpt对话,并且调用tts,最后播放
self.play_txt()
self.frames = []
self.speech_list = []
# 清理工作
self.audio.terminate()
print("Press and hold space to record, release to stop and save the audio. Press Esc to exit.")
recorder = VoiceRecorder()
recorder.run()
阿里的Funasr:
中间写了一个插曲,继续介绍第二个语音识别方案:阿里的funasr。
不得不说,我当初小瞧了它。但现在使用下来,funasr的优势还是很明显的:部署简单,模型小,推理速度快,对显存要求低。
最最关键的是,它对中文更友好!除非是方言和特殊的术语,基本上都能识别出来。
另外,它应该还是可以和阿里自家的emotion2vec结合起来,用于音频的情感识别,对后面的声音复刻很有用。
OK,但funasr的使用我就不多介绍了,大家直接看后面的内容就好了。(这个帖子今天晚上要写完,明天不能碰这块了,所以越写越急躁~)
声音复刻-gpt-sovits:
音色复刻,我的了解一样很少,印象中只是看过“AI孙燕姿”和“郭德纲说英文相声”,知道AI现在能做到比较恐怖的程度。但我看完花佬的新项目之后,才被深刻的震撼到,原来只需要花一两个小时的配置,加一分钟的录音,就能把别人的嗓子复刻出来,简直是太恐怖了。
我不知道之前的什么bert-sovits,甚至也不知道GPT-sovits的技术原理,我只是最简单的使用整合包,跑数据、微调模型+推理整活。
我之前玩的时候,官方仓库还有不少bug,现在大家闭眼冲官方仓库的整合包就行,
If you are a Windows user (tested with win>=10) you can install directly via the prezip. Just download the prezip, unzip it and double-click go-webui.bat to start GPT-SoVITS-WebUI.
点击prezip下载就好了,文件比较大,需要点魔法+好的网速。最好是谷歌浏览器下载,这样比较稳定。
数字人仓库:Linly-Talker
GitHub - Kedreamix/Linly-Talker
大家自己玩就好,我不介绍了,没时间了。
现在最大的问题是口型和眼神不太对。
数字人进阶方案:ER-NeRF和geneface++
参考视频:【AI 数字人制作(方案六)-哔哩哔哩】 https://b23.tv/vOk1IMg
点评:这两个是目前比较完善的开源方案,都是需要对特定个人微调,但好像存在长发乱飘的问题。
另外还有一个wav2lip的方案,值得探索。
马上咨询: 如果您有业务方面的问题或者需求,欢迎您咨询!我们带来的不仅仅是技术,还有行业经验积累。
QQ: 39764417/308460098 Phone: 13 9800 1 9844 / 135 6887 9550 联系人:石先生/雷先生