From 4f8972e88a1aeae9013bba2e9725c2cd3a97a12c Mon Sep 17 00:00:00 2001 From: "hanluzhi0429@gmail.com" Date: Sun, 13 Apr 2025 09:30:46 +0800 Subject: [PATCH] Initial commit --- LICENSE | 201 ++ inference.py | 59 + llava.egg-info/PKG-INFO | 299 +++ llava.egg-info/SOURCES.txt | 107 + llava.egg-info/dependency_links.txt | 1 + llava.egg-info/requires.txt | 41 + llava.egg-info/top_level.txt | 2 + llava/__init__.py | 1 + llava/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 197 bytes .../actiontokenizer.cpython-310.pyc | Bin 0 -> 2977 bytes llava/__pycache__/constants.cpython-310.pyc | Bin 0 -> 459 bytes .../__pycache__/conversation.cpython-310.pyc | Bin 0 -> 14806 bytes llava/__pycache__/mm_utils.cpython-310.pyc | Bin 0 -> 13916 bytes llava/__pycache__/utils.cpython-310.pyc | Bin 0 -> 6974 bytes llava/actiontokenizer.py | 128 ++ llava/constants.py | 12 + llava/conversation.py | 597 ++++++ llava/eval/evaluate_interleave.py | 339 +++ llava/eval/model_vqa.py | 240 +++ llava/mm_utils.py | 399 ++++ llava/model/__init__.py | 16 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 703 bytes .../model/__pycache__/builder.cpython-310.pyc | Bin 0 -> 7878 bytes .../__pycache__/llava_arch.cpython-310.pyc | Bin 0 -> 16884 bytes llava/model/apply_delta.py | 47 + llava/model/builder.py | 307 +++ llava/model/consolidate.py | 30 + .../__pycache__/llava_llama.cpython-310.pyc | Bin 0 -> 4269 bytes .../__pycache__/llava_mistral.cpython-310.pyc | Bin 0 -> 4028 bytes .../__pycache__/llava_mixtral.cpython-310.pyc | Bin 0 -> 4123 bytes .../__pycache__/llava_qwen.cpython-310.pyc | Bin 0 -> 4159 bytes llava/model/language_model/llava_gemma.py | 122 ++ llava/model/language_model/llava_llama.py | 156 ++ llava/model/language_model/llava_mistral.py | 127 ++ llava/model/language_model/llava_mixtral.py | 143 ++ llava/model/language_model/llava_mpt.py | 105 + llava/model/language_model/llava_qwen.py | 149 ++ llava/model/language_model/llava_qwen_moe.py | 149 ++ llava/model/language_model/modeling_llama.py | 1649 +++++++++++++++ llava/model/llava_arch.py | 623 ++++++ llava/model/make_delta.py | 52 + .../__pycache__/builder.cpython-310.pyc | Bin 0 -> 1177 bytes .../__pycache__/clip_encoder.cpython-310.pyc | Bin 0 -> 6465 bytes .../__pycache__/hf_vision.cpython-310.pyc | Bin 0 -> 3938 bytes .../__pycache__/imagebind.cpython-310.pyc | Bin 0 -> 2843 bytes .../open_clip_encoder.cpython-310.pyc | Bin 0 -> 6053 bytes .../siglip_encoder.cpython-310.pyc | Bin 0 -> 21976 bytes llava/model/multimodal_encoder/builder.py | 37 + .../model/multimodal_encoder/clip_encoder.py | 173 ++ .../dev_eva_clip/eva_clip/__init__.py | 9 + .../eva_clip/bpe_simple_vocab_16e6.txt.gz | Bin 0 -> 1356917 bytes .../dev_eva_clip/eva_clip/constants.py | 2 + .../dev_eva_clip/eva_clip/eva_vit_model.py | 571 +++++ .../dev_eva_clip/eva_clip/factory.py | 528 +++++ .../dev_eva_clip/eva_clip/hf_configs.py | 57 + .../dev_eva_clip/eva_clip/hf_model.py | 240 +++ .../dev_eva_clip/eva_clip/loss.py | 123 ++ .../dev_eva_clip/eva_clip/model.py | 429 ++++ .../eva_clip/model_configs/EVA-CLIP-18B.json | 27 + .../model_configs/EVA-CLIP-8B-plus.json | 27 + .../eva_clip/model_configs/EVA-CLIP-8B.json | 27 + .../model_configs/EVA01-CLIP-B-16.json | 19 + .../model_configs/EVA01-CLIP-g-14-plus.json | 24 + .../model_configs/EVA01-CLIP-g-14.json | 24 + .../model_configs/EVA02-CLIP-B-16.json | 29 + .../model_configs/EVA02-CLIP-L-14-336.json | 29 + .../model_configs/EVA02-CLIP-L-14.json | 29 + .../EVA02-CLIP-bigE-14-plus.json | 25 + .../model_configs/EVA02-CLIP-bigE-14.json | 25 + .../Internal-EVA02-CLIP-10B-14-448.json | 25 + .../Internal-EVA02-CLIP-10B-14.json | 25 + .../dev_eva_clip/eva_clip/modified_resnet.py | 179 ++ .../dev_eva_clip/eva_clip/openai.py | 144 ++ .../dev_eva_clip/eva_clip/pretrained.py | 314 +++ .../dev_eva_clip/eva_clip/rope.py | 131 ++ .../dev_eva_clip/eva_clip/timm_model.py | 114 + .../dev_eva_clip/eva_clip/tokenizer.py | 205 ++ .../dev_eva_clip/eva_clip/transform.py | 104 + .../dev_eva_clip/eva_clip/transformer.py | 683 ++++++ .../dev_eva_clip/eva_clip/utils.py | 321 +++ .../dev_eva_clip/eva_vit.py | 141 ++ .../eva_clip/eva_clip_encoder.py | 74 + .../eva_clip/eva_clip_processors.py | 72 + .../multimodal_encoder/eva_clip/eva_vit.py | 856 ++++++++ .../multimodal_encoder/eva_clip/factory.py | 60 + .../eva_clip/model_configs/EVA-CLIP-18B.json | 27 + .../model_configs/EVA-CLIP-8B-plus.json | 27 + .../eva_clip/model_configs/EVA-CLIP-8B.json | 27 + .../model_configs/EVA01-CLIP-B-16.json | 19 + .../model_configs/EVA01-CLIP-g-14-plus.json | 24 + .../model_configs/EVA01-CLIP-g-14.json | 24 + .../model_configs/EVA02-CLIP-B-16.json | 29 + .../model_configs/EVA02-CLIP-L-14-336.json | 29 + .../model_configs/EVA02-CLIP-L-14.json | 29 + .../EVA02-CLIP-bigE-14-plus.json | 25 + .../model_configs/EVA02-CLIP-bigE-14.json | 25 + .../Internal-EVA02-CLIP-10B-14-448.json | 25 + .../Internal-EVA02-CLIP-10B-14.json | 25 + llava/model/multimodal_encoder/hf_vision.py | 111 + llava/model/multimodal_encoder/imagebind.py | 73 + .../multimodal_encoder/open_clip_encoder.py | 163 ++ .../multimodal_encoder/siglip_encoder.py | 620 ++++++ .../__pycache__/builder.cpython-310.pyc | Bin 0 -> 2378 bytes .../pooler_projector.cpython-310.pyc | Bin 0 -> 1445 bytes llava/model/multimodal_projector/builder.py | 65 + .../multimodal_projector/pooler_projector.py | 33 + .../__pycache__/builder.cpython-310.pyc | Bin 0 -> 1426 bytes .../__pycache__/masked_drop.cpython-310.pyc | Bin 0 -> 2448 bytes .../__pycache__/perceiver.cpython-310.pyc | Bin 0 -> 4837 bytes .../__pycache__/qformer.cpython-310.pyc | Bin 0 -> 32694 bytes .../__pycache__/spatial_pool.cpython-310.pyc | Bin 0 -> 1878 bytes llava/model/multimodal_resampler/builder.py | 34 + .../model/multimodal_resampler/masked_drop.py | 80 + llava/model/multimodal_resampler/perceiver.py | 155 ++ llava/model/multimodal_resampler/qformer.py | 1160 ++++++++++ .../multimodal_resampler/spatial_pool.py | 45 + llava/model/utils.py | 20 + llava/serve/__init__.py | 0 llava/serve/cli.py | 111 + llava/serve/controller.py | 287 +++ llava/serve/examples/extreme_ironing.jpg | Bin 0 -> 62587 bytes llava/serve/examples/waterview.jpg | Bin 0 -> 95499 bytes llava/serve/gradio_multi_image.py | 448 ++++ llava/serve/gradio_web_server.py | 442 ++++ llava/serve/model_worker.py | 271 +++ llava/serve/register_worker.py | 26 + llava/serve/sglang_worker.py | 237 +++ llava/serve/test_message.py | 59 + .../__pycache__/llava_trainer.cpython-310.pyc | Bin 0 -> 21736 bytes llava/train/__pycache__/train.cpython-310.pyc | Bin 0 -> 47506 bytes llava/train/llama_flash_attn_monkey_patch.py | 87 + llava/train/llava_trainer.py | 527 +++++ llava/train/llava_trainer_eval.py | 76 + llava/train/train.py | 1873 +++++++++++++++++ llava/train/train_dpo.py | 1782 ++++++++++++++++ llava/train/train_mem.py | 4 + llava/utils.py | 198 ++ playground/2d_hist.py | 132 ++ playground/data_checker.py | 364 ++++ playground/demo/video_demo.py | 335 +++ playground/demo/xU25MMA2N4aVtYay.mp4 | Bin 0 -> 271700 bytes playground/equal_splitter.py | 38 + playground/remove_mid_ckpt.py | 35 + playground/sgl_llava_inference_multinode.py | 125 ++ playground/upload_data.py | 217 ++ pyproject.toml | 101 + requirements.txt | 324 +++ scripts/archived/convert_gqa_for_eval.py | 18 + scripts/archived/convert_mmvet_for_eval.py | 18 + scripts/archived/convert_sqa_to_llava.py | 88 + .../convert_sqa_to_llava_base_prompt.py | 298 +++ .../archived/convert_vizwiz_for_submission.py | 45 + .../archived/convert_vqav2_for_submission.py | 50 + scripts/archived/data_info.py | 124 ++ scripts/archived/dpo_data_info.py | 67 + scripts/archived/entry_cmd.sh | 30 + scripts/archived/finetune.sh | 95 + scripts/archived/finetune_1.5.sh | 99 + scripts/archived/finetune_full_schedule.sh | 46 + scripts/archived/finetune_lora.sh | 47 + scripts/archived/finetune_mixtral.sh | 96 + scripts/archived/finetune_mixtral_1.5.sh | 96 + .../finetune_mixtral_1.6_336px_anyres.sh | 100 + ..._mixtral_1.6_336px_anyres_freeze_vision.sh | 97 + ...tune_mixtral_1.6_336px_anyres_lmms_eval.sh | 78 + scripts/archived/finetune_mixtral_copy.sh | 96 + scripts/archived/finetune_qlora.sh | 48 + scripts/archived/finetune_sqa.sh | 34 + scripts/archived/merge_lora_weights.py | 22 + scripts/archived/pretrain.sh | 43 + scripts/archived/quick_check.py | 57 + scripts/archived/sqa_eval_batch.sh | 13 + scripts/archived/sqa_eval_gather.sh | 18 + scripts/interleave/eval_all.sh | 5 + scripts/interleave/eval_interleave_3d.sh | 28 + scripts/interleave/eval_multiprocess.sh | 54 + scripts/qwen.py | 19 + scripts/summarize_data.py | 81 + scripts/train/README.md | 94 + scripts/train/direct_finetune_clip.sh | 65 + scripts/train/direct_finetune_siglip_a4.sh | 67 + scripts/train/dpo.sh | 64 + scripts/train/dpo_ov7b.sh | 69 + scripts/train/finetune_ov_vla.sh | 74 + scripts/train/finetune_si.sh | 73 + scripts/train/mid_stage.yaml | 17 + scripts/train/onevision.yaml | 185 ++ scripts/train/pretrain_clip.sh | 55 + scripts/train/pretrain_siglip.sh | 55 + scripts/train/single_image.yaml | 187 ++ scripts/train/test.yaml | 3 + scripts/train/vla.yaml | 3 + scripts/zero2.json | 41 + scripts/zero2_fused_adamw.json | 41 + scripts/zero2_offload.json | 31 + scripts/zero3.json | 41 + scripts/zero3_offload.json | 48 + scripts/zero3pp.json | 53 + trl/__init__.py | 44 + trl/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1325 bytes trl/__pycache__/core.cpython-310.pyc | Bin 0 -> 11247 bytes trl/__pycache__/import_utils.cpython-310.pyc | Bin 0 -> 2658 bytes trl/core.py | 329 +++ trl/environment/__init__.py | 3 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 234 bytes .../base_environment.cpython-310.pyc | Bin 0 -> 14678 bytes trl/environment/base_environment.py | 463 ++++ trl/extras/__init__.py | 16 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 207 bytes .../best_of_n_sampler.cpython-310.pyc | Bin 0 -> 4977 bytes .../dataset_formatting.cpython-310.pyc | Bin 0 -> 2982 bytes trl/extras/best_of_n_sampler.py | 113 + trl/extras/dataset_formatting.py | 86 + trl/import_utils.py | 108 + trl/models/__init__.py | 35 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 702 bytes .../__pycache__/modeling_base.cpython-310.pyc | Bin 0 -> 17097 bytes .../modeling_value_head.cpython-310.pyc | Bin 0 -> 14351 bytes trl/models/__pycache__/utils.cpython-310.pyc | Bin 0 -> 3043 bytes trl/models/modeling_base.py | 640 ++++++ trl/models/modeling_sd_base.py | 624 ++++++ trl/models/modeling_value_head.py | 421 ++++ trl/models/utils.py | 83 + trl/trainer/__init__.py | 46 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1032 bytes trl/trainer/__pycache__/base.cpython-310.pyc | Bin 0 -> 1717 bytes .../__pycache__/ddpo_config.cpython-310.pyc | Bin 0 -> 3432 bytes .../__pycache__/dpo_trainer.cpython-310.pyc | Bin 0 -> 36994 bytes .../iterative_sft_trainer.cpython-310.pyc | Bin 0 -> 12443 bytes .../__pycache__/model_config.cpython-310.pyc | Bin 0 -> 2864 bytes .../__pycache__/ppo_config.cpython-310.pyc | Bin 0 -> 4439 bytes .../__pycache__/ppo_trainer.cpython-310.pyc | Bin 0 -> 42767 bytes .../__pycache__/reward_config.cpython-310.pyc | Bin 0 -> 1225 bytes .../reward_trainer.cpython-310.pyc | Bin 0 -> 9790 bytes .../__pycache__/sft_trainer.cpython-310.pyc | Bin 0 -> 16694 bytes trl/trainer/__pycache__/utils.cpython-310.pyc | Bin 0 -> 23812 bytes trl/trainer/base.py | 46 + trl/trainer/ddpo_config.py | 115 + trl/trainer/ddpo_trainer.py | 604 ++++++ trl/trainer/dpo_trainer.py | 1186 +++++++++++ trl/trainer/iterative_sft_trainer.py | 334 +++ trl/trainer/model_config.py | 71 + trl/trainer/ppo_config.py | 175 ++ trl/trainer/ppo_trainer.py | 1397 ++++++++++++ trl/trainer/reward_config.py | 38 + trl/trainer/reward_trainer.py | 257 +++ trl/trainer/sft_trainer.py | 480 +++++ trl/trainer/utils.py | 703 +++++++ 248 files changed, 34689 insertions(+) create mode 100644 LICENSE create mode 100644 inference.py create mode 100644 llava.egg-info/PKG-INFO create mode 100644 llava.egg-info/SOURCES.txt create mode 100644 llava.egg-info/dependency_links.txt create mode 100644 llava.egg-info/requires.txt create mode 100644 llava.egg-info/top_level.txt create mode 100644 llava/__init__.py create mode 100644 llava/__pycache__/__init__.cpython-310.pyc create mode 100644 llava/__pycache__/actiontokenizer.cpython-310.pyc create mode 100644 llava/__pycache__/constants.cpython-310.pyc create mode 100644 llava/__pycache__/conversation.cpython-310.pyc create mode 100644 llava/__pycache__/mm_utils.cpython-310.pyc create mode 100644 llava/__pycache__/utils.cpython-310.pyc create mode 100644 llava/actiontokenizer.py create mode 100644 llava/constants.py create mode 100644 llava/conversation.py create mode 100644 llava/eval/evaluate_interleave.py create mode 100644 llava/eval/model_vqa.py create mode 100644 llava/mm_utils.py create mode 100644 llava/model/__init__.py create mode 100644 llava/model/__pycache__/__init__.cpython-310.pyc create mode 100644 llava/model/__pycache__/builder.cpython-310.pyc create mode 100644 llava/model/__pycache__/llava_arch.cpython-310.pyc create mode 100644 llava/model/apply_delta.py create mode 100644 llava/model/builder.py create mode 100644 llava/model/consolidate.py create mode 100644 llava/model/language_model/__pycache__/llava_llama.cpython-310.pyc create mode 100644 llava/model/language_model/__pycache__/llava_mistral.cpython-310.pyc create mode 100644 llava/model/language_model/__pycache__/llava_mixtral.cpython-310.pyc create mode 100644 llava/model/language_model/__pycache__/llava_qwen.cpython-310.pyc create mode 100644 llava/model/language_model/llava_gemma.py create mode 100644 llava/model/language_model/llava_llama.py create mode 100644 llava/model/language_model/llava_mistral.py create mode 100644 llava/model/language_model/llava_mixtral.py create mode 100644 llava/model/language_model/llava_mpt.py create mode 100644 llava/model/language_model/llava_qwen.py create mode 100644 llava/model/language_model/llava_qwen_moe.py create mode 100644 llava/model/language_model/modeling_llama.py create mode 100644 llava/model/llava_arch.py create mode 100644 llava/model/make_delta.py create mode 100644 llava/model/multimodal_encoder/__pycache__/builder.cpython-310.pyc create mode 100644 llava/model/multimodal_encoder/__pycache__/clip_encoder.cpython-310.pyc create mode 100644 llava/model/multimodal_encoder/__pycache__/hf_vision.cpython-310.pyc create mode 100644 llava/model/multimodal_encoder/__pycache__/imagebind.cpython-310.pyc create mode 100644 llava/model/multimodal_encoder/__pycache__/open_clip_encoder.cpython-310.pyc create mode 100644 llava/model/multimodal_encoder/__pycache__/siglip_encoder.cpython-310.pyc create mode 100644 llava/model/multimodal_encoder/builder.py create mode 100644 llava/model/multimodal_encoder/clip_encoder.py create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/__init__.py create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/bpe_simple_vocab_16e6.txt.gz create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/constants.py create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/eva_vit_model.py create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/factory.py create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/hf_configs.py create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/hf_model.py create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/loss.py create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model.py create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA-CLIP-18B.json create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA-CLIP-8B-plus.json create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA-CLIP-8B.json create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA01-CLIP-B-16.json create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA01-CLIP-g-14-plus.json create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA01-CLIP-g-14.json create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA02-CLIP-B-16.json create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA02-CLIP-L-14-336.json create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA02-CLIP-L-14.json create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA02-CLIP-bigE-14-plus.json create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA02-CLIP-bigE-14.json create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/Internal-EVA02-CLIP-10B-14-448.json create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/Internal-EVA02-CLIP-10B-14.json create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/modified_resnet.py create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/openai.py create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/pretrained.py create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/rope.py create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/timm_model.py create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/tokenizer.py create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/transform.py create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/transformer.py create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_clip/utils.py create mode 100644 llava/model/multimodal_encoder/dev_eva_clip/eva_vit.py create mode 100644 llava/model/multimodal_encoder/eva_clip/eva_clip_encoder.py create mode 100644 llava/model/multimodal_encoder/eva_clip/eva_clip_processors.py create mode 100644 llava/model/multimodal_encoder/eva_clip/eva_vit.py create mode 100644 llava/model/multimodal_encoder/eva_clip/factory.py create mode 100644 llava/model/multimodal_encoder/eva_clip/model_configs/EVA-CLIP-18B.json create mode 100644 llava/model/multimodal_encoder/eva_clip/model_configs/EVA-CLIP-8B-plus.json create mode 100644 llava/model/multimodal_encoder/eva_clip/model_configs/EVA-CLIP-8B.json create mode 100644 llava/model/multimodal_encoder/eva_clip/model_configs/EVA01-CLIP-B-16.json create mode 100644 llava/model/multimodal_encoder/eva_clip/model_configs/EVA01-CLIP-g-14-plus.json create mode 100644 llava/model/multimodal_encoder/eva_clip/model_configs/EVA01-CLIP-g-14.json create mode 100644 llava/model/multimodal_encoder/eva_clip/model_configs/EVA02-CLIP-B-16.json create mode 100644 llava/model/multimodal_encoder/eva_clip/model_configs/EVA02-CLIP-L-14-336.json create mode 100644 llava/model/multimodal_encoder/eva_clip/model_configs/EVA02-CLIP-L-14.json create mode 100644 llava/model/multimodal_encoder/eva_clip/model_configs/EVA02-CLIP-bigE-14-plus.json create mode 100644 llava/model/multimodal_encoder/eva_clip/model_configs/EVA02-CLIP-bigE-14.json create mode 100644 llava/model/multimodal_encoder/eva_clip/model_configs/Internal-EVA02-CLIP-10B-14-448.json create mode 100644 llava/model/multimodal_encoder/eva_clip/model_configs/Internal-EVA02-CLIP-10B-14.json create mode 100644 llava/model/multimodal_encoder/hf_vision.py create mode 100644 llava/model/multimodal_encoder/imagebind.py create mode 100644 llava/model/multimodal_encoder/open_clip_encoder.py create mode 100644 llava/model/multimodal_encoder/siglip_encoder.py create mode 100644 llava/model/multimodal_projector/__pycache__/builder.cpython-310.pyc create mode 100644 llava/model/multimodal_projector/__pycache__/pooler_projector.cpython-310.pyc create mode 100644 llava/model/multimodal_projector/builder.py create mode 100644 llava/model/multimodal_projector/pooler_projector.py create mode 100644 llava/model/multimodal_resampler/__pycache__/builder.cpython-310.pyc create mode 100644 llava/model/multimodal_resampler/__pycache__/masked_drop.cpython-310.pyc create mode 100644 llava/model/multimodal_resampler/__pycache__/perceiver.cpython-310.pyc create mode 100644 llava/model/multimodal_resampler/__pycache__/qformer.cpython-310.pyc create mode 100644 llava/model/multimodal_resampler/__pycache__/spatial_pool.cpython-310.pyc create mode 100644 llava/model/multimodal_resampler/builder.py create mode 100644 llava/model/multimodal_resampler/masked_drop.py create mode 100644 llava/model/multimodal_resampler/perceiver.py create mode 100644 llava/model/multimodal_resampler/qformer.py create mode 100644 llava/model/multimodal_resampler/spatial_pool.py create mode 100644 llava/model/utils.py create mode 100644 llava/serve/__init__.py create mode 100644 llava/serve/cli.py create mode 100644 llava/serve/controller.py create mode 100644 llava/serve/examples/extreme_ironing.jpg create mode 100644 llava/serve/examples/waterview.jpg create mode 100644 llava/serve/gradio_multi_image.py create mode 100644 llava/serve/gradio_web_server.py create mode 100644 llava/serve/model_worker.py create mode 100644 llava/serve/register_worker.py create mode 100644 llava/serve/sglang_worker.py create mode 100644 llava/serve/test_message.py create mode 100644 llava/train/__pycache__/llava_trainer.cpython-310.pyc create mode 100644 llava/train/__pycache__/train.cpython-310.pyc create mode 100644 llava/train/llama_flash_attn_monkey_patch.py create mode 100644 llava/train/llava_trainer.py create mode 100644 llava/train/llava_trainer_eval.py create mode 100644 llava/train/train.py create mode 100644 llava/train/train_dpo.py create mode 100644 llava/train/train_mem.py create mode 100644 llava/utils.py create mode 100644 playground/2d_hist.py create mode 100644 playground/data_checker.py create mode 100644 playground/demo/video_demo.py create mode 100644 playground/demo/xU25MMA2N4aVtYay.mp4 create mode 100644 playground/equal_splitter.py create mode 100644 playground/remove_mid_ckpt.py create mode 100644 playground/sgl_llava_inference_multinode.py create mode 100644 playground/upload_data.py create mode 100644 pyproject.toml create mode 100644 requirements.txt create mode 100644 scripts/archived/convert_gqa_for_eval.py create mode 100644 scripts/archived/convert_mmvet_for_eval.py create mode 100644 scripts/archived/convert_sqa_to_llava.py create mode 100644 scripts/archived/convert_sqa_to_llava_base_prompt.py create mode 100644 scripts/archived/convert_vizwiz_for_submission.py create mode 100644 scripts/archived/convert_vqav2_for_submission.py create mode 100644 scripts/archived/data_info.py create mode 100644 scripts/archived/dpo_data_info.py create mode 100644 scripts/archived/entry_cmd.sh create mode 100644 scripts/archived/finetune.sh create mode 100644 scripts/archived/finetune_1.5.sh create mode 100644 scripts/archived/finetune_full_schedule.sh create mode 100644 scripts/archived/finetune_lora.sh create mode 100644 scripts/archived/finetune_mixtral.sh create mode 100644 scripts/archived/finetune_mixtral_1.5.sh create mode 100644 scripts/archived/finetune_mixtral_1.6_336px_anyres.sh create mode 100644 scripts/archived/finetune_mixtral_1.6_336px_anyres_freeze_vision.sh create mode 100644 scripts/archived/finetune_mixtral_1.6_336px_anyres_lmms_eval.sh create mode 100644 scripts/archived/finetune_mixtral_copy.sh create mode 100644 scripts/archived/finetune_qlora.sh create mode 100644 scripts/archived/finetune_sqa.sh create mode 100644 scripts/archived/merge_lora_weights.py create mode 100644 scripts/archived/pretrain.sh create mode 100644 scripts/archived/quick_check.py create mode 100644 scripts/archived/sqa_eval_batch.sh create mode 100644 scripts/archived/sqa_eval_gather.sh create mode 100644 scripts/interleave/eval_all.sh create mode 100644 scripts/interleave/eval_interleave_3d.sh create mode 100644 scripts/interleave/eval_multiprocess.sh create mode 100644 scripts/qwen.py create mode 100644 scripts/summarize_data.py create mode 100644 scripts/train/README.md create mode 100644 scripts/train/direct_finetune_clip.sh create mode 100644 scripts/train/direct_finetune_siglip_a4.sh create mode 100644 scripts/train/dpo.sh create mode 100644 scripts/train/dpo_ov7b.sh create mode 100644 scripts/train/finetune_ov_vla.sh create mode 100644 scripts/train/finetune_si.sh create mode 100644 scripts/train/mid_stage.yaml create mode 100644 scripts/train/onevision.yaml create mode 100644 scripts/train/pretrain_clip.sh create mode 100644 scripts/train/pretrain_siglip.sh create mode 100644 scripts/train/single_image.yaml create mode 100644 scripts/train/test.yaml create mode 100644 scripts/train/vla.yaml create mode 100644 scripts/zero2.json create mode 100644 scripts/zero2_fused_adamw.json create mode 100644 scripts/zero2_offload.json create mode 100644 scripts/zero3.json create mode 100644 scripts/zero3_offload.json create mode 100644 scripts/zero3pp.json create mode 100644 trl/__init__.py create mode 100644 trl/__pycache__/__init__.cpython-310.pyc create mode 100644 trl/__pycache__/core.cpython-310.pyc create mode 100644 trl/__pycache__/import_utils.cpython-310.pyc create mode 100644 trl/core.py create mode 100644 trl/environment/__init__.py create mode 100644 trl/environment/__pycache__/__init__.cpython-310.pyc create mode 100644 trl/environment/__pycache__/base_environment.cpython-310.pyc create mode 100644 trl/environment/base_environment.py create mode 100644 trl/extras/__init__.py create mode 100644 trl/extras/__pycache__/__init__.cpython-310.pyc create mode 100644 trl/extras/__pycache__/best_of_n_sampler.cpython-310.pyc create mode 100644 trl/extras/__pycache__/dataset_formatting.cpython-310.pyc create mode 100644 trl/extras/best_of_n_sampler.py create mode 100644 trl/extras/dataset_formatting.py create mode 100644 trl/import_utils.py create mode 100644 trl/models/__init__.py create mode 100644 trl/models/__pycache__/__init__.cpython-310.pyc create mode 100644 trl/models/__pycache__/modeling_base.cpython-310.pyc create mode 100644 trl/models/__pycache__/modeling_value_head.cpython-310.pyc create mode 100644 trl/models/__pycache__/utils.cpython-310.pyc create mode 100644 trl/models/modeling_base.py create mode 100644 trl/models/modeling_sd_base.py create mode 100644 trl/models/modeling_value_head.py create mode 100644 trl/models/utils.py create mode 100644 trl/trainer/__init__.py create mode 100644 trl/trainer/__pycache__/__init__.cpython-310.pyc create mode 100644 trl/trainer/__pycache__/base.cpython-310.pyc create mode 100644 trl/trainer/__pycache__/ddpo_config.cpython-310.pyc create mode 100644 trl/trainer/__pycache__/dpo_trainer.cpython-310.pyc create mode 100644 trl/trainer/__pycache__/iterative_sft_trainer.cpython-310.pyc create mode 100644 trl/trainer/__pycache__/model_config.cpython-310.pyc create mode 100644 trl/trainer/__pycache__/ppo_config.cpython-310.pyc create mode 100644 trl/trainer/__pycache__/ppo_trainer.cpython-310.pyc create mode 100644 trl/trainer/__pycache__/reward_config.cpython-310.pyc create mode 100644 trl/trainer/__pycache__/reward_trainer.cpython-310.pyc create mode 100644 trl/trainer/__pycache__/sft_trainer.cpython-310.pyc create mode 100644 trl/trainer/__pycache__/utils.cpython-310.pyc create mode 100644 trl/trainer/base.py create mode 100644 trl/trainer/ddpo_config.py create mode 100644 trl/trainer/ddpo_trainer.py create mode 100644 trl/trainer/dpo_trainer.py create mode 100644 trl/trainer/iterative_sft_trainer.py create mode 100644 trl/trainer/model_config.py create mode 100644 trl/trainer/ppo_config.py create mode 100644 trl/trainer/ppo_trainer.py create mode 100644 trl/trainer/reward_config.py create mode 100644 trl/trainer/reward_trainer.py create mode 100644 trl/trainer/sft_trainer.py create mode 100644 trl/trainer/utils.py diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/inference.py b/inference.py new file mode 100644 index 0000000..60bdc62 --- /dev/null +++ b/inference.py @@ -0,0 +1,59 @@ +from llava.model.builder import load_pretrained_model +from llava.mm_utils import get_model_name_from_path, process_images, tokenizer_image_token +from llava.constants import IMAGE_TOKEN_INDEX, DEFAULT_IMAGE_TOKEN, DEFAULT_IM_START_TOKEN, DEFAULT_IM_END_TOKEN, IGNORE_INDEX +from llava.conversation import conv_templates, SeparatorStyle +from llava.actiontokenizer import ActionTokenizer +from PIL import Image +import requests +import copy +import torch + +import sys +import warnings + +warnings.filterwarnings("ignore") +pretrained = "/mnt/d/ckpt2/llava-openvla-qwen" +model_name = "llava_qwen" +device = "cuda" +device_map = "auto" +tokenizer, model, image_processor, max_length = load_pretrained_model(pretrained, None, model_name, device_map=device_map) # Add any other thing you want to pass in llava_model_args +actiontokenizer = ActionTokenizer(tokenizer) + +print(model.model.embed_tokens.weight.shape) +print(model.lm_head.weight.shape) + +model.eval() +image_path = '/root/code/openvla-llava/LLaVA-Next/images/images/random_image.jpg' +image = Image.open(image_path) +image_tensor = process_images([image], image_processor, model.config) +image_tensor = [_image.to(dtype=torch.float16, device=device) for _image in image_tensor] + +conv_template = "qwen_1_5" +question = DEFAULT_IMAGE_TOKEN + "\n, the next position is:" +conv = copy.deepcopy(conv_templates[conv_template]) +conv.append_message(conv.roles[0], question) +conv.append_message(conv.roles[1], None) +prompt_question = conv.get_prompt() + +input_ids = tokenizer_image_token(prompt_question, tokenizer, IMAGE_TOKEN_INDEX, return_tensors="pt").unsqueeze(0).to(device) +image_sizes = [image.size] + +cont = model.generate( + input_ids, + images=image_tensor, + image_sizes=image_sizes, + do_sample=False, + temperature=0, + max_new_tokens=4096, +) +token_ids = cont.cpu().tolist()[0] +decoded_text = [] + +for token_id in token_ids: + if 151647 <= token_id <= 151903: + decoded_token = actiontokenizer.decode_token_ids_to_actions([token_id]) + else: + decoded_token = tokenizer.decode([token_id]) + decoded_text.append(decoded_token) +final_text = ''.join(str(item) for item in decoded_text) +print(final_text) diff --git a/llava.egg-info/PKG-INFO b/llava.egg-info/PKG-INFO new file mode 100644 index 0000000..c655957 --- /dev/null +++ b/llava.egg-info/PKG-INFO @@ -0,0 +1,299 @@ +Metadata-Version: 2.4 +Name: llava +Version: 1.7.0.dev0 +Summary: LLaVA OneVision: The Next Generation of LLaVA with Better Image and Video Understanding Capabilities +Project-URL: Homepage, https://llava-vl.github.io +Project-URL: Bug Tracker, https://github.com/haotian-liu/LLaVA/issues +Classifier: Programming Language :: Python :: 3 +Classifier: License :: OSI Approved :: Apache Software License +Requires-Python: >=3.8 +Description-Content-Type: text/markdown +License-File: LICENSE +Provides-Extra: standalone +Requires-Dist: shortuuid; extra == "standalone" +Requires-Dist: httpx==0.24.0; extra == "standalone" +Requires-Dist: einops; extra == "standalone" +Requires-Dist: ftfy; extra == "standalone" +Provides-Extra: train +Requires-Dist: llava[standalone]; extra == "train" +Requires-Dist: numpy==1.26.1; extra == "train" +Requires-Dist: open_clip_torch; extra == "train" +Requires-Dist: fastapi; extra == "train" +Requires-Dist: markdown2[all]; extra == "train" +Requires-Dist: numpy; extra == "train" +Requires-Dist: requests; extra == "train" +Requires-Dist: sentencepiece; extra == "train" +Requires-Dist: torch==2.1.2; extra == "train" +Requires-Dist: torchvision==0.16.2; extra == "train" +Requires-Dist: uvicorn; extra == "train" +Requires-Dist: wandb; extra == "train" +Requires-Dist: deepspeed==0.14.4; extra == "train" +Requires-Dist: peft==0.4.0; extra == "train" +Requires-Dist: accelerate>=0.29.1; extra == "train" +Requires-Dist: tokenizers~=0.15.2; extra == "train" +Requires-Dist: transformers@ git+https://github.com/huggingface/transformers.git@1c39974a4c4036fd641bc1191cc32799f85715a4 ; extra == "train" +Requires-Dist: bitsandbytes==0.41.0; extra == "train" +Requires-Dist: scikit-learn==1.2.2; extra == "train" +Requires-Dist: sentencepiece~=0.1.99; extra == "train" +Requires-Dist: einops==0.6.1; extra == "train" +Requires-Dist: einops-exts==0.0.4; extra == "train" +Requires-Dist: gradio_client==0.2.9; extra == "train" +Requires-Dist: urllib3<=2.0.0; extra == "train" +Requires-Dist: datasets==2.16.1; extra == "train" +Requires-Dist: pydantic==1.10.8; extra == "train" +Requires-Dist: timm; extra == "train" +Requires-Dist: hf_transfer; extra == "train" +Requires-Dist: opencv-python; extra == "train" +Requires-Dist: av; extra == "train" +Requires-Dist: decord; extra == "train" +Requires-Dist: tyro; extra == "train" +Requires-Dist: scipy; extra == "train" +Dynamic: license-file + +

+ +

+ +[Darren-greenhand/LLaVA_OpenVLA: Converted the training data of OpenVLA into general form of multimodal training instructions and then used with LLaVA-OneVision](https://github.com/Darren-greenhand/LLaVA_OpenVLA/tree/main) + +👆LLaVA_OpenVLA项目的part3 + +* 主要是实现了action tokenizer,能够解析part2处理好的数据里的 action tensor(target) + +ps:详细实现可见笔记:https://darren-dong.notion.site/OpenVLA-LLaVA-11a471fbaea480839ee6ca55f122a187?pvs=4:笔记-> +LLaVA-OpenVLA库详细实现 -> 迭代式dataloader + 保存 + +# LLaVA-NeXT: Open Large Multimodal Models + +[![Static Badge](https://img.shields.io/badge/llava_video-paper-green)](http://arxiv.org/abs/2410.02713) +[![Static Badge](https://img.shields.io/badge/llava_onevision-paper-green)](https://arxiv.org/abs/2408.03326) +[![llava_next-blog](https://img.shields.io/badge/llava_next-blog-green)](https://llava-vl.github.io/blog/) + +[![llava_onevision-demo](https://img.shields.io/badge/llava_onevision-demo-red)](https://llava-onevision.lmms-lab.com/) +[![llava_next-video_demo](https://img.shields.io/badge/llava_video-demo-red)](https://huggingface.co/spaces/WildVision/vision-arena) +[![llava_next-interleave_demo](https://img.shields.io/badge/llava_next-interleave_demo-red)](https://huggingface.co/spaces/lmms-lab/LLaVA-NeXT-Interleave-Demo) +[![Openbayes Demo](https://img.shields.io/static/v1?label=Demo&message=OpenBayes%E8%B4%9D%E5%BC%8F%E8%AE%A1%E7%AE%97&color=green)](https://openbayes.com/console/public/tutorials/gW0ng9jKXfO) + +[![llava_video-checkpoints](https://img.shields.io/badge/llava_video-checkpoints-blue)](https://huggingface.co/collections/lmms-lab/llava-next-video-661e86f5e8dabc3ff793c944) +[![llava_onevision-checkpoints](https://img.shields.io/badge/llava_onevision-checkpoints-blue)](https://huggingface.co/collections/lmms-lab/llava-onevision-66a259c3526e15166d6bba37) +[![llava_next-interleave_checkpoints](https://img.shields.io/badge/llava_next-interleave_checkpoints-blue)](https://huggingface.co/collections/lmms-lab/llava-next-interleave-66763c55c411b340b35873d1) +[![llava_next-image_checkpoints](https://img.shields.io/badge/llava_next-image_checkpoints-blue)](https://huggingface.co/lmms-lab) + +## Release Notes + +- **[2024/10/04] 🔥 LLaVA-Video** (formerly LLaVA-NeXT-Video) has undergone a major upgrade! We are excited to release **LLaVA-Video-178K**, a high-quality synthetic dataset for video instruction tuning. This dataset includes: + + - 178,510 caption entries + - 960,792 open-ended Q&A pairs + - 196,198 multiple-choice Q&A items + + Along with this, we’re also releasing the **LLaVA-Video 7B/72B models**, which deliver competitive performance on the latest video benchmarks, including [Video-MME](https://video-mme.github.io/home_page.html#leaderboard), [LongVideoBench](https://longvideobench.github.io/), and [Dream-1K](https://tarsier-vlm.github.io/). + + 📄 **Explore more**: + - [LLaVA-Video-178K Dataset](https://huggingface.co/datasets/lmms-lab/LLaVA-Video-178K): Download the dataset. + - [LLaVA-Video Models](https://huggingface.co/collections/lmms-lab/llava-video-661e86f5e8dabc3ff793c944): Access model checkpoints. + - [Paper](http://arxiv.org/abs/2410.02713): Detailed information about LLaVA-Video. + - [LLaVA-Video Documentation](https://github.com/LLaVA-VL/LLaVA-NeXT/blob/main/docs/LLaVA_Video_1003.md): Guidance on training, inference and evaluation. + +- [2024/09/13] 🔥 **🚀 [LLaVA-OneVision-Chat](docs/LLaVA_OneVision_Chat.md)**. The new LLaVA-OV-Chat (7B/72B) significantly improves the chat experience of LLaVA-OV. 📄 + + ![](docs/ov_chat_images/chat_results.png) + +- [2024/08/06] 🔥 **🚀 [LLaVA-OneVision (OV)](https://llava-vl.github.io/blog/2024-08-05-llava-onevision/)!** The new LLaVA-OV models (0.5B/7B/72B) achieve new state-of-the-art performance across single-image, multi-image, and video benchmarks, sometimes rivaling top commercial models on 47 diverse benchmarks. 📄 Explore More: + * [[Paper]](https://arxiv.org/abs/2408.03326): In-depth insights, new emegerging scenarios, ie, strong video understadning through task transfer from images. + * [[LLaVA-OV Doc]](https://github.com/LLaVA-VL/LLaVA-NeXT/blob/main/docs/LLaVA_OneVision.md): Model inference and evaluation guidance. + * [[Scripts]](https://github.com/LLaVA-VL/LLaVA-NeXT/blob/main/scripts/train): Start training models on your single-image/multi-image/video data. + +- [2024/07/16] 🔥 **LLaVA-NeXT-Video** has been upgraded. The new 32B model achieves the best open-source performance on several video benchmarks, including [Video-MME](https://video-mme.github.io/home_page.html#leaderboard). Please refer to [this page](docs/LLaVA-NeXT-Video_0716.md) for details, refer to [llava_next-video_demo](https://huggingface.co/spaces/WildVision/vision-arena) for demo. + + +- [2024/06/23] 🔥 **LLaVA-NeXT-Interleave** is released. We utilize image-text interleaved format to unify multi-image, video, and 3D tasks in one LLM and achieve **SoTA** performance on a wide range of benchmarks. Check out [paper](https://arxiv.org/pdf/2407.07895), [blog](https://llava-vl.github.io/blog/2024-06-16-llava-next-interleave/), and [checkpoints](https://huggingface.co/collections/lmms-lab/llava-next-interleave-66763c55c411b340b35873d1) to see new capabilities and improved performance! We have released 0.5b, 7b, and 7b-dpo models. + * An all-round LLM for multi-image, video, and 3D with strong performance \[[demo](https://huggingface.co/spaces/lmms-lab/LLaVA-NeXT-Interleave-Demo)\] + * Construct interleave training data [**M4-Instruct**](https://huggingface.co/datasets/lmms-lab/M4-Instruct-Data) + * Construct multi-image benchmark [**LLaVA-Interleave Bench**](https://huggingface.co/datasets/lmms-lab/LLaVA-NeXT-Interleave-Bench) + + +- [2024/05/25] 🔥 Wondering "[What Else Influences Visual Instruction Tuning Beyond Data?](https://llava-vl.github.io/blog/2024-05-25-llava-next-ablations/)" Our new [blog](https://llava-vl.github.io/blog/2024-05-25-llava-next-ablations/) summarizes empirical explorations to ablate the various design choices in improving LMMs except instruct data itself. Meanwhile, open-source the recapioned high-quality data using LLaVA-NeXT-34B on [[COCO]](https://huggingface.co/datasets/lmms-lab/LLaVA-ReCap-118K) [[LCS]](https://huggingface.co/datasets/lmms-lab/LLaVA-ReCap-558K) [[CC3M]](https://huggingface.co/datasets/lmms-lab/LLaVA-ReCap-CC3M). + * Architectures (LMM & Vision Encoder) + * Visual Representations (Resolution & # Tokens) + * Training Strategies (High-quality data & Trainable modules) + +- [2024/05/10] 🔥 **LLaVA-NeXT** (Stronger) models are released, with support of stronger LMM inlcuding LLama-3 (8B) and Qwen-1.5 (72B/110B) Check out [[blog](https://llava-vl.github.io/blog/2024-05-10-llava-next-stronger-llms/)] and [[checkpoints](https://huggingface.co/lmms-lab)] to see improved performance! +- [2024/05/10] 🔥 **LLaVA-NeXT** (Video) is released. The image-only-trained LLaVA-NeXT model is surprisingly strong on video tasks with zero-shot modality transfer. DPO training with AI feedback on videos can yield significant improvement. [[Blog](https://llava-vl.github.io/blog/2024-04-30-llava-next-video/)], [[checkpoints](https://huggingface.co/collections/lmms-lab/llava-next-video-661e86f5e8dabc3ff793c944)] and [[sglang](https://github.com/sgl-project/sglang)] +- [2024/01/30] 🔥 **LLaVA-NeXT** is out! With additional scaling to LLaVA-1.5, LLaVA-NeXT-34B outperforms Gemini Pro on some benchmarks. It can now process 4x more pixels and perform more tasks/applications than before. Check out the [blog post](https://llava-vl.github.io/blog/2024-01-30-llava-next/), and explore the [demo](https://llava.hliu.cc/)! Models are available in [Model Zoo](https://github.com/haotian-liu/LLaVA/blob/main/docs/MODEL_ZOO.md). Training/eval data and scripts coming soon. +
+More + +- [2024/03/10] 🔥 Releasing **LMMs-Eval**, a highly efficient evaluation pipeline we used when developing LLaVA-NeXT. It supports the evaluation of LMMs on dozens of public datasets and allows new dataset onboarding, making the dev of new LMMs much faster. [[Blog](https://lmms-lab.github.io/lmms-eval-blog/lmms-eval-0.1/)] [[Codebase](https://github.com/EvolvingLMMs-Lab/lmms-eval)] + +- [2023/11/10] [LLaVA-Plus](https://llava-vl.github.io/llava-plus/) is released: Learning to Use Tools for Creating Multimodal Agents, with LLaVA-Plus (LLaVA that Plug and Learn to Use Skills). [[Project Page](https://llava-vl.github.io/llava-plus/)] [[Demo](https://llavaplus.ngrok.io/)] [[Code](https://github.com/LLaVA-VL/LLaVA-Plus-Codebase)] [[Paper](https://arxiv.org/abs/2311.05437)] +- [2023/11/02] [LLaVA-Interactive](https://llava-vl.github.io/llava-interactive/) is released: Experience the future of human-AI multimodal interaction with an all-in-one demo for Image Chat, Segmentation, Generation and Editing. [[Project Page](https://llava-vl.github.io/llava-interactive/)] [[Demo](https://llavainteractive.ngrok.io/)] [[Code](https://github.com/LLaVA-VL/LLaVA-Interactive-Demo)] [[Paper](https://arxiv.org/abs/2311.00571)] +- [2023/10/26] 🔥 LLaVA-1.5 with LoRA achieves comparable performance as full-model finetuning, with a reduced GPU RAM requirement ([ckpts](https://github.com/haotian-liu/LLaVA/blob/main/docs/MODEL_ZOO.md#llava-v15), [script](https://github.com/haotian-liu/LLaVA#train)). We also provide a [doc](https://github.com/haotian-liu/LLaVA/blob/main/docs/Finetune_Custom_Data.md) on how to finetune LLaVA-1.5 on your own dataset with LoRA. +- [2023/10/12] Check out the Korean LLaVA (Ko-LLaVA), created by ETRI, who has generously supported our research! [[🤗 Demo](https://huggingface.co/spaces/etri-vilab/Ko-LLaVA)] +- [2023/10/05] 🔥 LLaVA-1.5 is out! Achieving SoTA on 11 benchmarks, with just simple modifications to the original LLaVA, utilizes all public data, completes training in ~1 day on a single 8-A100 node, and surpasses methods like Qwen-VL-Chat that use billion-scale data. Check out the [technical report](https://arxiv.org/abs/2310.03744), and explore the [demo](https://llava.hliu.cc/)! Models are available in [Model Zoo](https://github.com/haotian-liu/LLaVA/blob/main/docs/MODEL_ZOO.md). The training data and scripts of LLaVA-1.5 are released [here](https://github.com/haotian-liu/LLaVA#train), and evaluation scripts are released [here](https://github.com/haotian-liu/LLaVA/blob/main/docs/Evaluation.md)! +- [2023/09/26] LLaVA is improved with reinforcement learning from human feedback (RLHF) to improve fact grounding and reduce hallucination. Check out the new SFT and RLHF checkpoints at project [[LLavA-RLHF]](https://llava-rlhf.github.io/) +- [2023/09/22] [LLaVA](https://arxiv.org/abs/2304.08485) is accepted by NeurIPS 2023 as **oral presentation**, and [LLaVA-Med](https://arxiv.org/abs/2306.00890) is accepted by NeurIPS 2023 Datasets and Benchmarks Track as **spotlight presentation**. +- [2023/11/06] Support **Intel** dGPU and CPU platforms. [More details here.](https://github.com/haotian-liu/LLaVA/tree/intel/docs/intel) +- [2023/10/12] LLaVA is now supported in [llama.cpp](https://github.com/ggerganov/llama.cpp/pull/3436) with 4-bit / 5-bit quantization support! +- [2023/10/11] The training data and scripts of LLaVA-1.5 are released [here](https://github.com/haotian-liu/LLaVA#train), and evaluation scripts are released [here](https://github.com/haotian-liu/LLaVA/blob/main/docs/Evaluation.md)! +- [2023/10/10] [Roboflow Deep Dive](https://blog.roboflow.com/first-impressions-with-llava-1-5/): First Impressions with LLaVA-1.5. +- [2023/09/20] We summarize our empirical study of training 33B and 65B LLaVA models in a [note](https://arxiv.org/abs/2309.09958). Further, if you are interested in the comprehensive review, evolution and trend of multimodal foundation models, please check out our recent survey paper [``Multimodal Foundation Models: From Specialists to General-Purpose Assistants''.](https://arxiv.org/abs/2309.10020) +

+ +

+ +- [2023/07/19] 🔥 We release a major upgrade, including support for LLaMA-2, LoRA training, 4-/8-bit inference, higher resolution (336x336), and a lot more. We release [LLaVA Bench](https://github.com/haotian-liu/LLaVA/blob/main/docs/LLaVA_Bench.md) for benchmarking open-ended visual chat with results from Bard and Bing-Chat. We also support and verify training with RTX 3090 and RTX A6000. Check out [LLaVA-from-LLaMA-2](https://github.com/haotian-liu/LLaVA/blob/main/docs/LLaVA_from_LLaMA2.md), and our [model zoo](https://github.com/haotian-liu/LLaVA/blob/main/docs/MODEL_ZOO.md)! +- [2023/06/26] [CVPR 2023 Tutorial](https://vlp-tutorial.github.io/) on **Large Multimodal Models: Towards Building and Surpassing Multimodal GPT-4**! Please check out [[Slides](https://datarelease.blob.core.windows.net/tutorial/vision_foundation_models_2023/slides/Chunyuan_cvpr2023_tutorial_lmm.pdf)] [[Notes](https://arxiv.org/abs/2306.14895)] [[YouTube](https://youtu.be/mkI7EPD1vp8)] [[Bilibli](https://www.bilibili.com/video/BV1Ng4y1T7v3/)]. +- [2023/06/11] We released the preview for the most requested feature: DeepSpeed and LoRA support! Please see documentations [here](./docs/LoRA.md). +- [2023/06/01] We released **LLaVA-Med: Large Language and Vision Assistant for Biomedicine**, a step towards building biomedical domain large language and vision models with GPT-4 level capabilities. Checkout the [paper](https://arxiv.org/abs/2306.00890) and [page](https://github.com/microsoft/LLaVA-Med). +- [2023/05/06] We are releasing [LLaVA-Lighting-MPT-7B-preview](https://huggingface.co/liuhaotian/LLaVA-Lightning-MPT-7B-preview), based on MPT-7B-Chat! See [here](#LLaVA-MPT-7b) for more details. +- [2023/05/02] 🔥 We are releasing LLaVA-Lighting! Train a lite, multimodal GPT-4 with just $40 in 3 hours! See [here](#train-llava-lightning) for more details. +- [2023/04/27] Thanks to the community effort, LLaVA-13B with 4-bit quantization allows you to run on a GPU with as few as 12GB VRAM! Try it out [here](https://github.com/oobabooga/text-generation-webui/tree/main/extensions/llava). +- [2023/04/17] 🔥 We released **LLaVA: Large Language and Vision Assistant**. We propose visual instruction tuning, towards building large language and vision models with GPT-4 level capabilities. Checkout the [paper](https://arxiv.org/abs/2304.08485) and [demo](https://llava.hliu.cc/). + +
+ + + +**Usage and License Notices**: This project utilizes certain datasets and checkpoints that are subject to their respective original licenses. Users must comply with all terms and conditions of these original licenses, including but not limited to the [OpenAI Terms of Use](https://openai.com/policies/terms-of-use) for the dataset and the specific licenses for base language models for checkpoints trained using the dataset (e.g. [Llama-1/2 community license](https://ai.meta.com/llama/license/) for LLaMA-2 and Vicuna-v1.5, [Tongyi Qianwen RESEARCH LICENSE AGREEMENT](https://huggingface.co/Qwen/Qwen1.5-0.5B-Chat/blob/main/LICENSE) and [Llama-3 Research License](https://llama.meta.com/llama3/license/)). This project does not impose any additional constraints beyond those stipulated in the original licenses. Furthermore, users are reminded to ensure that their use of the dataset and checkpoints is in compliance with all applicable laws and regulations. + +## Models & Scripts + +### Installation + +#### 1. **Clone this repository and navigate to the LLaVA folder:** +```bash +git clone https://github.com/LLaVA-VL/LLaVA-NeXT +cd LLaVA-NeXT +``` + +#### 2. **Install the inference package:** +```bash +conda create -n llava python=3.10 -y +conda activate llava +pip install --upgrade pip # Enable PEP 660 support. +pip install -e ".[train]" +``` + +### Project Navigation +Please checkout the following page for more inference & evaluation details. + +#### - **LLaVA-OneVision: Easy Task Transfer** +- [LLaVA-OneVision]([./docs/LLaVA-NeXT.md](https://github.com/LLaVA-VL/LLaVA-NeXT/blob/main/docs/LLaVA_OneVision.md)): for demo inference. The evaluation code is in [lmms-eval](https://github.com/EvolvingLMMs-Lab/lmms-eval). + +#### - **LLaVA-NeXT: Stronger LLMs Supercharge Multimodal Capabilities in the Wild** +- [LLaVA-NeXT-Image](./docs/LLaVA-NeXT.md): for image demo inference and evaluation of stronger LMMs using [lmms-eval](https://github.com/EvolvingLMMs-Lab/lmms-eval). + + +#### - LLaVA-NeXT: A Strong Zero-shot Video Understanding Model +- [LLaVA-NeXT-Video](./docs/LLaVA-NeXT-Video.md): for video inference and evaluation scripts. We recommend to use [LMMs-video](https://lmms-lab.github.io/posts/lmms-eval-0.2/) for evaluation. + +#### - LLaVA-NeXT: Tackling Multi-image, Video, and 3D in Large Multimodal Models +- [LLaVA-NeXT-Interleave](./docs/LLaVA-NeXT-Interleave.md): for multi-image demo and evaluation scripts. + +## SGLang for SpeedUp Inference and Deployment + +We use [SGLang](https://github.com/sgl-project/sglang) to speed up inference and deployment of LLaVA-NeXT. You could make LLaVA-NeXT as a backend API service with SGLang. + +**Prepare Environment**: + Following the instruction in the [sglang](https://github.com/sgl-project/sglang?tab=readme-ov-file#install) + +### LLaVA-NeXT/OneVision + +Checkout the HTTP Post/Get and SRT usage at [sglang/examples/runtime/llava_onevision](https://github.com/sgl-project/sglang/tree/main/examples/runtime/llava_onevision) + +### LLaVA-NeXT (Video) + +**Launch and Run on (K) Nodes**: +- Go to sglang project + ``` + cd PATH_TO/sglang + ``` +- First node: + ```sh + bash examples/usage/llava_video/srt_example_llava_v.sh K 0 YOUR_VIDEO_PATH YOUR_MODEL_PATH FRAMES_PER_VIDEO + (e.g. bash examples/usage/llava_video/srt_example_llava_v.sh K 0 examples/usage/llava_video/videos/Q98Z4OTh8RwmDonc.mp4 lmms-lab/LLaVA-NeXT-Video-7B-DPO 16) + ``` +- Second node: + ```sh + bash examples/usage/llava_video/srt_example_llava_v.sh K 1 YOUR_VIDEO_PATH YOUR_MODEL_PATH FRAMES_PER_VIDEO + ``` +- The K node: + ```sh + bash examples/usage/llava_video/srt_example_llava_v.sh K K-1 YOUR_VIDEO_PATH YOUR_MODEL_PATH FRAMES_PER_VIDEO + ``` + + +## Citation + +If you find it useful for your research and applications, please cite related papers/blogs using this BibTeX: +```bibtex +@article{li2024llava, + title={LLaVA-NeXT-Interleave: Tackling Multi-image, Video, and 3D in Large Multimodal Models}, + author={Li, Feng and Zhang, Renrui and Zhang, Hao and Zhang, Yuanhan and Li, Bo and Li, Wei and Ma, Zejun and Li, Chunyuan}, + journal={arXiv preprint arXiv:2407.07895}, + year={2024} +} + +@misc{li2024llavanext-ablations, + title={LLaVA-NeXT: What Else Influences Visual Instruction Tuning Beyond Data?}, + url={https://llava-vl.github.io/blog/2024-05-25-llava-next-ablations/}, + author={Li, Bo and Zhang, Hao and Zhang, Kaichen and Guo, Dong and Zhang, Yuanhan and Zhang, Renrui and Li, Feng and Liu, Ziwei and Li, Chunyuan}, + month={May}, + year={2024} +} + +@misc{li2024llavanext-strong, + title={LLaVA-NeXT: Stronger LLMs Supercharge Multimodal Capabilities in the Wild}, + url={https://llava-vl.github.io/blog/2024-05-10-llava-next-stronger-llms/}, + author={Li, Bo and Zhang, Kaichen and Zhang, Hao and Guo, Dong and Zhang, Renrui and Li, Feng and Zhang, Yuanhan and Liu, Ziwei and Li, Chunyuan}, + month={May}, + year={2024} +} + +@misc{zhang2024llavanext-video, + title={LLaVA-NeXT: A Strong Zero-shot Video Understanding Model}, + url={https://llava-vl.github.io/blog/2024-04-30-llava-next-video/}, + author={Zhang, Yuanhan and Li, Bo and Liu, haotian and Lee, Yong jae and Gui, Liangke and Fu, Di and Feng, Jiashi and Liu, Ziwei and Li, Chunyuan}, + month={April}, + year={2024} +} + +@misc{liu2024llavanext, + title={LLaVA-NeXT: Improved reasoning, OCR, and world knowledge}, + url={https://llava-vl.github.io/blog/2024-01-30-llava-next/}, + author={Liu, Haotian and Li, Chunyuan and Li, Yuheng and Li, Bo and Zhang, Yuanhan and Shen, Sheng and Lee, Yong Jae}, + month={January}, + year={2024} +} + +@misc{liu2023improvedllava, + title={Improved Baselines with Visual Instruction Tuning}, + author={Liu, Haotian and Li, Chunyuan and Li, Yuheng and Lee, Yong Jae}, + publisher={arXiv:2310.03744}, + year={2023}, +} + +@misc{liu2023llava, + title={Visual Instruction Tuning}, + author={Liu, Haotian and Li, Chunyuan and Wu, Qingyang and Lee, Yong Jae}, + publisher={NeurIPS}, + year={2023}, +} +``` + +## Acknowledgement + +- [Vicuna](https://github.com/lm-sys/FastChat): the codebase we built upon, and our base model Vicuna-13B that has the amazing language capabilities! +- The LLaVA-NeXT project is currently maintained by the team along with our contributors (listed alphabetically by the first names): [Bo Li](https://brianboli.com/), [Dong Guo](https://www.linkedin.com/in/dongguoset/), [Feng Li](https://scholar.google.com/citations?hl=zh-CN&user=ybRe9GcAAAAJ&view_op=list_works&sortby=pubdate), [Hao Zhang](https://scholar.google.com/citations?user=B8hPxMQAAAAJ&hl=en), [Kaichen Zhang](https://www.linkedin.com/in/kaichen-zhang-014b17219/?originalSubdomain=sg), [Renrui Zhang](https://zrrskywalker.github.io/), [Yuanhan Zhang](https://zhangyuanhan-ai.github.io/), led by [Chunyuan Li](https://chunyuan.li/) and with the guidance and help from [Haotian Liu](https://hliu.cc/). +- The `lmms-eval` framework and its core contributors, including Peiyuan Zhang, Fanyi Pu, Joshua Adrian Cahyono, and Kairui Hu, for their support on the evaluation side. + +## Related Projects + +- [Instruction Tuning with GPT-4](https://github.com/Instruction-Tuning-with-GPT-4/GPT-4-LLM) +- [LLaVA-Med: Training a Large Language-and-Vision Assistant for Biomedicine in One Day](https://github.com/microsoft/LLaVA-Med) +- [Otter: In-Context Multi-Modal Instruction Tuning](https://github.com/Luodian/Otter) + +For future project ideas, please check out: +- [SEEM: Segment Everything Everywhere All at Once](https://github.com/UX-Decoder/Segment-Everything-Everywhere-All-At-Once) +- [Grounded-Segment-Anything](https://github.com/IDEA-Research/Grounded-Segment-Anything) to detect, segment, and generate anything by marrying [Grounding DINO](https://github.com/IDEA-Research/GroundingDINO) and [Segment-Anything](https://github.com/facebookresearch/segment-anything). diff --git a/llava.egg-info/SOURCES.txt b/llava.egg-info/SOURCES.txt new file mode 100644 index 0000000..d21c309 --- /dev/null +++ b/llava.egg-info/SOURCES.txt @@ -0,0 +1,107 @@ +LICENSE +README.md +pyproject.toml +llava/__init__.py +llava/actiontokenizer.py +llava/constants.py +llava/conversation.py +llava/mm_utils.py +llava/utils.py +llava.egg-info/PKG-INFO +llava.egg-info/SOURCES.txt +llava.egg-info/dependency_links.txt +llava.egg-info/requires.txt +llava.egg-info/top_level.txt +llava/eval/evaluate_interleave.py +llava/eval/model_vqa.py +llava/model/__init__.py +llava/model/apply_delta.py +llava/model/builder.py +llava/model/consolidate.py +llava/model/llava_arch.py +llava/model/make_delta.py +llava/model/utils.py +llava/model/language_model/llava_gemma.py +llava/model/language_model/llava_llama.py +llava/model/language_model/llava_mistral.py +llava/model/language_model/llava_mixtral.py +llava/model/language_model/llava_mpt.py +llava/model/language_model/llava_qwen.py +llava/model/language_model/llava_qwen_moe.py +llava/model/language_model/modeling_llama.py +llava/model/multimodal_encoder/builder.py +llava/model/multimodal_encoder/clip_encoder.py +llava/model/multimodal_encoder/hf_vision.py +llava/model/multimodal_encoder/imagebind.py +llava/model/multimodal_encoder/open_clip_encoder.py +llava/model/multimodal_encoder/siglip_encoder.py +llava/model/multimodal_encoder/dev_eva_clip/eva_vit.py +llava/model/multimodal_encoder/dev_eva_clip/eva_clip/__init__.py +llava/model/multimodal_encoder/dev_eva_clip/eva_clip/constants.py +llava/model/multimodal_encoder/dev_eva_clip/eva_clip/eva_vit_model.py +llava/model/multimodal_encoder/dev_eva_clip/eva_clip/factory.py +llava/model/multimodal_encoder/dev_eva_clip/eva_clip/hf_configs.py +llava/model/multimodal_encoder/dev_eva_clip/eva_clip/hf_model.py +llava/model/multimodal_encoder/dev_eva_clip/eva_clip/loss.py +llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model.py +llava/model/multimodal_encoder/dev_eva_clip/eva_clip/modified_resnet.py +llava/model/multimodal_encoder/dev_eva_clip/eva_clip/openai.py +llava/model/multimodal_encoder/dev_eva_clip/eva_clip/pretrained.py +llava/model/multimodal_encoder/dev_eva_clip/eva_clip/rope.py +llava/model/multimodal_encoder/dev_eva_clip/eva_clip/timm_model.py +llava/model/multimodal_encoder/dev_eva_clip/eva_clip/tokenizer.py +llava/model/multimodal_encoder/dev_eva_clip/eva_clip/transform.py +llava/model/multimodal_encoder/dev_eva_clip/eva_clip/transformer.py +llava/model/multimodal_encoder/dev_eva_clip/eva_clip/utils.py +llava/model/multimodal_encoder/eva_clip/eva_clip_encoder.py +llava/model/multimodal_encoder/eva_clip/eva_clip_processors.py +llava/model/multimodal_encoder/eva_clip/eva_vit.py +llava/model/multimodal_encoder/eva_clip/factory.py +llava/model/multimodal_projector/builder.py +llava/model/multimodal_projector/pooler_projector.py +llava/model/multimodal_resampler/builder.py +llava/model/multimodal_resampler/masked_drop.py +llava/model/multimodal_resampler/perceiver.py +llava/model/multimodal_resampler/qformer.py +llava/model/multimodal_resampler/spatial_pool.py +llava/serve/__init__.py +llava/serve/cli.py +llava/serve/controller.py +llava/serve/gradio_multi_image.py +llava/serve/gradio_web_server.py +llava/serve/model_worker.py +llava/serve/register_worker.py +llava/serve/sglang_worker.py +llava/serve/test_message.py +llava/train/llama_flash_attn_monkey_patch.py +llava/train/llava_trainer.py +llava/train/llava_trainer_eval.py +llava/train/train.py +llava/train/train_dpo.py +llava/train/train_mem.py +trl/__init__.py +trl/core.py +trl/import_utils.py +trl/environment/__init__.py +trl/environment/base_environment.py +trl/extras/__init__.py +trl/extras/best_of_n_sampler.py +trl/extras/dataset_formatting.py +trl/models/__init__.py +trl/models/modeling_base.py +trl/models/modeling_sd_base.py +trl/models/modeling_value_head.py +trl/models/utils.py +trl/trainer/__init__.py +trl/trainer/base.py +trl/trainer/ddpo_config.py +trl/trainer/ddpo_trainer.py +trl/trainer/dpo_trainer.py +trl/trainer/iterative_sft_trainer.py +trl/trainer/model_config.py +trl/trainer/ppo_config.py +trl/trainer/ppo_trainer.py +trl/trainer/reward_config.py +trl/trainer/reward_trainer.py +trl/trainer/sft_trainer.py +trl/trainer/utils.py \ No newline at end of file diff --git a/llava.egg-info/dependency_links.txt b/llava.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/llava.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/llava.egg-info/requires.txt b/llava.egg-info/requires.txt new file mode 100644 index 0000000..8b7e8fe --- /dev/null +++ b/llava.egg-info/requires.txt @@ -0,0 +1,41 @@ + +[standalone] +shortuuid +httpx==0.24.0 +einops +ftfy + +[train] +llava[standalone] +numpy==1.26.1 +open_clip_torch +fastapi +markdown2[all] +numpy +requests +sentencepiece +torch==2.1.2 +torchvision==0.16.2 +uvicorn +wandb +deepspeed==0.14.4 +peft==0.4.0 +accelerate>=0.29.1 +tokenizers~=0.15.2 +transformers@ git+https://github.com/huggingface/transformers.git@1c39974a4c4036fd641bc1191cc32799f85715a4 +bitsandbytes==0.41.0 +scikit-learn==1.2.2 +sentencepiece~=0.1.99 +einops==0.6.1 +einops-exts==0.0.4 +gradio_client==0.2.9 +urllib3<=2.0.0 +datasets==2.16.1 +pydantic==1.10.8 +timm +hf_transfer +opencv-python +av +decord +tyro +scipy diff --git a/llava.egg-info/top_level.txt b/llava.egg-info/top_level.txt new file mode 100644 index 0000000..d4ecd54 --- /dev/null +++ b/llava.egg-info/top_level.txt @@ -0,0 +1,2 @@ +llava +trl diff --git a/llava/__init__.py b/llava/__init__.py new file mode 100644 index 0000000..4d1f016 --- /dev/null +++ b/llava/__init__.py @@ -0,0 +1 @@ +from .model import LlavaLlamaForCausalLM diff --git a/llava/__pycache__/__init__.cpython-310.pyc b/llava/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a38e73695b3ff9416dbf2244ff209e9bc76bb424 GIT binary patch literal 197 zcmd1j<>g`k0w%Y6>6$?LF^Gc(44TX@8G*u@ zjJHI6auUlDfkbYiTYiyqVrg+=j*qXOCQ}q^ZhlH?P7%nQl?+A9KnhI!GSx52&o9wW z2FmK^7o_HulIYq;;;dlSDKS*2eP;r IWU2rI00ed}t^fc4 literal 0 HcmV?d00001 diff --git a/llava/__pycache__/actiontokenizer.cpython-310.pyc b/llava/__pycache__/actiontokenizer.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1dc1c336bfe42a842bb29ce8f13113255b0db659 GIT binary patch literal 2977 zcmb7G-ESL35Z{lxvwd+KCr$Iw59AhJENFs2d^So`3PL~{kXkBa6`d}(8~4)lox68Q z6Jzs|@W=!3h>#MAm%i{X@PF(ZLI~xVHzZnS_F_AA8zk1+x!Jkhnc11&%sCk!cL|Kg zkN;?%n8& zHy7`$-mDEy)EN{dWVPIKy5Jtc zEl8bdYc)(WZGRqv;k7!|v!5w>Tlrxu#qGX(?|};e?msySuvmeaeh);F28Ecg5*IVV zl3du*5{@WrIHD*@`!+)*fwC29hap#z-axl4)_)NUEX$ah}x^*G-rQ*EqUJ3HkG@>0dtlts{Qz zGuL!!5LIohhjFSM2<4WF*8&@^)*r|_FmJ$2e+JT}UDoAYYs0#0b%{q{QkV=T&p@(C z_Hb=y&Orh9frJV=OAa|mi?AvRBG@+&1;a!9ke?-jpGRFKVF`PmWGkNk!GOWL-GZ6U0cnEPFN;0! zr%QhXRR6@kV-FbUVx8jB^&7W8`gG0HbomjYhb_FSX{aXPm?3{JEvh8R7K22{#iT9c z?a03pMgF$GxVq}!xqf9$?qrL(Acx{0_D}=bU5WsWLDFh3e}V3xZ*!#c%19y?JTHvH z%<~qOq9pL6bQzfb!FpAd;Fxw|&m_}rg|V00(r(M&>GM32v33&`HsL+;EN<&!1o?0K zfmGNG^cZOH0vTsgrP@}0+?0Ae*OeDaNsj}3`Q{jCVfUGOy$cO6hjo`DpbbC)Mz-%kmdwN7QDyK&Aw-|SDY|TM zIt`4$snY|O56B_OF=~PaUChZ61Vo)F43y@OhLL!xGw`AX(|~OHxgY~*MW=wQtl%3# zv=u~QTiY8^;%CFu>7oEsVK>fmir&KIl=62~4R|$c1nE7$E$f!LjG~u-G^U1qTpZ&x zKkzITa^E~Sl~#aU;8S#lR#}Bvtiqp%g#3U}_Y@2e0{Cq+FnA3Z{H{UY!QOJ*cOcp9 zfOpxZbx3<0dKt)j*w5^)HIH3}uRWD2R5wT!VmZW?Qn<@{w8x-n(jQDi8D^x;2YK4j zJPXC}RStf4NrXY(7}}uEhu5MbOOfGB9LiXrbX(~&ddMf%1Mam}50~Jf@FS$&Ml#|- z1ybc<>on0%JFvwdq0BljjC%3()>A!EZ=#)J>m~vhPqRLGoW|k(zQ(Qicd&D5)ciAh z0pj@YLE+yfg&D0+VoQ(xmh?RBdR{9LTM_bQ&wH@tNBJ9tbp-t@&eV%|(sZ3dEb?Z8 zg{FC$DTT>U3rOBTS+T9)>#MR|^$yCeB3VK*jHu!8%c8IYd<-+iMpPt>Sx*>c`Q+|D zwdt8ED^w}tIH#%EUoYu`=ZPc$hn;M<9mdVv2jh|r(QohO&XzOyZl)WFf+|bRb&P`N Yd7_I;c@&lrtSN%jB2}lz%5-YwU)v1jlK=n! literal 0 HcmV?d00001 diff --git a/llava/__pycache__/constants.cpython-310.pyc b/llava/__pycache__/constants.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cae0ed92c1aff6a6701d712bfb38ceabd2ad5970 GIT binary patch literal 459 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 C8P;0> literal 0 HcmV?d00001 diff --git a/llava/__pycache__/mm_utils.cpython-310.pyc b/llava/__pycache__/mm_utils.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e1e5369154cfbd5e72abb8c7a2bd82a7c8b5e1cc GIT binary patch literal 13916 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 Ee_VP!h5!Hn literal 0 HcmV?d00001 diff --git a/llava/__pycache__/utils.cpython-310.pyc b/llava/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..31847b61784811dd620c39b7cd99837b3b8e5d79 GIT binary patch literal 6974 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 J({?J;{|mURwyXdE literal 0 HcmV?d00001 diff --git a/llava/actiontokenizer.py b/llava/actiontokenizer.py new file mode 100644 index 0000000..b0980fb --- /dev/null +++ b/llava/actiontokenizer.py @@ -0,0 +1,128 @@ +""" +action_tokenizer.py + +Extension class; wraps base LLM/VLM tokenizer with logic to discretize and tokenize continuous robot actions. +""" + +from typing import List, Union + +import numpy as np +from transformers import PreTrainedTokenizerBase + + +# class ActionTokenizer: +# def __init__( +# self, tokenizer: PreTrainedTokenizerBase, bins: int = 256, min_action: int = -1, max_action: int = 1 +# ) -> None: +# """ +# Discretizes continuous robot actions into N bins per dimension and maps to the least used tokens. + +# NOTE =>> by default, assumes a BPE-style tokenizer akin to the LlamaTokenizer, where *the least used tokens* +# appear at the end of the vocabulary! + +# :param tokenizer: Base LLM/VLM tokenizer to extend. +# :param bins: Number of bins for each continuous value; we'll adopt a uniform binning strategy. +# :param min_action: Minimum action value (for clipping, setting lower bound on bin interval). +# :param max_action: Maximum action value (for clipping, setting upper bound on bin interval). +# """ +# self.tokenizer, self.n_bins, self.min_action, self.max_action = tokenizer, bins, min_action, max_action + +# # Create Uniform Bins + Compute Bin Centers +# self.bins = np.linspace(min_action, max_action, self.n_bins) +# self.bin_centers = (self.bins[:-1] + self.bins[1:]) / 2.0 + +# # [Contract] Set "action_token_begin_idx" based on `self.tokenizer.vocab_size - (self.n_bins + 1)` +# # =>> Assumes we're always overwriting the final `n_bins` tokens of the vocabulary! +# self.action_token_begin_idx: int = int(self.tokenizer.vocab_size - (self.n_bins + 1)) + +# def __call__(self, action: np.ndarray) -> Union[str, List[str]]: +# """Clip & bin actions to *the last `n_bins` tokens* of the vocabulary (e.g., tokenizer.vocab[-256:]).""" +# action = np.clip(action, a_min=float(self.min_action), a_max=float(self.max_action)) +# discretized_action = np.digitize(action, self.bins) + +# # Handle single element vs. batch +# if len(discretized_action.shape) == 1: +# return self.tokenizer.decode(list(self.tokenizer.vocab_size - discretized_action)) +# else: +# return self.tokenizer.batch_decode((self.tokenizer.vocab_size - discretized_action).tolist()) + +# def decode_token_ids_to_actions(self, action_token_ids: np.ndarray) -> np.ndarray: +# """ +# Returns continuous actions for discrete action token IDs. + +# NOTE =>> Because of the way the actions are discretized w.r.t. the bins (and not the bin centers), the +# digitization returns bin indices between [1, # bins], inclusive, when there are actually only +# (# bins - 1) bin intervals. + +# Therefore, if the digitization returns the last possible index, we map this to the last bin interval. + +# EXAMPLE =>> Let's say self._bins has 256 values. Then self._bin_centers has 255 values. Digitization returns +# indices between [1, 256]. We subtract 1 from all indices so that they are between [0, 255]. There +# is still one index (i==255) that would cause an out-of-bounds error if used to index into +# self._bin_centers. Therefore, if i==255, we subtract 1 from it so that it just becomes the index of +# the last bin center. We implement this simply via clipping between [0, 255 - 1]. +# """ +# discretized_actions = self.tokenizer.vocab_size - action_token_ids +# discretized_actions = np.clip(discretized_actions - 1, a_min=0, a_max=self.bin_centers.shape[0] - 1) + +# return self.bin_centers[discretized_actions] + +# @property +# def vocab_size(self) -> int: +# return self.n_bins + +class ActionTokenizer: + def __init__( + self, tokenizer: PreTrainedTokenizerBase, bins: int = 285, min_action = -0.0025, max_action = 0.0025 + # self, tokenizer: PreTrainedTokenizerBase, bins: int = 256, min_action: int = -1, max_action: int = 1 + ) -> None: + self.tokenizer, self.n_bins, self.min_action, self.max_action = tokenizer, bins, min_action, max_action + + # print('initializing action tokenizer, the origin len(tokenizer) is:', len(self.tokenizer)) + self.origin_len = len(self.tokenizer) + # 创建等间距的区间和计算区间中心 + self.bins = np.linspace(min_action, max_action, self.n_bins + 1) + self.bin_centers = (self.bins[:-1] + self.bins[1:]) / 2.0 + + # 添加新的动作 tokens 到 tokenizer + self.action_tokens = [f"" for i in range(self.n_bins)] + self.tokenizer.add_tokens(self.action_tokens) + self.action_token_ids = self.tokenizer.convert_tokens_to_ids(self.action_tokens) + self.new_len = len(self.tokenizer) + + # print('initializing action tokenizer, the new len(tokenizer) is:', len(self.tokenizer)) + + def __call__(self, action: np.ndarray) -> Union[str, List[str]]: + + + action = np.clip(action, a_min=float(self.min_action), a_max=float(self.max_action)) + discretized_action = np.digitize(action, self.bins) - 1 + discretized_action = np.clip(discretized_action, a_min=0, a_max=self.n_bins - 1) + + action_token_ids = np.array(self.action_token_ids)[discretized_action] + # print(action_token_ids) + if len(action_token_ids.shape) == 1: + # return self.tokenizer.decode(action_token_ids) + return action_token_ids + else: + # return self.tokenizer.batch_decode(action_token_ids.tolist()) + return [ids.tolist() for ids in action_token_ids] + + def decode_token_ids_to_actions(self, action_token_ids: np.ndarray) -> np.ndarray: + token_id_to_action_index = {tid: idx for idx, tid in enumerate(self.action_token_ids)} + vectorized_lookup = np.vectorize(token_id_to_action_index.get) + action_indices = vectorized_lookup(action_token_ids) + return self.bin_centers[action_indices] + + # 返回所有 tokens 的数量,包括动作 tokens + @property + def vocab_size(self) -> int: + return len(self.tokenizer) + + @property + def num_new_tokens(self) -> int: + return self.new_len - self.origin_len + + @property + def action_vocab_size(self) -> int: + return len(self.action_tokens) \ No newline at end of file diff --git a/llava/constants.py b/llava/constants.py new file mode 100644 index 0000000..be8cf02 --- /dev/null +++ b/llava/constants.py @@ -0,0 +1,12 @@ +CONTROLLER_HEART_BEAT_EXPIRATION = 30 +WORKER_HEART_BEAT_INTERVAL = 15 + +LOGDIR = "." + +# Model Constants +IGNORE_INDEX = -100 +IMAGE_TOKEN_INDEX = -200 +DEFAULT_IMAGE_TOKEN = "" +DEFAULT_IMAGE_PATCH_TOKEN = "" +DEFAULT_IM_START_TOKEN = "" +DEFAULT_IM_END_TOKEN = "" diff --git a/llava/conversation.py b/llava/conversation.py new file mode 100644 index 0000000..ae1739b --- /dev/null +++ b/llava/conversation.py @@ -0,0 +1,597 @@ +import dataclasses +from enum import auto, Enum +from typing import List, Any, Dict, Union, Tuple +import re +import base64 +from io import BytesIO +from PIL import Image +from transformers import AutoTokenizer + + +class SeparatorStyle(Enum): + """Different separator style.""" + + SINGLE = auto() + TWO = auto() + MPT = auto() + PLAIN = auto() + CHATML = auto() + LLAMA_2 = auto() + LLAMA_3 = auto() + QWEN = auto() + GEMMA = auto() + + +@dataclasses.dataclass +class Conversation: + """A class that keeps all conversation history.""" + + system: str + roles: List[str] + messages: List[List[str]] + offset: int + sep_style: SeparatorStyle = SeparatorStyle.SINGLE + sep: str = "###" + sep2: str = None + version: str = "Unknown" + + tokenizer_id: str = "" + tokenizer: Any = None + # Stop criteria (the default one is EOS token) + stop_str: Union[str, List[str]] = None + # Stops generation if meeting any token in this list + stop_token_ids: List[int] = None + + skip_next: bool = False + + def get_prompt(self): + messages = self.messages + if len(messages) > 0 and type(messages[0][1]) is tuple: + messages = self.messages.copy() + init_role, init_msg = messages[0].copy() + init_msg = init_msg[0] + if "mmtag" in self.version: + init_msg = init_msg.replace("", "").strip() + messages[0] = (init_role, init_msg) + messages.insert(0, (self.roles[0], "")) + messages.insert(1, (self.roles[1], "Received.")) + elif not init_msg.startswith(""): + init_msg = init_msg.replace("", "").strip() + messages[0] = (init_role, "\n" + init_msg) + else: + messages[0] = (init_role, init_msg) + + if self.sep_style == SeparatorStyle.SINGLE: + ret = self.system + self.sep + for role, message in messages: + if message: + if type(message) is tuple: + message, _, _ = message + ret += role + ": " + message + self.sep + else: + ret += role + ":" + + elif self.sep_style == SeparatorStyle.TWO: + seps = [self.sep, self.sep2] + ret = self.system + seps[0] + for i, (role, message) in enumerate(messages): + if message: + if type(message) is tuple: + message, _, _ = message + ret += role + ": " + message + seps[i % 2] + else: + ret += role + ":" + + elif self.sep_style == SeparatorStyle.CHATML: + ret = "" if self.system == "" else self.system + self.sep + "\n" + for role, message in messages: + if message: + if type(message) is tuple: + message, images, _ = message + message = "" * len(images) + message + ret += role + "\n" + message + self.sep + "\n" + else: + ret += role + "\n" + return ret + + elif self.sep_style == SeparatorStyle.LLAMA_3: + if self.tokenizer is None: + raise ValueError("Llama 3 tokenizer is not available. Make sure you have the necessary permissions.") + chat_template_messages = [{"role": "system", "content": self.system}] + for role, message in messages: + if message: + if type(message) is tuple: + message, images = message + message = "" * len(images) + message + chat_template_messages.append({"role": role, "content": message}) + + # print(chat_template_messages) + return self.tokenizer.apply_chat_template(chat_template_messages, tokenize=False, add_generation_prompt=True) + # ret = "" if self.system == "" else self.system + self.sep + "\n" + # for role, message in messages: + # if message: + # if type(message) is tuple: + # message, images = message + # message = "" * len(images) + message + # ret += role + "\n" + message + self.sep + "\n" + # else: + # ret += role + "\n" + # return ret + + elif self.sep_style == SeparatorStyle.MPT: + ret = self.system + self.sep + for role, message in messages: + if message: + if type(message) is tuple: + message, _, _ = message + ret += role + message + self.sep + else: + ret += role + + elif self.sep_style == SeparatorStyle.GEMMA: + ret = "" + for i, (role, message) in enumerate(messages): + assert role == self.roles[i % 2], "Conversation should alternate user/assistant/user/assistant/..." + if message: + if type(message) is tuple: + message, _, _ = message + ret += role + message + self.sep + else: + ret += role + + elif self.sep_style == SeparatorStyle.LLAMA_2: + wrap_sys = lambda msg: f"<>\n{msg}\n<>\n\n" if len(msg) > 0 else msg + wrap_inst = lambda msg: f"[INST] {msg} [/INST]" + ret = "" + + for i, (role, message) in enumerate(messages): + if i == 0: + assert message, "first message should not be none" + assert role == self.roles[0], "first message should come from user" + if message: + if type(message) is tuple: + message, _, _ = message + if i == 0: + message = wrap_sys(self.system) + message + if i % 2 == 0: + message = wrap_inst(message) + ret += self.sep + message + else: + ret += " " + message + " " + self.sep2 + else: + ret += "" + ret = ret.lstrip(self.sep) + + elif self.sep_style == SeparatorStyle.PLAIN: + seps = [self.sep, self.sep2] + ret = self.system + for i, (role, message) in enumerate(messages): + if message: + if type(message) is tuple: + message, _, _ = message + ret += message + seps[i % 2] + else: + ret += "" + else: + raise ValueError(f"Invalid style: {self.sep_style}") + + return ret + + def append_message(self, role, message): + self.messages.append([role, message]) + + def process_image(self, image, image_process_mode, return_pil=False, image_format="PNG"): + if image_process_mode == "Pad": + + def expand2square(pil_img, background_color=(122, 116, 104)): + width, height = pil_img.size + if width == height: + return pil_img + elif width > height: + result = Image.new(pil_img.mode, (width, width), background_color) + result.paste(pil_img, (0, (width - height) // 2)) + return result + else: + result = Image.new(pil_img.mode, (height, height), background_color) + result.paste(pil_img, ((height - width) // 2, 0)) + return result + + image = expand2square(image) + elif image_process_mode in ["Default", "Crop"]: + pass + elif image_process_mode == "Resize": + image = image.resize((336, 336)) + else: + raise ValueError(f"Invalid image_process_mode: {image_process_mode}") + + if type(image) is not Image.Image: + image = Image.open(image).convert("RGB") + + max_hw, min_hw = max(image.size), min(image.size) + aspect_ratio = max_hw / min_hw + max_len, min_len = 672, 448 + shortest_edge = int(min(max_len / aspect_ratio, min_len, min_hw)) + longest_edge = int(shortest_edge * aspect_ratio) + W, H = image.size + if H > W: + H, W = longest_edge, shortest_edge + else: + H, W = shortest_edge, longest_edge + image = image.resize((W, H)) + if return_pil: + return image + else: + buffered = BytesIO() + image.save(buffered, format=image_format) + img_b64_str = base64.b64encode(buffered.getvalue()).decode() + return img_b64_str + + def get_images(self, return_pil=False, return_path=False): + images = [] + for i, (role, msg) in enumerate(self.messages[self.offset :]): + if i % 2 == 0: + if type(msg) is tuple: + msg, image, image_process_mode = msg + if type(image) != list: + image = [image] + for img in image: + if not return_path and self.is_image_file(img): + img = self.process_image(img, image_process_mode, return_pil=return_pil) + else: + images.append(img) + return images + + def is_image_file(self, filename): + image_extensions = [".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".webp"] + return any(filename.lower().endswith(ext) for ext in image_extensions) + + def is_video_file(self, filename): + video_extensions = [".mp4", ".mov", ".avi", ".mkv", ".wmv", ".flv", ".mpeg", ".mpg"] + return any(filename.lower().endswith(ext) for ext in video_extensions) + + def to_gradio_chatbot(self): + ret = [] + for i, (role, msg) in enumerate(self.messages[self.offset :]): + if i % 2 == 0: + if type(msg) is tuple: + msg, image, image_process_mode = msg + if type(image) != list: + image = [image] + if len(image) == 1: + msg = "\n" + msg.replace("", "").strip() + else: + msg = re.sub(r"()\n(?=)", r"\1 ", msg) + + img_str_list = [] + for img in image: + if self.is_image_file(img): + img_b64_str = self.process_image(img, "Default", return_pil=False, image_format="JPEG") + img_str = f'' + img_str_list.append(img_str) + elif self.is_video_file(img): + ret.append(((img,), None)) + + msg = msg.strip() + img_place_holder = "" + for img_str in img_str_list: + img_place_holder += f"{img_str}\n\n" + + if len(img_str_list) > 0: + msg = f"{img_place_holder}\n\n{msg}" + + if len(msg) > 0: + ret.append([msg, None]) + else: + ret.append([msg, None]) + else: + ret[-1][-1] = msg + return ret + + def copy(self): + return Conversation(system=self.system, roles=self.roles, messages=[[x, y] for x, y in self.messages], offset=self.offset, sep_style=self.sep_style, sep=self.sep, sep2=self.sep2, version=self.version) + + def dict(self): + if len(self.get_images()) > 0: + return { + "system": self.system, + "roles": self.roles, + "messages": [[x, y[0] if type(y) is tuple else y] for x, y in self.messages], + "offset": self.offset, + "sep": self.sep, + "sep2": self.sep2, + } + return { + "system": self.system, + "roles": self.roles, + "messages": self.messages, + "offset": self.offset, + "sep": self.sep, + "sep2": self.sep2, + } + + +conv_vicuna_v0 = Conversation( + system="A chat between a curious human and an artificial intelligence assistant. " "The assistant gives helpful, detailed, and polite answers to the human's questions.", + roles=("Human", "Assistant"), + messages=[ + ["Human", "What are the key differences between renewable and non-renewable energy sources?"], + [ + "Assistant", + "Renewable energy sources are those that can be replenished naturally in a relatively " + "short amount of time, such as solar, wind, hydro, geothermal, and biomass. " + "Non-renewable energy sources, on the other hand, are finite and will eventually be " + "depleted, such as coal, oil, and natural gas. Here are some key differences between " + "renewable and non-renewable energy sources:\n" + "1. Availability: Renewable energy sources are virtually inexhaustible, while non-renewable " + "energy sources are finite and will eventually run out.\n" + "2. Environmental impact: Renewable energy sources have a much lower environmental impact " + "than non-renewable sources, which can lead to air and water pollution, greenhouse gas emissions, " + "and other negative effects.\n" + "3. Cost: Renewable energy sources can be more expensive to initially set up, but they typically " + "have lower operational costs than non-renewable sources.\n" + "4. Reliability: Renewable energy sources are often more reliable and can be used in more remote " + "locations than non-renewable sources.\n" + "5. Flexibility: Renewable energy sources are often more flexible and can be adapted to different " + "situations and needs, while non-renewable sources are more rigid and inflexible.\n" + "6. Sustainability: Renewable energy sources are more sustainable over the long term, while " + "non-renewable sources are not, and their depletion can lead to economic and social instability.\n", + ], + ], + offset=2, + sep_style=SeparatorStyle.SINGLE, + sep="###", +) + +conv_vicuna_v1 = Conversation( + system="A chat between a curious user and an artificial intelligence assistant. " "The assistant gives helpful, detailed, and polite answers to the user's questions.", + roles=("USER", "ASSISTANT"), + version="v1", + messages=[], + offset=0, + sep_style=SeparatorStyle.TWO, + sep=" ", + sep2="", +) + +conv_llama_2 = Conversation( + system="""You are a helpful, respectful and honest assistant. Always answer as helpfully as possible, while being safe. Your answers should not include any harmful, unethical, racist, sexist, toxic, dangerous, or illegal content. Please ensure that your responses are socially unbiased and positive in nature. + +If a question does not make any sense, or is not factually coherent, explain why instead of answering something not correct. If you don't know the answer to a question, please don't share false information.""", + roles=("USER", "ASSISTANT"), + version="llama_v2", + messages=[], + offset=0, + sep_style=SeparatorStyle.LLAMA_2, + sep="", + sep2="", +) + +conv_llava_llama_2 = Conversation( + system="You are a helpful language and vision assistant. " "You are able to understand the visual content that the user provides, " "and assist the user with a variety of tasks using natural language.", + roles=("USER", "ASSISTANT"), + version="llama_v2", + messages=[], + offset=0, + sep_style=SeparatorStyle.LLAMA_2, + sep="", + sep2="", +) + +def safe_load_tokenizer(tokenizer_id): + try: + return AutoTokenizer.from_pretrained(tokenizer_id) + except Exception: + return None + +conv_llava_llama_3 = Conversation( + system="You are a helpful language and vision assistant. " "You are able to understand the visual content that the user provides, " "and assist the user with a variety of tasks using natural language.", + roles=("user", "assistant"), + version="llama_v3", + messages=[], + offset=0, + sep="<|eot_id|>", + sep_style=SeparatorStyle.LLAMA_3, + tokenizer_id="meta-llama/Meta-Llama-3-8B-Instruct", + tokenizer=safe_load_tokenizer("meta-llama/Meta-Llama-3-8B-Instruct"), + stop_token_ids=[128009], +) + +conv_mistral_instruct = Conversation( + system="", + roles=("USER", "ASSISTANT"), + version="llama_v2", + messages=[], + offset=0, + sep_style=SeparatorStyle.LLAMA_2, + sep="", + sep2="", +) + +conv_llava_llama_2_simple = Conversation( + system="Answer the questions about the visual content that the user provides.", + roles=("USER", "ASSISTANT"), + version="llama_v2", + messages=[], + offset=0, + sep_style=SeparatorStyle.LLAMA_2, + sep="", + sep2="", +) + +conv_llava_llama_2_mmtag = Conversation( + system="Answer the questions about the visual content that the user provides." "The visual content will be provided with the following format: visual content.", + roles=("USER", "ASSISTANT"), + version="llama_v2_mmtag", + messages=[], + offset=0, + sep_style=SeparatorStyle.LLAMA_2, + sep="", + sep2="", +) + +conv_mpt = Conversation( + system="""<|im_start|>system +A conversation between a user and an LLM-based AI assistant. The assistant gives helpful and honest answers.""", + roles=("<|im_start|>user\n", "<|im_start|>assistant\n"), + version="mpt", + messages=[], + offset=0, + sep_style=SeparatorStyle.MPT, + sep="<|im_end|>", +) + +conv_qwen = Conversation( + system="""<|im_start|>system +You are a helpful assistant.""", + roles=("<|im_start|>user", "<|im_start|>assistant"), + version="qwen", + messages=[], + offset=0, + sep_style=SeparatorStyle.CHATML, + sep="<|im_end|>", +) + +conv_qwen_vla = Conversation( + system="""<|im_start|>system +You are a helpful assistant.""", + roles=("<|im_start|>user", "<|im_start|>assistant"), + version="qwen_vla", + messages=[], + offset=0, + sep_style=SeparatorStyle.CHATML, + sep="<|im_end|>", +) + +conv_gemma_instruct = Conversation(system="", roles=("user\n", "model\n"), version="gemma", messages=[], offset=0, sep_style=SeparatorStyle.GEMMA, sep="\n") + +conv_llava_plain = Conversation( + system="", + roles=("", ""), + messages=[], + offset=0, + sep_style=SeparatorStyle.PLAIN, + sep="\n", +) + +conv_llava_v0 = Conversation( + system="A chat between a curious human and an artificial intelligence assistant. " "The assistant gives helpful, detailed, and polite answers to the human's questions.", + roles=("Human", "Assistant"), + messages=[], + offset=0, + sep_style=SeparatorStyle.SINGLE, + sep="###", +) + +conv_llava_v0_mmtag = Conversation( + system="A chat between a curious user and an artificial intelligence assistant. " + "The assistant is able to understand the visual content that the user provides, and assist the user with a variety of tasks using natural language." + "The visual content will be provided with the following format: visual content.", + roles=("Human", "Assistant"), + messages=[], + offset=0, + sep_style=SeparatorStyle.SINGLE, + sep="###", + version="v0_mmtag", +) + +conv_llava_v1 = Conversation( + system="A chat between a curious human and an artificial intelligence assistant. " "The assistant gives helpful, detailed, and polite answers to the human's questions.", + roles=("USER", "ASSISTANT"), + version="v1", + messages=[], + offset=0, + sep_style=SeparatorStyle.TWO, + sep=" ", + sep2="", +) + +conv_llava_v1_mmtag = Conversation( + system="A chat between a curious user and an artificial intelligence assistant. " + "The assistant is able to understand the visual content that the user provides, and assist the user with a variety of tasks using natural language." + "The visual content will be provided with the following format: visual content.", + roles=("USER", "ASSISTANT"), + messages=[], + offset=0, + sep_style=SeparatorStyle.TWO, + sep=" ", + sep2="", + version="v1_mmtag", +) + +conv_mistral_orca = Conversation( + system="""<|im_start|>system +You are MistralOrca, a large language model trained by Alignment Lab AI. Write out your reasoning step-by-step to be sure you get the right answers!""", + roles=("<|im_start|>user\n", "<|im_start|>assistant\n"), + version="mpt", + messages=[], + offset=0, + sep_style=SeparatorStyle.MPT, + sep="<|im_end|>", +) + +conv_mistral_zephyr = Conversation( + system="""<|system|> +You are a helpful AI assistant.""", + roles=("<|user|>\n", "<|assistant|>\n"), + version="mpt", + messages=[], + offset=0, + sep_style=SeparatorStyle.MPT, + sep="", +) + +conv_mistral_direct = Conversation( + system="""<|im_start|>system +Answer the questions.""", + roles=("<|im_start|>user\n", "<|im_start|>assistant\n"), + version="mpt", + messages=[], + offset=0, + sep_style=SeparatorStyle.MPT, + sep="<|im_end|>", +) + +conv_chatml_direct = Conversation( + system="""<|im_start|>system +Answer the questions.""", + roles=("<|im_start|>user\n", "<|im_start|>assistant\n"), + version="mpt", + messages=[], + offset=0, + sep_style=SeparatorStyle.MPT, + sep="<|im_end|>", +) + +default_conversation = conv_vicuna_v0 +conv_templates = { + "default": conv_vicuna_v0, + "v0": conv_vicuna_v0, + "v1": conv_vicuna_v1, + "vicuna_v1": conv_vicuna_v1, + "llama_2": conv_llama_2, + "mistral_instruct": conv_mistral_instruct, + "mistral_orca": conv_mistral_orca, + "mistral_zephyr": conv_mistral_zephyr, + "mistral_direct": conv_mistral_direct, + "plain": conv_llava_plain, + "v0_plain": conv_llava_plain, + "chatml_direct": conv_chatml_direct, + "llava_v0": conv_llava_v0, + "llava_v0_mmtag": conv_llava_v0_mmtag, + "llava_v1": conv_llava_v1, + "llava_v1_mmtag": conv_llava_v1_mmtag, + "llava_llama_2": conv_llava_llama_2, + "llava_llama_3": conv_llava_llama_3, + "llava_llama_2_simple": conv_llava_llama_2_simple, + "llava_llama_2_mmtag": conv_llava_llama_2_mmtag, + "llava_mistral_instruct": conv_mistral_instruct, + "mpt": conv_mpt, + "qwen_vla": conv_qwen_vla, + "qwen_1_5": conv_qwen, + "qwen_2": conv_qwen, + "gemma_instruct": conv_gemma_instruct, +} + + +if __name__ == "__main__": + print(default_conversation.get_prompt()) diff --git a/llava/eval/evaluate_interleave.py b/llava/eval/evaluate_interleave.py new file mode 100644 index 0000000..b00d32f --- /dev/null +++ b/llava/eval/evaluate_interleave.py @@ -0,0 +1,339 @@ +import re +from rouge import Rouge +import argparse +import os +import json +import numpy as np +from sklearn.feature_extraction.text import TfidfVectorizer +from sklearn.metrics.pairwise import cosine_similarity + + +spot_the_diff = ["Spot-the-Diff", "Birds-to-Words", "CLEVR-Change"] +image_edit_instruct = ["IEdit", "HQ-Edit", "MagicBrush"] +visual_story_telling = ["AESOP", "FlintstonesSV", "PororoSV", "VIST"] +visual_cloze = ["COMICS_Dialogue", "RecipeQA_VisualCloze"] +text_rich_vqa = ["WebQA", "TQA", "OCR-VQA", "DocVQA"] +multi_image_vqa = ["MIT-States_StateCoherence", "MIT-States_PropertyCoherence", "VISION", "RecipeQA_ImageCoherence"] + +puzzle = ["RAVEN"] +nlrv2 = ["NLVR2_Mantis"] +qbench = ["QBench"] + +class Eval: + def __init__(self): + self.periodStrip = re.compile("(?!<=\d)(\.)(?!\d)") + self.commaStrip = re.compile("(\d)(\,)(\d)") + self.punct = [ + ";", + r"/", + "[", + "]", + '"', + "{", + "}", + "(", + ")", + "=", + "+", + "\\", + "_", + "-", + ">", + "<", + "@", + "`", + ",", + "?", + "!", + ] + + def processPunctuation(self, inText): + outText = inText + for p in self.punct: + if (p + " " in inText or " " + p in inText) or ( + re.search(self.commaStrip, inText) != None + ): + outText = outText.replace(p, "") + else: + outText = outText.replace(p, " ") + outText = self.periodStrip.sub("", outText, re.UNICODE) + return outText + + def process(self, answer): + answer = answer.replace("\n", " ") + answer = answer.replace("\t", " ") + answer = answer.strip() + answer = self.processPunctuation(answer) + answer = answer.strip('\'') + answer = answer.strip('\"') + answer = answer.strip(')') + answer = answer.strip('(') + answer = answer.strip().lower() + return answer + + def evaluate_rouge(self,preds): + rouge = Rouge() + acc = {'f': []} + eval_list = [] + for i, res in enumerate(preds): + sample_id = res['sample_id'] + # print(sample_id) + gt_ans = self.process(res["gt_response"]) + pred_ans = self.process(res["pred_response"]) + # assert gt_ans != '' + + if gt_ans == '': + continue + + if pred_ans == '': + s = 0 + else: + if len(pred_ans) > 512: + pred_ans = pred_ans[0: 512] + s = rouge.get_scores(pred_ans, gt_ans)[0]['rouge-l']['f'] + acc['f'].append(s) + eval_list.append({'id':str(sample_id),'score':str(round(s,3))}) + results = {'Rouge-L f': np.mean(acc['f'])} + return results,eval_list + + + def judge_multi_choice(self,sample): + sample_id = sample['sample_id'] + gt_ans = sample["gt_response"] + pred_ans = sample["pred_response"] + + if ":" in pred_ans: + a_list = pred_ans.split(":") + a_list = [a.strip() for a in a_list ] + for a in a_list: + if len(a) == 1 and a[-1] in ["a", "b", "c", "d", "e", "f", "g", "h"]: + pred_ans = a + + if pred_ans == gt_ans: + return 1 + else: + return 0 + + def process_sample(self,sample): + sample["gt_response"] = self.process(sample["gt_response"]) + sample["pred_response"] = self.process(sample["pred_response"]) + + def evaluate_multichoice(self, preditions): + correct = 0 + eval_list = [] + for i, sample in enumerate(preditions): + self.process_sample(sample) + score = self.judge_multi_choice(sample) + sample_id = sample['sample_id'] + sample['result'] = score + eval_list.append({'id':str(sample_id),'score':str(score)}) + correct+=score + return {'Accuracy':correct/len(preditions)},eval_list + + def evaluate_multi_choice_image(self,preditions): + correct = 0 + eval_list = [] + for i,sample in enumerate(preditions): + gt_ans = self.process(sample["gt_response"]) + pred_ans = self.process(sample["pred_response"]) + sample_id = sample['sample_id'] + + if ":" in pred_ans: + a_list = pred_ans.split(":") + a_list = [a.strip() for a in a_list ] + for a in a_list: + if len(a) == 1 and a[-1] in ["a", "b", "c", "d", "e", "f", "g", "h"]: + pred_ans = a + + if gt_ans == pred_ans: + score = 1 + else: + score = 0 + sample_id = sample['sample_id'] + sample['result'] = score + eval_list.append({'id':str(sample_id),'score':str(score)}) + correct+=score + return {'Accuracy':correct/len(preditions)},eval_list + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('--result-dir', type=str, required=True) + + args = parser.parse_args() + + result_file = os.path.join(args.result_dir, "result.jsonl") + + if not os.path.exists(result_file): + print('No prediction file found') + exit(0) + with open(result_file, 'r') as f: + preds_all = [json.loads(line) for line in f] + + preds_all_dict = dict() + for pred in preds_all: + if pred["dataset"] not in preds_all_dict: + preds_all_dict[pred["dataset"]] = list() + preds_all_dict[pred["dataset"]].append(pred) + + image_choice_dataset_list = ["recipeqa-RecipeQA_VisualCloze", "RecipeQA_ImageCoherence", "COMICS_Panel"] + E = Eval() + + eval_result_list = dict() + eval_result_list_detail = dict() + + for dataset in preds_all_dict: + + preds = preds_all_dict[dataset] + question_type = preds[0]["question_type"] + + if question_type == 'open-ended': + eval_result, eval_list = E.evaluate_rouge(preds) + + elif question_type == 'multi-choice' or dataset == 'nlrv2': + if dataset in image_choice_dataset_list: + eval_result, eval_list = E.evaluate_multi_choice_image(preds) + else: + eval_result, eval_list = E.evaluate_multichoice(preds) + + else: + eval_result = 'Dataset not supported' + print('Dataset not supported') + exit(0) + + print(dataset, end = ': ') + print(eval_result) + + eval_result_list[dataset] = eval_result + eval_result_list_detail[dataset] = eval_list + + os.makedirs(args.result_dir, exist_ok=True) + with open(os.path.join(args.result_dir, 'eval_dataset.json'), 'w') as f: + json.dump(eval_result_list, f, indent=4) + + with open(os.path.join(args.result_dir,'eval_dataset_details.json'), 'w') as f: + json.dump(eval_result_list_detail, f, indent=4) + + + eval_cat_list = dict() + print() + + # spot_the_diff + score = 0 + count = 0 + for dataset in eval_result_list: + if dataset in spot_the_diff: + count += 1 + score += list(eval_result_list[dataset].values())[0] + if count > 0: + score /= count + eval_cat_list["spot_the_diff"] = score + print("spot_the_diff", end = ': ') + print('{:.2f}'.format(100 * score)) + + # image_edit_instruct + score = 0 + count = 0 + for dataset in eval_result_list: + if dataset in image_edit_instruct: + count += 1 + score += list(eval_result_list[dataset].values())[0] + if count > 0: + score /= count + eval_cat_list["image_edit_instruct"] = score + print("image_edit_instruct", end = ': ') + print('{:.2f}'.format(100 * score)) + + # visual_story_telling + score = 0 + count = 0 + for dataset in eval_result_list: + if dataset in visual_story_telling: + count += 1 + score += list(eval_result_list[dataset].values())[0] + if count > 0: + score /= count + eval_cat_list["visual_story_telling"] = score + print("visual_story_telling", end = ': ') + print('{:.2f}'.format(100 * score)) + + # visual_cloze + score = 0 + count = 0 + for dataset in eval_result_list: + if dataset in visual_cloze: + count += 1 + score += list(eval_result_list[dataset].values())[0] + if count > 0: + score /= count + eval_cat_list["visual_cloze"] = score + print("visual_cloze", end = ': ') + print('{:.2f}'.format(100 * score)) + + # text_rich_vqa + score = 0 + count = 0 + for dataset in eval_result_list: + if dataset in text_rich_vqa: + count += 1 + score += list(eval_result_list[dataset].values())[0] + if count > 0: + score /= count + eval_cat_list["text_rich_vqa"] = score + print("text_rich_vqa", end = ': ') + print('{:.2f}'.format(100 * score)) + + # multi_image_vqa + score = 0 + count = 0 + for dataset in eval_result_list: + if dataset in multi_image_vqa: + count += 1 + score += list(eval_result_list[dataset].values())[0] + if count > 0: + score /= count + eval_cat_list["multi_image_vqa"] = score + print("multi_image_vqa", end = ': ') + print('{:.2f}'.format(100 * score)) + + # puzzle + score = 0 + count = 0 + for dataset in eval_result_list: + if dataset in puzzle: + count += 1 + score += list(eval_result_list[dataset].values())[0] + if count > 0: + score /= count + eval_cat_list["puzzle"] = score + print("puzzle", end = ': ') + print('{:.2f}'.format(100 * score)) + + # nlrv2 + score = 0 + count = 0 + for dataset in eval_result_list: + if dataset in nlrv2: + count += 1 + score += list(eval_result_list[dataset].values())[0] + if count > 0: + score /= count + eval_cat_list["nlrv2"] = score + print("nlrv2", end = ': ') + print('{:.2f}'.format(100 * score)) + + # qbench + score = 0 + count = 0 + for dataset in eval_result_list: + if dataset in qbench: + count += 1 + score += list(eval_result_list[dataset].values())[0] + if count > 0: + score /= count + eval_cat_list["qbench"] = score + print("qbench", end = ': ') + print('{:.2f}'.format(100 * score)) + + with open(os.path.join(args.result_dir,'eval_cat.json'), 'w') as f: + json.dump(eval_cat_list, f, indent=4) \ No newline at end of file diff --git a/llava/eval/model_vqa.py b/llava/eval/model_vqa.py new file mode 100644 index 0000000..2ebceed --- /dev/null +++ b/llava/eval/model_vqa.py @@ -0,0 +1,240 @@ +import argparse +import torch +import os +import json +from tqdm import tqdm +import shortuuid + +from llava.constants import IMAGE_TOKEN_INDEX, DEFAULT_IMAGE_TOKEN, DEFAULT_IM_START_TOKEN, DEFAULT_IM_END_TOKEN +from llava.conversation import conv_templates, SeparatorStyle +from llava.model.builder import load_pretrained_model +from llava.utils import disable_torch_init +from llava.mm_utils import tokenizer_image_token, get_model_name_from_path, KeywordsStoppingCriteria + +from llava.constants import IGNORE_INDEX, DEFAULT_IMAGE_TOKEN, DEFAULT_IM_START_TOKEN, DEFAULT_IM_END_TOKEN, IMAGE_TOKEN_INDEX +from typing import Dict, Optional, Sequence, List +import transformers +import re + +from PIL import Image +import math + + +def split_list(lst, n): + """Split a list into n (roughly) equal-sized chunks""" + chunk_size = math.ceil(len(lst) / n) # integer division + return [lst[i:i+chunk_size] for i in range(0, len(lst), chunk_size)] + + +def get_chunk(lst, n, k): + chunks = split_list(lst, n) + return chunks[k] + +def preprocess_qwen(sources, tokenizer: transformers.PreTrainedTokenizer, has_image: bool = False, max_len=2048, system_message: str = "You are a helpful assistant.") -> Dict: + roles = {"human": "<|im_start|>user", "gpt": "<|im_start|>assistant"} + + im_start, im_end = tokenizer.additional_special_tokens_ids + nl_tokens = tokenizer("\n").input_ids + _system = tokenizer("system").input_ids + nl_tokens + _user = tokenizer("user").input_ids + nl_tokens + _assistant = tokenizer("assistant").input_ids + nl_tokens + + # Apply prompt templates + input_ids, targets = [], [] + + source = sources + if roles[source[0]["from"]] != roles["human"]: + source = source[1:] + + input_id, target = [], [] + system = [im_start] + _system + tokenizer(system_message).input_ids + [im_end] + nl_tokens + input_id += system + target += [im_start] + [IGNORE_INDEX] * (len(system) - 3) + [im_end] + nl_tokens + assert len(input_id) == len(target) + for j, sentence in enumerate(source): + role = roles[sentence["from"]] + if has_image and sentence["value"] is not None and "" in sentence["value"]: + num_image = len(re.findall(DEFAULT_IMAGE_TOKEN, sentence["value"])) + texts = sentence["value"].split('') + _input_id = tokenizer(role).input_ids + nl_tokens + for i,text in enumerate(texts): + _input_id += tokenizer(text).input_ids + if iuser": + _target = [im_start] + [IGNORE_INDEX] * (len(_input_id) - 3) + [im_end] + nl_tokens + elif role == "<|im_start|>assistant": + _target = [im_start] + [IGNORE_INDEX] * len(tokenizer(role).input_ids) + _input_id[len(tokenizer(role).input_ids) + 1 : -2] + [im_end] + nl_tokens + else: + raise NotImplementedError + target += _target + + input_ids.append(input_id) + targets.append(target) + input_ids = torch.tensor(input_ids, dtype=torch.long) + targets = torch.tensor(targets, dtype=torch.long) + return input_ids + +def eval_model(args): + + # Model + disable_torch_init() + model_path = os.path.expanduser(args.model_path) + model_name = get_model_name_from_path(model_path) + tokenizer, model, image_processor, context_len = load_pretrained_model(model_path, args.model_base, model_name) + + # Data + with open(os.path.expanduser(args.question_file)) as f: + questions = json.load(f) + questions = get_chunk(questions, args.num_chunks, args.chunk_idx) + answers_file = os.path.expanduser(args.answers_file) + os.makedirs(os.path.dirname(answers_file), exist_ok=True) + ans_file = open(answers_file, "w") + + for line in tqdm(questions): + idx = line["sample_id"] + question_type = line["metadata"]["question_type"] + dataset_name = line["metadata"]["dataset"] + gt = line["conversations"][1]["value"] + + image_files = line["image"] + qs = line["conversations"][0]["value"] + cur_prompt = args.extra_prompt + qs + + args.conv_mode = "qwen_1_5" + + conv = conv_templates[args.conv_mode].copy() + conv.append_message(conv.roles[0], qs) + conv.append_message(conv.roles[1], None) + prompt = conv.get_prompt() + + input_ids = preprocess_qwen([line["conversations"][0],{'from': 'gpt','value': None}], tokenizer, has_image=True).cuda() + img_num = list(input_ids.squeeze()).count(IMAGE_TOKEN_INDEX) + + image_tensors = [] + for image_file in image_files: + image = Image.open(os.path.join(args.image_folder, image_file)) + image_tensor = image_processor.preprocess(image, return_tensors='pt')['pixel_values'] + image_tensors.append(image_tensor.half().cuda()) + # image_tensors = torch.cat(image_tensors, dim=0) + + stop_str = conv.sep if conv.sep_style != SeparatorStyle.TWO else conv.sep2 + keywords = [stop_str] + stopping_criteria = KeywordsStoppingCriteria(keywords, tokenizer, input_ids) + + with torch.inference_mode(): + output_ids = model.generate( + input_ids, + images=image_tensors, + do_sample=True if args.temperature > 0 else False, + temperature=args.temperature, + top_p=args.top_p, + num_beams=args.num_beams, + # no_repeat_ngram_size=3, + max_new_tokens=1024, + use_cache=True) + + + outputs = tokenizer.batch_decode(output_ids, skip_special_tokens=True)[0] + outputs = outputs.strip() + if outputs.endswith(stop_str): + outputs = outputs[:-len(stop_str)] + outputs = outputs.strip() + + ans_id = shortuuid.uuid() + ans_file.write(json.dumps({ + "dataset": dataset_name, + "sample_id": idx, + "prompt": cur_prompt, + "pred_response": outputs, + "gt_response": gt, + "shortuuid": ans_id, + "model_id": model_name, + "question_type": question_type, + }) + "\n") + ans_file.flush() + + if len(line["conversations"]) > 2: + + for i in range(2, len(line["conversations"]), 2): + input_ids = torch.cat((input_ids, output_ids), dim=1) + + gt = line["conversations"][i + 1]["value"] + qs = line["conversations"][i]["value"] + cur_prompt = args.extra_prompt + qs + + args.conv_mode = "qwen_1_5" + + conv = conv_templates[args.conv_mode].copy() + conv.append_message(conv.roles[0], qs) + conv.append_message(conv.roles[1], None) + prompt = conv.get_prompt() + + input_ids_new = preprocess_qwen([line["conversations"][i],{'from': 'gpt','value': None}], tokenizer, has_image=True).cuda() + input_ids = torch.cat((input_ids, input_ids_new), dim=1) + img_num = list(input_ids_new.squeeze()).count(IMAGE_TOKEN_INDEX) + + stop_str = conv.sep if conv.sep_style != SeparatorStyle.TWO else conv.sep2 + keywords = [stop_str] + stopping_criteria = KeywordsStoppingCriteria(keywords, tokenizer, input_ids) + + with torch.inference_mode(): + output_ids = model.generate( + input_ids, + images=image_tensors, + do_sample=True if args.temperature > 0 else False, + temperature=args.temperature, + top_p=args.top_p, + num_beams=args.num_beams, + # no_repeat_ngram_size=3, + max_new_tokens=1024, + use_cache=True) + + outputs = tokenizer.batch_decode(output_ids, skip_special_tokens=True)[0] + outputs = outputs.strip() + if outputs.endswith(stop_str): + outputs = outputs[:-len(stop_str)] + outputs = outputs.strip() + + ans_id = shortuuid.uuid() + ans_file.write(json.dumps({ + "dataset": dataset_name, + "sample_id": idx, + "prompt": cur_prompt, + "pred_response": outputs, + "gt_response": gt, + "shortuuid": ans_id, + "model_id": model_name, + "question_type": question_type, + }) + "\n") + ans_file.flush() + + + ans_file.close() + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--model-path", type=str, default="facebook/opt-350m") + parser.add_argument("--model-base", type=str, default=None) + parser.add_argument("--image-folder", type=str, default="") + parser.add_argument("--extra-prompt", type=str, default="") + parser.add_argument("--question-file", type=str, default="tables/question.jsonl") + parser.add_argument("--answers-file", type=str, default="answer.jsonl") + parser.add_argument("--conv-mode", type=str, default="llava_v1") + parser.add_argument("--num-chunks", type=int, default=1) + parser.add_argument("--chunk-idx", type=int, default=0) + parser.add_argument("--temperature", type=float, default=0.2) + parser.add_argument("--top_p", type=float, default=None) + parser.add_argument("--num_beams", type=int, default=1) + parser.add_argument("--test_size", type=int, default=10000000) + args = parser.parse_args() + + eval_model(args) \ No newline at end of file diff --git a/llava/mm_utils.py b/llava/mm_utils.py new file mode 100644 index 0000000..177f865 --- /dev/null +++ b/llava/mm_utils.py @@ -0,0 +1,399 @@ +from PIL import Image +from io import BytesIO +import base64 +import math +import ast +import re +import torch +from transformers import StoppingCriteria +from llava.constants import IMAGE_TOKEN_INDEX + + +def resize_and_center_crop(image, shortest_edge_length): + # Calculate new dimensions and resize + aspect_ratio = float(image.width) / float(image.height) + if aspect_ratio > 1: + new_width = int(shortest_edge_length * aspect_ratio) + new_height = shortest_edge_length + else: + new_width = shortest_edge_length + new_height = int(shortest_edge_length / aspect_ratio) + resized_image = image.resize((new_width, new_height), Image.ANTIALIAS) + + # Calculate the position and perform the center crop + left = (new_width - shortest_edge_length) / 2 + top = (new_height - shortest_edge_length) / 2 + right = (new_width + shortest_edge_length) / 2 + bottom = (new_height + shortest_edge_length) / 2 + cropped_image = resized_image.crop((left, top, right, bottom)) + + return cropped_image + + +def auto_pad_images(image, grid_params): + assert isinstance(image, Image.Image), "Input should be a Pillow Image" + assert len(grid_params) > 0, "Grid parameters should not be empty" + + # Step 1: Calculate and find the closest aspect ratio + input_width, input_height = image.size + input_aspect_ratio = input_width / input_height + candidate_resolutions = [(w / h, w, h) for w in grid_params for h in grid_params] + closest_aspect_ratio = min(candidate_resolutions, key=lambda x: abs(input_aspect_ratio - x[0])) + + candidate_resolutions = [(x[1], x[2]) for x in candidate_resolutions if abs(x[0] - closest_aspect_ratio[0]) < 1e-3] + + target_resolution = min(candidate_resolutions, key=lambda res: abs(max(input_width, input_height) / max(res) - 1)) + + resize_width, resize_height = target_resolution + if input_width > input_height: + resize_height = int(resize_width / input_aspect_ratio) + else: + resize_width = int(resize_height * input_aspect_ratio) + resized_image = image.resize((resize_width, resize_height), Image.ANTIALIAS) + + # Step 5: Pad the resized image if necessary to match the target resolution + pad_width = target_resolution[0] - resize_width + pad_height = target_resolution[1] - resize_height + padded_image = Image.new("RGB", target_resolution, color=(0, 0, 0)) + padded_image.paste(resized_image, (pad_width // 2, pad_height // 2)) + + return padded_image + + +def extract_patches(image, patch_size, overlap_ratio): + assert isinstance(image, Image.Image), "Input should be a Pillow Image" + assert patch_size > 0, "Patch size should be greater than 0" + assert 0 <= overlap_ratio < 1, "Overlap ratio should be between 0 and 1" + + W, H = image.size + patches = [] + + stride = int(patch_size * (1 - overlap_ratio)) + + num_patches_y = (H - patch_size) // stride + 1 + num_patches_x = (W - patch_size) // stride + 1 + + y_start = (H - (num_patches_y - 1) * stride - patch_size) // 2 + x_start = (W - (num_patches_x - 1) * stride - patch_size) // 2 + + for y in range(y_start, y_start + num_patches_y * stride, stride): + for x in range(x_start, x_start + num_patches_x * stride, stride): + patch = image.crop((x, y, x + patch_size, y + patch_size)) + patches.append(patch) + + return patches + + +def process_highres_image_crop_split(image, data_args, processor=None): + crop_resolution = data_args.image_crop_resolution + split_resolution = data_args.image_split_resolution + if processor is None: + processor = data_args.image_processor + image_crop = resize_and_center_crop(image, crop_resolution) + image_patches = extract_patches(image_crop, patch_size=split_resolution, overlap_ratio=0) + image_patches = [processor.preprocess(image_patch, return_tensors="pt")["pixel_values"][0] for image_patch in image_patches] + return torch.stack(image_patches, dim=0) + + +def process_highres_image(image, processor, grid_pinpoints): + grid_params = [int(x) for x in grid_pinpoints.split(",")] + width_height = max(image.size) + fit_grid_params = [x for x in grid_params if x >= width_height] + if len(fit_grid_params) == 0: + select_size = max(grid_params) + else: + select_size = min(fit_grid_params) + # FIXME: always select the 448 + select_size = max(grid_params) + image_padded = expand2square(image, tuple(int(x * 255) for x in processor.image_mean)) + + # FIXME: this seems to be a bug that it always resizes instead of padding + image_original_resize = image.resize((processor.size["shortest_edge"], processor.size["shortest_edge"])) + image_padded = image_padded.resize((select_size, select_size)) + image_patches = extract_patches(image_padded, patch_size=processor.size["shortest_edge"], overlap_ratio=0) + image_patches = [image_original_resize] + image_patches + image_patches = [processor.preprocess(image_patch, return_tensors="pt")["pixel_values"][0] for image_patch in image_patches] + return torch.stack(image_patches, dim=0) + + +def select_best_resolution(original_size, possible_resolutions): + """ + Selects the best resolution from a list of possible resolutions based on the original size. + + Args: + original_size (tuple): The original size of the image in the format (width, height). + possible_resolutions (list): A list of possible resolutions in the format [(width1, height1), (width2, height2), ...]. + + Returns: + tuple: The best fit resolution in the format (width, height). + """ + original_width, original_height = original_size + best_fit = None + max_effective_resolution = 0 + min_wasted_resolution = float("inf") + + for width, height in possible_resolutions: + # Calculate the downscaled size to keep the aspect ratio + scale = min(width / original_width, height / original_height) + downscaled_width, downscaled_height = int(original_width * scale), int(original_height * scale) + + # Calculate effective and wasted resolutions + effective_resolution = min(downscaled_width * downscaled_height, original_width * original_height) + wasted_resolution = (width * height) - effective_resolution + + if effective_resolution > max_effective_resolution or (effective_resolution == max_effective_resolution and wasted_resolution < min_wasted_resolution): + max_effective_resolution = effective_resolution + min_wasted_resolution = wasted_resolution + best_fit = (width, height) + + return best_fit + + +def resize_and_pad_image(image, target_resolution): + """ + Resize and pad an image to a target resolution while maintaining aspect ratio. + + Args: + image (PIL.Image.Image): The input image. + target_resolution (tuple): The target resolution (width, height) of the image. + + Returns: + PIL.Image.Image: The resized and padded image. + """ + original_width, original_height = image.size + target_width, target_height = target_resolution + + # Determine which dimension (width or height) to fill + scale_w = target_width / original_width + scale_h = target_height / original_height + + if scale_w < scale_h: + # Width will be filled completely + new_width = target_width + new_height = min(math.ceil(original_height * scale_w), target_height) + else: + # Height will be filled completely + new_height = target_height + new_width = min(math.ceil(original_width * scale_h), target_width) + + # Resize the image + resized_image = image.resize((new_width, new_height)) + + # Create a new image with the target size and paste the resized image onto it + new_image = Image.new("RGB", (target_width, target_height), (0, 0, 0)) + paste_x = (target_width - new_width) // 2 + paste_y = (target_height - new_height) // 2 + new_image.paste(resized_image, (paste_x, paste_y)) + + return new_image + + +def divide_to_patches(image, patch_size): + """ + Divides an image into patches of a specified size. + + Args: + image (PIL.Image.Image): The input image. + patch_size (int): The size of each patch. + + Returns: + list: A list of PIL.Image.Image objects representing the patches. + """ + patches = [] + width, height = image.size + for i in range(0, height, patch_size): + for j in range(0, width, patch_size): + box = (j, i, j + patch_size, i + patch_size) + patch = image.crop(box) + patches.append(patch) + + return patches + + +def get_anyres_image_grid_shape(image_size, grid_pinpoints, patch_size): + """ + Calculate the shape of the image patch grid after the preprocessing for images of any resolution. + + Args: + image_size (tuple): The size of the input image in the format (width, height). + grid_pinpoints (str): A string representation of a list of possible resolutions. + patch_size (int): The size of each image patch. + + Returns: + tuple: The shape of the image patch grid in the format (width, height). + """ + if isinstance(grid_pinpoints, str) and "x" in grid_pinpoints: + assert patch_size in [224, 336, 384, 448, 512], "patch_size should be in [224, 336, 384, 448, 512]" + # Use regex to extract the range from the input string + matches = re.findall(r"\((\d+)x(\d+)\)", grid_pinpoints) + range_start = tuple(map(int, matches[0])) + range_end = tuple(map(int, matches[-1])) + # Generate a matrix of tuples from (range_start[0], range_start[1]) to (range_end[0], range_end[1]) + grid_pinpoints = [(i, j) for i in range(range_start[0], range_end[0] + 1) for j in range(range_start[1], range_end[1] + 1)] + # Multiply all elements by patch_size + grid_pinpoints = [[dim * patch_size for dim in pair] for pair in grid_pinpoints] + if type(grid_pinpoints) is list: + possible_resolutions = grid_pinpoints + else: + possible_resolutions = ast.literal_eval(grid_pinpoints) + width, height = select_best_resolution(image_size, possible_resolutions) + return width // patch_size, height // patch_size + + +def process_anyres_image(image, processor, grid_pinpoints): + """ + Process an image with variable resolutions. + + Args: + image (PIL.Image.Image): The input image to be processed. + processor: The image processor object. + grid_pinpoints (str): A string representation of a list of possible resolutions. + + Returns: + torch.Tensor: A tensor containing the processed image patches. + """ + # Convert grid_pinpoints from string to list + if isinstance(grid_pinpoints, str) and "x" in grid_pinpoints: + try: + patch_size = processor.size[0] + except Exception as e: + patch_size = processor.size["shortest_edge"] + assert patch_size in [224, 336, 384, 448, 512], "patch_size should be in [224, 336, 384, 448, 512]" + # Use regex to extract the range from the input string + matches = re.findall(r"\((\d+)x(\d+)\)", grid_pinpoints) + range_start = tuple(map(int, matches[0])) + range_end = tuple(map(int, matches[-1])) + # Generate a matrix of tuples from (range_start[0], range_start[1]) to (range_end[0], range_end[1]) + grid_pinpoints = [(i, j) for i in range(range_start[0], range_end[0] + 1) for j in range(range_start[1], range_end[1] + 1)] + # Multiply all elements by patch_size + grid_pinpoints = [[dim * patch_size for dim in pair] for pair in grid_pinpoints] + + if type(grid_pinpoints) is list: + possible_resolutions = grid_pinpoints + else: + possible_resolutions = ast.literal_eval(grid_pinpoints) + best_resolution = select_best_resolution(image.size, possible_resolutions) + image_padded = resize_and_pad_image(image, best_resolution) + + patches = divide_to_patches(image_padded, processor.crop_size["height"]) + + + # FIXME: this seems to be a bug that it resizes instead of pad. + # but to keep it consistent with previous, i will keep it as it is + # TODO: uncomment below to ablate with the padding + if isinstance(processor.size, dict): + shortest_edge = processor.size["shortest_edge"] + else: + shortest_edge = min(processor.size) + + image_original_resize = image.resize((shortest_edge, shortest_edge)) + + # image_padded_square = expand2square(image, tuple(int(x*255) for x in processor.image_mean)) + # image_original_resize = image_padded_square.resize((processor.size['shortest_edge'], processor.size['shortest_edge'])) + + # image_patches = [image_original_resize] + patches + image_patches = patches + image_patches = [processor.preprocess(image_patch, return_tensors="pt")["pixel_values"][0] for image_patch in image_patches] + return torch.stack(image_patches, dim=0) + + +def load_image_from_base64(image): + return Image.open(BytesIO(base64.b64decode(image))) + + +def expand2square(pil_img, background_color): + width, height = pil_img.size + if width == height: + return pil_img + elif width > height: + result = Image.new(pil_img.mode, (width, width), background_color) + result.paste(pil_img, (0, (width - height) // 2)) + return result + else: + result = Image.new(pil_img.mode, (height, height), background_color) + result.paste(pil_img, ((height - width) // 2, 0)) + return result + + +def process_images(images, image_processor, model_cfg): + image_aspect_ratio = getattr(model_cfg, "image_aspect_ratio", None) + new_images = [] + if image_aspect_ratio == "highres": + for image in images: + image = process_highres_image(image, image_processor, model_cfg.image_grid_pinpoints) + new_images.append(image) + elif image_aspect_ratio == "anyres" or "anyres_max" in image_aspect_ratio: + for image in images: + image = process_anyres_image(image, image_processor, model_cfg.image_grid_pinpoints) + new_images.append(image) + elif image_aspect_ratio == "crop_split": + for image in images: + image = process_highres_image_crop_split(image, model_cfg, image_processor) + new_images.append(image) + elif image_aspect_ratio == "pad": + for image in images: + image = expand2square(image, tuple(int(x * 255) for x in image_processor.image_mean)) + image = image_processor.preprocess(image, return_tensors="pt")["pixel_values"][0] + new_images.append(image) + else: + return image_processor.preprocess(images, return_tensors="pt")["pixel_values"] + if all(x.shape == new_images[0].shape for x in new_images): + new_images = torch.stack(new_images, dim=0) + return new_images + + +def tokenizer_image_token(prompt, tokenizer, image_token_index=IMAGE_TOKEN_INDEX, return_tensors=None): + prompt_chunks = [tokenizer(chunk).input_ids for chunk in prompt.split("")] + + def insert_separator(X, sep): + return [ele for sublist in zip(X, [sep] * len(X)) for ele in sublist][:-1] + + input_ids = [] + offset = 0 + if len(prompt_chunks) > 0 and len(prompt_chunks[0]) > 0 and prompt_chunks[0][0] == tokenizer.bos_token_id: + offset = 1 + input_ids.append(prompt_chunks[0][0]) + + for x in insert_separator(prompt_chunks, [image_token_index] * (offset + 1)): + input_ids.extend(x[offset:]) + + if return_tensors is not None: + if return_tensors == "pt": + return torch.tensor(input_ids, dtype=torch.long) + raise ValueError(f"Unsupported tensor type: {return_tensors}") + return input_ids + + +def get_model_name_from_path(model_path): + model_path = model_path.strip("/") + model_paths = model_path.split("/") + if model_paths[-1].startswith("checkpoint-"): + return model_paths[-2] + "_" + model_paths[-1] + else: + return model_paths[-1] + + +class KeywordsStoppingCriteria(StoppingCriteria): + def __init__(self, keywords, tokenizer, input_ids): + self.keywords = keywords + self.keyword_ids = [] + for keyword in keywords: + cur_keyword_ids = tokenizer(keyword).input_ids + if len(cur_keyword_ids) > 1 and cur_keyword_ids[0] == tokenizer.bos_token_id: + cur_keyword_ids = cur_keyword_ids[1:] + self.keyword_ids.append(torch.tensor(cur_keyword_ids)) + self.tokenizer = tokenizer + self.start_len = input_ids.shape[1] + + def __call__(self, output_ids: torch.LongTensor, scores: torch.FloatTensor, **kwargs) -> bool: + assert output_ids.shape[0] == 1, "Only support batch size 1 (yet)" # TODO + offset = min(output_ids.shape[1] - self.start_len, 3) + self.keyword_ids = [keyword_id.to(output_ids.device) for keyword_id in self.keyword_ids] + for keyword_id in self.keyword_ids: + if output_ids[0, -keyword_id.shape[0] :] == keyword_id: + return True + outputs = self.tokenizer.batch_decode(output_ids[:, -offset:], skip_special_tokens=True)[0] + for keyword in self.keywords: + if keyword in outputs: + return True + return False diff --git a/llava/model/__init__.py b/llava/model/__init__.py new file mode 100644 index 0000000..1abf0b3 --- /dev/null +++ b/llava/model/__init__.py @@ -0,0 +1,16 @@ +import os + +AVAILABLE_MODELS = { + "llava_llama": "LlavaLlamaForCausalLM, LlavaConfig", + "llava_qwen": "LlavaQwenForCausalLM, LlavaQwenConfig", + "llava_mistral": "LlavaMistralForCausalLM, LlavaMistralConfig", + "llava_mixtral": "LlavaMixtralForCausalLM, LlavaMixtralConfig", + # "llava_qwen_moe": "LlavaQwenMoeForCausalLM, LlavaQwenMoeConfig", + # Add other models as needed +} + +for model_name, model_classes in AVAILABLE_MODELS.items(): + try: + exec(f"from .language_model.{model_name} import {model_classes}") + except Exception as e: + print(f"Failed to import {model_name} from llava.language_model.{model_name}. Error: {e}") diff --git a/llava/model/__pycache__/__init__.cpython-310.pyc b/llava/model/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e2c2bcc17a3b43d73f6ea1a19e9df25667504732 GIT binary patch literal 703 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 literal 0 HcmV?d00001 diff --git a/llava/model/__pycache__/llava_arch.cpython-310.pyc b/llava/model/__pycache__/llava_arch.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d3dd156bc965a001b6d5c323c97d879de4862bb GIT binary patch literal 16884 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

w29b2eFDB*}b2J?j^+i=KKNxUWDd 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 literal 0 HcmV?d00001 diff --git a/llava/model/language_model/__pycache__/llava_mixtral.cpython-310.pyc b/llava/model/language_model/__pycache__/llava_mixtral.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1ca356a551a2d5daf0148b1c569a2e20ebcc2007 GIT binary patch literal 4123 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 b+yruA!J`2nbBhM>?a+?7WG%S??~wlk%8=Nx literal 0 HcmV?d00001 diff --git a/llava/model/language_model/__pycache__/llava_qwen.cpython-310.pyc b/llava/model/language_model/__pycache__/llava_qwen.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5a4ac3d9c81f44119411b92251aacaed058ce216 GIT binary patch literal 4159 zcmaJ^>yO;F5f`cFYPFAh?LBv6c{@B-E#g?(2!+8;R+g1lwqA`Ih?HiQ_v; zrpI_*Avh8l7`<%ntn5B`7PDQjN8eQzod98UQSm06~)`}YO?09 zB^|$`>JI2w_t%pReUHKp-Q#O~yiWS9r=rSn#T)OJx(r-wSW> zBCPbS&g+Xhby@$(J3M`Mwg6R`EG>0h3BOAt6eDW^=nQfTszQG7L2lQQSKp18EvFBUwYKG?903&1S2{gkEoLX9a z1&BUXx+;RaJ}o(}rqBvfYDe^hmgJNmPv-c^uE?Q;tH53N_RCOm3y7l`WQ(c|JYwM|p&g(6rvR?cTbR*f z%{Q6O4ES2ie9UVN3A33wXfcP|cN|vZHTb%lm>7_AfU5&n2j1J%pynw#@T@WK(FCpm zlGgOwo_?295A3@PTpivf>VW#nw8yHFx0GycD(mnzU*gLHx~UB=E0nCWjpK&D!tImQ zQ*F8vSoy$Un=qzYZ#5UL9OtTJi`r4^8tzExb(P*NcImirMEnk`=GkUD$2EUlp>YGy z_=K$T0HSd*w9jJ^)BZK|!%IL4?F6t0IK4RLyCioUC^d|L z2RcaIQah=iY87_7Mo+APuIrGjj9XEdcRjHJbp;`@37qsGaq=MVhjEk+D}0+OSS3wU zX&po<4~4AXju_)^$F#X~7b!u+oEVBe9dVry#zCqr?k8@cY=RD|#{o7nFW$IW7=6Z9vIu*@~UceCpW7T4E zSyx|Wey9juj71tSI1i-@jsrgy5%jLJQ5Fm`@lGh1w5ncxJThhv;sBkYL&<*&q%@f} z)!;2ytO=Ru6FM;_=EN$S;yExrz!G}t(9bOG(kW}D$H;@) zq+PZdgxzEbbkTC@luVY(<tHRSG!AGIs09`oA#zuN$fklrFoTn{D-ZyL&cPtghEb7=r*Ndo z_$t|BOrWwHjpM95O$=%f`p9d2?07D5h^0T;Mv3*&;r4|wF$Yp*r#@z?*@IVEEOjXP zuYr{231lp$ol*b_@{qZJx0zm=_uyD72s4hzZvX-SI3m6dK!WoYpk~80Ra3CjnfaiC zoHem0&ZIVRQX9aepK2!n9spFWFabywvvFKmW7d11{eAL#0uXX~6>JoM5H^RqZ+$Kf z-`b7x-83t9;Zz@GqTmej20EpypBt$x+_j}X%0_afmldxja1Bi$190}T5Sb7^f?)zv zoAgwW2Ss+5r+EbxXE?nf0zOq<`Z4xC!vK0rT6ZBxhxysek^~jn&-wTOdKXaC)z7PW z;754rS4fj?JuZ>X7SNwvqhLH-1~d&_s9g3YFr_hfnW^~7WmZAJ$l#oTidUw;2RQ+a zJh4@_AmT}kd`7NlCH#YcGrP)-6zb|4);rs4m!5?&nE6$hwX)I9xfsOYo30wNEL@9} z;Z%^F<$)VG{LGHZtmmwC6T18kN@XqLk*&&JKn-&E?1ec0|4X60UU&1NTblSHE@u~s zLV{UjqJLYu2U$9N1Hb?<(V!S-k1dye1X}*t^yojV2K~F+q<^zo^snwx^Uv1vROJCl!2-ptG(m#E?xs79c9eDG!et)PAKS_z-?);Riw`>@$LHlFk)a8T^L5fCpKQ emznLB19*k_rrU5u@<>}>C2ecjShl@EoBR)KJ>JOx literal 0 HcmV?d00001 diff --git a/llava/model/language_model/llava_gemma.py b/llava/model/language_model/llava_gemma.py new file mode 100644 index 0000000..5c0ac17 --- /dev/null +++ b/llava/model/language_model/llava_gemma.py @@ -0,0 +1,122 @@ +# Copyright 2024 Duc Q. Nguyen, Haotian Liu and Bo Li +# +# 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. + + +from typing import List, Optional, Tuple, Union + +import torch +import torch.nn as nn +from torch.nn import CrossEntropyLoss + +from transformers import AutoConfig, AutoModelForCausalLM, GemmaConfig, GemmaModel, GemmaForCausalLM + +from transformers.modeling_outputs import CausalLMOutputWithPast +from transformers.generation.utils import GenerateOutput + +from ..llava_arch import LlavaMetaModel, LlavaMetaForCausalLM + + +class LlavaGemmaConfig(GemmaConfig): + model_type = "llava_gemma" + + +class LlavaGemmaModel(LlavaMetaModel, GemmaModel): + config_class = LlavaGemmaConfig + + def __init__(self, config: GemmaConfig): + super(LlavaGemmaModel, self).__init__(config) + + +class LlavaGemmaForCausalLM(GemmaForCausalLM, LlavaMetaForCausalLM): + config_class = LlavaGemmaConfig + + def __init__(self, config): + super(GemmaForCausalLM, self).__init__(config) + self.model = LlavaGemmaModel(config) + + self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) + + # Initialize weights and apply final processing + self.post_init() + + def get_model(self): + return self.model + + def forward( + self, + input_ids: torch.LongTensor = None, + attention_mask: Optional[torch.Tensor] = None, + position_ids: Optional[torch.LongTensor] = None, + past_key_values: Optional[List[torch.FloatTensor]] = None, + inputs_embeds: Optional[torch.FloatTensor] = None, + labels: Optional[torch.LongTensor] = None, + use_cache: Optional[bool] = None, + output_attentions: Optional[bool] = None, + output_hidden_states: Optional[bool] = None, + images: Optional[torch.FloatTensor] = None, + image_sizes: Optional[List[List[int]]] = None, + return_dict: Optional[bool] = None, + cache_position: Optional[torch.LongTensor] = None, + ) -> Union[Tuple, CausalLMOutputWithPast]: + + if inputs_embeds is None: + (input_ids, position_ids, attention_mask, past_key_values, inputs_embeds, labels) = self.prepare_inputs_labels_for_multimodal(input_ids, position_ids, attention_mask, past_key_values, labels, images, image_sizes) + + return super().forward( + input_ids=input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + past_key_values=past_key_values, + inputs_embeds=inputs_embeds, + labels=labels, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + cache_position=cache_position, + ) + + @torch.no_grad() + def generate( + self, + inputs: Optional[torch.Tensor] = None, + images: Optional[torch.Tensor] = None, + image_sizes: Optional[torch.Tensor] = None, + **kwargs, + ) -> Union[GenerateOutput, torch.LongTensor]: + position_ids = kwargs.pop("position_ids", None) + attention_mask = kwargs.pop("attention_mask", None) + if "inputs_embeds" in kwargs: + raise NotImplementedError("`inputs_embeds` is not supported") + + if images is not None: + (inputs, position_ids, attention_mask, _, inputs_embeds, _) = self.prepare_inputs_labels_for_multimodal(inputs, position_ids, attention_mask, None, None, images, image_sizes=image_sizes) + else: + inputs_embeds = self.get_model().embed_tokens(inputs) + + return super().generate(position_ids=position_ids, attention_mask=attention_mask, inputs_embeds=inputs_embeds, **kwargs) + + def prepare_inputs_for_generation(self, input_ids, past_key_values=None, inputs_embeds=None, **kwargs): + images = kwargs.pop("images", None) + image_sizes = kwargs.pop("image_sizes", None) + inputs = super().prepare_inputs_for_generation(input_ids, past_key_values=past_key_values, inputs_embeds=inputs_embeds, **kwargs) + if images is not None: + inputs["images"] = images + if image_sizes is not None: + inputs["image_sizes"] = image_sizes + return inputs + + +AutoConfig.register("llava_gemma", LlavaGemmaConfig) +AutoModelForCausalLM.register(LlavaGemmaConfig, LlavaGemmaForCausalLM) diff --git a/llava/model/language_model/llava_llama.py b/llava/model/language_model/llava_llama.py new file mode 100644 index 0000000..1a930e9 --- /dev/null +++ b/llava/model/language_model/llava_llama.py @@ -0,0 +1,156 @@ +# Copyright 2023 Haotian Liu +# +# 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. + + +from typing import List, Optional, Tuple, Union + +import torch +import torch.nn as nn + +from transformers import AutoConfig, AutoModelForCausalLM, LlamaConfig + +from torch.nn import CrossEntropyLoss + + +# , LlamaModel, LlamaForCausalLM, GenerationConfig +# from .modeling_llama import LlamaModel, LlamaForCausalLM +from transformers import LlamaModel, LlamaForCausalLM +from transformers.modeling_outputs import CausalLMOutputWithPast +from transformers.generation.utils import GenerateOutput + +from llava.model.llava_arch import LlavaMetaModel, LlavaMetaForCausalLM + + +class LlavaConfig(LlamaConfig): + model_type = "llava_llama" + temperature: float = 0.0 # reset to 0.0, previously 0.9 for Vicuna + max_new_tokens: int = 1024 + do_sample: bool = False + top_p: Optional[float] = None + # rope_scaling: Optional[dict] = {} + + +class LlavaLlamaModel(LlavaMetaModel, LlamaModel): + config_class = LlavaConfig + + def __init__(self, config: LlamaConfig): + super(LlavaLlamaModel, self).__init__(config) + + +class LlavaLlamaForCausalLM(LlamaForCausalLM, LlavaMetaForCausalLM): + config_class = LlavaConfig + + def __init__(self, config): + LlamaForCausalLM.__init__(self, config) + + # configure default generation settings + config.model_type = "llava_llama" + # config.rope_scaling = None + + self.model = LlavaLlamaModel(config) + self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) + # Initialize weights and apply final processing + self.post_init() + + def get_model(self): + return self.model + + def forward( + self, + input_ids: torch.LongTensor = None, + attention_mask: Optional[torch.Tensor] = None, + position_ids: Optional[torch.LongTensor] = None, + past_key_values: Optional[List[torch.FloatTensor]] = None, + inputs_embeds: Optional[torch.FloatTensor] = None, + labels: Optional[torch.LongTensor] = None, + use_cache: Optional[bool] = None, + output_attentions: Optional[bool] = None, + output_hidden_states: Optional[bool] = None, + images: Optional[torch.FloatTensor] = None, + image_sizes: Optional[List[List[int]]] = None, + return_dict: Optional[bool] = None, + modalities: Optional[List[str]] = ["image"], + dpo_forward: Optional[bool] = None, + cache_position=None, + ) -> Union[Tuple, CausalLMOutputWithPast]: + + if inputs_embeds is None: + (input_ids, position_ids, attention_mask, past_key_values, inputs_embeds, labels) = self.prepare_inputs_labels_for_multimodal(input_ids, position_ids, attention_mask, past_key_values, labels, images, modalities, image_sizes) + + if dpo_forward: + outputs = self.model( + input_ids=input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + past_key_values=past_key_values, + inputs_embeds=inputs_embeds, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + ) + + hidden_states = outputs[0] + logits = self.lm_head(hidden_states) + return logits, labels + + else: + return super().forward( + input_ids=input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + past_key_values=past_key_values, + inputs_embeds=inputs_embeds, + labels=labels, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + ) + + @torch.no_grad() + def generate( + self, + inputs: Optional[torch.Tensor] = None, + images: Optional[torch.Tensor] = None, + image_sizes: Optional[torch.Tensor] = None, + modalities: Optional[List[str]] = ["image"], + **kwargs, + ) -> Union[GenerateOutput, torch.LongTensor]: + modalities = kwargs.pop("modalities", None) if "modalities" in kwargs and modalities is None else modalities + position_ids = kwargs.pop("position_ids", None) + attention_mask = kwargs.pop("attention_mask", None) + if "inputs_embeds" in kwargs: + raise NotImplementedError("`inputs_embeds` is not supported") + + if images is not None: + (inputs, position_ids, attention_mask, _, inputs_embeds, _) = self.prepare_inputs_labels_for_multimodal(inputs, position_ids, attention_mask, None, None, images, modalities, image_sizes=image_sizes) + else: + inputs_embeds = self.get_model().embed_tokens(inputs) + + return super().generate(position_ids=position_ids, attention_mask=attention_mask, inputs_embeds=inputs_embeds, **kwargs) + + def prepare_inputs_for_generation(self, input_ids, past_key_values=None, inputs_embeds=None, **kwargs): + images = kwargs.pop("images", None) + image_sizes = kwargs.pop("image_sizes", None) + inputs = super().prepare_inputs_for_generation(input_ids, past_key_values=past_key_values, inputs_embeds=inputs_embeds, **kwargs) + if images is not None: + inputs["images"] = images + if image_sizes is not None: + inputs["image_sizes"] = image_sizes + return inputs + + +AutoConfig.register("llava_llama", LlavaConfig) +AutoModelForCausalLM.register(LlavaConfig, LlavaLlamaForCausalLM) diff --git a/llava/model/language_model/llava_mistral.py b/llava/model/language_model/llava_mistral.py new file mode 100644 index 0000000..2cc3b01 --- /dev/null +++ b/llava/model/language_model/llava_mistral.py @@ -0,0 +1,127 @@ +# Copyright 2023 Haotian Liu +# +# 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. + + +from typing import List, Optional, Tuple, Union + +import torch +import torch.nn as nn +from torch.nn import CrossEntropyLoss + +from transformers import AutoConfig, AutoModelForCausalLM, MistralConfig, MistralModel, MistralForCausalLM, GenerationConfig + +from transformers.modeling_outputs import CausalLMOutputWithPast +from transformers.generation.utils import GenerateOutput + +from ..llava_arch import LlavaMetaModel, LlavaMetaForCausalLM + + +class LlavaMistralConfig(MistralConfig): + model_type = "llava_mistral" + temperature: float = 0.0 # reset to 0.0, previously 0.9 for Vicuna + max_new_tokens: int = 1024 + do_sample: bool = False + top_p: Optional[float] = None + + +class LlavaMistralModel(LlavaMetaModel, MistralModel): + config_class = LlavaMistralConfig + + def __init__(self, config: MistralConfig): + super(LlavaMistralModel, self).__init__(config) + + +class LlavaMistralForCausalLM(MistralForCausalLM, LlavaMetaForCausalLM): + config_class = LlavaMistralConfig + + def __init__(self, config): + super(MistralForCausalLM, self).__init__(config) + + config.model_type = "llava_mistral" + config.rope_scaling = None + + self.model = LlavaMistralModel(config) + self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) + # Initialize weights and apply final processing + self.post_init() + + def get_model(self): + return self.model + + def forward( + self, + input_ids: torch.LongTensor = None, + attention_mask: Optional[torch.Tensor] = None, + position_ids: Optional[torch.LongTensor] = None, + past_key_values: Optional[List[torch.FloatTensor]] = None, + inputs_embeds: Optional[torch.FloatTensor] = None, + labels: Optional[torch.LongTensor] = None, + use_cache: Optional[bool] = None, + output_attentions: Optional[bool] = None, + output_hidden_states: Optional[bool] = None, + images: Optional[torch.FloatTensor] = None, + image_sizes: Optional[List[List[int]]] = None, + return_dict: Optional[bool] = None, + cache_position=None, + ) -> Union[Tuple, CausalLMOutputWithPast]: + + if inputs_embeds is None: + (input_ids, position_ids, attention_mask, past_key_values, inputs_embeds, labels) = self.prepare_inputs_labels_for_multimodal(input_ids, position_ids, attention_mask, past_key_values, labels, images, image_sizes) + + return super().forward( + input_ids=input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + past_key_values=past_key_values, + inputs_embeds=inputs_embeds, + labels=labels, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + ) + + @torch.no_grad() + def generate( + self, + inputs: Optional[torch.Tensor] = None, + images: Optional[torch.Tensor] = None, + image_sizes: Optional[torch.Tensor] = None, + **kwargs, + ) -> Union[GenerateOutput, torch.LongTensor]: + position_ids = kwargs.pop("position_ids", None) + attention_mask = kwargs.pop("attention_mask", None) + if "inputs_embeds" in kwargs: + raise NotImplementedError("`inputs_embeds` is not supported") + + if images is not None: + (inputs, position_ids, attention_mask, _, inputs_embeds, _) = self.prepare_inputs_labels_for_multimodal(inputs, position_ids, attention_mask, None, None, images, image_sizes=image_sizes) + else: + inputs_embeds = self.get_model().embed_tokens(inputs) + + return super().generate(position_ids=position_ids, attention_mask=attention_mask, inputs_embeds=inputs_embeds, **kwargs) + + def prepare_inputs_for_generation(self, input_ids, past_key_values=None, inputs_embeds=None, **kwargs): + images = kwargs.pop("images", None) + image_sizes = kwargs.pop("image_sizes", None) + inputs = super().prepare_inputs_for_generation(input_ids, past_key_values=past_key_values, inputs_embeds=inputs_embeds, **kwargs) + if images is not None: + inputs["images"] = images + if image_sizes is not None: + inputs["image_sizes"] = image_sizes + return inputs + + +AutoConfig.register("llava_mistral", LlavaMistralConfig) +AutoModelForCausalLM.register(LlavaMistralConfig, LlavaMistralForCausalLM) diff --git a/llava/model/language_model/llava_mixtral.py b/llava/model/language_model/llava_mixtral.py new file mode 100644 index 0000000..ca6c25d --- /dev/null +++ b/llava/model/language_model/llava_mixtral.py @@ -0,0 +1,143 @@ +# Copyright 2023 Haotian Liu +# +# 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. + + +from typing import List, Optional, Tuple, Union + +import torch +import torch.nn as nn +from torch.nn import CrossEntropyLoss + +from transformers import AutoConfig, AutoModelForCausalLM, MixtralConfig, MixtralModel, MixtralForCausalLM, GenerationConfig + +from transformers.modeling_outputs import CausalLMOutputWithPast +from transformers.generation.utils import GenerateOutput + +from ..llava_arch import LlavaMetaModel, LlavaMetaForCausalLM + + +class LlavaMixtralConfig(MixtralConfig): + model_type = "llava_mixtral" + + +class LlavaMixtralModel(LlavaMetaModel, MixtralModel): + config_class = LlavaMixtralConfig + + def __init__(self, config: MixtralConfig): + super(LlavaMixtralModel, self).__init__(config) + + +class LlavaMixtralForCausalLM(MixtralForCausalLM, LlavaMetaForCausalLM): + config_class = LlavaMixtralConfig + + def __init__(self, config): + super(MixtralForCausalLM, self).__init__(config) + + config.model_type = "llava_mixtral" + config.rope_scaling = None + self.model = LlavaMixtralModel(config) + self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) + # Initialize weights and apply final processing + self.post_init() + + def get_model(self): + return self.model + + def forward( + self, + input_ids: torch.LongTensor = None, + attention_mask: Optional[torch.Tensor] = None, + position_ids: Optional[torch.LongTensor] = None, + past_key_values: Optional[List[torch.FloatTensor]] = None, + inputs_embeds: Optional[torch.FloatTensor] = None, + labels: Optional[torch.LongTensor] = None, + use_cache: Optional[bool] = None, + output_attentions: Optional[bool] = None, + output_hidden_states: Optional[bool] = None, + images: Optional[torch.FloatTensor] = None, + image_sizes: Optional[List[List[int]]] = None, + return_dict: Optional[bool] = None, + modalities: Optional[List[str]] = ["image"], + dpo_forward: Optional[bool] = None, + cache_position=None, + ) -> Union[Tuple, CausalLMOutputWithPast]: + + if inputs_embeds is None: + (input_ids, position_ids, attention_mask, past_key_values, inputs_embeds, labels) = self.prepare_inputs_labels_for_multimodal(input_ids, position_ids, attention_mask, past_key_values, labels, images, modalities, image_sizes) + + if dpo_forward: + outputs = self.model( + input_ids=input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + past_key_values=past_key_values, + inputs_embeds=inputs_embeds, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + ) + + hidden_states = outputs[0] + logits = self.lm_head(hidden_states) + return logits, labels + + else: + return super().forward( + input_ids=input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + past_key_values=past_key_values, + inputs_embeds=inputs_embeds, + labels=labels, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + ) + + @torch.no_grad() + def generate( + self, + inputs: Optional[torch.Tensor] = None, + images: Optional[torch.Tensor] = None, + image_sizes: Optional[torch.Tensor] = None, + modalities: Optional[List[str]] = ["image"], + **kwargs, + ) -> Union[GenerateOutput, torch.LongTensor]: + position_ids = kwargs.pop("position_ids", None) + attention_mask = kwargs.pop("attention_mask", None) + if "inputs_embeds" in kwargs: + raise NotImplementedError("`inputs_embeds` is not supported") + + if images is not None: + (inputs, position_ids, attention_mask, _, inputs_embeds, _) = self.prepare_inputs_labels_for_multimodal(inputs, position_ids, attention_mask, None, None, images, modalities, image_sizes=image_sizes) + else: + inputs_embeds = self.get_model().embed_tokens(inputs) + + return super().generate(position_ids=position_ids, attention_mask=attention_mask, inputs_embeds=inputs_embeds, **kwargs) + + def prepare_inputs_for_generation(self, input_ids, past_key_values=None, inputs_embeds=None, **kwargs): + images = kwargs.pop("images", None) + image_sizes = kwargs.pop("image_sizes", None) + inputs = super().prepare_inputs_for_generation(input_ids, past_key_values=past_key_values, inputs_embeds=inputs_embeds, **kwargs) + if images is not None: + inputs["images"] = images + if image_sizes is not None: + inputs["image_sizes"] = image_sizes + return inputs + + +AutoConfig.register("llava_mixtral", LlavaMixtralConfig) +AutoModelForCausalLM.register(LlavaMixtralConfig, LlavaMixtralForCausalLM) diff --git a/llava/model/language_model/llava_mpt.py b/llava/model/language_model/llava_mpt.py new file mode 100644 index 0000000..c3bce7d --- /dev/null +++ b/llava/model/language_model/llava_mpt.py @@ -0,0 +1,105 @@ +# Copyright 2023 Haotian Liu +# +# 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. + + +from typing import Optional, Tuple + +import torch + +from transformers import AutoConfig, AutoModelForCausalLM, MptConfig, MptForCausalLM, MptModel, GenerationConfig +from llava.model.llava_arch import LlavaMetaModel, LlavaMetaForCausalLM + + +class LlavaMptConfig(MptConfig): + model_type = "llava_mpt" + + +class LlavaMptModel(LlavaMetaModel, MptModel): + config_class = LlavaMptConfig + + def __init__(self, config: MptConfig): + config.hidden_size = config.d_model + super(LlavaMptModel, self).__init__(config) + + def embed_tokens(self, x): + return self.wte(x) + + +class LlavaMptForCausalLM(MptForCausalLM, LlavaMetaForCausalLM): + config_class = LlavaMptConfig + supports_gradient_checkpointing = True + + def __init__(self, config): + super(MptForCausalLM, self).__init__(config) + + config.model_type = "llava_mpt" + config.rope_scaling = None + self.generation_config = GenerationConfig( + temperature=0.0, + max_new_tokens=1024, + do_sample=False, + top_p=None, + ) + + self.transformer = LlavaMptModel(config) + self.lm_head = torch.nn.Linear(config.hidden_size, config.vocab_size, bias=False) + + # Initialize weights and apply final processing + self.post_init() + + def get_model(self): + return self.transformer + + def _set_gradient_checkpointing(self, module, value=False): + if isinstance(module, LlavaMptModel): + module.gradient_checkpointing = value + + def forward( + self, + input_ids: Optional[torch.LongTensor] = None, + past_key_values: Optional[Tuple[Tuple[torch.Tensor, torch.Tensor], ...]] = None, + attention_mask: Optional[torch.Tensor] = None, + inputs_embeds: Optional[torch.Tensor] = None, + labels: Optional[torch.Tensor] = None, + use_cache: Optional[bool] = None, + output_attentions: Optional[bool] = None, + output_hidden_states: Optional[bool] = None, + return_dict: Optional[bool] = None, + cache_position=None, + images=None, + ): + + input_ids, attention_mask, past_key_values, inputs_embeds, labels = self.prepare_inputs_labels_for_multimodal(input_ids, attention_mask, past_key_values, labels, images) + + return super().forward( + input_ids, + past_key_values=past_key_values, + attention_mask=attention_mask, + inputs_embeds=inputs_embeds, + labels=labels, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + ) + + def prepare_inputs_for_generation(self, input_ids, past_key_values=None, inputs_embeds=None, **kwargs): + images = kwargs.pop("images", None) + _inputs = super().prepare_inputs_for_generation(input_ids, past_key_values=past_key_values, inputs_embeds=inputs_embeds, **kwargs) + _inputs["images"] = images + return _inputs + + +AutoConfig.register("llava_mpt", LlavaMptConfig) +AutoModelForCausalLM.register(LlavaMptConfig, LlavaMptForCausalLM) diff --git a/llava/model/language_model/llava_qwen.py b/llava/model/language_model/llava_qwen.py new file mode 100644 index 0000000..c194747 --- /dev/null +++ b/llava/model/language_model/llava_qwen.py @@ -0,0 +1,149 @@ +# Copyright 2024 Hao Zhang +# +# 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. + + +from typing import List, Optional, Tuple, Union, Dict +import torch +import torch.nn as nn +from torch.nn import CrossEntropyLoss + +import transformers +from transformers import AutoConfig, AutoModelForCausalLM, LlamaConfig, LlamaModel, LlamaForCausalLM + +from transformers.modeling_outputs import CausalLMOutputWithPast +from transformers.generation.utils import GenerateOutput + +# from ...constants import IGNORE_INDEX, IMAGE_TOKEN_INDEX, DEFAULT_IMAGE_TOKEN, DEFAULT_IM_START_TOKEN, DEFAULT_IM_END_TOKEN +from llava.model.llava_arch import LlavaMetaModel, LlavaMetaForCausalLM +from transformers import Qwen2Config, Qwen2Model, Qwen2ForCausalLM + +# from .qwen.modeling_qwen import QWenLMHeadModel, QWenModel +# from .qwen.configuration_qwen import QWenConfig + + +class LlavaQwenConfig(Qwen2Config): + model_type = "llava_qwen" + + +class LlavaQwenModel(LlavaMetaModel, Qwen2Model): + config_class = LlavaQwenConfig + + def __init__(self, config: Qwen2Config): + super(LlavaQwenModel, self).__init__(config) + + +class LlavaQwenForCausalLM(Qwen2ForCausalLM, LlavaMetaForCausalLM): + config_class = LlavaQwenConfig + + def __init__(self, config): + # super(Qwen2ForCausalLM, self).__init__(config) + Qwen2ForCausalLM.__init__(self, config) + config.model_type = "llava_qwen" + config.rope_scaling = None + + self.model = LlavaQwenModel(config) + self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) + # Initialize weights and apply final processing + self.post_init() + + def get_model(self): + return self.model + + def forward( + self, + input_ids: torch.LongTensor = None, + attention_mask: Optional[torch.Tensor] = None, + position_ids: Optional[torch.LongTensor] = None, + past_key_values: Optional[List[torch.FloatTensor]] = None, + inputs_embeds: Optional[torch.FloatTensor] = None, + labels: Optional[torch.LongTensor] = None, + use_cache: Optional[bool] = None, + output_attentions: Optional[bool] = None, + output_hidden_states: Optional[bool] = None, + images: Optional[torch.FloatTensor] = None, + image_sizes: Optional[List[List[int]]] = None, + return_dict: Optional[bool] = None, + modalities: Optional[List[str]] = ["image"], + dpo_forward: Optional[bool] = False, + cache_position=None, + ) -> Union[Tuple, CausalLMOutputWithPast]: + + if inputs_embeds is None: + (input_ids, position_ids, attention_mask, past_key_values, inputs_embeds, labels) = self.prepare_inputs_labels_for_multimodal(input_ids, position_ids, attention_mask, past_key_values, labels, images, modalities, image_sizes) + + if dpo_forward: + outputs = self.model( + input_ids=input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + past_key_values=past_key_values, + inputs_embeds=inputs_embeds, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + ) + + hidden_states = outputs[0] + logits = self.lm_head(hidden_states) + return logits, labels + + else: + return super().forward( + input_ids=input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + past_key_values=past_key_values, + inputs_embeds=inputs_embeds, + labels=labels, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + ) + + @torch.no_grad() + def generate( + self, + inputs: Optional[torch.Tensor] = None, + images: Optional[torch.Tensor] = None, + image_sizes: Optional[torch.Tensor] = None, + modalities: Optional[List[str]] = ["image"], + **kwargs, + ) -> Union[GenerateOutput, torch.LongTensor]: + position_ids = kwargs.pop("position_ids", None) + attention_mask = kwargs.pop("attention_mask", None) + if "inputs_embeds" in kwargs: + raise NotImplementedError("`inputs_embeds` is not supported") + + if images is not None: + (inputs, position_ids, attention_mask, _, inputs_embeds, _) = self.prepare_inputs_labels_for_multimodal(inputs, position_ids, attention_mask, None, None, images, modalities, image_sizes=image_sizes) + else: + inputs_embeds = self.get_model().embed_tokens(inputs) + + return super().generate(position_ids=position_ids, attention_mask=attention_mask, inputs_embeds=inputs_embeds, **kwargs) + + def prepare_inputs_for_generation(self, input_ids, past_key_values=None, inputs_embeds=None, **kwargs): + images = kwargs.pop("images", None) + image_sizes = kwargs.pop("image_sizes", None) + inputs = super().prepare_inputs_for_generation(input_ids, past_key_values=past_key_values, inputs_embeds=inputs_embeds, **kwargs) + if images is not None: + inputs["images"] = images + if image_sizes is not None: + inputs["image_sizes"] = image_sizes + return inputs + + +AutoConfig.register("llava_qwen", LlavaQwenConfig) +AutoModelForCausalLM.register(LlavaQwenConfig, LlavaQwenForCausalLM) diff --git a/llava/model/language_model/llava_qwen_moe.py b/llava/model/language_model/llava_qwen_moe.py new file mode 100644 index 0000000..618a482 --- /dev/null +++ b/llava/model/language_model/llava_qwen_moe.py @@ -0,0 +1,149 @@ +# Copyright 2024 Hao Zhang +# +# 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. + + +from typing import List, Optional, Tuple, Union, Dict +import torch +import torch.nn as nn +from torch.nn import CrossEntropyLoss + +import transformers +from transformers import AutoConfig, AutoModelForCausalLM + +from transformers.modeling_outputs import CausalLMOutputWithPast +from transformers.generation.utils import GenerateOutput + +# from ...constants import IGNORE_INDEX, IMAGE_TOKEN_INDEX, DEFAULT_IMAGE_TOKEN, DEFAULT_IM_START_TOKEN, DEFAULT_IM_END_TOKEN +from llava.model.llava_arch import LlavaMetaModel, LlavaMetaForCausalLM +from transformers import Qwen2MoeConfig, Qwen2MoeModel, Qwen2MoeForCausalLM + +# from .qwen.modeling_qwen import QWenLMHeadModel, QWenModel +# from .qwen.configuration_qwen import QWenConfig + + +class LlavaQwenMoeConfig(Qwen2MoeConfig): + model_type = "llava_qwen_moe" + + +class LlavaQwenMoeModel(LlavaMetaModel, Qwen2MoeModel): + config_class = LlavaQwenMoeConfig + + def __init__(self, config: Qwen2MoeConfig): + super(LlavaQwenMoeModel, self).__init__(config) + + +class LlavaQwenMoeForCausalLM(Qwen2MoeForCausalLM, LlavaMetaForCausalLM): + config_class = LlavaQwenMoeConfig + + def __init__(self, config): + # super(Qwen2MoeForCausalLM, self).__init__(config) + Qwen2MoeForCausalLM.__init__(self, config) + config.model_type = "llava_qwen_moe" + config.rope_scaling = None + + self.model = LlavaQwenMoeModel(config) + self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) + # Initialize weights and apply final processing + self.post_init() + + def get_model(self): + return self.model + + def forward( + self, + input_ids: torch.LongTensor = None, + attention_mask: Optional[torch.Tensor] = None, + position_ids: Optional[torch.LongTensor] = None, + past_key_values: Optional[List[torch.FloatTensor]] = None, + inputs_embeds: Optional[torch.FloatTensor] = None, + labels: Optional[torch.LongTensor] = None, + use_cache: Optional[bool] = None, + output_attentions: Optional[bool] = None, + output_hidden_states: Optional[bool] = None, + images: Optional[torch.FloatTensor] = None, + image_sizes: Optional[List[List[int]]] = None, + return_dict: Optional[bool] = None, + modalities: Optional[List[str]] = ["image"], + dpo_forward: Optional[bool] = False, + cache_position=None, + ) -> Union[Tuple, CausalLMOutputWithPast]: + + if inputs_embeds is None: + (input_ids, position_ids, attention_mask, past_key_values, inputs_embeds, labels) = self.prepare_inputs_labels_for_multimodal(input_ids, position_ids, attention_mask, past_key_values, labels, images, modalities, image_sizes) + + if dpo_forward: + outputs = self.model( + input_ids=input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + past_key_values=past_key_values, + inputs_embeds=inputs_embeds, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + ) + + hidden_states = outputs[0] + logits = self.lm_head(hidden_states) + return logits, labels + + else: + return super().forward( + input_ids=input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + past_key_values=past_key_values, + inputs_embeds=inputs_embeds, + labels=labels, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + ) + + @torch.no_grad() + def generate( + self, + inputs: Optional[torch.Tensor] = None, + images: Optional[torch.Tensor] = None, + image_sizes: Optional[torch.Tensor] = None, + modalities: Optional[List[str]] = ["image"], + **kwargs, + ) -> Union[GenerateOutput, torch.LongTensor]: + position_ids = kwargs.pop("position_ids", None) + attention_mask = kwargs.pop("attention_mask", None) + if "inputs_embeds" in kwargs: + raise NotImplementedError("`inputs_embeds` is not supported") + + if images is not None: + (inputs, position_ids, attention_mask, _, inputs_embeds, _) = self.prepare_inputs_labels_for_multimodal(inputs, position_ids, attention_mask, None, None, images, modalities, image_sizes=image_sizes) + else: + inputs_embeds = self.get_model().embed_tokens(inputs) + + return super().generate(position_ids=position_ids, attention_mask=attention_mask, inputs_embeds=inputs_embeds, **kwargs) + + def prepare_inputs_for_generation(self, input_ids, past_key_values=None, inputs_embeds=None, **kwargs): + images = kwargs.pop("images", None) + image_sizes = kwargs.pop("image_sizes", None) + inputs = super().prepare_inputs_for_generation(input_ids, past_key_values=past_key_values, inputs_embeds=inputs_embeds, **kwargs) + if images is not None: + inputs["images"] = images + if image_sizes is not None: + inputs["image_sizes"] = image_sizes + return inputs + + +AutoConfig.register("llava_qwen_moe", LlavaQwenMoeConfig) +AutoModelForCausalLM.register(LlavaQwenMoeConfig, LlavaQwenMoeForCausalLM) diff --git a/llava/model/language_model/modeling_llama.py b/llava/model/language_model/modeling_llama.py new file mode 100644 index 0000000..0d93738 --- /dev/null +++ b/llava/model/language_model/modeling_llama.py @@ -0,0 +1,1649 @@ +# coding=utf-8 +# Copyright 2022 EleutherAI and the HuggingFace Inc. team. All rights reserved. +# +# This code is based on EleutherAI's GPT-NeoX library and the GPT-NeoX +# and OPT implementations in this library. It has been modified from its +# original forms to accommodate minor architectural differences compared +# to GPT-NeoX and OPT used by the Meta AI team that trained the model. +# +# 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. +""" PyTorch LLaMA model.""" +import math +import warnings +from typing import List, Optional, Tuple, Union + +import torch +import torch.nn.functional as F +import torch.utils.checkpoint +from torch import nn +from torch.nn import BCEWithLogitsLoss, CrossEntropyLoss, MSELoss + +from transformers.activations import ACT2FN +from transformers.cache_utils import Cache, DynamicCache, StaticCache +from transformers.modeling_outputs import ( + BaseModelOutputWithPast, + CausalLMOutputWithPast, + QuestionAnsweringModelOutput, + SequenceClassifierOutputWithPast, +) +from transformers.modeling_utils import PreTrainedModel +from transformers.pytorch_utils import ALL_LAYERNORM_LAYERS +from transformers.utils import ( + add_start_docstrings, + add_start_docstrings_to_model_forward, + is_flash_attn_2_available, + is_flash_attn_greater_or_equal_2_10, + logging, + replace_return_docstrings, +) +from transformers.models.llama.configuration_llama import LlamaConfig + +if is_flash_attn_2_available(): + from flash_attn import flash_attn_func, flash_attn_varlen_func + from flash_attn.bert_padding import index_first_axis, pad_input, unpad_input # noqa + + +logger = logging.get_logger(__name__) + +_CONFIG_FOR_DOC = "LlamaConfig" + + +def _get_unpad_data(attention_mask): + seqlens_in_batch = attention_mask.sum(dim=-1, dtype=torch.int32) + indices = torch.nonzero(attention_mask.flatten(), as_tuple=False).flatten() + max_seqlen_in_batch = seqlens_in_batch.max().item() + cu_seqlens = F.pad(torch.cumsum(seqlens_in_batch, dim=0, dtype=torch.int32), (1, 0)) + return ( + indices, + cu_seqlens, + max_seqlen_in_batch, + ) + + +class LlamaRMSNorm(nn.Module): + def __init__(self, hidden_size, eps=1e-6): + """ + LlamaRMSNorm is equivalent to T5LayerNorm + """ + super().__init__() + self.weight = nn.Parameter(torch.ones(hidden_size)) + self.variance_epsilon = eps + + def forward(self, hidden_states): + input_dtype = hidden_states.dtype + hidden_states = hidden_states.to(torch.float32) + variance = hidden_states.pow(2).mean(-1, keepdim=True) + hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon) + return self.weight * hidden_states.to(input_dtype) + + +ALL_LAYERNORM_LAYERS.append(LlamaRMSNorm) + + +class LlamaRotaryEmbedding(nn.Module): + def __init__(self, dim, max_position_embeddings=2048, base=10000, device=None, scaling_factor=1.0): + super().__init__() + self.scaling_factor = scaling_factor + self.dim = dim + self.max_position_embeddings = max_position_embeddings + self.base = base + inv_freq = 1.0 / (self.base ** (torch.arange(0, self.dim, 2, dtype=torch.int64).float().to(device) / self.dim)) + self.register_buffer("inv_freq", inv_freq, persistent=False) + # For BC we register cos and sin cached + self.max_seq_len_cached = max_position_embeddings + t = torch.arange(self.max_seq_len_cached, device=device, dtype=torch.int64).type_as(self.inv_freq) + t = t / self.scaling_factor + freqs = torch.outer(t, self.inv_freq) + # Different from paper, but it uses a different permutation in order to obtain the same calculation + emb = torch.cat((freqs, freqs), dim=-1) + self.register_buffer("_cos_cached", emb.cos().to(torch.get_default_dtype()), persistent=False) + self.register_buffer("_sin_cached", emb.sin().to(torch.get_default_dtype()), persistent=False) + + @property + def sin_cached(self): + logger.warning_once("The sin_cached attribute will be removed in 4.39. Bear in mind that its contents changed in v4.38. Use " "the forward method of RoPE from now on instead. It is not used in the `LlamaAttention` class") + return self._sin_cached + + @property + def cos_cached(self): + logger.warning_once("The cos_cached attribute will be removed in 4.39. Bear in mind that its contents changed in v4.38. Use " "the forward method of RoPE from now on instead. It is not used in the `LlamaAttention` class") + return self._cos_cached + + @torch.no_grad() + def forward(self, x, position_ids, seq_len=None): + if seq_len is not None: + logger.warning_once("The `seq_len` argument is deprecated and unused. It will be removed in v4.39.") + + # x: [bs, num_attention_heads, seq_len, head_size] + inv_freq_expanded = self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1) + position_ids_expanded = position_ids[:, None, :].float() + # Force float32 since bfloat16 loses precision on long contexts + # See https://github.com/huggingface/transformers/pull/29285 + device_type = x.device.type + device_type = device_type if isinstance(device_type, str) else "cpu" + with torch.autocast(device_type=device_type, enabled=False): + freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2) + emb = torch.cat((freqs, freqs), dim=-1) + cos = emb.cos() + sin = emb.sin() + return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype) + + +class LlamaLinearScalingRotaryEmbedding(LlamaRotaryEmbedding): + """LlamaRotaryEmbedding extended with linear scaling. Credits to the Reddit user /u/kaiokendev""" + + def forward(self, x, position_ids, seq_len=None): + # difference to the original RoPE: a scaling factor is aplied to the position ids + position_ids = position_ids.float() / self.scaling_factor + cos, sin = super().forward(x, position_ids, seq_len) + return cos, sin + + +class LlamaDynamicNTKScalingRotaryEmbedding(LlamaRotaryEmbedding): + """LlamaRotaryEmbedding extended with Dynamic NTK scaling. Credits to the Reddit users /u/bloc97 and /u/emozilla""" + + def forward(self, x, position_ids, seq_len=None): + # difference to the original RoPE: inv_freq is recomputed when the sequence length > original length + seq_len = torch.max(position_ids) + 1 + if seq_len > self.max_position_embeddings: + base = self.base * ((self.scaling_factor * seq_len / self.max_position_embeddings) - (self.scaling_factor - 1)) ** (self.dim / (self.dim - 2)) + inv_freq = 1.0 / (base ** (torch.arange(0, self.dim, 2, dtype=torch.int64).float().to(x.device) / self.dim)) + self.register_buffer("inv_freq", inv_freq, persistent=False) # TODO joao: this may break with compilation + + cos, sin = super().forward(x, position_ids, seq_len) + return cos, sin + + +def rotate_half(x): + """Rotates half the hidden dims of the input.""" + x1 = x[..., : x.shape[-1] // 2] + x2 = x[..., x.shape[-1] // 2 :] + return torch.cat((-x2, x1), dim=-1) + + +def apply_rotary_pos_emb(q, k, cos, sin, position_ids=None, unsqueeze_dim=1): + """Applies Rotary Position Embedding to the query and key tensors. + + Args: + q (`torch.Tensor`): The query tensor. + k (`torch.Tensor`): The key tensor. + cos (`torch.Tensor`): The cosine part of the rotary embedding. + sin (`torch.Tensor`): The sine part of the rotary embedding. + position_ids (`torch.Tensor`, *optional*): + Deprecated and unused. + unsqueeze_dim (`int`, *optional*, defaults to 1): + The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and + sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note + that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and + k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes + cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have + the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2. + Returns: + `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding. + """ + cos = cos.unsqueeze(unsqueeze_dim) + sin = sin.unsqueeze(unsqueeze_dim) + q_embed = (q * cos) + (rotate_half(q) * sin) + k_embed = (k * cos) + (rotate_half(k) * sin) + return q_embed, k_embed + + +class LlamaMLP(nn.Module): + def __init__(self, config): + super().__init__() + self.config = config + self.hidden_size = config.hidden_size + self.intermediate_size = config.intermediate_size + self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False) + self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False) + self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False) + self.act_fn = ACT2FN[config.hidden_act] + + def forward(self, x): + if self.config.pretraining_tp > 1: + slice = self.intermediate_size // self.config.pretraining_tp + gate_proj_slices = self.gate_proj.weight.split(slice, dim=0) + up_proj_slices = self.up_proj.weight.split(slice, dim=0) + down_proj_slices = self.down_proj.weight.split(slice, dim=1) + + gate_proj = torch.cat([F.linear(x, gate_proj_slices[i]) for i in range(self.config.pretraining_tp)], dim=-1) + up_proj = torch.cat([F.linear(x, up_proj_slices[i]) for i in range(self.config.pretraining_tp)], dim=-1) + + intermediate_states = (self.act_fn(gate_proj) * up_proj).split(slice, dim=2) + down_proj = [F.linear(intermediate_states[i], down_proj_slices[i]) for i in range(self.config.pretraining_tp)] + down_proj = sum(down_proj) + else: + down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x)) + + return down_proj + + +def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor: + """ + This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch, + num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim) + """ + batch, num_key_value_heads, slen, head_dim = hidden_states.shape + if n_rep == 1: + return hidden_states + hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim) + return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim) + + +class LlamaAttention(nn.Module): + """Multi-headed attention from 'Attention Is All You Need' paper""" + + def __init__(self, config: LlamaConfig, layer_idx: Optional[int] = None): + super().__init__() + self.config = config + self.layer_idx = layer_idx + if layer_idx is None: + logger.warning_once( + f"Instantiating {self.__class__.__name__} without passing a `layer_idx` is not recommended and will " + "lead to errors during the forward call if caching is used. Please make sure to provide a `layer_idx` " + "when creating this class." + ) + + self.attention_dropout = config.attention_dropout + self.hidden_size = config.hidden_size + self.num_heads = config.num_attention_heads + self.head_dim = self.hidden_size // self.num_heads + self.num_key_value_heads = config.num_key_value_heads + self.num_key_value_groups = self.num_heads // self.num_key_value_heads + self.max_position_embeddings = config.max_position_embeddings + self.rope_theta = config.rope_theta + self.is_causal = True + + if (self.head_dim * self.num_heads) != self.hidden_size: + raise ValueError(f"hidden_size must be divisible by num_heads (got `hidden_size`: {self.hidden_size}" f" and `num_heads`: {self.num_heads}).") + + self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=config.attention_bias) + self.k_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=config.attention_bias) + self.v_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=config.attention_bias) + self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=config.attention_bias) + self._init_rope() + + def _init_rope(self): + if self.config.rope_scaling is None: + self.rotary_emb = LlamaRotaryEmbedding( + self.head_dim, + max_position_embeddings=self.max_position_embeddings, + base=self.rope_theta, + ) + else: + scaling_type = self.config.rope_scaling["type"] + scaling_factor = self.config.rope_scaling["factor"] + if scaling_type == "linear": + self.rotary_emb = LlamaLinearScalingRotaryEmbedding( + self.head_dim, + max_position_embeddings=self.max_position_embeddings, + scaling_factor=scaling_factor, + base=self.rope_theta, + ) + elif scaling_type == "dynamic": + self.rotary_emb = LlamaDynamicNTKScalingRotaryEmbedding( + self.head_dim, + max_position_embeddings=self.max_position_embeddings, + scaling_factor=scaling_factor, + base=self.rope_theta, + ) + else: + raise ValueError(f"Unknown RoPE scaling type {scaling_type}") + + def forward( + self, + hidden_states: torch.Tensor, + attention_mask: Optional[torch.Tensor] = None, + position_ids: Optional[torch.LongTensor] = None, + past_key_value: Optional[Cache] = None, + output_attentions: bool = False, + use_cache: bool = False, + cache_position: Optional[torch.LongTensor] = None, + **kwargs, + ) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]: + bsz, q_len, _ = hidden_states.size() + + if self.config.pretraining_tp > 1: + key_value_slicing = (self.num_key_value_heads * self.head_dim) // self.config.pretraining_tp + query_slices = self.q_proj.weight.split((self.num_heads * self.head_dim) // self.config.pretraining_tp, dim=0) + key_slices = self.k_proj.weight.split(key_value_slicing, dim=0) + value_slices = self.v_proj.weight.split(key_value_slicing, dim=0) + + query_states = [F.linear(hidden_states, query_slices[i]) for i in range(self.config.pretraining_tp)] + query_states = torch.cat(query_states, dim=-1) + + key_states = [F.linear(hidden_states, key_slices[i]) for i in range(self.config.pretraining_tp)] + key_states = torch.cat(key_states, dim=-1) + + value_states = [F.linear(hidden_states, value_slices[i]) for i in range(self.config.pretraining_tp)] + value_states = torch.cat(value_states, dim=-1) + + else: + query_states = self.q_proj(hidden_states) + key_states = self.k_proj(hidden_states) + value_states = self.v_proj(hidden_states) + + query_states = query_states.view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2) + key_states = key_states.view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2) + value_states = value_states.view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2) + + past_key_value = getattr(self, "past_key_value", past_key_value) + cos, sin = self.rotary_emb(value_states, position_ids) + query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin) + + if past_key_value is not None: + # sin and cos are specific to RoPE models; position_ids needed for the static cache + cache_kwargs = {"sin": sin, "cos": cos, "cache_position": cache_position} + key_states, value_states = past_key_value.update(key_states, value_states, self.layer_idx, cache_kwargs) + + key_states = repeat_kv(key_states, self.num_key_value_groups) + value_states = repeat_kv(value_states, self.num_key_value_groups) + + attn_weights = torch.matmul(query_states, key_states.transpose(2, 3)) / math.sqrt(self.head_dim) + + if attention_mask is not None: # no matter the length, we just slice it + causal_mask = attention_mask + if cache_position is not None: + causal_mask = attention_mask[:, :, cache_position, : key_states.shape[-2]] + attn_weights = attn_weights + causal_mask + + # upcast attention to fp32 + attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query_states.dtype) + attn_weights = nn.functional.dropout(attn_weights, p=self.attention_dropout, training=self.training) + attn_output = torch.matmul(attn_weights, value_states) + + if attn_output.size() != (bsz, self.num_heads, q_len, self.head_dim): + raise ValueError(f"`attn_output` should be of size {(bsz, self.num_heads, q_len, self.head_dim)}, but is" f" {attn_output.size()}") + + attn_output = attn_output.transpose(1, 2).contiguous() + + attn_output = attn_output.reshape(bsz, q_len, self.hidden_size) + + if self.config.pretraining_tp > 1: + attn_output = attn_output.split(self.hidden_size // self.config.pretraining_tp, dim=2) + o_proj_slices = self.o_proj.weight.split(self.hidden_size // self.config.pretraining_tp, dim=1) + attn_output = sum([F.linear(attn_output[i], o_proj_slices[i]) for i in range(self.config.pretraining_tp)]) + else: + attn_output = self.o_proj(attn_output) + + if not output_attentions: + attn_weights = None + + return attn_output, attn_weights, past_key_value + + +class LlamaRingFlashAttention2(LlamaAttention): + """ + Llama flash attention module. This module inherits from `LlamaAttention` as the weights of the module stays + untouched. The only required change would be on the forward pass where it needs to correctly call the public API of + flash attention and deal with padding tokens in case the input contains any of them. + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # TODO: Should be removed once Flash Attention for RoCm is bumped to 2.1. + # flash_attn<2.1 generates top-left aligned causal mask, while what is needed here is bottom-right alignement, that was made default for flash_attn>=2.1. This attribute is used to handle this difference. Reference: https://github.com/Dao-AILab/flash-attention/releases/tag/v2.1.0. + # Beware that with flash_attn<2.1, using q_seqlen != k_seqlen (except for the case q_seqlen == 1) produces a wrong mask (top-left). + self._flash_attn_uses_top_left_mask = not is_flash_attn_greater_or_equal_2_10() + + def forward( + self, + hidden_states: torch.Tensor, + attention_mask: Optional[torch.LongTensor] = None, + position_ids: Optional[torch.LongTensor] = None, + past_key_value: Optional[Cache] = None, + output_attentions: bool = False, + use_cache: bool = False, + cache_position: Optional[torch.LongTensor] = None, + **kwargs, + ) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]: + output_attentions = False + + bsz, q_len, _ = hidden_states.size() + + query_states = self.q_proj(hidden_states) + key_states = self.k_proj(hidden_states) + value_states = self.v_proj(hidden_states) + + # Flash attention requires the input to have the shape + # batch_size x seq_length x head_dim x hidden_dim + # therefore we just need to keep the original shape + query_states = query_states.view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2) + key_states = key_states.view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2) + value_states = value_states.view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2) + + cos, sin = self.rotary_emb(value_states, position_ids) + query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin) + + past_key_value = getattr(self, "past_key_value", past_key_value) + + if past_key_value is not None: + # sin and cos are specific to RoPE models; position_ids needed for the static cache + cache_kwargs = {"sin": sin, "cos": cos, "cache_position": cache_position} + key_states, value_states = past_key_value.update(key_states, value_states, self.layer_idx, cache_kwargs) + + # TODO: These transpose are quite inefficient but Flash Attention requires the layout [batch_size, sequence_length, num_heads, head_dim]. We would need to refactor the KV cache + # to be able to avoid many of these transpose/reshape/view. + query_states = query_states.transpose(1, 2) + key_states = key_states.transpose(1, 2) + value_states = value_states.transpose(1, 2) + + dropout_rate = self.attention_dropout if self.training else 0.0 + + # In PEFT, usually we cast the layer norms in float32 for training stability reasons + # therefore the input hidden states gets silently casted in float32. Hence, we need + # cast them back in the correct dtype just to be sure everything works as expected. + # This might slowdown training & inference so it is recommended to not cast the LayerNorms + # in fp32. (LlamaRMSNorm handles it correctly) + + input_dtype = query_states.dtype + if input_dtype == torch.float32: + if torch.is_autocast_enabled(): + target_dtype = torch.get_autocast_gpu_dtype() + # Handle the case where the model is quantized + elif hasattr(self.config, "_pre_quantization_dtype"): + target_dtype = self.config._pre_quantization_dtype + else: + target_dtype = self.q_proj.weight.dtype + + logger.warning_once( + f"The input hidden states seems to be silently casted in float32, this might be related to" f" the fact you have upcasted embedding or layer norm layers in float32. We will cast back the input in" f" {target_dtype}." + ) + + query_states = query_states.to(target_dtype) + key_states = key_states.to(target_dtype) + value_states = value_states.to(target_dtype) + + attn_output = self._flash_attention_forward(query_states, key_states, value_states, attention_mask, q_len, dropout=dropout_rate) + + attn_output = attn_output.reshape(bsz, q_len, self.hidden_size).contiguous() + attn_output = self.o_proj(attn_output) + + if not output_attentions: + attn_weights = None + + return attn_output, attn_weights, past_key_value + + def _flash_attention_forward(self, query_states, key_states, value_states, attention_mask, query_length, dropout=0.0, softmax_scale=None): + """ + Calls the forward method of Flash Attention - if the input hidden states contain at least one padding token + first unpad the input, then computes the attention scores and pad the final attention scores. + + Args: + query_states (`torch.Tensor`): + Input query states to be passed to Flash Attention API + key_states (`torch.Tensor`): + Input key states to be passed to Flash Attention API + value_states (`torch.Tensor`): + Input value states to be passed to Flash Attention API + attention_mask (`torch.Tensor`): + The padding mask - corresponds to a tensor of size `(batch_size, seq_len)` where 0 stands for the + position of padding tokens and 1 for the position of non-padding tokens. + dropout (`int`, *optional*): + Attention dropout + softmax_scale (`float`, *optional*): + The scaling of QK^T before applying softmax. Default to 1 / sqrt(head_dim) + """ + if not self._flash_attn_uses_top_left_mask: + causal = self.is_causal + else: + # TODO: Remove the `query_length != 1` check once Flash Attention for RoCm is bumped to 2.1. For details, please see the comment in LlamaFlashAttention2 __init__. + causal = self.is_causal and query_length != 1 + + # Contains at least one padding token in the sequence + if attention_mask is not None: + batch_size = query_states.shape[0] + query_states, key_states, value_states, indices_q, cu_seq_lens, max_seq_lens = self._upad_input(query_states, key_states, value_states, attention_mask, query_length) + + cu_seqlens_q, cu_seqlens_k = cu_seq_lens + max_seqlen_in_batch_q, max_seqlen_in_batch_k = max_seq_lens + + attn_output_unpad = zigzag_ring_flash_attn_varlen_func( + query_states, + key_states, + value_states, + cu_seqlens_q=cu_seqlens_q, + cu_seqlens_k=cu_seqlens_k, + max_seqlen_q=max_seqlen_in_batch_q, + max_seqlen_k=max_seqlen_in_batch_k, + dropout_p=dropout, + softmax_scale=softmax_scale, + causal=causal, + ) + + attn_output = pad_input(attn_output_unpad, indices_q, batch_size, query_length) + else: + # pack qkv + # query_states: (batch_size, seqlen, nheads, headdim) + # qkv: (batch_size, seqlen, 3, nheads, headdim) + qkv = torch.stack([query_states, key_states, value_states], dim=2) + attn_output = zigzag_ring_flash_attn_qkvpacked_func(qkv, dropout, softmax_scale, causal=causal) + + return attn_output + + def _upad_input(self, query_layer, key_layer, value_layer, attention_mask, query_length): + indices_k, cu_seqlens_k, max_seqlen_in_batch_k = _get_unpad_data(attention_mask) + batch_size, kv_seq_len, num_key_value_heads, head_dim = key_layer.shape + + key_layer = index_first_axis(key_layer.reshape(batch_size * kv_seq_len, num_key_value_heads, head_dim), indices_k) + value_layer = index_first_axis(value_layer.reshape(batch_size * kv_seq_len, num_key_value_heads, head_dim), indices_k) + if query_length == kv_seq_len: + query_layer = index_first_axis(query_layer.reshape(batch_size * kv_seq_len, self.num_heads, head_dim), indices_k) + cu_seqlens_q = cu_seqlens_k + max_seqlen_in_batch_q = max_seqlen_in_batch_k + indices_q = indices_k + elif query_length == 1: + max_seqlen_in_batch_q = 1 + cu_seqlens_q = torch.arange(batch_size + 1, dtype=torch.int32, device=query_layer.device) # There is a memcpy here, that is very bad. + indices_q = cu_seqlens_q[:-1] + query_layer = query_layer.squeeze(1) + else: + # The -q_len: slice assumes left padding. + attention_mask = attention_mask[:, -query_length:] + query_layer, indices_q, cu_seqlens_q, max_seqlen_in_batch_q = unpad_input(query_layer, attention_mask) + + return ( + query_layer, + key_layer, + value_layer, + indices_q, + (cu_seqlens_q, cu_seqlens_k), + (max_seqlen_in_batch_q, max_seqlen_in_batch_k), + ) + + +class LlamaFlashAttention2(LlamaAttention): + """ + Llama flash attention module. This module inherits from `LlamaAttention` as the weights of the module stays + untouched. The only required change would be on the forward pass where it needs to correctly call the public API of + flash attention and deal with padding tokens in case the input contains any of them. + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # TODO: Should be removed once Flash Attention for RoCm is bumped to 2.1. + # flash_attn<2.1 generates top-left aligned causal mask, while what is needed here is bottom-right alignement, that was made default for flash_attn>=2.1. This attribute is used to handle this difference. Reference: https://github.com/Dao-AILab/flash-attention/releases/tag/v2.1.0. + # Beware that with flash_attn<2.1, using q_seqlen != k_seqlen (except for the case q_seqlen == 1) produces a wrong mask (top-left). + self._flash_attn_uses_top_left_mask = not is_flash_attn_greater_or_equal_2_10() + + def forward( + self, + hidden_states: torch.Tensor, + attention_mask: Optional[torch.LongTensor] = None, + position_ids: Optional[torch.LongTensor] = None, + past_key_value: Optional[Cache] = None, + output_attentions: bool = False, + use_cache: bool = False, + cache_position: Optional[torch.LongTensor] = None, + **kwargs, + ) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]: + output_attentions = False + + bsz, q_len, _ = hidden_states.size() + + query_states = self.q_proj(hidden_states) + key_states = self.k_proj(hidden_states) + value_states = self.v_proj(hidden_states) + + # Flash attention requires the input to have the shape + # batch_size x seq_length x head_dim x hidden_dim + # therefore we just need to keep the original shape + query_states = query_states.view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2) + key_states = key_states.view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2) + value_states = value_states.view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2) + + cos, sin = self.rotary_emb(value_states, position_ids) + query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin) + + past_key_value = getattr(self, "past_key_value", past_key_value) + + if past_key_value is not None: + # sin and cos are specific to RoPE models; position_ids needed for the static cache + cache_kwargs = {"sin": sin, "cos": cos, "cache_position": cache_position} + key_states, value_states = past_key_value.update(key_states, value_states, self.layer_idx, cache_kwargs) + + # TODO: These transpose are quite inefficient but Flash Attention requires the layout [batch_size, sequence_length, num_heads, head_dim]. We would need to refactor the KV cache + # to be able to avoid many of these transpose/reshape/view. + query_states = query_states.transpose(1, 2) + key_states = key_states.transpose(1, 2) + value_states = value_states.transpose(1, 2) + + dropout_rate = self.attention_dropout if self.training else 0.0 + + # In PEFT, usually we cast the layer norms in float32 for training stability reasons + # therefore the input hidden states gets silently casted in float32. Hence, we need + # cast them back in the correct dtype just to be sure everything works as expected. + # This might slowdown training & inference so it is recommended to not cast the LayerNorms + # in fp32. (LlamaRMSNorm handles it correctly) + + input_dtype = query_states.dtype + if input_dtype == torch.float32: + if torch.is_autocast_enabled(): + target_dtype = torch.get_autocast_gpu_dtype() + # Handle the case where the model is quantized + elif hasattr(self.config, "_pre_quantization_dtype"): + target_dtype = self.config._pre_quantization_dtype + else: + target_dtype = self.q_proj.weight.dtype + + logger.warning_once( + f"The input hidden states seems to be silently casted in float32, this might be related to" f" the fact you have upcasted embedding or layer norm layers in float32. We will cast back the input in" f" {target_dtype}." + ) + + query_states = query_states.to(target_dtype) + key_states = key_states.to(target_dtype) + value_states = value_states.to(target_dtype) + + attn_output = self._flash_attention_forward(query_states, key_states, value_states, attention_mask, q_len, dropout=dropout_rate) + + attn_output = attn_output.reshape(bsz, q_len, self.hidden_size).contiguous() + attn_output = self.o_proj(attn_output) + + if not output_attentions: + attn_weights = None + + return attn_output, attn_weights, past_key_value + + def _flash_attention_forward(self, query_states, key_states, value_states, attention_mask, query_length, dropout=0.0, softmax_scale=None): + """ + Calls the forward method of Flash Attention - if the input hidden states contain at least one padding token + first unpad the input, then computes the attention scores and pad the final attention scores. + + Args: + query_states (`torch.Tensor`): + Input query states to be passed to Flash Attention API + key_states (`torch.Tensor`): + Input key states to be passed to Flash Attention API + value_states (`torch.Tensor`): + Input value states to be passed to Flash Attention API + attention_mask (`torch.Tensor`): + The padding mask - corresponds to a tensor of size `(batch_size, seq_len)` where 0 stands for the + position of padding tokens and 1 for the position of non-padding tokens. + dropout (`int`, *optional*): + Attention dropout + softmax_scale (`float`, *optional*): + The scaling of QK^T before applying softmax. Default to 1 / sqrt(head_dim) + """ + if not self._flash_attn_uses_top_left_mask: + causal = self.is_causal + else: + # TODO: Remove the `query_length != 1` check once Flash Attention for RoCm is bumped to 2.1. For details, please see the comment in LlamaFlashAttention2 __init__. + causal = self.is_causal and query_length != 1 + + # Contains at least one padding token in the sequence + if attention_mask is not None: + batch_size = query_states.shape[0] + query_states, key_states, value_states, indices_q, cu_seq_lens, max_seq_lens = self._upad_input(query_states, key_states, value_states, attention_mask, query_length) + + cu_seqlens_q, cu_seqlens_k = cu_seq_lens + max_seqlen_in_batch_q, max_seqlen_in_batch_k = max_seq_lens + + attn_output_unpad = flash_attn_varlen_func( + query_states, + key_states, + value_states, + cu_seqlens_q=cu_seqlens_q, + cu_seqlens_k=cu_seqlens_k, + max_seqlen_q=max_seqlen_in_batch_q, + max_seqlen_k=max_seqlen_in_batch_k, + dropout_p=dropout, + softmax_scale=softmax_scale, + causal=causal, + ) + + attn_output = pad_input(attn_output_unpad, indices_q, batch_size, query_length) + else: + attn_output = flash_attn_func(query_states, key_states, value_states, dropout, softmax_scale=softmax_scale, causal=causal) + + return attn_output + + def _upad_input(self, query_layer, key_layer, value_layer, attention_mask, query_length): + indices_k, cu_seqlens_k, max_seqlen_in_batch_k = _get_unpad_data(attention_mask) + batch_size, kv_seq_len, num_key_value_heads, head_dim = key_layer.shape + + key_layer = index_first_axis(key_layer.reshape(batch_size * kv_seq_len, num_key_value_heads, head_dim), indices_k) + value_layer = index_first_axis(value_layer.reshape(batch_size * kv_seq_len, num_key_value_heads, head_dim), indices_k) + if query_length == kv_seq_len: + query_layer = index_first_axis(query_layer.reshape(batch_size * kv_seq_len, self.num_heads, head_dim), indices_k) + cu_seqlens_q = cu_seqlens_k + max_seqlen_in_batch_q = max_seqlen_in_batch_k + indices_q = indices_k + elif query_length == 1: + max_seqlen_in_batch_q = 1 + cu_seqlens_q = torch.arange(batch_size + 1, dtype=torch.int32, device=query_layer.device) # There is a memcpy here, that is very bad. + indices_q = cu_seqlens_q[:-1] + query_layer = query_layer.squeeze(1) + else: + # The -q_len: slice assumes left padding. + attention_mask = attention_mask[:, -query_length:] + query_layer, indices_q, cu_seqlens_q, max_seqlen_in_batch_q = unpad_input(query_layer, attention_mask) + + return ( + query_layer, + key_layer, + value_layer, + indices_q, + (cu_seqlens_q, cu_seqlens_k), + (max_seqlen_in_batch_q, max_seqlen_in_batch_k), + ) + + +class LlamaSdpaAttention(LlamaAttention): + """ + Llama attention module using torch.nn.functional.scaled_dot_product_attention. This module inherits from + `LlamaAttention` as the weights of the module stays untouched. The only changes are on the forward pass to adapt to + SDPA API. + """ + + # Adapted from LlamaAttention.forward + def forward( + self, + hidden_states: torch.Tensor, + attention_mask: Optional[torch.Tensor] = None, + position_ids: Optional[torch.LongTensor] = None, + past_key_value: Optional[Cache] = None, + output_attentions: bool = False, + use_cache: bool = False, + cache_position: Optional[torch.LongTensor] = None, + ) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]: + if output_attentions: + # TODO: Improve this warning with e.g. `model.config.attn_implementation = "manual"` once this is implemented. + logger.warning_once( + "LlamaModel is using LlamaSdpaAttention, but `torch.nn.functional.scaled_dot_product_attention` does not support `output_attentions=True`. Falling back to the manual attention implementation, " + 'but specifying the manual implementation will be required from Transformers version v5.0.0 onwards. This warning can be removed using the argument `attn_implementation="eager"` when loading the model.' + ) + return super().forward( + hidden_states=hidden_states, + attention_mask=attention_mask, + position_ids=position_ids, + past_key_value=past_key_value, + output_attentions=output_attentions, + use_cache=use_cache, + cache_position=cache_position, + ) + + bsz, q_len, _ = hidden_states.size() + + query_states = self.q_proj(hidden_states) + key_states = self.k_proj(hidden_states) + value_states = self.v_proj(hidden_states) + + query_states = query_states.view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2) + key_states = key_states.view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2) + value_states = value_states.view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2) + + cos, sin = self.rotary_emb(value_states, position_ids) + query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin) + + # In case static cache is used, it is an instance attribute. + past_key_value = getattr(self, "past_key_value", past_key_value) + + if past_key_value is not None: + # sin and cos are specific to RoPE models; position_ids needed for the static cache + cache_kwargs = {"sin": sin, "cos": cos, "cache_position": cache_position} + key_states, value_states = past_key_value.update(key_states, value_states, self.layer_idx, cache_kwargs) + + key_states = repeat_kv(key_states, self.num_key_value_groups) + value_states = repeat_kv(value_states, self.num_key_value_groups) + + causal_mask = attention_mask + if attention_mask is not None and cache_position is not None: + causal_mask = causal_mask[:, :, cache_position, : key_states.shape[-2]] + + # SDPA with memory-efficient backend is currently (torch==2.1.2) bugged with non-contiguous inputs with custom attn_mask, + # Reference: https://github.com/pytorch/pytorch/issues/112577. + if query_states.device.type == "cuda" and causal_mask is not None: + query_states = query_states.contiguous() + key_states = key_states.contiguous() + value_states = value_states.contiguous() + + attn_output = torch.nn.functional.scaled_dot_product_attention( + query_states, + key_states, + value_states, + attn_mask=causal_mask, + dropout_p=self.attention_dropout if self.training else 0.0, + ) + + attn_output = attn_output.transpose(1, 2).contiguous() + attn_output = attn_output.view(bsz, q_len, self.hidden_size) + + attn_output = self.o_proj(attn_output) + + return attn_output, None, past_key_value + + +try: + from ring_flash_attn import zigzag_ring_flash_attn_qkvpacked_func, zigzag_ring_flash_attn_varlen_func +except ImportError: + print("Please install the ring-flash-attn package") + +LLAMA_ATTENTION_CLASSES = { + "eager": LlamaAttention, + "flash_attention_2": LlamaFlashAttention2, + "ring_flash_attention_2": LlamaRingFlashAttention2, + "sdpa": LlamaSdpaAttention, +} + + +class LlamaDecoderLayer(nn.Module): + def __init__(self, config: LlamaConfig, layer_idx: int): + super().__init__() + self.hidden_size = config.hidden_size + + self.self_attn = LLAMA_ATTENTION_CLASSES[config._attn_implementation](config=config, layer_idx=layer_idx) + + self.mlp = LlamaMLP(config) + self.input_layernorm = LlamaRMSNorm(config.hidden_size, eps=config.rms_norm_eps) + self.post_attention_layernorm = LlamaRMSNorm(config.hidden_size, eps=config.rms_norm_eps) + + def forward( + self, + hidden_states: torch.Tensor, + attention_mask: Optional[torch.Tensor] = None, + position_ids: Optional[torch.LongTensor] = None, + past_key_value: Optional[Tuple[torch.Tensor]] = None, + output_attentions: Optional[bool] = False, + use_cache: Optional[bool] = False, + cache_position: Optional[torch.LongTensor] = None, + **kwargs, + ) -> Tuple[torch.FloatTensor, Optional[Tuple[torch.FloatTensor, torch.FloatTensor]]]: + """ + Args: + hidden_states (`torch.FloatTensor`): input to the layer of shape `(batch, seq_len, embed_dim)` + attention_mask (`torch.FloatTensor`, *optional*): + attention mask of size `(batch_size, sequence_length)` if flash attention is used or `(batch_size, 1, + query_sequence_length, key_sequence_length)` if default attention is used. + output_attentions (`bool`, *optional*): + Whether or not to return the attentions tensors of all attention layers. See `attentions` under + returned tensors for more detail. + use_cache (`bool`, *optional*): + If set to `True`, `past_key_values` key value states are returned and can be used to speed up decoding + (see `past_key_values`). + past_key_value (`Tuple(torch.FloatTensor)`, *optional*): cached past key and value projection states + """ + if "padding_mask" in kwargs: + warnings.warn("Passing `padding_mask` is deprecated and will be removed in v4.37. Please make sure use `attention_mask` instead.`") + + residual = hidden_states + + hidden_states = self.input_layernorm(hidden_states) + + # Self Attention + hidden_states, self_attn_weights, present_key_value = self.self_attn( + hidden_states=hidden_states, + attention_mask=attention_mask, + position_ids=position_ids, + past_key_value=past_key_value, + output_attentions=output_attentions, + use_cache=use_cache, + cache_position=cache_position, + **kwargs, + ) + hidden_states = residual + hidden_states + + # Fully Connected + residual = hidden_states + hidden_states = self.post_attention_layernorm(hidden_states) + hidden_states = self.mlp(hidden_states) + hidden_states = residual + hidden_states + + outputs = (hidden_states,) + + if output_attentions: + outputs += (self_attn_weights,) + + if use_cache: + outputs += (present_key_value,) + + return outputs + + +LLAMA_START_DOCSTRING = r""" + This model inherits from [`PreTrainedModel`]. Check the superclass documentation for the generic methods the + library implements for all its model (such as downloading or saving, resizing the input embeddings, pruning heads + etc.) + + This model is also a PyTorch [torch.nn.Module](https://pytorch.org/docs/stable/nn.html#torch.nn.Module) subclass. + Use it as a regular PyTorch Module and refer to the PyTorch documentation for all matter related to general usage + and behavior. + + Parameters: + config ([`LlamaConfig`]): + Model configuration class with all the parameters of the model. Initializing with a config file does not + load the weights associated with the model, only the configuration. Check out the + [`~PreTrainedModel.from_pretrained`] method to load the model weights. +""" + + +@add_start_docstrings( + "The bare LLaMA Model outputting raw hidden-states without any specific head on top.", + LLAMA_START_DOCSTRING, +) +class LlamaPreTrainedModel(PreTrainedModel): + config_class = LlamaConfig + base_model_prefix = "model" + supports_gradient_checkpointing = True + _no_split_modules = ["LlamaDecoderLayer"] + _skip_keys_device_placement = ["past_key_values", "causal_mask"] + _supports_flash_attn_2 = True + _supports_sdpa = True + _supports_cache_class = True + + def _init_weights(self, module): + std = self.config.initializer_range + if isinstance(module, nn.Linear): + module.weight.data.normal_(mean=0.0, std=std) + if module.bias is not None: + module.bias.data.zero_() + elif isinstance(module, nn.Embedding): + module.weight.data.normal_(mean=0.0, std=std) + if module.padding_idx is not None: + module.weight.data[module.padding_idx].zero_() + + def _setup_cache(self, cache_cls, max_batch_size, max_cache_len: Optional[int] = None): + if self.config._attn_implementation == "flash_attention_2" and cache_cls == StaticCache: + raise ValueError("`static` cache implementation is not compatible with `attn_implementation==flash_attention_2` " "make sure to use `sdpa` in the mean time, and open an issue at https://github.com/huggingface/transformers") + + if max_cache_len > self.model.causal_mask.shape[-1] or self.device != self.model.causal_mask.device: + causal_mask = torch.full((max_cache_len, max_cache_len), fill_value=True, device=self.device, dtype=torch.bool) + self.register_buffer("causal_mask", torch.triu(causal_mask, diagonal=1), persistent=False) + + for layer in self.model.layers: + device = layer.input_layernorm.weight.device + if hasattr(self.config, "_pre_quantization_dtype"): + dtype = self.config._pre_quantization_dtype + else: + dtype = layer.self_attn.o_proj.weight.dtype + layer.self_attn.past_key_value = cache_cls(self.config, max_batch_size, max_cache_len, device=device, dtype=dtype) + + def _reset_cache(self): + for layer in self.model.layers: + layer.self_attn.past_key_value = None + + +LLAMA_INPUTS_DOCSTRING = r""" + Args: + input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`): + Indices of input sequence tokens in the vocabulary. Padding will be ignored by default should you provide + it. + + Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and + [`PreTrainedTokenizer.__call__`] for details. + + [What are input IDs?](../glossary#input-ids) + attention_mask (`torch.Tensor` of shape `(batch_size, sequence_length)`, *optional*): + Mask to avoid performing attention on padding token indices. Mask values selected in `[0, 1]`: + + - 1 for tokens that are **not masked**, + - 0 for tokens that are **masked**. + + [What are attention masks?](../glossary#attention-mask) + + Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and + [`PreTrainedTokenizer.__call__`] for details. + + If `past_key_values` is used, optionally only the last `input_ids` have to be input (see + `past_key_values`). + + If you want to change padding behavior, you should read [`modeling_opt._prepare_decoder_attention_mask`] + and modify to your needs. See diagram 1 in [the paper](https://arxiv.org/abs/1910.13461) for more + information on the default strategy. + + - 1 indicates the head is **not masked**, + - 0 indicates the head is **masked**. + position_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*): + Indices of positions of each input sequence tokens in the position embeddings. Selected in the range `[0, + config.n_positions - 1]`. + + [What are position IDs?](../glossary#position-ids) + past_key_values (`Cache` or `tuple(tuple(torch.FloatTensor))`, *optional*): + Pre-computed hidden-states (key and values in the self-attention blocks and in the cross-attention + blocks) that can be used to speed up sequential decoding. This typically consists in the `past_key_values` + returned by the model at a previous stage of decoding, when `use_cache=True` or `config.use_cache=True`. + + Two formats are allowed: + - a [`~cache_utils.Cache`] instance; + - Tuple of `tuple(torch.FloatTensor)` of length `config.n_layers`, with each tuple having 2 tensors of + shape `(batch_size, num_heads, sequence_length, embed_size_per_head)`). This is also known as the legacy + cache format. + + The model will output the same cache format that is fed as input. If no `past_key_values` are passed, the + legacy cache format will be returned. + + If `past_key_values` are used, the user can optionally input only the last `input_ids` (those that don't + have their past key value states given to this model) of shape `(batch_size, 1)` instead of all `input_ids` + of shape `(batch_size, sequence_length)`. + inputs_embeds (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`, *optional*): + Optionally, instead of passing `input_ids` you can choose to directly pass an embedded representation. This + is useful if you want more control over how to convert `input_ids` indices into associated vectors than the + model's internal embedding lookup matrix. + use_cache (`bool`, *optional*): + If set to `True`, `past_key_values` key value states are returned and can be used to speed up decoding (see + `past_key_values`). + output_attentions (`bool`, *optional*): + Whether or not to return the attentions tensors of all attention layers. See `attentions` under returned + tensors for more detail. + output_hidden_states (`bool`, *optional*): + Whether or not to return the hidden states of all layers. See `hidden_states` under returned tensors for + more detail. + return_dict (`bool`, *optional*): + Whether or not to return a [`~utils.ModelOutput`] instead of a plain tuple. +""" + + +@add_start_docstrings( + "The bare LLaMA Model outputting raw hidden-states without any specific head on top.", + LLAMA_START_DOCSTRING, +) +class LlamaModel(LlamaPreTrainedModel): + """ + Transformer decoder consisting of *config.num_hidden_layers* layers. Each layer is a [`LlamaDecoderLayer`] + + Args: + config: LlamaConfig + """ + + def __init__(self, config: LlamaConfig): + super().__init__(config) + self.padding_idx = config.pad_token_id + self.vocab_size = config.vocab_size + + self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx) + self.layers = nn.ModuleList([LlamaDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]) + self.norm = LlamaRMSNorm(config.hidden_size, eps=config.rms_norm_eps) + self.gradient_checkpointing = False + + # Register a causal mask to separate causal and padding mask creation. Merging happens in the attention class. + # NOTE: This is not friendly with TorchScript, ONNX, ExportedProgram serialization for very large `max_position_embeddings`. + causal_mask = torch.full((config.max_position_embeddings, config.max_position_embeddings), fill_value=True, dtype=torch.bool) + self.register_buffer("causal_mask", torch.triu(causal_mask, diagonal=1), persistent=False) + # Initialize weights and apply final processing + self.post_init() + + def get_input_embeddings(self): + return self.embed_tokens + + def set_input_embeddings(self, value): + self.embed_tokens = value + + @add_start_docstrings_to_model_forward(LLAMA_INPUTS_DOCSTRING) + def forward( + self, + input_ids: torch.LongTensor = None, + attention_mask: Optional[torch.Tensor] = None, + position_ids: Optional[torch.LongTensor] = None, + past_key_values: Optional[List[torch.FloatTensor]] = None, + inputs_embeds: Optional[torch.FloatTensor] = None, + use_cache: Optional[bool] = None, + output_attentions: Optional[bool] = None, + output_hidden_states: Optional[bool] = None, + return_dict: Optional[bool] = None, + cache_position: Optional[torch.LongTensor] = None, + ) -> Union[Tuple, BaseModelOutputWithPast]: + output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions + output_hidden_states = output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states + use_cache = use_cache if use_cache is not None else self.config.use_cache + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + + if (input_ids is None) ^ (inputs_embeds is not None): + raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time, and must specify either one") + + if self.gradient_checkpointing and self.training and use_cache: + logger.warning_once("`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`.") + use_cache = False + + if inputs_embeds is None: + inputs_embeds = self.embed_tokens(input_ids) + + past_seen_tokens = 0 + if use_cache: # kept for BC (cache positions) + if not isinstance(past_key_values, StaticCache): + past_key_values = DynamicCache.from_legacy_cache(past_key_values) + past_seen_tokens = past_key_values.get_seq_length() + + if cache_position is None: + if isinstance(past_key_values, StaticCache): + raise ValueError("cache_position is a required argument when using StaticCache.") + cache_position = torch.arange(past_seen_tokens, past_seen_tokens + inputs_embeds.shape[1], device=inputs_embeds.device) + + if position_ids is None: + position_ids = cache_position.unsqueeze(0) + + causal_mask = self._update_causal_mask(attention_mask, inputs_embeds) + + # embed positions + hidden_states = inputs_embeds + + # decoder layers + all_hidden_states = () if output_hidden_states else None + all_self_attns = () if output_attentions else None + next_decoder_cache = None + + for decoder_layer in self.layers: + if output_hidden_states: + all_hidden_states += (hidden_states,) + + if self.gradient_checkpointing and self.training: + layer_outputs = self._gradient_checkpointing_func( + decoder_layer.__call__, + hidden_states, + causal_mask, + position_ids, + past_key_values, + output_attentions, + use_cache, + cache_position, + ) + else: + layer_outputs = decoder_layer( + hidden_states, + attention_mask=causal_mask, + position_ids=position_ids, + past_key_value=past_key_values, + output_attentions=output_attentions, + use_cache=use_cache, + cache_position=cache_position, + ) + + hidden_states = layer_outputs[0] + + if use_cache: + next_decoder_cache = layer_outputs[2 if output_attentions else 1] + + if output_attentions: + all_self_attns += (layer_outputs[1],) + + hidden_states = self.norm(hidden_states) + + # add hidden states from the last decoder layer + if output_hidden_states: + all_hidden_states += (hidden_states,) + + next_cache = None + if use_cache: + next_cache = next_decoder_cache.to_legacy_cache() if isinstance(next_decoder_cache, Cache) else next_decoder_cache + if not return_dict: + return tuple(v for v in [hidden_states, next_cache, all_hidden_states, all_self_attns] if v is not None) + return BaseModelOutputWithPast( + last_hidden_state=hidden_states, + past_key_values=next_cache, + hidden_states=all_hidden_states, + attentions=all_self_attns, + ) + + # TODO: As of torch==2.2.0, the `attention_mask` passed to the model in `generate` is 2D and of dynamic length even when the static + # KV cache is used. This is an issue for torch.compile which then recaptures cudagraphs at each decode steps due to the dynamic shapes. + # (`recording cudagraph tree for symint key 13`, etc.), which is VERY slow. A workaround is `@torch.compiler.disable`, but this prevents using + # `fullgraph=True`. See more context in https://github.com/huggingface/transformers/pull/29114 + def _update_causal_mask(self, attention_mask, input_tensor): + if self.config._attn_implementation == "flash_attention_2": + if attention_mask is not None and 0.0 in attention_mask: + return attention_mask + return None + + batch_size, seq_length = input_tensor.shape[:2] + dtype = input_tensor.dtype + device = input_tensor.device + + # support going beyond cached `max_position_embedding` + if seq_length > self.causal_mask.shape[-1]: + causal_mask = torch.full((2 * self.causal_mask.shape[-1], 2 * self.causal_mask.shape[-1]), fill_value=1) + self.register_buffer("causal_mask", torch.triu(causal_mask, diagonal=1), persistent=False) + + # We use the current dtype to avoid any overflows + min_dtype = torch.finfo(dtype).min + causal_mask = self.causal_mask[None, None, :, :].repeat(batch_size, 1, 1, 1).to(dtype) * min_dtype + + causal_mask = causal_mask.to(dtype=dtype, device=device) + if attention_mask is not None and attention_mask.dim() == 2: + mask_length = attention_mask.shape[-1] + padding_mask = causal_mask[..., :mask_length].eq(0.0) * attention_mask[:, None, None, :].eq(0.0) + causal_mask[..., :mask_length] = causal_mask[..., :mask_length].masked_fill(padding_mask, min_dtype) + + if self.config._attn_implementation == "sdpa" and attention_mask is not None and attention_mask.device.type == "cuda": + # TODO: For dynamo, rather use a check on fullgraph=True once this is possible (https://github.com/pytorch/pytorch/pull/120400). + is_tracing = torch.jit.is_tracing() or isinstance(input_tensor, torch.fx.Proxy) or (hasattr(torch, "_dynamo") and torch._dynamo.is_compiling()) + if not is_tracing and torch.any(attention_mask != 1): + # Attend to all tokens in fully masked rows in the causal_mask, for example the relevant first rows when + # using left padding. This is required by F.scaled_dot_product_attention memory-efficient attention path. + # Details: https://github.com/pytorch/pytorch/issues/110213 + causal_mask = AttentionMaskConverter._unmask_unattended(causal_mask, min_dtype) + + return causal_mask + + +class LlamaForCausalLM(LlamaPreTrainedModel): + _tied_weights_keys = ["lm_head.weight"] + + def __init__(self, config): + super().__init__(config) + self.model = LlamaModel(config) + self.vocab_size = config.vocab_size + self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) + + # Initialize weights and apply final processing + self.post_init() + + def get_input_embeddings(self): + return self.model.embed_tokens + + def set_input_embeddings(self, value): + self.model.embed_tokens = value + + def get_output_embeddings(self): + return self.lm_head + + def set_output_embeddings(self, new_embeddings): + self.lm_head = new_embeddings + + def set_decoder(self, decoder): + self.model = decoder + + def get_decoder(self): + return self.model + + @add_start_docstrings_to_model_forward(LLAMA_INPUTS_DOCSTRING) + @replace_return_docstrings(output_type=CausalLMOutputWithPast, config_class=_CONFIG_FOR_DOC) + def forward( + self, + input_ids: torch.LongTensor = None, + attention_mask: Optional[torch.Tensor] = None, + position_ids: Optional[torch.LongTensor] = None, + past_key_values: Optional[List[torch.FloatTensor]] = None, + inputs_embeds: Optional[torch.FloatTensor] = None, + labels: Optional[torch.LongTensor] = None, + use_cache: Optional[bool] = None, + output_attentions: Optional[bool] = None, + output_hidden_states: Optional[bool] = None, + return_dict: Optional[bool] = None, + cache_position: Optional[torch.LongTensor] = None, + ) -> Union[Tuple, CausalLMOutputWithPast]: + r""" + Args: + labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*): + Labels for computing the masked language modeling loss. Indices should either be in `[0, ..., + config.vocab_size]` or -100 (see `input_ids` docstring). Tokens with indices set to `-100` are ignored + (masked), the loss is only computed for the tokens with labels in `[0, ..., config.vocab_size]`. + + Returns: + + Example: + + ```python + >>> from transformers import AutoTokenizer, LlamaForCausalLM + + >>> model = LlamaForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf") + >>> tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf") + + >>> prompt = "Hey, are you conscious? Can you talk to me?" + >>> inputs = tokenizer(prompt, return_tensors="pt") + + >>> # Generate + >>> generate_ids = model.generate(inputs.input_ids, max_length=30) + >>> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0] + "Hey, are you conscious? Can you talk to me?\nI'm not conscious, but I can talk to you." + ```""" + output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions + output_hidden_states = output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + + # decoder outputs consists of (dec_features, layer_state, dec_hidden, dec_attn) + outputs = self.model( + input_ids=input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + past_key_values=past_key_values, + inputs_embeds=inputs_embeds, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + cache_position=cache_position, + ) + + hidden_states = outputs[0] + if self.config.pretraining_tp > 1: + lm_head_slices = self.lm_head.weight.split(self.vocab_size // self.config.pretraining_tp, dim=0) + logits = [F.linear(hidden_states, lm_head_slices[i]) for i in range(self.config.pretraining_tp)] + logits = torch.cat(logits, dim=-1) + else: + logits = self.lm_head(hidden_states) + logits = logits.float() + + loss = None + if labels is not None: + # Shift so that tokens < n predict n + shift_logits = logits[..., :-1, :].contiguous() + shift_labels = labels[..., 1:].contiguous() + # Flatten the tokens + loss_fct = CrossEntropyLoss() + shift_logits = shift_logits.view(-1, self.config.vocab_size) + shift_labels = shift_labels.view(-1) + # Enable model parallelism + shift_labels = shift_labels.to(shift_logits.device) + loss = loss_fct(shift_logits, shift_labels) + + if not return_dict: + output = (logits,) + outputs[1:] + return (loss,) + output if loss is not None else output + + return CausalLMOutputWithPast( + loss=loss, + logits=logits, + past_key_values=outputs.past_key_values, + hidden_states=outputs.hidden_states, + attentions=outputs.attentions, + ) + + def prepare_inputs_for_generation(self, input_ids, past_key_values=None, attention_mask=None, inputs_embeds=None, **kwargs): + past_length = 0 + if past_key_values is not None: + if isinstance(past_key_values, Cache): + cache_length = past_key_values.get_seq_length() + past_length = past_key_values.seen_tokens + max_cache_length = past_key_values.get_max_length() + else: + cache_length = past_length = past_key_values[0][0].shape[2] + max_cache_length = None + + # Keep only the unprocessed tokens: + # 1 - If the length of the attention_mask exceeds the length of input_ids, then we are in a setting where + # some of the inputs are exclusively passed as part of the cache (e.g. when passing input_embeds as + # input) + if attention_mask is not None and attention_mask.shape[1] > input_ids.shape[1]: + input_ids = input_ids[:, -(attention_mask.shape[1] - past_length) :] + # 2 - If the past_length is smaller than input_ids', then input_ids holds all input tokens. We can discard + # input_ids based on the past_length. + elif past_length < input_ids.shape[1]: + input_ids = input_ids[:, past_length:] + # 3 - Otherwise (past_length >= input_ids.shape[1]), let's assume input_ids only has unprocessed tokens. + + # If we are about to go beyond the maximum cache length, we need to crop the input attention mask. + if max_cache_length is not None and attention_mask is not None and cache_length + input_ids.shape[1] > max_cache_length: + attention_mask = attention_mask[:, -max_cache_length:] + + position_ids = kwargs.get("position_ids", None) + if attention_mask is not None and position_ids is None: + # create position_ids on the fly for batch generation + position_ids = attention_mask.long().cumsum(-1) - 1 + position_ids.masked_fill_(attention_mask == 0, 1) + if past_key_values: + position_ids = position_ids[:, -input_ids.shape[1] :] + + if self.generation_config.cache_implementation == "static": + # generation with static cache + cache_position = kwargs.get("cache_position", None) + if cache_position is None: + past_length = 0 + else: + past_length = cache_position[-1] + 1 + input_ids = input_ids[:, past_length:] + position_ids = position_ids[:, past_length:] + + # TODO @gante we should only keep a `cache_position` in generate, and do +=1. + # same goes for position ids. Could also help with continued generation. + input_length = position_ids.shape[-1] if position_ids is not None else input_ids.shape[-1] + cache_position = torch.arange(past_length, past_length + input_length, device=input_ids.device) + position_ids = position_ids.contiguous() if position_ids is not None else None + + # if `inputs_embeds` are passed, we only want to use them in the 1st generation step + if inputs_embeds is not None and past_key_values is None: + model_inputs = {"inputs_embeds": inputs_embeds} + else: + # The `contiguous()` here is necessary to have a static stride during decoding. torchdynamo otherwise + # recompiles graphs as the stride of the inputs is a guard. Ref: https://github.com/huggingface/transformers/pull/29114 + # TODO: use `next_tokens` directly instead. + model_inputs = {"input_ids": input_ids.contiguous()} + + model_inputs.update( + { + "position_ids": position_ids, + "cache_position": cache_position, + "past_key_values": past_key_values, + "use_cache": kwargs.get("use_cache"), + "attention_mask": attention_mask, + } + ) + return model_inputs + + @staticmethod + def _reorder_cache(past_key_values, beam_idx): + reordered_past = () + for layer_past in past_key_values: + reordered_past += (tuple(past_state.index_select(0, beam_idx.to(past_state.device)) for past_state in layer_past),) + return reordered_past + + +@add_start_docstrings( + """ + The LLaMa Model transformer with a sequence classification head on top (linear layer). + + [`LlamaForSequenceClassification`] uses the last token in order to do the classification, as other causal models + (e.g. GPT-2) do. + + Since it does classification on the last token, it requires to know the position of the last token. If a + `pad_token_id` is defined in the configuration, it finds the last token that is not a padding token in each row. If + no `pad_token_id` is defined, it simply takes the last value in each row of the batch. Since it cannot guess the + padding tokens when `inputs_embeds` are passed instead of `input_ids`, it does the same (take the last value in + each row of the batch). + """, + LLAMA_START_DOCSTRING, +) +class LlamaForSequenceClassification(LlamaPreTrainedModel): + def __init__(self, config): + super().__init__(config) + self.num_labels = config.num_labels + self.model = LlamaModel(config) + self.score = nn.Linear(config.hidden_size, self.num_labels, bias=False) + + # Initialize weights and apply final processing + self.post_init() + + def get_input_embeddings(self): + return self.model.embed_tokens + + def set_input_embeddings(self, value): + self.model.embed_tokens = value + + @add_start_docstrings_to_model_forward(LLAMA_INPUTS_DOCSTRING) + def forward( + self, + input_ids: torch.LongTensor = None, + attention_mask: Optional[torch.Tensor] = None, + position_ids: Optional[torch.LongTensor] = None, + past_key_values: Optional[List[torch.FloatTensor]] = None, + inputs_embeds: Optional[torch.FloatTensor] = None, + labels: Optional[torch.LongTensor] = None, + use_cache: Optional[bool] = None, + output_attentions: Optional[bool] = None, + output_hidden_states: Optional[bool] = None, + return_dict: Optional[bool] = None, + ) -> Union[Tuple, SequenceClassifierOutputWithPast]: + r""" + labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*): + Labels for computing the sequence classification/regression loss. Indices should be in `[0, ..., + config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If + `config.num_labels > 1` a classification loss is computed (Cross-Entropy). + """ + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + + transformer_outputs = self.model( + input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + past_key_values=past_key_values, + inputs_embeds=inputs_embeds, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + ) + hidden_states = transformer_outputs[0] + logits = self.score(hidden_states) + + if input_ids is not None: + batch_size = input_ids.shape[0] + else: + batch_size = inputs_embeds.shape[0] + + if self.config.pad_token_id is None and batch_size != 1: + raise ValueError("Cannot handle batch sizes > 1 if no padding token is defined.") + if self.config.pad_token_id is None: + sequence_lengths = -1 + else: + if input_ids is not None: + # if no pad token found, use modulo instead of reverse indexing for ONNX compatibility + sequence_lengths = torch.eq(input_ids, self.config.pad_token_id).int().argmax(-1) - 1 + sequence_lengths = sequence_lengths % input_ids.shape[-1] + sequence_lengths = sequence_lengths.to(logits.device) + else: + sequence_lengths = -1 + + pooled_logits = logits[torch.arange(batch_size, device=logits.device), sequence_lengths] + + loss = None + if labels is not None: + labels = labels.to(logits.device) + if self.config.problem_type is None: + if self.num_labels == 1: + self.config.problem_type = "regression" + elif self.num_labels > 1 and (labels.dtype == torch.long or labels.dtype == torch.int): + self.config.problem_type = "single_label_classification" + else: + self.config.problem_type = "multi_label_classification" + + if self.config.problem_type == "regression": + loss_fct = MSELoss() + if self.num_labels == 1: + loss = loss_fct(pooled_logits.squeeze(), labels.squeeze()) + else: + loss = loss_fct(pooled_logits, labels) + elif self.config.problem_type == "single_label_classification": + loss_fct = CrossEntropyLoss() + loss = loss_fct(pooled_logits.view(-1, self.num_labels), labels.view(-1)) + elif self.config.problem_type == "multi_label_classification": + loss_fct = BCEWithLogitsLoss() + loss = loss_fct(pooled_logits, labels) + if not return_dict: + output = (pooled_logits,) + transformer_outputs[1:] + return ((loss,) + output) if loss is not None else output + + return SequenceClassifierOutputWithPast( + loss=loss, + logits=pooled_logits, + past_key_values=transformer_outputs.past_key_values, + hidden_states=transformer_outputs.hidden_states, + attentions=transformer_outputs.attentions, + ) + + +@add_start_docstrings( + """ +The Llama Model transformer with a span classification head on top for extractive question-answering tasks like +SQuAD (a linear layer on top of the hidden-states output to compute `span start logits` and `span end logits`). + """, + LLAMA_START_DOCSTRING, +) +class LlamaForQuestionAnswering(LlamaPreTrainedModel): + base_model_prefix = "transformer" + + # Copied from transformers.models.bloom.modeling_bloom.BloomForQuestionAnswering.__init__ with Bloom->Llama + def __init__(self, config): + super().__init__(config) + self.transformer = LlamaModel(config) + self.qa_outputs = nn.Linear(config.hidden_size, 2) + + # Initialize weights and apply final processing + self.post_init() + + def get_input_embeddings(self): + return self.transformer.embed_tokens + + def set_input_embeddings(self, value): + self.transformer.embed_tokens = value + + @add_start_docstrings_to_model_forward(LLAMA_INPUTS_DOCSTRING) + def forward( + self, + input_ids: Optional[torch.LongTensor] = None, + attention_mask: Optional[torch.FloatTensor] = None, + position_ids: Optional[torch.LongTensor] = None, + past_key_values: Optional[List[torch.FloatTensor]] = None, + inputs_embeds: Optional[torch.FloatTensor] = None, + start_positions: Optional[torch.LongTensor] = None, + end_positions: Optional[torch.LongTensor] = None, + output_attentions: Optional[bool] = None, + output_hidden_states: Optional[bool] = None, + return_dict: Optional[bool] = None, + ) -> Union[Tuple, QuestionAnsweringModelOutput]: + r""" + start_positions (`torch.LongTensor` of shape `(batch_size,)`, *optional*): + Labels for position (index) of the start of the labelled span for computing the token classification loss. + Positions are clamped to the length of the sequence (`sequence_length`). Position outside of the sequence + are not taken into account for computing the loss. + end_positions (`torch.LongTensor` of shape `(batch_size,)`, *optional*): + Labels for position (index) of the end of the labelled span for computing the token classification loss. + Positions are clamped to the length of the sequence (`sequence_length`). Position outside of the sequence + are not taken into account for computing the loss. + """ + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + + outputs = self.transformer( + input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + past_key_values=past_key_values, + inputs_embeds=inputs_embeds, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + ) + + sequence_output = outputs[0] + + logits = self.qa_outputs(sequence_output) + start_logits, end_logits = logits.split(1, dim=-1) + start_logits = start_logits.squeeze(-1).contiguous() + end_logits = end_logits.squeeze(-1).contiguous() + + total_loss = None + if start_positions is not None and end_positions is not None: + # If we are on multi-GPU, split add a dimension + if len(start_positions.size()) > 1: + start_positions = start_positions.squeeze(-1).to(start_logits.device) + if len(end_positions.size()) > 1: + end_positions = end_positions.squeeze(-1).to(end_logits.device) + # sometimes the start/end positions are outside our model inputs, we ignore these terms + ignored_index = start_logits.size(1) + start_positions = start_positions.clamp(0, ignored_index) + end_positions = end_positions.clamp(0, ignored_index) + + loss_fct = CrossEntropyLoss(ignore_index=ignored_index) + start_loss = loss_fct(start_logits, start_positions) + end_loss = loss_fct(end_logits, end_positions) + total_loss = (start_loss + end_loss) / 2 + + if not return_dict: + output = (start_logits, end_logits) + outputs[2:] + return ((total_loss,) + output) if total_loss is not None else output + + return QuestionAnsweringModelOutput( + loss=total_loss, + start_logits=start_logits, + end_logits=end_logits, + hidden_states=outputs.hidden_states, + attentions=outputs.attentions, + ) diff --git a/llava/model/llava_arch.py b/llava/model/llava_arch.py new file mode 100644 index 0000000..e7beec7 --- /dev/null +++ b/llava/model/llava_arch.py @@ -0,0 +1,623 @@ +# Copyright 2023 Haotian Liu +# +# 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. + + +from abc import ABC, abstractmethod + +import math +import re +import time +import torch +import torch.nn as nn +from .multimodal_encoder.builder import build_vision_tower +from .multimodal_resampler.builder import build_vision_resampler +from .multimodal_projector.builder import build_vision_projector + +from llava.constants import IGNORE_INDEX, IMAGE_TOKEN_INDEX, DEFAULT_IMAGE_PATCH_TOKEN, DEFAULT_IM_START_TOKEN, DEFAULT_IM_END_TOKEN + +from llava.mm_utils import get_anyres_image_grid_shape +from llava.utils import rank0_print, rank_print +import random + + +class LlavaMetaModel: + + def __init__(self, config): + super(LlavaMetaModel, self).__init__(config) + + if hasattr(config, "mm_vision_tower"): + delay_load = getattr(config, "delay_load", False) + self.vision_tower = build_vision_tower(config, delay_load=delay_load) + self.vision_resampler = build_vision_resampler(config, vision_tower=self.vision_tower) + self.mm_projector = build_vision_projector(config, vision_cfg=self.vision_tower.config) + + if "unpad" in getattr(config, "mm_patch_merge_type", ""): + self.image_newline = nn.Parameter(torch.empty(config.hidden_size, dtype=self.dtype)) + + def get_vision_tower(self): + vision_tower = getattr(self, "vision_tower", None) + if type(vision_tower) is list: + vision_tower = vision_tower[0] + return vision_tower + + def initialize_vision_modules(self, model_args, fsdp=None): + vision_tower = model_args.vision_tower + mm_vision_select_layer = model_args.mm_vision_select_layer + mm_vision_select_feature = model_args.mm_vision_select_feature + pretrain_mm_mlp_adapter = model_args.pretrain_mm_mlp_adapter + mm_patch_merge_type = model_args.mm_patch_merge_type + + self.config.mm_vision_tower = vision_tower + self.config.vision_tower_pretrained = getattr(model_args, "vision_tower_pretrained", "") + + if self.get_vision_tower() is None: + vision_tower = build_vision_tower(model_args) + vision_resampler = build_vision_resampler(model_args, vision_tower=vision_tower) + for k, v in vision_resampler.config.items(): + setattr(self.config, k, v) + + if fsdp is not None and len(fsdp) > 0: + self.vision_tower = [vision_tower] + self.vision_resampler = [vision_resampler] + else: + self.vision_tower = vision_tower + self.vision_resampler = vision_resampler + else: + if fsdp is not None and len(fsdp) > 0: + vision_resampler = self.vision_resampler[0] + vision_tower = self.vision_tower[0] + else: + vision_resampler = self.vision_resampler + vision_tower = self.vision_tower + vision_tower.load_model() + + # In case it is frozen by LoRA + for p in self.vision_resampler.parameters(): + p.requires_grad = True + + self.config.use_mm_proj = True + self.config.mm_projector_type = getattr(model_args, "mm_projector_type", "linear") + self.config.mm_hidden_size = getattr(vision_resampler, "hidden_size", vision_tower.hidden_size) + self.config.mm_vision_select_layer = mm_vision_select_layer + self.config.mm_vision_select_feature = mm_vision_select_feature + self.config.mm_patch_merge_type = mm_patch_merge_type + + + if not hasattr(self.config, 'add_faster_video'): + if model_args.add_faster_video: + embed_std = 1 / torch.sqrt(torch.tensor(self.config.hidden_size, dtype=self.dtype)) + self.faster_token = nn.Parameter( + torch.randn(self.config.hidden_size, dtype=self.dtype) * embed_std + ) + + if getattr(self, "mm_projector", None) is None: + self.mm_projector = build_vision_projector(self.config, vision_cfg=vision_tower.config) + + if "unpad" in mm_patch_merge_type: + embed_std = 1 / torch.sqrt(torch.tensor(self.config.hidden_size, dtype=self.dtype)) + self.image_newline = nn.Parameter(torch.randn(self.config.hidden_size, dtype=self.dtype) * embed_std) + else: + # In case it is frozen by LoRA + for p in self.mm_projector.parameters(): + p.requires_grad = True + + if pretrain_mm_mlp_adapter is not None: + mm_projector_weights = torch.load(pretrain_mm_mlp_adapter, map_location="cpu") + + def get_w(weights, keyword): + return {k.split(keyword + ".")[1]: v for k, v in weights.items() if keyword in k} + + incompatible_keys = self.mm_projector.load_state_dict(get_w(mm_projector_weights, "mm_projector")) + rank0_print(f"Loaded mm projector weights from {pretrain_mm_mlp_adapter}. Incompatible keys: {incompatible_keys}") + incompatible_keys = self.vision_resampler.load_state_dict(get_w(mm_projector_weights, "vision_resampler"), strict=False) + rank0_print(f"Loaded vision resampler weights from {pretrain_mm_mlp_adapter}. Incompatible keys: {incompatible_keys}") + + +def unpad_image(tensor, original_size): + """ + Unpads a PyTorch tensor of a padded and resized image. + + Args: + tensor (torch.Tensor): The image tensor, assumed to be in CxHxW format. + original_size (tuple): The original size of the image (height, width). + + Returns: + torch.Tensor: The unpadded image tensor. + """ + original_width, original_height = original_size + current_height, current_width = tensor.shape[1:] + + # Compute aspect ratios + original_aspect_ratio = original_width / original_height + current_aspect_ratio = current_width / current_height + + # Determine padding size and direction + if original_aspect_ratio > current_aspect_ratio: + # Padding was added to the height + scale_factor = current_width / original_width + new_height = int(original_height * scale_factor) + padding = (current_height - new_height) // 2 + unpadded_tensor = tensor[:, padding : current_height - padding, :] + else: + # Padding was added to the width + scale_factor = current_height / original_height + new_width = int(original_width * scale_factor) + padding = (current_width - new_width) // 2 + unpadded_tensor = tensor[:, :, padding : current_width - padding] + + return unpadded_tensor + + +class LlavaMetaForCausalLM(ABC): + + @abstractmethod + def get_model(self): + pass + + def get_vision_tower(self): + return self.get_model().get_vision_tower() + + def get_2dPool(self, image_feature, stride=2): + height = width = self.get_vision_tower().num_patches_per_side + num_frames, num_tokens, num_dim = image_feature.shape + image_feature = image_feature.view(num_frames, height, width, -1) + image_feature = image_feature.permute(0, 3, 1, 2).contiguous() + # image_feature = nn.functional.max_pool2d(image_feature, self.config.mm_spatial_pool_stride) + if self.config.mm_spatial_pool_mode == "average": + image_feature = nn.functional.avg_pool2d(image_feature, stride) + elif self.config.mm_spatial_pool_mode == "max": + image_feature = nn.functional.max_pool2d(image_feature, stride) + elif self.config.mm_spatial_pool_mode == "bilinear": + height, width = image_feature.shape[2:] + scaled_shape = [math.ceil(height / stride), math.ceil(width / stride)] + image_feature = nn.functional.interpolate(image_feature, size=scaled_shape, mode='bilinear') + + else: + raise ValueError(f"Unexpected mm_spatial_pool_mode: {self.config.mm_spatial_pool_mode}") + image_feature = image_feature.permute(0, 2, 3, 1) + image_feature = image_feature.view(num_frames, -1, num_dim) + return image_feature + + def encode_images(self, images): + image_features = self.get_model().get_vision_tower()(images) + # image_features = self.get_model().vision_resampler(image_features, images=images) + image_features = self.get_model().mm_projector(image_features) + return image_features + + def encode_multimodals(self, videos_or_images, video_idx_in_batch, split_sizes=None): + videos_or_images_features = self.get_model().get_vision_tower()(videos_or_images) + per_videos_or_images_features = torch.split(videos_or_images_features, split_sizes, dim=0) # tuple, (dim_1, 576, 4096) + all_videos_or_images_features = [] + all_faster_video_features = [] + cur_mm_spatial_pool_stride = self.config.mm_spatial_pool_stride + + for idx, feat in enumerate(per_videos_or_images_features): + + feat = self.get_model().mm_projector(feat) + faster_video_feature = 0 + slower_img_feat = 0 + if idx in video_idx_in_batch and cur_mm_spatial_pool_stride > 1: + slower_img_feat = self.get_2dPool(feat,cur_mm_spatial_pool_stride) + if self.config.add_faster_video: + cur_mm_spatial_pool_stride = cur_mm_spatial_pool_stride * 2 + faster_video_feature = self.get_2dPool(feat,cur_mm_spatial_pool_stride) + if slower_img_feat is not 0: + all_videos_or_images_features.append(slower_img_feat) + else: + all_videos_or_images_features.append(feat) + all_faster_video_features.append(faster_video_feature) + return all_videos_or_images_features,all_faster_video_features + + def add_token_per_grid(self, image_feature): + resize_h = int(math.sqrt(image_feature.shape[1])) + num_frames = image_feature.shape[0] + feature_dim = image_feature.shape[-1] + + image_feature = image_feature.view(num_frames, 1, resize_h, resize_h, -1) + image_feature = image_feature.permute(4, 0, 2, 1, 3).contiguous() + image_feature = image_feature.flatten(1, 2).flatten(2, 3) + image_feature = torch.cat((image_feature, self.model.image_newline[:, None, None].expand(*image_feature.shape[:-1], 1).to(image_feature.device)), dim=-1) + if getattr(self.config, "add_faster_video", False): + # import pdb; pdb.set_trace() + # (3584, 832, 14) -> (3584, 64, 13, 14) + image_feature = image_feature.view(feature_dim, num_frames,resize_h, -1) + # (3584, 64, 13, 14) -> (64, 13, 14, 3584) + image_feature = image_feature.permute(1, 2, 3, 0).contiguous() + # (64, 13, 14, 3584) -> (64, 13*14, 3584) + image_feature = image_feature.flatten(1, 2) + # import pdb; pdb.set_trace() + return image_feature + # import pdb; pdb.set_trace() + image_feature = image_feature.flatten(1, 2).transpose(0, 1) + return image_feature + + def add_token_per_frame(self, image_feature): + image_feature = image_feature.permute(2, 0, 1).contiguous() + image_feature = torch.cat((image_feature, self.model.image_newline[:, None, None].expand(*image_feature.shape[:-1], 1).to(image_feature.device)), dim=-1) + image_feature = image_feature.permute(1, 2, 0).contiguous() + return image_feature + + def prepare_inputs_labels_for_multimodal(self, input_ids, position_ids, attention_mask, past_key_values, labels, images, modalities=["image"], image_sizes=None): + vision_tower = self.get_vision_tower() + # rank_print(modalities) + if vision_tower is None or images is None or input_ids.shape[1] == 1: + return input_ids, position_ids, attention_mask, past_key_values, None, labels + + if isinstance(modalities, str): + modalities = [modalities] + + # import pdb; pdb.set_trace() + if type(images) is list or images.ndim == 5: + if type(images) is list: + images = [x.unsqueeze(0) if x.ndim == 3 else x for x in images] + + video_idx_in_batch = [] + for _ in range(len(modalities)): + if modalities[_] == "video": + video_idx_in_batch.append(_) + + images_list = [] + for image in images: + if image.ndim == 4: + images_list.append(image) + else: + images_list.append(image.unsqueeze(0)) + + concat_images = torch.cat([image for image in images_list], dim=0) + split_sizes = [image.shape[0] for image in images_list] + encoded_image_features = self.encode_images(concat_images) + # image_features,all_faster_video_features = self.encode_multimodals(concat_images, video_idx_in_batch, split_sizes) + + # This is a list, each element is [num_images, patch * patch, dim] + # rank_print(f"Concat images : {concat_images.shape}") + encoded_image_features = torch.split(encoded_image_features, split_sizes) + image_features = [] + for idx, image_feat in enumerate(encoded_image_features): + if idx in video_idx_in_batch: + image_features.append(self.get_2dPool(image_feat)) + else: + image_features.append(image_feat) + # image_features = self.encode_multimodals(concat_images, video_idx_in_batch, split_sizes) + # rank_print(f"Encoded image feats : {[x.shape for x in image_features]}") + # image_features = torch.split(image_features, split_sizes, dim=0) + mm_patch_merge_type = getattr(self.config, "mm_patch_merge_type", "flat") + image_aspect_ratio = getattr(self.config, "image_aspect_ratio", "square") + mm_newline_position = getattr(self.config, "mm_newline_position", "one_token") + + if mm_patch_merge_type == "flat": + image_features = [x.flatten(0, 1) for x in image_features] + + elif mm_patch_merge_type.startswith("spatial"): + new_image_features = [] + for image_idx, image_feature in enumerate(image_features): + # FIXME: now assume the image is square, and split to 2x2 patches + # num_patches = h * w, where h = w = sqrt(num_patches) + # currently image_feature is a tensor of shape (4, num_patches, hidden_size) + # we want to first unflatten it to (2, 2, h, w, hidden_size) + # rank0_print("At least we are reaching here") + # import pdb; pdb.set_trace() + if image_idx in video_idx_in_batch: # video operations + # rank0_print("Video") + if mm_newline_position == "grid": + # Grid-wise + image_feature = self.add_token_per_grid(image_feature) + if getattr(self.config, "add_faster_video", False): + faster_video_feature = self.add_token_per_grid(all_faster_video_features[image_idx]) + # Add a token for each frame + concat_slow_fater_token = [] + # import pdb; pdb.set_trace() + for _ in range(image_feature.shape[0]): + if _ % self.config.faster_token_stride == 0: + concat_slow_fater_token.append(torch.cat((image_feature[_], self.model.faster_token[None].to(image_feature.device)), dim=0)) + else: + concat_slow_fater_token.append(torch.cat((faster_video_feature[_], self.model.faster_token[None].to(image_feature.device)), dim=0)) + # import pdb; pdb.set_trace() + image_feature = torch.cat(concat_slow_fater_token) + + # print("!!!!!!!!!!!!") + + new_image_features.append(image_feature) + elif mm_newline_position == "frame": + # Frame-wise + image_feature = self.add_token_per_frame(image_feature) + + new_image_features.append(image_feature.flatten(0, 1)) + + elif mm_newline_position == "one_token": + # one-token + image_feature = image_feature.flatten(0, 1) + if 'unpad' in mm_patch_merge_type: + image_feature = torch.cat(( + image_feature, + self.model.image_newline[None].to(image_feature.device) + ), dim=0) + new_image_features.append(image_feature) + elif mm_newline_position == "no_token": + new_image_features.append(image_feature.flatten(0, 1)) + else: + raise ValueError(f"Unexpected mm_newline_position: {mm_newline_position}") + elif image_feature.shape[0] > 1: # multi patches and multi images operations + # rank0_print("Single-images") + base_image_feature = image_feature[0] + image_feature = image_feature[1:] + height = width = self.get_vision_tower().num_patches_per_side + assert height * width == base_image_feature.shape[0] + + if "anyres_max" in image_aspect_ratio: + matched_anyres_max_num_patches = re.match(r"anyres_max_(\d+)", image_aspect_ratio) + if matched_anyres_max_num_patches: + max_num_patches = int(matched_anyres_max_num_patches.group(1)) + + if image_aspect_ratio == "anyres" or "anyres_max" in image_aspect_ratio: + if hasattr(self.get_vision_tower(), "image_size"): + vision_tower_image_size = self.get_vision_tower().image_size + else: + raise ValueError("vision_tower_image_size is not found in the vision tower.") + try: + num_patch_width, num_patch_height = get_anyres_image_grid_shape(image_sizes[image_idx], self.config.image_grid_pinpoints, vision_tower_image_size) + except Exception as e: + rank0_print(f"Error: {e}") + num_patch_width, num_patch_height = 2, 2 + image_feature = image_feature.view(num_patch_height, num_patch_width, height, width, -1) + else: + image_feature = image_feature.view(2, 2, height, width, -1) + + if "maxpool2x2" in mm_patch_merge_type: + image_feature = image_feature.permute(4, 0, 2, 1, 3).contiguous() + image_feature = image_feature.flatten(1, 2).flatten(2, 3) + image_feature = nn.functional.max_pool2d(image_feature, 2) + image_feature = image_feature.flatten(1, 2).transpose(0, 1) + elif "unpad" in mm_patch_merge_type and "anyres_max" in image_aspect_ratio and matched_anyres_max_num_patches: + unit = image_feature.shape[2] + image_feature = image_feature.permute(4, 0, 2, 1, 3).contiguous() + image_feature = image_feature.flatten(1, 2).flatten(2, 3) + image_feature = unpad_image(image_feature, image_sizes[image_idx]) + c, h, w = image_feature.shape + times = math.sqrt(h * w / (max_num_patches * unit**2)) + if times > 1.1: + image_feature = image_feature[None] + image_feature = nn.functional.interpolate(image_feature, [int(h // times), int(w // times)], mode="bilinear")[0] + image_feature = torch.cat((image_feature, self.model.image_newline[:, None, None].expand(*image_feature.shape[:-1], 1).to(image_feature.device)), dim=-1) + image_feature = image_feature.flatten(1, 2).transpose(0, 1) + elif "unpad" in mm_patch_merge_type: + image_feature = image_feature.permute(4, 0, 2, 1, 3).contiguous() + image_feature = image_feature.flatten(1, 2).flatten(2, 3) + image_feature = unpad_image(image_feature, image_sizes[image_idx]) + image_feature = torch.cat((image_feature, self.model.image_newline[:, None, None].expand(*image_feature.shape[:-1], 1).to(image_feature.device)), dim=-1) + image_feature = image_feature.flatten(1, 2).transpose(0, 1) + else: + image_feature = image_feature.permute(0, 2, 1, 3, 4).contiguous() + image_feature = image_feature.flatten(0, 3) + if "nobase" in mm_patch_merge_type: + pass + else: + image_feature = torch.cat((base_image_feature, image_feature), dim=0) + new_image_features.append(image_feature) + else: # single image operations + image_feature = image_feature[0] + if "unpad" in mm_patch_merge_type: + image_feature = torch.cat((image_feature, self.model.image_newline[None]), dim=0) + + new_image_features.append(image_feature) + image_features = new_image_features + else: + raise ValueError(f"Unexpected mm_patch_merge_type: {self.config.mm_patch_merge_type}") + else: + image_features = self.encode_images(images) + + # TODO: image start / end is not implemented here to support pretraining. + if getattr(self.config, "tune_mm_mlp_adapter", False) and getattr(self.config, "mm_use_im_start_end", False): + raise NotImplementedError + # rank_print(f"Total images : {len(image_features)}") + + # Let's just add dummy tensors if they do not exist, + # it is a headache to deal with None all the time. + # But it is not ideal, and if you have a better idea, + # please open an issue / submit a PR, thanks. + _labels = labels + _position_ids = position_ids + _attention_mask = attention_mask + if attention_mask is None: + attention_mask = torch.ones_like(input_ids, dtype=torch.bool) + else: + attention_mask = attention_mask.bool() + if position_ids is None: + position_ids = torch.arange(0, input_ids.shape[1], dtype=torch.long, device=input_ids.device) + if labels is None: + labels = torch.full_like(input_ids, IGNORE_INDEX) + + # remove the padding using attention_mask -- FIXME + _input_ids = input_ids + input_ids = [cur_input_ids[cur_attention_mask] for cur_input_ids, cur_attention_mask in zip(input_ids, attention_mask)] + labels = [cur_labels[cur_attention_mask] for cur_labels, cur_attention_mask in zip(labels, attention_mask)] + + new_input_embeds = [] + new_labels = [] + cur_image_idx = 0 + # rank_print("Inserting Images embedding") + for batch_idx, cur_input_ids in enumerate(input_ids): + num_images = (cur_input_ids == IMAGE_TOKEN_INDEX).sum() + # rank0_print(num_images) + if num_images == 0: + cur_image_features = image_features[cur_image_idx] + cur_input_embeds_1 = self.get_model().embed_tokens(cur_input_ids) + cur_input_embeds = torch.cat([cur_input_embeds_1, cur_image_features[0:0]], dim=0) + new_input_embeds.append(cur_input_embeds) + new_labels.append(labels[batch_idx]) + cur_image_idx += 1 + continue + + image_token_indices = [-1] + torch.where(cur_input_ids == IMAGE_TOKEN_INDEX)[0].tolist() + [cur_input_ids.shape[0]] + cur_input_ids_noim = [] + cur_labels = labels[batch_idx] + cur_labels_noim = [] + for i in range(len(image_token_indices) - 1): + cur_input_ids_noim.append(cur_input_ids[image_token_indices[i] + 1 : image_token_indices[i + 1]]) + cur_labels_noim.append(cur_labels[image_token_indices[i] + 1 : image_token_indices[i + 1]]) + split_sizes = [x.shape[0] for x in cur_labels_noim] + cur_input_embeds = self.get_model().embed_tokens(torch.cat(cur_input_ids_noim)) + cur_input_embeds_no_im = torch.split(cur_input_embeds, split_sizes, dim=0) + cur_new_input_embeds = [] + cur_new_labels = [] + + for i in range(num_images + 1): + cur_new_input_embeds.append(cur_input_embeds_no_im[i]) + cur_new_labels.append(cur_labels_noim[i]) + if i < num_images: + try: + cur_image_features = image_features[cur_image_idx] + except IndexError: + cur_image_features = image_features[cur_image_idx - 1] + cur_image_idx += 1 + cur_new_input_embeds.append(cur_image_features) + cur_new_labels.append(torch.full((cur_image_features.shape[0],), IGNORE_INDEX, device=cur_labels.device, dtype=cur_labels.dtype)) + + cur_new_input_embeds = [x.to(self.device) for x in cur_new_input_embeds] + + # import pdb; pdb.set_trace() + cur_new_input_embeds = torch.cat(cur_new_input_embeds) + cur_new_labels = torch.cat(cur_new_labels) + + new_input_embeds.append(cur_new_input_embeds) + new_labels.append(cur_new_labels) + + # Truncate sequences to max length as image embeddings can make the sequence longer + tokenizer_model_max_length = getattr(self.config, "tokenizer_model_max_length", None) + # rank_print("Finishing Inserting") + + new_input_embeds = [x[:tokenizer_model_max_length] for x, modality in zip(new_input_embeds, modalities)] + new_labels = [x[:tokenizer_model_max_length] for x, modality in zip(new_labels, modalities)] + # TODO: Hard code for control loss spike + # if tokenizer_model_max_length is not None: + # new_input_embeds = [x[:4096] if modality != "video" else x[:tokenizer_model_max_length] for x, modality in zip(new_input_embeds, modalities)] + # new_labels = [x[:4096] if modality != "video" else x[:tokenizer_model_max_length] for x, modality in zip(new_labels, modalities)] + + # Combine them + max_len = max(x.shape[0] for x in new_input_embeds) + batch_size = len(new_input_embeds) + + new_input_embeds_padded = [] + new_labels_padded = torch.full((batch_size, max_len), IGNORE_INDEX, dtype=new_labels[0].dtype, device=new_labels[0].device) + attention_mask = torch.zeros((batch_size, max_len), dtype=attention_mask.dtype, device=attention_mask.device) + position_ids = torch.zeros((batch_size, max_len), dtype=position_ids.dtype, device=position_ids.device) + # rank0_print("Prepare pos id") + + for i, (cur_new_embed, cur_new_labels) in enumerate(zip(new_input_embeds, new_labels)): + cur_len = cur_new_embed.shape[0] + if getattr(self.config, "tokenizer_padding_side", "right") == "left": + new_input_embeds_padded.append(torch.cat((torch.zeros((max_len - cur_len, cur_new_embed.shape[1]), dtype=cur_new_embed.dtype, device=cur_new_embed.device), cur_new_embed), dim=0)) + if cur_len > 0: + new_labels_padded[i, -cur_len:] = cur_new_labels + attention_mask[i, -cur_len:] = True + position_ids[i, -cur_len:] = torch.arange(0, cur_len, dtype=position_ids.dtype, device=position_ids.device) + else: + new_input_embeds_padded.append(torch.cat((cur_new_embed, torch.zeros((max_len - cur_len, cur_new_embed.shape[1]), dtype=cur_new_embed.dtype, device=cur_new_embed.device)), dim=0)) + if cur_len > 0: + new_labels_padded[i, :cur_len] = cur_new_labels + attention_mask[i, :cur_len] = True + position_ids[i, :cur_len] = torch.arange(0, cur_len, dtype=position_ids.dtype, device=position_ids.device) + + new_input_embeds = torch.stack(new_input_embeds_padded, dim=0) + # rank0_print("tokenizer padding") + + if _labels is None: + new_labels = None + else: + new_labels = new_labels_padded + + if _attention_mask is None: + attention_mask = None + else: + attention_mask = attention_mask.to(dtype=_attention_mask.dtype) + + if _position_ids is None: + position_ids = None + if getattr(self.config, "use_pos_skipping", False) and self.training: + position_ids = torch.arange(new_input_embeds.size(1), device=new_input_embeds.device).unsqueeze(0).to(new_input_embeds.device) + split_position = random.randint(0, new_input_embeds.size(1)) + left_add = random.randint(0, self.config.pos_skipping_range) + right_add = random.randint(left_add, self.config.pos_skipping_range) + position_ids[:, :split_position] += left_add + position_ids[:, split_position:] += right_add + # import pdb; pdb.set_trace() + # rank0_print("Finish preparing") + return None, position_ids, attention_mask, past_key_values, new_input_embeds, new_labels + + def initialize_vision_tokenizer(self, model_args, tokenizer): + if model_args.mm_use_im_patch_token: + tokenizer.add_tokens([DEFAULT_IMAGE_PATCH_TOKEN], special_tokens=True) + self.resize_token_embeddings(len(tokenizer)) + + if model_args.mm_use_im_start_end: + num_new_tokens = tokenizer.add_tokens([DEFAULT_IM_START_TOKEN, DEFAULT_IM_END_TOKEN], special_tokens=True) + self.resize_token_embeddings(len(tokenizer)) + + if num_new_tokens > 0: + input_embeddings = self.get_input_embeddings().weight.data + output_embeddings = self.get_output_embeddings().weight.data + + input_embeddings_avg = input_embeddings[:-num_new_tokens].mean(dim=0, keepdim=True) + output_embeddings_avg = output_embeddings[:-num_new_tokens].mean(dim=0, keepdim=True) + + input_embeddings[-num_new_tokens:] = input_embeddings_avg + output_embeddings[-num_new_tokens:] = output_embeddings_avg + + # 老参数?! + if model_args.tune_mm_mlp_adapter: + for p in self.get_input_embeddings().parameters(): + p.requires_grad = True + for p in self.get_output_embeddings().parameters(): + p.requires_grad = False + + # 这个是老的参数,新的模型projector被集成到model里面了?! + if model_args.pretrain_mm_mlp_adapter: + mm_projector_weights = torch.load(model_args.pretrain_mm_mlp_adapter, map_location="cpu") + embed_tokens_weight = mm_projector_weights["model.embed_tokens.weight"] + assert num_new_tokens == 2 + if input_embeddings.shape == embed_tokens_weight.shape: + input_embeddings[-num_new_tokens:] = embed_tokens_weight[-num_new_tokens:] + elif embed_tokens_weight.shape[0] == num_new_tokens: + input_embeddings[-num_new_tokens:] = embed_tokens_weight + else: + raise ValueError(f"Unexpected embed_tokens_weight shape. Pretrained: {embed_tokens_weight.shape}. Current: {input_embeddings.shape}. Numer of new tokens: {num_new_tokens}.") + elif model_args.mm_use_im_patch_token: + if model_args.tune_mm_mlp_adapter: + for p in self.get_input_embeddings().parameters(): + p.requires_grad = False + for p in self.get_output_embeddings().parameters(): + p.requires_grad = False + + + # 改这个!直接把图片和动作的新token一起考虑就行 + def initialize_action_tokenizer(self, model_args, actiontokenizer): + base_size = 152064 + input_embeddings = self.get_input_embeddings().weight.data + output_embeddings = self.get_output_embeddings().weight.data + print('Origin input embedding shape is :', input_embeddings.shape) + print('Origin output embedding shape is :', output_embeddings.shape) + if input_embeddings.shape[0] == base_size: + print('Before add action tokens, the vocab size is:', actiontokenizer.vocab_size) + + else: + print('After add action tokens, the vocab size is:', actiontokenizer.vocab_size) + + # 已经训练好的,那么直接用预训练的 + self.resize_token_embeddings(actiontokenizer.vocab_size) + num_new_tokens = actiontokenizer.num_new_tokens + + input_embeddings_avg = input_embeddings[:-num_new_tokens].mean(dim=0, keepdim=True) + output_embeddings_avg = output_embeddings[:-num_new_tokens].mean(dim=0, keepdim=True) + + input_embeddings[-num_new_tokens:] = input_embeddings_avg + output_embeddings[-num_new_tokens:] = output_embeddings_avg \ No newline at end of file diff --git a/llava/model/make_delta.py b/llava/model/make_delta.py new file mode 100644 index 0000000..7b3fbab --- /dev/null +++ b/llava/model/make_delta.py @@ -0,0 +1,52 @@ +""" +Usage: +python3 -m llava.model.make_delta --base ~/model_weights/llama-7b --target ~/model_weights/llava-7b --delta ~/model_weights/llava-7b-delta --hub-repo-id liuhaotian/llava-7b-delta +""" + +import argparse + +import torch +from tqdm import tqdm +from transformers import AutoTokenizer, AutoModelForCausalLM +from llava.model.utils import auto_upgrade + + +def make_delta(base_model_path, target_model_path, delta_path, hub_repo_id): + print("Loading base model") + base = AutoModelForCausalLM.from_pretrained(base_model_path, torch_dtype=torch.float16, low_cpu_mem_usage=True) + + print("Loading target model") + auto_upgrade(target_model_path) + target = AutoModelForCausalLM.from_pretrained(target_model_path, torch_dtype=torch.float16, low_cpu_mem_usage=True) + + print("Calculating delta") + for name, param in tqdm(target.state_dict().items(), desc="Calculating delta"): + if name not in base.state_dict(): + assert name in ["model.mm_projector.weight", "model.mm_projector.bias"], f"{name} not in base model" + continue + if param.data.shape == base.state_dict()[name].shape: + param.data -= base.state_dict()[name] + else: + assert name in ["model.embed_tokens.weight", "lm_head.weight"], f"{name} dimension mismatch: {param.data.shape} vs {base.state_dict()[name].shape}" + bparam = base.state_dict()[name] + param.data[: bparam.shape[0], : bparam.shape[1]] -= bparam + + print("Saving delta") + if hub_repo_id: + kwargs = {"push_to_hub": True, "repo_id": hub_repo_id} + else: + kwargs = {} + target.save_pretrained(delta_path, **kwargs) + target_tokenizer = AutoTokenizer.from_pretrained(target_model_path) + target_tokenizer.save_pretrained(delta_path, **kwargs) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--base-model-path", type=str, required=True) + parser.add_argument("--target-model-path", type=str, required=True) + parser.add_argument("--delta-path", type=str, required=True) + parser.add_argument("--hub-repo-id", type=str, default=None) + args = parser.parse_args() + + make_delta(args.base_model_path, args.target_model_path, args.delta_path, args.hub_repo_id) diff --git a/llava/model/multimodal_encoder/__pycache__/builder.cpython-310.pyc b/llava/model/multimodal_encoder/__pycache__/builder.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd9d5e0d38a755485c2fec26e3783ccf9bef41aa GIT binary patch literal 1177 zcmZuw!EVz)5Zzrnj^j9K0uhB<4qS35k*c05gbIj4A(TojLF~o2vE!Jo<7ju2l*+wO z&invEY9%iGk`G+q(qHI_*-Z-xWh0N@%+A}HeG{jZii2SNn*4~4iU|Ew&io?aY``Pl zfeBE68KNj4fx&P>D8UMw%t%aXYFuE2#G;nQ7Aqz;wKXoXQsPiYV|&I;%CroADR4d^ z>IQfhwaVvE1#O>Wuq~V~J??DnbYl^x!@YDGatPJ75$`E(iAVE6!VM^u*Z*PfuJ%+5SE~R3#6d6M5f@-2%0LQjNq|m_6XTdj{GOZ| z89BqphTbs(6XIr2$jAioyVA@^9|u;3Vw~Y`_%mEMGsId(P76Wt3|}H!p`fHW&oh&6 zNK5mad7dm@kLh#DXPCd0_WyPR7rKZfoD5wooTix-U{0iSeOH!)YDT`GujB-O#I2?6 zd?`u1iJpol)mUv3Qi%=C{0FKgz8Xrsc=9zWP)WF!t#aF)?cCFv8BR+5d literal 0 HcmV?d00001 diff --git a/llava/model/multimodal_encoder/__pycache__/clip_encoder.cpython-310.pyc b/llava/model/multimodal_encoder/__pycache__/clip_encoder.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0cf6932e73b5dc3a4802df4c357af2d9192090e6 GIT binary patch literal 6465 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 literal 0 HcmV?d00001 diff --git a/llava/model/multimodal_encoder/__pycache__/imagebind.cpython-310.pyc b/llava/model/multimodal_encoder/__pycache__/imagebind.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4af8df7f766706450ff3874a471dd5534d769c1a GIT binary patch literal 2843 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 literal 0 HcmV?d00001 diff --git a/llava/model/multimodal_encoder/__pycache__/siglip_encoder.cpython-310.pyc b/llava/model/multimodal_encoder/__pycache__/siglip_encoder.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..979d3418ffe68799534349eb7a8e32a4b90008eb GIT binary patch literal 21976 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 EAF?_mH2?qr literal 0 HcmV?d00001 diff --git a/llava/model/multimodal_encoder/builder.py b/llava/model/multimodal_encoder/builder.py new file mode 100644 index 0000000..c5ba291 --- /dev/null +++ b/llava/model/multimodal_encoder/builder.py @@ -0,0 +1,37 @@ +import os +from .clip_encoder import CLIPVisionTower +from .imagebind import ImageBindWrapper +from .open_clip_encoder import OpenCLIPVisionTower +from .hf_vision import HFVisionTower +from .siglip_encoder import SigLipVisionTower +from .clip_encoder import CLIPVisionTower, CLIPVisionTowerS2 + +# from .eva_clip.eva_clip_encoder import EvaClipVisionTower +# from .dev_eva_clip.eva_vit import EvaViTWrapper + + +def build_vision_tower(vision_tower_cfg, **kwargs): + vision_tower = getattr(vision_tower_cfg, "mm_vision_tower", getattr(vision_tower_cfg, "vision_tower", None)) + # print(vision_tower) + is_absolute_path_exists = os.path.exists(vision_tower) + use_s2 = getattr(vision_tower_cfg, "s2", False) + if "siglip" in vision_tower: + return SigLipVisionTower(vision_tower, vision_tower_cfg=vision_tower_cfg, **kwargs) + elif is_absolute_path_exists or vision_tower.startswith("openai") or vision_tower.startswith("laion") or "ShareGPT4V" in vision_tower: + if use_s2: + return CLIPVisionTowerS2(vision_tower, args=vision_tower_cfg, **kwargs) + else: + return CLIPVisionTower(vision_tower, args=vision_tower_cfg, **kwargs) + + elif vision_tower.startswith("hf:"): + return HFVisionTower(vision_tower, args=vision_tower_cfg, **kwargs) + elif vision_tower in ["imagebind_huge"]: + return ImageBindWrapper(vision_tower, args=vision_tower_cfg, **kwargs) + elif vision_tower.startswith("open_clip_hub"): + return OpenCLIPVisionTower(vision_tower, args=vision_tower_cfg, **kwargs) + # elif "internal-eva" in vision_tower.lower() or "eva02" in vision_tower.lower(): + # return EvaClipVisionTower(vision_tower, args=vision_tower_cfg, **kwargs) + # elif vision_tower in ["EVA-CLIP-8B", "EVA-CLIP-8B-plus"]: + # return EvaViTWrapper(vision_tower, args=vision_tower_cfg, **kwargs) + + raise ValueError(f"Unknown vision tower: {vision_tower}") diff --git a/llava/model/multimodal_encoder/clip_encoder.py b/llava/model/multimodal_encoder/clip_encoder.py new file mode 100644 index 0000000..212b262 --- /dev/null +++ b/llava/model/multimodal_encoder/clip_encoder.py @@ -0,0 +1,173 @@ +import torch +import torch.nn as nn +from llava.utils import rank0_print +from transformers import CLIPVisionModel, CLIPImageProcessor, CLIPVisionConfig + +try: + from s2wrapper import forward as multiscale_forward +except: + pass + + +class CLIPVisionTower(nn.Module): + def __init__(self, vision_tower, args, delay_load=False): + super().__init__() + + self.is_loaded = False + + self.vision_tower_name = vision_tower + self.select_layer = args.mm_vision_select_layer + self.select_feature = getattr(args, "mm_vision_select_feature", "patch") + + if not delay_load: + rank0_print(f"Loading vision tower: {vision_tower}") + self.load_model() + elif getattr(args, "unfreeze_mm_vision_tower", False): + # TODO: better detector is needed. + rank0_print(f"The checkpoint seems to contain `vision_tower` weights: `unfreeze_mm_vision_tower`: True.") + self.load_model() + elif hasattr(args, "mm_tunable_parts") and "mm_vision_tower" in args.mm_tunable_parts: + rank0_print(f"The checkpoint seems to contain `vision_tower` weights: `mm_tunable_parts` contains `mm_vision_tower`.") + self.load_model() + else: + self.cfg_only = CLIPVisionConfig.from_pretrained(self.vision_tower_name) + + def load_model(self, device_map=None): + if self.is_loaded: + rank0_print("{} is already loaded, `load_model` called again, skipping.".format(self.vision_tower_name)) + return + + self.image_processor = CLIPImageProcessor.from_pretrained(self.vision_tower_name) + self.vision_tower = CLIPVisionModel.from_pretrained(self.vision_tower_name, device_map=device_map) + self.vision_tower.requires_grad_(False) + + self.is_loaded = True + + def feature_select(self, image_forward_outs): + select_feature_type = self.select_feature + + if self.select_feature in ["slicefour_patch", "slicefour_cls_patch"]: + select_every_k_layer = len(image_forward_outs.hidden_states) // 4 + image_features = torch.cat([image_forward_outs.hidden_states[i] for i in range(select_every_k_layer + self.select_layer, len(image_forward_outs.hidden_states), select_every_k_layer)], dim=-1) + select_feature_type = select_feature_type.replace("slicefour_", "") + elif self.select_feature in ["slice_m25811_f6_patch", "slice_m25811_f6_cls_patch"]: + select_layers = [-2, -5, -8, -11, 6] + image_features = torch.cat([image_forward_outs.hidden_states[i] for i in select_layers], dim=-1) + select_feature_type = select_feature_type.replace("slice_m25811_f6_", "") + else: + image_features = image_forward_outs.hidden_states[self.select_layer] + + if select_feature_type == "patch": + image_features = image_features[:, 1:] + elif select_feature_type == "cls_patch": + image_features = image_features + else: + raise ValueError(f"Unexpected select feature: {select_feature_type}") + return image_features + + def forward(self, images): + if type(images) is list: + image_features = [] + for image in images: + image_forward_out = self.vision_tower(image.to(device=self.device, dtype=self.dtype).unsqueeze(0), output_hidden_states=True) + image_feature = self.feature_select(image_forward_out).to(image.dtype) + image_features.append(image_feature) + else: + image_forward_outs = self.vision_tower(images.to(device=self.device, dtype=self.dtype), output_hidden_states=True) + image_features = self.feature_select(image_forward_outs).to(images.dtype) + + return image_features + + @property + def dummy_feature(self): + return torch.zeros(1, self.hidden_size, device=self.device, dtype=self.dtype) + + @property + def dtype(self): + return self.vision_tower.dtype + + @property + def device(self): + return self.vision_tower.device + + @property + def config(self): + if self.is_loaded: + return self.vision_tower.config + else: + return self.cfg_only + + @property + def hidden_size(self): + _hidden_size = self.config.hidden_size + if "slicefour" in self.select_feature: + _hidden_size *= 4 + if "slice_m25811_f6" in self.select_feature: + _hidden_size *= 5 + return _hidden_size + + @property + def num_patches_per_side(self): + return self.config.image_size // self.config.patch_size + + @property + def num_patches(self): + _num_patches = (self.config.image_size // self.config.patch_size) ** 2 + if "cls_patch" in self.select_feature: + _num_patches += 1 + return _num_patches + + @property + def image_size(self): + return self.config.image_size + + +class CLIPVisionTowerS2(CLIPVisionTower): + def __init__(self, vision_tower, args, delay_load=False): + + self.s2_scales = getattr(args, "s2_scales", "336,672,1008") + self.s2_scales = list(map(int, self.s2_scales.split(","))) + self.s2_scales.sort() + self.s2_split_size = self.s2_scales[0] + self.s2_image_size = self.s2_scales[-1] + + super().__init__(vision_tower, args, delay_load) + + # change resize/crop size in preprocessing to the largest image size in s2_scale + if not delay_load or getattr(args, "unfreeze_mm_vision_tower", False): + self.image_processor.size["shortest_edge"] = self.s2_image_size + self.image_processor.crop_size["height"] = self.image_processor.crop_size["width"] = self.s2_image_size + + def load_model(self, device_map=None): + if self.is_loaded: + rank0_print("{} is already loaded, `load_model` called again, skipping.".format(self.vision_tower_name)) + return + + self.image_processor = CLIPImageProcessor.from_pretrained(self.vision_tower_name) + self.vision_tower = CLIPVisionModel.from_pretrained(self.vision_tower_name, device_map=device_map) + self.vision_tower.requires_grad_(False) + + self.image_processor.size["shortest_edge"] = self.s2_image_size + self.image_processor.crop_size["height"] = self.image_processor.crop_size["width"] = self.s2_image_size + + self.is_loaded = True + + def forward_feature(self, images): + image_forward_outs = self.vision_tower(images.to(device=self.device, dtype=self.dtype), output_hidden_states=True) + image_features = self.feature_select(image_forward_outs).to(images.dtype) + return image_features + + def forward(self, images): + if type(images) is list: + image_features = [] + for image in images: + image_feature = multiscale_forward(self.forward_feature, image.unsqueeze(0), img_sizes=self.s2_scales, max_split_size=self.s2_split_size, split_forward=True) + image_features.append(image_feature) + else: + image_features = multiscale_forward(self.forward_feature, images, img_sizes=self.s2_scales, max_split_size=self.s2_split_size, split_forward=True) + + return image_features + + @property + def hidden_size(self): + return self.config.hidden_size * len(self.s2_scales) diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/__init__.py b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/__init__.py new file mode 100644 index 0000000..ede6900 --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/__init__.py @@ -0,0 +1,9 @@ +from .constants import OPENAI_DATASET_MEAN, OPENAI_DATASET_STD +from .factory import create_model, create_model_and_transforms, create_model_from_pretrained, get_tokenizer +from .factory import list_models, add_model_config, get_model_config, load_checkpoint +from .loss import ClipLoss +from .model import CLIP, CustomCLIP, CLIPTextCfg, CLIPVisionCfg, convert_weights_to_lp, convert_weights_to_fp16, trace_model, get_cast_dtype +from .openai import load_openai_model, list_openai_models +from .pretrained import list_pretrained, list_pretrained_models_by_tag, list_pretrained_tags_by_model, get_pretrained_url, download_pretrained_from_url, is_pretrained_cfg, get_pretrained_cfg, download_pretrained +from .tokenizer import SimpleTokenizer, tokenize +from .transform import image_transform diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/bpe_simple_vocab_16e6.txt.gz b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/bpe_simple_vocab_16e6.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..7b5088a527f720063f044eb928eee315f63b2fc0 GIT binary patch literal 1356917 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%0D-8anuzg!0hrX~L}M*$l~SOpZ7=I+%ag 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%0A|F7fvQOLDQFPf3 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

Rp5@H-63rPkl?5M!`%e$q5M^(XmdeOwiW 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%0D+{l&4Elf+RUJ>DPlT<0vI!uxp-LitG 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

nj;X-C@gua07)qPBmS6kxKbsrp_1VbHQ%kr&^ z!k7u#?j%tu`H)R;4-JZO%2!$>0QKq&&?l(kFwOyPi2NUpf%zN7>I!<1?&7DVpkI{@ zmL_`FL^hDUgy{TA_m8lC`Sad3+E!0evYC5APM}? zn}5A`nP9%7s`jCld`%y;mWtGPnVBfxz%iS`08PY`1H7$<}!~CprTo z)#v>%DxgFCoTl9s)+Irb`hTaxwGxB*h@QgTc!zbtluM2V%bXNRZ`ntM4b$WFoRne0 zu3kt3yDfkl_t2DDc$ybS3Gak--iSSyr3B_e@?}j%xmv))(D`GWy!1vK8^N^o{WU-s zXIFuh2%`yi#eqI=PqppK9Vz+~Ezk_d+Ld0gnrYe?kc7?;t~|UeX?S z|M>>D?eFxjX*!T2C zQa|i{iG&hks#!K_dzy7zy*oq_SxwX~CZU~!<_+5D?-AbZQ%ivE)Kb`hijWQ(?clbL zX30qBI8!{ARUTXe11G6x3>YoDQplL?PZUlT1-qm!?d+ZOy(ZoV`}v=Tw?CfbrhZo8 z-4EnQK(MD@xU-lV#Z7dTPtftrL0a^28qc9WG*w7M$xsyd zaS&^bx<4OLLL}t0L+~H@LknnZ>I@qSB=+L!-{|eSLDs}}P6csZZbwyU%wN=yy-Yo{ z4hFeQYbfWCqoRW2+iocm)DQOlj23ajZ@A4Go{Vjk6eWhom2lH2*-+LbW}1gladIRH zIzBH@nL=-iTM3=}n&}N}0dsR<@VF))5*C12<=!(qw2|3hgY%+gsogE~<_k1hUC`ow z-!Rp{?8UE?6xGqOXI_%j*SCRy&NGu#ggn#G5#J*uFUGo!vB+V}S~ zIQf~5GCKOW{^DFT9mlD|5M`(ex21&>WV;XSUI+ifGoVne0(E|t9S?+@tqy#^ zP`Nb|Fs!zNCHp7!2NOWr(Jl@)j;IB0(Z-113Ur&|*^g(GOWu;%y%n)NSam9N97&V9 zELjR!dv?4eTv!tg=O}yA znfMznpj&qs2SxM(uJ~)1+LUYQm#<&{rOxR4@4fyqy#AW6f5F!e+4-|qp?kw*t4->= zBiHX1u2f}sw_~tmw>-dyNwy&I9g@}-%~q>8A1-nYMtpt|z*q&L#n0O?DHNsjHHLUp zbr>6|qvUVd=0xMqpW?El^L`+1bm4C5Qo&Fo&}HnDrK(a+gR2HF;~kUz*3-|$P(dqH zJMD-%^ugelst+jR9-bz*wn7f#AS64yY}cXpr3mQZt6N~ z(hUZX&8^2BU+2>C9@)N|@RT5b+mRh0fpVCYV<4$AHOaIkBv+f%tKPIqJ<56Y3}2Yk zU>-YCcu|cu$3)KsS>*w>yC+SsNWF4}Z*nT6h=-}fYK`Q&ha@280>ms$j20cY1!6d)A~W z$bOU!q(~t~T=mr9JQbj&VJFELn*#cT?{t(-?(u+gBmox64-4#sd^@hO&&YG{)E+QB zuzTn(r@o&a4UV*9MwI;86Y;pJzr5X)Pm^4!6>a^pGL@@e!|Ke#PS1NV6`oE2~>eIY-JzS{O@+@G?sJcIQ@?FPHm0r#569ynaeZn2m zQ9=DI^_RU&>^xl$VxUhLUw3k-fgHDf{g~B^^Kzn(H*v&QtQgOD8g@n$)qIp_?hxL0 zTsg9sS;gsokXCDywd|R2y)Qk++#O{#gQdE4xYVZ%m{VnfR<7HLK_htm#Ygg==);@? z#3$h&EeN1vxaOinHlpnQhf0kb_)q89TOqXGc}HwWy5#`_#*!Ky4-~cW(7&A5hrDA> ze`nR4$mctx{l+n<(sW!}Nrj1qNI6p3*;kBFR+~Vys$a(}N-pI61WWG5TS$iv{9@1t zDqc6}7)0nHx$fMEZ@vHY?RT%AzW?-3|NQpL@cz@+-@SeP{->|MrnA(?Z(oyQ{rT%3 zauAKrp|YFyB{Y^7r3DV71mAA>>77%1=hk+01td;fL4pG`xTwfa^@dry`Trao1KarS zoy)l9|+AvKvUwXfTt$iD$tlGmUooL8P{$J%Ly0fQ^# z$dgi4$f_`@0s-BFzJt>fI+l_}D}|eHTcWcX4{UKDgdW6;)q7@Vxqz8J_ny{4NjMN$ z(}ZSm#XOxMqGYQjx<@IOFd*v_*)QV-WG2U-j~8?US8}zLgT29;`^RrmAphv?Ya6QG zB_>)&=q7JJM_>3vxd_uXK}X#6ST@M4u#>F4TiHcw;@Df1s$e|YMs~9?hp{>AGw(l1 z6%54=NQY~Ljgm&ElIp_q8ZFn0d}y?405#|(8#|bYf=43f7!Wl$WM~*gpGCG7(P|G} zv0G*Fqn)d|wM0Ul!+kwM#PRl4svD@OI z+I4%i*~cf7?9fhefU{2!$h+EMc>{b?A1}^C0Fa|OP#%yFmAm=V zTp_o@zK~WeOG;!##T#;pqT#$tfmyMcBuV-cQee}bAp5F)%-SO+)VOs~zkEr$0@7s% zn`6So-$_orCjyKcZK~laLfwOYe+ws2q9CR_0|yXAUH(Ha+c1*65(0#glsTOIRrq@~ zNR3uutH;F|jgmzT7aE?y7s*)c3ut|+W+2F|gEmXCEvO0c4)_KaWs$^c+={QVMJPqW z+go$Wr~Eouo>Q-_$cctD5_dLlE{Ah`fVqM0$pYHRLbyX9kfy8L{by?0OAJz*%xW7pJ_Wr6JHpmr z2m>w-v5rGlN%didHXP7zkPF_+5;<$Ny;bz2R>Isd<-hXDrfx`^ z3v8pszk=H%37-D!mG?>@c~Cy&UCFM7JzT6~w{)vQn(Z)wz(M8HB}s%?OMy3Q_hlpe zc+Spaq?|}y_-xx!QOPFE3?BtP<{l3AClzSU^pCvTG(~D*N-i0v+{9b~RbrG6MM;Dm zqG&dyI)l{bW*E+wOE|F~poOJ20q<0eM&u@ShZda@qlpWyvMK}>E!GP8VBvxd9}H|H zeqqgg$=-kG^~>-rBwDW@V_x`#p*HOxgQYp^e#8k6j}v&Jfx8X#xiDR!9Y#>#hzW)D zw}*~BE-kIrJ|(*Tc5Bf?NC&_sh_12f8IQrw4MY=am@m zR_TmW=>!Z=J-l^It~0>{lOS&kq7Z!@jOOEnHsc8Zd>a#BBD$7hvy5|;^lG^fu$>la z(Z0094XB(|SmmZuG5tSUC+^-|K|Wzd9hVOpU6|)T93unH*JN)>Pf_PbE@*L5a^S*u zoiKKGdkHyMlsiY4>uF)c0qJRw!Zp)Q8F(a%F=`SCURTNa>vp=Z`PNHi-l0j(lX!vY zteri`$Z%4XV-Z4;7XxQ0Ib2P+p87^DWMEWNoF1Z$hT*9!NpfzCq8s(x8BP`j5>1fL zpJ#eHHy0BDog21i(AcY-R3Z-h9$F(U@M1A znOVyj|9N=(Asmy|9iGe2)soow4%z0xeY6M@U_)c74#9}aWRgD zP4NdV#j5Mw*}wz57oWjFborJ6rQDJ9OKah}oU-GJRC}1q$G)LL5;{n>FI6nqlQ!6R zE~b?TVKEc^mM7CI2g%xD^)5%eGxseOsizqy)#<~Zf8)=;&R4FKuZ(!G`~ViHiq;B~ z2OqA}kS)1Q(Nx97$xU)wNpm2PA96=lMns5IGLl>mK#C^FaaHZjME z%kaoK5jW$6$N%kbe>;5R8{a6q?=v~;*|+okzrXz={OP};ees82_`DZioI6k$D&;UD zWtPKTtA8;KBWnFvRuHI`$VhlS^6i0EtqVj!r?DaDf`IIK2VWMDC`d`Tyy=MT3YY5# zqwy2GL&CH1JedpVVy}uAO^y)8EQkc(wio1i-IE13oc@QI(w?x$I z{@`$$E3EMLYa5A&x6eQZw=OHMuPTa3Chua*^8=!jB`2SMcC4e_#zd zDM0)n-6Itpc&t<$?lj+TQonRlSwq%LRecV#rBRl}y=HNn$CTB6t*Z^?pu={7JfYpD z03@@uCzoR3`W9mALVuXKeW1T7?+G()V_%Lc+mn~9tI>v2&#`D}h(ugTW}fYJT=#=* zmqa`qppEw}X4VK|PZ8m1INY8a%8KX{UuqVIgkKpG3ktb+WwVuhu-bHr;)5&3}fIp0mz*L-xE2_muHtMns4N}bP109&&adR&< zY&_?c2JzUG^2{WaxY~GJX9bkGfF0OiP5w5b!)-*DPUS=L>!AKrIShbi*PT>h(q?Nr z9RH#OQiF2<_e2u>VU7oltZ{NhvZ|cO3RrPl+Y!lvgu%^f_-RL@^~xO!9U1vhF18FF z^d0HMLme8uq>^fUM7~C394sNH1GlW}eJb51HK4a*Xom} zrO-N4pYh+}&%aUj;19#wC#U!S^7{4re+jR@Ki!9;!p$9n6^pHs@K8w`h(TyM%csKx zIniiOE{ij5zTw3SWf%-pB=>`hmD+{uMFG&I4u(3zv*?M~A^nk@XSD5yrpMjrDjm98 zb%ofuzg)lzH5ZNHWHlAcv`^d^BE|q>1n-Xgv7}XbGC`VQY zvea_UOQd~a>%+;KP#KK+ESth8Mn3;F0jN*e5FlVXlKGY9mG~jn=6I^PM<0>R zkZH{0Ease29*(dTbETv)IGyT=ki3hsJ|-WJU6M)*_h_@IeIyQapVwLEy?6crA3=0uBI@IJhhgK`)Pp}Q-6|dnDwz- zEOJ;8KCB&%vLoF}(gQ`5QDxciyZqbDBb;gQ46g{*lG=~3Y@)`{EiWKw;025m@9Ie^ zL3^eoRB*NHy|Lk?TBCHxs}gb65Yv%QvPn4Hjd-?hAQ+&%#t9967^law6$9lEW&1Pu z&7E%HNTt8nr~-AqFJujO2y;EzZtr&hP|HvOlCiB1^!gMpyHB}#a7XpkQ*@<%z_t#Q zQmuv;xqrCgu#Rq>%NvrLXiIX{r&{yv>T$}8@ow6hXl?*n?U8<_qO$m>q;_od1K#Hd zkQ#Jj8^FtwU3ry1!nL2Oy{1a8C0Kv@XC1prF_UFdUm}HIw=CA%_ws7adRa9=vE3ly z?y^K1n<4|Vja}`Q9lhJp_F4IXagmP{e4Z3+X9)QL61T0bGBE`x1tB#AE6}{To_2H> z5H5`XBnh+04B0q9wv4Q@_NXt^M}=Cc#kDhlo*)csqgl;*@&kw%hs!E�J#6HekB- zpg%|cf)oM(=xr%!!i7-`+pzr^`1nibBx6WkgjloUQrpJXl`#~unjel1mO52kY9`EQ zTZQ0W2!w;_eBg!m6BwyJFotQt?XT4bGwfF_q-alz_%cud8s3gpM!~c|ev|TIlC!*e zH+2FTgo6Z$8IOIn*!gZRGV=4HZNK%;26fu5WPT=3)sirbt7P-+L>Wx#4(V0Njl4}D zB3ZRvz&_Tt-j$t4?E~ADu5jlC-S;A!A;o``)6+2+Y57|0fpIYh)p_TzyeLM(tZkT} zi#5yLrvm$rI9?~x%Hmo7GU`bE`kQ~NBBt6~e)}!_r^4%xPEa8Y?vHne(vVY2t04Y;8+yHR~Z(2qh<^BX{Mi&T{y%Xd34 zN9?OYFf+ddYw?ym-{|*kI@m~dt@CCj12@7A6mD>zTY9*aUT*{YB4<9!QpE7~Wd@W(T=qw3Eo7bf z?P13>bCJPqOk>o>ooaS^6Z`J)=7en!fWh|txso8S@=n&4#v*qTk6;?DJX>C^I;KDG zt|0`knQ*7(aHVqSKyNIde0xJb!Cd{k6FOJz4OG-PU{nLt= z_V2^^Me2$6D;;_uCDxAA?+@7f$AlSM4m%)r+a&bfhIh7Y;Q|BYcUd%v8y8AtY;u<@ zti%AK=^@`@X>wg1kJfy~IDNoQW6n|=QCse507KB%c~ZR516=BiJc_WgCPNiMUv9w# ze!0{*EsUd{+-A{Q?FHPVfe0F6=rOUy5V zuj@73PPOFFRmGcMVmoHM5-RGWD*gNKASnM6%9r08sRSz>XZTz%WRJ>5h7@Ph6I{1$ zj&K1Wf^)%ndW@ckfFmCY@I3EJr8(CAB_trW-sz^7Fxz(Q;|-G>d+mHeyby9DFU(do(pC*&uj=jbK)Ng`q+5>#|q8Aw( zjX?jRvZ0r6T80B58xn@rlLalBC{#&oi^N{NOmJmXV#9Qn$b%tpT1`w72HmLGTyO}A zF#hW*MK@c%41ajr_3n3|S>tcR>+i6g0h@`Vd{Fz)`gX-9hvo)J6C6DgOd#8r|$AuQB1ZFFg zo7`@Dk&?bf*4W^IQ9wbq66;|64R|FJw~9xX?_iY+mCWzT3n-~7TK=Td14>*^Q^n64 zCQGt2gWFqAy((3+VO@pMdU@1e`=@^j|B%m80nh2Eu<|*-%Br00l&k|v4c7#~NKTDf zHXjbkMb#GXp&1o`6J%F5RDo0;)VBTsbf2t_)Je~>GST^cm^#>5P)-;ik12bA%i*nv z_L4+y^`67ow8m&Zqm4Vpeq?MeIHoZYkBQ()?K-u>5GN{GDkIP(2*rnLM(PE z#qHnp^$TK}1uo!>E&oyu(WrX0<2n<=jmsQB45s7zU0Wbb8g+cZxj^3psTVVqtaAG0 z!bTSKgXBY3r)lhARml7XF1er-@Z}qZIda%1;Rg1a+53>ddS;&zeJJjWW*@PR0QCMghX9YRD^Y!kmi$t6aRdPl4oK=Vfk_VSe2 z^(+#MP7{?9ush1?4*-2rhiYHcWtI6HgwP6vDLwCs7Nxj@s@*t|MK*@c%ko#@LQND^ za>>3#IB1BKx4$3*=7V`GjlYj@_&ITXc>7rI>%A_7zewpQL%}Z z@((-uaMWVbh8^crJsicx@zkmyJxx&5_i&Lr(!K_at@?rQJsaWBaK>;n+KrA<8<#+ zb1Bn-_a93jG&YcwyuZqBJTF>`$o*Ma>wa+&s-2@LxwZF74NE3tDkx9gBB83)jX(w< zsex+xT4XuFbf-b$io@c5oPZXbdzVT8Nr+HD03Y)!U^&J>Yn2J33Nj!%>>Sj1SEM>< z(gF#u0BgzeX$8EN4!9h5u+IXpGqhkH9XF>MlUB`5*WyqPjWh_19a67)2bYYj%{%}o zB7tz}YE)^vl%RIT5NK%IdviH=v zz|hliPJx4Ar47Cyi?x>3O`h^C;ml4gEs!b5vhE2H<8#|LNDb`}!|k(4^yR)FksnD@ znsmd_z;Xs4gV@2V-k7o#ynr90)Ja_!I2yue&bAhT<47IH54e7EkDa~K4z1OC+gEu= zOx&S_5^BGMI!7G0+OBcQXn9Zy>D|cTQ55f9=t$=h-Im z5W~lBpNH39ekjfG14uT=zs%_Q4hTYyC^)_r46c$!z)i6GGoL*rp6#+tA0Z{E2=o3{ zJD_uSz=rLxl^I>%qE8!;}{+I;v~#j%XCHVkh+GtdNzrX`3CPBZmVi zvM|#&r%?p;eP#T1KjZ5HRF}1&&_QM+Ufw}_aJaJJN9Ls33x zs=0!s^S)G_l7y^L1chSuLERZ(gg}6G2BdoGhOP(Ln&>L*?cUDuQ{2^_*RTTXL1z<^ zY~;JNi>fg*@Xc zwmy^TwT31H-3C-w!g2>VD>q5oc%&}MQ~wgIPG{2B)Hu^=9wap_E`iG;w=6o0qj&CS zd@qIRe0tjD6oLzkl2TSj>XHRT+{SKBX4jmgplB%J2mv)k)sOMiKqcx%@Ob5q2iPKo z7QsX`f2WV#a+@BmYF2Bifv$~#ASxeHBf@v5=(5M(%6JDb18Q7w#J0s+?`4lh@g5iiB!o1J(Dh?14rX&o!#d@`01y$LSI)Kc5o;@0mP|dC z3Xf3x1qz21eTqZBqeEaS%=fTKG=?HNTPItKsh8BHrELh&`h8Xe?BT*Zkh+z3R3gZ* zqx4X5fO18TH~3=bb?cfwdnU0)mI{6_f&<$k3UtoF5eNJN5J8-%DLUMG=MXrhc4UF#r0woLsjAc>6)FZDORimPuNt`EsqmN@+k)tiQP)w$)3EJ(NjNUEU0*fl z8IlKI4rmJ}=0wHI$~F2h?w8zjM}%lKm3aU_m6AL~P7eR8{V)7i{qi6FA^gMXo8JuI z{AON9<@uWxAWtuRNYUlkviqII@D1%l%8Z8do{`vxjTxO5fuyZ-jR3Istz8&uGzMiU zhfVUsn}t(FaAcPZgSb4UI6AaV6-4buI$Or2>1zM@U~ZPJK!N7Dqqm9i9ZwxTh<&g&mWX7N`4l|pa-&HlBGclQG$l~mRXUr z1D1`MGH{@SQwBOZ1EI5U&%gc!b6i_#D$~EF<;EWY-a39u*E>8z-9OQGgOuE=J$Dx`_k}`D z3fYVO%x}dF58#Qtx+mILqRB{!sSai{SDemM&UOr}3o@D`VN`0{#9CA+ZS0CZFbh=7 z?oBf6tlGS>rxGP}kZROEL;yqbvp&_N`CzcD9?jU;wW~{v5sa@4!{OOejt^x&;F+Wy z!u6rH*qkE{59o&P{4oCz{txO`|LiZ-0q?Qj(4ZiPamS?7j5>jekegMd1-$cyr)Fvj zk^s5v*`lbPP62_EJhH_)4S)gic9>zGB)cJkeQ)(u8BO8lrJVg0tOpBkP{oEPkm_5k zt#N!i`H$~XT8(-8)LuYs$EI%GDp${zbcMTN;0+Q1vZ|C!;>Ik^P47$qK+aI<->l@X zD5deAk5Wrty#AERL4Q2N1$JkD@%B@6^s^Nt(Py^-kvW}i-OE8XSUy0wxgFWQg_~wH zXh=r4M#Q;3GY?vo<7C(N6#x;H*30XhT^A<(&8v$lV+hY;48OWo@H5tq?HeiBn$s0_ z)^t=;<3);RIbji|x|kQpvyls|-l;Tgb}PLUkYqH*;TqP<2Y_}EfnT)7Il;D%bC<(o*yi=o!wk#?ZuaF| z9tqT<`^RbJEDI$CI+L9X+w^nyLG~rb3EZMvfo6EArWjjQH!A*p88~ zwx6L_S4a!)dkow?r)>{z}2CjU48_WDIIO>=0=7(5lH*<`gP zFlM`BF8(~>asgYG{v^P45Y`-F18#qSxN;>A43>$8s&F-ng?34>D(T4*LkWF=kq6k! z*u-tSp*-+HIS-h8L^Qlsxk@f;M3qgHv4f z8DW&}Q1E(@DiQuLvGm;Pt0I~`vD;PF<)lTEJt5>qInP-RL%2VLx8K^i`O`=8+F*hO zFY9o|n+`pR?T~P)MRS7zGN!i^g4lR$v!WgA2?Wc`6^Gf!_1LSGcaPfTq!W8KwSg9L z*20KM3)`b%?X%P2nSw1>Nsrv0wA;2atAqc9oIe+4mu$eU{B8|6o|Hb5g1)=3<<}s* zemK`qg%g5?TAieiuB}%Csi01iP`Em2f;vJdT5XOWBtBxy=>&=!`<{CZDHV=7Fqi{h zA%6w&l6cxQ#aWYf5}7&67B@}$ImxY0G#m^E)P|14x+-@Q^A&+rTiNC4RppF`O{oU% z6Ah!V@GfTEv#EfF9ESzx1;U5{6<=}><Hao$rm|9lxcfGCo?ai+Hd5QbBYM@mv*=61oFYCk$?|4JWSXc-RPUMqLa`me^wH* zX)(*}_|!Gw3GLgw{~)lI*R#vd5DP0A$>a1STMXXni6#fyRXJgcpeT|@NWY4>901y& z&%*Mv?a{1s#&k(1|@szXEjQ^{vc%127P zPJ3e*6}zC6Lde&Y3oCMX!qxPGkv3M5;JR~v@b^uclpYA^2$aT9lu!2D9mZ&%zx_KZ zmXF_lj*j8m4+uh6_k%xul>bewC(PU4K0YQ2pS@*`&%4@DRBBMy4{R}LRd-r(quZ$m zls{!D@BEGKG8R1k(n;F1EoK%f75h8PB_tdso^VwA>;qtm(@U|7&}jj(gBj1&%ikv{ z=Iy!(R7AwBFrCy}CU-1!>>m4@C&$YL$8M*kTxh8KB&X==y4-*+Eo*y|Vv#Ypf%-Zi zVygSZu5Cvyxv^`uWa$7Pe(OR8k!04&N%YoF6-G|3!J)DA6{2=XZu1wFPT&)w!RnZ7 z{jicE%PrRt+Z4Qaf^ie{aNu9qI4DmU10@fYD6dn<6W$)|2Z(W!jdVs|8V|=N05hgO z9bY&0-El&u7cYC#ItbFPNcAK9kN5F{_AXH@K$!Mg?$@C8dG-V7N`!AQbfKq43ek%v zR7p09n&KQ>qmv)~X7_@$lba6&04m{heIseWs^cX_ZakCGJ5jK3?5WzGpHxAWEiS8y z!Krezh&dg{-J`}s{ul6%yy%0qA<#Gm(#Y~9#h7T#2w-QEOwAO7W9XpQq8rC+Kr)0|Miq0lBs=(fpH7?N{xwLFe0@>|K3XO6zZw2Tx^{i@A>MmUpZiekZ?;=A3AR8Wbgivz8%gXdiKYvV%sJxSE1-svjjCG+jNNNrI4TbmuM%bjgcF z_d-b)4t=du5|Y7jD%cfC?(p)4M?;OR7W_Hs z7xmTlnw#0JFyh)y8GB?qPtFB^F4myHkl4(kXN%24CyUuo8fcs`1SH^w-tNF(_v%$= z*A1qf9KPTgTL7DTEpFV!Y(zrr#p42;alsh$5XeW1h5%N*n|~~&;|iB&??!`n!!Ats60DE2ss@T}9oend*a9HW>@m*P zU7jPPs3LuRp~JrN!1lq}KM<)Pi8-aBbbd`cka0@>*-eIaN)L2L4dB*lrYL*-%+>GZ ztG#2l0$nk-pfP}yQ)i>u=~9@44YDTQ$~$KdCc0{gdSfcVY{2DJ{?m5?Uzozz>rVn- zXa^>|eVKLEzC7xzy~OMF9v*#@fKjkX0$7)P@npt@hzoH*s)gPPWYcsKMutL1?OjYi zX10-r4()NDk7-%yRlMFVCZVoUX-Vpis9Xg30k$7hz05U;CNRxVv(V#5-h$DB?=vQJKxyF#3r*BN}a$?82qmP|R#NqEn+gmouhR|zoG|tS2Q6DPFMCcnSjjO4D|NNOU1vtz@(9@;^t86&U9Q6puRGrJye z)-$S1&9;_QFnWVBSq|c!J@42)U#HP|lIn|ziKaxqUU8KYE^ZJ`%?)NH~txS4!l@RIE@B$osbcZj=MqTV9Vw?OHozsg|9vP&ngB zf~&;`zlc?FLK2B#k}ynmO@t+w^}F)%?j;ATZw3mkCp75s?3A!15$VC0D_7*eEs3Vt zK&@}d`3-jDVmIFof0HF3zV)qdg>UDVe;fWPJ9oVQ;oI+CrS|>d+t=n(ahJ<~J*zh2 zNSX~e!1%<^47_qzIZ#0b!uJ^nnMSWAe*~*+hz%Zo5BL5AJKyT!aGSLI@+8mAs5h(Y zFGCG$1KCUtTuJ6M^U_mnUnYmEO>CUYU8){-giT1T`TnEVPsp2v7Fs6-5G8Kghj*S^ zaDU}DFkRaTTgJduy+vonZ;1lN0!k{Ij|xVf;Ea<)xi<=KEsmb7J2^b{}}!{Kad9?_6V)*bXKP-c%hClFmHq`hirLwgTOl4 zOQt8o)HrYqTvYaR`iF{7a>R#eb+P*dIx?&L8x*}8ve@y%Y?SiudrGo*BzF%(UGGan zP+o#eSqmosbiM61l=Q_PFjk+v;zsp{Y}FEQSAo%Sa0MU4bTY_pXxkbQhpPoBd2Y2kNGR zXF`1Jl~VmhD;*1h=$7E}=w7fT zsB3$b{Pk?jtEbMPZz_^>!L4oTj7Y9Qa34EIWR~GS3Ni!E%F;Ac671EC*7YL3|N2AI z>bx(ioz0f-n8QE$gUD&k9fUKJL;$Hdc{==RG3;w&-?`@Zzl*ET9C~sc{$+k{u(s`2eAs zYTTZeSfMzqscNLK#~-rNIedWu59{IuYROxM)v>jsK3-@OSSRZy0c&|X$h%}AMkq{T-&`_xz_aOg+?h=oYar`lC+lVJEn@gq9=9G zw4SalNOXBGbpuk4sSvw#zPu%iMy<-f+six?;b=o@aJe)MPJ_t}Hq!z2q3$&5EWKJS4t}Op?90AFMQPQRvjGEvh3`~hnbs(`2y^qG#>yd*!R+TXY5HSaJFM#(4OvWgC`_8l_gL0#<6N%v=v(W7y&Vo1MsK91u;ww z_($grA9kI)(iTG{BiRk+G%)O{sEa+S6G0}K8;H?Ppkkh;m7n$jj~7{RLv0RJYPe3W za^C?*5>~&XTUE%C#3fDS1ww9dqa796+Ides)>9kK204|8^+LpX6Z{BY6lYQQFl)%5fl?7q>#D0Nr@9AP9xe%@b3i7XEO0=5p>U8n2pnUAsnUXN1K;gVuZ6 zEmRKkTGVpc-g#OF%g{1%u4exye3&c4WG(2-oXHKcA*y#PDuo1t{C%gIQH!ob@$bUeXgq z9ML&gv<|gYr_%!5+4XV_BWx$DQ`F$Pt0i?mvpKGUKd-qP^8Wt~fO##@J|;jz_C4G} zyb*Bgs$`mmx2Q7FAoEd(S7`&h=;cA)h(UoGK%6ySw0>1LZHEsxpZgu1^9 zb!maf+aqTH?*&%WD)S#nVU#5^vOflQ_Jqlbf>T^71H54!U<^?Y+G?%0@}B4@Yip!5B_B9fU7r zG53*T^7u_!irYStJ&mBt22ti5a3(Z{1={>`8&3%eC^iC`hAI|s_}c5Gx_>m*5a;SG z#!-RhR zat6oJf%72nT{QRAKU(Y>lgmc3CohJE9WGLxe`)-qNk?)ca~909ad)%axk3O!wxOKi zxSe$B-`nv#1BhjTv4deVhjM5{A-UPr%D7__ASBc9tGWs{>n0UO{S7xd&gLVQZ`~o* zoDYGXfzVY?`R}3EFN|UdVY1>YlF9;w#FwCtIyMa!|?$p8F{;t~TcPL25Qw z2Y382%@Rv`(Ew0JhgEz;8r5hd!1{lDKcTy=-Q)ia!{eAyu|v@qHmf`sl3fbc#s*Jj^SrOa`cF@reX{hYXkMY)7c(JEG zfc`3#3JbZZOvvSxaR#O`1-M2&ji(1758aiBW*~jedIu^Q)^xE|ZJ;H1`HTkYdeW{~ zhxw(Q=JYJbTfXB{jBR?xSEc+BA0=kc6035bR(yoyQFfpjsAAk#kPlp@k_Vr@SkQg8SvEjTzFkwKW6G7 zn(6WecL`6Fg$Fp_P$%fkWL{O-ze{`yz=ByJfw>I9GV}Tf+Lp5&GL?Dj9JM`w-VDT0 zRI+N!B4F$Yfjc~-b3vOX)#GxT$*WUbq49B}^fWxrL*-`n7d+@~1!86NY#OjpHssIY z*(BuDxNml;pagkE?P7SY5jX*=9vBQm3D6=-eDT{Y&g~@klbyI0%B!|V{x1C8KbC{{ ztJj|&ANt3Khn^|N?5bs>v5kmzonA%u*bU!82bkv%SVQ#Y$z@b6rhHr^n}mOOl=R@H4i$Arry{8_ZBxN|U3o;xd~ArmK+>73x}$3Dh2YSlGOs`PkcE!J{CsC2Qe zP>Az1x~5o>+$b5EEditsSI9+Wm+6(;q%lzku!v93op1VEbUhLYa#eJ7T!j?iT1AUz zaw7{#b;9H{x)s&O3PC*GP!HZKl+uS8uS1`})+Vt*Fz(;2J`RzUrO6O_W!Jg9^aZ zvI`jQ&*2@~`7cx|xL|-Cq59^|TOge9Hlx?&_vx1MDP;>jdA?8yJ3o?^m5zzdk>N9t`MbMOAi`znt?)>4<-UvjM=RX z7L}oH+Xu1`M{lw(-hRP<2foOE`sv&6-hL_n{Nw8v;r-9ve*5+-`RDfy0Py~kw@+Tb z;Gcf__UY>n=v77+*PZVz4DvN({g|m9l^HY@mwjotF#wM4)=84D2pv#KZoASeT?B6<)fHo+YU&<=P-KWa$+iG`Lhvp0?`L6Phy>YR$#IN_KBG@1_q!`v0`O(Ym@G6ZD22Rt*+@Z8 zi(O|@=5d6F6qD)rk_ARaeCi0du8{mjYm}T`DIRh6gG4r-eC%}YHs#U~GZc$xbcDV$ zd^di}P9xr)1Gw+q6NJy2on{Z0!!L$b!Bn62x(UthT{{W;pjURRF;{RPpHu=~vW!K! zL1(gRym)qENDTm$Wq)P0*mC8 zG{Lkgz0G0?fO5c-DogL=+GqG}Ay&B-foJ$OpGh{srAnL}Mwk$>&tEoDX4`bGnpK}_ z8c*>B-7C7N_Y;S2_Xxq*gALEVf!cP^==agCOX%dc^w(2;@LA1z$V zd?tTgAw|f{7#4Suxl|U+i3GW#M5W*kZj&UXl`cF-cxlq$!SbSE9d>Iw@nZlsm zT)ACXGoVnFZ~1prd9732qS=4t3D3?hhZj)^AUUzyW$9#pc`~jN8Aerlbf|G(wQR+weembW3x8C0k%~B=rda`AMotcl%I!%Fbi7kKb3-zDeM9Lvq z+rwwV;DIa0{LEh9rtVo3i`H4{Q0(JyH#X{jidh-@Z?>gys2^oKlMx+m`i0(OcOj-A z5Blx3w%6<^mstX4)fKaJse6=l0Qno6fk7P@U08}t1Nj1Q-{47mcb1PSb%Q*|>3kt? z_KeA@C6sV6Mw5b0s0kxp+m%xCkrD@_vE9TEK$Gmycd+2Ta&iYP(4%#G1^b7oxEbB? zQ0{cs$O`thb$WaS|jYYxxXj2ZE-L5 z6Uhpp&=lks`RiuC@()l;SN2DYdN47kE}*M3PS=Ud1Sw#S!YA=eF3UJubujl7g;{#csnRS^5C1w z1bY>MBw(5xo?Pc?T)Kn4ET7T(R&$UvX;cpY%Wy#|Fe=@{?~Nzi?(qg2od+bI6qsUx zIqLrvmqw$%pu;aqY*RB-w^FxZ$~h-W%|N#nb4KK8vk9iDquTDJ4yvg#GOKH$AB-GtD<|}lB_Qa5ty!$9E-XPvh<-c8MJhk!(9}b5wgy1U=AT{ zru}srvoHhrW`zm{XASH5%iEAbxb3R0$chidt{~o;r&$A+*HQ(({wTZ}H}0WCWZQ!u z`X=mwOW95u`l(-AyIKQaCuP&la&~Xg+B-9=Vo9a1?&G@=y)m%=J%pRBcNlA1>{e2k z{gV2X_eF~-g}oZoBdtg`j~;f^1Zr@Ts8GAh&KlUKfgQ-`_uGKs^yVE!cL|S;P417= zO;ikZds1uo!vO88*N@3$`bfX~*vUpwJSAL))e6dm6j7u}Ed;SZ=75_-$1zz~9pvrD zY+3M%EGa4FFy7+>Z2I7;SB-+K=hiP)WWD@2R6UZC)+yX0wES@oH$a&nEaIO&E|Cn# z`oaK2QJ1Rw!2YVW-wJAM^chd%GRyVR^R=Fosi$W>K-AridIYd)?+Cmag?MOl$M(7i* zj0Xg+SpvVS4b+VqEE)_I6~sbw@=meAt|ovo0c3F#vb#v?!UPLc6GsCy9r6rj@o+k2 zdVva^qUjKL(uOeTZBRdUgz4U5l`*3r*R6vzBBdf$z_7eTg{;%6b_KLGam8n#Kw%O$ zCZb4t=ZB;49>dF&**~%>bRLqDy{JoTZY3f0X+lS>sJv9u*jqdAa!fbN-ZU)ZL@w?c>Ppagh+qxs-~>} z(M>twbvb`>S3V?^xsMR!>S6l98@Qs{U$!=3+KRf!-oASNZp?rjE(UC$xoHea({HRH zgw_YdFR^4sRF~)lx`SpZgcmCN`M~9ui#9l>&HAzmpbKPZR?|_NNNv7+-uwQO*B`z8 zG~|DQ>GQ|%`n4Q2Qy5f6B8Mm{5tNtb-swF_TQx9&?9RxJmbOZ%_m+X`RLj{pa0>gl zzR>fvCo1a-&8QCCnM$tuX!r_luS+;Diow1(;TKLtD9#smCmut)uSFwN8z{~P?tQ+c zyWsUREj9L4o7ajLwP*psUF6={Q=werINf0S+N)h*L)L2_>e*u9qK+a-f|QLWMdT+uVlmf*ewbtrYVS2Vf2pPnRQ72}{`vw1 z4XNOGV@)>ImumIL^8dfqF0H)yT>jrIT2cbi5U4o`Aqb=Phylx5=+P|YkF{wwxBgty zj*^oZ7=}t5M^<)?U{=f4)W&LOcyKhRiq4Ty6*wD7v@TS)kMhzkaF~Aum-#)b(zu*Q zV_T^@yYiA9lu=71>Ods$rYxC+&r+0-z3dz!c0Rs~7r&%6bo{>9(0^XO`^(qguyOud zi@nQ%cqnJ>!G)5Lg+v|-Gd4?~h*FS{dCLaWMeIc$pM8-LmY#@pvYlNe4t>?7iC2S&RYu;-1KK3suxYO4SqEi6K@EmKGNfU5kJoD2R$~FYqPUWMNU? z?XIksClz(SR~B;QzVCHw<_g_%8IUN^a>KB~kS}>4m61p9{|5}%pPxg=^l5^F(?PeP zO(X2iS5@9#S=DT9v7}M=R9f2Xb3s9?No?j$GNvc>V7ATMLd5~7(A=|uIb>4!L6)h+ zu-8NK(0Nk|z{WBQ&{kqyL))NMkmR&8O-q>;ltIt3QyZriaZ(S!s ziiE@oKod`Ra|D1jawu<%7(kvCzxD`YLVC->EG*B?p1hR{FXz0tyOMNIcX~j9w?U{K zCKuEWTrtdAq|#CeR?y%VHlw9(zbx^n2P@BCJ2n?$dV<&XyF%Ix6AQa^;yOX}xd1h4 z&Q1&vu03}}JHj~|(-i`#3t-aPZXgjUYip?aE<|)7<1YJ>U{PgR$ur(T-Y*vmaJXc+ zLN9>|Eoj3`R&@b8X<4u8&8A%4y7wJ28aW(py1CbEv{ch}Fl(uxJYavI__y)oSqAXY z98X-`+41kg|E2$aOdPX4gWqw#g>B&WhiER~hBRX-)#8wzmBdMjV(7bD)|UWOAHCX5 zNae!1M3=HfeH>+t(Ha6^;~qW5y-`l5k&Q(VWg6xd<;3dae<$;P))lKahc%0!`d5+@>kN3H0~LhmZPABp9>AujXV?87@04DNmo8(cdiaD{(Ia<6 z6S&cVO^)C*^@s5b29S1d=*dXXAg4Zgui@8@iF&hj*%(r3rFy%7*De>bN9h`606>Ag z-oO?JzwwRmzvYd*%xWYpQIg6kxL4T>Uq@IJJhJJkp+_&+hIz+=*GwQ%nfF7^!0p9R z>>7$Ig19b47lwpwQZ*}_>$lIBp5G(qDJ-(_TYINsMmutDbZ-ZMS1ew z!cOCa-egz$ZfjrEbXKGeyb+W79~9mDF;+D9N`N_6Cy31GL*Yjf;*?e{o{^Mdhd#bpq~_t-x7FuAp9@5zA~o{w#&D z$t5I}QnlV>4dU}N02eMLkT?5p6*;p zoERNjvPId=Yv}je0iYfGEuK-Pt=Hsr%(*F)6_$WyuL zB2@o@`MCwmC>FH48(xLI1SDSV5(+u;xgsF~_6d+z;*ZbSSd&WJiadt8Es1N2l$7() z!OPqmbgFW?L6&@#Tul@ z4$9(_W>`H3skPFA25%9;d65G+V1-9?!CZCl3|+h3GyoxjZsIvRNOw}K*zT3LY6k=f z`q3d8qI00(5W`W)9iF2s)K!8j+N?!Nd{r$Sz9xSg3aBU<^4MmlgsircN5kC&fVL$| zNsEgnjdWZbItb9GyA*J{AuF#K30MZK0#=48!UbyBMnt8%L!Kp|NDc-=bBm$q7ZU7@ zJEO)lm}+Zh`#cSbIJ-3`&Nmw+eb9n<8lQHT7y&yCB;k&6TTKsTxh_n30YLIu8dzce zAmnM*A1L*!_}3>v|MhR>JKxXW`Niqp^$q)`djm|6XAXc!Ovu*cP3ylE<}Sy^F2Q#@ z6s_#>$!&RwYK7W(NF7xXCZ?XvWH(yNNT%4hFtQq09$2n$lT0qN`u-!SX`!U@JW2Wt zqN3XsTqNz3k1G$VL5{x}W+GK+qREXXSseoeV5(%pNgUFvk0o9krL={q9EE!&=K($RoHP%(Gr z`&KZJCMCD@U}`o)tz@3AlA9_!ZI~$Aat&-746#+b=H<|;g~(^0py?%vIga6?sB}tP zToK|&$q}AZc0AK;Rt(CDr|m3RdMjl=dratR9sGAMk>cZLotUfJo z98fD2jqInjphaqcT6hZ6GJv!A;&BgGeqP^)yP8D9Z$0=#yf3-lk1iSVp5~om=fLi_ zrj7;1G?34dxeZ;`f-6FL+uh$a_q^!6i6qik6+=rg6`jL5%vA2Z$CT0 zJ^KcI%BhTLD@xEn;1d`A3^T~yp-8L72;TG|Qm>|#Ds@2K320*DIgli(#WlN<)$4U{ zH}(^7qLN8Vs+7&@kN&)5R+!aQMkaWz zT0szEQJ)mK5)2e4>o<2Z$4AC_mF&liqkGIlY1#CD9xaLf0Cf2OoUdd`9e<>3dbS*W z$*!e&$&*i&4 z!BB6{Mvd&qWgSkxX(b|h7Yk3s`nG_4F-HR7>_aWvuin1+vu?ttZy4*n|Mc}oZ$D%h z2MD~`?c+SquDL&x)PXr|J#znsD+QO9D)=~5lJ0wirYtT$Nz#`G8rm0y?^vjn#q8zd zKZXDoko=)87Qe@hrhWLZ7HE3Lk=Gc= zZHg&)J^?$QpJ=f?e2Ryf1Mkh^J_j%o=66g<&YaJKLuMn79VXE>Sk?MURwxZKx#)E_ z)(;Yw>(Vy@o{zHhKz4wqHS)6Mw*L)spGA^^$jD;?aQD=?CfEktCG{JDVg2yF?Nm~+ zzamkUi{aUt^tG1k7LaZ#I}*MOoeC*xKh1Jy|Nep5>qme3j^wIs-x-LaE+IZ;=wx3= z@*43ml3cGuokK!IZCa$fbC(eGUqYYbpli;W_28RuRep9QyZIEh*U^A?=6l=gfL63nW z3lIoh-$fEpYL-DUjs2qtaRmQ&OheXGad82TCo65o#wd`CGp-#sCPSLR>$yp#wcS8e1w#*H7Ka0& z#z8Ka2umCHP1r*LJrXn@6^7P>_^7g$Bf@J|9r)I_^Tz%k!H8Iz$jId``R|6sGs+4~ z1&U3k+6OVaShE2kQW$UUB33`vdm@wrQPzY9q*(V>>9_+mDXU4Wgtc{{0$WEX@x#|+ zr*E*L)ZOMKON!x#?(Q?XZKQiDZfIJhQfuhZk-~+_xY&V;bdkMqs-CQftipDee;m52 z5or*Wv;M-(?X%bH;3ie*jb2jsKxJAuwy}?;mgIpudPV=UDLpO8^f`=AAMPlAni&opE zm8W^2dXT7DYZKLu^fNCqWG7vN9Aqbcw93c|-aBt!hri6{enbhjpXP=lTo)j`sR(g{ z?P_yKTcL*>awcb0!Z-;^154j22f^~npLPLCTUsBz6rj+ukZmb`y?B^#7I8H@uq zsQu7;cS|#1VRsHIR7&n#QV|F~5Ou|(0-&o*Gm+ARymoENSJTD9m;}umpM9t|MD=zo z5AVT5CA>uIRJL7%fzG+R_;y0FE5}DQ^~VlZYSJ`zw~GHcutLNY8QE#p0X^Gio!niL zC|IZ-s9uJ+d2rQ(!+})iZ$FcN{^0GCzj#(AYWC^d4_-fg|H+^J`R!NAyEQE&(8We7 zD5~laZHnaD(O%!J zK(e+C4YA7(0~O{%puivpb^(r7B^Wv4TU)2=u1U4e`1c|tjok5OEzFY{CL8u?(SBtz zJwjti?UpTi)%D^(Zcr0To`FlTZn|Vl;~cD@ z$-T7fVcw9bBc-r8CRCm;p6FyHz!3e0ffX?DUZ{TQd4Gr4)#QQWYJl+x@~gICCB=JC z9Q67)=+dh#t&zQ)TyU|@7nlo@QcH|B(%684(V1M?k?zRmeZn(&fLj-oC4^|x17~GK zM34(QoioSoq$2z&!=&8#2*^D5h>P;qovE=SCbnuJYsV0Hitv_0>od`EcO>;0zOUMI zl;%U=v9xcyA8d{yFv9DyUyMOGr&-ElYiA=AT9!xzZ-tix)=M?@&8Fs7JiKbx*FWUQqVYv{XiYpa6nQJIB~q5aJvqN#`H0ep zhkZ+4*fg4ul0Xl%{NnR^rOMc4@;AB5_nUHle-C?idx7}-Q;ol;hsyxn(Vn}bvtl6% ziilIeI=CXgO1#n;B0F8LvbD}r`iW33hfLse6&pJBQhNbmu7fzSY)%K{R}z3N$MezXqg_qvj?gYoE}? z=Ipr#{AewLVs%vGAr|YH0CabUn94%+KUs+9-9FGan8oEjHcTD3 z&*fbKlXB}EAhS!N35IBh!_0Pr>UZrgFZN(S&_hC!!@KS4ZIUDIaam2E3JyFj@s8Ab zKaxUK4~0xEo!GeUq~r=pp6NN|#6Fa`+!M7AmLH=C07>K(+g9sEll%{9qzCH);iqe;hX9=YtOHc6&n;;$tk1e z3HW!~UV%c0s+$`WKycU&zfKPh5c5@PITtNfb<6x{<6~qd4bgp|1MgJl%~;GD=8FF0^lp=H38YA;sVA#W&B#|S9V$s~=`5F|RL|tPSA|EE%z$3I$Z^cF~SfhAo z!pngiz^q=Lvzg+^Q=2BHj0=yx7CBOxTMuZGVA==pjBL@Kxjob*V$(6i1!yoSb_bUX zNocVqT1+ewZ{&1!?G-c-O9=G@{gw=jJM@S3xZ7sgphFx;joiYMoswAxR}v!3O$*jS zo@_{Ts+_WtC!)yk<~&;}x5v`AIjgYqkkm`h43$)>9iMzzkcC9%!IyII?F30hVCnGF-os2_OBwEhSrcoY z$2j z;LbC8%41_vPm19lQYM1X?mGW@on%d@p} zgg@n`?Hi9w234q)mR{swn}yEHEru?n<|oQVHVISmowt&b4`0}nt6Vi1{U)0M=l8M^ zI3Wb~OEQhIcuM2=nUdL5`|SP(Da~7?uIDW*4;0C=`UJnWq2iQqaidn;b8VVR=Z#Ig9A;^r3(xE{hwN$4IzL-taljaF4 z#}jBO{(jI#(OsP#_4Ug2Ub$@3NfrKrsWB3l^}tM(L(BWfVw&z#2R)XDii zyfVKN{*8Wow6+QFUD&MZPZX25turmLZ)&+^=lua)>J{?s9QUYRky0n@i>!(aa^*fp)E% zTwy_$c7+QC^eXz!1aQ!KwTS|2K6nnSo=pR0<+(CCgA97r1R-e34ejZG-pj0_<-kb( z<=1~5_ySD+-@pC9#5nU_r)t^*jc7MSUOQAbrvB|$@b-X@9H2C+Z3^A~Hl4Du*0CN^ zJDer5@LbQhF@XbJ&-wzIVpm&*4o*c&Qo*57{Z>+z{!W(EX&;7C$~81}XEs;lIh^wd zc~YJZhsvO=4t1@Q71!{BNPA#c<4N)kgh(`=`MjU93A0M{a_)cStm({)yzbFl-<(wM zd@kq=Nm*je83013kt$9gQBN$&KYsr8e+X~C%zIcin+vc%WCa4bG#lD5K#)_QW2X8I z2F!*Eoi8=T@z6`OqCM&8r%}kU>_n|GSS2nVUApNCL50fTCUQx8YOY2g7iA*H3N+5x z5cYXlpgOb9Miu<2RW^F=x^98V3cR2KFvs?2+IKpKS#{&**;fWKmOC7c ztUCqZ*lEdSrEiT>w!fAtqj4v5OcD>yty86bj2<#o+{u9XHS0MAcf+R{fUuQR?934c zGO}e89{J3wm7Ft?GA#R`0(+Tin7&%ldzL_j4o^?nw+-x?JtPf7O%5imBJJgOj z8X;}CEW4&Ex2W(n$z&I-IjeCMF0cnFudl3&(dI76!JJ%9H(Q^9oJuDAuAaZl)dqxd zLo}hdP*n|kC$W=BD{Oo0>_RSPhYxD@oP^Jgk{yV4meLIRjljfTE)rs&i!mH%MUw%_ z5a`#9!vGLLl=$tYn8TdB87$j`ri|5MEjfw3?e-GnNF^Ivfg`zqJiMqi)2^80Hn#c- zFC=VR^xqdWv8-jS-2+d**M~d7uy#-Zh3F1i5>Q>71#-mgc%Bkr3*abNn)k5oXpfDk3pFvbf1xX!gi<@XGEnt zi1zI#1_p7=k@|8tiU6XxxaMU4L;JbiA~UI`UA&{BodRzZ0OioR7-oRXsuxaEHcO(K z2#|BTa`Mtvs-t$3JQadFY}EmI9-cBhpqUJxfD#B>4x9t!YOKnSW&k+%>A7X`1J(}6 zo3u!4?A{kMC=7ZJ7H!L9~bJELPa@ ztmxpw2o%TC8Uc^dA}sROvK;b^iP0aGeavpkq>h~VwW3Ouyyw0MH)=x3<3-}t+9WqA zZ-i{K#(OX=tScrjVxWzAMnl^Q3+36WkO}z|10s9=+mEDN0Fdj{Fx>mmVCNU=0|@^E z_3IHjbqbzVK$0bywl4-a?d)DYQ4{S|!s0|(K)JtVKR>0&%MJb#f!Gc1X;dPrQ4AG| zP;hi3wbTX`-kbW2AY;iVvDChgLH^YfoI|bV!^||FdO25S!O$jO)-!G;fRcMO3Tzkm z-Z=voNv`vTL=*JU^OuVkl)h5NQv@)&2H;(;r9Uj;MTe9=&QHSC`^>qhd+Mr?UV5KGbv&> zH7|}VK=D@ou`NsTQ9nW+vbmhVoL<2(9w;zhH{+^#>eavz34rZ9$kS8`PkEp;9C5%R z8Mwpl`od^Mp{#H=`9MLho?Y{&{TVVPE&%+rtDwy)JKbRu?J*5$SB!>}EL%_KI7=He zYtV)QYebX>IWtfaVAHB2bw$G_wBf0l4|&e|q?#e@E#3m>t#T)z8RzC;t?c7+U03p0 zcfhGFt7jtv$b>zUM7=Meat&O=?9=p;t8mZ0055RW41b;1;AeaJh7^ zg_c8axj_{3>w+zF+}#*^QU_83fwP;JgRq>2Fg^$$n!SCUijvN<1~oyL9!*&r(;@)GSq zdv!uL)~&ATIE`M!+s^}EfMfBu#tgUz^>EBjCsE52YG%d-SNjgA)<^-vk^y44Fzh=F z(p+nCh%XJIZSwaEfTe3ogwbR~jXv=jl&bmQQPm+Lo6sSw1W5*MHAC*e!^SLHTW3W_ z_M*~se0)M8Rz`;jJD(IlGm+tW1KQej-!=*A4MTp!2TI5d!3! zQj_ZKl3z+LsbJ2=9@Ffq*}Ia;Tgx4!iawW4vz!1Gv4S;{R^}ZD1nP9Qt1tWnjok=iXN{`=TE3aBO| za>)69q|UM`eEEGRhZ_!!_Y^5R(GKVsvAsQIUvP+5pymN%ZOWzf`gl)mL(zxq$R0(p zbFZKxe2UL*B_c#n9QO!2WI|&>{`~C03|DYegW&Z`f$Pe-Jw|@$6 zrauqW<@^AFoXmfHzstEJ(&2obR9`5+H4#s+7Hsw4#v?!Hs@Pfrw=mV17RY%Lk}wa@ zZ!E&g@&L|0bnAPU;%rA7eYWA_e|-CqJ=uklI#^{sZ|GbSZH0{C)~390=@|{a;*_mc zH2oC05$h|SC5(35J_J%Tp_JE^N~vvLXWUCVk`;e#LONj}=h77rzFw`!*<`bsK`rK{ z#OWv@**lRefIj>*Du1y@4tTf_9p|D|HK}; z24Dc|&u;phQ~)VIW>j-T28?l5#tb6x5{0B;Sx4Fj09}vE?u(Id!e9eowq8qlIbV`C65SXNnoK4W+{)_Q%K+8JU+ALZ6dr^mGmNS5 zbNgP~!V;vqg50XW))^05pq|qSa47lz3|;wl z^LEAsXuHv`Z^q#mG`skqAFCNJ)XVEkXaAE4*}l&kQuYI_z^JxVoi%%~<_ z;xnnqz}CK|d?WX(RH{M6w0Rd@;47NeEBaMZ&f*d#HsJ|6IWK@e@0dzBH;JrAxjG6{ zbO$vLSL>EiLAk3y5VNVUe%PVq-_J!PXi4Xt7%NE#*5Hkf51}D;R(Cf)pYXe^8^;R)GH3PTgG%22z=jc*TC zvbtx~tni#!EfCig`-U+3pgt!=2(2pk_&F-Gk3Yd(pOtf7;F@ixMj@y?_jg@{1*F4O zDn|lLSGYY$B~N73l4^yH71qj! z2k^K-mtQ}HFyj%!tZhXe=|?c7^E-xK(e0#LP6J^(XZQbl53_Q}GBan3SG5jXFEByZ9LygDD=#t({A`La2IqjBLhk6Qq9NmFYwgjJGwb0h8Z$^PcBv zlDrJFyMQ5PIP{oraoErkWU2B^>PrED;3~dj$3YoJ_Y#N<2=944m09_HDnOk(@ z_*_?|B(~!db1!vLu_yNO@&FayYOdXz{1CcAB!NCQATkr_$IAdOJie>3*3K4PRU@P& zCHXF~_D&RNCa2b5)fLyetd~rbNaF&G5o27-1SPOc4`ntqa#w-7n*TLP`YbTP?zWd( zO0A{2FCPV!e@%|uK{u7IDjH!gCfEoW=5%e7VIz4Gi~ zya2VhTObSpB4kZrjmiq>{rWOZ<>?#is=WY;Duoex0X4@*708||n^>?Atz;#lYGYb# z2oCA`_^ouU+^`A6;!FonD7r`qb^P0mDa69!k3>qrzX?1i1GBb!u^Ta?mGy+k&t<>d&} za>CP!$@dwBO5+k$TUH6QJqX^$8KOerV6p+A&a9RZRh05k%W~V67_&probF^22h6HrCOIK zFszBFMyJtX)Ic<|f4+geU%9wPlJ)z--8|R&+CwPAYMwJ7c>ZU{sRGqPZB>W3z|~eB z=G9fgE{VFjl62tCgrjKV5=)Y#?=C&B!)G z7jC6+K;@zz5~De_y?KKTbQA(#nyODxpDm84!)IG&K!O9+aJ@Hp1DhiUrlX zWL^SMX&{+l7{qB!ugqVXcI=^x;3eUFAINu4z|7+8vXJQm(K_${7ryxk5YKyBoWfQ{ z4LyKf#fPGeByk6Le(1ux6I`Rz_0UbO8h|j2;!(En1|`HXP!6DR8j_q78*2d9K?A|8 zUrrMGCvRV>(B$8~{vlYa=`l!jto#-!y~b^-ZPAwtrjXtSHD(crYFsSOYmk1DW(qTb zhN%d8HW8vgoX-~-R&f5G+nj}`i#Ed6>jzsl&7o}uHR-5w(1}`Jwn;fq-p1|J+bW;X z0e7lh1z5MzMui&)s6S_nffc3{ZcF}I!Rv$*x(!u(a5HmDp4N;I;UEY8+u>gvMxDE#Dzz`B^3N$%D1b$J? zD#L{~(F0W&N0NY15MQvwgCwzhfdzzXEqMZ@s3{175JjK7m30qq^sQkHvA>) zvwll<+~5>zTqoz%5ACxfKw3=_>@^qwCcsbypL-8l->Vo6tO*txs6>_J8QaceXRpu) zDrkc8(?DVuSq%IX;#m!ep?p&_jbOEGCI4*DBS-#{M)=SQDIItWZ0@e z4AICuOR`JA`0Nc%HOR%zfb1R`>!AW>7eQM_P;<}41|8`V#)*h z)b2DRr)jlY9uX%<=AyzqQyHM4{2J3WcCqUyiWyZd*J`zR(4tlD{E{?C$erB46_aR< z78WiH9O#QNnIl@TQn`|yOsb_yMG{qxyItTm4*4;AlRqz?{Z)9CtK$1_KLktd4pwQ( z@Y+c>jV8^U;i?D=#Q-0K&$W1_`T4H0HqA5`ojW3=a~&X6nIF44zFNhJCw3@6Czz4g zlw;>37`t2%*cye430x?ALGl??Wz>j}C(Pt{V0REb9;EILo0Ig|K(LCwKQ$5q$-T$j zRz^~iA0HG^Y6*?5`XV>B>DB|Dj^_p6U_ySe2))7@sa*=o*%wLC(u@|+!}fKL$)rsx zk|*jI1j;^a-I#0bAfn0?C}6O7f=<7BQnZIZIAgN?6gi1O3&Li3GZogMc~064a!43q z$ZmgtmSY!VxVe{Cp;l5hAPekN1fO5BT$22Ey0lPH#>^rhEnq9ynwAtr-%W#O z5$qUD!h2V5LUNiP(7v9-eZ}Mm9W+8AHeK?K9X;8nEp*Xb8)!`+P|fBqTs0Gw zZ@I;xh$9iXbIOVh3os!_btFp-(FAajwT?tL78k(5odLOhK|~_lt9yhII0YsTS0zaC zwT_as5d#}BqZ?%j*zI(t@rdzo)a#FzxmuPQvRox0N#p_mXN9K{rY7vW!}e=9#XYd` z1kn=N4(dS)X71xKGWm6Q{bPRj&jhJ|{q{!+i&4_|om}i4j38c;_XdmS(7=2Qah~FA zg#}Wq-Ouql=gx(-q9b^!P`}Z)DiXhj<>8`vxIwH{GpV$aUBQx95CVig5$lpCwKk`7 zk6K>U`}z)!PQ{mY?Bk>Ce`Si>h@`=&&_O?s>1;xF*5Ogx!40_n2KC>Sr?CP$f0+o{ zh=XRlR=`GOOdZDKzj*yY_>1!PN3Wl~Pj+4=30D$%Xg6|Km2~4U0oQoCO>Lc~bC@b@ zF8#bBlv~wCw)MD6j62Is>&*3a!$cj@l;nqQ+81W88Ws?XQa6sA=T+_{8y-ps)9Fj` zX3w_@@atST^j1HcN_b9=NpRjWHhGt@mxgsOk@Vv7hL7o(@VgF(Qa(80Z6Or~2ZONuL#6CuC~ z4pMi29{wA%q51sn`;d+OTn^T+b8Y^>uPS8Ch_z?Dg^O2(%W2W#_^RX6`-DQ!Nz$?w zX8M4Qm_n#9GR4EZWa)5P*6Q))CzY3_Ps^cn6({qtJcKNGr>Fv~|c( zgVY`6go)4F#f@91W=$)bqMpd&*ZrDq_1Ej<*!+2JQ;*khfxx60leggtChGg@W)FLKf4B# z$`n+uDpO1B zCF7}ud66xTLzy3Ua+-lZt5?;z4|$qq4;fw$Y>iGk2RyhONiCBDG`HS>lVGmmgG;f( zg(ooh4ZC_xF{q!hyGUSB*=bhIiz5;y@ViJu1|f{C!s*I+7_55fVL3q43mrNnNTVC- zJWWzqf`M*{sNsbhMdp%AR)NM$qRl`J08AI!Xl}|lI3*540tf}}Pv3qn7oF-cWD6J{ zLrYSIUSJE7(GEqjQ>Jn3<2%0eDtQu3>LDoxm%4c$pXI`o3y$QBIc_-h(|V^p+6;_1 zsY{TCXYlERPlUf2NYpS)9$52E!dq!9X%Lh=iD?;7`mu^5JfcroIh@! z^FS@*QWR-nYWf~8d6DQg7?4kjXlqb{dJGFnY<2nRfpfF4kBI8@>We~v(DQBFvk+=A z)=Pujl{NT3NAKp2C`O{R)N-e?pTBWOLKs`pl>uF}KI|8xzkk@n#8AJ2t^9mK$U!fj zUKR_SUMy})4uJk6Hy~2TMq3RL9c;z4lF|Ioq{||8;TGl+p5itN4%_T@Li#gtY7L8o z6i8)Ox(IKNTETvP%|m2F#rPANiiyf)k-oiZgPM>!ePMEI?jA;{9yT34_^Xyp}Og_!(?CU( z;nxD4YE9@A8c_x4n4pwSXzFb& zd^o1e7_@K^$u@jSa$8jC#Uh?+q=fX04A7NIKkeSs;8%LTX+_Dx`bt<26XBB;-7%DPHo>% z1B5YHKSkbL?!I?2i4l##ZM@t|(hGi|7nVq-1SPSL^mn5P=c;p2aP{Q)JU zK0oCahRRH46q|}Aq3RuUP%Q7&ov9v4?x~74==M<7GxsA9KK@@2LIQ(vqUNGRT8yA2KR1x@EwG^8XYG=k2gyFk z#iW8dP%rYl5OJCt74DgK46=j4p-ijZK8HyVK;{-d zE`9Vt9`vCtr8(nSC+gEnL_z0fjUFB4Gj&$M@VXss{7O@!ZO@WJKgqVZnY8*6=zA$& ziHwGO1yAv3H|3dTaPjEpmoe%$zERNSiooHs1%Rr#gVT-pdXPp3J_jg?$j?EsYZL_( zk6NTion6TRQ%jNrn!qNuJkztuoyvU%emgHvsBZM}?lN-^r;^G~FeCM#cxR^e=|*?d z;9)8^V`}8Ld<}jHiMSYhFis;mvWS7xnAEX#LSNOV(d()`tX0R@4X7AH?jlG4NgPtZ zJJ_1HeR`5TwCE(nvKUyh0>RPk1I3di9W00C*KgnBY51OgR--GOHnD^AN-onhmw-Pj zsn)K!6(Se5oug}hE-4m?Oyjk$;9cHN|QJ<|o-{Agh_=Pu8HY z9<+u*vt{cG{s4^nkFj|6Ji)fx+7+4GTVg1Tu%LDf+L@iF2wzB0z#^5hfqAu)BB>=6 z!TLe({koi*K$3;r765sU4t@p3h8Hs!NXd)-J_GX^&F~e6iikotdjkZTpOg)TWgV66 zpFaiE-z2MdhRDo@rh=_u4+ObQPi797A3Zv313)UZ6wa1!GQ7>eM%=+bbAx%tS%9x zxgQ65!#Mx*N#OdZwBga$y%CT-$_kXV-W1<@VMLEcHZeJrd_ZRc3SU* z4iwx@y$|#ztCNm9mfjEX$IoBCheYNlZ$EweF$S9Lc#?m)2^RxMVKjjLaO^vKTu7Fe zBG*w;lo1KP%bkVP`~7fYm_sF94HN< zjU$>mkY*1sl@yL;pDr!BOXNzOt$A&Fjz6TZ;}5M3aAjZr1}$BQ2AJ;``wB2&z;g9= zHq63pmo5^*$(K}-0@(LLkCrnaw7!+By>Yj}v>`9R0ss&pOcDBjoKLLkJZ5Kpzqk}> zO}h$Ja(2pNANyVYQWiT=%lm=CKIk5elFLi!J)NF{pU!rWa||$zloKT3KjRFXA=Zc? zQxzX_sO5>LS{3Zct^;b6rRKpcv7A=bP&A$%+ldlRat$&)| z{e7^z_2b`#*Wc!y4+#3YYK!M{R$m#u+`(+LFOMV;1R$eHBM!30R%n`ViTu{FJO zdBEOESGX)BjcK13>71_cCE4oG%rx}OQyehGaOv!JQ*-pYE3uf~p1sDs@4$p>wvMyy z4)_nFf0{#M>c%?!Qi|$8%?*ed=$Jr$PBm23h7G1f7NVi=Jl61cYtezV#D5LUYE)Z+ z2IHS)MWrmKZ~Eq29z>T+6hmoBZ@`(S^+pX66_x0;EssRUhoPxqT`xtR;Yy>_kx!h> z4A8YhM;AV-R)p43k!h@apjb+FdJzT{s9^m#g^ZG}|1ia)W{5}eAld&?$EOMS-IKMimHq!@oG ze$8h@J-QcQcq&+6P#8_J8W>wjvfC6-*(IrWGK^gU(QptY>6~kkTmec=MN5NBO6-T0 z79>TF-oF7CJs((b<_t5U8#ixCj9lVnk`&7>+abl~opWmMyxk7)bDIDwg|~?(^vkWh zBoYaPDG4*@yonV5W{10mdacQ8qdT>w%W{B$);fVEWnK2B882bLV)b9u>OotzUqyAb z3W|Nja0LPS<&AYH~Z7?x)ezwq!;|G@vq))s(GJHDItg;iZek87X?4g7=hAi(Xfpz1c#g30;n*9V*y z3}LZ|*rKZs`8$Ig{+;Xsiz)h6gc;gJ+; zl{ya2-h`M!G4c-abIH*D@uWKQw#-0N&=tN|-mYk5Sc?vVQh+5a;DK|z+rp&=-yV%n zV1$0*wZ`gf%HaDEExqu8MWHM!2(R~cE%W&RjSl{{tHDN&oZzmnin(2&k`)Zf#2=6f3SS0jK>Vw%E)LQGkLgq}} zuft6Ryh=6?YO|A7mD@?!^bnupb8lRY{+=S{!O_N$P;4+}=ca@F)*D4n{GQCV^>6{U zFOvGnI$o@nTjEp9H<7&$gGC3;!!Hz{g^U4*x1GBtKke{Fk)vk$vt0QVj-w@c^1*s> z7nK_!mIvF(#4sf4Jz%9)u#*bQ&svY(kb@};g;3NQ6z^=T)~W%TZDAc2S!jVZ^I>xH z+wf);okUs1^QHb=p4rNRdmpuv#5wy8;EvREG%we&?mW$Vf3WbxVekp;*k(ISSMihl z7G6n`r)Jm~(G%XY?WPy_yLY3PupN3AEAiS&G_|}y%0H4o7KpW4+8Vb$YY-v}sEuW& zj!V;id^6%$&wWBwk=N{59Sy)Qv=mA)MruJAfNgp=wr@kOAI;)>7-HgAKi;<4vKIUF5n#j=QtBd7jqdS}vRC!w#b| zM$Zu{Q%#gZ!CR&wh7TYFz1}9`aL{F%W(Y7%u$z+Vg%w$Zk$j+W$_x9$p#2-+;7k34 z0Z@MS^n9_Xm`dRoN^TAeYMCX!Y_@AG`KEoc02q&0BEK^mn za$4^(OF!gy}#6UkZub>mM|od;JmKeHz|=170y8 zIytwe2Ubf#X4p%m6`RiB^qm@e+cLoFiZn5)o-H>3On*%e?X+cfJVbQm`L^^C+$?Dt zcDLY-klD4(uBnB0D=|!;An-JH&=ha7{y6cPXE+BXzFv}o5}5kNduRxdlh7Ehqe6kv z(cDHn=Z%Sc4arbR5{bgk-62MwUF~h4@RgtELltXA3Q0tW=-9Kqp>(h*xOSA$<+Z*7 znt!@c4T zZ5a!iU;!nEUa8eD`fvx@Zo|*r#oeaj9XO~ueQ~O-QPI@Sh+lHcpLPEz-~Ze~HgY_u zk7v9U@@UKM2e}ul?FOd8Wh@Yv0rUL)wVy(|Wv0atyvvIoqJ`imuEBMhC8Dk>_)@p- z8P>ZG+1dP^z`zE-xyX_4uqj?RN=_g4(9R`aeU%{j1AX@b55$}LlSFFD$|#2T@!%L zuQ0<98K(H{S4u^Ml-^yE`_0Bu_jtA*suCzC`xYRVeJ0>wosr0?Qxa=4^b94)17;IL zA&6mO6(yg)dN&dkI`rUCo<&4}4|eNzciQXl#=84}p;suo;FBcUQnhw4wl5m;0$k9&&9=CQHGweAVe%t(KnFdW@ zf5!)RHd0KU(ht+XP|><3`KmpL4V5Pba<;x8PLlK^$w7mrJ!}9ug2w1Mfrl=uZI|@0 zbqb+&9r5KdDG`ho-UzH#mUY(;vODFuI6eisu>-HyD`Y5Tiy4zbWrLxzY_t0WVL4*D z9`LD;7gs-iOy{#VQKb6aDp!E5D|yE9*dmu@)e?^~`%)F%2@*sD8B2(JcIl(*UMo{#vle!+TQz#GrR!ouB&4oN*jH`z7Zf_s~yr=r`1i$*5 zJyuB*h^D|xIqC8Neevx2C}$osSIQpgAeRfYsU_KHz|kIzL~p-C>~2=GKn2Qz6xY^h zpK`ZBxrA5iCGOCzAMK5oHE3~+&SvrJsa6XBM~f}ftezm0)CN_Tn5IhdKu?mOgsAGR z*7KC>?baUJvf%dFk@|D_FZ}tRAG+?4&B__eHz^3Njm7i!S+FDYfN_rvU6M_X9_--U zqC((KH~IlXO3AuMnSL2|w&tat#GkeX08ZNm9gxB=Mk4e?i1u{6y7g9IFJK0c$g3|& z8b|c(Pg-OR=+VQuB0^gcX?YikIzZy1(3y^4h)#jq=48=A4v7`omEYzCS9mmIcv!aQ zKaEkD^{o?gJM{6Vb0)?!3#_kuLDK+?J{3IJ9-d^GH+qv{s>00?uO`EW%2!nuRsBzr zPAOULsTtE+sC&u*Ua=IoOP#2=q54Jca)^FMm^_@rjJMvRM%&46l&V|VCGyo$!HMSa zHzs?^V~U(|Bn?p|t22Y)#}9xnkG!9C*36>`Oz21@=1-cTRM+yEudb_eWkxIIk;+jD=g3 zeSqz?%B@$0b&nL@T8c42>Nfh^k+h-T*rAj7@=l}UD2x98=VSQhC-PQ_eFv8115?ai zEs`zS!=x*(`5lDC14&U6X4?;QpWSf%$}4h~%0xa)I%FYEQ-s>n-P2@?3&bZ~9&S+&X5BwnQUWH;ks zgPIAo1ez6>ogHidoY7O9m+8!)3BPysAG9!XfnCcZe=s)DWd}aeqT@x*{O{fgifD{Okw)gy(R4D1g`a^&CttA88* z{a;xU08)`l1Ki;&CslJ!>6(Weg+X!0G)#}`WTxD+{2DKjP?=5wT9wpYk>KQf37m%2 zWjiEWsx2-#BNpz4#BWW2Li7AhvlJMubrYu^eJ8>bp}_~QZ42vWpNJ zTL!ngT;$5PEp`ZatrKMmgZLJUbPEtbP;3Mc@64Jt7gg{oFC!lLhAe?XblcD(rrlZU z&!Jh`qJbjSWVZ3MKM?yH#;uZ;Ahfvz)nKxbGvq9R$18BWSz!~@-jpiKl|Cddd}$f! zx=Asn10#?sil5-5h7N2=ML2N2LcJUuVny93hwosmc13)!PV!~C4M<+Dd)>t<5t_Z% zs>sfE&snZlxu7~gARxZ894?tNl9np!_V|I--AZS+E<6sFVMQU&y}0G``oh6Q)2>jy zUI*=VuIM(>#HViTa;QE46JqQ47k~a|LH?f~94y|7tc)b=*!&EKXzvvk5;|?|3xxBa zc4iz6$^Kktwz8y3b7u%E&V6%jTor;`AS3&e@b+U=puZxn`c7liyYfr&wV|VB zIbhUDf<#UG!8VWF#0oz>Q-tMC+xyWG;BY107RsUaF*qv-HnIi@c=BeV0e>Ztw3yq< zf1%|JxxizZX)BZ?*(o!Ufu`X%0Bvpzr)$RKbPkvlbLilF2NYPey#Vpl+TH4e2;bd? zN3+c??H~s2M-jod-?C81$hv)l940azyEJvao1Z3V4EII8@J?A$QPgbJ@mWbs4qwc zEkddlYTm9OaqhI%Yz_wVvg=_Q`eGCKN`=f;1vSEWJYrZ?^O^&f&JO5%=k)OU*-4v_ zU;ho-B8L*Z;&a$%wmElH%LHLsQzHo6_ypz912ot)|rDX)U0K@6X#;Vni zDwM%4bP@^K2X`EN+QfN7Aiq!5#V7mMuk^yXv{NRoslKJ%UrmG(Ufr%LG9vly=++0O z6#%a(U<~@+e9~b4yXj;u8jn8~P{0f3>!#t9P&hnvNXA1vt~Gc)1-?JP3%ZAp9OpQ9 zH8mzA3_W#p`1qsA`N{(UjD9%T8J}>8Pb7;=)~(IuiSMBL29Nrs3E6goBZetF@iKtU zagiJu;1S=Yo-p-&fi->G1FQ}_2ib=SfCj0)C(!XIi36!s$rTF52Y2LT;r1dqso8{~ zNwO)JC^polyg<%8|Ac4gS0Y7MlLAP2Ho<~9XYPGTpz0pV6y&?Bu#Enx$Em7nTm$b) znJP0zEEd;p@GNEY)a~1m!v6L%wlgvsBCEw@<(Ru{rwDu|t}s=pX=}g#w}(Eov12bd zI}kdG0O|E^uaeb>sB*f3?mK2=$mOSo8N(QnF3b67!lh8?$V0-`r3_7zw7oSzHRc`D z{V5$pa<5WEr)TW6)g>!)?9pBp{J~7|q2|;lL3B%Uq%OKG{2?u4vBBmu5LT2UJpkBiPvhI)4*w#fjXr(`KjTm2V|e{JRM&3cMvl}YR|F50=r@>` ztX8=iBv*4+BWIIMO5rKU15(&s>t#DVFcXqEc4k9i2@gwH zw1CI-ls0biw+zeGD&g2}VHDYXu&@~*)cvZs2{z#+Bb5zZnhre>3Mi;fRd!Wy=(L=Z zGY}s6O>iXqJ*=eiUSQvxFAUB;wy7vCs*WvL*s@gHl-qNzxwsBeZPuzJpnWOMYSX+! z`-tuz4WqN0O*C})?(Afx?K(B@HR1;t zUH0VkjSa--XNisSZ1a7O{s8l_%ie|;O5H&Cmj?{}Buu=m=0zul4KR$95}nv3y^t;I z=xg9HD?cY&x*%z<9T8o@K+NYVp!5wq7#nKDW%8q=!eXU^D1iuMVO-^T70)VlYkl9W zoj~L)ldbS)E?W&~~ z!Fr%)9(cBMLolncjO~g;il9dA42~+_F+|J-JLY-)Q!x85N#I{ztg=WYF!${7cI|MM zu+I45|zHgvAY^q<9uBbX~eh0v!P3_LWTUN3g zFDgf*_!qZXtlOnl+4#&8Dd!yQyrN{I8ag~VlH~n@SK3S1FOsx$N~17k7PQ*E`7wdS zxc3l!lJC6*ADcD88l4!s>~j8Fog`Zch55{ji>IOY6>Bel<(lDSbpc+wi39SQrjQ3P zAfvPvtY20dA0q}MUn2V$TJ|=V%>;e9@T8949R1!PGwoJ~z=t8yR`h8Cx&x|yQhR0- zE<00KJxGHKDKro_DotWZ;ci))kUh!}H5m6N<$c~Sc7mjGWIAD)~t|__nJea z5~!^(gi?YviAGbhRi!IF5>WQ>p<&B*bO5_rA71cNvPE7`qw)tmM^h>6-mymp+pAJC zAJ^j72-g5)-vRm{gcwWF8b{pTv{$v_!j4UueE^+4 z6G+5*MYAuASBuH!)owZvtJjuhzDw*M19WHBOJx@uZ5=hMFNSwR+vuj@s8Z6??bJ!` z>e?*bRq3Ndzile9fffNB>{SQCe(ozcp3|w!u`hJ8#jPfx76UE$a%|5*t!Gel*A1qg z>bq=5VoWXy*`$iZ?kw&Ix<^urYs@|{w#oMqsACUk{g@1F59v=rIL=a5?MUruB=Yg| zx39wM?}?<<&plGi=6d;(f{4w<~RYuDfZR~an@4UT+>CN4iYm7)5D0yQ5q^3 z~}3Xme+HQwm1$3>pJ+@gTaMi4WX2{`XWu7k=!^c5ETQ**|-v9yOR)w zjCJS89&Z%m&ReW1CM%Vy;o9^b(nM>IUq6*H z0pRS{pS=Ta=Lwun(i|P6*oIXuk0N|`Yn6~jjH3F*=Do5))>~qO#UpvR2Jga|cF4^7 zfl7?9ah2z%Xqyr^U~-sH1bYmcK}YDhMN+0V7R?FhE*8(2CrJ~@;N0cj&aa?KE(aG5 z+xuXbOT9A`R7IFbm25F!I|JIuhN><|VR3`K=m2FT_pHTN+ybjw8?AB^!px7tMN5QZ zfk`a@sk2B*nD*Op9Ez0fG}Lh0S6Z~TJjfVbZ`r|ci4Q*#ZS)ox(C zXCAz@W9ynk6P479c)HdEsbve1XqG>*Ku^(Iw34@DVz8TtfI6vF3;Ko7WMk2F4|n-b zHul>;!EEyrs1f}h1Aevi&_DB%q8c~Y9)`UMQn%4b;(g#_k-Gsvrgk)akV8qoJ|Tbn zQr6%S%Sc3>t?qP;hk*r5sZ{+!o$UcY@gdaHw44r1p^Tw&^JUrWY1ci`L&wNwG?HT6 zsP+$=#dVf4N|Tmk`{qds0{$D&Bb~^BLX#5%!;_>_y(bSN4Ie>+y2);m4Q&**O7829 zRDL+5;RrCF97EalYv~Q@0euH_qG#Gagx5cvKK`Fj;r`E(SG;J0UxG_81nv!b@@knt ze~~pR+aa{3N(OR)e1q89cVcg6h4oR4GC}(iJOhIL#PdCxHs0FFYd#{0ff=?HPDQ;BXO22pvh)#H99w5aONPgmFZi9 z$I)Hkb7t2JT4r=O$;$P2_C7GMs;{l&Z6#@ps_oe}DH7Q>1?ty<9U-;gWk-VATe5I( z(evlcjwyqz1DZ|Su4s+WQX?#lDkT@5tXS^U;BY}mywU)oC+QJ$?vAfuOg~{xD1{Ow zaT03W(U0)t)^e!SR!sH}{Djsf*OL?rtsL$fGjSyA7Y0V;1p7-1kc~mLTNPxvL`p?; zspSw?^=DX-1HjWz3U3Ugd?1AeY6*%G1mLvGsumg3`OG=_S)x)nGc;kD({SL#)gZ}t z5?$DsN1k`IXTUms*qQM)2t}ybtIDD5caUU(vS98z5%G>@BtZYR0FcR(Qiu47lKQqq zfe2;=z|*?8qpDmHM0%)$#5pjTR5?Y>!}6oYP3p*M z8(vpconcI?Hk0hPy^vBFc-AtQ1rj;*0%3t`B613j;kuG9>>KD#fdr_6o*F0KuNom5 z0yR^al%M=#hir9{@Ah$V;@oMyLZ#?g_TFNOH950J$Qdk3&avXP*q6Xz2; z6dN_{K_&2wZ`Q~VQdnmqf)E9wUp^yL8WC@t_fzQ=zo5FazZ1*gB-Sk$V|SX7m|fwQ zws*G-;w9TdjiKyOC=SK@*ih+L?>-LEmxR$v$K_n)YfR7Kpm#nW+)74?i;qw}7F!N5 zBCEZ%q-+=i&66*H>x^h;T9|;15cEVtW~ei<$0NL48GSEFcA%tg(4;X1$AxIV1R?N* z2e8iSA1;Pp7?{K0rrLE<*g&?BMlWL8-Rqh0bHjnKvzh2nuiB77suLYPy$X zN9p{4TM11g5CBc>C}S%8AP&04ofmUzB1IbM2d%fbH_!SO(X? zb@G`{Vcw~WGR%M@XkWzDEy_KKZfO-aqqbHV6lgWM=7?AF&cTH$DDhQ6Z;HsX-$(mz z-8-TgB>Jd8Z(@t(9yTkG?$ zQbU7#!=*Bo9lS)KQSB`}Vdu%~AGu`FnjaonwtX54sZ~M5`);>pzxw|qm!AoeDV$VmVbcj`ulQz{GLXxJGE9B(=$Io}q#(RgXm^>nM0{w8PTmSLi!lD3W@4jH!N?!d|3g zGMu*x*IHSz>z_HLvkeubI$$c9ER(GtRW`59?-n)Ktkm3wQqw^D4Yk&CA;OM3uA z;Q7uvmTFX>*R+7q<%f|YE{~7=y$3PuX_axL<$YckG?+O zj0Z}Hh3dHtt}h}F1HW3Wo8Z7jiWdANR9my`6ntuc9V^+eaDK4YsxWt&E`Siq_Om;0 zs&t&lvF>6-@?k=0SM!b)D|>>JG7(f_a<^bt`m*tG!7uO+_FO(d=?B0x=?KvHjeoWh*1=B?8!nRQkBoZfXR zcQ3RFoCj*zu7R-*t{b`>bPWfU*1`0;=ge{h{kFX%J#mpYc#&>|7fr$hwXkzjb4QJ& zZ9;>DKa_F?VB4;uW_EF4I<=_muHWWB>rBjRG0dSQQXW436cZsiw?jd9S17jdB0N9a z6M5I1%{o3LD<;fRY#zAFH4WWW5Xq42Jo}CD99Y)M{apMDFfquF0GDlk28lKh=V!o+ zfdgf8FZk|5C!gvj5?R3zA3}758XyyaNLxt%Vp1Sw#}GuHik&H_Aq? z%0=KD&|kmiZrw{u@ZVj6Y0W!ebw{VLBsa(1(%>>q>#j|tLVpWS_@!b2JEzbQN(q{o33uZErsjT#G-4caq4ilsWer7)JQheL9+NwTqhM@E=V9W zCRqFL#rRuR8s~zw_!;xcbb{TimfIZhv|Z@TXuH{$OO%cA^p2z3ffjMMLuP1fshUvfTBWv}odd4mV=N9&tU55R04v;pvoB&A=F*YF%iojBx$!+fG* z3;9lN%cg*=4e7jrnIRrO9h|VT4=b5g^F{&+HhVuJ zTV8kxGF?Hm>&z}%8I>y#9Z+`lz7s%xWV{qJ-isz?c!y(P<2oZt5ihNR6OF39oCtvV4mOlk zLJ;)Hl9fmQ@|`E9-JqjnKX5g*jdDv3h>n{BW__!{)E@Wvllctoi+cQ62NZurVsKsx zK=t~mzDf`S1_S7nvO@rH&6s9qE9I>MdSbS7W;?SFT-noE+CwpQ%UKqR^9uE@9J5}ed zU5!a643+t^TBwY!4z3lT`(q8GYj8=hy?DR7kHC<`5_mf0IGe9gr{%q2pV3OUBMM^p zm0Pb1Ui`~*G!^dUk>mQ&(-P9$?8{H}7Q>yvLGqB%^_*g^6x4VO87QB5m?P@i`oN#V z5dXjc|27a=pS*qg4O-jl@BVqf1?SSH_6Nkxj=p(hcmJBufOAK`X$E+M2u>3q@{j*K2B@kwEP9k` z2#oP?XtI0%e7oD#CX0HGC*{1QbwabYN$#5mwTmakGlJg?&&-~>kD3L_d3(ywbm}pk zC7opQeK!$m(N8L23rxJjN}wTUud`z8K_K}uoxOm2Qpwb6+Bsc$qNG9Vut;zLi3|!e z+3rgbcZH=0Q_eF?u4;Bsvsy(KR5gwpY?+dY=QNue+_Z>tN$PobRg4g1u%xq6U0gPZ zF(&YKq0-u>wN@Ec{$(t+6FsslNO&{WS^{$L-f^{{uV+n3^lylL1`yH(GZe6|3W$KO zLiGSLiQ2;C@UJKusGb!RcBt}sbNOL)QZxY(oZab+Y;7Gm`G=8e98a@yP+*El#R==! zDEiFfm+no=JpsK0rInX0*xh`peTp{!yz4^Qf|C5#ko4+ZDo%6f8%AM)j`MZfrZO zfVIFs7s@!}VPb&xLot7uv|1Zaqr^YCz$*>-&%uJIBG-dsU1}M*U&7`)ChG}he2Oe@tC$L(m~K%PR%*hz(F-W znue5!^N43=U5LP;dn}ih{jGg~gKrlZRUuGg_eq~IGDL^hz!*X7+lxM&D{PotMgotK zFeA;HbUx3&6jU{cv;8sCfv+Xp*upbJlp3~S&Kimu;+W#*sp~nS+zzHrE7vY(&!+~G z^>&j31MQtUNgsEIh{4oSLUc9AzxmE)E9XVw_1C11m<}s&a8!3RlZ>sM2?d#tX667n z(uWTsrz@Pb`;}v7ST5HvvYu#PAf-AgbuvSN1+l4zhJI6biqzDM&>mNm3auPt0Ncm06>Xp*=uo=}iGG zOAZifEs`)&JH=|1tPWlK$LZsDG2ep-%sjPbXh%pvzJ4^E^{ry-WX;sK%ETPwQ?S%x zUddOOCq?_s@-36jYu*jjeh}x7N>h0Wd}>e++L(w85E${jU&}$jkrb){R|92?;hgA<-*BBKbz+vnJbnl^rbNLzdb9v;f|ue2gZ>x(4xKz` z>G2ztIKV@MdYnZtHTYJm!chAAZi_O5v28&~okiYtWndn2H-|W>OYHzGewN1vw1tR+ zMf|e9>w}{}`FGd|9CF$BIz&vtg;jST2eAplV(Rm-d4?!4O%ildYmNEh=>2_gqD=16ynZiO1!nit=bVWA z__!9o<68Xg^znz#j#jV^U7V%JeY9ut+SvM#HBx$+u>~ZsnY=mDe_5*gsBJ_fT+6o5 zq7rg>j6>QxXSOl=5Wh#;XC?fz!!+nYgu+7(rhQ)W^BQ&$#sB7M(*CX7rmYY4+VP$9 z-4za0P{)z1tA%wLn`DbroF>AU@_d#}T>NFpK_p`UC@87K)Ik5tlx>}iu7*;}%5ynY zc562+7$`)bNc{G$CInsRiIbp8^6vV`>Xq(prwi zduH<5#67rFg?xE80YoTkeGXPDVz7Xbl`(iyH}A(cB%yvy$KoFc@bf--`|R}-8)v*E z2o6ggC)?=^ryD0fNlkLhe${JnQ;pNR1Tl6h~4mVT9mNVD-$4~Vpv+ilxu;=k)%H>Yy!)G1)JfZTHW@u!VSrllFU<1Hk4S+Avb~B9*XM; zz?2d>b^gGV2bKNO0u19>5EMk)7w(}E2FbSV)JnOC6KU{XCvOM8z#5gcu5 z!qo<8a*s>FzS#5&{*v3lxI@h`DJX#+7t8`+k}_UEwMk<}+b!0YO1LP;Vs9{A!X-KG zpGeTghHT^;g<+@q>k)?dJ6r+q~)7w=TIej3pnn5o~Jl#@cgNJ$pSis}{I4A56) zdpRdeghQ}x=90yGKcmCI}|2iNTrE?Z8OI(O>@LP#Q;R9{c+W~|_+tnYrE{J83A zb^fd7Xhu7}%J(d5>K(F0$&g(9Nsgqu05kxDavIyelR-mY^^u2Tx!>lDig}ruy|D6- zI3^v~kPraGJ{du>Wnf&;Vp}|bZ;s1}DM&fn&wG3%k+Cok0R3IJ3-=ECDF zE6)BVhWaojSFMVmP-_NBWxN+(=L|Wl7Y2UxVXiix&#G@GAWRwS(g#Us1MTYx+6L=< z08>D$za+oNZIKM|2FcYIe?xG;r29Hsw2})F7MKJNr5Tug!qzMj8>Jqn6*wGPXY2&{ zU(PAK{Jf+Tb1Hpcd7@ix@VoDN==MQ=Im2LL+3OV$PgfavtmVv$|k2 z1g+3pj;Kd!)sCO+^D}rvCHtGsz)hM#>_}Ng0-J9okKIdlndy0gwZ19 zZIaBb+U%%Lgd}e+&4M}KK*)UeP|%EmJlE?7y9JO~ta=P2bXgLxg3XyC{jH(0j|Sog zb&r~*K0#5#BbnXJ^Loq#aHq@F34s_}wYg(#KXDJux-?$B7ngnQ>~(DbM%gjFgrN4& z2x|wVzlYA+2J^lW!mSI(hQ| zou@x(ul)6QSq0~NYA5^phj7eA{P2 zdNF0PWr6?*UE|Of?sjNbW$12{+#5=``2u3sd;_}vopz$m;A!J~mV4q1HJDELW)Vj zB*Z9_bsz2z`YS2XhxCj}{*a?K5v9B=Q->NC&}onAwqLz{_5WbjYjwZPg`l8iCazZhp`&#+KU%EfJ`XF?AxWUOHR z%ykDr7=R^}zqjECMhTd5x_Bp;f7?ZJgW0EyFz>^yBGr|>?_I(it^OV6t@0tSJl#lp zxYFU5-sb3ii-W16ffFA=r)1)CxSp0k{D@+uk==1++iC#L#}@ONbsAU@L$=Y)*wQdx z&^RSoSILaBsK0*uqn3EDnJ)N}FiVzaZNp1; zaPw^IOq|2EpD8tjT5T}!%QB(&fgf*l&FS5)5V_@RIEzxzTm6W-(kg}yvFzHZ9}A%g zJ=|$@b^Nv%%Yyg-ACY%F#b(E>Y2Y0I(U+Ah1=;$uSO&-jaY03*}lw^)`X(cJF08c z4V{-X7u-Zpp>5}QkqdHYOXm2B!e|EZv=|czJ*SW zT3b|Qy9HP&050(i_4Hw|#(L5W4B{pvE1-_DoH-3eFs+)Fl3r9vMHuWN{gz9?oF1uZ z!ea-H!Sphrwt;qBoByG^k#MjDdfw0K#p8k|^8^B-ool&9R;51X?F6v~&TC=OG}#W2 zz_bBla#_d#7~qElTNe99dRcOv2K0N!=Xz#m^L$n@;{z<>8cd^1p$F^AI|x-vgJ1Bt z^XFux?NXA9BLAiIJOdoC6O*B%N7B8Z;jLP%W9@n$Jy7DPSmUBoJ{Mn)&E&O%%9Q%> zTh)}@0y`T>HS9+pq=VJ8%3kD~C3lNmZ3kBlaC%nwZ7^)R@Za1<($3Xg+K>rqQwCp6 zdUR*tcb0^<8$~v&4bfL1234lk0y(np-Ni8HY3{LBv+n+u+Kjl6(8)P0$_NH9_^z&x zFoJZ!XE1EH@_2^U6%ZiKZm_IwcR`VdPYhcMx1v8W(HL-1(LO6jfT>da^4W#Y&+6x5 z%tM%KOH8B6P^zjmoO_yyL6Flj+osTovyi*$J>~@k8^2va;Iae~dSk@#D@k3F9LPql zWRCo+vdofEgm34!pTGVf3oL#0_Pu;1O+fG3-+GCG8OFSyZUJL|7{D(P)ykY?!yV=m z&V>l>1tdktIZU334`2XTpi{teBnu2`#H-?}>_IO`aVnw|!-eY)M;bsL?Pda#>^wPr zBH9gCLo*@2jL=fk89hJ+C;lKZ``8NH!$o;b2nM(O{})|)Izl3iDVd;5w9R44$G$zkdBbCf;!H|LgVBcmERJehkSV+&=y4K6abJ;1!;j z4t+Pz$qv#|-Z5whpw_I&GcQ)#V#m4>E<{xT?>maKl+%0cH0+GH> zFd~-NZo`R5RU=u`+@3=>OLFXqD!I9Az7~ojn}#;by-^f)GF%bfM^}^pQ6)5PFqp+M zB5Srv5WYL}lSNY)PJ$$HN|h8O1dU8Z8zzs$XkY{?A*I!dl0IZhfsJf?nN_MnJEbWLcY{YUgxcVjd*zD< zsCSzn85UpzHQEwH>tJxns;D{3;-!FCGK(=yn3v>&3wZi|Ny7B{G*d>h zPIT`Y&>cn&>n!CHzCaoXk2Z)kHR3z%_N*IS{3 zkL+NOEU`M-+}H?{d9;eO6_rMa&@C)(@Iy%q>CjQzP(D!n2mI;-GW5`NTH~|ygq!!0 zQ0z`r9g=X{=*X^XvWxd9yM{t;tojpWfvH5ake|vFRx6znuw#b`+k;BqW#N=;l{uCp zr%6wKyM?Ys{X({$86_vl_;;Vb{xZB868vN&|Nlqgm0r@(@9J?_s>Te*DKX;==FswS zURCl_{!r4u5cN(9S3T|jC@wN!lh|$c>!^qeObc-oX-pc)O!<2fS)hksl8EK$wyeJ;FExSgLpelLGFh_%+5S z{2kV+9o9nc?azPp_M7luc#|P7emPvW5i11e7SG%8~m)LNW-89T~~X9lgNW^fgrJ7GD+TYH6eZTm&AcvyTNJf3CnC zw!0NHe5k+-l4;gVk$R$nQwBiicATYwW4JjzkE!~=qKXSwN+`mWQ=VgtrPM_cFkOhGO39kSdAyRS zP*d%%)a}{ z>!)v@r(c8o_Js|s?%)UC-PKC<4CM!A&9^I}JiP{Bu_Yn`g(%5567*|Nq+sfT^VesE z^~vm++aUs-64>v2Y0rx8m{?L3fhJOtI8ewPVjt@60OO`^5fE z04j-*4e)rQuDkkLZ%_L1NFkA%Z2jj+RJO)2i6*ff5YbLTFzHaK78!HpvBQo8AZ_hK z(W7cOzf)Bdr5R3h7L0Igc!4=(`lf{xJ?(qK;2(YWcaEQQ>=53*$?tyr`g3{*84v%< z*B{Zt;}LwS2xZGTxWuH~}Dz@jUk3(gWC$khw zm$`&Bnb)Ymc9X~`73*~;9T7rf7|i!}`A4X1^{NrSk^@r*F1(uH4rg22VZT`<2at#S zD%bxRs6;5FT5N|d7oF)UIMZbCdj+g<1jAf0a|1A|7d2-qc)9KhHNO%sQj9m3L0o`- zgpQHz@~-|TBXm^kxl%r(d$zqN0n|fncFYSaT~<>J;yIhey4H9LSPrKu$gQI)V;c+P zIz8)*nKI=n(I%(Huz7v=f-UYv^0X|1@5MTaXAapUiI^-mE>Snr^KYW znnj;^suroJTJlq0-t$U@wdcSb@)CV|{G!`d)k@r8zQ_&$lF>x-RNy%itE6S>LR}7c zO~Xm!=5^`-oABx^mr|!mh+U+0K(hl8H>gGf!Q4*3u}}-T)xyJd-HH-A=<;0*@rY== zX_Cb(WI?YnaWo4DI6f&@2IGmH=v)snHC78}HX;O}rF(Ko=p}Lqy8Wa$@b z?qpq~+P~RlQtlLI=y)R~0Tf$rd0UQEN7kq3I0s-1SFF+FyaW$2mSQ^~Nzm13Z5j{*gFPlh4bkJ!sJAj9zBhEM3wZ>PSTJ^?+C)|8 z5s|!8%$n7>NX(p?2XZl^y_7K=kmu3+82GXSyeb0vCJSc}N(IC|doN0zmtD~oah@ifPC&+BFLgb&; z__xdbQ0p_IHX-Rn<+2KL0qseRc#c`|2>C@VeinvQ2wnmac`35|F=}Sk*Tu+VhC;Bt zypx*dYp}>Z%Xr_oy1T+%-9^zLC5^P?$x3n@CJ@JfMM)zW38NxAe+V^3Vpt-qT2KB4 zON9J6sLfMasb*C@H>tw%QwqMJGKP=UB-0c~?V+&rZB+39yZmFWo6_o}NE2;jXFu#6 z`4Cs_glyw2cc<%zE72Ksb~dXt@thV(psbG0>m#mG2cJyU=t%SE9wwTc9{iTgLYfNc zuQnpOJo6`6mC`aZnA^m3o)iy9m;ta2Rvcl5Y!Bq5AV9+0rORjY+H(edlV=%<83*x> zV=WJ@PL}N3U(H-y>j--yTQ)|JZN=D?Zf-y%w)SjIt|}#$Z_P@buOGkuGVn$E>X)y7 z#NtPPOJDgUeEWA=qDZB~_}Z#49>v^XROeZ*$BgS_jl@<-vG-~}=kM#9b@6JC4T+dK zkuh~x2%={b)n0m<1B3d;E+vGN(szH78%p7S3H1Y?ssbB2JMPdhO4o8y;LWP!dEeD$ ztwZ410^wm$LS%LV(e?WZV_m`Im?8tP@ zUIM2z&k%By<}rm-m{Apyr%-LNQh4MRWG|(~h-1ORPdhe}8o@}RUulI2Bz>19k~t`T znHrEtOx9T{y6Tie(5_{nojKR+)6CN(MZOtu!%&KY8}*8G-Zse8{j>rF$OmSKH3k@0 zh3GRvcg1jlaDN@8k(q82flX2-N-#q2Ogk2Eu&Clpr&P6DF)%jx3+~Y;1qAUCp*qwx zZ>2LOYJ?6_n!Wl9rTaNOCmdCJ*Qrw2+Bol9(;Ud9_I`sphkXY)M!%%i2g>wOz2gvK^g zC^1bjs|EHxs$X^fuu}i1- zEZ2mlt_$5RckFXocC`5Ri%)#V;O(NSV@bSnz|-2n+2SzIN;L28wE)G?_T**?8@Q4R z0J9t&AWb1VAPEv@Q(~EwjXnCs={p7eEzy>mD9?AduKqon1w;6NU(M912a-toACoHR zsw|hTt0#y3!IV@kW5yKs1z}@sU)2=|Dg)a{dAw8# z7rDodNWIRj(#F>3BLo2Uio&sc`tBdzeuQah8T~o$##BpZTJa-Ybv2Om*|zkXH727_ z%a8PT&kHC}%uMQcAXxMYr9YcG92qYb`)N)}7U_wPkD$P$boWrEY{aXgX|+2C>h47! zf^@5{$+*aYV}Smv{|I*)0Ntx>DBBzP;0KwUY%A16jY`@DoKx*TUwiP3DXQH{^$M%$ zi9I1jnB8~5e|*5SDl0jb?&_Ntps2Zi%yGTUV(B-cx&668a7=m)l5bwU%Dp9b=4U+)&qT8twR@N!ooSdlsBfNcl$`#?&NM4`4 ze*X6Bcb~p}_3ht#C4$8aJqsj1giIc{7BwV!9jd;d8h| zf*MM|q$2xSPJZmVoJhHL-yOqF$rE>?bF?`_m>azo~cLI z+K{!ukX)yDBg$C`SrQx=(#r*gQI&G&)tYX>^wyi6ndzclZ+Go|Ep-wR-@o2;%S`V&qO z$Hr~AY7*C^B!Ku)qSC@_!(%e(wemAj2&D>-N_RpK-A|9el87c3Z5C`eCqLJeZ~5=u zer5&vH{rWq`0o4sU!9f;wxyK(X{%s09jFd>5P)=%eLxT@?Y>3KuXrqZCCa5(!kh)W~bpUtP+VE&Q zqZ`a6EKZ)lLnudxy?}PgEkZU)DHMxOsW8=aMdePRa8%^4dI*zQGD9$Sv$>K_G?olB zcq-6eZK3cV-jiImp8}->$M5LF{QA4Ur@#H9w;#Rz2bgStgMIe)qt{>Qmp+k> zb(&_?jIJ>T_gE>{^I>mb9KR$yz!smK;!12L9G~DqRL~80k^g||l{3VvHQ-aoJ&xG2 z^}P?Fa3&hAM?h1e zvF!TF&$yI2Q>8AwxVMAmNIT8l(ucKl$v9O=l|b#z26FngHe|OE^p9uzsI2y*ydIY> zm#UvO6Y_Ywqeu*{i7GK@w=@C2JUh{U75?JCrrQ(l3L4{D#iBy@bkSwh5_>Ku~>EZrl{yA5S;WyJ6%Owl->Ijl_JPIXh}}4j;ha)QR8@;0yceQE)% z5NH+fE$hp7pTGY0?L*)nU%v?NzIgrU?U$@5?);A*hw{=O=l0BI2pltent*y)rYegA zNC^L6FM9Tgka2_;LasnUN!(w{$?FVeqV0cZlmxd1)$2I1;^5S=IP(Wo2Qy!>?+xO% z_PmxecHUTMyybpFhX7S?ryJ45${DHRPHLvttEC)Z2koKgP`s(6Mlu83oXgNhwBj}} zhY!74V=`tU66%)E@M+R6%~+X2TdFMVhVHWs#P@`ZsXA#K`cr9qhu!Q3TX#7N9hvc4 zLdRRA!4}J?M|4<$comqPSIY$>)Iie97T$k$!YYjXBOTQGlNq+$;nIAH7ZNa0^Efcz zmV@O008x;tfb|;I=XG*ryj_rFNxX|2w&N%v;pia|&Hcz56?Bt(sqzFI!$j%aEVxN0 zLx`6=+6OpdJ@{yqBZROkfFwO&8(3+(jGI=Plr1lHwgL#>g54$L&x!1r0+}b;6)n)u zg60!(7z%%;6z>lBwH=PN+wv{F(?WYgf(UchPvjn>9N8XJL^fHx&ZlBwHSrp_f9f+}*aHpIIfiNzK3XL7(mj9HHfI49DvB zGV;)%Rz2C%p(~_Chz`Rn-Nf-Eg#*LK*Iz1};Y6yd9XF(Dl=FIRj+R6)bd@f77w z=yRFC8H|j5sEJ92h9<(I^uf;zd$T2`G6TRM3?S7OL;`*cn}&~JA@nZ*RQ-$jnL=t^ zu2M!50_PoVj;5wu}Y$d*^tQqf_E9J91r zuZ`-p+g=)=>MZ$t(eZ?lfBAvMEl?Ap(`131j3=e%2ApAgoN=qNKQfqiFWg@9Ak}&Q zpxS3UiU6swt*TLs5eK;-X&PGzD^+8rJit-TutiC{P@m6^A!?H;@e2#-3)r>@)$7Un zSS=sDctzPp75}AZ!ywmP8b___TB>QdNcC6A;RJqLRaZ>wqFBNT&C;zQ7 zmQu(VC3`06@=jVY0`(zb5CP_(_rfxrBx@P^^!V`l&@GTI-=X;U+E_eCMnZ?h0T4wX!dUUjEq=^oC)onw`+stlIJ zfOX)MhzKv8+z;2gd?ewC6Hxdr6{}@E$&669 zV|kBtg&{8dsb8j6)lIF&g{&R7KI??Sqjz3MwOiI0vXndQ9LZmeeT_1qjgF-G&L4Y2GdcIYZq7UMf?1J5tBNefeYqEbY-1}-RC=Np*l5DypvN#V_9gkgLUTVytJdenKL-$E3 ze?HDpQ;x_j!@Hzoo(oU{-nSve$T1XhXqinU8=1cD#x2AN5`0xY3^o7Tc!yjua$iO* zuM6l%8!RJP$VjnbtHXAY|5z1_u#u&>2n=!7%m#517o;yMKghFc&X%@bUlh4wRS(>4 zEoRFB2lf{$v6Xi@t2683l6_l3)%HY)_LDk>|IkD#K&@Y4LQS~)4Vt^0E922I2Mz{4=@;~qaCKXi28MlC0N$N>_%9T0paSNEJrLedMK2%q1wJoo~<-t;yRC>k& zFS-Rd!|38r3tAPPADkFN1?<-uPvoWQ1FM>1b3;Nz78l_l^sixH$g!@e= za0KboKsMu~gT^S?CtgtZMDIkVZ%Wv|rEWcpz7Snti`p$eiCb<*OfuZT zRknmOh}Wt%^OUur;MO!ikqx}DeSv1LM9jsJ6h7aCX_TX@(V=IhR%NqA3kt zO{7iC&QTua8PiiR0kS6tQ0uB(8DV(Vt?MlywJtx@g9|RT=8mOCMv$&P8>c-EDp=xO zl*eitq64Byu_h|u20QbTH@X|Im?#o<3p6SfM%pI0zZjT&H{+94e4r$El}uewb);9@ zC_i=bO7~q5Kuk`FAAsUNB$nXY@w-7hNNzPs-ErhRaRO6(oQ%^}Ky|)w{(`M*e`R*J z=&621o3Dek&S&(pHUX~U;zWR*X=GDdQ#xKk`QXmyY(2ozvwM1kVJ1v02BN@JJh_~X z)!%b31b8(#T!|ZI4+){I7*_U<2ng*VC&n+*a=ROL0gX?Ob*826JpHWiV&4Y5sv!R>9}4r{VEL zDylW>&d`ZrU&u=ZCZX1norO-1USV zmsa`|Plp#QcUmjF&eALCZ+xFW14%Zs%m+pEqLd-W(Il#Qu%Fnzszg9eFl)Dn??$9} zpGbCt6em_kTo?7Rrk21}H5hpsF<{x2tWAkJrVck+mjXC|QoyfW=Z#Rsr&(!hcknDS3w|BVy2-`nrs*zdo4U&Hi=uiqR>idJg$nHl{;7x<%T zuq>8JcQ>vE3=Yz;&#u+|Bqp})5>{xKl$rqmKO2J0*12;Hw6%ljSLJx=&CwhSpjDN_ zFJKELkMGq<PJN!p!(R)e)0??#$?K@_` zMTrfMl%ln+{h|Ksa|1c{_15Y5d%N2Z>j*3*+X(e7s>cnzfzIU0X%2O*j15ln&z3a$ zLN(Ux>a5uOIYtz$E=+U$eU#qaFJ6O6baFGQrU23v`~px!%LvO9LVKa|2RvqVs#@wN z3yqDAk;q7%h=dZe*OxN$1|kc*iVA~1tW*Y|OwLf=4qW#p!v4r?b5Ve@gc$>q^axROriVnxhpq!D%Y#ao zM2c!Q`Z@d!snZTHZbNy@6*!0r5K|~}DWU5Kc}8>^5y8U;rH=PHWfivc#Mw0CD@MVu z(yf`6URga0;OdyV$fK(M57-WMcW(-oq&CbTzK#dl(28VI?1%>zAxnG&pQfzEeuvf< zyu$XxZ02OR$s@H7n3?B^a{DuQ&>h*y6MpL}pTV|OW_ zh$|S)*z&26QibAew{TqKleR7?CfR$N8f=TO!y_IH8zZN-_q`K}F*C7scJb`GWRO!_ zyGn9CFi7eUX=i7wh*FwiJpJ*UIz2bg&OlWufM>r5JrDM9@Ufz?^;J#KA9#O zjZ5jx6^U|)e89Sna+Y2Q7Labtjr)>yj%)tPkykSW`V5RY&pYGX^F+NgnAD-g2UMo*aH8&eWj2m(T>OMQe(stWaahh&0 zfxQtSf(8GRgk`N|&7dXptAx`%xH2%rI-r%5gNlmtRC$Wh@TCrx;MyWTY^B$yy>bQN==H72cR71vAe``s#BIS(}|jTH(E zYt2DbuxJQKfGd5s5@)22F&VZ7VZ69Y(i_Q8z821d(Q|)MOcOHv6BA`XhBH2PQ2&L7z!T{c#uwUj1iE~{05y@_pVVpTc7eW( zJDa50Un50%xufj!HQW^w9SW3V`4Cn_P|VoIOO)V+nuR$c2ABN6NLdM)L;~?rf%uLU zJ`L0Z4O}bG#uA=jR$LHgIA9vF*PEdOGnA)Byi`pfCpg|&mZ6b0851ll_jFQT3R_xW zV{N-@Jh^gK0VQM7mVQB6P$D2C zVrqxi6Im{hvmYVwESGg`$qXCz9#Rlqw++*R#}bjXViz9epYRR=7El-=aq6$#FnOF2 zMk03l`b7y}n>!wHma*QPkfr`x%8B608n`YSFW*B6-06el7HFyVgohT;hJ-MmRJCN9 zeulY(R3yKB`$X%Kx8I_N{yMNHh^`oZj8^F$u~(5;ee*0&Ke?ulNrh4C%LdvgHA!fw z0Yk;oy$N*tNwvYXAq3K;Cv{!;@C_VAkanGI+3-poWw*4B-cpLWMeW#N5yI{?F<eR1Ye9RwrTeJ}iCX8x1j6h{O7w~G4TdkLO|@#8QLV0cGIa3* z66|~g&H(@+AMCX^{zC;;J<4L+b?cVWziMB^+copZEc@W~6}AR$lms+gW1n@_k(*Cauz6I8 zyJDvM+?EvEh+-=ah2O!S>N{<{vado7@pY2hn~rmZ=y^&4f)!`oU|8*20lZ@~rgUN@ zb1`)IKF{e<>C}Rp<3Fqv;|aWVs=R*feTrU{!>sx3mR@t|B z2aeoVH0ZRU&OobOFj`3#0{f1IwOk93Lm^@%g*gFH5|iX}A|qcXiX9;S$&o74f&L)4 z_eZblyVBSDg;q;gdoq%twIsiJI!XOVzG>AX)}1}1XHZa2`n(O$OYNnhHfmDdJfv(J zI`}}IRbLZOO&;bsyH!?YsPfRTDypheon32Jv^tX8adVhLvCF3e{oeYl0@Jgg!v?!i z6$HDdd{*Uwvy4{E|)vIv<0N#SPPF{C?(bUjc5iP6ZK&WpZ@RFGP=M$0gro zx`KdFP5a$3DLnuy@_V<84XCa5L`(1~9-I&WRA&#if@`;=8T|QqDDc&5QWm~rfS~4z z(weMVLC#9Il_JgkO19lsL0eQ1@$SNHQdT$no-5vrn8A+&9vdB!j;zc>7`2kHL52|MQ7kT#_WZPfmc@lg@sAs53`yYFW6@e|yCbz}~^+uUDY6UcVq`}4v{*`i(YAn!3 zhyv?*iFWgNu@nNk`tiW@Wz;cEy2?dP3-{dbg)6b?K5UQ@X(B9OomF@cXm!NrH7e~Y z6P#2np3=&-QhZg&eFwhFvh6UWjV||R&}*?rRbvSn-r1XxCIbI#i4ql1;r=>z%=uY| zfwY+Y%OT@-?HqxOdXig4xP4CBX{N6>ft-!pl+0r>I5>T&cJmHQLuhx7Q6w}sqQ#`S% z>kN%hPeMv*!VuX)uX;kel5Hq#);MYS!v?!gKYqI1I#-ln&INZn!CIoCvD)Nt#({zZ zgJd$f0MY?qN+l=qs^(znc!t${=iFsbkvPC;5;_U|oPG;kYqP{rPI2GK-%mxge#Vzv z@?pJ}hT}Xa(j~6y1F~^)ch4$XSog+iYQ}CoTaXwk2jY~S+_nCeQM@Z^#?(1(-JmO< ziZCOXE7*v##;roliZ!;?S7_|m+sMJ2v0<~+KZc#HW4b>z`~_@x+)9N)ae!oURAI=~ zjg>BCPr9wR43OSshp}4rQK{JUsyD4aY4xu~Wih?zCE356^*es``kTNP{5}2YzrFqz z4pR3#@K)6}G-7y!eWHjd8_;JkLxwp+@#5S3T{nC-li07$3Q+OR)x~Jh!}*#lhQ~6w zxtJsirz=SvdyQEU5>qH=_|NZ&PC_6X90hEEh}Flwvc>&4aWAlJA=750T>2AszW~8K zNqbmF_0!~0S*gZ|9)4pMk1KJ_^2#NTlt4@xMZVllQ; z-qNl&b&#^GBqc`U4wv&@hHMA?0RT|x%1iejt8^OVu*w0?mp|(Yq1&ei?F|$(5R?iR ze!T_iTu3wC=(BCO;3o`cIGK`v;@;`@%I?R2l|*)q85})=5xxt2Hi`$1tBw`H9+8jb zgBGbDvRz!GG3Dd~R9^$-(niQ~q}O4#;tU;i$JjOjAhbzKOw!Ws!jws~`q4SO2Xz)J zA9!jxB5JfX2&7ejo<2}s|1P|Kb~Z~vu~(S-DApNn;Rt~Mdn^E4qthSOPG}P=H1gDV z9V@x$6upbpp%$hve(n^_P;gYUhaT?P@)=`P-B;Mly1x(F7YMxizN^R1kE+Wz{d7u3 z2h&~4fxu#~kxt^7+ zuMz|&@ct=7ubOmpc`L3PG_U|rWp-0zvzpmOdAaCA{XFxff(BIo=et; zRKodCSv1n$0S;|@wr<@~d~=6_^23<~l7l6PZG-c<+47C{*L7$pJ2=87k^ z;kkqIrCW0nw8-~BN3)^m-3`8+NS9I{4CoBnu^a5h(v{Y0>3#vjWJAhMH4fKy=-3HF@@SJKHoi@q9Z!s4C7sO18)zcY z5*1wo6ZC;;<&1)eN z;Yf>Fef?18tf`S=)I(3HwpUpfatFag-H96q$YkUhg2^43!CLab=s<_r@?7nG+GwD{ zxoIgUwL|elKl~D~t}J+Z?wI3Ykq|>fKE6PF`_vK0?unw9IvIA?YQXkUdt^25!cf2^NEB@H z%QX-RpWfs1!CdXoQ*7PF0vi=Kxvi&#hQ0q=_}@J4KYFalhK(z)(pbFzr+ zyAO7o2X4hn%+Xj9jM}=QT>xG4#g5m6m6KAA(t*-Z;TkzK!9abWJ&10o0ba^05zMJ< zsFehjvA9H&w%d&hM}O&RDXsd{L{C>#S+!3)oaa!hTRh#0>K64y@g&JfA`BTDlKUnd z+Loms6(~d=Y1+lv7TBUg0h$J{Dgc{v?b{OwVokdi1C+h|B=xuHp+;@jPgTnfYdY4c z!Zx4V8z_TI#(+3bgh!6mnG+-|AiiQ;viQjMJR|hBT4@a$!mRB!t4l!giah!1kS99C z-h6O~enTS?V97jl@7KUwKn=y>!`g4K)+u+8;Z$f_Dhp$P@UJT3j$4QYT+(7{x|64R5B4BdbD-SCj-~}jAL1QRFlK*eDK9QR z2>3N6-&(z0H#{0;7=5mlrhFI|-( zkp56k%nK0AmW|}n29;&vd)N|Fu6j8Uh$IVk3$=Ql4ec^c92yQuHqFFNnTY_8i*Zo%Z!AGRKIThAukm8ZR1D(d0ht2#dz>kTB{=m z=z*Do1*xel19hOw*!9s}xL@EqtiEq{yC7_xJ}Cx=h93UHgVftEDK7r=*KaOLh(5Rm z-Q*#!7hj6D(p61r3iRNxcflM~^2I{-v?5jwJyT*$W#!r7MzIo*-ZK`)b#1^zp#6D| zp@|CirIWU(76v5Y<(f=Oj$|wg3vNdnoX~&DT;!w$7$5CkCW6Cb%0Oi zGHO#k^E;BeolPit-!b#P5+f+zW-Zrwg1{gvtia{9j=saDH#dC&iI;`a7G3}bT(PQD za6lCxI#)3sWPVnkMV(>8l?YAtekgo@BTvaKVmEQeRofkzvFOGwy~c#67&MUkpc*G} z+&PYf3bCsSRXaqs+=RsUEl^rCl*?B1WrwctF5!PJ4(mROiL+P?uPIW5mdEJvg&bS; zbJKT!8s7c=>u1<$pmshlAr?U1@Oq?7bB7 zmRp|_6Qt*)Kq{xr0p1w!5ncn;vy*Vj+Dg#{76514axF_+AHj+hunm>mk>jzuz>z$e zHb~M6#s^8H>`eDbHH*5-sB>{dIv1msZwa`q3r)G)@lza?#sWYG0dGkq!Kx@&W?Lby zBjhAa9Ucr!@DO$+)3?u=Tz(P2&r~l~ zbKJc=Vsx>WyVTc&OApz017_^hb11G3P65T@RF%v-{E{BwMao33N@vwoqqjV&W`cRa zyB!LNcfi=#)2OakCvpXP8o5Wv_6?qS^9-uwhC*IwCKIyTd`t$#Kun70z6eiRJJ~l^ zyHFS8-4#OPvjXoFR+YX0f|(`twbBYo25Yd| zEt72_W$9)10|X!1VP~;MYAd>*hEKz%;ZM@BmB7FQ-1u#RPYJ=9gE{ZB>~v$ z71FO`bE02PHiE|ZSg{OTBf~14+MCthtLixGY|Q7ne4j@r9D#NL7vr?EurJDO=Y0ml zqH|nh?QDL#wZCR*kwufk+ZXul^3rjQ1`-+o9g<3>9VQA09in$zb}f%5w@gR zM-ULDYnP2s9v$;F%2Ogy#vP+Odo9iDzUqdoU}e*&qX{)Vu`DE%uMK_rkyV+_KI=81 zRjhrFyOLJ;U(}LkPzZT!sgY~#&om`0v=gh~Vkgge@>iH>iCOh~8E8oc<;ElRwip^nD-Vp$_h6a>91+<_b+ds1bOkR%e#LJRzvRLh3lGqo|TNroEbdk0%Q#IF%iE3 zU_i~7lKVjr1AmcXjgnQ;1(crw*Zb7Or_KKg4vmD-3MQQPCsLHngfq@6^G^48fD~;_k$^i!Yb9+QP)zS9B?0q^E*+Dq+T@~{TPB>!v4&l3%l6PuL z4(-Big~Qaqmf}fCnU#kw`Dqr_Lv4H}xvJMo)wv0CH^M>lJk))_Qb#1> zurVdWWV|if~JR@vZ>sBN2Y!Xz$`Hl3qETud>T%xx8SMJd$x+ z;)qla?Cq|Bf{#ewY%3sFs?F5rH60X_FEXc8#OTt95n{1 z^%0I_aRod9;1_mhrz@FjatIWB9g*!NEF3xWM@+rJ26A~RxjUpODE`e})+q5HDKvJN zjq+U6@DyKR6BZJbH^c8cC^vWNq)bK$MmTO2&?Y7+{eh7U*`8i?0jQj-lk`O$<)nRp zLt9arGOc1+xGe{K9kAyv98or+6HJu?_qkAe?m6fIEq|(MVtc%F;OcU@yFpZKi|FuF z!ulKHS0IelTy*Koc2J&lsvJtRf8NgAy|2v@5a&C8^_St>zfJExeET|l`}e31yF{Kx2XtQ>P1Q~^~z$z_{(98kh2K7wn6N<& z1B*^}$&y=fcgksTXVnkFltiYkZUsHNXyV-amTQzB*NUJgq#EubcBF=f9LO<3wP#Tx zID(?$m5A#LtX6vL=MVX|s#gEz5hfmY)5aB$rl#@Eny= z(CWs;I_Xw@+?#1iY_Os~ToVH{=5;=45}D~hy%RWtA*3x4ppAV7dZ8XkK96Vq$JVmQZJ zL0&DMB-JU#yUXi9Y|OXI2O!Fj$N{eis=`k<9s#XvI-c8!W~v1oOGtC?r)qbJRT+o140go9kOZwfbi z+#9SDx5bSkV_kO&Hh=)UjZA8^(Ylh_I9#my;(;BMpV;N1%rmuHE_{(F*KEiDwk_;X zy|g$56d^2?7bv43*q+?M4I{Kwz}{7oS|EU9Z7TpKPm}!W-Bcmvg+%BX6hiB`6oBqv zpSgYxo9n1-P!J=SVP@EbCTvM0Vc5wl0Q5{J1Cfi_#8#Gk$Wh`=9iItgilZVm!!#V& z&#D?2C^Uo1q7XNT0q-Uq5IXv>FZWn;L%yzsJjxL~fE`{dlg;@T7T$^_Lyh4a+;b25 z89Y0a<1-i>mW=j(L#H`S^aTQqF2+Ch_(cB2+78qC7(g$)nNnh zUtCx_C~(#;Ch8I{laS0^uJbACJOJ0tXX{tp1Ys1Pp-A`(#oz@VAi37^!%qf~SI3Jx zC>Ba&h8LI8TW*|`p*F4?tT95(kPm$U-ZP}#t$+Z2Vv;rj|F#n=k-_;~BWpY2YR9l3 zednE}6;^kM(Gqh|o7t&efp2PQc!ELr^|I_;Juwa<}Ju3j#U+M-%$FLVP!(7z&o@$orc#s0w2A&S&hI3=Pn zNv+UYlnVyTo!m}rAqbfrVSIP*XatPmjL&AI?Snq3&s|lr$9GvOhH-Pv5lODkUBxLZ zJTN_G8*x6d<;$={q`ryHc^&(;PdU#E6*;d-o90fMv# zy+-3jt<75jh99s4_V7QEf22O`S4clPFQP5(rx4y2S|kvwgG$;8Hri@QWGO1&OuZ-1 zS;s6-BH0i!Q+exENy!bG*BL2XO8aDC3TvxyJFP3Sl-xJryI-)n3pVEVLj7fS{4X65 zQ*2kId&ZcQfYVC}fC+K1-X%u>-)KCVPJzL5)(0=frx@>N65ou0`bumsl;TG?^r1H8 zHt7Te9i&OhESdq-o!WgFU4sRPXwzDOv1Vsw?-N8KEnuA_Wx72g3Dwz~FcJ-qIeZ-6Y6n1ZW zkc)Eb{NKY5{)fIeoOOQRVOnkUqC3p3}G z9AYthwm(`)++L-rNoqXdDQ1^cm9O`-1V+Z)m3ij(S(^O-1Oxl3YjCf0Vx=Kb=!8Mc z9trSJg1CCqp~5GyO};odI<-cVV)bdSMxm_gDpVBuIV04Wxe6-WC8JclV6!J(=o?ngxFuLeW*yxvtkwBk9znsypcuc8^*t6P>Z#+GZ?=^#8sgiAXCYbRBm z>y*efY&ukcL@HpKYA+#cR_o}NeJ|Lf)WN_zG6P2v6VV4o#2t#iPY&BaM!NGS!6$8l z2Oy~6O3+(C!gT20(@Dn;7|lnwx%76cgB7RUUDbX{N^)4C zerK0l(&3^`OmZc7A&Oe!E5ECKQwwOHCHY&b@BA|T*}tk49rQrH{oBiP$hWMrUVpn| z8|iB)s2<;4&0_{khG7lVTq{;5l#{h_f~Dt}vf{vi+}tSMx_eO#)8gLg;8W`n*CMt7 zoR)hOs*&!*hAK}^6>SfRwi1HOaH3V1_rR`V-GS}&(HJb4sQuP?X2p!G?i!x3Bmwv6 z+)J$lAl>3l8C+!Ld*AzB`2Js~%lHX^)IUZl{w%znQ|?QQNumC7bSTN>8en79PQ7oH(UuoI8iTJjs23uJ+6P( zVAf}8T`fk_D~yyqWB8mGc?YIr`)Um{lodIE*%zl@o|zF%2bNELk!ProoZ8t*ggAjq zsz7n=Q5+}j0gX5Vql_|?%;s4}=ULTje33ORjRb;sc`0k0* z=udF3E|2!0OGa{73~W0NH!o-%j)kXuF*8_@4;fjCRvr`EbImFS#k_}+$;q1yo`6>C zJ^9=PdSC6OYY2Wz>$V*0*Yxx;j*I@ z5@@dstaQnX?6W65rXxjk^$&Of3G`CJw^Vd5^q&|v9}eeD$sq-UtpHj8$Dri_mGrg9 zW@RIgR}xm5BAd#rVEt-!3VcL&%A=C}(hm!CzjF{^ln5ipb?W_=%~9bky2Zt$k9m|N z!chegR_0PjK_d)Z>3rN1-UEFM18>w2MC^RZr!=mDdg;)GiUeum)Dxl$T;uFx2*)*t zzLp~Zf{g0taDp`IOu;Jx0NEC9ysFR~@=Lj|5L!mka6WCZJxi2^L|x^#%g_BYs6eYva{$hee`1_AUfu;jlD11jtZviBMe*PA z+;ONc_kK#Qb9rrNc#3vX$PPNYBCF35x77nZ+cN}88r?h*y&NjcxB1u(ac#hAh(e@p z2i5{p)02U&w(Br?>PDq>RVo_bz;$`%PSBM~#p}sNKvK2GPg+AGQ@kS@DSCCStn3?4~EXKYYWJJF!mRSUUT z8)v{kR09aYh|p20rA@Z*rw$R50x5Rwq!z9a17m43Xemtiqn>~p;YvU*vP6l-gqLBH za?I*lg!14jWte@IiYS^A-D-MO2j8HA7b1yzW$!4m$J|Y)?G4 zD|MbnXP#j<^=`OUpYr8p_PF?(UIcT)zXt9W_urU z(~+68yI}8VH(fO|QqJTk>&b!BQfF~Y36Y4x{$|ubIcP07+Yl%59J28aLYTknA;-JA z@=32Xylq#b%jOa&!GK)hVN{x-^smZu&Q_{03SnPGN^Tik=nZ?|p?g-D`Jy-|zo;AuDM)9PVsRe21BgKY;IdqoiDaj% zLepwKU%x=%`QguIn0S<7L8c^YH@08O%g1kE9Zx}~$?Ruv8rCKcIqAjYkE z^bIa$lFh8M0b!+To;00V7iW4Zz$I^X1u_{zDHu4 z+9TMz4kFrC^eweN!Ue2eiK|EHDM8bqzi(d?3H-_1Z(lzS^4lL?zkmnu7Z~z?YEOD+ zlA|_#9!3no!@&Yh9f_K( z5%X8!2kG*l2K(8ojV0y9#7}r5M<@GDz?<=pwQ1;H(z_1rjPVpH}-MU&1M3lJhj<&ZBPfjA>yj!BpiUq=l71ViX4KE49MXAw$tt}SimJ1t&i!~FMRb3J_I+dp%q`|61 zVRVMii5*M8*fedFoWTd|4v_m0NI1Lg3&^Q%6dkNo>}4HO4E?h|3x6j6OIP(@-abR1 z`10~HIo9v_IS^c~;@=kD;k%tKfPBF6GRqOSv55RNBZA${Phi;WD?_C&aJ*Kn0&h4_ z#TNV0tymG!8W27l7aU;Xwy)CgswJWYjcb@~3B6J}7LDj;C@MIfogEeH?7gI`jV|y+e*FrLSpf2 za$|q@d3gPbLoEHuy@PN(Joj-EymVRa5@2TH9Ws;MA(6!?JEme%FF#1{v`nHz*Y4G{-A76j|?xWYQ z!)wCjKcb7TV)sA=MQlrNmT_AA&kk=vpdd^y+GDBQbFC;+PS!6Sdx2wPYYN3ns%RGJ2KLWmKsy+h|;2 zuZxd$Ie#i}xI!3>X1h z7*-+=tbYp^opxFp<|wc%D6r8`qmCMIHfJ7g>J(y>Kwo=nF+Mw@P_D?7jJy}zYH*UIO6ajy!B1NZoN;$fbZvHk%>eI_geHIO zFiy%c86+yIM4EL92UPxmnUr!85d6l)qxK${XB}Hba~J4U`$jQAd0$$|bH)q=Nz6&r zHS9j{xll_Li*zqUE*5bD0M;4|n8sf(fBBc;FXeyu%1aBbj8XXS-O{8k+z1_A+VIK| z1F<k;_7M=FTltqILf`kGzCJXD%eiOS?bH&UqVt=RF)a%ViBToiZ_2?s&a#j zVZyDr4h9`aMcBtft!- zbY>ip0&j5`ff*b!j4MePja?R;Q37_IwHBpV#mI5=Ma9zNJX`iI_f735JC`C$2H{D4 z*Yf9#Kd0=+U;S0Er{&$pz|?+zc~Kxrnr*6VnyM1j1(YSylt0~b1wFB%fgb~JmIBIL z_X-j^+G=GGEs^t}*3D|AVIKx=w%RkDYXIHHrBt2XOGcu)L=}3Mtql#xZR$k}Wol>x z_Yo>H8-dWh`VK{ZFyvwHW=?Q!F=-)n6K80TRG#*i?i#F&0xRmEwvJ@ri2zJF>bO#k z6tO-|lZ0NS+ZrRe*tIUNs2c)kcu}2jZc;KD3Pf4Xw&4e)k&ekbQkGN}edG#ip5=OZ z-HE4kppP1$$?4$jriIZm^g_u8_n+yjBcjK>Jtiy z!P8l$ACE`PstC5tstR9o`vfW)ASvyk8d>JlikZPskhv)3C@C?m^txEk8M{ov7wRUf zllu(jZ(=xBUTc7UqEA2Il9Y*z*AA94-6%2ps+Q=dUNSx_4Blng8$RZBn9ZUU&fMq@ zD?IW15~eEZj;_E`JiS(HC*;Xmx>Yp8+G)#M%nZNwBf~HVK_O#{|19CuxZ^!6z~sY{-~-Oko%#HMM~pBQOb;nCP^zjo;@#=C zeDnHQNb)mB5k5!S#TC3`KvlMg7+Sl3g`150!wBRB602;k&-MPDG z5V5Cth=H<2)fh)zA4`Spb4@jMS|+!68tJ`T9bwtA;lBp-!Q4PJTC7Y* z6aeZ2KdnOCN><{VjGai;lQwhpm`8Vsd_Pn8&%Age007HuU|q}8z^=gYX8Oe!}q^`dHa1}TEY$f@$1j*cOzh)zHoW> ze?$HMMb0MZ)Jy8K_1}UGwp_fLD4YUSLz!VhC-?GWv|0N_LB80fTYsoFd`>Q$BW9Jp z*;tW)DyopJJ+Q`bGV{rIZHIPVCbe21JSuR&346W5)yN5#kSs0_B+Ro~uZWP_rPY;h z|ClJl^rX0mILil*2VqoKUpx#Z836V8EZ=!mL$s}Aq{Y6|YEuXqN!dc>c!(iwhg>8s zz%d2_TvG?z_&xTg6@78X^2~`B@<{J|6~~d1I{zw(&ejQ=)J)ry0W;b$18KETqkMzM zb$Be+hxA$h7*fc|5QjAwI2UfvyKm_6(=#<5WYLb(9e#v5Suscy_Eswpfmg!@N*y-W z@*g-9;~vqInLQauU;X7te&~q@61szuWPrG_fO=Q*MlCmxehRQiNw2iSgRG>T#bFw) zHD}*I@Mfjb5m@E={g^QMJ!|=ul7CA_7MFz!jFyUzAtD8x3{P&M-aXjndD(bH6DfBZ zFhasD)R@uX%q>W(lEKU9_`;}w@TiVc(({hMBo|>m$P-zB`mn&X5Zeh?_~q=ub0s|H zq*8b#{2qcnFE$K7O4alx;R$_&A!0&ZT6%-3unqq(dL7m2#LE3?%1DP+2MDcmB0;P! z%!i;+s8u>8lkyGL#0axVt8dbyBz&p$fvy;=h?hobQ$YfJ4n=0$mnC>Fw4;V(A+hK^ zVCpkBcGoKk;IyGA;p*Abd{4vF3LP@iadbJCMT*v>O;Sk_kgoFgQk4An*RL!|zWotY z+ZTuV99So(kA3gJMX^#+URsL12ZK-DRSLcDk|o))&wEb@0`OSG`~m4}W7rmwvgZU0 z_wYpp@1cJtNno9ld>%Ol*jo46bgDX{X(nR`xKnR#Ny9s(?Q-ZOp)&(%Gtg=T%UeF9 z2L^NpEn3cUIckmwh7eW3>IhHi0g_y#{`KF{D}l~odLSZpgmNRU07FTY#?H8gqE@bY zYg!ayYdEhQ27xMAM<|}sS9z*i$S!W%-BT)bD6PpuWS@y31;@Hf-iuVUsgz2iOUIyk zz-;^gx4-V&zK{okA#U7Mn2Lk)g`){rq0l}M+R>eyBD;?xf{9DOoVPnY)NAbgEwmX_ z!vH2FV7L~nq==`+M90z+o({^qqABN~7MKSsCvlVji*pH$lM)cMR;Mv(;}X9FFgwq5 zDaVr611*`gKN9DxaQkoJ`)G83&IRCxtf|NXw@<_xB|$OL7g{JFdO!!i;<6kuTa&}6 zhj7oBDEH1t!PrcFIXJwULpl00460wOwg(8ke6;6oQg;hDri{|&A}qy)gZ^;}w`sVH zRUo4|TPuvPY0X6g5@fS$`S9#`)E<}{NWvMQ2DmlK^K6nedP~1jEvK{Cs4pn(8cIB= zxbmj;wJ#?wXg{IjN+aJCD(enof8fxo@uZys!qaB6^m1PT;$m9`ktH2=)ykihbOq{o zaq5=8e*2t`b04R#{^RRcL4NtgyMMgg!E59+V4H3`PT9KiPICf_ULWb(AoR(7Ln4af zvlE2u&Xoq}CJuF7*Z{-kqRp&^jJn&ttPA(`9-tbUu(vll&zFxHB?ZBB31Iv5Cd7sr#!Qa=G>fgK;e zx++70!^D#(j7ng+EDp6jpj-ou7dg44LuQ z;Y~X0|0}#DPlo3MN-!-aj*sX=(?{62ZX9$JZ2Ca!K8_G^Wz)It7y(@rf^t*ROXRo` z5NMFEfNhcwfz#XgG+OFj1~GGQl4Qv zY-2vbD>exZNHSPn0*I?A;B3yRQcmoe#|i)wfh;)EVFn z=E-9+2Is0I?PT$B_V>(7kx@DiGVFOJTpMH!S#MUwxJxBxLPx~zXbS}_zEo1}@}4}` z@5AfApE!w-nXvQx=rXeXj$7lq0zgQyXjtC6H%qoU-K8*GFGxnWNrRKra^1?Q%(~Yg zZ^@_Lp%20JMi^{(grab5lc6>dGZRn?1pSXtu_GM>=f@gfqzt^ynoHhmSrrE2v|8 zH6{b~=5m9ovw9U6H0sHVl5PZX{N%idHPw8&{%r*Csp7;p8ksKpsTA++3V>!CWA+9p zJDpr$dp9+wpF^eoUQG@BpI-kE-u?62hjKKwY}>3V!S{((=Yv?`y$rq<-CyMJbQLGf zZFj}K%2x1`l*-Pb$90xZBE|B9s!TAhMYw7V$M*sJn0XT-Y{5uDQ$?Wkx(!^g&GNu) zOCDuQPSKY8no1eWOs{rl0&NB*C&=xMr>(puYo59WXf^fS<&!6O5>=rNNbCzvJ;w7E zpKal&4tR7Ml(L2rzi>g4x(Rb`SLis<$h5H@VduyT)L`iXKq|E`n+|gGEq-r`D=2Ul z5Nc%Bk-7d3QF})tLx9z_BJQXOsB z@x+lB;MV-IqYG#1fec&W1~;oJH_$aCw5nRddm~8*#B)Z~5kan#JFajl+YkD%2F*~f zIFW@4tcom_zYO7Ad9m7+kW9nI#d{hIO!*JM2RB?dop|!UgoxJS zLj8~%ghOr7+ZM$dMJbT#i~2F8YoqKP&N}4`+Wz)mQJ#K!In+6ZJ09TS$+bXuldHP# zVW)DI)cL&)8MY}Yl~RyECo;?4G~n&xsZEz%M;*{MX;fZ&fOv<~M+!EBaO7hH(GW^I zAvxZpX~fcUJCot2gfj*h4(lno2+~@#1nmw5eBH1iEb}>ON&l>foxd%NfVU!(6Xcm} zPfK{W3-%p6cvzHW$AwKFI}XnOjMrG51iCPX{>oaAK;Ac6jEo8^@uBQFM#7Um)Ed+R z!$C40q_Nr=F;SuYBE0@2y;Dma_45AqhnMZpYmMm`G95WRD&TPMN@5t(7fOlkW7g>i z&BSns4iB&g3w2Y6ij;aSy4bw#ecl$SJ)+&g#h7gnK%byq0EP4pLH}TOGT^*KLwG8x zhWdv20pSySR{fFs1AOsrQ_CN{J=C!3hHoFLs-LLbAtljANBY37In|mcWc|@M|LlQ>)G5F^V8Qtl+>sSiP94By3n8i${o- z!Ej8#YbE5bk)GXTb4F)je}M9=71k$fEkbg?{tF)wm`9Yi67Zgm3!rM0A+F zRW5U7N+s|_m(JNZC%~Hx9dgax6{Xie5hG|h;27dlO0X>%TW?gsyCn|^D0~~-v3T50 zYO7KTcfMeg`fdSsiX+H&s#3hJP_O}idj%?mHJkjCFs!D>ip0dZBOR|pO(j8+J#m@F zVd(}z7c||g?@<%_r~38)eWmY|jhCs0_oAPb%Nnz#I?Axi7B`H5Y;=bG-3n?Mf z5cdw%c9}=2M^u*pV`Vy07QE4h%maJ@|IU`b(2~QmNH>D~{nKDi+ z)DBCsamih88NE%H#|{;37b`z<>ohZ7-t2lHPuZXAKr(&rzcvtZqUZ;y&JAeywf+7Z zBl9Z-Je^xW9?@#=QMzo@&USN^y=pgdy5cA}Nk^U>YWc&lP|{f*nEG@9)}pGi#PnBT z2n)hxq{HW|tRxhcThHSPux{^WJ5wkqeg&;(^vAfYa*nstdS6D@i>NpPNb zd!VosF<4)%qaAxZ7G9vtBrp{2Jc=LtV7)oiCIvFJrvxq;7l-D)bj^ ztzuL;sq`mmUt)kFs0v%qXt7PVSg~M-6bJ3KrEyuEt~N@0a=X08CjpqF?I7e2ly1Mz zM8135qS(s9USfqPpJ0@-HcQd9%N>iL8<~dNS@RB|JZ_gO%s}epxWI|aMG&sVD9``W z>rKw?z%`5u=3=9+l|*qvrGc+v@H^66flcVSsBrY*vE4_(mTNC`}x$^8nTKd6$sq@lGP)!M0d+8BR4RT4Ts0 znzU#{w&^(O04B^Dj&SD^y$)kx{VrsYEImRy#d2fFYX`u`vQa8RGDtyX)JLP#8j^xy z5LEWTU-44FZcd)W7Ubl;j3wqYJ^2T&^THSWUH|j}^m4IX_P)S@EDHL$%f*R#BO>=3 z;NX*~0h&mL#Y-v`C@BZ0`x{#FA=?>9g_0#L#{HUTr|QwhDdKJC5Din*D+fn~)-y{M zUYzhuKjD$~23DRhmkppnWV1VLqKQ8Jxn{YXSVOvvOUruitIjD%D#Z>BV0LSA5GJ`B zSczO|C0a^lXi!$HyPr=`0JUDK-ZS8#uiX{YT&n2t9m_(DcRIC-P{2@%GKD zEwFy_`deu3|Mcy1dDf9V;eL&WzHsu~tsu5F>^I_p-c!PsLnl%Fg+P+f8t*5uDW{9d z99l^U{*KL;Tzz1!1F?&skUBtYJaH7Xl zX82okc!9EWB9s)}8E0y15G3{jrvW~XSMpgl9*7R-@+%Rd*eSr*`L43if-Ai&y}h(9 z>*xGEeD@2NV+cG9kBM%!cPt@^MZM?o9eUG=5=e^8gQf%Jf8=Md!RUI6y>j9!_|e<^ zY2@x@?v(muw?J2LmVQ0ZDz&_2w>(yuwA&N%w4;1%FP&R9NwiWEcurrhG2cd*q-@Ul zo0T8TaE&>p3N>&G1qw95%W_d9)^`i5LBObIEMPxVXJP#sAn;W1x_2sHVf(;_VU3VO=v>lR;#AfzKvyzDSTw!U23v;$5B`v@dF)YGar1MJO#vd1(Vp%S2h!yR;_8-$=ModSW9ZL} zdvI6pk&ojF0PyCyLB2t7{=+J*$;?FI&ereA-oh;BL=P-6=HLuMK1sT)at0LL4XA{5 zvawP?qp$cBMYphmg_fUP+Zzm|LDadyDJXq{Td^pC#$wvS@A!1fDzcEfu8J8@wwFlv zO{`GtD!~0s9$so`m01V13F|Y25-@pUL__FKpQoM%-+)5r-frEHZK_?tgHpXWcAce6bnU-CKm z(z=^(|BeU#%U39!rPm+7{>*T3e|-Ij79{CE{5_wz^f&cy@XrhjrplkJveXt*D#gCY z&*RqNWcu+1B*snrR`sV)#~zMEr0s*s#1z!tN56;_%H){u0##i{@6KWvG_Gw`o2zj8 zHpwef1L##0%$^}6^7K%ExOG*bTk1M!W>Q33L2WrSz&8dNrAkea0!bl6|eMO2tsm zom;tJ<@q~MZB+M8E-l^nxz^0h0%l%%4i!-QWQjo$- zi$bzcK3UQ!>o1g~f6(?eOQg|~LJ8ivF_uwFV?S6h@6=q@jx6AiPs#K#cX9*I5pt5y z*|rNjVseFVdV!XLJ+E*+)w#4{#6>e|INck|XXV~fO_Bv+Y~@K+Mb9uu67 z^P5g$_pB@n^pM%+T&sI{l#LEeb82EXSyy1QOG?>olndOIq3d`Sj=d0d(qgr+y0`eA zm+!fDl13vMk@%2x4D^~B4sND5dwOcm;!|}{t}lJmd9Ih z5Xm+iJ?m=KCem?at&UJ`*D3j7jFb*odnw+f#AP>6;+%%LFtM~Ru6x?SJWSkwFL6uX zJqKINd&eyFh7K+Kh0!OVRtV0f+g>aO2_z^lHr2(A;;($Lb?|$sZ`&c>1JMT8v~Ajv zwn+fL?#U{8}QJTp^3c@HyR8 zxNLIDx|E(b*6lNf9|lhWyG;?$wp`%bX-|K+7C{IL;~-qdMul3co*i`A<;nOkBSG#1 za10RtMK#MtI#`3It#ehM^dqd&j~AvoXKa9TFCpp4Gm5hV7NY|++eHC@9K9e$dwVrc z4`B>~4kws-OTN_59e2^RYHyKbLwaMLfm#IX-XboVliH&E`0Xd}e)#s$>o4>(pN4NX zGOdsK=l=e`{nx|Sf3JU>KlP*RvK7`lfIzPFJM4Y<|FVL#k{R${S94O`S!3DX5H)SC*BDS3vf-L?_oR5!SeeJqA{jlC=c zrU{~T*@R+Z__bC;nKZwB{LPOgJb(4}G5^+&%4dI`T`44!%%6i;&#(UH{~iAS{s*T= z3>%81A!NdV?LswT26zw{;Z1_)OoK}O6PJlZuX0lv%=|WmCUU437et~WCze?0OZ<8y zNso33_=!Lvwdk>4O}Ni?k#D*5uzkB*sW&|sqNmQ~W8unnLh?*)oe`?tY^+Hkc4ccv zq77OU^&;W%k#aiSYUn zX6x&2UvF2;4`b)LM`XW0rGN5_KInuSXG{(@nex0WGM4lch&-%<5nOiAzWbNL^w5Mo zsP9M75x`{5)7~Mxyu8@LZ`%RO3!;&ACG(Qs6kAeJGj3OlReHt&2SZS!$`94iad;0h z{e~UP6)UNs70Fh(j?%4)QILW)!^1eqV)dBh=7obQoL3A|#H>iyk*5HzcP0EN%VxVy zDuB3bR!xdm?uQXYN`IqVzcODLbU<_SPOSQ!7wtd#NLF4MWjQa5>581!herlX*0w;Z z1gaOFV!PYedz18{x?k@QL&$cXc6PM~v6Xei-gUL%^}>c!#AtvC3Fz|ZgfO$v)SciD zn6T5*#x(NywHK1l!KcVCq!btdXO=W}^BEGRxurQF>B>gQ8l|GkF$PXo(rDNTP~Zp& za$JGAC}SlhHWiC?buO*kBNeD7x$wYdx@!A2QvEM`(Xz)0E zCFA;+-~5=D=Ft>HC+s8T4mxeOFqt;3a;6}|eOL&ftr%7@&0(Vsn)#bQUq7SngiBW% zjE`0ywL-~F9;4i+t;AYt%c+{I(aElb>pV%ph+@v3?Nn_${1l1?nIx5L%8YTVYVB>h zLRC(h7f6rylx`dL5uhc5J0Ge1SG3WXb7o2(JN*D~0L1QqngsH*@wqcRlzRFYc7*D7 zU>KuT+a-fKtNE;Q0%}rVTa}x$!8>v#*l;MTKCkT(?{LMU^lMs2=o*78c41FlrC}#i z;wQ5f$4kMNa(>y*pMy5ifW=@zz-AId`0wP>|N6V(%@`*C{Q8@(zZdccefRe3pq_>w zpI)jaW3)1r_Q{eoWn8f+L|?Pox*lE_1 z6wAQf?=7_BZWXBdDxsLq>*S|ubTRqAAU%3G$up*VGO1Uzrs z!rBXB7{pztZqC%Cju(Pz&@0VSt7|kQ`A~@Y>R7*p-HrC35C<#I0a@l@u6??6S_%V7%Oc%_;ggSnadnVzexY|StSN~ls9In>X$mZnx7MkTJ)-wI^VwA6A*p-N-mJv&iD3f_ zA+n?dcAIZt(M>LSPa#Vr8xE$o8Yk7<1sgX{SdAaIlV%+Ci^tXs30c+T?vy z$TF+a@jSx zvcyN1!A>f_kw2+m{i#V__mBHgH(bkpB)Sz|w#c4ULd))J$itbCfpVj7=E~KoSE$Mn zN}*XKfMy`gDM2y7BVCVE^E%&NLkO%6JPG8iGk%Hyw-0ir4| zTC7u`Qg3-&>poo|-9lQ95}KAoEX>+JKeJo9c53X&3cJH*Kkw8i zu(>0J*3kO0A7{byKYg$#oco5qhd+1jW>T_k=X}}*_ z)*VYl4=;A5>TZs1d#VOWa^ggnNN6&X*oI1&6Gu=pZJ$)629hS!)L?S-#rv*?|vjD-0#A>AH9D0`gvyF|M-9W zcldLEpk3(KUk3ePwZ+J}IB+#!T$24A(dj>QfcFUqZtSHZH)CF=8*!Iv?^U^A!n!w5 zxv$x|?#ErX3EzidzedC|Nj7BL?z~nB|4&8y3%)p(bjlq?shsvtiCrr;i2dj+~jXe}@ggd7WkczjM0GM>@?kI&R5kqb-W zt#P8;OLA0pKS{Lwliha`9ndyPQFC6%rEmrO;zLJV0f-l@KQ<^ zPa0UAQmB1SNCze;DIh`g=AmTTXi=?@k#~`X2_ZtRBAWnUK%c*dTzhq#Bna#p9{@5L z^In19^uPf$(yp~G;g+;3C$)&#k-HBT-&UhsAj@p+%p z>sv|IERof6daD$l?ZPn?T0aiAUZlLBC?_bY&=3asV#Bz6D*~vssswNuZ>foa)LA&P zm5G~H8PlP?S6ae%&Fy&{w83LstzFIUBya8ge%KOt#fGCh(^401%#MA;WZnV2gFEC% zjKrx+1&mNFIes^lFSULKQw%g6$cct2oIU0l^Bv2dt1OCVi8E@KuvEFG8dV6|E+c5l zppURQc^tld$ix|@AzCuqX%F*|Fw)+4b?u-*iYc8fV3-H*iYsH-xV$b(#}Wj z#4&lmH2cxKH(rvb5obH0)sa+MXEOntW!oqlimQ-2ULbhZU)*M|B~lTBby=@2yndqN zLrDcs(2VCoqQ^d~lB~C^j(F9r|UDV)7p0-s87j?cWAVLUE`J%RuUaf~V^ma5GcU9*03B5ve z@rrfQj&VSP9sqD|?BL2zhJ~-rjw-2p!t8B7Jg=#cm9wiAqxG+)(UEfx+mI_)kCsQ$FQ~JOG+0>TS#%bOYqpt}wY3F0>v> zy-N;2#_&=&-?jyyfK4eJIr)&e&~_qO0kX5Kt8&FYBBZ?!&nFb26LDwqOg}!I&wFvK zLWx&7C_r(t2XpwC)vhUZ&P~GWT?($7EjmoYNZ)F5cTBB(kmA%TBT!UZpX5e*#8|kE z8G-mvOHj*!?ht>s$la@i^@UlE$`|%y=J#G@hfa8@v|hML(oAxZhpmn*#rtvSA^mxA zSS~QNb{e;)1N%=7$Fui}s<5_l`UY}EuE7Jk?1Jz>ZKuP!bk?qU&0+2(P#dxdk-G7+ zT_glIn_MF;aPo)__YVb@mC>Wdm_%sUY=h=gSoqH^`@r^MRG$&&YyF zpIIkG-9p^)*i7o7bsjku)ZJ&~yUqj-*GbNUg005qFq^pe0CX~{)P+C_?J}!X z1Pz`QIoQ+D`dYzdqw7Zo(2;uBCCTWa#7=8k>g~c{)tO6jT=T0X!4e~wA`vGf5*BPU zcUHR&t*)$eQthhMi!4bgZSBKQSN> zz;fi3SgZS8(cqS|$XUj1+3+TW`I($@eIXxlUvC~H;S7i0;ZzZnQ=M^!l*iXs(+TFTcbzD&po}KY_M?w$hj~H`cDQ zRo15m+dMXs`hKQ152ktK(NB^*K3Ud1_Th=LdgEBExyPz+ofP*} zjan!1lDYU8tv{oNdAL<@$1MTzF-22Z)LP^SxRR&bSlElybHTk%A0R?b8ig#a9AQHD zqb{9OpulVuswd5Zb-bGk{F>MV5ZaDy(qFJm>RRp@*Ya853vI4PRTEfG%9@0|{G}>& zaj0v`hSg*A=(#2c^rIN!bUQxxz}D-)n(3Od;_oLcn}e4Ud9<+q@z!&agyd13(o=|* zMkPn%V7$F^vqwlpjfze#3actO?YCfyz6v|u-3lyv53E+|LM?&Cs_2_#Z{qi(mF#kcuF=O zOXM>)tfWbVDEe~-j|(Em_aaJ%9-F5;Bh?F~0=yUahMK@habfWSfQiceiZ;N@3$)hV zIjdnt1`@Z^{~G@H5+D8zTrH_vAHMzZ?I$)2{u|p=7{~c+4(bc?Sgp~vavrs<`&Htu z^+!9kGVZhl+;H2u_eZWKdVnO+%c6{Vj}!)~JXG>s`?e+0a7EzgH0-c<2eLTiDo7NH z(EOfDvkrxlAq6TP7S;5c)Ljl3`AA?sL>AaEDap@MAuBw2MvZOP?GiJZti6>t3)G3h z(2Aov3rySq`=B;&9F>4(*eU%RE+8xY_ul^K>LC9{+VbCDKM(Kz=`9mc-u(b3ZvXi9 z>FGs^DV?vHrkb1_eStf+3Pq6f(L#N6Nw}$5Km!arxIP-!22mF{FvIQdIhB%}E<+OG*{M+R z{S^avd|~!eDoC;#oJf}H)A07m>D~9kYhJ6Doyt0rmng|9FV3!gVrcwGedjgHws^|E zjz=}>^(xh((-QjPEsb6(jDzzV5EL9FTIC5sBgL8HZ6472x}?P^$s5E?X)Qd-?>;yn zXx!Vd4nDHMF2>WAt*4Fm|^`J+pTaR#Fi9hUqVYDiMLm@Z=f z!kJo)$+OQhWRFg#3ayuphkQ`qYTE=cxCp>se=Gd;x8#4m_>gP*&_DfEWEW9R!s@-J)<*8EdwUIYi2XGI>1^Na5)tO^l*-K4dM9pET6+{2>!v19&aY^*y zOe8lh!G&~8*^_p4*AO)FpgC(kPR7`>aW)0fpnM=75bRu7HLlwM5qMXPD1)ETVdkc= z-JNsQT|KSSM&&fwbksw`n6XGJEmChBM$iD6#-XxUBwvD1?<6@F9II3oouA8E`QCD$ zm9i7a>z*rApK6gQM@wEv7J#9j`?pIbx5_41IL;qjr$P?!+JLPL)P7`ueu7ZjBQ#81 zpR{MV^j2%2P%zexoWuOUi;p@$lP^$C)?xe!+IqUh^56c;@U3rstK3qbzkZQFhSy&sVEr=K z0O+A*)am}1-1O~^B)73>qa&Zi$Euh+Ddvgb_-cabtv|61O|zi&I+wY+!D+T8mt6An zCSn8>T7!SU-ZiP&ckd4NtB+bR#4i&>^@EdB$aj+Db5uITY-o9!Kz(1CykK6iMjL_- z)ivsU3fJhE=xqpUBZzhqb%vFgTy?X@9EfTJko8C)tpOXAyk5IN?>f>}W}D z2jV(JJ5|0V8KMrF6{ZI!_pSUN>5o~W$hNG9g!?ZTQLf#W;@Z_pEpn67y=SHEnt_vob7K)YP5I)Gf2BeAhT^344O@^W2unckH@)v2b_C%|2!r9eMWf5U+(zTegJ=je|-A_ zlG6rz)<&78EQeBS2Zk;!#^`|tj^~Bwj5%-)t*N_f6D7H;VSKXO;pF5EA9zPVKv`w( zd^}n`9_aj3ytU?p!Xa1$_`CLqhe1oOKH6GimgJL{652zu_8Q( z2ReruM&5lbD++%uP}xj%S!IkcGHMM(Kdbj8?=H~61B_)n%qRWCy6OwAk;JHHj&`+! zkt2yLlJ&CW&=V{^wpP)MG#Sh-gfntc`U6dI~fYw=F!U zU;z;MS6Ftscj1EMYuK78#);vkfsU`#KBgf$04d|8Ehq)y0V$-s@RDX3HlV^ku99MF z{xg2$Ciq$T(=P*6EC2cRH!xnqho9l!_>BDNr+HU|=RR-uJGf967HD$B(&MD+iTNkJ zp@WG;W{hS(lNVddj`OfHJy_##R-%O^j4Rg99SI}}cf!GSH!QGRMUJt@)a2a5fDrVN zsrByMy>qEYaCX+olT*$3qTGK@Rvg^I9^GvC!d&64d(ysi0Tgtq=~_`&9i}txF>t|z z5>v_9vxN%;Htl7nOeRRol^)+2Vr2k5k0q={MZ3v*a~rj32Ze;0V;dph9#>S6s^Go2 z!Ll}{&S|0p!diTEsuipi4jbT}X5_-oN=cP2{SA~#|L~UGso#Bqr1z`SOLfIelL{uv;tn}uH0GRhJyp~Dxrf?V zSPlV3iDV~CU$J;v{+VBea1fXzw5co03ipxSW>Dq{Dw;U^1SRchs&-aaN{hM1iqIfpY>2gXE2UB zI~y!iH|Kq!g%7-q)Tuy!a*RE=3kjR2f9_-WbAP}f|68dn6dL?%5BpGtQNp*r`TX?a zHHyr2;3AYeVm4cKO<6_`jm*)OOy?X^l6&@rPo?uz73 zjRAYnJOEZG1HX;8D(8h~R2{edk%IXzXJ}*hH4_@HV4o_1bOAF|uHW%-IuB0V1ufri zV0+cxLH3n%t39k7V-x%pE!?6JY|Eu&F4<=uj3MyijXVid%7%}wV~R_jwC==~Q`Ig& z3aLp8;Sq|s`6L5wa$vqm;CZn6+ES-T%*n7xn@-Q!F+kqt+`g(f11yj?owVtRL{Z^V z2PV4|TUYz~Dsq8YbFqLNAV!}S5~nWU15NGeibyK|S{3~S561lXuj-~jKE(>+7R?5_ z1XG3az)bD^Va@cw_5{*F#hptIMsrbT2J`~HK`R-Rjwr3GqQe3Z zCrcd6v@vE5FV)%p_@y5?-Fj$W;Tli(hdgRe<#rmjslnquj#LT1(MV09`&Wg1sOGNY zK2*$@<9Gl;7vgVG(yIPs7uC(#zGg~cYvlqRABngDFdN3|q?EZx|4=Dvo^36I-O<2= zP$+T020JK~$gh21Z{SFw+oz4y{2x5+VSd2EE2)b?OC?3<+C{ADSVZ$uNtlQR67V#~yYIZ8@~o>A`skvdb5yNO)e5%*eNrYG)YF6r+=U2ENfh$+b4U2ym7gg5!`*wJPQMTid2E0WfJ+K5LJ{ zrLkapLu18Gqc)nPDh6!FmI%J3)R32?QA#p&si}ZB*>ogcIk;~Z^~#2jKeCxs65fbj zJ3w-N5Ncc&bRAdMg=$HJ2K36>27b?)OBJx5MHStOQX3cl^!4|DMq}#URe%gus7BHm zqUO6iWv?jZ%~ENcl^jL4O_~d?oNUbvGS%o!poL#uXy|fx9}yKAQuBy%zx6seLhMU(I=a@kbd?E2jgUO&UEH=}cY@cLWW;h_hqRi;q~ zZWsbVAiW1GrD|BZKy!9s(>AMJtfv*?k#f#dY$*X-{30o=3%;pRmyfj;J)0y>kHw{S z?4Q}dRO=+t276`xQMwmH-q{4?@kE!f7enwl^NAS$(EPTzD5+{rhau;NYV%+OJpj(e z$z!2~HG`s`hiIzVwUp!b^VKn?1)VspotEw`=MfPUgVdkei=sCm=A$x2id;kciWf=W^?D2C0b; zpYDowgqtLJE;J+|wRQB43W+8T9MJaQP=dWW976SrpS-Jpp@KX%Gz=Rp$&mlZA=s^| zh$SDM@D!`*%4T5$oFfmJ>f=oDfRTQtLNS4@RfLps+@IK|8wYv+=+79;xq0=ql}{Dz;O54uN=Ls zQahPCjU20ca{f+SEz*lkAzG)JF zrMkfw(>>U<_=+Jze!fpo(y^;w;)#V86)Q7M8hGP676ENQv>@2xhYw35Pvs1i&U_cZ z5%IDlW3sjBvVKGSq7Cca)^LGZK(UTCzBju-`<8$}y^xiU5bf3DO;w~^3KjAS@mj0%deHfMB-=EgW#+#Jf5Oa4o2cVf!`6^`2dLKYgh$YnnJ zpr8c+)1<&Ch?o*;^}Yo+l!TWUT^|734R60V<`%NMO^)>j@fF?|djaH2{W?KJ%C$JP9qV82_kHgxXeSb3a^M>29-6!VTo1Zn93kal_bKJ{EhDz zv-RGrjE1v(v;qcl6_RZPR> zl5421jw$-mYX(Tr0>(K2v3kMnIZ$0GX2U9iY6~}8o>2jyu+mkMVise2{S{NfKjdZ8 zDw~6Id6BOwz~(G?+>>fiy*M^wp}@qg>JFCRbOe;@5!5)!0Bo#yX~eps`|T&R}LzqwJp0*4hJ+q zs!`dWM>&vd-W#VUNp5PWbJORN$p^iR=W5C{1yjy;Mte z!RX!wNtZBEA!(3HzmvS*t}XD3i7kULBO#|=SffVu0Sa?~ZMbZWC9$aS_6j64X zyW#;*>Z(pO`4d|GgM?)F;Mk$SKj5tFt{KK~1&ITH+2SFlRgjyrG5rP8W6Sw>DUOz6 z1|m1uo$84@N)GkF0!u~Cw;UF%zGQSI`=}iw*+}ZtYeWXZvGU2b{FKyPT;`xdb>ty; z#swII3>3e7R9A}XZJ>~Aoi-Qq-cA)UMq8bAPI7NQhMUWzse#I^w*m1E`ICxBRLSk` z@=&6L(XOiN1E&GIec^=ZT1hRyA0NbMIczvRdv8UP;y}B4*iRqNl?qoy=Xui6wt)G0(Pc{%6ns;VR3m`<&X<=3u|P468<8;RnNj~Ss3!096@V|HJ0XOnV7rOp1cvw zR@I}ZqK!aSkrZMk9%$432tXw+^I=geqV}L!0xi&NAQJ^V#tTYcBD^LV8Dc3RW6JB% z3cOJjQ054kU#t_cWy3|T-#+_3NfD(Nx$DLZc%Pdks1ekiB`i|S=}S$A66UNdmJzajLmY$_f?)eG}@jt>UC7lb23#GXg%3>#&C}uv`UG1 zwt!7i^j*kCS$4}!rKc$$BRnrRvJiFf{scZgNZqW^xIT#S=aEc{6a$S) zXaZSg4h4eK2O+Is5Y6F*)J%!pu8BUVqH@jAot%pG!H(H*pgdm(C~CULSJ?fgp)tf+ z#}lrk5%r}hL=U9k5{S~DlKqa?k4 zDUFfP_6J7TX1s<}9`clGXnwh_j&_~ddjgg(8yVwYXc;m`?Qa+yF;W6^0q>)wM$grXc?JI>fiWGeqg~+$9dHlH`4SNQYYH(KiFzXIv?2H!eFMNSv~3z<|hw~;w(*trEsyTyw7aB zNz|90j)ML8HXXvv0v)a!7umuG>=Pec%_XlbmJv!+jjd_eZFsObDnyEsrm62zOTjDc`y)(lu6Sqq0@W>RXGx~sE0V(fL%_{Mvn4ytb`=yLafvIvLZ+vc4d?X-PSwVmnI676k z7gzErRH-DRw_!6!VBK1)j?`P$^}y<)n8XB^0;=`yiI!BfQnOOW1mOpC%%0<*0LO_QnZ&jCYH>-%HccC(_fD!MQQbTT zLBnNrFc3szeB2{n9HL=%l;o!?7wQ%1(YsG!`F_`p;R7Qm5H?T{kt_WQ{`~puLRKrl zo{!}4@;NqJ)#!Bvl1u0_wb@!)Ri5q`e56ZCL36dAwS(c=%(x_1iCprHA%GDNAf%9mGOw z=JaxFk6kJDAVgAeaWH4kFMDWtt}&gnDi(VZ4bWV&IW$W@;E#cOuh=-mS`zJx>D-GQ zo&|jqzyyJYSkTA|Ns^z|rKKu|R=#MghtnUqE|Vmt+Ilqazgy)-%eclsnzA>r1fmq| zz`A*zLHeM*?HHB~r|BN13K(IJjF~lL6tpB^dQECF+$@rm!tbb-6iEJn8x_R_G)F>h z&0j=R##1>1hZe+~P{I|~H#Iwwa~?)eZh3Uk!@lwI?3=FmheK8`zkrCYHD7lB<1S7M zepl9X>O)xX8!6xYfr2N2m9!VR8OiHL0gc)c~QfxvYAxe4(3NPmpDrBA) z{9y%F4FnPjT!PEzKn~Npf5KSypWc39*WThxw_PWb!7sSl$+mz<`#G1osQe{aDLF@W zy?nq#7oM7)uq?&CHV-D(a7!DsVJ#Z~g7EpIo-g(Z34tb3pP(i^RP3hdzSoJBVFMK> zQ$%6Af*!jJC?cHzb8cwzCCO%dp3`waA-}}4v*6pl*$yJ}atM}9g2iJzX=f;`E`ZZi zgGtsigl-4JT3lAlMnJC)t5(eaTlf$8yy4nCJ%Ad;T_j~e{?DKqFWg@ zN4YJ|Jqp<2aXN>omgMjXbQ6V>(TVK}$E%S=Kll#Su*|W6ye5mJV|1JZ!N4M_dY^Y$ znp&v7Uw?Ld(!dMB_bR9*dfYzba&vC z9l8~209?}1y7hD)*F$edK0!O!l(n9EIkN0SZ{DJuV(@{^b;6R-#xA*_|CsMwIg(1p z-FW*BDopDt>iu`i1Pw6R9)%Tp_W+k92bAQ@o2{(LY0Nbq?)g-c8SnJ1r zqiigsXfZ>A<>ndEcu`^`SdBy}fUxVdP^golgTiws+ki8o1-a)l%D|P&fI%f03)pnz z-6Gr*$O`WGlL?(ed{9a1YZwL#YD*7yw5#OnDZptNE%TLlI*?I(9zD1$wNyVn+C$bD zG`0Br8sqa-DVG?2t0Xq?-WFJBkj;S)dhb{{DMJaJ?cg9VQRhqIXO3lOsfQ*-o1l%} zTI+;y-j-yCN~XiJ0{A;cN2VEoGqQM>T_u=u%+?}O$D>wlkR+d^MF!G8jsmbE-U{budG9b|p>jUL%*&f?)tm66H6=KrNHqt&hwi`nMG3O%id`VhX?x94SBkXly;PO-uIsu`tUbCA;|&&Q-;uqQak&o{AP+UL)+g9 zm**g}eT$d49pK^nNcb)U@iyZBXO84o_Wns)M@TY)859NvS#02@E>- z|ILn^BlEZg9E#+V@Q1_%+2j**`~g}T@Ast!`W9Wg3Ub)}#xh%~>T86fkO^*rHiI>0 zo{kV~vo#PskTO}|!#HnV9_V)h48`f*G#8je-73%kB_di41qes`IO)W>OBwZesKD|o$X81F z%If)^wblvR!AX>a#&Y*uwT&o-& zJ2#;%X_}ntQO<^>p+Gd=!fIZiUF_A4aFXY$eB+IP{Q(3EN?M=MqcEH>{0l3mO197= z;nj1&JUb*eXl|?JejM4TwSYGzP#kS)YKe*46lRmuXw)4!?Zg)2=YSSIYTR05XDB6} zWC_`KUElpx_;-I(0_9KNe#npEo1fsv{I@^*`oD$OpJ!^+uR{KCdVUViCL76N(p_yh zX@FF@`0uUv0(w)|X3k3izgmeaK?+to+7oOPx2&Z@7d;NV_B+Gw5$u85|^A?Gc8~|nle`dKu zuGC&bC&B82i#-Oby0SbPUDu&l%lS;B{xk#*0 zSjrmN!95Rs@mxQH+t-2?w^hjQNtJ5)8fsIdXiMNTN2JEF>&*cgS1(l?vXNwdFUObN zOiWc8mqXopc_{B2zWc$jx&fs1&rhdEX69v@a&?;Epn~y85 zPhWnpN7Gt#y)xNDhJlr1M~V2-{^(*u{kug|Lu@2`K-k8_-IwI^i%GAtrO=LH?V;o=c%CBRaylfk)6LF* zoxJNUnGt$^6!P% zOqkd9M%0!nDP~x(t;}l1tcqQZ=&Z59BI^|j!#uhU2zXP$k#%6#P=5}C&e~QGb&GN zZ6L0vQpWXCIJL6iu(|*&1?e;7m`37Ppn;+1a`ibIE@@N}7DzF?^%am`9-7@!w=Syr zv`<=umu~uo371y5h@6z4#cd`(8#DaJ1bDu-$Ls{ok{Wf)KQ_XvItbGIZF zB+K;7r;47fo4oYrO$U^A6r*C=E=JNtp}=04}NelTvOw|ER3=WxXce!MdWFlZh={ed?tdn$^!TZ}m+S?zl2btHf4)mYA!YSnY(l$uuV?`O8QJ56}p z1h{$$mm+<6rv0bfos@

xjmfIWPbQXBQ-hJBfVL9X{NWyGi`~VAmgpK0ru`tRpDL z)~h#wmoE|g{oa>e@)8ccC&>P94*-EA(W!=_h-#S2KaoxTCnDOvB%=MxER^~ggm~=5 z>D?D!e>Z&nJzTVsH2wC_EO;dC!9Ajxq_|qsfX%BUYcC}-c+)2|TSB|(Oi07zD4z8> ztarne@h`6s%9;IK56T2uFo?ABGnR1=GyLmc1ws31k8r;5i=@k#miQD zVXLNotV+?LAnPi(Vsc8!W^w_Fp10``nwg-tkk|-P<42&3Pu8RWamNzd?5CHXI;L{2 zr6o7_K31oVT2brXUz0zWwUL^@5_MHYkSY*kq0YjI+6pA4D)};jRUTod5|?vqHrEoG zfd~fz?Be~3DUwJa|G7LB`4;!4XT8rXi8q+Y&k8viqug`2YsyvALiTVvVVBAhSB$wjUo$C!sFH)v; z&OU~4Ji9m!HPFtwL~(0jLajRg9N!5>6!^+M{zgCTo_enMe>}mzn$)ZP)%pF4QJls%1#W$A~k^AQPs!Jg@~3Ct{4k{q07ohuaVA2JdX{D5F_jM*5K zceFd7%f0J9W!3Tkaf1>qmk=K;qqG9duP|7w+(EL#R3fT&=IrL%7qnV06lVoz0cv>n zvH`l%#zwXvUqFY5x%o;m7~0GH*^typ9beS2W@buV=<+}LZL_GKr&Tb275)#q3Z+uo zr(WA%7MgCoiidMLHK8|F)JG9<9|Il#ROBeX>|%9RHX`oMRPtowHmdY)meIj}a>?tc zB5V$_nzcn@Iqr+)COWE04k427&-AO&#zS^YakUND3 zb?V%>Rx2Sc?&KGUpRP$oGWYC%rnRh4uq_r@8RJQ_Bn2^=SXwv>56i5Z7^cz!KXpkEv2aBBIPMP*I|aM~&rW z5wpNgiq-FVS1KhbLp{KfXAJ-48%LtTZhhL7%ShlY{QbN~yO-{huKD5JP$c1yRyoPH zLeGoO_yPRp=|Rc9dnEsAzbS8q+(r*hX(Mz~l12IUMvkneKr?>zOR zPK}|%?z%UN!xQ1~vI02cvLmX4vu$kcMF8EA6-~vj2?{ss+S~C8uOGtM_LovzvE%1=%=giI2-<>Ovv4Li>vsZsiPs~M{p9JI#z}REnrZB z$Tn(`E9UR~!JgCuuVO%)b5GViZa1g=Vz?>0@=}vv-Jo+#P^an6SVaunt+i~@)u)jN za2^f8izUD23XZsan|>TKBzdC{Lx-%|CTxJdlnqldQECn&;lV zDP(o}D-{Cw`e5cV@05UF2h>f~N%4Rx$ddPq{1fE=cCM+N1YVm`$`w{oOu*2VSNokC z6?d=U#d1LHCtds2vn^mgsuvT7km?%3d&oBOszD!H&XZy7A@OTBD{>tI>m||FnMMLg zD%6zR0JNc!TNzB18%-qSt97T_QuaS2;w~59rK6g#=I5X-BzHlADfuUOD1cAdS6Z@x zZGH{Ku3|`6T{`^ku2eTamZU|k&r-FZj4QYX2ei!`%REzgCPdrx+j}wAH991I_cl|cldLEaC#wrSyE~%&0zK#6iRHX1|@+DL2m2b zH2YZGZdZuFan6fbJn5K}$&ws!03hm#zzStVa>|9EM=gkGBYi~AdFhCbrm|x zC@N*Cc#j-8L|J+av;N}v_Q!{}uOGjD#E;>dpWsK|IN|M&`ufEcWYk&%#2Sl2k_mL+ zjY}#dS(7}adB1ST0GLrx(A_5q^ovR-E4!F76za#~K&rLM{X9N*GzFH*7=lRPdGyw5 z*f+&1#^vTS?)_4Hk>NopS>{qCvbqOPCbolN2DBT%1OA*|9 zEal+v2p#)q5{L~vb=jphpamq7h3SM+rc)#CHl7E!B?sCxHJD~$R-d&6@*oU)$s2ol z@*v@Dos9S;Wj{vj*8G#ryK_$c)XNi-q@$AOZzW>2vWZ;Dt!UjnEF4$izDt@l+lsCo zY`XQ3bBo>4oet(aBx`+fn+KFlh~2K;2ersU9VIy{qzHBzSwK1V7U`SRv7tT}m>fKk zvh9(|@O-dqmB{LF$jaYLZ$NZkP~h0GaysjzaM=N-wA-I%uz=5??N)x)9tA|t^)^?+ zynN`ulEf{4YqFQn=2#mWGqV{&uac^5rDjBDH?F6tR_+$s>8DXLXYKO>fnbrI4GQ#) z2J^LmW&(T0S3r%(Mx1FjfB_DhbI+j$qsSrm>S?B=vdchje_aGW;IxV;XpZ@~!F3+gJH#}*FEl#H^?7Erlbpa!6 zE#=IVN*-p*Uw0!E{K*Zr;k+K;4s9XqC4ydW5;{X#)ojzpiZMfVO!^dNd}wJ+9Q?}d zxL-Qr79Vr=8*uNy#u>2?vEA6}thK4K=#;`b!$=lMQ-O)BJYkX!VhYVx*#T#bMCD`H9&t zq&ACWroYnGC1?1sD$i71_UdM&7x|{yL3M)Kt+mFYdR6viI?u!bSW(8;9t~j&R&uB4 z2zzDlnk7VgAnOUU=+sJvh{nn^T4AVX8WX@dcz>foAC@=Kh)mk0CLopI#(p3b+H$~i zrq?kb6^fc|KPXzLdEY3R0_U#x3{_KJ0?W#M!o|NCX;E+e9INE~Y)SVF{~{U}Ytv&v zz5yptA%Px>rrplI@EY6W{euHZnOenG+SF9X#-NBO;ty6kohH!T30dG zUz!K$hTz@7GJCVjatQxedY%fFCMAd@I2pwipN*4S@~P@yFl@$m0?m> z^f0=YTpVgG-gDlm!?S_lUIwfO)An?W)^JlFa-?KG@ZnO(zeyXwsr;)YW*J z;3^j>!*hcmD&x+0h6#s`Pf59!b%%6x&#OiiO-&C*c3eePEU>M%+V`pt@BZ)C-&uW~ z$J_M1h0KT)GY_eRY)pi1mJjTqP~@26jQv)_umY;BQQa3K&H!aHDTPVU0H_lgNSJ3G z`q=ys?JOkRHfFWmp)xvIVl}k~n$EjVzW!c#{Zv`dWrC%lJj+}?tBwG3!Mo}0iJU$;8MJmZ7!C1^s1Bdl^1?#aNFD5EN-nmIe6r;6 zAULhs2Zz}v>@#2zD7o}DW%W6Qy4p`HrMig|08C!H(#zr=NL|XN1Td_YHv#Tqr>;)# zHNzqbRTYl06O8Uf4=4_lHV7;}SW4MlDo+Je&NQ>{(jslJy|EQ}7tAi)WTgQQojT*y zB0m_6-zFkjtLaHoz?J#RjuxY}t?$7b*zi9d2xLUGU_WNBLFzWd^}4tPhH5l`$DWwT zV`kf5ax-NH_O?tR)Pdb%Y10gWQerqo*UAtTkqjJDv$Bz7KhDv4NSOWXXk85n}I zd1tXMchKzwrRF1Zx*BV_kYt@DC8@`}3U}ElbRL1cQ+~{l;{5#Y3gU-^b}`hYp6;W|oGigtQJW35Zbv<4aQMXQY+@PC&80Fsv}Vd|o$6yCl6gIkQceY)T+ivG{w6BC;|F}*RG$3QjBA}r0ook(Q-dR<9d&NwZaIsBXt6- zT!u$k**I%67(*GWwvWonSu4Fe@(OlRm3seL)qYNMXd1MwY~Bvd8wcxSeg@98_cfH4 zd$5^Sg}Jlu5S8zc+b&%zRky8(;@{LfJO@?G-jPYmh`M($e*;5Ow6T|UM{(H(^{8xSb5pqttrA+BfC4`a;Jy}eaDr4ye`U)oI4n#aWA1tD#t&+nVC!BPXF!f1LalQz#|YVk z?e^q%r;pt50QaGl320sAngGT0-4}0vq>1#q zABNYD^3y9OT%WxCIQ(sX^AEr|eE-#Y%2HfEq!_S}iQleCTPYc3CsMY0xUN?iMG){A zhK*2{qzYkBn^^X&7~b4ZS!IJwpsji?L{R9aP{9Qb5}un%C?ow7bFK+`xRxR#4<$(( zwJ8wEL>yja*XML40q!6_y&+?~?KSNzuO}_KED)=~g{|M!#j-}UI?Sp;a#K%DsoL_C zAk#q*e+3vSmlkc~0EB}@@QY3|?C4w+xv%D(`w89Mh9f>GH*lk#73Gl)f%Oxu27J~a zVCZ;(sn)}p_3f>rdO)*>XhryXb`Gg{kwRO=Zo*7s2c@sn1S6{3<~$T9r|-u)O-uJZ2fD@-Xqe*KO5iM@Vydf7)nel|O~RH?F}HW8LinS?uH zcRF;kO=~*)I}SO~%N%2L?IiZ2VK6G=RTKRc$pKG&7e0X1>(gmzYRLnansnwY*5x2x z1oV-L=aQVq{l=54n^;mG0I=Aio|zx^6F@2!om)3a*9aZDeJY{2Z`<%Nia~_N4@}>d zsZ>F2_6I8t^HNa3$(vG40Oh)yGzJgEKUJFw1}v_HEPp9@n;HtRGf_UMMzNcDfnt;e z?<$Fu4{!(`#a#)#r6KD5WSSPWDkL*AgF=Q2Wv`Brl`jp^q%?6qfyxfZN-s5z%_{6q zFn7j)4ft;Q*bS|eG6VnESMTx*tr^VbHZ-QJ;aexn3A)u zY*s)Cu3w>n6Rwb`-O$m(=vVI!tOv+DlnG0PTC>6d5Z7l(=4Ihc?j73PxN7pw(nhgg zG_c9+b93H_ost{(L#o=qtD7clQ$$V6q4r9NN;~e>LxLe zcJjmSILD!%*GHI|({*Ki0-CL<jR#mjr`*Z~@al?X z8%lOVyu#YXa)d=Lp^ZXEl99N$gxtY3aip?mKH!e&-SI~OaC_0zt42stZh%%=!jC zpv}Q)8uQX>F`#@U2a2?@evOE_a$nmeiV-34@2Fwr`k}P+;_xr26@217U?gaZw@t+x z^b$3Pu}cFW)|qy`SYzUx0o1)DPv#^pwMu$U<`!zmu51m0kxdQ$0!>;V=A0kz@7{iL z9ASSc@%}F@VlOJFkc!ra{7!?$X3Rh*^K#z8MB3y=1OYCikZuu?nl86WlhwJPJ@(xd zFFx3-maTW3BZ=ahayTrgfY1Tlz+B;0sFRJQDyi^rXKuDbVM?~phY4~KJTGVzw?)to zf*hWv3|JJ7n>UTa@tAw;AmUYloBKU=9cx)r{Fx8ko`&4ii!%rk~@srY-kN z1&%|*A735>?408IUJ{6+2tSb0*n|2d=7ri02%!ox%yJykS$}xYL*(Yio%4)-&k@y-&QgzxOw~Rtz!HWE6 z&sCEPET0a-2wuykS(e;9nxV+cHDK&I*eyX3no)s+V*Xf~#>hT3$2GIvzjrX1d|8V7Jy=3x|NYXtX8 z#6)fI<8W|i3Af8G#0fhV(>V0 zu;qv!QhfFL)%zsL zI&Z?~XDM8W$HLJh_9K>T^xnwruka5P(h6O5!3m%*(O+ju z;6XRf{_+O^)vij$t!lP)R3{Kj*oh5Fxs_xI_PR1jX|NshZpk)6FgvvOLAhLQENR)G z?snGBjFTj@2_D%aSU@VD%u;zG?cxdGEgP-l_>`D&Xo|&Wj>pwol!cJWo<0tk_9h+# zvLuPGc=gwEzXUo!sV^@p7B>}_pQ33~V22!qvs#B$Ry1qf*}!I*T%Iz&gTe&_$)PTa z7X1NQDh1IK)d11&XPxgAQ25i_C;)jT_ z8g}T1l8G@Z)WrTe$R=roKBOc|bV8qWoD*e}3~FUcB@yVps_3A5JjnxEDmk7~kWozS zsVDhXyN#wNf*E>Gvd@#2YDU6TlqLrYXm4^lb3c0g|Z4@)TY7xpwV2sA~ zkBR1~md%4yh;khFu17wK6VLZZ<^rO+;~?eKSn09EL*NE!gLJ&B8=X{|@L$Yo9Fqc5 zIa}pA`aHk^toI4HO2(tQAbM4vWV@!_@8>|#3tWw7ztfP6*%Kw~V+gvZ3@YJw_W<(7(|B8-laP1>;aQWGF9sXsXQ8}==sRZtTo z{{VEebbVrx;gVk}P_hC)r<85idlCJ-#vAR`p0fRtgD)2`V7G8Awk;kQderc zzx1lOQzfgb>sp*)8xG}SOh9FS@AhnNaYB1m)x3J5)^2EoZrXt*@&a-ot{9}! z$@%3#@gUGqYlHbijwMo8F*!SRiTT~ST0`ytp zt*a_TPxLTp#1VMT%!`f=dehEaM^gWXPLbta0AAcQ8gZV7!F%3;<{M=zfRVwH0dBhx zeYAAss#1)#_3Riufh#AFW-EW){u95~Anwcfb4p`zV5 zZ4VphV4Get&WhEoLL@nym6T8^`lU2*ojH`n9r5{17pT&)hO6b1b$_`mP~?gi>oM+V z$jZ$yQLFnLik@|b=Dp_?Z%Vj7p{0EI<|p#sg8rNx4NY56tDJOcT*C%gN>3Cx?4?i@ zEGjgdKiT0Bd3$s#CW-6)Ud{3!3>ZgQppz^FMqKQ=X*+Ia@dl~i8+*QT9Ojv}7a$2* zpOJN(4%_{3|7CxRDTiihdLtPM?n6*j{>YoYC zc2coJIp%r9rSj^A^iC4Xj8B`?k3wK(%Rm?UJT1(GdAEtJW% zE=6Z%x#alN-#py|avG{>#kOs~6nA>bfJfy};|^L>iF~SV)`mcNYSBm1LW?;26I`sR z^n!%xLV&J%nnb=vx?m~5u&*iyKz}gR0Cm%$L7B+x$yO>R3kG3fh;2}IvAEEWS+ruK zs@j>;!0dZg*pBD<$*z*~0L~E0qqF>qX1%zo{`O#hE!GkYxzT#!?^Kl@-PbvVc}sr5 z!BXWtD#DM#v8|B*h${`aVB>_m6L1Af<_B+y+}Bh!t6e4HYSC8MT%QODf)= zceGpQ9Oq*-;W2>gdXB$o!me3f9!M0-%BWCX%YL;Rt_UVPYo&nx+$&4Jk2>h41b1=I zN4Q^xV()vdGBlM!=IG|yqeC5jNJ`mm!;bAOm2V(nU!p_3q$_Bb%fR>Y<5TfLP7mi8 zavr2uv!SFi^Os}x8Q%T{?eLG@evNkc+b_Yy{74qyv)A9g{aF9tC;A6p;Pu!4O+RCv zT_3*v7O16tRwElDq;EF>kX&CNGvX|+s|>}qpI`ucvvUYBJI_d~8~7tjf`^~nJFRz6 zF&|n_%h5SR^|65E%I^?FC(vP}xNNzuM$+ol>OSAnmFgqXmmXrMJP43ww1r%5$mY7! zB^mnM)@X1V4CFj2ZHfm7Ohb2Nqhe^i>=te71nwyTnefDvSPTp%Mi$SKX6=41X!@bG zSpGWt%gS9Z1=ZFSUbbqB9uenTh1kb{$MnFS((al?1~Bi7^D?Sd3>BHVS9}=hxi20N zQ#J^-pa$$*S6R+8L3`HPG*!cAeBFahA$nOiDmS77!*49LtlV^C_Li{js3beI-fWTU zNC{8;D-v$l3hq>osDX4~m#;=~GPEQd=8ib$hDg3K4Pbp*DV>`ZO!z5+JHuA;)2wI< zSR?#deu(GDay;8>=erFJ;pylvPG}_^jD()B_7|3Zw!7(z>l38;LdZScAn1tY2fL&VrZS` zuul<9FC*tiYvIXFk_>cG07czCW+&5$R8x2k$+s=v6*evnDdkz68!p*EVO;isw1o>M z=J_H)R}K@x6$UicPQgro{^^aBZdGm>+k0r16cIXG#O2@z31UHHqw3wKZ=Z&@KR{P} z07#D|5^{n8jgnQ8X^+7>p2T!EBr<4LRsd|pjTOpQt<7q2qKT_S_umPptL|l4L^ivk zTmctAM&nB00-mL;3Ah0|R#P~gT|}zwCeLm}xN{wB>=Jtk5H*k-4vM^@>-2zyAwfPM zC8@{*rxO79fwV!X++CRw3~tCjz{{#!oxC{8=Pg1vUF=Ej zk@6qmPPRQcbl6EeGh4xBDx{r_FtC?LTS-{6qq{J zN_x*L+c|#%ByGt*K;j)(Ur6aTTFxT3#s`{btRxW2oi*7G>>jEB#Vk#-;SuE=I0%@( z&)$mcI9lsC?Yv;j>;uO0E?IMa<^BV0Pt^%Dx(15EG^SXGLi8Sn0?d+CLbFSv*@d5y z>%0dL3wjBDY?`WvE~P|%zCT;et41x}8^vk-V2>H$W1$|s3%1s7-Yi9_t2mB)q%lph z8tits`(c@>JH!&I4I@x0mjAzK73-LQFtcos5MCx+2+scJ&o%6z$u)yNAT>j?HPTn{!MteJNC0+4_!{ zhLTn9fNBY;D=W>>Rrcg+g~3Ly2PDP(`t4_^9)1l~CCOty z&wuM5-#!Z}5h~RKaJvuo7kXNm;&5V1lQNu4!UvU;%}SANQNHvtR8XTD>``IZPt*@;B%t#d{pGx!*%bo> zElm3cPKMm>@DA8;?}V-3^sfa2n!#PjI~|UK+qk2PSPgxIYV%r&Gi)rZ%adEF`W0y# z|B6lag_fuK=Xb?lp6>m%cTJqT*2NZrB2{_dn$4*t=5D=ZL6SV-AG^W;58~;UGdA*C zQD(kmDUyxU?!O5JT)VRpSgN93xyHV5hgXRjytalY(M!G6r-vMYX6GMBy!OrJ9=J0c z*c_s!-d`{qdXep(gBT*;0uf@4WBcdW;-onP`>MimVU-P3SlS8^m%^SR2a;=PGmU!| z(q?GEWvqfn4VmCA(9?6!-`y{GU4>K~vF36>}@Da7Esn;&Sst{nv z!pIB26DKlx3d|lDSGO#v7Ij6oZ6c>${@_$yKs3U{HI*Ins{ozppS*nvBOLvpgBp?x zZ^=rdESsq5%BmgnykPwhs9v>Z<482Eyc4nj&xr16_E%uTe42_=!3@M(YOE&!8eo31 z2@%F5!NCW7Lvg;E7 zvjqLC_v>+dncE>WrG(ymHpX_m|GAbj>q%;|{CAu4UKT{Ca~jE80>3s?WDA*;5YS4ljmWlXAur#03b9YH?Xw_{~9h{3%-WGIPtJI@(== zrZ)_y@f{d8S zy2=U=9xjwvsa_t7{wS-ffo7>F$AxPNB{5SDAtt<%Iommf&9BKqh14ByUKaD@gedKdiWMXjPSD42nqxQmE$H6vF5GH}wm-3?^7~W7hN|G2{Y!@GibZ#cz zHU8sw)vYgBxl2oO!x@E>VWee0J#?;PAr6Fh2*LsV@6}VQv|+Fp?8@Z$`+cdcItzPR zs14`4omoP@E6HdM+T33v`OYsswU4IX_Wjr2y!(E5{oUy$xjps6tx?hSfb@ZyzP-4v zv^$;xIt*k7s%-`zv>gJ3&8QazOW-sR5Q-$h9j)MmdiLeq+^A>WAxjJf6=C_>gC5+h zQ*(d0cogoA&JM=OBO8b+rrl2p^1fE|w~`zTT5<`)#1-j+oo7HC<*ldoXe(E_&1)CyB)>iA#|A7!YYGav2W%+1 zY}o9WAIpz(Dcitf#WSYa{s0oXmwW}N>MWS5{rFC;+)F!dZw*QuM1q=(4D+$TN zhjMm)O@x60s7J@_sXOb1)CFX=k9}vTm8|L|T+?i`K@^ijSj^UVnzM65yEj{L?SuT` zGX+mn(NU3yQA)y?P4$^)&2@ zi^pUN;F*!RP7;{U);SGNaX3NW=b@_Z0KgCoS|5PbtwP@HwIs8)WGWlOychbb-gcS7 zo#HHYXf~Q45&KpxV|b+V>D%A_cKFZ#xg6@>!ScCa5pDht>f^8A+xW%XSH`$~&Pmb^ z;+(f%aFMZF2n%MO2voE3tQT$!*(9x==^l1sk{Rwm6GEe~asaA|DX}cW;3BV24^-um zVuwcR+Vp55+71N<$;uOTFZFhKr-PiwNh!Ss(1q=p)78UzoZs(0@j-UGosb?uzetLQ~f0 z*TKv&@*lna&=xGe`~1CkpXGPz3qFVjRk?)JgU;M{&qSOUsEc7tZ7rt5#GT@R4R@+I%k*p80V`aRud4d9L zP(vH9(0z-5)L%ghmQmg7-+^>b=Nb+~k>Yz9a?M z%=?45;yD)Z8KefLO>Th6lnLf!AUDFtB+r+0x!)w%Sj?``K#(x4dISvrvh@yd=WCwT zUMWW6LZN^(tM5oRtBO;T2-v#7OxKYr7fI`0-0(~xDS3gRPVcy3Fy7YnFyX9P(4!Ov(BaAR%uO*=%1c@9lS{{)k{M#qMA5&4&`^|D&B)c_zjxf&)QX3Sz4!HDpRD&D-~4!>(ZxrG#kcf;N^ zJ-e0g7VUWWt^{yr=vAEz8FCNfmh#0mn~u~gs6|ig%z?J;Y#P~KXA;h29p)a;CSMkyCupUEZd7LZpQiI3OwP(PSy0Nd zqNvxjmDa&lMnyTjYdMDihu6UPd2y@#d^@9H2^kFJ31x!uzA+fB3H^}VL@udw@w5QO z0!y#5knWfbVN`jx{owi|mFj=F+}tG|d`2rofz^Vd9NA)|+s0J|8@8yNp0v3Q$biu% z7aZ*`)MYt9AAaGQh%4xy{(q#s>#ii%l_vH-pJG#hF|F3L9-uvT&wC7}8*$kg5t(tT zxMZFTUiiXeJF7kBm5e)*-^B0-G8ky4OUMyk1^4o6Rj9+;idJvJc0 zxAalN-d=q|DGsaFi^ck2<*_9Ul#iY7zy^asPkiw8k+8HXry)zt_y7QDelD7PTzWzg zFb|(IpK}5%{G?kkviiE@=0t;s5kEwiH@d@-x#I(hwM{xp*e~j+3oeN~6T$S4TAe+k z<~kc<1v!3Vov+$%Nw_M-fDPqpy-OIu>?S?$2T%^g*d1j~v8ZZ-tXAdgPB4G13x#ya z_OP-bY7s(Gh5Sw6A?(}-=NdhYx4!<%T0Q^1FJAr__~LYz!$!3c;6;GcqJm~`fEl$G zu?Mlqp$*hJ?XsVs)PGp9NE_4?&%8J5JelTO+xdV99*_kG*e*7ydt@a`OY`mJ@0%S5 z>9E&sA%gz;A%{=g=TGp$n@^M6Gx71135>&eOL#3Jy1nmY}LB0Fz<@50J)w|D5&mDai?@Q8ZZlp~MrMLO& z?rmHGY&YyoU}NfmX20gC*%Yp(Z!|~&;(8I~PRH7A_AT`cjEq0JHY|^Mg}(<6K|-%o zPRB$zg>EnG--jDp@tWhzA%X$uGcq8cio@uENTHS%P<22*&KX|1gR&J9ygFEas2%*6 zS>HiPK?ertZad#)QW5K{p&E7c zZ6_arrY&Z^BTi+G6VUr>)prA-He+|?S?tZEq?QWd9tfZa(v$UG8C3XVy?_Thj+BkQ zgTWP8K^C4bovPX5PrNel_u5e|P)e0Ra3i=3CW;L=I_F-&t={=#E@;!WSi{I6!k2m{ z3a3_b40n;!NR4?0ImJ&8*|RU0EuV^AIBct&=;2;YSvbG!5zLVDKK19Yp6=~&hX^T>0%Wo*DGY=Yt@{;=>03v{8;sR`Xe{|+awX8j z=dr1vgu0gCMo18;@K}K^td`G`3XvFs9DIC{>u=P8HK6OW%?ogrn30>iTM;D-s1S>$ z_X-VK2i3#gYKie2NIf~jGo!PCjLuJQ5MCs{Hgb>wfW$s{hYdkkPtiafta2hw_~1w0 zgfg@L%R&jD6yjHf$FyL)w-2AzT}rrVB3b}LDXD@_Dn1P9R?F6_>$XTtflcV_@gK+P z8lEh97qDJ@tRkUtL1jH`n-3T;t=c)?U3O-Y%Ec=**mT9sO-u+&gYPYfg^m~CtF(^g z00x$n6IvN0J}_B;3&tUq6N`AM{c<0IiQ;01NCrxPd?u(dUG@JD~_I^NnJxQddaqBj;{s3R}TX zw9B%>hldXb;3{B&RCL@(Xmdf!O*JaA=690+$|P6i9z%rOu9W&KLh*`Zz-EbAwx#I} z1$veORSFlA*pvr+g0XqEtLI^O&eFKhS<1?8cz)LdweAFjBR$JNY#PFTi=~RiS8+3*d6dm#kNF34`qrr9_@I8unex3@T8{AooPjjjCuuX!R?5E4nhDe~hapz_?!FuTQ+m!n2`}HAlCt7YPVasszrKEB&zqD- zV6I$!E@?Ck4t>~`ta^%`MoMuNOsqM{Sax`ZnK?`$)p`#((Sa7rk_{%Kp&ivkH1`V_C9TsLSk%Im^-H&>#Tlnfd$^~+44&*#NV;lanN^xd zyGcC)j&Q*Qg!M`u)N{Nk2@@n7f~C+Rj1YKA zZ>K!Tor2xr5#mV%i1qjmr(FfdtDU=i43R(#>$YwL7#y1 z0-Ee5o%X|ELwyjbw)Qw}0M$#B&+C*842!a$@Z>G4iO5a4E*-Z>5(G)FXdrf;WQ@z4 z{U-H1rLms|3nFP}??zRY5ao1D4!WdrS8-MecbeZk`{G}ocd=x^#@rsg8WRw+y{zaB znLegfK8%yXttU(sj$r?t@-$4HQsr;CPnuXx0GNZ;)WXBkO<*n;IlfQ2y0i@qlqrpi z0}u}DAK=GT>^GgBxOH2G`JLU<=R7zZ1ebx+h|#n(EiLB z6$q~LqCj%*X+b~iRcno2J=#gr(4&XI@59Kds8j>p6&&#$EsE41<9%}x8;G9qzd8on zh1?)@?w(+P;*WC7)YcuG4G$!f@`Qbxy8>(DtKz4yaUw3|^B6lImAFTQa4CWd%2fhm zTa1`7CYRi-GhI7CBdHN%fp`hm;xJ2N(mkN`<}oIZ&y@j1<30o9-<{%m1?aUpX4RQU4+9qBs^~^cxYR!iML{BX zz}_i9Ep;CR_HHx5bj+7fs0hUenj_IxaJWH&7Oa3YUuFu2EuTSqj39q0yU>{o)KAW*N z>K37|4P6bF%u9@iK5tl561m5U4`m9mj!A3O(&{_`df42kF6$+Fm6#Di4qNOSMZ>+Iz6!y(|6y5nZJI!moV&^3h-ZGyGfs z0+>4GBi|3no$D(Cwmv=sYL6QHWS<+?*u&Ft z9Nk3WlO_KbaR5u>`BXQ-=SSC|fIY*IS8v?i8Vl}ujkXOuRI@|ci<+?&QWceiEEeV+B?l;aqi)u00x8IRgn6( z*9h@k@BvgHp=q`~&IQ8Ev`Fqoh06HYymdj^RE8(;w#5cXCiWmF%!$2>gtD|%O5HlA zEiJiqt)epQHRiTHpVjm{X>(OvQ4~o9#q@d)4jY3^pd2;JP?AK@t5#&Jw{n{>AGjDw z=7q;5@atc=c|@1%x4SpheIQr6JuATW$Av2heftw zg%upxt_tDSjI3qewyLGfZle=GMeuP;wGFooIHu{^Xk9(K>x!haG8E8|Y^9Enu(76I z9lJ5)5nO*fT>nb(&HT0j>9Yh0Z=k%LFD*M zb9UGRf*wXwVcp4-#^yyG2IS~svJAGmVK3*P@KLL(fqb??T0M#*qS$6?X|=NS`Eho8tEx@NMY4toW@I*$Xv~vq<2e!d5MTSo*ewOC2}6^p;Y)$qPhW z<1VS;+rr2&fo(iBzK4mU(NF>lg`UuT}jqKm2f)7%#_|@}j_$ZKtY)RgM9z zw{}|CpiAQ=9lP;^i)TkZ6d32HA%qqu@D1~jTJ$?)e;xpOzk2Etu8aokeKy#e0BuzGuNtvu z@SsH?iI4@8bFj9m@BncrD-maPtg0UM}{7-vJ0Zv1$oDnom-y3E9uRI{cKa$6S`))D!9Oh3 zbdBZa)MqpfPO+87KJ6$U8-6ZbRRZ`?@j@~i6T9|+uDK4ZEFrx!pbE>bo}L?|a~u>p zugTeht`u;<06SGu_8j=6(KB0lHK>3yChzF+ipAxNZiIfT?VEzg)Jf}v5#%Uwpr_k4 zP=D#k0}trCFpS`+8FDE@+novU+b~(@Wt5Zzjhbv4_&!Vw zAQkTn)eMir=z_+XI(^|pJY*3}w-#TVu^TV< zvQ&NV^#L^Kh_FSa;>n8c;G}CiRp3rJ%0DQu49$GwTP{0?>4l_B{}_v8!N|W%zC&9> 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

brXZX@VYhc!=SFxwhp z35L9L)9~j~4N&#DdIB@B$p?;5#`+0waAk#qv}U0S9KqM*qre1sP(IG#r1_ynOMv9G zQpHjXDzn4dA{|PMnMQz^P2vO(iyi?HZCbYzM&_2v+yT%sBw&gO7>0CXNkNktaHbU7 zP+Xvg7gqvrTJUe5wmuWsCQ<#2)H_y>)iGU|fMtAAyI1~XwUC53jlyIFzh1O8ZPglV zhgIE#J~QzFUB@|6l27GL434ICM z)?xP`e<3Gj$2wirrgnPPH32~M{g7I^KMD9tq}-55E!w`E+b$~%%nk!H5O$q|9|R{s z4^N^6=_QEYNYc15>_VoKP7#PdEye&$)O6Wb4sTc^a83?8vIe0I4RP_OKz8yJh^tJK zJ465iAs28PdD7gX8YShZT12==VhCn^b@Y?jLSIY3%c#w$V>O2<6~rWAOJgqzbnJ70 z|IE+@e+l1y;qvv5-v09T=VT8k$G>_1)z`3m=Zx>u_pjc*?RfgHZ@&s}zq!0Z!EWM| zRlDwZdYPj>)w3sqrJuf8z(uiB8nHVTY*o*J_}h3T;dPn?$$b; zMFJ(iCbjDo!^TqWLGu7Y^0!qY=cr9NqNQ{sWyP`LzA$V+f;a;PGSq=ar43? zJpoKm#Yzj+?Ib?G$~SE!EP?hZzhz(8jbHY=dQc5g=~FaV{=nJ3s<>c^c&gA{LlmuP zKUoSWWi1>jD}BrcNfBqzO1z$eAo(yYbL;%Q84aAX2Qq~EYO`IEz;RM=sh<)l8Gyor z;+#?q#fA(r*@)N6@b&lJeh?On`Fcr_ja_^MQe-j30%MkvcrXV|hb1aNHu7DK0dU`EX74AqQ2U zktOd-c_$RJgWX0Zx}7@rg^r9YUM*4olQSfx7qixW4wii@ zmRj;yNTy&?7!yxwW3++B+8ppR&UTf-9oGjtqQk1i-Jp}sMAcCw*i0U#6ngVzZdlyz36C`KLi zF_ru^ouvXYwcT(M7PiXzz!BS6!M@KimQ$`jk;%9hcM|RmcpBM2mzSg{=yG~i`FMSQrWG5s}Tcr|2<9heUP3*lz8PZhwZA=pc&XEX}^BFjWQuWiI+iVJM6&D?xl*&ok!S(+j|Sl|ol9t*Ce5D~_R_HBttoc`)^blm zQR!>RWIR`hEg)qCT$GS2?=wh1u6XIiA6mN^K`=vY@1nEMl4CzGQZJC$4GF_&@lejG zWQVQ)Qb^K?#FT?IBxX&}mSlQikqr;B<93FtV_yLrC}z73XsftzORpwt)PP3i_RT+i z{p0tap=SH+{YT8crqEiJa6pjOE;CRXA*n?r&)90rMvM{>RTspWQV@?adCg7Ja1cUO zWK(u3zmc1AS}kKjT0~IFrs-u)fi}$eovEhF`J9KZFsITj8*(gF-;VD0$#B$z)2>Knr^kfI`1(L^H>CbtBdVVG*il z4+$k*x9yg96fG_>BiAe}&I@%kfe|8KPM#hD+V@bK>fmiEkOF%o zyvf(7!S^&gCz@Obnss}DRa^9w&Zbi%88$a~m$r^Ou&BH7y1LuK|`Pe2OLAZ%0on! zrBpWox`dZn(WNQoVOO&>tPSPNz$k$=wrHX?Io?6*D^GwBiJ^heZ9aDZ)h#lBsR#i( z+X`PuiFWanO*s;YF4vUqnXp{m!OG=bgPXBb$gez^sm^&Qr>-ZyMuET9uaY!g!UsOl zE_F~JFU|=8w*sQnPI8k0n(9(wOZo=Y=JZkl;SXX**RmK@ZRZ-tD`D8?3iuNj73QM}nZ*58e6 zd}C+Km^vxYSUAd9N0s+Q%wtPzhZ9wfP>r)qt`NPZxDBQRuLRg5K*C6mE+#dNeWbQ@ z@j~OwIP7XydhzB=LVX?;m|{Ul)rRHT-lxUqq3RFm@Tz3C!R%`YTjXO?>@X{0Jep{S zTI8x;V}iAn(AN&{ze>lSzW+YF|M~=OfF~Y4CC`M{ZdP+(WfQl7H@DENBL2)V- z;zuVdSU57dO57zLo88K4{aB#9IX+lB2n3gSa3o#2gO19ARB{+ub+WDxhlj06h|7)x z^>7yjCR3VsL7j@(+qQ3R`GTE~P7cs?$A@N${DVJ)R?Vsu%aa!Ov}(bN10d)hY(Gtp z?vTxL&D#xxUV#tlRv-`4OKS-aMc9N**0NEN@TRqB;Qjw`>ejQV@p$tFdW}YEUC&0B z8H*n3RS>gNG`2dQ%7J{nC?M+L z3%!_%A{@Q-vRyR~`bf6+k=VPk93IKZ=6Yb+fr1iWvSGVKZ=$M{td56%aHoTb^y3P%S9oSjhz?KL%^6tEf~Uk4yF791az5P`Ha# zcan4g70pv1^A>*-(<{t^?u=h@eVj0Xq=Q8(rr1zz&{jIAT)RauxdWvVisk?Ohy5X2 zU+v{~KkZ%Gt0=$uP^%11vd1QNZ`E$LFl6sLs( z{EI~!i3M44y+H%>uyzz^9cFcec9hXFh|;QTF4V+aUSG2Y;59z)nfxc0$&_5La?{2n zmvpJ+-@(=15ep@Yn+o+FtD>sYnNyZ)Lzk(}A}P=?zf$ZMtdq`z7NGY9AdVKdYUps6 zNiz|x2%4Lf@hQ=$l1p4F^^AkrQk%6qnZNLy=!m2(jx)I1EX%@Fz#GlJ12!QR?%-&$ zGwZ329KliZ4kqae#CRR7`&g(F7clliw=Ax(loZvV2!R~eBH-%cj^>`7jz{ka$Np>{ zAF;5(5g&=SBm#SH}&8Z81^6ph$NmHv;xiQgn=6}UF#3WFV7v^|y zqM@or!t2`JE2HHVojT-4libZODwq|*oumq2Jm#2qMotCHkdd4c1J?7?O&wX6SXm%70QzN+P(tep~5 z7CwKi&L>Ne;Ur8l*maQLXJ*40_4-ZexaP>FEF{H`?|T670W0r22y`9F95=&#!oEe8n?B9n zeOPo?XMF={|IHOjFw5H3L=N-LE}~k`EM8gt-hKe13}Unxw31=8A`zuM58l&wdfg;7$kGhTQJU0VP9DrUavp*|8#CiP`WmA0@3w={2ThH~#IzwU zI(b}}HvqS=cI;_L=;w%k4qo9XR}uR4R=|XM;1l91->yG5Mr)kI36zZv-TYalph;q- z$RyEFq6hw@N)?sT%epk^`)%O^+md=HXq2ce5TKVejp@lu%O6x}TS0e3DS{}$J==l9 zv^x&fCj&mF;VGG{voBHY0LV3wYNb+-z6NM#dGR2`mH@C;6so(YCfdUKOfVHffR=HBuBLpnWs~3V=P!e}yX^KVP{Nu@L>HUjf-5ElX z5|{Ste|`HhnAhdk|MLEMkPn7iL~YjhhFCW;%0XLTw526|e8Lh@olc+|GFerzuYrpS zA~_XbG{!En?v7Fboy{Jw;tRT%FY?VLSzmA}DA{h<+9l*hZ?QKCVq`6bt#*TS*=!s9 zUrn*y!f9;ot+#IAz+n}ej>^2>RG!2NpbOJ&dCjd6k2S4FR>|cpK?+yvRmvh@TgN3) zs52h?&0iS?4k?3e>=2< zYboFg(4z=$0-_68xC{$|>iijVDc9%7WgM<7l&vk$A{X#6a#fdrm{b(V1zNV^@y#A0 z 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

B0o?DtF!VT!YjrDC^S2?RV?P>**rUM?tSk)et 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 literal 0 HcmV?d00001 diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/constants.py b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/constants.py new file mode 100644 index 0000000..a670bb3 --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/constants.py @@ -0,0 +1,2 @@ +OPENAI_DATASET_MEAN = (0.48145466, 0.4578275, 0.40821073) +OPENAI_DATASET_STD = (0.26862954, 0.26130258, 0.27577711) diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/eva_vit_model.py b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/eva_vit_model.py new file mode 100644 index 0000000..23cb38c --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/eva_vit_model.py @@ -0,0 +1,571 @@ +# -------------------------------------------------------- +# Adapted from https://github.com/microsoft/unilm/tree/master/beit +# -------------------------------------------------------- +import math +import os +import torch +import torch.nn as nn +import torch.nn.functional as F + +try: + from timm.models.layers import drop_path, to_2tuple, trunc_normal_ +except: + from timm.layers import drop_path, to_2tuple, trunc_normal_ + +from .transformer import PatchDropout +from .rope import VisionRotaryEmbedding, VisionRotaryEmbeddingFast + +if os.getenv("ENV_TYPE") == "deepspeed": + try: + from deepspeed.runtime.activation_checkpointing.checkpointing import checkpoint + except: + from torch.utils.checkpoint import checkpoint +else: + from torch.utils.checkpoint import checkpoint + +try: + import xformers.ops as xops +except ImportError: + xops = None + # print("Please 'pip install xformers'") + + +class DropPath(nn.Module): + """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).""" + + def __init__(self, drop_prob=None): + super(DropPath, self).__init__() + self.drop_prob = drop_prob + + def forward(self, x): + return drop_path(x, self.drop_prob, self.training) + + def extra_repr(self) -> str: + return "p={}".format(self.drop_prob) + + +class Mlp(nn.Module): + def __init__( + self, + in_features, + hidden_features=None, + out_features=None, + act_layer=nn.GELU, + norm_layer=nn.LayerNorm, + drop=0.0, + subln=False, + ): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + self.fc1 = nn.Linear(in_features, hidden_features) + self.act = act_layer() + + self.ffn_ln = norm_layer(hidden_features) if subln else nn.Identity() + + self.fc2 = nn.Linear(hidden_features, out_features) + self.drop = nn.Dropout(drop) + + def forward(self, x): + x = self.fc1(x) + x = self.act(x) + # x = self.drop(x) + # commit this for the orignal BERT implement + x = self.ffn_ln(x) + + x = self.fc2(x) + x = self.drop(x) + return x + + +class SwiGLU(nn.Module): + def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.SiLU, drop=0.0, norm_layer=nn.LayerNorm, subln=False): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + + self.w1 = nn.Linear(in_features, hidden_features) + self.w2 = nn.Linear(in_features, hidden_features) + + self.act = act_layer() + self.ffn_ln = norm_layer(hidden_features) if subln else nn.Identity() + self.w3 = nn.Linear(hidden_features, out_features) + + self.drop = nn.Dropout(drop) + + def forward(self, x): + x1 = self.w1(x) + x2 = self.w2(x) + hidden = self.act(x1) * x2 + x = self.ffn_ln(hidden) + x = self.w3(x) + x = self.drop(x) + return x + + +class Attention(nn.Module): + def __init__(self, dim, num_heads=8, qkv_bias=False, qk_scale=None, attn_drop=0.0, proj_drop=0.0, window_size=None, attn_head_dim=None, xattn=False, rope=None, subln=False, norm_layer=nn.LayerNorm): + super().__init__() + self.num_heads = num_heads + head_dim = dim // num_heads + if attn_head_dim is not None: + head_dim = attn_head_dim + all_head_dim = head_dim * self.num_heads + self.scale = qk_scale or head_dim**-0.5 + + self.subln = subln + if self.subln: + self.q_proj = nn.Linear(dim, all_head_dim, bias=False) + self.k_proj = nn.Linear(dim, all_head_dim, bias=False) + self.v_proj = nn.Linear(dim, all_head_dim, bias=False) + else: + self.qkv = nn.Linear(dim, all_head_dim * 3, bias=False) + + if qkv_bias: + self.q_bias = nn.Parameter(torch.zeros(all_head_dim)) + self.v_bias = nn.Parameter(torch.zeros(all_head_dim)) + else: + self.q_bias = None + self.v_bias = None + + if window_size: + self.window_size = window_size + self.num_relative_distance = (2 * window_size[0] - 1) * (2 * window_size[1] - 1) + 3 + self.relative_position_bias_table = nn.Parameter(torch.zeros(self.num_relative_distance, num_heads)) # 2*Wh-1 * 2*Ww-1, nH + # cls to token & token 2 cls & cls to cls + + # get pair-wise relative position index for each token inside the window + coords_h = torch.arange(window_size[0]) + coords_w = torch.arange(window_size[1]) + coords = torch.stack(torch.meshgrid([coords_h, coords_w])) # 2, Wh, Ww + coords_flatten = torch.flatten(coords, 1) # 2, Wh*Ww + relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :] # 2, Wh*Ww, Wh*Ww + relative_coords = relative_coords.permute(1, 2, 0).contiguous() # Wh*Ww, Wh*Ww, 2 + relative_coords[:, :, 0] += window_size[0] - 1 # shift to start from 0 + relative_coords[:, :, 1] += window_size[1] - 1 + relative_coords[:, :, 0] *= 2 * window_size[1] - 1 + relative_position_index = torch.zeros(size=(window_size[0] * window_size[1] + 1,) * 2, dtype=relative_coords.dtype) + relative_position_index[1:, 1:] = relative_coords.sum(-1) # Wh*Ww, Wh*Ww + relative_position_index[0, 0:] = self.num_relative_distance - 3 + relative_position_index[0:, 0] = self.num_relative_distance - 2 + relative_position_index[0, 0] = self.num_relative_distance - 1 + + self.register_buffer("relative_position_index", relative_position_index) + else: + self.window_size = None + self.relative_position_bias_table = None + self.relative_position_index = None + + self.attn_drop = nn.Dropout(attn_drop) + self.inner_attn_ln = norm_layer(all_head_dim) if subln else nn.Identity() + # self.proj = nn.Linear(all_head_dim, all_head_dim) + self.proj = nn.Linear(all_head_dim, dim) + self.proj_drop = nn.Dropout(proj_drop) + self.xattn = xattn + self.xattn_drop = attn_drop + + self.rope = rope + + def forward(self, x, rel_pos_bias=None, attn_mask=None): + B, N, C = x.shape + if self.subln: + q = F.linear(input=x, weight=self.q_proj.weight, bias=self.q_bias) + k = F.linear(input=x, weight=self.k_proj.weight, bias=None) + v = F.linear(input=x, weight=self.v_proj.weight, bias=self.v_bias) + + q = q.reshape(B, N, self.num_heads, -1).permute(0, 2, 1, 3) # B, num_heads, N, C + k = k.reshape(B, N, self.num_heads, -1).permute(0, 2, 1, 3) + v = v.reshape(B, N, self.num_heads, -1).permute(0, 2, 1, 3) + else: + + qkv_bias = None + if self.q_bias is not None: + qkv_bias = torch.cat((self.q_bias, torch.zeros_like(self.v_bias, requires_grad=False), self.v_bias)) + + qkv = F.linear(input=x, weight=self.qkv.weight, bias=qkv_bias) + qkv = qkv.reshape(B, N, 3, self.num_heads, -1).permute(2, 0, 3, 1, 4) # 3, B, num_heads, N, C + q, k, v = qkv[0], qkv[1], qkv[2] + + if self.rope: + # slightly fast impl + q_t = q[:, :, 1:, :] + ro_q_t = self.rope(q_t) + q = torch.cat((q[:, :, :1, :], ro_q_t), -2).type_as(v) + + k_t = k[:, :, 1:, :] + ro_k_t = self.rope(k_t) + k = torch.cat((k[:, :, :1, :], ro_k_t), -2).type_as(v) + + if self.xattn: + q = q.permute(0, 2, 1, 3) # B, num_heads, N, C -> B, N, num_heads, C + k = k.permute(0, 2, 1, 3) + v = v.permute(0, 2, 1, 3) + + x = xops.memory_efficient_attention( + q, + k, + v, + p=self.xattn_drop, + scale=self.scale, + ) + x = x.reshape(B, N, -1) + x = self.inner_attn_ln(x) + x = self.proj(x) + x = self.proj_drop(x) + else: + q = q * self.scale + attn = q @ k.transpose(-2, -1) + + if self.relative_position_bias_table is not None: + relative_position_bias = self.relative_position_bias_table[self.relative_position_index.view(-1)].view(self.window_size[0] * self.window_size[1] + 1, self.window_size[0] * self.window_size[1] + 1, -1) # Wh*Ww,Wh*Ww,nH + relative_position_bias = relative_position_bias.permute(2, 0, 1).contiguous() # nH, Wh*Ww, Wh*Ww + attn = attn + relative_position_bias.unsqueeze(0).type_as(attn) + + if rel_pos_bias is not None: + attn = attn + rel_pos_bias.type_as(attn) + + if attn_mask is not None: + attn_mask = attn_mask.bool() + attn = attn.masked_fill(~attn_mask[:, None, None, :], float("-inf")) + + attn = attn.softmax(dim=-1) + attn = self.attn_drop(attn) + + x = (attn @ v).transpose(1, 2).reshape(B, N, -1) + x = self.inner_attn_ln(x) + x = self.proj(x) + x = self.proj_drop(x) + return x + + +class Block(nn.Module): + + def __init__( + self, + dim, + num_heads, + mlp_ratio=4.0, + qkv_bias=False, + qk_scale=None, + drop=0.0, + attn_drop=0.0, + drop_path=0.0, + init_values=None, + act_layer=nn.GELU, + norm_layer=nn.LayerNorm, + window_size=None, + attn_head_dim=None, + xattn=False, + rope=None, + postnorm=False, + subln=False, + naiveswiglu=False, + ): + super().__init__() + self.norm1 = norm_layer(dim) + self.attn = Attention( + dim, num_heads=num_heads, qkv_bias=qkv_bias, qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop, window_size=window_size, attn_head_dim=attn_head_dim, xattn=xattn, rope=rope, subln=subln, norm_layer=norm_layer + ) + # NOTE: drop path for stochastic depth, we shall see if this is better than dropout here + self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity() + self.norm2 = norm_layer(dim) + mlp_hidden_dim = int(dim * mlp_ratio) + + if naiveswiglu: + self.mlp = SwiGLU( + in_features=dim, + hidden_features=mlp_hidden_dim, + subln=subln, + norm_layer=norm_layer, + ) + else: + self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, subln=subln, drop=drop) + + if init_values is not None and init_values > 0: + self.gamma_1 = nn.Parameter(init_values * torch.ones((dim)), requires_grad=True) + self.gamma_2 = nn.Parameter(init_values * torch.ones((dim)), requires_grad=True) + else: + self.gamma_1, self.gamma_2 = None, None + + self.postnorm = postnorm + + def forward(self, x, rel_pos_bias=None, attn_mask=None): + if self.gamma_1 is None: + if self.postnorm: + x = x + self.drop_path(self.norm1(self.attn(x, rel_pos_bias=rel_pos_bias, attn_mask=attn_mask))) + x = x + self.drop_path(self.norm2(self.mlp(x))) + else: + x = x + self.drop_path(self.attn(self.norm1(x), rel_pos_bias=rel_pos_bias, attn_mask=attn_mask)) + x = x + self.drop_path(self.mlp(self.norm2(x))) + else: + if self.postnorm: + x = x + self.drop_path(self.gamma_1 * self.norm1(self.attn(x, rel_pos_bias=rel_pos_bias, attn_mask=attn_mask))) + x = x + self.drop_path(self.gamma_2 * self.norm2(self.mlp(x))) + else: + x = x + self.drop_path(self.gamma_1 * self.attn(self.norm1(x), rel_pos_bias=rel_pos_bias, attn_mask=attn_mask)) + x = x + self.drop_path(self.gamma_2 * self.mlp(self.norm2(x))) + return x + + +class PatchEmbed(nn.Module): + """Image to Patch Embedding""" + + def __init__(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768): + super().__init__() + img_size = to_2tuple(img_size) + patch_size = to_2tuple(patch_size) + num_patches = (img_size[1] // patch_size[1]) * (img_size[0] // patch_size[0]) + self.patch_shape = (img_size[0] // patch_size[0], img_size[1] // patch_size[1]) + self.img_size = img_size + self.patch_size = patch_size + self.num_patches = num_patches + + self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size) + + def forward(self, x, **kwargs): + B, C, H, W = x.shape + # FIXME look at relaxing size constraints + assert H == self.img_size[0] and W == self.img_size[1], f"Input image size ({H}*{W}) doesn't match model ({self.img_size[0]}*{self.img_size[1]})." + x = self.proj(x).flatten(2).transpose(1, 2) + return x + + +class RelativePositionBias(nn.Module): + + def __init__(self, window_size, num_heads): + super().__init__() + self.window_size = window_size + self.num_relative_distance = (2 * window_size[0] - 1) * (2 * window_size[1] - 1) + 3 + self.relative_position_bias_table = nn.Parameter(torch.zeros(self.num_relative_distance, num_heads)) # 2*Wh-1 * 2*Ww-1, nH + # cls to token & token 2 cls & cls to cls + + # get pair-wise relative position index for each token inside the window + coords_h = torch.arange(window_size[0]) + coords_w = torch.arange(window_size[1]) + coords = torch.stack(torch.meshgrid([coords_h, coords_w])) # 2, Wh, Ww + coords_flatten = torch.flatten(coords, 1) # 2, Wh*Ww + relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :] # 2, Wh*Ww, Wh*Ww + relative_coords = relative_coords.permute(1, 2, 0).contiguous() # Wh*Ww, Wh*Ww, 2 + relative_coords[:, :, 0] += window_size[0] - 1 # shift to start from 0 + relative_coords[:, :, 1] += window_size[1] - 1 + relative_coords[:, :, 0] *= 2 * window_size[1] - 1 + relative_position_index = torch.zeros(size=(window_size[0] * window_size[1] + 1,) * 2, dtype=relative_coords.dtype) + relative_position_index[1:, 1:] = relative_coords.sum(-1) # Wh*Ww, Wh*Ww + relative_position_index[0, 0:] = self.num_relative_distance - 3 + relative_position_index[0:, 0] = self.num_relative_distance - 2 + relative_position_index[0, 0] = self.num_relative_distance - 1 + + self.register_buffer("relative_position_index", relative_position_index) + + def forward(self): + relative_position_bias = self.relative_position_bias_table[self.relative_position_index.view(-1)].view(self.window_size[0] * self.window_size[1] + 1, self.window_size[0] * self.window_size[1] + 1, -1) # Wh*Ww,Wh*Ww,nH + return relative_position_bias.permute(2, 0, 1).contiguous() # nH, Wh*Ww, Wh*Ww + + +class EVAVisionTransformer(nn.Module): + """Vision Transformer with support for patch or hybrid CNN input stage""" + + def __init__( + self, + img_size=224, + patch_size=16, + in_chans=3, + num_classes=1000, + embed_dim=768, + depth=12, + num_heads=12, + mlp_ratio=4.0, + qkv_bias=False, + qk_scale=None, + drop_rate=0.0, + attn_drop_rate=0.0, + drop_path_rate=0.0, + norm_layer=nn.LayerNorm, + init_values=None, + patch_dropout=0.0, + use_abs_pos_emb=True, + use_rel_pos_bias=False, + use_shared_rel_pos_bias=False, + rope=False, + use_mean_pooling=True, + init_scale=0.001, + grad_checkpointing=False, + xattn=False, + postnorm=False, + pt_hw_seq_len=16, + intp_freq=False, + naiveswiglu=False, + subln=False, + ): + super().__init__() + self.image_size = img_size + self.num_classes = num_classes + self.num_features = self.embed_dim = embed_dim # num_features for consistency with other models + + self.patch_embed = PatchEmbed(img_size=img_size, patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim) + num_patches = self.patch_embed.num_patches + + self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim)) + # self.mask_token = nn.Parameter(torch.zeros(1, 1, embed_dim)) + if use_abs_pos_emb: + self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + 1, embed_dim)) + else: + self.pos_embed = None + self.pos_drop = nn.Dropout(p=drop_rate) + + if use_shared_rel_pos_bias: + self.rel_pos_bias = RelativePositionBias(window_size=self.patch_embed.patch_shape, num_heads=num_heads) + else: + self.rel_pos_bias = None + + if rope: + half_head_dim = embed_dim // num_heads // 2 + hw_seq_len = img_size // patch_size + self.rope = VisionRotaryEmbeddingFast( + dim=half_head_dim, + pt_seq_len=pt_hw_seq_len, + ft_seq_len=hw_seq_len if intp_freq else None, + # patch_dropout=patch_dropout + ) + else: + self.rope = None + + self.naiveswiglu = naiveswiglu + + dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)] # stochastic depth decay rule + self.use_rel_pos_bias = use_rel_pos_bias + self.blocks = nn.ModuleList( + [ + Block( + dim=embed_dim, + num_heads=num_heads, + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + drop=drop_rate, + attn_drop=attn_drop_rate, + drop_path=dpr[i], + norm_layer=norm_layer, + init_values=init_values, + window_size=self.patch_embed.patch_shape if use_rel_pos_bias else None, + xattn=xattn, + rope=self.rope, + postnorm=postnorm, + subln=subln, + naiveswiglu=naiveswiglu, + ) + for i in range(depth) + ] + ) + self.norm = nn.Identity() if use_mean_pooling else norm_layer(embed_dim) + self.fc_norm = norm_layer(embed_dim) if use_mean_pooling else None + self.head = nn.Linear(embed_dim, num_classes, bias=qkv_bias) if num_classes > 0 else nn.Identity() + + if self.pos_embed is not None: + trunc_normal_(self.pos_embed, std=0.02) + + trunc_normal_(self.cls_token, std=0.02) + + self.apply(self._init_weights) + self.fix_init_weight() + + if isinstance(self.head, nn.Linear): + trunc_normal_(self.head.weight, std=0.02) + self.head.weight.data.mul_(init_scale) + if self.head.bias is not None: + self.head.bias.data.mul_(init_scale) + + # setting a patch_dropout of 0. would mean it is disabled and this function would be the identity fn + self.patch_dropout = PatchDropout(patch_dropout) if patch_dropout > 0.0 else nn.Identity() + + self.grad_checkpointing = grad_checkpointing + + def fix_init_weight(self): + def rescale(param, layer_id): + param.div_(math.sqrt(2.0 * layer_id)) + + for layer_id, layer in enumerate(self.blocks): + rescale(layer.attn.proj.weight.data, layer_id + 1) + if self.naiveswiglu: + rescale(layer.mlp.w3.weight.data, layer_id + 1) + else: + rescale(layer.mlp.fc2.weight.data, layer_id + 1) + + def get_cast_dtype(self) -> torch.dtype: + return self.blocks[0].mlp.fc2.weight.dtype + + def _init_weights(self, m): + if isinstance(m, nn.Linear): + trunc_normal_(m.weight, std=0.02) + if m.bias is not None: + nn.init.constant_(m.bias, 0) + elif isinstance(m, nn.LayerNorm): + nn.init.constant_(m.bias, 0) + nn.init.constant_(m.weight, 1.0) + + def get_num_layers(self): + return len(self.blocks) + + def lock(self, unlocked_groups=0, freeze_bn_stats=False): + assert unlocked_groups == 0, "partial locking not currently supported for this model" + for param in self.parameters(): + param.requires_grad = False + + @torch.jit.ignore + def set_grad_checkpointing(self, enable=True): + self.grad_checkpointing = enable + + @torch.jit.ignore + def no_weight_decay(self): + return {"pos_embed", "cls_token"} + + def get_classifier(self): + return self.head + + def reset_classifier(self, num_classes, global_pool=""): + self.num_classes = num_classes + self.head = nn.Linear(self.embed_dim, num_classes) if num_classes > 0 else nn.Identity() + + def forward_features(self, x, return_all_features=False): + + x = self.patch_embed(x) + batch_size, seq_len, _ = x.size() + + cls_tokens = self.cls_token.expand(batch_size, -1, -1) # stole cls_tokens impl from Phil Wang, thanks + x = torch.cat((cls_tokens, x), dim=1) + if self.pos_embed is not None: + x = x + self.pos_embed + x = self.pos_drop(x) + + # a patch_dropout of 0. would mean it is disabled and this function would do nothing but return what was passed in + # if os.getenv("RoPE") == "1": + # if self.training and not isinstance(self.patch_dropout, nn.Identity): + # x, patch_indices_keep = self.patch_dropout(x) + # self.rope.forward = partial(self.rope.forward, patch_indices_keep=patch_indices_keep) + # else: + # self.rope.forward = partial(self.rope.forward, patch_indices_keep=None) + # x = self.patch_dropout(x) + # else: + x = self.patch_dropout(x) + + rel_pos_bias = self.rel_pos_bias() if self.rel_pos_bias is not None else None + for blk in self.blocks: + if self.grad_checkpointing: + x = checkpoint(blk, x, (rel_pos_bias,)) + else: + x = blk(x, rel_pos_bias=rel_pos_bias) + + if not return_all_features: + x = self.norm(x) + if self.fc_norm is not None: + return self.fc_norm(x.mean(1)) + else: + return x[:, 0] + return x + + def forward(self, x, return_all_features=False): + if return_all_features: + return self.forward_features(x, return_all_features) + x = self.forward_features(x) + x = self.head(x) + return x diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/factory.py b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/factory.py new file mode 100644 index 0000000..535eeac --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/factory.py @@ -0,0 +1,528 @@ +import json +import logging +import os +import pathlib +import re +from copy import deepcopy +from pathlib import Path +from typing import Optional, Tuple, Union, Dict, Any +import torch + +try: + import deepspeed +except ImportError: + deepspeed = None + +from .constants import OPENAI_DATASET_MEAN, OPENAI_DATASET_STD +from .model import CLIP, CustomCLIP, convert_weights_to_lp, convert_to_custom_text_state_dict, get_cast_dtype +from .openai import load_openai_model +from .pretrained import is_pretrained_cfg, get_pretrained_cfg, download_pretrained, list_pretrained_tags_by_model +from .transform import image_transform +from .tokenizer import HFTokenizer, tokenize +from .utils import resize_clip_pos_embed, resize_evaclip_pos_embed, resize_visual_pos_embed, resize_eva_pos_embed + + +_MODEL_CONFIG_PATHS = [Path(__file__).parent / f"model_configs/"] +_MODEL_CONFIGS = {} # directory (model_name: config) of model architecture configs + + +def _natural_key(string_): + return [int(s) if s.isdigit() else s for s in re.split(r"(\d+)", string_.lower())] + + +def _rescan_model_configs(): + global _MODEL_CONFIGS + + config_ext = (".json",) + config_files = [] + for config_path in _MODEL_CONFIG_PATHS: + if config_path.is_file() and config_path.suffix in config_ext: + config_files.append(config_path) + elif config_path.is_dir(): + for ext in config_ext: + config_files.extend(config_path.glob(f"*{ext}")) + + for cf in config_files: + with open(cf, "r", encoding="utf8") as f: + model_cfg = json.load(f) + if all(a in model_cfg for a in ("embed_dim", "vision_cfg", "text_cfg")): + _MODEL_CONFIGS[cf.stem] = model_cfg + + _MODEL_CONFIGS = dict(sorted(_MODEL_CONFIGS.items(), key=lambda x: _natural_key(x[0]))) + + +_rescan_model_configs() # initial populate of model config registry + + +def list_models(): + """enumerate available model architectures based on config files""" + return list(_MODEL_CONFIGS.keys()) + + +def add_model_config(path): + """add model config path or file and update registry""" + if not isinstance(path, Path): + path = Path(path) + _MODEL_CONFIG_PATHS.append(path) + _rescan_model_configs() + + +def get_model_config(model_name): + if model_name in _MODEL_CONFIGS: + return deepcopy(_MODEL_CONFIGS[model_name]) + else: + return None + + +def get_tokenizer(model_name): + config = get_model_config(model_name) + tokenizer = HFTokenizer(config["text_cfg"]["hf_tokenizer_name"]) if "hf_tokenizer_name" in config["text_cfg"] else tokenize + return tokenizer + + +# loading openai CLIP weights when is_openai=True for training +def load_state_dict(checkpoint_path: str, map_location: str = "cpu", model_key: str = "model|module|state_dict", is_openai: bool = False, skip_list: list = []): + if is_openai: + model = torch.jit.load(checkpoint_path, map_location="cpu").eval() + state_dict = model.state_dict() + for key in ["input_resolution", "context_length", "vocab_size"]: + state_dict.pop(key, None) + else: + checkpoint = torch.load(checkpoint_path, map_location=map_location) + for mk in model_key.split("|"): + if isinstance(checkpoint, dict) and mk in checkpoint: + state_dict = checkpoint[mk] + break + else: + state_dict = checkpoint + if next(iter(state_dict.items()))[0].startswith("module"): + state_dict = {k[7:]: v for k, v in state_dict.items()} + + for k in skip_list: + if k in list(state_dict.keys()): + logging.info(f"Removing key {k} from pretrained checkpoint") + del state_dict[k] + + if os.getenv("RoPE") == "1": + for k in list(state_dict.keys()): + if "freqs_cos" in k or "freqs_sin" in k: + del state_dict[k] + return state_dict + + +def load_checkpoint(model, checkpoint_path, model_key="model|module|state_dict", strict=True): + state_dict = load_state_dict(checkpoint_path, model_key=model_key, is_openai=False) + # detect old format and make compatible with new format + if "positional_embedding" in state_dict and not hasattr(model, "positional_embedding"): + state_dict = convert_to_custom_text_state_dict(state_dict) + if "text.logit_scale" in state_dict and hasattr(model, "logit_scale"): + state_dict["logit_scale"] = state_dict["text.logit_scale"] + del state_dict["text.logit_scale"] + + # resize_clip_pos_embed for CLIP and open CLIP + if "visual.positional_embedding" in state_dict: + resize_clip_pos_embed(state_dict, model) + # specified to eva_vit_model + elif "visual.pos_embed" in state_dict: + resize_evaclip_pos_embed(state_dict, model) + + # resize_clip_pos_embed(state_dict, model) + incompatible_keys = model.load_state_dict(state_dict, strict=strict) + logging.info(f"incompatible_keys.missing_keys: {incompatible_keys.missing_keys}") + return incompatible_keys + + +def load_clip_visual_state_dict(checkpoint_path: str, map_location: str = "cpu", is_openai: bool = False, skip_list: list = []): + state_dict = load_state_dict(checkpoint_path, map_location=map_location, is_openai=is_openai, skip_list=skip_list) + + for k in list(state_dict.keys()): + if not k.startswith("visual."): + del state_dict[k] + for k in list(state_dict.keys()): + if k.startswith("visual."): + new_k = k[7:] + state_dict[new_k] = state_dict[k] + del state_dict[k] + return state_dict + + +def load_clip_text_state_dict(checkpoint_path: str, map_location: str = "cpu", is_openai: bool = False, skip_list: list = []): + state_dict = load_state_dict(checkpoint_path, map_location=map_location, is_openai=is_openai, skip_list=skip_list) + + for k in list(state_dict.keys()): + if k.startswith("visual."): + del state_dict[k] + return state_dict + + +def get_pretrained_tag(pretrained_model): + pretrained_model = pretrained_model.lower() + if "laion" in pretrained_model or "open_clip" in pretrained_model: + return "open_clip" + elif "openai" in pretrained_model: + return "clip" + elif "eva" in pretrained_model and "clip" in pretrained_model: + return "eva_clip" + else: + return "other" + + +def load_zero_partitions(model, state_dict, is_deepspeed_zero3_enabled, pretrained_model_path, ignore_mismatched_sizes=False): + """ + adept from pytorch lightning and transformers + with deepspeed.zero.Init(): + model = MyModel() + state_dict = torch.load(model_path, map_location="cpu") + load_zero_partitions(model, prefix="") + """ + + # because zero3 puts placeholders in model params, this context + # manager gathers (unpartitions) the params of the current layer, then loads from + # the state dict and then re-partitions them again + model_state_dict = model.state_dict() + expected_keys = list(model_state_dict.keys()) + loaded_keys = list(state_dict.keys()) + missing_keys = list(set(expected_keys) - set(loaded_keys)) + unexpected_keys = list(set(loaded_keys) - set(expected_keys)) + + # Mistmatched keys contains tuples key/shape1/shape2 of weights in the checkpoint that have a shape not + # matching the weights in the model. + mismatched_keys = [] + if ignore_mismatched_sizes: + for checkpoint_key in loaded_keys: + model_key = checkpoint_key + + if model_key in model_state_dict and state_dict[checkpoint_key].shape != model_state_dict[model_key].shape: + mismatched_keys.append((checkpoint_key, state_dict[checkpoint_key].shape, model_state_dict[model_key].shape)) + del state_dict[checkpoint_key] + # copy state_dict so _load_from_state_dict can modify it + metadata = getattr(state_dict, "_metadata", None) + state_dict = state_dict.copy() + if metadata is not None: + state_dict._metadata = metadata + + error_msgs = [] + + # PyTorch's `_load_from_state_dict` does not copy parameters in a module's descendants + # so we need to apply the function recursively. + def load(module, prefix=""): + local_metadata = {} if metadata is None else metadata.get(prefix[:-1], {}) + args = (state_dict, prefix, local_metadata, True, [], [], error_msgs) + if is_deepspeed_zero3_enabled: + # because zero3 puts placeholders in model params, this context + # manager gathers (unpartitions) the params of the current layer, then loads from + # the state dict and then re-partitions them again + with deepspeed.zero.GatheredParameters(list(module.parameters(recurse=False)), modifier_rank=0): + if torch.distributed.get_rank() == 0: + module._load_from_state_dict(*args) + else: + module._load_from_state_dict(*args) + + for name, child in module._modules.items(): + if child is not None: + load(child, prefix + name + ".") + + # Make sure we are able to load base models as well as derived models (with heads) + start_prefix = "" + model_to_load = model + load(model_to_load, prefix=start_prefix) + del state_dict + if len(error_msgs) > 0: + error_msg = "\n\t".join(error_msgs) + if "size mismatch" in error_msg: + error_msg += "\n\tYou may consider adding `ignore_mismatched_sizes=True` in the model `from_pretrained` method." + raise RuntimeError(f"Error(s) in loading state_dict for {model.__class__.__name__}:\n\t{error_msg}") + if len(unexpected_keys) > 0: + logging.warning( + f"Some weights of the model checkpoint at {pretrained_model_path} were not used when" + f" initializing {model.__class__.__name__}: {unexpected_keys}\n- This IS expected if you are" + f" initializing {model.__class__.__name__} from the checkpoint of a model trained on another task or" + " with another architecture (e.g. initializing a BertForSequenceClassification model from a" + " BertForPreTraining model).\n- This IS NOT expected if you are initializing" + f" {model.__class__.__name__} from the checkpoint of a model that you expect to be exactly identical" + " (initializing a BertForSequenceClassification model from a BertForSequenceClassification model)." + ) + else: + logging.info(f"All model checkpoint weights were used when initializing {model.__class__.__name__}.\n") + if len(missing_keys) > 0: + logging.warning( + f"Some weights of {model.__class__.__name__} were not initialized from the model checkpoint at" + f" {pretrained_model_path} and are newly initialized: {missing_keys}\nYou should probably" + " TRAIN this model on a down-stream task to be able to use it for predictions and inference." + ) + elif len(mismatched_keys) == 0: + logging.info( + f"All the weights of {model.__class__.__name__} were initialized from the model checkpoint at" + f" {pretrained_model_path}.\nIf your task is similar to the task the model of the checkpoint" + f" was trained on, you can already use {model.__class__.__name__} for predictions without further" + " training." + ) + if len(mismatched_keys) > 0: + mismatched_warning = "\n".join([f"- {key}: found shape {shape1} in the checkpoint and {shape2} in the model instantiated" for key, shape1, shape2 in mismatched_keys]) + logging.warning( + f"Some weights of {model.__class__.__name__} were not initialized from the model checkpoint at" + f" {pretrained_model_path} and are newly initialized because the shapes did not" + f" match:\n{mismatched_warning}\nYou should probably TRAIN this model on a down-stream task to be able" + " to use it for predictions and inference." + ) + + +def load_pretrained_checkpoint(model, visual_checkpoint_path, text_checkpoint_path, strict=True, visual_model=None, text_model=None, model_key="model|module|state_dict", skip_list=[]): + visual_tag = get_pretrained_tag(visual_model) + text_tag = get_pretrained_tag(text_model) + + logging.info(f"num of model state_dict keys: {len(model.state_dict().keys())}") + visual_incompatible_keys, text_incompatible_keys = None, None + if visual_checkpoint_path: + if visual_tag == "eva_clip" or visual_tag == "open_clip": + visual_state_dict = load_clip_visual_state_dict(visual_checkpoint_path, is_openai=False, skip_list=skip_list) + elif visual_tag == "clip": + visual_state_dict = load_clip_visual_state_dict(visual_checkpoint_path, is_openai=True, skip_list=skip_list) + else: + visual_state_dict = load_state_dict(visual_checkpoint_path, model_key=model_key, is_openai=False, skip_list=skip_list) + + # resize_clip_pos_embed for CLIP and open CLIP + if "positional_embedding" in visual_state_dict: + resize_visual_pos_embed(visual_state_dict, model) + # specified to EVA model + elif "pos_embed" in visual_state_dict: + resize_eva_pos_embed(visual_state_dict, model) + + visual_incompatible_keys = model.visual.load_state_dict(visual_state_dict, strict=strict) + logging.info(f"num of loaded visual_state_dict keys: {len(visual_state_dict.keys())}") + logging.info(f"visual_incompatible_keys.missing_keys: {visual_incompatible_keys.missing_keys}") + + if text_checkpoint_path: + if text_tag == "eva_clip" or text_tag == "open_clip": + text_state_dict = load_clip_text_state_dict(text_checkpoint_path, is_openai=False, skip_list=skip_list) + elif text_tag == "clip": + text_state_dict = load_clip_text_state_dict(text_checkpoint_path, is_openai=True, skip_list=skip_list) + else: + text_state_dict = load_state_dict(visual_checkpoint_path, model_key=model_key, is_openai=False, skip_list=skip_list) + + text_incompatible_keys = model.text.load_state_dict(text_state_dict, strict=strict) + + logging.info(f"num of loaded text_state_dict keys: {len(text_state_dict.keys())}") + logging.info(f"text_incompatible_keys.missing_keys: {text_incompatible_keys.missing_keys}") + + return visual_incompatible_keys, text_incompatible_keys + + +def create_model( + model_name: str, + pretrained: Optional[str] = None, + precision: str = "fp32", + device: Union[str, torch.device] = "cpu", + jit: bool = False, + force_quick_gelu: bool = False, + force_custom_clip: bool = False, + force_patch_dropout: Optional[float] = None, + pretrained_image: str = "", + pretrained_text: str = "", + pretrained_hf: bool = True, + pretrained_visual_model: str = None, + pretrained_text_model: str = None, + cache_dir: Optional[str] = None, + skip_list: list = [], +): + model_name = model_name.replace("/", "-") # for callers using old naming with / in ViT names + if isinstance(device, str): + device = torch.device(device) + + if pretrained and pretrained.lower() == "openai": + logging.info(f"Loading pretrained {model_name} from OpenAI.") + model = load_openai_model( + model_name, + precision=precision, + device=device, + jit=jit, + cache_dir=cache_dir, + ) + else: + model_cfg = get_model_config(model_name) + if model_cfg is not None: + logging.info(f"Loaded {model_name} model config.") + else: + logging.error(f"Model config for {model_name} not found; available models {list_models()}.") + raise RuntimeError(f"Model config for {model_name} not found.") + + if "rope" in model_cfg.get("vision_cfg", {}): + if model_cfg["vision_cfg"]["rope"]: + os.environ["RoPE"] = "1" + else: + os.environ["RoPE"] = "0" + + if force_quick_gelu: + # override for use of QuickGELU on non-OpenAI transformer models + model_cfg["quick_gelu"] = True + + if force_patch_dropout is not None: + # override the default patch dropout value + model_cfg["vision_cfg"]["patch_dropout"] = force_patch_dropout + + cast_dtype = get_cast_dtype(precision) + custom_clip = model_cfg.pop("custom_text", False) or force_custom_clip or ("hf_model_name" in model_cfg["text_cfg"]) + + if custom_clip: + if "hf_model_name" in model_cfg.get("text_cfg", {}): + model_cfg["text_cfg"]["hf_model_pretrained"] = pretrained_hf + model = CustomCLIP(**model_cfg, cast_dtype=cast_dtype) + else: + model = CLIP(**model_cfg, cast_dtype=cast_dtype) + + pretrained_cfg = {} + if pretrained: + checkpoint_path = "" + pretrained_cfg = get_pretrained_cfg(model_name, pretrained) + if pretrained_cfg: + checkpoint_path = download_pretrained(pretrained_cfg, cache_dir=cache_dir) + elif os.path.exists(pretrained): + checkpoint_path = pretrained + + if checkpoint_path: + logging.info(f"Loading pretrained {model_name} weights ({pretrained}).") + load_checkpoint(model, checkpoint_path, model_key="model|module|state_dict", strict=False) + else: + error_str = f"Pretrained weights ({pretrained}) not found for model {model_name}." f"Available pretrained tags ({list_pretrained_tags_by_model(model_name)}." + logging.warning(error_str) + raise RuntimeError(error_str) + else: + visual_checkpoint_path = "" + text_checkpoint_path = "" + + if pretrained_image: + pretrained_visual_model = pretrained_visual_model.replace("/", "-") # for callers using old naming with / in ViT names + pretrained_image_cfg = get_pretrained_cfg(pretrained_visual_model, pretrained_image) + if "timm_model_name" in model_cfg.get("vision_cfg", {}): + # pretrained weight loading for timm models set via vision_cfg + model_cfg["vision_cfg"]["timm_model_pretrained"] = True + elif pretrained_image_cfg: + visual_checkpoint_path = download_pretrained(pretrained_image_cfg, cache_dir=cache_dir) + elif os.path.exists(pretrained_image): + visual_checkpoint_path = pretrained_image + else: + logging.warning(f"Pretrained weights ({visual_checkpoint_path}) not found for model {model_name}.visual.") + raise RuntimeError(f"Pretrained weights ({visual_checkpoint_path}) not found for model {model_name}.visual.") + + if pretrained_text: + pretrained_text_model = pretrained_text_model.replace("/", "-") # for callers using old naming with / in ViT names + pretrained_text_cfg = get_pretrained_cfg(pretrained_text_model, pretrained_text) + if pretrained_image_cfg: + text_checkpoint_path = download_pretrained(pretrained_text_cfg, cache_dir=cache_dir) + elif os.path.exists(pretrained_text): + text_checkpoint_path = pretrained_text + else: + logging.warning(f"Pretrained weights ({text_checkpoint_path}) not found for model {model_name}.text.") + raise RuntimeError(f"Pretrained weights ({text_checkpoint_path}) not found for model {model_name}.text.") + + if visual_checkpoint_path: + logging.info(f"Loading pretrained {model_name}.visual weights ({visual_checkpoint_path}).") + if text_checkpoint_path: + logging.info(f"Loading pretrained {model_name}.text weights ({text_checkpoint_path}).") + + if visual_checkpoint_path or text_checkpoint_path: + load_pretrained_checkpoint(model, visual_checkpoint_path, text_checkpoint_path, strict=False, visual_model=pretrained_visual_model, text_model=pretrained_text_model, model_key="model|module|state_dict", skip_list=skip_list) + + if "fp16" in precision or "bf16" in precision: + logging.info(f"convert precision to {precision}") + model = model.to(torch.bfloat16) if "bf16" in precision else model.to(torch.float16) + + # model.to(device=device) + + # set image / mean metadata from pretrained_cfg if available, or use default + model.visual.image_mean = pretrained_cfg.get("mean", None) or OPENAI_DATASET_MEAN + model.visual.image_std = pretrained_cfg.get("std", None) or OPENAI_DATASET_STD + + if jit: + model = torch.jit.script(model) + + return model + + +def create_model_and_transforms( + model_name: str, + pretrained: Optional[str] = None, + precision: str = "fp32", + device: Union[str, torch.device] = "cpu", + jit: bool = False, + force_quick_gelu: bool = False, + force_custom_clip: bool = False, + force_patch_dropout: Optional[float] = None, + pretrained_image: str = "", + pretrained_text: str = "", + pretrained_hf: bool = True, + pretrained_visual_model: str = None, + pretrained_text_model: str = None, + image_mean: Optional[Tuple[float, ...]] = None, + image_std: Optional[Tuple[float, ...]] = None, + cache_dir: Optional[str] = None, + skip_list: list = [], +): + model = create_model( + model_name, + pretrained, + precision=precision, + device=device, + jit=jit, + force_quick_gelu=force_quick_gelu, + force_custom_clip=force_custom_clip, + force_patch_dropout=force_patch_dropout, + pretrained_image=pretrained_image, + pretrained_text=pretrained_text, + pretrained_hf=pretrained_hf, + pretrained_visual_model=pretrained_visual_model, + pretrained_text_model=pretrained_text_model, + cache_dir=cache_dir, + skip_list=skip_list, + ) + + image_mean = image_mean or getattr(model.visual, "image_mean", None) + image_std = image_std or getattr(model.visual, "image_std", None) + preprocess_train = image_transform(model.visual.image_size, is_train=True, mean=image_mean, std=image_std) + preprocess_val = image_transform(model.visual.image_size, is_train=False, mean=image_mean, std=image_std) + + return model, preprocess_train, preprocess_val + + +def create_model_from_pretrained( + model_name: str, + pretrained: str, + precision: str = "fp32", + device: Union[str, torch.device] = "cpu", + jit: bool = False, + force_quick_gelu: bool = False, + force_custom_clip: bool = False, + force_patch_dropout: Optional[float] = None, + return_transform: bool = True, + image_mean: Optional[Tuple[float, ...]] = None, + image_std: Optional[Tuple[float, ...]] = None, + cache_dir: Optional[str] = None, + is_frozen: bool = False, +): + if not is_pretrained_cfg(model_name, pretrained) and not os.path.exists(pretrained): + raise RuntimeError(f"{pretrained} is not a valid pretrained cfg or checkpoint for {model_name}." f" Use open_clip.list_pretrained() to find one.") + + model = create_model( + model_name, + pretrained, + precision=precision, + device=device, + jit=jit, + force_quick_gelu=force_quick_gelu, + force_custom_clip=force_custom_clip, + force_patch_dropout=force_patch_dropout, + cache_dir=cache_dir, + ) + + if is_frozen: + for param in model.parameters(): + param.requires_grad = False + + if not return_transform: + return model + + image_mean = image_mean or getattr(model.visual, "image_mean", None) + image_std = image_std or getattr(model.visual, "image_std", None) + preprocess = image_transform(model.visual.image_size, is_train=False, mean=image_mean, std=image_std) + + return model, preprocess diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/hf_configs.py b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/hf_configs.py new file mode 100644 index 0000000..ddd2c67 --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/hf_configs.py @@ -0,0 +1,57 @@ +# HF architecture dict: +arch_dict = { + # https://huggingface.co/docs/transformers/model_doc/roberta#roberta + "roberta": { + "config_names": { + "context_length": "max_position_embeddings", + "vocab_size": "vocab_size", + "width": "hidden_size", + "heads": "num_attention_heads", + "layers": "num_hidden_layers", + "layer_attr": "layer", + "token_embeddings_attr": "embeddings", + }, + "pooler": "mean_pooler", + }, + # https://huggingface.co/docs/transformers/model_doc/xlm-roberta#transformers.XLMRobertaConfig + "xlm-roberta": { + "config_names": { + "context_length": "max_position_embeddings", + "vocab_size": "vocab_size", + "width": "hidden_size", + "heads": "num_attention_heads", + "layers": "num_hidden_layers", + "layer_attr": "layer", + "token_embeddings_attr": "embeddings", + }, + "pooler": "mean_pooler", + }, + # https://huggingface.co/docs/transformers/model_doc/mt5#mt5 + "mt5": { + "config_names": { + # unlimited seqlen + # https://github.com/google-research/text-to-text-transfer-transformer/issues/273 + # https://github.com/huggingface/transformers/blob/v4.24.0/src/transformers/models/t5/modeling_t5.py#L374 + "context_length": "", + "vocab_size": "vocab_size", + "width": "d_model", + "heads": "num_heads", + "layers": "num_layers", + "layer_attr": "block", + "token_embeddings_attr": "embed_tokens", + }, + "pooler": "mean_pooler", + }, + "bert": { + "config_names": { + "context_length": "max_position_embeddings", + "vocab_size": "vocab_size", + "width": "hidden_size", + "heads": "num_attention_heads", + "layers": "num_hidden_layers", + "layer_attr": "layer", + "token_embeddings_attr": "embeddings", + }, + "pooler": "mean_pooler", + }, +} diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/hf_model.py b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/hf_model.py new file mode 100644 index 0000000..a156624 --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/hf_model.py @@ -0,0 +1,240 @@ +""" huggingface model adapter + +Wraps HuggingFace transformers (https://github.com/huggingface/transformers) models for use as a text tower in CLIP model. +""" + +import re + +import torch +import torch.nn as nn +from torch.nn import functional as F +from torch import TensorType + +try: + import transformers + from transformers import AutoModel, AutoModelForMaskedLM, AutoTokenizer, AutoConfig, PretrainedConfig + from transformers.modeling_outputs import BaseModelOutput, BaseModelOutputWithPooling, BaseModelOutputWithPoolingAndCrossAttentions +except ImportError as e: + transformers = None + + class BaseModelOutput: + pass + + class PretrainedConfig: + pass + + +from .hf_configs import arch_dict + + +# utils +def _camel2snake(s): + return re.sub(r"(? TensorType: + # image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(x.device) + # attn_mask = (x != self.config.pad_token_id).long() + # out = self.transformer( + # input_ids=x, + # attention_mask=attn_mask, + # encoder_hidden_states = image_embeds, + # encoder_attention_mask = image_atts, + # ) + # pooled_out = self.pooler(out, attn_mask) + + # return self.itm_proj(pooled_out) + + def mask(self, input_ids, vocab_size, device, targets=None, masked_indices=None, probability_matrix=None): + if masked_indices is None: + masked_indices = torch.bernoulli(probability_matrix).bool() + + masked_indices[input_ids == self.tokenizer.pad_token_id] = False + masked_indices[input_ids == self.tokenizer.cls_token_id] = False + + if targets is not None: + targets[~masked_indices] = -100 # We only compute loss on masked tokens + + # 80% of the time, we replace masked input tokens with tokenizer.mask_token ([MASK]) + indices_replaced = torch.bernoulli(torch.full(input_ids.shape, 0.8)).bool() & masked_indices + input_ids[indices_replaced] = self.tokenizer.mask_token_id + + # 10% of the time, we replace masked input tokens with random word + indices_random = torch.bernoulli(torch.full(input_ids.shape, 0.5)).bool() & masked_indices & ~indices_replaced + random_words = torch.randint(vocab_size, input_ids.shape, dtype=torch.long).to(device) + input_ids[indices_random] = random_words[indices_random] + # The rest of the time (10% of the time) we keep the masked input tokens unchanged + + if targets is not None: + return input_ids, targets + else: + return input_ids + + def forward_mlm(self, input_ids, image_embeds, mlm_probability=0.25): + labels = input_ids.clone() + attn_mask = (input_ids != self.config.pad_token_id).long() + image_atts = torch.ones(image_embeds.size()[:-1], dtype=torch.long).to(input_ids.device) + vocab_size = getattr(self.config, arch_dict[self.config.model_type]["config_names"]["vocab_size"]) + probability_matrix = torch.full(labels.shape, mlm_probability) + input_ids, labels = self.mask(input_ids, vocab_size, input_ids.device, targets=labels, probability_matrix=probability_matrix) + mlm_output = self.transformer( + input_ids, + attention_mask=attn_mask, + encoder_hidden_states=image_embeds, + encoder_attention_mask=image_atts, + return_dict=True, + labels=labels, + ) + return mlm_output.loss + # mlm_output = self.transformer(input_ids, + # attention_mask = attn_mask, + # encoder_hidden_states = image_embeds, + # encoder_attention_mask = image_atts, + # return_dict = True, + # ).last_hidden_state + # logits = self.mlm_proj(mlm_output) + + # # logits = logits[:, :-1, :].contiguous().view(-1, vocab_size) + # logits = logits[:, 1:, :].contiguous().view(-1, vocab_size) + # labels = labels[:, 1:].contiguous().view(-1) + + # mlm_loss = F.cross_entropy( + # logits, + # labels, + # # label_smoothing=0.1, + # ) + # return mlm_loss + + def forward(self, x: TensorType) -> TensorType: + attn_mask = (x != self.config.pad_token_id).long() + out = self.transformer(input_ids=x, attention_mask=attn_mask) + pooled_out = self.pooler(out, attn_mask) + + return self.proj(pooled_out) + + def lock(self, unlocked_layers: int = 0, freeze_layer_norm: bool = True): + if not unlocked_layers: # full freezing + for n, p in self.transformer.named_parameters(): + p.requires_grad = (not freeze_layer_norm) if "LayerNorm" in n.split(".") else False + return + + encoder = self.transformer.encoder if hasattr(self.transformer, "encoder") else self.transformer + layer_list = getattr(encoder, arch_dict[self.config.model_type]["config_names"]["layer_attr"]) + print(f"Unlocking {unlocked_layers}/{len(layer_list) + 1} layers of hf model") + embeddings = getattr(self.transformer, arch_dict[self.config.model_type]["config_names"]["token_embeddings_attr"]) + modules = [embeddings, *layer_list][:-unlocked_layers] + # freeze layers + for module in modules: + for n, p in module.named_parameters(): + p.requires_grad = (not freeze_layer_norm) if "LayerNorm" in n.split(".") else False + + @torch.jit.ignore + def set_grad_checkpointing(self, enable=True): + self.transformer.gradient_checkpointing_enable() + + def get_num_layers(self): + encoder = self.transformer.encoder if hasattr(self.transformer, "encoder") else self.transformer + layer_list = getattr(encoder, arch_dict[self.config.model_type]["config_names"]["layer_attr"]) + return len(layer_list) + + def init_parameters(self): + pass diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/loss.py b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/loss.py new file mode 100644 index 0000000..ec4c995 --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/loss.py @@ -0,0 +1,123 @@ +import math +import torch +import torch.nn as nn +from torch.nn import functional as F + +try: + import torch.distributed.nn + from torch import distributed as dist + + has_distributed = True +except ImportError: + has_distributed = False + +try: + import horovod.torch as hvd +except ImportError: + hvd = None + +from timm.loss import LabelSmoothingCrossEntropy + + +def gather_features(image_features, text_features, local_loss=False, gather_with_grad=False, rank=0, world_size=1, use_horovod=False): + assert has_distributed, "torch.distributed did not import correctly, please use a PyTorch version with support." + if use_horovod: + assert hvd is not None, "Please install horovod" + if gather_with_grad: + all_image_features = hvd.allgather(image_features) + all_text_features = hvd.allgather(text_features) + else: + with torch.no_grad(): + all_image_features = hvd.allgather(image_features) + all_text_features = hvd.allgather(text_features) + if not local_loss: + # ensure grads for local rank when all_* features don't have a gradient + gathered_image_features = list(all_image_features.chunk(world_size, dim=0)) + gathered_text_features = list(all_text_features.chunk(world_size, dim=0)) + gathered_image_features[rank] = image_features + gathered_text_features[rank] = text_features + all_image_features = torch.cat(gathered_image_features, dim=0) + all_text_features = torch.cat(gathered_text_features, dim=0) + else: + # We gather tensors from all gpus + if gather_with_grad: + all_image_features = torch.cat(torch.distributed.nn.all_gather(image_features), dim=0) + all_text_features = torch.cat(torch.distributed.nn.all_gather(text_features), dim=0) + # all_image_features = torch.cat(torch.distributed.nn.all_gather(image_features, async_op=True), dim=0) + # all_text_features = torch.cat(torch.distributed.nn.all_gather(text_features, async_op=True), dim=0) + else: + gathered_image_features = [torch.zeros_like(image_features) for _ in range(world_size)] + gathered_text_features = [torch.zeros_like(text_features) for _ in range(world_size)] + dist.all_gather(gathered_image_features, image_features) + dist.all_gather(gathered_text_features, text_features) + if not local_loss: + # ensure grads for local rank when all_* features don't have a gradient + gathered_image_features[rank] = image_features + gathered_text_features[rank] = text_features + all_image_features = torch.cat(gathered_image_features, dim=0) + all_text_features = torch.cat(gathered_text_features, dim=0) + + return all_image_features, all_text_features + + +class ClipLoss(nn.Module): + + def __init__( + self, + local_loss=False, + gather_with_grad=False, + cache_labels=False, + rank=0, + world_size=1, + use_horovod=False, + smoothing=0.0, + ): + super().__init__() + self.local_loss = local_loss + self.gather_with_grad = gather_with_grad + self.cache_labels = cache_labels + self.rank = rank + self.world_size = world_size + self.use_horovod = use_horovod + self.label_smoothing_cross_entropy = LabelSmoothingCrossEntropy(smoothing=smoothing) if smoothing > 0 else None + + # cache state + self.prev_num_logits = 0 + self.labels = {} + + def forward(self, image_features, text_features, logit_scale=1.0): + device = image_features.device + if self.world_size > 1: + all_image_features, all_text_features = gather_features(image_features, text_features, self.local_loss, self.gather_with_grad, self.rank, self.world_size, self.use_horovod) + + if self.local_loss: + logits_per_image = logit_scale * image_features @ all_text_features.T + logits_per_text = logit_scale * text_features @ all_image_features.T + else: + logits_per_image = logit_scale * all_image_features @ all_text_features.T + logits_per_text = logits_per_image.T + else: + logits_per_image = logit_scale * image_features @ text_features.T + logits_per_text = logit_scale * text_features @ image_features.T + # calculated ground-truth and cache if enabled + num_logits = logits_per_image.shape[0] + if self.prev_num_logits != num_logits or device not in self.labels: + labels = torch.arange(num_logits, device=device, dtype=torch.long) + if self.world_size > 1 and self.local_loss: + labels = labels + num_logits * self.rank + if self.cache_labels: + self.labels[device] = labels + self.prev_num_logits = num_logits + else: + labels = self.labels[device] + + if self.label_smoothing_cross_entropy: + total_loss = (self.label_smoothing_cross_entropy(logits_per_image, labels) + self.label_smoothing_cross_entropy(logits_per_text, labels)) / 2 + else: + total_loss = (F.cross_entropy(logits_per_image, labels) + F.cross_entropy(logits_per_text, labels)) / 2 + + acc = None + i2t_acc = (logits_per_image.argmax(-1) == labels).sum() / len(logits_per_image) + t2i_acc = (logits_per_text.argmax(-1) == labels).sum() / len(logits_per_text) + acc = {"i2t": i2t_acc, "t2i": t2i_acc} + return total_loss, acc diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model.py b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model.py new file mode 100644 index 0000000..b2f3a23 --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model.py @@ -0,0 +1,429 @@ +""" CLIP Model + +Adapted from https://github.com/openai/CLIP. Originally MIT License, Copyright (c) 2021 OpenAI. +""" + +import os +from dataclasses import dataclass +from typing import Optional, Tuple, Union +from functools import partial + +import numpy as np +import torch +import torch.nn.functional as F +from torch import nn + +try: + from .hf_model import HFTextEncoder +except: + HFTextEncoder = None +from .modified_resnet import ModifiedResNet +from .timm_model import TimmModel +from .eva_vit_model import EVAVisionTransformer +from .transformer import LayerNorm, QuickGELU, Attention, VisionTransformer, TextTransformer + +try: + from apex.normalization import FusedLayerNorm +except: + FusedLayerNorm = LayerNorm + # print("Please 'pip install apex'") + +try: + import xformers.ops as xops +except ImportError: + xops = None + # print("Please 'pip install xformers'") + + +class RMSnorm(nn.Module): + """ + adepted from transformers T5LayerNorm + """ + + def __init__(self, hidden_size, eps=1e-6): + """ + Construct a layernorm module in the T5 style. No bias and no subtraction of mean. + """ + super().__init__() + self.weight = nn.Parameter(torch.ones(hidden_size)) + self.variance_epsilon = eps + + def forward(self, hidden_states): + # T5 uses a layer_norm which only scales and doesn't shift, which is also known as Root Mean + # Square Layer Normalization https://arxiv.org/abs/1910.07467 thus varience is calculated + # w/o mean and there is no bias. Additionally we want to make sure that the accumulation for + # half-precision inputs is done in fp32 + + variance = hidden_states.to(torch.float32).pow(2).mean(-1, keepdim=True) + hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon) + + # convert into half-precision if necessary + if self.weight.dtype in [torch.float16, torch.bfloat16]: + hidden_states = hidden_states.to(self.weight.dtype) + + return self.weight * hidden_states + + +@dataclass +class CLIPVisionCfg: + layers: Union[Tuple[int, int, int, int], int] = 12 + width: int = 768 + head_width: int = 64 + mlp_ratio: float = 4.0 + patch_size: int = 16 + image_size: Union[Tuple[int, int], int] = 224 + ls_init_value: Optional[float] = None # layer scale initial value + patch_dropout: float = 0.0 # what fraction of patches to dropout during training (0 would mean disabled and no patches dropped) - 0.5 to 0.75 recommended in the paper for optimal results + global_average_pool: bool = False # whether to global average pool the last embedding layer, instead of using CLS token (https://arxiv.org/abs/2205.01580) + drop_path_rate: Optional[float] = None # drop path rate + timm_model_name: str = None # a valid model name overrides layers, width, patch_size + timm_model_pretrained: bool = False # use (imagenet) pretrained weights for named model + timm_pool: str = "avg" # feature pooling for timm model ('abs_attn', 'rot_attn', 'avg', '') + timm_proj: str = "linear" # linear projection for timm model output ('linear', 'mlp', '') + timm_proj_bias: bool = False # enable bias final projection + eva_model_name: str = None # a valid eva model name overrides layers, width, patch_size + qkv_bias: bool = True + fusedLN: bool = False + xattn: bool = False + postnorm: bool = False + rope: bool = False + pt_hw_seq_len: int = 16 # 224/14 + intp_freq: bool = False + naiveswiglu: bool = False + subln: bool = False + use_rms_norm: bool = False + + +@dataclass +class CLIPTextCfg: + context_length: int = 77 + vocab_size: int = 49408 + width: int = 512 + heads: int = 8 + layers: int = 12 + ls_init_value: Optional[float] = None # layer scale initial value + hf_model_name: str = None + hf_tokenizer_name: str = None + hf_model_pretrained: bool = True + proj: str = "mlp" + pooler_type: str = "mean_pooler" + masked_language_modeling: bool = False + fusedLN: bool = False + xattn: bool = False + attn_mask: bool = True + + +def get_cast_dtype(precision: str): + cast_dtype = None + if precision == "bf16": + cast_dtype = torch.bfloat16 + elif precision == "fp16": + cast_dtype = torch.float16 + return cast_dtype + + +def _build_vision_tower(embed_dim: int, vision_cfg: CLIPVisionCfg, quick_gelu: bool = False, cast_dtype: Optional[torch.dtype] = None): + if isinstance(vision_cfg, dict): + vision_cfg = CLIPVisionCfg(**vision_cfg) + + # OpenAI models are pretrained w/ QuickGELU but native nn.GELU is both faster and more + # memory efficient in recent PyTorch releases (>= 1.10). + # NOTE: timm models always use native GELU regardless of quick_gelu flag. + act_layer = QuickGELU if quick_gelu else nn.GELU + + if vision_cfg.eva_model_name: + vision_heads = vision_cfg.width // vision_cfg.head_width + + norm_layer = RMSnorm if vision_cfg.use_rms_norm else LayerNorm + + visual = EVAVisionTransformer( + img_size=vision_cfg.image_size, + patch_size=vision_cfg.patch_size, + num_classes=embed_dim, + use_mean_pooling=vision_cfg.global_average_pool, # False + init_values=vision_cfg.ls_init_value, + patch_dropout=vision_cfg.patch_dropout, + embed_dim=vision_cfg.width, + depth=vision_cfg.layers, + num_heads=vision_heads, + mlp_ratio=vision_cfg.mlp_ratio, + qkv_bias=vision_cfg.qkv_bias, + drop_path_rate=vision_cfg.drop_path_rate, + norm_layer=partial(norm_layer, eps=1e-6), + xattn=vision_cfg.xattn, + rope=vision_cfg.rope, + postnorm=vision_cfg.postnorm, + pt_hw_seq_len=vision_cfg.pt_hw_seq_len, # 224/14 + intp_freq=vision_cfg.intp_freq, + naiveswiglu=vision_cfg.naiveswiglu, + subln=vision_cfg.subln, + ) + elif vision_cfg.timm_model_name: + visual = TimmModel( + vision_cfg.timm_model_name, pretrained=vision_cfg.timm_model_pretrained, pool=vision_cfg.timm_pool, proj=vision_cfg.timm_proj, proj_bias=vision_cfg.timm_proj_bias, embed_dim=embed_dim, image_size=vision_cfg.image_size + ) + act_layer = nn.GELU # so that text transformer doesn't use QuickGELU w/ timm models + elif isinstance(vision_cfg.layers, (tuple, list)): + vision_heads = vision_cfg.width * 32 // vision_cfg.head_width + visual = ModifiedResNet(layers=vision_cfg.layers, output_dim=embed_dim, heads=vision_heads, image_size=vision_cfg.image_size, width=vision_cfg.width) + else: + vision_heads = vision_cfg.width // vision_cfg.head_width + norm_layer = LayerNormFp32 if cast_dtype in (torch.float16, torch.bfloat16) else LayerNorm + visual = VisionTransformer( + image_size=vision_cfg.image_size, + patch_size=vision_cfg.patch_size, + width=vision_cfg.width, + layers=vision_cfg.layers, + heads=vision_heads, + mlp_ratio=vision_cfg.mlp_ratio, + ls_init_value=vision_cfg.ls_init_value, + patch_dropout=vision_cfg.patch_dropout, + global_average_pool=vision_cfg.global_average_pool, + output_dim=embed_dim, + act_layer=act_layer, + norm_layer=norm_layer, + ) + + return visual + + +def _build_text_tower( + embed_dim: int, + text_cfg: CLIPTextCfg, + quick_gelu: bool = False, + cast_dtype: Optional[torch.dtype] = None, +): + if isinstance(text_cfg, dict): + text_cfg = CLIPTextCfg(**text_cfg) + + if text_cfg.hf_model_name: + text = HFTextEncoder(text_cfg.hf_model_name, output_dim=embed_dim, tokenizer_name=text_cfg.hf_tokenizer_name, proj=text_cfg.proj, pooler_type=text_cfg.pooler_type, masked_language_modeling=text_cfg.masked_language_modeling) + else: + act_layer = QuickGELU if quick_gelu else nn.GELU + norm_layer = LayerNorm + + text = TextTransformer( + context_length=text_cfg.context_length, + vocab_size=text_cfg.vocab_size, + width=text_cfg.width, + heads=text_cfg.heads, + layers=text_cfg.layers, + ls_init_value=text_cfg.ls_init_value, + output_dim=embed_dim, + act_layer=act_layer, + norm_layer=FusedLayerNorm if text_cfg.fusedLN else norm_layer, + xattn=text_cfg.xattn, + attn_mask=text_cfg.attn_mask, + ) + return text + + +class CLIP(nn.Module): + def __init__( + self, + embed_dim: int, + vision_cfg: CLIPVisionCfg, + text_cfg: CLIPTextCfg, + quick_gelu: bool = False, + cast_dtype: Optional[torch.dtype] = None, + ): + super().__init__() + self.visual = _build_vision_tower(embed_dim, vision_cfg, quick_gelu, cast_dtype) + + text = _build_text_tower(embed_dim, text_cfg, quick_gelu, cast_dtype) + self.transformer = text.transformer + self.vocab_size = text.vocab_size + self.token_embedding = text.token_embedding + self.positional_embedding = text.positional_embedding + self.ln_final = text.ln_final + self.text_projection = text.text_projection + self.register_buffer("attn_mask", text.attn_mask, persistent=False) + + self.logit_scale = nn.Parameter(torch.ones([]) * np.log(1 / 0.07)) + + def lock_image_tower(self, unlocked_groups=0, freeze_bn_stats=False): + # lock image tower as per LiT - https://arxiv.org/abs/2111.07991 + self.visual.lock(unlocked_groups=unlocked_groups, freeze_bn_stats=freeze_bn_stats) + + @torch.jit.ignore + def set_grad_checkpointing(self, enable=True): + self.visual.set_grad_checkpointing(enable) + self.transformer.grad_checkpointing = enable + + @torch.jit.ignore + def no_weight_decay(self): + return {"logit_scale"} + + def encode_image(self, image, normalize: bool = False): + features = self.visual(image) + return F.normalize(features, dim=-1) if normalize else features + + def encode_text(self, text, normalize: bool = False): + cast_dtype = self.transformer.get_cast_dtype() + + x = self.token_embedding(text).to(cast_dtype) # [batch_size, n_ctx, d_model] + + x = x + self.positional_embedding.to(cast_dtype) + x = x.permute(1, 0, 2) # NLD -> LND + x = self.transformer(x, attn_mask=self.attn_mask) + x = x.permute(1, 0, 2) # LND -> NLD + x = self.ln_final(x) # [batch_size, n_ctx, transformer.width] + # take features from the eot embedding (eot_token is the highest number in each sequence) + x = x[torch.arange(x.shape[0]), text.argmax(dim=-1)] @ self.text_projection + return F.normalize(x, dim=-1) if normalize else x + + def forward(self, image, text): + image_features = self.encode_image(image, normalize=True) + text_features = self.encode_text(text, normalize=True) + return image_features, text_features, self.logit_scale.exp() + + +class CustomCLIP(nn.Module): + def __init__( + self, + embed_dim: int, + vision_cfg: CLIPVisionCfg, + text_cfg: CLIPTextCfg, + quick_gelu: bool = False, + cast_dtype: Optional[torch.dtype] = None, + itm_task: bool = False, + ): + super().__init__() + self.visual = _build_vision_tower(embed_dim, vision_cfg, quick_gelu, cast_dtype) + self.text = _build_text_tower(embed_dim, text_cfg, quick_gelu, cast_dtype) + self.logit_scale = nn.Parameter(torch.ones([]) * np.log(1 / 0.07)) + + def lock_image_tower(self, unlocked_groups=0, freeze_bn_stats=False): + # lock image tower as per LiT - https://arxiv.org/abs/2111.07991 + self.visual.lock(unlocked_groups=unlocked_groups, freeze_bn_stats=freeze_bn_stats) + + def lock_text_tower(self, unlocked_layers: int = 0, freeze_layer_norm: bool = True): + self.text.lock(unlocked_layers, freeze_layer_norm) + + @torch.jit.ignore + def set_grad_checkpointing(self, enable=True): + self.visual.set_grad_checkpointing(enable) + self.text.set_grad_checkpointing(enable) + + @torch.jit.ignore + def no_weight_decay(self): + return {"logit_scale"} + + def encode_image(self, image, normalize: bool = False): + features = self.visual(image) + return F.normalize(features, dim=-1) if normalize else features + + def encode_text(self, text, normalize: bool = False): + features = self.text(text) + return F.normalize(features, dim=-1) if normalize else features + + def forward(self, image, text): + image_features = self.encode_image(image, normalize=True) + text_features = self.encode_text(text, normalize=True) + return image_features, text_features, self.logit_scale.exp() + + +def convert_weights_to_lp(model: nn.Module, dtype=torch.float16): + """Convert applicable model parameters to low-precision (bf16 or fp16)""" + + def _convert_weights(l): + + if isinstance(l, (nn.Conv1d, nn.Conv2d, nn.Linear)): + l.weight.data = l.weight.data.to(dtype) + if l.bias is not None: + l.bias.data = l.bias.data.to(dtype) + + if isinstance(l, (nn.MultiheadAttention, Attention)): + for attr in [*[f"{s}_proj_weight" for s in ["in", "q", "k", "v"]], "in_proj_bias", "bias_k", "bias_v"]: + tensor = getattr(l, attr, None) + if tensor is not None: + tensor.data = tensor.data.to(dtype) + + if isinstance(l, nn.Parameter): + l.data = l.data.to(dtype) + + for name in ["text_projection", "proj"]: + if hasattr(l, name) and isinstance(l, nn.Parameter): + attr = getattr(l, name, None) + if attr is not None: + attr.data = attr.data.to(dtype) + + model.apply(_convert_weights) + + +convert_weights_to_fp16 = convert_weights_to_lp # backwards compat + + +# used to maintain checkpoint compatibility +def convert_to_custom_text_state_dict(state_dict: dict): + if "text_projection" in state_dict: + # old format state_dict, move text tower -> .text + new_state_dict = {} + for k, v in state_dict.items(): + if any(k.startswith(p) for p in ("text_projection", "positional_embedding", "token_embedding", "transformer", "ln_final", "logit_scale")): + k = "text." + k + new_state_dict[k] = v + return new_state_dict + return state_dict + + +def build_model_from_openai_state_dict( + state_dict: dict, + quick_gelu=True, + cast_dtype=torch.float16, +): + vit = "visual.proj" in state_dict + + if vit: + vision_width = state_dict["visual.conv1.weight"].shape[0] + vision_layers = len([k for k in state_dict.keys() if k.startswith("visual.") and k.endswith(".attn.in_proj_weight")]) + vision_patch_size = state_dict["visual.conv1.weight"].shape[-1] + grid_size = round((state_dict["visual.positional_embedding"].shape[0] - 1) ** 0.5) + image_size = vision_patch_size * grid_size + else: + counts: list = [len(set(k.split(".")[2] for k in state_dict if k.startswith(f"visual.layer{b}"))) for b in [1, 2, 3, 4]] + vision_layers = tuple(counts) + vision_width = state_dict["visual.layer1.0.conv1.weight"].shape[0] + output_width = round((state_dict["visual.attnpool.positional_embedding"].shape[0] - 1) ** 0.5) + vision_patch_size = None + assert output_width**2 + 1 == state_dict["visual.attnpool.positional_embedding"].shape[0] + image_size = output_width * 32 + + embed_dim = state_dict["text_projection"].shape[1] + context_length = state_dict["positional_embedding"].shape[0] + vocab_size = state_dict["token_embedding.weight"].shape[0] + transformer_width = state_dict["ln_final.weight"].shape[0] + transformer_heads = transformer_width // 64 + transformer_layers = len(set(k.split(".")[2] for k in state_dict if k.startswith(f"transformer.resblocks"))) + + vision_cfg = CLIPVisionCfg( + layers=vision_layers, + width=vision_width, + patch_size=vision_patch_size, + image_size=image_size, + ) + text_cfg = CLIPTextCfg(context_length=context_length, vocab_size=vocab_size, width=transformer_width, heads=transformer_heads, layers=transformer_layers) + model = CLIP( + embed_dim, + vision_cfg=vision_cfg, + text_cfg=text_cfg, + quick_gelu=quick_gelu, # OpenAI models were trained with QuickGELU + cast_dtype=cast_dtype, + ) + + for key in ["input_resolution", "context_length", "vocab_size"]: + state_dict.pop(key, None) + + convert_weights_to_fp16(model) # OpenAI state dicts are partially converted to float16 + model.load_state_dict(state_dict) + return model.eval() + + +def trace_model(model, batch_size=256, device=torch.device("cpu")): + model.eval() + image_size = model.visual.image_size + example_images = torch.ones((batch_size, 3, image_size, image_size), device=device) + example_text = torch.zeros((batch_size, model.context_length), dtype=torch.int, device=device) + model = torch.jit.trace_module(model, inputs=dict(forward=(example_images, example_text), encode_text=(example_text,), encode_image=(example_images,))) + model.visual.image_size = image_size + return model diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA-CLIP-18B.json b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA-CLIP-18B.json new file mode 100644 index 0000000..4917556 --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA-CLIP-18B.json @@ -0,0 +1,27 @@ +{ + "embed_dim": 1536, + "vision_cfg": { + "image_size": 224, + "layers": 48, + "width": 5120, + "head_width": 128, + "mlp_ratio": 5, + "patch_size": 14, + "eva_model_name": "eva-clip-18b-14-x", + "drop_path_rate": 0, + "qkv_bias": false, + "xattn": true, + "postnorm": true, + "fusedLN": false, + "use_rms_norm": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 1280, + "heads": 20, + "layers": 32, + "xattn": false, + "fusedLN": false + } +} \ No newline at end of file diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA-CLIP-8B-plus.json b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA-CLIP-8B-plus.json new file mode 100644 index 0000000..7d843da --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA-CLIP-8B-plus.json @@ -0,0 +1,27 @@ +{ + "embed_dim": 1280, + "vision_cfg": { + "image_size": 448, + "layers": 32, + "width": 4096, + "head_width": 128, + "mlp_ratio": 5, + "patch_size": 14, + "eva_model_name": "eva-clip-8b-14-plus-x", + "drop_path_rate": 0, + "qkv_bias": false, + "xattn": true, + "postnorm": false, + "fusedLN": false, + "use_rms_norm": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 1280, + "heads": 20, + "layers": 32, + "xattn": false, + "fusedLN": false + } +} \ No newline at end of file diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA-CLIP-8B.json b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA-CLIP-8B.json new file mode 100644 index 0000000..689492a --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA-CLIP-8B.json @@ -0,0 +1,27 @@ +{ + "embed_dim": 1280, + "vision_cfg": { + "image_size": 224, + "layers": 32, + "width": 4096, + "head_width": 128, + "mlp_ratio": 5, + "patch_size": 14, + "eva_model_name": "eva-clip-8b-14-x", + "drop_path_rate": 0, + "qkv_bias": false, + "xattn": true, + "postnorm": false, + "fusedLN": false, + "use_rms_norm": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 1280, + "heads": 20, + "layers": 32, + "xattn": false, + "fusedLN": false + } +} \ No newline at end of file diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA01-CLIP-B-16.json b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA01-CLIP-B-16.json new file mode 100644 index 0000000..aad2058 --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA01-CLIP-B-16.json @@ -0,0 +1,19 @@ +{ + "embed_dim": 512, + "vision_cfg": { + "image_size": 224, + "layers": 12, + "width": 768, + "patch_size": 16, + "eva_model_name": "eva-clip-b-16", + "ls_init_value": 0.1, + "drop_path_rate": 0.0 + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 512, + "heads": 8, + "layers": 12 + } +} \ No newline at end of file diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA01-CLIP-g-14-plus.json b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA01-CLIP-g-14-plus.json new file mode 100644 index 0000000..1002795 --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA01-CLIP-g-14-plus.json @@ -0,0 +1,24 @@ +{ + "embed_dim": 1024, + "vision_cfg": { + "image_size": 224, + "layers": 40, + "width": 1408, + "head_width": 88, + "mlp_ratio": 4.3637, + "patch_size": 14, + "eva_model_name": "eva-clip-g-14-x", + "drop_path_rate": 0, + "xattn": true, + "fusedLN": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 1024, + "heads": 16, + "layers": 24, + "xattn": false, + "fusedLN": true + } +} \ No newline at end of file diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA01-CLIP-g-14.json b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA01-CLIP-g-14.json new file mode 100644 index 0000000..5d338b4 --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA01-CLIP-g-14.json @@ -0,0 +1,24 @@ +{ + "embed_dim": 1024, + "vision_cfg": { + "image_size": 224, + "layers": 40, + "width": 1408, + "head_width": 88, + "mlp_ratio": 4.3637, + "patch_size": 14, + "eva_model_name": "eva-clip-g-14-x", + "drop_path_rate": 0.4, + "xattn": true, + "fusedLN": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 768, + "heads": 12, + "layers": 12, + "xattn": false, + "fusedLN": true + } +} \ No newline at end of file diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA02-CLIP-B-16.json b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA02-CLIP-B-16.json new file mode 100644 index 0000000..e4a6e72 --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA02-CLIP-B-16.json @@ -0,0 +1,29 @@ +{ + "embed_dim": 512, + "vision_cfg": { + "image_size": 224, + "layers": 12, + "width": 768, + "head_width": 64, + "patch_size": 16, + "mlp_ratio": 2.6667, + "eva_model_name": "eva-clip-b-16-X", + "drop_path_rate": 0.0, + "xattn": true, + "fusedLN": true, + "rope": true, + "pt_hw_seq_len": 16, + "intp_freq": true, + "naiveswiglu": true, + "subln": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 512, + "heads": 8, + "layers": 12, + "xattn": true, + "fusedLN": true + } +} \ No newline at end of file diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA02-CLIP-L-14-336.json b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA02-CLIP-L-14-336.json new file mode 100644 index 0000000..3e1d124 --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA02-CLIP-L-14-336.json @@ -0,0 +1,29 @@ +{ + "embed_dim": 768, + "vision_cfg": { + "image_size": 336, + "layers": 24, + "width": 1024, + "drop_path_rate": 0, + "head_width": 64, + "mlp_ratio": 2.6667, + "patch_size": 14, + "eva_model_name": "eva-clip-l-14-336", + "xattn": true, + "fusedLN": true, + "rope": true, + "pt_hw_seq_len": 16, + "intp_freq": true, + "naiveswiglu": true, + "subln": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 768, + "heads": 12, + "layers": 12, + "xattn": false, + "fusedLN": true + } +} \ No newline at end of file diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA02-CLIP-L-14.json b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA02-CLIP-L-14.json new file mode 100644 index 0000000..03b22ad --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA02-CLIP-L-14.json @@ -0,0 +1,29 @@ +{ + "embed_dim": 768, + "vision_cfg": { + "image_size": 224, + "layers": 24, + "width": 1024, + "drop_path_rate": 0, + "head_width": 64, + "mlp_ratio": 2.6667, + "patch_size": 14, + "eva_model_name": "eva-clip-l-14", + "xattn": true, + "fusedLN": true, + "rope": true, + "pt_hw_seq_len": 16, + "intp_freq": true, + "naiveswiglu": true, + "subln": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 768, + "heads": 12, + "layers": 12, + "xattn": false, + "fusedLN": true + } +} \ No newline at end of file diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA02-CLIP-bigE-14-plus.json b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA02-CLIP-bigE-14-plus.json new file mode 100644 index 0000000..aa04e25 --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA02-CLIP-bigE-14-plus.json @@ -0,0 +1,25 @@ +{ + "embed_dim": 1024, + "vision_cfg": { + "image_size": 224, + "layers": 64, + "width": 1792, + "head_width": 112, + "mlp_ratio": 8.571428571428571, + "patch_size": 14, + "eva_model_name": "eva-clip-4b-14-x", + "drop_path_rate": 0, + "xattn": true, + "postnorm": true, + "fusedLN": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 1280, + "heads": 20, + "layers": 32, + "xattn": false, + "fusedLN": true + } +} diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA02-CLIP-bigE-14.json b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA02-CLIP-bigE-14.json new file mode 100644 index 0000000..747ffcc --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/EVA02-CLIP-bigE-14.json @@ -0,0 +1,25 @@ +{ + "embed_dim": 1024, + "vision_cfg": { + "image_size": 224, + "layers": 64, + "width": 1792, + "head_width": 112, + "mlp_ratio": 8.571428571428571, + "patch_size": 14, + "eva_model_name": "eva-clip-4b-14-x", + "drop_path_rate": 0, + "xattn": true, + "postnorm": true, + "fusedLN": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 1024, + "heads": 16, + "layers": 24, + "xattn": false, + "fusedLN": true + } +} \ No newline at end of file diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/Internal-EVA02-CLIP-10B-14-448.json b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/Internal-EVA02-CLIP-10B-14-448.json new file mode 100644 index 0000000..ad71aff --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/Internal-EVA02-CLIP-10B-14-448.json @@ -0,0 +1,25 @@ +{ + "embed_dim": 1024, + "vision_cfg": { + "image_size": 448, + "layers": 77, + "width": 2304, + "head_width": 144, + "mlp_ratio": 10.9722, + "patch_size": 14, + "eva_model_name": "eva-clip-10b-14-x", + "drop_path_rate": 0, + "xattn": true, + "postnorm": false, + "fusedLN": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 1280, + "heads": 20, + "layers": 32, + "xattn": false, + "fusedLN": true + } +} diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/Internal-EVA02-CLIP-10B-14.json b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/Internal-EVA02-CLIP-10B-14.json new file mode 100644 index 0000000..21b2068 --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/model_configs/Internal-EVA02-CLIP-10B-14.json @@ -0,0 +1,25 @@ +{ + "embed_dim": 1024, + "vision_cfg": { + "image_size": 224, + "layers": 77, + "width": 2304, + "head_width": 144, + "mlp_ratio": 10.9722, + "patch_size": 14, + "eva_model_name": "eva-clip-10b-14-x", + "drop_path_rate": 0, + "xattn": true, + "postnorm": false, + "fusedLN": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 1280, + "heads": 20, + "layers": 32, + "xattn": false, + "fusedLN": true + } +} diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/modified_resnet.py b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/modified_resnet.py new file mode 100644 index 0000000..9f29f84 --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/modified_resnet.py @@ -0,0 +1,179 @@ +from collections import OrderedDict + +import torch +from torch import nn +from torch.nn import functional as F + +from .utils import freeze_batch_norm_2d + + +class Bottleneck(nn.Module): + expansion = 4 + + def __init__(self, inplanes, planes, stride=1): + super().__init__() + + # all conv layers have stride 1. an avgpool is performed after the second convolution when stride > 1 + self.conv1 = nn.Conv2d(inplanes, planes, 1, bias=False) + self.bn1 = nn.BatchNorm2d(planes) + self.act1 = nn.ReLU(inplace=True) + + self.conv2 = nn.Conv2d(planes, planes, 3, padding=1, bias=False) + self.bn2 = nn.BatchNorm2d(planes) + self.act2 = nn.ReLU(inplace=True) + + self.avgpool = nn.AvgPool2d(stride) if stride > 1 else nn.Identity() + + self.conv3 = nn.Conv2d(planes, planes * self.expansion, 1, bias=False) + self.bn3 = nn.BatchNorm2d(planes * self.expansion) + self.act3 = nn.ReLU(inplace=True) + + self.downsample = None + self.stride = stride + + if stride > 1 or inplanes != planes * Bottleneck.expansion: + # downsampling layer is prepended with an avgpool, and the subsequent convolution has stride 1 + self.downsample = nn.Sequential(OrderedDict([("-1", nn.AvgPool2d(stride)), ("0", nn.Conv2d(inplanes, planes * self.expansion, 1, stride=1, bias=False)), ("1", nn.BatchNorm2d(planes * self.expansion))])) + + def forward(self, x: torch.Tensor): + identity = x + + out = self.act1(self.bn1(self.conv1(x))) + out = self.act2(self.bn2(self.conv2(out))) + out = self.avgpool(out) + out = self.bn3(self.conv3(out)) + + if self.downsample is not None: + identity = self.downsample(x) + + out += identity + out = self.act3(out) + return out + + +class AttentionPool2d(nn.Module): + def __init__(self, spacial_dim: int, embed_dim: int, num_heads: int, output_dim: int = None): + super().__init__() + self.positional_embedding = nn.Parameter(torch.randn(spacial_dim**2 + 1, embed_dim) / embed_dim**0.5) + self.k_proj = nn.Linear(embed_dim, embed_dim) + self.q_proj = nn.Linear(embed_dim, embed_dim) + self.v_proj = nn.Linear(embed_dim, embed_dim) + self.c_proj = nn.Linear(embed_dim, output_dim or embed_dim) + self.num_heads = num_heads + + def forward(self, x): + x = x.reshape(x.shape[0], x.shape[1], x.shape[2] * x.shape[3]).permute(2, 0, 1) # NCHW -> (HW)NC + x = torch.cat([x.mean(dim=0, keepdim=True), x], dim=0) # (HW+1)NC + x = x + self.positional_embedding[:, None, :].to(x.dtype) # (HW+1)NC + x, _ = F.multi_head_attention_forward( + query=x, + key=x, + value=x, + embed_dim_to_check=x.shape[-1], + num_heads=self.num_heads, + q_proj_weight=self.q_proj.weight, + k_proj_weight=self.k_proj.weight, + v_proj_weight=self.v_proj.weight, + in_proj_weight=None, + in_proj_bias=torch.cat([self.q_proj.bias, self.k_proj.bias, self.v_proj.bias]), + bias_k=None, + bias_v=None, + add_zero_attn=False, + dropout_p=0.0, + out_proj_weight=self.c_proj.weight, + out_proj_bias=self.c_proj.bias, + use_separate_proj_weight=True, + training=self.training, + need_weights=False, + ) + + return x[0] + + +class ModifiedResNet(nn.Module): + """ + A ResNet class that is similar to torchvision's but contains the following changes: + - There are now 3 "stem" convolutions as opposed to 1, with an average pool instead of a max pool. + - Performs anti-aliasing strided convolutions, where an avgpool is prepended to convolutions with stride > 1 + - The final pooling layer is a QKV attention instead of an average pool + """ + + def __init__(self, layers, output_dim, heads, image_size=224, width=64): + super().__init__() + self.output_dim = output_dim + self.image_size = image_size + + # the 3-layer stem + self.conv1 = nn.Conv2d(3, width // 2, kernel_size=3, stride=2, padding=1, bias=False) + self.bn1 = nn.BatchNorm2d(width // 2) + self.act1 = nn.ReLU(inplace=True) + self.conv2 = nn.Conv2d(width // 2, width // 2, kernel_size=3, padding=1, bias=False) + self.bn2 = nn.BatchNorm2d(width // 2) + self.act2 = nn.ReLU(inplace=True) + self.conv3 = nn.Conv2d(width // 2, width, kernel_size=3, padding=1, bias=False) + self.bn3 = nn.BatchNorm2d(width) + self.act3 = nn.ReLU(inplace=True) + self.avgpool = nn.AvgPool2d(2) + + # residual layers + self._inplanes = width # this is a *mutable* variable used during construction + self.layer1 = self._make_layer(width, layers[0]) + self.layer2 = self._make_layer(width * 2, layers[1], stride=2) + self.layer3 = self._make_layer(width * 4, layers[2], stride=2) + self.layer4 = self._make_layer(width * 8, layers[3], stride=2) + + embed_dim = width * 32 # the ResNet feature dimension + self.attnpool = AttentionPool2d(image_size // 32, embed_dim, heads, output_dim) + + self.init_parameters() + + def _make_layer(self, planes, blocks, stride=1): + layers = [Bottleneck(self._inplanes, planes, stride)] + + self._inplanes = planes * Bottleneck.expansion + for _ in range(1, blocks): + layers.append(Bottleneck(self._inplanes, planes)) + + return nn.Sequential(*layers) + + def init_parameters(self): + if self.attnpool is not None: + std = self.attnpool.c_proj.in_features**-0.5 + nn.init.normal_(self.attnpool.q_proj.weight, std=std) + nn.init.normal_(self.attnpool.k_proj.weight, std=std) + nn.init.normal_(self.attnpool.v_proj.weight, std=std) + nn.init.normal_(self.attnpool.c_proj.weight, std=std) + + for resnet_block in [self.layer1, self.layer2, self.layer3, self.layer4]: + for name, param in resnet_block.named_parameters(): + if name.endswith("bn3.weight"): + nn.init.zeros_(param) + + def lock(self, unlocked_groups=0, freeze_bn_stats=False): + assert unlocked_groups == 0, "partial locking not currently supported for this model" + for param in self.parameters(): + param.requires_grad = False + if freeze_bn_stats: + freeze_batch_norm_2d(self) + + @torch.jit.ignore + def set_grad_checkpointing(self, enable=True): + # FIXME support for non-transformer + pass + + def stem(self, x): + x = self.act1(self.bn1(self.conv1(x))) + x = self.act2(self.bn2(self.conv2(x))) + x = self.act3(self.bn3(self.conv3(x))) + x = self.avgpool(x) + return x + + def forward(self, x): + x = self.stem(x) + x = self.layer1(x) + x = self.layer2(x) + x = self.layer3(x) + x = self.layer4(x) + x = self.attnpool(x) + + return x diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/openai.py b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/openai.py new file mode 100644 index 0000000..9fbf6fc --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/openai.py @@ -0,0 +1,144 @@ +""" OpenAI pretrained model functions + +Adapted from https://github.com/openai/CLIP. Originally MIT License, Copyright (c) 2021 OpenAI. +""" + +import os +import warnings +from typing import List, Optional, Union + +import torch + +from .model import build_model_from_openai_state_dict, convert_weights_to_lp, get_cast_dtype +from .pretrained import get_pretrained_url, list_pretrained_models_by_tag, download_pretrained_from_url + +__all__ = ["list_openai_models", "load_openai_model"] + + +def list_openai_models() -> List[str]: + """Returns the names of available CLIP models""" + return list_pretrained_models_by_tag("openai") + + +def load_openai_model( + name: str, + precision: Optional[str] = None, + device: Optional[Union[str, torch.device]] = None, + jit: bool = True, + cache_dir: Optional[str] = None, +): + """Load a CLIP model + + Parameters + ---------- + name : str + A model name listed by `clip.available_models()`, or the path to a model checkpoint containing the state_dict + precision: str + Model precision, if None defaults to 'fp32' if device == 'cpu' else 'fp16'. + device : Union[str, torch.device] + The device to put the loaded model + jit : bool + Whether to load the optimized JIT model (default) or more hackable non-JIT model. + cache_dir : Optional[str] + The directory to cache the downloaded model weights + + Returns + ------- + model : torch.nn.Module + The CLIP model + preprocess : Callable[[PIL.Image], torch.Tensor] + A torchvision transform that converts a PIL image into a tensor that the returned model can take as its input + """ + if device is None: + device = "cuda" if torch.cuda.is_available() else "cpu" + if precision is None: + precision = "fp32" if device == "cpu" else "fp16" + + if get_pretrained_url(name, "openai"): + model_path = download_pretrained_from_url(get_pretrained_url(name, "openai"), cache_dir=cache_dir) + elif os.path.isfile(name): + model_path = name + else: + raise RuntimeError(f"Model {name} not found; available models = {list_openai_models()}") + + try: + # loading JIT archive + model = torch.jit.load(model_path, map_location=device if jit else "cpu").eval() + state_dict = None + except RuntimeError: + # loading saved state dict + if jit: + warnings.warn(f"File {model_path} is not a JIT archive. Loading as a state dict instead") + jit = False + state_dict = torch.load(model_path, map_location="cpu") + + if not jit: + # Build a non-jit model from the OpenAI jitted model state dict + cast_dtype = get_cast_dtype(precision) + try: + model = build_model_from_openai_state_dict(state_dict or model.state_dict(), cast_dtype=cast_dtype) + except KeyError: + sd = {k[7:]: v for k, v in state_dict["state_dict"].items()} + model = build_model_from_openai_state_dict(sd, cast_dtype=cast_dtype) + + # model from OpenAI state dict is in manually cast fp16 mode, must be converted for AMP/fp32/bf16 use + model = model.to(device) + if precision.startswith("amp") or precision == "fp32": + model.float() + elif precision == "bf16": + convert_weights_to_lp(model, dtype=torch.bfloat16) + + return model + + # patch the device names + device_holder = torch.jit.trace(lambda: torch.ones([]).to(torch.device(device)), example_inputs=[]) + device_node = [n for n in device_holder.graph.findAllNodes("prim::Constant") if "Device" in repr(n)][-1] + + def patch_device(module): + try: + graphs = [module.graph] if hasattr(module, "graph") else [] + except RuntimeError: + graphs = [] + + if hasattr(module, "forward1"): + graphs.append(module.forward1.graph) + + for graph in graphs: + for node in graph.findAllNodes("prim::Constant"): + if "value" in node.attributeNames() and str(node["value"]).startswith("cuda"): + node.copyAttributes(device_node) + + model.apply(patch_device) + patch_device(model.encode_image) + patch_device(model.encode_text) + + # patch dtype to float32 (typically for CPU) + if precision == "fp32": + float_holder = torch.jit.trace(lambda: torch.ones([]).float(), example_inputs=[]) + float_input = list(float_holder.graph.findNode("aten::to").inputs())[1] + float_node = float_input.node() + + def patch_float(module): + try: + graphs = [module.graph] if hasattr(module, "graph") else [] + except RuntimeError: + graphs = [] + + if hasattr(module, "forward1"): + graphs.append(module.forward1.graph) + + for graph in graphs: + for node in graph.findAllNodes("aten::to"): + inputs = list(node.inputs()) + for i in [1, 2]: # dtype can be the second or third argument to aten::to() + if inputs[i].node()["value"] == 5: + inputs[i].node().copyAttributes(float_node) + + model.apply(patch_float) + patch_float(model.encode_image) + patch_float(model.encode_text) + model.float() + + # ensure image_size attr available at consistent location for both jit and non-jit + model.visual.image_size = model.input_resolution.item() + return model diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/pretrained.py b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/pretrained.py new file mode 100644 index 0000000..a603b60 --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/pretrained.py @@ -0,0 +1,314 @@ +import hashlib +import os +import urllib +import warnings +from typing import Dict, Union + +from tqdm import tqdm + +try: + from huggingface_hub import hf_hub_download + + _has_hf_hub = True +except ImportError: + hf_hub_download = None + _has_hf_hub = False + + +def _pcfg(url="", hf_hub="", filename="", mean=None, std=None): + return dict( + url=url, + hf_hub=hf_hub, + mean=mean, + std=std, + ) + + +_VITB32 = dict( + openai=_pcfg("https://openaipublic.azureedge.net/clip/models/40d365715913c9da98579312b702a82c18be219cc2a73407c4526f58eba950af/ViT-B-32.pt"), + laion400m_e31=_pcfg("https://github.com/mlfoundations/open_clip/releases/download/v0.2-weights/vit_b_32-quickgelu-laion400m_e31-d867053b.pt"), + laion400m_e32=_pcfg("https://github.com/mlfoundations/open_clip/releases/download/v0.2-weights/vit_b_32-quickgelu-laion400m_e32-46683a32.pt"), + laion2b_e16=_pcfg("https://github.com/mlfoundations/open_clip/releases/download/v0.2-weights/vit_b_32-laion2b_e16-af8dbd0c.pth"), + laion2b_s34b_b79k=_pcfg(hf_hub="laion/CLIP-ViT-B-32-laion2B-s34B-b79K/"), +) + +_VITB32_quickgelu = dict( + openai=_pcfg("https://openaipublic.azureedge.net/clip/models/40d365715913c9da98579312b702a82c18be219cc2a73407c4526f58eba950af/ViT-B-32.pt"), + laion400m_e31=_pcfg("https://github.com/mlfoundations/open_clip/releases/download/v0.2-weights/vit_b_32-quickgelu-laion400m_e31-d867053b.pt"), + laion400m_e32=_pcfg("https://github.com/mlfoundations/open_clip/releases/download/v0.2-weights/vit_b_32-quickgelu-laion400m_e32-46683a32.pt"), +) + +_VITB16 = dict( + openai=_pcfg("https://openaipublic.azureedge.net/clip/models/5806e77cd80f8b59890b7e101eabd078d9fb84e6937f9e85e4ecb61988df416f/ViT-B-16.pt"), + laion400m_e31=_pcfg("https://github.com/mlfoundations/open_clip/releases/download/v0.2-weights/vit_b_16-laion400m_e31-00efa78f.pt"), + laion400m_e32=_pcfg("https://github.com/mlfoundations/open_clip/releases/download/v0.2-weights/vit_b_16-laion400m_e32-55e67d44.pt"), + laion2b_s34b_b88k=_pcfg(hf_hub="laion/CLIP-ViT-B-16-laion2B-s34B-b88K/"), +) + +_EVAB16 = dict( + eva=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA02_B_psz14to16.pt"), + eva02=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA02_B_psz14to16.pt"), + eva_clip=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA02_CLIP_B_psz16_s8B.pt"), + eva02_clip=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA02_CLIP_B_psz16_s8B.pt"), +) + +_VITB16_PLUS_240 = dict( + laion400m_e31=_pcfg("https://github.com/mlfoundations/open_clip/releases/download/v0.2-weights/vit_b_16_plus_240-laion400m_e31-8fb26589.pt"), + laion400m_e32=_pcfg("https://github.com/mlfoundations/open_clip/releases/download/v0.2-weights/vit_b_16_plus_240-laion400m_e32-699c4b84.pt"), +) + +_VITL14 = dict( + openai=_pcfg("https://openaipublic.azureedge.net/clip/models/b8cca3fd41ae0c99ba7e8951adf17d267cdb84cd88be6f7c2e0eca1737a03836/ViT-L-14.pt"), + laion400m_e31=_pcfg("https://github.com/mlfoundations/open_clip/releases/download/v0.2-weights/vit_l_14-laion400m_e31-69988bb6.pt"), + laion400m_e32=_pcfg("https://github.com/mlfoundations/open_clip/releases/download/v0.2-weights/vit_l_14-laion400m_e32-3d133497.pt"), + laion2b_s32b_b82k=_pcfg(hf_hub="laion/CLIP-ViT-L-14-laion2B-s32B-b82K/", mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5)), +) + +_EVAL14 = dict( + eva=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA02_L_psz14.pt"), + eva02=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA02_L_psz14.pt"), + eva_clip=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA02_CLIP_L_psz14_s4B.pt"), + eva02_clip=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA02_CLIP_L_psz14_s4B.pt"), +) + +_VITL14_336 = dict( + openai=_pcfg("https://openaipublic.azureedge.net/clip/models/3035c92b350959924f9f00213499208652fc7ea050643e8b385c2dac08641f02/ViT-L-14-336px.pt"), +) + +_EVAL14_336 = dict( + eva_clip=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA02_CLIP_L_336_psz14_s6B.pt"), + eva02_clip=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA02_CLIP_L_336_psz14_s6B.pt"), + eva_clip_224to336=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA02_CLIP_L_psz14_224to336.pt"), + eva02_clip_224to336=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA02_CLIP_L_psz14_224to336.pt"), +) + +_VITH14 = dict( + laion2b_s32b_b79k=_pcfg(hf_hub="laion/CLIP-ViT-H-14-laion2B-s32B-b79K/"), +) + +_VITg14 = dict( + laion2b_s12b_b42k=_pcfg(hf_hub="laion/CLIP-ViT-g-14-laion2B-s12B-b42K/"), + laion2b_s34b_b88k=_pcfg(hf_hub="laion/CLIP-ViT-g-14-laion2B-s34B-b88K/"), +) + +_EVAg14 = dict( + eva=_pcfg(hf_hub="QuanSun/EVA-CLIP/"), + eva01=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA01_g_psz14.pt"), + eva_clip=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA01_CLIP_g_14_psz14_s11B.pt"), + eva01_clip=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA01_CLIP_g_14_psz14_s11B.pt"), +) + +_EVAg14_PLUS = dict( + eva=_pcfg(hf_hub="QuanSun/EVA-CLIP/"), + eva01=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA01_g_psz14.pt"), + eva_clip=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA01_CLIP_g_14_plus_psz14_s11B.pt"), + eva01_clip=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA01_CLIP_g_14_plus_psz14_s11B.pt"), +) + +_VITbigG14 = dict( + laion2b_s39b_b160k=_pcfg(hf_hub="laion/CLIP-ViT-bigG-14-laion2B-39B-b160k/"), +) + +_EVAbigE14 = dict( + eva=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA02_E_psz14.pt"), + eva02=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA02_E_psz14.pt"), + eva_clip=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA02_CLIP_E_psz14_s4B.pt"), + eva02_clip=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA02_CLIP_E_psz14_s4B.pt"), +) + +_EVAbigE14_PLUS = dict( + eva=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA02_E_psz14.pt"), + eva02=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA02_E_psz14.pt"), + eva_clip=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA02_CLIP_E_psz14_plus_s9B.pt"), + eva02_clip=_pcfg(hf_hub="QuanSun/EVA-CLIP/EVA02_CLIP_E_psz14_plus_s9B.pt"), +) + +_EVA_8B = dict( + eva=_pcfg(hf_hub="BAAI/EVA-CLIP-8B/EVA_8B_psz14.bin"), + eva_clip=_pcfg(hf_hub="BAAI/EVA-CLIP-8B/EVA_CLIP_8B_psz14_s9B.pt"), +) + +_EVA_8B_PLUS = dict( + eva_clip=_pcfg(hf_hub="BAAI/EVA-CLIP-8B-448/EVA_CLIP_8B_psz14_plus_s0.6B.pt"), +) + + +_PRETRAINED = { + # "ViT-B-32": _VITB32, + "OpenaiCLIP-B-32": _VITB32, + "OpenCLIP-B-32": _VITB32, + # "ViT-B-32-quickgelu": _VITB32_quickgelu, + "OpenaiCLIP-B-32-quickgelu": _VITB32_quickgelu, + "OpenCLIP-B-32-quickgelu": _VITB32_quickgelu, + # "ViT-B-16": _VITB16, + "OpenaiCLIP-B-16": _VITB16, + "OpenCLIP-B-16": _VITB16, + "EVA02-B-16": _EVAB16, + "EVA02-CLIP-B-16": _EVAB16, + # "ViT-B-16-plus-240": _VITB16_PLUS_240, + "OpenCLIP-B-16-plus-240": _VITB16_PLUS_240, + # "ViT-L-14": _VITL14, + "OpenaiCLIP-L-14": _VITL14, + "OpenCLIP-L-14": _VITL14, + "EVA02-L-14": _EVAL14, + "EVA02-CLIP-L-14": _EVAL14, + # "ViT-L-14-336": _VITL14_336, + "OpenaiCLIP-L-14-336": _VITL14_336, + "EVA02-CLIP-L-14-336": _EVAL14_336, + # "ViT-H-14": _VITH14, + # "ViT-g-14": _VITg14, + "OpenCLIP-H-14": _VITH14, + "OpenCLIP-g-14": _VITg14, + "EVA01-CLIP-g-14": _EVAg14, + "EVA01-CLIP-g-14-plus": _EVAg14_PLUS, + # "ViT-bigG-14": _VITbigG14, + "OpenCLIP-bigG-14": _VITbigG14, + "EVA02-CLIP-bigE-14": _EVAbigE14, + "EVA02-CLIP-bigE-14-plus": _EVAbigE14_PLUS, + "EVA-CLIP-8B": _EVA_8B, + "EVA-CLIP-8B-448": _EVA_8B_PLUS, + "EVA-CLIP-8B-plus": _EVA_8B_PLUS, +} + + +def _clean_tag(tag: str): + # normalize pretrained tags + return tag.lower().replace("-", "_") + + +def list_pretrained(as_str: bool = False): + """returns list of pretrained models + Returns a tuple (model_name, pretrain_tag) by default or 'name:tag' if as_str == True + """ + return [":".join([k, t]) if as_str else (k, t) for k in _PRETRAINED.keys() for t in _PRETRAINED[k].keys()] + + +def list_pretrained_models_by_tag(tag: str): + """return all models having the specified pretrain tag""" + models = [] + tag = _clean_tag(tag) + for k in _PRETRAINED.keys(): + if tag in _PRETRAINED[k]: + models.append(k) + return models + + +def list_pretrained_tags_by_model(model: str): + """return all pretrain tags for the specified model architecture""" + tags = [] + if model in _PRETRAINED: + tags.extend(_PRETRAINED[model].keys()) + return tags + + +def is_pretrained_cfg(model: str, tag: str): + if model not in _PRETRAINED: + return False + return _clean_tag(tag) in _PRETRAINED[model] + + +def get_pretrained_cfg(model: str, tag: str): + if model not in _PRETRAINED: + return {} + model_pretrained = _PRETRAINED[model] + return model_pretrained.get(_clean_tag(tag), {}) + + +def get_pretrained_url(model: str, tag: str): + cfg = get_pretrained_cfg(model, _clean_tag(tag)) + return cfg.get("url", "") + + +def download_pretrained_from_url( + url: str, + cache_dir: Union[str, None] = None, +): + if not cache_dir: + cache_dir = os.path.expanduser("~/.cache/clip") + os.makedirs(cache_dir, exist_ok=True) + filename = os.path.basename(url) + + if "openaipublic" in url: + expected_sha256 = url.split("/")[-2] + elif "mlfoundations" in url: + expected_sha256 = os.path.splitext(filename)[0].split("-")[-1] + else: + expected_sha256 = "" + + download_target = os.path.join(cache_dir, filename) + + if os.path.exists(download_target) and not os.path.isfile(download_target): + raise RuntimeError(f"{download_target} exists and is not a regular file") + + if os.path.isfile(download_target): + if expected_sha256: + if hashlib.sha256(open(download_target, "rb").read()).hexdigest().startswith(expected_sha256): + return download_target + else: + warnings.warn(f"{download_target} exists, but the SHA256 checksum does not match; re-downloading the file") + else: + return download_target + + with urllib.request.urlopen(url) as source, open(download_target, "wb") as output: + with tqdm(total=int(source.headers.get("Content-Length")), ncols=80, unit="iB", unit_scale=True) as loop: + while True: + buffer = source.read(8192) + if not buffer: + break + + output.write(buffer) + loop.update(len(buffer)) + + if expected_sha256 and not hashlib.sha256(open(download_target, "rb").read()).hexdigest().startswith(expected_sha256): + raise RuntimeError(f"Model has been downloaded but the SHA256 checksum does not not match") + + return download_target + + +def has_hf_hub(necessary=False): + if not _has_hf_hub and necessary: + # if no HF Hub module installed, and it is necessary to continue, raise error + raise RuntimeError("Hugging Face hub model specified but package not installed. Run `pip install huggingface_hub`.") + return _has_hf_hub + + +def download_pretrained_from_hf( + model_id: str, + filename: str = "open_clip_pytorch_model.bin", + revision=None, + cache_dir: Union[str, None] = None, +): + has_hf_hub(True) + cached_file = hf_hub_download(model_id, filename, revision=revision, cache_dir=cache_dir) + return cached_file + + +def download_pretrained( + cfg: Dict, + force_hf_hub: bool = False, + cache_dir: Union[str, None] = None, +): + target = "" + if not cfg: + return target + + download_url = cfg.get("url", "") + download_hf_hub = cfg.get("hf_hub", "") + if download_hf_hub and force_hf_hub: + # use HF hub even if url exists + download_url = "" + + if download_url: + target = download_pretrained_from_url(download_url, cache_dir=cache_dir) + elif download_hf_hub: + has_hf_hub(True) + # we assume the hf_hub entries in pretrained config combine model_id + filename in + # 'org/model_name/filename.pt' form. To specify just the model id w/o filename and + # use 'open_clip_pytorch_model.bin' default, there must be a trailing slash 'org/model_name/'. + model_id, filename = os.path.split(download_hf_hub) + if filename: + target = download_pretrained_from_hf(model_id, filename=filename, cache_dir=cache_dir) + else: + target = download_pretrained_from_hf(model_id, cache_dir=cache_dir) + + return target diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/rope.py b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/rope.py new file mode 100644 index 0000000..5fb3cce --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/rope.py @@ -0,0 +1,131 @@ +from math import pi +import torch +from torch import nn +from einops import rearrange, repeat +import logging + + +def broadcat(tensors, dim=-1): + num_tensors = len(tensors) + shape_lens = set(list(map(lambda t: len(t.shape), tensors))) + assert len(shape_lens) == 1, "tensors must all have the same number of dimensions" + shape_len = list(shape_lens)[0] + dim = (dim + shape_len) if dim < 0 else dim + dims = list(zip(*map(lambda t: list(t.shape), tensors))) + expandable_dims = [(i, val) for i, val in enumerate(dims) if i != dim] + assert all([*map(lambda t: len(set(t[1])) <= 2, expandable_dims)]), "invalid dimensions for broadcastable concatentation" + max_dims = list(map(lambda t: (t[0], max(t[1])), expandable_dims)) + expanded_dims = list(map(lambda t: (t[0], (t[1],) * num_tensors), max_dims)) + expanded_dims.insert(dim, (dim, dims[dim])) + expandable_shapes = list(zip(*map(lambda t: t[1], expanded_dims))) + tensors = list(map(lambda t: t[0].expand(*t[1]), zip(tensors, expandable_shapes))) + return torch.cat(tensors, dim=dim) + + +def rotate_half(x): + x = rearrange(x, "... (d r) -> ... d r", r=2) + x1, x2 = x.unbind(dim=-1) + x = torch.stack((-x2, x1), dim=-1) + return rearrange(x, "... d r -> ... (d r)") + + +class VisionRotaryEmbedding(nn.Module): + def __init__( + self, + dim, + pt_seq_len, + ft_seq_len=None, + custom_freqs=None, + freqs_for="lang", + theta=10000, + max_freq=10, + num_freqs=1, + ): + super().__init__() + if custom_freqs: + freqs = custom_freqs + elif freqs_for == "lang": + freqs = 1.0 / (theta ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim)) + elif freqs_for == "pixel": + freqs = torch.linspace(1.0, max_freq / 2, dim // 2) * pi + elif freqs_for == "constant": + freqs = torch.ones(num_freqs).float() + else: + raise ValueError(f"unknown modality {freqs_for}") + + if ft_seq_len is None: + ft_seq_len = pt_seq_len + t = torch.arange(ft_seq_len) / ft_seq_len * pt_seq_len + + freqs_h = torch.einsum("..., f -> ... f", t, freqs) + freqs_h = repeat(freqs_h, "... n -> ... (n r)", r=2) + + freqs_w = torch.einsum("..., f -> ... f", t, freqs) + freqs_w = repeat(freqs_w, "... n -> ... (n r)", r=2) + + freqs = broadcat((freqs_h[:, None, :], freqs_w[None, :, :]), dim=-1) + + self.register_buffer("freqs_cos", freqs.cos()) + self.register_buffer("freqs_sin", freqs.sin()) + + logging.info(f"Shape of rope freq: {self.freqs_cos.shape}") + + def forward(self, t, start_index=0): + rot_dim = self.freqs_cos.shape[-1] + end_index = start_index + rot_dim + assert rot_dim <= t.shape[-1], f"feature dimension {t.shape[-1]} is not of sufficient size to rotate in all the positions {rot_dim}" + t_left, t, t_right = t[..., :start_index], t[..., start_index:end_index], t[..., end_index:] + t = (t * self.freqs_cos) + (rotate_half(t) * self.freqs_sin) + + return torch.cat((t_left, t, t_right), dim=-1) + + +class VisionRotaryEmbeddingFast(nn.Module): + def __init__(self, dim, pt_seq_len, ft_seq_len=None, custom_freqs=None, freqs_for="lang", theta=10000, max_freq=10, num_freqs=1, patch_dropout=0.0): + super().__init__() + if custom_freqs: + freqs = custom_freqs + elif freqs_for == "lang": + freqs = 1.0 / (theta ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim)) + elif freqs_for == "pixel": + freqs = torch.linspace(1.0, max_freq / 2, dim // 2) * pi + elif freqs_for == "constant": + freqs = torch.ones(num_freqs).float() + else: + raise ValueError(f"unknown modality {freqs_for}") + + if ft_seq_len is None: + ft_seq_len = pt_seq_len + t = torch.arange(ft_seq_len) / ft_seq_len * pt_seq_len + + freqs = torch.einsum("..., f -> ... f", t, freqs) + freqs = repeat(freqs, "... n -> ... (n r)", r=2) + freqs = broadcat((freqs[:, None, :], freqs[None, :, :]), dim=-1) + + freqs_cos = freqs.cos().view(-1, freqs.shape[-1]) + freqs_sin = freqs.sin().view(-1, freqs.shape[-1]) + + self.patch_dropout = patch_dropout + + self.register_buffer("freqs_cos", freqs_cos) + self.register_buffer("freqs_sin", freqs_sin) + + logging.info(f"Shape of rope freq: {self.freqs_cos.shape}") + + def forward(self, t, patch_indices_keep=None): + if patch_indices_keep is not None: + batch = t.size()[0] + batch_indices = torch.arange(batch) + batch_indices = batch_indices[..., None] + + freqs_cos = repeat(self.freqs_cos, "i j -> n i m j", n=t.shape[0], m=t.shape[1]) + freqs_sin = repeat(self.freqs_sin, "i j -> n i m j", n=t.shape[0], m=t.shape[1]) + + freqs_cos = freqs_cos[batch_indices, patch_indices_keep] + freqs_cos = rearrange(freqs_cos, "n i m j -> n m i j") + freqs_sin = freqs_sin[batch_indices, patch_indices_keep] + freqs_sin = rearrange(freqs_sin, "n i m j -> n m i j") + + return t * freqs_cos + rotate_half(t) * freqs_sin + + return t * self.freqs_cos + rotate_half(t) * self.freqs_sin diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/timm_model.py b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/timm_model.py new file mode 100644 index 0000000..65de78d --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/timm_model.py @@ -0,0 +1,114 @@ +""" timm model adapter + +Wraps timm (https://github.com/rwightman/pytorch-image-models) models for use as a vision tower in CLIP model. +""" + +import logging +from collections import OrderedDict + +import torch +import torch.nn as nn + +try: + import timm + from timm.models.layers import Mlp, to_2tuple + + try: + # old timm imports < 0.8.1 + from timm.models.layers.attention_pool2d import RotAttentionPool2d + from timm.models.layers.attention_pool2d import AttentionPool2d as AbsAttentionPool2d + except ImportError: + # new timm imports >= 0.8.1 + from timm.layers import RotAttentionPool2d + from timm.layers import AttentionPool2d as AbsAttentionPool2d +except ImportError: + timm = None + +from .utils import freeze_batch_norm_2d + + +class TimmModel(nn.Module): + """timm model adapter + # FIXME this adapter is a work in progress, may change in ways that break weight compat + """ + + def __init__(self, model_name, embed_dim, image_size=224, pool="avg", proj="linear", proj_bias=False, drop=0.0, pretrained=False): + super().__init__() + if timm is None: + raise RuntimeError("Please `pip install timm` to use timm models.") + + self.image_size = to_2tuple(image_size) + self.trunk = timm.create_model(model_name, pretrained=pretrained) + feat_size = self.trunk.default_cfg.get("pool_size", None) + feature_ndim = 1 if not feat_size else 2 + if pool in ("abs_attn", "rot_attn"): + assert feature_ndim == 2 + # if attn pooling used, remove both classifier and default pool + self.trunk.reset_classifier(0, global_pool="") + else: + # reset global pool if pool config set, otherwise leave as network default + reset_kwargs = dict(global_pool=pool) if pool else {} + self.trunk.reset_classifier(0, **reset_kwargs) + prev_chs = self.trunk.num_features + + head_layers = OrderedDict() + if pool == "abs_attn": + head_layers["pool"] = AbsAttentionPool2d(prev_chs, feat_size=feat_size, out_features=embed_dim) + prev_chs = embed_dim + elif pool == "rot_attn": + head_layers["pool"] = RotAttentionPool2d(prev_chs, out_features=embed_dim) + prev_chs = embed_dim + else: + assert proj, "projection layer needed if non-attention pooling is used." + + # NOTE attention pool ends with a projection layer, so proj should usually be set to '' if such pooling is used + if proj == "linear": + head_layers["drop"] = nn.Dropout(drop) + head_layers["proj"] = nn.Linear(prev_chs, embed_dim, bias=proj_bias) + elif proj == "mlp": + head_layers["mlp"] = Mlp(prev_chs, 2 * embed_dim, embed_dim, drop=drop, bias=(True, proj_bias)) + + self.head = nn.Sequential(head_layers) + + def lock(self, unlocked_groups=0, freeze_bn_stats=False): + """lock modules + Args: + unlocked_groups (int): leave last n layer groups unlocked (default: 0) + """ + if not unlocked_groups: + # lock full model + for param in self.trunk.parameters(): + param.requires_grad = False + if freeze_bn_stats: + freeze_batch_norm_2d(self.trunk) + else: + # NOTE: partial freeze requires latest timm (master) branch and is subject to change + try: + # FIXME import here until API stable and in an official release + from timm.models.helpers import group_parameters, group_modules + except ImportError: + raise RuntimeError("Please install latest timm `pip install git+https://github.com/rwightman/pytorch-image-models`") + matcher = self.trunk.group_matcher() + gparams = group_parameters(self.trunk, matcher) + max_layer_id = max(gparams.keys()) + max_layer_id = max_layer_id - unlocked_groups + for group_idx in range(max_layer_id + 1): + group = gparams[group_idx] + for param in group: + self.trunk.get_parameter(param).requires_grad = False + if freeze_bn_stats: + gmodules = group_modules(self.trunk, matcher, reverse=True) + gmodules = {k for k, v in gmodules.items() if v <= max_layer_id} + freeze_batch_norm_2d(self.trunk, gmodules) + + @torch.jit.ignore + def set_grad_checkpointing(self, enable=True): + try: + self.trunk.set_grad_checkpointing(enable) + except Exception as e: + logging.warning("grad checkpointing not supported for this timm image tower, continuing without...") + + def forward(self, x): + x = self.trunk(x) + x = self.head(x) + return x diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/tokenizer.py b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/tokenizer.py new file mode 100644 index 0000000..5f753e6 --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/tokenizer.py @@ -0,0 +1,205 @@ +""" CLIP tokenizer + +Copied from https://github.com/openai/CLIP. Originally MIT License, Copyright (c) 2021 OpenAI. +""" + +import gzip +import html +import os +from functools import lru_cache +from typing import Union, List + +import ftfy +import regex as re +import torch + +# https://stackoverflow.com/q/62691279 +import os + +os.environ["TOKENIZERS_PARALLELISM"] = "false" + + +@lru_cache() +def default_bpe(): + return os.path.join(os.path.dirname(os.path.abspath(__file__)), "bpe_simple_vocab_16e6.txt.gz") + + +@lru_cache() +def bytes_to_unicode(): + """ + Returns list of utf-8 byte and a corresponding list of unicode strings. + The reversible bpe codes work on unicode strings. + This means you need a large # of unicode characters in your vocab if you want to avoid UNKs. + When you're at something like a 10B token dataset you end up needing around 5K for decent coverage. + This is a signficant percentage of your normal, say, 32K bpe vocab. + To avoid that, we want lookup tables between utf-8 bytes and unicode strings. + And avoids mapping to whitespace/control characters the bpe code barfs on. + """ + bs = list(range(ord("!"), ord("~") + 1)) + list(range(ord("¡"), ord("¬") + 1)) + list(range(ord("®"), ord("ÿ") + 1)) + cs = bs[:] + n = 0 + for b in range(2**8): + if b not in bs: + bs.append(b) + cs.append(2**8 + n) + n += 1 + cs = [chr(n) for n in cs] + return dict(zip(bs, cs)) + + +def get_pairs(word): + """Return set of symbol pairs in a word. + Word is represented as tuple of symbols (symbols being variable-length strings). + """ + pairs = set() + prev_char = word[0] + for char in word[1:]: + pairs.add((prev_char, char)) + prev_char = char + return pairs + + +def basic_clean(text): + text = ftfy.fix_text(text) + text = html.unescape(html.unescape(text)) + return text.strip() + + +def whitespace_clean(text): + text = re.sub(r"\s+", " ", text) + text = text.strip() + return text + + +class SimpleTokenizer(object): + def __init__(self, bpe_path: str = default_bpe(), special_tokens=None): + self.byte_encoder = bytes_to_unicode() + self.byte_decoder = {v: k for k, v in self.byte_encoder.items()} + merges = gzip.open(bpe_path).read().decode("utf-8").split("\n") + merges = merges[1 : 49152 - 256 - 2 + 1] + merges = [tuple(merge.split()) for merge in merges] + vocab = list(bytes_to_unicode().values()) + vocab = vocab + [v + "" for v in vocab] + for merge in merges: + vocab.append("".join(merge)) + if not special_tokens: + special_tokens = ["", ""] + else: + special_tokens = ["", ""] + special_tokens + vocab.extend(special_tokens) + self.encoder = dict(zip(vocab, range(len(vocab)))) + self.decoder = {v: k for k, v in self.encoder.items()} + self.bpe_ranks = dict(zip(merges, range(len(merges)))) + self.cache = {t: t for t in special_tokens} + special = "|".join(special_tokens) + self.pat = re.compile(special + r"""|'s|'t|'re|'ve|'m|'ll|'d|[\p{L}]+|[\p{N}]|[^\s\p{L}\p{N}]+""", re.IGNORECASE) + + self.vocab_size = len(self.encoder) + self.all_special_ids = [self.encoder[t] for t in special_tokens] + + def bpe(self, token): + if token in self.cache: + return self.cache[token] + word = tuple(token[:-1]) + (token[-1] + "",) + pairs = get_pairs(word) + + if not pairs: + return token + "" + + while True: + bigram = min(pairs, key=lambda pair: self.bpe_ranks.get(pair, float("inf"))) + if bigram not in self.bpe_ranks: + break + first, second = bigram + new_word = [] + i = 0 + while i < len(word): + try: + j = word.index(first, i) + new_word.extend(word[i:j]) + i = j + except: + new_word.extend(word[i:]) + break + + if word[i] == first and i < len(word) - 1 and word[i + 1] == second: + new_word.append(first + second) + i += 2 + else: + new_word.append(word[i]) + i += 1 + new_word = tuple(new_word) + word = new_word + if len(word) == 1: + break + else: + pairs = get_pairs(word) + word = " ".join(word) + self.cache[token] = word + return word + + def encode(self, text): + bpe_tokens = [] + text = whitespace_clean(basic_clean(text)).lower() + for token in re.findall(self.pat, text): + token = "".join(self.byte_encoder[b] for b in token.encode("utf-8")) + bpe_tokens.extend(self.encoder[bpe_token] for bpe_token in self.bpe(token).split(" ")) + return bpe_tokens + + def decode(self, tokens): + text = "".join([self.decoder[token] for token in tokens]) + text = bytearray([self.byte_decoder[c] for c in text]).decode("utf-8", errors="replace").replace("", " ") + return text + + +_tokenizer = SimpleTokenizer() + + +def tokenize(texts: Union[str, List[str]], context_length: int = 77) -> torch.LongTensor: + """ + Returns the tokenized representation of given input string(s) + + Parameters + ---------- + texts : Union[str, List[str]] + An input string or a list of input strings to tokenize + context_length : int + The context length to use; all CLIP models use 77 as the context length + + Returns + ------- + A two-dimensional tensor containing the resulting tokens, shape = [number of input strings, context_length] + """ + if isinstance(texts, str): + texts = [texts] + + sot_token = _tokenizer.encoder[""] + eot_token = _tokenizer.encoder[""] + all_tokens = [[sot_token] + _tokenizer.encode(text) + [eot_token] for text in texts] + result = torch.zeros(len(all_tokens), context_length, dtype=torch.long) + + for i, tokens in enumerate(all_tokens): + if len(tokens) > context_length: + tokens = tokens[:context_length] # Truncate + tokens[-1] = eot_token + result[i, : len(tokens)] = torch.tensor(tokens) + + return result + + +class HFTokenizer: + "HuggingFace tokenizer wrapper" + + def __init__(self, tokenizer_name: str): + from transformers import AutoTokenizer + + self.tokenizer = AutoTokenizer.from_pretrained(tokenizer_name) + + def __call__(self, texts: Union[str, List[str]], context_length: int = 77) -> torch.Tensor: + # same cleaning as for default tokenizer, except lowercasing + # adding lower (for case-sensitive tokenizers) will make it more robust but less sensitive to nuance + if isinstance(texts, str): + texts = [texts] + texts = [whitespace_clean(basic_clean(text)) for text in texts] + input_ids = self.tokenizer(texts, return_tensors="pt", max_length=context_length, padding="max_length", truncation=True).input_ids + return input_ids diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/transform.py b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/transform.py new file mode 100644 index 0000000..8cad45a --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/transform.py @@ -0,0 +1,104 @@ +from typing import Optional, Sequence, Tuple + +import torch +import torch.nn as nn +import torchvision.transforms.functional as F + +from torchvision.transforms import Normalize, Compose, RandomResizedCrop, InterpolationMode, ToTensor, Resize, CenterCrop + +from .constants import OPENAI_DATASET_MEAN, OPENAI_DATASET_STD + + +class ResizeMaxSize(nn.Module): + + def __init__(self, max_size, interpolation=InterpolationMode.BICUBIC, fn="max", fill=0): + super().__init__() + if not isinstance(max_size, int): + raise TypeError(f"Size should be int. Got {type(max_size)}") + self.max_size = max_size + self.interpolation = interpolation + self.fn = min if fn == "min" else min + self.fill = fill + + def forward(self, img): + if isinstance(img, torch.Tensor): + height, width = img.shape[:2] + else: + width, height = img.size + scale = self.max_size / float(max(height, width)) + if scale != 1.0: + new_size = tuple(round(dim * scale) for dim in (height, width)) + img = F.resize(img, new_size, self.interpolation) + pad_h = self.max_size - new_size[0] + pad_w = self.max_size - new_size[1] + img = F.pad(img, padding=[pad_w // 2, pad_h // 2, pad_w - pad_w // 2, pad_h - pad_h // 2], fill=self.fill) + return img + + +def _convert_to_rgb(image): + return image.convert("RGB") + + +# class CatGen(nn.Module): +# def __init__(self, num=4): +# self.num = num +# def mixgen_batch(image, text): +# batch_size = image.shape[0] +# index = np.random.permutation(batch_size) + +# cat_images = [] +# for i in range(batch_size): +# # image mixup +# image[i,:] = lam * image[i,:] + (1 - lam) * image[index[i],:] +# # text concat +# text[i] = tokenizer((str(text[i]) + " " + str(text[index[i]])))[0] +# text = torch.stack(text) +# return image, text + + +def image_transform( + image_size: int, + is_train: bool, + mean: Optional[Tuple[float, ...]] = None, + std: Optional[Tuple[float, ...]] = None, + resize_longest_max: bool = False, + fill_color: int = 0, +): + mean = mean or OPENAI_DATASET_MEAN + if not isinstance(mean, (list, tuple)): + mean = (mean,) * 3 + + std = std or OPENAI_DATASET_STD + if not isinstance(std, (list, tuple)): + std = (std,) * 3 + + if isinstance(image_size, (list, tuple)) and image_size[0] == image_size[1]: + # for square size, pass size as int so that Resize() uses aspect preserving shortest edge + image_size = image_size[0] + + normalize = Normalize(mean=mean, std=std) + if is_train: + return Compose( + [ + RandomResizedCrop(image_size, scale=(0.9, 1.0), interpolation=InterpolationMode.BICUBIC), + _convert_to_rgb, + ToTensor(), + normalize, + ] + ) + else: + if resize_longest_max: + transforms = [ResizeMaxSize(image_size, fill=fill_color)] + else: + transforms = [ + Resize(image_size, interpolation=InterpolationMode.BICUBIC), + CenterCrop(image_size), + ] + transforms.extend( + [ + _convert_to_rgb, + ToTensor(), + normalize, + ] + ) + return Compose(transforms) diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/transformer.py b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/transformer.py new file mode 100644 index 0000000..bd5ce4b --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/transformer.py @@ -0,0 +1,683 @@ +import os +import logging +from collections import OrderedDict +import math +from typing import Callable, Optional, Sequence +import numpy as np +import torch +from torch import nn +from torch.nn import functional as F + +try: + from timm.models.layers import trunc_normal_ +except: + from timm.layers import trunc_normal_ + +from .rope import VisionRotaryEmbedding, VisionRotaryEmbeddingFast +from .utils import to_2tuple + +if os.getenv("ENV_TYPE") == "deepspeed": + try: + import deepspeed + from deepspeed.runtime.activation_checkpointing.checkpointing import checkpoint + except: + print("Please 'pip install deepspeed'") + deepspeed = None + from torch.utils.checkpoint import checkpoint +else: + from torch.utils.checkpoint import checkpoint + +try: + import xformers.ops as xops +except ImportError: + xops = None + # print("Please 'pip install xformers'") + + +class LayerNormFp32(nn.LayerNorm): + """Subclass torch's LayerNorm to handle fp16 (by casting to float32 and back).""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def forward(self, x: torch.Tensor): + output = F.layer_norm( + x.float(), + self.normalized_shape, + self.weight.float() if self.weight is not None else None, + self.bias.float() if self.bias is not None else None, + self.eps, + ) + return output.type_as(x) + + +class LayerNorm(nn.LayerNorm): + """Subclass torch's LayerNorm (with cast back to input dtype).""" + + def forward(self, x: torch.Tensor): + orig_type = x.dtype + x = F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps) + return x.to(orig_type) + + +class QuickGELU(nn.Module): + # NOTE This is slower than nn.GELU or nn.SiLU and uses more GPU memory + def forward(self, x: torch.Tensor): + return x * torch.sigmoid(1.702 * x) + + +class LayerScale(nn.Module): + def __init__(self, dim, init_values=1e-5, inplace=False): + super().__init__() + self.inplace = inplace + self.gamma = nn.Parameter(init_values * torch.ones(dim)) + + def forward(self, x): + return x.mul_(self.gamma) if self.inplace else x * self.gamma + + +class PatchDropout(nn.Module): + """ + https://arxiv.org/abs/2212.00794 + """ + + def __init__(self, prob, exclude_first_token=True): + super().__init__() + assert 0 <= prob < 1.0 + self.prob = prob + self.exclude_first_token = exclude_first_token # exclude CLS token + logging.info(f"os.getenv('RoPE')={os.getenv('RoPE')}") + + def forward(self, x): + if not self.training or self.prob == 0.0: + return x + + if self.exclude_first_token: + cls_tokens, x = x[:, :1], x[:, 1:] + else: + cls_tokens = torch.jit.annotate(torch.Tensor, x[:, :1]) + + batch = x.size()[0] + num_tokens = x.size()[1] + + batch_indices = torch.arange(batch) + batch_indices = batch_indices[..., None] + + keep_prob = 1 - self.prob + num_patches_keep = max(1, int(num_tokens * keep_prob)) + + rand = torch.randn(batch, num_tokens) + patch_indices_keep = rand.topk(num_patches_keep, dim=-1).indices + + x = x[batch_indices, patch_indices_keep] + + if self.exclude_first_token: + x = torch.cat((cls_tokens, x), dim=1) + + if self.training and os.getenv("RoPE") == "1": + return x, patch_indices_keep + + return x + + +def _in_projection_packed( + q: torch.Tensor, + k: torch.Tensor, + v: torch.Tensor, + w: torch.Tensor, + b: Optional[torch.Tensor] = None, +): + """ + https://github.com/pytorch/pytorch/blob/db2a237763eb8693a20788be94f8c192e762baa8/torch/nn/functional.py#L4726 + """ + E = q.size(-1) + if k is v: + if q is k: + # self-attention + return F.linear(q, w, b).chunk(3, dim=-1) + else: + # encoder-decoder attention + w_q, w_kv = w.split([E, E * 2]) + if b is None: + b_q = b_kv = None + else: + b_q, b_kv = b.split([E, E * 2]) + return (F.linear(q, w_q, b_q),) + F.linear(k, w_kv, b_kv).chunk(2, dim=-1) + else: + w_q, w_k, w_v = w.chunk(3) + if b is None: + b_q = b_k = b_v = None + else: + b_q, b_k, b_v = b.chunk(3) + return F.linear(q, w_q, b_q), F.linear(k, w_k, b_k), F.linear(v, w_v, b_v) + + +class Attention(nn.Module): + def __init__(self, dim, num_heads=8, qkv_bias=True, scaled_cosine=False, scale_heads=False, logit_scale_max=math.log(1.0 / 0.01), attn_drop=0.0, proj_drop=0.0, xattn=False, rope=False): + super().__init__() + self.scaled_cosine = scaled_cosine + self.scale_heads = scale_heads + assert dim % num_heads == 0, "dim should be divisible by num_heads" + self.num_heads = num_heads + self.head_dim = dim // num_heads + self.scale = self.head_dim**-0.5 + self.logit_scale_max = logit_scale_max + + # keeping in_proj in this form (instead of nn.Linear) to match weight scheme of original + self.in_proj_weight = nn.Parameter(torch.randn((dim * 3, dim)) * self.scale) + if qkv_bias: + self.in_proj_bias = nn.Parameter(torch.zeros(dim * 3)) + else: + self.in_proj_bias = None + + if self.scaled_cosine: + self.logit_scale = nn.Parameter(torch.log(10 * torch.ones((num_heads, 1, 1)))) + else: + self.logit_scale = None + self.attn_drop = nn.Dropout(attn_drop) + if self.scale_heads: + self.head_scale = nn.Parameter(torch.ones((num_heads, 1, 1))) + else: + self.head_scale = None + self.out_proj = nn.Linear(dim, dim) + self.out_drop = nn.Dropout(proj_drop) + self.xattn = xattn + self.xattn_drop = attn_drop + self.rope = rope + + def forward(self, x, attn_mask: Optional[torch.Tensor] = None): + L, N, C = x.shape + q, k, v = F.linear(x, self.in_proj_weight, self.in_proj_bias).chunk(3, dim=-1) + if self.xattn: + q = q.contiguous().view(L, N, self.num_heads, -1).transpose(0, 1) + k = k.contiguous().view(L, N, self.num_heads, -1).transpose(0, 1) + v = v.contiguous().view(L, N, self.num_heads, -1).transpose(0, 1) + + x = xops.memory_efficient_attention( + q, + k, + v, + p=self.xattn_drop, + scale=self.scale if self.logit_scale is None else None, + attn_bias=xops.LowerTriangularMask() if attn_mask is not None else None, + ) + else: + q = q.contiguous().view(L, N * self.num_heads, -1).transpose(0, 1) + k = k.contiguous().view(L, N * self.num_heads, -1).transpose(0, 1) + v = v.contiguous().view(L, N * self.num_heads, -1).transpose(0, 1) + + if self.logit_scale is not None: + attn = torch.bmm(F.normalize(q, dim=-1), F.normalize(k, dim=-1).transpose(-1, -2)) + logit_scale = torch.clamp(self.logit_scale, max=self.logit_scale_max).exp() + attn = attn.view(N, self.num_heads, L, L) * logit_scale + attn = attn.view(-1, L, L) + else: + q = q * self.scale + attn = torch.bmm(q, k.transpose(-1, -2)) + + if attn_mask is not None: + if attn_mask.dtype == torch.bool: + new_attn_mask = torch.zeros_like(attn_mask, dtype=q.dtype) + new_attn_mask.masked_fill_(attn_mask, float("-inf")) + attn_mask = new_attn_mask + attn += attn_mask + + attn = attn.softmax(dim=-1) + attn = self.attn_drop(attn) + + x = torch.bmm(attn, v) + + if self.head_scale is not None: + x = x.view(N, self.num_heads, L, C) * self.head_scale + x = x.view(-1, L, C) + x = x.transpose(0, 1).reshape(L, N, C) + x = self.out_proj(x) + x = self.out_drop(x) + return x + + +class CustomAttention(nn.Module): + def __init__(self, dim, num_heads=8, qkv_bias=True, scaled_cosine=True, scale_heads=False, logit_scale_max=math.log(1.0 / 0.01), attn_drop=0.0, proj_drop=0.0, xattn=False): + super().__init__() + self.scaled_cosine = scaled_cosine + self.scale_heads = scale_heads + assert dim % num_heads == 0, "dim should be divisible by num_heads" + self.num_heads = num_heads + self.head_dim = dim // num_heads + self.scale = self.head_dim**-0.5 + self.logit_scale_max = logit_scale_max + + # keeping in_proj in this form (instead of nn.Linear) to match weight scheme of original + self.in_proj_weight = nn.Parameter(torch.randn((dim * 3, dim)) * self.scale) + if qkv_bias: + self.in_proj_bias = nn.Parameter(torch.zeros(dim * 3)) + else: + self.in_proj_bias = None + + if self.scaled_cosine: + self.logit_scale = nn.Parameter(torch.log(10 * torch.ones((num_heads, 1, 1)))) + else: + self.logit_scale = None + self.attn_drop = nn.Dropout(attn_drop) + if self.scale_heads: + self.head_scale = nn.Parameter(torch.ones((num_heads, 1, 1))) + else: + self.head_scale = None + self.out_proj = nn.Linear(dim, dim) + self.out_drop = nn.Dropout(proj_drop) + self.xattn = xattn + self.xattn_drop = attn_drop + + def forward(self, query: torch.Tensor, key: torch.Tensor, value: torch.Tensor, attn_mask: Optional[torch.Tensor] = None): + q, k, v = _in_projection_packed(query, key, value, self.in_proj_weight, self.in_proj_bias) + N_q, B_q, C_q = q.shape + N_k, B_k, C_k = k.shape + N_v, B_v, C_v = v.shape + if self.xattn: + # B, N, C -> B, N, num_heads, C + q = q.permute(1, 0, 2).reshape(B_q, N_q, self.num_heads, -1) + k = k.permute(1, 0, 2).reshape(B_k, N_k, self.num_heads, -1) + v = v.permute(1, 0, 2).reshape(B_v, N_v, self.num_heads, -1) + + x = xops.memory_efficient_attention(q, k, v, p=self.xattn_drop, scale=self.scale if self.logit_scale is None else None, attn_bias=xops.LowerTriangularMask() if attn_mask is not None else None) + else: + # B*H, L, C + q = q.contiguous().view(N_q, B_q * self.num_heads, -1).transpose(0, 1) + k = k.contiguous().view(N_k, B_k * self.num_heads, -1).transpose(0, 1) + v = v.contiguous().view(N_v, B_v * self.num_heads, -1).transpose(0, 1) + + if self.logit_scale is not None: + # B*H, N_q, N_k + attn = torch.bmm(F.normalize(q, dim=-1), F.normalize(k, dim=-1).transpose(-1, -2)) + logit_scale = torch.clamp(self.logit_scale, max=self.logit_scale_max).exp() + attn = attn.view(B_q, self.num_heads, N_q, N_k) * logit_scale + attn = attn.view(-1, N_q, N_k) + else: + q = q * self.scale + attn = torch.bmm(q, k.transpose(-1, -2)) + + if attn_mask is not None: + if attn_mask.dtype == torch.bool: + new_attn_mask = torch.zeros_like(attn_mask, dtype=q.dtype) + new_attn_mask.masked_fill_(attn_mask, float("-inf")) + attn_mask = new_attn_mask + attn += attn_mask + + attn = attn.softmax(dim=-1) + attn = self.attn_drop(attn) + + x = torch.bmm(attn, v) + + if self.head_scale is not None: + x = x.view(B_q, self.num_heads, N_q, C_q) * self.head_scale + x = x.view(-1, N_q, C_q) + x = x.transpose(0, 1).reshape(N_q, B_q, C_q) + x = self.out_proj(x) + x = self.out_drop(x) + return x + + +class CustomResidualAttentionBlock(nn.Module): + def __init__( + self, + d_model: int, + n_head: int, + mlp_ratio: float = 4.0, + ls_init_value: float = None, + act_layer: Callable = nn.GELU, + norm_layer: Callable = LayerNorm, + scale_cosine_attn: bool = False, + scale_heads: bool = False, + scale_attn: bool = False, + scale_fc: bool = False, + cross_attn: bool = False, + xattn: bool = False, + ): + super().__init__() + + self.ln_1 = norm_layer(d_model) + self.ln_1_k = norm_layer(d_model) if cross_attn else self.ln_1 + self.ln_1_v = norm_layer(d_model) if cross_attn else self.ln_1 + self.attn = CustomAttention(d_model, n_head, qkv_bias=True, attn_drop=0.0, proj_drop=0.0, scaled_cosine=scale_cosine_attn, scale_heads=scale_heads, xattn=xattn) + + self.ln_attn = norm_layer(d_model) if scale_attn else nn.Identity() + self.ls_1 = LayerScale(d_model, ls_init_value) if ls_init_value is not None else nn.Identity() + + self.ln_2 = norm_layer(d_model) + mlp_width = int(d_model * mlp_ratio) + self.mlp = nn.Sequential(OrderedDict([("c_fc", nn.Linear(d_model, mlp_width)), ("ln", norm_layer(mlp_width) if scale_fc else nn.Identity()), ("gelu", act_layer()), ("c_proj", nn.Linear(mlp_width, d_model))])) + + self.ls_2 = LayerScale(d_model, ls_init_value) if ls_init_value is not None else nn.Identity() + + def forward(self, q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, attn_mask: Optional[torch.Tensor] = None): + q = q + self.ls_1(self.ln_attn(self.attn(self.ln_1(q), self.ln_1_k(k), self.ln_1_v(v), attn_mask=attn_mask))) + q = q + self.ls_2(self.mlp(self.ln_2(q))) + return q + + +class CustomTransformer(nn.Module): + def __init__( + self, + width: int, + layers: int, + heads: int, + mlp_ratio: float = 4.0, + ls_init_value: float = None, + act_layer: Callable = nn.GELU, + norm_layer: Callable = LayerNorm, + scale_cosine_attn: bool = True, + scale_heads: bool = False, + scale_attn: bool = False, + scale_fc: bool = False, + cross_attn: bool = False, + xattn: bool = False, + ): + super().__init__() + self.width = width + self.layers = layers + self.grad_checkpointing = False + self.xattn = xattn + + self.resblocks = nn.ModuleList( + [ + CustomResidualAttentionBlock( + width, + heads, + mlp_ratio, + ls_init_value=ls_init_value, + act_layer=act_layer, + norm_layer=norm_layer, + scale_cosine_attn=scale_cosine_attn, + scale_heads=scale_heads, + scale_attn=scale_attn, + scale_fc=scale_fc, + cross_attn=cross_attn, + xattn=xattn, + ) + for _ in range(layers) + ] + ) + + def get_cast_dtype(self) -> torch.dtype: + return self.resblocks[0].mlp.c_fc.weight.dtype + + def forward(self, q: torch.Tensor, k: torch.Tensor = None, v: torch.Tensor = None, attn_mask: Optional[torch.Tensor] = None): + if k is None and v is None: + k = v = q + for r in self.resblocks: + if self.grad_checkpointing and not torch.jit.is_scripting(): + q = checkpoint(r, q, k, v, attn_mask) + else: + q = r(q, k, v, attn_mask=attn_mask) + return q + + +class ResidualAttentionBlock(nn.Module): + def __init__( + self, + d_model: int, + n_head: int, + mlp_ratio: float = 4.0, + ls_init_value: float = None, + act_layer: Callable = nn.GELU, + norm_layer: Callable = LayerNorm, + xattn: bool = False, + ): + super().__init__() + + self.ln_1 = norm_layer(d_model) + if xattn: + self.attn = Attention(d_model, n_head, xattn=True) + else: + self.attn = nn.MultiheadAttention(d_model, n_head) + self.ls_1 = LayerScale(d_model, ls_init_value) if ls_init_value is not None else nn.Identity() + + self.ln_2 = norm_layer(d_model) + mlp_width = int(d_model * mlp_ratio) + self.mlp = nn.Sequential(OrderedDict([("c_fc", nn.Linear(d_model, mlp_width)), ("gelu", act_layer()), ("c_proj", nn.Linear(mlp_width, d_model))])) + + self.ls_2 = LayerScale(d_model, ls_init_value) if ls_init_value is not None else nn.Identity() + self.xattn = xattn + + def attention(self, x: torch.Tensor, attn_mask: Optional[torch.Tensor] = None): + attn_mask = attn_mask.to(x.dtype) if attn_mask is not None else None + if self.xattn: + return self.attn(x, attn_mask=attn_mask) + return self.attn(x, x, x, need_weights=False, attn_mask=attn_mask)[0] + + def forward(self, x: torch.Tensor, attn_mask: Optional[torch.Tensor] = None): + x = x + self.ls_1(self.attention(self.ln_1(x), attn_mask=attn_mask)) + x = x + self.ls_2(self.mlp(self.ln_2(x))) + return x + + +class Transformer(nn.Module): + def __init__( + self, + width: int, + layers: int, + heads: int, + mlp_ratio: float = 4.0, + ls_init_value: float = None, + act_layer: Callable = nn.GELU, + norm_layer: Callable = LayerNorm, + xattn: bool = False, + ): + super().__init__() + self.width = width + self.layers = layers + self.grad_checkpointing = False + + self.resblocks = nn.ModuleList([ResidualAttentionBlock(width, heads, mlp_ratio, ls_init_value=ls_init_value, act_layer=act_layer, norm_layer=norm_layer, xattn=xattn) for _ in range(layers)]) + + def get_cast_dtype(self) -> torch.dtype: + return self.resblocks[0].mlp.c_fc.weight.dtype + + def forward(self, x: torch.Tensor, attn_mask: Optional[torch.Tensor] = None): + for r in self.resblocks: + if self.grad_checkpointing and not torch.jit.is_scripting(): + x = checkpoint(r, x, attn_mask) + else: + x = r(x, attn_mask=attn_mask) + return x + + +class VisionTransformer(nn.Module): + def __init__( + self, + image_size: int, + patch_size: int, + width: int, + layers: int, + heads: int, + mlp_ratio: float, + ls_init_value: float = None, + patch_dropout: float = 0.0, + global_average_pool: bool = False, + output_dim: int = 512, + act_layer: Callable = nn.GELU, + norm_layer: Callable = LayerNorm, + xattn: bool = False, + ): + super().__init__() + self.image_size = to_2tuple(image_size) + self.patch_size = to_2tuple(patch_size) + self.grid_size = (self.image_size[0] // self.patch_size[0], self.image_size[1] // self.patch_size[1]) + self.output_dim = output_dim + self.conv1 = nn.Conv2d(in_channels=3, out_channels=width, kernel_size=patch_size, stride=patch_size, bias=False) + + scale = width**-0.5 + self.class_embedding = nn.Parameter(scale * torch.randn(width)) + self.positional_embedding = nn.Parameter(scale * torch.randn(self.grid_size[0] * self.grid_size[1] + 1, width)) + + # setting a patch_dropout of 0. would mean it is disabled and this function would be the identity fn + self.patch_dropout = PatchDropout(patch_dropout) if patch_dropout > 0.0 else nn.Identity() + self.ln_pre = norm_layer(width) + + self.transformer = Transformer(width, layers, heads, mlp_ratio, ls_init_value=ls_init_value, act_layer=act_layer, norm_layer=norm_layer, xattn=xattn) + + self.global_average_pool = global_average_pool + self.ln_post = norm_layer(width) + self.proj = nn.Parameter(scale * torch.randn(width, output_dim)) + + def lock(self, unlocked_groups=0, freeze_bn_stats=False): + for param in self.parameters(): + param.requires_grad = False + + if unlocked_groups != 0: + groups = [ + [ + self.conv1, + self.class_embedding, + self.positional_embedding, + self.ln_pre, + ], + *self.transformer.resblocks[:-1], + [ + self.transformer.resblocks[-1], + self.ln_post, + ], + self.proj, + ] + + def _unlock(x): + if isinstance(x, Sequence): + for g in x: + _unlock(g) + else: + if isinstance(x, torch.nn.Parameter): + x.requires_grad = True + else: + for p in x.parameters(): + p.requires_grad = True + + _unlock(groups[-unlocked_groups:]) + + def get_num_layers(self): + return self.transformer.layers + + @torch.jit.ignore + def set_grad_checkpointing(self, enable=True): + self.transformer.grad_checkpointing = enable + + @torch.jit.ignore + def no_weight_decay(self): + return {"positional_embedding", "class_embedding"} + + def forward(self, x: torch.Tensor, return_all_features: bool = False): + x = self.conv1(x) # shape = [*, width, grid, grid] + x = x.reshape(x.shape[0], x.shape[1], -1) # shape = [*, width, grid ** 2] + x = x.permute(0, 2, 1) # shape = [*, grid ** 2, width] + x = torch.cat([self.class_embedding.to(x.dtype) + torch.zeros(x.shape[0], 1, x.shape[-1], dtype=x.dtype, device=x.device), x], dim=1) # shape = [*, grid ** 2 + 1, width] + x = x + self.positional_embedding.to(x.dtype) + + # a patch_dropout of 0. would mean it is disabled and this function would do nothing but return what was passed in + x = self.patch_dropout(x) + x = self.ln_pre(x) + + x = x.permute(1, 0, 2) # NLD -> LND + x = self.transformer(x) + x = x.permute(1, 0, 2) # LND -> NLD + + if not return_all_features: + if self.global_average_pool: + x = x.mean(dim=1) # x = x[:,1:,:].mean(dim=1) + else: + x = x[:, 0] + + x = self.ln_post(x) + + if self.proj is not None: + x = x @ self.proj + + return x + + +class TextTransformer(nn.Module): + def __init__( + self, + context_length: int = 77, + vocab_size: int = 49408, + width: int = 512, + heads: int = 8, + layers: int = 12, + ls_init_value: float = None, + output_dim: int = 512, + act_layer: Callable = nn.GELU, + norm_layer: Callable = LayerNorm, + xattn: bool = False, + attn_mask: bool = True, + ): + super().__init__() + self.context_length = context_length + self.vocab_size = vocab_size + self.width = width + self.output_dim = output_dim + + self.token_embedding = nn.Embedding(vocab_size, width) + self.positional_embedding = nn.Parameter(torch.empty(self.context_length, width)) + self.transformer = Transformer(width=width, layers=layers, heads=heads, ls_init_value=ls_init_value, act_layer=act_layer, norm_layer=norm_layer, xattn=xattn) + + self.xattn = xattn + self.ln_final = norm_layer(width) + self.text_projection = nn.Parameter(torch.empty(width, output_dim)) + + if attn_mask: + self.register_buffer("attn_mask", self.build_attention_mask(), persistent=False) + else: + self.attn_mask = None + + self.init_parameters() + + def init_parameters(self): + nn.init.normal_(self.token_embedding.weight, std=0.02) + nn.init.normal_(self.positional_embedding, std=0.01) + + proj_std = (self.transformer.width**-0.5) * ((2 * self.transformer.layers) ** -0.5) + attn_std = self.transformer.width**-0.5 + fc_std = (2 * self.transformer.width) ** -0.5 + for block in self.transformer.resblocks: + nn.init.normal_(block.attn.in_proj_weight, std=attn_std) + nn.init.normal_(block.attn.out_proj.weight, std=proj_std) + nn.init.normal_(block.mlp.c_fc.weight, std=fc_std) + nn.init.normal_(block.mlp.c_proj.weight, std=proj_std) + + if self.text_projection is not None: + nn.init.normal_(self.text_projection, std=self.transformer.width**-0.5) + + @torch.jit.ignore + def set_grad_checkpointing(self, enable=True): + self.transformer.grad_checkpointing = enable + + @torch.jit.ignore + def no_weight_decay(self): + # return {'positional_embedding', 'token_embedding'} + return {"positional_embedding"} + + def get_num_layers(self): + return self.transformer.layers + + def build_attention_mask(self): + # lazily create causal attention mask, with full attention between the vision tokens + # pytorch uses additive attention mask; fill with -inf + mask = torch.empty(self.context_length, self.context_length) + mask.fill_(float("-inf")) + mask.triu_(1) # zero out the lower diagonal + return mask + + def forward(self, text, return_all_features: bool = False): + cast_dtype = self.transformer.get_cast_dtype() + x = self.token_embedding(text).to(cast_dtype) # [batch_size, n_ctx, d_model] + + x = x + self.positional_embedding.to(cast_dtype) + x = x.permute(1, 0, 2) # NLD -> LND + x = self.transformer(x, attn_mask=self.attn_mask) + # x = self.transformer(x) # no attention mask is applied + x = x.permute(1, 0, 2) # LND -> NLD + x = self.ln_final(x) + + if not return_all_features: + # x.shape = [batch_size, n_ctx, transformer.width] + # take features from the eot embedding (eot_token is the highest number in each sequence) + x = x[torch.arange(x.shape[0]), text.argmax(dim=-1)] @ self.text_projection + return x diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/utils.py b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/utils.py new file mode 100644 index 0000000..73b6d66 --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_clip/utils.py @@ -0,0 +1,321 @@ +from itertools import repeat +import collections.abc +import logging +import math +import numpy as np + +import torch +from torch import nn as nn +from torchvision.ops.misc import FrozenBatchNorm2d +import torch.nn.functional as F + + +# open CLIP +def resize_clip_pos_embed(state_dict, model, interpolation: str = "bicubic", seq_dim=1): + # Rescale the grid of position embeddings when loading from state_dict + old_pos_embed = state_dict.get("visual.positional_embedding", None) + if old_pos_embed is None or not hasattr(model.visual, "grid_size"): + return + grid_size = to_2tuple(model.visual.grid_size) + extra_tokens = 1 # FIXME detect different token configs (ie no class token, or more) + new_seq_len = grid_size[0] * grid_size[1] + extra_tokens + if new_seq_len == old_pos_embed.shape[0]: + return + + if extra_tokens: + pos_emb_tok, pos_emb_img = old_pos_embed[:extra_tokens], old_pos_embed[extra_tokens:] + else: + pos_emb_tok, pos_emb_img = None, old_pos_embed + old_grid_size = to_2tuple(int(math.sqrt(len(pos_emb_img)))) + + logging.info("Resizing position embedding grid-size from %s to %s", old_grid_size, grid_size) + pos_emb_img = pos_emb_img.reshape(1, old_grid_size[0], old_grid_size[1], -1).permute(0, 3, 1, 2) + pos_emb_img = F.interpolate( + pos_emb_img, + size=grid_size, + mode=interpolation, + align_corners=True, + ) + pos_emb_img = pos_emb_img.permute(0, 2, 3, 1).reshape(1, grid_size[0] * grid_size[1], -1)[0] + if pos_emb_tok is not None: + new_pos_embed = torch.cat([pos_emb_tok, pos_emb_img], dim=0) + else: + new_pos_embed = pos_emb_img + state_dict["visual.positional_embedding"] = new_pos_embed + + +def resize_visual_pos_embed(state_dict, model, interpolation: str = "bicubic", seq_dim=1): + # Rescale the grid of position embeddings when loading from state_dict + old_pos_embed = state_dict.get("positional_embedding", None) + if old_pos_embed is None or not hasattr(model.visual, "grid_size"): + return + grid_size = to_2tuple(model.visual.grid_size) + extra_tokens = 1 # FIXME detect different token configs (ie no class token, or more) + new_seq_len = grid_size[0] * grid_size[1] + extra_tokens + if new_seq_len == old_pos_embed.shape[0]: + return + + if extra_tokens: + pos_emb_tok, pos_emb_img = old_pos_embed[:extra_tokens], old_pos_embed[extra_tokens:] + else: + pos_emb_tok, pos_emb_img = None, old_pos_embed + old_grid_size = to_2tuple(int(math.sqrt(len(pos_emb_img)))) + + logging.info("Resizing position embedding grid-size from %s to %s", old_grid_size, grid_size) + pos_emb_img = pos_emb_img.reshape(1, old_grid_size[0], old_grid_size[1], -1).permute(0, 3, 1, 2) + pos_emb_img = F.interpolate( + pos_emb_img, + size=grid_size, + mode=interpolation, + align_corners=True, + ) + pos_emb_img = pos_emb_img.permute(0, 2, 3, 1).reshape(1, grid_size[0] * grid_size[1], -1)[0] + if pos_emb_tok is not None: + new_pos_embed = torch.cat([pos_emb_tok, pos_emb_img], dim=0) + else: + new_pos_embed = pos_emb_img + state_dict["positional_embedding"] = new_pos_embed + + +def resize_evaclip_pos_embed(state_dict, model, interpolation: str = "bicubic", seq_dim=1): + all_keys = list(state_dict.keys()) + # interpolate position embedding + if "visual.pos_embed" in state_dict: + pos_embed_checkpoint = state_dict["visual.pos_embed"] + embedding_size = pos_embed_checkpoint.shape[-1] + num_patches = model.visual.patch_embed.num_patches + # num_extra_tokens = model.visual.pos_embed.shape[-2] - num_patches + num_extra_tokens = 1 # FIXME detect different token configs (ie no class token, or more) + # height (== width) for the checkpoint position embedding + orig_size = int((pos_embed_checkpoint.shape[-2] - num_extra_tokens) ** 0.5) + # height (== width) for the new position embedding + new_size = int(num_patches**0.5) + # class_token and dist_token are kept unchanged + if orig_size != new_size: + print("Position interpolate from %dx%d to %dx%d" % (orig_size, orig_size, new_size, new_size)) + extra_tokens = pos_embed_checkpoint[:, :num_extra_tokens] + # only the position tokens are interpolated + pos_tokens = pos_embed_checkpoint[:, num_extra_tokens:] + pos_tokens = pos_tokens.reshape(-1, orig_size, orig_size, embedding_size).permute(0, 3, 1, 2) + pos_tokens = torch.nn.functional.interpolate(pos_tokens, size=(new_size, new_size), mode="bicubic", align_corners=False) + pos_tokens = pos_tokens.permute(0, 2, 3, 1).flatten(1, 2) + new_pos_embed = torch.cat((extra_tokens, pos_tokens), dim=1) + state_dict["visual.pos_embed"] = new_pos_embed + + patch_embed_proj = state_dict["visual.patch_embed.proj.weight"] + patch_size = model.visual.patch_embed.patch_size + state_dict["visual.patch_embed.proj.weight"] = torch.nn.functional.interpolate(patch_embed_proj.float(), size=patch_size, mode="bicubic", align_corners=False) + + +def resize_eva_pos_embed(state_dict, model, interpolation: str = "bicubic", seq_dim=1): + all_keys = list(state_dict.keys()) + # interpolate position embedding + if "pos_embed" in state_dict: + pos_embed_checkpoint = state_dict["pos_embed"] + embedding_size = pos_embed_checkpoint.shape[-1] + num_patches = model.visual.patch_embed.num_patches + # num_extra_tokens = model.visual.pos_embed.shape[-2] - num_patches + num_extra_tokens = 1 # FIXME detect different token configs (ie no class token, or more) + # height (== width) for the checkpoint position embedding + orig_size = int((pos_embed_checkpoint.shape[-2] - num_extra_tokens) ** 0.5) + # height (== width) for the new position embedding + new_size = int(num_patches**0.5) + # class_token and dist_token are kept unchanged + if orig_size != new_size: + print("Position interpolate from %dx%d to %dx%d" % (orig_size, orig_size, new_size, new_size)) + extra_tokens = pos_embed_checkpoint[:, :num_extra_tokens] + # only the position tokens are interpolated + pos_tokens = pos_embed_checkpoint[:, num_extra_tokens:] + pos_tokens = pos_tokens.reshape(-1, orig_size, orig_size, embedding_size).permute(0, 3, 1, 2) + pos_tokens = torch.nn.functional.interpolate(pos_tokens, size=(new_size, new_size), mode="bicubic", align_corners=False) + pos_tokens = pos_tokens.permute(0, 2, 3, 1).flatten(1, 2) + new_pos_embed = torch.cat((extra_tokens, pos_tokens), dim=1) + state_dict["pos_embed"] = new_pos_embed + + patch_embed_proj = state_dict["patch_embed.proj.weight"] + patch_size = model.visual.patch_embed.patch_size + state_dict["patch_embed.proj.weight"] = torch.nn.functional.interpolate(patch_embed_proj.float(), size=patch_size, mode="bicubic", align_corners=False) + + +def resize_rel_pos_embed(state_dict, model, interpolation: str = "bicubic", seq_dim=1): + all_keys = list(state_dict.keys()) + for key in all_keys: + if "relative_position_index" in key: + state_dict.pop(key) + + if "relative_position_bias_table" in key: + rel_pos_bias = state_dict[key] + src_num_pos, num_attn_heads = rel_pos_bias.size() + dst_num_pos, _ = model.visual.state_dict()[key].size() + dst_patch_shape = model.visual.patch_embed.patch_shape + if dst_patch_shape[0] != dst_patch_shape[1]: + raise NotImplementedError() + num_extra_tokens = dst_num_pos - (dst_patch_shape[0] * 2 - 1) * (dst_patch_shape[1] * 2 - 1) + src_size = int((src_num_pos - num_extra_tokens) ** 0.5) + dst_size = int((dst_num_pos - num_extra_tokens) ** 0.5) + if src_size != dst_size: + print("Position interpolate for %s from %dx%d to %dx%d" % (key, src_size, src_size, dst_size, dst_size)) + extra_tokens = rel_pos_bias[-num_extra_tokens:, :] + rel_pos_bias = rel_pos_bias[:-num_extra_tokens, :] + + def geometric_progression(a, r, n): + return a * (1.0 - r**n) / (1.0 - r) + + left, right = 1.01, 1.5 + while right - left > 1e-6: + q = (left + right) / 2.0 + gp = geometric_progression(1, q, src_size // 2) + if gp > dst_size // 2: + right = q + else: + left = q + + # if q > 1.090307: + # q = 1.090307 + + dis = [] + cur = 1 + for i in range(src_size // 2): + dis.append(cur) + cur += q ** (i + 1) + + r_ids = [-_ for _ in reversed(dis)] + + x = r_ids + [0] + dis + y = r_ids + [0] + dis + + t = dst_size // 2.0 + dx = np.arange(-t, t + 0.1, 1.0) + dy = np.arange(-t, t + 0.1, 1.0) + + print("Original positions = %s" % str(x)) + print("Target positions = %s" % str(dx)) + + all_rel_pos_bias = [] + + for i in range(num_attn_heads): + z = rel_pos_bias[:, i].view(src_size, src_size).float().numpy() + f = F.interpolate.interp2d(x, y, z, kind="cubic") + all_rel_pos_bias.append(torch.Tensor(f(dx, dy)).contiguous().view(-1, 1).to(rel_pos_bias.device)) + + rel_pos_bias = torch.cat(all_rel_pos_bias, dim=-1) + + new_rel_pos_bias = torch.cat((rel_pos_bias, extra_tokens), dim=0) + state_dict[key] = new_rel_pos_bias + + # interpolate position embedding + if "pos_embed" in state_dict: + pos_embed_checkpoint = state_dict["pos_embed"] + embedding_size = pos_embed_checkpoint.shape[-1] + num_patches = model.visual.patch_embed.num_patches + num_extra_tokens = model.visual.pos_embed.shape[-2] - num_patches + # height (== width) for the checkpoint position embedding + orig_size = int((pos_embed_checkpoint.shape[-2] - num_extra_tokens) ** 0.5) + # height (== width) for the new position embedding + new_size = int(num_patches**0.5) + # class_token and dist_token are kept unchanged + if orig_size != new_size: + print("Position interpolate from %dx%d to %dx%d" % (orig_size, orig_size, new_size, new_size)) + extra_tokens = pos_embed_checkpoint[:, :num_extra_tokens] + # only the position tokens are interpolated + pos_tokens = pos_embed_checkpoint[:, num_extra_tokens:] + pos_tokens = pos_tokens.reshape(-1, orig_size, orig_size, embedding_size).permute(0, 3, 1, 2) + pos_tokens = torch.nn.functional.interpolate(pos_tokens, size=(new_size, new_size), mode="bicubic", align_corners=False) + pos_tokens = pos_tokens.permute(0, 2, 3, 1).flatten(1, 2) + new_pos_embed = torch.cat((extra_tokens, pos_tokens), dim=1) + state_dict["pos_embed"] = new_pos_embed + + patch_embed_proj = state_dict["patch_embed.proj.weight"] + patch_size = model.visual.patch_embed.patch_size + state_dict["patch_embed.proj.weight"] = torch.nn.functional.interpolate(patch_embed_proj.float(), size=patch_size, mode="bicubic", align_corners=False) + + +def freeze_batch_norm_2d(module, module_match={}, name=""): + """ + Converts all `BatchNorm2d` and `SyncBatchNorm` layers of provided module into `FrozenBatchNorm2d`. If `module` is + itself an instance of either `BatchNorm2d` or `SyncBatchNorm`, it is converted into `FrozenBatchNorm2d` and + returned. Otherwise, the module is walked recursively and submodules are converted in place. + + Args: + module (torch.nn.Module): Any PyTorch module. + module_match (dict): Dictionary of full module names to freeze (all if empty) + name (str): Full module name (prefix) + + Returns: + torch.nn.Module: Resulting module + + Inspired by https://github.com/pytorch/pytorch/blob/a5895f85be0f10212791145bfedc0261d364f103/torch/nn/modules/batchnorm.py#L762 + """ + res = module + is_match = True + if module_match: + is_match = name in module_match + if is_match and isinstance(module, (nn.modules.batchnorm.BatchNorm2d, nn.modules.batchnorm.SyncBatchNorm)): + res = FrozenBatchNorm2d(module.num_features) + res.num_features = module.num_features + res.affine = module.affine + if module.affine: + res.weight.data = module.weight.data.clone().detach() + res.bias.data = module.bias.data.clone().detach() + res.running_mean.data = module.running_mean.data + res.running_var.data = module.running_var.data + res.eps = module.eps + else: + for child_name, child in module.named_children(): + full_child_name = ".".join([name, child_name]) if name else child_name + new_child = freeze_batch_norm_2d(child, module_match, full_child_name) + if new_child is not child: + res.add_module(child_name, new_child) + return res + + +# From PyTorch internals +def _ntuple(n): + def parse(x): + if isinstance(x, collections.abc.Iterable): + return x + return tuple(repeat(x, n)) + + return parse + + +to_1tuple = _ntuple(1) +to_2tuple = _ntuple(2) +to_3tuple = _ntuple(3) +to_4tuple = _ntuple(4) +to_ntuple = lambda n, x: _ntuple(n)(x) + + +def is_logging(args): + def is_global_master(args): + return args.rank == 0 + + def is_local_master(args): + return args.local_rank == 0 + + def is_master(args, local=False): + return is_local_master(args) if local else is_global_master(args) + + return is_master + + +class AllGather(torch.autograd.Function): + """An autograd function that performs allgather on a tensor. + Performs all_gather operation on the provided tensors. + *** Warning ***: torch.distributed.all_gather has no gradient. + """ + + @staticmethod + def forward(ctx, tensor, rank, world_size): + tensors_gather = [torch.empty_like(tensor) for _ in range(world_size)] + torch.distributed.all_gather(tensors_gather, tensor) + ctx.rank = rank + ctx.batch_size = tensor.shape[0] + return torch.cat(tensors_gather, 0) + + @staticmethod + def backward(ctx, grad_output): + return (grad_output[ctx.batch_size * ctx.rank : ctx.batch_size * (ctx.rank + 1)], None, None) + + +allgather = AllGather.apply diff --git a/llava/model/multimodal_encoder/dev_eva_clip/eva_vit.py b/llava/model/multimodal_encoder/dev_eva_clip/eva_vit.py new file mode 100644 index 0000000..9bd7465 --- /dev/null +++ b/llava/model/multimodal_encoder/dev_eva_clip/eva_vit.py @@ -0,0 +1,141 @@ +# Based on EVA, BEIT, timm and DeiT code bases +# https://github.com/baaivision/EVA +# https://github.com/rwightman/pytorch-image-models/tree/master/timm +# https://github.com/microsoft/unilm/tree/master/beit +# https://github.com/facebookresearch/deit/ +# https://github.com/facebookresearch/dino +# --------------------------------------------------------' +# not tested yet +import math +from transformers import CLIPImageProcessor + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.utils.checkpoint as checkpoint +from timm.models.layers import drop_path, to_2tuple, trunc_normal_ +from .eva_clip import create_model_and_transforms, get_model_config +import torch +import torchvision +import time + +from llava.utils import rank0_print + + +class EvaViTWrapper(nn.Module): + def __init__(self, vision_tower, args, delay_load=False): + super().__init__() + + self.is_loaded = False + self.vision_tower_name = vision_tower + self.pretrained = args.vision_tower_pretrained + self.args = args + + self.select_layer = args.mm_vision_select_layer + if self.select_layer < -1: + self.select_layer += 1 + self.select_feature = getattr(args, "mm_vision_select_feature", "patch") + + self.model_config = get_model_config(self.vision_tower_name) + + if not delay_load: + rank0_print(f"Loading vision tower: {vision_tower}") + self.load_model() + elif getattr(args, "unfreeze_mm_vision_tower", False): + # TODO: better detector is needed. + rank0_print(f"The checkpoint seems to contain `vision_tower` weights: `unfreeze_mm_vision_tower`: True.") + self.load_model() + elif hasattr(args, "mm_tunable_parts") and "mm_vision_tower" in args.mm_tunable_parts: + rank0_print(f"The checkpoint seems to contain `vision_tower` weights: `mm_tunable_parts` contains `mm_vision_tower`.") + self.load_model() + + def load_model(self): + rank0_print(f"Loading: {self.vision_tower_name}") + rank0_print(f"Pretrained: {self.pretrained}") + time_start = time.time() + model, _, image_processor = create_model_and_transforms(self.vision_tower_name, self.pretrained, force_custom_clip=True, precision="fp16") + time_end = time.time() + rank0_print(f"Loaded: {self.vision_tower_name} in {time_end - time_start:.2f}s") + self.device = next(model.parameters()).device + self.dtype = next(model.parameters()).dtype + if self.device.type != "meta": + model = model.to("cuda") + self.vision_tower = model.visual + resize_transform = [t for t in image_processor.transforms if isinstance(t, torchvision.transforms.Resize)][0] + normalize_transform = [t for t in image_processor.transforms if isinstance(t, torchvision.transforms.Normalize)][0] + self.resize_transform_size = resize_transform.size + self.image_processor = CLIPImageProcessor.from_pretrained( + "openai/clip-vit-large-patch14", + crop_size=resize_transform.size, + size={"shortest_edge": resize_transform.size}, + image_mean=list(normalize_transform.mean), + image_std=list(normalize_transform.std), + ) + rank0_print(f"Loaded image processor: {self.image_processor}") + self.vision_tower.requires_grad_(False) + self.is_loaded = True + + def feature_select(self, image_features): + select_feature_type = self.select_feature + + # if self.select_feature in ["slicefour_patch", "slicefour_cls_patch"]: + # select_every_k_layer = len(image_features) // 4 + # image_features = torch.cat([image_features[i] for i in range(select_every_k_layer + self.select_layer, len(image_features), select_every_k_layer)], dim=-1) + # select_feature_type = select_feature_type.replace("slicefour_", "") + # elif self.select_feature in ["slice_m25811_f6_patch", "slice_m25811_f6_cls_patch"]: + # select_layers = [-1, -4, -7, -10, 6] + # image_features = torch.cat([image_features[i] for i in select_layers], dim=-1) + # select_feature_type = select_feature_type.replace("slice_m25811_f6_", "") + # else: + # image_features = image_features[self.select_layer] + + if select_feature_type == "patch": + image_features = image_features[:, 1:] + elif select_feature_type == "cls_patch": + image_features = image_features + else: + raise ValueError(f"Unexpected select feature: {select_feature_type}") + return image_features + + def train(self, mode=True): + self.training = mode + + if self.is_loaded: + self.vision_tower.eval() + + def forward(self, images): + if type(images) is list: + image_features = [] + for image in images: + image_features = self.vision_tower.forward_features(image.to(self.dtype), return_all_features=True) + image_features = self.feature_select(image_features).to(self.dtype) + image_features.append(image_features) + else: + image_features = self.vision_tower.forward_features(images.to(self.dtype), return_all_features=True) + image_features = self.feature_select(image_features).to(self.dtype) + + return image_features + + @property + def dummy_feature(self): + return torch.zeros(1, self.hidden_size, device=self.device, dtype=self.dtype) + + @property + def hidden_size(self): + return self.model_config["vision_cfg"]["width"] + + @property + def num_patches(self): + return (self.model_config["vision_cfg"]["image_size"] // self.model_config["vision_cfg"]["patch_size"]) ** 2 + + @property + def num_patches_per_side(self): + return self.model_config["vision_cfg"]["image_size"] // self.model_config["vision_cfg"]["patch_size"] + + @property + def config(self): + return self.model_config + + @property + def image_size(self): + return self.model_config["vision_cfg"]["image_size"] diff --git a/llava/model/multimodal_encoder/eva_clip/eva_clip_encoder.py b/llava/model/multimodal_encoder/eva_clip/eva_clip_encoder.py new file mode 100644 index 0000000..aaef516 --- /dev/null +++ b/llava/model/multimodal_encoder/eva_clip/eva_clip_encoder.py @@ -0,0 +1,74 @@ +import torch +import torch.nn as nn + +from .eva_clip_processors import EvaClipImageTrainProcessor +from .eva_vit import EVAEncoderWrapper +from .factory import list_models, add_model_config, get_model_config + +from llava.utils import rank0_print + + +class EvaClipVisionTower(nn.Module): + def __init__(self, vision_tower, args, delay_load=False): + super().__init__() + + self.is_loaded = False + self.vision_tower_name = vision_tower + self.vision_tower_pretrained = args.vision_tower_pretrained + self.config = get_model_config(vision_tower) + + if not delay_load: + rank0_print(f"Loading EVA ViT: {self.vision_tower_name}") + self.load_model() + elif getattr(args, "unfreeze_mm_vision_tower", False): + # TODO: better detector is needed. + rank0_print(f"The checkpoint seems to contain `vision_tower` weights: `unfreeze_mm_vision_tower`: True.") + self.load_model() + elif hasattr(args, "mm_tunable_parts") and "mm_vision_tower" in args.mm_tunable_parts: + rank0_print(f"The checkpoint seems to contain `vision_tower` weights: `mm_tunable_parts` contains `mm_vision_tower`.") + self.load_model() + else: + self.cfg_only = self.config + + def load_model(self, device_map=None): + rank0_print(f"Pretrained: {self.vision_tower_pretrained}") + self.image_processor = EvaClipImageTrainProcessor(self.config["vision_cfg"]["image_size"]) + self.vision_tower = EVAEncoderWrapper(self.vision_tower_pretrained, self.config) + rank0_print(f"Loaded image processor: {self.image_processor}") + self.vision_tower.requires_grad_(False) + self.is_loaded = True + + def forward(self, images): + if type(images) is list: + image_features = [] + for image in images: + image_feature = self.vision_tower(image.to(device=self.device, dtype=self.dtype).unsqueeze(0)).to(image.dtype) + image_features.append(image_feature) + else: + image_features = self.vision_tower(images.to(device=self.device, dtype=self.dtype)).to(images.dtype) + + return image_features + + @property + def dtype(self): + return self.vision_tower.dtype + + @property + def device(self): + return self.vision_tower.device + + @property + def hidden_size(self): + return self.config["vision_cfg"]["width"] + + @property + def num_patches(self): + return (self.config["vision_cfg"]["image_size"] // self.config["vision_cfg"]["patch_size"]) ** 2 + + @property + def num_patches_per_side(self): + return self.config["vision_cfg"]["image_size"] // self.config["vision_cfg"]["patch_size"] + + @property + def image_size(self): + return self.config["vision_cfg"]["image_size"] diff --git a/llava/model/multimodal_encoder/eva_clip/eva_clip_processors.py b/llava/model/multimodal_encoder/eva_clip/eva_clip_processors.py new file mode 100644 index 0000000..7ee1273 --- /dev/null +++ b/llava/model/multimodal_encoder/eva_clip/eva_clip_processors.py @@ -0,0 +1,72 @@ +""" +# Adapted from https://github.com/baaivision/EVA/tree/master/EVA-CLIP +""" + +from torchvision import transforms +from torchvision.transforms.functional import InterpolationMode +from transformers.image_processing_utils import BatchFeature +from PIL import Image +from transformers.image_transforms import convert_to_rgb + + +class BaseProcessor: + def __init__(self): + self.transform = lambda x: x + return + + def __call__(self, item): + return self.transform(item) + + +class EvaClipImageBaseProcessor(BaseProcessor): + def __init__(self, mean=None, std=None): + self.mean = (0.48145466, 0.4578275, 0.40821073) if mean is None else mean + self.std = (0.26862954, 0.26130258, 0.27577711) if std is None else std + + self.normalize = transforms.Normalize(self.mean, self.std) + + @property + def image_mean(self): + return self.mean + + +class EvaClipImageTrainProcessor(EvaClipImageBaseProcessor): + def __init__(self, image_size=224, mean=None, std=None, min_scale=0.5, max_scale=1.0): + super().__init__(mean=mean, std=std) + + self.transform = transforms.Compose( + [ + convert_to_rgb, + transforms.Resize( + image_size, + interpolation=InterpolationMode.BICUBIC, + ), + transforms.CenterCrop(image_size), + transforms.ToTensor(), + self.normalize, + ] + ) + + self.image_size = image_size + + def preprocess(self, images, return_tensors): + if isinstance(images, Image.Image): + images = [images] + else: + assert isinstance(images, list) + + transformed_images = [self.transform(image).numpy() for image in images] + data = {"pixel_values": transformed_images} + + return BatchFeature(data=data, tensor_type=return_tensors) + + def __call__(self, item): + return self.transform(item) + + @property + def crop_size(self): + return {"height": self.image_size, "width": self.image_size} + + @property + def size(self): + return {"shortest_edge": self.image_size} diff --git a/llava/model/multimodal_encoder/eva_clip/eva_vit.py b/llava/model/multimodal_encoder/eva_clip/eva_vit.py new file mode 100644 index 0000000..d2330c3 --- /dev/null +++ b/llava/model/multimodal_encoder/eva_clip/eva_vit.py @@ -0,0 +1,856 @@ +""" +# Adapted from https://github.com/baaivision/EVA/tree/master/EVA-CLIP +""" + +from math import pi +import torch +from torch import nn +from einops import rearrange, repeat +import logging +from llava.utils import rank0_print + + +def broadcat(tensors, dim=-1): + num_tensors = len(tensors) + shape_lens = set(list(map(lambda t: len(t.shape), tensors))) + assert len(shape_lens) == 1, "tensors must all have the same number of dimensions" + shape_len = list(shape_lens)[0] + dim = (dim + shape_len) if dim < 0 else dim + dims = list(zip(*map(lambda t: list(t.shape), tensors))) + expandable_dims = [(i, val) for i, val in enumerate(dims) if i != dim] + assert all([*map(lambda t: len(set(t[1])) <= 2, expandable_dims)]), "invalid dimensions for broadcastable concatentation" + max_dims = list(map(lambda t: (t[0], max(t[1])), expandable_dims)) + expanded_dims = list(map(lambda t: (t[0], (t[1],) * num_tensors), max_dims)) + expanded_dims.insert(dim, (dim, dims[dim])) + expandable_shapes = list(zip(*map(lambda t: t[1], expanded_dims))) + tensors = list(map(lambda t: t[0].expand(*t[1]), zip(tensors, expandable_shapes))) + return torch.cat(tensors, dim=dim) + + +def rotate_half(x): + x = rearrange(x, "... (d r) -> ... d r", r=2) + x1, x2 = x.unbind(dim=-1) + x = torch.stack((-x2, x1), dim=-1) + return rearrange(x, "... d r -> ... (d r)") + + +class VisionRotaryEmbeddingFast(nn.Module): + def __init__(self, dim, pt_seq_len, ft_seq_len=None, custom_freqs=None, freqs_for="lang", theta=10000, max_freq=10, num_freqs=1, patch_dropout=0.0): + super().__init__() + if custom_freqs: + freqs = custom_freqs + elif freqs_for == "lang": + freqs = 1.0 / (theta ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim)) + elif freqs_for == "pixel": + freqs = torch.linspace(1.0, max_freq / 2, dim // 2) * pi + elif freqs_for == "constant": + freqs = torch.ones(num_freqs).float() + else: + raise ValueError(f"unknown modality {freqs_for}") + + if ft_seq_len is None: + ft_seq_len = pt_seq_len + t = torch.arange(ft_seq_len) / ft_seq_len * pt_seq_len + + freqs = torch.einsum("..., f -> ... f", t, freqs) + freqs = repeat(freqs, "... n -> ... (n r)", r=2) + freqs = broadcat((freqs[:, None, :], freqs[None, :, :]), dim=-1) + + freqs_cos = freqs.cos().view(-1, freqs.shape[-1]) + freqs_sin = freqs.sin().view(-1, freqs.shape[-1]) + + self.patch_dropout = patch_dropout + + self.register_buffer("freqs_cos", freqs_cos) + self.register_buffer("freqs_sin", freqs_sin) + + logging.info(f"Shape of rope freq: {self.freqs_cos.shape}") + + def forward(self, t, patch_indices_keep=None): + if patch_indices_keep is not None: + batch = t.size()[0] + batch_indices = torch.arange(batch) + batch_indices = batch_indices[..., None] + + freqs_cos = repeat(self.freqs_cos, "i j -> n i m j", n=t.shape[0], m=t.shape[1]) + freqs_sin = repeat(self.freqs_sin, "i j -> n i m j", n=t.shape[0], m=t.shape[1]) + + freqs_cos = freqs_cos[batch_indices, patch_indices_keep] + freqs_cos = rearrange(freqs_cos, "n i m j -> n m i j") + freqs_sin = freqs_sin[batch_indices, patch_indices_keep] + freqs_sin = rearrange(freqs_sin, "n i m j -> n m i j") + + return t * freqs_cos + rotate_half(t) * freqs_sin + + return t * self.freqs_cos + rotate_half(t) * self.freqs_sin + + +class LayerNorm(nn.LayerNorm): + """Subclass torch's LayerNorm (with cast back to input dtype).""" + + def forward(self, x: torch.Tensor): + orig_type = x.dtype + x = F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps) + return x.to(orig_type) + + +class PatchDropout(nn.Module): + """ + https://arxiv.org/abs/2212.00794 + """ + + def __init__(self, prob, exclude_first_token=True): + super().__init__() + assert 0 <= prob < 1.0 + self.prob = prob + self.exclude_first_token = exclude_first_token # exclude CLS token + logging.info(f"os.getenv('RoPE')={os.getenv('RoPE')}") + + def forward(self, x): + if not self.training or self.prob == 0.0: + return x + + if self.exclude_first_token: + cls_tokens, x = x[:, :1], x[:, 1:] + else: + cls_tokens = torch.jit.annotate(torch.Tensor, x[:, :1]) + + batch = x.size()[0] + num_tokens = x.size()[1] + + batch_indices = torch.arange(batch) + batch_indices = batch_indices[..., None] + + keep_prob = 1 - self.prob + num_patches_keep = max(1, int(num_tokens * keep_prob)) + + rand = torch.randn(batch, num_tokens) + patch_indices_keep = rand.topk(num_patches_keep, dim=-1).indices + + x = x[batch_indices, patch_indices_keep] + + if self.exclude_first_token: + x = torch.cat((cls_tokens, x), dim=1) + + if self.training and os.getenv("RoPE") == "1": + return x, patch_indices_keep + + return x + + +# -------------------------------------------------------- +# Adapted from https://github.com/microsoft/unilm/tree/master/beit +# -------------------------------------------------------- +import math +import os +import torch.nn as nn +import torch.nn.functional as F + +try: + from timm.models.layers import drop_path, to_2tuple, trunc_normal_ +except: + from timm.layers import drop_path, to_2tuple, trunc_normal_ + +if os.getenv("ENV_TYPE") == "deepspeed": + try: + from deepspeed.runtime.activation_checkpointing.checkpointing import checkpoint + except: + from torch.utils.checkpoint import checkpoint +else: + from torch.utils.checkpoint import checkpoint + +try: + import xformers.ops as xops +except ImportError: + xops = None + # print("Please 'pip install xformers'") + + +class DropPath(nn.Module): + """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).""" + + def __init__(self, drop_prob=None): + super(DropPath, self).__init__() + self.drop_prob = drop_prob + + def forward(self, x): + return drop_path(x, self.drop_prob, self.training) + + def extra_repr(self) -> str: + return "p={}".format(self.drop_prob) + + +class Mlp(nn.Module): + def __init__( + self, + in_features, + hidden_features=None, + out_features=None, + act_layer=nn.GELU, + norm_layer=nn.LayerNorm, + drop=0.0, + subln=False, + ): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + self.fc1 = nn.Linear(in_features, hidden_features) + self.act = act_layer() + + self.ffn_ln = norm_layer(hidden_features) if subln else nn.Identity() + + self.fc2 = nn.Linear(hidden_features, out_features) + self.drop = nn.Dropout(drop) + + def forward(self, x): + x = self.fc1(x) + x = self.act(x) + # x = self.drop(x) + # commit this for the orignal BERT implement + x = self.ffn_ln(x) + + x = self.fc2(x) + x = self.drop(x) + return x + + +class SwiGLU(nn.Module): + def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.SiLU, drop=0.0, norm_layer=nn.LayerNorm, subln=False): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + + self.w1 = nn.Linear(in_features, hidden_features) + self.w2 = nn.Linear(in_features, hidden_features) + + self.act = act_layer() + self.ffn_ln = norm_layer(hidden_features) if subln else nn.Identity() + self.w3 = nn.Linear(hidden_features, out_features) + + self.drop = nn.Dropout(drop) + + def forward(self, x): + x1 = self.w1(x) + x2 = self.w2(x) + hidden = self.act(x1) * x2 + x = self.ffn_ln(hidden) + x = self.w3(x) + x = self.drop(x) + return x + + +class Attention(nn.Module): + def __init__(self, dim, num_heads=8, qkv_bias=False, qk_scale=None, attn_drop=0.0, proj_drop=0.0, window_size=None, attn_head_dim=None, xattn=False, rope=None, subln=False, norm_layer=nn.LayerNorm): + super().__init__() + self.num_heads = num_heads + head_dim = dim // num_heads + if attn_head_dim is not None: + head_dim = attn_head_dim + all_head_dim = head_dim * self.num_heads + self.scale = qk_scale or head_dim**-0.5 + + self.subln = subln + if self.subln: + self.q_proj = nn.Linear(dim, all_head_dim, bias=False) + self.k_proj = nn.Linear(dim, all_head_dim, bias=False) + self.v_proj = nn.Linear(dim, all_head_dim, bias=False) + else: + self.qkv = nn.Linear(dim, all_head_dim * 3, bias=False) + + if qkv_bias: + self.q_bias = nn.Parameter(torch.zeros(all_head_dim)) + self.v_bias = nn.Parameter(torch.zeros(all_head_dim)) + else: + self.q_bias = None + self.v_bias = None + + if window_size: + self.window_size = window_size + self.num_relative_distance = (2 * window_size[0] - 1) * (2 * window_size[1] - 1) + 3 + self.relative_position_bias_table = nn.Parameter(torch.zeros(self.num_relative_distance, num_heads)) # 2*Wh-1 * 2*Ww-1, nH + # cls to token & token 2 cls & cls to cls + + # get pair-wise relative position index for each token inside the window + coords_h = torch.arange(window_size[0]) + coords_w = torch.arange(window_size[1]) + coords = torch.stack(torch.meshgrid([coords_h, coords_w])) # 2, Wh, Ww + coords_flatten = torch.flatten(coords, 1) # 2, Wh*Ww + relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :] # 2, Wh*Ww, Wh*Ww + relative_coords = relative_coords.permute(1, 2, 0).contiguous() # Wh*Ww, Wh*Ww, 2 + relative_coords[:, :, 0] += window_size[0] - 1 # shift to start from 0 + relative_coords[:, :, 1] += window_size[1] - 1 + relative_coords[:, :, 0] *= 2 * window_size[1] - 1 + relative_position_index = torch.zeros(size=(window_size[0] * window_size[1] + 1,) * 2, dtype=relative_coords.dtype) + relative_position_index[1:, 1:] = relative_coords.sum(-1) # Wh*Ww, Wh*Ww + relative_position_index[0, 0:] = self.num_relative_distance - 3 + relative_position_index[0:, 0] = self.num_relative_distance - 2 + relative_position_index[0, 0] = self.num_relative_distance - 1 + + self.register_buffer("relative_position_index", relative_position_index) + else: + self.window_size = None + self.relative_position_bias_table = None + self.relative_position_index = None + + self.attn_drop = nn.Dropout(attn_drop) + self.inner_attn_ln = norm_layer(all_head_dim) if subln else nn.Identity() + # self.proj = nn.Linear(all_head_dim, all_head_dim) + self.proj = nn.Linear(all_head_dim, dim) + self.proj_drop = nn.Dropout(proj_drop) + self.xattn = xattn + self.xattn_drop = attn_drop + + self.rope = rope + + def forward(self, x, rel_pos_bias=None, attn_mask=None): + B, N, C = x.shape + if self.subln: + q = F.linear(input=x, weight=self.q_proj.weight, bias=self.q_bias) + k = F.linear(input=x, weight=self.k_proj.weight, bias=None) + v = F.linear(input=x, weight=self.v_proj.weight, bias=self.v_bias) + + q = q.reshape(B, N, self.num_heads, -1).permute(0, 2, 1, 3) # B, num_heads, N, C + k = k.reshape(B, N, self.num_heads, -1).permute(0, 2, 1, 3) + v = v.reshape(B, N, self.num_heads, -1).permute(0, 2, 1, 3) + else: + + qkv_bias = None + if self.q_bias is not None: + qkv_bias = torch.cat((self.q_bias, torch.zeros_like(self.v_bias, requires_grad=False), self.v_bias)) + + qkv = F.linear(input=x, weight=self.qkv.weight, bias=qkv_bias) + qkv = qkv.reshape(B, N, 3, self.num_heads, -1).permute(2, 0, 3, 1, 4) # 3, B, num_heads, N, C + q, k, v = qkv[0], qkv[1], qkv[2] + + if self.rope: + # slightly fast impl + q_t = q[:, :, 1:, :] + ro_q_t = self.rope(q_t) + q = torch.cat((q[:, :, :1, :], ro_q_t), -2).type_as(v) + + k_t = k[:, :, 1:, :] + ro_k_t = self.rope(k_t) + k = torch.cat((k[:, :, :1, :], ro_k_t), -2).type_as(v) + + if self.xattn and xops is not None: + q = q.permute(0, 2, 1, 3) # B, num_heads, N, C -> B, N, num_heads, C + k = k.permute(0, 2, 1, 3) + v = v.permute(0, 2, 1, 3) + + x = xops.memory_efficient_attention( + q, + k, + v, + p=self.xattn_drop, + scale=self.scale, + ) + x = x.reshape(B, N, -1) + x = self.inner_attn_ln(x) + x = self.proj(x) + x = self.proj_drop(x) + else: + q = q * self.scale + attn = q @ k.transpose(-2, -1) + + if self.relative_position_bias_table is not None: + relative_position_bias = self.relative_position_bias_table[self.relative_position_index.view(-1)].view(self.window_size[0] * self.window_size[1] + 1, self.window_size[0] * self.window_size[1] + 1, -1) # Wh*Ww,Wh*Ww,nH + relative_position_bias = relative_position_bias.permute(2, 0, 1).contiguous() # nH, Wh*Ww, Wh*Ww + attn = attn + relative_position_bias.unsqueeze(0).type_as(attn) + + if rel_pos_bias is not None: + attn = attn + rel_pos_bias.type_as(attn) + + if attn_mask is not None: + attn_mask = attn_mask.bool() + attn = attn.masked_fill(~attn_mask[:, None, None, :], float("-inf")) + + attn = attn.softmax(dim=-1) + attn = self.attn_drop(attn) + + x = (attn @ v).transpose(1, 2).reshape(B, N, -1) + x = self.inner_attn_ln(x) + x = self.proj(x) + x = self.proj_drop(x) + return x + + +class Block(nn.Module): + + def __init__( + self, + dim, + num_heads, + mlp_ratio=4.0, + qkv_bias=False, + qk_scale=None, + drop=0.0, + attn_drop=0.0, + drop_path=0.0, + init_values=None, + act_layer=nn.GELU, + norm_layer=nn.LayerNorm, + window_size=None, + attn_head_dim=None, + xattn=False, + rope=None, + postnorm=False, + subln=False, + naiveswiglu=False, + ): + super().__init__() + self.norm1 = norm_layer(dim) + self.attn = Attention( + dim, num_heads=num_heads, qkv_bias=qkv_bias, qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop, window_size=window_size, attn_head_dim=attn_head_dim, xattn=xattn, rope=rope, subln=subln, norm_layer=norm_layer + ) + # NOTE: drop path for stochastic depth, we shall see if this is better than dropout here + self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity() + self.norm2 = norm_layer(dim) + mlp_hidden_dim = int(dim * mlp_ratio) + + if naiveswiglu: + self.mlp = SwiGLU( + in_features=dim, + hidden_features=mlp_hidden_dim, + subln=subln, + norm_layer=norm_layer, + ) + else: + self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, subln=subln, drop=drop) + + if init_values is not None and init_values > 0: + self.gamma_1 = nn.Parameter(init_values * torch.ones((dim)), requires_grad=True) + self.gamma_2 = nn.Parameter(init_values * torch.ones((dim)), requires_grad=True) + else: + self.gamma_1, self.gamma_2 = None, None + + self.postnorm = postnorm + + def forward(self, x, rel_pos_bias=None, attn_mask=None): + if self.gamma_1 is None: + if self.postnorm: + x = x + self.drop_path(self.norm1(self.attn(x, rel_pos_bias=rel_pos_bias, attn_mask=attn_mask))) + x = x + self.drop_path(self.norm2(self.mlp(x))) + else: + x = x + self.drop_path(self.attn(self.norm1(x), rel_pos_bias=rel_pos_bias, attn_mask=attn_mask)) + x = x + self.drop_path(self.mlp(self.norm2(x))) + else: + if self.postnorm: + x = x + self.drop_path(self.gamma_1 * self.norm1(self.attn(x, rel_pos_bias=rel_pos_bias, attn_mask=attn_mask))) + x = x + self.drop_path(self.gamma_2 * self.norm2(self.mlp(x))) + else: + x = x + self.drop_path(self.gamma_1 * self.attn(self.norm1(x), rel_pos_bias=rel_pos_bias, attn_mask=attn_mask)) + x = x + self.drop_path(self.gamma_2 * self.mlp(self.norm2(x))) + return x + + +class PatchEmbed(nn.Module): + """Image to Patch Embedding""" + + def __init__(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768): + super().__init__() + img_size = to_2tuple(img_size) + patch_size = to_2tuple(patch_size) + num_patches = (img_size[1] // patch_size[1]) * (img_size[0] // patch_size[0]) + self.patch_shape = (img_size[0] // patch_size[0], img_size[1] // patch_size[1]) + self.img_size = img_size + self.patch_size = patch_size + self.num_patches = num_patches + + self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size) + + def forward(self, x, **kwargs): + B, C, H, W = x.shape + # FIXME look at relaxing size constraints + assert H == self.img_size[0] and W == self.img_size[1], f"Input image size ({H}*{W}) doesn't match model ({self.img_size[0]}*{self.img_size[1]})." + x = self.proj(x).flatten(2).transpose(1, 2) + return x + + +class RelativePositionBias(nn.Module): + + def __init__(self, window_size, num_heads): + super().__init__() + self.window_size = window_size + self.num_relative_distance = (2 * window_size[0] - 1) * (2 * window_size[1] - 1) + 3 + self.relative_position_bias_table = nn.Parameter(torch.zeros(self.num_relative_distance, num_heads)) # 2*Wh-1 * 2*Ww-1, nH + # cls to token & token 2 cls & cls to cls + + # get pair-wise relative position index for each token inside the window + coords_h = torch.arange(window_size[0]) + coords_w = torch.arange(window_size[1]) + coords = torch.stack(torch.meshgrid([coords_h, coords_w])) # 2, Wh, Ww + coords_flatten = torch.flatten(coords, 1) # 2, Wh*Ww + relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :] # 2, Wh*Ww, Wh*Ww + relative_coords = relative_coords.permute(1, 2, 0).contiguous() # Wh*Ww, Wh*Ww, 2 + relative_coords[:, :, 0] += window_size[0] - 1 # shift to start from 0 + relative_coords[:, :, 1] += window_size[1] - 1 + relative_coords[:, :, 0] *= 2 * window_size[1] - 1 + relative_position_index = torch.zeros(size=(window_size[0] * window_size[1] + 1,) * 2, dtype=relative_coords.dtype) + relative_position_index[1:, 1:] = relative_coords.sum(-1) # Wh*Ww, Wh*Ww + relative_position_index[0, 0:] = self.num_relative_distance - 3 + relative_position_index[0:, 0] = self.num_relative_distance - 2 + relative_position_index[0, 0] = self.num_relative_distance - 1 + + self.register_buffer("relative_position_index", relative_position_index) + + def forward(self): + relative_position_bias = self.relative_position_bias_table[self.relative_position_index.view(-1)].view(self.window_size[0] * self.window_size[1] + 1, self.window_size[0] * self.window_size[1] + 1, -1) # Wh*Ww,Wh*Ww,nH + return relative_position_bias.permute(2, 0, 1).contiguous() # nH, Wh*Ww, Wh*Ww + + +class EVAVisionTransformer(nn.Module): + """Vision Transformer with support for patch or hybrid CNN input stage""" + + def __init__( + self, + img_size=224, + patch_size=16, + in_chans=3, + num_classes=1000, + embed_dim=768, + depth=12, + num_heads=12, + mlp_ratio=4.0, + qkv_bias=False, + qk_scale=None, + drop_rate=0.0, + attn_drop_rate=0.0, + drop_path_rate=0.0, + norm_layer=nn.LayerNorm, + init_values=None, + patch_dropout=0.0, + use_abs_pos_emb=True, + use_rel_pos_bias=False, + use_shared_rel_pos_bias=False, + rope=False, + use_mean_pooling=True, + init_scale=0.001, + grad_checkpointing=False, + xattn=False, + postnorm=False, + pt_hw_seq_len=16, + intp_freq=False, + naiveswiglu=False, + subln=False, + ): + super().__init__() + self.image_size = img_size + self.num_classes = num_classes + self.num_features = self.embed_dim = embed_dim # num_features for consistency with other models + + self.patch_embed = PatchEmbed(img_size=img_size, patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim) + num_patches = self.patch_embed.num_patches + + self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim)) + # self.mask_token = nn.Parameter(torch.zeros(1, 1, embed_dim)) + if use_abs_pos_emb: + self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + 1, embed_dim)) + else: + self.pos_embed = None + self.pos_drop = nn.Dropout(p=drop_rate) + + if use_shared_rel_pos_bias: + self.rel_pos_bias = RelativePositionBias(window_size=self.patch_embed.patch_shape, num_heads=num_heads) + else: + self.rel_pos_bias = None + + if rope: + half_head_dim = embed_dim // num_heads // 2 + hw_seq_len = img_size // patch_size + self.rope = VisionRotaryEmbeddingFast( + dim=half_head_dim, + pt_seq_len=pt_hw_seq_len, + ft_seq_len=hw_seq_len if intp_freq else None, + # patch_dropout=patch_dropout + ) + else: + self.rope = None + + self.naiveswiglu = naiveswiglu + + dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)] # stochastic depth decay rule + self.use_rel_pos_bias = use_rel_pos_bias + self.blocks = nn.ModuleList( + [ + Block( + dim=embed_dim, + num_heads=num_heads, + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + drop=drop_rate, + attn_drop=attn_drop_rate, + drop_path=dpr[i], + norm_layer=norm_layer, + init_values=init_values, + window_size=self.patch_embed.patch_shape if use_rel_pos_bias else None, + xattn=xattn, + rope=self.rope, + postnorm=postnorm, + subln=subln, + naiveswiglu=naiveswiglu, + ) + for i in range(depth) + ] + ) + self.norm = nn.Identity() if use_mean_pooling else norm_layer(embed_dim) + self.fc_norm = norm_layer(embed_dim) if use_mean_pooling else None + self.head = nn.Linear(embed_dim, num_classes) if num_classes > 0 else nn.Identity() + + if self.pos_embed is not None: + trunc_normal_(self.pos_embed, std=0.02) + + trunc_normal_(self.cls_token, std=0.02) + # trunc_normal_(self.mask_token, std=.02) + + self.apply(self._init_weights) + self.fix_init_weight() + + if isinstance(self.head, nn.Linear): + trunc_normal_(self.head.weight, std=0.02) + self.head.weight.data.mul_(init_scale) + self.head.bias.data.mul_(init_scale) + + # setting a patch_dropout of 0. would mean it is disabled and this function would be the identity fn + self.patch_dropout = PatchDropout(patch_dropout) if patch_dropout > 0.0 else nn.Identity() + + self.grad_checkpointing = grad_checkpointing + + def fix_init_weight(self): + def rescale(param, layer_id): + param.div_(math.sqrt(2.0 * layer_id)) + + for layer_id, layer in enumerate(self.blocks): + rescale(layer.attn.proj.weight.data, layer_id + 1) + if self.naiveswiglu: + rescale(layer.mlp.w3.weight.data, layer_id + 1) + else: + rescale(layer.mlp.fc2.weight.data, layer_id + 1) + + def get_cast_dtype(self) -> torch.dtype: + return self.blocks[0].mlp.fc2.weight.dtype + + def _init_weights(self, m): + if isinstance(m, nn.Linear): + trunc_normal_(m.weight, std=0.02) + if m.bias is not None: + nn.init.constant_(m.bias, 0) + elif isinstance(m, nn.LayerNorm): + nn.init.constant_(m.bias, 0) + nn.init.constant_(m.weight, 1.0) + + def get_num_layers(self): + return len(self.blocks) + + def lock(self, unlocked_groups=0, freeze_bn_stats=False): + assert unlocked_groups == 0, "partial locking not currently supported for this model" + for param in self.parameters(): + param.requires_grad = False + + @torch.jit.ignore + def set_grad_checkpointing(self, enable=True): + self.grad_checkpointing = enable + + @torch.jit.ignore + def no_weight_decay(self): + return {"pos_embed", "cls_token"} + + def get_classifier(self): + return self.head + + def reset_classifier(self, num_classes, global_pool=""): + self.num_classes = num_classes + self.head = nn.Linear(self.embed_dim, num_classes) if num_classes > 0 else nn.Identity() + + def forward_features(self, x, return_all_features=False): + + x = self.patch_embed(x) + batch_size, seq_len, _ = x.size() + + cls_tokens = self.cls_token.expand(batch_size, -1, -1) # stole cls_tokens impl from Phil Wang, thanks + x = torch.cat((cls_tokens, x), dim=1) + if self.pos_embed is not None: + x = x + self.pos_embed + x = self.pos_drop(x) + + # a patch_dropout of 0. would mean it is disabled and this function would do nothing but return what was passed in + if os.getenv("RoPE") == "1": + if self.training and not isinstance(self.patch_dropout, nn.Identity): + x, patch_indices_keep = self.patch_dropout(x) + # Directly pass patch_indices_keep to self.rope.forward + x = self.rope.forward(x, patch_indices_keep=patch_indices_keep) + else: + # Pass None or omit the patch_indices_keep argument for default behavior + x = self.rope.forward(x, patch_indices_keep=None) + x = self.patch_dropout(x) + else: + x = self.patch_dropout(x) + + rel_pos_bias = self.rel_pos_bias() if self.rel_pos_bias is not None else None + for i, blk in enumerate(self.blocks): + if i == len(self.blocks) - 1: + continue + if self.grad_checkpointing: + x = checkpoint(blk, x, (rel_pos_bias,)) + else: + x = blk(x, rel_pos_bias=rel_pos_bias) + + if not return_all_features: + x = self.norm(x) + if self.fc_norm is not None: + return self.fc_norm(x.mean(1)) + else: + return x[:, 0] + return x + + def forward(self, x, return_all_features=False): + if return_all_features: + return self.forward_features(x, return_all_features) + x = self.forward_features(x) + x = self.head(x) + return x + + +def load_state_dict(checkpoint_path: str, map_location: str = "cpu", model_key: str = "model|module|state_dict", is_openai: bool = False, skip_list: list = []): + if is_openai: + model = torch.jit.load(checkpoint_path, map_location="cpu").eval() + state_dict = model.state_dict() + for key in ["input_resolution", "context_length", "vocab_size"]: + state_dict.pop(key, None) + else: + checkpoint = torch.load(checkpoint_path, map_location=map_location) + for mk in model_key.split("|"): + if isinstance(checkpoint, dict) and mk in checkpoint: + state_dict = checkpoint[mk] + break + else: + state_dict = checkpoint + if next(iter(state_dict.items()))[0].startswith("module"): + state_dict = {k[7:]: v for k, v in state_dict.items()} + + for k in skip_list: + if k in list(state_dict.keys()): + logging.info(f"Removing key {k} from pretrained checkpoint") + del state_dict[k] + + if os.getenv("RoPE") == "1": + for k in list(state_dict.keys()): + if "freqs_cos" in k or "freqs_sin" in k: + del state_dict[k] + return state_dict + + +def load_clip_visual_state_dict(checkpoint_path: str, map_location: str = "cpu", is_openai: bool = False, skip_list: list = []): + state_dict = load_state_dict(checkpoint_path, map_location=map_location, is_openai=is_openai, skip_list=skip_list) + # for k in list(state_dict.keys()): + # if not k.startswith("visual."): + # del state_dict[k] + # for k in list(state_dict.keys()): + # if k.startswith("visual."): + # new_k = k[7:] + # state_dict[new_k] = state_dict[k] + # del state_dict[k] + return state_dict + + +from dataclasses import dataclass +from typing import Optional, Tuple, Union + +try: + from apex.normalization import FusedLayerNorm +except: + FusedLayerNorm = LayerNorm + # print("Please build and install Nvidia apex package with option '--cuda_ext' according to https://github.com/NVIDIA/apex#from-source .") + + +@dataclass +class CLIPVisionCfg: + layers: Union[Tuple[int, int, int, int], int] = 12 + width: int = 768 + head_width: int = 64 + mlp_ratio: float = 4.0 + patch_size: int = 16 + image_size: Union[Tuple[int, int], int] = 224 + ls_init_value: Optional[float] = None # layer scale initial value + patch_dropout: float = 0.0 # what fraction of patches to dropout during training (0 would mean disabled and no patches dropped) - 0.5 to 0.75 recommended in the paper for optimal results + global_average_pool: bool = False # whether to global average pool the last embedding layer, instead of using CLS token (https://arxiv.org/abs/2205.01580) + drop_path_rate: Optional[float] = None # drop path rate + timm_model_name: str = None # a valid model name overrides layers, width, patch_size + timm_model_pretrained: bool = False # use (imagenet) pretrained weights for named model + timm_pool: str = "avg" # feature pooling for timm model ('abs_attn', 'rot_attn', 'avg', '') + timm_proj: str = "linear" # linear projection for timm model output ('linear', 'mlp', '') + timm_proj_bias: bool = False # enable bias final projection + eva_model_name: str = None # a valid eva model name overrides layers, width, patch_size + qkv_bias: bool = True + fusedLN: bool = False + xattn: bool = False + postnorm: bool = False + rope: bool = False + pt_hw_seq_len: int = 16 # 224/14 + intp_freq: bool = False + naiveswiglu: bool = False + subln: bool = False + + +def create_norm_layer_factory(use_fused_ln, eps=1e-6): + # Otherwise, use the standard LayerNorm + return lambda num_features: nn.LayerNorm(num_features, eps=eps) + + +def _build_vision_tower(vision_tower_path: str, embed_dim: int, vision_cfg: CLIPVisionCfg, **kwargs): + if isinstance(vision_cfg, dict): + vision_cfg = CLIPVisionCfg(**vision_cfg) + + if vision_cfg.eva_model_name: + vision_heads = vision_cfg.width // vision_cfg.head_width + # Determine the appropriate norm layer factory based on the configuration + norm_layer_factory = create_norm_layer_factory(vision_cfg.fusedLN, eps=1e-6) + + visual = EVAVisionTransformer( + img_size=vision_cfg.image_size, + patch_size=vision_cfg.patch_size, + num_classes=embed_dim, + use_mean_pooling=vision_cfg.global_average_pool, # False + init_values=vision_cfg.ls_init_value, + patch_dropout=vision_cfg.patch_dropout, + embed_dim=vision_cfg.width, + depth=vision_cfg.layers, + num_heads=vision_heads, + mlp_ratio=vision_cfg.mlp_ratio, + qkv_bias=vision_cfg.qkv_bias, + drop_path_rate=vision_cfg.drop_path_rate, + norm_layer=norm_layer_factory, + xattn=vision_cfg.xattn, + rope=vision_cfg.rope, + postnorm=vision_cfg.postnorm, + pt_hw_seq_len=vision_cfg.pt_hw_seq_len, # 224/14 + intp_freq=vision_cfg.intp_freq, + naiveswiglu=vision_cfg.naiveswiglu, + subln=vision_cfg.subln, + ) + + state_dict = load_clip_visual_state_dict(vision_tower_path) + incompatible_keys = visual.load_state_dict(state_dict, strict=False) + rank0_print("EVA-CLIP incompatible_keys:", incompatible_keys) + + return visual + + +class EVAEncoderWrapper(nn.Module): + def __init__(self, vision_tower_pretrained, config): + super(EVAEncoderWrapper, self).__init__() + self.config = config + self.config["vision_tower_path"] = vision_tower_pretrained + self.model = _build_vision_tower(**self.config) + + def forward(self, image, **kwargs): + encode = self.model(image, return_all_features=True)[:, 1:, :] # remove the CLS token + return encode + + @property + def dtype(self): + return list(self.parameters())[-1].dtype + + @property + def device(self): + return list(self.parameters())[-1].device diff --git a/llava/model/multimodal_encoder/eva_clip/factory.py b/llava/model/multimodal_encoder/eva_clip/factory.py new file mode 100644 index 0000000..6d3fafc --- /dev/null +++ b/llava/model/multimodal_encoder/eva_clip/factory.py @@ -0,0 +1,60 @@ +import json +import logging +import os +import pathlib +import re +from copy import deepcopy +from pathlib import Path +from typing import Optional, Tuple, Union, Dict, Any +import torch + +_MODEL_CONFIG_PATHS = [Path(__file__).parent / f"model_configs/"] +_MODEL_CONFIGS = {} # directory (model_name: config) of model architecture configs + + +def _natural_key(string_): + return [int(s) if s.isdigit() else s for s in re.split(r"(\d+)", string_.lower())] + + +def _rescan_model_configs(): + global _MODEL_CONFIGS + + config_ext = (".json",) + config_files = [] + for config_path in _MODEL_CONFIG_PATHS: + if config_path.is_file() and config_path.suffix in config_ext: + config_files.append(config_path) + elif config_path.is_dir(): + for ext in config_ext: + config_files.extend(config_path.glob(f"*{ext}")) + + for cf in config_files: + with open(cf, "r", encoding="utf8") as f: + model_cfg = json.load(f) + if all(a in model_cfg for a in ("embed_dim", "vision_cfg", "text_cfg")): + _MODEL_CONFIGS[cf.stem] = model_cfg + + _MODEL_CONFIGS = dict(sorted(_MODEL_CONFIGS.items(), key=lambda x: _natural_key(x[0]))) + + +_rescan_model_configs() # initial populate of model config registry + + +def list_models(): + """enumerate available model architectures based on config files""" + return list(_MODEL_CONFIGS.keys()) + + +def add_model_config(path): + """add model config path or file and update registry""" + if not isinstance(path, Path): + path = Path(path) + _MODEL_CONFIG_PATHS.append(path) + _rescan_model_configs() + + +def get_model_config(model_name): + if model_name in _MODEL_CONFIGS: + return deepcopy(_MODEL_CONFIGS[model_name]) + else: + return None diff --git a/llava/model/multimodal_encoder/eva_clip/model_configs/EVA-CLIP-18B.json b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA-CLIP-18B.json new file mode 100644 index 0000000..4917556 --- /dev/null +++ b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA-CLIP-18B.json @@ -0,0 +1,27 @@ +{ + "embed_dim": 1536, + "vision_cfg": { + "image_size": 224, + "layers": 48, + "width": 5120, + "head_width": 128, + "mlp_ratio": 5, + "patch_size": 14, + "eva_model_name": "eva-clip-18b-14-x", + "drop_path_rate": 0, + "qkv_bias": false, + "xattn": true, + "postnorm": true, + "fusedLN": false, + "use_rms_norm": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 1280, + "heads": 20, + "layers": 32, + "xattn": false, + "fusedLN": false + } +} \ No newline at end of file diff --git a/llava/model/multimodal_encoder/eva_clip/model_configs/EVA-CLIP-8B-plus.json b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA-CLIP-8B-plus.json new file mode 100644 index 0000000..7d843da --- /dev/null +++ b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA-CLIP-8B-plus.json @@ -0,0 +1,27 @@ +{ + "embed_dim": 1280, + "vision_cfg": { + "image_size": 448, + "layers": 32, + "width": 4096, + "head_width": 128, + "mlp_ratio": 5, + "patch_size": 14, + "eva_model_name": "eva-clip-8b-14-plus-x", + "drop_path_rate": 0, + "qkv_bias": false, + "xattn": true, + "postnorm": false, + "fusedLN": false, + "use_rms_norm": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 1280, + "heads": 20, + "layers": 32, + "xattn": false, + "fusedLN": false + } +} \ No newline at end of file diff --git a/llava/model/multimodal_encoder/eva_clip/model_configs/EVA-CLIP-8B.json b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA-CLIP-8B.json new file mode 100644 index 0000000..689492a --- /dev/null +++ b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA-CLIP-8B.json @@ -0,0 +1,27 @@ +{ + "embed_dim": 1280, + "vision_cfg": { + "image_size": 224, + "layers": 32, + "width": 4096, + "head_width": 128, + "mlp_ratio": 5, + "patch_size": 14, + "eva_model_name": "eva-clip-8b-14-x", + "drop_path_rate": 0, + "qkv_bias": false, + "xattn": true, + "postnorm": false, + "fusedLN": false, + "use_rms_norm": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 1280, + "heads": 20, + "layers": 32, + "xattn": false, + "fusedLN": false + } +} \ No newline at end of file diff --git a/llava/model/multimodal_encoder/eva_clip/model_configs/EVA01-CLIP-B-16.json b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA01-CLIP-B-16.json new file mode 100644 index 0000000..aad2058 --- /dev/null +++ b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA01-CLIP-B-16.json @@ -0,0 +1,19 @@ +{ + "embed_dim": 512, + "vision_cfg": { + "image_size": 224, + "layers": 12, + "width": 768, + "patch_size": 16, + "eva_model_name": "eva-clip-b-16", + "ls_init_value": 0.1, + "drop_path_rate": 0.0 + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 512, + "heads": 8, + "layers": 12 + } +} \ No newline at end of file diff --git a/llava/model/multimodal_encoder/eva_clip/model_configs/EVA01-CLIP-g-14-plus.json b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA01-CLIP-g-14-plus.json new file mode 100644 index 0000000..1002795 --- /dev/null +++ b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA01-CLIP-g-14-plus.json @@ -0,0 +1,24 @@ +{ + "embed_dim": 1024, + "vision_cfg": { + "image_size": 224, + "layers": 40, + "width": 1408, + "head_width": 88, + "mlp_ratio": 4.3637, + "patch_size": 14, + "eva_model_name": "eva-clip-g-14-x", + "drop_path_rate": 0, + "xattn": true, + "fusedLN": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 1024, + "heads": 16, + "layers": 24, + "xattn": false, + "fusedLN": true + } +} \ No newline at end of file diff --git a/llava/model/multimodal_encoder/eva_clip/model_configs/EVA01-CLIP-g-14.json b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA01-CLIP-g-14.json new file mode 100644 index 0000000..5d338b4 --- /dev/null +++ b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA01-CLIP-g-14.json @@ -0,0 +1,24 @@ +{ + "embed_dim": 1024, + "vision_cfg": { + "image_size": 224, + "layers": 40, + "width": 1408, + "head_width": 88, + "mlp_ratio": 4.3637, + "patch_size": 14, + "eva_model_name": "eva-clip-g-14-x", + "drop_path_rate": 0.4, + "xattn": true, + "fusedLN": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 768, + "heads": 12, + "layers": 12, + "xattn": false, + "fusedLN": true + } +} \ No newline at end of file diff --git a/llava/model/multimodal_encoder/eva_clip/model_configs/EVA02-CLIP-B-16.json b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA02-CLIP-B-16.json new file mode 100644 index 0000000..e4a6e72 --- /dev/null +++ b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA02-CLIP-B-16.json @@ -0,0 +1,29 @@ +{ + "embed_dim": 512, + "vision_cfg": { + "image_size": 224, + "layers": 12, + "width": 768, + "head_width": 64, + "patch_size": 16, + "mlp_ratio": 2.6667, + "eva_model_name": "eva-clip-b-16-X", + "drop_path_rate": 0.0, + "xattn": true, + "fusedLN": true, + "rope": true, + "pt_hw_seq_len": 16, + "intp_freq": true, + "naiveswiglu": true, + "subln": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 512, + "heads": 8, + "layers": 12, + "xattn": true, + "fusedLN": true + } +} \ No newline at end of file diff --git a/llava/model/multimodal_encoder/eva_clip/model_configs/EVA02-CLIP-L-14-336.json b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA02-CLIP-L-14-336.json new file mode 100644 index 0000000..3e1d124 --- /dev/null +++ b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA02-CLIP-L-14-336.json @@ -0,0 +1,29 @@ +{ + "embed_dim": 768, + "vision_cfg": { + "image_size": 336, + "layers": 24, + "width": 1024, + "drop_path_rate": 0, + "head_width": 64, + "mlp_ratio": 2.6667, + "patch_size": 14, + "eva_model_name": "eva-clip-l-14-336", + "xattn": true, + "fusedLN": true, + "rope": true, + "pt_hw_seq_len": 16, + "intp_freq": true, + "naiveswiglu": true, + "subln": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 768, + "heads": 12, + "layers": 12, + "xattn": false, + "fusedLN": true + } +} \ No newline at end of file diff --git a/llava/model/multimodal_encoder/eva_clip/model_configs/EVA02-CLIP-L-14.json b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA02-CLIP-L-14.json new file mode 100644 index 0000000..03b22ad --- /dev/null +++ b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA02-CLIP-L-14.json @@ -0,0 +1,29 @@ +{ + "embed_dim": 768, + "vision_cfg": { + "image_size": 224, + "layers": 24, + "width": 1024, + "drop_path_rate": 0, + "head_width": 64, + "mlp_ratio": 2.6667, + "patch_size": 14, + "eva_model_name": "eva-clip-l-14", + "xattn": true, + "fusedLN": true, + "rope": true, + "pt_hw_seq_len": 16, + "intp_freq": true, + "naiveswiglu": true, + "subln": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 768, + "heads": 12, + "layers": 12, + "xattn": false, + "fusedLN": true + } +} \ No newline at end of file diff --git a/llava/model/multimodal_encoder/eva_clip/model_configs/EVA02-CLIP-bigE-14-plus.json b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA02-CLIP-bigE-14-plus.json new file mode 100644 index 0000000..aa04e25 --- /dev/null +++ b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA02-CLIP-bigE-14-plus.json @@ -0,0 +1,25 @@ +{ + "embed_dim": 1024, + "vision_cfg": { + "image_size": 224, + "layers": 64, + "width": 1792, + "head_width": 112, + "mlp_ratio": 8.571428571428571, + "patch_size": 14, + "eva_model_name": "eva-clip-4b-14-x", + "drop_path_rate": 0, + "xattn": true, + "postnorm": true, + "fusedLN": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 1280, + "heads": 20, + "layers": 32, + "xattn": false, + "fusedLN": true + } +} diff --git a/llava/model/multimodal_encoder/eva_clip/model_configs/EVA02-CLIP-bigE-14.json b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA02-CLIP-bigE-14.json new file mode 100644 index 0000000..747ffcc --- /dev/null +++ b/llava/model/multimodal_encoder/eva_clip/model_configs/EVA02-CLIP-bigE-14.json @@ -0,0 +1,25 @@ +{ + "embed_dim": 1024, + "vision_cfg": { + "image_size": 224, + "layers": 64, + "width": 1792, + "head_width": 112, + "mlp_ratio": 8.571428571428571, + "patch_size": 14, + "eva_model_name": "eva-clip-4b-14-x", + "drop_path_rate": 0, + "xattn": true, + "postnorm": true, + "fusedLN": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 1024, + "heads": 16, + "layers": 24, + "xattn": false, + "fusedLN": true + } +} \ No newline at end of file diff --git a/llava/model/multimodal_encoder/eva_clip/model_configs/Internal-EVA02-CLIP-10B-14-448.json b/llava/model/multimodal_encoder/eva_clip/model_configs/Internal-EVA02-CLIP-10B-14-448.json new file mode 100644 index 0000000..ad71aff --- /dev/null +++ b/llava/model/multimodal_encoder/eva_clip/model_configs/Internal-EVA02-CLIP-10B-14-448.json @@ -0,0 +1,25 @@ +{ + "embed_dim": 1024, + "vision_cfg": { + "image_size": 448, + "layers": 77, + "width": 2304, + "head_width": 144, + "mlp_ratio": 10.9722, + "patch_size": 14, + "eva_model_name": "eva-clip-10b-14-x", + "drop_path_rate": 0, + "xattn": true, + "postnorm": false, + "fusedLN": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 1280, + "heads": 20, + "layers": 32, + "xattn": false, + "fusedLN": true + } +} diff --git a/llava/model/multimodal_encoder/eva_clip/model_configs/Internal-EVA02-CLIP-10B-14.json b/llava/model/multimodal_encoder/eva_clip/model_configs/Internal-EVA02-CLIP-10B-14.json new file mode 100644 index 0000000..21b2068 --- /dev/null +++ b/llava/model/multimodal_encoder/eva_clip/model_configs/Internal-EVA02-CLIP-10B-14.json @@ -0,0 +1,25 @@ +{ + "embed_dim": 1024, + "vision_cfg": { + "image_size": 224, + "layers": 77, + "width": 2304, + "head_width": 144, + "mlp_ratio": 10.9722, + "patch_size": 14, + "eva_model_name": "eva-clip-10b-14-x", + "drop_path_rate": 0, + "xattn": true, + "postnorm": false, + "fusedLN": true + }, + "text_cfg": { + "context_length": 77, + "vocab_size": 49408, + "width": 1280, + "heads": 20, + "layers": 32, + "xattn": false, + "fusedLN": true + } +} diff --git a/llava/model/multimodal_encoder/hf_vision.py b/llava/model/multimodal_encoder/hf_vision.py new file mode 100644 index 0000000..a413208 --- /dev/null +++ b/llava/model/multimodal_encoder/hf_vision.py @@ -0,0 +1,111 @@ +import torch +import torch.nn as nn + +from transformers import AutoModel, AutoImageProcessor, AutoConfig, CLIPImageProcessor +from llava.utils import rank0_print + + +class HFVisionTower(nn.Module): + def __init__(self, vision_tower, args, delay_load=False): + super().__init__() + + self.is_loaded = False + + self.vision_tower_name = vision_tower.replace("hf:", "", 1) + self.select_layer = args.mm_vision_select_layer + self.select_feature = getattr(args, "mm_vision_select_feature", "patch") + + if not delay_load: + self.load_model() + else: + self.cfg_only = AutoConfig.from_pretrained(self.vision_tower_name) + + def load_model(self): + try: + self.image_processor = AutoImageProcessor.from_pretrained(self.vision_tower_name) + except Exception as e: + if "448" in self.vision_tower_name: + image_size = 448 + # use image processor with conig + self.image_processor = CLIPImageProcessor(size={"shortest_edge": image_size}, do_center_crop=True, crop_size=image_size) + else: + self.image_processor = CLIPImageProcessor.from_pretrained("openai/clip-vit-large-patch14") + rank0_print(f"Loaded image processor: {self.image_processor}") + self.vision_tower = AutoModel.from_pretrained(self.vision_tower_name, torch_dtype=torch.bfloat16, trust_remote_code=True).to("cuda") + self.device = self.vision_tower.device + self.dtype = self.vision_tower.dtype + self.config = self.vision_tower.config + + if hasattr(self.vision_tower, "vision_model"): + self.vision_tower = self.vision_tower.vision_model + self.vision_tower.requires_grad_(False) + # self.vision_tower.eval() + self.is_loaded = True + + def feature_select(self, image_forward_outs): + select_feature_type = self.select_feature + + if self.select_feature in ["slicefour_patch", "slicefour_cls_patch"]: + select_every_k_layer = len(image_forward_outs.hidden_states) // 4 + image_features = torch.cat([image_forward_outs.hidden_states[i] for i in range(select_every_k_layer + self.select_layer, len(image_forward_outs.hidden_states), select_every_k_layer)], dim=-1) + select_feature_type = select_feature_type.replace("slicefour_", "") + else: + image_features = image_forward_outs.hidden_states[self.select_layer] + + if select_feature_type == "patch": + image_features = image_features[:, 1:] + elif select_feature_type == "cls_patch": + image_features = image_features + else: + raise ValueError(f"Unexpected select feature: {select_feature_type}") + return image_features + + def forward(self, images): + if type(images) is list: + image_features = [] + for image in images: + image_forward_out = self.vision_tower(image.to(device=self.device, dtype=self.dtype).unsqueeze(0), output_hidden_states=True) + image_feature = self.feature_select(image_forward_out).to(image.dtype) + image_features.append(image_feature) + else: + image_forward_outs = self.vision_tower(images.to(device=self.device, dtype=self.dtype), output_hidden_states=True) + image_features = self.feature_select(image_forward_outs).to(images.dtype) + + return image_features + + @property + def dummy_feature(self): + return torch.zeros(1, self.hidden_size, device=self.device, dtype=self.dtype) + + # @property + # def dtype(self): + # return self.vision_tower.dtype + + # @property + # def device(self): + # return self.vision_tower.device + + @property + def hidden_size(self): + try: + _hidden_size = self.config.hidden_size + except: + _hidden_size = self.config.vision_config.hidden_size + if "slicefour" in self.select_feature: + _hidden_size *= 4 + return _hidden_size + + @property + def num_patches(self): + _num_patches = (self.config.image_size // self.config.patch_size) ** 2 + if "cls_patch" in self.select_feature: + _num_patches += 1 + return _num_patches + + @property + def num_patches_per_side(self): + return self.config.image_size // self.config.patch_size + + @property + def image_size(self): + return self.config.image_size diff --git a/llava/model/multimodal_encoder/imagebind.py b/llava/model/multimodal_encoder/imagebind.py new file mode 100644 index 0000000..8bbe71c --- /dev/null +++ b/llava/model/multimodal_encoder/imagebind.py @@ -0,0 +1,73 @@ +import torch +import torch.nn as nn + +from transformers import CLIPImageProcessor + +try: + from imagebind.models import imagebind_model + from imagebind.models.imagebind_model import ModalityType + from imagebind.data import load_and_transform_audio_data +except ImportError: + pass + + +class ImageBindWrapper(nn.Module): + def __init__(self, vision_tower, select_layer, select_feature="patch", delay_load=False): + super().__init__() + + self.is_loaded = False + + self.vision_tower_name = vision_tower + self.select_layer = select_layer + self.select_feature = select_feature + + if not delay_load: + self.load_model() + + def load_model(self): + self.image_processor = CLIPImageProcessor.from_pretrained("openai/clip-vit-large-patch14") + self.vision_tower = imagebind_model.imagebind_huge(pretrained=True) + for p in self.vision_tower.parameters(): + p.requires_grad = False + self.vision_tower.eval() + self.is_loaded = True + + def train(self, mode=True): + self.training = mode + + if self.is_loaded: + self.vision_tower.eval() + + @torch.no_grad() + def forward(self, x): + if type(x) == dict: + if x["audios"] is not None: + inputs = {ModalityType.AUDIO: load_and_transform_audio_data(x["audios"], device=self.device).half()} + embeddings = self.vision_tower(inputs) + audio_embedding = embeddings[ModalityType.AUDIO] + return audio_embedding.unsqueeze(1) + else: + inputs = {ModalityType.VISION: x.to(dtype=self.dtype)} + embeddings = self.vision_tower(inputs) + vision_embedding = embeddings[ModalityType.VISION] + if vision_embedding.ndim == 2: + return vision_embedding.unsqueeze(1) + if vision_embedding.shape[1] == 257: + return vision_embedding[:, 1:] + raise ValueError(f"Unexpected shape: {vision_embedding.shape}") + + @property + def dummy_feature(self): + return torch.zeros(1, 1024, device=self.device, dtype=self.dtype) + + @property + def dtype(self): + return self.vision_tower.modality_preprocessors.vision.cls_token.dtype + + @property + def device(self): + return self.vision_tower.modality_preprocessors.vision.cls_token.device + + @property + def hidden_size(self): + return 1024 diff --git a/llava/model/multimodal_encoder/open_clip_encoder.py b/llava/model/multimodal_encoder/open_clip_encoder.py new file mode 100644 index 0000000..17a3277 --- /dev/null +++ b/llava/model/multimodal_encoder/open_clip_encoder.py @@ -0,0 +1,163 @@ +import torch +import torch.nn as nn +from transformers import CLIPImageProcessor +from llava.utils import rank0_print + +try: + import open_clip + import torchvision + from open_clip.transformer import _expand_token +except ImportError: + print("OpenCLIP not installed") + open_clip = None + +HIDDEN_SIZE_DICT = { + "ViT-H-14-378-quickgelu": 1280, +} + + +class OpenCLIPVisionTower(nn.Module): + def __init__(self, vision_tower, args, delay_load=False): + super().__init__() + + self.is_loaded = False + self.model_name = vision_tower.replace("open_clip_hub:", "") + self.pretrained = args.vision_tower_pretrained + self.select_layer = args.mm_vision_select_layer + self.select_feature = getattr(args, "mm_vision_select_feature", "patch") + + if not delay_load: + rank0_print(f"Loading vision tower: {vision_tower}") + self.load_model() + elif getattr(args, "unfreeze_mm_vision_tower", False): + # TODO: better detector is needed. + rank0_print(f"The checkpoint seems to contain `vision_tower` weights: `unfreeze_mm_vision_tower`: True.") + self.load_model() + elif hasattr(args, "mm_tunable_parts") and "mm_vision_tower" in args.mm_tunable_parts: + rank0_print(f"The checkpoint seems to contain `vision_tower` weights: `mm_tunable_parts` contains `mm_vision_tower`.") + self.load_model() + + def load_model(self, device_map="auto"): + rank0_print(f"Loading OpenCLIP model: {self.model_name}") + rank0_print(f"Pretrained: {self.pretrained}") + vision_tower, _, image_processor = open_clip.create_model_and_transforms(model_name=self.model_name, pretrained=self.pretrained, precision="fp32", device="cuda") + + resize_transform = [t for t in image_processor.transforms if isinstance(t, torchvision.transforms.Resize)][0] + normalize_transform = [t for t in image_processor.transforms if isinstance(t, torchvision.transforms.Normalize)][0] + self.resize_transform_size = resize_transform.size # 224 or 384 + self.patch_size = vision_tower.visual.conv1.kernel_size[0] # 14 or 16 + + self.image_processor = CLIPImageProcessor.from_pretrained( + "openai/clip-vit-large-patch14", + crop_size=resize_transform.size, + size={"shortest_edge": resize_transform.size}, + image_mean=list(normalize_transform.mean), + image_std=list(normalize_transform.std), + ) + rank0_print(f"Loaded image processor: {self.image_processor}") + self.vision_tower = vision_tower.visual + self.vision_tower.requires_grad_(False) + + self.is_loaded = True + + def feature_select(self, image_forward_outs): + image_features = image_forward_outs[self.select_layer] + if self.select_feature == "patch": + image_features = image_features[:, 1:] + elif self.select_feature == "cls_patch": + image_features = image_features + elif self.select_feature == "conv_flatten": + image_features = image_features.flatten(2).transpose(1, 2) + else: + raise ValueError(f"Unexpected select feature: {self.select_feature}") + return image_features + + def forward_visual(self, x, output_hidden_states=False): + if hasattr(self.vision_tower, "trunk") and hasattr(self.vision_tower.trunk, "_intermediate_layers"): + return self.vision_tower.trunk._intermediate_layers(x, abs(self.select_layer)) + else: + + def forward_openclip(self, x: torch.Tensor): + features = [] + x = self.conv1(x) # shape = [*, width, grid, grid] + x = x.reshape(x.shape[0], x.shape[1], -1) # shape = [*, width, grid ** 2] + x = x.permute(0, 2, 1) # shape = [*, grid ** 2, width] + + # class embeddings and positional embeddings + x = torch.cat( + [_expand_token(self.class_embedding, x.shape[0]).to(x.dtype), x], + dim=1, + ) + # shape = [*, grid ** 2 + 1, width] + x = x + self.positional_embedding.to(x.dtype) + + x = self.patch_dropout(x) + x = self.ln_pre(x) + + x = x.permute(1, 0, 2) # NLD -> LND + for r in self.transformer.resblocks: + x = r(x, attn_mask=None) + features.append(x) + return features + + return forward_openclip(self.vision_tower, x) + + def forward(self, images): + if type(images) is list: + image_features = [] + for image in images: + image_forward_out = self.forward_visual(image.to(self.dtype).unsqueeze(0), output_hidden_states=True) + image_feature = self.feature_select(image_forward_out).to(image.dtype) + image_features.append(image_feature) + else: + image_forward_outs = self.forward_visual(images.to(self.dtype), output_hidden_states=True) + image_features = self.feature_select(image_forward_outs).to(images.dtype) + + return image_features + + @property + def dummy_feature(self): + return torch.zeros(1, self.hidden_size, device=self.device, dtype=self.dtype) + + @property + def dtype(self): + if hasattr(self.vision_tower, "conv1"): + return self.vision_tower.conv1.weight.dtype + if hasattr(self.vision_tower, "trunk"): + return self.vision_tower.trunk.patch_embed.proj.weight.dtype + raise NotImplementedError + + @property + def device(self): + if hasattr(self.vision_tower, "conv1"): + return self.vision_tower.conv1.weight.device + if hasattr(self.vision_tower, "trunk"): + return self.vision_tower.trunk.patch_embed.proj.weight.device + raise NotImplementedError + + @property + def config(self): + return None + + @property + def hidden_size(self): + if self.model_name in HIDDEN_SIZE_DICT: + return HIDDEN_SIZE_DICT[self.model_name] + else: + raise NotImplementedError + + @property + def num_patches(self): + image_size = self.resize_transform_size if isinstance(self.resize_transform_size, int) else self.resize_transform_size[0] + _num_patches = (image_size // self.patch_size) ** 2 + if "cls_patch" in self.select_feature: + _num_patches += 1 + return _num_patches + + @property + def image_size(self): + return self.resize_transform_size + + @property + def num_patches_per_side(self): + return self.resize_transform_size // self.patch_size diff --git a/llava/model/multimodal_encoder/siglip_encoder.py b/llava/model/multimodal_encoder/siglip_encoder.py new file mode 100644 index 0000000..f1e101a --- /dev/null +++ b/llava/model/multimodal_encoder/siglip_encoder.py @@ -0,0 +1,620 @@ +""" +# Adapted from https://huggingface.co/MILVLG/imp-v1-3b/blob/main/vision_encoder.py +""" + +from typing import Optional, Tuple, Union, Dict +from dataclasses import dataclass +from functools import partial, reduce +from PIL import Image +import torch +import torch.utils.checkpoint +from torch import nn +import os +from transformers.image_processing_utils import BatchFeature, get_size_dict +from transformers.image_transforms import ( + convert_to_rgb, + normalize, + rescale, + resize, + to_channel_dimension_format, +) +from transformers.image_utils import ( + ChannelDimension, + PILImageResampling, + to_numpy_array, +) +from transformers.activations import ACT2FN +from transformers.modeling_outputs import BaseModelOutput, BaseModelOutputWithPooling +from transformers.modeling_utils import PreTrainedModel +from transformers import PretrainedConfig +from transformers.utils import ModelOutput +from llava.utils import rank0_print + + +class SigLipImageProcessor: + def __init__(self, image_mean=(0.5, 0.5, 0.5), image_std=(0.5, 0.5, 0.5), size=(384, 384), crop_size: Dict[str, int] = None, resample=PILImageResampling.BICUBIC, rescale_factor=1 / 255, data_format=ChannelDimension.FIRST): + crop_size = crop_size if crop_size is not None else {"height": 384, "width": 384} + crop_size = get_size_dict(crop_size, default_to_square=True, param_name="crop_size") + + self.image_mean = image_mean + self.image_std = image_std + self.size = size + self.resample = resample + self.rescale_factor = rescale_factor + self.data_format = data_format + self.crop_size = crop_size + + def preprocess(self, images, return_tensors): + if isinstance(images, Image.Image): + images = [images] + else: + # to adapt video data + images = [to_numpy_array(image) for image in images] + assert isinstance(images, list) + + transforms = [ + convert_to_rgb, + to_numpy_array, + partial(resize, size=self.size, resample=self.resample, data_format=self.data_format), + partial(rescale, scale=self.rescale_factor, data_format=self.data_format), + partial(normalize, mean=self.image_mean, std=self.image_std, data_format=self.data_format), + partial(to_channel_dimension_format, channel_dim=self.data_format, input_channel_dim=self.data_format), + ] + + images = reduce(lambda x, f: [*map(f, x)], transforms, images) + data = {"pixel_values": images} + + return BatchFeature(data=data, tensor_type=return_tensors) + + +class SigLipVisionConfig(PretrainedConfig): + model_type = "siglip_vision_model" + + def __init__( + self, + hidden_size=1152, + image_mean=(0.5, 0.5, 0.5), + intermediate_size=4304, + num_hidden_layers=27, + num_attention_heads=16, + num_channels=3, + image_size=384, + patch_size=14, + hidden_act="gelu_pytorch_tanh", + layer_norm_eps=1e-6, + attention_dropout=0.0, + **kwargs, + ): + super().__init__(**kwargs) + + self.hidden_size = hidden_size + self.intermediate_size = intermediate_size + self.num_hidden_layers = num_hidden_layers + self.num_attention_heads = num_attention_heads + self.num_channels = num_channels + self.patch_size = patch_size + self.image_size = image_size + self.attention_dropout = attention_dropout + self.layer_norm_eps = layer_norm_eps + self.hidden_act = hidden_act + self.image_mean = image_mean + + @classmethod + def from_pretrained(cls, pretrained_model_name_or_path: Union[str, os.PathLike], **kwargs) -> "PretrainedConfig": + cls._set_token_in_kwargs(kwargs) + + config_dict, kwargs = cls.get_config_dict(pretrained_model_name_or_path, **kwargs) + + # get the vision config dict if we are loading from SigLipConfig + if config_dict.get("model_type") == "siglip": + config_dict = config_dict["vision_config"] + + if "model_type" in config_dict and hasattr(cls, "model_type") and config_dict["model_type"] != cls.model_type: + print(f"You are using a model of type {config_dict['model_type']} to instantiate a model of type " f"{cls.model_type}. This is not supported for all configurations of models and can yield errors.") + + return cls.from_dict(config_dict, **kwargs) + + +@dataclass +# Copied from transformers.models.clip.modeling_clip.CLIPVisionModelOutput with CLIP->SigLip +class SigLipVisionModelOutput(ModelOutput): + """ + Base class for vision model's outputs that also contains image embeddings of the pooling of the last hidden states. + + Args: + image_embeds (`torch.FloatTensor` of shape `(batch_size, output_dim)` *optional* returned when model is initialized with `with_projection=True`): + The image embeddings obtained by applying the projection layer to the pooler_output. + last_hidden_state (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`): + Sequence of hidden-states at the output of the last layer of the model. + hidden_states (`tuple(torch.FloatTensor)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`): + Tuple of `torch.FloatTensor` (one for the output of the embeddings, if the model has an embedding layer, + + one for the output of each layer) of shape `(batch_size, sequence_length, hidden_size)`. + + Hidden-states of the model at the output of each layer plus the optional initial embedding outputs. + attentions (`tuple(torch.FloatTensor)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`): + Tuple of `torch.FloatTensor` (one for each layer) of shape `(batch_size, num_heads, sequence_length, + sequence_length)`. + + Attentions weights after the attention softmax, used to compute the weighted average in the self-attention + heads. + """ + + image_embeds: Optional[torch.FloatTensor] = None + last_hidden_state: torch.FloatTensor = None + hidden_states: Optional[Tuple[torch.FloatTensor]] = None + attentions: Optional[Tuple[torch.FloatTensor]] = None + + +class SigLipVisionEmbeddings(nn.Module): + def __init__(self, config: SigLipVisionConfig): + super().__init__() + self.config = config + self.embed_dim = config.hidden_size + self.image_size = config.image_size + self.patch_size = config.patch_size + + self.patch_embedding = nn.Conv2d( + in_channels=config.num_channels, + out_channels=self.embed_dim, + kernel_size=self.patch_size, + stride=self.patch_size, + padding="valid", + ) + + self.num_patches = (self.image_size // self.patch_size) ** 2 + self.num_positions = self.num_patches + self.position_embedding = nn.Embedding(self.num_positions, self.embed_dim) + self.register_buffer("position_ids", torch.arange(self.num_positions).expand((1, -1)), persistent=False) + + def forward(self, pixel_values: torch.FloatTensor) -> torch.Tensor: + patch_embeds = self.patch_embedding(pixel_values) # shape = [*, width, grid, grid] + embeddings = patch_embeds.flatten(2).transpose(1, 2) + + embeddings = embeddings + self.position_embedding(self.position_ids) + return embeddings + + +class SigLipAttention(nn.Module): + """Multi-headed attention from 'Attention Is All You Need' paper""" + + # Copied from transformers.models.clip.modeling_clip.CLIPAttention.__init__ + def __init__(self, config): + super().__init__() + self.config = config + self.embed_dim = config.hidden_size + self.num_heads = config.num_attention_heads + self.head_dim = self.embed_dim // self.num_heads + if self.head_dim * self.num_heads != self.embed_dim: + raise ValueError(f"embed_dim must be divisible by num_heads (got `embed_dim`: {self.embed_dim} and `num_heads`:" f" {self.num_heads}).") + self.scale = self.head_dim**-0.5 + self.dropout = config.attention_dropout + + self.k_proj = nn.Linear(self.embed_dim, self.embed_dim) + self.v_proj = nn.Linear(self.embed_dim, self.embed_dim) + self.q_proj = nn.Linear(self.embed_dim, self.embed_dim) + self.out_proj = nn.Linear(self.embed_dim, self.embed_dim) + + def forward( + self, + hidden_states: torch.Tensor, + attention_mask: Optional[torch.Tensor] = None, + output_attentions: Optional[bool] = False, + ) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]: + """Input shape: Batch x Time x Channel""" + + batch_size, q_len, _ = hidden_states.size() + + query_states = self.q_proj(hidden_states) + key_states = self.k_proj(hidden_states) + value_states = self.v_proj(hidden_states) + + query_states = query_states.view(batch_size, q_len, self.num_heads, self.head_dim).transpose(1, 2) + key_states = key_states.view(batch_size, q_len, self.num_heads, self.head_dim).transpose(1, 2) + value_states = value_states.view(batch_size, q_len, self.num_heads, self.head_dim).transpose(1, 2) + + k_v_seq_len = key_states.shape[-2] + attn_weights = torch.matmul(query_states, key_states.transpose(2, 3)) * self.scale + + if attn_weights.size() != (batch_size, self.num_heads, q_len, k_v_seq_len): + raise ValueError(f"Attention weights should be of size {(batch_size, self.num_heads, q_len, k_v_seq_len)}, but is" f" {attn_weights.size()}") + + if attention_mask is not None: + if attention_mask.size() != (batch_size, 1, q_len, k_v_seq_len): + raise ValueError(f"Attention mask should be of size {(batch_size, 1, q_len, k_v_seq_len)}, but is {attention_mask.size()}") + attn_weights = attn_weights + attention_mask + + # upcast attention to fp32 + attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query_states.dtype) + attn_weights = nn.functional.dropout(attn_weights, p=self.dropout, training=self.training) + attn_output = torch.matmul(attn_weights, value_states) + + if attn_output.size() != (batch_size, self.num_heads, q_len, self.head_dim): + raise ValueError(f"`attn_output` should be of size {(batch_size, self.num_heads, q_len, self.head_dim)}, but is" f" {attn_output.size()}") + + attn_output = attn_output.transpose(1, 2).contiguous() + attn_output = attn_output.reshape(batch_size, q_len, self.embed_dim) + + attn_output = self.out_proj(attn_output) + + return attn_output, attn_weights + + +# Copied from transformers.models.clip.modeling_clip.CLIPMLP with CLIP->SigLip +class SigLipMLP(nn.Module): + def __init__(self, config): + super().__init__() + self.config = config + self.activation_fn = ACT2FN[config.hidden_act] + self.fc1 = nn.Linear(config.hidden_size, config.intermediate_size) + self.fc2 = nn.Linear(config.intermediate_size, config.hidden_size) + + def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: + hidden_states = self.fc1(hidden_states) + hidden_states = self.activation_fn(hidden_states) + hidden_states = self.fc2(hidden_states) + return hidden_states + + +# Copied from transformers.models.clip.modeling_clip.CLIPEncoderLayer with CLIP->SigLip +class SigLipEncoderLayer(nn.Module): + def __init__(self, config: SigLipVisionConfig): + super().__init__() + self.embed_dim = config.hidden_size + self.self_attn = SigLipAttention(config) + self.layer_norm1 = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_eps) + self.mlp = SigLipMLP(config) + self.layer_norm2 = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_eps) + + # Ignore copy + def forward( + self, + hidden_states: torch.Tensor, + attention_mask: torch.Tensor, + output_attentions: Optional[bool] = False, + ) -> Tuple[torch.FloatTensor]: + """ + Args: + hidden_states (`torch.FloatTensor`): + Input to the layer of shape `(batch, seq_len, embed_dim)`. + attention_mask (`torch.FloatTensor`): + Attention mask of shape `(batch, 1, q_len, k_v_seq_len)` where padding elements are indicated by very large negative values. + output_attentions (`bool`, *optional*, defaults to `False`): + Whether or not to return the attentions tensors of all attention layers. See `attentions` under + returned tensors for more detail. + """ + residual = hidden_states + + hidden_states = self.layer_norm1(hidden_states) + hidden_states, attn_weights = self.self_attn( + hidden_states=hidden_states, + attention_mask=attention_mask, + output_attentions=output_attentions, + ) + hidden_states = residual + hidden_states + + residual = hidden_states + hidden_states = self.layer_norm2(hidden_states) + hidden_states = self.mlp(hidden_states) + hidden_states = residual + hidden_states + + outputs = (hidden_states,) + + if output_attentions: + outputs += (attn_weights,) + + return outputs + + +class SigLipPreTrainedModel(PreTrainedModel): + """ + An abstract class to handle weights initialization and a simple interface for downloading and loading pretrained + models. + """ + + config_class = SigLipVisionConfig + base_model_prefix = "siglip" + supports_gradient_checkpointing = True + + def _init_weights(self, module): + """Initialize the weights""" + pass + + +# Copied from transformers.models.clip.modeling_clip.CLIPEncoder with CLIP->SigLip +class SigLipEncoder(nn.Module): + """ + Transformer encoder consisting of `config.num_hidden_layers` self attention layers. Each layer is a + [`SigLipEncoderLayer`]. + + Args: + config: SigLipVisionConfig + """ + + def __init__(self, config: SigLipVisionConfig): + super().__init__() + self.config = config + self.layers = nn.ModuleList([SigLipEncoderLayer(config) for _ in range(config.num_hidden_layers)]) + self.gradient_checkpointing = False + + # Ignore copy + def forward( + self, + inputs_embeds, + attention_mask: Optional[torch.Tensor] = None, + output_attentions: Optional[bool] = None, + output_hidden_states: Optional[bool] = None, + return_dict: Optional[bool] = None, + ) -> Union[Tuple, BaseModelOutput]: + r""" + Args: + inputs_embeds (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`): + Optionally, instead of passing `input_ids` you can choose to directly pass an embedded representation. + This is useful if you want more control over how to convert `input_ids` indices into associated vectors + than the model's internal embedding lookup matrix. + attention_mask (`torch.Tensor` of shape `(batch_size, sequence_length)`, *optional*): + Mask to avoid performing attention on padding token indices. Mask values selected in `[0, 1]`: + + - 1 for tokens that are **not masked**, + - 0 for tokens that are **masked**. + + [What are attention masks?](../glossary#attention-mask) + output_attentions (`bool`, *optional*): + Whether or not to return the attentions tensors of all attention layers. See `attentions` under + returned tensors for more detail. + output_hidden_states (`bool`, *optional*): + Whether or not to return the hidden states of all layers. See `hidden_states` under returned tensors + for more detail. + return_dict (`bool`, *optional*): + Whether or not to return a [`~utils.ModelOutput`] instead of a plain tuple. + """ + output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions + output_hidden_states = output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + + encoder_states = () if output_hidden_states else None + all_attentions = () if output_attentions else None + + hidden_states = inputs_embeds + for encoder_layer in self.layers: + if output_hidden_states: + encoder_states = encoder_states + (hidden_states,) + if self.gradient_checkpointing and self.training: + layer_outputs = self._gradient_checkpointing_func( + encoder_layer.__call__, + hidden_states, + attention_mask, + output_attentions, + ) + else: + layer_outputs = encoder_layer( + hidden_states, + attention_mask, + output_attentions=output_attentions, + ) + + hidden_states = layer_outputs[0] + + if output_attentions: + all_attentions = all_attentions + (layer_outputs[1],) + + if output_hidden_states: + encoder_states = encoder_states + (hidden_states,) + + if not return_dict: + return tuple(v for v in [hidden_states, encoder_states, all_attentions] if v is not None) + return BaseModelOutput(last_hidden_state=hidden_states, hidden_states=encoder_states, attentions=all_attentions) + + +class SigLipVisionTransformer(nn.Module): + def __init__(self, config: SigLipVisionConfig): + super().__init__() + self.config = config + embed_dim = config.hidden_size + + self.embeddings = SigLipVisionEmbeddings(config) + self.encoder = SigLipEncoder(config) + self.post_layernorm = nn.LayerNorm(embed_dim, eps=config.layer_norm_eps) + self.head = SigLipMultiheadAttentionPoolingHead(config) + + def forward( + self, + pixel_values, + output_attentions: Optional[bool] = None, + output_hidden_states: Optional[bool] = None, + return_dict: Optional[bool] = None, + ) -> Union[Tuple, BaseModelOutputWithPooling]: + r""" + Returns: + + """ + output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions + output_hidden_states = output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + + hidden_states = self.embeddings(pixel_values) + + encoder_outputs = self.encoder( + inputs_embeds=hidden_states, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + ) + + last_hidden_state = encoder_outputs[0] + last_hidden_state = self.post_layernorm(last_hidden_state) + + pooled_output = self.head(last_hidden_state) + + if not return_dict: + return (last_hidden_state, pooled_output) + encoder_outputs[1:] + + return BaseModelOutputWithPooling( + last_hidden_state=last_hidden_state, + pooler_output=pooled_output, + hidden_states=encoder_outputs.hidden_states, + attentions=encoder_outputs.attentions, + ) + + +class SigLipMultiheadAttentionPoolingHead(nn.Module): + """Multihead Attention Pooling.""" + + def __init__(self, config: SigLipVisionConfig): + super().__init__() + + self.probe = nn.Parameter(torch.randn(1, 1, config.hidden_size)) + self.attention = torch.nn.MultiheadAttention(config.hidden_size, config.num_attention_heads, batch_first=True) + self.layernorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.mlp = SigLipMLP(config) + + def forward(self, hidden_state): + batch_size = hidden_state.shape[0] + probe = self.probe.repeat(batch_size, 1, 1) + + hidden_state = self.attention(probe, hidden_state, hidden_state)[0] + + residual = hidden_state + hidden_state = self.layernorm(hidden_state) + hidden_state = residual + self.mlp(hidden_state) + + return hidden_state[:, 0] + + +class SigLipVisionModel(SigLipPreTrainedModel): + config_class = SigLipVisionConfig + main_input_name = "pixel_values" + _no_split_modules = ["SigLipEncoderLayer"] + + def __init__(self, config: SigLipVisionConfig): + super().__init__(config) + + self.vision_model = SigLipVisionTransformer(config) + + # Initialize weights and apply final processing + self.post_init() + + def get_input_embeddings(self) -> nn.Module: + return self.vision_model.embeddings.patch_embedding + + def forward( + self, + pixel_values, + output_attentions: Optional[bool] = None, + output_hidden_states: Optional[bool] = None, + return_dict: Optional[bool] = None, + ) -> Union[Tuple, BaseModelOutputWithPooling]: + r""" + Returns: + + Examples: + + ```python + >>> from PIL import Image + >>> import requests + >>> from transformers import AutoProcessor, SigLipVisionModel + + >>> model = SigLipVisionModel.from_pretrained("google/siglip-base-patch16-224") + >>> processor = AutoProcessor.from_pretrained("google/siglip-base-patch16-224") + + >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg" + >>> image = Image.open(requests.get(url, stream=True).raw) + + >>> inputs = processor(images=image, return_tensors="pt") + + >>> outputs = model(**inputs) + >>> last_hidden_state = outputs.last_hidden_state + >>> pooled_output = outputs.pooler_output # pooled features + ```""" + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + + return self.vision_model( + pixel_values=pixel_values, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + ) + + +class SigLipVisionTower(nn.Module): + def __init__(self, vision_tower, vision_tower_cfg, delay_load=False): + super().__init__() + + self.is_loaded = False + + self.config = SigLipVisionConfig() + + self.vision_tower_name = vision_tower + + self.image_processor = SigLipImageProcessor() + + if not delay_load: + rank0_print(f"Loading vision tower: {vision_tower}") + self.load_model() + elif getattr(vision_tower_cfg, "unfreeze_mm_vision_tower", False): + # TODO: better detector is needed. + rank0_print(f"The checkpoint seems to contain `vision_tower` weights: `unfreeze_mm_vision_tower`: True.") + self.load_model() + elif hasattr(vision_tower_cfg, "mm_tunable_parts") and "mm_vision_tower" in vision_tower_cfg.mm_tunable_parts: + rank0_print(f"The checkpoint seems to contain `vision_tower` weights: `mm_tunable_parts` contains `mm_vision_tower`.") + self.load_model() + else: + self.cfg_only = self.config + + def load_model(self, device_map=None): + if self.is_loaded: + rank0_print("{} is already loaded, `load_model` called again, skipping.".format(self.vision_tower_name)) + return + + self.vision_tower = SigLipVisionModel.from_pretrained(self.vision_tower_name, device_map=device_map) + + del self.vision_tower.vision_model.encoder.layers[-1:] + self.vision_tower.vision_model.head = nn.Identity() + self.vision_tower.requires_grad_(False) + + self.is_loaded = True + + def forward(self, images): + if type(images) is list: + image_features = [] + for image in images: + image_forward_out = self.vision_tower(image.to(device=self.device, dtype=self.dtype).unsqueeze(0), output_hidden_states=True) + image_feature = image_forward_out.hidden_states[-1].to(image.dtype) + assert image_features.shape[-2] == 729 + image_features.append(image_feature) + else: + image_forward_outs = self.vision_tower(images.to(device=self.device, dtype=self.dtype), output_hidden_states=True) + image_features = image_forward_outs.hidden_states[-1].to(images.dtype) + assert image_features.shape[-2] == 729 + + return image_features + + @property + def dummy_feature(self): + return torch.zeros(1, self.hidden_size, device=self.device, dtype=self.dtype) + + @property + def dtype(self): + for p in self.vision_tower.parameters(): + return p.dtype + + @property + def device(self): + for p in self.vision_tower.parameters(): + return p.device + + @property + def hidden_size(self): + return self.config.hidden_size + + @property + def num_patches(self): + return (self.config.image_size // self.config.patch_size) ** 2 + + @property + def num_patches_per_side(self): + return self.config.image_size // self.config.patch_size + # return self.model_config["vision_cfg"]["image_size"] // self.model_config["vision_cfg"]["patch_size"] + + @property + def image_size(self): + return self.config.image_size diff --git a/llava/model/multimodal_projector/__pycache__/builder.cpython-310.pyc b/llava/model/multimodal_projector/__pycache__/builder.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0b61dbefee8acf6134ebdf706176dc048a276c69 GIT binary patch literal 2378 zcmb_eOK;mo5Z+xrMazn#yi=rgfgHjXiFzn{2#TPuBq-`QLEYp)v>{mSTBc2r%5s(1 z0)43r`yjdQe3hIcED`&m+!ASC}h?8%}~y?((( zuNaB^d6wMFvgCQv+t^5+e0g(I?5kca7=ljL8;&!T0-0p-=s5P?&*L=XLf#r3NDmxp zp=an~hfdBn!nk$pO&=GJA1olo^bkggOa{benF}%b}08$&;aoV;#gXWOSS%-HzjD<0PwBOg80N*h>45ZK$X^ zkmr$IT@JpukRV5Qw$kA! z6W@#StE}knohm987>yf{-T`6>QpILgMd!34u?@5X!>uSt7*O7vEcv+wIzEEhz#(i& z7j`m9(Jaq(Ya=-jaX!-$+WwX<|2{WzJGV)v9=MgA*fEF{(CUY$p|Q%sEcnnv%q!eN_1OZ#OV@F7>NOoyn>Vo(M(H> zT!jV1lL;0vvgS&Z-8)rG)W^-fL(QjfRu#mCCuM3KS`{?bJtn(~&R9iup=7)EoNW=1 zTOhxtoQhP8+gHdGq{^Ln+^MWNJG3iX{-FHIp0&6;r&D_9oZe$V4_y^-w{oWJ8}ifn zL$7jYE1=V!lR2ei!J$>Vwu;hcw-w!8t=wNB)5e--tofBM=U~lmSgSeT*lztVw)a&~ zwWbuj4Hn*Z(6cF29z6SP=^13F1=;yiotdJaYdol|-^m|rN`Il@%BJi>s0*R+=mA z3}i7LX-6jcKxj7^!PUa$I_~&BPKYtnh;;BI$;RTYl!Xj~y0-;z&m;$NRwP`mfnwA- zanp3+r~{#!pkre)Y9Y_XNbP8^ZltBAadhEC4Kk4nb*v3st=%{h<_fuJ?y-20`yISY&5wa;`rupF>b5)We*q!$ BC!YWS literal 0 HcmV?d00001 diff --git a/llava/model/multimodal_projector/__pycache__/pooler_projector.cpython-310.pyc b/llava/model/multimodal_projector/__pycache__/pooler_projector.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e9e28a73ca86404177d5e73e47fae857e34149cc GIT binary patch literal 1445 zcmZ8h&2Aev5GJ|Y)k>D^Bxs$uEl{9`0xA%x$Tmd(lM7l5D&fdq*v&n?Ql101QQs}kTaV3cD|v?Uax~-kstq@{t+Pbmp|+; z3Wx7N%rn?H;#i@i(Ksgw!Hpdx0r-fbd6Fkv>vMgSdT&T=D_7^~@m-!ZyKndn3A@9%xbIi$Y4CU$q2^Ap^d31|za1y;k z6eTSv)N)5>WvPTd)8$-bw$#}!q0j2=)(34~gJIDPTH_7EXoXjZkrkN-AoMc?r_e_8 z)(X#KkT#SD0KZeX@7QR40Mgwc*n8gVWkWUuyoVeCkYKzekiSpd!MV_dP|V0H;UZ(T zFiY%^I7`E*vQe7oC>H@(Fi`dCq2% zb5S(@gZ)Drk}X*Uy>OkE;>|)7R;J3)=Rb^ob-+dEbTA@=7wb@ils^)E0r$O;b@e;`u_1)ytTvTBKPbL^M$e! z%v3R7t7c^AQ3?I=Vo+V`7@Fnh(cJR}j_@IVL@4pP^Uu|zJERVFUCVrvcPj$J%P$~C z!L~wk7>sLr9j@pq`~*0j1B)lu9Q{#zp5Bjh+cPomm%NNN#8Pa|mj- zFPe0vhB)qjYYt)S;A8w1?%)R?6kk2O+t5i9pLz7VC^eVF}XUvi=&yre(WDck>&y(Mu#z&poQ{wm$5gC zR5hM@X!JLb$;AK#`e`rg3a0jc?_ TsfwRAU-!(n`y(3CCC9;k!8m#8 literal 0 HcmV?d00001 diff --git a/llava/model/multimodal_projector/builder.py b/llava/model/multimodal_projector/builder.py new file mode 100644 index 0000000..3122a0c --- /dev/null +++ b/llava/model/multimodal_projector/builder.py @@ -0,0 +1,65 @@ +import torch +import torch.nn as nn +import re + +from .pooler_projector import PoolerProjector + + +class IdentityMap(nn.Module): + def __init__(self): + super().__init__() + + def forward(self, x, *args, **kwargs): + return x + + @property + def config(self): + return {"mm_projector_type": "identity"} + + +class SimpleResBlock(nn.Module): + def __init__(self, channels): + super().__init__() + self.pre_norm = nn.LayerNorm(channels) + + self.proj = nn.Sequential(nn.Linear(channels, channels), nn.GELU(), nn.Linear(channels, channels)) + + def forward(self, x): + x = self.pre_norm(x) + return x + self.proj(x) + + +def build_vision_projector(config, delay_load=False, **kwargs): + projector_type = getattr(config, "mm_projector_type", "linear") + + if projector_type == "linear": + return nn.Linear(config.mm_hidden_size, config.hidden_size) + + if projector_type == "pooler": + return PoolerProjector(config, kwargs["vision_cfg"]) + + mlp_gelu_match = re.match(r"^mlp(\d+)x_gelu$", projector_type) + if mlp_gelu_match: + mlp_depth = int(mlp_gelu_match.group(1)) + modules = [nn.Linear(config.mm_hidden_size, config.hidden_size)] + for _ in range(1, mlp_depth): + modules.append(nn.GELU()) + modules.append(nn.Linear(config.hidden_size, config.hidden_size)) + return nn.Sequential(*modules) + + mlp_gelu_resnet_match = re.match(r"^mlp(\d+)x_res(\d+)x_gelu$", projector_type) + if mlp_gelu_resnet_match: + mlp_depth = int(mlp_gelu_resnet_match.group(1)) + res_depth = int(mlp_gelu_resnet_match.group(2)) + modules = [nn.Linear(config.mm_hidden_size, config.hidden_size)] + for _ in range(1, mlp_depth): + modules.append(nn.GELU()) + modules.append(nn.Linear(config.hidden_size, config.hidden_size)) + for _ in range(res_depth): + modules.append(SimpleResBlock(config.hidden_size)) + return nn.Sequential(*modules) + + if projector_type == "identity": + return IdentityMap() + + raise ValueError(f"Unknown projector type: {projector_type}") diff --git a/llava/model/multimodal_projector/pooler_projector.py b/llava/model/multimodal_projector/pooler_projector.py new file mode 100644 index 0000000..ce5a2e0 --- /dev/null +++ b/llava/model/multimodal_projector/pooler_projector.py @@ -0,0 +1,33 @@ +import torch +import torch.nn as nn + +import math + +from transformers.models.clip.modeling_clip import CLIPVisionModel + + +class PoolerProjector(nn.Module): + def __init__(self, config, vision_cfg): + super().__init__() + self._config = config + self.hw = vision_cfg.image_size // vision_cfg.patch_size + + self.conv_pool = nn.Conv2d(config.mm_hidden_size, config.hidden_size, kernel_size=2, stride=2) + + self.proj = nn.Sequential( + nn.GELU(), + nn.Linear(config.hidden_size, config.hidden_size), + ) + + def forward(self, x, *args, **kwargs): + height = width = self.hw + assert height * width == x.shape[1] + x = x.view(x.shape[0], height, width, -1).permute(0, 3, 1, 2) + x = self.conv_pool(x) + x = x.flatten(2).transpose(1, 2) + x = self.proj(x) + return x + + @property + def config(self): + return {"mm_projector_type": "pooler"} diff --git a/llava/model/multimodal_resampler/__pycache__/builder.cpython-310.pyc b/llava/model/multimodal_resampler/__pycache__/builder.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..781dc9adb8a87f95de062e83d184a9005a22b57a GIT binary patch literal 1426 zcmZuxNskma6t-RK^aczQgrY2Sh$7Vi8gT=mC@9H9QJ98g6b^N*! zqI65#@&}?wAmzee@_`E{4*3H)KGsQ@<|!rL6Pd_#MP#bT#+T^Fh+LU0yhc*|a z4bJH!(BHJ-IX#&XbIV3cz^aX9ya@gzf3YR~oo30zd<1d0e(A@MOGw@g+HMMY17qm6 zpc>r}y5vbWd8b!@sgA4qX|?+Skh|Ed+q*DD$&s?cT4Qip45n)$Bry`KmFqnedZxZJ zrZ!pVdLHH61FZ*m7jv#_A*g(S>2Cn!q;3q{})YTbsFs1e?5a@;fE&^Te zn2wnF0*o2m%Z4tpwV6ILpMau^_*3M3*q^xAQnG%77>(ZT`AQWFaf6{;!(BETgzNEJOSRI6oY?5w@sb!XS9 zW34YFRZsm304Y9j=#9T;4qQ0(U+4win{^VWpsqFZ=Hq?Ly!U%=oNsJ+1U~l7UxPoI zg#3+@_0NRK7to4Nq2q+plms=UV-_%4kBz|imT<<6Q^F0whUSS8Se!m09rHEFkzS1b z`e&>r#b+=OB%quG>;>slPB}Yu1A`mfJSBlCEN*f8l$_AO<_>pZ&3Qpg61bqfp}dDt zF%o=V=98GM8Ne|%v{T%Jt|TYqj4lZ!6&Hu|tHh_9So1B0J+UvBG zRZJ&BDmM(1EGffKZH>p_7z~FT?1tl<3uWs5_LYrdluSa9j73&P1EDtGUf90EE~7HZ zl~t3Q|J@3w6Ff#!Gpr{6$Cl>LF4^>?!<1Qww68ooWg13uP`sk55r%OZ6-5}nB5xjb zWuBMa7#ON1z&uSMtAF9WxC?$;|3L@{jNX>m$OMTVq+Xg$IaE!i1cl94If zxH2FMv!eUtNwBagYiMwKi!30jLzA-+llMxi!kN7TI9fQBdFE8)%&n}m2B%9}IZK8T z*p`%%r`uZ60LjNF3AO+c!zQ%WlD%8Un!E|}Lfe8|;MQ|?uD#sg`fR_F-`NTMg=%Zg z5tXMU+~$Oqn^RgW^s0MAVD;K?t1>}H<0XT8nt)5MC&48|S^Z=tID#evfVpv6gn5=8 zzsAk^ogXqWn~1m++^+-UBVc`u-+jMBOfJ7iD?{Sn) z#n)2iQkmuP1kxu>ic(o+F5^QmRYvisXatc6GCZbo_qi0Bi%@yJ< zwl}V@D=(Xl!!jRF)`q?L_8N#+Dq{5f`G;c%+E>tu+t6{0wZ<<{s6snsLXpyK>Hn4-5P2>i29ZQEs$4rMtds_P(B8-lF`_gutXZ>Wb3tI(- zh8b|*)z5x2-&(s{%^9B;ZD1xCN5qG|+Mh6hvPph27RJI{SS6NNqk@#gRD5YHY*;ZX z8=nY(u%lnDZJ^Gu*LqBWQ0x!x>K3UMhtyYK(lyXzm^_4l9L^$P*H zlf~jfd50>Si=!kK@+SCN-h!_8`XBf@Oui2jWhEIGGhGxq4S5Hd5Xj2Jdq{k}m9sj6 zvIkLlD5U%lS8XXKkXp)wa~7ReopW@i+*D*bSrqLldZ6gOvh<-Pz$j#{ z2wdF31Pk1mE(K7UOoa4X*poh3#+Sdq^lRDMtZlUEj^RVY^zzvJ)*Ae-))sj6;U&(Z zv4EG@3-S4#rZ{ee;nQi9);s#?k$a$Cxf94HA+P|(YS$kQgSe8l5ab-W$C(>+dnlHes literal 0 HcmV?d00001 diff --git a/llava/model/multimodal_resampler/__pycache__/perceiver.cpython-310.pyc b/llava/model/multimodal_resampler/__pycache__/perceiver.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3456d81c63851baa540e681f35cb60bf92968f8a GIT binary patch literal 4837 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 random.random(): + return image_features + + masked_features = [] + + for image_feature in image_features: + num_tokens = image_feature.shape[0] + if self.mode == "fixed": + num_keep = int(num_tokens * self.ratio) + masked_features.append(self.random_masking(image_feature.unsqueeze(0), num_keep)[0][0]) + elif self.mode == "range": + num_keep = int(num_tokens * random.uniform(self.ratio_lower, self.ratio_upper)) + masked_features.append(self.random_masking(image_feature.unsqueeze(0), num_keep)[0]) + elif self.mode == "cls_only": + masked_features.append(image_feature[0:1]) + else: + raise ValueError(f"Unexpected masked drop mode: {self.mode}") + + if self.mode not in ["range"] and (type(image_features) is not list or self.mode in ["cls_only"]): + masked_features = torch.stack(masked_features, dim=0) + + return masked_features + + @property + def config(self): + return { + "mm_resampler_type": "masked_drop", + "mm_mask_drop_mode": self.mode, + "mm_mask_drop_skip_percentage": self.skip_percentage, + "mm_mask_drop_ratio": self.ratio, + "mm_mask_drop_ratio_upper": self.ratio_upper, + "mm_mask_drop_ratio_lower": self.ratio_lower, + } + + def random_masking(self, x, len_keep): + """ + Perform per-sample random masking by per-sample shuffling. + Per-sample shuffling is done by argsort random noise. + x: [N, L, D], sequence + """ + N, L, D = x.shape # batch, length, dim + + noise = torch.rand(N, L, device=x.device) # noise in [0, 1] + + # sort noise for each sample + ids_shuffle = torch.argsort(noise, dim=1) # ascend: small is keep, large is remove + ids_restore = torch.argsort(ids_shuffle, dim=1) + + # keep the first subset + ids_keep = ids_shuffle[:, :len_keep] + x_masked = torch.gather(x, dim=1, index=ids_keep.unsqueeze(-1).repeat(1, 1, D)) + + # generate the binary mask: 0 is keep, 1 is remove + mask = torch.ones([N, L], device=x.device) + mask[:, :len_keep] = 0 + # unshuffle to get the binary mask + mask = torch.gather(mask, dim=1, index=ids_restore) + + return x_masked, mask, ids_restore diff --git a/llava/model/multimodal_resampler/perceiver.py b/llava/model/multimodal_resampler/perceiver.py new file mode 100644 index 0000000..d6b17a5 --- /dev/null +++ b/llava/model/multimodal_resampler/perceiver.py @@ -0,0 +1,155 @@ +""" +Taken from https://github.com/lucidrains/flamingo-pytorch +""" + +import torch +from einops import rearrange, repeat + +try: + from einops_exts import rearrange_many +except: + pass + +from torch import einsum, nn + + +def exists(val): + return val is not None + + +def FeedForward(dim, mult=4): + inner_dim = int(dim * mult) + return nn.Sequential( + nn.LayerNorm(dim), + nn.Linear(dim, inner_dim, bias=False), + nn.GELU(), + nn.Linear(inner_dim, dim, bias=False), + ) + + +class PerceiverAttention(nn.Module): + def __init__(self, *, dim, dim_head=64, heads=8): + super().__init__() + self.scale = dim_head**-0.5 + self.heads = heads + inner_dim = dim_head * heads + + self.norm_media = nn.LayerNorm(dim) + self.norm_latents = nn.LayerNorm(dim) + + self.to_q = nn.Linear(dim, inner_dim, bias=False) + self.to_kv = nn.Linear(dim, inner_dim * 2, bias=False) + self.to_out = nn.Linear(inner_dim, dim, bias=False) + + def forward(self, x, latents): + """ + Args: + x (torch.Tensor): image features + shape (b, T, n1, D) + latent (torch.Tensor): latent features + shape (b, T, n2, D) + """ + x = self.norm_media(x) + latents = self.norm_latents(latents) + + h = self.heads + + q = self.to_q(latents) + kv_input = torch.cat((x, latents), dim=-2) + k, v = self.to_kv(kv_input).chunk(2, dim=-1) + q, k, v = rearrange_many((q, k, v), "b t n (h d) -> b h t n d", h=h) + q = q * self.scale + + # attention + sim = einsum("... i d, ... j d -> ... i j", q, k) + sim = sim - sim.amax(dim=-1, keepdim=True).detach() + attn = sim.softmax(dim=-1) + + out = einsum("... i j, ... j d -> ... i d", attn, v) + out = rearrange(out, "b h t n d -> b t n (h d)", h=h) + return self.to_out(out) + + +class PerceiverResamplerModule(nn.Module): + def __init__( + self, + *, + dim, + depth=6, + dim_head=64, + heads=8, + num_latents=64, + max_num_media=None, + max_num_frames=None, + ff_mult=4, + ): + super().__init__() + self.latents = nn.Parameter(torch.randn(num_latents, dim)) + self.frame_embs = nn.Parameter(torch.randn(max_num_frames, dim)) if exists(max_num_frames) else None + self.media_time_embs = nn.Parameter(torch.randn(max_num_media, 1, dim)) if exists(max_num_media) else None + + self.layers = nn.ModuleList([]) + for _ in range(depth): + self.layers.append( + nn.ModuleList( + [ + PerceiverAttention(dim=dim, dim_head=dim_head, heads=heads), + FeedForward(dim=dim, mult=ff_mult) if ff_mult > 0 else nn.Identity(), + ] + ) + ) + + self.norm = nn.LayerNorm(dim) + + def forward(self, x): + """ + Args: + x (torch.Tensor): image features + shape (b, T, F, v, D) + Returns: + shape (b, T, n, D) where n is self.num_latents + """ + b, T, F, v = x.shape[:4] + + # frame and media time embeddings + if exists(self.frame_embs): + frame_embs = repeat(self.frame_embs[:F], "F d -> b T F v d", b=b, T=T, v=v) + x = x + frame_embs + x = rearrange(x, "b T F v d -> b T (F v) d") # flatten the frame and spatial dimensions + if exists(self.media_time_embs): + x = x + self.media_time_embs[:T] + + # blocks + latents = repeat(self.latents, "n d -> b T n d", b=b, T=T) + for attn, ff in self.layers: + latents = attn(x, latents) + latents + latents = ff(latents) + latents + return self.norm(latents) + + +class PerceiverResampler(nn.Module): + def __init__(self, model_args, vision_tower): + super().__init__() + + self.depth = model_args.mm_perceiver_depth + self.num_latents = model_args.mm_perceiver_latents + self.ff_mult = model_args.mm_perceiver_ff_mult + self.pretrained = model_args.mm_perceiver_pretrained + + self.perceiver = PerceiverResamplerModule(dim=vision_tower.hidden_size, depth=self.depth, num_latents=self.num_latents, ff_mult=self.ff_mult) + + if self.pretrained is not None: + self.load_state_dict(torch.load(self.pretrained)) + + def forward(self, image_features, *args, **kwargs): + return self.perceiver(image_features[:, None, None]).squeeze(1) + + @property + def config(self): + return { + "mm_resampler_type": "perceiver", + "mm_perceiver_depth": self.depth, + "mm_perceiver_latents": self.num_latents, + "mm_perceiver_ff_mult": self.ff_mult, + "mm_perceiver_pretrained": self.pretrained, + } diff --git a/llava/model/multimodal_resampler/qformer.py b/llava/model/multimodal_resampler/qformer.py new file mode 100644 index 0000000..b86754c --- /dev/null +++ b/llava/model/multimodal_resampler/qformer.py @@ -0,0 +1,1160 @@ +""" + * Copyright (c) 2023, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li + * Based on huggingface code base + * https://github.com/huggingface/transformers/blob/v4.15.0/src/transformers/models/bert +""" + +import math +import os +import warnings +from dataclasses import dataclass +from typing import Optional, Tuple, Dict, Any + +import torch +from torch import Tensor, device, dtype, nn +import torch.utils.checkpoint +from torch import nn +from torch.nn import CrossEntropyLoss +import torch.nn.functional as F + +from transformers.activations import ACT2FN +from transformers.file_utils import ( + ModelOutput, +) +from transformers.modeling_outputs import ( + BaseModelOutputWithPastAndCrossAttentions, + BaseModelOutputWithPoolingAndCrossAttentions, + CausalLMOutputWithCrossAttentions, + MaskedLMOutput, + MultipleChoiceModelOutput, + NextSentencePredictorOutput, + QuestionAnsweringModelOutput, + SequenceClassifierOutput, + TokenClassifierOutput, +) +from transformers.modeling_utils import ( + PreTrainedModel, + apply_chunking_to_forward, + find_pruneable_heads_and_indices, + prune_linear_layer, +) +from transformers.utils import logging +from transformers.models.bert.configuration_bert import BertConfig + +logger = logging.get_logger(__name__) + + +def disabled_train(self, mode=True): + """Overwrite model.train with this function to make sure train/eval mode + does not change anymore.""" + return self + + +class BertEmbeddings(nn.Module): + """Construct the embeddings from word and position embeddings.""" + + def __init__(self, config): + super().__init__() + self.word_embeddings = nn.Embedding(config.vocab_size, config.hidden_size, padding_idx=config.pad_token_id) + self.position_embeddings = nn.Embedding(config.max_position_embeddings, config.hidden_size) + + # self.LayerNorm is not snake-cased to stick with TensorFlow model variable name and be able to load + # any TensorFlow checkpoint file + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + # position_ids (1, len position emb) is contiguous in memory and exported when serialized + self.register_buffer("position_ids", torch.arange(config.max_position_embeddings).expand((1, -1))) + self.position_embedding_type = getattr(config, "position_embedding_type", "absolute") + + self.config = config + + def forward( + self, + input_ids=None, + position_ids=None, + query_embeds=None, + past_key_values_length=0, + ): + if input_ids is not None: + seq_length = input_ids.size()[1] + else: + seq_length = 0 + + if position_ids is None: + position_ids = self.position_ids[:, past_key_values_length : seq_length + past_key_values_length].clone() + + if input_ids is not None: + embeddings = self.word_embeddings(input_ids) + if self.position_embedding_type == "absolute": + position_embeddings = self.position_embeddings(position_ids) + embeddings = embeddings + position_embeddings + + if query_embeds is not None: + embeddings = torch.cat((query_embeds, embeddings), dim=1) + else: + embeddings = query_embeds + + embeddings = self.LayerNorm(embeddings) + embeddings = self.dropout(embeddings) + return embeddings + + +class BertSelfAttention(nn.Module): + def __init__(self, config, is_cross_attention): + super().__init__() + self.config = config + if config.hidden_size % config.num_attention_heads != 0 and not hasattr(config, "embedding_size"): + raise ValueError("The hidden size (%d) is not a multiple of the number of attention " "heads (%d)" % (config.hidden_size, config.num_attention_heads)) + + self.num_attention_heads = config.num_attention_heads + self.attention_head_size = int(config.hidden_size / config.num_attention_heads) + self.all_head_size = self.num_attention_heads * self.attention_head_size + + self.query = nn.Linear(config.hidden_size, self.all_head_size) + if is_cross_attention: + self.key = nn.Linear(config.encoder_width, self.all_head_size) + self.value = nn.Linear(config.encoder_width, self.all_head_size) + else: + self.key = nn.Linear(config.hidden_size, self.all_head_size) + self.value = nn.Linear(config.hidden_size, self.all_head_size) + + self.dropout = nn.Dropout(config.attention_probs_dropout_prob) + self.position_embedding_type = getattr(config, "position_embedding_type", "absolute") + if self.position_embedding_type == "relative_key" or self.position_embedding_type == "relative_key_query": + self.max_position_embeddings = config.max_position_embeddings + self.distance_embedding = nn.Embedding(2 * config.max_position_embeddings - 1, self.attention_head_size) + self.save_attention = False + + def save_attn_gradients(self, attn_gradients): + self.attn_gradients = attn_gradients + + def get_attn_gradients(self): + return self.attn_gradients + + def save_attention_map(self, attention_map): + self.attention_map = attention_map + + def get_attention_map(self): + return self.attention_map + + def transpose_for_scores(self, x): + new_x_shape = x.size()[:-1] + ( + self.num_attention_heads, + self.attention_head_size, + ) + x = x.view(*new_x_shape) + return x.permute(0, 2, 1, 3) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + ): + + # If this is instantiated as a cross-attention module, the keys + # and values come from an encoder; the attention mask needs to be + # such that the encoder's padding tokens are not attended to. + is_cross_attention = encoder_hidden_states is not None + + if is_cross_attention: + key_layer = self.transpose_for_scores(self.key(encoder_hidden_states)) + value_layer = self.transpose_for_scores(self.value(encoder_hidden_states)) + attention_mask = encoder_attention_mask + elif past_key_value is not None: + key_layer = self.transpose_for_scores(self.key(hidden_states)) + value_layer = self.transpose_for_scores(self.value(hidden_states)) + key_layer = torch.cat([past_key_value[0], key_layer], dim=2) + value_layer = torch.cat([past_key_value[1], value_layer], dim=2) + else: + key_layer = self.transpose_for_scores(self.key(hidden_states)) + value_layer = self.transpose_for_scores(self.value(hidden_states)) + + mixed_query_layer = self.query(hidden_states) + + query_layer = self.transpose_for_scores(mixed_query_layer) + + past_key_value = (key_layer, value_layer) + + # Take the dot product between "query" and "key" to get the raw attention scores. + attention_scores = torch.matmul(query_layer, key_layer.transpose(-1, -2)) + + if self.position_embedding_type == "relative_key" or self.position_embedding_type == "relative_key_query": + seq_length = hidden_states.size()[1] + position_ids_l = torch.arange(seq_length, dtype=torch.long, device=hidden_states.device).view(-1, 1) + position_ids_r = torch.arange(seq_length, dtype=torch.long, device=hidden_states.device).view(1, -1) + distance = position_ids_l - position_ids_r + positional_embedding = self.distance_embedding(distance + self.max_position_embeddings - 1) + positional_embedding = positional_embedding.to(dtype=query_layer.dtype) # fp16 compatibility + + if self.position_embedding_type == "relative_key": + relative_position_scores = torch.einsum("bhld,lrd->bhlr", query_layer, positional_embedding) + attention_scores = attention_scores + relative_position_scores + elif self.position_embedding_type == "relative_key_query": + relative_position_scores_query = torch.einsum("bhld,lrd->bhlr", query_layer, positional_embedding) + relative_position_scores_key = torch.einsum("bhrd,lrd->bhlr", key_layer, positional_embedding) + attention_scores = attention_scores + relative_position_scores_query + relative_position_scores_key + + attention_scores = attention_scores / math.sqrt(self.attention_head_size) + if attention_mask is not None: + # Apply the attention mask is (precomputed for all layers in BertModel forward() function) + attention_scores = attention_scores + attention_mask + + # Normalize the attention scores to probabilities. + attention_probs = nn.Softmax(dim=-1)(attention_scores) + + if is_cross_attention and self.save_attention: + self.save_attention_map(attention_probs) + attention_probs.register_hook(self.save_attn_gradients) + + # This is actually dropping out entire tokens to attend to, which might + # seem a bit unusual, but is taken from the original Transformer paper. + attention_probs_dropped = self.dropout(attention_probs) + + # Mask heads if we want to + if head_mask is not None: + attention_probs_dropped = attention_probs_dropped * head_mask + + context_layer = torch.matmul(attention_probs_dropped, value_layer) + + context_layer = context_layer.permute(0, 2, 1, 3).contiguous() + new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,) + context_layer = context_layer.view(*new_context_layer_shape) + + outputs = (context_layer, attention_probs) if output_attentions else (context_layer,) + + outputs = outputs + (past_key_value,) + return outputs + + +class BertSelfOutput(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + def forward(self, hidden_states, input_tensor): + hidden_states = self.dense(hidden_states) + hidden_states = self.dropout(hidden_states) + hidden_states = self.LayerNorm(hidden_states + input_tensor) + return hidden_states + + +class BertAttention(nn.Module): + def __init__(self, config, is_cross_attention=False): + super().__init__() + self.self = BertSelfAttention(config, is_cross_attention) + self.output = BertSelfOutput(config) + self.pruned_heads = set() + + def prune_heads(self, heads): + if len(heads) == 0: + return + heads, index = find_pruneable_heads_and_indices( + heads, + self.self.num_attention_heads, + self.self.attention_head_size, + self.pruned_heads, + ) + + # Prune linear layers + self.self.query = prune_linear_layer(self.self.query, index) + self.self.key = prune_linear_layer(self.self.key, index) + self.self.value = prune_linear_layer(self.self.value, index) + self.output.dense = prune_linear_layer(self.output.dense, index, dim=1) + + # Update hyper params and store pruned heads + self.self.num_attention_heads = self.self.num_attention_heads - len(heads) + self.self.all_head_size = self.self.attention_head_size * self.self.num_attention_heads + self.pruned_heads = self.pruned_heads.union(heads) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + ): + self_outputs = self.self( + hidden_states, + attention_mask, + head_mask, + encoder_hidden_states, + encoder_attention_mask, + past_key_value, + output_attentions, + ) + attention_output = self.output(self_outputs[0], hidden_states) + + outputs = (attention_output,) + self_outputs[1:] # add attentions if we output them + return outputs + + +class BertIntermediate(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.intermediate_size) + if isinstance(config.hidden_act, str): + self.intermediate_act_fn = ACT2FN[config.hidden_act] + else: + self.intermediate_act_fn = config.hidden_act + + def forward(self, hidden_states): + hidden_states = self.dense(hidden_states) + hidden_states = self.intermediate_act_fn(hidden_states) + return hidden_states + + +class BertOutput(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.intermediate_size, config.hidden_size) + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + def forward(self, hidden_states, input_tensor): + hidden_states = self.dense(hidden_states) + hidden_states = self.dropout(hidden_states) + hidden_states = self.LayerNorm(hidden_states + input_tensor) + return hidden_states + + +class BertLayer(nn.Module): + def __init__(self, config, layer_num): + super().__init__() + self.config = config + self.chunk_size_feed_forward = config.chunk_size_feed_forward + self.seq_len_dim = 1 + self.attention = BertAttention(config) + self.layer_num = layer_num + if self.config.add_cross_attention and layer_num % self.config.cross_attention_freq == 0: + self.crossattention = BertAttention(config, is_cross_attention=self.config.add_cross_attention) + self.has_cross_attention = True + else: + self.has_cross_attention = False + self.intermediate = BertIntermediate(config) + self.output = BertOutput(config) + + self.intermediate_query = BertIntermediate(config) + self.output_query = BertOutput(config) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + query_length=0, + ): + # decoder uni-directional self-attention cached key/values tuple is at positions 1,2 + self_attn_past_key_value = past_key_value[:2] if past_key_value is not None else None + self_attention_outputs = self.attention( + hidden_states, + attention_mask, + head_mask, + output_attentions=output_attentions, + past_key_value=self_attn_past_key_value, + ) + attention_output = self_attention_outputs[0] + outputs = self_attention_outputs[1:-1] + + present_key_value = self_attention_outputs[-1] + + if query_length > 0: + query_attention_output = attention_output[:, :query_length, :] + + if self.has_cross_attention: + assert encoder_hidden_states is not None, "encoder_hidden_states must be given for cross-attention layers" + cross_attention_outputs = self.crossattention( + query_attention_output, + attention_mask, + head_mask, + encoder_hidden_states, + encoder_attention_mask, + output_attentions=output_attentions, + ) + query_attention_output = cross_attention_outputs[0] + outputs = outputs + cross_attention_outputs[1:-1] # add cross attentions if we output attention weights + + layer_output = apply_chunking_to_forward( + self.feed_forward_chunk_query, + self.chunk_size_feed_forward, + self.seq_len_dim, + query_attention_output, + ) + if attention_output.shape[1] > query_length: + layer_output_text = apply_chunking_to_forward( + self.feed_forward_chunk, + self.chunk_size_feed_forward, + self.seq_len_dim, + attention_output[:, query_length:, :], + ) + layer_output = torch.cat([layer_output, layer_output_text], dim=1) + else: + layer_output = apply_chunking_to_forward( + self.feed_forward_chunk, + self.chunk_size_feed_forward, + self.seq_len_dim, + attention_output, + ) + outputs = (layer_output,) + outputs + + outputs = outputs + (present_key_value,) + + return outputs + + def feed_forward_chunk(self, attention_output): + intermediate_output = self.intermediate(attention_output) + layer_output = self.output(intermediate_output, attention_output) + return layer_output + + def feed_forward_chunk_query(self, attention_output): + intermediate_output = self.intermediate_query(attention_output) + layer_output = self.output_query(intermediate_output, attention_output) + return layer_output + + +class BertEncoder(nn.Module): + def __init__(self, config): + super().__init__() + self.config = config + self.layer = nn.ModuleList([BertLayer(config, i) for i in range(config.num_hidden_layers)]) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_values=None, + use_cache=None, + output_attentions=False, + output_hidden_states=False, + return_dict=True, + query_length=0, + ): + all_hidden_states = () if output_hidden_states else None + all_self_attentions = () if output_attentions else None + all_cross_attentions = () if output_attentions and self.config.add_cross_attention else None + + next_decoder_cache = () if use_cache else None + + for i in range(self.config.num_hidden_layers): + layer_module = self.layer[i] + if output_hidden_states: + all_hidden_states = all_hidden_states + (hidden_states,) + + layer_head_mask = head_mask[i] if head_mask is not None else None + past_key_value = past_key_values[i] if past_key_values is not None else None + + if getattr(self.config, "gradient_checkpointing", False) and self.training: + + if use_cache: + logger.warn("`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`...") + use_cache = False + + def create_custom_forward(module): + def custom_forward(*inputs): + return module(*inputs, past_key_value, output_attentions, query_length) + + return custom_forward + + layer_outputs = torch.utils.checkpoint.checkpoint( + create_custom_forward(layer_module), + hidden_states, + attention_mask, + layer_head_mask, + encoder_hidden_states, + encoder_attention_mask, + ) + else: + layer_outputs = layer_module( + hidden_states, + attention_mask, + layer_head_mask, + encoder_hidden_states, + encoder_attention_mask, + past_key_value, + output_attentions, + query_length, + ) + + hidden_states = layer_outputs[0] + if use_cache: + next_decoder_cache += (layer_outputs[-1],) + if output_attentions: + all_self_attentions = all_self_attentions + (layer_outputs[1],) + all_cross_attentions = all_cross_attentions + (layer_outputs[2],) + + if output_hidden_states: + all_hidden_states = all_hidden_states + (hidden_states,) + + if not return_dict: + return tuple( + v + for v in [ + hidden_states, + next_decoder_cache, + all_hidden_states, + all_self_attentions, + all_cross_attentions, + ] + if v is not None + ) + return BaseModelOutputWithPastAndCrossAttentions( + last_hidden_state=hidden_states, + past_key_values=next_decoder_cache, + hidden_states=all_hidden_states, + attentions=all_self_attentions, + cross_attentions=all_cross_attentions, + ) + + +class BertPooler(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + self.activation = nn.Tanh() + + def forward(self, hidden_states): + # We "pool" the model by simply taking the hidden state corresponding + # to the first token. + first_token_tensor = hidden_states[:, 0] + pooled_output = self.dense(first_token_tensor) + pooled_output = self.activation(pooled_output) + return pooled_output + + +class BertPredictionHeadTransform(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + if isinstance(config.hidden_act, str): + self.transform_act_fn = ACT2FN[config.hidden_act] + else: + self.transform_act_fn = config.hidden_act + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + + def forward(self, hidden_states): + hidden_states = self.dense(hidden_states) + hidden_states = self.transform_act_fn(hidden_states) + hidden_states = self.LayerNorm(hidden_states) + return hidden_states + + +class BertLMPredictionHead(nn.Module): + def __init__(self, config): + super().__init__() + self.transform = BertPredictionHeadTransform(config) + + # The output weights are the same as the input embeddings, but there is + # an output-only bias for each token. + self.decoder = nn.Linear(config.hidden_size, config.vocab_size, bias=False) + + self.bias = nn.Parameter(torch.zeros(config.vocab_size)) + + # Need a link between the two variables so that the bias is correctly resized with `resize_token_embeddings` + self.decoder.bias = self.bias + + def forward(self, hidden_states): + hidden_states = self.transform(hidden_states) + hidden_states = self.decoder(hidden_states) + return hidden_states + + +class BertOnlyMLMHead(nn.Module): + def __init__(self, config): + super().__init__() + self.predictions = BertLMPredictionHead(config) + + def forward(self, sequence_output): + prediction_scores = self.predictions(sequence_output) + return prediction_scores + + +class BertPreTrainedModel(PreTrainedModel): + """ + An abstract class to handle weights initialization and a simple interface for downloading and loading pretrained + models. + """ + + config_class = BertConfig + base_model_prefix = "bert" + _keys_to_ignore_on_load_missing = [r"position_ids"] + + def _init_weights(self, module): + """Initialize the weights""" + if isinstance(module, (nn.Linear, nn.Embedding)): + # Slightly different from the TF version which uses truncated_normal for initialization + # cf https://github.com/pytorch/pytorch/pull/5617 + module.weight.data.normal_(mean=0.0, std=self.config.initializer_range) + elif isinstance(module, nn.LayerNorm): + module.bias.data.zero_() + module.weight.data.fill_(1.0) + if isinstance(module, nn.Linear) and module.bias is not None: + module.bias.data.zero_() + + +class BertModel(BertPreTrainedModel): + """ + The model can behave as an encoder (with only self-attention) as well as a decoder, in which case a layer of + cross-attention is added between the self-attention layers, following the architecture described in `Attention is + all you need `__ by Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, + Llion Jones, Aidan N. Gomez, Lukasz Kaiser and Illia Polosukhin. + argument and :obj:`add_cross_attention` set to :obj:`True`; an :obj:`encoder_hidden_states` is then expected as an + input to the forward pass. + """ + + def __init__(self, config, add_pooling_layer=False): + super().__init__(config) + self.config = config + + self.embeddings = BertEmbeddings(config) + + self.encoder = BertEncoder(config) + + self.pooler = BertPooler(config) if add_pooling_layer else None + + self.init_weights() + + def get_input_embeddings(self): + return self.embeddings.word_embeddings + + def set_input_embeddings(self, value): + self.embeddings.word_embeddings = value + + def _prune_heads(self, heads_to_prune): + """ + Prunes heads of the model. heads_to_prune: dict of {layer_num: list of heads to prune in this layer} See base + class PreTrainedModel + """ + for layer, heads in heads_to_prune.items(): + self.encoder.layer[layer].attention.prune_heads(heads) + + def get_extended_attention_mask( + self, + attention_mask: Tensor, + input_shape: Tuple[int], + device: device, + is_decoder: bool, + has_query: bool = False, + ) -> Tensor: + """ + Makes broadcastable attention and causal masks so that future and masked tokens are ignored. + + Arguments: + attention_mask (:obj:`torch.Tensor`): + Mask with ones indicating tokens to attend to, zeros for tokens to ignore. + input_shape (:obj:`Tuple[int]`): + The shape of the input to the model. + device: (:obj:`torch.device`): + The device of the input to the model. + + Returns: + :obj:`torch.Tensor` The extended attention mask, with a the same dtype as :obj:`attention_mask.dtype`. + """ + # We can provide a self-attention mask of dimensions [batch_size, from_seq_length, to_seq_length] + # ourselves in which case we just need to make it broadcastable to all heads. + if attention_mask.dim() == 3: + extended_attention_mask = attention_mask[:, None, :, :] + elif attention_mask.dim() == 2: + # Provided a padding mask of dimensions [batch_size, seq_length] + # - if the model is a decoder, apply a causal mask in addition to the padding mask + # - if the model is an encoder, make the mask broadcastable to [batch_size, num_heads, seq_length, seq_length] + if is_decoder: + batch_size, seq_length = input_shape + + seq_ids = torch.arange(seq_length, device=device) + causal_mask = seq_ids[None, None, :].repeat(batch_size, seq_length, 1) <= seq_ids[None, :, None] + + # add a prefix ones mask to the causal mask + # causal and attention masks must have same type with pytorch version < 1.3 + causal_mask = causal_mask.to(attention_mask.dtype) + + if causal_mask.shape[1] < attention_mask.shape[1]: + prefix_seq_len = attention_mask.shape[1] - causal_mask.shape[1] + if has_query: # UniLM style attention mask + causal_mask = torch.cat( + [ + torch.zeros( + (batch_size, prefix_seq_len, seq_length), + device=device, + dtype=causal_mask.dtype, + ), + causal_mask, + ], + axis=1, + ) + causal_mask = torch.cat( + [ + torch.ones( + (batch_size, causal_mask.shape[1], prefix_seq_len), + device=device, + dtype=causal_mask.dtype, + ), + causal_mask, + ], + axis=-1, + ) + extended_attention_mask = causal_mask[:, None, :, :] * attention_mask[:, None, None, :] + else: + extended_attention_mask = attention_mask[:, None, None, :] + else: + raise ValueError("Wrong shape for input_ids (shape {}) or attention_mask (shape {})".format(input_shape, attention_mask.shape)) + + # Since attention_mask is 1.0 for positions we want to attend and 0.0 for + # masked positions, this operation will create a tensor which is 0.0 for + # positions we want to attend and -10000.0 for masked positions. + # Since we are adding it to the raw scores before the softmax, this is + # effectively the same as removing these entirely. + extended_attention_mask = extended_attention_mask.to(dtype=self.dtype) # fp16 compatibility + extended_attention_mask = (1.0 - extended_attention_mask) * -10000.0 + return extended_attention_mask + + def forward( + self, + input_ids=None, + attention_mask=None, + position_ids=None, + head_mask=None, + query_embeds=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_values=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + is_decoder=False, + ): + r""" + encoder_hidden_states (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`, `optional`): + Sequence of hidden-states at the output of the last layer of the encoder. Used in the cross-attention if + the model is configured as a decoder. + encoder_attention_mask (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length)`, `optional`): + Mask to avoid performing attention on the padding token indices of the encoder input. This mask is used in + the cross-attention if the model is configured as a decoder. Mask values selected in ``[0, 1]``: + - 1 for tokens that are **not masked**, + - 0 for tokens that are **masked**. + past_key_values (:obj:`tuple(tuple(torch.FloatTensor))` of length :obj:`config.n_layers` with each tuple having 4 tensors of shape :obj:`(batch_size, num_heads, sequence_length - 1, embed_size_per_head)`): + Contains precomputed key and value hidden states of the attention blocks. Can be used to speed up decoding. + If :obj:`past_key_values` are used, the user can optionally input only the last :obj:`decoder_input_ids` + (those that don't have their past key value states given to this model) of shape :obj:`(batch_size, 1)` + instead of all :obj:`decoder_input_ids` of shape :obj:`(batch_size, sequence_length)`. + use_cache (:obj:`bool`, `optional`): + If set to :obj:`True`, :obj:`past_key_values` key value states are returned and can be used to speed up + decoding (see :obj:`past_key_values`). + """ + output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions + output_hidden_states = output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + + # use_cache = use_cache if use_cache is not None else self.config.use_cache + + if input_ids is None: + assert query_embeds is not None, "You have to specify query_embeds when input_ids is None" + + # past_key_values_length + past_key_values_length = past_key_values[0][0].shape[2] - self.config.query_length if past_key_values is not None else 0 + + query_length = query_embeds.shape[1] if query_embeds is not None else 0 + + embedding_output = self.embeddings( + input_ids=input_ids, + position_ids=position_ids, + query_embeds=query_embeds, + past_key_values_length=past_key_values_length, + ) + + input_shape = embedding_output.size()[:-1] + batch_size, seq_length = input_shape + device = embedding_output.device + + if attention_mask is None: + attention_mask = torch.ones(((batch_size, seq_length + past_key_values_length)), device=device) + + # We can provide a self-attention mask of dimensions [batch_size, from_seq_length, to_seq_length] + # ourselves in which case we just need to make it broadcastable to all heads. + if is_decoder: + extended_attention_mask = self.get_extended_attention_mask( + attention_mask, + input_ids.shape, + device, + is_decoder, + has_query=(query_embeds is not None), + ) + else: + extended_attention_mask = self.get_extended_attention_mask(attention_mask, input_shape, device, is_decoder) + + # If a 2D or 3D attention mask is provided for the cross-attention + # we need to make broadcastable to [batch_size, num_heads, seq_length, seq_length] + if encoder_hidden_states is not None: + if type(encoder_hidden_states) == list: + encoder_batch_size, encoder_sequence_length, _ = encoder_hidden_states[0].size() + else: + ( + encoder_batch_size, + encoder_sequence_length, + _, + ) = encoder_hidden_states.size() + encoder_hidden_shape = (encoder_batch_size, encoder_sequence_length) + + if type(encoder_attention_mask) == list: + encoder_extended_attention_mask = [self.invert_attention_mask(mask) for mask in encoder_attention_mask] + elif encoder_attention_mask is None: + encoder_attention_mask = torch.ones(encoder_hidden_shape, device=device) + encoder_extended_attention_mask = self.invert_attention_mask(encoder_attention_mask) + else: + encoder_extended_attention_mask = self.invert_attention_mask(encoder_attention_mask) + else: + encoder_extended_attention_mask = None + + # Prepare head mask if needed + # 1.0 in head_mask indicate we keep the head + # attention_probs has shape bsz x n_heads x N x N + # input head_mask has shape [num_heads] or [num_hidden_layers x num_heads] + # and head_mask is converted to shape [num_hidden_layers x batch x num_heads x seq_length x seq_length] + head_mask = self.get_head_mask(head_mask, self.config.num_hidden_layers) + + encoder_outputs = self.encoder( + embedding_output, + attention_mask=extended_attention_mask, + head_mask=head_mask, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_extended_attention_mask, + past_key_values=past_key_values, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + query_length=query_length, + ) + sequence_output = encoder_outputs[0] + pooled_output = self.pooler(sequence_output) if self.pooler is not None else None + + if not return_dict: + return (sequence_output, pooled_output) + encoder_outputs[1:] + + return BaseModelOutputWithPoolingAndCrossAttentions( + last_hidden_state=sequence_output, + pooler_output=pooled_output, + past_key_values=encoder_outputs.past_key_values, + hidden_states=encoder_outputs.hidden_states, + attentions=encoder_outputs.attentions, + cross_attentions=encoder_outputs.cross_attentions, + ) + + +class BertLMHeadModel(BertPreTrainedModel): + + _keys_to_ignore_on_load_unexpected = [r"pooler"] + _keys_to_ignore_on_load_missing = [r"position_ids", r"predictions.decoder.bias"] + + def __init__(self, config): + super().__init__(config) + + self.bert = BertModel(config, add_pooling_layer=False) + self.cls = BertOnlyMLMHead(config) + + self.init_weights() + + def get_output_embeddings(self): + return self.cls.predictions.decoder + + def set_output_embeddings(self, new_embeddings): + self.cls.predictions.decoder = new_embeddings + + def forward( + self, + input_ids=None, + attention_mask=None, + position_ids=None, + head_mask=None, + query_embeds=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + labels=None, + past_key_values=None, + use_cache=True, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + return_logits=False, + is_decoder=True, + reduction="mean", + ): + r""" + encoder_hidden_states (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`, `optional`): + Sequence of hidden-states at the output of the last layer of the encoder. Used in the cross-attention if + the model is configured as a decoder. + encoder_attention_mask (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length)`, `optional`): + Mask to avoid performing attention on the padding token indices of the encoder input. This mask is used in + the cross-attention if the model is configured as a decoder. Mask values selected in ``[0, 1]``: + - 1 for tokens that are **not masked**, + - 0 for tokens that are **masked**. + labels (:obj:`torch.LongTensor` of shape :obj:`(batch_size, sequence_length)`, `optional`): + Labels for computing the left-to-right language modeling loss (next word prediction). Indices should be in + ``[-100, 0, ..., config.vocab_size]`` (see ``input_ids`` docstring) Tokens with indices set to ``-100`` are + ignored (masked), the loss is only computed for the tokens with labels n ``[0, ..., config.vocab_size]`` + past_key_values (:obj:`tuple(tuple(torch.FloatTensor))` of length :obj:`config.n_layers` with each tuple having 4 tensors of shape :obj:`(batch_size, num_heads, sequence_length - 1, embed_size_per_head)`): + Contains precomputed key and value hidden states of the attention blocks. Can be used to speed up decoding. + If :obj:`past_key_values` are used, the user can optionally input only the last :obj:`decoder_input_ids` + (those that don't have their past key value states given to this model) of shape :obj:`(batch_size, 1)` + instead of all :obj:`decoder_input_ids` of shape :obj:`(batch_size, sequence_length)`. + use_cache (:obj:`bool`, `optional`): + If set to :obj:`True`, :obj:`past_key_values` key value states are returned and can be used to speed up + decoding (see :obj:`past_key_values`). + Returns: + Example:: + >>> from transformers import BertTokenizer, BertLMHeadModel, BertConfig + >>> import torch + >>> tokenizer = BertTokenizer.from_pretrained('bert-base-cased') + >>> config = BertConfig.from_pretrained("bert-base-cased") + >>> model = BertLMHeadModel.from_pretrained('bert-base-cased', config=config) + >>> inputs = tokenizer("Hello, my dog is cute", return_tensors="pt") + >>> outputs = model(**inputs) + >>> prediction_logits = outputs.logits + """ + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + if labels is not None: + use_cache = False + if past_key_values is not None: + query_embeds = None + + outputs = self.bert( + input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + head_mask=head_mask, + query_embeds=query_embeds, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_attention_mask, + past_key_values=past_key_values, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + is_decoder=is_decoder, + ) + + sequence_output = outputs[0] + if query_embeds is not None: + sequence_output = outputs[0][:, query_embeds.shape[1] :, :] + + prediction_scores = self.cls(sequence_output) + + if return_logits: + return prediction_scores[:, :-1, :].contiguous() + + lm_loss = None + if labels is not None: + # we are doing next-token prediction; shift prediction scores and input ids by one + shifted_prediction_scores = prediction_scores[:, :-1, :].contiguous() + labels = labels[:, 1:].contiguous() + loss_fct = CrossEntropyLoss(reduction=reduction, label_smoothing=0.1) + lm_loss = loss_fct( + shifted_prediction_scores.view(-1, self.config.vocab_size), + labels.view(-1), + ) + if reduction == "none": + lm_loss = lm_loss.view(prediction_scores.size(0), -1).sum(1) + + if not return_dict: + output = (prediction_scores,) + outputs[2:] + return ((lm_loss,) + output) if lm_loss is not None else output + + return CausalLMOutputWithCrossAttentions( + loss=lm_loss, + logits=prediction_scores, + past_key_values=outputs.past_key_values, + hidden_states=outputs.hidden_states, + attentions=outputs.attentions, + cross_attentions=outputs.cross_attentions, + ) + + def prepare_inputs_for_generation(self, input_ids, query_embeds, past=None, attention_mask=None, **model_kwargs): + # if model is used as a decoder in encoder-decoder model, the decoder attention mask is created on the fly + if attention_mask is None: + attention_mask = input_ids.new_ones(input_ids.shape) + query_mask = input_ids.new_ones(query_embeds.shape[:-1]) + attention_mask = torch.cat([query_mask, attention_mask], dim=-1) + + # cut decoder_input_ids if past is used + if past is not None: + input_ids = input_ids[:, -1:] + + return { + "input_ids": input_ids, + "query_embeds": query_embeds, + "attention_mask": attention_mask, + "past_key_values": past, + "encoder_hidden_states": model_kwargs.get("encoder_hidden_states", None), + "encoder_attention_mask": model_kwargs.get("encoder_attention_mask", None), + "is_decoder": True, + } + + def _reorder_cache(self, past, beam_idx): + reordered_past = () + for layer_past in past: + reordered_past += (tuple(past_state.index_select(0, beam_idx) for past_state in layer_past),) + return reordered_past + + +class BertForMaskedLM(BertPreTrainedModel): + + _keys_to_ignore_on_load_unexpected = [r"pooler"] + _keys_to_ignore_on_load_missing = [r"position_ids", r"predictions.decoder.bias"] + + def __init__(self, config): + super().__init__(config) + + self.bert = BertModel(config, add_pooling_layer=False) + self.cls = BertOnlyMLMHead(config) + + self.init_weights() + + def get_output_embeddings(self): + return self.cls.predictions.decoder + + def set_output_embeddings(self, new_embeddings): + self.cls.predictions.decoder = new_embeddings + + def forward( + self, + input_ids=None, + attention_mask=None, + position_ids=None, + head_mask=None, + query_embeds=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + labels=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + return_logits=False, + is_decoder=False, + ): + r""" + labels (:obj:`torch.LongTensor` of shape :obj:`(batch_size, sequence_length)`, `optional`): + Labels for computing the masked language modeling loss. Indices should be in ``[-100, 0, ..., + config.vocab_size]`` (see ``input_ids`` docstring) Tokens with indices set to ``-100`` are ignored + (masked), the loss is only computed for the tokens with labels in ``[0, ..., config.vocab_size]`` + """ + + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + + outputs = self.bert( + input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + head_mask=head_mask, + query_embeds=query_embeds, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_attention_mask, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + is_decoder=is_decoder, + ) + + if query_embeds is not None: + sequence_output = outputs[0][:, query_embeds.shape[1] :, :] + prediction_scores = self.cls(sequence_output) + + if return_logits: + return prediction_scores + + masked_lm_loss = None + if labels is not None: + loss_fct = CrossEntropyLoss() # -100 index = padding token + masked_lm_loss = loss_fct(prediction_scores.view(-1, self.config.vocab_size), labels.view(-1)) + + if not return_dict: + output = (prediction_scores,) + outputs[2:] + return ((masked_lm_loss,) + output) if masked_lm_loss is not None else output + + return MaskedLMOutput( + loss=masked_lm_loss, + logits=prediction_scores, + hidden_states=outputs.hidden_states, + attentions=outputs.attentions, + ) + + +class Qformer(nn.Module): + def __init__(self, model_args, vision_tower): + super().__init__() + + self.depth = model_args.mm_qformer_depth + self.num_latents = model_args.mm_qformer_latents + self.pretrained = model_args.mm_qformer_pretrained + + self.Qformer, self.query_tokens, self.ln_vision = self.build_Qformer(vision_tower.hidden_size, self.depth, self.num_latents) + + if self.pretrained is not None: + pretrained_dict = torch.load(self.pretrained, map_location="cpu")["model"] + pretrained_dict = {k: v for k, v in pretrained_dict.items() if not k.startswith("t5_proj")} + self.load_state_dict(pretrained_dict) + + def build_Qformer(self, vision_width, cross_attention_freq, num_query_token): + encoder_config = BertConfig.from_pretrained("bert-base-uncased") + encoder_config.encoder_width = vision_width + # insert cross-attention layer every other block + encoder_config.add_cross_attention = True + encoder_config.cross_attention_freq = cross_attention_freq + encoder_config.query_length = num_query_token + Qformer = BertLMHeadModel(config=encoder_config) + query_tokens = nn.Parameter(torch.zeros(1, num_query_token, encoder_config.hidden_size)) + query_tokens.data.normal_(mean=0.0, std=encoder_config.initializer_range) + Qformer.cls = None + Qformer.bert.embeddings.word_embeddings = None + Qformer.bert.embeddings.position_embeddings = None + for layer in Qformer.bert.encoder.layer: + layer.output = None + layer.intermediate = None + return Qformer, query_tokens, nn.LayerNorm(vision_width) + + def forward(self, image_features, *args, **kwargs): + x = self.ln_vision(image_features) + image_atts = torch.ones(x.size()[:-1], dtype=torch.long).to(x.device) + + query_tokens = self.query_tokens.expand(x.shape[0], -1, -1) + query_output = self.Qformer.bert( + query_embeds=query_tokens, + encoder_hidden_states=x, + encoder_attention_mask=image_atts, + return_dict=True, + ) + + return query_output.last_hidden_state + + @property + def hidden_size(self): + return 768 + + @property + def config(self): + return { + "mm_resampler_type": "qformer", + "mm_qformer_depth": self.depth, + "mm_qformer_latents": self.num_latents, + "mm_qformer_pretrained": self.pretrained, + } diff --git a/llava/model/multimodal_resampler/spatial_pool.py b/llava/model/multimodal_resampler/spatial_pool.py new file mode 100644 index 0000000..4bdbe3a --- /dev/null +++ b/llava/model/multimodal_resampler/spatial_pool.py @@ -0,0 +1,45 @@ +import torch +import torch.nn as nn +import math + + +class SpatialPool(nn.Module): + def __init__(self, model_args, vision_tower): + super().__init__() + + self.mode = model_args.mm_spatial_pool_mode + self.stride = model_args.mm_spatial_pool_stride + self.out_channels = getattr(model_args, "mm_spatial_pool_out_channels", vision_tower.hidden_size) + + if self.mode == "average": + self.pool = nn.AvgPool2d(kernel_size=self.stride, stride=self.stride) + elif self.mode == "max": + self.pool = nn.MaxPool2d(kernel_size=self.stride, stride=self.stride) + elif self.mode == "conv": + self.pool = nn.Conv2d(in_channels=vision_tower.hidden_size, out_channels=self.out_channels, kernel_size=self.stride, stride=self.stride) + else: + raise ValueError(f"Unknown pooling mode: {self.pool}.") + + def forward(self, image_features, images, *args, **kwargs): + ori_W = int(math.sqrt(image_features.shape[1] * images.shape[3] // images.shape[2])) + ori_H = int(ori_W * images.shape[2] // images.shape[3]) + + B, _, F = image_features.shape + + image_features_spatial = image_features.view(B, ori_H, ori_H, F).permute(0, 3, 1, 2) + image_features_spatial_pool = self.pool(image_features_spatial) + + return image_features_spatial_pool.flatten(2).transpose(1, 2).contiguous() + + @property + def config(self): + return { + "mm_resampler_type": "spatial_pool", + "mm_spatial_pool_stride": self.stride, + "mm_spatial_pool_mode": self.mode, + "mm_spatial_pool_out_channels": self.out_channels, + } + + @property + def hidden_size(self): + return self.out_channels diff --git a/llava/model/utils.py b/llava/model/utils.py new file mode 100644 index 0000000..10652a5 --- /dev/null +++ b/llava/model/utils.py @@ -0,0 +1,20 @@ +from transformers import AutoConfig + + +def auto_upgrade(config): + cfg = AutoConfig.from_pretrained(config) + if "llava" in config and "llava" not in cfg.model_type: + assert cfg.model_type == "llama" + print("You are using newer LLaVA code base, while the checkpoint of v0 is from older code base.") + print("You must upgrade the checkpoint to the new code base (this can be done automatically).") + confirm = input("Please confirm that you want to upgrade the checkpoint. [Y/N]") + if confirm.lower() in ["y", "yes"]: + print("Upgrading checkpoint...") + assert len(cfg.architectures) == 1 + setattr(cfg.__class__, "model_type", "llava") + cfg.architectures[0] = "LlavaLlamaForCausalLM" + cfg.save_pretrained(config) + print("Checkpoint upgraded.") + else: + print("Checkpoint upgrade aborted.") + exit(1) diff --git a/llava/serve/__init__.py b/llava/serve/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/llava/serve/cli.py b/llava/serve/cli.py new file mode 100644 index 0000000..88fbfe8 --- /dev/null +++ b/llava/serve/cli.py @@ -0,0 +1,111 @@ +import argparse +import torch + +from llava.constants import IMAGE_TOKEN_INDEX, DEFAULT_IMAGE_TOKEN, DEFAULT_IM_START_TOKEN, DEFAULT_IM_END_TOKEN +from llava.conversation import conv_templates, SeparatorStyle +from llava.model.builder import load_pretrained_model +from llava.utils import disable_torch_init +from llava.mm_utils import tokenizer_image_token, get_model_name_from_path, KeywordsStoppingCriteria + +from PIL import Image + +import requests +from PIL import Image +from io import BytesIO +from transformers import TextStreamer + + +def load_image(image_file): + if image_file.startswith("http") or image_file.startswith("https"): + response = requests.get(image_file) + image = Image.open(BytesIO(response.content)).convert("RGB") + else: + image = Image.open(image_file).convert("RGB") + return image + + +def main(args): + # Model + disable_torch_init() + + model_name = get_model_name_from_path(args.model_path) + tokenizer, model, image_processor, context_len = load_pretrained_model(args.model_path, args.model_base, model_name, args.load_8bit, args.load_4bit) + + if "llama-2" in model_name.lower(): + conv_mode = "llava_llama_2" + elif "v1" in model_name.lower(): + conv_mode = "llava_v1" + elif "mpt" in model_name.lower(): + conv_mode = "mpt" + else: + conv_mode = "llava_v0" + + if args.conv_mode is not None and conv_mode != args.conv_mode: + print("[WARNING] the auto inferred conversation mode is {}, while `--conv-mode` is {}, using {}".format(conv_mode, args.conv_mode, args.conv_mode)) + else: + args.conv_mode = conv_mode + + conv = conv_templates[args.conv_mode].copy() + if "mpt" in model_name.lower(): + roles = ("user", "assistant") + else: + roles = conv.roles + + image = load_image(args.image_file) + image_tensor = image_processor.preprocess(image, return_tensors="pt")["pixel_values"].half().cuda() + + while True: + try: + inp = input(f"{roles[0]}: ") + except EOFError: + inp = "" + if not inp: + print("exit...") + break + + print(f"{roles[1]}: ", end="") + + if image is not None: + # first message + if model.config.mm_use_im_start_end: + inp = DEFAULT_IM_START_TOKEN + DEFAULT_IMAGE_TOKEN + DEFAULT_IM_END_TOKEN + "\n" + inp + else: + inp = DEFAULT_IMAGE_TOKEN + "\n" + inp + conv.append_message(conv.roles[0], inp) + image = None + else: + # later messages + conv.append_message(conv.roles[0], inp) + conv.append_message(conv.roles[1], None) + prompt = conv.get_prompt() + + input_ids = tokenizer_image_token(prompt, tokenizer, IMAGE_TOKEN_INDEX, return_tensors="pt").unsqueeze(0).cuda() + stop_str = conv.sep if conv.sep_style != SeparatorStyle.TWO else conv.sep2 + keywords = [stop_str] + stopping_criteria = KeywordsStoppingCriteria(keywords, tokenizer, input_ids) + streamer = TextStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True) + + with torch.inference_mode(): + output_ids = model.generate(input_ids, images=image_tensor, do_sample=True, temperature=0.2, max_new_tokens=1024, streamer=streamer, use_cache=True, stopping_criteria=[stopping_criteria]) + + outputs = tokenizer.decode(output_ids[0, input_ids.shape[1] :]).strip() + conv.messages[-1][-1] = outputs + + if args.debug: + print("\n", {"prompt": prompt, "outputs": outputs}, "\n") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--model-path", type=str, default="facebook/opt-350m") + parser.add_argument("--model-base", type=str, default=None) + parser.add_argument("--image-file", type=str, required=True) + parser.add_argument("--num-gpus", type=int, default=1) + parser.add_argument("--conv-mode", type=str, default=None) + parser.add_argument("--temperature", type=float, default=0.2) + parser.add_argument("--max-new-tokens", type=int, default=512) + parser.add_argument("--load-8bit", action="store_true") + parser.add_argument("--load-4bit", action="store_true") + parser.add_argument("--debug", action="store_true") + args = parser.parse_args() + main(args) diff --git a/llava/serve/controller.py b/llava/serve/controller.py new file mode 100644 index 0000000..261f8c6 --- /dev/null +++ b/llava/serve/controller.py @@ -0,0 +1,287 @@ +""" +A controller manages distributed workers. +It sends worker addresses to clients. +""" + +import argparse +import asyncio +import dataclasses +from enum import Enum, auto +import json +import logging +import time +from typing import List, Union +import threading + +from fastapi import FastAPI, Request +from fastapi.responses import StreamingResponse +import numpy as np +import requests +import uvicorn + +from llava.constants import CONTROLLER_HEART_BEAT_EXPIRATION +from llava.utils import build_logger, server_error_msg + + +logger = build_logger("controller", "controller.log") + + +class DispatchMethod(Enum): + LOTTERY = auto() + SHORTEST_QUEUE = auto() + + @classmethod + def from_str(cls, name): + if name == "lottery": + return cls.LOTTERY + elif name == "shortest_queue": + return cls.SHORTEST_QUEUE + else: + raise ValueError(f"Invalid dispatch method") + + +@dataclasses.dataclass +class WorkerInfo: + model_names: List[str] + speed: int + queue_length: int + check_heart_beat: bool + last_heart_beat: str + + +def heart_beat_controller(controller): + while True: + time.sleep(CONTROLLER_HEART_BEAT_EXPIRATION) + controller.remove_stable_workers_by_expiration() + + +class Controller: + def __init__(self, dispatch_method: str): + # Dict[str -> WorkerInfo] + self.worker_info = {} + self.dispatch_method = DispatchMethod.from_str(dispatch_method) + + self.heart_beat_thread = threading.Thread(target=heart_beat_controller, args=(self,)) + self.heart_beat_thread.start() + + logger.info("Init controller") + + def register_worker(self, worker_name: str, check_heart_beat: bool, worker_status: dict): + if worker_name not in self.worker_info: + logger.info(f"Register a new worker: {worker_name}") + else: + logger.info(f"Register an existing worker: {worker_name}") + + if not worker_status: + worker_status = self.get_worker_status(worker_name) + if not worker_status: + return False + + self.worker_info[worker_name] = WorkerInfo(worker_status["model_names"], worker_status["speed"], worker_status["queue_length"], check_heart_beat, time.time()) + + logger.info(f"Register done: {worker_name}, {worker_status}") + return True + + def get_worker_status(self, worker_name: str): + try: + r = requests.post(worker_name + "/worker_get_status", timeout=5) + except requests.exceptions.RequestException as e: + logger.error(f"Get status fails: {worker_name}, {e}") + return None + + if r.status_code != 200: + logger.error(f"Get status fails: {worker_name}, {r}") + return None + + return r.json() + + def remove_worker(self, worker_name: str): + del self.worker_info[worker_name] + + def refresh_all_workers(self): + old_info = dict(self.worker_info) + self.worker_info = {} + + for w_name, w_info in old_info.items(): + if not self.register_worker(w_name, w_info.check_heart_beat, None): + logger.info(f"Remove stale worker: {w_name}") + + def list_models(self): + model_names = set() + + for w_name, w_info in self.worker_info.items(): + model_names.update(w_info.model_names) + + return list(model_names) + + def get_worker_address(self, model_name: str): + if self.dispatch_method == DispatchMethod.LOTTERY: + worker_names = [] + worker_speeds = [] + for w_name, w_info in self.worker_info.items(): + if model_name in w_info.model_names: + worker_names.append(w_name) + worker_speeds.append(w_info.speed) + worker_speeds = np.array(worker_speeds, dtype=np.float32) + norm = np.sum(worker_speeds) + if norm < 1e-4: + return "" + worker_speeds = worker_speeds / norm + if True: # Directly return address + pt = np.random.choice(np.arange(len(worker_names)), p=worker_speeds) + worker_name = worker_names[pt] + return worker_name + + # Check status before returning + while True: + pt = np.random.choice(np.arange(len(worker_names)), p=worker_speeds) + worker_name = worker_names[pt] + + if self.get_worker_status(worker_name): + break + else: + self.remove_worker(worker_name) + worker_speeds[pt] = 0 + norm = np.sum(worker_speeds) + if norm < 1e-4: + return "" + worker_speeds = worker_speeds / norm + continue + return worker_name + elif self.dispatch_method == DispatchMethod.SHORTEST_QUEUE: + worker_names = [] + worker_qlen = [] + for w_name, w_info in self.worker_info.items(): + if model_name in w_info.model_names: + worker_names.append(w_name) + worker_qlen.append(w_info.queue_length / w_info.speed) + if len(worker_names) == 0: + return "" + min_index = np.argmin(worker_qlen) + w_name = worker_names[min_index] + self.worker_info[w_name].queue_length += 1 + logger.info(f"names: {worker_names}, queue_lens: {worker_qlen}, ret: {w_name}") + return w_name + else: + raise ValueError(f"Invalid dispatch method: {self.dispatch_method}") + + def receive_heart_beat(self, worker_name: str, queue_length: int): + if worker_name not in self.worker_info: + logger.info(f"Receive unknown heart beat. {worker_name}") + return False + + self.worker_info[worker_name].queue_length = queue_length + self.worker_info[worker_name].last_heart_beat = time.time() + logger.info(f"Receive heart beat. {worker_name}") + return True + + def remove_stable_workers_by_expiration(self): + expire = time.time() - CONTROLLER_HEART_BEAT_EXPIRATION + to_delete = [] + for worker_name, w_info in self.worker_info.items(): + if w_info.check_heart_beat and w_info.last_heart_beat < expire: + to_delete.append(worker_name) + + for worker_name in to_delete: + self.remove_worker(worker_name) + + def worker_api_generate_stream(self, params): + worker_addr = self.get_worker_address(params["model"]) + if not worker_addr: + logger.info(f"no worker: {params['model']}") + ret = { + "text": server_error_msg, + "error_code": 2, + } + yield json.dumps(ret).encode() + b"\0" + + try: + response = requests.post(worker_addr + "/worker_generate_stream", json=params, stream=True, timeout=5) + for chunk in response.iter_lines(decode_unicode=False, delimiter=b"\0"): + if chunk: + yield chunk + b"\0" + except requests.exceptions.RequestException as e: + logger.info(f"worker timeout: {worker_addr}") + ret = { + "text": server_error_msg, + "error_code": 3, + } + yield json.dumps(ret).encode() + b"\0" + + # Let the controller act as a worker to achieve hierarchical + # management. This can be used to connect isolated sub networks. + def worker_api_get_status(self): + model_names = set() + speed = 0 + queue_length = 0 + + for w_name in self.worker_info: + worker_status = self.get_worker_status(w_name) + if worker_status is not None: + model_names.update(worker_status["model_names"]) + speed += worker_status["speed"] + queue_length += worker_status["queue_length"] + + return { + "model_names": list(model_names), + "speed": speed, + "queue_length": queue_length, + } + + +app = FastAPI() + + +@app.post("/register_worker") +async def register_worker(request: Request): + data = await request.json() + controller.register_worker(data["worker_name"], data["check_heart_beat"], data.get("worker_status", None)) + + +@app.post("/refresh_all_workers") +async def refresh_all_workers(): + models = controller.refresh_all_workers() + + +@app.post("/list_models") +async def list_models(): + models = controller.list_models() + return {"models": models} + + +@app.post("/get_worker_address") +async def get_worker_address(request: Request): + data = await request.json() + addr = controller.get_worker_address(data["model"]) + return {"address": addr} + + +@app.post("/receive_heart_beat") +async def receive_heart_beat(request: Request): + data = await request.json() + exist = controller.receive_heart_beat(data["worker_name"], data["queue_length"]) + return {"exist": exist} + + +@app.post("/worker_generate_stream") +async def worker_api_generate_stream(request: Request): + params = await request.json() + generator = controller.worker_api_generate_stream(params) + return StreamingResponse(generator) + + +@app.post("/worker_get_status") +async def worker_api_get_status(request: Request): + return controller.worker_api_get_status() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--host", type=str, default="localhost") + parser.add_argument("--port", type=int, default=21001) + parser.add_argument("--dispatch-method", type=str, choices=["lottery", "shortest_queue"], default="shortest_queue") + args = parser.parse_args() + logger.info(f"args: {args}") + + controller = Controller(args.dispatch_method) + uvicorn.run(app, host=args.host, port=args.port, log_level="info") diff --git a/llava/serve/examples/extreme_ironing.jpg b/llava/serve/examples/extreme_ironing.jpg new file mode 100644 index 0000000000000000000000000000000000000000..638b078837f175039b2db49a63821288d9681daa GIT binary patch literal 62587 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%0AuQ&Y-0T6$w%zIY2Zk~ZlC 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 XN9s{wnTKl7HyNX-mmaYFZ{+^~ZjaNl literal 0 HcmV?d00001 diff --git a/llava/serve/examples/waterview.jpg b/llava/serve/examples/waterview.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6f44ebaba1aa493b8bab3baa4e827b76752b1869 GIT binary patch literal 95499 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 0 else ""), + gr.Chatbot.update(visible=True), + gr.Textbox.update(visible=True), + gr.Button.update(visible=True), + gr.Row.update(visible=True), + gr.Accordion.update(visible=True), + ) + + +def vote_last_response(state, vote_type, model_selector, request: gr.Request): + with open(get_conv_log_filename(), "a") as fout: + data = { + "tstamp": round(time.time(), 4), + "type": vote_type, + "model": model_selector, + "state": state.dict(), + "ip": request.client.host, + } + fout.write(json.dumps(data) + "\n") + + +def upvote_last_response(state, model_selector, request: gr.Request): + logger.info(f"upvote. ip: {request.client.host}") + vote_last_response(state, "upvote", model_selector, request) + return ("",) + (disable_btn,) * 3 + + +def downvote_last_response(state, model_selector, request: gr.Request): + logger.info(f"downvote. ip: {request.client.host}") + vote_last_response(state, "downvote", model_selector, request) + return ("",) + (disable_btn,) * 3 + + +def flag_last_response(state, model_selector, request: gr.Request): + logger.info(f"flag. ip: {request.client.host}") + vote_last_response(state, "flag", model_selector, request) + return ("",) + (disable_btn,) * 3 + + +def regenerate(state, image_process_mode, request: gr.Request): + logger.info(f"regenerate. ip: {request.client.host}") + state.messages[-1][-1] = None + prev_human_msg = state.messages[-2] + if type(prev_human_msg[1]) in (tuple, list): + prev_human_msg[1] = (*prev_human_msg[1][:2], image_process_mode) + state.skip_next = False + return (state, state.to_gradio_chatbot(), "", None, None) + (disable_btn,) * 5 + + +def clear_history(request: gr.Request): + logger.info(f"clear_history. ip: {request.client.host}") + state = default_conversation.copy() + return (state, state.to_gradio_chatbot(), "", None, None) + (disable_btn,) * 5 + + +def add_text(state, text, image, image2, image_process_mode, request: gr.Request): + logger.info(f"add_text. ip: {request.client.host}. len: {len(text)}") + if len(text) <= 0 and image is None: + state.skip_next = True + return (state, state.to_gradio_chatbot(), "", None) + (no_change_btn,) * 5 + if args.moderate: + flagged = violates_moderation(text) + if flagged: + state.skip_next = True + return (state, state.to_gradio_chatbot(), moderation_msg, None) + (no_change_btn,) * 5 + + text = text[:3072] # Hard cut-off + images = [x for x in [image, image2] if x is not None] + num_images = len(images) + if num_images > 0: + text = text.replace("", "").strip() + text = text[: 3072 - 512 * num_images] + text = "\n" * num_images + text + text = (text, images, image_process_mode) + if len(state.get_images(return_pil=True)) > 0: + state = default_conversation.copy() + state.append_message(state.roles[0], text) + state.append_message(state.roles[1], None) + state.skip_next = False + return (state, state.to_gradio_chatbot(), "", None, None) + (disable_btn,) * 5 + + +def http_bot(state, model_selector, temperature, top_p, max_new_tokens, request: gr.Request): + logger.info(f"http_bot. ip: {request.client.host}") + start_tstamp = time.time() + model_name = model_selector + + if state.skip_next: + # This generate call is skipped due to invalid inputs + yield (state, state.to_gradio_chatbot()) + (no_change_btn,) * 5 + return + + if len(state.messages) == state.offset + 2: + # First round of conversation + if "llava" in model_name.lower(): + if "llama-2" in model_name.lower(): + if "sharegpt" in model_name.lower(): + if "mmtag" in model_name.lower(): + template_name = "v1_mmtag" + elif "plain" in model_name.lower() and "finetune" not in model_name.lower(): + template_name = "v1_mmtag" + else: + template_name = "llava_v1" + else: + if "mmtag" in model_name.lower(): + template_name = "llava_llama_2_mmtag" + elif "simple" in model_name.lower(): + template_name = "llava_llama_2_simple" + elif "plain" in model_name.lower() and "finetune" not in model_name.lower(): + template_name = "llava_llama_2_mmtag" + elif "simple" in model_name.lower(): + template_name = "llava_llama_2_simple" + else: + template_name = "llava_llama_2" + elif "v1" in model_name.lower(): + if "mmtag" in model_name.lower(): + template_name = "v1_mmtag" + elif "plain" in model_name.lower() and "finetune" not in model_name.lower(): + template_name = "v1_mmtag" + else: + template_name = "llava_v1" + elif "mpt" in model_name.lower(): + template_name = "mpt" + else: + if "mmtag" in model_name.lower(): + template_name = "v0_mmtag" + elif "plain" in model_name.lower() and "finetune" not in model_name.lower(): + template_name = "v0_mmtag" + else: + template_name = "llava_v0" + elif "mpt" in model_name.lower(): + template_name = "mpt_text" + elif "llama-2" in model_name.lower(): + if "sharegpt" in model_name.lower(): + template_name = "vicuna_v1" + else: + template_name = "llama_2" + else: + template_name = "vicuna_v1" + new_state = conv_templates[template_name].copy() + new_state.append_message(new_state.roles[0], state.messages[-2][1]) + new_state.append_message(new_state.roles[1], None) + state = new_state + + # Query worker address + controller_url = args.controller_url + ret = requests.post(controller_url + "/get_worker_address", json={"model": model_name}) + worker_addr = ret.json()["address"] + logger.info(f"model_name: {model_name}, worker_addr: {worker_addr}") + + # No available worker + if worker_addr == "": + state.messages[-1][-1] = server_error_msg + yield (state, state.to_gradio_chatbot(), disable_btn, disable_btn, disable_btn, enable_btn, enable_btn) + return + + # Construct prompt + prompt = state.get_prompt() + + all_images = state.get_images(return_pil=True) + all_image_hash = [hashlib.md5(image.tobytes()).hexdigest() for image in all_images] + for image, hash in zip(all_images, all_image_hash): + t = datetime.datetime.now() + filename = os.path.join(LOGDIR, "serve_images", f"{t.year}-{t.month:02d}-{t.day:02d}", f"{hash}.jpg") + if not os.path.isfile(filename): + os.makedirs(os.path.dirname(filename), exist_ok=True) + image.save(filename) + + # Make requests + pload = { + "model": model_name, + "prompt": prompt, + "temperature": float(temperature), + "top_p": float(top_p), + "max_new_tokens": min(int(max_new_tokens), 1536), + "stop": state.sep if state.sep_style in [SeparatorStyle.SINGLE, SeparatorStyle.MPT] else state.sep2, + "images": f"List of {len(state.get_images())} images: {all_image_hash}", + } + logger.info(f"==== request ====\n{pload}") + + pload["images"] = state.get_images() + + state.messages[-1][-1] = "▌" + yield (state, state.to_gradio_chatbot()) + (disable_btn,) * 5 + + try: + # Stream output + response = requests.post(worker_addr + "/worker_generate_stream", headers=headers, json=pload, stream=True, timeout=10) + for chunk in response.iter_lines(decode_unicode=False, delimiter=b"\0"): + if chunk: + data = json.loads(chunk.decode()) + if data["error_code"] == 0: + output = data["text"][len(prompt) :].strip() + state.messages[-1][-1] = output + "▌" + yield (state, state.to_gradio_chatbot()) + (disable_btn,) * 5 + else: + output = data["text"] + f" (error_code: {data['error_code']})" + state.messages[-1][-1] = output + yield (state, state.to_gradio_chatbot()) + (disable_btn, disable_btn, disable_btn, enable_btn, enable_btn) + return + time.sleep(0.03) + except requests.exceptions.RequestException as e: + state.messages[-1][-1] = server_error_msg + yield (state, state.to_gradio_chatbot()) + (disable_btn, disable_btn, disable_btn, enable_btn, enable_btn) + return + + state.messages[-1][-1] = state.messages[-1][-1][:-1] + yield (state, state.to_gradio_chatbot()) + (enable_btn,) * 5 + + finish_tstamp = time.time() + logger.info(f"{output}") + + with open(get_conv_log_filename(), "a") as fout: + data = { + "tstamp": round(finish_tstamp, 4), + "type": "chat", + "model": model_name, + "start": round(start_tstamp, 4), + "finish": round(start_tstamp, 4), + "state": state.dict(), + "images": all_image_hash, + "ip": request.client.host, + } + fout.write(json.dumps(data) + "\n") + + +title_markdown = """ +# 🌋 LLaVA: Large Language and Vision Assistant +[[Project Page](https://llava-vl.github.io)] [[Code](https://github.com/haotian-liu/LLaVA)] [[Model](https://github.com/haotian-liu/LLaVA/blob/main/docs/MODEL_ZOO.md)] | 📚 [[LLaVA](https://arxiv.org/abs/2304.08485)] [[LLaVA-v1.5](https://arxiv.org/abs/2310.03744)] +""" + +tos_markdown = """ +### Terms of use +By using this service, users are required to agree to the following terms: +The service is a research preview intended for non-commercial use only. It only provides limited safety measures and may generate offensive content. It must not be used for any illegal, harmful, violent, racist, or sexual purposes. The service may collect user dialogue data for future research. +Please click the "Flag" button if you get any inappropriate answer! We will collect those to keep improving our moderator. +For an optimal experience, please use desktop computers for this demo, as mobile devices may compromise its quality. +""" + + +learn_more_markdown = """ +### License +The service is a research preview intended for non-commercial use only, subject to the model [License](https://github.com/facebookresearch/llama/blob/main/MODEL_CARD.md) of LLaMA, [Terms of Use](https://openai.com/policies/terms-of-use) of the data generated by OpenAI, and [Privacy Practices](https://chrome.google.com/webstore/detail/sharegpt-share-your-chatg/daiacboceoaocpibfodeljbdfacokfjb) of ShareGPT. Please contact us if you find any potential violation. +""" + +block_css = """ + +#buttons button { + min-width: min(120px,100%); +} + +#chatbot img { + display: inline-block; +} + +""" + + +def build_demo(embed_mode): + textbox = gr.Textbox(show_label=False, placeholder="Enter text and press ENTER", container=False) + with gr.Blocks(title="LLaVA", theme=gr.themes.Default(), css=block_css) as demo: + state = gr.State() + + if not embed_mode: + gr.Markdown(title_markdown) + + with gr.Row(): + with gr.Column(scale=3): + with gr.Row(elem_id="model_selector_row"): + model_selector = gr.Dropdown(choices=models, value=models[0] if len(models) > 0 else "", interactive=True, show_label=False, container=False) + + with gr.Row(elem_id="images"): + imagebox = gr.Image(type="pil") + imagebox_2 = gr.Image(type="pil") + image_process_mode = gr.Radio(["Crop", "Resize", "Pad", "Default"], value="Default", label="Preprocess for non-square image", visible=False) + + cur_dir = os.path.dirname(os.path.abspath(__file__)) + gr.Examples( + examples=[ + [f"{cur_dir}/examples/extreme_ironing.jpg", "What is unusual about this image?"], + [f"{cur_dir}/examples/waterview.jpg", "What are the things I should be cautious about when I visit here?"], + ], + inputs=[imagebox, textbox], + ) + + with gr.Accordion("Parameters", open=False, visible=False) as parameter_row: + temperature = gr.Slider( + minimum=0.0, + maximum=1.0, + value=0.2, + step=0.1, + interactive=True, + label="Temperature", + ) + top_p = gr.Slider( + minimum=0.0, + maximum=1.0, + value=0.7, + step=0.1, + interactive=True, + label="Top P", + ) + max_output_tokens = gr.Slider( + minimum=0, + maximum=1024, + value=512, + step=64, + interactive=True, + label="Max output tokens", + ) + + with gr.Column(scale=8): + chatbot = gr.Chatbot(elem_id="chatbot", label="LLaVA Chatbot", visible=False, height=550) + with gr.Row(): + with gr.Column(scale=8): + textbox.render() + with gr.Column(scale=1, min_width=50): + submit_btn = gr.Button(value="Submit", visible=False) + with gr.Row(visible=False) as button_row: + upvote_btn = gr.Button(value="👍 Upvote", interactive=False) + downvote_btn = gr.Button(value="👎 Downvote", interactive=False) + flag_btn = gr.Button(value="⚠️ Flag", interactive=False) + # stop_btn = gr.Button(value="⏹️ Stop Generation", interactive=False) + regenerate_btn = gr.Button(value="🔄 Regenerate", interactive=False) + clear_btn = gr.Button(value="🗑️ Clear", interactive=False) + + if not embed_mode: + gr.Markdown(tos_markdown) + gr.Markdown(learn_more_markdown) + url_params = gr.JSON(visible=False) + + # Register listeners + btn_list = [upvote_btn, downvote_btn, flag_btn, regenerate_btn, clear_btn] + upvote_btn.click(upvote_last_response, [state, model_selector], [textbox, upvote_btn, downvote_btn, flag_btn]) + downvote_btn.click(downvote_last_response, [state, model_selector], [textbox, upvote_btn, downvote_btn, flag_btn]) + flag_btn.click(flag_last_response, [state, model_selector], [textbox, upvote_btn, downvote_btn, flag_btn]) + regenerate_btn.click(regenerate, [state, image_process_mode], [state, chatbot, textbox, imagebox, imagebox_2] + btn_list).then(http_bot, [state, model_selector, temperature, top_p, max_output_tokens], [state, chatbot] + btn_list) + clear_btn.click(clear_history, None, [state, chatbot, textbox, imagebox, imagebox_2] + btn_list) + + textbox.submit(add_text, [state, textbox, imagebox, imagebox_2, image_process_mode], [state, chatbot, textbox, imagebox, imagebox_2] + btn_list).then( + http_bot, [state, model_selector, temperature, top_p, max_output_tokens], [state, chatbot] + btn_list + ) + submit_btn.click(add_text, [state, textbox, imagebox, imagebox_2, image_process_mode], [state, chatbot, textbox, imagebox, imagebox_2] + btn_list).then( + http_bot, [state, model_selector, temperature, top_p, max_output_tokens], [state, chatbot] + btn_list + ) + + if args.model_list_mode == "once": + demo.load(load_demo, [url_params], [state, model_selector, chatbot, textbox, submit_btn, button_row, parameter_row], _js=get_window_url_params) + elif args.model_list_mode == "reload": + demo.load(load_demo_refresh_model_list, None, [state, model_selector, chatbot, textbox, submit_btn, button_row, parameter_row]) + else: + raise ValueError(f"Unknown model list mode: {args.model_list_mode}") + + return demo + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--host", type=str, default="0.0.0.0") + parser.add_argument("--port", type=int) + parser.add_argument("--controller-url", type=str, default="http://localhost:21001") + parser.add_argument("--concurrency-count", type=int, default=8) + parser.add_argument("--model-list-mode", type=str, default="once", choices=["once", "reload"]) + parser.add_argument("--share", action="store_true") + parser.add_argument("--moderate", action="store_true") + parser.add_argument("--embed", action="store_true") + args = parser.parse_args() + logger.info(f"args: {args}") + + models = get_model_list() + + logger.info(args) + demo = build_demo(args.embed) + demo.queue(concurrency_count=args.concurrency_count, status_update_rate=10, api_open=False).launch(server_name=args.host, server_port=args.port, share=args.share) diff --git a/llava/serve/gradio_web_server.py b/llava/serve/gradio_web_server.py new file mode 100644 index 0000000..4070dfa --- /dev/null +++ b/llava/serve/gradio_web_server.py @@ -0,0 +1,442 @@ +import argparse +import datetime +import json +import os +import time + +import gradio as gr +import requests + +from llava.conversation import default_conversation, conv_templates, SeparatorStyle +from llava.constants import LOGDIR +from llava.utils import build_logger, server_error_msg, violates_moderation, moderation_msg +import hashlib + + +logger = build_logger("gradio_web_server", "gradio_web_server.log") + +headers = {"User-Agent": "LLaVA Client"} + +no_change_btn = gr.Button.update() +enable_btn = gr.Button.update(interactive=True) +disable_btn = gr.Button.update(interactive=False) + +priority = { + "vicuna-13b": "aaaaaaa", + "koala-13b": "aaaaaab", +} + + +def get_conv_log_filename(): + t = datetime.datetime.now() + name = os.path.join(LOGDIR, f"{t.year}-{t.month:02d}-{t.day:02d}-conv.json") + return name + + +def get_model_list(): + ret = requests.post(args.controller_url + "/refresh_all_workers") + assert ret.status_code == 200 + ret = requests.post(args.controller_url + "/list_models") + models = ret.json()["models"] + models.sort(key=lambda x: priority.get(x, x)) + logger.info(f"Models: {models}") + return models + + +get_window_url_params = """ +function() { + const params = new URLSearchParams(window.location.search); + url_params = Object.fromEntries(params); + console.log(url_params); + return url_params; + } +""" + + +def load_demo(url_params, request: gr.Request): + logger.info(f"load_demo. ip: {request.client.host}. params: {url_params}") + + dropdown_update = gr.Dropdown.update(visible=True) + if "model" in url_params: + model = url_params["model"] + if model in models: + dropdown_update = gr.Dropdown.update(value=model, visible=True) + + state = default_conversation.copy() + return state, dropdown_update + + +def load_demo_refresh_model_list(request: gr.Request): + logger.info(f"load_demo. ip: {request.client.host}") + models = get_model_list() + state = default_conversation.copy() + dropdown_update = gr.Dropdown.update(choices=models, value=models[0] if len(models) > 0 else "") + return state, dropdown_update + + +def vote_last_response(state, vote_type, model_selector, request: gr.Request): + with open(get_conv_log_filename(), "a") as fout: + data = { + "tstamp": round(time.time(), 4), + "type": vote_type, + "model": model_selector, + "state": state.dict(), + "ip": request.client.host, + } + fout.write(json.dumps(data) + "\n") + + +def upvote_last_response(state, model_selector, request: gr.Request): + logger.info(f"upvote. ip: {request.client.host}") + vote_last_response(state, "upvote", model_selector, request) + return ("",) + (disable_btn,) * 3 + + +def downvote_last_response(state, model_selector, request: gr.Request): + logger.info(f"downvote. ip: {request.client.host}") + vote_last_response(state, "downvote", model_selector, request) + return ("",) + (disable_btn,) * 3 + + +def flag_last_response(state, model_selector, request: gr.Request): + logger.info(f"flag. ip: {request.client.host}") + vote_last_response(state, "flag", model_selector, request) + return ("",) + (disable_btn,) * 3 + + +def regenerate(state, image_process_mode, request: gr.Request): + logger.info(f"regenerate. ip: {request.client.host}") + state.messages[-1][-1] = None + prev_human_msg = state.messages[-2] + if type(prev_human_msg[1]) in (tuple, list): + prev_human_msg[1] = (*prev_human_msg[1][:2], image_process_mode) + state.skip_next = False + return (state, state.to_gradio_chatbot(), "", None) + (disable_btn,) * 5 + + +def clear_history(request: gr.Request): + logger.info(f"clear_history. ip: {request.client.host}") + state = default_conversation.copy() + return (state, state.to_gradio_chatbot(), "", None) + (disable_btn,) * 5 + + +def add_text(state, text, image, image_process_mode, request: gr.Request): + logger.info(f"add_text. ip: {request.client.host}. len: {len(text)}") + if len(text) <= 0 and image is None: + state.skip_next = True + return (state, state.to_gradio_chatbot(), "", None) + (no_change_btn,) * 5 + if args.moderate: + flagged = violates_moderation(text) + if flagged: + state.skip_next = True + return (state, state.to_gradio_chatbot(), moderation_msg, None) + (no_change_btn,) * 5 + + text = text[:1536] # Hard cut-off + if image is not None: + text = text[:1200] # Hard cut-off for images + if "" not in text: + # text = '' + text + text = text + "\n" + text = (text, image, image_process_mode) + if len(state.get_images(return_pil=True)) > 0: + state = default_conversation.copy() + state.append_message(state.roles[0], text) + state.append_message(state.roles[1], None) + state.skip_next = False + return (state, state.to_gradio_chatbot(), "", None) + (disable_btn,) * 5 + + +def http_bot(state, model_selector, temperature, top_p, max_new_tokens, request: gr.Request, template_name=None): + logger.info(f"http_bot. ip: {request.client.host}") + start_tstamp = time.time() + model_name = model_selector + + if state.skip_next: + # This generate call is skipped due to invalid inputs + yield (state, state.to_gradio_chatbot()) + (no_change_btn,) * 5 + return + + if len(state.messages) == state.offset + 2: + # First round of conversation + if "llava" in model_name.lower(): + if "llama-2" in model_name.lower(): + template_name = "llava_llama_2" + elif "mistral" in model_name.lower() or "mixtral" in model_name.lower(): + if "orca" in model_name.lower(): + template_name = "mistral_orca" + elif "hermes" in model_name.lower(): + template_name = "mistral_direct" + else: + template_name = "mistral_instruct" + elif "zephyr" in model_name.lower(): + template_name = "mistral_zephyr" + elif "hermes" in model_name.lower(): + template_name = "mistral_direct" + elif "v1" in model_name.lower(): + if "mmtag" in model_name.lower(): + template_name = "llava_v1_mmtag" + elif "plain" in model_name.lower() and "finetune" not in model_name.lower(): + template_name = "llava_v1_mmtag" + else: + template_name = "llava_v1" + elif "mpt" in model_name.lower(): + template_name = "mpt" + else: + if "mmtag" in model_name.lower(): + template_name = "v0_plain" + elif "plain" in model_name.lower() and "finetune" not in model_name.lower(): + template_name = "v0_plain" + else: + template_name = "llava_v0" + elif "mistral" in model_name.lower() or "mixtral" in model_name.lower(): + if "orca" in model_name.lower(): + template_name = "mistral_orca" + elif "hermes" in model_name.lower(): + template_name = "mistral_direct" + else: + template_name = "mistral_instruct" + elif "hermes" in model_name.lower(): + template_name = "mistral_direct" + elif "zephyr" in model_name.lower(): + template_name = "mistral_zephyr" + elif "mpt" in model_name: + template_name = "mpt_text" + elif "llama-2" in model_name: + template_name = "llama_2" + else: + template_name = "vicuna_v1" + new_state = conv_templates[template_name].copy() + new_state.append_message(new_state.roles[0], state.messages[-2][1]) + new_state.append_message(new_state.roles[1], None) + state = new_state + + # Query worker address + controller_url = args.controller_url + ret = requests.post(controller_url + "/get_worker_address", json={"model": model_name}) + worker_addr = ret.json()["address"] + logger.info(f"model_name: {model_name}, worker_addr: {worker_addr}") + + # No available worker + if worker_addr == "": + state.messages[-1][-1] = server_error_msg + yield (state, state.to_gradio_chatbot(), disable_btn, disable_btn, disable_btn, enable_btn, enable_btn) + return + + # Construct prompt + prompt = state.get_prompt() + + all_images = state.get_images(return_pil=True) + all_image_hash = [hashlib.md5(image.tobytes()).hexdigest() for image in all_images] + for image, hash in zip(all_images, all_image_hash): + t = datetime.datetime.now() + filename = os.path.join(LOGDIR, "serve_images", f"{t.year}-{t.month:02d}-{t.day:02d}", f"{hash}.jpg") + if not os.path.isfile(filename): + os.makedirs(os.path.dirname(filename), exist_ok=True) + image.save(filename) + + # Make requests + pload = { + "model": model_name, + "prompt": prompt, + "temperature": float(temperature), + "top_p": float(top_p), + "max_new_tokens": min(int(max_new_tokens), 1536), + "stop": state.sep if state.sep_style in [SeparatorStyle.SINGLE, SeparatorStyle.MPT] else state.sep2, + "images": f"List of {len(state.get_images())} images: {all_image_hash}", + } + logger.info(f"==== request ====\n{pload}") + + pload["images"] = state.get_images() + + state.messages[-1][-1] = "▌" + yield (state, state.to_gradio_chatbot()) + (disable_btn,) * 5 + + try: + # Stream output + response = requests.post(worker_addr + "/worker_generate_stream", headers=headers, json=pload, stream=True, timeout=100) + last_print_time = time.time() + for chunk in response.iter_lines(decode_unicode=False, delimiter=b"\0"): + if chunk: + data = json.loads(chunk.decode()) + if data["error_code"] == 0: + output = data["text"][len(prompt) :].strip() + state.messages[-1][-1] = output + "▌" + if time.time() - last_print_time > 0.05: + last_print_time = time.time() + yield (state, state.to_gradio_chatbot()) + (disable_btn,) * 5 + else: + output = data["text"] + f" (error_code: {data['error_code']})" + state.messages[-1][-1] = output + yield (state, state.to_gradio_chatbot()) + (disable_btn, disable_btn, disable_btn, enable_btn, enable_btn) + return + time.sleep(0.03) + except requests.exceptions.RequestException as e: + state.messages[-1][-1] = server_error_msg + yield (state, state.to_gradio_chatbot()) + (disable_btn, disable_btn, disable_btn, enable_btn, enable_btn) + return + + state.messages[-1][-1] = state.messages[-1][-1][:-1] + yield (state, state.to_gradio_chatbot()) + (enable_btn,) * 5 + + finish_tstamp = time.time() + logger.info(f"{output}") + + with open(get_conv_log_filename(), "a") as fout: + data = { + "tstamp": round(finish_tstamp, 4), + "type": "chat", + "model": model_name, + "start": round(start_tstamp, 4), + "finish": round(start_tstamp, 4), + "state": state.dict(), + "images": all_image_hash, + "ip": request.client.host, + } + fout.write(json.dumps(data) + "\n") + + +title_markdown = """ +# 🌋 LLaVA: Large Language and Vision Assistant +[[Project Page](https://llava-vl.github.io)] [[Code](https://github.com/haotian-liu/LLaVA)] [[Model](https://github.com/haotian-liu/LLaVA/blob/main/docs/MODEL_ZOO.md)] | 📚 [[LLaVA](https://arxiv.org/abs/2304.08485)] [[LLaVA-v1.5](https://arxiv.org/abs/2310.03744)] +""" + +tos_markdown = """ +### Terms of use +By using this service, users are required to agree to the following terms: +The service is a research preview intended for non-commercial use only. It only provides limited safety measures and may generate offensive content. It must not be used for any illegal, harmful, violent, racist, or sexual purposes. The service may collect user dialogue data for future research. +Please click the "Flag" button if you get any inappropriate answer! We will collect those to keep improving our moderator. +For an optimal experience, please use desktop computers for this demo, as mobile devices may compromise its quality. +""" + + +learn_more_markdown = """ +### License +The service is a research preview intended for non-commercial use only, subject to the model [License](https://github.com/facebookresearch/llama/blob/main/MODEL_CARD.md) of LLaMA, [Terms of Use](https://openai.com/policies/terms-of-use) of the data generated by OpenAI, and [Privacy Practices](https://chrome.google.com/webstore/detail/sharegpt-share-your-chatg/daiacboceoaocpibfodeljbdfacokfjb) of ShareGPT. Please contact us if you find any potential violation. +""" + +block_css = """ + +#buttons button { + min-width: min(120px,100%); +} + +""" + + +def build_demo(embed_mode): + textbox = gr.Textbox(show_label=False, placeholder="Enter text and press ENTER", container=False) + with gr.Blocks(title="LLaVA", theme=gr.themes.Default(), css=block_css) as demo: + state = gr.State() + + if not embed_mode: + gr.Markdown(title_markdown) + + with gr.Row(): + with gr.Column(scale=3): + with gr.Row(elem_id="model_selector_row"): + model_selector = gr.Dropdown(choices=models, value=models[0] if len(models) > 0 else "", interactive=True, show_label=False, container=False) + + imagebox = gr.Image(type="pil") + image_process_mode = gr.Radio(["Crop", "Resize", "Pad", "Default"], value="Default", label="Preprocess for non-square image", visible=False) + + cur_dir = os.path.dirname(os.path.abspath(__file__)) + gr.Examples( + examples=[ + [f"{cur_dir}/examples/extreme_ironing.jpg", "What is unusual about this image?"], + [f"{cur_dir}/examples/waterview.jpg", "What are the things I should be cautious about when I visit here?"], + ], + inputs=[imagebox, textbox], + ) + + with gr.Accordion("Parameters", open=False) as parameter_row: + temperature = gr.Slider( + minimum=0.0, + maximum=1.0, + value=0.2, + step=0.1, + interactive=True, + label="Temperature", + ) + top_p = gr.Slider( + minimum=0.0, + maximum=1.0, + value=0.7, + step=0.1, + interactive=True, + label="Top P", + ) + max_output_tokens = gr.Slider( + minimum=0, + maximum=1024, + value=512, + step=64, + interactive=True, + label="Max output tokens", + ) + + with gr.Column(scale=8): + chatbot = gr.Chatbot(elem_id="chatbot", label="LLaVA Chatbot", height=550) + with gr.Row(): + with gr.Column(scale=8): + textbox.render() + with gr.Column(scale=1, min_width=50): + submit_btn = gr.Button(value="Send", variant="primary") + with gr.Row(elem_id="buttons") as button_row: + upvote_btn = gr.Button(value="👍 Upvote", interactive=False) + downvote_btn = gr.Button(value="👎 Downvote", interactive=False) + flag_btn = gr.Button(value="⚠️ Flag", interactive=False) + # stop_btn = gr.Button(value="⏹️ Stop Generation", interactive=False) + regenerate_btn = gr.Button(value="🔄 Regenerate", interactive=False) + clear_btn = gr.Button(value="🗑️ Clear", interactive=False) + + if not embed_mode: + gr.Markdown(tos_markdown) + gr.Markdown(learn_more_markdown) + url_params = gr.JSON(visible=False) + + # Register listeners + btn_list = [upvote_btn, downvote_btn, flag_btn, regenerate_btn, clear_btn] + upvote_btn.click(upvote_last_response, [state, model_selector], [textbox, upvote_btn, downvote_btn, flag_btn], queue=False) + downvote_btn.click(downvote_last_response, [state, model_selector], [textbox, upvote_btn, downvote_btn, flag_btn], queue=False) + flag_btn.click(flag_last_response, [state, model_selector], [textbox, upvote_btn, downvote_btn, flag_btn], queue=False) + + regenerate_btn.click(regenerate, [state, image_process_mode], [state, chatbot, textbox, imagebox] + btn_list, queue=False).then(http_bot, [state, model_selector, temperature, top_p, max_output_tokens], [state, chatbot] + btn_list) + + clear_btn.click(clear_history, None, [state, chatbot, textbox, imagebox] + btn_list, queue=False) + + textbox.submit(add_text, [state, textbox, imagebox, image_process_mode], [state, chatbot, textbox, imagebox] + btn_list, queue=False).then( + http_bot, [state, model_selector, temperature, top_p, max_output_tokens], [state, chatbot] + btn_list + ) + + submit_btn.click(add_text, [state, textbox, imagebox, image_process_mode], [state, chatbot, textbox, imagebox] + btn_list, queue=False).then( + http_bot, [state, model_selector, temperature, top_p, max_output_tokens], [state, chatbot] + btn_list + ) + + if args.model_list_mode == "once": + demo.load(load_demo, [url_params], [state, model_selector], _js=get_window_url_params, queue=False) + elif args.model_list_mode == "reload": + demo.load(load_demo_refresh_model_list, None, [state, model_selector], queue=False) + else: + raise ValueError(f"Unknown model list mode: {args.model_list_mode}") + + return demo + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--host", type=str, default="0.0.0.0") + parser.add_argument("--port", type=int) + parser.add_argument("--controller-url", type=str, default="http://localhost:21001") + parser.add_argument("--concurrency-count", type=int, default=10) + parser.add_argument("--model-list-mode", type=str, default="once", choices=["once", "reload"]) + parser.add_argument("--share", action="store_true") + parser.add_argument("--moderate", action="store_true") + parser.add_argument("--embed", action="store_true") + args = parser.parse_args() + logger.info(f"args: {args}") + + models = get_model_list() + + logger.info(args) + demo = build_demo(args.embed) + demo.queue(concurrency_count=args.concurrency_count, api_open=False).launch(server_name=args.host, server_port=args.port, share=args.share) diff --git a/llava/serve/model_worker.py b/llava/serve/model_worker.py new file mode 100644 index 0000000..cd22561 --- /dev/null +++ b/llava/serve/model_worker.py @@ -0,0 +1,271 @@ +""" +A model worker executes the model. +""" + +import argparse +import asyncio +import json +import time +import threading +import uuid + +from fastapi import FastAPI, Request, BackgroundTasks +from fastapi.responses import StreamingResponse +import requests +import torch +import uvicorn +from functools import partial + +from llava.constants import WORKER_HEART_BEAT_INTERVAL +from llava.utils import build_logger, server_error_msg, pretty_print_semaphore +from llava.model.builder import load_pretrained_model +from llava.mm_utils import process_images, load_image_from_base64, tokenizer_image_token, KeywordsStoppingCriteria +from llava.constants import IMAGE_TOKEN_INDEX, DEFAULT_IMAGE_TOKEN, DEFAULT_IM_START_TOKEN, DEFAULT_IM_END_TOKEN +from transformers import TextIteratorStreamer +from threading import Thread + + +GB = 1 << 30 + +worker_id = str(uuid.uuid4())[:6] +logger = build_logger("model_worker", f"model_worker_{worker_id}.log") +global_counter = 0 + +model_semaphore = None + + +def heart_beat_worker(controller): + + while True: + time.sleep(WORKER_HEART_BEAT_INTERVAL) + controller.send_heart_beat() + + +class ModelWorker: + def __init__(self, controller_addr, worker_addr, worker_id, no_register, model_path, model_base, model_name, load_8bit, load_4bit): + self.controller_addr = controller_addr + self.worker_addr = worker_addr + self.worker_id = worker_id + if model_path.endswith("/"): + model_path = model_path[:-1] + if model_name is None: + model_paths = model_path.split("/") + if model_paths[-1].startswith("checkpoint-"): + self.model_name = model_paths[-2] + "_" + model_paths[-1] + else: + self.model_name = model_paths[-1] + else: + self.model_name = model_name + + logger.info(f"Loading the model {self.model_name} on worker {worker_id} ...") + self.tokenizer, self.model, self.image_processor, self.context_len = load_pretrained_model(model_path, model_base, self.model_name, load_8bit, load_4bit) + self.is_multimodal = "llava" in self.model_name.lower() + + if not no_register: + self.register_to_controller() + self.heart_beat_thread = threading.Thread(target=heart_beat_worker, args=(self,)) + self.heart_beat_thread.start() + + def register_to_controller(self): + logger.info("Register to controller") + + url = self.controller_addr + "/register_worker" + data = {"worker_name": self.worker_addr, "check_heart_beat": True, "worker_status": self.get_status()} + r = requests.post(url, json=data) + assert r.status_code == 200 + + def send_heart_beat(self): + logger.info(f"Send heart beat. Models: {[self.model_name]}. " f"Semaphore: {pretty_print_semaphore(model_semaphore)}. " f"global_counter: {global_counter}") + + url = self.controller_addr + "/receive_heart_beat" + + while True: + try: + ret = requests.post(url, json={"worker_name": self.worker_addr, "queue_length": self.get_queue_length()}, timeout=5) + exist = ret.json()["exist"] + break + except requests.exceptions.RequestException as e: + logger.error(f"heart beat error: {e}") + time.sleep(5) + + if not exist: + self.register_to_controller() + + def get_queue_length(self): + if model_semaphore is None: + return 0 + else: + return args.limit_model_concurrency - model_semaphore._value + (len(model_semaphore._waiters) if model_semaphore._waiters is not None else 0) + + def get_status(self): + return { + "model_names": [self.model_name], + "speed": 1, + "queue_length": self.get_queue_length(), + } + + @torch.inference_mode() + def generate_stream(self, params): + tokenizer, model, image_processor = self.tokenizer, self.model, self.image_processor + + prompt = params["prompt"] + ori_prompt = prompt + images = params.get("images", None) + num_image_tokens = 0 + if images is not None and len(images) > 0 and self.is_multimodal: + if len(images) > 0: + if len(images) != prompt.count(DEFAULT_IMAGE_TOKEN): + raise ValueError("Number of images does not match number of tokens in prompt") + + images = [load_image_from_base64(image) for image in images] + image_sizes = [image.size for image in images] + images = process_images(images, image_processor, model.config) + + if type(images) is list: + images = [image.to(self.model.device, dtype=torch.float16) for image in images] + else: + images = images.to(self.model.device, dtype=torch.float16) + + replace_token = DEFAULT_IMAGE_TOKEN + if getattr(self.model.config, "mm_use_im_start_end", False): + replace_token = DEFAULT_IM_START_TOKEN + replace_token + DEFAULT_IM_END_TOKEN + prompt = prompt.replace(DEFAULT_IMAGE_TOKEN, replace_token) + + num_image_tokens = prompt.count(replace_token) * model.get_vision_tower().num_patches + else: + images = None + image_sizes = None + image_args = {"images": images, "image_sizes": image_sizes} + else: + images = None + image_args = {} + + temperature = float(params.get("temperature", 1.0)) + top_p = float(params.get("top_p", 1.0)) + max_context_length = getattr(model.config, "max_position_embeddings", 2048) + max_new_tokens = min(int(params.get("max_new_tokens", 256)), 1024) + stop_str = params.get("stop", None) + do_sample = True if temperature > 0.001 else False + + input_ids = tokenizer_image_token(prompt, tokenizer, IMAGE_TOKEN_INDEX, return_tensors="pt").unsqueeze(0).cuda() + keywords = [stop_str] + stopping_criteria = KeywordsStoppingCriteria(keywords, tokenizer, input_ids) + streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True, timeout=15) + + max_new_tokens = min(max_new_tokens, max_context_length - input_ids.shape[-1] - num_image_tokens) + + if max_new_tokens < 1: + yield json.dumps({"text": ori_prompt + "Exceeds max token length. Please start a new conversation, thanks.", "error_code": 0}).encode() + b"\0" + return + + thread = Thread( + target=model.generate, + kwargs=dict( + inputs=input_ids, + do_sample=do_sample, + temperature=temperature, + top_p=top_p, + max_new_tokens=max_new_tokens, + streamer=streamer, + # stopping_criteria=[stopping_criteria], + use_cache=True, + **image_args, + ), + ) + thread.start() + + start_time = time.time() + generated_text = ori_prompt + for new_text in streamer: + generated_text += new_text + if generated_text.endswith(stop_str): + generated_text = generated_text[: -len(stop_str)] + yield json.dumps({"text": generated_text, "error_code": 0}).encode() + b"\0" + + end_time = time.time() + + new_generated = generated_text[len(ori_prompt) :] + new_generated_tokens = tokenizer(new_generated).input_ids + token_per_second = len(new_generated_tokens) / (end_time - start_time) + print(f"token_per_second: {token_per_second}") + + def generate_stream_gate(self, params): + try: + for x in self.generate_stream(params): + yield x + except ValueError as e: + print("Caught ValueError:", e) + ret = { + "text": server_error_msg, + "error_code": 1, + } + yield json.dumps(ret).encode() + b"\0" + except torch.cuda.CudaError as e: + print("Caught torch.cuda.CudaError:", e) + ret = { + "text": server_error_msg, + "error_code": 1, + } + yield json.dumps(ret).encode() + b"\0" + except Exception as e: + print("Caught Unknown Error", e) + ret = { + "text": server_error_msg, + "error_code": 1, + } + yield json.dumps(ret).encode() + b"\0" + + +app = FastAPI() + + +def release_model_semaphore(fn=None): + model_semaphore.release() + if fn is not None: + fn() + + +@app.post("/worker_generate_stream") +async def generate_stream(request: Request): + global model_semaphore, global_counter + global_counter += 1 + params = await request.json() + + if model_semaphore is None: + model_semaphore = asyncio.Semaphore(args.limit_model_concurrency) + await model_semaphore.acquire() + worker.send_heart_beat() + generator = worker.generate_stream_gate(params) + background_tasks = BackgroundTasks() + background_tasks.add_task(partial(release_model_semaphore, fn=worker.send_heart_beat)) + return StreamingResponse(generator, background=background_tasks) + + +@app.post("/worker_get_status") +async def get_status(request: Request): + return worker.get_status() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--host", type=str, default="localhost") + parser.add_argument("--port", type=int, default=21002) + parser.add_argument("--worker-address", type=str, default="http://localhost:21002") + parser.add_argument("--controller-address", type=str, default="http://localhost:21001") + parser.add_argument("--model-path", type=str, default="facebook/opt-350m") + parser.add_argument("--model-base", type=str, default=None) + parser.add_argument("--model-name", type=str) + parser.add_argument("--multi-modal", action="store_true", help="Multimodal mode is automatically detected with model name, please make sure `llava` is included in the model path.") + parser.add_argument("--limit-model-concurrency", type=int, default=5) + parser.add_argument("--stream-interval", type=int, default=1) + parser.add_argument("--no-register", action="store_true") + parser.add_argument("--load-8bit", action="store_true") + parser.add_argument("--load-4bit", action="store_true") + args = parser.parse_args() + logger.info(f"args: {args}") + + if args.multi_modal: + logger.warning("Multimodal mode is automatically detected with model name, please make sure `llava` is included in the model path.") + + worker = ModelWorker(args.controller_address, args.worker_address, worker_id, args.no_register, args.model_path, args.model_base, args.model_name, args.load_8bit, args.load_4bit) + uvicorn.run(app, host=args.host, port=args.port, log_level="info") diff --git a/llava/serve/register_worker.py b/llava/serve/register_worker.py new file mode 100644 index 0000000..2c2c402 --- /dev/null +++ b/llava/serve/register_worker.py @@ -0,0 +1,26 @@ +""" +Manually register workers. + +Usage: +python3 -m fastchat.serve.register_worker --controller http://localhost:21001 --worker-name http://localhost:21002 +""" + +import argparse + +import requests + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--controller-address", type=str) + parser.add_argument("--worker-name", type=str) + parser.add_argument("--check-heart-beat", action="store_true") + args = parser.parse_args() + + url = args.controller_address + "/register_worker" + data = { + "worker_name": args.worker_name, + "check_heart_beat": args.check_heart_beat, + "worker_status": None, + } + r = requests.post(url, json=data) + assert r.status_code == 200 diff --git a/llava/serve/sglang_worker.py b/llava/serve/sglang_worker.py new file mode 100644 index 0000000..09c047f --- /dev/null +++ b/llava/serve/sglang_worker.py @@ -0,0 +1,237 @@ +""" +A model worker executes the model. +""" + +import argparse +import asyncio +from concurrent.futures import ThreadPoolExecutor +import json +import time +import threading +import uuid + +from fastapi import FastAPI, Request, BackgroundTasks +from fastapi.responses import StreamingResponse +import requests +import re +import uvicorn +from functools import partial + +from llava.constants import WORKER_HEART_BEAT_INTERVAL +from llava.utils import build_logger, server_error_msg, pretty_print_semaphore +from llava.model.builder import load_pretrained_model +from llava.mm_utils import process_images, load_image_from_base64, tokenizer_image_token, expand2square +from llava.constants import IMAGE_TOKEN_INDEX, DEFAULT_IMAGE_TOKEN, DEFAULT_IM_START_TOKEN, DEFAULT_IM_END_TOKEN +from transformers import AutoTokenizer + +import sglang as sgl +from sglang.test.test_utils import add_common_sglang_args_and_parse, select_sglang_backend +from sglang.backend.runtime_endpoint import RuntimeEndpoint +from sglang.utils import read_jsonl, dump_state_text +from sglang.lang.interpreter import ProgramState + + +GB = 1 << 30 + +worker_id = str(uuid.uuid4())[:6] +logger = build_logger("model_worker", f"model_worker_{worker_id}.log") +global_counter = 0 + +model_semaphore = None + + +def heart_beat_worker(controller): + while True: + time.sleep(WORKER_HEART_BEAT_INTERVAL) + controller.send_heart_beat() + + +@sgl.function +def pipeline(s, prompt, max_tokens): + for p in prompt: + if type(p) is str: + s += p + else: + s += sgl.image(p) + s += sgl.gen("response", max_tokens=max_tokens) + + +class ModelWorker: + def __init__(self, controller_addr, worker_addr, sgl_endpoint, worker_id, no_register, model_name): + self.controller_addr = controller_addr + self.worker_addr = worker_addr + self.worker_id = worker_id + + # Select backend + backend = RuntimeEndpoint(sgl_endpoint) + sgl.set_default_backend(backend) + model_path = backend.model_info["model_path"] + + if model_path.endswith("/"): + model_path = model_path[:-1] + if model_name is None: + model_paths = model_path.split("/") + if model_paths[-1].startswith("checkpoint-"): + self.model_name = model_paths[-2] + "_" + model_paths[-1] + else: + self.model_name = model_paths[-1] + else: + self.model_name = model_name + + logger.info(f"Loading the SGLANG model {self.model_name} on worker {worker_id} ...") + + if not no_register: + self.register_to_controller() + self.heart_beat_thread = threading.Thread(target=heart_beat_worker, args=(self,)) + self.heart_beat_thread.start() + + def register_to_controller(self): + logger.info("Register to controller") + + url = self.controller_addr + "/register_worker" + data = {"worker_name": self.worker_addr, "check_heart_beat": True, "worker_status": self.get_status()} + r = requests.post(url, json=data) + assert r.status_code == 200 + + def send_heart_beat(self): + logger.info(f"Send heart beat. Models: {[self.model_name]}. " f"Semaphore: {pretty_print_semaphore(model_semaphore)}. " f"global_counter: {global_counter}") + + url = self.controller_addr + "/receive_heart_beat" + + while True: + try: + ret = requests.post(url, json={"worker_name": self.worker_addr, "queue_length": self.get_queue_length()}, timeout=5) + exist = ret.json()["exist"] + break + except requests.exceptions.RequestException as e: + logger.error(f"heart beat error: {e}") + time.sleep(5) + + if not exist: + self.register_to_controller() + + def get_queue_length(self): + if model_semaphore is None: + return 0 + else: + return args.limit_model_concurrency - model_semaphore._value + (len(model_semaphore._waiters) if model_semaphore._waiters is not None else 0) + + def get_status(self): + return { + "model_names": [self.model_name], + "speed": 1, + "queue_length": self.get_queue_length(), + } + + async def generate_stream(self, params): + ori_prompt = prompt = params["prompt"] + images = params.get("images", None) + if images is not None and len(images) > 0: + if len(images) > 0: + if len(images) != prompt.count(DEFAULT_IMAGE_TOKEN): + raise ValueError("Number of images does not match number of tokens in prompt") + + images = [load_image_from_base64(image) for image in images] + # FIXME: hacky padding + images = [expand2square(image, tuple(int(x * 255) for x in [0.48145466, 0.4578275, 0.40821073])) for image in images] + + # FIXME: for image-start/end token + # replace_token = DEFAULT_IMAGE_TOKEN + # if getattr(self.model.config, 'mm_use_im_start_end', False): + # replace_token = DEFAULT_IM_START_TOKEN + replace_token + DEFAULT_IM_END_TOKEN + # prompt = prompt.replace(DEFAULT_IMAGE_TOKEN, replace_token) + prompt = prompt.replace(" " + DEFAULT_IMAGE_TOKEN + "\n", DEFAULT_IMAGE_TOKEN) + prompt_split = prompt.split(DEFAULT_IMAGE_TOKEN) + prompt = [] + for i in range(len(prompt_split)): + prompt.append(prompt_split[i]) + if i < len(images): + prompt.append(images[i]) + else: + prompt = [prompt] + + temperature = float(params.get("temperature", 1.0)) + top_p = float(params.get("top_p", 1.0)) + # max_context_length = getattr(model.config, 'max_position_embeddings', 2048) + max_new_tokens = min(int(params.get("max_new_tokens", 256)), 1024) + stop_str = params.get("stop", None) + stop_str = [stop_str] if stop_str is not None else None + + if max_new_tokens < 1: + yield json.dumps({"text": ori_prompt + "Exceeds max token length. Please start a new conversation, thanks.", "error_code": 0}).encode() + b"\0" + return + + # print(prompt) + state = pipeline.run(prompt, max_new_tokens, temperature=temperature, top_p=top_p, stream=True) + + generated_text = ori_prompt + async for text_outputs in state.text_async_iter(var_name="response"): + generated_text += text_outputs + yield json.dumps({"text": generated_text, "error_code": 0}).encode() + b"\0" + + async def generate_stream_gate(self, params): + try: + async for x in self.generate_stream(params): + yield x + except ValueError as e: + print("Caught ValueError:", e) + ret = { + "text": server_error_msg, + "error_code": 1, + } + yield json.dumps(ret).encode() + b"\0" + except Exception as e: + print("Caught Unknown Error", e) + ret = { + "text": server_error_msg, + "error_code": 1, + } + yield json.dumps(ret).encode() + b"\0" + + +app = FastAPI() + + +def release_model_semaphore(fn=None): + model_semaphore.release() + if fn is not None: + fn() + + +@app.post("/worker_generate_stream") +async def generate_stream(request: Request): + global model_semaphore, global_counter + global_counter += 1 + params = await request.json() + + if model_semaphore is None: + model_semaphore = asyncio.Semaphore(args.limit_model_concurrency) + await model_semaphore.acquire() + worker.send_heart_beat() + generator = worker.generate_stream_gate(params) + background_tasks = BackgroundTasks() + background_tasks.add_task(partial(release_model_semaphore, fn=worker.send_heart_beat)) + return StreamingResponse(generator, background=background_tasks) + + +@app.post("/worker_get_status") +async def get_status(request: Request): + return worker.get_status() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--host", type=str, default="localhost") + parser.add_argument("--port", type=int, default=21002) + parser.add_argument("--worker-address", type=str, default="http://localhost:21002") + parser.add_argument("--controller-address", type=str, default="http://localhost:21001") + parser.add_argument("--model-name", type=str) + parser.add_argument("--sgl-endpoint", type=str) + parser.add_argument("--limit-model-concurrency", type=int, default=5) + parser.add_argument("--stream-interval", type=int, default=1) + parser.add_argument("--no-register", action="store_true") + args = parser.parse_args() + logger.info(f"args: {args}") + + worker = ModelWorker(args.controller_address, args.worker_address, args.sgl_endpoint, worker_id, args.no_register, args.model_name) + uvicorn.run(app, host=args.host, port=args.port, log_level="info") diff --git a/llava/serve/test_message.py b/llava/serve/test_message.py new file mode 100644 index 0000000..45acd53 --- /dev/null +++ b/llava/serve/test_message.py @@ -0,0 +1,59 @@ +import argparse +import json + +import requests + +from llava.conversation import default_conversation + + +def main(): + if args.worker_address: + worker_addr = args.worker_address + else: + controller_addr = args.controller_address + ret = requests.post(controller_addr + "/refresh_all_workers") + ret = requests.post(controller_addr + "/list_models") + models = ret.json()["models"] + models.sort() + print(f"Models: {models}") + + ret = requests.post(controller_addr + "/get_worker_address", json={"model": args.model_name}) + worker_addr = ret.json()["address"] + print(f"worker_addr: {worker_addr}") + + if worker_addr == "": + return + + conv = default_conversation.copy() + conv.append_message(conv.roles[0], args.message) + prompt = conv.get_prompt() + + headers = {"User-Agent": "LLaVA Client"} + pload = { + "model": args.model_name, + "prompt": prompt, + "max_new_tokens": args.max_new_tokens, + "temperature": 0.7, + "stop": conv.sep, + } + response = requests.post(worker_addr + "/worker_generate_stream", headers=headers, json=pload, stream=True) + + print(prompt.replace(conv.sep, "\n"), end="") + for chunk in response.iter_lines(chunk_size=8192, decode_unicode=False, delimiter=b"\0"): + if chunk: + data = json.loads(chunk.decode("utf-8")) + output = data["text"].split(conv.sep)[-1] + print(output, end="\r") + print("") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--controller-address", type=str, default="http://localhost:21001") + parser.add_argument("--worker-address", type=str) + parser.add_argument("--model-name", type=str, default="facebook/opt-350m") + parser.add_argument("--max-new-tokens", type=int, default=32) + parser.add_argument("--message", type=str, default="Tell me a story with more than 1000 words.") + args = parser.parse_args() + + main() diff --git a/llava/train/__pycache__/llava_trainer.cpython-310.pyc b/llava/train/__pycache__/llava_trainer.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..44bb8db965b5d117cb1e4d56da8a02692cfc037b GIT binary patch literal 21736 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 literal 0 HcmV?d00001 diff --git a/llava/train/__pycache__/train.cpython-310.pyc b/llava/train/__pycache__/train.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8220eb1eb5666129172d001edf15b3dd55ccc460 GIT binary patch literal 47506 zcmeIbcbr_ubtc+a560MEy)&nMv@Uc1NmqnmW&lzldXk# zGG1s)wn_S6zP-?q>?m|5JLNr;?<#aByS==6lD(2gU$Rf0{mFiL4kQQUIhY)j=TLG; zp2Nvud9F#Wk>^NqM4oGtYw--{*AzyRqmp+^eqAAvOo%^{Utide+)&t<+*r6Pc~@am za#LY*alj>695&cd$buEOr* z?!unr9*J+y?=9S$ytl9~xvy|v@;<*WGo0JM_&(Kf+mHCpOlRtV@~7|5bT1yvjN~4; z?Wa$5-S%G!B_GTLkNZ@&>bdDtJvXcy{^Uav(yRIq(kCGgOGv*OK*&I5O>W{w0O=-F zQVpr$oBre>wMLEH^d%3g=hRv?ijX5}ok}3&s9LW!;CW1K#CONlU71IwgKCr7d^4Cl zp|)gVYAeDYRby};n+~hH)jfzgskUX}nMa0vUhMWv3~}RX2jWhtooW|yJdLt;t34>| zakbY=eQ)MrWzz0b_aW^W^}LtE{>&3z%=^@Vo1x@c#O%$q;V+hX1a-28eEeIII->4Z z2a)=mI^PiUQ05WIE217&6NsH$CH9ayjMyimgV6_~N7M@|=5SOULz<^nsONF@2x6XI zA?Ad76fw`#T5WpJwEHo2677CgTG!Zv)%Hi!DRmlOCo$%at1}qSGnvGVK=L^m;V0Bt zgq+PhFXLNnGjk5Ab7~Sfzo4E}PoeiyDy5!Q&!G2T%%s(`m#o+Q`Y$st-tb|*`OnF3 z?8UdZ-cOHNZd9e6^i)1=+iqw&o5`!Opc_1ropRjB+af2tb zwu7%iCkpAAj2o55v1~pw=672zXS9tE;EhepJEg;=;&gU~5l2vjopHuOZv4dY(~qA$ znmTd%$kAuqo+C$(O+0yWGIiqA#POr4$;Tf%dfM%;2d2(VPMn?e!h0LSkDfl_g?2T3 zYH~$^@slUhPfbkfbhen$c<-2)VoN4Vmomj{S%TtIr6TK_W(1P>??$`(W_4*QW80~V z*_n$vW2dsx%HC>lx_GS~ny7|0B~DH0(rn6}&1W6Aw@GD^w&Sio)7>EflGsac&Z zI_^LcZJkD!r|fIClPOG=gFDA}?VaL)HvS9Z z3w*%byHpLYeT8FVms|2N@bvAr;R0Rm&|a8L7oZYH?97 zE?SF=NnESs7OREECA3XKTe0|CRC_~6n}l>UgtSXYr-TIMtBzV;T@9g~wb1T{ue;>y zo`#Ta3F(!PKFO~KU-n8%`y2A>tK~N!p@WjPzZN>wkmrEpIV>S-B;BBdj7Z2@2^p$= zIVz#+By_kInvhV_!Zo$f^%A;4(vD=p=&g+sa+idx&5R;slZ0%RkaZHWr6I2b@*0)a zZEXl$Uke>;2;G2C(-U_~=sl9>Mk#k&L%O?a>9$MgxTM=u3*8~1J0)~;Ep(TJ?v~Ik z2sO3Z(~$30gqk_Gw;|uLS`PO%85bYDXb_tbK@uOW0BLQQG=8$!3&(!NhZ&72%Z zXk7a1fP~&J_1#fRd$1wx&RW_BB=kW^y9=SF7ax+)hb44(Er$sSHS=UoEr&x5`R=WS z9+uD}lEb~Ve2*UVso*)^*fF>LR7qv>6MAO8kSRKLxobK-mAOzVUD{Eab++%?eQ)7S z09cioPS58Zx4Xd9sbad2NtJYJHtk$I?FKJq^0Va+KWS%F!YL)P#jKN%bO{0b+Y-|y zT}V5LtetoU|BjXP;q<(n&YwIrp2&>Pj3=h%wMOCjYZA|NQx{8irkI%4r9$FlK7Bb2 zr;y%e{HL;49i7hC-t5|2A^oPM6M#u>q>ypa#5``Nsj^f>DqjBL;nI9wB`#zV8w!O~ zAwQc!%2_9)H*AxT%UNQX6d-ZBFq>Z*?3Au#^tR>?`I=7`XXb&kQc_-XR{x}L8Y;9x zzGzP&<;)k;7XYAV)7r6J>+()bdi$8$F|9M1awc^lJ$30qshDwlkPp_rRf7Y#;qgVXu6 zBcGViLPletB%hFkxP!~8Hl0a3^ExAC*QSE^&2;uEx`DMSr0q*7MU>28pTQ`|KX;@l z&c2kLP0eQX6lOW5h?nfQ9>RKR%tDh`kgC+Pm9W(OEWTbbCeP_8J!_*S*>pZNTPo$z zCmPt^ja^rda^tDg+Fk8#20$+AU3}d`XD^+5>D))>eRK}c zIY{RLIH2g!SVup|fQRTjOlN}5VLC_X9Ha9Hok!_BM&~4*Q*=(#d7RD}I#19!OXnP& zC+R#*=NUTB(s>Tfn?9}Rr*mij4qYlaJEkzBfweNl%lY*7Jh8?Oq0_gY&RlhNm;eD` zJH#EIy{1R;`Th9WFTjcU`mGVaWkvkm@AQYd{rv04U$XraudB)n9-y z!+t4i3dJ=2<8pZzKYPT#oQPHdkiMtDs8Q9<{9 z6$1fI;y46CB&=GIBA#p`0ioI&Qjjz@DGUj1zUx3}r+jC~XcJ08*n}EV+JurAHaQql zx?4)_siif9bFYN<)EhY|w4vHm^phoYouAZ{;gz zK!cF%a4u#NGOk#?31a?jphOZf>RW7hv0Ub5mo}cWOCT+TCK*qh186O2haVA0Wr6S$ zCINOpcH(k2o!9}udU1zS+VN6)+l6gY3zC7}h|&YA-+a1!jcC%_R=I6iTL51c z3mTi;c3`snwNxQ{HRWJJ>UGQ@E<3=~bSbX@#=|z&J8(|72~FFxM3M$A^qMe2pA;a} zYzd3icKb}IYzl#;O8I$#MExdMwuvNUs^LKJ+yk28-olha6TF~VTg!Xv1OF-siLGU&7p&Lj4O#$o*g)}yxpGQ{uJRPni1FMMUbc)W4bS~0K)5+4w(YZv2 z;7k|j6zP=cT%a>cXO50d$DuP%=Q5qz`ZvA|!=~X}!O!Nxj`;?yxSxOh@{<+HRcY2E z)2_ZAzr4bFTyay&tu9t1wozE0*ye_X)Q*C;)WTI-3u%=Qv)wgm;u2z3Y)rnA6)IZ? ztXO;*S8b{tFrWi4pgq%p9L?J6LU^|q-YL0M*IAqD^TNBNPG(IFApM{h-YxlH^9h*I zBi~}%sn#TWB^(=0HIVER|62G*ll_?il)Dc8L~>B_#FkQRNDhe~n@QzQauYfr^}b8q zx&1WnW@~BMJ0hiRZs?P>5@I%+Ce5gXn2n|hStlW8li4M`l#md!!8B>sOUONv^ER|~ zyVuqYQpa)D2@qt5+cilF7Hho$ipVmIPNkPAvldaec6s~}9)KeGT5H>;C!ZEi` zX9}gunbdp{gn~*s? zF`-KH*mEQrla0G^6G}P-tfoM5dT;rmCmJ&%Buue5U9Q)&d0lXB`lRGLY`t)nZa?-fsY?R>F_GO4P~{h}Sq9ZPGsA!`>~-vmoLWfCeeCY? z-A~$r8;iCfvh74IHo@7K;nrWqecbJ-&B=P`*v1v%`&G_a0p4{We~n>fIs)lmqOU^d z{d7J+M?m{^`aVSGCY=w{d6~{DbY7)H*sQ;W&Mi70qw{e(uhaQfI-j8PNjl#~=TmgP zgU(90fq3CH{A|*&r1cDiM=H+3U)bONjltlce^4-n!5eXZC;qDG_#E+T=5uT+0Jr*& zeGtL#Fn;zT9CASOzJh-tK$bYfQ&u6c5cK<;UU?_;c8z`sZ zJLlWwvmd&G@A&sDehjPln!jP#SopLcd+?=$U{{yO!cO9OCJo$w4pU*?PKFfN)2h7p z0N@Kk$MH&_FmET;Kag0Gc>MI^lc|ZPCQh83ICS#pfy7ulD5Fef7W7L->F-33B=4!c zj{`E1o)~Az<78pT?upC{5FXes+4{Tih1+s54eq(4lhIny$+o6~+?ML3Z@H4zgzIki zanNcRol(*h$;}N4A1JKA5dugLOwG=_p{YC;IphV{{-acoYBR->20bG!1PDM)q~21M zhUr8q!;Kfx*DhpItbc0H7ZGBQz~PjMf#3*YLbdyQ{jqm~!OpMY_fF8Se-H_#EI8GF zoG&E)WzPHtJQct4V>ZN5#RZZ&sz(j1#A*3q088kRl*gk0^+0+S_Jp~wqJqVR?z|9LQ5sMXjKs6_swlm zQAzy>QU{oNZc``PF(_e-v$+ zB1KvK#}&gCb6+R)Sr)B4--`UvPKEX(f2-oJSf8}!kO$a~A%Vft`)>wZYka5czvTLz zrNa?Bs31<70xo;-%gFm}cJrvOykjN5ukI-g&? z#+#9=)VQOu#&)~%FRl5Qr9X?r#7}A4RQ650FR8Zv5rq8~em3j7*_SjJr&P>elh0*= z>dzx?S)E!CxMD9>{En}JzEu8O)n2qF$Nc*H@c|y3gl`7)=kU<~8jkC~yi%pU!uTJB zgY^-BSj`O?aZ>H?9QzLn!^-=h1&<3JzI$|B~JTF$2;76w)`F{;SuJz)fa;%*_0Xm1|n5QZ=BE zHVpzb3wkm|T1Brm=l>Gf$r>G5eP+MCp;~5}S7yV^Ui9Dg85p5|49URq=fu{x>HIhy zRdWcgWud(Vj}*EAfJOg1yp09)Pvep7@@g%WPRa878GhBAiF?<3>DRx&hjt83JEj!L z1x&2}fY%i#7AKdz70%1PX~2F=oJbYwFP~U4je^b7h;So}Uk@|d21f>|`+4hC-vYWz zvc;^u8F|G|WO4(O>J@-l({*E3(||KCR%j1?Zx6!xAc1qs0)X2qzRSL+KupX5un;Vt zHsfsx97#qmWx&%Qor-=XP%#}Rcrto$CR5B@oz)Ni4YTExx32g5$`7$GK~#HUm-h&t z$7{J^t_83g0Gt(2MtA{i(T1AX*teP5=r6G@wee>K1ki5tmGAv$72nid?5Zjh*8iR* zaVF}Yr?c7={O^qX1v`;eMU3(s}E@X)XU7(%PH=-b^F#YpNyVLiou{ zBh~Eqa{e6ftrGyPV9j81d>?33P>6!P&)*|cC+LJ^>W~hJkOq*cvu>`#30Hy@Kd9FO zulN>QRPeTcAp}qtaX>lT@T(B0k$WqU@elcmpdeJskWVO!4h6~&*%1f)!Gj4_BbffO+`?)o3#z zeg&`pz@T4+Q{F0DE#Ur?5tSHJ)IcFrC$Z}v*P5S^s?j^(fB-F|E)ZKh>O59X@YNre-Zo+<7a;seFd5X^GQRRU27sGnUNCcEoiocHYLm z>xM;II@giwTI{Z1|Ak!qs2P;%6qw)(A*UzTyV%E=1yjR*)pg0TE6hFDuR5_7J_mYx zKy}~t>+hA+gB7s0Z(9p3cpIv;fX?r^WwF+a!6xABL)WBpUVQ`hLbGKxs$S5Np8i_!}E8 zj~y=MDVQ}JA?}7U2@=-{@|M(mVV2Anm9;O8i#%ykL``@6+hkq1?O1e;d@#yrmoDUE z<^IP(;wJ3$wUjIGz7W|_gXp0SN!@Ap~SW;%o?ZfkmK3KDim4k!%0lFm9(NCwQJ zYuNbe-fNOO8Ue{xTiA!veL}~%A@PsJ^dGaDf5Jo?WhMwQ=k;@)*nskb@CpU z7qnu4xg;YaRy&@ZbirI01VrZhh(8$U2V)_OR6(#ExVN!LaDH6_Rzc28(Gj%)%#x95 z!>yR7peUo+#KR)Q4&oaZ6xf6@Vk^r zTr17%L@lF)lmd}D`Rgi6@I|iog=>j@d&i{=0XC;6p`;p6*@D}035+gyu>0+%sI0l; z-2v=}P`DX~RIM0WbH%#7B=R840~)PIxnVOY_4hJKACq{fwjoWhkVzNEA{y`;;x;<6 zK--J+g%lJMym5Csmu23yJg?rRiBjpyGj89qgbZFfvG1}A)V`?=3-l3Gy)BeIqV+pC zy?fD`ShO7znw+vhYY-C}6ZRi;7bk2BHCBo(}R$|Stnj4z`*qbOZATnTt) zutLtq7qCSFHxAVFE501&s|wr-eXH=Wpz>?YI%HEqbHsKg-we9#rg;zn7wr;YT??f* zFEvdNKdzMJDT$?vY$b*Edu^<=VB@$*^h;E0$I8i zBsg*Othel-uoJN73vQ5nD!m>lH1#b^jYt$rWzI3^7w|R~G`WQH=?l<^>z+VQ{x`nAKfY}4GPzLZVpfZ-seaLMN2W1G2jr70}eW8o<5Y4^C5yK>H z!@~`fv$NQ`grS!Q;N>><2D>i;5DCD`$q#G`A;VR-t)as*#bk?@V#$BbfXTz8_ty-WM23A=p^pD5rp>>)toqvc?aoZk-&6;=WCSxcpb3_anx zuW_(j$psWtTozi29bOFi?pwVW>~ayqok$CMsbxSo>!kFDeCR>;BK+t<3w(G29fYGN zLvs=0Ir=Ig^k{2&$AnS|ui1jyK<;U02gomAq!}ENNC0tVAizjK&RA6A<>1DR8xyWS z2IWR*$?9!rvaCWkFf;3zp=p^fUMiNZ6z^CdugL)eJOWRh3WJ@E#}hC~FSxy)jxU7U z^@9s;NSB}n1KbwoHdjP(QP(tuwkBX;0gp@!f zQbpU-&LNS!44c$8sN?v8Km{8~%f<$iTPRG2Z^6SaHa+LGR}klPK+qFzYQu-TyzzY{T>GxE zm7lDpP*K*@8YwWa4CYf!9A@Ul=M&FB`Ooj>tXtiA@83YylGN?*W%9hEAUvi7yHPNNa@d(0(8M`$Apicl@&Re&|4+Zp^n;kYeO zu*yS69a0HU472)v6sr%?i80MyIvsQf8OB=lVMZOHlV#{b^gRqG878TeEhNFVmJAI5 zUOFdnWT&do)u7Dv5;71u11NRQNYLNy!G6rf4WQ6_!99tSLKnG1yVdJ&5A<;W-#w&;ooKNb%bi`8I)XBMVV{ivj0%3$aR!-&P_j z)Q@-YHcmbYSoUd@^&vm_CSk-!$?Llf3UqEScpZR$i;>%g=hqK-S9GGem=IwtDgq)b zL?R6PfhcwZt;JT*Od)b*fXiE{=yWTTi!ZcRT1B!QN7?OYeMb(Q6#FBO`XFg+Uubi> z05#)+wLXO!w=Z-!-Ib2T9^}yrdZ6z%N?7Quv@iDK`vL4-pfI%1?F?2rfcLru?*%Je z(&DqyTRoK?)d6V!Q+{9ZM5PDcwc)E?XQ-NOGZ1-jHg@XF^nH(6`WE>~@CG&Ym0oT->C>o1`)!CKc;f>Be8bhi$tneZUOe zb3bBYh`&*0H+$-m4~OkGAZ_QR0DY!yCWW&`bym8;KkBMx3Texm8x|b2Hw)?1?v?0Nln~@MEKT>pnbiGo%aP}H zoH%`M@_a%QDBVdsJ#qH*iPOgqR5y-By{VAJwgoba1BpAK#ASOtai?`WvDGL6s4>*H zMzM{ptwR_$I8}nu@B=8vjb;Eo$Y6nzJs7bzpdARsH1zbb|M3Fcuz7c5M3!FTh4lj1 zg(Dh9A4ZD$K2#P8=@}gDbFSqxZu_|mg{WZPoRjyKlP4!mO{8{f;wz&H2d5FWn$X z0YezKcoZ1L!?S|kHMe+VFgV~j7(O=16pzGXKmc@$s9Qj1D=IXMr0ZzVDrLY@y10A! zba(?r*+sN01}1ed&I0~>WIfcKWiSU}>HG7vxN8t5m{{>z*!D~R^E@BV`Yy}de z4UoDDyMM-m-93Qay?|H}_>ssikbQ9NrGe2DE zst|&Mcp|syw(zIsehe_b*I6Slzt@BL-2(HU6PVvu=~L~1`F{Y&ezwxLI3O^;AEoqr zF#o?I=4hpVDa;4F4Fk@PRQfy^Zvn;+NC?u_AU{I~i@+g(^?e?k_fjH6QZ|GRRt8iE z(0+h5uJqz>(8B_~l>vnJ&iyA7<6!~zm%#$3kk(*L{Z7Fes058!{NV1X_V3w+WU z6)ey#SfHl?3+Tr%uiq>9*C(0y6rIQEoT0PAUixbc7kGYG!vdbk6bk8;pt(NF^h;p5 zK=5+HQ|($6g6m0s`y`#G;Eb)S18n^?LkX|-GjyJ%lce(;9d00n6|T?IM?Q-I{Dj8( z1%{>IBtyrKo;o$5a}0QQ_#2WlLI(DJjhS#iEKpcq;MW3!hnB!#tr#V>%+NPYN9a^; zd<6LFi}VSm$kHcO;kHWPuD-;uJe*_{N^AmLAtfO==k8h;7+s`OqBBcpj?NMuMCa0> z`b#9TFQb@MA#Gq4NbB#cLE2t_0?>6SybU9M5JzkJK|FV2*B-Z4fVlH0|1Am=?~FWK znoocg4;CFyn@-Q?6R;=%-U`%?$K7^wQoV|Ir^q_0{v%Oc+wJ`gGNh`7kk26ptRxZP zffb^Av1_taa0Y-zg((&T0~~A+03zcgA z5rQ%aG*vfao~0UKE5Rm2k#IqdN*N0fb3z3b;>v|SlurV*6QH#R`^|qB z(91Mkb2Fw!Z~>%2tpDtzJ`+MgrU|7wE4K~k71SMd?nSTkqny4<6#4%qo?R83RKSz0 zmEVPswv+F3zfg%UwpBVs{MrT|&;khBeNKC>qY{VPsrs;)ps%CSR(t0*Azo=y{Q~L+ za6lP)=h}_dCX7J5J1qz0kj#UV;9IsgbM4>y5+;poXz%>eOPhFBDB+QGJX2I9_Y#{H zUV3TE!Aez9S$S|v;+|^kb6ZF*Z8?vSEwQF>EK90pn<)7jB%}EjDHj${AOct>^52xm zPAA9}p%Q`|1S)!jq?Dd$D!$f06j;p%pKn?)TXmuEh2W$tnZ91^NqoXQy*x!Gny*4d*cdyah&!BYfhH z1{gheB~!c)WT#DzFXV;6AVDbzSN?*5us_z)2_Q?gLdmChC5>k|mA z!q-mk`z}DbF2K6z68PFx>2_j(udRe*0_CvD2CVw*LJ#0;kHFVnJPBX>8sKY>z*oi) zzV-=xje|7n1$>38EI_ocR>Dr(VtWOIF4zG5m0qWVq@f3Sd(Z<>!dle}Xf~kw7C;m( zcA7MmLF6+8X8jQ2VTI{dU}3m2tOilm8iWoa6iOWOKB9)znw!3bwUsrCT@a0QgK;!k z8Ch6YSz8&ctbG9Quf4Zp3(!_+SL+3DY&dNIUzy8TNMtho zCUu4H@25kCr~UvP{5%S}23`eV>JKuuwzdVJ9)rXvQWbZUL(iQ(bMm}UzGIdSGdUS2 z0yoC6-+;e~cg?LP1wA5>I|PqIQ4UrE1v)qUgII?HhVtlMuqqHcR4fx?wn!BoF&M2_ zmd}qY=5W{mETO<@!^NdW8!BP5e8N+x=0PVTTTJ038kp+y7w9q6=@OvSDJo@1Q9ZNv z%Y;hY8jk?}MEuq}L94ud3FJ!Qn09?HurvqJ>nU$ILssuaIUSpzP=13kcVPtjQov*>SUEIBb|OHrG6$*7lkR*~HebcPc$athgP1$wRaaI(KC zj@n6CnCw!?&T6HUr-?v0bYV8x(eSZg?l)NWr|Eo#&UeB|wox6SK3w0yh$ZuKCe5QWFXMo|VxF0eToZ=p(Kj1og@8+b_O z4dv94Uw|AW@O^#&HNNvR%QqlZ6DK0BGkR_fo03HB1L}|P%p$v;8x~l}fno|ifNh6_ zM|ByU6j4|KtxCl>9?Q13N#2I8GwLjirP35OLO5uc%{bSnt^o6Amuwubgd#zrNV_G- zxmvyPCd5si6%jW9iM@3h;(i62jaOwm^vaU$&?~j=koa(F=vANipwYX~BEBemP&UW5 zDF$B@`o*o#FOI$JhkiRa{5K<&7-awptuOl!W~p}7aWk?ImoGr%FSL0mntc7LRO3eh z__-wtRl10JiDZGKLaIkLP#J{6h@)(vz%LR4p_`i>58mlHSix+EZjg~2 z13n-S=>UG3f?^0NK?@4eP~Z3=f``x2d6UlPxc01AYzoiXKg`;icF5a}YAMi5$F}{p zzfmK^FJK>d?Q{6b4FHm@#INedde9qCNO@BOEE4&{o;s4MYuPNT57(1ut89kn6+y`m zDg!AS&LGe(h|an$M#XOsFPmks?>cx<%QbiidV14x<2cxB@Bw;fsmf2EXcSffvrd`1 z4x=76*EdOco<%J0e3$mDn1%k=tb$amdB?aaNwXpf?r1>4b?mzuy8R}|a~aF58F>n- zOm-}pZZLS#IMtG2GtH81P1`nuS3l1-3vyjy$F?e7)q>23$e~8#Uk@^A~|0TzfDUjseH5j?>~+H@0u@X}k7gs7I5ef=sZQWcfAJL4g;8WWl>F%$l-$s;_Mxux-ofNi)=j|Vzr~V0RtJ{l==j!+Gyz~gP>v?5YBq*d zv;KcQ^gMND^1VP$5zyAST@9kzud*FKL0EW!4%s)_p(AXII*@E{?x^o4lq~;qpkz&2 z+7EsNq@}`sZ`4i+*R)ecm%++JqmIf?p_)w$L!MU%;fK2UY>Ks@RRP8z_KrJfGXy6c zu|vUbvzOTFc$js*6B;h-)4y5z-QK$H5w=1~u%5 zWA79>ZIso}UL&ZIUDUE|SygTAz3MGkL1ZViLZjXS*j(8aPfM6792a(}(NVph^W-3% z9G&OrJWWSm6ceVN%NjKOFFV^HM(BKjBMetj<@Ouaq)ddVDYl-Wx)|CY0mN_s(v zb~>r;gc%iwq&b135@8uv$dLU&1NmYgkr!olg`Wib>O zzW_eP@)(E(^d$&U8e%Z_(H6}89;kJ*9n_PIyaDBH(JX~!CJ3tnIPnX0Z9w3XXso0z4mzxo-qe)6YT=6x=oC zY4@ZjFodxL{vC(a-eRKRT$53a-)4>y;EY_F97rCFGm4R9YLA-(bU2P`42B($oOX>T zCU``_97fo}(+68%M@+bq!up~8C6jGc(|?=cRALui`z*HQe#ve3c;awL4Ag;LnQ5v6 z4ZJ~+3oA}0=^oUldO(W|Qq-4*?f9K)`p_ibJDxbjV}0pFRU!?htklvdEi&4^l#o*^ z3EBssIX&=G8!wJ92mO)}3Ro$ofU@bM=uueO-sJ{>65SwU+>q!qB_l>n_3|F!oHT(T zd?b)pI78idkr~ur>qEFeO`m|A6$y`g(yVMtNsa-HjSMc!Kp%lBXHeiu?5*uJu(rRB zv+9VQ>Zw&u5|uFPF?EiY03OJ$T8tj6y`&gd4umvjvJGX5$&GG*xdE8g^Ilr&vTpdN z0`Hx*R*tUJ%7(TOur4bOUyEGrZNgqraSjUu4-98dx!G?HJ}AV$=`j@jo`oldnd9DT zk@tS&CFk0DS1(5YJO}F+=*Y*vSo1aKbpUx$4c#0bTea4b9Ylu0vtte(%hTwONtVs| z-e1dCL}o(`bXbgyq1dBQ3GU3vi4&&Cs6y}4)DzJ2bjX4-48ue8 z2_C$Mz9qx5gZK0R8|3-%GTHy&vhMKlVG_*Xq)RDA|Pc(sFJ2exV;i1Qpga~i_tN2?XV1UsI+`ki79k|6Pkb(XaZ!v;M1)CS#)YG{voOe70lU*<4jCZmd zK&Q{`I|*~euzsA;kVIwF5!mU3tuH-;IvQ5<5w-=rQ~O7V#F-Jm&;EJ1p6uD;5f$jm zfvijgxNcH7yCg=@7I5U7X5AK?&>{}kfi-W@R0mL@95{rI8;C`agHf10#L*HM`v^kB z5#;tq4yeQ`VIb?{JX%N7hm5VXAYF@R9z^q0bM^_>@tdFl-h>Gq7 zg;O!4iGn1E$wn(Csiia+#0V~SRv@U z(bxm^?uhEY?Zu(^+=9Fl0)ugfir0~J1+;5PuB8jO*Gn0wVlTs5 z+-#g99BCBaVO3$os^UA)8c|we7puHLY}CrAit$>!j16E{$f%Bc(|}nY>95nJDxZ~d>Oq{n^Vcv!YbpSU2@)$-BpcYUnls6p{v-Dz)UQ{OR@$vC< zxRZh^FSIW&7we3T;_xFOxBe1qi_0TSg9L87?WEyT>>#oFp^q|6NRB(XVKF^y8>3Ss zE|YCuPf9P_f~VaeFHwM17IZ%KCSl8u>OlH!);fafdRALxh@&DyltiL7gY(5{sBk`l zLiP8tPB=EHl;vlP{V4^b+I)8<0RrQn6%QE+m6F z8K{Edq78Wy>sD^G=7)D4R=mXKOpu|;Hl8o9^+Ynt8q2%>kF4Ks(&2#XFT;WSkaL)$ zj-y={s4*V{I4;{TT@(yfN1}MU%uRRb0g%R^_ zh7M;hR9kq$l=;()M!Vno!_ohN^Uu5LWsH7(7@L3SfBMU#@4g0_7ABhE$hg9m5><;FIDy&_t3X-y^2KY+klv0Bu zcmb+|EIr3P`!KA;LNf*A1gwV@VONIM4QQDLMmIx1uoh3F63G(H;y^S2-W<;M1I)FY zhJ{j|M}F%%vXmv*R4VJql5PGHa6X83pKmqi8)@cZ(a+OKPZ9m24#?4xjR3AUG#$dT zcOGamnpX+|4HM*7@cL<#V6znKI*ilqT)9usBQW)DH$bMU@j0P}VQh+hZ>Z@_@WE0A zM*$Wo`mkpCw(6_Tvr0HO92yt#it^L9!5U@O(pHuB{$IvmW(O8XU6keH2HU zt47^GWx(_xSd8Rdyd0)~`6jwu0Zu{!wlw>D1&!$z0A1x+H9*Mbkx!2ghbr17Ag zqV})^1$a31Vehl0^+B!$s;vbDJZ}ze^f?S`1S|aokcG=r-G@}3>X)O zEa;)#<~E#PVDd27x_p2jG^ zt`TmZ%j22}C?1!*}|$*~`RKgW&M zX0DNXy~D%>CMJn)uI8Z{ktS?{wbOZ!`$RFA_&9f$E^6{VV1-ZckrysR*a$98(O0lo zd08J*H(qNbHvqZOdPNCulYz`t5P2rG%r&V;1Y@*psx%GsPq3?ZqkhzrvaC^ootQE( z^Bsl`v6((N;SCat%p8Ucw+)i?@|QQhhk?;Os9n=acM)cWt_liY?0R*DA-GGRx>i7uQHn# z18WT4CNiP_5$QypSB^{K5~Tr&jbl+coB$%6tYfj|)y_F_0m-3}jyol=FUsQ_8kqFi zVx3k6;udr~jdaOQnthRt1uvbLi4z#PxzBYoEsn_1vLlkA$bM$DOw^The1+BMCj|K0x}L0 z{hDuzVVaI6eTLcu9q{ZR5R6F*5fa~~X}4C)SgQzL9pYLWN6` zym$rn&2u0tU`!@}>n~vc2yOZ(M8@st3mWNyTkDbAw2vgnA8m&ZB!GytyPyaSUS&OJ z{Ub1ogOG#1;`YW8^-tHr*CTanu0y->?96q68wpOJ@`HmHp{hDuT?bjXP@a$tg9sTg=XJ`PkCcjAoCHmLC9Yh&mcX^U zpj)qK8XXbR#EAU_I|%+>hVV#d^x&s|7_&y^G`4lUSW<6Rr5n@wZ>mvs4oeu{84k;y zRc2~+ebzB=oyAgaH-MeUA@N{h*Roo1N5NVN6)bX?2u&lD)v@~00hP1s8VP6koQXE6>gv()2lX37+pdbNQO)I+Kr(BPNbm%=W z0(lU*GqlA!0vle0*m6wB+G<7KiGZN!w{Wg)-8WeSVw`@gXXzDMG<4?1YeIIKL+m-D zGn3|y<*LM6%aw-0VbV~B1OE*K4?5YorvH{*_xE(TVl`T2j9IwhNwF=e*D#nZhTTNN zwSrCPJT8fqP7&v?`MLBAIf)bO8B*F2oZE{$VWGtqj!DJ(2Dc{G3$Jkskxl=&Rv*UC zJ_4sI+j7Wl5k%yw9M1~YG5CfXF5R{KXk z7JwnzPQ%0P6)n_bV+G!l+-L;S4M+~ile$f0Ezn*jwVr@lgd^pp-{e4~1%Ls)4Tx_5 zK+uFROb+C6@zAnm%H9%~(=68~C8c=Hmj~PQC<*UMoxG90UFZI*<< zf@4i7D1f7LJUr(E-880fG%n{)S#U{b;dq?HMKO&`SU^RbAdKRHo#}w51xaVvX~~h1 zaU7~h(c8Wv*qOXK!+Z&OSqy1|G@QpPHbCLLgu`{Ao1hqE%95>tH)C!O{58-LP$+5) z!5yMdC8~vRPk?mY2*n`-FVEK?W3J_iSlrl4&cRrJv(bEXm2gh?;AwAwBfIAKtA-E+ z7^q%8A(fG*{yC&BKk(0~ zZ>@V)t*buBl35@90DORIpMxKA0%JpYr8>P-t&>n)lX@4w3)D#ynAxjcZb|zaLIW(T z04Eh;ZB;L(Mz`tru|`Y449Tkxv)pCm6=4D*B(JxdX+`Yr!%(KR#1eL3E^Mx?GspRu z|C{D+h-3@wg|Mgex>ZVknI)4}bX)68k7l81V>R@{>=D0FfR%mdsfT!)mpBwgaVF^Ewp1(wy2K)hQ<*Ps!Xc8+6a7-8FK{m&%qgM)g5eC zfB|>WAu_0I&gzda%+QH2aJXl;Ic>j@pN0*wD66za)4Cx+X!>Ziw=f$e#h){y4ErCl zR09pJpeh21R3aGiJh11FwBSe{sKfHudtN%H$J&?Bfs~l3(iEt& zEvyw!Wa}4E6Vp~AB}3VXU0gw}cQeT&)g~;R$lDN+W!(+KpR{Ylx+|g?-o!NsB5R~G zM9@}vDC~nJ64D}mVkcN{!5-)qLY ztYI0-88+$&yPO(XyaB2WWH8PSdl@F<88A1iZ>~tF$v>6?KsAt5HKJlR$|#QoP1cg@ zqYQnFj!ZUjWN_E;zK2x3c1AReg)yJcd?)*i-Hl^N7+Zak{l&#m{l{Zgho~Y?pAe5dGzt!jFEBbmexBI83gW6Q^c{|d%OykLK?HGKCoM?xdJOFO$*J?6 zQSS-u771_lo^d{B!nnb0LbP&l;0L@s@b_T>i(G>z;lptq&j+E13Ibmv48D-~40qw% zt7U2Wgh!~tpTXsz3Rilf72U|gr>}_^1o6EHOTjK!4yGFwZZ4+}R#qq^kytsV{_QxX z-iPa!5C^_tjInq^K9K_-@eR~j?9BLb(l-5b2$$B0PueFwX`%6A(*qUph#bR&o)@&d`c-cwits)Y@9FA9b8>;y zH0J}ZWFns`$K#cQm=&WCSmTl-V;mS*EhcF-%#xFdLR6Tvvrj^{o=dSX=6hkxL#S^)$s1O z|5R<_JNnqtF=uR*{J*+#{^;Y)`MV4EEW{i7I9~5#XWNSTS$cTo{G<=JG}I4QLp$47 z$>ZSac>vmMt@zA5$E;tI8($@l7go<>Yz*60Tg|4+q21%V#&?$=n}oLg7KUtrqCaic zL9Dst!tg}(&QB=qYjE%>NoOj_fp@x72Aa|1S_pwB0Xwm;3{S%x4TMq~0b|`b`57r2 zSQk4;T(E@u>8smBs**q*qTEgSVjg!tRIjc-kSMR&mf$DR`)-aYt5p@NrLWg?jOv0dBc6>R7$ew&vS8^-<1?E zjVeHBfNW-9?Y@w`idy7_?uGhW-zoEc4yK^(%&Ai55H8TV#AMVu%e&!nX7=JWeJ6G$ zTTW}0FKplKhA(HQ=8I|E^FZ6#u5~SYr?ps`x3^;yV7Ty3#H8K6Tl1JtGEgXGlEH~x zdk>+!{wHv%VK>TYE>Qn0QkHw4G?z(;1{&DyHS>M=Mo_aG zo}UF`&&bV(`f+x~89EbmYNKFAhLSY5L#~IY4~SmRl(qRI`x$p&mBBYEX2N|WkHy>& z)&j+5Okk}yG3cvwgxKQ9;Fjj;UHja~g=r|RIlK1B{ge6tYVQu5%%?A>y)kGQ?c_G} zzBw9$**-3Vj)xg5^Gaq&bucGMiF`K;y%)Y@4=R%?@4?(oPt(E@Xb-zql#FXbQP9$6 z9pDtw4Z%rMH4Ya>t1)8dN>$@wmE9;SQ?FbtNT25-?3H4eUt?yb@Z&Vv+Ysz_F0Ia3 z8*VR>CCa-tJZg?zAg`kriWjOE-SOg&`p8OrvTJJIc1i{6?x!xn*zb&uTiBYGWU{j^ zLN*K0+%)@d(W%J+z!Dfz@=7qud2-uatErNN$mLjCMuP2KN*G2duaM;5OEPdLMxV-i zP@w}c7zBxLf&8bm*!)>{g73s*y@*39uz+D#>Km~jnZv{Q3%|29%ozFO_h2LQXo1{( z1%aQ~(nYQ`iWbC{f;Tf+;qaZ{Gx5*E4*3c(@kpl?Jd*g?mEwYjI)8SvbpRe%ENatU#SXxi}--TByBchAw*C}*d?Oy^uUc^Bn+WsDFM`&)>fe6wG zPD21l&c$K~hXJtF;B{Rf9K(9AgS#t4eINH@;*=-^lpwR9W8{TFWJsZ7Q&7v*Iz(gyYh*``h}>3 z#`M2oi@Y{|q25NxX#;ei*!qn;83^4g`1Bt*cToU|%WS@kTR}JR=thx8O8Rc{){n^I zW|)rL;;F?4b6ZKkqRf7jIkwPRxSOO8P96qx_u#k9*8Rjdh*9A?YaG!G29${-gzJ9=sjxN zEk7)DMb!3N*uoU==9g+*QhyPt4^-My`-|)7lTC(Sf2kVEl+OLRgR<55UHTXT)y(@- z4-~_ES3Arb!XK2cgZh0KFKnQ44{=`S9#%WWOm<{(;x^1zS2}V#y|Mm>+ANGCq*v{d zbRD@vIR0`-?S^|8Z0;j6Lyy3BRPwj<$DCt)bB^DzK%8G!d-x`V`gJIr1agORkB~k6 zDvszBhoC-l;&tm94ofd|LL-qk#xQ=bi5I1n&i8i~L)dWRNu5M6x9??c_6ckc_sRC~ z(Z$EqeJlmCn=a?1bILglyB8>fx4OJ))k5}rA^w;BFGIyny-yvu8F3ze#bVl5{B+g* zIK?YU3=7?&262#SDx!6W79TCPH?{6#&Y8t0q+}ivIZB`O2YqM7cNV^L;yZ^?n-t%Z z@a+@dQy7=0#rHIP&xr3C_?{Ktv-mbCz9f9liSIeYJ}G*gC=(_oOboZq2MDnvA5*(1+9qR>-@WdJoE|w#UO${iKb%J?)pj!n1C>pyUR?4%ReMK%i!I5~$8u*IO;VQmb7*>xi(OSIU|L=>@YH7ll{LbYTI*bM%8M_d=9Sz2#Rc^=4OXMhe^FVR zd%xPPp1H+!6RfOVd_U<1JU_rE*1Mr@NH2tdiLay9n3u?B2Qc}l^Fi~D*|H8{F!YU{ z2-&0!qG~_=pwvi}Jd2(S=@I6(_#smZGFlLml$bHZ+^EOkO;W*p^>f5APV3^$$~x!6 zxsNQq%&*n+=C<@=i}MPMU^C>rq-#-tb-7A)t;=D!7f+YBoG8*9y&S)Ss&Ga_rLgEE zE>f+9*D)33&g%oQa$9}7lXxH@=l;uavae-JgP?LJHexuH`yt=Ij6~Fcd!=5`{t%+ckN4=n}ywe?#Y-7`054RKAiEG5<|6cT$up5#5`=* zrsn2x$%flgi<0*gb$xK3WK3niaexYNdREVIYo+11J(31o2{CEx?b%Kv23&@9*Z}fY z?dqNom!Dr^O{yI>=1&f@^cjtFhv+KS^S{iC@oc z2Y+N7*zIt5gFBBiV7el3%)%AUxcH&UzmgnM5K;UWB*k$dn&@}K<_hGc+r>eVBbf7f zP#K+@&!RP~E(HjpZ`STFZ;|%YH7i#82KPwEPL?zjUgRG2c5X#yp*&)`CmB6F@#MLQ zlc|%ZAk+GF?634Is32~b#YWj|TFF4ruOd)?o>h<17O;$tzJd4h0IYVP-x4QFXD1SA zh3gl|v=|@PPja-*(Rqr_c8*TKaI54doV^SslL(i9Qd?g?&46dKa5 zYM;6h4&pS<1e80^$h~h><{;N_j;_a>u; ztChOE3(F{n;&AIJ7?XIR4A2dmNjRxvjB3N|*H}=vQ(Mj8K*C#pf{e0fkT83l&d<|< z61K0r3ugtVE<$Yu7mJ&F<$3Ejj#@sqd-vXLi9LJv!PyVr-o5*`CGOp|`+T|M`K?=@ zSNDuv75Diuu~Xi(@8zVUAgKpGxg$~}^}Rez21*e+4WL8eDp*bu3rG|pL$_$!`qsc) z2Svzwg8k_iU1xL%<>kie`V1KiBPaFMRC3zJ<4t(x05>sJ3JKlw786}!hqgiWFA z2O(}c-%&kjh&?%s3FQ1mo@|6sh5Cs@aLpsi=E2lV9uiaiX=eYeXp$R}us5p;yR%Dx zm1w4PwxGBmKC!NEMq`)uU{&2^^{S3ntJ+PrP0pu9vs_^UV-pXVr&Ku5SYiD-DhHtO zv#e;Xw)ztsg-_C{&4IcCO;^2|+kEAoNnR$HXu!LP_s<|V-}Rg06Xn48?&)$caq7@m zd0>*LmZR33n2?2#^vgQPK9hfu~?EAJ>Wl%8{>Gj}rlb+r@76uI%wJ;Qzx+28uY-j>AxCgYj5z zd;CJSh|2sii~AEM>NrJyDh@N(Qv%IyIW2SKv#o zL8k%ul9x)Fpd^BBVT1xze2Qtw9Cw44kxbtaFR25=#&4-|!l^Ex6&zf{OXfHfEN=4K zS%#Wp$>3?SF>RocX42D|p2M1-{c|#ymmi+ZZi# z<|V#HP>S29VO|{P%XDhGq`@Utsf#6;^8PTBUT3Ng(s`M$M3DNA=m=K41)pm{^s=5o z!r5<|n0HEb726m2sfcL=dr@;jf0$uiEIEo>w9PSEv5*7iH1AcbUQA>gV{iAckRcw! z&q9z@&QyT}=tRNKyv@LvNNR!}ah8&0c?CK}I&!=;MV}nOYG;z8?C4{3s1bwXEugk8 zFp13?27qKi4&wL;q(UhwPv{aOXXy})yZu$eOd7}IjT$F)HQnKQxS?q4GB*7*zkZg^ zb9CnET&6QYXEU8KI(O3{xFh(k(#U4r6aML!=v3%zr}G6m|AWpZwr4MWAEWbeI)6^* zUO354PKt4WW0EUeF8WjKiQi=cu3e-3Lp2RK*M_5oJYuF}jOu10>Y?K-BCHSadh_5J zc=VqzEI5-dUBC%8LJc>@B?dDOlu&(K|ZOZR#dl1tfTYLZA}ol$-^C#E*0eFPNRt zS(Tav=}B_f*58Ial5I^mOaA~r`&V$1adRE3jEap*JgQ(4a{%K6NWdD9-c7d3-o;E- zJ<4DKMSsn>o*KN8UNS}{2)zLxZ!zg>biRd-pzF`Wx1YoZ>8ws@ONAji>6O1Qt_$R` zW*DZYNJak4vwB%8gbx20{2Ih7UHry~R>C1(^AY!NfSx`DGSI;fvEn!4D~`e-vJr)VSK?e|EwO1MZH;7jo4HEHlJ$5-Tp2o^YKk zt|WvI$V%V`TMDSbFw!w6+-ZYzTHS$m=zn$lH(ULv8N@$Qx0>k369L5orfi-Z=w{u; zrS&MjH9k=ns}rf255M7sh?2&0c}WSxN!J-WDn1+whdwklqUG9Jgo!l?92)QB$ufkq zuW163hsT3B*AT}s@a{dSlUi|d5m0K( zI)+29*f<><3mnszF~Ir?oZ8C|GUS&zH!kQO;*5}$^E5(TnPOEOgec%uI{%qpe1^Us zVM$?ZE3sQKI&UEcV}>HZH~&7G4`` z)lP>ZF1c%7t}sXrnUsda62C$7K+=Rt$vzWr?&GOdK`tp0nLSW4$!3FUT1{dB&9Lht zbpWX;wKK9R(b<&U)u%xs&dtB-*}+TV$xZj~dg^ zO|tLg;}b_xlV_hieHa$QkEBkVnmB&+oS_nM#GB;C3+&yWV4pVA9r_1Z*ca&t6#WhQ zh=|>mNx~lVf#5a5PtkOAJI>TjyqqGzBN$hUow&VBbcoIgI%(!bzKJm}Cx+&JfUiGB z=L>XxiVjVin)5|pf)6|R3pp@!MEy~8`V4)-S#1tv|-2*KRQK?4aNL=FIBa3Y-q zR@E!=xPZ948fC)9j|B#R!-dTFK`uR9Jx${-78bU3KYraX+_Edu85@d3BH`XnYa2ih zB*E4 Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]: + if output_attentions: + warnings.warn("Output attentions is not supported for patched `LlamaAttention`, returning `None` instead.") + + bsz, q_len, _ = hidden_states.size() + + query_states = self.q_proj(hidden_states).view(bsz, q_len, self.num_heads, self.head_dim).transpose(1, 2) + key_states = self.k_proj(hidden_states).view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2) + value_states = self.v_proj(hidden_states).view(bsz, q_len, self.num_key_value_heads, self.head_dim).transpose(1, 2) # shape: (b, num_heads, s, head_dim) + + kv_seq_len = key_states.shape[-2] + if past_key_value is not None: + kv_seq_len += past_key_value[0].shape[-2] + + cos, sin = self.rotary_emb(value_states, seq_len=kv_seq_len) + query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin, position_ids) + + if past_key_value is not None: + # reuse k, v + key_states = torch.cat([past_key_value[0], key_states], dim=2) + value_states = torch.cat([past_key_value[1], value_states], dim=2) + + past_key_value = (key_states, value_states) if use_cache else None + + # repeat k/v heads if n_kv_heads < n_heads + key_states = repeat_kv(key_states, self.num_key_value_groups) + value_states = repeat_kv(value_states, self.num_key_value_groups) + + # Transform the data into the format required by flash attention + qkv = torch.stack([query_states, key_states, value_states], dim=2) + qkv = qkv.transpose(1, 3) # shape: [b, s, 3, num_heads, head_dim] + key_padding_mask = attention_mask + + if key_padding_mask is None: + qkv = qkv.reshape(-1, 3, self.num_heads, self.head_dim) + cu_q_lens = torch.arange(0, (bsz + 1) * q_len, step=q_len, dtype=torch.int32, device=qkv.device) + max_s = q_len + output = flash_attn_unpadded_qkvpacked_func(qkv, cu_q_lens, max_s, 0.0, softmax_scale=None, causal=True) + output = output.view(bsz, q_len, -1) + else: + qkv = qkv.reshape(bsz, q_len, -1) + qkv, indices, cu_q_lens, max_s = unpad_input(qkv, key_padding_mask) + qkv = qkv.view(-1, 3, self.num_heads, self.head_dim) + output_unpad = flash_attn_unpadded_qkvpacked_func(qkv, cu_q_lens, max_s, 0.0, softmax_scale=None, causal=True) + output_unpad = output_unpad.reshape(-1, self.num_heads * self.head_dim) + output = pad_input(output_unpad, indices, bsz, q_len) + + return self.o_proj(output), None, past_key_value + + +# Disable the transformation of the attention mask in LlamaModel as the flash attention +# requires the attention mask to be the same as the key_padding_mask +def _prepare_decoder_attention_mask(self, attention_mask, input_shape, inputs_embeds, past_key_values_length): + # [bsz, seq_len] + return attention_mask + + +def replace_llama_attn_with_flash_attn(): + cuda_major, cuda_minor = torch.cuda.get_device_capability() + if cuda_major < 8: + warnings.warn("Flash attention is only supported on A100 or H100 GPU during training due to head dim > 64 backward." "ref: https://github.com/HazyResearch/flash-attention/issues/190#issuecomment-1523359593") + transformers.models.llama.modeling_llama.LlamaModel._prepare_decoder_attention_mask = _prepare_decoder_attention_mask + transformers.models.llama.modeling_llama.LlamaAttention.forward = forward diff --git a/llava/train/llava_trainer.py b/llava/train/llava_trainer.py new file mode 100644 index 0000000..e65473c --- /dev/null +++ b/llava/train/llava_trainer.py @@ -0,0 +1,527 @@ +import os +import torch +import torch.nn as nn +import datetime + +from accelerate import Accelerator +from accelerate.utils import InitProcessGroupKwargs, GradientAccumulationPlugin +from torch.utils.data import Dataset, Sampler, DataLoader + +from trl.trainer import DPOTrainer +from trl.trainer.utils import DPODataCollatorWithPadding + +from transformers import Trainer +from transformers.trainer import is_sagemaker_mp_enabled, get_parameter_names, has_length, ALL_LAYERNORM_LAYERS, logger, is_accelerate_available, is_datasets_available, GradientAccumulationPlugin +from transformers.trainer_utils import seed_worker +from transformers.trainer_pt_utils import get_length_grouped_indices as get_length_grouped_indices_hf +from transformers.trainer_pt_utils import AcceleratorConfig +from typing import List, Optional +from datetime import timedelta + +if is_accelerate_available(): + from accelerate import Accelerator, skip_first_batches, InitProcessGroupKwargs + +if is_datasets_available(): + import datasets + +from llava.utils import rank0_print + + +def maybe_zero_3(param, ignore_status=False, name=None): + from deepspeed import zero + from deepspeed.runtime.zero.partition_parameters import ZeroParamStatus + + if hasattr(param, "ds_id"): + if param.ds_status == ZeroParamStatus.NOT_AVAILABLE: + if not ignore_status: + print(name, "no ignore status") + with zero.GatheredParameters([param]): + param = param.data.detach().cpu().clone() + else: + param = param.detach().cpu().clone() + return param + + +def get_mm_adapter_state_maybe_zero_3(named_params, keys_to_match): + to_return = {k: t for k, t in named_params if any(key_match in k for key_match in keys_to_match)} + to_return = {k: maybe_zero_3(v, ignore_status=True, name=k).cpu() for k, v in to_return.items()} + return to_return + + +def split_to_even_chunks(indices, lengths, num_chunks): + """ + Split a list of indices into `chunks` chunks of roughly equal lengths. + """ + + if len(indices) % num_chunks != 0: + return [indices[i::num_chunks] for i in range(num_chunks)] + + num_indices_per_chunk = len(indices) // num_chunks + + chunks = [[] for _ in range(num_chunks)] + chunks_lengths = [0 for _ in range(num_chunks)] + for index in indices: + shortest_chunk = chunks_lengths.index(min(chunks_lengths)) + chunks[shortest_chunk].append(index) + chunks_lengths[shortest_chunk] += lengths[index] + if len(chunks[shortest_chunk]) == num_indices_per_chunk: + chunks_lengths[shortest_chunk] = float("inf") + + return chunks + + +def get_variable_length_grouped_indices(lengths, batch_size, world_size, megabatch_mult=8, generator=None): + # We need to use torch for the random part as a distributed sampler will set the random seed for torch. + indices = torch.randperm(len(lengths), generator=generator) + sorted_indices = sorted(range(len(lengths)), key=lambda i: lengths[i], reverse=True) + megabatch_size = world_size * batch_size * megabatch_mult + megabatches = [sorted_indices[i : i + megabatch_size] for i in range(0, len(lengths), megabatch_size)] + megabatches = [sorted(megabatch, key=lambda i: indices[i], reverse=True) for megabatch in megabatches] + shuffled_indices = [i for megabatch in megabatches for i in megabatch] + world_batch_size = world_size * batch_size + batches = [shuffled_indices[i : i + world_batch_size] for i in range(0, len(lengths), world_batch_size)] + batch_indices = torch.randperm(len(batches), generator=generator) + batches = [batches[i] for i in batch_indices] + + return [i for batch in batches for i in batch] + + +def get_modality_length_grouped_indices(lengths, batch_size, world_size, generator=None): + """ + Return a list of indices so that each slice of `batch_size` consecutive indices correspond to elements of similar + lengths. To do this, the indices are: + + - randomly permuted + - grouped in mega-batches of size `mega_batch_mult * batch_size` + - reorder by length in each mega-batch + + The result is the concatenation of all mega-batches, with the batch of `batch_size` containing the element of + maximum length placed first, so that an OOM happens sooner rather than later. + """ + + # We need to use torch for the random part as a distributed sampler will set the random seed for torch. + assert all(l != 0 for l in lengths), "Should not have zero length." + if all(l > 0 for l in lengths) or all(l < 0 for l in lengths): + # all samples are in the same modality + return get_length_grouped_indices(lengths, batch_size, world_size, generator=generator) + mm_indices, mm_lengths = zip(*[(i, l) for i, l in enumerate(lengths) if l > 0]) + lang_indices, lang_lengths = zip(*[(i, -l) for i, l in enumerate(lengths) if l < 0]) + + mm_shuffle = [mm_indices[i] for i in get_length_grouped_indices(mm_lengths, batch_size, world_size, generator=None)] + lang_shuffle = [lang_indices[i] for i in get_length_grouped_indices(lang_lengths, batch_size, world_size, generator=None)] + megabatch_size = world_size * batch_size + mm_megabatches = [mm_shuffle[i : i + megabatch_size] for i in range(0, len(mm_shuffle), megabatch_size)] + lang_megabatches = [lang_shuffle[i : i + megabatch_size] for i in range(0, len(lang_shuffle), megabatch_size)] + + last_mm = mm_megabatches[-1] + last_lang = lang_megabatches[-1] + additional_batch = last_mm + last_lang + megabatches = mm_megabatches[:-1] + lang_megabatches[:-1] + megabatch_indices = torch.randperm(len(megabatches), generator=generator) + megabatches = [megabatches[i] for i in megabatch_indices] + + if len(additional_batch) > 0: + megabatches.append(sorted(additional_batch)) + + return [i for megabatch in megabatches for i in megabatch] + + +def get_length_grouped_indices(lengths, batch_size, world_size, generator=None, merge=True): + """ + Return a list of indices so that each slice of `batch_size` consecutive indices correspond to elements of similar + lengths. To do this, the indices are: + + - randomly permuted + - grouped in mega-batches of size `mega_batch_mult * batch_size` + - reorder by length in each mega-batch + + The result is the concatenation of all mega-batches, with the batch of `batch_size` containing the element of + maximum length placed first, so that an OOM happens sooner rather than later. + """ + + # We need to use torch for the random part as a distributed sampler will set the random seed for torch. + indices = torch.randperm(len(lengths), generator=generator) + megabatch_size = world_size * batch_size + megabatches = [indices[i : i + megabatch_size].tolist() for i in range(0, len(lengths), megabatch_size)] + megabatches = [sorted(megabatch, key=lambda i: lengths[i], reverse=True) for megabatch in megabatches] + megabatches = [split_to_even_chunks(megabatch, lengths, world_size) for megabatch in megabatches] + + return [i for megabatch in megabatches for batch in megabatch for i in batch] + + +def get_length_grouped_indices_auto_single(lengths, batch_size, world_size, generator=None): + indices = get_length_grouped_indices_hf(lengths, batch_size * world_size, generator=generator) + + megabatch_size = world_size * batch_size + megabatches = [indices[i : i + megabatch_size] for i in range(0, len(lengths), megabatch_size)] + megabatches = [sorted(megabatch, key=lambda i: lengths[i], reverse=True) for megabatch in megabatches] + megabatches = [split_to_even_chunks(megabatch, lengths, world_size) for megabatch in megabatches] + + # We need to use torch for the random part as a distributed sampler will set the random seed for torch. + batch_indices = torch.randperm(len(megabatches), generator=generator) + megabatches = [megabatches[i] for i in batch_indices] + + return [i for megabatch in megabatches for batch in megabatch for i in batch] + + +def get_modality_length_grouped_indices_auto(lengths, batch_size, world_size, generator=None): + # We need to use torch for the random part as a distributed sampler will set the random seed for torch. + assert all(l != 0 for l in lengths), "Should not have zero length." + if all(l > 0 for l in lengths) or all(l < 0 for l in lengths): + # all samples are in the same modality + return get_length_grouped_indices_auto_single(lengths, batch_size, world_size, generator=generator) + mm_indices, mm_lengths = zip(*[(i, l) for i, l in enumerate(lengths) if l > 0]) + lang_indices, lang_lengths = zip(*[(i, -l) for i, l in enumerate(lengths) if l < 0]) + + mm_shuffle = [mm_indices[i] for i in get_length_grouped_indices_auto_single(mm_lengths, batch_size, world_size, generator=None)] + lang_shuffle = [lang_indices[i] for i in get_length_grouped_indices_auto_single(lang_lengths, batch_size, world_size, generator=None)] + megabatch_size = world_size * batch_size + mm_megabatches = [mm_shuffle[i : i + megabatch_size] for i in range(0, len(mm_shuffle), megabatch_size)] + lang_megabatches = [lang_shuffle[i : i + megabatch_size] for i in range(0, len(lang_shuffle), megabatch_size)] + + last_mm = mm_megabatches[-1] + last_lang = lang_megabatches[-1] + additional_batch = last_mm + last_lang + megabatches = mm_megabatches[:-1] + lang_megabatches[:-1] + megabatch_indices = torch.randperm(len(megabatches), generator=generator) + megabatches = [megabatches[i] for i in megabatch_indices] + + # FIXME: Hard code to avoid last batch mixed with different modalities + # if len(additional_batch) > 0: + # megabatches.append(sorted(additional_batch)) + + return [i for megabatch in megabatches for i in megabatch] + + +class LengthGroupedSampler(Sampler): + r""" + Sampler that samples indices in a way that groups together features of the dataset of roughly the same length while + keeping a bit of randomness. + """ + + def __init__( + self, + batch_size: int, + world_size: int, + lengths: Optional[List[int]] = None, + generator=None, + variable_length: bool = False, + group_by_modality: bool = False, + group_by_modality_auto: bool = False, + ): + if lengths is None: + raise ValueError("Lengths must be provided.") + + self.batch_size = batch_size + self.world_size = world_size + self.lengths = lengths + self.generator = generator + self.variable_length = variable_length + self.group_by_modality = group_by_modality + self.group_by_modality_auto = group_by_modality_auto + + def __len__(self): + return len(self.lengths) + + def __iter__(self): + if self.variable_length: + assert not self.group_by_modality, "Variable length grouping is not supported with modality grouping." + indices = get_variable_length_grouped_indices(self.lengths, self.batch_size, self.world_size, generator=self.generator) + else: + if self.group_by_modality: + indices = get_modality_length_grouped_indices(self.lengths, self.batch_size, self.world_size, generator=self.generator) + elif self.group_by_modality_auto: + indices = get_modality_length_grouped_indices_auto(self.lengths, self.batch_size, self.world_size, generator=self.generator) + else: + indices = get_length_grouped_indices_auto_single(self.lengths, self.batch_size, self.world_size, generator=self.generator) + return iter(indices) + + +class LLaVATrainer(Trainer): + + def create_accelerator_and_postprocess(self): + grad_acc_kwargs = {"num_steps": self.args.gradient_accumulation_steps} + grad_acc_kwargs["sync_with_dataloader"] = False + gradient_accumulation_plugin = GradientAccumulationPlugin(**grad_acc_kwargs) + + accelerator_kwargs = InitProcessGroupKwargs(timeout=timedelta(weeks=52)) + rank0_print("Setting NCCL timeout to INF to avoid running errors.") + + # create accelerator object + self.accelerator = Accelerator( + dispatch_batches=self.args.dispatch_batches, split_batches=self.args.split_batches, deepspeed_plugin=self.args.deepspeed_plugin, gradient_accumulation_plugin=gradient_accumulation_plugin, kwargs_handlers=[accelerator_kwargs] + ) + # some Trainer classes need to use `gather` instead of `gather_for_metrics`, thus we store a flag + self.gather_function = self.accelerator.gather_for_metrics + + # deepspeed and accelerate flags covering both trainer args and accelerate launcher + self.is_deepspeed_enabled = getattr(self.accelerator.state, "deepspeed_plugin", None) is not None + self.is_fsdp_enabled = getattr(self.accelerator.state, "fsdp_plugin", None) is not None + + # post accelerator creation setup + if self.is_fsdp_enabled: + fsdp_plugin = self.accelerator.state.fsdp_plugin + fsdp_plugin.limit_all_gathers = self.args.fsdp_config.get("limit_all_gathers", fsdp_plugin.limit_all_gathers) + if is_accelerate_available("0.23.0"): + fsdp_plugin.activation_checkpointing = self.args.fsdp_config.get("activation_checkpointing", fsdp_plugin.activation_checkpointing) + if fsdp_plugin.activation_checkpointing and self.args.gradient_checkpointing: + raise ValueError("The activation_checkpointing in FSDP config and the gradient_checkpointing in training arg " "can't be set to True simultaneously. Please use FSDP's activation_checkpointing logic " "when using FSDP.") + + if self.is_deepspeed_enabled and getattr(self.args, "hf_deepspeed_config", None) is None: + self.propagate_args_to_deepspeed() + + def _get_train_sampler(self) -> Optional[torch.utils.data.Sampler]: + if self.train_dataset is None or not has_length(self.train_dataset): + return None + + if self.args.group_by_length: + lengths = self.train_dataset.lengths + return LengthGroupedSampler( + # self.args.train_batch_size * self.args.gradient_accumulation_steps, # TODO: seems that we should not have gradient_accumulation_steps + self.args.train_batch_size, + # world_size=self.args.world_size, + world_size=self.args.world_size * self.args.gradient_accumulation_steps, # TODO: seems that this may work? + lengths=lengths, + ) + elif self.args.group_by_modality_length: + lengths = self.train_dataset.modality_lengths + return LengthGroupedSampler( + # self.args.train_batch_size * self.args.gradient_accumulation_steps, # TODO: seems that we should not have gradient_accumulation_steps + self.args.train_batch_size, + # world_size=self.args.world_size, + world_size=self.args.world_size * self.args.gradient_accumulation_steps, # TODO: seems that this may work? + lengths=lengths, + group_by_modality=True, + ) + elif self.args.group_by_modality_length_auto: + lengths = self.train_dataset.modality_lengths + return LengthGroupedSampler( + # self.args.train_batch_size * self.args.gradient_accumulation_steps, # TODO: seems that we should not have gradient_accumulation_steps + self.args.train_batch_size, + # world_size=self.args.world_size, + world_size=self.args.world_size * self.args.gradient_accumulation_steps, # TODO: seems that this may work? + lengths=lengths, + group_by_modality_auto=True, + ) + elif self.args.group_by_varlen: + lengths = self.train_dataset.lengths + return LengthGroupedSampler( + self.args.train_batch_size * self.args.gradient_accumulation_steps, + # self.args.train_batch_size, # TODO: seems that we should have gradient_accumulation_steps + # world_size=self.args.world_size, + world_size=self.args.world_size * self.args.gradient_accumulation_steps, # TODO: seems that this may work? + lengths=lengths, + variable_length=True, + ) + else: + return super()._get_train_sampler() + + def get_train_dataloader(self) -> DataLoader: + """ + Returns the training [`~torch.utils.data.DataLoader`]. + + Will use no sampler if `train_dataset` does not implement `__len__`, a random sampler (adapted to distributed + training if necessary) otherwise. + + Subclass and override this method if you want to inject some custom behavior. + """ + if self.train_dataset is None: + raise ValueError("Trainer: training requires a train_dataset.") + + train_dataset = self.train_dataset + data_collator = self.data_collator + if is_datasets_available() and isinstance(train_dataset, datasets.Dataset): + train_dataset = self._remove_unused_columns(train_dataset, description="training") + else: + data_collator = self._get_collator_with_removed_columns(data_collator, description="training") + + dataloader_params = { + "batch_size": self._train_batch_size, + "collate_fn": data_collator, + "num_workers": self.args.dataloader_num_workers, + "pin_memory": self.args.dataloader_pin_memory, + "persistent_workers": self.args.dataloader_persistent_workers, + } + + if not isinstance(train_dataset, torch.utils.data.IterableDataset): + dataloader_params["sampler"] = self._get_train_sampler() + dataloader_params["drop_last"] = self.args.dataloader_drop_last + dataloader_params["worker_init_fn"] = seed_worker + dataloader_params["prefetch_factor"] = self.args.dataloader_num_workers * 2 if self.args.dataloader_num_workers != 0 else None + + dataloader = self.accelerator.prepare(DataLoader(train_dataset, **dataloader_params)) + + return dataloader + + def create_optimizer(self): + """ + Setup the optimizer. + + We provide a reasonable default that works well. If you want to use something else, you can pass a tuple in the + Trainer's init through `optimizers`, or subclass and override this method in a subclass. + """ + if is_sagemaker_mp_enabled(): + return super().create_optimizer() + + opt_model = self.model + + if self.optimizer is None: + decay_parameters = get_parameter_names(opt_model, ALL_LAYERNORM_LAYERS) + decay_parameters = [name for name in decay_parameters if "bias" not in name] + lr_mapper = {} + if self.args.mm_projector_lr is not None: + lr_mapper["mm_projector"] = self.args.mm_projector_lr + if self.args.mm_vision_tower_lr is not None: + lr_mapper["vision_tower"] = self.args.mm_vision_tower_lr + if len(lr_mapper) > 0: + special_lr_parameters = [name for name, _ in opt_model.named_parameters() if any(module_keyword in name for module_keyword in lr_mapper)] + optimizer_grouped_parameters = [ + { + "params": [p for n, p in opt_model.named_parameters() if (n in decay_parameters and n not in special_lr_parameters and p.requires_grad)], + "weight_decay": self.args.weight_decay, + }, + { + "params": [p for n, p in opt_model.named_parameters() if (n not in decay_parameters and n not in special_lr_parameters and p.requires_grad)], + "weight_decay": 0.0, + }, + ] + for module_keyword, lr in lr_mapper.items(): + module_parameters = [name for name, _ in opt_model.named_parameters() if module_keyword in name] + optimizer_grouped_parameters.extend( + [ + { + "params": [p for n, p in opt_model.named_parameters() if (n in decay_parameters and n in module_parameters and p.requires_grad)], + "weight_decay": self.args.weight_decay, + "lr": lr, + }, + { + "params": [p for n, p in opt_model.named_parameters() if (n not in decay_parameters and n in module_parameters and p.requires_grad)], + "weight_decay": 0.0, + "lr": lr, + }, + ] + ) + else: + optimizer_grouped_parameters = [ + { + "params": [p for n, p in opt_model.named_parameters() if (n in decay_parameters and p.requires_grad)], + "weight_decay": self.args.weight_decay, + }, + { + "params": [p for n, p in opt_model.named_parameters() if (n not in decay_parameters and p.requires_grad)], + "weight_decay": 0.0, + }, + ] + + optimizer_cls, optimizer_kwargs = Trainer.get_optimizer_cls_and_kwargs(self.args) + + self.optimizer = optimizer_cls(optimizer_grouped_parameters, **optimizer_kwargs) + if optimizer_cls.__name__ == "Adam8bit": + import bitsandbytes + + manager = bitsandbytes.optim.GlobalOptimManager.get_instance() + + skipped = 0 + for module in opt_model.modules(): + if isinstance(module, nn.Embedding): + skipped += sum({p.data_ptr(): p.numel() for p in module.parameters()}.values()) + logger.info(f"skipped {module}: {skipped/2**20}M params") + manager.register_module_override(module, "weight", {"optim_bits": 32}) + logger.debug(f"bitsandbytes: will optimize {module} in fp32") + logger.info(f"skipped: {skipped/2**20}M params") + + return self.optimizer + + def _save_checkpoint(self, model, trial, metrics=None): + if getattr(self.args, "tune_mm_mlp_adapter", False) or ( + hasattr(self.args, "mm_tunable_parts") and (len(self.args.mm_tunable_parts.split(",")) == 1 and ("mm_mlp_adapter" in self.args.mm_tunable_parts or "mm_vision_resampler" in self.args.mm_tunable_parts)) + ): + from transformers.trainer_utils import PREFIX_CHECKPOINT_DIR + + checkpoint_folder = f"{PREFIX_CHECKPOINT_DIR}-{self.state.global_step}" + + run_dir = self._get_output_dir(trial=trial) + output_dir = os.path.join(run_dir, checkpoint_folder) + + # Only save Adapter + keys_to_match = ["mm_projector", "vision_resampler"] + if getattr(self.args, "use_im_start_end", False): + keys_to_match.extend(["embed_tokens", "embed_in"]) + + weight_to_save = get_mm_adapter_state_maybe_zero_3(self.model.named_parameters(), keys_to_match) + + if self.args.local_rank == 0 or self.args.local_rank == -1: + self.model.config.save_pretrained(output_dir) + torch.save(weight_to_save, os.path.join(output_dir, f"mm_projector.bin")) + else: + super(LLaVATrainer, self)._save_checkpoint(model, trial, metrics) + + def _save(self, output_dir: Optional[str] = None, state_dict=None): + if getattr(self.args, "tune_mm_mlp_adapter", False): + pass + else: + super(LLaVATrainer, self)._save(output_dir, state_dict) + + +class LLaVADPOTrainer(DPOTrainer): + def _get_train_sampler(self) -> Optional[torch.utils.data.Sampler]: + if self.train_dataset is None or not has_length(self.train_dataset): + return None + + if self.args.group_by_modality_length: + lengths = self.train_dataset.modality_lengths + return LengthGroupedSampler( + # self.args.train_batch_size * self.args.gradient_accumulation_steps, # TODO: seems that we should not have gradient_accumulation_steps + self.args.train_batch_size, + world_size=self.args.world_size, + lengths=lengths, + group_by_modality=True, + ) + else: + return super()._get_train_sampler() + + def _save_checkpoint(self, model, trial, metrics=None): + if getattr(self.args, "tune_mm_mlp_adapter", False) or ( + hasattr(self.args, "mm_tunable_parts") and (len(self.args.mm_tunable_parts.split(",")) == 1 and ("mm_mlp_adapter" in self.args.mm_tunable_parts or "mm_vision_resampler" in self.args.mm_tunable_parts)) + ): + from transformers.trainer_utils import PREFIX_CHECKPOINT_DIR + + checkpoint_folder = f"{PREFIX_CHECKPOINT_DIR}-{self.state.global_step}" + + run_dir = self._get_output_dir(trial=trial) + output_dir = os.path.join(run_dir, checkpoint_folder) + + # Only save Adapter + keys_to_match = ["mm_projector", "vision_resampler"] + if getattr(self.args, "use_im_start_end", False): + keys_to_match.extend(["embed_tokens", "embed_in"]) + + weight_to_save = get_mm_adapter_state_maybe_zero_3(self.model.named_parameters(), keys_to_match) + + if self.args.local_rank == 0 or self.args.local_rank == -1: + self.model.config.save_pretrained(output_dir) + torch.save(weight_to_save, os.path.join(output_dir, f"mm_projector.bin")) + else: + # super(LLaVADPOTrainer, self)._save_checkpoint(model, trial, metrics) + # print(type(model)) + # from transformers.modeling_utils import unwrap_model + # print(type(unwrap_model(model))) + # print(unwrap_model(model).config) + if self.args.lora_enable: + from transformers.trainer_utils import PREFIX_CHECKPOINT_DIR + + checkpoint_folder = f"{PREFIX_CHECKPOINT_DIR}-{self.state.global_step}" + run_dir = self._get_output_dir(trial=trial) + output_dir = os.path.join(run_dir, checkpoint_folder) + from transformers.modeling_utils import unwrap_model + + unwrapped_model = unwrap_model(model) + self.save_my_lora_ckpt(output_dir, self.args, unwrapped_model) + else: + super(LLaVADPOTrainer, self)._save_checkpoint(model, trial, metrics) + + def _save(self, output_dir: Optional[str] = None, state_dict=None): + if getattr(self.args, "tune_mm_mlp_adapter", False): + pass + else: + super(LLaVADPOTrainer, self)._save(output_dir, state_dict) diff --git a/llava/train/llava_trainer_eval.py b/llava/train/llava_trainer_eval.py new file mode 100644 index 0000000..e822258 --- /dev/null +++ b/llava/train/llava_trainer_eval.py @@ -0,0 +1,76 @@ +import json +import subprocess + +from llava.train.llava_trainer import LLaVATrainer + + +class LLaVAEvalTrainer(LLaVATrainer): + def evaluate(self, evaluate_args): + cmd = f"accelerate launch --num_processes {evaluate_args.eval_num_processes} -m lmms_eval \ + --model {evaluate_args.model} \ + --model_args {evaluate_args.model_args} \ + --tasks {evaluate_args.task_names} \ + --batch_size {evaluate_args.batch_size} \ + --log_samples_suffix {evaluate_args.log_samples_suffix} \ + --output_path {evaluate_args.output_path}" + if evaluate_args.limit: + cmd += f" --limit {evaluate_args.limit}" + if evaluate_args.num_fewshot: + cmd += f" --num_fewshot {evaluate_args.num_fewshot}" + if evaluate_args.gen_kwargs != "": + cmd += f" --gen_kwargs {evaluate_args.gen_kwargs}" + if evaluate_args.log_samples: + cmd += f" --log_samples" + else: + assert False, "Please log samples so that the result can be parsed" + results = subprocess.run([cmd], shell=True, capture_output=True, text=True) + try: + result_file_index_start = results.stdout.index("Saved samples to ") + result_file_index_end = results.stdout.index(f".json") + result_file_index_start += len("Saved samples to ") + file = results.stdout[result_file_index_start:result_file_index_end] + except: + result_file_index_start = results.stderr.index("Saved samples to ") + result_file_index_end = results.stderr.index(f".json") + result_file_index_start += len("Saved samples to ") + file = results.stderr[result_file_index_start:result_file_index_end] + file = file.split("/")[:-1] + file = "/".join(file) + "/results.json" + with open(file, "r") as f: + lmms_eval_results = json.load(f) + result_dict = {} + tasks_list = evaluate_args.task_names.split(",") + for task in tasks_list: + task_results = lmms_eval_results["results"][task] + for k, v in task_results.items(): + if k != "alias" and "stderr" not in k: + metric = k.split(",")[0] + result_dict[f"{task}_{metric}"] = v + return result_dict + + """def evaluate(self, evaluate_args): + initialize_tasks() + tasks_list = evaluate_args.task_names.split(",") + result_dict = {} + results = evaluator.simple_evaluate( + model=evaluate_args.model, + model_args=evaluate_args.model_args, + tasks=tasks_list, + num_fewshot=evaluate_args.num_fewshot, + batch_size=evaluate_args.batch_size, + device=evaluate_args.device, + limit=evaluate_args.limit, + check_integrity=evaluate_args.check_integrity, + show_task_to_terminal=evaluate_args.show_task_to_terminal, + log_samples=evaluate_args.log_samples, + gen_kwargs=evaluate_args.gen_kwargs, + cli_args=evaluate_args, + ) + for task in tasks_list: + task_results = results["results"][task] + for k,v in task_results.items(): + if k != "alias" and "stderr" not in k: + metric = k.split(",")[0] + result_dict[f"{task}_{metric}"] = v + + return result_dict""" diff --git a/llava/train/train.py b/llava/train/train.py new file mode 100644 index 0000000..baeef64 --- /dev/null +++ b/llava/train/train.py @@ -0,0 +1,1873 @@ +# Adopted from https://github.com/lm-sys/FastChat. Below is the original copyright: +# Adopted from tatsu-lab@stanford_alpaca. Below is the original copyright: +# Copyright 2023 Rohan Taori, Ishaan Gulrajani, Tianyi Zhang, Yann Dubois, Xuechen Li +# +# 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. + +import ast +import os +import copy +from dataclasses import dataclass, field +import json +import logging +import pathlib +from typing import Dict, Optional, Sequence, List +from PIL import Image, ImageFile +from packaging import version +import numpy as np + +import time +import random +import yaml +import math +import re +import torch + +import transformers +import tokenizers +import deepspeed + +from transformers import AutoConfig +from torch.utils.data import Dataset +from llava.constants import IGNORE_INDEX, DEFAULT_IMAGE_TOKEN, DEFAULT_IM_START_TOKEN, DEFAULT_IM_END_TOKEN, IMAGE_TOKEN_INDEX +from llava.train.llava_trainer import LLaVATrainer + +from llava.actiontokenizer import ActionTokenizer +from llava import conversation as conversation_lib +from llava.model import * +from llava.mm_utils import process_highres_image, process_anyres_image, process_highres_image_crop_split, tokenizer_image_token +from llava.utils import rank0_print, process_video_with_pyav, process_video_with_decord + +torch.multiprocessing.set_sharing_strategy("file_system") + +ImageFile.LOAD_TRUNCATED_IMAGES = True +local_rank = None + +IS_TOKENIZER_GREATER_THAN_0_14 = version.parse(tokenizers.__version__) >= version.parse("0.14") + + +@dataclass +class ModelArguments: + model_name_or_path: Optional[str] = field(default="facebook/opt-125m") + model_class_name: Optional[str] = field(default=None, metadata={"help": "Used to init model class, format is XXXXForCausalLM. e.g. currently XXXX is chosen from LlavaLlama, LlavaMixtral, LlavaMistral, Llama"}) + + mm_tunable_parts: Optional[str] = field( + default=None, metadata={"help": 'Could be "mm_mlp_adapter", "mm_vision_resampler", "mm_vision_tower,mm_mlp_adapter,mm_language_model", "mm_vision_tower,mm_mlp_adapter,mm_language_model", "mm_mlp_adapter,mm_language_model"'} + ) + # deciding which part of the multimodal model to tune, will overwrite other previous settings + + version: Optional[str] = field(default="v0") + freeze_backbone: bool = field(default=False) + tune_mm_mlp_adapter: bool = field(default=False) + tune_mm_vision_resampler: bool = field(default=False) + vision_tower: Optional[str] = field(default=None) + vision_tower_pretrained: Optional[str] = field(default=None) # default to the last layer + + unfreeze_mm_vision_tower: bool = field(default=False) + unfreeze_language_model: bool = field(default=False) + mm_vision_select_layer: Optional[int] = field(default=-1) # default to the last layer + pretrain_mm_mlp_adapter: Optional[str] = field(default=None) + mm_projector_type: Optional[str] = field(default="linear") + mm_use_im_start_end: bool = field(default=False) + mm_use_im_patch_token: bool = field(default=True) + mm_patch_merge_type: Optional[str] = field(default="flat") + mm_vision_select_feature: Optional[str] = field(default="patch") + mm_resampler_type: Optional[str] = field(default=None) + mm_mask_drop_mode: str = field(default="fixed") + mm_mask_drop_skip_percentage: float = field(default=0.0) + mm_mask_drop_ratio: float = field(default=0.25) + mm_mask_drop_ratio_upper: Optional[float] = field(default=None) + mm_mask_drop_ratio_lower: Optional[float] = field(default=None) + mm_spatial_pool_stride: Optional[int] = field(default=None) + mm_spatial_pool_mode: str = field(default="bilinear") + mm_spatial_pool_out_channels: Optional[int] = field(default=None) + mm_perceiver_depth: Optional[int] = field(default=3) + mm_perceiver_latents: Optional[int] = field(default=32) + mm_perceiver_ff_mult: Optional[float] = field(default=4) + mm_perceiver_pretrained: Optional[str] = field(default=None) + mm_qformer_depth: Optional[int] = field(default=3) + mm_qformer_latents: Optional[int] = field(default=32) + mm_qformer_pretrained: Optional[str] = field(default=None) + + rope_scaling_factor: Optional[float] = field(default=None) + rope_scaling_type: Optional[str] = field(default=None) + + s2: Optional[bool] = field(default=False) + s2_scales: Optional[str] = field(default="336,672,1008") + + use_pos_skipping: Optional[bool] = field(default=False) + pos_skipping_range: Optional[int] = field(default=4096) + + + mm_newline_position: Optional[str] = field(default="grid") + delay_load: Optional[bool] = field(default=True) + add_faster_video: Optional[bool] = field(default=False) + faster_token_stride: Optional[int] = field(default=10) + + + +@dataclass +class DataArguments: + data_path: str = field(default=None, metadata={"help": "Path to the training data, in llava's instruction.json format. Supporting multiple json files via /path/to/{a,b,c}.json"}) + lazy_preprocess: bool = False + is_multimodal: bool = False + early_mix_text: bool = False + image_folder: Optional[str] = field(default=None) + image_aspect_ratio: str = "square" + image_grid_pinpoints: Optional[str] = field(default=None) + image_crop_resolution: Optional[int] = field(default=None) + image_split_resolution: Optional[int] = field(default=None) + + video_folder: Optional[str] = field(default=None) + video_fps: Optional[int] = field(default=1) + frames_upbound: Optional[int] = field(default=0) + add_time_instruction: Optional[bool] = field(default=False) + force_sample: Optional[bool] = field(default=False) + + +@dataclass +class TrainingArguments(transformers.TrainingArguments): + cache_dir: Optional[str] = field(default=None) + optim: str = field(default="adamw_torch") + remove_unused_columns: bool = field(default=False) + freeze_mm_mlp_adapter: bool = field(default=False) + freeze_mm_vision_resampler: bool = field(default=False) + mpt_attn_impl: Optional[str] = field(default="triton") + model_max_length: int = field( + default=4096, + metadata={"help": "Maximum sequence length. Sequences will be right padded (and possibly truncated)."}, + ) + double_quant: bool = field(default=True, metadata={"help": "Compress the quantization statistics through double quantization."}) + quant_type: str = field(default="nf4", metadata={"help": "Quantization data type to use. Should be one of `fp4` or `nf4`."}) + bits: int = field(default=16, metadata={"help": "How many bits to use."}) + lora_enable: bool = False + lora_r: int = 64 + lora_alpha: int = 16 + lora_dropout: float = 0.05 + lora_weight_path: str = "" + lora_bias: str = "none" + mm_projector_lr: Optional[float] = None + mm_vision_tower_lr: Optional[float] = None + group_by_varlen: bool = field(default=False) + group_by_modality_length: bool = field(default=False) + group_by_modality_length_auto: bool = field(default=False) + auto_find_batch_size: bool = field(default=False) + gradient_checkpointing: bool = field(default=True) + verbose_logging: bool = field(default=False) + attn_implementation: str = field(default="flash_attention_2", metadata={"help": "Use transformers attention implementation."}) + + +# @dataclass +# class EvaluationArguments: +# eval_num_processes: int = field(default=1) +# task_names: str = field(default=None) +# model: str = field(default="llava") +# model_args: Optional[str] = field(default=None) +# num_fewshot: Optional[int] = field(default=None) +# batch_size: int = field(default=1) +# device: Optional[str] = field(default=None) +# limit: Optional[int] = field(default=None) +# check_integrity: Optional[bool] = field(default=False) +# show_task_to_terminal: Optional[bool] = field(default=False) +# log_samples: Optional[bool] = field(default=True) +# gen_kwargs: Optional[str] = field(default="") +# log_samples_suffix: Optional[str] = field(default="") +# output_path: Optional[str] = field(default="./logs/") + + +def maybe_zero_3(param, ignore_status=False, name=None): + from deepspeed import zero + from deepspeed.runtime.zero.partition_parameters import ZeroParamStatus + + if hasattr(param, "ds_id"): + if param.ds_status == ZeroParamStatus.NOT_AVAILABLE: + if not ignore_status: + logging.warning(f"{name}: param.ds_status != ZeroParamStatus.NOT_AVAILABLE: {param.ds_status}") + with zero.GatheredParameters([param]): + param = param.data.detach().cpu().clone() + else: + param = param.detach().cpu().clone() + return param + + +# Borrowed from peft.utils.get_peft_model_state_dict +def get_peft_state_maybe_zero_3(named_params, bias): + if bias == "none": + to_return = {k: t for k, t in named_params if "lora_" in k} + elif bias == "all": + to_return = {k: t for k, t in named_params if "lora_" in k or "bias" in k} + elif bias == "lora_only": + to_return = {} + maybe_lora_bias = {} + lora_bias_names = set() + for k, t in named_params: + if "lora_" in k: + to_return[k] = t + bias_name = k.split("lora_")[0] + "bias" + lora_bias_names.add(bias_name) + elif "bias" in k: + maybe_lora_bias[k] = t + for k, t in maybe_lora_bias: + if bias_name in lora_bias_names: + to_return[bias_name] = t + else: + raise NotImplementedError + to_return = {k: maybe_zero_3(v, ignore_status=True) for k, v in to_return.items()} + return to_return + + +def get_peft_state_non_lora_maybe_zero_3(named_params, require_grad_only=True): + to_return = {k: t for k, t in named_params if "lora_" not in k} + if require_grad_only: + to_return = {k: t for k, t in to_return.items() if t.requires_grad} + to_return = {k: maybe_zero_3(v, ignore_status=True).cpu() for k, v in to_return.items()} + return to_return + + +def get_mm_adapter_state_maybe_zero_3(named_params, keys_to_match): + to_return = {k: t for k, t in named_params if any(key_match in k for key_match in keys_to_match)} + to_return = {k: maybe_zero_3(v, ignore_status=True).cpu() for k, v in to_return.items()} + return to_return + + +def find_all_linear_names(model): + cls = torch.nn.Linear + lora_module_names = set() + multimodal_keywords = ["mm_projector", "vision_tower", "vision_resampler"] + for name, module in model.named_modules(): + if any(mm_keyword in name for mm_keyword in multimodal_keywords): + continue + if isinstance(module, cls): + names = name.split(".") + lora_module_names.add(names[0] if len(names) == 1 else names[-1]) + + if "lm_head" in lora_module_names: # needed for 16-bit + lora_module_names.remove("lm_head") + return list(lora_module_names) + + +def safe_save_model_for_hf_trainer(trainer: transformers.Trainer, output_dir: str): + """Collects the state dict and dump to disk.""" + if hasattr(trainer.args, "tune_mm_mlp_adapter") and trainer.args.tune_mm_mlp_adapter: + check_only_save_mm_adapter_tunnable = True + # only has mm_mlp_adapter and mm_vision_resampler in the tuneable parts + elif hasattr(trainer.args, "mm_tunable_parts") and (len(trainer.args.mm_tunable_parts.split(",")) == 1 and ("mm_mlp_adapter" in trainer.args.mm_tunable_parts or "mm_vision_resampler" in trainer.args.mm_tunable_parts)): + check_only_save_mm_adapter_tunnable = True + else: + check_only_save_mm_adapter_tunnable = False + + trainer.accelerator.wait_for_everyone() + torch.cuda.synchronize() + rank0_print(f"Only save projectors: {check_only_save_mm_adapter_tunnable}") + if check_only_save_mm_adapter_tunnable: + # Only save Adapter + keys_to_match = ["mm_projector", "vision_resampler"] + if getattr(trainer.args, "use_im_start_end", False): + keys_to_match.extend(["embed_tokens", "embed_in"]) + + weight_to_save = get_mm_adapter_state_maybe_zero_3(trainer.model.named_parameters(), keys_to_match) + trainer.model.config.save_pretrained(output_dir) + + current_folder = output_dir.split("/")[-1] + parent_folder = os.path.dirname(output_dir) + if trainer.args.local_rank == 0 or trainer.args.local_rank == -1: + if current_folder.startswith("checkpoint-"): + mm_projector_folder = os.path.join(parent_folder, "mm_projector") + os.makedirs(mm_projector_folder, exist_ok=True) + torch.save(weight_to_save, os.path.join(mm_projector_folder, f"{current_folder}.bin")) + else: + torch.save(weight_to_save, os.path.join(output_dir, f"mm_projector.bin")) + return + + if trainer.deepspeed: + trainer.save_model(output_dir) + return + + state_dict = trainer.model.state_dict() + if trainer.args.should_save: + cpu_state_dict = {key: value.cpu() for key, value in state_dict.items()} + del state_dict + trainer._save(output_dir, state_dict=cpu_state_dict) # noqa + + +def smart_tokenizer_and_embedding_resize( + special_tokens_dict: Dict, + tokenizer: transformers.PreTrainedTokenizer, + model: transformers.PreTrainedModel, +): + """Resize tokenizer and embedding. + + Note: This is the unoptimized version that may make your embedding size not be divisible by 64. + """ + num_new_tokens = tokenizer.add_special_tokens(special_tokens_dict) + model.resize_token_embeddings(len(tokenizer)) + + if num_new_tokens > 0: + input_embeddings = model.get_input_embeddings().weight.data + output_embeddings = model.get_output_embeddings().weight.data + + input_embeddings_avg = input_embeddings[:-num_new_tokens].mean(dim=0, keepdim=True) + output_embeddings_avg = output_embeddings[:-num_new_tokens].mean(dim=0, keepdim=True) + + input_embeddings[-num_new_tokens:] = input_embeddings_avg + output_embeddings[-num_new_tokens:] = output_embeddings_avg + + +def _tokenize_fn(strings: Sequence[str], tokenizer: transformers.PreTrainedTokenizer) -> Dict: + """Tokenize a list of strings.""" + tokenized_list = [ + tokenizer( + text, + return_tensors="pt", + padding="longest", + max_length=tokenizer.model_max_length, + truncation=True, + ) + for text in strings + ] + input_ids = labels = [tokenized.input_ids[0] for tokenized in tokenized_list] + input_ids_lens = labels_lens = [tokenized.input_ids.ne(tokenizer.pad_token_id).sum().item() for tokenized in tokenized_list] + return dict( + input_ids=input_ids, + labels=labels, + input_ids_lens=input_ids_lens, + labels_lens=labels_lens, + ) + + +def _mask_targets(target, tokenized_lens, speakers): + # cur_idx = 0 + cur_idx = tokenized_lens[0] + tokenized_lens = tokenized_lens[1:] + target[:cur_idx] = IGNORE_INDEX + for tokenized_len, speaker in zip(tokenized_lens, speakers): + if speaker == "human": + target[cur_idx + 2 : cur_idx + tokenized_len] = IGNORE_INDEX + cur_idx += tokenized_len + + +def _add_speaker_and_signal(header, source, get_conversation=True): + """Add speaker and start/end signal on each round.""" + BEGIN_SIGNAL = "### " + END_SIGNAL = "\n" + conversation = header + for sentence in source: + from_str = sentence["from"] + if from_str.lower() == "human": + from_str = conversation_lib.default_conversation.roles[0] + elif from_str.lower() == "gpt": + from_str = conversation_lib.default_conversation.roles[1] + else: + from_str = "unknown" + sentence["value"] = BEGIN_SIGNAL + from_str + ": " + sentence["value"] + END_SIGNAL + if get_conversation: + conversation += sentence["value"] + conversation += BEGIN_SIGNAL + return conversation + + +def preprocess_multimodal(sources: Sequence[str], data_args: DataArguments) -> Dict: + is_multimodal = data_args.is_multimodal + if not is_multimodal: + return sources + + for source in sources: + for sentence in source: + # TODO maybe this should be changed for interleaved data? + # if DEFAULT_IMAGE_TOKEN in sentence["value"] and not sentence["value"].startswith(DEFAULT_IMAGE_TOKEN): + # only check for num_im=1 + if isinstance(sentence["value"], str): + num_im = len(re.findall(DEFAULT_IMAGE_TOKEN, sentence["value"])) + else: + num_im = 0 + if num_im == 1 and DEFAULT_IMAGE_TOKEN in sentence["value"] and not sentence["value"].startswith(DEFAULT_IMAGE_TOKEN): + sentence["value"] = sentence["value"].replace(DEFAULT_IMAGE_TOKEN, "").strip() + sentence["value"] = DEFAULT_IMAGE_TOKEN + "\n" + sentence["value"] + sentence["value"] = sentence["value"].strip() + if "mmtag" in conversation_lib.default_conversation.version: + sentence["value"] = sentence["value"].replace(DEFAULT_IMAGE_TOKEN, "" + DEFAULT_IMAGE_TOKEN + "") + replace_token = DEFAULT_IMAGE_TOKEN + if data_args.mm_use_im_start_end: + replace_token = DEFAULT_IM_START_TOKEN + replace_token + DEFAULT_IM_END_TOKEN + + if isinstance(sentence["value"], str): + sentence["value"] = sentence["value"].replace(DEFAULT_IMAGE_TOKEN, replace_token) + + # For videoInstruct-100k noisy_data. TODO: Ask Yuanhan to clean the data instead of leaving the noise code here. + if isinstance(sentence["value"], str): + sentence["value"] = sentence["value"].replace("QA_GT_caption_based_noisy", "") + + return sources + + +def preprocess_llama_2(sources, tokenizer: transformers.PreTrainedTokenizer, has_image: bool = False) -> Dict: + conv = conversation_lib.default_conversation.copy() + roles = {"human": conv.roles[0], "gpt": conv.roles[1]} + + # Apply prompt templates + conversations = [] + for i, source in enumerate(sources): + if roles[source[0]["from"]] != conv.roles[0]: + # Skip the first one if it is not from human + source = source[1:] + + conv.messages = [] + for j, sentence in enumerate(source): + role = roles[sentence["from"]] + assert role == conv.roles[j % 2], f"{i}" + conv.append_message(role, sentence["value"]) + conversations.append(conv.get_prompt()) + + # Tokenize conversations + + if has_image: + input_ids = torch.stack([tokenizer_image_token(prompt, tokenizer, return_tensors="pt") for prompt in conversations], dim=0) + else: + input_ids = tokenizer( + conversations, + return_tensors="pt", + padding="longest", + max_length=tokenizer.model_max_length, + truncation=True, + ).input_ids + + targets = input_ids.clone() + + assert conv.sep_style == conversation_lib.SeparatorStyle.LLAMA_2 + + # Mask targets + sep = "[/INST] " + for conversation, target in zip(conversations, targets): + total_len = int(target.ne(tokenizer.pad_token_id).sum()) + + rounds = conversation.split(conv.sep2) + cur_len = 1 + target[:cur_len] = IGNORE_INDEX + for i, rou in enumerate(rounds): + if rou == "": + break + + parts = rou.split(sep) + if len(parts) != 2: + break + parts[0] += sep + + if has_image: + round_len = len(tokenizer_image_token(rou, tokenizer)) + instruction_len = len(tokenizer_image_token(parts[0], tokenizer)) - 2 + else: + round_len = len(tokenizer(rou).input_ids) + instruction_len = len(tokenizer(parts[0]).input_ids) - 2 + + target[cur_len : cur_len + instruction_len] = IGNORE_INDEX + + cur_len += round_len + target[cur_len:] = IGNORE_INDEX + + if cur_len < tokenizer.model_max_length: + if cur_len != total_len: + target[:] = IGNORE_INDEX + print(f"WARNING: tokenization mismatch: {cur_len} vs. {total_len}." f" (ignored)") + + return dict( + input_ids=input_ids, + labels=targets, + ) + + +def preprocess_gemma(sources: List[List[Dict[str, str]]], tokenizer: transformers.PreTrainedTokenizer, has_image: bool = False) -> Dict: + conv: conversation_lib.Conversation = conversation_lib.default_conversation.copy() + roles: Dict[str, str] = {"human": conv.roles[0], "gpt": conv.roles[1]} + + # Apply prompt templates + conversations: List[str] = [] + for i, source in enumerate(sources): + if roles[source[0]["from"]] != conv.roles[0]: + # Skip the first one if it is not from human + source: List[Dict[str, str]] = source[1:] + + conv.messages = [] + for j, sentence in enumerate(source): + role: str = roles[sentence["from"]] + assert role == conv.roles[j % 2], f"{i}" + conv.append_message(role, sentence["value"]) + conversations.append(conv.get_prompt()) + + # Tokenize conversations + if has_image: + input_ids: torch.Tensor = torch.stack([tokenizer_image_token(prompt, tokenizer, return_tensors="pt") for prompt in conversations], dim=0) + else: + input_ids: torch.Tensor = tokenizer( + conversations, + return_tensors="pt", + padding="longest", + max_length=tokenizer.model_max_length, + truncation=True, + ).input_ids + + targets: torch.Tensor = input_ids.clone() + assert conv.sep_style == conversation_lib.SeparatorStyle.GEMMA + + # Mask target + sep: str = conv.sep + conv.roles[1] + for conversation, target in zip(conversations, targets): + total_len: int = int(target.ne(tokenizer.pad_token_id).sum()) + + rounds: List[str] = conversation.split(conv.sep) + re_rounds = [] + for conv_idx in range(0, len(rounds), 2): + re_rounds.append(conv.sep.join(rounds[conv_idx : conv_idx + 2])) + + cur_len = 1 # Ignore + target[:cur_len] = IGNORE_INDEX + for i, rou in enumerate(re_rounds): + if rou == "": + break + + parts = rou.split(sep) + if len(parts) != 2: + break + parts[0] += sep # Re-append sep because split on this + # Now "".join(parts)==rou + + if has_image: + round_len = len(tokenizer_image_token(rou, tokenizer)) - 1 # Ignore + instruction_len = len(tokenizer_image_token(parts[0], tokenizer)) - 1 # Ignore + else: + round_len = len(tokenizer(rou).input_ids) - 1 # Ignore + instruction_len = len(tokenizer(parts[0]).input_ids) - 1 # Ignore + + round_len += 2 # sep: \n takes 2 tokens + target[cur_len : cur_len + instruction_len] = IGNORE_INDEX + cur_len += round_len + + target[cur_len:] = IGNORE_INDEX + + if cur_len < tokenizer.model_max_length: + if cur_len != total_len: + target[:] = IGNORE_INDEX + print(f"warning: tokenization mismatch: {cur_len} vs. {total_len}." f" (ignored)") + + return dict( + input_ids=input_ids, + labels=targets, + ) + + +def preprocess_qwen(sources, tokenizer: transformers.PreTrainedTokenizer, has_image: bool = False, max_len=2048, system_message: str = "You are a helpful assistant.") -> Dict: + # roles = {"human": "<|im_start|>user", "gpt": "<|im_start|>assistant"} + roles = {"human": "user", "gpt": "assistant"} + + # Add image tokens to tokenizer as a special tokens + # Use a deepcopy of tokenizer so that we don't modify on the tokenizer + tokenizer = copy.deepcopy(tokenizer) + # When there is actually an image, we add the image tokens as a special token + if has_image: + tokenizer.add_tokens([""], special_tokens=True) + + image_token_index = tokenizer.convert_tokens_to_ids("") + im_start, im_end = tokenizer.additional_special_tokens_ids + # unmask_tokens = ["<|im_start|>", "<|im_start|>", "\n"] + unmask_tokens_idx = [198, im_start, im_end] + nl_tokens = tokenizer("\n").input_ids + + # Reset Qwen chat templates so that it won't include system message every time we apply + chat_template = "{% for message in messages %}{{'<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant\n' }}{% endif %}" + tokenizer.chat_template = chat_template + + # _system = tokenizer("system").input_ids + nl_tokens + # _user = tokenizer("user").input_ids + nl_tokens + # _assistant = tokenizer("assistant").input_ids + nl_tokens + + # Apply prompt templates + input_ids, targets = [], [] + for i, source in enumerate(sources): + if roles[source[0]["from"]] != roles["human"]: + source = source[1:] + + input_id, target = [], [] + + # New version, use apply chat template + # Build system message for each sentence + input_id += tokenizer.apply_chat_template([{"role" : "system", "content" : system_message}]) + target += [IGNORE_INDEX] * len(input_id) + + for conv in source: + # Make sure llava data can load + try: + role = conv["role"] + content = conv["content"] + except: + role = conv["from"] + content = conv["value"] + + role = roles.get(role, role) + + conv = [{"role" : role, "content" : content}] + encode_id = tokenizer.apply_chat_template(conv) + + # print('type of encode_id:',type(encode_id)) + # print('encode_id:',encode_id) + + input_id += encode_id + if role in ["user", "system"]: + target += [IGNORE_INDEX] * len(encode_id) + else: + target += encode_id + + + + assert len(input_id) == len(target), f"{len(input_id)} != {len(target)}" + for idx, encode_id in enumerate(input_id): + if encode_id in unmask_tokens_idx: + target[idx] = encode_id + if encode_id == image_token_index: + input_id[idx] = IMAGE_TOKEN_INDEX + input_ids.append(input_id) + targets.append(target) + input_ids = torch.tensor(input_ids, dtype=torch.long) + targets = torch.tensor(targets, dtype=torch.long) + + return dict( + input_ids=input_ids, # tensor(bs x seq_len) + labels=targets, # tensor(bs x seq_len) + ) + +def preprocess_qwen_vla(sources, tokenizer: transformers.PreTrainedTokenizer, has_image: bool = False, max_len=2048, system_message: str = "You are a helpful assistant.") -> Dict: + # roles = {"human": "<|im_start|>user", "gpt": "<|im_start|>assistant"} + roles = {"human": "user", "gpt": "assistant"} + + # Add image tokens to tokenizer as a special tokens + # Use a deepcopy of tokenizer so that we don't modify on the tokenizer + tokenizer = copy.deepcopy(tokenizer) + action_tokenizer = ActionTokenizer(tokenizer) + # When there is actually an image, we add the image tokens as a special token + if has_image: + tokenizer.add_tokens([""], special_tokens=True) + + image_token_index = tokenizer.convert_tokens_to_ids("") + im_start, im_end = tokenizer.additional_special_tokens_ids + # unmask_tokens = ["<|im_start|>", "<|im_start|>", "\n"] + unmask_tokens_idx = [198, im_start, im_end] + nl_tokens = tokenizer("\n").input_ids + + # Reset Qwen chat templates so that it won't include system message every time we apply + chat_template = "{% for message in messages %}{{'<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant\n' }}{% endif %}" + tokenizer.chat_template = chat_template + + # _system = tokenizer("system").input_ids + nl_tokens + # _user = tokenizer("user").input_ids + nl_tokens + # _assistant = tokenizer("assistant").input_ids + nl_tokens + + # Apply prompt templates + input_ids, targets = [], [] + for i, source in enumerate(sources): + if roles[source[0]["from"]] != roles["human"]: + source = source[1:] + + input_id, target = [], [] + + # New version, use apply chat template + # Build system message for each sentence + input_id += tokenizer.apply_chat_template([{"role" : "system", "content" : system_message}]) + target += [IGNORE_INDEX] * len(input_id) + + for conv in source: + # Make sure llava data can load + try: + role = conv["role"] + content = conv["content"] + except: + role = conv["from"] + content = conv["value"] + + role = roles.get(role, role) + + # 动作编码 + if role == "assistant": + action_id = action_tokenizer(np.array(content)) + conv = [{"role" : role, "content" : str(content)}] + rendered_text = tokenizer.apply_chat_template(conv,tokenize=False) + # 占位符 + placeholder = '[RPL]' + rendered_text_modified = rendered_text.replace(str(content), placeholder) + tokenized_output = tokenizer(rendered_text_modified,add_special_tokens=False,return_tensors='pt') + temp = tokenized_output['input_ids'] + # 获取占位符的 token IDs + placeholder_ids = tokenizer(placeholder, add_special_tokens=False)['input_ids'] + + def find_subsequence(main_seq, sub_seq): + for i in range(len(main_seq) - len(sub_seq) + 1): + if main_seq[i:i+len(sub_seq)] == sub_seq: + return i + return -1 + + main_seq = temp.squeeze().tolist() + sub_seq = placeholder_ids + + idx = find_subsequence(main_seq, sub_seq) + if idx == -1: + raise ValueError("Placeholder not found in input_ids") + + #action_id是一个ndarray,需要转换成list + new_input_ids = action_id.tolist() + + # print(type(new_input_ids)) + # print('new_input_ids:',new_input_ids) + # print(main_seq[:idx]) + # print(main_seq[idx+len(sub_seq):]) + + # 拼接三个列表成一个列表 + + encode_id = main_seq[:idx] + new_input_ids + main_seq[idx+len(sub_seq):] + # print('encode_id:',encode_id) + + + else: + # print('role:',role) + # print('content:',content) + conv = [{"role" : role, "content" : content}] + encode_id = tokenizer.apply_chat_template(conv) + input_id += encode_id + if role in ["user", "system"]: + target += [IGNORE_INDEX] * len(encode_id) + else: + target += encode_id + + + assert len(input_id) == len(target), f"{len(input_id)} != {len(target)}" + for idx, encode_id in enumerate(input_id): + if encode_id in unmask_tokens_idx: + target[idx] = encode_id + if encode_id == image_token_index: + input_id[idx] = IMAGE_TOKEN_INDEX + input_ids.append(input_id) + targets.append(target) + input_ids = torch.tensor(input_ids, dtype=torch.long) + targets = torch.tensor(targets, dtype=torch.long) + # print(input_ids.shape) + # print(targets.shape) + return dict( + input_ids=input_ids, # tensor(bs x seq_len) + labels=targets, # tensor(bs x seq_len) + ) + + + +def preprocess_llama3( + sources, + tokenizer: transformers.PreTrainedTokenizer, + has_image: bool = False, + max_len=2048, + system_message: str = "You are a helpful language and vision assistant. You are able to understand the visual content that the user provides, and assist the user with a variety of tasks using natural language.", +) -> Dict: + # roles = {"human": "<|start_header_id|>user<|end_header_id|>", "gpt": "<|start_header_id|>assistant<|end_header_id|>"} + roles = {"human": "user", "gpt": "assistant"} + + # Add image tokens to tokenizer as a special tokens + # Use a deepcopy of tokenizer so that we don't modify on the tokenizer + tokenizer = copy.deepcopy(tokenizer) + # When there is actually an image, we add the image tokens as a special token + if has_image: + tokenizer.add_tokens([""], special_tokens=True) + image_token_index = tokenizer.convert_tokens_to_ids("") + bos_token_id = tokenizer.convert_tokens_to_ids("<|begin_of_text|>") + start_header_id = tokenizer.convert_tokens_to_ids("<|start_header_id|>") + end_header_id = tokenizer.convert_tokens_to_ids("<|end_header_id|>") + eot_id = tokenizer.convert_tokens_to_ids("<|eot_id|>") + + unmask_tokens = ["<|begin_of_text|>", "<|start_header_id|>", "<|end_header_id|>", "<|eot_id|>", "\n\n"] + unmask_tokens_idx = [tokenizer.convert_tokens_to_ids(tok) for tok in unmask_tokens] + + # After update, calling tokenizer of llama3 will + # auto add bos id for the tokens. ヽ(`⌒´)ノ + def safe_tokenizer_llama3(text): + input_ids = tokenizer(text).input_ids + if input_ids[0] == bos_token_id: + input_ids = input_ids[1:] + return input_ids + + nl_tokens = tokenizer.convert_tokens_to_ids("\n\n") + # Apply prompt templates + input_ids, targets = [], [] + for i, source in enumerate(sources): + if roles[source[0]["from"]] != roles["human"]: + source = source[1:] + + input_id, target = [], [] + + # New version, use apply chat template + # Build system message for each sentence + input_id += tokenizer.apply_chat_template([{"role" : "system", "content" : system_message}]) + target += [IGNORE_INDEX] * len(input_id) + + for conv in source: + # Make sure llava data can load + try: + role = conv["role"] + content = conv["content"] + except: + role = conv["from"] + content = conv["value"] + + role = roles.get(role, role) + + conv = [{"role" : role, "content" : content}] + # First is bos token we don't need here + encode_id = tokenizer.apply_chat_template(conv)[1:] + input_id += encode_id + if role in ["user", "system"]: + target += [IGNORE_INDEX] * len(encode_id) + else: + target += encode_id + + + + assert len(input_id) == len(target), f"{len(input_id)} != {len(target)}" + for idx, encode_id in enumerate(input_id): + if encode_id in unmask_tokens_idx: + target[idx] = encode_id + if encode_id == image_token_index: + input_id[idx] = IMAGE_TOKEN_INDEX + input_ids.append(input_id) + targets.append(target) + input_ids = torch.tensor(input_ids, dtype=torch.long) + targets = torch.tensor(targets, dtype=torch.long) + + return dict( + input_ids=input_ids, # tensor(bs x seq_len) + labels=targets, # tensor(bs x seq_len) + ) + + +def preprocess_v1(sources, tokenizer: transformers.PreTrainedTokenizer, has_image: bool = False) -> Dict: + conv = conversation_lib.default_conversation.copy() + roles = {"human": conv.roles[0], "gpt": conv.roles[1]} + + # Apply prompt templates + conversations = [] + for i, source in enumerate(sources): + if roles[source[0]["from"]] != conv.roles[0]: + # Skip the first one if it is not from human + source = source[1:] + + conv.messages = [] + for j, sentence in enumerate(source): + role = roles[sentence["from"]] + assert role == conv.roles[j % 2], f"{i}" + conv.append_message(role, sentence["value"]) + conversations.append(conv.get_prompt()) + + # Tokenize conversations + + if has_image: + input_ids = torch.stack([tokenizer_image_token(prompt, tokenizer, return_tensors="pt") for prompt in conversations], dim=0) + else: + input_ids = tokenizer( + conversations, + return_tensors="pt", + padding="longest", + max_length=tokenizer.model_max_length, + truncation=True, + ).input_ids + + targets = input_ids.clone() + + assert conv.sep_style == conversation_lib.SeparatorStyle.TWO + + # Mask targets + sep = conv.sep + conv.roles[1] + ": " + for conversation, target in zip(conversations, targets): + total_len = int(target.ne(tokenizer.pad_token_id).sum()) + + rounds = conversation.split(conv.sep2) + cur_len = 1 + target[:cur_len] = IGNORE_INDEX + for i, rou in enumerate(rounds): + if rou == "": + break + + parts = rou.split(sep) + if len(parts) != 2: + break + parts[0] += sep + + if has_image: + round_len = len(tokenizer_image_token(rou, tokenizer)) + instruction_len = len(tokenizer_image_token(parts[0], tokenizer)) - 2 + else: + round_len = len(tokenizer(rou).input_ids) + instruction_len = len(tokenizer(parts[0]).input_ids) - 2 + + if i != 0 and not tokenizer.legacy and IS_TOKENIZER_GREATER_THAN_0_14: + round_len -= 1 + instruction_len -= 1 + + target[cur_len : cur_len + instruction_len] = IGNORE_INDEX + + cur_len += round_len + target[cur_len:] = IGNORE_INDEX + + if cur_len < tokenizer.model_max_length: + if cur_len != total_len: + target[:] = IGNORE_INDEX + print(f"WARNING: tokenization mismatch: {cur_len} vs. {total_len}." f" (ignored)") + + return dict( + input_ids=input_ids, + labels=targets, + ) + + +def preprocess_mpt(sources, tokenizer: transformers.PreTrainedTokenizer, has_image: bool = False) -> Dict: + conv = conversation_lib.default_conversation.copy() + roles = {"human": conv.roles[0], "gpt": conv.roles[1]} + + # Apply prompt templates + conversations = [] + for i, source in enumerate(sources): + if roles[source[0]["from"]] != conv.roles[0]: + # Skip the first one if it is not from human + source = source[1:] + + conv.messages = [] + for j, sentence in enumerate(source): + role = roles[sentence["from"]] + assert role == conv.roles[j % 2], f"{i}" + conv.append_message(role, sentence["value"]) + conversations.append(conv.get_prompt()) + + # Tokenize conversations + + if has_image: + input_ids = torch.stack([tokenizer_image_token(prompt, tokenizer, return_tensors="pt") for prompt in conversations], dim=0) + else: + input_ids = tokenizer( + conversations, + return_tensors="pt", + padding="longest", + max_length=tokenizer.model_max_length, + truncation=True, + ).input_ids + + targets = input_ids.clone() + assert conv.sep_style == conversation_lib.SeparatorStyle.MPT + + # Mask targets + sep = conv.sep + conv.roles[1] + for conversation, target in zip(conversations, targets): + total_len = int(target.ne(tokenizer.pad_token_id).sum()) + + rounds = conversation.split(conv.sep) + re_rounds = [conv.sep.join(rounds[:3])] # system + user + gpt + for conv_idx in range(3, len(rounds), 2): + re_rounds.append(conv.sep.join(rounds[conv_idx : conv_idx + 2])) # user + gpt + cur_len = 1 + target[:cur_len] = IGNORE_INDEX + for i, rou in enumerate(re_rounds): + if rou == "": + break + + parts = rou.split(sep) + if len(parts) != 2: + break + parts[0] += sep + + if has_image: + round_len = len(tokenizer_image_token(rou, tokenizer)) + instruction_len = len(tokenizer_image_token(parts[0], tokenizer)) - 1 + else: + round_len = len(tokenizer(rou).input_ids) + instruction_len = len(tokenizer(parts[0]).input_ids) - 1 + + if i != 0 and getattr(tokenizer, "legacy", False) and IS_TOKENIZER_GREATER_THAN_0_14: + round_len += 1 + instruction_len += 1 + + target[cur_len : cur_len + instruction_len] = IGNORE_INDEX + + cur_len += round_len + target[cur_len:] = IGNORE_INDEX + + if cur_len < tokenizer.model_max_length: + if cur_len != total_len: + target[:] = IGNORE_INDEX + print(f"WARNING: tokenization mismatch: {cur_len} vs. {total_len}." f"(#turns={len(re_rounds)} ignored)") + + return dict( + input_ids=input_ids, + labels=targets, + ) + + +def preprocess_plain( + sources: Sequence[str], + tokenizer: transformers.PreTrainedTokenizer, +) -> Dict: + # add end signal and concatenate together + conversations = [] + for source in sources: + assert len(source) == 2 + assert DEFAULT_IMAGE_TOKEN in source[0]["value"] + source[0]["value"] = DEFAULT_IMAGE_TOKEN + conversation = source[0]["value"] + source[1]["value"] + conversation_lib.default_conversation.sep + conversations.append(conversation) + # tokenize conversations + input_ids = [tokenizer_image_token(prompt, tokenizer, return_tensors="pt") for prompt in conversations] + targets = copy.deepcopy(input_ids) + for target, source in zip(targets, sources): + tokenized_len = len(tokenizer_image_token(source[0]["value"], tokenizer)) + target[:tokenized_len] = IGNORE_INDEX + + return dict(input_ids=input_ids, labels=targets) + + +def preprocess(sources: Sequence[str], tokenizer: transformers.PreTrainedTokenizer, has_image: bool = False) -> Dict: + """ + Given a list of sources, each is a conversation list. This transform: + 1. Add signal '### ' at the beginning each sentence, with end signal '\n'; + 2. Concatenate conversations together; + 3. Tokenize the concatenated conversation; + 4. Make a deepcopy as the target. Mask human words with IGNORE_INDEX. + """ + if conversation_lib.default_conversation.version == "qwen_vla": + # print("Using qwen_vla preprocess") + return preprocess_qwen_vla(sources, tokenizer, has_image=has_image) + if conversation_lib.default_conversation.sep_style == conversation_lib.SeparatorStyle.PLAIN: + return preprocess_plain(sources, tokenizer) + if conversation_lib.default_conversation.sep_style == conversation_lib.SeparatorStyle.LLAMA_2: + return preprocess_llama_2(sources, tokenizer, has_image=has_image) + if conversation_lib.default_conversation.version.startswith("v1"): + return preprocess_v1(sources, tokenizer, has_image=has_image) + if conversation_lib.default_conversation.version == "mpt": + return preprocess_mpt(sources, tokenizer, has_image=has_image) + if conversation_lib.default_conversation.version == "qwen": + return preprocess_qwen(sources, tokenizer, has_image=has_image) + if conversation_lib.default_conversation.version == "gemma": + return preprocess_gemma(sources, tokenizer, has_image=has_image) + if conversation_lib.default_conversation.version == "llama_v3": + return preprocess_llama3(sources, tokenizer, has_image=has_image) + # add end signal and concatenate together + conversations = [] + for source in sources: + header = f"{conversation_lib.default_conversation.system}\n\n" + conversation = _add_speaker_and_signal(header, source) + conversations.append(conversation) + + # tokenize conversations + def get_tokenize_len(prompts): + return [len(tokenizer_image_token(prompt, tokenizer)) for prompt in prompts] + + if has_image: + input_ids = [tokenizer_image_token(prompt, tokenizer, return_tensors="pt") for prompt in conversations] + else: + conversations_tokenized = _tokenize_fn(conversations, tokenizer) + input_ids = conversations_tokenized["input_ids"] + + targets = copy.deepcopy(input_ids) + for target, source in zip(targets, sources): + if has_image: + tokenized_lens = get_tokenize_len([header] + [s["value"] for s in source]) + else: + tokenized_lens = _tokenize_fn([header] + [s["value"] for s in source], tokenizer)["input_ids_lens"] + speakers = [sentence["from"] for sentence in source] + _mask_targets(target, tokenized_lens, speakers) + + return dict(input_ids=input_ids, labels=targets) + + +class LazySupervisedDataset(Dataset): + def __init__(self, data_path: str, tokenizer: transformers.PreTrainedTokenizer, data_args: DataArguments): + super(LazySupervisedDataset, self).__init__() + self.tokenizer = tokenizer + self.list_data_dict = [] + + # Handle multiple JSON files specified in the data_path + if "{" in data_path and "}" in data_path: + base_path, file_pattern = re.match(r"^(.*)\{(.*)\}\.json$", data_path).groups() + file_names = file_pattern.split(",") + rank0_print(f"Loading {file_names} from {base_path}") + data_args.dataset_paths = [] + for file_name in file_names: + data_args.dataset_paths.append(f"{base_path}{file_name}.json") + full_path = f"{base_path}{file_name}.json" + rank0_print(f"Loading {full_path}") + with open(full_path, "r") as file: + cur_data_dict = json.load(file) + rank0_print(f"Loaded {len(cur_data_dict)} samples from {full_path}") + self.list_data_dict.extend(cur_data_dict) + elif data_path.endswith(".yaml"): + with open(data_path, "r") as file: + yaml_data = yaml.safe_load(file) + datasets = yaml_data.get("datasets") + # file should be in the format of: + # datasets: + # - json_path: xxxx1.json + # sampling_strategy: first:1000 + # - json_path: xxxx2.json + # sampling_strategy: end:3000 + # - json_path: xxxx3.json + # sampling_strategy: random:999 + data_args.dataset_paths = [dataset.get("json_path") for dataset in datasets] + for dataset in datasets: + json_path = dataset.get("json_path") + sampling_strategy = dataset.get("sampling_strategy", "all") + sampling_number = None + + rank0_print(f"Loading {json_path} with {sampling_strategy} sampling strategy") + + if json_path.endswith(".jsonl"): + cur_data_dict = [] + with open(json_path, "r") as json_file: + for line in json_file: + cur_data_dict.append(json.loads(line.strip())) + elif json_path.endswith(".json"): + with open(json_path, "r") as json_file: + cur_data_dict = json.load(json_file) + else: + raise ValueError(f"Unsupported file type: {json_path}") + + if ":" in sampling_strategy: + sampling_strategy, sampling_number = sampling_strategy.split(":") + if "%" in sampling_number: + sampling_number = math.ceil(int(sampling_number.split("%")[0]) * len(cur_data_dict) / 100) + else: + sampling_number = int(sampling_number) + + # Apply the sampling strategy + if sampling_strategy == "first" and sampling_number is not None: + cur_data_dict = cur_data_dict[:sampling_number] + elif sampling_strategy == "end" and sampling_number is not None: + cur_data_dict = cur_data_dict[-sampling_number:] + elif sampling_strategy == "random" and sampling_number is not None: + random.shuffle(cur_data_dict) + cur_data_dict = cur_data_dict[:sampling_number] + + rank0_print(f"Loaded {len(cur_data_dict)} samples from {json_path}") + self.list_data_dict.extend(cur_data_dict) + else: + data_args.dataset_paths = [data_path] + rank0_print(f"Loading {data_path}") + with open(data_path, "r") as file: + cur_data_dict = json.load(file) + rank0_print(f"Loaded {len(cur_data_dict)} samples from {data_path}") + self.list_data_dict.extend(cur_data_dict) + + rank0_print(f"Loaded {len(self.list_data_dict)} samples from {data_path}") + rank0_print("Formatting inputs...Skip in lazy mode") + self.tokenizer = tokenizer + self.data_args = data_args + + def __len__(self): + return len(self.list_data_dict) + + @property + def lengths(self): + length_list = [] + for sample in self.list_data_dict: + img_tokens = 128 if "image" in sample else 0 + length_list.append(sum(len(conv["value"].split()) for conv in sample["conversations"]) + img_tokens) + return length_list + + @property + def modality_lengths(self): + length_list = [] + for sample in self.list_data_dict: + + # cur_len = sum(len(conv["value"].split()) for conv in sample["conversations"]) + # 有可能是列表,也有可能是字符串,要改一下 + cur_len = 0 + for conv in sample["conversations"]: + if isinstance(conv['value'], list): + cur_len += len(conv['value']) + else: + cur_len += len(conv["value"].split()) + + + assert cur_len > 0, f"Conversation length is 0 for {sample}" + if "image" in sample or "video" in sample or self.data_args.early_mix_text: + length_list.append(cur_len) + else: + length_list.append(-cur_len) + return length_list + + def process_image(self, image_file, overwrite_image_aspect_ratio=None): + image_folder = self.data_args.image_folder + processor = self.data_args.image_processor + # print(f"\n\nInspecting the image path, folder = {image_folder}, image={image_file}\n\n") + try: + image = Image.open(os.path.join(image_folder, image_file)).convert("RGB") + except Exception as exn: + print(f"Failed to open image {image_file}. Exception:", exn) + raise exn + + image_size = image.size + image_aspect_ratio = self.data_args.image_aspect_ratio + if overwrite_image_aspect_ratio is not None: + image_aspect_ratio = overwrite_image_aspect_ratio + if image_aspect_ratio == "highres": + image = process_highres_image(image, self.data_args.image_processor, self.data_args.image_grid_pinpoints) + elif image_aspect_ratio == "anyres" or "anyres_max" in image_aspect_ratio: + image = process_anyres_image(image, self.data_args.image_processor, self.data_args.image_grid_pinpoints) + elif image_aspect_ratio == "crop_split": + image = process_highres_image_crop_split(image, self.data_args) + elif image_aspect_ratio == "pad": + def expand2square(pil_img, background_color): + width, height = pil_img.size + if width == height: + return pil_img + elif width > height: + result = Image.new(pil_img.mode, (width, width), background_color) + result.paste(pil_img, (0, (width - height) // 2)) + return result + else: + result = Image.new(pil_img.mode, (height, height), background_color) + result.paste(pil_img, ((height - width) // 2, 0)) + return result + + image = expand2square(image, tuple(int(x * 255) for x in processor.image_mean)) + image = processor.preprocess(image, return_tensors="pt")["pixel_values"][0] + else: + image = processor.preprocess(image, return_tensors="pt")["pixel_values"][0] + return image, image_size, "image" + + def __getitem__(self, i) -> Dict[str, torch.Tensor]: + # TODO: define number of retries somewhere else + num_base_retries = 3 + num_final_retries = 300 + + # try the current sample first + for attempt_idx in range(num_base_retries): + try: + sample = self._get_item(i) + return sample + except Exception as e: + # sleep 1s in case it is a cloud disk issue + print(f"[Try #{attempt_idx}] Failed to fetch sample {i}. Exception:", e) + time.sleep(1) + + # try other samples, in case it is file corruption issue + for attempt_idx in range(num_base_retries): + try: + next_index = min(i + 1, len(self.list_data_dict) - 1) + # sample_idx = random.choice(range(len(self))) + sample = self._get_item(next_index) + return sample + except Exception as e: + # no need to sleep + print(f"[Try other #{attempt_idx}] Failed to fetch sample {next_index}. Exception:", e) + pass + + try: + sample = self._get_item(i) + return sample + except Exception as e: + raise e + + def _get_item(self, i) -> Dict[str, torch.Tensor]: + sources = self.list_data_dict[i] + if isinstance(i, int): + sources = [sources] + assert len(sources) == 1, "Don't know why it is wrapped to a list" # FIXME + + if "image" in sources[0]: + image_file = self.list_data_dict[i]["image"] + if type(image_file) is list: + image = [self.process_image(f) for f in image_file] + # Handling multi images + # overwrite to process with simple pad + if len(image_file) > 1: + print("image_file > 1") + image = [self.process_image(f, "pad") for f in image_file] + image = [[im[0], im[1], "image"] for im in image] + else: + image = [self.process_image(image_file)] + sources = preprocess_multimodal(copy.deepcopy([e["conversations"] for e in sources]), self.data_args) + + # elif "video" in sources[0]: + # video_file = self.list_data_dict[i]["video"] + # video_folder = self.data_args.video_folder + # video_file = os.path.join(video_folder, video_file) + # suffix = video_file.split(".")[-1] + # if not os.path.exists(video_file): + # print("File {} not exist!".format(video_file)) + + # try: + # if "shareVideoGPTV" in video_file: + # frame_files = [os.path.join(video_file, f) for f in os.listdir(video_file) if os.path.isfile(os.path.join(video_file, f))] + # frame_files.sort() # Ensure the frames are sorted if they are named sequentially + + # # TODO: Hard CODE: Determine the indices for uniformly sampling 10 frames + # if self.data_args.force_sample: + # num_frames_to_sample = self.data_args.frames_upbound + # else: + # num_frames_to_sample = 10 + + # avg_fps = 2 + + # total_frames = len(frame_files) + # sampled_indices = np.linspace(0, total_frames - 1, num_frames_to_sample, dtype=int) + + + # frame_time = [i/2 for i in sampled_indices] + # frame_time = ",".join([f"{i:.2f}s" for i in frame_time]) + + # video_time = total_frames / avg_fps + + # # Read and store the sampled frames + # video = [] + # for idx in sampled_indices: + # frame_path = frame_files[idx] + # try: + # with Image.open(frame_path) as img: + # frame = img.convert("RGB") + # video.append(frame) + # except IOError: + # print(f"Failed to read frame at path: {frame_path}") + # else: + # video, video_time, frame_time, num_frames_to_sample = process_video_with_decord(video_file, self.data_args) + + # processor = self.data_args.image_processor + # image = processor.preprocess(video, return_tensors="pt")["pixel_values"] + # if self.data_args.add_time_instruction: + # time_instruciton = f"The video lasts for {video_time:.2f} seconds, and {num_frames_to_sample} frames are uniformly sampled from it. These frames are located at {frame_time}.Please answer the following questions related to this video." + # sources[0]["conversations"][0]["value"] = f'{DEFAULT_IMAGE_TOKEN}\n{time_instruciton}\n{sources[0]["conversations"][0]["value"].replace(DEFAULT_IMAGE_TOKEN, "")}' + # image = [(image, video[0].size, "video")] + # sources = preprocess_multimodal(copy.deepcopy([e["conversations"] for e in sources]), self.data_args) + # # print(sources) + # except Exception as e: + # print(f"Error: {e}") + # print(f"Failed to read video file: {video_file}") + # return self._get_item(i + 1) + else: + sources = copy.deepcopy([e["conversations"] for e in sources]) + + has_image = ("image" in self.list_data_dict[i]) or ("video" in self.list_data_dict[i]) + data_dict = preprocess(sources, self.tokenizer, has_image=has_image) + if "prompt" in data_dict: + prompt = data_dict["prompt"] + else: + prompt = None + + if isinstance(i, int): + data_dict = dict(input_ids=data_dict["input_ids"][0], labels=data_dict["labels"][0]) + + # image exist in the data + if "image" in self.list_data_dict[i]: + data_dict["image"] = image + elif "video" in self.list_data_dict[i]: + data_dict["image"] = image + elif self.data_args.is_multimodal: + # image does not exist in the data, but the model is multimodal + crop_size = self.data_args.image_processor.crop_size + data_dict["image"] = [ + (torch.zeros(1, 3, crop_size["height"], crop_size["width"]), (crop_size["width"], crop_size["height"]), "text"), + ] + # prompt exist in the data + if prompt is not None: + data_dict["prompt"] = prompt + + data_dict["id"] = self.list_data_dict[i].get("id", i) + + return data_dict + + +@dataclass +class DataCollatorForSupervisedDataset(object): + """Collate examples for supervised fine-tuning.""" + + tokenizer: transformers.PreTrainedTokenizer + + def pad_sequence(self, input_ids, batch_first, padding_value): + if self.tokenizer.padding_side == "left": + input_ids = [torch.flip(_input_ids, [0]) for _input_ids in input_ids] + input_ids = torch.nn.utils.rnn.pad_sequence(input_ids, batch_first=batch_first, padding_value=padding_value) + if self.tokenizer.padding_side == "left": + input_ids = torch.flip(input_ids, [1]) + return input_ids + + def __call__(self, instances: Sequence[Dict]) -> Dict[str, torch.Tensor]: + input_ids, labels = tuple([instance[key] for instance in instances] for key in ("input_ids", "labels")) + # input_ids, labels, ids = tuple([instance[key] for instance in instances] for key in ("input_ids", "labels", "id")) + input_ids = [_input_ids[: self.tokenizer.model_max_length] for _input_ids in input_ids] + labels = [_labels[: self.tokenizer.model_max_length] for _labels in labels] + if self.tokenizer.pad_token_id is None: + # self.tokenizer.pad_token_id = self.tokenizer.eos_token_id # FIXME: this could only be triggered for llama3 model. + self.tokenizer.pad_token_id = 0 # This gets the best result. Don't know why. + input_ids = self.pad_sequence(input_ids, batch_first=True, padding_value=self.tokenizer.pad_token_id) + labels = self.pad_sequence(labels, batch_first=True, padding_value=IGNORE_INDEX) + batch = dict(input_ids=input_ids, labels=labels.long() if labels.dtype == torch.int32 else labels, attention_mask=input_ids.ne(self.tokenizer.pad_token_id)) + # batch = dict(input_ids=input_ids, labels=labels, attention_mask=input_ids.ne(self.tokenizer.pad_token_id), ids=ids) + + if "image" in instances[0]: + images = [instance["image"] for instance in instances] + + batch["image_sizes"] = [im[1] for im_list in images for im in im_list] + batch["modalities"] = [im[2] for im_list in images for im in im_list] + images = [im[0] for im_list in images for im in im_list] + + # if all(x is not None and x.shape == images[0].shape for x in images): + # Image: (N, P, C, H, W) + # Video: (N, F, C, H, W) + # batch["images"] = torch.stack(images) + # else: + batch["images"] = images + + if "prompt" in instances[0]: + batch["prompts"] = [instance["prompt"] for instance in instances] + + return batch + + +def make_supervised_data_module(tokenizer: transformers.PreTrainedTokenizer, data_args) -> Dict: + """Make dataset and collator for supervised fine-tuning.""" + train_dataset = LazySupervisedDataset(tokenizer=tokenizer, data_path=data_args.data_path, data_args=data_args) + data_collator = DataCollatorForSupervisedDataset(tokenizer=tokenizer) + return dict(train_dataset=train_dataset, eval_dataset=None, data_collator=data_collator) + + +def get_model(model_args, training_args, bnb_model_from_pretrained_args): + assert training_args.attn_implementation + if training_args.attn_implementation == "sdpa" and torch.__version__ < "2.1.2": + raise ValueError("The 'sdpa' attention implementation requires torch version 2.1.2 or higher.") + + customized_kwargs = dict() + customized_kwargs.update(bnb_model_from_pretrained_args) + cfg_pretrained = None + + overwrite_config = {} + if any( + [ + model_args.rope_scaling_factor is not None, + model_args.rope_scaling_type is not None, + model_args.mm_spatial_pool_stride is not None, + model_args.mm_spatial_pool_out_channels is not None, + model_args.mm_spatial_pool_mode is not None, + model_args.mm_resampler_type is not None, + ] + ): + cfg_pretrained = AutoConfig.from_pretrained(model_args.model_name_or_path) + + if model_args.use_pos_skipping is not None and model_args.pos_skipping_range is not None: + overwrite_config["use_pos_skipping"] = model_args.use_pos_skipping + overwrite_config["pos_skipping_range"] = model_args.pos_skipping_range + + if model_args.rope_scaling_factor is not None and model_args.rope_scaling_type is not None: + overwrite_config["rope_scaling"] = { + "factor": model_args.rope_scaling_factor, + "type": model_args.rope_scaling_type, + } + if training_args.model_max_length is None: + training_args.model_max_length = cfg_pretrained.max_position_embeddings * model_args.rope_scaling_factor + overwrite_config["max_sequence_length"] = training_args.model_max_length + assert training_args.model_max_length == int(cfg_pretrained.max_position_embeddings * model_args.rope_scaling_factor), print( + f"model_max_length: {training_args.model_max_length}, max_position_embeddings: {cfg_pretrained.max_position_embeddings}, rope_scaling_factor: {model_args.rope_scaling_factor}" + ) + # overwrite_config["max_sequence_length"] = model_args.max_sequence_length + # overwrite_config["tokenizer_model_max_length"] = model_args.tokenizer_model_max_length + + if model_args.mm_spatial_pool_stride is not None and model_args.mm_spatial_pool_out_channels is not None and model_args.mm_spatial_pool_mode is not None and model_args.mm_resampler_type is not None: + overwrite_config["mm_resampler_type"] = model_args.mm_resampler_type + overwrite_config["mm_spatial_pool_stride"] = model_args.mm_spatial_pool_stride + overwrite_config["mm_spatial_pool_out_channels"] = model_args.mm_spatial_pool_out_channels + overwrite_config["mm_spatial_pool_mode"] = model_args.mm_spatial_pool_mode + + if model_args.mm_spatial_pool_mode is not None: + overwrite_config["mm_spatial_pool_mode"] = model_args.mm_spatial_pool_mode + + if overwrite_config: + assert cfg_pretrained is not None, "cfg_pretrained is None" + + rank0_print(f"Overwriting config with {overwrite_config}") + for k, v in overwrite_config.items(): + setattr(cfg_pretrained, k, v) + + customized_kwargs["config"] = cfg_pretrained + + if model_args.model_class_name is not None: + actual_model_class_name = f"{model_args.model_class_name}ForCausalLM" + model_class = getattr(transformers, actual_model_class_name) + rank0_print(f"Using model class {model_class} from {model_args.model_class_name}") + model = model_class.from_pretrained( + model_args.model_name_or_path, + cache_dir=training_args.cache_dir, + attn_implementation=training_args.attn_implementation, + torch_dtype=(torch.bfloat16 if training_args.bf16 else None), + low_cpu_mem_usage=False, + **customized_kwargs, + ) + elif model_args.vision_tower is not None: + if "mixtral" in model_args.model_name_or_path.lower(): + model = LlavaMixtralForCausalLM.from_pretrained( + model_args.model_name_or_path, + cache_dir=training_args.cache_dir, + attn_implementation=training_args.attn_implementation, + torch_dtype=(torch.bfloat16 if training_args.bf16 else None), + low_cpu_mem_usage=False, + **customized_kwargs, + ) + from transformers.models.mixtral.modeling_mixtral import MixtralSparseMoeBlock + + deepspeed.utils.set_z3_leaf_modules(model, [MixtralSparseMoeBlock]) + elif "mistral" in model_args.model_name_or_path.lower() or "zephyr" in model_args.model_name_or_path.lower(): + model = LlavaMistralForCausalLM.from_pretrained( + model_args.model_name_or_path, + cache_dir=training_args.cache_dir, + attn_implementation=training_args.attn_implementation, + torch_dtype=(torch.bfloat16 if training_args.bf16 else None), + low_cpu_mem_usage=False, + **customized_kwargs, + ) + elif ( + "wizardlm-2" in model_args.model_name_or_path.lower() + or "vicuna" in model_args.model_name_or_path.lower() + or "llama" in model_args.model_name_or_path.lower() + or "yi" in model_args.model_name_or_path.lower() + or "nous-hermes" in model_args.model_name_or_path.lower() + and "wizard-2" in model_args.model_name_or_path.lower() + ): + model = LlavaLlamaForCausalLM.from_pretrained( + model_args.model_name_or_path, + cache_dir=training_args.cache_dir, + attn_implementation=training_args.attn_implementation, + torch_dtype=(torch.bfloat16 if training_args.bf16 else None), + low_cpu_mem_usage=False, + **customized_kwargs, + ) + elif "qwen" in model_args.model_name_or_path.lower(): + if "moe" in model_args.model_name_or_path.lower() or "A14B" in model_args.model_name_or_path: + model = LlavaQwenMoeForCausalLM.from_pretrained( + model_args.model_name_or_path, + cache_dir=training_args.cache_dir, + attn_implementation=training_args.attn_implementation, + torch_dtype=(torch.bfloat16 if training_args.bf16 else None), + low_cpu_mem_usage=False, + **customized_kwargs, + ) + from transformers.models.qwen2_moe.modeling_qwen2_moe import Qwen2MoeSparseMoeBlock + + deepspeed.utils.set_z3_leaf_modules(model, [Qwen2MoeSparseMoeBlock]) + else: + model = LlavaQwenForCausalLM.from_pretrained( + model_args.model_name_or_path, + cache_dir=training_args.cache_dir, + attn_implementation=training_args.attn_implementation, + torch_dtype=(torch.bfloat16 if training_args.bf16 else None), + low_cpu_mem_usage=False, + **customized_kwargs, + ) + elif "gemma" in model_args.model_name_or_path.lower(): + model = LlavaGemmaForCausalLM.from_pretrained( + model_args.model_name_or_path, + cache_dir=training_args.cache_dir, + attn_implementation=training_args.attn_implementation, + torch_dtype=(torch.bfloat16 if training_args.bf16 else None), + low_cpu_mem_usage=False, + **customized_kwargs, + ) + else: + raise ValueError(f"Unknown model class {model_args}") + else: + model = transformers.LlamaForCausalLM.from_pretrained( + model_args.model_name_or_path, + cache_dir=training_args.cache_dir, + attn_implementation=training_args.attn_implementation, + torch_dtype=(torch.bfloat16 if training_args.bf16 else None), + low_cpu_mem_usage=False, + **customized_kwargs, + ) + return model + + +def train(attn_implementation=None): + global local_rank + + parser = transformers.HfArgumentParser((ModelArguments, DataArguments, TrainingArguments)) + model_args, data_args, training_args = parser.parse_args_into_dataclasses() + + if training_args.verbose_logging: + rank0_print(f"Inspecting experiment hyperparameters:\n") + rank0_print(f"model_args = {vars(model_args)}\n\n") + rank0_print(f"data_args = {vars(data_args)}\n\n") + rank0_print(f"training_args = {vars(training_args)}\n\n") + # rank0_print(f"evaluation_args = {vars(evaluation_args)}\n\n") + + local_rank = training_args.local_rank + compute_dtype = torch.float16 if training_args.fp16 else (torch.bfloat16 if training_args.bf16 else torch.float32) + + bnb_model_from_pretrained_args = {} + if training_args.bits in [4, 8]: + from transformers import BitsAndBytesConfig + + bnb_model_from_pretrained_args.update( + dict( + device_map={"": training_args.device}, + load_in_4bit=training_args.bits == 4, + load_in_8bit=training_args.bits == 8, + quantization_config=BitsAndBytesConfig( + load_in_4bit=training_args.bits == 4, + load_in_8bit=training_args.bits == 8, + llm_int8_threshold=6.0, + llm_int8_has_fp16_weight=False, + bnb_4bit_compute_dtype=compute_dtype, + bnb_4bit_use_double_quant=training_args.double_quant, + bnb_4bit_quant_type=training_args.quant_type, # {'fp4', 'nf4'} + ), + ) + ) + + model = get_model(model_args, training_args, bnb_model_from_pretrained_args) + model.config.use_cache = False + if model_args.rope_scaling_factor is not None and model_args.rope_scaling_type is not None: + model.config.rope_scaling = { + "factor": model_args.rope_scaling_factor, + "type": model_args.rope_scaling_type, + } + + if model_args.freeze_backbone: + model.model.requires_grad_(False) + + if training_args.bits in [4, 8]: + from peft import prepare_model_for_kbit_training + + model.config.torch_dtype = torch.float32 if training_args.fp16 else (torch.bfloat16 if training_args.bf16 else torch.float32) + model = prepare_model_for_kbit_training(model, use_gradient_checkpointing=training_args.gradient_checkpointing) + + if training_args.gradient_checkpointing: + if hasattr(model, "enable_input_require_grads"): + model.enable_input_require_grads() + else: + + def make_inputs_require_grad(module, input, output): + output.requires_grad_(True) + + model.get_input_embeddings().register_forward_hook(make_inputs_require_grad) + + if training_args.lora_enable: + from peft import LoraConfig, get_peft_model + + lora_config = LoraConfig( + r=training_args.lora_r, + lora_alpha=training_args.lora_alpha, + target_modules=find_all_linear_names(model), + lora_dropout=training_args.lora_dropout, + bias=training_args.lora_bias, + task_type="CAUSAL_LM", + ) + if training_args.bits == 16: + if training_args.bf16: + model.to(torch.bfloat16) + if training_args.fp16: + model.to(torch.float16) + rank0_print("Adding LoRA adapters...") + model = get_peft_model(model, lora_config) + + if "mistral" in model_args.model_name_or_path.lower() or "mixtral" in model_args.model_name_or_path.lower() or "zephyr" in model_args.model_name_or_path.lower(): + tokenizer = transformers.AutoTokenizer.from_pretrained(model_args.model_name_or_path, cache_dir=training_args.cache_dir, model_max_length=training_args.model_max_length, padding_side="left") + elif "qwen" in model_args.model_name_or_path.lower(): + tokenizer = transformers.AutoTokenizer.from_pretrained(model_args.model_name_or_path, cache_dir=training_args.cache_dir, model_max_length=training_args.model_max_length, padding_side="right") + elif ( + "wizardlm-2" in model_args.model_name_or_path.lower() + or "vicuna" in model_args.model_name_or_path.lower() + or "llama" in model_args.model_name_or_path.lower() + or "yi" in model_args.model_name_or_path.lower() + or "nous-hermes" in model_args.model_name_or_path.lower() + and "wizard-2" in model_args.model_name_or_path.lower() + ): + tokenizer = transformers.AutoTokenizer.from_pretrained( + model_args.model_name_or_path, + cache_dir=training_args.cache_dir, + model_max_length=training_args.model_max_length, + padding_side="right", + use_fast=False, + ) + + rank0_print(f"Prompt version: {model_args.version}") + if model_args.version == "v0": + if tokenizer.pad_token is None: + smart_tokenizer_and_embedding_resize( + special_tokens_dict=dict(pad_token="[PAD]"), + tokenizer=tokenizer, + model=model, + ) + elif model_args.version == "v0.5": + tokenizer.pad_token = tokenizer.unk_token + else: + if tokenizer.unk_token is not None: + tokenizer.pad_token = tokenizer.unk_token + if model_args.version in conversation_lib.conv_templates: + conversation_lib.default_conversation = conversation_lib.conv_templates[model_args.version] + else: + conversation_lib.default_conversation = conversation_lib.conv_templates["vicuna_v1"] + + if model_args.vision_tower is not None: + model.get_model().initialize_vision_modules(model_args=model_args, fsdp=training_args.fsdp) + + vision_tower = model.get_vision_tower() + vision_tower.to(dtype=torch.bfloat16 if training_args.bf16 else torch.float16, device=training_args.device) + + data_args.image_processor = vision_tower.image_processor + data_args.is_multimodal = True + + model.config.image_aspect_ratio = data_args.image_aspect_ratio + if data_args.image_grid_pinpoints is not None: + if isinstance(data_args.image_grid_pinpoints, str) and "x" in data_args.image_grid_pinpoints: + try: + patch_size = data_args.image_processor.size[0] + except Exception as e: + patch_size = data_args.image_processor.size["shortest_edge"] + + assert patch_size in [224, 336, 384, 448, 512], "patch_size should be in [224, 336, 384, 448, 512]" + # Use regex to extract the range from the input string + matches = re.findall(r"\((\d+)x(\d+)\)", data_args.image_grid_pinpoints) + range_start = tuple(map(int, matches[0])) + range_end = tuple(map(int, matches[-1])) + # Generate a matrix of tuples from (range_start[0], range_start[1]) to (range_end[0], range_end[1]) + grid_pinpoints = [(i, j) for i in range(range_start[0], range_end[0] + 1) for j in range(range_start[1], range_end[1] + 1)] + # Multiply all elements by patch_size + data_args.image_grid_pinpoints = [[dim * patch_size for dim in pair] for pair in grid_pinpoints] + elif isinstance(data_args.image_grid_pinpoints, str): + data_args.image_grid_pinpoints = ast.literal_eval(data_args.image_grid_pinpoints) + + model.config.image_grid_pinpoints = data_args.image_grid_pinpoints + model.config.image_crop_resolution = data_args.image_crop_resolution + model.config.image_split_resolution = data_args.image_split_resolution + model.config.tokenizer_padding_side = tokenizer.padding_side + model.config.tokenizer_model_max_length = tokenizer.model_max_length + model.config.mm_newline_position = model_args.mm_newline_position + model.config.add_faster_video = model_args.add_faster_video + model.config.faster_token_stride = model_args.faster_token_stride + model.config.add_time_instruction = data_args.add_time_instruction + model.config.force_sample = data_args.force_sample + model.config.mm_spatial_pool_stride = model_args.mm_spatial_pool_stride + + ### Deciding train which part of the model + if model_args.mm_tunable_parts is None: # traditional way of deciding which part to train + model.config.tune_mm_mlp_adapter = training_args.tune_mm_mlp_adapter = model_args.tune_mm_mlp_adapter + model.config.tune_mm_vision_resampler = training_args.tune_mm_vision_resampler = model_args.tune_mm_vision_resampler + if model_args.tune_mm_mlp_adapter or model_args.tune_mm_vision_resampler: + model.requires_grad_(False) + if model_args.tune_mm_mlp_adapter: + for p in model.get_model().mm_projector.parameters(): + p.requires_grad = True + if model_args.tune_mm_vision_resampler: + for p in model.get_model().vision_resampler.parameters(): + p.requires_grad = True + + model.config.freeze_mm_mlp_adapter = training_args.freeze_mm_mlp_adapter + if training_args.freeze_mm_mlp_adapter: + for p in model.get_model().mm_projector.parameters(): + p.requires_grad = False + + model.config.freeze_mm_vision_resampler = training_args.freeze_mm_vision_resampler + if training_args.freeze_mm_vision_resampler: + for p in model.get_model().vision_resampler.parameters(): + p.requires_grad = False + + model.config.unfreeze_mm_vision_tower = model_args.unfreeze_mm_vision_tower + if model_args.unfreeze_mm_vision_tower: + vision_tower.requires_grad_(True) + else: + vision_tower.requires_grad_(False) + + else: + rank0_print(f"Using mm_tunable_parts: {model_args.mm_tunable_parts}") + model.config.mm_tunable_parts = training_args.mm_tunable_parts = model_args.mm_tunable_parts + # Set the entire model to not require gradients by default + model.requires_grad_(False) + vision_tower.requires_grad_(False) + model.get_model().mm_projector.requires_grad_(False) + model.get_model().vision_resampler.requires_grad_(False) + # Parse the mm_tunable_parts to decide which parts to unfreeze + tunable_parts = model_args.mm_tunable_parts.split(",") + if "mm_mlp_adapter" in tunable_parts: + for p in model.get_model().mm_projector.parameters(): + p.requires_grad = True + if "mm_vision_resampler" in tunable_parts: + for p in model.get_model().vision_resampler.parameters(): + p.requires_grad = True + if "mm_vision_tower" in tunable_parts: + for name, param in model.named_parameters(): + if "vision_tower" in name: + param.requires_grad_(True) + if "mm_language_model" in tunable_parts: + for name, param in model.named_parameters(): + if "vision_tower" not in name and "mm_projector" not in name and "vision_resampler" not in name: + param.requires_grad_(True) + + total_params = sum(p.ds_numel if hasattr(p, "ds_numel") else p.numel() for p in model.parameters()) + trainable_params = sum(p.ds_numel if hasattr(p, "ds_numel") else p.numel() for p in model.parameters() if p.requires_grad) + rank0_print(f"Total parameters: ~{total_params/1e6:.2f} MB)") + rank0_print(f"Trainable parameters: ~{trainable_params/1e6:.2f} MB)") + if training_args.bits in [4, 8]: + model.get_model().mm_projector.to(dtype=compute_dtype, device=training_args.device) + + model.config.mm_use_im_start_end = data_args.mm_use_im_start_end = model_args.mm_use_im_start_end + model.config.mm_projector_lr = training_args.mm_projector_lr + model.config.mm_vision_tower_lr = training_args.mm_vision_tower_lr + training_args.use_im_start_end = model_args.mm_use_im_start_end + model.config.mm_use_im_patch_token = model_args.mm_use_im_patch_token + # model.initialize_vision_tokenizer(model_args, tokenizer=tokenizer) + # model.initialize_action_tokenizer(model_args, actiontokenizer=ActionTokenizer(tokenizer)) + + if training_args.bits in [4, 8]: + from peft.tuners.lora import LoraLayer + + for name, module in model.named_modules(): + if isinstance(module, LoraLayer): + if training_args.bf16: + module = module.to(torch.bfloat16) + if "norm" in name: + module = module.to(torch.float32) + if "lm_head" in name or "embed_tokens" in name: + if hasattr(module, "weight"): + if training_args.bf16 and module.weight.dtype == torch.float32: + module = module.to(torch.bfloat16) + + data_module = make_supervised_data_module(tokenizer=tokenizer, data_args=data_args) + + # 探针,看看这个时候embedding还是不是0 + with open("embedding.txt", "w") as f: + f.write(str(model.get_input_embeddings().weight.size())) + f.write(str(model.get_output_embeddings().weight.size())) + f.write(str(model)) + + + trainer = LLaVATrainer(model=model, tokenizer=tokenizer, args=training_args, **data_module) + + if list(pathlib.Path(training_args.output_dir).glob("checkpoint-*")): + trainer.train(resume_from_checkpoint=True) + else: + trainer.train() + trainer.save_state() + + model.config.use_cache = True + + if training_args.lora_enable: + state_dict = get_peft_state_maybe_zero_3(model.named_parameters(), training_args.lora_bias) + non_lora_state_dict = get_peft_state_non_lora_maybe_zero_3(model.named_parameters()) + if training_args.local_rank == 0 or training_args.local_rank == -1: + if hasattr(model, "config"): + model.config.save_pretrained(training_args.output_dir) + if hasattr(model, "generation_config"): + model.generation_config.save_pretrained(training_args.output_dir) + model.save_pretrained(training_args.output_dir, state_dict=state_dict) + torch.save(non_lora_state_dict, os.path.join(training_args.output_dir, "non_lora_trainables.bin")) + else: + safe_save_model_for_hf_trainer(trainer=trainer, output_dir=training_args.output_dir) + + rank0_print(f"Model saved to {training_args.output_dir}") + + +if __name__ == "__main__": + train() diff --git a/llava/train/train_dpo.py b/llava/train/train_dpo.py new file mode 100644 index 0000000..037eec4 --- /dev/null +++ b/llava/train/train_dpo.py @@ -0,0 +1,1782 @@ +# Adopted from https://github.com/lm-sys/FastChat. Below is the original copyright: +# Adopted from tatsu-lab@stanford_alpaca. Below is the original copyright: +# Copyright 2023 Rohan Taori, Ishaan Gulrajani, Tianyi Zhang, Yann Dubois, Xuechen Li +# +# 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. + +import os +import copy +import deepspeed +from dataclasses import dataclass, field +import json +import logging +import pathlib +from typing import Dict, Optional, Sequence, List +import ast + +import yaml +import time +import random +import yaml +import math +import re +import torch + +import transformers +import tokenizers + +from llava.constants import IGNORE_INDEX, DEFAULT_IMAGE_TOKEN, DEFAULT_IM_START_TOKEN, DEFAULT_IM_END_TOKEN, IMAGE_TOKEN_INDEX +from torch.utils.data import Dataset +from llava.train.llava_trainer import LLaVADPOTrainer +from data_processing.utils import load_jsonl, load_json +from llava import conversation as conversation_lib +from llava.model import * +from llava.model.language_model.llava_qwen import LlavaQwenConfig +from llava.model.language_model.llava_llama import LlavaConfig +from llava.model.language_model.llava_mistral import LlavaMistralConfig +from llava.mm_utils import process_highres_image, process_anyres_image, process_highres_image_crop_split, tokenizer_image_token +from llava.utils import rank0_print +from transformers import AutoConfig +import pickle + +from trl.trainer.utils import DPODataCollatorWithPadding +from PIL import Image, ImageFile +from decord import VideoReader, cpu + +ImageFile.LOAD_TRUNCATED_IMAGES = True +from packaging import version +from typing import Any + +local_rank = None +import numpy as np + +IS_TOKENIZER_GREATER_THAN_0_14 = version.parse(tokenizers.__version__) >= version.parse("0.14") + + +@dataclass +class ModelArguments: + model_name_or_path: Optional[str] = field(default="facebook/opt-125m") + model_class_name: Optional[str] = field(default=None, metadata={"help": "Used to init model class, format is XXXXForCausalLM. e.g. currently XXXX is chosen from LlavaLlama, LlavaMixtral, LlavaMistral, Llama"}) + + mm_tunable_parts: Optional[str] = field( + default=None, metadata={"help": 'Could be "mm_mlp_adapter", "mm_vision_resampler", "mm_vision_tower,mm_mlp_adapter,mm_language_model", "mm_vision_tower,mm_mlp_adapter,mm_language_model", "mm_mlp_adapter,mm_language_model"'} + ) + # deciding which part of the multimodal model to tune, will overwrite other previous settings + + version: Optional[str] = field(default="v0") + freeze_backbone: bool = field(default=False) + tune_mm_mlp_adapter: bool = field(default=False) + tune_mm_vision_resampler: bool = field(default=False) + vision_tower: Optional[str] = field(default=None) + vision_tower_pretrained: Optional[str] = field(default=None) # default to the last layer + + unfreeze_mm_vision_tower: bool = field(default=False) + unfreeze_language_model: bool = field(default=False) + mm_vision_select_layer: Optional[int] = field(default=-1) # default to the last layer + pretrain_mm_mlp_adapter: Optional[str] = field(default=None) + mm_projector_type: Optional[str] = field(default="linear") + mm_use_im_start_end: bool = field(default=False) + mm_use_im_patch_token: bool = field(default=True) + mm_patch_merge_type: Optional[str] = field(default="flat") + mm_vision_select_feature: Optional[str] = field(default="patch") + mm_resampler_type: Optional[str] = field(default=None) + mm_mask_drop_mode: str = field(default="fixed") + mm_mask_drop_skip_percentage: float = field(default=0.0) + mm_mask_drop_ratio: float = field(default=0.25) + mm_mask_drop_ratio_upper: Optional[float] = field(default=None) + mm_mask_drop_ratio_lower: Optional[float] = field(default=None) + mm_spatial_pool_stride: Optional[int] = field(default=None) + mm_spatial_pool_mode: str = field(default="average") + mm_spatial_pool_out_channels: Optional[int] = field(default=None) + mm_perceiver_depth: Optional[int] = field(default=3) + mm_perceiver_latents: Optional[int] = field(default=32) + mm_perceiver_ff_mult: Optional[float] = field(default=4) + mm_perceiver_pretrained: Optional[str] = field(default=None) + mm_qformer_depth: Optional[int] = field(default=3) + mm_qformer_latents: Optional[int] = field(default=32) + mm_qformer_pretrained: Optional[str] = field(default=None) + + rope_scaling_factor: Optional[float] = field(default=None) + rope_scaling_type: Optional[str] = field(default=None) + + s2: Optional[bool] = field(default=False) + s2_scales: Optional[str] = field(default="336,672,1008") + + +@dataclass +class DataArguments: + data_path: str = field(default=None, metadata={"help": "Path to the training data, in llava's instruction.json format. Supporting multiple json files via /path/to/{a,b,c}.json"}) + lazy_preprocess: bool = False + is_multimodal: bool = False + image_folder: Optional[str] = field(default=None) + video_folder: Optional[str] = field(default=None) + video_fps: Optional[int] = field(default=1) + image_aspect_ratio: str = "square" + image_grid_pinpoints: Optional[str] = field(default=None) + image_crop_resolution: int = 384 + image_split_resolution: int = 384 + input_prompt: Optional[str] = field(default=None) + refine_prompt: Optional[bool] = field(default=False) + frames_upbound: Optional[int] = field(default=0) + num_sample: Optional[int] = field(default=None) + + +@dataclass +class TrainingArguments(transformers.TrainingArguments): + cache_dir: Optional[str] = field(default=None) + optim: str = field(default="adamw_torch") + remove_unused_columns: bool = field(default=False) + freeze_mm_mlp_adapter: bool = field(default=False) + freeze_mm_vision_resampler: bool = field(default=False) + mpt_attn_impl: Optional[str] = field(default="triton") + model_max_length: int = field( + default=4096, + metadata={"help": "Maximum sequence length. Sequences will be right padded (and possibly truncated)."}, + ) + double_quant: bool = field(default=True, metadata={"help": "Compress the quantization statistics through double quantization."}) + quant_type: str = field(default="nf4", metadata={"help": "Quantization data type to use. Should be one of `fp4` or `nf4`."}) + bits: int = field(default=16, metadata={"help": "How many bits to use."}) + lora_enable: bool = False + lora_r: int = 64 + lora_alpha: int = 16 + lora_dropout: float = 0.05 + lora_weight_path: str = "" + lora_bias: str = "none" + mm_projector_lr: Optional[float] = None + mm_vision_tower_lr: Optional[float] = None + group_by_varlen: bool = field(default=False) + group_by_modality_length: bool = field(default=False) + group_by_modality_length_auto: bool = field(default=False) + auto_find_batch_size: bool = field(default=False) + gradient_checkpointing: bool = field(default=True) + verbose_logging: bool = field(default=False) + attn_implementation: str = field(default="flash_attention_2", metadata={"help": "Use transformers attention implementation."}) + dpo_alpha: float = field(default=1.0) + beta: float = field(default=0.1) + gamma: float = field(default=1.0) + generate_during_eval: bool = field(default=False) + precompute_ref_log_probs: bool = field(default=False) + + +def maybe_zero_3(param, ignore_status=False, name=None): + from deepspeed import zero + from deepspeed.runtime.zero.partition_parameters import ZeroParamStatus + + if hasattr(param, "ds_id"): + if param.ds_status == ZeroParamStatus.NOT_AVAILABLE: + if not ignore_status: + logging.warning(f"{name}: param.ds_status != ZeroParamStatus.NOT_AVAILABLE: {param.ds_status}") + with zero.GatheredParameters([param]): + param = param.data.detach().cpu().clone() + else: + param = param.detach().cpu().clone() + return param + + +# Borrowed from peft.utils.get_peft_model_state_dict +def get_peft_state_maybe_zero_3(named_params, bias): + if bias == "none": + to_return = {k: t for k, t in named_params if "lora_" in k} + elif bias == "all": + to_return = {k: t for k, t in named_params if "lora_" in k or "bias" in k} + elif bias == "lora_only": + to_return = {} + maybe_lora_bias = {} + lora_bias_names = set() + for k, t in named_params: + if "lora_" in k: + to_return[k] = t + bias_name = k.split("lora_")[0] + "bias" + lora_bias_names.add(bias_name) + elif "bias" in k: + maybe_lora_bias[k] = t + for k, t in maybe_lora_bias: + if bias_name in lora_bias_names: + to_return[bias_name] = t + else: + raise NotImplementedError + to_return = {k: maybe_zero_3(v, ignore_status=True) for k, v in to_return.items()} + return to_return + + +def get_peft_state_non_lora_maybe_zero_3(named_params, require_grad_only=True): + to_return = {k: t for k, t in named_params if "lora_" not in k} + if require_grad_only: + to_return = {k: t for k, t in to_return.items() if t.requires_grad} + to_return = {k: maybe_zero_3(v, ignore_status=True).cpu() for k, v in to_return.items()} + return to_return + + +def get_mm_adapter_state_maybe_zero_3(named_params, keys_to_match): + to_return = {k: t for k, t in named_params if any(key_match in k for key_match in keys_to_match)} + to_return = {k: maybe_zero_3(v, ignore_status=True).cpu() for k, v in to_return.items()} + return to_return + + +def find_all_linear_names(model): + cls = torch.nn.Linear + lora_module_names = set() + multimodal_keywords = ["mm_projector", "vision_tower", "vision_resampler"] + for name, module in model.named_modules(): + if any(mm_keyword in name for mm_keyword in multimodal_keywords): + continue + if isinstance(module, cls): + names = name.split(".") + lora_module_names.add(names[0] if len(names) == 1 else names[-1]) + + if "lm_head" in lora_module_names: # needed for 16-bit + lora_module_names.remove("lm_head") + return list(lora_module_names) + + +def safe_save_model_for_hf_trainer(trainer: transformers.Trainer, output_dir: str): + """Collects the state dict and dump to disk.""" + if hasattr(trainer.args, "tune_mm_mlp_adapter") and trainer.args.tune_mm_mlp_adapter: + check_only_save_mm_adapter_tunnable = True + # only has mm_mlp_adapter and mm_vision_resampler in the tuneable parts + elif hasattr(trainer.args, "mm_tunable_parts") and (len(trainer.args.mm_tunable_parts.split(",")) == 1 and ("mm_mlp_adapter" in trainer.args.mm_tunable_parts or "mm_vision_resampler" in trainer.args.mm_tunable_parts)): + check_only_save_mm_adapter_tunnable = True + else: + check_only_save_mm_adapter_tunnable = False + + trainer.accelerator.wait_for_everyone() + torch.cuda.synchronize() + rank0_print(f"Only save projectors: {check_only_save_mm_adapter_tunnable}") + if check_only_save_mm_adapter_tunnable: + # Only save Adapter + keys_to_match = ["mm_projector", "vision_resampler"] + if getattr(trainer.args, "use_im_start_end", False): + keys_to_match.extend(["embed_tokens", "embed_in"]) + + weight_to_save = get_mm_adapter_state_maybe_zero_3(trainer.model.named_parameters(), keys_to_match) + trainer.model.config.save_pretrained(output_dir) + + current_folder = output_dir.split("/")[-1] + parent_folder = os.path.dirname(output_dir) + if trainer.args.local_rank == 0 or trainer.args.local_rank == -1: + if current_folder.startswith("checkpoint-"): + mm_projector_folder = os.path.join(parent_folder, "mm_projector") + os.makedirs(mm_projector_folder, exist_ok=True) + torch.save(weight_to_save, os.path.join(mm_projector_folder, f"{current_folder}.bin")) + else: + torch.save(weight_to_save, os.path.join(output_dir, f"mm_projector.bin")) + return + + if trainer.deepspeed: + trainer.save_model(output_dir) + return + + state_dict = trainer.model.state_dict() + if trainer.args.should_save: + cpu_state_dict = {key: value.cpu() for key, value in state_dict.items()} + del state_dict + trainer._save(output_dir, state_dict=cpu_state_dict) # noqa + + +def smart_tokenizer_and_embedding_resize( + special_tokens_dict: Dict, + tokenizer: transformers.PreTrainedTokenizer, + model: transformers.PreTrainedModel, +): + """Resize tokenizer and embedding. + + Note: This is the unoptimized version that may make your embedding size not be divisible by 64. + """ + num_new_tokens = tokenizer.add_special_tokens(special_tokens_dict) + model.resize_token_embeddings(len(tokenizer)) + + if num_new_tokens > 0: + input_embeddings = model.get_input_embeddings().weight.data + output_embeddings = model.get_output_embeddings().weight.data + + input_embeddings_avg = input_embeddings[:-num_new_tokens].mean(dim=0, keepdim=True) + output_embeddings_avg = output_embeddings[:-num_new_tokens].mean(dim=0, keepdim=True) + + input_embeddings[-num_new_tokens:] = input_embeddings_avg + output_embeddings[-num_new_tokens:] = output_embeddings_avg + + +def _tokenize_fn(strings: Sequence[str], tokenizer: transformers.PreTrainedTokenizer) -> Dict: + """Tokenize a list of strings.""" + tokenized_list = [ + tokenizer( + text, + return_tensors="pt", + padding="longest", + max_length=tokenizer.model_max_length, + truncation=True, + ) + for text in strings + ] + input_ids = labels = [tokenized.input_ids[0] for tokenized in tokenized_list] + input_ids_lens = labels_lens = [tokenized.input_ids.ne(tokenizer.pad_token_id).sum().item() for tokenized in tokenized_list] + return dict( + input_ids=input_ids, + labels=labels, + input_ids_lens=input_ids_lens, + labels_lens=labels_lens, + ) + + +def _mask_targets(target, tokenized_lens, speakers): + # cur_idx = 0 + cur_idx = tokenized_lens[0] + tokenized_lens = tokenized_lens[1:] + target[:cur_idx] = IGNORE_INDEX + for tokenized_len, speaker in zip(tokenized_lens, speakers): + if speaker == "human": + target[cur_idx + 2 : cur_idx + tokenized_len] = IGNORE_INDEX + cur_idx += tokenized_len + + +def _add_speaker_and_signal(header, source, get_conversation=True): + """Add speaker and start/end signal on each round.""" + BEGIN_SIGNAL = "### " + END_SIGNAL = "\n" + conversation = header + for sentence in source: + from_str = sentence["from"] + if from_str.lower() == "human": + from_str = conversation_lib.default_conversation.roles[0] + elif from_str.lower() == "gpt": + from_str = conversation_lib.default_conversation.roles[1] + else: + from_str = "unknown" + sentence["value"] = BEGIN_SIGNAL + from_str + ": " + sentence["value"] + END_SIGNAL + if get_conversation: + conversation += sentence["value"] + conversation += BEGIN_SIGNAL + return conversation + + +def preprocess_multimodal(sources: Sequence[str], data_args: DataArguments) -> Dict: + is_multimodal = data_args.is_multimodal + if not is_multimodal: + return sources + + for source in sources: + for sentence in source: + if DEFAULT_IMAGE_TOKEN in sentence["value"] and not sentence["value"].startswith(DEFAULT_IMAGE_TOKEN): + sentence["value"] = sentence["value"].replace(DEFAULT_IMAGE_TOKEN, "").strip() + sentence["value"] = DEFAULT_IMAGE_TOKEN + "\n" + sentence["value"] + sentence["value"] = sentence["value"].strip() + if "mmtag" in conversation_lib.default_conversation.version: + sentence["value"] = sentence["value"].replace(DEFAULT_IMAGE_TOKEN, "" + DEFAULT_IMAGE_TOKEN + "") + replace_token = DEFAULT_IMAGE_TOKEN + if data_args.mm_use_im_start_end: + replace_token = DEFAULT_IM_START_TOKEN + replace_token + DEFAULT_IM_END_TOKEN + sentence["value"] = sentence["value"].replace(DEFAULT_IMAGE_TOKEN, replace_token) + + return sources + + +def preprocess_multimodal_movie(sources: Sequence[str], data_args: DataArguments, video_inputs: str) -> Dict: + is_multimodal = data_args.is_multimodal + if not is_multimodal: + return sources + + for source in sources: + for sentence in source: + if DEFAULT_IMAGE_TOKEN in sentence["value"]: + prompt = sentence["value"].replace(DEFAULT_IMAGE_TOKEN, "").strip() + replace_token = video_inputs + if data_args.mm_use_im_start_end: + replace_token = DEFAULT_IM_START_TOKEN + replace_token + DEFAULT_IM_END_TOKEN + sentence["value"] = sentence["value"].replace(DEFAULT_IMAGE_TOKEN, replace_token) + + return sources, prompt + + +def preprocess_llama_2(sources, tokenizer: transformers.PreTrainedTokenizer, has_image: bool = False) -> Dict: + conv = conversation_lib.default_conversation.copy() + roles = {"human": conv.roles[0], "gpt": conv.roles[1]} + + # Apply prompt templates + conversations = [] + for i, source in enumerate(sources): + if roles[source[0]["from"]] != conv.roles[0]: + # Skip the first one if it is not from human + source = source[1:] + + conv.messages = [] + for j, sentence in enumerate(source): + role = roles[sentence["from"]] + assert role == conv.roles[j % 2], f"{i}" + conv.append_message(role, sentence["value"]) + conversations.append(conv.get_prompt()) + + # Tokenize conversations + + if has_image: + input_ids = torch.stack([tokenizer_image_token(prompt, tokenizer, return_tensors="pt") for prompt in conversations], dim=0) + else: + input_ids = tokenizer( + conversations, + return_tensors="pt", + padding="longest", + max_length=tokenizer.model_max_length, + truncation=True, + ).input_ids + + targets = input_ids.clone() + + assert conv.sep_style == conversation_lib.SeparatorStyle.LLAMA_2 + + # Mask targets + sep = "[/INST] " + for conversation, target in zip(conversations, targets): + total_len = int(target.ne(tokenizer.pad_token_id).sum()) + + rounds = conversation.split(conv.sep2) + cur_len = 1 + target[:cur_len] = IGNORE_INDEX + for i, rou in enumerate(rounds): + if rou == "": + break + + parts = rou.split(sep) + if len(parts) != 2: + break + parts[0] += sep + + if has_image: + round_len = len(tokenizer_image_token(rou, tokenizer)) + instruction_len = len(tokenizer_image_token(parts[0], tokenizer)) - 2 + else: + round_len = len(tokenizer(rou).input_ids) + instruction_len = len(tokenizer(parts[0]).input_ids) - 2 + + target[cur_len : cur_len + instruction_len] = IGNORE_INDEX + + cur_len += round_len + target[cur_len:] = IGNORE_INDEX + + if cur_len < tokenizer.model_max_length: + if cur_len != total_len: + target[:] = IGNORE_INDEX + rank0_print(f"WARNING: tokenization mismatch: {cur_len} vs. {total_len}." f" (ignored)") + + return dict( + input_ids=input_ids, + labels=targets, + ) + + +def make_conv(prompt, answer): + return [ + { + "from": "human", + "value": prompt, + }, + { + "from": "gpt", + "value": answer, + }, + ] + + +def preprocess_gemma(sources: List[List[Dict[str, str]]], tokenizer: transformers.PreTrainedTokenizer, has_image: bool = False) -> Dict: + conv: conversation_lib.Conversation = conversation_lib.default_conversation.copy() + roles: Dict[str, str] = {"human": conv.roles[0], "gpt": conv.roles[1]} + + # Apply prompt templates + conversations: List[str] = [] + for i, source in enumerate(sources): + if roles[source[0]["from"]] != conv.roles[0]: + # Skip the first one if it is not from human + source: List[Dict[str, str]] = source[1:] + + conv.messages = [] + for j, sentence in enumerate(source): + role: str = roles[sentence["from"]] + assert role == conv.roles[j % 2], f"{i}" + conv.append_message(role, sentence["value"]) + conversations.append(conv.get_prompt()) + + # Tokenize conversations + if has_image: + input_ids: torch.Tensor = torch.stack([tokenizer_image_token(prompt, tokenizer, return_tensors="pt") for prompt in conversations], dim=0) + else: + input_ids: torch.Tensor = tokenizer( + conversations, + return_tensors="pt", + padding="longest", + max_length=tokenizer.model_max_length, + truncation=True, + ).input_ids + + targets: torch.Tensor = input_ids.clone() + assert conv.sep_style == conversation_lib.SeparatorStyle.GEMMA + + # Mask target + sep: str = conv.sep + conv.roles[1] + for conversation, target in zip(conversations, targets): + total_len: int = int(target.ne(tokenizer.pad_token_id).sum()) + + rounds: List[str] = conversation.split(conv.sep) + re_rounds = [] + for conv_idx in range(0, len(rounds), 2): + re_rounds.append(conv.sep.join(rounds[conv_idx : conv_idx + 2])) + + cur_len = 1 # Ignore + target[:cur_len] = IGNORE_INDEX + for i, rou in enumerate(re_rounds): + if rou == "": + break + + parts = rou.split(sep) + if len(parts) != 2: + break + parts[0] += sep # Re-append sep because split on this + # Now "".join(parts)==rou + + if has_image: + round_len = len(tokenizer_image_token(rou, tokenizer)) - 1 # Ignore + instruction_len = len(tokenizer_image_token(parts[0], tokenizer)) - 1 # Ignore + else: + round_len = len(tokenizer(rou).input_ids) - 1 # Ignore + instruction_len = len(tokenizer(parts[0]).input_ids) - 1 # Ignore + + round_len += 2 # sep: \n takes 2 tokens + target[cur_len : cur_len + instruction_len] = IGNORE_INDEX + cur_len += round_len + + target[cur_len:] = IGNORE_INDEX + + if cur_len < tokenizer.model_max_length: + if cur_len != total_len: + target[:] = IGNORE_INDEX + rank0_print(f"WARNING: tokenization mismatch: {cur_len} vs. {total_len}." f" (ignored)") + + return dict( + input_ids=input_ids, + labels=targets, + ) + + +def preprocess_qwen(sources, tokenizer: transformers.PreTrainedTokenizer, has_image: bool = False, max_len=2048, system_message: str = "You are a helpful assistant.") -> Dict: + roles = {"human": "<|im_start|>user", "gpt": "<|im_start|>assistant"} + + im_start, im_end = tokenizer.additional_special_tokens_ids + nl_tokens = tokenizer("\n").input_ids + _system = tokenizer("system").input_ids + nl_tokens + _user = tokenizer("user").input_ids + nl_tokens + _assistant = tokenizer("assistant").input_ids + nl_tokens + + # Apply prompt templates + input_ids, targets = [], [] + for i, source in enumerate(sources): + if roles[source[0]["from"]] != roles["human"]: + source = source[1:] + + input_id, target = [], [] + system = [im_start] + _system + tokenizer(system_message).input_ids + [im_end] + nl_tokens + input_id += system + target += [im_start] + [IGNORE_INDEX] * (len(system) - 3) + [im_end] + nl_tokens + assert len(input_id) == len(target) + for j, sentence in enumerate(source): + role = roles[sentence["from"]] + if has_image and "" in sentence["value"]: + assert sentence["value"].startswith(""), print(sentence["value"]) + + _input_id = tokenizer(role).input_ids + nl_tokens + [IMAGE_TOKEN_INDEX] + nl_tokens + tokenizer(sentence["value"][len("") :]).input_ids + [im_end] + nl_tokens + else: + _input_id = tokenizer(role).input_ids + nl_tokens + tokenizer(sentence["value"]).input_ids + [im_end] + nl_tokens + input_id += _input_id + if role == "<|im_start|>user": + _target = [im_start] + [IGNORE_INDEX] * (len(_input_id) - 3) + [im_end] + nl_tokens + elif role == "<|im_start|>assistant": + _target = [im_start] + [IGNORE_INDEX] * len(tokenizer(role).input_ids) + _input_id[len(tokenizer(role).input_ids) + 1 : -2] + [im_end] + nl_tokens + else: + raise NotImplementedError + target += _target + assert len(input_id) == len(target) + # input_id += [tokenizer.pad_token_id] * (max_len - len(input_id)) + # target += [IGNORE_INDEX] * (max_len - len(target)) + input_ids.append(input_id) + targets.append(target) + input_ids = torch.tensor(input_ids, dtype=torch.long) + targets = torch.tensor(targets, dtype=torch.long) + + return dict( + input_ids=input_ids, # tensor(bs x seq_len) + labels=targets, # tensor(bs x seq_len) + # attention_mask=input_ids.ne(tokenizer.pad_token_id), # tensor(bs x seq_len) + ) + + +def preprocess_llama3( + sources, + tokenizer: transformers.PreTrainedTokenizer, + has_image: bool = False, + max_len=2048, + system_message: str = "You are a helpful language and vision assistant. You are able to understand the visual content that the user provides, and assist the user with a variety of tasks using natural language.", +) -> Dict: + roles = {"human": "<|start_header_id|>user<|end_header_id|>", "gpt": "<|start_header_id|>assistant<|end_header_id|>"} + + eot_id = tokenizer.convert_tokens_to_ids("<|eot_id|>") + nl_tokens = tokenizer("\n").input_ids + + # Apply prompt templates + input_ids, targets = [], [] + for i, source in enumerate(sources): + if roles[source[0]["from"]] != roles["human"]: + source = source[1:] + + input_id, target = [], [] + system = tokenizer("<|begin_of_text|>").input_ids + tokenizer("<|start_header_id|>system<|end_header_id|>").input_ids + nl_tokens * 2 + tokenizer(system_message).input_ids + [eot_id] + input_id += system + target += [IGNORE_INDEX] * len(system) + for j, sentence in enumerate(source): + role = roles[sentence["from"]] + if has_image and "" in sentence["value"]: + assert sentence["value"].startswith(""), print(sentence["value"]) + _input_id = tokenizer(role).input_ids + nl_tokens * 2 + [IMAGE_TOKEN_INDEX] + tokenizer(sentence["value"][len("") :]).input_ids + [eot_id] + else: + _input_id = tokenizer(role).input_ids + nl_tokens * 2 + tokenizer(sentence["value"]).input_ids + [eot_id] + input_id += _input_id + if role == "<|start_header_id|>user<|end_header_id|>": + _target = [IGNORE_INDEX] * len(_input_id) + elif role == "<|start_header_id|>assistant<|end_header_id|>": + _target = [IGNORE_INDEX] * (len(tokenizer(role).input_ids) + 2) + _input_id[len(tokenizer(role).input_ids) + 2 : -1] + [eot_id] + else: + raise NotImplementedError + target += _target + assert len(input_id) == len(target), f"{len(input_id)} != {len(target)}" + input_ids.append(input_id) + targets.append(target) + input_ids = torch.tensor(input_ids, dtype=torch.long) + targets = torch.tensor(targets, dtype=torch.long) + + return dict( + input_ids=input_ids, # tensor(bs x seq_len) + labels=targets, # tensor(bs x seq_len) + ) + + +def preprocess_v1(sources, tokenizer: transformers.PreTrainedTokenizer, has_image: bool = False) -> Dict: + conv = conversation_lib.default_conversation.copy() + roles = {"human": conv.roles[0], "gpt": conv.roles[1]} + + # Apply prompt templates + conversations = [] + for i, source in enumerate(sources): + if roles[source[0]["from"]] != conv.roles[0]: + # Skip the first one if it is not from human + source = source[1:] + + conv.messages = [] + for j, sentence in enumerate(source): + role = roles[sentence["from"]] + assert role == conv.roles[j % 2], f"{i}" + conv.append_message(role, sentence["value"]) + conversations.append(conv.get_prompt()) + + # Tokenize conversations + + if has_image: + input_ids = torch.stack([tokenizer_image_token(prompt, tokenizer, return_tensors="pt") for prompt in conversations], dim=0) + else: + input_ids = tokenizer( + conversations, + return_tensors="pt", + padding="longest", + max_length=tokenizer.model_max_length, + truncation=True, + ).input_ids + + targets = input_ids.clone() + + assert conv.sep_style == conversation_lib.SeparatorStyle.TWO + + # Mask targets + sep = conv.sep + conv.roles[1] + ": " + for conversation, target in zip(conversations, targets): + total_len = int(target.ne(tokenizer.pad_token_id).sum()) + + rounds = conversation.split(conv.sep2) + cur_len = 1 + target[:cur_len] = IGNORE_INDEX + for i, rou in enumerate(rounds): + if rou == "": + break + + parts = rou.split(sep) + if len(parts) != 2: + break + parts[0] += sep + + if has_image: + round_len = len(tokenizer_image_token(rou, tokenizer)) + instruction_len = len(tokenizer_image_token(parts[0], tokenizer)) - 2 + else: + round_len = len(tokenizer(rou).input_ids) + instruction_len = len(tokenizer(parts[0]).input_ids) - 2 + + if i != 0 and not tokenizer.legacy and IS_TOKENIZER_GREATER_THAN_0_14: + round_len -= 1 + instruction_len -= 1 + + target[cur_len : cur_len + instruction_len] = IGNORE_INDEX + + cur_len += round_len + target[cur_len:] = IGNORE_INDEX + + if cur_len < tokenizer.model_max_length: + if cur_len != total_len: + target[:] = IGNORE_INDEX + print(f"WARNING: tokenization mismatch: {cur_len} vs. {total_len}." f" (ignored)") + + return dict( + input_ids=input_ids, + labels=targets, + ) + + +def preprocess_mpt(sources, tokenizer: transformers.PreTrainedTokenizer, has_image: bool = False) -> Dict: + conv = conversation_lib.default_conversation.copy() + roles = {"human": conv.roles[0], "gpt": conv.roles[1]} + + # Apply prompt templates + conversations = [] + for i, source in enumerate(sources): + if roles[source[0]["from"]] != conv.roles[0]: + # Skip the first one if it is not from human + source = source[1:] + + conv.messages = [] + for j, sentence in enumerate(source): + role = roles[sentence["from"]] + assert role == conv.roles[j % 2], f"{i}" + conv.append_message(role, sentence["value"]) + conversations.append(conv.get_prompt()) + + # Tokenize conversations + + if has_image: + input_ids = torch.stack([tokenizer_image_token(prompt, tokenizer, return_tensors="pt") for prompt in conversations], dim=0) + else: + input_ids = tokenizer( + conversations, + return_tensors="pt", + padding="longest", + max_length=tokenizer.model_max_length, + truncation=True, + ).input_ids + + targets = input_ids.clone() + assert conv.sep_style == conversation_lib.SeparatorStyle.MPT + + # Mask targets + sep = conv.sep + conv.roles[1] + for conversation, target in zip(conversations, targets): + total_len = int(target.ne(tokenizer.pad_token_id).sum()) + + rounds = conversation.split(conv.sep) + re_rounds = [conv.sep.join(rounds[:3])] # system + user + gpt + for conv_idx in range(3, len(rounds), 2): + re_rounds.append(conv.sep.join(rounds[conv_idx : conv_idx + 2])) # user + gpt + cur_len = 1 + target[:cur_len] = IGNORE_INDEX + for i, rou in enumerate(re_rounds): + if rou == "": + break + + parts = rou.split(sep) + if len(parts) != 2: + break + parts[0] += sep + + if has_image: + round_len = len(tokenizer_image_token(rou, tokenizer)) + instruction_len = len(tokenizer_image_token(parts[0], tokenizer)) - 1 + else: + round_len = len(tokenizer(rou).input_ids) + instruction_len = len(tokenizer(parts[0]).input_ids) - 1 + + if i != 0 and getattr(tokenizer, "legacy", False) and IS_TOKENIZER_GREATER_THAN_0_14: + round_len += 1 + instruction_len += 1 + + target[cur_len : cur_len + instruction_len] = IGNORE_INDEX + + cur_len += round_len + target[cur_len:] = IGNORE_INDEX + + if cur_len < tokenizer.model_max_length: + if cur_len != total_len: + target[:] = IGNORE_INDEX + print(f"WARNING: tokenization mismatch: {cur_len} vs. {total_len}." f"(#turns={len(re_rounds)} ignored)") + + return dict( + input_ids=input_ids, + labels=targets, + ) + + +def preprocess_plain( + sources: Sequence[str], + tokenizer: transformers.PreTrainedTokenizer, +) -> Dict: + # add end signal and concatenate together + conversations = [] + for source in sources: + assert len(source) == 2 + assert DEFAULT_IMAGE_TOKEN in source[0]["value"] + source[0]["value"] = DEFAULT_IMAGE_TOKEN + conversation = source[0]["value"] + source[1]["value"] + conversation_lib.default_conversation.sep + conversations.append(conversation) + # tokenize conversations + input_ids = [tokenizer_image_token(prompt, tokenizer, return_tensors="pt") for prompt in conversations] + targets = copy.deepcopy(input_ids) + for target, source in zip(targets, sources): + tokenized_len = len(tokenizer_image_token(source[0]["value"], tokenizer)) + target[:tokenized_len] = IGNORE_INDEX + + return dict(input_ids=input_ids, labels=targets) + + +def preprocess(sources: Sequence[str], tokenizer: transformers.PreTrainedTokenizer, has_image: bool = False) -> Dict: + """ + Given a list of sources, each is a conversation list. This transform: + 1. Add signal '### ' at the beginning each sentence, with end signal '\n'; + 2. Concatenate conversations together; + 3. Tokenize the concatenated conversation; + 4. Make a deepcopy as the target. Mask human words with IGNORE_INDEX. + """ + if conversation_lib.default_conversation.sep_style == conversation_lib.SeparatorStyle.PLAIN: + return preprocess_plain(sources, tokenizer) + if conversation_lib.default_conversation.sep_style == conversation_lib.SeparatorStyle.LLAMA_2: + return preprocess_llama_2(sources, tokenizer, has_image=has_image) + if conversation_lib.default_conversation.version.startswith("v1"): + return preprocess_v1(sources, tokenizer, has_image=has_image) + if conversation_lib.default_conversation.version == "mpt": + return preprocess_mpt(sources, tokenizer, has_image=has_image) + if conversation_lib.default_conversation.version == "qwen": + return preprocess_qwen(sources, tokenizer, has_image=has_image) + if conversation_lib.default_conversation.version == "gemma": + return preprocess_gemma(sources, tokenizer, has_image=has_image) + if conversation_lib.default_conversation.version == "llama_v3": + return preprocess_llama3(sources, tokenizer, has_image=has_image) + # add end signal and concatenate together + conversations = [] + for source in sources: + header = f"{conversation_lib.default_conversation.system}\n\n" + conversation = _add_speaker_and_signal(header, source) + conversations.append(conversation) + + # tokenize conversations + def get_tokenize_len(prompts): + return [len(tokenizer_image_token(prompt, tokenizer)) for prompt in prompts] + + if has_image: + input_ids = [tokenizer_image_token(prompt, tokenizer, return_tensors="pt") for prompt in conversations] + else: + conversations_tokenized = _tokenize_fn(conversations, tokenizer) + input_ids = conversations_tokenized["input_ids"] + + targets = copy.deepcopy(input_ids) + for target, source in zip(targets, sources): + if has_image: + tokenized_lens = get_tokenize_len([header] + [s["value"] for s in source]) + else: + tokenized_lens = _tokenize_fn([header] + [s["value"] for s in source], tokenizer)["input_ids_lens"] + speakers = [sentence["from"] for sentence in source] + _mask_targets(target, tokenized_lens, speakers) + + return dict(input_ids=input_ids, labels=targets) + + +def load_data(data_path): + if "jsonl" in data_path: + data_list = load_jsonl(data_path) + else: + data_list = load_json(data_path) + return data_list + + +class DPODataset(Dataset): + """Dataset for DPODataset fine-tuning.""" + + def __init__(self, data_path: str, tokenizer: transformers.PreTrainedTokenizer, data_args: DataArguments): + super(DPODataset, self).__init__() + # Handle multiple JSON files specified in the data_path + self.list_data_dict = [] + + if "{" in data_path and "}" in data_path: + base_path, file_pattern = re.match(r"^(.*)\{(.*)\}\.json$", data_path).groups() + file_names = file_pattern.split(",") + rank0_print(f"Loading {file_names} from {base_path}") + data_args.dataset_paths = [] + for file_name in file_names: + data_args.dataset_paths.append(f"{base_path}{file_name}.json") + full_path = f"{base_path}{file_name}.json" + rank0_print(f"Loading {full_path}") + cur_data_dict = load_data(full_path) + rank0_print(f"Loaded {len(cur_data_dict)} samples from {full_path}") + self.list_data_dict.extend(cur_data_dict) + elif data_path.endswith(".yaml"): + with open(data_path, "r") as file: + yaml_data = yaml.safe_load(file) + datasets = yaml_data.get("datasets") + # file should be in the format of: + # datasets: + # - json_path: xxxx1.json + # sampling_strategy: first:1000 + # - json_path: xxxx2.json + # sampling_strategy: end:3000 + # - json_path: xxxx3.json + # sampling_strategy: random:999 + data_args.dataset_paths = [dataset.get("json_path") for dataset in datasets] + for dataset in datasets: + json_path = dataset.get("json_path") + sampling_strategy = dataset.get("sampling_strategy", "all") + sampling_number = None + + rank0_print(f"Loading {json_path} with {sampling_strategy} sampling strategy") + cur_data_dict = load_data(json_path) + + if ":" in sampling_strategy: + sampling_strategy, sampling_number = sampling_strategy.split(":") + if "%" in sampling_number: + sampling_number = math.ceil(int(sampling_number.split("%")[0]) * len(cur_data_dict) / 100) + else: + sampling_number = int(sampling_number) + + # Apply the sampling strategy + if sampling_strategy == "first" and sampling_number is not None: + cur_data_dict = cur_data_dict[:sampling_number] + elif sampling_strategy == "end" and sampling_number is not None: + cur_data_dict = cur_data_dict[-sampling_number:] + elif sampling_strategy == "random" and sampling_number is not None: + random.shuffle(cur_data_dict) + cur_data_dict = cur_data_dict[:sampling_number] + + rank0_print(f"Loaded {len(cur_data_dict)} samples from {json_path}") + self.list_data_dict.extend(cur_data_dict) + else: + data_args.dataset_paths = [data_path] + rank0_print(f"Loading {data_path}") + cur_data_dict = load_data(data_path) + rank0_print(f"Loaded {len(cur_data_dict)} samples from {data_path}") + self.list_data_dict.extend(cur_data_dict) + + rank0_print("Formatting inputs...Skip in lazy mode") + self.tokenizer = tokenizer + self.data_args = data_args + + def __len__(self): + return len(self.list_data_dict) + + @property + def lengths(self): + length_list = [] + for sample in self.list_data_dict: + # Calculate the length of the prompt, answer, chosen, and rejected text + cur_len = len(sample["prompt"].split()) + len(sample["answer"].split()) + len(sample["chosen"].split()) + len(sample["rejected"].split()) + # Add additional tokens if an image is present + img_tokens = 128 if "image" in sample else 0 + length_list.append(cur_len + img_tokens) + return length_list + + @property + def modality_lengths(self): + length_list = [] + for sample in self.list_data_dict: + # Calculate the length of the prompt, answer, chosen, and rejected text + cur_len = len(sample["prompt"].split()) + len(sample["answer"].split()) + len(sample["chosen"].split()) + len(sample["rejected"].split()) + # If the sample includes a video, the length is positive; otherwise, it is negative + cur_len = cur_len if ("video" in sample or "image" in sample) else -cur_len + length_list.append(cur_len) + return length_list + + def process_image(self, image_file): + image_folder = self.data_args.image_folder + processor = self.data_args.image_processor + # print(f"\n\nInspecting the image path, folder = {image_folder}, image={image_file}\n\n") + try: + image = Image.open(os.path.join(image_folder, image_file)).convert("RGB") + except Exception as exn: + print(f"Failed to open image {image_file}. Exception:", exn) + raise exn + + image_size = image.size + if self.data_args.image_aspect_ratio == "highres": + image = process_highres_image(image, self.data_args.image_processor, self.data_args.image_grid_pinpoints) + elif self.data_args.image_aspect_ratio == "anyres" or "anyres" in self.data_args.image_aspect_ratio: + image = process_anyres_image(image, self.data_args.image_processor, self.data_args.image_grid_pinpoints) + elif self.data_args.image_aspect_ratio == "crop_split": + image = process_highres_image_crop_split(image, self.data_args) + elif self.data_args.image_aspect_ratio == "pad": + + def expand2square(pil_img, background_color): + width, height = pil_img.size + if width == height: + return pil_img + elif width > height: + result = Image.new(pil_img.mode, (width, width), background_color) + result.paste(pil_img, (0, (width - height) // 2)) + return result + else: + result = Image.new(pil_img.mode, (height, height), background_color) + result.paste(pil_img, ((height - width) // 2, 0)) + return result + + image = expand2square(image, tuple(int(x * 255) for x in processor.image_mean)) + image = processor.preprocess(image, return_tensors="pt")["pixel_values"][0] + else: + image = processor.preprocess(image, return_tensors="pt")["pixel_values"][0] + return image, image_size, "image" + + def __getitem__(self, i) -> Dict[str, torch.Tensor]: + # TODO: define number of retries somewhere else + num_base_retries = 3 + num_final_retries = 300 + + # try the current sample first + for attempt_idx in range(num_base_retries): + try: + sample = self._get_item(i) + return sample + except Exception as e: + # sleep 1s in case it is a cloud disk issue + print(f"[Try #{attempt_idx}] Failed to fetch sample {i}. Exception:", e) + time.sleep(1) + + # try other samples, in case it is file corruption issue + for attempt_idx in range(num_base_retries): + try: + next_index = min(i + 1, len(self.list_data_dict) - 1) + # sample_idx = random.choice(range(len(self))) + sample = self._get_item(next_index) + return sample + except Exception as e: + # no need to sleep + print(f"[Try other #{attempt_idx}] Failed to fetch sample {next_index}. Exception:", e) + pass + + # still fail, most likely to be path issue or cloud disk issue, retry the same sample for longer + # for attempt_idx in range(num_final_retries): + # try: + # sample = self._get_item(i) + # return sample + # except Exception as e: + # # sleep 1s in case it is a cloud disk issue + # print(f"[Final try #{attempt_idx}] Failed to fetch sample {i}. Exception:", e) + # time.sleep(1) + + # Finally raise exception on failing. + assert False, "Failed to fetch sample." + + def _get_item(self, i) -> Dict[str, torch.Tensor]: + sources = self.list_data_dict[i] + if isinstance(i, int): + sources = [sources] + assert len(sources) == 1, "Don't know why it is wrapped to a list" # FIXME + + suffix = None + if "image" in sources[0]: + image_file = self.list_data_dict[i]["image"] + if type(image_file) is list: + image = [self.process_image(f) for f in image_file] + else: + image = [self.process_image(image_file)] + # sources = preprocess_multimodal(copy.deepcopy([e["conversations"] for e in sources]), self.data_args) + + elif "video" in sources[0]: # FIXME: This logic should be largely improved by Yuanhan. It's too messy now. + video_file = self.list_data_dict[i]["video"] + video_folder = self.data_args.video_folder + video_file = os.path.join(video_folder, video_file) + suffix = video_file.split(".")[-1] + if not os.path.exists(video_file): + print("File {} not exist!".format(video_file)) + + if suffix == "pkl": + video_info = pickle.load(open(video_file, "rb")) + image = torch.from_numpy(video_info["feats"][:, 1:]) + input_prompt = video_info["inputs"].replace("...", "") + # replace the default image token with multiple tokens + input_prompt = input_prompt.replace(DEFAULT_IMAGE_TOKEN, DEFAULT_IMAGE_TOKEN * self.data_args.video_token) + sources, query_prompt = preprocess_multimodal_movie(copy.deepcopy([e["conversations"] for e in sources]), self.data_args, input_prompt) + else: # using videoreader + if "shareVideoGPTV" not in video_file and "liangke" not in video_file: + vr = VideoReader(video_file, ctx=cpu(0)) + total_frame_num = len(vr) + avg_fps = round(vr.get_avg_fps() / self.data_args.video_fps) + frame_idx = [i for i in range(0, total_frame_num, avg_fps)] + if self.data_args.frames_upbound > 0: + if len(frame_idx) > self.data_args.frames_upbound: + uniform_sampled_frames = np.linspace(0, total_frame_num - 1, self.data_args.frames_upbound, dtype=int) + frame_idx = uniform_sampled_frames.tolist() + video = vr.get_batch(frame_idx).asnumpy() + video = np.array(video) + else: + if "liangke" in video_file: + video_file = self.list_data_dict[i]["video"] + frame_files = [os.path.join(video_file, f) for f in os.listdir(video_file) if os.path.isfile(os.path.join(video_file, f))] + frame_files.sort() # Ensure the frames are sorted if they are named sequentially + + # TODO: Hard CODE: Determine the indices for uniformly sampling 10 frames + num_frames_to_sample = 10 + + total_frames = len(frame_files) + + sampled_indices = np.linspace(0, total_frames - 1, num_frames_to_sample, dtype=int) + + # Read and store the sampled frames + video = [] + for idx in sampled_indices: + frame_path = frame_files[idx] + try: + with Image.open(frame_path) as img: + frame = img.convert("RGB") + video.append(frame) + except IOError: + print(f"Failed to read frame at path: {frame_path}") + + processor = self.data_args.image_processor + image = processor.preprocess(video, return_tensors="pt")["pixel_values"] + image = [(image, video[0].size, "video")] + # sources = preprocess_multimodal(copy.deepcopy([e["conversations"] for e in sources]), self.data_args) + + else: + sources = copy.deepcopy([e["conversations"] for e in sources]) + + has_image = ("image" in self.list_data_dict[i]) or ("video" in self.list_data_dict[i]) + # data_dict = preprocess(sources, self.tokenizer, has_image=has_image) + data_dict = copy.deepcopy(self.list_data_dict[i]) # inplace modification following + + if "prompt" in data_dict: + prompt = data_dict["prompt"] + prompt = prompt.replace("", "").strip() + prompt = "\n" + prompt + data_dict["prompt"] = prompt + else: + prompt = None + + if suffix == "pkl": + prompt = [query_prompt] + + # image exist in the data + if "image" in self.list_data_dict[i]: + data_dict["image"] = image + elif "video" in self.list_data_dict[i]: + data_dict["image"] = image + elif self.data_args.is_multimodal: + # image does not exist in the data, but the model is multimodal + crop_size = self.data_args.image_processor.crop_size + data_dict["image"] = [ + (torch.zeros(1, 3, crop_size["height"], crop_size["width"]), (crop_size["width"], crop_size["height"]), "text"), + ] + # prompt exist in the data + data_dict["has_image"] = has_image + return data_dict + + +@dataclass +class DPODataCollator(DPODataCollatorWithPadding): + """Collate examples for DPO fine-tuning.""" + + # tokenizer: transformers.PreTrainedTokenizer + + def collate(self, batch): + # first, pad everything to the same length + # input_ids, labels = tuple([instance[key] for instance in instances] + # for key in ("input_ids", "labels")) + # input_ids = torch.nn.utils.rnn.pad_sequence( + # input_ids, + # batch_first=True, + # padding_value=self.tokenizer.pad_token_id) + # labels = torch.nn.utils.rnn.pad_sequence(labels, + # batch_first=True, + # padding_value=IGNORE_INDEX) + # input_ids = input_ids[:, :self.tokenizer.model_max_length] + # labels = labels[:, :self.tokenizer.model_max_length] + # batch = dict( + # input_ids=input_ids, + # labels=labels, + # attention_mask=input_ids.ne(self.tokenizer.pad_token_id), + # ) + padded_batch = {} + for k in batch[0].keys(): + if k.endswith("_input_ids") or k.endswith("_attention_mask") or k.endswith("_labels"): + # if "prompt" in k: + # to_pad = [torch.LongTensor(ex[k][::-1]) for ex in batch] + # else: + to_pad = [torch.LongTensor(ex[k]) for ex in batch] + if k.endswith("_input_ids"): + padding_value = self.tokenizer.pad_token_id + elif k.endswith("_labels"): + padding_value = self.label_pad_token_id + else: + continue + # elif k.endswith("_attention_mask"): + # padding_value = self.padding_value + # else: + # raise ValueError(f"Unexpected key in batch '{k}'") + + padded_batch[k] = torch.nn.utils.rnn.pad_sequence(to_pad, batch_first=True, padding_value=padding_value) + # for the prompt, flip back so padding is on left side + # if "prompt" in k: + # padded_batch[k] = padded_batch[k].flip(dims=[1]) + else: + padded_batch[k] = [ex[k] for ex in batch] + for k in ["chosen_input_ids", "rejected_input_ids"]: + attn_k = k.replace("input_ids", "attention_mask") + padded_batch[attn_k] = padded_batch[k].ne(self.tokenizer.pad_token_id) + return padded_batch + + def tokenize_batch_element(self, prompt: str, chosen: str, rejected: str, has_image: bool = True) -> Dict: + """Tokenize a single batch element. + + At this stage, we don't convert to PyTorch tensors yet; we just handle the truncation + in case the prompt + chosen or prompt + rejected responses is/are too long. First + we truncate the prompt; if we're still too long, we truncate the chosen/rejected. + + We also create the labels for the chosen/rejected responses, which are of length equal to + the sum of the length of the prompt and the chosen/rejected response, with + label_pad_token_id for the prompt tokens. + """ + # import pdb; pdb.set_trace() + batch = {} + + chosen_sources = make_conv(prompt, chosen) + rejected_sources = make_conv(prompt, rejected) + chosen_data_dict = preprocess([chosen_sources], self.tokenizer, has_image=has_image) + # chosen_data_dict['attention_mask'] = chosen_data_dict["input_ids"].ne(self.tokenizer.pad_token_id) + + rejected_data_dict = preprocess([rejected_sources], self.tokenizer, has_image=has_image) + # rejected_data_dict['attention_mask'] = rejected_data_dict["input_ids"].ne(self.tokenizer.pad_token_id) + + chosen_data_dict = {k: v[0] for k, v in chosen_data_dict.items()} + rejected_data_dict = {k: v[0] for k, v in rejected_data_dict.items()} + + for k, toks in { + "chosen": chosen_data_dict, + "rejected": rejected_data_dict, + }.items(): + for type_key, tokens in toks.items(): + if type_key == "token_type_ids": + continue + batch[f"{k}_{type_key}"] = tokens + return batch + + def __call__(self, features: List[Dict[str, Any]]) -> Dict[str, Any]: + + tokenized_batch = [] + Xs, keys = [], [] + for feature in features: + prompt = feature["prompt"] + chosen = feature["chosen"] + rejected = feature["rejected"] + has_image = feature["has_image"] + # Xs.append(feature[has_X]) + # keys.append(has_X) + + batch_element = self.tokenize_batch_element(prompt, chosen, rejected, has_image=has_image) + tokenized_batch.append(batch_element) + + # return collated batch + padded_batch = self.collate(tokenized_batch) + # import pdb;pdb.set_trace() + if "image" in features[0]: + # instances[1]['image'][0][0].shape + # torch.Size([5, 3, 224, 224]) + images = [instance["image"] for instance in features] + + padded_batch["image_sizes"] = [im[1] for im_list in images for im in im_list] + padded_batch["modalities"] = [im[2] for im_list in images for im in im_list] + images = [im[0] for im_list in images for im in im_list] + # import pdb;pdb.set_trace() + + padded_batch["images"] = images + # padded_batch["images"] =[padded_batch["modalities"], images] + + return padded_batch + + +def make_dpo_data_module(tokenizer: transformers.PreTrainedTokenizer, data_args) -> Dict: + """Make dataset and collator for supervised fine-tuning.""" + train_dataset = DPODataset(tokenizer=tokenizer, data_path=data_args.data_path, data_args=data_args) + return train_dataset + + +def get_model(model_args, training_args, bnb_model_from_pretrained_args): + assert training_args.attn_implementation + if training_args.attn_implementation == "sdpa" and torch.__version__ < "2.1.2": + raise ValueError("The 'sdpa' attention implementation requires torch version 2.1.2 or higher.") + + ######################### Overwrite config ######################### + customized_kwargs = dict() + customized_kwargs.update(bnb_model_from_pretrained_args) + overwrite_config = {} + cfg_pretrained = None + if "qwen" in model_args.model_name_or_path.lower(): + cfg_pretrained = LlavaQwenConfig.from_pretrained(model_args.model_name_or_path) + elif "mistral" in model_args.model_name_or_path.lower() or "zephyr" in model_args.model_name_or_path.lower(): + cfg_pretrained = LlavaMistralConfig.from_pretrained(model_args.model_name_or_path) + elif ( + "wizardlm-2" in model_args.model_name_or_path.lower() + or "vicuna" in model_args.model_name_or_path.lower() + or "llama" in model_args.model_name_or_path.lower() + or "yi" in model_args.model_name_or_path.lower() + or "nous-hermes" in model_args.model_name_or_path.lower() + and "wizard-2" in model_args.model_name_or_path.lower() + ): + cfg_pretrained = LlavaConfig.from_pretrained(model_args.model_name_or_path) + else: + cfg_pretrained = AutoConfig.from_pretrained(model_args.model_name_or_path) + + if model_args.rope_scaling_factor is not None and model_args.rope_scaling_type is not None and cfg_pretrained is not None: + overwrite_config["rope_scaling"] = { + "factor": model_args.rope_scaling_factor, + "type": model_args.rope_scaling_type, + } + if training_args.model_max_length is None: + training_args.model_max_length = cfg_pretrained.max_position_embeddings * model_args.rope_scaling_factor + overwrite_config["max_sequence_length"] = training_args.model_max_length + assert training_args.model_max_length == int(cfg_pretrained.max_position_embeddings * model_args.rope_scaling_factor), print( + f"model_max_length: {training_args.model_max_length}, max_position_embeddings: {cfg_pretrained.max_position_embeddings}, rope_scaling_factor: {model_args.rope_scaling_factor}" + ) + # overwrite_config["max_sequence_length"] = model_args.max_sequence_length + # overwrite_config["tokenizer_model_max_length"] = model_args.tokenizer_model_max_length + + if model_args.mm_spatial_pool_stride is not None and model_args.mm_spatial_pool_out_channels is not None and model_args.mm_spatial_pool_mode is not None and model_args.mm_resampler_type is not None and cfg_pretrained is not None: + overwrite_config["mm_resampler_type"] = model_args.mm_resampler_type + overwrite_config["mm_spatial_pool_stride"] = model_args.mm_spatial_pool_stride + overwrite_config["mm_spatial_pool_out_channels"] = model_args.mm_spatial_pool_out_channels + overwrite_config["mm_spatial_pool_mode"] = model_args.mm_spatial_pool_mode + + if overwrite_config: + rank0_print(f"Overwriting config with {overwrite_config}") + for k, v in overwrite_config.items(): + setattr(cfg_pretrained, k, v) + + customized_kwargs["config"] = cfg_pretrained + + ######################### Finish Overwrite ########################### + + ref_model = None + if model_args.model_class_name is not None: + actual_model_class_name = f"{model_args.model_class_name}ForCausalLM" + model_class = getattr(transformers, actual_model_class_name) + rank0_print(f"Using model class {model_class} from {model_args.model_class_name}") + model = model_class.from_pretrained( + model_args.model_name_or_path, + cache_dir=training_args.cache_dir, + attn_implementation=training_args.attn_implementation, + torch_dtype=(torch.bfloat16 if training_args.bf16 else None), + low_cpu_mem_usage=False, + **customized_kwargs, + ) + elif model_args.vision_tower is not None: + if "mixtral" in model_args.model_name_or_path.lower(): + model = LlavaMixtralForCausalLM.from_pretrained( + model_args.model_name_or_path, + cache_dir=training_args.cache_dir, + attn_implementation=training_args.attn_implementation, + torch_dtype=(torch.bfloat16 if training_args.bf16 else None), + low_cpu_mem_usage=False, + **customized_kwargs, + ) + from transformers.models.mixtral.modeling_mixtral import MixtralSparseMoeBlock + + deepspeed.utils.set_z3_leaf_modules(model, [MixtralSparseMoeBlock]) + elif "mistral" in model_args.model_name_or_path.lower() or "zephyr" in model_args.model_name_or_path.lower(): + model = LlavaMistralForCausalLM.from_pretrained( + model_args.model_name_or_path, + cache_dir=training_args.cache_dir, + attn_implementation=training_args.attn_implementation, + torch_dtype=(torch.bfloat16 if training_args.bf16 else None), + low_cpu_mem_usage=False, + **customized_kwargs, + ) + elif ( + "wizardlm-2" in model_args.model_name_or_path.lower() + or "vicuna" in model_args.model_name_or_path.lower() + or "llama" in model_args.model_name_or_path.lower() + or "yi" in model_args.model_name_or_path.lower() + or "nous-hermes" in model_args.model_name_or_path.lower() + and "wizard-2" in model_args.model_name_or_path.lower() + ): + model = LlavaLlamaForCausalLM.from_pretrained( + model_args.model_name_or_path, + cache_dir=training_args.cache_dir, + attn_implementation=training_args.attn_implementation, + torch_dtype=(torch.bfloat16 if training_args.bf16 else None), + low_cpu_mem_usage=False, + **customized_kwargs, + ) + + if "zero3" in training_args.deepspeed: + rank0_print("#### Initialize reference model #####") + ref_model = LlavaLlamaForCausalLM.from_pretrained( + model_args.model_name_or_path, + cache_dir=training_args.cache_dir, + attn_implementation=training_args.attn_implementation, + torch_dtype=(torch.bfloat16 if training_args.bf16 else None), + low_cpu_mem_usage=False, + **customized_kwargs, + ) + + elif "qwen" in model_args.model_name_or_path.lower() or "quyen" in model_args.model_name_or_path.lower(): + if "moe" in model_args.model_name_or_path.lower(): + model = LlavaQwenMoeForCausalLM.from_pretrained( + model_args.model_name_or_path, + cache_dir=training_args.cache_dir, + attn_implementation=training_args.attn_implementation, + torch_dtype=(torch.bfloat16 if training_args.bf16 else None), + low_cpu_mem_usage=False, + **customized_kwargs, + ) + from transformers.models.qwen2_moe.modeling_qwen2_moe import Qwen2MoeSparseMoeBlock + + deepspeed.utils.set_z3_leaf_modules(model, [Qwen2MoeSparseMoeBlock]) + else: + model = LlavaQwenForCausalLM.from_pretrained( + model_args.model_name_or_path, + cache_dir=training_args.cache_dir, + attn_implementation=training_args.attn_implementation, + torch_dtype=(torch.bfloat16 if training_args.bf16 else None), + low_cpu_mem_usage=False, + **customized_kwargs, + ) + + if "zero3" in training_args.deepspeed: + rank0_print("#### Initialize reference model #####") + ref_model = LlavaQwenForCausalLM.from_pretrained( + model_args.model_name_or_path, + cache_dir=training_args.cache_dir, + attn_implementation=training_args.attn_implementation, + torch_dtype=(torch.bfloat16 if training_args.bf16 else None), + low_cpu_mem_usage=False, + **customized_kwargs, + ) + + elif "gemma" in model_args.model_name_or_path.lower(): + model = LlavaGemmaForCausalLM.from_pretrained( + model_args.model_name_or_path, + cache_dir=training_args.cache_dir, + attn_implementation=training_args.attn_implementation, + torch_dtype=(torch.bfloat16 if training_args.bf16 else None), + low_cpu_mem_usage=False, + **customized_kwargs, + ) + else: + raise ValueError(f"Unknown model class {model_args}") + else: + model = transformers.LlamaForCausalLM.from_pretrained( + model_args.model_name_or_path, cache_dir=training_args.cache_dir, attn_implementation=training_args.attn_implementation, torch_dtype=(torch.bfloat16 if training_args.bf16 else None), **customized_kwargs + ) + return model, ref_model + + +def train(attn_implementation=None): + global local_rank + + parser = transformers.HfArgumentParser((ModelArguments, DataArguments, TrainingArguments)) + model_args, data_args, training_args = parser.parse_args_into_dataclasses() + + if training_args.verbose_logging: + rank0_print(f"Inspecting experiment hyperparameters:\n") + rank0_print(f"model_args = {vars(model_args)}\n\n") + rank0_print(f"data_args = {vars(data_args)}\n\n") + rank0_print(f"training_args = {vars(training_args)}\n\n") + # rank0_print(f"evaluation_args = {vars(evaluation_args)}\n\n") + + local_rank = training_args.local_rank + compute_dtype = torch.float16 if training_args.fp16 else (torch.bfloat16 if training_args.bf16 else torch.float32) + + bnb_model_from_pretrained_args = {} + if training_args.bits in [4, 8]: + from transformers import BitsAndBytesConfig + + bnb_model_from_pretrained_args.update( + dict( + device_map={"": training_args.device}, + load_in_4bit=training_args.bits == 4, + load_in_8bit=training_args.bits == 8, + quantization_config=BitsAndBytesConfig( + load_in_4bit=training_args.bits == 4, + load_in_8bit=training_args.bits == 8, + llm_int8_threshold=6.0, + llm_int8_has_fp16_weight=False, + bnb_4bit_compute_dtype=compute_dtype, + bnb_4bit_use_double_quant=training_args.double_quant, + bnb_4bit_quant_type=training_args.quant_type, # {'fp4', 'nf4'} + ), + ) + ) + + model, ref_model = get_model(model_args, training_args, bnb_model_from_pretrained_args) + model.config.use_cache = False + + if model_args.freeze_backbone: + model.model.requires_grad_(False) + + if training_args.bits in [4, 8]: + from peft import prepare_model_for_kbit_training + + model.config.torch_dtype = torch.float32 if training_args.fp16 else (torch.bfloat16 if training_args.bf16 else torch.float32) + model = prepare_model_for_kbit_training(model, use_gradient_checkpointing=training_args.gradient_checkpointing) + + if training_args.gradient_checkpointing: + if hasattr(model, "enable_input_require_grads"): + model.enable_input_require_grads() + if ref_model is not None: + ref_model.enable_input_require_grads() + else: + + def make_inputs_require_grad(module, input, output): + output.requires_grad_(True) + + model.get_input_embeddings().register_forward_hook(make_inputs_require_grad) + + if ref_model is not None: + ref_model.get_input_embeddings().register_forward_hook(make_inputs_require_grad) + + if training_args.lora_enable: + from peft import LoraConfig, get_peft_model + + lora_config = LoraConfig( + r=training_args.lora_r, + lora_alpha=training_args.lora_alpha, + target_modules=find_all_linear_names(model), + lora_dropout=training_args.lora_dropout, + bias=training_args.lora_bias, + task_type="CAUSAL_LM", + ) + if training_args.bits == 16: + if training_args.bf16: + model.to(torch.bfloat16) + if training_args.fp16: + model.to(torch.float16) + rank0_print("Adding LoRA adapters...") + model = get_peft_model(model, lora_config) + + if "mpt" in model_args.model_name_or_path: + tokenizer = transformers.AutoTokenizer.from_pretrained(model_args.model_name_or_path, cache_dir=training_args.cache_dir, model_max_length=training_args.model_max_length, padding_side="right") + elif "mistral" in model_args.model_name_or_path.lower() or "mixtral" in model_args.model_name_or_path.lower() or "zephyr" in model_args.model_name_or_path.lower(): + tokenizer = transformers.AutoTokenizer.from_pretrained(model_args.model_name_or_path, cache_dir=training_args.cache_dir, model_max_length=training_args.model_max_length, padding_side="left") + elif "qwen" in model_args.model_name_or_path.lower(): + tokenizer = transformers.AutoTokenizer.from_pretrained(model_args.model_name_or_path, cache_dir=training_args.cache_dir, model_max_length=training_args.model_max_length, padding_side="right") + else: # for all other models + tokenizer = transformers.AutoTokenizer.from_pretrained( + model_args.model_name_or_path, + cache_dir=training_args.cache_dir, + model_max_length=training_args.model_max_length, + padding_side="right", + use_fast=False, + ) + + rank0_print(f"Prompt version: {model_args.version}") + if model_args.version == "v0": + if tokenizer.pad_token is None: + smart_tokenizer_and_embedding_resize( + special_tokens_dict=dict(pad_token="[PAD]"), + tokenizer=tokenizer, + model=model, + ) + elif model_args.version == "v0.5": + tokenizer.pad_token = tokenizer.unk_token + else: + if tokenizer.unk_token is not None: + tokenizer.pad_token = tokenizer.unk_token + if model_args.version in conversation_lib.conv_templates: + conversation_lib.default_conversation = conversation_lib.conv_templates[model_args.version] + else: + conversation_lib.default_conversation = conversation_lib.conv_templates["vicuna_v1"] + + if model_args.vision_tower is not None: + model.get_model().initialize_vision_modules(model_args=model_args, fsdp=training_args.fsdp) + + vision_tower = model.get_vision_tower() + vision_tower.to(dtype=torch.bfloat16 if training_args.bf16 else torch.float16, device=training_args.device) + + data_args.image_processor = vision_tower.image_processor + data_args.is_multimodal = True + + model.config.image_aspect_ratio = data_args.image_aspect_ratio + if data_args.image_grid_pinpoints is not None: + # for input like "(1x1)...(3x3)", convert to [(1, 1), (2, 1), (3, 1), (1, 2), (2, 2), (3, 2), (1, 3), (2, 3), (3, 3)] + if "x" in data_args.image_grid_pinpoints and "..." in data_args.image_grid_pinpoints: + vis_encoder_size = data_args.image_processor.size[0] + matches = re.findall(r"\((\d+)x(\d+)\)", data_args.image_grid_pinpoints) + range_start = tuple(map(int, matches[0])) + range_end = tuple(map(int, matches[-1])) + grid_pinpoints = [(i, j) for i in range(range_start[0], range_end[0] + 1) for j in range(range_start[1], range_end[1] + 1)] + grid_pinpoints = [[dim * vis_encoder_size for dim in pair] for pair in grid_pinpoints] + data_args.image_grid_pinpoints = grid_pinpoints + elif "x" in data_args.image_grid_pinpoints: + vis_encoder_size = data_args.image_processor.size[0] + assert vis_encoder_size in [224, 336, 384, 448, 512], "vis_encoder_size should be in [224, 336, 384, 448, 512]" + grid_pinpoints = data_args.image_grid_pinpoints.replace(" ", "").replace("x", ",")[1:-1].split("),(") + data_args.image_grid_pinpoints = [[int(x) * vis_encoder_size for x in item.split(",")] for item in grid_pinpoints] + else: + data_args.image_grid_pinpoints = ast.literal_eval(data_args.image_grid_pinpoints) # for backward compatibility + model.config.image_grid_pinpoints = data_args.image_grid_pinpoints + model.config.image_crop_resolution = data_args.image_crop_resolution + model.config.image_split_resolution = data_args.image_split_resolution + model.config.tokenizer_padding_side = tokenizer.padding_side + model.config.tokenizer_model_max_length = tokenizer.model_max_length + + ### Deciding train which part of the model + if model_args.mm_tunable_parts is None: # traditional way of deciding which part to train + model.config.tune_mm_mlp_adapter = training_args.tune_mm_mlp_adapter = model_args.tune_mm_mlp_adapter + model.config.tune_mm_vision_resampler = training_args.tune_mm_vision_resampler = model_args.tune_mm_vision_resampler + if model_args.tune_mm_mlp_adapter or model_args.tune_mm_vision_resampler: + model.requires_grad_(False) + if model_args.tune_mm_mlp_adapter: + for p in model.get_model().mm_projector.parameters(): + p.requires_grad = True + if model_args.tune_mm_vision_resampler: + for p in model.get_model().vision_resampler.parameters(): + p.requires_grad = True + + model.config.freeze_mm_mlp_adapter = training_args.freeze_mm_mlp_adapter + if training_args.freeze_mm_mlp_adapter: + for p in model.get_model().mm_projector.parameters(): + p.requires_grad = False + + model.config.freeze_mm_vision_resampler = training_args.freeze_mm_vision_resampler + if training_args.freeze_mm_vision_resampler: + for p in model.get_model().vision_resampler.parameters(): + p.requires_grad = False + + model.config.unfreeze_mm_vision_tower = model_args.unfreeze_mm_vision_tower + if model_args.unfreeze_mm_vision_tower: + vision_tower.requires_grad_(True) + else: + vision_tower.requires_grad_(False) + + else: + rank0_print(f"Using mm_tunable_parts: {model_args.mm_tunable_parts}") + model.config.mm_tunable_parts = training_args.mm_tunable_parts = model_args.mm_tunable_parts + # Set the entire model to not require gradients by default + model.requires_grad_(False) + vision_tower.requires_grad_(False) + model.get_model().mm_projector.requires_grad_(False) + model.get_model().vision_resampler.requires_grad_(False) + # Parse the mm_tunable_parts to decide which parts to unfreeze + tunable_parts = model_args.mm_tunable_parts.split(",") + if "mm_mlp_adapter" in tunable_parts: + for p in model.get_model().mm_projector.parameters(): + p.requires_grad = True + if "mm_vision_resampler" in tunable_parts: + for p in model.get_model().vision_resampler.parameters(): + p.requires_grad = True + if "mm_vision_tower" in tunable_parts: + for name, param in model.named_parameters(): + if "vision_tower" in name: + param.requires_grad_(True) + if "mm_language_model" in tunable_parts: + for name, param in model.named_parameters(): + if "vision_tower" not in name and "mm_projector" not in name and "vision_resampler" not in name: + param.requires_grad_(True) + + total_params = sum(p.ds_numel if hasattr(p, "ds_numel") else p.numel() for p in model.parameters()) + trainable_params = sum(p.ds_numel if hasattr(p, "ds_numel") else p.numel() for p in model.parameters() if p.requires_grad) + rank0_print(f"Total parameters: ~{total_params/1e6:.2f} MB)") + rank0_print(f"Trainable parameters: ~{trainable_params/1e6:.2f} MB)") + if training_args.bits in [4, 8]: + model.get_model().mm_projector.to(dtype=compute_dtype, device=training_args.device) + + model.config.mm_use_im_start_end = data_args.mm_use_im_start_end = model_args.mm_use_im_start_end + model.config.mm_projector_lr = training_args.mm_projector_lr + model.config.mm_vision_tower_lr = training_args.mm_vision_tower_lr + training_args.use_im_start_end = model_args.mm_use_im_start_end + model.config.mm_use_im_patch_token = model_args.mm_use_im_patch_token + model.initialize_vision_tokenizer(model_args, tokenizer=tokenizer) + + if ref_model is not None: + ref_model.get_model().initialize_vision_modules(model_args=model_args, fsdp=training_args.fsdp) + ref_vision_tower = ref_model.get_vision_tower() + ref_vision_tower.to(dtype=torch.bfloat16 if training_args.bf16 else torch.float16, device=training_args.device) + ref_model.config.image_aspect_ratio = data_args.image_aspect_ratio + ref_model.config.image_grid_pinpoints = data_args.image_grid_pinpoints + ref_model.config.image_crop_resolution = data_args.image_crop_resolution + ref_model.config.image_split_resolution = data_args.image_split_resolution + ref_model.config.tokenizer_padding_side = tokenizer.padding_side + ref_model.config.tokenizer_model_max_length = tokenizer.model_max_length + ref_model.config.mm_use_im_start_end = data_args.mm_use_im_start_end + ref_model.config.mm_use_im_patch_token = model_args.mm_use_im_patch_token + ref_model.initialize_vision_tokenizer(model_args, tokenizer=tokenizer) + parameter_names = [n for n, _ in ref_model.named_parameters()] + for param_name in parameter_names: + param = ref_model.get_parameter(param_name) + param.requires_grad = False + ref_model.eval() + + if training_args.bits in [4, 8]: + from peft.tuners.lora import LoraLayer + + for name, module in model.named_modules(): + if isinstance(module, LoraLayer): + if training_args.bf16: + module = module.to(torch.bfloat16) + if "norm" in name: + module = module.to(torch.float32) + if "lm_head" in name or "embed_tokens" in name: + if hasattr(module, "weight"): + if training_args.bf16 and module.weight.dtype == torch.float32: + module = module.to(torch.bfloat16) + + train_dataset = make_dpo_data_module(tokenizer=tokenizer, data_args=data_args) + data_collator = DPODataCollator( + tokenizer, + label_pad_token_id=IGNORE_INDEX, + pad_token_id=tokenizer.pad_token_id, + ) + + trainer = LLaVADPOTrainer( + model, + ref_model, + args=training_args, + dpo_alpha=training_args.dpo_alpha, + beta=training_args.beta, + gamma=training_args.gamma, + train_dataset=train_dataset, + eval_dataset=None, + data_collator=data_collator, + tokenizer=tokenizer, + max_length=training_args.model_max_length, + generate_during_eval=False, # training_args.generate_during_eval, + precompute_ref_log_probs=training_args.precompute_ref_log_probs, + ) + + if list(pathlib.Path(training_args.output_dir).glob("checkpoint-*")): + trainer.train(resume_from_checkpoint=True) + else: + trainer.train() + trainer.save_state() + + model.config.use_cache = True + + if training_args.lora_enable: + state_dict = get_peft_state_maybe_zero_3(model.named_parameters(), training_args.lora_bias) + non_lora_state_dict = get_peft_state_non_lora_maybe_zero_3(model.named_parameters()) + if training_args.local_rank == 0 or training_args.local_rank == -1: + if hasattr(model, "config"): + model.config.save_pretrained(training_args.output_dir) + if hasattr(model, "generation_config"): + model.generation_config.save_pretrained(training_args.output_dir) + model.save_pretrained(training_args.output_dir, state_dict=state_dict) + torch.save(non_lora_state_dict, os.path.join(training_args.output_dir, "non_lora_trainables.bin")) + else: + safe_save_model_for_hf_trainer(trainer=trainer, output_dir=training_args.output_dir) + + rank0_print(f"Model saved to {training_args.output_dir}") + + +if __name__ == "__main__": + train() diff --git a/llava/train/train_mem.py b/llava/train/train_mem.py new file mode 100644 index 0000000..6135ca4 --- /dev/null +++ b/llava/train/train_mem.py @@ -0,0 +1,4 @@ +from llava.train.train import train + +if __name__ == "__main__": + train() diff --git a/llava/utils.py b/llava/utils.py new file mode 100644 index 0000000..1e004c3 --- /dev/null +++ b/llava/utils.py @@ -0,0 +1,198 @@ +import datetime +import logging +import logging.handlers +import os +import sys +import numpy as np + +import requests + +from llava.constants import LOGDIR + +server_error_msg = "**NETWORK ERROR DUE TO HIGH TRAFFIC. PLEASE REGENERATE OR REFRESH THIS PAGE.**" +moderation_msg = "I am sorry. Your input may violate our content moderation guidelines. Please avoid using harmful or offensive content." + +handler = None + +import torch.distributed as dist + +try: + import av + from decord import VideoReader, cpu +except ImportError: + print("Please install pyav to use video processing functions.") + +def process_video_with_decord(video_file, data_args): + vr = VideoReader(video_file, ctx=cpu(0), num_threads=1) + total_frame_num = len(vr) + video_time = total_frame_num / vr.get_avg_fps() + avg_fps = round(vr.get_avg_fps() / data_args.video_fps) + frame_idx = [i for i in range(0, total_frame_num, avg_fps)] + frame_time = [i/avg_fps for i in frame_idx] + + + if data_args.frames_upbound > 0: + if len(frame_idx) > data_args.frames_upbound or data_args.force_sample: + uniform_sampled_frames = np.linspace(0, total_frame_num - 1, data_args.frames_upbound, dtype=int) + frame_idx = uniform_sampled_frames.tolist() + frame_time = [i/vr.get_avg_fps() for i in frame_idx] + + video = vr.get_batch(frame_idx).asnumpy() + frame_time = ",".join([f"{i:.2f}s" for i in frame_time]) + + num_frames_to_sample = num_frames = len(frame_idx) + # https://github.com/dmlc/decord/issues/208 + vr.seek(0) + return video, video_time, frame_time, num_frames_to_sample + +def process_video_with_pyav(video_file, data_args): + container = av.open(video_file) + # !!! This is the only difference. Using auto threading + container.streams.video[0].thread_type = "AUTO" + + video_frames = [] + for packet in container.demux(): + if packet.stream.type == 'video': + for frame in packet.decode(): + video_frames.append(frame) + total_frame_num = len(video_frames) + video_time = video_frames[-1].time + avg_fps = round(total_frame_num / video_time / data_args.video_fps) + frame_idx = [i for i in range(0, total_frame_num, avg_fps)] + + if data_args.frames_upbound > 0: + if len(frame_idx) > data_args.frames_upbound: + uniform_sampled_frames = np.linspace(0, total_frame_num - 1, data_args.frames_upbound, dtype=int) + frame_idx = uniform_sampled_frames.tolist() + + + frames = [video_frames[i] for i in frame_idx] + return np.stack([x.to_ndarray(format="rgb24") for x in frames]) + + +def rank0_print(*args): + if dist.is_initialized(): + if dist.get_rank() == 0: + print(f"Rank {dist.get_rank()}: ", *args) + else: + print(*args) + + +def rank_print(*args): + if dist.is_initialized(): + print(f"Rank {dist.get_rank()}: ", *args) + else: + print(*args) + +def build_logger(logger_name, logger_filename): + global handler + + formatter = logging.Formatter( + fmt="%(asctime)s | %(levelname)s | %(name)s | %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + + # Set the format of root handlers + if not logging.getLogger().handlers: + logging.basicConfig(level=logging.INFO) + logging.getLogger().handlers[0].setFormatter(formatter) + + # Redirect stdout and stderr to loggers + stdout_logger = logging.getLogger("stdout") + stdout_logger.setLevel(logging.INFO) + sl = StreamToLogger(stdout_logger, logging.INFO) + sys.stdout = sl + + stderr_logger = logging.getLogger("stderr") + stderr_logger.setLevel(logging.ERROR) + sl = StreamToLogger(stderr_logger, logging.ERROR) + sys.stderr = sl + + # Get logger + logger = logging.getLogger(logger_name) + logger.setLevel(logging.INFO) + + # Add a file handler for all loggers + if handler is None: + os.makedirs(LOGDIR, exist_ok=True) + filename = os.path.join(LOGDIR, logger_filename) + handler = logging.handlers.TimedRotatingFileHandler(filename, when="D", utc=True) + handler.setFormatter(formatter) + + for name, item in logging.root.manager.loggerDict.items(): + if isinstance(item, logging.Logger): + item.addHandler(handler) + + return logger + + +class StreamToLogger(object): + """ + Fake file-like stream object that redirects writes to a logger instance. + """ + + def __init__(self, logger, log_level=logging.INFO): + self.terminal = sys.stdout + self.logger = logger + self.log_level = log_level + self.linebuf = "" + + def __getattr__(self, attr): + return getattr(self.terminal, attr) + + def write(self, buf): + temp_linebuf = self.linebuf + buf + self.linebuf = "" + for line in temp_linebuf.splitlines(True): + # From the io.TextIOWrapper docs: + # On output, if newline is None, any '\n' characters written + # are translated to the system default line separator. + # By default sys.stdout.write() expects '\n' newlines and then + # translates them so this is still cross platform. + if line[-1] == "\n": + self.logger.log(self.log_level, line.rstrip()) + else: + self.linebuf += line + + def flush(self): + if self.linebuf != "": + self.logger.log(self.log_level, self.linebuf.rstrip()) + self.linebuf = "" + + +def disable_torch_init(): + """ + Disable the redundant torch default initialization to accelerate model creation. + """ + import torch + + setattr(torch.nn.Linear, "reset_parameters", lambda self: None) + setattr(torch.nn.LayerNorm, "reset_parameters", lambda self: None) + + +def violates_moderation(text): + """ + Check whether the text violates OpenAI moderation API. + """ + url = "https://api.openai.com/v1/moderations" + headers = {"Content-Type": "application/json", "Authorization": "Bearer " + os.environ["OPENAI_API_KEY"]} + text = text.replace("\n", "") + data = "{" + '"input": ' + f'"{text}"' + "}" + data = data.encode("utf-8") + try: + ret = requests.post(url, headers=headers, data=data, timeout=5) + flagged = ret.json()["results"][0]["flagged"] + except requests.exceptions.RequestException as e: + print(f"######################### Moderation Error: {e} #########################") + flagged = False + except KeyError as e: + print(f"######################### Moderation Error: {e} #########################") + flagged = False + + return flagged + + +def pretty_print_semaphore(semaphore): + if semaphore is None: + return "None" + return f"Semaphore(value={semaphore._value}, locked={semaphore.locked()})" diff --git a/playground/2d_hist.py b/playground/2d_hist.py new file mode 100644 index 0000000..3592348 --- /dev/null +++ b/playground/2d_hist.py @@ -0,0 +1,132 @@ +import json +import os +from PIL import Image +from tqdm import tqdm +import matplotlib.pyplot as plt +import numpy as np +from multiprocessing import Pool +import functools +import argparse + + +def load_data(json_path): + with open(json_path, "r") as f: + return json.load(f) + + +def filter_data(data): + filtered_data = [item for item in data if "image" in item] + return filtered_data + + +def calculate_image_dimension(image_path, images_folder): + full_path = os.path.join(images_folder, image_path) + try: + with Image.open(full_path) as img: + width, height = img.size + return width, height + except Exception as e: + print(f"Error opening {full_path}: {e}") + return None, None + + +def calculate_image_dimensions_multiprocess(filtered_data, images_folder, num_processes=256): + image_paths = [] + for item in filtered_data: + if isinstance(item["image"], list): + image_paths.extend(item["image"]) + else: + image_paths.append(item["image"]) + + with Pool(num_processes) as p: + dimensions = list( + tqdm( + p.imap(functools.partial(calculate_image_dimension, images_folder=images_folder), image_paths), + total=len(image_paths), + desc="Calculating image dimensions", + ) + ) + widths, heights = zip(*[dim for dim in dimensions if dim[0] is not None]) + return list(widths), list(heights) + + +def tokenize(text): + return text.split() + + +def calculate_tokenized_lengths(data): + lengths = [] + for item in tqdm(data, desc="Tokenizing conversations"): + for conversation in item["conversations"]: + tokenized_value = tokenize(conversation["value"]) + lengths.append(len(tokenized_value)) + return lengths + + +def main(): + parser = argparse.ArgumentParser(description="Process data for LLaVA_Next project.") + parser.add_argument( + "--json_path", + type=str, + help="Path to the JSON file containing data.", + default="/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/llava_ofa_DEMON-FULL.json", + ) + parser.add_argument( + "--images_folder", + type=str, + default="/mnt/bn/vl-research/data/llava_data", + help="Path to the folder containing images.", + ) + args = parser.parse_args() + + llava_instruct_name = os.path.basename(args.json_path).replace(".json", "") + images_folder = args.images_folder + + data = load_data(args.json_path) + filtered_data = filter_data(data) + + print(f"Total data items: {len(data)}, Filtered data items: {len(filtered_data)}") + widths, heights = calculate_image_dimensions_multiprocess(filtered_data, images_folder) + max_width, max_height = max(widths), max(heights) + print(f"Max width: {max_width}, Max height: {max_height}") + + tokenized_lengths = calculate_tokenized_lengths(filtered_data) + + fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(24, 12)) + + # Plot 2D histogram + widths_bins = [min(widths), max(widths) + 1] if min(widths) == max(widths) else np.arange(min(widths), max(widths) + 100, 100) + heights_bins = [min(heights), max(heights) + 1] if min(heights) == max(heights) else np.arange(min(heights), max(heights) + 100, 100) + + h, xedges, yedges, image = ax1.hist2d(widths, heights, bins=[widths_bins, heights_bins], cmap=plt.cm.jet, density=True) + fig.colorbar(image, ax=ax1) + ax1.set_xlabel("Width") + ax1.set_ylabel("Height") + ax1.set_title( + f"dist_{llava_instruct_name}_2d_w_h\nMax width: {max(widths)}, Max height: {max(heights)}", + fontsize=10, + ) + + # Plot histogram + hist, bin_edges = np.histogram(tokenized_lengths, bins=np.arange(0, max(tokenized_lengths) + 10, 10)) + bins = np.arange(0, max(tokenized_lengths) + 10, 10) + ax2.bar(bin_edges[:-1], hist, width=7, edgecolor="black", log=True) + + # Display every nth label on the x-axis + n = 8 # Adjust this value to control the number of labels displayed + ticks = bins[::n] + tick_labels = [int(tick) for tick in ticks] + ax2.set_xticks(ticks) + ax2.set_xticklabels(tick_labels, rotation=90, fontsize=8) + + ax2.set_xlim(min(bin_edges), max(bin_edges)) + ax2.set_xlabel("Tokenized Length") + ax2.set_ylabel("Count (log scale)") + ax2.set_title(f"dist_{llava_instruct_name}_tokenized_length", fontsize=8) + + plt.tight_layout() + plt.savefig(f"./dist_{llava_instruct_name}_combined.png") + + +if __name__ == "__main__": + main() diff --git a/playground/data_checker.py b/playground/data_checker.py new file mode 100644 index 0000000..9d145ca --- /dev/null +++ b/playground/data_checker.py @@ -0,0 +1,364 @@ +import json +import os +from tqdm import tqdm +from multiprocessing import Pool, cpu_count +import yaml + + +class DataProcessor: + def __init__(self, file_path, image_root, video_root): + self.file_path = file_path + self.image_root = image_root + self.data = None + self.video_root = video_root + self.load_data() + + def load_data(self): + if self.file_path.endswith(".json"): + with open(self.file_path, "r") as f: + self.data = json.load(f) + elif self.file_path.endswith(".yaml"): + with open(self.file_path, "r") as f: + self.data = yaml.safe_load(f) + elif self.file_path.endswith(".jsonl"): + with open(self.file_path, "r") as f: + self.data = [json.loads(line) for line in f.readlines()] + else: + raise ValueError("Unsupported file format") + + def load_json_data(self, json_path): + if json_path.endswith(".jsonl"): + cur_data_dict = [] + with open(json_path, "r") as json_file: + for line in json_file: + cur_data_dict.append(json.loads(line.strip())) + return cur_data_dict + elif json_path.endswith(".json"): + with open(json_path, "r") as f: + return json.load(f) + else: + raise ValueError("Unsupported file format") + + def check_image_existence(self, data): + if "image" in data: + if type(data["image"]) == list: + images = data["image"] + else: + images = [data["image"]] + + for image in images: + full_image_path = os.path.join(self.image_root, image) + if not os.path.exists(full_image_path): + print(f"WARNING!!! {full_image_path} not exists !!!") + + if "video" in data: + full_video_path = os.path.join(self.video_root, data["video"]) + if not os.path.exists(full_video_path): + print(f"WARNING!!! {full_video_path} not exists !!!") + + # if data["conversations"][0]["value"].count("") > 1: + # print(f"WARNING!!! {data['conversations'][0]['value']} has more than one !!!") + + def check_item_structure(self, item): + if not all(key in item for key in ["conversations"]): + print(f"WARNING!!! Item {item.get('id', 'unknown')} is missing required fields!") + return False + + conversations = item["conversations"] + if not isinstance(conversations, list) or len(conversations) < 2 or len(conversations) % 2 != 0: + print(f"WARNING!!! Item {item['id']} has invalid conversations structure!") + return False + + for i, conv in enumerate(conversations): + if not all(key in conv for key in ["from", "value"]): + print(f"WARNING!!! Item {item['id']} has invalid conversation format!") + return False + + expected_from = "human" if i % 2 == 0 else "gpt" + if conv["from"] != expected_from: + print(f"WARNING!!! Item {item['id']} has incorrect conversation order!") + return False + + return True + + def check_image_and_structure(self, item): + if not self.check_item_structure(item): + return + + # self.check_image_existence(item) + + def process_images(self): + if isinstance(self.data, list): + args = [d for d in self.data] + with Pool(processes=cpu_count()) as pool: + list(tqdm(pool.imap(self.check_image_and_structure, args), total=len(self.data))) + elif isinstance(self.data, dict): + for d in self.data["datasets"]: + dd_json_path = d["json_path"] + data = self.load_json_data(dd_json_path) + args = [d for d in data] + with Pool(processes=cpu_count()) as pool: + list(tqdm(pool.imap(self.check_image_and_structure, args), total=len(data), desc=f"Processing {dd_json_path}")) + + def count_items(self): + if isinstance(self.data, list): # Assuming JSON data loaded directly + return len(self.data) + elif isinstance(self.data, dict): # Assuming YAML data loaded + total_items_count = 0 + for d in self.data["datasets"]: + dd_json_path = d["json_path"] + data = self.load_json_data(dd_json_path) + current_items_count = len(data) + + sampling_strategy = d["sampling_strategy"] + try: + if sampling_strategy != "all": + percentage = float(sampling_strategy.split(":")[-1].replace("%", "")) / 100.0 + else: + percentage = 1.0 + except Exception as e: + print(f"Error: {e}") + percentage = 1.0 + + sampling_count = int(current_items_count * percentage) + total_items_count += sampling_count + print(f"{dd_json_path}: {sampling_count}") + return total_items_count + + def stat_data(self): + if isinstance(self.data, dict): + cur_lens_list = [] + single_image_count = 0 + multiple_image_count = 0 + video_count = 0 + total_count = 0 + text_count = 0 + max_tokens_item = None + max_tokens = 0 + + for d in self.data["datasets"]: + dd_json_path = d["json_path"] + data = self.load_json_data(dd_json_path) + sampling_strategy = d["sampling_strategy"] + + try: + if sampling_strategy != "all": + percentage = float(sampling_strategy.split(":")[-1].replace("%", "")) / 100.0 + else: + percentage = 1.0 + except Exception as e: + print(f"Error parsing sampling strategy: {e}") + percentage = 1.0 + + sampled_count = int(len(data) * percentage) + print(f"{dd_json_path}: {sampled_count} (sampled from {len(data)})") + + for item in data[:sampled_count]: + conversations = item["conversations"] + cur_len = sum([len(conv["value"].split()) for conv in conversations]) + cur_lens_list.append(cur_len) + + if cur_len > max_tokens: + max_tokens = cur_len + max_tokens_item = item + + total_count += 1 + if "image" in item: + if isinstance(item["image"], list): + if len(item["image"]) > 1: + multiple_image_count += 1 + else: + single_image_count += 1 + else: + single_image_count += 1 + elif "video" in item: + video_count += 1 + else: + text_count += 1 + + print(f"Max length: {max(cur_lens_list)}, Min length: {min(cur_lens_list)}, Average length: {sum(cur_lens_list) / len(cur_lens_list)}") + print(f"Total items: {total_count}") + print(f"Text items: {text_count} ({text_count/total_count*100:.2f}%)") + print(f"Single image items: {single_image_count} ({single_image_count/total_count*100:.2f}%)") + print(f"Multiple image items: {multiple_image_count} ({multiple_image_count/total_count*100:.2f}%)") + print(f"Video items: {video_count} ({video_count/total_count*100:.2f}%)") + + print("\nItem with the largest number of tokens:") + print(f"Token count: {max_tokens}") + print("Item content:") + print(json.dumps(max_tokens_item, indent=2)) + + def filter_data(self): + if isinstance(self.data, dict): + for d in self.data["datasets"]: + dd_json_path = d["json_path"] + print(f"Processing {dd_json_path}") + data = self.load_json_data(dd_json_path) + + filtered_data = [] + mismatch_data = [] + mismatch_flag = False + for item in data: + try: + if "image" in item: + num_image = len(item["image"]) if isinstance(item["image"], list) else 1 + else: + num_image = 0 + + if "video" in item: + num_video = len(item["video"]) if isinstance(item["video"], list) else 1 + else: + num_video = 0 + + num_visuals = num_image + num_video + conv_text = "" + for conv in item["conversations"]: + conv_text += conv["value"] + + num_img_token_appearance = conv_text.count("") + if len(conv_text) == 0: + print(f"Conversation text is empty for {item}") + + if num_img_token_appearance == num_visuals or num_img_token_appearance < num_visuals and len(conv_text) > 0: + filtered_data.append(item) + elif num_img_token_appearance > num_visuals: + item["num_img_token_appearance"] = num_img_token_appearance + item["num_visuals"] = num_visuals + mismatch_data.append(item) + + if not mismatch_flag: + print(f"Data mismatch for {item}") + + mismatch_flag = True + except Exception as e: + print(f"Error: {e}") + print() + + if mismatch_flag: + print(f"Data mismatch for {dd_json_path}") + + if len(filtered_data) < len(data): + saving_dd_json_path = dd_json_path.replace(".jsonl", f"fltd_{len(filtered_data)}.json").replace(".json", f"fltd_{len(filtered_data)}.json") + with open(saving_dd_json_path, "w") as f: + json.dump(filtered_data, f, indent=2) + print(f"Filtered data count: {len(filtered_data)}") + else: + pass + + def stat_and_filter_data(self, threshold): + if isinstance(self.data, dict): + cur_lens_list = [] + single_image_count = 0 + multiple_image_count = 0 + video_count = 0 + total_count = 0 + text_count = 0 + + for d in self.data["datasets"]: + dd_json_path = d["json_path"] + data = self.load_json_data(dd_json_path) + sampling_strategy = d["sampling_strategy"] + filtered_data = [] + + try: + if sampling_strategy != "all": + percentage = float(sampling_strategy.split(":")[-1].replace("%", "")) / 100.0 + else: + percentage = 1.0 + except Exception as e: + print(f"Error parsing sampling strategy: {e}") + percentage = 1.0 + + sampled_count = int(len(data) * percentage) + print(f"{dd_json_path}: {sampled_count} (sampled from {len(data)})") + + save_flag = False + for item in data: + total_count += 1 + conversations = item["conversations"] + filtered_conversations = [] + current_token_count = 0 + + for i in range(0, len(conversations), 2): + if i + 1 < len(conversations): + human_conv = conversations[i] + gpt_conv = conversations[i + 1] + pair_tokens = len(human_conv["value"].split()) + len(gpt_conv["value"].split()) + + if current_token_count + pair_tokens <= threshold: + filtered_conversations.extend([human_conv, gpt_conv]) + current_token_count += pair_tokens + else: + save_flag = True + break + + if filtered_conversations: + item["conversations"] = filtered_conversations + cur_len = sum([len(conv["value"].split()) for conv in filtered_conversations]) + cur_lens_list.append(cur_len) + filtered_data.append(item) + + if "image" in item: + if isinstance(item["image"], list): + if len(item["image"]) > 1: + multiple_image_count += 1 + else: + single_image_count += 1 + else: + single_image_count += 1 + elif "video" in item: + video_count += 1 + else: + text_count += 1 + + # Save filtered data for each dataset + if filtered_data and save_flag: + if dd_json_path.endswith(".jsonl"): + output_file = dd_json_path.replace(".jsonl", f"_filtered_{threshold}tokens_{len(filtered_data)}.jsonl") + with open(output_file, "w") as f: + for item in filtered_data: + f.write(json.dumps(item) + "\n") + else: + output_file = dd_json_path.replace(".json", f"_filtered_{threshold}tokens_{len(filtered_data)}.json") + with open(output_file, "w") as f: + json.dump(filtered_data, f, indent=2) + print(f"Filtered data for {dd_json_path} saved to: {output_file}") + + print(f"Max length: {max(cur_lens_list)}, Min length: {min(cur_lens_list)}, Average length: {sum(cur_lens_list) / len(cur_lens_list)}") + print(f"Total items: {total_count}") + print(f"Text items: {text_count} ({text_count/total_count*100:.2f}%)") + print(f"Single image items: {single_image_count} ({single_image_count/total_count*100:.2f}%)") + print(f"Multiple image items: {multiple_image_count} ({multiple_image_count/total_count*100:.2f}%)") + print(f"Video items: {video_count} ({video_count/total_count*100:.2f}%)") + + +def main(file_path, image_root, operation, video_root, threshold=None): + processor = DataProcessor(file_path, image_root, video_root) + if operation == "check": + processor.process_images() + elif operation == "count": + total_items = processor.count_items() + print(f"Total items: {total_items}") + elif operation == "filter": + processor.filter_data() + elif operation == "stat": + processor.stat_data() + elif operation == "stat_and_filter": + if threshold is None: + raise ValueError("Threshold must be provided for stat_and_filter operation") + processor.stat_and_filter_data(threshold) + else: + raise ValueError("Unsupported operation") + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument("--file_path", type=str, default="/mnt/bn/vl-research/workspace/boli01/projects/LLaVA_Next/scripts/i18n/scale_llms/next_continual.yaml") + parser.add_argument("--image_root", type=str, default="/mnt/bn/vl-research/data/llava_data") + parser.add_argument("--video_root", type=str, default="/mnt/bn/vl-research/data/llava_video") + parser.add_argument("--operation", type=str, default="filter") + parser.add_argument("--threshold", type=int, default=None, help="Threshold for stat_and_filter operation") + args = parser.parse_args() + main(args.file_path, args.image_root, args.operation, args.video_root, args.threshold) diff --git a/playground/demo/video_demo.py b/playground/demo/video_demo.py new file mode 100644 index 0000000..d93f658 --- /dev/null +++ b/playground/demo/video_demo.py @@ -0,0 +1,335 @@ +import argparse +import torch + +from llava.constants import IMAGE_TOKEN_INDEX, DEFAULT_IMAGE_TOKEN, DEFAULT_IM_START_TOKEN, DEFAULT_IM_END_TOKEN +from llava.conversation import conv_templates, SeparatorStyle +from llava.model.builder import load_pretrained_model +from llava.utils import disable_torch_init +from llava.mm_utils import process_anyres_image,tokenizer_image_token, get_model_name_from_path, KeywordsStoppingCriteria + +import json +import os +import math +from tqdm import tqdm +from decord import VideoReader, cpu + +from transformers import AutoConfig + +import cv2 +import base64 +import openai + +from PIL import Image + + + +import numpy as np + +def split_list(lst, n): + """Split a list into n (roughly) equal-sized chunks""" + chunk_size = math.ceil(len(lst) / n) # integer division + return [lst[i : i + chunk_size] for i in range(0, len(lst), chunk_size)] + + +def get_chunk(lst, n, k): + chunks = split_list(lst, n) + return chunks[k] + + +def parse_args(): + """ + Parse command-line arguments. + """ + parser = argparse.ArgumentParser() + + # Define the command-line arguments + parser.add_argument("--video_path", help="Path to the video files.", required=True) + parser.add_argument("--output_dir", help="Directory to save the model results JSON.", required=True) + parser.add_argument("--output_name", help="Name of the file for storing results JSON.", required=True) + parser.add_argument("--model-path", type=str, default="facebook/opt-350m") + parser.add_argument("--model-base", type=str, default=None) + parser.add_argument("--conv-mode", type=str, default=None) + parser.add_argument("--chunk-idx", type=int, default=0) + parser.add_argument("--mm_resampler_type", type=str, default="spatial_pool") + parser.add_argument("--mm_spatial_pool_stride", type=int, default=4) + parser.add_argument("--mm_spatial_pool_out_channels", type=int, default=1024) + parser.add_argument("--mm_spatial_pool_mode", type=str, default="average") + parser.add_argument("--image_aspect_ratio", type=str, default="anyres") + parser.add_argument("--image_grid_pinpoints", type=str, default="[(224, 448), (224, 672), (224, 896), (448, 448), (448, 224), (672, 224), (896, 224)]") + parser.add_argument("--mm_patch_merge_type", type=str, default="spatial_unpad") + parser.add_argument("--overwrite", type=lambda x: (str(x).lower() == 'true'), default=True) + parser.add_argument("--for_get_frames_num", type=int, default=4) + parser.add_argument("--load_8bit", type=lambda x: (str(x).lower() == 'true'), default=False) + parser.add_argument("--prompt", type=str, default=None) + parser.add_argument("--api_key", type=str, help="OpenAI API key") + parser.add_argument("--mm_newline_position", type=str, default="no_token") + parser.add_argument("--force_sample", type=lambda x: (str(x).lower() == 'true'), default=False) + parser.add_argument("--add_time_instruction", type=str, default=False) + return parser.parse_args() + +def load_video(video_path,args): + if args.for_get_frames_num == 0: + return np.zeros((1, 336, 336, 3)) + vr = VideoReader(video_path, ctx=cpu(0),num_threads=1) + total_frame_num = len(vr) + video_time = total_frame_num / vr.get_avg_fps() + fps = round(vr.get_avg_fps()) + frame_idx = [i for i in range(0, len(vr), fps)] + frame_time = [i/fps for i in frame_idx] + if len(frame_idx) > args.for_get_frames_num or args.force_sample: + sample_fps = args.for_get_frames_num + uniform_sampled_frames = np.linspace(0, total_frame_num - 1, sample_fps, dtype=int) + frame_idx = uniform_sampled_frames.tolist() + frame_time = [i/vr.get_avg_fps() for i in frame_idx] + frame_time = ",".join([f"{i:.2f}s" for i in frame_time]) + spare_frames = vr.get_batch(frame_idx).asnumpy() + # import pdb;pdb.set_trace() + + return spare_frames,frame_time,video_time + + + + +def load_video_base64(path): + video = cv2.VideoCapture(path) + + base64Frames = [] + while video.isOpened(): + success, frame = video.read() + if not success: + break + _, buffer = cv2.imencode(".jpg", frame) + base64Frames.append(base64.b64encode(buffer).decode("utf-8")) + + video.release() + # print(len(base64Frames), "frames read.") + return base64Frames + + +def run_inference(args): + """ + Run inference on ActivityNet QA DataSet using the Video-ChatGPT model. + + Args: + args: Command-line arguments. + """ + # Initialize the model + if "gpt4v" != args.model_path: + model_name = get_model_name_from_path(args.model_path) + # Set model configuration parameters if they exist + if args.overwrite == True: + overwrite_config = {} + overwrite_config["mm_spatial_pool_mode"] = args.mm_spatial_pool_mode + overwrite_config["mm_spatial_pool_stride"] = args.mm_spatial_pool_stride + overwrite_config["mm_newline_position"] = args.mm_newline_position + + cfg_pretrained = AutoConfig.from_pretrained(args.model_path) + + # import pdb;pdb.set_trace() + if "qwen" not in args.model_path.lower(): + if "224" in cfg_pretrained.mm_vision_tower: + # suppose the length of text tokens is around 1000, from bo's report + least_token_number = args.for_get_frames_num*(16//args.mm_spatial_pool_stride)**2 + 1000 + else: + least_token_number = args.for_get_frames_num*(24//args.mm_spatial_pool_stride)**2 + 1000 + + scaling_factor = math.ceil(least_token_number/4096) + if scaling_factor >= 2: + if "vicuna" in cfg_pretrained._name_or_path.lower(): + print(float(scaling_factor)) + overwrite_config["rope_scaling"] = {"factor": float(scaling_factor), "type": "linear"} + overwrite_config["max_sequence_length"] = 4096 * scaling_factor + overwrite_config["tokenizer_model_max_length"] = 4096 * scaling_factor + + tokenizer, model, image_processor, context_len = load_pretrained_model(args.model_path, args.model_base, model_name, load_8bit=args.load_8bit, overwrite_config=overwrite_config) + else: + tokenizer, model, image_processor, context_len = load_pretrained_model(args.model_path, args.model_base, model_name) + else: + pass + + # import pdb;pdb.set_trace() + if getattr(model.config, "force_sample", None) is not None: + args.force_sample = model.config.force_sample + else: + args.force_sample = False + + # import pdb;pdb.set_trace() + + if getattr(model.config, "add_time_instruction", None) is not None: + args.add_time_instruction = model.config.add_time_instruction + else: + args.add_time_instruction = False + + # Create the output directory if it doesn't exist + if not os.path.exists(args.output_dir): + os.makedirs(args.output_dir) + + output_name = args.output_name + answers_file = os.path.join(args.output_dir, f"{output_name}.json") + ans_file = open(answers_file, "w") + + video_path = args.video_path + + all_video_pathes = [] + + # Check if the video_path is a directory or a file + if os.path.isdir(video_path): + # If it's a directory, loop over all files in the directory + for filename in os.listdir(video_path): + # Load the video file + cur_video_path = os.path.join(video_path, f"{filename}") + all_video_pathes.append(os.path.join(video_path, cur_video_path)) + else: + # If it's a file, just process the video + all_video_pathes.append(video_path) + + # import pdb;pdb.set_trace() + for video_path in all_video_pathes: + + sample_set = {} + question = args.prompt + sample_set["Q"] = question + sample_set["video_name"] = video_path + + + # Check if the video exists + if os.path.exists(video_path): + if "gpt4v" != args.model_path: + video,frame_time,video_time = load_video(video_path, args) + video = image_processor.preprocess(video, return_tensors="pt")["pixel_values"].half().cuda() + video = [video] + else: + spare_frames,frame_time,video_time = load_video_base64(video_path) + interval = int(len(video) / args.for_get_frames_num) + + # try: + # Run inference on the video and add the output to the list + if "gpt4v" != args.model_path: + qs = question + if args.add_time_instruction: + time_instruciton = f"The video lasts for {video_time:.2f} seconds, and {len(video[0])} frames are uniformly sampled from it. These frames are located at {frame_time}.Please answer the following questions related to this video." + qs = f'{time_instruciton}\n{qs}' + if model.config.mm_use_im_start_end: + qs = DEFAULT_IM_START_TOKEN + DEFAULT_IMAGE_TOKEN + DEFAULT_IM_END_TOKEN + "\n" + qs + else: + qs = DEFAULT_IMAGE_TOKEN + "\n" + qs + + conv = conv_templates[args.conv_mode].copy() + conv.append_message(conv.roles[0], qs) + conv.append_message(conv.roles[1], None) + prompt = conv.get_prompt() + + input_ids = tokenizer_image_token(prompt, tokenizer, IMAGE_TOKEN_INDEX, return_tensors="pt").unsqueeze(0).cuda() + if tokenizer.pad_token_id is None: + if "qwen" in tokenizer.name_or_path.lower(): + print("Setting pad token to bos token for qwen model.") + tokenizer.pad_token_id = 151643 + + attention_masks = input_ids.ne(tokenizer.pad_token_id).long().cuda() + + stop_str = conv.sep if conv.sep_style != SeparatorStyle.TWO else conv.sep2 + keywords = [stop_str] + stopping_criteria = KeywordsStoppingCriteria(keywords, tokenizer, input_ids) + + cur_prompt = question + else: + prompt = question + + system_error = "" + + if "gpt4v" != args.model_path: + + + with torch.inference_mode(): + # model.update_prompt([[cur_prompt]]) + # import pdb;pdb.set_trace() + # output_ids = model.generate(inputs=input_ids, images=video, attention_mask=attention_masks, modalities="video", do_sample=True, temperature=0.2, max_new_tokens=1024, use_cache=True, stopping_criteria=[stopping_criteria]) + if "mistral" not in cfg_pretrained._name_or_path.lower(): + output_ids = model.generate(inputs=input_ids, images=video, attention_mask=attention_masks, modalities="video", do_sample=False, temperature=0.0, max_new_tokens=1024, top_p=0.1,num_beams=1,use_cache=True, stopping_criteria=[stopping_criteria]) + # output_ids = model.generate(inputs=input_ids, images=video, attention_mask=attention_masks, modalities="video", do_sample=True, temperature=0.2, max_new_tokens=1024, use_cache=True, stopping_criteria=[stopping_criteria]) + else: + output_ids = model.generate(inputs=input_ids, images=video, attention_mask=attention_masks, modalities="video", do_sample=False, temperature=0.0, max_new_tokens=1024, top_p=0.1, num_beams=1, use_cache=True) + # output_ids = model.generate(inputs=input_ids, images=video, attention_mask=attention_masks, modalities="video", do_sample=True, temperature=0.2, max_new_tokens=1024, use_cache=True) + else: + openai.api_key = args.api_key # Your API key here + + max_num_retries = 0 + retry = 5 + PROMPT_MESSAGES = [ + { + "role": "user", + "content": [ + f"These are frames from a video that I want to upload. Answer me one question of this video: {prompt}", + *map(lambda x: {"image": x, "resize": 336}, video[0::interval]), + ], + }, + ] + params = { + "model": "gpt-4-vision-preview", #gpt-4-1106-vision-preview + "messages": PROMPT_MESSAGES, + "max_tokens": 1024, + } + sucess_flag=False + while max_num_retries < retry: + try: + result = openai.ChatCompletion.create(**params) + outputs = result.choices[0].message.content + sucess_flag = True + break + except Exception as inst : + if 'error' in dir(inst): + # import pdb;pdb.set_trace() + if inst.error.code == 'rate_limit_exceeded': + if "TPM" in inst.error.message: + time.sleep(30) + continue + else: + import pdb;pdb.set_trace() + elif inst.error.code == 'insufficient_quota': + print(f'insufficient_quota key') + exit() + elif inst.error.code == 'content_policy_violation': + print(f'content_policy_violation') + system_error = "content_policy_violation" + + break + print('Find error message in response: ',str(inst.error.message), 'error code: ', str(inst.error.code)) + + continue + if not sucess_flag: + print(f'Calling OpenAI failed after retrying for {max_num_retries} times. Check the logs for details.') + exit() + + if "gpt4v" != args.model_path: + outputs = tokenizer.batch_decode(output_ids, skip_special_tokens=True)[0].strip() + else: + print(len(video[0::interval])) + + print(f"Question: {prompt}\n") + print(f"Response: {outputs}\n") + + if "gpt4v" == args.model_path: + if system_error == 'content_policy_violation': + continue + elif system_error == "": + continue + else: + import pdb;pdb.set_trace() + + # import pdb;pdb.set_trace() + if "mistral" not in cfg_pretrained._name_or_path.lower(): + if outputs.endswith(stop_str): + outputs = outputs[: -len(stop_str)] + + outputs = outputs.strip() + + sample_set["pred"] = outputs + ans_file.write(json.dumps(sample_set, ensure_ascii=False) + "\n") + ans_file.flush() + + ans_file.close() + + +if __name__ == "__main__": + args = parse_args() + run_inference(args) \ No newline at end of file diff --git a/playground/demo/xU25MMA2N4aVtYay.mp4 b/playground/demo/xU25MMA2N4aVtYay.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..5bc7f52cea706bf268b4cfcacbcd58f4bf9b24df GIT binary patch literal 271700 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

-i71Kca{uE~7S)tv;$`?ic+Gg=~c3s~-Ik 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%0Dv7PDe^g#FLeQa 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

!&5+7{jhj?(#m*=7QIx64ZGom6VT_Ued~ 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%0D9sx22l~vtl#w-@M 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

RWeG6ZHVf!jbdSBFXuWQ+V9ab%0TWvLLvf=o zuQUFYDt||$lgBRs@KdSyjv;o(Q#J7DdOSdwoOcgl>yB}H);hz6)ws;X`eePFxb~*O zvGQ^X!m^Z}^_%$aWcJIGcng-Xu298 z3LwD%sk8Jpm>Ag2Zr7VGP7B_33z3t8yO=$(_McO%QQ2Mbh9)dDP8gA%2 zK$2#fs~xo3lhS2%Mj6lt2Rh!;gOuqa>ZbB5`Ftu`M{ec&5M))%qU}cDe^RAiklkQe z#+6b{`92EBs1JX~bnS|N76h|2r7w*QOWF{bdW!ARW>uLk@0!V>PiAdm5UeC9OTDM! zsxe8ZH2#fPj-kBjdXf~mREcci_})ioEe5BPhbeH%-69 z(`kR_oA~Z&mC236u}Sb5RwW8QbIh^c-M+3Niinxg2r0!Cuny_{;VR^JbME9O+ZWFC zk1`>n!B6X-A2aFpVAEL_>wl|LJ%Ma7Qe@Fya&#yfoM zh08V#GO32=D+U~+X!oSKiT2osJ=Zui$cY!AFca1Ll+5=a*RGa%L&1-&T4uc2jW-u zwL9q#c9hrd!34vFA8!fFgD~~ICnN8XdBco3$lV5c6)mpRV86rA^`Jb*1jzFOx;ob7 z3+mVumWW;^3ePOOq?|H@RWAx}r23+E)9prDvvpj?$Lf;BqbgCmGJ3Otd_L3THm5Id z*U8)89k513Yv)^Dg}2n!)WF=TN(q%eUzwKm*N8xS8_;J7UhBOv`KI_&No&5zT!8s% z7HaK{pxCvN$4O^Mu)n1=CxWmD*&qgKnYhm%b^@dNwDZ_-3J@_oRFD;h2#rlf5jp@} zDY$~{IyT%Ep~5MKGD-F52OOxDST^1)+HR2M!qUF4QGlNOGrnWl3W8)ICJ#|8^^Pj@ z&ZZS)5CqsWl=rTnNl9|wysIeUYrEn>kI<5oT?Y5DHeBO{o6k(ant{!^&`^l2`p8V` zeukj9y_Wf1qg?H`>#NJi0tHvcXl0YKjb-0aI(yc<*#t34$z*1-L}O)G2_b}Ar7tT2 zrP`q2m7#%#8p51mKe&~CtE-fXht!zM{jhNFS|*k$1Zlo%+^|jXuf~{OB{0<2i6T`Z zQF5-Yk>AoX_dFwQwBD&qum-KSSVg6bhLvAmh46@tnjiPe%1OZLLFRwSy2oXF<|tU# z!k1)qB2N8)U6_?3Ut*YU7S)-S?QW>^!rQn`@f_CorN>)HlFNwo7ikHxGex$#l0P1M zKvg<3RW{e?yjSRF;>>sdBl{J!Uq)n7l3HEi`JB?5&5^^?P}DEaWI#UD?*j#{4`* z#N&fmTpq>(a3iB71qj6Q$qtFn#rU+DswXtpa!;6uJ@AA828vI%fwvO!%4Ei139&`* z=S(ZSG!2vk^!cvkosCg(w2>Am;+0AHze%_`5~4 zimEq8?cp2)St~FrVllWy=tNG9Q_^k^2+ghV3jg}iokAm>`V4N@j|;WR%OdPA5*={6 z>S3{S=KL1J{=7Ti6OBy09pG1c@pvfgi5b;~OYG=j(%mU3k|p;-w|J;7C8D*h@Ipa8 zKeUgeXx0jKvbc2Qny~Jjr@1;_{F(p5;()7*fxi)K=_VZ+FFGSmAXK>u>Ec#QJ^Qvs zxq^Q71%NleVb(oCsGQGy*WAJLAFVi3^_b4akDoM!C}S%%@l%f zM0U*3gRv7HrhpPhdsWBo3+$%-8~`jQTEu8UXm4@99_hGLh#wWMaE>?=L*b4glv5}A z#@pr*AFwwMbhM=&S4##1jbdL^#W1D^55d`Uu82C>R%8GY+?*+)rKN+2O>Xbn-dJ*& zWzAiXPr-7Bf88CaN&`S6_n>fSs-4dS<3b5^ut*IT4YEk=nW{Gew=nA zK=)(*-$N#?kR@yR?dGjKU1Pe7vkngx$x|^CWj5c9pae*;7<9JIB&$>$5@HpS?_HYC zRF%h(QsH2|ia|)HBGda;Mp}NhyiIA|1zn={D2buD{-7>$lRu3h{|x#DymK23#GaWU zZ@-rd8ggdKpP5xbW*MI4@MCjt>;c0|LCq`QS!}|QX1zV{d*pYS#g3(9so6^vmfKar zGFzsi85Gg#xn?Kaw@(fXsFa(UJ}vqOAtTngGI|p?&k-Dy_grfho!Qg=%oGPS!%&r@ zKeTH?jv8064Tsi+o$Ku&ytBklTislY<1o`Cj)S)fQLGGTax00#e^<8{gUvUn{R+MY z{&Ad1fM{jiG@HeG2<~#<->&qu@m#ctNg;H!G~PdNfP~3^AtIpeGU1A8Elkl-9H!gb z`kiqHw?hp4w2#7b2RH@Qt%_m{;SC3)x{Fz?%`JSM-_fvA)mFfv6@x}hv!U0(rW=gk zJVoiACr*v@x7YZs2Bo!i3u)!c21V&6&$HOQ$Yu&^TD^0M+S6VF<=as7HKb@!{27q9 zf~PlhoHBwhc6y8$=sjvtft&MWsMpDisd%v~cGECh5hlyrI{hw1z#s7G_Q~(aSHe$w zYAtcYzQ9~a6w%|}#4f3H{udf8J*T7b5^veC8CeXtSn#ml-99s8$!+NjeR4uoivj#9p4mxPr#ud zw=P$Hd-$MOWrP9EBERJ1W4MH+_2N54aTh-a;Ykvi@49fCR`Gr`0tc@?Qe)vyDo^?* z2YsBczi!^jHSwvtmGizjU!vnT$uT7<;TghD?*#z6ZBMvlCFP!BRPwc{>FtrpmvSCh zVZE@GFo6e4L)zvKcRW~CMVkkT{_^JLcJ}A|9d^|19vf-~;MI5=y>0vM$!7aqCV zHO)@H(*e+DEn2m0i*tec!ZeL7ldxp@b!7x1yF-EIjw)a8&ZY|+F zHq@FWfjY@6E~!_=%NxIwzjx0PadL64mk|K5$ruTMUHqL zO$VRWV{z=_*Y~{?u|;)SovZ9ZICY;ECrcY1oJ>-s(;`1jB+U@Y^S|MS8J6mHVj!5S3K@4RmSEjmM$sp96g+LVkvjb_4Zh7bl4l-c(8#i?XgJvC3BYfN) znTmtu7s=(7dVgy|i>_xd8?3%%9aPvH7P+`bH~Ae}w7DIpkGs@2oSj^@@HNn}(|eFu zlb^@7v6fg8BU7itW=W5XpFpn>ARf4#JFYxl@PbkxQ)`1#RxJS6ofQZ5tS2qG9?qke zv}Yj@%F`3x>3wbyU(oX=`BvbMnz6fTkYrGcsy%G-X;G2Cn8w=Sx~B4b!a9&{{T<(q z0f5$3EWpD60gF{~>bp$etRMmv79B@P5Cnc|ze<_qF;gYzDqI?5Ywl1-H#Z0D!%iA}~ITSgyD(DK$X|&p;Bm%`u>?gO-Y1R-Sj}yVt>X0}V9RDi%>1+&uyhJkpZH=4432Cegne~bb|GxJs!UP6bnDx&`h^?<<7 zhVdF%Y!X9_VngFy_r2~V4&(sNGVljZRe5s5$e3-;?fx6m%$XyHAQSfL4}3Xl9~T^SUHWu^M%%e_`&tbHQ)& z)2Yx2jEq7bCUm?GeIOK^{;)xd5o#E6!;N)ZW}&H|g^H4o5!dNL(pNm#yy_I+kNrK0 z`N`1~y2GYZH;4e~j*NZ3?m4N?nL>ttz4buD5tT={Gaz!7FKwFUP|A#8@^_#%IN(!I#3l6UGQ9Osqm{E6p-xVDy)( zO45)zz$s>8;02Gh>0;{6<5t;Y<8XVYpTk5JWnbPs#dcs%g9X1}8<-KzY30YXuMk2}*FAwzBwo&P5)Cq-A?dZaL-!uzQE z9Wr0RJ)|VNUvF-@aR^aKAD`O4L`J({v~0f}5X(i_2fqdv-+YYWzxPM;WM2rm3xhR8 z9~=(??EA+1z*LyQ?xr#yem@s>B>r^nW90}tyDy{KjgaoTAeN+KPB$MJP0#UOIld0Y zHaH(J0oZ2>-0qON*2N4(+PLez1ah_Fh;21PVbXK?yrx})KN0&8npezUQG8!4J*&Ep8rp?! zO{MX9(7pnFvw^?Lju|sN5;FmxK)p00avKYaTR}ma&16j*+P;vu*bze+>nmkbC43C& zb4Vgp5aooYj2y$yHF7U8Y;)E&@vBzt<0APp*na^(jqitxZQNXfX69XO)oNwbn!W3z z>`<)tEM7ki)|6{8AzLY&rt>TV4)|WHhU#WkglX#Q^|wetVo%vetwFuC^z6f(tIMbG zH!dKZ^N9EE50wdQ*z__Uc$eKR1je8Bt%zq;zmwmta+?~beM?5|6<{V^_Zo>CB=e-^ zd7ZFRhw4SBT)nSqENnlqUbP*{LG-b}Xsp3m^`Rz{deF*wG&_)% zc?@R26+H(GLjs1m2j>VJtAtkZhFz8L&h8{!ZT;SdoTmt?b_L?pQm3nPG9)wTTx*iU zTEq42ed}wgOG+-2GKcy%0JX_c7N5v#fTu0?Sty4MdMIyp2HtIsHZk0IC%8j7KoO;u z@P0AGU185t&6yNKS5Wy~@1&)eEDZ(XPo1=M6eYxK8o+<=1g9_DTbejb(Z4UUZS>x$ zNr)%Lt49@rFv0fiS@HNuv?~!99h!GwbX|#{K_gpb2dnG3 z5p?TR{znnC;j4r7Nj%-b?^UH%=zNn;IsmN-399z5rb{|Pr~ivQc+N$AmHd7eb->sO z2@l>-eqvdA)tV2Q$HePFMZ3{=H%=s*IUBuBFFLvYuUF5HJGSu>eLnXg4No}9xy>K^ z>$TbI3qg?_Ow5lprd|&Bl$(i3@HunU9D8Qw@T}zviE)&ksz)-M=f9IcCgDz6k`|d^`OSCcB)UX!SwO1 zSV^C0Pe133oVQmIl`GqbBeATz>UffSiM_&QY(U%Bc4673xfLwlFAih)eq?Hy&Uu0E z0|upt2uEpL_}j}WGx7O5-N>Kq1CR5(7TPI>Pc6kE=es?Un$^H$W|@|s_;IGDGA}4_ zUN?Q+)=FuaSLGS`=x|RvnZ(1JZJA_JiCb1KI1Ics-NA+*v1f7D)pqqCDTD!Bqc2kmw1mTMy) z;Z6{VJbp!{?4abA1Q?xt4QROhJZvLowuWLs-6a&MiFfT*6j>ATNCds*Mf-f4gWF~~ zrNF2V8gefyWs|iDo+}xc+T0q?sR#~48OD{WYiaVO0{CvC2a?QK+TMGV%+;7r|Bn0~ zknhgl)iUDeA5cLXctsQ8%q-n`$Y_T>xJZPJuyN;rmw|c5_1y06FH5vJ#bnP-6~vKM zDXqObV$lMgAc9Yy3h^o(>-pAzHo}k$V^E&fSM7VkfXyx$XQciW`%C@7=TFw@=E?aM z8L7~;?-9f)=N=C3{>p4g{)QQV`}_9o`M}OrUW8qOmeD!vLu!`Leu6`Q2G-kDKF=w*mJ-&T+Z z{&#x*Ul@p2^Nvs+xm2IXiIR++DmWx9bGncPYZd2aSEQG_H-IQ*^}i)p-_IR&XCUT? zY!zGYJ$oAp61(fz{TwjlDMF3lPYDSp^1`lcOuvn~ETXu6gK|5q{ji*GyyT6>&sbcU z<%;k|DIs%bo^z#Sy-8E6!u>sIKj^9^Wf9w1@3>s118VE%$UG}F0_T-zrvw@OQebFQ zMON6>CYW*<{@XGz!$gkdfml4=VFS>l<&KLuCg#>KJ3xG(%Wg=UtZw@ZH#PpJKh5qH z#_jSv>~?9uJ(rxl#U_L!8~8X&oH$3Z-jpN&B5@X&^ZHB_EJNA+U(!Ktek|GZE;#79 zcHn-cycb`ybrAfB&^LtW7=n}X{=Z_%92-F?TzPd|u|~MeiOGuj@{!BvNN*iBAwnkm z?ND_#XqiyO{wD@a^`hwVg~xsUP5hE!$BKd6zRv8IDRxzk=r@(?H@?fY9E!VQ0Gu^z zJ{^Va;#i!`m*Z{y_8B`PAdvF0LwnM#!sFyC!_B|`fved*22p3BQT*Ygn>Q81S?3>X zL4N;Ycwu_ODHSVzTzi!XG!E<)&Ye7x8UflYE`bAN5^{>RX#VP9(Jx2NCWOZVQ(3#r z6b7|od4IO=tEaY;+8Y1tz9yW4T;Ve?gj8`ht|x^_C_tuR?>JsXgMZR{M?gn4#gHWB z=S?1=F`zh(&O?zR9*=JGiEdWmg<`&hhB5%1E|T#q#+7(}acguZ(}2~tP=`lCc~}Y@ z27|IoJZOT(E$t^%PkiM}P7NLTyX7hI(XuO<6hn;zGWZT}%qVfc!eId;Xo;y6N^s22 z`#fVSr3uEl{cFI5wtjd@&3r+7bJU~XHkR%t2je=>NE!-RmSIZ@whN3ux<-%8fqsWO zNd!y>ZPY;=R!Ys?L8PW|6{Lw*z(%p9=`q5db7~r2u@;X@MExRUsv$&;GAgfkPB`wv ziiSFqSwsefUX|eYG+Y2)u;UOUX(?p2qS}5 ztSTTop3ixWPY9@Rjv3rwE@s8iHYQk!c~ZS8oS^&p{n>yR4ak-<&BXZPoL#l;(gDv= z(p5r7X@1EWn+5Y#mSoqrl>Sw+%-F=!Ff@rS2E_61a49Sx1L8$q0p$bK4$g)b zeloYPtG}Y{(C^3$VYec-%L%`VO1 zQ;MS1_1~jO*iUj~$rk-=ywBkyN<;IhZIz)%@5g+VY)xS#>oS_^9n{B2LH>jw(uY?3 zijw=sT=*blO58kp?jih-@D0_t`qO9%y{7;698K0815<2m&yxH;4dFZ7(<0(cFa}Y< znRuS2m#9y}1rrXXrK{9H5x=HCJkS16dPmeBst9a27WaQ^duyM{1?`mVZs~g+o#8Z> zxWx}ah8{RS@a}1|ESy$y!kw_Byt_M(vF$`aluCyoA5xqQThjbWANd4xul`HnO{}ev z#wG*-2&ndUxC-5-*$z@h1-J*At~d;WeFCi~0o~sj<4^%0CK0u+VgcY2JOl>uDDLoE zK^!m-`oCk_0Ig}d^#m+8$d}oqo7~P~&&0E}*vGJkuz_ETsPg)DnC%_8yT6dB_jeqhR z0vT7a$=4tEXL746d3zqwUaG+FW?NTm5uz)=zwq6wd$jO4{U<%|_{cPR3{ORy(k)Y4 znq97xO2wMiZoOoV(p^j8{fW};09|&@nHGSD%~HDRe@vLj^*WXh&&g}waY_6t<7fUb z0dKOL4DV~r+7{NRve*INc0?~2gXzleSNUO`!CL3&H|xFiif&*qOuJ{yf}ED*=?^Cf zjq8C7B^)r-RJ8`8jC=8Ss6%$kH)vp^WzJ&CxZRhr*RCZmv#3H02t{JhXFlz7xLA0q%QK+4aV?a9Q#rS#-B>D+<_|;dk~4w(=dRie*4bj2RXlh@ zE$NvRz*i^c(aI{P-my+8rVPD4Y!RtvEpO=ZA@ol@8D*PeZ~i;Pw)rm84}AL7 zYm1J9Cgh~TF=5dQ{_Ay3WHf7y%?dll%x~1n9?JrKd77&-$A{}Wa29105if2cdiqPgw3vpKF08{B7-xM6=-;EA5GOF} zMRGfMadczan;y$3|AA{8@K&>nw6iK^J(1V{P$)o-3+)2Bx zdo|@@tIQ^R7J;K-Irx4ye`Upvg%TNP{BLy<6jah^PXn1p$Zqa(d{l3!i~LsVnN7%6 z_s40^Nh6q55f%6knkt!90<^{1eTp??*yQEypUbm1IrvB&ql6HV$;dhlEY}4=&@|xv z3sXcknx~}mJWQcrEK{0S_?Ga3Tt{*D8c62~NZ`_rQT^e9Aaw~jywo_^f-aSVeqGiS zKf!~!tV|)&Kgpe_nrM_UHtJuu`5C_=fRtEGs>a@ZasG6BpYp)d#_&|^ z6^1u*`k#+#t-6gA0VxNlgo&a;^jLodT;YQ|YXuxF#9iut31 zFxO@bM^;xt2Y&ZTN`^TxpxK!mOy?~GZHnR$_!bB4!>kw(+XR*Xg7kLS8a+dFIE{Lb z41I526YfzX8&RwhyRsozDk}Z(g_4X%pHz>nbw@QOymuR%x3^Zfv$JqdV?w(|vc|Iy zMa2OwBky9yPCF)%wSr$gOSLCSNfhOo#<7CkD@wq+PQa1t0%U{G3FwUanqLQeDsB}< zwy>trweR#I{Ub+uYe8O^!yUqS4Z zXuR(CR^o;%%ssVH`fcHknXdwPk1gO zlAC@;&mo)04l?Lei-c2Ik<8fxp4T@9>yvc1YoUhO0wL8V-Yxbk>}i?`qH5-im}A$2 zJQkjxkK%_C%2v|rU|F0vFCCjlmv)!PnhuB>fN@D~qBpDCbzHJV#bY@ejW2Kl;Q-5U zxB4H%i0AlxU_|%xNP4{*l?|3SqvV_1nxEFN+ftKP?#+TJCj`#RR-$h4l{FC5snx>Lg~f#h*{^+G+)2kepkdVWf2MH zG5p^XQvZvwemM+C?ROOkV4nQ{M4SFU*5J+*wO|hj8UP6Q|2F|D{{IjlD3f58+W!#X zPou78w*ahA+3@+ps099CJJQ^^(*1H?8)DhZf^qz>K}COQ6(WO{fy4EsfC6$j5e|u$ zAv??igV0nmH6BdDq2}DbiY%8wWm+K>M?4fq6NHMl@krfMpv9ZN9g@7!;n)u-L^=^p zP$MXqvD9Br$?k6~?Jcn`pz9q}OP*-#02l%Bz1J^E%iB~7F1L5<<<$8oqDX1MU4OXP z=u}35AB;cQaY1FhS2gUrf$XgLsnZPRe$ZEDO8XjL>foiuH@SruCn_l_1A}bbajUbc zo(&;f|Blu(m3M|mtNGac%M!~ppUz@%RsdauqnB=jFoyuAd|TQ~EuS9$Xj3(reY@pY zkYL8BUsNyh0Kt7EoL*LrqiqPHd!=8O!0`!zZ$nzNd{M-L`#h{JRL5w~D-2MzLSC%XU+av+vx?I<}-5^8Q0!SeGD}+RJnu2z06m ztio2Q0QVuA8dJsVqGoI8W;D}_p~WttUMEPMwsP;<>gKT?D)rNL(fldD7^tgU_SRZ( z9E^ozlDWYCMx{(8<$8(tuOQr+kRE4j<)3cP~f$H z)tPSnMN1!xPe+Xf$a}~wJJr4u(4S6dH+O5}zh`Axn&yKPs4tBzW>Wcyy=(=@61hPq zh_71JO(+>v3#0PFeB8|huowD;0tmHB3g`73+%hZ$KPpUXy}gkv-7^GE<6-EUhcQ?( zt`BdgE>_e^b@JGqsu78uO3EV7P6mu~lh0XEl8()~^k_SyDL$7}uI2lIM&aS2A3PfH zjHidqff0WP9k{|jL=ajm6f1Q_-3Z*)hgzuZbu9HAj0+<$=Y4Q!LT+)LKH!pT&u5=( z@aiq9WV7R*tJN^l?jm}CsO|hr(91DD+1Xa1R0&z=(pM88YSAxNYa6o*&z7Q+kaB*}ULFMeb-gC2Dx?;hppk~OE;N#8kQ zyQAq6?~Qrli9yenX(T?V?=v4uyj=gW`kdh1@)hE%XO2nRU2|FClp%`reb9%uP6%Pv z+%Wf82uwC)A)9dCF|&G(hmRQ`q_!qWV&rIXtcU$3T*lF!XRw`5?c};?(6Zbv+@mNP z3=X4KQ-&q}?F4f+_ghJqM_AS<+sI&Yw%`4f-=KlnNx4yONUMkVzy_{k)pu9x=FX0V z$oenQeX!H;BIPZ7KX@c(h~YztQG2XpQ4K#$ z@(!$~)NjDa-OW-P_iucNa6!3J<}W(vKZrpG)D%A~} zzTo~)WzkaS7)_LPOjEB01+tS8${L9_TiXI}J$(O0vLp=03 zFt6EtmbHjBQw(rOtfA`-wG2Vo(dVR%sCwKssJ8%E1R3WNAwOe;!9sS{B9>PNmu>GF~ zNp=mC6KL9TW!3gAeh}>Cb?G@yo*n$r=!l1* z0941Vl4g<*K?ZxzxTO~iQdf-X)kS48vRfC;P<(Ew5(Aa zE4uX~!`b6wrY;dU62L)u{p87ygT-n+Cb@p?HK$YZvgx^Q5859XVo4j$NTgB`t7YH( zBVu>~aD&HL);mDCHKwL^Fr&5T#Hvkao!T1t+Hkc&`DP20iMhTYqpmH@x!L! z&9^+bXkTI1vbmnyME%J!IO3vdVudjN|5*KC2>uU}EVvw!`x(%UAVSxV^J;PAN~ zGB|1g$IXZVKv`BG?&s1ksj)3m6;(mbmhwJDrZGYqrLE}zDsrqXUYMa(H}r23IP~m< zK>zjQobK6WJqBJb(LMga4kPQkGL)APnFp^737s%Jh++SS(N>y+-{|!N5X#gfV&oxr zPaO&n^E>M2@MkDk^Ly=f>fwSvoE$(?yV7|)fo9pb@VnQ)<<~*p_%A}F|BJDIiWVJU zwnX7&+qP}nwr%WX+qP}n+RL_W+qUlR|C~O5kNfc5eoP)RMk=#rDyf>YxK|<2v5(YE94MRz;O+4629|c?}J!T7;|BK1~{^yEQ{l8dh^*=G$f3w~Ce`2zK zYzGu7n6>jiOeW+Jd#Qa|Zj0FZRV>&ak%=piAwP& zd-?ut(H%$-c3F7&CxLoeRrLiqqxa06FVlN}WKYygKHhT|Y(oai{V_VByiP_cpxIom z0U!3=KO4Dl56G~0MH(m%!{sS@{Jc&n&`dBMk5953V>qkcg%#PFHlm)8~W3N+QV_n@mOvwR2N`k=va*AM zcvJ6s;bYySc&pK;mTb-cUaF#Z2Qe;Ce(00abV(!Y7OaT-LR1R9N3}+zuX%8I0vI#; zn8+x7lx~fUc3I$ZI;~3-@|S7BvVQwKAWs#HMDQ7&!bA9JcTZG?bXnrHlAi?8jZBGN zso%j;Sco266^yazY^lU{45G;A_>iTWeC$HhX&->s-P0tXcZ~{~VM?NcBfc&{(o1Iw4^RX+EB&*@LFxs%U4O@zi|Kce}ns6?Ei%O|9XSx;{GSx|H}=)RKZN3|H3`^ zF91T)K|JjihTtpNxZBkHu&YD#Oz{!+)2wZ$NFO`pp{`l~Eo1%gGEo{tFGL0wo zYG^fdW?BV@^%qWBkudsjf5&^cSQ0N-6w<%Pkf|!C6zveoD$7w6KhJf-FiE69=?mg???R7_MPEwSXclxO&kulWXos$FM#z_@%56+LEv z=9Kl30RdaKOJeQ$tZe9?5qt(0+SDQK70|Y?7m$Ar66!iURqU2`iHFst)C966-0}1J zXcCN{!~gbxgpXo(<~drB%QV3N&t%YBHnR=mdvMEbu}K9MX~aHX9iN=g?zPuU>IUp_ zgkejiU204$EfjnJlTm<{AMcH}t;5028R`57s{LgAa@^A7v{RVV(#AW(F|M!1Y}Qh! zFt3N%X{Ud&;;)pNGV=-jtmLjCCVxrEf-y5wDUT)~U|*Ys$CnwEs=q&|jr@K9VE%OP zBhd0!nycExvrm%#;wT}|-oc%WU6HMU9!Z>kfE3n$;UJgb{|X2C{|N{G#ecbi{|N{G z3o8kNnPUILf52kGXWuiNtEag+pCKmFvaR~RzieZ1SE|)4%-*_)14CUA`s;b`ka=>h zUs>M$k_FofDSC}-6N>iOTH$!=ZrI^gpS1{Rr-%1|7Y#=ogt@|^xxrk%DV2d7Tyb#%%P^5ZkX<0>TM+^4Etb?-ZlUL=u11(`3(Qv z6!rh#YlaS&3uB>u+L>z4_c9N)4*)7Qrtkl#+e!ad3<&<)x#IuzKjnWP{&zY5KfVtC z`~3fL8Q}j_&QqcOk3j>-?xq%{FDKqjo}fjn3ixL*hX$@FkgMiC{op)6j!iB(($uAG;lMz4wdV`U%B%;j1Lf7~gA7eZ8~*C>w~3p}&}bmFVq1*l@OC<;S9a9lmO?$GHWN{<*U@Tu5@m#omUZc}?PN1UE>yD=uq58Ps6T=TcOvl_VJ9j5(6heTk3!|$YtFTAH z=VRhY)77@k_s)aRCeP$ii7Ivw`@3Qe`OL8YZQ5h^NXBt$*j&|x*>!U8Ck346gOqNzpa5G_&jA4(OR8s5eS^f(z&IQim zKhsR+y~zW-*u^UHw-=2K6LUIdUoO?NPC`_PCYu>sNxK`y=oU!dS~3F75cxp5SaP7m zRs{T&a(Ezs<}K5!1h9@!HU}#B!k8j_1CB^qE$p55x%@j!yY+^e;6>>VCy`{2sa@NU*Pr@5GhW&SRhklH+=jIA%u|w z(b;-5;56VWPp8z{f9>AGH258hjB*i*Sfz>`0s0T-IMpOBB9EUrxf<7axxnkuouPaU zc}r|;CojC^D(KjFaI3j{dddeeF^nc5Wp28o5Y13J!rh86vA z*Ncg%Y92UnQK@T%7e_VN@>!cU3ME{aDq~V*w!T%Ak_D*aj9h*a!89LU z(sB8!ye=kW3?y9izXTb?pxxhZ!Ln<>0EK;DTsZo7P7v9;pud*n6?e+@#o)n~2!l#$ zk>w$XKSSWfnw2jP4uVIFy@zD+33Jd!?A~sVTUij#<%>cmP1A#!$Xhwt5)JSM@2CUN zk|{pip+*;(UGv8mVG4^+4knVr@mNDuHp2JI*JG1;+O|)UW~{exG9~_+RbIe2*C{9E z_7Wyh74KEK`&uETorZT6UP%c@5w=Nk zpRvufivxNI0j9DBfW{}5n>n27=P__g)AY0{V5E1rIocH#IE05&!9a_`m%7pEg{&6j zlw$LNa^|&zASF#Tc1Gix&QTuv`~`*-Bdo9QvfDLT>w~zss6S~%v!EPi^Six%w%~g* zPg?lIg*%>nixM`DW8!8T*c~;l>;|ACLO-){D+^qQqQFmAV@4;481P+jc*M2B5~;9o ziW?pfUt@ztdPpUcRzAVvV-O>!{=1O|UNk^+fmhWwF1V8S@>7nVef8 z5}{m4f|Cvl@XBdWxrvvWGXeiT}VD0?-NX*tyuFBVjkN|dLy^0X%Uw7s0hYO`e^W;FH*%s}F zXwlV-B{Fg&K$oPVxf}GYik=m}keH%u=!@oi7wVAuHb5y(>uDX%V-T8b30 z`@2D|y-@cM>8!z4 zPKB`t-HYTNk4;C%tXQ*?hRM8Ax(#Bqqx!B2?bEi2{U$j&wp*=IE`Q&zsNFJau2%{u z(LMJD;i3HoTrYY3C*()MrS6iACvzL-?Gl8hDz7kO!3P|Xidzu^&*7X)*CD*AVaR8F~G|P;; zJ9(;tQ!xDzzbFAPaBg*s2aw0o22s!LFGx~iJub$YO+yvs>u8ub#Wt&0b5(9M7Pr`t zc#x?|2?+d-$K+4|+qhatf9~vk@QvI|;JkUu;U#!u)DoTBv7};*51$fc285HQON2)CYCaeOYstU_z}Qa$ z%^{$}w!hC`j#}WVENl{nrLQg#bZxnL_F_V;!HE;N+l!@>6_Pi?!7#V_Lu~^aS-6)k zDrL@}?b>PxYjz3QZRF?sW&pSX%BWjG4sWm$SQZ_XpheDW39NVnm?!&F?}qvW#je?|eHh#k zeN~MEPrdn`MmNR(EU@jqCG^c&^WkLR}0n>if<&BGA@p7I$ zl2OyR283}mnN=E1xj04KFlOq;+4OTn8|{pqy@uSbX9^lHkp!D<2SmKx%$+Q4aXpCi zJS)d|A``+t*q?CgsqUA8Z_A~kIz(;N?_UTg}~sAbXLwM-fI-KO@d zRXVRa-0=kq?`{P&{p?uezuAhdj$RC2AO_o%sVYgcX+WZG<2#I8jIg(>$#q>N`8mA+ z^%XKgrJotcY8Uqj28IuqryK0*(f?$>?0P6mWQST8oZby*hx{s5Ha-YbA1O!b%8aRV zO?34nTO8yj0rDijYQUQ;bJW!*QOh`a!|~4zV>lHrd{n+FKC=O*Pe)8wZxzQeQH`u6 z%(--d9vhY8O&0%D)&FzBrJ7iZeokVe2`12Pw7SHpHoll7=5@9)-V4PJFK*PXy-j&d zs;yN~*-07z0a75+A4psNghFz-rX$lW=tgUjy0ape4Q7l+=dBekT9ZMgT*t}#p?Kf-G>9Gvd{F`Mimrv*&_iwyxN zVjZIQhJrd|j!!cReuz|$H7gU*VMe*agB*@{@m}NCXZ&XsEa7V7=5e222U7fUVo%#= z{1$WzW>26hstu-YuIu)j%=mRa1;O*{U}7em?)AGd6n|DQAFWgHwD_B`5c?#v)DLKU zc1+`&Da%%3dNca$9I4)T$w}~_I;&WSH~^h((~1l{&=H-0A6C$jNm0N=K`LRBZvCr3 zP=cc)fq}BFQE4zt(U$6`0dZ_2&DxZ-e!=etR?6~E#S~!`^b!nnh))Eer?_N`nZ2Qh z+l2Xo3e6L6kRQ8&EHJy7*L>hRBNba9H~yc~?JnV#KbtSO9(DA>!|dG<69%bpf!U@D zuKHyRed`sIUjGz@qn>1GQxg(VATJfzJYIN`wPnj}aQR$_V`NyRw(-Lb>Oez=aW*x(Ah&hHD%)qPAAu(Rx4Ff)#l;% zvOLMAoL~5>KQ@~b@kxIS$0A&cazfKSRF1d-&v(>!tvcmA?;5ENLS3#9bj~ILMy#gk zd^W5|fW}D-epKUf zkOqANxKvO6rT{G>?vj1ZO0JIJ$uSg2cWn|Zaa4%!H%-pqC%r+x0j}SO4ruREEgMi% z?wDu7#sEOmjQN12VW>9n zhuHy5#yE6`XJ6kIXO5%H9h2qud#nYHj2FO?O^h@t-EJeR6E>O-Hy82)uq>yb4TU?i zrj}DbZLLl{zFtCaKNQI)G=Mkw5I`ciHrsS)IyXGd#9e_=HrEtV z`K)XQKSh!1*|;#HDokFh$SiWAN6xS5Q|=ri7HEBRunPpm*CCy_`e@m)A$o|kmZS63 z*yfGIh{%Xbx;UmZBiDjN2v!m&zV{=zf^Zmc>lj>_jZ)F0`CitNJ$s9jx7Zk}KYkQ} zb?UMJKo+?oLG)kslj%It-?VtT^CBO+NCsz_#9UI%B&*F{y5jaFTPe|tgDj5Ls0Xbc zPZdopGnrf=L#n3UY~ym2OBG@n-;H$yvUw*)DERZ} z#~wIG5ME}%elWdTAv0q;Jf#f1E|?e4_3(vWu^zIy(8iEE7D;gcuCx2B!rjE=?6ieP zup15&{$-zuFq23p&yZn{6=g#L_lJeXgi=R4RXF%?;R3|PmvGCfW^!`Sw z`}qf%u|LW*IPO3I2ePtUCtWJ9)~CHBITZT-Rr#y;@o95Uj7oFTP=+`q#XS*9y3pOU ze8~UIW}6mkEP3ZTL>Z5WqIZrYvy)yM1)~kmq8?V?;kUdEO7~t=YZv;*(g(y09s~#v z^-DA#FC^MsSm>=SCuX8&5)%ZW&CB8rRmU>?9SoL#a3qYzJ0EQ+4r%H!eRJiT4W>zI zG5L$ZFvZ7zsVDfi=PKcYfXW{PPG!6iy2tngmVlpYu8mS15i;*Ztin~$yp8ItSE zI&Q2_TPBxoc24`85$pOxl*Y6ikB_u1bC^-;W~TrnyrGwvs5?fZDz+AOd_K48*QXJO zAkFq&CXx&W6eLiign&prV|xM)0Dk*JS5#lACW1tgbGZc%^<$O}=^tstjTejgU{B~) zBclsH+q8D8m7(Mx_|PCK>!wGKb3eDp0Y2RQV?)VSY4U&nUa}n7`D6^o%K8FRPW@Ht__QxY2^PCB}xh|aAe3x|1)WGlLJdyGB$JDrbTCzFF%Ce}i%A^;9jUP*Yz-~L_v zXv$IQ$#kfP@bpteOWcz-b~0^wYr5tv0FePGi~<-@?3Q*DiVfc@HYw7`Wv|mA)m)wP z*_NLb2-5d?Yd^ow9GjE8G%=hMyZ=>)L-zO)W2?xmvMRAdAe zcE2b<-8P`wSgY!&BGBYfFn$Z5)9iag{!Z%k?>K1HU{7d&v|r)CJ|mH7H~BZaIsf7@ zRh|Vt=aIH=ifd&&JMc4rK4G6gE^)*d#XEcr`HlgxPU7aCWV1f=jL)KJf8IK{I5KEK zk=T2klkYOSKKxp=!bN*p;Bz8y+J+(0{tz%KLl3;;@YD*%XPHY9cOF;s zN!)Ae>i$7}?2IDP-Lc!XzPhsvxMy?k!sO$O_WNQ{G_605!~Qyn39+(e)VXOACAe5< zezE&U!zZ`(8y3BUrvVajF#}}m45B*6l)7p*GzV^j6cq(uNI>!ri=miuqlLA+l_tdR zZG@J1)rM8_I=#RMKvUxS?intHeu@%g~vCO_@QPj9hc&5d$%v^Ip{7 zGpew=;xhebsY1Lp-sD8H2N~09T#H26MIijbVCoS9{x)$A-7G}yGZIe*81H!Lv_S6J z3$#v`$!&ND+t$m{zctteFhmN_E~$A}7t7RoxJ|-)AHN`-WlC`F@WnnPkr2BbsHoN-iDwR|}PiHxs z3&zKbW0#o$q@AW2tetyUSHId3VTkcgZMeaygeQyP2PeA+5x9qa1>c<=q>@bKy?=yT zx?c-6Zohj$aQvhO>gUJI@qv2P@KWAh>-%1H70+H#twHWzG8B}yg2o|4bm=nGQi@QrI00-0iuOJ{B` zg9XnXkEZqhNit)*At_p6RNVxa+Wc%R_fI6zykFbJn>wM@0#abmtlTr2a=hb}Hq?@K zu##BrSLUlf_XnyOy4zoKRz7^gA{BUv4nV{}ka{?&&Ob0MEi>0P1PH;Tck4!(P}G%L zGJN3W!z|o^EnWDPC=ogKJ7oIFQOp%aG`Xu_84xZOI3mQo3NVI%YqeR-Rn)MmVe7c# zDl$aIC}Kt!OU8sw%3?*>qtc#k9^3W#zl$mox0jUzn)Q9iR8Hg0ML=QQ>ybrMQecCb#nm^ccdum|MeR&Tmz^JL-=m* zYHt?nNIguW(G&$Jxz$fH|2!R?U4=G(cP`8!x1;H)DV0?0P*N2Yl*8f!-eR_*oudrz zpC_Y$<|fqs*hHcmcLE{s(2St~1 zU>~WZ&_yp8S}(jco;t3QN?R^1O&VKE&vRGyD(nV&RL-r4-hL`>)Q^`qsMZ0Q0d_wT z;@9CPs@sY_lzg&2`U#F`_6rMJFTR>L4Ls}oi)7dmG^c!6@#6=*;O2U<7(8{G%)QR+ z6DYoq9>S6$2_TA@u_o7sDWgbHN1zPu z{pvdR=V3~>nYcmbKcl*{fNgf0p^q2lJ!DVBH99Z*h`_@tkU9RVJ0gQIKrlngw?Srb3){=;8_AE< znsZf{t#`H3?>u=D8k36alf!U7Ka2#8^%gpjxmO|-r$FxiB-Q$UJ98RvE(U%Dcg&Cj zPz*U!2{Y0HLZCMd0GQQz%%NfoDX{yRo#4@3>A=vxlG+SPm^x4LQ_!(aX88^VRHhyV zSr|#X4-X;OFT(SaPner{w!|$nHgzt;p|<5df((`a2Y3G3;{YE79RKiZlwGpA?%86P z0#d?`7yeEq6D?pSCn8i?AAHA8_=2|XbAt9rA~oI~qGBpcv~k`Eje)p?!42ERWadnR zPx*1L^deA*A0demB#`n)Ul5XaD996c;O--3X(x%jn+@uIdXwIYuj3i3S0nfQ(d%}Z zzr3bjy^3*X>Y~SS#E|;Wrt&5{3LB#A`Kt>$aygLUF_e#g><FK9Nu8^_O2uo5(G+gE^qvwvGED2@jNePt8Ej2L4S~e{CQnjvd&r!XFJAh8nuZwa9 zt_fs)HFJ}hTVg>c-(#}hh`+twhPd6pCi@$loxQY@l@U}Fy8io*;F{}6$pwn6_H>kv zmLFqD-Ov1)eF%8?>`)WFx0B+@da1Xe1lKk{lv^cHDg6xmX?y6!%)ttZ<5Dd*^)zn> zUf%tRqN`C;{RhKQA?4uj-*snQ#3a6r>L*|Wa`)w*#8jX^CuHE#S44m)O@_^kRTasq zDIO6XIG)^i8+KB+Sisg&U>t+1>42-H-aI!J6A zA=u^tS(frhJfBTEe#Mx+Rah0#xxF%G3e%@39q0!5lfFzA$1t4d1N1dk!ANpEi6t|* zxm9{%w+~SrS6j;@dqUOAke;U&s-j-d#;cu^w=&5#8cEVQ8m~d#iVUi>d(L#8g#oU$ z3dX;90qBtmAL^Krfv=(ZVdT{B?0%?+_>xG~gzi=r&NFPXHkv5vFK0K3^=A>%EotD` zNC-)Z_lTjE8fYpZ{rv=pD)wLkMIu39FIsY9EkwoWb*Kp00fi#M6NA9AzFbbzo_V_H z%-tKyb?Ld#^p6QnV|+!lMQf;kkz=#Dqo7L)AJDmX84{2-wHCe4IR+YJwF5S_`CPR6 zCrXEJJ6nuIaIeHsja}QpgpoG>g9>o>i8ye#4%rT_NA>yqeq3L8(k0B6yfv|ReGA`x z{A-)AJG2C~fljce#XMLJ4yY4EQL;mK>m=^X(#1@CEcjwk!8SRi8|ZLxi#Kb9r*a6) zYe#OI;uMLG@);M8*^(o@vdg<5z<#bA9KCKOSE9@-P3wL$@&1M^7RQmy868j7NdqSM zGuZBfNr8AzR7IJq{^=r*rUCk6%^H)G+~?B~L!ZBU7?$C@!GBy@SZjKe&V1qxAG>|P z{*VNH9=;U`yX9b~idR4>&BP4FZKs0q#^hb@}epNX8*~7q7U$eyr%=9U+yvEYu_(d)CXntpAcq;Ln{@6 z@bth}>7@(OZe{u3{=*!(1~E3>+k(S3OXg1(z8=~-YKj>aN;v;%ln5`{5rgUaahieG z>jRb?8I}}jE1g~Al_0ELA(IW>AaLOM3WUUi$NTn}f|lRb*zTiLQ$<~;DOM&3@vQZ;AmH^8(uZu-}`L7BpukZw* z1dfG$0WU^7{M$G$CZp?6sfrL;(Fh2N#!?_TWU4<*gMYE*O-RAy+ljCP80&;f{KalQ za;obgf|Za$gL4Rz-62_=t7!?6)hp%zJ-zAhO|v^BVhZc(afXO`i?$k$bdR+4#<4H< zIPHVP8t2g-sL)QlV{q19{QWa@A(h~R~2)&)DU z!eA}d6ih&~B!!P${C8czP27w=M?sI$kX10V^T|ItlL8p^$obj}PFhe30|V`ncf4)`vCXnR=H9|wI-;qhAFsKRDTArvezHL1&?+(u2$4heUHtR>*;I<$$?-eph+s zV1-WMHlv*GI0I_l-%uNM=@PuzlR`1Q-+tyB;)J|zuoc!8W!D7IU!SF zXZ*THH%co^;v2cW0uIu-`yx)? z?uHpgop0q=K_9wzW}c#b-?iGt2rJ{@?|gfYV;~BT%AOP7UjHOpK1c zZGv3Wp%XVg=kv3Vb_e&d=E1N?i(GN@cA$z=4J!s^#1z*}fR4|=1bPaO^vE|F zDkpzRBh`Fd7PBl23tgq43of3<aO$ z41s4btmivVLA=SlhpDk7brk)2Z!48-W?L19WB`3r$Es>J)aguR?b~3r?fN6prRIL|7hXB+WpS32$2=YFpKam(-NaKW`ZFby9*UPcCmC_jXNETh3se)qSA zH4lH5#m+m8)N~x-0P7W!7s51WMdGyG*1*^i6_s9v@~_NIg!k#!UX$+m!mS^siH&17 zMBc&hk=S6U_3rd4v%?-%BCqyQw!i~UXB;bUXROnL3rW;Aa(=MW(@qoQD)P6Teyr}! zzdTo996mFV>aY^!J}n$>Fj$B)XH{oPMO$&qOsve^$FNt5O5WdjCooyALr%9oE3V78Bn%p zNaszKB6$#uKWRG#gNO^c%c;a9tLQ6fGA+aOnoREUMoJZ*mhyRg%x~ms@z*S&S*7&N1ZgWxn`wv=Dj#e&C!vF_EIXH69LJt4d5D>`T^!KPxESv zz`@(MyaKBeV2%@jvHWAep@g8jdyA}>kT!rgY5k1bEc*v1+6` zYcEbTCnR?b!`)CMO9C8K$ByT1GNE-*PQrumrmyYohFN}stHgYP@RbL>#p9RyVbO{n zU7UBWH~E6e1IV!gCE4D?KY6D&V}%oP>q~kAkLO<%sEdDXIF)}>!3QVlj(Nd4V}??^ zU;aXSr$qP>#mhi-Y%kL}Yfi$4Dx*x^3SUd_x~=qf_|zKrU)O~h)12%0Sg9s zwoJVpjzKO(rUg2=$L*WGxb_EZwK=a`VJ5a-O2fec(h+755Y4I$w+_5@~y%<%OrTKcfw z@TgYnsc>rG!L;)>(-A(SWamx8_H16;kdmS~YTMVo4_A5A=eH&hua&|JU#&x1kLs$* zEvS8{FLGl3ly1UJ7=mhp({@bKXSq;l$Le93jq>DO_x0m;Pdte+*$)X3O8!NWy5izt z58X*YLg$)urptUq2LAB{Mhk^iaGB~blKKPah!llDF#pfT%)}LyjTwA0yAQY7?))y3 z4%mx8PNt>eCpEmL2ZaS45xeX)ke>9Fl}(zW(VOJow`Wct%|NaloolQqpV7~9MEC+; zN#1zQF2Kuq*&JW4aBiN?@Isi7`1PRq$2X*LStQV48oT$m#rxXmD7GtBwCLR6RKzZP z3e-ketS3OhX~dUK!;oX$s`=XG(NzwXcrF~W9!YO_tu(`iYGG#t^cxq1Rl#oK#--67 z>|ps57$^&E*8cg3S8{~<2H{3SbFib&6K;xm^cc6<<^}aryd{Pt<-c8o53Q6~ih56u z;udVy1!@oUd0in5dWQaF5}S7Zn#}@%)Neh)%sN2dit_!9Oe0)X5u)%Jb8^XO_hm;T z^j2WPq!w+bxqETafb}^_5w!*ri)o-2)>Whc;@yb&aU8Lyio88#m8z&!2T{U{RLm51 zr#wO2ZIV2S?#ur;t^-uAb3Xt)VIpyAyateDgFV zOS`8rcO8v}R;5UXR2JW6RqWeFg}$25ca}V+w{HtfmeLXB4oWH zOQD@&iu*f8{V4r$()*KJafBh=Z8sy!FQq=Fl=ORW$c_%yzIjy2Wt6dJmLW-DOT8NF zI3AwG(*s{jpwPjg3oW>1k~9O8xeU8((}VI|Oou~6h9e60YW-tAdhbH;Pq85qO1gv) z8EV&U7e#1)!*jzuLrsrNN8L2n8$I71#c8KYbqwN~0RYy72keJYE8|6v$U@a{CPvO* zOH4CvyBeU=Q`*|zlTZyz5Ke9<07PwP{fwWB5l$1xWT(4N*qMYZn7f1JY++VpB{Qrk z7z9k>{Hm&c&t*%n?nJYYUZa(@^V)>-rEY$lZ* zDQAT_?QROR&B^dW3AeqLn*)w*!F+)+?vC%;eu+y(hT;KfD0Dz)}{dEA03QkgMD{2PYnd2on z>~S|8nibv(XbAX)>B1K14(T!S@I@rp1#}>bESDFliq`N8SO2?<`9O@)pbC^j6AX)F z=8DU+h@fldt7JC@Jt>JeYp)G|kJ&?UaFTD|SzsVs^PH~(4*Zn+CO|T91@i%t7u~7N z$Gsj~8ehT@unc0Ig(wAa>d`l|-rsc%&$u&=XWcLl*|BxGb}+deEs}^HQ#b*&DbGwL zRfz0xS*D&z74?IvT^~PMO)+xdZ-kQ65y_1BfT1|{LFjCSUBom-@V6%-gW&ImYHq?` zGj(MrojH^&3&GJ?*iVu#u;FgxoFrH#;U{tF2aV`qG`DBv#{9OQC3!dpLSIsMbFOY4C*lmVNByIb0WkoiZ+k;)|g$QSVr=;3w27f?#Fj zeSa4a7<{bnC}__CIoj#Drk9FL4O5tH=qRVBD;}vs9Z334=CuVhU~4e4O@vxbmIn(@ zi>-ZU23-!~=R&Rct;+#hWvUezDQ(zzZUy=23SGWKYZ$HXC)4D0ClG))up{Iv-!z9~ zQDLt|i(Dh9h#3&fQn5|Q2_yLh@{Dk>%R7B~OMBTottuh~52%vjX=$i}f#uLUr)|%m zUO*0^lVJfshy%JdHiHBbk(YzqHCsN52xo(-;%IFS zoxaS)+hUUOSuCn8d4>$0FrD8C@Z0tfA;Y~ieg_gyN;C-tX2qdbOL&?H9rXJ{Bo{SF zHbl{00RZ6N|Hpt0(4b(sz~2mPZ^Q*YXc#~y@h9(Ye(d@(N4j~7B{3N-IjnUXrV>U~ zi7X#yX-@#eMo-qMHvKKYMNWQ1^w_0`dSmu)`Q{xPrn+5tl#(=7uP`)L!J*QH2E1kj z?kcK$TR8zJ>a4I{J$rM!v#@#weJP_vwNxwN)6%kLPbn!8I15?_0`=YIv#HRaia9Qr zV5<_BOtVXjqydLfLv9yLK&W*#Il1cx>*`=#x;i)(lQXhbdSEs3OqwnFM7+_6WK*A^ zuvg}(3=hi|eaFlf)KcS1zD+%i`g0aRFIvr-vzpZuU@%6_#hSBj0~<{uER4%)3b2H0 z>Ab(L|23QSZwt$&H|LtKuC`L-8}kzyRxeRu`@KYP0;An>p^R&Xk9WPuCP*W5PH9^8 zj~PRz#!@hV!@C*l8ND81_O2%Asr;=sGv3hJDY&R)1tyTJZJWc->$Sar1;`!apr?Mv z?3G~|XRw^?m39Dd_^>l($lSIVN<&~oe=bpuK74gnp3Q?Wmx5!|Sj3mgLoMo7k4aFH zwTP#`$OsGK`NbHoTAZxW7XS7V&ipG7MAy?oPF#|JynV5O5KO-Cx%U!TagcV|by1a# zh(4S;LJ&+T5GpnjgDj{yC1MbcL?MIUuaY9La-Foo_l*~Dk_i6b$v~=VclEHf6ZRx6 zR&Tq3VJ>NdjIkuAh663-;IEUOB7Hc?Z#JpX&%zq5MTMCb$KhFTy=ayJO8KV1sZ*L- zE}Jn>sLP;`r+SrDg9ql6gA8O>=|j9yN{h9upV<^Vgs;;k!TY$sB=XzsrFVPS6@9(6 zw0p&|oGm6y?3qmOL>upl6WT+AVl2FP@A-tp-~CnRV(aVnaouW zM;5Y2-C*hh`F)8!AEvV)EN?C+YNPOSDI34P4&mbzP0$c?Z?IEX6Mt@YTQh-UD}J*{MALf!p| zly}k%H15fgvk~?_<_On$6kf{%U~Jx^8?&aCWyrluSy_4VoLa+(`p6k_97jO+pFMOf zB7Y3YHnKJALXvDOH^K+_k1kFkeWyXCkUe^{WYi0n<+!`x-f@3^k>o>wS?=^XTw(KP zvf%az1ZuENLMrSE0zBzV z-0ln<4`8_h(zpxU*zzF-CvLriU;GaUf?(39(X;C)UesB})G$tEbasO1t3KeJSe$!n zU-4~Dm7Y%?LhopxnxC*| zN6=tx`L5OF>kwXzl-0)KGAuqx0+O>v6w=s@Im6vs@^2Rmc|NvGeTCJ-=Il>Xyk;b0 zzBmu9G64HYc9lnL>NMfa#A1<=$deM-*1G{L(zNAmn@PXI@z>f}jUx-mt>^?4ag{^s z*H4U`5Cw8}*guk`jn~E6{`hhat5hH?Ys3Et%(r~?k7+qM<3A09<&F-#L>aqp0d%+iAGuxdd8e22qFWfBCEP>)n#PC5d#NFSX0oW@M&_?OPo`9 zU^$=rpt+D=ZOfnG?(D5eMOZJvhVov#$B#&O8ezo+CVsF4Hl3QPdpcdsOL{hdi(xz+ zwI^Uw?4+^HEY15m;`NuOME)NDVL+b03g0P4aFBpmFu;W5IglcuT2k(vuJquwk?NoX4&u{}#{!zR$HgbL$ESK;BxcVV^oQu2$Nj}_lbvL*HIEJtBs8|* z{aga;IRq+{q_7GQYq@Xz8j@R}ag z6#MfQ>siRy_BwHgqdb*`eSS{7f1(AXql0~%0%8l2cW-ukT+{@)=LFoB&y4JUG@?j3 znx2m!!Wh@m*L0-Skv;uDtFKRgCDLEy2f~9~LR84Dh%<(p*td||^`;A%jaGvy!XG+| zK8Zy{i}rD)a0zQfF8s28!(zDPH5L*p7G5KI8}-(r{I=nTfMQzPp@sL&4{P^>hhS(2 zX||xZdu{pwpgRM{6QTFX*PgkVKv&^oX=hK%snkX8SE=4YjAALFgr$LWo%zxJz!f?k zPe?m9){pXUDZpLRiN>u!nmFf(tUX!btq7^t8xOwDrv+gOZ zOKPWxwQ{x<$h+?Bo%2Afz0B;2(-!REIEtMHishs}k!SBKc?3$7fr`A z6Gr|U76BlT>a}&i&nf?RW|Wbx=AG3A+3a-`Yhp(xJ36Mn2>>qW*Ao@lrj2%Svr#MC zg}iE2c%_nHx%R#*d}`Ral){x{A=bleGc5D&O$9JmB`!hVJyNBNwt<+IlqmULVY(7e_AZa;uBC}q3>*ZbXHVr=mrUouyBa$Cbv3{>B!)m zRM+a;lnCqY`X_POo~<-4iYmc|@m0h_XO1u)Z3XfA;WgV1wH=w`s0(@2G-6KUIK7;k zULnkl!F(G{^S5O~GIW=nIHK&3y$DJcTdjkB*nj}&hlcTFP zhpiK=%l7Z&SNs-rBu4rYhy-rqu)&}Z0hPfpmmJbFqBuxkCL;=A2xs=g7B(<7dEoim zRA?bZ%0ewmfeo(gPf7!Gl|y-$Z|F1wkA|4le6Bm0eqt>J`>jB}@vPXP@}*C&jjZa) zA*nnUNun2lJF8a2Ff&!X$y~$9=N@vR!4#5fV2)-`+oldw(FZ32cvlZB#7(1NOgDvG z?H!UpL`XmgLIn8TK+&`#3AlqwfX&mP&iYG{gyo#XOZ>Y7t;=D;|Cqj2pW@uCQBH|h z4UKlK5%ws_X`GbHC#dfU56=Rr_8QV#F4Y`Zg3>Ay zUj9hppJ6@a^g89KO{mq3P*SZ2I zBuxm|N5g5Y#{(vJ06QZ_q$aqC+}0LPR7aZYh;=6B6AnAYq{EIM*ws_vPlb_?ReJF# zfI?k8-I#vvZdde5hO(=+&ADK-oSLCX14KI&HQ1n$WMpmsi{LtBxneedmX*s=s9qbED>TI=jw4R zkgZ7fqx?YB+3eevcH#9eF5Zf(O1v8Kv%;{``e@P22<%EowPJa=f((^vn4s1H&lh@n zj0xLuCKbW^49~7@p-DZqxk~Mg3_k(&c2(C?cz;Ire5gED?ourzuV^5L6~U~`>xO&u zD1t6Vec)SH^m4?I6&=$+=E%%qUn%s=5`I2vKZ>8L#E{K zw?UT~whD2Q$c+VOH;dhncKB@Y`n)5U&CF32pV-UArGL9kyk6|qcZSfjoigddE!OGz z29vbrWB$+1E5iHrQ}lP+2cZ40L+?^@SqbZSfH95GG>yC}Re;M`sOsA81t*!qQabH& z%Wcr9Zy^jSvx-@VA?3#KGR&>kuZd@+Ew$gt48w{uNNkzq?M-iDgU69M;H{w4hkbfY zlI3HOtOr~(_cWUojzM3umam6)Rh#>;DZaYMbO$C|5ngI@Zt(C*hCww?NhS8HU{|*t zUqG%%XewP|gUc-wzjQ@z8MB9^nJ)zqDCxoR+ha5VDXPoL#pi2|X|dCEc4n5@{Z}%* zE$R$_Hj3+0>-T{&8ZeA zUDF4LVS?JP(J2wKR$Sj#@`uUN6|-k81$q%B4+abjsfhny}Tux_*1T zPa2|9yN0gmUOqTN1Czx|8Gm@Qpy!v=er+YpHZhKjre7exF{4Le+j$QAeTs*N{gr_d zWi*UgcCnc>h&OIWkIU55H?a(a8O&~L+D{Mx2>maK!^9|T|7$<|b9uo403zo>n-4Vz zEvYh?1fRWQftU#VqZtIl$$&m%5vBbLH+i3@%46(YlvuF;?O*$3&ZE$CcL}+`e4ndp z8O4}lRKuwWf@1_ZZ{7so%U+eYTe2=pm?#a(sl01u=WKS)ieCPbB6JI&Tca=24!u3# z3$E{YpU*Xy>?gOGiCu))&jA5qL)dE1m2rw(^Rx_{)#n<%|9ye&Ns3ecu&S9hY#&|R zX9P5IZj$Z@9BYaIb?eGJrK#DD1{b?-W{DL5F)zC`ZflL6w_%9yaH)A&{?WG9f&_PSOYDVp}Hwql{?qM_~}m-@`Wyv~XIw zEVcD)uOiKc+YiHVUF90YD9s-q8Kp2p6$FnkzJrugdMA}xNyme0SF)I+tdc27XCB=2 zxwpL-T27!?pU)5}&@I*Aoc(?vHL0?Gvlcu6JN8^%JJ3E~5(huw zPsUm)rb?0GX%}^UYlBa+?EwajMbRF{&UNqXfnnL1u*M~Z>+odT2~PE=I|{ryAj#3q z0wiazTkx&{za_+g*ThWlhVNEfH>^AfO-&2`c#tna6%I4mrjbDRLC)Zja$+Kna95}B zC(|*?B(ML5x+pG~20PcQXdoHL*&Q^HxXugzF=+-*HISo)m{t6Zf$wDb)pboowKbE% zFMo?;#82(|5lerN?74QbaKK0CDT|8hE-sauJKQN*sm(9zA{L|EZsz6)W=mH}5T0)> z&#ZVcQKk3n_rHO>9O5ki_MBUrI+GGU3?q~;-K{RydxF#hX=ECSkLO6LA~Ns7)kQh= zDlgfYWmx&dtle0e6Q?5mkV$?v3&r(Q@2@|?0ECTl$>R~!V^q|kuMUaP`y&?8t)%hc zmeurhFye&_>E5D!3%M}yd-8tWXE%$Y1|u1T#>sv(f@<@%3Q6i>#g#r6YTDm7UzpRs z^0z#J+}F`MtEpMC6jd;4T(B6HJ1 zbz?qHwT2p#TsPhe_ClUA^%9K;;oGn4qTuTni)eNv=K=Sg%_H=R!99t=;A^mAZ7EFW z{7iMx;czOR0nY+e?>%?vRiR|mf{5+#&TFhyEXBvkA&C(SAV3%|awzZ3$^KZ!i5w(% z^Ia=_Y(v5j{dAa#>`mJ5q6RL0`$cinlZv=(2~YrzE(-=)d*&2N)j~8N5Yap0Lq~&Y z^`4L)sdqmUAE1fa8?%Yd=^OJ;vo`AXcbRo)A_*T3KTX5$0Pd~5VU^ns8{%^hq#)I- z=gN|%YxP%;1mk%YD3o?;d;q@$aa-xBLOsd%D+hL9O!*%# z!^mH*U%LH3CE{QLK-o9Q&LPRfe+!8PPF7CZ1(6TAZ5;3SX|7@{==v@|^>acn9l=5{ z?jVWz<-348pmt8XkOx6(rVBQk=^A;)d`s~AVE7x^#@Y-BFny_o$(l`jkK?pD`RZDd zmuaUZJS%dro|wEtHri-<+j<=f`}lX0=&jl5vHk>pH9?W)s3k?cSRAkGo5Y<|>gZMq z^%;&XHZH{FTaap6Ya6Illzgt?eQG@;$`_F_eUXd5)0Rpz@y*w&9%XaN>_ZQUbLb`J z_S@)HFHRwK&6k*hxU+Agb6kJQdpdFfPSzU80F0X-em_!nH~m zA-e+#Fan@}$x#1#Kb(<*qE-RG(IXIdlh~C;iY&%YiF>jhncLHNch|g1D4tit@ckmbJG2l{F3bi)As|N)a$gu53uK{R$fdzX|FQB1iVt zPY+h^!(>pu+f2n&1*^m%R5>Ekmw^uLy@;T&t3~WP|B_DtU4_U6mc0c?!y~0?ow#ECa6|atExF7BJ zkb}$*I4gZ~KC|jTg?V-d3JC6oozmUAEm3A+XfUTh?I0mH38j|{6$I0mS#}66=~0an zkX#(xAhdKam4x0?V@7FtgH!M`*_#ta5N?Ff0Yjt%KwsJMKyJNNO$sGbq zIV(^Icphkoy}LA$sX~Vi@BYij51$_UakJ0aRj*9uO|-r#uE{cif2tK>EgOnUC|*pm`hpMk&PL9eCPi6Z2k|5?JDs8N(rg4h3 zfs%DP|0;XyUob*VJ*Y(srgBNGh6KU zKQzs^x--8z7~}jY4*@T;=#mww>D;6*E1TWd(VdfA@|mMx*dQKqcJ!~(Lm?&Z;xHGr zl)yXU$~{jf(FouKcl`a^UmV-J;Y3%!RKyF zzEluHVJ7SsX(*T7@vbh1!(~`&8?zi5jA_SjF~8e`+EsemnLb@TTNW3YdjPCzyo@@Z z$h7!DH@)#}u9tnBsLox(C;cGm4BF|Mr9B+VJ8Q6NbFCAw@ zN!CMzO&9jY&_`{I!r#dfL&mNm{Vj^`Il=uzb(xI_> zYQ{YCV!oklNci~bTq*f1myvY`G@|LE7Jp4cOQ)OTO7q$l*LQ&KO=}X2z&&icR><#y z&uHw9Nod?_>J}|0l@yMBF&N%ZeM532;tIo!Kr$DT?i+|BZZ4cAP4l1^YI=-xvF*UN zRsEZyQg19TX7~>Fu!R3<802bsl~x9mhE2S~lb9*QKQ0p~7VB`71IC`3Ok_ov9M~f( zg{bqxW{WooEfuy(byF_T_)!plAzGWgKu<*AJ`y|A@T zousDzeXQO>trQFNK9$3Nrs7*%mohiWt8d%b7BG=#zTHX&0$bu;Ava}|$VQ<;XWa^- z*(n5apTM4kjn%lm7F#*Xzf)7cdrfT9M=GU`!xbwfWKnzsgOX8K zI;p2|c5vRyFXUsH@eg@qedm_7po}y$<^>&6L}0STGl-xrR|FBWD%>U_)h1djavc#K z+`@`@OPFIQlDC~Xsj>@246o|1Pgb5(8ritnB^p5-HF~h4yFw)XHj5n|L@rROWXvXA zOpD30oL>NNAxv!;H`tnBe{s<7HTyKLx5xY^bZG21d56;&rmc5Rif^91o!Od^WOVeY zn+FX#rq@!>sJn(-gx&1AL8JqjFjF!ACCMI40H*`SvI07w(#iswL*VJwC=8huhilA< zw%2tAk7MClRYq z=6u3r2K9#JSZfxcD1$#>JYIb-Pcbvaz9c7{owFy$7hw=Uhld-6J%wcRpqOj&b{=mM zshcz8`-}-7JsP(}&c^u2IOOs`46|ogd@*5xfL+C|ilxleic_UKri3ZE-F-hbE)jf+ zAk}0^K;QfVI3H#$=#nWoY#z*vMcNw87hPVW>W`E4O?}L4308a78zX-3?9=aBS^^n2 zHm)}E-M$&B-#LEZipQy3k|bxZ8v*%s;$cQ9EumgfC81w{&PFY;MVz2Hg5`7$uVs-UKAiml+3?17`ZPc{DdvHVPY>^-%Bgdw4GS>{I` zzGhh|UHm}axy=|OOB=W;N5k8)c$Y6#a73m(ifBY%hVe&9WmsEqRtdMqZz?AA-{o{! zU6&osK6l17Gv5n86+{=N#9}~|6oX%sEVVx(7`>PBUK|JQ4}r?(XWlzLVZ86azSB(f z+upO^{L;gt^_GL`()o{mHDrU_-#)ySyKqxr9(WEgTv+-P{VYY;Mn1K0KhCRo?fii{ z=qY`N)HN7E0A8|88=W;veLZf?bFWB9ZS8f71f~RBu`)2muT$sND#*9u(3c(UIA@5V z5`^#3<0v%?-8tpYKxtOYPgt(Bqpze@QtqNmgm*G$8^fojCVqY6pl8J8!Mf0Vk7?^k z;rYBmxU!s1pi7qJ%yqJfEyW@WD0lOYE^wzFJSQHEjb|xmyFjfO3DoNEpXiOik^l6W zbDRFG&nlDZe$2nHT-riOJmzdiR%0_Eiu4))J}=$Yuq+|ejpE0?HEK8!@eix!!bfP< z-cy+7nmOEtB;=~qLxtW1a|BkuIp3z2PV%7ZS?+-puE65H}^PzqKnAvH)3|XlORG{KZ!#>%M{={2|ihn|BfKX&7oaX(KAn>b{5qdX>QaXj}Q}sGf)} zq-<{JB0OMyrROsz45sXSO~uka8gep!MF(Oi#?2zXMDBiNK;$OWjappnXg@*@9pVUR zOo!ul97|S|U5S7Yt!}BW>Q7e2UO0EWqE!F#XfSWk>?tt1U)LXw1BhGJ`-MQ`6R8}$ zK>r=1^4=Ndd)N|kP)Tl@)35#@=2+*>GF?!?ljaaS@iF7$XTWQZNmB@I;1q*#_GM}C#Pe!6tgzBrr7(ae6hSU3 zwH3iVn|+4?b)*;R5)sjti|}!edTN8oI6z@)>rftI%Cdl#PLzg-5VB_B8dU|(ds@PP z*}B)rE$#a{UR7yz%@rA;Ix+)=$lC|>>_w}C4bC9XMlIFKlMF(!wV?z-wd`ZpNHheY zSbEKGX5%xC1Loxy(vh+kD}^-^aaS*xIup9Fp(7-!NVZmTQKu>O%W6*xD-IMCW(k-8 z8Wl+;@93G+%F$0U#SmMHy+OTIU;}PAe#+FyWEL?B3obiV3HWxT0(ItCaYMR<@hx9M z>Crb&v&q@Pkrfq`Ib#DRXSP&ay5-wP0wN5d&*Pjz+ih!S0Ke|0EOC)o!}ALXr)H)} zz2{npjwy;q9!FWgYh^Wp7_ofVR0;BG$QP*iaZ4oEp-}qovNMyri$5k9?%f|3J))mbsc7h4Of4v#5?C!YG&>1ZJkH;Ql|>{jpZenC9_}qB z0&eTL9|6+KQ=wOEK0z$caT^{9LIUSgr2ua0KZx`PwhB&%BdWEae;mo1Ii#IxnO{uT zv8XK(wQ^jmEV&#ihod(#kzNPp-P<|I(sI#H`{SwqgWLiUG2+j0-lSLH1i0;ZKi42O zfF{Q){XPyoX?|;6=rJK4 zdn204l;^K>cm7S$+s;R?FI#-8KO8rL4wX#rGeZ-W_SP8~3OfwKq=*tuJi4oM%BJcC zu6|LqqaezWtI_hydFx#I?%>m=)s zh4w8m+#Qkx`UI9g3+1uWw>m&swwQoGnKqy~8u-RM$B@o|RhM=o6u)2e>IoKV5oNBk z=sWdiq>Q#Wqa6ZL`#Pg{|BPp?C@V$la;s>|=9JYj@ltnWs)%gIxC}VCpG=!ce-`>V zCakHYU@Ee6pwA%81FM{{pk)4qBQpeFgtRi`LbNdlEc-OIHy`P{HAW+h|NJ&`j?GG| z|CC_-Q9a{cl+01}`SA)j-Yp!~#SPlf|IR(LA}zlGf0up|mIb%yr8MXE79(0-u=K4Jo?n&umE=7Q95VLL*hp1^PyrRzN=ZvHEi38{f+ zPoK=PQTkb^uuA}+62l%`Iu)FM-N3L7#wahAQ&4G9Md?f38VXh(UZXD1i(4wTa z16+Q+r&%MN1H?;_@uyOds?CDi0aXry?XXH;G5a^f1m9q}=B2=Jjsw1r8nO?*MQuI) zI#97z9Lg+>1?J4MF!9u#AzogS)DaVzi?mc#0!FJE#49lAq;Fj}62Xx9clm(=>(lA9 zoPKPG-X^sTqfQxSBX4a6F0)QnFt9J#2SjS1S3=`wfjXDVh~PEx1DTjyRvmT~$&aX) z6r)g_vZn4@18HeXiX40jcML~s@o?CS6p+&JF_ueq?a(g@b^+L?oR0O92C0M5z6v{jcclUfSH@B`1M75 zSiw3en9pK2EnXz_c1dBrmy`OuFl5sLH+*v-GOhk&Cqe4nM<4+HB{UK8($=&(j55#z zzL(!}W+XseOFlveu6EdloxH!y_^0%S)S?O0Og@*W^(7_s-OW48*-5!n+1F8_u{g7_ zHwnMFr2s;47NRYwqvjIUBE$7LyQlqL-G@vpO6PSFV+Duax(U6})+TFs7Uvkq7S8LX&LzFpNf1Q9wtCHz9YpZghba zgu5Guh2(F>qG9zTjr$4gD0kjj5W%BuFy;cpS&kQNVbYD+#N9ml(oRL$u;Q77RQ+C$ z#tRn0#G8nW6~a;IOXYnmEhR#n^h5- z=;m<&K?_AAWhPAFN#op2daj657ts@{X0pnQ#d`N?vl`70@ZzzSTTaIZoq|P96@=Yq zt@|${NyD6sCD+Lh$njLak4TFdHZ`G6aX6c;ltYb)NzSb3Y(vr25z=6l?{TxQU} zSS3EiAN2>Ahg{@=f&yIvdyHE6wXG4BePz#SuV=@rq!2Tv3d?QAYF~)kf^D)tve+X3 zZ)VTGYJ_XcBni$vxy+(9?lk1Nd8*Jpm9&R^Tly`w=aQ8eC@3rq7d)(FMsUEFrTmo# z<(PmqLDxM7RUH5|mx9~vYAS_wb^$9=Out1fA{7cOmHI=kblU|D)?2xb zTqJN>4fZwX4d(I()7~95GXxr(OA$rw&A3#@}B{Ez=}54}&M9 zDC#d^k04)cs|a9V=C`;Go9#fchh&)=p>eQK)V$WgG#cPYL?oI>o(-;xsdMiOtCJEN zQPkZmYI1q5JTf2hx-? zbp(s$_#VIPe}uqjzOLiXTZM6yZ?nYj|NJmqe1$#Ls+ne8=wQB1dxRi|^BS+5E8rNJ zUSrVp{Nwo|ELAbosSsvKNn{~uFbCT^tW^JN>(Opw5>1Y%*okZW2)43r^v;_6R%*~& zBtO-;)!i2Iem(3e>mPZmcukC?I)zkDe}LU1*~;y_7)Q@f5#%n1d+p8`9*nhLsRG;K zmXV%(Tc8wuc%nZ2&*YdnfFypfivxjM<&IPmugAcmINFzn(@|Zak0k>(BVw=j$R<_! zqwLdc8qs@C$!$&Y6?@f5ywXaE#Tf`%A)n?WlUOUspccdV^N^Ff8oXSau;wb`O*2?B zFCDW7nHm*FkwS`}{5PtVSe$y&Z9#?&85}TD38AZi}?%T_gUv z1Z|=r&PhsuF1M)#D|%7r=kvs8viaP{&cXfsdl6<`=Am_0_M=*O50DQ7EEtz0w<(#A zVt@ylc(NDQ9h=xBY-nq^`k{9y|FqTI2Qa+j%R9~PyefKlGyAibY^ho8mWZ0vW$Z$d zfpFX$|C!$0Ee1#65+`ANMn=or%;ir$&~<3ES2TcZ$xc`45ol!`TK&99muk+Nti(SF z4wGFgEq{VkX=d@s`Z6#G@12uiRfeNmAN8D950^HNS5MU zt-iF{+;0_kV*?<`YZ)7BSHV~0-EtRb^kENfdnLlC4Lz3Q5$}(>n#WE^FoAfqP#DB$ z8Q?SgcxE~z&nXIL*(H69x*P*hB3+Yk(IizArcwj76JkMO+>|kdhH+!tE3{7S5s$mb z_pX0;$0ekhV5WefG}it+vUQGW!@C=*nU+RsmRd|$ilEM@4|#?ZM`N|OR2nWqW=1_f>IP#(bxoY5S9JY&qL(fC3Pv$YO5f)ji2_ktE5#DtALLLLHuaR&VL(-x)w~X zTOL0&q>^xX%?#AY2b=6b)Ldc!P{U{482McqSV^KcwC;mDj>NS*VRmIigAq>BYYdvo z5`A!(5qFc5B4J-6Zz`JCYl@PMZtV%EpP`37vadyHCC#>#_iBv_;dej8SdiZ(K@(AS zLB&t9gE(oW2OKdq0{F+1Di)@mFv!X+W6u*X#U1EvbV5>lSom5g;Z-=F$&~=Z1LxzM zJSEKYcOjTw7>tg@{;)DQ)m#!`p^A@ba=MqBt0?9+Eh4yAQJ*1UZ;5-fX1k`6tOZo# zM@mF@LITvI;Ycw((>+~IlfuB1P%2QAwXqeF0>bT*&qV!GpV(up@AmDE8QVA$Owo^4 zw@u~Yk^&Y|s?3iFU*~ zMsj{qMOl=SVG-#ai(88$C1~>Vt{yqhg=#XW1;F&!#S( zg+qI`uqx13|8{>GNik5>xIP$%iS$!YO!88Y8@soB=f)3+ZAkBGaGem)aANf)Q!1aQ zSA%3Ns0KpQuK)9HG~Zr=Qv*Ik0XScrST=!<@_)s{kqd5&3+)5YQi?ORVZm!G%!~Xc z0P8W#e^~qilbG{;vXO5E!-ph46pSqyrwzQBUYf=bTdFnF%fQg^w;Efs#&H1eS7S+-FY0Wq92mUdEFR~yfkK!+wWo0a2Fq}+ z)sHHln8FO}7Cpp@I&ftc$ynG9N;+5CPC2P8sS)Z2`QqxHR zJ}R)C9?@aX-E1+eFf&l;C2;;{8f?8TQuY3cnkRUKP2u}_z z_4NnYF$>(B8rUba+siAuq6$)DOs@AI*GIH@8aB?@|3_qLxuNPA{J5tTa8T)9RB}LO zDm(fdsKV(PWsD;K#Jxei7gSQcDP!(;Gz9tf_fQb6kpA#~R_^&oe;w7+`=Cen^f90- z^Jky)L@RM9a4^^rftFV8X7mGA)Zv?w z$x)a^UP|vuatzXSRz$D`KKrTn7QFh$UM6=j=;(*DE!Ss86tnX4aB9)7*dU!eRK&}kgSPDeHYj^pSj?Btq1@eF@3D(VRNtQ z9C4~_G0Pw?uoD3`!t7J< z+K_)zAFlUA1WGTgvP(9UfV^FHoRlKR&LOEE8rsd#bls6bQE(a{Jg-5ve#RXG05P&cLt(;99fyVebg@|$E?W`VA@nQ}=~@TN$uj{qqoU~q z!?;R>^=1z{n;PALG_o|y6crT&{>+_uP?eS{^xx*)A=Qo20_Y9imlDkTx}}sw9W?qS zT&|RZsWcKL8*2prsz9OtR$FZXLxz}p`j8uvFsBR?HG84%G;!V7kx6xM+i+BM%<200 zC}`*lvh~}dB%nCt437s543yAnBvrL$PMu62yRW3*<4rT$O-{&~P0Mj}%jsCP_E%pS#Hwh+pSnrkaex@0*o(Y1cZ9H zZUkr**DGZP^60TQR38?iqb|aMFx|Q>lTtA63FKo~;Dn

S4(X>_?%R)m8c%oYt9$ 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 0 else image_files + + # Shard the data + shard_size = len(image_files) // dist_size + start_index = shard_size * dist_rank + end_index = start_index + shard_size if dist_rank < dist_size - 1 else len(image_files) + shard_files = image_files[start_index:end_index] + + print(f"Querying {len(shard_files)} images from index {start_index} to {end_index - 1}") + + # Select backend + backend = select_sglang_backend(args) + sgl.set_default_backend(backend) + + tic = time.time() + batch_size = args.parallel + for batch_start in tqdm.tqdm(range(0, len(shard_files), batch_size)): + batch_end = min(batch_start + batch_size, len(shard_files)) + if batch_start <= progress_data.get("last_index", -1): + print(f"Skipping already processed batch starting at {batch_start}") + continue + batch_arguments = [{"image_file": image_file} for image_file in shard_files[batch_start:batch_end]] + try: + batch_states = image_description.run_batch(batch_arguments, temperature=0, num_threads=args.parallel, progress_bar=False) + for i, ret in enumerate(batch_states): + image_file = batch_arguments[i]["image_file"] + caption = ret.text().split("ASSISTANT:")[-1].strip() + progress_data["annotations"].append({"image_file": image_file, "caption": caption}) + progress_data["last_index"] = batch_start + i # Update last_index relative to this rank's shard + + save_progress(progress_file, progress_data) + except Exception as e: + print(f"Error during batch processing: {e}") + save_progress(progress_file, progress_data) + break + + latency = time.time() - tic + print(f"Latency: {latency:.3f}") + + value = { + "task": "image_captioning", + "backend": args.backend, + "num_gpus": 1, + "latency": round(latency, 3), + "num_requests": len(shard_files), + "parallel": args.parallel, + "results": progress_data["annotations"], + } + + result_file = args.result_file.replace(".json", f"_shard_{dist_rank}_or_{dist_size}.json") + print(f"Write output to {result_file}") + with open(result_file, "w") as fout: + json.dump(value, fout, indent=2) + + save_progress(progress_file, progress_data) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--images_root", type=str, default="/mnt/bn/vl-research/data/llava_data/cc3m") + parser.add_argument("--json_path", type=str, default="/mnt/bn/vl-research/data/llava_instruct/cc3m_recap_requery_363707.json") + parser.add_argument("--max_tokens", type=int, default=1024) + parser.add_argument("--parallel", type=int, default=32) + parser.add_argument("--backend", type=str, default="srt") + parser.add_argument("--host", type=str, default="http://127.0.0.1") + parser.add_argument("--port", type=int, default=30000) + parser.add_argument("--result_file", type=str, default="/mnt/bn/vl-research/workspace/boli01/projects/LLaVA_Next/playground/sgl_llava_inference.json") + parser.add_argument("--limit", type=int, default=-1) + parser.add_argument("--dist", type=int, default=0, help="The rank of the distributed machine") + parser.add_argument("--total_dist", type=int, default=6, help="Total number of distributed machines") + args = parser.parse_args() + main(args) diff --git a/playground/upload_data.py b/playground/upload_data.py new file mode 100644 index 0000000..d1acf2b --- /dev/null +++ b/playground/upload_data.py @@ -0,0 +1,217 @@ +from datasets import Dataset, Features, Value, ClassLabel, Sequence, Image +import json +import PIL.Image as pil_image +from io import BytesIO +from tqdm import tqdm + +json_paths = [ + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/mavis_math_metagen_87358.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/mavis_math_rule_geo_100000.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/k12_printing_train_256646.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/iiit5k_annotations_2000.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/hme100k_train_clean_74502.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/ai2d_azuregpt_detailed_understanding_4874.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/infographic_vqa_4404.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/infographic_azuregpt4v_1992.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/lrv_chart_1787.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/lrv_normal_gpt4v_filtered_10500.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/scienceqa_nona_context_19218.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/allava_instruct_vflan4v_20000.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/allava_instruct_laion4v_50000.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/textocr_gpt4v_train_converted_25114.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/ai2d_train_internvl_single_12413.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/textcaps_train_21952.json", + # "/mnt/bn/vl-research/data/llava_instruct/ureader_new/ureader_qa_sft.json", + # "/mnt/bn/vl-research/data/llava_instruct/ureader_new/ureader_cap_sft.json", + # "/mnt/bn/vl-research/data/llava_instruct/ureader_new/ureader_ie_sft.json", + # "/mnt/bn/vl-research/data/llava_instruct/ureader_new/ureader_kg_sft.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/vision_flan_filtered_186070.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/mathqa_29837.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/geo3k_2101.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/geo170k_qa_converted_67833.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/geo170k_align_converted_60252.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4v-coco-50k.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4v-knowledge-2k.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4v-llava-30k.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4v-sam-20k.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_CLEVR-Math_5290.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_FigureQA_17597.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_Geometry3K_9734.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_GeoQA+_17172.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_GEOS_508.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_IconQA_22599.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_MapQA_5235.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_PMC-VQA_35958.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_Super-CLEVR_8652.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_TabMWP_22462.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_UniGeo_11959.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_VizWiz_6614.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/magpie_pro_qwen2_72b_st_300000_sp_token_fltd_299992.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/magpie_pro_l3_80b_st_300000.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/magpie_pro_l3_80b_mt_300000_sp_token_fltd_299998.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/image_textualization_dataset_filtered.json", + # "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/cambrian_filtered_gpt4vo_sp_token_fltd_max10k.json", + "/mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4o_dataset.jsonl", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/ai2d_llava_format_2434.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/aokvqa_16539_llava_format.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/chart2text_26961.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/chartqa_18265_llava_format.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/clevr_70000_llava_format.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/diagram_image_to_text_300.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/dvqa_200000_llava_format.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/figureqa_100000_llava_format.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/geomverse_9303.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/hateful_memes_8500_llava_format.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/hitab_2500_llava_format.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/iam_5663.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/raven_42000.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/iconqa_llava_format_27307.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/infographic_vqa_2118_llava_format.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/intergps_1280_llava_format.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/mapqa_37417_llava_format.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/multihiertt_7619.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/rendered_text_10000.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/robut_sqa_8514.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/robut_wikisql_74989.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/robut_wtq_38246_llava_format.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/screen2words_15730.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/scienceqa_llava_format_4976.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/tabmwp_22722.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/tallyqa_98680_llava_format.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/st_vqa_17247_llava_format.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/tqa_llava_format_27307.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/visual7w_llava_format_14366.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/visualmrc_3027.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/vqarad_313_llava_format.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/vsr_2157_llava_format.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/vistext_9969.json", + "/mnt/bn/vl-research/data/llava_instruct/cauldron/websight_10000.json" +] + +short_names = [ + # "mavis_math_metagen", + # "mavis_math_rule_geo", + # "k12_printing", + # "iiit5k", + # "hme100k", + # "ai2d(gpt4v)", + # "infographic_vqa", + # "infographic(gpt4v)", + # "lrv_chart", + # "lrv_normal(filtered)", + # "scienceqa(nona_context)", + # "allava_instruct_vflan4v", + # "allava_instruct_laion4v", + # "textocr(gpt4v)", + # "ai2d(internvl)", + # "textcaps", + # "ureader_qa", # need to re-upload + # "ureader_cap", # need to re-upload + # "ureader_ie", # need to re-upload + # "ureader_kg", # need to re-upload + # "vision_flan(filtered)", + # "mathqa", + # "geo3k", + # "geo170k(qa)", + # "geo170k(align)", + # "sharegpt4v(coco)", + # "sharegpt4v(knowledge)", + # "sharegpt4v(llava)", + # "sharegpt4v(sam)", + # "CLEVR-Math(MathV360K)", + # "FigureQA(MathV360K)", + # "Geometry3K(MathV360K)", + # "GeoQA+(MathV360K)", + # "GEOS(MathV360K)", + # "IconQA(MathV360K)", + # "MapQA(MathV360K)", + # "PMC-VQA(MathV360K)", + # "Super-CLEVR(MathV360K)", + # "TabMWP(MathV360K)", + # "UniGeo(MathV360K)", + # "VizWiz(MathV360K)", + # "magpie_pro(qwen2_72b_st)", + # "magpie_pro(l3_80b_st)", + # "magpie_pro(l3_80b_mt)", + # "image_textualization(filtered)", + # "cambrian(filtered_gpt4vo)", # need to re-upload + "sharegpt4o", + "ai2d(cauldron,llava_format)", + "aokvqa(cauldron,llava_format)", + "chart2text(cauldron)", + "chartqa(cauldron,llava_format)", + "clevr(cauldron,llava_format)", + "diagram_image_to_text(cauldron)", + "dvqa(cauldron,llava_format)", + "figureqa(cauldron,llava_format)", + "geomverse(cauldron)", + "hateful_memes(cauldron,llava_format)", + "hitab(cauldron,llava_format)", + "iam(cauldron)", + "raven(cauldron)", + "iconqa(cauldron,llava_format)", + "infographic_vqa_llava_format", + "intergps(cauldron,llava_format)", + "mapqa(cauldron,llava_format)", + "multihiertt(cauldron)", + "rendered_text(cauldron)", + "robut_sqa(cauldron)", + "robut_wikisql(cauldron)", + "robut_wtq(cauldron,llava_format)", + "screen2words(cauldron)", + "scienceqa(cauldron,llava_format)", + "tabmwp(cauldron)", + "tallyqa(cauldron,llava_format)", + "st_vqa(cauldron,llava_format)", + "tqa(cauldron,llava_format)", + "visual7w(cauldron,llava_format)", + "visualmrc(cauldron)", + "vqarad(cauldron,llava_format)", + "vsr(cauldron,llava_format)", + "vistext(cauldron)", + "websight(cauldron)" +] + +def upload_data(json_path, short_name): + def gen(): + if json_path.endswith(".jsonl"): + with open(json_path, "r") as f: + data = [json.loads(line) for line in f] + else: + with open(json_path, "r") as f: + data = json.load(f) + + preview_index = 5 + idx = 0 + for item in tqdm(data): + if preview_index > 0: + preview_index -= 1 + print(item) + continue + + try: + if "image" in item: + image_path = f"/mnt/bn/vl-research/data/llava_data/{item['image']}" + try: + with open(image_path, "rb") as img_file: + image = pil_image.open(BytesIO(img_file.read())) + except: + print(f"Failed to load image {item['image']}") + continue + else: + image = None + + item_id = item["id"] if "id" in item else f"{idx:06d}" + yield {"id": item_id, "image": image, "conversations": item["conversations"], "data_source": short_name} + idx += 1 + + except Exception as e: + print(e) + continue + + + hf_dataset = Dataset.from_generator(generator=gen, num_proc=32) + hf_dataset.push_to_hub("lmms-lab/LLaVA-OneVision-Data", config_name=short_name, split="train") + +for json_path, short_name in zip(json_paths, short_names): + upload_data(json_path, short_name) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..23044a9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,101 @@ +[tool.black] +line-length = 240 + +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "llava" +version = "1.7.0.dev0" +description = "LLaVA OneVision: The Next Generation of LLaVA with Better Image and Video Understanding Capabilities" +readme = "README.md" +requires-python = ">=3.8" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: Apache Software License", +] + +[project.optional-dependencies] +standalone = [ + "shortuuid", + "httpx==0.24.0", + "einops", + "ftfy", +] + + +train = [ + "llava[standalone]", + "numpy==1.26.1", + "open_clip_torch", + "fastapi", + "markdown2[all]", + "numpy", + "requests", + "sentencepiece", + "torch==2.1.2", + "torchvision==0.16.2", + "uvicorn", + "wandb", + "deepspeed==0.14.4", + "peft==0.4.0", + "accelerate>=0.29.1", + "tokenizers~=0.15.2", + "transformers@git+https://github.com/huggingface/transformers.git@1c39974a4c4036fd641bc1191cc32799f85715a4", + "bitsandbytes==0.41.0", + "scikit-learn==1.2.2", + "sentencepiece~=0.1.99", + "einops==0.6.1", + "einops-exts==0.0.4", + "gradio_client==0.2.9", + "urllib3<=2.0.0", + "datasets==2.16.1", + "pydantic==1.10.8", + "timm", + "hf_transfer", + "opencv-python", + "av", + "decord", + "tyro", + "scipy", +] + +[project.urls] +"Homepage" = "https://llava-vl.github.io" +"Bug Tracker" = "https://github.com/haotian-liu/LLaVA/issues" + +[tool.setuptools.packages.find] +include = ["llava*", "trl*"] +exclude = [ + "assets*", + "benchmark*", + "docs", + "dist*", + "playground*", + "scripts*", + "tests*", + "checkpoints*", + "project_checkpoints*", + "debug_checkpoints*", + "mlx_configs*", + "wandb*", + "notebooks*", +] + +[tool.wheel] +exclude = [ + "assets*", + "benchmark*", + "docs", + "dist*", + "playground*", + "scripts*", + "tests*", + "checkpoints*", + "project_checkpoints*", + "debug_checkpoints*", + "mlx_configs*", + "wandb*", + "notebooks*", +] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..66ea557 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,324 @@ +Babel==2.14.0 +DataProperty==1.0.1 +Deprecated==1.2.14 +GitPython==3.1.43 +Jinja2==3.1.3 +Levenshtein==0.25.1 +MarkupSafe==2.1.5 +PyJWT==2.8.0 +PyYAML==6.0.1 +Pygments==2.17.2 +QtPy==2.4.1 +Send2Trash==1.8.3 +absl-py==2.1.0 +accelerate==0.29.3 +aiofiles==22.1.0 +aiohttp==3.9.5 +aiosignal==1.3.1 +aiosqlite==0.20.0 +altair==5.3.0 +anyio==4.3.0 +appdirs==1.4.4 +argon2-cffi-bindings==21.2.0 +argon2-cffi==23.1.0 +arrow==1.3.0 +asttokens==2.4.1 +async-timeout==4.0.3 +attrs==23.1.0 +beautifulsoup4==4.12.3 +bidict==0.23.1 +bitsandbytes==0.41.0 +black==24.1.0 +bleach==6.1.0 +byted-remote-ikernel==0.4.8 +byted-torch-monitor==0.0.1 +byted-wandb==0.13.72 +bytedance-context==0.7.1 +bytedance-metrics==0.5.1 +bytedance.modelhub==0.0.64 +bytedance.servicediscovery==0.1.2 +bytedbackgrounds==0.0.6 +byteddatabus==1.0.6 +byteddps==0.1.2 +bytedenv==0.6.2 +bytedlogger==0.15.1 +bytedmemfd==0.2 +bytedmetrics==0.10.2 +bytedpymongo==2.0.5 +bytedrh2==1.18.7a2 +bytedservicediscovery==0.17.4 +bytedtcc==1.4.2 +bytedtos==1.1.16 +bytedtrace==0.3.0 +bytedztijwthelper==0.0.22 +bytedztispiffe==0.0.11 +certifi==2024.2.2 +cffi==1.16.0 +cfgv==3.4.0 +chardet==5.2.0 +charset-normalizer==3.3.2 +click==8.1.7 +colorama==0.4.6 +comm==0.2.2 +contourpy==1.2.1 +crcmod==1.7 +cryptography==38.0.4 +cycler==0.12.1 +datasets==2.16.1 +debugpy==1.8.1 +decorator==5.1.1 +decord==0.6.0 +deepspeed==0.12.2 +defusedxml==0.7.1 +dill==0.3.7 +distlib==0.3.8 +distro==1.9.0 +dnspython==2.6.1 +docker-pycreds==0.4.0 +docstring_parser==0.16 +einops-exts==0.0.4 +einops==0.6.1 +entrypoints==0.4 +et-xmlfile==1.1.0 +eval_type_backport==0.2.0 +evaluate==0.4.1 +exceptiongroup==1.2.1 +executing==2.0.1 +fastapi==0.110.2 +fastjsonschema==2.19.1 +ffmpy==0.3.2 +filelock==3.13.4 +flash-attn==2.5.7 +fonttools==4.51.0 +fqdn==1.5.1 +frozenlist==1.4.1 +fsspec==2023.10.0 +ftfy==6.2.0 +gitdb==4.0.11 +gradio==3.35.2 +gradio_client==0.2.9 +grpcio==1.62.2 +h11==0.14.0 +hf_transfer==0.1.6 +hjson==3.1.0 +httpcore==0.17.3 +httpx==0.24.0 +huggingface-hub==0.22.2 +identify==2.5.36 +idna==3.7 +importlib_metadata==7.1.0 +importlib_resources==6.4.0 +iniconfig==2.0.0 +ipaddress==1.0.23 +ipykernel==6.29.4 +ipython-genutils==0.2.0 +ipython==8.18.1 +ipywidgets==8.1.2 +isoduration==20.11.0 +jedi==0.19.1 +joblib==1.4.0 +json5==0.9.25 +jsonlines==4.0.0 +jsonpointer==2.4 +jsonschema-specifications==2023.12.1 +jsonschema==4.21.1 +jupyter-client==7.0.0 +jupyter-console==6.6.3 +jupyter-events==0.10.0 +jupyter-ydoc==0.2.5 +jupyter==1.0.0 +jupyter_core==5.7.2 +jupyter_server==2.14.0 +jupyter_server_fileid==0.9.2 +jupyter_server_terminals==0.5.3 +jupyter_server_ydoc==0.8.0 +jupyterlab==3.6.4 +jupyterlab_pygments==0.3.0 +jupyterlab_server==2.27.1 +jupyterlab_widgets==3.0.10 +kiwisolver==1.4.5 +linkify-it-py==2.0.3 +llava==1.7.0.dev0 +llava==1.7.0.dev0 +lmms_eval==0.1.1 +lxml==5.2.1 +markdown-it-py==2.2.0 +markdown2==2.4.13 +matplotlib-inline==0.1.7 +matplotlib==3.8.4 +mbstrdecoder==1.1.3 +mdit-py-plugins==0.3.3 +mdurl==0.1.2 +mistune==3.0.2 +mpmath==1.3.0 +msgpack==1.0.8 +multidict==6.0.5 +multiprocess==0.70.15 +mypy-extensions==1.0.0 +nbclassic==1.0.0 +nbclient==0.10.0 +nbconvert==7.16.3 +nbformat==5.10.4 +nest-asyncio==1.6.0 +networkx==3.2.1 +ninja==1.11.1.1 +nltk==3.8.1 +nodeenv==1.8.0 +notebook==6.5.6 +notebook_shim==0.2.4 +numexpr==2.10.0 +numpy==1.26.4 +nvidia-cublas-cu12==12.1.3.1 +nvidia-cuda-cupti-cu12==12.1.105 +nvidia-cuda-nvrtc-cu12==12.1.105 +nvidia-cuda-runtime-cu12==12.1.105 +nvidia-cudnn-cu12==8.9.2.26 +nvidia-cufft-cu12==11.0.2.54 +nvidia-curand-cu12==10.3.2.106 +nvidia-cusolver-cu12==11.4.5.107 +nvidia-cusparse-cu12==12.1.0.106 +nvidia-nccl-cu12==2.18.1 +nvidia-nvjitlink-cu12==12.4.127 +nvidia-nvtx-cu12==12.1.105 +open-clip-torch==2.24.0 +openai==1.23.6 +opencv-python-headless==4.9.0.80 +openpyxl==3.1.2 +orjson==3.10.1 +overrides==7.7.0 +packaging==24.0 +pandas==2.2.2 +pandocfilters==1.5.1 +parso==0.8.4 +pathlib2==2.3.7.post1 +pathspec==0.12.1 +pathtools==0.1.2 +pathvalidate==3.2.0 +peft==0.4.0 +pexpect==4.8.0 +pillow==10.3.0 +pip==23.3.1 +pip==24.0 +platformdirs==4.2.1 +pluggy==1.5.0 +ply==3.11 +portalocker==2.8.2 +pre-commit==3.7.0 +prometheus_client==0.20.0 +promise==2.3 +prompt-toolkit==3.0.43 +protobuf==3.20.3 +psutil==5.9.8 +ptyprocess==0.7.0 +pure-eval==0.2.2 +py-cpuinfo==9.0.0 +py-spy==0.3.14 +py==1.11.0 +pyOpenSSL==22.1.0 +pyarrow-hotfix==0.6 +pyarrow==16.0.0 +pybind11==2.12.0 +pycocoevalcap==1.2 +pycocotools==2.0.7 +pycparser==2.22 +pycryptodomex==3.20.0 +pydantic==1.10.8 +pydub==0.25.1 +pynvml==11.5.0 +pyparsing==3.1.2 +pytablewriter==1.2.0 +pytest==6.2.5 +python-consul==1.1.0 +python-dateutil==2.9.0.post0 +python-engineio==4.9.0 +python-etcd==0.4.5 +python-json-logger==2.0.7 +python-multipart==0.0.9 +python-socketio==5.11.2 +pytz==2024.1 +pyzmq==24.0.1 +qtconsole==5.5.1 +rapidfuzz==3.8.1 +referencing==0.35.0 +regex==2024.4.16 +requests==2.31.0 +responses==0.18.0 +rfc3339-validator==0.1.4 +rfc3986-validator==0.1.1 +rich==13.7.1 +rouge-score==0.1.2 +rpds-py==0.18.0 +sacrebleu==2.4.2 +safetensors==0.4.3 +schedule==1.2.1 +scikit-learn==1.2.2 +scipy==1.13.0 +semantic-version==2.10.0 +sentencepiece==0.1.99 +sentry-sdk==2.0.0 +setproctitle==1.3.3 +setuptools==68.2.2 +shortuuid==1.0.13 +shtab==1.7.1 +simple-websocket==1.0.0 +six==1.16.0 +smmap==5.0.1 +sniffio==1.3.1 +soupsieve==2.5 +sqlitedict==2.1.0 +stack-data==0.6.3 +starlette==0.37.2 +svgwrite==1.4.3 +sympy==1.12 +tabledata==1.3.3 +tabulate==0.9.0 +tcolorpy==0.1.4 +tenacity==8.2.3 +terminado==0.18.1 +threadpoolctl==3.4.0 +thriftpy2==0.4.20 +tiktoken==0.6.0 +timm==0.9.16 +tinycss2==1.3.0 +tokenizers==0.15.2 +toml==0.10.2 +tomli==2.0.1 +toolz==0.12.1 +torch==2.1.2 +torchvision==0.16.2 +tornado==6.4 +tox==3.28.0 +tqdm-multiprocess==0.0.11 +tqdm==4.66.2 +traitlets==5.14.3 +transformers-stream-generator==0.0.5 +transformers==4.40.0.dev0 +triton==2.1.0 +typepy==1.3.2 +types-python-dateutil==2.9.0.20240316 +typing_extensions==4.11.0 +tyro==0.8.3 +tzdata==2024.1 +uc-micro-py==1.0.3 +uri-template==1.3.0 +urllib3==2.2.1 +uvicorn==0.29.0 +virtualenv==20.26.0 +wandb==0.16.5 +watchdog==4.0.0 +wavedrom==2.0.3.post3 +wcwidth==0.2.13 +webcolors==1.13 +webencodings==0.5.1 +websocket-client==1.8.0 +websockets==12.0 +wheel==0.41.2 +widgetsnbextension==4.0.10 +wrapt==1.16.0 +wsproto==1.2.0 +xxhash==3.4.1 +y-py==0.6.2 +yarl==1.9.4 +ypy-websocket==0.8.4 +zipp==3.18.1 +zstandard==0.22.0 \ No newline at end of file diff --git a/scripts/archived/convert_gqa_for_eval.py b/scripts/archived/convert_gqa_for_eval.py new file mode 100644 index 0000000..2842975 --- /dev/null +++ b/scripts/archived/convert_gqa_for_eval.py @@ -0,0 +1,18 @@ +import os +import json +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument("--src", type=str) +parser.add_argument("--dst", type=str) +args = parser.parse_args() + +all_answers = [] +for line_idx, line in enumerate(open(args.src)): + res = json.loads(line) + question_id = res["question_id"] + text = res["text"].rstrip(".").lower() + all_answers.append({"questionId": question_id, "prediction": text}) + +with open(args.dst, "w") as f: + json.dump(all_answers, f) diff --git a/scripts/archived/convert_mmvet_for_eval.py b/scripts/archived/convert_mmvet_for_eval.py new file mode 100644 index 0000000..80ff315 --- /dev/null +++ b/scripts/archived/convert_mmvet_for_eval.py @@ -0,0 +1,18 @@ +import os +import json +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument("--src", type=str) +parser.add_argument("--dst", type=str) +args = parser.parse_args() + +cur_result = {} + +for line in open(args.src): + data = json.loads(line) + qid = data["question_id"] + cur_result[f"v1_{qid}"] = data["text"] + +with open(args.dst, "w") as f: + json.dump(cur_result, f, indent=2) diff --git a/scripts/archived/convert_sqa_to_llava.py b/scripts/archived/convert_sqa_to_llava.py new file mode 100644 index 0000000..b0d2422 --- /dev/null +++ b/scripts/archived/convert_sqa_to_llava.py @@ -0,0 +1,88 @@ +import json +import os +import fire +import re +from convert_sqa_to_llava_base_prompt import build_prompt_chatbot + + +def convert_to_llava(base_dir, split, prompt_format="QCM-LEA"): + split_indices = json.load(open(os.path.join(base_dir, "pid_splits.json")))[split] + problems = json.load(open(os.path.join(base_dir, "problems.json"))) + + split_problems = build_prompt_chatbot(problems, split_indices, prompt_format, use_caption=False, is_test=False) + + target_format = [] + for prob_id, (input, output) in split_problems.items(): + if input.startswith("Question: "): + input = input.replace("Question: ", "") + if output.startswith("Answer: "): + output = output.replace("Answer: ", "") + + raw_prob_data = problems[prob_id] + if raw_prob_data["image"] is None: + target_format.append( + { + "id": prob_id, + "conversations": [ + {"from": "human", "value": f"{input}"}, + {"from": "gpt", "value": f"{output}"}, + ], + } + ) + + else: + target_format.append( + { + "id": prob_id, + "image": os.path.join(prob_id, raw_prob_data["image"]), + "conversations": [ + {"from": "human", "value": f"{input}\n"}, + {"from": "gpt", "value": f"{output}"}, + ], + } + ) + + print(f"Number of samples: {len(target_format)}") + + with open(os.path.join(base_dir, f"llava_{split}_{prompt_format}.json"), "w") as f: + json.dump(target_format, f, indent=2) + + +def convert_to_jsonl(base_dir, split, prompt_format="QCM-LEPA"): + split_indices = json.load(open(os.path.join(base_dir, "pid_splits.json")))[split] + problems = json.load(open(os.path.join(base_dir, "problems.json"))) + + split_problems = build_prompt_chatbot(problems, split_indices, prompt_format, use_caption=False, is_test=False) + + writer = open(os.path.join(base_dir, f"scienceqa_{split}_{prompt_format}.jsonl"), "w") + for prob_id, (input, output) in split_problems.items(): + if input.startswith("Question: "): + input = input.replace("Question: ", "") + if output.startswith("Answer: "): + output = output.replace("Answer: ", "") + + raw_prob_data = problems[prob_id] + if raw_prob_data["image"] is None: + data = { + "id": prob_id, + "instruction": f"{input}", + "output": f"{output}", + } + + else: + data = { + "id": prob_id, + "image": os.path.join(prob_id, raw_prob_data["image"]), + "instruction": f"{input}\n", + "output": f"{output}", + } + writer.write(json.dumps(data) + "\n") + writer.close() + + +def main(task, **kwargs): + globals()[task](**kwargs) + + +if __name__ == "__main__": + fire.Fire(main) diff --git a/scripts/archived/convert_sqa_to_llava_base_prompt.py b/scripts/archived/convert_sqa_to_llava_base_prompt.py new file mode 100644 index 0000000..9a457dc --- /dev/null +++ b/scripts/archived/convert_sqa_to_llava_base_prompt.py @@ -0,0 +1,298 @@ +def get_question_text(problem): + question = problem["question"] + return question + + +def get_context_text(problem, use_caption): + txt_context = problem["hint"] + img_context = problem["caption"] if use_caption else "" + context = " ".join([txt_context, img_context]).strip() + if context == "": + context = "N/A" + return context + + +def get_choice_text(probelm, options): + choices = probelm["choices"] + choice_list = [] + for i, c in enumerate(choices): + choice_list.append("({}) {}".format(options[i], c)) + choice_txt = " ".join(choice_list) + # print(choice_txt) + return choice_txt + + +def get_answer(problem, options): + return options[problem["answer"]] + + +def get_lecture_text(problem): + # \\n: GPT-3 can generate the lecture with more tokens. + lecture = problem["lecture"].replace("\n", "\\n") + return lecture + + +def get_solution_text(problem): + # \\n: GPT-3 can generate the solution with more tokens + solution = problem["solution"].replace("\n", "\\n") + return solution + + +def create_one_example_chatbot(format, question, context, choice, answer, lecture, solution, test_example=True): + + input_format, output_format = format.split("-") + + ## Inputs + if input_format == "CQM": + input = f"Context: {context}\nQuestion: {question}\nOptions: {choice}\n" + elif input_format == "QCM": + input = f"Question: {question}\nContext: {context}\nOptions: {choice}\n" + # upper bound experiment + elif input_format == "QCML": + input = f"Question: {question}\nContext: {context}\nOptions: {choice}\nBECAUSE: {lecture}\n" + elif input_format == "QCME": + input = f"Question: {question}\nContext: {context}\nOptions: {choice}\nBECAUSE: {solution}\n" + elif input_format == "QCMLE": + input = f"Question: {question}\nContext: {context}\nOptions: {choice}\nBECAUSE: {lecture} {solution}\n" + + elif input_format == "QCLM": + input = f"Question: {question}\nContext: {context}\nBECAUSE: {lecture}\nOptions: {choice}\n" + elif input_format == "QCEM": + input = f"Question: {question}\nContext: {context}\nBECAUSE: {solution}\nOptions: {choice}\n" + elif input_format == "QCLEM": + input = f"Question: {question}\nContext: {context}\nBECAUSE: {lecture} {solution}\nOptions: {choice}\n" + + # Outputs + if test_example: + output = "Answer:" + elif output_format == "A": + output = f"Answer: The answer is {answer}." + + elif output_format == "AL": + output = f"Answer: The answer is {answer}. BECAUSE: {solution}" + elif output_format == "AE": + output = f"Answer: The answer is {answer}. BECAUSE: {lecture}" + elif output_format == "ALE": + output = f"Answer: The answer is {answer}. BECAUSE: {lecture} {solution}" + elif output_format == "AEL": + output = f"Answer: The answer is {answer}. BECAUSE: {solution} {lecture}" + + elif output_format == "LA": + output = f"Answer: {lecture} The answer is {answer}." + elif output_format == "EA": + output = f"Answer: {solution} The answer is {answer}." + elif output_format == "LEA": + output = f"Answer: {lecture} {solution} The answer is {answer}." + elif output_format == "ELA": + output = f"Answer: {solution} {lecture} The answer is {answer}." + elif output_format == "LEPA": + output = "" + if len(lecture.strip()) > 0: + output += f"LECTURE: {lecture}\n" + if len(solution.strip()) > 0: + output += f"SOLUTION: {solution}\n" + output += "###\n" + output += f"ANSWER: {answer}." + + input = input.replace(" ", " ").strip() + output = output.replace(" ", " ").strip() + if input.endswith("BECAUSE:"): + input = input.replace("BECAUSE:", "").strip() + if output.endswith("BECAUSE:"): + output = output.replace("BECAUSE:", "").strip() + return input, output + + +def create_one_example(format, question, context, choice, answer, lecture, solution, test_example=True): + + input_format, output_format = format.split("-") + + ## Inputs + if input_format == "CQM": + input = f"Context: {context}\nQuestion: {question}\nOptions: {choice}\n" + elif input_format == "QCM": + input = f"Question: {question}\nContext: {context}\nOptions: {choice}\n" + # upper bound experiment + elif input_format == "QCML": + input = f"Question: {question}\nContext: {context}\nOptions: {choice}\nBECAUSE: {lecture}\n" + elif input_format == "QCME": + input = f"Question: {question}\nContext: {context}\nOptions: {choice}\nBECAUSE: {solution}\n" + elif input_format == "QCMLE": + input = f"Question: {question}\nContext: {context}\nOptions: {choice}\nBECAUSE: {lecture} {solution}\n" + + elif input_format == "QCLM": + input = f"Question: {question}\nContext: {context}\nBECAUSE: {lecture}\nOptions: {choice}\n" + elif input_format == "QCEM": + input = f"Question: {question}\nContext: {context}\nBECAUSE: {solution}\nOptions: {choice}\n" + elif input_format == "QCLEM": + input = f"Question: {question}\nContext: {context}\nBECAUSE: {lecture} {solution}\nOptions: {choice}\n" + + # Outputs + if test_example: + output = "Answer:" + elif output_format == "A": + output = f"Answer: The answer is {answer}." + + elif output_format == "AL": + output = f"Answer: The answer is {answer}. BECAUSE: {solution}" + elif output_format == "AE": + output = f"Answer: The answer is {answer}. BECAUSE: {lecture}" + elif output_format == "ALE": + output = f"Answer: The answer is {answer}. BECAUSE: {lecture} {solution}" + elif output_format == "AEL": + output = f"Answer: The answer is {answer}. BECAUSE: {solution} {lecture}" + + elif output_format == "LA": + output = f"Answer: {lecture} The answer is {answer}." + elif output_format == "EA": + output = f"Answer: {solution} The answer is {answer}." + elif output_format == "LEA": + output = f"Answer: {lecture} {solution} The answer is {answer}." + elif output_format == "ELA": + output = f"Answer: {solution} {lecture} The answer is {answer}." + + text = input + output + text = text.replace(" ", " ").strip() + if text.endswith("BECAUSE:"): + text = text.replace("BECAUSE:", "").strip() + return text + + +def create_one_example_gpt4(format, question, context, choice, answer, lecture, solution, test_example=True): + + input_format, output_format = format.split("-") + + ## Inputs + if input_format == "CQM": + input = f"Context: {context}\nQuestion: {question}\nOptions: {choice}\n" + elif input_format == "QCM": + input = f"Question: {question}\nContext: {context}\nOptions: {choice}\n" + # upper bound experiment + elif input_format == "QCML": + input = f"Question: {question}\nContext: {context}\nOptions: {choice}\nBECAUSE: {lecture}\n" + elif input_format == "QCME": + input = f"Question: {question}\nContext: {context}\nOptions: {choice}\nBECAUSE: {solution}\n" + elif input_format == "QCMLE": + input = f"Question: {question}\nContext: {context}\nOptions: {choice}\nBECAUSE: {lecture} {solution}\n" + + elif input_format == "QCLM": + input = f"Question: {question}\nContext: {context}\nBECAUSE: {lecture}\nOptions: {choice}\n" + elif input_format == "QCEM": + input = f"Question: {question}\nContext: {context}\nBECAUSE: {solution}\nOptions: {choice}\n" + elif input_format == "QCLEM": + input = f"Question: {question}\nContext: {context}\nBECAUSE: {lecture} {solution}\nOptions: {choice}\n" + + # Outputs + if test_example: + output = "Answer:" + elif output_format == "A": + output = f"Answer: The answer is {answer}." + + elif output_format == "AL": + output = f"Answer: The answer is {answer}. BECAUSE: {solution}" + elif output_format == "AE": + output = f"Answer: The answer is {answer}. BECAUSE: {lecture}" + elif output_format == "ALE": + output = f"Answer: The answer is {answer}. BECAUSE: {lecture} {solution}" + elif output_format == "AEL": + output = f"Answer: The answer is {answer}. BECAUSE: {solution} {lecture}" + + elif output_format == "LA": + output = f"Answer: {lecture} The answer is {answer}." + elif output_format == "EA": + output = f"Answer: {solution} The answer is {answer}." + elif output_format == "LEA": + output = f"Answer: {lecture} {solution} The answer is {answer}." + elif output_format == "ELA": + output = f"Answer: {solution} {lecture} The answer is {answer}." + + input = input.replace(" ", " ").strip() + output = output.replace(" ", " ").strip() + if output.endswith("BECAUSE:"): + output = output.replace("BECAUSE:", "").strip() + + user_prompt = {"role": "user", "content": f"Can you explain {input}?"} + assistant_prompt = {"role": "assistant", "content": f"{output}"} + + return user_prompt, assistant_prompt + + +def build_prompt_chatbot(problems, shot_qids, prompt_format, use_caption=False, options=["A", "B", "C", "D", "E"], is_test=False): + examples = {} + + for qid in shot_qids: + question = get_question_text(problems[qid]) + context = get_context_text(problems[qid], use_caption) + choice = get_choice_text(problems[qid], options) + answer = get_answer(problems[qid], options) + lecture = get_lecture_text(problems[qid]).replace("\\n", "\n") + solution = get_solution_text(problems[qid]).replace("\\n", "\n") + + train_example = create_one_example_chatbot(prompt_format, question, context, choice, answer, lecture, solution, test_example=is_test) + examples[qid] = train_example + return examples + + +def build_prompt(problems, shot_qids, test_qid, args): + + examples = [] + + # n-shot training examples + for qid in shot_qids: + question = get_question_text(problems[qid]) + context = get_context_text(problems[qid], args.use_caption) + choice = get_choice_text(problems[qid], args.options) + answer = get_answer(problems[qid], args.options) + lecture = get_lecture_text(problems[qid]) + solution = get_solution_text(problems[qid]) + + train_example = create_one_example(args.prompt_format, question, context, choice, answer, lecture, solution, test_example=False) + examples.append(train_example) + + # test example + question = get_question_text(problems[test_qid]) + context = get_context_text(problems[test_qid], args.use_caption) + choice = get_choice_text(problems[test_qid], args.options) + answer = get_answer(problems[test_qid], args.options) + lecture = get_lecture_text(problems[test_qid]) + solution = get_solution_text(problems[test_qid]) + + test_example = create_one_example(args.prompt_format, question, context, choice, answer, lecture, solution, test_example=True) + examples.append(test_example) + + # create the prompt input + prompt_input = "\n\n".join(examples) + + return prompt_input + + +def build_prompt_gpt4(problems, shot_qids, test_qid, args): + + prompt_array = [{"role": "system", "content": "You are a helpful assistant."}] + + # n-shot training examples + for qid in shot_qids: + question = get_question_text(problems[qid]) + context = get_context_text(problems[qid], args.use_caption) + choice = get_choice_text(problems[qid], args.options) + answer = get_answer(problems[qid], args.options) + lecture = get_lecture_text(problems[qid]) + solution = get_solution_text(problems[qid]) + + user_prompt, assistant_prompt = create_one_example_gpt4(args.prompt_format, question, context, choice, answer, lecture, solution, test_example=False) + prompt_array.append(user_prompt) + prompt_array.append(assistant_prompt) + + # test example + question = get_question_text(problems[test_qid]) + context = get_context_text(problems[test_qid], args.use_caption) + choice = get_choice_text(problems[test_qid], args.options) + answer = get_answer(problems[test_qid], args.options) + lecture = get_lecture_text(problems[test_qid]) + solution = get_solution_text(problems[test_qid]) + + user_prompt, assistant_prompt = create_one_example_gpt4(args.prompt_format, question, context, choice, answer, lecture, solution, test_example=True) + prompt_array.append(user_prompt) + prompt_array.append(assistant_prompt) + + return prompt_array diff --git a/scripts/archived/convert_vizwiz_for_submission.py b/scripts/archived/convert_vizwiz_for_submission.py new file mode 100644 index 0000000..80b8253 --- /dev/null +++ b/scripts/archived/convert_vizwiz_for_submission.py @@ -0,0 +1,45 @@ +import os +import argparse +import json + +from llava.eval.m4c_evaluator import EvalAIAnswerProcessor + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--annotation-file", type=str, required=True) + parser.add_argument("--result-file", type=str, required=True) + parser.add_argument("--result-upload-file", type=str, required=True) + return parser.parse_args() + + +if __name__ == "__main__": + + args = parse_args() + + os.makedirs(os.path.dirname(args.result_upload_file), exist_ok=True) + + results = [] + error_line = 0 + for line_idx, line in enumerate(open(args.result_file)): + try: + results.append(json.loads(line)) + except: + error_line += 1 + results = {x["question_id"]: x["text"] for x in results} + test_split = [json.loads(line) for line in open(args.annotation_file)] + split_ids = set([x["question_id"] for x in test_split]) + + print(f"total results: {len(results)}, total split: {len(test_split)}, error_line: {error_line}") + + all_answers = [] + + answer_processor = EvalAIAnswerProcessor() + + for x in test_split: + # import pdb; pdb.set_trace() + assert x["question_id"] in results, print(x) + all_answers.append({"image": x["image"], "answer": answer_processor(results[x["question_id"]])}) + + with open(args.result_upload_file, "w") as f: + json.dump(all_answers, f) diff --git a/scripts/archived/convert_vqav2_for_submission.py b/scripts/archived/convert_vqav2_for_submission.py new file mode 100644 index 0000000..f0a8057 --- /dev/null +++ b/scripts/archived/convert_vqav2_for_submission.py @@ -0,0 +1,50 @@ +import os +import argparse +import json + +from llava.eval.m4c_evaluator import EvalAIAnswerProcessor + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--dir", type=str, default="./playground/data/eval/vqav2") + parser.add_argument("--ckpt", type=str, required=True) + parser.add_argument("--split", type=str, required=True) + return parser.parse_args() + + +if __name__ == "__main__": + + args = parse_args() + + src = os.path.join(args.dir, "answers", args.split, args.ckpt, "merge.jsonl") + test_split = os.path.join(args.dir, "llava_vqav2_mscoco_test2015.jsonl") + dst = os.path.join(args.dir, "answers_upload", args.split, f"{args.ckpt}.json") + os.makedirs(os.path.dirname(dst), exist_ok=True) + + results = [] + error_line = 0 + for line_idx, line in enumerate(open(src)): + try: + results.append(json.loads(line)) + except: + error_line += 1 + + results = {x["question_id"]: x["text"] for x in results} + test_split = [json.loads(line) for line in open(test_split)] + split_ids = set([x["question_id"] for x in test_split]) + + print(f"total results: {len(results)}, total split: {len(test_split)}, error_line: {error_line}") + + all_answers = [] + + answer_processor = EvalAIAnswerProcessor() + + for x in test_split: + if x["question_id"] not in results: + all_answers.append({"question_id": x["question_id"], "answer": ""}) + else: + all_answers.append({"question_id": x["question_id"], "answer": answer_processor(results[x["question_id"]])}) + + with open(dst, "w") as f: + json.dump(all_answers, open(dst, "w")) diff --git a/scripts/archived/data_info.py b/scripts/archived/data_info.py new file mode 100644 index 0000000..d4aa7a5 --- /dev/null +++ b/scripts/archived/data_info.py @@ -0,0 +1,124 @@ +import json +import os +from PIL import Image +from tqdm import tqdm +import matplotlib.pyplot as plt +import numpy as np + + +def load_data(json_path): + with open(json_path, "r") as f: + return json.load(f) + + +def filter_data(data): + # filtered_data = [item for item in data if "image" in item and "text" in item["image"]] + filtered_data = [item for item in data if "image" in item] + return filtered_data + + +from multiprocessing import Pool +import functools + + +def calculate_image_dimension(item, images_folder): + image_path = os.path.join(images_folder, item["image"]) + try: + with Image.open(image_path) as img: + width, height = img.size + return width, height + except Exception as e: + print(f"Error opening {image_path}: {e}") + return None, None + + +def calculate_image_dimensions_multiprocess(filtered_data, images_folder, num_processes=256): + with Pool(num_processes) as p: + dimensions = list(tqdm(p.imap(functools.partial(calculate_image_dimension, images_folder=images_folder), filtered_data), total=len(filtered_data), desc="Calculating image dimensions")) + widths, heights = zip(*[dim for dim in dimensions if dim[0] is not None]) + return list(widths), list(heights) + + +def tokenize(text): + return text.split() + + +def calculate_tokenized_lengths(data): + lengths = [] + for item in tqdm(data, desc="Tokenizing conversations"): + for conversation in item["conversations"]: + tokenized_value = tokenize(conversation["value"]) + lengths.append(len(tokenized_value)) + return lengths + + +import argparse + + +def main(): + parser = argparse.ArgumentParser(description="Process data for LLaVA_Next project.") + parser.add_argument("--json_path", type=str, help="Path to the JSON file containing data.") + parser.add_argument("--images_folder", type=str, default="/mnt/bn/vl-research/data/llava_data", help="Path to the folder containing images.") + args = parser.parse_args() + + llava_instruct_name = args.json_path.split("/")[-1].replace(".json", "") + json_path = args.json_path + llava_instruct_name = os.path.basename(json_path).replace(".json", "") + images_folder = args.images_folder + + data = load_data(json_path) + filtered_data = filter_data(data) + + if len(filtered_data) != 0: + print(f"Total data items: {len(data)}, Filtered data items: {len(filtered_data)}") + widths, heights = calculate_image_dimensions_multiprocess(filtered_data, images_folder) + max_width = max(widths) + max_height = max(heights) + print(f"Max width: {max_width}, Max height: {max_height}") + + tokenized_lengths = calculate_tokenized_lengths(data) + + fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(24, 12)) + + if len(filtered_data) != 0: + # Plot 2D histogram + if min(widths) == max(widths): + widths_bins = [min(widths), max(widths) + 1] + else: + widths_bins = np.arange(min(widths), max(widths) + 100, 100) + + if min(heights) == max(heights): + heights_bins = [min(heights), max(heights) + 1] + else: + heights_bins = np.arange(min(heights), max(heights) + 100, 100) + + h, xedges, yedges, image = ax1.hist2d(widths, heights, bins=[widths_bins, heights_bins], cmap=plt.cm.jet, density=True) + fig.colorbar(image, ax=ax1) + ax1.set_xlabel("Width") + ax1.set_ylabel("Height") + ax1.set_title(f"dist_{llava_instruct_name}_2d_w_h\nMax width: {max(widths)}, Max height: {max(heights)}", fontsize=10) + + # Plot histogram + hist, bin_edges = np.histogram(tokenized_lengths, bins=np.arange(0, max(tokenized_lengths) + 10, 100)) + bins = np.arange(0, max(tokenized_lengths) + 10, 100) + ax2.bar(bin_edges[:-1], hist, width=7, edgecolor="black", log=True) + + # Display every nth label on the x-axis + n = 8 # Adjust this value to control the number of labels displayed + ticks = bins[::n] + tick_labels = [int(tick) for tick in ticks] + ax2.set_xticks(ticks) + ax2.set_xticklabels(tick_labels, rotation=90, fontsize=8) + + ax2.set_xlim(min(bin_edges), max(bin_edges)) + ax2.set_xlabel("Tokenized Length") + ax2.set_ylabel("Count (log scale)") + ax2.set_title(f"dist_{llava_instruct_name}_tokenized_length", fontsize=8) + + plt.tight_layout() + plt.savefig(f"/mnt/bn/vl-research/workspace/boli01/projects/LLaVA_Next/notebooks/sft_data/dist_{llava_instruct_name}_combined.png") + print(f"Plots saved to /mnt/bn/vl-research/workspace/boli01/projects/LLaVA_Next/notebooks/sft_data/dist_{llava_instruct_name}_combined.png") + + +if __name__ == "__main__": + main() diff --git a/scripts/archived/dpo_data_info.py b/scripts/archived/dpo_data_info.py new file mode 100644 index 0000000..ea60ec1 --- /dev/null +++ b/scripts/archived/dpo_data_info.py @@ -0,0 +1,67 @@ +import json +import re + +json_path = "/mnt/bn/vl-research/workspace/boli01/projects/sft_data_workspace/vlfeedback_80k.jsonl" + +with open(json_path, "r") as f: + data = f.readlines() + +data = [json.loads(d) for d in data] + + +def convert_format(original_data, dimension="Visual Faithfulness"): + converted_data = [] + for item in original_data: + # Assuming the best response is the one with the highest helpfulness rating + best_completion = max(item["completions"], key=lambda x: int(x["annotations"]["Helpfulness"]["Rating"])) + best_response = best_completion["response"] + best_model = best_completion["model"] + + if "†source" in best_response: + print(best_response) + # Regex pattern to match the pattern 【digit†source】 + pattern = r"【\d+†source】" + # Replace the matched patterns with an empty string + cleaned_text = re.sub(pattern, "", best_response) + best_response = cleaned_text + print(f"*****************************************") + print(best_response) + + # Assuming the worst response is the one with the lowest helpfulness rating + worst_completion = min(item["completions"], key=lambda x: int(x["annotations"]["Helpfulness"]["Rating"])) + worst_response = worst_completion["response"] + + if "†source" in worst_response: + print(worst_response) + # Regex pattern to match the pattern ��digit†source】 + pattern = r"【\d+†source】" + # Replace the matched patterns with an empty string + cleaned_text = re.sub(pattern, "", worst_response) + worst_response = cleaned_text + print(f"*****************************************") + print(worst_response) + + # Extract scores + best_score = int(best_completion["annotations"][dimension]["Rating"]) + worst_score = int(worst_completion["annotations"][dimension]["Rating"]) + + # Construct the new format + new_item = { + "id": item["id"], + "prompt": item["prompt"], + "answer": "", + "image": f"silkie_dpo/{item['id']}.jpg", # Assuming the video ID is the last part of the original ID + "chosen": best_response, + "rejected": worst_response, + "chosen_score": best_score, + "rejected_score": worst_score, + } + converted_data.append(new_item) + + return converted_data + + +for dimension in ["Visual Faithfulness", "Helpfulness", "Ethical Considerations"]: + converted_data = convert_format(data, dimension=dimension) + with open(f"/mnt/bn/vl-research/data/llava_instruct/dpo_data/silkie_dpo_data_{dimension.replace(' ', '_').lower()}_{len(converted_data)}.json", "w") as f: + json.dump(converted_data, f, indent=4) diff --git a/scripts/archived/entry_cmd.sh b/scripts/archived/entry_cmd.sh new file mode 100644 index 0000000..807f597 --- /dev/null +++ b/scripts/archived/entry_cmd.sh @@ -0,0 +1,30 @@ +python3 -m pip install --upgrade pip; + +export http_proxy=http://sys-proxy-rd-relay.byted.org:8118; +export https_proxy=http://sys-proxy-rd-relay.byted.org:8118; + +export HF_HOME=/mnt/bn/vl-research-boli01-cn/.cache/huggingface; +export HF_TOKEN=""; +export HF_HUB_ENABLE_HF_TRANSFER="1"; + +cd /mnt/bn/vl-research-boli01-cn/projects/zzz/lmms-eval; +pip install -e .; + +cd /mnt/bn/vl-research-boli01-cn/projects/zzz/LLaVA_Next; +pip install -e .; + +python3 -m pip install ninja; +python3 -m pip install flash-attn --no-build-isolation; + +bash /mnt/bn/vl-research-boli01-cn/projects/zzz/LLaVA_Next/cn_scripts/vicuna/internal0.6m_finetune_llava1.6mix_7b_v0.2_unfreeze.sh + + +accelerate launch --num_processes 8 --main_process_port 12345 -m lmms_eval \ + --model llava \ + --model_args pretrained="/mnt/bn/vl-research-boli01-cn/projects/zzz/LLaVA_Next/internal_project_checkpoints/llavanext-lmsys_vicuna-7b-v1.5-clip-vit-large-patch14-336-mlp2x_gelu-pretrain_internal0.6m_vicuna_v1_finetune_llava1.6_datamix_unfreezeVIS_1e" \ + --tasks ok_vqa,textcaps_val,mme_test,mmmu,cmmmu,coco2017_cap_val,vizwiz_vqa_val,ai2d,chartqa,pope \ + --batch_size 1 \ + --log_samples \ + --log_samples_suffix debug \ + --output_path ./logs/ \ + --wandb_args 'project=llava-next-lmms-eval,job_type=eval'; diff --git a/scripts/archived/finetune.sh b/scripts/archived/finetune.sh new file mode 100644 index 0000000..9d15036 --- /dev/null +++ b/scripts/archived/finetune.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +cd /mnt/bn/vl-research/workspace/boli01/zzzprojects/LLaVA + +# Install yolk3k if not installed +if ! pip show yolk3k > /dev/null 2>&1; then + pip install yolk3k +fi + +# Get the installed version of transformers +installed_version=$(pip show transformers | grep Version | cut -d ' ' -f 2) + +# Get the latest version of transformers from PyPI +latest_version=$(yolk -V transformers | cut -d ' ' -f 2) + +# Check if the installed version is not the latest +if [ "$installed_version" != "$latest_version" ]; then + pip install -U transformers +fi + +# Get the installed version of deepspeed +installed_version=$(pip show deepspeed | grep Version | cut -d ' ' -f 2) + +# Get the latest version of deepspeed from PyPI +latest_version=$(yolk -V deepspeed | cut -d ' ' -f 2) + +# Check if the installed version is not the latest + # pip install deepspeed==0.12.2 +if [ "$installed_version" != "$latest_version" ]; then + pip install deepspeed==0.12.2 +fi + +# Install flash-attn if not installed +if ! pip show flash-attn > /dev/null 2>&1; then + pip install flash-attn --no-build-isolation +fi + +################## VICUNA ################## +PROMPT_VERSION=v1 +MODEL_VERSION="vicuna-7b-v1-5" +################## VICUNA ################## + + +################## project ################## +PROJECT_NAME="ds_llava-vicuna-7b-v1-5-mlp2x_gelu-pretrain_blip558k_plain" + +################## data ################## +DATA_NAME="mixtral_instruct_158K_V1" + +# wandb configure +export WANDB_API_KEY="03fc62d68025c9498cf6493432551badd7d4f953" +wandb login $WANDB_API_KEY + +export WANDB_NAME=$PROJECT_NAME--$MODEL_VERSION--$DATA_NAME + +export WANDB_PROJECT=LLaVA_Mixtral + +export WANDB_MODE=online + +# wandb online + +deepspeed --master_port 26000 \ + llava/train/train_mem.py \ + --deepspeed ./scripts/zero2.json \ + --model_name_or_path ./checkpoints/$MODEL_VERSION \ + --version $PROMPT_VERSION \ + --data_path ./playground/data/$DATA_NAME.json \ + --image_folder /mnt/bn/vl-research/workspace/boli01/data/playground/data/coco/train2017 \ + --vision_tower openai/clip-vit-large-patch14 \ + --pretrain_mm_mlp_adapter ./checkpoints/$PROJECT_NAME/mm_projector.bin \ + --mm_vision_select_layer -2 \ + --mm_projector_type mlp2x_gelu \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --bf16 True \ + --output_dir ./checkpoints/llava--$PROJECT_NAME--$MODEL_VERSION--$DATA_NAME--finetune \ + --num_train_epochs 1 \ + --per_device_train_batch_size 16 \ + --per_device_eval_batch_size 4 \ + --gradient_accumulation_steps 1 \ + --evaluation_strategy "no" \ + --save_strategy "steps" \ + --save_steps 50000 \ + --save_total_limit 1 \ + --learning_rate 2e-5 \ + --weight_decay 0. \ + --warmup_ratio 0.03 \ + --lr_scheduler_type "cosine" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 2048 \ + --gradient_checkpointing True \ + --dataloader_num_workers 16 \ + --lazy_preprocess True \ + --report_to wandb diff --git a/scripts/archived/finetune_1.5.sh b/scripts/archived/finetune_1.5.sh new file mode 100644 index 0000000..13cb818 --- /dev/null +++ b/scripts/archived/finetune_1.5.sh @@ -0,0 +1,99 @@ +#!/bin/bash +dataset_name=$1 + +# Uncomment and set the following variables correspondingly to run this script: + +cd /mnt/bn/vl-research/workspace/boli01/zzzprojects/LLaVA + +# Install yolk3k if not installed +if ! pip show yolk3k > /dev/null 2>&1; then + pip install yolk3k +fi + +# Get the installed version of transformers +installed_version=$(pip show transformers | grep Version | cut -d ' ' -f 2) + +# Get the latest version of transformers from PyPI +latest_version=$(yolk -V transformers | cut -d ' ' -f 2) + +# Check if the installed version is not the latest +if [ "$installed_version" != "$latest_version" ]; then + pip install -U transformers +fi + +# Get the installed version of deepspeed +installed_version=$(pip show deepspeed | grep Version | cut -d ' ' -f 2) + +# Get the latest version of deepspeed from PyPI +latest_version=$(yolk -V deepspeed | cut -d ' ' -f 2) + +# Check if the installed version is not the latest +if [ "$installed_version" != "$latest_version" ]; then + pip install deepspeed==0.12.2 +fi + +# Install yolk3k if not installed +if ! pip show flash-attn > /dev/null 2>&1; then + pip install flash-attn --no-build-isolation +fi + + +################## VICUNA ################## +PROMPT_VERSION=v1 +MODEL_VERSION="vicuna-7b-v1-5" +################## VICUNA ################## + +################## project ################## +PROJECT_NAME="ds_llava-vicuna-7b-v1-5-mlp2x_gelu-pretrain_blip558k_plain" + +################## data ################## +DATA_NAME=$dataset_name + + +# wandb configure +export WANDB_API_KEY="03fc62d68025c9498cf6493432551badd7d4f953" +wandb login $WANDB_API_KEY + +export WANDB_NAME=$PROJECT_NAME--$MODEL_VERSION--$DATA_NAME + +export WANDB_PROJECT=LLaVA_Mixtral + +export WANDB_MODE=online + +wandb online + + +deepspeed --master_port 26000 \ + llava/train/train_mem.py \ + --deepspeed ./scripts/zero2.json \ + --model_name_or_path ./checkpoints/$MODEL_VERSION \ + --version $PROMPT_VERSION \ + --data_path ./playground/data/$DATA_NAME.json \ + --image_folder /mnt/bn/vl-research/workspace/boli01/data/playground/data \ + --vision_tower openai/clip-vit-large-patch14 \ + --pretrain_mm_mlp_adapter ./checkpoints/$PROJECT_NAME/mm_projector.bin \ + --mm_vision_select_layer -2 \ + --mm_projector_type mlp2x_gelu \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --bf16 True \ + --output_dir ./checkpoints/llava--$PROJECT_NAME--$MODEL_VERSION--$DATA_NAME--finetune \ + --num_train_epochs 1 \ + --per_device_train_batch_size 16 \ + --per_device_eval_batch_size 4 \ + --gradient_accumulation_steps 1 \ + --evaluation_strategy "no" \ + --save_strategy "steps" \ + --save_steps 50000 \ + --save_total_limit 1 \ + --learning_rate 2e-5 \ + --weight_decay 0. \ + --warmup_ratio 0.03 \ + --lr_scheduler_type "cosine" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 2048 \ + --gradient_checkpointing True \ + --dataloader_num_workers 16 \ + --lazy_preprocess True \ + --report_to wandb diff --git a/scripts/archived/finetune_full_schedule.sh b/scripts/archived/finetune_full_schedule.sh new file mode 100644 index 0000000..9769666 --- /dev/null +++ b/scripts/archived/finetune_full_schedule.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# Uncomment and set the following variables correspondingly to run this script: + +################## VICUNA ################## +# PROMPT_VERSION=v1 +# MODEL_VERSION="vicuna-v1-3-7b" +################## VICUNA ################## + +################## LLaMA-2 ################## +# PROMPT_VERSION="llava_llama_2" +# MODEL_VERSION="llama-2-7b-chat" +################## LLaMA-2 ################## + +deepspeed llava/train/train_mem.py \ + --deepspeed ./scripts/zero2.json \ + --model_name_or_path ./checkpoints/$MODEL_VERSION \ + --version $PROMPT_VERSION \ + --data_path ./playground/data/llava_instruct_158k.json \ + --image_folder /path/to/coco/train2017 \ + --vision_tower openai/clip-vit-large-patch14 \ + --pretrain_mm_mlp_adapter ./checkpoints/llava-$MODEL_VERSION-pretrain/mm_projector.bin \ + --mm_vision_select_layer -2 \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --bf16 True \ + --output_dir ./checkpoints/llava-$MODEL_VERSION-finetune \ + --num_train_epochs 3 \ + --per_device_train_batch_size 16 \ + --per_device_eval_batch_size 4 \ + --gradient_accumulation_steps 1 \ + --evaluation_strategy "no" \ + --save_strategy "steps" \ + --save_steps 50000 \ + --save_total_limit 1 \ + --learning_rate 2e-5 \ + --weight_decay 0. \ + --warmup_ratio 0.03 \ + --lr_scheduler_type "cosine" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 2048 \ + --gradient_checkpointing True \ + --dataloader_num_workers 16 \ + --lazy_preprocess True \ + --report_to wandb diff --git a/scripts/archived/finetune_lora.sh b/scripts/archived/finetune_lora.sh new file mode 100644 index 0000000..5ff90c0 --- /dev/null +++ b/scripts/archived/finetune_lora.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# Uncomment and set the following variables correspondingly to run this script: + +################## VICUNA ################## +# PROMPT_VERSION=v1 +# MODEL_VERSION="vicuna-v1-3-7b" +################## VICUNA ################## + +################## LLaMA-2 ################## +# PROMPT_VERSION="llava_llama_2" +# MODEL_VERSION="llama-2-7b-chat" +################## LLaMA-2 ################## + +deepspeed llava/train/train_mem.py \ + --deepspeed ./scripts/zero2.json \ + --lora_enable True \ + --model_name_or_path ./checkpoints/$MODEL_VERSION \ + --version $PROMPT_VERSION \ + --data_path ./playground/data/llava_instruct_80k.json \ + --image_folder /path/to/coco/train2017 \ + --vision_tower openai/clip-vit-large-patch14 \ + --pretrain_mm_mlp_adapter ./checkpoints/llava-$MODEL_VERSION-pretrain/mm_projector.bin \ + --mm_vision_select_layer -2 \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --bf16 True \ + --output_dir ./checkpoints/llava-$MODEL_VERSION-finetune_lora \ + --num_train_epochs 1 \ + --per_device_train_batch_size 16 \ + --per_device_eval_batch_size 4 \ + --gradient_accumulation_steps 1 \ + --evaluation_strategy "no" \ + --save_strategy "steps" \ + --save_steps 50000 \ + --save_total_limit 1 \ + --learning_rate 2e-5 \ + --weight_decay 0. \ + --warmup_ratio 0.03 \ + --lr_scheduler_type "cosine" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 2048 \ + --gradient_checkpointing True \ + --lazy_preprocess True \ + --dataloader_num_workers 16 \ + --report_to wandb diff --git a/scripts/archived/finetune_mixtral.sh b/scripts/archived/finetune_mixtral.sh new file mode 100644 index 0000000..f7ca383 --- /dev/null +++ b/scripts/archived/finetune_mixtral.sh @@ -0,0 +1,96 @@ +#!/bin/bash + +cd /mnt/bn/vl-research/workspace/boli01/zzzprojects/LLaVA + +# Install yolk3k if not installed +if ! pip show yolk3k > /dev/null 2>&1; then + pip install yolk3k +fi + +# Get the installed version of transformers +installed_version=$(pip show transformers | grep Version | cut -d ' ' -f 2) + +# Get the latest version of transformers from PyPI +latest_version=$(yolk -V transformers | cut -d ' ' -f 2) + +# Check if the installed version is not the latest +if [ "$installed_version" != "$latest_version" ]; then + pip install -U transformers +fi + +# Get the installed version of deepspeed +installed_version=$(pip show deepspeed | grep Version | cut -d ' ' -f 2) + +# Get the latest version of deepspeed from PyPI +latest_version=$(yolk -V deepspeed | cut -d ' ' -f 2) + +# Check if the installed version is not the latest +if [ "$installed_version" != "$latest_version" ]; then + pip install deepspeed==0.12.2 +fi + +# Install yolk3k if not installed +if ! pip show flash-attn > /dev/null 2>&1; then + pip install flash-attn --no-build-isolation +fi + + +################## MISTRAL ################## +PROMPT_VERSION=mistral_instruct +MODEL_VERSION="Mistral-7B-Instruct-v0.2" +################## VICUNA ################## + + +################## project ################## +PROJECT_NAME="ds_llava-Mistral-7B-Instruct-v0.2-mlp2x_gelu-pretrain_blip558k_plain" + +################## data ################## +DATA_NAME="mixtral_instruct_158K_V1" + +# wandb configure +export WANDB_API_KEY="03fc62d68025c9498cf6493432551badd7d4f953" +wandb login $WANDB_API_KEY + +export WANDB_NAME=$PROJECT_NAME--$MODEL_VERSION--$DATA_NAME + +export WANDB_PROJECT=LLaVA_Mixtral + +export WANDB_MODE=online + +wandb online + + +deepspeed --master_port 26000 \ + llava/train/train_mem.py \ + --deepspeed ./scripts/zero2.json \ + --model_name_or_path ./checkpoints/$MODEL_VERSION \ + --version $PROMPT_VERSION \ + --data_path ./playground/data/$DATA_NAME.json \ + --image_folder /mnt/bn/vl-research/workspace/boli01/data/playground/data/coco/train2017 \ + --vision_tower openai/clip-vit-large-patch14 \ + --pretrain_mm_mlp_adapter ./checkpoints/$PROJECT_NAME/mm_projector.bin \ + --mm_vision_select_layer -2 \ + --mm_projector_type mlp2x_gelu \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --bf16 True \ + --output_dir ./checkpoints/llava--$PROJECT_NAME--$MODEL_VERSION--$DATA_NAME--finetune \ + --num_train_epochs 1 \ + --per_device_train_batch_size 16 \ + --per_device_eval_batch_size 4 \ + --gradient_accumulation_steps 1 \ + --evaluation_strategy "no" \ + --save_strategy "steps" \ + --save_steps 50000 \ + --save_total_limit 1 \ + --learning_rate 2e-5 \ + --weight_decay 0. \ + --warmup_ratio 0.03 \ + --lr_scheduler_type "cosine" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 2048 \ + --gradient_checkpointing True \ + --dataloader_num_workers 16 \ + --lazy_preprocess True \ + --report_to wandb diff --git a/scripts/archived/finetune_mixtral_1.5.sh b/scripts/archived/finetune_mixtral_1.5.sh new file mode 100644 index 0000000..71f4ed8 --- /dev/null +++ b/scripts/archived/finetune_mixtral_1.5.sh @@ -0,0 +1,96 @@ +#!/bin/bash +dataset_name=$1 + +cd /mnt/bn/vl-research/workspace/yhzhang/LLaVA + +# Install yolk3k if not installed +if ! pip show yolk3k > /dev/null 2>&1; then + pip install yolk3k +fi + +# Get the installed version of transformers +installed_version=$(pip show transformers | grep Version | cut -d ' ' -f 2) + +# Get the latest version of transformers from PyPI +latest_version=$(yolk -V transformers | cut -d ' ' -f 2) + +# Check if the installed version is not the latest +if [ "$installed_version" != "$latest_version" ]; then + pip install -U transformers +fi + +# Get the installed version of deepspeed +installed_version=$(pip show deepspeed | grep Version | cut -d ' ' -f 2) + +# Get the latest version of deepspeed from PyPI +# latest_version=$(yolk -V deepspeed | cut -d ' ' -f 2) + +# Check if the installed version is not the latest +if [ "$installed_version" != "0.12.2" ]; then + pip install deepspeed==0.12.2 +fi + +# Install yolk3k if not installed +if ! pip show flash-attn > /dev/null 2>&1; then + pip install flash-attn --no-build-isolation +fi + +################## MISTRAL ################## +PROMPT_VERSION=mistral_instruct +MODEL_VERSION="Mistral-7B-Instruct-v0.2" +################## MISTRAL ################## + + +################## project ################## +PROJECT_NAME="ds_llava-Mistral-7B-Instruct-v0.2-mlp2x_gelu-pretrain_blip558k_plain" + +################## data ################## +DATA_NAME=$dataset_name + + +# wandb configure +export WANDB_API_KEY="03fc62d68025c9498cf6493432551badd7d4f953" +wandb login $WANDB_API_KEY + +export WANDB_NAME=$PROJECT_NAME--$MODEL_VERSION--$DATA_NAME + +export WANDB_PROJECT=LLaVA_Mixtral + +export WANDB_MODE=online + +wandb online + +deepspeed --master_port 26000 \ + llava/train/train_mem.py \ + --deepspeed ./scripts/zero2.json \ + --model_name_or_path ./checkpoints/$MODEL_VERSION \ + --version $PROMPT_VERSION \ + --data_path ./playground/data/$DATA_NAME.json \ + --image_folder /mnt/bn/vl-research/workspace/boli01/data/playground/data \ + --vision_tower openai/clip-vit-large-patch14 \ + --pretrain_mm_mlp_adapter ./checkpoints/$PROJECT_NAME/mm_projector.bin \ + --mm_vision_select_layer -2 \ + --mm_projector_type mlp2x_gelu \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --bf16 True \ + --output_dir ./checkpoints/llava--$PROJECT_NAME--$MODEL_VERSION--$DATA_NAME--finetune \ + --num_train_epochs 1 \ + --per_device_train_batch_size 16 \ + --per_device_eval_batch_size 4 \ + --gradient_accumulation_steps 1 \ + --evaluation_strategy "no" \ + --save_strategy "steps" \ + --save_steps 50000 \ + --save_total_limit 1 \ + --learning_rate 2e-5 \ + --weight_decay 0. \ + --warmup_ratio 0.03 \ + --lr_scheduler_type "cosine" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 2048 \ + --gradient_checkpointing True \ + --dataloader_num_workers 16 \ + --lazy_preprocess True + # --report_to wandb diff --git a/scripts/archived/finetune_mixtral_1.6_336px_anyres.sh b/scripts/archived/finetune_mixtral_1.6_336px_anyres.sh new file mode 100644 index 0000000..28b2e0b --- /dev/null +++ b/scripts/archived/finetune_mixtral_1.6_336px_anyres.sh @@ -0,0 +1,100 @@ +#!/bin/bash +dataset_name=$1 + +cd /mnt/bn/vl-research/workspace/boli01/projects/LLaVA_Next + +# Install yolk3k if not installed +if ! pip show yolk3k > /dev/null 2>&1; then + pip install yolk3k +fi + +pip install pydantic + +# Get the installed version of transformers +installed_version=$(pip show transformers | grep Version | cut -d ' ' -f 2) + +# Get the latest version of transformers from PyPI +latest_version=$(yolk -V transformers | cut -d ' ' -f 2) + +# Check if the installed version is not the latest +if [ "$installed_version" != "4.36.2" ]; then + pip install transformers==4.36.2 +fi + +# Get the installed version of deepspeed +installed_version=$(pip show deepspeed | grep Version | cut -d ' ' -f 2) + + +# Check if the installed version is not the latest +if [ "$installed_version" != "0.12.2" ]; then + pip install deepspeed==0.12.2 +fi + +# Install flash-atten if not installed +if ! pip show flash-attn > /dev/null 2>&1; then + pip install flash-attn --no-build-isolation +fi + +################## MISTRAL ################## +PROMPT_VERSION=mistral_instruct +MODEL_VERSION="Mistral-7B-Instruct-v0.2" +################## MISTRAL ################## + + +################## project ################## +PROJECT_NAME="ds_llava-Mistral-7B-Instruct-v0.2-clip_large_336px-mlp2x_gelu-pretrain_blip558k_plain" + +################## data ################## +DATA_NAME=$dataset_name + + +# wandb configure +export WANDB_API_KEY=e464cc107357c7b38e87f239bc3eb2ce5fb73c7c +export WANDB_PROJECT=llava + +export WANDB_NAME=$PROJECT_NAME--$DATA_NAME--336px--anyres--sft + +export WANDB_MODE=online + +wandb online + +deepspeed --master_port 26000 \ + llava/train/train_mem.py \ + --deepspeed ./scripts/zero3.json \ + --model_name_or_path /mnt/bn/vl-research/workspace/project/2023/LLaVA/checkpoints/$MODEL_VERSION \ + --version $PROMPT_VERSION \ + --data_path ./playground/data/$DATA_NAME.json \ + --image_folder /mnt/bn/vl-research/workspace/boli01/data/playground/data \ + --vision_tower openai/clip-vit-large-patch14-336 \ + --pretrain_mm_mlp_adapter /mnt/bn/vl-research/workspace/project/2023/LLaVA/checkpoints/ds_llava-Mistral-7B-Instruct-v0.2-clip_large_336px-mlp2x_gelu-pretrain_blip558k_plain/mm_projector.bin \ + --mm_projector_type mlp2x_gelu \ + --mm_vision_select_layer -2 \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --group_by_modality_length True \ + --unfreeze_mm_vision_tower True \ + --mm_vision_tower_lr 2e-6 \ + --image_aspect_ratio anyres \ + --image_grid_pinpoints "[(336, 672), (672, 336), (672, 672), (1008, 336), (336, 1008)]" \ + --mm_patch_merge_type spatial_unpad \ + --bf16 True \ + --output_dir ./checkpoints/$PROJECT_NAME--$DATA_NAME--336px--anyres--sft \ + --num_train_epochs 9 \ + --per_device_train_batch_size 8 \ + --per_device_eval_batch_size 4 \ + --gradient_accumulation_steps 1 \ + --evaluation_strategy "no" \ + --save_strategy "epoch" \ + --save_steps 1500 \ + --learning_rate 5e-6 \ + --weight_decay 0. \ + --warmup_ratio 0.03 \ + --lr_scheduler_type "cosine" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 4096 \ + --gradient_checkpointing True \ + --dataloader_num_workers 8 \ + --lazy_preprocess True \ + --report_to wandb + diff --git a/scripts/archived/finetune_mixtral_1.6_336px_anyres_freeze_vision.sh b/scripts/archived/finetune_mixtral_1.6_336px_anyres_freeze_vision.sh new file mode 100644 index 0000000..e04f487 --- /dev/null +++ b/scripts/archived/finetune_mixtral_1.6_336px_anyres_freeze_vision.sh @@ -0,0 +1,97 @@ +#!/bin/bash +dataset_name=$1 + +cd /mnt/bn/vl-research/workspace/yhzhang/LLaVA + +# Install yolk3k if not installed +if ! pip show yolk3k > /dev/null 2>&1; then + pip install yolk3k +fi + +pip install pydantic + +# Get the installed version of transformers +installed_version=$(pip show transformers | grep Version | cut -d ' ' -f 2) + +# Get the latest version of transformers from PyPI +latest_version=$(yolk -V transformers | cut -d ' ' -f 2) + +# Check if the installed version is not the latest +if [ "$installed_version" != "4.36.2" ]; then + pip install transformers==4.36.2 +fi + +# Get the installed version of deepspeed +installed_version=$(pip show deepspeed | grep Version | cut -d ' ' -f 2) + + +# Check if the installed version is not the latest +if [ "$installed_version" != "0.12.2" ]; then + pip install deepspeed==0.12.2 +fi + +# Install flash-atten if not installed +if ! pip show flash-attn > /dev/null 2>&1; then + pip install flash-attn --no-build-isolation +fi + +################## MISTRAL ################## +PROMPT_VERSION=mistral_instruct +MODEL_VERSION="Mistral-7B-Instruct-v0.2" +################## MISTRAL ################## + + +################## project ################## +PROJECT_NAME="ds_llava-Mistral-7B-Instruct-v0.2-clip_large_336px-mlp2x_gelu-pretrain_blip558k_plain" + +################## data ################## +DATA_NAME=$dataset_name + + +# wandb configure +export WANDB_API_KEY=e464cc107357c7b38e87f239bc3eb2ce5fb73c7c +export WANDB_PROJECT=llava + +export WANDB_NAME=$PROJECT_NAME--$DATA_NAME--336px--unfreeze--anyres--sft + +export WANDB_MODE=online + +wandb online + +deepspeed --master_port 26000 \ + llava/train/train_mem.py \ + --deepspeed ./scripts/zero3.json \ + --model_name_or_path ./checkpoints/$MODEL_VERSION \ + --version $PROMPT_VERSION \ + --data_path ./playground/data/$DATA_NAME.json \ + --image_folder /mnt/bn/vl-research/workspace/boli01/data/playground/data \ + --vision_tower openai/clip-vit-large-patch14-336 \ + --pretrain_mm_mlp_adapter /mnt/bn/vl-research/workspace/project/2023/LLaVA/checkpoints/ds_llava-Mistral-7B-Instruct-v0.2-clip_large_336px-mlp2x_gelu-pretrain_blip558k_plain/mm_projector.bin \ + --mm_vision_select_layer -2 \ + --mm_projector_type mlp2x_gelu \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --group_by_modality_length True \ + --image_aspect_ratio anyres \ + --image_grid_pinpoints "[(336, 672), (672, 336), (672, 672), (1008, 336), (336, 1008)]" \ + --mm_patch_merge_type spatial_unpad \ + --bf16 True \ + --output_dir ./checkpoints/$PROJECT_NAME--$DATA_NAME--336px--anyres--unfreeze--sft \ + --num_train_epochs 1 \ + --per_device_train_batch_size 16 \ + --per_device_eval_batch_size 4 \ + --gradient_accumulation_steps 1 \ + --evaluation_strategy "no" \ + --save_strategy "steps" \ + --save_steps 50000 \ + --save_total_limit 1 \ + --learning_rate 2e-5 \ + --weight_decay 0. \ + --warmup_ratio 0.03 \ + --lr_scheduler_type "cosine" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 2048 \ + --gradient_checkpointing True \ + --dataloader_num_workers 16 \ + diff --git a/scripts/archived/finetune_mixtral_1.6_336px_anyres_lmms_eval.sh b/scripts/archived/finetune_mixtral_1.6_336px_anyres_lmms_eval.sh new file mode 100644 index 0000000..c31fdc5 --- /dev/null +++ b/scripts/archived/finetune_mixtral_1.6_336px_anyres_lmms_eval.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +# set up wandb +export WANDB_API_KEY=a651c244635bc6f913ab654af3f0eebaecdc9381 +export WANDB_ENTITY=llava-vl +export WANDB_PROJECT=llava-next +export PYTHONWARNINGS="ignore" + +cd /mnt/bn/vl-research/workspace/boli01/projects/lmms-eval + +pip install -e . + +# set up llava dev env +cd /mnt/bn/vl-research/workspace/boli01/projects/LLaVA_Next + +################## MISTRAL ################## +PROMPT_VERSION=mistral_instruct +MODEL_VERSION="Mistral-7B-Instruct-v0.2" +################## MISTRAL ################## + +################## project ################## +PROJECT_NAME="ds_llava-Mistral-7B-Instruct-v0.2-clip_large_336px-mlp2x_gelu-pretrain_blip558k_plain" + +################## data ################## +DATA_NAME='llava_caps20k_chartqa19k' + +export WANDB_NAME=$PROJECT_NAME--$DATA_NAME--336px--anyres--sft +export WANDB_MODE=online + +wandb online + +CUDA_VISIBLE_DEVICES="0,1,2,3,4,5,6,7" deepspeed --master_port 26000 --include localhost:0,1,2,3,4,5,6,7 llava/train/train_mem.py \ + --deepspeed ./scripts/zero3_offload.json \ + --model_name_or_path mistralai/$MODEL_VERSION \ + --version $PROMPT_VERSION \ + --data_path ./playground/data/llava_instruct/$DATA_NAME.json \ + --image_folder /mnt/bn/vl-research/data/llava \ + --vision_tower openai/clip-vit-large-patch14-336 \ + --mm_projector_type mlp2x_gelu \ + --mm_vision_select_layer -2 \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --group_by_modality_length True \ + --unfreeze_mm_vision_tower True \ + --mm_vision_tower_lr 2e-6 \ + --image_aspect_ratio anyres \ + --image_grid_pinpoints "[(336, 672), (672, 336), (672, 672), (1008, 336), (336, 1008)]" \ + --mm_patch_merge_type spatial_unpad \ + --bf16 True \ + --output_dir ./checkpoints/$PROJECT_NAME--llava1.6--336px--anyres--sft \ + --num_train_epochs 1 \ + --per_device_train_batch_size 8 \ + --per_device_eval_batch_size 4 \ + --gradient_accumulation_steps 1 \ + --evaluation_strategy "no" \ + --save_strategy "steps" \ + --save_steps 1500 \ + --learning_rate 2e-5 \ + --weight_decay 0. \ + --warmup_ratio 0.03 \ + --lr_scheduler_type "cosine" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 4096 \ + --gradient_checkpointing True \ + --dataloader_num_workers 32 \ + --lazy_preprocess True \ + --report_to wandb \ + --run_name $WANDB_NAME +# starting here is the args for evaluation + --eval_num_processes 4 \ + --task_names mme,docvqa_val \ + --model_args pretrained=./checkpoints/$PROJECT_NAME--$DATA_NAME--336px--anyres--sft \ + --limit 8 \ + --batch_size 1 \ + --log_samples \ + --log_samples_suffix debug \ + --output_path ./logs/ diff --git a/scripts/archived/finetune_mixtral_copy.sh b/scripts/archived/finetune_mixtral_copy.sh new file mode 100644 index 0000000..23cab89 --- /dev/null +++ b/scripts/archived/finetune_mixtral_copy.sh @@ -0,0 +1,96 @@ +#!/bin/bash + +cd /mnt/bn/vl-research/workspace/boli01/zzzprojects/LLaVA + +# Install yolk3k if not installed +if ! pip show yolk3k > /dev/null 2>&1; then + pip install yolk3k +fi + +# Get the installed version of transformers +installed_version=$(pip show transformers | grep Version | cut -d ' ' -f 2) + +# Get the latest version of transformers from PyPI +latest_version=$(yolk -V transformers | cut -d ' ' -f 2) + +# Check if the installed version is not the latest +if [ "$installed_version" != "$latest_version" ]; then + pip install -U transformers +fi + +# Get the installed version of deepspeed +installed_version=$(pip show deepspeed | grep Version | cut -d ' ' -f 2) + +# Get the latest version of deepspeed from PyPI +latest_version=$(yolk -V deepspeed | cut -d ' ' -f 2) + +# Check if the installed version is not the latest +if [ "$installed_version" != "$latest_version" ]; then + pip install deepspeed==0.12.2 +fi + +# Install yolk3k if not installed +if ! pip show flash-attn > /dev/null 2>&1; then + pip install flash-attn --no-build-isolation +fi + + +################## MISTRAL ################## +PROMPT_VERSION=mistral_instruct +MODEL_VERSION="Mistral-7B-Instruct-v0.2" +################## VICUNA ################## + + +################## project ################## +PROJECT_NAME="ds_llava-Mistral-7B-Instruct-v0.2-mlp2x_gelu-pretrain_blip558k_plain" + +################## data ################## +DATA_NAME="llava_instruct_150k" + +# wandb configure +export WANDB_API_KEY="03fc62d68025c9498cf6493432551badd7d4f953" +wandb login $WANDB_API_KEY + +export WANDB_NAME=$PROJECT_NAME--$MODEL_VERSION--$DATA_NAME + +export WANDB_PROJECT=LLaVA_Mixtral + +export WANDB_MODE=online + +wandb online + + +deepspeed --master_port 26000 \ + llava/train/train_mem.py \ + --deepspeed ./scripts/zero2.json \ + --model_name_or_path ./checkpoints/$MODEL_VERSION \ + --version $PROMPT_VERSION \ + --data_path ./playground/data/$DATA_NAME.json \ + --image_folder /mnt/bn/vl-research/workspace/boli01/data/playground/data/coco/train2017 \ + --vision_tower openai/clip-vit-large-patch14 \ + --pretrain_mm_mlp_adapter ./checkpoints/$PROJECT_NAME/mm_projector.bin \ + --mm_vision_select_layer -2 \ + --mm_projector_type mlp2x_gelu \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --bf16 True \ + --output_dir ./checkpoints/llava--$PROJECT_NAME--$MODEL_VERSION--$DATA_NAME--finetune \ + --num_train_epochs 1 \ + --per_device_train_batch_size 16 \ + --per_device_eval_batch_size 4 \ + --gradient_accumulation_steps 1 \ + --evaluation_strategy "no" \ + --save_strategy "steps" \ + --save_steps 50000 \ + --save_total_limit 1 \ + --learning_rate 2e-5 \ + --weight_decay 0. \ + --warmup_ratio 0.03 \ + --lr_scheduler_type "cosine" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 2048 \ + --gradient_checkpointing True \ + --dataloader_num_workers 16 \ + --lazy_preprocess True \ + --report_to wandb diff --git a/scripts/archived/finetune_qlora.sh b/scripts/archived/finetune_qlora.sh new file mode 100644 index 0000000..05be856 --- /dev/null +++ b/scripts/archived/finetune_qlora.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Uncomment and set the following variables correspondingly to run this script: + +################## VICUNA ################## +# PROMPT_VERSION=v1 +# MODEL_VERSION="vicuna-v1-3-7b" +################## VICUNA ################## + +################## LLaMA-2 ################## +# PROMPT_VERSION="llava_llama_2" +# MODEL_VERSION="llama-2-7b-chat" +################## LLaMA-2 ################## + +deepspeed llava/train/train_mem.py \ + --deepspeed ./scripts/zero2.json \ + --lora_enable True \ + --bits 4 \ + --model_name_or_path ./checkpoints/$MODEL_VERSION \ + --version $PROMPT_VERSION \ + --data_path ./playground/data/llava_instruct_80k.json \ + --image_folder /path/to/coco/train2017 \ + --vision_tower openai/clip-vit-large-patch14 \ + --pretrain_mm_mlp_adapter ./checkpoints/llava-$MODEL_VERSION-pretrain/mm_projector.bin \ + --mm_vision_select_layer -2 \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --bf16 True \ + --output_dir ./checkpoints/llava-$MODEL_VERSION-finetune_lora \ + --num_train_epochs 1 \ + --per_device_train_batch_size 16 \ + --per_device_eval_batch_size 4 \ + --gradient_accumulation_steps 1 \ + --evaluation_strategy "no" \ + --save_strategy "steps" \ + --save_steps 50000 \ + --save_total_limit 1 \ + --learning_rate 2e-5 \ + --weight_decay 0. \ + --warmup_ratio 0.03 \ + --lr_scheduler_type "cosine" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 2048 \ + --gradient_checkpointing True \ + --lazy_preprocess True \ + --dataloader_num_workers 16 \ + --report_to wandb diff --git a/scripts/archived/finetune_sqa.sh b/scripts/archived/finetune_sqa.sh new file mode 100644 index 0000000..ac1359c --- /dev/null +++ b/scripts/archived/finetune_sqa.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +deepspeed llava/train/train_mem.py \ + --deepspeed ./scripts/zero2.json \ + --model_name_or_path lmsys/vicuna-13b-v1.3 \ + --version $PROMPT_VERSION \ + --data_path /Data/ScienceQA/data/scienceqa/llava_train_QCM-LEA.json \ + --image_folder /Data/ScienceQA/data/scienceqa/images/train \ + --vision_tower openai/clip-vit-large-patch14 \ + --pretrain_mm_mlp_adapter ./checkpoints/huggingface/liuhaotian/llava-pretrain-vicuna-13b-v1.3/mm_projector.bin \ + --mm_vision_select_layer -2 \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --bf16 True \ + --output_dir ./checkpoints/llava-vicuna-13b-v1.3-pretrain_lcs558k_plain-ScienceQA_QCM_LEA-12e \ + --num_train_epochs 12 \ + --per_device_train_batch_size 16 \ + --per_device_eval_batch_size 4 \ + --gradient_accumulation_steps 1 \ + --evaluation_strategy "no" \ + --save_strategy "steps" \ + --save_steps 50000 \ + --save_total_limit 1 \ + --learning_rate 2e-5 \ + --weight_decay 0. \ + --warmup_ratio 0.03 \ + --lr_scheduler_type "cosine" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 2048 \ + --gradient_checkpointing True \ + --dataloader_num_workers 16 \ + --lazy_preprocess True \ + --report_to wandb diff --git a/scripts/archived/merge_lora_weights.py b/scripts/archived/merge_lora_weights.py new file mode 100644 index 0000000..90b4aa0 --- /dev/null +++ b/scripts/archived/merge_lora_weights.py @@ -0,0 +1,22 @@ +import argparse +from llava.model.builder import load_pretrained_model +from llava.mm_utils import get_model_name_from_path + + +def merge_lora(args): + model_name = get_model_name_from_path(args.model_path) + tokenizer, model, image_processor, context_len = load_pretrained_model(args.model_path, args.model_base, model_name, device_map="cpu") + + model.save_pretrained(args.save_model_path) + tokenizer.save_pretrained(args.save_model_path) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--model-path", type=str, required=True) + parser.add_argument("--model-base", type=str, required=True) + parser.add_argument("--save-model-path", type=str, required=True) + + args = parser.parse_args() + + merge_lora(args) diff --git a/scripts/archived/pretrain.sh b/scripts/archived/pretrain.sh new file mode 100644 index 0000000..f3cf7d9 --- /dev/null +++ b/scripts/archived/pretrain.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# Uncomment and set the following variables correspondingly to run this script: + +# MODEL_VERSION=vicuna-v1-3-7b +# MODEL_VERSION=llama-2-7b-chat + +########### DO NOT CHANGE ########### +########### USE THIS FOR BOTH ########### +PROMPT_VERSION=plain +########### DO NOT CHANGE ########### + +deepspeed llava/train/train_mem.py \ + --deepspeed ./scripts/zero2.json \ + --model_name_or_path ./checkpoints/$MODEL_VERSION \ + --version $PROMPT_VERSION \ + --data_path /path/to/pretrain_data.json \ + --image_folder /path/to/images \ + --vision_tower openai/clip-vit-large-patch14 \ + --tune_mm_mlp_adapter True \ + --mm_vision_select_layer -2 \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --bf16 True \ + --output_dir ./checkpoints/llava-$MODEL_VERSION-pretrain \ + --num_train_epochs 1 \ + --per_device_train_batch_size 16 \ + --per_device_eval_batch_size 4 \ + --gradient_accumulation_steps 1 \ + --evaluation_strategy "no" \ + --save_strategy "steps" \ + --save_steps 24000 \ + --learning_rate 2e-3 \ + --weight_decay 0. \ + --warmup_ratio 0.03 \ + --lr_scheduler_type "cosine" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 2048 \ + --gradient_checkpointing True \ + --dataloader_num_workers 16 \ + --lazy_preprocess True \ + --report_to wandb diff --git a/scripts/archived/quick_check.py b/scripts/archived/quick_check.py new file mode 100644 index 0000000..ca7cff9 --- /dev/null +++ b/scripts/archived/quick_check.py @@ -0,0 +1,57 @@ +import json +import os +import argparse +from tqdm import tqdm +import yaml + + +def check_missing_images(json_path, images_folder): + data = json.load(open(json_path, "r")) + missing_data = [] + + for i, d in enumerate(tqdm(data)): + image = d["image"] if "image" in d else "" + if image != "": + path = os.path.join(images_folder, image) + if not os.path.exists(path): + print(f"Missing image: {path}") + missing_data.append(d) + + return missing_data + + +def read_yaml_to_llava_data(yaml_path, images_folder): + print(f"Reading YAML file: {yaml_path}") + with open(yaml_path, "r") as f: + data = yaml.safe_load(f) + + llava_json_paths = data["datasets"] + for item in llava_json_paths: + json_path = item["json_path"] + missing_data = check_missing_images(json_path, images_folder) + if len(missing_data) > 0: + print(f"Missing images in {json_path}:") + for d in missing_data: + print(d) + + +def direct_check_llava_data(json_path, images_folder): + missing_data = check_missing_images(json_path, images_folder) + if len(missing_data) > 0: + print(f"Missing images in {json_path}:") + for d in missing_data: + print(d) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Check for missing images in dataset.") + parser.add_argument("--yaml_path", type=str, default="", help="Path to the YAML file containing the dataset.") + parser.add_argument("--json_path", type=str, default="", help="Path to the JSON file containing the dataset.") + parser.add_argument("--images_folder", type=str, default="/mnt/bn/vl-research/data/llava_data", help="Path to the folder containing the images.") + + args = parser.parse_args() + + if args.json_path != "": + direct_check_llava_data(args.json_path, args.images_folder) + elif args.yaml_path != "": + read_yaml_to_llava_data(args.yaml_path, args.images_folder) diff --git a/scripts/archived/sqa_eval_batch.sh b/scripts/archived/sqa_eval_batch.sh new file mode 100644 index 0000000..adbf46e --- /dev/null +++ b/scripts/archived/sqa_eval_batch.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +CHUNKS=8 +for IDX in {0..7}; do + CUDA_VISIBLE_DEVICES=$IDX python -m llava.eval.model_vqa_science \ + --model-path liuhaotian/llava-lcs558k-scienceqa-vicuna-13b-v1.3 \ + --question-file ~/haotian/datasets/ScienceQA/data/scienceqa/llava_test_QCM-LEA.json \ + --image-folder ~/haotian/datasets/ScienceQA/data/scienceqa/images/test \ + --answers-file ./test_llava-13b-chunk$CHUNKS_$IDX.jsonl \ + --num-chunks $CHUNKS \ + --chunk-idx $IDX \ + --conv-mode llava_v1 & +done diff --git a/scripts/archived/sqa_eval_gather.sh b/scripts/archived/sqa_eval_gather.sh new file mode 100644 index 0000000..525bd43 --- /dev/null +++ b/scripts/archived/sqa_eval_gather.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +CHUNKS=8 +output_file="test_llava-13b.jsonl" + +# Clear out the output file if it exists. +> "$output_file" + +# Loop through the indices and concatenate each file. +for idx in $(seq 0 $((CHUNKS-1))); do + cat "./test_llava-13b-chunk${idx}.jsonl" >> "$output_file" +done + +python llava/eval/eval_science_qa.py \ + --base-dir ~/haotian/datasets/ScienceQA/data/scienceqa \ + --result-file ./test_llava-13b.jsonl \ + --output-file ./test_llava-13b_output.json \ + --output-result ./test_llava-13b_result.json diff --git a/scripts/interleave/eval_all.sh b/scripts/interleave/eval_all.sh new file mode 100644 index 0000000..ee04c6a --- /dev/null +++ b/scripts/interleave/eval_all.sh @@ -0,0 +1,5 @@ + +# evaluate +./scripts/interleave/eval_interleave_3d.sh /path/to/ckpt /path/to/images multi_image_in_domain +./scripts/interleave/eval_interleave_3d.sh /path/to/ckpt /path/to/images multi_image_out_domain +./scripts/interleave/eval_interleave_3d.sh /path/to/ckpt /path/to/images multi_view_in_domain \ No newline at end of file diff --git a/scripts/interleave/eval_interleave_3d.sh b/scripts/interleave/eval_interleave_3d.sh new file mode 100644 index 0000000..cea796c --- /dev/null +++ b/scripts/interleave/eval_interleave_3d.sh @@ -0,0 +1,28 @@ +alias python=python3 +CKPT_PATH=$1 +NAME=$(echo "$CKPT_PATH" | awk -F'/' '{print $NF}') +echo $NAME +##### set images path +DATA_PATH=$2 +EVAL_TYPE=$3 +JSON_PATH=$2/$3.json +############################### eval multi-image +RESULT_NAME="logs/${NAME}/${EVAL_TYPE}" +echo $RESULT_NAME + +mkdir -p logs/${NAME} + +file_path=${RESULT_NAME}/result.jsonl + +bash scripts/interleave/eval_multiprocess.sh \ +${CKPT_PATH} \ +${JSON_PATH} \ +${RESULT_NAME} \ +${DATA_PATH} \ +"" \ +8 0 + +python3 llava/eval/evaluate_interleave.py --result-dir ${RESULT_NAME} + + + diff --git a/scripts/interleave/eval_multiprocess.sh b/scripts/interleave/eval_multiprocess.sh new file mode 100644 index 0000000..bab4f72 --- /dev/null +++ b/scripts/interleave/eval_multiprocess.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +# Check if three arguments are passed +if [ "$#" -ne 7 ]; then + echo "Usage: $0 " + exit 1 +fi + +# Assign the command line arguments to variables +model_path=$1 +question_path=$2 +base_answer_path=$3 +image_folder=$4 +extra_prompt=$5 +N=$6 +temperature=$7 + +# Loop over each chunk/process +for (( chunk_id=0; chunk_id "${base_answer_path}.jsonl" +for ((i=0; i> "${base_answer_path}/result.jsonl" +done +# remove the unmerged files +for (( chunk_id=0; chunk_id They are basically the same as the basic training scripts, but with some modifications, such as the data yaml. + +- `finetune_clip.sh`: This could be seen as the first image version LLaVA-NeXT (2024-01) training script, with `anyres` strategy and maximum 2x2 image grids. +- `finetune_siglip.sh`: Same but with `siglip` encoder, each grid becomes 729 tokens. +- `finetune_onevision.sh`: This is our latest training script, with `anyres_max_9` strategy and image grids weaving from 1x1 to 6x6, at most to 2304x2304 resolution. Inside the script, we also incorporate the multi-image and video data into training loop. the detail token strategy could be found in our paper. + +# About the LLaVA-OneVision Data + +We need to address the fact that our data has been collected and used in different projects/people. LLaVA-OneVision is our first attempt to integrate these datasets. For the data that has already been uploaded, we will refer you to the corresponding locations. We kindly ask everyone to gather the "fragments" and piece them together into a "diamond" in your own environment. + +Here we explain the some technical details on our data. + +- pretrain data - BLIP558K (same as previous llava 1.5 series) +- mid stage data mixture + ```yaml + datasets: + - json_path: /mnt/bn/vl-research/data/llava_instruct/blip558k_stage1.5_finetune_w_prompt.json + sampling_strategy: all + - json_path: /mnt/bn/vl-research/data/llava_instruct/coco118k_stage1.5_finetune_w_prompt.json + sampling_strategy: all + - json_path: /mnt/bn/vl-research/data/llava_instruct/cc3m_recap_data_prompt_v2.json + sampling_strategy: all + - json_path: /mnt/bn/vl-research/data/llava_instruct/ureader_tr_sft.json + sampling_strategy: all + - json_path: /mnt/bn/vl-research/data/llava_instruct/instruct_azure_dc_zh_92K.json + sampling_strategy: all + - json_path: /mnt/bn/vl-research/data/llava_instruct/allava/Evol-Instruct-GPT4-Turbo-143K.json + sampling_strategy: all + - json_path: /mnt/bn/vl-research/data/llava_instruct/synthdog_zh/synthdog_zh_100k.json + sampling_strategy: all + - json_path: /mnt/bn/vl-research/data/llava_instruct/synthdog_en/synthdog_en_100k.json + sampling_strategy: all + ``` + The first three datasets can be collected via the [LLaVA-Recap](https://huggingface.co/collections/lmms-lab/llava-next-6623288e2d61edba3ddbf5ff) series data. We did a slightly modification to make the data more compatible to other data with an added prompt behind `` at each question. You may download our dataset and process it in local environment. Each data we added the **same prompt** "\nPlease generate detailed descriptions of the given image.". Here's an example: + ```json + { + "id": "000000000009", + "image": "coco/train2017/000000000009.jpg", + "conversations": [ + { + "from": "human", + "value": "\nPlease generate detailed descriptions of the given image." + }, + { + "from": "gpt", + "value": "The image shows a meal served in a blue tray with compartments. In the top left compartment, there is a slice of bread with a spread that appears to be butter, accompanied by a few almonds and a slice of what looks like a baked potato or sweet potato. The top right compartment contains a variety of fruits, including what seems to be pineapple, orange slices, and possibly a piece of melon.\n\nIn the bottom left compartment, there is a piece of bread with a spread that could be butter or margarine, and a small portion of what might be a meatball or a similar type of meat covered in a sauce. The bottom right compartment contains a serving of broccoli, which appears to be steamed or lightly cooked.\n\nThe meal is presented in a way that suggests it is a balanced meal, with a variety of food groups represented carbohydrates (bread), protein (meatball), healthy fats (almonds and butter), and fruits and vegetables (broccoli and the fruit assortment). The image is a close-up photograph with a focus on the food, and the colors are vibrant, indicating freshness." + } + ] + } + ``` + + The rest files are being uploaded to [LLaVA-OneVision Mid-Stage Data](https://huggingface.co/datasets/lmms-lab/LLaVA-OneVision-Mid-Data), except from `instruct_azure_dc_zh_92K` since it's internal data due and have policy issue (sad but I could preview one example). The `instruct_azure_dc_zh_92K` images are from COCO and QAs are generated by GPT-4V service provided by AzureAI. We uses this data in early stage experiments wishing to add Chinese knowledge to our model, and just kept using it (yes, just legacy, no other specific reason). + ```json + { + "id": "000000051920", + "image": "azure_dc_zh_92K/000000051920.jpg", + "conversations": [ + { + "from": "human", + "value": "\n这幅画中的关键信息有哪些?" + }, + { + "from": "gpt", + "value": "这张图片展示了一位穿着深色(可能是黑色或深蓝色)毛衣的人坐在一张桌子前,正在用右手拿着一片披萨。披萨看起来有多种配料,包括绿色的菠菜和橙红色的番茄,放在一个银色的圆形披萨盘上。披萨盘上还剩下大约六片披萨。在背景中,可以看到一个穿着浅色衣服的人站在柜台后面,柜台上方有一个黑板菜单,上面用白色粉笔写着菜单项,但是字迹不清晰,无法阅读。整个场景看起来像是在一个披萨店内部,环境显得比较休闲。" + } + ], + "data_source": "azure_dc_zh_92K" + } + ``` + +- single-image stage data mixture + +We have placed our single-image stage data in [single-image-yaml](https://github.com/LLaVA-VL/LLaVA-NeXT/blob/main/scripts/train/single_image.yaml) for users to review. You can download each subset from [onevision-data](https://huggingface.co/datasets/lmms-lab/LLaVA-OneVision-Data). + +Inside the data yaml, the first indicates the previous llava-1.6/next 790K data, you can download them in [llava-next-data](https://huggingface.co/datasets/lmms-lab/LLaVA-NeXT-Data). + +Inside the yaml, the naming would be different with our paper figure due to writing consideration. For users who need to explore our dataset, you can check the [upload script](https://github.com/LLaVA-VL/LLaVA-NeXT/blob/0070d0ae4931c9b19d9cc57c38e16a87c270a61c/playground/upload_data.py#L175) to find the mapping from our local dataset to HF's version. + +- onevision stage data mixture + +Our onevision stage data is available in [onevision-yaml](https://github.com/LLaVA-VL/LLaVA-NeXT/blob/main/scripts/train/onevision.yaml). The single-image portion can be downloaded from the above Huggingface link for onevision data. Here's a breakdown of each part: + + - Around 800K higher-quality data re-sampled from the previous stage (yes, it's data replay!). + - [M4-Instruct Data](https://huggingface.co/datasets/lmms-lab/M4-Instruct-Data) + - Video Data: We have released the video part along with [llava-video-data](https://huggingface.co/datasets/lmms-lab/LLaVA-Video-178K). Users can download the data, and we utilize the subset used in LLaVA-OneVision: + - We have included captions and open-ended questions in the 0_30_s_academic_v0_1 split, along with 240,000 open-ended QA items and 15,000 caption entries, as part of the video data in LLaVA-Hound for LLaVA-OneVision. + - 0_30_s_academic_v0_1 captions + - 0_30_s_academic_v0_1 open-ended QA + - LLaVA-Hound: Same as above. diff --git a/scripts/train/direct_finetune_clip.sh b/scripts/train/direct_finetune_clip.sh new file mode 100644 index 0000000..ad246b4 --- /dev/null +++ b/scripts/train/direct_finetune_clip.sh @@ -0,0 +1,65 @@ +export OMP_NUM_THREADS=8 +export NCCL_IB_DISABLE=0 +export NCCL_IB_GID_INDEX=3 +export NCCL_SOCKET_IFNAME=eth0 +export NCCL_DEBUG=INFO + +LLM_VERSION="Qwen/Qwen2-7B-Instruct" +LLM_VERSION_CLEAN="${LLM_VERSION//\//_}" +VISION_MODEL_VERSION="openai/clip-vit-large-patch14-336" +VISION_MODEL_VERSION_CLEAN="${VISION_MODEL_VERSION//\//_}" + +############### Pretrain ################ + +PROMPT_VERSION="qwen_1_5" + +BASE_RUN_NAME="llavanext-${VISION_MODEL_VERSION_CLEAN}-${LLM_VERSION_CLEAN}-mlp2x_gelu-pretrain_blip558k_plain" +echo "BASE_RUN_NAME: ${BASE_RUN_NAME}" + +ACCELERATE_CPU_AFFINITY=1 torchrun --nproc_per_node="${NUM_GPUS}" --nnodes="${NNODES}" --node_rank="${RANK}" --master_addr="${ADDR}" --master_port="${PORT}" \ + llava/train/train_mem.py \ + --deepspeed scripts/zero3.json \ + --model_name_or_path ${LLM_VERSION} \ + --version ${PROMPT_VERSION} \ + --data_path=llava_1_6.json \ + --image_folder your_image_folder \ + --pretrain_mm_mlp_adapter="/checkpoints/projectors/${BASE_RUN_NAME}/mm_projector.bin" \ + --mm_tunable_parts="mm_vision_tower,mm_mlp_adapter,mm_language_model" \ + --mm_vision_tower_lr=2e-6 \ + --vision_tower ${VISION_MODEL_VERSION} \ + --mm_projector_type mlp2x_gelu \ + --mm_vision_select_layer -2 \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --group_by_modality_length True \ + --image_aspect_ratio anyres \ + --image_grid_pinpoints "[(336, 672), (672, 336), (672, 672), (1008, 336), (336, 1008)]" \ + --mm_patch_merge_type spatial_unpad \ + --bf16 True \ + --run_name $MID_RUN_NAME \ + --output_dir "/checkpoints/${MID_RUN_NAME}" \ + --num_train_epochs 1 \ + --per_device_train_batch_size 4 \ + --per_device_eval_batch_size 4 \ + --gradient_accumulation_steps 1 \ + --evaluation_strategy "no" \ + --save_strategy "steps" \ + --save_steps 3000 \ + --save_total_limit 1 \ + --learning_rate 1e-5 \ + --weight_decay 0. \ + --warmup_ratio 0.03 \ + --lr_scheduler_type "cosine" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 32768 \ + --gradient_checkpointing True \ + --dataloader_num_workers 16 \ + --lazy_preprocess True \ + --report_to wandb \ + --torch_compile True \ + --torch_compile_backend "inductor" \ + --dataloader_drop_last True \ + --attn_implementation sdpa + +# You can delete the sdpa attn_implementation if you want to use flash attn diff --git a/scripts/train/direct_finetune_siglip_a4.sh b/scripts/train/direct_finetune_siglip_a4.sh new file mode 100644 index 0000000..2a55cd9 --- /dev/null +++ b/scripts/train/direct_finetune_siglip_a4.sh @@ -0,0 +1,67 @@ +export OMP_NUM_THREADS=8 +export NCCL_IB_DISABLE=0 +export NCCL_IB_GID_INDEX=3 +export NCCL_SOCKET_IFNAME=eth0 +export NCCL_DEBUG=INFO + +LLM_VERSION="Qwen/Qwen2-7B-Instruct" +LLM_VERSION_CLEAN="${LLM_VERSION//\//_}" +VISION_MODEL_VERSION="google/siglip-so400m-patch14-384" +VISION_MODEL_VERSION_CLEAN="${VISION_MODEL_VERSION//\//_}" + +############### Pretrain ################ + +PROMPT_VERSION="qwen_1_5" + +BASE_RUN_NAME="llavanext-${VISION_MODEL_VERSION_CLEAN}-${LLM_VERSION_CLEAN}-mlp2x_gelu-pretrain_blip558k_plain" +echo "BASE_RUN_NAME: ${BASE_RUN_NAME}" + +CKPT_PATH=$LLM_VERSION # this could also be the previous stage checkpoint + +ACCELERATE_CPU_AFFINITY=1 torchrun --nproc_per_node="${NUM_GPUS}" --nnodes="${NNODES}" --node_rank="${RANK}" --master_addr="${ADDR}" --master_port="${PORT}" \ + llava/train/train_mem.py \ + --deepspeed scripts/zero3.json \ + --model_name_or_path ${CKPT_PATH} \ + --version ${PROMPT_VERSION} \ + --data_path=llava_1_6.json \ + --image_folder your_image_folder \ + --pretrain_mm_mlp_adapter="/checkpoints/projectors/${BASE_RUN_NAME}/mm_projector.bin" \ + --mm_tunable_parts="mm_vision_tower,mm_mlp_adapter,mm_language_model" \ + --mm_vision_tower_lr=2e-6 \ + --vision_tower ${VISION_MODEL_VERSION} \ + --mm_projector_type mlp2x_gelu \ + --mm_vision_select_layer -2 \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --group_by_modality_length True \ + --image_aspect_ratio anyres \ + --image_grid_pinpoints "[(384, 768), (768, 384), (768, 768), (1152, 384), (384, 1152)]" \ + --mm_patch_merge_type spatial_unpad \ + --bf16 True \ + --run_name $MID_RUN_NAME \ + --output_dir "/checkpoints/${MID_RUN_NAME}" \ + --num_train_epochs 1 \ + --per_device_train_batch_size 4 \ + --per_device_eval_batch_size 4 \ + --gradient_accumulation_steps 1 \ + --evaluation_strategy "no" \ + --save_strategy "steps" \ + --save_steps 3000 \ + --save_total_limit 1 \ + --learning_rate 1e-5 \ + --weight_decay 0. \ + --warmup_ratio 0.03 \ + --lr_scheduler_type "cosine" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 32768 \ + --gradient_checkpointing True \ + --dataloader_num_workers 16 \ + --lazy_preprocess True \ + --report_to wandb \ + --torch_compile True \ + --torch_compile_backend "inductor" \ + --dataloader_drop_last True \ + --attn_implementation sdpa + +# You can delete the sdpa attn_implementation if you want to use flash attn diff --git a/scripts/train/dpo.sh b/scripts/train/dpo.sh new file mode 100644 index 0000000..83bfe84 --- /dev/null +++ b/scripts/train/dpo.sh @@ -0,0 +1,64 @@ +export OMP_NUM_THREADS=8 +export NCCL_IB_DISABLE=0 +export NCCL_IB_GID_INDEX=3 +# export NCCL_IB_HCA=${ARNOLD_RDMA_DEVICE} +export NCCL_SOCKET_IFNAME=eth0 +export NCCL_DEBUG=INFO + +VISION_MODEL_VERSION="openai/clip-vit-large-patch14-336" +VISION_MODEL_VERSION_CLEAN="${VISION_MODEL_VERSION//\//_}" + +############### Pretrain ################ + +# Stage 2 +PROMPT_VERSION="qwen_1_5" + +#torchrun --nproc_per_node="${ARNOLD_WORKER_GPU}" --nnodes="${ARNOLD_WORKER_NUM}" --node_rank="${ARNOLD_ID}" --master_addr="${METIS_WORKER_0_HOST}" --master_port="${port_in_cmd}" \ +ACCELERATE_CPU_AFFINITY=1 torchrun --nproc_per_node="${ARNOLD_WORKER_GPU}" --nnodes="${ARNOLD_WORKER_NUM}" --node_rank="${ARNOLD_ID}" --master_addr="${METIS_WORKER_0_HOST}" --master_port="${port_in_cmd}" \ + llava/train/train_dpo.py \ + --deepspeed scripts/zero3.json \ + --model_name_or_path lmms-lab/LongVA-7B \ + --version $PROMPT_VERSION \ + --dpo_alpha 1.0 --beta 0.1 --gamma 0 \ + --data_path="/data/llava_video/shareVideoGPTV/dpo/sft_dpo_17k.jsonl" \ + --image_folder /data/llava_data \ + --video_folder /llava_video/shareVideoGPTV/frames/all_frames/ \ + --mm_tunable_parts="mm_vision_tower,mm_mlp_adapter,mm_language_model" \ + --vision_tower ${VISION_MODEL_VERSION} \ + --mm_projector_type mlp2x_gelu \ + --mm_vision_select_layer -2 \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --mm_spatial_pool_stride 2 \ + --mm_resampler_type "spatial_pool" \ + --mm_spatial_pool_out_channels 1024 \ + --group_by_modality_length True \ + --image_aspect_ratio anyres \ + --image_grid_pinpoints "[(336, 672), (672, 336), (672, 672), (1008, 336), (336, 1008)]" \ + --mm_patch_merge_type unires \ + --bf16 True \ + --run_name $MID_RUN_NAME \ + --output_dir "/checkpoints/${MID_RUN_NAME}" \ + --num_train_epochs 3 \ + --per_device_train_batch_size 1 \ + --per_device_eval_batch_size 4 \ + --gradient_accumulation_steps 16 \ + --evaluation_strategy "no" \ + --save_strategy "steps" \ + --save_steps 3000 \ + --save_total_limit 1 \ + --learning_rate 5e-7 \ + --weight_decay 0. \ + --warmup_ratio 0.1 \ + --lr_scheduler_type "linear" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 32768 \ + --gradient_checkpointing True \ + --dataloader_num_workers 16 \ + --lazy_preprocess True \ + --report_to wandb \ + --torch_compile True \ + --torch_compile_backend "inductor" \ + --dataloader_drop_last True \ + --attn_implementation sdpa \ No newline at end of file diff --git a/scripts/train/dpo_ov7b.sh b/scripts/train/dpo_ov7b.sh new file mode 100644 index 0000000..26748e4 --- /dev/null +++ b/scripts/train/dpo_ov7b.sh @@ -0,0 +1,69 @@ +export OMP_NUM_THREADS=8 +export NCCL_IB_DISABLE=0 +export NCCL_IB_GID_INDEX=3 +# export NCCL_IB_HCA=${ARNOLD_RDMA_DEVICE} +export NCCL_SOCKET_IFNAME=eth0 +export NCCL_DEBUG=INFO + +VISION_MODEL_VERSION="google/siglip-so400m-patch14-384" +VISION_MODEL_VERSION_CLEAN="${VISION_MODEL_VERSION//\//_}" + +# DPO Stage +PROMPT_VERSION="qwen_1_5" +SFT_MODEL="lmms-lab/llava-onevision-qwen2-7b-ov" +EPOCH=1 +beta=0.1 + +DPO_RUN_NAME="llava-onevision-qwen2-7b-ov_dpo-beta${beta}-epoch${EPOCH}" +DPO_CLEAN_NAME="${DPO_RUN_NAME##*/}" +OUTPUT_DIR="/${DPO_CLEAN_NAME}" +DATA_PATH="" + +echo $DPO_RUN_NAME + +ACCELERATE_CPU_AFFINITY=1 torchrun --nproc_per_node="${NUM_GPUS}" --nnodes="${NNODES}" --node_rank="${RANK}" --master_addr="${ADDR}" --master_port="${PORT}" \ + llava/train/train_dpo.py \ + --deepspeed scripts/zero3.json \ + --model_name_or_path=${SFT_MODEL} \ + --dpo_alpha=1.0 \ + --beta=${beta} \ + --gamma=0 \ + --version $PROMPT_VERSION \ + --data_path=$DATA_PATH \ + --image_folder "" \ + --mm_tunable_parts="mm_vision_tower,mm_mlp_adapter,mm_language_model" \ + --unfreeze_mm_vision_tower True \ + --vision_tower ${VISION_MODEL_VERSION} \ + --mm_projector_type mlp2x_gelu \ + --mm_vision_select_layer -2 \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --group_by_modality_length True \ + --image_aspect_ratio anyres_max_9 \ + --image_grid_pinpoints "(1x1),...,(6x6)" \ + --mm_patch_merge_type spatial_unpad \ + --bf16 True \ + --run_name $DPO_CLEAN_NAME \ + --output_dir $OUTPUT_DIR \ + --num_train_epochs $EPOCH \ + --per_device_train_batch_size 1 \ + --per_device_eval_batch_size 1 \ + --gradient_accumulation_steps 8 \ + --evaluation_strategy "no" \ + --save_strategy "steps" \ + --save_steps 1000 \ + --save_total_limit 1 \ + --learning_rate 5e-7 \ + --weight_decay 0. \ + --warmup_ratio 0.1 \ + --lr_scheduler_type "cosine" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 32768 \ + --gradient_checkpointing True \ + --dataloader_num_workers 4 \ + --lazy_preprocess True \ + --report_to wandb \ + --dataloader_drop_last True + + diff --git a/scripts/train/finetune_ov_vla.sh b/scripts/train/finetune_ov_vla.sh new file mode 100644 index 0000000..2a5d012 --- /dev/null +++ b/scripts/train/finetune_ov_vla.sh @@ -0,0 +1,74 @@ +export OMP_NUM_THREADS=8 +export NCCL_IB_DISABLE=0 +export NCCL_IB_GID_INDEX=3 +export NCCL_SOCKET_IFNAME=eth0 +export NCCL_DEBUG=INFO +VISION_MODEL_VERSION="google/siglip-so400m-patch14-384" +VISION_MODEL_VERSION_CLEAN="${VISION_MODEL_VERSION//\//_}" + +############### Pretrain ################ + +BASE_RUN_NAME="llavanext-google_siglip-so400m-patch14-384-Qwen_Qwen2-7B-Instruct-mlp2x_gelu-pretrain_blip558k_plain" +echo "BASE_RUN_NAME: ${BASE_RUN_NAME}" + +############### Finetune ################ +MIXTURE="qwen" + +# Stage 2 +PROMPT_VERSION="qwen_vla" +RUN_NAME="llava-openvla-${MIXTURE}" +PREV_STAGE_CHECKPOINT="/mnt/d/temp/huggingface/hub/llava_qwen_vla" +# PREV_STAGE_CHECKPOINT="/mnt/d/ckpt/llava-openvla-qwen" +echo "PREV_STAGE_CHECKPOINT: ${PREV_STAGE_CHECKPOINT}" +echo "MID_RUN_NAME: ${RUN_NAME}" + +NUM_GPUS=1 +NNODES=1 + +ACCELERATE_CPU_AFFINITY=1 torchrun --nproc_per_node="${NUM_GPUS}" --nnodes="${NNODES}" \ + llava/train/train_mem.py \ + --deepspeed scripts/zero2.json \ + --model_name_or_path $PREV_STAGE_CHECKPOINT \ + --version $PROMPT_VERSION \ + --data_path /root/code/openvla-llava/LLaVA-Next/scripts/train/vla.yaml \ + --image_folder /root/code/openvla-llava/LLaVA-Next/images \ + --mm_tunable_parts="mm_language_model" \ + --mm_vision_tower_lr=2e-6 \ + --vision_tower google/siglip-so400m-patch14-384 \ + --mm_projector_type mlp2x_gelu \ + --mm_vision_select_layer -2 \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --group_by_modality_length True \ + --image_aspect_ratio anyres_max_9 \ + --image_grid_pinpoints "(1x1),...,(6x6)" \ + --mm_patch_merge_type spatial_unpad \ + --bf16 True \ + --run_name $RUN_NAME \ + --output_dir /mnt/d/ckpt_3_28/$RUN_NAME \ + --num_train_epochs 5 \ + --per_device_train_batch_size 8 \ + --per_device_eval_batch_size 100 \ + --gradient_accumulation_steps 2 \ + --evaluation_strategy "no" \ + --save_strategy "steps" \ + --save_steps 900 \ + --save_total_limit 2 \ + --learning_rate 1e-4 \ + --weight_decay 0. \ + --warmup_ratio 0.03 \ + --lr_scheduler_type "cosine" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 4096 \ + --gradient_checkpointing True \ + --dataloader_num_workers 1 \ + --lazy_preprocess True \ + --report_to wandb \ + --torch_compile True \ + --torch_compile_backend "inductor" \ + --dataloader_drop_last True \ + --frames_upbound 1 +exit 0; + +# You can delete the sdpa attn_implementation if you want to use flash attn diff --git a/scripts/train/finetune_si.sh b/scripts/train/finetune_si.sh new file mode 100644 index 0000000..32768f3 --- /dev/null +++ b/scripts/train/finetune_si.sh @@ -0,0 +1,73 @@ +export OMP_NUM_THREADS=8 +export NCCL_IB_DISABLE=0 +export NCCL_IB_GID_INDEX=3 +export NCCL_SOCKET_IFNAME=eth0 +export NCCL_DEBUG=INFO + +LLM_VERSION="Qwen/Qwen2-7B-Instruct" +# for 7b model we recommend bs=1, accum=2, 16 nodes, 128 gpus, lr=1e-5, warmup=0.03 +# for 72b model we recommend bs=1, accum=1, 32 nodes, 256 gpus, lr=1e-5, warmup=0.03 +LLM_VERSION_CLEAN="${LLM_VERSION//\//_}" +VISION_MODEL_VERSION="google/siglip-so400m-patch14-384" +VISION_MODEL_VERSION_CLEAN="${VISION_MODEL_VERSION//\//_}" + +############### Pretrain ################ + +BASE_RUN_NAME="llavanext-google_siglip-so400m-patch14-384-Qwen_Qwen2-7B-Instruct-mlp2x_gelu-pretrain_blip558k_plain" +echo "BASE_RUN_NAME: ${BASE_RUN_NAME}" + +############### Finetune ################ + +# Stage 2 +PROMPT_VERSION="qwen_1_5" +RUN_NAME="llava-onevision-${VISION_MODEL_VERSION_CLEAN}-${LLM_VERSION_CLEAN}-si_stage_am9" +PREV_STAGE_CHECKPOINT="/mnt/bn/vl-research/checkpoints/onevision/xxxxxxxxxxxxxxxx" # replace it with your last checkpoint training from mid stage +echo "PREV_STAGE_CHECKPOINT: ${PREV_STAGE_CHECKPOINT}" +echo "MID_RUN_NAME: ${RUN_NAME}" + +ACCELERATE_CPU_AFFINITY=1 torchrun --nproc_per_node="${NUM_GPUS}" --nnodes="${NNODES}" --node_rank="${RANK}" --master_addr="${ADDR}" --master_port="${PORT}" \ + llava/train/train_mem.py \ + --deepspeed scripts/zero3.json \ + --model_name_or_path $PREV_STAGE_CHECKPOINT \ + --version $PROMPT_VERSION \ + --data_path /mnt/bn/vl-research/workspace/boli01/projects/LLaVA_Next/scripts/i18n/scale_llms/next_3p2m_single_image.yaml \ + --image_folder /mnt/bn/vl-research/data/llava_data \ + --video_folder /mnt/bn/vl-research/data/llava_video \ + --mm_tunable_parts="mm_vision_tower,mm_mlp_adapter,mm_language_model" \ + --mm_vision_tower_lr=2e-6 \ + --vision_tower ${VISION_MODEL_VERSION} \ + --mm_projector_type mlp2x_gelu \ + --mm_vision_select_layer -2 \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --group_by_modality_length True \ + --image_aspect_ratio anyres_max_9 \ + --image_grid_pinpoints "(1x1),...,(6x6)" \ + --mm_patch_merge_type spatial_unpad \ + --bf16 True \ + --run_name $RUN_NAME \ + --output_dir /mnt/bn/vl-research/checkpoints/onevision/$RUN_NAME \ + --num_train_epochs 1 \ + --per_device_train_batch_size 1 \ + --per_device_eval_batch_size 4 \ + --gradient_accumulation_steps 2 \ + --evaluation_strategy "no" \ + --save_strategy "steps" \ + --save_steps 1000 \ + --save_total_limit 1 \ + --learning_rate 1e-5 \ + --weight_decay 0. \ + --warmup_ratio 0.03 \ + --lr_scheduler_type "cosine" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 32768 \ + --gradient_checkpointing True \ + --dataloader_num_workers 4 \ + --lazy_preprocess True \ + --report_to wandb \ + --torch_compile True \ + --torch_compile_backend "inductor" \ + --dataloader_drop_last True \ + --frames_upbound 32 +exit 0; diff --git a/scripts/train/mid_stage.yaml b/scripts/train/mid_stage.yaml new file mode 100644 index 0000000..5669987 --- /dev/null +++ b/scripts/train/mid_stage.yaml @@ -0,0 +1,17 @@ +datasets: + - json_path: /mnt/bn/vl-research/data/llava_instruct/blip558k_stage1.5_finetune_w_prompt.json # released in lmms-lab/LLaVA-ReCap-* + sampling_strategy: all + - json_path: /mnt/bn/vl-research/data/llava_instruct/coco118k_stage1.5_finetune_w_prompt.json # released in lmms-lab/LLaVA-ReCap-* + sampling_strategy: all + - json_path: /mnt/bn/vl-research/data/llava_instruct/cc3m_recap_data_prompt_v2.json # released in lmms-lab/LLaVA-ReCap-* + sampling_strategy: all + - json_path: /mnt/bn/vl-research/data/llava_instruct/ureader_tr_sft.json # released in lmms-lab/LLaVA-OneVision-Mid-Data + sampling_strategy: all + - json_path: /mnt/bn/vl-research/data/llava_instruct/instruct_azure_dc_zh_92K.json # not released, explained at https://github.com/LLaVA-VL/LLaVA-NeXT/tree/main/scripts/train + sampling_strategy: all + - json_path: /mnt/bn/vl-research/data/llava_instruct/Evol-Instruct-GPT4-Turbo-143K.json # released in lmms-lab/LLaVA-OneVision-Mid-Data + sampling_strategy: all + - json_path: /mnt/bn/vl-research/data/llava_instruct/synthdog_zh/synthdog_zh_100k.json # released in lmms-lab/LLaVA-OneVision-Mid-Data + sampling_strategy: all + - json_path: /mnt/bn/vl-research/data/llava_instruct/synthdog_en/synthdog_en_100k.json # released in lmms-lab/LLaVA-OneVision-Mid-Data + sampling_strategy: all \ No newline at end of file diff --git a/scripts/train/onevision.yaml b/scripts/train/onevision.yaml new file mode 100644 index 0000000..117a5e8 --- /dev/null +++ b/scripts/train/onevision.yaml @@ -0,0 +1,185 @@ +datasets: + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/llava_next_fit_mix_filtered_text_wild_738590.json + sampling_strategy: "first:50%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/llava_wild_4v_39k.json # not released + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/llava_wild_4v_12k.json # not released + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/mavis_math_metagen_87358.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/mavis_math_rule_geo_100000.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/cambrian_filtered_gpt4vo_sp_token_fltd_max10k_checked.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/VisualWebInstruct_filtered_263589.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/visual_chat_en_26048_gpt4o_coco_checked.json # not released + sampling_strategy: "all" + # - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/gpt4o_combinations_51316.json + # sampling_strategy: "all" + # - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/chrome_writting_train_8835.json + # sampling_strategy: "first:20%" + # - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/k12_printing_train_256646.json + # sampling_strategy: "first:1%" + # - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/iiit5k_annotations_2000.json + # sampling_strategy: "first:20%" + # - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/hme100k_train_clean_74502.json + # sampling_strategy: "first:10%" + # - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sroie_data_33626.json + # sampling_strategy: "first:1%" + # - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/orand_car_a_train_2009.json + # sampling_strategy: "first:10%" + # - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/orand_car_b_train_3000.json + # sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/llavar_gpt4_20k.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/ai2d_azuregpt_detailed_understanding_4874.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/infographic_vqa_4404.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/infographic_azuregpt4v_1992.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/lrv_chart_1787.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/lrv_normal_gpt4v_filtered_10500.json + sampling_strategy: "first:10%" + # - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/scienceqa_nona_context_19218.json + # sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/allava_instruct_vflan4v_20000.json + sampling_strategy: "first:30%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/allava_instruct_laion4v_50000.json + sampling_strategy: "first:30%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/textocr_gpt4v_train_converted_25114.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/ai2d_train_internvl_single_12413.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/textcaps_train_21952.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/ureader_new/ureader_qa_sft.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/ureader_new/ureader_cap_sft.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/ureader_new/ureader_ie_sft.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/ureader_new/ureader_kg_sft.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/vision_flan_filtered_186070.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/mathqa_29837.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/geo3k_2101.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/geo170k_qa_converted_67833.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/geo170k_align_converted_60252.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4o_dataset.jsonl + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4v-coco-50k.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4v-knowledge-2k.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4v-llava-30k.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4v-sam-20k.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_CLEVR-Math_5290.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_FigureQA_17597.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_Geometry3K_9734.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_GeoQA+_17172.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_GEOS_508.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_IconQA_22599.json + sampling_strategy: "first:5%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_MapQA_5235.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_PlotQA_5485.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_PMC-VQA_35958.json + sampling_strategy: "first:1%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_Super-CLEVR_8652.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_TabMWP_22462.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_TQA_10181.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_UniGeo_11959.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_VizWiz_6614.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_VQA-AS_5907.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_VQA-RAD_2130.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/magpie_pro_qwen2_72b_st_300000_sp_token_fltd_299992.json + sampling_strategy: "end:20%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/magpie_pro_l3_80b_st_300000.json + sampling_strategy: "end:20%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/magpie_pro_l3_80b_mt_300000_sp_token_fltd_299998.json + sampling_strategy: "end:20%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/image_textualization_dataset_filtered.json + sampling_strategy: "first:20%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/ai2d_llava_format_2434.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/chart2text_26961.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/chartqa_18265_llava_format.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/diagram_image_to_text_300.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/hateful_memes_8500_llava_format.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/hitab_2500_llava_format.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/iam_5663.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/infographic_vqa_2118_llava_format.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/intergps_1280_llava_format.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/mapqa_37417_llava_format.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/rendered_text_10000.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/robut_sqa_8514.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/robut_wikisql_74989.json + sampling_strategy: "first:10%" + # - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/robut_wtq_38246_llava_format_filtered_4000tokens_38236.json + # sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/screen2words_15730.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/tabmwp_22722.json + sampling_strategy: "first:5%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/tallyqa_98680_llava_format.json + sampling_strategy: "first:5%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/st_vqa_17247_llava_format.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/tqa_llava_format_27307.json + sampling_strategy: "first:5%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/visual7w_llava_format_14366.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/visualmrc_3027.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/vqarad_313_llava_format.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/vsr_2157_llava_format.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/vistext_9969.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/websight_10000.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/llava_ofa_DEMON-FULL_filtered_311085.json # released in lmms-lab/M4-Instruct + sampling_strategy: all + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/llava_ofa_mantis-instruct_reformatted.json # released in lmms-lab/M4-Instruct + sampling_strategy: all + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/academic_source_30s_v1_all.json # will be released in next version of LLaVA-NeXT-Video + sampling_strategy: all + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/0718_0_30_s_academic_mc_v0_1_all.json # will be released in next version of LLaVA-NeXT-Video + sampling_strategy: all + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4video_255000.json # download from sharegpt4video + sampling_strategy: all diff --git a/scripts/train/pretrain_clip.sh b/scripts/train/pretrain_clip.sh new file mode 100644 index 0000000..2eb5c27 --- /dev/null +++ b/scripts/train/pretrain_clip.sh @@ -0,0 +1,55 @@ +export OMP_NUM_THREADS=8 +export NCCL_IB_DISABLE=0 +export NCCL_IB_GID_INDEX=3 +export NCCL_SOCKET_IFNAME=eth0 +export NCCL_DEBUG=INFO + +LLM_VERSION="Qwen/Qwen2-7B-Instruct" +LLM_VERSION_CLEAN="${LLM_VERSION//\//_}" +VISION_MODEL_VERSION="openai/clip-vit-large-patch14-336" +VISION_MODEL_VERSION_CLEAN="${VISION_MODEL_VERSION//\//_}" + +############### Pretrain ################ + +PROMPT_VERSION=plain + +BASE_RUN_NAME="llavanext-${VISION_MODEL_VERSION_CLEAN}-${LLM_VERSION_CLEAN}-mlp2x_gelu-pretrain_blip558k_plain" +echo "BASE_RUN_NAME: ${BASE_RUN_NAME}" + +ACCELERATE_CPU_AFFINITY=1 torchrun --nproc_per_node="${NUM_GPUS}" --nnodes="${NNODES}" --node_rank="${RANK}" --master_addr="${ADDR}" --master_port="${PORT}" \ + llava/train/train_mem.py \ + --deepspeed scripts/zero3.json \ + --model_name_or_path ${LLM_VERSION} \ + --version ${PROMPT_VERSION} \ + --data_path /blip_558k/blip_558k_plain.json \ + --image_folder /blip_558k/images \ + --vision_tower ${VISION_MODEL_VERSION} \ + --mm_tunable_parts="mm_mlp_adapter" \ + --mm_vision_select_layer -2 \ + --mm_projector_type mlp2x_gelu \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --bf16 True \ + --output_dir /checkpoints/projectors/${BASE_RUN_NAME} \ + --num_train_epochs 1 \ + --per_device_train_batch_size 16 \ + --per_device_eval_batch_size 4 \ + --gradient_accumulation_steps 1 \ + --evaluation_strategy "no" \ + --save_strategy "no" \ + --save_steps 50000 \ + --learning_rate 1e-3 \ + --weight_decay 0. \ + --warmup_ratio 0.03 \ + --lr_scheduler_type "cosine" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 8192 \ + --gradient_checkpointing True \ + --dataloader_num_workers 16 \ + --lazy_preprocess True \ + --report_to wandb \ + --run_name $BASE_RUN_NAME \ + --attn_implementation sdpa + +# You can delete the sdpa attn_implementation if you want to use flash attn \ No newline at end of file diff --git a/scripts/train/pretrain_siglip.sh b/scripts/train/pretrain_siglip.sh new file mode 100644 index 0000000..ef13793 --- /dev/null +++ b/scripts/train/pretrain_siglip.sh @@ -0,0 +1,55 @@ +export OMP_NUM_THREADS=8 +export NCCL_IB_DISABLE=0 +export NCCL_IB_GID_INDEX=3 +export NCCL_SOCKET_IFNAME=eth0 +export NCCL_DEBUG=INFO + +LLM_VERSION="Qwen/Qwen2-7B-Instruct" +LLM_VERSION_CLEAN="${LLM_VERSION//\//_}" +VISION_MODEL_VERSION="google/siglip-so400m-patch14-384" +VISION_MODEL_VERSION_CLEAN="${VISION_MODEL_VERSION//\//_}" + +############### Pretrain ################ + +PROMPT_VERSION=plain + +BASE_RUN_NAME="llavanext-${VISION_MODEL_VERSION_CLEAN}-${LLM_VERSION_CLEAN}-mlp2x_gelu-pretrain_blip558k_plain" +echo "BASE_RUN_NAME: ${BASE_RUN_NAME}" + +ACCELERATE_CPU_AFFINITY=1 torchrun --nproc_per_node="${NUM_GPUS}" --nnodes="${NNODES}" --node_rank="${RANK}" --master_addr="${ADDR}" --master_port="${PORT}" \ + llava/train/train_mem.py \ + --deepspeed scripts/zero3.json \ + --model_name_or_path ${LLM_VERSION} \ + --version ${PROMPT_VERSION} \ + --data_path /blip_558k/blip_558k_plain.json \ + --image_folder /blip_558k/images \ + --vision_tower ${VISION_MODEL_VERSION} \ + --mm_tunable_parts="mm_mlp_adapter" \ + --mm_vision_select_layer -2 \ + --mm_projector_type mlp2x_gelu \ + --mm_use_im_start_end False \ + --mm_use_im_patch_token False \ + --bf16 True \ + --output_dir /checkpoints/projectors/${BASE_RUN_NAME} \ + --num_train_epochs 1 \ + --per_device_train_batch_size 16 \ + --per_device_eval_batch_size 4 \ + --gradient_accumulation_steps 1 \ + --evaluation_strategy "no" \ + --save_strategy "no" \ + --save_steps 50000 \ + --learning_rate 1e-3 \ + --weight_decay 0. \ + --warmup_ratio 0.03 \ + --lr_scheduler_type "cosine" \ + --logging_steps 1 \ + --tf32 True \ + --model_max_length 8192 \ + --gradient_checkpointing True \ + --dataloader_num_workers 16 \ + --lazy_preprocess True \ + --report_to wandb \ + --run_name $BASE_RUN_NAME \ + --attn_implementation sdpa + +# You can delete the sdpa attn_implementation if you want to use flash attn \ No newline at end of file diff --git a/scripts/train/single_image.yaml b/scripts/train/single_image.yaml new file mode 100644 index 0000000..a21e3a1 --- /dev/null +++ b/scripts/train/single_image.yaml @@ -0,0 +1,187 @@ +datasets: + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/llava_next_fit_mix_filtered_text_wild_738590.json # released in lmms-lab/LLaVA-NeXT-Data + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/llava_wild_4v_39k.json # not released + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/llava_wild_4v_12k.json # not released + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/llavar_gpt4_20k.json + sampling_strategy: "all" + # - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sroie_data_33626.json + # sampling_strategy: "first:10%" + # - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/orand_car_a_train_2009.json + # sampling_strategy: "all" + # - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/orand_car_b_train_3000.json + # sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/mavis_math_metagen_87358.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/mavis_math_rule_geo_100000.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/chrome_writting_train_8835.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/k12_printing_train_256646.json + sampling_strategy: "first:1%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/iiit5k_annotations_2000.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/hme100k_train_clean_74502.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/ai2d_azuregpt_detailed_understanding_4874.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/infographic_vqa_4404.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/infographic_azuregpt4v_1992.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/lrv_chart_1787.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/lrv_normal_gpt4v_filtered_10500.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/scienceqa_nona_context_19218.json + sampling_strategy: "first:5%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/allava_instruct_vflan4v_20000.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/allava_instruct_laion4v_50000.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/textocr_gpt4v_train_converted_25114.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/ai2d_train_internvl_single_12413.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/textcaps_train_21952.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/ureader_new/ureader_qa_sft.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/ureader_new/ureader_cap_sft.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/ureader_new/ureader_ie_sft.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/ureader_new/ureader_kg_sft.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/vision_flan_filtered_186070.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/mathqa_29837.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/geo3k_2101.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/geo170k_qa_converted_67833.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/geo170k_align_converted_60252.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4v-coco-50k.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4v-knowledge-2k.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4v-llava-30k.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4v-sam-20k.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_CLEVR-Math_5290.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_FigureQA_17597.json + sampling_strategy: "first:5%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_Geometry3K_9734.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_GeoQA+_17172.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_GEOS_508.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_IconQA_22599.json + sampling_strategy: "first:5%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_MapQA_5235.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_PMC-VQA_35958.json + sampling_strategy: "first:5%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_Super-CLEVR_8652.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_TabMWP_22462.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_TQA_10181.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_UniGeo_11959.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_VizWiz_6614.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_VQA-AS_5907.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/MathV360K_VQA-RAD_2130.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/Evol-Instruct-GPT4-Turbo-143000.json + sampling_strategy: "first:30%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/magpie_pro_qwen2_72b_st_300000_sp_token_fltd_299992.json + sampling_strategy: "first:50%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/magpie_pro_l3_80b_st_300000.json + sampling_strategy: "first:50%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/magpie_pro_l3_80b_mt_300000_sp_token_fltd_299998.json + sampling_strategy: "first:50%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/image_textualization_dataset_filtered.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/cambrian_filtered_gpt4vo_sp_token_fltd_max10k.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/real_vision_flan/sharegpt4o_dataset.jsonl + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/ai2d_llava_format_2434.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/aokvqa_16539_llava_format.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/chart2text_26961.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/chartqa_18265_llava_format.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/clevr_70000_llava_format.json + sampling_strategy: "first:1%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/diagram_image_to_text_300.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/dvqa_200000_llava_format.json + sampling_strategy: "first:1%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/figureqa_100000_llava_format.json + sampling_strategy: "first:1%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/geomverse_9303.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/hateful_memes_8500_llava_format.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/hitab_2500_llava_format.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/iam_5663.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/raven_42000.json + sampling_strategy: "first:5%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/iconqa_llava_format_27307.json + sampling_strategy: "first:5%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/infographic_vqa_2118_llava_format.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/intergps_1280_llava_format.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/mapqa_37417_llava_format.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/multihiertt_7619.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/rendered_text_10000.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/robut_sqa_8514.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/robut_wikisql_74989.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/robut_wtq_38246_llava_format.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/screen2words_15730.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/scienceqa_llava_format_4976.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/tabmwp_22722.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/tallyqa_98680_llava_format.json + sampling_strategy: "first:10%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/st_vqa_17247_llava_format.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/tqa_llava_format_27307.json + sampling_strategy: "first:5%" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/visual7w_llava_format_14366.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/visualmrc_3027.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/vqarad_313_llava_format.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/vsr_2157_llava_format.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/vistext_9969.json + sampling_strategy: "all" + - json_path: /mnt/bn/vl-research/data/llava_instruct/cauldron/websight_10000.json + sampling_strategy: "all" diff --git a/scripts/train/test.yaml b/scripts/train/test.yaml new file mode 100644 index 0000000..b4a82b1 --- /dev/null +++ b/scripts/train/test.yaml @@ -0,0 +1,3 @@ +datasets: + - json_path: /data/jcy/data/llavaov/iiit5k/iiit5k.json + sampling_strategy: "all" \ No newline at end of file diff --git a/scripts/train/vla.yaml b/scripts/train/vla.yaml new file mode 100644 index 0000000..d7bf631 --- /dev/null +++ b/scripts/train/vla.yaml @@ -0,0 +1,3 @@ +datasets: + - json_path: /root/code/openvla-llava/LLaVA-Next/vla_llava.json + sampling_strategy: "all" \ No newline at end of file diff --git a/scripts/zero2.json b/scripts/zero2.json new file mode 100644 index 0000000..b5ba7eb --- /dev/null +++ b/scripts/zero2.json @@ -0,0 +1,41 @@ +{ + "fp16": { + "enabled": "auto", + "loss_scale": 0, + "loss_scale_window": 1000, + "initial_scale_power": 16, + "hysteresis": 2, + "min_loss_scale": 1 + }, + "bf16": { + "enabled": "auto" + }, + "optimizer": { + "type": "AdamW", + "params": { + "lr": "auto", + "betas": "auto", + "eps": "auto", + "weight_decay": "auto" + } + }, + "zero_optimization": { + "stage": 2, + "offload_optimizer": { + "device": "none", + "pin_memory": true + }, + "allgather_partitions": true, + "allgather_bucket_size": 2e8, + "overlap_comm": false, + "reduce_scatter": true, + "reduce_bucket_size": 2e8, + "contiguous_gradients": true + }, + "gradient_accumulation_steps": "auto", + "gradient_clipping": "auto", + "steps_per_print": 100, + "train_batch_size": "auto", + "train_micro_batch_size_per_gpu": "auto", + "wall_clock_breakdown": false +} \ No newline at end of file diff --git a/scripts/zero2_fused_adamw.json b/scripts/zero2_fused_adamw.json new file mode 100644 index 0000000..0c37172 --- /dev/null +++ b/scripts/zero2_fused_adamw.json @@ -0,0 +1,41 @@ +{ + "fp16": { + "enabled": "auto", + "loss_scale": 0, + "loss_scale_window": 1000, + "initial_scale_power": 16, + "hysteresis": 2, + "min_loss_scale": 1 + }, + "bf16": { + "enabled": "auto" + }, + "optimizer": { + "type": "AdamW", + "params": { + "lr": "auto", + "betas": "auto", + "eps": "auto", + "weight_decay": "auto" + } + }, + "zero_optimization": { + "stage": 2, + "offload_optimizer": { + "device": "none", + "pin_memory": true + }, + "allgather_partitions": true, + "allgather_bucket_size": 2e8, + "overlap_comm": true, + "reduce_scatter": true, + "reduce_bucket_size": 2e8, + "contiguous_gradients": true + }, + "gradient_accumulation_steps": "auto", + "gradient_clipping": "auto", + "steps_per_print": 100, + "train_batch_size": "auto", + "train_micro_batch_size_per_gpu": "auto", + "wall_clock_breakdown": false +} \ No newline at end of file diff --git a/scripts/zero2_offload.json b/scripts/zero2_offload.json new file mode 100644 index 0000000..2d24e89 --- /dev/null +++ b/scripts/zero2_offload.json @@ -0,0 +1,31 @@ +{ + "fp16": { + "enabled": "auto", + "loss_scale": 0, + "loss_scale_window": 1000, + "initial_scale_power": 16, + "hysteresis": 2, + "min_loss_scale": 1 + }, + "bf16": { + "enabled": "auto" + }, + "train_micro_batch_size_per_gpu": "auto", + "train_batch_size": "auto", + "gradient_accumulation_steps": "auto", + "zero_optimization": { + "stage": 2, + "offload_optimizer": { + "device": "cpu", + "pin_memory": true + }, + "offload_param": { + "device": "cpu", + "pin_memory": true + }, + "overlap_comm": true, + "contiguous_gradients": true, + "sub_group_size": 1e9, + "reduce_bucket_size": "auto" + } +} \ No newline at end of file diff --git a/scripts/zero3.json b/scripts/zero3.json new file mode 100644 index 0000000..02d3431 --- /dev/null +++ b/scripts/zero3.json @@ -0,0 +1,41 @@ +{ + "fp16": { + "enabled": "auto", + "loss_scale": 0, + "loss_scale_window": 1000, + "initial_scale_power": 16, + "hysteresis": 2, + "min_loss_scale": 1 + }, + "bf16": { + "enabled": "auto" + }, + + "zero_optimization": { + "stage": 3, + "offload_optimizer": { + "device": "none", + "pin_memory": true + }, + "offload_param": { + "device": "none", + "pin_memory": true + }, + "overlap_comm": true, + "contiguous_gradients": true, + "sub_group_size": 1e9, + "reduce_bucket_size": "auto", + "stage3_prefetch_bucket_size": "auto", + "stage3_param_persistence_threshold": "auto", + "stage3_max_live_parameters": 1e9, + "stage3_max_reuse_distance": 1e9, + "stage3_gather_16bit_weights_on_model_save": true + }, + + "gradient_accumulation_steps": "auto", + "gradient_clipping": "auto", + "steps_per_print": 100, + "train_batch_size": "auto", + "train_micro_batch_size_per_gpu": "auto", + "wall_clock_breakdown": false +} \ No newline at end of file diff --git a/scripts/zero3_offload.json b/scripts/zero3_offload.json new file mode 100644 index 0000000..9da12de --- /dev/null +++ b/scripts/zero3_offload.json @@ -0,0 +1,48 @@ +{ + "fp16": { + "enabled": "auto", + "loss_scale": 0, + "loss_scale_window": 1000, + "initial_scale_power": 16, + "hysteresis": 2, + "min_loss_scale": 1 + }, + "bf16": { + "enabled": "auto" + }, + "optimizer": { + "type": "AdamW", + "params": { + "lr": "auto", + "betas": "auto", + "eps": "auto", + "weight_decay": "auto" + } + }, + "zero_optimization": { + "stage": 3, + "offload_optimizer": { + "device": "cpu", + "pin_memory": true + }, + "offload_param": { + "device": "cpu", + "pin_memory": true + }, + "overlap_comm": true, + "contiguous_gradients": true, + "sub_group_size": 1e9, + "reduce_bucket_size": "auto", + "stage3_prefetch_bucket_size": "auto", + "stage3_param_persistence_threshold": "auto", + "stage3_max_live_parameters": 1e9, + "stage3_max_reuse_distance": 1e9, + "gather_16bit_weights_on_model_save": true + }, + "gradient_accumulation_steps": "auto", + "gradient_clipping": "auto", + "train_batch_size": "auto", + "train_micro_batch_size_per_gpu": "auto", + "steps_per_print": 1e5, + "wall_clock_breakdown": false +} \ No newline at end of file diff --git a/scripts/zero3pp.json b/scripts/zero3pp.json new file mode 100644 index 0000000..f7ca45f --- /dev/null +++ b/scripts/zero3pp.json @@ -0,0 +1,53 @@ +{ + "fp16": { + "enabled": "auto", + "loss_scale": 0, + "loss_scale_window": 1000, + "initial_scale_power": 16, + "hysteresis": 2, + "min_loss_scale": 1 + }, + "bf16": { + "enabled": "auto" + }, + "optimizer": { + "type": "AdamW", + "params": { + "lr": "auto", + "betas": "auto", + "eps": "auto", + "weight_decay": "auto" + } + }, + + "zero_optimization": { + "stage": 3, + "offload_optimizer": { + "device": "none", + "pin_memory": true + }, + "offload_param": { + "device": "none", + "pin_memory": true + }, + "overlap_comm": true, + "contiguous_gradients": true, + "zero_quantized_weights": true, + "zero_hpz_partition_size": 16, + "zero_quantized_gradients": true, + "sub_group_size": 1e9, + "reduce_bucket_size": "auto", + "stage3_prefetch_bucket_size": "auto", + "stage3_param_persistence_threshold": "auto", + "stage3_max_live_parameters": 1e9, + "stage3_max_reuse_distance": 1e9, + "stage3_gather_16bit_weights_on_model_save": true + }, + + "gradient_accumulation_steps": "auto", + "gradient_clipping": "auto", + "steps_per_print": 100, + "train_batch_size": "auto", + "train_micro_batch_size_per_gpu": "auto", + "wall_clock_breakdown": false +} \ No newline at end of file diff --git a/trl/__init__.py b/trl/__init__.py new file mode 100644 index 0000000..c9a1a6b --- /dev/null +++ b/trl/__init__.py @@ -0,0 +1,44 @@ +# flake8: noqa + +__version__ = "0.7.11.dev0" + +from .core import set_seed +from .environment import TextEnvironment, TextHistory +from .extras import BestOfNSampler +from .import_utils import ( + is_bitsandbytes_available, + is_diffusers_available, + is_npu_available, + is_peft_available, + is_wandb_available, + is_xpu_available, +) +from .models import ( + AutoModelForCausalLMWithValueHead, + AutoModelForSeq2SeqLMWithValueHead, + PreTrainedModelWrapper, + create_reference_model, + setup_chat_format, +) +from .trainer import ( + DataCollatorForCompletionOnlyLM, + DPOTrainer, + IterativeSFTTrainer, + ModelConfig, + PPOConfig, + PPOTrainer, + RewardConfig, + RewardTrainer, + SFTTrainer, +) +from .trainer.utils import get_kbit_device_map, get_peft_config, get_quantization_config + + +if is_diffusers_available(): + from .models import ( + DDPOPipelineOutput, + DDPOSchedulerOutput, + DDPOStableDiffusionPipeline, + DefaultDDPOStableDiffusionPipeline, + ) + from .trainer import DDPOConfig, DDPOTrainer diff --git a/trl/__pycache__/__init__.cpython-310.pyc b/trl/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e0b722933d244e94e2f4b497f13c8c54ca64f8e GIT binary patch literal 1325 zcmaKsOOM+&5XU9IZP~ki#ru9aPX&#gw1-}bqUpMe6xdxG@gf0u5x5q;E0iNC6y;=d z?WgHi;vtuw`VrbwXJjv8^iTo(f&9;q9CC(=8jYHTU+(P7&GUj~{YiuMFNcGluq9s* zzQrux_H)vP9Lw3%&&xa%SOJQxn2igv1Z7sv`bAlRDyu?`)xcp6)L9)GtN|NrBeR#} zCNxUvKcj8{V972&)72@JJYPW_0P`B z!S{n#uLizYy!^|?6T7ylBtm;#E-9q5FI{`H*}iN_Z_ko;N-(?gRV%;#w$)Vkh8J&e;e&lW1NnCmsE zjnYS2bWNj)pVlY3MRW2x)zL-d3wai)Q=TSVo?pBV^zAz?Q}IS{-}KjArsBi5*#7I& zn<#Omcn}IdTkO5!aV(VS&6MC;cuLHL65&jEKzo@MURoM^vs;Y4&xI*7t?DDTcMOl>q7FLzO<^Q#t$>tG?KXIJ;4lWl$)h4bLXR40@1s$0YE zy-)qE_{f#NvNV>Xdt>eEVs74aZZNgKVR}4FctFAP*ld!R-S%v`&lU+EQXcByGX@le z-qPR}OnZb8Pl8xTyqR&T<5ZgtY17%Q@KY>-mAIFQn({u%QjW{q&oljznDbQX|M9`v ztC4wiDu?>3GhMo_aXfE&*AZ!pe2~C74^i6zSm%OK=#)cy`HOV6`py z@cf*=d;Q`P&pp&i4m~djG3(wSzB82{@KBhlAL+d(6jUKs&o%A3eQfVnzA1N|QhwX+ ImI{CW3qf0xm;e9( literal 0 HcmV?d00001 diff --git a/trl/__pycache__/core.cpython-310.pyc b/trl/__pycache__/core.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d969a700b09188a081f36e2e42baba3b186e377c GIT binary patch literal 11247 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 literal 0 HcmV?d00001 diff --git a/trl/core.py b/trl/core.py new file mode 100644 index 0000000..4cf481e --- /dev/null +++ b/trl/core.py @@ -0,0 +1,329 @@ +# Copyright 2022 The HuggingFace Team. 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. +import gc +import random +import warnings +from contextlib import contextmanager +from typing import Dict, List, Optional, Tuple, Union + +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F +from torch.nn.utils.rnn import pad_sequence + +# from transformers import top_k_top_p_filtering + +from .import_utils import is_npu_available, is_xpu_available + + +try: + from collections.abc import Mapping +except ImportError: + from collections import Mapping + + +WANDB_PADDING = -1 + + +def top_k_top_p_filtering( + logits: torch.FloatTensor, + top_k: int = 0, + top_p: float = 1.0, + filter_value: float = -float("Inf"), + min_tokens_to_keep: int = 1, +) -> torch.FloatTensor: + """ + Filter a distribution of logits using top-k and/or nucleus (top-p) filtering. + + Args: + logits: logits distribution shape (batch size, vocabulary size) + top_k (`int`, *optional*, defaults to 0): + If > 0, only keep the top k tokens with highest probability (top-k filtering) + top_p (`float`, *optional*, defaults to 1.0): + If < 1.0, only keep the top tokens with cumulative probability >= top_p (nucleus filtering). Nucleus + filtering is described in Holtzman et al. (http://arxiv.org/abs/1904.09751) + min_tokens_to_keep (`int`, *optional*, defaults to 1): + Minimumber of tokens we keep per batch example in the output. + + From: https://gist.github.com/thomwolf/1a5a29f6962089e871b94cbd09daf317 + """ + + if top_k > 0: + logits = TopKLogitsWarper(top_k=top_k, filter_value=filter_value, min_tokens_to_keep=min_tokens_to_keep)(None, logits) + + if 0 <= top_p <= 1.0: + logits = TopPLogitsWarper(top_p=top_p, filter_value=filter_value, min_tokens_to_keep=min_tokens_to_keep)(None, logits) + + return logits + + +def flatten_dict(nested: Dict, sep: str = "/") -> Dict: + """Flatten dictionary and concatenate nested keys with separator.""" + + def recurse(nest: Dict, prefix: str, into: Dict) -> None: + for k, v in nest.items(): + if sep in k: + raise ValueError(f"separator '{sep}' not allowed to be in key '{k}'") + if isinstance(v, Mapping): + recurse(v, prefix + k + sep, into) + else: + into[prefix + k] = v + + flat = {} + recurse(nested, "", flat) + return flat + + +def convert_to_scalar(stats: Dict) -> Dict: + """ + Converts the stats from a flattened dict to single scalar dicts + """ + tensorboard_stats = {} + for k, v in stats.items(): + # for tensorboard compatibility - arrays and tensors are ignored with tensorboard + # therefore we convert single element tensors to scalars + if (isinstance(v, torch.Tensor) or isinstance(v, np.ndarray)) and (len(v.shape) == 0 or (len(v.shape) == 1 and v.shape[0] == 1)): + v = v.item() + tensorboard_stats[k] = v + return tensorboard_stats + + +def stack_dicts(stats_dicts: List[Dict]) -> Dict: + """Stack the values of a dict.""" + results = dict() + for k in stats_dicts[0]: + stats_list = [torch.flatten(d[k]) for d in stats_dicts] + results[k] = pad_sequence(stats_list, batch_first=True, padding_value=WANDB_PADDING) + return results + + +def add_suffix(input_dict: Dict, suffix: str) -> Dict: + """Add suffix to dict keys.""" + return dict((k + suffix, v) for k, v in input_dict.items()) + + +def pad_to_size(tensor: torch.Tensor, size: int, dim: int = 1, padding: int = 50256) -> torch.Tensor: + """Pad tensor to size.""" + t_size = tensor.size()[dim] + if t_size == size: + return tensor + else: + return torch.nn.functional.pad(tensor, (0, size - t_size), "constant", padding) + + +def logprobs_from_logits(logits: torch.Tensor, labels: torch.Tensor, gather: bool = True) -> torch.Tensor: + """ + See: https://github.com/pytorch/pytorch/issues/563#issuecomment-330103591 + """ + logp = F.log_softmax(logits, dim=2) + + if not gather: + return logp + logpy = torch.gather(logp, 2, labels.unsqueeze(2)).squeeze(-1) + return logpy + + +def whiten(values: torch.Tensor, shift_mean: bool = True) -> torch.Tensor: + """Whiten values.""" + mean, var = torch.mean(values), torch.var(values) + whitened = (values - mean) * torch.rsqrt(var + 1e-8) + if not shift_mean: + whitened += mean + return whitened + + +def masked_mean(values: torch.Tensor, mask: torch.Tensor, axis: bool = None) -> torch.Tensor: + """Compute mean of tensor with a masked values.""" + if axis is not None: + return (values * mask).sum(axis=axis) / mask.sum(axis=axis) + else: + return (values * mask).sum() / mask.sum() + + +def masked_var(values: torch.Tensor, mask: torch.Tensor, unbiased: bool = True) -> torch.Tensor: + """Compute variance of tensor with masked values.""" + mean = masked_mean(values, mask) + centered_values = values - mean + variance = masked_mean(centered_values**2, mask) + if unbiased: + mask_sum = mask.sum() + if mask_sum == 0: + raise ValueError("The sum of the mask is zero, which can happen when `mini_batch_size=1`;" "try increase the `mini_batch_size` or `gradient_accumulation_steps`") + # note that if mask_sum == 1, then there is a division by zero issue + # to avoid it you just need to use a larger minibatch_size + bessel_correction = mask_sum / (mask_sum - 1) + variance = variance * bessel_correction + return variance + + +def masked_whiten(values: torch.Tensor, mask: torch.Tensor, shift_mean: bool = True) -> torch.Tensor: + """Whiten values with masked values.""" + mean, var = masked_mean(values, mask), masked_var(values, mask) + whitened = (values - mean) * torch.rsqrt(var + 1e-8) + if not shift_mean: + whitened += mean + return whitened + + +def clip_by_value(x: torch.Tensor, tensor_min: float, tensor_max: float) -> torch.Tensor: + """ + Tensor extension to torch.clamp + https://github.com/pytorch/pytorch/issues/2793#issuecomment-428784713 + """ + clipped = torch.max(torch.min(x, tensor_max), tensor_min) + return clipped + + +def entropy_from_logits(logits: torch.Tensor) -> torch.Tensor: + """Calculate entropy from logits.""" + pd = torch.nn.functional.softmax(logits, dim=-1) + entropy = torch.logsumexp(logits, axis=-1) - torch.sum(pd * logits, axis=-1) + return entropy + + +def average_torch_dicts(list_of_dicts: List[Dict]) -> Dict: + """Average values of a list of dicts with torch tensors.""" + average_dict = dict() + for key in list_of_dicts[0].keys(): + average_dict[key] = torch.mean(torch.stack([d[key] for d in list_of_dicts]), axis=0) + return average_dict + + +def stats_to_np(stats_dict: Dict) -> Dict: + """Cast all torch.tensors in dict to numpy arrays.""" + new_dict = dict() + for k, v in stats_dict.items(): + if isinstance(v, torch.Tensor): + new_dict[k] = v.detach().cpu() + if new_dict[k].dtype == torch.bfloat16: + new_dict[k] = new_dict[k].float() + new_dict[k] = new_dict[k].numpy() + else: + new_dict[k] = v + if np.isscalar(new_dict[k]): + new_dict[k] = float(new_dict[k]) + return new_dict + + +def respond_to_batch(model: nn.Module, queries: List[torch.LongTensor], txt_len: int = 20, top_k: int = 0, top_p: float = 1.0) -> torch.LongTensor: + """Sample text from language model.""" + input_ids = queries + for i in range(txt_len): + # Get Logits + outputs = model(input_ids) + next_token_logits = outputs[0][:, -1, :] + next_token_logits = top_k_top_p_filtering(next_token_logits, top_k=top_k, top_p=top_p) + # Sample + probs = F.softmax(next_token_logits, dim=-1) + next_token = torch.multinomial(probs, num_samples=1).squeeze(1) + input_ids = torch.cat([input_ids, next_token.unsqueeze(-1)], dim=-1) + return input_ids[:, -txt_len:] + + +def set_seed(seed: int) -> None: + """ + Helper function for reproducible behavior to set the seed in `random`, `numpy`, and `torch`. + + Args: + seed (`int`): The seed to set. + """ + random.seed(seed) + np.random.seed(seed) + torch.manual_seed(seed) + if is_xpu_available(): + torch.xpu.manual_seed_all(seed) + elif is_npu_available(): + torch.npu.manual_seed_all(seed) + else: + torch.cuda.manual_seed_all(seed) + + +class LengthSampler: + """ + Samples a length + """ + + def __init__(self, min_value: int, max_value: int): + self.values = list(range(min_value, max_value)) + + def __call__(self) -> int: + return np.random.choice(self.values) + + +class PPODecorators(object): + optimize_device_cache = False + + @classmethod + @contextmanager + def empty_device_cache(cls): + yield + if cls.optimize_device_cache: + if is_xpu_available(): + gc.collect() + torch.xpu.empty_cache() + gc.collect() + elif is_npu_available(): + gc.collect() + torch.npu.empty_cache() + gc.collect() + elif torch.cuda.is_available(): + gc.collect() + torch.cuda.empty_cache() + gc.collect() + + +def randn_tensor( + shape: Union[Tuple, List], + generator: Optional[Union[List[torch.Generator], torch.Generator]] = None, + device: Optional[torch.device] = None, + dtype: Optional[torch.dtype] = None, + layout: Optional[torch.layout] = None, +) -> torch.Tensor: + """A helper function to create random tensors on the desired `device` with the desired `dtype`. When + passing a list of generators, you can seed each batch size individually. If CPU generators are passed, the tensor + is always created on the CPU. + """ + # device on which tensor is created defaults to device + rand_device = device + batch_size = shape[0] + + layout = layout or torch.strided + device = device or torch.device("cpu") + + if generator is not None: + gen_device_type = generator.device.type if not isinstance(generator, list) else generator[0].device.type + if gen_device_type != device.type and gen_device_type == "cpu": + rand_device = "cpu" + if device != "mps": + warnings.warn( + f"The passed generator was created on 'cpu' even though a tensor on {device} was expected." + f" Tensors will be created on 'cpu' and then moved to {device}. Note that one can probably" + f" slighly speed up this function by passing a generator that was created on the {device} device." + ) + elif gen_device_type != device.type and gen_device_type == "cuda": + raise ValueError(f"Cannot generate a {device} tensor from a generator of type {gen_device_type}.") + + # make sure generator list of length 1 is treated like a non-list + if isinstance(generator, list) and len(generator) == 1: + generator = generator[0] + + if isinstance(generator, list): + shape = (1,) + shape[1:] + latents = [torch.randn(shape, generator=generator[i], device=rand_device, dtype=dtype, layout=layout) for i in range(batch_size)] + latents = torch.cat(latents, dim=0).to(device) + else: + latents = torch.randn(shape, generator=generator, device=rand_device, dtype=dtype, layout=layout).to(device) + + return latents diff --git a/trl/environment/__init__.py b/trl/environment/__init__.py new file mode 100644 index 0000000..ae1cda4 --- /dev/null +++ b/trl/environment/__init__.py @@ -0,0 +1,3 @@ +# flake8: noqa + +from .base_environment import TextEnvironment, TextHistory diff --git a/trl/environment/__pycache__/__init__.cpython-310.pyc b/trl/environment/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8203890fbdefab7cb604416a8d77bd26bbec7b8f GIT binary patch literal 234 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 literal 0 HcmV?d00001 diff --git a/trl/environment/base_environment.py b/trl/environment/base_environment.py new file mode 100644 index 0000000..4c06b1c --- /dev/null +++ b/trl/environment/base_environment.py @@ -0,0 +1,463 @@ +# Copyright 2022 The HuggingFace Team. 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. + +import re +import warnings + +import torch +from accelerate.utils import extract_model_from_parallel +from transformers import StoppingCriteria, StoppingCriteriaList + +from ..import_utils import is_rich_available + + +if is_rich_available(): + from rich import print + from rich.text import Text + + +class StringStoppingCriteria(StoppingCriteria): + """Custom `StoppingCriteria` which checks if all generations in the batch are completed.""" + + def __init__(self, stop_strings, tokenizer): + self.stop_strings = stop_strings + self.tokenizer = tokenizer + self.first_call = True + + def __call__(self, input_ids, scores, **kwargs): + """Returns true if all generated sequences contain any of the stop strings.""" + if self.first_call: + self.generated_tokens = [1 for _ in range(input_ids.shape[0])] + self.start_length = input_ids.shape[-1] - 1 + self.first_call = False + decoded_generations = self.tokenizer.batch_decode(input_ids[:, self.start_length :]) + done = [] + + for i, decoded_generation in enumerate(decoded_generations): + sequence_complete = any([stop_string in decoded_generation for stop_string in self.stop_strings]) + done.append(sequence_complete) + if not sequence_complete: + self.generated_tokens[i] += 1 + + if all(done): + self.first_call = True + + return all(done) + + +class TextHistory: + """The TextHistory class keeps track of the history of an interaction between the language model and the environment.""" + + def __init__(self, text, tokens, system=True): + """ + Initialize TextHistory. + + args: + text (`str`): The text of the first segment. + tokens (`torch.LongTensor`): The tokens of the first segment. + system (`bool`, *optional*): Whether the first segment is a system or user segment. + """ + self.system_spans = [] + self.text_spans = [] + self.token_spans = [] + self.token_masks = torch.tensor([], dtype=torch.long).to(tokens.device) + self.text = "" + self.tokens = torch.tensor([], dtype=torch.long).to(tokens.device) + self.completed = False + self.truncated = False + self.reward = 0.0 + + self.prompt_color = "black on grey85" + self.system_color = "black on cyan3" + self.model_color = "black on deep_sky_blue1" + self.reward_color = "black on plum1" + + self.append_segment(text, tokens, system=system) + + def append_segment(self, text, tokens, system=True): + """ + Append a new segment to the history. + + args: + text (`str`): The text of the new segment. + tokens (`torch.LongTensor`): The tokens of the new segment. + system (`bool`, *optional*): Whether the new segment is a system or user segment. + """ + + if len(text) == 0 or len(tokens) == 0: + raise ValueError("Can't append empty text or token list to history.") + + original_text_length = len(self.text) + + self.text += text + self.text_spans.append((original_text_length, len(self.text))) + self.system_spans.append(system) + + original_token_length = len(self.tokens) + + self.tokens = torch.cat((self.tokens, tokens)) + if system: + self.token_masks = torch.cat((self.token_masks, torch.zeros_like(tokens))) + else: + self.token_masks = torch.cat((self.token_masks, torch.ones_like(tokens))) + self.token_spans.append((original_token_length, len(self.tokens))) + + def complete(self, truncated=False): + """ + Mark the history as completed. + """ + self.completed = True + self.truncated = truncated + + @property + def last_text_segment(self): + """ + Get the last text segment. + """ + start, end = self.text_spans[-1] + return self.text[start:end] + + def split_query_response_tokens(self): + """ + Split the tokens into query and response tokens. + """ + split_index = self.token_spans[0][1] + query = self.tokens[:split_index] + response = self.tokens[split_index:] + mask = self.token_masks[split_index:] + + return query, response, mask + + def show_text(self, show_legend=False): + """ + Print the text history. + """ + if not is_rich_available(): + warnings.warn("install rich to display text") + return + + text = Text(self.text) + text.stylize(self.prompt_color, self.text_spans[0][0], self.text_spans[1][0]) + for i, (start, end) in enumerate(self.text_spans[1:]): + if self.system_spans[i + 1]: + text.stylize(self.system_color, start, end) + else: + text.stylize(self.model_color, start, end) + + text.append(f"\n\nReward: {self.reward}", style=self.reward_color) + print(text) + + if show_legend: + self.show_colour_legend() + + def show_tokens(self, tokenizer, show_legend=False): + """ + Print the history tokens. + """ + if not is_rich_available(): + warnings.warn("install rich to display tokens") + return + + text = Text() + prompt_end = self.token_spans[0][1] + for i, (token, mask) in enumerate(zip(self.tokens, self.token_masks)): + if i < prompt_end: + text.append(tokenizer.convert_ids_to_tokens(token.item()), style=self.prompt_color) + text.append(" ") + elif mask == 0: + text.append(tokenizer.convert_ids_to_tokens(token.item()), style=self.system_color) + text.append(" ") + else: + text.append(tokenizer.convert_ids_to_tokens(token.item()), style=self.model_color) + text.append(" ") + text.append(f"\n\nReward: {self.reward}", style=self.reward_color) + print(text) + if show_legend: + self.show_colour_legend() + + def show_colour_legend(self): + """ + Print the colour legend. + """ + if not is_rich_available(): + warnings.warn("install rich to display colour legend") + return + text = Text("\n\n(Colour Legend: ") + text.append("Prompt", style=self.prompt_color) + text.append("|") + text.append("System", style=self.system_color) + text.append("|") + text.append("Model", style=self.model_color) + text.append("|") + text.append("Reward", style=self.reward_color) + text.append(")") + print(text) + + +class TextEnvironment: + """ + The TextEnvironment enables interaction of a LLM with an environment using tools. + """ + + def __init__( + self, + model=None, + tokenizer=None, + tools=None, + reward_fn=None, + prompt=None, + max_turns=4, + max_tool_reponse=100, + max_length=None, + generation_kwargs=None, + ): + """ + Initialize TextEnvironment. + + Args: + model (`PreTrainedModelWrapper`): The model to use for generation. + tokenizer (`transformers.PreTrainedTokenizer`): The tokenizer to use for generation. + tools (list): A list of tools to use for interaction. + reward_fn (function): A function that takes a string and returns a reward. + prompt (str): The base prompt to use for generation. Is prepended to the tasks. + max_turns (Optional[int]): The maximum number of turns to allow. + max_tool_response (Optional[int]): The maximum number of characters to allow in a tool response. + max_length (Optional[int]): The maximum number of tokens to allow in an episode. + generation_kwargs (Optional[dict]): A dictionary of keyword arguments to pass to the model's generate method. + """ + self.model = model + self.tokenizer = tokenizer + self.prompt = prompt + if isinstance(tools, dict): + self.tools = tools + else: + self.tools = dict([(tool.__class__.__name__, tool) for tool in tools]) + self.reward_fn = reward_fn + self.max_length = max_length + self.request_token = "" + self.call_token = "" + self.response_token = "" + self.submit_token = "" + self.max_turns = max_turns + self.max_tool_response = max_tool_reponse + + if generation_kwargs is None: + self.generation_kwargs = dict() + else: + self.generation_kwargs = generation_kwargs + + self.is_encoder_decoder = hasattr(self.model, "is_encoder_decoder") + self.current_device = extract_model_from_parallel(self.model).pretrained_model.device + + def run(self, queries, **rewards_kwargs): + """ + Run the environment on a list of queries. + + Args: + queries (list[str]): A list of queries to run the model in the environment on. + """ + turns = 0 + + queries = [self.prompt + task for task in queries] + queries_tokens = [self.tokenizer(query, return_tensors="pt").input_ids[0].to(self.model.pretrained_model.device) for query in queries] + + histories = [TextHistory(q, qt, system=True) for q, qt in zip(queries, queries_tokens)] + + while any([not history.completed for history in histories]) and turns < self.max_turns: + histories = self.generate(histories) + histories = self.tasks_end_check(histories) + # TODO: make this parallel rather than for-loop + for i in range(len(histories)): + histories[i] = self.step(histories[i]) + histories = self.tasks_end_check(histories, model_turn=False) + turns += 1 + self.compute_reward(histories, **rewards_kwargs) + + # convert a list of (q, r, m) tuples to lists of all qs, rs, and ms respectively + queries, responses, masks = map(list, zip(*[history.split_query_response_tokens() for history in histories])) + + rewards = [history.reward for history in histories] + return queries, responses, masks, rewards, histories + + def step(self, history): + """ + Step the environment forward one turn. + + Args: + history (`TextHistory`): The history to step forward. + """ + truncated, ended = self.task_end_check(history) + if ended: + history.complete(truncated=truncated) + if history.completed: + return history + + tool, query = self.parse_tool_call(history.last_text_segment) + if tool is None or query is None: + response = f"Unknown tool call: {history.last_text_segment}" + else: + if tool not in self.tools: + response = f"Unknown tool {tool}." + try: + response = self.tools[tool](query) + except Exception as error: + response = f"Tool error: {str(error)}" + + if len(response) > self.max_tool_response: + response = response[: (self.max_tool_response - 3)] + "..." + + history.append_segment( + response + self.response_token, + self.tokenizer(response + self.response_token, return_tensors="pt").input_ids[0].to(self.model.pretrained_model.device), + system=True, + ) + + return history + + def parse_tool_call(self, text): + """ + Parse request string. Expected format: query + """ + result = re.search(f"(?<={self.request_token}).*?(?={self.call_token})", text, re.DOTALL) + + # if we can't find a / span we return none + if result is None: + return None, None + else: + extracted_text = result.group() + + result = re.search(r"<(.*?)>", extracted_text) + + # if we can't find a tool name we return none + if result is None: + return None, None + else: + tool = result.group(1) + + # split off the tool name + query = ">".join(extracted_text.split(">")[1:]) + + return tool, query + + def compute_reward(self, histories, **reward_kwargs): + """ + Compute the reward for a list of histories. + """ + rewards = self.reward_fn([history.last_text_segment for history in histories], **reward_kwargs) + for history, reward in zip(histories, rewards): + history.reward = reward + return histories + + def generate(self, histories): + """ + Generate responses for a list of histories. + """ + active_histories = [i for i, history in enumerate(histories) if not history.completed] + + query_tensors = [histories[i].tokens for i in active_histories] + response_tensors = self._generate_batched(query_tensors) + response_texts = self.tokenizer.batch_decode(response_tensors) + + for i, response_text, response_tensor in zip(active_histories, response_texts, response_tensors): + histories[i].append_segment(response_text, response_tensor, system=False) + + return histories + + def tasks_end_check(self, histories, model_turn=True): + """ + Check if the current generation sequences have finished. + """ + for history in histories: + if not history.completed: + truncated, ended = self.task_end_check(history, model_turn=model_turn) + if ended: + history.complete(truncated=truncated) + return histories + + def task_end_check(self, history, model_turn=True): + """ + Check if the current generation sequence has finished. + """ + truncated = False + ended = False + if history.completed: + return truncated, ended + if self.max_length is not None and len(self.tokenizer(history.text).input_ids[0]) > self.max_length: + truncated = True + ended = True + elif self.tokenizer.eos_token in history.text: + ended = True + elif model_turn and not ((self.request_token in history.last_text_segment and self.call_token in history.last_text_segment) or self.submit_token in history.last_text_segment): + ended = True + elif self.submit_token in history.last_text_segment: + ended = True + return truncated, ended + + def _generate_batched( + self, + query_tensors, + batch_size: int = 16, + pad_to_multiple_of: int = None, + ): + """ + Generate responses for a list of query tensors. + + args: + query_tensors (list[torch.Tensor]): A list of query tensors to generate responses for. + batch_size (int): The batch size to use for generation. + pad_to_multiple_of (int): The padding length to use for generation. + """ + outputs = [] + padding_side_default = self.tokenizer.padding_side + if not self.is_encoder_decoder: + self.tokenizer.padding_side = "left" + + # in case we have fewer examples than bs + batch_size = min(len(query_tensors), batch_size) + + for i in range(0, len(query_tensors), batch_size): + # prevent overflow if query tensors are not even multiple of bs + end_index = min(len(query_tensors), i + batch_size) + + batch = query_tensors[i:end_index] + batch_mask = [torch.ones_like(element) for element in batch] + inputs = {"input_ids": batch, "attention_mask": batch_mask} + + padded_inputs = self.tokenizer.pad( + inputs, + padding=True, + max_length=None, + pad_to_multiple_of=pad_to_multiple_of, + return_tensors="pt", + ).to(self.current_device) + + stopping_criteria = StringStoppingCriteria([self.call_token, self.submit_token], self.tokenizer) + + self.generation_kwargs["stopping_criteria"] = StoppingCriteriaList([stopping_criteria]) + + generations = extract_model_from_parallel(self.model).generate(**padded_inputs, **self.generation_kwargs) + + for generation, mask, generated_tokens in zip(generations, padded_inputs["attention_mask"], stopping_criteria.generated_tokens): + if not self.is_encoder_decoder: + output = generation[(1 - mask).sum() :] # remove padding + else: + output = generation + + if not self.is_encoder_decoder: + output = output[(mask).sum() :] # remove prompt + + # remove chunk generated after stopping criteria in batch mode + outputs.append(output[:generated_tokens]) + self.tokenizer.padding_side = padding_side_default + return outputs diff --git a/trl/extras/__init__.py b/trl/extras/__init__.py new file mode 100644 index 0000000..6b3035d --- /dev/null +++ b/trl/extras/__init__.py @@ -0,0 +1,16 @@ +# flake8: noqa + +# Copyright 2022 The HuggingFace Team. 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. +from .best_of_n_sampler import BestOfNSampler diff --git a/trl/extras/__pycache__/__init__.cpython-310.pyc b/trl/extras/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..33bdc41070142f8afd17d73672926c8971b13452 GIT binary patch literal 207 zcmd1j<>g`k0w%Y6>2sJE7#@Q-$N(tD-~hzM0ze{#A%!uAA(t_Vk&z*VDVRZ%`6VMz zSd;MO~UKP^5lz8E4}1Tt(TLlHBO0u#Ti^o#QI zOZ1cTQ&RQw3sUpSauRiOauUlD^?iI2!yI+}QY%XIONw&zfpk$~v3`7fW?p7Ve7s&k XAt+)zw|~ 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 bwWanOxtA|fmP)7!J!{x}ja9L+YlZ&+JC;-{ literal 0 HcmV?d00001 diff --git a/trl/extras/__pycache__/dataset_formatting.cpython-310.pyc b/trl/extras/__pycache__/dataset_formatting.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f0851e624e13bfaad99edfea60bd5cef8e625f88 GIT binary patch literal 2982 zcmc&$Pj3@P6yKR$uQ$dvP6C0@f|j&U+>)e!RFy^uHA)mzDHz1RgDYwl&)6)iciowB z3Tbi*5?tt^XAbmIDe4#KXXrz4^_1lhkSz z3m&@hNB=_Ivi`(L@hQXPEBMRbfiR1a$ZF%8Mr1(SbYQpbfzx&drFMxR-;Uftxm_N3 zZO_b|XlhVtS72OXZggl+ZC8oKJyvFfdC%!&#HL=*KAFrXBUX7q*`XJ-U1L>VVKr{E z`n=V1*kRUq;FRL&Zf9Y47S#g-Hyf(ybTNFol5N;eq{ zBaYs+I}|Wcz;(O#12u^Bp%83~QP+H)Qn_zkA8;vyK9`+d$RjlD0a25e z&NK3$l=62FMU>gk>Cc2%-_7m-b|zobFOVgtCD8rGKG9yj`h8bdLWw&T#9gkPD3pL6 z5yXA2?TEwvLS#H#XLya%M+ zNHGBAp(D)!*e5A!5J3he0-=LjFVRfh0JK3H#CbjEIHYP66E=f-syYHnK2;2e!l0Y+m`T6LXiaUN#6oEM|oc z)&sg_iIey?yKOV5N)M?xshpQ3W@BYi?he3w4=Oh9u95%3$U9K)a{15SkavuH`3CrU z0F@9i0ah;?Dc4vn??7Dyn;_Hlg5RLVnX|JI#!Q+D+)aewU6q$~$ibv99H{A8^nRMR zk>E@I+6Z)G1;t&ChQmY@8Ul0)!B>p0MgQ&^RK_ctcm3}}b>IKyhXw4F3oB^qyuScw z!48_vckkpng&mqGrVX{An)lkVwozXdeVIDP&X=$J+(W8u2#uLZaHtz^aD7~^Y_+pt$`fSENvl!a9CNOHfG#N5~y zSkMJF`Ry|WgL!Z!wdpM*GbWCq0yfa?5C2V}kmO9O)%3(WsOSRGUXKTGZGaY5-?)1H z%IeL|mDOw4zHVLCg8%6vQULGXw ZkY^ny4O%5tdcrwQ+}Cq%edg?me*ihcE$RRO literal 0 HcmV?d00001 diff --git a/trl/extras/best_of_n_sampler.py b/trl/extras/best_of_n_sampler.py new file mode 100644 index 0000000..b64231f --- /dev/null +++ b/trl/extras/best_of_n_sampler.py @@ -0,0 +1,113 @@ +from typing import Any, Callable, List, Optional, Union + +import torch +from transformers import GenerationConfig, PreTrainedTokenizer, PreTrainedTokenizerFast + +from ..core import set_seed +from ..models import SUPPORTED_ARCHITECTURES, PreTrainedModelWrapper + + +class BestOfNSampler(object): + def __init__( + self, + model: PreTrainedModelWrapper, + tokenizer: Union[PreTrainedTokenizer, PreTrainedTokenizerFast], + queries_to_scores: Callable[[List[str]], List[float]], + length_sampler: Any, + sample_size: int = 4, + seed: Optional[int] = None, + n_candidates: int = 1, + generation_config: Optional[GenerationConfig] = None, + ) -> None: + r""" + Initialize the sampler for best-of-n generation + + Args: + model (`PreTrainedModelWrapper`): + The pretrained model to use for generation + tokenizer (`PreTrainedTokenizer` or `PreTrainedTokenizerFast`): + Tokenizer associated with the pretrained model + queries_to_scores (`Callable[[List[str]], List[float]]`): + Callable that takes a list of generated texts and returns the associated reward scores + length_sampler (`Any`): + Sampler used to sample the length of the generated text + sample_size (`int`): + Number of samples to generate for each query + seed (`int`, *optional*): + Random seed used to control generation + n_candidates (`int`): + Number of candidates to return for each query + generation_config (`GenerationConfig`, *optional*): + Generation config passed to the underlying model's `generate` method. + See `GenerationConfig` (https://huggingface.co/docs/transformers/v4.29.1/en/main_classes/text_generation#transformers.GenerationConfig) for more details + """ + if seed is not None: + set_seed(seed) + + if not isinstance(tokenizer, (PreTrainedTokenizer, PreTrainedTokenizerFast)): + raise ValueError(f"tokenizer must be a PreTrainedTokenizer or PreTrainedTokenizerFast, got {type(tokenizer)}") + if not isinstance(model, (SUPPORTED_ARCHITECTURES)): + raise ValueError(f"model must be a PreTrainedModelWrapper, got {type(model)} - supported architectures are: {SUPPORTED_ARCHITECTURES}") + + self.model = model + self.tokenizer = tokenizer + + self.queries_to_scores = queries_to_scores + self.length_sampler = length_sampler + self.gen_config = generation_config + self.sample_size = sample_size + self.n_candidates = n_candidates + + def generate( + self, + tokenized_query: Union[List[int], torch.Tensor, List[torch.Tensor], List[List[int]]], + skip_special_tokens: bool = True, + device: Optional[Union[str, torch.device]] = None, + **generation_kwargs, + ) -> List[List[str]]: + r""" + Generate the best of n samples for input queries + + Args: + tokenized_query (`List[int]` or `torch.Tensor` or `List[torch.Tensor]` or `List[int]`): + represents either a single tokenized query (a single tensor or a list of integers) or a batch of tokenized queries (a list of tensors or a list of lists of integers) + skip_special_tokens (`bool`): + Whether to remove the special tokens from the output + device (`str` or `torch.device`, *optional*): + The device on which the model will be loaded + **generation_kwargs (`dict`, *optional*): + Additional keyword arguments passed along to the underlying model's `generate` method. + This is used to override generation config + + Returns: + List[List[str]]: A list of lists of generated texts + """ + queries = None + + if isinstance(tokenized_query, torch.Tensor) and tokenized_query.ndim == 1: + queries = tokenized_query.unsqueeze(0) + elif isinstance(tokenized_query, List): + element_type = type(tokenized_query[0]) + if element_type == int: + queries = torch.tensor(tokenized_query).unsqueeze(0) + elif element_type == torch.Tensor: + queries = [tensor.reshape((1, -1)) for tensor in tokenized_query] + else: + queries = [torch.tensor(query).reshape((1, -1)) for query in tokenized_query] + + result = [] + + for query in queries: + queries = query.repeat((self.sample_size, 1)) + output = self.model.generate( + queries.to(device), + max_new_tokens=self.length_sampler(), + generation_config=self.gen_config, + **generation_kwargs, + ).squeeze() + output = self.tokenizer.batch_decode(output, skip_special_tokens=skip_special_tokens) + scores = torch.tensor(self.queries_to_scores(output)) + output = [output[i] for i in scores.topk(self.n_candidates).indices] + result.append(output) + + return result diff --git a/trl/extras/dataset_formatting.py b/trl/extras/dataset_formatting.py new file mode 100644 index 0000000..b9a691b --- /dev/null +++ b/trl/extras/dataset_formatting.py @@ -0,0 +1,86 @@ +import logging +from typing import Callable, Literal, Optional, Union + +from datasets import Dataset, Value +from transformers import AutoTokenizer + +from ..trainer.utils import ConstantLengthDataset + + +FORMAT_MAPPING = { + "chatml": [{"content": Value(dtype="string", id=None), "role": Value(dtype="string", id=None)}], + "instruction": {"completion": Value(dtype="string", id=None), "prompt": Value(dtype="string", id=None)}, +} + + +def conversations_formatting_function(tokenizer: AutoTokenizer, messages_field: Literal["messages", "conversations"]): + r""" + return a callable function that takes in a "messages" dataset and returns a formatted dataset, based on the tokenizer + apply chat template to the dataset + """ + + def format_dataset(examples): + if isinstance(examples[messages_field][0], list): + output_texts = [] + for i in range(len(examples[messages_field])): + output_texts.append(tokenizer.apply_chat_template(examples[messages_field][i], tokenize=False)) + return output_texts + else: + return tokenizer.apply_chat_template(examples[messages_field], tokenize=False) + + return format_dataset + + +def instructions_formatting_function(tokenizer: AutoTokenizer): + r""" + return a callable function that takes in an "instructions" dataset and returns a formatted dataset, based on the tokenizer + apply chat template to the dataset + """ + + def format_dataset(examples): + if isinstance(examples["prompt"], list): + output_texts = [] + for i in range(len(examples["prompt"])): + converted_sample = [ + {"role": "user", "content": examples["prompt"][i]}, + {"role": "assistant", "content": examples["completion"][i]}, + ] + output_texts.append(tokenizer.apply_chat_template(converted_sample, tokenize=False)) + return output_texts + else: + converted_sample = [ + {"role": "user", "content": examples["prompt"]}, + {"role": "assistant", "content": examples["completion"]}, + ] + return tokenizer.apply_chat_template(converted_sample, tokenize=False) + + return format_dataset + + +def get_formatting_func_from_dataset(dataset: Union[Dataset, ConstantLengthDataset], tokenizer: AutoTokenizer) -> Optional[Callable]: + r""" + Finds the correct formatting function based on the dataset structure. Currently supported datasets are: + - `ChatML` with [{"role": str, "content": str}] + - `instruction` with [{"prompt": str, "completion": str}] + + Args: + dataset (Dataset): User dataset + tokenizer (AutoTokenizer): Tokenizer used for formatting + + Returns: + Callable: Formatting function if the dataset format is supported else None + """ + if isinstance(dataset, Dataset): + if "messages" in dataset.features: + if dataset.features["messages"] == FORMAT_MAPPING["chatml"]: + logging.info("Formatting dataset with chatml format") + return conversations_formatting_function(tokenizer, "messages") + if "conversations" in dataset.features: + if dataset.features["conversations"] == FORMAT_MAPPING["chatml"]: + logging.info("Formatting dataset with chatml format") + return conversations_formatting_function(tokenizer, "conversations") + elif dataset.features == FORMAT_MAPPING["instruction"]: + logging.info("Formatting dataset with instruction format") + return instructions_formatting_function(tokenizer) + + return None diff --git a/trl/import_utils.py b/trl/import_utils.py new file mode 100644 index 0000000..88a04f7 --- /dev/null +++ b/trl/import_utils.py @@ -0,0 +1,108 @@ +# Copyright 2022 The HuggingFace Team. 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. +import importlib +import sys + + +if sys.version_info < (3, 8): + _is_python_greater_3_8 = False +else: + _is_python_greater_3_8 = True + + +def is_peft_available() -> bool: + return importlib.util.find_spec("peft") is not None + + +def is_unsloth_available() -> bool: + return importlib.util.find_spec("unsloth") is not None + + +def is_accelerate_greater_20_0() -> bool: + if _is_python_greater_3_8: + from importlib.metadata import version + + accelerate_version = version("accelerate") + else: + import pkg_resources + + accelerate_version = pkg_resources.get_distribution("accelerate").version + return accelerate_version >= "0.20.0" + + +def is_transformers_greater_than(version: str) -> bool: + _transformers_version = importlib.metadata.version("transformers") + return _transformers_version > version + + +def is_torch_greater_2_0() -> bool: + if _is_python_greater_3_8: + from importlib.metadata import version + + torch_version = version("torch") + else: + import pkg_resources + + torch_version = pkg_resources.get_distribution("torch").version + return torch_version >= "2.0" + + +def is_diffusers_available() -> bool: + return importlib.util.find_spec("diffusers") is not None + + +def is_bitsandbytes_available() -> bool: + import torch + + # bnb can be imported without GPU but is not usable. + return importlib.util.find_spec("bitsandbytes") is not None and torch.cuda.is_available() + + +def is_torchvision_available() -> bool: + return importlib.util.find_spec("torchvision") is not None + + +def is_rich_available() -> bool: + return importlib.util.find_spec("rich") is not None + + +def is_wandb_available() -> bool: + return importlib.util.find_spec("wandb") is not None + + +def is_xpu_available() -> bool: + if is_accelerate_greater_20_0(): + import accelerate + + return accelerate.utils.is_xpu_available() + else: + if importlib.util.find_spec("intel_extension_for_pytorch") is None: + return False + try: + import torch + + return hasattr(torch, "xpu") and torch.xpu.is_available() + except RuntimeError: + return False + + +def is_npu_available() -> bool: + """Checks if `torch_npu` is installed and potentially if a NPU is in the environment""" + if importlib.util.find_spec("torch") is None or importlib.util.find_spec("torch_npu") is None: + return False + + import torch + import torch_npu # noqa: F401 + + return hasattr(torch, "npu") and torch.npu.is_available() diff --git a/trl/models/__init__.py b/trl/models/__init__.py new file mode 100644 index 0000000..ec20345 --- /dev/null +++ b/trl/models/__init__.py @@ -0,0 +1,35 @@ +# flake8: noqa + +# Copyright 2022 The HuggingFace Team. 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. +from .modeling_base import PreTrainedModelWrapper, create_reference_model +from .modeling_value_head import AutoModelForCausalLMWithValueHead, AutoModelForSeq2SeqLMWithValueHead +from .utils import setup_chat_format + + +SUPPORTED_ARCHITECTURES = ( + AutoModelForCausalLMWithValueHead, + AutoModelForSeq2SeqLMWithValueHead, +) + +from ..import_utils import is_diffusers_available + + +if is_diffusers_available(): + from .modeling_sd_base import ( + DDPOPipelineOutput, + DDPOSchedulerOutput, + DDPOStableDiffusionPipeline, + DefaultDDPOStableDiffusionPipeline, + ) diff --git a/trl/models/__pycache__/__init__.cpython-310.pyc b/trl/models/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6cb98b5e0e4114b0e712ada2042a3c0af1df47f5 GIT binary patch literal 702 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 bWhNiT9F?dRX3O+{LB?XHa#s7+(zE{!Ma*`u literal 0 HcmV?d00001 diff --git a/trl/models/__pycache__/utils.cpython-310.pyc b/trl/models/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3e7dca9f24bc0877fad940801856bc5805f8d020 GIT binary patch literal 3043 zcmbVOL2nc{6drqKcV}m3lR$tH)Pg~xVMR#PUXW;nh@hZKWE(-sVG$Z8Yi}5so!Q!6 z+ANzM8q`~VL4^cI?mhR1_|QvF?Uh?gOW*VCZW0<)wY#$C*?xZS`T6r_=ki-4 zdGoi>ael$Z-d`OWSD>ixpyCcEsnaDbLQtkD?Yb?u>$SXYsa3LlZd&eET9vNf@`)qJ z4TpQY^wi;{9lAqWRbCd=gtUTcxmw}V+!qyI6+RDyCtO~hbLJdg1Gxs@P?SVP_#Hau zbOK)IQ%_5+kXLRyjhSB|7NmB%`6r?FBE*HmsMTF5vx{^vcN zq*#}6k_o<=bCH_a-H!GAk;syPkZrWO_vb?63KZ3WN;oaTofiE9a?o-)ZFvx1nY+ud z!dAt~B`f<@E-yQx$}8M|>cF}KkgGhfat-7f53L-6j4MN(I-kb1nc1rkV!fK)?um91 zr#3n@NUn`n$~w7Zsu*M%V^^%N1ZPRc?97`BAhy#!PqIy>wv-m#1=NrW3=)+n9cOxh zZRBc!39RCbvtGsp}OE+)EcUCSm#n*aC%XF!W`>B@tI!V=HZ%ZOzXP~@7=OG=OI3S&ih3j|5 zD=}V-(ZjsSDQL-QtT42qI*ig0sIbUcs0POm@`MKd-A{G0Gw*hG6nq??IQ!rrXA>*` z@K}L~xD)CmRGf_Vg2I=;p1AO(@b#YI@hUY3?8#Y+v#wAo-V}(fk*L_&?I%y>2OrO~ z^W(m|^D$jZ~tD=j|x6(HU21X{Mtn&azzF zbtxFKG1njtcpl8{gu5bBQ06p^T7 z%D9AX+B7A5MoiTnV`Ms0hSYasZH`KzpaYI?)a|D_0k(|tjwwr_`!Z{LU}pR)fl!8G zQS>7SL#my}M9ydectZ3*`#F69XZ)GBL%t;%H0GQgXWJWk58WY&T=;tM#dZn4P@V&U z+7@VR(uBN#iv`*h7_9&%eO(=r9Wr#e*C+Cmp_>uBPQIta(ZSH+W$x`1V|Rw+VGU-4 zQ4OTJmFgf(SqbVP=^*b^;-7#Q0tUa^u*M$493j*p8*F0>VQ6V{Kfmgn=?9`6od-w1OOjvi0+0c45#sykSy=miFRAqMvUy8XSOR7U%f@{i-C=H zMQ*A~R${D}ckcc-TE>~e;DuC+yEob@QsVu_CAJRH70R%J4J#*|MDWFtp$Q<^yN#`n zug9^$-AM-<9VPSJ{qZ$?cXTn|JyQVk%)NVKTO-}V5ae``T@#%ckfX2C7kC|3W4+DXBlFi1nnY#Y@t<{zFXmw?6?X%_$6Woc@zW7wiTpI6@*iyT^R*>IeWc2+G zCGwhiZ?cW>c8MmtqKuo0%v3f7P$lqYIC*$P36~$@%y9DPfG!hG-W||tl;kN;kqoQT zSe=2YRkJK!pllDJBA1#&fVX}x+M{tEx)oj%{_JrQP~Y{5PmU3vo^)q`Ve#)%`4K2I zsKkr3*{GY^#ZJ~e2LFTi4-c4J59Lvv`VB>iMO)lYB`S@F5uUi-xKPD x`;EU0fPhQqGQO-7CM0x@kQqw78RDO!WlFr)l+ZVn(!T!39C$KRwnOWptg literal 0 HcmV?d00001 diff --git a/trl/models/modeling_base.py b/trl/models/modeling_base.py new file mode 100644 index 0000000..9e6c4fc --- /dev/null +++ b/trl/models/modeling_base.py @@ -0,0 +1,640 @@ +# Copyright 2022 The HuggingFace Team. 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. +import json +import logging +import os +from copy import deepcopy + +import torch +import torch.nn as nn +from accelerate import PartialState +from huggingface_hub import hf_hub_download +from huggingface_hub.utils import ( + EntryNotFoundError, + HFValidationError, + LocalEntryNotFoundError, + RepositoryNotFoundError, +) +from safetensors.torch import load_file as safe_load_file +from transformers import PreTrainedModel + +from ..import_utils import is_npu_available, is_peft_available, is_transformers_greater_than, is_xpu_available + + +if is_peft_available(): + from peft import ( + PeftConfig, + PeftModel, + PeftModelForCausalLM, + PeftModelForSeq2SeqLM, + PromptLearningConfig, + get_peft_model, + prepare_model_for_kbit_training, + ) + +if is_transformers_greater_than("4.33.0"): + from transformers.integrations.deepspeed import is_deepspeed_zero3_enabled +else: + from transformers.deepspeed import is_deepspeed_zero3_enabled + +LAYER_PATTERNS = [ + "transformer.h.{layer}", + "model.decoder.layers.{layer}", + "gpt_neox.layers.{layer}", + "model.layers.{layer}", +] + + +class PreTrainedModelWrapper(nn.Module): + r""" + A wrapper class around a (`transformers.PreTrainedModel`) to be compatible with the + (`~transformers.PreTrained`) class in order to keep some attributes and methods of the + (`~transformers.PreTrainedModel`) class. + + Attributes: + pretrained_model: (`transformers.PreTrainedModel`) + The model to be wrapped. + parent_class: (`transformers.PreTrainedModel`) + The parent class of the model to be wrapped. + supported_args: (`list`) + The list of arguments that are supported by the wrapper class. + """ + + transformers_parent_class = None + supported_args = None + supported_modules = ("v_head",) + supported_rm_modules = ("score",) + supported_pretrained_model_architectures = (PreTrainedModel) if not is_peft_available() else (PreTrainedModel, PeftModelForCausalLM, PeftModelForSeq2SeqLM) + + def __init__(self, pretrained_model=None, score_module=None, supports_rm_adapter=False, rm_adapter_name=None, **kwargs): + super().__init__() + self.pretrained_model = pretrained_model + + self.config = pretrained_model.config + self.prepare_inputs_for_generation = pretrained_model.prepare_inputs_for_generation + self.is_loaded_in_8bit = getattr(pretrained_model, "is_loaded_in_8bit", False) + self.is_loaded_in_4bit = getattr(pretrained_model, "is_loaded_in_4bit", False) + self.is_sequential_parallel = False + + if hasattr(pretrained_model, "gradient_checkpointing_disable"): + self.gradient_checkpointing_disable = pretrained_model.gradient_checkpointing_disable + + if hasattr(pretrained_model, "gradient_checkpointing_enable"): + self.gradient_checkpointing_enable = pretrained_model.gradient_checkpointing_enable + + self.supports_rm_adapter = supports_rm_adapter + self.rm_adapter_name = rm_adapter_name + self.policy_adapter_name = "default" + if score_module is not None: + self.score = score_module + + @classmethod + def from_pretrained(cls, pretrained_model_name_or_path, *model_args, **kwargs): + r""" + Instantiates a new model from a pretrained model from `transformers`. The + pretrained model is loaded using the `from_pretrained` method of the + `transformers.PreTrainedModel` class. The arguments that are specific to the + `transformers.PreTrainedModel` class are passed along this method and filtered + out from the `kwargs` argument. + + + Args: + pretrained_model_name_or_path (`str` or `transformers.PreTrainedModel`): + The path to the pretrained model or its name. + *model_args (`list`, *optional*)): + Additional positional arguments passed along to the underlying model's + `from_pretrained` method. + **kwargs (`dict`, *optional*): + Additional keyword arguments passed along to the underlying model's + `from_pretrained` method. We also pre-process the kwargs to extract + the arguments that are specific to the `transformers.PreTrainedModel` + class and the arguments that are specific to trl models. The kwargs + also support `prepare_model_for_kbit_training` arguments from + `peft` library. + """ + if kwargs is not None: + peft_config = kwargs.pop("peft_config", None) + reward_adapter = kwargs.pop("reward_adapter", None) + reward_adapter_name = kwargs.pop("reward_adapter_name", "reward_adapter") + is_trainable = kwargs.pop("is_trainable", False) + trl_model_args, pretrained_kwargs, peft_quantization_kwargs = cls._split_kwargs(kwargs) + token = pretrained_kwargs.get("token", None) + else: + peft_config = None + is_trainable = False + trl_model_args = {} + pretrained_kwargs = {} + peft_quantization_kwargs = {} + token = None + + if reward_adapter is not None and not isinstance(reward_adapter, str): + raise ValueError("The `reward_adapter` argument should be a string representing the name of local path or the Hub id to the Reward Modeling adapter.") + + is_peft_model = False + + current_device = cls._get_current_device() + if isinstance(pretrained_model_name_or_path, str): + is_loaded_in_8bit = pretrained_kwargs["load_in_8bit"] if "load_in_8bit" in pretrained_kwargs else False + is_loaded_in_4bit = pretrained_kwargs["load_in_4bit"] if "load_in_4bit" in pretrained_kwargs else False + else: + is_loaded_in_8bit = getattr(pretrained_model_name_or_path, "is_loaded_in_8bit", False) + is_loaded_in_4bit = getattr(pretrained_model_name_or_path, "is_loaded_in_4bit", False) + + if (is_loaded_in_8bit or is_loaded_in_4bit) and "device_map" not in pretrained_kwargs: + # warn users + logging.warning( + "The `device_map` argument is not provided. We will override the device_map argument." + " to set the entire" + " model on the current device. If you want to set the model on multiple devices, please provide" + " a custom `device_map` argument." + ) + pretrained_kwargs["device_map"] = {"": current_device} + + if is_peft_available() and peft_config is not None and not isinstance(peft_config, PeftConfig): + raise ValueError("The `peft_config` argument should be an instance of `peft.PeftConfig` class.") + + # First, load the pre-trained model using the parent-class + # either `AutoModelForCausalLM` or `AutoModelForSeq2SeqLM` + if isinstance(pretrained_model_name_or_path, str): + if is_peft_available(): + try: + # If there is a trained peft adapter in the hub, load its config. + remote_adapter_config = hf_hub_download( + pretrained_model_name_or_path, + "adapter_config.json", + token=token, + ) + except (EntryNotFoundError, LocalEntryNotFoundError, HFValidationError, RepositoryNotFoundError): + remote_adapter_config = None + else: + remote_adapter_config = None + + local_adapter_present = os.path.exists(os.path.join(pretrained_model_name_or_path, "adapter_config.json")) + + if (local_adapter_present or remote_adapter_config is not None) and is_peft_available(): + if peft_config is not None: + logging.warning("`peft_config` argument ignored since a peft config file was found in " f"{pretrained_model_name_or_path}") + + # Load the trained peft adapter config + if local_adapter_present: + trained_adapter_config = PeftConfig.from_pretrained(pretrained_model_name_or_path) + else: + remote_adapter_dir = os.path.dirname(remote_adapter_config) + trained_adapter_config = PeftConfig.from_pretrained(remote_adapter_dir) + + # Load the pretrained base model + pretrained_model = cls.transformers_parent_class.from_pretrained(trained_adapter_config.base_model_name_or_path, *model_args, **pretrained_kwargs) + + # Wrap the pretrained model with the trained peft adapter + pretrained_model = PeftModel.from_pretrained(pretrained_model, pretrained_model_name_or_path, is_trainable=is_trainable) + logging.info("Trained peft adapter loaded") + else: + pretrained_model = cls.transformers_parent_class.from_pretrained(pretrained_model_name_or_path, *model_args, **pretrained_kwargs) + + if peft_config is not None: + # Initialize a new peft adapter with the given config + if is_loaded_in_8bit or is_loaded_in_4bit: + pretrained_model = prepare_model_for_kbit_training( + pretrained_model, + **peft_quantization_kwargs, + ) + pretrained_model = get_peft_model(pretrained_model, peft_config) + logging.info("peft adapter initialised") + + elif isinstance(pretrained_model_name_or_path, cls.supported_pretrained_model_architectures): + pretrained_model = pretrained_model_name_or_path + + if peft_config is not None and isinstance(pretrained_model, PreTrainedModel): + # Initialize a new peft adapter with the given config + if is_loaded_in_8bit or is_loaded_in_4bit: + pretrained_model = prepare_model_for_kbit_training( + pretrained_model, + **peft_quantization_kwargs, + ) + pretrained_model = get_peft_model(pretrained_model, peft_config) + logging.info("peft adapter initialised") + else: + raise ValueError("pretrained_model_name_or_path should be a string or a PreTrainedModel, " f"but is {type(pretrained_model_name_or_path)}") + + if is_peft_available(): + if isinstance(pretrained_model, PeftModel): + is_peft_model = True + # for backward compatibility + if hasattr(pretrained_model, "active_peft_config") and isinstance(pretrained_model.active_peft_config, PromptLearningConfig): + raise ValueError("PromptLearningConfig is not supported for PPO training.") + + # Add reward modeling adapter if specified + if not is_peft_model and reward_adapter is not None: + raise ValueError("reward_adapter can only be used with a PeftModel. ") + elif is_peft_model and reward_adapter is not None: + score_module = cls.add_and_load_reward_modeling_adapter(pretrained_model, reward_adapter, reward_adapter_name, token=token) + multi_adapter_args = { + "score_module": score_module, + "supports_rm_adapter": True, + "rm_adapter_name": reward_adapter_name, + } + else: + multi_adapter_args = {"supports_rm_adapter": False} + + # Then, create the full model by instantiating the wrapper class + model = cls(pretrained_model, **multi_adapter_args, **trl_model_args) + + # if resume_training, load the state_dict again - this is ok since the + # state_dict is removed from the model after loading it. + is_resuming_training = True + if isinstance(pretrained_model_name_or_path, str): + safe_filename = os.path.join(pretrained_model_name_or_path, "model.safetensors") + filename = os.path.join(pretrained_model_name_or_path, "pytorch_model.bin") + + sharded_index_filename = os.path.join(pretrained_model_name_or_path, "pytorch_model.bin.index.json") + safe_sharded_index_filename = os.path.join(pretrained_model_name_or_path, "model.safetensors.index.json") + is_sharded = False + use_safe = os.path.exists(safe_filename) + + if not (os.path.exists(filename) or os.path.exists(safe_filename)): + # Try with `pytorch_model.bin` + filename, files_to_download, is_sharded, is_resuming_training = cls._get_checkpoint_from_hub( + pretrained_model, + pretrained_model_name_or_path, + sharded_index_filename, + token=token, + ) + # Try with safetensors + if filename is None and files_to_download is None: + safe_filename, files_to_download, is_sharded, is_resuming_training = cls._get_checkpoint_from_hub( + pretrained_model, + pretrained_model_name_or_path, + safe_sharded_index_filename, + token=token, + model_name="model.safetensors", + model_index_name="model.safetensors.index.json", + ) + use_safe = True + else: + use_safe = False + + loading_func = safe_load_file if use_safe else torch.load + load_kwargs = {} if use_safe else {"map_location": "cpu"} + + if is_resuming_training: + if is_sharded: + # download each file and add it to the state_dict + state_dict = {} + + for shard_file in files_to_download: + filename = hf_hub_download( + pretrained_model_name_or_path, + shard_file, + token=token, + ) + state_dict.update(loading_func(filename, **load_kwargs)) + else: + state_dict = loading_func(filename if not use_safe else safe_filename, **load_kwargs) + + else: + state_dict = pretrained_model_name_or_path.state_dict() + + model.is_peft_model = is_peft_model + model.current_device = current_device + + if is_resuming_training: + model.post_init(state_dict=state_dict) + + return model + + @classmethod + def _get_checkpoint_from_hub( + cls, + pretrained_model, + pretrained_model_name_or_path, + index_filename, + token=None, + model_name="pytorch_model.bin", + model_index_name="pytorch_model.bin.index.json", + ): + files_to_download = None + filename = None + is_resuming_training = True + is_sharded = False + + try: + filename = hf_hub_download( + pretrained_model_name_or_path, + model_name, + token=token, + ) + # sharded + except (EntryNotFoundError, LocalEntryNotFoundError, HFValidationError, RepositoryNotFoundError): + if os.path.exists(index_filename): + index_file_name = index_filename + else: + try: + index_file_name = hf_hub_download( + pretrained_model_name_or_path, + model_index_name, + token=token, + ) + except (EntryNotFoundError, LocalEntryNotFoundError, HFValidationError, RepositoryNotFoundError): + # not continue training, do not have v_head weight + is_resuming_training = False + logging.warning(f"A {type(pretrained_model)} model is loaded from '{pretrained_model_name_or_path}', " f"and no v_head weight is found. This IS expected if you are not resuming PPO training.") + # load json + if is_resuming_training: + with open(index_file_name, "r") as f: + index = json.load(f) + # check filename with `v_head` or any known extra module: + files_to_download = set() + for k, v in index["weight_map"].items(): + if any([module in k for module in cls.supported_modules]): + files_to_download.add(v) + is_sharded = True + + return filename, files_to_download, is_sharded, is_resuming_training + + @classmethod + def _get_current_device(cls): + r""" + Get the current device. For GPU, we return the local process index using the `accelerate.PartialState` + object to handle corner cases when running scripts in distributed environments. + + Returns: + current_device (`Union[int, str]`): + The current device. + """ + state = PartialState() + if is_xpu_available(): + return f"xpu:{state.local_process_index}" + elif is_npu_available(): + return f"npu:{state.local_process_index}" + else: + return state.local_process_index if torch.cuda.is_available() else "cpu" + + @classmethod + def _split_kwargs(cls, kwargs): + """ + Separate the kwargs from the arguments that we support inside + `supported_args` and the ones that we don't. + """ + check_peft_kwargs = False + + if is_peft_available(): + from peft import prepare_model_for_kbit_training + + check_peft_kwargs = True + + supported_kwargs = {} + unsupported_kwargs = {} + peft_kwargs = {} + + for key, value in kwargs.items(): + if key in cls.supported_args: + supported_kwargs[key] = value + else: + unsupported_kwargs[key] = value + + if check_peft_kwargs: + if key in prepare_model_for_kbit_training.__code__.co_varnames: + peft_kwargs[key] = value + if key in unsupported_kwargs: + unsupported_kwargs.pop(key) + + return supported_kwargs, unsupported_kwargs, peft_kwargs + + @classmethod + def add_and_load_reward_modeling_adapter(cls, pretrained_model, adapter_model_id, adapter_name="reward_model_adapter", token=None): + r""" + Add and load a reward modeling adapter. This method can only be used if the + model is a `PeftModel` and if you have initialized the model with the `reward_modeling_adapter_id` + argument, pointing to the id of the reward modeling adapter. The latest needs also to contain the + score head in order to produce the reward. + """ + pretrained_model.load_adapter(adapter_model_id, adapter_name, is_trainable=False) + pretrained_model.train() + + filename = os.path.join(adapter_model_id, "adapter_model.bin") + safe_loading = False + if not os.path.exists(filename): + try: + local_filename = hf_hub_download( + adapter_model_id, + "adapter_model.bin", + token=token, + ) + except: # noqa + filename = os.path.join(adapter_model_id, "adapter_model.safetensors") + safe_loading = True + if not os.path.exists(filename): + try: + local_filename = hf_hub_download( + adapter_model_id, + "adapter_model.safetensors", + token=token, + ) + except: # noqa + raise ValueError("Could not find adapter model in the Hub, make sure you have the correct adapter model id.") + else: + local_filename = filename + else: + local_filename = filename + + loading_func = safe_load_file if safe_loading else torch.load + load_kwargs = {} if safe_loading else {"map_location": "cpu"} + + adapter_state_dict = loading_func(local_filename, **load_kwargs) + + for score_name_candidate in cls.supported_rm_modules: + if any([score_name_candidate in name for name in adapter_state_dict.keys()]): + score_name = score_name_candidate + # we have found the correct head name and can break + break + + score_dict = {} + + for name, param in adapter_state_dict.items(): + if score_name in name: + key_name = ".".join(name.split(".")[-1:]) + score_dict[key_name] = param.to(cls._get_current_device()) + + num_labels, hidden_dim = score_dict["weight"].shape + has_bias = any(["bias" in name for name in adapter_state_dict.keys()]) + + score = nn.Linear(hidden_dim, num_labels, bias=has_bias).to( + device=cls._get_current_device(), + dtype=pretrained_model.dtype, + ) + score.load_state_dict(score_dict) + for param in score.parameters(): + param.requires_grad = False + + return score + + def push_to_hub(self, *args, **kwargs): + r""" + Push the pretrained model to the hub. This method is a wrapper around + `transformers.PreTrainedModel.push_to_hub`. Please refer to the documentation + of `transformers.PreTrainedModel.push_to_hub` for more information. + + Args: + *args (`list`, *optional*): + Positional arguments passed along to the underlying model's + `push_to_hub` method. + **kwargs (`dict`, *optional*): + Keyword arguments passed along to the underlying model's + `push_to_hub` method. + """ + raise NotImplementedError + + def save_pretrained(self, *args, **kwargs): + r""" + Save the pretrained model to a directory. This method is a wrapper around + `transformers.PreTrainedModel.save_pretrained`. Please refer to the documentation + of `transformers.PreTrainedModel.save_pretrained` for more information. + + Args: + *args (`list`, *optional*): + Positional arguments passed along to the underlying model's + `save_pretrained` method. + **kwargs (`dict`, *optional*): + Keyword arguments passed along to the underlying model's + `save_pretrained` method. + """ + state_dict = kwargs.get("state_dict") + if state_dict is None: + state_dict = self.state_dict() + kwargs["state_dict"] = state_dict + + # if it is a peft model only save the `v_head` state_dict and + # pop the `state_dict` from the kwargs to avoid slient bugs with `peft` + if self.is_peft_model: + save_path = args[0] + save_path = os.path.join(save_path, "pytorch_model.bin") + torch.save(state_dict, save_path) + _ = kwargs.pop("state_dict", None) + + return self.pretrained_model.save_pretrained(*args, **kwargs) + + def state_dict(self, *args, **kwargs): + r""" + Return the state_dict of the pretrained model. + """ + raise NotImplementedError + + def post_init(self, *args, **kwargs): + r""" + Post initialization method. This method is called after the model is + instantiated and loaded from a checkpoint. It can be used to perform + additional operations such as loading the state_dict. + """ + raise NotImplementedError + + def compute_reward_score(self, input_ids, attention_mask=None, **kwargs): + r""" + Computes the reward score for a given input. The method has first to enable the adapter + and then compute the reward score. After that the model disables the reward modeling + adapter and enables the default ppo adapter again. + """ + if not self.supports_rm_adapter: + raise ValueError("This model does not support reward modeling adapter.") + + # enable rm adapter + self.pretrained_model.set_adapter(self.rm_adapter_name) + self.pretrained_model.eval() + + with torch.no_grad(): + base_model_output = self.pretrained_model( + input_ids=input_ids, + attention_mask=attention_mask, + output_hidden_states=True, + return_dict=True, + **kwargs, + ) + + last_hidden_states = base_model_output.hidden_states[-1] + scores = self.score(last_hidden_states) + + self.pretrained_model.set_adapter(self.policy_adapter_name) + self.pretrained_model.eval() + + return scores + + +def create_reference_model(model: PreTrainedModelWrapper, num_shared_layers: int = None, pattern: str = None) -> PreTrainedModelWrapper: + """ + Creates a static reference copy of a model. Note that model will be in `.eval()` mode. + + Args: + model (`PreTrainedModelWrapper`): The model to be copied. + num_shared_layers (`int`, *optional*): The number of initial layers that are shared between both models and kept frozen. + pattern (`str`, *optional*): The shared layers are selected with a string pattern + (e.g. "transformer.h.{layer}" for GPT2) and if a custom pattern is necessary it can be passed here. + + Returns + `PreTrainedModelWrapper` + """ + if is_deepspeed_zero3_enabled(): + raise ValueError("DeepSpeed ZeRO-3 is enabled and is not compatible with `create_reference_model()`. Please instantiate your reference model directly with `AutoCausalLM.from_pretrained()`.") + + parameter_names = [n for n, _ in model.named_parameters()] + ref_model = deepcopy(model) + + # if no layers are shared, return copy of model + if num_shared_layers is None: + for param_name in parameter_names: + param = ref_model.get_parameter(param_name) + param.requires_grad = False + return ref_model.eval() + + # identify layer name pattern + if pattern is not None: + pattern = pattern.format(layer=num_shared_layers) + else: + for pattern_candidate in LAYER_PATTERNS: + pattern_candidate = pattern_candidate.format(layer=num_shared_layers) + if any([pattern_candidate in name for name in parameter_names]): + pattern = pattern_candidate + break + + if pattern is None: + raise ValueError("Layer pattern could not be matched.") + + # divide parameters in shared and unshared parameter lists + shared_param_list = [] + unshared_param_list = [] + + shared_parameter = True + for name, param in model.named_parameters(): + if pattern in name: + shared_parameter = False + if shared_parameter: + shared_param_list.append(name) + else: + unshared_param_list.append(name) + + # create reference of the original parameter if they are shared + for param_name in shared_param_list: + param = model.get_parameter(param_name) + param.requires_grad = False + + ref_param = ref_model.get_parameter(param_name) # noqa + ref_param = param # noqa + + # for all other parameters just make sure they don't use gradients + for param_name in unshared_param_list: + param = ref_model.get_parameter(param_name) + param.requires_grad = False + + if pattern is not None and len(unshared_param_list) == 0: + logging.warning("Pattern passed or found, but no layers matched in the model. Check for a typo.") + + return ref_model.eval() diff --git a/trl/models/modeling_sd_base.py b/trl/models/modeling_sd_base.py new file mode 100644 index 0000000..5cf6a1d --- /dev/null +++ b/trl/models/modeling_sd_base.py @@ -0,0 +1,624 @@ +# Copyright 2023 DDPO-pytorch authors (Kevin Black), The HuggingFace Team, metric-space. 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. + +import contextlib +import os +import warnings +from dataclasses import dataclass +from typing import Any, Callable, Dict, List, Optional, Union + +import numpy as np +import torch +from diffusers import DDIMScheduler, StableDiffusionPipeline, UNet2DConditionModel +from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion import rescale_noise_cfg +from diffusers.utils import convert_state_dict_to_diffusers + +from ..core import randn_tensor +from ..import_utils import is_peft_available + + +if is_peft_available(): + from peft import LoraConfig + from peft.utils import get_peft_model_state_dict + + +@dataclass +class DDPOPipelineOutput(object): + """ + Output class for the diffusers pipeline to be finetuned with the DDPO trainer + + Args: + images (`torch.Tensor`): + The generated images. + latents (`List[torch.Tensor]`): + The latents used to generate the images. + log_probs (`List[torch.Tensor]`): + The log probabilities of the latents. + + """ + + images: torch.Tensor + latents: torch.Tensor + log_probs: torch.Tensor + + +@dataclass +class DDPOSchedulerOutput(object): + """ + Output class for the diffusers scheduler to be finetuned with the DDPO trainer + + Args: + latents (`torch.Tensor`): + Predicted sample at the previous timestep. Shape: `(batch_size, num_channels, height, width)` + log_probs (`torch.Tensor`): + Log probability of the above mentioned sample. Shape: `(batch_size)` + """ + + latents: torch.Tensor + log_probs: torch.Tensor + + +class DDPOStableDiffusionPipeline(object): + """ + Main class for the diffusers pipeline to be finetuned with the DDPO trainer + """ + + def __call__(self, *args, **kwargs) -> DDPOPipelineOutput: + raise NotImplementedError + + def scheduler_step(self, *args, **kwargs) -> DDPOSchedulerOutput: + raise NotImplementedError + + @property + def unet(self): + """ + Returns the 2d U-Net model used for diffusion. + """ + raise NotImplementedError + + @property + def vae(self): + """ + Returns the Variational Autoencoder model used from mapping images to and from the latent space + """ + raise NotImplementedError + + @property + def tokenizer(self): + """ + Returns the tokenizer used for tokenizing text inputs + """ + raise NotImplementedError + + @property + def scheduler(self): + """ + Returns the scheduler associated with the pipeline used for the diffusion process + """ + raise NotImplementedError + + @property + def text_encoder(self): + """ + Returns the text encoder used for encoding text inputs + """ + raise NotImplementedError + + @property + def autocast(self): + """ + Returns the autocast context manager + """ + raise NotImplementedError + + def set_progress_bar_config(self, *args, **kwargs): + """ + Sets the progress bar config for the pipeline + """ + raise NotImplementedError + + def save_pretrained(self, *args, **kwargs): + """ + Saves all of the model weights + """ + raise NotImplementedError + + def get_trainable_layers(self, *args, **kwargs): + """ + Returns the trainable parameters of the pipeline + """ + raise NotImplementedError + + def save_checkpoint(self, *args, **kwargs): + """ + Light wrapper around accelerate's register_save_state_pre_hook which is run before saving state + """ + raise NotImplementedError + + def load_checkpoint(self, *args, **kwargs): + """ + Light wrapper around accelerate's register_lad_state_pre_hook which is run before loading state + """ + raise NotImplementedError + + +def _left_broadcast(input_tensor, shape): + """ + As opposed to the default direction of broadcasting (right to left), this function broadcasts + from left to right + Args: + input_tensor (`torch.FloatTensor`): is the tensor to broadcast + shape (`Tuple[int]`): is the shape to broadcast to + """ + input_ndim = input_tensor.ndim + if input_ndim > len(shape): + raise ValueError("The number of dimensions of the tensor to broadcast cannot be greater than the length of the shape to broadcast to") + return input_tensor.reshape(input_tensor.shape + (1,) * (len(shape) - input_ndim)).broadcast_to(shape) + + +def _get_variance(self, timestep, prev_timestep): + alpha_prod_t = torch.gather(self.alphas_cumprod, 0, timestep.cpu()).to(timestep.device) + alpha_prod_t_prev = torch.where( + prev_timestep.cpu() >= 0, + self.alphas_cumprod.gather(0, prev_timestep.cpu()), + self.final_alpha_cumprod, + ).to(timestep.device) + beta_prod_t = 1 - alpha_prod_t + beta_prod_t_prev = 1 - alpha_prod_t_prev + + variance = (beta_prod_t_prev / beta_prod_t) * (1 - alpha_prod_t / alpha_prod_t_prev) + + return variance + + +def scheduler_step( + self, + model_output: torch.FloatTensor, + timestep: int, + sample: torch.FloatTensor, + eta: float = 0.0, + use_clipped_model_output: bool = False, + generator=None, + prev_sample: Optional[torch.FloatTensor] = None, +) -> DDPOSchedulerOutput: + """ + + Predict the sample at the previous timestep by reversing the SDE. Core function to propagate the diffusion + process from the learned model outputs (most often the predicted noise). + + Args: + model_output (`torch.FloatTensor`): direct output from learned diffusion model. + timestep (`int`): current discrete timestep in the diffusion chain. + sample (`torch.FloatTensor`): + current instance of sample being created by diffusion process. + eta (`float`): weight of noise for added noise in diffusion step. + use_clipped_model_output (`bool`): if `True`, compute "corrected" `model_output` from the clipped + predicted original sample. Necessary because predicted original sample is clipped to [-1, 1] when + `self.config.clip_sample` is `True`. If no clipping has happened, "corrected" `model_output` would + coincide with the one provided as input and `use_clipped_model_output` will have not effect. + generator: random number generator. + variance_noise (`torch.FloatTensor`): instead of generating noise for the variance using `generator`, we + can directly provide the noise for the variance itself. This is useful for methods such as + CycleDiffusion. (https://arxiv.org/abs/2210.05559) + + Returns: + `DDPOSchedulerOutput`: the predicted sample at the previous timestep and the log probability of the sample + """ + + if self.num_inference_steps is None: + raise ValueError("Number of inference steps is 'None', you need to run 'set_timesteps' after creating the scheduler") + + # See formulas (12) and (16) of DDIM paper https://arxiv.org/pdf/2010.02502.pdf + # Ideally, read DDIM paper in-detail understanding + + # Notation ( -> + # - pred_noise_t -> e_theta(x_t, t) + # - pred_original_sample -> f_theta(x_t, t) or x_0 + # - std_dev_t -> sigma_t + # - eta -> η + # - pred_sample_direction -> "direction pointing to x_t" + # - pred_prev_sample -> "x_t-1" + + # 1. get previous step value (=t-1) + prev_timestep = timestep - self.config.num_train_timesteps // self.num_inference_steps + # to prevent OOB on gather + prev_timestep = torch.clamp(prev_timestep, 0, self.config.num_train_timesteps - 1) + + # 2. compute alphas, betas + alpha_prod_t = self.alphas_cumprod.gather(0, timestep.cpu()) + alpha_prod_t_prev = torch.where( + prev_timestep.cpu() >= 0, + self.alphas_cumprod.gather(0, prev_timestep.cpu()), + self.final_alpha_cumprod, + ) + alpha_prod_t = _left_broadcast(alpha_prod_t, sample.shape).to(sample.device) + alpha_prod_t_prev = _left_broadcast(alpha_prod_t_prev, sample.shape).to(sample.device) + + beta_prod_t = 1 - alpha_prod_t + + # 3. compute predicted original sample from predicted noise also called + # "predicted x_0" of formula (12) from https://arxiv.org/pdf/2010.02502.pdf + if self.config.prediction_type == "epsilon": + pred_original_sample = (sample - beta_prod_t ** (0.5) * model_output) / alpha_prod_t ** (0.5) + pred_epsilon = model_output + elif self.config.prediction_type == "sample": + pred_original_sample = model_output + pred_epsilon = (sample - alpha_prod_t ** (0.5) * pred_original_sample) / beta_prod_t ** (0.5) + elif self.config.prediction_type == "v_prediction": + pred_original_sample = (alpha_prod_t**0.5) * sample - (beta_prod_t**0.5) * model_output + pred_epsilon = (alpha_prod_t**0.5) * model_output + (beta_prod_t**0.5) * sample + else: + raise ValueError(f"prediction_type given as {self.config.prediction_type} must be one of `epsilon`, `sample`, or" " `v_prediction`") + + # 4. Clip or threshold "predicted x_0" + if self.config.thresholding: + pred_original_sample = self._threshold_sample(pred_original_sample) + elif self.config.clip_sample: + pred_original_sample = pred_original_sample.clamp(-self.config.clip_sample_range, self.config.clip_sample_range) + + # 5. compute variance: "sigma_t(η)" -> see formula (16) + # σ_t = sqrt((1 − α_t−1)/(1 − α_t)) * sqrt(1 − α_t/α_t−1) + variance = _get_variance(self, timestep, prev_timestep) + std_dev_t = eta * variance ** (0.5) + std_dev_t = _left_broadcast(std_dev_t, sample.shape).to(sample.device) + + if use_clipped_model_output: + # the pred_epsilon is always re-derived from the clipped x_0 in Glide + pred_epsilon = (sample - alpha_prod_t ** (0.5) * pred_original_sample) / beta_prod_t ** (0.5) + + # 6. compute "direction pointing to x_t" of formula (12) from https://arxiv.org/pdf/2010.02502.pdf + pred_sample_direction = (1 - alpha_prod_t_prev - std_dev_t**2) ** (0.5) * pred_epsilon + + # 7. compute x_t without "random noise" of formula (12) from https://arxiv.org/pdf/2010.02502.pdf + prev_sample_mean = alpha_prod_t_prev ** (0.5) * pred_original_sample + pred_sample_direction + + if prev_sample is not None and generator is not None: + raise ValueError("Cannot pass both generator and prev_sample. Please make sure that either `generator` or" " `prev_sample` stays `None`.") + + if prev_sample is None: + variance_noise = randn_tensor( + model_output.shape, + generator=generator, + device=model_output.device, + dtype=model_output.dtype, + ) + prev_sample = prev_sample_mean + std_dev_t * variance_noise + + # log prob of prev_sample given prev_sample_mean and std_dev_t + log_prob = -((prev_sample.detach() - prev_sample_mean) ** 2) / (2 * (std_dev_t**2)) - torch.log(std_dev_t) - torch.log(torch.sqrt(2 * torch.as_tensor(np.pi))) + # mean along all but batch dimension + log_prob = log_prob.mean(dim=tuple(range(1, log_prob.ndim))) + + return DDPOSchedulerOutput(prev_sample.type(sample.dtype), log_prob) + + +# 1. The output type for call is different as the logprobs are now returned +# 2. An extra method called `scheduler_step` is added which is used to constraint the scheduler output +@torch.no_grad() +def pipeline_step( + self, + prompt: Optional[Union[str, List[str]]] = None, + height: Optional[int] = None, + width: Optional[int] = None, + num_inference_steps: int = 50, + guidance_scale: float = 7.5, + negative_prompt: Optional[Union[str, List[str]]] = None, + num_images_per_prompt: Optional[int] = 1, + eta: float = 0.0, + generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None, + latents: Optional[torch.FloatTensor] = None, + prompt_embeds: Optional[torch.FloatTensor] = None, + negative_prompt_embeds: Optional[torch.FloatTensor] = None, + output_type: Optional[str] = "pil", + return_dict: bool = True, + callback: Optional[Callable[[int, int, torch.FloatTensor], None]] = None, + callback_steps: int = 1, + cross_attention_kwargs: Optional[Dict[str, Any]] = None, + guidance_rescale: float = 0.0, +): + r""" + Function invoked when calling the pipeline for generation. Args: prompt (`str` or `List[str]`, *optional*): The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`. instead. height (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor): The height in pixels of the generated image. + width (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor): + The width in pixels of the generated image. + num_inference_steps (`int`, *optional*, defaults to 50): + The number of denoising steps. More denoising steps usually lead to a higher quality image at the + expense of slower inference. + guidance_scale (`float`, *optional*, defaults to 7.5): + Guidance scale as defined in [Classifier-Free Diffusion Guidance](https://arxiv.org/abs/2207.12598). + `guidance_scale` is defined as `w` of equation 2. of [Imagen + Paper](https://arxiv.org/pdf/2205.11487.pdf). Guidance scale is enabled by setting `guidance_scale > + 1`. Higher guidance scale encourages to generate images that are closely linked to the text `prompt`, + usually at the expense of lower image quality. + negative_prompt (`str` or `List[str]`, *optional*): + The prompt or prompts not to guide the image generation. If not defined, one has to pass + `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is + less than `1`). + num_images_per_prompt (`int`, *optional*, defaults to 1): + The number of images to generate per prompt. + eta (`float`, *optional*, defaults to 0.0): + Corresponds to parameter eta (η) in the DDIM paper: https://arxiv.org/abs/2010.02502. Only applies to + [`schedulers.DDIMScheduler`], will be ignored for others. + generator (`torch.Generator` or `List[torch.Generator]`, *optional*): + One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html) + to make generation deterministic. + latents (`torch.FloatTensor`, *optional*): + Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image + generation. Can be used to tweak the same generation with different prompts. If not provided, a latents + tensor will ge generated by sampling using the supplied random `generator`. + prompt_embeds (`torch.FloatTensor`, *optional*): + Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not + provided, text embeddings will be generated from `prompt` input argument. + negative_prompt_embeds (`torch.FloatTensor`, *optional*): + Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt + weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input + argument. + output_type (`str`, *optional*, defaults to `"pil"`): + The output format of the generate image. Choose between + [PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`. + return_dict (`bool`, *optional*, defaults to `True`): + Whether or not to return a [`~pipelines.stable_diffusion.StableDiffusionPipelineOutput`] instead of a + plain tuple. + callback (`Callable`, *optional*): + A function that will be called every `callback_steps` steps during inference. The function will be + called with the following arguments: `callback(step: int, timestep: int, latents: torch.FloatTensor)`. + callback_steps (`int`, *optional*, defaults to 1): + The frequency at which the `callback` function will be called. If not specified, the callback will be + called at every step. + cross_attention_kwargs (`dict`, *optional*): + A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under + `self.processor` in + [diffusers.cross_attention](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/cross_attention.py). + guidance_rescale (`float`, *optional*, defaults to 0.7): + Guidance rescale factor proposed by [Common Diffusion Noise Schedules and Sample Steps are + Flawed](https://arxiv.org/pdf/2305.08891.pdf) `guidance_scale` is defined as `φ` in equation 16. of + [Common Diffusion Noise Schedules and Sample Steps are Flawed](https://arxiv.org/pdf/2305.08891.pdf). + Guidance rescale factor should fix overexposure when using zero terminal SNR. + + Examples: + + Returns: + `DDPOPipelineOutput`: The generated image, the predicted latents used to generate the image and the associated log probabilities + """ + # 0. Default height and width to unet + height = height or self.unet.config.sample_size * self.vae_scale_factor + width = width or self.unet.config.sample_size * self.vae_scale_factor + + # 1. Check inputs. Raise error if not correct + self.check_inputs( + prompt, + height, + width, + callback_steps, + negative_prompt, + prompt_embeds, + negative_prompt_embeds, + ) + + # 2. Define call parameters + if prompt is not None and isinstance(prompt, str): + batch_size = 1 + elif prompt is not None and isinstance(prompt, list): + batch_size = len(prompt) + else: + batch_size = prompt_embeds.shape[0] + + device = self._execution_device + # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2) + # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1` + # corresponds to doing no classifier free guidance. + do_classifier_free_guidance = guidance_scale > 1.0 + + # 3. Encode input prompt + text_encoder_lora_scale = cross_attention_kwargs.get("scale", None) if cross_attention_kwargs is not None else None + prompt_embeds = self._encode_prompt( + prompt, + device, + num_images_per_prompt, + do_classifier_free_guidance, + negative_prompt, + prompt_embeds=prompt_embeds, + negative_prompt_embeds=negative_prompt_embeds, + lora_scale=text_encoder_lora_scale, + ) + + # 4. Prepare timesteps + self.scheduler.set_timesteps(num_inference_steps, device=device) + timesteps = self.scheduler.timesteps + + # 5. Prepare latent variables + num_channels_latents = self.unet.config.in_channels + latents = self.prepare_latents( + batch_size * num_images_per_prompt, + num_channels_latents, + height, + width, + prompt_embeds.dtype, + device, + generator, + latents, + ) + + # 6. Denoising loop + num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order + all_latents = [latents] + all_log_probs = [] + with self.progress_bar(total=num_inference_steps) as progress_bar: + for i, t in enumerate(timesteps): + # expand the latents if we are doing classifier free guidance + latent_model_input = torch.cat([latents] * 2) if do_classifier_free_guidance else latents + latent_model_input = self.scheduler.scale_model_input(latent_model_input, t) + + # predict the noise residual + noise_pred = self.unet( + latent_model_input, + t, + encoder_hidden_states=prompt_embeds, + cross_attention_kwargs=cross_attention_kwargs, + return_dict=False, + )[0] + + # perform guidance + if do_classifier_free_guidance: + noise_pred_uncond, noise_pred_text = noise_pred.chunk(2) + noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond) + + if do_classifier_free_guidance and guidance_rescale > 0.0: + # Based on 3.4. in https://arxiv.org/pdf/2305.08891.pdf + noise_pred = rescale_noise_cfg(noise_pred, noise_pred_text, guidance_rescale=guidance_rescale) + + # compute the previous noisy sample x_t -> x_t-1 + scheduler_output = scheduler_step(self.scheduler, noise_pred, t, latents, eta) + latents = scheduler_output.latents + log_prob = scheduler_output.log_probs + + all_latents.append(latents) + all_log_probs.append(log_prob) + + # call the callback, if provided + if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0): + progress_bar.update() + if callback is not None and i % callback_steps == 0: + callback(i, t, latents) + + if not output_type == "latent": + image = self.vae.decode(latents / self.vae.config.scaling_factor, return_dict=False)[0] + image, has_nsfw_concept = self.run_safety_checker(image, device, prompt_embeds.dtype) + else: + image = latents + has_nsfw_concept = None + + if has_nsfw_concept is None: + do_denormalize = [True] * image.shape[0] + else: + do_denormalize = [not has_nsfw for has_nsfw in has_nsfw_concept] + + image = self.image_processor.postprocess(image, output_type=output_type, do_denormalize=do_denormalize) + + # Offload last model to CPU + if hasattr(self, "final_offload_hook") and self.final_offload_hook is not None: + self.final_offload_hook.offload() + + return DDPOPipelineOutput(image, all_latents, all_log_probs) + + +class DefaultDDPOStableDiffusionPipeline(DDPOStableDiffusionPipeline): + def __init__(self, pretrained_model_name: str, *, pretrained_model_revision: str = "main", use_lora: bool = True): + self.sd_pipeline = StableDiffusionPipeline.from_pretrained(pretrained_model_name, revision=pretrained_model_revision) + + self.use_lora = use_lora + self.pretrained_model = pretrained_model_name + self.pretrained_revision = pretrained_model_revision + + try: + self.sd_pipeline.load_lora_weights( + pretrained_model_name, + weight_name="pytorch_lora_weights.safetensors", + revision=pretrained_model_revision, + ) + self.use_lora = True + except OSError: + if use_lora: + warnings.warn("If you are aware that the pretrained model has no lora weights to it, ignore this message. " "Otherwise please check the if `pytorch_lora_weights.safetensors` exists in the model folder.") + + self.sd_pipeline.scheduler = DDIMScheduler.from_config(self.sd_pipeline.scheduler.config) + self.sd_pipeline.safety_checker = None + + # memory optimization + self.sd_pipeline.vae.requires_grad_(False) + self.sd_pipeline.text_encoder.requires_grad_(False) + self.sd_pipeline.unet.requires_grad_(not self.use_lora) + + def __call__(self, *args, **kwargs) -> DDPOPipelineOutput: + return pipeline_step(self.sd_pipeline, *args, **kwargs) + + def scheduler_step(self, *args, **kwargs) -> DDPOSchedulerOutput: + return scheduler_step(self.sd_pipeline.scheduler, *args, **kwargs) + + @property + def unet(self): + return self.sd_pipeline.unet + + @property + def vae(self): + return self.sd_pipeline.vae + + @property + def tokenizer(self): + return self.sd_pipeline.tokenizer + + @property + def scheduler(self): + return self.sd_pipeline.scheduler + + @property + def text_encoder(self): + return self.sd_pipeline.text_encoder + + @property + def autocast(self): + return contextlib.nullcontext if self.use_lora else None + + def save_pretrained(self, output_dir): + if self.use_lora: + state_dict = convert_state_dict_to_diffusers(get_peft_model_state_dict(self.sd_pipeline.unet)) + self.sd_pipeline.save_lora_weights(save_directory=output_dir, unet_lora_layers=state_dict) + self.sd_pipeline.save_pretrained(output_dir) + + def set_progress_bar_config(self, *args, **kwargs): + self.sd_pipeline.set_progress_bar_config(*args, **kwargs) + + def get_trainable_layers(self): + if self.use_lora: + lora_config = LoraConfig( + r=4, + lora_alpha=4, + init_lora_weights="gaussian", + target_modules=["to_k", "to_q", "to_v", "to_out.0"], + ) + self.sd_pipeline.unet.add_adapter(lora_config) + + # To avoid accelerate unscaling problems in FP16. + for param in self.sd_pipeline.unet.parameters(): + # only upcast trainable parameters (LoRA) into fp32 + if param.requires_grad: + param.data = param.to(torch.float32) + return self.sd_pipeline.unet + else: + return self.sd_pipeline.unet + + def save_checkpoint(self, models, weights, output_dir): + if len(models) != 1: + raise ValueError("Given how the trainable params were set, this should be of length 1") + if self.use_lora and hasattr(models[0], "peft_config") and getattr(models[0], "peft_config", None) is not None: + state_dict = convert_state_dict_to_diffusers(get_peft_model_state_dict(models[0])) + self.sd_pipeline.save_lora_weights(save_directory=output_dir, unet_lora_layers=state_dict) + elif not self.use_lora and isinstance(models[0], UNet2DConditionModel): + models[0].save_pretrained(os.path.join(output_dir, "unet")) + else: + raise ValueError(f"Unknown model type {type(models[0])}") + + def load_checkpoint(self, models, input_dir): + if len(models) != 1: + raise ValueError("Given how the trainable params were set, this should be of length 1") + if self.use_lora: + lora_state_dict, network_alphas = self.sd_pipeline.lora_state_dict(input_dir, weight_name="pytorch_lora_weights.safetensors") + self.sd_pipeline.load_lora_into_unet(lora_state_dict, network_alphas=network_alphas, unet=models[0]) + + elif not self.use_lora and isinstance(models[0], UNet2DConditionModel): + load_model = UNet2DConditionModel.from_pretrained(input_dir, subfolder="unet") + models[0].register_to_config(**load_model.config) + models[0].load_state_dict(load_model.state_dict()) + del load_model + else: + raise ValueError(f"Unknown model type {type(models[0])}") diff --git a/trl/models/modeling_value_head.py b/trl/models/modeling_value_head.py new file mode 100644 index 0000000..f2ca25e --- /dev/null +++ b/trl/models/modeling_value_head.py @@ -0,0 +1,421 @@ +# Copyright 2022 The HuggingFace Team. 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. +import torch +import torch.nn as nn +from transformers import AutoModelForCausalLM, AutoModelForSeq2SeqLM + +from .modeling_base import PreTrainedModelWrapper + + +class ValueHead(nn.Module): + r""" + The ValueHead class implements a head for GPT2 that returns a scalar for each output token. + """ + + def __init__(self, config, **kwargs): + super().__init__() + if not hasattr(config, "summary_dropout_prob"): + summary_dropout_prob = kwargs.pop("summary_dropout_prob", 0.1) + else: + summary_dropout_prob = config.summary_dropout_prob + + self.dropout = nn.Dropout(summary_dropout_prob) if summary_dropout_prob else nn.Identity() + + # some models such as OPT have a projection layer before the word embeddings - e.g. OPT-350m + if hasattr(config, "hidden_size"): + hidden_size = config.hidden_size + if hasattr(config, "word_embed_proj_dim"): + hidden_size = config.word_embed_proj_dim + elif hasattr(config, "is_encoder_decoder"): + if config.is_encoder_decoder and hasattr(config, "decoder"): + if hasattr(config.decoder, "hidden_size"): + hidden_size = config.decoder.hidden_size + + self.summary = nn.Linear(hidden_size, 1) + + self.flatten = nn.Flatten() + + def forward(self, hidden_states): + output = self.dropout(hidden_states) + + # For now force upcast in fp32 if needed. Let's keep the + # output in fp32 for numerical stability. + if output.dtype != self.summary.weight.dtype: + output = output.to(self.summary.weight.dtype) + + output = self.summary(output) + return output + + +class AutoModelForCausalLMWithValueHead(PreTrainedModelWrapper): + r""" + An autoregressive model with a value head in addition to the language model head. + This class inherits from `~trl.PreTrainedModelWrapper` and wraps a + `transformers.PreTrainedModel` class. The wrapper class supports classic functions + such as `from_pretrained`, `push_to_hub` and `generate`. To call a method of the wrapped + model, simply manipulate the `pretrained_model` attribute of this class. + + Class attributes: + - **transformers_parent_class** (`transformers.PreTrainedModel`) -- The parent class of the wrapped model. This + should be set to `transformers.AutoModelForCausalLM` for this class. + - **lm_head_namings** (`tuple`) -- A tuple of strings that are used to identify the language model head of the + wrapped model. This is set to `("lm_head", "embed_out")` for this class but can be changed for other models + in the future + - **supported_args** (`tuple`) -- A tuple of strings that are used to identify the arguments that are supported + by the `ValueHead` class. Currently, the supported args are: + - **summary_dropout_prob** (`float`, `optional`, defaults to `None`) -- The dropout probability for the + `ValueHead` class. + - **v_head_initializer_range** (`float`, `optional`, defaults to `0.2`) -- The initializer range for the + `ValueHead` if a specific initialization strategy is selected. + - **v_head_init_strategy** (`str`, `optional`, defaults to `None`) -- The initialization strategy for the + `ValueHead`. Currently, the supported strategies are: + - **`None`** -- Initializes the weights of the `ValueHead` with a random distribution. This is the default + strategy. + - **"normal"** -- Initializes the weights of the `ValueHead` with a normal distribution. + + """ + + transformers_parent_class = AutoModelForCausalLM + lm_head_namings = ["lm_head", "embed_out"] + supported_args = ( + "summary_dropout_prob", + "v_head_initializer_range", + "v_head_init_strategy", + ) + + def __init__(self, pretrained_model, **kwargs): + r""" + Initializes the model. + + Args: + pretrained_model (`transformers.PreTrainedModel`): + The model to wrap. It should be a causal language model such as GPT2. + or any model mapped inside the `AutoModelForCausalLM` class. + kwargs (`dict`, `optional`): + Additional keyword arguments, that are passed to the `ValueHead` class. + """ + super().__init__(pretrained_model, **kwargs) + v_head_kwargs, _, _ = self._split_kwargs(kwargs) + + if not any(hasattr(self.pretrained_model, attribute) for attribute in self.lm_head_namings): + raise ValueError("The model does not have a language model head, please use a model that has one.") + + self.v_head = ValueHead(self.pretrained_model.config, **v_head_kwargs) + + self._init_weights(**v_head_kwargs) + + def _init_weights(self, **kwargs): + r""" + Initializes the weights of the value head. The default initialization strategy is random. + Users can pass a different initialization strategy by passing the `v_head_init_strategy` argument + when calling `.from_pretrained`. Supported strategies are: + - `normal`: initializes the weights with a normal distribution. + + Args: + **kwargs (`dict`, `optional`): + Additional keyword arguments, that are passed to the `ValueHead` class. These arguments + can contain the `v_head_init_strategy` argument as well as the `v_head_initializer_range` + argument. + """ + initializer_range = kwargs.pop("v_head_initializer_range", 0.2) + # random init by default + init_strategy = kwargs.pop("v_head_init_strategy", None) + if init_strategy is None: + # do nothing + pass + elif init_strategy == "normal": + self.v_head.summary.weight.data.normal_(mean=0.0, std=initializer_range) + self.v_head.summary.bias.data.zero_() + + def forward( + self, + input_ids=None, + past_key_values=None, + attention_mask=None, + **kwargs, + ): + r""" + Applies a forward pass to the wrapped model and returns the logits of the value head. + + Args: + input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`): + Indices of input sequence tokens in the vocabulary. + past_key_values (`tuple(tuple(torch.FloatTensor))`, `optional`): + Contains pre-computed hidden-states (key and values in the attention blocks) as computed by the model + (see `past_key_values` input) to speed up sequential decoding. + attention_mask (`torch.FloatTensor` of shape `(batch_size, sequence_length)`, `optional`): + Mask to avoid performing attention on padding token indices. Mask values selected in ``[0, 1]``: + - 1 for tokens that are **not masked**, + - 0 for tokens that are **masked**. + kwargs (`dict`, `optional`): + Additional keyword arguments, that are passed to the wrapped model. + """ + kwargs["output_hidden_states"] = True # this had already been set in the LORA / PEFT examples + kwargs["past_key_values"] = past_key_values + + if self.is_peft_model and self.pretrained_model.active_peft_config.peft_type == "PREFIX_TUNING": + kwargs.pop("past_key_values") + + base_model_output = self.pretrained_model( + input_ids=input_ids, + attention_mask=attention_mask, + **kwargs, + ) + + last_hidden_state = base_model_output.hidden_states[-1] + lm_logits = base_model_output.logits + loss = base_model_output.loss + + if last_hidden_state.device != self.v_head.summary.weight.device: + last_hidden_state = last_hidden_state.to(self.v_head.summary.weight.device) + + value = self.v_head(last_hidden_state).squeeze(-1) + + # force upcast in fp32 if logits are in half-precision + if lm_logits.dtype != torch.float32: + lm_logits = lm_logits.float() + + return (lm_logits, loss, value) + + def generate(self, *args, **kwargs): + r""" + A simple wrapper around the `generate` method of the wrapped model. + Please refer to the [`generate`](https://huggingface.co/docs/transformers/internal/generation_utils) + method of the wrapped model for more information about the supported arguments. + + Args: + *args (`list`, *optional*): + Positional arguments passed to the `generate` method of the wrapped model. + **kwargs (`dict`, *optional*): + Keyword arguments passed to the `generate` method of the wrapped model. + """ + return self.pretrained_model.generate(*args, **kwargs) + + def state_dict(self, *args, **kwargs): + r""" + Returns the state dictionary of the model. We add the state dictionary of the value head + to the state dictionary of the wrapped model by prepending the key with `v_head.`. + """ + if not self.is_peft_model: + pretrained_model_state_dict = self.pretrained_model.state_dict(*args, **kwargs) + else: + # if it is a peft model, only save the v_head + pretrained_model_state_dict = {} + + v_head_state_dict = self.v_head.state_dict(*args, **kwargs) + for k, v in v_head_state_dict.items(): + pretrained_model_state_dict[f"v_head.{k}"] = v + return pretrained_model_state_dict + + def push_to_hub(self, *args, **kwargs): + setattr(self.pretrained_model, "v_head", self.v_head) + + return self.pretrained_model.push_to_hub(*args, **kwargs) + + def post_init(self, state_dict): + r""" + We add the state dictionary of the value head to the state dictionary of the wrapped model + by prepending the key with `v_head.`. This function removes the `v_head.` prefix from the + keys of the value head state dictionary. + """ + for k in list(state_dict.keys()): + if "v_head." in k: + state_dict[k.replace("v_head.", "")] = state_dict.pop(k) + self.v_head.load_state_dict(state_dict, strict=False) + del state_dict + + if hasattr(self.pretrained_model, "hf_device_map"): + if "cpu" in self.pretrained_model.hf_device_map.values() or "disk" in self.pretrained_model.hf_device_map.values(): + raise ValueError("The model is offloaded on CPU or disk - CPU & disk offloading is not supported for ValueHead models.") + + first_device = list(set(self.pretrained_model.hf_device_map.values()))[0] + + self.v_head = self.v_head.to(first_device) + + def set_device_hook(module, input, outputs): + new_output = () + for output in outputs: + if isinstance(output, torch.Tensor): + new_output += (output.to(first_device),) + else: + new_output += (output,) + return new_output + + self.register_forward_hook(set_device_hook) + + self.is_sequential_parallel = True + + +class AutoModelForSeq2SeqLMWithValueHead(PreTrainedModelWrapper): + r""" + A seq2seq model with a value head in addition to the language model head. + This class inherits from `~trl.PreTrainedModelWrapper` and wraps a + `transformers.PreTrainedModel` class. The wrapper class supports classic functions + such as `from_pretrained` and `push_to_hub` and also provides some additional + functionalities such as `generate`. + + Args: + pretrained_model (`transformers.PreTrainedModel`): + The model to wrap. It should be a causal language model such as GPT2. + or any model mapped inside the `AutoModelForSeq2SeqLM` class. + kwargs: + Additional keyword arguments passed along to the `ValueHead` class. + """ + + transformers_parent_class = AutoModelForSeq2SeqLM + lm_head_namings = ["lm_head", "embed_out", "output_projection"] + supported_args = ( + "summary_dropout_prob", + "v_head_initializer_range", + "v_head_init_strategy", + ) + + def __init__(self, pretrained_model, **kwargs): + super().__init__(pretrained_model, **kwargs) + v_head_kwargs, _, _ = self._split_kwargs(kwargs) + self.is_encoder_decoder = True + + if not self._has_lm_head(): + raise ValueError("The model does not have a language model head, please use a model that has one.") + + self.v_head = ValueHead(self.pretrained_model.config, **v_head_kwargs) + + self._init_weights(**v_head_kwargs) + + def _has_lm_head(self): + # check module names of all modules inside `pretrained_model` to find the language model head + for name, module in self.pretrained_model.named_modules(): + if any(attribute in name for attribute in self.lm_head_namings): + return True + return False + + def post_init(self, state_dict): + r""" + We add the state dictionary of the value head to the state dictionary of the wrapped model + by prepending the key with `v_head.`. This function removes the `v_head.` prefix from the + keys of the value head state dictionary. + """ + for k in list(state_dict.keys()): + if "v_head." in k: + state_dict[k.replace("v_head.", "")] = state_dict.pop(k) + self.v_head.load_state_dict(state_dict, strict=False) + del state_dict + + if hasattr(self.pretrained_model, "hf_device_map"): + if "cpu" in self.pretrained_model.hf_device_map.values() or "disk" in self.pretrained_model.hf_device_map.values(): + raise ValueError("The model is offloaded on CPU or disk - CPU & disk offloading is not supported for ValueHead models.") + + # get the lm_head device + for name, module in self.pretrained_model.named_modules(): + if any(attribute in name for attribute in self.lm_head_namings): + lm_head_device = module.weight.device + break + + # put v_head on the same device as the lm_head to avoid issues + self.v_head = self.v_head.to(lm_head_device) + + def set_device_hook(module, input, outputs): + r""" + A hook that sets the device of the output of the model to the device of the first + parameter of the model. + + Args: + module (`nn.Module`): + The module to which the hook is attached. + input (`tuple`): + The input to the module. + outputs (`tuple`): + The output of the module. + """ + new_output = () + for output in outputs: + if isinstance(output, torch.Tensor): + new_output += (output.to(lm_head_device),) + else: + new_output += (output,) + return new_output + + self.register_forward_hook(set_device_hook) + self.is_sequential_parallel = True + + def state_dict(self, *args, **kwargs): + r""" + Returns the state dictionary of the model. We add the state dictionary of the value head + to the state dictionary of the wrapped model by prepending the key with `v_head.`. + """ + if not self.is_peft_model: + pretrained_model_state_dict = self.pretrained_model.state_dict(*args, **kwargs) + else: + # if it is a peft model, only save the v_head + pretrained_model_state_dict = {} + + v_head_state_dict = self.v_head.state_dict(*args, **kwargs) + for k, v in v_head_state_dict.items(): + pretrained_model_state_dict[f"v_head.{k}"] = v + return pretrained_model_state_dict + + def push_to_hub(self, *args, **kwargs): + setattr(self.pretrained_model, "v_head", self.v_head) + + return self.pretrained_model.push_to_hub(*args, **kwargs) + + def _init_weights(self, **kwargs): + r""" + We initialize the weights of the value head. + """ + initializer_range = kwargs.pop("v_head_initializer_range", 0.2) + # random init by default + init_strategy = kwargs.pop("v_head_init_strategy", None) + if init_strategy is None: + # do nothing + pass + elif init_strategy == "normal": + self.v_head.summary.weight.data.normal_(mean=0.0, std=initializer_range) + self.v_head.summary.bias.data.zero_() + + def forward( + self, + input_ids=None, + past_key_values=None, + attention_mask=None, + **kwargs, + ): + kwargs["past_key_values"] = past_key_values + if self.is_peft_model and self.pretrained_model.active_peft_config.peft_type == "PREFIX_TUNING": + kwargs.pop("past_key_values") + + base_model_output = self.pretrained_model( + input_ids=input_ids, + attention_mask=attention_mask, + output_hidden_states=True, # We force the model to output hidden states + **kwargs, + ) + + last_hidden_state = base_model_output.decoder_hidden_states[-1] + lm_logits = base_model_output.logits + loss = base_model_output.loss + + value = self.v_head(last_hidden_state).squeeze(-1) + + # force upcast in fp32 if logits are in half-precision + if lm_logits.dtype != torch.float32: + lm_logits = lm_logits.float() + + return (lm_logits, loss, value) + + def generate(self, *args, **kwargs): + r""" + We call `generate` on the wrapped model. + """ + return self.pretrained_model.generate(*args, **kwargs) diff --git a/trl/models/utils.py b/trl/models/utils.py new file mode 100644 index 0000000..d9caf75 --- /dev/null +++ b/trl/models/utils.py @@ -0,0 +1,83 @@ +from dataclasses import dataclass +from typing import Literal, Optional, Tuple + +from transformers import PreTrainedModel, PreTrainedTokenizer + + +# TODO: Add Abstract Base Class if more formats are added +@dataclass +class ChatMlSpecialTokens: + """Dataclass for special tokens used in ChatML, including system, user, assistant, bos, eos, and pad tokens.""" + + bos_token: str = "<|im_start|>" + eos_token: str = "<|im_end|>" + pad_token: str = "<|im_end|>" + + @property + def system(self): + return f"{self.bos_token}system" + + @property + def user(self): + return f"{self.bos_token}user" + + @property + def assistant(self): + return f"{self.bos_token}assistant" + + @property + def chat_template(self): + return ( + "{% for message in messages %}" + f"{{{{'{self.bos_token}' + message['role'] + '\n' + message['content'] + '{self.eos_token}' + '\n'}}}}" + "{% endfor %}" + "{% if add_generation_prompt %}" + f"{{{{ '{self.assistant}\n' }}}}" + "{% endif %}" + ) + + +FORMAT_MAPPING = {"chatml": ChatMlSpecialTokens} + + +def setup_chat_format( + model: PreTrainedModel, + tokenizer: PreTrainedTokenizer, + format: Optional[Literal["chatml"]] = "chatml", + resize_to_multiple_of: Optional[int] = None, +) -> Tuple[PreTrainedModel, PreTrainedTokenizer]: + """ + Setup chat format by adding special tokens to the tokenizer, setting the correct format, and extending the embedding layer of the model based on the new special tokens. + + Args: + model (`~transformers.PreTrainedModel`): The model to be modified. + tokenizer (`~transformers.PreTrainedTokenizer`): The tokenizer to be modified. + format (`Optional[Literal["chatml"]]`): The format to be set. Defaults to "chatml". + resize_to_multiple_of (`Optional[int]`): Number to resize the embedding layer to. Defaults to None. + Returns: + model (`~transformers.PreTrainedModel`): The modified model. + tokenizer (`~transformers.PreTrainedTokenizer`): The modified tokenizer. + """ + # check if format available and retrieve + if format not in FORMAT_MAPPING: + raise ValueError(f"Format {format} not available. Please use one of {FORMAT_MAPPING.keys()}") + + chat_format = FORMAT_MAPPING[format]() + + # set special tokens and them + tokenizer.eos_token = chat_format.eos_token + tokenizer.pad_token = chat_format.pad_token + tokenizer.bos_token = chat_format.bos_token + tokenizer.add_special_tokens({"additional_special_tokens": [chat_format.bos_token, chat_format.eos_token]}) + # set chat format for tokenizer + tokenizer.chat_template = chat_format.chat_template + + # resize embedding layer to a multiple of 64, https://x.com/karpathy/status/1621578354024677377 + model.resize_token_embeddings(len(tokenizer), pad_to_multiple_of=resize_to_multiple_of if resize_to_multiple_of is not None else None) + # Make sure to update the generation config to use the new eos & bos token + if getattr(model, "generation_config", None) is not None: + model.generation_config.bos_token_id = tokenizer.bos_token_id + model.generation_config.eos_token_id = tokenizer.eos_token_id + model.generation_config.pad_token_id = tokenizer.pad_token_id + + return model, tokenizer diff --git a/trl/trainer/__init__.py b/trl/trainer/__init__.py new file mode 100644 index 0000000..d354601 --- /dev/null +++ b/trl/trainer/__init__.py @@ -0,0 +1,46 @@ +# flake8: noqa + +# Copyright 2022 The HuggingFace Team. 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. + +# There is a circular import in the PPOTrainer if we let isort sort these +# isort: off +from .utils import ( + AdaptiveKLController, + FixedKLController, + ConstantLengthDataset, + DataCollatorForCompletionOnlyLM, + RunningMoments, + disable_dropout_in_model, + peft_module_casting_to_bf16, +) + +# isort: on + +from ..import_utils import is_diffusers_available +from .base import BaseTrainer +from .ddpo_config import DDPOConfig + + +if is_diffusers_available(): + from .ddpo_trainer import DDPOTrainer + +from .dpo_trainer import DPOTrainer +from .iterative_sft_trainer import IterativeSFTTrainer +from .model_config import ModelConfig +from .ppo_config import PPOConfig +from .ppo_trainer import PPOTrainer +from .reward_config import RewardConfig +from .reward_trainer import RewardTrainer, compute_accuracy +from .sft_trainer import SFTTrainer diff --git a/trl/trainer/__pycache__/__init__.cpython-310.pyc b/trl/trainer/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f189a87dbe3469f3be0d3bef4f68cc9bd705b8da GIT binary patch literal 1032 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 bE4|3Pi*-t4?$jKJY}o%e#5kaL}I z-z&)~s=**JNroc=!#QvU0<;ps;9-Vg7>2+(oCCwK%mTBWVb1X8urmW@9|8=qop^q~ z@4l_7TXrB}{u=4@t@rNw-R;}&eeL1lLIVHHbHBd)U;Z?a_#M8)|E2Kq1pb^&BayHY zmeEL5^siAdpxN&Nc36mMW#@os~P~J>NLcysL7TJQo^wH}9$3BhN#Pdz%L< z2b+f~hnnR|xp`mZzUKXv`Fu|^vYHjh<~HIG-0 z%lla4q2`IoiRQzVhw(mcO*G!se5CS7^WBwqH&0ehHs4cuPxDmel##HHT~DYVMPDWx zk2W8xJl1@?@_6%!$`eRAj+CF2UY@R;wo7>X8F_oM@+5wz8fThkD`$1vPj_YO5*IirH8eZMh%noZikT;!=CfZq>WCdJ@%pS)EOJueUpHwcgT&z2Wtm zRdw6dhTU3rSG+v}A?ocQ^J2|$QJTpMOAkKm?RQn9>eO0ww`*6|uGZADQ(bDSs#{xL zM!s^=%U?hT^>~gf+pg}fKT(iT+VFPwO*qECeY|S72)DJ! zRT}^Il>g%V8{iu|LCgZIVpNirX(gA3jzNv-zC7Tc^ei-lk z3;qvV9c#o++Zj7+=T`G}VJW#Qu`6MXZWA|V4_V{*o50^B{-*4rwF7ylk#`usJMlY$ z-x+(<+J)y)Jnyy()*d{M;dw89GxnH0ZWrvaC39DTe~90Q_zC>($M58F!p{2fci?@> zMhi$Q+0*vU)tS{@67F8z!#viVEP$by>IF4#}Z41^`Ld^#&G2h zjA?1fw2oU3-AGmLv`*LumP|>1*m~EEV&yLDii`$Fg=r~Cd&GJ-%HD0AwBBj`@tbusm{^(0y?+xKCn^H>=v>#B7| zMm>Y^+_`!`>*60qVV^%+qt;pUW#{T)frv8{DeG&jDqwsVv%`6Q z=XU2YPBqNY6jvS~&shuBB3gMD`e+FpLjtwmGwXzv0wlJzgglSPeqXk})>^R-Sas~l zdDO)9AFI(?Mf?eS|58y_sOiTCr=^Z0UoJZI#gHQoXs}s4v#tlCnXhtkPn;2|6rb zqBmBl(RQ5K(lbk?Ryzcm0}&EdzumfnacEzuW1$=_xfmrG99xyH)*FqIrpAD4u$X|$ z>`tv|muhNp1+34a+fjINq#iv>80`hS)CN1$#LQaJ04~^W4P&>|XxH5N!zG_DL(z8W zf!PPQnLR16R9ggNQ>wSPM*KcLU0+@SyprCuF|MVC?KmZ?e#KVHoW#uW>8WZjbR1Xrkd>68N+KA-KE1ezp}2f##dX)6lShx* zs=nB)yNfG!qcK~nA6>N8+q3J|QW@P@Zz~s|*4&b_((W|S0h=AZYTK>S1F~u!m^~5e z0hUnJy|!)xcE?o%vowFesV_I%b?d<3O25Qa#A5BGaGE%;9mk$6J%x3SA&ixl!Sm-o z%zt?P07uO}FfX&Lr|`w+&)3gHU9G8?>sMymYWZkw!8v;D_yZ?qAAI1UV<*abDh{KO z`g$8R$XLCINxpsA2eIgmJ#hTtuq;-_{DCz$EY2Bz`tHY`{a*#cY;fw0GAQw~@eZgR`p*TmqT^lS61f!*jZQCKDnQOP~61EORnMI(?0G|Xi z+h56{d92!ayD?6FzO0cCRwtH>)rY>WuGAM-bTXKFSvro7s|9y(nwqtj>&;FRXn^cK zS0Q7gZ2o>-s9~2JE-eF3w1D9VALfVI*K2i!?H!hghEZ>7Elxmws@5%xWxX|s!RC)V z_`n0(jii)E{eYbESE&w^erW}R7EB`tP@zKtKz*rnt=%a>dcwAq{nNKs`bIQ@5w%ku zszN~PaO-XZ-nOv$-n$#Xv$Wq28-j+y#M6Pp4IDR zJMe?t{+y9qZ{3kL#`!C+m$h_+KE;MV5bX!|dBmvnN&nk*A)Mr3;q`UmtG;H1S%AQf zC3k2((5<}q;$^TT5vR0;$-BHAU{rPz|_m0kohw74=W3A5BWk;zgRv{-aQRUL2xRCcJ@ z^R?!JRXcxiTQI`NifaKpL^oCFmFV)G;Q}H{<6W?yxTzKl-B`isn>?kcxY`4|PMv>yLgH;4evbB^J z(KKcXun`s0>tI*F*7!vvJpvmVN54x@HA|NOJ+TEf)Gscy+l|ZU$2_+`W4@d|0nS!C z8nj4)ylR(P&}4I$uC>}%OJE~p|51sn@w>7iB|=DhyMZ3i7Nfp`IvYQxe=NH=%KBSA}>WUdmAXu)f&!i!9e>E zkYNFkrhuVJe$5zZuvSueDWc&e$FV`W8~BTpL(Y0@3-tzMf1B$Ly5x{+3fO+= zN`wyw!wZ>Xsng)fjWgfE!1eW)DiD|%WZ(vyWh0oJ4plwEGP_tm@4fIrj7GDK;o%;M zYeRgFvD)f1ISh;O4GQY?j`NCg3taU}rK@eV27OqJOa`o+K*EM*OJ}teOmY!rMbjc1 zLpG{!{D?#Hj!TRg2t8&UpYY0S{C~Al|bX0@RpR#!I6DEfRsQ;h{n-Z=nj@! z3XuxwQi2Cp5R%t-w4H%rY&g14=HcZkK^(Ax=~07h(Zu?b;>q<0$2< z0}tE&5PC`2{$N~NZ_oXnw!Hn>719A#=Sh}!GA<$)mmeHrRJrmp!CwqwPv&CjSgrBy*%}`qHHUt)KNB-7X6o(gbE7{ z!8}Q=gqOlPalBz}<4~RBO>9+5h|M!n19+p6NG+gw!?NxI0n{rJC5CVL0B0z#pw~I& zDK8%iDqew9Mx+hL+ZQq7+sFW@A>_5a@hu#zj_TtRy`dPR<X6=g^K}>8vEZ*fC zclpETVf-cW=bT5_+ixX036-rUHuksjZlY(n#&y#fb7B6yZhWKByF=1D5fnSTgFU{rin?F*cY>%BgdbD;4H4CD$F2%e2zHL^a#n ztuDH`>j~!~+Q%snd<9%OdD^Z%<8anqqkRm+)7Bys5=3NkAL8%H_?MzhQy`a$sWdn z&%IG;_509jPFkIk_aBm8<=v@UCfrU68&ka<68|}gAM)di8#{V%qrqtZ0@^L8Uy#({ zFm<|@m(<^Jcl1VAr+et>YQY-0ncT?ta;rPlZ(w|K)typm)Gsv#sAgJ88M&F>F7;RL zF2ZV{H>=~Q&5#&8OWYK4@9ynJxQDe@_x6UE=UWYx=?%3^X*E&Z*V}=#{kJ9^Uy zOTAHqclP!oJRoh|CE?w@tTlcmf%3Dxj5V>A;*-EP$rsF`+NHk9(IphPpr=Nj9#vn?+y1x(C%1oPj9?8(VMiUZw_H?9qqyV zf8G36!&z`2ux4Zy5~~lQ<*VrPvDM?N53QcCcC8_Y#2nx+=4-c~lcPe2ZzA_FYWW0y zk;nOF?_!)aX6?NxYl-pYeRJK5AMvFs zh$ecsMw|)hP0Zq0dJ;U{fK1qcQ;Kyh#<_rZ>sDfQMu_3ds5i_^^=fU~y1zSCcg=?w zx@OR=L1=|*6;ZGThoB~yDkAA?q2+GCuo-v?%~L`GZy)-y%$E-N94|RF3=SDAfud%Snh#aa#%0Z1>@if`Va# zECqN%yNFDqucO5<(~{{^pV{s=&PUX8p6i2BEX@+W(IQ%?(7ubl6e?4h{=m+HPR*Zq z@Yp=9lYSh`ntDwKc0a34KUzcyelb!fkxtta5j)%}D30;VZaHvR44dGOdH ze|vaddj(Xn`;#(1ni-B%2k4+o)U)i=kH$I$NX~+*3!L|2_OPw}QII09*ws#p_A&B-oo358n+$o{kJ0B;=awAn?p)c22WhtK2B$suqPV1nT9`KmEjHWib+ zR09h6R}<=^c#+`s$BrskDAf&Fh&|yFPUCj&6mQFmAS>cL0$d ztdYh?#GmpbUZIYKN=?L~?IoeE@(LfK!SpGm;5nOvPYoS59WTWvHN{yNr6K^5DYS81 z8kVb2=z}taYt8&%$35n`1mV1W5a|{FtJ;A5*ySx-hq~5qPEw}Abw5_ADm0{g(m-laQJufm5 zG6&~5M5GXEj)6>=;04Z$jFKYOQL=)p+kRb14Mhia+P~5#;X8 z7zHC|A*);}$+=(aMcj4=h6bPD4}&~B!{vRnUW{<1g%Xp*}D zQPCVD+;8QQ6UL0$o!ka7FLLb#|1x;sACa*0vj`wRBvy@8bHj9#J##g6-PlO>w4{61 zP2&l2xH{`X4qeT{4Lfl?2?KMEZ>#z1Ml0i|B$=|2>ZL^5JKIZIa7?w*_{*%Jd@s3K zfCQGBN?2J5a}4Dv|0$8y`${C$MXM0RoJzPut3^m-No(k)`8AN^Hvre>@GTto=<&Ei zaZc6I6R)^Zhhu1~>cA-Mctf!_Z(MePufq|06t*4rgqlam*Le74`Imo*|NiLla#z4R^Pqvl3t13CkgABF}2PWxe8a&x4Y*&Mx<5Pd@mZ`t7OrNi#n=6DZld~@Pf zVsr9VVk0Lp&_0DR0?q0av>7=oBa*ymT@aInHpY*!a?ni}?vCq;&FNc|^Yh)$g=T|` zzLk-ZnoutCJ1@?Eo%S>6xb=oJOZq5oJo-@0{AJ(U7`Cj^^PL63p`xXayP!B~aTXN@ z-n$N8$S2Oy&aG)A>xEk@c`XQI5e+TP_YudKx$)eqX|EvuXtI;Mf_ApEt4l4ONorPo ztBzM#=b;v{Awd_t(pg$+*xfrX5rja7j4@Jy@ymJD^m1}ENlq+TwzKF>hx}G`ai#45 zO(6F=bEas^Am2i%!kdZmDVv-m$hG9WRNkqC9JR3Cgs0`f8U=!UrOpj5>_XHhVR*gi zXl%sR74&OIEI#U@CgoXw52#5tB*b(aPu`wDyBk3mG9KQhAVisgr2(M{m-q@W_;cd(qF{bc0W=xn9@|Q6SX7`R5TH0JAaoeQ1q@ z6DrnWpbMOqU&@!P0NK_lEjrkncnlN@;{i zcN*%|ofx5^n?k;8JngnECX1w)zk6IJ+NS9Z zk@R-H3#vJ@;n_IR@%^$Lg&%1deveX)Q-~& z9yUC-y15fb&oY4!$cG6s{{B?~UWDP6zO*obZ9t&t|ATHyYn@PwF>1FU) z8(F;hCGnO+X;{7x!s@n`R8P1-dPJ_eR#>Y1yjH50XA8)45u8-^5uh;Levl&YP)_l4 zZWMGK2n$ko3w3KNo4}1)`d|*JQ~d-Tn^d+`^XA3H1LBxx{gESM^7Ao@UFV! zbf`e#)<5##iFcnk@yNr+PP{8bmFFO=PmtRX(*W%6G8m4RueWfPt6I05Id25w3W~$W ztJUSugDi#RlaAH528Gm7-J-=Y#E`jTT>+6{A7-HGMdePa8Hg4S?~ z!dVOK1>06rPI+U#eISI_+u_IbL#_S=T2w!Vpqv#2la~Z6p4nRWh9E;ezC8seg$Ql`)+N{i{EEl^uReKQxscQd&10 z%dIO7OM80*{}4SdoDV9F6ldmT_2cZ4uvH;%J2IY^_s?kewmkFBm$a6{)yZK z^crBUk|W76YEUv$(434xXTdlAnxY?}jwADC_s$sV*}mpJ>@$rPLZ<7}sYC+a0n}X> zf@Oez1hW`|b;wS^-s-&0-R+w2EhxEQs$A$Kt{dN-_#VxgCBS;6Gy}DfwX#<3Mgq)H z=2qe??8Cr8MlUU8A4AzZ%Hoi4RCW`Z4akY+Cy3jCz0k%*Sfu$Qdm3YZ<3)ZkT zas&DjU6)@An8E8X6l@HGJw#nIY;UADY>lp^obT=7`iixb`ZORN^KH}V)!}7eRzk27 zbz7(#qbISt-kk~l#$p9p#Nzi9j?znM96FcM<2nWYJ+{43zz6HO_zQdl zPZQRZ*z4bA?Lgj$cE+gEfQn@t}TzXq>IgB>LKDqrR#fL*9k8n`0@=_4vn( zCBxbsi<#)*QWEyehjHB6BeOZR%(3&=A@p;IXRavjn@r@J=qo$XSJq#*D2sT%tRE+3 z=|hpV_I@gJ>0a8}kI~p+?YHi@nfipRR#4%19Q8ij+l-sk`8Kk8&4C{%Vg1R9!SkKyVU|6_ zGuL>Qs|?RrqtFW#;iqD%=iF&LAAtV{o}o3}$#UrJT{o$vyqh^Ct`x@IgE&B2zPEP9 z1Ya6i!)ZN)yR?GuxbJrfBpBGv7XyYjrl~ni4_t8hqG}XBLaW_6=+fIB;(@q;U$}OO zGDiti24_#4(ly(C4;>Cy$(*mi(1Zp_BqJZWkVHeTf+N{FS<@hJAzJvR2Spea$lP=& z&_0Ewx^tAWf!l7w7K*c&rE_%!lth^#_y-vLHsdY4hrY*G?Sm+cyGbDD1XW1y`U+@B zaHLdgW@5wgf(_Rarycl#uzO-xb@TyIybhF&AV)KD%97B-NmX+Vz89t2f0|ra3mmod zeOdji{+mBIG~_Yg?b@R-=dSdP%T|BJQV6zRt7erV$7S_XpnlL=i7v~_1tZc8rYJh` z41o&fK5>ayhK=)_`sXOwr%okH5Avxk%X{$vm$}5{0=6R5qHswQ;?*<1-F(TUwYdTf zmAFYLassa=J#+Q}&sg(}D-xjts)D`3DKa(GTRk>`WiRIAy@~GqF*+ac4pr=cmo;d8^D`6C`OYo#W=fJ*HaT%YubRXoeGgC&ee zfTwLOoQH)*HW*@@`r!=$xShb6)$ClzrdA5kp;A~I$X@Ck58|o2>w_&GZbALwL02(L zpDZD7SV!OJVg!UYqoDlxyAjBe8li#+Sl3@tyFlZ6;MR5~^wLs4i;Oa^ubMK_>gN#w zbM~-a=DxfTEim`^`B5%Wp@aJ?-vwhugv&>Zb^YML`&ZiVJNxaC4oZ$D;gzQ*}J*n^;^7@Td9oJ_e67cs~P&!GA+g zp4N)x>=G`*f=ny+CNC{o7(sICXPEV0A)v8Z5RWwLRHpn_py61@_xK&(HgR7#V%sIl zdQ-oErYfT}LvUyLOQTW@BhXxBnj{YT_JI8PNB%0qz69n&IPJp{9IX#3gO$NwJOe41 zhW46M$#F8OYxXrSqid2ikKOjm9E<V@v-35qW6Gkajgx))6Oed#dkQV*7F_A;F+$-?4=-< z;h6%3WqgPxW2T%4Q*!I~TTn0=i5HNb0vWuXWX|Weyg!(5p16v9{QnXBF%L||FPza= zU_gH$cj^h<79NWhINq-pGf-*&ory^qYV_RP>A@!b>2_XpC@*uM4W_M3)>IO@Srn1jALJ_@yKT#6?E*>!U2?Z4% z&9tkjmf^y(;TjYGKKWtHkBJ$4MCK_gmXk+d#mFITl5v}Pl!ndWmif`t1{@AH(i`dc z%(Zfur(^MXf|I^3m3^C16Daj;yi~kS2p_2>a6a3`pzhe$nN)R`NvxZ9XdrA|%79J!gHd+DI~(gU)CLqbgcwFSXdQYCzDYy>FPW6o-R%#J+Oq&vWC&Kh2Gg z;jepdY@rbdjAqOx7+OEoZ=!k+cYo0myGVbC;2+l=@rSIvp8__4iU`glY6^!lu!DC2 z7f?-vXQ-t0GgJnd)~KYzg(Xafia1K|K_PVc5jX>6H*q-RmZ2`gc?O~)RM@cI!C~ey zEMnO!3Fjo*db9`SO^a5xjFm&)r+P`evn&+Fct4M$8dUCNt&BBv%Y=;$mO9o2Cxe_- zyqSbbIS=*DJ<{q^eyhVM`PqS1KkK*JL8~thwED_Gs~-zn9rjyIt>)Ph`Y_Nw?)BpV z5EJ_@WsO3_)$q)tm7l}N;ZDCz+>;0_8p~l#K;YF1oULh7lE#}9$Dt@lThsEsGko7E z?=#{14Bpegw!3N{!(sx$4HT#`Ijf&t7CxB{<6wWo4W-55n9R7u5)t z)#rn`_UU*);SQwPDIN5wd&rd^6eu9b`ouj-h|8FPSdKmItl$!@(xJ%YbQn&a`YJY` zqbNrrAEoH6zV|0faHBJxy95*tIUd+%YzZb>c0Z7h{UFT1y7Z+!NpLS*t!lhFH7Ft zD@3(=qt$+my*_+djKcsaD9?GK`NbA)7{-*ORd2p(s6QnnBD3vf&C~I6Q7apW*_X+W9Fkw(yN<^sad{z6n3s!+tUes-xR||ym<$~1IMf0G z`wSB^mqZuk<=~JaXgit`<$R2NV@HI*_;#(p?`?grONmo;%)Blijl zMSi$H7S>goGQMP{Qg5U(h1bnjQn|h0md7$MbkM~uZd2`pOyenUQeV*5=a~f4lHA${ zg6CrsaYKqIZXcl=1=xA~38DK55ISlwW=QPFY)z8cvJB|u)9{Q-ucly)O@hq9O4>^y zSEiTVWX=p3X_U#t-jLSIAdh&ySxI1j7Klb8bO&?!S8U`%8D zR05@Qtra|Htu)Gi1k7Gh$`qJu2KkWwVM(XA=qhxsDUdQSdQ*u#NVl?+tl_WuWIpK4C2gaKmPcXrfr|@k?+wu!LO9Vz6r1lA zgIJJ0u$pKajxTgxS0cxQ1g0INDHhwgMC6UkWJfVaO2b_2Cn~{=)=YIL;Wh+)DMfS$!vQ0)Ox( zi9pRM%QD8X3P#mBt3Vva^%Bn$8=UN;ZRLI%vfc9jo3_*2+@d8;d4cFi1y7pL-K3U8GdCJA) zlt;D7Aj69BC5M<-Ql+?Fr*o{_@Z?m6v9_aE=uG8)H2!iGRr3D)z5je=C|p2z2^goS zqKI({cr|~xf z|8#mO@Qg>woHjw^LCQ04Oyur0?}gH=?^H!ls~Y1_2q!m&GlmL$6Z?)6HtmHz%A7^Z zRDlR(rVbbs0ivg6isjZ=rEk&*oI^EDNjYVDFj&SnnV^9PdBydn3D--|Qa@hG>Sta& zzCl`_TuW}G;Xe-jI-X6OnSg2qijOpvTNX}lK-h$!YGrRGDQJS?W^e<+MTnT`DX3SN z3p{}Q?tvXB9uJz7t35eDlh9RKnqv11`P4yQFRInlF`n9wmKE`~vh%UaaL9oY`p3dqb z+N|Q}OKNJq*n>gXDpHBdq`vG0PLkWu#X+F^0UcajC+X>VNiLDmMea@-bumuZ|*f*qw`kx{pV$o`@E45vOww&tj`ea z%95a$srIYwcT1BVXy-N|PKu-lCW61jkC(1PoP8Op<1gY(8+d@_L1}z(Odl8UhS0C7 z(_X?CVP5v~ot6XLgWbjVQTzwxvCeFS?9>jTJrT$#n)&zmaBWpa=}0T~2qMCDXAmKe z((hGByH4K&l;Y5bYif#kcLrh=&a)*Cn0y{FyZrWv?!t8 zfI?wv1PTR`+)Mz4;(!~_W>VsaRvDLqNI>)o7t~FBX9OXHqg!P9AsR6Ts1iPwVJ`#Jn3|ha(;SmKWu$nN1>TFxopuL=r`7Vt&M45Jr&=yP(sJ!Q$x-S` z-M)%rmay6=q1NOjFvg;okElw~S*hd0G8jtWK`(d{WaZ^Z*Q!hW5D%XQN4m(7cBHW5 zoaQi(p^4o>;$RDcEr!;aJ_}5|MBnI+pk%1M|82aGeDUI%RSI|f75mF-+A?mYWrQ)Z zIBrG4lp-*5<27;6ej;!umudEAH0XMuDO= zS6H(N^?w-rHG)dN%12`z^*WQ^VDKh_bCAh2G6 zIpICX2FD4(8zC@rP#UGVYr+`y1goQYOY^aUw`6;K260|ijw~HNrtn2CeWV%(A!WxL zXOZ7zfX|L4Dg_#Y^~hHWJSXIDphC;OTGidE{>@gmZFlOGgDT$rb7S!^N?)Pr1 z*`m)Jz8eB0<{w@Rh4pQ*txlCwpfoVpjvL{;Gf$tMd+$>xOApS%vW|}$z`{-%8FGfl z5D%lgW`Kg`!qiB;Iw$;8pEm;biulRVsmb{;0vA+}+=IGLo|1g}xUBt8B*YCDqS}p| zo0e4Ax33%Gkc*4xP!9I!k0)*c#o-MH>bW~^!g>7TJldP?_f=!VY5YmUWSPWQEz2A2 zHd%=g^HjsW7mO(vKj4z``Wn8wr+yBF6iu`+7vK|&KEe`Q_3se-^TbtbWjfKla2vi0 z9EU$Sk)IwBsekfRYa*hgQ;}O6+DoXvKmdMJU>m}HhW^bKmRAJdAAJaL1aHwvbW4q* zu$RUGWZYjdLM{bIXmR9#+!Ic&hRuue0{vv)yipAj(4(&&5$ud_HTqsN<54+3$Ir6= zML-BG02KEl_@7F4PkkjLA&A#Y%ke4opHX!eiWm}a-mnusV|jLqe!SbpIqEKGkm%Ho zYRc4wqC>w2w5bdAh>pP-KHRRo2pdcnq)Ep$YYNwXINcGUXh*|BXaqcYP${YqRSjao z9B3$r(@lfIl8(ugsXyk^LkwPHAXFFM<;1~O1}7MIh)?h0(`OlcngOQ|2d4)pH9nyU zlJ2i26>65?(4}?45m5&3TbbU*5Q6v^_5BVu#RGqpNysp`g$Pq0C~9m+O%O1+kAORf zVk_fe)__&6$Y=GX^KUrTWVQMBkg{@(iSuXt(@5uC0#)xKyE# zH@~1=A9OgD;vFNrBLNIK?f^00%9zkSeUbda-Y>$8G!&0RnOFAyMQS{#q3~ud17;p& zbID>7+N;3r3u~CCYjBtu|LNE@4cTh9x!agdPvZ- zpOAz3&q#hC$zD>Oq9uxsIN}VDnyZl)4BSGvhhQDo9Y4O+h5skjaoKPQ5ji z&Os@=_pOl>uh1EgT;nDCBv&rgkk7@3c=d8kyG9EK!036lN_jPjOP`D-CX}5{r<& z_=}XuzdJ&&Gk)55(pjso51b^0twPqctl!UY5%(`_FBjafsU4HgGRsF83{bYa5eE-v zelg4chD13xK*TCDL0yB(yfW+)EDfxB5^FEC=Ic1WQmYcXZlphDaEFXXh*}SglSVI{ zrvWIphd$uV?Lrwdm)BH9OPD$~_r~70%vbQ~T;vGyfu6iJaR)EHfD^FU-McNn<2UEQ z+d4!=+@@6?KJbRLVw=9oPM_Ul2#|66VKc)IF4%rCYQb{=*gSjS#0EAQPIJ3(Qqq7T z3D;R%PksWH-0F3w3!i zzD2gAejuKT&47zCV1Y^B)U;bu1n2HbD1ye-KY=ici4k zT#+_dDkFI;2>7rV8Ee#2M$4G0$D>Xve{U z{WOo#3!n4rp&IBpebWxL@4Se>Jp#`Zq6U5Q1r$o{b!zEl^a34{NE+hKB78%nk-8AB z;iCg}z}pv>Xizf14IqK7%gNZ5Fa-i;QKdDX#vVJZHb1=Lf1<$6aM+v z2o4~cpU1RRa`+~PI4j_kTIPbBsKT#E-rf8~TyOp?zXZ{T>9~L?{ymRBxmrdPP}9(^ z0@Hzu#%RK09+wQzw#KC#e_IX?Qp$2R&x|`cFN%PB?xqK!&_xEyi-+6%<8=?NL+?HKibP)TJB!= z(wFevZCm{hHhhJ(isco?9^4LfjRUZ*F9(WW+{*6fP$67yru=uGk=0$T6yve#9BQGf@{%vZg{|?~I4TFy<{bI&!nLAOhUqtUp{9 z+n!sUE!hZe1v?8`^&*s@hf8AF+VX+Q68_QYO|ZI{1W_>B%SxzUXyAc#BJ@Eh#b>z4 zs0bCx>!o;_Q&yMao({-Iof$|z)FlQ&O?oUT6ht(`%>MT|#YF34`DxhYVWBs=Be87= zs9qm^it2lO`OY61IBVe&pgKr3t_R}9!#R8udT|Y7)<+ppvuL#`DF|icp&Q?!2utlS}q-c+V21^0l<-I7M4j~vBUcNtiMm=%h$p86|iY|>;fMk_&Rhw zx*{{c%i z<4s5+BQ>XZTzroI6iV}Ok3Hh!u6Qn1tNhfJpOURst@a`=fnG-;MPs>x8{LKmI0P zhVqK>Z_j$U^KuRT`HSi;l)?9~@JUCths70xI~ZJJFv;Kt8T<%?pJwp842b-+y8Zj` z1RoIG<%_S~%RPKv;XF)UQoICq9vcRy<|CX{K>lFk`-MX3Z~Ta^_!W^5*}+J=uR{4w-+MDVl#_4x69P zjF^8uJ!<~%%$WISBje_u7ADL;nVK~JcxKA{qs$KT4>Qx|A7pl#uVJr4(WzO1zmu>;#X9oYrH5 z10yKM96>quFokltdmbA$Po>_RLP^|F{-(K>db5apd@rR7Z`?g&9#6dy#Bgj1j6H-? zD^!}k9>9{3lwj0GzOeuP+}M4F;%t@Ad0E(ssGU~xoOv6OyrB$Vkr9=%qwlR3pC*W2 zjrxLKlSCFcY6_1gefxA?zO#w-?Z~>pq4jIH)pl9QI+G0~`$=|@Y&qGTvXx|u{s8Jz zKf!>s%ggC2Gg~d$ww1}?bvF2%XRYBCqGwg$3N3_Rfw0vmYZ;3oX65!xHO3akr3Ihu zgKbE%>K39qI%Q9K#W+@%Zz2eq&=^IKgKQAukIKGy-r$mC_`~SWU<>Z>c5jm*UZK`SgOKVnUS}Se)aJ z;i+<0A0)tfwszuf;g;y&3j+Fs3|uEMwmf7G@L>>*hp9_RIHwTgcq={lgE9OSjYHy3 f&0D^Mn36%S7T)@{*U03jj7*aE?eh-%spS6!d4R8< literal 0 HcmV?d00001 diff --git a/trl/trainer/__pycache__/iterative_sft_trainer.cpython-310.pyc b/trl/trainer/__pycache__/iterative_sft_trainer.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7c8b58fe8053d1038b46729cd1dfa63cbee39fb5 GIT binary patch literal 12443 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 literal 0 HcmV?d00001 diff --git a/trl/trainer/__pycache__/model_config.cpython-310.pyc b/trl/trainer/__pycache__/model_config.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d6ff7317bd49546248e1df306021af9e0a3d915b GIT binary patch literal 2864 zcmbtWOLN>r5SFyshu3Szu^q=I2^n6AH^gfmE-F<7LI?>(RpL+(pxA;M&uV6Ohe#SR znzgaYxsY7>0Z=y>9Cv*zvl7o#D)|C}w$X>q9AG4!rQD%#Cf`&WuzkiysCk>f6pR zX2yt=av7Nj^$%ZlSiA*y{;ThK!t49o>jxi!r~R4;d4N7ipVz+$`gPF|%{5Q7c;m7M zSQD^Th0Ta2@Mj!9;O&veXMgs2=lj5Ewx^nNPGTN3k5!tpq63->m1d0}ulRFQoyDon zQiaVson^;A0k&2u{xQ&5a2}p?*eYOa6?S}zJ>{@dfSs{rxt-%mnFfYGmn@3+>eWe@d8JJVsjK)ev zBDb^7d+Ij6QJlY{C%GY7B+3ZL1%$dc7O5+zOh=G7my(`6Zcvbxf)oXd-#&4@;Z6xG zAdyKH3v3%#&#DHmlYKQISO+~x$;JkVr}U`g8*tR*V>&fNU%3dqQMF#gppIxFK}Thp z8%R!>+=QhQm#M)%8c)&TolIs^DQDO12pgwrV=$31-;lY2qYWJ1u}lBqO|kyTSU_!D zTVoTwfBnWC?37fM?Q1l2AMDk$Er4kzhUVb{i7 z9me8p%zC!%5Vsh8_bpz(T&c9VR+*ZZp6xi9YFR8hb;I;X7%a&o7J1JuIhm46D{et< zxe1NgKFoqELORq!6oQ;1&UL%&)GB#u6wTSLDBi$WFOdNZL#AP<+#KP!VxJDM3ZMa_ zzd`ntzMB)3LXYlAGbZR8`pY<#DZK<1N@H99-`Exyv$b@%Ra_{YDo63l2}@0NZV;S9 zvOdhVT#<%bUAr_$2NYGQ6Qn3noW%H2ksBDP!}@j^tB4vUSn5RZU7KF`J;wVQ-1#~X z>D}`Wyf3^T{hz#dyw6*o1^0q`wS(G$e-K>uzHFM{z`yUq>FRHHYqmw*N0d@qmqsMH zZh{FM-dw~(I}7E`CdP@_+Fh}4>#l)*@$?0d`}4}CR?2MR$Ze`jq`NWOh-0?PHgDc! zA79PKjK%=`xcPUU-a94%P*E(tCS~yuaY_v zI3@xA2VCH%;RS=%ZJ@sHn1Dl&U&E*WL-;9yV1w=A(x>IHpEvIOE!zn{7XR!8W?Wz8 za2^vQ6Va{dvtVBnAh!K6PSAC&mG1ib9P%+0C3z7g<|xe+oJaTxq4YYi@&=F&MAiDM zSUVio5y|cutcLrTP}`=}&_HE66zd*WNE0fiB1&})x9uRoKOS4ljkd=qWhop6?hMI6 zXddxpBqvZP94Hm*6@E0GI&w2L@f`%VHT70zH4E%qiPoy7fJ$|lKR#qOZ zhO7SRVAXF0orc?UyE|iNr$3V-*VrX4Tebny2B%{gmdjcgDJ@*nJKg^Z#I`O&+rp{# TCbnGO2D0ES_?;lAE&G21%m_Q% literal 0 HcmV?d00001 diff --git a/trl/trainer/__pycache__/ppo_config.cpython-310.pyc b/trl/trainer/__pycache__/ppo_config.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..145eb53be1048375b6279ebe44455d2d4aba5d49 GIT binary patch literal 4439 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

c0n()l?$g0m*Em;@Dw)rh-rtFnF`4)_+*Ba*VW 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 GXa8?b&G&== literal 0 HcmV?d00001 diff --git a/trl/trainer/__pycache__/reward_config.cpython-310.pyc b/trl/trainer/__pycache__/reward_config.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5558de9192b1a8e8907336ea77d309d9b90ec4c0 GIT binary patch literal 1225 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 _)W+L ze};-@Q6yRzCbc|j$AxSS7{LAnk>@6Qz?=|MZqaC|{#Rw41Tk+MeY9`iq>?w6@Y zr#4spqa~)*4WkChH}HiP{z}vJkG1mnn`v0;oTfv2=xctW>j#TP zy%-0Z%)pEg2N6w5L9(OJ-%si=q&Q{RJ8VDhk#^*tDk{kDrAgp_LuIHOL$V*LhuRNu zdymBDqEMkh(48ZV&x;b(w{=(BRE|nNQbIg`q=+(Uz+X&5x0LOQ)S{BBzgzl_c33_% z?gNgF?@S0CHhZXrOA2)^-B!8Pocbf02+b!Jl({lo2%Eyz#3evyOPf%h?noLkiyDHa)+H0WiEzMP4QQlKmq#!^8w9eCsx|688@2UI^OdS)D>T{bc#O)Tp`AY&! zR!#ffRpsF6;(m|qFpdQM&U1c*6MB5JGn&q4Fcncp$@KzGh_|Pc37Z@Y`IpRiR<@j& zylyJczl~A+J1CN}Jl`gjFp`lff1MhQ^gjXUO#i0f2^PeBT8@V+@9pr{s9uSq4S^6p zQCE{PZYrWUDaTt*pS7!}l!tdQK4rik(ad^wjX|=@U!)cT|14mBrbX*CT58e6De`}Y zo|t@atwL@HZVQfSPQ45#Q-gC_gk!o~g0q6_I>`QxkUNx9klM|a{KKyTAYR()UH-?Y zwjZH5fG+H8JE__92L$9QP;b~2{QFr?#kSq3XWN|qLc{5Dg};I#F1OKdQRMTO05-GWqQ`t6cuQj?i*M0rVmQBo;yQlucTt!M{Q7LsQ_DAab?PH^?eKcoi9~u?yzgwpE%gL(tU$vU{pEGssgZT;V7sjOaAI4Pc=gOaG zQ~Jj=a|Sc5a_uASy8coA4%7E>4Un!H(;rG)+E4M{A-u?VCUx>{GCX4OSX5^qM1>bi zX_hP{Hm59=n;=;#cS-WqOmjM6nC_QJP!T4_a*L7%f~nCz1JbRNdn-3=f&f!6+`TeB ztb-Q{!0bGECbu#3;!$4x|eJ literal 0 HcmV?d00001 diff --git a/trl/trainer/__pycache__/sft_trainer.cpython-310.pyc b/trl/trainer/__pycache__/sft_trainer.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2f91e1bb1d02a34ebdb93a58fc8e739bee5bf4ec GIT binary patch literal 16694 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 U{C8HZR`?Ee$5g#Mug>QF9<7Ycng9R* literal 0 HcmV?d00001 diff --git a/trl/trainer/__pycache__/utils.cpython-310.pyc b/trl/trainer/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5101e0c122c5161ca192f2123380ba200a394a53 GIT binary patch literal 23812 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 literal 0 HcmV?d00001 diff --git a/trl/trainer/base.py b/trl/trainer/base.py new file mode 100644 index 0000000..f0314cb --- /dev/null +++ b/trl/trainer/base.py @@ -0,0 +1,46 @@ +# Copyright 2022 The HuggingFace Team. 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. + +from huggingface_hub import PyTorchModelHubMixin + + +class BaseTrainer(PyTorchModelHubMixin): + r""" + Base class for all trainers - this base class implements the basic functions that we + need for a trainer. + + The trainer needs to have the following functions: + - step: takes in a batch of data and performs a step of training + - loss: takes in a batch of data and returns the loss + - compute_rewards: takes in a batch of data and returns the rewards + - _build_models_and_tokenizer: builds the models and tokenizer + - _build_dataset: builds the dataset + Each user is expected to implement their own trainer class that inherits from this base + if they want to use a new training algorithm. + """ + + def __init__(self, config): + self.config = config + + def step(self, *args): + raise NotImplementedError("Not implemented") + + def loss(self, *args): + raise NotImplementedError("Not implemented") + + def compute_rewards(self, *args): + raise NotImplementedError("Not implemented") + + def _save_pretrained(self, save_directory): + raise NotImplementedError("Not implemented") diff --git a/trl/trainer/ddpo_config.py b/trl/trainer/ddpo_config.py new file mode 100644 index 0000000..2c91095 --- /dev/null +++ b/trl/trainer/ddpo_config.py @@ -0,0 +1,115 @@ +import os +import sys +import warnings +from dataclasses import dataclass, field +from typing import Literal, Optional + +from ..core import flatten_dict +from ..import_utils import is_bitsandbytes_available, is_torchvision_available + + +@dataclass +class DDPOConfig: + """ + Configuration class for DDPOTrainer + """ + + # common parameters + exp_name: str = os.path.basename(sys.argv[0])[: -len(".py")] + """the name of this experiment (by default is the file name without the extension name)""" + run_name: Optional[str] = "" + """Run name for wandb logging and checkpoint saving.""" + seed: int = 0 + """Seed value for random generations""" + log_with: Optional[Literal["wandb", "tensorboard"]] = None + """Log with either 'wandb' or 'tensorboard', check https://huggingface.co/docs/accelerate/usage_guides/tracking for more details""" + tracker_kwargs: dict = field(default_factory=dict) + """Keyword arguments for the tracker (e.g. wandb_project)""" + accelerator_kwargs: dict = field(default_factory=dict) + """Keyword arguments for the accelerator""" + project_kwargs: dict = field(default_factory=dict) + """Keyword arguments for the accelerator project config (e.g. `logging_dir`)""" + tracker_project_name: str = "trl" + """Name of project to use for tracking""" + logdir: str = "logs" + """Top-level logging directory for checkpoint saving.""" + + # hyperparameters + num_epochs: int = 100 + """Number of epochs to train.""" + save_freq: int = 1 + """Number of epochs between saving model checkpoints.""" + num_checkpoint_limit: int = 5 + """Number of checkpoints to keep before overwriting old ones.""" + mixed_precision: str = "fp16" + """Mixed precision training.""" + allow_tf32: bool = True + """Allow tf32 on Ampere GPUs.""" + resume_from: Optional[str] = "" + """Resume training from a checkpoint.""" + sample_num_steps: int = 50 + """Number of sampler inference steps.""" + sample_eta: float = 1.0 + """Eta parameter for the DDIM sampler.""" + sample_guidance_scale: float = 5.0 + """Classifier-free guidance weight.""" + sample_batch_size: int = 1 + """Batch size (per GPU!) to use for sampling.""" + sample_num_batches_per_epoch: int = 2 + """Number of batches to sample per epoch.""" + train_batch_size: int = 1 + """Batch size (per GPU!) to use for training.""" + train_use_8bit_adam: bool = False + """Whether to use the 8bit Adam optimizer from bitsandbytes.""" + train_learning_rate: float = 3e-4 + """Learning rate.""" + train_adam_beta1: float = 0.9 + """Adam beta1.""" + train_adam_beta2: float = 0.999 + """Adam beta2.""" + train_adam_weight_decay: float = 1e-4 + """Adam weight decay.""" + train_adam_epsilon: float = 1e-8 + """Adam epsilon.""" + train_gradient_accumulation_steps: int = 1 + """Number of gradient accumulation steps.""" + train_max_grad_norm: float = 1.0 + """Maximum gradient norm for gradient clipping.""" + train_num_inner_epochs: int = 1 + """Number of inner epochs per outer epoch.""" + train_cfg: bool = True + """Whether or not to use classifier-free guidance during training.""" + train_adv_clip_max: float = 5 + """Clip advantages to the range.""" + train_clip_range: float = 1e-4 + """The PPO clip range.""" + train_timestep_fraction: float = 1.0 + """The fraction of timesteps to train on.""" + per_prompt_stat_tracking: bool = False + """Whether to track statistics for each prompt separately.""" + per_prompt_stat_tracking_buffer_size: int = 16 + """Number of reward values to store in the buffer for each prompt.""" + per_prompt_stat_tracking_min_count: int = 16 + """The minimum number of reward values to store in the buffer.""" + async_reward_computation: bool = False + """Whether to compute rewards asynchronously.""" + max_workers: int = 2 + """The maximum number of workers to use for async reward computation.""" + negative_prompts: Optional[str] = "" + """Comma-separated list of prompts to use as negative examples.""" + + def to_dict(self): + output_dict = {} + for key, value in self.__dict__.items(): + output_dict[key] = value + return flatten_dict(output_dict) + + def __post_init__(self): + if self.log_with not in ["wandb", "tensorboard"]: + warnings.warn(("Accelerator tracking only supports image logging if `log_with` is set to 'wandb' or 'tensorboard'.")) + + if self.log_with == "wandb" and not is_torchvision_available(): + warnings.warn("Wandb image logging requires torchvision to be installed") + + if self.train_use_8bit_adam and not is_bitsandbytes_available(): + raise ImportError("You need to install bitsandbytes to use 8bit Adam. " "You can install it with `pip install bitsandbytes`.") diff --git a/trl/trainer/ddpo_trainer.py b/trl/trainer/ddpo_trainer.py new file mode 100644 index 0000000..f1f897a --- /dev/null +++ b/trl/trainer/ddpo_trainer.py @@ -0,0 +1,604 @@ +# Copyright 2023 DDPO-pytorch authors (Kevin Black), metric-space, The HuggingFace Team. 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. + +import os +import warnings +from collections import defaultdict +from concurrent import futures +from typing import Any, Callable, Optional, Tuple +from warnings import warn + +import torch +from accelerate import Accelerator +from accelerate.logging import get_logger +from accelerate.utils import ProjectConfiguration, set_seed +from huggingface_hub import whoami + +from ..models import DDPOStableDiffusionPipeline +from . import BaseTrainer, DDPOConfig +from .utils import PerPromptStatTracker + + +logger = get_logger(__name__) + + +MODEL_CARD_TEMPLATE = """--- +license: apache-2.0 +tags: +- trl +- ddpo +- diffusers +- reinforcement-learning +- text-to-image +- stable-diffusion +--- + +# {model_name} + +This is a diffusion model that has been fine-tuned with reinforcement learning to + guide the model outputs according to a value, function, or human feedback. The model can be used for image generation conditioned with text. + +""" + + +class DDPOTrainer(BaseTrainer): + """ + The DDPOTrainer uses Deep Diffusion Policy Optimization to optimise diffusion models. + Note, this trainer is heavily inspired by the work here: https://github.com/kvablack/ddpo-pytorch + As of now only Stable Diffusion based pipelines are supported + + Attributes: + **config** (`DDPOConfig`) -- Configuration object for DDPOTrainer. Check the documentation of `PPOConfig` for more + details. + **reward_function** (Callable[[torch.Tensor, Tuple[str], Tuple[Any]], torch.Tensor]) -- Reward function to be used + **prompt_function** (Callable[[], Tuple[str, Any]]) -- Function to generate prompts to guide model + **sd_pipeline** (`DDPOStableDiffusionPipeline`) -- Stable Diffusion pipeline to be used for training. + **image_samples_hook** (Optional[Callable[[Any, Any, Any], Any]]) -- Hook to be called to log images + """ + + _tag_names = ["trl", "ddpo"] + + def __init__( + self, + config: DDPOConfig, + reward_function: Callable[[torch.Tensor, Tuple[str], Tuple[Any]], torch.Tensor], + prompt_function: Callable[[], Tuple[str, Any]], + sd_pipeline: DDPOStableDiffusionPipeline, + image_samples_hook: Optional[Callable[[Any, Any, Any], Any]] = None, + ): + if image_samples_hook is None: + warn("No image_samples_hook provided; no images will be logged") + + self.prompt_fn = prompt_function + self.reward_fn = reward_function + self.config = config + self.image_samples_callback = image_samples_hook + + accelerator_project_config = ProjectConfiguration(**self.config.project_kwargs) + + if self.config.resume_from: + self.config.resume_from = os.path.normpath(os.path.expanduser(self.config.resume_from)) + if "checkpoint_" not in os.path.basename(self.config.resume_from): + # get the most recent checkpoint in this directory + checkpoints = list( + filter( + lambda x: "checkpoint_" in x, + os.listdir(self.config.resume_from), + ) + ) + if len(checkpoints) == 0: + raise ValueError(f"No checkpoints found in {self.config.resume_from}") + checkpoint_numbers = sorted([int(x.split("_")[-1]) for x in checkpoints]) + self.config.resume_from = os.path.join( + self.config.resume_from, + f"checkpoint_{checkpoint_numbers[-1]}", + ) + + accelerator_project_config.iteration = checkpoint_numbers[-1] + 1 + + # number of timesteps within each trajectory to train on + self.num_train_timesteps = int(self.config.sample_num_steps * self.config.train_timestep_fraction) + + self.accelerator = Accelerator( + log_with=self.config.log_with, + mixed_precision=self.config.mixed_precision, + project_config=accelerator_project_config, + # we always accumulate gradients across timesteps; we want config.train.gradient_accumulation_steps to be the + # number of *samples* we accumulate across, so we need to multiply by the number of training timesteps to get + # the total number of optimizer steps to accumulate across. + gradient_accumulation_steps=self.config.train_gradient_accumulation_steps * self.num_train_timesteps, + **self.config.accelerator_kwargs, + ) + + is_okay, message = self._config_check() + if not is_okay: + raise ValueError(message) + + is_using_tensorboard = config.log_with is not None and config.log_with == "tensorboard" + + if self.accelerator.is_main_process: + self.accelerator.init_trackers( + self.config.tracker_project_name, + config=dict(ddpo_trainer_config=config.to_dict()) if not is_using_tensorboard else config.to_dict(), + init_kwargs=self.config.tracker_kwargs, + ) + + logger.info(f"\n{config}") + + set_seed(self.config.seed, device_specific=True) + + self.sd_pipeline = sd_pipeline + + self.sd_pipeline.set_progress_bar_config( + position=1, + disable=not self.accelerator.is_local_main_process, + leave=False, + desc="Timestep", + dynamic_ncols=True, + ) + + # For mixed precision training we cast all non-trainable weights (vae, non-lora text_encoder and non-lora unet) to half-precision + # as these weights are only used for inference, keeping weights in full precision is not required. + if self.accelerator.mixed_precision == "fp16": + inference_dtype = torch.float16 + elif self.accelerator.mixed_precision == "bf16": + inference_dtype = torch.bfloat16 + else: + inference_dtype = torch.float32 + + self.sd_pipeline.vae.to(self.accelerator.device, dtype=inference_dtype) + self.sd_pipeline.text_encoder.to(self.accelerator.device, dtype=inference_dtype) + self.sd_pipeline.unet.to(self.accelerator.device, dtype=inference_dtype) + + trainable_layers = self.sd_pipeline.get_trainable_layers() + + self.accelerator.register_save_state_pre_hook(self._save_model_hook) + self.accelerator.register_load_state_pre_hook(self._load_model_hook) + + # Enable TF32 for faster training on Ampere GPUs, + # cf https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices + if self.config.allow_tf32: + torch.backends.cuda.matmul.allow_tf32 = True + + self.optimizer = self._setup_optimizer(trainable_layers.parameters() if not isinstance(trainable_layers, list) else trainable_layers) + + self.neg_prompt_embed = self.sd_pipeline.text_encoder( + self.sd_pipeline.tokenizer( + [""] if self.config.negative_prompts is None else self.config.negative_prompts, + return_tensors="pt", + padding="max_length", + truncation=True, + max_length=self.sd_pipeline.tokenizer.model_max_length, + ).input_ids.to(self.accelerator.device) + )[0] + + if config.per_prompt_stat_tracking: + self.stat_tracker = PerPromptStatTracker( + config.per_prompt_stat_tracking_buffer_size, + config.per_prompt_stat_tracking_min_count, + ) + + # NOTE: for some reason, autocast is necessary for non-lora training but for lora training it isn't necessary and it uses + # more memory + self.autocast = self.sd_pipeline.autocast or self.accelerator.autocast + + if hasattr(self.sd_pipeline, "use_lora") and self.sd_pipeline.use_lora: + unet, self.optimizer = self.accelerator.prepare(trainable_layers, self.optimizer) + self.trainable_layers = list(filter(lambda p: p.requires_grad, unet.parameters())) + else: + self.trainable_layers, self.optimizer = self.accelerator.prepare(trainable_layers, self.optimizer) + + if self.config.async_reward_computation: + self.executor = futures.ThreadPoolExecutor(max_workers=config.max_workers) + + if config.resume_from: + logger.info(f"Resuming from {config.resume_from}") + self.accelerator.load_state(config.resume_from) + self.first_epoch = int(config.resume_from.split("_")[-1]) + 1 + else: + self.first_epoch = 0 + + def compute_rewards(self, prompt_image_pairs, is_async=False): + if not is_async: + rewards = [] + for images, prompts, prompt_metadata in prompt_image_pairs: + reward, reward_metadata = self.reward_fn(images, prompts, prompt_metadata) + rewards.append( + ( + torch.as_tensor(reward, device=self.accelerator.device), + reward_metadata, + ) + ) + else: + rewards = self.executor.map(lambda x: self.reward_fn(*x), prompt_image_pairs) + rewards = [(torch.as_tensor(reward.result(), device=self.accelerator.device), reward_metadata.result()) for reward, reward_metadata in rewards] + + return zip(*rewards) + + def step(self, epoch: int, global_step: int): + """ + Perform a single step of training. + + Args: + epoch (int): The current epoch. + global_step (int): The current global step. + + Side Effects: + - Model weights are updated + - Logs the statistics to the accelerator trackers. + - If `self.image_samples_callback` is not None, it will be called with the prompt_image_pairs, global_step, and the accelerator tracker. + + Returns: + global_step (int): The updated global step. + + """ + samples, prompt_image_data = self._generate_samples( + iterations=self.config.sample_num_batches_per_epoch, + batch_size=self.config.sample_batch_size, + ) + + # collate samples into dict where each entry has shape (num_batches_per_epoch * sample.batch_size, ...) + samples = {k: torch.cat([s[k] for s in samples]) for k in samples[0].keys()} + rewards, rewards_metadata = self.compute_rewards(prompt_image_data, is_async=self.config.async_reward_computation) + + for i, image_data in enumerate(prompt_image_data): + image_data.extend([rewards[i], rewards_metadata[i]]) + + if self.image_samples_callback is not None: + self.image_samples_callback(prompt_image_data, global_step, self.accelerator.trackers[0]) + + rewards = torch.cat(rewards) + rewards = self.accelerator.gather(rewards).cpu().numpy() + + self.accelerator.log( + { + "reward": rewards, + "epoch": epoch, + "reward_mean": rewards.mean(), + "reward_std": rewards.std(), + }, + step=global_step, + ) + + if self.config.per_prompt_stat_tracking: + # gather the prompts across processes + prompt_ids = self.accelerator.gather(samples["prompt_ids"]).cpu().numpy() + prompts = self.sd_pipeline.tokenizer.batch_decode(prompt_ids, skip_special_tokens=True) + advantages = self.stat_tracker.update(prompts, rewards) + else: + advantages = (rewards - rewards.mean()) / (rewards.std() + 1e-8) + + # ungather advantages; keep the entries corresponding to the samples on this process + samples["advantages"] = torch.as_tensor(advantages).reshape(self.accelerator.num_processes, -1)[self.accelerator.process_index].to(self.accelerator.device) + + del samples["prompt_ids"] + + total_batch_size, num_timesteps = samples["timesteps"].shape + + for inner_epoch in range(self.config.train_num_inner_epochs): + # shuffle samples along batch dimension + perm = torch.randperm(total_batch_size, device=self.accelerator.device) + samples = {k: v[perm] for k, v in samples.items()} + + # shuffle along time dimension independently for each sample + # still trying to understand the code below + perms = torch.stack([torch.randperm(num_timesteps, device=self.accelerator.device) for _ in range(total_batch_size)]) + + for key in ["timesteps", "latents", "next_latents", "log_probs"]: + samples[key] = samples[key][ + torch.arange(total_batch_size, device=self.accelerator.device)[:, None], + perms, + ] + + original_keys = samples.keys() + original_values = samples.values() + # rebatch them as user defined train_batch_size is different from sample_batch_size + reshaped_values = [v.reshape(-1, self.config.train_batch_size, *v.shape[1:]) for v in original_values] + + # Transpose the list of original values + transposed_values = zip(*reshaped_values) + # Create new dictionaries for each row of transposed values + samples_batched = [dict(zip(original_keys, row_values)) for row_values in transposed_values] + + self.sd_pipeline.unet.train() + global_step = self._train_batched_samples(inner_epoch, epoch, global_step, samples_batched) + # ensure optimization step at the end of the inner epoch + if not self.accelerator.sync_gradients: + raise ValueError("Optimization step should have been performed by this point. Please check calculated gradient accumulation settings.") + + if epoch != 0 and epoch % self.config.save_freq == 0 and self.accelerator.is_main_process: + self.accelerator.save_state() + + return global_step + + def calculate_loss(self, latents, timesteps, next_latents, log_probs, advantages, embeds): + """ + Calculate the loss for a batch of an unpacked sample + + Args: + latents (torch.Tensor): + The latents sampled from the diffusion model, shape: [batch_size, num_channels_latents, height, width] + timesteps (torch.Tensor): + The timesteps sampled from the diffusion model, shape: [batch_size] + next_latents (torch.Tensor): + The next latents sampled from the diffusion model, shape: [batch_size, num_channels_latents, height, width] + log_probs (torch.Tensor): + The log probabilities of the latents, shape: [batch_size] + advantages (torch.Tensor): + The advantages of the latents, shape: [batch_size] + embeds (torch.Tensor): + The embeddings of the prompts, shape: [2*batch_size or batch_size, ...] + Note: the "or" is because if train_cfg is True, the expectation is that negative prompts are concatenated to the embeds + + Returns: + loss (torch.Tensor), approx_kl (torch.Tensor), clipfrac (torch.Tensor) + (all of these are of shape (1,)) + """ + with self.autocast(): + if self.config.train_cfg: + noise_pred = self.sd_pipeline.unet( + torch.cat([latents] * 2), + torch.cat([timesteps] * 2), + embeds, + ).sample + noise_pred_uncond, noise_pred_text = noise_pred.chunk(2) + noise_pred = noise_pred_uncond + self.config.sample_guidance_scale * (noise_pred_text - noise_pred_uncond) + else: + noise_pred = self.sd_pipeline.unet( + latents, + timesteps, + embeds, + ).sample + # compute the log prob of next_latents given latents under the current model + + scheduler_step_output = self.sd_pipeline.scheduler_step( + noise_pred, + timesteps, + latents, + eta=self.config.sample_eta, + prev_sample=next_latents, + ) + + log_prob = scheduler_step_output.log_probs + + advantages = torch.clamp( + advantages, + -self.config.train_adv_clip_max, + self.config.train_adv_clip_max, + ) + + ratio = torch.exp(log_prob - log_probs) + + loss = self.loss(advantages, self.config.train_clip_range, ratio) + + approx_kl = 0.5 * torch.mean((log_prob - log_probs) ** 2) + + clipfrac = torch.mean((torch.abs(ratio - 1.0) > self.config.train_clip_range).float()) + + return loss, approx_kl, clipfrac + + def loss( + self, + advantages: torch.Tensor, + clip_range: float, + ratio: torch.Tensor, + ): + unclipped_loss = -advantages * ratio + clipped_loss = -advantages * torch.clamp( + ratio, + 1.0 - clip_range, + 1.0 + clip_range, + ) + return torch.mean(torch.maximum(unclipped_loss, clipped_loss)) + + def _setup_optimizer(self, trainable_layers_parameters): + if self.config.train_use_8bit_adam: + import bitsandbytes + + optimizer_cls = bitsandbytes.optim.AdamW8bit + else: + optimizer_cls = torch.optim.AdamW + + return optimizer_cls( + trainable_layers_parameters, + lr=self.config.train_learning_rate, + betas=(self.config.train_adam_beta1, self.config.train_adam_beta2), + weight_decay=self.config.train_adam_weight_decay, + eps=self.config.train_adam_epsilon, + ) + + def _save_model_hook(self, models, weights, output_dir): + self.sd_pipeline.save_checkpoint(models, weights, output_dir) + weights.pop() # ensures that accelerate doesn't try to handle saving of the model + + def _load_model_hook(self, models, input_dir): + self.sd_pipeline.load_checkpoint(models, input_dir) + models.pop() # ensures that accelerate doesn't try to handle loading of the model + + def _generate_samples(self, iterations, batch_size): + """ + Generate samples from the model + + Args: + iterations (int): Number of iterations to generate samples for + batch_size (int): Batch size to use for sampling + + Returns: + samples (List[Dict[str, torch.Tensor]]), prompt_image_pairs (List[List[Any]]) + """ + samples = [] + prompt_image_pairs = [] + self.sd_pipeline.unet.eval() + + sample_neg_prompt_embeds = self.neg_prompt_embed.repeat(batch_size, 1, 1) + + for _ in range(iterations): + prompts, prompt_metadata = zip(*[self.prompt_fn() for _ in range(batch_size)]) + + prompt_ids = self.sd_pipeline.tokenizer( + prompts, + return_tensors="pt", + padding="max_length", + truncation=True, + max_length=self.sd_pipeline.tokenizer.model_max_length, + ).input_ids.to(self.accelerator.device) + prompt_embeds = self.sd_pipeline.text_encoder(prompt_ids)[0] + + with self.autocast(): + sd_output = self.sd_pipeline( + prompt_embeds=prompt_embeds, + negative_prompt_embeds=sample_neg_prompt_embeds, + num_inference_steps=self.config.sample_num_steps, + guidance_scale=self.config.sample_guidance_scale, + eta=self.config.sample_eta, + output_type="pt", + ) + + images = sd_output.images + latents = sd_output.latents + log_probs = sd_output.log_probs + + latents = torch.stack(latents, dim=1) # (batch_size, num_steps + 1, ...) + log_probs = torch.stack(log_probs, dim=1) # (batch_size, num_steps, 1) + timesteps = self.sd_pipeline.scheduler.timesteps.repeat(batch_size, 1) # (batch_size, num_steps) + + samples.append( + { + "prompt_ids": prompt_ids, + "prompt_embeds": prompt_embeds, + "timesteps": timesteps, + "latents": latents[:, :-1], # each entry is the latent before timestep t + "next_latents": latents[:, 1:], # each entry is the latent after timestep t + "log_probs": log_probs, + "negative_prompt_embeds": sample_neg_prompt_embeds, + } + ) + prompt_image_pairs.append([images, prompts, prompt_metadata]) + + return samples, prompt_image_pairs + + def _train_batched_samples(self, inner_epoch, epoch, global_step, batched_samples): + """ + Train on a batch of samples. Main training segment + + Args: + inner_epoch (int): The current inner epoch + epoch (int): The current epoch + global_step (int): The current global step + batched_samples (List[Dict[str, torch.Tensor]]): The batched samples to train on + + Side Effects: + - Model weights are updated + - Logs the statistics to the accelerator trackers. + + Returns: + global_step (int): The updated global step + """ + info = defaultdict(list) + for i, sample in enumerate(batched_samples): + if self.config.train_cfg: + # concat negative prompts to sample prompts to avoid two forward passes + embeds = torch.cat([sample["negative_prompt_embeds"], sample["prompt_embeds"]]) + else: + embeds = sample["prompt_embeds"] + + for j in range(self.num_train_timesteps): + with self.accelerator.accumulate(self.sd_pipeline.unet): + loss, approx_kl, clipfrac = self.calculate_loss( + sample["latents"][:, j], + sample["timesteps"][:, j], + sample["next_latents"][:, j], + sample["log_probs"][:, j], + sample["advantages"], + embeds, + ) + info["approx_kl"].append(approx_kl) + info["clipfrac"].append(clipfrac) + info["loss"].append(loss) + + self.accelerator.backward(loss) + if self.accelerator.sync_gradients: + self.accelerator.clip_grad_norm_( + self.trainable_layers.parameters() if not isinstance(self.trainable_layers, list) else self.trainable_layers, + self.config.train_max_grad_norm, + ) + self.optimizer.step() + self.optimizer.zero_grad() + + # Checks if the accelerator has performed an optimization step behind the scenes + if self.accelerator.sync_gradients: + # log training-related stuff + info = {k: torch.mean(torch.stack(v)) for k, v in info.items()} + info = self.accelerator.reduce(info, reduction="mean") + info.update({"epoch": epoch, "inner_epoch": inner_epoch}) + self.accelerator.log(info, step=global_step) + global_step += 1 + info = defaultdict(list) + return global_step + + def _config_check(self) -> Tuple[bool, str]: + samples_per_epoch = self.config.sample_batch_size * self.accelerator.num_processes * self.config.sample_num_batches_per_epoch + total_train_batch_size = self.config.train_batch_size * self.accelerator.num_processes * self.config.train_gradient_accumulation_steps + + if not self.config.sample_batch_size >= self.config.train_batch_size: + return ( + False, + f"Sample batch size ({self.config.sample_batch_size}) must be greater than or equal to the train batch size ({self.config.train_batch_size})", + ) + if not self.config.sample_batch_size % self.config.train_batch_size == 0: + return ( + False, + f"Sample batch size ({self.config.sample_batch_size}) must be divisible by the train batch size ({self.config.train_batch_size})", + ) + if not samples_per_epoch % total_train_batch_size == 0: + return ( + False, + f"Number of samples per epoch ({samples_per_epoch}) must be divisible by the total train batch size ({total_train_batch_size})", + ) + return True, "" + + def train(self, epochs: Optional[int] = None): + """ + Train the model for a given number of epochs + """ + global_step = 0 + if epochs is None: + epochs = self.config.num_epochs + for epoch in range(self.first_epoch, epochs): + global_step = self.step(epoch, global_step) + + def create_model_card(self, path: str, model_name: Optional[str] = "TRL DDPO Model") -> None: + """Creates and saves a model card for a TRL model. + + Args: + path (`str`): The path to save the model card to. + model_name (`str`, *optional*): The name of the model, defaults to `TRL DDPO Model`. + """ + try: + user = whoami()["name"] + # handle the offline case + except: # noqa + warnings.warn("Cannot retrieve user information assuming you are running in offline mode.") + return + + if not os.path.exists(path): + os.makedirs(path) + + model_card_content = MODEL_CARD_TEMPLATE.format(model_name=model_name, model_id=f"{user}/{path}") + with open(os.path.join(path, "README.md"), "w", encoding="utf-8") as f: + f.write(model_card_content) + + def _save_pretrained(self, save_directory): + self.sd_pipeline.save_pretrained(save_directory) + self.create_model_card(save_directory) diff --git a/trl/trainer/dpo_trainer.py b/trl/trainer/dpo_trainer.py new file mode 100644 index 0000000..ae2df89 --- /dev/null +++ b/trl/trainer/dpo_trainer.py @@ -0,0 +1,1186 @@ +# DPO Authors: Rafael Rafailov, Archit Sharma, Eric Mitchell, Stefano Ermon, Christopher D. Manning, and Chelsea Finn 2023 +# Copyright 2023 The HuggingFace Team. 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. +import inspect +import random +import warnings +from collections import defaultdict +from contextlib import contextmanager, nullcontext +from copy import deepcopy +from functools import wraps +from typing import Any, Callable, Dict, List, Literal, Optional, Tuple, Union + +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F +from accelerate import PartialState +from accelerate.utils import is_deepspeed_available, tqdm +from datasets import Dataset +from torch.utils.data import DataLoader +from transformers import ( + AutoModelForCausalLM, + DataCollator, + PreTrainedModel, + PreTrainedTokenizerBase, + Trainer, + TrainingArguments, +) +from transformers.trainer_callback import TrainerCallback +from transformers.trainer_utils import EvalLoopOutput + +from ..import_utils import is_peft_available, is_wandb_available +from ..models import PreTrainedModelWrapper, create_reference_model +from .utils import ( + DPODataCollatorWithPadding, + disable_dropout_in_model, + pad_to_length, + peft_module_casting_to_bf16, + trl_sanitze_kwargs_for_tagging, +) + + +if is_peft_available(): + from peft import PeftModel, get_peft_model, prepare_model_for_kbit_training + + +if is_wandb_available(): + import wandb + +if is_deepspeed_available(): + import deepspeed + +from transformers.integrations.deepspeed import is_deepspeed_zero3_enabled + + +class DPOTrainer(Trainer): + r""" + Initialize DPOTrainer. + + Args: + model (`transformers.PreTrainedModel`): + The model to train, preferably an `AutoModelForSequenceClassification`. + ref_model (`PreTrainedModelWrapper`): + Hugging Face transformer model with a casual language modelling head. Used for implicit reward computation and loss. If no + reference model is provided, the trainer will create a reference model with the same architecture as the model to be optimized. + beta (`float`, defaults to 0.1): + The beta factor in DPO loss. Higher beta means less divergence from the initial policy. For the IPO loss, beta is the regularization parameter denoted by tau in the paper. + label_smoothing (`float`, defaults to 0): + The robust DPO label smoothing parameter from the [cDPO](https://ericmitchell.ai/cdpo.pdf) report that should be between 0 and 0.5. + loss_type (`str`, defaults to `"sigmoid"`): + The type of DPO loss to use. Either `"sigmoid"` the default DPO loss,`"hinge"` loss from [SLiC](https://arxiv.org/abs/2305.10425) paper, `"ipo"` from [IPO](https://arxiv.org/abs/2310.12036) paper, or `"kto"` from the HALOs [report](https://github.com/ContextualAI/HALOs/blob/main/assets/report.pdf). + args (`transformers.TrainingArguments`): + The arguments to use for training. + data_collator (`transformers.DataCollator`): + The data collator to use for training. If None is specified, the default data collator (`DPODataCollatorWithPadding`) will be used + which will pad the sequences to the maximum length of the sequences in the batch, given a dataset of paired sequences. + label_pad_token_id (`int`, defaults to `-100`): + The label pad token id. This argument is required if you want to use the default data collator. + padding_value (`int`, defaults to `0`): + The padding value if it is different to the tokenizer's pad_token_id. + truncation_mode (`str`, defaults to `keep_end`): + The truncation mode to use, either `keep_end` or `keep_start`. This argument is required if you want to use the default data collator. + train_dataset (`datasets.Dataset`): + The dataset to use for training. + eval_dataset (`datasets.Dataset`): + The dataset to use for evaluation. + tokenizer (`transformers.PreTrainedTokenizerBase`): + The tokenizer to use for training. This argument is required if you want to use the default data collator. + model_init (`Callable[[], transformers.PreTrainedModel]`): + The model initializer to use for training. If None is specified, the default model initializer will be used. + callbacks (`List[transformers.TrainerCallback]`): + The callbacks to use for training. + optimizers (`Tuple[torch.optim.Optimizer, torch.optim.lr_scheduler.LambdaLR]`): + The optimizer and scheduler to use for training. + preprocess_logits_for_metrics (`Callable[[torch.Tensor, torch.Tensor], torch.Tensor]`): + The function to use to preprocess the logits before computing the metrics. + max_length (`int`, defaults to `None`): + The maximum length of the sequences in the batch. This argument is required if you want to use the default data collator. + max_prompt_length (`int`, defaults to `None`): + The maximum length of the prompt. This argument is required if you want to use the default data collator. + max_target_length (`int`, defaults to `None`): + The maximum length of the target. This argument is required if you want to use the default data collator and your model is an encoder-decoder. + peft_config (`Dict`, defaults to `None`): + The PEFT configuration to use for training. If you pass a PEFT configuration, the model will be wrapped in a PEFT model. + is_encoder_decoder (`Optional[bool]`, `optional`, defaults to `None`): + If no model is provided, we need to know if the model_init returns an encoder-decoder. + disable_dropout (`bool`, defaults to `True`): + Whether or not to disable dropouts in `model` and `ref_model`. + generate_during_eval (`bool`, defaults to `False`): + Whether to sample and log generations during evaluation step. + compute_metrics (`Callable[[EvalPrediction], Dict]`, *optional*): + The function to use to compute the metrics. Must take a `EvalPrediction` and return + a dictionary string to metric values. + precompute_ref_log_probs (`bool`, defaults to `False`): + Flag to precompute reference model log probabilities and evaluation datasets. This is useful if you want to train + without the reference model and reduce the total GPU memory needed. + dataset_num_proc (`Optional[int]`, *optional*): + The number of workers to use to tokenize the data. Defaults to None. + model_init_kwargs (`Optional[Dict]`, *optional*): + Dict of Optional kwargs to pass when instantiating the model from a string + ref_model_init_kwargs (`Optional[Dict]`, *optional*): + Dict of Optional kwargs to pass when instantiating the ref model from a string + model_adapter_name (`str`, defaults to `None`): + Name of the train target PEFT adapter, when using LoRA with multiple adapters. + ref_adapter_name (`str`, defaults to `None`): + Name of the reference PEFT adapter, when using LoRA with multiple adapters. + reference_free (`bool`): + If True, we ignore the _provided_ reference model and implicitly use a reference model that assigns equal probability to all responses. + """ + + _tag_names = ["trl", "dpo"] + + def __init__( + self, + model: Optional[Union[PreTrainedModel, nn.Module, str]] = None, + ref_model: Optional[Union[PreTrainedModel, nn.Module, str]] = None, + dpo_alpha: float = 1.0, + beta: float = 0.1, + gamma: float = 0.1, + label_smoothing: float = 0, + loss_type: Literal["sigmoid", "hinge", "ipo", "kto_pair"] = "sigmoid", + args: Optional[TrainingArguments] = None, + data_collator: Optional[DataCollator] = None, + label_pad_token_id: int = -100, + padding_value: Optional[int] = None, + truncation_mode: str = "keep_end", + train_dataset: Optional[Dataset] = None, + eval_dataset: Optional[Union[Dataset, Dict[str, Dataset]]] = None, + tokenizer: Optional[PreTrainedTokenizerBase] = None, + model_init: Optional[Callable[[], PreTrainedModel]] = None, + callbacks: Optional[List[TrainerCallback]] = None, + optimizers: Tuple[torch.optim.Optimizer, torch.optim.lr_scheduler.LambdaLR] = (None, None), + preprocess_logits_for_metrics: Optional[Callable[[torch.Tensor, torch.Tensor], torch.Tensor]] = None, + max_length: Optional[int] = None, + max_prompt_length: Optional[int] = None, + max_target_length: Optional[int] = None, + peft_config: Optional[Dict] = None, + is_encoder_decoder: Optional[bool] = None, + disable_dropout: bool = True, + generate_during_eval: bool = False, + compute_metrics: Optional[Callable[[EvalLoopOutput], Dict]] = None, + precompute_ref_log_probs: bool = False, + dataset_num_proc: Optional[int] = None, + model_init_kwargs: Optional[Dict] = None, + ref_model_init_kwargs: Optional[Dict] = None, + model_adapter_name: Optional[str] = None, + ref_adapter_name: Optional[str] = None, + reference_free: bool = False, + ): + # import pdb;pdb.set_trace() + if model_init_kwargs is None: + model_init_kwargs = {} + elif not isinstance(model, str): + raise ValueError("You passed model_kwargs to the DPOTrainer. But your model is already instantiated.") + + if ref_model_init_kwargs is None: + ref_model_init_kwargs = {} + elif not isinstance(ref_model, str): + raise ValueError("You passed ref_model_kwargs to the DPOTrainer. But your ref_model is already instantiated.") + + if isinstance(model, str): + warnings.warn("You passed a model_id to the DPOTrainer. This will automatically create an " "`AutoModelForCausalLM` or a `PeftModel` (if you passed a `peft_config`) for you.") + model = AutoModelForCausalLM.from_pretrained(model, **model_init_kwargs) + + if isinstance(ref_model, str): + warnings.warn("You passed a ref model_id to the DPOTrainer. This will automatically create an " "`AutoModelForCausalLM`") + ref_model = AutoModelForCausalLM.from_pretrained(ref_model, **ref_model_init_kwargs) + + # Initialize this variable to False. This helps tracking the case when `peft_module_casting_to_bf16` + # has been called in order to properly call autocast if needed. + self._peft_has_been_casted_to_bf16 = False + + if generate_during_eval and not is_wandb_available(): + raise ValueError("`generate_during_eval=True` requires Weights and Biases to be installed." " Please install `wandb` to resolve.") + + if model is not None: + self.is_encoder_decoder = model.config.is_encoder_decoder + elif is_encoder_decoder is None: + raise ValueError("When no model is provided, you need to pass the parameter is_encoder_decoder.") + else: + self.is_encoder_decoder = is_encoder_decoder + + self.is_peft_model = is_peft_available() and isinstance(model, PeftModel) + self.model_adapter_name = model_adapter_name + self.ref_adapter_name = ref_adapter_name + self.reference_free = reference_free + + if ref_model: + self.ref_model = ref_model + elif self.is_peft_model or precompute_ref_log_probs: + # The `model` with adapters turned off will be used as the reference model + self.ref_model = None + else: + if is_deepspeed_zero3_enabled(): + self.ref_model = AutoModelForCausalLM.from_pretrained(model) + else: + self.ref_model = create_reference_model(model) + + if tokenizer is None: + raise ValueError("tokenizer must be specified to tokenize a DPO dataset.") + if max_length is None: + warnings.warn( + "`max_length` is not set in the DPOTrainer's init" " it will default to `512` by default, but you should do it yourself in the future.", + UserWarning, + ) + max_length = 512 + if max_prompt_length is None: + warnings.warn( + "`max_prompt_length` is not set in the DPOTrainer's init" " it will default to `128` by default, but you should do it yourself in the future.", + UserWarning, + ) + max_prompt_length = 128 + + if max_target_length is None and self.is_encoder_decoder: + warnings.warn( + "When using an encoder decoder architecture, you should set `max_target_length` in the DPOTrainer's init" " it will default to `128` by default, but you should do it yourself in the future.", + UserWarning, + ) + max_target_length = 128 + + if data_collator is None: + data_collator = DPODataCollatorWithPadding( + pad_token_id=tokenizer.pad_token_id, + label_pad_token_id=label_pad_token_id, + is_encoder_decoder=self.is_encoder_decoder, + ) + + if args.remove_unused_columns: + args.remove_unused_columns = False + # warn users + warnings.warn( + "When using DPODataCollatorWithPadding, you should set `remove_unused_columns=False` in your TrainingArguments" " we have set it for you, but you should do it yourself in the future.", + UserWarning, + ) + + self.use_dpo_data_collator = True + else: + self.use_dpo_data_collator = False + + if disable_dropout: + disable_dropout_in_model(model) + if self.ref_model is not None: + disable_dropout_in_model(self.ref_model) + + self.max_length = max_length + self.generate_during_eval = generate_during_eval + self.label_pad_token_id = label_pad_token_id + self.padding_value = padding_value if padding_value is not None else tokenizer.pad_token_id + self.max_prompt_length = max_prompt_length + self.truncation_mode = truncation_mode + self.max_target_length = max_target_length + self.tokenizer = tokenizer + self.precompute_ref_log_probs = precompute_ref_log_probs + + # Since ref_logs are precomputed on the first call to get_train/eval_dataloader + # keep track of first called to avoid computation of future calls + self._precomputed_train_ref_log_probs = False + self._precomputed_eval_ref_log_probs = False + + if loss_type in ["hinge", "ipo", "kto_pair"] and label_smoothing > 0: + warnings.warn("You are using a loss type that does not support label smoothing. Ignoring label_smoothing parameter.") + + self.dpo_alpha = dpo_alpha + self.beta = beta + self.gamma = gamma + self.label_smoothing = label_smoothing + self.loss_type = loss_type + + self._stored_metrics = defaultdict(lambda: defaultdict(list)) + + self.dataset_num_proc = dataset_num_proc + + # Compute that only on the main process for faster data processing. + # see: https://github.com/huggingface/trl/pull/1255 + # with PartialState().local_main_process_first(): + # # tokenize the dataset + # train_dataset = train_dataset.map(self.tokenize_row, num_proc=self.dataset_num_proc) + # if eval_dataset is not None: + # eval_dataset = eval_dataset.map(self.tokenize_row, num_proc=self.dataset_num_proc) + + super().__init__( + model=model, + args=args, + data_collator=data_collator, + train_dataset=train_dataset, + eval_dataset=eval_dataset, + tokenizer=tokenizer, + model_init=model_init, + compute_metrics=compute_metrics, + callbacks=callbacks, + optimizers=optimizers, + preprocess_logits_for_metrics=preprocess_logits_for_metrics, + ) + + if not hasattr(self, "accelerator"): + raise AttributeError("Your `Trainer` does not have an `accelerator` object. Consider upgrading `transformers`.") + + # Deepspeed Zero-3 does not support precompute_ref_log_probs + if self.is_deepspeed_enabled: + if self.accelerator.state.deepspeed_plugin.zero_stage == 3 and self.precompute_ref_log_probs: + raise ValueError("You cannot use `precompute_ref_log_probs=True` with Deepspeed ZeRO-3. Please set `precompute_ref_log_probs=False`.") + + if self.ref_model is None: + if not (self.is_peft_model or self.precompute_ref_log_probs): + raise ValueError("No reference model and model is not a Peft model. Try setting `precompute_ref_log_probs=True`") + else: + if self.is_deepspeed_enabled: + self.ref_model = self._prepare_deepspeed(self.ref_model) + else: + self.ref_model = self.accelerator.prepare_model(self.ref_model, evaluation_mode=True) + + def _prepare_deepspeed(self, model: PreTrainedModelWrapper): + # Adapted from accelerate: https://github.com/huggingface/accelerate/blob/739b135f8367becb67ffaada12fe76e3aa60fefd/src/accelerate/accelerator.py#L1473 + deepspeed_plugin = self.accelerator.state.deepspeed_plugin + config_kwargs = deepcopy(deepspeed_plugin.deepspeed_config) + + if model is not None: + if hasattr(model, "config"): + hidden_size = max(model.config.hidden_sizes) if getattr(model.config, "hidden_sizes", None) else getattr(model.config, "hidden_size", None) + if hidden_size is not None and config_kwargs["zero_optimization"]["stage"] == 3: + # Note that `stage3_prefetch_bucket_size` can produce DeepSpeed messages like: `Invalidate trace cache @ step 0: expected module 1, but got module 0` + # This is expected and is not an error, see: https://github.com/microsoft/DeepSpeed/discussions/4081 + config_kwargs.update( + { + "zero_optimization.reduce_bucket_size": hidden_size * hidden_size, + "zero_optimization.stage3_param_persistence_threshold": 10 * hidden_size, + "zero_optimization.stage3_prefetch_bucket_size": 0.9 * hidden_size * hidden_size, + } + ) + + # If ZeRO-3 is used, we shard both the active and reference model. + # Otherwise, we assume the reference model fits in memory and is initialized on each device with ZeRO disabled (stage 0) + if config_kwargs["zero_optimization"]["stage"] != 3: + config_kwargs["zero_optimization"]["stage"] = 0 + model, *_ = deepspeed.initialize(model=model, config=config_kwargs) + model.eval() + return model + + def get_train_dataloader(self) -> DataLoader: + """ + Returns the training [`~torch.utils.data.DataLoader`]. + + Subclass of transformers.src.transformers.trainer.get_train_dataloader to precompute `ref_log_probs`. + """ + + if self.precompute_ref_log_probs and not self._precomputed_train_ref_log_probs: + dataloader_params = { + "batch_size": self.args.per_device_train_batch_size, + "collate_fn": self.data_collator, + "num_workers": self.args.dataloader_num_workers, + "pin_memory": self.args.dataloader_pin_memory, + "shuffle": False, + } + + # prepare dataloader + data_loader = self.accelerator.prepare(DataLoader(self.train_dataset, **dataloader_params)) + + reference_chosen_logps = [] + reference_rejected_logps = [] + for padded_batch in tqdm(iterable=data_loader, desc="Train dataset reference log probs"): + reference_chosen_logp, reference_rejected_logp = self.compute_reference_log_probs(padded_batch) + reference_chosen_logp, reference_rejected_logp = self.accelerator.gather_for_metrics((reference_chosen_logp, reference_rejected_logp)) + reference_chosen_logps.append(reference_chosen_logp.cpu()) + reference_rejected_logps.append(reference_rejected_logp.cpu()) + + all_reference_chosen_logps = torch.cat(reference_chosen_logps).float().numpy() + all_reference_rejected_logps = torch.cat(reference_rejected_logps).float().numpy() + + self.train_dataset = self.train_dataset.add_column(name="reference_chosen_logps", column=all_reference_chosen_logps) + self.train_dataset = self.train_dataset.add_column(name="reference_rejected_logps", column=all_reference_rejected_logps) + + self._precomputed_train_ref_log_probs = True + + return super().get_train_dataloader() + + def get_eval_dataloader(self, eval_dataset: Optional[Dataset] = None) -> DataLoader: + """ + Returns the evaluation [`~torch.utils.data.DataLoader`]. + + Subclass of transformers.src.transformers.trainer.get_eval_dataloader to precompute `ref_log_probs`. + + Args: + eval_dataset (`torch.utils.data.Dataset`, *optional*): + If provided, will override `self.eval_dataset`. If it is a [`~datasets.Dataset`], columns not accepted + by the `model.forward()` method are automatically removed. It must implement `__len__`. + """ + if eval_dataset is None and self.eval_dataset is None: + raise ValueError("Trainer: evaluation requires an eval_dataset.") + eval_dataset = eval_dataset if eval_dataset is not None else self.eval_dataset + + if self.precompute_ref_log_probs and not self._precomputed_eval_ref_log_probs: + dataloader_params = { + "batch_size": self.args.per_device_eval_batch_size, + "collate_fn": self.data_collator, + "num_workers": self.args.dataloader_num_workers, + "pin_memory": self.args.dataloader_pin_memory, + "shuffle": False, + } + + # prepare dataloader + data_loader = self.accelerator.prepare(DataLoader(eval_dataset, **dataloader_params)) + + reference_chosen_logps = [] + reference_rejected_logps = [] + for padded_batch in tqdm(iterable=data_loader, desc="Eval dataset reference log probs"): + reference_chosen_logp, reference_rejected_logp = self.compute_reference_log_probs(padded_batch) + reference_chosen_logp, reference_rejected_logp = self.accelerator.gather_for_metrics((reference_chosen_logp, reference_rejected_logp)) + reference_chosen_logps.append(reference_chosen_logp.cpu()) + reference_rejected_logps.append(reference_rejected_logp.cpu()) + + all_reference_chosen_logps = torch.cat(reference_chosen_logps).float().numpy() + all_reference_rejected_logps = torch.cat(reference_rejected_logps).float().numpy() + + eval_dataset = eval_dataset.add_column(name="reference_chosen_logps", column=all_reference_chosen_logps) + eval_dataset = eval_dataset.add_column(name="reference_rejected_logps", column=all_reference_rejected_logps) + + # Save calculated reference_chosen_logps and reference_rejected_logps to the eval_dataset for subsequent runs + if self.eval_dataset is not None: + self.eval_dataset = eval_dataset + self._precomputed_eval_ref_log_probs = True + + return super().get_eval_dataloader(eval_dataset=eval_dataset) + + def build_tokenized_answer(self, prompt, answer): + """ + Llama tokenizer does satisfy `enc(a + b) = enc(a) + enc(b)`. + It does ensure `enc(a + b) = enc(a) + enc(a + b)[len(enc(a)):]`. + Reference: + https://github.com/EleutherAI/lm-evaluation-harness/pull/531#issuecomment-1595586257 + """ + + full_tokenized = self.tokenizer(prompt + answer, add_special_tokens=False) + prompt_input_ids = self.tokenizer(prompt, add_special_tokens=False)["input_ids"] + + answer_input_ids = full_tokenized["input_ids"][len(prompt_input_ids) :] + answer_attention_mask = full_tokenized["attention_mask"][len(prompt_input_ids) :] + + # Concat tokens to form `enc(a) + enc(a + b)[len(enc(a)):]` + full_concat_input_ids = np.concatenate([prompt_input_ids, answer_input_ids]) + + # Prepare input tokens for token by token comparison + full_input_ids = np.array(full_tokenized["input_ids"]) + + if len(full_input_ids) != len(full_concat_input_ids): + raise ValueError("Prompt input ids and answer input ids should have the same length.") + + # On some tokenizers, like Llama-2 tokenizer, there are occasions where tokens + # can be merged together when tokenizing prompt+answer. This could result + # on the last token from the prompt being different when tokenized on its own + # vs when done as prompt+answer. + response_token_ids_start_idx = len(prompt_input_ids) + + # If tokenized prompt is different than both prompt+answer, then it means the + # last token has changed due to merging. + if prompt_input_ids != full_tokenized["input_ids"][:response_token_ids_start_idx]: + response_token_ids_start_idx -= 1 + + prompt_input_ids = full_tokenized["input_ids"][:response_token_ids_start_idx] + prompt_attention_mask = full_tokenized["attention_mask"][:response_token_ids_start_idx] + + if len(prompt_input_ids) != len(prompt_attention_mask): + raise ValueError("Prompt input ids and attention mask should have the same length.") + + answer_input_ids = full_tokenized["input_ids"][response_token_ids_start_idx:] + answer_attention_mask = full_tokenized["attention_mask"][response_token_ids_start_idx:] + + return dict( + prompt_input_ids=prompt_input_ids, + prompt_attention_mask=prompt_attention_mask, + input_ids=answer_input_ids, + attention_mask=answer_attention_mask, + ) + + def tokenize_row(self, feature, model: Optional[Union[PreTrainedModel, nn.Module]] = None) -> Dict: + """Tokenize a single row from a DPO specific dataset. + + At this stage, we don't convert to PyTorch tensors yet; we just handle the truncation + in case the prompt + chosen or prompt + rejected responses is/are too long. First + we truncate the prompt; if we're still too long, we truncate the chosen/rejected. + + We also create the labels for the chosen/rejected responses, which are of length equal to + the sum of the length of the prompt and the chosen/rejected response, with + label_pad_token_id for the prompt tokens. + """ + batch = {} + prompt = feature["prompt"] + chosen = feature["chosen"] + rejected = feature["rejected"] + + if not self.is_encoder_decoder: + # Check issues below for more details + # 1. https://github.com/huggingface/trl/issues/907 + # 2. https://github.com/EleutherAI/lm-evaluation-harness/pull/531#issuecomment-1595586257 + # 3. https://github.com/LianjiaTech/BELLE/issues/337 + + if not isinstance(prompt, str): + raise ValueError(f"prompt should be an str but got {type(prompt)}") + prompt_tokens = self.tokenizer(prompt, add_special_tokens=False) + prompt_tokens = {f"prompt_{k}": v for k, v in prompt_tokens.items()} + + if not isinstance(chosen, str): + raise ValueError(f"chosen should be an str but got {type(chosen)}") + chosen_tokens = self.build_tokenized_answer(prompt, chosen) + + if not isinstance(rejected, str): + raise ValueError(f"rejected should be an str but got {type(rejected)}") + rejected_tokens = self.build_tokenized_answer(prompt, rejected) + + # Last prompt token might get merged by tokenizer and + # it should not be included for generation if that happens + prompt_len_input_ids = len(prompt_tokens["prompt_input_ids"]) + + chosen_prompt_len_input_ids = len(chosen_tokens["prompt_input_ids"]) + rejected_prompt_len_input_ids = len(rejected_tokens["prompt_input_ids"]) + prompt_len_input_ids = min(chosen_prompt_len_input_ids, rejected_prompt_len_input_ids) + + for k, v in prompt_tokens.items(): + prompt_tokens[k] = v[:prompt_len_input_ids] + + # Make sure prompts only have one different token at most an + # and length only differs by 1 at most + num_diff_tokens = sum([a != b for a, b in zip(chosen_tokens["prompt_input_ids"], rejected_tokens["prompt_input_ids"])]) + num_diff_len = abs(chosen_prompt_len_input_ids - rejected_prompt_len_input_ids) + if num_diff_tokens > 1 or num_diff_len > 1: + raise ValueError("Chosen and rejected prompt_input_ids might only differ on the " "last token due to tokenizer merge ops.") + + # add BOS token to head of prompt + prompt_tokens["prompt_input_ids"] = [self.tokenizer.bos_token_id] + prompt_tokens["prompt_input_ids"] + chosen_tokens["prompt_input_ids"] = [self.tokenizer.bos_token_id] + chosen_tokens["prompt_input_ids"] + rejected_tokens["prompt_input_ids"] = [self.tokenizer.bos_token_id] + rejected_tokens["prompt_input_ids"] + + prompt_tokens["prompt_attention_mask"] = [1] + prompt_tokens["prompt_attention_mask"] + chosen_tokens["prompt_attention_mask"] = [1] + chosen_tokens["prompt_attention_mask"] + rejected_tokens["prompt_attention_mask"] = [1] + rejected_tokens["prompt_attention_mask"] + + # add EOS token to end of answer + chosen_tokens["input_ids"].append(self.tokenizer.eos_token_id) + chosen_tokens["attention_mask"].append(1) + + rejected_tokens["input_ids"].append(self.tokenizer.eos_token_id) + rejected_tokens["attention_mask"].append(1) + + longer_response_length = max(len(chosen_tokens["input_ids"]), len(rejected_tokens["input_ids"])) + + # if combined sequence is too long, truncate the prompt + for answer_tokens in [chosen_tokens, rejected_tokens, prompt_tokens]: + if len(answer_tokens["prompt_input_ids"]) + longer_response_length > self.max_length: + if self.truncation_mode == "keep_start": + for k in ["prompt_input_ids", "prompt_attention_mask"]: + answer_tokens[k] = answer_tokens[k][: self.max_prompt_length] + elif self.truncation_mode == "keep_end": + for k in ["prompt_input_ids", "prompt_attention_mask"]: + answer_tokens[k] = answer_tokens[k][-self.max_prompt_length :] + else: + raise ValueError(f"Unknown truncation mode: {self.truncation_mode}") + + # if that's still too long, truncate the response + for answer_tokens in [chosen_tokens, rejected_tokens]: + if len(answer_tokens["prompt_input_ids"]) + longer_response_length > self.max_length: + for k in ["input_ids", "attention_mask"]: + answer_tokens[k] = answer_tokens[k][: self.max_length - self.max_prompt_length] + + # Create labels + chosen_sequence_tokens = {k: chosen_tokens[f"prompt_{k}"] + chosen_tokens[k] for k in ["input_ids", "attention_mask"]} + rejected_sequence_tokens = {k: rejected_tokens[f"prompt_{k}"] + rejected_tokens[k] for k in ["input_ids", "attention_mask"]} + chosen_sequence_tokens["labels"] = chosen_sequence_tokens["input_ids"][:] + chosen_sequence_tokens["labels"][: len(chosen_tokens["prompt_input_ids"])] = [self.label_pad_token_id] * len(chosen_tokens["prompt_input_ids"]) + rejected_sequence_tokens["labels"] = rejected_sequence_tokens["input_ids"][:] + rejected_sequence_tokens["labels"][: len(rejected_tokens["prompt_input_ids"])] = [self.label_pad_token_id] * len(rejected_tokens["prompt_input_ids"]) + + for k, toks in { + "chosen_": chosen_sequence_tokens, + "rejected_": rejected_sequence_tokens, + "": prompt_tokens, + }.items(): + for type_key, tokens in toks.items(): + if type_key == "token_type_ids": + continue + batch[f"{k}{type_key}"] = tokens + + else: + chosen_tokens = self.tokenizer(chosen, truncation=True, max_length=self.max_target_length, add_special_tokens=True) + rejected_tokens = self.tokenizer(rejected, truncation=True, max_length=self.max_target_length, add_special_tokens=True) + prompt_tokens = self.tokenizer(prompt, truncation=True, max_length=self.max_prompt_length, add_special_tokens=True) + + batch["chosen_labels"] = chosen_tokens["input_ids"] + batch["rejected_labels"] = rejected_tokens["input_ids"] + batch["prompt_input_ids"] = prompt_tokens["input_ids"] + batch["prompt_attention_mask"] = prompt_tokens["attention_mask"] + + if model is not None and hasattr(model, "prepare_decoder_input_ids_from_labels"): + batch["rejected_decoder_input_ids"] = model.prepare_decoder_input_ids_from_labels(labels=batch["rejected_labels"]) + batch["chosen_decoder_input_ids"] = model.prepare_decoder_input_ids_from_labels(labels=batch["chosen_labels"]) + + return batch + + @contextmanager + def null_ref_context(self): + """Context manager for handling null reference model (that is, peft adapter manipulation).""" + with self.accelerator.unwrap_model(self.model).disable_adapter() if self.is_peft_model and not self.ref_adapter_name else nullcontext(): + if self.ref_adapter_name: + self.model.set_adapter(self.ref_adapter_name) + yield + if self.ref_adapter_name: + self.model.set_adapter(self.model_adapter_name or "default") + + def compute_reference_log_probs(self, padded_batch: Dict) -> Dict: + """Computes log probabilities of the reference model for a single padded batch of a DPO specific dataset.""" + compte_ref_context_manager = torch.cuda.amp.autocast if self._peft_has_been_casted_to_bf16 else nullcontext + + # compute reference logps + with torch.no_grad(), compte_ref_context_manager(): + if self.ref_model is None: + with self.null_ref_context(): + ( + reference_chosen_logps, + reference_rejected_logps, + _, + _, + ) = self.concatenated_forward(self.model, padded_batch) + else: + ( + reference_chosen_logps, + reference_rejected_logps, + _, + _, + ) = self.concatenated_forward(self.ref_model, padded_batch) + + return reference_chosen_logps, reference_rejected_logps + + @staticmethod + def concatenated_inputs( + batch: Dict[str, Union[List, torch.LongTensor]], + is_encoder_decoder: bool = False, + label_pad_token_id: int = -100, + padding_value: int = 0, + device: Optional[torch.device] = None, + ) -> Dict[str, torch.LongTensor]: + """Concatenate the chosen and rejected inputs into a single tensor. + + Args: + batch: A batch of data. Must contain the keys 'chosen_input_ids' and 'rejected_input_ids', which are tensors of shape (batch_size, sequence_length). + is_encoder_decoder: Whether the model is an encoder-decoder model. + label_pad_token_id: The label pad token id. + padding_value: The padding value to use for the concatenated inputs_ids. + device: The device for the concatenated inputs. + + Returns: + A dictionary containing the concatenated inputs under the key 'concatenated_input_ids'. + """ + concatenated_batch = {} + + if is_encoder_decoder: + max_length = max(batch["chosen_labels"].shape[1], batch["rejected_labels"].shape[1]) + else: + max_length = max(batch["chosen_input_ids"].shape[1], batch["rejected_input_ids"].shape[1]) + + for k in batch: + # import pdb; pdb.set_trace() + if k.startswith("chosen") and isinstance(batch[k], torch.Tensor): + if "labels" in k or is_encoder_decoder: + pad_value = label_pad_token_id + elif k.endswith("_input_ids"): + pad_value = padding_value + elif k.endswith("_attention_mask"): + pad_value = 0 + concatenated_key = k.replace("chosen", "concatenated") + concatenated_batch[concatenated_key] = pad_to_length(batch[k], max_length, pad_value=pad_value) + for k in batch: + if k.startswith("rejected") and isinstance(batch[k], torch.Tensor): + if "labels" in k or is_encoder_decoder: + pad_value = label_pad_token_id + elif k.endswith("_input_ids"): + pad_value = padding_value + elif k.endswith("_attention_mask"): + pad_value = 0 + concatenated_key = k.replace("rejected", "concatenated") + concatenated_batch[concatenated_key] = torch.cat( + ( + concatenated_batch[concatenated_key], + pad_to_length(batch[k], max_length, pad_value=pad_value), + ), + dim=0, + ).to(device=device) + + if is_encoder_decoder: + concatenated_batch["concatenated_input_ids"] = batch["prompt_input_ids"].repeat(2, 1).to(device=device) + concatenated_batch["concatenated_attention_mask"] = batch["prompt_attention_mask"].repeat(2, 1).to(device=device) + # import pdb; pdb.set_trace() + # repeated_list = [ + # batch['images'][0] * 2, + # batch['images'][1] * 2 + # ] + concatenated_batch["concatenated_images"] = batch["images"] * 2 + concatenated_batch["image_sizes"] = batch["image_sizes"] * 2 + concatenated_batch["modalities"] = batch["modalities"] * 2 + return concatenated_batch + + def dpo_loss( + self, + policy_chosen_logps: torch.FloatTensor, + policy_rejected_logps: torch.FloatTensor, + reference_chosen_logps: torch.FloatTensor, + reference_rejected_logps: torch.FloatTensor, + ) -> Tuple[torch.FloatTensor, torch.FloatTensor, torch.FloatTensor]: + """Compute the DPO loss for a batch of policy and reference model log probabilities. + + Args: + policy_chosen_logps: Log probabilities of the policy model for the chosen responses. Shape: (batch_size,) + policy_rejected_logps: Log probabilities of the policy model for the rejected responses. Shape: (batch_size,) + reference_chosen_logps: Log probabilities of the reference model for the chosen responses. Shape: (batch_size,) + reference_rejected_logps: Log probabilities of the reference model for the rejected responses. Shape: (batch_size,) + + Returns: + A tuple of three tensors: (losses, chosen_rewards, rejected_rewards). + The losses tensor contains the DPO loss for each example in the batch. + The chosen_rewards and rejected_rewards tensors contain the rewards for the chosen and rejected responses, respectively. + """ + pi_logratios = policy_chosen_logps - policy_rejected_logps + if self.reference_free: + ref_logratios = torch.tensor([0], dtype=pi_logratios.dtype, device=pi_logratios.device) + else: + ref_logratios = reference_chosen_logps - reference_rejected_logps + + pi_logratios = pi_logratios.to(self.accelerator.device) + ref_logratios = ref_logratios.to(self.accelerator.device) + logits = pi_logratios - ref_logratios + # print(f"pi log ratios: {pi_logratios}") + # print(f"ref log ratios: {ref_logratios}") + # print(f"logits: {logits}") + # The beta is a temperature parameter for the DPO loss, typically something in the range of 0.1 to 0.5. + # We ignore the reference model as beta -> 0. The label_smoothing parameter encodes our uncertainty about the labels and + # calculates a conservative DPO loss. + if self.loss_type == "sigmoid": + losses = -F.logsigmoid(self.beta * logits) * (1 - self.label_smoothing) - F.logsigmoid(-self.beta * logits) * self.label_smoothing + elif self.loss_type == "hinge": + losses = torch.relu(1 - self.beta * logits) + elif self.loss_type == "ipo": + # eqn (17) of the paper where beta is the regularization parameter for the IPO loss, denoted by tau in the paper. + losses = (logits - 1 / (2 * self.beta)) ** 2 + elif self.loss_type == "kto_pair": + # eqn (7) of the HALOs paper + chosen_KL = (policy_chosen_logps - reference_chosen_logps).mean().clamp(min=0) + rejected_KL = (policy_rejected_logps - reference_rejected_logps).mean().clamp(min=0) + + chosen_logratios = policy_chosen_logps - reference_chosen_logps + rejected_logratios = policy_rejected_logps - reference_rejected_logps + # As described in the KTO report, the KL term for chosen (rejected) is estimated using the rejected (chosen) half. + losses = torch.cat( + ( + 1 - F.sigmoid(self.beta * (chosen_logratios - rejected_KL)), + 1 - F.sigmoid(self.beta * (chosen_KL - rejected_logratios)), + ), + 0, + ) + else: + raise ValueError(f"Unknown loss type: {self.loss_type}. Should be one of ['sigmoid', 'hinge', 'ipo', 'kto_pair']") + + chosen_rewards = self.beta * (policy_chosen_logps.to(self.accelerator.device) - reference_chosen_logps.to(self.accelerator.device)).detach() + rejected_rewards = self.beta * (policy_rejected_logps.to(self.accelerator.device) - reference_rejected_logps.to(self.accelerator.device)).detach() + + return losses, chosen_rewards, rejected_rewards + + @staticmethod + def get_batch_logps( + logits: torch.FloatTensor, + labels: torch.LongTensor, + average_log_prob: bool = False, + label_pad_token_id: int = -100, + is_encoder_decoder: bool = False, + ) -> torch.FloatTensor: + """Compute the log probabilities of the given labels under the given logits. + + Args: + logits: Logits of the model (unnormalized). Shape: (batch_size, sequence_length, vocab_size) + labels: Labels for which to compute the log probabilities. Label tokens with a value of label_pad_token_id are ignored. Shape: (batch_size, sequence_length) + average_log_prob: If True, return the average log probability per (non-masked) token. Otherwise, return the sum of the log probabilities of the (non-masked) tokens. + label_pad_token_id: The label pad token id. + is_encoder_decoder: Whether the model is an encoder-decoder model. + + Returns: + A tensor of shape (batch_size,) containing the average/sum log probabilities of the given labels under the given logits. + """ + if logits.shape[:-1] != labels.shape: + raise ValueError("Logits (batch and sequence length dim) and labels must have the same shape.") + + if not is_encoder_decoder: + labels = labels[:, 1:].clone() + logits = logits[:, :-1, :] + loss_mask = labels != label_pad_token_id + + # dummy token; we'll ignore the losses on these tokens later + labels[labels == label_pad_token_id] = 0 + + per_token_logps = torch.gather(logits.log_softmax(-1), dim=2, index=labels.unsqueeze(2)).squeeze(2) + + if average_log_prob: + return (per_token_logps * loss_mask).sum(-1) / loss_mask.sum(-1) + else: + return (per_token_logps * loss_mask).sum(-1) + + def get_sft_loss(self, logits, labels): + # Shift so that tokens < n predict n + shift_logits = logits[..., :-1, :].contiguous() + shift_labels = labels[..., 1:].contiguous() + # Flatten the tokens + loss_fct = nn.CrossEntropyLoss() + shift_logits = shift_logits.view(-1, shift_logits.size(-1)) + shift_labels = shift_labels.view(-1) + # Enable model/pipeline parallelism + shift_labels = shift_labels.to(shift_logits.device) + loss = loss_fct(shift_logits, shift_labels) + return loss + + def concatenated_forward(self, model: nn.Module, batch: Dict[str, Union[List, torch.LongTensor]]) -> Tuple[torch.FloatTensor, torch.FloatTensor, torch.FloatTensor, torch.FloatTensor]: + """Run the given model on the given batch of inputs, concatenating the chosen and rejected inputs together. + + We do this to avoid doing two forward passes, because it's faster for FSDP. + """ + # import pdb; pdb.set_trace() + concatenated_batch = self.concatenated_inputs( + batch, + is_encoder_decoder=self.is_encoder_decoder, + label_pad_token_id=self.label_pad_token_id, + padding_value=self.padding_value, + device=self.accelerator.device, + ) + len_chosen = batch["chosen_labels"].shape[0] + + # import pdb; pdb.set_trace() + all_logits, new_labels = model( + concatenated_batch["concatenated_input_ids"], + attention_mask=concatenated_batch["concatenated_attention_mask"], + labels=concatenated_batch["concatenated_labels"], + images=concatenated_batch["concatenated_images"], + image_sizes=concatenated_batch["image_sizes"], + modalities=concatenated_batch["modalities"], + use_cache=False, + dpo_forward=True, + ) + all_logits = all_logits.to(torch.float32) + all_logps = self.get_batch_logps( + all_logits, + new_labels, + average_log_prob=self.loss_type == "ipo", + is_encoder_decoder=self.is_encoder_decoder, + label_pad_token_id=self.label_pad_token_id, + ) + + chosen_logps = all_logps[:len_chosen] + rejected_logps = all_logps[len_chosen:] + + # don't count image embeds logits + # loss_mask = new_labels != -100 + # logits = [all_logits[i][loss_mask[i]] for i in range(loss_mask.shape[0])] + # chosen_logits = logits[:len_chosen] + # rejected_logits = logits[len_chosen:] + # chosen_logits = [l.detach().cpu().mean() for l in chosen_logits] + # rejected_logits = [l.detach().cpu().mean() for l in rejected_logits] + # chosen_logits = sum(chosen_logits)/len_chosen + # rejected_logits = sum(rejected_logits)/len_chosen + + chosen_logits = all_logits[:len_chosen] + rejected_logits = all_logits[len_chosen:] + + chosen_labels = new_labels[:len_chosen] + rejected_labels = new_labels[len_chosen:] + + return (chosen_logps, rejected_logps, chosen_logits, rejected_logits, chosen_labels, rejected_labels) + + def get_batch_loss_metrics( + self, + model, + batch: Dict[str, Union[List, torch.LongTensor]], + train_eval: Literal["train", "eval"] = "train", + ): + """Compute the DPO loss and other metrics for the given batch of inputs for train or test. + CHANGE: 1. add sft loss + 2. all gather metrics + """ + metrics = {} + + ( + policy_chosen_logps, + policy_rejected_logps, + policy_chosen_logits, + policy_rejected_logits, + chosen_labels, + rejected_labels, + ) = self.concatenated_forward(model, batch) + + # if reference_chosen_logps and reference_rejected_logps in batch use them, otherwise use the reference model + if "reference_chosen_logps" in batch and "reference_rejected_logps" in batch: + reference_chosen_logps = batch["reference_chosen_logps"] + reference_rejected_logps = batch["reference_rejected_logps"] + else: + with torch.no_grad(): + if self.ref_model is None: + with self.null_ref_context(): + ( + reference_chosen_logps, + reference_rejected_logps, + ) = self.concatenated_forward( + self.model, batch + )[:2] + else: + ( + reference_chosen_logps, + reference_rejected_logps, + ) = self.concatenated_forward( + self.ref_model, batch + )[:2] + + unscaled_dpo_losses, chosen_rewards, rejected_rewards = self.dpo_loss( + policy_chosen_logps, + policy_rejected_logps, + reference_chosen_logps, + reference_rejected_logps, + ) + unscaled_dpo_losses = unscaled_dpo_losses.mean() + dpo_losses = unscaled_dpo_losses * self.dpo_alpha + unscaled_sft_loss = self.get_sft_loss(policy_chosen_logits, chosen_labels) + sft_loss = unscaled_sft_loss * self.gamma + + # print(sft_loss.shape, dpo_losses.shape) + losses = dpo_losses + sft_loss + # losses = sft_loss # sft only + # losses = dpo_losses # dpo only + reward_accuracies = (chosen_rewards > rejected_rewards).float() + + def all_gather_tensor(tensor): + if torch.distributed.is_available() and torch.distributed.is_initialized(): + tensor = tensor.detach() + gathered_tensor = [torch.zeros_like(tensor) for _ in range(torch.distributed.get_world_size())] + torch.distributed.all_gather(gathered_tensor, tensor) + tensor = torch.cat(gathered_tensor, dim=0) + # else: + # print('not distributed') + return tensor + + # gather chosen_rewards across devices + chosen_rewards = all_gather_tensor(chosen_rewards) + rejected_rewards = all_gather_tensor(rejected_rewards) + reward_accuracies = all_gather_tensor(reward_accuracies) + policy_chosen_logps = all_gather_tensor(policy_chosen_logps) + policy_rejected_logps = all_gather_tensor(policy_rejected_logps) + reference_chosen_logps = all_gather_tensor(reference_chosen_logps) + reference_rejected_logps = all_gather_tensor(reference_rejected_logps) + + prefix = "eval_" if train_eval == "eval" else "" + metrics[f"{prefix}losses/dpo"] = unscaled_dpo_losses.cpu() + metrics[f"{prefix}losses/sft"] = unscaled_sft_loss.cpu() + metrics[f"{prefix}losses/total"] = losses.cpu() + metrics[f"{prefix}rewards/chosen"] = chosen_rewards.mean().cpu() + metrics[f"{prefix}rewards/rejected"] = rejected_rewards.mean().cpu() + metrics[f"{prefix}rewards/accuracies"] = reward_accuracies.mean().cpu() + metrics[f"{prefix}rewards/margins"] = (chosen_rewards - rejected_rewards).mean().cpu() + # policy logps + metrics[f"{prefix}logps/rejected"] = policy_rejected_logps.detach().mean().cpu() + metrics[f"{prefix}logps/chosen"] = policy_chosen_logps.detach().mean().cpu() + # policy logits (exclude image tokens) + # metrics[f"{prefix}logits/rejected"] =policy_rejected_logits + # metrics[f"{prefix}logits/chosen"] = policy_chosen_logits + # reference logps + metrics[f"{prefix}ref_logps/rejected"] = reference_rejected_logps.mean().cpu() + metrics[f"{prefix}ref_logps/chosen"] = reference_chosen_logps.mean().cpu() + + # metrics all pick .4 digits + # for k in metrics: + # metrics[k] = round(metrics[k].item(), 4) + + return losses, metrics + + def compute_loss( + self, + model: Union[PreTrainedModel, nn.Module], + inputs: Dict[str, Union[torch.Tensor, Any]], + return_outputs=False, + ) -> Union[torch.Tensor, Tuple[torch.Tensor, Dict[str, torch.Tensor]]]: + if not self.use_dpo_data_collator: + warnings.warn( + "compute_loss is only implemented for DPODataCollatorWithPadding, and you passed a datacollator that is different than " + "DPODataCollatorWithPadding - you might see unexpected behavior. Alternatively, you can implement your own prediction_step method if you are using a custom data collator" + ) + + compute_loss_context_manager = torch.cuda.amp.autocast if self._peft_has_been_casted_to_bf16 else nullcontext + + with compute_loss_context_manager(): + loss, metrics = self.get_batch_loss_metrics(model, inputs, train_eval="train") + + # force log the metrics + self.store_metrics(metrics, train_eval="train") + + if return_outputs: + return (loss, metrics) + return loss + + def get_batch_samples(self, model, batch: Dict[str, torch.LongTensor]) -> Tuple[str, str]: + """Generate samples from the model and reference model for the given batch of inputs.""" + + # If one uses `generate_during_eval` with peft + bf16, we need to explictly call generate with + # the torch cuda amp context manager as some hidden states are silently casted to full precision. + generate_context_manager = nullcontext if not self._peft_has_been_casted_to_bf16 else torch.cuda.amp.autocast + + with generate_context_manager(): + policy_output = model.generate( + input_ids=batch["prompt_input_ids"], + attention_mask=batch["prompt_attention_mask"], + max_length=self.max_length, + do_sample=True, + pad_token_id=self.tokenizer.pad_token_id, + ) + + # if reference_output in batch use that otherwise use the reference model + if "reference_output" in batch: + reference_output = batch["reference_output"] + else: + if self.ref_model is None: + with self.null_ref_context(): + reference_output = self.model.generate( + input_ids=batch["prompt_input_ids"], + attention_mask=batch["prompt_attention_mask"], + max_length=self.max_length, + do_sample=True, + pad_token_id=self.tokenizer.pad_token_id, + ) + else: + reference_output = self.ref_model.generate( + input_ids=batch["prompt_input_ids"], + attention_mask=batch["prompt_attention_mask"], + max_length=self.max_length, + do_sample=True, + pad_token_id=self.tokenizer.pad_token_id, + ) + + policy_output = pad_to_length(policy_output, self.max_length, self.tokenizer.pad_token_id) + policy_output_decoded = self.tokenizer.batch_decode(policy_output, skip_special_tokens=True) + + reference_output = pad_to_length(reference_output, self.max_length, self.tokenizer.pad_token_id) + reference_output_decoded = self.tokenizer.batch_decode(reference_output, skip_special_tokens=True) + + return policy_output_decoded, reference_output_decoded + + def prediction_step( + self, + model: Union[PreTrainedModel, nn.Module], + inputs: Dict[str, Union[torch.Tensor, Any]], + prediction_loss_only: bool, + ignore_keys: Optional[List[str]] = None, + ): + if not self.use_dpo_data_collator: + warnings.warn( + "prediction_step is only implemented for DPODataCollatorWithPadding, and you passed a datacollator that is different than " + "DPODataCollatorWithPadding - you might see unexpected behavior. Alternatively, you can implement your own prediction_step method if you are using a custom data collator" + ) + if ignore_keys is None: + if hasattr(model, "config"): + ignore_keys = getattr(model.config, "keys_to_ignore_at_inference", []) + else: + ignore_keys = [] + + prediction_context_manager = torch.cuda.amp.autocast if self._peft_has_been_casted_to_bf16 else nullcontext + + with torch.no_grad(), prediction_context_manager(): + loss, metrics = self.get_batch_loss_metrics(model, inputs, train_eval="eval") + + # force log the metrics + self.store_metrics(metrics, train_eval="eval") + + if prediction_loss_only: + return (loss.detach(), None, None) + + # logits for the chosen and rejected samples from model + logits_dict = { + "eval_logits/chosen": metrics["eval_logits/chosen"], + "eval_logits/rejected": metrics["eval_logits/rejected"], + } + logits = tuple(v.unsqueeze(dim=0) for k, v in logits_dict.items() if k not in ignore_keys) + logits = torch.stack(logits).mean(axis=1).to(self.accelerator.device) + labels = torch.zeros(logits.shape[0], device=self.accelerator.device) + + return (loss.detach(), logits, labels) + + def store_metrics(self, metrics: Dict[str, float], train_eval: Literal["train", "eval"] = "train") -> None: + for key, value in metrics.items(): + self._stored_metrics[train_eval][key].append(value) + + def evaluation_loop( + self, + dataloader: DataLoader, + description: str, + prediction_loss_only: Optional[bool] = None, + ignore_keys: Optional[List[str]] = None, + metric_key_prefix: str = "eval", + ) -> EvalLoopOutput: + """ + Overriding built-in evaluation loop to store metrics for each batch. + Prediction/evaluation loop, shared by `Trainer.evaluate()` and `Trainer.predict()`. + + Works both with or without labels. + """ + + # Sample and save to game log if requested (for one batch to save time) + if self.generate_during_eval: + # Generate random indices within the range of the total number of samples + num_samples = len(dataloader.dataset) + random_indices = random.sample(range(num_samples), k=self.args.eval_batch_size) + + # Use dataloader.dataset.select to get the random batch without iterating over the DataLoader + random_batch_dataset = dataloader.dataset.select(random_indices) + random_batch = self.data_collator(random_batch_dataset) + random_batch = self._prepare_inputs(random_batch) + + policy_output_decoded, ref_output_decoded = self.get_batch_samples(self.model, random_batch) + + self.log( + { + "game_log": wandb.Table( + columns=["Prompt", "Policy", "Ref Model"], + rows=[[prompt, pol[len(prompt) :], ref[len(prompt) :]] for prompt, pol, ref in zip(random_batch["prompt"], policy_output_decoded, ref_output_decoded)], + ) + } + ) + self.state.log_history.pop() + + # Base evaluation + initial_output = super().evaluation_loop(dataloader, description, prediction_loss_only, ignore_keys, metric_key_prefix) + + return initial_output + + def log(self, logs: Dict[str, float]) -> None: + """ + Log `logs` on the various objects watching training, including stored metrics. + + Args: + logs (`Dict[str, float]`): + The values to log. + """ + # logs either has 'loss' or 'eval_loss' + train_eval = "train" if "loss" in logs else "eval" + # Add averaged stored metrics to logs + for key, metrics in self._stored_metrics[train_eval].items(): + logs[key] = torch.tensor(metrics).mean().item() + del self._stored_metrics[train_eval] + return super().log(logs) + + @wraps(Trainer.push_to_hub) + def push_to_hub(self, commit_message: Optional[str] = "End of training", blocking: bool = True, **kwargs) -> str: + """ + Overwrite the `push_to_hub` method in order to force-add the tag "sft" when pushing the + model on the Hub. Please refer to `~transformers.Trainer.push_to_hub` for more details. + """ + kwargs = trl_sanitze_kwargs_for_tagging(model=self.model, tag_names=self._tag_names, kwargs=kwargs) + + return super().push_to_hub(commit_message=commit_message, blocking=blocking, **kwargs) diff --git a/trl/trainer/iterative_sft_trainer.py b/trl/trainer/iterative_sft_trainer.py new file mode 100644 index 0000000..1fd5544 --- /dev/null +++ b/trl/trainer/iterative_sft_trainer.py @@ -0,0 +1,334 @@ +# Copyright 2023 The HuggingFace Team. 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. +import warnings +from typing import Callable, Dict, List, Optional, Tuple, Union + +import torch +from datasets import Dataset +from torch.utils.data import DataLoader +from transformers import ( + DataCollator, + DataCollatorForLanguageModeling, + DataCollatorForSeq2Seq, + PreTrainedModel, + PreTrainedTokenizerBase, + Trainer, + TrainingArguments, +) +from transformers.trainer_utils import EvalLoopOutput + +from ..core import PPODecorators +from ..import_utils import is_peft_available + + +if is_peft_available(): + from peft import PeftModel + + +class IterativeSFTTrainer(Trainer): + """ + The IterativeSFTTrainer can be used to finetune models with methods that requires some steps between optimization. + + Attributes: + **model** (`PreTrainedModel`) -- Model to be optimized, either an 'AutoModelForCausalLM' or an 'AutoModelForSeq2SeqLM'. + Check the documentation of `PreTrainedModel` for more details. + **args** (`transformers.TrainingArguments`): -- The arguments to use for training. + **tokenizer** (`PreTrainedTokenizerBase`) -- Tokenizer to be used for encoding the + data. Check the documentation of `transformers.PreTrainedTokenizer` and + `transformers.PreTrainedTokenizerFast` for more details. + **optimizers** (`Tuple[torch.optim.Optimizer, torch.optim.lr_scheduler.LambdaLR]`): -- The optimizer and scheduler to use for training. + **data_collator** (Union[DataCollatorForLanguageModeling, DataCollatorForSeq2Seq], *optional*) -- Data collator to be used for training and + passed along the dataloader. + **eval_dataset** (`datasets.Dataset`): The dataset to use for evaluation. + **max_length** (`int`, defaults to `None`): -- The maximum length of the input. + **truncation_mode** (`str`, defaults to `keep_end`): -- The truncation mode to use, either `keep_end` or `keep_start`. + **preprocess_logits_for_metrics** (`Callable[[torch.Tensor, torch.Tensor], torch.Tensor]`): -- The function to use to preprocess the logits before computing the metrics. + **compute_metrics** (`Callable[[EvalPrediction], Dict]`, *optional*): -- The function to use to compute the metrics. Must take a `EvalPrediction` and return a dictionary string to metric values. + **optimize_device_cache ** (`bool`, *optional*, defaults to `False`) -- Optimize CUDA cache for slightly more memory-efficient training. + """ + + def __init__( + self, + model: PreTrainedModel = None, + args: TrainingArguments = None, + tokenizer: PreTrainedTokenizerBase = None, + optimizers: Tuple[torch.optim.Optimizer, torch.optim.lr_scheduler.LambdaLR] = ( + None, + None, + ), + data_collator: Optional[DataCollator] = None, + eval_dataset: Optional[Union[Dataset, Dict[str, Dataset]]] = None, + max_length: Optional[int] = None, + truncation_mode: Optional[str] = "keep_end", + preprocess_logits_for_metrics: Optional[Callable[[torch.Tensor, torch.Tensor], torch.Tensor]] = None, + compute_metrics: Optional[Callable[[EvalLoopOutput], Dict]] = None, + optimize_device_cache: Optional[bool] = False, + ): + # Step 0: check positional arguments validity + if not isinstance(tokenizer, (PreTrainedTokenizerBase)): + raise ValueError(f"tokenizer must be a PreTrainedTokenizerBase like a PreTrainedTokenizer or a PreTrainedTokenizerFast, got {type(tokenizer)}") + if not isinstance(model, PreTrainedModel): + raise ValueError(f"model must be a PreTrainedModel, got {type(model)}") + if not model.can_generate(): + warnings.warn(f"The current model class {type(model)} is not compatible with `.generate()`" "Please make sure that this is intended.") + if optimizers[1] is None and args.max_steps == -1: + raise ValueError("When no scheduler is provided, you need to set the total number of training steps to perform `max_steps`") + + self.is_encoder_decoder = getattr(model.config, "is_encoder_decoder", False) + self.is_peft_model = is_peft_available() and isinstance(model, PeftModel) + + self.tokenizer = tokenizer + + if data_collator is None: + if self.is_encoder_decoder: + warnings.warn("No data collator is provided. Using 'DataCollatorForSeq2Seq' with" "'labels_pad_token_id' set to '-100' and 'pad_to_multiple_of' set to 8.") + self.data_collator = DataCollatorForSeq2Seq(tokenizer, label_pad_token_id=-100, pad_to_multiple_of=8) + else: + warnings.warn("No data collator is provided. Using 'DataCollatorForLanguageModeling'") + self.data_collator = DataCollatorForLanguageModeling(self.tokenizer, mlm=False) + else: + self.data_collator = data_collator + + self.max_length = max_length + self.truncation_mode = truncation_mode + self.optimize_device_cache = optimize_device_cache + + super().__init__( + model=model, + args=args, + data_collator=self.data_collator, + eval_dataset=eval_dataset, + tokenizer=tokenizer, + compute_metrics=compute_metrics, + optimizers=optimizers, + preprocess_logits_for_metrics=preprocess_logits_for_metrics, + ) + + self.create_optimizer_and_scheduler(self.args.max_steps) + + # prepare model, optimizer and lr_scheduler + self.model, self.optimizer, self.lr_scheduler = self.accelerator.prepare(self.model, self.optimizer, self.lr_scheduler) + + self.tokenizer.truncation_side = "left" if self.truncation_mode == "keep_end" else "right" + + if not hasattr(self, "accelerator"): + raise AttributeError("Your `Trainer` does not have an `accelerator` object. Consider upgrading `transformers`.") + + PPODecorators.optimize_device_cache = self.optimize_device_cache + + def prepare_model_inputs(self, input_ids: torch.Tensor, attention_mask: torch.Tensor, labels: torch.Tensor): + if attention_mask is None: + attention_mask = [torch.ones_like(ids) for ids in input_ids] + + if self.is_encoder_decoder: + input_data = self.data_collator([{"input_ids": ids, "attention_mask": att, "labels": lab} for ids, att, lab in zip(input_ids, attention_mask, labels)]).to(self.model.device) + + input_data.pop("decoder_input_ids", None) # This is directly computed inside the model + + input_data["labels"][input_data["labels"] == self.tokenizer.pad_token_id] = -100 + + else: + input_data = self.data_collator([{"input_ids": ids, "attention_mask": att} for ids, att in zip(input_ids, attention_mask)]).to(self.model.device) + + # truncate in case the user has provided input_ids, attention_mask and labels + if self.max_length is not None: + if self.truncation_mode == "keep_start": + input_data = {k: v[: self.max_length] for k, v in input_data.items()} + elif self.truncation_mode == "keep_end": + input_data = {k: v[-self.max_length :] for k, v in input_data.items()} + else: + raise ValueError(f"Unknown truncation mode: {self.truncation_mode}") + + return input_data + + @staticmethod + def _step_safety_checker( + input_ids: List[torch.LongTensor], + attention_mask: List[torch.LongTensor], + labels: List[torch.LongTensor], + texts: List[str], + texts_labels: List[str], + ): + """ + Check if the input data is valid for training. + + Args: + input_ids (List[`torch.LongTensor`]): + List of tensors containing the input_ids + attention_mask (List[`torch.LongTensor`]): + List of tensors containing the attention_mask + labels (List[`torch.FloatTensor`]): + List of tensors containing the labels + texts (List[`str`]): + List of string containing the text input. + texts_labels (List[`str`]): + List of string containing the text labels. + Returns: + `tuple`: The input data. + """ + if texts is None: + if attention_mask is None: + for name, tensor_list in zip(["input_ids", "labels"], [input_ids, labels]): + if not isinstance(tensor_list, list): + raise ValueError(f"{name} must be a list of tensors - got {type(tensor_list)}") + if not isinstance(tensor_list[0], torch.Tensor): + raise ValueError(f"Elements in {name} must be tensors - got {type(tensor_list[0])}") + else: + for name, tensor_list in zip(["input_ids", "attention_mask", "labels"], [input_ids, attention_mask, labels]): + if not isinstance(tensor_list, list): + raise ValueError(f"{name} must be a list of tensors - got {type(tensor_list)}") + if not isinstance(tensor_list[0], torch.Tensor): + raise ValueError(f"Elements in {name} must be tensors - got {type(tensor_list[0])}") + else: + if not isinstance(texts, list): + raise ValueError(f"'text' must be a list of strings - got {type(texts)}") + if not isinstance(texts[0], str): + raise ValueError(f"Elements in 'text' must be strings - got {type(texts[0])}") + if texts_labels is not None: + if not isinstance(texts_labels, list): + raise ValueError(f"'text_labels' must be a list of strings - got {type(texts_labels)}") + if not isinstance(texts_labels[0], str): + raise ValueError(f"Elements in 'text_labels' must be strings - got {type(texts_labels[0])}") + + return input_ids, attention_mask, labels, texts, texts_labels + + @PPODecorators.empty_device_cache() + def step( + self, + input_ids: Optional[List[torch.LongTensor]] = None, + attention_mask: Optional[List[torch.LongTensor]] = None, + labels: Optional[List[torch.LongTensor]] = None, + texts: Optional[List[str]] = None, + texts_labels: Optional[List[str]] = None, + ): + """ + Run an optimisation step given a list of input_ids, attention_mask, and labels or a list of text and text_labels. + Args: + input_ids (List[`torch.LongTensor`]): + List of tensors containing the input_ids (if not provided, text will be used) + attention_mask (List[`torch.LongTensor`], , *optional*): + List of tensors containing the attention_mask + labels (List[`torch.FloatTensor`], *optional*): + List of tensors containing the labels (if set to None, will default to input_ids) + texts (List[`str`], *optional*): + List of strings containing the text input (if not provided, input_ids will directly be used) + texts_labels (List[`str`], *optional*): + List of strings containing the text labels (if set to None, will default to text) + Returns: + `dict[str, Any]`: A summary of the training statistics + """ + self.model.train() + + if self.state.global_step == 0: + self.tr_loss = torch.tensor(0.0).to(self.args.device) + self._globalstep_last_logged = self.state.global_step + + if input_ids is None and texts is None: + raise ValueError("Step should include `input_ids` or `texts` as keyword arguments.") + elif input_ids is not None and texts is not None: + warnings.warn("Both 'input_ids' and 'texts' are provided. 'input_ids' will be overwritten using inputs provided by the 'texts' keyword argument.") + + if labels is None and texts_labels is None and self.is_encoder_decoder: + raise ValueError("No 'labels' or 'text_labels' are provided. When using an encoder-decoder architecture, 'labels' or 'text_labels' must be passed.") + + input_ids, attention_mask, labels, texts, texts_labels = self._step_safety_checker(input_ids, attention_mask, labels, texts, texts_labels) + + if texts is not None: + model_inputs = self.tokenizer(texts, max_length=self.max_length, truncation=True, padding=True, return_tensors="pt") + + input_ids, attention_mask = model_inputs["input_ids"], model_inputs["attention_mask"] + + if texts_labels is not None: + labels = self.tokenizer(texts, max_length=self.max_length, truncation=True, padding=True, return_tensors="pt")["input_ids"] + + if labels is None: + warnings.warn("No labels are provided. Setting labels to input_ids") + labels = input_ids + + model_inputs = self.prepare_model_inputs(input_ids, attention_mask, labels) + + model_inputs_names = list(model_inputs.keys()) + + batch_dict = {} + batch_dict.update(model_inputs) + + def collator(data): + return_dict = dict() + for key in data[0]: + if key in ["input_ids", "attention_mask", "labels"]: + return_dict[key] = torch.stack([d[key] for d in data]).to(self.model.device) + return return_dict + + batch_data = Dataset.from_dict(batch_dict) + batch_data.set_format("torch") + + step_dataloader = DataLoader( + batch_data, + batch_size=self.args.per_device_train_batch_size, + shuffle=True, + collate_fn=collator, + ) + + for _, batch in enumerate(step_dataloader): + with self.accelerator.accumulate(self.model): + model_inputs = {k: batch[k] for k in model_inputs_names} + loss = self.compute_loss(self.model, model_inputs) + + if self.args.n_gpu > 1: + loss = loss.mean() + + tr_loss_step = loss.detach() + + self.accelerator.backward(loss) + + if self.accelerator.sync_gradients and self.args.max_grad_norm is not None: + self.accelerator.clip_grad_norm_( + self.model.parameters(), + self.args.max_grad_norm, + ) + + self.optimizer.step() + self.optimizer.zero_grad() + if self.lr_scheduler is not None: + self.lr_scheduler.step() + + self.state.global_step += 1 + + # update stats etc + self.tr_loss += tr_loss_step + + self._maybe_log_save_evaluate() + + def _maybe_log_save_evaluate(self): + # check if eval is required + if self.args.eval_steps is not None: + if self.state.global_step % self.args.eval_steps == 0 and self.state.global_step != 0: + self.evaluate(self.eval_dataset) + + # check if logging is required + if self.args.logging_steps is not None: + if self.state.global_step % self.args.logging_steps == 0 and self.state.global_step != 0: + logs: Dict[str, float] = {} + + tr_loss_scalar = self._nested_gather(self.tr_loss).mean().item() + + # reset tr_loss to zero + self.tr_loss -= self.tr_loss + + logs["loss"] = round(tr_loss_scalar / (self.state.global_step - self._globalstep_last_logged), 4) + logs["learning_rate"] = self._get_learning_rate() + + self._globalstep_last_logged = self.state.global_step + + self.log(logs) diff --git a/trl/trainer/model_config.py b/trl/trainer/model_config.py new file mode 100644 index 0000000..e6df859 --- /dev/null +++ b/trl/trainer/model_config.py @@ -0,0 +1,71 @@ +from dataclasses import dataclass, field +from typing import List, Optional + +from ..core import flatten_dict + + +@dataclass +class ModelConfig: + """ + Arguments which define the model and tokenizer to load. + """ + + model_name_or_path: Optional[str] = field( + default=None, + metadata={"help": ("The model checkpoint for weights initialization.")}, + ) + model_revision: str = field( + default="main", + metadata={"help": "The specific model version to use (can be a branch name, tag name or commit id)."}, + ) + torch_dtype: Optional[str] = field( + default=None, + metadata={ + "help": ("Override the default `torch.dtype` and load the model under this dtype. If `auto` is passed, the " "dtype will be automatically derived from the model's weights."), + "choices": ["auto", "bfloat16", "float16", "float32"], + }, + ) + trust_remote_code: bool = field(default=False, metadata={"help": "Trust remote code when loading a model."}) + attn_implementation: Optional[str] = field( + default=None, + metadata={"help": ("Which attention implementation to use; you can run --attn_implementation=flash_attention_2, in which case you must install this manually by running `pip install flash-attn --no-build-isolation`")}, + ) + use_peft: bool = field( + default=False, + metadata={"help": ("Whether to use PEFT or not for training.")}, + ) + lora_r: Optional[int] = field( + default=16, + metadata={"help": ("LoRA R value.")}, + ) + lora_alpha: Optional[int] = field( + default=32, + metadata={"help": ("LoRA alpha.")}, + ) + lora_dropout: Optional[float] = field( + default=0.05, + metadata={"help": ("LoRA dropout.")}, + ) + lora_target_modules: Optional[List[str]] = field( + default=None, + metadata={"help": ("LoRA target modules.")}, + ) + lora_modules_to_save: Optional[List[str]] = field( + default=None, + metadata={"help": ("Model layers to unfreeze & train")}, + ) + load_in_8bit: bool = field(default=False, metadata={"help": "use 8 bit precision for the base model - works only with LoRA"}) + load_in_4bit: bool = field(default=False, metadata={"help": "use 4 bit precision for the base model - works only with LoRA"}) + + bnb_4bit_quant_type: Optional[str] = field(default="nf4", metadata={"help": "precise the quantization type (fp4 or nf4)"}) + use_bnb_nested_quant: bool = field(default=False, metadata={"help": "use nested quantization"}) + + def to_dict(self): + output_dict = {} + for key, value in self.__dict__.items(): + output_dict[key] = value + return flatten_dict(output_dict) + + def __post_init__(self): + if self.load_in_8bit and self.load_in_4bit: + raise ValueError("You can't use 8 bit and 4 bit precision at the same time") diff --git a/trl/trainer/ppo_config.py b/trl/trainer/ppo_config.py new file mode 100644 index 0000000..8642eab --- /dev/null +++ b/trl/trainer/ppo_config.py @@ -0,0 +1,175 @@ +# Copyright 2022 The HuggingFace Team. 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. +import json +import os +import sys +import warnings +from dataclasses import dataclass, field +from typing import Literal, Optional + +import numpy as np +import tyro +from typing_extensions import Annotated + +from trl.trainer.utils import exact_div + +from ..core import flatten_dict +from ..import_utils import is_wandb_available + + +JSONDict = Annotated[Optional[dict], tyro.conf.arg(metavar="JSON", constructor=json.loads)] + + +@dataclass +class PPOConfig: + """ + Configuration class for PPOTrainer + """ + + # common parameters + exp_name: str = os.path.basename(sys.argv[0])[: -len(".py")] + """the name of this experiment (by default is the file name without the extension name)""" + seed: int = 0 + """Seed value for random generations""" + log_with: Optional[Literal["wandb", "tensorboard"]] = None + """Log with either 'wandb' or 'tensorboard', check https://huggingface.co/docs/accelerate/usage_guides/tracking for more details""" + task_name: Optional[str] = None + """Name of task to use - used only for tracking purposes""" + model_name: Optional[str] = "gpt2" + """Name of model to use - used only for tracking purposes""" + query_dataset: Optional[str] = "imdb" + """Name of dataset to query - used only for tracking purposes""" + reward_model: Optional[str] = "sentiment-analysis:lvwerra/distilbert-imdb" + """The reward model to use - used only for tracking purposes""" + remove_unused_columns: bool = True + """Remove unused columns from the dataset if `datasets.Dataset` is used""" + tracker_kwargs: JSONDict = field(default_factory=dict) + """Keyword arguments for the tracker (e.g. python ppo.py --tracker_kwargs='{"wandb": {"entity": "my_wandb_entity", "name": "my_exp_name"}}'""" + accelerator_kwargs: JSONDict = field(default_factory=dict) + """Keyword arguments for the accelerator""" + project_kwargs: JSONDict = field(default_factory=dict) + """Keyword arguments for the accelerator project config (e.g. `logging_dir`)""" + tracker_project_name: str = "trl" + """Name of project to use for tracking""" + push_to_hub_if_best_kwargs: JSONDict = field(default_factory=dict) + """Keyword arguments for pushing model to the hub during training (e.g. repo_id)""" + + # hyperparameters + steps: int = 20000 + """Number of training steps""" + learning_rate: float = 1.41e-5 + """Adam learning rate""" + adap_kl_ctrl: bool = True + """Use adaptive KL control, otherwise linear""" + init_kl_coef: Optional[float] = 0.2 + """Initial KL penalty coefficient (used for adaptive and linear control)""" + kl_penalty: Literal["kl", "abs", "mse", "full"] = "kl" + """kl penalty options: 'kl': model_logp - ref_logp, 'abs': abs(kl), 'mse': mean squared error mse(kl) and 'full': the actual kl for all tokens in the distribution""" + target: Optional[float] = 6 + """Target KL value for adaptive KL control""" + horizon: Optional[float] = 10000 + """Horizon for adaptive KL control""" + gamma: float = 1 + """Gamma parameter for advantage calculation""" + lam: float = 0.95 + """Lambda parameter for advantage calculation""" + cliprange: float = 0.2 + """Range for clipping in PPO policy gradient loss""" + cliprange_value: float = 0.2 + """Range for clipping values in loss calculation""" + vf_coef: float = 0.1 + """Scaling factor for value loss""" + batch_size: int = 128 + """Number of samples per optimisation step""" + forward_batch_size: Optional[int] = None + """DEPRECATED: use `mini_batch_size` instead, which does the same thing.""" + mini_batch_size: int = 128 + """Number of samples optimized in each mini batch""" + gradient_accumulation_steps: int = 1 + """The number of gradient accumulation steps""" + world_size: tyro.conf.Suppress[int] = None + """The world size for distributed training""" + ppo_epochs: int = 4 + """Number of optimisation epochs per batch of samples""" + max_grad_norm: Optional[float] = None + """Maximum gradient norm for gradient clipping""" + optimize_cuda_cache: Optional[bool] = None + """DEPRECATED: use `optimize_device_cache` instead, which does the same thing.""" + optimize_device_cache: Optional[bool] = False + """Optimize device cache for slightly more memory-efficient training""" + early_stopping: bool = False + """Whether to stop the PPO optimization loop early is the KL too high""" + target_kl: float = 1 + """Stop early if we exceed this value by over 50%""" + compare_steps: int = 1 + """Number of steps between comparison of the current reward with the best seen so far""" + ratio_threshold: float = 10.0 + """Skip mini-batches with high PPO ratios that can cause loss spikes""" + use_score_scaling: bool = False + """Use score scaling""" + use_score_norm: bool = False + """Use score normalization. Only applicable if use_score_scaling is True""" + score_clip: Optional[float] = None + """Score clipping""" + whiten_rewards: bool = False + """Whiten the rewards before compute advantages""" + + # computed hyperparameters at runtime; we use `tyro.conf.Suppress` to hide them from the help text + is_encoder_decoder: Optional[tyro.conf.Suppress[bool]] = None + """TO BE FILLED In RUNTIME: Whether the model is an encoder-decoder model""" + is_peft_model: Optional[tyro.conf.Suppress[bool]] = None + """TO BE FILLED In RUNTIME: Whether the model is a PEFT model""" + backward_batch_size: tyro.conf.Suppress[int] = None + """TO BE FILLED In RUNTIME: Number of samples optimized in an `optimizer.step()` call""" + global_backward_batch_size: tyro.conf.Suppress[int] = None + """TO BE FILLED In RUNTIME: the effective `backward_batch_size` across all processes""" + global_batch_size: tyro.conf.Suppress[int] = None + """TO BE FILLED In RUNTIME: the effective `batch_size` across all processes""" + + if optimize_cuda_cache is not None: + warnings.warn("The `optimize_cuda_cache` argument will be deprecated soon, please use `optimize_device_cache` instead.") + optimize_device_cache = optimize_cuda_cache + else: + optimize_device_cache = False + + def __post_init__(self): + if self.forward_batch_size is not None: + warnings.warn( + "Note that using `forward_batch_size` is deprecated, use `mini_batch_size` instead. By setting it you overwrite `mini_batch_size` which affects both the batch size during forward passes and also the mini batch size for PPO optimization." + ) + self.mini_batch_size = self.forward_batch_size + + self.backward_batch_size = self.mini_batch_size * self.gradient_accumulation_steps + exact_div( + self.batch_size, + self.backward_batch_size, + "`batch_size`", + "`mini_batch_size * gradient_accumulation_steps`", + "`batch_size` must be a multiple of `mini_batch_size * gradient_accumulation_steps`", + ) + + # check if wandb is installed + if self.log_with == "wandb": + # raise error if wandb is not installed + if not is_wandb_available(): + raise ImportError("Please install wandb to use wandb logging. You can do this by running `pip install wandb`.") + + self.total_ppo_epochs = int(np.ceil(self.steps / self.batch_size)) + assert self.kl_penalty in ["kl", "abs", "mse", "full"] + + def to_dict(self): + output_dict = {} + for key, value in self.__dict__.items(): + output_dict[key] = value + return flatten_dict(output_dict) diff --git a/trl/trainer/ppo_trainer.py b/trl/trainer/ppo_trainer.py new file mode 100644 index 0000000..ce7e093 --- /dev/null +++ b/trl/trainer/ppo_trainer.py @@ -0,0 +1,1397 @@ +# Copyright 2022 The HuggingFace Team. 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. +import inspect +import math +import os +import time +import typing +import warnings +from contextlib import nullcontext +from typing import Callable, List, Optional, Union + +import datasets +import numpy as np +import torch +import torch.nn.functional as F +from accelerate import Accelerator +from accelerate.utils import ProjectConfiguration, gather_object, is_deepspeed_available +from datasets import Dataset +from huggingface_hub import whoami +from packaging import version +from torch.optim import Adam +from transformers import ( + DataCollatorForLanguageModeling, + PreTrainedTokenizer, + PreTrainedTokenizerBase, + PreTrainedTokenizerFast, +) + +from ..core import ( + WANDB_PADDING, + PPODecorators, + clip_by_value, + convert_to_scalar, + entropy_from_logits, + flatten_dict, + logprobs_from_logits, + masked_mean, + masked_var, + masked_whiten, + set_seed, + stack_dicts, + stats_to_np, +) +from ..import_utils import is_npu_available, is_torch_greater_2_0, is_xpu_available +from ..models import SUPPORTED_ARCHITECTURES, PreTrainedModelWrapper, create_reference_model +from . import AdaptiveKLController, BaseTrainer, FixedKLController, PPOConfig, RunningMoments + + +if is_deepspeed_available(): + import deepspeed + +MODEL_CARD_TEMPLATE = """--- +license: apache-2.0 +tags: +- trl +- ppo +- transformers +- reinforcement-learning +--- + +# {model_name} + +This is a [TRL language model](https://github.com/huggingface/trl) that has been fine-tuned with reinforcement learning to + guide the model outputs according to a value, function, or human feedback. The model can be used for text generation. + +## Usage + +To use this model for inference, first install the TRL library: + +```bash +python -m pip install trl +``` + +You can then generate text as follows: + +```python +from transformers import pipeline + +generator = pipeline("text-generation", model="{model_id}") +outputs = generator("Hello, my llama is cute") +``` + +If you want to use the model for training or to obtain the outputs from the value head, load the model as follows: + +```python +from transformers import AutoTokenizer +from trl import AutoModelForCausalLMWithValueHead + +tokenizer = AutoTokenizer.from_pretrained("{model_id}") +model = AutoModelForCausalLMWithValueHead.from_pretrained("{model_id}") + +inputs = tokenizer("Hello, my llama is cute", return_tensors="pt") +outputs = model(**inputs, labels=inputs["input_ids"]) +``` +""" + + +class PPOTrainer(BaseTrainer): + """ + The PPOTrainer uses Proximal Policy Optimization to optimise language models. + Note, this trainer is heavily inspired by the original OpenAI learning to summarize work here: + https://github.com/openai/summarize-from-feedback + + Attributes: + **config** (`PPOConfig`) -- Configuration object for PPOTrainer. Check the documentation of `PPOConfig` for more + details. + **model** (`PreTrainedModelWrapper`) -- Model to be optimized, Hugging Face transformer model with a value head. + Check the documentation of `PreTrainedModelWrapper` for more details. + **ref_model** (`PreTrainedModelWrapper`, *optional*) -- Reference model to be used for KL penalty, Hugging Face + transformer model with a casual language modelling head. Check the documentation of `PreTrainedModelWrapper` + for more details. If no reference model is provided, the trainer will create a reference model with the same + architecture as the model to be optimized with shared layers. + **tokenizer** (`PreTrainedTokenizerBase`) -- Tokenizer to be used for encoding the + data. Check the documentation of `transformers.PreTrainedTokenizer` and + `transformers.PreTrainedTokenizerFast` for more details. + **dataset** (Union[`torch.utils.data.Dataset`, `datasets.Dataset`], *optional*) -- PyTorch dataset or Hugging + Face dataset. This is used to create a PyTorch dataloader. If no dataset is provided, the dataloader must be + created outside the trainer users needs to design their own dataloader and make sure the batch + size that is used is the same as the one specified in the configuration object. + **optimizer** (`torch.optim.Optimizer`, *optional*) -- Optimizer to be used for training. If no optimizer is + provided, the trainer will create an Adam optimizer with the learning rate specified in the configuration + object. + **data_collator** (DataCollatorForLanguageModeling, *optional*) -- Data collator to be used for training and + passed along the dataloader + **num_shared_layers** (int, *optional*) -- Number of layers to be shared between the model and the reference + model, if no reference model is passed. If no number is provided, all the layers will be shared. + **lr_scheduler** (`torch.optim.lr_scheduler`, *optional*) -- Learning rate scheduler to be used for training. + """ + + _tag_names = ["trl", "ppo"] + + def __init__( + self, + config: PPOConfig = None, + model: PreTrainedModelWrapper = None, + ref_model: Optional[PreTrainedModelWrapper] = None, + tokenizer: PreTrainedTokenizerBase = None, + dataset: Optional[Union[torch.utils.data.Dataset, Dataset]] = None, + optimizer: Optional[torch.optim.Optimizer] = None, + data_collator: Optional[typing.Callable] = None, + num_shared_layers: Optional[int] = None, + lr_scheduler: Optional[torch.optim.lr_scheduler._LRScheduler] = None, + ): + """ + Initialize PPOTrainer. + + Args: + config (`PPOConfig`): + Configuration object for PPOTrainer. Check the documentation of `PPOConfig` for more details. + model (`PreTrainedModelWrapper`): + Hugging Face transformer model with a value head. + ref_model (`PreTrainedModelWrapper`): + Hugging Face transformer model with a casual language modelling head. Used for KL penalty + tokenizer (`transformers.PreTrainedTokenizerBase`): + Hugging Face tokenizer + dataset (Optional[Union[`torch.utils.data.Dataset`, `datasets.Dataset`]]): + PyTorch dataset or Hugging Face dataset. If a Hugging Face dataset is passed, the dataset + will be preprocessed by removing the columns that are not used by the model. If none is passed, + a warning will be raised in a multi-GPU setting. + optimizer (Optional[`torch.optim.Optimizer`]): + Optimizer used for training. If `None`, the `Adam` is used as default. + data_collator (Optional[function]): + Data collator function. + num_shared_layers (Optional[int]): + Number of shared layers between the model and the reference model. If `None`, all layers are shared. + used only if `ref_model` is `None`. + lr_scheduler (Optional[`torch.optim.lr_scheduler`]): + Learning rate scheduler used for training. + """ + super().__init__(config) + + # initial seed for reproducible experiments + set_seed(config.seed) + + # Step 0: check positional arguments validity + if not isinstance(config, PPOConfig): + raise ValueError(f"config must be a PPOConfig, got {type(config)}") + if not isinstance(tokenizer, (PreTrainedTokenizerBase)): + raise ValueError(f"tokenizer must be a PreTrainedTokenizerBase like a PreTrainedTokenizer or a PreTrainedTokenizerFast, got {type(tokenizer)}") + if not isinstance(model, (SUPPORTED_ARCHITECTURES)): + raise ValueError(f"model must be a PreTrainedModelWrapper, got {type(model)} - supported architectures are: {SUPPORTED_ARCHITECTURES}") + # Step 1: Initialize Accelerator + self.accelerator = Accelerator( + log_with=config.log_with, + gradient_accumulation_steps=config.gradient_accumulation_steps, + project_config=ProjectConfiguration(**config.project_kwargs), + **config.accelerator_kwargs, + ) + + # Step 1.1 Runtime variables filled by the accelerator + config.world_size = self.accelerator.num_processes + config.global_backward_batch_size = config.backward_batch_size * config.world_size + config.global_batch_size = config.batch_size * config.world_size + + self.model = model + self.model_params = filter(lambda p: p.requires_grad, self.model.parameters()) + self.is_encoder_decoder = hasattr(self.model, "is_encoder_decoder") + self.is_peft_model = getattr(self.model, "is_peft_model", False) + config.is_encoder_decoder = self.is_encoder_decoder + config.is_peft_model = self.is_peft_model + + is_using_tensorboard = config.log_with is not None and config.log_with == "tensorboard" + self.accelerator.init_trackers( + config.tracker_project_name, + config=dict(trl_ppo_trainer_config=config.to_dict()) if not is_using_tensorboard else config.to_dict(), + init_kwargs=config.tracker_kwargs, + ) + self.is_using_text_environment = getattr(config, "use_text_environment", False) + + if isinstance(ref_model, SUPPORTED_ARCHITECTURES): + self.ref_model = ref_model + if num_shared_layers is not None: + warnings.warn( + "num_shared_layers is ignored when ref_model is provided. Two different models are used for the " "model and the reference model and no layers are shared.", + UserWarning, + ) + elif ref_model is None and not self.is_peft_model: + self.ref_model = create_reference_model(self.model, num_shared_layers=num_shared_layers) + elif self.is_peft_model: + self.ref_model = None + else: + raise ValueError(f"ref_model must be a PreTrainedModelWrapper or `None`, got {type(ref_model)} - supported " f"architectures are: {SUPPORTED_ARCHITECTURES} ") + self.optional_peft_ctx = self.accelerator.unwrap_model(self.model).pretrained_model.disable_adapter if self.is_peft_model else nullcontext + + if not (isinstance(tokenizer, PreTrainedTokenizer) or isinstance(tokenizer, PreTrainedTokenizerFast)): + raise ValueError("tokenizer must be a transformers.PreTrainedTokenizer or transformers.PreTrainedTokenizerFast") + self.tokenizer = tokenizer + + if dataset is not None and not (isinstance(dataset, torch.utils.data.Dataset) or isinstance(dataset, Dataset)): + raise ValueError("dataset must be a torch.utils.data.Dataset or datasets.Dataset") + elif dataset is None: + warnings.warn( + "No dataset is provided. Make sure to set config.batch_size to the correct value before training.", + UserWarning, + ) + self.dataset = dataset + self._signature_columns = None + if self.dataset is not None: + self.dataloader = self.prepare_dataloader(self.dataset, data_collator) + elif self.dataset is None and self.accelerator.num_processes > 1: + warnings.warn( + "No dataset is provided. In a multi-GPU setting, this will lead to an error. You should" + " prepare your dataloader yourself with `dataloader = ppo_trainer.accelerator.prepare(dataloader)`" + " and using `torch.utils.data.DataLoader`, or pass a dataset to the `PPOTrainer`. Please " + " refer to the documentation for more details.", + UserWarning, + ) + self.dataloader = None + else: + self.dataloader = None + + # Step 3: Initialize optimizer and data collator + self.data_collator = DataCollatorForLanguageModeling(self.tokenizer, mlm=False) + if optimizer is None: + self.optimizer = Adam( + filter(lambda p: p.requires_grad, self.model.parameters()), + lr=self.config.learning_rate, + ) + else: + self.optimizer = optimizer + + self.lr_scheduler = lr_scheduler + if self.lr_scheduler is not None: + lr_scheduler_class = torch.optim.lr_scheduler._LRScheduler if not is_torch_greater_2_0() else torch.optim.lr_scheduler.LRScheduler + + if not isinstance(self.lr_scheduler, lr_scheduler_class): + raise ValueError("lr_scheduler must be a torch.optim.lr_scheduler._LRScheduler or torch.optim.lr_scheduler.LRScheduler (for torch >= 2.0)") + + if self.config.adap_kl_ctrl: + self.kl_ctl = AdaptiveKLController(self.config.init_kl_coef, self.config.target, self.config.horizon) + else: + self.kl_ctl = FixedKLController(self.config.init_kl_coef) + + # Safety checkers for DS integration + is_deepspeed_used = self.accelerator.distributed_type == "DEEPSPEED" and hasattr(self.accelerator.state, "deepspeed_plugin") + + ( + self.model, + self.optimizer, + self.data_collator, + self.dataloader, + self.lr_scheduler, + ) = self.accelerator.prepare( + self.model, + self.optimizer, + self.data_collator, + self.dataloader, + self.lr_scheduler, + ) + if is_deepspeed_used: + # Quantized models are already set on the correct device + if not self.is_peft_model and not (getattr(self.ref_model.pretrained_model, "is_loaded_in_8bit", False) or getattr(self.ref_model.pretrained_model, "is_loaded_in_4bit", False)): + self.ref_model = self._prepare_deepspeed(self.ref_model) + else: + self.ref_model = self.accelerator.prepare(self.ref_model) + + # In a distributed setup, only logging needs to be performed on the main process + # check: https://pytorch.org/docs/stable/generated/torch.nn.parallel.DistributedDataParallel.html + # or: https://discuss.pytorch.org/t/use-distributed-data-parallel-correctly/82500/11 + self.is_distributed = self.accelerator.num_processes > 1 + + # init the current step + self.current_step = 0 + + # init variables for pushing model to hub + if config.push_to_hub_if_best_kwargs: + if "repo_id" not in config.push_to_hub_if_best_kwargs: + raise ValueError("You have to specify repo_id in order to push the model to the hub!") + self.push_to_hub_kwargs = config.push_to_hub_if_best_kwargs + self.compare_step = 0 + self.highest_reward = torch.tensor(-float("inf")) + + # post process for PP + if not getattr(self.model, "is_sequential_parallel", False): + self.current_device = self.accelerator.device + else: + if is_xpu_available(): + self.current_device = torch.device("xpu:0") + elif is_npu_available(): + self.current_device = torch.device("npu:0") + else: + self.current_device = torch.device("cuda:0") + + PPODecorators.optimize_device_cache = self.config.optimize_device_cache + + self.running = RunningMoments(self.accelerator) + + def _filter_kwargs(self, kwargs, target_func): + """ + filter the keyword arguments that are supported by the target function. + + Args: + kwargs (dict): + Keyword arguments + target_func (function): + Target function + """ + return {k: v for k, v in kwargs.items() if k in inspect.signature(target_func).parameters.keys()} + + def prepare_dataloader(self, dataset: Union[torch.utils.data.Dataset, Dataset], data_collator=None): + """ + Prepare the dataloader for training. + + Args: + dataset (Union[`torch.utils.data.Dataset`, `datasets.Dataset`]): + PyTorch dataset or Hugging Face dataset. If a Hugging Face dataset is passed, the dataset + will be preprocessed by removing the columns that are not used by the model. + data_collator (Optional[function]): + Data collator function. + + Returns: + `torch.utils.data.DataLoader`: PyTorch dataloader + """ + if isinstance(dataset, Dataset): + dataset = self._remove_unused_columns(dataset) + dataloader = torch.utils.data.DataLoader( + dataset, + batch_size=self.config.batch_size, + collate_fn=data_collator, + shuffle=True, + drop_last=True, + ) + return dataloader + + # Adapted from transformers.Trainer._set_signature_columns_if_needed + def _set_signature_columns_if_needed(self): + if self._signature_columns is None: + # Inspect model forward signature to keep only the arguments it accepts. + signature = inspect.signature(self.model.forward) + self._signature_columns = list(signature.parameters.keys()) + # label => sentiment | we need query and response for logging purpose + self._signature_columns += ["label", "query", "response"] + + # Adapted from transformers.Trainer._remove_unused_columns + def _remove_unused_columns(self, dataset: "Dataset"): + if not self.config.remove_unused_columns: + return dataset + self._set_signature_columns_if_needed() + signature_columns = self._signature_columns + + ignored_columns = list(set(dataset.column_names) - set(signature_columns)) + + columns = [k for k in signature_columns if k in dataset.column_names] + + if version.parse(datasets.__version__) < version.parse("1.4.0"): + dataset.set_format( + type=dataset.format["type"], + columns=columns, + format_kwargs=dataset.format["format_kwargs"], + ) + return dataset + else: + return dataset.remove_columns(ignored_columns) + + def generate( + self, + query_tensor: Union[torch.Tensor, List[torch.Tensor]], + length_sampler: Callable = None, + batch_size: int = 4, + return_prompt: bool = True, + generate_ref_response: bool = False, + **generation_kwargs, + ): + """ + Generate response with the model given the query tensor. + call the `generate` method of the model. + + Args: + query_tensor (`torch.LongTensor`): + A tensor of shape (`seq_len`) containing query tokens or a list of tensors of shape (`seq_len`). + length_sampler (`Callable`, *optional*): + Callable that returns the number of newly generated tokens. + batch_size (`int`, *optional): + Batch size used for generation, defaults to `4`. + return_prompt (`bool`, *optional*): + If set to `False` the prompt is not returned but only the newly generated tokens, defaults to `True`. + generate_ref_response (`bool`, *optional*): + If set to `True` the reference response is also generated, defaults to `False`. + generation_kwargs (dict[str, Any]): + Keyword arguments for generation. + + Returns: + `torch.LongTensor`: A tensor of shape (`batch_size`, `gen_len`) containing response tokens. + """ + if generate_ref_response: + ref_model = self.model if self.is_peft_model else self.ref_model + if isinstance(query_tensor, List): + response = self._generate_batched( + self.model, + query_tensor, + length_sampler=length_sampler, + batch_size=batch_size, + return_prompt=return_prompt, + **generation_kwargs, + ) + if generate_ref_response: + with self.optional_peft_ctx(): + ref_response = self._generate_batched( + ref_model, + query_tensor, + length_sampler=length_sampler, + batch_size=batch_size, + return_prompt=return_prompt, + **generation_kwargs, + ) + + else: + if len(query_tensor.shape) == 2: + raise ValueError("query_tensor must be a tensor of shape (`seq_len`) or a list of tensors of shape (`seq_len`)") + + if length_sampler is not None: + generation_kwargs["max_new_tokens"] = length_sampler() + response = self.accelerator.unwrap_model(self.model).generate(input_ids=query_tensor.unsqueeze(dim=0), **generation_kwargs) + if generate_ref_response: + with self.optional_peft_ctx(): + ref_response = ref_model.generate(input_ids=query_tensor.unsqueeze(dim=0), **generation_kwargs) + + if not return_prompt and not self.is_encoder_decoder: + response = response[:, query_tensor.shape[0] :] + if generate_ref_response: + ref_response = ref_response[:, query_tensor.shape[0] :] + + if generate_ref_response: + return response, ref_response + return response + + def _generate_batched( + self, + model: PreTrainedModelWrapper, + query_tensors: List[torch.Tensor], + length_sampler: Callable = None, + batch_size: int = 4, + return_prompt: bool = True, + pad_to_multiple_of: int = None, + remove_padding: bool = True, + **generation_kwargs, + ): + outputs = [] + + padding_side_default = self.tokenizer.padding_side + if not self.is_encoder_decoder: + self.tokenizer.padding_side = "left" + + # in case we have fewer examples than bs + batch_size = min(len(query_tensors), batch_size) + + for i in range(0, len(query_tensors), batch_size): + if length_sampler is not None: + generation_kwargs["max_new_tokens"] = length_sampler() + + # prevent overflow if query tensors are not even multiple of bs + end_index = min(len(query_tensors), i + batch_size) + + batch = query_tensors[i:end_index] + batch_mask = [torch.ones_like(element) for element in batch] + inputs = {"input_ids": batch, "attention_mask": batch_mask} + + padded_inputs = self.tokenizer.pad( + inputs, + padding=True, + max_length=None, + pad_to_multiple_of=pad_to_multiple_of, + return_tensors="pt", + ).to(self.current_device) + + generations = self.accelerator.unwrap_model(model).generate(**padded_inputs, **generation_kwargs) + + for generation, mask in zip(generations, padded_inputs["attention_mask"]): + if not self.is_encoder_decoder: + output = generation[(1 - mask).sum() :] # remove padding + else: + output = generation + + if not return_prompt and not self.is_encoder_decoder: + output = output[(mask).sum() :] # remove prompt + + if remove_padding and self.tokenizer.eos_token_id in output: + pad_mask = output == self.tokenizer.eos_token_id + pad_start = torch.nonzero(pad_mask, as_tuple=False)[0, 0].item() + output = output[: pad_start + 1] # keep the eos token at the end + + outputs.append(output) + + self.tokenizer.padding_side = padding_side_default + return outputs + + def _step_safety_checker( + self, + batch_size: int, + queries: List[torch.LongTensor], + responses: List[torch.LongTensor], + scores: List[torch.FloatTensor], + masks: Optional[List[torch.LongTensor]] = None, + ): + """ + Check if the input data is valid for training. + + Args: + batch_size (int): + Batch size from the config file. + queries (List[`torch.LongTensor`]): + List of tensors containing the encoded queries of shape (`query_length`) + responses (List[`torch.LongTensor`]): + List of tensors containing the encoded responses of shape (`response_length`) + scores (List[`torch.FloatTensor`]): + List of tensors containing the scores. + masks (List[`torch.LongTensor`], *optional*): + list of optional tensors containing the masks of shape (`query_length` + `response_length`) + Returns: + `tuple`: The input processed data. + """ + for name, tensor_list in zip(["queries", "responses", "scores"], [queries, responses, scores]): + if not isinstance(tensor_list, list): + raise ValueError(f"{name} must be a list of tensors - got {type(tensor_list)}") + if not isinstance(tensor_list[0], torch.Tensor): + raise ValueError(f"Elements in {name} must be tensors - got {type(tensor_list[0])}") + if batch_size is not None and len(tensor_list) != batch_size: + raise ValueError(f"Batch size ({batch_size}) does not match number of examples - but got {len(tensor_list)} for: {name}") + + # add queries, scores and responses on the correct device + queries = [tensor.to(self.current_device) for tensor in queries] + responses = [tensor.to(self.current_device) for tensor in responses] + scores = [tensor.to(self.current_device) for tensor in scores] + masks = [tensor.to(self.current_device) for tensor in masks] if masks is not None else None + + # squeeze scores if needed + for i, score in enumerate(scores): + if score.dim() > 1: + raise ValueError(f"Scores must be 1-dimensional - got {score.dim()} for {score}") + elif score.dim() == 1: + scores[i] = score.squeeze() + + return queries, responses, scores, masks + + @PPODecorators.empty_device_cache() + def step( + self, + queries: List[torch.LongTensor], + responses: List[torch.LongTensor], + scores: List[torch.FloatTensor], + response_masks: Optional[List[torch.LongTensor]] = None, + ): + """ + Run a PPO optimisation step given a list of queries, model responses, and rewards. + + Args: + queries (List[`torch.LongTensor`]): + List of tensors containing the encoded queries of shape (`query_length`) + responses (List[`torch.LongTensor`]): + List of tensors containing the encoded responses of shape (`response_length`) + scores (List[`torch.FloatTensor`]): + List of tensors containing the scores. + response_masks (List[`torch.FloatTensor`], *optional*)): + List of tensors containing masks of the response tokens. + + Returns: + `dict[str, Any]`: A summary of the training statistics + """ + bs = self.config.batch_size + + queries, responses, scores, response_masks = self._step_safety_checker(bs, queries, responses, scores, response_masks) + scores = torch.tensor(scores, device=self.current_device) + if self.config.use_score_scaling: + # Score scaling + scores_mean, scores_std = self.running.update(scores) + tensor_to_kwargs = dict(dtype=scores.dtype, device=scores.device) + score_scaling_factor = self.running.std.to(**tensor_to_kwargs) + torch.finfo(scores.dtype).eps + if self.config.use_score_norm: + scores = (scores - self.running.mean.to(**tensor_to_kwargs)) / score_scaling_factor + else: + scores /= score_scaling_factor + + if self.config.score_clip is not None: + # Score clipping + scores_dtype = scores.dtype + scores = torch.clip(scores.float(), -self.config.score_clip, self.config.score_clip).to(dtype=scores_dtype) + + # if we want to push best model to the hub + if hasattr(self, "highest_reward"): + if self.compare_step % self.config.compare_steps == 0: + curr_mean_reward = scores.mean() + # if the best reward ever seen + if curr_mean_reward > self.highest_reward: + self.highest_reward = curr_mean_reward + # push model to hub + self.push_to_hub(**self.push_to_hub_kwargs) + self.compare_step += 1 + + timing = dict() + t0 = time.time() + + t = time.time() + + model_inputs = self.prepare_model_inputs(queries, responses) + + if self.is_distributed: + pad_first = self.tokenizer.padding_side == "left" + + model_inputs["input_ids"] = self.accelerator.pad_across_processes( + model_inputs["input_ids"], + dim=1, + pad_index=self.tokenizer.pad_token_id, + pad_first=pad_first, + ) + model_inputs["attention_mask"] = self.accelerator.pad_across_processes(model_inputs["attention_mask"], dim=1, pad_index=0, pad_first=pad_first) + if self.is_encoder_decoder: + model_inputs["decoder_input_ids"] = self.accelerator.pad_across_processes( + model_inputs["decoder_input_ids"], + dim=1, + pad_index=self.tokenizer.pad_token_id, + pad_first=pad_first, + ) + model_inputs["decoder_attention_mask"] = self.accelerator.pad_across_processes( + model_inputs["decoder_attention_mask"], + dim=1, + pad_index=0, + pad_first=pad_first, + ) + + model_inputs_names = list(model_inputs.keys()) + + full_kl_penalty = self.config.kl_penalty == "full" + + with torch.no_grad(): + all_logprobs, logits_or_none, values, masks = self.batched_forward_pass( + self.model, + queries, + responses, + model_inputs, + response_masks=response_masks, + return_logits=full_kl_penalty, + ) + with self.optional_peft_ctx(): + ref_logprobs, ref_logits_or_none, _, _ = self.batched_forward_pass( + self.model if self.is_peft_model else self.ref_model, + queries, + responses, + model_inputs, + return_logits=full_kl_penalty, + ) + + timing["time/ppo/forward_pass"] = time.time() - t + + with torch.no_grad(): + t = time.time() + if full_kl_penalty: + active_full_logprobs = logprobs_from_logits(logits_or_none, None, gather=False) + ref_full_logprobs = logprobs_from_logits(ref_logits_or_none, None, gather=False) + + rewards, non_score_reward, kls = self.compute_rewards(scores, active_full_logprobs, ref_full_logprobs, masks) + else: + rewards, non_score_reward, kls = self.compute_rewards(scores, all_logprobs, ref_logprobs, masks) + timing["time/ppo/compute_rewards"] = time.time() - t + + t = time.time() + values, advantages, returns = self.compute_advantages(values, rewards, masks) + timing["time/ppo/compute_advantages"] = time.time() - t + + # upcast to float32 to avoid dataset issues + batch_dict = { + "queries": queries, + "responses": responses, + "logprobs": all_logprobs.to(torch.float32), + "values": values.to(torch.float32), + "masks": masks, + "advantages": advantages, + "returns": returns, + } + batch_dict.update(model_inputs) + + t = time.time() + all_stats = [] + early_stop = False + for _ in range(self.config.ppo_epochs): + if early_stop: + break + b_inds = np.random.permutation(bs) + for backward_batch_start in range(0, bs, self.config.backward_batch_size): + backward_batch_end = backward_batch_start + self.config.backward_batch_size + backward_batch_inds = b_inds[backward_batch_start:backward_batch_end] + + for mini_batch_start in range(0, self.config.backward_batch_size, self.config.mini_batch_size): + mini_batch_end = mini_batch_start + self.config.mini_batch_size + mini_batch_inds = backward_batch_inds[mini_batch_start:mini_batch_end] + mini_batch_dict = { + "logprobs": batch_dict["logprobs"][mini_batch_inds], + "values": batch_dict["values"][mini_batch_inds], + "masks": batch_dict["masks"][mini_batch_inds], + # hacks: the queries and responses are ragged. + "queries": [batch_dict["queries"][i] for i in mini_batch_inds], + "responses": [batch_dict["responses"][i] for i in mini_batch_inds], + "advantages": batch_dict["advantages"][mini_batch_inds], + "returns": batch_dict["returns"][mini_batch_inds], + } + for k in model_inputs_names: + mini_batch_dict[k] = batch_dict[k][mini_batch_inds] + with self.accelerator.accumulate(self.model): + model_inputs = {k: mini_batch_dict[k] for k in model_inputs_names} + + logprobs, logits, vpreds, _ = self.batched_forward_pass( + self.model, + mini_batch_dict["queries"], + mini_batch_dict["responses"], + model_inputs, + return_logits=True, + ) + train_stats = self.train_minibatch( + mini_batch_dict["logprobs"], + mini_batch_dict["values"], + logprobs, + logits, + vpreds, + mini_batch_dict["masks"], + mini_batch_dict["advantages"], + mini_batch_dict["returns"], + ) + all_stats.append(train_stats) + + # typically, early stopping is done at the epoch level + if self.config.early_stopping: + policykl = train_stats["policy/policykl"] + early_stop = self._early_stop(policykl) + if early_stop: + break + + timing["time/ppo/optimize_step"] = time.time() - t + + t = time.time() + train_stats = stack_dicts(all_stats) + + # reshape advantages/ratios such that they are not averaged. + train_stats["policy/advantages"] = torch.flatten(train_stats["policy/advantages"]).unsqueeze(0) + train_stats["policy/advantages"] = torch.nan_to_num(train_stats["policy/advantages"], WANDB_PADDING) + train_stats["policy/ratio"] = torch.flatten(train_stats["policy/ratio"]).unsqueeze(0) + + stats = self.record_step_stats( + scores=scores, + logprobs=all_logprobs, + ref_logprobs=ref_logprobs, + non_score_reward=non_score_reward, + train_stats=train_stats, + kl_coef=self.kl_ctl.value, + masks=masks, + queries=queries, + responses=responses, + kls=kls, + ) + # Gather/Reduce stats from all processes + if self.is_distributed: + stats = self.gather_stats(stats) + stats = stats_to_np(stats) + timing["time/ppo/calc_stats"] = time.time() - t + stats["ppo/learning_rate"] = self.optimizer.param_groups[0]["lr"] + + # Update the KL control - multiply the batch_size by the number of processes + self.kl_ctl.update( + stats["objective/kl"], + self.config.batch_size * self.accelerator.num_processes, + ) + + # Log the total ppo time + timing["time/ppo/total"] = time.time() - t0 + stats.update(timing) + + # post-process stats for tensorboard and other loggers + if self.config.log_with != "wandb": + stats = convert_to_scalar(stats) + + if self.lr_scheduler is not None: + self.lr_scheduler.step() + + return stats + + def _early_stop(self, policykl): + r""" + Handles the early stopping logic. If the policy KL is greater than the target KL, then the gradient is zeroed and + the optimization step is skipped. + This also handles the multi-gpu case where the policy KL is averaged across all processes. + + Args: + policy_kl (torch.Tensor): + the policy KL + + Returns: + `bool`: whether to early stop or not + """ + early_stop = False + if not self.config.early_stopping: + return early_stop + + if not self.is_distributed and policykl > 1.5 * self.config.target_kl: + self.optimizer.zero_grad() + early_stop = True + elif self.is_distributed: + import torch.distributed as dist + + # Wait for all processes to finish + dist.barrier() + + # all gather the policykl + dist.all_reduce(policykl, dist.ReduceOp.SUM) + policykl /= self.accelerator.num_processes + + if policykl > 1.5 * self.config.target_kl: + self.optimizer.zero_grad() + early_stop = True + return early_stop + + def gather_stats(self, stats): + """ + Gather stats from all processes. Useful in the context of distributed training. + + Args: + stats (dict[str, Any]): + a dictionary of stats to be gathered. The stats should contain torch tensors. + + Returns: + `dict[str, Any]`: A dictionary of stats with the tensors gathered. + """ + import torch.distributed as dist + + # Wait for all processes to finish + dist.barrier() + + for k, v in stats.items(): + if isinstance(v, torch.Tensor): + dist.all_reduce(v.to(self.accelerator.device), dist.ReduceOp.SUM) + v /= self.accelerator.num_processes + stats[k] = v + return stats + + def prepare_model_inputs(self, queries: torch.Tensor, responses: torch.Tensor): + if self.is_encoder_decoder: + input_data = self.data_collator([{"input_ids": q, "attention_mask": torch.ones_like(q)} for q in queries]).to(self.current_device) + + decoder_inputs = self.data_collator([{"input_ids": r, "attention_mask": torch.ones_like(r)} for r in responses]).to(self.current_device) + + input_data["decoder_input_ids"] = decoder_inputs["input_ids"] + input_data["decoder_attention_mask"] = decoder_inputs["attention_mask"] + else: + input_ids = [torch.cat([q, r]) for q, r in zip(queries, responses)] + input_data = self.data_collator([{"input_ids": ids, "attention_mask": torch.ones_like(ids)} for ids in input_ids]).to(self.current_device) + + input_data.pop("labels", None) # we don't want to compute LM losses + return input_data + + @PPODecorators.empty_device_cache() + def batched_forward_pass( + self, + model: PreTrainedModelWrapper, + queries: torch.Tensor, + responses: torch.Tensor, + model_inputs: dict, + return_logits: bool = False, + response_masks: Optional[torch.Tensor] = None, + ): + """ + Calculate model outputs in multiple batches. + + Args: + queries (`torch.LongTensor`): + List of tensors containing the encoded queries, shape (`batch_size`, `query_length`) + responses (`torch.LongTensor`): + List of tensors containing the encoded responses, shape (`batch_size`, `response_length`) + return_logits (`bool`, *optional*, defaults to `False`): + Whether to return all_logits. Set to `False` if logits are not needed to reduce memory consumption. + Returns: + (tuple): + - all_logprobs (`torch.FloatTensor`): Log probabilities of the responses, + shape (`batch_size`, `response_length`) + - all_ref_logprobs (`torch.FloatTensor`): Log probabilities of the responses, + shape (`batch_size`, `response_length`) + - all_values (`torch.FloatTensor`): Values of the responses, shape (`batch_size`, `response_length`) + """ + bs = len(queries) + fbs = self.config.mini_batch_size + all_logprobs = [] + all_logits = [] + all_masks = [] + all_values = [] + + model.eval() + + for i in range(math.ceil(bs / fbs)): + input_kwargs = {key: value[i * fbs : (i + 1) * fbs] for key, value in model_inputs.items()} + query_batch = queries[i * fbs : (i + 1) * fbs] + response_batch = responses[i * fbs : (i + 1) * fbs] + if response_masks is not None: + response_masks_batch = response_masks[i * fbs : (i + 1) * fbs] + logits, _, values = model(**input_kwargs) + + if self.is_encoder_decoder: + input_ids = input_kwargs["decoder_input_ids"] + attention_mask = input_kwargs["decoder_attention_mask"] + else: + input_ids = input_kwargs["input_ids"] + attention_mask = input_kwargs["attention_mask"] + + logprobs = logprobs_from_logits(logits[:, :-1, :], input_ids[:, 1:]) + masks = torch.zeros_like(attention_mask) + masks[:, :-1] = attention_mask[:, 1:] + + for j in range(len(query_batch)): + if self.is_encoder_decoder: + # Decoder sentence starts always in the index 1 after padding in the Enc-Dec Models + start = 1 + end = attention_mask[j, :].sum() - 1 + else: + start = len(query_batch[j]) - 1 # logprobs starts from the second query token + if attention_mask[j, 0] == 0: # offset left padding + start += attention_mask[j, :].nonzero()[0] + end = start + len(response_batch[j]) + if response_masks is not None: + response_masks_batch[j] = torch.cat((torch.zeros_like(query_batch[j]), response_masks_batch[j]))[1:] + + masks[j, :start] = 0 + masks[j, end:] = 0 + if response_masks is not None: + masks[j, start:end] = masks[j, start:end] * response_masks_batch[j][start:end] + + if return_logits: + all_logits.append(logits) + else: + del logits + all_values.append(values) + all_logprobs.append(logprobs) + all_masks.append(masks) + + return ( + torch.cat(all_logprobs), + torch.cat(all_logits)[:, :-1] if return_logits else None, + torch.cat(all_values)[:, :-1], + torch.cat(all_masks)[:, :-1], + ) + + @PPODecorators.empty_device_cache() + def train_minibatch( + self, + old_logprobs: torch.FloatTensor, + values: torch.FloatTensor, + logprobs: torch.FloatTensor, + logits: torch.FloatTensor, + vpreds: torch.FloatTensor, + mask: torch.LongTensor, + advantages: torch.FloatTensor, + returns: torch.FloatTensor, + ): + """ + Train one PPO minibatch + + Args: + logprobs (`torch.FloatTensor`): + Log probabilities of the model, shape [mini_batch_size, response_length] + values (`torch.FloatTensor`): + Values of the value head, shape [mini_batch_size, response_length] + query (`torch.LongTensor`): + Encoded queries, shape [mini_batch_size, query_length] + response (`torch.LongTensor`): + Encoded responses, shape [mini_batch_size, response_length] + model_input (`torch.LongTensor`): + Concatenated queries and responses, shape [mini_batch_size, query_length+response_length] + + Returns: + train_stats (dict[str, `torch.Tensor`]): + Dictionary of training statistics + """ + self.model.train() + loss_p, loss_v, train_stats = self.loss(old_logprobs, values, logits, vpreds, logprobs, mask, advantages, returns) + loss = loss_p + loss_v + self.accelerator.backward(loss) + if self.config.max_grad_norm is not None: + if self.accelerator.sync_gradients: + self.accelerator.clip_grad_norm_(self.model_params, self.config.max_grad_norm) + self.optimizer.step() + # we call optimizer.zero_grad() every time and let `accelerator` handle accumulation + # see https://huggingface.co/docs/accelerate/usage_guides/gradient_accumulation#the-finished-code + self.optimizer.zero_grad() + return train_stats + + def compute_rewards( + self, + scores: torch.FloatTensor, + logprobs: torch.FloatTensor, + ref_logprobs: torch.FloatTensor, + masks: torch.LongTensor, + ): + """ + Compute per token rewards from scores and KL-penalty. + + Args: + scores (`torch.FloatTensor`): + Scores from the reward model, shape (`batch_size`) + logprobs (`torch.FloatTensor`): + Log probabilities of the model, shape (`batch_size`, `response_length`) + ref_logprobs (`torch.FloatTensor`): + Log probabilities of the reference model, shape (`batch_size`, `response_length`) + + Returns: + `torch.FloatTensor`: Per token rewards, shape (`batch_size`, `response_length`) + `torch.FloatTensor`: Non score rewards, shape (`batch_size`, `response_length`) + `torch.FloatTensor`: KL penalty, shape (`batch_size`, `response_length`) + """ + rewards, non_score_rewards, kls = [], [], [] + for score, logprob, ref_logprob, mask in zip(scores, logprobs, ref_logprobs, masks): + # compute KL penalty (from difference in logprobs) + kl = self._kl_penalty(logprob, ref_logprob) + kls.append(kl) + non_score_reward = -self.kl_ctl.value * kl + non_score_rewards.append(non_score_reward) + reward = non_score_reward.clone() + last_non_masked_index = mask.nonzero()[-1] + + # reward is preference model score + KL penalty + reward[last_non_masked_index] += score + rewards.append(reward) + return torch.stack(rewards), torch.stack(non_score_rewards), torch.stack(kls) + + def _kl_penalty(self, logprob: torch.FloatTensor, ref_logprob: torch.FloatTensor) -> torch.FloatTensor: + if self.config.kl_penalty == "kl": + return logprob - ref_logprob + + if self.config.kl_penalty == "abs": + return (logprob - ref_logprob).abs() + + if self.config.kl_penalty == "mse": + return 0.5 * (logprob - ref_logprob).square() + + if self.config.kl_penalty == "full": + # Flip is required due to this issue? :https://github.com/pytorch/pytorch/issues/57459 + return F.kl_div(ref_logprob, logprob, log_target=True, reduction="none").sum(-1) + + raise NotImplementedError + + def compute_advantages( + self, + values: torch.FloatTensor, + rewards: torch.FloatTensor, + mask: torch.FloatTensor, + ): + lastgaelam = 0 + advantages_reversed = [] + gen_len = rewards.shape[-1] + + values = values * mask + rewards = rewards * mask + + if self.config.whiten_rewards: + rewards = masked_whiten(rewards, mask, shift_mean=False) + + for t in reversed(range(gen_len)): + nextvalues = values[:, t + 1] if t < gen_len - 1 else 0.0 + delta = rewards[:, t] + self.config.gamma * nextvalues - values[:, t] + lastgaelam = delta + self.config.gamma * self.config.lam * lastgaelam + advantages_reversed.append(lastgaelam) + advantages = torch.stack(advantages_reversed[::-1]).transpose(0, 1) + + returns = advantages + values + advantages = masked_whiten(advantages, mask) + advantages = advantages.detach() + return values, advantages, returns + + def loss( + self, + old_logprobs: torch.FloatTensor, + values: torch.FloatTensor, + logits: torch.FloatTensor, + vpreds: torch.FloatTensor, + logprobs: torch.FloatTensor, + mask: torch.LongTensor, + advantages: torch.FloatTensor, + returns: torch.FloatTensor, + ): + """ + Calculate policy and value losses. + + Args: + old_logprobs (`torch.FloatTensor`): + Log probabilities of the model, shape (`batch_size`, `response_length`) + values (`torch.FloatTensor`): + Values of the value head, shape (`batch_size`, `response_length`) + rewards (`torch.FloatTensor`): + Rewards from the reward model, shape (`batch_size`, `response_length`) + logits (`torch.FloatTensor`): + Logits of the model, shape (`batch_size`, `response_length`, `vocab_size`) + v_pred (`torch.FloatTensor`): + Values of the value head, shape (`batch_size`, `response_length`) + logprobs (`torch.FloatTensor`): + Log probabilities of the model, shape (`batch_size`, `response_length`) + """ + + vpredclipped = clip_by_value( + vpreds, + values - self.config.cliprange_value, + values + self.config.cliprange_value, + ) + + vf_losses1 = (vpreds - returns) ** 2 + vf_losses2 = (vpredclipped - returns) ** 2 + vf_loss = 0.5 * masked_mean(torch.max(vf_losses1, vf_losses2), mask) + vf_clipfrac = masked_mean(torch.gt(vf_losses2, vf_losses1).float(), mask) + + ratio = torch.exp(logprobs - old_logprobs) + + pg_losses = -advantages * ratio + pg_losses2 = -advantages * torch.clamp(ratio, 1.0 - self.config.cliprange, 1.0 + self.config.cliprange) + + pg_loss = masked_mean(torch.max(pg_losses, pg_losses2), mask) + pg_clipfrac = masked_mean(torch.gt(pg_losses2, pg_losses).float(), mask) + + loss = pg_loss + self.config.vf_coef * vf_loss + + avg_ratio = masked_mean(ratio, mask).item() + if avg_ratio > self.config.ratio_threshold: + warnings.warn(f"The average ratio of batch ({avg_ratio:.2f}) exceeds threshold {self.config.ratio_threshold:.2f}. Skipping batch.") + pg_loss = pg_loss * 0.0 + vf_loss = vf_loss * 0.0 + loss = loss * 0.0 + + entropy = masked_mean(entropy_from_logits(logits), mask) + + approxkl = 0.5 * masked_mean((logprobs - old_logprobs) ** 2, mask) + policykl = masked_mean(old_logprobs - logprobs, mask) + + return_mean, return_var = masked_mean(returns, mask), masked_var(returns, mask) + value_mean, value_var = masked_mean(values, mask), masked_var(values, mask) + + stats = dict( + loss=dict(policy=pg_loss.detach(), value=vf_loss.detach(), total=loss.detach()), + policy=dict( + entropy=entropy.detach(), + approxkl=approxkl.detach(), + policykl=policykl.detach(), + clipfrac=pg_clipfrac.detach(), + advantages=advantages.detach(), + advantages_mean=masked_mean(advantages, mask).detach(), + ratio=ratio.detach(), + ), + returns=dict(mean=return_mean.detach(), var=return_var.detach()), + val=dict( + vpred=masked_mean(vpreds, mask).detach(), + error=masked_mean((vpreds - returns) ** 2, mask).detach(), + clipfrac=vf_clipfrac.detach(), + mean=value_mean.detach(), + var=value_var.detach(), + ), + ) + return pg_loss, self.config.vf_coef * vf_loss, flatten_dict(stats) + + def record_step_stats(self, kl_coef: float, **data): + """ + Record training step statistics. + + + Args: + kl_coef (`float`): + KL coefficient + data (`dict`): + Dictionary of training step data + + Returns: + stats (`dict`): + Dictionary of training step statistics + """ + mask = data.pop("masks") + + kls = data.pop("kls") + kl_list = ((kls) * mask).sum(axis=-1) + mean_kl = kl_list.mean() + mean_entropy = (-data["logprobs"] * mask).sum(axis=-1).mean() + + mean_non_score_reward = masked_mean(data["non_score_reward"], mask) # non_score_reward is size `batch_size`, `response_length` + mean_scores = data["scores"].mean() # scores is size `batch_size` + std_scores = data["scores"].std() + + if mean_kl.item() < -1.0: + # warn users + warnings.warn( + f"KL divergence is starting to become negative: {mean_kl.item():.2f} - this might be a precursor for failed training." + " sometimes this happens because the generation kwargs are not correctly set. Please make sure" + " that the generation kwargs are set correctly, or review your training hyperparameters." + ) + + stats = { + "objective/kl": mean_kl, + "objective/kl_dist": kl_list, + "objective/logprobs": data["logprobs"], + "objective/ref_logprobs": data["ref_logprobs"], + "objective/kl_coef": kl_coef, + "objective/entropy": mean_entropy, + "ppo/mean_non_score_reward": mean_non_score_reward, + "ppo/mean_scores": mean_scores, + "ppo/std_scores": std_scores, + } + + # Log text properties + query_lens = torch.tensor([len(query) for query in data["queries"]], dtype=torch.float) + response_lens = torch.tensor([len(response) for response in data["responses"]], dtype=torch.float) + + stats["tokens/queries_len_mean"] = torch.mean(query_lens).cpu().numpy().item() + stats["tokens/queries_len_std"] = torch.std(query_lens).cpu().numpy().item() + stats["tokens/queries_dist"] = query_lens.cpu().numpy() + stats["tokens/responses_len_mean"] = torch.mean(response_lens).cpu().numpy().item() + stats["tokens/responses_len_std"] = torch.std(response_lens).cpu().numpy().item() + stats["tokens/responses_dist"] = response_lens.cpu().numpy() + + for k, v in data["train_stats"].items(): + stats[f"ppo/{k}"] = torch.mean(v, axis=0) + stats["ppo/val/var_explained"] = 1 - stats["ppo/val/error"] / stats["ppo/returns/var"] + return stats + + def log_stats( + self, + stats: dict, + batch: dict, + rewards: List[torch.FloatTensor], + columns_to_log: List[str] = ["query", "response"], + ): + """ + A function that logs all the training stats. Call it at the end of each epoch. + + Args: + stats (dict[str, Any]): + A dictionary of training stats. + batch (dict[str, Any]): + A dictionary of batch data, this contains the queries and responses. + rewards (`List[torch.FloatTensor]`): + A tensor of rewards. + """ + + # all gather stats + if not isinstance(rewards, torch.Tensor): + rewards = torch.tensor(rewards).to(self.current_device) + rewards = self.accelerator.gather(rewards).flatten() + + if self.config.log_with == "wandb": + import wandb + + if any([column_to_log not in batch.keys() for column_to_log in columns_to_log]): + raise ValueError(f"Columns to log {columns_to_log} are not present in the batch {batch.keys()}.") + + batch_list = [batch[column_to_log] for column_to_log in columns_to_log] + if self.is_distributed: + gathered_batch_list = [] + for b in batch_list: + flattened = gather_object(b) + gathered_batch_list.append(flattened) + batch_list = gathered_batch_list + + # Log only if we are in the main process + if self.accelerator.is_main_process: + logs = {} + + # Log stats + if "query" not in batch.keys() and "response" not in batch.keys(): + # warn the user that the game logs will not be logged + warnings.warn("The game logs will not be logged because the batch does not contain the keys 'query' and " "'response'. ") + elif self.config.log_with == "wandb": + table_rows = [list(r) for r in zip(*batch_list, rewards.cpu().tolist())] + logs.update({"game_log": wandb.Table(columns=[*columns_to_log, "reward"], rows=table_rows)}) + + logs.update(stats) + + # manually cast in fp32 for bf16 torch tensors + for k, v in logs.items(): + if isinstance(v, torch.Tensor) and v.dtype == torch.bfloat16: + logs[k] = v.float() + + logs["env/reward_mean"] = torch.mean(rewards).cpu().numpy().item() + logs["env/reward_std"] = torch.std(rewards).cpu().numpy().item() + logs["env/reward_dist"] = rewards.cpu().numpy() + + if self.config.log_with == "tensorboard": + # update the current step + self.current_step += 1 + + self.accelerator.log( + logs, + step=self.current_step if self.config.log_with == "tensorboard" else None, + ) + + def create_model_card(self, path: str, model_name: Optional[str] = "TRL Model") -> None: + """Creates and saves a model card for a TRL model. + + Args: + path (`str`): The path to save the model card to. + model_name (`str`, *optional*): The name of the model, defaults to `TRL Model`. + """ + try: + user = whoami()["name"] + # handle the offline case + except: # noqa + warnings.warn("Cannot retrieve user information assuming you are running in offline mode.") + return + + if not os.path.exists(path): + os.makedirs(path) + + model_card_content = MODEL_CARD_TEMPLATE.format(model_name=model_name, model_id=f"{user}/{path}") + with open(os.path.join(path, "README.md"), "w", encoding="utf-8") as f: + f.write(model_card_content) + + def _save_pretrained(self, save_directory: str) -> None: + self.accelerator.unwrap_model(self.model).save_pretrained(save_directory) + self.tokenizer.save_pretrained(save_directory) + self.create_model_card(save_directory) + + def _show_tokens(self, tokens, masks): + from rich import print + from rich.text import Text + + text = Text() + + for i, (token, mask) in enumerate(zip(tokens, masks)): + if mask == 1: + text.append(self.tokenizer.decode(token.item()), style="black on deep_sky_blue1") + text.append(" ") + else: + text.append(self.tokenizer.decode(token.item()), style="black on cyan3") + text.append(" ") + print(text) + + def _prepare_deepspeed(self, model: PreTrainedModelWrapper): + # Adapted from accelerate: https://github.com/huggingface/accelerate/blob/739b135f8367becb67ffaada12fe76e3aa60fefd/src/accelerate/accelerator.py#L1473 + deepspeed_plugin = self.accelerator.state.deepspeed_plugin + config_kwargs = deepspeed_plugin.deepspeed_config + if model is not None: + if hasattr(model, "config"): + hidden_size = max(model.config.hidden_sizes) if getattr(model.config, "hidden_sizes", None) else getattr(model.config, "hidden_size", None) + if hidden_size is not None and config_kwargs["zero_optimization"]["stage"] == 3: + # Note that `stage3_prefetch_bucket_size` can produce DeepSpeed messages like: `Invalidate trace cache @ step 0: expected module 1, but got module 0` + # This is expected and is not an error, see: https://github.com/microsoft/DeepSpeed/discussions/4081 + config_kwargs.update( + { + "zero_optimization.reduce_bucket_size": hidden_size * hidden_size, + "zero_optimization.stage3_param_persistence_threshold": 10 * hidden_size, + "zero_optimization.stage3_prefetch_bucket_size": 0.9 * hidden_size * hidden_size, + } + ) + + # If ZeRO-3 is used, we shard both the active and reference model. + # Otherwise, we assume the reference model fits in memory and is initialized on each device with ZeRO disabled (stage 0) + if config_kwargs["zero_optimization"]["stage"] != 3: + config_kwargs["zero_optimization"]["stage"] = 0 + model, *_ = deepspeed.initialize(model=model, config=config_kwargs) + model.eval() + return model diff --git a/trl/trainer/reward_config.py b/trl/trainer/reward_config.py new file mode 100644 index 0000000..32c7e26 --- /dev/null +++ b/trl/trainer/reward_config.py @@ -0,0 +1,38 @@ +# Copyright 2024 The HuggingFace Team. 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. + +from dataclasses import dataclass +from typing import Optional + +from transformers import TrainingArguments + + +@dataclass +class RewardConfig(TrainingArguments): + """ + RewardConfig collects all training arguments related to the [`RewardTrainer`] class. + + Using [`HfArgumentParser`] we can turn this class into + [argparse](https://docs.python.org/3/library/argparse#module-argparse) arguments that can be specified on the + command line. + + Parameters: + max_length (`int`, *optional*, defaults to `None`): + The maximum length of the sequences in the batch. This argument is required if you want to use the default data collator. + gradient_checkpointing (`bool`, *optional*, defaults to `True`): + If True, use gradient checkpointing to save memory at the expense of slower backward pass. + """ + + max_length: Optional[int] = None + """The maximum length of the sequences in the batch. This argument is required if you want to use the default data collator.""" diff --git a/trl/trainer/reward_trainer.py b/trl/trainer/reward_trainer.py new file mode 100644 index 0000000..eff4eb7 --- /dev/null +++ b/trl/trainer/reward_trainer.py @@ -0,0 +1,257 @@ +# Copyright 2023 The HuggingFace Team. 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. +import inspect +import warnings +from dataclasses import FrozenInstanceError, replace +from typing import Any, Callable, Dict, List, Optional, Tuple, Union + +import torch +import torch.nn as nn +from datasets import Dataset +from transformers import DataCollator, PreTrainedModel, PreTrainedTokenizerBase, Trainer, TrainingArguments +from transformers.trainer_callback import TrainerCallback +from transformers.trainer_pt_utils import nested_detach +from transformers.trainer_utils import EvalPrediction + +from ..import_utils import is_peft_available +from .reward_config import RewardConfig +from .utils import RewardDataCollatorWithPadding, compute_accuracy + + +if is_peft_available(): + from peft import PeftModel, get_peft_model, prepare_model_for_kbit_training + + +class RewardTrainer(Trainer): + r""" + The RewardTrainer can be used to train your custom Reward Model. It is a subclass of the + `transformers.Trainer` class and inherits all of its attributes and methods. It is recommended to use + an `AutoModelForSequenceClassification` as the reward model. The reward model should be trained on a dataset + of paired examples, where each example is a tuple of two sequences. The reward model should be trained to + predict which example in the pair is more relevant to the task at hand. + + The reward trainer expects a very specific format for the dataset. The dataset should contain two 4 entries at least + if you don't use the default `RewardDataCollatorWithPadding` data collator. The entries should be named + - `input_ids_chosen` + - `attention_mask_chosen` + - `input_ids_rejected` + - `attention_mask_rejected` + + Optionally, you can also pass a `margin` entry to the dataset. This entry should contain the margin used to modulate the + loss of the reward model as outlined in https://ai.meta.com/research/publications/llama-2-open-foundation-and-fine-tuned-chat-models/. + If you don't pass a margin, no margin will be used. + """ + + def __init__( + self, + model: Union[PreTrainedModel, nn.Module] = None, + args: Optional[RewardConfig] = None, + data_collator: Optional[DataCollator] = None, + train_dataset: Optional[Dataset] = None, + eval_dataset: Optional[Union[Dataset, Dict[str, Dataset]]] = None, + tokenizer: Optional[PreTrainedTokenizerBase] = None, + model_init: Optional[Callable[[], PreTrainedModel]] = None, + compute_metrics: Optional[Callable[[EvalPrediction], Dict]] = None, + callbacks: Optional[List[TrainerCallback]] = None, + optimizers: Tuple[torch.optim.Optimizer, torch.optim.lr_scheduler.LambdaLR] = ( + None, + None, + ), + preprocess_logits_for_metrics: Optional[Callable[[torch.Tensor, torch.Tensor], torch.Tensor]] = None, + max_length: Optional[int] = None, + peft_config: Optional[Dict] = None, + ): + """ + Initialize RewardTrainer. + + Args: + model (`transformers.PreTrainedModel`): + The model to train, preferably an `AutoModelForSequenceClassification`. + args (`RewardConfig`): + The arguments to use for training. + data_collator (`transformers.DataCollator`): + The data collator to use for training. If None is specified, the default data collator (`RewardDataCollatorWithPadding`) will be used + which will pad the sequences to the maximum length of the sequences in the batch, given a dataset of paired sequences. + train_dataset (`datasets.Dataset`): + The dataset to use for training. + eval_dataset (`datasets.Dataset`): + The dataset to use for evaluation. + tokenizer (`transformers.PreTrainedTokenizerBase`): + The tokenizer to use for training. This argument is required if you want to use the default data collator. + model_init (`Callable[[], transformers.PreTrainedModel]`): + The model initializer to use for training. If None is specified, the default model initializer will be used. + compute_metrics (`Callable[[transformers.EvalPrediction], Dict]`, *optional* defaults to `compute_accuracy`): + The metrics to use for evaluation. If no metrics are specified, the default metric (`compute_accuracy`) will be used. + callbacks (`List[transformers.TrainerCallback]`): + The callbacks to use for training. + optimizers (`Tuple[torch.optim.Optimizer, torch.optim.lr_scheduler.LambdaLR]`): + The optimizer and scheduler to use for training. + preprocess_logits_for_metrics (`Callable[[torch.Tensor, torch.Tensor], torch.Tensor]`): + The function to use to preprocess the logits before computing the metrics. + max_length (`int`, defaults to `None`): + The maximum length of the sequences in the batch. This argument is required if you want to use the default data collator. + peft_config (`Dict`, defaults to `None`): + The PEFT configuration to use for training. If you pass a PEFT configuration, the model will be wrapped in a PEFT model. + """ + if type(args) == TrainingArguments: + warnings.warn( + "Using `transformers.TrainingArguments` for `args` is deprecated and will be removed in a future version. Please use `RewardConfig` instead.", + FutureWarning, + ) + if max_length is not None: + warnings.warn( + "The `max_length` argument is deprecated and will be removed in a future version. Please use the `RewardConfig` to set `max_length` instead.", + FutureWarning, + ) + else: + if max_length is not None and args.max_length is not None: + raise ValueError("You cannot specify both `max_length` and `args.max_length`. Please use the `RewardConfig` to set `max_length` once.") + if max_length is not None and args.max_length is None: + warnings.warn( + "The `max_length` argument is deprecated and will be removed in a future version. Please use the `RewardConfig` to set `max_length` instead.", + FutureWarning, + ) + if not is_peft_available() and peft_config is not None: + raise ValueError("PEFT is not installed and you passed a `peft_config` in the trainer's kwargs, please install it to use the PEFT models") + elif is_peft_available() and peft_config is not None: + if not isinstance(model, PeftModel): + if getattr(model, "is_loaded_in_8bit", False) or getattr(model, "is_quantized", False): + _supports_gc_kwargs = "gradient_checkpointing_kwargs" in list(inspect.signature(prepare_model_for_kbit_training).parameters) + + preprare_model_kwargs = {"use_gradient_checkpointing": args.gradient_checkpointing} + + if not _supports_gc_kwargs and args.gradient_checkpointing_kwargs is not None: + warnings.warn("You passed `gradient_checkpointing_kwargs` in the trainer's kwargs, but your peft version does not support it. " "please update to the latest version of peft to use `gradient_checkpointing_kwargs`.") + elif _supports_gc_kwargs and args.gradient_checkpointing_kwargs is not None: + preprare_model_kwargs["gradient_checkpointing_kwargs"] = args.gradient_checkpointing_kwargs + + model = prepare_model_for_kbit_training(model, **preprare_model_kwargs) + + model = get_peft_model(model, peft_config) + + if compute_metrics is None: + compute_metrics = compute_accuracy + + if data_collator is None: + if tokenizer is None: + raise ValueError("max_length or a tokenizer must be specified when using the default RewardDataCollatorWithPadding") + if type(args) == TrainingArguments: + if max_length is None: + warnings.warn( + "When using RewardDataCollatorWithPadding, you should set `max_length` in RewardConfig." " It will be set to `512` by default, but you should do it yourself in the future.", + UserWarning, + ) + max_length = 512 + else: + if max_length is None and args.max_length is None: + warnings.warn( + "When using RewardDataCollatorWithPadding, you should set `max_length` in RewardConfig." " It will be set to `512` by default, but you should do it yourself in the future.", + UserWarning, + ) + max_length = 512 + if max_length is None and args.max_length is not None: + max_length = args.max_length + + data_collator = RewardDataCollatorWithPadding(tokenizer, max_length=max_length) + + if args.remove_unused_columns: + try: # for bc before https://github.com/huggingface/transformers/pull/25435 + args.remove_unused_columns = False + except FrozenInstanceError: + args = replace(args, remove_unused_columns=False) + # warn users + warnings.warn( + "When using RewardDataCollatorWithPadding, you should set `remove_unused_columns=False` in your RewardConfig" " we have set it for you, but you should do it yourself in the future.", + UserWarning, + ) + + self.use_reward_data_collator = True + else: + self.use_reward_data_collator = False + super().__init__( + model, + args, + data_collator, + train_dataset, + eval_dataset, + tokenizer, + model_init, + compute_metrics, + callbacks, + optimizers, + preprocess_logits_for_metrics, + ) + + def compute_loss( + self, + model: Union[PreTrainedModel, nn.Module], + inputs: Dict[str, Union[torch.Tensor, Any]], + return_outputs=False, + ) -> Union[torch.Tensor, Tuple[torch.Tensor, Dict[str, torch.Tensor]]]: + if not self.use_reward_data_collator: + warnings.warn("The current compute_loss is implemented for RewardDataCollatorWithPadding," " if you are using a custom data collator make sure you know what you are doing or" " implement your own compute_loss method.") + rewards_chosen = model( + input_ids=inputs["input_ids_chosen"], + attention_mask=inputs["attention_mask_chosen"], + return_dict=True, + )["logits"] + rewards_rejected = model( + input_ids=inputs["input_ids_rejected"], + attention_mask=inputs["attention_mask_rejected"], + return_dict=True, + )["logits"] + # calculate loss, optionally modulate with margin + if "margin" in inputs: + loss = -nn.functional.logsigmoid(rewards_chosen - rewards_rejected - inputs["margin"]).mean() + else: + loss = -nn.functional.logsigmoid(rewards_chosen - rewards_rejected).mean() + + if return_outputs: + return loss, { + "rewards_chosen": rewards_chosen, + "rewards_rejected": rewards_rejected, + } + return loss + + def prediction_step( + self, + model: Union[PreTrainedModel, nn.Module], + inputs: Dict[str, Union[torch.Tensor, Any]], + prediction_loss_only: bool, + ignore_keys: Optional[List[str]] = None, + ) -> Tuple[Optional[torch.Tensor], Optional[torch.Tensor], Optional[torch.Tensor]]: + inputs = self._prepare_inputs(inputs) + if ignore_keys is None: + if hasattr(self.model, "config"): + ignore_keys = getattr(self.model.config, "keys_to_ignore_at_inference", []) + else: + ignore_keys = [] + + with torch.no_grad(): + loss, logits_dict = self.compute_loss(model, inputs, return_outputs=True) + + if prediction_loss_only: + return (loss, None, None) + + loss = loss.detach() + logits = tuple(v for k, v in logits_dict.items() if k not in ignore_keys) + logits = nested_detach(logits) + # Stack accepted against rejected, mean over logits + # and softmax to get preferences between accepted and rejected to sum to 1 + logits = torch.stack(logits).mean(dim=2).softmax(dim=0).T + + labels = torch.zeros(logits.shape[0]) + labels = self._prepare_inputs(labels) + + return loss, logits, labels diff --git a/trl/trainer/sft_trainer.py b/trl/trainer/sft_trainer.py new file mode 100644 index 0000000..69f3275 --- /dev/null +++ b/trl/trainer/sft_trainer.py @@ -0,0 +1,480 @@ +# Copyright 2023 The HuggingFace Team. 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. +import dataclasses +import inspect +import warnings +from functools import wraps +from typing import Callable, Dict, List, Optional, Tuple, Union + +import torch +import torch.nn as nn +from accelerate.state import PartialState +from datasets import Dataset +from datasets.arrow_writer import SchemaInferenceError +from datasets.builder import DatasetGenerationError +from transformers import ( + AutoModelForCausalLM, + AutoTokenizer, + DataCollator, + DataCollatorForLanguageModeling, + PreTrainedModel, + PreTrainedTokenizerBase, + Trainer, + TrainingArguments, +) +from transformers.modeling_utils import unwrap_model +from transformers.trainer_callback import TrainerCallback +from transformers.trainer_utils import EvalPrediction + +from ..extras.dataset_formatting import get_formatting_func_from_dataset +from ..import_utils import is_peft_available +from .utils import ( + ConstantLengthDataset, + DataCollatorForCompletionOnlyLM, + neftune_post_forward_hook, + peft_module_casting_to_bf16, + trl_sanitze_kwargs_for_tagging, +) + + +if is_peft_available(): + from peft import PeftConfig, PeftModel, get_peft_model, prepare_model_for_kbit_training + + +class SFTTrainer(Trainer): + r""" + Class definition of the Supervised Finetuning Trainer (SFT Trainer). + This class is a wrapper around the `transformers.Trainer` class and inherits all of its attributes and methods. + The trainer takes care of properly initializing the PeftModel in case a user passes a `PeftConfig` object. + + Args: + model (Union[`transformers.PreTrainedModel`, `nn.Module`, `str`]): + The model to train, can be a `PreTrainedModel`, a `torch.nn.Module` or a string with the model name to + load from cache or download. The model can be also converted to a `PeftModel` if a `PeftConfig` object is + passed to the `peft_config` argument. + args (Optional[`transformers.TrainingArguments`]): + The arguments to tweak for training. Please refer to the official documentation of `transformers.TrainingArguments` + for more information. + data_collator (Optional[`transformers.DataCollator`]): + The data collator to use for training. + train_dataset (Optional[`datasets.Dataset`]): + The dataset to use for training. We recommend users to use `trl.trainer.ConstantLengthDataset` to create their dataset. + eval_dataset (Optional[Union[`datasets.Dataset`, Dict[`str`, `datasets.Dataset`]]]): + The dataset to use for evaluation. We recommend users to use `trl.trainer.ConstantLengthDataset` to create their dataset. + tokenizer (Optional[`transformers.PreTrainedTokenizer`]): + The tokenizer to use for training. If not specified, the tokenizer associated to the model will be used. + model_init (`Callable[[], transformers.PreTrainedModel]`): + The model initializer to use for training. If None is specified, the default model initializer will be used. + compute_metrics (`Callable[[transformers.EvalPrediction], Dict]`, *optional* defaults to None): + The function used to compute metrics during evaluation. It should return a dictionary mapping metric names to metric values. + If not specified, only the loss will be computed during evaluation. + callbacks (`List[transformers.TrainerCallback]`): + The callbacks to use for training. + optimizers (`Tuple[torch.optim.Optimizer, torch.optim.lr_scheduler.LambdaLR]`): + The optimizer and scheduler to use for training. + preprocess_logits_for_metrics (`Callable[[torch.Tensor, torch.Tensor], torch.Tensor]`): + The function to use to preprocess the logits before computing the metrics. + peft_config (`Optional[PeftConfig]`): + The PeftConfig object to use to initialize the PeftModel. + dataset_text_field (`Optional[str]`): + The name of the text field of the dataset, in case this is passed by a user, the trainer will automatically create a + `ConstantLengthDataset` based on the `dataset_text_field` argument. + formatting_func (`Optional[Callable]`): + The formatting function to be used for creating the `ConstantLengthDataset`. + max_seq_length (`Optional[int]`): + The maximum sequence length to use for the `ConstantLengthDataset` and for automatically creating the Dataset. Defaults to `512`. + infinite (`Optional[bool]`): + Whether to use an infinite dataset or not. Defaults to `False`. + num_of_sequences (`Optional[int]`): + The number of sequences to use for the `ConstantLengthDataset`. Defaults to `1024`. + chars_per_token (`Optional[float]`): + The number of characters per token to use for the `ConstantLengthDataset`. Defaults to `3.6`. You can check how this is computed in the + stack-llama example: https://github.com/huggingface/trl/blob/08f550674c553c36c51d1027613c29f14f3676a5/examples/stack_llama/scripts/supervised_finetuning.py#L53. + packing (`Optional[bool]`): + Used only in case `dataset_text_field` is passed. This argument is used by the `ConstantLengthDataset` to pack the sequences + of the dataset. + dataset_num_proc (`Optional[int]`): + The number of workers to use to tokenize the data. Only used when `packing=False`. Defaults to None. + dataset_batch_size (`int`): + The number of examples to tokenize per batch. If batch_size <= 0 or batch_size == None, + tokenize the full dataset as a single batch. Defaults to 1000. + neftune_noise_alpha (`Optional[float]`): + If not `None`, this will activate NEFTune noise embeddings. This has been proven to drastically improve model performances for instruction + fine-tuning. Check out the original paper here: https://arxiv.org/abs/2310.05914 and the original code here: https://github.com/neelsjain/NEFTune + model_init_kwargs: (`Optional[Dict]`, *optional*): + Dict of Optional kwargs to pass when instantiating the model from a string + dataset_kwargs: (`Optional[Dict]`, *optional*): + Dict of Optional kwargs to pass when creating packed or non-packed datasets + """ + + _tag_names = ["trl", "sft"] + + def __init__( + self, + model: Union[PreTrainedModel, nn.Module, str] = None, + args: TrainingArguments = None, + data_collator: Optional[DataCollator] = None, + train_dataset: Optional[Dataset] = None, + eval_dataset: Optional[Union[Dataset, Dict[str, Dataset]]] = None, + tokenizer: Optional[PreTrainedTokenizerBase] = None, + model_init: Optional[Callable[[], PreTrainedModel]] = None, + compute_metrics: Optional[Callable[[EvalPrediction], Dict]] = None, + callbacks: Optional[List[TrainerCallback]] = None, + optimizers: Tuple[torch.optim.Optimizer, torch.optim.lr_scheduler.LambdaLR] = (None, None), + preprocess_logits_for_metrics: Optional[Callable[[torch.Tensor, torch.Tensor], torch.Tensor]] = None, + peft_config: Optional["PeftConfig"] = None, + dataset_text_field: Optional[str] = None, + packing: Optional[bool] = False, + formatting_func: Optional[Callable] = None, + max_seq_length: Optional[int] = None, + infinite: Optional[bool] = None, + num_of_sequences: Optional[int] = 1024, + chars_per_token: Optional[float] = 3.6, + dataset_num_proc: Optional[int] = None, + dataset_batch_size: int = 1000, + neftune_noise_alpha: Optional[float] = None, + model_init_kwargs: Optional[Dict] = None, + dataset_kwargs: Optional[Dict] = None, + ): + if model_init_kwargs is None: + model_init_kwargs = {} + elif not isinstance(model, str): + raise ValueError("You passed model_kwargs to the SFTTrainer. But your model is already instantiated.") + + if infinite is not None: + warnings.warn("The `infinite` argument is deprecated and will be removed in a future version of TRL. Use `TrainingArguments.max_steps` or `TrainingArguments.num_train_epochs` instead to control training length.") + + if isinstance(model, str): + warnings.warn("You passed a model_id to the SFTTrainer. This will automatically create an " "`AutoModelForCausalLM` or a `PeftModel` (if you passed a `peft_config`) for you.") + model = AutoModelForCausalLM.from_pretrained(model, **model_init_kwargs) + + if packing and data_collator is not None and isinstance(data_collator, DataCollatorForCompletionOnlyLM): + raise ValueError("You passed a `DataCollatorForCompletionOnlyLM` to the SFTTrainer. This is not compatible with the `packing` argument.") + + if is_peft_available() and peft_config is not None: + if not isinstance(peft_config, PeftConfig): + raise ValueError("If you want to use the PeftModel, you need to pass a PeftConfig object to the SFTTrainer." f" and you passed a {type(peft_config)}.") + + if not isinstance(model, PeftModel): + _support_gc_kwargs = hasattr(args, "gradient_checkpointing_kwargs") and "gradient_checkpointing_kwargs" in list(inspect.signature(prepare_model_for_kbit_training).parameters) + gradient_checkpointing_kwargs = getattr(args, "gradient_checkpointing_kwargs", None) or {} + if getattr(model, "is_loaded_in_8bit", False) or getattr(model, "is_loaded_in_4bit", False): + preprare_model_kwargs = {"use_gradient_checkpointing": getattr(args, "gradient_checkpointing", False)} + + if _support_gc_kwargs: + preprare_model_kwargs["gradient_checkpointing_kwargs"] = gradient_checkpointing_kwargs + + model = prepare_model_for_kbit_training(model, **preprare_model_kwargs) + + if args is not None: + args = dataclasses.replace(args, gradient_checkpointing=False) + elif getattr(args, "gradient_checkpointing", False) and ("use_reentrant" not in gradient_checkpointing_kwargs or gradient_checkpointing_kwargs["use_reentrant"]): + # For backward compatibility with older versions of transformers + if hasattr(model, "enable_input_require_grads"): + model.enable_input_require_grads() + else: + + def make_inputs_require_grad(module, input, output): + output.requires_grad_(True) + + model.get_input_embeddings().register_forward_hook(make_inputs_require_grad) + + model = get_peft_model(model, peft_config) + if args.bf16 and getattr(model, "is_loaded_in_4bit", False): + peft_module_casting_to_bf16(model) + + if tokenizer is None: + tokenizer = AutoTokenizer.from_pretrained(model.config._name_or_path) + if getattr(tokenizer, "pad_token", None) is None: + tokenizer.pad_token = tokenizer.eos_token + + if max_seq_length is None: + # to overcome some issues with broken tokenizers + max_seq_length = min(tokenizer.model_max_length, 1024) + + warnings.warn(f"You didn't pass a `max_seq_length` argument to the SFTTrainer, this will default to {max_seq_length}") + + self.dataset_num_proc = dataset_num_proc + self.dataset_batch_size = dataset_batch_size + + self._trainer_supports_neftune = hasattr(args, "neftune_noise_alpha") + + if neftune_noise_alpha is not None and self._trainer_supports_neftune: + args.neftune_noise_alpha = neftune_noise_alpha + warnings.warn("You passed a `neftune_noise_alpha` argument to the SFTTrainer, the value you passed will override the one in the `TrainingArguments`.") + # self.neftune_noise_alpha is done at Trainer level + elif not self._trainer_supports_neftune: + self.neftune_noise_alpha = neftune_noise_alpha + + if formatting_func is None and dataset_text_field is None: + # check if dataset has ChatML format or instruction format and is supported + # if not stays #None + formatting_func = get_formatting_func_from_dataset(train_dataset, tokenizer) + + if not packing: + if dataset_text_field is None and formatting_func is None: + raise ValueError("You passed `packing=False` to the SFTTrainer, but you didn't pass a `dataset_text_field` or `formatting_func` argument.") + + if data_collator is None: + data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False) + + # Pre-process the datasets only once per node. The remaining processes will use the cache. + with PartialState().local_main_process_first(): + if dataset_kwargs is None: + dataset_kwargs = {} + if train_dataset is not None: + train_dataset = self._prepare_dataset( + train_dataset, + tokenizer, + packing, + dataset_text_field, + max_seq_length, + formatting_func, + num_of_sequences, + chars_per_token, + remove_unused_columns=args.remove_unused_columns if args is not None else True, + **dataset_kwargs, + ) + if eval_dataset is not None: + _multiple = isinstance(eval_dataset, dict) + _eval_datasets = eval_dataset if _multiple else {"singleton": eval_dataset} + for _eval_dataset_name, _eval_dataset in _eval_datasets.items(): + _eval_datasets[_eval_dataset_name] = self._prepare_dataset( + _eval_dataset, + tokenizer, + packing, + dataset_text_field, + max_seq_length, + formatting_func, + num_of_sequences, + chars_per_token, + remove_unused_columns=args.remove_unused_columns if args is not None else True, + **dataset_kwargs, + ) + if not _multiple: + eval_dataset = _eval_datasets["singleton"] + + if tokenizer.padding_side is not None and tokenizer.padding_side != "right": + warnings.warn( + "You passed a tokenizer with `padding_side` not equal to `right` to the SFTTrainer. This might lead to some unexpected behaviour due to " + "overflow issues when training a model in half-precision. You might consider adding `tokenizer.padding_side = 'right'` to your code." + ) + + super().__init__( + model=model, + args=args, + data_collator=data_collator, + train_dataset=train_dataset, + eval_dataset=eval_dataset, + tokenizer=tokenizer, + model_init=model_init, + compute_metrics=compute_metrics, + callbacks=callbacks, + optimizers=optimizers, + preprocess_logits_for_metrics=preprocess_logits_for_metrics, + ) + + if self.args.max_steps > 0 and packing: + warnings.warn("You passed `packing=True` to the SFTTrainer, and you are training your model with `max_steps` strategy. The dataset will be iterated until the `max_steps` are reached.") + self.train_dataset.infinite = True + elif self.args.max_steps == -1 and packing: + self.train_dataset.infinite = False + + @wraps(Trainer.train) + def train(self, *args, **kwargs): + # Activate neftune right before training. + if self.neftune_noise_alpha is not None and not self._trainer_supports_neftune: + self.model = self._trl_activate_neftune(self.model) + + output = super().train(*args, **kwargs) + + # After training we make sure to retrieve back the original forward pass method + # for the embedding layer by removing the forward post hook. + if self.neftune_noise_alpha is not None and not self._trainer_supports_neftune: + unwrapped_model = unwrap_model(self.model) + if is_peft_available() and isinstance(unwrapped_model, PeftModel): + embeddings = unwrapped_model.base_model.model.get_input_embeddings() + else: + embeddings = unwrapped_model.get_input_embeddings() + + self.neftune_hook_handle.remove() + del embeddings.neftune_noise_alpha + + return output + + @wraps(Trainer.push_to_hub) + def push_to_hub(self, commit_message: Optional[str] = "End of training", blocking: bool = True, **kwargs) -> str: + """ + Overwrite the `push_to_hub` method in order to force-add the tag "sft" when pushing the + model on the Hub. Please refer to `~transformers.Trainer.push_to_hub` for more details. + """ + kwargs = trl_sanitze_kwargs_for_tagging(model=self.model, tag_names=self._tag_names, kwargs=kwargs) + + return super().push_to_hub(commit_message=commit_message, blocking=blocking, **kwargs) + + def _prepare_dataset( + self, + dataset, + tokenizer, + packing, + dataset_text_field, + max_seq_length, + formatting_func, + num_of_sequences, + chars_per_token, + remove_unused_columns=True, + append_concat_token=True, + add_special_tokens=True, + ): + if dataset is None: + raise ValueError("The dataset should not be None") + + # check if torch dataset / dataloader and do nothing + if isinstance(dataset, (torch.utils.data.IterableDataset, torch.utils.data.Dataset, ConstantLengthDataset)): + return dataset + + if not packing: + return self._prepare_non_packed_dataloader( + tokenizer, + dataset, + dataset_text_field, + max_seq_length, + formatting_func, + add_special_tokens, + remove_unused_columns, + ) + + else: + return self._prepare_packed_dataloader( + tokenizer, + dataset, + dataset_text_field, + max_seq_length, + num_of_sequences, + chars_per_token, + formatting_func, + append_concat_token, + add_special_tokens, + ) + + def _prepare_non_packed_dataloader( + self, + tokenizer, + dataset, + dataset_text_field, + max_seq_length, + formatting_func=None, + add_special_tokens=True, + remove_unused_columns=True, + ): + use_formatting_func = formatting_func is not None and dataset_text_field is None + self._dataset_sanity_checked = False + + # Inspired from: https://huggingface.co/learn/nlp-course/chapter7/6?fw=pt + def tokenize(element): + outputs = tokenizer( + element[dataset_text_field] if not use_formatting_func else formatting_func(element), + add_special_tokens=add_special_tokens, + truncation=True, + padding=False, + max_length=max_seq_length, + return_overflowing_tokens=False, + return_length=False, + ) + + if use_formatting_func and not self._dataset_sanity_checked: + if not isinstance(formatting_func(element), list): + raise ValueError("The `formatting_func` should return a list of processed strings since it can lead to silent bugs.") + else: + self._dataset_sanity_checked = True + + return {"input_ids": outputs["input_ids"], "attention_mask": outputs["attention_mask"]} + + signature_columns = ["input_ids", "labels", "attention_mask"] + + extra_columns = list(set(dataset.column_names) - set(signature_columns)) + + if not remove_unused_columns and len(extra_columns) > 0: + warnings.warn( + "You passed `remove_unused_columns=False` on a non-packed dataset. This might create some issues with the default collator and yield to errors. If you want to " + f"inspect dataset other columns (in this case {extra_columns}), you can subclass `DataCollatorForLanguageModeling` in case you used the default collator and create your own data collator in order to inspect the unused dataset columns." + ) + + tokenized_dataset = dataset.map( + tokenize, + batched=True, + remove_columns=dataset.column_names if remove_unused_columns else None, + num_proc=self.dataset_num_proc, + batch_size=self.dataset_batch_size, + ) + + return tokenized_dataset + + def _prepare_packed_dataloader( + self, + tokenizer, + dataset, + dataset_text_field, + max_seq_length, + num_of_sequences, + chars_per_token, + formatting_func=None, + append_concat_token=True, + add_special_tokens=True, + ): + if dataset_text_field is not None or formatting_func is not None: + if tokenizer is None: + raise ValueError("You need to pass a tokenizer when using `dataset_text_field` with `SFTTrainer`.") + + constant_length_iterator = ConstantLengthDataset( + tokenizer, + dataset, + dataset_text_field=dataset_text_field, + formatting_func=formatting_func, + seq_length=max_seq_length, + infinite=False, + num_of_sequences=num_of_sequences, + chars_per_token=chars_per_token, + eos_token_id=tokenizer.eos_token_id, + append_concat_token=append_concat_token, + add_special_tokens=add_special_tokens, + ) + + def data_generator(constant_length_iterator): + for i in constant_length_iterator: + yield i + + try: + packed_dataset = Dataset.from_generator(data_generator, gen_kwargs={"constant_length_iterator": constant_length_iterator}) + except (DatasetGenerationError, SchemaInferenceError): + raise ValueError("Error occurred while packing the dataset. Make sure that your dataset has enough samples to at least yield one packed sequence.") + return packed_dataset + else: + raise ValueError("You need to pass a `dataset_text_field` or `formatting_func` argument to the SFTTrainer if you want to use the `ConstantLengthDataset`.") + + def _trl_activate_neftune(self, model): + r""" + Activates the neftune as presented in this code: https://github.com/neelsjain/NEFTune and paper: https://arxiv.org/abs/2310.05914 + Since in transformers Trainer we do have an `_activate_neftune` method, we need to rename this method to avoid conflicts. + """ + unwrapped_model = unwrap_model(model) + if is_peft_available() and isinstance(unwrapped_model, PeftModel): + embeddings = unwrapped_model.base_model.model.get_input_embeddings() + else: + embeddings = unwrapped_model.get_input_embeddings() + + embeddings.neftune_noise_alpha = self.neftune_noise_alpha + hook_handle = embeddings.register_forward_hook(neftune_post_forward_hook) + self.neftune_hook_handle = hook_handle + return model diff --git a/trl/trainer/utils.py b/trl/trainer/utils.py new file mode 100644 index 0000000..1c566fa --- /dev/null +++ b/trl/trainer/utils.py @@ -0,0 +1,703 @@ +# Copyright 2022 The HuggingFace Team. 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. +import random +import warnings +from collections import deque +from dataclasses import dataclass +from typing import Any, Dict, List, Optional, Tuple, Union + +import numpy as np +import torch +from accelerate import PartialState +from torch.nn.utils.rnn import pad_sequence +from torch.utils.data import IterableDataset +from transformers import BitsAndBytesConfig, DataCollatorForLanguageModeling, PreTrainedTokenizerBase + +from ..import_utils import is_peft_available, is_unsloth_available, is_xpu_available +from ..trainer.model_config import ModelConfig + + +if is_peft_available(): + from peft import LoraConfig, PeftConfig + + +class AdaptiveKLController: + """ + Adaptive KL controller described in the paper: + https://arxiv.org/pdf/1909.08593.pdf + """ + + def __init__(self, init_kl_coef, target, horizon): + self.value = init_kl_coef + self.target = target + self.horizon = horizon + + def update(self, current, n_steps): + target = self.target + proportional_error = np.clip(current / target - 1, -0.2, 0.2) + mult = 1 + proportional_error * n_steps / self.horizon + self.value *= mult + + +class FixedKLController: + """Fixed KL controller.""" + + def __init__(self, kl_coef): + self.value = kl_coef + + def update(self, current, n_steps): + pass + + +class DataCollatorForCompletionOnlyLM(DataCollatorForLanguageModeling): + """ + Data collator used for completion tasks. It ensures that all the tokens of the labels are set to an 'ignore_index' + when they do not come from the assistant. This ensure that the loss is only + calculated on the completion made by the assistant. + + Args: + response_template (`Union[str, List[int]]`): the template form that indicates the start of the response, typically something like + '### Response:\n'. It can also be passed as tokenized ids, which can be useful when using a tokenizer that encodes the response + differently if it does not have proper context. + instruction_template (`Union[str, List[int]]`): the template form that indicates the start of the human instruction, typically something like + '### Human:\n'. Useful for assistant-style conversation datasets. It can also be passed as tokenized ids. + mlm (`bool`, *optional*, defaults to `False`): Whether or not to use masked language modeling in the underlying + `DataCollatorForLanguageModeling` class. Note that this option currently has no effect but is present + for flexibility and backwards-compatibility. + ignore_index (`int`, *optional*, defaults to `-100`): + The index to use to ignore the initial tokens with + """ + + def __init__( + self, + response_template: Union[str, List[int]], + instruction_template: Union[str, List[int]] = None, + *args, + mlm: bool = False, + ignore_index: int = -100, + **kwargs, + ): + super().__init__(*args, mlm=mlm, **kwargs) + + self.instruction_template = instruction_template + if isinstance(instruction_template, str): + # The user provides a string, must tokenize + self.instruction_token_ids = self.tokenizer.encode(self.instruction_template, add_special_tokens=False) + else: + # The user already provides the token ids + self.instruction_token_ids = instruction_template + + self.response_template = response_template + if isinstance(response_template, str): + # The user provides a string, must tokenize + self.response_token_ids = self.tokenizer.encode(self.response_template, add_special_tokens=False) + else: + # The user already provides the token ids + self.response_token_ids = response_template + + if not self.mlm and self.instruction_template and self.tokenizer.pad_token_id == self.tokenizer.eos_token_id: + warnings.warn( + "The pad_token_id and eos_token_id values of this tokenizer are identical. " + "If you are planning for multi-turn training, " + "it can result in the model continuously generating questions and answers without eos token. " + "To avoid this, set the pad_token_id to a different value." + ) + + self.ignore_index = ignore_index + + def torch_call(self, examples: List[Union[List[int], Any, Dict[str, Any]]]) -> Dict[str, Any]: + batch = super().torch_call(examples) + + if self.instruction_template is None: + for i in range(len(examples)): + response_token_ids_start_idx = None + + for idx in np.where(batch["labels"][i] == self.response_token_ids[0])[0]: + # `response_token_ids` is `'### Response:\n'`, here we are just making sure that the token IDs match + if self.response_token_ids == batch["labels"][i][idx : idx + len(self.response_token_ids)].tolist(): + response_token_ids_start_idx = idx + + if response_token_ids_start_idx is None: + warnings.warn( + f"Could not find response key `{self.response_template}` in the " + f'following instance: {self.tokenizer.decode(batch["input_ids"][i])} ' + f"This instance will be ignored in loss calculation. " + f"Note, if this happens often, consider increasing the `max_seq_length`." + ) + batch["labels"][i, :] = self.ignore_index + else: + response_token_ids_end_idx = response_token_ids_start_idx + len(self.response_token_ids) + + # Make pytorch loss function ignore all tokens up through the end of the response key + batch["labels"][i, :response_token_ids_end_idx] = self.ignore_index + + else: + for i in range(len(examples)): + response_token_ids_idxs = [] + human_token_ids_idxs = [] + + for assistant_idx in np.where(batch["labels"][i] == self.response_token_ids[0])[0]: + # find the indexes of the start of a response. + if self.response_token_ids == batch["labels"][i][assistant_idx : assistant_idx + len(self.response_token_ids)].tolist(): + response_token_ids_idxs.append(assistant_idx + len(self.response_token_ids)) + + if len(response_token_ids_idxs) == 0: + warnings.warn( + f"Could not find response key `{self.response_template}` in the " + f'following instance: {self.tokenizer.decode(batch["input_ids"][i])} ' + f"This instance will be ignored in loss calculation. " + f"Note, if this happens often, consider increasing the `max_seq_length`." + ) + batch["labels"][i, :] = self.ignore_index + + human_token_ids = self.instruction_token_ids + for human_idx in np.where(batch["labels"][i] == human_token_ids[0])[0]: + # find the indexes of the start of a human answer. + if human_token_ids == batch["labels"][i][human_idx : human_idx + len(human_token_ids)].tolist(): + human_token_ids_idxs.append(human_idx) + + if len(human_token_ids_idxs) == 0: + warnings.warn( + f"Could not find instruction key `{self.instruction_template}` in the " + f'following instance: {self.tokenizer.decode(batch["input_ids"][i])} ' + f"This instance will be ignored in loss calculation. " + f"Note, if this happens often, consider increasing the `max_seq_length`." + ) + batch["labels"][i, :] = self.ignore_index + + if len(human_token_ids_idxs) > 0 and len(response_token_ids_idxs) > 0 and human_token_ids_idxs[0] > response_token_ids_idxs[0]: + human_token_ids_idxs = [0] + human_token_ids_idxs + + for idx, (start, end) in enumerate(zip(human_token_ids_idxs, response_token_ids_idxs)): + # Make pytorch loss function ignore all non response tokens + if idx != 0: + batch["labels"][i, start:end] = self.ignore_index + else: + batch["labels"][i, :end] = self.ignore_index + + if len(response_token_ids_idxs) < len(human_token_ids_idxs): + batch["labels"][i, human_token_ids_idxs[-1] :] = self.ignore_index + + return batch + + +@dataclass +class RewardDataCollatorWithPadding: + r""" + Reward DataCollator class that pads the inputs to the maximum length of the batch. + Args: + tokenizer (`PreTrainedTokenizerBase`): + The tokenizer used for encoding the data. + padding (`Union[bool, str, `PaddingStrategy`]`, `optional`, defaults to `True`): + padding_strategy to pass to the tokenizer. + max_length (`Optional[int]`, `optional`, defaults to `None`): + The maximum length of the sequence to be processed. + pad_to_multiple_of (`Optional[int]`, `optional`, defaults to `None`): + If set will pad the sequence to a multiple of the provided value. + return_tensors (`str`, `optional`, defaults to `"pt"`): + The tensor type to use. + """ + + tokenizer: PreTrainedTokenizerBase + padding: Union[bool, str] = True + max_length: Optional[int] = None + pad_to_multiple_of: Optional[int] = None + return_tensors: str = "pt" + + def __call__(self, features: List[Dict[str, Any]]) -> Dict[str, Any]: + features_chosen = [] + features_rejected = [] + margin = [] + # check if we have a margin. If we do, we need to batch it as well + has_margin = "margin" in features[0] + for feature in features: + # check if the keys are named as expected + if "input_ids_chosen" not in feature or "input_ids_rejected" not in feature or "attention_mask_chosen" not in feature or "attention_mask_rejected" not in feature: + raise ValueError("The features should include `input_ids_chosen`, `attention_mask_chosen`, `input_ids_rejected` and `attention_mask_rejected`") + + features_chosen.append( + { + "input_ids": feature["input_ids_chosen"], + "attention_mask": feature["attention_mask_chosen"], + } + ) + features_rejected.append( + { + "input_ids": feature["input_ids_rejected"], + "attention_mask": feature["attention_mask_rejected"], + } + ) + if has_margin: + margin.append(feature["margin"]) + batch_chosen = self.tokenizer.pad( + features_chosen, + padding=self.padding, + max_length=self.max_length, + pad_to_multiple_of=self.pad_to_multiple_of, + return_tensors=self.return_tensors, + ) + batch_rejected = self.tokenizer.pad( + features_rejected, + padding=self.padding, + max_length=self.max_length, + pad_to_multiple_of=self.pad_to_multiple_of, + return_tensors=self.return_tensors, + ) + batch = { + "input_ids_chosen": batch_chosen["input_ids"], + "attention_mask_chosen": batch_chosen["attention_mask"], + "input_ids_rejected": batch_rejected["input_ids"], + "attention_mask_rejected": batch_rejected["attention_mask"], + "return_loss": True, + } + if has_margin: + margin = torch.tensor(margin, dtype=torch.float) + batch["margin"] = margin + return batch + + +@dataclass +class DPODataCollatorWithPadding: + r""" + DPO DataCollator class that pads the tokenized inputs to the maximum length of the batch. + Args: + pad_token_id (`int` defaults to 0): + The tokenizer's pad_token_id. + label_pad_token_id (`int`, defaults to -100): + The label used for masking. + is_encoder_decoder (`Optional[bool]`, `optional`, defaults to `None`): + Whether or not you model has an encoder_decoder architecture. + """ + + tokenizer: PreTrainedTokenizerBase + pad_token_id: int = 0 + label_pad_token_id: int = -100 + is_encoder_decoder: Optional[bool] = False + + def __call__(self, features: List[Dict[str, Any]]) -> Dict[str, Any]: + # first, pad everything to the same length + padded_batch = {} + for k in features[0].keys(): + if k.endswith("_input_ids") or k.endswith("_attention_mask") or k.endswith("_labels"): + if self.is_encoder_decoder: + to_pad = [torch.LongTensor(ex[k]) for ex in features] + + if (k.startswith("prompt")) and (k.endswith("input_ids")): + if self.pad_token_id is None: + raise ValueError( + "Padding is enabled, but the tokenizer is not configured with a padding token." " Explicitly set `tokenizer.pad_token` (e.g. `tokenizer.pad_token = tokenizer.eos_token`)" " before calling the trainer." + ) + padding_value = self.pad_token_id + elif k.endswith("_attention_mask"): + padding_value = 0 + elif (k.startswith("chosen")) or (k.startswith("rejected")) or ("decoder" in k): + padding_value = self.label_pad_token_id + else: + raise ValueError(f"Unexpected key in batch '{k}'") + padded_batch[k] = pad_sequence(to_pad, batch_first=True, padding_value=padding_value) + else: + # adapted from https://stackoverflow.com/questions/73256206 + if "prompt" in k: + to_pad = [torch.LongTensor(ex[k][::-1]) for ex in features] + else: + to_pad = [torch.LongTensor(ex[k]) for ex in features] + if k.endswith("_input_ids"): + if self.pad_token_id is None: + raise ValueError( + "Padding is enabled, but the tokenizer is not configured with a padding token." " Explicitly set `tokenizer.pad_token` (e.g. `tokenizer.pad_token = tokenizer.eos_token`)" " before calling the trainer." + ) + padding_value = self.pad_token_id + elif k.endswith("_labels"): + padding_value = self.label_pad_token_id + elif k.endswith("_attention_mask"): + padding_value = 0 + else: + raise ValueError(f"Unexpected key in batch '{k}'") + + padded_batch[k] = pad_sequence(to_pad, batch_first=True, padding_value=padding_value) + # for the prompt, flip back so padding is on left side + if "prompt" in k: + padded_batch[k] = padded_batch[k].flip(dims=[1]) + elif k.endswith("_logps"): + # the cached reference model logprobs + padded_batch[k] = torch.tensor([ex[k] for ex in features]) + else: + padded_batch[k] = [ex[k] for ex in features] + + return padded_batch + + +class ConstantLengthDataset(IterableDataset): + """ + Iterable dataset that returns constant length chunks of tokens from stream of text files. + The dataset also formats the text before tokenization with a specific format that is provided + by the user. + + Args: + tokenizer (`transformers.PreTrainedTokenizer`): + The processor used for processing the data. + dataset (`dataset.Dataset`): + Dataset with text files. + dataset_text_field (`str`, **optional**): + Name of the field in the dataset that contains the text. Used only if `formatting_func` is `None`. + formatting_func (`Callable`, **optional**): + Function that formats the text before tokenization. Usually it is recommended to have follows a certain + pattern such as `"### Question: {question} ### Answer: {answer}"` + infinite (`bool`, *optional*, defaults to `False`): + If True the iterator is reset after dataset reaches end else stops. + seq_length (`int`, *optional*, defaults to `1024`): + Length of token sequences to return. + num_of_sequences (`int`, *optional*, defaults to `1024`): + Number of token sequences to keep in buffer. + chars_per_token (`int`, *optional*, defaults to `3.6`): + Number of characters per token used to estimate number of tokens in text buffer. + eos_token_id (`int`, *optional*, defaults to `0`): + Id of the end of sequence token if the passed tokenizer does not have an EOS token. + shuffle ('bool', *optional*, defaults to True) + Shuffle the examples before they are returned + append_concat_token ('bool', *optional*, defaults to True) + If true, appends `eos_token_id` at the end of each sample being packed. + add_special_tokens ('bool', *optional*, defaults to True) + If true, tokenizers adds special tokens to each sample being packed. + """ + + def __init__( + self, + tokenizer, + dataset, + dataset_text_field=None, + formatting_func=None, + infinite=False, + seq_length=1024, + num_of_sequences=1024, + chars_per_token=3.6, + eos_token_id=0, + shuffle=True, + append_concat_token=True, + add_special_tokens=True, + ): + self.tokenizer = tokenizer + + if tokenizer.eos_token_id is None: + warnings.warn( + "The passed tokenizer does not have an EOS token. We will use the passed eos_token_id instead which corresponds" f" to {eos_token_id}. If this is not the correct EOS token, make sure to pass the correct eos_token_id." + ) + + self.concat_token_id = tokenizer.eos_token_id if tokenizer.eos_token_id else eos_token_id + self.dataset = dataset + self.seq_length = seq_length + self.infinite = infinite + self.current_size = 0 + self.max_buffer_size = seq_length * chars_per_token * num_of_sequences + self.shuffle = shuffle + self.append_concat_token = append_concat_token + self.add_special_tokens = add_special_tokens + if formatting_func is None: + self.formatting_func = lambda x: x[dataset_text_field] + else: + self.formatting_func = formatting_func + + if formatting_func is not None: + if formatting_func.__code__.co_argcount > 1: + warnings.warn( + "The passed formatting_func has more than one argument. Usually that function should have a single argument `example`" + " which corresponds to the dictionary returned by each element of the dataset. Make sure you know what you are doing." + ) + + def __len__(self): + return len(self.dataset) + + def __iter__(self): + iterator = iter(self.dataset) + more_examples = True + while more_examples: + buffer, buffer_len = [], 0 + while True: + if buffer_len >= self.max_buffer_size: + break + try: + buffer.append(self.formatting_func(next(iterator))) + buffer_len += len(buffer[-1]) + except StopIteration: + if self.infinite: + iterator = iter(self.dataset) + warnings.warn("The dataset reached end and the iterator is reset to the start.") + else: + more_examples = False + break + tokenized_inputs = self.tokenizer(buffer, add_special_tokens=self.add_special_tokens, truncation=False)["input_ids"] + all_token_ids = [] + for tokenized_input in tokenized_inputs: + if self.append_concat_token: + tokenized_input = tokenized_input + [self.concat_token_id] + all_token_ids.extend(tokenized_input) + examples = [] + for i in range(0, len(all_token_ids), self.seq_length): + input_ids = all_token_ids[i : i + self.seq_length] + if len(input_ids) == self.seq_length: + examples.append(input_ids) + if self.shuffle: + random.shuffle(examples) + for example in examples: + self.current_size += 1 + yield { + "input_ids": torch.LongTensor(example), + "labels": torch.LongTensor(example), + } + + +class RunningMoments: + def __init__(self, accelerator): + """ + Calculates the running mean and standard deviation of a data stream. Reference: + https://github.com/OpenLMLab/MOSS-RLHF/blob/40b91eb2f2b71b16919addede0341d2bef70825d/utils.py#L75 + """ + self.mean = 0 + self.std = 1 + self.var = 1 + self.count = 1e-24 + self.accelerator = accelerator + + @torch.no_grad() + def update(self, xs: torch.Tensor) -> Tuple[float, float]: + """ + Updates running moments from batch's moments computed across ranks + """ + if self.accelerator.use_distributed: + xs_mean, xs_var, xs_count = get_global_statistics(self.accelerator, xs) + else: + xs_count = xs.numel() + xs_var, xs_mean = torch.var_mean(xs, unbiased=False) + xs_mean, xs_var = xs_mean.float(), xs_var.float() + + delta = xs_mean - self.mean + tot_count = self.count + xs_count + + new_sum = xs_var * xs_count + # correct old_sum deviation accounting for the new mean + old_sum = self.var * self.count + delta**2 * self.count * xs_count / tot_count + tot_sum = old_sum + new_sum + + self.mean += delta * xs_count / tot_count + self.var = tot_sum / tot_count + self.std = (self.var * tot_count / (tot_count - 1)).float().sqrt() + self.count = tot_count + + return xs_mean.item(), (xs_var * xs_count / (xs_count - 1)).float().sqrt().item() + + +@torch.no_grad() +def get_global_statistics(accelerator, xs: torch.Tensor, mask=None, device="cpu") -> Tuple[float, float, int]: + """ + Computes element-wise mean and variance of the tensor across processes. Reference: + https://github.com/OpenLMLab/MOSS-RLHF/blob/40b91eb2f2b71b16919addede0341d2bef70825d/utils.py#L57C1-L73C75 + """ + xs = xs.to(accelerator.device) + sum_and_count = torch.tensor([xs.sum(), (xs.numel() if mask is None else mask.sum())], device=xs.device) + sum_and_count = accelerator.reduce(sum_and_count) + global_sum, count = sum_and_count + global_mean = global_sum / count + + sum_var = torch.sum(((xs - global_mean) ** 2).mul(1 if mask is None else mask)) + sum_var = accelerator.reduce(sum_var) + global_var = sum_var / count + + return global_mean.to(device), global_var.to(device), count.to(device) + + +def compute_accuracy(eval_pred) -> Dict[str, float]: + predictions, labels = eval_pred + # Here, predictions is rewards_chosen and rewards_rejected. + # We want to see how much of the time rewards_chosen > rewards_rejected. + if np.array(predictions[:, 0] == predictions[:, 1], dtype=float).sum() > 0: + warnings.warn(f"There are {np.array(predictions[:, 0] == predictions[:, 1]).sum()} out of {len(predictions[:, 0])} instances where the predictions for both options are equal. As a consequence the accuracy can be misleading.") + predictions = np.argmax(predictions, axis=1) + + accuracy = np.array(predictions == labels, dtype=float).mean().item() + return {"accuracy": accuracy} + + +def pad_to_length(tensor: torch.Tensor, length: int, pad_value: Union[int, float], dim: int = -1) -> torch.Tensor: + if tensor.size(dim) >= length: + return tensor + else: + pad_size = list(tensor.shape) + pad_size[dim] = length - tensor.size(dim) + return torch.cat( + [ + tensor, + pad_value * torch.ones(*pad_size, dtype=tensor.dtype, device=tensor.device), + ], + dim=dim, + ) + + +def disable_dropout_in_model(model: torch.nn.Module) -> None: + for module in model.modules(): + if isinstance(module, torch.nn.Dropout): + module.p = 0 + + +def exact_div(a, b, a_str, b_str, custom_error_message=""): + q = a // b + if a != q * b: + raise ValueError(f"{custom_error_message}, {a_str}={a}, {b_str}={b}, inexact division: {a} / {b} = {a / b}") + return q + + +# copied from https://github.com/kvablack/ddpo-pytorch/blob/main/ddpo_pytorch/stat_tracking.py#L5 +class PerPromptStatTracker: + r""" + Class for tracking statistics per prompt. Mainly used to calculate advantage for the DPPO algorithm + + Args: + buffer_size (`int`): + Size of the buffer to keep for each prompt. + min_count (`int`): + Minimum number of samples to keep in the buffer before calculating the mean and std. + """ + + def __init__(self, buffer_size, min_count): + self.buffer_size = buffer_size + self.min_count = min_count + self.stats = {} + + def update(self, prompts, rewards): + prompts = np.array(prompts) + rewards = np.array(rewards) + unique = np.unique(prompts) + advantages = np.empty_like(rewards) + for prompt in unique: + prompt_rewards = rewards[prompts == prompt] + if prompt not in self.stats: + self.stats[prompt] = deque(maxlen=self.buffer_size) + self.stats[prompt].extend(prompt_rewards) + + if len(self.stats[prompt]) < self.min_count: + mean = np.mean(rewards) + std = np.std(rewards) + 1e-6 + else: + mean = np.mean(self.stats[prompt]) + std = np.std(self.stats[prompt]) + 1e-6 + advantages[prompts == prompt] = (prompt_rewards - mean) / std + + return advantages + + def get_stats(self): + return {k: {"mean": np.mean(v), "std": np.std(v), "count": len(v)} for k, v in self.stats.items()} + + +def neftune_post_forward_hook(module, input, output): + """ + Implements the NEFTune forward pass for the model using forward hooks. Note this works only for + torch.nn.Embedding layers. This method is slightly adapted from the original source code + that can be found here: https://github.com/neelsjain/NEFTune + + Simply add it to your model as follows: + ```python + model = ... + model.embed_tokens.neftune_noise_alpha = 0.1 + model.embed_tokens.register_forward_hook(neftune_post_forward_hook) + ``` + + Args: + module (`torch.nn.Module`): + The embedding module where the hook is attached. Note that you need to set + `module.neftune_noise_alpha` to the desired noise alpha value. + input (`torch.Tensor`): + The input tensor to the model. + output (`torch.Tensor`): + The output tensor of the model (i.e. the embeddings). + """ + if module.training: + dims = torch.tensor(output.size(1) * output.size(2)) + mag_norm = module.neftune_noise_alpha / torch.sqrt(dims) + output = output + torch.zeros_like(output).uniform_(-mag_norm, mag_norm) + return output + + +def peft_module_casting_to_bf16(model): + from peft.tuners.tuners_utils import BaseTunerLayer + + for name, module in model.named_modules(): + if isinstance(module, BaseTunerLayer): + module = module.to(torch.bfloat16) + elif isinstance(module, torch.nn.LayerNorm) or "norm" in name: + module = module.to(torch.float32) + elif any(x in name for x in ["lm_head", "embed_tokens", "wte", "wpe"]): + if hasattr(module, "weight"): + if module.weight.dtype == torch.float32: + module = module.to(torch.bfloat16) + + +def trl_sanitze_kwargs_for_tagging(model, tag_names, kwargs=None): + if is_unsloth_available(): + # Unsloth adds a new attribute in the model config `unsloth_version` + # to keep track of models that have been patched with unsloth. + if hasattr(model, "config") and getattr(model.config, "unsloth_version", None) is not None: + tag_names.append("unsloth") + + if kwargs is not None: + if "tags" not in kwargs: + kwargs["tags"] = tag_names + elif "tags" in kwargs and isinstance(kwargs["tags"], list): + kwargs["tags"].extend(tag_names) + elif "tags" in kwargs and isinstance(kwargs["tags"], str): + tag_names.append(kwargs["tags"]) + kwargs["tags"] = tag_names + return kwargs + + +def get_quantization_config(model_config: ModelConfig) -> Optional[BitsAndBytesConfig]: + if model_config.load_in_4bit: + quantization_config = BitsAndBytesConfig( + load_in_4bit=True, + bnb_4bit_compute_dtype=model_config.torch_dtype, # For consistency with model weights, we use the same value as `torch_dtype` + bnb_4bit_quant_type=model_config.bnb_4bit_quant_type, + bnb_4bit_use_double_quant=model_config.use_bnb_nested_quant, + ) + elif model_config.load_in_8bit: + quantization_config = BitsAndBytesConfig( + load_in_8bit=True, + ) + else: + quantization_config = None + + return quantization_config + + +def get_kbit_device_map() -> Optional[Dict[str, int]]: + if is_xpu_available(): + return {"": f"xpu:{PartialState().local_process_index}"} + elif torch.cuda.is_available(): + return {"": PartialState().local_process_index} + else: + return None + + +def get_peft_config(model_config: ModelConfig) -> "Optional[PeftConfig]": + if model_config.use_peft is False: + return None + + peft_config = LoraConfig( + r=model_config.lora_r, + lora_alpha=model_config.lora_alpha, + lora_dropout=model_config.lora_dropout, + bias="none", + task_type="CAUSAL_LM", + target_modules=model_config.lora_target_modules, + modules_to_save=model_config.lora_modules_to_save, + ) + + return peft_config