-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtrain.py
More file actions
168 lines (120 loc) · 5.41 KB
/
Copy pathtrain.py
File metadata and controls
168 lines (120 loc) · 5.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
import os
import librosa
import random
import torch
import evaluate
import torchaudio
import numpy as np
import pandas as pd
from datasets import Dataset
from dataclasses import dataclass
from typing import Any, Dict, List, Union
from transformers import Seq2SeqTrainer
from transformers import Seq2SeqTrainingArguments
from model.tokenization_whisper import WhisperTokenizer
from model.processing_whisper import WhisperProcessor
from model.feature_extraction_whisper import WhisperFeatureExtractor
from model.modeling_whisper import WhisperForConditionalGeneration
# common_voice = Data
model_type = "openai/whisper-small"
mp3_path = "./dataset/clips"
train_tsv = "./dataset/train.tsv"
val_tsv = "./dataset/dev.tsv"
def read_data(data_path, data_chunk = 0):
if data_chunk==0:
data = pd.read_csv(data_path, sep='\t')
else:
data = pd.read_csv(data_path, sep='\t')[:data_chunk]
data.columns = ["client_id", "path", "sentence", "up_votes", "down_votes", "age", "gender", "accents", "locale", "segment"]
data = data.drop(["client_id","up_votes", "down_votes", "age", "gender", "accents", "locale", "segment"], axis=1)
return data
train_data = read_data(train_tsv, data_chunk=0)
val_data = read_data(val_tsv, data_chunk=0)
def add_file_path(path):
return os.path.join(mp3_path, path)
# read data from tsv file
feature_extractor = WhisperFeatureExtractor.from_pretrained(model_type)
tokenizer = WhisperTokenizer.from_pretrained(model_type, language="Bengali", task="transcribe")
processor = WhisperProcessor.from_pretrained(model_type, language="Bengali", task="transcribe")
def prepare_dataset(batch):
speech_array, sampling_rate = torchaudio.load(batch["path"], format="mp3")
speech_array = speech_array[0].numpy()
speech_array = librosa.resample(np.asarray(speech_array), orig_sr=sampling_rate, target_sr=16000)
batch["input_features"] = feature_extractor(speech_array, sampling_rate=16000).input_features[0]
batch["sampling_rate"] = 16000
batch["labels"] = tokenizer(batch["sentence"]).input_ids
return batch
train_data['path'] = train_data['path'].map(lambda x: add_file_path(x))
val_data['path'] = val_data['path'].map(lambda x: add_file_path(x))
train_data = Dataset.from_pandas(train_data)
val_data = Dataset.from_pandas(val_data)
train_data2 = train_data.map(prepare_dataset, num_proc=4)
val_data2 = val_data.map(prepare_dataset, num_proc=4)
@dataclass
class DataCollatorSpeechSeq2SeqWithPadding:
processor: Any
def __call__(self, features: List[Dict[str, Union[List[int], torch.Tensor]]]) -> Dict[str, torch.Tensor]:
# split inputs and labels since they have to be of different lengths and need different padding methods
# first treat the audio inputs by simply returning torch tensors
input_features = [{"input_features": feature["input_features"]} for feature in features]
batch = self.processor.feature_extractor.pad(input_features, return_tensors="pt")
# get the tokenized label sequences
label_features = [{"input_ids": feature["labels"]} for feature in features]
# pad the labels to max length
labels_batch = self.processor.tokenizer.pad(label_features, return_tensors="pt")
# replace padding with -100 to ignore loss correctly
labels = labels_batch["input_ids"].masked_fill(labels_batch.attention_mask.ne(1), -100)
# if bos token is appended in previous tokenization step,
# cut bos token here as it's append later anyways
if (labels[:, 0] == self.processor.tokenizer.bos_token_id).all().cpu().item():
labels = labels[:, 1:]
batch["labels"] = labels
return batch
data_collator = DataCollatorSpeechSeq2SeqWithPadding(processor=processor)
metric = evaluate.load("wer")
def compute_metrics(pred):
pred_ids = pred.predictions
label_ids = pred.label_ids
# replace -100 with the pad_token_id
label_ids[label_ids == -100] = tokenizer.pad_token_id
# we do not want to group tokens when computing the metrics
pred_str = tokenizer.batch_decode(pred_ids, skip_special_tokens=True)
label_str = tokenizer.batch_decode(label_ids, skip_special_tokens=True)
wer = 100 * metric.compute(predictions=pred_str, references=label_str)
return {"wer": wer}
model = WhisperForConditionalGeneration.from_pretrained(model_type)
model.config.forced_decoder_ids = None
model.config.suppress_tokens = []
training_args = Seq2SeqTrainingArguments(
output_dir="./whisper-small-bn", # change to a repo name of your choice
per_device_train_batch_size=26, #16
gradient_accumulation_steps=1, # increase by 2x for every 2x decrease in batch size
learning_rate=1e-5,
warmup_steps=500,
max_steps=12000,
gradient_checkpointing=True,
fp16=True,
evaluation_strategy="steps",
per_device_eval_batch_size=26,
predict_with_generate=True,
generation_max_length=225,
save_steps=500,
eval_steps=500,
logging_steps=25,
report_to=["tensorboard"],
load_best_model_at_end=True,
metric_for_best_model="wer",
greater_is_better=False,
push_to_hub=False,
)
trainer = Seq2SeqTrainer(
args=training_args,
model=model,
train_dataset=train_data2,
eval_dataset=val_data2,
data_collator=data_collator,
compute_metrics=compute_metrics,
tokenizer=processor.feature_extractor,
)
processor.save_pretrained(training_args.output_dir)
trainer.train()#provide a checkpoint path if you want to