【问题标题】:Running Tensorflow Classifier Model in C++/Obj-C++ results in different result than Python在 C++/Obj-C++ 中运行 Tensorflow 分类器模型的结果与 Python 不同
【发布时间】:2017-06-05 15:45:08
【问题描述】:

我通过Tensorflow for Poets 教程重新训练了一个 InceptionV3 模型,并且可以成功地在我训练的数据和新数据上运行 label_image.py 并获得准确的正确标签。太棒了!

如果我通过我的 Mac Obj-C++ 应用程序运行我的模型,我得到的标签会大不相同。

例如 - 我的训练是对视频帧的“镜头类型”进行分类(超近、近、中、长、超长),以对视频编辑内容进行分类。

label_image.py 将视频中的帧分类为 85% 的可能特写。 我的 C++ / Obj-C 应用程序以相同的帧运行,将其归类为 Extreme Long,占 60%

两者都在使用 AVX/SIMD/FMA 优化编译的 Mac OS X CPU 上运行相同版本的 Tensorflow (1.1)。

我的应用程序管道:

我有一个 BGR 订购的 OpenCV Mat 图像,我可以在其他地方成功使用它并从中获得合理的结果。我从映射到 BGRA CV MAT 的 OS X CVPixelBufferRef 创建这个 CV Mat,如下所示:

cv::cvtColor(BGRAImage, frameMat, cv::COLOR_BGRA2BGR);

我通过从 iOS 贡献示例中借用的代码将 BGR CV Mat(名为 frameMat)输入到张量中,如下所示:

 void* baseAddress = (void*)frameMat.datastart;
 size_t width = (size_t) frameMat.cols;
 size_t height = (size_t) frameMat.rows;
 size_t bytesPerRow =  (size_t) frameMat.cols * 3; // (BGR)

 const int wanted_input_width = 299;
 const int wanted_input_height = 299;
 const int wanted_input_channels = 3;
 const float input_mean = 128.0f;
 const float input_std = 128.0f;

 resized_tensor = tensorflow::Tensor( tensorflow::DT_FLOAT, tensorflow::TensorShape({1, wanted_input_height, wanted_input_width, wanted_input_channels}));

auto image_tensor_mapped = resized_tensor.tensor<float, 4>();
tensorflow::uint8 *in = sourceStartAddr;
float *out = image_tensor_mapped.data();
for (int y = 0; y < wanted_input_height; ++y)
{
    float *out_row = out + (y * wanted_input_width * wanted_input_channels);
    for (int x = 0; x < wanted_input_width; ++x)
    {
        const int in_x = (y * (int)width) / wanted_input_width;
        const int in_y = (x * image_height) / wanted_input_height;

        tensorflow::uint8 *in_pixel = in + (in_y * width * (image_channels)) + (in_x * (image_channels));
        float *out_pixel = out_row + (x * wanted_input_channels);

        // Interestingly the iOS example uses BGRA and DOES NOT re-order tensor channels to RGB <-> BGR
        // Matching that.
        out_pixel[0] = ((float)in_pixel[0] - (float)input_mean) / (float)input_std;
        out_pixel[1] = ((float)in_pixel[1] - (float)input_mean) / (float)input_std;
        out_pixel[2] = ((float)in_pixel[2] - (float)input_mean) / (float)input_std;
    }
}

我的会话创建代码:

 tensorflow::Status load_graph_status = ReadBinaryProto(tensorflow::Env::Default(), [inception2015GraphPath cStringUsingEncoding:NSUTF8StringEncoding], &inceptionGraphDef);

 if (load_graph_status.ok())
 {      
      tensorflow::SessionOptions options;        
      inceptionSession = std::unique_ptr<tensorflow::Session>(tensorflow::NewSession(options));        
      tensorflow::Status session_create_status = inceptionSession->Create(inceptionGraphDef);
 }

运行图表:

    tensorflow::Status run_status = inceptionSession->Run({ {input_layer, resized_tensor} }, {feature_layer, final_layer}, {}, &outputs);

并拉出标签/特征向量(倒数第二层)

    NSMutableArray* outputLabels = [NSMutableArray arrayWithCapacity:self.labelsArray.count];
NSMutableArray* outputScores = [NSMutableArray arrayWithCapacity:self.labelsArray.count];

// 1 = labels and scores
auto predictions = outputs[1].flat<float>();

for (int index = 0; index < predictions.size(); index += 1)
{
    const float predictionValue = predictions(index);

    NSString* labelKey  = self.labelsArray[index % predictions.size()];

    NSNumber* currentLabelScore = self.averageLabelScores[labelKey];

    NSNumber* incrementedScore = @([currentLabelScore floatValue] + predictionValue );
    self.averageLabelScores[labelKey] = incrementedScore;

    [outputLabels addObject:labelKey];
    [outputScores addObject:@(predictionValue)];
}

// 0 is feature vector
tensorflow::Tensor feature = outputs[0];
int64_t numElements = feature.NumElements();
tensorflow::TTypes<float>::Flat featureVec = feature.flat<float>();

NSMutableArray* featureElements = [NSMutableArray arrayWithCapacity:numElements];

for(int i = 0; i < numElements; i++)
{
    [featureElements addObject:@( featureVec(i) ) ];
}

if(self.averageFeatureVec == nil)
{
    self.averageFeatureVec = featureElements;
}
else
{
    // average each vector element with the prior
    for(int i = 0; i < featureElements.count; i++)
    {
        float  a = [featureElements[i] floatValue];
        float  b = [self.averageFeatureVec[i] floatValue];

        self.averageFeatureVec[i] = @( MAX(a,b)) ;
    }
}

return @{ kSynopsisStandardMetadataFeatureVectorDictKey : featureElements ,
          @"Labels" : outputLabels,
          @"Scores" : outputScores,
          };

我试图研究张量排序 (NHWC),并检查了张量创建代码,但我可能遗漏了一些对其他人来说显而易见的东西。我也尝试过更改频道顺序,但无济于事。

任何见解都会非常有帮助。谢谢!

【问题讨论】:

    标签: python c++ opencv machine-learning tensorflow


    【解决方案1】:

    我调试此类问题的常用方法是:

    • 首先从我知道有效的示例输入中保存一个原始 C 值数组。例如,确保 label_image 适用于您新训练的模型,然后使用如下伪代码写出您从 input_layer-&gt;flat&lt;float&gt;().data() 获得的 float* 数组:

    float* input_data = input_layer->flat<float>().data(); int input_data_count = input_layer->flat<float>().size(); printf("float g_test_input[]={\n"); for (int i = 0; i < input_data_count; ++i) { printf(" %f,\n", input_data[i]); } printf("};\n");

    • 您最终应该得到一个大数组,您可以将其复制到新代码中。覆盖您要测试的代码中的任何输入。现在运行它,您应该会看到与 label_image 相同的输出。如果你不这样做,你就会知道你正在加载的模型有些不同。如果输出相同,那么你就知道输入预处理不同了。

    • 假设这是错误的预处理,我的下一步是尝试从磁盘加载图像。 iOS 示例代码在简单示例中执行此操作。将您预期的一些输入保存到图像文件中,然后确保 label_image 和您的代码产生相同的结果。

    【讨论】:

    • 谢谢皮特 - 我没有提到(我认为这无关紧要)我通过 graph_transform 工具从 retrain.py 运行图表,使用这些参数:bazel-bin/tensorflow/tools/ graph_transforms/transform_graph --in_graph="in.pb" --out_graph="out.pb" --inputs='Mul' --outputs='pool_3, final_result' --transforms='strip_unused_nodes(type=float, shape= "1,229,229,3") remove_nodes(op=Identity, op=CheckNumerics) fold_constants(ignore_errors=true) fold_batch_norms fold_old_batch_norms quantize_weights sort_by_execution_order fuse_resize_pad_and_conv' 删除 quantize_weights 修复它。
    【解决方案2】:

    所以这个很棘手。

    我没有提到我正在重新训练的图表上运行 graph_transform 工具 - 并且正在运行量化权重以减小我的图表大小。过去,我完全没有遇到过这种混乱的分类分数的问题,但显然这导致了问题。

    在没有量化权重的情况下使用图形转换调用运行上述代码解决了该问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-04
      相关资源
      最近更新 更多