Dulamaa commited on
Commit
88ba839
·
1 Parent(s): d1324d0

intital commit

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ text_to_digit_diffusion_mnist.pth filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """app.ipynb
3
+
4
+ Automatically generated by Colab.
5
+
6
+ Original file is located at
7
+ https://colab.research.google.com/drive/104LGaVaxm2qBgfuuNORSOHuzKCmgOVKm
8
+ """
9
+
10
+ ### 1. Imports and setup ###
11
+
12
+ import gradio as gr
13
+ import torch
14
+ import numpy as np
15
+
16
+ from model import create_diffusion_model
17
+ from timeit import default_timer as timer
18
+ from typing import Tuple
19
+
20
+ # ------------------------------
21
+ # 2. Model preparation
22
+ # ------------------------------
23
+
24
+ device = "cuda" if torch.cuda.is_available() else "cpu"
25
+
26
+ # Digit vocabulary
27
+ digit_words = ["zero","one","two","three","four","five","six","seven","eight","nine"]
28
+ word_to_idx = {w:i for i,w in enumerate(digit_words)}
29
+
30
+ # Create diffusion model
31
+ model = create_diffusion_model(
32
+ emb_dim=64,
33
+ timesteps=100,
34
+ seed=42,
35
+ device=device
36
+ )
37
+
38
+ # Load trained weights
39
+ model.load_state_dict(
40
+ torch.load(
41
+ "text_to_digit_diffusion_mnist.pth",
42
+ map_location=device
43
+ )
44
+ )
45
+
46
+ model.eval()
47
+
48
+ # ------------------------------
49
+ # 3. Diffusion scheduler utils
50
+ # ------------------------------
51
+
52
+ timesteps = 100
53
+ betas = torch.linspace(1e-4, 0.02, timesteps).to(device)
54
+ alphas = 1.0 - betas
55
+ alphas_cumprod = torch.cumprod(alphas, dim=0)
56
+
57
+ def extract(a, t, x_shape):
58
+ return a.gather(-1, t).reshape(-1,1,1,1).expand(x_shape)
59
+
60
+ # ------------------------------
61
+ # 4. Predict / Generate function
62
+ # ------------------------------
63
+
64
+ @torch.inference_mode()
65
+ def predict(text: str) -> Tuple[np.ndarray, float]:
66
+ start_time = timer()
67
+
68
+ text = text.strip().lower()
69
+
70
+ # Normalize input
71
+ if text.isdigit():
72
+ label = int(text)
73
+ else:
74
+ label = word_to_idx.get(text, None)
75
+
76
+ if label is None or not (0 <= label <= 9):
77
+ raise ValueError("Please enter a digit (0–9) or its word form.")
78
+
79
+ labels = torch.tensor([label], device=device)
80
+ uncond_labels = torch.tensor([0], device=device)
81
+
82
+ # Start from noise
83
+ x = torch.randn(1, 1, 28, 28, device=device)
84
+
85
+ guidance_scale = 3.0
86
+
87
+ for i in reversed(range(1, timesteps)):
88
+ t = torch.full((1,), i, device=device)
89
+
90
+ pred_cond = model(x, t, labels)
91
+ pred_uncond = model(x, t, uncond_labels)
92
+
93
+ pred_noise = pred_uncond + guidance_scale * (pred_cond - pred_uncond)
94
+
95
+ beta_t = extract(betas, t, x.shape)
96
+ alpha_t = extract(alphas, t, x.shape)
97
+ alpha_bar_t = extract(alphas_cumprod, t, x.shape)
98
+
99
+ pred_x0 = (x - torch.sqrt(1 - alpha_bar_t) * pred_noise) / torch.sqrt(alpha_bar_t)
100
+ x = torch.sqrt(alpha_t) * pred_x0 + torch.sqrt(beta_t) * pred_noise
101
+
102
+ img = (x.clamp(-1,1) + 1) / 2
103
+ img = img[0,0].cpu().numpy()
104
+
105
+ end_time = timer()
106
+ gen_time = round(end_time - start_time, 4)
107
+
108
+ return img, gen_time
109
+
110
+ # ------------------------------
111
+ # 5. Gradio app
112
+ # ------------------------------
113
+
114
+ title = "Text-to-Digit Diffusion (MNIST)"
115
+ description = (
116
+ "A **conditional diffusion model** trained on MNIST. "
117
+ "Type a digit (e.g. `seven` or `7`) to generate a handwritten number."
118
+ )
119
+ article = "Created by [Programming Ocean Academy](https://www.programming-ocean.com/)"
120
+
121
+ demo = gr.Interface(
122
+ fn=predict,
123
+ inputs=gr.Textbox(
124
+ label="Enter digit (0–9 or word)",
125
+ placeholder="seven or 7"
126
+ ),
127
+ outputs=[
128
+ gr.Image(
129
+ label="Generated Digit",
130
+ type="numpy",
131
+ width=256,
132
+ height=256
133
+ ),
134
+ gr.Number(label="generation time (s)")
135
+ ],
136
+ title=title,
137
+ description=description,
138
+ article=article
139
+ )
140
+
141
+ # Launch demo
142
+ demo.launch(debug=False)
143
+
model.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """model.ipynb
3
+
4
+ Automatically generated by Colab.
5
+
6
+ Original file is located at
7
+ https://colab.research.google.com/drive/1_9Ac8If0MzzNMrKBdneReuZPjByJNzHE
8
+ """
9
+
10
+ import torch
11
+ from torch import nn
12
+ import torch.nn.functional as F
13
+
14
+
15
+ # --------------------------------------------------
16
+ # UNet for Text-to-Digit Diffusion (MNIST)
17
+ # --------------------------------------------------
18
+ class UNet(nn.Module):
19
+ def __init__(self, emb_dim: int = 64, num_classes: int = 10, timesteps: int = 100):
20
+ super().__init__()
21
+
22
+ # Time embedding
23
+ self.time_mlp = nn.Sequential(
24
+ nn.Linear(1, emb_dim),
25
+ nn.ReLU(),
26
+ nn.Linear(emb_dim, emb_dim)
27
+ )
28
+
29
+ # Label embedding
30
+ self.label_embed = nn.Embedding(num_classes, emb_dim)
31
+
32
+ # Encoder
33
+ self.enc1 = nn.Conv2d(1, 32, 3, padding=1)
34
+ self.enc2 = nn.Conv2d(32, 64, 3, stride=2, padding=1)
35
+ self.enc3 = nn.Conv2d(64, 128, 3, stride=2, padding=1)
36
+
37
+ # Bottleneck
38
+ self.bot = nn.Conv2d(128, 128, 3, padding=1)
39
+
40
+ # Conditioning projection
41
+ self.cond_proj = nn.Linear(emb_dim, 128)
42
+
43
+ # Decoder
44
+ self.dec3 = nn.ConvTranspose2d(128, 64, 4, stride=2, padding=1)
45
+ self.dec2 = nn.ConvTranspose2d(64, 32, 4, stride=2, padding=1)
46
+ self.dec1 = nn.Conv2d(32, 1, 3, padding=1)
47
+
48
+ self.timesteps = timesteps
49
+
50
+ def forward(self, x, t, labels):
51
+ # Time embedding
52
+ t = t.unsqueeze(-1).float() / self.timesteps
53
+ t_emb = self.time_mlp(t)
54
+
55
+ # Label embedding
56
+ l_emb = self.label_embed(labels)
57
+
58
+ # Conditioning
59
+ cond = t_emb + l_emb
60
+ cond = self.cond_proj(cond).unsqueeze(-1).unsqueeze(-1)
61
+
62
+ # Encoder
63
+ x1 = F.relu(self.enc1(x))
64
+ x2 = F.relu(self.enc2(x1))
65
+ x3 = F.relu(self.enc3(x2))
66
+
67
+ # Bottleneck + conditioning
68
+ h = F.relu(self.bot(x3 + cond))
69
+
70
+ # Decoder with skip connections
71
+ h = F.relu(self.dec3(h)) + x2
72
+ h = F.relu(self.dec2(h)) + x1
73
+
74
+ return self.dec1(h)
75
+
76
+
77
+ # --------------------------------------------------
78
+ # Factory function (EffNet-style)
79
+ # --------------------------------------------------
80
+ def create_diffusion_model(
81
+ emb_dim: int = 64,
82
+ num_classes: int = 10,
83
+ timesteps: int = 100,
84
+ seed: int = 42,
85
+ device: str = "cpu"
86
+ ):
87
+ """
88
+ Creates a conditional diffusion UNet model.
89
+
90
+ Returns:
91
+ model (nn.Module): diffusion UNet
92
+ """
93
+
94
+ # Reproducibility
95
+ torch.manual_seed(seed)
96
+
97
+ model = UNet(
98
+ emb_dim=emb_dim,
99
+ num_classes=num_classes,
100
+ timesteps=timesteps
101
+ ).to(device)
102
+
103
+ return model
104
+
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ torch==2.9.0
2
+ torchvision==0.24.0
3
+ gradio==5.50.0
text_to_digit_diffusion_mnist.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3cde23169edfec8f5ada8bfe2cdf9d30e15c022c6b9b4b98296ee5c2d37a18ed
3
+ size 1679030
text_to_digit_diffusion_mnist.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3cde23169edfec8f5ada8bfe2cdf9d30e15c022c6b9b4b98296ee5c2d37a18ed
3
+ size 1679030