我听到了⌈归来⌋的回响😊
间断了一天,我又回来了
要明确迁移学习在干什么,我感觉有两个重点:一是建立在大量数据集上的预训练模型,二是冻结卷积基,训练全连接层
文章目录
-
- [1. 迁移学习概念与思路 🧠](#1. 迁移学习概念与思路 🧠)
- [2. 数据预处理与加载 ⚙️](#2. 数据预处理与加载 ⚙️)
- [3. 预训练模型加载与调整 🛠️](#3. 预训练模型加载与调整 🛠️)
- [4. 模型训练与评估 📈](#4. 模型训练与评估 📈)
- [5. 结果可视化与分析 📊](#5. 结果可视化与分析 📊)
- [6. 总结与思考 💡](#6. 总结与思考 💡)
1. 迁移学习概念与思路 🧠
迁移学习核心思想
- 利用预训练模型:使用在大规模数据集上训练好的模型解决小数据集问题
- 特征提取器:预训练模型作为通用特征提取器,提取的特征在不同问题间具有可移植性
- 三大步骤 :
- 冻结卷积基:保持预训练模型特征提取能力
- 重置分类器:根据新任务定制全连接层
- 微调训练:用自己的数据集训练分类器
torchvision模型库
- 提供常见预训练模型:
VGG
,ResNet
,DenseNet
,Inception
等 - 本章重点使用VGG16架构
2. 数据预处理与加载 ⚙️
数据集准备
python
# 数据集路径与类别
imgs = glob.glob('D:/my_all_learning/dataset2/dataset2/*.jpg')
species = ['cloudy','rain','shine','sunrise']
species_to_idx = dict((c,i) for i,c in enumerate(species))
数据预处理流程
python
transform = transforms.Compose([
transforms.Resize((96,96)), # 统一尺寸
transforms.ToTensor(), # 转为张量
transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) # 标准化
])
自定义数据集类
python
class WT_Dataset(data.Dataset):
def __init__(self, imgs_path, labels):
self.imgs_path = imgs_path
self.labels = labels
def __len__(self):
return len(self.imgs_path)
def __getitem__(self, index):
img = Image.open(self.imgs_path[index]).convert('RGB')
return transform(img), self.labels[index]
数据集划分与加载
python
# 80%训练集,20%测试集
train_count = int(0.8*len(dataset))
test_count = len(dataset) - train_count
train_dataset, test_dataset = data.random_split(dataset, [train_count, test_count])
# 创建DataLoader
BATCH_SIZE = 16
train_dl = data.DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True)
test_dl = data.DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=False)
3. 预训练模型加载与调整 🛠️
加载VGG16预训练模型
python
model = torchvision.models.vgg16(pretrained=True)
print(model) # 查看模型结构
关键调整步骤
python
# 冻结卷积基参数
for param in model.features.parameters():
param.requires_grad = False
# 修改输出层适应四分类问题
model.classifier[-1].out_features = 4
# 设备选择与模型部署
device = 'cuda' if torch.cuda.is_available() else 'cpu'
model = model.to(device)
优化器与损失函数
python
optimizer = optim.Adam(model.parameters(), lr=0.0001)
loss_fn = nn.CrossEntropyLoss()
4. 模型训练与评估 📈
训练函数实现
python
def train(dataloader, model, loss_fn, optimizer):
model.train() # 训练模式
train_loss, correct = 0, 0
for X, y in dataloader:
X, y = X.to(device), y.to(device)
pred = model(X)
loss = loss_fn(pred, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
with torch.no_grad():
correct += (pred.argmax(1) == y).float().sum().item()
train_loss += loss.item()
return train_loss/len(dataloader), correct/len(dataloader.dataset)
测试函数实现
python
def test(dataloader, model):
model.eval() # 评估模式
test_loss, correct = 0, 0
with torch.no_grad():
for X, y in dataloader:
X, y = X.to(device), y.to(device)
pred = model(X)
test_loss += loss_fn(pred, y).item()
correct += (pred.argmax(1) == y).float().sum().item()
return test_loss/len(dataloader), correct/len(dataloader.dataset)
训练循环封装
python
def fit(epochs, model, train_dl, test_dl, loss_fn, optimizer):
train_loss, train_acc = [], []
test_loss, test_acc = [], []
for epoch in range(epochs):
epoch_loss, epoch_acc = train(train_dl, model, loss_fn, optimizer)
epoch_test_loss, epoch_test_acc = test(test_dl, model)
# 记录指标
train_loss.append(epoch_loss)
train_acc.append(epoch_acc)
test_loss.append(epoch_test_loss)
test_acc.append(epoch_test_acc)
# 打印进度
print(f"epoch:{epoch+1:2d}, train_loss:{epoch_loss:.5f}, train_acc:{epoch_acc*100:.1f}%,"
f"test_loss:{epoch_test_loss:.5f}, test_acc:{epoch_test_acc*100:.1f}%")
return train_loss, train_acc, test_loss, test_acc
# 开始训练(30个epoch)
train_loss, train_acc, test_loss, test_acc = fit(30, model, train_dl, test_dl, loss_fn, optimizer)
训练结果摘要
epoch: 1, train_loss:0.038000, train_acc:98.3%, test_loss:0.11699, test_acc:96.0%
epoch: 2, train_loss:0.028070, train_acc:99.1%, test_loss:0.11466, test_acc:97.8%
...
epoch:30, train_loss:0.088539, train_acc:98.6%, test_loss:0.40013, test_acc:96.9%
Done
5. 结果可视化与分析 📊
损失曲线对比
python
plt.plot(range(1,31), train_loss, label='train_loss')
plt.plot(range(1,31), test_loss, label='test_loss')
plt.legend()
plt.show()
关键观察:
- 训练损失快速下降后保持稳定
- 测试损失波动较大,显示一定过拟合迹象
- 后期测试损失上升提示可能需要早停或正则化
准确率曲线对比
python
plt.plot(range(1,31), train_acc, label='train_acc')
plt.plot(range(1,31), test_acc, label='test_acc')
plt.legend()
plt.show()
关键发现:
- 训练准确率快速达到98%以上 🚀
- 测试准确率稳定在95%左右
- 最终测试准确率96.9%表现优异 👍
6. 总结与思考 💡
迁移学习核心价值
- 小数据高效利用:仅用少量样本达到高准确率
- 特征迁移能力:VGG16卷积基有效提取通用图像特征
- 训练效率:仅需微调顶层,大幅减少训练时间和资源
关键成功因素
- 卷积基冻结:保留预训练特征提取能力
- 学习率选择:0.0001的小学习率确保稳定微调
- 数据增强:标准化等预处理提升模型泛化能力
改进方向
- 数据增强扩展:添加旋转、翻转等增强技术
- 正则化应用:Dropout/L2正则减少过拟合
- 学习率调度:动态调整学习率提升收敛效率
- 早停机制:根据验证损失自动停止训练
迁移学习就像站在巨人肩膀上 👣:利用大规模数据集预训练的知识,快速解决特定小样本问题,是计算机视觉领域的强大范式!