You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何增量训练人脸识别模型,无需从头重新训练?

问题描述

已基于克里斯蒂亚诺·罗纳尔多(Cristiano Ronaldo)和利昂内尔·梅西(Lionel Messi)的图像训练完成一个人脸识别模型,现在希望为模型新增识别人物(如玛丽亚·莎拉波娃(Maria Sharapova)),但不想从头重新训练整个模型,询问是否可以基于新数据集训练并高效合并到现有模型中。

现有训练代码如下:

import torch
import torchvision
from torchvision import datasets, models, transforms
import os

import ssl
ssl._create_default_https_context = ssl._create_unverified_context

data_transforms = {
    'train': transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
    'test': transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
}

data_dir = './new_dataset'
image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x), data_transforms[x])
                  for x in ['train', 'test']}
dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=4, shuffle=True)
               for x in ['train', 'test']}
class_names = image_datasets['train'].classes

model = models.resnet18(pretrained=True, progress=True)

num_classes = len(class_names)
model.fc = torch.nn.Linear(model.fc.in_features, num_classes)

device = torch.device("cpu")
model = model.to(device)

criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)

num_epochs = 10

for epoch in range(num_epochs):
    for inputs, labels in dataloaders['train']:
        inputs = inputs.to(device)
        labels = labels.to(device)

        optimizer.zero_grad()

        outputs = model(inputs)
        loss = criterion(outputs, labels)

        loss.backward()
        optimizer.step()

torch.save(model.state_dict(), 'model.pth')

model.eval()

correct = 0
total = 0

with torch.no_grad():
    for inputs, labels in dataloaders['test']:
        inputs = inputs.to(device)
        labels = labels.to(device)

        outputs = model(inputs)
        _, predicted = torch.max(outputs.data, 1)

        total += labels.size(0)
        correct += (predicted == labels).sum().item()

accuracy = 100 * correct / total
print(f"Accuracy on the test set: {accuracy}%")

原数据集文件夹结构:

new_dataset/
--test/
----cristiano_ronaldo
----lionel_messi
--train/
----cristiano_ronaldo
----lionel_messi
解决方案

完全可以通过增量训练实现该需求,无需从头训练整个模型。核心思路是保留已训练好的特征提取层权重,仅调整最后一层分类器(或微调少量特征层)适配新类别,具体步骤如下:

1. 调整数据集结构

在原数据集的train和test目录下新增玛丽亚·莎拉波娃的图像文件夹:

new_dataset/
--test/
----cristiano_ronaldo
----lionel_messi
----maria_sharapova
--train/
----cristiano_ronaldo
----lionel_messi
----maria_sharapova

2. 加载现有模型并修改分类层

加载之前保存的model.pth,将最后一层全连接层(FC)的输出维度从2修改为3(对应3个类别),同时保留原FC层中对应C罗和梅西的权重,仅初始化新增类别的权重:

import torch
import torchvision
from torchvision import datasets, models, transforms
import os

import ssl
ssl._create_default_https_context = ssl._create_unverified_context

# 数据变换保持不变
data_transforms = {
    'train': transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
    'test': transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
}

# 加载更新后的数据集
data_dir = './new_dataset'
image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x), data_transforms[x])
                  for x in ['train', 'test']}
dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=4, shuffle=True)
               for x in ['train', 'test']}
class_names = image_datasets['train'].classes
num_classes = len(class_names)  # 现在为3

# 加载ResNet18结构,读取已训练好的模型权重
model = models.resnet18(pretrained=False)
original_state_dict = torch.load('model.pth')

# 构建原FC层并加载权重,用于提取原类别参数
original_fc = torch.nn.Linear(model.fc.in_features, 2)
original_fc.load_state_dict({
    'weight': original_state_dict['fc.weight'],
    'bias': original_state_dict['fc.bias']
})

# 创建新FC层,保留原类别权重,初始化新增类别参数
new_fc = torch.nn.Linear(model.fc.in_features, num_classes)
with torch.no_grad():
    new_fc.weight[:2] = original_fc.weight
    new_fc.bias[:2] = original_fc.bias
model.fc = new_fc

# 加载原模型除FC层外的其他参数
del original_state_dict['fc.weight']
del original_state_dict['fc.bias']
model.load_state_dict(original_state_dict, strict=False)

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)

3. 冻结特征提取层,仅训练新分类层

为了高效训练,冻结ResNet18的所有特征提取层参数,只训练新的FC层:

# 冻结特征提取层参数
for param in model.parameters():
    param.requires_grad = False
# 仅开启FC层参数的梯度更新
for param in model.fc.parameters():
    param.requires_grad = True

# 定义损失函数和优化器(仅优化FC层参数)
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.fc.parameters(), lr=0.001, momentum=0.9)
num_epochs = 10

# 训练过程
for epoch in range(num_epochs):
    model.train()
    running_loss = 0.0
    for inputs, labels in dataloaders['train']:
        inputs = inputs.to(device)
        labels = labels.to(device)

        optimizer.zero_grad()

        outputs = model(inputs)
        loss = criterion(outputs, labels)

        loss.backward()
        optimizer.step()

        running_loss += loss.item() * inputs.size(0)
    
    epoch_loss = running_loss / len(image_datasets['train'])
    print(f'Epoch {epoch+1}/{num_epochs}, Loss: {epoch_loss:.4f}')

# 保存更新后的模型
torch.save(model.state_dict(), 'updated_model.pth')

# 测试模型精度
model.eval()
correct = 0
total = 0

with torch.no_grad():
    for inputs, labels in dataloaders['test']:
        inputs = inputs.to(device)
        labels = labels.to(device)

        outputs = model(inputs)
        _, predicted = torch.max(outputs.data, 1)

        total += labels.size(0)
        correct += (predicted == labels).sum().item()

accuracy = 100 * correct / total
print(f"Accuracy on the test set: {accuracy}%")

4. 可选:微调部分特征层(提升精度)

如果新数据集图像数量较多,可以解冻ResNet18的最后几个卷积层(如layer4),和FC层一起微调,让模型更好适配新类别的特征:

# 解冻layer4和FC层参数
for param in model.layer4.parameters():
    param.requires_grad = True
for param in model.fc.parameters():
    param.requires_grad = True

# 使用分层学习率微调,避免破坏已训练特征
optimizer = torch.optim.SGD([
    {'params': model.layer4.parameters(), 'lr': 0.0001},
    {'params': model.fc.parameters(), 'lr': 0.001}
], momentum=0.9)

这种方式既保留了原模型对C罗和梅西的识别能力,又高效新增了对莎拉波娃的识别,无需从头训练整个模型。

内容的提问来源于stack exchange,提问作者Sammy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 00:30:55