File size: 9,160 Bytes
efe877a
 
 
c1594b5
e213b02
 
 
 
 
 
efe877a
691e1ae
a4eeccc
efbfba5
 
a4eeccc
691e1ae
a4eeccc
 
efe877a
691e1ae
 
a4eeccc
efe877a
 
 
a4eeccc
 
 
691e1ae
 
 
 
 
a4eeccc
691e1ae
a4eeccc
 
 
 
691e1ae
a4eeccc
efe877a
 
 
691e1ae
 
 
 
a4eeccc
efe877a
691e1ae
 
efe877a
a4eeccc
691e1ae
 
efe877a
691e1ae
35ee7cf
691e1ae
efbfba5
691e1ae
 
 
 
 
 
 
a4eeccc
efe877a
691e1ae
 
 
 
 
 
 
efe877a
691e1ae
 
efe877a
691e1ae
a4eeccc
efe877a
691e1ae
efe877a
 
691e1ae
a4eeccc
efe877a
 
 
691e1ae
 
 
efe877a
 
a4eeccc
691e1ae
 
efe877a
a4eeccc
691e1ae
 
efe877a
691e1ae
 
efe877a
691e1ae
 
 
 
 
 
 
 
 
 
efe877a
691e1ae
a4eeccc
 
efe877a
a4eeccc
 
 
 
691e1ae
 
 
 
efe877a
a4eeccc
 
efe877a
 
 
691e1ae
a4eeccc
691e1ae
efe877a
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# ==============================================================================
# 1. DEPENDANCES & COMPATIBILITE COLAB / ZERO-GPU
# ==============================================================================
#!pip install -q diffusers transformers accelerate datasets gradio torch torchvision huggingface_hub
try:
    import spaces
except ImportError:
    class MockSpaces:
        def GPU(self, fn=None, duration=None): return fn if fn is not None else lambda f: f
    spaces = MockSpaces()

import os, json, torch, torch.nn as nn, numpy as np, gradio as ui
from PIL import Image
from datasets import load_dataset
from torchvision import transforms
from transformers import CLIPTextModel, CLIPTokenizer
from diffusers import UNet2DConditionModel, AutoencoderKL, DDIMScheduler
from huggingface_hub import HfApi


torch.backends.cuda.matmul.allow_tf32 = True
LOU_UNET, LOU_VAE, LOU_TOKENIZER, LOU_TEXT_ENCODER, LOU_SCHEDULER, LOU_IMAGE_SIZE = None, None, None, None, None, 64

# ==============================================================================
# 2. INITIALISATION ARCHITECTURE LOU DE ZERO
# ==============================================================================
def init_lou_architecture(image_size):
    tokenizer = CLIPTokenizer.from_pretrained("openai/clip-vit-large-patch14")
    text_encoder = CLIPTextModel.from_pretrained("openai/clip-vit-large-patch14")
    vae = AutoencoderKL(
        in_channels=3, out_channels=3, latent_channels=4,
        down_block_types=("DownEncoderBlock2D", "DownEncoderBlock2D"),
        up_block_types=("UpDecoderBlock2D", "UpDecoderBlock2D"), block_out_channels=(64, 128)
    )
    unet = UNet2DConditionModel(
        sample_size=image_size // 8, in_channels=4, out_channels=4, layers_per_block=1,
        block_out_channels=(64, 128), down_block_types=("DownBlock2D", "CrossAttnDownBlock2D"),
        up_block_types=("CrossAttnUpBlock2D", "UpBlock2D"), cross_attention_dim=768
    )
    noise_scheduler = DDIMScheduler(num_train_timesteps=1000, beta_start=0.00085, beta_end=0.012, beta_schedule="scaled_linear")
    return tokenizer, text_encoder, unet, vae, noise_scheduler

# ==============================================================================
# 3. TRAIN ET EXPORT REPO STRUCTURÉ (VAE + UNET COMPLET)
# ==============================================================================
@spaces.GPU
def train_lou_model(repo, token, dataset_path, lr, epochs, size, progress=ui.Progress()):
    global LOU_UNET, LOU_VAE, LOU_TOKENIZER, LOU_TEXT_ENCODER, LOU_SCHEDULER, LOU_IMAGE_SIZE
    LOU_IMAGE_SIZE = int(size)
    device = "cuda" if torch.cuda.is_available() else "cpu"

    LOU_TOKENIZER, LOU_TEXT_ENCODER, LOU_UNET, LOU_VAE, LOU_SCHEDULER = init_lou_architecture(LOU_IMAGE_SIZE)
    LOU_UNET.to(device); LOU_TEXT_ENCODER.to(device); LOU_VAE.to(device)

    dataset = load_dataset(dataset_path, split="train")
    tf = transforms.Compose([transforms.Resize((LOU_IMAGE_SIZE, LOU_IMAGE_SIZE)), transforms.ToTensor(), transforms.Normalize([0.5], [0.5])])
    optimizer = torch.optim.AdamW(list(LOU_UNET.parameters()) + list(LOU_VAE.parameters()), lr=float(lr))

    LOU_UNET.train(); LOU_VAE.train()
    for epoch in range(int(epochs)):
        for batch in progress.tqdm(dataset, desc=f"Epoch {epoch + 1}/{epochs}"):
            optimizer.zero_grad()
            clean = tf(batch["image"].convert("RGB")).unsqueeze(0).to(device)
            latents = LOU_VAE.encode(clean).latent_dist.sample() * 0.18215
            noise = torch.randn_like(latents)
            t = torch.randint(0, 1000, (1,), device=device).long()
            noisy = LOU_SCHEDULER.add_noise(latents, noise, t)
            txt = LOU_TOKENIZER(batch["text"], padding="max_length", truncation=True, return_tensors="pt").input_ids.to(device)
            loss = nn.functional.mse_loss(LOU_UNET(noisy, t, LOU_TEXT_ENCODER(txt).last_hidden_state).sample, noise)
            loss.backward(); optimizer.step()

    base_dir = "./lou_model_repo"
    os.makedirs(base_dir, exist_ok=True)
    LOU_UNET.save_pretrained(os.path.join(base_dir, "unet"))
    LOU_VAE.save_pretrained(os.path.join(base_dir, "vae"))
    LOU_TEXT_ENCODER.save_pretrained(os.path.join(base_dir, "text_encoder"))
    LOU_TOKENIZER.save_pretrained(os.path.join(base_dir, "tokenizer"))
    LOU_SCHEDULER.save_pretrained(os.path.join(base_dir, "scheduler"))

    with open(os.path.join(base_dir, "model_index.json"), "w") as f:
        json.dump({"_class_name": "LouDiffusionArchitecture", "_diffusers_version": "0.21.4", "scheduler": ["diffusers", "DDIMScheduler"], "text_encoder": ["transformers", "CLIPTextModel"], "tokenizer": ["transformers", "CLIPTokenizer"], "unet": ["diffusers", "UNet2DConditionModel"], "vae": ["diffusers", "AutoencoderKL"]}, f, indent=2)

    if token and repo:
        try:
            api = HfApi(); api.create_repo(repo_id=repo, token=token, exist_ok=True)
            api.upload_folder(folder_path=base_dir, repo_id=repo, repo_type="model", token=token)
            return f"✅ Entraîné et synchronisé sur HF via hfapi."
        except Exception as e: return f"⚠️ Structure locale ok, erreur hfapi : {str(e)}"
    return f"✅ Entraîné ! Structure locale créée dans : {base_dir}"

# ==============================================================================
# 4. INFERENCE EN ESPACE LATENT (VITESSE MAXIMALE FP16)
# ==============================================================================
@spaces.GPU
def generate_lou_images(prompt, steps, num_images, seed, cfg, auto):
    global LOU_UNET, LOU_VAE, LOU_TOKENIZER, LOU_TEXT_ENCODER, LOU_SCHEDULER, LOU_IMAGE_SIZE
    if LOU_UNET is None: return [Image.new("RGB", (64, 64), color="gray")], "❌ Lancez d'abord l'entraînement."

    device = "cuda" if torch.cuda.is_available() else "cpu"
    LOU_UNET.to(device=device, dtype=torch.float16); LOU_TEXT_ENCODER.to(device=device, dtype=torch.float16); LOU_VAE.to(device=device, dtype=torch.float16)
    LOU_UNET.eval(); LOU_TEXT_ENCODER.eval(); LOU_VAE.eval()

    if seed >= 0: torch.manual_seed(int(seed)); np.random.seed(int(seed))
    t_in = LOU_TOKENIZER([prompt], padding="max_length", truncation=True, return_tensors="pt").input_ids.to(device)
    u_in = LOU_TOKENIZER([""], padding="max_length", truncation=True, return_tensors="pt").input_ids.to(device)

    with torch.no_grad(), torch.autocast(device_type="cuda", dtype=torch.float16):
        context = torch.cat([LOU_TEXT_ENCODER(u_in).last_hidden_state, LOU_TEXT_ENCODER(t_in).last_hidden_state])

    generated = []
    LOU_SCHEDULER.set_timesteps(int(steps))
    with torch.no_grad(), torch.autocast(device_type="cuda", dtype=torch.float16):
        for _ in range(int(num_images)):
            latents = torch.randn((1, 4, LOU_IMAGE_SIZE // 8, LOU_IMAGE_SIZE // 8), device=device, dtype=torch.float16)
            for t in LOU_SCHEDULER.timesteps:
                p_uncond, p_text = LOU_UNET(torch.cat([latents] * 2), t, encoder_hidden_states=context).sample.chunk(2)
                latents = LOU_SCHEDULER.step(p_uncond + cfg * (p_text - p_uncond), t, latents).prev_sample
            img_tensor = (LOU_VAE.decode(latents / 0.18215).sample / 2 + 0.5).clamp(0, 1).squeeze(0)
            generated.append(Image.fromarray((img_tensor.cpu().float().permute(1, 2, 0).numpy() * 255).astype(np.uint8)))

    return generated, f"🎉 Images {LOU_IMAGE_SIZE}x{LOU_IMAGE_SIZE} générées par Lou !"

# ==============================================================================
# 5. CODE GRADIO INTERFACE MINIMALISTE COMPACTE
# ==============================================================================
with ui.Blocks(theme=ui.themes.Default()) as app:
    with ui.Tab("🏋️ Train"):
        with ui.Row():
            repo_i = ui.Textbox(label="Repo HF", placeholder="user/repo"); token_i = ui.Textbox(label="Token HF", type="password")
            ds_i = ui.Textbox(label="Dataset", value="diffusers/pokemon-gpt4-captions"); lr_i = ui.Number(label="LR", value=1e-4)
            ep_i = ui.Slider(1, 50, value=3, step=1, label="Époques"); sz_i = ui.Slider(32, 1024, value=64, step=32, label="Taille Image")
        t_btn = ui.Button("🚀 Lancer l'entraînement de Lou", variant="primary"); t_out = ui.Textbox(label="Statut", lines=1)

    with ui.Tab("🎨 Playground"):
        with ui.Row():
            pr_i = ui.Textbox(label="Prompt", value="a cute pokemon", scale=2); st_i = ui.Slider(5, 100, value=20, step=1, label="Steps")
            num_i = ui.Slider(1, 4, value=1, step=1, label="Images"); sd_i = ui.Number(label="Seed", value=-1)
            gd_i = ui.Slider(1.0, 20.0, value=7.5, step=0.5, label="Guidance"); au_i = ui.Checkbox(label="Autotraining", value=True)
        g_btn = ui.Button("🔮 Générer", variant="secondary"); out_gal = ui.Gallery(label="Outputs", height=200); g_out = ui.Textbox(label="Statut Gen", lines=1)

    t_btn.click(train_lou_model, inputs=[repo_i, token_i, ds_i, lr_i, ep_i, sz_i], outputs=t_out)
    g_btn.click(generate_lou_images, inputs=[pr_i, st_i, num_i, sd_i, gd_i, au_i], outputs=[out_gal, g_out])
app.queue(default_concurrency_limit=4)
app.launch(inline=True, share=True,debug=True)