Help us understand the problem. What is going on with this article?

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

More than 3 years have 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にあげています。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away