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]))