├── .gitignore ├── 2ndDLCAT ├── GAN.py └── classification.py ├── Appendix └── A.py ├── DeepLearningBootcampwithPyTorch.pdf ├── LICENSE ├── README.md ├── class_materials ├── cycleGAN │ ├── networks.py │ └── pipeline.py └── pix2pix │ ├── models.py │ ├── pipeline.py │ └── train.py ├── classification ├── DenseNet │ └── models.py ├── ResidualNetwork │ ├── CIFAR10_models.py │ ├── CIFAR10_pipeline.py │ └── CIFAR_10_train.py ├── SEResidualNet │ ├── CIFAR10_models.py │ ├── CIFAR10_pipeline.py │ └── CIFAR10_train.py └── plain │ ├── MNIST_models.py │ ├── MNIST_test.py │ └── MNIST_train.py └── generation ├── CycleGAN ├── networks.py ├── options.py ├── pipeline.py ├── train.py └── utils.py ├── DCGAN ├── models.py └── train.py ├── GAN ├── CIFAR10_models.py ├── CIFAR10_test.py ├── CIFAR10_train.py ├── MNIST_models.py ├── MNIST_test.py └── MNIST_train.py ├── SGAN ├── models.py └── train.py └── cGAN ├── MNIST_models.py └── MNIST_train.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /2ndDLCAT/GAN.py: -------------------------------------------------------------------------------- 1 | # Run this cell to mount your Google Drive. 2 | # from google.colab import drive 3 | # drive.mount('/content/drive') 4 | 5 | import torch 6 | import torch.nn as nn 7 | from torchvision.datasets import MNIST 8 | from torchvision.transforms import ToTensor 9 | from torchvision.utils import save_image 10 | from tqdm import tqdm 11 | 12 | DEVICE = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu:0') 13 | 14 | 15 | # Model definition 16 | class Generator(nn.Module): 17 | def __init__(self): 18 | super(Generator, self).__init__() 19 | model = [nn.Linear(in_features=100, out_features=128), 20 | nn.ReLU(inplace=True), 21 | nn.Dropout(0.5)] 22 | model += [nn.Linear(in_features=128, out_features=256), 23 | nn.ReLU(inplace=True), 24 | nn.Dropout(0.5)] 25 | model += [nn.Linear(in_features=256, out_features=28 * 28), 26 | nn.Sigmoid()] 27 | 28 | self.model = nn.Sequential(*model) 29 | 30 | def forward(self, x): 31 | return self.model(x) 32 | 33 | 34 | class Discriminator(nn.Module): 35 | def __init__(self): 36 | super(Discriminator, self).__init__() 37 | model = [nn.Linear(28 * 28, 256), 38 | nn.ReLU(inplace=True)] 39 | model += [nn.Linear(256, 128), 40 | nn.ReLU(inplace=True), 41 | nn.Dropout(0.5)] 42 | model += [nn.Linear(128, 1), 43 | nn.Sigmoid()] 44 | 45 | self.model = nn.Sequential(*model) 46 | 47 | def forward(self, x): 48 | return self.model(x) 49 | 50 | 51 | dataset = MNIST(root='.', 52 | transform=ToTensor(), 53 | download=True, 54 | train=True) 55 | 56 | data_loader = torch.utils.data.DataLoader(dataset=dataset, 57 | shuffle=True, 58 | batch_size=1) 59 | 60 | G = Generator().to(DEVICE) 61 | D = Discriminator().to(DEVICE) 62 | 63 | print(G) 64 | print(D) 65 | 66 | G_optim = torch.optim.Adam(params=G.parameters(), lr=0.0002, betas=(0.5, 0.9)) 67 | D_optim = torch.optim.Adam(params=D.parameters(), lr=0.0002, betas=(0.5, 0.9)) 68 | 69 | total_step = 0 70 | for epoch in range(10): 71 | for real, _ in tqdm(data_loader): 72 | total_step += 1 73 | 74 | z = torch.rand(real.shape[0], 100).to(DEVICE) 75 | 76 | fake = G(z) 77 | real = real.view(real.shape[0], -1).to(DEVICE) 78 | 79 | fake_score = D(fake.detach()) 80 | real_score = D(real) 81 | 82 | D_loss = -torch.mean(torch.log(real_score + 1e-8) + torch.log(1 - fake_score + 1e-8)) 83 | D_optim.zero_grad() 84 | D_loss.backward() 85 | D_optim.step() 86 | 87 | fake_score = D(fake) 88 | 89 | G_loss = -torch.mean(torch.log(fake_score + 1e-8)) 90 | G_optim.zero_grad() 91 | G_loss.backward() 92 | G_optim.step() 93 | 94 | if total_step % 100 == 0: 95 | save_image(fake.view(fake.shape[0], 1, 28, 28), 96 | '{}_fake.png'.format(epoch + 1), 97 | nrow=1, 98 | normalize=True, range=(0, 1)) 99 | 100 | save_image(real.view(real.shape[0], 1, 28, 28), 101 | '{}_real.png'.format(epoch + 1), 102 | nrow=1, 103 | normalize=True, range=(0, 1)) 104 | 105 | torch.save(G.state_dict(), 'G.pt') 106 | 107 | # Below is for testing model. 108 | # G = Generator() 109 | # G.load_state_dict(torch.load('./G.pt')) 110 | # 111 | # z = torch.rand(16, 100) 112 | # fake = G(z) 113 | # save_image(fake.view(fake.shape[0], 1, 28, 28), 114 | # "./test_fake.png", 115 | # nrow=4, 116 | # normalize=True, 117 | # range=(0, 1)) 118 | 119 | 120 | -------------------------------------------------------------------------------- /2ndDLCAT/classification.py: -------------------------------------------------------------------------------- 1 | # Run this cell to mount your Google Drive. 2 | # from google.colab import drive 3 | # drive.mount('/content/drive') 4 | 5 | import torch 6 | import torch.nn as nn 7 | import torch.nn.functional as F 8 | from torchvision.datasets import MNIST 9 | from torchvision.transforms import Compose, ToTensor, Normalize 10 | from torch.utils.data import DataLoader 11 | 12 | 13 | class MLP(nn.Module): 14 | def __init__(self): 15 | super(MLP, self).__init__() 16 | self.layer1 = nn.Linear(784, 256) 17 | self.layer2 = nn.Linear(256, 128) 18 | self.layer3 = nn.Linear(128, 10) 19 | 20 | def forward(self, x): 21 | x = F.relu(self.layer1(x)) 22 | x = F.relu(self.layer2(x)) 23 | x = self.layer3(x) 24 | return x 25 | 26 | 27 | print(torch.cuda.is_available()) 28 | DEVICE = torch.device('cuda:0' if torch.cuda.is_available() else "cpu:0") 29 | BATCH_SIZE = 64 30 | EPOCHS = 5 31 | 32 | transform = Compose([ToTensor(), Normalize(mean=[0.5], std=[0.5])]) 33 | dataset = MNIST(root='.', 34 | download=True, 35 | transform=transform, 36 | train=True) 37 | 38 | dataloader = DataLoader(dataset=dataset, 39 | batch_size=64, 40 | shuffle=True) 41 | 42 | model = MLP().to(DEVICE) 43 | print(model) 44 | 45 | criterion = nn.CrossEntropyLoss() 46 | 47 | optim = torch.optim.SGD(params=model.parameters(), lr=0.01) 48 | 49 | total_step = 0 50 | list_acc = list() 51 | list_loss = list() 52 | for epoch in range(EPOCHS): 53 | for input, label in dataloader: 54 | total_step += 1 55 | 56 | input = input.view(input.shape[0], -1).to(DEVICE) 57 | label = label.to(DEVICE) 58 | output = model(input) 59 | loss = criterion(output, label) 60 | 61 | optim.zero_grad() 62 | loss.backward() 63 | optim.step() 64 | 65 | estimation = torch.argmax(output, dim=1) 66 | n_correct_answers = torch.sum(torch.eq(estimation, label)) 67 | acc = (float(n_correct_answers) / BATCH_SIZE) * 100 68 | 69 | list_acc.append(acc) 70 | list_loss.append(loss.item()) 71 | 72 | if total_step % 10 == 0: 73 | print("Total step: {:d}, Loss: {:.3f}, Acc: {:.3f}" 74 | .format(total_step, loss.item(), acc)) 75 | 76 | torch.save(model.state_dict(), './classification_model.pt') 77 | 78 | 79 | import matplotlib.pyplot as plt 80 | 81 | fig, axs = plt.subplots(nrows=1, ncols=2, figsize=[12, 6]) 82 | fig.suptitle("Loss & Accuracy graph") 83 | 84 | axs[0].plot(range(total_step), list_loss, linestyle='--', label='Loss') 85 | axs[0].set_ylabel("Loss") 86 | axs[0].set_xlabel("Iteration") 87 | axs[0].grid(True) 88 | 89 | axs[1].plot(range(total_step), list_acc, linestyle='--', label='Accuracy') 90 | axs[1].set_ylabel("Accuracy(%)") 91 | axs[1].set_xlabel("Iteration") 92 | axs[1].grid(True) 93 | 94 | plt.show() 95 | 96 | # Below is for testing model. 97 | 98 | # transform = Compose([ToTensor(), Normalize(mean=[0.5], std=[0.5])]) 99 | # dataset = MNIST(root='.', train=False, transform=transform) 100 | # data_loader = DataLoader(dataset, batch_size=BATCH_SIZE) 101 | # 102 | # model = MLP() 103 | # model.load_state_dict(torch.load('./classification_model.pt')) 104 | # 105 | # total_correct_answers = 0 106 | # with torch.no_grad(): 107 | # for input, label in data_loader: 108 | # input = input.view(input.shape[0], -1) 109 | # output = model(input) 110 | # estimation = torch.argmax(output, dim=1) 111 | # n_correct_answers = torch.sum(torch.eq(estimation, label)) 112 | # total_correct_answers += n_correct_answers 113 | # 114 | # acc = float(total_correct_answers) / len(dataset) * 100 115 | # print("Acc: {:.3f}%".format(acc)) 116 | -------------------------------------------------------------------------------- /Appendix/A.py: -------------------------------------------------------------------------------- 1 | class Calculator: 2 | def __init__(self): # __init__ function is called "constructor" as every time you make an instance of Calculator, 3 | # codes written in the constructor will be implemented. 4 | self.value = 0 5 | 6 | def add(self, n): 7 | self.value += n 8 | print("After addition:", self.value) 9 | 10 | def subtract(self, n): 11 | self.value -= n 12 | print("After subtraction:", self.value) 13 | 14 | def multiply(self, n): 15 | self.value *= n 16 | print("After multiplication:", self.value) 17 | 18 | def divide(self, n): 19 | if n == 0: 20 | print("You can't divide with 0.") 21 | return 22 | 23 | else: 24 | self.value /= n 25 | print("After division", self.value) 26 | 27 | def reset(self): 28 | self.value = 0 29 | print("reset value to", self.value) 30 | 31 | 32 | print("Calculator a...") 33 | a = Calculator() # Make an instance of Calculator class. You need parenthesis after class name to do this. 34 | 35 | a.add(5) # Call add function defined in Calculator class. 36 | a.divide(0) 37 | a.divide(5) 38 | 39 | print() 40 | print("Calculator b...") 41 | b = Calculator() # Make another instance of Calculator class. This does not share value with calculator a. 42 | 43 | b.add(1) 44 | b.multiply(100) 45 | b.reset() 46 | -------------------------------------------------------------------------------- /DeepLearningBootcampwithPyTorch.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noelshin/Deep-Learning-Bootcamp-with-PyTorch/dc048975aa5576f2e88b3583cbc78b9590d8c323/DeepLearningBootcampwithPyTorch.pdf -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Noel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deep-Learning-Bootcamp-with-PyTorch 2 | Deep learning introduction for beginners with PyTorch 3 | -------------------------------------------------------------------------------- /class_materials/cycleGAN/networks.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | 3 | 4 | class Discriminator(nn.Module): 5 | def __init__(self): 6 | super(Discriminator, self).__init__() 7 | act = nn.LeakyReLU(0.2, inplace=True) 8 | in_channels = 3 9 | n_df = 64 10 | norm = nn.InstanceNorm2d 11 | 12 | network = [nn.Conv2d(in_channels, n_df, kernel_size=4, stride=2, padding=1), act] 13 | network += [nn.Conv2d(n_df, 2 * n_df, kernel_size=4, stride=2, padding=1), norm(2 * n_df), act] 14 | network += [nn.Conv2d(2 * n_df, 4 * n_df, kernel_size=4, stride=2, padding=1), norm(4 * n_df), act] 15 | network += [nn.Conv2d(4 * n_df, 8 * n_df, kernel_size=4, padding=1), norm(8 * n_df), act] 16 | network += [nn.Conv2d(8 * n_df, 1, 4, padding=1)] 17 | self.network = nn.Sequential(*network) 18 | 19 | def forward(self, x): 20 | return self.network(x) 21 | 22 | 23 | class Generator(nn.Module): 24 | def __init__(self): 25 | super(Generator, self).__init__() 26 | act = nn.ReLU(inplace=True) 27 | in_channels = 3 28 | out_channels = 3 29 | 30 | n_gf = 64 31 | n_RB = 6 32 | norm = nn.InstanceNorm2d 33 | # 128 x 128 34 | network = [nn.ReflectionPad2d(3), nn.Conv2d(in_channels, n_gf, kernel_size=7), norm(n_gf), act] 35 | network += [nn.Conv2d(n_gf, 2 * n_gf, kernel_size=3, stride=2, padding=1), norm(2 * n_gf), act] 36 | 37 | # 64 x 64 38 | # [i - k + 2 * p / s] + 1 39 | # [64 - 3 + 2 * 1 / s ] + 1 >> [63 / 2] + 1 >> 32 40 | network += [nn.Conv2d(2 * n_gf, 4 * n_gf, kernel_size=3, stride=2, padding=1), norm(4 * n_gf), act] 41 | 42 | for i in range(n_RB): 43 | network += [ResidualBlock(4 * n_gf)] 44 | 45 | network += [nn.ConvTranspose2d(4 * n_gf, 2 * n_gf, 3, stride=2, padding=1, output_padding=1), norm(2 * n_gf), 46 | act] 47 | network += [nn.ConvTranspose2d(4 * n_gf, 2 * n_gf, 3, stride=2, padding=1, output_padding=1), norm(2 * n_gf), 48 | act] 49 | network += [nn.ReflectionPad2d(3), nn.Conv2d(n_gf, out_channels, 7), nn.Tanh()] 50 | self.network = nn.Sequential(*network) 51 | 52 | def forward(self, x): 53 | return self.network(x) 54 | 55 | 56 | class ResidualBlock(nn.Module): 57 | def __init__(self, n_ch): 58 | super(ResidualBlock, self).__init__() 59 | act = nn.ReLU(inplace=True) 60 | norm = nn.InstanceNorm2d 61 | 62 | block = [nn.ReflectionPad2d(1), nn.Conv2d(n_ch, n_ch, 3), norm(n_ch), act] 63 | block += [nn.ReflectionPad2d(1), nn.Conv2d(n_ch, n_ch, 3), norm(n_ch)] 64 | self.block = nn.Sequential(*block) 65 | 66 | def forward(self, x): 67 | return x + self.block(x) 68 | 69 | 70 | def weights_init(module): 71 | if isinstance(module, nn.Conv2d) or isinstance(module, nn.ConvTranspose2d): 72 | module.weight.detach().normal_(mean=0., std=0.02) 73 | -------------------------------------------------------------------------------- /class_materials/cycleGAN/pipeline.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | from random import random 4 | from torch.utils.data import Dataset 5 | from torchvision.transforms import Compose, Normalize, RandomHorizontalFlip, Resize, ToTensor 6 | from PIL import Image 7 | 8 | 9 | class CustomDataset(Dataset): 10 | def __init__(self, is_train=True): 11 | dataset_name = "my_dataset" 12 | dir_datasets = "./datasets" 13 | 14 | if is_train: 15 | dir_A = os.path.join(dir_datasets, dataset_name, "Train", 'A') 16 | dir_B = os.path.join(dir_datasets, dataset_name, "Train", 'B') 17 | self.list_paths_A, self.list_paths_B = sorted(os.listdir(dir_A)), sorted(os.listdir(dir_B)) 18 | self.dir_A, self.dir_B = dir_A, dir_B 19 | 20 | else: 21 | dir_A = os.path.join(dir_datasets, dataset_name, "Test", 'A') 22 | self.list_paths_A = sorted(os.listdir(dir_A)) 23 | self.dir_A = dir_A 24 | 25 | self.dataset_name = dataset_name 26 | self.load_size = 128 27 | self.is_train = is_train 28 | 29 | def __getitem__(self, index): 30 | transforms = [Resize((self.load_size, self.load_size), Image.BILINEAR)] 31 | transforms += [RandomHorizontalFlip()] if random() > 0.5 else [] 32 | transforms += [ToTensor(), Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])] 33 | image_A = Image.open(os.path.join(self.dir_A, self.list_paths_A[index])) 34 | 35 | if self.is_train: 36 | index_random = random.randint(0, len(self.list_paths_B) - 1) 37 | image_B = Image.open(os.path.join(self.dir_B, self.list_paths_B[index_random])) 38 | 39 | transforms = Compose(transforms) 40 | 41 | A = transforms(image_A) 42 | B = transforms(image_B) if self.is_train else 0 43 | 44 | return A, B 45 | 46 | def __len__(self): 47 | return len(self.list_paths_A) 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /class_materials/pix2pix/models.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | 4 | 5 | class Discriminator(nn.Module): 6 | def __init__(self): 7 | super(Discriminator, self).__init__() 8 | act = nn.LeakyReLU(0.2, inplace=True) 9 | norm = nn.BatchNorm2d 10 | n_df = 64 11 | 12 | model = [nn.Conv2d(3, n_df, kernel_size=4, stride=2, padding=1, bias=False), act] 13 | model += [nn.Conv2d(n_df, 2 * n_df, kernel_size=4, stride=2, padding=1, bias=False), norm(2 * n_df), act] 14 | model += [nn.Conv2d(2 * n_df, 4 * n_df, kernel_size=4, stride=2, padding=1, bias=False), norm(4 * n_df), act] 15 | model += [nn.Conv2d(4 * n_df, 8 * n_df, kernel_size=4, stride=2, padding=1, bias=False), norm(8 * n_df), act] 16 | model += [nn.Conv2d(8 * n_df, 1, kernel_size=4, bias=False), nn.Sigmoid()] 17 | self.model = nn.Sequential(*model) 18 | 19 | def forward(self, x): 20 | return self.model(x) 21 | 22 | 23 | class Generator(nn.Module): 24 | def __init__(self): 25 | super(Generator, self).__init__() 26 | act_down = nn.LeakyReLU(0.2, inplace=True) 27 | act_up = nn.ReLU(inplace=True) 28 | norm = nn.BatchNorm2d 29 | n_gf = 64 30 | 31 | # Input shape 1x128x128 32 | # Output shape 3x128x128 33 | self.down_1 = nn.Sequential(nn.Conv2d(1, n_gf, kernel_size=4, stride=2, padding=1, bias=False)) # 64x64x64 34 | self.down_2 = nn.Sequential(act_down, nn.Conv2d(n_gf, 2 * n_gf, kernel_size=4, stride=2, padding=1, bias=False), 35 | norm(2 * n_gf)) # 128x32x32 36 | self.down_3 = nn.Sequential(act_down, nn.Conv2d(2 * n_gf, 4 * n_gf, kernel_size=4, stride=2, padding=1, 37 | bias=False), norm(4 * n_gf)) # 256x16x16 38 | self.down_4 = nn.Sequential(act_down, nn.Conv2d(4 * n_gf, 8 * n_gf, kernel_size=4, stride=2, padding=1, 39 | bias=False), norm(8 * n_gf)) # 512x8x8 40 | self.down_5 = nn.Sequential(act_down, nn.Conv2d(8 * n_gf, 8 * n_gf, kernel_size=4, stride=2, padding=1, 41 | bias=False), norm(8 * n_gf)) 42 | self.down_6 = nn.Sequential(act_down, nn.Conv2d(8 * n_gf, 8 * n_gf, kernel_size=4, stride=2, padding=1, 43 | bias=False), norm(8 * n_gf)) 44 | self.down_up = nn.Sequential(act_down, 45 | nn.Conv2d(8 * n_gf, 8 * n_gf, kernel_size=4, stride=2, padding=1, bias=False), 46 | act_up, 47 | nn.ConvTranspose2d(8 * n_gf, 8 * n_gf, kernel_size=4, stride=2, padding=1, 48 | bias=False), norm(8 * n_gf), nn.Dropout2d(0.5)) # 2x2 49 | self.up_6 = nn.Sequential(act_up, 50 | nn.ConvTranspose2d(2 * 8 * n_gf, 8 * n_gf, kernel_size=4, stride=2, padding=1, 51 | bias=False), 52 | norm(8 * n_gf), nn.Dropout(0.5)) # 4x4 2 ** (8 - 6) 53 | self.up_5 = nn.Sequential(act_up, 54 | nn.ConvTranspose2d(2 * 8 * n_gf, 8 * n_gf, kernel_size=4, stride=2, padding=1, 55 | bias=False), 56 | norm(8 * n_gf), nn.Dropout(0.5)) # 8x8 57 | self.up_4 = nn.Sequential(act_up, 58 | nn.ConvTranspose2d(2 * 8 * n_gf, 4 * n_gf, kernel_size=4, stride=2, padding=1, 59 | bias=False), 60 | norm(4 * n_gf)) # 16x16 61 | self.up_3 = nn.Sequential(act_up, 62 | nn.ConvTranspose2d(2 * 4 * n_gf, 2 * n_gf, kernel_size=4, stride=2, padding=1, 63 | bias=False), 64 | norm(2 * n_gf)) # 32x32 65 | self.up_2 = nn.Sequential(act_up, 66 | nn.ConvTranspose2d(2 * 2 * n_gf, n_gf, kernel_size=4, stride=2, padding=1, 67 | bias=False), 68 | norm(n_gf)) # 64x64 69 | self.up_1 = nn.Sequential(act_up, 70 | nn.ConvTranspose2d(2 * n_gf, 3, kernel_size=4, stride=2, padding=1, bias=False), 71 | nn.Tanh()) 72 | 73 | def forward(self, x): 74 | intermediate_layers = [x] 75 | intermediate_layers += [self.down_1(intermediate_layers[-1])] 76 | intermediate_layers += [self.down_2(intermediate_layers[-1])] 77 | intermediate_layers += [self.down_3(intermediate_layers[-1])] 78 | intermediate_layers += [self.down_4(intermediate_layers[-1])] 79 | intermediate_layers += [self.down_5(intermediate_layers[-1])] 80 | intermediate_layers += [self.down_6(intermediate_layers[-1])] 81 | x = self.down_up(intermediate_layers[-1]) 82 | x = self.up_6(torch.cat((intermediate_layers[-1], x), dim=1)) 83 | x = self.up_5(torch.cat((intermediate_layers[-2], x), dim=1)) 84 | x = self.up_4(torch.cat((intermediate_layers[-3], x), dim=1)) 85 | x = self.up_3(torch.cat((intermediate_layers[-4], x), dim=1)) 86 | x = self.up_2(torch.cat((intermediate_layers[-5], x), dim=1)) 87 | x = self.up_1(torch.cat((intermediate_layers[-6], x), dim=1)) 88 | return x 89 | -------------------------------------------------------------------------------- /class_materials/pix2pix/pipeline.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | from torch.utils.data import Dataset 4 | from torchvision.transforms import Compose, RandomCrop, RandomHorizontalFlip, Grayscale, ToTensor, Normalize 5 | from PIL import Image 6 | 7 | 8 | class CustomDataset(Dataset): 9 | def __init__(self, root, crop_size=0, flip=False): 10 | super(CustomDataset, self).__init__() 11 | self.crop_size= crop_size 12 | self.flip = flip 13 | self.list_paths = os.listdir(root) 14 | self.root = root 15 | 16 | def __getitem__(self, index): 17 | image = Image.open(os.path.join(self.root, self.list_paths[index])) # Open image from the given path. 18 | 19 | # Get transform list 20 | list_transforms = list() 21 | if self.crop_size > 0: 22 | list_transforms.append(RandomCrop((self.crop_size, self.crop_size))) 23 | if self.flip: 24 | coin = random.random() > 0.5 25 | if coin: 26 | list_transforms.append(RandomHorizontalFlip()) 27 | transforms = Compose(list_transforms) 28 | 29 | image = transforms(image) # Implement common transform 30 | 31 | input_image = Grayscale(num_output_channels=1)(image) # For input image, we need to make it B/W. 32 | 33 | input_tensor, target_tensor = ToTensor()(input_image), ToTensor()(image) # Make input, target as torch.Tensor 34 | 35 | input_tensor = Normalize(mean=[0.5], std=[0.5])(input_tensor) # As the input tensor has only one channel, 36 | # Normalize parameters also have one value each. 37 | target_tensor = Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])(target_tensor) # As the target tensor has 38 | # three channels Normalize parameters also have three values each. 39 | 40 | return input_tensor, target_tensor 41 | 42 | def __len__(self): 43 | return len(self.list_paths) 44 | 45 | 46 | if __name__ == '__main__': 47 | from torch.utils.data import DataLoader 48 | import numpy as np 49 | 50 | dataset = CustomDataset('./datasets/Noel', crop_size=128, flip=True) 51 | dataloader = DataLoader(dataset=dataset, batch_size=1, shuffle=True) 52 | for input, target in dataloader: 53 | input_image_np = np.array(input[0, 0]) 54 | input_image_np -= input_image_np.min() 55 | input_image_np /= input_image_np.max() 56 | input_image_np *= 255.0 57 | input_image_np = input_image_np.astype(np.uint8) 58 | 59 | input_image = Image.fromarray(input_image_np, mode='L') 60 | input_image.show() 61 | 62 | target_image_np = np.array(target[0]) 63 | target_image_np -= target_image_np.min() 64 | target_image_np /= target_image_np.max() 65 | target_image_np *= 255.0 66 | target_image_np = target_image_np.astype(np.uint8) 67 | 68 | target_image = Image.fromarray(target_image_np.transpose(1, 2, 0), mode='RGB') 69 | target_image.show() 70 | break 71 | -------------------------------------------------------------------------------- /class_materials/pix2pix/train.py: -------------------------------------------------------------------------------- 1 | if __name__ == '__main__': 2 | import os 3 | import torch 4 | import torch.nn as nn 5 | from torch.utils.data import DataLoader 6 | from torchvision.utils import save_image 7 | from models import Discriminator, Generator 8 | from pipeline import CustomDataset 9 | 10 | DEVICE = torch.device("gpu:0" if torch.cuda.is_available() else "cpu:0") 11 | DIR_IMAGE = './pix2pix/checkpoints/IMAGE' 12 | DIR_MODEL = './pix2pix/checkpoints/MODEL' 13 | EPOCHS = 10 14 | EPOCH_SAVE = 5 15 | ITER_DISPLAY = 10 16 | 17 | os.makedirs(DIR_IMAGE) if not os.path.isdir(DIR_IMAGE) else None 18 | os.makedirs(DIR_MODEL) if not os.path.isdir(DIR_MODEL) else None 19 | 20 | dataset = CustomDataset(root='./datasets/Noel', crop_size=128, flip=True) 21 | data_loader = DataLoader(dataset=dataset, batch_size=1, shuffle=True, num_workers=1) 22 | 23 | D = Discriminator() 24 | G = Generator() 25 | 26 | GAN_Loss = nn.BCELoss() 27 | L1_Loss = nn.L1Loss() 28 | 29 | D_optim = torch.optim.Adam(D.parameters(), lr=2e-4, betas=(0.5, 0.999)) 30 | G_optim = torch.optim.Adam(G.parameters(), lr=2e-4, betas=(0.5, 0.999)) 31 | 32 | total_iter = 0 33 | for epoch in range(EPOCHS): 34 | for input, target in data_loader: 35 | total_iter += 1 36 | input, target = input.to(DEVICE), target.to(DEVICE) 37 | fake = G(input) 38 | 39 | valid_fake, valid_real = D(fake.detach()), D(target) 40 | fake_loss = GAN_Loss(valid_fake, torch.zeros_like(valid_fake).to(DEVICE)) 41 | real_loss = GAN_Loss(valid_real, torch.ones_like(valid_real).to(DEVICE)) 42 | D_loss = (fake_loss + real_loss) * 0.5 43 | 44 | D_optim.zero_grad() 45 | D_loss.backward() 46 | D_optim.step() 47 | 48 | valid_fake = D(fake) 49 | G_loss = GAN_Loss(valid_fake, torch.ones_like(valid_fake).to(DEVICE)) + L1_Loss(fake, target) 50 | 51 | G_optim.zero_grad() 52 | G_loss.backward() 53 | G_optim.step() 54 | 55 | print("Epoch: {}, Iter: {}, D_loss: {:.{prec}}, G_loss: {:.{prec}}" 56 | .format(epoch + 1, total_iter, D_loss.detach().item(), G_loss.detach().item(), prec=4)) 57 | 58 | if total_iter % ITER_DISPLAY == 0: 59 | save_image(fake.detach(), os.path.join(DIR_IMAGE, '{}_fake.png'.format(epoch + 1)), normalize=True, 60 | nrow=1) 61 | save_image(target.detach(), os.path.join(DIR_IMAGE, '{}_real.png'.format(epoch + 1)), normalize=True, 62 | nrow=1) 63 | 64 | if (epoch + 1) % EPOCH_SAVE == 0: 65 | torch.save(G.state_dict(), os.path.join(DIR_MODEL, '{}_G.pt'.format(epoch + 1))) 66 | torch.save(D.state_dict(), os.path.join(DIR_MODEL, '{}_D.pt'.format(epoch + 1))) 67 | 68 | -------------------------------------------------------------------------------- /classification/DenseNet/models.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | from torch.utils.checkpoint import checkpoint 4 | 5 | 6 | class DenseBlock(nn.Module): 7 | def __init__(self, n_layers, n_ch, growth_rate, bottleneck=True, efficient=True): 8 | super(DenseBlock, self).__init__() 9 | for i in range(n_layers): 10 | self.add_module('Dense_layer_{:d}'.format(i), 11 | DenseLayer(n_ch + i * growth_rate, growth_rate, bottleneck, efficient)) 12 | 13 | self.n_layers = n_layers 14 | 15 | def forward(self, x): 16 | for i in range(self.n_layers): 17 | x = getattr(self, 'Dense_layer_{:d}'.format(i))(x) 18 | return x 19 | 20 | 21 | class DenseLayer(nn.Module): 22 | def __init__(self, n_ch, growth_rate, bottleneck=True, efficient=True): 23 | super(DenseLayer, self).__init__() 24 | layer = [] 25 | if bottleneck: 26 | layer += [nn.BatchNorm2d(n_ch), 27 | nn.ReLU(inplace=True), 28 | nn.Conv2d(n_ch, 4 * growth_rate, 1, bias=False)] 29 | layer += [nn.BatchNorm2d(4 * growth_rate), 30 | nn.ReLU(inplace=True), 31 | nn.Conv2d(4 * growth_rate, growth_rate, 3, padding=1, bias=False)] 32 | else: 33 | layer += [nn.BatchNorm2d(n_ch), 34 | nn.ReLU(inplace=True), 35 | nn.Conv2d(n_ch, growth_rate, 3, padding=1, bias=False)] 36 | 37 | self.layer = nn.Sequential(*layer) 38 | 39 | self.efficient = efficient 40 | 41 | def function(self, *inputs): 42 | return self.layer(torch.cat(inputs, dim=1)) 43 | 44 | def forward(self, *inputs): 45 | if self.efficient and any(input.requires_grad for input in inputs): 46 | x = checkpoint(self.function, *inputs) 47 | else: 48 | x = self.layer(torch.cat(inputs, dim=1)) 49 | return torch.cat((*inputs, x), dim=1) 50 | 51 | 52 | class DenseNet(nn.Module): 53 | def __init__(self, depth, growth_rate, efficient=True, input_ch=3, n_classes=100): 54 | super(DenseNet, self).__init__() 55 | 56 | """ 57 | Before entering the first dense block, a convolution with 16 (or twice the growth rate for DenseNet-BC) output 58 | channel is performed on the input images. 59 | """ 60 | assert depth in [40, 100] 61 | n_layers = 6 if depth == 40 else 16 62 | init_ch = 16 63 | network = [nn.Conv2d(input_ch, init_ch, 3, padding=1, bias=False)] 64 | 65 | network += [DenseBlock(n_layers=n_layers, n_ch=init_ch, growth_rate=growth_rate, bottleneck=False, 66 | efficient=efficient)] 67 | n_ch = init_ch + growth_rate * n_layers 68 | 69 | network += [TransitionLayer(n_ch, compress_factor=1)] 70 | network += [DenseBlock(n_layers=n_layers, n_ch=n_ch, growth_rate=growth_rate, bottleneck=False, 71 | efficient=efficient)] 72 | n_ch = n_ch + growth_rate * n_layers 73 | 74 | network += [TransitionLayer(n_ch, compress_factor=1)] 75 | network += [DenseBlock(n_layers=n_layers, n_ch=growth_rate, growth_rate=growth_rate, bottleneck=False, 76 | efficient=efficient)] 77 | n_ch = n_ch + growth_rate * n_layers 78 | 79 | network += [nn.BatchNorm2d(n_ch), 80 | nn.ReLU(True), 81 | nn.AdaptiveAvgPool2d(1), 82 | View(-1), 83 | nn.Linear(n_ch, n_classes)] 84 | 85 | self.network = nn.Sequential(*network) 86 | print(self) 87 | 88 | def forward(self, x): 89 | return self.network(x) 90 | 91 | 92 | class DenseNetBC(nn.Module): 93 | def __init__(self, depth, growth_rate, efficient=True, ImageNet=False, input_ch=3, n_classes=100): 94 | super(DenseNetBC, self).__init__() 95 | """ 96 | Depth is one of [121, 169, 201, 265]. (Please note that DenseNet-264 in the paper is errata. It has to be 265 97 | including the last fc-layer.) 98 | """ 99 | init_ch = 2 * growth_rate 100 | if ImageNet: 101 | assert depth in [121, 169, 201, 265], "Choose among [121, 169, 201, 265]." 102 | assert n_classes == 1000, "ImageNet has 1000 classes. Check n_classes: {}.".format(n_classes) 103 | 104 | if depth == 121: 105 | list_n_layers = [6, 12, 24, 16] 106 | elif depth == 169: 107 | list_n_layers = [6, 12, 32, 32] 108 | elif depth == 201: 109 | list_n_layers = [6, 12, 48, 32] 110 | else: 111 | list_n_layers = [6, 12, 64, 48] 112 | 113 | network = [nn.Conv2d(input_ch, init_ch, 7, stride=2, padding=3, bias=False), 114 | nn.BatchNorm2d(init_ch), 115 | nn.ReLU(inplace=True), 116 | nn.MaxPool2d(kernel_size=3, stride=2)] 117 | 118 | network += [DenseBlock(list_n_layers[0], init_ch, growth_rate, bottleneck=True, efficient=efficient)] 119 | n_ch = init_ch + growth_rate * list_n_layers[0] 120 | 121 | network += [TransitionLayer(n_ch)] 122 | network += [DenseBlock(list_n_layers[1], n_ch // 2, growth_rate, bottleneck=True, efficient=efficient)] 123 | n_ch = n_ch // 2 + growth_rate * list_n_layers[1] 124 | 125 | network += [TransitionLayer(n_ch)] 126 | network += [DenseBlock(list_n_layers[2], n_ch // 2, growth_rate, bottleneck=True, efficient=efficient)] 127 | n_ch = n_ch // 2 + growth_rate * list_n_layers[2] 128 | 129 | network += [TransitionLayer(n_ch)] 130 | network += [DenseBlock(list_n_layers[3], n_ch // 2, growth_rate, bottleneck=True, efficient=efficient)] 131 | n_ch = n_ch //2 + growth_rate * list_n_layers[3] 132 | 133 | else: 134 | assert depth in [40, 100, 190, 250] 135 | n_layers = ((depth - 4) // 3) // 2 # Dividing 2 is because there are two weighted layers in one dense layer 136 | # in DenseNet BC, i.e. 1x1 and 3x3 convolutions. 137 | 138 | network = [nn.Conv2d(input_ch, init_ch, 3, padding=1, bias=False)] 139 | network += [DenseBlock(n_layers, init_ch, growth_rate, bottleneck=True, efficient=efficient)] 140 | n_ch = init_ch + growth_rate * n_layers 141 | 142 | network += [TransitionLayer(n_ch)] 143 | network += [DenseBlock(n_layers, n_ch // 2, growth_rate, bottleneck=True, efficient=efficient)] 144 | n_ch = n_ch // 2 + growth_rate * n_layers 145 | 146 | network += [TransitionLayer(n_ch)] 147 | network += [DenseBlock(n_layers, n_ch // 2, growth_rate, bottleneck=True, efficient=efficient)] 148 | n_ch = n_ch // 2 + growth_rate * n_layers 149 | 150 | network += [nn.BatchNorm2d(n_ch), 151 | nn.ReLU(True), 152 | nn.AdaptiveAvgPool2d(1), 153 | View(-1), 154 | nn.Linear(n_ch, n_classes)] 155 | 156 | self.network = nn.Sequential(*network) 157 | 158 | for module in self.modules(): 159 | if isinstance(module, nn.Conv2d): 160 | nn.init.kaiming_normal_(module.weight) 161 | 162 | elif isinstance(module, nn.BatchNorm2d): 163 | nn.init.constant_(module.weight, 1.0) 164 | nn.init.constant_(module.bias, 0.0) 165 | 166 | elif isinstance(module, nn.Linear): 167 | nn.init.constant_(module.bias, 0.0) 168 | 169 | print(self) 170 | print("# of params: {}".format(sum(p.numel() for p in self.parameters() if p.requires_grad))) 171 | 172 | def forward(self, x): 173 | return self.network(x) 174 | 175 | 176 | class TransitionLayer(nn.Module): 177 | def __init__(self, n_ch, compress_factor=0.5): 178 | super(TransitionLayer, self).__init__() 179 | layer = [nn.BatchNorm2d(n_ch), 180 | nn.ReLU(inplace=True), 181 | nn.Conv2d(n_ch, int(n_ch * compress_factor), kernel_size=1, bias=False), 182 | nn.AvgPool2d(kernel_size=2, stride=2)] 183 | self.layer = nn.Sequential(*layer) 184 | 185 | def forward(self, x): 186 | return self.layer(x) 187 | 188 | 189 | class View(nn.Module): 190 | def __init__(self, *shape): 191 | super(View, self).__init__() 192 | self.shape = shape 193 | 194 | def forward(self, x): 195 | return x.view(x.shape[0], *self.shape) 196 | 197 | 198 | if __name__ == '__main__': 199 | from ptflops import get_model_complexity_info 200 | densenet_bc = DenseNetBC(depth=100, growth_rate=12, n_classes=100, efficient=False) 201 | flops, params = get_model_complexity_info(densenet_bc, (3, 32, 32), as_strings=False, print_per_layer_stat=False) 202 | print("flops: {}, params: {}".format(flops, params)) 203 | -------------------------------------------------------------------------------- /classification/ResidualNetwork/CIFAR10_models.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | import torch.nn.functional as F 3 | 4 | 5 | class PlainNetwork(nn.Module): 6 | def __init__(self, n): 7 | super(PlainNetwork, self).__init__() 8 | act = nn.ReLU(inplace=True) 9 | norm = nn.BatchNorm2d 10 | 11 | network = [] 12 | network += [nn.ZeroPad2d(1), nn.Conv2d(3, 16, 3, bias=False), norm(16), act] 13 | for _ in range(2 * n): 14 | network += [nn.ZeroPad2d(1), nn.Conv2d(16, 16, 3, bias=False), norm(16), act] 15 | 16 | network += [nn.ZeroPad2d(1), nn.Conv2d(16, 32, 3, bias=False), norm(32), act] 17 | for _ in range(2 * n - 1): 18 | network += [nn.ZeroPad2d(1), nn.Conv2d(32, 32, 3, bias=False), norm(32), act] 19 | 20 | network += [nn.ZeroPad2d(1), nn.Conv2d(32, 64, 3, bias=False), norm(64), act] 21 | for _ in range(2 * n - 1): 22 | network += [nn.ZeroPad2d(1), nn.Conv2d(64, 64, 3, bias=False), norm(64), act] 23 | 24 | network += [nn.AdaptiveAvgPool2d(1), View(-1), nn.Linear(64, 10)] 25 | 26 | self.network = nn.Sequential(*network) 27 | 28 | self.apply(init_weight) 29 | 30 | print("Total parameters: {}".format(sum(p.numel() for p in self.parameters() if p.requires_grad))) 31 | 32 | def forward(self, x): 33 | return self.network(x) 34 | 35 | 36 | class ResidualNetwork(nn.Module): 37 | def __init__(self, n): 38 | super(ResidualNetwork, self).__init__() 39 | act = nn.ReLU(inplace=True) 40 | norm = nn.BatchNorm2d 41 | 42 | network = [] 43 | network += [nn.ZeroPad2d(1), nn.Conv2d(3, 16, 3, bias=False), norm(16), act] 44 | for _ in range(n): 45 | network += [ResidualBlock(16, 16)] 46 | 47 | network += [ResidualBlock(16, 32, first_conv_stride=2)] 48 | for _ in range(n - 1): 49 | network += [ResidualBlock(32, 32)] 50 | 51 | network += [ResidualBlock(32, 64, first_conv_stride=2)] 52 | for _ in range(n - 1): 53 | network += [ResidualBlock(64, 64)] 54 | 55 | network += [nn.AdaptiveAvgPool2d(1), View(-1), nn.Linear(64, 10)] 56 | 57 | self.network = nn.Sequential(*network) 58 | 59 | self.apply(init_weight) 60 | 61 | print("Total parameters: {}".format(sum(p.numel() for p in self.parameters() if p.requires_grad))) 62 | 63 | def forward(self, x): 64 | return self.network(x) 65 | 66 | 67 | class ResidualBlock(nn.Module): 68 | def __init__(self, input_ch, output_ch, first_conv_stride=1): 69 | super(ResidualBlock, self).__init__() 70 | act = nn.ReLU(inplace=True) 71 | norm = nn.BatchNorm2d 72 | pad = nn.ZeroPad2d 73 | 74 | block = [pad(1), nn.Conv2d(input_ch, output_ch, 3, stride=first_conv_stride, bias=False), norm(output_ch), act] 75 | block += [pad(1), nn.Conv2d(output_ch, output_ch, 3, bias=False), norm(output_ch)] 76 | 77 | if input_ch != output_ch: 78 | self.varying_size = True 79 | """ 80 | As far as I know, the original authors didn't mention about what down-sampling method they used in identity 81 | mapping. This can be max pooling or average pooling. Please give me an advice if anyone knows about this 82 | pooling layer. For now, I'll use max pooling layer. Also, when I pad along the channel dimension, I add zero 83 | entries behind (not front) original data. This, best of my knowledge, is also not mentioned whether front or 84 | behind (or may be half and half) the original data across channel dimension. But I believe this is not a big 85 | issue. 86 | """ 87 | side_block = [pad(1), nn.MaxPool2d(kernel_size=3, stride=2), 88 | nn.ConstantPad3d((0, 0, 0, 0, 0, output_ch - input_ch), value=0.)] 89 | self.side_block = nn.Sequential(*side_block) 90 | 91 | else: 92 | self.varying_size = False 93 | 94 | self.block = nn.Sequential(*block) 95 | 96 | def forward(self, x): 97 | if self.varying_size: 98 | return F.relu(self.side_block(x) + self.block(x)) 99 | 100 | else: 101 | return F.relu(x + self.block(x)) 102 | 103 | 104 | class View(nn.Module): 105 | def __init__(self, *shape): 106 | super(View, self).__init__() 107 | self.shape = shape 108 | 109 | def forward(self, x): 110 | return x.view(x.shape[0], *self.shape) 111 | 112 | 113 | def init_weight(module): 114 | if isinstance(module, nn.Conv2d): 115 | nn.init.kaiming_normal_(module.weight.detach(), mode='fan_out', nonlinearity='relu') 116 | 117 | elif isinstance(module, nn.BatchNorm2d): 118 | module.weight.detach().fill_(1.) 119 | module.bias.detach().fill_(0.) 120 | -------------------------------------------------------------------------------- /classification/ResidualNetwork/CIFAR10_pipeline.py: -------------------------------------------------------------------------------- 1 | import random 2 | import numpy as np 3 | from torch.utils.data import Dataset 4 | from torchvision.datasets import CIFAR10 5 | from torchvision.transforms import Compose, Lambda, ToTensor 6 | from PIL import Image 7 | import PIL 8 | 9 | 10 | class CustomCIFAR10(Dataset): 11 | def __init__(self, train=True): 12 | super(CustomCIFAR10, self).__init__() 13 | self.cifar10_train = CIFAR10(root='./datasets', train=train, download=True) 14 | 15 | # Get only images (i.e. without labels) 16 | tensors = list() 17 | for i in range(len(self.cifar10_train)): 18 | tensors.append(np.array(self.cifar10_train[i][0])) # Need to convert PIL.Image.Image to numpy.ndarray 19 | self.per_pixel_mean_grid = np.mean(tensors, axis=0).astype(np.float32) 20 | # Calculate per-pixel mean along the batch dimension 21 | 22 | if not train: 23 | self.cifar10_test = CIFAR10(root='./datasets', train=train, download=True) 24 | 25 | self.train = train 26 | 27 | def __getitem__(self, index): 28 | transforms = list() 29 | transforms.append(Lambda(self.__to_numpy)) # First convert PIL.Image.Image to numpy.ndarray. HxWxC 30 | transforms.append(Lambda(self.__per_pixel_subtraction_normalization)) # Subtract per-pixel mean 31 | 32 | if self.train: 33 | if random.random() > 0.5: 34 | transforms.append(Lambda(self.__horizontal_flip)) # Flip horizontally with 50:50 chance 35 | transforms.append(Lambda(self.__pad_and_random_crop)) # Pad 0 along H and W dims and randomly crop 36 | 37 | transforms.append(ToTensor()) # convert numpy.ndarray to torch.tensor (notice that HxWxC -> CxHxW) 38 | transforms = Compose(transforms) 39 | 40 | if self.train: 41 | return transforms(self.cifar10_train[index][0]), self.cifar10_train[index][1] 42 | else: 43 | return transforms(self.cifar10_test[index][0]), self.cifar10_test[index][1] 44 | 45 | def __len__(self): 46 | if self.train: 47 | return len(self.cifar10_train) 48 | else: 49 | return len(self.cifar10_test) 50 | 51 | def __per_pixel_subtraction_normalization(self, x): 52 | return (x - self.per_pixel_mean_grid) / 255. 53 | 54 | @staticmethod 55 | def __pad_and_random_crop(x): 56 | p = 4 # Original paper pad 4 on each side 57 | x = np.pad(x, ((p, p), (p, p), (0, 0)), mode='constant', constant_values=0) # Pad along H, W, and C dims 58 | y_index = random.randint(0, 2 * p - 1) 59 | x_index = random.randint(0, 2 * p - 1) 60 | x = x[y_index: y_index + 32, x_index: x_index + 32, :] # Crop 61 | return x 62 | 63 | @staticmethod 64 | def __horizontal_flip(x): 65 | return np.fliplr(x) 66 | 67 | @staticmethod 68 | def __to_numpy(x): 69 | assert isinstance(x, PIL.Image.Image) 70 | return np.array(x).astype(np.float32) 71 | -------------------------------------------------------------------------------- /classification/ResidualNetwork/CIFAR_10_train.py: -------------------------------------------------------------------------------- 1 | if __name__ == '__main__': 2 | import torch 3 | from torch.utils.data import DataLoader 4 | import torch.nn as nn 5 | from torch.optim.lr_scheduler import MultiStepLR 6 | from CIFAR10_pipeline import CustomCIFAR10 7 | from CIFAR10_models import PlainNetwork, ResidualNetwork 8 | 9 | BATCH_SIZE = 16 10 | 11 | device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu:0') 12 | 13 | dataset = CustomCIFAR10(train=True) 14 | data_loader = DataLoader(dataset=dataset, batch_size=BATCH_SIZE, num_workers=2, shuffle=True) 15 | # 128 of minibatch size was used for the paper. 16 | 17 | pnet = PlainNetwork(3).to(device) 18 | resnet = ResidualNetwork(3).to(device) 19 | 20 | criterion = nn.CrossEntropyLoss() 21 | 22 | optim_pnet = torch.optim.SGD(pnet.parameters(), lr=0.1, momentum=0.9, weight_decay=1e-4) 23 | optim_resnet = torch.optim.SGD(resnet.parameters(), lr=0.1, momentum=0.9, weight_decay=1e-4) 24 | scheduler_pnet = MultiStepLR(optim_pnet, milestones=[32000, 48000], gamma=0.1) 25 | scheduler_resnet = MultiStepLR(optim_resnet, milestones=[32000, 48000], gamma=0.1) 26 | 27 | iter_total = 0 28 | while iter_total < 640000: 29 | for input, label in data_loader: 30 | iter_total += 1 31 | input, label = input.to(device), label.to(device) 32 | 33 | output = pnet(input) 34 | 35 | loss = criterion(output, label) 36 | optim_resnet.zero_grad() 37 | loss.backward() 38 | optim_resnet.step() 39 | scheduler_resnet.step() 40 | 41 | n_correct_answers = torch.sum(torch.eq(output.argmax(dim=1), label)) 42 | 43 | print("Loss : {:.{prec}}, Acc : {:.{prec}}".format(loss.detach().item(), 44 | (float(n_correct_answers.item()) / BATCH_SIZE) * 100, prec=4)) 45 | 46 | if iter_total == 640000: 47 | break 48 | -------------------------------------------------------------------------------- /classification/SEResidualNet/CIFAR10_models.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | import torch.nn.functional as F 3 | 4 | 5 | class SEResidualNetwork(nn.Module): 6 | def __init__(self, n): 7 | super(SEResidualNetwork, self).__init__() 8 | act = nn.ReLU(inplace=True) 9 | norm = nn.BatchNorm2d 10 | 11 | network = [] 12 | network += [nn.ZeroPad2d(1), nn.Conv2d(3, 16, 3, bias=False), norm(16), act] 13 | for _ in range(n): 14 | network += [SEResidualBlock(16, 16)] 15 | 16 | network += [SEResidualBlock(16, 32, first_conv_stride=2)] 17 | for _ in range(n - 1): 18 | network += [SEResidualBlock(32, 32)] 19 | 20 | network += [SEResidualBlock(32, 64, first_conv_stride=2)] 21 | for _ in range(n - 1): 22 | network += [SEResidualBlock(64, 64)] 23 | 24 | network += [nn.AdaptiveAvgPool2d(1), View(-1), nn.Linear(64, 10)] 25 | 26 | self.network = nn.Sequential(*network) 27 | 28 | self.apply(init_weight) 29 | 30 | print("Total parameters: {}".format(sum(p.numel() for p in self.parameters() if p.requires_grad))) 31 | 32 | def forward(self, x): 33 | return self.network(x) 34 | 35 | 36 | class SEResidualBlock(nn.Module): 37 | def __init__(self, input_ch, output_ch, first_conv_stride=1): 38 | super(SEResidualBlock, self).__init__() 39 | act = nn.ReLU(inplace=True) 40 | norm = nn.BatchNorm2d 41 | pad = nn.ZeroPad2d 42 | 43 | block = [pad(1), nn.Conv2d(input_ch, output_ch, 3, stride=first_conv_stride, bias=False), norm(output_ch), act] 44 | block += [pad(1), nn.Conv2d(output_ch, output_ch, 3, bias=False), norm(output_ch), 45 | SqueezeExcitationBlock(output_ch, output_ch)] 46 | 47 | if input_ch != output_ch: 48 | self.varying_size = True 49 | """ 50 | As far as I know, the original authors didn't mention about what down-sampling method they used in identity 51 | mapping. This can be max pooling or average pooling. Please give me an advice if anyone knows about this 52 | pooling layer. For now, I'll use max pooling layer. Also, when I pad along the channel dimension, I add zero 53 | entries behind (not front) original data. This, best of my knowledge, is also not mentioned whether front or 54 | behind (or may be half and half) the original data across channel dimension. But I believe this is not a big 55 | issue. 56 | """ 57 | side_block = [pad(1), nn.MaxPool2d(kernel_size=3, stride=2), 58 | nn.ConstantPad3d((0, 0, 0, 0, 0, output_ch - input_ch), value=0.)] 59 | self.side_block = nn.Sequential(*side_block) 60 | 61 | else: 62 | self.varying_size = False 63 | 64 | self.block = nn.Sequential(*block) 65 | 66 | def forward(self, x): 67 | if self.varying_size: 68 | return F.relu(self.side_block(x) + self.block(x)) 69 | 70 | else: 71 | return F.relu(x + self.block(x)) 72 | 73 | 74 | class SqueezeExcitationBlock(nn.Module): 75 | def __init__(self, input_ch, output_ch, reduction_ratio=16, pooling='average'): 76 | super(SqueezeExcitationBlock, self).__init__() 77 | if pooling == 'average': 78 | pool = nn.AdaptiveAvgPool2d 79 | elif pooling == 'max': 80 | pool = nn.AdaptiveMaxPool2d 81 | else: 82 | raise NotImplementedError(print("Invalid pooling {}. Please choose among ['average', 'max']." 83 | .format(pooling))) 84 | 85 | block = [pool(1), View(-1), nn.Linear(input_ch, input_ch // reduction_ratio), nn.ReLU(inplace=True), 86 | nn.Linear(input_ch // reduction_ratio, output_ch), nn.Sigmoid(), View(output_ch, 1, 1)] 87 | self.block = nn.Sequential(*block) 88 | 89 | def forward(self, x): 90 | return x * self.block(x) 91 | 92 | 93 | class View(nn.Module): 94 | def __init__(self, *shape): 95 | super(View, self).__init__() 96 | self.shape = shape 97 | 98 | def forward(self, x): 99 | return x.view(x.shape[0], *self.shape) 100 | 101 | 102 | def init_weight(module): 103 | if isinstance(module, (nn.Conv2d, nn.Linear)): 104 | nn.init.kaiming_normal_(module.weight.detach(), mode='fan_out', nonlinearity='relu') 105 | 106 | elif isinstance(module, nn.BatchNorm2d): 107 | module.weight.detach().fill_(1.) 108 | module.bias.detach().fill_(0.) 109 | -------------------------------------------------------------------------------- /classification/SEResidualNet/CIFAR10_pipeline.py: -------------------------------------------------------------------------------- 1 | import random 2 | import numpy as np 3 | from torch.utils.data import Dataset 4 | from torchvision.datasets import CIFAR10 5 | from torchvision.transforms import Compose, Lambda, ToTensor 6 | from PIL import Image 7 | import PIL 8 | 9 | 10 | class CustomCIFAR10(Dataset): 11 | def __init__(self, train=True): 12 | super(CustomCIFAR10, self).__init__() 13 | self.cifar10_train = CIFAR10(root='./datasets', train=train, download=True) 14 | 15 | # Get only images (i.e. without labels) 16 | tensors = list() 17 | for i in range(len(self.cifar10_train)): 18 | tensors.append(np.array(self.cifar10_train[i][0])) # Need to convert PIL.Image.Image to numpy.ndarray 19 | self.per_pixel_mean_grid = np.mean(tensors, axis=0).astype(np.float32) 20 | # Calculate per-pixel mean along the batch dimension 21 | 22 | if not train: 23 | self.cifar10_test = CIFAR10(root='./datasets', train=train, download=True) 24 | 25 | self.train = train 26 | 27 | def __getitem__(self, index): 28 | transforms = list() 29 | transforms.append(Lambda(self.__to_numpy)) # First convert PIL.Image.Image to numpy.ndarray. HxWxC 30 | transforms.append(Lambda(self.__per_pixel_subtraction_normalization)) # Subtract per-pixel mean 31 | 32 | if self.train: 33 | if random.random() > 0.5: 34 | transforms.append(Lambda(self.__horizontal_flip)) # Flip horizontally with 50:50 chance 35 | transforms.append(Lambda(self.__pad_and_random_crop)) # Pad 0 along H and W dims and randomly crop 36 | 37 | transforms.append(ToTensor()) # convert numpy.ndarray to torch.tensor (notice that HxWxC -> CxHxW) 38 | transforms = Compose(transforms) 39 | 40 | if self.train: 41 | return transforms(self.cifar10_train[index][0]), self.cifar10_train[index][1] 42 | else: 43 | return transforms(self.cifar10_test[index][0]), self.cifar10_test[index][1] 44 | 45 | def __len__(self): 46 | if self.train: 47 | return len(self.cifar10_train) 48 | else: 49 | return len(self.cifar10_test) 50 | 51 | def __per_pixel_subtraction_normalization(self, x): 52 | return (x - self.per_pixel_mean_grid) / 255. 53 | 54 | @staticmethod 55 | def __pad_and_random_crop(x): 56 | p = 4 # Original paper pad 4 on each side 57 | x = np.pad(x, ((p, p), (p, p), (0, 0)), mode='constant', constant_values=0) # Pad along H, W, and C dims 58 | y_index = random.randint(0, 2 * p - 1) 59 | x_index = random.randint(0, 2 * p - 1) 60 | x = x[y_index: y_index + 32, x_index: x_index + 32, :] # Crop 61 | return x 62 | 63 | @staticmethod 64 | def __horizontal_flip(x): 65 | return np.fliplr(x) 66 | 67 | @staticmethod 68 | def __to_numpy(x): 69 | assert isinstance(x, PIL.Image.Image) 70 | return np.array(x).astype(np.float32) 71 | -------------------------------------------------------------------------------- /classification/SEResidualNet/CIFAR10_train.py: -------------------------------------------------------------------------------- 1 | if __name__ == '__main__': 2 | import torch 3 | from torch.utils.data import DataLoader 4 | import torch.nn as nn 5 | from torch.optim.lr_scheduler import MultiStepLR 6 | from CIFAR10_pipeline import CustomCIFAR10 7 | from CIFAR10_models import SEResidualNetwork 8 | 9 | BATCH_SIZE = 16 10 | 11 | device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu:0') 12 | 13 | dataset = CustomCIFAR10(train=True) 14 | data_loader = DataLoader(dataset=dataset, batch_size=BATCH_SIZE, num_workers=2, shuffle=True) 15 | # 128 of minibatch size was used for the paper. 16 | 17 | se_resnet = SEResidualNetwork(3).to(device) 18 | 19 | criterion = nn.CrossEntropyLoss() 20 | 21 | optim_se_resnet = torch.optim.SGD(se_resnet.parameters(), lr=0.1, momentum=0.9, weight_decay=1e-4) 22 | scheduler_se_resnet = MultiStepLR(optim_se_resnet, milestones=[32000, 48000], gamma=0.1) 23 | 24 | iter_total = 0 25 | while iter_total < 640000: 26 | for input, label in data_loader: 27 | iter_total += 1 28 | input, label = input.to(device), label.to(device) 29 | 30 | output = se_resnet(input) 31 | 32 | loss = criterion(output, label) 33 | optim_se_resnet.zero_grad() 34 | loss.backward() 35 | optim_se_resnet.step() 36 | scheduler_se_resnet.step() 37 | 38 | n_correct_answers = torch.sum(torch.eq(output.argmax(dim=1), label)) 39 | 40 | print("Loss : {:.{prec}}, Acc : {:.{prec}}".format(loss.detach().item(), 41 | (float(n_correct_answers.item()) / BATCH_SIZE) * 100, prec=4)) 42 | 43 | if iter_total == 640000: 44 | break 45 | -------------------------------------------------------------------------------- /classification/plain/MNIST_models.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | import torch.nn.functional as F 3 | 4 | 5 | class CNN(nn.Module): 6 | def __init__(self): 7 | super(CNN, self).__init__() 8 | self.conv1 = nn.Conv2d(in_channels=1, out_channels=64, kernel_size=3, stride=2, padding=1) 9 | self.conv2 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=2, padding=1) 10 | self.conv3 = nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=2, padding=1) 11 | self.fc = nn.Linear(in_features=4 * 4 * 256, out_features=10) 12 | 13 | def forward(self, x): 14 | x = F.relu(self.conv1(x)) 15 | x = F.relu(self.conv2(x)) 16 | x = F.relu(self.conv3(x)) 17 | x = x.view(x.shape[0], -1) 18 | x = self.fc(x) 19 | return x 20 | 21 | 22 | class MLP(nn.Module): 23 | def __init__(self): 24 | super(MLP, self).__init__() 25 | self.linear1 = nn.Linear(in_features=28 * 28, out_features=256) 26 | self.linear2 = nn.Linear(in_features=256, out_features=128) 27 | self.linear3 = nn.Linear(in_features=128, out_features=64) 28 | self.linear4 = nn.Linear(in_features=64, out_features=10) 29 | 30 | def forward(self, x): 31 | x = F.relu(self.linear1(x)) 32 | x = F.relu(self.linear2(x)) 33 | x = F.relu(self.linear3(x)) 34 | x = self.linear4(x) 35 | return x 36 | -------------------------------------------------------------------------------- /classification/plain/MNIST_test.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch.utils.data import DataLoader 3 | from torchvision import transforms 4 | from torchvision import datasets 5 | if __name__ == '__main__': 6 | MODEL = 'MLP' 7 | 8 | # Construct input pipeline 9 | transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize(mean=[0.5], std=[0.5])]) 10 | dataset = datasets.MNIST(root='./datasets', train=False, transform=transform, download=True) 11 | data_loader = DataLoader(dataset=dataset, batch_size=1, shuffle=False, num_workers=0) 12 | 13 | # Define model 14 | if MODEL == 'CNN': 15 | from MNIST_models import CNN 16 | model = CNN() 17 | elif MODEL == 'MLP': 18 | from MNIST_models import MLP 19 | model = MLP() 20 | else: 21 | raise NotImplementedError("Invalid model type {}. Choose in [CNN, MLP]".format(MODEL)) 22 | 23 | # Load the trained model 24 | state_dict = torch.load('MNIST_model_{}.pt'.format(MODEL)).state_dict() 25 | model.load_state_dict(state_dict) 26 | 27 | # Test loop 28 | total_step = 0 29 | nb_correct_answers = 0 30 | for i, data in enumerate(data_loader): 31 | total_step += 1 32 | input_tensor, label = data[0], data[1] 33 | input_tensor = input_tensor.view(input_tensor.shape[0], -1) if MODEL == 'MLP' else input_tensor 34 | classification_results = model(input_tensor) 35 | nb_correct_answers += torch.eq(classification_results.argmax(), label).item() 36 | print("Average acc.: {} %.".format(nb_correct_answers / len(dataset) * 100)) 37 | 38 | -------------------------------------------------------------------------------- /classification/plain/MNIST_train.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | from torch.utils.data import DataLoader 4 | from torchvision import datasets 5 | from torchvision import transforms 6 | import matplotlib.pyplot as plt 7 | import time 8 | 9 | 10 | if __name__ == '__main__': 11 | # Set hyper parameters 12 | BATCH_SIZE = 32 13 | BETA1 = 0.5 14 | BETA2 = 0.99 15 | CLASS_NUMBER = 10 16 | EPOCHS = 1 17 | EPSILON = 1e-8 18 | LR = 2e-4 19 | MODEL = 'MLP' # choose among CNN or MLP 20 | REPORT_FREQ = 100 21 | 22 | # Construct input pipeline 23 | transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize(mean=[0.5], std=[0.5])]) 24 | dataset = datasets.MNIST(root='./datasets', train=True, transform=transform, download=True) 25 | data_loader = DataLoader(dataset=dataset, batch_size=BATCH_SIZE, num_workers=0, shuffle=True) 26 | 27 | # Define model, loss function, optimizer 28 | if MODEL == 'CNN': 29 | from MNIST_models import CNN 30 | model = CNN() 31 | elif MODEL == 'MLP': 32 | from MNIST_models import MLP 33 | model = MLP() 34 | else: 35 | raise NotImplementedError("Invalid model type {}. Choose in [CNN, MLP]".format(MODEL)) 36 | 37 | cel = nn.CrossEntropyLoss() 38 | optim = torch.optim.Adam(model.parameters(), betas=(BETA1, BETA2), lr=LR, eps=EPSILON) 39 | 40 | # Training loop 41 | start_time = time.time() 42 | 43 | total_step = 0 44 | list_loss = list() 45 | list_acc = list() 46 | for epoch in range(EPOCHS): 47 | for i, data in enumerate(data_loader): 48 | total_step += 1 49 | input_tensor, label = data[0], data[1] 50 | 51 | input_tensor = input_tensor.view(BATCH_SIZE, -1) if MODEL == 'MLP' else input_tensor 52 | 53 | total_step += 1 54 | classification_result = model(input_tensor) 55 | loss = cel(classification_result, label) 56 | list_loss.append(loss.detach().item()) 57 | 58 | optim.zero_grad() 59 | loss.backward() 60 | optim.step() 61 | 62 | nb_correct_answer = torch.eq(classification_result.argmax(dim=1), label).sum() 63 | acc = float(nb_correct_answer.item()) / BATCH_SIZE * 100 64 | list_acc.append(acc) 65 | 66 | if total_step % REPORT_FREQ == 0: 67 | print("Epoch {} Acc. is {}%".format(epoch + 1, acc)) 68 | print("Training time taken: {} seconds".format(time.time() - start_time)) 69 | 70 | # Save the trained model 71 | torch.save(model, './basics/classification/MNIST_model_{}.pt'.format(MODEL)) 72 | 73 | # Visualize and analyze results 74 | plt.figure() 75 | plt.plot(range(len(list_loss)), list_loss, linestyle='--', color='g') 76 | plt.title('Classification_MNIST_{}'.format(MODEL)) 77 | plt.xlabel('Iteration') 78 | plt.ylabel('Loss') 79 | plt.savefig('./basics/classification/MNIST_Loss_graph_{}.png'.format(MODEL)) 80 | plt.show() 81 | 82 | plt.figure() 83 | plt.plot(range(len(list_acc)), list_acc, linestyle='--', color='b') 84 | plt.title('Classification_MNIST_{}'.format(MODEL)) 85 | plt.xlabel('Iteration') 86 | plt.ylabel('Accuracy (%)') 87 | plt.savefig('./basics/classification/MNIST_Accuracy_graph_{}.png'.format(MODEL)) 88 | plt.show() 89 | -------------------------------------------------------------------------------- /generation/CycleGAN/networks.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | 3 | 4 | class Discriminator(nn.Module): 5 | def __init__(self, opt): 6 | super(Discriminator, self).__init__() 7 | """ 8 | 1. The authors used 70x70 (receptive field size) patch GAN. The structures can be varied with your own purpose. 9 | 2. Discriminator does NOT take a condition image which is fed to the generator. 10 | 3. No normalization layer is applied after the first convolution layer. 11 | 4. As the authors used LSGAN for stable training, no sigmoid activation is attached at the last layer. 12 | """ 13 | act = nn.LeakyReLU(0.2, inplace=True) 14 | in_channels = opt.out_channels 15 | n_df = opt.n_df 16 | norm = nn.InstanceNorm2d 17 | 18 | network = [nn.Conv2d(in_channels, n_df, 4, stride=2, padding=1), act] 19 | network += [nn.Conv2d(n_df, 2 * n_df, 4, stride=2, padding=1), norm(2 * n_df), act] 20 | network += [nn.Conv2d(2 * n_df, 4 * n_df, 4, stride=2, padding=1), norm(4 * n_df), act] 21 | network += [nn.Conv2d(4 * n_df, 8 * n_df, 4, padding=1), norm(8 * n_df), act] 22 | network += [nn.Conv2d(8 * n_df, 1, 4, padding=1)] 23 | self.network = nn.Sequential(*network) 24 | 25 | def forward(self, x): 26 | return self.network(x) 27 | 28 | 29 | class Generator(nn.Module): 30 | def __init__(self, opt): 31 | super(Generator, self).__init__() 32 | """ 33 | 1. The authors used n_RB = 6 for 128x128 and 9 for 256x256 or higher image size. You can change n_RB in the 34 | options.py. 35 | 2. No normalization layer is applied after the last convolution layer. 36 | """ 37 | act = nn.ReLU(inplace=True) 38 | in_channels = opt.in_channels 39 | out_channels = opt.out_channels 40 | n_gf = opt.n_gf 41 | n_RB = opt.n_RB 42 | norm = nn.InstanceNorm2d 43 | 44 | network = [nn.ReflectionPad2d(3), nn.Conv2d(in_channels, n_gf, 7), norm(n_gf), act] 45 | network += [nn.Conv2d(n_gf, 2 * n_gf, 3, stride=2, padding=1), norm(2 * n_gf), act] 46 | network += [nn.Conv2d(2 * n_gf, 4 * n_gf, 3, stride=2, padding=1), norm(4 * n_gf), act] 47 | 48 | for block in range(n_RB): 49 | network += [ResidualBlock(4 * n_gf)] 50 | 51 | network += [nn.ConvTranspose2d(4 * n_gf, 2 * n_gf, 3, stride=2, padding=1, output_padding=1), norm(2 * n_gf), 52 | act] 53 | network += [nn.ConvTranspose2d(2 * n_gf, n_gf, 3, stride=2, padding=1, output_padding=1), norm(n_gf), act] 54 | network += [nn.ReflectionPad2d(3), nn.Conv2d(n_gf, out_channels, 7), nn.Tanh()] 55 | self.network = nn.Sequential(*network) 56 | 57 | def forward(self, x): 58 | return self.network(x) 59 | 60 | 61 | class ResidualBlock(nn.Module): 62 | def __init__(self, n_ch): 63 | super(ResidualBlock, self).__init__() 64 | act = nn.ReLU(inplace=True) 65 | norm = nn.InstanceNorm2d 66 | block = [nn.ReflectionPad2d(1), nn.Conv2d(n_ch, n_ch, 3), norm(n_ch), act] 67 | block += [nn.ReflectionPad2d(1), nn.Conv2d(n_ch, n_ch, 3), norm(n_ch)] 68 | self.block = nn.Sequential(*block) 69 | 70 | def forward(self, x): 71 | return x + self.block(x) 72 | 73 | 74 | def update_lr(init_lr, old_lr, n_epoch_decay, *optims): 75 | delta_lr = init_lr / n_epoch_decay 76 | new_lr = old_lr - delta_lr 77 | 78 | for optim in optims: 79 | for param_group in optim.param_groups: 80 | param_group['lr'] = new_lr 81 | 82 | print("Learning rate has been updated from {} to {}.".format(old_lr, new_lr)) 83 | 84 | return new_lr 85 | 86 | 87 | def weights_init(module): 88 | if isinstance(module, nn.Conv2d) or isinstance(module, nn.ConvTranspose2d): 89 | module.weight.detach().normal_(mean=0., std=0.02) 90 | -------------------------------------------------------------------------------- /generation/CycleGAN/options.py: -------------------------------------------------------------------------------- 1 | import os 2 | import argparse 3 | 4 | 5 | class BaseOption(object): 6 | def __init__(self): 7 | self.args = argparse.ArgumentParser() 8 | self.args.add_argument('--debug', action='store_true', default=True) 9 | self.args.add_argument('--gpu_id', type=str, default='0', help='-1 for using CPU.') 10 | 11 | self.args.add_argument('--batch_size', type=int, default=1) 12 | self.args.add_argument('--dataset_name', type=str, default='facades') 13 | self.args.add_argument('--dir_checkpoints', type=str, default='./checkpoints') 14 | self.args.add_argument('--dir_datasets', type=str, default='./datasets') 15 | self.args.add_argument('--in_channels', type=int, default=3, 16 | help='Number of input channels in the generator.') 17 | self.args.add_argument('--load_size', type=int, default=256) 18 | self.args.add_argument('--n_df', type=int, default=64, 19 | help='Nb of output channels of the first layer in the discriminator.') 20 | self.args.add_argument('--n_gf', type=int, default=32, 21 | help='Nb of output channels of the first layer in the generator.') 22 | self.args.add_argument('--n_RB', type=int, default=9, help='Nb of residual blocks in the generator.') 23 | self.args.add_argument('--n_workers', type=int, default=2, help='Nb of cpu threads to load data.') 24 | self.args.add_argument('--out_channels', type=int, default=3, 25 | help='Number of output channels in the generator.') 26 | 27 | def parse(self): 28 | args = self.args.parse_args() 29 | args.dir_image_train = os.path.join(args.dir_checkpoints, args.dataset_name, 'Train', 'Image') 30 | args.dir_image_test = os.path.join(args.dir_checkpoints, args.dataset_name, 'Test', 'Image') 31 | args.dir_model = os.path.join(args.dir_checkpoints, args.dataset_name, 'Train', 'Model') 32 | os.makedirs(args.dir_image_train, exist_ok=True) 33 | os.makedirs(args.dir_image_test, exist_ok=True) 34 | os.makedirs(args.dir_model, exist_ok=True) 35 | args.file_log = os.path.join(args.dir_model, 'options.txt') 36 | dict_opt = vars(args) 37 | 38 | if args.is_train: 39 | with open(args.file_log, 'wt') as log: 40 | print("-" * 50, 'Options', "-" * 50) 41 | for k, v in dict_opt.items(): 42 | print("{}: {}".format(k, v)) 43 | log.write('{}, {}\n'.format(k, v)) 44 | print("-" * 100) 45 | 46 | return args 47 | 48 | 49 | class TrainOption(BaseOption): 50 | def __init__(self): 51 | super(TrainOption, self).__init__() 52 | self.args.add_argument('--is_train', action='store_true', default=True) 53 | 54 | self.args.add_argument('--beta_1', type=float, default=0.5, help='Adam optimizer param.') 55 | self.args.add_argument('--beta_2', type=float, default=0.999, help='Adam optimizer param.') 56 | self.args.add_argument('--iter_display', type=int, default=100, help='frequency you want to see training images' 57 | 'in iteration.') 58 | self.args.add_argument('--iter_report', type=int, default=10, help='frequency you want to be reported losses.') 59 | self.args.add_argument('--iter_val', type=int, default=100, help='frequency you want to see validation images' 60 | 'in iteration.') 61 | self.args.add_argument('--epoch_decay', type=int, default=100, help='epoch where learning rate starts to' 62 | ' decay.') 63 | self.args.add_argument('--epoch_save', type=int, default=20, help='frequency you want to save models in epoch.') 64 | self.args.add_argument('--lambda_cycle', type=int, default=10, help='weight for cycle consistency loss.') 65 | self.args.add_argument('--lr', type=float, default=2e-4, help='Initial learning rate.') 66 | self.args.add_argument('--n_buffer_images', type=int, default=50, help='how many images the model stores for' 67 | 'discriminator updates') 68 | self.args.add_argument('--n_epochs', type=int, default=200) 69 | self.args.add_argument('--val_during_training', action='store_true', default=True) 70 | 71 | 72 | class TestOption(BaseOption): 73 | def __init__(self): 74 | super(TestOption, self).__init__() 75 | self.args.add_argument('--is_train', action='store_true', default=False) 76 | -------------------------------------------------------------------------------- /generation/CycleGAN/pipeline.py: -------------------------------------------------------------------------------- 1 | import os 2 | from os.path import join 3 | import random 4 | from torch.utils.data import Dataset 5 | from torchvision.transforms import Compose, Normalize, RandomHorizontalFlip, Resize, ToTensor 6 | from PIL import Image 7 | 8 | 9 | class CustomDataset(Dataset): 10 | def __init__(self, opt, mode): 11 | super(CustomDataset, self).__init__() 12 | dataset_name = opt.dataset_name 13 | dir_datasets = opt.dir_datasets 14 | 15 | if mode == 'train': 16 | dir_input = join(dir_datasets, dataset_name, 'Train', 'A') 17 | dir_real = join(dir_datasets, dataset_name, 'Train', 'B') 18 | self.list_paths_input, self.list_paths_real = sorted(os.listdir(dir_input)), sorted(os.listdir(dir_real)) 19 | self.dir_input, self.dir_real = dir_input, dir_real 20 | 21 | elif mode == 'val': 22 | dir_input = join(dir_datasets, dataset_name, 'Test', 'A') 23 | dir_real = join(dir_datasets, dataset_name, 'Test', 'B') 24 | self.list_paths_input, self.list_paths_real = sorted(os.listdir(dir_input)), sorted(os.listdir(dir_real)) 25 | self.dir_input, self.dir_real = dir_input, dir_real 26 | 27 | elif mode == 'val_A2B': 28 | dir_input = join(dir_datasets, dataset_name, 'Test', 'A') 29 | self.list_paths_input = sorted(os.listdir(dir_input)) 30 | self.dir_input = dir_input 31 | 32 | elif mode == 'val_B2A': 33 | dir_input = join(dir_datasets, dataset_name, 'Test', 'B') 34 | self.list_paths_input = sorted(os.listdir(dir_input)) 35 | self.dir_input = dir_input 36 | 37 | elif mode == 'test': 38 | dir_input = join(dir_datasets, dataset_name, 'Test', 'A') 39 | self.list_paths_input = sorted(os.listdir(dir_input)) 40 | self.dir_input = dir_input 41 | 42 | else: 43 | raise NotImplemented("Invalid mode {}. Choose among 'train', 'val', and 'test'.".format(mode)) 44 | 45 | self.dataset_name = opt.dataset_name 46 | self.load_size = opt.load_size 47 | self.mode = mode 48 | 49 | def __getitem__(self, index): 50 | transforms = list() 51 | 52 | if self.dataset_name == 'facades': 53 | transforms += [Resize((self.load_size, self.load_size), Image.BILINEAR)] 54 | transforms += [RandomHorizontalFlip()] if random.random() > 0.5 else [] 55 | transforms += [ToTensor(), Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])] 56 | 57 | image_input = Image.open(join(self.dir_input, self.list_paths_input[index])).convert('RGB') 58 | 59 | if self.mode == 'train' or self.mode == 'val': 60 | index_random = random.randint(0, len(self.list_paths_real) - 1) 61 | image_real = Image.open(join(self.dir_real, self.list_paths_real[index_random])).convert('RGB') 62 | 63 | else: 64 | transforms += [Resize((self.load_size, self.load_size), Image.BILINEAR)] 65 | transforms += [ToTensor(), Normalize(mean=[0.5], std=[0.5])] 66 | 67 | image_input = Image.open(join(self.dir_input, self.list_paths_input[index])).convert('L') 68 | 69 | if self.mode == 'train' or self.mode == 'val': 70 | index_random = random.randint(0, len(self.list_paths_real) - 1) 71 | image_real = Image.open(join(self.dir_real, self.list_paths_real[index_random])).convert('L') 72 | 73 | transforms = Compose(transforms) 74 | input = transforms(image_input) 75 | real = transforms(image_real) if self.mode == 'train' or self.mode == 'val' else 0 76 | 77 | return input, real 78 | 79 | def __len__(self): 80 | return len(self.list_paths_input) 81 | 82 | 83 | if __name__ == '__main__': 84 | dir_image = './datasets/facades/Train/Input' 85 | list_paths = os.listdir(dir_image) 86 | list_sizes = [] 87 | for path in list_paths: 88 | size = Image.open(os.path.join(dir_image, path)).size 89 | print(size) 90 | list_sizes.append(size) 91 | -------------------------------------------------------------------------------- /generation/CycleGAN/train.py: -------------------------------------------------------------------------------- 1 | if __name__ == '__main__': 2 | import os 3 | from os.path import join 4 | import torch 5 | import torch.nn as nn 6 | from torch.utils.data import DataLoader 7 | from torchvision.utils import save_image 8 | from options import TrainOption 9 | from pipeline import CustomDataset 10 | from networks import Discriminator, Generator, update_lr, weights_init 11 | from utils import ImageBuffer 12 | from datetime import datetime 13 | 14 | opt = TrainOption().parse() 15 | os.environ['CUDA_VISIBLE_DEVICES'] = opt.gpu_id 16 | batch_size = opt.batch_size 17 | beta_1, beta_2 = opt.beta_1, opt.beta_2 18 | debug = opt.debug 19 | dir_image_train = opt.dir_image_train 20 | dir_model = opt.dir_model 21 | epoch_decay = opt.epoch_decay 22 | epoch_save = opt.epoch_save 23 | iter_display = opt.iter_display 24 | iter_report = opt.iter_report 25 | iter_val = opt.iter_val 26 | n_epochs = opt.n_epochs 27 | lambda_cycle = opt.lambda_cycle 28 | lr = opt.lr 29 | n_buffer_images = opt.n_buffer_images 30 | num_workers = opt.n_workers 31 | val_during_training = opt.val_during_training 32 | if val_during_training: 33 | from options import TestOption 34 | opt_test = TestOption().parse() 35 | dir_image_test = opt.dir_image_test 36 | 37 | device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu:0') 38 | 39 | dataset = CustomDataset(opt, 'train') 40 | data_loader = DataLoader(dataset=dataset, batch_size=batch_size, num_workers=num_workers, shuffle=True) 41 | 42 | D_A = Discriminator(opt).apply(weights_init).to(device) 43 | D_B = Discriminator(opt).apply(weights_init).to(device) 44 | G_A = Generator(opt).apply(weights_init).to(device) 45 | G_B = Generator(opt).apply(weights_init).to(device) 46 | print(D_A) 47 | print(G_A) 48 | 49 | optim_D = torch.optim.Adam(list(D_A.parameters()) + list(D_B.parameters()), lr=lr, betas=(beta_1, beta_2)) 50 | optim_G = torch.optim.Adam(list(G_A.parameters()) + list(G_B.parameters()), lr=lr, betas=(beta_1, beta_2)) 51 | 52 | loss_GAN = nn.MSELoss() # Note that the authors used LSGAN. 53 | loss_cycle = nn.L1Loss() # Cycle consistency loss. 54 | 55 | image_buffer_A = ImageBuffer(n_images=n_buffer_images) 56 | image_buffer_B = ImageBuffer(n_images=n_buffer_images) 57 | 58 | st = datetime.now() 59 | iter_total = 0 60 | for epoch in range(n_epochs): 61 | if (epoch + 1) > epoch_decay: 62 | lr = update_lr(opt.lr, lr, n_epochs - epoch_decay, optim_D, optim_G) 63 | for A, B in data_loader: 64 | iter_total += 1 65 | A, B = A.to(device), B.to(device) 66 | 67 | fake_B = G_A(A) 68 | fake_A = G_B(B) 69 | 70 | val_fake_B = D_B(image_buffer_B(fake_B.detach())) 71 | val_real_B = D_B(B) 72 | val_fake_A = D_A(image_buffer_A(fake_A.detach())) 73 | val_real_A = D_A(A) 74 | 75 | loss_D = 0 76 | loss_D += loss_GAN(val_fake_B, torch.zeros_like(val_fake_B).to(device)) 77 | loss_D += loss_GAN(val_fake_A, torch.zeros_like(val_fake_A).to(device)) 78 | loss_D += loss_GAN(val_real_B, torch.ones_like(val_real_B).to(device)) 79 | loss_D += loss_GAN(val_real_A, torch.ones_like(val_real_A).to(device)) 80 | loss_D *= 0.5 81 | 82 | optim_D.zero_grad() 83 | loss_D.backward() 84 | optim_D.step() 85 | 86 | val_fake_B = D_B(fake_B) 87 | val_fake_A = D_A(fake_A) 88 | cycle_A = G_B(fake_B) 89 | cycle_B = G_A(fake_A) 90 | 91 | loss_G = 0 92 | loss_G += loss_GAN(val_fake_B, torch.ones_like(val_fake_B).to(device)) 93 | loss_G += loss_GAN(val_fake_A, torch.ones_like(val_fake_A).to(device)) 94 | loss_G += lambda_cycle * (loss_cycle(cycle_A, A) + loss_cycle(cycle_B, B)) 95 | 96 | optim_G.zero_grad() 97 | loss_G.backward() 98 | optim_G.step() 99 | 100 | if iter_total % iter_report == 0: 101 | print("Epoch: {}, Iter: {}, Loss D: {:.{prec}}, Loss G: {:.{prec}}" 102 | .format(epoch + 1, iter_total, loss_D.detach().item(), loss_G.detach().item(), prec=4)) 103 | 104 | if iter_total % iter_display == 0: 105 | save_image(fake_A.detach(), join(dir_image_train, '{}_fake_A.png'.format(epoch + 1)), nrow=1, 106 | normalize=True) 107 | save_image(fake_B.detach(), join(dir_image_train, '{}_fake_B.png'.format(epoch + 1)), nrow=1, 108 | normalize=True) 109 | 110 | if (iter_total % iter_val == 0) and val_during_training: 111 | dir_image_test_A2B = join(dir_image_test, str(iter_total), 'A2B') 112 | dir_image_test_B2A = join(dir_image_test, str(iter_total), 'B2A') 113 | os.makedirs(dir_image_test_A2B, exist_ok=True) 114 | os.makedirs(dir_image_test_B2A, exist_ok=True) 115 | 116 | # Generate fake B images with input A images. 117 | dataset_test_A2B = CustomDataset(opt_test, 'val_A2B') 118 | data_loader_test_A2B = DataLoader(dataset=dataset_test_A2B, batch_size=batch_size, 119 | num_workers=num_workers, shuffle=False) 120 | 121 | for p in G_A.parameters(): 122 | p.requires_grad_(False) 123 | 124 | for i, (A_val, _) in enumerate(data_loader_test_A2B): 125 | A_val = A_val.to(device) 126 | 127 | fake_B = G_A(A_val) 128 | 129 | save_image(fake_B.detach(), join(dir_image_test_A2B, 130 | '{}_fake_B.png'.format(i)), nrow=1, normalize=True) 131 | 132 | for p in G_A.parameters(): 133 | p.requires_grad_(True) 134 | 135 | # Generate fake A image with input B images. 136 | dataset_test_B2A = CustomDataset(opt_test, 'val_B2A') 137 | data_loader_test_B2A = DataLoader(dataset=dataset_test_B2A, batch_size=batch_size, 138 | num_workers=num_workers, shuffle=False) 139 | 140 | for p in G_B.parameters(): 141 | p.requires_grad_(False) 142 | 143 | for i, (B_val, _) in enumerate(data_loader_test_B2A): 144 | B_val = B_val.to(device) 145 | 146 | fake_A = G_B(B_val) 147 | 148 | save_image(fake_A.detach(), join(dir_image_test_B2A, 149 | '{}_fake_A.png'.format(i)), nrow=1, normalize=True) 150 | 151 | for p in G_B.parameters(): 152 | p.requires_grad_(True) 153 | 154 | if (epoch + 1) % epoch_save == 0: 155 | torch.save(G_A.state_dict(), join(dir_model, '{}_G_A.pt'.format(epoch + 1))) 156 | torch.save(G_B.state_dict(), join(dir_model, '{}_G_B.pt'.format(epoch + 1))) 157 | torch.save(D_A.state_dict(), join(dir_model, '{}_D_A.pt'.format(epoch + 1))) 158 | torch.save(D_B.state_dict(), join(dir_model, '{}_D_B.pt'.format(epoch + 1))) 159 | 160 | print(datetime.now() - st) 161 | -------------------------------------------------------------------------------- /generation/CycleGAN/utils.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | 4 | class ImageBuffer(object): 5 | def __init__(self, n_images=50): 6 | """ 7 | ImageBuffer is for updating the discriminator with historical images of the generator. By doing this, one can 8 | prevent severe artifacts which appears in synthetic images from the generator in an original setting without 9 | buffer. To this end, we store previous n_images and randomly picked one from the buffer or return the current 10 | synthesized image by 50% chance every iteration. 11 | For more information, you can check "Learning from Simulated and Unsupervised Images through Adversarial 12 | Training" 13 | """ 14 | 15 | self.buffer = list() 16 | self.n_images = n_images 17 | 18 | def __call__(self, image): 19 | if len(self.buffer) < self.n_images: 20 | self.buffer.append(image) 21 | else: 22 | if random.random() > 0.5: 23 | new_image = image 24 | index = random.randint(0, self.n_images - 1) # Get a random index in [0, n_images - 1] 25 | image = self.buffer[index] # Draw image from the buffer. 26 | self.buffer[index] = new_image # Replace the drawn image with current image. 27 | 28 | else: 29 | pass 30 | return image 31 | -------------------------------------------------------------------------------- /generation/DCGAN/models.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | 3 | 4 | def weights_init(module): 5 | if isinstance(module, nn.Conv2d) or isinstance(module, nn.ConvTranspose2d): 6 | module.weight.detach().normal_(mean=0., std=0.02) 7 | 8 | elif isinstance(module, nn.BatchNorm2d): 9 | module.weight.detach().normal_(1., 0.02) 10 | module.bias.detach().zero_() 11 | 12 | else: 13 | pass 14 | 15 | 16 | class View(nn.Module): 17 | def __init__(self, output_shape): 18 | super(View, self).__init__() 19 | self.output_shape = output_shape 20 | 21 | def forward(self, x): 22 | return x.view(x.shape[0], *self.output_shape) 23 | 24 | 25 | class Generator(nn.Module): 26 | def __init__(self, dataset_name): 27 | super(Generator, self).__init__() 28 | act = nn.ReLU(inplace=True) 29 | norm = nn.BatchNorm2d 30 | 31 | if dataset_name == 'CIFAR10': # Output shape 3x32x32 32 | model = [nn.Linear(100, 512 * 4 * 4), View([512, 4, 4]), norm(512), act] # 4x4 33 | model += [nn.ConvTranspose2d(512, 256, 5, stride=2, padding=2, output_padding=1), norm(256), act] # 8x8 34 | model += [nn.ConvTranspose2d(256, 128, 5, stride=2, padding=2, output_padding=1), norm(128), act] # 16x16 35 | model += [nn.ConvTranspose2d(128, 3, 5, stride=2, padding=2, output_padding=1), nn.Tanh()] # 32x32 36 | 37 | elif dataset_name == 'LSUN': # Output shape 3x64x64 38 | model = [nn.Linear(100, 1024 * 4 * 4), View([1024, 4, 4]), norm(1024), act] # 4x4 39 | model += [nn.ConvTranspose2d(1024, 512, 5, stride=2, padding=2, output_padding=1), norm(512), act] # 8x8 40 | model += [nn.ConvTranspose2d(512, 256, 5, stride=2, padding=2, output_padding=1), norm(256), act] # 16x16 41 | model += [nn.ConvTranspose2d(256, 128, 5, stride=2, padding=2, output_padding=1), norm(128), act] # 32x32 42 | model += [nn.ConvTranspose2d(128, 3, 5, stride=2, padding=2, output_padding=1), nn.Tanh()] # 64x64 43 | 44 | elif dataset_name == 'MNIST': # Output shape 1x28x28 45 | model = [nn.Linear(100, 256 * 4 * 4), View([256, 4, 4]), norm(256), act] # 4x4 46 | model += [nn.ConvTranspose2d(256, 128, 5, stride=2, padding=2), norm(128), act] # 7x7 47 | model += [nn.ConvTranspose2d(128, 64, 5, stride=2, padding=2, output_padding=1), norm(64), act] # 14x14 48 | model += [nn.ConvTranspose2d(64, 1, 5, stride=2, padding=2, output_padding=1), nn.Tanh()] # 28x28 49 | 50 | else: 51 | raise NotImplementedError 52 | 53 | self.model = nn.Sequential(*model) 54 | 55 | def forward(self, x): 56 | return self.model(x) 57 | 58 | 59 | class Discriminator(nn.Module): 60 | def __init__(self, dataset_name): 61 | super(Discriminator, self).__init__() 62 | act = nn.LeakyReLU(inplace=True, negative_slope=0.2) 63 | norm = nn.BatchNorm2d 64 | 65 | if dataset_name == 'CIFAR10': # Input shape 3x32x32 66 | model = [nn.Conv2d(3, 128, 5, stride=2, padding=2, bias=False), act] # 16x16 67 | model += [nn.Conv2d(128, 256, 5, stride=2, padding=2, bias=False), norm(128), act] # 8x8 68 | model += [nn.Conv2d(256, 512, 5, stride=2, padding=2, bias=False), norm(256), act] # 4x4 69 | model += [nn.Conv2d(512, 1, 4, stride=2, padding=2, bias=False), nn.Sigmoid()] # 1x1 70 | 71 | elif dataset_name == 'LSUN': # Input shape 3x64x64 72 | model = [nn.Conv2d(3, 128, 5, stride=2, padding=2, bias=False), act] # 128x32x32 73 | model += [nn.Conv2d(128, 256, 5, stride=2, padding=2, bias=False), norm(128), act] # 256x16x16 74 | model += [nn.Conv2d(256, 512, 5, stride=2, padding=2, bias=False), norm(256), act] # 512x8x8 75 | model += [nn.Conv2d(512, 1024, 5, stride=2, padding=2, bias=False), norm(512), act] # 1024x4x4 76 | model += [nn.Conv2d(1024, 1, 4), nn.Sigmoid()] # 1x1x1 77 | 78 | elif dataset_name == 'MNIST': # Input shape 1x28x28 79 | model = [nn.Conv2d(1, 64, 5, stride=2, padding=2, bias=False), act] # 14x14 80 | model += [nn.Conv2d(64, 128, 5, stride=2, padding=2, bias=False), norm(128), act] # 7x7 81 | model += [nn.Conv2d(128, 256, 5, stride=2, padding=2, bias=False), norm(256), act] # 4x4 82 | model += [nn.Conv2d(256, 1, 4, bias=False), nn.Sigmoid()] # 1x1 83 | 84 | else: 85 | raise NotImplementedError 86 | 87 | self.model = nn.Sequential(*model) 88 | 89 | def forward(self, x): 90 | return self.model(x) 91 | -------------------------------------------------------------------------------- /generation/DCGAN/train.py: -------------------------------------------------------------------------------- 1 | if __name__ == '__main__': 2 | import os 3 | from torchvision.transforms import Compose, Normalize, Resize, ToTensor 4 | from torch.utils.data import DataLoader 5 | from models import Discriminator, Generator, weights_init 6 | import torch 7 | import torch.nn as nn 8 | import matplotlib.pyplot as plt 9 | from time import time 10 | from tqdm import tqdm 11 | os.environ['CUDA_VISIBLE_DEVICES'] = '0' 12 | 13 | BETA1, BETA2 = 0.5, 0.99 14 | BATCH_SIZE = 16 15 | DATASET_NAME = 'MNIST' 16 | DEVICE = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu:0') 17 | EPOCHS = 1 18 | ITER_REPORT = 10 19 | LATENT_DIM = 100 20 | LR = 2e-4 21 | N_D_STEP = 1 22 | 23 | if DATASET_NAME == 'CIFAR10': 24 | from torchvision.datasets import CIFAR10 25 | transforms = Compose([ToTensor(), Normalize(mean=[0.5], std=[0.5])]) 26 | dataset = CIFAR10(root='./datasets', train=True, transform=transforms, download=True) 27 | elif DATASET_NAME == 'LSUN': 28 | from torchvision.datasets import LSUN 29 | transforms = Compose([Resize(64), ToTensor(), Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])]) 30 | dataset = LSUN(root='./datasets/LSUN', classes=['bedroom_train'], transform=transforms) 31 | elif DATASET_NAME == 'MNIST': 32 | from torchvision.datasets import MNIST 33 | transforms = Compose([ToTensor(), Normalize(mean=[0.5], std=[0.5])]) 34 | dataset = MNIST(root='./datasets', train=True, transform=transforms, download=True) 35 | else: 36 | raise NotImplementedError 37 | 38 | data_loader = DataLoader(dataset=dataset, batch_size=BATCH_SIZE, num_workers=0, shuffle=True) 39 | 40 | D = Discriminator(DATASET_NAME).apply(weights_init).to(DEVICE) 41 | G = Generator(DATASET_NAME).apply(weights_init).to(DEVICE) 42 | print(D, G) 43 | criterion = nn.BCELoss() 44 | 45 | optim_D = torch.optim.Adam(D.parameters(), lr=LR, betas=(BETA1, BETA2)) 46 | optim_G = torch.optim.Adam(G.parameters(), lr=LR, betas=(BETA1, BETA2)) 47 | 48 | list_D_loss = list() 49 | list_G_loss = list() 50 | total_step = 0 51 | 52 | st = time() 53 | for epoch in range(EPOCHS): 54 | for data in tqdm(data_loader): 55 | total_step += 1 56 | real, label = data[0].to(DEVICE), data[1].to(DEVICE) 57 | z = torch.randn(BATCH_SIZE, LATENT_DIM).to(DEVICE) 58 | 59 | fake = G(z) 60 | 61 | fake_score = D(fake.detach()) 62 | real_score = D(real) 63 | 64 | D_loss = 0.5 * (criterion(fake_score, torch.zeros_like(fake_score).to(DEVICE)) 65 | + criterion(real_score, torch.ones_like(real_score).to(DEVICE))) 66 | optim_D.zero_grad() 67 | D_loss.backward() 68 | optim_D.step() 69 | list_D_loss.append(D_loss.detach().cpu().item()) 70 | 71 | if total_step % N_D_STEP == 0: 72 | fake_score = D(fake) 73 | G_loss = criterion(fake_score, torch.ones_like(fake_score)) 74 | optim_G.zero_grad() 75 | G_loss.backward() 76 | optim_G.step() 77 | list_G_loss.append(G_loss.detach().cpu().item()) 78 | 79 | if total_step % ITER_REPORT == 0: 80 | print("Epoch: {}, D_loss: {:.{prec}} G_loss: {:.{prec}}" 81 | .format(epoch, D_loss.detach().cpu().item(), G_loss.detach().cpu().item(), prec=4)) 82 | 83 | torch.save(D.state_dict(), '{}_D.pt'.format(DATASET_NAME)) 84 | torch.save(G.state_dict(), '{}_G.pt'.format(DATASET_NAME)) 85 | 86 | plt.figure() 87 | plt.plot(range(0, len(list_D_loss)), list_D_loss, linestyle='--', color='r', label='Discriminator loss') 88 | plt.plot(range(0, len(list_G_loss) * N_D_STEP, N_D_STEP), list_G_loss, linestyle='--', color='g', 89 | label='Generator loss') 90 | plt.xlabel('Iteration') 91 | plt.ylabel('Loss') 92 | plt.legend() 93 | plt.savefig('Loss.png') 94 | 95 | print(time() - st) 96 | -------------------------------------------------------------------------------- /generation/GAN/CIFAR10_models.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | 3 | 4 | class Generator(nn.Module): 5 | def __init__(self): 6 | super(Generator, self).__init__() 7 | model = [nn.Linear(in_features=100, out_features=512), nn.ReLU(inplace=True), nn.Dropout(p=0.5)] 8 | model += [nn.Linear(in_features=512, out_features=256), nn.ReLU(inplace=True), nn.Dropout(p=0.5)] 9 | model += [nn.Linear(in_features=256, out_features=3 * 32 * 32), nn.Tanh()] 10 | self.model = nn.Sequential(*model) 11 | 12 | def forward(self, x): 13 | return self.model(x) 14 | 15 | 16 | class Discriminator(nn.Module): 17 | def __init__(self): 18 | super(Discriminator, self).__init__() 19 | model = [nn.Linear(in_features=3 * 32 * 32, out_features=512), nn.LeakyReLU(inplace=True, negative_slope=0.2)] 20 | model += [nn.Linear(in_features=512, out_features=256), nn.LeakyReLU(inplace=True, negative_slope=0.2)] 21 | model += [nn.Linear(in_features=256, out_features=3), nn.Sigmoid()] 22 | self.model = nn.Sequential(*model) 23 | 24 | def forward(self, x): 25 | return self.model(x) 26 | -------------------------------------------------------------------------------- /generation/GAN/CIFAR10_test.py: -------------------------------------------------------------------------------- 1 | if __name__ == '__main__': 2 | import os 3 | from os.path import join 4 | import torch 5 | from torch.utils.data import DataLoader 6 | from torchvision.datasets import CIFAR10 7 | from torchvision.transforms import Compose, Normalize, ToTensor 8 | from torchvision.utils import save_image 9 | from CIFAR10_models import Generator 10 | import time 11 | 12 | st = time.time() 13 | 14 | BATCH_SIZE = 16 15 | IMAGE_DIR = './GAN/checkpoints/CIFAR10/Image/Test' 16 | IMAGE_SIZE = 32 17 | LATENT_DIM = 100 18 | MODEL_DIR = './GAN/checkpoints/CIFAR10/Model' 19 | OUTPUT_CHANNEL = 3 20 | os.makedirs(IMAGE_DIR, exist_ok=True) 21 | 22 | transforms = Compose([ToTensor(), Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])]) 23 | dataset = CIFAR10(root='./datasets', train=False, transform=transforms, download=True) 24 | data_loader = DataLoader(dataset=dataset, batch_size=BATCH_SIZE, num_workers=0, shuffle=False) 25 | 26 | G = Generator() 27 | G.load_state_dict(torch.load(join(MODEL_DIR, 'Latest_G.pt')).state_dict()) 28 | 29 | for p in G.parameters(): 30 | p.requires_grad_(False) if p.requires_grad else None 31 | 32 | z = torch.randn(BATCH_SIZE, LATENT_DIM) 33 | 34 | fake = G(z).view(BATCH_SIZE, OUTPUT_CHANNEL, IMAGE_SIZE, IMAGE_SIZE) 35 | save_image(fake.detach(), join(IMAGE_DIR, 'Latest_G_results.png'), nrow=4, normalize=True) 36 | 37 | print("Total time taken: ", time.time() - st) 38 | -------------------------------------------------------------------------------- /generation/GAN/CIFAR10_train.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | 3 | 4 | def init_weights(m): 5 | if isinstance(m, nn.Linear): 6 | m.weight.detach().normal_(0., 0.02) 7 | m.bias.detach().fill_(0.) 8 | 9 | 10 | if __name__ == '__main__': 11 | import os 12 | from os.path import join 13 | import torch 14 | from torch.utils.data import DataLoader 15 | from torchvision.datasets import CIFAR10 16 | from torchvision.transforms import Compose, Normalize, ToTensor 17 | from torchvision.utils import save_image 18 | from CIFAR10_models import Generator, Discriminator 19 | import datetime 20 | 21 | BATCH_SIZE = 16 22 | EPOCHS = 25 23 | IMAGE_DIR = './GAN/checkpoints/CIFAR10/Image/Training' 24 | IMAGE_SIZE = 32 25 | ITER_DISPLAY = 100 26 | ITER_REPORT = 10 27 | LATENT_DIM = 100 28 | MODEL_DIR = './GAN/checkpoints/CIFAR10/Model' 29 | OUT_CHANNEL = 3 30 | os.makedirs(IMAGE_DIR, exist_ok=True) 31 | os.makedirs(MODEL_DIR, exist_ok=True) 32 | 33 | transform = Compose([ToTensor(), Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])]) 34 | dataset = CIFAR10(root='./datasets', train=True, transform=transform, download=True) 35 | data_loader = DataLoader(dataset=dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=1) 36 | 37 | D = Discriminator().apply(init_weights) 38 | G = Generator().apply(init_weights) 39 | print(D) 40 | print(G) 41 | 42 | optim_D = torch.optim.Adam(D.parameters(), lr=2e-4, betas=(0.5, 0.9)) 43 | optim_G = torch.optim.Adam(G.parameters(), lr=2e-4, betas=(0.5, 0.9)) 44 | 45 | st = datetime.datetime.now() 46 | iter = 0 47 | for epoch in range(EPOCHS): 48 | for i, data in enumerate(data_loader): 49 | iter += 1 50 | 51 | z = torch.rand(BATCH_SIZE, LATENT_DIM) 52 | 53 | fake = G(z) 54 | real = data[0].view(BATCH_SIZE, -1) 55 | 56 | loss_D = -torch.mean(torch.log(D(real)) + torch.log(1 - D(fake.detach()))) 57 | optim_D.zero_grad() 58 | loss_D.backward() 59 | optim_D.step() 60 | 61 | loss_G = -torch.mean(torch.log(D(fake))) # Non saturating loss 62 | # For saturaing loss, loss_G = torch.mean(torch.log(1-D(fake))) 63 | optim_G.zero_grad() 64 | loss_G.backward() 65 | optim_G.step() 66 | 67 | if iter % ITER_DISPLAY == 0: 68 | fake = fake.view(BATCH_SIZE, OUT_CHANNEL, IMAGE_SIZE, IMAGE_SIZE) 69 | real = real.view(BATCH_SIZE, OUT_CHANNEL, IMAGE_SIZE, IMAGE_SIZE) 70 | save_image(fake, IMAGE_DIR + '/{}_fake.png'.format(epoch + 1), nrow=4, normalize=True) 71 | save_image(real, IMAGE_DIR + '/{}_real.png'.format(epoch + 1), nrow=4, normalize=True) 72 | 73 | if iter % ITER_REPORT == 0: 74 | print("Epoch: {} Iter: {} Loss D: {:.{prec}} Loss G: {:.{prec}}" 75 | .format(epoch + 1, iter, loss_D.detach().item(), loss_G.detach().item(), prec=4)) 76 | 77 | torch.save(D, join(MODEL_DIR, 'Latest_D.pt')) 78 | torch.save(G, join(MODEL_DIR, 'Latest_G.pt')) 79 | print("Total time taken: ", datetime.datetime.now() - st) 80 | -------------------------------------------------------------------------------- /generation/GAN/MNIST_models.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | 3 | 4 | class Generator(nn.Module): 5 | def __init__(self): 6 | super(Generator, self).__init__() 7 | model = [nn.Linear(in_features=100, out_features=128), nn.ReLU(inplace=True)] 8 | model += [nn.Linear(in_features=128, out_features=256), nn.ReLU(inplace=True)] 9 | model += [nn.Linear(in_features=256, out_features=28 * 28), nn.Sigmoid()] 10 | self.model = nn.Sequential(*model) 11 | 12 | # "The generator nets used a mixture of rectifier linear activations and sigmoid activations, while the 13 | # discriminator net used maxout activations." - Generative Adversarial Networks 14 | 15 | def forward(self, x): 16 | return self.model(x) 17 | 18 | 19 | class Discriminator(nn.Module): 20 | def __init__(self): 21 | super(Discriminator, self).__init__() 22 | model = [Maxout(28 * 28, 256, dropout=False, k=5)] 23 | model += [Maxout(256, 128, dropout=True, k=5)] 24 | model += [nn.Linear(128, 1), nn.Sigmoid()] 25 | self.model = nn.Sequential(*model) 26 | 27 | def forward(self, x): 28 | return self.model(x) 29 | 30 | 31 | class Maxout(nn.Module): 32 | def __init__(self, in_features, out_features, k=2, dropout=True, p=0.5): 33 | super(Maxout, self).__init__() 34 | model = [nn.Dropout(p)] if dropout else [] 35 | model += [nn.Linear(in_features, out_features * k)] 36 | 37 | self.model = nn.Sequential(*model) 38 | self.k = k 39 | 40 | # Note that dropout is used before weight multiplication following 'Maxout Networks' paper. 41 | # "When training with dropout, we perform the elementwise multiplication with the dropout mask immediately prior 42 | # to the multiplication by the weights in all cases-we do not drop inputs to the max operator." - Maxout 43 | # Networks 44 | 45 | def forward(self, x): 46 | x = self.model(x) 47 | x, _ = x.view(x.shape[0], x.shape[1] // self.k, self.k).max(-1) 48 | return x 49 | 50 | -------------------------------------------------------------------------------- /generation/GAN/MNIST_test.py: -------------------------------------------------------------------------------- 1 | if __name__ == '__main__': 2 | import os 3 | from os.path import join 4 | import torch 5 | from torch.utils.data import DataLoader 6 | from torchvision.datasets import MNIST 7 | from torchvision.transforms import ToTensor 8 | from torchvision.utils import save_image 9 | from MNIST_models import Generator 10 | import time 11 | 12 | st = time.time() 13 | 14 | BATCH_SIZE = 16 15 | IMAGE_DIR = './GAN/checkpoints/MNIST/Image/Test' 16 | LATENT_DIM = 100 17 | MODEL_DIR = './GAN/checkpoints/MNIST/Model' 18 | os.makedirs(IMAGE_DIR, exist_ok=True) 19 | 20 | dataset = MNIST(root='./datasets', train=False, transform=ToTensor(), download=True) 21 | data_loader = DataLoader(dataset=dataset, batch_size=32, num_workers=0, shuffle=False) 22 | 23 | G = Generator() 24 | G.load_state_dict(torch.load(join(MODEL_DIR, 'Latest_G.pt')).state_dict()) 25 | 26 | for p in G.parameters(): 27 | p.requires_grad_(False) if p.requires_grad else None 28 | 29 | z = torch.randn(BATCH_SIZE, LATENT_DIM) 30 | 31 | fake = G(z).view(BATCH_SIZE, 1, 28, 28) 32 | save_image(fake.detach(), join(IMAGE_DIR, 'Latest_G_results.png'), nrow=4, normalize=True) 33 | 34 | print("Total time taken: ", time.time() - st) 35 | -------------------------------------------------------------------------------- /generation/GAN/MNIST_train.py: -------------------------------------------------------------------------------- 1 | from os.path import join 2 | import torch.nn as nn 3 | 4 | 5 | def init_weights(m): 6 | if isinstance(m, nn.Linear): 7 | m.weight.detach().normal_(0., 0.02) 8 | m.bias.detach().fill_(0.) 9 | 10 | 11 | if __name__ == '__main__': 12 | import os 13 | import torch 14 | from torch.utils.data import DataLoader 15 | from torchvision.datasets import MNIST 16 | from torchvision.transforms import Compose, ToTensor 17 | from torchvision.utils import save_image 18 | from MNIST_models import Generator, Discriminator 19 | import datetime 20 | 21 | BATCH_SIZE = 16 22 | EPOCHS = 25 23 | IMAGE_DIR = './GAN/checkpoints/MNIST/Image/Training' 24 | IMAGE_SIZE = 28 25 | ITER_DISPLAY = 100 26 | ITER_REPORT = 10 27 | LATENT_DIM = 100 28 | MODEL_DIR = './GAN/checkpoints/MNIST/Model' 29 | OUT_CHANNEL = 1 30 | os.makedirs(IMAGE_DIR, exist_ok=True) 31 | os.makedirs(MODEL_DIR, exist_ok=True) 32 | 33 | dataset = MNIST(root='./datasets', train=True, transform=ToTensor(), download=True) 34 | data_loader = DataLoader(dataset=dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=1) 35 | 36 | D = Discriminator() 37 | G = Generator() 38 | print(D) 39 | print(G) 40 | 41 | optim_D = torch.optim.Adam(D.parameters(), lr=2e-4, betas=(0.5, 0.9)) 42 | optim_G = torch.optim.Adam(G.parameters(), lr=2e-4, betas=(0.5, 0.9)) 43 | 44 | st = datetime.datetime.now() 45 | iter = 0 46 | for epoch in range(EPOCHS): 47 | for i, data in enumerate(data_loader): 48 | iter += 1 49 | 50 | z = torch.rand(BATCH_SIZE, LATENT_DIM) 51 | 52 | fake = G(z) 53 | real = data[0].view(BATCH_SIZE, -1) 54 | 55 | loss_D = -torch.mean(torch.log(D(real)) + torch.log(1 - D(fake.detach()))) 56 | optim_D.zero_grad() 57 | loss_D.backward() 58 | optim_D.step() 59 | 60 | loss_G = -torch.mean(torch.log(D(fake))) # Non saturating loss 61 | # For saturaing loss, loss_G = torch.mean(torch.log(1-D(fake))) 62 | optim_G.zero_grad() 63 | loss_G.backward() 64 | optim_G.step() 65 | 66 | if iter % ITER_DISPLAY == 0: 67 | fake = fake.view(BATCH_SIZE, OUT_CHANNEL, IMAGE_SIZE, IMAGE_SIZE) 68 | real = real.view(BATCH_SIZE, OUT_CHANNEL, IMAGE_SIZE, IMAGE_SIZE) 69 | save_image(fake, IMAGE_DIR + '/{}_fake.png'.format(epoch + 1), nrow=4, normalize=True) 70 | save_image(real, IMAGE_DIR + '/{}_real.png'.format(epoch + 1), nrow=4, normalize=True) 71 | 72 | if iter % ITER_REPORT == 0: 73 | print("Epoch: {} Iter: {} Loss D: {:.{prec}} Loss G: {:.{prec}}" 74 | .format(epoch + 1, iter, loss_D.detach().item(), loss_G.detach().item(), prec=4)) 75 | 76 | torch.save(D, join(MODEL_DIR, 'Latest_D.pt')) 77 | torch.save(G, join(MODEL_DIR, 'Latest_G.pt')) 78 | print("Total time taken: ", datetime.datetime.now() - st) 79 | -------------------------------------------------------------------------------- /generation/SGAN/models.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | 3 | 4 | class AuxiliaryClassifier(nn.Module): 5 | def __init__(self, in_features, n_classes, kernel_size=1, stride=1, padding=0, bias=False, softmax=False, 6 | cnn=False): 7 | super(AuxiliaryClassifier, self).__init__() 8 | if cnn: 9 | classes = [nn.Conv2d(in_features, n_classes, kernel_size, stride, padding, bias=bias)] 10 | validity = [nn.Conv2d(in_features, n_classes, kernel_size, stride, padding, bias=bias), nn.Sigmoid()] 11 | else: 12 | classes = [nn.Linear(in_features, n_classes)] 13 | validity = [nn.Linear(in_features, 1), nn.Sigmoid()] 14 | self.classes = nn.Sequential(*classes, nn.Softmax if softmax else Identity()) 15 | self.validity = nn.Sequential(*validity) 16 | 17 | def forward(self, x): 18 | return self.classes(x), self.validity(x) 19 | 20 | 21 | class Generator(nn.Module): 22 | def __init__(self, cnn=False): 23 | super(Generator, self).__init__() 24 | act = nn.ReLU(inplace=True) 25 | if not cnn: 26 | model = [nn.Linear(in_features=100, out_features=512), act, nn.Dropout(p=0.5)] 27 | model += [nn.Linear(in_features=512, out_features=256), act, nn.Dropout(p=0.5)] 28 | model += [nn.Linear(in_features=256, out_features=28 * 28), nn.Tanh()] 29 | else: 30 | norm = nn.BatchNorm2d 31 | model = [nn.Linear(100, 512 * 4 * 4), View([512, 4, 4]), norm(512), act] # 4x4 32 | model += [nn.ConvTranspose2d(512, 256, 5, stride=2, padding=2), norm(256), act] # 7x7 33 | model += [nn.ConvTranspose2d(256, 128, 5, stride=2, padding=2, output_padding=1), norm(128), act] # 14x14 34 | model += [nn.ConvTranspose2d(128, 1, 5, stride=2, padding=2, output_padding=1), nn.Tanh()] # 28x28 35 | self.model = nn.Sequential(*model) 36 | 37 | def forward(self, x): 38 | return self.model(x) 39 | 40 | 41 | class Identity(nn.Module): 42 | def __init__(self): 43 | super(Identity, self).__init__() 44 | 45 | def forward(self, x): 46 | return x 47 | 48 | 49 | class Discriminator(nn.Module): 50 | def __init__(self, cnn=False): 51 | super(Discriminator, self).__init__() 52 | act = nn.LeakyReLU(inplace=True, negative_slope=0.2) 53 | if not cnn: 54 | model = [nn.Linear(in_features=28 * 28, out_features=512), act] 55 | model += [nn.Linear(in_features=512, out_features=256), act] 56 | model += [AuxiliaryClassifier(256, 10)] 57 | else: 58 | norm = nn.BatchNorm2d 59 | model = [nn.Conv2d(1, 128, 5, stride=2, padding=2, bias=False), act] # 14x14 60 | model += [nn.Conv2d(128, 256, 5, stride=2, padding=2, bias=False), norm(256), act] # 7x7 61 | model += [nn.Conv2d(256, 512, 5, stride=2, padding=2, bias=False), norm(512), act] # 4x4 62 | model += [AuxiliaryClassifier(512, 10, kernel_size=4, bias=False, cnn=True)] 63 | self.model = nn.Sequential(*model) 64 | 65 | def forward(self, x): 66 | return self.model(x) 67 | 68 | 69 | class View(nn.Module): 70 | def __init__(self, output_shape): 71 | super(View, self).__init__() 72 | self.output_shape = output_shape 73 | 74 | def forward(self, x): 75 | return x.view(x.shape[0], *self.output_shape) 76 | 77 | 78 | def weights_init(module): 79 | if isinstance(module, nn.Conv2d) or isinstance(module, nn.ConvTranspose2d): 80 | module.weight.detach().normal_(mean=0., std=0.02) 81 | 82 | elif isinstance(module, nn.BatchNorm2d): 83 | module.weight.detach().normal_(1., 0.02) 84 | module.bias.detach().zero_() 85 | 86 | else: 87 | pass 88 | -------------------------------------------------------------------------------- /generation/SGAN/train.py: -------------------------------------------------------------------------------- 1 | if __name__ == '__main__': 2 | import os 3 | from os.path import join 4 | from torchvision.transforms import Compose, ToTensor, Normalize 5 | from torchvision.datasets import MNIST 6 | from torchvision.utils import save_image 7 | from torch.utils.data import DataLoader 8 | from models import Discriminator, Generator, weights_init 9 | import torch 10 | import torch.nn as nn 11 | import matplotlib.pyplot as plt 12 | from time import time 13 | from tqdm import tqdm # For visualizing a time bar for training 14 | 15 | os.environ['CUDA_VISIBLE_DEVICES'] = '0' if torch.cuda.device_count() > 1 else '' 16 | 17 | BATCH_SIZE = 16 18 | BETA1, BETA2 = 0.5, 0.99 19 | CNN = False 20 | DEVICE = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu:0') 21 | DIR_ANALYSIS = './SGAN/checkpoints/Analysis' 22 | DIR_IMAGE = './SGAN/checkpoints/Image/Training' 23 | DIR_MODEL = './SGAN/checkpoints/Model' 24 | EPOCHS = 25 25 | ITER_DISPLAY = 100 26 | ITER_REPORT = 10 27 | LATENT_DIM = 100 28 | LR = 2e-4 29 | N_D_STEP = 1 30 | 31 | os.makedirs(DIR_ANALYSIS, exist_ok=True) 32 | os.makedirs(DIR_IMAGE, exist_ok=True) 33 | os.makedirs(DIR_MODEL, exist_ok=True) 34 | 35 | transforms = Compose([ToTensor(), Normalize(mean=[0.5], std=[0.5])]) 36 | dataset = MNIST(root='./datasets', train=True, transform=transforms, download=True) 37 | data_loader = DataLoader(dataset=dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=0) 38 | 39 | D = Discriminator(cnn=CNN).apply(weights_init).to(DEVICE) 40 | G = Generator(cnn=CNN).apply(weights_init).to(DEVICE) 41 | print(D, G) 42 | 43 | CELoss = nn.CrossEntropyLoss() 44 | BCELoss = nn.BCELoss() 45 | 46 | optim_D = torch.optim.Adam(D.parameters(), lr=LR, betas=(BETA1, BETA2)) 47 | optim_G = torch.optim.Adam(G.parameters(), lr=LR, betas=(BETA1, BETA2)) 48 | 49 | list_D_loss = list() 50 | list_G_loss = list() 51 | total_step = 0 52 | st = time() 53 | for epoch in range(EPOCHS): 54 | for data in tqdm(data_loader): 55 | total_step += 1 56 | real, label = data[0].to(DEVICE), data[1].to(DEVICE) 57 | real = real.view(real.shape[0], -1) if not CNN else real 58 | z = torch.randn(BATCH_SIZE, LATENT_DIM).to(DEVICE) 59 | fake = G(z) 60 | 61 | class_fake, validity_fake = D(fake.detach()) 62 | class_real, validity_real = D(real) 63 | loss_class_fake = CELoss(class_fake, label) 64 | loss_class_real = CELoss(class_real, label) 65 | loss_fake = BCELoss(validity_fake, torch.zeros_like(validity_fake).to(DEVICE)) 66 | loss_real = BCELoss(validity_real, torch.ones_like(validity_real).to(DEVICE)) 67 | D_loss = (loss_class_fake + loss_class_real + loss_fake + loss_real).mean() 68 | 69 | optim_D.zero_grad() 70 | D_loss.backward() 71 | optim_D.step() 72 | 73 | list_D_loss.append(D_loss.detach().cpu().item()) 74 | 75 | if total_step % N_D_STEP == 0: 76 | class_fake, validity_fake = D(fake) 77 | loss_class_fake = CELoss(class_fake, label) 78 | loss_fake = BCELoss(validity_fake, torch.ones_like(validity_fake)) 79 | 80 | G_loss = (loss_class_fake + loss_fake).mean() 81 | 82 | optim_G.zero_grad() 83 | G_loss.backward() 84 | optim_G.step() 85 | 86 | list_G_loss.append(G_loss.detach().cpu().item()) 87 | 88 | if total_step % ITER_REPORT == 0: 89 | print(" Epoch: {}, D_loss: {:.{prec}}, G_loss {:.{prec}}" 90 | .format(epoch + 1, D_loss.detach().cpu().item(), G_loss.detach().cpu().item(), prec=4)) 91 | 92 | if total_step % ITER_DISPLAY == 0: 93 | save_image(fake.detach().view(BATCH_SIZE, 1, 28, 28), 94 | join(DIR_IMAGE, '{}_fake.png'.format(epoch + 1)), nrow=int(BATCH_SIZE ** (1/2)), 95 | normalize=True) 96 | save_image(real.view(BATCH_SIZE, 1, 28, 28), join(DIR_IMAGE, '{}_real.png'.format(epoch + 1)), 97 | nrow=int(BATCH_SIZE ** (1/2)), normalize=True) 98 | 99 | print("Total time taken: ", time() - st) 100 | 101 | torch.save(G.state_dict(), join(DIR_MODEL, 'G.pt')) 102 | torch.save(D.state_dict(), join(DIR_MODEL, 'D.pt')) 103 | 104 | plt.figure() 105 | plt.title('Semi-Supervised GAN Training') 106 | plt.xlabel('Iteration') 107 | plt.ylabel('Loss') 108 | plt.plot(range(0, len(list_D_loss)), list_D_loss, linestyle='--', color='r', label='Discriminator loss') 109 | plt.plot(range(0, len(list_G_loss) * N_D_STEP, N_D_STEP), list_G_loss, linestyle='--', color='g', 110 | label='Generator loss') 111 | plt.legend() 112 | plt.savefig(join(DIR_ANALYSIS, 'SGAN_training.png')) 113 | 114 | -------------------------------------------------------------------------------- /generation/cGAN/MNIST_models.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | 5 | 6 | class Generator(nn.Module): 7 | def __init__(self): 8 | super(Generator, self).__init__() 9 | self.linear_z = nn.Linear(100, 200) 10 | self.linear_y = nn.Linear(10, 1000) 11 | self.linear = nn.Linear(1200, 784) 12 | 13 | # "In our generator net, a noise prior z with dimensionality 100 was drawn from a uniform distribution within 14 | # the unit hypercube. Both z and y (one-hot label <- added by Noel) are mapped to hidden layers with Rectified 15 | # Linear Unit (ReLU) activation, with layer sizes 200 and 1000 respectively, before both being mapped to second, 16 | # combined hidden ReLu layer of dimensionality 1200. We then have a final sigmoid unit layer as our output for 17 | # generating the 784-dimensional MNIST samples." - Conditional Generative Adversarial Networks. 18 | 19 | def forward(self, z, y): 20 | x = torch.cat((self.linear_z(z), self.linear_y(y)), dim=1) 21 | x = F.dropout(x, p=0.5) 22 | return nn.Sigmoid()(self.linear(x)) 23 | 24 | 25 | class Discriminator(nn.Module): 26 | def __init__(self): 27 | super(Discriminator, self).__init__() 28 | self.linear_x = Maxout(28 * 28, 240, k=5, dropout=False) 29 | self.linear_y = Maxout(10, 50, k=5, dropout=False) 30 | self.linear_1 = Maxout(290, 240, k=4) 31 | self.linear_2 = nn.Linear(240, 1) 32 | 33 | # "The discriminator maps x to a maxout layer with 240 units and 5 pieces, and y to a maxout layer with 50 units 34 | # and 5 pieces. Both of the hidden layers mapped to a joint maxout layer with 240 units and 4 pieces before 35 | # being fed to the sigmoid layer." 36 | 37 | def forward(self, x, y): 38 | x = torch.cat((self.linear_x(x), self.linear_y(y)), dim=1) 39 | x = F.dropout(self.linear_1(x), p=0.5) 40 | return nn.Sigmoid()(self.linear_2(x)) 41 | 42 | 43 | class Maxout(nn.Module): 44 | def __init__(self, in_features, out_features, k=2, dropout=True, p=0.5): 45 | super(Maxout, self).__init__() 46 | model = [nn.Dropout(p)] if dropout else [] 47 | model += [nn.Linear(in_features, out_features * k)] 48 | 49 | self.model = nn.Sequential(*model) 50 | self.k = k 51 | 52 | # Note that dropout is used before weight multiplication following 'Maxout Networks' paper. 53 | # "When training with dropout, we perform the element-wise multiplication with the dropout mask immediately 54 | # prior to the multiplication by the weights in all cases-we do not drop inputs to the max operator." - Maxout 55 | # Networks 56 | 57 | def forward(self, x): 58 | x = self.model(x) 59 | x, _ = x.view(x.shape[0], x.shape[1] // self.k, self.k).max(-1) 60 | return x 61 | -------------------------------------------------------------------------------- /generation/cGAN/MNIST_train.py: -------------------------------------------------------------------------------- 1 | from os.path import join 2 | import torch.nn as nn 3 | 4 | 5 | if __name__ == '__main__': 6 | import os 7 | import torch 8 | from torch.utils.data import DataLoader 9 | from torchvision.datasets import MNIST 10 | from torchvision.transforms import Compose, Normalize, ToTensor 11 | from torchvision.utils import save_image 12 | from MNIST_models import Generator, Discriminator 13 | import datetime 14 | 15 | BATCH_SIZE = 16 # Original batch size is 100. 16 | EPOCHS = 25 17 | IMAGE_DIR = './cGAN/checkpoints/MNIST/Image/Training' 18 | IMAGE_SIZE = 28 19 | ITER_DISPLAY = 100 20 | ITER_REPORT = 10 21 | LATENT_DIM = 100 # Original latent dimension is 100. 22 | MODEL_DIR = './cGAN/checkpoints/MNIST/Model' 23 | OUT_CHANNEL = 1 24 | os.makedirs(IMAGE_DIR, exist_ok=True) 25 | os.makedirs(MODEL_DIR, exist_ok=True) 26 | 27 | transform = Compose([ToTensor()]) # The output activation in the generator is sigmoid. Thus we don't normalize 28 | # input to have [-1, 1] but [0, 1]. ToTensor() does it. 29 | dataset = MNIST(root='./datasets', train=True, transform=transform, download=True) 30 | data_loader = DataLoader(dataset=dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=1) 31 | 32 | # They don't refer about parameter initialization. 33 | D = Discriminator() 34 | G = Generator() 35 | print(D) 36 | print(G) 37 | 38 | optim_D = torch.optim.Adam(D.parameters(), lr=2e-4, betas=(0.5, 0.9)) 39 | optim_G = torch.optim.Adam(G.parameters(), lr=2e-4, betas=(0.5, 0.9)) 40 | 41 | st = datetime.datetime.now() 42 | iter = 0 43 | for epoch in range(EPOCHS): 44 | for i, data in enumerate(data_loader): 45 | iter += 1 46 | 47 | real, label = data[0].view(BATCH_SIZE, -1), data[1] 48 | one_hot = torch.zeros(BATCH_SIZE, 10) 49 | one_hot.scatter_(dim=1, index=label.view(BATCH_SIZE, 1), src=torch.ones(BATCH_SIZE, 1)) 50 | 51 | z = torch.rand(BATCH_SIZE, LATENT_DIM) 52 | fake = G(z, one_hot) 53 | 54 | loss_D = -torch.mean(torch.log(D(real, one_hot)) + torch.log(1 - D(fake.detach(), one_hot))) 55 | optim_D.zero_grad() 56 | loss_D.backward() 57 | optim_D.step() 58 | 59 | loss_G = -torch.mean(torch.log(D(fake, one_hot))) # Non saturating loss 60 | # For saturaing loss, loss_G = torch.mean(torch.log(1-D(fake))) 61 | optim_G.zero_grad() 62 | loss_G.backward() 63 | optim_G.step() 64 | 65 | if iter % ITER_DISPLAY == 0: 66 | fake = fake.view(BATCH_SIZE, OUT_CHANNEL, IMAGE_SIZE, IMAGE_SIZE) 67 | real = real.view(BATCH_SIZE, OUT_CHANNEL, IMAGE_SIZE, IMAGE_SIZE) 68 | save_image(fake, IMAGE_DIR + '/{}_fake.png'.format(epoch + 1), nrow=4, normalize=True) 69 | save_image(real, IMAGE_DIR + '/{}_real.png'.format(epoch + 1), nrow=4, normalize=True) 70 | 71 | if iter % ITER_REPORT == 0: 72 | print("Epoch: {} Iter: {} Loss D: {:.{prec}} Loss G: {:.{prec}}" 73 | .format(epoch + 1, iter, loss_D.detach().item(), loss_G.detach().item(), prec=4)) 74 | 75 | torch.save(D, join(MODEL_DIR, 'Latest_D.pt')) 76 | torch.save(G, join(MODEL_DIR, 'Latest_G.pt')) 77 | print("Total time taken: ", datetime.datetime.now() - st) 78 | --------------------------------------------------------------------------------