在使用卷机神经网络训练模型时,很多时候需要查看卷积层的特征矩阵是什么样子的,看它提取的是什么特征,有多少卷积层参数以及将它可视化出来,本期本文就给大家讲解一下怎么使用Pytorch和TensorFlow查看中间层的特征矩阵和卷积层参数。
1、Pytorch查看特征图和卷积参数
我们以AlexNet这个简单的卷积神经网络为例来查看它的中间层特征矩阵和卷积层参数。
首先建立我们的model.py,在正向传播函数中保存需要显示的卷积层输出的特征矩阵在一个列表中。
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, 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 torch from analyze_weights_featuremap.alexnet_model import AlexNet from analyze_weights_featuremap.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 = "./resNet34.pth" # "./resNet34.pth, ./AlexNet.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] # 特征矩阵的每一个channel所对应的是一个二维特征矩阵,就像灰度图像一样,channel=1 plt.imshow(im[:, :, i]) # cmap='gray'如果不指定的话它是使用蓝色和绿色来替代我们灰度图像的黑色和白色。 plt.show() """ 上面代码是显示第0层、第3层和第六层卷积层输出的特征图,显示每个输出特征层的前12个channel的特征图, 可以发现在越往后的输出特征层里面,输出的特征图更加的抽象,还有很多是黑色的,说明这些卷积核没有起作用, 没有学到特征。 在使用ResNet网络时,明显可以看到网络学习到的信息要比AlexNet网络要多。 """
显示效果:
使用同样的方法可以输出每层卷积核的所有参数,并计算它们权重和偏置的均值、标准差、最大值、最小值,并绘制直方图直观显示。
kernel_weight.py
import torch from analyze_weights_featuremap.alexnet_model import AlexNet from analyze_weights_featuremap.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" # ./AlexNet.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: # 排除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, Sequential def 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_v2 from PIL import Image import numpy as np import matplotlib.pyplot as plt from tensorflow.keras import Model, Input im_height = 224 im_width = 224 # load image img = Image.open("../tulip.jpg") # resize image to 224x224 img = 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 API try: 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_v2 import numpy as np import 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学习”。