利用全连接神经网络对车辆进行识别

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_dir
    all_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)) / 255
        label = self.labels[index]
        label = np.array([label], dtype="int64")
        return img, label

    def 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 += 1
        x_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 数组,并将数据类型设置为 float32
    img = 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]))                                               

你可能感兴趣的:(神经网络,python,机器学习)