毕设开源 糖尿病视网膜病变数据据分析
0 简介
今天我们的导师王同学为大家分享了他正在研究的图像识别技术课题,并介绍了基于深度学习的人脸识别系统的开发进展。
项目运行效果:
毕业设计 糖尿病视网膜预测
项目获取:
https://gitee.com/assistant-a/project-sharing
1 任务目标
本次研究的数据集来源于来自糖尿病视网膜网层的四个不同级别的眼底图像样本(共1000例),为了实现对糖尿病视网膜病变程度进行准确评估的研究目标,在实验阶段我们采用了深度学习框架PyTorch来进行这一目标的实现。


2 数据处理
1.数据分析
通过对数据统计可以得到(已经划分的训练集):

图类间的分布存在一定不均衡性,在图像样本总量相对较少的情况下,则采取了补充完善原始图像数据集的方法。主要采用图像翻转作为增强手段:将单类图像分别进行水平和垂直翻转操作,并使样本数量增加两倍;而对双类与三类图像仅进行垂直方向上的翻转操作,则使样本数量增加一倍;至于零类图像,则保持原有形态不变。

这是处理后的训练集分布。
我是7:3分割的训练集和验证集
2.模型训练
2.1模型准备
使用的模型是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这个模型需要设置的,
#不知道什么意思,但不设置会报错。
2.2参数设置
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()
2.3读取数据并转换为tensor类型
这一段中,我定义了一个名为mydataset的类,并使其继承自父类Dataset。该类的主要功能是以可迭代的数据对象为数据源,并包含图片处理以及相应的transform转换操作。在此处不做进一步解释,默认情况下大家应该已经了解过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函数对数据进行分组并打乱顺序。
2.4开始训练
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}")
2.5测试模型准确率
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与ResNet-50在不同类型下的分类精度存在显著差异。因此我们能够将这两个模型集成以提升分类精度。

可以看出准确率提升的幅度显著。我们还可以从kaggle获取一些数据来补充训练集,并且由于数量有限,在四分类任务中1000张图片显得不足为奇。
3 数据分析
数据集描述
1. 获取路径messidor_features.arff
2. 数据集前面的部分非必要信息对模型训练无影响,在这里我决定去除这些内容以简化数据加载过程,并采用pandas库中的read_csv函数进行文件导入

当然不删也是可以的,有专门的函数读取.arff文件
from scipy.io import arff
import pandas as pd
df = arff.loadarff('messidor_features.arff') #读取出来是一个元组
dataframe = pd.DataFrame(df[0])
3. 来自Messidor图像集的数据集messidor_features.arff包含了提取的各种特征。这些特征被设计用来判断图像中是否存在糖尿病视网膜病变的相关征象。每个特征均反映检测到的症状、解剖部位及其对应的影像学标记。其中包含了20个属性指标,并且这些指标涵盖了从病变症状到影像学标记等多个维度的信息。

在获得对数据集的大致了解后, 进行初步的数据探索分析。首先调用data.info()函数, 检查是否存在缺失值以及各字段的数据类型情况。
import pandas as pd
path='E:/Python_file/zuoye/messidor_features.arff'
Cnames = ['x0', 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x9',
'x10', 'x11', 'x12', 'x13', 'x14', 'x15', 'x16', 'x17', 'x18', 'y']
#删掉与数据集无关的内容
data=pd.read_csv(path,header=None,names=Cnames)
print('数据集基础信息:')
print(data.info())
经统计可知,在提供的数据集中不存在缺失值问题。进一步分析发现,在各类别样本中(标签为‘1’的数据共有611条占总样本量的53.1%,标签为‘0’的数据则有540条占46.9%)。可以看出正负类别的样本数量比例较为平衡
import pandas_profiling
report= pandas_profiling.ProfileReport(data)
report.to_file("output_file.html")
运行后会生成一个互动式的.html文件,并通常会包含以下数据分析功能:首先进行数据类型的检测;其次计算唯一值与缺失值的数量;接着进行分位数统计分析包括最小值、最大值、四分位数以及中位数等指标;最后展示变量间的相关性分析结果。让我们分析该数据集各属性间的单调相关性,并使用Spearman秩相关系数(ρ)作为度量工具绘制热力图。其中ρ的取值范围在-1到+1之间:当ρ为-1时,则表示完全负向的相关性;当ρ为0时,则表示变量间不存在显著的相关关系;而当ρ达到+1时,则表明变量间存在完全正向的相关关系。

三. 方法介绍
逻辑回归的基本概念已经被许多博主详细阐述,我不打算深入探讨其理论基础,重点介绍代码实现过程.通过使用sklearn库中的LogisticRegression类来实现模型训练与预测过程,该方法能够高效地完成分类任务.
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
X=data[data.columns[0:19]] #提取特征,不要标签
y=data['y'] #train_size=0.8,80%的训练集占比
x_train,x_test,y_train,y_test=train_test_split(X,y,train_size=0.8,random_state=90)
lr=LogisticRegression(max_iter=3000)
clm=lr.fit(x_train,y_train)
print('对测试集的预测结果:')
#输出预测结果、预测结果的结构类型及尺寸
result=clm.predict(x_test)
该模型拥有众多参数配置项,在实际应用中并非都需要手动调整。基于此数据集的设计目标是实现均衡类别的分布,在本场景中,默认类别加权策略即可满足需求。推荐使用'solver=liblinear'作为默认选项。针对本问题场景(仅关注有无病变情况),无需依赖复杂的大规模优化算法如'sag'和'saga'。而最大迭代次数这一超参数决定了模型训练过程中的收敛性,请通过将最大迭代次数设定为3000次可有效避免模型过快终止训练而不充分收敛。
四. 结果和模型评价及可视化

2.
模型评估指标繁多,主要涉及召回率、精确率、准确率等基本指标,并计算F统计量、决定系数R²值以及ROC曲线下面积AUC值。借助于classification_report()函数,能够快速生成详细的性能评估报告。
from sklearn.metrics import classification_report
print('性能报告;')
print(classification_report(y_test,result))
confusion = metrics.confusion_matrix(y_test, result)

3. 以FPR为横轴,TPR为纵轴,绘制ROC曲线,并由曲线计算得AUC=0.77。
from sklearn.metrics import roc_curve,auc
from matplotlib import pyplot as plt
fpr, tpr, thr = roc_curve(y_test, result, drop_intermediate=False)
fpr, tpr = [0] + list(fpr), [0] + list(tpr)
plt.plot(fpr, tpr)
plt.title('ROC curve for diabetes classifier')
plt.xlabel('False Positive Rate (1 - Specificity)')
plt.ylabel('True Positive Rate (Sensitivity)')
plt.grid(True)
plt.show()
print('AUC:'+ str(auc(fpr,tpr)))

该模型的检测性能尚可。
print('逻辑回归各变量系数:')
print(clm.coef_)
coef_lr = pd.DataFrame({'var' : x_test.columns,
'coef' : clm.coef_.flatten()
})
index_sort = np.abs(coef_lr['coef']).sort_values().index
coef_lr_sort = coef_lr.loc[index_sort,:]
# 水平柱形图绘图
fig,ax=plt.subplots()
x, y = coef_lr_sort['var'], coef_lr_sort['coef']
rects = plt.barh(x, y, color='dodgerblue')
plt.grid(linestyle="-.", axis='y', alpha=0.4)
plt.tight_layout()
#添加数据标签
for rect in rects:
w = rect.get_width()
ax.text(w, rect.get_y()+rect.get_height()/2,'%.2f' %w,ha='left',va='center')

逻辑回归实际上是将线性回归的结果代入sigmoid函数中进行计算的过程;因此各变量的系数仍然存在。

经观察分析,在糖尿病视网膜病变检测指标中具有显著影响的关键因素包括x_{14}、x_{1}、x_{2}、x_{0}以及x_{15}。这些指标的具体意义参考前述内容。经分析可知,这些指标的变化与糖尿病性视网膜病变的发生存在密切关联。
项目运行效果:
毕业设计 糖尿病视网膜预测
项目获取:
