如何使用TensorFlow和Keras在UTKFace数据集上实现多标签分类
UTKFace人脸多属性预测实现修复
1. ImageDataGenerator的class_mode参数选择与报错根因
两个参数的适用边界和报错原因直接拆解:
class_mode="raw":适配统一格式的多列数值标签,返回形状为(batch_size, 标签总列数)的二维numpy数组,是当前场景的正确选择。class_mode="multi_output":为旧版本Keras中不规则格式标签设计,会把每一列标签拆分为独立的数组组成元组返回,需要搭配多输出头模型使用。
你遇到的两个报错的直接原因:
- 切换为
multi_output时触发AttributeError: 'tuple' object has no attribute 'shape':使用的是单输出Sequential结构,无法解析多元素元组格式的返回标签,标签结构和模型结构不匹配。 - 切换为
raw时触发reshape维度不匹配错误:没有在flow_from_dataframe中指定target_size,生成器默认将图片读取为256x256尺寸,和模型定义的100x100输入要求不匹配,最终导致flatten层的张量维度和全连接层期望维度不一致。
修正后的生成器定义:
testdatagenerator = ImageDataGenerator(rescale=1. /255) testdata = testdatagenerator.flow_from_dataframe( dataframe=test, directory=None, x_col="Filepath", y_col=["Age","Gender","Racevalues"], class_mode="raw", target_size=(100, 100), batch_size=20 ) traindatagenerator = ImageDataGenerator(rescale=1. /255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True) traindata = traindatagenerator.flow_from_dataframe( dataframe=train, directory=None, x_col="Filepath", y_col=["Age","Gender","Racevalues"], class_mode="raw", target_size=(100, 100), batch_size=20, shuffle=True )
2. 三类预测属性的损失函数选择
损失函数必须匹配每个属性对应的任务类型,不能统一使用binary_crossentropy:
- 年龄:属于连续值回归任务,使用
mean_absolute_error(MAE)作为损失函数。原因是年龄是有序连续量,MAE对年龄预测中的离群样本鲁棒性强,也是人脸年龄预测领域的通用评价指标。如果需要将年龄划分为离散组别做分类任务,可替换为sparse_categorical_crossentropy。 - 性别:属于二分类任务,使用
binary_crossentropy作为损失函数,对应输出头为1个神经元+sigmoid激活,适配0/1的二分类标签。 - 种族:属于5类多分类任务,使用
sparse_categorical_crossentropy作为损失函数,因为标签存储为0-4的类别整数索引,不需要手动做one-hot编码,对应输出头为5个神经元+softmax激活。
模型结构适配修正
单输出Sequential模型无法同时承接三个不同任务的标签和输出要求,需要调整为共享特征提取主干+独立任务输出头的多输出结构:
from tensorflow.keras.models import Model from tensorflow.keras.layers import Input # 共享卷积特征提取主干 input_layer = Input(shape=(100,100,3)) x = Conv2D(32, (3, 3), padding='same')(input_layer) x = Activation('relu')(x) x = Conv2D(32, (3, 3))(x) x = Activation('relu')(x) x = MaxPool2D(pool_size=(2, 2))(x) x = Dropout(0.25)(x) x = Conv2D(64, (3, 3), padding='same')(x) x = Activation('relu')(x) x = Conv2D(64, (3, 3))(x) x = Activation('relu')(x) x = MaxPool2D(pool_size=(2, 2))(x) x = Dropout(0.25)(x) x = Flatten()(x) x = Dense(512)(x) x = Activation('relu')(x) x = Dropout(0.5)(x) # 三个独立任务输出头 age_output = Dense(1, name='age_output')(x) # 回归任务使用线性激活 gender_output = Dense(1, activation='sigmoid', name='gender_output')(x) # 二分类 race_output = Dense(5, activation='softmax', name='race_output')(x) # 5类多分类 model = Model(inputs=input_layer, outputs=[age_output, gender_output, race_output]) # 编译时为每个输出头单独匹配损失和权重 model.compile( optimizer='Adam', loss={ 'age_output': 'mean_absolute_error', 'gender_output': 'binary_crossentropy', 'race_output': 'sparse_categorical_crossentropy' }, loss_weights={ 'age_output': 0.5, 'gender_output': 1.0, 'race_output': 1.0 }, metrics={ 'age_output': 'mae', 'gender_output': 'accuracy', 'race_output': 'accuracy' } ) # 训练时不需要额外调整标签输入,flow_from_dataframe返回的3列标签会自动匹配三个输出头 model.fit( traindata, steps_per_epoch=100, epochs=100, validation_data=testdata, validation_steps=100 )
内容的提问来源于stack exchange,提问作者J. Doe
相关产品推荐
相关产品推荐

