clip
copied
wxywb
2 years ago
2 changed files with 437 additions and 14 deletions
@ -0,0 +1,410 @@ |
|||||
|
import logging |
||||
|
import os |
||||
|
import sys |
||||
|
import transformers |
||||
|
import dataclasses |
||||
|
import ipdb |
||||
|
from dataclasses import dataclass, field |
||||
|
from typing import Optional, List |
||||
|
|
||||
|
import torch |
||||
|
from datasets import load_dataset |
||||
|
from PIL import Image |
||||
|
from torchvision.io import ImageReadMode, read_image |
||||
|
from torchvision.transforms import CenterCrop, ConvertImageDtype, Normalize, Resize |
||||
|
from torchvision.transforms.functional import InterpolationMode |
||||
|
|
||||
|
from transformers import ( |
||||
|
MODEL_FOR_CAUSAL_LM_MAPPING, |
||||
|
TrainingArguments, |
||||
|
default_data_collator, |
||||
|
is_torch_tpu_available, |
||||
|
set_seed, |
||||
|
) |
||||
|
|
||||
|
# We use torchvision for faster image pre-processing. The transforms are implemented as nn.Module, |
||||
|
# so we jit it to be faster. |
||||
|
|
||||
|
|
||||
|
logger = logging.getLogger(__name__) |
||||
|
|
||||
|
dataset_name_mapping = { |
||||
|
"image_caption_dataset.py": ("image_path", "caption"), |
||||
|
} |
||||
|
|
||||
|
|
||||
|
def dataclass_from_dict(klass, d): |
||||
|
try: |
||||
|
fieldtypes = {f.name: f.type for f in dataclasses.fields(klass)} |
||||
|
return klass(**{f: dataclass_from_dict(fieldtypes[f], d[f]) for f in d}) |
||||
|
except: |
||||
|
return d # Not a dataclass field |
||||
|
|
||||
|
|
||||
|
@dataclass |
||||
|
class DataTrainingArguments: |
||||
|
""" |
||||
|
Arguments pertaining to what data we are going to input our model for training and eval. |
||||
|
""" |
||||
|
|
||||
|
dataset_name: Optional[str] = field( |
||||
|
default=None, metadata={"help": "The name of the dataset to use (via the datasets library)."} |
||||
|
) |
||||
|
dataset_config_name: Optional[str] = field( |
||||
|
default=None, metadata={"help": "The configuration name of the dataset to use (via the datasets library)."} |
||||
|
) |
||||
|
data_dir: Optional[str] = field(default=None, metadata={"help": "The data directory containing input files."}) |
||||
|
image_column: Optional[str] = field( |
||||
|
default="image_path", |
||||
|
metadata={"help": "The name of the column in the datasets containing the full image file paths."}, |
||||
|
) |
||||
|
caption_column: Optional[str] = field( |
||||
|
default="caption", |
||||
|
metadata={"help": "The name of the column in the datasets containing the image captions."}, |
||||
|
) |
||||
|
train_file: Optional[str] = field( |
||||
|
default=None, metadata={"help": "The input training data file (a jsonlines file)."} |
||||
|
) |
||||
|
validation_file: Optional[str] = field( |
||||
|
default=None, |
||||
|
metadata={"help": "An optional input evaluation data file (a jsonlines file)."}, |
||||
|
) |
||||
|
max_seq_length: Optional[int] = field( |
||||
|
default=77, |
||||
|
metadata={ |
||||
|
"help": ( |
||||
|
"The maximum total input sequence length after tokenization. Sequences longer " |
||||
|
"than this will be truncated, sequences shorter will be padded." |
||||
|
) |
||||
|
}, |
||||
|
) |
||||
|
max_train_samples: Optional[int] = field( |
||||
|
default=None, |
||||
|
metadata={ |
||||
|
"help": ( |
||||
|
"For debugging purposes or quicker training, truncate the number of training examples to this " |
||||
|
"value if set." |
||||
|
) |
||||
|
}, |
||||
|
) |
||||
|
max_eval_samples: Optional[int] = field( |
||||
|
default=None, |
||||
|
metadata={ |
||||
|
"help": ( |
||||
|
"For debugging purposes or quicker training, truncate the number of evaluation examples to this " |
||||
|
"value if set." |
||||
|
) |
||||
|
}, |
||||
|
) |
||||
|
overwrite_cache: bool = field( |
||||
|
default=False, metadata={"help": "Overwrite the cached training and evaluation sets"} |
||||
|
) |
||||
|
preprocessing_num_workers: Optional[int] = field( |
||||
|
default=None, |
||||
|
metadata={"help": "The number of processes to use for the preprocessing."}, |
||||
|
) |
||||
|
cache_dir: Optional[str] = field( |
||||
|
default=None, metadata={"help": "Where do you want to store the pretrained models downloaded from s3"} |
||||
|
) |
||||
|
image_mean: Optional[str] = field( |
||||
|
default=None, metadata={"help": "image preprocessing mean"} |
||||
|
) |
||||
|
image_std: Optional[str] = field( |
||||
|
default=None, metadata={"help": "image preprocessing std"} |
||||
|
) |
||||
|
freeze_vision_model: bool = field( |
||||
|
default=False, metadata={"help":"Whether to freeze the vision model parameters or not."} |
||||
|
) |
||||
|
freeze_text_model: bool = field( |
||||
|
default=False, metadata={"help": "Whether to freeze the text model parameters or not."} |
||||
|
) |
||||
|
|
||||
|
|
||||
|
def __post_init__(self): |
||||
|
if self.dataset_name is None and self.train_file is None and self.validation_file is None: |
||||
|
raise ValueError("Need either a dataset name or a training/validation file.") |
||||
|
else: |
||||
|
if self.train_file is not None: |
||||
|
extension = self.train_file.split(".")[-1] |
||||
|
assert extension in ["csv", "json"], "`train_file` should be a csv or a json file." |
||||
|
if self.validation_file is not None: |
||||
|
extension = self.validation_file.split(".")[-1] |
||||
|
assert extension in ["csv", "json"], "`validation_file` should be a csv or a json file." |
||||
|
if self.validation_file is not None: |
||||
|
extension = self.validation_file.split(".")[-1] |
||||
|
assert extension == "json", "`validation_file` should be a json file." |
||||
|
|
||||
|
|
||||
|
class Transform(torch.nn.Module): |
||||
|
def __init__(self, image_size, mean, std): |
||||
|
super().__init__() |
||||
|
self.transforms = torch.nn.Sequential( |
||||
|
Resize([image_size], interpolation=InterpolationMode.BICUBIC), |
||||
|
CenterCrop(image_size), |
||||
|
ConvertImageDtype(torch.float), |
||||
|
Normalize(mean, std), |
||||
|
) |
||||
|
|
||||
|
def forward(self, x) -> torch.Tensor: |
||||
|
"""`x` should be an instance of `PIL.Image.Image`""" |
||||
|
with torch.no_grad(): |
||||
|
x = self.transforms(x) |
||||
|
return x |
||||
|
|
||||
|
def collate_fn(examples): |
||||
|
pixel_values = torch.stack([example["pixel_values"] for example in examples]) |
||||
|
input_ids = torch.tensor([example["input_ids"] for example in examples], dtype=torch.long) |
||||
|
attention_mask = torch.tensor([example["attention_mask"] for example in examples], dtype=torch.long) |
||||
|
return { |
||||
|
"pixel_values": pixel_values, |
||||
|
"input_ids": input_ids, |
||||
|
"attention_mask": attention_mask, |
||||
|
"return_loss": True, |
||||
|
} |
||||
|
|
||||
|
|
||||
|
def train_with_hf_trainer(model, tokenizer, data_args, training_args, **kwargs): |
||||
|
|
||||
|
import evaluate |
||||
|
import datasets |
||||
|
|
||||
|
from transformers import Trainer |
||||
|
from datasets import load_dataset |
||||
|
from towhee.trainer.training_config import get_dataclasses_help |
||||
|
|
||||
|
print('**** DataTrainingArguments ****') |
||||
|
get_dataclasses_help(DataTrainingArguments) |
||||
|
data_args = dataclass_from_dict(DataTrainingArguments, data_args) |
||||
|
|
||||
|
print('**** TrainingArguments ****') |
||||
|
get_dataclasses_help(TrainingArguments) |
||||
|
training_args = dataclass_from_dict(TrainingArguments, training_args) |
||||
|
|
||||
|
# 2. Setup logging |
||||
|
logging.basicConfig( |
||||
|
format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", |
||||
|
datefmt="%m/%d/%Y %H:%M:%S", |
||||
|
handlers=[logging.StreamHandler(sys.stdout)], |
||||
|
) |
||||
|
|
||||
|
# 2. Setup logging |
||||
|
#+ training_args |
||||
|
#log_level = training_args.get_process_log_level() |
||||
|
log_level = training_args.get_process_log_level() |
||||
|
logger.setLevel(log_level) |
||||
|
transformers.utils.logging.set_verbosity(log_level) |
||||
|
transformers.utils.logging.enable_default_handler() |
||||
|
transformers.utils.logging.enable_explicit_format() |
||||
|
|
||||
|
temp_cache_dir = data_args.cache_dir |
||||
|
logger.warning( |
||||
|
f"Process rank: {training_args.local_rank}, device: {training_args.device}, n_gpu: {training_args.n_gpu}" |
||||
|
+ f"distributed training: {bool(training_args.local_rank != -1)}, 16-bits training: {training_args.fp16}" |
||||
|
) |
||||
|
logger.info(f"Training/evaluation parameters {training_args}") |
||||
|
|
||||
|
# Detecting last checkpoint and eventualy continue from last checkpoint |
||||
|
### place holder ### |
||||
|
### place holder ### |
||||
|
### place holder ### |
||||
|
|
||||
|
|
||||
|
# Load dataset |
||||
|
# Get the datasets: you can either provide your own CSV/JSON training and evaluation files (see below) |
||||
|
# or just provide the name of one of the public datasets available on the hub at https://huggingface.co/datasets/ |
||||
|
# (the dataset will be downloaded automatically from the datasets Hub). |
||||
|
# |
||||
|
# For CSV/JSON files this script will use the first column for the full image path and the second column for the |
||||
|
# captions (unless you specify column names for this with the `image_column` and `caption_column` arguments). |
||||
|
# |
||||
|
|
||||
|
if data_args.dataset_name is not None: |
||||
|
# Downloading and loading a dataset from the hub. |
||||
|
dataset = load_dataset( |
||||
|
data_args.dataset_name, |
||||
|
data_args.dataset_config_name, |
||||
|
cache_dir=temp_cache_dir, |
||||
|
keep_in_memory=False, |
||||
|
data_dir=data_args.data_dir, |
||||
|
# use_auth_token=True if model_args.use_auth_token else None, |
||||
|
) |
||||
|
else: |
||||
|
data_files = {} |
||||
|
if data_args.train_file is not None: |
||||
|
data_files["train"] = data_args.train_file |
||||
|
extension = data_args.train_file.split(".")[-1] |
||||
|
if data_args.validation_file is not None: |
||||
|
data_files["validation"] = data_args.validation_file |
||||
|
extension = data_args.validation_file.split(".")[-1] |
||||
|
if data_args.test_file is not None: |
||||
|
data_files["test"] = data_args.test_file |
||||
|
extension = data_args.test_file.split(".")[-1] |
||||
|
dataset = load_dataset( |
||||
|
extension, |
||||
|
data_files=data_files, |
||||
|
cache_dir=temp_cache_dir, |
||||
|
# use_auth_token=True if model_args.use_auth_token else None, |
||||
|
) |
||||
|
|
||||
|
config = model.config |
||||
|
|
||||
|
freeze_vision_model = data_args.freeze_vision_model |
||||
|
freeze_text_model = data_args.freeze_text_model |
||||
|
|
||||
|
def _freeze_params(module): |
||||
|
for param in module.parameters(): |
||||
|
param.requires_grad = False |
||||
|
|
||||
|
if model_args.freeze_vision_model: |
||||
|
_freeze_params(model.vision_model) |
||||
|
|
||||
|
if model_args.freeze_text_model: |
||||
|
_freeze_params(model.text_model) |
||||
|
|
||||
|
if freeze_vision_model is True: |
||||
|
_freeze_params(model.vision_model) |
||||
|
|
||||
|
if freeze_text_model is True: |
||||
|
_freeze_params(model.text_model) |
||||
|
|
||||
|
set_seed(training_args.seed) |
||||
|
|
||||
|
if training_args.do_train: |
||||
|
column_names = dataset["train"].column_names |
||||
|
elif training_args.do_eval: |
||||
|
column_names = dataset["validation"].column_names |
||||
|
elif training_args.do_predict: |
||||
|
column_names = dataset["test"].column_names |
||||
|
else: |
||||
|
logger.info("There is nothing to do. Please pass `do_train`, `do_eval` and/or `do_predict`.") |
||||
|
return |
||||
|
|
||||
|
dataset_columns = dataset_name_mapping.get(data_args.dataset_name, None) |
||||
|
if data_args.image_column is None: |
||||
|
image_column = dataset_columns[0] if dataset_columns is not None else column_names[0] |
||||
|
else: |
||||
|
image_column = data_args.image_column |
||||
|
if image_column not in column_names: |
||||
|
raise ValueError( |
||||
|
f"--image_column' value '{data_args.image_column}' needs to be one of: {', '.join(column_names)}" |
||||
|
) |
||||
|
if data_args.caption_column is None: |
||||
|
caption_column = dataset_columns[1] if dataset_columns is not None else column_names[1] |
||||
|
else: |
||||
|
caption_column = data_args.caption_column |
||||
|
if caption_column not in column_names: |
||||
|
raise ValueError( |
||||
|
f"--caption_column' value '{data_args.caption_column}' needs to be one of: {', '.join(column_names)}" |
||||
|
) |
||||
|
|
||||
|
|
||||
|
image_mean, image_std = data_args.image_mean, data_args.image_std |
||||
|
# Preprocessing the datasets. |
||||
|
# Initialize torchvision transforms and jit it for faster processing. |
||||
|
|
||||
|
image_transformations = Transform( |
||||
|
config.vision_config.image_size, image_mean, image_std |
||||
|
) |
||||
|
image_transformations = torch.jit.script(image_transformations) |
||||
|
|
||||
|
# Preprocessing the datasets. |
||||
|
# We need to tokenize input captions and transform the images. |
||||
|
#data_args |
||||
|
|
||||
|
def tokenize_captions(examples): |
||||
|
captions = [caption for caption in examples[caption_column]] |
||||
|
text_inputs = tokenizer(captions, max_length=data_args.max_seq_length, padding="max_length", truncation=True) |
||||
|
examples["input_ids"] = text_inputs.input_ids |
||||
|
examples["attention_mask"] = text_inputs.attention_mask |
||||
|
return examples |
||||
|
|
||||
|
def transform_images(examples): |
||||
|
images = [read_image(image_file, mode=ImageReadMode.RGB) for image_file in examples[image_column]] |
||||
|
examples["pixel_values"] = [image_transformations(image) for image in images] |
||||
|
return examples |
||||
|
|
||||
|
def filter_corrupt_images(examples): |
||||
|
"""remove problematic images""" |
||||
|
valid_images = [] |
||||
|
for image_file in examples[image_column]: |
||||
|
try: |
||||
|
Image.open(image_file) |
||||
|
valid_images.append(True) |
||||
|
except Exception: |
||||
|
valid_images.append(False) |
||||
|
return valid_images |
||||
|
|
||||
|
if training_args.do_train: |
||||
|
if "train" not in dataset: |
||||
|
raise ValueError("--do_train requires a train dataset") |
||||
|
train_dataset = dataset["train"] |
||||
|
if data_args.max_train_samples is not None: |
||||
|
max_train_samples = min(len(train_dataset), data_args.max_train_samples) |
||||
|
train_dataset = train_dataset.select(range(max_train_samples)) |
||||
|
train_dataset = train_dataset.filter( |
||||
|
filter_corrupt_images, batched=True, num_proc=data_args.preprocessing_num_workers |
||||
|
) |
||||
|
train_dataset = train_dataset.map( |
||||
|
function=tokenize_captions, |
||||
|
batched=True, |
||||
|
remove_columns=[col for col in column_names if col != image_column], |
||||
|
num_proc=data_args.preprocessing_num_workers, |
||||
|
load_from_cache_file=not data_args.overwrite_cache, |
||||
|
desc="Running tokenizer on train dataset", |
||||
|
) |
||||
|
|
||||
|
# Transform images on the fly as doing it on the whole dataset takes too much time. |
||||
|
train_dataset.set_transform(transform_images) |
||||
|
|
||||
|
if training_args.do_eval: |
||||
|
if "validation" not in dataset: |
||||
|
raise ValueError("--do_eval requires a train validation") |
||||
|
eval_dataset = dataset["validation"] |
||||
|
if data_args.max_eval_samples is not None: |
||||
|
max_eval_samples = min(len(eval_dataset), data_args.max_eval_samples) |
||||
|
eval_dataset = eval_dataset.select(range(max_eval_samples)) |
||||
|
|
||||
|
eval_dataset = eval_dataset.filter( |
||||
|
filter_corrupt_images, batched=True, num_proc=data_args.preprocessing_num_workers |
||||
|
) |
||||
|
eval_dataset = eval_dataset.map( |
||||
|
function=tokenize_captions, |
||||
|
batched=True, |
||||
|
num_proc=data_args.preprocessing_num_workers, |
||||
|
remove_columns=[col for col in column_names if col != image_column], |
||||
|
load_from_cache_file=not data_args.overwrite_cache, |
||||
|
desc="Running tokenizer on validation dataset", |
||||
|
) |
||||
|
|
||||
|
# Transform images on the fly as doing it on the whole dataset takes too much time. |
||||
|
eval_dataset.set_transform(transform_images) |
||||
|
|
||||
|
# Initalize our trainer |
||||
|
trainer = Trainer( |
||||
|
model=model, |
||||
|
args=training_args, |
||||
|
train_dataset=train_dataset if training_args.do_train else None, |
||||
|
eval_dataset=eval_dataset if training_args.do_eval else None, |
||||
|
data_collator=collate_fn, |
||||
|
) |
||||
|
|
||||
|
# Training |
||||
|
last_checkpoint = None |
||||
|
if training_args.do_train: |
||||
|
checkpoint = None |
||||
|
if training_args.resume_from_checkpoint is not None: |
||||
|
checkpoint = training_args.resume_from_checkpoint |
||||
|
elif last_checkpoint is not None: |
||||
|
checkpoint = last_checkpoint |
||||
|
train_result = trainer.train(resume_from_checkpoint=checkpoint) |
||||
|
trainer.save_model() |
||||
|
trainer.log_metrics("train", train_result.metrics) |
||||
|
trainer.save_metrics("train", train_result.metrics) |
||||
|
trainer.save_state() |
||||
|
# Evaluation |
||||
|
if training_args.do_eval: |
||||
|
metrics = trainer.evaluate() |
||||
|
trainer.log_metrics("eval", metrics) |
||||
|
trainer.save_metrics("eval", metrics) |
||||
|
|
||||
|
|
Loading…
Reference in new issue