Chainer
CNN

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

More than 1 year has passed since last update.


chainerでのデータセットの作り方

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


train_mnist.py

    ...

# Load the MNIST dataset
train, test = chainer.datasets.get_mnist()
...

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

普通にLinearだけを使う場合と、CNNを使う場合とで作り方が違うので、それぞれ書きます。

データは普通の画像を使って、そのラベルは適当に設定します。

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

train, test = chainer.datasets.get_mnist()

print(train[0])

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というのも使うので

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がラベルです。


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
...

具体的なコードです。


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でカラー画像を使ったデータセットの作り方になります。

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では後者の形にしないと入らないので、変えてあげます。

# ここが、上記したポイント部分

# 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にあげています。