maxseats commited on
Commit
2e9613b
·
verified ·
1 Parent(s): 6ebb17f

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +281 -3
README.md CHANGED
@@ -11,10 +11,288 @@ metrics:
11
  ---
12
  # Model Name : maxseats/SungBeom-whisper-small-ko-set0
13
  # Description
 
14
 
15
- - 파인튜닝 데이터셋 : maxseats/aihub-464-preprocessed-680GB-set-0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
  # 설명
18
- - 주요 영역별 회의 음성 데이터셋 680GB 중 첫번째 데이터(10GB)를 파인튜닝한 모델입니다.
19
- - 링크 : https://huggingface.co/datasets/maxseats/aihub-464-preprocessed-680GB-set-0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
 
11
  ---
12
  # Model Name : maxseats/SungBeom-whisper-small-ko-set0
13
  # Description
14
+ - 파인튜닝 데이터셋 : maxseats/aihub-464-preprocessed-680GB-set-1
15
 
16
+ # 설명
17
+ - AI hub의 주요 영역별 회의 음성 데이터셋을 학습 중이에요.
18
+ - 680GB 중 첫번째 데이터(10GB)를 파인튜닝한 모델을 불러와서, 두번째 데이터를 학습한 모델입니다.
19
+ - 링크 : https://huggingface.co/datasets/maxseats/aihub-464-preprocessed-680GB-set-0, https://huggingface.co/datasets/maxseats/aihub-464-preprocessed-680GB-set-1
20
+
21
+ - 다음 코드를 통해 작성했어요.
22
+
23
+ ```
24
+ from datasets import load_dataset
25
+ import torch
26
+ from dataclasses import dataclass
27
+ from typing import Any, Dict, List, Union
28
+ import evaluate
29
+ from transformers import WhisperTokenizer, WhisperFeatureExtractor, WhisperProcessor, WhisperForConditionalGeneration, Seq2SeqTrainingArguments, Seq2SeqTrainer
30
+ import mlflow
31
+ from mlflow.tracking.client import MlflowClient
32
+ import subprocess
33
+ from huggingface_hub import create_repo, Repository
34
+ import os
35
+ import shutil
36
+ import math # 임시 테스트용
37
+ model_dir = "./tmpp" # 수정 X
38
+
39
+
40
+ #########################################################################################################################################
41
+ ################################################### 사용자 설정 변수 #####################################################################
42
+ #########################################################################################################################################
43
+
44
+ model_description = """
45
+ - 파인튜닝 데이터셋 : maxseats/aihub-464-preprocessed-680GB-set-1
46
 
47
  # 설명
48
+ - AI hub의 주요 영역별 회의 음성 데이터셋을 학습 중이에요.
49
+ - 680GB 첫번째 데이터(10GB)를 파인튜닝한 모델을 불러와서, 두번째 데이터를 학습한 모델입니다.
50
+ - 링크 : https://huggingface.co/datasets/maxseats/aihub-464-preprocessed-680GB-set-0, https://huggingface.co/datasets/maxseats/aihub-464-preprocessed-680GB-set-1
51
+ """
52
+
53
+ # model_name = "openai/whisper-base"
54
+ model_name = "maxseats/SungBeom-whisper-small-ko-set0" # 대안 : "SungBeom/whisper-small-ko"
55
+ # dataset_name = "maxseats/aihub-464-preprocessed-680GB-set-1" # 불러올 데이터셋(허깅페이스 기준)
56
+ dataset_name = "maxseats/aihub-464-preprocessed-680GB-set-1" # 불러올 데이터셋(허깅페이스 기준)
57
+
58
+ CACHE_DIR = '/mnt/a/maxseats/.finetuning_cache' # 캐시 디렉토리 지정
59
+ is_test = False # True: 소량의 샘플 데이터로 테스트, False: 실제 파인튜닝
60
+
61
+ token = "hf_" # 허깅페이스 토큰 입력
62
+
63
+ training_args = Seq2SeqTrainingArguments(
64
+ output_dir=model_dir, # 원하는 리포지토리 이름을 입력한다.
65
+ per_device_train_batch_size=16,
66
+ gradient_accumulation_steps=2, # 배치 크기가 2배 감소할 때마다 2배씩 증가
67
+ learning_rate=1e-5,
68
+ warmup_steps=500,
69
+ # max_steps=2, # epoch 대신 설정
70
+ num_train_epochs=1, # epoch 수 설정 / max_steps와 이것 중 하나만 설정
71
+ gradient_checkpointing=True,
72
+ fp16=True,
73
+ evaluation_strategy="steps",
74
+ per_device_eval_batch_size=16,
75
+ predict_with_generate=True,
76
+ generation_max_length=225,
77
+ save_steps=1000,
78
+ eval_steps=1000,
79
+ logging_steps=25,
80
+ report_to=["tensorboard"],
81
+ load_best_model_at_end=True,
82
+ metric_for_best_model="cer", # 한국어의 경우 'wer'보다는 'cer'이 더 적합할 것
83
+ greater_is_better=False,
84
+ push_to_hub=True,
85
+ save_total_limit=5, # 최대 저장할 모델 수 지정
86
+ )
87
+
88
+ #########################################################################################################################################
89
+ ################################################### 사용자 설정 변수 #####################################################################
90
+ #########################################################################################################################################
91
+
92
+
93
+ @dataclass
94
+ class DataCollatorSpeechSeq2SeqWithPadding:
95
+ processor: Any
96
+
97
+ def __call__(self, features: List[Dict[str, Union[List[int], torch.Tensor]]]) -> Dict[str, torch.Tensor]:
98
+ # 인풋 데이터와 라벨 데이터의 길이가 다르며, 따라서 서로 다른 패딩 방법이 적용되어야 한다. 그러므로 두 데이터를 분리해야 한다.
99
+ # 먼저 오디오 인풋 데이터를 간단히 토치 텐서로 반환하는 작업을 수행한다.
100
+ input_features = [{"input_features": feature["input_features"]} for feature in features]
101
+ batch = self.processor.feature_extractor.pad(input_features, return_tensors="pt")
102
+
103
+ # Tokenize된 레이블 시퀀스를 가져온다.
104
+ label_features = [{"input_ids": feature["labels"]} for feature in features]
105
+ # 레이블 시퀀스에 대해 최대 길이만큼 패딩 작업을 실시한다.
106
+ labels_batch = self.processor.tokenizer.pad(label_features, return_tensors="pt")
107
+
108
+ # 패딩 토큰을 -100으로 치환하여 loss 계산 과정에서 무시되도록 한다.
109
+ labels = labels_batch["input_ids"].masked_fill(labels_batch.attention_mask.ne(1), -100)
110
+
111
+ # 이전 토크나이즈 과정에서 bos 토큰이 추가되었다면 bos 토큰을 잘라낸다.
112
+ # 해당 토큰은 이후 언제든 추가할 수 있다.
113
+ if (labels[:, 0] == self.processor.tokenizer.bos_token_id).all().cpu().item():
114
+ labels = labels[:, 1:]
115
+
116
+ batch["labels"] = labels
117
+
118
+ return batch
119
+
120
+
121
+ def compute_metrics(pred):
122
+ pred_ids = pred.predictions
123
+ label_ids = pred.label_ids
124
+
125
+ # pad_token을 -100으로 치환
126
+ label_ids[label_ids == -100] = tokenizer.pad_token_id
127
+
128
+ # metrics 계산 시 special token들을 빼고 계산하도록 설정
129
+ pred_str = tokenizer.batch_decode(pred_ids, skip_special_tokens=True)
130
+ label_str = tokenizer.batch_decode(label_ids, skip_special_tokens=True)
131
+
132
+ cer = 100 * metric.compute(predictions=pred_str, references=label_str)
133
+
134
+ return {"cer": cer}
135
+
136
+
137
+ # model_dir, ./repo 초기화
138
+ if os.path.exists(model_dir):
139
+ shutil.rmtree(model_dir)
140
+ os.makedirs(model_dir)
141
+
142
+ if os.path.exists('./repo'):
143
+ shutil.rmtree('./repo')
144
+ os.makedirs('./repo')
145
+
146
+ # 파인튜닝을 진행하고자 하는 모델의 processor, tokenizer, feature extractor, model 로드
147
+ processor = WhisperProcessor.from_pretrained(model_name, language="Korean", task="transcribe")
148
+ tokenizer = WhisperTokenizer.from_pretrained(model_name, language="Korean", task="transcribe")
149
+ feature_extractor = WhisperFeatureExtractor.from_pretrained(model_name)
150
+ model = WhisperForConditionalGeneration.from_pretrained(model_name)
151
+
152
+ data_collator = DataCollatorSpeechSeq2SeqWithPadding(processor=processor)
153
+ metric = evaluate.load('cer')
154
+ model.config.forced_decoder_ids = None
155
+ model.config.suppress_tokens = []
156
+
157
+
158
+ # Hub로부터 "모든 전처리가 완료된" 데이터셋을 로드(이게 진짜 오래걸려요.)
159
+ preprocessed_dataset = load_dataset(dataset_name, cache_dir=CACHE_DIR)
160
+
161
+ # 30%까지의 valid 데이터셋 선택(코드 작동 테스트를 위함)
162
+ if is_test:
163
+ preprocessed_dataset["valid"] = preprocessed_dataset["valid"].select(range(math.ceil(len(preprocessed_dataset) * 0.3)))
164
+
165
+ # training_args 객체를 JSON 형식으로 변환
166
+ training_args_dict = training_args.to_dict()
167
+
168
+ # MLflow UI 관리 폴더 지정
169
+ mlflow.set_tracking_uri("sqlite:////content/drive/MyDrive/STT_test/mlflow.db")
170
+
171
+ # MLflow 실험 이름을 모델 이름으로 설정
172
+ experiment_name = model_name
173
+ existing_experiment = mlflow.get_experiment_by_name(experiment_name)
174
+
175
+ if existing_experiment is not None:
176
+ experiment_id = existing_experiment.experiment_id
177
+ else:
178
+ experiment_id = mlflow.create_experiment(experiment_name)
179
+
180
+
181
+ model_version = 1 # 로깅 하려는 모델 버전(이미 존재하면, 자동 할당)
182
+
183
+ # MLflow 로깅
184
+ with mlflow.start_run(experiment_id=experiment_id, description=model_description):
185
+ # training_args 로깅
186
+ for key, value in training_args_dict.items():
187
+ mlflow.log_param(key, value)
188
+
189
+
190
+ mlflow.set_tag("Dataset", dataset_name) # 데이터셋 로깅
191
+
192
+ trainer = Seq2SeqTrainer(
193
+ args=training_args,
194
+ model=model,
195
+ train_dataset=preprocessed_dataset["train"],
196
+ eval_dataset=preprocessed_dataset["valid"], # or "test"
197
+ data_collator=data_collator,
198
+ compute_metrics=compute_metrics,
199
+ tokenizer=processor.feature_extractor,
200
+ )
201
+
202
+ trainer.train()
203
+ trainer.save_model(model_dir) # 학습 후 모델 저장
204
+
205
+ # Metric 로깅
206
+ metrics = trainer.evaluate()
207
+ for metric_name, metric_value in metrics.items():
208
+ mlflow.log_metric(metric_name, metric_value)
209
+
210
+ # MLflow 모델 레지스터
211
+ model_uri = "runs:/{run_id}/{artifact_path}".format(run_id=mlflow.active_run().info.run_id, artifact_path=model_dir)
212
+
213
+ # 이 값 이용해서 허깅페이스 모델 이름 설정 예정
214
+ model_details = mlflow.register_model(model_uri=model_uri, name=model_name.replace('/', '-')) # 모델 이름에 '/'를 '-'로 대체
215
+
216
+ # 모델 Description
217
+ client = MlflowClient()
218
+ client.update_model_version(name=model_details.name, version=model_details.version, description=model_description)
219
+ model_version = model_details.version # 버전 정보 허깅페이스 업로드 시 사용
220
+
221
+
222
+
223
+ ## 허깅페이스 로그인
224
+ while True:
225
+
226
+ if token =="exit":
227
+ break
228
+
229
+ try:
230
+ result = subprocess.run(["huggingface-cli", "login", "--token", token])
231
+ if result.returncode != 0:
232
+ raise Exception()
233
+ break
234
+ except Exception as e:
235
+ token = input("Please enter your Hugging Face API token: ")
236
+
237
+
238
+ os.environ["HUGGINGFACE_HUB_TOKEN"] = token
239
+
240
+ # 리포지토리 이름 설정
241
+ repo_name = "maxseats/" + model_name.replace('/', '-') + '-' + str(model_version) # 허깅페이스 레포지토리 이름 설정
242
+
243
+ # 리포지토리 생성
244
+ create_repo(repo_name, exist_ok=True, token=token)
245
+
246
+
247
+
248
+ # 리포지토리 클론
249
+ repo = Repository(local_dir='./repo', clone_from=f"{repo_name}", use_auth_token=token)
250
+
251
+
252
+ # model_dir 필요한 파일 복사
253
+ max_depth = 1 # 순회할 최대 깊이
254
+
255
+ for root, dirs, files in os.walk(model_dir):
256
+ depth = root.count(os.sep) - model_dir.count(os.sep)
257
+ if depth < max_depth:
258
+ for file in files:
259
+ # 파일 경로 생성
260
+ source_file = os.path.join(root, file)
261
+ # 대상 폴더에 복사
262
+ shutil.copy(source_file, './repo')
263
+
264
+
265
+ # 토크나이저 다운로드 및 로컬 디렉토리에 저장
266
+ tokenizer.save_pretrained('./repo')
267
+
268
+
269
+ readme = f"""
270
+ ---
271
+ language: ko
272
+ tags:
273
+ - whisper
274
+ - speech-recognition
275
+ datasets:
276
+ - {dataset_name}
277
+ metrics:
278
+ - cer
279
+ ---
280
+ # Model Name : {model_name}
281
+ # Description
282
+ {model_description}
283
+ """
284
+
285
+
286
+ # 모델 카드 및 기타 메타데이터 파일 작성
287
+ with open("./repo/README.md", "w") as f:
288
+ f.write(readme)
289
+
290
+ # 파일 커밋 푸시
291
+ repo.push_to_hub(commit_message="Initial commit")
292
+
293
+ # 폴더와 하위 내용 삭제
294
+ shutil.rmtree(model_dir)
295
+ shutil.rmtree('./repo')
296
+ ```
297
+
298