在C /OBJ-C 中运行Tensorflow分类器模型与Python的结果不同

Running Tensorflow Classifier Model in C++/Obj-C++ results in different result than Python

本文关键字:Python 结果 模型 分类器 OBJ-C Tensorflow 运行      更新时间:2023-10-16

ive通过Tensorflow的诗人教程进行了InceptionV3模型,并且可以在我的训练有素的数据和新数据上成功运行Label_image.py,并以良好的精度获得正确的标签。太棒了!

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

例如 - 我的培训是对哪种视频框架进行分类,(极端闭合,近距离,中等,长,极端长(用于分类视频编辑内容。

label_image.py将视频中的框架分类为85%的可能关闭。我的C /OBJ-C应用程序以相同的框架运行,将其分类为超过60%

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

我的应用程序管道:

我有一个BGR订购的OpenCV垫图像,我可以在其他地方成功使用,并从中获得理智的结果。我从OS X CVPixelBufferRef创建此CV垫,映射到BGRA CV垫子,如:

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

i通过从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(,并检查了张量创建代码,但我可能缺少其他人明显的东西。我还尝试更改频道顺序,但无济于事。

任何洞察力都会非常有帮助。谢谢!

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

  • 首先从我知道有效的示例输入中保存原始的c值。例如,请确保Label_image与您的新训练模型一起使用,然后使用伪代码来写出您从input_layer->flat<float>().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工具 - 并且正在运行量化权重以降低图形的大小。过去,我根本没有这个混乱的分类分数有问题,但是显然这引起了问题。

使用图形转换调用的上述代码未经量化权重解决问题。