You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 16:47:34