如何在caffe中caffe实现rnn矩阵操作

君,已阅读到文档的结尾了呢~~
广告剩余8秒
文档加载中
Caffe 深度学习框架上手教程
扫扫二维码,随身浏览文档
手机或平板扫扫即可继续访问
Caffe 深度学习框架上手教程
举报该文档为侵权文档。
举报该文档含有违规或不良信息。
反馈该文档无法正常浏览。
举报该文档为重复文档。
推荐理由:
将文档分享至:
分享完整地址
文档地址:
粘贴到BBS或博客
flash地址:
支持嵌入FLASH地址的网站使用
html代码:
&embed src='/DocinViewer--144.swf' width='100%' height='600' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'&&/embed&
450px*300px480px*400px650px*490px
支持嵌入HTML代码的网站使用
您的内容已经提交成功
您所提交的内容需要审核后才能发布,请您等待!
3秒自动关闭窗口转载请注明出处,楼燚(yì)航的blog,
Vision_layer里面主要是包括了一些关于一些视觉上的操作,比如卷积、反卷积、池化等等。这里的类跟data layer一样好很多种继承关系。主要包括了这几个类,其中CuDNN分别是CUDA版本,这里先不讨论,在这里先讨论ConvolutionLayer
BaseConvolutionLayer
ConvolutionLaye
DeconvolutionLayer
CuDNNConvolutionLayer
Im2colLayer
CuDNNLRNLayer
CuDNNLCNLayer
PoolingLayer
CuDNNPoolingLayer
这里我画了一个类图,将关系梳理了一下:
BaseConvolutionLayer
其继承自Layer,是一个卷积以及反卷积操作的基类,首先我们来看BaseConvolutionLayer的LayerSetUp函数
void BaseConvolutionLayer&Dtype&::LayerSetUp(const vector&Blob&Dtype&*&& bottom,
const vector&Blob&Dtype&*&& top)
//首先这里主要是在配置卷积kernel 的size,padding,stride以及inputs
ConvolutionParameter conv_param = this-&layer_param_.convolution_param();
force_nd_im2col_ = conv_param.force_nd_im2col();
channel_axis_ = bottom[0]-&CanonicalAxisIndex(conv_param.axis());
const int first_spatial_axis = channel_axis_ + 1;
const int num_axes = bottom[0]-&num_axes();
num_spatial_axes_ = num_axes - first_spatial_
CHECK_GE(num_spatial_axes_, 0);
vector&int& bottom_dim_blob_shape(1, num_spatial_axes_ + 1);
vector&int& spatial_dim_blob_shape(1, std::max(num_spatial_axes_, 1));
// 设置kernel的dimensions
kernel_shape_.Reshape(spatial_dim_blob_shape);
int* kernel_shape_data = kernel_shape_.mutable_cpu_data();
接着是设置相应的stride dimensions,对于2D,设置在h和w方向上的stride,代码太长列出简要的
pad_.Reshape(spatial_dim_blob_shape);
int* pad_data = pad_.mutable_cpu_data();
pad_data[0] = conv_param.pad_h();
pad_data[1] = conv_param.pad_w();
......一堆if else判断
对于kernel的pad也做相应设置
pad_.Reshape(spatial_dim_blob_shape);
int* pad_data = pad_.mutable_cpu_data();
pad_data[0] = conv_param.pad_h();
pad_data[1] = conv_param.pad_w();
接下来是对widhts 和bias左设置和填充,其中blob[0]里面存放的是filter weights,而blob[1]里面存放的是biases,当然biases是可选的,也可以没有
//设置相应的shape,并检查
vector&int& weight_shape(2);
weight_shape[0] = conv_out_channels_;
weight_shape[1] = conv_in_channels_ / group_;
bias_term_ = this-&layer_param_.convolution_param().bias_term();
vector&int& bias_shape(bias_term_, num_output_);
//填充权重
this-&blobs_[0].reset(new Blob&Dtype&(weight_shape));
shared_ptr&Filler&Dtype& & weight_filler(GetFiller&Dtype&(
this-&layer_param_.convolution_param().weight_filler()));
weight_filler-&Fill(this-&blobs_[0].get());
//填充偏置项
if (bias_term_) {
this-&blobs_[1].reset(new Blob&Dtype&(bias_shape));
shared_ptr&Filler&Dtype& & bias_filler(GetFiller&Dtype&(
this-&layer_param_.convolution_param().bias_filler()));
bias_filler-&Fill(this-&blobs_[1].get());
ConvolutionLayer
ConvolutionLayer继承了BaseConvolutionLayer,主要作用就是将一副image做卷积操作,使用学到的filter的参数和biaes。同时在Caffe里面,卷积操作做了优化,变成了一个矩阵相乘的操作。其中有两个比较主要的函数是im2col以及col2im。
图中上半部分是一个传统卷积,下图是一个矩阵相乘的版本
下图是在一个卷积层中将卷积操作展开的具体操作过程,他里面按照卷积核的大小取数据然后展开,在同一张图里的不同卷积核选取的逐行摆放,不同N的话,就在同一行后面继续拼接,不同个可以是多个通道,但是需要注意的是同一行里面每一段都应该对应的是原图中中一个位置的卷积窗口。
对于卷积层中的卷积操作,还有一个group的概念要说明一下,groups是代表filter 组的个数。引入gruop主要是为了选择性的连接卷基层的输入端和输出端的channels,否则参数会太多。每一个group 和1/ group的input 通道和 1/group 的output通道进行卷积操作。比如有4个input, 8个output,那么1-4属于第一组,5-8属于第二个gruop
ConvolutionLayer里面,主要重写了Forward_cpu和Backward_cpu
void ConvolutionLayer&Dtype&::Forward_cpu(const vector&Blob&Dtype&*&& bottom,
const vector&Blob&Dtype&*&& top) {
const Dtype* weight = this-&blobs_[0]-&cpu_data();
for (int i = 0; i & bottom.size(); ++i) {
const Dtype* bottom_data = bottom[i]-&cpu_data();
Dtype* top_data = top[i]-&mutable_cpu_data();
for (int n = 0; n & this-&num_; ++n) {
this-&forward_cpu_gemm(bottom_data + n * this-&bottom_dim_, weight,
top_data + n * this-&top_dim_);
if (this-&bias_term_) {
const Dtype* bias = this-&blobs_[1]-&cpu_data();
this-&forward_cpu_bias(top_data + n * this-&top_dim_, bias);
可以看到其实这里面他调用了forward_cpu_gemm,而这个函数内部又调用了math_function里面的caffe_cpu_gemm的通用矩阵相乘接口,GEMM的全称是General Matrix Matrix Multiply。其基本形式如下:
\[C=alpha*op( A )*op( B ) + beta*C,\]
template &typename Dtype&
void ConvolutionLayer&Dtype&::Backward_cpu(const vector&Blob&Dtype&*&& top,
const vector&bool&& propagate_down, const vector&Blob&Dtype&*&& bottom) {
//反向传播梯度误差
const Dtype* weight = this-&blobs_[0]-&cpu_data();
Dtype* weight_diff = this-&blobs_[0]-&mutable_cpu_diff();
for (int i = 0; i & top.size(); ++i) {
const Dtype* top_diff = top[i]-&cpu_diff();
const Dtype* bottom_data = bottom[i]-&cpu_data();
Dtype* bottom_diff = bottom[i]-&mutable_cpu_diff();
//如果有bias项,计算Bias导数
if (this-&bias_term_ && this-&param_propagate_down_[1]) {
Dtype* bias_diff = this-&blobs_[1]-&mutable_cpu_diff();
for (int n = 0; n & this-&num_; ++n) {
this-&backward_cpu_bias(bias_diff, top_diff + n * this-&top_dim_);
//计算weight
if (this-&param_propagate_down_[0] || propagate_down[i]) {
for (int n = 0; n & this-&num_; ++n) {
// 计算weights权重的梯度
if (this-&param_propagate_down_[0]) {
this-&weight_cpu_gemm(bottom_data + n * this-&bottom_dim_,
top_diff + n * this-&top_dim_, weight_diff);
//计算botttom数据的梯度,下后传递
if (propagate_down[i]) {
this-&backward_cpu_gemm(top_diff + n * this-&top_dim_, weight,
bottom_diff + n * this-&bottom_dim_);
阅读(...) 评论()16:39:50 UTC
是一个清晰而高效的框架,其作者是博士毕业于UC Berkeley的,目前在工作。
是纯粹的C++/CUDA架构,支持命令行、Python和MATLAB接口;可以在CPU和直接无缝切换:
Caffe::set_mode(Caffe::GPU);
Caffe的优势
上手快:模型与相应优化都是以文本形式而非代码形式给出。Caffe给出了模型的定义、最优化设置以及预训练的权重,方便立即上手。
速度快:能够运行最棒的模型与海量的数据。Caffe与cuDNN结合使用,测试AlexNet模型,在K40上处理每张图片只需要1.17ms.
模块化:方便扩展到新的任务和设置上。可以使用Caffe提供的各层类型来定义自己的模型。
开放性:公开的代码和参考模型用于再现。
社区好:可以通过BSD-2参与开发与讨论。
Caffe的网络定义
Caffe中的网络都是有向无环图的集合,可以直接定义:
name: "dummy-net"
layers {name: "data" …}
layers {name: "conv" …}
layers {name: "pool" …}
layers {name: "loss" …}
数据及其导数以blobs的形式在层间流动。
Caffe的各层定义
Caffe层的定义由2部分组成:层属性与层参数,例如
name:"conv1"
type:CONVOLUTION
bottom:"data"
top:"conv1"
convolution_param{
num_output:20
kernel_size:5
weight_filler{
type: "xavier"
这段配置文件的前4行是层属性,定义了层名称、层类型以及层连接结构(输入blob和输出blob);而后半部分是各种层参数。
Blob是用以存储数据的4维数组,例如
对于数据:Number*Channel*Height*Width
对于卷积权重:Output*Input*Height*Width
对于卷积偏置:Output*1*1*1
网络参数的定义也非常方便,可以随意设置相应参数。
甚至调用GPU运算只需要写一句话:
solver_mode:GPU
Caffe的安装与配置
Caffe需要预先安装一些依赖项,首先是CUDA驱动。不论是CentOS还是Ubuntu都预装了开源的nouveau显卡驱动(SUSE没有这种问题),如果不禁用,则CUDA驱动不能正确安装。以Ubuntu为例,介绍一下这里的处理方法,当然也有其他处理方法。
# sudo vi/etc/modprobe.d/blacklist.conf
# 增加一行 :blacklist nouveau
sudoapt-get --purge remove xserver-xorg-video-nouveau
#把官方驱动彻底卸载:
sudoapt-get --purge remove nvidia-*
#清除之前安装的任何NVIDIA驱动
sudo service lightdm stop
#进命令行,关闭Xserver
sudo kill all Xorg
安装了CUDA之后,依次按照安装BLAS、OpenCV、Boost即可。
Caffe跑跑MNIST试试
在Caffe安装目录之下,首先获得MNIST数据集:
cd data/mnist
sh get_mnist.sh
生成mnist-train-leveldb/ 和 mnist-test-leveldb/,把数据转化成leveldb格式:
sh examples/mnist/create_mnist.sh
sh train_lenet.sh
16:46:57 UTC
让Caffe生成的数据集能在Theano上直接运行
不论使用何种框架进行CNNs训练,共有3种数据集:
Training Set:用于训练网络
Validation Set:用于训练时测试网络准确率
Test Set:用于测试网络训练完成后的最终正确率
Caffe生成的数据分为2种格式:Lmdb和Leveldb
它们都是键/值对(Key/Value Pair)嵌入式数据库管理系统编程库。虽然lmdb的内存消耗是leveldb的1.1倍,但是lmdb的速度比leveldb快10%至15%,更重要的是lmdb允许多种训练模型同时读取同一组数据集。因此lmdb取代了leveldb成为Caffe默认的数据集生成格式。
Google Protocol Buffer的安装
Protocol Buffer是一种类似于XML的用于序列化数据的自动机制。首先在Protocol Buffers的中下载最新版本:解压后运行:
./configure
$ make check
$ make install
pip installprotobuf
添加动态链接库
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
Lmdb的安装
pip install lmdb
要parse(解析)一个protobuf类型数据,首先要告诉计算机你这个protobuf数据内部是什么格式(有哪些项,这些项各是什么数据类型的决定了占用多少字节,这些项可否重复,重复几次),安装protobuf这个module就可以用protobuf专用的语法来定义这些格式(这个是.proto文件)了,然后用protoc来编译这个.proto文件就可以生成你需要的目标文件。想要定义自己的.proto文件请阅读:
编译.proto文件
protoc--proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR--python_out=DST_DIR path/to/file.proto
--proto_path 也可以简写成-I 是.proto所在的路径
输出路径:
--cpp_out 要生成C++可用的头文件,分别是***.pb.h(包含申明类)***.pb.cc(包含可执行类),使用的时候只要include “***.pb.h”
--java_out 生成java可用的头文件
--python_out 生成python可用的头文件,**_pb2.py,使用的时候import**_pb2.py即可
最后一个参数就是你的.proto文件完整路径。
16:54:22 UTC
Caffe (CNN, deep learning) 介绍
Caffe -----------Convolution Architecture For Feature Embedding (Extraction)
Caffe 是什么东东?
CNN (Deep Learning) 工具箱
C++ 语言架构
CPU 和GPU 无缝交换
Python 和matlab的封装
但是,Decaf只是CPU 版本。
为什么要用Caffe?
运算速度快。简单 友好的架构 用到的一些库:
Google Logging library (Glog): 一个C++语言的应用级日志记录框架,提供了C++风格的流操作和各种助手宏.
lebeldb(数据存储): 是一个google实现的非常高效的kv数据库,单进程操作。
CBLAS library(CPU版本的矩阵操作)
CUBLAS library (GPU 版本的矩阵操作)
Caffe 架构
预处理图像的leveldb构建输入:一批图像和label (2和3)输出:leveldb (4)指令里包含如下信息:
conver_imageset (构建leveldb的可运行程序)
(此目录放处理的jpg或者其他格式的图像)
(图像文件名及其label信息)
输出的leveldb文件夹的名字
CPU/GPU (指定是在cpu上还是在gpu上运行code)
CNN网络配置文件
Imagenet_solver.prototxt
(包含全局参数的配置的文件)
Imagenet.prototxt (包含训练网络的配置的文件)
Imagenet_val.prototxt (包含测试网络的配置文件)
17:01:25 UTC
Caffe深度学习之图像分类模型AlexNet解读
在imagenet上的图像分类challenge上Alex提出的alexnet网络结构模型赢得了2012届的冠军。要研究CNN类型DL网络模型在图像分类上的应用,就逃不开研究alexnet,这是CNN在图像分类上的经典模型(DL火起来之后)。
在DL开源实现caffe的model样例中,它也给出了alexnet的复现,具体网络配置文件如下
接下来本文将一步步对该网络配置结构中各个层进行详细的解读(训练阶段):
conv1阶段DFD(data flow diagram):
conv2阶段DFD(data flow diagram):
conv3阶段DFD(data flow diagram):
conv4阶段DFD(data flow diagram):
conv5阶段DFD(data flow diagram):
fc6阶段DFD(data flow diagram):
fc7阶段DFD(data flow diagram):
fc8阶段DFD(data flow diagram):
各种layer的operation更多解释可以参考
从计算该模型的数据流过程中,该模型参数大概5kw+。
caffe的输出中也有包含这块的内容日志,详情如下:
I:15.92 net.cpp:125] Top shape: 256 3 227 227 ()
I:15.92 net.cpp:125] Top shape: 256 1 1 1 (256)
I:15.92 net.cpp:156] data does not need backward computation.
I:15.92 net.cpp:74] Creating Layer conv1
I:15.92 net.cpp:84] conv1 &- data
I:15.92 net.cpp:110] conv1 -& conv1
I:16.92 net.cpp:125] Top shape: 256 96 55 55 ()
I:16.92 net.cpp:151] conv1 needs backward computation.
I:16.92 net.cpp:74] Creating Layer relu1
I:16.92 net.cpp:84] relu1 &- conv1
I:16.92 net.cpp:98] relu1 -& conv1 (in-place)
I:16.92 net.cpp:125] Top shape: 256 96 55 55 ()
I:16.92 net.cpp:151] relu1 needs backward computation.
I:16.92 net.cpp:74] Creating Layer pool1
I:16.92 net.cpp:84] pool1 &- conv1
I:16.92 net.cpp:110] pool1 -& pool1
I:16.92 net.cpp:125] Top shape: 256 96 27 27 ()
I:16.92 net.cpp:151] pool1 needs backward computation.
I:16.92 net.cpp:74] Creating Layer norm1
I:16.92 net.cpp:84] norm1 &- pool1
I:16.92 net.cpp:110] norm1 -& norm1
I:16.92 net.cpp:125] Top shape: 256 96 27 27 ()
I:16.92 net.cpp:151] norm1 needs backward computation.
I:16.92 net.cpp:74] Creating Layer conv2
I:16.92 net.cpp:84] conv2 &- norm1
I:16.92 net.cpp:110] conv2 -& conv2
I:16.92 net.cpp:125] Top shape: 256 256 27 27 ()
I:16.92 net.cpp:151] conv2 needs backward computation.
I:16.92 net.cpp:74] Creating Layer relu2
I:16.92 net.cpp:84] relu2 &- conv2
I:16.92 net.cpp:98] relu2 -& conv2 (in-place)
I:16.92 net.cpp:125] Top shape: 256 256 27 27 ()
I:16.92 net.cpp:151] relu2 needs backward computation.
I:16.92 net.cpp:74] Creating Layer pool2
I:16.92 net.cpp:84] pool2 &- conv2
I:16.92 net.cpp:110] pool2 -& pool2
I:16.92 net.cpp:125] Top shape: 256 256 13 13 ()
I:16.92 net.cpp:151] pool2 needs backward computation.
I:16.92 net.cpp:74] Creating Layer norm2
I:16.92 net.cpp:84] norm2 &- pool2
I:16.92 net.cpp:110] norm2 -& norm2
I:16.92 net.cpp:125] Top shape: 256 256 13 13 ()
I:16.92 net.cpp:151] norm2 needs backward computation.
I:16.92 net.cpp:74] Creating Layer conv3
I:16.92 net.cpp:84] conv3 &- norm2
I:16.92 net.cpp:110] conv3 -& conv3
I:16.92 net.cpp:125] Top shape: 256 384 13 13 ()
I:16.92 net.cpp:151] conv3 needs backward computation.
I:16.92 net.cpp:74] Creating Layer relu3
I:16.92 net.cpp:84] relu3 &- conv3
I:16.92 net.cpp:98] relu3 -& conv3 (in-place)
I:16.92 net.cpp:125] Top shape: 256 384 13 13 ()
I:16.92 net.cpp:151] relu3 needs backward computation.
I:16.92 net.cpp:74] Creating Layer conv4
I:16.92 net.cpp:84] conv4 &- conv3
I:16.92 net.cpp:110] conv4 -& conv4
I:16.92 net.cpp:125] Top shape: 256 384 13 13 ()
I:16.92 net.cpp:151] conv4 needs backward computation.
I:16.92 net.cpp:74] Creating Layer relu4
I:16.92 net.cpp:84] relu4 &- conv4
I:16.92 net.cpp:98] relu4 -& conv4 (in-place)
I:16.92 net.cpp:125] Top shape: 256 384 13 13 ()
I:16.92 net.cpp:151] relu4 needs backward computation.
I:16.92 net.cpp:74] Creating Layer conv5
I:16.92 net.cpp:84] conv5 &- conv4
I:16.92 net.cpp:110] conv5 -& conv5
I:16.92 net.cpp:125] Top shape: 256 256 13 13 ()
I:16.92 net.cpp:151] conv5 needs backward computation.
I:16.92 net.cpp:74] Creating Layer relu5
I:16.92 net.cpp:84] relu5 &- conv5
I:16.92 net.cpp:98] relu5 -& conv5 (in-place)
I:16.92 net.cpp:125] Top shape: 256 256 13 13 ()
I:16.92 net.cpp:151] relu5 needs backward computation.
I:16.92 net.cpp:74] Creating Layer pool5
I:16.92 net.cpp:84] pool5 &- conv5
I:16.92 net.cpp:110] pool5 -& pool5
I:16.92 net.cpp:125] Top shape: 256 256 6 6 (2359296)
I:16.92 net.cpp:151] pool5 needs backward computation.
I:16.92 net.cpp:74] Creating Layer fc6
I:16.92 net.cpp:84] fc6 &- pool5
I:16.92 net.cpp:110] fc6 -& fc6
I:17.92 net.cpp:125] Top shape: 256
I:17.92 net.cpp:151] fc6 needs backward computation.
I:17.92 net.cpp:74] Creating Layer relu6
I:17.92 net.cpp:84] relu6 &- fc6
I:17.92 net.cpp:98] relu6 -& fc6 (in-place)
I:17.92 net.cpp:125] Top shape: 256
I:17.92 net.cpp:151] relu6 needs backward computation.
I:17.92 net.cpp:74] Creating Layer drop6
I:17.92 net.cpp:84] drop6 &- fc6
I:17.92 net.cpp:98] drop6 -& fc6 (in-place)
I:17.92 net.cpp:125] Top shape: 256
I:17.92 net.cpp:151] drop6 needs backward computation.
I:17.92 net.cpp:74] Creating Layer fc7
I:17.92 net.cpp:84] fc7 &- fc6
I:17.92 net.cpp:110] fc7 -& fc7
I:17.92 net.cpp:125] Top shape: 256
I:17.92 net.cpp:151] fc7 needs backward computation.
I:17.92 net.cpp:74] Creating Layer relu7
I:17.92 net.cpp:84] relu7 &- fc7
I:17.92 net.cpp:98] relu7 -& fc7 (in-place)
I:17.92 net.cpp:125] Top shape: 256
I:17.92 net.cpp:151] relu7 needs backward computation.
I:17.92 net.cpp:74] Creating Layer drop7
I:17.92 net.cpp:84] drop7 &- fc7
I:17.92 net.cpp:98] drop7 -& fc7 (in-place)
I:17.92 net.cpp:125] Top shape: 256
I:17.92 net.cpp:151] drop7 needs backward computation.
I:17.92 net.cpp:74] Creating Layer fc8
I:17.92 net.cpp:84] fc8 &- fc7
I:17.92 net.cpp:110] fc8 -& fc8
I:17.92 net.cpp:125] Top shape: 256 22 1 1 (5632)
I:17.92 net.cpp:151] fc8 needs backward computation.
I:17.92 net.cpp:74] Creating Layer loss
I:17.92 net.cpp:84] loss &- fc8
I:17.92 net.cpp:84] loss &- label
I:17.92 net.cpp:151] loss needs backward computation.
I:17.92 net.cpp:173] Collecting Learning Rate and Weight Decay.
I:17.92 net.cpp:166] Network initialization done.
I:17.92 net.cpp:167] Memory required for Data
17:15:07 UTC
CIFAR-10在caffe上进行训练与学习
使用数据库:CIFAR-10
60000张 32X32 彩色图像 10类,50000张训练,10000张测试
在终端运行以下指令:
cd $CAFFE_ROOT/data/cifar10
./get_cifar10.sh
cd $CAFFE_ROOT/examples/cifar10
./create_cifar10.sh
其中CAFFE_ROOT是caffe-master在你机子的地址
运行之后,将会在examples中出现数据库文件./cifar10-leveldb和数据库图像均值二进制文件./mean.binaryproto
该CNN由卷积层,POOLing层,非线性变换层,在顶端的局部对比归一化线性分类器组成。该模型的定义在CAFFE_ROOT/examples/cifar10 directory’s cifar10_quick_train.prototxt中,可以进行修改。其实后缀为prototxt很多都是用来修改配置的。
训练和测试
训练这个模型非常简单,当我们写好参数设置的文件cifar10_quick_solver.prototxt和定义的文件cifar10_quick_train.prototxt和cifar10_quick_test.prototxt后,运行train_quick.sh或者在终端输入下面的命令:
cd $CAFFE_ROOT/examples/cifar10
./train_quick.sh
即可,train_quick.sh是一个简单的脚本,会把执行的信息显示出来,培训的工具是train_net.bin,cifar10_quick_solver.prototxt作为参数。
然后出现类似以下的信息:这是搭建模型的相关信息
I:48.8298256 net.cpp:74] Creating Layer conv1
I:48.8298256 net.cpp:84] conv1 &- data
I:48.8298256 net.cpp:110] conv1 -& conv1
I:49.8298256 net.cpp:125] Top shape: 100 32 32 32 (3276800)
I:49.8298256 net.cpp:151] conv1 needs backward computation.
:49.8298256 net.cpp:166] Network initialization done.
I:49.8298256 net.cpp:167] Memory required for Data
I:49.8298256 solver.cpp:36] Solver scaffolding done.
I:49.8298256 solver.cpp:47] Solving CIFAR10_quick_train
之后,训练开始
I:12.8298256 solver.cpp:208] Iteration 100, lr = 0.001
I:12.8298256 solver.cpp:65] Iteration 100, loss = 1.73643
I:41.8298256 solver.cpp:87] Iteration 500, Testing net
I:47.8298256 solver.cpp:114] Test score #0: 0.5504
I:47.8298256 solver.cpp:114] Test score #1: 1.27805
其中每100次迭代次数显示一次训练时lr(learningrate),和loss(训练损失函数),每500次测试一次,输出score 0(准确率)和score 1(测试损失函数)
当5000次迭代之后,正确率约为75%,模型的参数存储在二进制protobuf格式在cifar10_quick_iter_5000
然后,这个模型就可以用来运行在新数据上了。
另外,更改cifar*solver.prototxt文件可以使用CPU训练,
# solver mode: CPU or GPU
solver_mode: CPU
可以看看CPU和GPU训练的差别。
主要资料来源:caffe官网教程
00:58:32 UTC
正对Caffe感兴趣, 谢谢楼主的帖子了
01:51:24 UTC
感谢楼主的帖子,让我受益匪浅。不过参照,Caffe的全称应该是Convolutional Architecture for Fast Feature Embedding.
02:38:53 UTC
好文,学习了。[caffe]的项目架构和源码解析 - 推酷
[caffe]的项目架构和源码解析
Caffe是一个基于c++/cuda语言的深度学习框架,开发者能够利用它自由的组成自己想要的网络。目前支持卷积神经网络和全连接神经网络(人工神经网络)。Linux上,c++可以通过命令行来操作接口,matlab、python有专门的接口,运算支持gpu也支持cpu,目前版本能够支持多gpu,但是分布式多机版本仍在开发中。大量的研究者都在采用caffe的架构,并且也得到了很多有效的成果。2013年9月-12月,贾扬清在伯克利大学准备毕业论文的时候开发了caffe最初的版本,后期有其他的牛人加入之后,近两年的不断优化,到现在成了最受欢迎的深度学习框架。近期,caffe2也开源了,但是仍旧在开发。本文主要主要基于源代码的层面来对caffe进行解读,并且给出了几个自己在测试的过程中感兴趣的东西。
1.如何调试
为了能够调试,首先要在makefile的配置文件中将DEBUG选项设置为1,这步谨慎选择,debug版本会在打印输出的时候输出大量的每个阶段耗时,也可直接从整个项目的caffe.cpp入手来查看源文件。编译好可调试的版本之后,执行下面的指令可以启动调试。
gdb --args ./build/tools/caffe train –solver=examples/cifar10/cifar10_full_solver.prototxt
调试过程中需要注意的一个问题是,源码中使用了函数指针,执行下一步很容易就跳过了,所以要在合适的时机使用s来进入函数。
2.第三方库
gflags是Google出的一个能够简化命令行参数处理的工具,在c++代码中定义实际意义,在命令行中将参数传进去。例如下面的例子中,c++的代码中声明这样的内容,DEFINE_string是一个string类型,括号内的solver就是一个flag,这个flag从命令行中读取的参数就会解析成string,存在FLAGS_solver中,使用时当成正常的string使用即可。在命令行调用时(参见调试部分举出的例子),用-solver=xxxxx,将实际的值给传递进去。这里的string可以替换成int32/int64/bool等。
DEFINE_string(solver, &&, &The solver definition protocol buffer text file.&);
需要注意的时,这个定义过程只能在一个文件中定义一次,其他文件要是想用的话可以有两种选择,一种是直接在需要的文件中declare,一种方式在一个头文件中declare,其他文件要用就直接include。声明方式如下:
DECLARE_bool(solver);
假如需要设置bool变量为false,一个简便的方法是在变量前面加上no,即变成-nosolver。此外,–会导致解析停止,例如下面的式子中,f1是flag,它的值为1,但是f2并不是2。
foo -f1 1 -- -f2 2
Google Protocol Buffer(简称Protobuf)是Google公司内部的混合语言数据标准,目前已经正在使用的有超过48,162种报文格式定义和超过12,183个.proto文件。它是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化、序列化结构数据,自己定义一次数据如何结构化,目前提供了c++、java、python三种语言的API。相对于xml优点在于简单、体积小、读取处理时间快、更少产生歧义、更容易产生易于编程的类。
就caffe而言,这个工具的用处体现在生成caffe所需的 参数类 ,这些类能从以.prototxt结尾的文件中解析参数,然后对应生成Net、Layer的参数。自己定义序列化文件a.proto,文件内容如图1,以关键词message来定义一个类,本图中它是卷积层的参数类,这个类的成员类型有bool和uint32等,也可用自己定义的类型。等号后面的数字是一个唯一的编号tag,来区分这些不同参数,在官方文档中这些称为field。可以在一个.proto中定义多个message,注释风格与c/c++一致。定义好的proto进行编译后生成.h和.cc对应c++的头文件和源文件。
图1 自定义proto
图2是编译后自动生成的文件,可以看到生成了ConvolutionalParameter类。
图2 自动生成的c++类
这个工具也是谷歌出品,用来打印初始化、运行时的信息,记录意外中断等。使用先要初始化google的logging库。一般在caffe中常见的LOG(INFO)…和CHECK(XXX)…都是它执行的。相关的内容可以参考下面的图片,图3是标出颜色的是代码中用到了的打印,图4是对应打印到屏幕上的信息。
图3 c++代码
图4 打印信息
lmdb是一个读取速度快、轻量级的数据库,支持多线程、多进程并发,数据由key-value对存储。caffe还提供leveldb的接口,本文只讨论python实现的lmdb。在这个数据库中存放的是序列化生成的字符串。caffe提供脚本文件先生成lmdb格式的数据,这个脚本文件会生成一个文件夹,文件夹下包括两个文件,一个数据文件,一个lock文件。然后调用训练网络的DataLayer层来读取lmdb格式的数据。图5是定义ldmb数据库类型,图6是将数据序列化再存入数据库中。
图5 定义db
图6 存入db
3.caffe基本结构
这是caffe的数据存储类blob,它实现了关于一个变量的所有相关信息和相关操作。存储数据的方式可以看成是一个N维的c数组,存储空间连续。例如存储图片是4维(num, channel, height, width),变量(n,k,h,w)在数组中存储位置为((n K+k) H+h)*W+w。相应的四维参数保存为(out_channel, in_channel, filter_size, filter_size)。blob有以下三个特征:
两块数据,一个是原始data,一个是求导值diff
两种内存分配方式,一种是分配在cpu上,一种是分配在gpu上,通过前缀cpu、gpu来区分
两种访问方式,一种是不能改变数据,一种能改变数据
其中让人眼前一亮的是data和diff的设计,其实在卷积网络中,很多情况下一个变量不仅有它自身的值,另外还有cost function对它的导数,采用过多的变量来保存这两个信息还不如将它们放在一起直观,下图是源码blob.hpp中的定义。
图7 定义blob
caffe根据不同的功能将它们包装成不同的Layer,例如卷积、pooling、非线性变换、数据层等等。具体有多少种layer及其内容参考官方文档即可,本文主要讨论它的实现,它的实现分为三个部分,也可参考演示图8:
setup,初始化每一层,和它对应的连接关系
forward,由bottom求top
backward,由top的梯度求bottom的梯度,有参数的求参数梯度
图8 caffe的layer实现方式
而前向传播、后向传播的函数也分别有两种实现方式,一种基于gpu一种基于cpu。Forward函数,参数分别是两个存放blob指针的vector,分别是bottom、top。通过指针数组的方式能够实现多个输入多个输出。值得一提的是,caffe的卷积部分采用了将数据进行变换,变成矩阵之后再用矩阵乘法来实现卷积,cudnn也是采用这样的方式,经过我的实验,确实这种方式比直接实现cuda kernel要快一些。caffe大部分底层实现都是用blas或者cublas处理的。
Net它将不同的层正确的连接起来,是层和它们之间连接的集合。通过Net::Init()来初始化模型,构造blobs和layers,调用layers的setup函数。Net的Forward函数内部调用了ForwardPrefilled,并且调用了ForwardFromTo,它从给定的层数id(start)到end来调用Layer对象的Forward函数。
Solver是控制网络的关键所在,它的具体功能包括解析传递的prototxt、执行train、调用网络前向传播计算输出和loss、后向传播计算梯度、根据不同优化方式更新参数(可能不止有learning rate这种参数,而是由alpha、beta构成的更新方式)等。在解析.prototxt时,首先初始化NetParameter对象,用于放置全部的网络参数, 然后在初始化训练网络的时候,通过net变量给出的proto文件地址,来解析并获取网络的层次结构参数。其中的函数solve会根据命令行传递进来的参数来解析并恢复之前保存好的网络文件和权重等,恢复上次执行的iteration次数、loss等。当网络参数配置好,需要恢复的文件处理完成就调用net.cpp的Forward函数开始执行网络。Forward会返回这一次迭代的loss,然后打印出来。接下来会调用ApplyUpdata函数,它会根据不同的策略来改变当前权重的学习率大小,再更新权重。此外,solver还提供保存快照的功能。
4.运行实例
假如是自己的图片数据,可以按照如下的方法来进行分类。我全部采用的c++,改源代码比较方便。
将图片整理成train和test两个文件夹,并将图片的名称和label保存到一个txt中
将数据变成lmdb格式,采用的是convert_imagenet这个工具
生成均值处理后的图片,采用compute_image_mean这个工具
修改模型并执行train
此外,我还测试过一维数据,并且修改了convert_imagenet.cpp源码,将数据读入lmdb,大致代码如下:
datum.set_channels(num_channels);
datum.set_height(num_height);
datum.set_width(num_width);
datum.clear_data();
datum.set_encoded(false);
datum.set_data(lines[line_id].first);
datum.set_label(lines[line_id].second);
通过改这个代码,可以将一维数据读入网络,进行处理。此外,在执行这个一维数据的过程中,也出了一个错,报错信息”Too big key/data, key is empty, or wrong DUPFIXED size”,这个问题是因为lmdb是保存的key-value对,而lmdb对key的长度进行了限制,长度不能超过512,但是我在传递的时候key的值给多了,因此得到了解决。
通过阅读源码可以看到,caffe作为一个架构,层次、思路、需要解决的问题都非常清晰,它的高效体现在很多方面,不仅采用了读取快速的lmdb,而且计算部分基本上都是用很高效的blas库完成的。而它的数据、层次、网络的构成和执行是分开控制的,这点就提供了比较大的灵活性,唯一的遗憾就是安装比较繁琐,总是会出现某个依赖包没装好的情况。总的来说,caffe在科研领域使用的非常广泛,大量的研究都是基于caffe预训练好的imagenet的网络而得到了很好的进展,作者这种分享的精神值得肯定。
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致

我要回帖

更多关于 caffe 卷积实现 的文章

 

随机推荐