Yash Nagraj commited on
Commit
931841c
·
1 Parent(s): ef356ee

Add training code

Browse files
Files changed (1) hide show
  1. BIG_GAN.ipynb +581 -0
BIG_GAN.ipynb CHANGED
@@ -0,0 +1,581 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 3,
6
+ "metadata": {},
7
+ "outputs": [],
8
+ "source": [
9
+ "import torch \n",
10
+ "import torch.nn as nn\n",
11
+ "import torch.nn.functional as F\n",
12
+ "\n",
13
+ "def orthogonal_regularization(weight):\n",
14
+ " weight = weight.flatten(1)\n",
15
+ " return torch.norm(\n",
16
+ " torch.dot(weight, weight) * (torch.ones_like(weight) - torch.eye(weight.shape[0]))\n",
17
+ " )\n"
18
+ ]
19
+ },
20
+ {
21
+ "cell_type": "code",
22
+ "execution_count": 4,
23
+ "metadata": {},
24
+ "outputs": [],
25
+ "source": [
26
+ "class ClassConditionalBatchNorm2d(nn.Module):\n",
27
+ " def __init__(self, in_channels, out_channels):\n",
28
+ " super().__init__()\n",
29
+ " self.bn = nn.BatchNorm2d(out_channels)\n",
30
+ " self.class_scale_transform = nn.utils.spectral_norm(nn.Linear(in_channels, out_channels))\n",
31
+ " self.class_shift_transform = nn.utils.spectral_norm(nn.Linear(in_channels, out_channels))\n",
32
+ "\n",
33
+ " def forward(self, x, y):\n",
34
+ " normalized_image = self.bn(x)\n",
35
+ " class_scale = (1 + self.class_scale_transform(y))[:,:,None,None]\n",
36
+ " class_shift = self.class_shift_transform(y)[:,:,None,None]\n",
37
+ " tranformed_image = class_scale * normalized_image + class_shift\n",
38
+ " return tranformed_image\n"
39
+ ]
40
+ },
41
+ {
42
+ "cell_type": "markdown",
43
+ "metadata": {},
44
+ "source": [
45
+ "## Attention Block for Big GAN\n",
46
+ "This paper has all that you need to implement this class:\n",
47
+ "https://arxiv.org/pdf/1805.08318"
48
+ ]
49
+ },
50
+ {
51
+ "cell_type": "code",
52
+ "execution_count": 5,
53
+ "metadata": {},
54
+ "outputs": [],
55
+ "source": [
56
+ "class AttentionBlock(nn.Module):\n",
57
+ " '''\n",
58
+ " AttentionBlock Class\n",
59
+ " Values:\n",
60
+ " channels: number of channels in input\n",
61
+ " '''\n",
62
+ " def __init__(self, channels):\n",
63
+ " super().__init__()\n",
64
+ "\n",
65
+ " self.channels = channels\n",
66
+ "\n",
67
+ " self.theta = nn.utils.spectral_norm(nn.Conv2d(channels, channels // 8, kernel_size=1, padding=0, bias=False))\n",
68
+ " self.phi = nn.utils.spectral_norm(nn.Conv2d(channels, channels // 8, kernel_size=1, padding=0, bias=False))\n",
69
+ " self.g = nn.utils.spectral_norm(nn.Conv2d(channels, channels // 2, kernel_size=1, padding=0, bias=False))\n",
70
+ " self.o = nn.utils.spectral_norm(nn.Conv2d(channels // 2, channels, kernel_size=1, padding=0, bias=False))\n",
71
+ "\n",
72
+ " self.gamma = nn.Parameter(torch.tensor(0.), requires_grad=True)\n",
73
+ "\n",
74
+ " def forward(self, x):\n",
75
+ " spatial_size = x.shape[2] * x.shape[3]\n",
76
+ "\n",
77
+ " # Apply convolutions to get query (theta), key (phi), and value (g) transforms\n",
78
+ " theta = self.theta(x)\n",
79
+ " phi = F.max_pool2d(self.phi(x), kernel_size=2)\n",
80
+ " g = F.max_pool2d(self.g(x), kernel_size=2)\n",
81
+ "\n",
82
+ " # Reshape spatial size for self-attention\n",
83
+ " theta = theta.view(-1, self.channels // 8, spatial_size)\n",
84
+ " phi = phi.view(-1, self.channels // 8, spatial_size // 4)\n",
85
+ " g = g.view(-1, self.channels // 2, spatial_size // 4)\n",
86
+ "\n",
87
+ " # Compute dot product attention with query (theta) and key (phi) matrices\n",
88
+ " beta = F.softmax(torch.bmm(theta.transpose(1, 2), phi), dim=-1)\n",
89
+ "\n",
90
+ " # Compute scaled dot product attention with value (g) and attention (beta) matrices\n",
91
+ " o = self.o(torch.bmm(g, beta.transpose(1, 2)).view(-1, self.channels // 2, x.shape[2], x.shape[3]))\n",
92
+ "\n",
93
+ " # Apply gain and residual\n",
94
+ " return self.gamma * o + x"
95
+ ]
96
+ },
97
+ {
98
+ "cell_type": "code",
99
+ "execution_count": 6,
100
+ "metadata": {},
101
+ "outputs": [],
102
+ "source": [
103
+ "class GResidualBlock(nn.Module):\n",
104
+ " '''\n",
105
+ " GResidualBlock Class\n",
106
+ " Values:\n",
107
+ " c_dim: the dimension of conditional vector [c, z], a scalar\n",
108
+ " in_channels: the number of channels in the input, a scalar\n",
109
+ " out_channels: the number of channels in the output, a scalar\n",
110
+ " '''\n",
111
+ "\n",
112
+ " def __init__(self, c_dim, in_channels, out_channels):\n",
113
+ " super().__init__()\n",
114
+ "\n",
115
+ " self.conv1 = nn.utils.spectral_norm(nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1))\n",
116
+ " self.conv2 = nn.utils.spectral_norm(nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1))\n",
117
+ "\n",
118
+ " self.bn1 = ClassConditionalBatchNorm2d(c_dim, in_channels)\n",
119
+ " self.bn2 = ClassConditionalBatchNorm2d(c_dim, out_channels)\n",
120
+ "\n",
121
+ " self.activation = nn.ReLU()\n",
122
+ " self.upsample_fn = nn.Upsample(scale_factor=2) # upsample occurs in every gblock\n",
123
+ "\n",
124
+ " self.mixin = (in_channels != out_channels)\n",
125
+ " if self.mixin:\n",
126
+ " self.conv_mixin = nn.utils.spectral_norm(nn.Conv2d(in_channels, out_channels, kernel_size=1, padding=0))\n",
127
+ "\n",
128
+ " def forward(self, x, y):\n",
129
+ " # h := upsample(x, y)\n",
130
+ " h = self.bn1(x, y)\n",
131
+ " h = self.activation(h)\n",
132
+ " h = self.upsample_fn(h)\n",
133
+ " h = self.conv1(h)\n",
134
+ "\n",
135
+ " # h := conv(h, y)\n",
136
+ " h = self.bn2(h, y)\n",
137
+ " h = self.activation(h)\n",
138
+ " h = self.conv2(h)\n",
139
+ "\n",
140
+ " # x := upsample(x)\n",
141
+ " x = self.upsample_fn(x)\n",
142
+ " if self.mixin:\n",
143
+ " x = self.conv_mixin(x)\n",
144
+ "\n",
145
+ " return h + x"
146
+ ]
147
+ },
148
+ {
149
+ "cell_type": "code",
150
+ "execution_count": 7,
151
+ "metadata": {},
152
+ "outputs": [],
153
+ "source": [
154
+ "class Generator(nn.Module):\n",
155
+ " '''\n",
156
+ " Generator Class\n",
157
+ " Values:\n",
158
+ " z_dim: the dimension of random noise sampled, a scalar\n",
159
+ " shared_dim: the dimension of shared class embeddings, a scalar\n",
160
+ " base_channels: the number of base channels, a scalar\n",
161
+ " bottom_width: the height/width of image before it gets upsampled, a scalar\n",
162
+ " n_classes: the number of image classes, a scalar\n",
163
+ " '''\n",
164
+ " \n",
165
+ " def __init__(self, base_channels=96, bottom_width=4, z_dim=120, shared_dim=128, n_classes=1000):\n",
166
+ " super().__init__()\n",
167
+ " \n",
168
+ " n_chunks = 6 # 5 (generator blocks) + 1 (generator input)\n",
169
+ " self.z_chunk_size = z_dim // n_chunks\n",
170
+ " self.z_dim = z_dim\n",
171
+ " self.shared_dim = shared_dim\n",
172
+ " self.bottom_width = bottom_width\n",
173
+ " \n",
174
+ " # No spectral normalization on embeddings, which authors observe to cripple the generator\n",
175
+ " self.shared_emb = nn.Embedding(n_classes, shared_dim)\n",
176
+ " \n",
177
+ " self.proj_z = nn.Linear(self.z_chunk_size, 16 * base_channels * bottom_width ** 2)\n",
178
+ " \n",
179
+ " # Can't use one big nn.Sequential since we are adding class+noise at each block\n",
180
+ " self.g_blocks = nn.ModuleList([\n",
181
+ " nn.ModuleList([\n",
182
+ " GResidualBlock(shared_dim + self.z_chunk_size, 16 * base_channels, 16 * base_channels),\n",
183
+ " AttentionBlock(16 * base_channels),\n",
184
+ " ]),\n",
185
+ " nn.ModuleList([\n",
186
+ " GResidualBlock(shared_dim + self.z_chunk_size, 16 * base_channels, 8 * base_channels),\n",
187
+ " AttentionBlock(8 * base_channels),\n",
188
+ " ]),\n",
189
+ " nn.ModuleList([\n",
190
+ " GResidualBlock(shared_dim + self.z_chunk_size, 8 * base_channels, 4 * base_channels),\n",
191
+ " AttentionBlock(4 * base_channels),\n",
192
+ " ]),\n",
193
+ " nn.ModuleList([\n",
194
+ " GResidualBlock(shared_dim + self.z_chunk_size, 4 * base_channels, 2 * base_channels),\n",
195
+ " AttentionBlock(2 * base_channels),\n",
196
+ " ]),\n",
197
+ " nn.ModuleList([\n",
198
+ " GResidualBlock(shared_dim + self.z_chunk_size, 2 * base_channels, base_channels),\n",
199
+ " AttentionBlock(base_channels),\n",
200
+ " ]),\n",
201
+ " ])\n",
202
+ " self.proj_o = nn.Sequential(\n",
203
+ " nn.BatchNorm2d(base_channels),\n",
204
+ " nn.ReLU(inplace=True),\n",
205
+ " nn.utils.spectral_norm(nn.Conv2d(base_channels, 3, kernel_size=1, padding=0)),\n",
206
+ " nn.Tanh(),\n",
207
+ " )\n",
208
+ " \n",
209
+ " def forward(self, z, y):\n",
210
+ " '''\n",
211
+ " z: random noise with size self.z_dim\n",
212
+ " y: class embeddings with size self.shared_dim\n",
213
+ " = NOTE =\n",
214
+ " y should be class embeddings from self.shared_emb, not the raw class labels\n",
215
+ " '''\n",
216
+ " # Chunk z and concatenate to shared class embeddings\n",
217
+ " zs = torch.split(z, self.z_chunk_size, dim=1)\n",
218
+ " z = zs[0]\n",
219
+ " ys = [torch.cat([y, z], dim=1) for z in zs[1:]]\n",
220
+ " \n",
221
+ " # Project noise and reshape to feed through generator blocks\n",
222
+ " h = self.proj_z(z)\n",
223
+ " h = h.view(h.size(0), -1, self.bottom_width, self.bottom_width)\n",
224
+ " \n",
225
+ " # Feed through generator blocks\n",
226
+ " for idx, g_block in enumerate(self.g_blocks):\n",
227
+ " h = g_block[0](h, ys[idx])\n",
228
+ " h = g_block[1](h)\n",
229
+ " \n",
230
+ " # Project to 3 RGB channels with tanh to map values to [-1, 1]\n",
231
+ " h = self.proj_o(h)\n",
232
+ " \n",
233
+ " return h"
234
+ ]
235
+ },
236
+ {
237
+ "cell_type": "code",
238
+ "execution_count": 8,
239
+ "metadata": {},
240
+ "outputs": [],
241
+ "source": [
242
+ "class DResidualBlock(nn.Module):\n",
243
+ " '''\n",
244
+ " DResidualBlock Class\n",
245
+ " Values:\n",
246
+ " in_channels: the number of channels in the input, a scalar\n",
247
+ " out_channels: the number of channels in the output, a scalar\n",
248
+ " downsample: whether to apply downsampling\n",
249
+ " use_preactivation: whether to apply an activation function before the first convolution\n",
250
+ " '''\n",
251
+ "\n",
252
+ " def __init__(self, in_channels, out_channels, downsample=True, use_preactivation=False):\n",
253
+ " super().__init__()\n",
254
+ "\n",
255
+ " self.conv1 = nn.utils.spectral_norm(nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1))\n",
256
+ " self.conv2 = nn.utils.spectral_norm(nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1))\n",
257
+ "\n",
258
+ " self.activation = nn.ReLU()\n",
259
+ " self.use_preactivation = use_preactivation # apply preactivation in all except first dblock\n",
260
+ "\n",
261
+ " self.downsample = downsample # downsample occurs in all except last dblock\n",
262
+ " if downsample:\n",
263
+ " self.downsample_fn = nn.AvgPool2d(2)\n",
264
+ " self.mixin = (in_channels != out_channels) or downsample\n",
265
+ " if self.mixin:\n",
266
+ " self.conv_mixin = nn.utils.spectral_norm(nn.Conv2d(in_channels, out_channels, kernel_size=1, padding=0))\n",
267
+ "\n",
268
+ " def _residual(self, x):\n",
269
+ " if self.use_preactivation:\n",
270
+ " if self.mixin:\n",
271
+ " x = self.conv_mixin(x)\n",
272
+ " if self.downsample:\n",
273
+ " x = self.downsample_fn(x)\n",
274
+ " else:\n",
275
+ " if self.downsample:\n",
276
+ " x = self.downsample_fn(x)\n",
277
+ " if self.mixin:\n",
278
+ " x = self.conv_mixin(x)\n",
279
+ " return x\n",
280
+ "\n",
281
+ " def forward(self, x):\n",
282
+ " # Apply preactivation if applicable\n",
283
+ " if self.use_preactivation:\n",
284
+ " h = F.relu(x)\n",
285
+ " else:\n",
286
+ " h = x\n",
287
+ "\n",
288
+ " h = self.conv1(h)\n",
289
+ " h = self.activation(h)\n",
290
+ " if self.downsample:\n",
291
+ " h = self.downsample_fn(h)\n",
292
+ "\n",
293
+ " return h + self._residual(x)"
294
+ ]
295
+ },
296
+ {
297
+ "cell_type": "code",
298
+ "execution_count": 9,
299
+ "metadata": {},
300
+ "outputs": [],
301
+ "source": [
302
+ "class Discriminator(nn.Module):\n",
303
+ " def __init__(self, base_channels=96, n_classes = 38):\n",
304
+ " super().__init__()\n",
305
+ "\n",
306
+ " # Adding the class information\n",
307
+ " self.shared_emb = nn.utils.spectral_norm(nn.Embedding(n_classes, 16 * base_channels))\n",
308
+ "\n",
309
+ " self.d_blocks = nn.Sequential(\n",
310
+ " DResidualBlock(3, base_channels, downsample=True, use_preactivation=False),\n",
311
+ " AttentionBlock(base_channels),\n",
312
+ "\n",
313
+ " DResidualBlock(base_channels, 2 * base_channels, downsample=True, use_preactivation=True),\n",
314
+ " AttentionBlock(2 * base_channels),\n",
315
+ "\n",
316
+ " DResidualBlock(2 * base_channels, 4 * base_channels, downsample=True, use_preactivation=True),\n",
317
+ " AttentionBlock(4 * base_channels),\n",
318
+ "\n",
319
+ " DResidualBlock(4 * base_channels, 8 * base_channels, downsample=True, use_preactivation=True),\n",
320
+ " AttentionBlock(8 * base_channels),\n",
321
+ "\n",
322
+ " DResidualBlock(8 * base_channels, 16 * base_channels, downsample=True, use_preactivation=True),\n",
323
+ " AttentionBlock(16 * base_channels),\n",
324
+ "\n",
325
+ " DResidualBlock(16 * base_channels, 16 * base_channels, downsample=False, use_preactivation=True),\n",
326
+ " AttentionBlock(16 * base_channels),\n",
327
+ "\n",
328
+ " nn.ReLU(inplace=True)\n",
329
+ " )\n",
330
+ "\n",
331
+ " self.proj_o = nn.utils.spectral_norm(nn.Linear(16 * base_channels, 1))\n",
332
+ "\n",
333
+ " def forward(self, x, y=None):\n",
334
+ " h = self.d_blocks(x)\n",
335
+ " h = torch.sum(h, dim=[2,3])\n",
336
+ "\n",
337
+ " # Class unconditional output\n",
338
+ " uncond_out = self.proj_o(h)\n",
339
+ " if y is None:\n",
340
+ " return uncond_out\n",
341
+ " \n",
342
+ " # Class conditional output\n",
343
+ " cond_out = torch.sum(self.shared_emb(y) * h, dim=1, keepdim=True)\n",
344
+ " return uncond_out + cond_out\n"
345
+ ]
346
+ },
347
+ {
348
+ "cell_type": "code",
349
+ "execution_count": null,
350
+ "metadata": {},
351
+ "outputs": [],
352
+ "source": [
353
+ "device = 'cuda'\n",
354
+ "\n",
355
+ "# Initialize models\n",
356
+ "base_channels = 96\n",
357
+ "z_dim = 120\n",
358
+ "n_classes = 38 \n",
359
+ "shared_dim = 128\n",
360
+ "generator = Generator(base_channels=base_channels, bottom_width=4, z_dim=z_dim, shared_dim=shared_dim, n_classes=n_classes).to(device)\n",
361
+ "discriminator = Discriminator(base_channels=base_channels, n_classes=n_classes).to(device)\n",
362
+ "\n",
363
+ "# Initialize weights orthogonally\n",
364
+ "for module in generator.modules():\n",
365
+ " if (isinstance(module, nn.Conv2d) or isinstance(module, nn.Linear) or isinstance(module, nn.Embedding)):\n",
366
+ " nn.init.orthogonal_(module.weight)\n",
367
+ "for module in discriminator.modules():\n",
368
+ " if (isinstance(module, nn.Conv2d) or isinstance(module, nn.Linear) or isinstance(module, nn.Embedding)):\n",
369
+ " nn.init.orthogonal_(module.weight)\n",
370
+ "\n",
371
+ "# Initialize optimizers\n",
372
+ "g_optimizer = torch.optim.Adam(generator.parameters(), lr=1e-4, betas=(0.0, 0.999), eps=1e-6)\n",
373
+ "d_optimizer = torch.optim.Adam(discriminator.parameters(), lr=4e-4, betas=(0.0, 0.999), eps=1e-6)"
374
+ ]
375
+ },
376
+ {
377
+ "cell_type": "code",
378
+ "execution_count": null,
379
+ "metadata": {},
380
+ "outputs": [],
381
+ "source": [
382
+ "from datasets import load_dataset\n",
383
+ "dataset = load_dataset('YashNagraj75/gochiUSA')"
384
+ ]
385
+ },
386
+ {
387
+ "cell_type": "code",
388
+ "execution_count": null,
389
+ "metadata": {},
390
+ "outputs": [],
391
+ "source": [
392
+ "from torch.utils.data import DataLoader\n",
393
+ "from torchvision import transforms\n",
394
+ "\n",
395
+ "\n",
396
+ "train_dataset = dataset['train']\n",
397
+ "test_dataset = dataset['test']\n",
398
+ "# Define transformations for both train and test datasets\n",
399
+ "transform = transforms.Compose([\n",
400
+ " transforms.Resize((128, 128)), # Resize to 128x128, adjust as needed\n",
401
+ " transforms.ToTensor(), # Convert to tensor\n",
402
+ " transforms.Normalize((0.5,), (0.5,)),\n",
403
+ "])\n",
404
+ "\n",
405
+ "def transform_examples(example):\n",
406
+ " example['image'] = transform(example['image'])\n",
407
+ " return example\n",
408
+ "\n",
409
+ "transformed_train_dataset = train_dataset.map(transform_examples)\n",
410
+ "transformed_test_dataset = test_dataset.map(transform_examples)\n",
411
+ "\n",
412
+ "transformed_train_dataset.set_format('torch', columns=['image', 'label'])\n",
413
+ "transformed_test_dataset.set_format('torch', columns=['image', 'label'])\n",
414
+ "# DataLoader for training and testing\n",
415
+ "train_dataloader = DataLoader(transformed_train_dataset, batch_size=38, shuffle=True) \n",
416
+ "test_dataloader = DataLoader(transformed_test_dataset, batch_size=38, shuffle=False) \n",
417
+ "# Example usage: iterate over the train DataLoader\n"
418
+ ]
419
+ },
420
+ {
421
+ "cell_type": "code",
422
+ "execution_count": null,
423
+ "metadata": {},
424
+ "outputs": [
425
+ {
426
+ "data": {
427
+ "text/plain": [
428
+ "torch.Tensor"
429
+ ]
430
+ },
431
+ "execution_count": 108,
432
+ "metadata": {},
433
+ "output_type": "execute_result"
434
+ }
435
+ ],
436
+ "source": [
437
+ "type(train_dataloader.dataset[0]['image'])"
438
+ ]
439
+ },
440
+ {
441
+ "cell_type": "code",
442
+ "execution_count": null,
443
+ "metadata": {},
444
+ "outputs": [
445
+ {
446
+ "ename": "NameError",
447
+ "evalue": "name 'train_dataloader' is not defined",
448
+ "output_type": "error",
449
+ "traceback": [
450
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
451
+ "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
452
+ "Cell \u001b[0;32mIn[1], line 17\u001b[0m\n\u001b[1;32m 13\u001b[0m display_step \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m500\u001b[39m\n\u001b[1;32m 16\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m epoch \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(num_epochs):\n\u001b[0;32m---> 17\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i, batch \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(\u001b[43mtrain_dataloader\u001b[49m):\n\u001b[1;32m 18\u001b[0m g_losses \u001b[38;5;241m=\u001b[39m []\n\u001b[1;32m 19\u001b[0m d_losses \u001b[38;5;241m=\u001b[39m []\n",
453
+ "\u001b[0;31mNameError\u001b[0m: name 'train_dataloader' is not defined"
454
+ ]
455
+ }
456
+ ],
457
+ "source": [
458
+ "import torchvision.utils as vutils\n",
459
+ "import matplotlib.pyplot as plt\n",
460
+ "\n",
461
+ "def critic_loss(real_output, fake_output):\n",
462
+ " return torch.mean(fake_output) - torch.mean(real_output)\n",
463
+ "\n",
464
+ "def generator_loss(fake_output):\n",
465
+ " return -torch.mean(fake_output)\n",
466
+ "\n",
467
+ "num_epochs = 200\n",
468
+ "batch_size = 38\n",
469
+ "n_critic = 5\n",
470
+ "display_step = 500\n",
471
+ "\n",
472
+ "\n",
473
+ "g_losses = []\n",
474
+ "d_losses = []\n",
475
+ "for epoch in range(num_epochs):\n",
476
+ " for i, batch in enumerate(train_dataloader):\n",
477
+ " real_images = batch[\"image\"].to(device)\n",
478
+ " real_labels = batch[\"label\"].to(device)\n",
479
+ "\n",
480
+ " \n",
481
+ " d_optimizer.zero_grad()\n",
482
+ " \n",
483
+ " real_output = discriminator(real_images, real_labels)\n",
484
+ " \n",
485
+ " z = torch.randn(batch_size, z_dim, device=device)\n",
486
+ " y = torch.arange(start=0, end=n_classes, device=device).long()\n",
487
+ " y_emb = generator.shared_emb(y)\n",
488
+ " fake_images = generator(z, y_emb)\n",
489
+ " fake_output = discriminator(fake_images.detach(), y)\n",
490
+ " \n",
491
+ " d_loss = critic_loss(real_output, fake_output)\n",
492
+ " d_losses += [d_loss.item()]\n",
493
+ " d_loss.backward()\n",
494
+ " d_optimizer.step()\n",
495
+ " \n",
496
+ " for p in discriminator.parameters():\n",
497
+ " p.data.clamp_(-0.01, 0.01)\n",
498
+ " \n",
499
+ " if i % n_critic == 0:\n",
500
+ " g_optimizer.zero_grad()\n",
501
+ " \n",
502
+ " fake_output = discriminator(fake_images, y)\n",
503
+ " g_loss = generator_loss(fake_output)\n",
504
+ " g_losses += [g_loss.item()]\n",
505
+ " \n",
506
+ " g_loss.backward()\n",
507
+ " g_optimizer.step()\n",
508
+ " \n",
509
+ " if i % 100 == 0:\n",
510
+ " print(f\"Epoch [{epoch}/{num_epochs}], Step [{i}/{len(train_dataloader)}], \"\n",
511
+ " f\"D Loss: {d_loss.item():.4f}, G Loss: {g_loss.item():.4f}\") # type: ignore\n",
512
+ " \n",
513
+ " # Display generated images every 500 steps\n",
514
+ " if i % display_step == 0:\n",
515
+ " with torch.no_grad():\n",
516
+ " fake_images_display = generator(z, y_emb).detach().cpu()\n",
517
+ " real_images_display = real_images.cpu()\n",
518
+ " \n",
519
+ " # Normalize images to [0, 1] for display\n",
520
+ " fake_images_display = (fake_images_display + 1) / 2\n",
521
+ " real_images_display = (real_images_display + 1) / 2\n",
522
+ " \n",
523
+ " # Plot real and fake images side by side\n",
524
+ " fig, ax = plt.subplots(1, 2, figsize=(12, 6))\n",
525
+ " \n",
526
+ " ax[0].imshow(vutils.make_grid(real_images_display, normalize=False).permute(1, 2, 0))\n",
527
+ " ax[0].set_title('Real Images')\n",
528
+ " ax[0].axis('off')\n",
529
+ " \n",
530
+ " ax[1].imshow(vutils.make_grid(fake_images_display, normalize=False).permute(1, 2, 0))\n",
531
+ " ax[1].set_title('Generated Images')\n",
532
+ " ax[1].axis('off')\n",
533
+ "\n",
534
+ " plt.figure(figsize=(10, 5))\n",
535
+ " plt.plot(g_losses, label=\"Generator Loss\")\n",
536
+ " plt.plot(d_losses, label=\"Discriminator Loss\")\n",
537
+ " plt.xlabel(\"Training Steps\")\n",
538
+ " plt.ylabel(\"Loss\")\n",
539
+ " plt.title(\"Generator and Discriminator Loss During Training\")\n",
540
+ " plt.legend()\n",
541
+ " plt.grid(True)\n",
542
+ " plt.show()\n",
543
+ " \n",
544
+ " plt.show()\n",
545
+ "\n",
546
+ " # Save checkpoints periodically\n",
547
+ " if epoch % 10 == 0:\n",
548
+ " torch.save(generator, f'generator_epoch_{epoch}.pth')\n",
549
+ " torch.save(discriminator, f'discriminator_epoch_{epoch}.pth')\n"
550
+ ]
551
+ },
552
+ {
553
+ "cell_type": "code",
554
+ "execution_count": null,
555
+ "metadata": {},
556
+ "outputs": [],
557
+ "source": []
558
+ }
559
+ ],
560
+ "metadata": {
561
+ "kernelspec": {
562
+ "display_name": "Python 3",
563
+ "language": "python",
564
+ "name": "python3"
565
+ },
566
+ "language_info": {
567
+ "codemirror_mode": {
568
+ "name": "ipython",
569
+ "version": 3
570
+ },
571
+ "file_extension": ".py",
572
+ "mimetype": "text/x-python",
573
+ "name": "python",
574
+ "nbconvert_exporter": "python",
575
+ "pygments_lexer": "ipython3",
576
+ "version": "3.12.3"
577
+ }
578
+ },
579
+ "nbformat": 4,
580
+ "nbformat_minor": 2
581
+ }