日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

LMDB使用说明_ldd教程

發布時間:2023/12/15 综合教程 29 生活家
生活随笔 收集整理的這篇文章主要介紹了 LMDB使用说明_ldd教程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Jetbrains全系列IDE穩定放心使用

http://rayz0620.github.io/2015/05/25/lmdb_in_caffe/

官方的extract_feature.bin很好用,但是輸出的特征是放在LMDB里的。以前嫌LMDB麻煩,一直都圖方便直接用ImageDataLayer來讀原始圖像。這次繞不過去了,就順便研究了一下Caffe對LMDB的使用,一些心得寫下來和大家分享一下。提取特征的內容下一篇再寫。

Caffe中DataLayer默認的數據格式是LMDB。許多example中提供的輸入數據是LMDB格式。使用extract_features.bin提取特征時支持的輸出格式之一也是LMDB。LMDB在Caffe的IO功能中有相當重要的地位。因此,搞明白如何存取Caffe的LMDB數據,對于我們使用Caffe是很有幫助的。

LMDB

Caffe使用LMDB來存放訓練/測試用的數據集,以及使用網絡提取出的feature(為了方便,以下還是統稱數據集)。數據集的結構很簡單,就是大量的矩陣/向量數據平鋪開來。數據之間沒有什么關聯,數據內沒有復雜的對象結構,就是向量和矩陣。既然數據并不復雜,Caffe就選擇了LMDB這個簡單的數據庫來存放數據。

LMDB的全稱是Lightning Memory-Mapped Database,閃電般的內存映射數據庫。它文件結構簡單,一個文件夾,里面一個數據文件,一個鎖文件。數據隨意復制,隨意傳輸。它的訪問簡單,不需要運行單獨的數據庫管理進程,只要在訪問數據的代碼里引用LMDB庫,訪問時給文件路徑即可。

圖像數據集歸根究底從圖像文件而來。既然有ImageDataLayer可以直接讀取圖像文件,為什么還要用數據庫來放數據集,增加讀寫的麻煩呢?我認為,Caffe引入數據庫存放數據集,是為了減少IO開銷。讀取大量小文件的開銷是非常大的,尤其是在機械硬盤上。LMDB的整個數據庫放在一個文件里,避免了文件系統尋址的開銷。LMDB使用內存映射的方式訪問文件,使得文件內尋址的開銷非常小,使用指針運算就能實現。數據庫單文件還能減少數據集復制/傳輸過程的開銷。一個幾萬,幾十萬文件的數據集,不管是直接復制,還是打包再解包,過程都無比漫長而痛苦。LMDB數據庫只有一個文件,你的介質有多塊,就能復制多快,不會因為文件多而慢如蝸牛。

Caffe中的LMDB數據

接下來要介紹Caffe是如何使用LMDB存放數據的。
Caffe中的LMDB數據大約有兩類:一類是輸入DataLayer的訓練/測試數據集;另一類則是extract_feature輸出的特征數據。

Datum數據結構

首先需要注意的是,Caffe并不是把向量和矩陣直接放進數據庫的,而是將數據通過caffe.proto里定義的一個datum類來封裝。數據庫里放的是一個個的datum序列化成的字符串。Datum的定義摘錄如下:

1
2
3
4
5
6
7
8
9
10
11
12
message Datum {
      
      
  optional int32 channels = 1;
  optional int32 height = 2;
  optional int32 width = 3;
  // the actual image data, in bytes
  optional bytes data = 4;
  optional int32 label = 5;
  // Optionally, the datum could also hold float data.
  repeated float float_data = 6;
  // If true data contains an encoded image that need to be decoded
  optional bool encoded = 7 [default = false];
}

一個Datum有三個維度,channels,height,和width,可以看做是少了num維度的Blob。存放數據的地方有兩個:byte_datafloat_data,分別存放整數型和浮點型數據。圖像數據一般是整形,放在byte_data里,特征向量一般是浮點型,放在float_data里。label存放數據的類別標簽,是整數型。encoded標識數據是否需要被解碼(里面有可能放的是JPEG或者PNG之類經過編碼的數據)。

Datum這個數據結構將數據和標簽封裝在一起,兼容整形和浮點型數據。經過Protobuf編譯后,可以在Python和C++中都提供高效的訪問。同時Protubuf還為它提供了序列化與反序列化的功能。存放進LMDB的就是Datum序列化生成的字符串。

Caffe中讀寫LMDB的代碼

要想知道Caffe是如何使用LMDB的,最好的方法當然是去看Caffe的代碼。Caffe中關于LMDB的代碼有三類:生成數據集、讀取數據集、生成特征向量。接下來就分別針對三者進行分析。

生成數據集

生成數據集的代碼在examples,隨數據集提供,比如MNIST。

首先,創建訪問LMDB所需的一些變量:

1
2
3
4
5
MDB_env *mdb_env;
MDB_dbi mdb_dbi;
MDB_val mdb_key, mdb_data;
MDB_txn *mdb_txn;
...

mdb_env是整個數據庫環境的句柄,mdb_dbi是環境中一個數據庫的句柄,mdb_keymdb_data用來存放向數據庫中輸入數據的“值”。mdb_txn是數據庫事物操作的句柄,”txn”是”transaction”的縮寫。

然后,創建數據庫環境,創建并打開數據庫:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if (db_backend == "lmdb") {  // lmdb
  LOG(INFO) << "Opening lmdb " << db_path;
  CHECK_EQ(mkdir(db_path, 0744), 0)
      << "mkdir " << db_path << "failed";
  CHECK_EQ(mdb_env_create(&mdb_env), MDB_SUCCESS) << "mdb_env_create failed";
  CHECK_EQ(mdb_env_set_mapsize(mdb_env, 1099511627776), MDB_SUCCESS)  // 1TB
      << "mdb_env_set_mapsize failed";
  CHECK_EQ(mdb_env_open(mdb_env, db_path, 0, 0664), MDB_SUCCESS)
      << "mdb_env_open failed";
  CHECK_EQ(mdb_txn_begin(mdb_env, NULL, 0, &mdb_txn), MDB_SUCCESS)
      << "mdb_txn_begin failed";
  CHECK_EQ(mdb_open(mdb_txn, NULL, 0, &mdb_dbi), MDB_SUCCESS)
      << "mdb_open failed. Does the lmdb already exist? ";
} else {
      
      
  LOG(FATAL) << "Unknown db backend " << db_backend;
}

第3行代碼為數據庫創建文件夾,如果文件夾已經存在,程序會報錯退出。也就是說,程序不會覆蓋已有的數據庫。已有的數據庫如果不要了,需要手動刪除。第13行處創建并打開了一個數據庫。需要注意的是,LMDB的一個環境中是可以有多個數據庫的,數據庫之間以名字區分。mdb_open()的第二個參數實際上就是數據庫的名稱(char *)。當一個環境中只有一個數據庫的時候,這個參數可以給NULL

最后,為每一個圖像創建Datum對象,向對象內寫入數據,然后將其序列化成字符串,將字符串放入數據庫中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Datum datum;
datum.set_channels(1);
datum.set_height(rows);
datum.set_width(cols);
for (int item_id = 0; item_id < num_items; ++item_id) {
      
      
  image_file.read(pixels, rows * cols);
  label_file.read(&label, 1);
  datum.set_data(pixels, rows*cols);
  datum.set_label(label);
  snprintf(key_cstr, kMaxKeyLength, "%08d", item_id);
  datum.SerializeToString(&value);
  string keystr(key_cstr);

  // Put in db
  if (db_backend == "lmdb") {  // lmdb
    mdb_data.mv_size = value.size();
    mdb_data.mv_data = reinterpret_cast<void*>(&value[0]);
    mdb_key.mv_size = keystr.size();
    mdb_key.mv_data = reinterpret_cast<void*>(&keystr[0]);
    CHECK_EQ(mdb_put(mdb_txn, mdb_dbi, &mdb_key, &mdb_data, 0), MDB_SUCCESS)
        << "mdb_put failed";
  } else {
      
      
    LOG(FATAL) << "Unknown db backend " << db_backend;
  }

  if (++count % 1000 == 0) {
      
      
    // Commit txn
    if (db_backend == "lmdb") {  // lmdb
      CHECK_EQ(mdb_txn_commit(mdb_txn), MDB_SUCCESS)
          << "mdb_txn_commit failed";
      CHECK_EQ(mdb_txn_begin(mdb_env, NULL, 0, &mdb_txn), MDB_SUCCESS)
          << "mdb_txn_begin failed";
    } else {
      
      
      LOG(FATAL) << "Unknown db backend " << db_backend;
    }
  }
}

放入數據的Key是圖像的編號,前面補0至8位。需要注意的是18至21行,MDB_val類型的mdb_datamdb_key中存放的是數據來源的指針,以及數據的長度。第20行的mdb_put()函數將數據存入數據庫。每隔1000個圖像commit一次數據庫。只有commit之后,數據才真正寫入磁盤。

讀取數據集

Caffe中讀取LMDB數據集的代碼是DataLayer,用在網絡的最下層,提供數據。DataLayer采用順序遍歷的方式讀取數據,不支持打亂數據順序,只能隨機跳過前若干個數據。

首先,在DataLayerDataLayerSetUp方法中,打開數據庫,并獲取迭代器cursor_

1
2
3
db_.reset(db::GetDB(this->layer_param_.data_param().backend()));
db_->Open(this->layer_param_.data_param().source(), db::READ);
cursor_.reset(db_->NewCursor());

然后,在每一次的數據預取時,InternalThreadEntry()方法中,從數據庫中讀取字符串,反序列化為Datum對象,再從Datum對象中取出數據:

1
2
Datum datum;
datum.ParseFromString(cursor_->value());

其中,cursor_->value()獲取序列化后的字符串。datum.ParseFromString()方法對字符串進行反序列化。

最后,要將cursor_向前推進:

1
2
3
4
5
cursor_->Next();
if (!cursor_->valid()) {
      
      
  DLOG(INFO) << "Restarting data prefetching from start."
      cursor_->SeekToFirst();
}

如果cursor->valid()返回false,說明數據庫已經遍歷到頭,這時需要將cursor_重置回數據庫開頭。

不支持樣本隨機排序應該是DataLayer的致命弱點。如果數據庫的key能夠統一,其實可以通過對key隨機枚舉的方式實現。

總結

以上是生活随笔為你收集整理的LMDB使用说明_ldd教程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。