Update README.md
Browse files
README.md
CHANGED
@@ -11,10 +11,288 @@ metrics:
|
|
11 |
---
|
12 |
# Model Name : maxseats/SungBeom-whisper-small-ko-set0
|
13 |
# Description
|
|
|
14 |
|
15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
# 설명
|
18 |
-
- 주요 영역별 회의 음성
|
19 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
|