利用全连接神经网络对车辆进行识别
import os
import zipfile
import random
import paddle
import numpy as np
import matplotlib.pyplot as plt
import PIL.Image as Image
from paddle.io import Dataset
(1)import os:导入操作系统(OS)模块,该模块提供了一种与文件系统交互、执行系统命令和管理环境变量等的方法。
(2)import zipfile:导入模块,该模块提供用于处理zip存档的工具。
(3)import random:导入模块,该模块提供用于生成随机数、随机序列和从序列中选择随机元素的功能。random
(4)import paddle:导入模块,该模块是一个开源深度学习平台,提供一套全面的工具来开发和训练机器学习模型。paddle
(5)import numpy as np:导入模块并将其别名为np,这是 Python 中流行的数值计算库,提供用于处理数组、矩阵和其他数学运算的工具。numpy
(6)import matplotlib.pyplot as plt:这是一个 Python 绘图库,提供用于创建可视化的工具,例如折线图、散点图、直方图等。matplotlib.pyplot
(7)import PIL.Image:这是 Python 映像库 (PIL) 的一部分,并提供用于处理图像文件的工具。PIL.Image
(8)from paddle.io import Dataset:从包中导入模块,该包提供了用于在 PaddlePaddle 中处理数据集的工具。例如加载、预处理和迭代数据。
'''
参数配置
'''
train_parameters = {"input_size": [3, 120, 120], #输入图片的shape"class_dim": 3, #分类数"src_path":"/home/aistudio/data/data72920/Data.zip", #原始数据集路径"target_path":"/home/aistudio/work/", #要解压的路径"train_list_path": "/home/aistudio/data/train.txt", #train.txt路径"eval_list_path": "/home/aistudio/data/eval.txt", #eval.txt路径"label_dict":{'0':'汽车','1':'摩托车','2':'货车'}, #标签字典"num_epochs": 3, #训练轮数"train_batch_size": 8, #训练时每个批次的大小"learning_strategy": { #优化函数相关的配置"lr": 0.1 #超参数学习率}, 'skip_steps': 50, #每N个批次打印一次结果'save_steps': 500, #每N个批次保存一次模型参数"checkpoints": "/home/aistudio/work/checkpoints" #保存的路径
}
一、数据准备
(1)解压原始数据集
(2)按照比例划分训练集与验证集
(3)乱序,生成数据列表
(4)定义数据读取器
#解压原始数据集
def unzip_data(src_path,target_path):'''解压原始数据集,将src_path路径下的zip包解压至target_path目录下'''if(not os.path.isdir(os.path.join(target_path,'Data'))): z = zipfile.ZipFile(src_path, 'r')z.extractall(path=target_path)z.close()print('数据集解压完成')else:print('文件已存在')
这段代码定义了一个名为 unzip_data 的函数,它有两个参数: src_path 和 target_path。
def get_data_list(target_path, train_list_path, eval_list_path):'''生成数据列表'''data_dir = 'work/Data' #这会将变量设置为包含数据的目录的路径。data_dirall_data_list = [] #这将初始化一个名为的空列表,该列表将用于存储数据路径及其相应的标签。for im in os.listdir(data_dir): #这将循环访问 指定的目录中的所有文件img_path = os.path.join(data_dir, im) #通过联接图像和文件名来构造图像文件的完整路径img_label = str(int(im.split('_')[0])-1) #假定标签是文件名的第一部分,下划线之前all_data_list.append(img_path + '\\t' + img_label + '\\n') #这会附加一个字符串,其中包含图像路径,后跟制表符 ,后跟标签和换行符。# 对训练列表进行乱序random.shuffle(all_data_list) #随机洗牌列表,为了防止模型在训练期间记住数据的顺序。with open(train_list_path, 'a') as f1: #这将在追加模式下打开两个文件。用于确保文件在使用后正确关闭。with open(eval_list_path, 'a') as f2:for ind, img_path_label in enumerate(all_data_list): #循环并将每个元素分配变量,并将索引分配给变量。#划分测试集和训练集if ind % 10 == 0: #这会将 的每个元素写入,具体取决于值。每 10个元素写入 ,其余元素写入f2.write(img_path_label) else:f1.write(img_path_label)print ('生成数据列表完成!')
#参数初始化
src_path=train_parameters['src_path']
target_path=train_parameters['target_path']
train_list_path=train_parameters['train_list_path']
eval_list_path=train_parameters['eval_list_path']#解压原始数据到指定路径
unzip_data(src_path,target_path)#每次生成数据列表前,首先清空train.txt和eval.txt
with open(train_list_path, 'w') as f: f.seek(0)f.truncate()
with open(eval_list_path, 'w') as f: f.seek(0)f.truncate() #生成数据列表
get_data_list(target_path,train_list_path,eval_list_path)
#参数初始化
src_path=train_parameters['src_path']
target_path=train_parameters['target_path']
train_list_path=train_parameters['train_list_path'] #此代码使用源数据、目标目录以及训练和评估数据列表
eval_list_path=train_parameters['eval_list_path'] #的文件路径的路径初始化变量。#解压原始数据到指定路径
unzip_data(src_path,target_path)#每次生成数据列表前,首先清空train.txt和eval.txt
with open(train_list_path, 'w') as f: f.seek(0)f.truncate()
with open(eval_list_path, 'w') as f: f.seek(0)f.truncate()
#生成数据列表
get_data_list(target_path,train_list_path,eval_list_path)
此代码定义了一个名为的 PyTorch 数据集类,该类可用于函数生成的训练或评估数据列表中加载图像和标签。
class dataset(Dataset):def __init__(self, data_path, mode='train'): #此代码为采用两个参数的类定义初始化方法"""数据读取器:param data_path: 数据集所在路径:param mode: train or eval"""super().__init__() #此代码调用继承自的类的初始化方法#这些行定义类的实例变量,包含训练和评估数据列表的目录的路径,存储图像文件路径的列表,存储相应标签的列表。self.data_path = data_path self.img_paths = []self.labels = []
#这些行根据 mode的值读取训练或评估数据列表中,并使用图像的文件路径和相应标签填充img和lable列表。if mode == 'train':with open(os.path.join(self.data_path, "train.txt"), "r", encoding="utf-8") as f:self.info = f.readlines()for img_info in self.info:img_path, label = img_info.strip().split('\\t')self.img_paths.append(img_path)self.labels.append(int(label))else:with open(os.path.join(self.data_path, "eval.txt"), "r", encoding="utf-8") as f:self.info = f.readlines()for img_info in self.info:img_path, label = img_info.strip().split('\\t')self.img_paths.append(img_path)self.labels.append(int(label))# 此代码定义了类的方法,PyTorch DataLoader 调用该方法以检索给定索引的特定图像和标签。def __getitem__(self, index):"""获取一组数据:param index: 文件索引号:return:"""# 第一步打开图像文件并获取label值#此代码检索与给定索引对应的图像的文件路径img_path = self.img_paths[index]img = Image.open(img_path)if img.mode != 'RGB':img = img.convert('RGB') img = np.array(img).astype('float32')img = img.transpose((2, 0, 1)) / 255label = self.labels[index]label = np.array([label], dtype="int64")return img, labeldef print_sample(self, index: int = 0):print("文件名", self.img_paths[index], "\\t标签值", self.labels[index])def __len__(self):return len(self.img_paths)
#训练数据加载
train_dataset = dataset('/home/aistudio/data',mode='train')
train_loader = paddle.io.DataLoader(train_dataset, batch_size=train_parameters['train_batch_size'], shuffle=True)
#测试数据加载
eval_dataset = dataset('/home/aistudio/data',mode='eval')
eval_loader = paddle.io.DataLoader(eval_dataset,batch_size=train_parameters['train_batch_size'], shuffle=False)#在这里,batch_size 参数用于指定每个 batch 的数据大小,shuffle 参数用于打乱数据集。
二、模型配置
class MyDNN(paddle.nn.Layer):def __init__(self):super(MyDNN,self).__init__()self.linear1 = paddle.nn.Linear(in_features=3*120*120, out_features=4096) # 定义了一个全连接层 linear1,输入维度为 3*120*120,输出维度为 4096。self.relu1 = paddle.nn.ReLU() # 定义了一个激活函数 relu1,使用 ReLU 激活函数。self.linear2 = paddle.nn.Linear(in_features=4096, out_features=2048) #定义了一个全连接层 linear2,输入维度为 4096,输出维度为 2048。self.relu2 = paddle.nn.ReLU() # 定义了一个激活函数 relu2,使用 ReLU 激活函数。self.linear3 = paddle.nn.Linear(in_features=2048, out_features=3) # 定义了一个全连接层 linear3,输入维度为 2048,输出维度为 3。def forward(self,input): # forward 定义执行实际运行时网络的执行逻辑、前向传播# input.shape (8, 3, 120, 120)x = paddle.reshape(input, shape=[-1,3*120*120]) #-1 表示这个维度的值是从 x
的元素总数和剩余维度推断出来的,有且只能有一个维度设置为-1# print(x.shape)x = self.linear1(x) # 将变换后的数据 x 传入 linear1 进行线性变换。x = self.relu1(x) # 将经过线性变换后的数据 x 传入 relu1 进行 ReLU 激活。x = self.linear2(x)x = self.relu2(x)y = self.linear3(x)return y # 返回y
这段代码定义了一个自定义的神经网络类 MyDNN,该类继承自 paddle.nn.Layer,并重写了 init 方法和 forward 方法。
三、模型训练
def draw_process(title,color,iters,data,label):plt.title(title, fontsize=24)plt.xlabel("iter", fontsize=20)plt.ylabel(label, fontsize=20)plt.plot(iters, data,color=color,label=label) plt.legend()plt.grid()plt.show()
该函数用于绘制训练过程中某个指标的变化曲线,其中参数含义如下:
title:图像的标题。
color:曲线的颜色。
iters:横坐标,代表迭代次数。
data:纵坐标,代表某个指标在每个迭代周期内的数值。
label:纵坐标的标签,用于说明纵坐标代表的具体指标。
model = MyDNN() #定义模型
model.train() #训练模型
cross_entropy = paddle.nn.CrossEntropyLoss() #定义损失函数
optimizer = paddle.optimizer.Adam(learning_rate=train_parameters['learning_strategy']['lr'],parameters=model.parameters()) #定义优化器steps = 0
Iters, total_loss, total_acc = [], [], []for epo in range(train_parameters['num_epochs']): for _, data in enumerate(train_loader()): #迭代训练steps += 1x_data = data[0]y_data = data[1]predicts = model(x_data)loss = cross_entropy(predicts, y_data)acc = paddle.metric.accuracy(predicts, y_data)loss.backward()optimizer.step()optimizer.clear_grad()if steps % train_parameters["skip_steps"] == 0:Iters.append(steps)total_loss.append(loss.numpy()[0])total_acc.append(acc.numpy()[0])#打印中间过程print('epo: {}, step: {}, loss is: {}, acc is: {}'\\.format(epo, steps, loss.numpy(), acc.numpy()))#保存模型参数if steps % train_parameters["save_steps"] == 0:save_path = train_parameters["checkpoints"]+"/"+"save_dir_" + str(steps) + '.pdparams'print('save model to: ' + save_path)paddle.save(model.state_dict(),save_path)
paddle.save(model.state_dict(),train_parameters["checkpoints"]+"/"+"save_dir_final.pdparams")
draw_process("trainning loss","red",Iters,total_loss,"trainning loss")
draw_process("trainning acc","green",Iters,total_acc,"trainning acc")
这段代码是一个训练模型的主程序。
四、模型评估
model__state_dict = paddle.load('work/checkpoints/save_dir_final.pdparams') #加载已经训练好的模型。
model_eval = MyDNN()
model_eval.set_state_dict(model__state_dict)
model_eval.eval()#代码对验证数据集进行迭代,对每个批次数据进行评估,并记录每个批次数据的准确率。这些准确率存储在 accs 列表中。
accs = []
for _, data in enumerate(eval_loader()):x_data = data[0]y_data = data[1]predicts = model_eval(x_data)acc = paddle.metric.accuracy(predicts, y_data)accs.append(acc.numpy()[0])
print('模型在验证集上的准确率为:',np.mean(accs)) # 代码计算并输出模型在验证集上的平均准确率。
五、模型预测
def load_image(img_path):'''预测图片预处理'''img = Image.open(img_path) #打开待预测图像# print(img.mode) if img.mode != 'RGB': img = img.convert('RGB') img = img.resize((120, 120), Image.ANTIALIAS) #使用 resize 方法将图像大小重置为 120x120 像素img = np.array(img).astype('float32') #将图像转换为 Numpy 数组,并将数据类型设置为 float32img = img.transpose((2, 0, 1)) / 255 # HWC to CHW 并像素归一化return img
#对一张待预测的图片进行预测,并输出预测结果
model__state_dict = paddle.load('work/checkpoints/save_dir_final.pdparams')
model_predict = MyDNN()
model_predict.set_state_dict(model__state_dict)
model_predict.eval()
infer_path='work/车辆.png'
infer_img = Image.open(infer_path)
plt.imshow(infer_img) #根据数组绘制图像
plt.show() #显示图像
#对预测图片进行预处理
infer_img = load_image(infer_path)
# print(type(infer_img))
infer_img = infer_img[np.newaxis,:, : ,:] #reshape(-1,3,50,50)
infer_img = paddle.to_tensor(infer_img)
results = model_predict(infer_img)
print(results)
results = paddle.nn.functional.softmax(results)
print(results)
print("汽车:{:.2f},摩托车:{:.2f},货车:{:.2f}" .format(results.numpy()[0][0],results.numpy()[0][1],results.numpy()[0][2]))