Mnistは手書き数字画像とラベルデータをまとめたデータセットで機械学習の性能評価などによく利用さます。Deep Learningの入門書ではPythonで扱われることが多いのですが、今回はC++で読み込みEigenで扱う形式に変換してみたいと思います。
1. Mnistデータセットのダウンロード
以下のサイトからダウンロードすることができます。
※学習用画像データセット
※学習用ラベルデータセット
※検証用画像データセット
※検証用画像データセット
ダウンロードしたファイルをそれぞれ任意の場所に解凍します。
2. Mnistデータセットの内訳
C++で読み込む前にMnistデータセットの内容を確認します。
・画像データセット
| アドレス | 内容 | サイズ |
|---|---|---|
| 0x0000 | マジックナンバー | 4Byte |
| 0x0004 | データ総数 | 4Byte |
| 0x0008 | 画像データ縦サイズ[Rows] | 4Byte |
| 0x000C | 画像データ横サイズ[Cols] | 4Byte |
| 0x0010 | 画像データ | (Rows×Cols)Byte |
| … | … | … |
| 0xXXXX | 画像データ | (Rows×Cols)Byte |
・ラベルデータセット
| アドレス | 内容 | サイズ |
|---|---|---|
| 0x0000 | マジックナンバー | 4Byte |
| 0x0004 | データ総数 | 4Byte |
| 0x0008 | ラベルデータ | 1Byte |
| … | … | … |
| 0xXXXX | ラベルデータ | 1Byte |
画像データセットは0x0000~0x000Fまでがヘッダー情報、0x0010以降が画像データとなっています。一方ラベルデータセットは0x0000~0x0007までがヘッダー情報、0x0008以降がラベルデータとなっています。
3. ヘッダー情報を数値データへ変換
まずはヘッダー情報を読み込むため、4Byte分のデータを数値データへ変換するための関数を作成します。
// 4Byte分のデータを数値データへ変換
int Convert4Byte(char *cData) {
unsigned char ucData[4];
int iData = 0;
for (int i = 0; i < 4; i++) {
ucData[i] = (unsigned char)cData[i];
iData |= (int)ucData[i] << (24 - (i * 8));
}
return iData;
}
4. 画像データセットの読み込み
画像データセットをバイナリ形式で読み込みEigen変数へ格納する関数を作成します。
// 画像データ読み込み
int ReadFile_Image(string filename, vector<MatrixXd>& mat_img) {
char cData[16];
int magic_number = 0;
int number_of_images = 0;
int rows = 0;
int cols = 0;
ifstream ifs(filename.c_str(), std::ios::in | std::ios::binary);
// ヘッダー読み込み
ifs.read(cData, 16);
// マジックナンバー
magic_number = Convert4Byte(cData);
// データ総数
number_of_images = Convert4Byte(&cData[4]);
// 各データの行数
rows = Convert4Byte(&cData[8]);
// 各データの列数
cols = Convert4Byte(&cData[12]);
// ヘッダー表示
cout << "マジックナンバー" << " " << magic_number << endl;
cout << "画像総数" << " " << number_of_images << endl;
cout << "画像縦サイズ[Rows]" << " " << rows << endl;
cout << "画像横サイズ[Cols]" << " " << cols << endl;
// データセット読み込み->Eigen変数へ格納
MatrixXd mat = MatrixXd::Zero(rows, cols);
int i;
mat_img.resize(number_of_images);
for (i = 0; i < number_of_images; i++) {
mat_img[i] = MatrixXd::Zero(rows, cols);
unsigned char *temp = new unsigned char[rows*cols];
ifs.read((char*)temp, rows*cols);
int count = 0;
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
mat_img[i](r, c) = (double)temp[count++];
}
}
delete[] temp;
}
return number_of_images;
}
5. ラベルデータセットの読み込み
ラベルデータセットをバイナリ形式で読み込みEigen変数へ格納する関数を作成します。
// ラベルデータ読み込み
int ReadFile_Label(string filename, MatrixXd& mat_label) {
char cData[8];
int magic_number = 0;
int number_of_items = 0;
ifstream ifs(filename.c_str(), std::ios::in | std::ios::binary);
// ヘッダー読み込み
ifs.read(cData, 8);
// マジックナンバー
magic_number = Convert4Byte(cData);
// データ総数
number_of_items = Convert4Byte(&cData[4]);
// ヘッダー表示
cout << "マジックナンバー" << " " << magic_number << endl;
cout << "ラベル総数" << " " << number_of_items << endl;
// データセット読み込み->Eigen変数へ格納
mat_label = MatrixXd::Zero(number_of_items, 1);
unsigned char *temp = new unsigned char[number_of_items];
ifs.read((char*)temp, number_of_items);
for (int i = 0; i < number_of_items; i++) {
mat_label(i, 0) = (double)temp[i];
}
delete[] temp;
return number_of_items;
}
6. 読み込みデータの確認
テスト作成した関数を使って実際にデータセットを読み込み、Eigen変数に格納されていることを確認してみます。
#include <iostream>
#include <ios>
#include <iomanip>
#include <vector>
#include <fstream>
#include "Eigen\\Core"
using namespace std;
using namespace Eigen;
int main() {
int number = 1;
vector<MatrixXd> eigen_img;
MatrixXd eigen_label;
// データセットのファイルパスを記載
ReadFile_Image("train-images.idx3-ubyte", eigen_img);
ReadFile_Label("train-labels.idx1-ubyte", eigen_label);
cout << "ラベル" << " " << eigen_label(number, 0) << endl;
for (int y = 0; y < 28; y++) {
for (int x = 0; x < 28; x++) {
cout << setfill(' ') <<
right << setw(3) <<
eigen_img[number](y, x);
}
cout << endl;
}
return 0;
}
実行結果は以下の通りです。
ラベルと画像データがEigen変数に格納されていることが確認できました。

以上です。



コメント