run_glue.py 6.79 KB
Newer Older
thomwolf's avatar
thomwolf committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# coding=utf-8
# Copyright 2018 The Google AI Language Team Authors and The HuggingFace Inc. team.
# Copyright (c) 2018, NVIDIA CORPORATION.  All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
16
""" Finetuning the library models for sequence classification on GLUE (Bert, XLM, XLNet, RoBERTa, Albert, XLM-RoBERTa)."""
thomwolf's avatar
thomwolf committed
17
18


Julien Chaumond's avatar
Julien Chaumond committed
19
import dataclasses
thomwolf's avatar
thomwolf committed
20
21
import logging
import os
22
from dataclasses import dataclass, field
Julien Chaumond's avatar
Julien Chaumond committed
23
from typing import Dict, Optional
thomwolf's avatar
thomwolf committed
24
25
26

import numpy as np

27
from transformers import (
28
29
30
    AutoConfig,
    AutoModelForSequenceClassification,
    AutoTokenizer,
Julien Chaumond's avatar
Julien Chaumond committed
31
32
33
    EvalPrediction,
    GlueDataset,
    GlueDataTrainingArguments,
34
    HfArgumentParser,
Julien Chaumond's avatar
Julien Chaumond committed
35
    Trainer,
36
    TrainingArguments,
Julien Chaumond's avatar
Julien Chaumond committed
37
38
39
40
    glue_compute_metrics,
    glue_output_modes,
    glue_tasks_num_labels,
    set_seed,
41
)
Aymeric Augustin's avatar
Aymeric Augustin committed
42

thomwolf's avatar
thomwolf committed
43
44
45

logger = logging.getLogger(__name__)

thomwolf's avatar
thomwolf committed
46

47
48
49
50
51
52
53
@dataclass
class ModelArguments:
    """
    Arguments pertaining to which model/config/tokenizer we are going to fine-tune from.
    """

    model_name_or_path: str = field(
Julien Chaumond's avatar
Julien Chaumond committed
54
        metadata={"help": "Path to pretrained model or model identifier from huggingface.co/models"}
55
    )
56
57
    config_name: Optional[str] = field(
        default=None, metadata={"help": "Pretrained config name or path if not the same as model_name"}
58
    )
59
60
    tokenizer_name: Optional[str] = field(
        default=None, metadata={"help": "Pretrained tokenizer name or path if not the same as model_name"}
61
    )
62
    cache_dir: Optional[str] = field(
Julien Chaumond's avatar
Julien Chaumond committed
63
        default=None, metadata={"help": "Where do you want to store the pretrained models downloaded from s3"}
64
65
66
    )


67
def main():
Julien Chaumond's avatar
Julien Chaumond committed
68
69
70
    # See all possible arguments in src/transformers/training_args.py
    # or by passing the --help flag to this script.
    # We now keep distinct sets of args, for a cleaner separation of concerns.
71

Julien Chaumond's avatar
Julien Chaumond committed
72
73
    parser = HfArgumentParser((ModelArguments, GlueDataTrainingArguments, TrainingArguments))
    model_args, data_args, training_args = parser.parse_args_into_dataclasses()
thomwolf's avatar
thomwolf committed
74

75
    if (
Julien Chaumond's avatar
Julien Chaumond committed
76
77
78
79
        os.path.exists(training_args.output_dir)
        and os.listdir(training_args.output_dir)
        and training_args.do_train
        and not training_args.overwrite_output_dir
80
81
    ):
        raise ValueError(
Julien Chaumond's avatar
Julien Chaumond committed
82
            f"Output directory ({training_args.output_dir}) already exists and is not empty. Use --overwrite_output_dir to overcome."
83
        )
thomwolf's avatar
thomwolf committed
84

thomwolf's avatar
thomwolf committed
85
    # Setup logging
86
87
88
    logging.basicConfig(
        format="%(asctime)s - %(levelname)s - %(name)s -   %(message)s",
        datefmt="%m/%d/%Y %H:%M:%S",
Julien Chaumond's avatar
Julien Chaumond committed
89
        level=logging.INFO if training_args.local_rank in [-1, 0] else logging.WARN,
90
91
92
    )
    logger.warning(
        "Process rank: %s, device: %s, n_gpu: %s, distributed training: %s, 16-bits training: %s",
Julien Chaumond's avatar
Julien Chaumond committed
93
94
95
96
97
        training_args.local_rank,
        training_args.device,
        training_args.n_gpu,
        bool(training_args.local_rank != -1),
        training_args.fp16,
98
    )
Julien Chaumond's avatar
Julien Chaumond committed
99
    logger.info("Training/evaluation parameters %s", training_args)
thomwolf's avatar
thomwolf committed
100

thomwolf's avatar
thomwolf committed
101
    # Set seed
Julien Chaumond's avatar
Julien Chaumond committed
102
    set_seed(training_args.seed)
thomwolf's avatar
thomwolf committed
103

Julien Chaumond's avatar
Julien Chaumond committed
104
105
106
107
108
    try:
        num_labels = glue_tasks_num_labels[data_args.task_name]
        output_mode = glue_output_modes[data_args.task_name]
    except KeyError:
        raise ValueError("Task not found: %s" % (data_args.task_name))
thomwolf's avatar
thomwolf committed
109
110

    # Load pretrained model and tokenizer
Julien Chaumond's avatar
Julien Chaumond committed
111
112
113
114
    #
    # Distributed training:
    # The .from_pretrained methods guarantee that only one local process can concurrently
    # download model & vocab.
thomwolf's avatar
thomwolf committed
115

116
    config = AutoConfig.from_pretrained(
Julien Chaumond's avatar
Julien Chaumond committed
117
        model_args.config_name if model_args.config_name else model_args.model_name_or_path,
118
        num_labels=num_labels,
Julien Chaumond's avatar
Julien Chaumond committed
119
120
        finetuning_task=data_args.task_name,
        cache_dir=model_args.cache_dir,
121
    )
122
    tokenizer = AutoTokenizer.from_pretrained(
Julien Chaumond's avatar
Julien Chaumond committed
123
124
        model_args.tokenizer_name if model_args.tokenizer_name else model_args.model_name_or_path,
        cache_dir=model_args.cache_dir,
125
    )
126
    model = AutoModelForSequenceClassification.from_pretrained(
Julien Chaumond's avatar
Julien Chaumond committed
127
128
        model_args.model_name_or_path,
        from_tf=bool(".ckpt" in model_args.model_name_or_path),
129
        config=config,
Julien Chaumond's avatar
Julien Chaumond committed
130
        cache_dir=model_args.cache_dir,
131
    )
thomwolf's avatar
thomwolf committed
132

Julien Chaumond's avatar
Julien Chaumond committed
133
134
135
136
137
138
139
140
141
142
143
    # Get datasets
    train_dataset = (
        GlueDataset(data_args, tokenizer=tokenizer, local_rank=training_args.local_rank)
        if training_args.do_train
        else None
    )
    eval_dataset = (
        GlueDataset(data_args, tokenizer=tokenizer, local_rank=training_args.local_rank, evaluate=True)
        if training_args.do_eval
        else None
    )
thomwolf's avatar
thomwolf committed
144

Julien Chaumond's avatar
Julien Chaumond committed
145
146
147
148
149
150
    def compute_metrics(p: EvalPrediction) -> Dict:
        if output_mode == "classification":
            preds = np.argmax(p.predictions, axis=1)
        elif output_mode == "regression":
            preds = np.squeeze(p.predictions)
        return glue_compute_metrics(data_args.task_name, preds, p.label_ids)
thomwolf's avatar
thomwolf committed
151

Julien Chaumond's avatar
Julien Chaumond committed
152
153
154
155
156
157
158
159
    # Initialize our Trainer
    trainer = Trainer(
        model=model,
        args=training_args,
        train_dataset=train_dataset,
        eval_dataset=eval_dataset,
        compute_metrics=compute_metrics,
    )
thomwolf's avatar
thomwolf committed
160

thomwolf's avatar
thomwolf committed
161
    # Training
Julien Chaumond's avatar
Julien Chaumond committed
162
163
164
165
166
    if training_args.do_train:
        trainer.train(
            model_path=model_args.model_name_or_path if os.path.isdir(model_args.model_name_or_path) else None
        )
        trainer.save_model()
thomwolf's avatar
thomwolf committed
167

thomwolf's avatar
thomwolf committed
168
    # Evaluation
thomwolf's avatar
thomwolf committed
169
    results = {}
Julien Chaumond's avatar
Julien Chaumond committed
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
    if training_args.do_eval and training_args.local_rank in [-1, 0]:
        logger.info("*** Evaluate ***")

        # Loop to handle MNLI double evaluation (matched, mis-matched)
        eval_datasets = [eval_dataset]
        if data_args.task_name == "mnli":
            mnli_mm_data_args = dataclasses.replace(data_args, task_name="mnli-mm")
            eval_datasets.append(
                GlueDataset(mnli_mm_data_args, tokenizer=tokenizer, local_rank=training_args.local_rank, evaluate=True)
            )

        for eval_dataset in eval_datasets:
            result = trainer.evaluate(eval_dataset=eval_dataset)

            output_eval_file = os.path.join(
                training_args.output_dir, f"eval_results_{eval_dataset.args.task_name}.txt"
186
            )
Julien Chaumond's avatar
Julien Chaumond committed
187
188
189
190
191
            with open(output_eval_file, "w") as writer:
                logger.info("***** Eval results {} *****".format(eval_dataset.args.task_name))
                for key, value in result.items():
                    logger.info("  %s = %s", key, value)
                    writer.write("%s = %s\n" % (key, value))
192

thomwolf's avatar
thomwolf committed
193
194
            results.update(result)

thomwolf's avatar
thomwolf committed
195
    return results
thomwolf's avatar
thomwolf committed
196
197
198
199


if __name__ == "__main__":
    main()