CoolFace
Modelpublic

NbAiLabArchive/test_w5_long_dataset

sourceHugging Faceupdated 5y agoView on Hugging Face
0likes83downloads
run_mlm_flax_stream.py639 linesDownload Raw Back to root
1#!/usr/bin/env python32# coding=utf-83# Copyright 2021 The HuggingFace Team All rights reserved.4#5# Licensed under the Apache License, Version 2.0 (the "License");6# you may not use this file except in compliance with the License.7# You may obtain a copy of the License at8#9#     http://www.apache.org/licenses/LICENSE-2.010#11# Unless required by applicable law or agreed to in writing, software12# distributed under the License is distributed on an "AS IS" BASIS,13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.14# See the License for the specific language governing permissions and15# limitations under the License.16"""17Fine-tuning the library models for masked language modeling (BERT, ALBERT, RoBERTa...) with whole word masking on a18text file or a dataset.19 20Here is the full list of checkpoints on the hub that can be fine-tuned by this script:21https://huggingface.co/models?filter=masked-lm22"""23import logging24import os25import sys26import time27from collections import defaultdict28from dataclasses import dataclass, field29 30# You can also adapt this script on your own masked language modeling task. Pointers for this are left as comments.31from pathlib import Path32from typing import Dict, List, Optional, Tuple33 34import datasets35import numpy as np36from datasets import load_dataset37from tqdm import tqdm38 39import flax40import jax41import jax.numpy as jnp42import optax43from flax import jax_utils, traverse_util44from flax.training import train_state45from flax.training.common_utils import get_metrics, onehot, shard46from transformers import (47    CONFIG_MAPPING,48    FLAX_MODEL_FOR_MASKED_LM_MAPPING,49    AutoConfig,50    AutoTokenizer,51    FlaxAutoModelForMaskedLM,52    HfArgumentParser,53    PreTrainedTokenizerBase,54    TensorType,55    TrainingArguments,56    is_tensorboard_available,57    set_seed,58)59 60 61#if datasets.__version__ <= "1.8.0":62#    raise ValueError("Make sure to upgrade `datasets` to a version >= 1.9.0 to use dataset streaming")63 64 65MODEL_CONFIG_CLASSES = list(FLAX_MODEL_FOR_MASKED_LM_MAPPING.keys())66MODEL_TYPES = tuple(conf.model_type for conf in MODEL_CONFIG_CLASSES)67 68 69@dataclass70class ModelArguments:71    """72    Arguments pertaining to which model/config/tokenizer we are going to fine-tune, or train from scratch.73    """74 75    model_name_or_path: Optional[str] = field(76        default=None,77        metadata={78            "help": "The model checkpoint for weights initialization."79            "Don't set if you want to train a model from scratch."80        },81    )82    model_type: Optional[str] = field(83        default=None,84        metadata={"help": "If training from scratch, pass a model type from the list: " + ", ".join(MODEL_TYPES)},85    )86    config_name: Optional[str] = field(87        default=None, metadata={"help": "Pretrained config name or path if not the same as model_name"}88    )89    tokenizer_name: Optional[str] = field(90        default=None, metadata={"help": "Pretrained tokenizer name or path if not the same as model_name"}91    )92    cache_dir: Optional[str] = field(93        default=None, metadata={"help": "Where do you want to store the pretrained models downloaded from s3"}94    )95    use_fast_tokenizer: bool = field(96        default=True,97        metadata={"help": "Whether to use one of the fast tokenizer (backed by the tokenizers library) or not."},98    )99    dtype: Optional[str] = field(100        default="float32",101        metadata={102            "help": "Floating-point format in which the model weights should be initialized and trained. Choose one of `[float32, float16, bfloat16]`."103        },104    )105 106 107@dataclass108class DataTrainingArguments:109    """110    Arguments pertaining to what data we are going to input our model for training and eval.111    """112 113    dataset_name: Optional[str] = field(114        default=None, metadata={"help": "The name of the dataset to use (via the datasets library)."}115    )116    dataset_config_name: Optional[str] = field(117        default=None, metadata={"help": "The configuration name of the dataset to use (via the datasets library)."}118    )119    train_file: Optional[str] = field(default=None, metadata={"help": "The input training data file (a text file)."})120    validation_file: Optional[str] = field(121        default=None,122        metadata={"help": "An optional input evaluation data file to evaluate the perplexity on (a text file)."},123    )124    train_ref_file: Optional[str] = field(125        default=None,126        metadata={"help": "An optional input train ref data file for whole word masking in Chinese."},127    )128    validation_ref_file: Optional[str] = field(129        default=None,130        metadata={"help": "An optional input validation ref data file for whole word masking in Chinese."},131    )132    auth_token: bool = field(133        default=False, metadata={"help": "Use authorisation token"}134    )135    overwrite_cache: bool = field(136        default=False, metadata={"help": "Overwrite the cached training and evaluation sets"}137    )138    validation_split_percentage: Optional[int] = field(139        default=5,140        metadata={141            "help": "The percentage of the train set used as validation set in case there's no validation split"142        },143    )144    max_seq_length: Optional[int] = field(145        default=None,146        metadata={147            "help": "The maximum total input sequence length after tokenization. Sequences longer "148            "than this will be truncated. Default to the max input length of the model."149        },150    )151    preprocessing_num_workers: Optional[int] = field(152        default=None,153        metadata={"help": "The number of processes to use for the preprocessing."},154    )155    mlm_probability: float = field(156        default=0.15, metadata={"help": "Ratio of tokens to mask for masked language modeling loss"}157    )158    pad_to_max_length: bool = field(159        default=False,160        metadata={161            "help": "Whether to pad all samples to `max_seq_length`. "162            "If False, will pad the samples dynamically when batching to the maximum length in the batch."163        },164    )165    line_by_line: bool = field(166        default=False,167        metadata={"help": "Whether distinct lines of text in the dataset are to be handled as distinct sequences."},168    )169    text_column_name: str = field(170        default="text", metadata={"help": "The name of the column to retrieve the training text."}171    )172    shuffle_buffer_size: int = field(173        default=10000, metadata={"help": "The number of examples to pre-load for shuffling."}174    )175    num_train_steps: int = field(default=50000, metadata={"help": "The number of training steps."})176    num_eval_samples: int = field(default=50000, metadata={"help": "The number of samples to be used for evaluation"})177 178    def __post_init__(self):179        if self.dataset_name is None and self.train_file is None and self.validation_file is None:180            raise ValueError("Need either a dataset name or a training/validation file.")181        else:182            if self.train_file is not None:183                extension = self.train_file.split(".")[-1]184                assert extension in ["csv", "json", "txt"], "`train_file` should be a csv, a json or a txt file."185            if self.validation_file is not None:186                extension = self.validation_file.split(".")[-1]187                assert extension in ["csv", "json", "txt"], "`validation_file` should be a csv, a json or a txt file."188 189 190@flax.struct.dataclass191class FlaxDataCollatorForLanguageModeling:192    """193    Data collator used for language modeling. Inputs are dynamically padded to the maximum length of a batch if they194    are not all of the same length.195 196    Args:197        tokenizer (:class:`~transformers.PreTrainedTokenizer` or :class:`~transformers.PreTrainedTokenizerFast`):198            The tokenizer used for encoding the data.199        mlm_probability (:obj:`float`, `optional`, defaults to 0.15):200            The probability with which to (randomly) mask tokens in the input.201 202    .. note::203 204        For best performance, this data collator should be used with a dataset having items that are dictionaries or205        BatchEncoding, with the :obj:`"special_tokens_mask"` key, as returned by a206        :class:`~transformers.PreTrainedTokenizer` or a :class:`~transformers.PreTrainedTokenizerFast` with the207        argument :obj:`return_special_tokens_mask=True`.208    """209 210    tokenizer: PreTrainedTokenizerBase211    mlm_probability: float = 0.15212 213    def __post_init__(self):214        if self.tokenizer.mask_token is None:215            raise ValueError(216                "This tokenizer does not have a mask token which is necessary for masked language modeling. "217                "You should pass `mlm=False` to train on causal language modeling instead."218            )219 220    def __call__(self, examples: List[Dict[str, np.ndarray]]) -> Dict[str, np.ndarray]:221        # Handle dict or lists with proper padding and conversion to tensor.222        batch = self.tokenizer.pad(examples, return_tensors=TensorType.NUMPY)223 224        # If special token mask has been preprocessed, pop it from the dict.225        special_tokens_mask = batch.pop("special_tokens_mask", None)226 227        batch["input_ids"], batch["labels"] = self.mask_tokens(228            batch["input_ids"], special_tokens_mask=special_tokens_mask229        )230        return batch231 232    def mask_tokens(233        self, inputs: np.ndarray, special_tokens_mask: Optional[np.ndarray]234    ) -> Tuple[jnp.ndarray, jnp.ndarray]:235        """236        Prepare masked tokens inputs/labels for masked language modeling: 80% MASK, 10% random, 10% original.237        """238        labels = inputs.copy()239        # We sample a few tokens in each sequence for MLM training (with probability `self.mlm_probability`)240        probability_matrix = np.full(labels.shape, self.mlm_probability)241        special_tokens_mask = special_tokens_mask.astype("bool")242 243        probability_matrix[special_tokens_mask] = 0.0244        masked_indices = np.random.binomial(1, probability_matrix).astype("bool")245        labels[~masked_indices] = -100  # We only compute loss on masked tokens246 247        # 80% of the time, we replace masked input tokens with tokenizer.mask_token ([MASK])248        indices_replaced = np.random.binomial(1, np.full(labels.shape, 0.8)).astype("bool") & masked_indices249        inputs[indices_replaced] = self.tokenizer.convert_tokens_to_ids(self.tokenizer.mask_token)250 251        # 10% of the time, we replace masked input tokens with random word252        indices_random = np.random.binomial(1, np.full(labels.shape, 0.5)).astype("bool")253        indices_random &= masked_indices & ~indices_replaced254 255        random_words = np.random.randint(self.tokenizer.vocab_size, size=labels.shape, dtype="i4")256        inputs[indices_random] = random_words[indices_random]257 258        # The rest of the time (10% of the time) we keep the masked input tokens unchanged259        return inputs, labels260 261 262def generate_batch_splits(samples_idx: jnp.ndarray, batch_size: int) -> jnp.ndarray:263    num_samples = len(samples_idx)264    samples_to_remove = num_samples % batch_size265 266    if samples_to_remove != 0:267        samples_idx = samples_idx[:-samples_to_remove]268    sections_split = num_samples // batch_size269    batch_idx = np.split(samples_idx, sections_split)270    return batch_idx271 272 273def advance_iter_and_group_samples(train_iterator, num_samples, max_seq_length):274    """275    The training iterator is advanced so that after groupifying the samples,276    `num_samples` of length `max_seq_length` are returned.277    """278    num_total_tokens = max_seq_length * num_samples279    samples = defaultdict(list)280 281    i = 0282    while i < num_total_tokens:283        tokenized_samples = next(train_iterator)284        i += len(tokenized_samples["input_ids"])285 286        # concatenate tokenized samples to list287        samples = {k: samples[k] + tokenized_samples[k] for k in tokenized_samples.keys()}288 289    # Concatenated tokens are split to lists of length `max_seq_length`.290    # Note that remainedr of % max_seq_length are thrown away.291    def group_texts(examples):292        result = {293            k: [t[i : i + max_seq_length] for i in range(0, num_total_tokens, max_seq_length)]294            for k, t in examples.items()295        }296        return result297 298    grouped_samples = group_texts(samples)299    return grouped_samples300 301 302def write_train_metric(summary_writer, train_metrics, train_time, step):303    summary_writer.scalar("train_time", train_time, step)304 305    train_metrics = get_metrics(train_metrics)306    for key, vals in train_metrics.items():307        tag = f"train_{key}"308        for i, val in enumerate(vals):309            summary_writer.scalar(tag, val, step - len(vals) + i + 1)310 311 312def write_eval_metric(summary_writer, eval_metrics, step):313    for metric_name, value in eval_metrics.items():314        summary_writer.scalar(f"eval_{metric_name}", value, step)315 316 317if __name__ == "__main__":318    # See all possible arguments in src/transformers/training_args.py319    # or by passing the --help flag to this script.320    # We now keep distinct sets of args, for a cleaner separation of concerns.321 322    parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments))323    if len(sys.argv) == 2 and sys.argv[1].endswith(".json"):324        # If we pass only one argument to the script and it's the path to a json file,325        # let's parse it to get our arguments.326        model_args, data_args, training_args = parser.parse_json_file(json_file=os.path.abspath(sys.argv[1]))327    else:328        model_args, data_args, training_args = parser.parse_args_into_dataclasses()329 330    if (331        os.path.exists(training_args.output_dir)332        and os.listdir(training_args.output_dir)333        and training_args.do_train334        and not training_args.overwrite_output_dir335    ):336        raise ValueError(337            f"Output directory ({training_args.output_dir}) already exists and is not empty."338            "Use --overwrite_output_dir to overcome."339        )340 341    # Setup logging342    logging.basicConfig(343        format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",344        level="INFO",345        datefmt="[%X]",346    )347 348    # Log on each process the small summary:349    logger = logging.getLogger(__name__)350    #logger.warning(351    #    f"Process rank: {training_args.local_rank}, device: {training_args.device}, n_gpu: {training_args.n_gpu}"352    #    + f"distributed training: {bool(training_args.local_rank != -1)}, 16-bits training: {training_args.fp16}"353    #)354 355    # Set the verbosity to info of the Transformers logger (on main process only):356    logger.info(f"Training/evaluation parameters {training_args}")357 358    # Set seed before initializing model.359    set_seed(training_args.seed)360 361    # Get the datasets: you can either provide your own CSV/JSON/TXT training and evaluation files (see below)362    # or just provide the name of one of the public datasets available on the hub at https://huggingface.co/datasets/363    # (the dataset will be downloaded automatically from the datasets Hub).364    #365    # For CSV/JSON files, this script will use the column called 'text' or the first column if no column called366    # 'text' is found. You can easily tweak this behavior (see below).367    368    if data_args.dataset_name is not None:369        # Downloading and loading a dataset from the hub.370        dataset = load_dataset(371            data_args.dataset_name,372            data_args.dataset_config_name,373            cache_dir=model_args.cache_dir,374            streaming=True,375            use_auth_token=data_args.auth_token,376            split="train",377        )378 379    if model_args.config_name:380        config = AutoConfig.from_pretrained(model_args.config_name, cache_dir=model_args.cache_dir)381    elif model_args.model_name_or_path:382        config = AutoConfig.from_pretrained(model_args.model_name_or_path, cache_dir=model_args.cache_dir)383    else:384        config = CONFIG_MAPPING[model_args.model_type]()385        logger.warning("You are instantiating a new config instance from scratch.")386 387    if model_args.tokenizer_name:388        tokenizer = AutoTokenizer.from_pretrained(389            model_args.tokenizer_name, cache_dir=model_args.cache_dir, use_fast=model_args.use_fast_tokenizer390        )391    elif model_args.model_name_or_path:392        tokenizer = AutoTokenizer.from_pretrained(393            model_args.model_name_or_path, cache_dir=model_args.cache_dir, use_fast=model_args.use_fast_tokenizer394        )395    else:396        raise ValueError(397            "You are instantiating a new tokenizer from scratch. This is not supported by this script."398            "You can do it from another script, save it, and load it from here, using --tokenizer_name."399        )400 401    # Otherwise, we tokenize every text, then concatenate them together before splitting them in smaller parts.402    # We use `return_special_tokens_mask=True` because DataCollatorForLanguageModeling (see below) is more403    # efficient when it receives the `special_tokens_mask`.404    def tokenize_function(examples):405        return tokenizer(examples[data_args.text_column_name], return_special_tokens_mask=True)406 407    tokenized_datasets = dataset.map(408        tokenize_function,409        batched=True,410    )411 412    shuffle_seed = training_args.seed413    tokenized_datasets = tokenized_datasets.shuffle(buffer_size=data_args.shuffle_buffer_size, seed=shuffle_seed)414 415    has_tensorboard = is_tensorboard_available()416    if has_tensorboard and jax.process_index() == 0:417        try:418            from flax.metrics.tensorboard import SummaryWriter419        except ImportError as ie:420            has_tensorboard = False421            logger.warning(422                f"Unable to display metrics through TensorBoard because some package are not installed: {ie}"423            )424 425        summary_writer = SummaryWriter(log_dir=Path(training_args.output_dir))426 427    # Data collator428    # This one will take care of randomly masking the tokens.429    data_collator = FlaxDataCollatorForLanguageModeling(tokenizer=tokenizer, mlm_probability=data_args.mlm_probability)430 431    # Initialize our training432    rng = jax.random.PRNGKey(training_args.seed)433    dropout_rngs = jax.random.split(rng, jax.local_device_count())434 435    if model_args.model_name_or_path:436        model = FlaxAutoModelForMaskedLM.from_pretrained(437            model_args.model_name_or_path, config=config, seed=training_args.seed, dtype=getattr(jnp, model_args.dtype)438        )439    else:440        model = FlaxAutoModelForMaskedLM.from_config(441            config, seed=training_args.seed, dtype=getattr(jnp, model_args.dtype)442        )443 444    # Store some constant445    num_epochs = int(training_args.num_train_epochs)446    train_batch_size = int(training_args.per_device_train_batch_size) * jax.device_count()447    eval_batch_size = int(training_args.per_device_eval_batch_size) * jax.device_count()448 449    # define number steps per stream epoch450    num_train_steps = data_args.num_train_steps451 452    # Create learning rate schedule453    warmup_fn = optax.linear_schedule(454        init_value=0.0, end_value=training_args.learning_rate, transition_steps=training_args.warmup_steps455    )456    decay_fn = optax.linear_schedule(457        init_value=training_args.learning_rate,458        end_value=0,459        transition_steps=num_train_steps - training_args.warmup_steps,460    )461    linear_decay_lr_schedule_fn = optax.join_schedules(462        schedules=[warmup_fn, decay_fn], boundaries=[training_args.warmup_steps]463    )464 465    # We use Optax's "masking" functionality to not apply weight decay466    # to bias and LayerNorm scale parameters. decay_mask_fn returns a467    # mask boolean with the same structure as the parameters.468    # The mask is True for parameters that should be decayed.469    # Note that this mask is specifically adapted for FlaxBERT-like models.470    # For other models, one should correct the layer norm parameter naming471    # accordingly.472    def decay_mask_fn(params):473        flat_params = traverse_util.flatten_dict(params)474        flat_mask = {path: (path[-1] != "bias" and path[-2:] != ("LayerNorm", "scale")) for path in flat_params}475        return traverse_util.unflatten_dict(flat_mask)476 477 478 479    #te adam optimizer480    if training_args.adafactor:481        # We use the default parameters here to initialize adafactor,482        # For more details about the parameters please check https://github.com/deepmind/optax/blob/ed02befef9bf81cbbf236be3d2b0e032e9ed4a40/optax/_src/alias.py#L74483        optimizer = optax.adafactor(484            learning_rate=linear_decay_lr_schedule_fn,485        )486    else:487        optimizer = optax.adamw(488            learning_rate=linear_decay_lr_schedule_fn,489            b1=training_args.adam_beta1,490            b2=training_args.adam_beta2,491            eps=training_args.adam_epsilon,492            weight_decay=training_args.weight_decay,493            mask=decay_mask_fn,494        )495 496    # Setup train state497    state = train_state.TrainState.create(apply_fn=model.__call__, params=model.params, tx=optimizer)498 499    # Define gradient update step fn500    def train_step(state, batch, dropout_rng):501        dropout_rng, new_dropout_rng = jax.random.split(dropout_rng)502 503        def loss_fn(params):504            labels = batch.pop("labels")505 506            logits = state.apply_fn(**batch, params=params, dropout_rng=dropout_rng, train=True)[0]507 508            # compute loss, ignore padded input tokens509            label_mask = jnp.where(labels > 0, 1.0, 0.0)510            loss = optax.softmax_cross_entropy(logits, onehot(labels, logits.shape[-1])) * label_mask511 512            # take average513            loss = loss.sum() / label_mask.sum()514 515            return loss516 517        grad_fn = jax.value_and_grad(loss_fn)518        loss, grad = grad_fn(state.params)519        grad = jax.lax.pmean(grad, "batch")520        new_state = state.apply_gradients(grads=grad)521 522        metrics = jax.lax.pmean(523            {"loss": loss, "learning_rate": linear_decay_lr_schedule_fn(state.step)}, axis_name="batch"524        )525 526        return new_state, metrics, new_dropout_rng527 528    # Create parallel version of the train step529    p_train_step = jax.pmap(train_step, "batch", donate_argnums=(0,))530 531    # Define eval fn532    def eval_step(params, batch):533        labels = batch.pop("labels")534 535        logits = model(**batch, params=params, train=False)[0]536 537        # compute loss, ignore padded input tokens538        label_mask = jnp.where(labels > 0, 1.0, 0.0)539        loss = optax.softmax_cross_entropy(logits, onehot(labels, logits.shape[-1])) * label_mask540 541        # compute accuracy542        accuracy = jnp.equal(jnp.argmax(logits, axis=-1), labels) * label_mask543 544        # summarize metrics545        metrics = {"loss": loss.sum(), "accuracy": accuracy.sum(), "normalizer": label_mask.sum()}546        metrics = jax.lax.psum(metrics, axis_name="batch")547 548        return metrics549 550    p_eval_step = jax.pmap(eval_step, "batch", donate_argnums=(0,))551 552    # Replicate the train state on each device553    state = jax_utils.replicate(state)554 555    train_time = 0556    train_start = time.time()557    train_metrics = []558    eval_metrics = []559 560    training_iter = iter(tokenized_datasets)561 562    max_seq_length = min(data_args.max_seq_length, tokenizer.model_max_length)563    eval_samples = advance_iter_and_group_samples(training_iter, data_args.num_eval_samples, max_seq_length)564 565    steps = tqdm(range(num_train_steps), desc="Training...", position=0)566    for step in range(num_train_steps):567        # ======================== Training ================================568        try:569            samples = advance_iter_and_group_samples(training_iter, train_batch_size, max_seq_length)570        except StopIteration:571            # Once the end of the dataset stream is reached, the training iterator572            # is reinitialized and reshuffled and a new eval dataset is randomely chosen.573            shuffle_seed += 1574            tokenized_datasets.set_epoch(shuffle_seed)575 576            training_iter = iter(tokenized_datasets)577 578            eval_dataset = advance_iter_and_group_samples(training_iter, data_args.num_eval_samples, max_seq_length)579            samples = advance_iter_and_group_samples(training_iter, train_batch_size, max_seq_length)580 581        # process input samples582        model_inputs = data_collator(samples)583 584        # Model forward585        model_inputs = shard(model_inputs.data)586        state, train_metric, dropout_rngs = p_train_step(state, model_inputs, dropout_rngs)587 588        train_metrics.append(train_metric)589 590        if step % training_args.logging_steps == 0 and step > 0:591            steps.write(592                f"Step... ({step} | Loss: {train_metric['loss'].mean()}, Learning Rate: {train_metric['learning_rate'].mean()})"593            )594            train_time += time.time() - train_start595            if has_tensorboard and jax.process_index() == 0:596                write_train_metric(summary_writer, train_metrics, train_time, step)597            train_metrics = []598 599        # ======================== Evaluating ==============================600        if step % training_args.eval_steps == 0 and step > 0:601            eval_samples_idx = jnp.arange(data_args.num_eval_samples)602            eval_batch_idx = generate_batch_splits(eval_samples_idx, eval_batch_size)603 604            for i, batch_idx in enumerate(tqdm(eval_batch_idx, desc="Evaluating ...", position=1)):605                # process input samples606                batch_eval_samples = {k: [v[idx] for idx in batch_idx] for k, v in eval_samples.items()}607                model_inputs = data_collator(batch_eval_samples)608 609                # Model forward610                model_inputs = shard(model_inputs.data)611                metrics = p_eval_step(state.params, model_inputs)612                eval_metrics.append(metrics)613 614            # normalize eval metrics615            eval_metrics = get_metrics(eval_metrics)616            eval_metrics = jax.tree_map(jnp.sum, eval_metrics)617            eval_normalizer = eval_metrics.pop("normalizer")618            eval_metrics = jax.tree_map(lambda x: x / eval_normalizer, eval_metrics)619 620            # Update progress bar621            steps.desc = f"Step... ({step + 1}/{num_train_steps} | Loss: {eval_metrics['loss']}, Acc: {eval_metrics['accuracy']})"622 623            if has_tensorboard and jax.process_index() == 0:624                write_eval_metric(summary_writer, eval_metrics, step)625            eval_metrics = []626 627            # save checkpoint after each epoch and push checkpoint to the hub628            if jax.process_index() == 0:629                params = jax.device_get(jax.tree_map(lambda x: x[0], state.params))630                model.save_pretrained(631                    training_args.output_dir,632                    params=params,633                    push_to_hub=training_args.push_to_hub,634                    commit_message=f"Saving weights and logs of step {step+1}",635                )636 637        # update tqdm bar638        steps.update(1)639