说明
- 本项目是关于图像的一个三分类问题。
- 是基于百度飞浆的paddlepaddle框架完成的。
- 数据集是包含‘新冠肺炎患者’,‘正常人’,‘病毒性肺炎患者’ 三类图片。
- 参考:链接
导入包
#导入需要的包
import os
import math
import zipfile
import random
import json
import cv2
import numpy as np
from PIL import Image
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph import Linear,Conv2D,Pool2D
import matplotlib.pyplot as plt
paddle.enable_static() #转换为静态图
参数设置
#一些参数的设置
configs = {
"input_size": [3, 1024,1024], #输入图片的shape
"class_dim":3, #分类数
'src_path':'data/data82373/input_data.rar', #数据的路径
'train_path':'input_data', #解压路径
'model_save_dir':'save_model', # 模型保存路径
'learning_rate':0.001, #学习率
'batch_size':32, #批次大小
'epoch':10 #学习次数
}
数据预处理
作用:将三个个文件中的图片提取出来,并打上对应的标签,按照比例返回训练集和测试集。
COVID =[] #新冠肺炎患者的胸透图片
COVID_label = []
NORMAL = [] #正常人的胸透图片
NORMAL_label = []
Viral_Pneumonia = [] #病毒性肺炎患者的胸透图片
Viral_Pneumonia_label = []
# 获取所以图片的路径名
# 对应的列表中,同时贴上标签,存放到label列表中
def get_files(file_path, ratio):
for file in os.listdir(file_path + '/COVID'):
COVID.append(file_path + '/COVID' + '/' + file)
COVID_label.append(0) # 0为新冠肺炎患者
for file in os.listdir(file_path + '/NORMAL'):
NORMAL.append(file_path + '/NORMAL' + '/' + file)
NORMAL_label.append(1) # 1为正常人
for file in os.listdir(file_path + '/Viral_Pneumonia'):
Viral_Pneumonia.append(file_path + '/Viral_Pneumonia' + '/' + file)
Viral_Pneumonia_label.append(2) # 2为病毒性肺炎患者
#检测是否读取成功
for i in range(1):
# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
img = plt.imread(NORMAL[i+5])
plt.title('图片的类型是:'+str(NORMAL_label[i+5]))
plt.imshow(img)
plt.show()
#将新路径的图片进行打乱处理
image_list = np.hstack((COVID, NORMAL, Viral_Pneumonia))
label_list = np.hstack((COVID_label, NORMAL_label, Viral_Pneumonia_label))
# 利用shuffle打乱顺序
temp = np.array([image_list, label_list])
temp = temp.transpose()
np.random.shuffle(temp)
# 将所有的img和lab转换成list
all_image_list = list(temp[:, 0])
all_label_list = list(temp[:, 1])
# 将所得List分为两部分,一部分用来训练tra,一部分用来测试val
# ratio是测试集的比例,看情况填入0-1的一个小数
n_sample = len(all_label_list)
n_val = int(math.ceil(n_sample * ratio)) # 测试样本数
n_train = n_sample - n_val # 训练样本数
tra_images = all_image_list[0:n_train]
tra_labels = all_label_list[0:n_train]
tra_labels = [int(float(i)) for i in tra_labels]
val_images = all_image_list[n_train:-1]
val_labels = all_label_list[n_train:-1]
val_labels = [int(float(i)) for i in val_labels]
return tra_images, tra_labels, val_images, val_labels
读取函数
作用:将上一步提取出来的文件名,使得图片读取成数组数据。
def data_reader(images, labels):
def reader():
for item in range(len(images)):
img_path = images[item]
lab = labels[item]
img = cv2.imread(img_path) # imread()函数将图片转换为数组
img = np.resize(img,(3,256,256)) # 转换格式
img = np.array(img).reshape(3,256,256).astype('float32')
img = img/255.0
yield img, int(lab)
return reader
定义网络结构
def CNN_model(tra_images):
# 第一个卷积-池化层
conv_pool_1 = fluid.nets.simple_img_conv_pool(
input=tra_images, # 输入图像
filter_size=5, # 滤波器的大小
num_filters=20, # filter 的数量。它与输出的通道相同
pool_size=2, # 池化核大小2*2
pool_stride=2, # 池化步长
act="relu") # 激活类型
conv_pool_1 = fluid.layers.batch_norm(conv_pool_1)
# 第二个卷积-池化层
conv_pool_2 = fluid.nets.simple_img_conv_pool(
input=conv_pool_1,
filter_size=5,
num_filters=50,
pool_size=2,
pool_stride=2,
act="relu")
conv_pool_2 = fluid.layers.batch_norm(conv_pool_2)
# 第三个卷积-池化层
conv_pool_3 = fluid.nets.simple_img_conv_pool(
input=conv_pool_2,
filter_size=5,
num_filters=50,
pool_size=2,
pool_stride=2,
act="relu")
# 以softmax为激活函数的全连接输出层,因为是分成三类,所以size是三
prediction = fluid.layers.fc(input=conv_pool_3, size=3, act='softmax')
return prediction
定义损失函数和准确率
data_shape = [3,256,256]
tra_images = fluid.layers.data(name='image', shape=data_shape, dtype='float32')
label = fluid.layers.data(name='label', shape=[1], dtype='int64')
# 获取分类器,用cnn进行分类
#model = paddle.vision.models.resnet50(pretrained=True,num_classes=3)
#predict = model(tra_images)
predict = CNN_model(tra_images)
print(np.shape(predict))
# 获取损失函数和准确率
cost = fluid.layers.cross_entropy(input=predict, label=label) # 交叉熵
avg_cost = fluid.layers.mean(cost) # 计算cost中所有元素的平均值
acc = fluid.layers.accuracy(input=predict, label=label) #使用输入和标签计算准确率
# 定义优化方法
optimizer =fluid.optimizer.Adam(learning_rate=configs['learning_rate'])
optimizer.minimize(avg_cost)
print("完成")
执行器与数据读取
创建执行器
# 定义使用CPU还是GPU,使用CPU时use_cuda = False,使用GPU时use_cuda = True
use_cuda = True
place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace()
# 创建执行器,初始化参数
exe = fluid.Executor(place)
exe.run(fluid.default_startup_program())
读取数据
feeder = fluid.DataFeeder(feed_list=[tra_images, label],place=place)
all_train_iter=0
all_train_iters=[]
all_train_costs=[]
all_train_accs=[]
#定义数据读取
data_shape = configs['input_size']
train_image, train_label, val_image, val_label =get_files(configs['train_path'], 0.8)
print(type(train_image[0]))
train_reader = paddle.batch(data_reader(train_image,train_label),
batch_size=configs['batch_size'],
drop_last=True)
eval_reader = paddle.batch(data_reader(val_image,val_label),
batch_size=configs['batch_size'],
drop_last=True)
test_reader = paddle.batch(data_reader(val_image,val_label),
batch_size=1,
drop_last=True)
模型的训练与预测
训练
test_program = fluid.default_main_program().clone(for_test=True)
for pass_id in range(configs['epoch']):
# 开始训练
train_costs = []
train_accs = []
for batch_id, data in enumerate(train_reader()): #遍历train_reader的迭代器,并为数据加上索引batch_id
train_cost,train_acc = exe.run(program=fluid.default_main_program(),#运行主程序
feed=feeder.feed(data), #喂入一个batch的数据
fetch_list=[avg_cost, acc]) #fetch均方误差和准确率
all_train_iter=all_train_iter+configs['batch_size']
all_train_iters.append(all_train_iter)
train_costs.append(train_cost[0])
train_accs.append(train_acc[0])
train_cost = np.mean(train_costs)
train_acc = np.mean(train_accs)
#每10次batch打印一次训练、进行一次测试
if batch_id % 10 == 0 and batch_id!= 0 :
print('Pass:%d, Batch:%d, Cost:%0.5f, Accuracy:%0.5f' %
(pass_id, batch_id, train_cost, train_acc))
# 开始测试
test_costs = [] #测试的损失值
test_accs = [] #测试的准确率
for batch_id, data in enumerate(eval_reader()):
test_cost, test_acc = exe.run(program=test_program, #执行测试程序
feed=feeder.feed(data), #喂入数据
fetch_list=[avg_cost, acc]) #fetch 误差、准确率
test_costs.append(test_cost[0]) #记录每个batch的误差
test_accs.append(test_acc[0]) #记录每个batch的准确率
# 求测试结果的平均值
test_cost = (sum(test_costs) / len(test_costs)) #计算误差平均值(误差和/误差的个数)
test_acc = (sum(test_accs) / len(test_accs)) #计算准确率平均值( 准确率的和/准确率的个数)
print('Test:%d, Cost:%0.5f, ACC:%0.5f' % (pass_id, test_cost, test_acc))
#保存模型
# 如果保存路径不存在就创建
if not os.path.exists(configs['model_save_dir']):
os.makedirs(configs['model_save_dir'])
print ('save models to %s' % (configs['model_save_dir']))
fluid.io.save_inference_model(configs['model_save_dir'],
['image'],
[predict],
exe)
print('训练模型保存完成!')
预测
label_list = ['新冠肺炎患者','正常人','病毒性肺炎患者']
for j in range(len(val_image)):
for batch_id, data in enumerate(eval_reader()):
results = exe.run(program=test_program, #运行预测程序
feed=feeder.feed(data), #喂入要预测的img
fetch_list=predict) #得到推测结果
#print(results)
results_list = results[0].tolist()
#print(results_list)
for i in range(len(results_list)):
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
img = plt.imread(val_image[i])
plt.title('图片的类型是:'+str(val_label[i]))
plt.imshow(img)
plt.show()
predict_result=label_list[np.argmax(results_list[i])]
print("预测结果为: \n", predict_result)
print('------------------------------------')
if i ==5: #根据自己想查看多少张,可以修改这个i的值
break
break
break