基于ResNet50的血细胞图像分类模型训练全记录

基于ResNet50的血细胞图像分类模型训练全记录

项目概述

本项目使用深度学习技术对血细胞图像进行自动分类,识别四种主要的血细胞类型:

  • 嗜酸性粒细胞 (EOSINOPHIL)
  • 淋巴细胞 (LYMPHOCYTE)
  • 单核细胞 (MONOCYTE)
  • 中性粒细胞 (NEUTROPHIL)

模型架构

采用预训练的ResNet50作为骨干网络,这是一个在ImageNet上预训练的深度残差网络,具有强大的特征提取能力。

模型参数统计:

  • 总参数数量:23,516,228
  • 网络深度:50层
  • 预训练权重:ImageNet

数据集信息

训练集:

  • 嗜酸性粒细胞:2,494张图像
  • 淋巴细胞:2,480张图像
  • 单核细胞:2,475张图像
  • 中性粒细胞:2,496张图像

验证集:

  • 嗜酸性粒细胞:374张图像
  • 淋巴细胞:372张图像
  • 单核细胞:371张图像
  • 中性粒细胞:374张图像

训练策略

损失函数

使用加权交叉熵损失,针对数据不平衡问题:

weights = [0.9971, 1.0023, 1.0046, 0.9960]

训练参数

  • 训练轮数: 50 epochs
  • 早停机制: 验证准确率连续下降时自动停止
  • 优化器: AdamW
  • 学习率调度: 余弦退火重启调度器

核心代码实现

1. 数据集类定义

class BloodCellDataset(Dataset):
    """血细胞图像数据集"""
    
    def __init__(self, data_root: str, split: str, transform=None):
        self.data_root = Path(data_root)
        self.split = split
        self.transform = transform
        
        # 类别映射
        self.class_names = ['EOSINOPHIL', 'LYMPHOCYTE', 'MONOCYTE', 'NEUTROPHIL']
        self.class_to_idx = {name: i for i, name in enumerate(self.class_names)}
        
        # 加载图像路径和标签
        self.samples = self._load_samples()
    
    def _load_samples(self):
        """加载样本路径和标签"""
        samples = []
        split_dir = self.data_root / self.split
        
        for class_name in self.class_names:
            class_dir = split_dir / class_name
            if not class_dir.exists():
                continue
                
            class_idx = self.class_to_idx[class_name]
            
            # 获取所有图片文件
            for img_path in class_dir.glob('*.jpeg'):
                samples.append((str(img_path), class_idx))
        
        return samples

2. 模型架构定义

class BloodCellClassifier(nn.Module):
    """血细胞分类器"""
    
    def __init__(self, model_name: str = 'resnet50', num_classes: int = 4, pretrained: bool = True):
        super(BloodCellClassifier, self).__init__()
        
        self.model_name = model_name
        self.num_classes = num_classes
        
        # 加载预训练模型
        if model_name == 'resnet50':
            self.backbone = models.resnet50(pretrained=pretrained)
            in_features = self.backbone.fc.in_features
            self.backbone.fc = nn.Linear(in_features, num_classes)
        
    def forward(self, x):
        return self.backbone(x)

3. 数据增强策略

def _setup_transforms(self):
    """设置数据变换"""
    # 训练时的数据增强
    self.train_transform = transforms.Compose([
        transforms.Resize((256, 256)),
        transforms.RandomResizedCrop(224, scale=(0.8, 1.0)),
        transforms.RandomHorizontalFlip(p=0.5),
        transforms.RandomRotation(degrees=10),
        transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.1, hue=0.1),
        transforms.ToTensor(),
        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
    ])
    
    # 验证和测试时的变换
    self.val_transform = transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor(),
        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
    ])

4. 训练循环实现

def train_epoch(self) -> Dict[str, float]:
    """训练一个epoch"""
    self.model.train()
    
    running_loss = 0.0
    correct = 0
    total = 0
    
    pbar = tqdm(self.train_loader, desc='训练中')
    for batch_idx, (images, labels) in enumerate(pbar):
        images, labels = images.to(self.device), labels.to(self.device)
        
        # 前向传播
        self.optimizer.zero_grad()
        outputs = self.model(images)
        loss = self.criterion(outputs, labels)
        
        # 反向传播
        loss.backward()
        self.optimizer.step()
        
        # 统计
        running_loss += loss.item()
        _, predicted = outputs.max(1)
        total += labels.size(0)
        correct += predicted.eq(labels).sum().item()
        
        # 更新进度条
        pbar.set_postfix({
            'Loss': f'{loss.item():.4f}',
            'Acc': f'{100.*correct/total:.2f}%'
        })
    
    epoch_loss = running_loss / len(self.train_loader)
    epoch_acc = 100. * correct / total
    
    return {'loss': epoch_loss, 'accuracy': epoch_acc}

5. 早停机制

def _setup_training(self):
    """设置训练相关组件"""
    # 计算类别权重来处理不平衡数据集
    class_counts = self._calculate_class_weights()
    if class_counts:
        weights = torch.tensor([1.0/count for count in class_counts.values()], dtype=torch.float32)
        weights = weights / weights.sum() * len(weights)  # 归一化
        self.criterion = nn.CrossEntropyLoss(weight=weights.to(self.device))
    
    # 优化器 - 使用AdamW
    self.optimizer = optim.AdamW(
        self.model.parameters(),
        lr=0.001,
        weight_decay=0.01,
        betas=(0.9, 0.999),
        eps=1e-8
    )
    
    # 余弦退火重启调度器
    self.scheduler = optim.lr_scheduler.CosineAnnealingWarmRestarts(
        self.optimizer,
        T_0=10,  # 初始重启周期
        T_mult=2,  # 周期倍增因子
        eta_min=1e-6  # 最小学习率
    )
    
    # 早停机制
    self.early_stopping_patience = 15
    self.early_stopping_counter = 0
    self.best_val_loss = float('inf')

6. 主训练函数

def train(self, epochs: int = 50):
    """训练模型"""
    print(f"🚀 开始训练 {epochs} 个epochs")
    
    best_val_acc = 0.0
    train_losses = []
    val_losses = []
    train_accs = []
    val_accs = []
    
    for epoch in range(epochs):
        # 训练
        train_metrics = self.train_epoch()
        
        # 验证
        val_metrics = self.validate()
        
        # 记录指标
        train_losses.append(train_metrics['loss'])
        val_losses.append(val_metrics['loss'])
        train_accs.append(train_metrics['accuracy'])
        val_accs.append(val_metrics['accuracy'])
        
        # 学习率调度
        self.scheduler.step()
        
        # 早停检查
        if val_metrics['loss'] < self.best_val_loss:
            self.best_val_loss = val_metrics['loss']
            self.early_stopping_counter = 0
        else:
            self.early_stopping_counter += 1
            
        if self.early_stopping_counter >= self.early_stopping_patience:
            print(f"\n⏱️ Early stopping triggered after {epoch+1} epochs")
            break
        
        # 保存最佳模型
        if val_metrics['accuracy'] > best_val_acc:
            best_val_acc = val_metrics['accuracy']
            self.save_model('best.pt')
            print(f"✅ 保存最佳模型 (验证准确率: {best_val_acc:.2f}%)")

训练过程分析

训练曲线详细分析

训练曲线详细分析

从训练曲线可以看出:

  • 训练准确率:从75.99%快速提升到99.89%
  • 验证准确率:从65.06%稳步提升到100.00%
  • 训练损失:从0.5884快速下降到0.0039
  • 验证损失:从1.7814下降到0.0005

关键训练阶段

  1. 快速学习阶段 (Epoch 1-10):模型快速适应数据分布
  2. 稳定提升阶段 (Epoch 11-30):性能持续改善
  3. 收敛阶段 (Epoch 31-45):达到最优性能

早停触发

训练在第45轮时触发早停机制,避免了过拟合,最终最佳验证准确率达到100.00%

最终性能评估

整体性能指标

性能总结

最终测试结果:

  • 整体准确率: 99.00%
  • 宏平均F1分数: 0.99
  • 加权平均F1分数: 0.99

各类别详细性能

细胞类型精确率召回率F1分数支持数
嗜酸性粒细胞1.000.980.99374
淋巴细胞1.000.991.00372
单核细胞0.991.001.00371
中性粒细胞0.981.000.99374

混淆矩阵分析

混淆矩阵分析

混淆矩阵显示:

  • 所有类别的分类准确率都非常高
  • 几乎没有类别间的混淆
  • 模型对各类血细胞都有很好的识别能力

模型架构可视化

模型架构

ResNet50的核心特点:

  • 残差连接:解决深层网络的梯度消失问题
  • 批量归一化:加速训练收敛
  • 全局平均池化:减少参数数量
  • 全连接分类层:输出4个类别的概率分布

预测结果示例

样本预测结果

展示了模型在实际血细胞图像上的预测效果,包括:

  • 原始图像
  • 预测类别
  • 预测置信度
  • 真实标签对比

训练亮点

  1. 快速收敛:仅用45轮就达到最优性能
  2. 高准确率:验证集准确率达到100%
  3. 无过拟合:训练和验证性能同步提升
  4. 类别平衡:各类别性能均衡,无偏向性

🚀 应用前景

该模型可以应用于:

  • 医学诊断:辅助医生进行血细胞计数和分类
  • 实验室自动化:减少人工操作,提高效率
  • 医学教育:作为教学工具帮助医学生学习
  • 研究支持:为血液学研究提供数据支持

技术总结

  1. 数据预处理:适当的图像增强和标准化
  2. 模型选择:ResNet50在医学图像分类任务中表现优异
  3. 训练策略:加权损失函数解决数据不平衡问题
  4. 正则化:早停机制防止过拟合
  5. 评估方法:多维度性能指标全面评估模型

🔮 未来改进方向

  1. 数据增强:增加更多样化的训练数据
  2. 模型优化:尝试更先进的网络架构
  3. 多模态融合:结合临床信息提高诊断准确性
  4. 实时推理:优化模型推理速度
  5. 部署优化:开发用户友好的医疗软件界面

本文档记录了基于ResNet50的血细胞分类模型的完整训练过程,展示了深度学习在医学图像分析中的强大应用潜力。

训练完成时间: 45 epochs
最佳验证准确率: 100.00%
模型保存路径: runs/classification/train/best.pt

好的,以下是基于PyTorch训练基于ResNet50图像分类模型的代码: ```python import torch import torch.nn as nn import torchvision.transforms as transforms from torch.utils.data import DataLoader from torchvision.datasets import ImageFolder from torchvision.models.resnet import resnet50 # 定义超参数 num_epochs = 10 batch_size = 32 learning_rate = 0.001 # 加载数据集并进行预处理 transform = transforms.Compose([ transforms.Resize(224), transforms.CenterCrop(224), transforms.ToTensor() ]) train_dataset = ImageFolder('train/', transform=transform) test_dataset = ImageFolder('test/', transform=transform) train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True) test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False) # 定义模型和损失函数 model = resnet50(pretrained=True) model.fc = nn.Linear(model.fc.in_features, 2) # 全连接层的输出为2(前景和背景) criterion = nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate) # 训练模型 total_step = len(train_loader) for epoch in range(num_epochs): for i, (images, labels) in enumerate(train_loader): # 前向传播 outputs = model(images) loss = criterion(outputs, labels) # 反向传播并优化 optimizer.zero_grad() loss.backward() optimizer.step() if (i+1) % 20 == 0: print ('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}'.format(epoch+1, num_epochs, i+1, total_step, loss.item())) # 测试模型 with torch.no_grad(): correct = 0 total = 0 for images, labels in test_loader: outputs = model(images) _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum().item() print('Accuracy of the model on the test images: {} %'.format(100 * correct / total)) ``` 希望这个代码能对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值