{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# MNIST手書き文字\n",
"\n",
"生き物の模型として*drosophila*が生物学の各界で重宝されるように、機械学習の世界では、識別能力を試すときに使う実データとして、MNISTの手書き文字データがもっとも有名であろう。また、提供されているデータが二値(バイナリ)データなので、それを扱うことも良い練習である。本当にゼロとイチでの表現なので、読み書きの際は一定の注意が必要であるが、何も難しいことはない。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"__目次__\n",
"\n",
"- データセットの概要\n",
"\n",
"- 入力パターンを調べる\n",
"\n",
"- ラベルを調べる\n",
"\n",
"- 作業用のデータを整える\n",
"\n",
"___"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"## データセットの概要\n",
"\n",
"このデータセットの略称のMNISTは、米国の(N)ational (I)nstitute of (S)tandards and (T)echnologyがもともと管理していた大きな手書き文字データベースから一部を抜粋、調整した(m)odified subsetであることからきている。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"ダウンロード方法はいくつもあるが、Y. LeCun氏のサイト( http://yann.lecun.com/exdb/mnist/ )から入手することが多い。彼らの説明には、\n",
"\n",
"> *\"The MNIST database of handwritten digits, available from this page, has a training set of 60,000 examples, and a test set of 10,000 examples. It is a subset of a larger set available from NIST. The digits have been size-normalized and centered in a fixed-size image\"*"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"とあり、その中身を収容するファイルが下記の4つである。\n",
"\n",
"> train-images-idx3-ubyte.gz: training set images (9912422 bytes)\n",
"> train-labels-idx1-ubyte.gz: training set labels (28881 bytes)\n",
"> t10k-images-idx3-ubyte.gz: test set images (1648877 bytes)\n",
"> t10k-labels-idx1-ubyte.gz: test set labels (4542 bytes)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"いずれも__IDX__という二値形式で保存されている。まずは圧縮されたファイルを解凍する。\n",
"```\n",
"$ cd data/MNIST\n",
"$ gunzip train-images-idx3-ubyte.gz\n",
"$ gunzip train-labels-idx1-ubyte.gz\n",
"$ gunzip t10k-images-idx3-ubyte.gz\n",
"$ gunzip t10k-labels-idx1-ubyte.gz\n",
"```\n",
"残るのは肝心のバイナリデータだけだ。\n",
"___"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"## 入力パターンを調べる\n",
"\n",
"まずは、訓練データのファイルを開くことにしよう。"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<_io.BufferedReader name='data/mnist/train-images-idx3-ubyte'>\n"
]
}
],
"source": [
"\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"\n",
"toread = \"data/mnist/train-images-idx3-ubyte\"\n",
"\n",
"f_bin = open(toread, mode=\"rb\")\n",
"\n",
"print(f_bin)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"ここで、正しく読み込めているかどうか確かめる必要が出てくる。その唯一の方法は、データの提供者が「あるはずだ」という内容と照合することである。\n",
"LeCunらの説明より:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```\n",
"TRAINING SET IMAGE FILE (train-images-idx3-ubyte):\n",
"[offset] [type] [value] [description]\n",
"0000 32 bit integer 0x00000803(2051) magic number\n",
"0004 32 bit integer 60000 number of images\n",
"0008 32 bit integer 28 number of rows\n",
"0012 32 bit integer 28 number of columns\n",
"0016 unsigned byte ?? pixel\n",
"0017 unsigned byte ?? pixel\n",
"........\n",
"xxxx unsigned byte ?? pixel\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"ここでいう「offset」とは、ファイル開始点から何バイト読み進んだか示す数値である。ゼロならば一バイトも読んでおらず、1バイト目を目前にする地点である。offsetが0004であれば5バイト目、0008であれば9バイト目など、以下同様。期待どおりの中身が読み込めているかチェックしてみよう。"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"First four bytes:\n",
"bytes: b'\\x00\\x00\\x08\\x03'\n",
" int: 2051\n"
]
}
],
"source": [
"print(\"First four bytes:\") # should be magic number, 2051.\n",
"b = f_bin.read(4)\n",
"print(\"bytes: \", b)\n",
"print(\" int: \", int.from_bytes(b, byteorder=\"big\", signed=False))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"ここに出てきたPythonのバイトデータ`b'\\x00\\x00\\x08\\x03'`は、最初の4バイトを十六進法(hexadecimal)の表記で示したものである。上記の表の一行目の「value」、つまり``0x00000803``と一致しているので、一安心。これらの``\\x``はただ単にバイトの区切りを表している。思い出してみると、十六進法の2桁を使うと、$0, 1, 2, \\ldots$から$(15 \\times 16^{1} + 15 \\times 16^{0}) = 255$までの整数を表現することができる。二進法の8桁、つまり8ビット(=1バイト)と同じである。さて、十進法に変換して$3 \\times 16^{0} + 8 \\times 16^{2} = 2051$が得られるので、期待どおりの「magic number」である。\n",
"\n",
"次は、`read`というメソッドを使い、4バイトずつ読み進めるようにして、残りの内容を確認していく。"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Second four bytes:\n",
"bytes: b'\\x00\\x00\\xea`'\n",
" int: 60000\n"
]
}
],
"source": [
"\n",
"print(\"Second four bytes:\") # should be number of imgs = 60000\n",
"b = f_bin.read(4)\n",
"print(\"bytes: \", b)\n",
"print(\" int: \", int.from_bytes(b, byteorder=\"big\", signed=False))\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Third four bytes:\n",
"bytes: b'\\x00\\x00\\x00\\x1c'\n",
" int: 28\n"
]
}
],
"source": [
"\n",
"print(\"Third four bytes:\") # should be number of rows = 28\n",
"b = f_bin.read(4)\n",
"print(\"bytes: \", b)\n",
"print(\" int: \", int.from_bytes(b, byteorder=\"big\", signed=False))\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Fourth four bytes:\n",
"bytes: b'\\x00\\x00\\x00\\x1c'\n",
" int: 28\n"
]
}
],
"source": [
"\n",
"print(\"Fourth four bytes:\") # should be number of cols = 28\n",
"b = f_bin.read(4)\n",
"print(\"bytes: \", b)\n",
"print(\" int: \", int.from_bytes(b, byteorder=\"big\", signed=False))\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"これでもっとも重要なファイル情報を無事に入手できた。これを使って、肝心の入力パターンを取り出していくことにしよう。幸いにも、デジタル画像の画素値なので、中身のもっともらしさは目視でほぼ確認できる。"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"n = 60000 # (anticipated) number of images.\n",
"d = 28*28 # number of entries (int values) per image.\n",
"times_todo = 5 # number of images to view.\n",
"bytes_left = d\n",
"data_x = np.zeros((d,), dtype=np.uint8) # initialize."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"データの提供者の説明より:\n",
"\n",
"> *\"Pixels are organized row-wise. Pixel values are 0 to 255. 0 means background (white), 255 means foreground (black).\"*\n",
"\n",
"つまり、画素値が0〜255の整数値を取り、2次元画像の一行ずつ読んでいくことになる。値の範囲がわかっているので、`uint8`(無符号の1バイト整数)のデータ型とする。まだ読んでいない中身として、\n",
"```\n",
"0016 unsigned byte ?? pixel\n",
"0017 unsigned byte ?? pixel\n",
"........\n",
"xxxx unsigned byte ?? pixel\n",
"```\n",
"と1バイトずつファイルの最後まで読んでいくと、ちょうど60000枚の画像の全画素を読み込むことになるはずである。1枚につき画素数が$28 \\times 28 = 784$である。"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"scrolled": false
},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"