在C++/Obj-C++中运行Tensorflow分类模型与Python结果不同

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

但是,当我在Mac的Obj-C++应用程序中运行我的模型时,得到的结果标签却大相径庭。

例如,我的训练目的是对视频帧进行“镜头类型”分类(特写镜头、近景镜头、中景镜头、远景镜头、极远景镜头),以便对视频编辑内容进行分类。

label_image.py将视频中的一帧分类为近景镜头的可能性为85%。而我在相同的帧上运行我的C++/Obj-C应用时,却将其分类为极远景镜头,可能性为60%。

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

我的应用程序的处理流程如下:

我有一个BGR排序的OpenCV Mat图像,这个图像我可以在其他地方成功使用,并得到合理的结果。我通过将OS X的CVPixelBufferRef映射到BGRA的CV MAT来创建这个CV Mat,如下所示:

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

我将这个BGR CV Mat(名为frameMat)输入到一个Tensor中,借用了iOS contrib示例中的代码,如下所示:

 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);        // 有趣的是,iOS示例使用BGRA并且没有重新排序张量通道到RGB <-> BGR        // 匹配这个。        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 scoresauto 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 vectortensorflow::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),并检查了张量创建代码,但可能遗漏了一些对其他人来说显而易见的东西。我还尝试了更改通道顺序,但没有成功。

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


回答:

这个问题有点棘手。

我忘了提到我在重新训练的图形上运行了graph_transform工具,并且使用了量化权重来减小图形的大小。过去,我从未遇到过这会搞乱分类得分的问题,但显然这引起了问题。

在不使用量化权重的graph_transform调用上运行上述代码解决了这个问题。

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注