pere commited on
Commit
5008784
·
1 Parent(s): 36ed005

Saving weights and logs of epoch 1

Browse files
.gitattributes CHANGED
@@ -25,3 +25,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
25
  *.zip filter=lfs diff=lfs merge=lfs -text
26
  *.zstandard filter=lfs diff=lfs merge=lfs -text
27
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
25
  *.zip filter=lfs diff=lfs merge=lfs -text
26
  *.zstandard filter=lfs diff=lfs merge=lfs -text
27
  *tfevents* filter=lfs diff=lfs merge=lfs -text
28
+ *model* filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!---
2
+ Copyright 2021 The HuggingFace Team. All rights reserved.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ -->
16
+
17
+ # Vision-Text dual encoder model training examples
18
+
19
+ > Note: This example is experimental and might not give the best possible results
20
+
21
+ The following example showcases how to train a CLIP like vision-text dual encoder model
22
+ using a pre-trained vision and text encoder using the JAX/Flax backend.
23
+
24
+ Such a model can be used for natural language image search and potentially zero-shot image classification.
25
+ The model is inspired by the [CLIP](https://openai.com/blog/clip/) approach, introduced by Alec Radford et al.
26
+ The idea is to train a vision encoder and a text encoder jointly to project the representation of images and their
27
+ captions into the same embedding space, such that the caption embeddings are located near the embeddings
28
+ of the images they describe.
29
+
30
+ JAX/Flax allows you to trace pure functions and compile them into efficient, fused accelerator code on both GPU and TPU.
31
+ Models written in JAX/Flax are **immutable** and updated in a purely functional
32
+ way which enables simple and efficient model parallelism.
33
+
34
+ In this example we will use the vision model from [CLIP](https://huggingface.co/models?filter=clip)
35
+ as the image encoder and [`roberta-base`](https://huggingface.co/roberta-base) as the text encoder.
36
+ Note that one can also use the [ViT](https://huggingface.co/models?filter=vit) model as image encoder and any other BERT or ROBERTa model as text encoder.
37
+ To train the model on languages other than English one should choose a text encoder trained on the desired
38
+ language and a image-text dataset in that language. One such dataset is [WIT](https://github.com/google-research-datasets/wit).
39
+
40
+ Let's start by creating a model repository to save the trained model and logs.
41
+ Here we call the model `"clip-roberta-base"`, but you can change the model name as you like.
42
+
43
+ You can do this either directly on [huggingface.co](https://huggingface.co/new) (assuming that
44
+ you are logged in) or via the command line:
45
+
46
+ ```
47
+ huggingface-cli repo create clip-roberta-base
48
+ ```
49
+ Next we clone the model repository to add the tokenizer and model files.
50
+ ```
51
+ git clone https://huggingface.co/<your-username>/clip-roberta-base
52
+ ```
53
+ To ensure that all tensorboard traces will be uploaded correctly, we need to
54
+ track them. You can run the following command inside your model repo to do so.
55
+
56
+ ```
57
+ cd clip-roberta-base
58
+ git lfs track "*tfevents*"
59
+ ```
60
+
61
+ Great, we have set up our model repository. During training, we will automatically
62
+ push the training logs and model weights to the repo.
63
+
64
+ Next, let's add a symbolic link to the `run_hybrid_clip.py`.
65
+
66
+ ```bash
67
+ export MODEL_DIR="./clip-roberta-base
68
+ ln -s ~/transformers/examples/research_projects/jax-projects/hybrid_clip/run_hybrid_clip.py run_hybrid_clip.py
69
+ ```
70
+
71
+ ## How to use the `FlaxHybridCLIP` model:
72
+
73
+ The `FlaxHybridCLIP` class let's you load any text and vision encoder model to create a dual encoder.
74
+ Here is an example of how to load the model using pre-trained text and vision models.
75
+
76
+ ```python
77
+ from modeling_hybrid_clip import FlaxHybridCLIP
78
+
79
+ model = FlaxHybridCLIP.from_text_vision_pretrained("bert-base-uncased", "openai/clip-vit-base-patch32")
80
+
81
+ # save the model
82
+ model.save_pretrained("bert-clip")
83
+
84
+ # load the saved model
85
+ model = FlaxHybridCLIP.from_pretrained("bert-clip")
86
+ ```
87
+
88
+ If the checkpoints are in PyTorch then one could pass `text_from_pt=True` and `vision_from_pt=True`. This will load the model
89
+ PyTorch checkpoints convert them to flax and load the model.
90
+
91
+ ```python
92
+ model = FlaxHybridCLIP.from_text_vision_pretrained("bert-base-uncased", "openai/clip-vit-base-patch32", text_from_pt=True, vision_from_pt=True)
93
+ ```
94
+
95
+ This loads both the text and vision encoders using pre-trained weights, the projection layers are randomly
96
+ initialized except for CLIP's vision model. If you use CLIP to initialize the vision model then the vision projection weights are also
97
+ loaded using the pre-trained weights.
98
+
99
+ ## Prepare the dataset
100
+
101
+ We will use the MS-COCO dataset to train our dual encoder model. MS-COCO contains over 82,000 images, each of which has at least 5 different caption annotations. The dataset is usually used for image captioning tasks, but we can repurpose the image-caption pairs to train our dual encoder model for image search.
102
+
103
+ ### Download and extract the data.
104
+
105
+ It consists of two compressed folders: one with images, and the other—with associated image captions. Note that the compressed images folder is 13GB in size.
106
+
107
+ ```bash
108
+ wget http://images.cocodataset.org/annotations/annotations_trainval2014.zip
109
+ wget http://images.cocodataset.org/zips/train2014.zip
110
+
111
+ unzip annotations_trainval2014.zip
112
+ unzip train2014.zip
113
+
114
+ mkdir coco_dataset
115
+ mv train2014 coco_dataset/
116
+ mv annotations coco_dataset/
117
+ ```
118
+
119
+ ### Prepare dataset files and split the dataset.
120
+
121
+ ```python
122
+ import json
123
+ import collections
124
+
125
+ images_dir = "coco_dataset/train2014"
126
+ annotation_file = "coco_dataset/annotations/captions_train2014.json"
127
+ with open(annotation_file, "r") as f:
128
+ annotations = json.load(f)["annotations"]
129
+
130
+ image_path_to_caption = collections.defaultdict(list)
131
+ for element in annotations:
132
+ caption = f"{element['caption'].lower().rstrip('.')}"
133
+ image_path = images_dir + "/COCO_train2014_" + "%012d.jpg" % (element["image_id"])
134
+ image_path_to_caption[image_path].append(caption)
135
+
136
+ lines = []
137
+ for image_path, captions in image_path_to_caption.items():
138
+ lines.append(json.dumps({"image_path": image_path, "captions": captions}))
139
+
140
+ train_lines = lines[:-8000]
141
+ valid_line = lines[-8000:]
142
+ with open("coco_dataset/train_dataset.json", "w") as f:
143
+ f.write("\n".join(train_lines))
144
+
145
+ with open("coco_dataset/valid_dataset.json", "w") as f:
146
+ f.write("\n".join(valid_line))
147
+ ```
148
+
149
+ > Note: The data loading and processing part of this script can still be improved for maximum performance. In particular one should decode the images beforehand and use those instead decoding them each time. If the dataset is small or if you have huge disk space the you could also pre-process all the dataset beforehand and then use it.
150
+
151
+ ## Train the model
152
+ Next we can run the example script to train the model:
153
+
154
+ ```bash
155
+ python run_hybrid_clip.py \
156
+ --output_dir ${MODEL_DIR} \
157
+ --text_model_name_or_path="roberta-base" \
158
+ --vision_model_name_or_path="openai/clip-vit-base-patch32" \
159
+ --tokenizer_name="roberta-base" \
160
+ --train_file="coco_dataset/train_dataset.json" \
161
+ --validation_file="coco_dataset/validation_dataset.json" \
162
+ --do_train --do_eval \
163
+ --num_train_epochs="40" --max_seq_length 96 \
164
+ --per_device_train_batch_size="64" \
165
+ --per_device_eval_batch_size="64" \
166
+ --learning_rate="5e-5" --warmup_steps="0" --weight_decay 0.1 \
167
+ --overwrite_output_dir \
168
+ --preprocessing_num_workers 32 \
169
+ --push_to_hub
170
+ ```
171
+
172
+ This should finish in ~1h50 mins with min validation loss 2.43. Training statistics can be accessed on [tfhub.de](https://tensorboard.dev/experiment/RUNPYd1yRgSD5kZSb9hDig/#scalars)
__pycache__/configuration_hybrid_clip.cpython-38.pyc ADDED
Binary file (4.28 kB). View file
 
__pycache__/modeling_hybrid_clip.cpython-38.pyc ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f193a46cc81ce5500e6975eccd549cc24f732fb8d149cceee00fee8eff25752e
3
+ size 12949
config.json ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "HybridCLIP"
4
+ ],
5
+ "initializer_factor": 1.0,
6
+ "model_type": "hybrid-clip",
7
+ "projection_dim": 512,
8
+ "seed": 42,
9
+ "text_config": {
10
+ "_name_or_path": "",
11
+ "add_cross_attention": false,
12
+ "architectures": [
13
+ "RobertaForMaskedLM"
14
+ ],
15
+ "attention_probs_dropout_prob": 0.1,
16
+ "bad_words_ids": null,
17
+ "bos_token_id": 0,
18
+ "chunk_size_feed_forward": 0,
19
+ "classifier_dropout": null,
20
+ "decoder_start_token_id": null,
21
+ "diversity_penalty": 0.0,
22
+ "do_sample": false,
23
+ "early_stopping": false,
24
+ "encoder_no_repeat_ngram_size": 0,
25
+ "eos_token_id": 2,
26
+ "finetuning_task": null,
27
+ "forced_bos_token_id": null,
28
+ "forced_eos_token_id": null,
29
+ "gradient_checkpointing": false,
30
+ "hidden_act": "gelu",
31
+ "hidden_dropout_prob": 0.1,
32
+ "hidden_size": 768,
33
+ "id2label": {
34
+ "0": "LABEL_0",
35
+ "1": "LABEL_1"
36
+ },
37
+ "initializer_range": 0.02,
38
+ "intermediate_size": 3072,
39
+ "is_decoder": false,
40
+ "is_encoder_decoder": false,
41
+ "label2id": {
42
+ "LABEL_0": 0,
43
+ "LABEL_1": 1
44
+ },
45
+ "layer_norm_eps": 1e-05,
46
+ "length_penalty": 1.0,
47
+ "max_length": 20,
48
+ "max_position_embeddings": 514,
49
+ "min_length": 0,
50
+ "model_type": "roberta",
51
+ "no_repeat_ngram_size": 0,
52
+ "num_attention_heads": 12,
53
+ "num_beam_groups": 1,
54
+ "num_beams": 1,
55
+ "num_hidden_layers": 12,
56
+ "num_return_sequences": 1,
57
+ "output_attentions": false,
58
+ "output_hidden_states": false,
59
+ "output_scores": false,
60
+ "pad_token_id": 1,
61
+ "position_embedding_type": "absolute",
62
+ "prefix": null,
63
+ "problem_type": null,
64
+ "pruned_heads": {},
65
+ "remove_invalid_values": false,
66
+ "repetition_penalty": 1.0,
67
+ "return_dict": true,
68
+ "return_dict_in_generate": false,
69
+ "sep_token_id": null,
70
+ "task_specific_params": null,
71
+ "temperature": 1.0,
72
+ "tie_encoder_decoder": false,
73
+ "tie_word_embeddings": true,
74
+ "tokenizer_class": null,
75
+ "top_k": 50,
76
+ "top_p": 1.0,
77
+ "torch_dtype": null,
78
+ "torchscript": false,
79
+ "transformers_version": "4.11.0.dev0",
80
+ "type_vocab_size": 1,
81
+ "use_bfloat16": false,
82
+ "use_cache": true,
83
+ "vocab_size": 50265
84
+ },
85
+ "transformers_version": null,
86
+ "vision_config": {
87
+ "_name_or_path": "",
88
+ "add_cross_attention": false,
89
+ "architectures": null,
90
+ "attention_dropout": 0.0,
91
+ "bad_words_ids": null,
92
+ "bos_token_id": null,
93
+ "chunk_size_feed_forward": 0,
94
+ "decoder_start_token_id": null,
95
+ "diversity_penalty": 0.0,
96
+ "do_sample": false,
97
+ "dropout": 0.0,
98
+ "early_stopping": false,
99
+ "encoder_no_repeat_ngram_size": 0,
100
+ "eos_token_id": null,
101
+ "finetuning_task": null,
102
+ "forced_bos_token_id": null,
103
+ "forced_eos_token_id": null,
104
+ "gradient_checkpointing": false,
105
+ "hidden_act": "quick_gelu",
106
+ "hidden_size": 768,
107
+ "id2label": {
108
+ "0": "LABEL_0",
109
+ "1": "LABEL_1"
110
+ },
111
+ "image_size": 224,
112
+ "initializer_factor": 1.0,
113
+ "initializer_range": 0.02,
114
+ "intermediate_size": 3072,
115
+ "is_decoder": false,
116
+ "is_encoder_decoder": false,
117
+ "label2id": {
118
+ "LABEL_0": 0,
119
+ "LABEL_1": 1
120
+ },
121
+ "layer_norm_eps": 1e-05,
122
+ "length_penalty": 1.0,
123
+ "max_length": 20,
124
+ "min_length": 0,
125
+ "model_type": "clip_vision_model",
126
+ "no_repeat_ngram_size": 0,
127
+ "num_attention_heads": 12,
128
+ "num_beam_groups": 1,
129
+ "num_beams": 1,
130
+ "num_hidden_layers": 12,
131
+ "num_return_sequences": 1,
132
+ "output_attentions": false,
133
+ "output_hidden_states": false,
134
+ "output_scores": false,
135
+ "pad_token_id": null,
136
+ "patch_size": 32,
137
+ "prefix": null,
138
+ "problem_type": null,
139
+ "pruned_heads": {},
140
+ "remove_invalid_values": false,
141
+ "repetition_penalty": 1.0,
142
+ "return_dict": true,
143
+ "return_dict_in_generate": false,
144
+ "sep_token_id": null,
145
+ "task_specific_params": null,
146
+ "temperature": 1.0,
147
+ "tie_encoder_decoder": false,
148
+ "tie_word_embeddings": true,
149
+ "tokenizer_class": null,
150
+ "top_k": 50,
151
+ "top_p": 1.0,
152
+ "torch_dtype": null,
153
+ "torchscript": false,
154
+ "transformers_version": "4.11.0.dev0",
155
+ "use_bfloat16": false
156
+ }
157
+ }
configuration_hybrid_clip.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+
3
+ from transformers.configuration_utils import PretrainedConfig
4
+ from transformers.utils import logging
5
+
6
+
7
+ logger = logging.get_logger(__name__)
8
+
9
+
10
+ class HybridCLIPConfig(PretrainedConfig):
11
+ r"""
12
+ :class:`HybridCLIPConfig` is the configuration class to store the configuration of a
13
+ :class:`~HybridCLIPModel`. It is used to instantiate HybridCLIPModel model according to the specified arguments,
14
+ defining the text model and vision model configs.
15
+
16
+ Configuration objects inherit from :class:`~transformers.PretrainedConfig` and can be used to control the model
17
+ outputs. Read the documentation from :class:`~transformers.PretrainedConfig` for more information.
18
+
19
+ Args:
20
+ text_config_dict (:obj:`dict`):
21
+ Dictionary of configuration options that defines text model config.
22
+ vision_config_dict (:obj:`dict`):
23
+ Dictionary of configuration options that defines vison model config.
24
+ projection_dim (:obj:`int`, `optional`, defaults to 512):
25
+ Dimentionality of text and vision projection layers.
26
+ kwargs (`optional`):
27
+ Dictionary of keyword arguments.
28
+
29
+ Examples::
30
+
31
+ >>> from transformers import BertConfig, CLIPConfig, HybridCLIPConfig, FlaxHybridCLIP
32
+
33
+ >>> # Initializing a BERT and CLIP configuration
34
+ >>> config_text = BertConfig()
35
+ >>> config_vision = CLIPConfig()
36
+
37
+ >>> config = HybridCLIPConfig.from_text_vision_configs(config_text, config_vision, projection_dim=512)
38
+
39
+ >>> # Initializing a BERT and CLIPVision model
40
+ >>> model = EncoderDecoderModel(config=config)
41
+
42
+ >>> # Accessing the model configuration
43
+ >>> config_text = model.config.text_config
44
+ >>> config_vision = model.config.vision_config
45
+
46
+ >>> # Saving the model, including its configuration
47
+ >>> model.save_pretrained('my-model')
48
+
49
+ >>> # loading model and config from pretrained folder
50
+ >>> encoder_decoder_config = HybridCLIPConfig.from_pretrained('my-model')
51
+ >>> model = FlaxHybridCLIP.from_pretrained('my-model', config=encoder_decoder_config)
52
+ """
53
+
54
+ model_type = "hybrid-clip"
55
+ is_composition = True
56
+
57
+ def __init__(self, projection_dim=512, **kwargs):
58
+ super().__init__(**kwargs)
59
+
60
+ if "text_config" not in kwargs:
61
+ raise ValueError("`text_config` can not be `None`.")
62
+
63
+ if "vision_config" not in kwargs:
64
+ raise ValueError("`vision_config` can not be `None`.")
65
+
66
+ text_config = kwargs.pop("text_config")
67
+ vision_config = kwargs.pop("vision_config")
68
+
69
+ text_model_type = text_config.pop("model_type")
70
+ vision_model_type = vision_config.pop("model_type")
71
+
72
+ from transformers import AutoConfig
73
+
74
+ self.text_config = AutoConfig.for_model(text_model_type, **text_config)
75
+
76
+ if vision_model_type == "clip":
77
+ self.vision_config = AutoConfig.for_model(vision_model_type, **vision_config).vision_config
78
+ elif vision_model_type == "clip_vision_model":
79
+ from transformers import CLIPVisionConfig
80
+
81
+ self.vision_config = CLIPVisionConfig(**vision_config)
82
+ else:
83
+ self.vision_config = AutoConfig.for_model(vision_model_type, **vision_config)
84
+
85
+ self.projection_dim = projection_dim
86
+ self.initializer_factor = 1.0
87
+
88
+ @classmethod
89
+ def from_text_vision_configs(cls, text_config: PretrainedConfig, vision_config: PretrainedConfig, **kwargs):
90
+ r"""
91
+ Instantiate a :class:`HybridCLIPConfig` (or a derived class) from text model configuration and
92
+ vision model configuration.
93
+
94
+ Returns:
95
+ :class:`HybridCLIPConfig`: An instance of a configuration object
96
+ """
97
+
98
+ return cls(text_config=text_config.to_dict(), vision_config=vision_config.to_dict(), **kwargs)
99
+
100
+ def to_dict(self):
101
+ """
102
+ Serializes this instance to a Python dictionary. Override the default
103
+ :meth:`~transformers.PretrainedConfig.to_dict`.
104
+
105
+ Returns:
106
+ :obj:`Dict[str, any]`: Dictionary of all the attributes that make up this configuration instance,
107
+ """
108
+ output = copy.deepcopy(self.__dict__)
109
+ output["text_config"] = self.text_config.to_dict()
110
+ output["vision_config"] = self.vision_config.to_dict()
111
+ output["model_type"] = self.__class__.model_type
112
+ return output
flax_model.msgpack ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:03259afded272bf6c209f4346a796b9c7d57fc44db9f44750e3bee6a8528a3b4
3
+ size 851566424
logs/events.out.tfevents.1631220194.t1v-n-f6f5b6cc-w-0.33452.0.v2 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4a4831138bc80c188e3e1d0ac3809e543e5a8043df7eb975981b65ca0829e26d
3
+ size 47745
modeling_hybrid_clip.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7ded50b11d851c53afc7d73f62043cfcf04c8e49e11b3b6fc75f19e39dedd27c
3
+ size 18060
prepare_dataset_coco.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import collections
3
+
4
+ images_dir = "../coco_dataset/train2014"
5
+ annotation_file = "../coco_dataset/annotations/captions_train2014.json"
6
+ with open(annotation_file, "r") as f:
7
+ annotations = json.load(f)["annotations"]
8
+
9
+ image_path_to_caption = collections.defaultdict(list)
10
+ for element in annotations:
11
+ caption = f"{element['caption'].lower().rstrip('.')}"
12
+ image_path = images_dir + "/COCO_train2014_" + "%012d.jpg" % (element["image_id"])
13
+ image_path_to_caption[image_path].append(caption)
14
+
15
+ lines = []
16
+ for image_path, captions in image_path_to_caption.items():
17
+ lines.append(json.dumps({"image_path": image_path, "captions": captions}))
18
+
19
+ train_lines = lines[:-8000]
20
+ valid_line = lines[-8000:]
21
+ with open("../coco_dataset/train_dataset.json", "w") as f:
22
+ f.write("\n".join(train_lines))
23
+
24
+ with open("../coco_dataset/valid_dataset.json", "w") as f:
25
+ f.write("\n".join(valid_line))
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ jax>=0.2.8
2
+ jaxlib>=0.1.59
3
+ flax>=0.3.4
4
+ optax>=0.0.8
5
+ -f https://download.pytorch.org/whl/torch_stable.html
6
+ torch==1.9.0+cpu
7
+ -f https://download.pytorch.org/whl/torch_stable.html
8
+ torchvision==0.10.0+cpu
run.sh ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ python run_hybrid_clip.py \
2
+ --output_dir "./" \
3
+ --text_model_name_or_path="roberta-base" \
4
+ --vision_model_name_or_path="openai/clip-vit-base-patch32" \
5
+ --tokenizer_name="roberta-base" \
6
+ --train_file="../coco_dataset/train_dataset.json" \
7
+ --validation_file="../coco_dataset/valid_dataset.json" \
8
+ --do_train --do_eval \
9
+ --num_train_epochs="40" --max_seq_length 96 \
10
+ --per_device_train_batch_size="64" \
11
+ --per_device_eval_batch_size="64" \
12
+ --learning_rate="5e-5" --warmup_steps="0" --weight_decay 0.1 \
13
+ --overwrite_output_dir \
14
+ --preprocessing_num_workers 32 \
15
+ --push_to_hub
run_hybrid_clip.py ADDED
@@ -0,0 +1,565 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # coding=utf-8
3
+ # 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 at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # 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 and
15
+ # limitations under the License.
16
+ """
17
+ Training a CLIP like dual encoder models using text and vision encoders in the library.
18
+
19
+ The script can be used to train CLIP like models for languages other than english by using
20
+ a text encoder pre-trained in the desired language. Currently this script support the following vision
21
+ and text models:
22
+ Vision models: ViT(https://huggingface.co/models?filter=vit), CLIP (https://huggingface.co/models?filter=clip)
23
+ Text models: BERT, ROBERTa (https://huggingface.co/models?filter=masked-lm)
24
+ """
25
+
26
+ import json
27
+ import logging
28
+ import os
29
+ import sys
30
+ import time
31
+ from dataclasses import dataclass, field
32
+ from pathlib import Path
33
+ from typing import Callable, Optional
34
+
35
+ import torch
36
+ from torchvision.datasets import VisionDataset
37
+ from torchvision.io import ImageReadMode, read_image
38
+ from torchvision.transforms import CenterCrop, ConvertImageDtype, Normalize, Resize
39
+ from torchvision.transforms.functional import InterpolationMode
40
+ from tqdm import tqdm
41
+
42
+ import jax
43
+ import jax.numpy as jnp
44
+ import optax
45
+ import transformers
46
+ from flax import jax_utils
47
+ from flax.jax_utils import unreplicate
48
+ from flax.training import train_state
49
+ from flax.training.common_utils import get_metrics, shard, shard_prng_key
50
+ from modeling_hybrid_clip import FlaxHybridCLIP
51
+ from transformers import AutoTokenizer, HfArgumentParser, TrainingArguments, is_tensorboard_available, set_seed
52
+
53
+
54
+ logger = logging.getLogger(__name__)
55
+
56
+ # Cache the result
57
+ has_tensorboard = is_tensorboard_available()
58
+ if has_tensorboard:
59
+ try:
60
+ from flax.metrics.tensorboard import SummaryWriter
61
+ except ImportError as ie:
62
+ has_tensorboard = False
63
+ print(f"Unable to display metrics through TensorBoard because some package are not installed: {ie}")
64
+
65
+ else:
66
+ print(
67
+ "Unable to display metrics through TensorBoard because the package is not installed: "
68
+ "Please run pip install tensorboard to enable."
69
+ )
70
+
71
+
72
+ @dataclass
73
+ class ModelArguments:
74
+ """
75
+ Arguments pertaining to which model/config/tokenizer we are going to fine-tune, or train from scratch.
76
+ """
77
+
78
+ text_model_name_or_path: str = field(
79
+ metadata={
80
+ "help": "The text model checkpoint for weights initialization."
81
+ "Don't set if you want to train a model from scratch."
82
+ },
83
+ )
84
+ vision_model_name_or_path: str = field(
85
+ metadata={
86
+ "help": "The vision model checkpoint for weights initialization."
87
+ "Don't set if you want to train a model from scratch."
88
+ },
89
+ )
90
+ from_pt: bool = field(
91
+ default=True,
92
+ metadata={"help": "whether to load the text and vision model using PyTorch checkpoints."},
93
+ )
94
+ config_name: Optional[str] = field(
95
+ default=None, metadata={"help": "Pretrained config name or path if not the same as model_name"}
96
+ )
97
+ tokenizer_name: Optional[str] = field(
98
+ default=None, metadata={"help": "Pretrained tokenizer name or path if not the same as model_name"}
99
+ )
100
+ cache_dir: Optional[str] = field(
101
+ default=None, metadata={"help": "Where do you want to store the pretrained models downloaded from s3"}
102
+ )
103
+ use_fast_tokenizer: bool = field(
104
+ default=True,
105
+ metadata={"help": "Whether to use one of the fast tokenizer (backed by the tokenizers library) or not."},
106
+ )
107
+ dtype: Optional[str] = field(
108
+ default="float32",
109
+ metadata={
110
+ "help": "Floating-point format in which the model weights should be initialized and trained. Choose one of `[float32, float16, bfloat16]`."
111
+ },
112
+ )
113
+
114
+
115
+ @dataclass
116
+ class DataTrainingArguments:
117
+ """
118
+ Arguments pertaining to what data we are going to input our model for training and eval.
119
+ """
120
+
121
+ data_dir: Optional[str] = field(default=None, metadata={"help": "The data directory containing input files."})
122
+ train_file: Optional[str] = field(
123
+ default=None, metadata={"help": "The input training data file (a jsonlines file)."}
124
+ )
125
+ validation_file: Optional[str] = field(
126
+ default=None,
127
+ metadata={"help": "An optional input evaluation data file (a jsonlines file)."},
128
+ )
129
+ max_seq_length: Optional[int] = field(
130
+ default=72,
131
+ metadata={
132
+ "help": "The maximum total input sequence length after tokenization. Sequences longer "
133
+ "than this will be truncated, sequences shorter will be padded."
134
+ },
135
+ )
136
+ max_train_samples: Optional[int] = field(
137
+ default=None,
138
+ metadata={
139
+ "help": "For debugging purposes or quicker training, truncate the number of training examples to this "
140
+ "value if set."
141
+ },
142
+ )
143
+ max_eval_samples: Optional[int] = field(
144
+ default=None,
145
+ metadata={
146
+ "help": "For debugging purposes or quicker training, truncate the number of evaluation examples to this "
147
+ "value if set."
148
+ },
149
+ )
150
+ overwrite_cache: bool = field(
151
+ default=False, metadata={"help": "Overwrite the cached training and evaluation sets"}
152
+ )
153
+ overwrite_cache: bool = field(
154
+ default=False, metadata={"help": "Overwrite the cached training and evaluation sets"}
155
+ )
156
+ preprocessing_num_workers: Optional[int] = field(
157
+ default=None,
158
+ metadata={"help": "The number of processes to use for the preprocessing."},
159
+ )
160
+
161
+ def __post_init__(self):
162
+ if self.train_file is None and self.validation_file is None:
163
+ raise ValueError("Need either a dataset name or a training/validation file.")
164
+ else:
165
+ if self.train_file is not None:
166
+ extension = self.train_file.split(".")[-1]
167
+ assert extension == "json", "`train_file` should be a json file."
168
+ if self.validation_file is not None:
169
+ extension = self.validation_file.split(".")[-1]
170
+ assert extension == "json", "`validation_file` should be a json file."
171
+
172
+
173
+ # We use torchvision for faster image pre-processing.
174
+ # We need to ensure faster processing speed as it can become a bottleneck on TPU
175
+ class Transform(torch.nn.Module):
176
+ def __init__(self, image_size):
177
+ super().__init__()
178
+ self.transforms = torch.nn.Sequential(
179
+ Resize([image_size], interpolation=InterpolationMode.BICUBIC),
180
+ CenterCrop(image_size),
181
+ ConvertImageDtype(torch.float),
182
+ Normalize((0.48145466, 0.4578275, 0.40821073), (0.26862954, 0.26130258, 0.27577711)),
183
+ )
184
+
185
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
186
+ with torch.no_grad():
187
+ x = self.transforms(x)
188
+ return x
189
+
190
+
191
+ class ImageTextDataset(VisionDataset):
192
+ """
193
+ Dtaset for loading image-text data for tasks like CLIP training, Image Captioning.
194
+
195
+ Args:
196
+ root: (string): The root path where the dataset is stored
197
+ file_path: (string): Path to the file containing the image_paths and associated captions.
198
+ The expected format is jsonlines where each line is a json object containing to keys.
199
+ `image_path`: The path to the image.
200
+ `captions`: An `array` of captions.
201
+ transform (callable, optional): A function/transform that takes in an PIL image
202
+ and returns a transformed version. E.g, ``transforms.ToTensor``
203
+ target_transform (callable, optional): A function/transform that takes in the
204
+ target and transforms it.
205
+ transforms (callable, optional): A function/transform that takes input sample and its target as entry
206
+ and returns a transformed version.
207
+ """
208
+
209
+ def __init__(
210
+ self,
211
+ root: str,
212
+ file_path: str,
213
+ captions_per_image=2,
214
+ transform: Optional[Callable] = None,
215
+ target_transform: Optional[Callable] = None,
216
+ transforms: Optional[Callable] = None,
217
+ ):
218
+ super().__init__(root, transforms, transform, target_transform)
219
+
220
+ with open(file_path, "r") as f:
221
+ examples = [json.loads(line) for line in f.readlines()]
222
+
223
+ self.captions = []
224
+ self.image_paths = []
225
+
226
+ for example in examples:
227
+ captions_subset = example["captions"][:captions_per_image]
228
+ self.captions.extend(captions_subset)
229
+ self.image_paths.extend([example["image_path"]] * len(captions_subset))
230
+
231
+ def _load_image(self, idx: int):
232
+ path = self.image_paths[idx]
233
+ return read_image(path, mode=ImageReadMode.RGB)
234
+
235
+ def _load_target(self, idx):
236
+ return self.captions[idx]
237
+
238
+ def __getitem__(self, index: int):
239
+ image = self._load_image(index)
240
+ target = self._load_target(index)
241
+
242
+ if self.transforms is not None:
243
+ image, target = self.transforms(image, target)
244
+
245
+ return image, target
246
+
247
+ def __len__(self) -> int:
248
+ return len(self.captions)
249
+
250
+
251
+ class TrainState(train_state.TrainState):
252
+ dropout_rng: jnp.ndarray
253
+
254
+ def replicate(self):
255
+ return jax_utils.replicate(self).replace(dropout_rng=shard_prng_key(self.dropout_rng))
256
+
257
+
258
+ def write_metric(summary_writer, train_metrics, eval_metrics, train_time, step):
259
+ summary_writer.scalar("train_time", train_time, step)
260
+
261
+ train_metrics = get_metrics(train_metrics)
262
+ for key, vals in train_metrics.items():
263
+ tag = f"train_{key}"
264
+ for i, val in enumerate(vals):
265
+ summary_writer.scalar(tag, val, step - len(vals) + i + 1)
266
+
267
+ for metric_name, value in eval_metrics.items():
268
+ summary_writer.scalar(f"eval_{metric_name}", value, step)
269
+
270
+
271
+ def create_learning_rate_fn(
272
+ train_ds_size: int, train_batch_size: int, num_train_epochs: int, num_warmup_steps: int, learning_rate: float
273
+ ) -> Callable[[int], jnp.array]:
274
+ """Returns a linear warmup, linear_decay learning rate function."""
275
+ steps_per_epoch = train_ds_size // train_batch_size
276
+ num_train_steps = steps_per_epoch * num_train_epochs
277
+ warmup_fn = optax.linear_schedule(init_value=0.0, end_value=learning_rate, transition_steps=num_warmup_steps)
278
+ decay_fn = optax.linear_schedule(
279
+ init_value=learning_rate, end_value=0, transition_steps=num_train_steps - num_warmup_steps
280
+ )
281
+ schedule_fn = optax.join_schedules(schedules=[warmup_fn, decay_fn], boundaries=[num_warmup_steps])
282
+ return schedule_fn
283
+
284
+
285
+ def main():
286
+ parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments))
287
+ if len(sys.argv) == 2 and sys.argv[1].endswith(".json"):
288
+ # If we pass only one argument to the script and it's the path to a json file,
289
+ # let's parse it to get our arguments.
290
+ model_args, data_args, training_args = parser.parse_json_file(json_file=os.path.abspath(sys.argv[1]))
291
+ else:
292
+ model_args, data_args, training_args = parser.parse_args_into_dataclasses()
293
+
294
+ if (
295
+ os.path.exists(training_args.output_dir)
296
+ and os.listdir(training_args.output_dir)
297
+ and training_args.do_train
298
+ and not training_args.overwrite_output_dir
299
+ ):
300
+ raise ValueError(
301
+ f"Output directory ({training_args.output_dir}) already exists and is not empty."
302
+ "Use --overwrite_output_dir to overcome."
303
+ )
304
+
305
+ # Make one log on every process with the configuration for debugging.
306
+ logging.basicConfig(
307
+ format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
308
+ datefmt="%m/%d/%Y %H:%M:%S",
309
+ level=logging.INFO,
310
+ )
311
+ # Setup logging, we only want one process per machine to log things on the screen.
312
+ logger.setLevel(logging.INFO if jax.process_index() == 0 else logging.ERROR)
313
+ if jax.process_index() == 0:
314
+ transformers.utils.logging.set_verbosity_info()
315
+ else:
316
+ transformers.utils.logging.set_verbosity_error()
317
+
318
+ # Set the verbosity to info of the Transformers logger (on main process only):
319
+ logger.info(f"Training/evaluation parameters {training_args}")
320
+
321
+ if model_args.tokenizer_name:
322
+ tokenizer = AutoTokenizer.from_pretrained(
323
+ model_args.tokenizer_name, cache_dir=model_args.cache_dir, use_fast=model_args.use_fast_tokenizer
324
+ )
325
+ elif model_args.text_model_name_or_path:
326
+ tokenizer = AutoTokenizer.from_pretrained(
327
+ model_args.text_model_name_or_path, cache_dir=model_args.cache_dir, use_fast=model_args.use_fast_tokenizer
328
+ )
329
+ else:
330
+ raise ValueError(
331
+ "You are instantiating a new tokenizer from scratch. This is not supported by this script."
332
+ "You can do it from another script, save it, and load it from here, using --tokenizer_name."
333
+ )
334
+
335
+ model = FlaxHybridCLIP.from_text_vision_pretrained(
336
+ model_args.text_model_name_or_path,
337
+ model_args.vision_model_name_or_path,
338
+ seed=training_args.seed,
339
+ dtype=getattr(jnp, model_args.dtype),
340
+ text_from_pt=model_args.from_pt,
341
+ vision_from_pt=model_args.from_pt,
342
+ )
343
+ config = model.config
344
+ # set seed for torch dataloaders
345
+ set_seed(training_args.seed)
346
+
347
+ # Initialize torchvision transforms and jit them for faster processing
348
+ preprocess = Transform(config.vision_config.image_size)
349
+ preprocess = torch.jit.script(preprocess)
350
+
351
+ # Initialize the image-text dataset
352
+ train_dataset = ImageTextDataset(
353
+ data_args.data_dir,
354
+ data_args.train_file,
355
+ captions_per_image=2,
356
+ transform=preprocess,
357
+ )
358
+
359
+ eval_dataset = ImageTextDataset(
360
+ data_args.data_dir,
361
+ data_args.validation_file,
362
+ captions_per_image=1,
363
+ transform=preprocess,
364
+ )
365
+
366
+ # Store some constant
367
+ num_epochs = int(training_args.num_train_epochs)
368
+ train_batch_size = int(training_args.per_device_train_batch_size) * jax.device_count()
369
+ eval_batch_size = int(training_args.per_device_eval_batch_size) * jax.device_count()
370
+ steps_per_epoch = len(train_dataset) // train_batch_size
371
+ total_train_steps = steps_per_epoch * num_epochs
372
+
373
+ # Use collate function to tokenizer the text and convert the processed images to numpy
374
+ def collate_fn(examples):
375
+ pixel_values = torch.stack([example[0] for example in examples]).permute(0, 2, 3, 1).numpy()
376
+ captions = [example[1] for example in examples]
377
+ inputs = tokenizer(
378
+ captions, max_length=data_args.max_seq_length, padding="max_length", truncation=True, return_tensors="np"
379
+ )
380
+
381
+ batch = {
382
+ "pixel_values": pixel_values,
383
+ "input_ids": inputs["input_ids"],
384
+ "attention_mask": inputs["attention_mask"],
385
+ }
386
+
387
+ return batch
388
+
389
+ # Create data loaders
390
+ train_loader = torch.utils.data.DataLoader(
391
+ train_dataset,
392
+ batch_size=train_batch_size,
393
+ shuffle=True,
394
+ num_workers=data_args.preprocessing_num_workers,
395
+ persistent_workers=True,
396
+ drop_last=True,
397
+ collate_fn=collate_fn,
398
+ )
399
+
400
+ eval_loader = torch.utils.data.DataLoader(
401
+ eval_dataset,
402
+ batch_size=eval_batch_size,
403
+ shuffle=False,
404
+ num_workers=data_args.preprocessing_num_workers,
405
+ persistent_workers=True,
406
+ drop_last=True,
407
+ collate_fn=collate_fn,
408
+ )
409
+
410
+ # Enable tensorboard only on the master node
411
+ if has_tensorboard and jax.process_index() == 0:
412
+ summary_writer = SummaryWriter(log_dir=Path(training_args.output_dir).joinpath("logs").as_posix())
413
+
414
+ # Initialize our training
415
+ rng = jax.random.PRNGKey(training_args.seed)
416
+ rng, dropout_rng = jax.random.split(rng)
417
+
418
+ # Create learning rate schedule
419
+ linear_decay_lr_schedule_fn = create_learning_rate_fn(
420
+ len(train_dataset),
421
+ train_batch_size,
422
+ training_args.num_train_epochs,
423
+ training_args.warmup_steps,
424
+ training_args.learning_rate,
425
+ )
426
+
427
+ # create adam optimizer
428
+ adamw = optax.adamw(
429
+ learning_rate=linear_decay_lr_schedule_fn,
430
+ b1=training_args.adam_beta1,
431
+ b2=training_args.adam_beta2,
432
+ eps=training_args.adam_epsilon,
433
+ weight_decay=training_args.weight_decay,
434
+ )
435
+
436
+ # Setup train state
437
+ state = TrainState.create(apply_fn=model.__call__, params=model.params, tx=adamw, dropout_rng=dropout_rng)
438
+
439
+ def cross_entropy(logits, axis):
440
+ logprobs = jax.nn.log_softmax(logits, axis=axis)
441
+ nll = jnp.diag(logprobs)
442
+ ce = -jnp.mean(nll)
443
+ return ce
444
+
445
+ def clip_loss(similarity):
446
+ loss = (cross_entropy(similarity, axis=0) + cross_entropy(similarity, axis=1)) / 2
447
+ return loss
448
+
449
+ # Define gradient update step fn
450
+ def train_step(state, batch):
451
+ dropout_rng, new_dropout_rng = jax.random.split(state.dropout_rng)
452
+
453
+ def compute_loss(params):
454
+ logits = state.apply_fn(**batch, params=params, dropout_rng=dropout_rng, train=True)[0]
455
+ loss = clip_loss(logits)
456
+ return loss
457
+
458
+ grad_fn = jax.value_and_grad(compute_loss)
459
+ loss, grad = grad_fn(state.params)
460
+ grad = jax.lax.pmean(grad, "batch")
461
+
462
+ new_state = state.apply_gradients(grads=grad, dropout_rng=new_dropout_rng)
463
+
464
+ metrics = {"loss": loss, "learning_rate": linear_decay_lr_schedule_fn(state.step)}
465
+ metrics = jax.lax.pmean(metrics, axis_name="batch")
466
+
467
+ return new_state, metrics
468
+
469
+ # Define eval fn
470
+ def eval_step(params, batch):
471
+ logits = model(**batch, params=params, train=False)[0]
472
+ loss = clip_loss(logits)
473
+
474
+ # summarize metrics
475
+ metrics = {"loss": loss}
476
+ metrics = jax.lax.pmean(metrics, axis_name="batch")
477
+ return metrics
478
+
479
+ # Create parallel version of the train and eval step
480
+ p_train_step = jax.pmap(train_step, "batch", donate_argnums=(0,))
481
+ p_eval_step = jax.pmap(eval_step, "batch")
482
+
483
+ # Replicate the train state on each device
484
+ state = state.replicate()
485
+
486
+ logger.info("***** Running training *****")
487
+ logger.info(f" Num examples = {len(train_dataset)}")
488
+ logger.info(f" Num Epochs = {num_epochs}")
489
+ logger.info(f" Instantaneous batch size per device = {training_args.per_device_train_batch_size}")
490
+ logger.info(f" Total train batch size (w. parallel & distributed) = {train_batch_size}")
491
+ logger.info(f" Total optimization steps = {total_train_steps}")
492
+
493
+ train_time = 0
494
+ # Create sampling rng
495
+ rng, input_rng = jax.random.split(rng)
496
+
497
+ epochs = tqdm(range(num_epochs), desc=f"Epoch ... (1/{num_epochs})", position=0)
498
+ for epoch in epochs:
499
+ # ======================== Training ================================
500
+ train_start = time.time()
501
+
502
+ # Create sampling rng
503
+ rng, input_rng = jax.random.split(rng)
504
+ train_metrics = []
505
+
506
+ steps_per_epoch = len(train_dataset) // train_batch_size
507
+ train_step_progress_bar = tqdm(total=steps_per_epoch, desc="Training...", position=1, leave=False)
508
+ # train
509
+ for batch in train_loader:
510
+ batch = shard(batch)
511
+ state, train_metric = p_train_step(state, batch)
512
+ train_metrics.append(train_metric)
513
+
514
+ train_step_progress_bar.update(1)
515
+
516
+ train_time += time.time() - train_start
517
+
518
+ train_metric = unreplicate(train_metric)
519
+
520
+ train_step_progress_bar.close()
521
+ epochs.write(
522
+ f"Epoch... ({epoch + 1}/{num_epochs} | Loss: {train_metric['loss']}, Learning Rate: {train_metric['learning_rate']})"
523
+ )
524
+
525
+ # ======================== Evaluating ==============================
526
+ eval_metrics = []
527
+ eval_steps = len(eval_dataset) // eval_batch_size
528
+ eval_step_progress_bar = tqdm(total=eval_steps, desc="Evaluating...", position=2, leave=False)
529
+ for batch in eval_loader:
530
+ # Model forward
531
+ batch = shard(batch)
532
+ metrics = p_eval_step(state.params, batch)
533
+ eval_metrics.append(metrics)
534
+
535
+ eval_step_progress_bar.update(1)
536
+
537
+ # normalize eval metrics
538
+ eval_metrics = get_metrics(eval_metrics)
539
+
540
+ eval_metrics = jax.tree_map(jnp.mean, eval_metrics)
541
+
542
+ # Print metrics and update progress bar
543
+ eval_step_progress_bar.close()
544
+ desc = f"Epoch... ({epoch + 1}/{num_epochs} | Eval Loss: {eval_metrics['loss']})"
545
+ epochs.write(desc)
546
+ epochs.desc = desc
547
+
548
+ # Save metrics
549
+ if has_tensorboard and jax.process_index() == 0:
550
+ cur_step = epoch * (len(train_dataset) // train_batch_size)
551
+ write_metric(summary_writer, train_metrics, eval_metrics, train_time, cur_step)
552
+
553
+ # save checkpoint after each epoch and push checkpoint to the hub
554
+ if jax.process_index() == 0:
555
+ params = jax.device_get(unreplicate(state.params))
556
+ model.save_pretrained(
557
+ training_args.output_dir,
558
+ params=params,
559
+ push_to_hub=training_args.push_to_hub,
560
+ commit_message=f"Saving weights and logs of epoch {epoch+1}",
561
+ )
562
+
563
+
564
+ if __name__ == "__main__":
565
+ main()