A-Funakoshi commited on
Commit
4e104fc
·
1 Parent(s): 4b2a7cc

Upload finetuning_multilingual_02_adamw.py

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