使用Caffe内存层不会产生一致且确定的结果

我在Windows 7 64位计算机上使用Windows的Caffe框架(从here下载) . 我在Visual Studio Community 2013中使用C.我使用预先训练的GoogLeNet模型来提取loss1-fc图层输出,以用作每个图像的特征向量 . 到现在为止还挺好 .

最近我尝试更改我的软件用于视频帧 . 所以我将第一层从ImageData层更改为Memory层,因此我可以向Caffe发送一个OpenCV垫的向量,而不是将每个帧写入磁盘并将文件列表发送到caffe的天真方法 .

现在,我注意到我不会得到相同图像的相同结果!使用ImageData图层时,没有这样的东西 .

我使用CPU(没有Cudnn,没有GPU) .

我用于特征提取的功能如下:

void feature_extraction_pipeline_memory(boost::shared_ptr<Net<Dtype>> feature_extraction_net, vector<cv::Mat> imgs, vector<int> labels, float** blobFeats, vector<string> blob_names){

    boost::dynamic_pointer_cast<caffe::MemoryDataLayer<float>>(feature_extraction_net->layers()[0])->AddMatVector(imgs, labels);

    size_t num_mini_batches = imgs.size();
    size_t num_features = blob_names.size();
    int dim_features;
    int batch_size;
    vector<Blob<float>*> input_vec;
    vector<int> image_indices(num_features, 0);

    for (size_t batch_index = 0; batch_index < num_mini_batches; ++batch_index) {
        feature_extraction_net->Forward(input_vec);
        for (size_t i = 0; i < num_features; ++i) {
            const boost::shared_ptr<Blob<Dtype>> feature_blob =     feature_extraction_net->blob_by_name(blob_names[i]);
            batch_size = feature_blob->num();
            dim_features = feature_blob->count() / batch_size;
            const Dtype* feature_blob_data;
            for (size_t n = 0; n < batch_size; ++n) {
                feature_blob_data = feature_blob->cpu_data() + feature_blob->offset(n);
                for (size_t d = 0; d < dim_features; ++d)
                    blobFeats[i][(image_indices[i] * dim_features) + d] = feature_blob_data[d];

                ++image_indices[i];
            }  // n < batch_size
        }  // i < num_features
    }  // batch_index < num_mini_batches
}

imgs 向量是mat的向量 . labels 是int的向量,全部设置为0.我们将所有图像添加到向量后再次将其写入磁盘 . 我查了一下,没问题 . 所以加载图像时没有错 . 顺便说一句,我使用OpenCV 3.1 .

GoogLeNet prototxt文件中的内存层声明如下:

layer {
  name: "data"
  type: "MemoryData"
  top: "data"
  top: "label"
  memory_data_param {
   batch_size: 1
   channels: 3
   height: 227
   width: 227
  }
  transform_param {
    crop_size: 227
    mirror: true
    mean_file: "model_googlenet_mem/imagenet_mean.binaryproto"
  }
  include: { phase: TEST }
}

并且是第一层 .

我打印每个图像的前10个值 . 请注意,图像0,1,2,3是复制的完全相同的文件,同样适用于6,7和8图像 .

1st run:
0.jpg ::  3.149, 0.000, 0.000, 0.000, 1.586, 0.000, 0.000, 0.755, 0.000, 4.749,
1.jpg ::  2.680, 0.000, 0.000, 0.560, 0.970, 0.000, 0.000, 1.083, 0.000, 4.420,
2.jpg ::  2.680, 0.000, 0.000, 0.560, 0.970, 0.000, 0.000, 1.083, 0.000, 4.420,
3.jpg ::  2.680, 0.000, 0.000, 0.560, 0.970, 0.000, 0.000, 1.083, 0.000, 4.420,
4.jpg ::  3.957, 0.000, 0.000, 0.000, 0.868, 0.000, 0.000, 0.000, 0.000, 6.396,
5.jpg ::  3.179, 0.000, 0.000, 0.000, 0.906, 0.000, 0.000, 0.000, 0.000, 5.508,
6.jpg ::  4.951, 0.000, 0.000, 0.000, 0.000, 0.343, 2.993, 0.000, 0.000, 0.000,
7.jpg ::  4.567, 0.000, 0.000, 0.000, 0.000, 1.251, 2.446, 0.000, 0.000, 0.000,
8.jpg ::  4.951, 0.000, 0.000, 0.000, 0.000, 0.343, 2.993, 0.000, 0.000, 0.000,
9.jpg ::  5.678, 0.000, 0.000, 2.010, 0.000, 1.064, 2.412, 0.000, 0.000, 0.000,

第二轮:

0.jpg ::  2.680, 0.000, 0.000, 0.560, 0.970, 0.000, 0.000, 1.083, 0.000, 4.420,
1.jpg ::  2.680, 0.000, 0.000, 0.560, 0.970, 0.000, 0.000, 1.083, 0.000, 4.420,
2.jpg ::  3.149, 0.000, 0.000, 0.000, 1.586, 0.000, 0.000, 0.755, 0.000, 4.749,
3.jpg ::  2.680, 0.000, 0.000, 0.560, 0.970, 0.000, 0.000, 1.083, 0.000, 4.420,
4.jpg ::  3.957, 0.000, 0.000, 0.000, 0.868, 0.000, 0.000, 0.000, 0.000, 6.396,
5.jpg ::  2.928, 0.000, 0.000, 0.000, 0.769, 0.000, 0.000, 0.000, 0.000, 5.552,
6.jpg ::  4.567, 0.000, 0.000, 0.000, 0.000, 1.251, 2.446, 0.000, 0.000, 0.000,
7.jpg ::  4.567, 0.000, 0.000, 0.000, 0.000, 1.251, 2.446, 0.000, 0.000, 0.000,
8.jpg ::  4.951, 0.000, 0.000, 0.000, 0.000, 0.343, 2.993, 0.000, 0.000, 0.000,
9.jpg ::  5.678, 0.000, 0.000, 2.010, 0.000, 1.064, 2.412, 0.000, 0.000, 0.000,

相同图像的图层输出不同,不同的图像不同!当与ImageData层使用相同的过程时,没有这样的问题 . 此外,问题也适用于其他层的输出,例如loss3 / classifier . 因此,我怀疑MemoryLayer实现中可能存在错误 .

有人注意到这种奇怪的行为吗?我读到cudnn可能产生非确定性结果,但我在CPU上运行我的模型 . 对此有任何想法都是受欢迎的 .

回答(1)

3 years ago

我发现了什么问题,我会在这里发布帮助他人的答案 .

事实证明,GoogLeNet要求输入图像的大小为224x224x3,并且您不能在TEST阶段减去平均值 . 因此,通过将.prototxt文件中的内存层定义更改为:

name: "GoogleNet"
layer {
  name: "data"
  type: "MemoryData"
  top: "data"
  top: "label"
  memory_data_param {
    batch_size: 1
    channels: 3
    height: 224
    width: 224
  }
}
...

我得到了我预期的结果 . 非常感谢@Miki指出他们的dnn模块上的OpenCV教程,这有助于我澄清这一点 .