借助针对英特尔® 架构优化的 Caffe* 管理深度学习网络

总结

Caffe* 是伯克利愿景和学习中心 (BVLC) 开发的一款深度学习框架。 该框架使用 C++ 和 CUDA* C++ 语言编写,并采用 Python* 和 MATLAB* 封装程序。非常适用于卷积神经网络、循环神经网络和多层感知器。主 Caffe 分支有多个不同的分解,支持检测和分类分割以及兼容的 Spark 等。

面向英特尔® 架构优化的 Caffe 目前集成了最新版英特尔® 数学核心函数库(英特尔® MKL)2017,专门面向高级矢量扩展指令集 (AVX)-2 和 AVX-512 指令优化,英特尔® 至强® 处理器和英特尔® 至强融核™ 处理器等支持这些指令集。也就是说,面向英特尔® 架构优化的 Caffe 包含 BVLC Caffe 中的所有优势,能够在英特尔架构上高效运行,并且可用于跨不同节点的分布式训练。 本教程将介绍如何构建面向英特尔架构优化的 Caffe,使用一个或多个计算节点训练深度网络模型,以及如何部署网络。此外,本文还详细介绍了 Caffe 的各种功能,包括如何优化、提取和查看不同模型的特性,以及如何使用 Caffe Python API。

词汇使用:

  • 权重 — 也称作内核、过滤器、模板或特性提取器
  • blob - 也称作张量 - 一种 N 维数据结构,也就是说,一个包含数据、梯度或权重(包括偏差)的 N-D 张量
  • 单元 - 也称作神经元 - 在数据 blob 上执行非线性转换
  • 特性图 — 也称作渠道
  • 测试 — 也称作推理、分类、得分或部署
  • 模型 — 也称作拓扑或架构

快速熟悉 Caffe 的方法是:

请注意,本文内容基于博客的一部分内容。

安装

以下命令适用于 Ubuntu* 14.04。针对其他 Linux* 或 OS *X 操作系统或 Ubuntu 版本的类似命令,请参见 BVLC Caffe 安装网站。获得关联组件:(请注意,当您将鼠标放在代码上时,屏幕上会出现三个图标。点击 "view source" 图标查看没有行号的代码。)

sudo apt-get update &&
sudo apt-get -y install build-essential git cmake &&
sudo apt-get -y install libprotobuf-dev libleveldb-dev libsnappy-dev &&
sudo apt-get -y install libopencv-dev libhdf5-serial-dev protobuf-compiler &&
sudo apt-get -y install --no-install-recommends libboost-all-dev &&
sudo apt-get -y install libgflags-dev libgoogle-glog-dev liblmdb-dev &&
sudo apt-get -y install libatlas-base-dev

对于 Ubuntu 16.04,链接以下库:

find .-type f -exec sed -i -e 's^"hdf5.h"^"hdf5/serial/hdf5.h"^g' -e 's^"hdf5_hl.h"^"hdf5/serial/hdf5_hl.h"^g' '{}' ;
cd /usr/lib/x86_64-linux-gnu
sudo ln -s libhdf5_serial.so.10.1.0 libhdf5.so
sudo ln -s libhdf5_serial_hl.so.10.0.2 libhdf5_hl.so

在 CentOS* 7 上安装关联组件,如下所示:

sudo yum -y update &&
sudo yum -y groupinstall "Development Tools" &&
sudo yum -y install wget cmake git &&
sudo yum -y install protobuf-devel protobuf-compiler boost-devel &&
sudo yum -y install snappy-devel opencv-devel atlas-devel &&
sudo yum -y install gflags-devel glog-devel lmdb-devel leveldb-devel hdf5-devel

# The following steps are only required if some packages failed to install
# add EPEL repository then install missing packages
wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
sudo rpm -ivh epel-release-latest-7.noarch.rpm
sudo yum -y install gflags-devel glog-devel lmdb-devel leveldb-devel hdf5-devel &&
sudo yum -y install protobuf-devel protobuf-compiler boost-devel

# if packages are still not found--download and install/build the packages, e.g.,
# snappy:
wget http://mirror.centos.org/centos/7/os/x86_64/Packages/snappy-devel-1.1.0-3.el7.x86_64.rpm
sudo yum -y install http://mirror.centos.org/centos/7/os/x86_64/Packages/snappy-devel-1.1.0-3.el7.x86_64.rpm
# atlas:
wget http://mirror.centos.org/centos/7/os/x86_64/Packages/atlas-devel-3.10.1-10.el7.x86_64.rpm
sudo yum -y install http://mirror.centos.org/centos/7/os/x86_64/Packages/atlas-devel-3.10.1-10.el7.x86_64.rpm
# opencv:
wget https://github.com/Itseez/opencv/archive/2.4.13.zip
unzip 2.4.13.zip
cd opencv-2.4.13/
mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr/local ..
NUM_THREADS=$(($(grep 'core id' /proc/cpuinfo | sort -u | wc -l)*2))
make all -j $NUM_THREADS
sudo make install -j $NUM_THREADS

# optional (not required for Caffe)
# other useful repositories for CentOS are RepoForge and IUS:
wget http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.3-1.el7.rf.x86_64.rpm
sudo rpm -Uvh rpmforge-release-0.5.3-1.el7.rf.x86_64.rpm
wget https://rhel7.iuscommunity.org/ius-release.rpm
sudo rpm -Uvh ius-release*.rpm

使用关联组件的原因(来源):

  • boost:一种 C++ 库,用于其数学函数和共享指针
  • gloggflags:提供登录和命令行实用程序。对于调试非常重要
  • leveldblmdb:数据库 IO。用于准备你自己的数据
  • protobuf:用于有效地定义数据结构
  • BLAS(基本线性代数子程序):英特尔® 数学核心函数库(英特尔® MKL)、ATLAS*、openBLAS* 等提供的矩阵乘法、矩阵加法等运算。

Caffe 安装指南 指出:安装“MKL 有助于提高 CPU 性能。”

如欲获得最佳性能,请使用英特尔® 数学核心函数库(英特尔® MKL)2017,作为英特尔® Parallel Studio XE 2017 Beta 中的测试版免费提供。英特尔 MKL 2017 正式版(也称作黄金版)将于 2016 年 9 月推出。

或者,用户也可以下载并安装英特尔 MKL 11.3.3(2016 版)。如要下载此版本,请先注册免费的社区许可,然后按照安装说明进行操作。

完成安装后,请按照下面的说明设置正确的环境库(可能需要修改路径):

echo 'source /opt/intel/bin/compilervars.sh intel64' >> ~/.bashrc
# alternatively edit <mkl_path>/mkl/bin/mklvars.sh replacing INSTALLDIR in
# CPRO_PATH=<INSTALLDIR> with the actual mkl path:CPRO_PATH=<full mkl path>
# echo 'source <mkl path>/mkl/bin/mklvars.sh intel64' >> ~/.bashrc

克隆和准备面向英特尔架构优化的 Caffe,以进行编译:

cd ~
# For BVLC caffe use:
# git clone https://github.com/BVLC/caffe.git
# For intel caffe use:
git clone https://github.com/intel/caffe.git
cd caffe
echo "export CAFFE_ROOT=`pwd`" >> ~/.bashrc
source ~/.bashrc
cp Makefile.config.example Makefile.config
# Open Makefile.config and modify it (see comments in the Makefile)
vi Makefile.config

编辑 Makefile.config:

# To run on CPU only and to avoid installing CUDA installers, uncomment
CPU_ONLY := 1

# To use MKL, replace atlas with mkl as follows
# (make sure that the BLAS_DIR and BLAS_LIB paths are correct)
BLAS := mkl
BLAS_DIR := $(MKLROOT)/include
BLAS_LIB := $(MKLROOT)/lib/intel64

# To use MKL2017 DNN primitives as the default engine, uncomment
# (however leave it commented if using multinode training)
# USE_MKL2017_AS_DEFAULT_ENGINE := 1

# To customized compiler choice, uncomment and set the following
# CUSTOM_CXX := g++

# To train on multinode uncomment and verify path
# USE_MPI := 1
# CXX := /usr/bin/mpicxx

如果使用 Ubuntu 16.04,请编辑 Makefile:

INCLUDE_DIRS := $(PYTHON_INCLUDE) /usr/local/include /usr/include/hdf5/serial/

并创建符号链接:

cd /usr/lib/x86_64-linux-gnu
sudo ln -s libhdf5_serial.so.10.1.0 libhdf5.so
sudo ln -s libhdf5_serial_hl.so.10.0.2 libhdf5_hl.so

如果使用 CentOS 7 和 ATLAS(而不是推荐的 MKL 库),请编辑 Makefile:

# Change this line
LIBRARIES += cblas atlas
# to
LIBRARIES += satlas

构建面向英特尔架构优化的 Caffe:

NUM_THREADS=$(($(grep 'core id' /proc/cpuinfo | sort -u | wc -l)*2))
make -j $NUM_THREADS
# To save the output stream to file makestdout.log use this instead
# make -j $NUM_THREADS 2>&1 | tee makestdout.log

或者,也可以使用 cmake:

mkdir build
cd build
cmake -DCPU_ONLY=on -DBLAS-mkl -DUSE_MKL2017_AS_DEFAULT_ENGINE=on /path/to/caffe
NUM_THREADS=$(($(grep 'core id' /proc/cpuinfo | sort -u | wc -l)*2))
make -j $NUM_THREADS

安装 Python 关联组件:

# These steps are OPTIONAL but highly recommended to use the Python interface
sudo apt-get -y install gfortran python-dev python-pip
cd ~/caffe/python
for req in $(cat requirements.txt); do sudo pip install $req; done
sudo pip install scikit-image #depends on other packages
sudo ln -s /usr/include/python2.7/ /usr/local/include/python2.7
sudo ln -s /usr/local/lib/python2.7/dist-packages/numpy/core/include/numpy/ \
  /usr/local/include/python2.7/numpy
cd ~/caffe
make pycaffe -j NUM_THREADS
echo "export PYTHONPATH=$CAFFE_ROOT/python" >> ~/.bashrc
source ~/.bashrc

其他安装选项:

# These steps are OPTIONAL to test caffe
make test -j $NUM_THREADS
make runtest #"YOU HAVE <some number> DISABLED TESTS" output is OK

# This step is OPTIONAL to disable cam hardware OpenCV driver
# alternatively, the user can skip this and ignore the harmless
# libdc1394 error that may occasionally appears
sudo ln /dev/null /dev/raw1394

数据层

本节为可选内容,讨论了不同的数据类型;即便不了解这些内容,也不影响使用 Caffe。如果您计划使用不同格式的数据,这些内容可能会有用。本节内容基于此教程教程,以及 src/caffe/proto/caffe.proto

数据通过数据层进入 Caffe,并位于网络底部并在 prototxt 文件中定义。训练部分提供了关于 prototxt 文件的更多信息。数据可来自高效的数据库(LevelDB 或 LMDB)、直接来自内存,或者,当不需要太高的效率时,还可以来自磁盘上的文件(采用 HDF5 或通用映像格式)。

指定 transform_params(不受所有数据类型的支持,如 HDF5)可支持通用输入预处理(平均值减法、扩展、随机裁剪和镜像)转换。如果提前执行所需的数据转换,就不必在数据层中使用该选项。通用数据转换可按照以下方式执行:

  transform_param {
    # randomly horizontally mirror the image
    mirror:1
    # crop a `crop_size` x `crop_size` patch:
    # - at random during training
    # - from the center during testing
    crop_size:227
    # substract mean value: these mean_values can equivalently be replaced with a mean.binaryproto file as
    # mean_file: name_of_mean_file.binaryproto
    mean_value:104
    mean_value:117
    mean_value:123
  }

在该示例中,图像被剪裁、镜像并减去平均值。如欲了解其他常用的数据转换,请参阅 message TransformationParameter 下的 src/caffe/proto/caffe.proto

数据

速度极快的内存映射数据库 (LMDB) 和 LevelDB 数据库格式可作为输入数据高效处理。它们仅适用于 1-of-k 分类。鉴于 Caffe 在读取数据集方面的高效性,我们建议 1-of-k 分类采用这些推荐的数据格式。

data_params

必需

  • source:包含数据库的目录的名称
  • batch_size:一次可以处理的输入的数量

可选

  • backend [default LEVELDB]:选择是否使用 LEVELDB 或 LMDB
  • rand_skip:开始的时候跳过此输入数量。对异步 sgd 十分有用

如欲了解其他可用的数据层转换,请参阅 message DataParameter 下的 src/caffe/proto/caffe.proto

layer {
  name:"data"
  type:"Data"
  top:"data"
  top:"label"
  include {
    phase:TRAIN
  }
  transform_param {
    mirror:1
    crop_size:227
    mean_value:104
    mean_value:117
    mean_value:123
  }
  data_param {
    source:"examples/imagenet/ilsvrc12_train_lmdb"
    batch_size:32
    backend:LMDB
  }
}

层以及层中顶部的 blob 具有相同的名称很常见,但是并不是必需的;也就是说,在每层的 prototxt 文件中,nametop 通常是相同的。

或者,也可以减去平均数,具体做法是传递一个平均数映像,并使用一个 mean_file:"data/ilsvrc12/imagenet_mean.binaryproto"替换所有 mean_value 行。该 binaryproto 文件可以从 LMDB 数据集创建,如下所示:

cd ~/caffe
build/tools/compute_image_mean examples/imagenet/ilsvr12_train_lmdb 
data/ilsvrc12/imagenet_mean.binaryproto

分别使用相应的 lmdb 文件夹和所需的 binaryproto 文件来替换 examples/imagenet/ilsvr12_train_lmdbdata/ilsvrc12/imagenet_mean.binaryproto

ImageData

直接从映像文件获得映像和标签。

image_data_params

必需

  • source:包含数据输入和标签路径的文本文件的名称

可选

  • batch_size [默认为 1]:一次可以处理的输入的数量
  • new_height [默认为 0]:重新调整高度为该值;如果设置为 0,将会被忽略
  • new_width [默认为 0]:重新调整宽度为该值;如果设置为 0,将会被忽略
  • shuffle [默认为 0]:移动数据;如果设置为 0,将会被忽略
  • rand_skip [default 0]:开始的时候跳过该输入数量;可能会用于 async sgd

如欲了解其他常用的映像数据转换,请参看 message ImageDataParameter 下的 src/caffe/proto/caffe.proto

在该示例中,图像被移动、剪裁、镜像并减去平均值。

layer {
  name:"data"
  type:"ImageData"
  top:"data"
  top:"label"
  include {
    phase:TRAIN
  }
  transform_param {
    mirror: true
    crop_size:227
    mean_value:104
    mean_value:117
    mean_value:123
  }
  image_data_param {
    source:"/path/to/file/train.txt"
    batch_size:32
    shuffle:1
  }
}

请注意,文本文件具有映像文件名称和相应的标签。例如,"train.txt" 如下所示

/path/to/images/img3423.jpg 2
/path/to/images/img3424.jpg 13
/path/to/images/img3425.jpg 8
...

输入

使用零的 blob 作为输入数据,并指定维度。这通常用于对向前和向后传播进行计时。如欲了解关于网络计时的更多信息,请参考训练章节的结尾。

input_params

必需

  • shape:用于定义顶部 blob 的一个或多个形状
layer {
  name:"input"
  type:"Input"
  top:"data"
  input_param {
    shape {
      dim:32
      dim:3
      dim:227
      dim:227
    }
  }
}

或者,该层可被写为:

input:"data"
input_dim:32
input_dim:3
input_dim:227
input_dim:227

DummyData

除了可以指定数据类型外,与输入相似。通常用于调试,但是也可用于对向前和向后传播进行计时。示例基于

dummy_data_params

必需

  • shape:用于定义顶部 blob 的一个或多个形状

可选

  • data_filler [默认的 ConstantFiller,数值为 0]:指定用于顶部 blob 中的值
layer {
  name:"data"
  type:"DummyData"
  top:"data"
  include {
    phase:TRAIN
  }
  dummy_data_param {
    data_filler {
      type:"constant"
      value:0.01
    }
    shape {
      dim:32
      dim:3
      dim:227
      dim:227
    }
  }
}
layer {
  name:"data"
  type:"DummyData"
  top:"label"
  include {
    phase:TRAIN
  }
  dummy_data_param {
    data_filler {
      type:"constant"
    }
    shape {
      dim:32
    }
  }
}

该示例有两个数据层,因为必须指定为每个顶部 blob 提供的数据,所以每个数据层对应一个顶部。请注意,在 Data、ImageData 或 HDF5Data 数据层中,关于标签顶部 blob 的信息位于源文件中。

MemoryData

内存数据层直接从内存读取数据,而不是复制数据。如欲使用,请调用 MemoryDataLayer::Reset(从 C++)或 Net.set_input_arrays(从 Python),以便指定连续数据源(作为 4D 行主要阵列),一次读取一个批大小的数据块。

该方法可能速度较慢,因为在使用前需要将数据复制到内存中。但是,一旦复制到内存后,便具备极高的效率。

memory_data_param

必需

  • batch_size通道高度宽度:指定从内存中读取的数据块的大小
layers {
  name:"data"
  type:MEMORY_DATA
  top:"data"
  top:"label"
  transform_param {
    crop_size:227
    mirror: true
    mean_file:"mean.binaryproto"
  }
  memory_data_param {
   batch_size:32
   channels:3
   height:227
   width:227
  }

HDF5Data

从 HDF5 文件读取任意数据。适用于仅使用 FP32 和 FP64 数据(非 uint8)的任务,因此映像数据非常大。不允许 transform_param。尽量在需要的时候使用。

hdf5_data_param

必需

  • source:包含数据输入和标签路径的文本文件的名称
  • batch_size

可选

  • shuffle [默认错误]:移动 HDF5 文件
layer {
  name:"data"
  type:"HDF5_DATA"
  top:"data"
  top:"label"
  include {
    phase:TRAIN
  }
  hdf5_data_param {
    source:"examples/hdf5_classification/data/train.txt"
    batch_size:32
  }
}

HDF5DataOutput

HDF5 输出层在本部分中执行其它层的反向函数,并将输入 blob 写入磁盘。

hdf5_output_param

必需

  • file_name
layer {
  name:"data_output"
  type:"HDF5_OUTPUT"
  bottom:"data"
  bottom:"label"
  include {
    phase:TRAIN
  }
  hdf5_output_param {
    file_name:"output_file.h5"
  }
}

WindowData

专门用于检测。从映像文件类标签读取窗口。

window_data_param

必需

  • source:指定数据源
  • mean_file
  • batch_size

可选

  • mirror
  • crop_size:随机裁剪图像。
  • crop_mode [默认 "warp"]:裁剪检测窗口的模式;例如,"warp" 改变固定大小;"square"围绕窗口裁剪最紧密的正方形
  • fg_threshold [默认为 0.5]:前景(对象)重叠阈值
  • bg_threshold [默认为 0.5]:背景(对象)重叠阈值
  • fg_fraction [默认为 0.25]:应作为前景对象的批次的部分
  • context_pad [默认为 10]:围绕窗口的环境填充物的数量

如欲了解其他窗口数据转换,请参看 message WindowDataParameter 下的 src/caffe/proto/caffe.proto

layers {
  name:"data"
  type:"WINDOW_DATA"
  top:"data"
  top:"label"
  window_data_param {
    source:"/path/to/file/window_train.txt"

    mean_file:"data/ilsvrc12/imagenet_mean.binaryproto"
    batch_size:128
    mirror: true
    crop_size:227
    fg_threshold:0.5
    bg_threshold:0.5
    fg_fraction:0.25
    context_pad:16
  }
}

数据集准备

推荐的 1-of-k 类的数据格式为 LMDB。请按照下面的说明操作,使用 Caffe 工具从 exthe 创建 LMDB:

  • 带有数据的文件夹
  • 必须不存在输出文件夹,例如 mydataset_train_lmdb,
  • 具有映像文件名称和相应标签的文本文件,例如,"train.txt" 如下所示
img3423.jpg 2
img3424.jpg 13
img3425.jpg 8
...

请注意,如果该数据分散在不同的文件夹中,train.txt 便可包含该数据点的完整路径。

create_label_file.py 是一个创建训练和验证文本文件的简单脚本,用于 Kaggle 的 Dog vs Cats 挑战赛,并且可以轻松地处理其他任务。

请注意,在测试中,我们假设标签丢失。如果标签可用,便可使用这些相同的步骤来准备 LMDB 测试数据集。

准备带有三个通道的数据(例如,RGB 图像)

下面的示例(基于)可生成训练 LMDB 并且需要 train.txt。它可以从 $CAFFE_ROOT 目录运行。

#!/usr/bin/env sh
# folder containing the training and validation images
TRAIN_DATA_ROOT=/path/to/training/images

# folder containing the file with the name of training images
DATA=/path/to/file
# folder for the lmdb datasets
OUTPUT=/path/to/output/directory
TOOLS=/path/to/caffe/build/tools

# Set to resize the images to 256x256
RESIZE_HEIGHT=256
RESIZE_WIDTH=256
echo "Creating train lmdb..."

# Delete the shuffle line if shuffle is not desired
GLOG_logtostderr=1 $TOOLS/convert_imageset 
    --resize_height=$RESIZE_HEIGHT 
    --resize_width=$RESIZE_WIDTH 
    --shuffle 
    $TRAIN_DATA_ROOT/ 
    $DATA/train.txt 
    $OUTPUT/mydataset_train_lmdb
echo "Done."

计算 LMDB 数据集中图像的平均值:

#!/usr/bin/env sh
# Compute the mean image in lmdb dataset
OUTPUT=/path/to/output/directory

 # folder for the lmdb datasets and output for mean image
TOOLS=/path/to/caffe/build/tools

$TOOLS/compute_image_mean $OUTPUT/mydataset_train_lmdb 
  $OUTPUT/train_mean.binaryproto

$TOOLS/compute_image_mean $OUTPUT/mydataset_val_lmdb 
  $OUTPUT/val_mean.binaryproto

准备带有不同通道的数据

灰度图像(一个通道)、RADAR 图像(两个通道)、视频(四个通道)、图像+景深(四个通道)、振动测量(一个通道)以及频谱图(一个通道)需要一个封装程序,以便设置 LMDB 数据集(请参考该博客脚本作为指南)。

调整图像大小

有两个常用的方法可以调整图像大小:

  • 将图像调整为所需的尺寸
  • 按比例调整大小,先确定小一点的尺寸,然后居中裁剪较大的尺寸,直到达到所需的尺寸

用户可以通过多种方式调整大小:

  • 通过 OpenCV*,在创建 LMDB 文件夹时,例如 build/tools/convert_imageset --resize_height=256 --resize_width=256将图像调整为期望的尺寸;convert_imageset 调用 ReadImageToDatum,后者调用 caffe/src/util/io.cpp 中的 ReadImageToCVMat
  • 通过 ImageMagick,例如 convert -resize 256x256\!<input_img> <output_img> warps image to desired size
  • 通过 OpenCV,使用支持在 tools/extra/resize_and_crop_images.py 中进行多线程图像转换的脚本,按比例调整大小,然后居中裁剪。这需要:
sudo pip install git+https://github.com/Yangqing/mincepie.git
sudo apt-get install -y python-opencv
vi tools/extra/launch_resize_and_crop_images.sh # set number of clients (use num_of_cores*2); file.txt, input, and output folders

此外,作为数据层的一部分,可以对图像进行裁剪或调整大小:

layer {
  name:"data"
  transform_param {
    crop_size:227
...
}

裁剪图像(训练期间为随机图像;测试期间为中央图像),而且

layer {
  name:"data"
  image_data_param {
    new_height:227
    new_width:227
...
}

使用 OpenCV 将图像调整为 new_heightnew_width

训练

训练要求:

  • train_val.prototxt:定义网络架构、初始化参数和本地学习速度
  • solver.prototxt:定义优化/训练参数,并作为调用的实际文件来训练深度网络
  • deploy.prototxt:仅在测试中使用。必须与 train_val.prototxt 完全相同,输入层、损失层和权重初始化(例如 weight_filler)除外,因为后两个在 deploy.prototxt 中不存在。

层以及层中的 blob 具有相同的名称很常见,但是并不是必需的。在每层的 prototxt 文件中,nametop 通常是相同的。

关于每层的具体描述,请参看此处。初始化参数极为重要。在此处设置初始化参数。一些其他有用的技巧

  • weight_filter 初始化(对于 ReLU 单元来说,MSRAFiller 通常优于 xavier,而 xavier 通常优于 gaussian;请注意,对于 MSRAFillerxavier,无需手动指定 std
  • gaussian:来自 Gaussian 分布 N(0,std) 的示例权重
  • xavier:来自均匀分布 U(-a,a) 的示例权重,其中 a=sqrt(3/fan_in)fan_in 代表输入的数量
  • MSRAFiller:来自正常分布 N(0,a) 的示例权重,其中 a=sqrt(2/fan_in)
  • base_lr:初始学习速度(默认为.01,如果在训练中发生 NAN 损失,将会变为更小的数值)
  • lr_mult:偏差通常设置为两倍,lr_mult 无偏差权重

LeNet 示例 lenet_train_test.prototxtdeploy.prototxtsolver.prototxt 在下面予以介绍(包括关于每个参数含义的评价):

solver.prototxt

# The train/validation net protocol buffer definition, that is, the training architecture
net:"examples/mnist/lenet_train_test.prototxt"

# Note:1 iteration = 1 forward pass over all the images in one batch

# Carry out a validation test every 500 training iterations.
test_interval:500

# test_iter specifies how many forward passes the validation test should carry out
#  a good number is num_val_imgs / batch_size (see batch_size in Data layer in phase TEST in train_test.prototxt)
test_iter:100

# The base learning rate, momentum and the weight decay of the network.
base_lr:0.01
momentum:0.9
weight_decay:0.0005

# We want to initially move fast towards the local minimum and as we approach it, we want to move slower
# To this end, there are various learning rates policies available:
#  fixed: always return base_lr.
#  step: return base_lr * gamma ^ (floor(iter / step))
#  exp: return base_lr * gamma ^ iter
#  inv: return base_lr * (1 + gamma * iter) ^ (- power)
#  multistep: similar to step but it allows non uniform steps defined by stepvalue
#  poly: the effective learning rate follows a polynomial decay, to be zero by the max_iter: return base_lr (1 - iter/max_iter) ^ (power)
#  sigmoid: the effective learning rate follows a sigmod decay: return base_lr * ( 1/(1 + exp(-gamma * (iter - stepsize))))
lr_policy:"step"
gamma:0.1
stepsize:10000 # Drop the learning rate in steps by a factor of gamma every stepsize iterations

# Display every 100 iterations
display:100

# The maximum number of iterations
max_iter:10000

# snapshot intermediate results, that is, every 5000 iterations it saves a snapshot of the weights
snapshot:5000
snapshot_prefix:"examples/mnist/lenet_multistep"

# solver mode:CPU or GPU
solver_mode:CPU

训练网络:

# The name of the output file (aka the trained weights) is in solver.prototxt
$CAFFE_ROOT/build/tools/caffe train -solver solver.prototxt

训练将提供两种类型的文件(注意 10000 是完成的迭代的数量):

  • lenet_multistep_10000.caffemodel:测试中使用的架构的权重
  • lenet_multistep_10000.solverstate:训练停止(例如断电)时使用,以便从当前的迭代恢复训练

如欲训练网络并确定相对于迭代的验证准确性或损失:

#CHART_TYPE=[0-7]
#  0:Test accuracy  vs. Iters
#  1:Test accuracy  vs. Seconds
#  2:Test loss  vs. Iters
#  3:Test loss  vs. Seconds
#  4:Train learning rate  vs. Iters
#  5:Train learning rate  vs. Seconds
#  6:Train loss  vs. Iters
#  7:Train loss  vs. Seconds
CHART_TYPE=0
$CAFFE_ROOT/build/tools/caffe train -solver solver.prototxt 2>&1 | tee logfile.log
python $CAFFE_ROOT/tools/extra/plot_training_log.py.example $CHART_TYPE name_of_plot.png logfile.log

中途退出(Dropout)可与完全连接层结合使用。它只是在每次正向传递时减少不同权重的百分比,以便减少过适现象,从而防止权重之间的相互适应。在测试中忽略。

layer {
  name:"fc6"
  type:"InnerProduct"
  bottom:"pool5"
  top:"fc6"
  param {
    lr_mult:1
    decay_mult:1
  }
  param {
    lr_mult:2
    decay_mult:0
  }
  inner_product_param {
    num_output:4096
    weight_filler {
      type:"gaussian"
      std:0.005
    }
    bias_filler {
      type:"constant"
      value:1
    }
  }
}
layer {
  name:"relu6"
  type:"ReLU"
  bottom:"fc6"
  top:"fc6"
}
layer {
  name:"drop6"
  type:"Dropout"
  bottom:"fc6"
  top:"fc6"
  dropout_param {
    dropout_ratio:0.5
  }
}

测量向前和向后传播时间(并非权重更新):

# Computes 50 iterations and returns forward, backward, and total time and the average
# note that the training samples and mean.binaryproto may be required or
# alternatively, use dummy variables
NUMITER=50
/path/to/caffe/build/tools/caffe time --model=train_val.prototxt -iterations $NUMITER

为确保计时准确,可使用 Linux 实用程序 numactl 在 MCDRAM 中分配内存缓冲区:

numactl -i all /path/to/caffe/build/tools/caffe time --model=train_val.prototxt -iterations $NUMITER

Model Zoo

Caffe Model Zoo 收集了多种经过训练的深度学习模式和/或 prototxt 文件,可用于各种不同的任务。这些模式可用于调试或测试目的。

多节点分布式训练

本节中的内容基于英特尔的 Caffe Github wiki。有两种主要的方法可用于在多个节点之间分配训练:模式并行化和数据并行化。在模式并行化中,模式在多个节点之间划分,每个节点都具有完整的数据批次。在数据并行化中,数据批次在多个节点之间划分,每个节点都具有完整的模式。当模式中的权重数较小以及数据批次较大时,数据并行化特别有用。在某些情况下还可以使用混合模式与数据并行化,比如当使用数据并行化方法来训练具有较少权重的层(例如卷积层)时,以及使用模式并行化方法来训练具有较多权重的层(例如完全连接的层)时。英特尔发布了理论分析,以便在这种混合方法中实现数据与模式并行化的最优分配。

鉴于带有更少权重的深入网络(比如 GoogleNet 和 ResNetand)的普及,以及使用数据并行化的分布式训练的成功,针对英特尔架构优化的 Caffe 支持数据并行化。多节点分布式训练目前正处于快速开发阶段,我们将对新的特性进行评估。

要训练不同的节点,请确保这两行位于 Makefile.config

USE_MPI := 1
# update with the path to binary MPI library
CXX := /usr/bin/mpicxx

使用多节点非常简单:

mpirun --hostfile path/to/hostfile -n <num_processes> /path/to/caffe/build/tools/caffe train --solver=/path/to/solver.prototxt --param_server=mpi

其中,<num_processes> 代表要使用的节点的数量,hostfile 包含每行节点的 ip 地址。请注意,solver.prototxt 指向每个节点中的 train.prototxt,每个 train.prototxt 需要指向不同的数据集部分。如欲了解更多详细信息,请点击此处

优化

循环利用层定义 prototxt 文件并做出两个变化。

1.更改数据层以包括新的数据(请注意,比例为 1/255):

layer {
  name:"mnist"
  type:"Data"
  top:"data"
  top:"label"
  transform_param {
    scale:0.00390625
  }
  data_param {
    source:"newdata_lmdb" # CHANGED THIS LINE TO THE NEW DATASET
    batch_size:64
    backend:LMDB
  }
}

2.更改最后一层,本例为 ip2(在测试中,对 deploy.prototxt 文件进行相同的修改):

layer {
  name:"ip2-ft" # CHANGED THIS LINE
  type:"InnerProduct"
  bottom:"ip1"
  top:"ip2-ft" # CHANGED THIS LINE
  param {
    lr_mult:1
  }
  param {
    lr_mult:2
  }
  inner_product_param {
    num_output:2 #CHANGED THIS LINE TO THE NUMBER OF CLASSES IN NEW DATASET
    bias_filler {
      type:"constant"
    }
  }
}

调用 Caffe:

#From the command line on $CAFFE_ROOT
./build/tools/caffe train -solver /path/to/solver.prototxt -weights  /path/to/trained_model.caffemodel

优化指南

  • 首先学习最后一层(早期层权重不会在优化方面有什么变化)
  • 将初始学习速度(在 solver.prototxt 中)降低为之前的 1/101/100
  • Caffe 层支持本地学习速度:lr_mult
  • 冻结除最后一层外的所有层(也可能包括倒数第二层),以便进行快速优化,即本地学习速度中的 lr_mult=0
  • 将最后一层和倒数第二层的本地学习速递提高 10 倍5 倍
  • 够用时便可停止,或者继续优化其他层

系统内部发生了什么:

  • 创建一个新的网络
  • 复制之前的权重以便初始化网络权重
  • 用常规方法解决(查看示例

测试

测试通常是指在 Python 中,或使用随 Caffe 提供的本机 C++ 实用程序进行推理、分类或评分。如果要对一张或一组图像(或信号)分类,则需要:

  • 图像
  • 网络架构
  • 网络权重

使用本机 C++ 实用程序进行测试的灵活性较低,推荐使用 Python 。具有模型的 protoxt 文件应在数据层(包含测试数据集)具备 phase:TEST,这样才能对模型进行测试。

/path/to/caffe/build/tools/caffe test -model /path/to/train_val.prototxt 
- weights /path/to/trained_model.caffemodel -iterations <num_iter>

上面的示例摘自这篇博客。要使用预先训练的模型来分类图像,请先下载预先训练模型:

./scripts/download_model_binary.py models/bvlc_reference_caffenet

接下来,下载数据集(本例中为 ILSVRC 2012)标签(也称作 synset 文件),因为需要将预测映射到类名称:

./data/ilsvrc12/get_ilsvrc_aux.sh

然后,对图像进行分类:

./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

输出结果如下所示:

---------- 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"

特性提取器和可视化

在卷积层中,可通过 blob 来代表一个层到下一个层的权重:output_feature_maps x x x input_feature_mapsfeature_maps 也称作渠道)。可以通过两种方法将 Caffe 中训练的网络用于特性提取器:第一种方法,也是建议的方法,是使用 Python API。第二种方法是使用随 Caffe 提供的本机 C++ 实用程序:

# Download model params
scripts/download_model_binary.py models/bvlc_reference_caffenet

# Generate a list of the files to process
# Use the images that ship with caffe
find `pwd`/examples/images -type f -exec echo {} ; > examples/images/test.txt

# Add a 0 to the end of each line
# input data structures expect labels after each image file name
sed -i "s/$/ 0/" examples/images/test.txt

# Get the mean of trainint set to subtract it from images
./data/ilsvrc12/get_ilsvrc_aux.sh

# Copy and modify the data layer to load and resize the images:
cp examples/feature_extraction/imagenet_val.prototxt examples/images
vi examples/iamges/imagenet_val.prototxt

# Extract features
./build/tools/extract_features.bin models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel 
  examples/images/imagenet_val.prototxt fc7 examples/images/features 10 lmdb

特性 blob 从上面的 fc7 提取,后者代表了参考模型的最高级别特性。或者,也可以使用其它层,例如 conv5pool3。上面的最后一个参数 10 lmdb 是迷你批量尺寸。特性被存储于 LevelDB examples/images/features,可供其它部分代码访问。

使用 Python* API

您无需了解本部分的内容也可以开始使用 Caffe。本部分的内容基于这篇博客。在测试、分类和特性提取中可以轻松地使用 Python 接口,而且该接口也可用于定义和训练网络。

设置 Python Caffe

确保编译 Caffe 时调用 make pycaffe。在 Python 中,首先导入 caffe 模块:

# Make sure that caffe is on the python path:
# (alternatively set PYTHONCAFFE var as explained the installation)
import sys
CAFFE_ROOT = '/path/to/caffe/'
sys.path.insert(0, CAFFE_ROOT + 'python')
import caffe
caffe.set_mode_cpu()

加载网络架构

网络架构位于 train_val.prototxtdeploy.prototxt 文件中。加载网络:

net = caffe.Net('train_val.prototxt', caffe.TRAIN)

或者,如果加载一套特定的权重:

net = caffe.Net('deploy.prototxt', 'trained_model.caffemodel', caffe.TRAIN)

使用 caffe.TRAIN 的原因是,如果运行两次,caffe.TEST 便会崩溃,caffe.TRAIN 也会出现相同的结果。

net 包含数据 blob (net.blobs) 和参数权重 blob (net.params)。在下面的命令中,可使用任意其它层的名称来替换 conv1

  • net.blobs['conv1']conv1 层的数据输出,也称为特性图
  • net.params['conv1'][0]conv1 层的权重 blob
  • net.params['conv1'][1]conv1 层的偏差 blob
  • net.blobs.items():返回所有层的数据 blob - 适用于重复循环层的 for 环路

网络可视化

要显示网络,请首先安装 pydot 模块和 graphviz

sudo apt-get install -y GraphViz
sudo pip install pydot

运行 draw_net.py python 脚本:

python python/draw_net.py examples/net_surgery/deploy.prototxt train_val_net.png
open train_val_net.png

数据输入

使用下列技巧中的一种,将数据输入数据层 blob:

  • 修改数据层,以匹配图像大小:
import numpy as np
# get input image and arrange it as a 4-D tensor
im = np.array(Image.open('/path/to/caffe/examples/images/cat_gray.jpg'))
im = im[np.newaxis, np.newaxis, :, :]
# resize the blob to be the size of the input image
net.blobs['data'].reshape(im.shape) # if the image input is different
# compute the blobs given the input data
net.blobs['data'].data[...]= im
  • 修改输入数据,以匹配数据层预期输入的大小:
im = caffe.io.load.image('/path/to/caffe/examples/images/cat_gray.jpg')
shape = net.blobs['data'].data.shape
# resize the img to be the size of the data blob
im = caffe.io.resize(im, shape[3], shape[2], shape[1])
# compute the blobs given the input data
net.blobs['data'].data[...]= im

通常使用的输入数据转化包括:

net = caffe.Net('deploy.prototxt', 'trained_model.caffemodel', caffe.TRAIN)
transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})
ilsvrc_mean = 'python/caffe/imagenet/ilsvrc_2012_mean.npy'
transformer.set_mean('data', np.load(ilsvrc_mean).mean(1).mean(1))
# puts the channel as the first dimention
transformer.set_transpose('data', (2,0,1))
# (2,1,0) maps RGB to BGR for example
transformer.set_channel_swap('data', (2,1,0))
transformer.set_raw_scale('data', 255.0)
# the batch size can be changed on-the-fly
net.blobs['data'].reshape(1,3,227,227)
# load the image in the data layer
im = caffe.io.load.image('/path/to/caffe/examples/images/cat_gray.jpg')
# transform the image and store it in the net.blob
net.blobs['data'].data[...]= transformer.preprocess('data', im)

如欲查看 im

import matplotlib.pyplot as plt
plt.imshow(im)

推断

有关输入图像的预测可通过以下方式计算:

# assumes that images are loaded
prediction = net.forward()
print 'predicted class:', prediction['prob'].argmax()

要对前向传播进行计时(忽略数据预处理时间):

timeit net.forward()

另外一个能够转换数据并且可对不同数据输入进行分类的模块是 net.Classifier。也就是说,net.Classifier 可用来取代net.Net caffe.io.Transformer

im1 = caffe.io.load.images('/path/to/caffe/examples/images/cat.jpg')
im2 = caffe.io.load.images('/path/to/caffe/examples/images/fish-bike.jpg')
imgs = [im1, im2]
ilsvrc_mean = '/path/to/caffe/python/caffe/imagenet/ilsvrc_2012_mean.npy'
net = caffe.Classifier('deploy.prototxt', 'trained_model.caffemodel',
                       mean=np.load(ilsvrc_mean).mean(1).mean(1),
                       channel_swap=(2,1,0),
                       raw_scale=255,
                       image_dims=(256, 256))
prediction = net.predict(imgs) # predict takes any number of images
print 'predicted classes:', prediction[0].argmax(), prediction[1].argmax()

如果使用具有多个图像的文件夹,请按照下面的方式替换 imgs(其他保持不变):

IMAGES_FOLDER = '/path/to/folder/w/images/'
import os
images = os.listdir(IMAGES_FOLDER)
imgs = [ caffe.io.load_image(IMAGES_FOLDER + im) for im in images ]

整个测试集可能不适用于内存。因此,可以成批计算预测,例如 100 张图像的批次。

要在条形图中查看 im1 的所有类的概率

plt.plot(prediction[0])

要对一张图像(过度取样)的完整分类管道(包括 im1 转换)进行计时。过度取样裁剪 10 张图像:中心、角落和镜像:

timeit net.predict([im1])

如果过度取样被设置为 false,仅裁剪中心:

timeit net.predict([im1], oversample=0)

特性提取和可视化

要检查每个特定层的数据,例如 fc7

net.blobs['fc7'].data

要检索网络层和形状的详细信息

# Retrieve details of the network's layers
[(k, v.data.shape) for k, v in net.blobs.items()]

# Retrieve weights of the network's layers
[(k, v[0].data.shape) for k, v in net.params.items()]

# Retrieve the features in the last fully connected layer
# prior to outputting class probabilities
feat = net.blobs['fc7'].data[4]

# Retrieve size/dimensions of the array
feat.shape

blob 可视化:

# Assumes that the "net = caffe.Classifier" module has been called
# and data has been formatted as in the example above

# Take an array of shape (n, height, width) or (n, height, width, channels)
# and visualize each (height, width) section in a grid
# of size approx. sqrt(n) by sqrt(n)
def vis_square(data, padsize=1, padval=0):
    # values between 0 and 1
    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.rcParams['figure.figsize'] = (25.0, 20.0)

# visualize the weights after the 1st conv layer
net.params['conv1'][0].data.shape
filters = net.params['conv1'][0].data
vis_square(filters.transpose(0, 2, 3, 1))

# visualize the feature maps after 1st conv layer
net.blobs['conv1'].data.shape
feat = net.blobs['conv1'].data[0,:96]
vis_square(feat, padval=1)

# visualize the weights after the 2nd conv layer
net.blobs['conv2'].data.shape
feat = net.blobs['conv2'].data[0,:96]
vis_square(feat, padval=1)

# visualize the weights after the 2nd pool layer
net.blobs['pool2'].data.shape
feat = net.blobs['pool2'].data[0,:256] # change 256 to number of pool outputs
vis_square(feat, padval=1)

# Visualize the neuron activations for the 2nd fully-connected layer
net.blobs['ip2'].data.shape
feat = net.blobs['ip2'].data[0]
plt.plot(feat.flat)
plt.legend()
plt.show()

定义网络

网络可以在 Python 中定义并保存至 prototxt 文件:

from caffe import layers as L
from caffe import params as P

def lenet(lmdb, batch_size):
    # auto generated LeNet
    n = caffe.NetSpec()
    n.data, n.label = L.Data(batch_size=batch_size, backend=P.Data.LMDB, source=lmdb, transform_param=dict(scale=1./255), ntop=2)
    n.conv1 = L.Convolution(n.data, kernel_size=5, num_output=20, weight_filler=dict(type='xavier'))
    n.pool1 = L.Pooling(n.conv1, kernel_size=2, stride=2, pool=P.Pooling.MAX)
    n.conv2 = L.Convolution(n.pool1, kernel_size=5, num_output=50, weight_filler=dict(type='xavier'))
    n.pool2 = L.Pooling(n.conv2, kernel_size=2, stride=2, pool=P.Pooling.MAX)
    n.ip1 = L.InnerProduct(n.pool2, num_output=500, weight_filler=dict(type='xavier'))
    n.relu1 = L.ReLU(n.ip1, in_place=True)
    n.ip2 = L.InnerProduct(n.relu1, num_output=10, weight_filler=dict(type='xavier'))
    n.loss = L.SoftmaxWithLoss(n.ip2, n.label)
    return n.to_proto()

with open('examples/mnist/lenet_auto_train.prototxt', 'w') as f:
    f.write(str(lenet('examples/mnist/mnist_train_lmdb', 64)))

with open('examples/mnist/lenet_auto_test.prototxt', 'w') as f:
    f.write(str(lenet('examples/mnist/mnist_test_lmdb', 100)))

上面的代码将生成下面的 prototxt 文件:

layer {
  name:"data"
  type:"Data"
  top:"data"
  top:"label"
  transform_param {
    scale:0.00392156862745
  }
  data_param {
    source:"examples/mnist/mnist_train_lmdb"
    batch_size:64
    backend:LMDB
  }
}
layer {
  name:"conv1"
  type:"Convolution"
  bottom:"data"
  top:"conv1"
  convolution_param {
    num_output:20
    kernel_size:5
    weight_filler {
      type:"xavier"
    }
  }
}
layer {
  name:"pool1"
  type:"Pooling"
  bottom:"conv1"
  top:"pool1"
  pooling_param {
    pool:MAX
    kernel_size:2
    stride:2
  }
}
layer {
  name:"conv2"
  type:"Convolution"
  bottom:"pool1"
  top:"conv2"
  convolution_param {
    num_output:50
    kernel_size:5
    weight_filler {
      type:"xavier"
    }
  }
}
layer {
  name:"pool2"
  type:"Pooling"
  bottom:"conv2"
  top:"pool2"
  pooling_param {
    pool:MAX
    kernel_size:2
    stride:2
  }
}
layer {
  name:"ip1"
  type:"InnerProduct"
  bottom:"pool2"
  top:"ip1"
  inner_product_param {
    num_output:500
    weight_filler {
      type:"xavier"
    }
  }
}
layer {
  name:"relu1"
  type:"ReLU"
  bottom:"ip1"
  top:"ip1"
}
layer {
  name:"ip2"
  type:"InnerProduct"
  bottom:"ip1"
  top:"ip2"
  inner_product_param {
    num_output:10
    weight_filler {
      type:"xavier"
    }
  }
}
layer {
  name:"loss"
  type:"SoftmaxWithLoss"
  bottom:"ip2"
  bottom:"label"
  top:"loss"
}

训练网络

在 Python 中加载解析器并执行向前传播:

solver = caffe.get_solver('models/bvlc_reference_caffenet/solver.prototxt')
net = caffe.Net('train_val.prototxt', caffe.TRAIN)
solver.net.forward()  # train net
solver.test_nets[0].forward()  # test net (there can be more than one)

计算梯度:

solver.net.backward()

梯度值可以按照下面的方式显示:

# data gradients
net.blobs['conv1'].diff

# weight gradients
net.params['conv1'][0].diff

# biases gradients
net.params['conv1'][1].diff

启动一次迭代、向前传播、向后传播和更新:

solver.step(1)

启动 solver.prototxt 中定义为 max_iter 的所有迭代:

solver.step()

调试

本节为可选内容,仅面向 Caffe 开发人员。

关于调试的若干技巧:

  • 消除随机性
  • 比较 caffemodel
  • 使用 Caffe 的调试信息

消除随机性有助于再现行为和输出结果。从非结合浮点算数运算消除随机性不在本文的讨论范围内。

可以在不同阶段添加随机性:

  • 权重通常是在分配(例如 Gaussian)后随机初始化。
  • 通过随机水平翻转图像或者随机剪裁图像的不同部分(例如从 256x256 图像裁剪 227x227 补丁),可以对图像进行预处理;而且也可以随机地乱序执行图像
  • 在训练中的 dropout 层,可以随机使用某些权重,并忽略其它权重

解决这一问题的方法是使用 seed。在 solver.prototxt 中添加一行:

# pick some value for random_seed that is greater or equal to 1, for example:
random_seed:42

这可确保使用相同的“随机”值。但是,seed 可能会在不同的设备上生成不同的值。跨设备工作时更加稳定:

  • 使用相同的乱序执行图像来准备数据,每次尝试的时候不要重复乱序执行
  • train.prototxtImageData 层以及 transform_param 中:不要裁剪和镜像图像。如果需要更小尺寸的图像,可以对 image_data_param 中的图像进行调整:
layer {
  name:"data"
  type:"ImageData"
  top:"data"
  top:"label"
  include {
    phase:TRAIN
  }
  transform_param {
 #   mirror: true
 #   crop_size:227
    mean_value:104
    mean_value:117
    mean_value:123
  }
  image_data_param {
    source:"/path/to/file/train.txt"
    batch_size:32
    new_height:224
    new_width:224
  }
}

在 dropout 层的 train.prototxt 中,dropout_ratio:0

其它有用的指南

  • solver.prototxt 中,将 lr_policy 更改为 fixed
  • solver.prototxt 中添加行 debug_info:1

比较两个 affemodel,下面的脚本返回 caffemodel 中所有权重差值的总和:

# Intel Corporation
# Author:Ravi Panchumarthy

import sys, os, argparse, time
import pdb
import numpy as np

def get_args():
    parser = argparse.ArgumentParser('Compare weights of two caffe models')

    parser.add_argument('-m1', dest='modelFile1', type=str, required=True,
                        help='Caffe model weights file to compare')
    parser.add_argument('-m2', dest='modelFile2', type=str, required=True,
                        help='Caffe model weights file to compare aganist')
    parser.add_argument('-n', dest='netFile', type=str, required=True,
                        help='Network prototxt file associated with model')
    return parser.parse_args()

if __name__ == "__main__":
    import caffe

    args = get_args()
    net = caffe.Net(args.netFile, args.modelFile1, caffe.TRAIN)
    net2compare = caffe.Net(args.netFile, args.modelFile2, caffe.TRAIN)

    wt_sumOfAbsDiffByName = dict()
    bias_sumOfAbsDiffByName = dict()

    for name, blobs in net.params.iteritems():
        wt_diffTensor = np.subtract(net.params[name][0].data, net2compare.params[name][0].data)
        wt_absDiffTensor = np.absolute(wt_diffTensor)
        wt_sumOfAbsDiff = wt_absDiffTensor.sum()
        wt_sumOfAbsDiffByName.update({name : wt_sumOfAbsDiff})

        # if args.layerDebug == 1:
        #     print("%s :%s" % (name,wt_sumOfAbsDiff))

        bias_diffTensor = np.subtract(net.params[name][1].data, net2compare.params[name][1].data)
        bias_absDiffTensor = np.absolute(bias_diffTensor)
        bias_sumOfAbsDiff = bias_absDiffTensor.sum()
        bias_sumOfAbsDiffByName.update({name : bias_sumOfAbsDiff})

    print("\nThe sum of absolute difference of all layer's weight is :%s" % sum(wt_sumOfAbsDiffByName.values()))
    print("The sum of absolute difference of all layer's bias is :%s" % sum(bias_sumOfAbsDiffByName.values()))

    finalDiffVal = sum(wt_sumOfAbsDiffByName.values())+ sum(bias_sumOfAbsDiffByName.values())
    print("The sum of absolute difference of all layers weight's and bias's is :%s" % finalDiffVal )

要进行进一步的调试,在 Makefile.config 中取消行 DEBUG := 1 的批注,对代码进行编译,然后执行命令:

gdb /path/to/caffe/build/caffe

一旦 gdb 开始使用 run 命令,添加其余的参数

run train -solver /path/to/solver.prototxt

示例

在 MNIST 上训练 LeNet

本节的目的是展示具体操作的步骤,例如准备数据集、训练模型以及对模型进行计时。本节内容基于此处此处

准备数据集:

cd $CAFFE_ROOT
./data/mnist/get_mnist.sh # downloads MNIST dataset
./examples/mnist/create_mnist.sh # creates dataset in LMDB format

训练数据集:

# Reduce the number of iterations from 10K to 1K to quickly run through this example
sed -i 's/max_iter:10000/max_iter:1000/g' examples/mnist/lenet_solver.prototxt
./build/tools/caffe train -solver examples/mnist/lenet_solver.prototxt

对向前和向后传播进行计时(不包过权重更新):

./build/tools/caffe time --model=examples/mnist/lenet_train_test.prototxt -iterations 50 # runs on CPU

为确保计时准确,可使用实用程序 numactl 在 MCDRAM 中分配内存缓冲区:

numactl -i all /path/to/caffe/build/tools/caffe time --model=train_val.prototxt -iterations $NUMITER

测试训练模型。本示例介绍了在验证测试中进行测试。在实际场景中,应使用不同的数据集进行测试,并使用下面的格式或上面介绍的格式:

# the file with the model should have a 'phase:TEST'
./build/tools/caffe test -model examples/mnist/lenet_train_test.prototxt 
  -weights examples/mnist/lenet_iter_1000.caffemodel -iterations 50

Dogs vs Cats 挑战赛

申请一个 Kaggle 账户并下载数据。请注意,您不能仅仅是执行 wget ,因为您必须登录 Kaggle。登录 Kaggle,下载数据并将数据传输至设备。

解压 dogvscat.zip,并执行 zip 文件中的 dogvscat.sh 脚本。脚本如下所示:

#!/usr/bin/env sh
CAFFE_ROOT=/path/to/caffe
mkdir dogvscat
DOG_VS_CAT_FOLDER=/path/to/dogvscat

cd $DOG_VS_CAT_FOLDER
## Download datasets (requires first a login)
#https://www.kaggle.com/c/dogs-vs-cats/download/train.zip
#https://www.kaggle.com/c/dogs-vs-cats/download/test1.zip

# Unzip train and test data
sudo apt-get -y install unzip
unzip train.zip -d .
unzip test1.zip -d .

# Format data
python create_label_file.py # creates 2 text files with labels for training and validation
./build_datasets.sh # build lmdbs

# Download ImageNet pretrained weights (takes ~20 min)
$CAFFE_ROOT/scripts/download_model_binary.py $CAFFE_ROOT/models/bvlc_reference_caffenet

# Fine-tune weights in the AlexNet architecture (takes ~100 min)
$CAFFE_ROOT/build/tools/caffe train -solver $DOG_VS_CAT_FOLDER/dogvscat_solver.prototxt 
    -weights $CAFFE_ROOT/models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel

# Classify test dataset
cd $DOGVSCAT_FOLDER
python convert_binaryproto2npy.py
python dogvscat_classify.py # Returns prediction.txt (takes ~30 min)

# A better approach is to train five AlexNets w/init parameters from the same distribution,
# fine-tune those five, and compute the average of the five networks

我将结果提交至 Kaggle,准确度为 0.97566(在全部 215 个结果中排名第 15 位)。

PASCAL VOC 分类

解压 voc2012.zip 并执行 voc2012.sh 脚本(在 zip 文件中,如下方所示)。输入 sudo chmod 700 *.sh 以确保脚本得到执行。它可以训练并运行 AlexNet。

#!/usr/bin/env sh

# Copy and unzip voc2012.zip (it contains this file) then run this file.But first
#  change paths in: voc2012.sh; build_datasets.sh; solvers/*; nets/*; classify.py

# As you run various files, you can ignore the following error if it shows up:
#  libdc1394 error:Failed to initialize libdc1394

# set Caffe root directory
CAFFE_ROOT=$CAFFE_ROOT
VOC=/path/to/voc2012

chmod 700 *.sh

# Download datasets
# Details: http://host.robots.ox.ac.uk/pascal/VOC/voc2012/index.html#devkit
if [ !-f VOCtrainval_11-May-2012.tar ]; then
  wget http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar
fi
# VOCtraival_11-May-2012.tar contains the VOC folder with:
#  JPGImages: all jpg images
#  Annotations: objects and corresponding bounding box/pose/truncated/occluded per jpg
#  ImageSets: breaks the images by the type of task they are used for
#  SegmentationClass and SegmentationObject: segmented images (duplicate directories)
tar -xvf VOCtrainval_11-May-2012.tar

# Run Python scripts to create labeled text files
python create_labeled_txt_file.py

# Execute shell script to create training and validation lmdbs
# Note that lmdbs directories w/the same name cannot exist prior to creating them
./build_datasets.sh

# Execute following command to download caffenet pre-trained weights (takes ~20 min)
#  if weights exist already then the command is ignored
$CAFFE_ROOT/scripts/download_model_binary.py $CAFFE_ROOT/models/bvlc_reference_caffenet

# Fine-tune weights in the AlexNet architecture (takes ~60 min)
# you can also chose one of six solvers: pascal_solver[1-6].prototxt
$CAFFE_ROOT/build/tools/caffe train -solver $VOC/solvers/voc2012_solver.prototxt 
  -weights $CAFFE_ROOT/models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel

# The lines below are not really needed; they served as examples on how to do some tasks

# Test against voc2012_val_lmbd dataset (name of lmdb is the model under PHASE: test)
 $CAFFE_ROOT/build/tools/caffe test -model $VOC/nets/voc2012_train_val_ft678.prototxt 
   -weights $VOC/weights_iter_5000.caffemodel -iterations 116

# Classify validation dataset: returns a file w/the labels of the val dataset
#  but it doesn't report accuracy (that would require some adjusting of the code)
python convert_binaryproto2npy.py
mkdir results
python cls_confidence.py
python average_precision.py

# Note to submit results to the VOC scoreboard, retrain NN using the trainval set
# and test on the unlabeled test data provided by VOC

# A better approach is to train five CNNs w/init parameters from the same distribution,
# fine-tune those five, and compute the average of the five networks

更多 VOC 信息(便于读者了解关于 VOC 的更多信息):

  • PASCAL VOC 数据集
  • 比较方法或设计选择
  • 使用全部 VOC2007 数据,所有注释(包括测试注释)均可用
  • 使用单独设置的 VOC2012 "trainval" 报告交叉验证结果(2008 年到 2012 年未提供测试注释)
  • 最常见的指标为平均准确率 (AP):准确率/撤销曲线下方的区域
  • VOC 2012 数据总结
  • 2008 年有一套全新的数据集,而且每年都会添加更多的数据。因此,我们会经常看到 VOC2007 和 VOC2012 中公布的结果(或 VOC2011-- 2011 年和 2012 年之间没有分类和检测任务的额外数据)
  • 20 个类
  • 训练:5,717 张图像,13,609 个对象
  • 验证:5,823 张图像,13,841 个对象
  • 测试:10,991 张图像

当前 Caffe 用例

列出了一小部分热门 Caffe 用例如欲查看更完整的列表,请访问 Caffe Model-Zoo

扩展阅读

有关编译器优化的更完整信息,请参阅优化通知