【深度学习系列】PaddlePaddle之数据预处理

  上篇文章讲了卷积神经网络的基本知识,本来这篇文章准备继续深入讲CNN的相关知识和手写CNN,但是有很多同学跟我发邮件或私信问我关于PaddlePaddle如何读取数据、做数据预处理相关的内容。网上看的很多教程都是几个常见的例子,数据集不需要自己准备,所以不需要关心,但是实际做项目的时候做数据预处理感觉一头雾水,所以我就写一篇文章汇总一下,讲讲如何用PaddlePaddle做数据预处理。



PaddlePaddle的基本数据格式

  根据官网的资料,总结出PaddlePaddle支持多种不同的数据格式,包括四种数据类型和三种序列格式:

四种数据类型:

  • dense_vector:稠密的浮点数向量。
  • sparse_binary_vector:稀疏的二值向量,即大部分值为0,但有值的地方必须为1。
  • sparse_float_vector:稀疏的向量,即大部分值为0,但有值的部分可以是任何浮点数。
  • integer:整型格式

api如下:

  • paddle.v2.data_type.dense_vector(dimseq_type=0)
    • 说明:稠密向量,输入特征是一个稠密的浮点向量。举个例子,手写数字识别里的输入图片是28*28的像素,Paddle的神经网络的输入应该是一个784维的稠密向量。
    • 参数:
      • dim(int) 向量维度
      • seq_type(int)输入的序列格式
    • 返回类型:InputType
  • paddle.v2.data_type.sparse_binary_vector(dimseq_type=0)
    • 说明:稀疏的二值向量。输入特征是一个稀疏向量,这个向量的每个元素要么是0,要么是1
    • 参数:同上
    • 返回类型:同上
  • paddle.v2.data_type.sparse_vector(dimseq_type=0)

    • 说明:稀疏向量,向量里大多数元素是0,其他的值可以是任意的浮点值
    • 参数:同上
    • 返回类型:同上
  • paddle.v2.data_type.integer_value(value_rangeseq_type=0)
    • 说明:整型格式
    • 参数:  
      • seq_type(int):输入的序列格式
      • value_range(int):每个元素的范围
    • 返回类型:InputType

三种序列格式:

  • SequenceType.NO_SEQUENCE:不是一条序列
  • SequenceType.SEQUENCE:是一条时间序列
  • SequenceType.SUB_SEQUENCE: 是一条时间序列,且序列的每一个元素还是一个时间序列。

api如下:

  • paddle.v2.data_type.dense_vector_sequence(dimseq_type=0)
    • 说明:稠密向量的序列格式
    • 参数:dim(int):稠密向量的维度
    • 返回类型:InputType
  • paddle.v2.data_type.sparse_binary_vector_sequence(dimseq_type=0)
    • 说明:稀疏的二值向量序列。每个序列里的元素要么是0要么是1
    • 参数:dim(int):稀疏向量的维度
    • 返回类型:InputType
  • paddle.v2.data_type.sparse_non_value_slot(dimseq_type=0)
    • 说明:稀疏的向量序列。每个序列里的元素要么是0要么是1
    • 参数:
      • dim(int):稀疏向量的维度
      • seq_type(int):输入的序列格式
    • 返回类型:InputType
  • paddle.v2.data_type.sparse_value_slot(dimseq_type=0)
    • 说明:稀疏的向量序列,向量里大多数元素是0,其他的值可以是任意的浮点值
    • 参数:
      • dim(int):稀疏向量的维度
        • seq_type(int):输入的序列格式
    • 返回类型:InputType
  • paddle.v2.data_type.integer_value_sequence(value_rangeseq_type=0)
    • 说明:value_range(int):每个元素的范围

  不同的数据类型和序列模式返回的格式不同,如下表:

  其中f表示浮点数,i表示整数

注意:对sparse_binary_vector和sparse_float_vector,PaddlePaddle存的是有值位置的索引。例如,

  • 对一个5维非序列的稀疏01向量 [0, 1, 1, 0, 0] ,类型是sparse_binary_vector,返回的是 [1, 2] 。(因为只有第1位和第2位有值)
  • 对一个5维非序列的稀疏浮点向量 [0, 0.5, 0.7, 0, 0] ,类型是sparse_float_vector,返回的是 [(1, 0.5), (2, 0.7)] 。(因为只有第一位和第二位有值,分别是0.5和0.7)


PaddlePaddle的数据读取方式

  我们了解了上文的四种基本数据格式和三种序列模式后,在处理自己的数据时可以根据需求选择,但是处理完数据后如何把数据放到模型里去训练呢?我们知道,基本的方法一般有两种:

  • 一次性加载到内存:模型训练时直接从内存中取数据,不需要大量的IO消耗,速度快,适合少量数据。
  • 加载到磁盘/HDFS/共享存储等:这样不用占用内存空间,在处理大量数据时一般采取这种方式,但是缺点是每次数据加载进来也是一次IO的开销,非常影响速度。

  在PaddlePaddle中我们可以有三种模式来读取数据:分别是reader、reader creator和reader decorator,这三者有什么区别呢?

  • reader:从本地、网络、分布式文件系统HDFS等读取数据,也可随机生成数据,并返回一个或多个数据项。
  • reader creator:一个返回reader的函数。
  • reader decorator:装饰器,可组合一个或多个reader。

  reader

  我们先以reader为例,为房价数据(斯坦福吴恩达的公开课第一课举例的数据)创建一个reader:

  1. 创建一个reader,实质上是一个迭代器,每次返回一条数据(此处以房价数据为例)
reader = paddle.dataset.uci_housing.train()

  2. 创建一个shuffle_reader,把上一步的reader放进去,配置buf_size就可以读取buf_size大小的数据自动做shuffle,让数据打乱,随机化

shuffle_reader = paddle.reader.shuffle(reader,buf_size= 100)

  3.创建一个batch_reader,把上一步混洗好的shuffle_reader放进去,给定batch_size,即可创建。

batch_reader = paddle.batch(shuffle_reader,batch_size = 2)

  这三种方式也可以组合起来放一块:

reader = paddle.batch(
    paddle.reader.shuffle(
        uci_housing.train(),
    buf_size = 100),
    batch_size=2)    

  可以以一个直观的图来表示:

 

  从图中可以看到,我们可以直接从原始数据集里拿去数据,用reader读取,一条条灌倒shuffle_reader里,在本地随机化,把数据打乱,做shuffle,然后把shuffle后的数据,一个batch一个batch的形式,批量的放到训练器里去进行每一步的迭代和训练。 流程简单,而且只需要使用一行代码即可实现整个过程。

  reader creator

  如果想要生成一个简单的随机数据,以reader creator为例:

def reader_creator():
    def reader():
        while True:
            yield numpy.random.uniform(-1,1,size=784)
    return reader

reader decorator

  如果想要读取同时读取两部分的数据,那么可以定义两个reader,合并后对其进行shuffle。如我想读取所有用户对比车系的数据和浏览车系的数据,可以定义两个reader,分别为contrast()和view(),然后通过预定义的reader decorator缓存并组合这些数据,在对合并后的数据进行乱序操作。

data = paddle.reader.shuffle(
        paddle.reader.compose(
            paddle.reader(contradt(contrast_path),buf_size = 100),
            paddle.reader(view(view_path),buf_size = 200),
            500)

   

  这样有一个很大的好处,就是组合特征来训练变得更容易了!传统的跑模型的方法是,确定label和feature,尽可能多的找合适的feature扔到模型里去训练,这样我们就需要做一张大表,训练完后我们可以分析某些特征的重要性然后重新增加或减少一些feature来进行训练,这样我们有需要对原来的label-feature表进行修改,如果数据量小没啥影响,就是麻烦点,但是数据量大的话需要每一次增加feature,和主键、label来join的操作都会很耗时,如果采取这种方式的话,我们可以对某些同一类的特征做成一张表,数据存放的地址存为一个变量名,每次跑模型的时候想选取几类特征,就创建几个reader,用reader decorator 组合起来,最后再shuffle灌倒模型里去训练。这!样!是!不!是!很!方!便!

  如果没理解,我举一个实例,假设我们要预测用户是否会买车,label是买车 or 不买车,feature有浏览车系、对比车系、关注车系的功能偏好等等20个,传统的思维是做成这样一张表:

  如果想要减少feature_2,看看feature_2对模型的准确率影响是否很大,那么我们需要在这张表里去掉这一列,想要增加一个feature的话,也需要在feature里增加一列,如果用reador decorator的话,我们可以这样做数据集:

  把相同类型的feature放在一起,不用频繁的join减少时间,一共做四个表,创建4个reador:

data = paddle.reader.shuffle(
            paddle.reader.compose(
                paddle.reader(table1(table1_path),buf_size = 100),
                paddle.reader(table2(table2_path),buf_size = 100),
                paddle.reader(table3(table3_path),buf_size = 100),
                paddle.reader(table4(table4_path),buf_size = 100),
            500)                    

  如果新发现了一个特征,想尝试这个特征对模型提高准确率有没有用,可以再单独把这个特征数据提取出来,再增加一个reader,用reader decorator组合起来,shuffle后放入模型里跑就行了。



PaddlePaddle的数据预处理实例

  还是以手写数字为例,对数据进行处理后并划分train和test,只需要4步即可:

  1. 指定数据地址
 1 import paddle.v2.dataset.common
 2 import subprocess
 3 import numpy
 4 import platform
 5 __all__ = [‘train‘, ‘test‘, ‘convert‘]
 6
 7 URL_PREFIX = ‘http://yann.lecun.com/exdb/mnist/‘
 8 TEST_IMAGE_URL = URL_PREFIX + ‘t10k-images-idx3-ubyte.gz‘
 9 TEST_IMAGE_MD5 = ‘9fb629c4189551a2d022fa330f9573f3‘
10 TEST_LABEL_URL = URL_PREFIX + ‘t10k-labels-idx1-ubyte.gz‘
11 TEST_LABEL_MD5 = ‘ec29112dd5afa0611ce80d1b7f02629c‘
12 TRAIN_IMAGE_URL = URL_PREFIX + ‘train-images-idx3-ubyte.gz‘
13 TRAIN_IMAGE_MD5 = ‘f68b3c2dcbeaaa9fbdd348bbdeb94873‘
14 TRAIN_LABEL_URL = URL_PREFIX + ‘train-labels-idx1-ubyte.gz‘
15 TRAIN_LABEL_MD5 = ‘d53e105ee54ea40749a09fcbcd1e9432‘

  2.创建reader creator

 1 def reader_creator(image_filename, label_filename, buffer_size):
 2     # 创建一个reader
 3     def reader():
 4         if platform.system() == ‘Darwin‘:
 5             zcat_cmd = ‘gzcat‘
 6         elif platform.system() == ‘Linux‘:
 7             zcat_cmd = ‘zcat‘
 8         else:
 9             raise NotImplementedError()
10
11         m = subprocess.Popen([zcat_cmd, image_filename], stdout=subprocess.PIPE)
12         m.stdout.read(16)
13
14         l = subprocess.Popen([zcat_cmd, label_filename], stdout=subprocess.PIPE)
15         l.stdout.read(8)
16
17         try:  # reader could be break.
18             while True:
19                 labels = numpy.fromfile(
20                     l.stdout, ‘ubyte‘, count=buffer_size).astype("int")
21
22                 if labels.size != buffer_size:
23                     break  # numpy.fromfile returns empty slice after EOF.
24
25                 images = numpy.fromfile(
26                     m.stdout, ‘ubyte‘, count=buffer_size * 28 * 28).reshape(
27                         (buffer_size, 28 * 28)).astype(‘float32‘)
28
29                 images = images / 255.0 * 2.0 - 1.0
30
31                 for i in xrange(buffer_size):
32                     yield images[i, :], int(labels[i])
33         finally:
34             m.terminate()
35             l.terminate()
36
37     return reader

  3.创建训练集和测试集

 1 def train():
 2     """
 3     创建mnsit的训练集 reader creator
 4     返回一个reador creator,每个reader里的样本都是图片的像素值,在区间[0,1]内,label为0~9
 5     返回:training reader creator
 6     """
 7     return reader_creator(
 8         paddle.v2.dataset.common.download(TRAIN_IMAGE_URL, ‘mnist‘,
 9                                           TRAIN_IMAGE_MD5),
10         paddle.v2.dataset.common.download(TRAIN_LABEL_URL, ‘mnist‘,
11                                           TRAIN_LABEL_MD5), 100)
12
13
14 def test():
15     """
16     创建mnsit的测试集 reader creator
17     返回一个reador creator,每个reader里的样本都是图片的像素值,在区间[0,1]内,label为0~9
18     返回:testreader creator
19     """
20     return reader_creator(
21         paddle.v2.dataset.common.download(TEST_IMAGE_URL, ‘mnist‘,
22                                           TEST_IMAGE_MD5),
23         paddle.v2.dataset.common.download(TEST_LABEL_URL, ‘mnist‘,
24                                           TEST_LABEL_MD5), 100)

  4.下载数据并转换成相应格式

 1 def fetch():
 2     paddle.v2.dataset.common.download(TRAIN_IMAGE_URL, ‘mnist‘, TRAIN_IMAGE_MD5)
 3     paddle.v2.dataset.common.download(TRAIN_LABEL_URL, ‘mnist‘, TRAIN_LABEL_MD5)
 4     paddle.v2.dataset.common.download(TEST_IMAGE_URL, ‘mnist‘, TEST_IMAGE_MD5)
 5     paddle.v2.dataset.common.download(TEST_LABEL_URL, ‘mnist‘, TRAIN_LABEL_MD5)
 6
 7
 8 def convert(path):
 9     """
10     将数据格式转换为 recordio format
11     """
12     paddle.v2.dataset.common.convert(path, train(), 1000, "minist_train")
13     paddle.v2.dataset.common.convert(path, test(), 1000, "minist_test")

  如果想换成自己的训练数据,只需要按照步骤改成自己的数据地址,创建相应的reader creator(或者reader decorator)即可。

  这是图像的例子,如果我们想训练一个文本模型,做一个情感分析,这个时候如何处理数据呢?步骤也很简单。

  假设我们有一堆数据,每一行为一条样本,以 \t 分隔,第一列是类别标签,第二列是输入文本的内容,文本内容中的词语以空格分隔。以下是两条示例数据:

positive        今天终于试了自己理想的车 外观太骚气了 而且中控也很棒
negative       这台车好贵 而且还费油 性价比太低了

  现在开始做数据预处理

  1.创建reader

 1 def train_reader(data_dir, word_dict, label_dict):
 2     def reader():
 3         UNK_ID = word_dict["<UNK>"]
 4         word_col = 0
 5         lbl_col = 1
 6
 7         for file_name in os.listdir(data_dir):
 8             with open(os.path.join(data_dir, file_name), "r") as f:
 9                 for line in f:
10                     line_split = line.strip().split("\t")
11                     word_ids = [
12                         word_dict.get(w, UNK_ID)
13                         for w in line_split[word_col].split()
14                     ]
15                     yield word_ids, label_dict[line_split[lbl_col]]
16
17     return reader

  返回类型为: paddle.data_type.integer_value_sequence(词语在字典的序号)和 paddle.data_type.integer_value(类别标签)

  2.组合读取方式

1 train_reader = paddle.batch(
2         paddle.reader.shuffle(
3             reader.train_reader(train_data_dir, word_dict, lbl_dict),
4             buf_size=1000),
5         batch_size=batch_size)

  

  完整的代码如下(加上了划分train和test部分):

 1 import os
 2
 3
 4 def train_reader(data_dir, word_dict, label_dict):
 5     """
 6    创建训练数据reader
 7     :param data_dir: 数据地址.
 8     :type data_dir: str
 9     :param word_dict: 词典地址,
10         词典里必须有 "UNK" .
11     :type word_dict:python dict
12     :param label_dict: label 字典的地址
13     :type label_dict: Python dict
14     """
15
16     def reader():
17         UNK_ID = word_dict["<UNK>"]
18         word_col = 1
19         lbl_col = 0
20
21         for file_name in os.listdir(data_dir):
22             with open(os.path.join(data_dir, file_name), "r") as f:
23                 for line in f:
24                     line_split = line.strip().split("\t")
25                     word_ids = [
26                         word_dict.get(w, UNK_ID)
27                         for w in line_split[word_col].split()
28                     ]
29                     yield word_ids, label_dict[line_split[lbl_col]]
30
31     return reader
32
33
34 def test_reader(data_dir, word_dict):
35     """
36     创建测试数据reader
37     :param data_dir: 数据地址.
38     :type data_dir: str
39     :param word_dict: 词典地址,
40         词典里必须有 "UNK" .
41     :type word_dict:python dict
42     """
43
44     def reader():
45         UNK_ID = word_dict["<UNK>"]
46         word_col = 1
47
48         for file_name in os.listdir(data_dir):
49             with open(os.path.join(data_dir, file_name), "r") as f:
50                 for line in f:
51                     line_split = line.strip().split("\t")
52                     if len(line_split) < word_col: continue
53                     word_ids = [
54                         word_dict.get(w, UNK_ID)
55                         for w in line_split[word_col].split()
56                     ]
57                     yield word_ids, line_split[word_col]
58
59     return reader


 总结 

  这篇文章主要讲了在paddlepaddle里如何加载自己的数据集,转换成相应的格式,并划分train和test。我们在使用一个框架的时候通常会先去跑几个简单的demo,但是如果不用常见的demo的数据,自己做一个实际的项目,完整的跑通一个模型,这才代表我们掌握了这个框架的基本应用知识。跑一个模型第一步就是数据预处理,在paddlepaddle里,提供的方式非常简单,但是有很多优点:

  •   shuffle数据非常方便
  •   可以将数据组合成batch训练
  •   可以利用reader decorator来组合多个reader,提高组合特征运行模型的效率
  •   可以多线程读取数据

  而我之前使用过mxnet来训练车牌识别的模型,50w的图片数据想要一次训练是非常慢的,这样的话就有两个解决方法:一是批量训练,这一点大多数的框架都会有,而是转换成mxnet特有的rec格式,提高读取效率,可以通过im2rec.py将图片转换,比较麻烦,如果是tesnorflow,也有相对应的特定格式tfrecord,这几种方式各有优劣,从易用性上,paddlepaddle是比较简单的。

  这篇文章没有与上篇衔接起来,因为看到有好几封邮件都有问怎么自己加载数据训练,所以就决定插入一节先把这个写了。下篇文章我们接着讲CNN的进阶知识。下周见^_^!

参考文章:

1.官网说明:http://doc.paddlepaddle.org/develop/doc_cn/getstarted/concepts/use_concepts_cn.html

时间: 2024-10-29 19:09:45

【深度学习系列】PaddlePaddle之数据预处理的相关文章

Deep Learning 十一_深度学习UFLDL教程:数据预处理(斯坦福大学深度学习教程)

理论知识:UFLDL数据预处理和http://www.cnblogs.com/tornadomeet/archive/2013/04/20/3033149.html 数据预处理是深度学习中非常重要的一步!如果说原始数据的获得,是深度学习中最重要的一步,那么获得原始数据之后对它的预处理更是重要的一部分. 1.数据预处理的方法: ①数据归一化: 简单缩放:对数据的每一个维度的值进行重新调节,使其在 [0,1]或[ − 1,1] 的区间内 逐样本均值消减:在每个样本上减去数据的统计平均值,用于平稳的数

【深度学习系列】关于PaddlePaddle的一些避“坑”技巧

最近除了工作以外,业余在参加Paddle的AI比赛,在用Paddle训练的过程中遇到了一些问题,并找到了解决方法,跟大家分享一下: PaddlePaddle的Anaconda的兼容问题 之前我是在服务器上安装的PaddlePaddle的gpu版本,我想把BROAD数据拷贝到服务器上面,结果发现我们服务器的22端口没开,不能用scp传上去,非常郁闷,只能在本地训练.本机mac的显卡是A卡,所以只能装cpu版本的,安装完以后,我发现运行一下程序的时候报错了: 1 import paddle.v2 a

【深度学习系列2】Mariana DNN多GPU数据并行框架

[深度学习系列2]Mariana DNN多GPU数据并行框架 本文是腾讯深度学习系列文章的第二篇,聚焦于腾讯深度学习平台Mariana中深度神经网络DNN的多GPU数据并行框架. 深度神经网络(Deep Neural Networks, 简称DNN)是近年来机器学习领域中的研究热点[1][2],产生了广泛的应用.DNN具有深层结构.数千万参数需要学习,导致训练非常耗时.GPU有强大的计算能力,适合于加速深度神经网络训练.DNN的单机多GPU数据并行框架是Mariana的一部分,Mariana技术

【深度学习系列1】 深度学习在腾讯的平台化和应用实践(转载)

转载:原文链接 [深度学习系列1] 深度学习在腾讯的平台化和应用实践 引言:深度学习是近年机器学习领域的重大突破,有着广泛的应用前景.随着Google公开 Google Brain计划,业界对深度学习的热情高涨.腾讯在深度学习领域持续投入,获得了实际落地的产出.我们准备了四篇文章,阐述深度学习的原理和在腾讯的实 践,介绍腾讯深度学习平台Mariana,本文为第一篇. 深度学习(Deep Learning)是近年来机器学习领域的热点,在语音识别.图像识别等领域均取得了突破性进展.腾讯提供广泛的互联

【深度学习系列3】 Mariana CNN并行框架与图像识别

[深度学习系列3] Mariana CNN并行框架与图像识别 本文是腾讯深度学习系列文章的第三篇,聚焦于腾讯深度学习平台Mariana中深度卷积神经网络Deep CNNs的多GPU模型并行和数据并行框架. 将深度卷积神经网络(Convolutional Neural Networks, 简称CNNs)用于图像识别在研究领域吸引着越来越多目光.由于卷积神经网络结构非常适合模型并行的训练,因此以模型并行+数据并行的方式来加速Deep CNNs训练,可预期取得较大收获.Deep CNNs的单机多GPU

使用腾讯云 GPU 学习深度学习系列之二:Tensorflow 简明原理【转】

转自:https://www.qcloud.com/community/article/598765?fromSource=gwzcw.117333.117333.117333 这是<使用腾讯云 GPU 学习深度学习>系列文章的第二篇,主要介绍了 Tensorflow 的原理,以及如何用最简单的Python代码进行功能实现.本系列文章主要介绍如何使用 腾讯云GPU服务器 进行深度学习运算,前面主要介绍原理部分,后期则以实践为主. 往期内容: 使用腾讯云 GPU 学习深度学习系列之一:传统机器学

【深度学习系列4】深度学习及并行化实现概述

[深度学习系列4]深度学习及并行化实现概述 摘要: 深度学习可以完成需要高度抽象特征的人工智能任务,如语音识别.图像识别和检索.自然语言理解等.深层模型是包含多个隐藏层的人工神经网络,多层非线性结构使其具备强大的特征表达能力和对复杂任务建模能力.训练深层模型是长期以来的难题,近年来以层次化.逐层初始化为代表的一系列方法的提出给训练深层模型带来了希望,并在多个应用领域获得了成功.深层模型的并行化框架和训练加速方法是深度学习走向实用的重要基石,已有多个针对不同深度模型的开源实现,Google.Fac

基于TensorFlow的深度学习系列教程 2——常量Constant

前面介绍过了Tensorflow的基本概念,比如如何使用tensorboard查看计算图.本篇则着重介绍和整理下Constant相关的内容. 基于TensorFlow的深度学习系列教程 1--Hello World! 常量的概念 在tensorflow中,数据分为几种类型: 常量Constant.变量Variable.占位符Placeholder.其中: 常量:用于存储一些不变的数值,在计算图创建的时候,调用初始化方法时,直接保存在计算图中 变量:模型训练的参数,比如全连接里面的W和bias 占

深度学习系列(2) | Global Average Pooling是否可以替代全连接层?

深度学习系列 | Global Average Pooling是否可以替代全连接层? Global Average Pooling(简称GAP,全局池化层)技术最早提出是在这篇论文(第3.2节)中,被认为是可以替代全连接层的一种新技术.在keras发布的经典模型中,可以看到不少模型甚至抛弃了全连接层,转而使用GAP,而在支持迁移学习方面,各个模型几乎都支持使用Global Average Pooling和Global Max Pooling(GMP). 然而,GAP是否真的可以取代全连接层?其背