Aluode/PerceptionLabPortable
0
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 at6#7# http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# 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 and13# limitations under the License.14"""Configuration base class and utilities."""15 16import copy17import json18import os19import warnings20from dataclasses import dataclass21from pathlib import Path22from typing import Any, Optional, Union23 24import requests25import yaml26from huggingface_hub import model_info27from huggingface_hub.errors import OfflineModeIsEnabled28from huggingface_hub.utils import HFValidationError29 30from . import __version__31from .models.auto.modeling_auto import (32 MODEL_FOR_AUDIO_CLASSIFICATION_MAPPING_NAMES,33 MODEL_FOR_CAUSAL_LM_MAPPING_NAMES,34 MODEL_FOR_CTC_MAPPING_NAMES,35 MODEL_FOR_IMAGE_CLASSIFICATION_MAPPING_NAMES,36 MODEL_FOR_IMAGE_SEGMENTATION_MAPPING_NAMES,37 MODEL_FOR_IMAGE_TEXT_TO_TEXT_MAPPING_NAMES,38 MODEL_FOR_MASKED_LM_MAPPING_NAMES,39 MODEL_FOR_OBJECT_DETECTION_MAPPING_NAMES,40 MODEL_FOR_QUESTION_ANSWERING_MAPPING_NAMES,41 MODEL_FOR_SEQ_TO_SEQ_CAUSAL_LM_MAPPING_NAMES,42 MODEL_FOR_SEQUENCE_CLASSIFICATION_MAPPING_NAMES,43 MODEL_FOR_SPEECH_SEQ_2_SEQ_MAPPING_NAMES,44 MODEL_FOR_TABLE_QUESTION_ANSWERING_MAPPING_NAMES,45 MODEL_FOR_TOKEN_CLASSIFICATION_MAPPING_NAMES,46 MODEL_FOR_ZERO_SHOT_IMAGE_CLASSIFICATION_MAPPING_NAMES,47)48from .training_args import ParallelMode49from .utils import (50 MODEL_CARD_NAME,51 cached_file,52 is_datasets_available,53 is_offline_mode,54 is_tf_available,55 is_tokenizers_available,56 is_torch_available,57 logging,58)59 60 61TASK_MAPPING = {62 "text-generation": MODEL_FOR_CAUSAL_LM_MAPPING_NAMES,63 "image-classification": MODEL_FOR_IMAGE_CLASSIFICATION_MAPPING_NAMES,64 "image-segmentation": MODEL_FOR_IMAGE_SEGMENTATION_MAPPING_NAMES,65 "fill-mask": MODEL_FOR_MASKED_LM_MAPPING_NAMES,66 "object-detection": MODEL_FOR_OBJECT_DETECTION_MAPPING_NAMES,67 "question-answering": MODEL_FOR_QUESTION_ANSWERING_MAPPING_NAMES,68 "text2text-generation": MODEL_FOR_SEQ_TO_SEQ_CAUSAL_LM_MAPPING_NAMES,69 "text-classification": MODEL_FOR_SEQUENCE_CLASSIFICATION_MAPPING_NAMES,70 "table-question-answering": MODEL_FOR_TABLE_QUESTION_ANSWERING_MAPPING_NAMES,71 "token-classification": MODEL_FOR_TOKEN_CLASSIFICATION_MAPPING_NAMES,72 "audio-classification": MODEL_FOR_AUDIO_CLASSIFICATION_MAPPING_NAMES,73 "automatic-speech-recognition": {**MODEL_FOR_CTC_MAPPING_NAMES, **MODEL_FOR_SPEECH_SEQ_2_SEQ_MAPPING_NAMES},74 "zero-shot-image-classification": MODEL_FOR_ZERO_SHOT_IMAGE_CLASSIFICATION_MAPPING_NAMES,75 "image-text-to-text": MODEL_FOR_IMAGE_TEXT_TO_TEXT_MAPPING_NAMES,76}77 78logger = logging.get_logger(__name__)79 80 81class ModelCard:82 r"""83 Structured Model Card class. Store model card as well as methods for loading/downloading/saving model cards.84 85 Please read the following paper for details and explanation on the sections: "Model Cards for Model Reporting" by86 Margaret Mitchell, Simone Wu, Andrew Zaldivar, Parker Barnes, Lucy Vasserman, Ben Hutchinson, Elena Spitzer,87 Inioluwa Deborah Raji and Timnit Gebru for the proposal behind model cards. Link: https://huggingface.co/papers/1810.0399388 89 Note: A model card can be loaded and saved to disk.90 """91 92 def __init__(self, **kwargs):93 warnings.warn(94 "The class `ModelCard` is deprecated and will be removed in version 5 of Transformers", FutureWarning95 )96 # Recommended attributes from https://huggingface.co/papers/1810.03993 (see papers)97 self.model_details = kwargs.pop("model_details", {})98 self.intended_use = kwargs.pop("intended_use", {})99 self.factors = kwargs.pop("factors", {})100 self.metrics = kwargs.pop("metrics", {})101 self.evaluation_data = kwargs.pop("evaluation_data", {})102 self.training_data = kwargs.pop("training_data", {})103 self.quantitative_analyses = kwargs.pop("quantitative_analyses", {})104 self.ethical_considerations = kwargs.pop("ethical_considerations", {})105 self.caveats_and_recommendations = kwargs.pop("caveats_and_recommendations", {})106 107 # Open additional attributes108 for key, value in kwargs.items():109 try:110 setattr(self, key, value)111 except AttributeError as err:112 logger.error(f"Can't set {key} with value {value} for {self}")113 raise err114 115 def save_pretrained(self, save_directory_or_file):116 """Save a model card object to the directory or file `save_directory_or_file`."""117 if os.path.isdir(save_directory_or_file):118 # If we save using the predefined names, we can load using `from_pretrained`119 output_model_card_file = os.path.join(save_directory_or_file, MODEL_CARD_NAME)120 else:121 output_model_card_file = save_directory_or_file122 123 self.to_json_file(output_model_card_file)124 logger.info(f"Model card saved in {output_model_card_file}")125 126 @classmethod127 def from_pretrained(cls, pretrained_model_name_or_path, **kwargs):128 r"""129 Instantiate a [`ModelCard`] from a pre-trained model model card.130 131 Parameters:132 pretrained_model_name_or_path: either:133 134 - a string, the *model id* of a pretrained model card hosted inside a model repo on huggingface.co.135 - a path to a *directory* containing a model card file saved using the [`~ModelCard.save_pretrained`]136 method, e.g.: `./my_model_directory/`.137 - a path or url to a saved model card JSON *file*, e.g.: `./my_model_directory/modelcard.json`.138 139 cache_dir: (*optional*) string:140 Path to a directory in which a downloaded pre-trained model card should be cached if the standard cache141 should not be used.142 143 kwargs: (*optional*) dict: key/value pairs with which to update the ModelCard object after loading.144 145 - The values in kwargs of any keys which are model card attributes will be used to override the loaded146 values.147 - Behavior concerning key/value pairs whose keys are *not* model card attributes is controlled by the148 *return_unused_kwargs* keyword parameter.149 150 proxies: (*optional*) dict, default None:151 A dictionary of proxy servers to use by protocol or endpoint, e.g.: {'http': 'foo.bar:3128',152 'http://hostname': 'foo.bar:4012'}. The proxies are used on each request.153 154 return_unused_kwargs: (*optional*) bool:155 156 - If False, then this function returns just the final model card object.157 - If True, then this functions returns a tuple *(model card, unused_kwargs)* where *unused_kwargs* is a158 dictionary consisting of the key/value pairs whose keys are not model card attributes: ie the part of159 kwargs which has not been used to update *ModelCard* and is otherwise ignored.160 161 Examples:162 163 ```python164 # Download model card from huggingface.co and cache.165 modelcard = ModelCard.from_pretrained("google-bert/bert-base-uncased")166 # Model card was saved using *save_pretrained('./test/saved_model/')*167 modelcard = ModelCard.from_pretrained("./test/saved_model/")168 modelcard = ModelCard.from_pretrained("./test/saved_model/modelcard.json")169 modelcard = ModelCard.from_pretrained("google-bert/bert-base-uncased", output_attentions=True, foo=False)170 ```"""171 cache_dir = kwargs.pop("cache_dir", None)172 proxies = kwargs.pop("proxies", None)173 return_unused_kwargs = kwargs.pop("return_unused_kwargs", False)174 from_pipeline = kwargs.pop("_from_pipeline", None)175 176 user_agent = {"file_type": "model_card"}177 if from_pipeline is not None:178 user_agent["using_pipeline"] = from_pipeline179 180 is_local = os.path.isdir(pretrained_model_name_or_path)181 if os.path.isfile(pretrained_model_name_or_path):182 resolved_model_card_file = pretrained_model_name_or_path183 is_local = True184 else:185 try:186 # Load from URL or cache if already cached187 resolved_model_card_file = cached_file(188 pretrained_model_name_or_path,189 filename=MODEL_CARD_NAME,190 cache_dir=cache_dir,191 proxies=proxies,192 user_agent=user_agent,193 )194 if is_local:195 logger.info(f"loading model card file {resolved_model_card_file}")196 else:197 logger.info(f"loading model card file {MODEL_CARD_NAME} from cache at {resolved_model_card_file}")198 # Load model card199 modelcard = cls.from_json_file(resolved_model_card_file)200 201 except (OSError, json.JSONDecodeError):202 # We fall back on creating an empty model card203 modelcard = cls()204 205 # Update model card with kwargs if needed206 to_remove = []207 for key, value in kwargs.items():208 if hasattr(modelcard, key):209 setattr(modelcard, key, value)210 to_remove.append(key)211 for key in to_remove:212 kwargs.pop(key, None)213 214 logger.info(f"Model card: {modelcard}")215 if return_unused_kwargs:216 return modelcard, kwargs217 else:218 return modelcard219 220 @classmethod221 def from_dict(cls, json_object):222 """Constructs a `ModelCard` from a Python dictionary of parameters."""223 return cls(**json_object)224 225 @classmethod226 def from_json_file(cls, json_file):227 """Constructs a `ModelCard` from a json file of parameters."""228 with open(json_file, encoding="utf-8") as reader:229 text = reader.read()230 dict_obj = json.loads(text)231 return cls(**dict_obj)232 233 def __eq__(self, other):234 return self.__dict__ == other.__dict__235 236 def __repr__(self):237 return str(self.to_json_string())238 239 def to_dict(self):240 """Serializes this instance to a Python dictionary."""241 output = copy.deepcopy(self.__dict__)242 return output243 244 def to_json_string(self):245 """Serializes this instance to a JSON string."""246 return json.dumps(self.to_dict(), indent=2, sort_keys=True) + "\n"247 248 def to_json_file(self, json_file_path):249 """Save this instance to a json file."""250 with open(json_file_path, "w", encoding="utf-8") as writer:251 writer.write(self.to_json_string())252 253 254AUTOGENERATED_TRAINER_COMMENT = """255<!-- This model card has been generated automatically according to the information the Trainer had access to. You256should probably proofread and complete it, then remove this comment. -->257"""258 259AUTOGENERATED_KERAS_COMMENT = """260<!-- This model card has been generated automatically according to the information Keras had access to. You should261probably proofread and complete it, then remove this comment. -->262"""263 264 265TASK_TAG_TO_NAME_MAPPING = {266 "fill-mask": "Masked Language Modeling",267 "image-classification": "Image Classification",268 "image-segmentation": "Image Segmentation",269 "multiple-choice": "Multiple Choice",270 "object-detection": "Object Detection",271 "question-answering": "Question Answering",272 "summarization": "Summarization",273 "table-question-answering": "Table Question Answering",274 "text-classification": "Text Classification",275 "text-generation": "Causal Language Modeling",276 "text2text-generation": "Sequence-to-sequence Language Modeling",277 "token-classification": "Token Classification",278 "translation": "Translation",279 "zero-shot-classification": "Zero Shot Classification",280 "automatic-speech-recognition": "Automatic Speech Recognition",281 "audio-classification": "Audio Classification",282}283 284 285METRIC_TAGS = [286 "accuracy",287 "bleu",288 "f1",289 "matthews_correlation",290 "pearsonr",291 "precision",292 "recall",293 "rouge",294 "sacrebleu",295 "spearmanr",296 "wer",297]298 299 300def _listify(obj):301 if obj is None:302 return []303 elif isinstance(obj, str):304 return [obj]305 else:306 return obj307 308 309def _insert_values_as_list(metadata, name, values):310 if values is None:311 return metadata312 if isinstance(values, str):313 values = [values]314 values = [v for v in values if v is not None]315 if len(values) == 0:316 return metadata317 metadata[name] = values318 return metadata319 320 321def infer_metric_tags_from_eval_results(eval_results):322 if eval_results is None:323 return {}324 result = {}325 for key in eval_results:326 if key.lower().replace(" ", "_") in METRIC_TAGS:327 result[key.lower().replace(" ", "_")] = key328 elif key.lower() == "rouge1":329 result["rouge"] = key330 return result331 332 333def _insert_value(metadata, name, value):334 if value is None:335 return metadata336 metadata[name] = value337 return metadata338 339 340def is_hf_dataset(dataset):341 if not is_datasets_available():342 return False343 344 from datasets import Dataset, IterableDataset345 346 return isinstance(dataset, (Dataset, IterableDataset))347 348 349def _get_mapping_values(mapping):350 result = []351 for v in mapping.values():352 if isinstance(v, (tuple, list)):353 result += list(v)354 else:355 result.append(v)356 return result357 358 359@dataclass360class TrainingSummary:361 model_name: str362 language: Optional[Union[str, list[str]]] = None363 license: Optional[str] = None364 tags: Optional[Union[str, list[str]]] = None365 finetuned_from: Optional[str] = None366 tasks: Optional[Union[str, list[str]]] = None367 dataset: Optional[Union[str, list[str]]] = None368 dataset_tags: Optional[Union[str, list[str]]] = None369 dataset_args: Optional[Union[str, list[str]]] = None370 dataset_metadata: Optional[dict[str, Any]] = None371 eval_results: Optional[dict[str, float]] = None372 eval_lines: Optional[list[str]] = None373 hyperparameters: Optional[dict[str, Any]] = None374 source: Optional[str] = "trainer"375 376 def __post_init__(self):377 # Infer default license from the checkpoint used, if possible.378 if (379 self.license is None380 and not is_offline_mode()381 and self.finetuned_from is not None382 and len(self.finetuned_from) > 0383 ):384 try:385 info = model_info(self.finetuned_from)386 for tag in info.tags:387 if tag.startswith("license:"):388 self.license = tag[8:]389 except (390 requests.exceptions.HTTPError,391 requests.exceptions.ConnectionError,392 HFValidationError,393 OfflineModeIsEnabled,394 ):395 pass396 397 def create_model_index(self, metric_mapping):398 model_index = {"name": self.model_name}399 400 # Dataset mapping tag -> name401 dataset_names = _listify(self.dataset)402 dataset_tags = _listify(self.dataset_tags)403 dataset_args = _listify(self.dataset_args)404 dataset_metadata = _listify(self.dataset_metadata)405 if len(dataset_args) < len(dataset_tags):406 dataset_args = dataset_args + [None] * (len(dataset_tags) - len(dataset_args))407 dataset_mapping = dict(zip(dataset_tags, dataset_names))408 dataset_arg_mapping = dict(zip(dataset_tags, dataset_args))409 dataset_metadata_mapping = dict(zip(dataset_tags, dataset_metadata))410 411 task_mapping = {412 task: TASK_TAG_TO_NAME_MAPPING[task] for task in _listify(self.tasks) if task in TASK_TAG_TO_NAME_MAPPING413 }414 415 model_index["results"] = []416 417 if len(task_mapping) == 0 and len(dataset_mapping) == 0:418 return [model_index]419 if len(task_mapping) == 0:420 task_mapping = {None: None}421 if len(dataset_mapping) == 0:422 dataset_mapping = {None: None}423 424 # One entry per dataset and per task425 all_possibilities = [(task_tag, ds_tag) for task_tag in task_mapping for ds_tag in dataset_mapping]426 for task_tag, ds_tag in all_possibilities:427 result = {}428 if task_tag is not None:429 result["task"] = {"name": task_mapping[task_tag], "type": task_tag}430 431 if ds_tag is not None:432 metadata = dataset_metadata_mapping.get(ds_tag, {})433 result["dataset"] = {434 "name": dataset_mapping[ds_tag],435 "type": ds_tag,436 **metadata,437 }438 if dataset_arg_mapping[ds_tag] is not None:439 result["dataset"]["args"] = dataset_arg_mapping[ds_tag]440 441 if len(metric_mapping) > 0:442 result["metrics"] = []443 for metric_tag, metric_name in metric_mapping.items():444 result["metrics"].append(445 {446 "name": metric_name,447 "type": metric_tag,448 "value": self.eval_results[metric_name],449 }450 )451 452 # Remove partial results to avoid the model card being rejected.453 if "task" in result and "dataset" in result and "metrics" in result:454 model_index["results"].append(result)455 else:456 logger.info(f"Dropping the following result as it does not have all the necessary fields:\n{result}")457 458 return [model_index]459 460 def create_metadata(self):461 metric_mapping = infer_metric_tags_from_eval_results(self.eval_results)462 463 metadata = {}464 metadata = _insert_value(metadata, "library_name", "transformers")465 metadata = _insert_values_as_list(metadata, "language", self.language)466 metadata = _insert_value(metadata, "license", self.license)467 if self.finetuned_from is not None and isinstance(self.finetuned_from, str) and len(self.finetuned_from) > 0:468 metadata = _insert_value(metadata, "base_model", self.finetuned_from)469 metadata = _insert_values_as_list(metadata, "tags", self.tags)470 metadata = _insert_values_as_list(metadata, "datasets", self.dataset_tags)471 metadata = _insert_values_as_list(metadata, "metrics", list(metric_mapping.keys()))472 metadata["model-index"] = self.create_model_index(metric_mapping)473 474 return metadata475 476 def to_model_card(self):477 model_card = ""478 479 metadata = yaml.dump(self.create_metadata(), sort_keys=False)480 if len(metadata) > 0:481 model_card = f"---\n{metadata}---\n"482 483 # Now the model card for realsies.484 if self.source == "trainer":485 model_card += AUTOGENERATED_TRAINER_COMMENT486 else:487 model_card += AUTOGENERATED_KERAS_COMMENT488 489 model_card += f"\n# {self.model_name}\n\n"490 491 if self.finetuned_from is None:492 model_card += "This model was trained from scratch on "493 else:494 model_card += (495 "This model is a fine-tuned version of"496 f" [{self.finetuned_from}](https://huggingface.co/{self.finetuned_from}) on "497 )498 499 if self.dataset is None or (isinstance(self.dataset, list) and len(self.dataset) == 0):500 model_card += "an unknown dataset."501 else:502 if isinstance(self.dataset, str):503 model_card += f"the {self.dataset} dataset."504 elif isinstance(self.dataset, (tuple, list)) and len(self.dataset) == 1:505 model_card += f"the {self.dataset[0]} dataset."506 else:507 model_card += (508 ", ".join([f"the {ds}" for ds in self.dataset[:-1]]) + f" and the {self.dataset[-1]} datasets."509 )510 511 if self.eval_results is not None:512 model_card += "\nIt achieves the following results on the evaluation set:\n"513 model_card += "\n".join([f"- {name}: {_maybe_round(value)}" for name, value in self.eval_results.items()])514 model_card += "\n"515 516 model_card += "\n## Model description\n\nMore information needed\n"517 model_card += "\n## Intended uses & limitations\n\nMore information needed\n"518 model_card += "\n## Training and evaluation data\n\nMore information needed\n"519 520 model_card += "\n## Training procedure\n"521 model_card += "\n### Training hyperparameters\n"522 if self.hyperparameters is not None:523 model_card += "\nThe following hyperparameters were used during training:\n"524 model_card += "\n".join([f"- {name}: {value}" for name, value in self.hyperparameters.items()])525 model_card += "\n"526 else:527 model_card += "\nMore information needed\n"528 529 if self.eval_lines is not None:530 model_card += "\n### Training results\n\n"531 model_card += make_markdown_table(self.eval_lines)532 model_card += "\n"533 534 model_card += "\n### Framework versions\n\n"535 model_card += f"- Transformers {__version__}\n"536 537 if self.source == "trainer" and is_torch_available():538 import torch539 540 model_card += f"- Pytorch {torch.__version__}\n"541 elif self.source == "keras" and is_tf_available():542 import tensorflow as tf543 544 model_card += f"- TensorFlow {tf.__version__}\n"545 if is_datasets_available():546 import datasets547 548 model_card += f"- Datasets {datasets.__version__}\n"549 if is_tokenizers_available():550 import tokenizers551 552 model_card += f"- Tokenizers {tokenizers.__version__}\n"553 554 return model_card555 556 @classmethod557 def from_trainer(558 cls,559 trainer,560 language=None,561 license=None,562 tags=None,563 model_name=None,564 finetuned_from=None,565 tasks=None,566 dataset_tags=None,567 dataset_metadata=None,568 dataset=None,569 dataset_args=None,570 ):571 # Infer default from dataset572 one_dataset = trainer.eval_dataset if trainer.eval_dataset is not None else trainer.train_dataset573 if is_hf_dataset(one_dataset) and (dataset_tags is None or dataset_args is None or dataset_metadata is None):574 default_tag = one_dataset.builder_name575 # Those are not real datasets from the Hub so we exclude them.576 if default_tag not in ["csv", "json", "pandas", "parquet", "text"]:577 if dataset_metadata is None:578 dataset_metadata = [{"config": one_dataset.config_name, "split": str(one_dataset.split)}]579 if dataset_tags is None:580 dataset_tags = [default_tag]581 if dataset_args is None:582 dataset_args = [one_dataset.config_name]583 584 if dataset is None and dataset_tags is not None:585 dataset = dataset_tags586 587 # Infer default finetuned_from588 if (589 finetuned_from is None590 and hasattr(trainer.model.config, "_name_or_path")591 and not os.path.isdir(trainer.model.config._name_or_path)592 ):593 finetuned_from = trainer.model.config._name_or_path594 595 # Infer default task tag:596 if tasks is None:597 model_class_name = trainer.model.__class__.__name__598 for task, mapping in TASK_MAPPING.items():599 if model_class_name in _get_mapping_values(mapping):600 tasks = task601 602 if model_name is None:603 model_name = Path(trainer.args.output_dir).name604 if len(model_name) == 0:605 model_name = finetuned_from606 607 # Add `generated_from_trainer` to the tags608 if tags is None:609 tags = ["generated_from_trainer"]610 elif isinstance(tags, str) and tags != "generated_from_trainer":611 tags = [tags, "generated_from_trainer"]612 elif "generated_from_trainer" not in tags:613 tags.append("generated_from_trainer")614 615 _, eval_lines, eval_results = parse_log_history(trainer.state.log_history)616 hyperparameters = extract_hyperparameters_from_trainer(trainer)617 618 return cls(619 language=language,620 license=license,621 tags=tags,622 model_name=model_name,623 finetuned_from=finetuned_from,624 tasks=tasks,625 dataset=dataset,626 dataset_tags=dataset_tags,627 dataset_args=dataset_args,628 dataset_metadata=dataset_metadata,629 eval_results=eval_results,630 eval_lines=eval_lines,631 hyperparameters=hyperparameters,632 )633 634 @classmethod635 def from_keras(636 cls,637 model,638 model_name,639 keras_history=None,640 language=None,641 license=None,642 tags=None,643 finetuned_from=None,644 tasks=None,645 dataset_tags=None,646 dataset=None,647 dataset_args=None,648 ):649 # Infer default from dataset650 if dataset is not None:651 if is_hf_dataset(dataset) and (dataset_tags is None or dataset_args is None):652 default_tag = dataset.builder_name653 # Those are not real datasets from the Hub so we exclude them.654 if default_tag not in ["csv", "json", "pandas", "parquet", "text"]:655 if dataset_tags is None:656 dataset_tags = [default_tag]657 if dataset_args is None:658 dataset_args = [dataset.config_name]659 660 if dataset is None and dataset_tags is not None:661 dataset = dataset_tags662 663 # Infer default finetuned_from664 if (665 finetuned_from is None666 and hasattr(model.config, "_name_or_path")667 and not os.path.isdir(model.config._name_or_path)668 ):669 finetuned_from = model.config._name_or_path670 671 # Infer default task tag:672 if tasks is None:673 model_class_name = model.__class__.__name__674 for task, mapping in TASK_MAPPING.items():675 if model_class_name in _get_mapping_values(mapping):676 tasks = task677 678 # Add `generated_from_keras_callback` to the tags679 if tags is None:680 tags = ["generated_from_keras_callback"]681 elif isinstance(tags, str) and tags != "generated_from_keras_callback":682 tags = [tags, "generated_from_keras_callback"]683 elif "generated_from_keras_callback" not in tags:684 tags.append("generated_from_keras_callback")685 686 if keras_history is not None:687 _, eval_lines, eval_results = parse_keras_history(keras_history)688 else:689 eval_lines = []690 eval_results = {}691 hyperparameters = extract_hyperparameters_from_keras(model)692 693 return cls(694 language=language,695 license=license,696 tags=tags,697 model_name=model_name,698 finetuned_from=finetuned_from,699 tasks=tasks,700 dataset_tags=dataset_tags,701 dataset=dataset,702 dataset_args=dataset_args,703 eval_results=eval_results,704 eval_lines=eval_lines,705 hyperparameters=hyperparameters,706 source="keras",707 )708 709 710def parse_keras_history(logs):711 """712 Parse the `logs` of either a `keras.History` object returned by `model.fit()` or an accumulated logs `dict`713 passed to the `PushToHubCallback`. Returns lines and logs compatible with those returned by `parse_log_history`.714 """715 if hasattr(logs, "history"):716 # This looks like a `History` object717 if not hasattr(logs, "epoch"):718 # This history looks empty, return empty results719 return None, [], {}720 logs.history["epoch"] = logs.epoch721 logs = logs.history722 else:723 # Training logs is a list of dicts, let's invert it to a dict of lists to match a History object724 logs = {log_key: [single_dict[log_key] for single_dict in logs] for log_key in logs[0]}725 726 lines = []727 for i in range(len(logs["epoch"])):728 epoch_dict = {log_key: log_value_list[i] for log_key, log_value_list in logs.items()}729 values = {}730 for k, v in epoch_dict.items():731 if k.startswith("val_"):732 k = "validation_" + k[4:]733 elif k != "epoch":734 k = "train_" + k735 splits = k.split("_")736 name = " ".join([part.capitalize() for part in splits])737 values[name] = v738 lines.append(values)739 740 eval_results = lines[-1]741 742 return logs, lines, eval_results743 744 745def parse_log_history(log_history):746 """747 Parse the `log_history` of a Trainer to get the intermediate and final evaluation results.748 """749 idx = 0750 while idx < len(log_history) and "train_runtime" not in log_history[idx]:751 idx += 1752 753 # If there are no training logs754 if idx == len(log_history):755 idx -= 1756 while idx >= 0 and "eval_loss" not in log_history[idx]:757 idx -= 1758 759 if idx >= 0:760 return None, None, log_history[idx]761 else:762 return None, None, None763 764 # From now one we can assume we have training logs:765 train_log = log_history[idx]766 lines = []767 training_loss = "No log"768 for i in range(idx):769 if "loss" in log_history[i]:770 training_loss = log_history[i]["loss"]771 if "eval_loss" in log_history[i]:772 metrics = log_history[i].copy()773 _ = metrics.pop("total_flos", None)774 epoch = metrics.pop("epoch", None)775 step = metrics.pop("step", None)776 _ = metrics.pop("eval_runtime", None)777 _ = metrics.pop("eval_samples_per_second", None)778 _ = metrics.pop("eval_steps_per_second", None)779 _ = metrics.pop("eval_jit_compilation_time", None)780 values = {"Training Loss": training_loss, "Epoch": epoch, "Step": step}781 for k, v in metrics.items():782 if k == "eval_loss":783 values["Validation Loss"] = v784 else:785 splits = k.split("_")786 name = " ".join([part.capitalize() for part in splits[1:]])787 values[name] = v788 lines.append(values)789 790 idx = len(log_history) - 1791 while idx >= 0 and "eval_loss" not in log_history[idx]:792 idx -= 1793 794 if idx > 0:795 eval_results = {}796 for key, value in log_history[idx].items():797 key = key.removeprefix("eval_")798 if key not in ["runtime", "samples_per_second", "steps_per_second", "epoch", "step"]:799 camel_cased_key = " ".join([part.capitalize() for part in key.split("_")])800 eval_results[camel_cased_key] = value801 return train_log, lines, eval_results802 else:803 return train_log, lines, None804 805 806def extract_hyperparameters_from_keras(model):807 from .modeling_tf_utils import keras808 809 hyperparameters = {}810 if hasattr(model, "optimizer") and model.optimizer is not None:811 hyperparameters["optimizer"] = model.optimizer.get_config()812 else:813 hyperparameters["optimizer"] = None814 hyperparameters["training_precision"] = keras.mixed_precision.global_policy().name815 816 return hyperparameters817 818 819def _maybe_round(v, decimals=4):820 if isinstance(v, float) and len(str(v).split(".")) > 1 and len(str(v).split(".")[1]) > decimals:821 return f"{v:.{decimals}f}"822 return str(v)823 824 825def _regular_table_line(values, col_widths):826 values_with_space = [f"| {v}" + " " * (w - len(v) + 1) for v, w in zip(values, col_widths)]827 return "".join(values_with_space) + "|\n"828 829 830def _second_table_line(col_widths):831 values = ["|:" + "-" * w + ":" for w in col_widths]832 return "".join(values) + "|\n"833 834 835def make_markdown_table(lines):836 """837 Create a nice Markdown table from the results in `lines`.838 """839 if lines is None or len(lines) == 0:840 return ""841 col_widths = {key: len(str(key)) for key in lines[0]}842 for line in lines:843 for key, value in line.items():844 if col_widths[key] < len(_maybe_round(value)):845 col_widths[key] = len(_maybe_round(value))846 847 table = _regular_table_line(list(lines[0].keys()), list(col_widths.values()))848 table += _second_table_line(list(col_widths.values()))849 for line in lines:850 table += _regular_table_line([_maybe_round(v) for v in line.values()], list(col_widths.values()))851 return table852 853 854_TRAINING_ARGS_KEYS = [855 "learning_rate",856 "train_batch_size",857 "eval_batch_size",858 "seed",859]860 861 862def extract_hyperparameters_from_trainer(trainer):863 hyperparameters = {k: getattr(trainer.args, k) for k in _TRAINING_ARGS_KEYS}864 865 if trainer.args.parallel_mode not in [ParallelMode.NOT_PARALLEL, ParallelMode.NOT_DISTRIBUTED]:866 hyperparameters["distributed_type"] = (867 "multi-GPU" if trainer.args.parallel_mode == ParallelMode.DISTRIBUTED else trainer.args.parallel_mode.value868 )869 if trainer.args.world_size > 1:870 hyperparameters["num_devices"] = trainer.args.world_size871 if trainer.args.gradient_accumulation_steps > 1:872 hyperparameters["gradient_accumulation_steps"] = trainer.args.gradient_accumulation_steps873 874 total_train_batch_size = (875 trainer.args.train_batch_size * trainer.args.world_size * trainer.args.gradient_accumulation_steps876 )877 if total_train_batch_size != hyperparameters["train_batch_size"]:878 hyperparameters["total_train_batch_size"] = total_train_batch_size879 total_eval_batch_size = trainer.args.eval_batch_size * trainer.args.world_size880 if total_eval_batch_size != hyperparameters["eval_batch_size"]:881 hyperparameters["total_eval_batch_size"] = total_eval_batch_size882 883 if trainer.args.optim:884 optimizer_name = trainer.args.optim885 optimizer_args = trainer.args.optim_args if trainer.args.optim_args else "No additional optimizer arguments"886 887 if "adam" in optimizer_name.lower():888 hyperparameters["optimizer"] = (889 f"Use {optimizer_name} with betas=({trainer.args.adam_beta1},{trainer.args.adam_beta2}) and"890 f" epsilon={trainer.args.adam_epsilon} and optimizer_args={optimizer_args}"891 )892 else:893 hyperparameters["optimizer"] = f"Use {optimizer_name} and the args are:\n{optimizer_args}"894 895 hyperparameters["lr_scheduler_type"] = trainer.args.lr_scheduler_type.value896 if trainer.args.warmup_ratio != 0.0:897 hyperparameters["lr_scheduler_warmup_ratio"] = trainer.args.warmup_ratio898 if trainer.args.warmup_steps != 0.0:899 hyperparameters["lr_scheduler_warmup_steps"] = trainer.args.warmup_steps900 if trainer.args.max_steps != -1:901 hyperparameters["training_steps"] = trainer.args.max_steps902 else:903 hyperparameters["num_epochs"] = trainer.args.num_train_epochs904 905 if trainer.args.fp16:906 if trainer.use_apex:907 hyperparameters["mixed_precision_training"] = f"Apex, opt level {trainer.args.fp16_opt_level}"908 else:909 hyperparameters["mixed_precision_training"] = "Native AMP"910 911 if trainer.args.label_smoothing_factor != 0.0:912 hyperparameters["label_smoothing_factor"] = trainer.args.label_smoothing_factor913 914 return hyperparameters915 