【问题标题】:Writing data to LMDB with Python very slow使用 Python 将数据写入 LMDB 非常慢
【发布时间】:2015-10-17 09:50:09
【问题描述】:

使用Caffe 创建用于训练的数据集我都尝试使用 HDF5 和 LMDB。但是,创建 LMDB 非常慢,甚至比 HDF5 还要慢。我正在尝试写大约 20,000 张图片。

我做错了什么?有什么我不知道的吗?

这是我创建 LMDB 的代码:

DB_KEY_FORMAT = "{:0>10d}"
db = lmdb.open(path, map_size=int(1e12))
    curr_idx = 0
    commit_size = 1000
    for curr_commit_idx in range(0, num_data, commit_size):
        with in_db_data.begin(write=True) as in_txn:
            for i in range(curr_commit_idx, min(curr_commit_idx + commit_size, num_data)):
                d, l = data[i], labels[i]
                im_dat = caffe.io.array_to_datum(d.astype(float), label=int(l))
                key = DB_KEY_FORMAT.format(curr_idx)
                in_txn.put(key, im_dat.SerializeToString())
                curr_idx += 1
    db.close()

如您所见,我为每 1,000 张图像创建一个事务,因为我认为为每个图像创建一个事务会产生开销,但这似乎不会对性能产生太大影响。

【问题讨论】:

  • 您为什么不使用convert_imageset 工具?
  • @Shai:其实我不知道,但我也没有我的图像作为文件。但是,为什么它应该更快? Python实现这么慢吗?
  • 我正在与convert_imageset 合作,在 ilsvrc12 (imagenet) 上转换约 100 万张图像的数据集,这需要一段时间,但它可以工作。
  • 你从哪里得到你的data
  • 我有包含我的数据的 HDF5 文件。我知道 Caffe 可以使用 HDF5 文件作为数据源,不幸的是,这样做时 Caffe 不允许数据转换。

标签: python caffe lmdb


【解决方案1】:

我做了一个小基准来说明 Ophir 的观点:

机器:

RasPi 4B - 超频至 1.75 GHz、4GB、RasperryPi 操作系统、SSD 上的操作系统

代码:

def insert_lmdb(fsobj, transaction):
    transaction.put(key=str(fsobj).encode("utf-8", "ignore"), value=generate_hash_from_file(fsobj).hexdigest().encode("utf-8", "ignore"))
list_f = list_files(FOLDER)

print(f"\n> Insert results in lmdb <")
list_f = Directory(path=DIR_ECTORY, use_hash=False, hash_from_content=False).lists["files"]

# list_f = sorted(list_f) # Run only in the 'sorted' case.

st = timeit.default_timer()

env = lmdb.open(path=DB_NAME)

with env.begin(write=True) as txn:
    for i in list_f:
        insert_lmdb(i, transaction=txn)
average = (timeit.default_timer() - st)*1000000/records

print(f"Test repeated {TIMES} times.\nNumber of files: {records}\nAverage time: {round(average, 3)} us or {round(1000000/average/1000, 3)}k inserts/sec")

结果:

没有排序:

> Insert results in lmdb <
Test repeated 50000 times.
Number of files: 363
Average time: 84 us or 12k inserts/sec

带排序:

> Insert results in lmdb <
Test repeated 50000 times.
Number of files: 363
Average time: 18.5 us or 54k inserts/sec

排序带来了 4.5 倍的写入速度提升,仅增加一行代码就不错了 :)。

【讨论】:

    【解决方案2】:

    LMDB 写入对顺序非常敏感 - 如果您可以在插入速度显着提高之前对数据进行排序

    【讨论】:

      【解决方案3】:

      根据我的经验,我有 50-100 毫秒从 Python 写入 LMDB 在 Ubuntu 的 ext4 硬盘上写入 Caffe 数据。 这就是我使用 tmpfsRAM 磁盘 内置于 Linux 中的功能)并在大约 0.07 毫秒 内完成这些写入的原因。您可以在 ramdisk 上创建较小的数据库并将它们复制到硬盘上,然后在所有这些上进行训练。我正在制作大约 20-40GB 的内存,因为我有 64 GB 的 RAM。

      一些代码可以帮助你们动态地创建、填充和移动 LMDB 到存储。随意编辑它以适合您的情况。它应该可以为您节省一些时间来了解 LMDB 和文件操作在 Python 中的工作原理。

      import shutil
      import lmdb
      import random
      
      
      def move_db():
          global image_db
          image_db.close();
          rnd = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(5))
          shutil.move( fold + 'ram/train_images',  '/storage/lmdb/'+rnd)
          open_db()
      
      
      def open_db():
          global image_db
          image_db    = lmdb.open(os.path.join(fold, 'ram/train_images'),
                  map_async=True,
                  max_dbs=0)
      
      def write_to_lmdb(db, key, value):
          """
          Write (key,value) to db
          """
          success = False
          while not success:
              txn = db.begin(write=True)
              try:
                  txn.put(key, value)
                  txn.commit()
                  success = True
              except lmdb.MapFullError:
                  txn.abort()
                  # double the map_size
                  curr_limit = db.info()['map_size']
                  new_limit = curr_limit*2
                  print '>>> Doubling LMDB map size to %sMB ...' % (new_limit>>20,)
                  db.set_mapsize(new_limit) # double it
      
      ...
      
      image_datum                 = caffe.io.array_to_datum( transformed_image, label )
      write_to_lmdb(image_db, str(itr), image_datum.SerializeToString())
      

      【讨论】:

      • 你能多介绍一下tempfs是什么吗?
      • 您能否提供描述您的解决方案/工作流程的具体代码?
      • 这是一个很好的建议! @SteveHeim 有关在 Ubuntu 中创建 RAM 磁盘的详细信息,请参阅 this post。您可以将目录挂载到 RAM 位置,而不是将数据写入硬盘(当涉及大量写入时会非常慢)。虽然接口与任何其他目录相同,但对挂载目录的读写访问速度将快几个数量级。使用完数据库后,您可以将其移动到硬盘上的另一个目录以进行长期存储。
      • Steve,正如我所写,tempfs 是 Linux 上的 RAM 磁盘 fs。如果你在另一个操作系统上,你可以使用不同的 RAM 磁盘文件系统,没关系。
      • 其实,抱歉打错了,我的意思是 tmpfs。 Shai,我的特定代码非常具体,因为我通过套接字通过进程间通信获取数据 - 我正在扭曲 HTML 画布上的内容并将其发布到套接字。但是好的,我将更新我的答案以包含用于操作数据库的代码。不过,请在其他地方阅读有关 tmpfs 的信息。那是有据可查的。
      【解决方案4】:

      试试这个:

      DB_KEY_FORMAT = "{:0>10d}"
      db = lmdb.open(path, map_size=int(1e12))
          curr_idx = 0
          commit_size = 1000
          with in_db_data.begin(write=True) as in_txn:
              for curr_commit_idx in range(0, num_data, commit_size):
                  for i in range(curr_commit_idx, min(curr_commit_idx + commit_size, num_data)):
                      d, l = data[i], labels[i]
                      im_dat = caffe.io.array_to_datum(d.astype(float), label=int(l))
                      key = DB_KEY_FORMAT.format(curr_idx)
                      in_txn.put(key, im_dat.SerializeToString())
                      curr_idx += 1
          db.close()
      

      代码

      with in_db_data.begin(write=True) as in_txn:
      

      需要很多时间。

      【讨论】:

        猜你喜欢
        • 2019-05-01
        • 2014-08-25
        • 1970-01-01
        • 2016-12-13
        • 1970-01-01
        • 2017-12-25
        • 2016-07-25
        • 2020-04-27
        • 2018-10-12
        相关资源
        最近更新 更多