caffe提取任意层深度特征

本文详细介绍如何使用Caffe从图像中提取特征,并将其保存为LMDB及.mat文件,包括环境搭建、数据准备、特征提取脚本编写等步骤。

一.数据模型与准备

(1) 进入caffe根目录,创建临时文件夹,用于存放所需要的临时文件

cd caffe
mkdir examples/_temp

(2) 根据examples/images文件夹中的图片,创建包含图像列表的txt文件,并添加标签
这里建议参考本人之前的一篇帖子,方便快捷
https://blog.youkuaiyun.com/swiftfake/article/details/79799880

我们首先生成temp.txt(带标签)文件
接下来我们要创建一个sh脚本文件,手动创建并命名为create_temp.sh

# /usr/bin/env sh
DATA=/home/hca/caffe/examples/images  #DATA为训练集的路径
echo "Create train.txt..."
rm -rf $DATA/train.txt
find $DATA -name cat*.jpg | cut -d '/' -f | sed "s/$/ 1/">>$DATA/temp.txt
find $DATA -name fish*.jpg | cut -d '/' -f | sed "s/$/ 2/">>$DATA/tmp.txt
cat $DATA/tmp.txt>>$DATA/temp.txt
rm -rf $DATA/tmp.txt
echo "Done.."

接下来生成file_list.txt(不带标签)文件
接下来我们要创建一个sh脚本文件,手动创建并命名为create_file_list.sh

# /usr/bin/env sh
DATA=/home/hca/caffe/examples/images  #DATA为训练集的路径
echo "Create train.txt..."
rm -rf $DATA/train.txt
find $DATA -name cat*.jpg | cut -d '/' -f | sed "s/$/ /">>$DATA/file_list.txt
find $DATA -name fish*.jpg | cut -d '/' -f | sed "s/$/ /">>$DATA/tmp.txt
cat $DATA/tmp.txt>>$DATA/file_list.txt
rm -rf $DATA/tmp.txt
echo "Done.."

然后执行刚刚创建好的两个sh文件,

cd caffe
sh /XXX/create_temp.sh      #/XXX/为存放create_temp.sh的路径
sh /XXX/create_file_list.sh #/xxx/为存放create_file_list.sh的路径

然后将生成的file_list.txt和temp.txt文件存放到_temp文件夹中。

(3)我们需要准备好已经训练好的网络模型和网络定义prototxt文件,然后将训练好的模型.caffemodel存放到/examples/imagenet文件夹下,将网络定义prototxt文件存放到_temp
文件夹中。(本人会尽快上传一个到百度云,方便新手练习)

二.提取特征并存储
(1) 安装CAFFE的python依赖库,并使用以下两个辅助文件把lmdb转换为mat。在caffe 根目录下创建feat_helper_pb2.py 和lmdb2mat.py,直接copy 下面的python程序即可。

cd caffe
sudo gedit  feat_helper_pb2.py
sudo gedit lmdb2mat.py

需要添加的内容如下
feat_helper_pb2.py:

# Generated by the protocol buffer compiler.  DO NOT EDIT!

from google.protobuf import descriptor
from google.protobuf import message
from google.protobuf import reflection
from google.protobuf import descriptor_pb2
# @@protoc_insertion_point(imports)


DESCRIPTOR = descriptor.FileDescriptor(
  name='datum.proto',
  package='feat_extract',
  serialized_pb='\n\x0b\x64\x61tum.proto\x12\x0c\x66\x65\x61t_extract\"i\n\x05\x44\x61tum\x12\x10\n\x08\x63hannels\x18\x01 \x01(\x05\x12\x0e\n\x06height\x18\x02 \x01(\x05\x12\r\n\x05width\x18\x03 \x01(\x05\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c\x12\r\n\x05label\x18\x05 \x01(\x05\x12\x12\n\nfloat_data\x18\x06 \x03(\x02')


_DATUM = descriptor.Descriptor(
  name='Datum',
  full_name='feat_extract.Datum',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  fields=[
    descriptor.FieldDescriptor(
      name='channels', full_name='feat_extract.Datum.channels', index=0,
      number=1, type=5, cpp_type=1, label=1,
      has_default_value=False, default_value=0,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
    descriptor.FieldDescriptor(
      name='height', full_name='feat_extract.Datum.height', index=1,
      number=2, type=5, cpp_type=1, label=1,
      has_default_value=False, default_value=0,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
    descriptor.FieldDescriptor(
      name='width', full_name='feat_extract.Datum.width', index=2,
      number=3, type=5, cpp_type=1, label=1,
      has_default_value=False, default_value=0,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
    descriptor.FieldDescriptor(
      name='data', full_name='feat_extract.Datum.data', index=3,
      number=4, type=12, cpp_type=9, label=1,
      has_default_value=False, default_value="",
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
    descriptor.FieldDescriptor(
      name='label', full_name='feat_extract.Datum.label', index=4,
      number=5, type=5, cpp_type=1, label=1,
      has_default_value=False, default_value=0,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
    descriptor.FieldDescriptor(
      name='float_data', full_name='feat_extract.Datum.float_data', index=5,
      number=6, type=2, cpp_type=6, label=3,
      has_default_value=False, default_value=[],
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  options=None,
  is_extendable=False,
  extension_ranges=[],
  serialized_start=29,
  serialized_end=134,
)

DESCRIPTOR.message_types_by_name['Datum'] = _DATUM

class Datum(message.Message):
  __metaclass__ = reflection.GeneratedProtocolMessageType
  DESCRIPTOR = _DATUM

  # @@protoc_insertion_point(class_scope:feat_extract.Datum)

# @@protoc_insertion_point(module_scope)

lmdb2mat.py:

import lmdb
import feat_helper_pb2
import numpy as np
import scipy.io as sio
import time

def main(argv):
    lmdb_name = sys.argv[1]
    print "%s" % sys.argv[1]
    batch_num = int(sys.argv[2]);
    batch_size = int(sys.argv[3]);
    window_num = batch_num*batch_size;

    start = time.time()
    if 'db' not in locals().keys():
        db = lmdb.open(lmdb_name)
        txn= db.begin()
        cursor = txn.cursor()
        cursor.iternext()
        datum = feat_helper_pb2.Datum()

        keys = []
        values = []
        for key, value in enumerate( cursor.iternext_nodup()):
            keys.append(key)
            values.append(cursor.value())

    ft = np.zeros((window_num, int(sys.argv[4])))
    for im_idx in range(window_num):
        datum.ParseFromString(values[im_idx])
        ft[im_idx, :] = datum.float_data

    print 'time 1: %f' %(time.time() - start)
    sio.savemat(sys.argv[5], {'feats':ft})
    print 'time 2: %f' %(time.time() - start)
    print 'done!'

if __name__ == '__main__':
    import sys
    main(sys.argv)

(2) 在caffe 根目录下创建脚本文件extract_feature_example1.sh, 并执行,将在examples/_temp文件夹下得到lmdb文件(features_conv1)和.mat文件(features_conv1.mat)

cd caffe
sudo gedit extract_feature_example.sh

需要添加的内容如下:

#!/usr/bin/env sh
# args for EXTRACT_FEATURE
TOOL=./build/tools
MODEL=./examples/imagenet/caffe_reference_imagenet_model #下载得到的caffe model
PROTOTXT=./examples/_temp/imagenet_val.prototxt # 网络定义
LAYER=conv1 # 提取层的名字,如提取fc7等
LEVELDB=./examples/_temp/features_conv1 # 保存的leveldb路径
BATCHSIZE=10

# args for LEVELDB to MAT
DIM=290400 # 需要手工计算feature长度
OUT=./examples/_temp/features_conv1.mat #.mat文件保存路径
BATCHNUM=1 # 有多少个batch, 本例只有两张图, 所以只有一个batch

$TOOL/extract_features.bin  $MODEL $PROTOTXT $LAYER $LEVELDB $BATCHSIZE lmdb
python lmdb2mat.py $LEVELDB $BATCHNUM  $BATCHSIZE $DIM $OUT

执行该文件

cd caffe
sh extract_feature_example.sh

执行完之后,会在_temp文件夹下生成features_conv1文件夹和features_conv1.mat文件

参考:
https://blog.youkuaiyun.com/jiandanjinxin/article/details/50410290
http://caffe.berkeleyvision.org/gathered/examples/feature_extraction.html
http://caffe.berkeleyvision.org/tutorial/interfaces.html

### 使用CNN进行特征提取的图片与可视化结果 卷积神经网络(Convolutional Neural Networks, CNNs)在图像识别领域具有广泛的应用,其核心功能之一是对输入图像中的多特征进行自动提取。为了帮助理解这一过程,研究人员开发了许多工具和技术来可视化这些特征。 #### 1. **低特征** 低特征通常由靠近输入端的卷积捕获,主要表现为简单的视觉模式,例如边缘、角点和基本纹理[^1]。 下图展示了某个预训练模型前几特征图: ![Low-Level Features](https://via.placeholder.com/400x300?text=Low-level+Features) 可以看到,在较低次上,CNN能够很好地捕捉到原始像素级别的细节信息,比如物体边界或颜色变化区域[^2]。 --- #### 2. **高特征** 随着数增加,CNN逐渐关注更复杂的结构化信息,例如局部形状组合或者特定类别对象的部分(如动物的眼睛、鼻子)。最终到达顶时,则会形成针对目标分类任务高度优化的整体表示形式[^1]。 以下是较高次上的特征映射实例: ![High-Level Features](https://via.placeholder.com/400x300?text=High-level+Features) 从该图可以看出,相比起底较为具体的图案描述,这里已经转变为更为抽象的概念表达方式;而且由于经过多次非线性变换操作之后,数据分布变得更加紧凑集中于某些关键部位周围[^3]。 --- #### 3. **具体案例:猫脸检测** 当我们将一张猫咪照片送入一个典型的深度学习框架下的Caffe/TensorFlow实现版本之中后,可以获得如下几个阶段性的中间产物展示效果[^4]: - **第一 (Layer 1)**: 显示的是最基础的一些线条方向感知能力。 ```plaintext Edge detection results. ``` - **第五至第八 (Layers 5–8)**: 开始显现部分器官轮廓,像耳朵尖端位置变得明显起来。 ![Middle Layers](https://via.placeholder.com/400x300?text=Middle-Layers) - **最后一两**: 几乎只保留了那些对于判断是否属于“猫”这个标签至关重要的线索要素而已。 ```plaintext Abstract semantic features focusing on cat's face structure. ``` --- #### 代码示例:如何利用PyTorch Hook机制获取指定输出? 下面提供了一段基于Python编程语言编写的小程序片段用于演示怎样通过挂钩技术截取任意内部状态以便后续绘制成图形界面供观察者直观感受整个流程运作情况: ```python import torch from torchvision import models import matplotlib.pyplot as plt def get_activation(name): def hook(model, input, output): activations[name] = output.detach() return hook model = models.vgg16(pretrained=True).eval() # Load pre-trained VGG model activations = {} # Register hooks to specific layers you want to visualize for name, layer in model.named_children(): if isinstance(layer, torch.nn.Conv2d): # Only attach to Conv2D layers layer.register_forward_hook(get_activation(name)) image = ... # Your image tensor here output = model(image) fig, axes = plt.subplots(nrows=len(activations), ncols=4, figsize=(16, 12)) for i, (name, act) in enumerate(activations.items()): for j in range(min(act.shape[1], 4)): # Show first four channels of each activation map ax = axes[i][j] ax.imshow(act[0,j].cpu().numpy(), cmap='viridis') ax.set_title(f"{name} Channel {j}") plt.tight_layout() plt.show() ``` 此脚本允许用户轻松定制所需监控的具体哪一部分网络架构,并自动生成相应的热力图表征出来便于分析研究之用。 --- ### 总结说明 以上就是有关采用CNN执行特征抽取过程中所产生的典型成果样本及其背后逻辑原理概述。借助现代软件平台所提供的强大支持手段,不仅可以深入探究各个独立单元的工作机理,还能有效提升整体系统的可解释性和透明度水平。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值