模型简介
模型特点
模型能力
使用案例
语言: es
数据集:
- common_voice
指标: - wer
标签: - audio
- automatic-speech-recognition
- speech
- xlsr-fine-tuning-week
许可证: apache-2.0
模型索引: - 名称: XLSR Wav2Vec2 Large 53 Spanish by pcuenq
结果:- 任务:
名称: Speech Recognition
类型: automatic-speech-recognition
数据集:
名称: Common Voice es
类型: common_voice
参数: es
指标:- 名称: Test WER
类型: wer
值: 10.50
- 名称: Test WER
- 任务:
Wav2Vec2-Large-XLSR-53-Spanish
基于西班牙语的Common Voice数据集对facebook/wav2vec2-large-xlsr-53进行微调。
使用此模型时,请确保语音输入采样率为16kHz。
使用方法
该模型可直接使用(无需语言模型),如下所示:
import torch
import torchaudio
from datasets import load_dataset
from transformers import Wav2Vec2ForCTC, Wav2Vec2Processor
test_dataset = load_dataset("common_voice", "es", split="test[:2%]")
processor = Wav2Vec2Processor.from_pretrained("pcuenq/wav2vec2-large-xlsr-53-es")
model = Wav2Vec2ForCTC.from_pretrained("pcuenq/wav2vec2-large-xlsr-53-es")
resampler = torchaudio.transforms.Resample(48_000, 16_000)
# 数据预处理
# 需要将音频文件读取为数组
def speech_file_to_array_fn(batch):
speech_array, sampling_rate = torchaudio.load(batch["path"])
batch["speech"] = resampler(speech_array).squeeze().numpy()
return batch
test_dataset = test_dataset.map(speech_file_to_array_fn)
inputs = processor(test_dataset["speech"][:2], sampling_rate=16_000, return_tensors="pt", padding=True)
with torch.no_grad():
logits = model(inputs.input_values, attention_mask=inputs.attention_mask).logits
predicted_ids = torch.argmax(logits, dim=-1)
print("预测结果:", processor.batch_decode(predicted_ids))
print("参考文本:", test_dataset["sentence"][:2])
评估
该模型可按以下方式在Common Voice的西班牙语测试数据上进行评估:
import torch
import torchaudio
from datasets import load_dataset, load_metric
from transformers import Wav2Vec2ForCTC, Wav2Vec2Processor
import re
test_dataset = load_dataset("common_voice", "es", split="test")
wer = load_metric("wer")
processor = Wav2Vec2Processor.from_pretrained("pcuenq/wav2vec2-large-xlsr-53-es")
model = Wav2Vec2ForCTC.from_pretrained("pcuenq/wav2vec2-large-xlsr-53-es")
model.to("cuda")
## 文本预处理
chars_to_ignore_regex = '[\,\¿\?\.\¡\!\-\;\:\"\“\%\‘\”\\…\’\ː\'\‹\›\`\´\®\—\→]'
chars_to_ignore_pattern = re.compile(chars_to_ignore_regex)
def remove_special_characters(batch):
batch["sentence"] = chars_to_ignore_pattern.sub('', batch["sentence"]).lower() + " "
return batch
def replace_diacritics(batch):
sentence = batch["sentence"]
sentence = re.sub('ì', 'í', sentence)
sentence = re.sub('ù', 'ú', sentence)
sentence = re.sub('ò', 'ó', sentence)
sentence = re.sub('à', 'á', sentence)
batch["sentence"] = sentence
return batch
def replace_additional(batch):
sentence = batch["sentence"]
sentence = re.sub('ã', 'a', sentence) # 葡萄牙语,如São Paulo
sentence = re.sub('ō', 'o', sentence) # 日语
sentence = re.sub('ê', 'e', sentence) # 葡萄牙语
batch["sentence"] = sentence
return batch
## 音频预处理
# 尝试使用`torchaudio`的`Resampler`进行重采样,
# 但在多进程使用时发现会死锁。
# 可能是我的torchaudio底层使用了错误的sox库,不确定。
# 幸运的是,`librosa`可以正常工作,因此暂时使用它。
import librosa
def speech_file_to_array_fn(batch):
speech_array, sample_rate = torchaudio.load(batch["path"])
batch["speech"] = librosa.resample(speech_array.squeeze().numpy(), sample_rate, 16_000)
return batch
# 单次映射函数
# 文本转换和音频重采样
def cv_prepare(batch):
batch = remove_special_characters(batch)
batch = replace_diacritics(batch)
batch = replace_additional(batch)
batch = speech_file_to_array_fn(batch)
return batch
# CPU数量或None
num_proc = 16
test_dataset = test_dataset.map(cv_prepare, remove_columns=['path'], num_proc=num_proc)
def evaluate(batch):
inputs = processor(batch["speech"], sampling_rate=16_000, return_tensors="pt", padding=True)
with torch.no_grad():
logits = model(inputs.input_values.to("cuda"), attention_mask=inputs.attention_mask.to("cuda")).logits
pred_ids = torch.argmax(logits, dim=-1)
batch["pred_strings"] = processor.batch_decode(pred_ids)
return batch
result = test_dataset.map(evaluate, batched=True, batch_size=8)
# WER指标计算
# `wer.compute`在样本超过约10000时会崩溃。
# 直到在其他机器上确认前,我创建了一个“分块”版本的计算方法。
# 对于较小的数据集,其结果与`wer.compute`一致。
import jiwer
def chunked_wer(targets, predictions, chunk_size=None):
if chunk_size is None: return jiwer.wer(targets, predictions)
start = 0
end = chunk_size
H, S, D, I = 0, 0, 0, 0
while start < len(targets):
chunk_metrics = jiwer.compute_measures(targets[start:end], predictions[start:end])
H = H + chunk_metrics["hits"]
S = S + chunk_metrics["substitutions"]
D = D + chunk_metrics["deletions"]
I = I + chunk_metrics["insertions"]
start += chunk_size
end += chunk_size
return float(S + D + I) / float(H + S + D)
print("WER: {:2f}".format(100 * chunked_wer(result["sentence"], result["pred_strings"], chunk_size=4000)))
#print("WER: {:2f}".format(100 * wer.compute(predictions=result["pred_strings"], references=result["sentence"])))
测试结果: 10.50%
文本处理
Common Voice的es
数据集中有许多不属于西班牙语的字符,即使在去除分隔符和标点符号后也是如此。我进行了一些转换并丢弃了大部分无关字符。
我决定保留所有西班牙语的变音符号。这是一个艰难的决定。有时变音符号仅因拼写规则而添加,但不会改变单词的含义。然而在其他情况下,变音符号具有区分不同含义的作用。如果仅使用非变音字符,可能会获得更好的WER分数,且生成的文本仍能被西班牙语使用者理解。尽管如此,我认为保留它们“更正确”。
所有应用的规则均在评估脚本中展示。
训练
使用了Common Voice的train
和validation
数据集进行训练。
出于数据集处理的原因,我最初将train
+validation
分为10%的子集,以便更早看到进展并在需要时调整。
- 我仅在第一个子集上训练了30个epoch,使用了与Patrick在演示笔记本中类似的参数。我使用了batch_size为24,梯度累积步数为2。在完整测试集上的WER约为16.3%。
- 然后,我在剩余的9个子集上训练了生成的模型,每个子集训练3个epoch,但使用了更快的75步预热。
- 接着,我在每个子集上使用较小的学习率
1e-4
训练了3个epoch。此阶段也使用了75步预热。最终模型的WER约为11.7%。 - 此时我们已经找到了训练时间延迟的原因,并决定使用完整数据集进行训练。然而,在测试中我发现调整学习率效果良好,因此希望复现这一点。我选择了带有硬重启的余弦调度,参考学习率为
3e-5
,训练10个epoch。配置余弦调度为10个周期,且不使用预热。最终WER约为10.5%。
其他尝试
- 从相同的微调模型开始,我比较了恒定学习率1e-4与带预热的线性调度。线性调度效果更好(WER 11.85 vs 12.72)。
- 尝试使用西班牙语模型改进巴斯克语模型。通过转换文本使拼写更接近目标语言,但巴斯克语模型未改善。
- 标签平滑未奏效。
问题与其他技术挑战
我之前仅作为终端用户使用过transformers
库,尝试过Bert的一些任务,但这是第一次需要深入研究代码。
-
Datasets
抽象非常棒,因为基于内存映射文件,可以处理任意大小的数据集。然而,理解其局限性和权衡很重要。我发现缓存很方便,但磁盘使用量迅速膨胀。我将当前项目的数据集保存在1TB的高速SSD上,有几次空间不足。必须理解缓存文件的存储方式,并学会何时禁用缓存并在需要时手动保存。我发现数据探索更适合较小的数据集或采样数据集,但实际处理在确定所需转换并通过单次map
操作应用时效率最高。 -
训练开始前有明显的延迟。幸运的是,我们找到了原因,在Slack和论坛中讨论并创建了解决方法。
-
WER指标在大数据集上崩溃。我在小样本上评估(速度更快),并编写了一个固定内存的累积版本wer。希望验证此更改是否适用于训练循环。
-
torchaudio
在多进程使用时死锁。librosa
工作正常。待调查。 -
在笔记本中使用
num_proc
时,无法看到进度条。这可能是我的电脑权限问题。仍需查明原因。



