import math import torch import torch.nn as nn import torch.nn.functional as F ## Basic CNN ## class LeNet_F(nn.Module): def __init__(self, num_inp, num_out): super(LeNet_F, self).__init__() self._params = nn.ParameterDict({ 'w1': nn.Parameter(torch.zeros(20, num_inp, 5, 5)), 'b1': nn.Parameter(torch.zeros(20)), 'w2': nn.Parameter(torch.zeros(50, 20, 5, 5)), 'b2': nn.Parameter(torch.zeros(50)), #'w3': nn.Parameter(torch.zeros(500,4*4*50)), #num_imp=1 'w3': nn.Parameter(torch.zeros(500,5*5*50)), #num_imp=3 'b3': nn.Parameter(torch.zeros(500)), 'w4': nn.Parameter(torch.zeros(num_out, 500)), 'b4': nn.Parameter(torch.zeros(num_out)) }) self.initialize() def initialize(self): nn.init.kaiming_uniform_(self._params["w1"], a=math.sqrt(5)) nn.init.kaiming_uniform_(self._params["w2"], a=math.sqrt(5)) nn.init.kaiming_uniform_(self._params["w3"], a=math.sqrt(5)) nn.init.kaiming_uniform_(self._params["w4"], a=math.sqrt(5)) def forward(self, x): #print("Start Shape ", x.shape) out = F.relu(F.conv2d(input=x, weight=self._params["w1"], bias=self._params["b1"])) #print("Shape ", out.shape) out = F.max_pool2d(out, 2) #print("Shape ", out.shape) out = F.relu(F.conv2d(input=out, weight=self._params["w2"], bias=self._params["b2"])) #print("Shape ", out.shape) out = F.max_pool2d(out, 2) #print("Shape ", out.shape) out = out.view(out.size(0), -1) #print("Shape ", out.shape) out = F.relu(F.linear(out, self._params["w3"], self._params["b3"])) #print("Shape ", out.shape) out = F.linear(out, self._params["w4"], self._params["b4"]) #print("Shape ", out.shape) #return F.log_softmax(out, dim=1) return out def __getitem__(self, key): return self._params[key] def __str__(self): return "LeNet" class LeNet(nn.Module): def __init__(self, num_inp, num_out): super(LeNet, self).__init__() self.conv1 = nn.Conv2d(num_inp, 20, 5) self.pool = nn.MaxPool2d(2, 2) self.conv2 = nn.Conv2d(20, 50, 5) self.pool2 = nn.MaxPool2d(2, 2) self.fc1 = nn.Linear(5*5*50, 500) self.fc2 = nn.Linear(500, num_out) def forward(self, x): x = self.pool(F.relu(self.conv1(x))) x = self.pool2(F.relu(self.conv2(x))) x = x.view(x.size(0), -1) x = F.relu(self.fc1(x)) x = self.fc2(x) return x def __str__(self): return "LeNet" ## MobileNetv2 ## def _make_divisible(v, divisor, min_value=None): """ This function is taken from the original tf repo. It ensures that all layers have a channel number that is divisible by 8 It can be seen here: https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py :param v: :param divisor: :param min_value: :return: """ if min_value is None: min_value = divisor new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) # Make sure that round down does not go down by more than 10%. if new_v < 0.9 * v: new_v += divisor return new_v class ConvBNReLU(nn.Sequential): def __init__(self, in_planes, out_planes, kernel_size=3, stride=1, groups=1): padding = (kernel_size - 1) // 2 super(ConvBNReLU, self).__init__( nn.Conv2d(in_planes, out_planes, kernel_size, stride, padding, groups=groups, bias=False), nn.BatchNorm2d(out_planes), nn.ReLU6(inplace=True) ) class InvertedResidual(nn.Module): def __init__(self, inp, oup, stride, expand_ratio): super(InvertedResidual, self).__init__() self.stride = stride assert stride in [1, 2] hidden_dim = int(round(inp * expand_ratio)) self.use_res_connect = self.stride == 1 and inp == oup layers = [] if expand_ratio != 1: # pw layers.append(ConvBNReLU(inp, hidden_dim, kernel_size=1)) layers.extend([ # dw ConvBNReLU(hidden_dim, hidden_dim, stride=stride, groups=hidden_dim), # pw-linear nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False), nn.BatchNorm2d(oup), ]) self.conv = nn.Sequential(*layers) def forward(self, x): if self.use_res_connect: return x + self.conv(x) else: return self.conv(x) class MobileNetV2(nn.Module): def __init__(self, num_classes=1000, width_mult=1.0, inverted_residual_setting=None, round_nearest=8, block=None): """ MobileNet V2 main class Args: num_classes (int): Number of classes width_mult (float): Width multiplier - adjusts number of channels in each layer by this amount inverted_residual_setting: Network structure round_nearest (int): Round the number of channels in each layer to be a multiple of this number Set to 1 to turn off rounding block: Module specifying inverted residual building block for mobilenet """ super(MobileNetV2, self).__init__() if block is None: block = InvertedResidual input_channel = 32 last_channel = 1280 if inverted_residual_setting is None: inverted_residual_setting = [ # t, c, n, s [1, 16, 1, 1], [6, 24, 2, 2], [6, 32, 3, 2], [6, 64, 4, 2], [6, 96, 3, 1], [6, 160, 3, 2], [6, 320, 1, 1], ] # only check the first element, assuming user knows t,c,n,s are required if len(inverted_residual_setting) == 0 or len(inverted_residual_setting[0]) != 4: raise ValueError("inverted_residual_setting should be non-empty " "or a 4-element list, got {}".format(inverted_residual_setting)) # building first layer input_channel = _make_divisible(input_channel * width_mult, round_nearest) self.last_channel = _make_divisible(last_channel * max(1.0, width_mult), round_nearest) features = [ConvBNReLU(3, input_channel, stride=2)] # building inverted residual blocks for t, c, n, s in inverted_residual_setting: output_channel = _make_divisible(c * width_mult, round_nearest) for i in range(n): stride = s if i == 0 else 1 features.append(block(input_channel, output_channel, stride, expand_ratio=t)) input_channel = output_channel # building last several layers features.append(ConvBNReLU(input_channel, self.last_channel, kernel_size=1)) # make it nn.Sequential self.features = nn.Sequential(*features) # building classifier self.classifier = nn.Sequential( nn.Dropout(0.2), nn.Linear(self.last_channel, num_classes), ) # weight initialization for m in self.modules(): if isinstance(m, nn.Conv2d): nn.init.kaiming_normal_(m.weight, mode='fan_out') if m.bias is not None: nn.init.zeros_(m.bias) elif isinstance(m, nn.BatchNorm2d): nn.init.ones_(m.weight) nn.init.zeros_(m.bias) elif isinstance(m, nn.Linear): nn.init.normal_(m.weight, 0, 0.01) nn.init.zeros_(m.bias) def _forward_impl(self, x): # This exists since TorchScript doesn't support inheritance, so the superclass method # (this one) needs to have a name other than `forward` that can be accessed in a subclass x = self.features(x) x = x.mean([2, 3]) x = self.classifier(x) return x def forward(self, x): return self._forward_impl(x) def __str__(self): return "MobileNetV2" ## Wide ResNet ## #https://github.com/xternalz/WideResNet-pytorch/blob/master/wideresnet.py #https://github.com/arcelien/pba/blob/master/pba/wrn.py #https://github.com/szagoruyko/wide-residual-networks/blob/master/pytorch/resnet.py class BasicBlock(nn.Module): def __init__(self, in_planes, out_planes, stride, dropRate=0.0): super(BasicBlock, self).__init__() self.bn1 = nn.BatchNorm2d(in_planes) self.relu1 = nn.ReLU(inplace=True) self.conv1 = nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False) self.bn2 = nn.BatchNorm2d(out_planes) self.relu2 = nn.ReLU(inplace=True) self.conv2 = nn.Conv2d(out_planes, out_planes, kernel_size=3, stride=1, padding=1, bias=False) self.droprate = dropRate self.equalInOut = (in_planes == out_planes) self.convShortcut = (not self.equalInOut) and nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, padding=0, bias=False) or None def forward(self, x): if not self.equalInOut: x = self.relu1(self.bn1(x)) else: out = self.relu1(self.bn1(x)) out = self.relu2(self.bn2(self.conv1(out if self.equalInOut else x))) if self.droprate > 0: out = F.dropout(out, p=self.droprate, training=self.training) out = self.conv2(out) return torch.add(x if self.equalInOut else self.convShortcut(x), out) class NetworkBlock(nn.Module): def __init__(self, nb_layers, in_planes, out_planes, block, stride, dropRate=0.0): super(NetworkBlock, self).__init__() self.layer = self._make_layer(block, in_planes, out_planes, nb_layers, stride, dropRate) def _make_layer(self, block, in_planes, out_planes, nb_layers, stride, dropRate): layers = [] for i in range(int(nb_layers)): layers.append(block(i == 0 and in_planes or out_planes, out_planes, i == 0 and stride or 1, dropRate)) return nn.Sequential(*layers) def forward(self, x): return self.layer(x) #wrn_size: 32 = WRN-28-2 ? 160 = WRN-28-10 class WideResNet(nn.Module): #def __init__(self, depth, num_classes, widen_factor=1, dropRate=0.0): def __init__(self, num_classes, wrn_size, depth=28, dropRate=0.0): super(WideResNet, self).__init__() self.kernel_size = wrn_size self.depth=depth filter_size = 3 nChannels = [min(self.kernel_size, 16), self.kernel_size, self.kernel_size * 2, self.kernel_size * 4] strides = [1, 2, 2] # stride for each resblock #nChannels = [16, 16*widen_factor, 32*widen_factor, 64*widen_factor] assert((depth - 4) % 6 == 0) n = (depth - 4) / 6 block = BasicBlock # 1st conv before any network block self.conv1 = nn.Conv2d(filter_size, nChannels[0], kernel_size=3, stride=1, padding=1, bias=False) # 1st block self.block1 = NetworkBlock(n, nChannels[0], nChannels[1], block, strides[0], dropRate) # 2nd block self.block2 = NetworkBlock(n, nChannels[1], nChannels[2], block, strides[1], dropRate) # 3rd block self.block3 = NetworkBlock(n, nChannels[2], nChannels[3], block, strides[2], dropRate) # global average pooling and classifier self.bn1 = nn.BatchNorm2d(nChannels[3]) self.relu = nn.ReLU(inplace=True) self.fc = nn.Linear(nChannels[3], num_classes) self.nChannels = nChannels[3] for m in self.modules(): if isinstance(m, nn.Conv2d): nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') elif isinstance(m, nn.BatchNorm2d): m.weight.data.fill_(1) m.bias.data.zero_() elif isinstance(m, nn.Linear): m.bias.data.zero_() def forward(self, x): out = self.conv1(x) out = self.block1(out) out = self.block2(out) out = self.block3(out) out = self.relu(self.bn1(out)) out = F.avg_pool2d(out, 8) out = out.view(-1, self.nChannels) return self.fc(out) def architecture(self): return super(WideResNet, self).__str__() def __str__(self): return "WideResNet(s{}-d{})".format(self.kernel_size, self.depth)