最近、ディープラーニングのターゲット検出ネットワークを研究していましたが、コードを研究している最中に、画像の特徴のブラックボックスの内容を抽出することに興味があったので、さらに研究を行い、関連情報を調べて実現しました。 vgg16ネットワーク機能マップ。視覚化。
最初に完全なコードを投稿します。
import torch
from torchvision import models, transforms
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
import scipy.misc
import cv2
# 将输入图片转化为卷积运算格式tensor数据
def transfer_image(image_dir):
# 以RGB格式打开图像
# Pytorch DataLoader就是使用PIL所读取的图像格式
# 建议就用这种方法读取图像,当读入灰度图像时convert('')
image_info = Image.open(image_dir).convert('RGB')
# 数据预处理方法
image_transform = transforms.Compose([
transforms.Resize(1024),#放缩图片大小至1024*1024
transforms.CenterCrop(672),#从图象中间剪切672*672大小
transforms.ToTensor(),#将图像数据转化为tensor
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])#归一化处理
])
image_info = image_transform(image_info)#更新载入图像的数据格式
#下面这部分是tensor张量转化为图片的过程,opencv画图和PIL、torch画图方式
array1 = image_info.numpy()
maxvalue = array1.max()
array1 = array1*255/maxvalue
mat = np.uint8(array1)
print('mat_shape',mat.shape)
mat = mat.transpose(1,2,0)
cv2.imwrite('convert_cocomi1.jpg',mat)
mat=cv2.cvtColor(mat,cv2.COLOR_BGR2RGB)
cv2.imwrite('convert_cocomi2.jpg',mat)
image_info = image_info.unsqueeze(0)#增加tensor维度,便于卷积层进行运算
return image_info
# 获取第k层的特征图
def get_k_layer_feature_map(feature_extractor, k, x):
with torch.no_grad():
#feature_extractor是特征提取层,后面可以具体看一下vgg16网络
for index,layer in enumerate(feature_extractor):
x = layer(x)#x是输入图像的张量数据,layer是该位置进行运算的卷积层,就是进行特征提取
print('k values:',k)
print('feature_extractor layer:',index)
if k == index:#k代表想看第几层的特征图
return x
# 可视化特征图
def show_feature_map(feature_map):
feature_map = feature_map.squeeze(0)#squeeze(0)实现tensor降维,开始将数据转化为图像格式显示
feature_map = feature_map.cpu().numpy()#进行卷积运算后转化为numpy格式
feature_map_num = feature_map.shape[0]#特征图数量等于该层卷积运算后的特征图维度
row_num = np.ceil(np.sqrt(feature_map_num))
plt.figure()
for index in range(1, feature_map_num+1):
plt.subplot(row_num, row_num, index)
plt.imshow(feature_map[index-1], cmap='gray')
plt.axis('off')
scipy.misc.imsave(str(index)+".png", feature_map[index-1])
plt.show()
if __name__ == '__main__':
# 初始化图像的路径
image_dir = 'cocomi.jpg'
# 定义提取第几层的feature map
k = 5
# 导入Pytorch封装的vgg16网络模型
model = models.vgg16(pretrained=False)
# 是否使用gpu运算
use_gpu = torch.cuda.is_available()
use_gpu =False
# 读取图像信息
image_info = transfer_image(image_dir)
# 判断是否使用gpu
if use_gpu:
model = model.cuda()
image_info = image_info.cuda()
# vgg16包含features和classifier,但只有features部分有特征图
# classifier部分的feature map是向量
feature_extractor = model.features
feature_map = get_k_layer_feature_map(feature_extractor, k, image_info)
show_feature_map(feature_map)
コードを実行すると、結果は次のようになり
ます。k= 5の場合に128個の特徴マップが生成されることがわかります。128個の特徴マップがあるのはなぜですか?ネットワーク構造を見て理解してください。初心者にとっても特徴マップを理解するのに役立ちます。寸法。
ipdbを使用したデバッグでは、次のことがわかります。
ipdb> model
VGG(
(features): Sequential(
(0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(1): ReLU(inplace=True)
(2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(3): ReLU(inplace=True)
(4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(6): ReLU(inplace=True)
(7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(8): ReLU(inplace=True)
(9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(11): ReLU(inplace=True)
(12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(13): ReLU(inplace=True)
(14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(15): ReLU(inplace=True)
(16): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(17): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(18): ReLU(inplace=True)
(19): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(20): ReLU(inplace=True)
(21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(22): ReLU(inplace=True)
(23): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(24): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(25): ReLU(inplace=True)
(26): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(27): ReLU(inplace=True)
(28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(29): ReLU(inplace=True)
(30): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(avgpool): AdaptiveAvgPool2d(output_size=(7, 7))
(classifier): Sequential(
(0): Linear(in_features=25088, out_features=4096, bias=True)
(1): ReLU(inplace=True)
(2): Dropout(p=0.5, inplace=False)
(3): Linear(in_features=4096, out_features=4096, bias=True)
(4): ReLU(inplace=True)
(5): Dropout(p=0.5, inplace=False)
(6): Linear(in_features=4096, out_features=1000, bias=True)
)
)
上記は完全なvgg16ネットワーク構造であり、機能と分類子の2つの部分に分かれています。機能部分は機能マップを表示するために使用されます。(0)から(30)までは、畳み込み層、活性化関数層です。そして最大プーリング層。k = 5は(5)に対応します:Conv2d(64、128、kernel_size =(3、3)、stride =(1、1)、padding =(1、1))
入力次元は64、出力次元は128、これは128の特徴マップを出力することを意味します。
以下にいくつかの特別な写真を置いて、抽出効果を見てみましょう
。上記の結果から、より直感的に見えます。
画像畳み込みデータの流れについては、コードから部分ごとのデバッグを慎重に開始できれば、ある程度のメリットがあり、理解がより具体的になると思います。コードと組み合わせて簡単に説明しましょう。
1つ目は、3チャンネルのカラー画像が畳み込み層に入るというものです。操作を実行するとき、画像は別々の畳み込み操作のために3つのチャンネルに分割され、特徴マップを取得するために追加されます。畳み込み層の最初の層の後に、64があります。畳み込み。製品カーネル(手動で設定)、および各畳み込みカーネル操作により、特徴マップが生成されます。この時点で、画像の出力は、テンソルフローの形式で64の特徴マップである64次元の特徴マップになります。その後の畳み込みプーリングはすべてこの原則に基づいています。
一度に複数の画像を処理する場合、テンソルの形式に新しい変更が加えられることがあります。これを確認できます。ミニバッチバッチ処理
で畳み込み演算を実行した後も、特徴マップはテンソルの形式で存在します。特徴マップ、テンソルをnumpy形式に変換するために、cvライブラリはそれを描画できます。これらは投稿されたコードから理解できます。特定の描画方法の違いは次のとおりです。
opencvでサポートされている画像データはnumpy形式で、データ型はuint8で、ピクセル値は[0,255]で分散されますが、テンソルデータのピクセル値は[0,255]で分散されず、データ型はfloat32です。 、したがって、正規化とデータ変換を行う必要があります。画像データを[0,255]に拡張します。もう1つのポイントは、テンソルとnumpyはデータを異なる次元の順序で格納するため、変換には転置が必要になることです。
さらに、opencvのカラーチャネルの順序はBGRであり、PILとトーチの画像カラーチャネルはRGBです。cvtColorを使用してカラーチャネルを変換します。