掘金 人工智能 7小时前
想要训练语音模型,如何从影视剧音频中获取到纯净的语音数据?
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文旨在探索使用WebRTCVAD、FunASR和YAMNet等模型工具处理音频数据,以期提取纯净的语音用于训练TTS模型。文章详细介绍了这些工具的功能和使用方法,包括WebRTCVAD的语音活动检测能力、FunASR的多功能语音处理(ASR、VAD、多人说话检测)以及YAMNet的声音事件分类功能。虽然这些工具为音频数据处理提供了便利,但作者也指出它们并非完全开箱即用,可能需要进行后处理或模型微调才能满足特定需求,例如YAMNet在杂音检测的准确性方面仍有提升空间,而FunASR的多人说话检测能有效减少音色标注的麻烦。

🎙️ **WebRTC VAD用于语音活动检测**:WebRTC VAD是Google开发的语音活动检测工具,能区分音频中的语音和非语音(静音或噪声)。文章通过代码示例展示了其使用,但结论指出其在区分噪音类型上的准确性不高,因为其核心目的是区分语音活动,而非噪音类型识别,且噪声与语音在频谱上常有重叠。

🗣️ **FunASR实现多功能语音处理**:FunASR(Functional Automatic Speech Recognition)是一个功能强大的语音识别工具包,支持ASR、VAD和多人说话检测。文章重点介绍了其多人说话检测功能,认为这能极大简化音色标注工作,同时展示了如何利用其ASR能力进行句子级别的音频切分和导出,并指出识别结果的speaker标签需要进一步匹配。

🎶 **YAMNet进行声音事件分类**:YAMNet是Google开发的预训练深度学习模型,能识别521种声音类别,适用于检测音频中的杂音。文章提供了使用YAMNet分析音频并输出声音类别概率的代码示例,但结论表明其输出结果的准确性有待提高,建议进行筛选、聚类或模型微调来优化使用效果。

⚙️ **音频数据处理的整体流程与挑战**:文章提出的初步方案包括音频分段、说话人识别、杂音去除或筛选,最终目标是获得高质量、带有音色标注的音频数据。尽管介绍了多种工具,但作者强调这些工具并非“开箱即用”,实际应用中需要根据具体需求进行不同程度的后处理、参数调整或模型微调,以达到理想的处理效果。

这篇文章主要是对音频数据进行处理的一些初步探索和尝试。这篇文章并不会讲述这些模型的结构和原理。目的是使用这几个模型工具从音频数据中提取出纯净的语音数据用于训练,这里进行了尝试,并展示了结果,可以让大家对这些工具有一个直观的认识,主要尝试的模型包括WebRTCVADFunASRYAMNet

在工作中遇到了一个需求,就是想要训练某个特别音色的TTS模型,用于输出语音。虽然cosyvoice 等其他语音模型都提供了音色克隆,但是某些情况下仍然不能满足,所以想要进行训练来满足需求。但是数据从哪里来呢?我初步想到的一个方法就是从现有的影视剧作品中提取音频数据进行训练(商业模型的话需要考虑版权问题哟😊,我这里只是进行技术探索~)。

初步方案

对一段音频进行处理达到可以进行训练的标准,需要质量非常的高,并且标注不同的说话人音色。我这里的初步方案是:

    对长音频进行分段。识别出不同的说话人。对片段进行杂音去除,或者对片段进行筛选,筛选出没有杂音的片段。最后得到音频、文本、音色(或者角色标注)的数据。

静音分割

直接看代码吧这里不再赘述。

from pydub import AudioSegmentfrom pydub.silence import split_on_silence# 加载音频audio = AudioSegment.from_file("input.wav", format="wav")# 按静音切分(参数需根据音频调整)chunks = split_on_silence(    audio,    min_silence_len=500,  # 最小静音时长(毫秒)    silence_thresh=-40,   # 静音阈值(dBFS,低于此值视为静音)    keep_silence=300      # 每段前后保留的静音时长(毫秒))# 保存分段for i, chunk in enumerate(chunks):    chunk.export(f"chunk_{i}.wav", format="wav")

WebRTC VAD

WebRTC VAD(Voice Activity Detection)也是Google 研发的,是 WebRTC项目中的核心模块,可用于实时检测音频流中的语音活动。WebRTC VAD可以进行4种检测模式:0(Normal):通用场景,平衡了灵敏度与误检。1(Low Bitrate):进行了低带宽优化。2(Aggressive)3(Very Aggressive):适合高噪环境,减少漏检但增加了误检的情况。

代码

import numpy as npfrom pydub import AudioSegmentimport webrtcvaddef strict_vad_detection(audio_path, segment_length_ms=500, vad_aggressiveness=1):    # 1. 加载音频并转换为16kHz单声道16-bit PCM    audio = AudioSegment.from_file(audio_path)    audio = audio.set_frame_rate(16000).set_channels(1)    samples = np.array(audio.get_array_of_samples(), dtype=np.int16)        # 2. 初始化VAD    vad = webrtcvad.Vad(vad_aggressiveness)    frame_duration_ms = 30 # WebRTCVAD要求10ms/20ms/30ms    frame_size = int(16000 * frame_duration_ms / 1000)  # 每帧采样数        segments = []    for start_ms in range(0, len(audio), segment_length_ms):        end_ms = start_ms + segment_length_ms        segment_samples = samples[start_ms*16 : end_ms*16]  # 16=16000Hz/1000ms                # 3. 逐帧检测,只要有一帧不是人声,整个片段标记为噪声        is_pure_speech = True        for i in range(0, len(segment_samples), frame_size):            frame = segment_samples[i:i+frame_size]            if len(frame) < frame_size:                continue  # 跳过不完整的最后一帧            frame_bytes = frame.tobytes()            if not vad.is_speech(frame_bytes, sample_rate=16000):                is_pure_speech = False                break  # 发现噪声,立即终止判断                segments.append({            "start_ms": start_ms,            "end_ms": end_ms,            "is_speech": is_pure_speech,  # True=纯净人声,False=噪声/音乐            "audio": audio[start_ms:end_ms]        })        return segmentssegments = strict_vad_detection("output/chunk_0.wav", segment_length_ms=500)for seg in segments:    label = "人声" if seg["is_speech"] else "噪声"    print(f"{seg['start_ms']}-{seg['end_ms']}ms: {label}")    seg["audio"].export(f"segment_{seg['start_ms']}_{label}.wav", format="wav")

结论

想要通过这种方式检测其中是否包含噪音其实特别的不准确,其实WebRTC VAD的核心目的是区分语音与非语音或者说静音或噪声,并不是专门识别噪音的类型的。它基于高斯混合模型的检测逻辑,通过子带能量分析判断语音活动,但是噪声与语音通常在频谱上有大量的重叠,最终导致误判~。

FunASR

FunASR(Functional Automatic Speech Recognition)是由阿里巴巴达摩院开源的多功能语音识别工具包,他能够进行ASRVAD多人说话检测。其中VAD基于FSMN-VAD模型,精准检测语音的起始和结束。最让我心动的是可以进行多人说话检测,这不就省去了对音色进行标注的麻烦了嘛~~

代码

from funasr import AutoModelfrom pydub import AudioSegmentimport jsonimport os# Initialize model with relative pathsmodel = AutoModel(model="models/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-pytorch",                    vad_model="models/speech_fsmn_vad_zh-cn-16k-common-pytorch",                    punc_model="models/punc_ct-transformer_zh-cn-common-vocab272727-pytorch",                   spk_model="models/speech_campplus_sv_zh-cn_16k-common")def split_audio_by_sentences(audio_path, output_folder, asr_result):    audio = AudioSegment.from_file(audio_path)    os.makedirs(output_folder, exist_ok=True)        for i, sentence in enumerate(asr_result[0]['sentence_info']):        print(sentence)        start_ms = sentence['start']  # 开始时间(毫秒)        end_ms = sentence['end']      # 结束时间(毫秒)        text = sentence['text'].strip().replace(' ', '_')[:50]  # 清理文本作为文件名                # 提取音频片段        segment = audio[start_ms:end_ms]                # 生成输出文件名        output_path = os.path.join(output_folder, f"sentence_{i+1}_{start_ms}-{end_ms}_{text}.wav")                # 保存音频片段        segment.export(output_path, format="wav")        print(f"已保存: {output_path} ({len(segment)/1000:.2f}秒)")if __name__ == "__main__":    input_file = "01最爱玩跳泥坑游戏.mp3"    asr_result = model.generate(input=input_file,                      batch_size_s=300,                      hotword='魔搭')    split_audio_by_sentences(        audio_path=input_file,        output_folder="output/funasr_out",        asr_result=asr_result    )

输出举例

{  "text": "猪妈妈和猪爸爸也穿着他们的靴子。",  "start": 245710,  "end": 248575,  "timestamp": [    [245710, 245950],    [246030, 246170],    [246170, 246290],    [246290, 246430],    [246430, 246630],    [246630, 246830],    [246830, 247070],    [247090, 247330],    [247370, 247470],    [247470, 247670],    [247670, 247830],    [247830, 247950],    [247950, 248050],    [248050, 248250],    [248250, 248575]  ],  "spk": 2}

结论

这个工具非常好用,识别的还算准确,对识别结果稍作处理即可应用。后续的工作是,两个片段中的speaker 不能直接对应起来,这方面还需要自己进行匹配。

YAMNet

YAMNet(Yet Another Music Recognition Network)是由谷歌开发的预训练深度学习模型,专门为音频事件分类设计。它基于MobileNetV1的深度可分离卷积架构,在AudioSet数据集(包含大规模音频-视频数据)上训练,它能够识别521种声音类别(如环境音、音乐、人声等)。512中声音呐,检测一段音频中是否包含杂音应该足够了吧~~

代码

import tensorflow as tfimport tensorflow_hub as hubimport numpy as npimport osimport librosafrom collections import defaultdictdef load_yamnet_model():    """Load and cache the YAMNet model to avoid repeated downloads"""    model = hub.load('https://tfhub.dev/google/yamnet/1')    # Load class names once    class_map_path = model.class_map_path().numpy()    with tf.io.gfile.GFile(class_map_path, 'r') as f:        class_names = [line.strip() for line in f]    return model, class_names# Cache the model and class names at module levelYAMNET_MODEL, CLASS_NAMES = load_yamnet_model()def analyze_audio(waveform, model, class_names):    """Run YAMNet model and process results"""    scores, _, _ = model(waveform)    mean_scores = np.mean(scores.numpy(), axis=0)    top_k_indices = np.argsort(mean_scores)[-5:][::-1]  # Top-5 predictions    return {        class_names[i].split('/')[-1].lower(): mean_scores[i]        for i in top_k_indices    }def detect_non_speech_yamnet(file_path):    """Detect non-speech elements in audio file using YAMNet"""    try:        # Load audio with librosa (mono, 16kHz)        waveform, _ = librosa.load(file_path, sr=16000, mono=True)        waveform = tf.convert_to_tensor(waveform, dtype=tf.float32)                # Analyze audio and get top predictions        return analyze_audio(waveform, YAMNET_MODEL, CLASS_NAMES)            except Exception as e:        print(f"Error processing {file_path}: {str(e)}")        return Nonedef process_directory(directory_path, extension=".wav"):    """Process all audio files in a directory and return statistics"""    stats = defaultdict(float)        for filename in os.listdir(directory_path):        if not filename.endswith(extension):            continue                    filepath = os.path.join(directory_path, filename)        try:            result = detect_non_speech_yamnet(filepath)            if result:                for key, value in result.items():                    stats[key] += value        except Exception as e:            print(f"Fatal error processing {filename}: {str(e)}")        return sorted(stats.items(), key=lambda x: x[1], reverse=True)if __name__ == "__main__":    # Example usage    audio_dir = "output/peggy"    statistics = process_directory(audio_dir)        print("\nAudio Classification Statistics:")    for key, value in statistics:        print(f"{key}: {value:.4f}")

输出样例

index,mid,display_name: 6.081626892089844032n05,whale vocalization: 1.020454406738281209x0r,speech: 0.32054355740547180155w,blues: 0.2389743030071258502yds9,purr: 0.199967876076698307qyrcz,plop: 0.14755500853061676dd00136,whimper (dog): 0.14755499362945557016622,tubular bells: 0.1403512209653854407pt_g0,pulse: 0.1246077418327331503q5t,harpsichord: 0.096752308309078220dwsp,"marimba, xylophone": 0.09039849787950516dd00013,children playing: 0.082002848386764530j45pbj,mallet percussion: 0.073054611682891850261r1,babbling: 0.0693439766764640801h8n0,conversation: 0.0417588986456394207phhsh,rumble: 0.030374335125088690239kh,cowbell: 0.0246097389608621607qcx4z,tearing: 0.0195935145020484920ytgt,"child speech, kid speaking": 0.018814150243997574

结论

对于输出的结果,就我自己的观察,并不是特别的准确,但是存在一定的输出模式的规律,后面如果想用这个模型,应该进行后续的处理,比如:

    筛选,观察一些符合自己要求的模式。进行聚类分析。标注部分数据对模型进行微调等等。

总结

本篇文章介绍了几种现有的模型工具用于处理语音的数据,包括WebRTCVADFunASRYAMNet。这些工具很好,但并不是开箱即用的,可能需要根据自己想要实现的功能不同进行不同程度的后处理或者进行模型的预训练。

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

音频处理 VAD ASR YAMNet TTS
相关文章