中身を理解！Chainerにおけるデータの準備・設定方法１　〜　MNISTデータをコピーしてみる（バイナリデータの操作）　〜

#Chainerにおけるデータの準備・設定方法について書いてみます。

MNISTデータを学習するChainerのプログラムの説明はよくあるのですが、データの準備・設定方法については「train, test = datasets.get_mnist(ndim = 3)」の1行で終わっていてブラックボックスとなっていてよく分からないため、この記事を書いています。MNISTデータを学習するChainerのプログラム自身は理解していることを前提としています。以下の内容からなる全3回になっています。

中身を理解！Chainerにおけるデータの準備・設定方法１
〜　MNISTデータをコピーしてみる（バイナリデータの操作）　〜
中身を理解！Chainerにおけるデータの準備・設定方法２
〜　JPEGファイルからMNISTと同じ仕様のイメージデータファイルを作成してみる　〜
中身を理解！Chainerにおけるデータの準備・設定方法３
〜　chainer_datasets.get_mnist()と同等機能の関数を作成してみる　〜

#第1回では、バイナリデータの操作も含めて、MNISTデータをコピーするプログラムについて説明します。

Importは、以下の通りです。

```
import numpy as np
import chainer
from chainer import cuda, Function, report, training, utils, Variable
from chainer import datasets, iterators, optimizers, serializers
from chainer import Link, Chain, ChainList
import chainer.functions as F
import chainer.links as L
```

verboseを1にすると、内容を確認できるprintが表示されます。

```
verbose = 1
```

イメージデータファイルのパス名をimagepathに、ファイル名をimagefileに設定します。ここでは、コピー元ファイルとしてMNISTデータファイルのtrain-images-idx3-ubyteと後述のラベルデータファイルのtrain-labels-idx1-ubyteを事前にダウンロードし、フォルダtmpの下に置いておきます。MNISTデータセットは、http://yann.lecun.com/exdb/mnist/ からダウンロードします。

```
imagepath = 'tmp/'
imagefile = 'train-images-idx3-ubyte'
copyimagefile = 'copyimage'
imagepathfile = imagepath + imagefile
copyimagepathfile = imagepath + copyimagefile
```

同様に、ラベルデータファイルのパス名をlabelpathに、ファイル名をlabelfileに設定します。

```
labelpath = 'tmp/'
labelfile = 'train-labels-idx1-ubyte'
copylabelfile = 'copylabel'
labelpathfile = labelpath + labelfile
copylabelpathfile = labelpath + copylabelfile
```

コピー元イメージデータファイルimagepathfileは、バイナリモード・READモードでオープンします（’rb’）。コピー先イメージデータファイルcopyimagepathfileは、バイナリモード・WRITEモードでオープンします（’wb’）。

```
#
#copy image dataset
#
print('copy image dataset start')
imagef = open(imagepathfile, 'rb')
copyimagef = open(copyimagepathfile, 'wb')
```

コピー元イメージデータファイルの先頭に入っている識別子、データ数、1画像当たりのデータ行数、1画像当たりのデータ列数を読み込みます。仕様の詳細は、http://yann.lecun.com/exdb/mnist/ に書かれています。バイナリデータなのでread(4)で4バイトをREADし、それをint.from_bytes()でINTに変換します。エンディアン（byteorder）に関しては、他の資料を参照して下さい。ここでは、ビッグエンディアンとして扱っています。

```
bdataid = imagef.read(4)
bdatanum = imagef.read(4)
blinenum = imagef.read(4)
brownum = imagef.read(4)
if (verbose): print('bdataid = ', bdataid)
if (verbose): print('bdatanum = ', bdatanum)
if (verbose): print('blinenum = ', blinenum)
if (verbose): print('brownum = ', brownum)

dataid = int.from_bytes(bdataid, byteorder='big')
datanum = int.from_bytes(bdatanum, byteorder='big')
linenum = int.from_bytes(blinenum, byteorder='big')
rownum = int.from_bytes(brownum, byteorder='big')
if (verbose): print('dataid = ', dataid)
if (verbose): print('datanum = ', datanum)
if (verbose): print('linenum = ', linenum)
if (verbose): print('rownum = ', rownum)
```

コピー先イメージデータファイルもバイナリモードでオープンしているので、INTのデータ（dataid、datanum、linenum、rownum）をto_bytes(4, byteorder='big')で4バイトのデータに変換します。この処理は実際には不要で、最初にコピー元のイメージデータファイルから読み込んだ4バイトのデータをそのまま使えばいいのですが、バイト型からINT型への変換であるfrom_bytes()に対して、INT型からバイト型への変換であるto_bytes()の操作を示すためにあえて書いています。

```
cbdataid = dataid.to_bytes(4, byteorder='big')
cbdatanum = datanum.to_bytes(4, byteorder='big')
cblinenum = linenum.to_bytes(4, byteorder='big')
cbrownum = rownum.to_bytes(4, byteorder='big')
if (verbose): print('cbdataid = ', cbdataid)
if (verbose): print('cbdatanum = ', cbdatanum)
if (verbose): print('cblinenum = ', cblinenum)
if (verbose): print('cbrownum = ', cbrownum)
```

コピー先イメージデータファイルの先頭に識別子、データ数、1画像当たりのデータ行数、1画像当たりのデータ列数を書き込みます。

```
copyimagef.write(cbdataid)
copyimagef.write(cbdatanum)
copyimagef.write(cblinenum)
copyimagef.write(cbrownum)
```

1イメージデータは、データ行数（linenum）×データ列数（rownum）で、それがdatanum個あります。それを1バイトづつ読み出し（read(1)）、1バイトづつ書き出し（write(1)）ます。1バイトづつ読み出し、書き出しを行うのは効率が悪いのですが、分り易いように単純に書いています。

```
for i in range(datanum * linenum * rownum):
    pixel = imagef.read(1)
    copyimagef.write(pixel)
```

全てのデータをコピー元イメージデータファイルから読み出し、コピー先イメージデータファイルに書き出したので、両ファイルともクローズします。

```    
imagef.close()
copyimagef.close()
```

ラベルデータファイルのコピーも基本的に同じです。コピー元ラベルデータファイルlabelpathfileは、バイナリモード・READモードでオープンします（’rb’）。コピー先ラベルデータファイルcopylabelpathfileは、バイナリモード・WRITEモードでオープンします（’wb’）。

```
#
#copy label dataset
#
print('copy label dataset start')
labelf = open(labelpathfile, 'rb')
copylabelf = open(copylabelpathfile, 'wb')
```

コピー元ラベルデータファイルの先頭に入っている識別子、データ数を読み込みます。ラベルデータファイルには、1画像当たりのデータ行数、1画像当たりのデータ列数は入っていません。

```
bdataid = labelf.read(4)
bdatanum = labelf.read(4)
if (verbose): print('bdataid = ', bdataid)
if (verbose): print('bdatanum = ', bdatanum)

dataid = int.from_bytes(bdataid, byteorder='big')
datanum = int.from_bytes(bdatanum, byteorder='big')
if (verbose): print('dataid = ', dataid)
if (verbose): print('datanum = ', datanum)
```

コピー先ラベルデータファイルもバイナリモードでオープンしているので、INTのデータ（dataid、datanum）をto_bytes(4, byteorder='big')で4バイトのデータに変換します。この処理も実際には不要ですが、バイト型からINT型への変換であるfrom_bytes()に対して、INT型からバイト型への変換であるto_bytes()の操作を示すためにあえて書いています。

```
cbdataid = dataid.to_bytes(4, byteorder='big')
cbdatanum = datanum.to_bytes(4, byteorder='big')
if (verbose): print('cbdataid = ', cbdataid)
if (verbose): print('cbdatanum = ', cbdatanum)
```

コピー先ラベルデータファイルの先頭に識別子、データ数を書き込みます。

```
copylabelf.write(cbdataid)
copylabelf.write(cbdatanum)
```

1ラベルデータは、1バイトです。ラベルデータを1バイトづつ読み出し（read(1)）、1バイトづつ書き出し（write(1)）ます。先程同様、1バイトづつ読み出し、書き出しを行うのは効率が悪いのですが、分り易いように単純に書いています。

```
for i in range(datanum):
    label = labelf.read(1)
    copylabelf.write(label)
```

全てのデータをコピー元ラベルデータファイルから読み出し、コピー先ラベルデータファイルに書き出したので、両ファイルともクローズします。

```
labelf.close()
copylabelf.close()

print('copy complete')
```

コピーが上手くいったかどうかは、diffコマンドを使って確認します。

```
Diff train-images-idx3-ubyte copyimage
Diff train-labels-idx1-ubyte copylable
```

# リスト

```copy_mnist_dataset.py
import numpy as np
import chainer
from chainer import cuda, Function, report, training, utils, Variable
from chainer import datasets, iterators, optimizers, serializers
from chainer import Link, Chain, ChainList
import chainer.functions as F
import chainer.links as L

verbose = 1

imagepath = 'tmp/'
imagefile = 'train-images-idx3-ubyte'
copyimagefile = 'copyimage'
imagepathfile = imagepath + imagefile
copyimagepathfile = imagepath + copyimagefile

labelpath = 'tmp/'
labelfile = 'train-labels-idx1-ubyte'
copylabelfile = 'copylabel'
labelpathfile = labelpath + labelfile
copylabelpathfile = labelpath + copylabelfile

#
#copy image dataset
#
print('copy image dataset start')
imagef = open(imagepathfile, 'rb')
copyimagef = open(copyimagepathfile, 'wb')

bdataid = imagef.read(4)
bdatanum = imagef.read(4)
blinenum = imagef.read(4)
brownum = imagef.read(4)
if (verbose): print('bdataid = ', bdataid)
if (verbose): print('bdatanum = ', bdatanum)
if (verbose): print('blinenum = ', blinenum)
if (verbose): print('brownum = ', brownum)

dataid = int.from_bytes(bdataid, byteorder='big')
datanum = int.from_bytes(bdatanum, byteorder='big')
linenum = int.from_bytes(blinenum, byteorder='big')
rownum = int.from_bytes(brownum, byteorder='big')
if (verbose): print('dataid = ', dataid)
if (verbose): print('datanum = ', datanum)
if (verbose): print('linenum = ', linenum)
if (verbose): print('rownum = ', rownum)

cbdataid = dataid.to_bytes(4, byteorder='big')
cbdatanum = datanum.to_bytes(4, byteorder='big')
cblinenum = linenum.to_bytes(4, byteorder='big')
cbrownum = rownum.to_bytes(4, byteorder='big')
if (verbose): print('cbdataid = ', cbdataid)
if (verbose): print('cbdatanum = ', cbdatanum)
if (verbose): print('cblinenum = ', cblinenum)
if (verbose): print('cbrownum = ', cbrownum)

copyimagef.write(cbdataid)
copyimagef.write(cbdatanum)
copyimagef.write(cblinenum)
copyimagef.write(cbrownum)

for i in range(datanum * linenum * rownum):
    pixel = imagef.read(1)
    copyimagef.write(pixel)
    
imagef.close()
copyimagef.close()

#
#copy label dataset
#
print('copy label dataset start')
labelf = open(labelpathfile, 'rb')
copylabelf = open(copylabelpathfile, 'wb')

bdataid = labelf.read(4)
bdatanum = labelf.read(4)
if (verbose): print('bdataid = ', bdataid)
if (verbose): print('bdatanum = ', bdatanum)

dataid = int.from_bytes(bdataid, byteorder='big')
datanum = int.from_bytes(bdatanum, byteorder='big')
if (verbose): print('dataid = ', dataid)
if (verbose): print('datanum = ', datanum)

cbdataid = dataid.to_bytes(4, byteorder='big')
cbdatanum = datanum.to_bytes(4, byteorder='big')
if (verbose): print('cbdataid = ', cbdataid)
if (verbose): print('cbdatanum = ', cbdatanum)

copylabelf.write(cbdataid)
copylabelf.write(cbdatanum)

for i in range(datanum):
    label = labelf.read(1)
    copylabelf.write(label)
    
labelf.close()
copylabelf.close()

print('copy complete')
```