如何自定义迭代器读取非图片的二进制文件?

目前我有40万个二进制文件,分别位于data和label文件夹下,现在想同他们来做数据训练,但是卡在如何写迭代器。

每个data文件夹的每个文件存储一个524维的向量,所以对于每个文件,读取很简单就是

float_size=4
fin=open('./data/0000001.dat','rb')
data_in=np.array(struct.unpack('<524f',fin.read(float_size*524)))
fin.close()

每个label文件夹的每个文件存储一个43维的向量,所以对于每个文件,读取很简单就是

float_size=4
fin=open('./label/0000001.dat','rb')
label_in=np.array(struct.unpack('<43f',fin.read(float_size*43)))
fin.close()

但是不知道怎么把数据读取和mxnet结合起来?

目前找到三个特别好的教程
自定义hdf5文件迭代器
自定义图片迭代器
自定义迭代器的源码io.py指南

2赞

写一个Dataet类的子类

class custom_dset(gl.data.Dataset):
    def __init__(self, txt_file):
        with open(txt_file, 'r') as f:
            lines = f.readlines()
    
    def __getitem(self, idx):
        return lines[idx], label[idx]
    
    def __len__(self):
        return len(lines)

我现在写了个大概版本

现在新数据是这样 每个样本 输入是523维向量 输出是frame*43维的矩阵

如果想用mini-batch那就必须同时返回多个样本,可是我发现
1.我看DataDesc 只支持固定维度,可是这里一个mini-batch内部每个输出样本维度是各自的frame,都是不一样的。不提供provide_data也行吧 Databatch似乎支持多维度

image

2.mx.nd.array([[1,2,3,4,5,6],[1,2,3]]) 这种列表不支持 那该怎么办呢?

难道是只能一次处理一个样本?那可是几十万个样本量呀!!!

已解决 自定义的迭代器

import mxnet as mx
from mxnet.io import DataIter,DataBatch
from mxnet import nd
from tools import ReadFloatRawMat,read_file_list

class FileIter(DataIter):

    def __init__(self, batch_size=32,root_dir='./data/',data_dims=523,label_dims=43,
                 data_name="data", label_name="softmax_label",
                 last_batch_handle="pad"):
        super(DataIter, self).__init__()

        self.root_dir=root_dir
        self.batch_size = batch_size
        self.cursor = -batch_size
        self.last_batch_handle = last_batch_handle
        self.data_name = data_name
        self.label_name = label_name
        training_files=read_file_list(root_dir+'filelist.train.lst')
        self.training_set_data = map(lambda i:self.root_dir+'data/'+i+'.dat',training_files)
        self.training_set_label = map(lambda i:self.root_dir+'label_normalization/'+i+'.dat',training_files)
        self.num_trainingfiles = len(self.training_set_data)
        self.data_dims=data_dims
        self.label_dims=label_dims

    def read_data_files(self,filelst):
        data_lst=[]
        for i in filelst:
            data_lst.append(nd.array(ReadFloatRawMat(i,self.data_dims)))
        return data_lst
    
    def read_label_files(self,filelst):
        label_lst=[]
        for i in filelst:
            label_lst.append(nd.array(ReadFloatRawMat(i,self.label_dims)))
        return label_lst

    def reset(self):
        self.cursor = -self.batch_size 


    def iter_next(self):
        self.cursor += self.batch_size
        return self.cursor < self.num_trainingfiles


    def next(self):
        if self.iter_next():
            return DataBatch(data=self.getdata(),
                             label=self.getlabel(),
                             pad=self.getpad())
        else:
            raise StopIteration


    def getdata(self):
        assert(self.cursor < self.num_trainingfiles), "DataIter needs reset."
        if self.cursor + self.batch_size <= self.num_trainingfiles:
            return self.read_data_files(self.training_set_data[self.cursor:self.cursor+self.batch_size])
        else:
            pad = self.batch_size - self.num_trainingfiles + self.cursor
            return self.read_data_files(self.training_set_data[self.cursor:]+self.training_set_data[:pad])


    def getlabel(self):
        assert(self.cursor < self.num_trainingfiles), "DataIter needs reset."
        if self.cursor + self.batch_size <= self.num_trainingfiles:
            return self.read_label_files(self.training_set_label[self.cursor:self.cursor+self.batch_size])
        else:
            pad = self.batch_size - self.num_trainingfiles + self.cursor
            return self.read_label_files(self.training_set_label[self.cursor:]+self.training_set_label[:pad])


    def getpad(self):
        if self.last_batch_handle == "pad" and self.cursor + self.batch_size > self.num_trainingfiles:
            return self.cursor + self.batch_size - self.num_trainingfiles
        else:
            return 0
            
if __name__=='__main__':
    dataiter=FileIter(batch_size=10,root_dir='./data/')
    batchidx=0
    for batch in dataiter:
        batchidx++
    print batchidx

Databatch支持多维度

1赞

这是去年十一月的帖子 之后一直用的是这个方法读取数据 但是慢
由于再之后数据量不大就全部导入内存中了,这个方法也就搁置了半年

现在首先我有继续使用这种迭代器的需求,其次不想这么慢了,而且了解到现在Dataloader速度因为加入了多线程速度可以很快(见Gluon Datasets and DataLoader 以及 mxnet gluon GPU 训练速度问题?

现在我准备仿照ImageFolderDataset重新搭建基于Dataset + DataLoader的迭代器

话说读mxnet这个迭代器的源码发现写的真是很好 简洁易懂

已实现 单线程情况

# -*- coding: utf-8 -*-
import mxnet as mx
from mxnet.gluon import nn
from mxnet import nd,gluon,autograd,gpu,cpu
import numpy as np
from tqdm import tqdm
import pickle,os
from tools import ReadFloatRawMat,read_file_list

def default_batchify_fn(data):
    """Collate data into batch."""
    if isinstance(data[0], nd.NDArray):
        return list(data)
    elif isinstance(data[0], tuple):
        data = zip(*data)
        return [default_batchify_fn(i) for i in data]
    else:
        data = np.asarray(data)
        return nd.array(data, dtype=data.dtype)

class BinaryFolderDataset(mx.gluon.data.dataset.Dataset):
    def __init__(self, root, data_dims=12, label_dims=43):
        self._root = root
        self._exts = '.dat'
        self._list_images(self._root)
        self.data_dims=data_dims
        self.label_dims=label_dims

    def _list_images(self, root):
        self.items = []

        self.training_files=read_file_list(os.path.join(root,'filelist_Random.lst'))
        self.training_set_phoneId_Dict = pickle.load(open(os.path.join(root,'data_normalization','data_phone_id','phone_id.dict'),'r'))
        self.training_set_data_frameInfo = map(lambda i:\
            os.path.join(root,'data_normalization', 'data_frame_info', i+'.dat'),self.training_files)
        self.training_set_label = map(lambda i:\
            os.path.join(root,'label_normalization_MinMax', i+'.dat'),self.training_files)

        for i,file in enumerate(self.training_files):
            self.items.append((self.training_set_phoneId_Dict[file], self.training_set_data_frameInfo[i], self.training_set_label[i]))

    def __getitem__(self, idx):
        phoneIdx = self.items[idx][0]
        frame_info = nd.array(ReadFloatRawMat(self.items[idx][1],self.data_dims))
        acoustic = nd.array(ReadFloatRawMat(self.items[idx][2],self.label_dims))
        return phoneIdx, frame_info, acoustic

    def __len__(self):
        return len(self.items)

if __name__=='__main__':

    dataset = BinaryFolderDataset(root='../Unit2Vec_LSTM/data')

    data_loader = mx.gluon.data.DataLoader(dataset, batch_size=3, batchify_fn=default_batchify_fn)
    for  X1_batch, X2_batch, y_batch in data_loader:
        print X1_batch, X2_batch, y_batch
        break
1赞

请问你这样实现后导入数据速度如何?我用于背景分割,这样实现后加上增强transform函数,导入数据很慢,训练一个epoch大概要10分钟,感觉很慢

其实不慢 你可以试试光读数据不训练
可以一秒60batch 但是一旦训练 就变成4batch/s
我猜测是模型训练太慢 拖累了整体速度