【caffe范例详解】 - 1.Classification分类
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分类的更多相关文章
- 详解BOM用途分类及在汽车企业中的应用
摘要:在整车企业中,信息系统的BOM是联系CAD.CAPP.PDM和ERP的纽带,按照用途划分产品要经过产品设计,工程设计.工艺制造设计.生产制造4个阶段,相应的在这4个过程中分别产生了名称十分相似但 ...
- SSD算法及Caffe代码详解(最详细版本)
SSD(single shot multibox detector)算法及Caffe代码详解 https://blog.csdn.net/u014380165/article/details/7282 ...
- Windows下caffe安装详解(仅CPU)
本文大多转载自 http://blog.csdn.net/guoyk1990/article/details/52909864,加入部分自己实战心得. 1.环境:windows 7\VS2013 2. ...
- SSD(single shot multibox detector)算法及Caffe代码详解[转]
转自:AI之路 这篇博客主要介绍SSD算法,该算法是最近一年比较优秀的object detection算法,主要特点在于采用了特征融合. 论文:SSD single shot multibox det ...
- 机器学习 | 详解GBDT在分类场景中的应用原理与公式推导
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是机器学习专题的第31篇文章,我们一起继续来聊聊GBDT模型. 在上一篇文章当中,我们学习了GBDT这个模型在回归问题当中的原理.GBD ...
- caffe参数详解
转载自:https://blog.csdn.net/qq_14845119/article/details/54929389 solver.prototxt net:训练预测的网络描述文件,trai ...
- 转 Yolov3转化Caffe框架详解
转自https://blog.csdn.net/watermelon1123/article/details/82083522 前些日子因工程需求,需要将yolov3从基于darknet转化为基于Ca ...
- java内部类深入详解 内部类的分类 特点 定义方式 使用
本文关键词: java内部类 内部类的分类 特点 定义方式 使用 外部类调用内部类 多层嵌套内部类 内部类访问外部类属性 接口中的内部类 内部类的继承 内部类的覆盖 局部内部类 成员内 ...
- caffe 配置文件详解
主要是遇坑了,要记录一下. solver算是caffe的核心的核心,它协调着整个模型的运作.caffe程序运行必带的一个参数就是solver配置文件.运行代码一般为 # caffe train --s ...
随机推荐
- PhoneGap API 之多媒体
一. MediaApi 简单介绍 PhoneGap API Media 对象提供录制和回放设备上的音频文件的能力 参数: var media = new Media(src, mediaSuccess ...
- 阅读Hibernate框架Session接口的源码的笔记
一.阅读Session接口的注释的理解.(看着注释翻译的,有些地方可能翻译的不太对,有疑问再看源文档) ①.Session的生命周期随着事务开始而开始,事务的结束而结束. 附原文:The lifecy ...
- .net控件
Asp.net 自带的Ajax Extensions中得ScriptManage和 UpdatePanel可以一起实现局部刷新,提高速度和节省网络流量 前台代码: <!DOCTYPE html ...
- 可决系数R^2和MSE,MAE,SMSE
波士顿房价预测 首先这个问题非常好其实要完整的回答这个问题很有难度,我也没有找到一个完整叙述这个东西的资料,所以下面主要是结合我自己的理解和一些资料谈一下r^2,mean square error 和 ...
- mybaitis动态sql利用bind标签代替%拼接完成模糊查询
Oracle中使用bind的写法 <select id="selectUser" resultType="user" parameterType=&quo ...
- C# 面向对象之多态
//多态:让一个对象表现的多种状态 //实现:子类重写父类的虚方法 Person person = new Person(); Chinese chinese = new Chinese(); Ame ...
- shrio的rememberMe不起作用
在移植项目.每次重启服务器需要登录.比较麻烦.于是研究下shrio的功能. rememberMe大概可以满足我的需求.但是跟着网上配置了.不起作用...... 于是乎查看源代码拉.java的好处... ...
- js判断值是不是全是数字
if(isNaN(value)){ 不是数字 }else{ 全是数字 }
- linux下安装rar
1. sudo wget https://www.rarlab.com/rar/rarlinux-x64-5.5.0.tar.gz 2.解压 tar -zxf rarlinux-5.0.1.tar.g ...
- oracle本地安装注意事项
这两天组员在本地windows上安装oracle数据库,安装完各种问题,pl/sql developer以及tns_admin配置以及tnsnames.ora和sqlnet.ora listener. ...