pythonproject/qrtrain.py
2024-08-07 09:46:47 +08:00

222 lines
8.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import numpy as np
import torch
import torch.nn as nn
from torch.optim.lr_scheduler import StepLR
from torchvision import models, transforms
from torch.utils.data import DataLoader
from torchvision.datasets import ImageFolder
from torch.optim import Adam
from tqdm import tqdm
from torchvision.transforms import functional
from FocalLoss import FocalLoss
from torchvision.models import ResNet18_Weights
from torch.cuda.amp import GradScaler, autocast
# 加载预训练的ResNet-18模型
model = models.resnet18(weights=ResNet18_Weights.IMAGENET1K_V1)
# 将模型移到GPU上如果有
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model = model.to(device)
# 替换最后一层以适应二分类任务
num_features = model.fc.in_features
model.fc = nn.Linear(num_features, 2) # 假设是二分类问题
model.fc = model.fc.to(device)
# 冻结所有层
for param in model.parameters():
param.requires_grad = False
# 解冻最后一层,用于微调
# for param in model.fc.parameters():
# param.requires_grad = True
# 解冻最后几层
for name, param in model.named_parameters():
if "layer4" in name or "fc" in name: # 只解冻最后几层
param.requires_grad = True
def random_brightness(image):
"""
随机调整图像的亮度。
该函数通过生成一个随机的亮度因子,然后应用到图像上,以增加或减少图像的亮度。这种方法常用于数据增强,
旨在帮助模型更好地泛化,因为它教模型在不同亮度条件下处理图像。
参数:
image (Tensor): 输入的图像Tensor。
返回:
Tensor: 亮度经过随机调整后的图像Tensor。
"""
# 生成一个随机的亮度因子
brightness_factor = torch.rand(1).item()
# 调整图像的亮度
return functional.adjust_brightness(image, brightness_factor)
def random_contrast(image):
"""
随机调整图像的对比度。
该函数通过生成一个随机的对比度因子,然后应用到图像上,以增加或减少图像的对比度。
对比度因子是在0.5到1.5之间随机生成的其中0.5是最低对比度1.5是最高对比度。
参数:
image (Tensor): 输入的图像Tensor。
返回:
Tensor: 对比度经过随机调整后的图像Tensor。
"""
# 生成一个随机的对比度因子其值在0.5到1.5之间。
contrast_factor = torch.rand(1).item() + 0.5
# 使用生成的对比度因子调整图像的对比度。
return functional.adjust_contrast(image, contrast_factor)
def random_hue(image):
"""
随机调整图像的色相。
色相的变化是由一个随机生成的因子决定的,这个因子是在-0.05到0.05之间均匀分布的。
这个函数的目的是为图像数据增强,使得模型在训练过程中看到更多变体,从而提高泛化能力。
参数:
image (Tensor): 输入的图像Tensor。
返回:
Tensor: 色相被随机调整后的图像Tensor。
"""
# 生成一个随机的色相调整因子,其值在-0.05到0.05之间。
hue_factor = (torch.rand(1).item() - 0.5) / 10
# 使用调整的色相因子来改变图像的色相。
return functional.adjust_hue(image, hue_factor)
def add_gaussian_noise(image):
"""
向图像数据添加高斯噪声。
该函数的目的是为了模拟现实世界中图像经常受到的各种噪声干扰。通过在图像中添加高斯噪声,
可以使模型在训练过程中更加鲁棒,提高其在真实场景下的泛化能力。
参数:
image: 输入的图像数据作为一个torch张量。
返回值:
返回添加了高斯噪声的图像数据,与输入图像数据类型和形状相同。
添加的噪声是通过torch.randn_like函数生成的确保了噪声的分布与图像数据相同
并且乘以了一个小的常数0.1来控制噪声的强度。
"""
# 生成与图像数据相同形状的高斯噪声张量,并将其与图像数据相加
return image + torch.randn_like(image) * 0.1
data_transforms = {
'train': transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.RandomRotation(degrees=15), # 随机旋转图像最多15度
transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1), # 随机改变图像的颜色
transforms.ToTensor(),
# transforms.Lambda(lambda x: functional.adjust_brightness(x, brightness_factor=torch.rand(1).item())), # 随机调整亮度
# transforms.Lambda(lambda x: functional.adjust_contrast(x, contrast_factor=torch.rand(1).item() + 0.5)), # 随机调整对比度
# transforms.Lambda(lambda x: functional.adjust_hue(x, hue_factor=(torch.rand(1).item()-0.5)/10)), # 随机调整色相
# transforms.Lambda(lambda x: x + torch.randn_like(x) * 0.1), # 添加随机高斯噪声
transforms.Lambda(random_brightness),
transforms.Lambda(random_contrast),
transforms.Lambda(random_hue),
transforms.Lambda(add_gaussian_noise),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
]),
'val': transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
]),
}
# 加载数据集
data_dir = '/Users/jasonwong/Downloads/二维码测试1/data'
image_datasets = {x: ImageFolder(os.path.join(data_dir, x), data_transforms[x])
for x in ['train', 'val']}
dataloaders = {x: DataLoader(image_datasets[x], batch_size=32, shuffle=True, num_workers=4)
for x in ['train', 'val']}
dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']}
#设置损失函数和优化器】
num_normal_samples = 7891
num_anomaly_samples = 846
class_sample_counts = [num_normal_samples, num_anomaly_samples] # 替换为实际样本数量
weights = 1.0 / np.array(class_sample_counts)
weights /= weights.sum() # 确保权重和为1
class_weights = torch.FloatTensor(weights).to(device) # 将权重转换为PyTorch张量并移动到设备上
# criterion = nn.CrossEntropyLoss()
# optimizer = torch.optim.SGD(model.fc.parameters(), lr=0.001, momentum=0.9)
optimizer = Adam(model.parameters(),
lr=1e-4, # 学习率
betas=(0.9, 0.999), # beta_1 和 beta_2
eps=1e-08) # epsilon
scheduler = StepLR(optimizer, step_size=7, gamma=0.1)
# 初始化GradScaler
# scaler = GradScaler()
#训练模型
def train_model(model, dataloaders, dataset_sizes, device, num_epochs=10, save_path='path_to_save/full_model'):
# criterion = FocalLoss(gamma=2, alpha=[0.25, 0.75], reduction='mean')
# 加载加权交叉熵损失函数
criterion = nn.CrossEntropyLoss(weight=class_weights)
for epoch in range(num_epochs):
print(f'Starting epoch {epoch + 1}/{num_epochs}')
for phase in ['train', 'val']:
if phase == 'train':
model.train()
else:
model.eval()
running_loss = 0.0
running_corrects = 0
for inputs, labels in tqdm(dataloaders[phase], desc=f'Epoch {epoch + 1} {phase.capitalize()}'):
inputs = inputs.to(device)
labels = labels.to(device)
optimizer.zero_grad()
with torch.set_grad_enabled(phase == 'train'):
outputs = model(inputs)
_, preds = torch.max(outputs, 1)
loss = criterion(outputs, labels)
if phase == 'train':
loss.backward()
optimizer.step()
running_loss += loss.item() * inputs.size(0)
running_corrects += torch.sum(preds == labels.data)
epoch_loss = running_loss / dataset_sizes[phase]
epoch_acc = running_corrects.double() / dataset_sizes[phase]
print(f'{phase} Loss: {epoch_loss:.4f} Acc: {epoch_acc:.4f}')
scheduler.step()
torch.save({
'epoch': epoch + 1,
'model_state_dict': model.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
'loss': epoch_loss,
}, f'{save_path}_epoch_{epoch + 1}.pth')
if __name__ == '__main__':
train_model(model, dataloaders, dataset_sizes, device, num_epochs=10, save_path='')