我通过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调用上运行上述代码解决了这个问题。