etchisone commited on
Commit
0712d19
·
verified ·
1 Parent(s): e0d47b4

Delete text_to_image_colab.ipynb

Browse files
Files changed (1) hide show
  1. text_to_image_colab.ipynb +0 -351
text_to_image_colab.ipynb DELETED
@@ -1,351 +0,0 @@
1
- {
2
- "nbformat": 4,
3
- "nbformat_minor": 0,
4
- "metadata": {
5
- "colab": { "provenance": [], "gpuType": "T4" },
6
- "kernelspec": { "name": "python3", "display_name": "Python 3" },
7
- "language_info": { "name": "python" },
8
- "accelerator": "GPU"
9
- },
10
- "cells": [
11
- {
12
- "cell_type": "markdown",
13
- "metadata": {},
14
- "source": [
15
- "# Text-to-Image Generator + Evaluation Metrics\n",
16
- "**Dataset:** `rhli/genarena` | **Model:** `runwayml/stable-diffusion-v1-5`\n",
17
- "\n",
18
- "### Metrics covered\n",
19
- "| Metric | Analogue | Range | Better |\n",
20
- "|--------|----------|-------|--------|\n",
21
- "| CLIP Score | Recall | 0-100 | Higher |\n",
22
- "| FID | Precision | 0-inf | Lower |\n",
23
- "| Aesthetic Score | Quality | 1-10 | Higher |\n",
24
- "\n",
25
- "> Enable GPU: **Runtime → Change runtime type → T4 GPU**"
26
- ]
27
- },
28
- {
29
- "cell_type": "code",
30
- "metadata": {},
31
- "source": [
32
- "# ── 1. Install dependencies ──────────────────────────────────────────────────\n",
33
- "!pip install -q diffusers transformers accelerate datasets pillow huggingface_hub torchvision"
34
- ],
35
- "execution_count": null,
36
- "outputs": []
37
- },
38
- {
39
- "cell_type": "code",
40
- "metadata": {},
41
- "source": [
42
- "# ── 2. Imports ───────────────────────────────────────────────────────────────\n",
43
- "import torch\n",
44
- "import numpy as np\n",
45
- "import random\n",
46
- "import matplotlib.pyplot as plt\n",
47
- "from PIL import Image\n",
48
- "from diffusers import StableDiffusionPipeline, DPMSolverMultistepScheduler\n",
49
- "from datasets import load_dataset\n",
50
- "\n",
51
- "DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'\n",
52
- "DTYPE = torch.float16 if DEVICE == 'cuda' else torch.float32\n",
53
- "print(f'Device: {DEVICE}')\n",
54
- "if DEVICE == 'cuda':\n",
55
- " print(f'GPU: {torch.cuda.get_device_name(0)}')"
56
- ],
57
- "execution_count": null,
58
- "outputs": []
59
- },
60
- {
61
- "cell_type": "code",
62
- "metadata": {},
63
- "source": [
64
- "# ── 3. Load generation pipeline ──────────────────────────────────────────────\n",
65
- "pipe = StableDiffusionPipeline.from_pretrained(\n",
66
- " 'runwayml/stable-diffusion-v1-5',\n",
67
- " torch_dtype=DTYPE,\n",
68
- " safety_checker=None,\n",
69
- " requires_safety_checker=False,\n",
70
- ")\n",
71
- "pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)\n",
72
- "pipe = pipe.to(DEVICE)\n",
73
- "if DEVICE == 'cuda':\n",
74
- " pipe.enable_attention_slicing()\n",
75
- "print('Generation pipeline ready')"
76
- ],
77
- "execution_count": null,
78
- "outputs": []
79
- },
80
- {
81
- "cell_type": "code",
82
- "metadata": {},
83
- "source": [
84
- "# ── 4. Load dataset ───────────────────────────────────────────────────────────\n",
85
- "PROMPT_COLUMN = 'prompt' # adjust if different after inspecting dataset[0]\n",
86
- "dataset = load_dataset('rhli/genarena', split='train')\n",
87
- "print(f'Dataset size: {len(dataset)}')\n",
88
- "print(f'Columns : {dataset.column_names}')\n",
89
- "print('Sample:', dataset[0])"
90
- ],
91
- "execution_count": null,
92
- "outputs": []
93
- },
94
- {
95
- "cell_type": "code",
96
- "metadata": {},
97
- "source": [
98
- "# ── 5. Generation helper ─────────────────────────────────────────────────────\n",
99
- "def generate(prompt, negative_prompt='blurry, low quality',\n",
100
- " num_steps=20, guidance=7.5, seed=42, show=True):\n",
101
- " generator = torch.Generator(DEVICE).manual_seed(seed)\n",
102
- " ctx = torch.autocast(DEVICE) if DEVICE == 'cuda' else torch.no_grad()\n",
103
- " with ctx:\n",
104
- " result = pipe(\n",
105
- " prompt,\n",
106
- " negative_prompt=negative_prompt,\n",
107
- " num_inference_steps=num_steps,\n",
108
- " guidance_scale=guidance,\n",
109
- " generator=generator,\n",
110
- " height=512, width=512,\n",
111
- " )\n",
112
- " img = result.images[0]\n",
113
- " if show:\n",
114
- " plt.figure(figsize=(5, 5))\n",
115
- " plt.imshow(img)\n",
116
- " plt.axis('off')\n",
117
- " plt.title(prompt[:80], fontsize=9)\n",
118
- " plt.tight_layout()\n",
119
- " plt.show()\n",
120
- " return img\n",
121
- "\n",
122
- "# Quick test\n",
123
- "sample_prompt = dataset[0][PROMPT_COLUMN]\n",
124
- "print(f'Generating: {sample_prompt}')\n",
125
- "img = generate(sample_prompt)"
126
- ],
127
- "execution_count": null,
128
- "outputs": []
129
- },
130
- {
131
- "cell_type": "markdown",
132
- "metadata": {},
133
- "source": [
134
- "---\n",
135
- "## Evaluation Metrics\n",
136
- "\n",
137
- "Standard precision/recall require ground-truth labels, which generated images don't have.\n",
138
- "The field uses these proxies instead:\n",
139
- "\n",
140
- "- **CLIP Score** → *Recall* proxy: did the image capture the prompt?\n",
141
- "- **FID** → *Precision* proxy: are the images realistic vs. the dataset distribution?\n",
142
- "- **Aesthetic Score** → visual quality predictor"
143
- ]
144
- },
145
- {
146
- "cell_type": "code",
147
- "metadata": {},
148
- "source": [
149
- "# ── 6. CLIP Score (Recall analogue) ──────────────────────────────────────────\n",
150
- "from transformers import CLIPModel, CLIPProcessor\n",
151
- "\n",
152
- "clip_model = CLIPModel.from_pretrained('openai/clip-vit-base-patch32').to(DEVICE)\n",
153
- "clip_processor = CLIPProcessor.from_pretrained('openai/clip-vit-base-patch32')\n",
154
- "\n",
155
- "def clip_score(image: Image.Image, prompt: str) -> float:\n",
156
- " \"\"\"\n",
157
- " CLIP Score in [0, 100]. Measures prompt-image alignment.\n",
158
- " Higher is better. Analogue of recall.\n",
159
- " \"\"\"\n",
160
- " inputs = clip_processor(text=[prompt], images=image,\n",
161
- " return_tensors='pt', padding=True).to(DEVICE)\n",
162
- " with torch.no_grad():\n",
163
- " out = clip_model(**inputs)\n",
164
- " img_emb = out.image_embeds / out.image_embeds.norm(dim=-1, keepdim=True)\n",
165
- " txt_emb = out.text_embeds / out.text_embeds.norm(dim=-1, keepdim=True)\n",
166
- " sim = (img_emb * txt_emb).sum().item()\n",
167
- " return round(float(np.clip(sim * 100, 0, 100)), 2)\n",
168
- "\n",
169
- "# Example\n",
170
- "score = clip_score(img, sample_prompt)\n",
171
- "print(f'CLIP Score: {score}/100 ({\"Good\" if score >= 25 else \"Moderate\" if score >= 15 else \"Low\"})')"
172
- ],
173
- "execution_count": null,
174
- "outputs": []
175
- },
176
- {
177
- "cell_type": "code",
178
- "metadata": {},
179
- "source": [
180
- "# ── 7. Aesthetic Score (Visual quality) ───────────────────────────────────────\n",
181
- "import torch.nn as nn\n",
182
- "from huggingface_hub import hf_hub_download\n",
183
- "from transformers import CLIPModel as CLIPModelL, CLIPProcessor as CLIPProcessorL\n",
184
- "\n",
185
- "class AestheticPredictor(nn.Module):\n",
186
- " def __init__(self, input_size=768):\n",
187
- " super().__init__()\n",
188
- " self.layers = nn.Sequential(\n",
189
- " nn.Linear(input_size, 1024), nn.Dropout(0.2),\n",
190
- " nn.Linear(1024, 128), nn.Dropout(0.2),\n",
191
- " nn.Linear(128, 64), nn.Dropout(0.1),\n",
192
- " nn.Linear(64, 16),\n",
193
- " nn.Linear(16, 1),\n",
194
- " )\n",
195
- " def forward(self, x):\n",
196
- " return self.layers(x)\n",
197
- "\n",
198
- "# Load CLIP ViT-L/14 (larger, used by aesthetic predictor)\n",
199
- "print('Loading CLIP ViT-L/14 + aesthetic predictor...')\n",
200
- "clip_l = CLIPModelL.from_pretrained('openai/clip-vit-large-patch14').to(DEVICE)\n",
201
- "proc_l = CLIPProcessorL.from_pretrained('openai/clip-vit-large-patch14')\n",
202
- "aes_weights = hf_hub_download('shunk031/aesthetics-predictor',\n",
203
- " filename='sac+logos+ava1-l14-linearMSE.pth')\n",
204
- "aes_model = AestheticPredictor(768)\n",
205
- "aes_model.load_state_dict(torch.load(aes_weights, map_location='cpu'))\n",
206
- "aes_model.eval().to(DEVICE)\n",
207
- "\n",
208
- "def aesthetic_score(image: Image.Image) -> float:\n",
209
- " \"\"\"LAION aesthetic score in [1, 10]. Higher = more visually pleasing.\"\"\"\n",
210
- " inputs = proc_l(images=image, return_tensors='pt').to(DEVICE)\n",
211
- " with torch.no_grad():\n",
212
- " emb = clip_l.get_image_features(**inputs)\n",
213
- " emb = emb / emb.norm(dim=-1, keepdim=True)\n",
214
- " score = aes_model(emb).item()\n",
215
- " return round(float(np.clip(score, 1, 10)), 2)\n",
216
- "\n",
217
- "aes = aesthetic_score(img)\n",
218
- "print(f'Aesthetic Score: {aes}/10 ({\"Good\" if aes >= 5 else \"Moderate\" if aes >= 3 else \"Low\"})')"
219
- ],
220
- "execution_count": null,
221
- "outputs": []
222
- },
223
- {
224
- "cell_type": "code",
225
- "metadata": {},
226
- "source": [
227
- "# ── 8. FID — Frechet Inception Distance (Precision analogue) ─────────────────\n",
228
- "import torchvision.transforms as T\n",
229
- "from torchvision.models import inception_v3\n",
230
- "\n",
231
- "print('Loading Inception-v3 for FID...')\n",
232
- "inception = inception_v3(pretrained=True, transform_input=False)\n",
233
- "inception.fc = torch.nn.Identity() # strip classifier -> 2048-d features\n",
234
- "inception.eval().to(DEVICE)\n",
235
- "\n",
236
- "_tfm = T.Compose([\n",
237
- " T.Resize((299, 299)),\n",
238
- " T.ToTensor(),\n",
239
- " T.Normalize([0.5]*3, [0.5]*3),\n",
240
- "])\n",
241
- "\n",
242
- "def inception_features(images):\n",
243
- " \"\"\"Extract 2048-d Inception-v3 features from a list of PIL images.\"\"\"\n",
244
- " feats = []\n",
245
- " for img in images:\n",
246
- " t = _tfm(img.convert('RGB')).unsqueeze(0).to(DEVICE)\n",
247
- " with torch.no_grad():\n",
248
- " feats.append(inception(t).cpu().numpy())\n",
249
- " return np.concatenate(feats, axis=0)\n",
250
- "\n",
251
- "def fid_score(gen_images, ref_images):\n",
252
- " \"\"\"\n",
253
- " Frechet Inception Distance. Lower = better.\n",
254
- " Precision analogue: how close is the generated distribution to the real one?\n",
255
- " \"\"\"\n",
256
- " assert len(gen_images) >= 2 and len(ref_images) >= 2, \\\n",
257
- " 'Need at least 2 images per group'\n",
258
- "\n",
259
- " gen_f = inception_features(gen_images)\n",
260
- " ref_f = inception_features(ref_images)\n",
261
- "\n",
262
- " mu_g, sigma_g = gen_f.mean(0), np.cov(gen_f, rowvar=False)\n",
263
- " mu_r, sigma_r = ref_f.mean(0), np.cov(ref_f, rowvar=False)\n",
264
- "\n",
265
- " diff = mu_g - mu_r\n",
266
- " eigvals, eigvecs = np.linalg.eigh(sigma_g @ sigma_r)\n",
267
- " eigvals = np.maximum(eigvals, 0)\n",
268
- " sqrt_prod = eigvecs @ np.diag(np.sqrt(eigvals)) @ eigvecs.T\n",
269
- " return round(float(diff @ diff + np.trace(sigma_g + sigma_r - 2 * sqrt_prod)), 2)\n",
270
- "\n",
271
- "print('FID helpers ready')"
272
- ],
273
- "execution_count": null,
274
- "outputs": []
275
- },
276
- {
277
- "cell_type": "code",
278
- "metadata": {},
279
- "source": [
280
- "# ── 9. Batch evaluation: CLIP + FID ──────────────────────────────────────────\n",
281
- "N_GEN = 4 # number of prompts to generate (keep small for speed)\n",
282
- "N_REF = 4 # reference images from dataset\n",
283
- "\n",
284
- "gen_prompts = [dataset[i][PROMPT_COLUMN] for i in random.sample(range(len(dataset)), N_GEN)]\n",
285
- "ref_prompts = [dataset[i][PROMPT_COLUMN] for i in random.sample(range(len(dataset)), N_REF)]\n",
286
- "\n",
287
- "print('Generating images for evaluation...')\n",
288
- "gen_images = [generate(p, show=False) for p in gen_prompts]\n",
289
- "ref_images = [generate(p, show=False) for p in ref_prompts]\n",
290
- "\n",
291
- "# Per-image CLIP scores\n",
292
- "clip_scores = [clip_score(img, p) for img, p in zip(gen_images, gen_prompts)]\n",
293
- "\n",
294
- "# FID\n",
295
- "fid = fid_score(gen_images, ref_images)\n",
296
- "\n",
297
- "# Report\n",
298
- "print('\\n=== Evaluation Results ===')\n",
299
- "print(f'Average CLIP Score : {np.mean(clip_scores):.1f}/100 (recall analogue)')\n",
300
- "print(f'FID : {fid:.1f} (precision analogue; lower=better)')\n",
301
- "print()\n",
302
- "for i, (p, s) in enumerate(zip(gen_prompts, clip_scores)):\n",
303
- " print(f' [{i}] CLIP={s:.1f} \"{p[:60]}\"')\n",
304
- "\n",
305
- "# Show generated images with scores\n",
306
- "fig, axes = plt.subplots(1, N_GEN, figsize=(4*N_GEN, 4))\n",
307
- "for ax, img, p, s in zip(axes, gen_images, gen_prompts, clip_scores):\n",
308
- " ax.imshow(img)\n",
309
- " ax.axis('off')\n",
310
- " ax.set_title(f'CLIP={s:.1f}\\n{p[:30]}...', fontsize=7)\n",
311
- "plt.suptitle(f'FID={fid:.1f} | Avg CLIP={np.mean(clip_scores):.1f}', fontsize=10)\n",
312
- "plt.tight_layout()\n",
313
- "plt.show()"
314
- ],
315
- "execution_count": null,
316
- "outputs": []
317
- },
318
- {
319
- "cell_type": "code",
320
- "metadata": {},
321
- "source": [
322
- "# ── 10. Full evaluation on a single custom prompt ────────────────────────────\n",
323
- "custom = 'a serene Japanese zen garden at dawn, watercolor style'\n",
324
- "\n",
325
- "custom_img = generate(custom, seed=999)\n",
326
- "\n",
327
- "c_score = clip_score(custom_img, custom)\n",
328
- "a_score = aesthetic_score(custom_img)\n",
329
- "\n",
330
- "print(f'Prompt : {custom}')\n",
331
- "print(f'CLIP Score : {c_score:.1f}/100 (recall – prompt alignment)')\n",
332
- "print(f'Aesthetic Score : {a_score:.2f}/10 (visual quality)')"
333
- ],
334
- "execution_count": null,
335
- "outputs": []
336
- },
337
- {
338
- "cell_type": "code",
339
- "metadata": {},
340
- "source": [
341
- "# ── 11. (Optional) Push model to HF Hub ──────────────────────────────────────\n",
342
- "# from huggingface_hub import notebook_login\n",
343
- "# notebook_login()\n",
344
- "# pipe.push_to_hub('YOUR_USERNAME/my-txt2img')\n",
345
- "# print('Pushed to Hub')"
346
- ],
347
- "execution_count": null,
348
- "outputs": []
349
- }
350
- ]
351
- }