Caffe提供了较好的训练API,不过对于输出并不是很友好,只能看到训练的准确率,Loss值。
虽然也有相应的例程,比如tools下的extract_features.cpp , example文件夹下的cpp_classification,但是效果都不好。比如第一个虽然提取了特征,但是将特征保存为leveldb格式,这让我怎么用,第二个稍微改动可以做相应输出,但是加载一次模型只预测一个结果,如果要一次预测上万的结果那可有的等了。
下面是我根据extract_features.cpp 改写的特征层输出文件,放在tools下通过cmake以及make指令可以得到相应的执行文件。使用方法如下:
get_features XXX.prototxt XXX.caffemodel XXXfeature_blob,YYYfeatre_blob XXX.txt num_mini_batches
第一个是可执行文件名,接着是配置文件,caffe模型,要输出层的名字,要输出的图片地址放在txt中(后面随便加上一个标签),这个数值*配置文件中的batchsize决定了输出的数量
#include <caffe/caffe.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <algorithm>
#include <iosfwd>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <iostream>
#include <fstream>
#include "boost/algorithm/string.hpp"
using namespace caffe; // NOLINT(build/namespaces)
using std::string;
int main(int argc, char** argv) {
const int num_required_args = 6;
if (argc < num_required_args) {
LOG(ERROR) <<
"This program takes in a trained network and then extract features of the net.\n"
"Usage: get_features XXX.prototxt XXX.caffemodel XXXfeature_blob,YYYfeatre_blob XXX.txt num_mini_batches\n"
"Note: the last parameter is the txt save image names.\n";
return 1;
}
string model_file = argv[1]; //prototxt
string trained_file = argv[2]; //caffemodel
std::string extract_feature_blob_names(argv[3]); //比如fc7,prob 层的名字
std::vector<std::string> blob_names;
boost::split(blob_names, extract_feature_blob_names, boost::is_any_of(","));
const int num_features = blob_names.size();
//
string batch_str = argv[5];
const int num_mini_batches = atoi(batch_str.data());
boost::shared_ptr<Net<float> > feature_extraction_net(new Net<float>(model_file, caffe::TEST));
Caffe::set_mode(Caffe::GPU);
feature_extraction_net->CopyTrainedLayersFrom(trained_file);
// Check if the feature blob exist
for (size_t i = 0; i < blob_names.size(); i++) {
CHECK(feature_extraction_net->has_blob(blob_names[i]))
<< "Unknown feature blob name " << blob_names[i]
<< " in the network " << model_file;
}
std::cout << "Extracting Features";
std::vector<std::vector<std::vector<float> > > output(num_features);
std::vector<float> tmp;
for (int batch_index = 0; batch_index < num_mini_batches; ++batch_index) {
std::cout<<batch_index<<std::endl; //输出运行到第几个batch
feature_extraction_net->Forward();
for (int i = 0; i < num_features; ++i) { //num_features 提取的特征层的个数
const boost::shared_ptr<Blob<float> > feature_blob = feature_extraction_net->blob_by_name(blob_names[i]);//获得要提取特征层的指针
int batch_size = feature_blob->num(); //
int dim_features = feature_blob->count() / batch_size; //每个输入的特征维度
const float* feature_blob_data; //指针
for (int n = 0; n < batch_size; ++n) {
feature_blob_data = feature_blob->cpu_data() + feature_blob->offset(n); //每个batch偏移一次指针 偏移量为batch_size*dim_feature
tmp.clear();
for(int d = 0;d<dim_features;++d){
tmp.push_back(feature_blob_data[d]);
}
output[i].push_back(tmp);
}
}
}
std::cout<<"output size:"<<output[0].size()<<std::endl;
//写入获取的特征值
//这里可以不看,是将得到的结果输出到csv文件中
string img_name = argv[4];
std::ifstream f(img_name.data(), ios::in);
std::vector<string> imgs;
string img;
while (getline(f, img)){
imgs.push_back(img);
}
f.close();
std::cout<<"imgs:"<<imgs.size()<<std::endl;
for (int m = 0; m < num_features; ++m){
string output_name = "./out_" + blob_names[m] + ".csv";
std::ofstream out(output_name.data(), ios::out);
out << "c0,c1,c2,c3,c4,c5,c6,c7,c8,c9,img" << "\n";
for (int i = 0; i<imgs.size(); ++i){
for (int j = 0; j<output[m][i].size(); ++j){
out << output[m][i][j] << ",";
}
out << imgs[i] << "\n";
}
out.close();
}
return 0;
}