caffecpp代码解析.docx
《caffecpp代码解析.docx》由会员分享,可在线阅读,更多相关《caffecpp代码解析.docx(17页珍藏版)》请在冰豆网上搜索。
![caffecpp代码解析.docx](https://file1.bdocx.com/fileroot1/2023-1/21/30160da8-7259-4dd4-ba99-71894251cdc6/30160da8-7259-4dd4-ba99-71894251cdc61.gif)
caffecpp代码解析
main()函数--->>GetBrewFunction函数--->>train函数--->>Solve()
#ifdefWITH_PYTHON_LAYER
#include"boost/python.hpp"
namespacebp=boost:
:
python;
#endif
#include
#include
#include
#include
#include
#include
#include"boost/algorithm/string.hpp"
#include"caffe/caffe.hpp"
#include"caffe/util/signal_handler.h"
usingcaffe:
:
Blob;
usingcaffe:
:
Caffe;
usingcaffe:
:
Net;
usingcaffe:
:
Layer;
usingcaffe:
:
Solver;
usingcaffe:
:
shared_ptr;
usingcaffe:
:
string;
usingcaffe:
:
Timer;
usingcaffe:
:
vector;
usingstd:
:
ostringstream;
/*gflags是google的一个开源的处理命令行参数的库。
在使用命令行参数的文件文件中(源文件或头文件),首先使用一下定义语句进行变量的定义。
DEFINE_int32,DEFINE_int64,DEFINE_bool,DEFINE_double,DEFINE_string等,语法为:
DEFINE_int32(name,default_value,"description")。
接着你就可以使用FLAGS_name变量了,这些变量的值则是由命令行参数传递,无则为默认值,在其他代码文件中若想用该命令参数,可以用DECLARE_int32(name)声明(name为int32类型,也可以使用其他支持的类型)。
在caffe.cpp中有很多FLAGS_name定义,如DEFINE_string(gpu,"","somedescription"),则命令行后-gpu0,表示FLAGS_gpu=0,默认值为空。
*/
DEFINE_string(gpu,"",
"Optional;runinGPUmodeongivendeviceIDsseparatedby','."
"Use'-gpuall'torunonallavailableGPUs.Theeffectivetraining"
"batchsizeismultipliedbythenumberofdevices.");
DEFINE_string(solver,"",
"Thesolverdefinitionprotocolbuffertextfile.");
DEFINE_string(model,"",
"Themodeldefinitionprotocolbuffertextfile.");
DEFINE_string(phase,"",
"Optional;networkphase(TRAINorTEST).Onlyusedfor'time'.");
DEFINE_int32(level,0,
"Optional;networklevel.");
DEFINE_string(stage,"",
"Optional;networkstages(nottobeconfusedwithphase),"
"separatedby','.");
DEFINE_string(snapshot,"",
"Optional;thesnapshotsolverstatetoresumetraining.");
DEFINE_string(weights,"",
"Optional;thepretrainedweightstoinitializefinetuning,"
"separatedby','.Cannotbesetsimultaneouslywithsnapshot.");
DEFINE_int32(iterations,50,
"Thenumberofiterationstorun.");
DEFINE_string(sigint_effect,"stop",
"Optional;actiontotakewhenaSIGINTsignalisreceived:
"
"snapshot,stopornone.");
DEFINE_string(sighup_effect,"snapshot",
"Optional;actiontotakewhenaSIGHUPsignalisreceived:
"
"snapshot,stopornone.");
//Asimpleregistryforcaffecommands.
typedefint(*BrewFunction)();/*声明了一个BrewFunction函数指针类型,可以用它来定义一个函数指针*/
typedefstd:
:
map:
string,BrewFunction>BrewMap;/*声明了一个BrewFunction函数指针类型,可以用它来定义一个函数指针,创建了一个名为BrewMap,并且包含caffe:
:
string类型数据的map空对象,该对象使用BrewFunction函数来对集合中的元素进行排序*/
BrewMapg_brew_map;/*定义key为string的map容器实例*/
/*这里巧妙的用宏定义的方式声明了分别包含train(),test(),device_query(),time()四个函数的四个不同类*//*理解这个关键理解宏在预编译阶段是如何被展开*/
#defineRegisterBrewFunction(func)\
namespace{\
class__Registerer_##func{\
public:
/*NOLINT*/\
__Registerer_##func(){\
g_brew_map[#func]=&func;\
}\
};\
__Registerer_##funcg_registerer_##func;\
}
//在C/C++的宏中,"#"的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号。
//”##”被称为连接符(concatenator),用来将两个子串Token连接为一个Token。
注意这里连接的对象是Token就行,而不一定是宏的变量。
所谓的子串(token)就是指编译器能够识别的最小语法单元
举例说明##用法:
#definePRINT(n)printf(token#n=%d,token##n)
同时又定义了二个整形变量:
inttoken9=9;
现在在主程序中以下面的方式调用这个宏:
PRINT(9);
那么在编译时,上面的这句话被扩展为:
printf(token9=%d,token9);
注意到在这个例子中,PRINT(9);中的这个”9”被原封不动的当成了一个字符串,与”token”连接在了一起,从而成为了token9。
而#n也被”9”所替代。
/*在caffe.cpp中BrewFunction作为GetBrewFunction()函数的返回类型,可以是train(),test(),device_query(),time()这四个函数指针的其中一个。
在train(),test(),中可以调用solver类的函数,从而进入到net,进入到每一层,运行整个caffe程序。
*/
/*1:
加了static后表示该函数失去了全局可见性,只在该函数所在的文件作用域内可见
2:
当函数声明为static以后,编译器在该目标编译单元内只含有该函数的入口地址,没有函数名,其它编译单元便不能通过该函数名来调用该函数,这也是对1的解析与说明*/
staticBrewFunctionGetBrewFunction(constcaffe:
:
string&name){
if(g_brew_map.count(name)){//判断输入的是不是g_brew_map中train,test,device_query,time中一个
returng_brew_map[name];//如果是的话,就调用相应的train(),test(),device_query(),time()
}else{
LOG(ERROR)<<"Availablecaffeactions:
";
for(BrewMap:
:
iteratorit=g_brew_map.begin();
it!
=g_brew_map.end();++it){
LOG(ERROR)<<"\t"<first;//LOG来源于google的glog库,控制程序的日志输出消息和测试消息(根据不同的level输出消息)
}
LOG(FATAL)<<"Unknownaction:
"<returnNULL;//notreachable,justtosuppressoldcompilerwarnings.
}
}
//ParseGPUidsoruseallavailabledevices
//解析可用GPU,使用所有可用硬件
staticvoidget_gpus(vector*gpus){
if(FLAGS_gpu=="all"){
intcount=0;
#ifndefCPU_ONLY
CUDA_CHECK(cudaGetDeviceCount(&count));
#else
NO_GPU;
#endif
for(inti=0;igpus->push_back(i);
}
}elseif(FLAGS_gpu.size()){
vectorstrings;
boost:
:
split(strings,FLAGS_gpu,boost:
:
is_any_of(","));
for(inti=0;igpus->push_back(boost:
:
lexical_cast(strings[i]));
}
}else{
CHECK_EQ(gpus->size(),0);
}
}
//Parsephasefromflags
caffe:
:
Phaseget_phase_from_flags(caffe:
:
Phasedefault_value){
if(FLAGS_phase=="")
returndefault_value;
if(FLAGS_phase=="TRAIN")
returncaffe:
:
TRAIN;
if(FLAGS_phase=="TEST")
returncaffe:
:
TEST;
LOG(FATAL)<<"phasemustbe\"TRAIN\"or\"TEST\"";
returncaffe:
:
TRAIN;//Avoidwarning
}
//Parsestagesfromflags
vectorget_stages_from_flags(){
vectorstages;
boost:
:
split(stages,FLAGS_stage,boost:
:
is_any_of(","));
returnstages;
}
//caffecommandstocallby
//caffe
//
//Toaddacommand,defineafunction"intcommand()"andregisteritwith
//RegisterBrewFunction(action);
//DeviceQuery:
showdiagnosticinformationforaGPUdevice.
intdevice_query(){/*这里定义device_query函数*/
LOG(INFO)<<"QueryingGPUs"<vectorgpus;
get_gpus(&gpus);/*获得有几个GPU*/
for(inti=0;icaffe:
:
Caffe:
:
SetDevice(gpus[i]);
caffe:
:
Caffe:
:
DeviceQuery();
}
return0;
}
RegisterBrewFunction(device_query);/*这里通过预编译阶段的宏替换,将定义的device_query函数指针赋值到map容器中*/
/*加载训练的或者传入的模型*/
//Loadtheweightsfromthespecifiedcaffemodel(s)intothetrainand
//testnets.
voidCopyLayers(caffe:
:
Solver*solver,conststd:
:
string&model_list){
std:
:
vector:
string>model_names;
boost:
:
split(model_names,model_list,boost:
:
is_any_of(","));
for(inti=0;iLOG(INFO)<<"Finetuningfrom"<solver->net()->CopyTrainedLayersFrom(model_names[i]);
for(intj=0;jtest_nets().size();++j){
solver->test_nets()[j]->CopyTrainedLayersFrom(model_names[i]);
}
}
}
//将交互端传来的string类型的标志转成枚举类型的变量
//Translatethesignaleffecttheuserspecifiedonthecommand-linetothe
//correspondingenumeration.
caffe:
:
SolverAction:
:
EnumGetRequestedAction(
conststd:
:
string&flag_value){
if(flag_value=="stop"){
returncaffe:
:
SolverAction:
:
STOP;
}
if(flag_value=="snapshot"){
returncaffe:
:
SolverAction:
:
SNAPSHOT;
}
if(flag_value=="none"){
returncaffe:
:
SolverAction:
:
NONE;
}
LOG(FATAL)<<"Invalidsignaleffect\""<returncaffe:
:
SolverAction:
:
NONE;
}
//Train/Finetuneamodel.
/*训练或者微调网络都是走这个分支*/
inttrain(){/*定义train函数*/
//google的glog库,检查--solver、--snapshot和--weight并输出消息;必须有指定solver,并且snapshot和weight两者只需指定其一;
CHECK_GT(FLAGS_solver.size(),0)<<"Needasolverdefinitiontotrain.";
CHECK(!
FLAGS_snapshot.size()||!
FLAGS_weights.size())
<<"Giveasnapshottoresumetrainingorweightstofinetune"
"butnotboth.";
vectorstages=get_stages_from_flags();
/*实例化SolverParameter类,该类保存solver参数和相应的方法,SolverParameter是通过GoogleProtocolBuffer自动生成的一个类*/
caffe:
:
SolverParametersolver_param;/*定义SolverParameter的对象,该类保存solver参数和相应的方法*/
caffe:
:
ReadSolverParamsFromTextFileOrDie(FLAGS_solver,&solver_param);
solver_param.mutable_train_state()->set_level(FLAGS_level);
for(inti=0;isolver_param.mutable_train_state()->add_stage(stages[i]);
}
//Ifthegpusflagisnotprovided,allowthemodeanddevicetobeset
//inthesolverprototxt.
if(FLAGS_gpu.size()==0//根据命令参数-gpu或者solver.prototxt提供的信息设置GPU
&&solver_param.solver_mode()==caffe:
:
SolverParameter_SolverMode_GPU){
if(solver_param.has_device_id()){
FLAGS_gpu=""+
boost:
:
lexical_cast(solver_param.device_id());
}else{//SetdefaultGPUifunspecified
FLAGS_gpu=""+boost:
:
lexical_cast(0);//boost:
:
lexical_cast(0)是将数值0转换为字符串'“0”;
}
}
/*上述代码:
:
:
:
首先是判断用户在CommandLine中是否输入了gpu相关的参数,如果没有(FLAGS_gpu.size()==0)但是用户在solver的prototxt定义中提供了相关的参数,那就把相关的参数放到FLAGS_gpu中,如果用户仅仅是选择了在solver的prototxt定义中选择了GPU模式,但是没有指明具体的gpu_id,那么就默认设置为0。
*/
//多GPU下,将GPU编号存入vector容器中(get_gpus()函数通过FLAGS_gpu获取)
vectorgpus;
get_gpus(&gpus);
if(gpus.size()==0){
LOG(INFO)<<"UseCPU.";
Caffe:
:
set_mode(Caffe:
:
CPU);
}else{
ostringstreams;
for(inti=0;is<<(i?
",":
"")<}
LOG(INFO)<<"UsingGPUs"<#ifndefCPU_ONLY
cudaDevicePropdevice_prop;
for(inti=0;icudaGetDeviceProperties(&device_prop,gpus[i]);
LOG(INFO)<<"GPU"<"<}
#endif
solver_param.set_device_id(gpus[0]);
Caffe:
:
SetDevice(gpus[0]);
Caffe:
:
set_mode(Caffe:
:
GPU);
Caffe:
:
set_solver_count(gpus.size());
}
//处理snapshot,stopornone信号,其声明在include/caffe/util/signal_Handler.h中;//GetRequestedAction在caffe.cpp中,将‘stop’,‘snapshot’,‘none’转换为标准信号,即解析;
caffe:
:
SignalHandlersignal_handler(
GetRequestedAction(FLAGS_sigint_effect),
GetRequestedAction(FLAGS_sighup_effect));
//声明boost库中智能指针solver,指向caffe:
:
Solver对象,该对象由CreateSolver创建,后续细讲;
shared_ptr:
Solver>
solver(caffe:
:
SolverRegistry:
:
CreateSolver(solver_param));
/*通过GetActionFunction来处理获得的系统信号*//*在SetActionFunction中将GetActionFunction函数地址传给参数action_request_function_*//*在网络训练的过程中,在GetRequestedAction中来处理action_request_function_得到的函数指针*/
s