检查此段代码,看是否还有什么问题:""" 改进版CNN人脸识别系统 包含数据增强、模型训练、特征比对完整流程 使用LFW数据集进行训练 """ import warnings import logging import os import numpy as np import tensorflow as tf from tensorflow.keras import layers, models, applications from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score import cv2 import matplotlib.pyplot as plt # 配置环境 warnings.filterwarnings("ignore") logging.getLogger("tensorflow").setLevel(logging.ERROR) os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # 人脸检测器 face_cascade = cv2.CascadeClassifier( cv2.data.haarcascades + 'haarcascade_frontalface_default.xml') # 超参数配置 CONFIG = { "input_shape": (160, 160, 3), # 输入尺寸 "embedding_dim": 128, # 特征向量维度 "batch_size": 32, # 训练批次大小 "epochs": 30, # 训练轮次 "data_path": "./lfw", # LFW数据集路径 "augmentation": True, # 启用数据增强 "test_size": 0.2, # 验证集比例 "threshold": 0.85 # 相似度阈值 } class FaceRecognitionSystem: def __init__(self): self.model = None self.history = None def _build_model(self): """构建改进的CNN模型""" base_model = applications.MobileNetV2( input_shape=CONFIG["input_shape"], include_top=False, weights='imagenet' ) base_model.trainable = True inputs = tf.keras.Input(shape=CONFIG["input_shape"]) x = base_model(inputs, training=True) x = layers.GlobalAveragePooling2D()(x) x = layers.Dense(256, activation='relu')(x) x = layers.Dropout(0.5)(x) outputs = layers.Dense(CONFIG["embedding_dim"])(x) self.model = tf.keras.Model(inputs, outputs) def _preprocess_image(self, img_path): """图像预处理流程""" # 读取并检测人脸 img = cv2.imread(img_path) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.3, 5) if len(faces) == 0: return None # 裁剪人脸区域 (x,y,w,h) = faces[0] face = img[y:y+h, x:x+w] # 调整尺寸和归一化 face = cv2.resize(face, CONFIG["input_shape"][:2]) face = face.astype("float32") / 255.0 # 数据增强 if CONFIG["augmentation"]: face = self._augment_image(face) return face def _augment_image(self, img): """数据增强方法""" # 随机水平翻转 if np.random.rand() > 0.5: img = cv2.flip(img, 1) # 随机旋转 angle = np.random.uniform(-15, 15) rows, cols = img.shape[:2] M = cv2.getRotationMatrix2D((cols/2, rows/2), angle, 1) img = cv2.warpAffine(img, M, (cols, rows)) # 随机亮度调整 img = cv2.convertScaleAbs(img, alpha=np.random.uniform(0.8, 1.2), beta=np.random.randint(-10, 10)) return img def _load_dataset(self): """加载LFW数据集""" image_paths = [] labels = [] # 确保路径存在 data_path = CONFIG[r"D:\BaiduNetdiskDownload\lfw"] if not os.path.exists(data_path): raise FileNotFoundError(f"数据集路径不存在: {data_path}") # 遍历所有人名目录 for person_name in os.listdir(data_path): person_dir = os.path.join(data_path, person_name) # 过滤非目录文件 if not os.path.isdir(person_dir): continue # 遍历该人目录下的所有图片 for img_name in os.listdir(person_dir): # 过滤非图片文件(可选) if not img_name.lower().endswith(('.png', '.jpg', '.jpeg')): continue img_path = os.path.join(person_dir, img_name) image_paths.append(img_path) labels.append(person_name) # 过滤无效图片并预处理 valid_images = [] valid_labels = [] for path, label in zip(image_paths, labels): try: img = self._preprocess_image(path) if img is not None: valid_images.append(img) valid_labels.append(label) except Exception as e: print(f"处理图片 {path} 时出错: {e}") # 将标签转换为整数编码 from sklearn.preprocessing import LabelEncoder le = LabelEncoder() encoded_labels = le.fit_transform(valid_labels) return np.array(valid_images), encoded_labels def _preprocess_image(self, path): """图片预处理函数""" import cv2 # 读取图片(包括中文路径处理) img = cv2.imdecode(np.fromfile(path, dtype=np.uint8), cv2.IMREAD_COLOR) if img is None: print(f"警告: 无法读取图片 {path}") return None # 调整图片尺寸(示例使用224x224) img = cv2.resize(img, (224, 224)) # 转换颜色通道(BGR -> RGB) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 归一化处理 img = img.astype(np.float32) / 255.0 return img def _create_pairs(self, images, labels): """创建训练正负样本对""" num_classes = len(np.unique(labels)) digit_indices = [np.where(labels == i)[0] for i in range(num_classes)] pairs = [] labels = [] # 创建正样本对 for idx in range(len(images)): _, label = images[idx], labels[idx] indices = digit_indices[label] if len(indices) >= 2: n = np.random.choice(indices) while n == idx: n = np.random.choice(indices) pairs += [[images[idx], images[n]]] labels += [1] # 创建负样本对 for idx in range(len(images)): _, label = images[idx], labels[idx] indices = np.where(labels != label)[0] n = np.random.choice(indices) pairs += [[images[idx], images[n]]] labels += [0] return np.array(pairs), np.array(labels) def train(self): """训练流程""" # 加载数据 images, labels = self._load_dataset() # 创建样本对 pairs, pair_labels = self._create_pairs(images, labels) # 划分数据集 X_train, X_val, y_train, y_val = train_test_split( pairs, pair_labels, test_size=CONFIG["test_size"]) # 构建模型 self._build_model() # 定义对比损失 def contrastive_loss(y_true, y_pred): margin = 1 square_pred = tf.square(y_pred) margin_square = tf.square(tf.maximum(margin - y_pred, 0)) return tf.reduce_mean( y_true * square_pred + (1 - y_true) * margin_square) # 编译模型 self.model.compile( optimizer=tf.keras.optimizers.Adam(0.001), loss=contrastive_loss, metrics=["accuracy"] ) # 训练模型 self.history = self.model.fit( X_train, y_train, validation_data=(X_val, y_val), batch_size=CONFIG["batch_size"], epochs=CONFIG["epochs"] ) # 保存模型 self.model.save("face_recognition_cnn.h5") def compute_similarity(self, img1_path, img2_path): """计算两张图片的相似度""" # 预处理图片 img1 = self._preprocess_image(img1_path) img2 = self._preprocess_image(img2_path) if img1 is None or img2 is None: return 0.0 # 提取特征 emb1 = self.model.predict(np.expand_dims(img1, axis=0))[0] emb2 = self.model.predict(np.expand_dims(img2, axis=0))[0] # 计算余弦相似度 similarity = np.dot(emb1, emb2) / (np.linalg.norm(emb1) * np.linalg.norm(emb2)) return similarity def verify(self, img1_path, img2_path): """验证是否为同一人""" similarity = self.compute_similarity(img1_path, img2_path) return similarity > CONFIG["threshold"], similarity def visualize_training(self): """可视化训练过程""" plt.figure(figsize=(12, 6)) plt.subplot(1, 2, 1) plt.plot(self.history.history['loss'], label='Training Loss') plt.plot(self.history.history['val_loss'], label='Validation Loss') plt.legend() plt.title('Loss Evolution') plt.subplot(1, 2, 2) plt.plot(self.history.history['accuracy'], label='Training Accuracy') plt.plot(self.history.history['val_accuracy'], label='Validation Accuracy') plt.legend() plt.title('Accuracy Evolution') plt.show() # # 使用示例 # if __name__ == "__main__": # system = FaceRecognitionSystem() # # # 训练模型(首次运行需要) # # system.train() # # system.visualize_training() # # # 加载预训练模型 # system.model = tf.keras.models.load_model("face_recognition_cnn.h5") # # # 测试验证 # is_same, similarity = system.verify("person1.jpg", "person2.jpg") # print(f"是否为同一人: {is_same}, 相似度: {similarity:.2%}")
时间: 2025-08-10 19:28:56 浏览: 11
<think>好的,我需要检查用户提供的这段改进版CNN人脸识别系统代码,找出可能存在的问题。首先,我会通读整个代码,注意各个函数和配置部分是否有明显的错误或潜在问题。首先看配置部分。CONFIG中的data_path被设置为"./lfw",但在_load_dataset方法中,用户试图使用CONFIG[r"D:\BaiduNetdiskDownload\lfw"],这明显是一个错误,应该是CONFIG["data_path"]。这会导致KeyError,因为CONFIG中没有这个键。接下来是预处理函数。注意到代码中有两个_preprocess_image方法,一个在FaceRecognitionSystem类中,另一个可能是在类外定义的?这会导致后者覆盖前者,引发方法重复定义的问题。需要确认方法的位置,确保类内部只有一个_preprocess_image。在数据加载部分,_load_dataset方法中遍历目录时,过滤非目录文件是正确的,但可能缺少对隐藏文件的处理,比如macOS的.DS_Store,这可能导致错误。此外,图片预处理时使用的尺寸是224x224,而CONFIG中的input_shape是160x160,这会导致尺寸不匹配,影响模型输入。
数据增强部分,_augment_image方法使用了OpenCV的函数,但输入的img可能已经是归一化后的浮点数组,而OpenCV处理时需要的是整数类型,这可能导致错误。例如,cv2.flip处理浮点图像可能会引发类型错误。模型构建部分,使用了MobileNetV2作为基础模型,但include_top=False时,输出特征图的尺寸可能不符合预期,需要确保GlobalAveragePooling2D能正确连接。此外,模型输出层没有归一化处理,如L2归一化,这可能导致后续相似度计算时数值不稳定。训练流程中,_create_pairs函数生成正负样本对,但负样本对的生成可能不够随机,且正样本对的数量可能与负样本不平衡,影响模型训练效果。另外,contrastive_loss的定义是否正确?需要核对损失函数的实现是否符合对比学习的标准形式。
在计算相似度时,使用余弦相似度是正确的,但模型输出的embeddings是否经过归一化?如果没有,余弦相似度的计算可能不准确。此外,预处理函数在处理中文路径时使用了cv2.imdecode,这需要确保路径是否正确传递,避免因编码问题导致的读取失败。
还有可能存在的一些问题包括:没有使用数据生成器(如ImageDataGenerator)导致内存不足;训练时的输入数据形状是否正确;标签编码是否正确应用于所有情况;以及模型保存和加载时是否包含自定义层和损失函数。最后,检查代码的注释和文档字符串是否准确,是否存在未处理的异常情况,例如图片读取失败时的错误处理是否完善,以及路径处理是否跨平台兼容。总结下来,需要指出代码中的主要问题,并提供修改建议,例如修复配置路径错误、统一预处理函数、调整图像尺寸一致性、处理数据增强中的类型问题、改进样本对生成逻辑、添加嵌入归一化层等。</think>经过详细分析,代码存在以下问题及改进建议:
1. **路径配置错误**:
```python
# 错误代码
data_path = CONFIG[r"D:\BaiduNetdiskDownload\lfw"]
# 修正为
data_path = CONFIG["data_path"]
```
2. **重复的预处理方法**:
```python
# 两个同名的_preprocess_image方法会导致覆盖
# 需要合并为一个方法,修正后的预处理方法:
def _preprocess_image(self, img_path):
"""统一的图片预处理方法"""
# 处理中文路径
img = cv2.imdecode(np.fromfile(img_path, dtype=np.uint8), cv2.IMREAD_COLOR)
if img is None:
print(f"警告: 无法读取图片 {img_path}")
return None
# 人脸检测优化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
if len(faces) == 0:
return None
# 使用最大的人脸区域
(x,y,w,h) = max(faces, key=lambda f: f[2]*f[3])
face = img[y:y+h, x:x+w]
# 尺寸与配置一致
face = cv2.resize(face, CONFIG["input_shape"][:2])
face = cv2.cvtColor(face, cv2.COLOR_BGR2RGB)
face = face.astype("float32") / 255.0 # 统一归一化方式
return face
```
3. **数据增强类型问题**:
```python
# 修正后的增强方法
def _augment_image(self, img):
"""改进的数据增强"""
# 转换回0-255范围进行OpenCV操作
temp_img = (img * 255).astype(np.uint8)
# 随机水平翻转
if np.random.rand() > 0.5:
temp_img = cv2.flip(temp_img, 1)
# 随机旋转(限制角度范围)
angle = np.random.uniform(-15, 15)
rows, cols = temp_img.shape[:2]
M = cv2.getRotationMatrix2D((cols/2, rows/2), angle, 1)
temp_img = cv2.warpAffine(temp_img, M, (cols, rows))
# 转换回float32
temp_img = temp_img.astype(np.float32) / 255.0
return temp_img
```
4. **模型架构改进**:
```python
def _build_model(self):
"""改进的模型架构"""
base_model = applications.MobileNetV2(
input_shape=CONFIG["input_shape"],
include_top=False,
weights='imagenet',
pooling='avg' # 直接添加全局池化
)
# 添加L2归一化层
inputs = tf.keras.Input(shape=CONFIG["input_shape"])
x = base_model(inputs)
x = layers.Dense(256, activation='relu')(x)
x = layers.Lambda(lambda x: tf.math.l2_normalize(x, axis=1))(x)
self.model = tf.keras.Model(inputs, x)
```
5. **样本对生成优化**:
```python
def _create_pairs(self, images, labels):
"""改进的样本对生成"""
label_to_indices = defaultdict(list)
for idx, label in enumerate(labels):
label_to_indices[label].append(idx)
pairs = []
pair_labels = []
# 平衡正负样本比例
for idx in range(len(images)):
# 正样本
if len(label_to_indices[labels[idx]]) >= 2:
n = random.choice(label_to_indices[labels[idx]])
while n == idx:
n = random.choice(label_to_indices[labels[idx]])
pairs.append([images[idx], images[n]])
pair_labels.append(1)
# 负样本
neg_label = random.choice(list(set(labels) - {labels[idx]}))
n = random.choice(label_to_indices[neg_label])
pairs.append([images[idx], images[n]])
pair_labels.append(0)
return np.array(pairs), np.array(pair_labels)
```
**其他改进建议**:
1. 添加数据生成器避免内存溢出:
```python
from tensorflow.keras.preprocessing.image import ImageDataGenerator
datagen = ImageDataGenerator(
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
horizontal_flip=True)
```
2. 使用Triplet Loss替代对比损失:
```python
def triplet_loss(y_true, y_pred, alpha=0.2):
anchor, positive, negative = y_pred[0], y_pred[1], y_pred[2]
pos_dist = tf.reduce_sum(tf.square(anchor - positive), axis=1)
neg_dist = tf.reduce_sum(tf.square(anchor - negative), axis=1)
basic_loss = pos_dist - neg_dist + alpha
loss = tf.reduce_mean(tf.maximum(basic_loss, 0.0))
return loss
```
3. 添加TensorBoard回调:
```python
callbacks = [
tf.keras.callbacks.TensorBoard(log_dir='./logs'),
tf.keras.callbacks.EarlyStopping(patience=5)
]
```
阅读全文
相关推荐



















