Skip to content

Commit

Permalink
Trainer push to hub (huggingface#11328)
Browse files Browse the repository at this point in the history
* Initial support for upload to hub

* push -> upload

* Fixes + examples

* Fix torchhub test

* Torchhub test I hate you

* push_model_to_hub -> push_to_hub

* Apply mixin to other pretrained models

* Remove ABC inheritance

* Add tests

* Typo

* Run tests

* Install git-lfs

* Change approach

* Add push_to_hub to all

* Staging test suite

* Typo

* Maybe like this?

* More deps

* Cache

* Adapt name

* Quality

* MOAR tests

* Put it in testing_utils

* Docs + torchhub last hope

* Styling

* Wrong method

* Typos

* Update src/transformers/file_utils.py

Co-authored-by: Julien Chaumond <julien@huggingface.co>

* Address review comments

* Apply suggestions from code review

Co-authored-by: Patrick von Platen <patrick.v.platen@gmail.com>

Co-authored-by: Julien Chaumond <julien@huggingface.co>
Co-authored-by: Patrick von Platen <patrick.v.platen@gmail.com>
  • Loading branch information
3 people authored Apr 23, 2021
1 parent 7bc86be commit bf2e0cf
Show file tree
Hide file tree
Showing 31 changed files with 766 additions and 31 deletions.
17 changes: 13 additions & 4 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -317,24 +317,33 @@ jobs:
- store_artifacts:
path: ~/transformers/reports

run_tests_git_lfs:
run_tests_hub:
working_directory: ~/transformers
docker:
- image: circleci/python:3.7
environment:
HUGGINGFACE_CO_STAGING: yes
RUN_GIT_LFS_TESTS: yes
TRANSFORMERS_IS_CI: yes
resource_class: xlarge
parallelism: 1
steps:
- checkout
- restore_cache:
keys:
- v0.4-hub-{{ checksum "setup.py" }}
- v0.4-{{ checksum "setup.py" }}
- run: sudo apt-get install git-lfs
- run: |
git config --global user.email "ci@dummy.com"
git config --global user.name "ci"
- run: pip install --upgrade pip
- run: pip install .[testing]
- run: python -m pytest -sv ./tests/test_hf_api.py -k "HfLargefilesTest"
- run: pip install .[torch,sentencepiece,testing]
- save_cache:
key: v0.4-hub-{{ checksum "setup.py" }}
paths:
- '~/.cache/pip'
- run: python -m pytest -sv ./tests/ -m is_staging_test

build_doc:
working_directory: ~/transformers
Expand Down Expand Up @@ -469,7 +478,7 @@ workflows:
- run_tests_flax
- run_tests_pipelines_torch
- run_tests_pipelines_tf
- run_tests_git_lfs
- run_tests_hub
- build_doc
- deploy_doc: *workflow_filters
# tpu_testing_jobs:
Expand Down
7 changes: 7 additions & 0 deletions docs/source/main_classes/model.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,10 @@ Generation

.. autoclass:: transformers.generation_tf_utils.TFGenerationMixin
:members:


Pushing to the Hub
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. autoclass:: transformers.file_utils.PushToHubMixin
:members:
104 changes: 101 additions & 3 deletions docs/source/model_sharing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,14 @@ the `model hub <https://huggingface.co/models>`__.

Optionally, you can join an existing organization or create a new one.

Prepare your model for uploading
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

We have seen in the :doc:`training tutorial <training>`: how to fine-tune a model on a given task. You have probably
done something similar on your task, either using the model directly in your own training loop or using the
:class:`~.transformers.Trainer`/:class:`~.transformers.TFTrainer` class. Let's see how you can share the result on the
`model hub <https://huggingface.co/models>`__.

Model versioning
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Since version v3.5.0, the model hub has built-in model versioning based on git and git-lfs. It is based on the paradigm
that one model *is* one repo.
Expand All @@ -54,6 +52,106 @@ For instance:
>>> revision="v2.0.1" # tag name, or branch name, or commit hash
>>> )
Push your model from Python
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Preparation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The first step is to make sure your credentials to the hub are stored somewhere. This can be done in two ways. If you
have access to a terminal, you cam just run the following command in the virtual environment where you installed 🤗
Transformers:

.. code-block:: bash
transformers-cli login
It will store your access token in the Hugging Face cache folder (by default :obj:`~/.cache/`).

If you don't have an easy access to a terminal (for instance in a Colab session), you can find a token linked to your
acount by going on `huggingface.co <https://huggingface.co/>`, click on your avatar on the top left corner, then on
`Edit profile` on the left, just beneath your profile picture. In the submenu `API Tokens`, you will find your API
token that you can just copy.

Directly push your model to the hub
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Once you have an API token (either stored in the cache or copied and pasted in your notebook), you can directly push a
finetuned model you saved in :obj:`save_drectory` by calling:

.. code-block:: python
finetuned_model.push_to_hub("my-awesome-model")
If you have your API token not stored in the cache, you will need to pass it with :obj:`use_auth_token=your_token`.
This is also be the case for all the examples below, so we won't mention it again.

This will create a repository in your namespace name :obj:`my-awesome-model`, so anyone can now run:

.. code-block:: python
from transformers import AutoModel
model = AutoModel.from_pretrained("your_username/my-awesome-model")
Even better, you can combine this push to the hub with the call to :obj:`save_pretrained`:

.. code-block:: python
finetuned_model.save_pretrained(save_directory, push_to_hub=True, repo_name="my-awesome-model")
If you are a premium user and want your model to be private, just add :obj:`private=True` to this call.

If you are a member of an organization and want to push it inside the namespace of the organization instead of yours,
just add :obj:`organization=my_amazing_org`.

Add new files to your model repo
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Once you have pushed your model to the hub, you might want to add the tokenizer, or a version of your model for another
framework (TensorFlow, PyTorch, Flax). This is super easy to do! Let's begin with the tokenizer. You can add it to the
repo you created before like this

.. code-block:: python
tokenizer.push_to_hub("my-awesome-model")
If you know its URL (it should be :obj:`https://huggingface.co/username/repo_name`), you can also do:

.. code-block:: python
tokenizer.push_to_hub(repo_url=my_repo_url)
And that's all there is to it! It's also a very easy way to fix a mistake if one of the files online had a bug.

To add a model for another backend, it's also super easy. Let's say you have fine-tuned a TensorFlow model and want to
add the pytorch model files to your model repo, so that anyone in the community can use it. The following allows you to
directly create a PyTorch version of your TensorFlow model:

.. code-block:: python
from transfomers import AutoModel
model = AutoModel.from_pretrained(save_directory, from_tf=True)
You can also replace :obj:`save_directory` by the identifier of your model (:obj:`username/repo_name`) if you don't
have a local save of it anymore. Then, just do the same as before:

.. code-block:: python
model.push_to_hub("my-awesome-model")
or

.. code-block:: python
model.push_to_hub(repo_url=my_repo_url)
Use your terminal and git
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Basic steps
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
Empty file.
3 changes: 3 additions & 0 deletions examples/pytorch/language-modeling/run_clm.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,9 @@ def group_texts(examples):
trainer.log_metrics("eval", metrics)
trainer.save_metrics("eval", metrics)

if training_args.push_to_hub:
trainer.push_to_hub()


def _mp_fn(index):
# For xla_spawn (TPUs)
Expand Down
3 changes: 3 additions & 0 deletions examples/pytorch/language-modeling/run_mlm.py
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,9 @@ def group_texts(examples):
trainer.log_metrics("eval", metrics)
trainer.save_metrics("eval", metrics)

if training_args.push_to_hub:
trainer.push_to_hub()


def _mp_fn(index):
# For xla_spawn (TPUs)
Expand Down
3 changes: 3 additions & 0 deletions examples/pytorch/language-modeling/run_plm.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,9 @@ def group_texts(examples):
trainer.log_metrics("eval", metrics)
trainer.save_metrics("eval", metrics)

if training_args.push_to_hub:
trainer.push_to_hub()


def _mp_fn(index):
# For xla_spawn (TPUs)
Expand Down
3 changes: 3 additions & 0 deletions examples/pytorch/multiple-choice/run_swag.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,9 @@ def compute_metrics(eval_predictions):
trainer.log_metrics("eval", metrics)
trainer.save_metrics("eval", metrics)

if training_args.push_to_hub:
trainer.push_to_hub()


def _mp_fn(index):
# For xla_spawn (TPUs)
Expand Down
3 changes: 3 additions & 0 deletions examples/pytorch/question-answering/run_qa.py
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,9 @@ def compute_metrics(p: EvalPrediction):
trainer.log_metrics("test", metrics)
trainer.save_metrics("test", metrics)

if training_args.push_to_hub:
trainer.push_to_hub()


def _mp_fn(index):
# For xla_spawn (TPUs)
Expand Down
3 changes: 3 additions & 0 deletions examples/pytorch/question-answering/run_qa_beam_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,9 @@ def compute_metrics(p: EvalPrediction):
trainer.log_metrics("test", metrics)
trainer.save_metrics("test", metrics)

if training_args.push_to_hub:
trainer.push_to_hub()


def _mp_fn(index):
# For xla_spawn (TPUs)
Expand Down
3 changes: 3 additions & 0 deletions examples/pytorch/summarization/run_summarization.py
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,9 @@ def compute_metrics(eval_preds):
with open(output_test_preds_file, "w") as writer:
writer.write("\n".join(test_preds))

if training_args.push_to_hub:
trainer.push_to_hub()

return results


Expand Down
3 changes: 3 additions & 0 deletions examples/pytorch/text-classification/run_glue.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,9 @@ def compute_metrics(p: EvalPrediction):
item = label_list[item]
writer.write(f"{index}\t{item}\n")

if training_args.push_to_hub:
trainer.push_to_hub()


def _mp_fn(index):
# For xla_spawn (TPUs)
Expand Down
3 changes: 3 additions & 0 deletions examples/pytorch/token-classification/run_ner.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,9 @@ def compute_metrics(p):
for prediction in true_predictions:
writer.write(" ".join(prediction) + "\n")

if training_args.push_to_hub:
trainer.push_to_hub()


def _mp_fn(index):
# For xla_spawn (TPUs)
Expand Down
3 changes: 3 additions & 0 deletions examples/pytorch/translation/run_translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,9 @@ def compute_metrics(eval_preds):
with open(output_test_preds_file, "w") as writer:
writer.write("\n".join(test_preds))

if training_args.push_to_hub:
trainer.push_to_hub()

return results


Expand Down
2 changes: 1 addition & 1 deletion hubconf.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
)


dependencies = ["torch", "numpy", "tokenizers", "filelock", "requests", "tqdm", "regex", "sentencepiece", "sacremoses", "importlib_metadata"]
dependencies = ["torch", "numpy", "tokenizers", "filelock", "requests", "tqdm", "regex", "sentencepiece", "sacremoses", "importlib_metadata", "huggingface_hub"]


@add_start_docstrings(AutoConfig.__doc__)
Expand Down
15 changes: 12 additions & 3 deletions src/transformers/configuration_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@
from typing import Any, Dict, Tuple, Union

from . import __version__
from .file_utils import CONFIG_NAME, cached_path, hf_bucket_url, is_offline_mode, is_remote_url
from .file_utils import CONFIG_NAME, PushToHubMixin, cached_path, hf_bucket_url, is_offline_mode, is_remote_url
from .utils import logging


logger = logging.get_logger(__name__)


class PretrainedConfig(object):
class PretrainedConfig(PushToHubMixin):
r"""
Base class for all configuration classes. Handles a few parameters common to all models' configurations as well as
methods for loading/downloading/saving configurations.
Expand Down Expand Up @@ -310,14 +310,19 @@ def num_labels(self, num_labels: int):
self.id2label = {i: f"LABEL_{i}" for i in range(num_labels)}
self.label2id = dict(zip(self.id2label.values(), self.id2label.keys()))

def save_pretrained(self, save_directory: Union[str, os.PathLike]):
def save_pretrained(self, save_directory: Union[str, os.PathLike], push_to_hub: bool = False, **kwargs):
"""
Save a configuration object to the directory ``save_directory``, so that it can be re-loaded using the
:func:`~transformers.PretrainedConfig.from_pretrained` class method.
Args:
save_directory (:obj:`str` or :obj:`os.PathLike`):
Directory where the configuration JSON file will be saved (will be created if it does not exist).
push_to_hub (:obj:`bool`, `optional`, defaults to :obj:`False`):
Whether or not to push your model to the Hugging Face model hub after saving it.
kwargs:
Additional key word arguments passed along to the
:meth:`~transformers.file_utils.PushToHubMixin.push_to_hub` method.
"""
if os.path.isfile(save_directory):
raise AssertionError(f"Provided path ({save_directory}) should be a directory, not a file")
Expand All @@ -328,6 +333,10 @@ def save_pretrained(self, save_directory: Union[str, os.PathLike]):
self.to_json_file(output_config_file, use_diff=True)
logger.info(f"Configuration saved in {output_config_file}")

if push_to_hub:
url = self._push_to_hub(save_files=[output_config_file], **kwargs)
logger.info(f"Configuration pushed to the hub in this commit: {url}")

@classmethod
def from_pretrained(cls, pretrained_model_name_or_path: Union[str, os.PathLike], **kwargs) -> "PretrainedConfig":
r"""
Expand Down
Loading

0 comments on commit bf2e0cf

Please sign in to comment.