caffe之特征图可视化及特征提取

xiaoxiao2021-02-28  157

上一篇博客,介绍了怎么对训练好的model的各层权重可视化,这篇博客,我们介绍测试图片输入网络后产生的特征图的可视化

记得上篇中,我们是写了一个新的文件test.cpp,然后编译运行那个文件的,这是因为权重可视化,是不需要网络的前向传播,只要加载model就好了,而我们如果需要可视化特征图,那就必须进行网络的前向传播了,所以,这次,我们对caffe根目录下的examples/cpp_classification/classification.cpp文件基础上,进行修改,添加我们自己需要的功能(可视化),同样的,我们把这个文件拷贝过来,放在自己的某个文件夹下,假设取名依然为test.cpp,而我们要修改的,就是Predict函数,定位到Predict函数,在net_->Forward();后面加入以下代码:

//打印出一张图片经过网络各层产出的各层输出 cout<<"网络中的Blobs名称为:\n"; vector<shared_ptr<Blob<float> > > blobs=net_->blobs(); //得到各层的输出特征向量 vector<string> blob_names=net_->blob_names(); //各层的输出向量名字 cout<<blobs.size()<<" "<<blob_names.size()<<endl; for(int i=0;i<blobs.size();i++){ cout<<blob_names[i]<<" "<<blobs[i]->shape_string()<<endl; } cout<<endl;编译该文件,编译命令参照上一篇博客,此时会打印出如下信息 网络中的Blobs名称为: 15 15 data 1 3 227 227 (154587) conv1 1 96 55 55 (290400) pool1 1 96 27 27 (69984) norm1 1 96 27 27 (69984) conv2 1 256 27 27 (186624) pool2 1 256 13 13 (43264) norm2 1 256 13 13 (43264) conv3 1 384 13 13 (64896) conv4 1 384 13 13 (64896) conv5 1 256 13 13 (43264) pool5 1 256 6 6 (9216) fc6 1 4096 (4096) fc7 1 4096 (4096) fc8 1 1000 (1000) prob 1 1000 (1000) 可以看到,此时将每个层的输出特征图的维度信息都打印出来了,比如data层的输出是1 3 227 277,因为网络的输入图片是1个,3通道,尺寸是227*227;经过conv1层的输出是1 96 55 55,因为conv1层有96个卷积核,卷积核大小为11*11,步长为4,所以其输出的每个特征图的尺寸为(227-11)/4+1=55,所以特征图的尺寸也对应着1 96 55 55.

好了,我们接下来再输入一下代码,进行对经过conv1的特征图可视化,其尺寸是1 96 55 55,也就看出是96个55*55大小的灰度图

//将测试图片经过第一个卷积层的特征图可视化 string blobName="conv1"; //我们取经过第一个卷积层的特征图 assert(net_->has_blob(blobName)); //为免出错,我们必须断言,网络中确实有名字为blobName的特征图 shared_ptr<Blob<float> > conv1Blob=net_->blob_by_name(blobName); //1*96*55*55 断言成功后,按名字返回该 特征向量 cout<<"测试图片的特征响应图的形状信息为:"<<conv1Blob->shape_string()<<endl; //打印输出的特征图的形状信息 //和前面的博客一样,此时的特征向量是经过了ReLU激活函数的,范围在0~无穷大,我们为了可视化,仍然需要归一化到0~255 //下面的代码,跟上一篇博客中是一样的 float maxValue=-10000000,minValue=10000000; const float* tmpValue=conv1Blob->cpu_data(); for(int i=0;i<conv1Blob->count();i++){ maxValue=std::max(maxValue,tmpValue[i]); minValue=std::min(minValue,tmpValue[i]); } int width=conv1Blob->shape(2); //响应图的宽度 int height=conv1Blob->shape(3); //响应图的高度 int num=conv1Blob->shape(1); //个数 int imgHeight=(int)(1+sqrt(num))*height; int imgWidth=(int)(1+sqrt(num))*width; Mat img2(imgHeight,imgWidth,CV_8UC1,Scalar(0)); //此时,应该是灰度图 int kk=0; for(int x=0;x<imgHeight;x+=height){ for(int y=0;y<imgWidth;y+=width){ if(kk>=num) continue; Mat roi=img2(Rect(y,x,width,height)); for(int i=0;i<height;i++){ for(int j=0;j<width;j++){ float value=conv1Blob->data_at(0,kk,i,j); roi.at<uchar>(i,j)=(value-minValue)/(maxValue-minValue)*255; } } kk++; } } resize(img2,img2,Size(500,500));//进行显示 imshow("2",img2); waitKey(0);

输入图片

特征图

会了这一点后,大家如果想将CNN单纯的当做特征提取器,可以将第一个全连接层的前一层的输出特征向量(比如这个网络,是pool5层的输出特征1*256*6*6=9216)提取出来,这是该图片经过CNN后的最初步的特征,当然如果觉得维度高,也可以提取出第一个全连接层的特征向量,这里是4096维,然后再送进泛化性能更好的分类器(如SVM)去分类识别,我用提取第一个全连接层的4096维特征试验了下SVM,在线性核下,分类效果大致相当。大家可以根据自己的需求去尝试。

转载请注明原文地址: https://www.6miu.com/read-25342.html

最新回复(0)