Art generation with VQGAN + CLIP in Docker, with a simple web UI for anyone with a GPU. A simplified and expanded take on Kevin Costa's work.
aiai-artclipdockerdocker-composegpuhandcodedimagenetpythonpytorchtorchtorchvisionvqganvqgan-clip
1import torch
2import torch.nn as nn
3
4import numpy as np
5
6from core.taming.utils import Normalize, nonlinearity
7
8from core.taming.modules.diffusion import AttnBlock, ResnetBlock, Upsample
9
10
11class Decoder(nn.Module):
12 def __init__(self, *, ch, out_ch, ch_mult=(1, 2, 4, 8), num_res_blocks,
13 attn_resolutions, dropout=0.0, resamp_with_conv=True, in_channels,
14 resolution, z_channels, give_pre_end=False, **ignorekwargs):
15 super().__init__()
16 self.ch = ch
17 self.temb_ch = 0
18 self.num_resolutions = len(ch_mult)
19 self.num_res_blocks = num_res_blocks
20 self.resolution = resolution
21 self.in_channels = in_channels
22 self.give_pre_end = give_pre_end
23
24 # compute in_ch_mult, block_in and curr_res at lowest res
25 # in_ch_mult = (1,)+tuple(ch_mult)
26 block_in = ch * ch_mult[self.num_resolutions - 1]
27 curr_res = resolution // 2**(self.num_resolutions - 1)
28 self.z_shape = (1, z_channels, curr_res, curr_res)
29 print("Working with z of shape {} = {} dimensions.".format(
30 self.z_shape, np.prod(self.z_shape)))
31
32 # z to block_in
33 self.conv_in = torch.nn.Conv2d(z_channels, block_in, kernel_size=3, stride=1, padding=1)
34
35 # middle
36 self.mid = nn.Module()
37 self.mid.block_1 = ResnetBlock(
38 in_channels=block_in, out_channels=block_in, temb_channels=self.temb_ch, dropout=dropout
39 )
40 self.mid.attn_1 = AttnBlock(block_in)
41 self.mid.block_2 = ResnetBlock(
42 in_channels=block_in, out_channels=block_in, temb_channels=self.temb_ch, dropout=dropout
43 )
44
45 # upsampling
46 self.up = nn.ModuleList()
47 for i_level in reversed(range(self.num_resolutions)):
48 block = nn.ModuleList()
49 attn = nn.ModuleList()
50 block_out = ch * ch_mult[i_level]
51 for i_block in range(self.num_res_blocks + 1):
52 block.append(ResnetBlock(in_channels=block_in,
53 out_channels=block_out,
54 temb_channels=self.temb_ch,
55 dropout=dropout))
56 block_in = block_out
57 if curr_res in attn_resolutions:
58 attn.append(AttnBlock(block_in))
59 up = nn.Module()
60 up.block = block
61 up.attn = attn
62 if i_level != 0:
63 up.upsample = Upsample(block_in, resamp_with_conv)
64 curr_res = curr_res * 2
65 self.up.insert(0, up) # prepend to get consistent order
66
67 # end
68 self.norm_out = Normalize(block_in)
69 self.conv_out = torch.nn.Conv2d(block_in, out_ch, kernel_size=3, stride=1, padding=1)
70
71 def forward(self, z):
72 # assert z.shape[1:] == self.z_shape[1:]
73 self.last_z_shape = z.shape
74
75 # timestep embedding
76 temb = None
77
78 # z to block_in
79 h = self.conv_in(z)
80
81 # middle
82 h = self.mid.block_1(h, temb)
83 h = self.mid.attn_1(h)
84 h = self.mid.block_2(h, temb)
85
86 # upsampling
87 for i_level in reversed(range(self.num_resolutions)):
88 for i_block in range(self.num_res_blocks + 1):
89 h = self.up[i_level].block[i_block](h, temb)
90 if len(self.up[i_level].attn) > 0:
91 h = self.up[i_level].attn[i_block](h)
92 if i_level != 0:
93 h = self.up[i_level].upsample(h)
94
95 # end
96 if self.give_pre_end:
97 return h
98
99 h = self.norm_out(h)
100 h = nonlinearity(h)
101 h = self.conv_out(h)
102 return h