areddydev commited on
Commit
0c98c23
Β·
verified Β·
1 Parent(s): eee2437

Upload 3 files

Browse files
Files changed (3) hide show
  1. README.md +4 -1
  2. app.py +392 -0
  3. requirements.txt +2 -0
README.md CHANGED
@@ -10,4 +10,7 @@ app_file: app.py
10
  pinned: false
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
10
  pinned: false
11
  ---
12
 
13
+ This is built by rlkit team.
14
+
15
+ Avinash Reddy
16
+
app.py ADDED
@@ -0,0 +1,392 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ from dataclasses import dataclass
3
+
4
+ import matplotlib
5
+
6
+ matplotlib.use("Agg") # headless backend for Spaces
7
+ import matplotlib.pyplot as plt
8
+
9
+ import gradio as gr
10
+ import torch
11
+ import torch.nn as nn
12
+ import torch.nn.functional as F
13
+
14
+
15
+ # ----------------------------
16
+ # Simple starter datasets
17
+ # ----------------------------
18
+ DATASETS = {
19
+ "πŸͺ„ PoemBot": """Roses are red and skies are blue.
20
+ The moon shines softly over you.
21
+ A little bird sings in the tree.
22
+ The wind is dancing wild and free.
23
+ Morning light begins to glow.
24
+ Tiny flowers start to grow.
25
+ Clouds are floating, soft and slow.
26
+ Kindness is the seed we sow.
27
+ Stars are bright in velvet night.
28
+ Dreams can fly like paper kites.
29
+ Rain can tap a gentle beat.
30
+ Puddles sparkle on the street.
31
+ The sun comes up, the shadows run.
32
+ A day begins with hope and fun.
33
+ A quiet river hums a song.
34
+ It carries little leaves along.
35
+ """,
36
+ "πŸ“– StoryBot": """Once upon a time, a small turtle found a golden key. It opened a tiny door under an old tree. Inside, the turtle discovered a library for animals.
37
+ One day, Maya built a robot from cardboard and tape. The robot could only say kind things. Soon, everyone wanted to build one too.
38
+ A brave squirrel wanted to touch a cloud. It climbed the tallest pine tree in the park. From the top, the cloud looked like a giant pillow.
39
+ Leo lost his red balloon at the fair. A bird carried it across the sky. The next morning, Leo found it tied to his mailbox.
40
+ A dragon lived behind the school garden. It was not scary at all. Every Friday, it helped water the tomatoes.
41
+ """,
42
+ "🎬 ReviewBot": """Movie: The Lion King
43
+ Review: This movie is emotional, exciting, and full of memorable songs.
44
+ Movie: Frozen
45
+ Review: This movie is magical and funny, with strong characters and great music.
46
+ Movie: Toy Story
47
+ Review: This movie is creative, warm, and teaches a lesson about friendship.
48
+ Movie: Finding Nemo
49
+ Review: This movie is colorful, adventurous, and perfect for families.
50
+ Movie: Spider-Man
51
+ Review: This movie is action packed, funny, and inspiring.
52
+ Movie: Inside Out
53
+ Review: This movie is smart, creative, and helps explain feelings.
54
+ Movie: Moana
55
+ Review: This movie is beautiful, brave, and filled with adventure.
56
+ Movie: Coco
57
+ Review: This movie is touching, musical, and full of family love.
58
+ """,
59
+ }
60
+
61
+ DEFAULT_PROJECT = "πŸͺ„ PoemBot"
62
+
63
+
64
+ # ----------------------------
65
+ # Tiny character language model
66
+ # ----------------------------
67
+ class TinyCharModel(nn.Module):
68
+ def __init__(self, vocab_size, emb_size=48, hidden_size=96):
69
+ super().__init__()
70
+ self.embedding = nn.Embedding(vocab_size, emb_size)
71
+ self.rnn = nn.GRU(emb_size, hidden_size, batch_first=True)
72
+ self.head = nn.Linear(hidden_size, vocab_size)
73
+
74
+ def forward(self, idx, hidden=None):
75
+ x = self.embedding(idx)
76
+ out, hidden = self.rnn(x, hidden)
77
+ logits = self.head(out)
78
+ return logits, hidden
79
+
80
+
81
+ @dataclass
82
+ class TrainState:
83
+ model: object = None
84
+ stoi: object = None
85
+ itos: object = None
86
+ vocab: object = None
87
+ device: str = "cpu"
88
+ trained: bool = False
89
+
90
+
91
+ def build_vocab(text):
92
+ chars = sorted(list(set(text)))
93
+ stoi = {ch: i for i, ch in enumerate(chars)}
94
+ itos = {i: ch for ch, i in stoi.items()}
95
+ return chars, stoi, itos
96
+
97
+
98
+ def encode(text, stoi):
99
+ return torch.tensor([stoi[c] for c in text if c in stoi], dtype=torch.long)
100
+
101
+
102
+ def decode(indices, itos):
103
+ return "".join(itos[int(i)] for i in indices)
104
+
105
+
106
+ def sample_batch(data, block_size=64, batch_size=16):
107
+ if len(data) <= block_size + 1:
108
+ block_size = max(4, len(data) - 2)
109
+ ix = torch.randint(0, len(data) - block_size - 1, (batch_size,))
110
+ x = torch.stack([data[i : i + block_size] for i in ix])
111
+ y = torch.stack([data[i + 1 : i + block_size + 1] for i in ix])
112
+ return x, y
113
+
114
+
115
+ @torch.no_grad()
116
+ def generate_text(model, start_text, stoi, itos, length=300, temperature=0.8):
117
+ model.eval()
118
+ device = next(model.parameters()).device
119
+
120
+ # keep only characters known to the model
121
+ clean_start = "".join([c for c in start_text if c in stoi])
122
+ if clean_start == "":
123
+ clean_start = random.choice(list(stoi.keys()))
124
+
125
+ idx = torch.tensor([[stoi[c] for c in clean_start]], dtype=torch.long, device=device)
126
+
127
+ for _ in range(length):
128
+ idx_cond = idx[:, -64:]
129
+ logits, _ = model(idx_cond)
130
+ logits = logits[:, -1, :] / max(temperature, 0.1)
131
+ probs = F.softmax(logits, dim=-1)
132
+ next_id = torch.multinomial(probs, num_samples=1)
133
+ idx = torch.cat([idx, next_id], dim=1)
134
+
135
+ return decode(idx[0].tolist(), itos)
136
+
137
+
138
+ def make_loss_plot(losses):
139
+ fig, ax = plt.subplots(figsize=(6, 3.4))
140
+ fig.patch.set_facecolor("#ffffff")
141
+ ax.set_facecolor("#fbfbfd")
142
+
143
+ ax.plot(losses, color="#7c3aed", linewidth=2.2)
144
+ ax.fill_between(range(len(losses)), losses, min(losses) if losses else 0,
145
+ color="#7c3aed", alpha=0.12)
146
+
147
+ ax.set_xlabel("Training step", fontsize=11)
148
+ ax.set_ylabel("Loss", fontsize=11)
149
+ ax.set_title("Training loss goes down as the model learns πŸ“‰",
150
+ fontsize=12, fontweight="bold", color="#1f2937")
151
+ ax.grid(True, linestyle="--", alpha=0.35)
152
+ for spine in ["top", "right"]:
153
+ ax.spines[spine].set_visible(False)
154
+ fig.tight_layout()
155
+ return fig
156
+
157
+
158
+ def load_dataset(project):
159
+ return DATASETS[project]
160
+
161
+
162
+ def create_fresh_model(dataset_text):
163
+ vocab, stoi, itos = build_vocab(dataset_text)
164
+ model = TinyCharModel(len(vocab))
165
+ return TrainState(model=model, stoi=stoi, itos=itos, vocab=vocab, trained=False)
166
+
167
+
168
+ def untrained_output(project, dataset_text, start_text, output_length, temperature):
169
+ if len(dataset_text.strip()) < 100:
170
+ return "⚠️ Please add more training text first. Try at least 100 characters."
171
+
172
+ state = create_fresh_model(dataset_text)
173
+ text = generate_text(
174
+ state.model,
175
+ start_text=start_text,
176
+ stoi=state.stoi,
177
+ itos=state.itos,
178
+ length=int(output_length),
179
+ temperature=float(temperature),
180
+ )
181
+ return text
182
+
183
+
184
+ def train_model(
185
+ project,
186
+ dataset_text,
187
+ training_steps,
188
+ start_text,
189
+ output_length,
190
+ temperature,
191
+ progress=gr.Progress(),
192
+ ):
193
+ if len(dataset_text.strip()) < 100:
194
+ return None, "⚠️ Please add more training text first. Try at least 100 characters.", ""
195
+
196
+ torch.manual_seed(7)
197
+ random.seed(7)
198
+
199
+ state = create_fresh_model(dataset_text)
200
+ device = "cuda" if torch.cuda.is_available() else "cpu"
201
+ state.device = device
202
+ state.model.to(device)
203
+
204
+ data = encode(dataset_text, state.stoi).to(device)
205
+ optimizer = torch.optim.AdamW(state.model.parameters(), lr=2e-3)
206
+
207
+ losses = []
208
+ steps = int(training_steps)
209
+
210
+ state.model.train()
211
+ for step in progress.tqdm(range(steps), desc="Training tiny model"):
212
+ xb, yb = sample_batch(data, block_size=64, batch_size=16)
213
+ xb, yb = xb.to(device), yb.to(device)
214
+
215
+ logits, _ = state.model(xb)
216
+ loss = F.cross_entropy(logits.reshape(-1, logits.size(-1)), yb.reshape(-1))
217
+
218
+ optimizer.zero_grad()
219
+ loss.backward()
220
+ optimizer.step()
221
+
222
+ losses.append(float(loss.item()))
223
+
224
+ fig = make_loss_plot(losses)
225
+ sample = generate_text(
226
+ state.model,
227
+ start_text=start_text,
228
+ stoi=state.stoi,
229
+ itos=state.itos,
230
+ length=int(output_length),
231
+ temperature=float(temperature),
232
+ )
233
+
234
+ state.trained = True
235
+ first_loss = losses[0] if losses else 0.0
236
+ last_loss = losses[-1] if losses else 0.0
237
+ message = (
238
+ f"βœ… Done! The tiny model trained on **{len(dataset_text)} characters** "
239
+ f"using **{len(state.vocab)} unique characters**.\n\n"
240
+ f"Loss dropped from **{first_loss:.3f} β†’ {last_loss:.3f}** over {steps} steps. "
241
+ f"Device used: `{device}`."
242
+ )
243
+ return fig, sample, message
244
+
245
+
246
+ EXPLANATION = """
247
+ # 🧠 Tiny Generative AI Trainer
248
+
249
+ A **kid-friendly mini version** of how generative AI is trained. Students change only three main things:
250
+
251
+ 1. **🎯 Project type** &nbsp;β€”&nbsp; poems, stories, or movie reviews
252
+ 2. **⏱️ Training steps** &nbsp;β€”&nbsp; how long the model learns
253
+ 3. **✍️ Start text** &nbsp;β€”&nbsp; the beginning of the text the model completes
254
+
255
+ The model learns by trying to **predict the next character** again and again.
256
+ When the loss curve goes down, the model is getting better at copying the pattern of the examples.
257
+ """
258
+
259
+ CUSTOM_CSS = """
260
+ .gradio-container { max-width: 1100px !important; margin: auto !important; }
261
+
262
+ #hero {
263
+ background: linear-gradient(135deg, #7c3aed 0%, #db2777 50%, #f59e0b 100%);
264
+ border-radius: 18px;
265
+ padding: 6px 26px;
266
+ color: white;
267
+ box-shadow: 0 10px 30px rgba(124, 58, 237, 0.25);
268
+ margin-bottom: 8px;
269
+ }
270
+ #hero h1 { color: white !important; font-size: 2.1rem !important; }
271
+ #hero p, #hero li, #hero strong { color: rgba(255,255,255,0.95) !important; }
272
+ #hero a { color: #fde68a !important; }
273
+
274
+ .panel-card {
275
+ border-radius: 16px !important;
276
+ padding: 16px !important;
277
+ background: var(--block-background-fill);
278
+ box-shadow: 0 4px 18px rgba(0,0,0,0.06);
279
+ border: 1px solid var(--border-color-primary);
280
+ }
281
+
282
+ #train-btn { font-weight: 700 !important; }
283
+
284
+ footer { visibility: hidden; }
285
+ """
286
+
287
+
288
+ with gr.Blocks(
289
+ title="Tiny Generative AI Trainer",
290
+ theme=gr.themes.Soft(
291
+ primary_hue="purple",
292
+ secondary_hue="pink",
293
+ font=[gr.themes.GoogleFont("Quicksand"), "system-ui", "sans-serif"],
294
+ ),
295
+ css=CUSTOM_CSS,
296
+ ) as demo:
297
+ with gr.Group(elem_id="hero"):
298
+ gr.Markdown(EXPLANATION)
299
+
300
+ with gr.Row():
301
+ with gr.Column(scale=1):
302
+ with gr.Group(elem_classes="panel-card"):
303
+ gr.Markdown("### βš™οΈ Controls")
304
+ project = gr.Dropdown(
305
+ choices=list(DATASETS.keys()),
306
+ value=DEFAULT_PROJECT,
307
+ label="1. Choose project",
308
+ )
309
+ training_steps = gr.Slider(
310
+ minimum=50,
311
+ maximum=1500,
312
+ value=500,
313
+ step=50,
314
+ label="2. Training steps",
315
+ info="More steps = more learning (but slower).",
316
+ )
317
+ start_text = gr.Textbox(
318
+ value="Once upon",
319
+ label="3. Start text / prompt",
320
+ )
321
+ with gr.Row():
322
+ output_length = gr.Slider(
323
+ minimum=80,
324
+ maximum=600,
325
+ value=250,
326
+ step=20,
327
+ label="Output length",
328
+ )
329
+ temperature = gr.Slider(
330
+ minimum=0.3,
331
+ maximum=1.5,
332
+ value=0.8,
333
+ step=0.1,
334
+ label="Creativity 🎨",
335
+ )
336
+
337
+ with gr.Row():
338
+ untrained_btn = gr.Button("🎲 Generate (untrained)", variant="secondary")
339
+ train_btn = gr.Button("πŸš€ Train & Generate", variant="primary", elem_id="train-btn")
340
+
341
+ with gr.Column(scale=1):
342
+ with gr.Group(elem_classes="panel-card"):
343
+ with gr.Accordion("πŸ“š Training examples / dataset", open=True):
344
+ dataset_text = gr.Textbox(
345
+ value=DATASETS[DEFAULT_PROJECT],
346
+ lines=14,
347
+ label="Students can edit this text or paste their own examples",
348
+ )
349
+
350
+ gr.Markdown("## πŸ” Results")
351
+ with gr.Row():
352
+ with gr.Column():
353
+ untrained_box = gr.Textbox(
354
+ lines=8, label="🎲 Untrained model output", show_copy_button=True
355
+ )
356
+ with gr.Column():
357
+ trained_box = gr.Textbox(
358
+ lines=8, label="✨ Trained model output", show_copy_button=True
359
+ )
360
+
361
+ with gr.Row():
362
+ loss_plot = gr.Plot(label="πŸ“‰ Training loss plot")
363
+ status = gr.Markdown()
364
+
365
+ project.change(load_dataset, inputs=project, outputs=dataset_text)
366
+
367
+ untrained_btn.click(
368
+ untrained_output,
369
+ inputs=[project, dataset_text, start_text, output_length, temperature],
370
+ outputs=untrained_box,
371
+ )
372
+
373
+ train_btn.click(
374
+ train_model,
375
+ inputs=[project, dataset_text, training_steps, start_text, output_length, temperature],
376
+ outputs=[loss_plot, trained_box, status],
377
+ )
378
+
379
+ with gr.Accordion("πŸ’¬ Classroom discussion questions", open=False):
380
+ gr.Markdown(
381
+ """
382
+ - What changed after training?
383
+ - Did more training steps make the output better?
384
+ - What happens if the dataset is very small?
385
+ - What happens if the examples are all poems versus all movie reviews?
386
+ - Why does the model sometimes make spelling mistakes?
387
+ """
388
+ )
389
+
390
+
391
+ if __name__ == "__main__":
392
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ torch
2
+ matplotlib