modelcard.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. # Copyright 2018 The HuggingFace Inc. team.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """Configuration base class and utilities."""
  15. import os
  16. from dataclasses import dataclass
  17. from pathlib import Path
  18. from typing import Any
  19. import httpx
  20. import yaml
  21. from huggingface_hub import is_offline_mode, model_info
  22. from huggingface_hub.errors import OfflineModeIsEnabled
  23. from huggingface_hub.utils import HFValidationError
  24. from . import __version__
  25. from .models.auto.modeling_auto import (
  26. MODEL_FOR_AUDIO_CLASSIFICATION_MAPPING_NAMES,
  27. MODEL_FOR_CAUSAL_LM_MAPPING_NAMES,
  28. MODEL_FOR_CTC_MAPPING_NAMES,
  29. MODEL_FOR_IMAGE_CLASSIFICATION_MAPPING_NAMES,
  30. MODEL_FOR_IMAGE_SEGMENTATION_MAPPING_NAMES,
  31. MODEL_FOR_IMAGE_TEXT_TO_TEXT_MAPPING_NAMES,
  32. MODEL_FOR_MASKED_LM_MAPPING_NAMES,
  33. MODEL_FOR_OBJECT_DETECTION_MAPPING_NAMES,
  34. MODEL_FOR_QUESTION_ANSWERING_MAPPING_NAMES,
  35. MODEL_FOR_SEQ_TO_SEQ_CAUSAL_LM_MAPPING_NAMES,
  36. MODEL_FOR_SEQUENCE_CLASSIFICATION_MAPPING_NAMES,
  37. MODEL_FOR_SPEECH_SEQ_2_SEQ_MAPPING_NAMES,
  38. MODEL_FOR_TABLE_QUESTION_ANSWERING_MAPPING_NAMES,
  39. MODEL_FOR_TOKEN_CLASSIFICATION_MAPPING_NAMES,
  40. MODEL_FOR_ZERO_SHOT_IMAGE_CLASSIFICATION_MAPPING_NAMES,
  41. )
  42. from .training_args import ParallelMode
  43. from .utils import (
  44. is_datasets_available,
  45. is_tokenizers_available,
  46. is_torch_available,
  47. logging,
  48. )
  49. TASK_MAPPING = {
  50. "text-generation": MODEL_FOR_CAUSAL_LM_MAPPING_NAMES,
  51. "image-classification": MODEL_FOR_IMAGE_CLASSIFICATION_MAPPING_NAMES,
  52. "image-segmentation": MODEL_FOR_IMAGE_SEGMENTATION_MAPPING_NAMES,
  53. "fill-mask": MODEL_FOR_MASKED_LM_MAPPING_NAMES,
  54. "object-detection": MODEL_FOR_OBJECT_DETECTION_MAPPING_NAMES,
  55. "question-answering": MODEL_FOR_QUESTION_ANSWERING_MAPPING_NAMES,
  56. "text2text-generation": MODEL_FOR_SEQ_TO_SEQ_CAUSAL_LM_MAPPING_NAMES,
  57. "text-classification": MODEL_FOR_SEQUENCE_CLASSIFICATION_MAPPING_NAMES,
  58. "table-question-answering": MODEL_FOR_TABLE_QUESTION_ANSWERING_MAPPING_NAMES,
  59. "token-classification": MODEL_FOR_TOKEN_CLASSIFICATION_MAPPING_NAMES,
  60. "audio-classification": MODEL_FOR_AUDIO_CLASSIFICATION_MAPPING_NAMES,
  61. "automatic-speech-recognition": {**MODEL_FOR_CTC_MAPPING_NAMES, **MODEL_FOR_SPEECH_SEQ_2_SEQ_MAPPING_NAMES},
  62. "zero-shot-image-classification": MODEL_FOR_ZERO_SHOT_IMAGE_CLASSIFICATION_MAPPING_NAMES,
  63. "image-text-to-text": MODEL_FOR_IMAGE_TEXT_TO_TEXT_MAPPING_NAMES,
  64. }
  65. logger = logging.get_logger(__name__)
  66. AUTOGENERATED_TRAINER_COMMENT = """
  67. <!-- This model card has been generated automatically according to the information the Trainer had access to. You
  68. should probably proofread and complete it, then remove this comment. -->
  69. """
  70. TASK_TAG_TO_NAME_MAPPING = {
  71. "fill-mask": "Masked Language Modeling",
  72. "image-classification": "Image Classification",
  73. "image-segmentation": "Image Segmentation",
  74. "multiple-choice": "Multiple Choice",
  75. "object-detection": "Object Detection",
  76. "question-answering": "Question Answering",
  77. "table-question-answering": "Table Question Answering",
  78. "text-classification": "Text Classification",
  79. "text-generation": "Causal Language Modeling",
  80. "token-classification": "Token Classification",
  81. "zero-shot-classification": "Zero Shot Classification",
  82. "automatic-speech-recognition": "Automatic Speech Recognition",
  83. "audio-classification": "Audio Classification",
  84. }
  85. METRIC_TAGS = [
  86. "accuracy",
  87. "bleu",
  88. "f1",
  89. "matthews_correlation",
  90. "pearsonr",
  91. "precision",
  92. "recall",
  93. "rouge",
  94. "sacrebleu",
  95. "spearmanr",
  96. "wer",
  97. ]
  98. def _listify(obj):
  99. if obj is None:
  100. return []
  101. elif isinstance(obj, str):
  102. return [obj]
  103. else:
  104. return obj
  105. def _insert_values_as_list(metadata, name, values):
  106. if values is None:
  107. return metadata
  108. if isinstance(values, str):
  109. values = [values]
  110. values = [v for v in values if v is not None]
  111. if len(values) == 0:
  112. return metadata
  113. metadata[name] = values
  114. return metadata
  115. def infer_metric_tags_from_eval_results(eval_results):
  116. if eval_results is None:
  117. return {}
  118. result = {}
  119. for key in eval_results:
  120. if key.lower().replace(" ", "_") in METRIC_TAGS:
  121. result[key.lower().replace(" ", "_")] = key
  122. elif key.lower() == "rouge1":
  123. result["rouge"] = key
  124. return result
  125. def _insert_value(metadata, name, value):
  126. if value is None:
  127. return metadata
  128. metadata[name] = value
  129. return metadata
  130. def is_hf_dataset(dataset):
  131. if not is_datasets_available():
  132. return False
  133. from datasets import Dataset, IterableDataset
  134. return isinstance(dataset, (Dataset, IterableDataset))
  135. def _get_mapping_values(mapping):
  136. result = []
  137. for v in mapping.values():
  138. if isinstance(v, (tuple, list)):
  139. result += list(v)
  140. else:
  141. result.append(v)
  142. return result
  143. @dataclass
  144. class TrainingSummary:
  145. model_name: str
  146. language: str | list[str] | None = None
  147. license: str | None = None
  148. tags: str | list[str] | None = None
  149. finetuned_from: str | None = None
  150. tasks: str | list[str] | None = None
  151. dataset: str | list[str] | None = None
  152. dataset_tags: str | list[str] | None = None
  153. dataset_args: str | list[str] | None = None
  154. dataset_metadata: dict[str, Any] | None = None
  155. eval_results: dict[str, float] | None = None
  156. eval_lines: list[str] | None = None
  157. hyperparameters: dict[str, Any] | None = None
  158. source: str | None = "trainer"
  159. def __post_init__(self):
  160. # Infer default license from the checkpoint used, if possible.
  161. if (
  162. self.license is None
  163. and not is_offline_mode()
  164. and self.finetuned_from is not None
  165. and len(self.finetuned_from) > 0
  166. ):
  167. try:
  168. info = model_info(self.finetuned_from)
  169. for tag in info.tags:
  170. if tag.startswith("license:"):
  171. self.license = tag[8:]
  172. except (httpx.HTTPError, HFValidationError, OfflineModeIsEnabled):
  173. pass
  174. def create_model_index(self, metric_mapping):
  175. model_index = {"name": self.model_name}
  176. # Dataset mapping tag -> name
  177. dataset_names = _listify(self.dataset)
  178. dataset_tags = _listify(self.dataset_tags)
  179. dataset_args = _listify(self.dataset_args)
  180. dataset_metadata = _listify(self.dataset_metadata)
  181. if len(dataset_args) < len(dataset_tags):
  182. dataset_args = dataset_args + [None] * (len(dataset_tags) - len(dataset_args))
  183. dataset_mapping = dict(zip(dataset_tags, dataset_names))
  184. dataset_arg_mapping = dict(zip(dataset_tags, dataset_args))
  185. dataset_metadata_mapping = dict(zip(dataset_tags, dataset_metadata))
  186. task_mapping = {
  187. task: TASK_TAG_TO_NAME_MAPPING[task] for task in _listify(self.tasks) if task in TASK_TAG_TO_NAME_MAPPING
  188. }
  189. model_index["results"] = []
  190. if len(task_mapping) == 0 and len(dataset_mapping) == 0:
  191. return [model_index]
  192. if len(task_mapping) == 0:
  193. task_mapping = {None: None}
  194. if len(dataset_mapping) == 0:
  195. dataset_mapping = {None: None}
  196. # One entry per dataset and per task
  197. all_possibilities = [(task_tag, ds_tag) for task_tag in task_mapping for ds_tag in dataset_mapping]
  198. for task_tag, ds_tag in all_possibilities:
  199. result = {}
  200. if task_tag is not None:
  201. result["task"] = {"name": task_mapping[task_tag], "type": task_tag}
  202. if ds_tag is not None:
  203. metadata = dataset_metadata_mapping.get(ds_tag, {})
  204. result["dataset"] = {
  205. "name": dataset_mapping[ds_tag],
  206. "type": ds_tag,
  207. **metadata,
  208. }
  209. if dataset_arg_mapping[ds_tag] is not None:
  210. result["dataset"]["args"] = dataset_arg_mapping[ds_tag]
  211. if len(metric_mapping) > 0:
  212. result["metrics"] = []
  213. for metric_tag, metric_name in metric_mapping.items():
  214. result["metrics"].append(
  215. {
  216. "name": metric_name,
  217. "type": metric_tag,
  218. "value": self.eval_results[metric_name],
  219. }
  220. )
  221. # Remove partial results to avoid the model card being rejected.
  222. if "task" in result and "dataset" in result and "metrics" in result:
  223. model_index["results"].append(result)
  224. else:
  225. logger.info(f"Dropping the following result as it does not have all the necessary fields:\n{result}")
  226. return [model_index]
  227. def create_metadata(self):
  228. metric_mapping = infer_metric_tags_from_eval_results(self.eval_results)
  229. metadata = {}
  230. metadata = _insert_value(metadata, "library_name", "transformers")
  231. metadata = _insert_values_as_list(metadata, "language", self.language)
  232. metadata = _insert_value(metadata, "license", self.license)
  233. if self.finetuned_from is not None and isinstance(self.finetuned_from, str) and len(self.finetuned_from) > 0:
  234. metadata = _insert_value(metadata, "base_model", self.finetuned_from)
  235. metadata = _insert_values_as_list(metadata, "tags", self.tags)
  236. metadata = _insert_values_as_list(metadata, "datasets", self.dataset_tags)
  237. metadata = _insert_values_as_list(metadata, "metrics", list(metric_mapping.keys()))
  238. metadata["model-index"] = self.create_model_index(metric_mapping)
  239. return metadata
  240. def to_model_card(self):
  241. model_card = ""
  242. metadata = yaml.dump(self.create_metadata(), sort_keys=False)
  243. if len(metadata) > 0:
  244. model_card = f"---\n{metadata}---\n"
  245. # Now the model card for realsies.
  246. if self.source == "trainer":
  247. model_card += AUTOGENERATED_TRAINER_COMMENT
  248. model_card += f"\n# {self.model_name}\n\n"
  249. if self.finetuned_from is None:
  250. model_card += "This model was trained from scratch on "
  251. else:
  252. model_card += (
  253. "This model is a fine-tuned version of"
  254. f" [{self.finetuned_from}](https://huggingface.co/{self.finetuned_from}) on "
  255. )
  256. if self.dataset is None or (isinstance(self.dataset, list) and len(self.dataset) == 0):
  257. model_card += "an unknown dataset."
  258. else:
  259. if isinstance(self.dataset, str):
  260. model_card += f"the {self.dataset} dataset."
  261. elif isinstance(self.dataset, (tuple, list)) and len(self.dataset) == 1:
  262. model_card += f"the {self.dataset[0]} dataset."
  263. else:
  264. model_card += (
  265. ", ".join([f"the {ds}" for ds in self.dataset[:-1]]) + f" and the {self.dataset[-1]} datasets."
  266. )
  267. if self.eval_results is not None:
  268. model_card += "\nIt achieves the following results on the evaluation set:\n"
  269. model_card += "\n".join([f"- {name}: {_maybe_round(value)}" for name, value in self.eval_results.items()])
  270. model_card += "\n"
  271. model_card += "\n## Model description\n\nMore information needed\n"
  272. model_card += "\n## Intended uses & limitations\n\nMore information needed\n"
  273. model_card += "\n## Training and evaluation data\n\nMore information needed\n"
  274. model_card += "\n## Training procedure\n"
  275. model_card += "\n### Training hyperparameters\n"
  276. if self.hyperparameters is not None:
  277. model_card += "\nThe following hyperparameters were used during training:\n"
  278. model_card += "\n".join([f"- {name}: {value}" for name, value in self.hyperparameters.items()])
  279. model_card += "\n"
  280. else:
  281. model_card += "\nMore information needed\n"
  282. if self.eval_lines is not None:
  283. model_card += "\n### Training results\n\n"
  284. model_card += make_markdown_table(self.eval_lines)
  285. model_card += "\n"
  286. model_card += "\n### Framework versions\n\n"
  287. model_card += f"- Transformers {__version__}\n"
  288. if self.source == "trainer" and is_torch_available():
  289. import torch
  290. model_card += f"- Pytorch {torch.__version__}\n"
  291. if is_datasets_available():
  292. import datasets
  293. model_card += f"- Datasets {datasets.__version__}\n"
  294. if is_tokenizers_available():
  295. import tokenizers
  296. model_card += f"- Tokenizers {tokenizers.__version__}\n"
  297. return model_card
  298. @classmethod
  299. def from_trainer(
  300. cls,
  301. trainer,
  302. language=None,
  303. license=None,
  304. tags=None,
  305. model_name=None,
  306. finetuned_from=None,
  307. tasks=None,
  308. dataset_tags=None,
  309. dataset_metadata=None,
  310. dataset=None,
  311. dataset_args=None,
  312. ):
  313. # Infer default from dataset
  314. one_dataset = trainer.eval_dataset if trainer.eval_dataset is not None else trainer.train_dataset
  315. if is_hf_dataset(one_dataset) and (dataset_tags is None or dataset_args is None or dataset_metadata is None):
  316. default_tag = one_dataset.builder_name
  317. # Those are not real datasets from the Hub so we exclude them.
  318. if default_tag not in ["csv", "json", "pandas", "parquet", "text"]:
  319. if dataset_metadata is None:
  320. dataset_metadata = [{"config": one_dataset.config_name, "split": str(one_dataset.split)}]
  321. if dataset_tags is None:
  322. dataset_tags = [default_tag]
  323. if dataset_args is None:
  324. dataset_args = [one_dataset.config_name]
  325. if dataset is None and dataset_tags is not None:
  326. dataset = dataset_tags
  327. # Infer default finetuned_from
  328. if (
  329. finetuned_from is None
  330. and hasattr(trainer.model.config, "_name_or_path")
  331. and not os.path.isdir(trainer.model.config._name_or_path)
  332. ):
  333. finetuned_from = trainer.model.config._name_or_path
  334. # Infer default task tag:
  335. if tasks is None:
  336. model_class_name = trainer.model.__class__.__name__
  337. for task, mapping in TASK_MAPPING.items():
  338. if model_class_name in _get_mapping_values(mapping):
  339. tasks = task
  340. if model_name is None:
  341. model_name = Path(trainer.args.output_dir).name
  342. if len(model_name) == 0:
  343. model_name = finetuned_from
  344. # Add `generated_from_trainer` to the tags
  345. if tags is None:
  346. tags = ["generated_from_trainer"]
  347. elif isinstance(tags, str) and tags != "generated_from_trainer":
  348. tags = [tags, "generated_from_trainer"]
  349. elif "generated_from_trainer" not in tags:
  350. tags.append("generated_from_trainer")
  351. _, eval_lines, eval_results = parse_log_history(trainer.state.log_history)
  352. hyperparameters = extract_hyperparameters_from_trainer(trainer)
  353. return cls(
  354. language=language,
  355. license=license,
  356. tags=tags,
  357. model_name=model_name,
  358. finetuned_from=finetuned_from,
  359. tasks=tasks,
  360. dataset=dataset,
  361. dataset_tags=dataset_tags,
  362. dataset_args=dataset_args,
  363. dataset_metadata=dataset_metadata,
  364. eval_results=eval_results,
  365. eval_lines=eval_lines,
  366. hyperparameters=hyperparameters,
  367. )
  368. def parse_log_history(log_history):
  369. """
  370. Parse the `log_history` of a Trainer to get the intermediate and final evaluation results.
  371. """
  372. idx = 0
  373. while idx < len(log_history) and "train_runtime" not in log_history[idx]:
  374. idx += 1
  375. # If there are no training logs
  376. if idx == len(log_history):
  377. idx -= 1
  378. while idx >= 0 and "eval_loss" not in log_history[idx]:
  379. idx -= 1
  380. if idx >= 0:
  381. return None, None, log_history[idx]
  382. else:
  383. return None, None, None
  384. # From now one we can assume we have training logs:
  385. train_log = log_history[idx]
  386. lines = []
  387. training_loss = "No log"
  388. for i in range(idx):
  389. if "loss" in log_history[i]:
  390. training_loss = log_history[i]["loss"]
  391. if "eval_loss" in log_history[i]:
  392. metrics = log_history[i].copy()
  393. _ = metrics.pop("total_flos", None)
  394. epoch = metrics.pop("epoch", None)
  395. step = metrics.pop("step", None)
  396. _ = metrics.pop("eval_runtime", None)
  397. _ = metrics.pop("eval_samples_per_second", None)
  398. _ = metrics.pop("eval_steps_per_second", None)
  399. values = {"Training Loss": training_loss, "Epoch": epoch, "Step": step}
  400. for k, v in metrics.items():
  401. if k == "eval_loss":
  402. values["Validation Loss"] = v
  403. else:
  404. splits = k.split("_")
  405. name = " ".join([part.capitalize() for part in splits[1:]])
  406. values[name] = v
  407. lines.append(values)
  408. idx = len(log_history) - 1
  409. while idx >= 0 and "eval_loss" not in log_history[idx]:
  410. idx -= 1
  411. if idx > 0:
  412. eval_results = {}
  413. for key, value in log_history[idx].items():
  414. key = key.removeprefix("eval_")
  415. if key not in ["runtime", "samples_per_second", "steps_per_second", "epoch", "step"]:
  416. camel_cased_key = " ".join([part.capitalize() for part in key.split("_")])
  417. eval_results[camel_cased_key] = value
  418. return train_log, lines, eval_results
  419. else:
  420. return train_log, lines, None
  421. def _maybe_round(v, decimals=4):
  422. if isinstance(v, float) and len(str(v).split(".")) > 1 and len(str(v).split(".")[1]) > decimals:
  423. return f"{v:.{decimals}f}"
  424. return str(v)
  425. def _regular_table_line(values, col_widths):
  426. values_with_space = [f"| {v}" + " " * (w - len(v) + 1) for v, w in zip(values, col_widths)]
  427. return "".join(values_with_space) + "|\n"
  428. def _second_table_line(col_widths):
  429. values = ["|:" + "-" * w + ":" for w in col_widths]
  430. return "".join(values) + "|\n"
  431. def make_markdown_table(lines):
  432. """
  433. Create a nice Markdown table from the results in `lines`.
  434. """
  435. if lines is None or len(lines) == 0:
  436. return ""
  437. col_widths = {key: len(str(key)) for key in lines[0]}
  438. for line in lines:
  439. for key, value in line.items():
  440. if col_widths[key] < len(_maybe_round(value)):
  441. col_widths[key] = len(_maybe_round(value))
  442. table = _regular_table_line(list(lines[0].keys()), list(col_widths.values()))
  443. table += _second_table_line(list(col_widths.values()))
  444. for line in lines:
  445. table += _regular_table_line([_maybe_round(v) for v in line.values()], list(col_widths.values()))
  446. return table
  447. _TRAINING_ARGS_KEYS = [
  448. "learning_rate",
  449. "train_batch_size",
  450. "eval_batch_size",
  451. "seed",
  452. ]
  453. def extract_hyperparameters_from_trainer(trainer):
  454. hyperparameters = {k: getattr(trainer.args, k) for k in _TRAINING_ARGS_KEYS}
  455. if trainer.args.parallel_mode not in [ParallelMode.NOT_PARALLEL, ParallelMode.NOT_DISTRIBUTED]:
  456. hyperparameters["distributed_type"] = (
  457. "multi-GPU" if trainer.args.parallel_mode == ParallelMode.DISTRIBUTED else trainer.args.parallel_mode.value
  458. )
  459. if trainer.args.world_size > 1:
  460. hyperparameters["num_devices"] = trainer.args.world_size
  461. if trainer.args.gradient_accumulation_steps > 1:
  462. hyperparameters["gradient_accumulation_steps"] = trainer.args.gradient_accumulation_steps
  463. total_train_batch_size = (
  464. trainer.args.train_batch_size * trainer.args.world_size * trainer.args.gradient_accumulation_steps
  465. )
  466. if total_train_batch_size != hyperparameters["train_batch_size"]:
  467. hyperparameters["total_train_batch_size"] = total_train_batch_size
  468. total_eval_batch_size = trainer.args.eval_batch_size * trainer.args.world_size
  469. if total_eval_batch_size != hyperparameters["eval_batch_size"]:
  470. hyperparameters["total_eval_batch_size"] = total_eval_batch_size
  471. if trainer.args.optim:
  472. optimizer_name = trainer.args.optim
  473. optimizer_args = trainer.args.optim_args if trainer.args.optim_args else "No additional optimizer arguments"
  474. if "adam" in optimizer_name.lower():
  475. hyperparameters["optimizer"] = (
  476. f"Use {optimizer_name} with betas=({trainer.args.adam_beta1},{trainer.args.adam_beta2}) and"
  477. f" epsilon={trainer.args.adam_epsilon} and optimizer_args={optimizer_args}"
  478. )
  479. else:
  480. hyperparameters["optimizer"] = f"Use {optimizer_name} and the args are:\n{optimizer_args}"
  481. hyperparameters["lr_scheduler_type"] = trainer.args.lr_scheduler_type.value
  482. if trainer.args.warmup_steps != 0.0:
  483. hyperparameters["lr_scheduler_warmup_steps"] = trainer.args.warmup_steps
  484. if trainer.args.max_steps != -1:
  485. hyperparameters["training_steps"] = trainer.args.max_steps
  486. else:
  487. hyperparameters["num_epochs"] = trainer.args.num_train_epochs
  488. if trainer.args.fp16:
  489. hyperparameters["mixed_precision_training"] = "Native AMP"
  490. if trainer.args.label_smoothing_factor != 0.0:
  491. hyperparameters["label_smoothing_factor"] = trainer.args.label_smoothing_factor
  492. return hyperparameters