DeepLearning
深層学習
CNN
TensorFlow
ConvolutionalNeuralNetworks

ロジスティック回帰 [TensorFlowでDeep Learning 1]

More than 1 year has passed since last update.

(目次はこちら)

はじめに

最近よく耳にする、Deep Learning / 深層学習についてちょっと勉強してみたいと思う。でも、自分で実装するほどやる気(実力も)ないので、TensorFlow使う。
本来であれば、数式やらを交えた方がいいが、数式アレルギーの人にとっては苦痛なので、そういった説明は他の専門家の方々に任せてシンプルに説明する。

Deep Learning / 深層学習とは?

  • ニューラルネットワークの一種で、誰かのbreakthroughで最近になって突然出てきたものではない。
  • ニューラルネットワークのうち、中間層 / 隠れ層が2層以上あるもの
  • ニューラルネットワークは、2度の冬の時代を経て、Deep Learningという形で再びホットになった。

ロジスティック回帰(logistic regression)

ニューラルネットワークとの違いを理解するためにも、まずはロジスティック回帰からやってみる。

ロジスティック回帰は、パーセプトロンの拡張で、入力を線形変換して活性化関数をかませて出力を得るというものには違いない。パーセプトロンでは、活性化関数にステップ関数を利用するが、ロジスティック回帰ではシグモイド関数を使う。

コード

とりあえず、コードを。
mnist_logistic.py

mnist_logistic.py
from helper import *

IMAGE_SIZE = 28 * 28
CATEGORY_NUM = 1
LEARNING_RATE = 0.1
TRAINING_LOOP = 20000
BATCH_SIZE = 100
SUMMARY_DIR = 'log_logistic'
SUMMARY_INTERVAL = 100

mnist = input_data.read_data_sets('data', one_hot=True)

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

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

    with tf.name_scope('optimize'):
        log_likelihood = tf.reduce_mean(tf.reduce_sum(y_ * tf.log(y) + (1 - y_) * tf.log(1 - y), reduction_indices=[1]))
        train_step = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(-log_likelihood)
        log_likelihood_summary = tf.scalar_summary('log likelihood', log_likelihood)

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

        correct_prediction = tf.equal(tf.sign(y - 0.5), tf.sign(y_ - 0.5))
        accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
        train_accuracy_summary = tf.scalar_summary('accuracy', accuracy)
        test_accuracy_summary = tf.scalar_summary('accuracy', accuracy)

        sess.run(tf.initialize_all_variables())
        for i in range(TRAINING_LOOP + 1):
            images, labels = mnist.train.next_batch(BATCH_SIZE)
            labels = [[1] if l[0] == 1 else [0] for l in labels]
            sess.run(train_step, {x: images, y_: labels})

            if i % SUMMARY_INTERVAL == 0:
                print('step %d' % i)
                summary = sess.run(tf.merge_summary([log_likelihood_summary, train_accuracy_summary]), {x: images, y_: labels})
                train_writer.add_summary(summary, i)
                summary = sess.run(tf.merge_summary([test_accuracy_summary]), {x: mnist.test.images, y_: [[1] if l[0] == 1 else [0] for l in mnist.test.labels]})
                test_writer.add_summary(summary, i)

後ほど使う分も含まれているけど、
helper.py

helper.py
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from tensorflow.examples.tutorials.mnist import input_data

import numpy as np
import tensorflow as tf

def weight_variable(shape, name=None):
    initial = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(initial, name=name)

def bias_variable(shape, name=''):
    initial = tf.constant(0.1, shape=shape)
    return tf.Variable(initial, name=name)

def conv2d(x, W):
    return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

def max_pool_2x2(x):
    return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

def prewitt_filter():
    v = np.array([[ 1, 0, -1]] * 3)
    h = v.swapaxes(0, 1)
    f = np.zeros(3 * 3 * 1 * 2).reshape(3, 3, 1, 2)
    f[:, :, 0, 0] = v
    f[:, :, 0, 1] = h
    return tf.constant(f, dtype = tf.float32)

コードの説明

データの読み込み

MNISTってのは、28x28ピクセルの手書き数字の画像のデータセット。

mnist = input_data.read_data_sets('data', one_hot=True)

入力層

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

出力層

入力xWで線形変換して、シグモイド関数に渡して結果yが得られる。Wbが求めるパラメータ。
ちなみに、この時点では、モデル(Tensorflowではgraphという)を作っているだけなのでなにも動いていない。

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

最適化(パラメーター推定)

ロジスティック回帰の最適化では対数尤度ってのを最大化してパラメータを求める。最適化手法は、Stochastic Gradient Descent / 確率的勾配降下法。

        log_likelihood = tf.reduce_mean(tf.reduce_sum(y_ * tf.log(y) + (1 - y_) * tf.log(1 - y), reduction_indices=[1]))
        train_step = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(-log_likelihood)

実行

変数の初期化後、sess.run()で、データを食わせて学習を実行する。
MNISTは0〜9の10カテゴリの分類問題だけど、ロジスティック回帰のために、無理矢理、1かそれ以外かの2カテゴリの分類問題にしているので、データの教師ラベルを付け替えている(1以外の時にラベルを0)。
本来であれば、カテゴリ間でデータの偏りがあるのは望ましくないが、今回は識別率は重要じゃないので気にしない。

        sess.run(tf.initialize_all_variables())
        for i in range(TRAINING_LOOP + 1):
            images, labels = mnist.train.next_batch(BATCH_SIZE)
            labels = [[1] if l[0] == 1 else [0] for l in labels]
            sess.run(train_step, {x: images, y_: labels})

評価

出力が、0.5以上なら1であるとして評価。正解ラベルと推定値が一致しているかを集計。

        correct_prediction = tf.equal(tf.sign(y - 0.5), tf.sign(y_ - 0.5))
        accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

学習の過程を可視化

TensorFlowはTensorBoardという可視化ツールを持っているので、それを利用して、学習過程での、対数尤度 / 学習データでの正解率 / テストデータでの正解率を定期的に出力して可視化。

        log_likelihood_summary = tf.scalar_summary('log likelihood', log_likelihood)
        train_accuracy_summary = tf.scalar_summary('accuracy', accuracy)
        test_accuracy_summary = tf.scalar_summary('accuracy', accuracy)
            if i % SUMMARY_INTERVAL == 0:
                print('step %d' % i)
                summary = sess.run(tf.merge_summary([log_likelihood_summary, train_accuracy_summary]), {x: images, y_: labels})
                train_writer.add_summary(summary, i)
                summary = sess.run(tf.merge_summary([test_accuracy_summary]), {x: mnist.test.images, y_: [[1] if l[0] == 1 else [0] for l in mnist.test.labels]})
                test_writer.add_summary(summary, i)

結果

テストデータ(青線)での識別率は、99.2%程度。

Screen Shot 2016-05-25 at 22.51.47.png

ちなみに、今回は単純なモデルなのであまり意味は無いが、モデル(グラフ)も可視化できる。
png.png

あとがき

次回の記事では、今回の2クラスの分類から、多クラスの分類に拡張してみます。