caffe.proto
作用:caffe数据结构定义的主要文件,用protobuf语言写的各个模块的参数设置。
代码中包含三类参数:
required: 必须的参数,这个参数必须要存在,而且只有一个。
optional: 可选择参数,这个参数可以有也可以无,但是不超过一个。
repeated: 可重复参数,这个参数可以重复多次使用。
代码路径:{caffe_root_dir}/src/caffe/proto/caffe.proto
例如:设置了solverparameter中对应的参数。
from caffe.proto import caffe_pb2
def gen_solver(solver_file, net_file, test_net_file=None):
s = caffe_pb2.SolverParameter() #继承自SolverParameter
s.train_net = net_file
if not test_net_file:
s.test_net.append(net_file)
else:
s.test_net.append(test_net_file)
s.test_interval = 500 # 每训练500次,执行一次测试
s.test_iter.append(100) # 测试迭代次数,假设测试数据有8000个,那batch size=80
s.max_iter = 20000 # 最大迭代次数
s.base_lr = 0.001 # 基础学习率
s.momentum = 0.9 # momentum系数
s.weight_decay = 5e-4 # 正则化权值衰减因子,防止过拟合
s.lr_policy = 'step' # 学习率衰减方法
s.stepsize=1000 # 只对step方法有效, base_lr*gamma^floor(iter/stepsize)
s.gamma = 0.1
s.display = 500 # 输出日志间隔迭代次数
s.snapshot = 10000 # 在指定迭代次数时保存模型
s.snapshot_prefix = 'shapshot'
s.type = 'SGD' # 迭代算法类型, ADADELTA, ADAM, ADAGRAD, RMSPROP, NESTEROV
s.solver_mode = caffe_pb2.SolverParameter.GPU #采用GPU训练 或单独设置
with open(solver_file, 'w') as f:
f.write(str(s))
solver = caffe.SGDSolver(solver_file)
return solver
caffe四大模块
Blobs
作用:数据的封装包,保存网络的传递的普通数据和梯度。可同步到CPU和GPU。
存储方式:
一个N * K * H * W的数据,(n,k,h,w)的对应的物理地址为:((n*K+k)*H+h)*W+w
代码地址:
hpp: {caffe_root_dir}/include/caffe/blob.hpp
cpp:{caffe_root_dir}/src/caffe/blob.cpp
其中包含:
- Reshape()
- Blob()构造函数
- data_数据操作函数 : cpu_data() 返回cpu 中的数据,set_cpu_data()清空cpu 数据,gpu_data()返回gpu 中的数据。
- 反向传播导数diff_ 操作函数:cpu_diff()返回cpu 中的数据,gpu_diff()返回gpu 中的数据。
- ShareData 函数:ShareData(const Blob& other)把other的Blob地址给当前的blob,ShareDiff(const Blob& other)。
- Updata 函数:用于参数blob的更新(weight,bias 等减去对应的导数)。
- asum_data 函数:返回data_ 中所有 element 的绝对值之和。
- asum_diff 函数:返回diff_ 中所有 element 的绝对值之和。
- sumsq_data 函数:返回 data_ 中所有 element 的平方和。
- sumsq_diff函数:返回 diff_ 中所有 element 的平方和。
- scale_data(Dtype scale_factor) :给data乘以scale_factor。
- scale_diff(Dtype scale_factor):给diff乘以scale_factor。
- ShapeEquals(const BlobProto& other):检查当前的blob和已知的 other 的 shape 是否相同,相同返回true。
- CopyFrom(const Blob& source, bool copy_diff, bool reshape):从source 拷贝数据 , copy_diff控制是拷贝diff还是data。
- FromProto(const BlobProto& proto, bool reshape):将proto传入的data和diff拷贝到cpu。
- ToProto(BlobProto* proto, bool write_diff):将data和diff写入proto。
Layers
caffe模型的本质内容和执行计算的基本单元。
基本结构:
bottom blob ->Convolution ->top blob
一个layer通过bottom接收数据,通过top输出数据。一个Layer定义了3种运算:
- setup(初始化参数):初始化时重置layers及其相互之间的连接。
- Forward:从bottom接收数据,计算后送入top中。
- Backward:给定top层输出的梯度,计算其相对于输入的梯度,并传递给bottom。
如果自定义layer:只需定义setup,forward,backward。
代码地址:
{caffe_root_dir}/include/caffe/layer.hpp
{caffe_root_dir}/src/caffe/layer.cpp
而具体的层在:
{caffe_root_dir}/include/caffe/layers
{caffe_root_dir}/src/caffe/layers
-
DataLayer:
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 } }
-
Vision_layer
layer { name: "conv1" type: "Convolution" bottom: "data" top: "conv1" convolution_param { num_output: 96 kernel_size: 11 stride: 4 } }
-
Activation layer
layer { name: "relu1" type: "ReLU" bottom: "conv1" top: "conv1" }
-
Common layer
layer { name: "fc8" type: "InnerProduct" bottom: "fc7" top: "fc8" inner_product_param { num_output: 1000 } }
-
Loss Layer
layer { name: "loss" type: "SoftmaxWithLoss" bottom: "fc8" bottom: "label" top: "loss" }
Net
作用:合成网络和自动微分,同时定义了一个模型函数和其梯度。通过合层各层的输出来计算这个函数;通过合层各层的反向传播来计算来自损失函数的梯度。
代码地址:
{caffe_root_dir}/include/caffe/net.hpp
{caffe_root_dir}/src/caffe/net.cpp
构建网络结构:
- 通过写.prototxt构建网络。需要写train和test不同的prototxt
- 利用Python写网络结构,生成prototxt。
- 利用C++写网络结构,比较麻烦,没有必要。
例子:
from __future__ import print_function
from caffe import layers as L, params as P, to_proto
from caffe.proto import caffe_pb2
# helper function for common structures
def conv_relu(bottom, ks, nout, stride=1, pad=0, group=1):
conv = L.Convolution(bottom, kernel_size=ks, stride=stride,
num_output=nout, pad=pad, group=group)
return conv, L.ReLU(conv, in_place=True)
def fc_relu(bottom, nout):
fc = L.InnerProduct(bottom, num_output=nout)
return fc, L.ReLU(fc, in_place=True)
def max_pool(bottom, ks, stride=1):
return L.Pooling(bottom, pool=P.Pooling.MAX, kernel_size=ks, stride=stride)
def caffenet(lmdb, batch_size=256, include_acc=False):
data, label = L.Data(source=lmdb, backend=P.Data.LMDB, batch_size=batch_size, ntop=2,
transform_param=dict(crop_size=227, mean_value=[104, 117, 123], mirror=True))
# the net itself
conv1, relu1 = conv_relu(data, 11, 96, stride=4)
pool1 = max_pool(relu1, 3, stride=2)
norm1 = L.LRN(pool1, local_size=5, alpha=1e-4, beta=0.75)
conv2, relu2 = conv_relu(norm1, 5, 256, pad=2, group=2)
pool2 = max_pool(relu2, 3, stride=2)
norm2 = L.LRN(pool2, local_size=5, alpha=1e-4, beta=0.75)
conv3, relu3 = conv_relu(norm2, 3, 384, pad=1)
conv4, relu4 = conv_relu(relu3, 3, 384, pad=1, group=2)
conv5, relu5 = conv_relu(relu4, 3, 256, pad=1, group=2)
pool5 = max_pool(relu5, 3, stride=2)
fc6, relu6 = fc_relu(pool5, 4096)
drop6 = L.Dropout(relu6, in_place=True)
fc7, relu7 = fc_relu(drop6, 4096)
drop7 = L.Dropout(relu7, in_place=True)
fc8 = L.InnerProduct(drop7, num_output=1000)
loss = L.SoftmaxWithLoss(fc8, label)
if include_acc:
acc = L.Accuracy(fc8, label)
return to_proto(loss, acc)
else:
return to_proto(loss)
def make_net():
with open('train.prototxt', 'w') as f:
print(caffenet('/path/to/caffe-train-lmdb'), file=f)
with open('test.prototxt', 'w') as f:
print(caffenet('/path/to/caffe-val-lmdb', batch_size=50, include_acc=True), file=f)
if __name__ == '__main__':
make_net()
Solver
作用:用于优化网络参数。
代码地址:
{caffe_root_dir}/include/caffe/solver.hpp
{caffe_root_dir}/src/caffe/solver.cpp
不同的优化方式具体在:
{caffe_root_dir}/include/caffe/solvers
{caffe_root_dir}/src/caffe/solvers
包含优化方式有:
“SGD”,“AdaDelta”,“AdaGrad”,“Adam”,“Neserov”,“RMSprop”
生成solver.prototxt的方式:
- 自己写solver.prototxt文件
- 用python编写生成solver.prototxt
自己写的例子:
# The train/test net protocol buffer definition
net: "examples/cifar10/cifar10_full_sigmoid_train_test.prototxt"
# test_iter specifies how many forward passes the test should carry out.
# In the case of CIFAR10, we have test batch size 100 and 100 test iterations,
# covering the full 10,000 testing images.
test_iter: 10
# Carry out testing every 1000 training iterations.
test_interval: 1000
# The base learning rate, momentum and the weight decay of the network.
base_lr: 0.001
momentum: 0.9
#weight_decay: 0.004
# The learning rate policy
lr_policy: "step"
gamma: 1
stepsize: 5000
# Display every 100 iterations
display: 100
# The maximum number of iterations
max_iter: 60000
# snapshot intermediate results
snapshot: 10000
snapshot_prefix: "examples/cifar10_full_sigmoid"
# solver mode: CPU or GPU
solver_mode: GPU
Python生成的例子:
from caffe.proto import caffe_pb2
def gen_solver(solver_file, net_file, test_net_file=None):
s = caffe_pb2.SolverParameter() #继承自SolverParameter
s.train_net = net_file
if not test_net_file:
s.test_net.append(net_file)
else:
s.test_net.append(test_net_file)
s.test_interval = 500 # 每训练500次,执行一次测试
s.test_iter.append(100) # 测试迭代次数,假设测试数据有8000个,那batch size=80
s.max_iter = 20000 # 最大迭代次数
s.base_lr = 0.001 # 基础学习率
s.momentum = 0.9 # momentum系数
s.weight_decay = 5e-4 # 正则化权值衰减因子,防止过拟合
s.lr_policy = 'step' # 学习率衰减方法
s.stepsize=1000 # 只对step方法有效, base_lr*gamma^floor(iter/stepsize)
s.gamma = 0.1
s.display = 500 # 输出日志间隔迭代次数
s.snapshot = 10000 # 在指定迭代次数时保存模型
s.snapshot_prefix = 'shapshot'
s.type = 'SGD' # 迭代算法类型, ADADELTA, ADAM, ADAGRAD, RMSPROP, NESTEROV
s.solver_mode = caffe_pb2.SolverParameter.GPU #采用GPU训练 或单独设置
with open(solver_file, 'w') as f:
f.write(str(s))
math_functions
定义了caffe用到的一些矩阵操作和数值计算的一些函数。
代码地址:
{caffe_root_dir}/include/caffe/util/math_functions.hpp
{caffe_root_dir}/src/caffe/util/math_functions.cpp
函数及其功能:
- caffe_cpu_gemm(const CBLAS_TRANSPOSE TransA,
const CBLAS_TRANSPOSE TransB, const int M, const int N, const int K,
const float alpha, const float* A, const float* B, const float beta,
float* C):C=alphaAB+beta*C - caffe_cpu_gemv(const CBLAS_TRANSPOSE TransA, const int M,
const int N, const float alpha, const float* A, const float* x,
const float beta, float* y):y=alphaAx+beta*y - caffe_axpy(const int N, const float alpha, const float* X,
float* Y):Y=alpha*X+Y - caffe_set(const int N, const Dtype alpha, Dtype* Y):用常数 alpha 对 Y 进行初始化
- caffe_add_scalar(const int N, const float alpha, float* Y):给 Y 的每个 element 加上常数 alpha
- caffe_copy(const int N, const Dtype* X, Dtype* Y):将X拷贝到Y所指向的内存中。
- caffe_scal(const int N, const float alpha, float X):X = alphaX
- caffe_cpu_axpby(const int N, const float alpha, const float* X,
const float beta, float* Y):Y= alphaX+betaY - caffe_add、 caffe_sub、 caffe_mul、 caffe_div :y[i] = a[i] + - * \ b[i]。
- caffe_powx、 caffe_sqr、 caffe_exp、 caffe_abs:y[i] = a[i] ^ b, y[i] = a[i]^2,y[i] = exp(a[i] ),y[i] = |a[i] |。
- caffe_rng_rand():返回一个随机数
- caffe_nextafer(const Dtype b):返回 b 最大方向上可以表示的最接近的数值。
- caffe_cpu_strided_dot(const int n, const double* x,
const int incx, const double* y, const int incy):返回 vector X 和 vector Y 的内积。 - caffe_cpu_hamming_distance(const int n, const float* x,
const float* y):返回 x 和 y 之间的海明距离。(两个等长字符串之间的海明距离是两个字符串对应位置的不同字符的个数。) - caffe_cpu_asum(const int n, const float* x):计算 vector x 的所有element的绝对值之和。
- caffe_cpu_scale(const int n, const float alpha, const float x,
float y):y = alpha*x。
Caffe.io
作用:io.cpp 主要定义了一些读取图像或者文件,以及它们之间的一些转化的函数。
代码地址:
caffe_root_dir}/include/caffe/util/io.hpp
{caffe_root_dir}/src/caffe/util/io.cpp
函数及功能:
- ReadProtoFromTextFile(const char* filename, Message* proto):从prototxt文件中读取message参数。
- WriteProtoToTextFile(const Message& proto, const char* filename) :将message写到prototxt文件中。
- ReadProtoFromBinaryFile(const char* filename, Message* proto):从二进制文件中读取message 参数。
- writeProtoToBinaryFile(const Message& proto, const char* filename):将message写为一个二进制文件。
- ReadImageToCVMat(const string& filename,const int height, const int width, const bool is_color):以cvMat格式读入图像。
- matchExt(const std::string & fn,std::string en):匹配文件后缀名。
- CVMatToDatum(const cv::Mat& cv_img, Datum* datum):cvMat 格式数据转化为Datum格式。
- ReadFileToDatum(const string& filename, const int label,Datum* datum):读取文件为Datum格式。
- ReadImageToDatum(const string& filename, const int label,const int height, const int width, const bool is_color, const std::string & encoding, Datum* datum):读取图像为Datum格式。
- DecodeDatumToCVMat(const Datum& datum, bool is_color):将encode的Datum格式转为cvMat格式。
- DecodeDatum(Datum* datum, bool is_color):将encodedDatum转化为没有encode的Datum。