FaceRank检测人脸颜值、亚欧、性别的模型(案例) 作者:马育民 • 2020-04-20 16:44 • 阅读:10493 # 介绍 FaceRank是一个基于TensorFlow与Keras框架的CNN人脸打分开源项目,核心功能是通过深度学习模型量化评估人脸吸引力,输出颜值分数,适合深度学习初学者实践图像特征提取与模型训练 ### 核心定位 面向深度学习入门者的人脸吸引力量化工具,通过CNN自动提取人脸特征并打分,提供完整的训练与部署流程,帮助开发者熟悉TensorFlow/Keras在图像任务中的应用。 ### 技术栈 - **框架**:TensorFlow(核心)+ Keras(简化模型搭建),支持两种版本实现。 - **核心模型**:CNN,含卷积层、池化层、激活函数(如ReLU)、全连接层,用于自动学习人脸边缘、纹理、形状等特征。 - **依赖库**:face_recognition(人脸检测与裁剪)、OpenCV(图像处理)、NumPy(数据计算)等。 ### 模型结构 典型流程为“输入(128×128像素人脸图)→ 卷积层(特征提取)→ 池化层(降维)→ 全连接层(特征映射)→ 输出(0-10分颜值分数)”。 ### 部署与使用流程 1. **环境搭建**:安装Python、TensorFlow/Keras、face_recognition等依赖,建议用Docker解决face_recognition安装问题。 2. **数据预处理** - 用find_faces_in_picture.py从原始图片中检测并裁剪人脸,保存至face_image文件夹。 - 用resize_image.py将人脸图统一缩放为128×128像素,适配模型输入。 3. **模型训练**:运行train_model.py,定义CNN结构、设置损失函数(如MSE)与优化器(如Adam),训练后保存模型至model目录。 4. **推理测试**:运行run_model.py,加载训练好的模型,输入人脸图输出颜值分数。 5. **模型获取**:可自行训练,也可从第三方平台下载预训练模型。 ### 应用场景 - 深度学习教学:作为CNN图像任务的实战案例,覆盖数据预处理、模型搭建、训练与部署全流程。 - 趣味应用:社交平台颜值排序、相亲APP匹配辅助、短视频人脸吸引力筛选等。 - 二次开发:可扩展至年龄/性别预测、表情识别等衍生任务。 ### 局限 - **数据偏差**:默认数据集多针对特定人群(如年轻女性),泛化性不足,需自定义数据集优化。 - **伦理风险**:颜值打分易引发歧视,需遵守隐私保护与公平性原则,避免用于招聘、婚恋等敏感场景。 - **精度限制**:开源模型为入门级,打分结果受训练数据质量影响大,需调参或迁移学习提升精度。 ### 项目资源与扩展 1. **获取渠道**:GitHub(fendouai/FaceRank)、Gitee(国内镜像),含完整代码、中文说明与教程。 2. **扩展方向** - 迁移学习:用VGG16、ResNet等预训练模型提升特征提取能力。 - 数据增强:通过旋转、翻转、亮度调整扩充数据集,减少过拟合。 - 部署优化:用TensorFlow Lite适配移动端,或用FastAPI封装为API服务。 ### 难点 在 **多输出** 需要做额外处理: - 颜值检测是线性回归,loss是`mse` - 亚洲人或欧洲人识别是逻辑回归,loss是`sigmoid` - 性别检测是逻辑回归,loss是`sigmoid` ### 训练结果: - 颜值误差 0.3 - 亚洲/欧洲检测准确率 0.99 - 性别检测准确率 0.98 # 数据集 使用 华南理工大学人机智能交互实验室 的数据集 ### 介绍 数据集共有5500张正面,包括2000个亚洲女性(AF),2000个亚洲男性(AM),750个白人女性(CF)和750个白人男性(CM) 这些正面具有不同的属性(男性/女性,亚洲人/白种人,年龄)和不同的标签(面部标志,5个等级的美容分数,美容分数分布) 详细介绍见链接: https://github.com/HCIILAB/SCUT-FBP5500-Database-Release ### 标签 所有图像都由60名志愿者的美容评分标记为[1、5],并且在每个图像的重要面部组件上还定位了86个面部标志。 **缺点:** 由于审美不同,我对很多人脸的评分不认同,训练好模型后,预测知名网红、明星时,颜值分数不高,反而 **凤姐的颜值颇高** 所以 **测试自己颜值** 时参考就好,不必纠结 ### 下载链接 https://pan.baidu.com/s/1Ff2W2VLJ1ZbWSeV5JbF0Iw(PASSWORD:if7p ) ### 目录说明: [](https://www.malaoshi.top/upload/0/0/1EF5NTsLAeNv.png) # 技术 - TensorFlow2.0 - keras - matplotlib - numpy # 代码 ### 导包 ``` import os,glob os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' import numpy as np import tensorflow as tf from tensorflow import keras import matplotlib.pyplot as plt from PIL import Image import IPython.display as display import xml.etree.ElementTree as ET print(tf.__version__) print(tf.test.is_gpu_available()) ``` ### 常量 ``` img_path="/kaggle/input/facial-beauty-prediction/SCUT-FBP5500_v2/Images/" img_size=224 batch_size=8 AUTOTUNE=tf.data.experimental.AUTOTUNE ac_map={"A":0,"C":1} fm_map={"F":0,"M":1} ``` ### 定义读取文本的函数 ``` def read_txt(path): with open(path,"rt") as f: lines=f.readlines() # print(lines) names=[] labels=[] acs=[] fms=[] for item in lines : item=item.strip() name,label=item.split(" ") ac=ac_map[name[0]] fm=fm_map[name[1]] acs.append(ac) fms.append(fm) # print(name,label) names.append(os.path.join(img_path,name)) labels.append(float(label)) return names,labels,acs,fms ``` ``` train_names,train_labels,train_acs,train_fms=read_txt("/kaggle/input/facial-beauty-prediction/SCUT-FBP5500_v2/train_test_files/split_of_60%training and 40%testing/train.txt") ``` ``` train_num=len(train_names) train_num ``` ``` test_names,test_labels,test_acs,test_fms=read_txt("/kaggle/input/facial-beauty-prediction/SCUT-FBP5500_v2/train_test_files/split_of_60%training and 40%testing/test.txt") ``` ### 生成训练数据集(关键) ``` train_ds=tf.data.Dataset.from_tensor_slices((train_names,(train_labels,train_acs,train_fms))) train_ds ``` **注意:** 传入一个tuple类型,第一个元素train_names是list类型,第二个元素是tuple类型,该tuple里面的每个元素都是list ### 定义预处理函数 ``` def process_train(path,label): t=tf.io.read_file(path) arr=tf.io.decode_jpeg(t,channels=3) arr=tf.image.resize(arr,(img_size,img_size)) arr = arr/255 return arr,label ``` ``` train_ds2=train_ds.map(process_train,num_parallel_calls=AUTOTUNE).shuffle(train_num).batch(batch_size).prefetch(AUTOTUNE) test_ds=tf.data.Dataset.from_tensor_slices((test_names,(test_labels,test_acs,test_fms))) test_ds2=test_ds.map(process_train,num_parallel_calls=AUTOTUNE).batch(batch_size).prefetch(AUTOTUNE) ``` ### 构建模型(关键) ``` def build(): inp=tf.keras.Input((img_size, img_size, 3)) x=tf.keras.layers.Conv2D(64,3,padding='same',activation="relu",)(inp) x=tf.keras.layers.Conv2D(64,3,padding='same',activation="relu")(x) x=tf.keras.layers.MaxPooling2D()(x) x=tf.keras.layers.Conv2D(128,3,padding='same',activation="relu")(x) x=tf.keras.layers.Conv2D(128,3,padding='same',activation="relu")(x) x=tf.keras.layers.MaxPooling2D()(x) x=tf.keras.layers.Conv2D(256,3,padding='same',activation="relu")(x) x=tf.keras.layers.Conv2D(256,3,padding='same',activation="relu")(x) x=tf.keras.layers.MaxPooling2D()(x) x=tf.keras.layers.Conv2D(512,3,padding='same',activation="relu")(x) x=tf.keras.layers.Conv2D(512,3,padding='same',activation="relu")(x) x=tf.keras.layers.GlobalAveragePooling2D()(x) x=tf.keras.layers.Dense(1024,activation="relu")(x) # x=tf.keras.layers.Dropout(0.5)(x) # x=tf.keras.layers.Dense(1024,activation="relu")(x) # x=tf.keras.layers.Dropout(0.5)(x) score=tf.keras.layers.Dense(1,name="score")(x) ac=tf.keras.layers.Dense(1,activation="sigmoid",name="ac")(x) fm=tf.keras.layers.Dense(1,activation="sigmoid",name="fm")(x) model=tf.keras.Model(inp,[score,ac,fm]) return model ``` **注意:** 模型中输出层layer的 **name**,在 **编译** 时会用到 ``` model=build() ``` ### 编译(关键) 编译时要注意,因为是多输出模型,所以要分别设置每个输出的loss,分别设置每个输出的metrics,如下代码: ``` model.compile(optimizer=tf.keras.optimizers.Adam(lr=0.0001), loss=["mse","binary_crossentropy","binary_crossentropy"], metrics={"score":"mae","ac":"acc","fm":"acc"}) ``` **注意:** metrics是dict类型,key是:score、ac、fm,是模型中layer的name 或者 ``` model.compile(optimizer=tf.keras.optimizers.Adam(lr=0.0001), loss=["mse","binary_crossentropy","binary_crossentropy"], metrics=[["mae"],["acc"],"acc"]) ``` **注意:** 必须是`metrics=[["mae"],"acc","acc"]`,不能是`metrics=["mae","acc","acc"]` ### 训练 ``` history=model.fit(train_ds2,epochs=40,validation_data=test_ds2) ``` 最后一次结果: ``` Epoch 36/40 217/413 [==============>...............] - ETA: 15s - loss: 0.2152 - score_loss: 0.1608 - ac_loss: 0.0205 - fm_loss: 0.0340 - score_mae: 0.3112 - ac_acc: 0.9948 - fm_acc: 0.9896 ``` 可知: - 颜值 score_mae 误差在0.3 - 亚洲/欧洲检测准确率在0.99 - 性别检测准确率在0.98 ### 查看loss和正确率 ``` plt.plot(history.epoch,history.history["score_mae"],label="score_mae") plt.plot(history.epoch,history.history["val_score_mae"],label="val_score_mae") plt.legend() ``` ``` plt.plot(history.epoch,history.history["ac_acc"],label="ac_acc") plt.plot(history.epoch,history.history["val_ac_acc"],label="val_ac_acc") plt.legend() ``` ``` plt.plot(history.epoch,history.history["fm_acc"],label="fm_acc") plt.plot(history.epoch,history.history["val_fm_acc"],label="val_fm_acc") plt.legend() ``` ``` plt.plot(history.epoch,history.history["score_loss"],label="score_loss") plt.plot(history.epoch,history.history["val_score_loss"],label="val_score_loss") plt.legend() ``` ``` plt.plot(history.epoch,history.history["ac_loss"],label="ac_loss") plt.plot(history.epoch,history.history["val_ac_loss"],label="val_ac_loss") plt.legend() ``` ``` plt.plot(history.epoch,history.history["fm_loss"],label="fm_loss") plt.plot(history.epoch,history.history["val_fm_loss"],label="val_fm_loss") plt.legend() ``` ### 保存模型 ``` model.save("model.h5",save_format="h5") ``` # 预测 ``` import tensorflow as tf import matplotlib.pyplot as plt img_size=224 ac_map={0:"亚洲",1:"欧洲"} fm_map={0:"女",1:"男"} ``` ### 加载模型 ``` model=tf.keras.models.load_model("/Users/mym/Desktop/数据集/SCUT-FBP5500_v2/model颜值亚欧性别.h5") ``` ### 定义函数 ``` def test(path): t=tf.io.read_file(path) arr=tf.io.decode_jpeg(t,channels=3) plt.imshow(arr) arr=tf.image.resize(arr,(img_size,img_size)) arr = arr/255 arr=tf.expand_dims(arr,0) prd=model.predict(arr) score,ac,fm=tf.squeeze(prd).numpy() print("score:",score,"ac:",ac,"fm:",fm) score=int(round(score*20)) ac=1 if ac>0.5 else 0 ac=ac_map.get(ac) fm=1 if fm>0.5 else 0 fm=fm_map.get(fm) print("颜值:%s,亚欧:%s,性别:%s"%(score,ac,fm)) ``` ``` test("/Users/mym/Desktop/可删除/OIP.jpeg") ``` 原文出处:http://www.malaoshi.top/show_1EF5NUEm0T3B.html