MindSpore数据加载报错【too many open files】

使用GeneratorDataset加载自定义数据集时,会根据num_parallel_workers来启动多线程或多线程来加载数据集,每个进程或者线程中都会尝试去打开数据集文件。

Linux一般默认允许同时打开的文件数为1024 ,当并发度过高 (num_parallel_workers)并且数据集文件较多,可能会导致出现 OSERROR: [Errno 24] Too many open files

此时可以在shell环境上执行ulimit -n 65535 来调整同时打开文件的阈值。

另外对Dataset对象进行“非常规”的操作,也有可能导致 Too many open files 错误。

  1 import numpy as np
  2 import mindspore.dataset as ds
  3 import argparse
  4 from mindspore import context
  5 from datasets.online_adaptation_dataset import OnlineAdaptationDataset, OnlineAdaptationDatasetStorage
  6
  7 class MyAccessible:
  8     def __init__(self):
  9         self._data = np.random.sample((10, 2))
 10         self._label = np.random.sample((10, 1))
 11
 12     def __getitem__(self, index):
 13         return self._data[index], self._label[index]
 14
 15     def __len__(self):
 16         return len(self._data)
 17
 18 class MyData:
 19     def __init__(self, data):
 20         self.data = data
 21
 22     def __getitem__(self, index):
 23         return self.data.__getitem__(index)
 24
 25 if __name__ == '__main__':
 26     parser = argparse.ArgumentParser(description='MindSpore LeNet Example')
 27     parser.add_argument('--device_target', type=str, default="Ascend", choices=['Ascend', 'GPU', 'CPU'])
 28
 29     args = parser.parse_known_args()[0]
 30     context.set_context(device_id=4, mode=context.PYNATIVE_MODE, device_target=args.device_target)
 31     for i in range(10):
 32         data = MyAccessible()
 33         dataset = ds.GeneratorDataset(source=MyData(data), column_names=["data", "label"],num_parallel_workers=4, shuffle=True)
 34         dataset = dataset.batch(batch_size=2)
 35         import pdb;pdb.set_trace()
 36         datas = iter(dataset)
 37         print(next(datas))
 38         datas = iter(dataset)

脚本中第36行使用iter()来创建迭代器对象后,在第37行中使用next(datas)读取迭代器中第一个元素,然后在第38行重复使用iter()来创建迭代器。

原因分析:

1. Mindspore提供了create_dict_iterator()和create_tuple_iterator(),分别返回dict迭代器和tuple迭代器,不建议直接使用iter()方法来生成迭代器。

create_dict_iterator()以及create_tuple_iterator(),可以设置num_epochs参数来控制迭代器的迭代次数,以及output_numpy参数来控制输出数据类型为numpy.ndarray或者Tensor。

2. 重复调用iter()会重复创建迭代器,而GeneratorDataset加载数据集时默认为多进程加载,每次打开的句柄在主进程停止前得不到释放,导致打开句柄数一直在增长。

解决方法:

第36行修改为

datas=dataset.create_tuple_iterator(num_epochs=1, output_numpy=True)

移除第38行