TensorFlow CNN实现FaceID时出现ValueError:logits与labels形状不匹配((None, 2) vs (None, 1))的问题排查
解决FaceID项目中的
ValueError: logits and labels must have the same shape问题 嘿,我来帮你排查这个错误,其实问题出在模型输出形状和标签形状不匹配,还有几个关键细节需要调整,咱们一步步来解决:
一、核心错误拆解
你现在的模型最后一层是Dense(2)加上sigmoid激活,输出形状是(None, 2)(对应两个类别的预测概率),但你的标签labels是(None, 1)的二维数组(每个样本仅存单个0/1值),这就直接导致了形状不匹配的报错。
另外,你的标签生成逻辑完全失效:
if img == img: label = 1 else: label = 0
img == img永远为True,所以所有样本的标签都是1,这根本没法训练出能区分"是/否"的二分类模型!
二、分步修复方案
1. 先修正标签生成逻辑
假设你的Data文件夹下有两个子文件夹:me(存放你的15张人脸图)和not_me(存放非本人的人脸图),咱们按文件夹自动生成对应标签:
# 修改读取数据的部分 data = [] labels = [] # 读取本人图片,标签设为1 me_images = glob.glob("C:/Users/berna/Desktop/Programming/AI_ML_DL/Projects/FaceID/Data/me/*") for img in me_images: image = cv2.imread(img) image = cv2.resize(image, (img_dims[0], img_dims[1])) image = img_to_array(image) data.append(image) labels.append(1) # 读取非本人图片,标签设为0 not_me_images = glob.glob("C:/Users/berna/Desktop/Programming/AI_ML_DL/Projects/FaceID/Data/not_me/*") for img in not_me_images: image = cv2.imread(img) image = cv2.resize(image, (img_dims[0], img_dims[1])) image = img_to_array(image) data.append(image) labels.append(0) # 预处理时把标签转成一维数组(后续匹配更方便) labels = np.array(labels, dtype="float32")
2. 调整模型输出层与损失函数匹配
这里有两种可行方案,选一种即可:
方案A:单输出二分类(推荐,更简洁)
- 修改
build函数的classes参数为1,最后一层输出单个神经元:
def build(width, height, depth, classes): # ... 前面的卷积层代码保持不变 ... # 修改最后两层 model.add(Dense(classes)) # 现在classes=1 model.add(Activation("sigmoid")) return model # 构建模型时传入classes=1 model = build(width=img_dims[0], height=img_dims[1], depth=img_dims[2], classes=1)
- 损失函数保持
binary_crossentropy不变,单输出sigmoid正好对应二分类的概率输出,和(None,1)形状的标签完美匹配。
方案B:双输出多分类方式处理二分类
如果坚持用classes=2,需要把标签转换成独热编码,同时修改激活函数和损失函数:
# 导入独热编码工具 from tensorflow.keras.utils import to_categorical # 标签转独热编码,形状变为(None,2) labels = to_categorical(labels, num_classes=2) # 修改build函数的最后一层激活为softmax def build(width, height, depth, classes): # ... 前面的卷积层代码保持不变 ... model.add(Dense(classes)) model.add(Activation("softmax")) # 替换sigmoid为softmax return model # 构建模型还是传入classes=2 model = build(width=img_dims[0], height=img_dims[1], depth=img_dims[2], classes=2) # 编译时损失函数改成categorical_crossentropy model.compile(loss="categorical_crossentropy", optimizer=opt, metrics=['accuracy'])
3. 调整batch_size参数
你只有15张样本,batch_size=64太大了,会导致训练时批次不足,建议改成2或3:
batch_size = 3 # 或者2,根据你的样本总数灵活调整
三、完整修复后的代码(以方案A为例)
# Face ID project, using CNN tensorflow from tensorflow.keras.preprocessing.image import img_to_array from tensorflow.keras.optimizers import Adam from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D, BatchNormalization, Activation from tensorflow.keras import backend as K import numpy as np import cv2 import glob # Preparing the data and parameters epochs = 10 lr = 1e-3 batch_size = 3 # 调整小批量大小 img_dims = (96,96,3) data = [] labels = [] # 分文件夹读取图片并生成标签 me_images = glob.glob("C:/Users/berna/Desktop/Programming/AI_ML_DL/Projects/FaceID/Data/me/*") for img in me_images: image = cv2.imread(img) image = cv2.resize(image, (img_dims[0], img_dims[1])) image = img_to_array(image) data.append(image) labels.append(1) not_me_images = glob.glob("C:/Users/berna/Desktop/Programming/AI_ML_DL/Projects/FaceID/Data/not_me/*") for img in not_me_images: image = cv2.imread(img) image = cv2.resize(image, (img_dims[0], img_dims[1])) image = img_to_array(image) data.append(image) labels.append(0) # Preproccesing the data (convert arrays) data = np.array(data, dtype="float32") / 255.0 labels = np.array(labels, dtype="float32") X = data y = labels def build(width, height, depth, classes): model = Sequential() inputShape = height, width, depth chanDim = -1 if K.image_data_format() == "channels_first": inputShape = depth, height, width chanDim = 1 # Creating the model model.add(Conv2D(32, (3,3), padding="same", input_shape=inputShape)) model.add(Activation("relu")) model.add(BatchNormalization(axis=chanDim)) model.add(MaxPooling2D(pool_size=(3,3))) model.add(Dropout(0.25)) model.add(Conv2D(64, (3,3), padding="same")) model.add(Activation("relu")) model.add(BatchNormalization(axis=chanDim)) model.add(Conv2D(64, (3,3), padding="same")) model.add(Activation("relu")) model.add(BatchNormalization(axis=chanDim)) model.add(MaxPooling2D(pool_size=(2,2))) model.add(Dropout(0.25)) model.add(Conv2D(128, (3,3), padding="same")) model.add(Activation("relu")) model.add(BatchNormalization(axis=chanDim)) model.add(Conv2D(128, (3,3), padding="same")) model.add(Activation("relu")) model.add(BatchNormalization(axis=chanDim)) model.add(MaxPooling2D(pool_size=(2,2))) model.add(Dropout(0.25)) model.add(Flatten()) model.add(Dense(1024)) model.add(Activation("relu")) model.add(BatchNormalization()) model.add(Dropout(0.5)) model.add(Dense(classes)) model.add(Activation("sigmoid")) return model # Build the model call model = build(width=img_dims[0], height=img_dims[1], depth=img_dims[2], classes=1) # compile the model opt = Adam(lr=lr, decay=lr/epochs) model.compile(loss="binary_crossentropy", optimizer=opt, metrics=['accuracy']) # fitting the model H = model.fit(X, y, batch_size=batch_size, epochs=epochs, verbose=1) model.save('faceid.model')
额外提示
- 15张样本数量太少,很容易过拟合,建议多收集不同角度、光线条件下的本人图片,以及更多非本人的人脸图,提升模型泛化能力。
- 可以用
ImageDataGenerator做数据增强,生成翻转、平移等样本变体,进一步扩充训练数据。
内容的提问来源于stack exchange,提问作者Bernardo Olisan
相关产品推荐
相关产品推荐

