前言
最近接到了一个数据标注需求:需要对大量的音频进行标注。要求输出的 JSON 格式非常复杂,包含对话轮次、角色(A/B)、情感强度、语调、转写文本等。
起初我尝试手动听写 + Excel 记录,效率低到令人发指。后来折腾了一套 Label Studio + OpenAI Whisper 的自动化方案,效率直接提升 80%!
这篇博客记录了整个部署过程中遇到的巨多坑(尤其是 Windows 路径、404 报错、JSON 格式清洗),以及最终的完美解决方案。
一、 环境准备与安装
为了不污染电脑的 Python 环境,强烈建议使用 Conda。
1.1 创建虚拟环境
不要直接装在 base 环境里,否则以后包冲突会很麻烦。
codeBash
# 创建一个 python 3.9 的环境(Label Studio 对 3.8/3.9 支持最好)
conda create -n ls_env python=3.9
# 激活环境
conda activate ls_env1.2 安装核心软件
codeBash
# 安装标注平台
pip install label-studio
# 安装 AI 语音转文字库 (用于预标注)
pip install openai-whisper
conda install ffmpeg # Whisper 依赖这个处理音频二、 Label Studio 启动与配置 (Windows 避坑指南)
2.1 编写启动脚本 (坑点 1:中文乱码与本地权限)
直接命令行启动虽然行,但每次都要打一堆字,而且 Windows 下默认编码会导致中文报错。我写了一个 .bat 脚本来实现一键启动。
启动标注.bat:
codeBatch
@echo off
chcp 65001 >nul :: 修复中文乱码
:: 开启本地文件读取权限(必须开!否则后面加载本地音频会报 404)
set LABEL_STUDIO_LOCAL_FILES_SERVING_ENABLED=true
set LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT=D:\
echo 正在启动 Label Studio...
call "D:\conda\Scripts\activate.bat" ls_env :: 自动激活环境
cd /d %~dp0
label-studio --data-dir ./my_data :: 指定数据存放在当前目录,不乱跑
pause2.2 配置 XML 模版 (实现多轮对话标注)
需求是“多轮对话”,Label Studio 默认的音频模板只能标整段。我们需要用 Regions (区域标注) 功能。
Settings -> Labeling Setup -> Custom Template 代码:
(代码太长此处略,核心逻辑是使用 <Labels> 做说话人切分,<TextArea perRegion="true"> 做分段转写)
踩坑记录 (坑点 2:Validation Error)
现象:修改 XML 时提示 Validation error... These labels still exist...
原因:之前测试时生成了一些草稿数据,旧数据用到了旧标签,导致新模版无法保存。
解决:直接把项目删了重建,或者把所有 Tasks 全部删除,保持项目空白再改代码。
三、 解决音频加载 404 问题 (坑点 3:史诗级大坑)
这是最折磨的一步。我希望 Label Studio 直接读取我本地 D:\ls_data\raw_audio 里的文件,而不是手动一个个上传。但配置后总是报错:
Technical description: HTTP error status: 404
❌ 失败尝试
修改环境变量 DOCUMENT_ROOT:改了斜杠、反斜杠、大小写,依然时好时坏。
Python 脚本生成绝对路径 URL:浏览器显示 blocked。
✅ 最终解决方案:UI 挂载法
官方推荐的最稳妥方案,不用改代码,直接在网页上配。
进入 Settings -> Cloud Storage。
点击 Add Source Storage -> 选择 Local files。
Absolute local path: 填入音频文件夹路径 (如 D:\ls_data\raw_audio)。
File Name Filter: 改成 .* (默认只读 JSON,必须改这个才能读音频!)。
Treat every bucket object as a source file: 必须开启 (ON) (这是最关键的一步!)。
点击 Sync。
配置完这一步,音频终于能正常播放了!
四、 引入 AI 预标注 (效率提升神器)
为了不手打字幕,我写了一个 Python 脚本调用 Whisper 模型,先把音频的文字和时间轴生成出来。
4.1 编写 auto_label.py
思路:
遍历文件夹里的音频。
调用 whisper.load_model("medium") 识别。
将识别出的 start, end, text 封装成 Label Studio 的 JSON 格式。
生成 import_tasks.json。
4.2 导入技巧
在 Label Studio 点击 Import 时,只需要拖入 import_tasks.json。
因为我们在第三步已经配置了“本地存储挂载”,系统会自动把 JSON 里的任务和本地的音频文件关联起来。
工作流优化:
Whisper 分不清说话人 A 和 B。
我的做法:脚本默认全标为 A。在标注界面,利用快捷键(键盘 1 是 A,2 是 B),左手按键盘,右手点鼠标,1秒钟改一个,比 AI 瞎猜要准得多且快。
五、 数据导出与格式清洗 (坑点 4:导出格式不可用)
Label Studio 导出的 JSON 是“源数据”(包含各种内部 ID、from_name 等),结构非常乱,无法直接交付给客户。而且它默认导出一个大文件,而我需要每个音频单独存一个 JSON。
5.1 编写 convert.py
我写了一个后处理脚本,功能包括:
清洗格式:提取 value 里的核心数据,丢弃系统字段。
数据拼接:把散落在不同 dict 里的 说话人、文本、情感 拼成一个对象。
幽灵数据过滤:自动剔除时长为 0 的无效标注。
自动切分:将大文件切分为 DIALOG_001.json, DIALOG_002.json ...
使用方法:
codeBash
python convert.py运行后会自动生成 output_files 文件夹,里面就是整整齐齐的交付标准格式。
六、 总结
这一套流程跑通后,我的工作流变成了:
扔音频:把几百个文件丢进文件夹。
跑脚本:一键生成预标注数据。
修数据:进网页只负责听和点选情感(不打字)。
一键导出:跑转换脚本,直接交付。
遇到的最大教训:
Windows 的路径问题(\ 和 /)真的很搞心态,能用 UI 界面配置挂载就别用脚本硬改路径。
文件后缀名一定要看清楚,别把 .py 存成了 .txt。
Label Studio 的 Import 逻辑要搞懂:要么同时拖音频和JSON,要么配置好 Local Storage 后只拖 JSON。
希望这篇踩坑记录能帮到同样在做音频标注的朋友!