diff --git a/pytorch_learning/analyze_weights_featuremap/alexnet_model.py b/pytorch_learning/analyze_weights_featuremap/alexnet_model.py new file mode 100644 index 000000000..03ef3d69a --- /dev/null +++ b/pytorch_learning/analyze_weights_featuremap/alexnet_model.py @@ -0,0 +1,52 @@ +import torch.nn as nn +import torch + + +class AlexNet(nn.Module): + def __init__(self, num_classes=1000, init_weights=False): + super(AlexNet, self).__init__() + self.features = nn.Sequential( + nn.Conv2d(3, 48, kernel_size=11, stride=4, padding=2), # input[3, 224, 224] output[48, 55, 55] + nn.ReLU(inplace=True), + nn.MaxPool2d(kernel_size=3, stride=2), # output[48, 27, 27] + nn.Conv2d(48, 128, kernel_size=5, padding=2), # output[128, 27, 27] + nn.ReLU(inplace=True), + nn.MaxPool2d(kernel_size=3, stride=2), # output[128, 13, 13] + nn.Conv2d(128, 192, kernel_size=3, padding=1), # output[192, 13, 13] + nn.ReLU(inplace=True), + nn.Conv2d(192, 192, kernel_size=3, padding=1), # output[192, 13, 13] + nn.ReLU(inplace=True), + nn.Conv2d(192, 128, kernel_size=3, padding=1), # output[128, 13, 13] + nn.ReLU(inplace=True), + nn.MaxPool2d(kernel_size=3, stride=2), # output[128, 6, 6] + ) + self.classifier = nn.Sequential( + nn.Dropout(p=0.5), + nn.Linear(128 * 6 * 6, 2048), + nn.ReLU(inplace=True), + nn.Dropout(p=0.5), + nn.Linear(2048, 2048), + nn.ReLU(inplace=True), + nn.Linear(2048, num_classes), + ) + if init_weights: + self._initialize_weights() + + def forward(self, x): + outputs = [] + for name, module in self.features.named_children(): + x = module(x) + if name in ["0", "3", "6"]: + outputs.append(x) + + return outputs + + def _initialize_weights(self): + for m in self.modules(): + if isinstance(m, nn.Conv2d): + nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') + if m.bias is not None: + nn.init.constant_(m.bias, 0) + elif isinstance(m, nn.Linear): + nn.init.normal_(m.weight, 0, 0.01) + nn.init.constant_(m.bias, 0) diff --git a/pytorch_learning/analyze_weights_featuremap/analyze_feature_map.py b/pytorch_learning/analyze_weights_featuremap/analyze_feature_map.py new file mode 100644 index 000000000..fcc8ffec1 --- /dev/null +++ b/pytorch_learning/analyze_weights_featuremap/analyze_feature_map.py @@ -0,0 +1,50 @@ +import torch +from alexnet_model import AlexNet +from resnet_model import resnet34 +import matplotlib.pyplot as plt +import numpy as np +from PIL import Image +from torchvision import transforms + +data_transform = transforms.Compose( + [transforms.Resize((224, 224)), + transforms.ToTensor(), + transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) + +# data_transform = transforms.Compose( +# [transforms.Resize(256), +# transforms.CenterCrop(224), +# transforms.ToTensor(), +# transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]) + +# create model +model = AlexNet(num_classes=5) +# model = resnet34(num_classes=5) +# load model weights +model_weight_path = "./AlexNet.pth" # "./resNet34.pth" +model.load_state_dict(torch.load(model_weight_path)) +print(model) + +# load image +img = Image.open("../tulip.jpg") +# [N, C, H, W] +img = data_transform(img) +# expand batch dimension +img = torch.unsqueeze(img, dim=0) + +# forward +out_put = model(img) +for feature_map in out_put: + # [N, C, H, W] -> [C, H, W] + im = np.squeeze(feature_map.detach().numpy()) + # [C, H, W] -> [H, W, C] + im = np.transpose(im, [1, 2, 0]) + + # show top 12 feature maps + plt.figure() + for i in range(12): + ax = plt.subplot(3, 4, i+1) + # [H, W, C] + plt.imshow(im[:, :, i], cmap='gray') + plt.show() + diff --git a/pytorch_learning/analyze_weights_featuremap/analyze_kernel_weight.py b/pytorch_learning/analyze_weights_featuremap/analyze_kernel_weight.py new file mode 100644 index 000000000..d80967bc7 --- /dev/null +++ b/pytorch_learning/analyze_weights_featuremap/analyze_kernel_weight.py @@ -0,0 +1,43 @@ +import torch +from alexnet_model import AlexNet +from resnet_model import resnet34 +import matplotlib.pyplot as plt +import numpy as np + + +# create model +model = AlexNet(num_classes=5) +# model = resnet34(num_classes=5) +# load model weights +model_weight_path = "./AlexNet.pth" # "resNet34.pth" +model.load_state_dict(torch.load(model_weight_path)) +print(model) + +weights_keys = model.state_dict().keys() +for key in weights_keys: + # remove num_batches_tracked para(in bn) + if "num_batches_tracked" in key: + continue + # [kernel_number, kernel_channel, kernel_height, kernel_width] + weight_t = model.state_dict()[key].numpy() + + # read a kernel information + # k = weight_t[0, :, :, :] + + # calculate mean, std, min, max + weight_mean = weight_t.mean() + weight_std = weight_t.std(ddof=1) + weight_min = weight_t.min() + weight_max = weight_t.max() + print("mean is {}, std is {}, min is {}, max is {}".format(weight_mean, + weight_std, + weight_max, + weight_min)) + + # plot hist image + plt.close() + weight_vec = np.reshape(weight_t, [-1]) + plt.hist(weight_vec, bins=50) + plt.title(key) + plt.show() + diff --git a/pytorch_learning/analyze_weights_featuremap/resnet_model.py b/pytorch_learning/analyze_weights_featuremap/resnet_model.py new file mode 100644 index 000000000..900a60932 --- /dev/null +++ b/pytorch_learning/analyze_weights_featuremap/resnet_model.py @@ -0,0 +1,145 @@ +import torch.nn as nn +import torch + + +class BasicBlock(nn.Module): + expansion = 1 + + def __init__(self, in_channel, out_channel, stride=1, downsample=None): + super(BasicBlock, self).__init__() + self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=out_channel, + kernel_size=3, stride=stride, padding=1, bias=False) + self.bn1 = nn.BatchNorm2d(out_channel) + self.relu = nn.ReLU() + self.conv2 = nn.Conv2d(in_channels=out_channel, out_channels=out_channel, + kernel_size=3, stride=1, padding=1, bias=False) + self.bn2 = nn.BatchNorm2d(out_channel) + self.downsample = downsample + + def forward(self, x): + identity = x + if self.downsample is not None: + identity = self.downsample(x) + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + + out += identity + out = self.relu(out) + + return out + + +class Bottleneck(nn.Module): + expansion = 4 + + def __init__(self, in_channel, out_channel, stride=1, downsample=None): + super(Bottleneck, self).__init__() + self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=out_channel, + kernel_size=1, stride=1, bias=False) # squeeze channels + self.bn1 = nn.BatchNorm2d(out_channel) + # ----------------------------------------- + self.conv2 = nn.Conv2d(in_channels=out_channel, out_channels=out_channel, + kernel_size=3, stride=stride, bias=False, padding=1) + self.bn2 = nn.BatchNorm2d(out_channel) + # ----------------------------------------- + self.conv3 = nn.Conv2d(in_channels=out_channel, out_channels=out_channel*self.expansion, + kernel_size=1, stride=1, bias=False) # unsqueeze channels + self.bn3 = nn.BatchNorm2d(out_channel*self.expansion) + self.relu = nn.ReLU(inplace=True) + self.downsample = downsample + + def forward(self, x): + identity = x + if self.downsample is not None: + identity = self.downsample(x) + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + out = self.relu(out) + + out = self.conv3(out) + out = self.bn3(out) + + out += identity + out = self.relu(out) + + return out + + +class ResNet(nn.Module): + + def __init__(self, block, blocks_num, num_classes=1000, include_top=True): + super(ResNet, self).__init__() + self.include_top = include_top + self.in_channel = 64 + + self.conv1 = nn.Conv2d(3, self.in_channel, kernel_size=7, stride=2, + padding=3, bias=False) + self.bn1 = nn.BatchNorm2d(self.in_channel) + self.relu = nn.ReLU(inplace=True) + self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) + self.layer1 = self._make_layer(block, 64, blocks_num[0]) + self.layer2 = self._make_layer(block, 128, blocks_num[1], stride=2) + self.layer3 = self._make_layer(block, 256, blocks_num[2], stride=2) + self.layer4 = self._make_layer(block, 512, blocks_num[3], stride=2) + if self.include_top: + self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) # output size = (1, 1) + self.fc = nn.Linear(512 * block.expansion, num_classes) + + for m in self.modules(): + if isinstance(m, nn.Conv2d): + nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') + + def _make_layer(self, block, channel, block_num, stride=1): + downsample = None + if stride != 1 or self.in_channel != channel * block.expansion: + downsample = nn.Sequential( + nn.Conv2d(self.in_channel, channel * block.expansion, kernel_size=1, stride=stride, bias=False), + nn.BatchNorm2d(channel * block.expansion)) + + layers = [] + layers.append(block(self.in_channel, channel, downsample=downsample, stride=stride)) + self.in_channel = channel * block.expansion + + for _ in range(1, block_num): + layers.append(block(self.in_channel, channel)) + + return nn.Sequential(*layers) + + def forward(self, x): + outputs = [] + x = self.conv1(x) + outputs.append(x) + x = self.bn1(x) + x = self.relu(x) + x = self.maxpool(x) + + x = self.layer1(x) + outputs.append(x) + # x = self.layer2(x) + # x = self.layer3(x) + # x = self.layer4(x) + # + # if self.include_top: + # x = self.avgpool(x) + # x = torch.flatten(x, 1) + # x = self.fc(x) + + return outputs + + +def resnet34(num_classes=1000, include_top=True): + return ResNet(BasicBlock, [3, 4, 6, 3], num_classes=num_classes, include_top=include_top) + + +def resnet101(num_classes=1000, include_top=True): + return ResNet(Bottleneck, [3, 4, 23, 3], num_classes=num_classes, include_top=include_top)