Yash Nagraj commited on
Commit ·
931841c
1
Parent(s): ef356ee
Add training code
Browse files- 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 |
+
}
|