使用TensorFlow C-API加载LeNet模型时输入设置失败求助
我尝试用TensorFlow C-API运行从Keras/TF导出的LeNet模型,但在设置输入时一直出问题。相关代码片段如下:
// Load the image with openCV CvMat * img = cvLoadImageM(argv[1], CV_LOAD_IMAGE_COLOR ); // Create an Tensor from the image int64_t dims4[]={1,1,28,28}; TF_Tensor * imgTensor = TF_NewTensor(TF_FLOAT,dims4,4,img,28*28*sizeof(float),NULL,NULL); TF_Operation* init_op2 = TF_GraphOperationByName(graph, "conv2d_1_input"); TF_Operation* targets[] = {init_op2}; // Build up the inputs TF_Output inp = { init_op2, 0 }; TF_Output * inputs[] = {&inp}; TF_Tensor * input_values[] = {imgTensor}; printf("\nBefore\n"); TF_SessionRun(session, NULL, &inp, input_values, 1, // inputs NULL, NULL, 0, // outputs &init_op2, 1, // targets NULL, status); printf("After\n"); printf("Status %d %s\n", TF_GetCode(status), TF_Message(status));
无论怎么构建输入张量,都会收到错误:
Status 3 You must feed a value for placeholder tensor 'conv2d_1_input' with dtype float and shape [?,1,28,28] [[Node: conv2d_1_input = Placeholder_output_shapes=[[?,1,28,28]], dtype=DT_FLOAT, shape=[?,1,28,28], _device=...]]
请问我哪里操作有误?
我帮你梳理下几个关键的错误点,逐个修正就能解决问题:
1. 图像数据类型与通道数不匹配
你用CV_LOAD_IMAGE_COLOR加载的是3通道彩色图,但模型输入明确要求单通道(shape [?,1,28,28]),而且OpenCV默认加载的图像是uint8类型,和模型需要的float类型不匹配——直接把CvMat的指针传给TF_NewTensor,相当于把8位整数当成32位浮点数用,数据完全错乱。
先把图像转成单通道灰度图,再转换为float类型并做归一化(和你训练模型时的预处理保持一致):
// 加载为单通道灰度图,而非彩色图 CvMat *img = cvLoadImageM(argv[1], CV_LOAD_IMAGE_GRAYSCALE); // 创建float类型的Mat存储转换后的数据 CvMat *float_img = cvCreateMat(img->rows, img->cols, CV_32FC1); cvConvertScale(img, float_img, 1.0/255.0, 0); // 归一化到0-1区间,符合模型训练逻辑
2. TF_SessionRun的参数逻辑错误
你把conv2d_1_input这个占位符节点放到了targets参数里,这是完全错误的逻辑。Targets参数是用来指定要执行的操作(比如模型的初始化操作、推理/训练操作),而占位符只是用来接收输入数据的载体,根本不需要被"执行"。
正确的用法是:
- 如果模型还没初始化,先找到模型里的初始化节点(比如叫
init,用TF_GraphOperationByName查找),把它放到targets里执行初始化; - 输入部分只需要在
inputs和input_values里传入占位符和对应张量即可,不用碰targets。
修正后的TF_SessionRun调用示例:
// 找到模型的初始化操作(名称根据你的实际导出模型调整) TF_Operation* init_op = TF_GraphOperationByName(graph, "init"); printf("\nBefore\n"); // 先执行初始化(如果已经初始化过可以跳过这一步) TF_SessionRun(session, NULL, inputs, input_values, 1, // 传入输入占位符和对应张量 NULL, NULL, 0, // 暂时不需要输出 &init_op, 1, // 要执行的初始化操作 NULL, status); // 检查初始化状态 if (TF_GetCode(status) != TF_OK) { printf("Init failed: %s\n", TF_Message(status)); return -1; } // 后续再运行模型获取输出(找到输出节点放到outputs参数即可)
3. 张量内存管理的潜在风险
TF_NewTensor直接传入img的指针,如果后续img被释放,张量会指向无效内存。最好设置释放回调函数,让TensorFlow在销毁张量时自动释放对应的OpenCV Mat,避免内存泄漏:
// 定义张量释放回调函数 void DeallocateTensor(void* data, size_t len, void* arg) { cvReleaseMat((CvMat**)&arg); } // 创建张量时传入回调和对应的float_img指针 TF_Tensor *imgTensor = TF_NewTensor(TF_FLOAT, dims4, 4, float_img->data.fl, 28*28*sizeof(float), DeallocateTensor, float_img);
总结修正步骤
- 将图像转为单通道灰度图,转换为float类型并归一化;
- 修正
TF_SessionRun参数,不要把占位符放到targets中; - 确保张量的维度、数据类型和模型输入要求完全匹配;
- 配置张量的内存释放回调,避免内存泄漏和野指针问题。
内容的提问来源于stack exchange,提问作者Andreas Graf

