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

大模型应用:语音转文本(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. 示例代码

# 第一步:导入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。

整体参数示例:

# 优化中文转写准确性的基础配置 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输出示例:

# 在上方代码的函数中添加以下代码,打印段落时间戳 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:通用安静短音频(如语音消息、录音笔记)

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:噪声 / 口音重音频(如户外录音、方言口音)

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 分钟)

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,获取每个单词的精准时间戳(如制作字幕):

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

输出示例:

[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. 批量转录

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

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实现“说话人 + 文本 + 时间戳”的转写

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)

输出示例:

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

六、辅助优化

1. 音频预处理(降噪)

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

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. 简单后处理(校正错字)

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

七、应用示例

1. 完整的应用实例

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

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. 转录后处理

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

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).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 核心逻辑。

Read more

AI魔术师:基于视觉的增强现实特效

AI魔术师:基于视觉的增强现实特效

AI魔术师:基于视觉的增强现实特效 * 一、前言 * 二、AR 与视觉 AI 的技术基石 * 2.1 增强现实的核心概念 * 2.2 计算机视觉与 AI 的技术融合 * 2.3 技术栈选型与环境搭建 * 三、视觉 AR 的核心技术解析 * 3.1 相机标定与坐标系统 * 3.1.1 相机标定原理 * 3.1.2 标定代码实现 * 3.2 实时特征跟踪技术 * 3.2.1 ORB 特征跟踪原理 * 3.2.2 单目视觉里程计实现 * 3.3 语义分割与虚实融合

FPGA 跨时钟域 CDC 处理:3 种最实用的工程方案

本人多年 FPGA 工程与教学经验,今天跟大家聊一个重点——跨时钟域 CDC,这可是项目里最容易出玄学 bug、最难复现、最难定位的一类问题,新手必踩坑,老手也得谨慎! 还是老规矩,不搞虚的、不扯理论,只给大家工程里真正在用、稳定可靠、可直接复制上板的3种方案,不管是自学、做项目,还是面试,都能用得上、能拿分。 1. 什么是跨时钟域 CDC? 不用记复杂定义,简单说清楚3个关键点,就完全够用: * 核心场景:信号从一个时钟域(比如clk_a)传到另一个时钟域(比如clk_b); * 触发条件:两个时钟的频率不同,或者相位无关(没有固定的时间关系); * 直接后果:如果不做处理,直接打拍会出现亚稳态,进而导致数据错误,严重的还会让整个系统死机。 划重点:只要是多时钟系统,就必须做 CDC 处理,

手把手教你配置飞书 OpenClaw 机器人,打造企业级 AI 智能助手

手把手教你配置飞书 OpenClaw 机器人,打造企业级 AI 智能助手

目标:在飞书(Feishu/Lark)中添加 OpenClaw 机器人,实现 7×24 小时 AI 智能对话与自动化办公。 OpenClaw GitHub | feishu-openclaw 桥接项目 想让你的机器人具备语音交互能力?试试 Seeed Studio 的 ReSpeaker 系列吧! 我会后续出reSpeaker XVF3800与Openclaw联动实现语音输入的教程,完全开放源码。 reSpeaker XVF3800 是一款基于 XMOS XVF3800 芯片的专业级 4 麦克风圆形阵列麦克风,即使在嘈杂的环境中也能清晰地拾取目标语音。它具备双模式、360° 远场语音拾取(最远 5 米)、自动回声消除 (AEC)、自动增益控制 (AGC)、声源定位 (DoA)、去混响、波束成形和噪声抑制等功能。

仿生新势力:Openclaw开源仿生爪,如何革新机器人抓取?

仿生新势力:Openclaw开源仿生爪,如何革新机器人抓取?

仿生新势力:Openclaw开源仿生爪,如何革新机器人抓取? 引言 在仓储、农业乃至家庭服务中,机器人如何像猫一样灵巧、自适应地抓取千变万化的物体?这曾是行业难题。如今,一个名为 Openclaw 的开源仿生机械爪项目,正以其独特的被动适应性设计和亲民的成本,在机器人末端执行器领域掀起波澜。本文将深入解析Openclaw的仿生奥秘、实现原理、应用场景及未来布局,带你全面了解这款来自开源社区的“仿生新势力”。 一、 核心揭秘:从猫爪到机械爪的实现原理 本节将拆解Openclaw如何将生物灵感转化为工程现实。 1. 仿生学设计理念 Openclaw的核心灵感源于猫科动物爪部。当猫抓取物体时,其爪趾会自然地包裹贴合物体表面,这种能力主要依赖于其肌腱和骨骼的被动结构,而非大脑的实时精密控制。Openclaw借鉴了这一思想,核心是被动适应性机制。它无需依赖复杂的传感器反馈和实时力控算法,仅凭精巧的机械结构即可根据物体形状自动调整接触点和抓取力,从而极大地简化了控制系统。 配图建议:猫爪与Openclaw的对比图,或Openclaw抓取不同形状物体的动态示意图。 2. 欠驱动与