1. 安装

  • 首先,导入numpy和matplotlib库
# numpy是常用的科学计算库,matplot是常用的绘图库
import numpy as np
import matplotlib.pyplot as plt
# 在notebook中展示图例
%matplotlib inline # 设置图例展示的默认参数
plt.rcParams['figure.figsize'] = (10, 10) # 图片大小为10*10
plt.rcParams['image.interpolation'] = 'nearest' # 图片的放缩采用最近邻插值法
plt.rcParams['image.cmap'] = 'gray' # 图片的色彩图为灰阶
  • 导入caffe框架
import caffe
# 传入caffe的地址,因为该例程的一些数据都存在caffe目录中
caffe_root = r'G:/Projects/caffe/'

2. 载入网络并对输入进行预处理

  • 设置caffe为cpu模式
caffe.set_mode_cpu()
  • 基于已有的一些配置项定义网络模型
model_def = caffe_root + 'models/bvlc_reference_caffenet/deploy.prototxt'
model_weights = caffe_root + 'models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel' net = caffe.Net(model_def, # 定义模型的结构
model_weights, # 载入预训练模型的权重
caffe.TEST) # 用测试模型进行测试(测试模型中不包括dropout层)
  • 详细解释一下模型配置文件(deploy.prototxt)
name: "CaffeNet"        # 模型的名称(自定义)
layer { # 第一层(输入层)
name: "data" # 该层的名称(自定义),此处为“data”
type: "Input" # 该层的类型(重要参数),此处为输入层(Input)
top: "data" # 该层的输出数组名为“data”
input_param { shape: { dim: 10 dim: 3 dim: 227 dim: 227 } }
} # 配置输入层参数,4维数组,数量*通道*高*宽,即10张RGB彩色图像,大小为227*227
layer { # 第二层(卷积层1)
name: "conv1" # 该层的名称(自定义),此处为“conv1”
type: "Convolution" # 该层的类型(重要参数),此处为卷积层(Convolution)
bottom: "data" # 该层的输入数组名为“data”
top: "conv1" # 该层的输出数组名为“conv1”
convolution_param { # 配置卷积层参数
num_output: 96 # 卷积核数量为96
kernel_size: 11 # 卷积核大小为11*11
stride: 4 # 卷积核的步长为4
}
}
layer { # 第三层(激活层1)
name: "relu1" # 该层的名称(自定义),此处为“relu1”
type: "ReLU" # 该层的类型(重要参数),此处是以ReLu为激活函数的激活层
bottom: "conv1" # 该层的输入数组名为“conv1”
top: "conv1" # 该层的输出数组名为“conv1”
}
layer { # 第四层(池化层1)
name: "pool1" # 该层的名称(自定义),此处为“pool1”
type: "Pooling" # 该层的类型(重要参数),此处是池化层
bottom: "conv1" # 该层的输入数组名为“conv1”
top: "pool1" # 该层的输出数字名为“pool1”
pooling_param { # 配置池化层参数
pool: MAX # 池化方式为 Max-pooling
kernel_size: 3 # 池化区域大小为3*3
stride: 2 # 池化操作的步长为2
}
}
layer { # 第五层(局部响应归一化层)
name: "norm1" # 该层的名称(自定义),此处为“norm1”
type: "LRN" # 该层的类型(重要参数),此处是LRN(Local Response Normalization)
bottom: "pool1" # 该层的输入数组名为“pool1”
top: "norm1" # 该层的输出数组名为“norm1”
lrn_param { # 配置LRN层参数
local_size: 5 # 参与求和的矩形区域边长
alpha: 0.0001 # 尺度参数
beta: 0.75 # 指数参数
}
}
layer { # 第六层(卷积层2)
name: "conv2" # 该层的名称(自定义),此处为“conv2”
type: "Convolution" # 该层的类型(重要参数),此处为卷积层(Convolution)
bottom: "norm1" # 该层的输入数组名为“norm1”
top: "conv2" # 该层的输出数组名为“conv2”
convolution_param { # 配置卷积层参数
num_output: 256 # 卷积核数量为256
pad: 2 # 指定添加到输入每条边的像素点个数
kernel_size: 5 # 卷积核大小为5*5
group: 2 # 分组卷积操作的组数为2,即输入和输出的通道都被分为2组,第1组输入只会连接到第1组输出,第2组输入只会连接到第2组输出。
}
}
layer {
name: "relu2"
type: "ReLU"
bottom: "conv2"
top: "conv2"
}
layer {
name: "pool2"
type: "Pooling"
bottom: "conv2"
top: "pool2"
pooling_param {
pool: MAX
kernel_size: 3
stride: 2
}
}
layer {
name: "norm2"
type: "LRN"
bottom: "pool2"
top: "norm2"
lrn_param {
local_size: 5
alpha: 0.0001
beta: 0.75
}
}
layer {
name: "conv3"
type: "Convolution"
bottom: "norm2"
top: "conv3"
convolution_param {
num_output: 384
pad: 1
kernel_size: 3
}
}
layer {
name: "relu3"
type: "ReLU"
bottom: "conv3"
top: "conv3"
}
layer {
name: "conv4"
type: "Convolution"
bottom: "conv3"
top: "conv4"
convolution_param {
num_output: 384
pad: 1
kernel_size: 3
group: 2
}
}
layer {
name: "relu4"
type: "ReLU"
bottom: "conv4"
top: "conv4"
}
layer {
name: "conv5"
type: "Convolution"
bottom: "conv4"
top: "conv5"
convolution_param {
num_output: 256
pad: 1
kernel_size: 3
group: 2
}
}
layer {
name: "relu5"
type: "ReLU"
bottom: "conv5"
top: "conv5"
}
layer {
name: "pool5"
type: "Pooling"
bottom: "conv5"
top: "pool5"
pooling_param {
pool: MAX
kernel_size: 3
stride: 2
}
}
layer { # 第(我也不知道多少)层(全连接层)
name: "fc6" # 该层的名称(自定义),此处为“fc6”
type: "InnerProduct" # 该层的类型(重要参数),此处为内积层(也叫全连接层)
bottom: "pool5" # 该层的输入数组名为“pool5”
top: "fc6" # 该层的输出数组名为“fc6”
inner_product_param { # 配置全连接层参数
num_output: 4096 # 该层的神经元个数
}
}
layer {
name: "relu6"
type: "ReLU"
bottom: "fc6"
top: "fc6"
}
layer { # 第(我也不知道多少+2)层(Dropout层,防止过拟合)
name: "drop6" # 该层的名称(自定义),此处为“drop6”
type: "Dropout" # 该层的类型(重要参数),此处为Dropout层
bottom: "fc6" # 该层的输入数组名为“fc6”
top: "fc6" # 该层的输出数组名为“fc6”
dropout_param { # 配置Dropout层参数
dropout_ratio: 0.5 # dropout的比例系数为0.5
}
}
layer {
name: "fc7"
type: "InnerProduct"
bottom: "fc6"
top: "fc7"
inner_product_param {
num_output: 4096
}
}
layer {
name: "relu7"
type: "ReLU"
bottom: "fc7"
top: "fc7"
}
layer {
name: "drop7"
type: "Dropout"
bottom: "fc7"
top: "fc7"
dropout_param {
dropout_ratio: 0.5
}
}
layer {
name: "fc8"
type: "InnerProduct"
bottom: "fc7"
top: "fc8"
inner_product_param {
num_output: 1000
}
}
layer { # 第(最后一)层(输出层)
name: "prob" # 该层的名称(自定义),此处为“prob”,含义是输出概率
type: "Softmax" # 该层的类型(重要参数),此处为Softmax层
bottom: "fc8" # 该层的输入数组名为“fc8”
top: "prob" # 该层的输出数组名为“prob”
}
  • 给每一层都命名的好处在于之后可以方便的调用任意一层的输入或者输出来进行分析(如可视化,或者是提取特征等等)

  • 一般而言,习惯于将激活层、Dropout层的输入输出给予同样的名称,因为这并不是我们所关心的神经网络黑箱中的内容,相对而言,我们更关心卷积层、池化层的输出(常见的finetune技巧即提取conv或者是pooling层的输出作为特征)

  • 一些预处理的准备工作

# 载入ImageNet的均值图,是一个3*256*256的矩阵
mu = np.load(caffe_root + 'python/caffe/imagenet/ilsvrc_2012_mean.npy')
mu = mu.mean(1).mean(1) # 计算出BGR通道的平均像素值
print('Mean-subtracted values:', list(zip('BGR', mu)))
Mean-subtracted values: [('B', 104.0069879317889), ('G', 116.66876761696767), ('R', 122.6789143406786)]
  • 这里我们使用 caffe.io.Transformer 对输入数据进行预处理。

    我们默认的CaffeNet是将彩色图片以BGR格式读入,并且范围是[0, 255],通道维在第一个(outermost)维度上。

# 为该网络的输入层(“data”层,10*3*227*227的数组)创建一个transformer对象
transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape}) transformer.set_transpose('data', (2,0,1)) # 用caffe.io.load_image读入的图像以row*col*channel形式储存,而在caffe中我们定义的是channel*row*col,
# 因此需要转换读入的图像数据,将原来下标为2的channel维移至下标为0的位置。
transformer.set_mean('data', mu) # 对每一个色彩通道做减均值的操作
transformer.set_raw_scale('data', 255) # 将归一化的数据转为[0, 255]范围的数据
transformer.set_channel_swap('data', (2,1,0)) # 将色彩通道的顺序由RGB改为BGR(这样减去均值的时候才是对应的)

3. 基于CPU的分类

  • 尽管我们只分类一张图片,也还是可以设置batch-size为50
# 设置输入图片的大小
net.blobs['data'].reshape(50, # batch size
3, # 3-channel (BGR) images
227, 227) # image size is 227x227
image = caffe.io.load_image(caffe_root + 'examples/images/cat.jpg')
transformed_image = transformer.preprocess('data', image)
plt.imshow(image)
<matplotlib.image.AxesImage at 0x150008d3128>

  • 下面开始进行分类
# 将该图片传入网络作为输入(这样似乎是把输入的十张图都表示成该图像)
net.blobs['data'].data[...] = transformed_image # 开始进行分类
# 将输入参数进行前向传播(因为我们已经载入了预先训练的网络,因此一次前向传播即可获得最终结果)
output = net.forward()
# 获得对第一张图片的输出预测向量
output_prob = output['prob'][0]
# 选取其中获得最大预测值的向量下标,作为预测的分类下标
print('predicted class is:', output_prob.argmax())
predicted class is: 281
  • 通过比对下标在ImageNet中表示的真实类别,验证分类结果的准确性
import os
# load ImageNet labels
labels_file = caffe_root + 'data/ilsvrc12/synset_words.txt'
if not os.path.exists(labels_file):
!caffe_root + 'data/ilsvrc12/get_ilsvrc_aux.sh' labels = np.loadtxt(labels_file, str, delimiter='\t') print('output label:', labels[output_prob.argmax()])
output label: b'n02123045 tabby, tabby cat'
  • 该类别下标表示的是 tabby cat, 结果正确!

  • 我们可以看一下该图片在1000类中top5可能性的分类结果

# 将softmax输出的结果用argsort()方法排序并翻转(因为该方法默认由小到大排序)
# 取前5项输出
top_inds = output_prob.argsort()[::-1][:5] print('probabilities and labels:')
list(zip(output_prob[top_inds], labels[top_inds]))
probabilities and labels:

[(0.3124359, "b'n02123045 tabby, tabby cat'"),
(0.23797129, "b'n02123159 tiger cat'"),
(0.1238722, "b'n02124075 Egyptian cat'"),
(0.10075704, "b'n02119022 red fox, Vulpes vulpes'"),
(0.070956774, "b'n02127052 lynx, catamount'")]

4. 切换到GPU模式

  • 我们来看一下用cpu做计算时,一次前向传播的耗时
%timeit net.forward()
1 loop, best of 3: 1.61 s per loop
  • 切换到GPU模式,再看一下前向传播的耗时
caffe.set_device(0)  # 如果我们有多个GPU的话,选择第一个
caffe.set_mode_gpu() # 切换到GPU模式
net.forward() # 进行一次前向传播运算
%timeit net.forward()
10 loops, best of 3: 26 ms per loop

5. 检查中间输出

  • 我们来打开网络中间这个“黑箱子”,看看其中的一些参数和激活值

  • 首先,我们来看一下网络中各层神经元输出的参数(即激活值)的shape

  • 对于每一层,我们关注以下信息:(批处理数据大小, 通道数, 高度, 宽度),即 (batch_size, channel_dim, height, width)

  • 激活值保存在net.blobs中

for layer_name, blob in net.blobs.items():
print(layer_name, '\t', str(blob.data.shape))
data 	 (10, 3, 227, 227)
conv1 (10, 96, 55, 55)
pool1 (10, 96, 27, 27)
norm1 (10, 96, 27, 27)
conv2 (10, 256, 27, 27)
pool2 (10, 256, 13, 13)
norm2 (10, 256, 13, 13)
conv3 (10, 384, 13, 13)
conv4 (10, 384, 13, 13)
conv5 (10, 256, 13, 13)
pool5 (10, 256, 6, 6)
fc6 (10, 4096)
fc7 (10, 4096)
fc8 (10, 1000)
prob (10, 1000)
  • 然后,我们来看一下各层之间参数的shape

  • 参数值保存在net.params中,权重(weight)在[0]下标的索引中,偏置(bias)在[1]下标的索引中。

  • 对于权重,我们关注(输出通道数,输入通道数,滤波器高度,滤波器宽度),即 (output_channels, input_channels, filter_height, filter_width)

  • 对于偏置,我们关注(输出通道数,),即(output_channels,)

for layer_name, param in net.params.items():
print( layer_name, '\t', str(param[0].data.shape), str(param[1].data.shape))
conv1 	 (96, 3, 11, 11) (96,)
conv2 (256, 48, 5, 5) (256,)
conv3 (384, 256, 3, 3) (384,)
conv4 (384, 192, 3, 3) (384,)
conv5 (256, 192, 3, 3) (256,)
fc6 (4096, 9216) (4096,)
fc7 (4096, 4096) (4096,)
fc8 (1000, 4096) (1000,)
  • 我们来定义一个矩形热力图的可视化函数来帮助我们观察“黑箱”中的特征
def vis_square(data):
"""
输入矩阵的大小为(n, height, width)或者(n, height, width, 3)
可视化每一个输入以sqrt(n)*sqrt(n)的方格展示
""" # 归一化
data = (data - data.min()) / (data.max() - data.min()) # force the number of filters to be square
n = int(np.ceil(np.sqrt(data.shape[0])))
padding = (((0, n ** 2 - data.shape[0]),
(0, 1), (0, 1)) # add some space between filters
+ ((0, 0),) * (data.ndim - 3)) # don't pad the last dimension (if there is one)
data = np.pad(data, padding, mode='constant', constant_values=1) # pad with ones (white) # 将滤波器排列成图片的shape用于展示
data = data.reshape((n, n) + data.shape[1:]).transpose((0, 2, 1, 3) + tuple(range(4, data.ndim + 1)))
data = data.reshape((n * data.shape[1], n * data.shape[3]) + data.shape[4:]) plt.imshow(data); plt.axis('off')
  • 首先,我们来看第一层滤波器conv1
# 可视化的参数是 [weights, biases]
filters = net.params['conv1'][0].data
vis_square(filters.transpose(0, 2, 3, 1))

  • 第一层conv1的输出(只展示前36个)
feat = net.blobs['conv1'].data[0, :36]
vis_square(feat)

  • 第五层pooling之后的输出,pool5
feat = net.blobs['pool5'].data[0]
vis_square(feat)

  • 第一个全连接层,fc6
  • 展示输出值以及正值的直方图
feat = net.blobs['fc6'].data[0]
plt.subplot(2, 1, 1)
plt.plot(feat.flat)
plt.subplot(2, 1, 2)
_ = plt.hist(feat.flat[feat.flat > 0], bins=100)

  • 最终概率预测输出的直方图
feat = net.blobs['prob'].data[0]
plt.figure(figsize=(15, 3))
plt.plot(feat.flat)
[<matplotlib.lines.Line2D at 0x15017512278>]


【caffe范例详解】 - 1.Classification分类的更多相关文章

  1. 详解BOM用途分类及在汽车企业中的应用

    摘要:在整车企业中,信息系统的BOM是联系CAD.CAPP.PDM和ERP的纽带,按照用途划分产品要经过产品设计,工程设计.工艺制造设计.生产制造4个阶段,相应的在这4个过程中分别产生了名称十分相似但 ...

  2. SSD算法及Caffe代码详解(最详细版本)

    SSD(single shot multibox detector)算法及Caffe代码详解 https://blog.csdn.net/u014380165/article/details/7282 ...

  3. Windows下caffe安装详解(仅CPU)

    本文大多转载自 http://blog.csdn.net/guoyk1990/article/details/52909864,加入部分自己实战心得. 1.环境:windows 7\VS2013 2. ...

  4. SSD(single shot multibox detector)算法及Caffe代码详解[转]

    转自:AI之路 这篇博客主要介绍SSD算法,该算法是最近一年比较优秀的object detection算法,主要特点在于采用了特征融合. 论文:SSD single shot multibox det ...

  5. 机器学习 | 详解GBDT在分类场景中的应用原理与公式推导

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是机器学习专题的第31篇文章,我们一起继续来聊聊GBDT模型. 在上一篇文章当中,我们学习了GBDT这个模型在回归问题当中的原理.GBD ...

  6. caffe参数详解

     转载自:https://blog.csdn.net/qq_14845119/article/details/54929389 solver.prototxt net:训练预测的网络描述文件,trai ...

  7. 转 Yolov3转化Caffe框架详解

    转自https://blog.csdn.net/watermelon1123/article/details/82083522 前些日子因工程需求,需要将yolov3从基于darknet转化为基于Ca ...

  8. java内部类深入详解 内部类的分类 特点 定义方式 使用

    本文关键词: java内部类 内部类的分类 特点  定义方式 使用   外部类调用内部类 多层嵌套内部类  内部类访问外部类属性  接口中的内部类  内部类的继承  内部类的覆盖  局部内部类 成员内 ...

  9. caffe 配置文件详解

    主要是遇坑了,要记录一下. solver算是caffe的核心的核心,它协调着整个模型的运作.caffe程序运行必带的一个参数就是solver配置文件.运行代码一般为 # caffe train --s ...

随机推荐

  1. ApiServer_YiChat apache项目布置过程

    1.复制文件到   /var/www/  文件夹下 2.配置项目目录 3.修改/var/www/api/public 文件夹下的隐藏文件  .htaccess     增加‘?’号 4.打开/etc/ ...

  2. eclipse去掉xml验证的方法

    eclipse Multiple annotations found at this line错误,eclipse开发过程中,一些XML配置文件会报错,但是这些其实不是错,飘红的原因是因为eclips ...

  3. BZOJ2440:[中山市选2011]完全平方数(莫比乌斯函数)

    Description 小 X 自幼就很喜欢数.但奇怪的是,他十分讨厌完全平方数.他觉得这些数看起来很令人难受.由此,他也讨厌所有是完全平方数的正整数倍的数.然而这丝毫不影响他对其他数的热爱. 这天是 ...

  4. Thread-Specific-Storage for C/C++

    引用出处:https://www.cse.wustl.edu/~schmidt/PDF/TSS-pattern.pdf 摘要: 理论上多线程会提高程序性能,但实际上,由于在获取和释放锁的开销,多线程经 ...

  5. libconfig C++ 学习笔记

    1. C++API 头文件 #include <libconfig.h++> ,命名空间:using namespace libconfig; 2.多线程使用问题: (1)libconfi ...

  6. 7.spring:SpringAOP(配置文件)

    SpringAOP(xml文件配置) 配置文件的方式,主要是在xml文件中进行配置,不使用注解! 目录: AtithmeticCalculator.java public interface Atit ...

  7. Linux实用指令(1):运行级别&找回密码&帮助指令&文件目录类

    指令运行级别: 0 :关机 1 :单用户[找回丢失密码] 2:多用户状态没有网络服务 3:多用户状态有网络服务 4:系统未使用保留给用户 5:图形界面 6:系统重启   常用运行级别是3和5 ,要修改 ...

  8. iPhone 耳机在PC电脑上使用方法

    把主声道(Master)从正中间调整到最左或者最右就行了

  9. 手机站全局的html+css加载等待效果

    本文只提供思路,CSS神马的自己定制吧,JS是可以优化的,比如,输出图片的JS也可以放到showdiv()里面,我没有做优化,只是实现,别笑话我,我比较懒... 基本思路:由于Html的解析是从上到下 ...

  10. java 编写小工具 尝试 学习(二)

    1. 新建一个窗口  ,代码 如下 ,截图 如下 package jFrameDemo; import javax.swing.JFrame; import javax.swing.WindowCon ...