LoginSignup
5
6

More than 3 years have passed since last update.

畳み込みニューラルネット Part3 [TensorFlowでDeep Learning 6]

Last updated at Posted at 2016-05-28

(目次はこちら)
(Tensorflow 2.0版: https://qiita.com/kumonkumon/items/14f7e8f60e942b65f7da)

はじめに

前回の記事では、単純な畳み込みニューラルネットに全結合層を追加して、ちょっとディープニューラルネットっぽくした。次回の記事では、これをまたさらに少し拡張してみようかと。

拡張:プーリング層(pooling layer)の追加

人間って、たとえ文字が回転していても、多少歪んでいても、それを何も意識せずに読むことができるが、それを計算機で実現しようとすると割りと大変。
これまでに書いた内容では、学習によって求められるパラメータ(ウェイト)は特定の位置専用のものとなっていて、データが少し移動してしまうだけで、うまく動かなくなってしまう。MNISTは比較的きれいなデータなので、まぁそこまで識別率が下がるわけではないけど。

で、この位置の変化についてロバストにしてくれる層が、プーリング層。この呼び方の由来は、一旦、特徴なりをプール(pool)しておいて、そこから代表的なものを選択or算出するので、プーリング(pooling)と呼ばれている。

このプーリング層で何をやるのかというと、データのサンプリング。以上。
画像データの場合だと、画像を縮小するという作業。なので、非常にシンプルな処理。

追加するとこんな感じ。
mnist_fixed_cnn_pl

次元が、28 x 28 x 2から14 x 14 x 2に変わっているのがわかる。実際の処理は、MaxPoolってやつで、2 x 2の領域から、値が最大のものを代表値としてサンプリングしている。なので、情報量というか次元が1/4になっている。最大値の他にも、平均にしたり、ちょっと複雑にしてみたりといろいろある。

なぜ、こんな処理でうまくいくのかというと、画像は小さいほうが移動が目立たないからという単純な理由。

プーリングは単純だけど、これだけで論文になるほど奥が深い。
http://cs.nyu.edu/~ylan/files/publi/boureau-icml-10.pdf

Y-Lan Boureau, Jean Ponce, and Yann LeCun, 
A theoretical analysis of feature pooling in vision algorithms,
Proc. International Conference on Machine learning (ICML'10), 2010

コード

Python: 3.6.8, Tensorflow: 1.13.1で動作確認済み
(もともと2016年前半に書いたものなので、順次更新しています。)

mnist_fixed_cnn_pl.py

mnist_fixed_cnn_pl.py
from helper import *

IMAGE_WIDTH, IMAGE_HEIGHT = 28, 28
CATEGORY_NUM = 10
LEARNING_RATE = 0.1
FILTER_NUM = 2
FEATURE_DIM = 100
TRAINING_LOOP = 20000
BATCH_SIZE = 100
SUMMARY_DIR = 'log_fixed_cnn_fc'
SUMMARY_INTERVAL = 1000
BUFFER_SIZE = 1000
EPS = 1e-10


with tf.Graph().as_default():
    (X_train, y_train), (X_test, y_test) = mnist_samples()
    ds = tf.data.Dataset.from_tensor_slices((X_train, y_train))
    ds = ds.shuffle(BUFFER_SIZE).batch(BATCH_SIZE).repeat(int(TRAINING_LOOP * BATCH_SIZE / X_train.shape[0]) + 1)
    next_batch = ds.make_one_shot_iterator().get_next()

    with tf.name_scope('input'):
        y_ = tf.placeholder(tf.float32, [None, CATEGORY_NUM], name='labels')
        x = tf.placeholder(tf.float32, [None, IMAGE_HEIGHT, IMAGE_WIDTH], name='input_images')

    with tf.name_scope('convolution'):
        W_conv = prewitt_filter()
        x_image = tf.reshape(x, [-1, IMAGE_HEIGHT, IMAGE_WIDTH, 1])
        h_conv = tf.abs(tf.nn.conv2d(x_image, W_conv, strides=[1, 1, 1, 1], padding='SAME'))

    with tf.name_scope('pooling'):
        h_pool = tf.nn.max_pool(h_conv, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

    with tf.name_scope('fully-connected'):
        W_fc = weight_variable([int(IMAGE_HEIGHT / 2 * IMAGE_WIDTH / 2 * FILTER_NUM), FEATURE_DIM], name='weight_fc')
        b_fc = bias_variable([FEATURE_DIM], name='bias_fc')
        h_pool_flat = tf.reshape(h_pool, [-1, int(IMAGE_HEIGHT / 2 * IMAGE_WIDTH / 2 * FILTER_NUM)])
        h_fc = tf.nn.relu(tf.matmul(h_pool_flat, W_fc) + b_fc)

    with tf.name_scope('readout'):
        W = weight_variable([FEATURE_DIM, CATEGORY_NUM], name='weight')
        b = bias_variable([CATEGORY_NUM], name='bias')
        y = tf.nn.softmax(tf.matmul(h_fc, W) + b)

    with tf.name_scope('optimize'):
        y = tf.clip_by_value(y, EPS, 1.0)
        cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), axis=1))
        train_step = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(cross_entropy)
        cross_entropy_summary = tf.summary.scalar('cross entropy', cross_entropy)

    with tf.Session() as sess:
        train_writer = tf.summary.FileWriter(SUMMARY_DIR + '/train', sess.graph)
        test_writer = tf.summary.FileWriter(SUMMARY_DIR + '/test')

        correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
        accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
        accuracy_summary = tf.summary.scalar('accuracy', accuracy)

        sess.run(tf.global_variables_initializer())
        for i in range(TRAINING_LOOP + 1):
            images, labels = sess.run(next_batch)
            sess.run(train_step, {x: images, y_: labels})

            if i % SUMMARY_INTERVAL == 0:
                train_acc, summary = sess.run(
                        [accuracy, tf.summary.merge([cross_entropy_summary, accuracy_summary])],
                        {x: images, y_: labels})
                train_writer.add_summary(summary, i)
                test_acc, summary = sess.run(
                        [accuracy, tf.summary.merge([cross_entropy_summary, accuracy_summary])],
                        {x: X_test, y_: y_test})
                test_writer.add_summary(summary, i)
                print(f'step: {i}, train-acc: {train_acc}, test-acc: {test_acc}')

コードの説明

変更が入った部分を。

プーリング層

追加された層。

with tf.name_scope('pooling'):
    h_pool = tf.nn.max_pool(h_conv, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

全結合層

プーリング層の出力を入力にする変更。それに伴って入力の次元が変わっているのでその変更。

with tf.name_scope('fully-connected'):
    W_fc = weight_variable([int(IMAGE_HEIGHT / 2 * IMAGE_WIDTH / 2 * FILTER_NUM), FEATURE_DIM], name='weight_fc')
    b_fc = bias_variable([FEATURE_DIM], name='bias_fc')
    h_pool_flat = tf.reshape(h_pool, [-1, int(IMAGE_HEIGHT / 2 * IMAGE_WIDTH / 2 * FILTER_NUM)])
    h_fc = tf.nn.relu(tf.matmul(h_pool_flat, W_fc) + b_fc)

となって、変更量は非常に少ない。

結果

テストデータ(青線)での識別率は、98.0%程度。
ようやく、多層パーセプトロンでの結果(97.8%)を超えてきた。
この辺りが、固定フィルタ(しかもPrewitt)の限界だろうか。

image.png

あとがき

今回は、前回の記事で紹介したフィルタを固定した畳み込みニューラルネットワークに、プーリング層を追加してみました。次回の記事では、これをさらに拡張してみます。

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6