同一Python脚本中使用VOSK与Resemblyzer致电脑崩溃的优化求助
问题描述
尝试用VOSK实现语音转写、Resemblyzer实现说话人分群,组合完成音频转写+说话人识别功能。单独运行两个工具均正常,但在同一Python脚本中执行时电脑完全冻结,仅IDE界面卡死,需长按电源键强制重启,无蓝屏或黑屏现象。
使用工具
- VOSK
- Resemblyzer
电脑配置
Intel(R) Core(TM) i3-7100 CPU @ 3.90GHz,3912MHz,2核4线程,32GB内存
问题代码
from vosk import Model, KaldiRecognizer from pydub import AudioSegment import json import sys import os import subprocess import datetime from resemblyzer import preprocess_wav, VoiceEncoder from pathlib import Path from resemblyzer.hparams import sampling_rate from spectralcluster import SpectralClusterer import threading import queue import gc def recognition(queue, audio, FRAME_RATE): model = Model("Vosk_Models/vosk-model-small-en-us-0.15") rec = KaldiRecognizer(model, FRAME_RATE) rec.SetWords(True) rec.AcceptWaveform(audio.raw_data) result = rec.Result() transcript = json.loads(result)#["text"] #return transcript queue.put(transcript) def diarization(queue, audio): wav = preprocess_wav(audio) encoder = VoiceEncoder("cpu") _, cont_embeds, wav_splits = encoder.embed_utterance(wav, return_partials=True, rate=16) print(cont_embeds.shape) clusterer = SpectralClusterer( min_clusters=2, max_clusters=100, p_percentile=0.90, gaussian_blur_sigma=1) labels = clusterer.predict(cont_embeds) def create_labelling(labels, wav_splits): times = [((s.start + s.stop) / 2) / sampling_rate for s in wav_splits] labelling = [] start_time = 0 for i, time in enumerate(times): if i > 0 and labels[i] != labels[i - 1]: temp = [str(labels[i - 1]), start_time, time] labelling.append(tuple(temp)) start_time = time if i == len(times) - 1: temp = [str(labels[i]), start_time, time] labelling.append(tuple(temp)) return labelling #return labelling = create_labelling(labels, wav_splits) queue.put(labelling) def identify_speaker(queue1, queue2): transcript = queue1.get() labelling = queue2.get() for speaker in labelling: speakerID = speaker[0] speakerStart = speaker[1] speakerEnd = speaker[2] result = transcript['result'] words = [r['word'] for r in result if speakerStart < r['start'] < speakerEnd] #return print("Speaker",speakerID,":",' '.join(words), "\n") def main(): queue1 = queue.Queue() queue2 = queue.Queue() FRAME_RATE = 16000 CHANNELS = 1 podcast = AudioSegment.from_mp3("Podcast_Audio/Film-Release-Clip.mp3") podcast = podcast.set_channels(CHANNELS) podcast = podcast.set_frame_rate(FRAME_RATE) first_thread = threading.Thread(target=recognition, args=(queue1, podcast, FRAME_RATE)) second_thread = threading.Thread(target=diarization, args=(queue2, podcast)) third_thread = threading.Thread(target=identify_speaker, args=(queue1, queue2)) first_thread.start() first_thread.join() gc.collect() second_thread.start() second_thread.join() gc.collect() third_thread.start() third_thread.join() gc.collect() # transcript = recognition(podcast,FRAME_RATE) # # labelling = diarization(podcast) # # print(identify_speaker(transcript, labelling)) if __name__ == '__main__': main()
优化方案与问题修复
核心问题分析
- 线程逻辑冗余:代码创建线程后通过
join()强制串行执行,既没用到多线程并行优势,还增加了线程调度开销。 - 内存占用过高:
AudioSegment对象重复传递可能导致多份音频数据副本;模型在函数内重复初始化,浪费内存。 - 音频处理不合理:一次性传入全部音频数据到VOSK,容易造成内存峰值。
优化后的代码
from vosk import Model, KaldiRecognizer from pydub import AudioSegment import json from resemblyzer import preprocess_wav, VoiceEncoder from resemblyzer.hparams import sampling_rate from spectralcluster import SpectralClusterer import gc # 全局初始化模型,避免重复加载 vosk_model = Model("Vosk_Models/vosk-model-small-en-us-0.15") voice_encoder = VoiceEncoder("cpu") def recognition(audio, frame_rate): rec = KaldiRecognizer(vosk_model, frame_rate) rec.SetWords(True) # 分块处理音频,降低内存峰值 chunk_size = 4000 for i in range(0, len(audio), chunk_size): chunk = audio[i:i+chunk_size] rec.AcceptWaveform(chunk.raw_data) result = rec.Result() return json.loads(result) def diarization(audio): wav = preprocess_wav(audio) _, cont_embeds, wav_splits = voice_encoder.embed_utterance(wav, return_partials=True, rate=16) clusterer = SpectralClusterer( min_clusters=2, max_clusters=100, p_percentile=0.90, gaussian_blur_sigma=1) labels = clusterer.predict(cont_embeds) def create_labelling(labels, wav_splits): times = [((s.start + s.stop) / 2) / sampling_rate for s in wav_splits] labelling = [] start_time = 0 for i, time in enumerate(times): if i > 0 and labels[i] != labels[i - 1]: labelling.append((str(labels[i-1]), start_time, time)) start_time = time if i == len(times) - 1: labelling.append((str(labels[i]), start_time, time)) return labelling return create_labelling(labels, wav_splits) def identify_speaker(transcript, labelling): for speaker_id, start, end in labelling: words = [r['word'] for r in transcript['result'] if start < r['start'] < end] print(f"Speaker {speaker_id}: {' '.join(words)}\n") def main(): FRAME_RATE = 16000 CHANNELS = 1 # 仅加载一次音频并预处理 podcast = AudioSegment.from_mp3("Podcast_Audio/Film-Release-Clip.mp3") podcast = podcast.set_channels(CHANNELS).set_frame_rate(FRAME_RATE) # 串行执行适配2核CPU,避免资源耗尽 transcript = recognition(podcast, FRAME_RATE) gc.collect() # 主动释放识别阶段临时内存 labelling = diarization(podcast) gc.collect() # 主动释放分群阶段临时内存 identify_speaker(transcript, labelling) if __name__ == '__main__': main()
关键优化点说明
- 全局模型初始化:VOSK和Resemblyzer模型只加载一次,减少内存占用和重复初始化时间。
- 音频分块处理:VOSK识别时分块传入音频,降低内存峰值。
- 移除冗余线程:针对2核CPU,串行执行避免资源竞争,减少线程调度开销。
- 主动内存回收:完成阶段性任务后调用
gc.collect(),及时释放无用内存。 - 代码简化:去掉不必要的队列和线程,逻辑更清晰,减少潜在同步问题。
额外建议
- 若音频文件过大,可切割为小片段分批处理,进一步降低内存压力。
- 监控CPU和内存占用,排查是否存在内存泄漏或过载情况。
- 确认VOSK模型路径正确,避免加载时的隐性错误。
内容的提问来源于stack exchange,提问作者SL1997
相关产品推荐
相关产品推荐

