2022“域见杯”医检人工智能开发者大赛Rank 48代码分享(附过采样和降采样实现)
2023-09-27 14:23:05 时间
比赛
https://competition.huaweicloud.com/information/1000041723/introduction
代码
!pip install torch==1.11.0
!pip install torchvision==0.12.0
!pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu113
Looking in indexes: http://mirrors.aliyun.com/pypi/simple, https://download.pytorch.org/whl/cu113
Requirement already satisfied: torch in ./miniconda3/lib/python3.8/site-packages (1.10.0+cu113)
Requirement already satisfied: torchvision in ./miniconda3/lib/python3.8/site-packages (0.11.1+cu113)
Requirement already satisfied: torchaudio in ./miniconda3/lib/python3.8/site-packages (0.10.0+cu113)
Requirement already satisfied: typing-extensions in ./miniconda3/lib/python3.8/site-packages (from torch) (4.0.0)
Requirement already satisfied: pillow!=8.3.0,>=5.3.0 in ./miniconda3/lib/python3.8/site-packages (from torchvision) (8.4.0)
Requirement already satisfied: numpy in ./miniconda3/lib/python3.8/site-packages (from torchvision) (1.21.4)
[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv[0m
!pip install torchtoolbox -i https://pypi.tuna.tsinghua.edu.cn/simple
import os
import time
import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
from torch.autograd import Variable
import torchvision
from torchvision import datasets, models, transforms
from torch.utils.data.sampler import WeightedRandomSampler
import numpy as np
#import albumentations as A
def seed_torch(seed=2021):
random.seed(seed)
os.environ['PYTHONHASHSEED'] = str(seed) # 为了禁止hash随机化,使得实验可复现
np.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)
torch.cuda.manual_seed_all(seed) # if you are using multi-GPU.
torch.backends.cudnn.benchmark = False
torch.backends.cudnn.deterministic = True
seed_torch()
mean_nums = [0.849, 0.838, 0.851]
std_nums = [0.146, 0.154, 0.114]
from torchtoolbox.transform import Cutout
train_dataTrans = transforms.Compose([
transforms.Resize(640),
transforms.CenterCrop(640),
Cutout(),
#transforms.RandomHorizontalFlip(), # 水平翻转
#transforms.RandomVerticalFlip(), # 垂直翻转
transforms.RandomRotation(degrees=(-5,5)), # 随机选择图片
#transforms.ColorJitter(brightness=0, contrast=0.5, saturation=0, hue=0),
#transforms.RandomErasing(), # 对图像进行随机遮挡
#transforms.RandomAffine(), # 图像放射变换
#transforms.RandomErasing(), # 随机遮挡
#transforms.RandAugment(),
transforms.ToTensor(),
transforms.Normalize(mean_nums, std_nums)
])
val_dataTrans = transforms.Compose([
transforms.Resize(640),
transforms.CenterCrop(640),
transforms.ToTensor(),
transforms.Normalize(mean_nums, std_nums)
])
data_dir = './autodl-nas/'
train_data_dir = './autodl-nas/train'
val_data_dir = './autodl-nas/val'
train_dataset = datasets.ImageFolder(train_data_dir, train_dataTrans)
print(train_dataset.class_to_idx)
val_dataset = datasets.ImageFolder(val_data_dir, val_dataTrans)
image_datasets = {'train':train_dataset,'val':val_dataset}
# 参考 https://blog.csdn.net/TH_NUM/article/details/80877772
# 参考 https://blog.csdn.net/weixin_40970506/article/details/109467365
#按样本权重采样器
def creater_sampler(train_set):
classes_idx = train_set.class_to_idx
appear_times = Variable(torch.zeros(len(classes_idx), 1))
for label in train_set.targets:
appear_times[label] += 1
classes_weight = (1./(appear_times / len(train_set))).view( -1)
weight=list(map(lambda x:classes_weight[x],train_set.targets))
num_sample=int(len(train_set))
print("num_sample:{}\n".format(num_sample))
print("total:{}".format(num_sample)+",targets0:{},targets1:{},targets2:{},targets3:{}".format(appear_times[0],appear_times[1],appear_times[2],appear_times[3]))
sampler = WeightedRandomSampler(weight, num_sample, replacement=False)
return sampler
train_sampler = creater_sampler(train_dataset)
val_sampler = creater_sampler(val_dataset)
dataloders = {x: torch.utils.data.DataLoader(image_datasets[x],
batch_size=8,
sampler=creater_sampler(image_datasets[x]),
num_workers=4) for x in ['train', 'val']}
dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']}
use_gpu = torch.cuda.is_available()
{'ASC-H&HSIL': 0, 'ASC-US&LSIL': 1, 'NILM': 2, 'SCC&AdC': 3}
num_sample:6932
total:6932,targets0:tensor([1488.]),targets1:tensor([3246.]),targets2:tensor([1824.]),targets3:tensor([374.])
num_sample:1688
total:1688,targets0:tensor([401.]),targets1:tensor([732.]),targets2:tensor([494.]),targets3:tensor([61.])
num_sample:6932
total:6932,targets0:tensor([1488.]),targets1:tensor([3246.]),targets2:tensor([1824.]),targets3:tensor([374.])
num_sample:1688
total:1688,targets0:tensor([401.]),targets1:tensor([732.]),targets2:tensor([494.]),targets3:tensor([61.])
# 混合精度
from torch.optim.lr_scheduler import CosineAnnealingLR
scaler = torch.cuda.amp.GradScaler()
def train_model(model, lossfunc, optimizer, scheduler, num_epochs=10):
start_time = time.time()
best_model_wts = model.state_dict()
best_acc = 0.0
for epoch in range(num_epochs):
print('Epoch {}/{}'.format(epoch, num_epochs - 1))
print('-' * 10)
# Each epoch has a training and validation phase
for phase in ['train', 'val']:
if phase == 'train':
scheduler.step()
model.train(True) # Set model to training mode
else:
model.train(False) # Set model to evaluate mode
running_loss = 0.0
running_corrects = 0.0
# Iterate over data.
for data in dataloders[phase]:
# get the inputs
inputs, labels = data
# wrap them in Variable
if use_gpu:
inputs = Variable(inputs.cuda())
labels = Variable(labels.cuda())
else:
inputs, labels = Variable(inputs), Variable(labels)
# zero the parameter gradients
optimizer.zero_grad()
# forward
outputs = model(inputs)
_, preds = torch.max(outputs.data, 1)
loss = lossfunc(outputs, labels)
# backward + optimize only if in training phase
if phase == 'train':
#loss.backward()
scaler.scale(loss).backward()
#optimizer.step()
scaler.step(optimizer)
scaler.update()
# statistics
running_loss += loss.data
running_corrects += torch.sum(preds == labels.data).to(torch.float32)
epoch_loss = running_loss / dataset_sizes[phase]
epoch_acc = running_corrects / dataset_sizes[phase]
print('{} Loss: {:.4f} Acc: {:.4f}'.format(
phase, epoch_loss, epoch_acc))
# deep copy the model
if phase == 'val' and epoch_acc > best_acc:
best_acc = epoch_acc
best_model_wts = model.state_dict()
elapsed_time = time.time() - start_time
print('Training complete in {:.0f}m {:.0f}s'.format(
elapsed_time // 60, elapsed_time % 60))
print('Best val Acc: {:4f}'.format(best_acc))
# load best model weights
model.load_state_dict(best_model_wts)
return model
class LabelSmoothingCrossEntropy(nn.Module):
"""
Cross Entropy loss with label smoothing.
"""
def __init__(self, smoothing=0.1):
"""
Constructor for the LabelSmoothing module.
:param smoothing: label smoothing factor
"""
super(LabelSmoothingCrossEntropy, self).__init__()
assert 0.0 < smoothing < 1.0
self.smoothing = smoothing
self.confidence = 1. - smoothing
def forward(self, x, target):
"""
写法1
"""
# logprobs = F.log_softmax(x, dim=-1)
# nll_loss = -logprobs.gather(dim=-1, index=target.unsqueeze(1))
# nll_loss = nll_loss.squeeze(1) # 得到交叉熵损失
# # 注意这里要结合公式来理解,同时留意预测正确的那个类,也有a/K,其中a为平滑因子,K为类别数
# smooth_loss = -logprobs.mean(dim=1)
# loss = self.confidence * nll_loss + self.smoothing * smooth_loss
"""
写法2
"""
y_hat = torch.softmax(x, dim=1)
# 这里cross_loss和nll_loss等价
cross_loss = self.cross_entropy(y_hat, target)
smooth_loss = -torch.log(y_hat).mean(dim=1)
# smooth_loss也可以用下面的方法计算,注意loga + logb = log(ab)
# smooth_loss = -torch.log(torch.prod(y_hat, dim=1)) / y_hat.shape[1]
loss = self.confidence * cross_loss + self.smoothing * smooth_loss
return loss.mean()
def cross_entropy(self, y_hat, y):
return - torch.log(y_hat[range(len(y_hat)), y])
# 针对多分类任务的 CELoss 和 Focal Loss
# https://blog.csdn.net/m0_37531129/article/details/107831624
import torch
import torch.nn as nn
import torch.nn.functional as F
class CELoss(nn.Module):
def __init__(self, class_num, alpha=None, use_alpha=False, size_average=True):
super(CELoss, self).__init__()
self.class_num = class_num
self.alpha = alpha
if use_alpha:
self.alpha = torch.tensor(alpha).cuda()
self.softmax = nn.Softmax(dim=1)
self.use_alpha = use_alpha
self.size_average = size_average
def forward(self, pred, target):
prob = self.softmax(pred.view(-1,self.class_num))
prob = prob.clamp(min=0.0001,max=1.0)
target_ = torch.zeros(target.size(0),self.class_num).cuda()
target_.scatter_(1, target.view(-1, 1).long(), 1.)
if self.use_alpha:
batch_loss = - self.alpha.double() * prob.log().double() * target_.double()
else:
batch_loss = - prob.log().double() * target_.double()
batch_loss = batch_loss.sum(dim=1)
# print(prob[0],target[0],target_[0],batch_loss[0])
# print('--')
if self.size_average:
loss = batch_loss.mean()
else:
loss = batch_loss.sum()
return loss
class FocalLoss(nn.Module):
def __init__(self, class_num, alpha=None, gamma=2, use_alpha=False, size_average=True):
super(FocalLoss, self).__init__()
self.class_num = class_num
self.alpha = alpha
self.gamma = gamma
if use_alpha:
self.alpha = torch.tensor(alpha).cuda()
self.softmax = nn.Softmax(dim=1)
self.use_alpha = use_alpha
self.size_average = size_average
def forward(self, pred, target):
prob = self.softmax(pred.view(-1,self.class_num))
prob = prob.clamp(min=0.0001,max=1.0)
target_ = torch.zeros(target.size(0),self.class_num).cuda()
target_.scatter_(1, target.view(-1, 1).long(), 1.)
if self.use_alpha:
batch_loss = - self.alpha.double() * torch.pow(1-prob,self.gamma).double() * prob.log().double() * target_.double()
else:
batch_loss = - torch.pow(1-prob,self.gamma).double() * prob.log().double() * target_.double()
batch_loss = batch_loss.sum(dim=1)
if self.size_average:
loss = batch_loss.mean()
else:
loss = batch_loss.sum()
return loss
# get model and replace the original fc layer with your fc layer
model_ft = models.resnet50(pretrained=True, progress=False)
#model_ft = models.resnet101(pretrained=True, progress=False)
#print(model_ft)
num_ftrs = model_ft.fc.in_features
model_ft.fc = nn.Linear(num_ftrs, len(train_dataset.classes))
#model_ft = models.efficientnet_b1(pretrained=True, progress=False)
#print(model_ft)
#num_ftrs = model_ft.classifier[1].in_features
#model_ft.classifier[1] = nn.Linear(num_ftrs, len(train_dataset.classes))
if use_gpu:
model_ft = model_ft.cuda()
# define loss function
#lossfunc = nn.CrossEntropyLoss()
lossfunc = LabelSmoothingCrossEntropy(smoothing=0.1)
#lossfunc = nn.BCEWithLogitsLoss()
#lossfunc = FocalLoss(class_num=len(train_dataset.classes))
# setting optimizer and trainable parameters
params = model_ft.parameters()
# list(model_ft.fc.parameters())+list(model_ft.layer4.parameters())
#params = list(model_ft.fc.parameters())+list( model_ft.parameters())
#params = list(model_ft.fc.parameters())
#params = list(model_ft.heads.head.parameters())
from torchtoolbox.optimizer import Lookahead
#optimizer_ft = optim.SGD(params, lr=0.001, momentum=0.9)
#optimizer_ft = optim.SGD(filter(lambda p: p.requires_grad, params), lr=1e-3, momentum=0.9)
optimizer_ft = torch.optim.Adam(filter(lambda p: p.requires_grad, params), lr=1e-4)
#optimizer_ft = torch.optim.AdamW(params, lr=1e-3)
optimizer_ft = Lookahead(optimizer_ft)
# Decay LR by a factor of 0.1 every 7 epochs
#exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=2, gamma=0.1)
exp_lr_scheduler = lr_scheduler.CosineAnnealingLR(optimizer_ft, T_max=2)
# 传入优化器让学习率受其管理,当连续3次没有减少loss时就会减少学习率(乘以0.7)
#exp_lr_scheduler = lr_scheduler.ReduceLROnPlateau(optimizer_ft, mode="min", patience=3, factor=0.7)
#exp_lr_scheduler = lr_scheduler.ReduceLROnPlateau(optimizer_ft,'min',factor=0.5,patience=5,verbose=True,min_lr=1e-6)
model_ft = train_model(model=model_ft,
lossfunc=lossfunc,
optimizer=optimizer_ft,
scheduler=exp_lr_scheduler,
num_epochs=25)
Epoch 0/24
----------
train Loss: 0.1080 Acc: 0.7027
val Loss: 0.1288 Acc: 0.6463
Epoch 1/24
----------
train Loss: 0.1571 Acc: 0.5593
val Loss: 0.1324 Acc: 0.6499
Epoch 2/24
----------
train Loss: 0.0953 Acc: 0.7778
val Loss: 0.1142 Acc: 0.7085
Epoch 3/24
----------
train Loss: 0.0943 Acc: 0.7856
val Loss: 0.1132 Acc: 0.7334
Epoch 4/24
----------
train Loss: 0.0829 Acc: 0.8459
val Loss: 0.1064 Acc: 0.7441
Epoch 5/24
----------
train Loss: 0.1351 Acc: 0.6167
val Loss: 0.1045 Acc: 0.7470
Epoch 6/24
----------
train Loss: 0.0787 Acc: 0.8609
val Loss: 0.1073 Acc: 0.7613
Epoch 7/24
----------
train Loss: 0.0825 Acc: 0.8410
val Loss: 0.1222 Acc: 0.6979
Epoch 8/24
----------
train Loss: 0.0752 Acc: 0.8821
val Loss: 0.1008 Acc: 0.7684
Epoch 9/24
----------
train Loss: 0.1068 Acc: 0.7128
val Loss: 0.0999 Acc: 0.7707
Epoch 10/24
----------
train Loss: 0.0691 Acc: 0.9110
val Loss: 0.1041 Acc: 0.7559
Epoch 11/24
----------
train Loss: 0.0759 Acc: 0.8772
val Loss: 0.1138 Acc: 0.7233
Epoch 12/24
----------
train Loss: 0.0667 Acc: 0.9257
val Loss: 0.0971 Acc: 0.7743
Epoch 13/24
----------
train Loss: 0.0981 Acc: 0.7346
val Loss: 0.0977 Acc: 0.7743
Epoch 14/24
----------
train Loss: 0.0627 Acc: 0.9433
val Loss: 0.1031 Acc: 0.7761
Epoch 15/24
----------
train Loss: 0.0685 Acc: 0.9130
val Loss: 0.1245 Acc: 0.7222
Epoch 16/24
----------
train Loss: 0.0618 Acc: 0.9475
val Loss: 0.1024 Acc: 0.7642
Epoch 17/24
----------
train Loss: 0.0926 Acc: 0.7643
val Loss: 0.0969 Acc: 0.7826
Epoch 18/24
----------
train Loss: 0.0580 Acc: 0.9634
val Loss: 0.1051 Acc: 0.7636
Epoch 19/24
----------
train Loss: 0.0644 Acc: 0.9377
val Loss: 0.1080 Acc: 0.7642
Epoch 20/24
----------
train Loss: 0.0581 Acc: 0.9618
val Loss: 0.1052 Acc: 0.7719
Epoch 21/24
----------
train Loss: 0.0850 Acc: 0.7969
val Loss: 0.1028 Acc: 0.7784
Epoch 22/24
----------
train Loss: 0.0546 Acc: 0.9794
val Loss: 0.1016 Acc: 0.7838
Epoch 23/24
----------
train Loss: 0.0603 Acc: 0.9514
val Loss: 0.1383 Acc: 0.7109
Epoch 24/24
----------
train Loss: 0.0557 Acc: 0.9717
val Loss: 0.0979 Acc: 0.7956
Training complete in 81m 39s
Best val Acc: 0.795616
torch.save(model_ft.state_dict(), './model.pth', _use_new_zipfile_serialization=False)
from math import exp
import numpy as np
from PIL import Image
import cv2
infer_transformation = transforms.Compose([
transforms.Resize(640),
transforms.CenterCrop(640),
transforms.ToTensor(),
transforms.Normalize(mean_nums, std_nums)
])
IMAGES_KEY = 'images'
MODEL_INPUT_KEY = 'images'
LABEL_OUTPUT_KEY = 'predicted_label'
MODEL_OUTPUT_KEY = 'scores'
LABELS_FILE_NAME = 'labels.txt'
def decode_image(file_content):
image = Image.open(file_content)
image = image.convert('RGB')
return image
def read_label_list(path):
with open(path, 'r',encoding="utf8") as f:
label_list = f.read().split(os.linesep)
label_list = [x.strip() for x in label_list if x.strip()]
return label_list
def resnet50(model_path):
"""Constructs a ResNet-50 model.
Args:
pretrained (bool): If True, returns a model pre-trained on ImageNet
"""
model = models.resnet50(pretrained=False)
num_ftrs = model.fc.in_features
model.fc = nn.Linear(num_ftrs, 4)
model.load_state_dict(torch.load(model_path,map_location ='cpu'))
# model.load_state_dict(torch.load(model_path))
model.eval()
return model
def predict(file_name):
LABEL_LIST = read_label_list('./labels.txt')
model = resnet50('./model.pth')
image1 = decode_image(file_name)
input_img = infer_transformation(image1)
input_img = torch.autograd.Variable(torch.unsqueeze(input_img, dim=0).float(), requires_grad=False)
logits_list = model(input_img)[0].detach().numpy().tolist()
print(logits_list)
maxlist=max(logits_list)
print(maxlist)
z_exp = [exp(i-maxlist) for i in logits_list]
sum_z_exp = sum(z_exp)
softmax = [round(i / sum_z_exp, 3) for i in z_exp]
print(softmax)
labels_to_logits = {
LABEL_LIST[i]: s for i, s in enumerate(softmax)
}
predict_result = {
LABEL_OUTPUT_KEY: max(labels_to_logits, key=labels_to_logits.get),
MODEL_OUTPUT_KEY: labels_to_logits
}
return predict_result
file_name = './autodl-nas/val/ASC-US&LSIL/05147.jpg'
result = predict(file_name) #可以替换其他图片
import matplotlib.pyplot as plt
plt.figure(figsize=(10,10)) #设置窗口大小
img = decode_image(file_name)
plt.imshow(img)
plt.show()
print(result)
[2.7242531776428223, -0.8248791694641113, 1.2405871152877808, -0.18008685111999512]
2.7242531776428223
[0.763, 0.022, 0.173, 0.042]
!pip install opencv-python
!pip install albumentations