hHoai commited on
Commit
8c59545
·
verified ·
1 Parent(s): bfec79b
model/__pycache__/model.cpython-310.pyc ADDED
Binary file (15.3 kB). View file
 
model/model.py ADDED
@@ -0,0 +1,511 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from bartpho.preprocess import normalize, tokenize
2
+ from bartpho.utils import tag_dict, polarity_dict, polarity_list, tags, eng_tags, eng_polarity, detect_labels, no_polarity, no_tag
3
+ from bartpho.utils import predict, predict_df, predict_detect, predict_df_detect
4
+ from simpletransformers.config.model_args import Seq2SeqArgs
5
+ import random
6
+ import numpy as np
7
+ import torch
8
+ from transformers import (
9
+ AdamW,
10
+ AutoConfig,
11
+ AutoModel,
12
+ AutoTokenizer,
13
+ MBartConfig,
14
+ MBartForConditionalGeneration,
15
+ MBartTokenizer,
16
+ get_linear_schedule_with_warmup,
17
+ )
18
+ from pyvi.ViTokenizer import tokenize as model_tokenize
19
+
20
+ class Seq2SeqModel:
21
+ def __init__(
22
+ self,
23
+ encoder_decoder_type=None,
24
+ encoder_decoder_name=None,
25
+ config=None,
26
+ args=None,
27
+ use_cuda=False,
28
+ cuda_device=0,
29
+ **kwargs,
30
+ ):
31
+
32
+ """
33
+ Initializes a Seq2SeqModel.
34
+
35
+ Args:
36
+ encoder_decoder_type (optional): The type of encoder-decoder model. (E.g. bart)
37
+ encoder_decoder_name (optional): The path to a directory containing the saved encoder and decoder of a Seq2SeqModel. (E.g. "outputs/") OR a valid BART or MarianMT model.
38
+ config (optional): A configuration file to build an EncoderDecoderModel.
39
+ args (optional): Default args will be used if this parameter is not provided. If provided, it should be a dict containing the args that should be changed in the default args.
40
+ use_cuda (optional): Use GPU if available. Setting to False will force model to use CPU only.
41
+ cuda_device (optional): Specific GPU that should be used. Will use the first available GPU by default.
42
+ **kwargs (optional): For providing proxies, force_download, resume_download, cache_dir and other options specific to the 'from_pretrained' implementation where this will be supplied.
43
+ """ # noqa: ignore flake8"
44
+
45
+ if not config:
46
+ # if not ((encoder_name and decoder_name) or encoder_decoder_name) and not encoder_type:
47
+ if not encoder_decoder_name:
48
+ raise ValueError(
49
+ "You must specify a Seq2Seq config \t OR \t"
50
+ "encoder_decoder_name"
51
+ )
52
+ elif not encoder_decoder_type:
53
+ raise ValueError(
54
+ "You must specify a Seq2Seq config \t OR \t"
55
+ "encoder_decoder_name"
56
+ )
57
+
58
+ self.args = self._load_model_args(encoder_decoder_name)
59
+ print(args)
60
+ if args:
61
+ self.args.update_from_dict(args)
62
+ print(args)
63
+
64
+ if self.args.manual_seed:
65
+ random.seed(self.args.manual_seed)
66
+ np.random.seed(self.args.manual_seed)
67
+ torch.manual_seed(self.args.manual_seed)
68
+ if self.args.n_gpu > 0:
69
+ torch.cuda.manual_seed_all(self.args.manual_seed)
70
+
71
+ if use_cuda:
72
+ if torch.cuda.is_available():
73
+ self.device = torch.device("cuda")
74
+ else:
75
+ raise ValueError(
76
+ "'use_cuda' set to True when cuda is unavailable."
77
+ "Make sure CUDA is available or set `use_cuda=False`."
78
+ )
79
+ else:
80
+ self.device = "cpu"
81
+
82
+ self.results = {}
83
+
84
+ if not use_cuda:
85
+ self.args.fp16 = False
86
+
87
+ # config = EncoderDecoderConfig.from_encoder_decoder_configs(config, config)
88
+ #if encoder_decoder_type:
89
+ config_class, model_class, tokenizer_class = MODEL_CLASSES[encoder_decoder_type]
90
+
91
+ self.model = model_class.from_pretrained(encoder_decoder_name)
92
+ self.encoder_tokenizer = tokenizer_class.from_pretrained(encoder_decoder_name)
93
+ self.decoder_tokenizer = self.encoder_tokenizer
94
+ self.config = self.model.config
95
+
96
+ if self.args.wandb_project and not wandb_available:
97
+ warnings.warn("wandb_project specified but wandb is not available. Wandb disabled.")
98
+ self.args.wandb_project = None
99
+
100
+ self.args.model_name = encoder_decoder_name
101
+ self.args.model_type = encoder_decoder_type
102
+
103
+ def train_model(
104
+ self,
105
+ train_data,
106
+ best_accuracy,
107
+ output_dir=None,
108
+ show_running_loss=True,
109
+ args=None,
110
+ eval_data=None,
111
+ test_data=None,
112
+ verbose=True,
113
+ **kwargs,
114
+ ):
115
+ if args:
116
+ self.args.update_from_dict(args)
117
+ #self.args = args
118
+ if self.args.silent:
119
+ show_running_loss = False
120
+
121
+
122
+ if not output_dir:
123
+ output_dir = self.args.output_dir
124
+ self._move_model_to_device()
125
+
126
+ train_dataset = self.load_and_cache_examples(train_data, verbose=verbose)
127
+
128
+ os.makedirs(output_dir, exist_ok=True)
129
+
130
+ global_step, tr_loss, best_accuracy = self.train(
131
+ train_dataset,
132
+ output_dir,
133
+ best_accuracy,
134
+ show_running_loss=show_running_loss,
135
+ eval_data=eval_data,
136
+ test_data=test_data,
137
+ verbose=verbose,
138
+ **kwargs,
139
+ )
140
+
141
+ final_dir = self.args.output_dir + "/final"
142
+ self._save_model(final_dir, model=self.model)
143
+
144
+ if verbose:
145
+ logger.info(" Training of {} model complete. Saved best to {}.".format(self.args.model_name, final_dir))
146
+
147
+ return best_accuracy
148
+
149
+ def train(
150
+ self,
151
+ train_dataset,
152
+ output_dir,
153
+ best_accuracy,
154
+ show_running_loss=True,
155
+ eval_data=None,
156
+ test_data=None,
157
+ verbose=True,
158
+ **kwargs,
159
+ ):
160
+ """
161
+ Trains the model on train_dataset.
162
+
163
+ Utility function to be used by the train_model() method. Not intended to be used directly.
164
+ """
165
+
166
+ #epoch_lst = []
167
+ #acc_detects, pre_detects, rec_detects, f1_detects, accs, pre_absas, rec_absas, f1_absas = [], [], [], [], [], [], [], []
168
+ #tacc_detects, tpre_detects, trec_detects, tf1_detects, taccs, tpre_absas, trec_absas, tf1_absas = [], [], [], [], [], [], [], []
169
+
170
+ model = self.model
171
+ args = self.args
172
+
173
+ tb_writer = SummaryWriter(logdir=args.tensorboard_dir)
174
+ train_sampler = RandomSampler(train_dataset)
175
+ train_dataloader = DataLoader(
176
+ train_dataset,
177
+ sampler=train_sampler,
178
+ batch_size=args.train_batch_size,
179
+ num_workers=self.args.dataloader_num_workers,
180
+ )
181
+
182
+ if args.max_steps > 0:
183
+ t_total = args.max_steps
184
+ args.num_train_epochs = args.max_steps // (len(train_dataloader) // args.gradient_accumulation_steps) + 1
185
+ else:
186
+ t_total = len(train_dataloader) // args.gradient_accumulation_steps * args.num_train_epochs
187
+
188
+ no_decay = ["bias", "LayerNorm.weight"]
189
+
190
+ optimizer_grouped_parameters = []
191
+ custom_parameter_names = set()
192
+ for group in self.args.custom_parameter_groups:
193
+ params = group.pop("params")
194
+ custom_parameter_names.update(params)
195
+ param_group = {**group}
196
+ param_group["params"] = [p for n, p in model.named_parameters() if n in params]
197
+ optimizer_grouped_parameters.append(param_group)
198
+
199
+ for group in self.args.custom_layer_parameters:
200
+ layer_number = group.pop("layer")
201
+ layer = f"layer.{layer_number}."
202
+ group_d = {**group}
203
+ group_nd = {**group}
204
+ group_nd["weight_decay"] = 0.0
205
+ params_d = []
206
+ params_nd = []
207
+ for n, p in model.named_parameters():
208
+ if n not in custom_parameter_names and layer in n:
209
+ if any(nd in n for nd in no_decay):
210
+ params_nd.append(p)
211
+ else:
212
+ params_d.append(p)
213
+ custom_parameter_names.add(n)
214
+ group_d["params"] = params_d
215
+ group_nd["params"] = params_nd
216
+
217
+ optimizer_grouped_parameters.append(group_d)
218
+ optimizer_grouped_parameters.append(group_nd)
219
+
220
+ if not self.args.train_custom_parameters_only:
221
+ optimizer_grouped_parameters.extend(
222
+ [
223
+ {
224
+ "params": [
225
+ p
226
+ for n, p in model.named_parameters()
227
+ if n not in custom_parameter_names and not any(nd in n for nd in no_decay)
228
+ ],
229
+ "weight_decay": args.weight_decay,
230
+ },
231
+ {
232
+ "params": [
233
+ p
234
+ for n, p in model.named_parameters()
235
+ if n not in custom_parameter_names and any(nd in n for nd in no_decay)
236
+ ],
237
+ "weight_decay": 0.0,
238
+ },
239
+ ]
240
+ )
241
+
242
+ warmup_steps = math.ceil(t_total * args.warmup_ratio)
243
+ args.warmup_steps = warmup_steps if args.warmup_steps == 0 else args.warmup_steps
244
+
245
+ # TODO: Use custom optimizer like with BertSum?
246
+ optimizer = AdamW(optimizer_grouped_parameters, lr=args.learning_rate, eps=args.adam_epsilon)
247
+ scheduler = get_linear_schedule_with_warmup(
248
+ optimizer, num_warmup_steps=args.warmup_steps, num_training_steps=t_total
249
+ )
250
+
251
+ if (args.model_name and os.path.isfile(os.path.join(args.model_name, "optimizer.pt")) and os.path.isfile(os.path.join(args.model_name, "scheduler.pt"))):
252
+ # Load in optimizer and scheduler states
253
+ optimizer.load_state_dict(torch.load(os.path.join(args.model_name, "optimizer.pt")))
254
+ scheduler.load_state_dict(torch.load(os.path.join(args.model_name, "scheduler.pt")))
255
+
256
+ if args.n_gpu > 1:
257
+ model = torch.nn.DataParallel(model)
258
+
259
+ logger.info(" Training started")
260
+
261
+ global_step = 0
262
+ tr_loss, logging_loss = 0.0, 0.0
263
+ model.zero_grad()
264
+ train_iterator = trange(int(args.num_train_epochs), desc="Epoch", disable=args.silent, mininterval=0)
265
+ epoch_number = 0
266
+ best_eval_metric = None
267
+ early_stopping_counter = 0
268
+ steps_trained_in_current_epoch = 0
269
+ epochs_trained = 0
270
+
271
+ if args.model_name and os.path.exists(args.model_name):
272
+ try:
273
+ # set global_step to gobal_step of last saved checkpoint from model path
274
+ checkpoint_suffix = args.model_name.split("/")[-1].split("-")
275
+ if len(checkpoint_suffix) > 2:
276
+ checkpoint_suffix = checkpoint_suffix[1]
277
+ else:
278
+ checkpoint_suffix = checkpoint_suffix[-1]
279
+ global_step = int(checkpoint_suffix)
280
+ epochs_trained = global_step // (len(train_dataloader) // args.gradient_accumulation_steps)
281
+ steps_trained_in_current_epoch = global_step % (
282
+ len(train_dataloader) // args.gradient_accumulation_steps
283
+ )
284
+
285
+ logger.info(" Continuing training from checkpoint, will skip to saved global_step")
286
+ logger.info(" Continuing training from epoch %d", epochs_trained)
287
+ logger.info(" Continuing training from global step %d", global_step)
288
+ logger.info(" Will skip the first %d steps in the current epoch", steps_trained_in_current_epoch)
289
+ except ValueError:
290
+ logger.info(" Starting fine-tuning.")
291
+
292
+ if args.wandb_project:
293
+ wandb.init(project=args.wandb_project, config={**asdict(args)}, **args.wandb_kwargs)
294
+ wandb.watch(self.model)
295
+
296
+ if args.fp16:
297
+ from torch.cuda import amp
298
+
299
+ scaler = amp.GradScaler()
300
+
301
+ model.train()
302
+ for current_epoch in train_iterator:
303
+ if epochs_trained > 0:
304
+ epochs_trained -= 1
305
+ continue
306
+ train_iterator.set_description(f"Epoch {epoch_number + 1} of {args.num_train_epochs}")
307
+ batch_iterator = tqdm(
308
+ train_dataloader,
309
+ desc=f"Running Epoch {epoch_number} of {args.num_train_epochs}",
310
+ disable=args.silent,
311
+ mininterval=0,
312
+ )
313
+ for step, batch in enumerate(batch_iterator):
314
+ if steps_trained_in_current_epoch > 0:
315
+ steps_trained_in_current_epoch -= 1
316
+ continue
317
+ # batch = tuple(t.to(device) for t in batch)
318
+
319
+ inputs = self._get_inputs_dict(batch)
320
+ if args.fp16:
321
+ with amp.autocast():
322
+ outputs = model(**inputs)
323
+ # model outputs are always tuple in pytorch-transformers (see doc)
324
+ loss = outputs[0]
325
+ else:
326
+ outputs = model(**inputs)
327
+ # model outputs are always tuple in pytorch-transformers (see doc)
328
+ loss = outputs[0]
329
+
330
+ if args.n_gpu > 1:
331
+ loss = loss.mean() # mean() to average on multi-gpu parallel training
332
+
333
+ current_loss = loss.item()
334
+
335
+ if show_running_loss:
336
+ batch_iterator.set_description(
337
+ f"Epochs {epoch_number}/{args.num_train_epochs}. Running Loss: {current_loss:9.4f}"
338
+ )
339
+
340
+ if args.gradient_accumulation_steps > 1:
341
+ loss = loss / args.gradient_accumulation_steps
342
+
343
+ if args.fp16:
344
+ scaler.scale(loss).backward()
345
+ else:
346
+ loss.backward()
347
+
348
+ tr_loss += loss.item()
349
+ if (step + 1) % args.gradient_accumulation_steps == 0:
350
+ if args.fp16:
351
+ scaler.unscale_(optimizer)
352
+ torch.nn.utils.clip_grad_norm_(model.parameters(), args.max_grad_norm)
353
+
354
+ if args.fp16:
355
+ scaler.step(optimizer)
356
+ scaler.update()
357
+ else:
358
+ optimizer.step()
359
+ scheduler.step() # Update learning rate schedule
360
+ model.zero_grad()
361
+ global_step += 1
362
+
363
+ if args.logging_steps > 0 and global_step % args.logging_steps == 0:
364
+ # Log metrics
365
+ tb_writer.add_scalar("lr", scheduler.get_lr()[0], global_step)
366
+ tb_writer.add_scalar("loss", (tr_loss - logging_loss) / args.logging_steps, global_step)
367
+ logging_loss = tr_loss
368
+ if args.wandb_project:
369
+ wandb.log(
370
+ {
371
+ "Training loss": current_loss,
372
+ "lr": scheduler.get_lr()[0],
373
+ "global_step": global_step,
374
+ }
375
+ )
376
+
377
+ # if args.save_steps > 0 and global_step % args.save_steps == 0:
378
+ # # Save model checkpoint
379
+ # output_dir_current = os.path.join(output_dir, "checkpoint-{}".format(global_step))
380
+
381
+ # self._save_model(output_dir_current, optimizer, scheduler, model=model)
382
+
383
+ epoch_number += 1
384
+ output_dir_current = os.path.join(output_dir, "checkpoint-{}-epoch-{}".format(global_step, epoch_number))
385
+
386
+
387
+ print('batch: '+str(args.train_batch_size)+' accumulation_steps: '+str(args.gradient_accumulation_steps)+\
388
+ ' lr: '+str(args.learning_rate)+' epochs: '+str(args.num_train_epochs)+' epoch: '+str(epoch_number))
389
+ print('---dev dataset----')
390
+ acc_detect, pre_detect, rec_detect, f1_detect, acc, pre_absa, rec_absa, f1_absa = predict_df(model, eval_data, tokenizer=self.encoder_tokenizer, device=self.device)
391
+ print('---test dataset----')
392
+ tacc_detect, tpre_detect, trec_detect, tf1_detect, tacc, tpre_absa, trec_absa, tf1_absa = predict_df(model, test_data, tokenizer=self.encoder_tokenizer, device=self.device)
393
+ # if acc > best_accuracy:
394
+ # best_accuracy = acc
395
+ # if not args.save_model_every_epoch:
396
+ # self._save_model(output_dir_current, optimizer, scheduler, model=model)
397
+ # with open('./MAMS_best_accuracy.txt', 'a') as f0:
398
+ # f0.writelines('batch: '+str(args.train_batch_size)+' accumulation_steps: '+str(args.gradient_accumulation_steps)+\
399
+ # ' lr: '+str(args.learning_rate)+' epochs: '+str(args.num_train_epochs)+' epoch: '+str(epoch_number)+' val_accuracy: '+str(best_accuracy)+\
400
+ # ' test_accuracy: '+str(tacc)+'\n')
401
+
402
+ # if args.save_model_every_epoch:
403
+ # os.makedirs(output_dir_current, exist_ok=True)
404
+ # self._save_model(output_dir_current, optimizer, scheduler, model=model)
405
+
406
+ if acc > best_accuracy:
407
+ # Cập nhật best_accuracy nếu tìm thấy mô hình tốt hơn
408
+ best_accuracy = acc
409
+
410
+ # Lưu mô hình tốt nhất vào output_dir_current
411
+ self._save_model(output_dir_current, optimizer, scheduler, model=model)
412
+
413
+ # Ghi lại thông tin về best_accuracy vào file log
414
+ with open('./MAMS_best_accuracy.txt', 'a') as f0:
415
+ f0.writelines(
416
+ 'batch: ' + str(args.train_batch_size) +
417
+ ' accumulation_steps: ' + str(args.gradient_accumulation_steps) +
418
+ ' lr: ' + str(args.learning_rate) +
419
+ ' epochs: ' + str(args.num_train_epochs) +
420
+ ' epoch: ' + str(epoch_number) +
421
+ ' val_accuracy: ' + str(best_accuracy) +
422
+ ' test_accuracy: ' + str(tacc) + '\n'
423
+ )
424
+
425
+
426
+
427
+ return global_step, tr_loss / global_step, best_accuracy
428
+
429
+ def load_and_cache_examples(self, data, evaluate=False, no_cache=False, verbose=True, silent=False):
430
+ """
431
+ Creates a T5Dataset from data.
432
+
433
+ Utility function for train() and eval() methods. Not intended to be used directly.
434
+ """
435
+
436
+ encoder_tokenizer = self.encoder_tokenizer
437
+ decoder_tokenizer = self.decoder_tokenizer
438
+ args = self.args
439
+
440
+ if not no_cache:
441
+ no_cache = args.no_cache
442
+
443
+ if not no_cache:
444
+ os.makedirs(self.args.cache_dir, exist_ok=True)
445
+
446
+ mode = "dev" if evaluate else "train"
447
+
448
+ if args.dataset_class:
449
+ CustomDataset = args.dataset_class
450
+ return CustomDataset(encoder_tokenizer, decoder_tokenizer, args, data, mode)
451
+ else:
452
+ return SimpleSummarizationDataset(encoder_tokenizer, self.args, data, mode)
453
+
454
+ def _save_model(self, output_dir=None, optimizer=None, scheduler=None, model=None, results=None):
455
+ if not output_dir:
456
+ output_dir = self.args.output_dir
457
+ os.makedirs(output_dir, exist_ok=True)
458
+
459
+ logger.info(f"Saving model into {output_dir}")
460
+
461
+ if model and not self.args.no_save:
462
+ # Take care of distributed/parallel training
463
+ model_to_save = model.module if hasattr(model, "module") else model
464
+ self._save_model_args(output_dir)
465
+
466
+ os.makedirs(os.path.join(output_dir), exist_ok=True)
467
+ model_to_save.save_pretrained(output_dir)
468
+ self.config.save_pretrained(output_dir)
469
+ self.encoder_tokenizer.save_pretrained(output_dir)
470
+
471
+ torch.save(self.args, os.path.join(output_dir, "training_args.bin"))
472
+ if optimizer and scheduler and self.args.save_optimizer_and_scheduler:
473
+ torch.save(optimizer.state_dict(), os.path.join(output_dir, "optimizer.pt"))
474
+ torch.save(scheduler.state_dict(), os.path.join(output_dir, "scheduler.pt"))
475
+
476
+ if results:
477
+ output_eval_file = os.path.join(output_dir, "eval_results.txt")
478
+ with open(output_eval_file, "w") as writer:
479
+ for key in sorted(results.keys()):
480
+ writer.write("{} = {}\n".format(key, str(results[key])))
481
+
482
+ def _move_model_to_device(self):
483
+ self.model.to(self.device)
484
+
485
+ def _get_inputs_dict(self, batch):
486
+ device = self.device
487
+ pad_token_id = self.encoder_tokenizer.pad_token_id
488
+ source_ids, source_mask, y = batch["source_ids"], batch["source_mask"], batch["target_ids"]
489
+ y_ids = y[:, :-1].contiguous()
490
+ lm_labels = y[:, 1:].clone()
491
+ lm_labels[y[:, 1:] == pad_token_id] = -100
492
+
493
+ inputs = {
494
+ "input_ids": source_ids.to(device),
495
+ "attention_mask": source_mask.to(device),
496
+ "decoder_input_ids": y_ids.to(device),
497
+ "labels": lm_labels.to(device),
498
+ }
499
+ return inputs
500
+
501
+ def _save_model_args(self, output_dir):
502
+ os.makedirs(output_dir, exist_ok=True)
503
+ self.args.save(output_dir)
504
+
505
+ def _load_model_args(self, input_dir):
506
+ args = Seq2SeqArgs()
507
+ args.load(input_dir)
508
+ return args
509
+
510
+ def get_named_parameters(self):
511
+ return [n for n, p in self.model.named_parameters()]