FaceRank检测人脸颜值、亚欧、性别的模型(案例)

介绍

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 )

目录说明:

技术

  • 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")