そのうちシンギュラリティを起こすblog

強い人工知能を作ってそのうちシンギュラリティを起こします。

ChainerのMNISTをConvolution2Dに改造する

Convolution2Dの使い方がよくわかんない。
よくわかんないのでとりあえず動作しているサンプルを改造して動きを見てみることにした。

#!/usr/bin/env python
"""Chainer example: train a multi-layer perceptron on MNIST

This is a minimal example to write a feed-forward net. It requires scikit-learn
to load MNIST dataset.

"""
import argparse

import numpy as np
import six

import chainer
from chainer import computational_graph as c
from chainer import cuda
import chainer.functions as F
from chainer import optimizers

import data


parser = argparse.ArgumentParser(description='Chainer example: MNIST')
parser.add_argument('--gpu', '-g', default=-1, type=int,
                    help='GPU ID (negative value indicates CPU)')
args = parser.parse_args()
if args.gpu >= 0:
    cuda.check_cuda_available()
xp = cuda.cupy if args.gpu >= 0 else np

batchsize = 100
n_epoch = 20

# Prepare dataset
print('load MNIST dataset')
mnist = data.load_mnist_data()
mnist['data'] = mnist['data'].astype(np.float32)
mnist['data'] /= 255
mnist['data'] = mnist['data'].reshape(70000, 1,28,28)

mnist['target'] = mnist['target'].astype(np.int32)

N = 60000
x_train, x_test = np.split(mnist['data'],   [N])
y_train, y_test = np.split(mnist['target'], [N])
N_test = y_test.size

# Prepare multi-layer perceptron model
model = chainer.FunctionSet(cv1=F.Convolution2D(1,30, 3),
                            bn2 = F.BatchNormalization( 30),
                            ln3=F.Linear(5070, 1000),
                            ln4=F.Linear(1000, 10))


if args.gpu >= 0:
    cuda.get_device(args.gpu).use()
    model.to_gpu()


def forward(x_data, y_data, train=True):
    # Neural net architecture
    x, t = chainer.Variable(x_data), chainer.Variable(y_data)
    h = F.max_pooling_2d(F.dropout(F.relu(model.bn2(model.cv1(x))),  train=train),2)
    h = F.dropout(F.relu(model.ln3(h)), train=train)
    y = model.ln4(h)
    return F.softmax_cross_entropy(y, t), F.accuracy(y, t)


# Setup optimizer
optimizer = optimizers.Adam()
optimizer.setup(model)

# Learning loop
for epoch in six.moves.range(1, n_epoch + 1):
    print('epoch', epoch)

    # training
    perm = np.random.permutation(N)
    sum_accuracy = 0
    sum_loss = 0
    for i in six.moves.range(0, N, batchsize):
        x_batch = xp.asarray(x_train[perm[i:i + batchsize]])
        y_batch = xp.asarray(y_train[perm[i:i + batchsize]])

        optimizer.zero_grads()
        loss, acc = forward(x_batch, y_batch)
        loss.backward()
        optimizer.update()

        if epoch == 1 and i == 0:
            with open("graph.dot", "w") as o:
                o.write(c.build_computational_graph((loss, )).dump())
            with open("graph.wo_split.dot", "w") as o:
                g = c.build_computational_graph((loss, ),
                                                remove_split=True)
                o.write(g.dump())
            print('graph generated')

        sum_loss += float(loss.data) * len(y_batch)
        sum_accuracy += float(acc.data) * len(y_batch)

    print('train mean loss={}, accuracy={}'.format(
        sum_loss / N, sum_accuracy / N))

    # evaluation
    sum_accuracy = 0
    sum_loss = 0
    for i in six.moves.range(0, N_test, batchsize):
        x_batch = xp.asarray(x_test[i:i + batchsize])
        y_batch = xp.asarray(y_test[i:i + batchsize])

        loss, acc = forward(x_batch, y_batch, train=False)

        sum_loss += float(loss.data) * len(y_batch)
        sum_accuracy += float(acc.data) * len(y_batch)

    print('test  mean loss={}, accuracy={}'.format(
        sum_loss / N_test, sum_accuracy / N_test))

解説

Convolution2Dに渡せるように配列、行列の形を変えます。
サンプル数70000個、チャンネル数1、縦横28*28

mnist['data'] = mnist['data'].reshape(70000, 1,28,28)

ニューラルネットワークに使う部品

# Prepare multi-layer perceptron model
model = chainer.FunctionSet(cv1=F.Convolution2D(1,30, 3),
                            bn2 = F.BatchNormalization( 30),
                            ln3=F.Linear(5070, 1000),
                            ln4=F.Linear(1000, 10))

ニューラルネットワークの定義

def forward(x_data, y_data, train=True):
    # Neural net architecture
    x, t = chainer.Variable(x_data), chainer.Variable(y_data)
    h = F.max_pooling_2d(F.dropout(F.relu(model.bn2(model.cv1(x))),  train=train),2)
    h = F.dropout(F.relu(model.ln3(h)), train=train)
    y = model.ln4(h)
    return F.softmax_cross_entropy(y, t), F.accuracy(y, t)

変わっているのはこの辺だけです。

Convolution2DからLinearにつなげる時にサイズがわからないけど、とりあえず実行してみて

chainer.utils.type_check.InvalidType: Expect: prod(in_types[0].shape[1:]) == W.shape[1]
Actual: 1690 != 6760

エラーを見て数値を変更すればおk。