A-Funakoshi commited on
Commit
9fa51cd
·
1 Parent(s): e3e555a

Upload finetuning_multilingual_02_adafactor.py

Browse files
finetuning_multilingual_02_adafactor.py ADDED
@@ -0,0 +1,295 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # multilingual-sentimentsデータでfinetuning
2
+
3
+ # %%
4
+ import torch
5
+ # GPUが使用可能か判断
6
+ if torch.cuda.is_available():
7
+ print('gpu is available')
8
+ else:
9
+ raise Exception('gpu is NOT available')
10
+
11
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
12
+ device
13
+
14
+ # %%
15
+ from datasets import load_dataset, DatasetDict
16
+ from transformers import AutoTokenizer
17
+ from transformers import AutoModelForSequenceClassification
18
+ from transformers import TrainingArguments
19
+ from transformers import Trainer
20
+ from sklearn.metrics import accuracy_score, f1_score
21
+ import numpy as np
22
+ import pandas as pd
23
+ import torch
24
+ import random
25
+
26
+ # %%
27
+ from transformers.trainer_utils import set_seed
28
+
29
+ # 乱数シードを42に固定
30
+ set_seed(42)
31
+
32
+ # %% [markdown]
33
+ # ## データ取得
34
+
35
+ # %%
36
+ from pprint import pprint
37
+ from datasets import load_dataset
38
+
39
+ # Hugging Face Hub上のllm-book/wrime-sentimentのリポジトリから
40
+ # データを読み込む
41
+ train_dataset = load_dataset("tyqiangz/multilingual-sentiments", "japanese", split="train")
42
+ valid_dataset = load_dataset("tyqiangz/multilingual-sentiments", "japanese", split="validation")
43
+ # pprintで見やすく表示する
44
+ pprint(train_dataset)
45
+ pprint(valid_dataset)
46
+
47
+ # # 実験のためデータセットを縮小したい場合はコチラを有効化
48
+ # train_dataset = train_dataset.select(
49
+ # random.sample(range(train_dataset.num_rows), k=500))
50
+ # valid_dataset = valid_dataset.select(
51
+ # random.sample(range(valid_dataset.num_rows), k=500))
52
+ # pprint(train_dataset)
53
+ # pprint(valid_dataset)
54
+
55
+ ## データ前処理
56
+
57
+ # %%
58
+ # トークナイザのロード
59
+ model_name = "cl-tohoku/bert-base-japanese-whole-word-masking"
60
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
61
+
62
+ # %%
63
+ # トークナイザ実行用関数
64
+ def preprocess_text(batch):
65
+ encoded_batch = tokenizer(batch['text'], max_length=512)
66
+ encoded_batch['labels'] = batch['label']
67
+ return encoded_batch
68
+
69
+ # %%
70
+ # トークナイズ+エンコード処理実行
71
+ encoded_train_dataset = train_dataset.map(
72
+ preprocess_text,
73
+ remove_columns=train_dataset.column_names,
74
+ )
75
+ encoded_valid_dataset = valid_dataset.map(
76
+ preprocess_text,
77
+ remove_columns=valid_dataset.column_names,
78
+ )
79
+
80
+ # %%
81
+ # ミニバッチ構築
82
+ from transformers import DataCollatorWithPadding
83
+
84
+ data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
85
+
86
+ ## 最適化するハイパーパラメータの設定
87
+
88
+ # %%
89
+ # オプティマイザ
90
+ OPTIMIZER_NAME = "adafactor"
91
+
92
+ # 最適化するハイパーパラメータ
93
+ def optuna_hp_space(trial):
94
+ return {
95
+ "lr_scheduler_type": trial.suggest_categorical("lr_scheduler_type", ["constant", "linear", "cosine"]),
96
+ "learning_rate": trial.suggest_float("learning_rate", 1e-6, 1e-4, log=True),
97
+ # "per_device_train_batch_size": trial.suggest_categorical("per_device_train_batch_size", [16]),
98
+ "gradient_accumulation_steps": trial.suggest_categorical("gradient_accumulation_steps", [1, 2, 4, 8, 16]),
99
+ "weight_decay": trial.suggest_float("weight_decay", 1e-6, 1e-1, log=True),
100
+ }
101
+
102
+ # %%
103
+ # モデルの準備
104
+ from transformers import AutoModelForSequenceClassification
105
+
106
+ def model_init(trial):
107
+ class_label = train_dataset.features["label"]
108
+ label2id = {label: id for id, label in enumerate(class_label.names)}
109
+ id2label = {id: label for id, label in enumerate(class_label.names)}
110
+ model = AutoModelForSequenceClassification.from_pretrained(
111
+ model_name,
112
+ num_labels=class_label.num_classes,
113
+ label2id=label2id, # ラベル名からIDへの対応を指定
114
+ id2label=id2label, # IDからラベル名への対応を指定
115
+ )
116
+ return model
117
+
118
+ # %%
119
+ # 訓練の実行
120
+ from transformers import TrainingArguments
121
+
122
+ training_args = TrainingArguments(
123
+ optim=OPTIMIZER_NAME, # オプティマイザの種類(adamw_torch/adafactor)
124
+ output_dir="output_multilingual", # 結果の保存フォルダ
125
+ per_device_train_batch_size=16, # 訓練時のバッチサイズ
126
+ # gradient_accumulation_steps=1, # 勾配累積
127
+ # per_device_eval_batch_size=32, # 評価時のバッチサイズ
128
+ # learning_rate=2e-5, # 学習率
129
+ # lr_scheduler_type="constant", # 学習率スケジューラの種類
130
+ warmup_ratio=0.1, # 学習率のウォームアップの長さを指定
131
+ num_train_epochs=3, # エポック数
132
+ save_strategy="epoch", # チェックポイントの保存タイミング
133
+ logging_strategy="epoch", # ロギングのタイミング
134
+ evaluation_strategy="epoch", # 検証セットによる評価のタイミング
135
+ load_best_model_at_end=True, # 訓練後に開発セットで最良のモデルをロード
136
+ metric_for_best_model="accuracy", # 最良のモデルを決定する評価指標
137
+ fp16=True, # 自動混合精度演算の有効化
138
+ )
139
+
140
+ # %%
141
+ # メトリクスの定義
142
+ def compute_metrics(pred):
143
+ labels = pred.label_ids
144
+ preds = pred.predictions.argmax(-1)
145
+ f1 = f1_score(labels, preds, average="weighted")
146
+ acc = accuracy_score(labels, preds)
147
+ return {"accuracy": acc, "f1": f1}
148
+
149
+ # %% [markdown]
150
+ # ## ハイパーパラメータ探索
151
+
152
+ # %%
153
+ from transformers import Trainer
154
+
155
+ trainer = Trainer(
156
+ model=None,
157
+ train_dataset=encoded_train_dataset,
158
+ eval_dataset=encoded_valid_dataset,
159
+ data_collator=data_collator,
160
+ args=training_args,
161
+ compute_metrics=compute_metrics,
162
+ model_init=model_init,
163
+ )
164
+
165
+ # %%
166
+ def compute_objective(metrics):
167
+ return metrics["eval_f1"]
168
+
169
+ # %%
170
+ # ハイパーパラメータ探索
171
+ best_trial = trainer.hyperparameter_search(
172
+ direction="maximize",
173
+ backend="optuna",
174
+ hp_space=optuna_hp_space,
175
+ n_trials=50,
176
+ compute_objective=compute_objective,
177
+ )
178
+
179
+ # %%
180
+ # ベスト-ハイパーパラメータ
181
+ print('optimizer:',OPTIMIZER_NAME)
182
+ print('best param:',best_trial)
183
+
184
+ ## 最適化されたハイパーパラメータでFineTuning
185
+
186
+ # %%
187
+ # モデルの準備
188
+ from transformers import AutoModelForSequenceClassification
189
+
190
+ class_label = train_dataset.features["label"]
191
+ label2id = {label: id for id, label in enumerate(class_label.names)}
192
+ id2label = {id: label for id, label in enumerate(class_label.names)}
193
+ model = AutoModelForSequenceClassification.from_pretrained(
194
+ model_name,
195
+ num_labels=class_label.num_classes,
196
+ label2id=label2id, # ラベル名からIDへの対応を指定
197
+ id2label=id2label, # IDからラベル名への対応を指定
198
+ )
199
+ print(type(model).__name__)
200
+
201
+ # %%
202
+ # 訓練用の設定
203
+ from transformers import TrainingArguments
204
+
205
+ # ベストパラメータ
206
+ best_lr_type = best_trial.hyperparameters['lr_scheduler_type']
207
+ best_lr = best_trial.hyperparameters['learning_rate']
208
+ best_grad_acc_steps = best_trial.hyperparameters['gradient_accumulation_steps']
209
+ best_weight_decay = best_trial.hyperparameters['weight_decay']
210
+ # 保存ディレクトリ
211
+ save_dir = f'bert-finetuned-multilingual-sentiments-{OPTIMIZER_NAME}'
212
+
213
+ training_args = TrainingArguments(
214
+ output_dir=save_dir, # 結果の保存フォルダ
215
+ optim=OPTIMIZER_NAME, # オプティマイザの種類
216
+ per_device_train_batch_size=16, # 訓練時のバッチサイズ
217
+ per_device_eval_batch_size=16, # 評価時のバッチサイズ
218
+ gradient_accumulation_steps=best_grad_acc_steps, # 勾配累積
219
+ learning_rate=best_lr, # 学習率
220
+ lr_scheduler_type=best_lr_type, # 学習率スケジューラの種類
221
+ weight_decay=best_weight_decay, # 正則化
222
+ warmup_ratio=0.1, # 学習率のウォームアップの長さを指定
223
+ num_train_epochs=100, # エポック数
224
+ save_strategy="epoch", # チェックポイントの保存タイミング
225
+ logging_strategy="epoch", # ロギングのタイミング
226
+ evaluation_strategy="epoch", # 検証セットによる評価のタイミング
227
+ load_best_model_at_end=True, # 訓練後に開発セットで最良のモデルをロード
228
+ metric_for_best_model="accuracy", # 最良のモデルを決定する評価指標
229
+ fp16=True, # 自動混合精度演算の有効化
230
+ )
231
+
232
+ # %%
233
+ # 訓練の実施
234
+ from transformers import Trainer
235
+ from transformers import EarlyStoppingCallback
236
+
237
+ trainer = Trainer(
238
+ model=model,
239
+ train_dataset=encoded_train_dataset,
240
+ eval_dataset=encoded_valid_dataset,
241
+ data_collator=data_collator,
242
+ args=training_args,
243
+ compute_metrics=compute_metrics,
244
+ callbacks=[EarlyStoppingCallback(early_stopping_patience=3)],
245
+ )
246
+ trainer.train()
247
+
248
+ # %%
249
+ # モデルの保存
250
+ trainer.save_model(save_dir)
251
+ tokenizer.save_pretrained(save_dir)
252
+
253
+ # %%
254
+ # 結果描画用関数
255
+ import matplotlib.pyplot as plt
256
+ from sklearn.linear_model import LinearRegression
257
+
258
+ def show_graph(df, suptitle, output='output.png'):
259
+ suptitle_size = 23
260
+ graph_title_size = 20
261
+ legend_size = 18
262
+ ticks_size = 13
263
+ # 学習曲線
264
+ fig = plt.figure(figsize=(20, 5))
265
+ plt.suptitle(suptitle, fontsize=suptitle_size)
266
+ # Train Loss
267
+ plt.subplot(131)
268
+ plt.title('Train Loss', fontsize=graph_title_size)
269
+ plt.plot(df['loss'].dropna(), label='train')
270
+ plt.legend(fontsize=legend_size)
271
+ plt.yticks(fontsize=ticks_size)
272
+ # Validation Loss
273
+ plt.subplot(132)
274
+ plt.title(f'Val Loss', fontsize=graph_title_size)
275
+ y = df['eval_loss'].dropna().values
276
+ x = np.arange(len(y)).reshape(-1, 1)
277
+ plt.plot(y, color='tab:orange', label='val')
278
+ plt.legend(fontsize=legend_size)
279
+ plt.yticks(fontsize=ticks_size)
280
+ # Accuracy/F1
281
+ plt.subplot(133)
282
+ plt.title('eval Accuracy/F1', fontsize=graph_title_size)
283
+ plt.plot(df['eval_accuracy'].dropna(), label='accuracy')
284
+ plt.plot(df['eval_f1'].dropna(), label='F1')
285
+ plt.legend(fontsize=legend_size)
286
+ plt.yticks(fontsize=ticks_size)
287
+ plt.tight_layout()
288
+ plt.savefig(output)
289
+
290
+ # %%
291
+ history_df = pd.DataFrame(trainer.state.log_history)
292
+ history_df.to_csv(f'{save_dir}/history.csv')
293
+ # 結果を表示
294
+ suptitle = f'batch:16, lr:{best_lr}, gradient_accumulation: {best_grad_acc_steps}, type:{best_lr_type}, weight_decay:{best_weight_decay}'
295
+ show_graph(history_df, suptitle, f'{save_dir}/output.png')