2026-09-22 【音色评估】项目拆解(二):环境搭建与跑通

3910 字
20 分钟
2026-09-22 【音色评估】项目拆解(二):环境搭建与跑通

系列定位:5 篇走完「调研摸底 → 环境跑通 → 代码拆解 → 调试改参 → 复盘沉淀」。 本篇目标:把项目从”一堆压缩包”变成「一条命令能跑、一个接口能验、一份环境手册能复刻」的可用状态。 与前面的衔接:第一篇定了”项目是什么”(双路线融合、四模块分工);第 1.5 篇补了底层知识(STFT / 梅尔刻度 / Fisher 判别 / fbank / ViT)。本篇不再重复概念,只讲实操与排错。 说明:本篇所有命令、报错、数字都来自真实复现过程(Windows + Python 3.10,项目自带 html.zip 解压版)。


0. 先把”跑通”定义清楚#

新手最大的误区是:窗口没报错 = 跑通了。这不成立。本文用的判据是三条,缺一不可:

判据怎么验本项目实测结果
① 服务起来了curl 首页状态码GET / → 200
② 核心接口能出结果用一段真实音频 POST 核心接口POST /api/evaluate → 200,返回 scores / weights_scores / *_features
③ 结果数值合理看返回里的分数与等级是否在预期范围11 类概率、三维等级 1–3、物理分数量级正确

附带一条经验:“能跑”和”能信”是两件事。第 ② 条只证明链路通了;第 ③ 条才说明你没把张量形状、采样率、标签顺序之类搞错。这就是为什么后面要用”对拍”再验一遍。


1. 摸清”到底要装什么”(不要照着 requirements 盲装)#

项目 requirements.txt 写的是声明,而实际运行取决于 import 链。做法:把代码里所有 import 抓出来。

Terminal window
# 在解压后的 html/ 目录里
grep -rhoE "^\s*(import|from) [A-Za-z_][A-Za-z0-9_.]*" --include=*.py . \
| sed 's/^\s*//' | sort -u

这一步在本项目里抓到两个”requirements 里看不出”的关键点:

发现为什么要紧
html/model/model_utils/stat.py 里 from sklearn import metricsscikit-learn 是运行必需,不在直觉里的”音频/深度学习”清单上
timm 的顶层 import 会连带 torchvision(layers/create_norm.py)只装 timm 不装 torchvision,import timm 直接失败

顺手做的第二件事:确认哪些依赖是”训练用”、哪些是”跑推理用”。本项目里 TimbreMask/(训练框架)与 frenquence_check/(科研脚本)都不参与 Web 运行,装环境时不要被它们的 requirements 带跑偏。


2. 建一个干净环境(不要动系统 Python)#

本机的现实:系统 Python 3.10 存在,但 pip 模块是坏的(python -m pip 报 No module named 'pip',pip.exe 也起不来),且没有 conda / uv。所以第一个坑就是”连包管理器都没有”。

Terminal window
# 用系统 Python 造一个自带 pip 的虚拟环境(venv 会自带 ensurepip)
python -m venv .venv_html
.\.venv_html\Scripts\python.exe -m pip --version # pip 22.0.4 ✓
# Linux/macOS
python3 -m venv venv && ./venv/bin/python -m pip --version

三条硬经验:

  1. 虚拟环境放在项目目录内(.venv_html/)。好处:路径可预测、便于写启动脚本、删库跑路只删一个文件夹;本项目用它同时服务 html/(评估网站)和 physics_lab/(实验台)。
  2. 别用系统 Python 装依赖:一旦装坏,全机项目一起坏,而且没法回滚。
  3. 环境装完先跑一次 import 自检,别等到启动服务才发现少包:
Terminal window
python -c "import torch, torchaudio, librosa, soundfile, flask, timm, sklearn, numpy, scipy; print('imports OK')"

3. 装依赖:分三批,别一把梭#

按”编译依赖强度 / 下载渠道”分批,出问题好定位:

Terminal window
# 第 1 批:深度学习核心(必须走官方 CPU 索引,见 4.2)
pip install torch torchaudio torchvision --index-url https://download.pytorch.org/whl/cpu
# 第 2 批:音频与科学计算
pip install librosa soundfile numpy scipy scikit-learn numba
# 第 3 批:Web 与工具
pip install flask python-dotenv

本项目最终验证可用的版本组合(建议直接固化):

包版本备注
Python3.10.33.8–3.11 均可
torch / torchaudio2.8.0+cpu必须同版本号配对
numpy / scipy1.26.4 / 1.9.3scipy 1.9 会警告 numpy 上界,仅警告
librosa0.10.2.post1依赖 numba/llvmlite,三者有耦合
soundfile0.14.0(win_amd64)必须带 DLL 的那个 wheel
timm0.9.16顶层 import 依赖 torchvision
flask3.1.3

4. 网络与镜像:这一步决定你是 10 分钟还是 3 小时#

4.1 先测速,再选源(别凭感觉)#

本项目实测(同一台机器):

源速度结论
pypi.tuna.tsinghua.edu.cn≈ 1.2 MB/s首选
pypi.org≈ 34 KB/s兜底
download.pytorch.org≈ 42 KB/s只在装 torch CPU 版时用

4.2 两个大坑(都会让你卡很久)#

坑 A:pip 的远程解析在本机会”空转”。 表现为 pip 进程 CPU 一直涨、日志停在 Looking in indexes...、下载目录不增长。本项目里它空转了十几分钟、只下到 14.5 MB。

坑 B:大 wheel 的连接会被中断。 例如 torch-2.x 这种上百 MB 的文件,下载到一半停住,pip 只会重试整份文件。

解决方案:手动下载 wheel(分块 + 断点续传)+ 离线解包安装。 这是本篇最值得留下的一段代码:

# save as dl_wheels.py ;用法:python dl_wheels.py <index-url> <包名...>
import os, re, sys, time, zipfile, urllib.request
def pick(base, pkg):
"""从 simple 索引里挑一个匹配当前平台的 wheel(优先 cp310 win_amd64,其次 py3-none-any)"""
html = urllib.request.urlopen(f'{base}/simple/{pkg}/', timeout=30).read().decode('utf-8', 'ignore')
hrefs = [m.group(1).split('#')[0] for m in re.finditer(r'href="([^"]*\.whl[^"]*)"', html)]
best, score = None, -1
for h in hrefs:
fn = h.split('/')[-1]
s = 3 if 'cp310-cp310-win_amd64' in fn else (2 if 'none-any' in fn else 0)
if s > score:
score, best = s, h
return best
def download(url, dest, chunk=4 << 20, tries=8):
"""按 4MB 分块下载:每块单独一个请求,断了只重试这一块"""
def total():
req = urllib.request.Request(url, headers={'Range': 'bytes=0-0'})
with urllib.request.urlopen(req, timeout=40) as r:
return int(r.headers['Content-Range'].split('/')[1])
n = total(); have = os.path.getsize(dest) if os.path.exists(dest) else 0
with open(dest, 'ab') as f:
while have < n:
end = min(have + chunk - 1, n - 1)
for _ in range(tries):
try:
req = urllib.request.Request(url, headers={'Range': f'bytes={have}-{end}'})
with urllib.request.urlopen(req, timeout=120) as r:
data = r.read()
f.write(data); f.flush(); have += len(data); break
except Exception:
time.sleep(1.5)
else:
raise IOError('stuck at %d' % have)
return n
if __name__ == '__main__':
base, pkgs = sys.argv[1], sys.argv[2:]
os.makedirs('wheels', exist_ok=True)
for pkg in pkgs:
h = pick(base, pkg); fn = h.split('/')[-1]
url = base + '/' + h.lstrip('./').replace('../', '')
dest = os.path.join('wheels', fn)
print(pkg, '->', fn, download(url, dest), 'bytes')

装的时候直接解包(等价于 pip 安装,但跳过元数据解析,因此不会被”Requires-Python 不满足”之类的元数据问题一把拒绝整批):

import glob, os, zipfile
SP = r'.venv_html\Lib\site-packages' # Linux: .venv/lib/python3.x/site-packages
for wh in sorted(glob.glob('wheels/*.whl')):
if os.path.basename(wh).startswith(('torch-', 'torchaudio-')): # 已装的可跳过
continue
zipfile.ZipFile(wh).extractall(SP)
print('installed', os.path.basename(wh))

⚠️ 用这种方式装,依赖不会自动补齐(这就是第 5 节里几个 ModuleNotFoundError 的来源)。代价换来的好处是:100% 可控、可断点续传、可离线复现。


5. 版本地雷清单(全部是本项目真实踩过的)#

按”现象 → 根因 → 解决”给表,遇到直接查:

#现象根因解决
1No module named 'pip'系统 Python 的 pip 被破坏用 venv(自带 pip)
2pip 卡在 Looking in indexes 空转本机 pip 远程解析异常手动下载 wheel + 离线解包(4.2)
3Linux 上 pip install torch 拉 2.4 GBPyPI 的 Linux torch 是 CUDA 构建加 --index-url .../whl/cpu
4ImportError: TorchCodec is required for load_with_torchcodectorchaudio ≥2.9 默认改用 TorchCodec降到 torchaudio 2.8(sox 后端)
5Could not load this library: libtorchcodec_core7.dll装了 torchcodec 但原生 DLL 起不来直接移除 torchcodec,用 2.8
6RuntimeError: operator torchvision::nms does not existtorchvision 新版 _C_stable 与 torch CPU 构建不兼容见下方”垫片”方案
7ModuleNotFoundError: torchvision(来自 timm)timm 顶层 import 需要 torchvision装配套 torchvision,或写最小垫片
8No module named 'typing_extensions'--no-deps 安装 torch 没带依赖补 typing-extensions 等
9ImportError: cannot import name 'TypeIs' from 'typing_extensions'typing_extensions 版本过旧(rc 版)升级到 4.16.0
10ValueError: invalid literal for int() with base 10: '6rc1'numba 解析 colorama 0.4.6rc1 版本号崩溃装稳定版 colorama 0.4.6
11No module named 'decorator' / 'cloudpickle'librosa / joblib 的隐式依赖补装
12sndfile library not found / cannot load library 'libsndfile.dll'装了不带 DLL 的 soundfile wheel用 soundfile-0.14.0-py2.py3-none-**win_amd64**.whl(内含 _soundfile_data/libsndfile_x64.dll);Linux 装 libsndfile1
13A NumPy version >=1.18.5 and <1.26.0 is required ...(仅警告)scipy 1.9 元数据写的上界可忽略;要干净就 numpy 1.25.x 或升级 scipy
14torch.load 报 “Weights only load failed”PyTorch ≥2.6 默认 weights_only=True显式 torch.load(..., weights_only=False)
15装依赖奇慢(10 分钟才拷完一个包)项目在网盘同步目录里,小文件写入极慢加 --no-compile、少装可选项;必要时换本地盘

第 6 项的”垫片”方案:当 torchvision 的原生扩展与 torch CPU 构建对不上、而项目运行时并不真的调用 torchvision(只是 timm 顶层 import 需要它)时,可以放一个最小占位模块,把 import 链喂饱即可:

.venv_html/Lib/site-packages/torchvision/__init__.py
__version__ = '0.0.0-shim'
# ops/misc.py 里提供 timm 需要的符号
class FrozenBatchNorm2d: # timm.layers.create_norm 只在本项目不走的路径上引用它
def __init__(self, num_features, eps=1e-5): ...
# datasets/__init__.py 里给 timm.data.dataset_factory 需要的桩类
CIFAR10 = CIFAR100 = MNIST = KMNIST = FashionMNIST = ImageFolder = object
# transforms/functional.py 里提供 InterpolationMode 枚举

这不是”最佳实践”,而是”在受限环境下让 import 链通起来的止血方案”;能装配套 torchvision 时优先装配套。关键是:要清楚自己在绕什么,并在文档里写清楚。


6. 数据与资源准备(跑通前必须核对的两件事)#

6.1 模型权重:先校验字节数,再谈运行#

Terminal window
stat -c%s html/model/checkpoint-300.pth # Linux
(Get-Item html\model\checkpoint-300.pth).Length # Windows
# 期望:1024918304

本项目这个文件约 1.02 GB。顺便一个很有用的认知(复现时能省很多疑惑):

ViT-B 约 86M 参数,fp32 权重只有 ≈345 MB,而文件是 3 倍体量 → 里面约 2/3 是 AdamW 优化器状态。所以:① 加载要 10–20 秒;② 发布时可以只保存 model 权重把体积压到 1/3。 校验字节数这一步,能直接排除”下载/解压被截断”这一类最难查的问题。

6.2 测试音频:自己造一条”确定性”的#

用真实录音做回归测试会引入变量(内容/环境/设备)。本项目用一段合成音频做冒烟测试(12 秒、分段基频 + 谐波 + 少量噪声、16 kHz/16 bit):

import numpy as np, soundfile as sf
sr, dur = 16000, 12
t = np.arange(sr * dur) / sr
seg = (t // 2 % 3) + 0.5 # 每 2 秒换一个基频
f0 = 110 * seg * (1 + 0.03 * np.sin(2 * np.pi * 5 * t))
y = sum((1.0 / h) * np.sin(2 * np.pi * f0 * h * t + 0.5 * h) for h in range(1, 7))
y = 0.35 * y + 0.06 * np.random.default_rng(42).normal(size=len(t))
sf.write('test_input.wav', np.clip(y, -1, 1).astype('float32'), sr, subtype='PCM_16')

它的价值:波形已知、时长已知、每段的基频已知。一旦推理输出与上次不同,就能确定是环境/代码变了,而不是音频变了。


7. 首次运行:“跑通三判据”逐条验#

Terminal window
cd html
..\.venv_html\Scripts\python.exe app.py # 或 python app.py
# 控制台出现:Running on http://127.0.0.1:5001
Terminal window
# 判据 ① 首页
curl -s -o /dev/null -w "首页 HTTP %{http_code}\n" http://127.0.0.1:5001/
# → 首页 HTTP 200
# 判据 ②③ 核心接口(multipart 传音频)
curl -s -X POST http://127.0.0.1:5001/api/evaluate -F "audio=@../test_input.wav" | head -c 400

本项目实测返回(片段):

{"scores": {"thickness": 3, "solidity": 3, "brightness": 3},
"scores_change": {"thickness": 0, "solidity": 0, "brightness": 0},
"weights_scores": {"thickness": 80.29, "solidity": 541.92, "brightness": 9400.03},
"thick_features": [ ... 12 维 ... ]}

首请求耗时:模型加载 + 第一次推理约 9.7 秒(CPU),之后的请求快得多。把这个数字记下来 —— 它是你判断”环境是否正常”的基准线。

顺手记两条”跑通即暴露”的问题,它们在这一步才会出现(第三篇会详细拆):

  1. 浏览器录音上传必 500:MediaRecorder 录出的是 WebM/Opus,而 librosa/libsndfile 不认这个容器 → 需要在前端转成 WAV再上传;
  2. 物理链路在某个分支崩:模型判 C 级且物理分偏高时会进”C→B 跨级”分支,两组权重与特征长度不匹配 → ValueError: operands could not be broadcast together with shapes (12,) (13,)。

这正是”跑通”的边界:能跑 ≠ 所有分支都跑得通。第三篇拆代码、第四篇做调试,都是在补这个差。


8. 环境固化:让”下次还能量出来”#

8.1 锁版本(比 requirements 更硬)#

Terminal window
pip freeze > requirements.lock.txt

把第 5 节那张版本表写进项目 README,标注”实测可用组合”。深度学习生态半年一变,不锁版本 = 半年后重装跑不起来。

8.2 一键启动脚本(3 个真实的 Windows 编码坑)#

本项目做启动器时踩了三个坑,跨平台都值得注意:

现象根因解决
'ython.exe' 不是内部或外部命令.bat 存成 UTF-8,而 cmd 按 GBK(936) 解析 → 中文字节”吃掉”了相邻的 ASCII 字符(python.exe → ython.exe),变量 %PY% 随之损坏,最后回退到没有依赖的系统 Python,报 No module named 'flask'批处理内容全用纯 ASCII(提示信息写英文),中文只出现在文件名
call "...\?????.bat" 找不到目标文件内容里写了中文文件名,而文件按 ASCII 保存 → 中文变成 ?真实脚本用 ASCII 名(start_web.bat),中文名文件只作入口
控制台中文乱码Python 输出 UTF-8,cmd 按 GBK 显示脚本头部加 chcp 65001 >nul,并设 PYTHONUTF8=1 / PYTHONIOENCODING=utf-8

可复用的启动器骨架(纯 ASCII):

Terminal window
@echo off
chcp 65001 >nul
cd /d "%~dp0"
set "PY=%~dp0..\.venv_html\Scripts\python.exe"
set PYTHONUTF8=1
if not exist "%PY%" (
echo [ERROR] venv python not found: "%PY%"
pause
exit /b 1
)
echo Open in browser: http://127.0.0.1:5001
"%PY%" app.py
pause >nul

8.3 自检脚本(把”跑通”变成可重复动作)#

本项目留了两个,思路可以直接搬:

脚本作用
physics_lab/selftest.py不开浏览器跑完全部实验,打印关键数字
physics_lab/diag_bad_inputs.py构造 13 种异常输入(空文件 / 二进制 / WebM / 全静音 / 超短 / 削波…),检查接口是否都给出可读的 400 而不是 500

这类”异常输入清单”值得每个项目都建一份 —— 它把”我试过了报错”变成”我知道它为什么报错、报什么、怎么提示用户”。


9. 本篇产出物(可直接复用的东西)#

  • ✅ 一个 100% 可运行版本:html/ + .venv_html/(已验证版本组合)
  • ✅ 环境搭建手册:第 3、5 节(分三批安装 + 15 条地雷清单)
  • ✅ 离线安装资产:wheels/ 目录(全部依赖 wheel,断网也能装)
  • ✅ 可复用下载器:dl_wheels.py(分块 + 断点续传)
  • ✅ 测试资产:test_input.wav(确定性合成音频)
  • ✅ 启动器:启动音色评估网站.bat / 启动物理链路实验台.bat(纯 ASCII + 绝对路径 + UTF-8 控制台)
  • ✅ 自检与异常清单脚本:selftest.py / diag_bad_inputs.py

10. 报错速查(按症状索引)#

症状先看
ModuleNotFoundError: No module named 'xxx'是不是用 --no-deps/离线解包装的?按第 5 节补依赖
import 成功但启动就退端口占用(netstat -ano | findstr :5001);或 venv 路径写错
首页 200 但接口 500打开后端日志看 traceback 最后几行;再按第 7 节两条典型问题排查
接口 500 但错误信息为空典型是 NoBackendError(无消息)→ 说明音频容器不支持(WebM/M4A),需要前端转码
结果里出现 NaN分母为 0(静音)导致;NaN 不是合法 JSON,必须显式兜底
分数是负数或几千分不是崩溃,而是”线性映射外推”(第四篇会专门拆这个)
加载模型极慢1 GB checkpoint 的正常表现;首次 10–30 秒

11. 本篇收尾与下一篇预告#

收尾目标达成:项目从”压缩包”变成了「有环境、有版本锁定、有一键启动、有自检脚本、有验证记录」的可运行版本;同时明确了”跑通”的边界 —— 接口通了,但某些分支还没被覆盖(浏览器录音、跨级修正)。

**下一篇(第三篇:逐行拆解代码)**会顺着本篇留下的两个”未覆盖分支”往下挖:

  1. 从 app.py → talk.py → inference.py → speechprocess.py 的完整调用链,每段的入参/出参/形状变化;
  2. 两条”同一项目里并存”的音频管线(推理侧 fbank vs 物理侧 STFT 频段占比)为什么要分开;
  3. 物理链路的六套模板与 bands_change 常量,到底是从哪几个科研脚本”抄”过来的;
  4. 给核心文件加中文注释的方法:“把别人的代码变成自己能讲出来的代码”。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

2026-09-22 【音色评估】项目拆解(二):环境搭建与跑通

本文链接

https://study.aokana.cn/posts/2026-09-22-音色评估-项目拆解-二-环境搭建与跑通-160/
作者
乌冬面
发布于
2026-09-22
许可协议
CC BY-NC-SA 4.0
相关文章智能推荐
1
2026-09-23 【音色评估】项目拆解(三):逐行拆解代码
项目拆解系列定位:5 篇走完「调研摸底 → 环境跑通 → 代码拆解 → 调试改参 → 复盘沉淀」。 本篇目标:把"能跑通的代码"变成"能讲清楚的代码"——不看源码也能说出每一步在做什么、数据形状怎么变、每个常量从哪来。 与前面的衔接:第一篇给了全景(双路线融合、四模块);第 1.5 篇给了底层知识(STFT/梅尔/fbank/Fisher/ViT);第二篇把环境跑通了,并留下两个"跑通即暴露"的 bug。
2
2026-09-24 【音色评估】项目拆解(四):调试改参与变式测试
项目拆解系列定位:5 篇走完「调研摸底 → 环境跑通 → 代码拆解 → 调试改参 → 复盘沉淀」。 本篇目标:从"看懂代码"进阶到"能改、敢改、知道改了会怎样"。所有实验不需要新数据,全部用项目自带音频即可复跑。 与前面的衔接:第三篇把两条管线的数据契约讲透了(15 维特征、六套模板、1213 位置编码)。本篇就沿着这些契约故意去改,看哪一类参数会把系统改坏、哪一类安全。 配套可运行试验台:tuner_l
3
2026-09-11 【音色评估】项目拆解(一):前置调研与全局认知
项目拆解系列定位:本系列共5篇文章,从零完整复现并深度吃透「声音色客观评估系统」,覆盖「调研摸底→环境跑通→代码拆解→调试改参→复盘沉淀」全流程。 目录 1. 项目定位:业务背景与核心价值 2. 前置基础:音色三维度的声学本质 2.1 数字音频基础概念 2.2 语音声学核心特征 2.3 「厚薄/明暗/虚实」的声学对应 3. 系统核心架构:双路线融合设计全解析 3.1 整体流程总览 3.2 路线一:物理声学
4
2026-09-14 【音色评估】项目拆解(一点五)前置基础知识全解
项目拆解系列定位:本系列共 5 篇文章(第 1 篇前置调研 → 第 2 篇环境跑通 → 第 3 篇代码拆解 → 第 4 篇调试改参 → 第 5 篇复盘沉淀)。 本篇是第 1 篇与第 2 篇之间的"知识补丁":第 1 篇告诉你"项目是什么",但里面出现的采样定理、STFT、梅尔刻度、Fisher 判别、ViT、MAE、BCE、mAP、ffbank、patch、AMP、DDP 等概念,如果基础不牢,第 2 篇
5
2026-10-25 【泛知碎藏】让真话更容易说出口:从六种聊天方法,理解一个人为什么愿意多说一点
泛知碎藏本文由 B 站创作者「站在外面的Q」的视频《话术博弈》如何优雅的套出对方的真心话?引出。下文保留视频的六条方法线索,研究证据、应用边界和综合讨论为补充延伸。除明确标注的原视频示例外,文中新增的日常情境与对话均为帮助理解而构拟,并非真实个案;真实研究会交代其研究对象与结论范围。 为什么越想问清楚 越容易听到场面话 “你到底怎么想的?”有时是一句关心,有时却像一张必须马上交卷的考卷。朋友说新工作“还
随机文章随机推荐

一起讨论,一起进步

交流想法,也欢迎提出建议。

评论
游客发言

讨论区

评论加载中…

Profile Image of the Author
乌冬面
形而上学,不行退学。
bilibiliSteamMyGameBlog
公告
很难说这是个有用的网站。 或许会存放大量不明所以的东西。
分类
标签
最新动态
站点统计
文章
41
分类
9
标签
45
总字数
218,155
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.14.5
文章许可
CC BY-NC-SA 4.0