一、项目概述
1.1 项目背景
宠物分类是计算机视觉中的重要应用场景,旨在自动识别不同品种的猫狗。本项目使用Oxford-IIIT Pet数据集,通过迁移学习技术构建高效准确的宠物分类模型。
1.2 数据集介绍
Oxford-IIIT Pet数据集包含:
-
37个宠物类别(12种猫+25种狗)
-
7349张图片(训练集:3680,测试集:3669)
-
每张图片包含:
-
宠物主体分割掩码
-
头部区域标注
-
品种标签(如"Abyssinian", "Persian"等)
-
1.3 项目目标
实现多类别宠物品种分类
达到90%以上的分类准确率
展示迁移学习在实际应用中的优势
二、技术架构
2.1 核心思想:迁移学习
迁移学习的优势:
1. 利用在大规模数据集(如ImageNet)上预训练的模型
2. 只需要少量数据即可达到良好效果
3. 减少训练时间和计算资源需求
2.2 技术栈
-
深度学习框架:PyTorch / TensorFlow
-
预训练模型:
-
ResNet50/101
-
EfficientNet
-
VGG16
-
MobileNetV2(轻量化部署)
-
-
数据增强:Albumentations或Torchvision Transforms
-
评估指标:准确率、F1-score、混淆矩阵
三、项目实施步骤
Resnet预训练模型介绍
Resnet是计算机视觉(CV)领域著名的预训练模型,ResNet50是计算机视觉领域最具影响力的深度学习架构之一,由何恺明等人在2015年提出。它解决了深度神经网络中的退化问题,使得训练极深的网络成为可能。下面详细介绍ResNet50的架构与设计思想.
- 传统网络:直接学习目标映射 H(x)
- 残差网络:学习残差映射 F(x) = H(x) – x,最终输出为 F(x) + x
这种设计允许梯度直接流过恒等映射(identity mapping)路径,有效缓解了梯度消失问题,使得训练上百层的网络成为可能。
ResNet50总共有50层(按权重层计算),由以下部分组成:
ResNet50使用Bottleneck结构作为基本单元,每个Bottleneck包含3个卷积层:
具体公式:y = F(x, {Wi}) + x
- F(x)是残差函数
- x是输入
- y是输出
- "+"操作通过元素相加实现,要求F(x)和x维度相同
基于Resnet50和Resnet101实现宠物分类任务
下面给出基于Resnet50实现cifar-10数据集分类任务的具体实现代码:
1.导包与预训练模型的导入
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader, random_split
import os
from tqdm import tqdm
import matplotlib.pyplot as plt
import numpy as np
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
import seaborn as sns
import time
# 设置随机种子以保证可重复性
def set_seed(seed=42):
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
np.random.seed(seed)
set_seed(42)
# 检查是否有可用的GPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"使用设备: {device}")
2.数据准备和增强
# 1. 数据准备和增强
def get_data_transforms():
"""
定义训练和验证的数据转换
"""
# 图像大小调整到适合预训练模型
IMG_SIZE = 224
train_transform = transforms.Compose([
transforms.RandomResizedCrop(IMG_SIZE, scale=(0.8, 1.0)),
transforms.RandomHorizontalFlip(p=0.5),
transforms.RandomRotation(15),
transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1),
transforms.RandomAffine(degrees=0, translate=(0.1, 0.1)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
val_transform = transforms.Compose([
transforms.Resize((IMG_SIZE, IMG_SIZE)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
return train_transform, val_transform
# 2. 加载Oxford-IIIT Pet数据集
def load_pet_dataset(batch_size=32):
"""
加载和准备宠物数据集
"""
train_transform, val_transform = get_data_transforms()
# 下载并加载数据集
train_val_dataset = torchvision.datasets.OxfordIIITPet(
root='./data/oxford_pets',
split='trainval',
target_types='category',
download=False,
transform=train_transform # 暂时使用train_transform,后面会分割
)
test_dataset = torchvision.datasets.OxfordIIITPet(
root='./data/oxford_pets',
split='test',
target_types='category',
download=False,
transform=val_transform
)
# 分割训练集和验证集
dataset_size = len(train_val_dataset)
train_size = int(0.85 * dataset_size) # 85%用于训练
val_size = dataset_size – train_size
train_dataset, val_dataset = random_split(train_val_dataset, [train_size, val_size])
# 为训练集应用训练变换,验证集应用验证变换
# 注意:我们需要手动设置变换,因为random_split会保留原变换
train_dataset.dataset.transform = train_transform
val_dataset.dataset.transform = val_transform
print(f"训练集大小: {len(train_dataset)}")
print(f"验证集大小: {len(val_dataset)}")
print(f"测试集大小: {len(test_dataset)}")
print(f"类别数量: {len(train_val_dataset.classes)}")
# 创建数据加载器
train_loader = DataLoader(
train_dataset,
batch_size=batch_size,
shuffle=True,
num_workers=4,
pin_memory=True
)
val_loader = DataLoader(
val_dataset,
batch_size=batch_size,
shuffle=False,
num_workers=4,
pin_memory=True
)
test_loader = DataLoader(
test_dataset,
batch_size=batch_size,
shuffle=False,
num_workers=4,
pin_memory=True
)
return train_loader, val_loader, test_loader, train_val_dataset.classes
3.构建网络模型
# 3. 定义模型架构(使用预训练的ResNet50)
class PetClassifier(nn.Module):
def __init__(self, num_classes=37, dropout_rate=0.5):
super(PetClassifier, self).__init__()
# 使用预训练的ResNet50作为特征提取器
self.backbone = torchvision.models.resnet50(pretrained=True)
# 冻结前几层(可选,可根据需要调整)
for param in list(self.backbone.parameters())[:100]:
param.requires_grad = False
# 获取ResNet的特征维度
num_features = self.backbone.fc.in_features
# 替换最后的全连接层
self.backbone.fc = nn.Sequential(
nn.Dropout(dropout_rate),
nn.Linear(num_features, 512),
nn.BatchNorm1d(512),
nn.ReLU(),
nn.Dropout(dropout_rate/2),
nn.Linear(512, 256),
nn.BatchNorm1d(256),
nn.ReLU(),
nn.Dropout(dropout_rate/3),
nn.Linear(256, num_classes)
)
def forward(self, x):
return self.backbone(x)
4.编写训练函数和推理函数
# 3. 定义模型架构(使用预训练的ResNet50)
class PetClassifier(nn.Module):
def __init__(self, num_classes=37, dropout_rate=0.5):
super(PetClassifier, self).__init__()
# 使用预训练的ResNet50作为特征提取器
self.backbone = torchvision.models.resnet50(pretrained=True)
# 冻结前几层(可选,可根据需要调整)
for param in list(self.backbone.parameters())[:100]:
param.requires_grad = False
# 获取ResNet的特征维度
num_features = self.backbone.fc.in_features
# 替换最后的全连接层
self.backbone.fc = nn.Sequential(
nn.Dropout(dropout_rate),
nn.Linear(num_features, 512),
nn.BatchNorm1d(512),
nn.ReLU(),
nn.Dropout(dropout_rate/2),
nn.Linear(512, 256),
nn.BatchNorm1d(256),
nn.ReLU(),
nn.Dropout(dropout_rate/3),
nn.Linear(256, num_classes)
)
def forward(self, x):
return self.backbone(x)
# 4. 训练和评估函数
class PetTrainer:
def __init__(self, model, device, num_classes=37):
self.model = model.to(device)
self.device = device
self.num_classes = num_classes
# 使用交叉熵损失
self.criterion = nn.CrossEntropyLoss(label_smoothing=0.1) # 标签平滑防止过拟合
# 使用AdamW优化器(比Adam更好)
self.optimizer = optim.AdamW(
filter(lambda p: p.requires_grad, self.model.parameters()),
lr=0.0001,
weight_decay=1e-4
)
# 使用余弦退火学习率调度器
self.scheduler = optim.lr_scheduler.CosineAnnealingLR(
self.optimizer,
T_max=20, # 20个epoch后重置
eta_min=1e-6
)
# 添加早停机制
self.best_val_acc = 0.0
self.patience = 10
self.counter = 0
def train_epoch(self, train_loader):
self.model.train()
running_loss = 0.0
all_preds = []
all_labels = []
# 使用tqdm显示训练进度
pbar = tqdm(train_loader, desc="训练中", leave=False)
for batch_idx, (inputs, labels) in enumerate(pbar):
inputs, labels = inputs.to(self.device), labels.to(self.device)
# 梯度清零
self.optimizer.zero_grad()
# 前向传播
outputs = self.model(inputs)
loss = self.criterion(outputs, labels)
# 反向传播和优化
loss.backward()
# 梯度裁剪防止梯度爆炸
torch.nn.utils.clip_grad_norm_(self.model.parameters(), max_norm=1.0)
self.optimizer.step()
# 统计
running_loss += loss.item()
_, predicted = outputs.max(1)
all_preds.extend(predicted.cpu().numpy())
all_labels.extend(labels.cpu().numpy())
# 更新进度条
pbar.set_postfix({'loss': loss.item()})
# 计算准确率
train_acc = accuracy_score(all_labels, all_preds)
train_loss = running_loss / len(train_loader)
return train_loss, train_acc
def validate(self, val_loader):
self.model.eval()
running_loss = 0.0
all_preds = []
all_labels = []
with torch.no_grad():
pbar = tqdm(val_loader, desc="验证中", leave=False)
for inputs, labels in pbar:
inputs, labels = inputs.to(self.device), labels.to(self.device)
outputs = self.model(inputs)
loss = self.criterion(outputs, labels)
running_loss += loss.item()
_, predicted = outputs.max(1)
all_preds.extend(predicted.cpu().numpy())
all_labels.extend(labels.cpu().numpy())
val_acc = accuracy_score(all_labels, all_preds)
val_loss = running_loss / len(val_loader)
return val_loss, val_acc
def test(self, test_loader):
self.model.eval()
all_preds = []
all_labels = []
all_probs = []
with torch.no_grad():
pbar = tqdm(test_loader, desc="测试中")
for inputs, labels in pbar:
inputs, labels = inputs.to(self.device), labels.to(self.device)
outputs = self.model(inputs)
probs = torch.nn.functional.softmax(outputs, dim=1)
_, predicted = outputs.max(1)
all_preds.extend(predicted.cpu().numpy())
all_labels.extend(labels.cpu().numpy())
all_probs.extend(probs.cpu().numpy())
test_acc = accuracy_score(all_labels, all_preds)
return test_acc, all_preds, all_labels, all_probs
def train(self, train_loader, val_loader, num_epochs=30):
history = {'train_loss': [], 'train_acc': [], 'val_loss': [], 'val_acc': []}
print("开始训练…")
start_time = time.time()
for epoch in range(num_epochs):
print(f"\\nEpoch {epoch+1}/{num_epochs}")
print("-" * 50)
# 训练一个epoch
train_loss, train_acc = self.train_epoch(train_loader)
# 验证
val_loss, val_acc = self.validate(val_loader)
# 更新学习率
self.scheduler.step()
# 保存历史记录
history['train_loss'].append(train_loss)
history['train_acc'].append(train_acc)
history['val_loss'].append(val_loss)
history['val_acc'].append(val_acc)
# 打印结果
current_lr = self.optimizer.param_groups[0]['lr']
print(f"训练损失: {train_loss:.4f}, 训练准确率: {train_acc:.4f}")
print(f"验证损失: {val_loss:.4f}, 验证准确率: {val_acc:.4f}")
print(f"学习率: {current_lr:.6f}")
# 早停检查和保存最佳模型
if val_acc > self.best_val_acc:
self.best_val_acc = val_acc
self.counter = 0
# 保存最佳模型
torch.save({
'epoch': epoch,
'model_state_dict': self.model.state_dict(),
'optimizer_state_dict': self.optimizer.state_dict(),
'val_acc': val_acc,
}, 'best_pet_classifier.pth')
print(f"保存最佳模型,验证准确率: {val_acc:.4f}")
else:
self.counter += 1
if self.counter >= self.patience:
print(f"早停触发,在epoch {epoch+1}停止训练")
break
# 加载最佳模型
checkpoint = torch.load('best_pet_classifier.pth')
self.model.load_state_dict(checkpoint['model_state_dict'])
training_time = time.time() – start_time
print(f"\\n训练完成!总共用时: {training_time:.2f}秒")
print(f"最佳验证准确率: {self.best_val_acc:.4f}")
return history
5.编写可视化函数
# 5. 可视化函数
def plot_training_history(history):
"""绘制训练历史曲线"""
fig, axes = plt.subplots(1, 2, figsize=(15, 5))
# 绘制损失曲线
axes[0].plot(history['train_loss'], label='训练损失')
axes[0].plot(history['val_loss'], label='验证损失')
axes[0].set_xlabel('Epoch')
axes[0].set_ylabel('损失')
axes[0].set_title('训练和验证损失曲线')
axes[0].legend()
axes[0].grid(True)
# 绘制准确率曲线
axes[1].plot(history['train_acc'], label='训练准确率')
axes[1].plot(history['val_acc'], label='验证准确率')
axes[1].set_xlabel('Epoch')
axes[1].set_ylabel('准确率')
axes[1].set_title('训练和验证准确率曲线')
axes[1].legend()
axes[1].grid(True)
plt.tight_layout()
plt.savefig('training_history.png', dpi=100)
plt.show()
def plot_confusion_matrix(y_true, y_pred, class_names):
"""绘制混淆矩阵"""
cm = confusion_matrix(y_true, y_pred)
plt.figure(figsize=(12, 10))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=class_names, yticklabels=class_names)
plt.title('混淆矩阵')
plt.xlabel('预测标签')
plt.ylabel('真实标签')
plt.xticks(rotation=45, ha='right')
plt.tight_layout()
plt.savefig('confusion_matrix.png', dpi=100)
plt.show()
def visualize_predictions(model, test_loader, class_names, device, num_samples=10):
"""可视化部分预测结果"""
model.eval()
images_shown = 0
with torch.no_grad():
for inputs, labels in test_loader:
inputs, labels = inputs.to(device), labels.to(device)
outputs = model(inputs)
_, preds = torch.max(outputs, 1)
probs = torch.nn.functional.softmax(outputs, dim=1)
inputs = inputs.cpu()
labels = labels.cpu()
preds = preds.cpu()
probs = probs.cpu()
fig, axes = plt.subplots(2, 5, figsize=(15, 6))
axes = axes.ravel()
for idx in range(min(num_samples, len(inputs))):
# 反标准化图像
img = inputs[idx].numpy().transpose(1, 2, 0)
mean = np.array([0.485, 0.456, 0.406])
std = np.array([0.229, 0.224, 0.225])
img = std * img + mean
img = np.clip(img, 0, 1)
axes[idx].imshow(img)
true_label = class_names[labels[idx]]
pred_label = class_names[preds[idx]]
prob = probs[idx][preds[idx]].item()
color = 'green' if labels[idx] == preds[idx] else 'red'
axes[idx].set_title(f"真实: {true_label}\\n预测: {pred_label}\\n置信度: {prob:.2f}",
color=color, fontsize=9)
axes[idx].axis('off')
images_shown += 1
if images_shown >= num_samples:
break
plt.tight_layout()
plt.savefig('sample_predictions.png', dpi=100)
plt.show()
break
6.集成学习增强(可选)
# 6. 集成学习增强(可选)
class EnsembleModel:
def __init__(self, model_paths, num_classes=37, device='cuda'):
self.models = []
self.device = device
for path in model_paths:
model = PetClassifier(num_classes=num_classes)
checkpoint = torch.load(path, map_location=device)
model.load_state_dict(checkpoint['model_state_dict'])
model.to(device)
model.eval()
self.models.append(model)
def predict(self, inputs):
all_probs = []
with torch.no_grad():
for model in self.models:
outputs = model(inputs)
probs = torch.nn.functional.softmax(outputs, dim=1)
all_probs.append(probs)
# 平均所有模型的概率
avg_probs = torch.stack(all_probs).mean(0)
_, predictions = torch.max(avg_probs, 1)
return predictions, avg_probs
7.编写主函数
# 7. 主程序
def main():
# 参数设置
BATCH_SIZE = 32
NUM_EPOCHS = 40
NUM_CLASSES = 37
print("=" * 60)
print("Oxford-IIIT Pet 数据集分类任务")
print("=" * 60)
# 加载数据
print("\\n1. 加载数据集…")
train_loader, val_loader, test_loader, class_names = load_pet_dataset(BATCH_SIZE)
# 创建模型
print("\\n2. 创建模型…")
model = PetClassifier(num_classes=NUM_CLASSES, dropout_rate=0.5)
# 打印模型摘要
total_params = sum(p.numel() for p in model.parameters())
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
print(f"总参数: {total_params:,}")
print(f"可训练参数: {trainable_params:,}")
# 创建训练器并训练
print("\\n3. 开始训练…")
trainer = PetTrainer(model, device, NUM_CLASSES)
history = trainer.train(train_loader, val_loader, NUM_EPOCHS)
# 绘制训练历史
print("\\n4. 绘制训练历史…")
plot_training_history(history)
# 在测试集上评估
print("\\n5. 在测试集上评估模型…")
test_acc, test_preds, test_labels, test_probs = trainer.test(test_loader)
print(f"测试集准确率: {test_acc:.4f}")
# 生成分类报告
print("\\n6. 生成分类报告…")
report = classification_report(test_labels, test_preds, target_names=class_names)
print(report)
# 绘制混淆矩阵
print("\\n7. 绘制混淆矩阵…")
plot_confusion_matrix(test_labels, test_preds, class_names)
# 可视化一些预测结果
print("\\n8. 可视化预测结果…")
visualize_predictions(model, test_loader, class_names, device, num_samples=10)
# 性能分析
print("\\n9. 性能分析…")
if test_acc >= 0.95:
print(f"✅ 目标达成!测试准确率: {test_acc:.4f} (> 0.95)")
else:
print(f"⚠️ 未达到目标,测试准确率: {test_acc:.4f} (< 0.95)")
# 保存最终模型
torch.save({
'model_state_dict': model.state_dict(),
'class_names': class_names,
'test_accuracy': test_acc
}, 'final_pet_classifier.pth')
print("\\n✅ 模型已保存为 'final_pet_classifier.pth'")
8.优化版
# 8. 进阶技巧:微调和数据增强增强版
def train_with_advanced_techniques():
"""
使用更高级的技术训练模型
"""
print("\\n使用进阶技巧训练模型…")
# 使用更大的批处理大小和更复杂的增强
BATCH_SIZE = 48
# 定义更复杂的数据增强
IMG_SIZE = 256
advanced_train_transform = transforms.Compose([
transforms.RandomResizedCrop(IMG_SIZE, scale=(0.7, 1.0)),
transforms.RandomHorizontalFlip(p=0.5),
transforms.RandomVerticalFlip(p=0.2),
transforms.RandomRotation(20),
transforms.ColorJitter(brightness=0.3, contrast=0.3, saturation=0.3, hue=0.2),
transforms.RandomAffine(degrees=0, translate=(0.15, 0.15), scale=(0.9, 1.1)),
transforms.RandomPerspective(distortion_scale=0.2, p=0.3),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
transforms.RandomErasing(p=0.2, scale=(0.02, 0.2), ratio=(0.3, 3.3))
])
val_transform = transforms.Compose([
transforms.Resize((IMG_SIZE, IMG_SIZE)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
# 加载数据
train_val_dataset = torchvision.datasets.OxfordIIITPet(
root='./data/oxford_pets',
split='trainval',
target_types='category',
download=False,
transform=advanced_train_transform
)
test_dataset = torchvision.datasets.OxfordIIITPet(
root='./data/oxford_pets',
split='test',
target_types='category',
download=False,
transform=val_transform
)
# 分割数据集
dataset_size = len(train_val_dataset)
train_size = int(0.9 * dataset_size) # 90%用于训练
val_size = dataset_size – train_size
train_dataset, val_dataset = random_split(train_val_dataset, [train_size, val_size])
val_dataset.dataset.transform = val_transform
# 创建数据加载器
train_loader = DataLoader(
train_dataset,
batch_size=BATCH_SIZE,
shuffle=True,
num_workers=6,
pin_memory=True,
drop_last=True
)
val_loader = DataLoader(
val_dataset,
batch_size=BATCH_SIZE,
shuffle=False,
num_workers=6,
pin_memory=True
)
test_loader = DataLoader(
test_dataset,
batch_size=BATCH_SIZE,
shuffle=False,
num_workers=6,
pin_memory=True
)
# 使用更大的模型(ResNet101)
model = torchvision.models.resnet101(pretrained=True)
num_features = model.fc.in_features
# 更复杂的分类头
model.fc = nn.Sequential(
nn.Dropout(0.5),
nn.Linear(num_features, 1024),
nn.BatchNorm1d(1024),
nn.ReLU(),
nn.Dropout(0.4),
nn.Linear(1024, 512),
nn.BatchNorm1d(512),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(512, 256),
nn.BatchNorm1d(256),
nn.ReLU(),
nn.Dropout(0.2),
nn.Linear(256, 37)
)
model = model.to(device)
# 使用SWA(随机权重平均)技术
optimizer = optim.AdamW(model.parameters(), lr=0.0001, weight_decay=1e-4)
scheduler = optim.lr_scheduler.CosineAnnealingWarmRestarts(
optimizer,
T_0=10, # 初始周期长度
T_mult=2, # 周期倍增因子
eta_min=1e-6
)
# 训练循环(简化版)
best_acc = 0.0
for epoch in range(30):
print(f"\\nEpoch {epoch+1}/30")
# 训练阶段
model.train()
train_loss = 0.0
train_correct = 0
train_total = 0
pbar = tqdm(train_loader, desc=f"训练 Epoch {epoch+1}")
for inputs, labels in pbar:
inputs, labels = inputs.to(device), labels.to(device)
optimizer.zero_grad()
outputs = model(inputs)
loss = nn.CrossEntropyLoss()(outputs, labels)
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
optimizer.step()
train_loss += loss.item()
_, predicted = outputs.max(1)
train_total += labels.size(0)
train_correct += predicted.eq(labels).sum().item()
pbar.set_postfix({'loss': loss.item(), 'acc': train_correct/train_total})
scheduler.step()
# 验证阶段
model.eval()
val_correct = 0
val_total = 0
with torch.no_grad():
for inputs, labels in val_loader:
inputs, labels = inputs.to(device), labels.to(device)
outputs = model(inputs)
_, predicted = outputs.max(1)
val_total += labels.size(0)
val_correct += predicted.eq(labels).sum().item()
val_acc = val_correct / val_total
print(f"验证准确率: {val_acc:.4f}")
# 保存最佳模型
if val_acc > best_acc:
best_acc = val_acc
torch.save(model.state_dict(), 'advanced_model.pth')
print(f"保存最佳模型,准确率: {val_acc:.4f}")
return best_acc
9.运行代码:
if __name__ == "__main__":
try:
main()
except Exception as e:
print(f"发生错误: {e}")
print("尝试使用进阶技巧…")
# 如果主方法失败,尝试进阶方法
try:
acc = train_with_advanced_techniques()
print(f"进阶方法训练完成,最佳准确率: {acc:.4f}")
if acc >= 0.95:
print("✅ 使用进阶技巧达到目标准确率!")
except Exception as e2:
print(f"进阶方法也失败: {e2}")
print("请检查数据下载或安装必要的包")
10.运行结果展示
mlstat) [haichao@node01 demo1]$ python demo4.py
使用设备: cpu
============================================================
Oxford-IIIT Pet 数据集分类任务
============================================================
1. 加载数据集…
训练集大小: 3128
验证集大小: 552
测试集大小: 3669
类别数量: 37
2. 创建模型…
/home/haichao/anaconda/anaconda_install/envs/mlstat/lib/python3.11/site-packages/torchvision/models/_utils.py:208: UserWarning: The parameter 'pretrained' is deprecated since 0.13 and may be removed in the future, please use 'weights' instead.
warnings.warn(
/home/haichao/anaconda/anaconda_install/envs/mlstat/lib/python3.11/site-packages/torchvision/models/_utils.py:223: UserWarning: Arguments other than a weight enum or `None` for 'weights' are deprecated since 0.13 and may be removed in the future. The current behavior is equivalent to passing `weights=ResNet50_Weights.IMAGENET1K_V1`. You can also use `weights=ResNet50_Weights.DEFAULT` to get the most up-to-date weights.
warnings.warn(msg)
Downloading: "https://download.pytorch.org/models/resnet50-0676ba61.pth" to /home/haichao/.cache/torch/hub/checkpoints/resnet50-0676ba61.pth
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 97.8M/97.8M [01:32<00:00, 1.11MB/s]
总参数: 24,699,493
可训练参数: 19,509,797
3. 开始训练…
开始训练…
Epoch 1/40
————————————————–
训练损失: 2.8773, 训练准确率: 0.4012
验证损失: 1.8949, 验证准确率: 0.7989
学习率: 0.000099
保存最佳模型,验证准确率: 0.7989
Epoch 2/40
————————————————–
训练损失: 1.7745, 训练准确率: 0.7874
验证损失: 1.3117, 验证准确率: 0.8859
学习率: 0.000098
保存最佳模型,验证准确率: 0.8859
Epoch 3/40
————————————————–
训练损失: 1.3021, 训练准确率: 0.8795
验证损失: 1.1134, 验证准确率: 0.8967
学习率: 0.000095
保存最佳模型,验证准确率: 0.8967
Epoch 4/40
————————————————–
训练损失: 1.0909, 训练准确率: 0.9105
验证损失: 1.0414, 验证准确率: 0.8841
学习率: 0.000091
Epoch 5/40
————————————————–
训练损失: 0.9758, 训练准确率: 0.9313
验证损失: 1.0194, 验证准确率: 0.8877
学习率: 0.000086
Epoch 6/40
————————————————–
训练损失: 0.8985, 训练准确率: 0.9511
验证损失: 1.1017, 验证准确率: 0.8533
学习率: 0.000080
Epoch 7/40
————————————————–
训练损失: 0.8466, 训练准确率: 0.9655
验证损失: 1.0448, 验证准确率: 0.8895
学习率: 0.000073
Epoch 8/40
————————————————–
训练损失: 0.8186, 训练准确率: 0.9696
验证损失: 0.9821, 验证准确率: 0.9076
学习率: 0.000066
保存最佳模型,验证准确率: 0.9076
Epoch 9/40
————————————————–
训练损失: 0.7915, 训练准确率: 0.9786
验证损失: 0.9865, 验证准确率: 0.8967
学习率: 0.000058
Epoch 10/40
————————————————–
训练损失: 0.7790, 训练准确率: 0.9840
验证损失: 1.0045, 验证准确率: 0.8986
学习率: 0.000051
Epoch 11/40
————————————————–
训练损失: 0.7580, 训练准确率: 0.9872
验证损失: 0.9778, 验证准确率: 0.9040
学习率: 0.000043
Epoch 12/40
————————————————–
训练损失: 0.7432, 训练准确率: 0.9910
验证损失: 1.0484, 验证准确率: 0.9004
学习率: 0.000035
Epoch 13/40
————————————————–
训练损失: 0.7395, 训练准确率: 0.9917
验证损失: 0.9999, 验证准确率: 0.9130
学习率: 0.000028
保存最佳模型,验证准确率: 0.9130
Epoch 14/40
————————————————–
训练损失: 0.7299, 训练准确率: 0.9923
验证损失: 1.0175, 验证准确率: 0.9130
学习率: 0.000021
Epoch 15/40
————————————————–
训练损失: 0.7217, 训练准确率: 0.9958
验证损失: 0.9745, 验证准确率: 0.9312
学习率: 0.000015
保存最佳模型,验证准确率: 0.9312
Epoch 16/40
————————————————–
训练损失: 0.7198, 训练准确率: 0.9958
验证损失: 0.9395, 验证准确率: 0.9330
学习率: 0.000010
保存最佳模型,验证准确率: 0.9330
Epoch 17/40
————————————————–
训练损失: 0.7161, 训练准确率: 0.9974
验证损失: 0.9662, 验证准确率: 0.9185
学习率: 0.000006
Epoch 18/40
————————————————–
训练损失: 0.7121, 训练准确率: 0.9984
验证损失: 0.9268, 验证准确率: 0.9239
学习率: 0.000003
Epoch 19/40
————————————————–
训练损失: 0.7118, 训练准确率: 0.9981
验证损失: 0.9353, 验证准确率: 0.9221
学习率: 0.000002
Epoch 20/40
————————————————–
训练损失: 0.7149, 训练准确率: 0.9968
验证损失: 0.9530, 验证准确率: 0.9203
学习率: 0.000001
Epoch 21/40
————————————————–
训练损失: 0.7125, 训练准确率: 0.9984
验证损失: 1.0208, 验证准确率: 0.9130
学习率: 0.000002
Epoch 22/40
————————————————–
训练损失: 0.7104, 训练准确率: 0.9984
验证损失: 0.9540, 验证准确率: 0.9221
学习率: 0.000003
Epoch 23/40
————————————————–
训练损失: 0.7108, 训练准确率: 0.9984
验证损失: 0.9207, 验证准确率: 0.9348
学习率: 0.000006
保存最佳模型,验证准确率: 0.9348
Epoch 24/40
————————————————–
训练损失: 0.7081, 训练准确率: 0.9984
验证损失: 0.9419, 验证准确率: 0.9293
学习率: 0.000010
Epoch 25/40
————————————————–
训练损失: 0.7099, 训练准确率: 0.9987
验证损失: 1.0030, 验证准确率: 0.9167
学习率: 0.000015
Epoch 26/40
————————————————–
训练损失: 0.7163, 训练准确率: 0.9962
验证损失: 0.9952, 验证准确率: 0.9130
学习率: 0.000021
Epoch 27/40
————————————————–
训练损失: 0.7101, 训练准确率: 0.9974
验证损失: 0.9511, 验证准确率: 0.9203
学习率: 0.000028
Epoch 28/40
————————————————–
训练损失: 0.7103, 训练准确率: 0.9981
验证损失: 0.9734, 验证准确率: 0.9149
学习率: 0.000035
Epoch 29/40
————————————————–
训练损失: 0.7149, 训练准确率: 0.9962
验证损失: 1.0185, 验证准确率: 0.9094
学习率: 0.000043
Epoch 30/40
————————————————–
训练损失: 0.7138, 训练准确率: 0.9965
验证损失: 1.0045, 验证准确率: 0.9094
学习率: 0.000050
Epoch 31/40
————————————————–
训练损失: 0.7162, 训练准确率: 0.9946
验证损失: 1.0169, 验证准确率: 0.9094
学习率: 0.000058
Epoch 32/40
————————————————–
训练损失: 0.7264, 训练准确率: 0.9923
验证损失: 1.0629, 验证准确率: 0.9022
学习率: 0.000066
Epoch 33/40
————————————————–
训练损失: 0.7261, 训练准确率: 0.9914
验证损失: 1.0436, 验证准确率: 0.8949
学习率: 0.000073
早停触发,在epoch 33停止训练
发生错误: Weights only load failed. This file can still be loaded, to do so you have two options, do those steps only if you trust the source of the checkpoint.
(1) In PyTorch 2.6, we changed the default value of the `weights_only` argument in `torch.load` from `False` to `True`. Re-running `torch.load` with `weights_only` set to `False` will likely succeed, but it can result in arbitrary code execution. Do it only if you got the file from a trusted source.
(2) Alternatively, to load with `weights_only=True` please check the recommended steps in the following error message.
WeightsUnpickler error: Unsupported global: GLOBAL numpy.core.multiarray.scalar was not an allowed global by default. Please use `torch.serialization.add_safe_globals([scalar])` or the `torch.serialization.safe_globals([scalar])` context manager to allowlist this global if you trust this class/function.
Check the documentation of torch.load to learn more about types accepted by default with weights_only https://pytorch.org/docs/stable/generated/torch.load.html.
尝试使用进阶技巧…
使用进阶技巧训练模型…
/home/haichao/anaconda/anaconda_install/envs/mlstat/lib/python3.11/site-packages/torchvision/models/_utils.py:208: UserWarning: The parameter 'pretrained' is deprecated since 0.13 and may be removed in the future, please use 'weights' instead.
warnings.warn(
/home/haichao/anaconda/anaconda_install/envs/mlstat/lib/python3.11/site-packages/torchvision/models/_utils.py:223: UserWarning: Arguments other than a weight enum or `None` for 'weights' are deprecated since 0.13 and may be removed in the future. The current behavior is equivalent to passing `weights=ResNet101_Weights.IMAGENET1K_V1`. You can also use `weights=ResNet101_Weights.DEFAULT` to get the most up-to-date weights.
warnings.warn(msg)
Epoch 1/30
训练 Epoch 1: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:07<00:00, 3.59s/it, loss=3.08, acc=0.0848]
验证准确率: 0.3804
保存最佳模型,准确率: 0.3804
Epoch 2/30
训练 Epoch 2: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:08<00:00, 3.60s/it, loss=2.35, acc=0.391]
验证准确率: 0.5435
保存最佳模型,准确率: 0.5435
Epoch 3/30
训练 Epoch 3: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:09<00:00, 3.62s/it, loss=1.81, acc=0.617]
验证准确率: 0.7473
保存最佳模型,准确率: 0.7473
Epoch 4/30
训练 Epoch 4: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:08<00:00, 3.61s/it, loss=1.38, acc=0.761]
验证准确率: 0.8179
保存最佳模型,准确率: 0.8179
Epoch 5/30
训练 Epoch 5: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:09<00:00, 3.62s/it, loss=1.02, acc=0.821]
验证准确率: 0.8397
保存最佳模型,准确率: 0.8397
Epoch 6/30
训练 Epoch 6: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:10<00:00, 3.64s/it, loss=0.97, acc=0.864]
验证准确率: 0.8370
Epoch 7/30
训练 Epoch 7: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:05<00:00, 3.56s/it, loss=0.817, acc=0.896]
验证准确率: 0.8859
保存最佳模型,准确率: 0.8859
Epoch 8/30
训练 Epoch 8: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:09<00:00, 3.62s/it, loss=0.659, acc=0.912]
验证准确率: 0.8967
保存最佳模型,准确率: 0.8967
Epoch 9/30
训练 Epoch 9: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:13<00:00, 3.68s/it, loss=0.583, acc=0.926]
验证准确率: 0.9022
保存最佳模型,准确率: 0.9022
Epoch 10/30
训练 Epoch 10: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:11<00:00, 3.64s/it, loss=0.633, acc=0.944]
验证准确率: 0.9049
保存最佳模型,准确率: 0.9049
Epoch 11/30
训练 Epoch 11: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:12<00:00, 3.66s/it, loss=0.698, acc=0.885]
验证准确率: 0.8370
Epoch 12/30
训练 Epoch 12: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:06<00:00, 3.58s/it, loss=0.671, acc=0.886]
验证准确率: 0.8261
Epoch 13/30
训练 Epoch 13: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:07<00:00, 3.59s/it, loss=0.6, acc=0.885]
验证准确率: 0.8288
Epoch 14/30
训练 Epoch 14: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:10<00:00, 3.63s/it, loss=0.43, acc=0.901]
验证准确率: 0.8424
Epoch 15/30
训练 Epoch 15: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:11<00:00, 3.65s/it, loss=0.441, acc=0.906]
验证准确率: 0.8505
Epoch 16/30
训练 Epoch 16: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:07<00:00, 3.59s/it, loss=0.56, acc=0.911]
验证准确率: 0.8614
Epoch 17/30
训练 Epoch 17: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:16<00:00, 3.72s/it, loss=0.484, acc=0.927]
验证准确率: 0.8804
Epoch 18/30
训练 Epoch 18: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:32<00:00, 3.94s/it, loss=0.287, acc=0.929]
验证准确率: 0.8370
Epoch 19/30
训练 Epoch 19: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [04:26<00:00, 3.86s/it, loss=0.158, acc=0.932]
验证准确率: 0.8750
Epoch 20/30
训练 Epoch 20: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:48<00:00, 3.31s/it, loss=0.247, acc=0.949]
验证准确率: 0.8723
Epoch 21/30
训练 Epoch 21: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:28<00:00, 3.02s/it, loss=0.27, acc=0.953]
验证准确率: 0.8886
Epoch 22/30
训练 Epoch 22: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:28<00:00, 3.02s/it, loss=0.125, acc=0.958]
验证准确率: 0.9076
保存最佳模型,准确率: 0.9076
Epoch 23/30
训练 Epoch 23: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:26<00:00, 3.00s/it, loss=0.165, acc=0.963]
验证准确率: 0.8913
Epoch 24/30
训练 Epoch 24: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:29<00:00, 3.04s/it, loss=0.111, acc=0.971]
验证准确率: 0.8913
Epoch 25/30
训练 Epoch 25: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:25<00:00, 2.97s/it, loss=0.168, acc=0.971]
验证准确率: 0.9266
保存最佳模型,准确率: 0.9266
Epoch 26/30
训练 Epoch 26: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:26<00:00, 2.99s/it, loss=0.307, acc=0.98]
验证准确率: 0.9293
保存最佳模型,准确率: 0.9293
Epoch 27/30
训练 Epoch 27: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:27<00:00, 3.01s/it, loss=0.147, acc=0.978]
验证准确率: 0.8967
Epoch 28/30
训练 Epoch 28: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:25<00:00, 2.98s/it, loss=0.179, acc=0.981]
验证准确率: 0.9212
Epoch 29/30
训练 Epoch 29: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:35<00:00, 3.12s/it, loss=0.104, acc=0.982]
验证准确率: 0.9130
Epoch 30/30
训练 Epoch 30: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 69/69 [03:40<00:00, 3.19s/it, loss=0.1, acc=0.979]
验证准确率: 0.9103
进阶方法训练完成,最佳准确率: 0.9293
这个代码还有继续优化的空间,感兴趣的友友可以在这个基础上进行模型微调及参数优化。
最终模型的大小为244MB
总用量 3.2G
drwxrwxr-x 15 haichao haichao 4.0K 1月 22 15:13 .
drwx——. 37 haichao haichao 4.0K 1月 22 18:15 ..
-rw-rw-r– 1 haichao haichao 387K 1月 20 16:23 adaptive_rk_variable_stiffness.png
-rw-rw-r– 1 haichao haichao 174M 1月 22 15:36 advanced_model.pth
drwxrwxr-x 2 haichao haichao 4.0K 1月 5 13:51 atomsk
-rw-rw-r– 1 haichao haichao 244M 1月 22 13:38 best_pet_classifier.pth
drwxrwxr-x 3 haichao haichao 4.0K 1月 19 20:29 data
-rw-rw-r– 1 haichao haichao 1.6G 1月 20 10:28 data.zip
-rw-rw-r– 1 haichao haichao 20K 1月 20 16:23 demo10.py
-rw-rw-r– 1 haichao haichao 106K 1月 20 15:49 demo1.png
-rw-rw-r– 1 haichao haichao 251 11月 18 21:31 demo1.py
-rw-rw-r– 1 haichao haichao 102 11月 28 15:19 demo2.py
-rw-rw-r– 1 haichao haichao 18K 1月 19 20:29 demo3.py
-rw-rw-r– 1 haichao haichao 24K 1月 22 13:09 demo4.py







