Views
No views yet
@misc{mfleck/wav2vec2-large-xls-r-300m-german-with-lm,
title={XLS-R-300 Wav2Vec2 German with language model},
author={Fleck, Michael},
publisher={Hugging Face},
journal={Hugging Face Hub},
howpublished={\url{https://huggingface.co/mfleck/wav2vec2-large-xls-r-300m-german-with-lm}},
year={2022}
}1from transformers import pipeline
2
3pipe = pipeline(model="mfleck/wav2vec2-large-xls-r-300m-german-with-lm")
4output = pipe("/path/to/file.wav",chunk_length_s=5, stride_length_s=1)
5print(output["text"])1import random
2import re
3import json
4from typing import Any, Dict, List, Optional, Union
5
6import pandas as pd
7import numpy as np
8import torch
9# import soundfile
10
11from datasets import load_dataset, load_metric, Audio
12from dataclasses import dataclass, field
13
14from transformers import Wav2Vec2CTCTokenizer, Wav2Vec2FeatureExtractor, Wav2Vec2Processor, TrainingArguments, Trainer, Wav2Vec2ForCTC
15
16
17'''
18 Most parts of this script are following the tutorial: https://huggingface.co/blog/fine-tune-xlsr-wav2vec2
19'''
20
21
22common_voice_train = load_dataset("common_voice", "de", split="train+validation")
23# Use train dataset with less training data
24#common_voice_train = load_dataset("common_voice", "de", split="train[:3%]")
25common_voice_test = load_dataset("common_voice", "de", split="test")
26
27
28# Remove unused columns
29common_voice_train = common_voice_train.remove_columns(["accent", "age", "client_id", "down_votes", "gender", "locale", "segment", "up_votes"])
30common_voice_test = common_voice_test.remove_columns(["accent", "age", "client_id", "down_votes", "gender", "locale", "segment", "up_votes"])
31
32
33# Remove batches with chars which do not exist in German
34print(len(common_voice_train))
35regex = "[^A-Za-zäöüÄÖÜß,?.! ]+"
36common_voice_train = common_voice_train.filter(lambda example: bool(re.search(regex, example['sentence']))==False)
37common_voice_test = common_voice_test.filter(lambda example: bool(re.search(regex, example['sentence']))==False)
38print(len(common_voice_train))
39
40
41# Remove special chars from transcripts
42chars_to_remove_regex = '[\,\?\.\!\-\;\:\"\“\%\‘\”\�\']'
43def remove_special_characters(batch):
44 batch["sentence"] = re.sub(chars_to_remove_regex, '', batch["sentence"]).lower()
45 return batch
46common_voice_train = common_voice_train.map(remove_special_characters, num_proc=10)
47common_voice_test = common_voice_test.map(remove_special_characters, num_proc=10)
48
49
50# Show some random transcripts to proof that preprocessing worked as expected
51def show_random_elements(dataset, num_examples=10):
52 assert num_examples <= len(dataset), "Can't pick more elements than there are in the dataset."
53 picks = []
54 for _ in range(num_examples):
55 pick = random.randint(0, len(dataset)-1)
56 while pick in picks:
57 pick = random.randint(0, len(dataset)-1)
58 picks.append(pick)
59
60 print(str(dataset[picks]))
61show_random_elements(common_voice_train.remove_columns(["path","audio"]))
62
63
64# Extract all chars which exist in datasets and add wav2vek tokens
65def extract_all_chars(batch):
66 all_text = " ".join(batch["sentence"])
67 vocab = list(set(all_text))
68 return {"vocab": [vocab], "all_text": [all_text]}
69vocab_train = common_voice_train.map(extract_all_chars, batched=True, batch_size=-1, keep_in_memory=True, remove_columns=common_voice_train.column_names)
70vocab_test = common_voice_test.map(extract_all_chars, batched=True, batch_size=-1, keep_in_memory=True, remove_columns=common_voice_test.column_names)
71
72vocab_list = list(set(vocab_train["vocab"][0]) | set(vocab_test["vocab"][0]))
73vocab_dict = {v: k for k, v in enumerate(sorted(vocab_list))}
74vocab_dict
75vocab_dict["|"] = vocab_dict[" "]
76del vocab_dict[" "]
77vocab_dict["[UNK]"] = len(vocab_dict)
78vocab_dict["[PAD]"] = len(vocab_dict)
79len(vocab_dict)
80with open('vocab.json', 'w') as vocab_file:
81 json.dump(vocab_dict, vocab_file)
82
83
84
85# Create tokenizer and repo at Huggingface
86tokenizer = Wav2Vec2CTCTokenizer.from_pretrained("./", unk_token="[UNK]", pad_token="[PAD]", word_delimiter_token="|")
87repo_name = "wav2vec2-large-xls-r-300m-german-with-lm"
88tokenizer.push_to_hub(repo_name)
89print("pushed to hub")
90
91
92
93# Create feature extractor and processor
94feature_extractor = Wav2Vec2FeatureExtractor(feature_size=1, sampling_rate=16000, padding_value=0.0, do_normalize=True, return_attention_mask=True)
95processor = Wav2Vec2Processor(feature_extractor=feature_extractor, tokenizer=tokenizer)
96
97
98# Cast audio column
99common_voice_train = common_voice_train.cast_column("audio", Audio(sampling_rate=16_000))
100common_voice_test = common_voice_test.cast_column("audio", Audio(sampling_rate=16_000))
101
102
103# Convert audio signal to array and 16khz sampling rate
104def prepare_dataset(batch):
105 audio = batch["audio"]
106
107 # batched output is "un-batched"
108 batch["input_values"] = processor(audio["array"], sampling_rate=audio["sampling_rate"]).input_values[0]
109 # Save an audio file to check if it gets loaded correctly
110 # soundfile.write("/home/debian/trainnew/test.wav",batch["input_values"],audio["sampling_rate"])
111 batch["input_length"] = len(batch["input_values"])
112
113 with processor.as_target_processor():
114 batch["labels"] = processor(batch["sentence"]).input_ids
115 return batch
116
117common_voice_train = common_voice_train.map(prepare_dataset, remove_columns=common_voice_train.column_names)
118common_voice_test = common_voice_test.map(prepare_dataset, remove_columns=common_voice_test.column_names)
119print("dataset prepared")
120
121
122
123
124@dataclass
125class DataCollatorCTCWithPadding:
126 """
127 Data collator that will dynamically pad the inputs received.
128 Args:
129 processor (:class:`~transformers.Wav2Vec2Processor`)
130 The processor used for proccessing the data.
131 padding (:obj:`bool`, :obj:`str` or :class:`~transformers.tokenization_utils_base.PaddingStrategy`, `optional`, defaults to :obj:`True`):
132 Select a strategy to pad the returned sequences (according to the model's padding side and padding index)
133 among:
134 * :obj:`True` or :obj:`'longest'`: Pad to the longest sequence in the batch (or no padding if only a single
135 sequence if provided).
136 * :obj:`'max_length'`: Pad to a maximum length specified with the argument :obj:`max_length` or to the
137 maximum acceptable input length for the model if that argument is not provided.
138 * :obj:`False` or :obj:`'do_not_pad'` (default): No padding (i.e., can output a batch with sequences of
139 different lengths).
140 """
141
142 processor: Wav2Vec2Processor
143 padding: Union[bool, str] = True
144
145 def __call__(self, features: List[Dict[str, Union[List[int], torch.Tensor]]]) -> Dict[str, torch.Tensor]:
146 # split inputs and labels since they have to be of different lenghts and need
147 # different padding methods
148 input_features = [{"input_values": feature["input_values"]} for feature in features]
149 label_features = [{"input_ids": feature["labels"]} for feature in features]
150
151 batch = self.processor.pad(
152 input_features,
153 padding=self.padding,
154 return_tensors="pt",
155 )
156 with self.processor.as_target_processor():
157 labels_batch = self.processor.pad(
158 label_features,
159 padding=self.padding,
160 return_tensors="pt",
161 )
162
163 # replace padding with -100 to ignore loss correctly
164 labels = labels_batch["input_ids"].masked_fill(labels_batch.attention_mask.ne(1), -100)
165
166 batch["labels"] = labels
167
168 return batch
169
170data_collator = DataCollatorCTCWithPadding(processor=processor, padding=True)
171
172
173# Use word error rate as metric
174wer_metric = load_metric("wer")
175def compute_metrics(pred):
176 pred_logits = pred.predictions
177 pred_ids = np.argmax(pred_logits, axis=-1)
178
179 pred.label_ids[pred.label_ids == -100] = processor.tokenizer.pad_token_id
180
181 pred_str = processor.batch_decode(pred_ids)
182 # we do not want to group tokens when computing the metrics
183 label_str = processor.batch_decode(pred.label_ids, group_tokens=False)
184
185 wer = wer_metric.compute(predictions=pred_str, references=label_str)
186
187 return {"wer": wer}
188
189
190
191# Model and training parameters
192model = Wav2Vec2ForCTC.from_pretrained(
193 "facebook/wav2vec2-xls-r-300m",
194 attention_dropout=0.094,
195 hidden_dropout=0.01,
196 feat_proj_dropout=0.04,
197 mask_time_prob=0.08,
198 layerdrop=0.04,
199 ctc_loss_reduction="mean",
200 pad_token_id=processor.tokenizer.pad_token_id,
201 vocab_size=len(processor.tokenizer),
202)
203model.freeze_feature_extractor()
204
205training_args = TrainingArguments(
206 output_dir=repo_name,
207 group_by_length=True,
208 per_device_train_batch_size=32,
209 gradient_accumulation_steps=2,
210 evaluation_strategy="steps",
211 num_train_epochs=20,
212 gradient_checkpointing=True,
213 fp16=True,
214 save_steps=5000,
215 eval_steps=5000,
216 logging_steps=100,
217 learning_rate=1e-4,
218 warmup_steps=500,
219 save_total_limit=3,
220 push_to_hub=True,
221)
222
223trainer = Trainer(
224 model=model,
225 data_collator=data_collator,
226 args=training_args,
227 compute_metrics=compute_metrics,
228 train_dataset=common_voice_train,
229 eval_dataset=common_voice_test,
230 tokenizer=processor.feature_extractor,
231)
232
233# Start fine tuning
234trainer.train()
235
236# When done push final model to Huggingface hub
237trainer.push_to_hub()1import argparse
2import re
3from typing import Dict
4
5import torch
6from datasets import Audio, Dataset, load_dataset, load_metric
7
8from transformers import AutoFeatureExtractor, pipeline
9
10
11
12# load dataset
13dataset = load_dataset("common_voice", "de", split="test")
14# use only 1% of data
15#dataset = load_dataset("common_voice", "de", split="test[:1%]")
16
17
18# load processor
19feature_extractor = AutoFeatureExtractor.from_pretrained("mfleck/wav2vec2-large-xls-r-300m-german-with-lm")
20sampling_rate = feature_extractor.sampling_rate
21
22dataset = dataset.cast_column("audio", Audio(sampling_rate=sampling_rate))
23
24# load eval pipeline
25# device=0 means GPU, use device=-1 for CPU
26asr = pipeline("automatic-speech-recognition", model="mfleck/wav2vec2-large-xls-r-300m-german-with-lm", device=0)
27
28# Remove batches with chars which do not exist in German
29regex = "[^A-Za-zäöüÄÖÜß,?.! ]+"
30dataset = dataset.filter(lambda example: bool(re.search(regex, example['sentence']))==False)
31
32chars_to_ignore_regex = '[\,\?\.\!\-\;\:\"\“\%\‘\”\�\']'
33# map function to decode audio
34def map_to_pred(batch):
35 prediction = asr(batch["audio"]["array"], chunk_length_s=5, stride_length_s=1)
36
37 # Print automatic generated transcript
38 #print(str(prediction))
39
40 batch["prediction"] = prediction["text"]
41 text = batch["sentence"]
42 batch["target"] = re.sub(chars_to_ignore_regex, "", text.lower()) + " "
43
44 return batch
45
46# run inference on all examples
47result = dataset.map(map_to_pred, remove_columns=dataset.column_names)
48
49# load metric
50wer = load_metric("wer")
51cer = load_metric("cer")
52
53# compute metrics
54wer_result = wer.compute(references=result["target"], predictions=result["prediction"])
55cer_result = cer.compute(references=result["target"], predictions=result["prediction"])
56
57# print results
58result_str = f"WER: {wer_result}\n" f"CER: {cer_result}"
59print(result_str)| Training Loss | Epoch | Step | Validation Loss | Wer |
|---|---|---|---|---|
| 0.1396 | 1.42 | 5000 | 0.1449 | 0.1479 |
| 0.1169 | 2.83 | 10000 | 0.1285 | 0.1286 |
| 0.0938 | 4.25 | 15000 | 0.1277 | 0.1230 |
| 0.0924 | 5.67 | 20000 | 0.1305 | 0.1191 |
| 0.0765 | 7.09 | 25000 | 0.1256 | 0.1158 |
| 0.0749 | 8.5 | 30000 | 0.1186 | 0.1092 |
| 0.066 | 9.92 | 35000 | 0.1173 | 0.1068 |
| 0.0581 | 11.34 | 40000 | 0.1225 | 0.1030 |
| 0.0582 | 12.75 | 45000 | 0.1153 | 0.0999 |
| 0.0507 | 14.17 | 50000 | 0.1182 | 0.0971 |
| 0.0491 | 15.59 | 55000 | 0.1136 | 0.0939 |
| 0.045 | 17.01 | 60000 | 0.1140 | 0.0914 |
| 0.0395 | 18.42 | 65000 | 0.1160 | 0.0902 |
| 0.037 | 19.84 | 70000 | 0.1148 | 0.0882 |