lucio commited on
Commit
007afbd
1 Parent(s): 75a6d6a

add code for using kenlm

Browse files
Files changed (2) hide show
  1. Fine_Tune_XLS_R_on_Common_Voice.ipynb +0 -0
  2. eval_lm.py +229 -0
Fine_Tune_XLS_R_on_Common_Voice.ipynb ADDED
The diff for this file is too large to render. See raw diff
eval_lm.py ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import argparse
3
+ import functools
4
+ import re
5
+ import string
6
+ import unidecode
7
+ from typing import Dict
8
+
9
+ from datasets import Audio, Dataset, DatasetDict, load_dataset, load_metric
10
+
11
+ from transformers import AutoFeatureExtractor, AutoTokenizer, pipeline
12
+
13
+
14
+ def log_results(result: Dataset, args: Dict[str, str]):
15
+ """DO NOT CHANGE. This function computes and logs the result metrics."""
16
+
17
+ log_outputs = args.log_outputs
18
+ dataset_id = "_".join(args.dataset.split("/") + [args.config, args.split])
19
+
20
+ # load metric
21
+ wer = load_metric("wer")
22
+ cer = load_metric("cer")
23
+
24
+ # compute metrics
25
+ wer_result = wer.compute(references=result["target"], predictions=result["prediction"])
26
+ cer_result = cer.compute(references=result["target"], predictions=result["prediction"])
27
+
28
+ # print & log results
29
+ result_str = f"WER: {wer_result}\n" f"CER: {cer_result}"
30
+ print(result_str)
31
+
32
+ with open(f"{dataset_id}_eval_results.txt", "w") as f:
33
+ f.write(result_str)
34
+
35
+ # log all results in text file. Possibly interesting for analysis
36
+ if log_outputs is not None:
37
+ pred_file = f"log_{dataset_id}_predictions.txt"
38
+ target_file = f"log_{dataset_id}_targets.txt"
39
+
40
+ with open(pred_file, "w") as p, open(target_file, "w") as t:
41
+
42
+ # mapping function to write output
43
+ def write_to_file(batch, i):
44
+ p.write(f"{i}" + "\n")
45
+ p.write(batch["prediction"] + "\n")
46
+ t.write(f"{i}" + "\n")
47
+ t.write(batch["target"] + "\n")
48
+
49
+ result.map(write_to_file, with_indices=True)
50
+
51
+
52
+ def normalize_text(text: str) -> str:
53
+ """DO ADAPT FOR YOUR USE CASE. this function normalizes the target text."""
54
+
55
+ chars_to_ignore_regex = f'[{re.escape(string.punctuation)}]' # noqa: W605 IMPORTANT: this should correspond to the chars that were ignored during training
56
+
57
+ text = re.sub(
58
+ chars_to_ignore_regex,
59
+ "",
60
+ re.sub("['`´]", "’", # elsewhere probably meant as glottal stop
61
+ re.sub("([og])['`´]", "\g<1>‘", # after o/g indicate modified char
62
+ unidecode.unidecode(text).lower()
63
+ )
64
+ )
65
+ ) + " "
66
+
67
+ # In addition, we can normalize the target text, e.g. removing new lines characters etc...
68
+ # note that order is important here!
69
+ token_sequences_to_ignore = ["\n\n", "\n", " ", " "]
70
+
71
+ for t in token_sequences_to_ignore:
72
+ text = " ".join(text.split(t))
73
+
74
+ return text
75
+
76
+
77
+ def create_vocabulary_from_data(
78
+ datasets: DatasetDict,
79
+ word_delimiter_token = None,
80
+ unk_token = None,
81
+ pad_token = None,
82
+ ):
83
+ # Given training and test labels create vocabulary
84
+ def extract_all_chars(batch):
85
+ all_text = " ".join(batch["target"])
86
+ vocab = list(set(all_text))
87
+ return {"vocab": [vocab], "all_text": [all_text]}
88
+
89
+ vocabs = datasets.map(
90
+ extract_all_chars,
91
+ batched=True,
92
+ batch_size=-1,
93
+ keep_in_memory=True,
94
+ remove_columns=datasets["test"].column_names,
95
+ )
96
+
97
+
98
+ vocab_dict = {v: k for k, v in enumerate(sorted(vocabs["test"]["vocab"][0]))}
99
+
100
+ # replace white space with delimiter token
101
+ if word_delimiter_token is not None:
102
+ vocab_dict[word_delimiter_token] = vocab_dict[" "]
103
+ del vocab_dict[" "]
104
+
105
+ # add unk and pad token
106
+ if unk_token is not None:
107
+ vocab_dict[unk_token] = len(vocab_dict)
108
+
109
+ if pad_token is not None:
110
+ vocab_dict[pad_token] = len(vocab_dict)
111
+
112
+ return vocab_dict
113
+
114
+
115
+ def asr_pipeline_lm(model_id):
116
+ from transformers.models.auto.modeling_auto import AutoModelForCTC
117
+ from transformers.models.auto.processing_auto import AutoProcessor
118
+ import torch
119
+
120
+ model = AutoModelForCTC.from_pretrained(model_id)
121
+ processor = AutoProcessor.from_pretrained(model_id)
122
+
123
+ def asr(audio_array, sampling_rate):
124
+ input_values = processor(
125
+ audio_array,
126
+ return_tensors="pt",
127
+ sampling_rate=sampling_rate
128
+ ).input_values
129
+ with torch.no_grad():
130
+ logits = model(input_values).logits
131
+ return processor.batch_decode(logits.numpy()).text
132
+
133
+ return asr
134
+
135
+
136
+ def main(args):
137
+ # load dataset
138
+ dataset = load_dataset(args.dataset, args.config, split=args.split, use_auth_token=True)
139
+
140
+ # for testing: only process the first two examples as a test
141
+ # dataset = dataset.select(range(10))
142
+
143
+ # load processor
144
+ feature_extractor = AutoFeatureExtractor.from_pretrained(args.model_id)
145
+ sampling_rate = feature_extractor.sampling_rate
146
+
147
+ # resample audio
148
+ dataset = dataset.cast_column("audio", Audio(sampling_rate=sampling_rate))
149
+
150
+ if args.use_lm:
151
+ asr = asr_pipeline_lm(args.model_id)
152
+
153
+ def map_to_pred(batch):
154
+ prediction = asr(batch["audio"]["array"])
155
+ batch["prediction"] = prediction[0]
156
+ batch["target"] = normalize_text(batch["sentence"])
157
+ return batch
158
+
159
+ else:
160
+ # load eval pipeline
161
+ asr = pipeline("automatic-speech-recognition", model=args.model_id)
162
+
163
+ # map function to decode audio
164
+ def map_to_pred(batch):
165
+ prediction = asr(
166
+ batch["audio"]["array"], chunk_length_s=args.chunk_length_s, stride_length_s=args.stride_length_s
167
+ )
168
+
169
+ batch["prediction"] = prediction["text"]
170
+ batch["target"] = normalize_text(batch["sentence"])
171
+ return batch
172
+
173
+ # run inference on all examples
174
+ result = dataset.map(map_to_pred, remove_columns=dataset.column_names)
175
+
176
+ # compute and log_results
177
+ # do not change function below
178
+ log_results(result, args)
179
+
180
+ if args.check_vocab:
181
+ tokenizer = AutoTokenizer.from_pretrained(args.model_id)
182
+ unk_token = "[UNK]"
183
+ pad_token = "[PAD]"
184
+ word_delimiter_token = "|"
185
+ raw_datasets = DatasetDict({"test": result})
186
+ vocab_dict = create_vocabulary_from_data(
187
+ raw_datasets,
188
+ word_delimiter_token=word_delimiter_token,
189
+ unk_token=unk_token,
190
+ pad_token=pad_token,
191
+ )
192
+ print(vocab_dict)
193
+ print("OOV chars:", set(vocab_dict) - set(tokenizer.get_vocab()))
194
+
195
+
196
+ if __name__ == "__main__":
197
+ parser = argparse.ArgumentParser()
198
+
199
+ parser.add_argument(
200
+ "--model_id", type=str, required=True, help="Model identifier. Should be loadable with 🤗 Transformers"
201
+ )
202
+ parser.add_argument(
203
+ "--dataset",
204
+ type=str,
205
+ required=True,
206
+ help="Dataset name to evaluate the `model_id`. Should be loadable with 🤗 Datasets",
207
+ )
208
+ parser.add_argument(
209
+ "--config", type=str, required=True, help="Config of the dataset. *E.g.* `'en'` for Common Voice"
210
+ )
211
+ parser.add_argument("--split", type=str, required=True, help="Split of the dataset. *E.g.* `'test'`")
212
+ parser.add_argument(
213
+ "--chunk_length_s", type=float, default=None, help="Chunk length in seconds. Defaults to 5 seconds."
214
+ )
215
+ parser.add_argument(
216
+ "--stride_length_s", type=float, default=None, help="Stride of the audio chunks. Defaults to 1 second."
217
+ )
218
+ parser.add_argument(
219
+ "--log_outputs", action="store_true", help="If defined, write outputs to log file for analysis."
220
+ )
221
+ parser.add_argument(
222
+ "--check_vocab", action="store_true", help="Verify that normalized target text is within character set"
223
+ )
224
+ parser.add_argument(
225
+ "--use-lm", action="store_true", help="Use kenlm decoder"
226
+ )
227
+ args = parser.parse_args()
228
+
229
+ main(args)