『MXNet』第八彈_數據處理API_上


一、Gluon數據加載

下面的兩個dataset處理類一般會成對出現,兩個都可做預處理,但是由於后面還可能用到原始圖片,.ImageFolderDataset不加預處理的話可以滿足,所以建議在.DataLoader預處理

圖片數據(含標簽)加載函數:gluon.data.vision.ImageFolderDataset

  • .synsets,標簽名列表list,因為實際存儲位置是數字
  • .__len__

給出ImageFolderDataset類的描述,

Init signature:
 mxnet.gluon.data.vision.datasets.ImageFolderDataset(root, flag=1, transform=None)
Source:        
class ImageFolderDataset(dataset.Dataset):
    """A dataset for loading image files stored in a folder structure like::

        root/car/0001.jpg
        root/car/xxxa.jpg
        root/car/yyyb.jpg
        root/bus/123.jpg
        root/bus/023.jpg
        root/bus/wwww.jpg

    Parameters
    ----------
    root : str
        Path to root directory.
    flag : {0, 1}, default 1  # 控制彩色or灰度
        If 0, always convert loaded images to greyscale (1 channel).
        If 1, always convert loaded images to colored (3 channels).
    transform : callable, default None
        A function that takes data and label and transforms them:
    ::

        transform = lambda data, label: (data.astype(np.float32)/255, label)

    Attributes
    ----------
    synsets : list  # 查看類別,實際就是文件名
        List of class names. `synsets[i]` is the name for the integer label `i`
    items : list of tuples  # 生成的數據
        List of all images in (filename, label) pairs.
    """

實例:

train_imgs = gluon.data.vision.ImageFolderDataset(
    data_dir+'/hotdog/train',
    transform=lambda X, y: transform(X, y, train_augs))
test_imgs = gluon.data.vision.ImageFolderDataset(
    data_dir+'/hotdog/test',
    transform=lambda X, y: transform(X, y, test_augs))

print(train_imgs)
print(train_imgs.synsets)
data = gluon.data.DataLoader(train_imgs, 32, shuffle=True)
<mxnet.gluon.data.vision.datasets.ImageFolderDataset object at 0x7fbed5641c18>
['hotdog', 'not-hotdog']

batch迭代器:gluon.data.DataLoader

具有特殊方法,def __iter__(self),其實例可以被迭代,也就是每次返回一個batch的數據,在第一維度上切割。

首個定位參數文檔如下:

dataset : Dataset
        Source dataset. Note that numpy and mxnet arrays can be directly used
        as a Dataset.

最后生成的X_batch送入net(X_batch)向前傳播,y_batch送入loss(output,y_batch)計算loss后反向傳播。

二、MXNet.io:數據讀取迭代器

 https://blog.csdn.net/qq_36165459/article/details/78394322

從內存中讀取數據

當數據存儲在內存中,由NDArraynumpy ndarray 支持時,我們可以使用NDArrayIter 讀取數據:

import mxnet as mx
import numpy as np

data = np.random.rand(100,3)  # 100個數據每個數據3特征
label = np.random.randint(0, 10, (100,))  # 100個標簽
data_iter = mx.io.NDArrayIter(data=data, label=label, batch_size=30)
for batch in data_iter:
    print([batch.data, batch.label, batch.pad],'\n')

 

從CSV文件中讀取數據

MXNet提供 CSVIter 從CSV文件中讀取數據,用法如下:

#lets save `data` into a csv file first and try reading it back
np.savetxt('data.csv', data, delimiter=',')
# data_shape對應不上會報錯
data_iter = mx.io.CSVIter(data_csv='data.csv', data_shape=(3,), batch_size=30)  

for batch in data_iter:
    print([batch.data, batch.pad])

 

以上兩種方法中的pad屬性為int,表示有多少條數據是補充的(最后一個batch數據不夠時的策略)。

三、MXNet.recordio:二進制rec文件交互

RecordIO是MXNet用於數據IO的文件格式,文件后綴為.rec

它緊湊地打包數據,以便從Hadoop HDFS和AWS S3等分布式文件系統進行高效的讀寫。 MXNet提供MXRecordIOMXIndexedRecordIO,用於數據的順序訪問和隨機訪問。

注意,.rec文件寫入的必須是整數或者二進制數據。

mx.recordio.MXRecordIO:順序式rec文件

首先,我們來看一下如何使用MXRecordIO 順序讀寫的例子,

record = mx.recordio.MXRecordIO('tmp.rec', 'w')
for i in range(5):
    record.write(b'record_%d'%i)
record.close()

通過r 選項打開文件來讀取數據,如下所示,

record = mx.recordio.MXRecordIO('tmp.rec', 'r')
while True:
    item = record.read()
    if not item:
        break
    print (item)
record.close()

mx.recordio.MXIndexedRecordIO:隨機索引式rec文件

MXIndexedRecordIO 支持隨機或索引訪問數據。 我們將創建一個索引記錄文件和一個相應的索引文件,如下所示:

record = mx.recordio.MXIndexedRecordIO('tmp.idx', 'tmp.rec', 'w')
for i in range(5):
    record.write_idx(i, b'record_%d'%i)
record.close()

現在,我們可以使用鍵值訪問各個記錄:

record = mx.recordio.MXIndexedRecordIO('tmp.idx', 'tmp.rec', 'r')
record.read_idx(3)

還可以列出文件中的所有鍵:

record.keys

數據文件打包為規整的二進制結構

.rec文件中的每個記錄都可以包含任意的二進制數據,然而,大多數深度學習任務需要以標簽/數據格式作為輸入。

mx.recordio 包為此類操作提供了一些實用功能,即:pack,unpack,pack_imgunpack_img

二進制數據的裝包(mx.recordio.pack)與拆包(mx.recordio.unpack)

packunpack 用於存儲浮點數(或1維浮點數組)標簽和二進制數據。

數據與頭文件一起打包。

# pack
data = b'data'
label1 = 1.0
header1 = mx.recordio.IRHeader(flag=0, label=label1, id=1, id2=0)
s1 = mx.recordio.pack(header1, data)

label2 = [1.0, 2.0, 3.0]
header2 = mx.recordio.IRHeader(flag=3, label=label2, id=2, id2=0)
s2 = mx.recordio.pack(header2, data)

# unpack
print(mx.recordio.unpack(s1))
print(mx.recordio.unpack(s2))
(HEADER(flag=0, label=1.0, id=1, id2=0), b'data')
(HEADER(flag=3, label=array([ 1.,  2.,  3.], dtype=float32), id=2, id2=0), b'data')

mx.recordio.pack返回的s1、s2為二進制字節流,而mx.recordio.unpack則返回tuple。

s = mx.recordio.unpack(s1)
isinstance(s[0], tuple) 

 True

圖像數據的裝包與拆包

由於圖片數據在DL中尤為常用,所以單獨給圖片數組設計出接口,這個接口可以接收numpy數組,自動將之轉化為二進制數據存入文件,解壓時逆向操作。

MXNet提供pack_imgunpack_img 來打包/解壓圖像數據,pack_img 打包的記錄可以由mx.io.ImageRecordIter 加載。

data = np.ones((3,3,1), dtype=np.uint8)
label = 1.0
header = mx.recordio.IRHeader(flag=0, label=label, id=0, id2=0)
s = mx.recordio.pack_img(header, data, quality=100, img_fmt='.jpg')

# unpack_img
print(mx.recordio.unpack_img(s))

 (HEADER(flag=0, label=1.0, id=0, id2=0),

  array([[1, 1, 1],

             [1, 1, 1],

             [1, 1, 1]], dtype=uint8))

四、自定義迭代器

自定義迭代器

當內置的迭代器不符合應用需求時,可以創建自己的自定義數據迭代器。

MXNet中的迭代器應該:

  1. 實現Python2中的next() 或者Python3中的__ next() __,返回DataBatch 或者到數據流的末尾時拋出StopIteration 異常。
  2. 實現reset() 方法,從頭開始讀取數據
  3. 具有一個provide_data 屬性,包括存儲了數據的名稱,形狀,類型和布局信息的DataDesc 對象的列表
  4. 具有一個provide_label 屬性,包括存儲了標簽的名稱,形狀,類型和布局信息的DataDesc 對象的列表

當創建一個新的迭代器時,你既可以從頭開始定義一個迭代器,也可以使用一個現有的迭代器。例如,在圖像字幕應用中,輸入樣本是圖像,而標簽是句子。 因此,我們可以通過以下方式創建一個新的迭代器:

  • 通過使用ImageRecordIter 創建一個image_iter,它提供多線程的預取和增強。
  • 通過使用NDArrayIter 或 rnn 包中提供的bucketing 迭代器創建caption_iter
  • next() 返回image_iter.next()caption_iter.next() 的組合結果
class SimpleIter(mx.io.DataIter):
    def __init__(self, 
                 # data_shaps:包含batch維的數據,data_gen:函數,接收參數data_shapes
                 data_names, data_shapes, data_gen,
                 # label_shaps:包含batch維的標簽,label_gen:函數,接收參數label_shapes
                 label_names, label_shapes, label_gen,  
                 num_batches=10):
        """
        實際上這個class着重修改__init__和next即可,包證next的return是一個batch的數據
        n = 32
        data_iter = SimpleIter(['data'], [(n, 100)], 
                               [lambda s: np.random.uniform(-1, 1, s)], 
                               ['softmax_label'], [(n,)], 
                               [lambda s: np.random.randint(0, num_classes, s)])    
        """
        self._provide_data = zip(data_names, data_shapes)
        self._provide_label = zip(label_names, label_shapes)
        self.num_batches = num_batches
        self.data_gen = data_gen
        self.label_gen = label_gen
        self.cur_batch = 0

    def __iter__(self):
        return self

    def reset(self):
        self.cur_batch = 0

    def __next__(self):
        return self.next()

    @property
    def provide_data(self):
        return self._provide_data

    @property
    def provide_label(self):
        return self._provide_label

    def next(self):
        if self.cur_batch < self.num_batches:
            self.cur_batch += 1
            data = [mx.nd.array(g(d[1])) 
                    for d,g in zip(self._provide_data, self.data_gen)]
            label = [mx.nd.array(g(d[1])) 
                     for d,g in zip(self._provide_label, self.label_gen)]
            return mx.io.DataBatch(data, label)
        else:
            raise StopIteration

簡單定義一個網絡,

import mxnet as mx
num_classes = 10
net = mx.sym.Variable('data')
net = mx.sym.FullyConnected(data=net, name='fc1', num_hidden=64)
net = mx.sym.Activation(data=net, name='relu1', act_type="relu")
net = mx.sym.FullyConnected(data=net, name='fc2', num_hidden=num_classes)
net = mx.sym.SoftmaxOutput(data=net, name='softmax')
print(net.list_arguments())
print(net.list_outputs())

訓練示意,

import logging
logging.basicConfig(level=logging.INFO)

n = 32
data_iter = SimpleIter(['data'], [(n, 100)],
                  [lambda s: np.random.uniform(-1, 1, s)],
                  ['softmax_label'], [(n,)],
                  [lambda s: np.random.randint(0, num_classes, s)])

mod = mx.mod.Module(symbol=net)
mod.fit(data_iter, num_epoch=5)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM