[人脸识别]使用VGG Face Model微调(Fine tune)自己的数据集

本文介绍了如何使用VGG Face Model对人脸识别数据集进行微调,包括准备数据集、数据处理、修改caffe训练配置文件以及训练过程。重点讲解了数据集的划分、LMDB格式转换、prototxt文件的修改以及训练命令。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关键词:人脸识别、Caffe、VGG Face Model

一、准备数据集

  对于一个未经加工的数据集,基本情况如下图所示


  图中每一个文件夹内是该类别的所有图像。我们需要对每一类划分训练集与测试集。这里可以写个脚本进行划分,训练集与测试集的比例自己把握,一般来说是训练集:测试集=4:1。
  需要注意的是,最终只需要生成两个文件夹:train与val。train文件夹中包含所有训练图片,换句话说,就是把所有训练图片都放在一个文件夹内。val同理。
  我们在使用脚本划分数据集时,同时需要新建两个文本:train.txt与val.txt,用来记录训练集与测试集中图片名称,以及类别。如下图所示:


  需要注意的非常重要的一点是,train.txt和val.txt中的名称与类别中间分隔符是一个空格,其次,类别号要从0开始,否则后续处理会出现错误。

二、数据处理

  经过上一步后,我们会得到两个文件夹:train与val、以及相对应的train.txt和val.txt。
  接下来,我们需要将上述数据集处理成caffe能读取的数据格式LMDB。这部分有标准的处理脚本。

#!/usr/bin/env sh
# Create the imagenet lmdb inputs
# N.B. set the path to the imagenet train + val data dirs

EXAMPLE=    #修改
DATA=       #修改
TOOLS=      #修改

TRAIN_DATA_ROOT=$DATA/train/
VAL_DATA_ROOT=$DATA/val/
DBTYPE=lmdb

# Set RESIZE=true to resize the images to 224×224. Leave as false if images have
# already been resized using another tool.
RESIZE=true
if $RESIZE; then
  RESIZE_HEIGHT=224
  RESIZE_WIDTH=224
else
  RESIZE_HEIGHT=0
  RESIZE_WIDTH=0
fi

if [ ! -d "$TRAIN_DATA_ROOT" ]; then
  echo "Error: TRAIN_DATA_ROOT is not a path to a directory: $TRAIN_DATA_ROOT"
  echo "Set the TRAIN_DATA_ROOT variable in create_imagenet.sh to the path" \
       "where the ImageNet training data is stored."
  exit 1
fi

if [ ! -d "$VAL_DATA_ROOT" ]; then
  echo "Error: VAL_DATA_ROOT is not a path to a directory: $VAL_DATA_ROOT"
  echo "Set the VAL_DATA_ROOT variable in create_imagenet.sh to the path" \
       "where the ImageNet validation data is stored."
  exit 1
fi

echo "Creating train lmdb..."

GLOG_logtostderr=1 $TOOLS/convert_imageset \
    --resize_height=$RESIZE_HEIGHT \
    --resize_width=$RESIZE_WIDTH \
    --shuffle \
    $TRAIN_DATA_ROOT \
    $DATA/train.txt \
    $EXAMPLE/train_lmdb

echo "Creating val lmdb..."

GLOG_logtostderr=1 $TOOLS/convert_imageset \
    --resize_height=$RESIZE_HEIGHT \
    --resize_width=$RESIZE_WIDTH \
    --shuffle \
    $VAL_DATA_ROOT \
    $DATA/val.txt \
    $EXAMPLE/val_lmdb

echo "Computing image mean..."
$TOOLS/compute_image_mean -backend=$DBTYPE \
  $EXAMPLE/train_lmdb $EXAMPLE/mean.binaryproto

echo "Done."

  你需要更改的地方有:
  1.EXAMPLE后面的路径,尽量设置为第一步中的train与test文件夹的父路径
  2.DATA为第一步中的train与test文件夹的父路径
  3.TOOLS的标准格式为:(caffe路径)/build/tools;
  4.TRAIN_DATA_ROOT与VAL_DATA_ROOT不需要改变,分别对应train与val文件夹的路径。
  其他参数的意义解释可参考:http://www.cnblogs.com/dupuleng/articles/4370236.html
  确保上述路径设置正确后,便可将脚本拖入终端运行。
  运行完成后,在你设置的EXAMPLE路径中会产生train_lmdb与val_lmdb两个文件夹,以及mean.binaryproto文件。

三、caffe训练相关配置文件修改

  下载VGG Face Model,并解压,移动到(caffe路径)/models中:


  接下来,我们需要修改vgg_face_caffe中的VGG_FACE_deploy.prototxt 文件,推荐你直接复制下面的文件

name: "VGG_FACE_16_Net"
layer {
  name: "data"
  type: "Data"  #这里注意
  top: "data"
  top: "label"
  data_param {
    source: "$/train_lmdb"   #这里修改
    backend:LMDB
    batch_size: 100   #这里修改
  }
  transform_param {
     mean_file: "$/mean.binaryproto"   #这里修改
     mirror: true
  }
  include: { phase: TRAIN }
}

layer {
  name: "data"
  type: "Data"
  top: "data"
  top: "label"
  data_param {
    source: "$/val_lmdb"  #这里修改
    backend:LMDB
    batch_size: 25   #这里修改
  }
  transform_param {
    mean_file: "$/mean.binaryproto"   #这里修改
    mirror: true
  }
  include: { 
    phase: TEST 
  }
}
layer {
  name: "conv1_1"
  type: "Convolution"
  bottom: "data"
  top: "conv1_1"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param 
修改后的代码如下:model_definitions.py的代码:import torch import torch.nn as nn from facenet_pytorch import InceptionResnetV1 from utils import evaluate # 从utils导入evaluate函数 def fine_tune_facenet(num_classes, device='cuda'): """微调FaceNet模型""" # 加载预训练模型 model = InceptionResnetV1( pretrained='vggface2', classify=False, device=device ) # 冻结底层参数 for param in model.parameters(): param.requires_grad = False # 解冻最后几个层 for param in model.last_linear.parameters(): param.requires_grad = True for param in model.last_bn.parameters(): param.requires_grad = True # 添加自定义分类层 model.logits = nn.Linear(512, num_classes).to(device) return model def train_facenet(model, train_loader, val_loader, num_epochs=20, lr=0.001, device='cuda'): # 确保在函数内部正确使用设备 model = model.to(device) optimizer = torch.optim.Adam(model.parameters(), lr=lr) criterion = nn.CrossEntropyLoss() best_val_acc = 0.0 for epoch in range(num_epochs): model.train() running_loss = 0.0 correct_train = 0 total_train = 0 for inputs, labels in train_loader: if inputs is None: # 跳过加载失败的样本 continue inputs, labels = inputs.to(device), labels.to(device) optimizer.zero_grad() # 前向传播 embeddings = model(inputs) outputs = model.logits(embeddings) # 计算损失 loss = criterion(outputs, labels) loss.backward() optimizer.step() # 统计训练准确率 _, predicted = torch.max(outputs.data, 1) total_train += labels.size(0) correct_train += (predicted == labels).sum().item() running_loss += loss.item() train_loss = running_loss / len(train_loader) train_acc = 100 * correct_train / total_train # 验证 val_acc = evaluate(model, val_loader, device) print(f'Epoch {epoch + 1}/{num_epochs} - ' f'Train Loss: {train_loss:.4f}, Train Acc: {train_acc:.2f}%, ' f'Val Acc: {val_acc:.2f}%') # 保存最佳模型 if val_acc > best_val_acc: best_val_acc = val_acc torch.save(model.state_dict(), 'best_facenet_model.pth') return model utils.py代码:# utils.py import torch # 添加这行导入语句 import numpy as np from torch.utils.data import WeightedRandomSampler def create_balanced_sampler(dataset): """创建平衡采样器解决类别不平衡""" # 获取标签 labels = [label for _, label in dataset] # 计算每个类别的样本数 class_counts = {} for label in labels: class_counts[label] = class_counts.get(label, 0) + 1 # 计算每个样本的权重 weights = [] for label in labels: weights.append(1.0 / class_counts[label]) return WeightedRandomSampler(weights, len(weights), replacement=True) def evaluate(model, loader, device): """评估模型性能""" model.eval() correct = 0 total = 0 with torch.no_grad(): # 这里使用了torch for inputs, labels in loader: if inputs is None: # 跳过加载失败的样本 continue inputs, labels = inputs.to(device), labels.to(device) embeddings = model(inputs) outputs = model.logits(embeddings) _, predicted = torch.max(outputs.data, 1) # 这里也使用了torch total += labels.size(0) correct += (predicted == labels).sum().item() return 100 * correct / total if total > 0 else 0
06-26
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值