LoginSignup
8
9

More than 3 years have passed since last update.

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

Last updated at Posted at 2016-05-26

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

はじめに

前回の記事 では、多項ロジスティック回帰を拡張して、多層パーセプトロンにしてみた。
今回は、画像処理分野におけるDeep Learning / 深層学習において外せない、畳み込みニューラルネットワークに足を踏み入れてみようかと。

畳み込みニューラルネットワーク(convolutional neural network: CNN)

非常に単純に言うと、入力画像に対して、エッジ抽出フィルタとか、ぼかしフィルタとかのフィルタをかけたものを、前回の記事までにも出てきた、多項ロジスティック回帰とか多層パーセプトロンの入力としたニューラルネットワーク。
「畳み込み」っていう言葉は個人的には好きではないけど、簡単に言うと、「畳み込み」はフィルタをかける処理。

そういえば、前回の記事で、

画像に限らず、何かのデータを識別するときには、「入力データから特徴抽出して識別器にかける」というのが王道。

今回は、特徴抽出部分を追加してみる。特徴抽出といってもやり方はいろいろあって、

  • エッジ抽出フィルタ
  • SIFT

と書いた。
これは、多項ロジスティック回帰を多層パーセプトロンに拡張するときの説明として書いたものだけど、特徴抽出部分の選択肢としてエッジ抽出フィルタとある。

多項ロジスティック回帰を多層パーセプトロンに拡張するときには、多項ロジスティック回帰の中に、「特徴抽出的な層」としてロジスティック回帰をねじ込んだが、ロジスティック回帰ではなくエッジ抽出フィルタを「特徴抽出的な層」としてねじ込めば畳み込みニューラルネットワークになる。

多項ロジスティック回帰を多層パーセプトロンに拡張した時と同様に、変更量は少ない。

多項ロジスティック回帰は、
softmax

畳み込みニューラルネットワークでは、
cnn_simple
となる。

いわゆる畳み込みニューラルネットワークでは、convolusion layer(畳み込み層)での、フィルタ(重み係数)もパラメータ推定するが、ここでは、単純にエッジ抽出フィルタであるPrewitt filterを使ってみる。

縦方向のエッジ検出フィルタは、これで、

\left(
  \begin{array}{ccc}
    1 & 0 & -1 \\
    1 & 0 & -1 \\
    1 & 0 & -1
  \end{array}
\right)

横方向は、これ、

\left(
  \begin{array}{ccc}
    1 & 1 & 1 \\
    0 & 0 & 0 \\
    -1 & -1 & -1
  \end{array}
\right)

これらを、入力データにかけて、その出力を多項ロジスティック回帰に入れる。縦横の2種類のフィルタを利用するので、多項ロジスティック回帰への入力は、28 x 28ピクセル x 2フィルタで、1568次元となる。上図に小さく、? x 28 x 28 x 2って書かれているやつ。

コード

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

mnist_fixed_cnn_simple.py

mnist_fixed_cnn_simple.py
from helper import *

IMAGE_WIDTH, IMAGE_HEIGHT = 28, 28
CATEGORY_NUM = 10
LEARNING_RATE = 0.1
FILTER_NUM = 2
TRAINING_LOOP = 20000
BATCH_SIZE = 100
SUMMARY_DIR = 'log_fixed_cnn_simple'
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('readout'):
        W = weight_variable([IMAGE_HEIGHT * IMAGE_WIDTH * FILTER_NUM, CATEGORY_NUM], name='weight')
        b = bias_variable([CATEGORY_NUM], name='bias')
        h_conv_flat = tf.reshape(h_conv, [-1, IMAGE_HEIGHT * IMAGE_WIDTH * FILTER_NUM])
        y = tf.nn.softmax(tf.matmul(h_conv_flat, 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})' 

コードの説明

以前の記事で扱った多項ロジスティック回帰と異なる部分を。

畳み込み層

追加された層。今回は、畳み込み層はPrewitt filter固定。Prewitt filterの出力は負の値にもなるので絶対値とっている。絶対値を取らなくても動くが、画像特徴としては絶対値にした方が有効。

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

出力層

入力が畳み込み層の出力となるように変更。
畳み込み層の出力の次元は、フィルタが2つなので画像サイズの2倍になっている。
畳み込み層の出力はreshape()でフラットにして、線形変換&ソフトマックス関数へ。

with tf.name_scope('readout'):
    W = weight_variable([IMAGE_WIDTH * IMAGE_HEIGHT * FILTER_NUM, CATEGORY_NUM], name='weight')
    b = bias_variable([CATEGORY_NUM], name='bias')
    h_conv_flat = tf.reshape(h_conv, [-1, IMAGE_WIDTH * IMAGE_HEIGHT * FILTER_NUM])
    y = tf.nn.softmax(tf.matmul(h_conv_flat, W) + b)

以上。

結果

テストデータ(青線)での識別率は、96.8%程度。
多層パーセプトロンでの、97.8%には及ばないが、多項ロジスティック回帰での、92.4%と比べると、かなり改善している。

image.png

あとがき

今回は、多項ロジスティック回帰を拡張して、単純な畳み込みニューラルネットを作ってみました。次回の記事では、これをまた少し拡張してみようかと。

8
9
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
8
9