大模型应用:语音转文本(ASR)实践:OpenAI Whisper精准转录解析.21

一、前言

前面我们详细介绍了文本转语音的细节和实践,今天我们继续探讨一下语音转文本(ASR),初次接触,OpenAI Whisper 是最易上手、效果最均衡的开源大模型,它无需复杂的专业知识,一行代码就能实现多语言语音转写,且在噪声、口音、多语言场景下的表现远优于传统 ASR。

今天我们从基础概念入手,逐行拆解代码、详解核心参数,结合实际场景选择参数提升转录准确性,覆盖从零基础运行到精准适配场景的全流程,所有内容优先讲解基础点,确保我们都能理解、能举一反三的可用复用。

二、基础概念

1. 语音转文本(ASR)

ASR,全称Automatic Speech Recognition,即自动语音识别,核心是把人类说话的音频信号转换成文字。日常用的微信语音转文字、会议纪要自动生成,本质都是 ASR 技术。

**核心评价指标:**字错率(WER),简单理解为 "转错的字数/总字数",数值越低,转录越准确(比如 WER=5%,代表 100 个字里错 5 个)。

2. Whisper模型特征

Whisper 是 OpenAI 开源的 ASR 大模型,新手只需记住 3 个核心特点:

  • 端到端设计:无需手动处理音频特征(比如传统 ASR 要做的 MFCC 特征提取),直接输入音频就能输出文本,新手无需懂声学知识;
  • 多语言支持:原生支持 99 种语言,包括中文(普通话 / 方言)、英文、日语等,无需单独训练;
  • 预训练数据足:基于 68 万小时多语言标注音频训练,噪声、口音、不同语速的适配性都很强。

3. Whisper模型分类

Whisper 提供 5 种预训练模型尺寸,新手可简单理解为 "模型越大,越准但越慢、占内存越多",各尺寸的基础属性如下(新手重点看 "适用场景"):

  • **tiny 尺寸:**参数量为 39M(百万),中文安静场景下的字错率(WER)约 8.5%,仅需要 CPU 就能运行,适合快速粗略转写(比如语音消息);
  • **base 尺寸:**参数量为 74M,中文安静场景下的字错率(WER)约 6.2%,仅需要 CPU 就能运行,是新手首选(平衡准度和速度);
  • **small 尺寸:**参数量为 244M,中文安静场景下的字错率(WER)约 4.1%,CPU 或 GPU 都能运行,适合要求稍高的通用场景;
  • **medium 尺寸:**参数量为 769M,中文安静场景下的字错率(WER)约 2.8%,需要 GPU(显存≥4GB)才能流畅运行,适合高精度需求(比如会议记录);
  • **large-v3 尺寸:**参数量为 1550M,中文安静场景下的字错率(WER)约 2.1%,需要 GPU(显存≥8GB)才能运行,适合专业场景(比如医疗、法律领域)。

4. Whisper与传统ASR的差异

维度 传统 ASR(如 MFCC+HMM) Whisper 大模型
语言支持 需单独训练单语言模型 原生支持 99 种语言,无需额外适配
噪声鲁棒性 噪声场景 WER 飙升至 30%+ 80dB 噪声下 WER 仍 < 15%
特征提取 手动设计声学特征(MFCC) 端到端自学习特征,适配复杂音频
部署成本 需部署特征提取、解码等多模块 单模型即可完成从音频到文本的转换

三、基础示例精析

转录音频内容,支持 WAV/MP3/M4A 等格式,实现音频内容解析输出

1. 示例代码

python 复制代码
# 第一步:导入Whisper库(基础操作,所有功能都需要)
import whisper

# 第二步:定义基础转录函数
def transcribe_audio_basic(audio_path):
    # 2.1 加载模型:选择base尺寸
    # load_model是Whisper的核心函数,参数传模型尺寸
    model = whisper.load_model("base")
    
    # 2.2 执行转录:传入音频路径,得到结果
    # transcribe是实现"音频→文本"的核心方法
    result = model.transcribe(audio_path)
    
    # 2.3 返回关键结果(新手先关注text和language)
    return {
        "text": result["text"],       # 完整转录文本
        "language": result["language"], # 检测到的语言
        "segments": result["segments"]  # 段落时间戳
    }

# 第三步:调用函数并打印结果
if __name__ == "__main__":
    # 替换为你的音频文件路径
    audio_path = "qqqq.wav"
    # 调用转录函数
    result = transcribe_audio_basic(audio_path)
    # 打印结果(基础输出,看是否转写成功)
    print(f"转录文本: {result['text']}")
    print(f"检测语言: {result['language']}")

2. 输出结果

100%|████████████████████████████| 139M/139M [00:03<00:00, 46.9MiB/s]

C:\ProgramData\Anaconda3\lib\site-packages\whisper\transcribe.py:132: UserWarning: FP16 is not supported on CPU; using FP32 instead

warnings.warn("FP16 is not supported on CPU; using FP32 instead")

转录文本: 欢迎使用DDS文本朗读器。请在这里输入想要朗读的文本。

检测语言: zh

3. 结果解析

3.1. 逐行解析:

  • 第一行:下载 base 模型权重(139M),首次运行会下载,后续直接复用;
  • 警告行:FP16 is not supported on CPU,简单理解:
    • FP16/FP32 是数据精度,FP16 更快、占内存更少,但 CPU 不支持,自动用 FP32;
    • 仅影响速度,不影响转写结果,GPU 运行时会自动用 FP16,无此警告;
  • 转录文本:音频对应的文字内容;
  • 检测语言:zh代表中文(ISO 639-1 标准码,en= 英文、ja= 日语)。

3.2. 常见问题:

  • 报错 "找不到音频文件":检查audio_path是否正确(建议用绝对路径,如C:\audio\qqqq.wav);
  • 报错 "ffmpeg not found":按 2.1 节补充安装 ffmpeg 并配置环境变量;
  • 转写结果乱码:打印时加ensure_ascii=False,如print(f"转录文本: {result['text']}", ensure_ascii=False)。

4. 核心参数详解

提升转写准确性的第一步,必须要搞懂"模型加载"和"转录方法"的核心参数,参数选择的合理,准确性可直接大幅度提升。

4.1 模型加载参数

模型加载方式 whisper.load_model(),仅 1 个核心参数

  • 参数名:model_size
  • 基础含义:选择预训练模型的尺寸,决定 "准度 - 速度 - 内存" 的平衡;
  • 取值范围:tiny、base、small、medium、large-v3,可对应前面的模型分类详细了解;
  • 选择建议:仅 CPU 选 base;有 GPU(显存≥4GB)选 medium。

4.2 转录方法参数

model.transcribe() 是实现转写的核心方法,重点先了解以下 6 个基础参数(按重要性排序):

4.2.1 audio 参数:

  • 基础含义:输入的音频(必须传入);
  • 常用取值:音频文件路径(比如 "qqqq.wav");
  • 基础作用:指定要转写的音频;
  • 使用建议:直接传文件路径即可。

4.2.2 language 参数:

  • 基础含义:指定转写语言;
  • 常用取值:"zh"(中文)、"en"(英文)、None(自动检测);
  • 基础作用:避免模型误判语言(比如把中文识别成日语);
  • 使用建议:纯中文音频必传 "zh",提升准确性。

4.2.3 task 参数:

  • 基础含义:任务类型;
  • 常用取值:"transcribe"(转写)、"translate"(翻译为英文);
  • 基础作用:决定是 "转写成本语言" 还是 "翻译为英文";
  • 使用建议:默认用 "transcribe"。

4.2.4 temperature 参数:

  • 基础含义:控制输出随机性;
  • 常用取值:0.0(首选);
  • 基础作用:0.0 代表结果固定(无随机错误),数值越高代表结果越随机;
  • 使用建议:非创意场景(如会议记录)必设为 0.0。

4.2.5 beam_size 参数:

  • 基础含义:集束搜索宽度(通俗理解:模型尝试的 "候选文本数量");
  • 常用取值:5(默认)、8、10;
  • 基础作用:数值越大,越易找到 "最准确的文本",但速度越慢;
  • 使用建议:通用场景设 8,噪声场景设 10。

4.2.6 best_of 参数:

  • 基础含义:集束搜索候选数;
  • 常用取值:5(默认)、6、8;
  • 基础作用:生成多组候选结果,选最优的一组;
  • 使用建议:与 beam_size 配合,beam_size=8 则 best_of=6。

整体参数示例:

python 复制代码
# 优化中文转写准确性的基础配置
result = model.transcribe(
    audio_path,
    language="zh",       # 强制指定中文,避免误判
    temperature=0.0,    # 固定结果,无随机错误
    beam_size=8,        # 增加候选数量,提升准确性
    best_of=6           # 配合beam_size,选最优结果
)

4.3 返回结果参数

transcribe返回的result是一个字典,我们可以先从以下 3 个基础字段入手:

4.3.1 text 字段:

  • 基础含义:完整的转写文本;
  • 示例值:"欢迎使用 DDS 文本朗读器。";
  • 使用场景:直接获取转写结果。

4.3.2 language 字段:

  • 基础含义:检测到的语言码;
  • 示例值:"zh";
  • 使用场景:验证模型是否正确识别语言。

4.3.3 segments 字段:

  • 基础含义:段落级时间戳列表;
  • 示例值:[{"start": 0.0, "end": 3.5, "text": "欢迎使用 DDS 文本朗读器。"}, ...];
  • 使用场景:查看 "某段话出现在音频的第几秒"。

segments输出示例:

python 复制代码
# 在上方代码的函数中添加以下代码,打印段落时间戳
for segment in result["segments"]:
    print(f"[{segment['start']:.1f}秒 - {segment['end']:.1f}秒]:{segment['text']}")

#输出结果
#[0.0秒 - 3.5秒]: 欢迎使用DDS文本朗读器。
#[3.5秒 - 6.8秒]: 请在这里输入想要朗读的文本。

5. 执行流程

流程细节说明:

  • **1. 环境准备:**安装必要的依赖库和工具,主要包括:ASR相关库(如transformers, whisper)、音频处理库(如librosa, soundfile)
  • **2. 模型选择:**根据需求选择合适的语音识别模型:开源模型:Whisper、商用API、多语言/单语言模型选择
  • **3. 参数配置:**设置转录过程的关键参数:语言选择(自动检测或指定)、任务类型(转录/翻译)、采样率设置
  • **4. 音频转录:**执行核心的音频转文本处理:音频预处理(降噪、归一化)、特征提取(MFCC, Mel频谱)、模型推理、解码输出
  • **5. 结果处理:**对原始转录结果进行处理:提取文本内容、获取时间戳信息、说话人分离、置信度评估
  • **6. 优化调整:**根据结果进行优化:参数调优(温度、集束搜索宽度)、后处理(标点恢复、数字规范化)、错误校正、性能评估
  • **7. 完成转录:**输出最终结果:完整转录文本、带时间戳的文本分段、元数据(语言、置信度等)、可选格式(TXT, SRT, JSON等)

这个流程提供了从音频到文本的完整转录路径,每个步骤都是构建高质量语音识别系统的重要组成部分。

四、模型参数选择

提升转写准确性,无需复杂操作,按"先选模型→再调基础参数→最后适配场景"的步骤即可,每一步都基于基础操作。

1. 第一步:选对模型尺寸

模型尺寸是准确性的基础,尺寸越大,准度越高,我们需要按硬件选择即可,不同硬件对应的模型选择、准确性和转写速度如下:

  • CPU(4 核 8G):选择 base 模型(首选),中文安静场景下的准确性为 WER≈6.2%,转写 1 分钟音频大约需要 15 秒;
  • CPU(8 核 16G):选择 small 模型,中文安静场景下的准确性为 WER≈4.1%,转写 1 分钟音频大约需要 30 秒;
  • GPU(显存≥4GB):选择 medium 模型,中文安静场景下的准确性为 WER≈2.8%,转写 1 分钟音频大约需要 1 分钟;
  • GPU(显存≥8GB):选择 large-v3 模型,中文安静场景下的准确性为 WER≈2.1%,转写 1 分钟音频大约需要 2 分钟。

2. 第二步:调优基础参数

选好模型后,先改 2 个最易上手的参数,可直接提升准确性:

  • 强制指定 language:纯中文音频必传language="zh",避免模型误判为日语/韩语;
  • 固定 temperature=0.0:避免随机错误(如漏字、错字)。

3. 第三步:进阶参数调优

若基础参数调整后仍需提升准确性,再调以下 2 个参数(平衡准度和速度),不同场景的参数取值和基础效果如下:

  • 通用安静场景(如录音笔记):beam_size 设为 8,best_of 设为 6,准确性提升 3%-5%,速度慢 20%;
  • 噪声/口音场景(如户外录音):beam_size 设为 10,best_of 设为 8,准确性提升 5%-8%,速度慢 30%;
  • 专业场景(如医疗/法律):beam_size 设为 12,best_of 设为 8,术语识别更准,速度慢 40%。

4. 第四步:场景化基础配置

不同场景的参数最优组合不同,我们可以按以下配置调整后观察输出效果,持续的微调优化:

场景 1:通用安静短音频(如语音消息、录音笔记)

python 复制代码
def transcribe_basic_scene(audio_path):
    model = whisper.load_model("base")  # CPU首选
    result = model.transcribe(
        audio_path,
        language="zh",
        temperature=0.0,
        beam_size=8,
        best_of=6
    )
    return result

场景 2:噪声 / 口音重音频(如户外录音、方言口音)

python 复制代码
def transcribe_noise_scene(audio_path):
    model = whisper.load_model("small")  # 比base更准
    result = model.transcribe(
        audio_path,
        language="zh",
        temperature=0.0,
        beam_size=10,
        best_of=8
    )
    return result

场景 3:长音频(如会议录音,>5 分钟)

python 复制代码
def transcribe_long_audio(audio_path):
    model = whisper.load_model("medium")  # 有GPU优先选
    result = model.transcribe(
        audio_path,
        language="zh",
        temperature=0.0,
        beam_size=8,
        best_of=6,
        condition_on_previous_text=True  # 保持上下文连贯(新手无需改,默认True)
    )
    return result

五、进阶扩展

1. 带词级时间戳的转录

实现精细化转写,我们可以开启word_timestamps=True,获取每个单词的精准时间戳(如制作字幕):

python 复制代码
def transcribe_with_word_timestamps(audio_path):
    model = whisper.load_model("base")
    result = model.transcribe(
        audio_path,
        language="zh",
        temperature=0.0,
        word_timestamps=True  # 启用词级时间戳
    )
    # 打印词级时间戳(新手可理解为"每个字/词的起止时间")
    for segment in result["segments"]:
        print(f"[{segment['start']:.1f}秒 - {segment['end']:.1f}秒]:{segment['text']}")
        for word in segment["words"]:
            print(f"  「{word['word']}」:{word['start']:.1f}秒 - {word['end']:.1f}秒")
    return result

输出示例:

python 复制代码
[0.0秒 - 3.5秒]: 欢迎使用DDS文本朗读器。
  「欢迎」:0.0秒 - 0.5秒
  「使用」:0.5秒 - 1.0秒
  「DDS」:1.0秒 - 1.5秒
  「文本」:1.5秒 - 2.0秒
  「朗读器」:2.0秒 - 3.5秒

2. 批量转录

处理多个音频文件,我们可以封装批量处理函数,避免重复操作,增加基础异常处理,防止单个文件报错中断:

python 复制代码
def batch_transcribe(audio_files):
    # audio_files是音频文件路径列表,如["audio1.wav", "audio2.wav"]
    model = whisper.load_model("base")
    results = {}
    for file in audio_files:
        try:
            print(f"正在处理:{file}")
            result = model.transcribe(file, language="zh", temperature=0.0)
            results[file] = result["text"]  # 仅保存转写文本
        except Exception as e:
            print(f"处理{file}失败:{str(e)}")
            results[file] = "转写失败"
    return results

audio_list = ["meeting1.wav", "meeting2.wav"]
batch_results = batch_transcribe(audio_list)
# 打印批量结果
for file, text in batch_results.items():
    print(f"\n{file}转写结果:")
    print(text)

3. 说话人分离

识别"谁在什么时候说什么",结合pyannote.audio实现"说话人 + 文本 + 时间戳"的转写

python 复制代码
def transcribe_with_speaker(audio_path, hf_token):
    # 第一步:基础转写(获取文本+时间戳)
    model = whisper.load_model("base")
    transcribe_result = model.transcribe(
        audio_path,
        language="zh",
        temperature=0.0,
        word_timestamps=True
    )
    
    # 第二步:加载说话人分离模型
    from pyannote.audio import Pipeline
    diarization_pipeline = Pipeline.from_pretrained(
        "pyannote/speaker-diarization-3.1",
        use_auth_token=hf_token  # 替换为你的HuggingFace令牌
    )
    diarization_result = diarization_pipeline(audio_path)
    
    # 第三步:匹配"说话人-文本-时间戳"
    final_result = []
    for segment in transcribe_result["segments"]:
        # 找到该时间段内的主要说话人
        speaker = "unknown"
        for turn, _, spk in diarization_result.itertracks(yield_label=True):
            if turn.start <= segment["end"] and turn.end >= segment["start"]:
                speaker = spk
                break
        # 保存结果
        final_result.append({
            "start": segment["start"],
            "end": segment["end"],
            "speaker": speaker,
            "text": segment["text"]
        })
    
    # 打印结果
    for item in final_result:
        print(f"[{item['start']:.1f}秒 - {item['end']:.1f}秒] {item['speaker']}:{item['text']}")
    return final_result
hf_token = "你的HuggingFace令牌"
transcribe_with_speaker("group_discussion.wav", hf_token)

输出示例:

python 复制代码
[0.0秒 - 5.0秒] SPEAKER_00: 今天我们讨论一下项目进度。
[5.0秒 - 10.0秒] SPEAKER_01: 我这边的模块已经完成了80%。

六、辅助优化

1. 音频预处理(降噪)

进一步提升转写的准确性,可以对噪声音频先降噪,再进行转写操作,需要应用安装noisereduce库。

python 复制代码
import noisereduce as nr
import soundfile as sf
# 读取音频
data, rate = sf.read(audio_path)
# 提取噪声样本(取音频前1秒作为噪声)
noise_sample = data[:rate]
# 降噪
reduced_noise = nr.reduce_noise(y=data, y_noise=noise_sample, sr=rate)
# 保存降噪后的音频
sf.write("audio_denoised.wav", reduced_noise, rate)
# 对降噪后的音频转写
model.transcribe("audio_denoised.wav", language="zh")

2. 简单后处理(校正错字)

python 复制代码
result_text = result["text"]
# 替换常见错字
corrected_text = result_text.replace("北惊", "北京").replace("百份之", "百分之")
print("校正后文本:", corrected_text)

七、应用示例

1. 完整的应用实例

包括单文件转录、批量转录以及带说话人分离的转录

python 复制代码
import whisper
import numpy as np
import torch
from typing import Dict, List
import json

class AdvancedTranscriber:
    """
    高级语音转录器,包含多种优化功能
    """
    
    def __init__(self, model_size="base", device="cuda"):
        """
        初始化转录器
        
        Args:
            model_size: 模型尺寸 (tiny, base, small, medium, large)
            device: 计算设备 (cuda/cpu)
        """
        self.device = device if torch.cuda.is_available() else "cpu"
        self.model = whisper.load_model(model_size).to(self.device)
        
        # 配置转录参数
        self.transcribe_options = {
            "language": "zh",  # 指定语言(可选自动检测)
            "task": "transcribe",  # transcribe或translate
            "temperature": 0.0,  # 采样温度(0为确定性)
            "best_of": 5,  # 集束搜索候选数
            "beam_size": 5,  # 集束搜索宽度
            "fp16": True if self.device == "cuda" else False,
        }
    
    def transcribe_with_timestamps(self, audio_path: str) -> Dict:
        """
        带时间戳的详细转录
        """
        result = self.model.transcribe(
            audio_path,
            **self.transcribe_options,
            word_timestamps=True  # 启用词级时间戳
        )
        
        # 结构化输出
        structured_output = {
            "full_text": result["text"],
            "language": result["language"],
            "duration": self._get_audio_duration(audio_path),
            "segments": []
        }
        
        for segment in result["segments"]:
            segment_info = {
                "start": segment["start"],
                "end": segment["end"],
                "text": segment["text"],
                "confidence": segment.get("confidence", 0),
                "words": segment.get("words", [])
            }
            structured_output["segments"].append(segment_info)
        
        return structured_output
    
    def batch_transcribe(self, audio_files: List[str]) -> Dict[str, Dict]:
        """
        批量转录多个音频文件
        """
        results = {}
        
        for audio_file in audio_files:
            try:
                print(f"正在处理: {audio_file}")
                result = self.transcribe_with_timestamps(audio_file)
                results[audio_file] = result
            except Exception as e:
                print(f"处理失败 {audio_file}: {str(e)}")
                results[audio_file] = {"error": str(e)}
        
        return results
    
    def transcribe_with_speaker_diarization(self, audio_path: str):
        """
        结合说话人分离的转录
        需要额外安装:pip install pyannote.audio
        """
        from pyannote.audio import Pipeline
        
        # 第一步:使用Whisper转录
        transcription_result = self.transcribe_with_timestamps(audio_path)
        
        # 第二步:说话人分离
        diarization_pipeline = Pipeline.from_pretrained(
            "pyannote/speaker-diarization-3.1",
            use_auth_token="YOUR_HUGGINGFACE_TOKEN"
        )
        
        diarization_result = diarization_pipeline(audio_path)
        
        # 第三步:对齐说话人标签和转录
        aligned_result = self._align_speakers_with_transcription(
            transcription_result, 
            diarization_result
        )
        
        return aligned_result
    
    def _align_speakers_with_transcription(self, transcription, diarization):
        """
        对齐说话人标签和转录文本
        """
        aligned_segments = []
        
        for transcript_segment in transcription["segments"]:
            # 找到该时间段内的说话人
            segment_start = transcript_segment["start"]
            segment_end = transcript_segment["end"]
            
            # 收集该时间段的所有说话人
            speakers_in_segment = []
            for turn, _, speaker in diarization.itertracks(yield_label=True):
                if turn.start <= segment_end and turn.end >= segment_start:
                    overlap_start = max(turn.start, segment_start)
                    overlap_end = min(turn.end, segment_end)
                    overlap_duration = overlap_end - overlap_start
                    
                    speakers_in_segment.append({
                        "speaker": speaker,
                        "overlap_duration": overlap_duration
                    })
            
            # 选择重叠时间最长的说话人
            if speakers_in_segment:
                main_speaker = max(
                    speakers_in_segment, 
                    key=lambda x: x["overlap_duration"]
                )["speaker"]
                transcript_segment["speaker"] = main_speaker
            else:
                transcript_segment["speaker"] = "unknown"
            
            aligned_segments.append(transcript_segment)
        
        transcription["segments"] = aligned_segments
        return transcription
    
    def _get_audio_duration(self, audio_path: str) -> float:
        """获取音频时长"""
        import soundfile as sf
        data, sample_rate = sf.read(audio_path)
        return len(data) / sample_rate

# 使用示例
transcriber = AdvancedTranscriber(model_size="small")

# 单文件转录
result = transcriber.transcribe_with_timestamps("qqqq.wav")
print(json.dumps(result, ensure_ascii=False, indent=2))

# 批量转录
batch_results = transcriber.batch_transcribe([
    "meeting1.wav",
    "meeting2.wav",
    "presentation.wav"
])

# 带说话人分离的转录
diarization_result = transcriber.transcribe_with_speaker_diarization(
    "group_discussion.wav"
)

输出结果:

{

"full_text": "欢迎使用GDS文本朗读器,请在这里输入想要朗读的文本。",

"language": "zh",

"duration": 11.321179138321995,

"segments": [

{

"start": 0.0,

"end": 9.98,

"text": "欢迎使用GDS文本朗读器,请在这里输入想要朗读的文本。",

"confidence": 0,

"words": [

{

"word": "欢",

"start": 0.0,

"end": 0.44,

"probability": 0.6587960124015808

},

{

"word": "迎",

"start": 0.44,

"end": 0.78,

"probability": 0.9997932314872742

},

{

"word": "使",

"start": 0.78,

"end": 1.16,

"probability": 0.9810769557952881

},

{

"word": "用",

"start": 1.16,

"end": 1.5,

"probability": 0.9999412298202515

},

{

"word": "G",

"start": 1.5,

"end": 1.84,

"probability": 0.5033477544784546

},

{

"word": "DS",

"start": 1.84,

"end": 2.3,

"probability": 0.9630266427993774

},

{

"word": "文",

"start": 2.3,

"end": 2.86,

"probability": 0.698738694190979

},

{

"word": "本",

"start": 2.86,

"end": 3.28,

"probability": 0.9940272569656372

},

{

"word": "朗",

"start": 3.28,

"end": 3.62,

"probability": 0.7624131739139557

},

{

"word": "读",

"start": 3.62,

"end": 3.88,

"probability": 0.985887736082077

},

{

"word": "器,",

"start": 3.88,

"end": 4.3,

"probability": 0.9958699345588684

},

{

"word": "请",

"start": 5.44,

"end": 6.06,

"probability": 0.9576152563095093

},

{

"word": "在",

"start": 6.06,

"end": 6.46,

"probability": 0.9904884099960327

},

{

"word": "这里",

"start": 6.46,

"end": 6.96,

"probability": 0.9948416352272034

},

{

"word": "输",

"start": 6.96,

"end": 7.42,

"probability": 0.9913595616817474

},

{

"word": "入",

"start": 7.42,

"end": 7.74,

"probability": 0.9998476505279541

},

{

"word": "想",

"start": 7.74,

"end": 8.04,

"probability": 0.9758610725402832

},

{

"word": "要",

"start": 8.04,

"end": 8.46,

"probability": 0.9975632429122925

},

{

"word": "朗",

"start": 8.46,

"end": 8.82,

"probability": 0.9854574501514435

},

{

"word": "读",

"start": 8.82,

"end": 9.14,

"probability": 0.9997773170471191

},

{

"word": "的",

"start": 9.14,

"end": 9.36,

"probability": 0.9987922310829163

},

{

"word": "文",

"start": 9.36,

"end": 9.64,

"probability": 0.9949055910110474

},

{

"word": "本。",

"start": 9.64,

"end": 9.98,

"probability": 0.9980852603912354

}

]

}

]

}

2. 转录后处理

处理转录后的内容,包括标点恢复、数字规范化等,可根据实际情况调整

python 复制代码
import re
from typing import List, Dict

class TranscriptionPostProcessor:
    """
    转录后处理:标点恢复、数字规范化等
    """
    
    def __init__(self, language="zh"):
        self.language = language
        
        # 定义正则规则
        self.patterns = {
            "numbers": {
                "zh": r"(\d+)",
                "en": r"(\d+)"
            },
            "urls": r"https?://\S+|www\.\S+",
            "emails": r"\S+@\S+\.\S+"
        }
        
        # 数字转换规则(中文)
        self.number_map_zh = {
            '0': '零', '1': '一', '2': '二', '3': '三', '4': '四',
            '5': '五', '6': '六', '7': '七', '8': '八', '9': '九'
        }
    
    def process_transcription(self, text: str) -> str:
        """
        完整的后处理流程
        """
        # 1. 清理多余空格
        text = self._clean_spaces(text)
        
        # 2. 标点标准化
        text = self._normalize_punctuation(text)
        
        # 3. 数字规范化
        if self.language == "zh":
            text = self._normalize_numbers_chinese(text)
        elif self.language == "en":
            text = self._normalize_numbers_english(text)
        
        # 4. 大写规范化(英文)
        if self.language == "en":
            text = self._normalize_capitalization(text)
        
        # 5. 分割过长的句子
        text = self._split_long_sentences(text)
        
        return text
    
    def _clean_spaces(self, text: str) -> str:
        """清理多余空格"""
        # 去除首尾空格
        text = text.strip()
        # 将多个空格合并为一个
        text = re.sub(r'\s+', ' ', text)
        # 修复标点前的空格
        text = re.sub(r'\s+([,.!?;:])', r'\1', text)
        
        return text
    
    def _normalize_punctuation(self, text: str) -> str:
        """标点符号标准化"""
        # 英文标点转中文标点(如果目标语言是中文)
        if self.language == "zh":
            punctuation_map = {
                ',': ',', '.': '。', '!': '!', '?': '?',
                ';': ';', ':': ':', '(': '(', ')': ')',
                '"': '「', "'": '『'
            }
            
            for eng_punc, zh_punc in punctuation_map.items():
                text = text.replace(eng_punc, zh_punc)
        
        # 确保标点后有空格(英文)
        if self.language == "en":
            text = re.sub(r'([,.!?;:])([A-Za-z])', r'\1 \2', text)
        
        return text
    
    def _normalize_numbers_chinese(self, text: str) -> str:
        """中文数字规范化"""
        def number_to_chinese(match):
            num_str = match.group(1)
            
            # 简单转换:将每个数字转为中文
            if len(num_str) == 1:
                return self.number_map_zh.get(num_str, num_str)
            elif len(num_str) == 4 and 1000 <= int(num_str) <= 9999:
                # 年份处理:2023 -> 二零二三
                return ''.join(self.number_map_zh.get(d, d) for d in num_str)
            else:
                # 保持数字形式
                return num_str
        
        # 替换数字
        text = re.sub(self.patterns["numbers"][self.language], number_to_chinese, text)
        
        return text
    
    def _normalize_numbers_english(self, text: str) -> str:
        """英文数字规范化"""
        def number_to_words(match):
            num = int(match.group(1))
            
            # 简单的数字转单词(0-99)
            if 0 <= num <= 99:
                words_0_19 = [
                    'zero', 'one', 'two', 'three', 'four', 'five',
                    'six', 'seven', 'eight', 'nine', 'ten',
                    'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen',
                    'sixteen', 'seventeen', 'eighteen', 'nineteen'
                ]
                tens = [
                    '', '', 'twenty', 'thirty', 'forty',
                    'fifty', 'sixty', 'seventy', 'eighty', 'ninety'
                ]
                
                if num < 20:
                    return words_0_19[num]
                else:
                    ten = tens[num // 10]
                    one = words_0_19[num % 10] if num % 10 != 0 else ''
                    return f"{ten}-{one}" if one else ten
            else:
                # 保持数字形式
                return str(num)
        
        # 替换数字
        text = re.sub(self.patterns["numbers"][self.language], number_to_words, text)
        
        return text
    
    def _split_long_sentences(self, text: str, max_length=50) -> str:
        """分割过长的句子"""
        sentences = re.split(r'([。!?.!?])', text)
        processed_sentences = []
        
        for i in range(0, len(sentences), 2):
            if i + 1 < len(sentences):
                sentence = sentences[i] + sentences[i + 1]
            else:
                sentence = sentences[i]
            
            if len(sentence) > max_length:
                # 根据逗号分割
                parts = re.split(r'([,,])', sentence)
                reconstructed = []
                
                for j in range(0, len(parts), 2):
                    if j + 1 < len(parts):
                        part = parts[j] + parts[j + 1]
                    else:
                        part = parts[j]
                    
                    reconstructed.append(part)
                
                sentence = ''.join(reconstructed)
            
            processed_sentences.append(sentence)
        
        return ''.join(processed_sentences)

# 使用后处理
post_processor = TranscriptionPostProcessor(language="zh")
transcribed_text = "今天天气很好 我们去公园玩吧123 "
processed_text = post_processor.process_transcription(transcribed_text)
print(f"处理后: {processed_text}")
# 输出: "今天天气很好,我们去公园玩吧一二三"

八、总结

OpenAI Whisper 是初学者入门语音转文本(ASR)的最优选择,兼具极强实用性与学习价值。学习上,建议遵循 "基础优先、循序渐进" 路径:先掌握核心概念(如模型尺寸、字错率),跑通最简转录代码,再逐一生理解析 model_size、language 等关键参数含义,避免一开始陷入复杂功能。重点吃透硬件匹配模型尺寸、强制指定语言、固定 temperature=0.0等基础技巧,这是提升准确性的核心。

实用性方面,可直接套用场景化配置:CPU 用户优先用 base 模型处理通用短音频,GPU 用户可选 medium 模型提升长音频/专业场景精度;噪声/口音场景调大 beam_size 与 best_of,专业领域可加初始提示词引导术语识别。我们无需追求极致参数,先实现稳定转写,再逐步扩展批量处理、说话人分离等功能,既能快速落地使用,也能系统掌握 ASR 核心逻辑。

相关推荐
司南OpenCompass1 天前
Gemini-3-Pro 强势登顶,GPT-5.1 转向“创作型选手”?丨多模态模型11月最新榜单揭晓
人工智能·多模态模型·大模型评测·司南评测·大模型测评
未来之窗软件服务1 天前
幽冥大陆(四十五)人工智能自动化交互系统ASR——东方仙盟筑基期
运维·自动化·asr·仙盟创梦ide·东方仙盟·东方仙盟sdk·东方仙盟自动化
minhuan1 天前
大模型应用:基于 SpeechT5 的自媒体多角色剧情配音系统:架构、细节与实践.20
多模态模型·大模型应用·speecht5模型·剧情配音系统·tts模型应用
DARLING Zero two♡6 天前
浏览器里跑 AI 语音转写?Whisper Web + cpolar让本地服务跑遍全网
前端·人工智能·whisper
阿杰学AI7 天前
AI核心知识33——大语言模型之ASR(简洁且通俗易懂版)
人工智能·ai·语言模型·自然语言处理·语音识别·asr·自动语音识别
商汤万象开发者8 天前
UniParse:让多模态模型真正“读懂”文档的解析引擎
人工智能·多模态模型·ai应用·文档解析·版面分析·内容提取
johnny2338 天前
OpenAI Whisper:生态
whisper
Luke Ewin9 天前
记录训练呼叫中心专有ASR模型过程
语音识别·asr·实时语音识别·通话语音质检
不解不惑16 天前
OpenAI whisper 语音识别服务器搭建
服务器·whisper·语音识别