スポンサーリンク

【C++】Mnistデータセットを読み込みEigenで扱う

Mnistの読み込み

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変数に格納されていることが確認できました。

以上です。

コメント

タイトルとURLをコピーしました