简介:这份《100首歌声数据集》面向歌声合成方向的研究者与深度学习实践者,尤其适合关注韩语歌声建模、HMM声学建模及神经网络声码器实验的中高级学习者。资源以zip压缩包形式提供,共502个文件,约971.99MB,包含100个wav人声录音、100个mid旋律文件、100个csv时长标注、200个txt歌词文本,以及json等辅助配置,覆盖从旋律、发音到时间对齐的完整训练链路。其中MIDI用于提取音高与节奏,WAV提供高质量原始歌声,歌词与逐音节时长标注则支撑发音与同步建模,可服务于WaveNet、Tacotron、Transformer类模型的训练与评测。内容预览显示文件按kr编号成对组织,便于按曲目切分训练集与验证集。目前已有838人学习下载,适合希望快速搭建歌声合成基线、复现声学建模流程或扩展多语言合成研究的人员参考使用。
1. 歌声数据集到底解决什么问题:从 100 首带标注的 midi、歌词、时长、wav 说起
做歌声合成、歌词对齐、旋律提取或者音乐信息检索的人,几乎都卡在同一个地方:找不到一份「四件套齐全」的数据。网上能下到的 wav 一大堆,但要么没有 midi,要么 midi 和音频对不上拍;歌词文本倒是有,可跟音频逐句对齐的时间戳基本靠手标,标十首就想放弃。这份「100 首歌声数据集(含 midi 歌词 标注时长 wav)」的价值就在这——它把一首歌拆成了四个互相能对上的模态:音频波形、音符事件、歌词文本、时间标注。你拿到手就能直接喂给模型,不用先花两周做数据清洗。
它适合三类人:一是做歌声合成(SVS)的,需要 midi 提供音高和时值、wav 提供音色、歌词提供音素序列;二是做强制对齐(forced alignment)的,需要歌词和时长标注来训练或验证对齐器;三是做翻唱/音高提取的,midi 就是天然的 ground truth。100 首这个量级不算大,但胜在结构完整,适合跑通 pipeline、验证想法,而不是一上来就烧显卡训大模型。下面我按「先看懂数据长什么样,再动手跑通,最后避开坑」的顺序讲,每一步都给能直接抄的命令和参数。
2. 拆开压缩包先看结构:四个模态怎么对齐、字段怎么读
2.1 目录组织与文件命名约定
拿到 zip 别急着解压到桌面,先unzip -l看一眼清单,确认没有嵌套过深的目录和乱码文件名。常见做法是每首歌一个文件夹,文件夹名是歌曲 ID 或歌名,里面放xxx.wav、xxx.mid、xxx.txt(歌词)、xxx.lab或xxx.json(时长标注)。中文歌名容易在 Windows 解压时变乱码,建议在 Linux 或 macOS 下用unzip -O gbk指定编码,或者干脆用 Python 的zipfile读。
# 先看清单,不落盘 unzip -l "100首歌声数据集.zip" | head -50 # 确认编码后解压到指定目录,避免中文乱码 unzip -O gbk "100首歌声数据集.zip" -d ./song_dataset # 统计四类文件数量,验证是否每首齐全 find ./song_dataset -name "*.wav" | wc -l find ./song_dataset -name "*.mid" | wc -l find ./song_dataset -name "*.txt" | wc -l find ./song_dataset -name "*.lab" -o -name "*.json" | wc -l四个数字应该都接近 100。如果某个模态明显少几首,说明数据集本身有缺失,后面训练时要按交集取,别硬凑。这一步花两分钟,能省掉后面「为什么对齐结果总有一批异常」的排查时间。
2.2 时长标注的两种常见格式与解析
时长标注是这份数据里最容易被忽略、却最影响对齐质量的部分。常见两种格式:一种是.lab纯文本,每行「起始秒 结束秒 歌词」;另一种是.json,字段名可能是start/end/text或begin/end/lyric。先读一条看字段,别假设。
import json, os def parse_lab(path): # .lab 格式:0.32 1.85 春 items = [] with open(path, encoding="utf-8") as f: for line in f: parts = line.strip().split() if len(parts) >= 3: items.append({ "start": float(parts[0]), "end": float(parts[1]), "text": " ".join(parts[2:]) }) return items def parse_json(path): with open(path, encoding="utf-8") as f: data = json.load(f) # 兼容不同字段命名,先探测 key_map = {"start": ["start", "begin", "s"], "end": ["end", "e"], "text": ["text", "lyric", "word"]} items = [] for seg in data if isinstance(data, list) else data.get("segments", []): item = {} for std, cands in key_map.items(): for c in cands: if c in seg: item[std] = seg[c] break if len(item) == 3: items.append(item) return items解析完一定要做一致性检查:相邻片段的end和下一个start是否衔接、有没有重叠、总时长是否和 wav 时长吻合。用soundfile读 wav 的帧数和采样率算真实时长,差超过 0.1 秒就要警惕。
import soundfile as sf info = sf.info("./song_dataset/song_001/song_001.wav") wav_dur = info.frames / info.samplerate segs = parse_lab("./song_dataset/song_001/song_001.lab") print("wav 时长", wav_dur, "标注末尾", segs[-1]["end"])参数上,采样率常见 44100 或 16000,做声学模型一般重采样到 22050 或 24000;标注精度到 10ms 级别就够用,别追求毫秒级,人耳和模型都吃不出差别。
2.3 midi 与歌词、音频的对应关系
midi 提供的是音符级信息:音高(pitch)、起始时间、持续时长。歌词提供的是音节或字。两者不是一一对应——一个音符可能对应一个字,也可能一个字拖多个音符(一字多音),或者一个音符跨多个字(装饰音)。做 SVS 时,通常要把歌词按音节切分后,用时长标注把音节边界对齐到音符边界。先验证 midi 的音符总数和歌词字数是否在同一量级,差太多说明这份数据的对齐需要额外处理。
import pretty_midi midi = pretty_midi.PrettyMIDI("./song_dataset/song_001/song_001.mid") notes = [n for inst in midi.instruments for n in inst.notes] print("音符数", len(notes), "音高范围", min(n.pitch for n in notes), max(n.pitch for n in notes))音高范围落在 48~84(C3~C6)之间比较正常,超出说明有异常音符或 midi 本身有问题。音符数明显少于歌词字数,多半是 midi 只标了主旋律、没标装饰音,这时候对齐要以歌词时长为准,midi 只取音高。
3. 用 Python 跑通「音频-midi-歌词」三模态对齐的最小流程
3.1 环境准备与依赖版本
这套流程依赖soundfile、pretty_midi、numpy、librosa。librosa 版本差异较大,0.10 之后很多 API 改了,建议锁版本,避免「昨天能跑今天报错」的玄学。
python -m venv venv source venv/bin/activate pip install numpy==1.26.4 soundfile==0.12.1 pretty_midi==0.2.10 librosa==0.10.2.post1装完先python -c "import librosa; print(librosa.__version__)"确认。如果要用 GPU 做后续声学特征,再单独装 torch,别和上面混在一个 requirements 里,方便排查。
3.2 把 wav 切成片段并和标注对齐
核心思路:按时长标注把 wav 切成一个个音节片段,每个片段带上对应的歌词和 midi 音高。这样切出来的片段可以直接做声学模型的训练样本。
import numpy as np import soundfile as sf def slice_by_labels(wav_path, segs, sr_target=22050): audio, sr = sf.read(wav_path) if audio.ndim > 1: # 双声道转单声道 audio = audio.mean(axis=1) if sr != sr_target: # 重采样 import librosa audio = librosa.resample(audio, orig_sr=sr, target_sr=sr_target) sr = sr_target slices = [] for seg in segs: s = int(seg["start"] * sr) e = int(seg["end"] * sr) s = max(0, s) e = min(len(audio), e) if e - s < int(0.05 * sr): # 太短的片段丢弃 continue slices.append({ "audio": audio[s:e], "text": seg["text"], "start": seg["start"], "end": seg["end"] }) return slices, sr参数说明:sr_target选 22050 是声学模型的常见折中,兼顾音质和显存;0.05秒是最短片段阈值,低于这个长度的片段多半是标注噪声,留着反而干扰训练。切完统计一下片段时长分布,如果大量片段集中在 0.05~0.1 秒,说明标注粒度太细,可能要合并。
3.3 提取音高并与 midi 交叉验证
用 librosa 的 pyin 提取基频,和 midi 的音高做对比,能快速判断这份数据的 midi 是否可信。
import librosa def extract_f0(audio, sr): f0, voiced_flag, _ = librosa.pyin( audio, fmin=librosa.note_to_hz("C3"), fmax=librosa.note_to_hz("C6"), sr=sr) return f0, voiced_flag # 对单个片段提取,和 midi 音高比对 f0, vf = extract_f0(slices[0]["audio"], sr) if np.any(vf): mean_hz = np.nanmean(f0[vf]) print("提取基频均值", mean_hz, "对应 midi", librosa.hz_to_midi(mean_hz))如果提取出的 midi 音高和 midi 文件里的音高差超过 2 个半音,要么是八度错误(pyin 常见),要么是 midi 本身标错。八度错误可以通过整体平移修正,标错就只能丢弃该片段。这一步是数据质量的黑匣子,跑一遍心里有底。
4. 避坑与排查:这份数据集最容易翻车的 5 个地方
4.1 现象:解压后中文文件名全是乱码,脚本读不到文件
原因:zip 在 Windows 下用 GBK 编码打包,Linux/macOS 默认按 UTF-8 解,文件名直接损坏。解决:用unzip -O gbk指定编码,或者用 Python 的zipfile手动按cp936解码文件名再落盘。已经解压坏的就删掉重来,别试图改名,容易漏。
4.2 现象:时长标注的 end 和下一个 start 对不上,切片出现空白或重叠
原因:标注是人工做的,边界有抖动,或者不同标注员标准不一致。解决:切片前做一次规整,把重叠部分取中点,空白小于 50ms 的直接合并到相邻片段,大于 50ms 的标记出来人工抽查。别小看这几十毫秒,对齐模型对边界噪声很敏感。
4.3 现象:midi 音符数和歌词字数差很多,对齐结果全乱
原因:一字多音或一音多字,直接按索引硬对必然错位。解决:先做音节切分,中文按拼音声母韵母切,英文按音素切,再用动态规划(DTW)把音节序列和音符序列对齐,而不是按位置一一对应。这一步是 SVS 数据预处理的核心,偷懒后面全崩。
4.4 现象:wav 采样率不统一,有的 44100 有的 16000,训练时报维度错
原因:数据集从不同来源拼凑,没做统一重采样。解决:读每个 wav 的sf.info,按目标采样率批量重采样,重采样用librosa.resample的res_type="soxr_hq",质量比默认的kaiser_best快且够用。重采样后统一存成 16bit PCM,别留 float64 占空间。
4.5 现象:pyin 提取的基频整体高一个八度,和 midi 对不上
原因:pyin 对含大量谐波的歌声容易把二次谐波当基频。解决:提取后和 midi 音高做中位数比对,若整体差约 12 个半音,直接f0 / 2修正;若差得没规律,说明该片段质量差,丢弃。别硬修,修出来的数据比没有更害人。
5. 把 100 首用出 1000 首的效果:数据增强与质量筛选的实操技巧
100 首直接训大模型肯定不够,但做对了增强和筛选,能把它用出几倍的量。我一般先做质量筛选,再做增强,顺序不能反——脏数据增强只会放大噪声。
质量筛选我盯三个指标:一是切片后片段时长中位数,落在 0.2~0.8 秒的片段占比应超过 70%,低于这个说明标注粒度有问题;二是 pyin 提取的 voiced 帧占比,低于 30% 的片段直接丢,多半是静音或噪声;三是 midi 音高和提取基频的一致性,差超过 2 个半音的片段标记为可疑,人工抽查 10% 决定去留。这三个指标跑一遍,100 首通常能留下 85~90 首的可用片段。
增强上,歌声数据别用图像那套翻转旋转。有效的做法是:音高平移(±2 个半音,用librosa.effects.pitch_shift)、时长微扰(0.9~1.1 倍速,用librosa.effects.time_stretch)、加轻微背景噪声(信噪比 20~30dB)。音高平移要注意同步改 midi 的音高值,否则音频和标注对不上,这是最容易翻车的地方。
import librosa import numpy as np def augment(audio, sr, midi_pitch, shift=2): # 音高平移,同步修正 midi 音高 y_shift = librosa.effects.pitch_shift(audio, sr=sr, n_steps=shift) new_pitch = midi_pitch + shift # 时长微扰 rate = np.random.uniform(0.9, 1.1) y_stretch = librosa.effects.time_stretch(y_shift, rate=rate) return y_stretch, new_pitch参数上,n_steps别超过 ±3,超过后人耳能听出明显失真,模型学到的也是假特征;rate控制在 0.9~1.1,再大音色就变了。增强后的数据要和原始数据分开存,训练时按比例混合,别把增强数据当独立样本重复计数。
最后一个习惯:每处理完一批数据,存一份manifest.json,记录每首歌的片段数、总时长、筛选通过率。下次换数据集或者调参数,直接对比 manifest 就能看出哪一步变了,不用重新跑一遍全流程。这份 100 首的数据集不大,但把上面这套流程跑顺,换成 1000 首、10000 首也是同一套代码,差别只在磁盘和耐心。希望帮到你。
本文还有配套的精品资源,点击获取