في منشورنا السابق، استكشاف الواجهات الخلفية للتكميم في الناشرين، تعمقنا في كيفية قيام تقنيات التكميم المختلفة بتقليص نماذج الانتشار مثل FLUX.1-dev، مما يجعلها أكثر سهولة في الوصول إليها بشكل ملحوظ الاستدلال دون المساس بشكل كبير بالأداء. لقد رأينا كيف bitsandbytes, torchaoوغيرها تقلل من آثار الذاكرة لتوليد الصور.
يعد إجراء الاستدلال أمرًا رائعًا، ولكن لجعل هذه النماذج خاصة بنا، نحتاج أيضًا إلى أن نكون قادرين على ضبطها بشكل دقيق. لذلك، في هذه المقالة، نتناول فعال الكون المثالى من هذه الطرز ذات ذروة استخدام الذاكرة أقل من 10 جيجابايت من VRAM على وحدة معالجة رسومات واحدة. سوف يرشدك هذا المنشور خلال الضبط الدقيق لـ FLUX.1-dev باستخدام QLoRA مع diffusers مكتبة. سنعرض النتائج من NVIDIA RTX 4090. وسنسلط الضوء أيضًا على كيفية تدريب FP8 مع torchao يمكنه تحسين السرعة على الأجهزة المتوافقة.
جدول المحتويات
مجموعة البيانات
ونحن نهدف إلى صقل black-forest-labs/FLUX.1-dev لاعتماد الأسلوب الفني لألفونس موتشا، باستخدام مجموعة بيانات صغيرة.
هندسة الفلوكس
يتكون النموذج من ثلاثة مكونات رئيسية:
- برامج ترميز النص (CLIP وT5)
- المحول (النموذج الرئيسي – محول التدفق)
- التشفير التلقائي المتغير (VAE)
في نهج QLoRA الخاص بنا، نركز حصريًا على الضبط الدقيق لـ مكون المحول. تظل برامج تشفير النص وVAE مجمدة طوال فترة التدريب.
QLoRA ضبط FLUX.1-dev مع الناشرين
استخدمنا أ diffusers نص تدريبي (تم تعديله قليلاً من هنا مصمم لضبط LoRA على طراز DreamBooth لنماذج FLUX. كما تتوفر هنا نسخة مختصرة لإعادة إنتاج النتائج في منشور المدونة هذا (والمستخدمة في Google Colab). دعنا نتفحص الأجزاء المهمة لـ QLoRA وكفاءة الذاكرة:
تقنيات التحسين الرئيسية
LoRA (التكيف ذو الرتبة المنخفضة) الغوص العميق: تجعل LoRA تدريب النماذج أكثر كفاءة من خلال تتبع تحديثات الوزن باستخدام مصفوفات منخفضة الرتبة. بدلاً من تحديث مصفوفة الوزن الكامل تتعلم LoRA مصفوفتين أصغر و . التحديث على الأوزان للنموذج هو ، أين و . الرقم (مُسَمًّى رتبة) أصغر بكثير من الأبعاد الأصلية، مما يعني وجود عدد أقل من المعلمات المطلوب تحديثها. أخيرا، هو عامل تحجيم لتنشيط LoRA. يؤثر هذا على مدى تأثير LoRA على التحديثات، وغالبًا ما يتم تعيينه على نفس قيمة ملف أو متعددة منه. فهو يساعد على تحقيق التوازن بين تأثير النموذج المدرّب مسبقًا ومحول LoRA. للحصول على مقدمة عامة عن هذا المفهوم، راجع منشور مدونتنا السابق: استخدام LoRA للضبط الدقيق للانتشار المستقر الفعال.
QLoRA: قوة الكفاءة: يعمل QLoRA على تحسين LoRA عن طريق تحميل النموذج الأساسي المُدرب مسبقًا بتنسيق كمي (عادةً 4 بت عبر bitsandbytes) ، مما أدى إلى خفض مساحة ذاكرة النموذج الأساسي بشكل كبير. ثم يقوم بتدريب محولات LoRA (عادةً في FP16/BF16) فوق هذه القاعدة الكمية. يؤدي هذا إلى تقليل VRAM اللازمة للاحتفاظ بالنموذج الأساسي بشكل كبير.
على سبيل المثال، في البرنامج النصي التدريبي لـ DreamBooth الخاص بـ HiDream، يقلل تكميم 4 بت باستخدام وحدات البت والبايت من الحد الأقصى لاستخدام الذاكرة لضبط LoRA الدقيق من 60 جيجابايت تقريبًا إلى 37 جيجابايت تقريبًا مع تدهور جودة لا يُذكر إلى لا شيء. نفس المبدأ هو ما نطبقه هنا لضبط FLUX.1 على الأجهزة المخصصة للمستهلكين.
محسن 8 بت (AdamW):
يحتفظ مُحسِّن AdamW القياسي بتقديرات اللحظة الأولى والثانية لكل معلمة في 32 بت (FP32)، مما يستهلك قدرًا كبيرًا من الذاكرة. يستخدم AdamW ذو 8 بتات التكميم الكتلي لتخزين حالات المُحسِّن بدقة 8 بت، مع الحفاظ على استقرار التدريب. يمكن أن تقلل هذه التقنية من استخدام ذاكرة المُحسِّن بنسبة 75% تقريبًا مقارنةً بـ FP32 AdamW القياسي. يعد تمكينه في البرنامج النصي أمرًا مباشرًا:
if args.use_8bit_adam:
optimizer_class = bnb.optim.AdamW8bit
else:
optimizer_class = torch.optim.AdamW
optimizer = optimizer_class(
params_to_optimize,
betas=(args.adam_beta1, args.adam_beta2),
weight_decay=args.adam_weight_decay,
eps=args.adam_epsilon,
)
نقطة تفتيش التدرج:
أثناء التمرير للأمام، عادةً ما يتم تخزين عمليات التنشيط المتوسطة لحساب تدرج التمرير للخلف. تعمل نقاط التفتيش المتدرجة على تداول الحساب للذاكرة عن طريق تخزين بعض العناصر فقط تفعيل نقاط التفتيش وإعادة حساب الآخرين أثناء الانتشار العكسي.
if args.gradient_checkpointing:
transformer.enable_gradient_checkpointing()
ذاكرة التخزين المؤقت الكامنة:
تقوم تقنية التحسين هذه بمعالجة جميع صور التدريب مسبقًا من خلال جهاز تشفير VAE قبل بداية التدريب. يقوم بتخزين التمثيلات الكامنة الناتجة في الذاكرة. أثناء التدريب، بدلاً من تشفير الصور بشكل سريع، يتم استخدام الملفات الكامنة المخزنة مؤقتًا مباشرةً. يقدم هذا النهج فائدتين رئيسيتين:
- يلغي حسابات ترميز VAE الزائدة أثناء التدريب، مما يؤدي إلى تسريع كل خطوة تدريب
- يسمح بإزالة VAE بالكامل من ذاكرة وحدة معالجة الرسومات بعد التخزين المؤقت. تتمثل المقايضة في زيادة استخدام ذاكرة الوصول العشوائي (RAM) لتخزين جميع الملفات الكامنة المخزنة مؤقتًا، ولكن يمكن التحكم في ذلك عادةً لمجموعات البيانات الصغيرة.
if args.cache_latents:
latents_cache = []
for batch in tqdm(train_dataloader, desc="Caching latents"):
with torch.no_grad():
batch["pixel_values"] = batch["pixel_values"].to(
accelerator.device, non_blocking=True, dtype=weight_dtype
)
latents_cache.append(vae.encode(batch["pixel_values"]).latent_dist)
del vae
free_memory()
إعداد التكميم 4 بت (BitsAndBytesConfig):
يوضح هذا القسم تكوين QLoRA للنموذج الأساسي:
bnb_4bit_compute_dtype = torch.float32
if args.mixed_precision == "fp16":
bnb_4bit_compute_dtype = torch.float16
elif args.mixed_precision == "bf16":
bnb_4bit_compute_dtype = torch.bfloat16
nf4_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=bnb_4bit_compute_dtype,
)
transformer = FluxTransformer2DModel.from_pretrained(
args.pretrained_model_name_or_path,
subfolder="transformer",
quantization_config=nf4_config,
torch_dtype=bnb_4bit_compute_dtype,
)
transformer = prepare_model_for_kbit_training(transformer, use_gradient_checkpointing=False)
تعريف تكوين LoRA (LoraConfig):
تتم إضافة المحولات إلى المحول الكمي:
transformer_lora_config = LoraConfig(
r=args.rank,
lora_alpha=args.rank,
init_lora_weights="gaussian",
target_modules=["to_k", "to_q", "to_v", "to_out.0"],
)
transformer.add_adapter(transformer_lora_config)
print(f"trainable params: transformer.num_parameters(only_trainable=True) || all params: transformer.num_parameters()")
تصبح معلمات LoRA هذه فقط قابلة للتدريب.
تضمينات النص قبل الحوسبة (CLIP/T5)
قبل أن نطلق عملية الضبط الدقيق لـ QLoRA، يمكننا توفير جزء كبير من VRAM ووقت ساعة الحائط عن طريق التخزين المؤقت لمخرجات برامج تشفير النص مرة واحدة.
في وقت التدريب، يقوم برنامج تحميل البيانات ببساطة بقراءة التضمينات المخزنة مؤقتًا بدلاً من إعادة تشفير التسمية التوضيحية، لذلك لا يضطر برنامج التشفير CLIP/T5 أبدًا إلى الجلوس في ذاكرة وحدة معالجة الرسومات.
شفرة
import argparse
import pandas as pd
import torch
from datasets import load_dataset
from huggingface_hub.utils import insecure_hashlib
from tqdm.auto import tqdm
from transformers import T5EncoderModel
from diffusers import FluxPipeline
MAX_SEQ_LENGTH = 77
OUTPUT_PATH = "embeddings.parquet"
def generate_image_hash(image):
return insecure_hashlib.sha256(image.tobytes()).hexdigest()
def load_flux_dev_pipeline():
id = "black-forest-labs/FLUX.1-dev"
text_encoder = T5EncoderModel.from_pretrained(id, subfolder="text_encoder_2", load_in_8bit=True, device_map="auto")
pipeline = FluxPipeline.from_pretrained(
id, text_encoder_2=text_encoder, transformer=None, vae=None, device_map="balanced"
)
return pipeline
@torch.no_grad()
def compute_embeddings(pipeline, prompts, max_sequence_length):
all_prompt_embeds = []
all_pooled_prompt_embeds = []
all_text_ids = []
for prompt in tqdm(prompts, desc="Encoding prompts."):
(
prompt_embeds,
pooled_prompt_embeds,
text_ids,
) = pipeline.encode_prompt(prompt=prompt, prompt_2=None, max_sequence_length=max_sequence_length)
all_prompt_embeds.append(prompt_embeds)
all_pooled_prompt_embeds.append(pooled_prompt_embeds)
all_text_ids.append(text_ids)
max_memory = torch.cuda.max_memory_allocated() / 1024 / 1024 / 1024
print(f"Max memory allocated: max_memory:.3f GB")
return all_prompt_embeds, all_pooled_prompt_embeds, all_text_ids
def run(args):
dataset = load_dataset("Norod78/Yarn-art-style", split="train")
image_prompts = generate_image_hash(sample["image"]): sample["text"] for sample in dataset
all_prompts = list(image_prompts.values())
print(f"len(all_prompts)=")
pipeline = load_flux_dev_pipeline()
all_prompt_embeds, all_pooled_prompt_embeds, all_text_ids = compute_embeddings(
pipeline, all_prompts, args.max_sequence_length
)
data = []
for i, (image_hash, _) in enumerate(image_prompts.items()):
data.append((image_hash, all_prompt_embeds[i], all_pooled_prompt_embeds[i], all_text_ids[i]))
print(f"len(data)=")
embedding_cols = ["prompt_embeds", "pooled_prompt_embeds", "text_ids"]
df = pd.DataFrame(data, columns=["image_hash"] + embedding_cols)
print(f"len(df)=")
for col in embedding_cols:
df[col] = df[col].apply(lambda x: x.cpu().numpy().flatten().tolist())
df.to_parquet(args.output_path)
print(f"Data successfully serialized to args.output_path")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"--max_sequence_length",
type=int,
default=MAX_SEQ_LENGTH,
help="Maximum sequence length to use for computing the embeddings. The more the higher computational costs.",
)
parser.add_argument("--output_path", type=str, default=OUTPUT_PATH, help="Path to serialize the parquet file.")
args = parser.parse_args()
run(args)
كيفية استخدامه
python compute_embeddings.py \
--max_sequence_length 77 \
--output_path embeddings_alphonse_mucha.parquet
من خلال دمج هذا مع العناصر الكامنة لـ VAE المخزنة مؤقتًا (--cache_latents) يمكنك تقليص النموذج النشط إلى المحول الكمي فقط + محولات LoRA، مع الحفاظ على الضبط الدقيق بالكامل بشكل مريح تحت 10 جيجابايت من ذاكرة وحدة معالجة الرسومات.
الإعداد والنتائج
في هذا العرض التوضيحي، استخدمنا NVIDIA RTX 4090 (ذاكرة VRAM سعة 24 جيجابايت) لاستكشاف أدائها. أمر التدريب الكامل باستخدام accelerate هو مبين أدناه.
accelerate launch --config_file=accelerate.yaml \
train_dreambooth_lora_flux_miniature.py \
--pretrained_model_name_or_path="black-forest-labs/FLUX.1-dev" \
--data_df_path="embeddings_alphonse_mucha.parquet" \
--output_dir="alphonse_mucha_lora_flux_nf4" \
--mixed_precision="bf16" \
--use_8bit_adam \
--weighting_scheme="none" \
--width=512 \
--height=768 \
--train_batch_size=1 \
--repeats=1 \
--learning_rate=1e-4 \
--guidance_scale=1 \
--report_to="wandb" \
--gradient_accumulation_steps=4 \
--gradient_checkpointing \
--lr_scheduler="constant" \
--lr_warmup_steps=0 \
--cache_latents \
--rank=4 \
--max_train_steps=700 \
--seed="0"
التكوين لـ RTX 4090:
في بطاقة RTX 4090 لدينا، استخدمنا ملف train_batch_size من 1، gradient_accumulation_steps من 4، mixed_precision="bf16", gradient_checkpointing=True, use_8bit_adam=Trueلورا rank 4، ودقة 512×768. تم تخزين العناصر الكامنة مؤقتًا باستخدام cache_latents=True.
بصمة الذاكرة (RTX 4090):
- كلورا: بلغ الحد الأقصى لاستخدام VRAM لضبط QLoRA حوالي 9 جيجابايت.
- BF16 لورا: تشغيل LoRA القياسي (مع قاعدة FLUX.1-dev في FP16) على نفس الإعداد يستهلك 26 جيجابايت من VRAM.
- BF16 الضبط الكامل: سيكون التقدير حوالي 120 جيجابايت من VRAM بدون تحسينات في الذاكرة.
وقت التدريب (RTX 4090):
استغرق الضبط الدقيق لـ 700 خطوة على مجموعة بيانات Alphonse Mucha حوالي 41 دقيقة على RTX 4090 مع train_batch_size 1 ودقة 512×768.
جودة الإخراج:
المقياس النهائي هو الفن المتولد. فيما يلي نماذج من نموذج QLoRA المضبوط بدقة على مجموعة بيانات نمط derekl35/alphonse-mucha:
يقارن هذا الجدول الابتدائي bf16 نتائج دقيقة. كان الهدف من الضبط الدقيق هو تعليم النموذج الأسلوب المميز لألفونس موتشا.
استحوذ الطراز المضبوط بدقة على أسلوب فن الآرت نوفو الأيقوني لموتشا، والذي يظهر بوضوح في الزخارف الزخرفية ولوحة الألوان المميزة. حافظت عملية QLoRA على دقة ممتازة أثناء تعلم النمط الجديد.
انقر لرؤية مقارنة fp16
النتائج متطابقة تقريبًا، مما يدل على أن QLoRA يعمل بشكل فعال مع كليهما fp16 و bf16 دقة مختلطة.
مقارنة النموذج: القاعدة مقابل QLoRA المضبوطة بدقة (fp16)
ضبط FP8 باستخدام TorchAO
بالنسبة للمستخدمين الذين لديهم وحدات معالجة الرسومات NVIDIA التي تمتلك قدرة حسابية 8.9 أو أكبر (مثل H100 وRTX 4090)، يمكن تحقيق كفاءة أكبر في السرعة من خلال الاستفادة من تدريب FP8 عبر torchao مكتبة.
لقد قمنا بضبط FLUX.1-dev LoRA على وحدة معالجة الرسومات H100 SXM المعدلة قليلاًdiffusers-torchao مخطوطات التدريب. تم استخدام الأمر التالي:
accelerate launch train_dreambooth_lora_flux.py \
--pretrained_model_name_or_path=black-forest-labs/FLUX.1-dev \
--dataset_name=derekl35/alphonse-mucha-style --instance_prompt="a woman, alphonse mucha style" --caption_column="text" \
--output_dir=alphonse_mucha_fp8_lora_flux \
--mixed_precision=bf16 --use_8bit_adam \
--weighting_scheme=none \
--height=768 --width=512 --train_batch_size=1 --repeats=1 \
--learning_rate=1e-4 --guidance_scale=1 --report_to=wandb \
--gradient_accumulation_steps=1 --gradient_checkpointing \
--lr_scheduler=constant --lr_warmup_steps=0 --rank=4 \
--max_train_steps=700 --checkpointing_steps=600 --seed=0 \
--do_fp8_training --push_to_hub
وكان التدريب على ذروة استخدام الذاكرة 36.57 جيجابايت واكتمل في حوالي 20 دقيقة.
تتوفر أيضًا نتائج نوعية من طراز FP8 المضبوط بدقة:
الخطوات الأساسية لتمكين التدريب على FP8 torchao يتضمن:
- حقن طبقات FP8 في النموذج باستخدام
convert_to_float8_trainingمنtorchao.float8. - تعريف أ
module_filter_fnلتحديد الوحدات التي ينبغي أو لا ينبغي تحويلها إلى FP8.
للحصول على دليل أكثر تفصيلاً ومقتطفات التعليمات البرمجية، يرجى الرجوع إلى هذا الجوهر و diffusers-torchao مستودع.
الاستدلال باستخدام محولات LoRA المدربة
بعد تدريب محولات LoRA، لديك طريقتان رئيسيتان للاستدلال.
الخيار 1: تحميل محولات LoRA
تتمثل إحدى الطرق في تحميل محولات LoRA المدربة أعلى النموذج الأساسي.
فوائد تحميل LoRA:
- المرونة: يمكنك التبديل بسهولة بين محولات LoRA المختلفة دون إعادة تحميل النموذج الأساسي
- التجريب: اختبر أنماطًا أو مفاهيم فنية متعددة عن طريق تبديل المحولات
- نمطية: الجمع بين محولات LoRA المتعددة باستخدام
set_adapters()للمزج الإبداعي - كفاءة التخزين: احتفظ بنموذج أساسي واحد وملفات محولات صغيرة متعددة
شفرة
from diffusers import FluxPipeline, FluxTransformer2DModel, BitsAndBytesConfig
import torch
ckpt_id = "black-forest-labs/FLUX.1-dev"
pipeline = FluxPipeline.from_pretrained(
ckpt_id, torch_dtype=torch.float16
)
pipeline.load_lora_weights("derekl35/alphonse_mucha_qlora_flux", weight_name="pytorch_lora_weights.safetensors")
pipeline.enable_model_cpu_offload()
image = pipeline(
"a puppy in a pond, alphonse mucha style", num_inference_steps=28, guidance_scale=3.5, height=768, width=512, generator=torch.manual_seed(0)
).images[0]
image.save("alphonse_mucha.png")
الخيار 2: دمج LoRA في النموذج الأساسي
لأنه عندما تريد أقصى قدر من الكفاءة بنمط واحد، يمكنك دمج أوزان LoRA في النموذج الأساسي.
فوائد دمج LoRA:
- كفاءة VRAM: لا يوجد حمل إضافي للذاكرة من أوزان المحول أثناء الاستدلال
- سرعة: استنتاج أسرع قليلاً حيث لا توجد حاجة لتطبيق حسابات المحول
- التوافق الكمي: يمكن إعادة تحديد حجم النموذج المدمج لتحقيق أقصى قدر من كفاءة الذاكرة
شفرة
from diffusers import FluxPipeline, AutoPipelineForText2Image, FluxTransformer2DModel, BitsAndBytesConfig
import torch
ckpt_id = "black-forest-labs/FLUX.1-dev"
pipeline = FluxPipeline.from_pretrained(
ckpt_id, text_encoder=None, text_encoder_2=None, torch_dtype=torch.float16
)
pipeline.load_lora_weights("derekl35/alphonse_mucha_qlora_flux", weight_name="pytorch_lora_weights.safetensors")
pipeline.fuse_lora()
pipeline.unload_lora_weights()
pipeline.transformer.save_pretrained("fused_transformer")
bnb_4bit_compute_dtype = torch.bfloat16
nf4_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=bnb_4bit_compute_dtype,
)
transformer = FluxTransformer2DModel.from_pretrained(
"fused_transformer",
quantization_config=nf4_config,
torch_dtype=bnb_4bit_compute_dtype,
)
pipeline = AutoPipelineForText2Image.from_pretrained(
ckpt_id, transformer=transformer, torch_dtype=bnb_4bit_compute_dtype
)
pipeline.enable_model_cpu_offload()
image = pipeline(
"a puppy in a pond, alphonse mucha style", num_inference_steps=28, guidance_scale=3.5, height=768, width=512, generator=torch.manual_seed(0)
).images[0]
image.save("alphonse_mucha_merged.png")
يعمل على جوجل كولاب
بينما عرضنا النتائج على RTX 4090، يمكن تشغيل نفس الكود على أجهزة يسهل الوصول إليها مثل وحدة معالجة الرسومات T4 المتوفرة في Google Colab مجانًا.
في T4، يمكنك أن تتوقع أن تستغرق عملية الضبط الدقيق وقتًا أطول بكثير حوالي 4 ساعات لنفس عدد الخطوات. هذه مقايضة لإمكانية الوصول، ولكنها تجعل الضبط الدقيق ممكنًا بدون أجهزة متطورة. كن على دراية بحدود الاستخدام إذا كنت تستخدم Colab، حيث أن التدريب لمدة 4 ساعات قد يدفعك إلى ذلك.
خاتمة
QLoRA، إلى جانب diffusers تعمل المكتبة على إضفاء الطابع الديمقراطي بشكل كبير على القدرة على تخصيص أحدث النماذج مثل FLUX.1-dev. كما هو موضح في RTX 4090، فإن الضبط الدقيق الفعال في متناول اليد، مما يؤدي إلى تعديلات أسلوبية عالية الجودة. علاوة على ذلك، بالنسبة للمستخدمين الذين لديهم أحدث أجهزة NVIDIA، torchao يتيح تدريبًا أسرع من خلال دقة FP8.
مشاركة إبداعاتك على المحور!
تعد مشاركة محولات LoRA المضبوطة طريقة رائعة للمساهمة في مجتمع المصادر المفتوحة. فهو يتيح للآخرين تجربة أنماطك بسهولة، والبناء على عملك، ويساعد في إنشاء نظام بيئي نابض بالحياة لأدوات الذكاء الاصطناعي الإبداعية.
إذا قمت بتدريب LoRA على FLUX.1-dev، فنحن نشجعك على مشاركته. أسهل طريقة هي إضافة علامة –push_to_hub إلى البرنامج النصي للتدريب. وبدلاً من ذلك، إذا كنت قد قمت بالفعل بتدريب نموذج وتريد تحميله، فيمكنك استخدام المقتطف التالي.
from huggingface_hub import create_repo, upload_folder
repo_id = "your-username/alphonse_mucha_qlora_flux"
create_repo(repo_id, exist_ok=True)
upload_folder(
repo_id=repo_id,
folder_path="alphonse_mucha_qlora_flux",
commit_message="Add Alphonse Mucha LoRA adapter"
)
تحقق من Mucha LoRA وTorchAO FP8 LoRA. يمكنك العثور على كليهما، بالإضافة إلى محولات أخرى، في هذه المجموعة.
لا يمكننا الانتظار لرؤية ما تقوم بإنشائه!