├── .gitignore
├── LICENSE
├── README.md
├── main.py
├── misc.py
└── models
├── AlexNet.py
├── DenseNet.py
├── GoogleNet.py
├── LeNet.py
├── ResNet.py
├── VGG.py
├── WideResNet.py
└── __init__.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.idea/
2 | .pyc
3 | *.pyc
4 | *data/
5 | *__pycache__/
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [2018] [IvoryCandy]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pytorch-cifar10
2 | Personal practice on CIFAR10 with PyTorch
3 | Inspired by [pytorch-cifar](https://github.com/kuangliu/pytorch-cifar) by [kuangliu](https://github.com/kuangliu).
4 |
5 | ## Introduction
6 | The CIFAR-10 dataset consists of 60000 32x32 colour images in 10 classes, with 6000 images per class.
7 | There are 50000 training images and 10000 test images.
8 |
9 | The dataset is divided into five training batches and one test batch, each with 10000 images.
10 | The test batch contains exactly 1000 randomly-selected images from each class.
11 | The training batches contain the remaining images in random order, but some training batches may contain more images from one class than another.
12 | Between them, the training batches contain exactly 5000 images from each class.
13 |
14 | ## Requirement
15 | - python3.6
16 | - numpy
17 | - pytorch 0.4.0
18 | - torchvision 0.2.0
19 |
20 | ## Usage
21 | ```bash
22 | python3 main.py
23 | ```
24 | optional arguments:
25 |
26 | --lr default=1e-3 learning rate
27 | --epoch default=200 number of epochs tp train for
28 | --trainBatchSize default=100 training batch size
29 | --testBatchSize default=100 test batch size
30 | ## Configs
31 | __200__ epochs for each run-through,
32 | __500__ batches for each training epoch,
33 | __100__ batches for each validating epoch,
34 | __100__ images for each training and validating batch
35 |
36 | ##### Learning Rate
37 | __1e-3__ for [1,74] epochs
38 | __5e-4__ for [75,149] epochs
39 | __2.5e-4__ for [150,200) epochs
40 |
41 | ## Result
42 | Models | Accuracy | Comments
43 | :---:|:---:|:---:
44 | [LeNet](https://github.com/IvoryCandy/pytorch-cifar10/blob/master/models/LeNet.py) | 67.52% | - - - -
45 | [Alexnet](https://github.com/IvoryCandy/pytorch-cifar10/blob/master/models/AlexNet.py) | 74.74% | Result is far away from my expectation (5%+). Reasons might be inappropriate modification to fit dataset(32x32 images).
46 | [VGG11](https://github.com/IvoryCandy/pytorch-cifar10/blob/master/models/VGG.py) | 87.48% | - - - -
47 | [VGG13](https://github.com/IvoryCandy/pytorch-cifar10/blob/master/models/VGG.py) | 90.17% | - - - -
48 | [VGG16](https://github.com/IvoryCandy/pytorch-cifar10/blob/master/models/VGG.py) | TBD | - - - -
49 | [VGG19](https://github.com/IvoryCandy/pytorch-cifar10/blob/master/models/VGG.py) | TBD | - - - -
50 | [GoogleNet](https://github.com/IvoryCandy/pytorch-cifar10/blob/master/models/GoogleNet.py) | 92.57% | - - - -
51 | [ResNet18](https://github.com/IvoryCandy/pytorch-cifar10/blob/master/models/ResNet.py) | TBD | - - - -
52 | [ResNet34](https://github.com/IvoryCandy/pytorch-cifar10/blob/master/models/ResNet.py) | TBD | - - - -
53 | [ResNet50](https://github.com/IvoryCandy/pytorch-cifar10/blob/master/models/ResNet.py) | TBD | - - - -
54 | [ResNet101](https://github.com/IvoryCandy/pytorch-cifar10/blob/master/models/ResNet.py) | TBD | - - - -
55 | [ResNet152](https://github.com/IvoryCandy/pytorch-cifar10/blob/master/models/ResNet.py) | TBD | - - - -
56 | [DenseNet121](https://github.com/IvoryCandy/pytorch-cifar10/blob/master/models/DenseNet.py) | TBD | - - - -
57 | [DenseNet161](https://github.com/IvoryCandy/pytorch-cifar10/blob/master/models/DenseNet.py) | TBD | - - - -
58 | [DenseNet169](https://github.com/IvoryCandy/pytorch-cifar10/blob/master/models/DenseNet.py) | TBD | - - - -
59 | [DenseNet201](https://github.com/IvoryCandy/pytorch-cifar10/blob/master/models/DenseNet.py) | TBD | - - - -
60 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import torch.optim as optim
2 | import torch.utils.data
3 | import torch.backends.cudnn as cudnn
4 | import torchvision
5 | from torchvision import transforms as transforms
6 | import numpy as np
7 |
8 | import argparse
9 |
10 | from models import *
11 | from misc import progress_bar
12 |
13 |
14 | CLASSES = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
15 |
16 |
17 | def main():
18 | parser = argparse.ArgumentParser(description="cifar-10 with PyTorch")
19 | parser.add_argument('--lr', default=0.001, type=float, help='learning rate')
20 | parser.add_argument('--epoch', default=200, type=int, help='number of epochs tp train for')
21 | parser.add_argument('--trainBatchSize', default=100, type=int, help='training batch size')
22 | parser.add_argument('--testBatchSize', default=100, type=int, help='testing batch size')
23 | parser.add_argument('--cuda', default=torch.cuda.is_available(), type=bool, help='whether cuda is in use')
24 | args = parser.parse_args()
25 |
26 | solver = Solver(args)
27 | solver.run()
28 |
29 |
30 | class Solver(object):
31 | def __init__(self, config):
32 | self.model = None
33 | self.lr = config.lr
34 | self.epochs = config.epoch
35 | self.train_batch_size = config.trainBatchSize
36 | self.test_batch_size = config.testBatchSize
37 | self.criterion = None
38 | self.optimizer = None
39 | self.scheduler = None
40 | self.device = None
41 | self.cuda = config.cuda
42 | self.train_loader = None
43 | self.test_loader = None
44 |
45 | def load_data(self):
46 | train_transform = transforms.Compose([transforms.RandomHorizontalFlip(), transforms.ToTensor()])
47 | test_transform = transforms.Compose([transforms.ToTensor()])
48 | train_set = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=train_transform)
49 | self.train_loader = torch.utils.data.DataLoader(dataset=train_set, batch_size=self.train_batch_size, shuffle=True)
50 | test_set = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=test_transform)
51 | self.test_loader = torch.utils.data.DataLoader(dataset=test_set, batch_size=self.test_batch_size, shuffle=False)
52 |
53 | def load_model(self):
54 | if self.cuda:
55 | self.device = torch.device('cuda')
56 | cudnn.benchmark = True
57 | else:
58 | self.device = torch.device('cpu')
59 |
60 | # self.model = LeNet().to(self.device)
61 | # self.model = AlexNet().to(self.device)
62 | # self.model = VGG11().to(self.device)
63 | # self.model = VGG13().to(self.device)
64 | # self.model = VGG16().to(self.device)
65 | # self.model = VGG19().to(self.device)
66 | # self.model = GoogLeNet().to(self.device)
67 | # self.model = resnet18().to(self.device)
68 | # self.model = resnet34().to(self.device)
69 | # self.model = resnet50().to(self.device)
70 | # self.model = resnet101().to(self.device)
71 | # self.model = resnet152().to(self.device)
72 | # self.model = DenseNet121().to(self.device)
73 | # self.model = DenseNet161().to(self.device)
74 | # self.model = DenseNet169().to(self.device)
75 | # self.model = DenseNet201().to(self.device)
76 | self.model = WideResNet(depth=28, num_classes=10).to(self.device)
77 |
78 | self.optimizer = optim.Adam(self.model.parameters(), lr=self.lr)
79 | self.scheduler = optim.lr_scheduler.MultiStepLR(self.optimizer, milestones=[75, 150], gamma=0.5)
80 | self.criterion = nn.CrossEntropyLoss().to(self.device)
81 |
82 | def train(self):
83 | print("train:")
84 | self.model.train()
85 | train_loss = 0
86 | train_correct = 0
87 | total = 0
88 |
89 | for batch_num, (data, target) in enumerate(self.train_loader):
90 | data, target = data.to(self.device), target.to(self.device)
91 | self.optimizer.zero_grad()
92 | output = self.model(data)
93 | loss = self.criterion(output, target)
94 | loss.backward()
95 | self.optimizer.step()
96 | train_loss += loss.item()
97 | prediction = torch.max(output, 1) # second param "1" represents the dimension to be reduced
98 | total += target.size(0)
99 |
100 | # train_correct incremented by one if predicted right
101 | train_correct += np.sum(prediction[1].cpu().numpy() == target.cpu().numpy())
102 |
103 | progress_bar(batch_num, len(self.train_loader), 'Loss: %.4f | Acc: %.3f%% (%d/%d)'
104 | % (train_loss / (batch_num + 1), 100. * train_correct / total, train_correct, total))
105 |
106 | return train_loss, train_correct / total
107 |
108 | def test(self):
109 | print("test:")
110 | self.model.eval()
111 | test_loss = 0
112 | test_correct = 0
113 | total = 0
114 |
115 | with torch.no_grad():
116 | for batch_num, (data, target) in enumerate(self.test_loader):
117 | data, target = data.to(self.device), target.to(self.device)
118 | output = self.model(data)
119 | loss = self.criterion(output, target)
120 | test_loss += loss.item()
121 | prediction = torch.max(output, 1)
122 | total += target.size(0)
123 | test_correct += np.sum(prediction[1].cpu().numpy() == target.cpu().numpy())
124 |
125 | progress_bar(batch_num, len(self.test_loader), 'Loss: %.4f | Acc: %.3f%% (%d/%d)'
126 | % (test_loss / (batch_num + 1), 100. * test_correct / total, test_correct, total))
127 |
128 | return test_loss, test_correct / total
129 |
130 | def save(self):
131 | model_out_path = "model.pth"
132 | torch.save(self.model, model_out_path)
133 | print("Checkpoint saved to {}".format(model_out_path))
134 |
135 | def run(self):
136 | self.load_data()
137 | self.load_model()
138 | accuracy = 0
139 | for epoch in range(1, self.epochs + 1):
140 | self.scheduler.step(epoch)
141 | print("\n===> epoch: %d/200" % epoch)
142 | train_result = self.train()
143 | print(train_result)
144 | test_result = self.test()
145 | accuracy = max(accuracy, test_result[1])
146 | if epoch == self.epochs:
147 | print("===> BEST ACC. PERFORMANCE: %.3f%%" % (accuracy * 100))
148 | self.save()
149 |
150 |
151 | if __name__ == '__main__':
152 | main()
153 |
--------------------------------------------------------------------------------
/misc.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import time
3 |
4 |
5 | TOTAL_BAR_LENGTH = 80
6 | LAST_T = time.time()
7 | BEGIN_T = LAST_T
8 |
9 |
10 | def progress_bar(current, total, msg=None):
11 |
12 | global LAST_T, BEGIN_T
13 | if current == 0:
14 | BEGIN_T = time.time() # Reset for new bar.
15 |
16 | current_len = int(TOTAL_BAR_LENGTH * (current + 1) / total)
17 | rest_len = int(TOTAL_BAR_LENGTH - current_len) - 1
18 |
19 | sys.stdout.write(' %d/%d' % (current + 1, total))
20 | sys.stdout.write(' [')
21 | for i in range(current_len):
22 | sys.stdout.write('=')
23 | sys.stdout.write('>')
24 | for i in range(rest_len):
25 | sys.stdout.write('.')
26 | sys.stdout.write(']')
27 |
28 | current_time = time.time()
29 | step_time = current_time - LAST_T
30 | LAST_T = current_time
31 | total_time = current_time - BEGIN_T
32 |
33 | time_used = ' Step: %s' % format_time(step_time)
34 | time_used += ' | Tot: %s' % format_time(total_time)
35 | if msg:
36 | time_used += ' | ' + msg
37 |
38 | msg = time_used
39 | sys.stdout.write(msg)
40 |
41 | if current < total - 1:
42 | sys.stdout.write('\r')
43 | else:
44 | sys.stdout.write('\n')
45 | sys.stdout.flush()
46 |
47 |
48 | def format_time(seconds):
49 | days = int(seconds / 3600/24)
50 | seconds = seconds - days*3600*24
51 | hours = int(seconds / 3600)
52 | seconds = seconds - hours*3600
53 | minutes = int(seconds / 60)
54 | seconds = seconds - minutes*60
55 | secondsf = int(seconds)
56 | seconds = seconds - secondsf
57 | millis = int(seconds*1000)
58 |
59 | f = ''
60 | i = 1
61 | if days > 0:
62 | f += str(days) + 'D'
63 | i += 1
64 | if hours > 0 and i <= 2:
65 | f += str(hours) + 'h'
66 | i += 1
67 | if minutes > 0 and i <= 2:
68 | f += str(minutes) + 'm'
69 | i += 1
70 | if secondsf > 0 and i <= 2:
71 | f += str(secondsf) + 's'
72 | i += 1
73 | if millis > 0 and i <= 2:
74 | f += str(millis) + 'ms'
75 | i += 1
76 | if f == '':
77 | f = '0ms'
78 | return f
79 |
--------------------------------------------------------------------------------
/models/AlexNet.py:
--------------------------------------------------------------------------------
1 | import torch.nn as nn
2 |
3 | '''
4 | modified to fit dataset size
5 | '''
6 | NUM_CLASSES = 10
7 |
8 |
9 | class AlexNet(nn.Module):
10 | def __init__(self, num_classes=NUM_CLASSES):
11 | super(AlexNet, self).__init__()
12 | self.features = nn.Sequential(
13 | nn.Conv2d(3, 64, kernel_size=3, stride=2, padding=1),
14 | nn.ReLU(inplace=True),
15 | nn.MaxPool2d(kernel_size=2),
16 | nn.Conv2d(64, 192, kernel_size=3, padding=1),
17 | nn.ReLU(inplace=True),
18 | nn.MaxPool2d(kernel_size=2),
19 | nn.Conv2d(192, 384, kernel_size=3, padding=1),
20 | nn.ReLU(inplace=True),
21 | nn.Conv2d(384, 256, kernel_size=3, padding=1),
22 | nn.ReLU(inplace=True),
23 | nn.Conv2d(256, 256, kernel_size=3, padding=1),
24 | nn.ReLU(inplace=True),
25 | nn.MaxPool2d(kernel_size=2),
26 | )
27 | self.classifier = nn.Sequential(
28 | nn.Dropout(),
29 | nn.Linear(256 * 2 * 2, 4096),
30 | nn.ReLU(inplace=True),
31 | nn.Dropout(),
32 | nn.Linear(4096, 4096),
33 | nn.ReLU(inplace=True),
34 | nn.Linear(4096, num_classes),
35 | )
36 |
37 | def forward(self, x):
38 | x = self.features(x)
39 | x = x.view(x.size(0), 256 * 2 * 2)
40 | x = self.classifier(x)
41 | return x
42 |
--------------------------------------------------------------------------------
/models/DenseNet.py:
--------------------------------------------------------------------------------
1 | import math
2 | import torch
3 | import torch.nn as nn
4 | import torch.nn.functional as func
5 |
6 |
7 | class Bottleneck(nn.Module):
8 | def __init__(self, in_planes, growth_rate):
9 | super(Bottleneck, self).__init__()
10 | self.bn1 = nn.BatchNorm2d(in_planes)
11 | self.conv1 = nn.Conv2d(in_planes, 4 * growth_rate, kernel_size=1, bias=False)
12 | self.bn2 = nn.BatchNorm2d(4 * growth_rate)
13 | self.conv2 = nn.Conv2d(4 * growth_rate, growth_rate, kernel_size=3, padding=1, bias=False)
14 |
15 | def forward(self, x):
16 | y = self.conv1(func.relu(self.bn1(x)))
17 | y = self.conv2(func.relu(self.bn2(y)))
18 | x = torch.cat([y, x], 1)
19 | return x
20 |
21 |
22 | class Transition(nn.Module):
23 | def __init__(self, in_planes, out_planes):
24 | super(Transition, self).__init__()
25 | self.bn = nn.BatchNorm2d(in_planes)
26 | self.conv = nn.Conv2d(in_planes, out_planes, kernel_size=1, bias=False)
27 |
28 | def forward(self, x):
29 | x = self.conv(func.relu(self.bn(x)))
30 | x = func.avg_pool2d(x, 2)
31 | return x
32 |
33 |
34 | class DenseNet(nn.Module):
35 | def __init__(self, block, num_block, growth_rate=12, reduction=0.5, num_classes=10):
36 | super(DenseNet, self).__init__()
37 | self.growth_rate = growth_rate
38 |
39 | num_planes = 2 * growth_rate
40 | self.conv1 = nn.Conv2d(3, num_planes, kernel_size=3, padding=1, bias=False)
41 |
42 | self.dense1 = self._make_dense_layers(block, num_planes, num_block[0])
43 | num_planes += num_block[0] * growth_rate
44 | out_planes = int(math.floor(num_planes * reduction))
45 | self.trans1 = Transition(num_planes, out_planes)
46 | num_planes = out_planes
47 |
48 | self.dense2 = self._make_dense_layers(block, num_planes, num_block[1])
49 | num_planes += num_block[1] * growth_rate
50 | out_planes = int(math.floor(num_planes * reduction))
51 | self.trans2 = Transition(num_planes, out_planes)
52 | num_planes = out_planes
53 |
54 | self.dense3 = self._make_dense_layers(block, num_planes, num_block[2])
55 | num_planes += num_block[2] * growth_rate
56 | out_planes = int(math.floor(num_planes * reduction))
57 | self.trans3 = Transition(num_planes, out_planes)
58 | num_planes = out_planes
59 |
60 | self.dense4 = self._make_dense_layers(block, num_planes, num_block[3])
61 | num_planes += num_block[3] * growth_rate
62 |
63 | self.bn = nn.BatchNorm2d(num_planes)
64 | self.linear = nn.Linear(num_planes, num_classes)
65 |
66 | def _make_dense_layers(self, block, in_planes, num_block):
67 | layers = []
68 | for i in range(num_block):
69 | layers.append(block(in_planes, self.growth_rate))
70 | in_planes += self.growth_rate
71 | return nn.Sequential(*layers)
72 |
73 | def forward(self, x):
74 | x = self.conv1(x)
75 | x = self.trans1(self.dense1(x))
76 | x = self.trans2(self.dense2(x))
77 | x = self.trans3(self.dense3(x))
78 | x = self.dense4(x)
79 | x = func.avg_pool2d(func.relu(self.bn(x)), 4)
80 | x = x.view(x.size(0), -1)
81 | x = self.linear(x)
82 | return x
83 |
84 |
85 | def DenseNet121():
86 | return DenseNet(Bottleneck, [6, 12, 24, 16], growth_rate=32)
87 |
88 |
89 | def DenseNet169():
90 | return DenseNet(Bottleneck, [6, 12, 32, 32], growth_rate=32)
91 |
92 |
93 | def DenseNet201():
94 | return DenseNet(Bottleneck, [6, 12, 48, 32], growth_rate=32)
95 |
96 |
97 | def DenseNet161():
98 | return DenseNet(Bottleneck, [6, 12, 36, 24], growth_rate=48)
99 |
100 |
101 | def densenet_cifar():
102 | return DenseNet(Bottleneck, [6, 12, 24, 16], growth_rate=12)
103 |
--------------------------------------------------------------------------------
/models/GoogleNet.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 |
4 |
5 | class Inception(nn.Module):
6 | def __init__(self, in_planes, kernel_1_x, kernel_3_in, kernel_3_x, kernel_5_in, kernel_5_x, pool_planes):
7 | super(Inception, self).__init__()
8 | # 1x1 conv branch
9 | self.b1 = nn.Sequential(
10 | nn.Conv2d(in_planes, kernel_1_x, kernel_size=1),
11 | nn.BatchNorm2d(kernel_1_x),
12 | nn.ReLU(True),
13 | )
14 |
15 | # 1x1 conv -> 3x3 conv branch
16 | self.b2 = nn.Sequential(
17 | nn.Conv2d(in_planes, kernel_3_in, kernel_size=1),
18 | nn.BatchNorm2d(kernel_3_in),
19 | nn.ReLU(True),
20 | nn.Conv2d(kernel_3_in, kernel_3_x, kernel_size=3, padding=1),
21 | nn.BatchNorm2d(kernel_3_x),
22 | nn.ReLU(True),
23 | )
24 |
25 | # 1x1 conv -> 5x5 conv branch
26 | self.b3 = nn.Sequential(
27 | nn.Conv2d(in_planes, kernel_5_in, kernel_size=1),
28 | nn.BatchNorm2d(kernel_5_in),
29 | nn.ReLU(True),
30 | nn.Conv2d(kernel_5_in, kernel_5_x, kernel_size=3, padding=1),
31 | nn.BatchNorm2d(kernel_5_x),
32 | nn.ReLU(True),
33 | nn.Conv2d(kernel_5_x, kernel_5_x, kernel_size=3, padding=1),
34 | nn.BatchNorm2d(kernel_5_x),
35 | nn.ReLU(True),
36 | )
37 |
38 | # 3x3 pool -> 1x1 conv branch
39 | self.b4 = nn.Sequential(
40 | nn.MaxPool2d(3, stride=1, padding=1),
41 | nn.Conv2d(in_planes, pool_planes, kernel_size=1),
42 | nn.BatchNorm2d(pool_planes),
43 | nn.ReLU(True),
44 | )
45 |
46 | def forward(self, x):
47 | y1 = self.b1(x)
48 | y2 = self.b2(x)
49 | y3 = self.b3(x)
50 | y4 = self.b4(x)
51 | return torch.cat([y1,y2,y3,y4], 1)
52 |
53 |
54 | class GoogLeNet(nn.Module):
55 | def __init__(self):
56 | super(GoogLeNet, self).__init__()
57 | self.pre_layers = nn.Sequential(
58 | nn.Conv2d(3, 192, kernel_size=3, padding=1),
59 | nn.BatchNorm2d(192),
60 | nn.ReLU(True),
61 | )
62 |
63 | self.a3 = Inception(192, 64, 96, 128, 16, 32, 32)
64 | self.b3 = Inception(256, 128, 128, 192, 32, 96, 64)
65 |
66 | self.max_pool = nn.MaxPool2d(3, stride=2, padding=1)
67 |
68 | self.a4 = Inception(480, 192, 96, 208, 16, 48, 64)
69 | self.b4 = Inception(512, 160, 112, 224, 24, 64, 64)
70 | self.c4 = Inception(512, 128, 128, 256, 24, 64, 64)
71 | self.d4 = Inception(512, 112, 144, 288, 32, 64, 64)
72 | self.e4 = Inception(528, 256, 160, 320, 32, 128, 128)
73 |
74 | self.a5 = Inception(832, 256, 160, 320, 32, 128, 128)
75 | self.b5 = Inception(832, 384, 192, 384, 48, 128, 128)
76 |
77 | self.avgpool = nn.AvgPool2d(8, stride=1)
78 | self.linear = nn.Linear(1024, 10)
79 |
80 | def forward(self, x):
81 | x = self.pre_layers(x)
82 | x = self.a3(x)
83 | x = self.b3(x)
84 | x = self.max_pool(x)
85 | x = self.a4(x)
86 | x = self.b4(x)
87 | x = self.c4(x)
88 | x = self.d4(x)
89 | x = self.e4(x)
90 | x = self.max_pool(x)
91 | x = self.a5(x)
92 | x = self.b5(x)
93 | x = self.avgpool(x)
94 | x = x.view(x.size(0), -1)
95 | x = self.linear(x)
96 | return x
97 |
--------------------------------------------------------------------------------
/models/LeNet.py:
--------------------------------------------------------------------------------
1 | import torch.nn as nn
2 | import torch.nn.functional as func
3 |
4 |
5 | class LeNet(nn.Module):
6 | def __init__(self):
7 | super(LeNet, self).__init__()
8 | self.conv1 = nn.Conv2d(3, 6, kernel_size=5)
9 | self.conv2 = nn.Conv2d(6, 16, kernel_size=5)
10 | self.fc1 = nn.Linear(16*5*5, 120)
11 | self.fc2 = nn.Linear(120, 84)
12 | self.fc3 = nn.Linear(84, 10)
13 |
14 | def forward(self, x):
15 | x = func.relu(self.conv1(x))
16 | x = func.max_pool2d(x, 2)
17 | x = func.relu(self.conv2(x))
18 | x = func.max_pool2d(x, 2)
19 | x = x.view(x.size(0), -1)
20 | x = func.relu(self.fc1(x))
21 | x = func.relu(self.fc2(x))
22 | x = self.fc3(x)
23 | return x
--------------------------------------------------------------------------------
/models/ResNet.py:
--------------------------------------------------------------------------------
1 | import torch.nn as nn
2 | import math
3 |
4 |
5 | def conv3x3(in_planes, out_planes, stride=1):
6 | # 3x3 convolution with padding
7 | return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False)
8 |
9 |
10 | class BasicBlock(nn.Module):
11 | expansion = 1
12 |
13 | def __init__(self, inplanes, planes, stride=1, downsample=None):
14 | super(BasicBlock, self).__init__()
15 | self.conv1 = conv3x3(inplanes, planes, stride)
16 | self.bn1 = nn.BatchNorm2d(planes)
17 | self.relu = nn.ReLU(inplace=True)
18 | self.conv2 = conv3x3(planes, planes)
19 | self.bn2 = nn.BatchNorm2d(planes)
20 | self.downsample = downsample
21 | self.stride = stride
22 |
23 | def forward(self, x):
24 | residual = x
25 |
26 | x = self.conv1(x)
27 | x = self.bn1(x)
28 | x = self.relu(x)
29 |
30 | x = self.conv2(x)
31 | x = self.bn2(x)
32 |
33 | if self.downsample is not None:
34 | residual = self.downsample(x)
35 |
36 | x += residual
37 | x = self.relu(x)
38 |
39 | return x
40 |
41 |
42 | class Bottleneck(nn.Module):
43 | expansion = 4
44 |
45 | def __init__(self, inplanes, planes, stride=1, downsample=None):
46 | super(Bottleneck, self).__init__()
47 | self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
48 | self.bn1 = nn.BatchNorm2d(planes)
49 | self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)
50 | self.bn2 = nn.BatchNorm2d(planes)
51 | self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)
52 | self.bn3 = nn.BatchNorm2d(planes * 4)
53 | self.relu = nn.ReLU(inplace=True)
54 | self.downsample = downsample
55 | self.stride = stride
56 |
57 | def forward(self, x):
58 | residual = x
59 |
60 | x = self.conv1(x)
61 | x = self.bn1(x)
62 | x = self.relu(x)
63 |
64 | x = self.conv2(x)
65 | x = self.bn2(x)
66 | x = self.relu(x)
67 |
68 | x = self.conv3(x)
69 | x = self.bn3(x)
70 |
71 | if self.downsample is not None:
72 | residual = self.downsample(x)
73 |
74 | x += residual
75 | x = self.relu(x)
76 |
77 | return x
78 |
79 |
80 | class ResNet(nn.Module):
81 |
82 | def __init__(self, block, layers, num_classes=10):
83 | self.inplanes = 64
84 | super(ResNet, self).__init__()
85 | self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)
86 | self.bn1 = nn.BatchNorm2d(64)
87 | self.relu = nn.ReLU(inplace=True)
88 | self.layer1 = self._make_layer(block, 64, layers[0])
89 | self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
90 | self.layer3 = self._make_layer(block, 256, layers[2], stride=2)
91 | self.layer4 = self._make_layer(block, 512, layers[3], stride=2)
92 | self.avgpool = nn.AvgPool2d(kernel_size=4)
93 | self.fc = nn.Linear(512 * block.expansion, num_classes)
94 |
95 | for m in self.modules():
96 | if isinstance(m, nn.Conv2d):
97 | n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
98 | m.weight.data.normal_(0, math.sqrt(2. / n))
99 | elif isinstance(m, nn.BatchNorm2d):
100 | m.weight.data.fill_(1)
101 | m.bias.data.zero_()
102 |
103 | def _make_layer(self, block, planes, blocks, stride=1):
104 | downsample = None
105 | if stride != 1 or self.inplanes != planes * block.expansion:
106 | downsample = nn.Sequential(
107 | nn.Conv2d(self.inplanes, planes * block.expansion, kernel_size=1, stride=stride, bias=False),
108 | nn.BatchNorm2d(planes * block.expansion),
109 | )
110 |
111 | layers = []
112 | layers.append(block(self.inplanes, planes, stride, downsample))
113 | self.inplanes = planes * block.expansion
114 | for i in range(1, blocks):
115 | layers.append(block(self.inplanes, planes))
116 | return nn.Sequential(*layers)
117 |
118 | def forward(self, x):
119 | x = self.conv1(x)
120 | x = self.bn1(x)
121 | x = self.relu(x)
122 |
123 | x = self.layer1(x)
124 | x = self.layer2(x)
125 | x = self.layer3(x)
126 | x = self.layer4(x)
127 |
128 | x = self.avgpool(x)
129 | x = x.view(x.size(0), -1)
130 | x = self.fc(x)
131 |
132 | return x
133 |
134 |
135 | def resnet18(**kwargs):
136 | return ResNet(BasicBlock, [2, 2, 2, 2], **kwargs)
137 |
138 |
139 | def resnet34(**kwargs):
140 | return ResNet(BasicBlock, [3, 4, 6, 3], **kwargs)
141 |
142 |
143 | def resnet50(**kwargs):
144 | return ResNet(Bottleneck, [3, 4, 6, 3], **kwargs)
145 |
146 |
147 | def resnet101(**kwargs):
148 | return ResNet(Bottleneck, [3, 4, 23, 3], **kwargs)
149 |
150 |
151 | def resnet152(**kwargs):
152 | return ResNet(Bottleneck, [3, 8, 36, 3], **kwargs)
153 |
--------------------------------------------------------------------------------
/models/VGG.py:
--------------------------------------------------------------------------------
1 | import torch.nn as nn
2 |
3 |
4 | cfg = {
5 | 'VGG11': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
6 | 'VGG13': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
7 | 'VGG16': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'],
8 | 'VGG19': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'],
9 | }
10 |
11 |
12 | class VGG(nn.Module):
13 | def __init__(self, vgg_name):
14 | super(VGG, self).__init__()
15 | self.features = self._make_layers(cfg[vgg_name])
16 | self.classifier = nn.Linear(512, 10)
17 |
18 | def forward(self, x):
19 | out = self.features(x)
20 | out = out.view(out.size(0), -1)
21 | out = self.classifier(out)
22 | return out
23 |
24 | def _make_layers(self, cfg):
25 | layers = []
26 | in_channels = 3
27 | for x in cfg:
28 | if x == 'M':
29 | layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
30 | else:
31 | layers += [nn.Conv2d(in_channels, x, kernel_size=3, padding=1),
32 | nn.BatchNorm2d(x),
33 | nn.ReLU(inplace=True)]
34 | in_channels = x
35 | layers += [nn.AvgPool2d(kernel_size=1, stride=1)]
36 | return nn.Sequential(*layers)
37 |
38 |
39 | def VGG11():
40 | return VGG('VGG11')
41 |
42 |
43 | def VGG13():
44 | return VGG('VGG13')
45 |
46 |
47 | def VGG16():
48 | return VGG('VGG16')
49 |
50 |
51 | def VGG19():
52 | return VGG('VGG19')
53 |
--------------------------------------------------------------------------------
/models/WideResNet.py:
--------------------------------------------------------------------------------
1 | import math
2 | import torch
3 | import torch.nn as nn
4 | import torch.nn.functional as F
5 |
6 |
7 | class BasicBlock(nn.Module):
8 | def __init__(self, in_planes, out_planes, stride, drop_rate=0.0):
9 | super(BasicBlock, self).__init__()
10 | self.bn1 = nn.BatchNorm2d(in_planes)
11 | self.relu1 = nn.ReLU(inplace=True)
12 | self.conv1 = nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,
13 | padding=1, bias=False)
14 | self.bn2 = nn.BatchNorm2d(out_planes)
15 | self.relu2 = nn.ReLU(inplace=True)
16 | self.conv2 = nn.Conv2d(out_planes, out_planes, kernel_size=3, stride=1,
17 | padding=1, bias=False)
18 | self.droprate = drop_rate
19 | self.equalInOut = (in_planes == out_planes)
20 | self.convShortcut = (not self.equalInOut) and nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride,
21 | padding=0, bias=False) or None
22 |
23 | def forward(self, x):
24 | if not self.equalInOut:
25 | x = self.relu1(self.bn1(x))
26 | else:
27 | out = self.relu1(self.bn1(x))
28 |
29 | out = self.relu2(self.bn2(self.conv1(out if self.equalInOut else x)))
30 | if self.droprate > 0:
31 | out = F.dropout(out, p=self.droprate, training=self.training)
32 | out = self.conv2(out)
33 | return torch.add(x if self.equalInOut else self.convShortcut(x), out)
34 |
35 |
36 | class NetworkBlock(nn.Module):
37 | def __init__(self, nb_layers, in_planes, out_planes, block, stride, dropRate=0.0):
38 | super(NetworkBlock, self).__init__()
39 | self.layer = self._make_layer(block, in_planes, out_planes, nb_layers, stride, dropRate)
40 |
41 | @staticmethod
42 | def _make_layer(block, in_planes, out_planes, nb_layers, stride, dropRate):
43 | layers = []
44 | for i in range(nb_layers):
45 | layers.append(block(i == 0 and in_planes or out_planes, out_planes, i == 0 and stride or 1, dropRate))
46 | return nn.Sequential(*layers)
47 |
48 | def forward(self, x):
49 | return self.layer(x)
50 |
51 |
52 | class WideResNet(nn.Module):
53 | def __init__(self, depth, num_classes, widen_factor=1, drop_rate=0.0):
54 | super(WideResNet, self).__init__()
55 | n_channels = [16, 16 * widen_factor, 32 * widen_factor, 64 * widen_factor]
56 | assert ((depth - 4) % 6 == 0)
57 | n = int((depth - 4) / 6)
58 | block = BasicBlock
59 | # 1st conv before any network block
60 | self.conv1 = nn.Conv2d(3, n_channels[0], kernel_size=3, stride=1,
61 | padding=1, bias=False)
62 | # 1st block
63 | self.block1 = NetworkBlock(n, n_channels[0], n_channels[1], block, 1, drop_rate)
64 | # 2nd block
65 | self.block2 = NetworkBlock(n, n_channels[1], n_channels[2], block, 2, drop_rate)
66 | # 3rd block
67 | self.block3 = NetworkBlock(n, n_channels[2], n_channels[3], block, 2, drop_rate)
68 | # global average pooling and classifier
69 | self.bn1 = nn.BatchNorm2d(n_channels[3])
70 | self.relu = nn.ReLU(inplace=True)
71 | self.fc = nn.Linear(n_channels[3], num_classes)
72 | self.nChannels = n_channels[3]
73 |
74 | for m in self.modules():
75 | if isinstance(m, nn.Conv2d):
76 | n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
77 | m.weight.data.normal_(0, math.sqrt(2. / n))
78 | elif isinstance(m, nn.BatchNorm2d):
79 | m.weight.data.fill_(1)
80 | m.bias.data.zero_()
81 | elif isinstance(m, nn.Linear):
82 | m.bias.data.zero_()
83 |
84 | def forward(self, x):
85 | out = self.conv1(x)
86 | out = self.block1(out)
87 | out = self.block2(out)
88 | out = self.block3(out)
89 | out = self.relu(self.bn1(out))
90 | out = F.avg_pool2d(out, 8)
91 | out = out.view(-1, self.nChannels)
92 | return self.fc(out)
93 |
--------------------------------------------------------------------------------
/models/__init__.py:
--------------------------------------------------------------------------------
1 | from .AlexNet import *
2 | from .VGG import *
3 | from .ResNet import *
4 | from .LeNet import *
5 | from .DenseNet import *
6 | from .GoogleNet import *
7 | from .WideResNet import *
8 |
--------------------------------------------------------------------------------