模型概述
模型特點
模型能力
使用案例
🚀 歌詞對齊框架
本項目是一個越南語歌曲歌詞對齊框架,旨在構建一個模型,將歌詞與音樂音頻進行對齊,輸出歌詞中每個單詞的開始時間和結束時間。
🚀 快速開始
加載模型
from transformers import AutoTokenizer, AutoFeatureExtractor
from model_handling import Wav2Vec2ForCTC
model_path = 'nguyenvulebinh/lyric-alignment'
model = Wav2Vec2ForCTC.from_pretrained(model_path).eval()
tokenizer = AutoTokenizer.from_pretrained(model_path)
feature_extractor = AutoFeatureExtractor.from_pretrained(model_path)
vocab = [tokenizer.convert_ids_to_tokens(i) for i in range(len(tokenizer.get_vocab()))]
進行歌詞對齊
確保音頻文件為 16kHz 單聲道,可以使用 preprocessing.py
文件進行轉換。
from predict import handle_sample
import torchaudio
import json
# wav_path: 音頻文件路徑,需為 16k 單聲道
# path_lyric: 歌詞數據的 json 文件路徑,包含片段和單詞列表
wav, _ = torchaudio.load(wav_path)
with open(path_lyric, 'r', encoding='utf-8') as file:
lyric_data = json.load(file)
lyric_alignment = handle_sample(wav, lyric_data)
示例輸入與輸出
示例輸入文件 38303730395f313239.json
[{"s": 0, "e": 0, "l": [{"s": 0, "e": 0, "d": "Endgame"}, {"s": 0, "e": 0, "d": "chiến"}, {"s": 0, "e": 0, "d": "thắng"}]}, {"s": 0, "e": 0, "l": [{"s": 0, "e": 0, "d": "Chỉ"}, {"s": 0, "e": 0, "d": "lần"}, {"s": 0, "e": 0, "d": "duy"}, {"s": 0, "e": 0, "d": "nhất"}]}, {"s": 0, "e": 0, "l": [{"s": 0, "e": 0, "d": "Bởi"}, {"s": 0, "e": 0, "d": "IronMan"}, {"s": 0, "e": 0, "d": "và"}, {"s": 0, "e": 0, "d": "số"}, {"s": 0, "e": 0, "d": "3000"}]}]
示例輸出
[{ "s": 0, "e": 661, "l": [ { "s": 0, "e": 240, "d": "Endgame" }, { "s": 200, "e": 420, "d": "chiến" }, { "s": 380, "e": 661, "d": "thắng" } ] }, { "s": 621, "e": 1543, "l": [ { "s": 621, "e": 861, "d": "Chỉ" }, { "s": 821, "e": 1082, "d": "lần" }, { "s": 1042, "e": 1302, "d": "duy" }, { "s": 1262, "e": 1543, "d": "nhất" } ] }, { "s": 1503, "e": 7274, "l": [ { "s": 1503, "e": 1703, "d": "Bởi" }, { "s": 1663, "e": 2404, "d": "IronMan" }, { "s": 2364, "e": 2605, "d": "và" }, { "s": 2565, "e": 2845, "d": "số" }, { "s": 2805, "e": 7274, "d": "3000" }]}]
✨ 主要特性
- 精準對齊:通過先進的算法和模型,實現歌詞與音樂音頻的精準對齊。
- 多數據源支持:既可以使用 Zalo 提供的公共數據集,也可以使用自行爬取的公共數據集。
- 靈活處理:對歌詞中的特殊字符、英文單詞、數字格式等進行靈活處理,提高模型的適應性。
📦 安裝指南
若要從頭開始復現模型,可運行以下命令:
CUDA_VISIBLE_DEVICES=0,1,2,3,4 python -m torch.distributed.launch --nproc_per_node 5 train.py
train.py
腳本會自動從 Hugging Face 下載數據集 nguyenvulebinh/song_dataset 和預訓練模型 nguyenvulebinh/wav2vec2-large-vi-vlsp2020,並進行訓練。
💻 使用示例
基礎用法
from transformers import AutoTokenizer, AutoFeatureExtractor
from model_handling import Wav2Vec2ForCTC
model_path = 'nguyenvulebinh/lyric-alignment'
model = Wav2Vec2ForCTC.from_pretrained(model_path).eval()
tokenizer = AutoTokenizer.from_pretrained(model_path)
feature_extractor = AutoFeatureExtractor.from_pretrained(model_path)
vocab = [tokenizer.convert_ids_to_tokens(i) for i in range(len(tokenizer.get_vocab()))]
高級用法
from predict import handle_sample
import torchaudio
import json
# wav_path: 音頻文件路徑,需為 16k 單聲道
# path_lyric: 歌詞數據的 json 文件路徑,包含片段和單詞列表
wav, _ = torchaudio.load(wav_path)
with open(path_lyric, 'r', encoding='utf-8') as file:
lyric_data = json.load(file)
lyric_alignment = handle_sample(wav, lyric_data)
📚 詳細文檔
任務描述(Zalo AI 挑戰賽 2022)
很多人喜歡跟著專輯中喜歡的歌手一起唱歌(卡拉 OK 風格),本項目的目標是構建一個模型,將歌詞與音樂音頻進行對齊。
- 輸入:一段音樂(包含人聲)及其歌詞。
- 輸出:歌詞中每個單詞的開始時間和結束時間。
評估時,將使用交併比($IoU$)來評估預測的準確性,$IoU$ 值越高越好。例如:
音頻片段 $S_i$ 的預測結果與真實標籤的 $IoU$ 計算公式如下:
$IoU(S_i) = \frac{1}{m} \sum_{j=1}^{m}{\frac{G_j\cap P_j}{G_j\cup P_j}}$ 其中,$m$ 是 $S_i$ 的標記數量。所有 $n$ 個音頻片段的最終 $IoU$ 是它們對應 $IoU$ 的平均值: $Final_IoU = \frac{1}{n} \sum_{i=1}^{n}{IoU(S_i)}$
數據描述
Zalo 公共數據集
- 訓練數據:來自約 480 首歌曲的 1057 個音樂片段。每個片段都提供了 WAV 格式的音頻文件和一個真實標籤的 JSON 文件,其中包含歌詞和每個單詞的對齊時間幀(以毫秒為單位)。
- 測試數據:
- 公共測試:來自約 120 首歌曲的 264 個音樂片段。
- 私有測試:來自約 200 首歌曲的 464 個音樂片段。
數據示例:
爬取的公共數據集
由於 Zalo 提供的數據集較小且有噪聲,我們決定從其他公共來源爬取數據。幸運的是,我們的策略(詳見 方法 部分)不需要每個單詞的對齊時間幀,只需要歌曲及其歌詞,就像典型的自動語音識別(ASR)數據集一樣。
我們在 data_preparation 文件夾中詳細介紹了數據爬取和處理過程。我們從 https://zingmp3.vn 網站共爬取了 30,000 首歌曲,約 1500 小時的音頻。
方法
我們的策略主要基於 Ludwig Kürzinger 的 CTC-Segmentation 研究和 PyTorch 的 Wav2Vec2 強制對齊教程。引用 Ludwig Kürzinger 的研究:
CTC 分割是一種在音頻記錄開頭或結尾存在額外未知語音部分的情況下提取正確音頻 - 文本對齊的算法。它使用基於 CTC 的端到端網絡,該網絡事先在已對齊的數據上進行了訓練,例如由 CTC/注意力 ASR 系統提供的數據。
基於 PyTorch 的 Wav2Vec2 強制對齊教程,對齊過程如下:
- 從音頻波形估計逐幀標籤概率。
- 生成表示時間步上標籤對齊概率的網格矩陣。
- 從網格矩陣中找到最可能的路徑。
只有當具有良好的逐幀概率和正確的標籤時,對齊效果才會良好。
- 良好的逐幀概率:可以通過強大的聲學模型實現。我們的設置中,聲學模型基於使用 CTC 損失訓練的 wav2vec2 架構。
- 正確的標籤:指口語形式的標籤。由於歌詞來源多樣,可能包含特殊字符、混合英文和越南語單詞、數字格式(日期、時間、貨幣等)、暱稱等,這類數據會使模型難以將音頻信號與文本歌詞進行映射。我們的解決方案是將歌詞中的所有單詞從書面形式映射到口語形式。例如: | 書面形式 | 口語形式 | |----------|----------| | joker | giốc cơ | | running | răn ninh | | 0h | không giờ |
為了將英文單詞轉換為越南語發音,我們使用了 nguyenvulebinh/spelling-oov 模型。對於處理數字格式,我們使用了 Vinorm。對於其他特殊字符 ".,?... ",我們將其刪除。
書面單詞(例如 0h)的最終時間對齊是其口語單詞(例如 không giờ)的時間對齊的拼接。
評估設置
聲學模型
我們的最終模型基於 nguyenvulebinh/wav2vec2-large-vi-vlsp2020 模型。該模型在 13k 小時的越南語 YouTube 音頻(無標籤數據)上進行了預訓練,並在 250 小時標記的 VLSP ASR 數據集上進行了微調,採樣語音音頻為 16kHz。我們使用該檢查點,利用之前準備的 1500 小時數據訓練了一個新的 ASR 模型。
在我們的實驗中,使用了 5 塊 RTX A6000 GPU(約 250GB),批量大小為 160,相當於每步 40 分鐘。我們訓練了約 50 個 epoch,耗時 78 小時。下圖展示了訓練過程前 35k 步的日誌,最終訓練損失約為 0.27。
訓練後模型的字錯誤率(WER)性能如下:
- Zalo 公共數據集 - 測試集:0.2267
- 爬取的公共數據集 - 測試集:0.1427
對齊過程
對齊過程在方法部分已有描述。然而,為了在公共排行榜上達到 $IoU = 0.632$ 的結果,我們需要一些額外的步驟,詳細如下:
- 輸入格式化和轉換為口語形式:例如,原始輸入:
['Endgame', 'chiến', 'thắng', 'Chỉ', 'lần', 'duy', 'nhất', 'Bởi', 'IronMan', 'và', 'số', '3000']
口語形式輸出將是:
['en gêm', 'chiến', 'thắng', 'chỉ', 'lần', 'duy', 'nhất', 'bởi', 'ai ron men', 'và', 'số', 'ba nghìn']
- 使用 CTC 分割算法對口語形式文本和音頻進行強制對齊:方法部分詳細介紹了 3 個步驟。
輸出單詞片段:
en: 0 -> 140
gêm: 200 -> 280
chiến: 340 -> 440
thắng: 521 -> 641
chỉ: 761 -> 861
lần: 961 -> 1042
duy: 1182 -> 1262
nhất: 1402 -> 1483
bởi: 1643 -> 1703
ai: 1803 -> 1863
ron: 2064 -> 2144
men: 2284 -> 2344
và: 2505 -> 2565
số: 2705 -> 2765
ba: 2946 -> 2986
nghìn: 3166 -> 3266
- 根據 Zalo 提供的標記數據的行為進行時間幀對齊:我們觀察到連續單詞的時間幀是連續的。因此,根據上一步的單詞片段輸出,我們按如下方式對齊每個單詞的時間幀:
for i in range(len(word_segments) - 1):
word = word_segments[i]
next_word = word_segments[i + 1]
word.end = next_word.start
然而,我們制定了一些啟發式規則,使輸出更加準確:
- 一個單詞的時長不超過 3 秒。
- 如果單詞時長小於 1.4 秒 / 140 毫秒,我們將在該單詞的開始和結束時間分別增加 20 毫秒 / 40 毫秒。這是因為數據是手動標記的,人類在處理小片段時容易出錯。
- 每個單詞的所有時間戳向左移動 120 毫秒。這條規則顯著提高了 $IoU$ 結果,有時絕對 $IoU$ 值可提高 10%。我們也從數據中觀察到了這種行為,就像我們唱卡拉 OK 時,希望歌詞能稍微提前出現一樣。在實踐中,我們不建議使用這條規則。
所有這些啟發式規則都在 utils.py
文件的 add_pad
函數中實現。
應用規則後的輸出:
en: 0 -> 100
gêm: 60 -> 240
chiến: 200 -> 420
thắng: 380 -> 661
chỉ: 621 -> 861
lần: 821 -> 1082
duy: 1042 -> 1302
nhất: 1262 -> 1543
bởi: 1503 -> 1703
ai: 1663 -> 1964
rừn: 1923 -> 2184
mừn: 2144 -> 2404
và: 2364 -> 2605
số: 2565 -> 2845
ba: 2805 -> 3066
nghìn: 3046 -> 7274
- 將口語形式重新對齊到原始單詞:
Endgame ['en: 0 -> 100', 'gêm: 60 -> 240']
chiến ['chiến: 200 -> 420']
thắng ['thắng: 380 -> 661']
Chỉ ['chỉ: 621 -> 861']
lần ['lần: 821 -> 1082']
duy ['duy: 1042 -> 1302']
nhất ['nhất: 1262 -> 1543']
Bởi ['bởi: 1503 -> 1703']
IronMan ['ai: 1663 -> 1964', 'ron: 1923 -> 2184', 'men: 2144 -> 2404']
và ['và: 2364 -> 2605']
số ['số: 2565 -> 2845']
3000 ['ba: 2805 -> 3066', 'nghìn: 3046 -> 7274']
最終的對齊結果與波形圖一起繪製如下:
🔧 技術細節
- 聲學模型:基於 wav2vec2 架構,使用 CTC 損失進行訓練,在大量越南語音頻數據上進行了預訓練和微調。
- 對齊算法:採用 CTC 分割算法,結合啟發式規則對時間幀進行調整,提高對齊的準確性。
📄 許可證
本項目採用 CC BY-NC 4.0 許可證。
致謝
感謝 Zalo AI 挑戰賽 2022 的組織者提供了這個有趣的挑戰。
聯繫信息



