在🤗 Transformers中用n-gram提升Wav2Vec2
Wav2Vec2是用于语音识别的流行预训练模型。该新颖架构于2020年9月由Meta AI Research发布,它推动了语音识别自监督预训练的进展,例如G. Ng et al., 2021、Chen et al, 2021、Hsu et al., 2021和Babu et al., 2021。在Hugging Face Hub上,Wav2Vec2最受欢迎的预训练检查点目前每月下载量超过250,000次。
使用连接时序分类(CTC),Wav2Vec2类预训练检查点在下游语音识别任务上极易进行微调。简而言之,微调预训练Wav2Vec2检查点的工作原理如下:
一个随机初始化的线性层堆叠在预训练检查点之上,并经过训练将原始音频输入分类为字母序列。它通过以下方式实现:
- 从原始音频中提取音频表示(使用CNN层),
- 使用一堆Transformer层处理音频表示序列,以及
- 将处理后的音频表示分类为输出字母序列。
以前的音频分类模型需要额外的语言模型(LM)和字典才能将分类的音频帧序列转换为连贯的转录。Wav2Vec2的架构基于Transformer层,因此为每个处理过的音频表示提供了所有其他音频表示的上下文。此外,Wav2Vec2利用CTC算法进行微调,该算法解决了可变“输入音频长度”-到-“输出文本长度”比率之间的对齐问题。
由于具有语境化音频分类且没有对齐问题,Wav2Vec2不需要外部语言模型或字典即可生成可接受的音频转录。
正如官方论文附录C所示,Wav2Vec2在LibriSpeech上表现出令人印象深刻的下游性能,完全没有使用语言模型。然而,从附录中也可以清楚地看到,将Wav2Vec2与语言模型结合使用可以带来显著的改进,特别是当模型仅在10分钟的转录音频上进行训练时。
直到最近,🤗 Transformers库还没有提供一个简单的用户界面来使用经过微调的Wav2Vec2和语言模型解码音频文件。幸运的是,这种情况已经改变了。🤗 Transformers现在提供了与Kensho Technologies的pyctcdecode库的易用集成。这篇博客文章是一个分步技术指南,解释如何创建n-gram语言模型并将其与现有的微调Wav2Vec2检查点结合使用🤗 Datasets和🤗 Transformers。
我们首先:
- 使用LM解码音频与不使用LM解码音频有何不同?
- 如何为语言模型获取合适的数据?
- 如何使用KenLM构建一个n-gram?
- 如何将n-gram与微调的Wav2Vec2检查点结合?
要深入了解Wav2Vec2的工作原理(对于本博客文章来说不是必需的),建议读者查阅以下材料:
1. 使用Wav2Vec2和语言模型解码音频数据
如🤗 Transformers Wav2Vec2的示例文档所示,音频可以按以下方式转录。
首先,我们安装 datasets
和 transformers
。
pip install datasets transformers
让我们加载Librispeech数据集的一小部分,以演示Wav2Vec2的语音转录能力。
from datasets import load_dataset
dataset = load_dataset("hf-internal-testing/librispeech_asr_demo", "clean", split="validation")
dataset
输出
Reusing dataset librispeech_asr (/root/.cache/huggingface/datasets/hf-internal-testing___librispeech_asr/clean/2.1.0/f2c70a4d03ab4410954901bde48c54b85ca1b7f9bf7d616e7e2a72b5ee6ddbfc)
Dataset({
features: ['file', 'audio', 'text', 'speaker_id', 'chapter_id', 'id'],
num_rows: 73
})
我们可以选择73个音频样本中的一个并聆听它。
audio_sample = dataset[2]
audio_sample["text"].lower()
输出
he tells us that at this festive season of the year with christmas and roast beef looming before us similes drawn from eating and its results occur most readily to the mind
选择了数据样本后,我们现在加载微调模型和处理器。
from transformers import Wav2Vec2Processor, Wav2Vec2ForCTC
processor = Wav2Vec2Processor.from_pretrained("facebook/wav2vec2-base-100h")
model = Wav2Vec2ForCTC.from_pretrained("facebook/wav2vec2-base-100h")
接下来,我们处理数据
inputs = processor(audio_sample["audio"]["array"], sampling_rate=audio_sample["audio"]["sampling_rate"], return_tensors="pt")
将其转发给模型
import torch
with torch.no_grad():
logits = model(**inputs).logits
并解码它
predicted_ids = torch.argmax(logits, dim=-1)
transcription = processor.batch_decode(predicted_ids)
transcription[0].lower()
输出
'he tells us that at this festive season of the year with christmaus and rose beef looming before us simalyis drawn from eating and its results occur most readily to the mind'
将转录与上面的目标转录进行比较,我们可以看到一些单词听起来正确,但拼写不正确,例如:
- christmaus vs. christmas
- rose vs. roast
- simalyis vs. similes
让我们看看将Wav2Vec2与n-gram语言模型结合是否有帮助。
首先,我们需要安装pyctcdecode
和kenlm
。
pip install https://github.com/kpu/kenlm/archive/master.zip pyctcdecode
为了演示目的,我们准备了一个新的模型库patrickvonplaten/wav2vec2-base-100h-with-lm,它包含相同的Wav2Vec2检查点,但额外包含一个用于英语的4-gram语言模型。
这次我们不使用Wav2Vec2Processor
,而是使用Wav2Vec2ProcessorWithLM
来加载4-gram模型以及特征提取器和分词器。
from transformers import Wav2Vec2ProcessorWithLM
processor = Wav2Vec2ProcessorWithLM.from_pretrained("patrickvonplaten/wav2vec2-base-100h-with-lm")
与不使用语言模型解码音频不同,现在处理器直接接收模型的输出logits
,而不是上面的argmax(logits)
(称为predicted_ids
)。原因是,当使用语言模型解码时,在每个时间步,处理器会考虑所有可能的输出字符的概率。让我们看一下logits
输出的维度。
logits.shape
输出
torch.Size([1, 624, 32])
我们可以看到logits
对应于一个由624个向量组成的序列,每个向量有32个条目。其中每个32个条目代表模型32个可能输出字符之一的逻辑概率
" ".join(sorted(processor.tokenizer.get_vocab()))
输出
"' </s> <pad> <s> <unk> A B C D E F G H I J K L M N O P Q R S T U V W X Y Z |"
直观地看,可以理解Wav2Vec2ProcessorWithLM
的解码过程,即在624 × 32概率矩阵上应用束搜索,同时利用n-gram语言模型提供的下一个字母的概率。
好的,我们再运行一次解码步骤。pyctcdecode
语言模型解码器不会自动将torch
张量转换为numpy
,因此我们必须先自行转换它们。
transcription = processor.batch_decode(logits.numpy()).text
transcription[0].lower()
输出
'he tells us that at this festive season of the year with christmas and rose beef looming before us similes drawn from eating and its results occur most readily to the mind'
太棒了!回顾之前facebook/wav2vec2-base-100h
在没有语言模型时转录错误的单词,例如:
- christmaus vs. christmas
- rose vs. roast
- simalyis vs. similes
我们可以再次看一下带4-gram语言模型的facebook/wav2vec2-base-100h
的转录。3个错误中有2个被纠正;christmas和similes已被正确转录。
有趣的是,rose的错误转录依然存在。然而,这不应该让我们感到非常惊讶。不带语言模型解码音频更容易产生拼写错误,例如christmaus或similes(据我所知,这些词在英语中不存在)。这是因为语音识别系统几乎完全基于其获得的声学输入进行预测,而不是真正基于先前和后续预测字母的语言建模上下文。另一方面,如果我们添加语言模型,我们可以相当确定语音识别系统会大大减少拼写错误,因为一个训练有素的n-gram模型肯定不会预测带有拼写错误的单词。但是rose是一个有效的英语单词,因此4-gram模型会以不低的概率预测这个单词。
语言模型本身最有可能支持正确的单词roast,因为单词序列roast beef在英语中比rose beef更常见。因为最终的转录是根据facebook/wav2vec2-base-100h
输出概率和n-gram语言模型的加权组合得出的,所以看到rose这样错误转录的单词是很常见的。
有关如何在使用Wav2Vec2ProcessorWithLM
解码时调整不同参数的更多信息,请参阅此处的官方文档。
一些研究表明,像facebook/wav2vec2-base-100h
这样的模型——当足够大且在足够多的数据上训练时——可以学习中间音频表示之间的语言建模依赖性,类似于语言模型。
太棒了,现在你已经看到了添加n-gram语言模型所带来的优势,让我们深入了解如何从头开始创建n-gram和Wav2Vec2ProcessorWithLM
。
2. 获取语言模型所需数据
对语音识别系统有用的语言模型应该支持声学模型(例如Wav2Vec2)预测下一个单词(或标记、字母),因此应该建模以下分布:,其中是下一个单词,而是从话语开始以来所有先前单词的序列。简单来说,语言模型应该善于根据所有先前转录的单词预测下一个单词,而不管给语音识别系统的音频输入是什么。
与往常一样,语言模型的好坏取决于其训练数据。在语音识别的情况下,我们应该问自己语音识别将用于哪种数据:对话、有声读物、电影、演讲等等……?
语言模型应善于对与语音识别系统目标转录相对应的语言进行建模。为演示目的,我们假设这里我们已经在一个预训练的facebook/wav2vec2-xls-r-300m
上对瑞典语的Common Voice 7进行了微调。微调后的检查点可在此处找到。Common Voice 7是一个相对众包的朗读音频数据集,我们将在其测试数据上评估模型。
现在让我们在Hugging Face Hub上寻找合适的文本数据。我们搜索所有包含瑞典语数据的数据集。浏览了一下数据集后,我们正在寻找一个与Common Voice的朗读音频数据相似的数据集。明显的选择oscar和mc4可能不是最合适的,因为它们
- 由网络爬取生成,可能不够干净,与口语不太对应
- 需要大量的预处理
- 体量非常大,不适合这里的演示目的 😉
europarl_bilingual数据集在这里看起来很合理,它相对干净且易于预处理,因为它是一个基于欧洲议会讨论和演讲的数据集。因此,它应该相对干净,并且与朗读音频数据很好地对应。该数据集最初是为机器翻译设计的,因此只能以翻译对的形式访问。我们将只从英语到瑞典语的翻译中提取目标语言(瑞典语,sv
)的文本。
target_lang="sv" # change to your target lang
让我们下载数据。
from datasets import load_dataset
dataset = load_dataset("europarl_bilingual", lang1="en", lang2=target_lang, split="train")
我们看到数据量相当大——它有超过一百万条翻译。不过,由于它只是文本数据,处理起来应该相对容易。
接下来,我们看看在瑞典语微调XLS-R检查点时数据是如何预处理的。查看run.sh
文件,我们可以看到以下字符已从官方转录中删除:
chars_to_ignore_regex = '[,?.!\-\;\:"“%‘”�—’…–]' # change to the ignored characters of your fine-tuned model
我们在这里也这样做,以便我们的语言模型的字母表与微调声学检查点的字母表匹配。
我们可以编写一个简单的映射函数来提取瑞典语文本并立即处理它。
import re
def extract_text(batch):
text = batch["translation"][target_lang]
batch["text"] = re.sub(chars_to_ignore_regex, "", text.lower())
return batch
让我们应用.map()
函数。这应该需要大约5分钟。
dataset = dataset.map(extract_text, remove_columns=dataset.column_names)
太棒了。让我们把它上传到Hub,以便我们更好地检查和重用它。
您可以通过执行以下单元格来登录。
from huggingface_hub import notebook_login
notebook_login()
输出
Login successful
Your token has been saved to /root/.huggingface/token
Authenticated through git-credential store but this isn't the helper defined on your machine.
You might have to re-authenticate when pushing to the Hugging Face Hub. Run the following command in your terminal in case you want to set this credential helper as the default
git config --global credential.helper store
接下来,我们调用 🤗 Hugging Face 的 push_to_hub
方法,将数据集上传到仓库 "sv_corpora_parliament_processed"
。
dataset.push_to_hub(f"{target_lang}_corpora_parliament_processed", split="train")
那太容易了!上传新数据集时,数据集查看器会自动启用,这非常方便。您现在可以直接在线检查数据集。
欢迎直接在hf-test/sv_corpora_parliament_processed
上浏览我们预处理过的数据集。即使我们不是瑞典语母语者,我们也可以看到数据处理得很好,并且看起来很干净。
接下来,我们使用数据构建一个语言模型。
3. 使用KenLM构建一个n-gram
尽管基于Transformer架构的大型语言模型已成为自然语言处理的标准,但使用n-gram语言模型来提升语音识别系统仍然非常普遍——如第1节所示。
再次查看官方Wav2Vec2论文附录C的表9,可以注意到,使用基于Transformer的语言模型进行解码明显优于使用n-gram模型,但n-gram与基于Transformer的语言模型之间的差异远小于n-gram与无语言模型之间的差异。
例如,对于仅在10分钟数据上进行微调的大型Wav2Vec2检查点,n-gram与无语言模型相比,词错误率(WER)降低了约80%,而基于Transformer的语言模型与n-gram相比,WER仅额外降低了23%。这种相对WER降低的幅度会随着声学模型训练数据量的增加而减小。例如,对于大型检查点,基于Transformer的语言模型与n-gram语言模型相比,WER仅降低了8%,而n-gram与无语言模型相比,WER仍降低了21%。
n-gram模型优于基于Transformer的语言模型的原因在于,n-gram模型的计算成本显著更低。对于n-gram模型,给定先前单词获取单词概率的计算开销几乎只相当于查询一个查找表或树状数据存储——也就是说,与现代基于Transformer的语言模型相比,n-gram模型非常快,后者需要完整的正向传播才能获取下一个单词的概率。
有关n-gram如何工作以及为何它们(仍然)对语音识别如此有用的更多信息,建议读者查阅斯坦福大学的这份优秀总结。
太棒了,让我们一步步看看如何构建一个n-gram。我们将使用流行的KenLM库来实现。首先安装Ubuntu库的先决条件:
sudo apt install build-essential cmake libboost-system-dev libboost-thread-dev libboost-program-options-dev libboost-test-dev libeigen3-dev zlib1g-dev libbz2-dev liblzma-dev
在下载并解压KenLM仓库之前。
wget -O - https://kheafield.com/code/kenlm.tar.gz | tar xz
KenLM是用C++编写的,所以我们将使用cmake
来构建二进制文件。
mkdir kenlm/build && cd kenlm/build && cmake .. && make -j2
ls kenlm/build/bin
太好了,正如我们所见,可执行函数已成功构建在kenlm/build/bin/
下。
KenLM默认使用Kneser-Ney平滑计算n-gram。所有用于创建n-gram的文本数据都应存储在一个文本文件中。我们下载数据集并将其保存为.txt
文件。
from datasets import load_dataset
username = "hf-test" # change to your username
dataset = load_dataset(f"{username}/{target_lang}_corpora_parliament_processed", split="train")
with open("text.txt", "w") as file:
file.write(" ".join(dataset["text"]))
现在,我们只需运行KenLM的lmplz
命令来构建我们的n-gram,名为"5gram.arpa"
。在语音识别中,构建5-gram相对常见,我们通过传入-o 5
参数来实现。有关KenLM可构建的不同n-gram语言模型的更多信息,可以查看KenLM的官方网站。
执行以下命令可能需要大约一分钟。
kenlm/build/bin/lmplz -o 5 <"text.txt" > "5gram.arpa"
输出
=== 1/5 Counting and sorting n-grams ===
Reading /content/swedish_text.txt
----5---10---15---20---25---30---35---40---45---50---55---60---65---70---75---80---85---90---95--100
tcmalloc: large alloc 1918697472 bytes == 0x55d40d0f0000 @ 0x7fdccb1a91e7 0x55d40b2f17a2 0x55d40b28c51e 0x55d40b26b2eb 0x55d40b257066 0x7fdcc9342bf7 0x55d40b258baa
tcmalloc: large alloc 8953896960 bytes == 0x55d47f6c0000 @ 0x7fdccb1a91e7 0x55d40b2f17a2 0x55d40b2e07ca 0x55d40b2e1208 0x55d40b26b308 0x55d40b257066 0x7fdcc9342bf7 0x55d40b258baa
****************************************************************************************************
Unigram tokens 42153890 types 360209
=== 2/5 Calculating and sorting adjusted counts ===
Chain sizes: 1:4322508 2:1062772928 3:1992699264 4:3188318720 5:4649631744
tcmalloc: large alloc 4649631744 bytes == 0x55d40d0f0000 @ 0x7fdccb1a91e7 0x55d40b2f17a2 0x55d40b2e07ca 0x55d40b2e1208 0x55d40b26b8d7 0x55d40b257066 0x7fdcc9342bf7 0x55d40b258baa
tcmalloc: large alloc 1992704000 bytes == 0x55d561ce0000 @ 0x7fdccb1a91e7 0x55d40b2f17a2 0x55d40b2e07ca 0x55d40b2e1208 0x55d40b26bcdd 0x55d40b257066 0x7fdcc9342bf7 0x55d40b258baa
tcmalloc: large alloc 3188326400 bytes == 0x55d695a86000 @ 0x7fdccb1a91e7 0x55d40b2f17a2 0x55d40b2e07ca 0x55d40b2e1208 0x55d40b26bcdd 0x55d40b257066 0x7fdcc9342bf7 0x55d40b258baa
Statistics:
1 360208 D1=0.686222 D2=1.01595 D3+=1.33685
2 5476741 D1=0.761523 D2=1.06735 D3+=1.32559
3 18177681 D1=0.839918 D2=1.12061 D3+=1.33794
4 30374983 D1=0.909146 D2=1.20496 D3+=1.37235
5 37231651 D1=0.944104 D2=1.25164 D3+=1.344
Memory estimate for binary LM:
type MB
probing 1884 assuming -p 1.5
probing 2195 assuming -r models -p 1.5
trie 922 without quantization
trie 518 assuming -q 8 -b 8 quantization
trie 806 assuming -a 22 array pointer compression
trie 401 assuming -a 22 -q 8 -b 8 array pointer compression and quantization
=== 3/5 Calculating and sorting initial probabilities ===
Chain sizes: 1:4322496 2:87627856 3:363553620 4:728999592 5:1042486228
----5---10---15---20---25---30---35---40---45---50---55---60---65---70---75---80---85---90---95--100
####################################################################################################
=== 4/5 Calculating and writing order-interpolated probabilities ===
Chain sizes: 1:4322496 2:87627856 3:363553620 4:728999592 5:1042486228
----5---10---15---20---25---30---35---40---45---50---55---60---65---70---75---80---85---90---95--100
####################################################################################################
=== 5/5 Writing ARPA model ===
----5---10---15---20---25---30---35---40---45---50---55---60---65---70---75---80---85---90---95--100
****************************************************************************************************
Name:lmplz VmPeak:14181536 kB VmRSS:2199260 kB RSSMax:4160328 kB user:120.598 sys:26.6659 CPU:147.264 real:136.344
太棒了,我们已经构建了一个5-gram语言模型!让我们检查一下前几行。
head -20 5gram.arpa
输出
\data\
ngram 1=360208
ngram 2=5476741
ngram 3=18177681
ngram 4=30374983
ngram 5=37231651
\1-grams:
-6.770219 <unk> 0
0 <s> -0.11831701
-4.6095004 återupptagande -1.2174699
-2.2361007 av -0.79668784
-4.8163533 sessionen -0.37327805
-2.2251768 jag -1.4205662
-4.181505 förklarar -0.56261665
-3.5790775 europaparlamentets -0.63611007
-4.771945 session -0.3647111
-5.8043895 återupptagen -0.3058712
-2.8580177 efter -0.7557702
-5.199537 avbrottet -0.43322718
有一个小问题,🤗 Transformers之后会不高兴。这个5-gram正确地包含了“未知”或<unk>
,以及句首标记<s>
,但没有句尾标记</s>
。不幸的是,目前必须在构建后进行修正。
我们可以简单地在“句首”标记下方添加一行0 </s> -0.11831701
,并将ngram 1
计数增加1,从而添加“句尾”标记。由于文件大约有1亿行,此命令将耗时约2分钟。
with open("5gram.arpa", "r") as read_file, open("5gram_correct.arpa", "w") as write_file:
has_added_eos = False
for line in read_file:
if not has_added_eos and "ngram 1=" in line:
count=line.strip().split("=")[-1]
write_file.write(line.replace(f"{count}", f"{int(count)+1}"))
elif not has_added_eos and "<s>" in line:
write_file.write(line)
write_file.write(line.replace("<s>", "</s>"))
has_added_eos = True
else:
write_file.write(line)
现在让我们检查更正后的5-gram。
head -20 5gram_correct.arpa
输出
\data\
ngram 1=360209
ngram 2=5476741
ngram 3=18177681
ngram 4=30374983
ngram 5=37231651
\1-grams:
-6.770219 <unk> 0
0 <s> -0.11831701
0 </s> -0.11831701
-4.6095004 återupptagande -1.2174699
-2.2361007 av -0.79668784
-4.8163533 sessionen -0.37327805
-2.2251768 jag -1.4205662
-4.181505 förklarar -0.56261665
-3.5790775 europaparlamentets -0.63611007
-4.771945 session -0.3647111
-5.8043895 återupptagen -0.3058712
-2.8580177 efter -0.7557702
太棒了,这样看起来好多了!我们到此为止,剩下的就是将"ngram"
正确地集成到pyctcdecode
和🤗 Transformers中。
4. 将n-gram与Wav2Vec2结合
最后一步,我们将5-gram封装到Wav2Vec2ProcessorWithLM
对象中,使5-gram增强解码像第1节所示那样无缝。我们首先下载xls-r-300m-sv
当前“无LM”的处理器。
from transformers import AutoProcessor
processor = AutoProcessor.from_pretrained("hf-test/xls-r-300m-sv")
接下来,我们提取其分词器的词汇表,因为它代表了pyctcdecode
的BeamSearchDecoder
类的"labels"
。
vocab_dict = processor.tokenizer.get_vocab()
sorted_vocab_dict = {k.lower(): v for k, v in sorted(vocab_dict.items(), key=lambda item: item[1])}
"labels"
和之前构建的5gram_correct.arpa
文件是构建解码器所需的全部。
from pyctcdecode import build_ctcdecoder
decoder = build_ctcdecoder(
labels=list(sorted_vocab_dict.keys()),
kenlm_model_path="5gram_correct.arpa",
)
输出
Found entries of length > 1 in alphabet. This is unusual unless style is BPE, but the alphabet was not recognized as BPE type. Is this correct?
Unigrams and labels don't seem to agree.
我们可以安全地忽略警告,现在剩下的就是将刚刚创建的decoder
与处理器的tokenizer
和feature_extractor
一起封装到Wav2Vec2ProcessorWithLM
类中。
from transformers import Wav2Vec2ProcessorWithLM
processor_with_lm = Wav2Vec2ProcessorWithLM(
feature_extractor=processor.feature_extractor,
tokenizer=processor.tokenizer,
decoder=decoder
)
我们希望将LM增强的处理器直接上传到xls-r-300m-sv
的模型文件夹中,以便将所有相关文件放在一处。
让我们克隆仓库,添加新的解码器文件,然后上传它们。首先,我们需要安装git-lfs
。
sudo apt-get install git-lfs tree
使用huggingface_hub
的Repository
类可以方便地克隆和上传建模文件。
有关如何使用huggingface_hub
上传任何文件的更多信息,请参阅官方文档。
from huggingface_hub import Repository
repo = Repository(local_dir="xls-r-300m-sv", clone_from="hf-test/xls-r-300m-sv")
输出
Cloning https://huggingface.co/hf-test/xls-r-300m-sv into local empty directory.
克隆xls-r-300m-sv
后,让我们将新的带有LM的处理器保存到其中。
processor_with_lm.save_pretrained("xls-r-300m-sv")
让我们检查一下本地仓库。tree
命令可以方便地显示不同文件的大小。
tree -h xls-r-300m-sv/
输出
xls-r-300m-sv/
├── [ 23] added_tokens.json
├── [ 401] all_results.json
├── [ 253] alphabet.json
├── [2.0K] config.json
├── [ 304] emissions.csv
├── [ 226] eval_results.json
├── [4.0K] language_model
│ ├── [4.1G] 5gram_correct.arpa
│ ├── [ 78] attrs.json
│ └── [4.9M] unigrams.txt
├── [ 240] preprocessor_config.json
├── [1.2G] pytorch_model.bin
├── [3.5K] README.md
├── [4.0K] runs
│ └── [4.0K] Jan09_22-00-50_brutasse
│ ├── [4.0K] 1641765760.8871996
│ │ └── [4.6K] events.out.tfevents.1641765760.brutasse.31164.1
│ ├── [ 42K] events.out.tfevents.1641765760.brutasse.31164.0
│ └── [ 364] events.out.tfevents.1641794162.brutasse.31164.2
├── [1.2K] run.sh
├── [ 30K] run_speech_recognition_ctc.py
├── [ 502] special_tokens_map.json
├── [ 279] tokenizer_config.json
├── [ 29K] trainer_state.json
├── [2.9K] training_args.bin
├── [ 196] train_results.json
├── [ 319] vocab.json
└── [4.0K] wandb
├── [ 52] debug-internal.log -> run-20220109_220240-1g372i3v/logs/debug-internal.log
├── [ 43] debug.log -> run-20220109_220240-1g372i3v/logs/debug.log
├── [ 28] latest-run -> run-20220109_220240-1g372i3v
└── [4.0K] run-20220109_220240-1g372i3v
├── [4.0K] files
│ ├── [8.8K] conda-environment.yaml
│ ├── [140K] config.yaml
│ ├── [4.7M] output.log
│ ├── [5.4K] requirements.txt
│ ├── [2.1K] wandb-metadata.json
│ └── [653K] wandb-summary.json
├── [4.0K] logs
│ ├── [3.4M] debug-internal.log
│ └── [8.2K] debug.log
└── [113M] run-1g372i3v.wandb
9 directories, 34 files
如您所见,5-gram LM相当大——它超过4GB。为了减小n-gram的大小并加快加载速度,kenLM
允许使用build_binary
可执行文件将.arpa
文件转换为二进制文件。
让我们在这里利用它。
kenlm/build/bin/build_binary xls-r-300m-sv/language_model/5gram_correct.arpa xls-r-300m-sv/language_model/5gram.bin
输出
Reading xls-r-300m-sv/language_model/5gram_correct.arpa
----5---10---15---20---25---30---35---40---45---50---55---60---65---70---75---80---85---90---95--100
****************************************************************************************************
SUCCESS
太棒了,成功了!让我们删除.arpa
文件,并检查二进制5-gram语言模型的大小。
rm xls-r-300m-sv/language_model/5gram_correct.arpa && tree -h xls-r-300m-sv/
输出
xls-r-300m-sv/
├── [ 23] added_tokens.json
├── [ 401] all_results.json
├── [ 253] alphabet.json
├── [2.0K] config.json
├── [ 304] emissions.csv
├── [ 226] eval_results.json
├── [4.0K] language_model
│ ├── [1.8G] 5gram.bin
│ ├── [ 78] attrs.json
│ └── [4.9M] unigrams.txt
├── [ 240] preprocessor_config.json
├── [1.2G] pytorch_model.bin
├── [3.5K] README.md
├── [4.0K] runs
│ └── [4.0K] Jan09_22-00-50_brutasse
│ ├── [4.0K] 1641765760.8871996
│ │ └── [4.6K] events.out.tfevents.1641765760.brutasse.31164.1
│ ├── [ 42K] events.out.tfevents.1641765760.brutasse.31164.0
│ └── [ 364] events.out.tfevents.1641794162.brutasse.31164.2
├── [1.2K] run.sh
├── [ 30K] run_speech_recognition_ctc.py
├── [ 502] special_tokens_map.json
├── [ 279] tokenizer_config.json
├── [ 29K] trainer_state.json
├── [2.9K] training_args.bin
├── [ 196] train_results.json
├── [ 319] vocab.json
└── [4.0K] wandb
├── [ 52] debug-internal.log -> run-20220109_220240-1g372i3v/logs/debug-internal.log
├── [ 43] debug.log -> run-20220109_220240-1g372i3v/logs/debug.log
├── [ 28] latest-run -> run-20220109_220240-1g372i3v
└── [4.0K] run-20220109_220240-1g372i3v
├── [4.0K] files
│ ├── [8.8K] conda-environment.yaml
│ ├── [140K] config.yaml
│ ├── [4.7M] output.log
│ ├── [5.4K] requirements.txt
│ ├── [2.1K] wandb-metadata.json
│ └── [653K] wandb-summary.json
├── [4.0K] logs
│ ├── [3.4M] debug-internal.log
│ └── [8.2K] debug.log
└── [113M] run-1g372i3v.wandb
9 directories, 34 files
太棒了,我们将n-gram的大小减少了一半以上,现在不到2GB。最后一步,让我们上传所有文件。
repo.push_to_hub(commit_message="Upload lm-boosted decoder")
输出
Git LFS: (1 of 1 files) 1.85 GB / 1.85 GB
Counting objects: 9, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (9/9), done.
Writing objects: 100% (9/9), 1.23 MiB | 1.92 MiB/s, done.
Total 9 (delta 3), reused 0 (delta 0)
To https://huggingface.co/hf-test/xls-r-300m-sv
27d0c57..5a191e2 main -> main
就这样。现在你应该能够像第1节中所示那样使用5gram进行LM增强解码了。
正如xls-r-300m-sv
的模型卡所示,我们的5gram LM增强解码器在Common Voice的7个测试集上获得了18.85%的WER,这是一个相对约30%的性能提升🔥。