当前位置 博文首页 > hallobike的博客:一文看懂网络中间层特征矩阵和卷积层参数

    hallobike的博客:一文看懂网络中间层特征矩阵和卷积层参数

    作者:[db:作者] 时间:2021-09-14 21:56

    在使用卷机神经网络训练模型时,很多时候需要查看卷积层的特征矩阵是什么样子的,看它提取的是什么特征,有多少卷积层参数以及将它可视化出来,本期本文就给大家讲解一下怎么使用Pytorch和TensorFlow查看中间层的特征矩阵和卷积层参数。

    1、Pytorch查看特征图和卷积参数

    我们以AlexNet这个简单的卷积神经网络为例来查看它的中间层特征矩阵和卷积层参数。

    首先建立我们的model.py,在正向传播函数中保存需要显示的卷积层输出的特征矩阵在一个列表中。

    import torch.nn as nnimport torchclass AlexNet(nn.Module):    def __init__(self, num_classes=1000, init_weights=False):        super(AlexNet, self).__init__()        self.features = nn.Sequential(            nn.Conv2d(3, 96, 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(96, 256, 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(256, 384, kernel_size=3, padding=1),          # output[192, 13, 13]            nn.ReLU(inplace=True),            nn.Conv2d(384, 384, kernel_size=3, padding=1),          # output[192, 13, 13]            nn.ReLU(inplace=True),            nn.Conv2d(384, 256, 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(256 * 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)

    然后使用matplotlib库显示特征矩阵对应的特征图。feature_map.py

    import torchfrom analyze_weights_featuremap.alexnet_model import AlexNetfrom analyze_weights_featuremap.resnet_model import resnet34import matplotlib.pyplot as pltimport numpy as npfrom PIL import Imagefrom 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 weightsmodel_weight_path = "./resNet34.pth"  # "./resNet34.pth, ./AlexNet.pth"model.load_state_dict(torch.load(model_weight_path))print(model)
    # load imageimg = Image.open("./tulip.jpg")# [N, C, H, W]
    img = data_transform(img)# expand batch dimensionimg = torch.unsqueeze(img, dim=0)
    # forwardout_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]        # 特征矩阵的每一个channel所对应的是一个二维特征矩阵,就像灰度图像一样,channel=1        plt.imshow(im[:, :, i])  # cmap='gray'如果不指定的话它是使用蓝色和绿色来替代我们灰度图像的黑色和白色。    plt.show()
    """上面代码是显示第0层、第3层和第六层卷积层输出的特征图,显示每个输出特征层的前12个channel的特征图,可以发现在越往后的输出特征层里面,输出的特征图更加的抽象,还有很多是黑色的,说明这些卷积核没有起作用,没有学到特征。
    在使用ResNet网络时,明显可以看到网络学习到的信息要比AlexNet网络要多。"""

    显示效果:

    图片

    使用同样的方法可以输出每层卷积核的所有参数,并计算它们权重和偏置的均值、标准差、最大值、最小值,并绘制直方图直观显示。

    kernel_weight.py

    import torchfrom analyze_weights_featuremap.alexnet_model import AlexNetfrom analyze_weights_featuremap.resnet_model import resnet34import matplotlib.pyplot as pltimport numpy as np
    # create modelmodel = AlexNet(num_classes=5)# model = resnet34(num_classes=5)# load model weightsmodel_weight_path = "./AlexNet.pth"  # "resNet34.pth" # ./AlexNet.pthmodel.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:  # 排除BN层不必要的信息        continue    # [kernel_number, kernel_channel, kernel_height, kernel_width]    weight_t = model.state_dict()[key].numpy()  # 读取key层的所有参数
        # 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)  # 使用hist方法来统计卷积核的权重值的一个分部,画直方图;    # bins=50将所取的最小值和最大值的区间均分成50等份,然后再统计落到每一个小区间上的值的个数。    plt.title(key)  # 给直方图加一个标题    plt.show()

    图片

    ?

    图片

    可以发现无论是特征图的可视化还是卷积层参数的可视化,效果都比较好。

    2、TensorFlow查看特征图和卷积层参数

    步骤和前面一样,首先是model.py

    from tensorflow.keras import layers, models, Model, Sequentialdef AlexNet_v1(im_height=224, im_width=224, class_num=1000):    # tensorflow中的tensor通道排序是NHWC    input_image = layers.Input(shape=(im_height, im_width, 3), dtype="float32")  # output(None, 224, 224, 3)    x = layers.ZeroPadding2D(((1, 2), (1, 2)))(input_image)                      # output(None, 227, 227, 3)    x = layers.Conv2D(48, kernel_size=11, strides=4, activation="relu")(x)       # output(None, 55, 55, 48)    x = layers.MaxPool2D(pool_size=3, strides=2)(x)                              # output(None, 27, 27, 48)    x = layers.Conv2D(128, kernel_size=5, padding="same", activation="relu")(x)  # output(None, 27, 27, 128)    x = layers.MaxPool2D(pool_size=3, strides=2)(x)                              # output(None, 13, 13, 128)    x = layers.Conv2D(192, kernel_size=3, padding="same", activation="relu")(x)  # output(None, 13, 13, 192)    x = layers.Conv2D(192, kernel_size=3, padding="same", activation="relu")(x)  # output(None, 13, 13, 192)    x = layers.Conv2D(128, kernel_size=3, padding="same", activation="relu")(x)  # output(None, 13, 13, 128)    x = layers.MaxPool2D(pool_size=3, strides=2)(x)                              # output(None, 6, 6, 128)
        x = layers.Flatten()(x)                         # output(None, 6*6*128)    x = layers.Dropout(0.2)(x)    x = layers.Dense(2048, activation="relu")(x)    # output(None, 2048)    x = layers.Dropout(0.2)(x)    x = layers.Dense(2048, activation="relu")(x)    # output(None, 2048)    x = layers.Dense(class_num)(x)                  # output(None, 5)    predict = layers.Softmax()(x)
        model = models.Model(inputs=input_image, outputs=predict)    return model

    feature_map.py,???????

    from alexnet_model import AlexNet_v1, AlexNet_v2from PIL import Imageimport numpy as npimport matplotlib.pyplot as pltfrom tensorflow.keras import Model, Input
    im_height = 224im_width = 224
    # load imageimg = Image.open("../tulip.jpg")# resize image to 224x224img = img.resize((im_width, im_height))
    # scaling pixel value to (0-1)img = np.array(img) / 255.
    # Add the image to a batch where it's the only member.img = (np.expand_dims(img, 0))
    
    model = AlexNet_v1(class_num=5)  # functional api# model = AlexNet_v2(class_num=5)  # subclass api# model.build((None, 224, 224, 3))# If `by_name` is False weights are loaded based on the network's topology.model.load_weights("./myAlex.h5")# model.load_weights("./submodel.h5")# for layer in model.layers:#     print(layer.name)model.summary()layers_name = ["conv2d", "conv2d_1"]
    # functional APItry:    input_node = model.input    output_node = [model.get_layer(name=layer_name).output for layer_name in layers_name]    model1 = Model(inputs=input_node, outputs=output_node)    outputs = model1.predict(img)    for index, feature_map in enumerate(outputs):        # [N, H, W, C] -> [H, W, C]        im = np.squeeze(feature_map)
            # 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.suptitle(layers_name[index])        plt.show()except Exception as e:    print(e)

    ?

    kernel_weight.py???????

    from alexnet_model import AlexNet_v1, AlexNet_v2import numpy as npimport matplotlib.pyplot as plt
    model = AlexNet_v1(class_num=5)  # functional api# model = AlexNet_v2(class_num=5)  # subclass api# model.build((None, 224, 224, 3))model.load_weights("./myAlex.h5")# model.load_weights("./submodel.h5")model.summary()for layer in model.layers:    for index, weight in enumerate(layer.weights):        # [kernel_height, kernel_width, kernel_channel, kernel_number]        weight_t = weight.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(weight.name)        plt.show()

    好了本期学习就到这里了,关于代码调不通问题,欢迎咨询微信进群,备注“AI学习”。

    图片

    cs