chainerのデータセットの作り方 LinearやCNN

#chainerでのデータセットの作り方
chainerのmnistのサンプルを見てみても、重用なデータセットの作り方がよくわかりません。

```python:train_mnist.py
    ...
    # Load the MNIST dataset
    train, test = chainer.datasets.get_mnist()
    ...
```

どっかから取ってきてるんだろうな−しかわからないこのコードで作られる、このtrain, testの作り方です。

普通にLinearだけを使う場合と、CNNを使う場合とで作り方が違うので、それぞれ書きます。
データは普通の画像を使って、そのラベルは適当に設定します。

chainerでは _train_,_test_ が必要ですが、どんな形をしているかというと

```py3
train, test = chainer.datasets.get_mnist()
print(train[0])
```

```bash
python train_minst.py
(array([ 0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
         0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
         0.71764708,  0.99215692,  0.99215692,  0.81176478,  0.00784314,

... 

         0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
         0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
         0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
         0.        ,  0.        ,  0.        ,  0.        ], dtype=float32), 5)
```
の様に、(array([0,0,...,0,0], dtype=float32), 5)
という形をしています。
訓練データの方はfloat32型、ラベルはint32型です。


#準備
どちらの場合でも _tuple\_dataset_ という関数を使うのと、CNNでは画像とglobというのも使うので

```py3
from chainer.datasets import tuple_dataset
from PIL import Image
import numpy as np
import glob
```
が必要です。

また、今回はデータセットの作り方ということで、chainerのモデルの作り方・意味はなんとなくでもわかっていないと難しいと思います。

#Linear(普通)の場合のデータセットの作り方
今回は例として、画素値が一列で保存され、その最後にラベルが書かれている行を一つの学習データとして、学習データの数だけ行数のあるcsvの行列データを使います。

例えば、あるデータが
ラベルが1で5x5の画像データであったとして
120,120,120,120,120
130,130,130,130,130
140,140,140,140,140
150,150,150,150,150
160,160,160,160,160
だとしたら
120,120,120,120,120,130,130,...150,150,160,160,160,160,160,1
のように、一つの学習データが一行として書かかれます。
data.txtの最後の1,5,3,5がラベルです。

```text:data.txt
...
0,0,11,1,388,484,236,268,500,260,212,392,324,220,216,412,204,244,252,292,4,447,403,589,471,434,448,450,430,410,4,434,448,450,430,410,410,410,410,410,1
0,30,11,0,308,368,324,264,372,384,276,216,372,248,212,192,260,204,208,192,4,434,448,450,430,410,410,410,410,410,4,560,220,238,217,305,267,231,202,185,5
0,0,30,1,216,264,268,236,248,272,244,216,284,236,232,180,280,236,188,188,4,560,220,238,217,305,267,231,202,185,4,305,267,231,202,185,185,185,185,185,3
0,30,5,0,220,192,188,188,184,196,204,184,208,188,188,168,204,200,192,160,4,305,267,231,202,185,185,185,185,185,4,418,418,418,418,418,418,418,418,418,5
...
```

具体的なコードです。


```py3:load_dataset.py
train_data = []
train_label = []
data_raw = open("data.txt")
for line in data_raw:
    train = np.array([np.float32(int(x)/255.0) for x in line.split(",")[0:input_num]])
    label = np.int32(line.split(",")[input_num])
    train_data.append(train)
    train_label.append(label)

threshold = np.int32(len(imageData)/10*9)
train = tuple_dataset.TupleDataset(imageData[0:threshold], labelData[0:threshold])
test  = tuple_dataset.TupleDataset(imageData[threshold:],  labelData[threshold:])

```
forの中で一つ一つのデータとラベルを作って次々にtrain_dataに追加しています。
そして、一回すべてのデータを入れてから、threshold(閾値)を使って _train_, _test_ とに分けています。
画素値は0~255なので255で割って0~1に正規化をしています。


#CNNの場合のデータセットの作り方
CNNの入力チャンネルは3でカラー画像を使ったデータセットの作り方になります。

```py3
pathsAndLabels = []
pathsAndLabels.append(np.asarray(["./imageDirectory0/", 0]))
pathsAndLabels.append(np.asarray(["./imageDirectory1/", 1]))
pathsAndLabels.append(np.asarray(["./imageDirectory2/", 2]))

# データを混ぜて、trainとtestがちゃんとまばらになるように。
allData = []
for pathAndLabel in pathsAndLabels:
    path = pathAndLabel[0]
    label = pathAndLabel[1]
    imagelist = glob.glob(path + "*")
    for imgName in imagelist:
        allData.append([imgName, label])
allData = np.random.permutation(allData)

imageData = []
labelData = []
for pathAndLabel in allData:
    img = Image.open(pathAndLabel[0])
    #3チャンネルの画像をr,g,bそれぞれの画像に分ける
    r,g,b = img.split()
    rImgData = np.asarray(np.float32(r)/255.0)
    gImgData = np.asarray(np.float32(g)/255.0)
    bImgData = np.asarray(np.float32(b)/255.0)
    imgData = np.asarray([rImgData, gImgData, bImgData])
    imageData.append(imgData)
    labelData.append(np.int32(pathAndLabel[1]))

threshold = np.int32(len(imageData)/8*7)
train = tuple_dataset.TupleDataset(imageData[0:threshold], labelData[0:threshold])
test  = tuple_dataset.TupleDataset(imageData[threshold:],  labelData[threshold:])
```
allData = []の箇所はについてでは
ある人の画像が入っているフォルダごとに読み込んでいるので、 _train_,_test_ とを作るときに、_test_ に同じラベルのデータしか入らなくなってしまうのでシャッフルしています。

pathsAndLabelsには同じラベルで学習させたい画像データがあるディレクトリとそのラベルを入れます。

ポイントは入力用の画像データを H(高さ)xW(幅)xK(深) から KxHxWにするところです。
pillowのImage.open(path)で読み込んだ画像は前者の行列の形になっているのですが
CNNでは後者の形にしないと入らないので、変えてあげます。

```bash
# ここが、上記したポイント部分
# Image.openで取り入れた画像のデータは
# [ [ [r,g,b], [r,g,b], ... ,[r,g,b] ],
#   [ [r,g,b], [r,g,b], ... ,[r,g,b] ],
#   ...
#   [ [r,g,b], [r,g,b], ... ,[r,g,b] ] ]
# のようにrgbがおなじ位置に入ってる感じなのですが、これをそれぞれ赤の画像、緑の画像、青の画像の様にして行列にしないといけません。
# 以下のようなデータにする
# [ [ [ r, r, r, ... ,r ],      ここから
#     ...
#     [ r, r, r, ... ,r ] ],        ここまでが赤の画像
#
#   [ [ g, g, g, ... ,g ],      ここから
#     ...
#     [ g, g, g, ... ,g ] ],        ここまでが緑の画像
#
#     [ b, b, b, ... ,b ],      ここから
#     ...
#     [ b, b, b, ... ,b ] ] ]       ここまでが青の画像
```

実際に使っているコードは [Github](https://github.com/tommyfms2/face_prediction/blob/master/image2TrainAndTest.py)にあげています。