我们今年是一个实践就是 糖尿病视网膜病变检测
这个题目是kaggle的一个竞赛原题,Diabetic Retinopathy Detection。
只不过kaggle上是五分类,而我们是四分类。
这次任务的数据集是1000的糖网的4个等级的眼底图像,我们需要利用深度学习框架pytorch 来根据眼底图像预测其分类。
通过对数据统计可以得到(已经划分的训练集):
图片种类的分布是有一点不均匀的,同时图片数量也有一点少,所以我们先简单的对图片数据进行一下扩充,这里我们使用最简单的图片反转作为数据增强的方式。我们对一类的图片进行的左右翻转和上下翻转,扩充为原来的三倍。对二类和三类的图片我们做了上下翻转,扩充到原来的二倍。对零类图片不做任何处理。
这是处理后的训练集分布。
我是7:3分割的训练集和验证集
使用的模型是torchvision.model里的经典模型和预训练好的参数。
from torchvision import models as models
# inception_v3,ResNet50
model = models.resnet50(pretrained=True)
#将pretrained置为true,意思是使用已经预训练好的参数。
model.fc#打印模型全连接层的输入和输出参数
#Linear(in_features=2048, out_features=1000, bias=True)
因为我们是四分类所以调整模型输出为:
model.fc = torch.nn.Linear(in_features=2048, out_features=4, bias=True)
model.aux_logits = False #这个设置是InceptionV3这个模型需要设置的,
#不知道什么意思,但不设置会报错。
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = model.to(device=device)
learning_rate = 1e-4
num_epochs = 10
batch_size = 32
optimizer = torch.optim.Adam(model.parameters(), lr = learning_rate)
loss_criterion = torch.nn.CrossEntropyLoss()
这一部分我是定义了一个类mydataset继承父类Dataset来获取可迭代的数据对象,同时对图片的处理和transform转换也在这里面实现。这里就不多说,对dataset不懂得可以看我之前写的dataset类。直接贴代码。
my_transform = transforms.Compose([
transforms.Resize((299,299)),
transforms.ToTensor(),
# transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
#这里进行transform是因为inception_v3模型的输入是(299*299)
#resnet就不需要是(299*299)了
class retinaDataset(Dataset):
def __init__(self, imagepath=r"D:\course\junior_2\deep_learning\third\train", csv_path=" ",transform=my_transform):
self.df = pd.read_csv(csv_path)
# if (total is not None):
# self.df = self.df[:total]
self.transform = transform
self.imagepath = imagepath
def __len__(self):
return len(self.df)
def __getitem__(self, index):
img_path = os.path.join(self.imagepath, self.df.iloc[index].image +".png")
img = Image.open(img_path)
if(self.transform):
img = self.transform(img)
return img, torch.tensor(self.df.iloc[index].Retinopathy_grade)
train_dataset = retinaDataset(csv_path=r"D:\course\junior_2\deep_learning\mythird\train.csv")
train_dataloader = DataLoader(dataset=train_dataset,
batch_size=batch_size, shuffle=True)
#这里调用Dataloder函数对数据进行分组并打乱顺序。
for epoch in range(num_epochs):
for data, target in tqdm(train_dataloader):
data = data.to(device=device)
target = target.to(device=device)
score = model(data)
optimizer.zero_grad()
loss = loss_criterion(score, target)
loss.backward()
optimizer.step()
print(f"for epoch {epoch}, loss : {loss}")
def f_check_accuracy(model_i,model_r, loader):
model_i.eval() #模型inception_v3
model_r.eval() #模型resnet50
num0=0
num1=0
num2=0
num3=0
total0=0
total1=0
total2=0
total3=0
correct_output = 0
total_output = 0
with torch.no_grad(): #反向传播时不再自动求导,节省显存。
for x, y in tqdm(loader):
x = x.to(device=device)
y = y.to(device=device)
score_i = model_i(x)
score_r = model_r(x)
_,predictions_i = score_i.max(1)
_,predictions_r = score_r.max(1)
for i in range (len(y)):
if(y[i]==0):
total0=total0+1
if(predictions_i[i]==0):
num0=num0+1
elif(y[i]==1):
total1=total1+1
if(predictions_r[i]==1):
num1=num1+1
elif(y[i]==2):
total2=total2+1
if(predictions_r[i]==2):
num2=num2+1
elif(y[i]==3):
total3=total3+1
if(predictions_i[i]==3):
num3=num3+1
correct_output =num0+num1+num2+num3
total_output =total0+total1+total2+total3
# model.train()
print("0类准确率",num0/total0,"correct:",num0,"total:",total0)
print("1类准确率",num1/total1,"correct:",num1,"total:",total1)
print("2类准确率",num2/total2,"correct:",num2,"total:",total2)
print("3类准确率",num3/total3,"correct:",num3,"total:",total3)
print(f"out of {total_output} , total correct: {correct_output} with an accuracy of {float(correct_output/total_output)*100}")
解释一下我为什么要把inception_v3和resnet50结合到一起。
我们可以看到v3和resnet50再不同种类的准确率不同。所以可以把这两个模型结合在一起来提高准确率。
可以看到准确率提升的效果很好,大约20个点左右。
我们还可以从kaggle中下载一些数据来补充训练集,因为1000图片在划分之后对于四分类的任务来说是不够的。
这行代码的作用。
model.aux_logits = False