手写数字识别任务之数据处理

云深之无迹 2021-01-27 00:00

舍不得我的GPU

这次横向逐步进行优化


在前文中,我们直接用API导入了数据,但是现实中,搬砖环境千变万化,我们总是要拿自己的数据的处理的:

  • 读入数据

  • 划分数据集

  • 生成批次数据

  • 训练样本集乱序

  • 校验数据有效性

import paddleimport paddle.fluid as fluidfrom paddle.fluid.dygraph.nn import Linearimport numpy as npimport osimport gzipimport jsonimport random

导入一些工具库

数据集的储存结构


data包含三个元素的列表:train_setval_set、 test_set

  • train_set(训练集):包含50000条手写数字图片和对应的标签,用于确定模型参数。

  • val_set(验证集):包含10000条手写数字图片和对应的标签,用于调节模型超参数(如多个网络结构、正则化权重的最优选择)。

  • test_set(测试集):包含10000条手写数字图片和对应的标签,用于估计应用效果(没有在模型中应用过的数据,更贴近模型在真实场景应用的效果)。

train_set包含两个元素的列表:train_imagestrain_labels

  • train_images:[5000, 784]的二维列表,包含5000张图片。每张图片用一个长度为784的向量表示,内容是28*28尺寸的像素灰度值(黑白图片)。

  • train_labels:[5000, ]的列表,表示这些图片对应的分类标签,即0-9之间的一个数字。


# 声明数据集文件位置datafile = './work/mnist.json.gz'print('loading mnist dataset from {} ......'.format(datafile))# 加载json数据文件data = json.load(gzip.open(datafile))print('mnist dataset load done')# 读取到的数据区分训练集,验证集,测试集train_set, val_set, eval_set = data
# 数据集相关参数,图片高度IMG_ROWS, 图片宽度IMG_COLSIMG_ROWS = 28IMG_COLS = 28
# 打印数据信息imgs, labels = train_set[0], train_set[1]print("训练数据集数量: ", len(imgs))
# 观察验证集数量imgs, labels = val_set[0], val_set[1]print("验证数据集数量: ", len(imgs))
# 观察测试集数量imgs, labels = val= eval_set[0], eval_set[1]print("测试数据集数量: ", len(imgs))

读取以及拆分的代码



  • 训练样本乱序: 先将样本按顺序进行编号,建立ID集合index_list。然后将index_list乱序,最后按乱序后的顺序读取数据。


说明:

通过大量实验发现,模型对最后出现的数据印象更加深刻。训练数据导入后,越接近模型训练结束,最后几个批次数据对模型参数的影响越大。为了避免模型记忆影响训练效果,需要进行样本乱序操作。


  • 生成批次数据: 先设置合理的batch_size,再将数据转变成符合模型输入要求的np.array格式返回。同时,在返回数据时将Python生成器设置为yield模式,以减少内存占用。

在执行如上两个操作之前,需要先将数据处理代码封装成load_data函数,方便后续调用。load_data有三种模型:trainvalideval,分为对应返回的数据是训练集、验证集、测试集。

imgs, labels = train_set[0], train_set[1]print("训练数据集数量: ", len(imgs))# 获得数据集长度imgs_length = len(imgs)# 定义数据集每个数据的序号,根据序号读取数据index_list = list(range(imgs_length))# 读入数据时用到的批次大小BATCHSIZE = 100
# 随机打乱训练数据的索引序号random.shuffle(index_list)
# 定义数据生成器,返回批次数据def data_generator():
imgs_list = [] labels_list = [] for i in index_list: # 将数据处理成希望的格式,比如类型为float32,shape为[1, 28, 28] img = np.reshape(imgs[i], [1, IMG_ROWS, IMG_COLS]).astype('float32') label = np.reshape(labels[i], [1]).astype('float32') imgs_list.append(img) labels_list.append(label) if len(imgs_list) == BATCHSIZE: # 获得一个batchsize的数据,并返回 yield np.array(imgs_list), np.array(labels_list) # 清空数据读取列表 imgs_list = [] labels_list = []
# 如果剩余数据的数目小于BATCHSIZE, # 则剩余数据一起构成一个大小为len(imgs_list)的mini-batch if len(imgs_list) > 0: yield np.array(imgs_list), np.array(labels_list) return data_generator

# 声明数据读取函数,从训练集中读取数据train_loader = data_generator# 以迭代的形式读取数据for batch_id, data in enumerate(train_loader()): image_data, label_data = data if batch_id == 0: # 打印数据shape和类型 print("打印第一个batch数据的维度:") print("图像维度: {}, 标签维度: {}".format(image_data.shape, label_data.shape)) break



在实际应用中,原始数据可能存在标注不准确、数据杂乱或格式不统一等情况。因此在完成数据处理流程后,还需要进行数据校验,一般有两种方式:

  • 机器校验:加入一些校验和清理数据的操作。

  • 人工校验:先打印数据输出结果,观察是否是设置的格式。再从训练的结果验证数据处理和读取的有效性。


 imgs_length = len(imgs)
assert len(imgs) == len(labels), \ "length of train_imgs({}) should be the same as train_labels({})".format(len(imgs), len(label))


# 声明数据读取函数,从训练集中读取数据train_loader = data_generator# 以迭代的形式读取数据for batch_id, data in enumerate(train_loader()): image_data, label_data = data if batch_id == 0: # 打印数据shape和类型 print("打印第一个batch数据的维度,以及数据的类型:") print("图像维度: {}, 标签维度: {}, 图像数据类型: {}, 标签数据类型: {}".format(image_data.shape, label_data.shape, type(image_data), type(label_data))) break

再放一个人工校验的:

人工校验是指打印数据输出结果,观察是否是预期的格式。实现数据处理和加载函数后,我们可以调用它读取一次数据,观察数据的shape和类型是否与函数中设置的一致。

def load_data(mode='train'): datafile = './work/mnist.json.gz' print('loading mnist dataset from {} ......'.format(datafile)) # 加载json数据文件 data = json.load(gzip.open(datafile)) print('mnist dataset load done')
# 读取到的数据区分训练集,验证集,测试集 train_set, val_set, eval_set = data if mode=='train': # 获得训练数据集 imgs, labels = train_set[0], train_set[1] elif mode=='valid': # 获得验证数据集 imgs, labels = val_set[0], val_set[1] elif mode=='eval': # 获得测试数据集 imgs, labels = eval_set[0], eval_set[1] else: raise Exception("mode can only be one of ['train', 'valid', 'eval']") print("训练数据集数量: ", len(imgs))
# 校验数据 imgs_length = len(imgs)
assert len(imgs) == len(labels), \ "length of train_imgs({}) should be the same as train_labels({})".format(len(imgs), len(label))
# 获得数据集长度 imgs_length = len(imgs)
# 定义数据集每个数据的序号,根据序号读取数据 index_list = list(range(imgs_length)) # 读入数据时用到的批次大小 BATCHSIZE = 100
# 定义数据生成器 def data_generator(): if mode == 'train': # 训练模式下打乱数据 random.shuffle(index_list) imgs_list = [] labels_list = [] for i in index_list: # 将数据处理成希望的格式,比如类型为float32,shape为[1, 28, 28] img = np.reshape(imgs[i], [1, IMG_ROWS, IMG_COLS]).astype('float32') label = np.reshape(labels[i], [1]).astype('float32') imgs_list.append(img) labels_list.append(label) if len(imgs_list) == BATCHSIZE: # 获得一个batchsize的数据,并返回 yield np.array(imgs_list), np.array(labels_list) # 清空数据读取列表 imgs_list = [] labels_list = []
# 如果剩余数据的数目小于BATCHSIZE, # 则剩余数据一起构成一个大小为len(imgs_list)的mini-batch if len(imgs_list) > 0: yield np.array(imgs_list), np.array(labels_list) return data_generator

怕流程太复杂,那就写一个打包的函数就是这样~

#数据处理部分之后的代码,数据读取的部分调用Load_data函数# 定义网络结构,同上一节所使用的网络结构class MNIST(fluid.dygraph.Layer): def __init__(self): super(MNIST, self).__init__() self.fc = Linear(input_dim=784, output_dim=1, act=None)
def forward(self, inputs): inputs = fluid.layers.reshape(inputs, (-1, 784)) outputs = self.fc(inputs) return outputs
# 训练配置,并启动训练过程with fluid.dygraph.guard(): model = MNIST() model.train() #调用加载数据的函数 train_loader = load_data('train') optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.001, parameter_list=model.parameters()) EPOCH_NUM = 10 for epoch_id in range(EPOCH_NUM): for batch_id, data in enumerate(train_loader()): #准备数据,变得更加简洁 image_data, label_data = data image = fluid.dygraph.to_variable(image_data) label = fluid.dygraph.to_variable(label_data)
#前向计算的过程 predict = model(image)
#计算损失,取一个批次样本损失的平均值 loss = fluid.layers.square_error_cost(predict, label) avg_loss = fluid.layers.mean(loss)
#每训练了200批次的数据,打印下当前Loss的情况 if batch_id % 200 == 0: print("epoch: {}, batch: {}, loss is: {}".format(epoch_id, batch_id, avg_loss.numpy()))
#后向传播,更新参数的过程 avg_loss.backward() optimizer.minimize(avg_loss) model.clear_gradients()
#保存模型参数 fluid.save_dygraph(model.state_dict(), 'mnist')

定义一个神经网络层,然后开始训练

因为代码没有更改,所以.一样的lose

同步和异步的数据读取方式


上面提到的数据读取采用的是同步数据读取方式。对于样本量较大、数据读取较慢的场景,建议采用异步数据读取方式。

异步读取数据时,数据读取和模型训练并行执行,从而加快了数据读取速度,牺牲一小部分内存换取数据读取效率的提升


  • 同步数据读取:数据读取与模型训练串行。当模型需要数据时,才运行数据读取函数获得当前批次的数据。在读取数据期间,模型一直等待数据读取结束才进行训练,数据读取速度相对较慢。

  • 异步数据读取:数据读取和模型训练并行。读取到的数据不断的放入缓存区,无需等待模型训练就可以启动下一轮数据读取。当模型训练完一个批次后,不用等待数据读取过程,直接从缓存区获得下一批次数据进行训练,从而加快了数据读取速度。

  • 异步队列:数据读取和模型训练交互的仓库,二者均可以从仓库中读取数据,它的存在使得两者的工作节奏可以解耦。

# 定义数据读取后存放的位置,CPU或者GPU,这里使用CPU# place = fluid.CUDAPlace(0) 时,数据读取到GPU上place = fluid.CPUPlace()with fluid.dygraph.guard(place): # 声明数据加载函数,使用训练模式 train_loader = load_data(mode='train') # 定义DataLoader对象用于加载Python生成器产生的数据 data_loader = fluid.io.DataLoader.from_generator(capacity=5, return_list=True) # 设置数据生成器 data_loader.set_batch_generator(train_loader, places=place) # 迭代的读取数据并打印数据的形状 for i, data in enumerate(data_loader): image_data, label_data = data print(i, image_data.shape, label_data.shape) if i>=5: break

飞桨的异步读取是这样的

与同步数据读取相比,异步数据读取仅增加了三行代码,如下所示。

place = fluid.CPUPlace()

# 设置读取的数据是放在CPU还是GPU上。

data_loader = fluid.io.DataLoader.from_generator(capacity=5, return_list=True)

# 创建一个DataLoader对象用于加载Python生成器产生的数据。数据会由Python线程预先读取,并异步送入一个队列中。

data_loader.set_batch_generator(train_loader, place)

# 用创建的DataLoader对象设置一个数据生成器set_batch_generator,输入的参数是一个Python数据生成器train_loader和服务器资源类型place(标明CPU还是GPU)

fluid.io.DataLoader.from_generator参数名称和含义如下:

  • feed_list:仅在PaddlePaddle静态图中使用,动态图中设置为“None”,本教程默认使用动态图的建模方式;

  • capacity:表示在DataLoader中维护的队列容量,如果读取数据的速度很快,建议设置为更大的值;

  • use_double_buffer:是一个布尔型的参数,设置为“True”时,Dataloader会预先异步读取下一个batch的数据并放到缓存区;

  • iterable:表示创建的Dataloader对象是否是可迭代的,一般设置为“True”;

  • return_list:在动态图模式下需要设置为“True”。

异步数据读取并训练的完整案例代码如下

with fluid.dygraph.guard(): model = MNIST() model.train() #调用加载数据的函数 train_loader = load_data('train') # 创建异步数据读取器 place = fluid.CPUPlace() data_loader = fluid.io.DataLoader.from_generator(capacity=5, return_list=True) data_loader.set_batch_generator(train_loader, places=place)  optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.001, parameter_list=model.parameters()) EPOCH_NUM = 3 for epoch_id in range(EPOCH_NUM): for batch_id, data in enumerate(data_loader): image_data, label_data = data image = fluid.dygraph.to_variable(image_data) label = fluid.dygraph.to_variable(label_data)  predict = model(image)  loss = fluid.layers.square_error_cost(predict, label) avg_loss = fluid.layers.mean(loss)  if batch_id % 200 == 0: print("epoch: {}, batch: {}, loss is: {}".format(epoch_id, batch_id, avg_loss.numpy()))  avg_loss.backward() optimizer.minimize(avg_loss) model.clear_gradients()
fluid.save_dygraph(model.state_dict(), 'mnist')
评论
  • 在不断发展的电子元件领域,继电器——作为切换电路的关键设备,正在经历前所未有的技术变革。固态继电器(SSR)和机械继电器之间的争论由来已久。然而,从未来发展的角度来看,固态继电器正逐渐占据上风。本文将从耐用性、速度和能效三个方面,全面剖析固态继电器为何更具优势,并探讨其在行业中的应用与发展趋势。1. 耐用性:经久耐用的设计机械继电器:机械继电器依靠物理触点完成电路切换。然而,随着时间的推移,这些触点因电弧、氧化和材料老化而逐渐磨损,导致其使用寿命有限。因此,它们更适合低频或对切换耐久性要求不高的
    腾恩科技-彭工 2025-01-10 16:15 100浏览
  • 数字隔离芯片是现代电气工程师在进行电路设计时所必须考虑的一种电子元件,主要用于保护低压控制电路中敏感电子设备的稳定运行与操作人员的人身安全。其不仅能隔离两个或多个高低压回路之间的电气联系,还能防止漏电流、共模噪声与浪涌等干扰信号的传播,有效增强电路间信号传输的抗干扰能力,同时提升电子系统的电磁兼容性与通信稳定性。容耦隔离芯片的典型应用原理图值得一提的是,在电子电路中引入隔离措施会带来传输延迟、功耗增加、成本增加与尺寸增加等问题,而数字隔离芯片的目标就是尽可能消除这些不利影响,同时满足安全法规的要
    华普微HOPERF 2025-01-15 09:48 78浏览
  • 食物浪费已成为全球亟待解决的严峻挑战,并对环境和经济造成了重大影响。最新统计数据显示,全球高达三分之一的粮食在生产过程中损失或被无谓浪费,这不仅导致了资源消耗,还加剧了温室气体排放,并带来了巨大经济损失。全球领先的光学解决方案供应商艾迈斯欧司朗(SIX:AMS)近日宣布,艾迈斯欧司朗基于AS7341多光谱传感器开发的创新应用来解决食物浪费这一全球性难题。其多光谱传感解决方案为农业与食品行业带来深远变革,该技术通过精确判定最佳收获时机,提升质量控制水平,并在整个供应链中有效减少浪费。 在2024
    艾迈斯欧司朗 2025-01-14 18:45 59浏览
  • 随着通信技术的迅速发展,现代通信设备需要更高效、可靠且紧凑的解决方案来应对日益复杂的系统。中国自主研发和制造的国产接口芯片,正逐渐成为通信设备(从5G基站到工业通信模块)中的重要基石。这些芯片凭借卓越性能、成本效益及灵活性,满足了现代通信基础设施的多样化需求。 1. 接口芯片在通信设备中的关键作用接口芯片作为数据交互的桥梁,是通信设备中不可或缺的核心组件。它们在设备内的各种子系统之间实现无缝数据传输,支持高速数据交换、协议转换和信号调节等功能。无论是5G基站中的数据处理,还是物联网网关
    克里雅半导体科技 2025-01-10 16:20 444浏览
  • 随着全球向绿色能源转型的加速,对高效、可靠和环保元件的需求从未如此强烈。在这种背景下,国产固态继电器(SSR)在实现太阳能逆变器、风力涡轮机和储能系统等关键技术方面发挥着关键作用。本文探讨了绿色能源系统背景下中国固态继电器行业的前景,并强调了2025年的前景。 1.对绿色能源解决方案日益增长的需求绿色能源系统依靠先进的电源管理技术来最大限度地提高效率并最大限度地减少损失。固态继电器以其耐用性、快速开关速度和抗机械磨损而闻名,正日益成为传统机电继电器的首选。可再生能源(尤其是太阳能和风能
    克里雅半导体科技 2025-01-10 16:18 325浏览
  • Snyk 是一家为开发人员提供安全平台的公司,致力于协助他们构建安全的应用程序,并为安全团队提供应对数字世界挑战的工具。以下为 Snyk 如何通过 CircleCI 实现其“交付”使命的案例分析。一、Snyk 的挑战随着客户对安全工具需求的不断增长,Snyk 的开发团队面临多重挑战:加速交付的需求:Snyk 的核心目标是为开发者提供更快、更可靠的安全解决方案,但他们的现有 CI/CD 工具(TravisCI)运行缓慢,无法满足快速开发和部署的要求。扩展能力不足:随着团队规模和代码库的不断扩大,S
    艾体宝IT 2025-01-10 15:52 164浏览
  •   在信号处理过程中,由于信号的时域截断会导致频谱扩展泄露现象。那么导致频谱泄露发生的根本原因是什么?又该采取什么样的改善方法。本文以ADC性能指标的测试场景为例,探讨了对ADC的输出结果进行非周期截断所带来的影响及问题总结。 两个点   为了更好的分析或处理信号,实际应用时需要从频域而非时域的角度观察原信号。但物理意义上只能直接获取信号的时域信息,为了得到信号的频域信息需要利用傅里叶变换这个工具计算出原信号的频谱函数。但对于计算机来说实现这种计算需要面对两个问题: 1.
    TIAN301 2025-01-14 14:15 108浏览
  • 电动汽车(EV)正在改变交通运输,为传统内燃机提供更清洁、更高效的替代方案。这种转变的核心是电力电子和能源管理方面的创新,而光耦合器在其中发挥着关键作用。这些不起眼的组件可实现可靠的通信、增强安全性并优化电动汽车系统的性能,使其成为正在进行的革命中不可或缺的一部分。光耦合器,也称为光隔离器,是一种使用光传输电信号的设备。通过隔离高压和低压电路,光耦合器可确保安全性、减少干扰并保持信号完整性。这些特性对于电动汽车至关重要,因为精确控制和安全性至关重要。 光耦合器在电动汽车中的作用1.电池
    腾恩科技-彭工 2025-01-10 16:14 78浏览
  • 流量传感器是实现对燃气、废气、生活用水、污水、冷却液、石油等各种流体流量精准计量的关键手段。但随着工业自动化、数字化、智能化与低碳化进程的不断加速,采用传统机械式检测方式的流量传感器已不能满足当代流体计量行业对于测量精度、测量范围、使用寿命与维护成本等方面的精细需求。流量传感器的应用场景(部分)超声波流量传感器,是一种利用超声波技术测量流体流量的新型传感器,其主要通过发射超声波信号并接收反射回来的信号,根据超声波在流体中传播的时间、幅度或相位变化等参数,间接计算流体的流量,具有非侵入式测量、高精
    华普微HOPERF 2025-01-13 14:18 482浏览
  • PNT、GNSS、GPS均是卫星定位和导航相关领域中的常见缩写词,他们经常会被用到,且在很多情况下会被等同使用或替换使用。我们会把定位导航功能测试叫做PNT性能测试,也会叫做GNSS性能测试。我们会把定位导航终端叫做GNSS模块,也会叫做GPS模块。但是实际上他们之间是有一些重要的区别。伴随着技术发展与越发深入,我们有必要对这三个词汇做以清晰的区分。一、什么是GPS?GPS是Global Positioning System(全球定位系统)的缩写,它是美国建立的全球卫星定位导航系统,是GNSS概
    德思特测试测量 2025-01-13 15:42 491浏览
  • 01. 什么是过程能力分析?过程能力研究利用生产过程中初始一批产品的数据,预测制造过程是否能够稳定地生产符合规格的产品。可以把它想象成一种预测。通过历史数据的分析,推断未来是否可以依赖该工艺持续生产高质量产品。客户可能会要求将过程能力研究作为生产件批准程序 (PPAP) 的一部分。这是为了确保制造过程能够持续稳定地生产合格的产品。02. 基本概念在定义制造过程时,目标是确保生产的零件符合上下规格限 (USL 和 LSL)。过程能力衡量制造过程能多大程度上稳定地生产符合规格的产品。核心概念很简单:
    优思学院 2025-01-12 15:43 522浏览
  • 根据Global Info Research(环洋市场咨询)项目团队最新调研,预计2030年全球无人机电池和电源产值达到2834百万美元,2024-2030年期间年复合增长率CAGR为10.1%。 无人机电池是为无人机提供动力并使其飞行的关键。无人机使用的电池类型因无人机的大小和型号而异。一些常见的无人机电池类型包括锂聚合物(LiPo)电池、锂离子电池和镍氢(NiMH)电池。锂聚合物电池是最常用的无人机电池类型,因为其能量密度高、设计轻巧。这些电池以输出功率大、飞行时间长而著称。不过,它们需要
    GIRtina 2025-01-13 10:49 182浏览
  • 新年伊始,又到了对去年做总结,对今年做展望的时刻 不知道你在2024年初立的Flag都实现了吗? 2025年对自己又有什么新的期待呢? 2024年注定是不平凡的一年, 一年里我测评了50余块开发板, 写出了很多科普文章, 从一个小小的工作室成长为科工公司。 展望2025年, 中国香河英茂科工, 会继续深耕于,具身机器人、飞行器、物联网等方面的研发, 我觉得,要向未来学习未来, 未来是什么? 是掌握在孩子们生活中的发现,和精历, 把最好的技术带给孩子,
    丙丁先生 2025-01-11 11:35 457浏览
  • ARMv8-A是ARM公司为满足新需求而重新设计的一个架构,是近20年来ARM架构变动最大的一次。以下是对ARMv8-A的详细介绍: 1. 背景介绍    ARM公司最初并未涉足PC市场,其产品主要针对功耗敏感的移动设备。     随着技术的发展和市场需求的变化,ARM开始扩展到企业设备、服务器等领域,这要求其架构能够支持更大的内存和更复杂的计算任务。 2. 架构特点    ARMv8-A引入了Execution State(执行状
    丙丁先生 2025-01-12 10:30 466浏览
  • 随着数字化的不断推进,LED显示屏行业对4K、8K等超高清画质的需求日益提升。与此同时,Mini及Micro LED技术的日益成熟,推动了间距小于1.2 Pitch的Mini、Micro LED显示屏的快速发展。这类显示屏不仅画质卓越,而且尺寸适中,通常在110至1000英寸之间,非常适合应用于电影院、监控中心、大型会议、以及电影拍摄等多种室内场景。鉴于室内LED显示屏与用户距离较近,因此对于噪音控制、体积小型化、冗余备份能力及电气安全性的要求尤为严格。为满足这一市场需求,开关电源技术推出了专为
    晶台光耦 2025-01-13 10:42 498浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦