はじめにMNISTの手書き数字画像データの仕様を説明した後、ベクトル化したり、画像として表示・保存するJavaのコードを説明します。
MNISTの手書き数字画像データセットとは
ネットに公開されている0から9までの手書き数字画像データセットです。
個々の画像の大きさは28ピクセル四方で、色は255階調のグレースケールです。
機械学習の学習および評価によく利用されています。
データ仕様
MNISTの手書き数字データの仕様について説明します。
訓練データセット
訓練(学習)に用いる0から9までの6万枚の画像データと正解ラベルデータがあります。
訓練用画像データ
train-images-idx3-ubyte.gz
訓練用画像データが独自のバイナリフォーマットで格納されています。
オフセット | データ型 | 値 | 説明 |
---|---|---|---|
0 | 32bit整数 | 2051 | マジックナンバー(MSB First) |
4 | 32bit整数 | 60000 | 画像数 |
8 | 32bit整数 | 28 | 画像の縦のピクセル数 |
12 | 32bit整数 | 28 | 画像の横のピクセル数 |
16 | 符号なしバイト | 0から255 | 1枚目の画像の1行1列目のピクセルのグレースケール値 |
17 | 符号なしバイト | 0から255 | 1枚目の画像の1行2列目のピクセルのグレースケール値 |
・・・ | 符号なしバイト | 0から255 | 6万枚目の画像の28行目28列目のピクセルのグレースケール値 |
訓練用正解ラベルデータ
train-labels-idx1-ubyte.gz
訓練データの正解ラベルが独自のバイナリフォーマットで格納されています。
オフセット | データ型 | 値 | 説明 |
---|---|---|---|
0 | 32bit整数 | 2049 | マジックナンバー(MSB First) |
4 | 32bit整数 | 60000 | 画像数 |
8 | 符号なしバイト | 0から9 | 1枚目の画像の正解ラベル |
9 | 符号なしバイト | 0から9 | 2枚目の画像の正解ラベル |
・・・ | 符号なしバイト | 0から9 | 6万枚目の画像の正解ラベル |
テストデータセット
テスト(評価)に用いる0から9までの1万枚の画像データと正解ラベルデータがあります。
テスト用画像データ
t10k-images-idx3-ubyte.gz
テスト用画像データが独自のバイナリフォーマットで格納されています。
オフセット | データ型 | 値 | 説明 |
---|---|---|---|
0 | 32bit整数 | 2051 | マジックナンバー(MSB First) |
4 | 32bit整数 | 10000 | 画像数 |
8 | 32bit整数 | 28 | 画像の縦のピクセル数 |
12 | 32bit整数 | 28 | 画像の横のピクセル数 |
16 | 符号なしバイト | 0から255 | 1枚目の画像の1行1列目のピクセルのグレースケール値 |
17 | 符号なしバイト | 0から255 | 1枚目の画像の1行2列目のピクセルのグレースケール値 |
・・・ | 符号なしバイト | 0から255 | 1万枚目の画像の28行目28列目のピクセルのグレースケール値 |
テスト用正解ラベル
t10k-labels-idx1-ubyte.gz
テストデータの正解ラベルが独自のバイナリフォーマットで格納されています。
オフセット | データ型 | 値 | 説明 |
---|---|---|---|
0 | 32bit整数 | 2049 | マジックナンバー(MSB First) |
4 | 32bit整数 | 10000 | 画像数 |
8 | 符号なしバイト | 0から9 | 1枚目の画像の正解ラベル |
9 | 符号なしバイト | 0から9 | 2枚目の画像の正解ラベル |
・・・ | 符号なしバイト | 0から9 | 1万枚目の画像の正解ラベル |
Javaによるベクトル化、画像化
GitHub上のソースコード
簡単な使い方はREADME.mdをご覧ください。
画像データの読み込み
DataInputStreamを使ってデータを読み込みます。
readIntでマジックナンバー、画像数、画像の縦のピクセル数、画像の横のピクセル数を読み込みます。
次元数は28*28=784になります。
double型の2次元配列featuresにreadUnsignedByteに画像データを読み込みます。
1次元目が画像のインデックスで、2次元目が次元のインデックスです。
機械学習での利用を考慮して、値を255.0で割って正規化(normalize)しています。
private void loadFeatures() throws IOException {
System.out.println("Loading feature data from " + fileName + " ...");
DataInputStream is = new DataInputStream(new GZIPInputStream(new FileInputStream(Const.BASE_PATH + fileName)));
is.readInt();
numImages = is.readInt();
numDimensions = is.readInt() * is.readInt();
features = new double[numImages][numDimensions];
for (int i = 0; i < numImages; i++) {
for (int j = 0; j < numDimensions; j++) {
features[i][j] = (double) is.readUnsignedByte() / 255.0;
}
}
}
ラベルデータの読み込み
DataInputStreamを使ってデータを読み込みます。
readIntでマジックナンバー、画像数を読み込みます。
int型の配列labelsにreadUnsignedByteで正解ラベルを読み込みます。
private void loadLabels() throws IOException {
System.out.println("Loading label data from " + fileName + " ...");
DataInputStream is = new DataInputStream(new GZIPInputStream(new FileInputStream(Const.BASE_PATH + fileName)));
is.readInt();
numLabels = is.readInt();
labels = new int[numLabels];
for (int i = 0; i < numLabels; i++) {
labels[i] = is.readUnsignedByte();
}
}
画像の表示
テキストで表示
引数で画像のインデックスを指定して、コンソールに画像の概形をテキストで表示します。
public void showImageAsText(int index) {
System.out.println("Label: " + labels[index]);
for (int i = 0; i < 28; i++) {
for (int j = 0; j < 28; j++) {
double value = images[index][i * 28 + j];
if (value > 0.0) {
System.out.print("**");
} else {
System.out.print(" ");
}
}
System.out.println();
}
}
BufferedImageを作成
ベクトル化された画像データを復元して、BufferedImageを作成します。
値は正規化されているので、255.0を掛けて元のグレースケール値に戻します。
private BufferedImage makeImage(int index) {
BufferedImage image =
new BufferedImage(28, 28, BufferedImage.TYPE_INT_RGB);
for (int i = 0; i < 28; i++) {
for (int j = 0; j < 28; j++) {
int value = (int) (images[index][i * 28 + j] * 255.0);
image.setRGB(j, i, 0xff000000 | value << 16 | value << 8 | value);
}
}
return image;
}
ダイアログに表示
makeImageで読み込んだBufferedImageをダイアログに表示します。
public void showImage(int index) {
BufferedImage image = makeImage(index);
Icon icon = new ImageIcon(image);
JOptionPane.showMessageDialog(null, labels[index], "MnistImageViewer", JOptionPane.PLAIN_MESSAGE, icon);
}
画像ファイルに保存
makeImageで読み込んだBufferedImageをgifファイルに保存します。
public void saveImage(String dir, String prefix, int index) throws IOException {
BufferedImage image = makeImage(index);
File file = new File(dir + "/" + prefix + "_" + String.format("%05d", index) + "_" + labels[index] + ".gif");
if (file.exists()) file.delete();
ImageIO.write(image, "gif", file);
}