真・全力失踪

全力で道を見失うブログ

TensorFlowのチュートリアルスクリプトを理解する(続編)

翌々調べてみると
tensorflow/example/tutorial/mnist/fully_connected_feed.py
ちゃんとしたチュートリアルが存在していた。

色々とラップした関数があったけど、
今は一通り処理を追い駆けたいので全部展開してみました。

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from six.moves import xrange

import time
import math
import tensorflow as tf

モジュールをインポートしてます。

from tensorflow.examples.tutorials.mnist import input_data
NUM_CLASSES = 10
IMAGE_SIZE = 28
IMAGE_PIXELS = IMAGE_SIZE * IMAGE_SIZE

MNISTのデータを使うので、ここら辺もインポートしてます。

flags = tf.app.flags
flags.DEFINE_float('learning_rate', 0.01, 'Initial learning rate.')
flags.DEFINE_integer('max_steps', 2000, 'Number of steps to run trainer.')
flags.DEFINE_integer('hidden1', 128, 'Number of units in hidden layer 1.')
flags.DEFINE_integer('hidden2', 32, 'Number of units in hidden layer 2.')
flags.DEFINE_integer('batch_size', 100, 'Batch size. Must divide evenly into the dataset sizes.')
flags.DEFINE_string('train_dir', 'data', 'Directory to put the training data.')
flags.DEFINE_boolean('fake_data', False, 'If true, uses fake data for unit testing.')
FLAGS = flags.FLAGS

基本的なパラメータをFLAGとしてセットしてます。

def do_eval(sess, eval_correct, images_placeholder, labels_placeholder, data_set):
  true_count = 0  # Counts the number of correct predictions.
  steps_per_epoch = data_set.num_examples // FLAGS.batch_size
  num_examples = steps_per_epoch * FLAGS.batch_size
  for step in xrange(steps_per_epoch):
    images_feed, labels_feed = data_set.next_batch(FLAGS.batch_size, FLAGS.fake_data)
    feed_dict = {
      images_placeholder: images_feed,
      labels_placeholder: labels_feed,
    }
    true_count += sess.run(eval_correct, feed_dict=feed_dict)
  precision = true_count / num_examples
  print('  Num examples: %d  Num correct: %d  Precision @ 1: %0.04f' % (num_examples, true_count, precision))

画像識別率を評価する関数ですね。
演算子「//」は切り捨て除算だそうです。
「FLAGS.batch_size」のような形式で、先ほど設定したパラメータを取り出せるっぽいです。
「data_set.next_batch」は、データセットから指定したサイズのDNNの入力信号と教師信号を取り出してタプルで返してくれる便利メソッドです。
あと本筋とは離れますがxrangeは遅延評価されるレンジオブジェクトとのこと(参考)

def run_training():
  data_sets = input_data.read_data_sets(FLAGS.train_dir, FLAGS.fake_data)

  with tf.Graph().as_default():
    ### Generate placeholders for the images and labels.
    images_placeholder = tf.placeholder(tf.float32, shape=(FLAGS.batch_size, IMAGE_PIXELS))
    labels_placeholder = tf.placeholder(tf.int32, shape=(FLAGS.batch_size))

訓練データのためのメモリを確保してますね。
imagesはDNNの入力ベクトル、labelsはDNNの教師信号のインデックス、
一度に食わせるデータの数はbatch_sizeです。

    #### Build a Graph that computes predictions from the inference model.
    # Hidden 1
    with tf.name_scope('hidden1'):
      weights = tf.Variable(tf.truncated_normal(
                                                [IMAGE_PIXELS, FLAGS.hidden1],
                                                stddev=1.0 / math.sqrt(float(IMAGE_PIXELS))
                                               ), name='weights')
      biases = tf.Variable(tf.zeros([FLAGS.hidden1]), name='biases')
      hidden1 = tf.nn.relu(tf.matmul(images_placeholder, weights) + biases)

    # Hidden 2
    with tf.name_scope('hidden2'):
      weights = tf.Variable(tf.truncated_normal(
                                                [FLAGS.hidden1, FLAGS.hidden2],
                                                stddev=1.0 / math.sqrt(float(FLAGS.hidden1))
                                               ), name='weights')
      biases = tf.Variable(tf.zeros([FLAGS.hidden2]), name='biases')
      hidden2 = tf.nn.relu(tf.matmul(hidden1, weights) + biases)

ここで出力層以外のネットワークを構築してます。
tf.name_scope('hidden1')のwithブロックの中で定義したtf.Variableは、
'hidden1'というコンテキストの中でのみ有効ということみたいです。
あと、チュートリアルのレベルで活性化関数に「ReLU」が使われているとは驚きでした。

    # Linear
    with tf.name_scope('softmax_linear'):
      weights = tf.Variable(tf.truncated_normal(
                                                [FLAGS.hidden2, NUM_CLASSES],
                                                stddev=1.0 / math.sqrt(float(FLAGS.hidden2))
                                               ), name='weights')
      biases = tf.Variable(tf.zeros([NUM_CLASSES]), name='biases')
      logits = tf.matmul(hidden2, weights) + biases

    labels_placeholder = tf.to_int64(labels_placeholder)
    cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
                                                                   logits,
                                                                   labels_placeholder,
                                                                   name='xentropy'
                                                                  )
    loss = tf.reduce_mean(cross_entropy, name='xentropy_mean')

なぜここでint64にキャストしてるのかはちょっと謎ですが、
グラフに損失を計算するOperationを追加しています。
ところでlogitsってなんだろうと思って調べてみると、
ロジスティック関数の逆関数だそうです。
活性化関数がReLUの場合でもロジットになるのだろうか…?
tf.nn.sparse_softmax_cross_entropy_with_logitsは、
logitsとlabelベクトル間のソフトマックス・クロスエントロピーを計算する関数ですね。

続きはまた今度。

    ### Add to the Graph the Ops that calculate and apply gradients.
    # Add a scalar summary for the snapshot loss.
    tf.scalar_summary(loss.op.name, loss)

    # Create the gradient descent optimizer with the given learning rate.
    optimizer = tf.train.GradientDescentOptimizer(FLAGS.learning_rate)

    # Create a variable to track the global step.
    global_step = tf.Variable(0, name='global_step', trainable=False)

    # Use the optimizer to apply the gradients that minimize the loss
    # (and also increment the global step counter) as a single training step.
    train_op = optimizer.minimize(loss, global_step=global_step)


    # Add the Op to compare the logits to the labels during evaluation.
    correct = tf.nn.in_top_k(logits, labels_placeholder, 1)
    eval_correct = tf.reduce_sum(tf.cast(correct, tf.int32))

    # Build the summary operation based on the TF collection of Summaries.
    summary_op = tf.merge_all_summaries()

    # Add the variable initializer Op.
    init = tf.initialize_all_variables()

    # Create a saver for writing training checkpoints.
    saver = tf.train.Saver()

    # Create a session for running Ops on the Graph.
    sess = tf.Session()

    # Instantiate a SummaryWriter to output summaries and the Graph.
    summary_writer = tf.train.SummaryWriter(FLAGS.train_dir, sess.graph)

    # And then after everything is built:

    # Run the Op to initialize the variables.
    sess.run(init)

    # Start the training loop.
    for step in xrange(FLAGS.max_steps):
      start_time = time.time()

      # Fill a feed dictionary with the actual set of images and labels
      # for this particular training step.
      images_feed, labels_feed = data_sets.train.next_batch(FLAGS.batch_size, FLAGS.fake_data)
      feed_dict = {
        images_placeholder: images_feed,
        labels_placeholder: labels_feed,
      }

      # Run one step of the model.  The return values are the activations
      # from the `train_op` (which is discarded) and the `loss` Op.  To
      # inspect the values of your Ops or variables, you may include them
      # in the list passed to sess.run() and the value tensors will be
      # returned in the tuple from the call.
      _, loss_value = sess.run([train_op, loss], feed_dict=feed_dict)

      duration = time.time() - start_time

      # Write the summaries and print an overview fairly often.
      if step % 100 == 0:
        # Print status to stdout.
        print('Step %d: loss = %.2f (%.3f sec)' % (step, loss_value, duration))
        # Update the events file.
        summary_str = sess.run(summary_op, feed_dict=feed_dict)
        summary_writer.add_summary(summary_str, step)
        summary_writer.flush()

      # Save a checkpoint and evaluate the model periodically.
      if (step + 1) % 1000 == 0 or (step + 1) == FLAGS.max_steps:
        saver.save(sess, FLAGS.train_dir, global_step=step)
        # Evaluate against the training set.
        print('Training Data Eval:')
        do_eval(sess,
                eval_correct,
                images_placeholder,
                labels_placeholder,
                data_sets.train)
        # Evaluate against the validation set.
        print('Validation Data Eval:')
        do_eval(sess,
                eval_correct,
                images_placeholder,
                labels_placeholder,
                data_sets.validation)
        # Evaluate against the test set.
        print('Test Data Eval:')
        do_eval(sess,
                eval_correct,
                images_placeholder,
                labels_placeholder,
                data_sets.test)


def main(_):
  run_training()


if __name__ == '__main__':
  tf.app.run()

TensorFlowのチュートリアルスクリプトを理解する

from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)

このread_data_setsメソッドの実装は、
tensorflow/examples/tutorials/mnist/input_data.py
の中でインポートされている
tensorflow/examples/tutorials/mnist/mnist.py
の中にいます(ややこしい)。mnistにはDataSetというサンプル用の便利クラスのオブジェクトが返ります。カレントディレクトリにフォルダを作ってMNISTデータの圧縮ファイルがダウンロードされるので注意。

import tensorflow as tf
x = tf.placeholder(tf.float32, shape=[None, 784])
y_ = tf.placeholder(tf.float32, shape=[None, 10])
W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))

ここで入力ベクトルxおよび教師ベクトルy_を格納する領域の確保と、DNNの重みベクトルとバイアスベクトルをセットしている模様。重み行列が1つしか無いということは、もしかして単層のネットワークなのか…?

init_operations = tf.initialize_all_variables()
with tf.Session() as sess:
  sess.run(init_operations)

ここがよく分からないところ。セッションをwithブロックで開いて、Sessionクラスのrunを実行しています。initialize_all_variablesの戻り値は、全ての変数に対する(Initialize?)Operationのようなのですが、なぜrunを介して実行する必要があるのかがよく分からない…。

参照)
tensorflow/python/ops/variables.py
tensorflow/python/ops/control_flow_ops.py
tensorflow/python/framework/ops.py

  y = tf.nn.softmax(tf.matmul(x,W) + b)

入力に重みを掛けてバイアスを足してソフトマックス取ったのがy。あれ、活性化関数は…?

  cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
  train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)
  for i in range(1000):
    batch = mnist.train.next_batch(50)
    train_step.run(feed_dict={x: batch[0], y_: batch[1]})
  correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
  accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
  print(accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels}))

後半はなんとなく何をやっているのか分かりますが、相互情報量とかもう忘れてしまったなあ。

うーむ。他のチュートリアルも見てみよう。

TensorFlowの使い方がよく分からない

from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)

import tensorflow as tf
x = tf.placeholder(tf.float32, shape=[None, 784])
y_ = tf.placeholder(tf.float32, shape=[None, 10])
W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))

init_operations = tf.initialize_all_variables()
with tf.Session() as sess:
  sess.run(init_operations)
  y = tf.nn.softmax(tf.matmul(x,W) + b)
  cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))
  train_step = tf.train.GradientDescentOptimizer(
0.5).minimize(cross_entropy)
  for i in range(1000):
    batch = mnist.train.next_batch(50)
    train_step.run(feed_dict={x: batch[0], y_: batch[1]})
  correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
  accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
  print(accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels}))

このpythonスクリプトは、TensorFlowのチュートリアルプログラムを少し弄ったものです。パッケージへのパスが通ってれば動くはずです(パスを通すのに少し苦労した…)。スクリプトの詳細については次のポストへ続きます。

このページを見ながら、チュートリアルを動かしてライブラリの中を少し見てみたものの、見通しがまだイマイチ。なんとなく分かった気になっているのは、

  • tensorflowモジュールを介してDNNのノードや重みパラメータをセットし、
    ハンドル的なオブジェクトを受けとる
  • それらのパラメータに対する操作をするためにはSessionを開く必要がある
  • 開いたSessionはデフォルトセッションとしてセットされ、
    デフォルトセッションを介して操作できる
    (むしろセッションを明示する方法はないのだろうか…そういう使い方は想定していないのかも)

この場合ネットワークの構造ってどうなるんだろう。

 

音声認識システム作るには何が必要だっけ

もう1年以上も音声研究の分野から遠ざかっていた上に、学生時代の研究も終盤はかなりニッチなところを掘り下げていたので、音声認識システム全体がどうなっていたのかをおさらいするところから始めなくてはいけません。

統計的音声認識システムは誰かが発した「音声」と「音声に関する統計的なモデル」から、その誰かが「何と言おうとしてそう発音したのか」を推定するシステムなんですけど、そこら辺の話はきっと誰かがまとめてくれていると思うし、現時点でこの記事は自分用のメモとしての機能しか期待していないので色々割愛して、具体的な手順をメモするだけにとどめておこうと思います。

とりあえず、システムの仕様は以下。最初は楽したいので学生時代の研究のセッティングにかなり寄せています。

  • 状態遷移モデル:HMM
  • 言語モデル:音素バイグラム
  • 音響モデル:3状態(状態共有)トライフォン
  • 出力確率モデル:DNN(トポロジーは色々試したい)
  • 音響特徴量:色々試したい
  • 前処理:色々試したい

いわゆるDNN-HMMハイブリッドアプローチというやつです。個人的には、僕が大学院を卒業する頃に話題になりつつあった、DNNの活性化関数にReLUを使うのを試してみたいです。

次は、このシステムを構築するのにどういう作業が必要か。

  1. (ラベル付き)音声コーパスを用意する
  2. 音素バイグラムの生起確率を計算する
  3. (物理)トライフォンをリストアップする
  4. 音声コーパスから音響特徴量を抽出する
  5. 各音響特徴量をトライフォンの各状態に割り当てる
    (状態はとりあえず時間で3等分)
  6. ルールベースクラスタリングにより状態を共有する
  7. DNNの学習データを用意するため、特徴量系列の強制アライメントを行う
    …ために、各状態の出力確率モデルをGMMとしてパラメータを学習、
    学習したモデルを元にHMMの強制アライメントを行いパラメータを再学習、
    これを繰り返す(これってEMアルゴリズムって言うんですかね?)
  8. 学習したGMM-HMMを用いて特徴量系列の強制アライメントを行う
  9. 音響特徴量と対応する状態ベクトル(?)を与えてDNNのパラメータを学習する
  10. 学習したDNNに評価データの音響特徴量を入力し、
    出力をHMMの各状態の出力確率としてデコーディング

…って感じになると思うんですが、全てのプロセスを実装したことは無いので、どこかで躓くような気はしています。あ、トライフォン間の遷移確率には音素バイグラムを使います。

DNNの構築には話題のTensorFlowを使ってみようかなと思っています。TensorFlowのフロントエンドはPythonで実装されているようなのですが、Pythonはほとんど触ったことがないのでその勉強もしながらやる形になりそうです。

ディープラーニングをはじめよう

こんばんは、WEAPOMです。

2015年に某IT系の会社にプログラマとして入社して早1年。VisualStudio、XcodeEclipseなどのIDEは使わず(使えず)コーディングはTeraPad。扱える言語はMatlabとC、あとWeb系(HTML/CSS/JS)くらいのものだった僕も、なんとか仕事ができるくらいになってきました。ちなみに僕が今一番好きなテキストエディタVimです。

そんな僕ですが、プログラミングスキル向上の目的も兼ねて某かのアプリケーションを作りたいなと最近思ってまして。大学、大学院時代の研究テーマが「音声認識システムの精度向上のための音響特徴量抽出手法」だったので、「ナウいフレームワークやライブラリと、音声認識システムの知識を使って、何かしらアプリケーションを作る」というのを当面の目標としてやっていこうかなと思っております。