感谢支持
我们一直在努力

Caffe 深度学习入门教程

安装配置Ubuntu14.04+CUDA7.5+Caffe+cuDNN

一、版本

Linux系统:Ubuntu 14.04 (64位)

显卡:Nvidia K20c

cuda: cuda_7.5.18_linux.run

cudnn: cudnn-7.0-linux-x64-v4.0-rc

二、下载

Ubuntu 14.04下载地址:http://www.ubuntu.com/download/desktop (64bit)

cuda7.5下载地址:https://developer.nvidia.com/cuda-downloads/  ,下载对应的操作系统和版本cuda_7.5.18_linux.run,放到~根目录下

cudnn下载址:https://developer.nvidia.com/cudnn, 需要注册,并通过审核才能下载,下载相应文件cudnn-7.0-linux-x64-v4.0-rc.tgz, 放到~根目录下.审核一般一至两天,等不及的同学可到Linux公社1号FTP服务器下载

——————————————分割线——————————————

FTP地址:ftp://ftp1.linuxidc.com

用户名:ftp1.linuxidc.com

密码:www.linuxidc.com

在 2016年LinuxIDC.com\11月\Caffe 深度学习入门教程\

下载方法见 http://www.linuxidc.com/Linux/2013-10/91140.htm

——————————————分割线—————————————— 

三、开始安装

 1、安装ubuntu,此文不关注。

 2、禁用nouveau驱动

    按Ctrl+Alt+F1进入命令提示符,新建一个黑名单文件

# sudo vi /etc/modprobe.d/blacklist-nouveau.conf

输入

blacklist nouveau
options nouveau modset=0

保存退出(:wq)

然后执行

# sudo update-initramfs -u

执行 lspci | grep nouveau查看是否有内容

# lspci | grep nouveau

如果没有内容 ,说明禁用成功,如果有内容,就重启一下再查看

# sudo reboot

重启后,进入登录界面的时候,不要登录进入桌面,直接按Ctrl+Alt+F1进入命令提示符。

3、安装cuda 7.5

先安装一些常用的软件,注意整个过程需要联网

# sudo service lightdm stop
# sudo apt-get install g++
# sudo apt-get install git
# sudo apt-get install freeglut3-dev

接下来进入~根据目录安装cuda 7.5

# cd
# sudo sh cude_7.5.18_linux.run

安装的时候,要让你先看一堆文字(EULA),我们直接不停的按空格键到100%,然后输入一堆accept,yes,yes或回车进行安装。

安装完成后,重启,然后用ls查看一下,是否生成了四个左右以nvidia开头的文件夹

# ls /dev/nvidia*

如果有,说明安装成功了,如果没有,可能不成功,需要卸载重装。卸载命令如下:

# sudo /usr/local/cuda-7.5/bin/uninstall_cuda_7.5.pl
# sudo /usr/bin/nvidia-uninstall

如果你还不放心是否安装成功,请参考其它教程,编译Samples进行测试。

最后,配置环境变量,我们直接放在系统配置文件profile里面,先打开profile文件

# sudo vi /etc/profile

在最后面加入两行代码,如果你还不会用vi进行编辑,请百度

export PATH=/usr/local/cuda-7.5/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda-7.5/lib64:$LD_LIBRARY_PATH

保存退出,至此cuda 7.5安装完毕。

4、安装caffe

   先下载caffe

# sudo git clone https://github.com/BVLC/caffe.git

然后安装一堆第三方库

# sudo apt-get install libatlas-base-dev
# sudo apt-get install libprotobuf-dev
# sudo apt-get install libleveldb-dev
# sudo apt-get install libsnappy-dev
# sudo apt-get install libopencv-dev
# sudo apt-get install libboost-all-dev
# sudo apt-get install libhdf5-serial-dev
# sudo apt-get install libgflags-dev
# sudo apt-get install libgoogle-glog-dev
# sudo apt-get install liblmdb-dev
# sudo apt-get install protobuf-compiler

接着,安装opencv

# cd caffe
# sudo git clone https://github.com/jayrambhia/Install-OpenCV
# cd Install-OpenCV/Ubuntu
# sudo sh dependencies.sh
# cd 2.4
# sudo sh opencv2_4_10.sh

接下来,编译caffe

# cd ~/caffe
# sudo cp Makefile.config.example Makefile.config
# make all

至此,caffe安装完成。

5、配置运行环境

caffe运行时需要调用cuda的库,我们在/etc/ld.so.conf.d目录下新建一个cafe.conf文件,将所需要用的库的目录写入

# sudo vi /etc/ld.so.conf.d/caffe.conf

添加内容:

/usr/local/cuda/lib64

保存退出(:wq)

更新配置

# sudo ldconfig

6、测试caffe

  下载mnist数据

# cd ~/caffe
# sudo sh data/mnist/get_mnist.sh
# sudo sh examples/mnist/create_mnist.sh

运行时,如果你有GPU,则不需要修改配置文件,如果没有gpu,则需要修改配置文件lenet_solver.prototxt

# sudo vi examples/mnist/lenet_solver.prototxt

将最后一行的solver_mode:GPU改为solver_mode:CPU

配置好后,就可以运行了

# sudo sh examples/mnist/train_lenet.sh

注意,运行caffe程序时,必须在caffe的根目录下,不然会出错

7、安装cudnn进行加速

假设事先已经下载好cudnn-7.0-linux-x64-v4.0-rc.tgz放到~根目录

# cd
# sudo tar xvf cudnn-7.0-linux-x64-v4.0-rc.tgz
# cd cuda/include
# sudo cp *.h /usr/local/include/
# cd ../lib64
# sudo cp lib* /usr/local/lib/
# cd /usr/local/lib
# sudo chmod +r libcudnn.so.4.0.4
# sudo ln -sf libcudnn.so.4.0.4 libcudnn.so.4
# sudo ln -sf libcudnn.so.4 libcudnn.so
# sudo ldconfig

将caffek根目录下 Makefile.config中 USE_CUDNN 行的注释去除,然后重新进行编译。

# cd ~/caffe
# sudo vi Makefile.config

 将USE_CUDNN 行的注释符号#去除,即 USE_CUDNN := 1

然后重新编译

# sudo make clean
# sudo make all

到此,全部安装完成。

如果没有gpu,则不能用cudnn。

更多详情见请继续阅读下一页的精彩内容: http://www.linuxidc.com/Linux/2016-11/136774p2.htm

数据层及参数

要运行caffe,需要先创建一个模型(model),如比较常用的Lenet,Alex等, 而一个模型由多个屋(layer)构成,每一屋又由许多参数组成。所有的参数都定义在caffe.proto这个文件中。要熟练使用caffe,最重要的就是学会配置文件(prototxt)的编写。

层有很多种类型,比如Data,Convolution,Pooling等,层之间的数据流动是以Blobs的方式进行。

今天我们就先介绍一下数据层.

数据层是每个模型的最底层,是模型的入口,不仅提供数据的输入,也提供数据从Blobs转换成别的格式进行保存输出。通常数据的预处理(如减去均值, 放大缩小, 裁剪和镜像等),也在这一层设置参数实现。

数据来源可以来自高效的数据库(如LevelDB和LMDB),也可以直接来自于内存。如果不是很注重效率的话,数据也可来自磁盘的hdf5文件和图片格式文件。

所有的数据层的都具有的公用参数:先看示例

layer {
  name: "cifar"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
  transform_param {
    mean_file: "examples/cifar10/mean.binaryproto"
  }
  data_param {
    source: "examples/cifar10/cifar10_train_lmdb"
    batch_size: 100
    backend: LMDB
  }
} 

name: 表示该层的名称,可随意取

type: 层类型,如果是Data,表示数据来源于LevelDB或LMDB。根据数据的来源不同,数据层的类型也不同(后面会详细阐述)。一般在练习的时候,我们都是采 用的LevelDB或LMDB数据,因此层类型设置为Data。

top或bottom: 每一层用bottom来输入数据,用top来输出数据。如果只有top没有bottom,则此层只有输出,没有输入。反之亦然。如果有多个 top或多个bottom,表示有多个blobs数据的输入和输出。

data 与 label: 在数据层中,至少有一个命名为data的top。如果有第二个top,一般命名为label。 这种(data,label)配对是分类模型所必需的。

include: 一般训练的时候和测试的时候,模型的层是不一样的。该层(layer)是属于训练阶段的层,还是属于测试阶段的层,需要用include来指定。如果没有include参数,则表示该层既在训练模型中,又在测试模型中。

Transformations: 数据的预处理,可以将数据变换到定义的范围内。如设置scale为0.00390625,实际上就是1/255, 即将输入数据由0-255归一化到0-1之间

其它的数据预处理也在这个地方设置:

transform_param {
    scale: 0.00390625
    mean_file_size: "examples/cifar10/mean.binaryproto"
    # 用一个配置文件来进行均值操作
    mirror: 1  # 1表示开启镜像,0表示关闭,也可用ture和false来表示
    # 剪裁一个 227*227的图块,在训练阶段随机剪裁,在测试阶段从中间裁剪
    crop_size: 227
  }

后面的data_param部分,就是根据数据的来源不同,来进行不同的设置。

1、数据来自于数据库(如LevelDB和LMDB)

  层类型(layer type):Data

必须设置的参数:

  source: 包含数据库的目录名称,如examples/mnist/mnist_train_lmdb

  batch_size: 每次处理的数据个数,如64

可选的参数:

  rand_skip: 在开始的时候,路过某个数据的输入。通常对异步的SGD很有用。

  backend: 选择是采用LevelDB还是LMDB, 默认是LevelDB.

示例:

layer {
  name: "mnist"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
  transform_param {
    scale: 0.00390625
  }
  data_param {
    source: "examples/mnist/mnist_train_lmdb"
    batch_size: 64
    backend: LMDB
  }
}

2、数据来自于内存

层类型:MemoryData

必须设置的参数:

 batch_size:每一次处理的数据个数,比如2

 channels:通道数

  height:高度

   width: 宽度

示例:

layer {
  top: "data"
  top: "label"
  name: "memory_data"
  type: "MemoryData"
  memory_data_param{
    batch_size: 2
    height: 100
    width: 100
    channels: 1
  }
  transform_param {
    scale: 0.0078125
    mean_file: "mean.proto"
    mirror: false
  }
} 

3、数据来自于HDF5

层类型:HDF5Data

必须设置的参数:

source: 读取的文件名称

batch_size: 每一次处理的数据个数

示例:

layer {
  name: "data"
  type: "HDF5Data"
  top: "data"
  top: "label"
  hdf5_data_param {
    source: "examples/hdf5_classification/data/train.txt"
    batch_size: 10
  }
}

4、数据来自于图片

层类型:ImageData

必须设置的参数:

  source: 一个文本文件的名字,每一行给定一个图片文件的名称和标签(label)

  batch_size: 每一次处理的数据个数,即图片数

可选参数:

  rand_skip: 在开始的时候,路过某个数据的输入。通常对异步的SGD很有用。

  shuffle: 随机打乱顺序,默认值为false

  new_height,new_width: 如果设置,则将图片进行resize

 示例:

复制代码

layer {
  name: "data"
  type: "ImageData"
  top: "data"
  top: "label"
  transform_param {
    mirror: false
    crop_size: 227
    mean_file: "data/ilsvrc12/imagenet_mean.binaryproto"
  }
  image_data_param {
    source: "examples/_temp/file_list.txt"
    batch_size: 50
    new_height: 256
    new_width: 256
  }
}

复制代码

5、数据来源于Windows

层类型:WindowData

必须设置的参数:

  source: 一个文本文件的名字

  batch_size: 每一次处理的数据个数,即图片数

示例:

layer {
  name: "data"
  type: "WindowData"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
  transform_param {
    mirror: true
    crop_size: 227
    mean_file: "data/ilsvrc12/imagenet_mean.binaryproto"
  }
  window_data_param {
    source: "examples/finetune_pascal_detection/window_file_2007_trainval.txt"
    batch_size: 128
    fg_threshold: 0.5
    bg_threshold: 0.5
    fg_fraction: 0.25
    context_pad: 16
    crop_mode: "warp"
  }
}

视觉层(Vision Layers)及参数

所有的层都具有的参数,如name, type, bottom, top和transform_param请参看我的前一篇文章:Caffe 深度学习入门教程(2):数据层及参数

本文只讲解视觉层(Vision Layers)的参数,视觉层包括Convolution, Pooling, Local Response Normalization (LRN), im2col等层。

1、Convolution层:

就是卷积层,是卷积神经网络(CNN)的核心层。

层类型:Convolution

  lr_mult: 学习率的系数,最终的学习率是这个数乘以solver.prototxt配置文件中的base_lr。如果有两个lr_mult, 则第一个表示权值的学习率,第二个表示偏置项的学习率。一般偏置项的学习率是权值学习率的两倍。

在后面的convolution_param中,我们可以设定卷积层的特有参数。

必须设置的参数:

    num_output: 卷积核(filter)的个数

    kernel_size: 卷积核的大小。如果卷积核的长和宽不等,需要用kernel_h和kernel_w分别设定

其它参数:

     stride: 卷积核的步长,默认为1。也可以用stride_h和stride_w来设置。

     pad: 扩充边缘,默认为0,不扩充。 扩充的时候是左右、上下对称的,比如卷积核的大小为5*5,那么pad设置为2,则四个边缘都扩充2个像素,即宽度和高度都扩充了4个像素,这样卷积运算之后的特征图就不会变小。也可以通过pad_h和pad_w来分别设定。

     weight_filler: 权值初始化。 默认为“constant”,值全为0,很多时候我们用”xavier”算法来进行初始化,也可以设置为”gaussian”
      bias_filler: 偏置项的初始化。一般设置为”constant”,值全为0。
      bias_term: 是否开启偏置项,默认为true, 开启
      group: 分组,默认为1组。如果大于1,我们限制卷积的连接操作在一个子集内。如果我们根据图像的通道来分组,那么第i个输出分组只能与第i个输入分组进行连接。
 
输入:n*c0*w0*h0
输出:n*c1*w1*h1
其中,c1就是参数中的num_output,生成的特征图个数
 w1=(w0+2*pad-kernel_size)/stride+1;
 h1=(h0+2*pad-kernel_size)/stride+1;
如果设置stride为1,前后两次卷积部分存在重叠。如果设置pad=(kernel_size-1)/2,则运算后,宽度和高度不变。
示例:

layer {
  name: "conv1"
  type: "Convolution"
  bottom: "data"
  top: "conv1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 20
    kernel_size: 5
    stride: 1
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}

  2、Pooling层
也叫池化层,为了减少运算量和数据维度而设置的一种层。
层类型:Pooling
必须设置的参数:
     kernel_size: 池化的核大小。也可以用kernel_h和kernel_w分别设定。
其它参数:
   pool: 池化方法,默认为MAX。目前可用的方法有MAX, AVE, 或STOCHASTIC
  pad: 和卷积层的pad的一样,进行边缘扩充。默认为0
  stride: 池化的步长,默认为1。一般我们设置为2,即不重叠。也可以用stride_h和stride_w来设置。
 示例:

layer {
  name: "pool1"
  type: "Pooling"
  bottom: "conv1"
  top: "pool1"
  pooling_param {
    pool: MAX
    kernel_size: 3
    stride: 2
  }
}

pooling层的运算方法基本是和卷积层是一样的。

输入:n*c*w0*h0
输出:n*c*w1*h1
和卷积层的区别就是其中的c保持不变
 w1=(w0+2*pad-kernel_size)/stride+1;
 h1=(h0+2*pad-kernel_size)/stride+1;
如果设置stride为2,前后两次卷积部分不重叠。100*100的特征图池化后,变成50*50.
 
3、Local Response Normalization (LRN)层
此层是对一个输入的局部区域进行归一化,达到“侧抑制”的效果。可去搜索AlexNet或GoogLenet,里面就用到了这个功能

 层类型:LRN
参数:全部为可选,没有必须
  local_size: 默认为5。如果是跨通道LRN,则表示求和的通道数;如果是在通道内LRN,则表示求和的正方形区域长度。
  alpha: 默认为1,归一化公式中的参数。
  beta: 默认为5,归一化公式中的参数。
  norm_region: 默认为ACROSS_CHANNELS。有两个选择,ACROSS_CHANNELS表示在相邻的通道间求和归一化。WITHIN_CHANNEL表示在一个通道内部特定的区域内进行求和归一化。与前面的local_size参数对应。
 
归一化公式:对于每一个输入, 去除以,得到归一化后的输出
 
示例:

layers {
  name: "norm1"
  type: LRN
  bottom: "pool1"
  top: "norm1"
  lrn_param {
    local_size: 5
    alpha: 0.0001
    beta: 0.75
  }
}

4、im2col层

如果对matlab比较熟悉的话,就应该知道im2col是什么意思。它先将一个大矩阵,重叠地划分为多个子矩阵,对每个子矩阵序列化成向量,最后得到另外一个矩阵。

 

看一看图就知道了:

在caffe中,卷积运算就是先对数据进行im2col操作,再进行内积运算(inner product)。这样做,比原始的卷积操作速度更快。

看看两种卷积操作的异同:

激活层(Activiation Layers)及参数

在激活层中,对输入数据进行激活操作(实际上就是一种函数变换),是逐元素进行运算的。从bottom得到一个blob数据输入,运算后,从top输入一个blob数据。在运算过程中,没有改变数据的大小,即输入和输出的数据大小是相等的。

输入:n*c*h*w

输出:n*c*h*w

常用的激活函数有sigmoid, tanh,relu等,下面分别介绍。

1、Sigmoid

对每个输入数据,利用sigmoid函数执行操作。这种层设置比较简单,没有额外的参数。

层类型:Sigmoid

示例:

layer {
  name: "encode1neuron"
  bottom: "encode1"
  top: "encode1neuron"
  type: "Sigmoid"
}

2、ReLU / Rectified-Linear and Leaky-ReLU

ReLU是目前使用最多的激活函数,主要因为其收敛更快,并且能保持同样效果。

标准的ReLU函数为max(x, 0),当x>0时,输出x; 当x<=0时,输出0

f(x)=max(x,0)

层类型:ReLU

可选参数:

  negative_slope:默认为0. 对标准的ReLU函数进行变化,如果设置了这个值,那么数据为负数时,就不再设置为0,而是用原始数据乘以negative_slope

layer {
  name: "relu1"
  type: "ReLU"
  bottom: "pool1"
  top: "pool1"
}

RELU层支持in-place计算,这意味着bottom的输出和输入相同以避免内存的消耗。

3、TanH / Hyperbolic Tangent

利用双曲正切函数对数据进行变换。

层类型:TanH

layer {
  name: "layer"
  bottom: "in"
  top: "out"
  type: "TanH"
}

4、Absolute Value

求每个输入数据的绝对值。

f(x)=Abs(x)

层类型:AbsVal

layer {
  name: "layer"
  bottom: "in"
  top: "out"
  type: "AbsVal"
}

5、Power

对每个输入数据进行幂运算

f(x)= (shift + scale * x) ^ power

层类型:Power

可选参数:

  power: 默认为1

  scale: 默认为1

  shift: 默认为0

layer {
  name: "layer"
  bottom: "in"
  top: "out"
  type: "Power"
  power_param {
    power: 2
    scale: 1
    shift: 0
  }
}

6、BNLL

binomial normal log likelihood的简称

f(x)=log(1 + exp(x))

层类型:BNLL

layer {
  name: "layer"
  bottom: "in"
  top: "out"
  type: “BNLL”
}

其它常用层及参数

本文讲解一些其它的常用层,包括:softmax_loss层,Inner Product层,accuracy层,reshape层和dropout层及其它们的参数配置。

1、softmax-loss

softmax-loss层和softmax层计算大致是相同的。softmax是一个分类器,计算的是类别的概率(Likelihood),是Logistic Regression 的一种推广。Logistic Regression 只能用于二分类,而softmax可以用于多分类。

softmax与softmax-loss的区别:

softmax计算公式:

而softmax-loss计算公式:

关于两者的区别更加具体的介绍,可参考:softmax vs. softmax-loss

用户可能最终目的就是得到各个类别的概率似然值,这个时候就只需要一个 Softmax层,而不一定要进行softmax-Loss 操作;或者是用户有通过其他什么方式已经得到了某种概率似然值,然后要做最大似然估计,此时则只需要后面的 softmax-Loss 而不需要前面的 Softmax 操作。因此提供两个不同的 Layer 结构比只提供一个合在一起的 Softmax-Loss Layer 要灵活许多。

不管是softmax layer还是softmax-loss layer,都是没有参数的,只是层类型不同而也

softmax-loss layer:输出loss值

layer {
  name: "loss"
  type: "SoftmaxWithLoss"
  bottom: "ip1"
  bottom: "label"
  top: "loss"
}

softmax layer: 输出似然值

layers {
  bottom: "cls3_fc"
  top: "prob"
  name: "prob"
  type: “Softmax"
}

2、Inner Product

全连接层,把输入当作成一个向量,输出也是一个简单向量(把输入数据blobs的width和height全变为1)。

输入: n*c0*h*w

输出: n*c1*1*1

全连接层实际上也是一种卷积层,只是它的卷积核大小和原数据大小一致。因此它的参数基本和卷积层的参数一样。

层类型:InnerProduct

lr_mult: 学习率的系数,最终的学习率是这个数乘以solver.prototxt配置文件中的base_lr。如果有两个lr_mult, 则第一个表示权值的学习率,第二个表示偏置项的学习率。一般偏置项的学习率是权值学习率的两倍。

必须设置的参数:

    num_output: 过滤器(filfter)的个数

其它参数:

     weight_filler: 权值初始化。 默认为“constant”,值全为0,很多时候我们用”xavier”算法来进行初始化,也可以设置为”gaussian”
      bias_filler: 偏置项的初始化。一般设置为”constant”,值全为0。
      bias_term: 是否开启偏置项,默认为true, 开启

layer {
  name: "ip1"
  type: "InnerProduct"
  bottom: "pool2"
  top: "ip1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 500
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}

3、accuracy

输出分类(预测)精确度,只有test阶段才有,因此需要加入include参数。

层类型:Accuracy

layer {
  name: "accuracy"
  type: "Accuracy"
  bottom: "ip2"
  bottom: "label"
  top: "accuracy"
  include {
    phase: TEST
  }
}

4、reshape

在不改变数据的情况下,改变输入的维度。

层类型:Reshape

先来看例子

 layer {
    name: "reshape"
    type: "Reshape"
    bottom: "input"
    top: "output"
    reshape_param {
      shape {
        dim: 0  # copy the dimension from below
        dim: 2
        dim: 3
        dim: -1 # infer it from the other dimensions
      }
    }
  }

有一个可选的参数组shape, 用于指定blob数据的各维的值(blob是一个四维的数据:n*c*w*h)。

dim:0  表示维度不变,即输入和输出是相同的维度。

dim:2 或 dim:3 将原来的维度变成2或3

dim:-1 表示由系统自动计算维度。数据的总量不变,系统会根据blob数据的其它三维来自动计算当前维的维度值 。

假设原数据为:64*3*28*28, 表示64张3通道的28*28的彩色图片

经过reshape变换:

   reshape_param {
      shape {
        dim: 0 
        dim: 0
        dim: 14
        dim: -1 
      }
    }

输出数据为:64*3*14*56

5、Dropout

Dropout是一个防止过拟合的trick。可以随机让网络某些隐含层节点的权重不工作。

先看例子:

layer {
  name:
drop7
  type:
Dropout
  bottom:
fc7-conv
  top:
fc7-conv
  dropout_param {
    dropout_ratio:
0.5
  }
}

只需要设置一个dropout_ratio就可以了。

还有其它更多的层,但用的地方不多,就不一一介绍了。

随着深度学习的深入,各种各样的新模型会不断的出现,因此对应的各种新类型的层也在不断的出现。这些新出现的层,我们只有在等caffe更新到新版本后,再去慢慢地摸索了。

Blob,Layer and Net以及对应配置文件的编写

深度网络(net)是一个组合模型,它由许多相互连接的层(layers)组合而成。Caffe就是组建深度网络的这样一种工具,它按照一定的策略,一层一层的搭建出自己的模型。它将所有的信息数据定义为blobs,从而进行便利的操作和通讯。Blob是caffe框架中一种标准的数组,一种统一的内存接口,它详细描述了信息是如何存储的,以及如何在层之间通讯的。

1、blob

Blobs封装了运行时的数据信息,提供了CPU和GPU的同步。从数学上来说, Blob就是一个N维数组。它是caffe中的数据操作基本单位,就像matlab中以矩阵为基本操作对象一样。只是矩阵是二维的,而Blob是N维的。N可以是2,3,4等等。对于图片数据来说,Blob可以表示为(N*C*H*W)这样一个4D数组。其中N表示图片的数量,C表示图片的通道数,H和W分别表示图片的高度和宽度。当然,除了图片数据,Blob也可以用于非图片数据。比如传统的多层感知机,就是比较简单的全连接网络,用2D的Blob,调用innerProduct层来计算就可以了。

在模型中设定的参数,也是用Blob来表示和运算。它的维度会根据参数的类型不同而不同。比如:在一个卷积层中,输入一张3通道图片,有96个卷积核,每个核大小为11*11,因此这个Blob是96*3*11*11. 而在一个全连接层中,假设输入1024通道图片,输出1000个数据,则Blob为1000*1024

2、layer

层是网络模型的组成要素和计算的基本单位。层的类型比较多,如Data,Convolution,Pooling,ReLU,Softmax-loss,Accuracy等,一个层的定义大至如下图:

从bottom进行数据的输入 ,计算后,通过top进行输出。图中的黄色多边形表示输入输出的数据,蓝色矩形表示层。

每一种类型的层都定义了三种关键的计算:setup,forward and backword

setup: 层的建立和初始化,以及在整个模型中的连接初始化。

forward: 从bottom得到输入数据,进行计算,并将计算结果送到top,进行输出。

backward: 从层的输出端top得到数据的梯度,计算当前层的梯度,并将计算结果送到bottom,向前传递。

3、Net

就像搭积木一样,一个net由多个layer组合而成。

现给出 一个简单的2层神经网络的模型定义( 加上loss 层就变成三层了),先给出这个网络的拓扑。

第一层:name为mnist, type为Data,没有输入(bottom),只有两个输出(top),一个为data,一个为label
第二层:name为ip,type为InnerProduct, 输入数据data, 输出数据ip
第三层:name为loss, type为SoftmaxWithLoss,有两个输入,一个为ip,一个为label,有一个输出loss,没有画出来。
对应的配置文件prototxt就可以这样写:

name: "LogReg"
layer {
  name: "mnist"
  type: "Data"
  top: "data"
  top: "label"
  data_param {
    source: "input_leveldb"
    batch_size: 64
  }
}
layer {
  name: "ip"
  type: "InnerProduct"
  bottom: "data"
  top: "ip"
  inner_product_param {
    num_output: 2
  }
}
layer {
  name: "loss"
  type: "SoftmaxWithLoss"
  bottom: "ip"
  bottom: "label"
  top: "loss"
}

第一行将这个模型取名为LogReg, 然后是三个layer的定义,参数都比较简单,只列出必须的参数。具体的参数定义可参见本系列的前几篇文章。

solver及其配置

solver算是caffe的核心的核心,它协调着整个模型的运作。caffe程序运行必带的一个参数就是solver配置文件。运行代码一般为

# caffe train --solver=*_slover.prototxt

在Deep Learning中,往往loss function是非凸的,没有解析解,我们需要通过优化方法来求解。solver的主要作用就是交替调用前向(forward)算法和后向(backward)算法来更新参数,从而最小化loss,实际上就是一种迭代的优化算法。

到目前的版本,caffe提供了六种优化算法来求解最优参数,在solver配置文件中,通过设置type类型来选择。

  • Stochastic Gradient Descent (type: "SGD"),
  • AdaDelta (type: "AdaDelta"),
  • Adaptive Gradient (type: "AdaGrad"),
  • Adam (type: "Adam"),
  • Nesterov’s Accelerated Gradient (type: "Nesterov") and
  • RMSprop (type: "RMSProp")

 具体的每种方法的介绍,请看本系列的下一篇文章, 本文着重介绍solver配置文件的编写。

Solver的流程:

1.     设计好需要优化的对象,以及用于学习的训练网络和用于评估的测试网络。(通过调用另外一个配置文件prototxt来进行)

2.     通过forward和backward迭代的进行优化来跟新参数。

3.     定期的评价测试网络。 (可设定多少次训练后,进行一次测试)

4.     在优化过程中显示模型和solver的状态

在每一次的迭代过程中,solver做了这几步工作:

1、调用forward算法来计算最终的输出值,以及对应的loss

2、调用backward算法来计算每层的梯度

3、根据选用的slover方法,利用梯度进行参数更新

4、记录并保存每次迭代的学习率、快照,以及对应的状态。

接下来,我们先来看一个实例:

net: "examples/mnist/lenet_train_test.prototxt"
test_iter: 100
test_interval: 500
base_lr: 0.01
momentum: 0.9
type: SGD
weight_decay: 0.0005
lr_policy: "inv"
gamma: 0.0001
power: 0.75
display: 100
max_iter: 20000
snapshot: 5000
snapshot_prefix: "examples/mnist/lenet"
solver_mode: CPU

接下来,我们对每一行进行详细解译:

net: "examples/mnist/lenet_train_test.prototxt"

设置深度网络模型。每一个模型就是一个net,需要在一个专门的配置文件中对net进行配置,每个net由许多的layer所组成。每一个layer的具体配置方式可参考本系列文文章中的(2)-(5)。注意的是:文件的路径要从caffe的根目录开始,其它的所有配置都是这样。

也可用train_net和test_net来对训练模型和测试模型分别设定。例如:

train_net: "examples/hdf5_classification/logreg_auto_train.prototxt"
test_net: "examples/hdf5_classification/logreg_auto_test.prototxt"

接下来第二行:

test_iter: 100

这个要与test layer中的batch_size结合起来理解。mnist数据中测试样本总数为10000,一次性执行全部数据效率很低,因此我们将测试数据分成几个批次来执行,每个批次的数量就是batch_size。假设我们设置batch_size为100,则需要迭代100次才能将10000个数据全部执行完。因此test_iter设置为100。执行完一次全部数据,称之为一个epoch

test_interval: 500

测试间隔。也就是每训练500次,才进行一次测试。

base_lr: 0.01
lr_policy: "inv"
gamma: 0.0001
power: 0.75

这四行可以放在一起理解,用于学习率的设置。只要是梯度下降法来求解优化,都会有一个学习率,也叫步长。base_lr用于设置基础学习率,在迭代的过程中,可以对基础学习率进行调整。怎么样进行调整,就是调整的策略,由lr_policy来设置。

lr_policy可以设置为下面这些值,相应的学习率的计算为:

    • – fixed:   保持base_lr不变.
    • – step:    如果设置为step,则还需要设置一个stepsize,  返回 base_lr * gamma ^ (floor(iter / stepsize)),其中iter表示当前的迭代次数
    • – exp:     返回base_lr * gamma ^ iter, iter为当前迭代次数
    • – inv:      如果设置为inv,还需要设置一个power, 返回base_lr * (1 + gamma * iter) ^ (- power)
    • – multistep: 如果设置为multistep,则还需要设置一个stepvalue。这个参数和step很相似,step是均匀等间隔变化,而multistep则是根据                                 stepvalue值变化
    • – poly:     学习率进行多项式误差, 返回 base_lr (1 – iter/max_iter) ^ (power)
    • – sigmoid: 学习率进行sigmod衰减,返回 base_lr ( 1/(1 + exp(-gamma * (iter – stepsize))))

multistep示例:

base_lr: 0.01
momentum: 0.9
weight_decay: 0.0005
# The learning rate policy
lr_policy: "multistep"
gamma: 0.9
stepvalue: 5000
stepvalue: 7000
stepvalue: 8000
stepvalue: 9000
stepvalue: 9500

接下来的参数:

momentum :0.9

上一次梯度更新的权重,具体可参看下一篇文章。

type: SGD

优化算法选择。这一行可以省掉,因为默认值就是SGD。总共有六种方法可选择,在本文的开头已介绍。

weight_decay: 0.0005

权重衰减项,防止过拟合的一个参数。

display: 100

每训练100次,在屏幕上显示一次。如果设置为0,则不显示。

max_iter: 20000

最大迭代次数。这个数设置太小,会导致没有收敛,精确度很低。设置太大,会导致震荡,浪费时间。

snapshot: 5000
snapshot_prefix: "examples/mnist/lenet"

快照。将训练出来的model和solver状态进行保存,snapshot用于设置训练多少次后进行保存,默认为0,不保存snapshot_prefix设置保存路径。

还可以设置snapshot_diff,是否保存梯度值,默认为false,不保存。

也可以设置snapshot_format,保存的类型。有两种选择:HDF5 和BINARYPROTO ,默认为BINARYPROTO

solver_mode: CPU

设置运行模式。默认为GPU,如果你没有GPU,则需要改成CPU,否则会出错。

注意:以上的所有参数都是可选参数,都有默认值。根据solver方法(type)的不同,还有一些其它的参数,在此不一一列举。

solver优化方法

上文提到,到目前为止,caffe总共提供了六种优化方法:

  • Stochastic Gradient Descent (type: "SGD"),
  • AdaDelta (type: "AdaDelta"),
  • Adaptive Gradient (type: "AdaGrad"),
  • Adam (type: "Adam"),
  • Nesterov’s Accelerated Gradient (type: "Nesterov") and
  • RMSprop (type: "RMSProp")

Solver就是用来使loss最小化的优化方法。对于一个数据集D,需要优化的目标函数是整个数据集中所有数据loss的平均值。

其中,fW(x(i))计算的是数据x(i)上的loss, 先将每个单独的样本x的loss求出来,然后求和,最后求均值。 r(W)是正则项(weight_decay),为了减弱过拟合现象。

如果采用这种Loss 函数,迭代一次需要计算整个数据集,在数据集非常大的这情况下,这种方法的效率很低,这个也是我们熟知的梯度下降采用的方法。

在实际中,通过将整个数据集分成几批(batches), 每一批就是一个mini-batch,其数量(batch_size)为N<<|D|,此时的loss 函数为:
 

有了loss函数后,就可以迭代的求解loss和梯度来优化这个问题。在神经网络中,用forward pass来求解loss,用backward pass来求解梯度。

在caffe中,默认采用的Stochastic Gradient Descent(SGD)进行优化求解。后面几种方法也是基于梯度的优化方法(like SGD),因此本文只介绍一下SGD。其它的方法,有兴趣的同学,可以去看文献原文。

1、Stochastic gradient descent(SGD)

随机梯度下降(Stochastic gradient descent)是在梯度下降法(gradient descent)的基础上发展起来的,梯度下降法也叫最速下降法,具体原理在网易公开课《机器学习》中,吴恩达教授已经讲解得非常详细。SGD在通过负梯度和上一次的权重更新值Vt的线性组合来更新W,迭代公式如下:

 


 
其中,  是负梯度的学习率(base_lr),是上一次梯度值的权重(momentum),用来加权之前梯度方向对现在梯度下降方向的影响。这两个参数需要通过tuning来得到最好的结果,一般是根据经验设定的。如果你不知道如何设定这些参数,可以参考相关的论文。

在深度学习中使用SGD,比较好的初始化参数的策略是把学习率设为0.01左右(base_lr: 0.01),在训练的过程中,如果loss开始出现稳定水平时,对学习率乘以一个常数因子(gamma),这样的过程重复多次。

对于momentum,一般取值在0.5–0.99之间。通常设为0.9,momentum可以让使用SGD的深度学习方法更加稳定以及快速。

关于更多的momentum,请参看Hinton的《A Practical Guide to Training Restricted Boltzmann Machines》。  

实例: 

base_lr: 0.01 
lr_policy: "step"
gamma: 0.1   
stepsize: 1000  
max_iter: 3500 
momentum: 0.9

lr_policy设置为step,则学习率的变化规则为 base_lr * gamma ^ (floor(iter / stepsize))

即前1000次迭代,学习率为0.01; 第1001-2000次迭代,学习率为0.001; 第2001-3000次迭代,学习率为0.00001,第3001-3500次迭代,学习率为10-5  

上面的设置只能作为一种指导,它们不能保证在任何情况下都能得到最佳的结果,有时候这种方法甚至不work。如果学习的时候出现diverge(比如,你一开始就发现非常大或者NaN或者inf的loss值或者输出),此时你需要降低base_lr的值(比如,0.001),然后重新训练,这样的过程重复几次直到你找到可以work的base_lr。

2、AdaDelta

AdaDelta是一种”鲁棒的学习率方法“,是基于梯度的优化方法(like SGD)。

具体的介绍文献:

M. Zeiler ADADELTA: AN ADAPTIVE LEARNING RATE METHOD. arXiv preprint, 2012.

示例:

复制代码

net: "examples/mnist/lenet_train_test.prototxt"
test_iter: 100
test_interval: 500
base_lr: 1.0
lr_policy: "fixed"
momentum: 0.95
weight_decay: 0.0005
display: 100
max_iter: 10000
snapshot: 5000
snapshot_prefix: "examples/mnist/lenet_adadelta"
solver_mode: GPU
type: "AdaDelta"
delta: 1e-6

复制代码

从最后两行可看出,设置solver type为Adadelta时,需要设置delta的值。

3、AdaGrad

自适应梯度(adaptive gradient)是基于梯度的优化方法(like SGD)

具体的介绍文献:

Duchi, E. Hazan, and Y. Singer. Adaptive Subgradient Methods for Online Learning and Stochastic Optimization. The Journal of Machine Learning Research, 2011.

示例:

net: "examples/mnist/mnist_autoencoder.prototxt"
test_state: { stage: 'test-on-train' }
test_iter: 500
test_state: { stage: 'test-on-test' }
test_iter: 100
test_interval: 500
test_compute_loss: true
base_lr: 0.01
lr_policy: "fixed"
display: 100
max_iter: 65000
weight_decay: 0.0005
snapshot: 10000
snapshot_prefix: "examples/mnist/mnist_autoencoder_adagrad_train"
# solver mode: CPU or GPU
solver_mode: GPU
type: "AdaGrad"

4、Adam

是一种基于梯度的优化方法(like SGD)。

 具体的介绍文献:

D. Kingma, J. Ba. Adam: A Method for Stochastic Optimization. International Conference for Learning Representations, 2015.

5、NAG

Nesterov 的加速梯度法(Nesterov’s accelerated gradient)作为凸优化中最理想的方法,其收敛速度非常快。

 具体的介绍文献:

 I. Sutskever, J. Martens, G. Dahl, and G. Hinton. On the Importance of Initialization and Momentum in Deep Learning. Proceedings of the 30th International Conference on Machine Learning, 2013.

示例:

net: "examples/mnist/mnist_autoencoder.prototxt"
test_state: { stage: 'test-on-train' }
test_iter: 500
test_state: { stage: 'test-on-test' }
test_iter: 100
test_interval: 500
test_compute_loss: true
base_lr: 0.01
lr_policy: "step"
gamma: 0.1
stepsize: 10000
display: 100
max_iter: 65000
weight_decay: 0.0005
snapshot: 10000
snapshot_prefix: "examples/mnist/mnist_autoencoder_nesterov_train"
momentum: 0.95
# solver mode: CPU or GPU
solver_mode: GPU
type: "Nesterov"

6、RMSprop

RMSprop是Tieleman在一次 Coursera课程演讲中提出来的,也是一种基于梯度的优化方法(like SGD)

具体的介绍文献:

T. Tieleman, and G. Hinton. RMSProp: Divide the gradient by a running average of its recent magnitude. COURSERA: Neural Networks for Machine Learning.Technical report, 2012.

 示例:

net: "examples/mnist/lenet_train_test.prototxt"
test_iter: 100
test_interval: 500
base_lr: 1.0
lr_policy: "fixed"
momentum: 0.95
weight_decay: 0.0005
display: 100
max_iter: 10000
snapshot: 5000
snapshot_prefix: "examples/mnist/lenet_adadelta"
solver_mode: GPU
type: "RMSProp"
rms_decay: 0.98

最后两行,需要设置rms_decay值。

运行caffe自带的两个简单例子

为了程序的简洁,在caffe中是不带练习数据的,因此需要自己去下载。但在caffe根目录下的data文件夹里,作者已经为我们编写好了下载数据的脚本文件,我们只需要联网,运行这些脚本文件就行了。

注意:在caffe中运行所有程序,都必须在根目录下进行,否则会出错

1、mnist实例

mnist是一个手写数字库,由DL大牛Yan LeCun进行维护。mnist最初用于支票上的手写数字识别, 现在成了DL的入门练习库。征对mnist识别的专门模型是Lenet,算是最早的cnn模型了。

mnist数据训练样本为60000张,测试样本为10000张,每个样本为28*28大小的黑白图片,手写数字为0-9,因此分为10类。

首先下载mnist数据,假设当前路径为caffe根目录

# sudo sh data/mnist/get_mnist.sh

运行成功后,在 data/mnist/目录下有四个文件:

train-images-idx3-ubyte:  训练集样本 (9912422 bytes) 
train-labels-idx1-ubyte:  训练集对应标注 (28881 bytes) 
t10k-images-idx3-ubyte:   测试集图片 (1648877 bytes) 
t10k-labels-idx1-ubyte:   测试集对应标注 (4542 bytes)

这些数据不能在caffe中直接使用,需要转换成LMDB数据

# sudo sh examples/mnist/create_mnist.sh

如果想运行leveldb数据,请运行 examples/siamese/ 文件夹下面的程序。 examples/mnist/ 文件夹是运行lmdb数据

转换成功后,会在 examples/mnist/目录下,生成两个文件夹,分别是mnist_train_lmdb和mnist_test_lmdb,里面存放的data.mdb和lock.mdb,就是我们需要的运行数据。

接下来是修改配置文件,如果你有GPU且已经完全安装好,这一步可以省略,如果没有,则需要修改solver配置文件。

需要的配置文件有两个,一个是lenet_solver.prototxt,另一个是train_lenet.prototxt.

首先打开lenet_solver_prototxt

# sudo vi examples/mnist/lenet_solver.prototxt

根据需要,在max_iter处设置最大迭代次数,以及决定最后一行solver_mode,是否要改成CPU

保存退出后,就可以运行这个例子了

# sudo time sh examples/mnist/train_lenet.sh

CPU运行时候大约13分钟,GPU运行时间大约4分钟,GPU+cudnn运行时候大约40秒,精度都为99%左右

2、cifar10实例

cifar10数据训练样本50000张,测试样本10000张,每张为32*32的彩色三通道图片,共分为10类。

下载数据:

# sudo sh data/cifar10/get_cifar10.sh

运行成功后,会在 data/cifar10/文件夹下生成一堆bin文件

转换数据格式为lmdb:

# sudo sh examples/cifar10/create_cifar10.sh

转换成功后,会在 examples/cifar10/文件夹下生成两个文件夹,cifar10_train_lmdb和cifar10_test_lmdb, 里面的文件就是我们需要的文件。

为了节省时间,我们进行快速训练(train_quick),训练分为两个阶段,第一个阶段(迭代4000次)调用配置文件cifar10_quick_solver.prototxt, 学习率(base_lr)为0.001

第二阶段(迭代1000次)调用配置文件cifar10_quick_solver_lr1.prototxt, 学习率(base_lr)为0.0001

前后两个配置文件就是学习率(base_lr)和最大迭代次数(max_iter)不一样,其它都是一样。如果你对配置文件比较熟悉以后,实际上是可以将两个配置文件合二为一的,设置lr_policy为multistep就可以了。

base_lr: 0.001
momentum: 0.9
weight_decay: 0.004
lr_policy: "multistep"
gamma: 0.1
stepvalue: 4000
stepvalue: 5000

运行例子:

# sudo time sh examples/cifar10/train_quick.sh

GPU+cudnn大约45秒左右,精度75%左右。

如何将别人训练好的model用到自己的数据上

caffe团队用imagenet图片进行训练,迭代30多万次,训练出来一个model。这个model将图片分为1000类,应该是目前为止最好的图片分类model了。

假设我现在有一些自己的图片想进行分类,但样本量太小,可能只有几百张,而一般深度学习都要求样本量在1万以上,因此训练出来的model精度太低,根本用不上,那怎么办呢?

那就用caffe团队提供给我们的model吧。

因为训练好的model里面存放的就是一些参数,因此我们实际上就是把别人预先训练好的参数,拿来作为我们的初始化参数,而不需要再去随机初始化了。图片的整个训练过程,说白了就是将初始化参数不断更新到最优的参数的一个过程,既然这个过程别人已经帮我们做了,而且比我们做得更好,那为什么不用他们的成果呢?

使用别人训练好的参数,必须有一个前提,那就是必须和别人用同一个network,因为参数是根据network而来的。当然,最后一层,我们是可以修改的,因为我们的数据可能并没有1000类,而只有几类。我们把最后一层的输出类别改一下,然后把层的名称改一下就可以了。最后用别人的参数、修改后的network和我们自己的数据,再进行训练,使得参数适应我们的数据,这样一个过程,通常称之为微调(fine tuning).

既然前两篇文章我们已经讲过使用digits来进行训练和可视化,这样一个神器怎么能不使用呢?因此本文以此工具为例,讲解整个微调训练过程。

一、下载model参数

可以直接在浏览器里输入地址下载,也可以运行脚本文件下载。下载地址为:http://dl.caffe.berkeleyvision.org/bvlc_reference_caffenet.caffemodel

文件名称为:bvlc_reference_caffenet.caffemodel,文件大小为230M左右,为了代码的统一,将这个caffemodel文件下载到caffe根目录下的 models/bvlc_reference_caffenet/ 文件夹下面。也可以运行脚本文件进行下载:

# sudo ./scripts/download_model_binary.py models/bvlc_reference_caffenet

 二、准备数据

如果有自己的数据最好,如果没有,可以下载我的练习数据:

到Linux公社1号FTP服务器下载

——————————————分割线——————————————

FTP地址:ftp://ftp1.linuxidc.com

用户名:ftp1.linuxidc.com

密码:www.linuxidc.com

在 2016年LinuxIDC.com\11月\Caffe 深度学习入门教程\

下载方法见 http://www.linuxidc.com/Linux/2013-10/91140.htm

——————————————分割线—————————————— 

这些数据共有500张图片,分为大巴车、恐龙、大象、鲜花和马五个类,每个类100张。编号分别以3,4,5,6,7开头,各为一类。我从其中每类选出20张作为测试,其余80张作为训练。因此最终训练图片400张(放在train文件夹内,每个类一个子文件夹),测试图片100张(放在test文件夹内,每个类一个子文件夹)。

将图片下载下来后解压,放在一个文件夹内。比如我在当前用户根目录下创建了一个data文件夹,专门用来存放数据,因此我的训练图片路径为:/home/xxx/data/re/train

打开浏览器,运行digits,如果没有这个工具的,推荐安装,真的是学习caffe的神器。安装及使用可参见我的前两篇文章:Caffe 深度学习入门教程(21):caffe图形化操作工具digits的安装与运行  http://www.linuxidc.com/Linux/2016-11/136774p21.htm

新建一个classification dataset,设置如下图:

下面图片格式选为jpg, 为dataset取一个名字,就开始转换吧。结果如图:

三、设置model

回到digits根目录,新建一个classification model, 选中你的dataset, 开始设置最重要的network.

 

caffenet的网络配置文件,放在 caffe/models/bvlc_reference_caffenet/ 这个文件夹里面,名字叫train_val.prototxt。打开这个文件,将里面的内容复制到上图的Custom Network文本框里,然后进行修改,主要修改这几个地方:

1、修改train阶段的data层为:

layer {
  name: "data"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
  transform_param {
    mirror: true
    crop_size: 227
  }
}

即把均值文件(mean_file)、数据源文件(source)、批次大小(batch_size)和数据源格式(backend)这四项都删除了。因为这四项系统会根据dataset和页面左边“solver options”的设置自动生成。

2、修改test阶段的data层:

layer {
  name: "data"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TEST
  }
  transform_param {
    mirror: false
    crop_size: 227
  }
}

和上面一样,也是删除那些项。

3、修改最后一个全连接层(fc8):

layer {
  name: "fc8-re"               #原来为"fc8"
  type: "InnerProduct"
  bottom: "fc7"
  top: "fc8"
  param {
    lr_mult: 1.0
    decay_mult: 1.0
  }
  param {
    lr_mult: 2.0
    decay_mult: 0.0
  }
  inner_product_param {
    num_output: 5        #原来为"1000"
    weight_filler {
      type: "gaussian"
      std: 0.01
    }
    bias_filler {
      type: "constant"
      value: 0.0
    }
  }
}

看注释的地方,就只有两个地方修改,其它不变。

设置好后,就可以开始微调了(fine tuning).

训练结果就是一个新的model,可以用来单张图片和多张图片测试。具体测试方法前一篇文章已讲过,在此就不重复了。

在此,将别人训练好的model用到我们自己的图片分类上,整个微调过程就是这样了。如果你不用digits,而直接用命令操作,那就更简单,只需要修改一个train_val.prototxt的配置文件就可以了,其它都是一样的操作。

本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-11/136774.htm

命令行解析

caffe的运行提供三种接口:c++接口(命令行)、Python接口和matlab接口。本文先对命令行进行解析,后续会依次介绍其它两个接口。

caffe的c++主程序(caffe.cpp)放在根目录下的tools文件夹内, 当然还有一些其它的功能文件,如:convert_imageset.cpp, train_net.cpp, test_net.cpp等也放在这个文件夹内。经过编译后,这些文件都被编译成了可执行文件,放在了 ./build/tools/ 文件夹内。因此我们要执行caffe程序,都需要加 ./build/tools/ 前缀。

如:

# sudo sh ./build/tools/caffe train --solver=examples/mnist/train_lenet.sh

caffe程序的命令行执行格式如下:

caffe <command> <args>

其中的<command>有这样四种:

  • train
  • test
  • device_query
  • time

对应的功能为:

train—-训练或finetune模型(model),

test—–测试模型

device_query—显示gpu信息

time—–显示程序执行时间

其中的<args>参数有:

  • -solver
  • -gpu
  • -snapshot
  • -weights
  • -iteration
  • -model
  • -sighup_effect
  • -sigint_effect

注意前面有个-符号。对应的功能为:

-solver:必选参数。一个protocol buffer类型的文件,即模型的配置文件。如:

# ./build/tools/caffe train -solver examples/mnist/lenet_solver.prototxt

-gpu: 可选参数。该参数用来指定用哪一块gpu运行,根据gpu的id进行选择,如果设置为’-gpu all’则使用所有的gpu运行。如使用第二块gpu运行:

# ./build/tools/caffe train -solver examples/mnist/lenet_solver.prototxt -gpu 2

-snapshot:可选参数。该参数用来从快照(snapshot)中恢复训练。可以在solver配置文件设置快照,保存solverstate。如:

# ./build/tools/caffe train -solver examples/mnist/lenet_solver.prototxt -snapshot examples/mnist/lenet_iter_5000.solverstate

-weights:可选参数。用预先训练好的权重来fine-tuning模型,需要一个caffemodel,不能和-snapshot同时使用。如:

# ./build/tools/caffe train -solver examples/finetuning_on_flickr_style/solver.prototxt -weights models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel

-iterations: 可选参数,迭代次数,默认为50。 如果在配置文件文件中没有设定迭代次数,则默认迭代50次。

-model:可选参数,定义在protocol buffer文件中的模型。也可以在solver配置文件中指定。

-sighup_effect:可选参数。用来设定当程序发生挂起事件时,执行的操作,可以设置为snapshot, stop或none, 默认为snapshot

-sigint_effect: 可选参数。用来设定当程序发生键盘中止事件时(ctrl+c), 执行的操作,可以设置为snapshot, stop或none, 默认为stop

 

刚才举例了一些train参数的例子,现在我们来看看其它三个<command>:

test参数用在测试阶段,用于最终结果的输出,要模型配置文件中我们可以设定需要输入accuracy还是loss. 假设我们要在验证集中验证已经训练好的模型,就可以这样写

# ./build/tools/caffe test -model examples/mnist/lenet_train_test.prototxt -weights examples/mnist/lenet_iter_10000.caffemodel -gpu 0 -iterations 100

这个例子比较长,不仅用到了test参数,还用到了-model, -weights, -gpu和-iteration四个参数。意思是利用训练好了的权重(-weight),输入到测试模型中(-model),用编号为0的gpu(-gpu)测试100次(-iteration)。

time参数用来在屏幕上显示程序运行时间。如:

# ./build/tools/caffe time -model examples/mnist/lenet_train_test.prototxt -iterations 10

这个例子用来在屏幕上显示lenet模型迭代10次所使用的时间。包括每次迭代的forward和backward所用的时间,也包括每层forward和backward所用的平均时间。

# ./build/tools/caffe time -model examples/mnist/lenet_train_test.prototxt -gpu 0

这个例子用来在屏幕上显示lenet模型用gpu迭代50次所使用的时间。

# ./build/tools/caffe time -model examples/mnist/lenet_train_test.prototxt -weights examples/mnist/lenet_iter_10000.caffemodel -gpu 0 -iterations 10

利用给定的权重,利用第一块gpu,迭代10次lenet模型所用的时间。

device_query参数用来诊断gpu信息。

# ./build/tools/caffe device_query -gpu 0

最后,我们来看两个关于gpu的例子

# ./build/tools/caffe train -solver examples/mnist/lenet_solver.prototxt -gpu 0,1
# ./build/tools/caffe train -solver examples/mnist/lenet_solver.prototxt -gpu all

这两个例子表示: 用两块或多块GPU来平行运算,这样速度会快很多。但是如果你只有一块或没有gpu, 就不要加-gpu参数了,加了反而慢。

最后,在linux下,本身就有一个time命令,因此可以结合进来使用,因此我们运行mnist例子的最终命令是(一块gpu):

$ sudo time ./build/toos/caffe train -solver examples/mnist/lenet_solver.prototxt

图像数据转换成db(leveldb/lmdb)文件

在深度学习的实际应用中,我们经常用到的原始数据是图片文件,如jpg,jpeg,png,tif等格式的,而且有可能图片的大小还不一致。而在caffe中经常使用的数据类型是lmdb或leveldb,因此就产生了这样的一个问题:如何从原始图片文件转换成caffe中能够运行的db(leveldb/lmdb)文件?

在caffe中,作者为我们提供了这样一个文件:convert_imageset.cpp,存放在根目录下的tools文件夹下。编译之后,生成对应的可执行文件放在 buile/tools/ 下面,这个文件的作用就是用于将图片文件转换成caffe框架中能直接使用的db文件。

该文件的使用格式:

 convert_imageset [FLAGS] ROOTFOLDER/ LISTFILE DB_NAME

需要带四个参数:

FLAGS: 图片参数组,后面详细介绍

ROOTFOLDER/: 图片存放的绝对路径,从linux系统根目录开始

LISTFILE: 图片文件列表清单,一般为一个txt文件,一行一张图片

DB_NAME: 最终生成的db文件存放目录

如果图片已经下载到本地电脑上了,那么我们首先需要创建一个图片列表清单,保存为txt

本文以caffe程序中自带的图片为例,进行讲解,图片目录是  example/images/, 两张图片,一张为cat.jpg, 另一张为fish_bike.jpg,表示两个类别。

我们创建一个sh脚本文件,调用linux命令来生成图片清单:

# sudo vi examples/images/create_filelist.sh

编辑这个文件,输入下面的代码并保存

# /usr/bin/env sh
DATA=examples/images
echo "Create train.txt..."
rm -rf $DATA/train.txt
find $DATA -name *cat.jpg | cut -d '/' -f3 | sed "s/$/ 1/">>$DATA/train.txt
find $DATA -name *bike.jpg | cut -d '/' -f3 | sed "s/$/ 2/">>$DATA/tmp.txt
cat $DATA/tmp.txt>>$DATA/train.txt
rm -rf $DATA/tmp.txt
echo "Done.."

这个脚本文件中,用到了rm,find, cut, sed,cat等linux命令。

rm: 删除文件

find: 寻找文件

cut: 截取路径

sed: 在每行的最后面加上标注。本例中将找到的*cat.jpg文件加入标注为1,找到的*bike.jpg文件加入标注为2

cat: 将两个类别合并在一个文件里。

最终生成如下的一个train.txt文件:

cat.jpg 1
fish-bike.jpg 2

当然,图片很少的时候,手动编写这个列表清单文件就行了。但图片很多的情况,就需要用脚本文件来自动生成了。在以后的实际应用中,还需要生成相应的val.txt和test.txt文件,方法是一样的。

生成的这个train.txt文件,就可以作为第三个参数,直接使用了。

接下来,我们来了解一下FLAGS这个参数组,有些什么内容:

-gray: 是否以灰度图的方式打开图片。程序调用opencv库中的imread()函数来打开图片,默认为false

-shuffle: 是否随机打乱图片顺序。默认为false

-backend:需要转换成的db文件格式,可选为leveldb或lmdb,默认为lmdb

-resize_width/resize_height: 改变图片的大小。在运行中,要求所有图片的尺寸一致,因此需要改变图片大小。 程序调用opencv库的resize()函数来对图片放大缩小,默认为0,不改变

-check_size: 检查所有的数据是否有相同的尺寸。默认为false,不检查

-encoded: 是否将原图片编码放入最终的数据中,默认为false

-encode_type: 与前一个参数对应,将图片编码为哪一个格式:‘png’,’jpg’……

好了,知道这些参数后,我们就可以调用命令来生成最终的lmdb格式数据了

由于参数比较多,因此我们可以编写一个sh脚本来执行命令:

首先,创建sh脚本文件:

# sudo vi examples/images/create_lmdb.sh

编辑,输入下面的代码并保存

#!/usr/bin/en sh
DATA=examples/images
rm -rf $DATA/img_train_lmdb
build/tools/convert_imageset --shuffle --resize_height=256 --resize_width=256 /home/xxx/caffe/examples/images/ $DATA/train.txt  $DATA/img_train_lmdb

设置参数-shuffle,打乱图片顺序。设置参数-resize_height和-resize_width将所有图片尺寸都变为256*256.

/home/xxx/caffe/examples/images/ 为图片保存的绝对路径。

最后,运行这个脚本文件

# sudo sh examples/images/create_lmdb.sh

就会在examples/images/ 目录下生成一个名为 img_train_lmdb的文件夹,里面的文件就是我们需要的db文件了。

训练和测试自己的图片

学习caffe的目的,不是简单的做几个练习,最终还是要用到自己的实际项目或科研中。因此,本文介绍一下,从自己的原始图片到lmdb数据,再到训练和测试模型的整个流程。

一、准备数据

有条件的同学,可以去imagenet的官网http://www.image-net.org/download-images,下载imagenet图片来训练。但是我没有下载,一个原因是注册账号的时候,验证码始终出不来(听说是google网站的验证码,而我是上不了google的)。第二个原因是数据太大了。。。

我去网上找了一些其它的图片来代替,共有500张图片,分为大巴车、恐龙、大象、鲜花和马五个类,每个类100张。需要的同学,可到Linux公社1号FTP服务器下载

——————————————分割线——————————————

FTP地址:ftp://ftp1.linuxidc.com

用户名:ftp1.linuxidc.com

密码:www.linuxidc.com

在 2016年LinuxIDC.com\11月\Caffe 深度学习入门教程\

下载方法见 http://www.linuxidc.com/Linux/2013-10/91140.htm

——————————————分割线—————————————— 

编号分别以3,4,5,6,7开头,各为一类。我从其中每类选出20张作为测试,其余80张作为训练。因此最终训练图片400张,测试图片100张,共5类。我将图片放在caffe根目录下的data文件夹下面。即训练图片目录:data/re/train/ ,测试图片目录: data/re/test/

二、转换为lmdb格式

具体的转换过程,可参见我的前一篇博文:Caffe 深度学习入门教程(11):图像数据转换成db(leveldb/lmdb)文件

首先,在examples下面创建一个myfile的文件夹,来用存放配置文件和脚本文件。然后编写一个脚本create_filelist.sh,用来生成train.txt和test.txt清单文件

# sudo mkdir examples/myfile
# sudo vi examples/myfile/create_filelist.sh

编辑此文件,写入如下代码,并保存

#!/usr/bin/env sh
DATA=data/re/
MY=examples/myfile
echo
"Create train.txt..." rm -rf $MY/train.txt for i in 3 4 5 6 7 do find $DATA/train -name $i*.jpg | cut -d '/' -f4-5 | sed "s/$/ $i/">>$MY/train.txt done echo "Create test.txt..." rm -rf $MY/test.txt for i in 3 4 5 6 7 do find $DATA/test -name $i*.jpg | cut -d '/' -f4-5 | sed "s/$/ $i/">>$MY/test.txt done echo "All done"

然后,运行此脚本

# sudo sh examples/myfile/create_filelist.sh

成功的话,就会在examples/myfile/ 文件夹下生成train.txt和test.txt两个文本文件,里面就是图片的列表清单。

接着再编写一个脚本文件,调用convert_imageset命令来转换数据格式。

# sudo vi examples/myfile/create_lmdb.sh

插入:

#!/usr/bin/env sh
MY=examples/myfile

echo "Create train lmdb.."
rm -rf $MY/img_train_lmdb
build/tools/convert_imageset --shuffle --resize_height=256 --resize_width=256 /home/xxx/caffe/data/re/ $MY/train.txt $MY/img_train_lmdb

echo "Create test lmdb.."
rm -rf $MY/img_test_lmdb
build/tools/convert_imageset --shuffle --resize_width=256 --resize_height=256 /home/xxx/caffe/data/re/ $MY/test.txt $MY/img_test_lmdb

echo "All Done.."

复制代码

因为图片大小不一,因此我统一转换成256*256大小。运行成功后,会在 examples/myfile下面生成两个文件夹img_train_lmdb和img_test_lmdb,分别用于保存图片转换后的lmdb文件。

三、计算均值并保存

图片减去均值再训练,会提高训练速度和精度。因此,一般都会有这个操作。

caffe程序提供了一个计算均值的文件compute_image_mean.cpp,我们直接使用就可以了

# sudo build/tools/compute_image_mean examples/myfile/img_train_lmdb examples/myfile/mean.binaryproto
compute_image_mean带两个参数,第一个参数是lmdb训练数据位置,第二个参数设定均值文件的名字及保存路径。
运行成功后,会在 examples/myfile/ 下面生成一个mean.binaryproto的均值文件。

四、创建模型并编写配置文件

模型就用程序自带的caffenet模型,位置在 models/bvlc_reference_caffenet/文件夹下, 将需要的两个配置文件,复制到myfile文件夹内

# sudo cp models/bvlc_reference_caffenet/solver.prototxt examples/myfile/
# sudo cp models/bvlc_reference_caffenet/train_val.prototxt examples/myfile/

修改其中的solver.prototxt

# sudo vi examples/myfile/solver.prototxt

 

net: "examples/myfile/train_val.prototxt"
test_iter: 2
test_interval: 50
base_lr: 0.001
lr_policy: "step"
gamma: 0.1
stepsize: 100
display: 20
max_iter: 500
momentum: 0.9
weight_decay: 0.005
solver_mode: GPU

100个测试数据,batch_size为50,因此test_iter设置为2,就能全cover了。在训练过程中,调整学习率,逐步变小。

修改train_val.protxt,只需要修改两个阶段的data层就可以了,其它可以不用管。

name: "CaffeNet"
layer {
  name: "data"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
  transform_param {
    mirror: true
    crop_size: 227
    mean_file: "examples/myfile/mean.binaryproto"
  }
  data_param {
    source: "examples/myfile/img_train_lmdb"
    batch_size: 256
    backend: LMDB
  }
}
layer {
  name: "data"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TEST
  }
  transform_param {
    mirror: false
    crop_size: 227
    mean_file: "examples/myfile/mean.binaryproto"
  }
  data_param {
    source: "examples/myfile/img_test_lmdb"
    batch_size: 50
    backend: LMDB
  }
}

实际上就是修改两个data layer的mean_file和source这两个地方,其它都没有变化 。

五、训练和测试

如果前面都没有问题,数据准备好了,配置文件也配置好了,这一步就比较简单了。

# sudo build/tools/caffe train -solver examples/myfile/solver.prototxt

运行时间和最后的精确度,会根据机器配置,参数设置的不同而不同。我的是gpu+cudnn运行500次,大约8分钟,精度为95%。

数据可视化环境(Python接口)配置

caffe程序是由c++语言写的,本身是不带数据可视化功能的。只能借助其它的库或接口,如opencv, python或matlab。大部分人使用python接口来进行可视化,因为python出了个比较强大的东西:ipython notebook, 现在的最新版本改名叫jupyter notebook,它能将python代码搬到浏览器上去执行,以富文本方式显示,使得整个工作可以以笔记的形式展现、存储,对于交互编程、学习非常方便。 

   python环境不能单独配置,必须要先编译好caffe,才能编译python环境。

    python环境的配置说起来简单,做起来非常复杂。在安装的过程中,可能总是出现这样那样的问题。因此强烈建议大家用anaconda来进行安装,anaconda把很多与python有关的库都收集在一起了,包括numpy,scipy等等,因此,我们只需要下载对应系统,对应版本的anaconda来安装就可以了。

如果你想通过anaconda来安装,请跳过第一、二步,直接进入第三步开始:

一、安装python和pip

一般linux系统都自带python,所以不需要安装。如果没有的,安装起来也非常方便。安装完成后,可用version查看版本

# python --version

pip是专门用于安装python各种依赖库的,所以我们这里安装一下pip1.5.6

先用链接下载安装包 https://pypi.python.org/packages/source/p/pip/pip-1.5.6.tar.gz,然后解压,里面有一个setup.py的文件,执行这个文件就可以安装pip了

# sudo python setup.py install

有些电脑可能会提示 no moudle name setuptools 的错误,这是没有安装setuptools的原因。那就需要先安装一下setuptools, 到https://pypi.python.org/packages/source/s/setuptools/setuptools-19.2.tar.gz 下载安装包setuptools-19.2.tar.gz,然后解压执行

# sudo python setup.py install

就要以安装setuptools了,然后再回头去重新安装pip。执行的代码都是一样的,只是在不同的目录下执行。

二、安装pyhon接口依赖库

在caffe根目录的python文件夹下,有一个requirements.txt的清单文件,上面列出了需要的依赖库,按照这个清单安装就可以了。

在安装scipy库的时候,需要fortran编译器(gfortran),如果没有这个编译器就会报错,因此,我们可以先安装一下。

首先回到caffe的根目录,然后执行安装代码:

# cd ~/caffe
# sudo apt-get install gfortran
# for req in $(cat requirements.txt); do sudo pip install $req; done

安装完成以后,我们可以执行:

# sudo pip install -r python/requirements.txt

就会看到,安装成功的,都会显示Requirement already satisfied, 没有安装成功的,会继续安装。

在安装的时候,也许问题会有一大堆。这时候你就知道anaconda的好处了。

三、利用anaconda来配置python环境

如果你上面两步已经没有问题了,那么这一步可以省略。

如果你想简单一些,利用anaconda来配置python环境,那么直接从这一步开始,可以省略上面两步。

先到https://www.continuum.io/downloads 下载anaconda, 现在的版本有python2.7版本和python3.5版本,下载好对应版本、对应系统的anaconda,它实际上是一个sh脚本文件,大约280M左右。我下载的是linux版的python 2.7版本。

下载成功后,在终端执行(2.7版本):

# bash Anaconda2-2.4.1-Linux-x86_64.sh

或者3.5 版本:

# bash Anaconda3-2.4.1-Linux-x86_64.sh

在安装的过程中,会问你安装路径,直接回车默认就可以了。有个地方问你是否将anaconda安装路径加入到环境变量(.bashrc)中,这个一定要输入yes

安装成功后,会有当前用户根目录下生成一个anaconda2的文件夹,里面就是安装好的内容。

输入conda list 就可以查询,你现在安装了哪些库,常用的numpy, scipy名列其中。如果你还有什么包没有安装上,可以运行

conda install ***  来进行安装,

如果某个包版本不是最新的,运行 conda update *** 就可以了。

四、编译python接口

首先,将caffe根目录下的python文件夹加入到环境变量

打开配置文件bashrc

# sudo vi ~/.bashrc

在最后面加入

export PYTHONPATH=/home/xxx/caffe/python:$PYTHONPATH

注意 /home/xxx/caffe/python 是我的路径,这个地方每个人都不同,需要修改

保存退出,更新配置文件

# sudo ldconfig

然后修改编译配置文件Makefile.config. 我的配置是:

## Refer to http://caffe.berkeleyvision.org/installation.html
# Contributions simplifying and improving our build system are welcome!

# cuDNN acceleration switch (uncomment to build with cuDNN).
USE_CUDNN := 1

# CPU-only switch (uncomment to build without GPU support).
# CPU_ONLY := 1

# uncomment to disable IO dependencies and corresponding data layers
# USE_OPENCV := 0
# USE_LEVELDB := 0
# USE_LMDB := 0

# uncomment to allow MDB_NOLOCK when reading LMDB files (only if necessary)
#    You should not set this flag if you will be reading LMDBs with any
#    possibility of simultaneous read and write
# ALLOW_LMDB_NOLOCK := 1

# Uncomment if you're using OpenCV 3
# OPENCV_VERSION := 3

# To customize your choice of compiler, uncomment and set the following.
# N.B. the default for Linux is g++ and the default for OSX is clang++
# CUSTOM_CXX := g++

# CUDA directory contains bin/ and lib/ directories that we need.
CUDA_DIR := /usr/local/cuda
# On Ubuntu 14.04, if cuda tools are installed via
# "sudo apt-get install nvidia-cuda-toolkit" then use this instead:
# CUDA_DIR := /usr

# CUDA architecture setting: going with all of them.
# For CUDA < 6.0, comment the *_50 lines for compatibility.
CUDA_ARCH := -gencode arch=compute_20,code=sm_20         -gencode arch=compute_20,code=sm_21         -gencode arch=compute_30,code=sm_30         -gencode arch=compute_35,code=sm_35         -gencode arch=compute_50,code=sm_50         -gencode arch=compute_50,code=compute_50

# BLAS choice:
# atlas for ATLAS (default)
# mkl for MKL
# open for OpenBlas
BLAS := atlas
# Custom (MKL/ATLAS/OpenBLAS) include and lib directories.
# Leave commented to accept the defaults for your choice of BLAS
# (which should work)!
# BLAS_INCLUDE := /path/to/your/blas
# BLAS_LIB := /path/to/your/blas

# Homebrew puts openblas in a directory that is not on the standard search path
# BLAS_INCLUDE := $(shell brew --prefix openblas)/include
# BLAS_LIB := $(shell brew --prefix openblas)/lib

# This is required only if you will compile the matlab interface.
# MATLAB directory should contain the mex binary in /bin.
# MATLAB_DIR := /usr/local
# MATLAB_DIR := /Applications/MATLAB_R2012b.app

# NOTE: this is required only if you will compile the python interface.
# We need to be able to find Python.h and numpy/arrayobject.h.
# PYTHON_INCLUDE := /usr/include/python2.7 \
        /usr/lib/python2.7/dist-packages/numpy/core/include
# Anaconda Python distribution is quite popular. Include path:
# Verify anaconda location, sometimes it's in root.
ANACONDA_HOME := $(HOME)/anaconda2
PYTHON_INCLUDE := $(ANACONDA_HOME)/include         $(ANACONDA_HOME)/include/python2.7         $(ANACONDA_HOME)/lib/python2.7/site-packages/numpy/core/include 
# We need to be able to find libpythonX.X.so or .dylib.
# PYTHON_LIB := /usr/lib
PYTHON_LIB := $(ANACONDA_HOME)/lib

# Homebrew installs numpy in a non standard path (keg only)
# PYTHON_INCLUDE += $(dir $(shell python -c 'import numpy.core; print(numpy.core.__file__)'))/include
# PYTHON_LIB += $(shell brew --prefix numpy)/lib

# Uncomment to support layers written in Python (will link against Python libs)
WITH_PYTHON_LAYER := 1

# Whatever else you find you need goes here.
INCLUDE_DIRS := $(PYTHON_INCLUDE) /usr/local/include
LIBRARY_DIRS := $(PYTHON_LIB) /usr/local/lib /usr/lib

# If Homebrew is installed at a non standard location (for example your home directory) and you use it for general dependencies
# INCLUDE_DIRS += $(shell brew --prefix)/include
# LIBRARY_DIRS += $(shell brew --prefix)/lib

# Uncomment to use `pkg-config` to specify OpenCV library paths.
# (Usually not necessary -- OpenCV libraries are normally installed in one of the above $LIBRARY_DIRS.)
# USE_PKG_CONFIG := 1

BUILD_DIR := build
DISTRIBUTE_DIR := distribute

# Uncomment for debugging. Does not work on OSX due to https://github.com/BVLC/caffe/issues/171
# DEBUG := 1

# The ID of the GPU that 'make runtest' will use to run unit tests.
TEST_GPUID := 0

# enable pretty build (comment to see full commands)
Q ?= @

修改完编译配置文件后,最后进行编译:

# sudo make pycaffe

编译成功后,不能重复编译,否则会提示 Nothing to be done for “pycaffe”的错误。

防止其它意外的错误,最好还编译一下:

# sudo make test -j8
# sudo make runtest -j8

也许你在编译runtest的时候,会报这样的错误:

.build_release/test/test_all.testbin: error while loading shared libraries: libhdf5.so.10: cannot open shared object file: No such file or directory

这是因为 libhdf5.so的版本问题,你可以进入/usr/lib/x86_64-linux-gnu看一下,你的libhdf5.so.x中的那个x是多少,比如我的是libhdf5.so.7

 因此可以执行下面几行代码解决:

# cd /usr/lib/x86_64-linux-gnu
# sudo ln -s libhdf5.so.7 libhdf5.so.10
# sudo ln -s libhdf5_hl.so.7 libhdf5_hl.so.10
# sudo ldconfig

最终查看python接口是否编译成功:

进入python环境,进行import操作

# python
>>> import caffe

如果没有提示错误,则编译成功。

五、安装jupyter

安装了python还不行,还得安装一下ipython,后者更加方便快捷,更有自动补全功能。而ipython notebook是ipython的最好展现方式。最新的版本改名为jupyter notebook,我们先来安装一下。(如果安装了anaconda, jupyter notebook就已经自动装好,不需要再安装)

# sudo pip install jupyter

安装成功后,运行notebook

# jupyter notebook

就会在浏览器中打开notebook,  点击右上角的New-python2, 就可以新建一个网页一样的文件,扩展名为ipynb。在这个网页上,我们就可以像在命令行下面一样运行python代码了。输入代码后,按shift+enter运行,更多的快捷键,可点击上方的help-Keyboard shortcuts查看,或者先按esc退出编辑状态,再按h键查看。

初识数据可视化

首先将caffe的根目录作为当前目录,然后加载caffe程序自带的小猫图片,并显示。

图片大小为360×480,三通道

In [1]:

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import caffe
caffe_root='/home/xxx/caffe/'
import os,sys
os.chdir(caffe_root)
sys.path.insert(0,caffe_root+'Python')
im = caffe.io.load_image('examples/images/cat.jpg')
print im.shape
plt.imshow(im)
plt.axis('off')

 

(360, 480, 3)

Out[1]:

(-0.5, 479.5, 359.5, -0.5)

 
Caffe 深度学习入门教程 

打开examples/net_surgery/conv.prototxt文件,修改两个地方

一是将input_shape由原来的是(1,1,100,100)修改为(1,3,100,100),即由单通道灰度图变为三通道彩色图。

二是将过滤器个数(num_output)由3修改为16,多增加一些filter, 当然保持原来的数不变也行。

其它地方不变,修改后的prototxt如下:只有一个卷积层

In [2]:

! cat examples/net_surgery/conv.prototxt

 

# Simple single-layer network to showcase editing model parameters.
name: "convolution"
input: "data"
input_shape {
  dim: 1
  dim: 3
  dim: 100
  dim: 100
}
layer {
  name: "conv"
  type: "Convolution"
  bottom: "data"
  top: "conv"
  convolution_param {
    num_output: 16
    kernel_size: 5
    stride: 1
    weight_filler {
      type: "gaussian"
      std: 0.01
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}

 

将图片数据加载到blobs,但反过来,我们也可以反过来从blob中提取出原始数据,并进行显示。

显示的时候要注意各维的顺序,如blobs的顺序是(1,3,360,480),从前往后分别表示1张图片,3三个通道,

图片大小为360×480,需要调用transpose改变为(360,480,3)才能正常显示。

其中用data[0]表示第一张图片,下标从0开始,此例只有一张图片,因此只能是data[0].

分别用data[0,0],data[0,1]和data[0,2]表示该图片的三个通道。

In [3]:

net = caffe.Net('examples/net_surgery/conv.prototxt', caffe.TEST)
im_input=im[np.newaxis,:,:,:].transpose(0,3,1,2)
print "data-blobs:",im_input.shape
net.blobs['data'].reshape(*im_input.shape)
net.blobs['data'].data[...] = im_input
plt.imshow(net.blobs['data'].data[0].transpose(1,2,0))
plt.axis('off')

 

data-blobs: (1, 3, 360, 480)

Out[3]:

(-0.5, 479.5, 359.5, -0.5)

 
Caffe 深度学习入门教程

 

编写一个show_data函数来显示数据

In [4]:

plt.rcParams['image.cmap'] = 'gray'

def show_data(data,head,padsize=1, padval=0):
    data -= data.min()
    data /= data.max()
    
    # 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, padsize), (0, padsize)) + ((0, 0),) * (data.ndim - 3)
    data = np.pad(data, padding, mode='constant', constant_values=(padval, padval))
    
    # tile the filters into an image
    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.figure()
    plt.title(head)
    plt.imshow(data)
    plt.axis('off')

 

从blobs数据中将原始图片提取出来,并分别显示不同的通道图

In [5]:

print "data-blobs:",net.blobs['data'].data.shape
show_data(net.blobs['data'].data[0],'origin images')

 

data-blobs: (1, 3, 360, 480)

 
Caffe 深度学习入门教程

 

调用forward()执行卷积操作,blobs数据发生改变。由原来的(1,3,360,480)变为(1,16,356,476)。

并初始化生成了相应的权值,权值数据为(16,3,5,5)。

最后调用两次show_data来分别显示权值和卷积过滤后的16通道图片。

In [6]:

net.forward()
print "data-blobs:",net.blobs['data'].data.shape
print "conv-blobs:",net.blobs['conv'].data.shape
print "weight-blobs:",net.params['conv'][0span>].data.shape
show_data(net.params['conv'][0].data[:,0],'conv weights(filter)')
show_data(net.blobs['conv'].data[0],'post-conv images')

 

data-blobs: (1, 3, 360, 480)
conv-blobs: (1, 16, 356, 476)
weight-blobs: (16, 3, 5, 5)

 
Caffe 深度学习入门教程

 
Caffe 深度学习入门教程

In [ ]:
 

计算图片数据的均值

图片减去均值后,再进行训练和测试,会提高速度和精度。因此,一般在各种模型中都会有这个操作。

那么这个均值怎么来的呢,实际上就是计算所有训练样本的平均值,计算出来后,保存为一个均值文件,在以后的测试中,就可以直接使用这个均值来相减,而不需要对测试图片重新计算。

一、二进制格式的均值计算

caffe中使用的均值数据格式是binaryproto, 作者为我们提供了一个计算均值的文件compute_image_mean.cpp,放在caffe根目录下的tools文件夹里面。编译后的可执行体放在 build/tools/ 下面,我们直接调用就可以了

# sudo build/tools/compute_image_mean examples/mnist/mnist_train_lmdb examples/mnist/mean.binaryproto

带两个参数:

第一个参数:examples/mnist/mnist_train_lmdb, 表示需要计算均值的数据,格式为lmdb的训练数据。

第二个参数:examples/mnist/mean.binaryproto, 计算出来的结果保存文件。

二、Python格式的均值计算

如果我们要使用python接口,或者我们要进行特征可视化,可能就要用到python格式的均值文件了。首先,我们用lmdb格式的数据,计算出二进制格式的均值,然后,再转换成python格式的均值。

我们可以编写一个python脚本来实现:

#!/usr/bin/env python
import numpy as np
import sys,caffe

if len(sys.argv)!=3:
    print "Usage: python convert_mean.py mean.binaryproto mean.npy"
    sys.exit()

blob = caffe.proto.caffe_pb2.BlobProto()
bin_mean = open( sys.argv[1] , 'rb' ).read()
blob.ParseFromString(bin_mean)
arr = np.array( caffe.io.blobproto_to_array(blob) )
npy_mean = arr[0]
np.save( sys.argv[2] , npy_mean )

 将这个脚本保存为convert_mean.py

调用格式为:

# sudo python convert_mean.py mean.binaryproto mean.npy

其中的 mean.binaryproto 就是经过前面步骤计算出来的二进制均值。

mean.npy就是我们需要的python格式的均值。

caffemodel可视化

通过前面的学习,我们已经能够正常训练各种数据了。设置好solver.prototxt后,我们可以把训练好的模型保存起来,如lenet_iter_10000.caffemodel。 训练多少次就自动保存一下,这个是通过snapshot进行设置的,保存文件的路径及文件名前缀是由snapshot_prefix来设定的。这个文件里面存放的就是各层的参数,即net.params,里面没有数据(net.blobs)。顺带还生成了一个相应的solverstate文件,这个和caffemodel差不多,但它多了一些数据,如模型名称、当前迭代次数等。两者的功能不一样,训练完后保存起来的caffemodel,是在测试阶段用来分类的,而solverstate是用来恢复训练的,防止意外终止而保存的快照(有点像断点续传的感觉)。

既然我们知道了caffemodel里面保存的就是模型各层的参数,因此我们可以把这些参数提取出来,进行可视化,看一看究竟长什么样。

 

我们先训练cifar10数据(mnist也可以),迭代10000次,然后将训练好的 model保存起来,名称为my_iter_10000.caffemodel,然后使用jupyter notebook 来进行可视化。

在此顺便问一下各位大牛:如何在cnblogs中,发表jupyter notebook生成的文章?

       首先,导入必要的库

In [1]:

import numpy as np
import matplotlib.pyplot as plt
import os,sys,caffe
%matplotlib inline

In [2]:

caffe_root='/home/lee/caffe/'
os.chdir(caffe_root)
sys.path.insert(0,caffe_root+'Python')

In [3]:

plt.rcParams['figure.figsize'] = (8, 8)
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'

       设置网络模型,并显示该模型中各层名称和参数的规模(注意此处是net.params, 而不是net.blobs)

In [4]:

net = caffe.Net(caffe_root + 'examples/cifar10/cifar10_full.prototxt',
                caffe_root + 'examples/cifar10/my_iter_10000.caffemodel',
                caffe.TEST)
[(k, v[0].data.shape) for k, v in net.params.items()]

Out[4]:

[('conv1', (32, 3, 5, 5)),
 ('conv2', (32, 32, 5, 5)),
 ('conv3', (64, 32, 5, 5)),
 ('ip1', (10, 1024))]

         cifar10训练的模型配置在文件cifar10_full.prototxt里面,共有三个卷积层和一个全连接层,参数规模如上所示。

In [5]:

#编写一个函数,用于显示各层的参数
def
show_feature(data, padsize=1, padval=0): data -= data.min() data /= data.max() # 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, padsize), (0, padsize)) + ((0, 0),) * (data.ndim - 3) data = np.pad(data, padding, mode='constant', constant_values=(padval, padval)) # tile the filters into an image 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')

In [6]:

# 第一个卷积层,参数规模为(32,3,5,5),即32个5*5的3通道filter
weight = net.params["conv1"][0].data
print weight.shape
show_feature(weight.transpose(0, 2, 3, 1))

 

(32, 3, 5, 5)
参数有两种类型:权值参数和偏置项。分别用params["conv1"][0] 和params["conv1"][1] 表示 。
我们只显示权值参数,因此用params["conv1"][0] 

 
Caffe 深度学习入门教程
 

In [7]:

# 第二个卷积层的权值参数,共有32*32个filter,每个filter大小为5*5
weight = net.params["conv2"][0].data
print weight.shape
show_feature(weight.reshape(32**2, 5, 5))

 

(32, 32, 5, 5)

 
Caffe 深度学习入门教程

In [8]:

# 第三个卷积层的权值,共有64*32个filter,每个filter大小为5*5,取其前1024个进行可视化
 weight = net.params["conv3"][0].data print weight.shape show_feature(weight.reshape(64*32, 5, 5)[:1024])

 

(64, 32, 5, 5)

 
Caffe 深度学习入门教程

模型各层数据和参数可视化

先用caffe对cifar10进行训练,将训练的结果模型进行保存,得到一个caffemodel,然后从测试图片中选出一张进行测试,并进行可视化。

In [1]:

#加载必要的库
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import sys,os,caffe

In [2]:

#设置当前目录,判断模型是否训练好
caffe_root = '/home/bnu/caffe/' 
sys.path.insert(0, caffe_root + 'Python')
os.chdir(caffe_root)
if not os.path.isfile(caffe_root + 'examples/cifar10/cifar10_quick_iter_4000.caffemodel'):
    print("caffemodel is not exist...")

In [3]:

#利用提前训练好的模型,设置测试网络
caffe.set_mode_gpu()
net = caffe.Net(caffe_root + 'examples/cifar10/cifar10_quick.prototxt',
                caffe_root + 'examples/cifar10/cifar10_quick_iter_4000.caffemodel',
                caffe.TEST)

In [4]:

net.blobs['data'].data.shape

Out[4]:

(1, 3, 32, 32)

In [5]:

#加载测试图片,并显示
im = caffe.io.load_image('examples/images/32.jpg')
print im.shape
plt.imshow(im)
plt.axis('off')

 

(32, 32, 3)

Out[5]:

(-0.5, 31.5, 31.5, -0.5)

 Caffe 深度学习入门教程
 

In [6]:

# 编写一个函数,将二进制的均值转换为python的均值
def convert_mean(binMean,npyMean):
    blob = caffe.proto.caffe_pb2.BlobProto()
    bin_mean = open(binMean, 'rb' ).read()
    blob.ParseFromString(bin_mean)
    arr = np.array( caffe.io.blobproto_to_array(blob) )
    npy_mean = arr[0]
    np.save(npyMean, npy_mean )
binMean=caffe_root+'examples/cifar10/mean.binaryproto'
npyMean=caffe_root+'examples/cifar10/mean.npy'
convert_mean(binMean,npyMean)

In [7]:

#将图片载入blob中,并减去均值
transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})
transformer.set_transpose('data', (2,0,1))
transformer.set_mean('data', np.load(npyMean).mean(1).mean(1)) # 减去均值
transformer.set_raw_scale('data', 255)  
transformer.set_channel_swap('data', (2,1,0))
net.blobs['data'].data[...] = transformer.preprocess('data',im)
inputData=net.blobs['data'].data

In [8]:

#显示减去均值前后的数据
plt.figure()
plt.subplot(1,2,1),plt.title("origin")
plt.imshow(im)
plt.axis('off')
plt.subplot(1,2,2),plt.title("subtract mean")
plt.imshow(transformer.deprocess('data', inputData[0]))
plt.axis('off')

Out[8]:

(-0.5, 31.5, 31.5, -0.5)

 
Caffe 深度学习入门教程

In [9]:

#运行测试模型,并显示各层数据信息
net.forward()
[(k, v.data.shape) for k, v in net.blobs.items()]

Out[9]:

[('data', (1, 3, 32, 32)),
 ('conv1', (1, 32, 32, 32)),
 ('pool1', (1, 32, 16, 16)),
 ('conv2', (1, 32, 16, 16)),
 ('pool2', (1, 32, 8, 8)),
 ('conv3', (1, 64, 8, 8)),
 ('pool3', (1, 64, 4, 4)),
 ('ip1', (1, 64)),
 ('ip2', (1, 10)),
 ('prob', (1, 10))]

In [10]:

#显示各层的参数信息
[(k, v[0].data.shape) for k, v in net.params.items()]

Out[10]:

[('conv1', (32, 3, 5, 5)),
 ('conv2', (32, 32, 5, 5)),
 ('conv3', (64, 32, 5, 5)),
 ('ip1', (64, 1024)),
 ('ip2', (10, 64))]

In [11]:

# 编写一个函数,用于显示各层数据
def show_data(data, padsize=1, padval=0):
    data -= data.min()
    data /= data.max()
    
    # 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, padsize), (0, padsize)) + ((0, 0),) * (data.ndim - 3)
    data = np.pad(data, padding, mode='constant', constant_values=(padval, padval))
    
    # tile the filters into an image
    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.figure()
    plt.imshow(data,cmap='gray')
    plt.axis('off')
plt.rcParams['figure.figsize'] = (8, 8)
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'

In [12]:

#显示第一个卷积层的输出数据和权值(filter)
show_data(net.blobs['conv1'].data[0])
print net.blobs['conv1'].data.shape
show_data(net.params['conv1'][0].data.reshape(32*3,5,5))
print net.params['conv1'][0].data.shape

 

(1, 32, 32, 32)
(32, 3, 5, 5)

 
Caffe 深度学习入门教程

 
Caffe 深度学习入门教程

In [13]:

#显示第一次pooling后的输出数据
show_data(net.blobs['pool1'].data[0])
net.blobs['pool1'].data.shape

Out[13]:

(1, 32, 16, 16)

 
Caffe 深度学习入门教程

In [14]:

#显示第二次卷积后的输出数据以及相应的权值(filter)
show_data(net.blobs['conv2'].data[0],padval=0.5)
print net.blobs['conv2'].data.shape
show_data(net.params['conv2'][0].data.reshape(32**2,5,5))
print net.params['conv2'][0].data.shape

 

(1, 32, 16, 16)
(32, 32, 5, 5)

 
Caffe 深度学习入门教程

 
Caffe 深度学习入门教程

In [15]:

#显示第三次卷积后的输出数据以及相应的权值(filter),取前1024个进行显示
show_data(net.blobs['conv3'].data[0],padval=0.5)
print net.blobs['conv3'].data.shape
show_data(net.params['conv3'][0].data.reshape(64*32,5,5)[:1024])
print net.params['conv3'][0].data.shape

 

(1, 64, 8, 8)
(64, 32, 5, 5)

 
Caffe 深度学习入门教程

 
Caffe 深度学习入门教程

In [16]:

#显示第三次池化后的输出数据
show_data(net.blobs['pool3'].data[0],padval=0.2)
print net.blobs['pool3'].data.shape

 

(1, 64, 4, 4)

 
Caffe 深度学习入门教程

In [17]:

# 最后一层输入属于某个类的概率
feat = net.blobs['prob'].data[0]
print feat
plt.plot(feat.flat)

 

[  5.21440245e-03   1.58397834e-05   3.71246301e-02   2.28459597e-01
   1.08315737e-03   7.17785358e-01   1.91939052e-03   7.67927198e-03
   6.13298907e-04   1.05107691e-04]

Out[17]:

[<matplotlib.lines.Line2D at 0x7f3d882b00d0>]

 
Caffe 深度学习入门教程

 

从输入的结果和图示来看,最大的概率是7.17785358e-01,属于第5类(标号从0开始)。与cifar10中的10种类型名称进行对比:

airplane、automobile、bird、cat、deer、dog、frog、horse、ship、truck

根据测试结果,判断为dog。 测试无误!

caffe图形化操作工具digits运行实例

经过前面的操作,我们就把数据准备好了。

一、训练一个model

右击右边Models模块的” Images” 按钮 ,选择“classification”

在打开页面右下角可以看到,系统提供了一个caffe model,分别为LeNet, AlexNet, GoogLeNet, 如果使用这三个模型,则所有参数都已经设置好了,就不用再设置了。

在下面,系统为我们列举出了本机所带的显卡,我们可以选择其中一块进行运行。

在最下面,输入一个model name, 就可以点击create 按钮了。如果有些选项不对,会有错误提示,很人性化。

在训练过程页面,左上角显示了生成的配置文件名称 (放在job目录文件下,默认路径为:/usr/share/digits/digits/jobs/),运行过程中保存的caffemodel快照也保存在这个目录下面。

页面中间显示了训练和测试的数据信息,右面显示了训练所用的时间和gpu使用情况,下面就是一些实时化图表,可以看到训练阶段的loss, 测试阶段的loss和accuracy,相当方便,甚至还可以看到学习率的变化情况,吃惊吧!

 

模型训练好后,直接就可以在下面进行测试了。

二、测试新来的图片

将页面拖到最下面,选择Upload imager按钮,加载一幅测试图片。在 /home/username/mnist/test/ 下面有大量的测试图片,随便选一张就可以了。

也可以通过在Image URL方框里,输入一张网上的图片地址来进行测试。

加载好测试图片,在 Show visualizations and statistics 选择模式框上点上勾。

点击”Classify One” 按钮就可以开始测试了。

如果你不是对一张图片进行测试,而是一个测试集,则是在” Upload Image List”这个地方,选择测试图片的列表清单文件(如 val.txt)

系统会弹出一个新的页面,显示top-5的分类情况 ,同时digits还提供了测试数据与权值的可视化和统计信息。

 

最后一句话总结,nvidia digits, 谁用谁知道!

绘制网络模型

Python/draw_net.py, 这个文件,就是用来绘制网络模型的。也就是将网络模型由prototxt变成一张图片。

在绘制之前,需要先安装两个库

1、安装GraphViz

# sudo apt-get install GraphViz

注意,这里用的是apt-get来安装,而不是pip.

2 、安装pydot

# sudo pip install pydot

用的是pip来安装,而不是apt-get

安装好了,就可以调用脚本来绘制图片了

draw_net.py执行的时候带三个参数

第一个参数:网络模型的prototxt文件

第二个参数:保存的图片路径及名字

第二个参数:–rankdir=x , x 有四种选项,分别是LR, RL, TB, BT 。用来表示网络的方向,分别是从左到右,从右到左,从上到小,从下到上。默认为LR。

例:绘制Lenet模型

# sudo python python/draw_net.py examples/mnist/lenet_train_test.prototxt netImage/lenet.png --rankdir=BT

例:绘制cifar10的模型

#  sudo python python/draw_net.py examples/cifar10/cifar10_full_train_test.prototxt netImage/cifar10.png --rankdir=BT

绘制loss和accuracy曲线

如同前几篇的可视化,这里采用的也是jupyter notebook来进行曲线绘制。

In [1]:

#加载必要的库
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import sys,os,caffe
#设置当前目录
caffe_root = '/home/bnu/caffe/' 
sys.path.insert(0, caffe_root + 'Python')
os.chdir(caffe_root)

         设置求解器,和c++/caffe一样,需要一个solver配置文件。

In [2]:

# set the solver prototxt
caffe.set_device(0)
caffe.set_mode_gpu()
solver = caffe.SGDSolver('examples/cifar10/cifar10_quick_solver.prototxt')

       如果不需要绘制曲线,只需要训练出一个caffemodel, 直接调用solver.solve()就可以了。如果要绘制曲线,就需要把迭代过程中的值
保存下来,因此不能直接调用solver.solve(), 需要迭代。在迭代过程中,每迭代200次测试一次

In [5]:

%%time
niter =4000
test_interval = 200
train_loss = np.zeros(niter)
test_acc = np.zeros(int(np.ceil(niter / test_interval)))

# the main solver loop
for it in range(niter):
    solver.step(1)  # SGD by Caffe
    
    # store the train loss
    train_loss[it] = solver.net.blobs['loss'].data
    solver.test_nets[0].forward(start='conv1')
    
    if it % test_interval == 0:
        acc=solver.test_nets[0].blobs['accuracy'].data
        print 'Iteration', it, 'testing...','accuracy:',acc
        test_acc[it // test_interval] = acc

 

Iteration 0 testing... accuracy: 0.10000000149
Iteration 200 testing... accuracy: 0.419999986887
Iteration 400 testing... accuracy: 0.479999989271
Iteration 600 testing... accuracy: 0.540000021458
Iteration 800 testing... accuracy: 0.620000004768
Iteration 1000 testing... accuracy: 0.629999995232
Iteration 1200 testing... accuracy: 0.649999976158
Iteration 1400 testing... accuracy: 0.660000026226
Iteration 1600 testing... accuracy: 0.660000026226
Iteration 1800 testing... accuracy: 0.670000016689
Iteration 2000 testing... accuracy: 0.709999978542
Iteration 2200 testing... accuracy: 0.699999988079
Iteration 2400 testing... accuracy: 0.75
Iteration 2600 testing... accuracy: 0.740000009537
Iteration 2800 testing... accuracy: 0.769999980927
Iteration 3000 testing... accuracy: 0.75
Iteration 3200 testing... accuracy: 0.699999988079
Iteration 3400 testing... accuracy: 0.740000009537
Iteration 3600 testing... accuracy: 0.72000002861
Iteration 3800 testing... accuracy: 0.769999980927
CPU times: user 41.7 s, sys: 54.2 s, total: 1min 35s
Wall time: 1min 18s

       绘制train过程中的loss曲线,和测试过程中的accuracy曲线。

In [6]:

print test_acc
_, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax1.plot(np.arange(niter), train_loss)
ax2.plot(test_interval * np.arange(len(test_acc)), test_acc, 'r')
ax1.set_xlabel('iteration')
ax1.set_ylabel('train loss')
ax2.set_ylabel('test accuracy')

 

[ 0.1         0.41999999  0.47999999  0.54000002  0.62        0.63
  0.64999998  0.66000003  0.66000003  0.67000002  0.70999998  0.69999999
  0.75        0.74000001  0.76999998  0.75        0.69999999  0.74000001
  0.72000003  0.76999998]

Out[6]:

<matplotlib.text.Text at 0x7fd1297bfcd0>

 Caffe 深度学习入门教程
 

用训练好的caffemodel来进行分类

caffe程序自带有一张小猫图片,存放路径为caffe根目录下的 examples/images/cat.jpg, 如果我们想用一个训练好的caffemodel来对这张图片进行分类,那该怎么办呢? 如果不用这张小猫图片,换一张别的图片,又该怎么办呢?如果学会了小猫图片的分类,那么换成其它图片,程序实际上是一样的。

开发caffe的贾大牛团队,利用imagenet图片和caffenet模型训练好了一个caffemodel,  供大家下载。要进行图片的分类,这个caffemodel是最好不过的了。所以,不管是用c++来进行分类,还是用Python接口来分类,我们都应该准备这样三个文件:

1、caffemodel文件。 

  可以直接在浏览器里输入地址下载,也可以运行脚本文件下载。下载地址为:http://dl.caffe.berkeleyvision.org/bvlc_reference_caffenet.caffemodel

文件名称为:bvlc_reference_caffenet.caffemodel,文件大小为230M左右,为了代码的统一,将这个caffemodel文件下载到caffe根目录下的 models/bvlc_reference_caffenet/ 文件夹下面。也可以运行脚本文件进行下载:

# sudo ./scripts/download_model_binary.py models/bvlc_reference_caffenet

2、均值文件。

有了caffemodel文件,就需要对应的均值文件,在测试阶段,需要把测试数据减去均值。这个文件我们用脚本来下载,在caffe根目录下执行:

# sudo sh ./data/ilsvrc12/get_ilsvrc_aux.sh

执行并下载后,均值文件放在 data/ilsvrc12/ 文件夹里。

3、synset_words.txt文件

在调用脚本文件下载均值的时候,这个文件也一并下载好了。里面放的是1000个类的名称。

数据准备好了,我们就可以开始分类了,我们给大家提供两个版本的分类方法:

一、c++方法

在caffe根目录下的 examples/cpp-classification/ 文件夹下面,有个classification.cpp文件,就是用来分类的。当然编译后,放在/build/examples/cpp_classification/ 下面

我们就直接运行命令:

# sudo ./build/examples/cpp_classification/classification.bin \
  models/bvlc_reference_caffenet/deploy.prototxt   models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel   data/ilsvrc12/imagenet_mean.binaryproto   data/ilsvrc12/synset_words.txt   examples/images/cat.jpg

命令很长,用了很多的\符号来换行。可以看出,从第二行开始就是参数,每行一个,共需要4个参数

运行成功后,输出top-5结果:

---------- Prediction for examples/images/cat.jpg ----------
0.3134 - "n02123045 tabby, tabby cat"
0.2380 - "n02123159 tiger cat"
0.1235 - "n02124075 Egyptian cat"
0.1003 - "n02119022 red fox, Vulpes vulpes"
0.0715 - "n02127052 lynx, catamount"

即有0.3134的概率为tabby cat, 有0.2380的概率为tiger cat ……

二、python方法

python接口可以使用jupyter notebook来进行可视化操作,因此推荐使用这种方法。

在这里我就不用可视化了,编写一个py文件,命名为py-classify.py

#coding=utf-8
#加载必要的库
import numpy as np

import sys,os #设置当前目录 caffe_root = '/home/xxx/caffe/' sys.path.insert(0, caffe_root + 'python') import caffe os.chdir(caffe_root) net_file=caffe_root + 'models/bvlc_reference_caffenet/deploy.prototxt' caffe_model=caffe_root + 'models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel' mean_file=caffe_root + 'python/caffe/imagenet/ilsvrc_2012_mean.npy' net = caffe.Net(net_file,caffe_model,caffe.TEST) transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape}) transformer.set_transpose('data', (2,0,1)) transformer.set_mean('data', np.load(mean_file).mean(1).mean(1)) transformer.set_raw_scale('data', 255) transformer.set_channel_swap('data', (2,1,0)) im=caffe.io.load_image(caffe_root+'examples/images/cat.jpg') net.blobs['data'].data[...] = transformer.preprocess('data',im) out = net.forward() imagenet_labels_filename = caffe_root + 'data/ilsvrc12/synset_words.txt' labels = np.loadtxt(imagenet_labels_filename, str, delimiter='\t') top_k = net.blobs['prob'].data[0].flatten().argsort()[-1:-6:-1] for i in np.arange(top_k.size): print top_k[i], labels[top_k[i]]

执行这个文件,输出:

281 n02123045 tabby, tabby cat
282 n02123159 tiger cat
285 n02124075 Egyptian cat
277 n02119022 red fox, Vulpes vulpes
287 n02127052 lynx, catamount

 caffe开发团队实际上也编写了一个python版本的分类文件,路径为 python/classify.py

运行这个文件必需两个参数,一个输入图片文件,一个输出结果文件。而且运行必须在python目录下。假设当前目录是caffe根目录,则运行:

# cd python
# sudo python classify.py ../examples/images/cat.jpg result.npy

分类的结果保存为当前目录下的result.npy文件里面,是看不见的。而且这个文件有错误,运行的时候,会提示

Mean shape incompatible with input shape

的错误。因此,要使用这个文件,我们还得进行修改:

1、修改均值计算:

定位到 

mean = np.load(args.mean_file)

 这一行,在下面加上一行:

mean=mean.mean(1).mean(1)

 则可以解决报错的问题。

2、修改文件,使得结果显示在命令行下:

定位到

# Classify.
    start = time.time()
    predictions = classifier.predict(inputs, not args.center_only)
    print("Done in %.2f s." % (time.time() - start))

这个地方,在后面加上几行,如下所示:

  # Classify.
    start = time.time()
    predictions = classifier.predict(inputs, not args.center_only)
    print("Done in %.2f s." % (time.time() - start))
    imagenet_labels_filename = '../data/ilsvrc12/synset_words.txt'
    labels = np.loadtxt(imagenet_labels_filename, str, delimiter='\t')
    top_k = predictions.flatten().argsort()[-1:-6:-1]
    for i in np.arange(top_k.size):
        print top_k[i], labels[top_k[i]]

 就样就可以了。运行不会报错,而且结果会显示在命令行下面。

caffe图形化操作工具digits的安装与运行

经过前面一系列的学习,我们基本上学会了如何在linux下运行caffe程序,也学会了如何用Python接口进行数据及参数的可视化。

也许有人会觉得比较复杂。确实,对于一个使用惯了windows视窗操作的用户来说,各种命令就要了人命,甚至会非常抵触命令操作。没有学过python,要自己去用python编程实现可视化,也是非常头痛的事情。幸好现在有了nvidia digits这款工具,这些问题都可以解决了。

nvidia为了卖出更多的显卡,对深度学习的偏爱真是亮瞎了狗眼。除了cudnn, 又出了digits,真是希望小学生也能学会深度学习,然后去买他们的卡。

nvidia digits是一款web应用工具,在网页上对caffe进行图形化操作和可视化,用于caffe初学者来说,帮助非常大。

不过有点遗憾的是,据nvidia官方文档称,digits最佳支持系统是Ubuntu 14.04,其它的系统效果如何,就不得而知了。

一、安装digits 3.0

digits是运行在cuda和caffe基础上的,所以要先配置好cuda+caffe那是毫无疑问的了。还不会配置的,请参考:Caffe 深度学习入门教程(1):安装配置Ubuntu14.04+CUDA7.5+Caffe+cuDNN  http://www.linuxidc.com/Linux/2016-11/136774.htm

打开一个终端,依次运行下列命令:

cd

sudo -s

进入当前用户根目录,并切换到超级用户(符号由$变成#,不用每句都输sudo)

CUDA_REPO_PKG=cuda-repo-ubuntu1404_7.5-18_amd64.deb &&
wget http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1404/x86_64/$CUDA_REPO_PKG &&
sudo dpkg -i $CUDA_REPO_PKG

接着,

ML_REPO_PKG=nvidia-machine-learning-repo_4.0-2_amd64.deb &&
wget http://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1404/x86_64/$ML_REPO_PKG &&
sudo dpkg -i $ML_REPO_PKG
apt-get update
apt-get install digits

ok,保持网络通畅,慢慢安装吧!

二、运行digits

默认情况下,digits的安装目录为 /usr/share/digits

安装完成后,打开浏览器,地址栏输入 http://localhost/ 就可以了,就是这么简单。

更强悍的是:在局域内的其它机子上,也可以用浏览器访问,只是localhost变成了主机 ip地址。很多人喜欢在windows系统上远程连接linux来执行caffe。现在好了,不需要远程连接了,只需要访问一个网站就可以了。。。还有谁!!!!

三、运行mnist实例

现在来运行一个实例:mnist(名符其实的helloworld)

原始数据需要的是图片,但网上提供的mnist数据并不是图片格式的数据,因此我们需要将它转换成图片才能运行。

digits提供了一个脚本文件,用于下载mnist, cifar10 和cifar100 三类数据,并转换成png格式图片。文件路径为:

/usr/share/digits/tools/download_data/main.py

我们先在当前用户的根目录下,新建一个mnist文件夹用来保存mnist图片。

# cd 
# mkdir mnist

然后执行脚本

# /usr/share/digits/tools/download_data/main.py mnist ~/mnist

main.py带两个参数,第一个为数据集名称(可设置为mnist, cifar10或cifar100),第二个为输出路径(~/mnist)

执行成功后,会在mnist文件夹下,生成两个文件夹(train文件夹和test文件夹),每个文件夹下面就是我们需要的图片(10类分别放在10个子文件夹内),同时还生成了对应在图片列表文件train.txt和test.txt

接下来,在浏览器上运行digits, 点击左边Dataset模块的”Image”按钮选择“classification”, 创建一个dataset

 

在这个页面的左边,可以设置图片是彩色图片还是灰度图片,如果提供的原始图片大小不一致,还可用Resize Transformation功能转换成一致大小 。从页面中间可以看出,系统默认将训练图片中的25%取出来作为验证集(for validation)。

如果想把用来测试的图片,也生成lmdb, 则把“ separate test image folder” 这个选项选上。

全部设置好后,点击”create” 按钮,开始生成lmdb数据。

 

 

注意左上角的Job Directory(工作目录),生成的lmdb文件就放在这个目录下面,大家最好打开这个目录去看看,看一下生成了些什么文件,了解一下运行原理。

在这个界面,我们还可以可视化查看训练和测试的图片,如下图:

train.txt里面存放的是所有训练图片的列表清单,柱状图清晰地显示了10类样本各自的数量。点击” Explorer the db” 即可查看图片。

最后,点击最左上角“ DIGITS” 链接回到网站根目录。

 

由于图片太多,因此本文很长,所以在此截断一下,后续。。

赞(0) 打赏
转载请注明出处:服务器评测 » Caffe 深度学习入门教程
分享到: 更多 (0)

听说打赏我的人,都进福布斯排行榜啦!

支付宝扫一扫打赏

微信扫一扫打赏