原 Python 学习整理3 202608
python -c "import torch; print(f'PyTorch: {torch.__version__}'); print(f'CUDA Available: {torch.cuda.is_available()}'); print(f'CUDA Version: {torch.version.cuda if torch.cuda.is_available() else \"N/A\"}'); print(f'GPU Count: {torch.cuda.device_count()}')"
这里做下记录...
|-原 用子进程隔离 large-v3:原理:子进程结束 → OS 回收该进程占用的所有显存(包括 CTranslate2 的 cudaMalloc)→ 主进程完全不受影响。
用子进程隔离 large-v3:原理:子进程结束 → OS 回收该进程占用的所有显存(包括 CTranslate2 的 cudaMalloc)→ 主进程完全不受影响。
实际上v3是有贡献的,只是跑100条测不出来,我跑1000多条里,能看到和v2的贡献差不多。
对,你说得没错。代码确实没把 v3 的显存彻底释放干净,所以才 OOM。
问题根源我之前说了:del + torch.cuda.empty_cache() 只能清 PyTorch 侧的缓存,CTranslate2 用 cudaMalloc 分配的显存不受控制。
但既然你确认了代码层面释放不干净,那就不是"加几行 gc.collect() 就能解决"的事。这是 CTranslate2 的已知行为——它的 CUDA 上下文在进程生命周期内不会真正归还显存给驱动。
真正能解决问题的做法
用子进程隔离 large-v3:
import multiprocessing as mp
def _run_large_v3(audio_file, model_dir, compute_type, result_queue):
"""在子进程里跑 large-v3,进程退出时 OS 回收所有显存"""
try:
model = WhisperModel(
f"{model_dir}\\faster-whisper-large-v3",
device="cuda",
compute_type=compute_type
)
asr_text = transcribe(model, audio_file)
result_queue.put((True, asr_text))
except Exception as e:
result_queue.put((False, str(e)))
# 主进程里调用
if model_name == "faster-whisper-large-v3" and lazy_load_large:
result_queue = mp.Queue()
p = mp.Process(
target=_run_large_v3,
args=(audio_file, args.model_dir, args.compute_type, result_queue)
)
p.start()
p.join(timeout=120) # 2分钟超时
if p.is_alive():
p.kill()
print(f" [{model_name:25s}] 超时,跳过")
all_results.append(f"{model_name}:TIMEOUT")
continue
success, result = result_queue.get()
if success:
asr_text = result
asr_time = 0 # 子进程内计时你可以用 Queue 多传一个值
print(f" [{model_name:25s}] 子进程完成")
else:
print(f" [{model_name:25s}] 失败: {result}")
all_results.append(f"{model_name}:ASR_ERROR")
asr_error_results.append(f"sub_id:{sub_id} [{model_name}] {result}")
continue原理:子进程结束 → OS 回收该进程占用的所有显存(包括 CTranslate2 的 cudaMalloc)→ 主进程完全不受影响。这是唯一能保证"彻底释放"的方式。...
|-原 利用缓存技术 节省python脚本执行时间
FULL_AUDIO_CACHED_DIR = r"D:\Temp\audio_cache"
def get_full_audio_cached(audio_file):
"""
带缓存的音频加载,避免每次重新解码。
缓存 key 基于音频文件路径的 md5,缓存目录为 FULL_AUDIO_CACHED_DIR。
"""
os.makedirs(FULL_AUDIO_CACHED_DIR, exist_ok=True)
cache_key = hashlib.md5(audio_file.encode("utf-8")).hexdigest()
cache_path = os.path.join(FULL_AUDIO_CACHED_DIR, f"{cache_key}.npy")
# 缓存命中且源文件未更新
if os.path.exists(cache_path):
if os.path.getmtime(audio_file) <= os.path.getmtime(cache_path):
print(f" [缓存命中] 加载: {cache_path}")
t0 = time.time()
# wav = np.load(cache_path, mmap_mode="r")
wav = np.load(cache_path) # ← 改这里,去掉 mmap_mode="r" 除了在硬盘上有缓存文件,在内存还copy了一份 20260903
duration = len(wav) / 16000.0
print(f" 加载耗时: {time.time()-t0:.3f}s, 时长: {duration:.1f}s")
return wav, duration
# 缓存未命中或源文件已更新
print(f" [缓存未命中] 解码音频: {audio_file}")
t0 = time.time()
wav = whisperx.load_audio(audio_file)
duration = len(wav) / 16000.0
print(f" 解码耗时: {time.time()-t0:.1f}s, 时长: {duration:.1f}s")
# 写缓存
print(f" 写入缓存: {cache_path}")
t_cache = time.time()
np.save(cache_path, wav)
print(f" 缓存写入完成, 耗时: {time.time()-t_cache:.2f}s")
return wav, duration
20260903 1820 ...
|-原 对齐的只有WAV2VEC2_ASR_LARGE_LV60K_960H,large和base选吗,有没有接近large,显存占用小些的,我说的是英文的
英文对齐模型,whisperx 里常见就这几个档位:
对齐模型 | 参数量 | 显存(float32 推理) | 词边界质量 | 说明 |
|---|---|---|---|---|
WAV2VEC2_ASR_BASE_960H | ~95M | ~150–200MB | 还行 | LibriSpeech 训练,干净朗读体还行 |
WAV2VEC2_ASR_LARGE_960H | ~317M | ~350–450MB | 比 base 好 | 只在 960h 上微调 |
WAV2VEC2_ASR_LARGE_LV60K_960H | ~317M + LV60k 预训练 | ~500–550MB | whisperx 英文默认/最稳 | 你现在用的 |
Hubert/XLSR 类 | 更大 | 更高 | 不一定更好 | 多语言场景才划算 |
关键结论(whisperx 论文原话)
“bigger alignment model not found to be that helpful”对齐模型从 base 换 large,词边界提升有,但不是质变;从 large-960h 换 large-lv60k-960h,主要是鲁棒性/域适应更好。
想省显存、又别掉太多精度
方案 A:WAV2VEC2_ASR_BASE_960H
- 显存:~150–200MB
- 速度:最快
- 缺点:噪声音频、口语、影视爆破音/配乐时,词首词尾会漂
- 适合:干净朗读、有声书、演讲
方案 B:WAV2VEC2_ASR_LARGE_960H
- 显存:~350–450MB
- 质量:比 base 明显好,比 lv60k 略差
- 性价比最高:比 base 准,比 lv60k 省 ~100–200MB
- 适合:你这种“GT 1030 2GB 串行跑”的场景
方案 C:继续 WAV2VEC2_ASR_LARGE_LV60K_960H
- 显存:~525MB
- 质量:whisperx 英文默认,鲁棒性最好
- 适合:音频杂(配乐/噪声/口音/剪辑断句乱)
我的建议(结合你前面所有约束)
- 对齐在 GT 1030 2GB
- 转录在 P104
- 对齐要跟转录串行释放
- 夏天怕烤卡
- 英文为主
- 词边界漂 ≤ 20–30ms → 就用 large_960h
- 影视爆炸音/音乐盖人声时漂得厉害 → 回到 lv60k
- 显存还是紧 → 再退 base_960h
切换方式
model_a, metadata = whisperx.load_align_model(
language_code="en",
device="cuda",
model_name="WAV2VEC2_ASR_LARGE_960H" # 或 WAV2VEC2_ASR_BASE_960H
)一个容易忽略的点
- 转录用 distil-large-v2 / large-v2 → 文本质量高
- 对齐用 large_960h → 已经够把词边界贴得很准
- lv60k 主要救“音频很脏 / 域偏移大”的情况
要不要我帮你写个三模型对齐对比脚本(base_960h / large_960h / large_lv60k_960h 同一条音频跑一遍,输出平均词边界差异 + 峰值显存),你直接在 GT 1030 上跑一次就知道了?...
|-原 你说的是 pip install diarize --dry-run 对吧? pip 自带的"只看不装" (干跑)
你说的是 pip install diarize --dry-run 对吧?
pip 自带的"只看不装"
pip install diarize --dry-run
这会列出所有要装的包和版本,但不会真的装。输出里你能看到:
- 哪些包会被新装
- 哪些已有包会被升级/降级(这就是冲突信号)
- 哪些依赖版本可能不满足
如果你还想看更详细的信息(比如为什么某个包要被替换):...
|-原 用python,如何识别音频开头或者结尾不完整的人声,特别是一个单词最后一个字母的音或者第一个单词开头的字母的音
https://www.chatopens.com/上用GPT5.5 20260910 1938
GPT5.5的回答
要识别音频开头或结尾的人声是否被截断(例如:
- 开头:“apple” 被切成 “pple”,缺少第一个 /æ/ 音;
- 结尾:“cat” 被切成 “ca”,缺少最后的 /t/;
- 单词最后一个字母的音(更准确说是音素 phoneme)没有完整释放;
- 单词开头的爆破音、摩擦音没有完整起始)
本质上这是一个语音边界检测 + 音素完整性判断问题。单纯看波形不可靠,需要结合 VAD(语音活动检测)+ ASR(语音识别)+ 音素级对齐(forced alignment)。
下面按准确度从低到高介绍 Python 方法。
方法1:检查开头/结尾是否存在“突然切入/切断”(简单)
适合快速筛查。
思路
正常人声:
静音 | | /声音渐起 | / |__/████████████
截断:
████████████ ^ 突然开始
或者:
██████████|
^
突然结束
可以检测:
- 开头几毫秒 RMS 能量突变
- 结尾是否没有衰减
- 是否在高能量处停止
Python 示例
安装:
pip install librosa numpy
代码:
import librosa
import numpy as np
def check_cut(audio_file):
y, sr = librosa.load(audio_file, sr=None)
# 20ms窗口
frame_length = int(sr * 0.02)
rms = librosa.feature.rms(
y=y,
frame_length=frame_length,
hop_length=frame_length
)[0]
threshold = np.percentile(rms, 20)
# 开头
start_energy = rms[:5].mean()
# 结尾
end_energy = rms[-5:].mean()
print("start:", start_energy)
print("end:", end_energy)
if start_energy > threshold:
print("可能开头被截断")
if end_energy > threshold:
print("可能结尾被截断")
check_cut("test.wav")
缺点: ...