repos
/ ai-art master

ai-art

mirror archived upstream

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

2.6 KB · 87 lines · Python Raw History
 1import torch
 2import torch.nn as nn
 3
 4
 5class ActNorm(nn.Module):
 6    def __init__(self, num_features, logdet=False, affine=True, allow_reverse_init=False):
 7        assert affine
 8        super().__init__()
 9        self.logdet = logdet
10        self.loc = nn.Parameter(torch.zeros(1, num_features, 1, 1))
11        self.scale = nn.Parameter(torch.ones(1, num_features, 1, 1))
12        self.allow_reverse_init = allow_reverse_init
13
14        self.register_buffer('initialized', torch.tensor(0, dtype=torch.uint8))
15
16    def initialize(self, input):
17        with torch.no_grad():
18            flatten = input.permute(1, 0, 2, 3).contiguous().view(input.shape[1], -1)
19            mean = (
20                flatten.mean(1)
21                .unsqueeze(1)
22                .unsqueeze(2)
23                .unsqueeze(3)
24                .permute(1, 0, 2, 3)
25            )
26            std = (
27                flatten.std(1)
28                .unsqueeze(1)
29                .unsqueeze(2)
30                .unsqueeze(3)
31                .permute(1, 0, 2, 3)
32            )
33
34            self.loc.data.copy_(-mean)
35            self.scale.data.copy_(1 / (std + 1e-6))
36
37    def forward(self, input, reverse=False):
38        if reverse:
39            return self.reverse(input)
40        if len(input.shape) == 2:
41            input = input[:,:,None,None]
42            squeeze = True
43        else:
44            squeeze = False
45
46        _, _, height, width = input.shape
47
48        if self.training and self.initialized.item() == 0:
49            self.initialize(input)
50            self.initialized.fill_(1)
51
52        h = self.scale * (input + self.loc)
53
54        if squeeze:
55            h = h.squeeze(-1).squeeze(-1)
56
57        if self.logdet:
58            log_abs = torch.log(torch.abs(self.scale))
59            logdet = height*width*torch.sum(log_abs)
60            logdet = logdet * torch.ones(input.shape[0]).to(input)
61            return h, logdet
62
63        return h
64
65    def reverse(self, output):
66        if self.training and self.initialized.item() == 0:
67            if not self.allow_reverse_init:
68                raise RuntimeError(
69                    "Initializing ActNorm in reverse direction is "
70                    "disabled by default. Use allow_reverse_init=True to enable."
71                )
72            else:
73                self.initialize(output)
74                self.initialized.fill_(1)
75
76        if len(output.shape) == 2:
77            output = output[:,:,None,None]
78            squeeze = True
79        else:
80            squeeze = False
81
82        h = output / self.scale - self.loc
83
84        if squeeze:
85            h = h.squeeze(-1).squeeze(-1)
86        return h