デジタル・デザイン・ラボラトリーな日々

アラフィフプログラマーが数学と物理と英語を基礎からやり直す。https://qiita.com/yaju

人工知能ハンズオンの資料公開

はじめに

静岡Developers勉強会では、今年の勉強会のテーマとして「人工知能ハンズオン」を2016/4/23に開催しました。
勉強会が行われた静岡市産学交流センター 小会議室2には、10人+2人(私と友人)=12人名が集まりました。あと、懇親会は5人で行きましたことをご報告しておきます。
人工知能というタイトルですがサブタイトルは「TensorFlowコトハジメ」です。まーバズワードにしないと東京と違って集客力がないのです。静岡って地域は新幹線の駅が6つあるくらい広すぎてしまって逆に人が集まらないんすよね。
connpass.com

当日は自分のPCとプロジェクターが相性があまりよくなくて、スライドがフルスクリーンにならないのでマウスの操作等に手間取ってしまいました。
また、準備していた際にはプロジェクターが映っていたので他の準備をしていたら映らなくなっていて、PCを再起動するはめになり予定開始時刻を10分程遅らせてしまったのは運営的に申し訳なかったです。
フルスクリーン関しては、今にして思えばPC側の解像度を下げれば良かったのかも知れません。

Windows 32bit PCの方用にとクラウド(AWS EC2 Docker版)を作成しておいたのですが、よく考えたらパブリックIPでWebなので他の人にも使えることに気がついて、TensorFlow環境を作成出来なかった人など数人の方にはクラウド版を使ってもらいました。

人工知能ハンズオンの資料自体は勉強会当日に既に公開していたのですが、今回修正した上で再度アップロードし直しましたのでブログで告知という段階となりました。

セッション内容

13:00 会場準備/受付
13:25 開催・挨拶
13:30 人工知能の概要
13:50 TensorFlowコトハジメ 概要
14:30 TensorFlowコトハジメ フィボナッチ数列
15:00 TensorFlowコトハジメ 八百屋で勾配降下法八百屋で識別問題
16:00 TensorFlowコトハジメ 手書き文字認識(MNIST)による多クラス識別問題
(未定) TensorFlowコトハジメ Automatic Colorization(白黒画像に彩色)
17:15 片付け
17:30 終了

プロジェクターの問題で開始自体は10分遅れの13:35あたりになってしまったんですが、人工知能の概要の無駄な説明を省いて時間的には取り戻しました。
TensorFlow関連の説明で淡々と進んでしまったのと、「Automatic Colorization(白黒画像の自動彩色)」については事前に試すことが出来なかったため、紹介程度で済ませてしまったので、予定より1時間以上早く終わってしまいました。
ここから先は、自由時間ということで途中退席も可能としました。LTとかする方もいなかったので仕方なく残りの1時間は人工知能関連の動画を流してました。
確かに説明を淡々としすぎてしまったんですが、私自身も口下手なのと細かく説明できるほどのまだ理解が出来てないのでどうしようもなかったですよね。あと、フィボナッチとフィナボッチって間違えて覚えてしまっていて、ツッコミ受けてその場で記事を修正したりしました。

「Automatic Colorization(白黒画像の自動彩色)」については、GW期間とそれ以降に実際に試すことが出来たので記事を書きました。 yaju3d.hatenablog.jp

スライド資料

www.slideshare.net qiita.com

動画について

下記3つの動画を流しました。

www.youtube.com

https://www.youtube.com/watch?v=1zO2Nu__JWgwww.youtube.com

www.youtube.com

最後の動画は、下記ブログを書いている方で理解するには数学的背景は大切ですよね。
enakai00.hatenablog.com

動画の追加(2016/06/27)
www.youtube.com

www.youtube.com

はじまりは半分

韓国のことわざに「はじまりは半分」というのがあります。
「一度何かを始めたらそれは半分成し遂げたと同じことだ」という意味です。

今回の勉強会でしかTensorFlowをやることは無いって方もいるでしょう。でも、きっかけは出来たと思います。
ひとりでも多くの方に目覚めるきっかけが出来たら、勉強会をやったかいがあるってものです。

数学モデルの作成

人工知能をちゃんと出来るようになるには、数学モデルを作れるようにならないと駄目ですね。
TensorFlowのプログラム自体は難しくないのですが、やりたいことをTensorFlowを使ってプログラムを組みたいと思っても数学モデルが作れないと如何ともし難いです。でも、ネタ的なアニメキャラ等の分類器はMINSTの応用だったりするので、数学モデルはあまり関係なかったりしますけどね。

プログラマーは文系でも出来る仕事だったりするわけですが、そろそろ文系と理系の差が開く時代が到来してきているなと感じています。 プログラマのための数学勉強会とか人数が集まっていますもんね。

最後に

準備不足

昨年の静岡Developers勉強会で来年は「人工知能ハンズオン」をやってみたいなーという漠然とした構想はあったので本とWebサイトなど記事自体は集めていたのですが、今年に入って3月中旬くらいまで何もやる気は起きなくて、静岡Developers勉強会は年3回くらいはやらないとなーと思って、そろそろ動かねばならぬと、情報処理技術者試験とGW期間に入る間の4/23にやる宣言をしてしまったわけです。会議室も運良く取れた。
自分はお尻に火が着かないと駄目なので日程を決めたからにはと、そこからTensorFlowのインストール記事やTensorFlowのことを理解するための記事とか書いたりしたのですが、やはり準備期間が1ヶ月では遅かったです。スライド資料も前日~当日午前4時と出かける直前までで作成してたしね。

次回開催

次に開催する時は説明がちゃんと出来るくらい理解した上でやりたいですね。そうすると11月とかあたりですかね。
その間の8月くらいは、IoTとVRに興味があるのでそちらをやろうと今は考えています。

TensorFlowコトハジメ Automatic Colorization(白黒画像の自動彩色)

はじめに

静岡Developers勉強会では、今年の勉強会のテーマとして「人工知能ハンズオン」を2016/4/23に開催しました。
その際に「Automatic Colorization(白黒画像の自動彩色)」については、間に合わずに事前に試すことが出来なかった為、勉強会当日は紹介のみに留まってしまいました。
GW期間があって試すことができましたので、その顛末を記事を書いてみました。なお、実行する場合にはメモリ不足から解消しないと2度手間になりますので注意してください。

Automatic Colorizationとは

白黒写真や動画をディープ・ラーニングを用いて自動でカラーに変換するRyan Dahlさんによるプロジェクトです。ちなみにライアン・ダール(Ryan Dahl)さんは、Node.jsの創始者でもあります。 自動彩色については他にもバークレーのコンピュータ科学者 Richard Zhangさん他、幾つかのプロジェクトがあります。

日本での取り組み

早稲田大学にて、飯塚 里志さん、シモセラ エドガーさん、石川博さんが白黒写真の自動色付けを行っています。
参照: ディープネットワークを用いた大域特徴と局所特徴の学習による白黒写真の自動色付け-早稲田大学

eiji_kさんのTwitterによれば

試した方がいます。プログラム言語はLuaを使用しているようです。
参照: Automatic Colorization of Grayscale Images を試すメモ

トレーニング有り版

下記サイトを公開しているPavel Gonchar氏のcolornetでは、白黒写真の自動色付けのトレーニング用も含まれています。
GitHub - pavelgonchar/colornet: Neural Network to colorize grayscale images

これを使ったeiji_kさんのTwitterによれば

導入方法

VirtualBox内Ubuntu 14.04.2 LTSのPython数値計算環境「Anaconda」にて試したところ、「Segmentation fault (コアダンプ)」エラーとなって動作しなかった為、Dockerを使用することにしました。Dockerを使うことで少ないメモリで実行できるようです。
今回のDockerイメージは、TensorFlow公式サイト版で説明していきます。 yaju3d.hatenablog.jp

2016/07/13追記 Segmentation faultの回避方法 qiita.com

環境

  • Windows 10 Home 64bit(Intel(R) Core(TM) i7-4700MQ CPU 2.40GHz メモリ 8.00GB)
  • DockerToolbox 1.10.3 → 1.11.1
  • VirtualBox 5.0.16 → 5.0.20

Dockerイメージ(TensorFlow公式サイト版)

TensorflowのサイトにあるDockerイメージにはscikit-imageがデフォルトでインストールされていないため、Jupyter NotebookのTerminalにて下記コマンドでインストールする必要があります。インストールには10分程かかります。

pip install scikit-image
 ︙
Successfully installed dask-0.9.0 networkx-1.11 scikit-image-0.12.3 toolz-0.7.4   

※Docker stop等でコンテナが破棄されると初期化されるため、毎回インストールする必要があります。

学習済みモデルファイルの取得

学習済みモデルのファイルが「colorize-20160110.tgz.torrent 492M」というトレントファイル(.torrent)として配布されています。
"BitTorrent(ビットトレント)"とは、大容量のファイルを高速に配信する為のP2Pソフトウェアです。
ダウンロードするには専用のソフトが必要になり、私はBitComet 日本語公認サイトから辿った先の「BitComet 1.40」を使いました。

世の中にはトレントファイルではなくtgzファイルのまま公開している方がいるので、そちらからダウンロード(colorize-20160108.tgz)するのが楽かと思います。
※トレント版が20160110なので20160108と2日早いのが気になります。

解凍すると下記3つのファイルが展開されます。

  • colorize.tfmodel (学習済みモデル)
  • forward.py (自動彩色実行ファイル Python 2.7)
  • shark.jpg (サンプルの白黒のサメ画像 224x224)
フォルダ共有による方法(追記:2016/05/19)

DockerとWindowsのフォルダ共有方法 with tensorflowのサイトを参考にしたらフォルダ共有が出来ました。この方法ならDockerコンテナにコピーする必要がないのでいいですね。

colorize-20160110フォルダをC:\Users(ユーザー名)の配下にします。
ダウンロードフォルダのままでも問題なかったです。

$ docker run -d -m 4g -p 8888:8888 -p 6006:6006 -v /c/Users/(ユーザー名)/Downloads\colorize-20160110:/notebooks b.gcr.io/tensorflow/tensorflow

この後は「実行」まで読み飛ばしても構いません。試行段階の経緯も書いているため、参考程度にお読みください。

Dockerイメージ(mokemokechickenさん版)

mokemokechickenさんのDockerファイルにはscikit-imageが含まれており、その他にもGraphvizなどもあるので今後いろいろやりたい場合には便利です。

githubサイトから「Download ZIP」ボタンで「jupyter-tensorflow-master.zip」をダウンロードし、解凍してダウンロードフォルダに展開しました。

DockerファイルからDockerイメージを作成します。

$ docker build -t tensorenv /c/Users/(ユーザー名)/Downloads/jupyter-tensorflow-master

※VirtualBoxの設定にある共有フォルダは「c/Users」になっています。"c/"は英小文字なので注意してください。

qiita.com

以前は出来たのですが、直近(2016/05/15)では下記エラーが出ました。

E: Unable to locate package graphviz-dev
E: Unable to locate package graphviz
E: Package 'pkg-config' has no installation candidate
E: Unable to locate package libgdal-dev

DockerFileのRUN apt-getの直後に「update && apt-get」を付けるといいでしょう。

RUN apt-get install -y graphviz-dev graphviz pkg-config
RUN apt-get install -y libgdal-dev
↓
RUN apt-get update && apt-get install -y graphviz-dev graphviz pkg-config
RUN apt-get update && apt-get install -y libgdal-dev

後述するメモリ制限値の"-m"オプションを付けて実行します。"-d"オプションはコンテナをバックグラウンドで動かします。

$ docker run -d -m 4g -p 8888:8888 -p 6006:6006 tensorenv

学習済みモデルファイルのコンテナへのコピー

$ docker ps
#コンテナ名(NAME)を取得 例 loving_minsky

$ docker cp /c/Users/(ユーザー名)/Downloads/colorize-20160110/colorize.tfmodel loving_minsky:/home/jovyan/work

Docker内へのファイルアップロード

Jupyter NotebookにはUpload機能があるのですが、アップロードできるサイズが25MByteまでとなっています。そのため、colorize.tfmodelファイルは529MByteと巨大なため、別方法でアップロードする必要があります。 f:id:Yaju3D:20160514134901p:plain

docker cpコマンドによる方法

docker run している場合、Control-Cで停止してください。

$ docker cp [コピー元ファイル] [CONTAINER ID or NAME]:[コピー先ファイル]

今回、colorize-20160110はダウンロードフォルダに展開しました。
※VirtualBoxの設定にある共有フォルダは「c/Users」になっています。"c/"は英小文字なので注意してください。
※ホストからコンテナへのコピーにdocker execコマンドを使った方法があるのですが、Docker 1.8からホストからコンテナへのコピーも docker cp コマンドでサポートされたため、修正しました。

$ docker ps
#コンテナ名(NAME)を取得 例 cranky_murdock

$ docker cp /c/Users/(ユーザー名)/Downloads/colorize-20160110/colorize.tfmodel cranky_murdock:/notebooks

$ docker start cranky_murdock

qiita.com

curlコマンドによる方法

docker execコマンドを使わない方法として、Jupyter NotebookのTerminalにて下記コマンドを入力します。これによりnotebooksフォルダ配下にcolorize-20160108フォルダが出来ます。

# curl -SL https://s3.amazonaws.com/tinyclouds-storage/colorize-20160108.tgz | tar -xzC /notebooks                                                                     
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current                                                                               
                                 Dload  Upload   Total   Spent    Left  Speed                                                                                 
100  491M  100  491M    0     0  8714k      0  0:00:57  0:00:57 --:--:-- 9284k                                                                             

※この方法を使用した場合、ソースリストのファイル指定時に"colorize-20160108\colorize.tfmodel"としてフォルダを指定する必要があります。

実行

Jupyter Notebookにて下記ソースコードを実行します。 f:id:Yaju3D:20160514140332p:plain

import tensorflow as tf
import skimage.transform
from skimage.io import imsave, imread

def load_image(path):
    img = imread(path)
    # crop image from center
    short_edge = min(img.shape[:2])
    yy = int((img.shape[0] - short_edge) / 2)
    xx = int((img.shape[1] - short_edge) / 2)
    crop_img = img[yy : yy + short_edge, xx : xx + short_edge]
    # resize to 224, 224
    img = skimage.transform.resize(crop_img, (224, 224))
    # desaturate image
    return (img[:,:,0] + img[:,:,1] + img[:,:,2]) / 3.0

shark_gray = load_image("shark.jpg").reshape(1, 224, 224, 1)

with open("colorize.tfmodel", mode='rb') as f:
    fileContent = f.read()
graph_def = tf.GraphDef()
graph_def.ParseFromString(fileContent)
grayscale = tf.placeholder("float", [1, 224, 224, 1])
tf.import_graph_def(graph_def, input_map={ "grayscale": grayscale }, name='')

with tf.Session() as sess:
    inferred_rgb = sess.graph.get_tensor_by_name("inferred_rgb:0")
    inferred_batch = sess.run(inferred_rgb, feed_dict={ grayscale: shark_gray })
    imsave("shark-color.jpg", inferred_batch[0])
    print("saved shark-color.jpg")

メモリエラー

残念ながら実行すると「MemoryError」が出力されてしまいます。

/usr/local/lib/python2.7/dist-packages/google/protobuf/internal/python_message.pyc in SerializePartialToString(self)
   1072     out = BytesIO()
   1073     self._InternalSerialize(out.write)
-> 1074     return out.getvalue()
   1075   cls.SerializePartialToString = SerializePartialToString
   1076 

MemoryError: 

メモリを増やす

DockerToolboxで最初に作成されるdefaultマシンのメモリは1GByteとなっていますので、下記サイトを参考にVirtualBoxの設定にてdefaultマシンのメモリを増やします。
※ちなみにクラウドのAWS EC2の無料で使えるt2.microはメモリが1GByteとなっているので動作出来そうもありません。

qiita.com
defaultの電源オフの状態で設定ボタンをクリックする
f:id:Yaju3D:20160514135543p:plain
1024MBから4096MBにメモリを変更 f:id:Yaju3D:20160514135556p:plain

docker runの"-m"オプションを付けてメモリ制限値を指定します(※割り当てではない)。単位には b,k,m,g があります。

$ docker run -m 4g -p 8888:8888 -p 6006:6006 b.gcr.io/tensorflow/tensorflow

dockerコンテナが破棄されると初期化した状態になるため、再度scikit-imageのインストールと学習済みモデルをセットしてください。

Jupyter NotebookのTerminalにて「free」コマンドを入力するとメモリサイズが表示されます。

# free                                                                                                                                                        
             total       used       free     shared    buffers     cached                                                                                     
Mem:       4045896    1188836    2857060     167164      40364     953316                                                                                     
-/+ buffers/cache:     195156    3850740                                                                                                                      
Swap:      1939036          0    1939036   

再実行

先ずはサンプルのshark.jpgをやってみました。変換は15秒程度です。
f:id:Yaju3D:20160105021604j:plain f:id:Yaju3D:20160514140945j:plain

鉛筆画を試す

Twitterユーザーの古谷振一さん(@shtt4881)の鉛筆画が白黒写真のようにしか見えないと話題になっておりました。
参照:きれいな写真だなー……って絵だとぉ!? 鉛筆で描かれた芸能人たちが目を疑う精巧さと美しさ
むむ、白黒画像なら自動彩色してみようと試してみました(サイズ 224x224 2値画像に変換してから試す)。ちょっと微妙ですかね。 連続してやるとメモリ不足になるので、毎回Shutodownしてメモリを解消しました。
f:id:Yaju3D:20160407003537p:plain

f:id:Yaju3D:20160514160825j:plain f:id:Yaju3D:20160514160838j:plain
f:id:Yaju3D:20160514160854j:plain f:id:Yaju3D:20160514160914j:plain
f:id:Yaju3D:20160514160931j:plain f:id:Yaju3D:20160514160947j:plain
f:id:Yaju3D:20160514161005j:plain f:id:Yaju3D:20160514161015j:plain

坂本龍馬を試す

顔写真だけだとつまらないので、坂本龍馬で検索した画像を試してみました。背景の草や板に色が付くようになっています。
f:id:Yaju3D:20160514162118j:plain f:id:Yaju3D:20160514162106j:plain

動画の変換

下記サイトにて動画のカラー化を行っています。
前後にffmpegでの処理を行って、Automatic Colorizationをフォルダ内の画像に対してループしているとのこと。 http://www.shun.bz/20160127/1050814061.htmlwww.shun.bz

カラー化した画像ファイルをホストへコピー

docker cpコマンドでホストへファイルをコピーすることが出来ます。

$ docker ps
#コンテナ名(NAME)を取得 例 cranky_murdock

$ docker cp cranky_murdock:/notebooks/shark-color.jpg /c/Users/(ユーザー名)

TensorFlowコトハジメ 手書き文字認識(MNIST)による多クラス識別問題

はじめに

手書き文字認識(MNIST)による多クラス識別問題をやってみる。

前回に引き続きこの資料を基に理解していく。

MNISTとは

手書きの文字列を認識するもので、画像認識では定番と言えるテーマだ。 手書き文字の認識データは、機械学習の著名な研究者であるYann LeCun氏のwebsiteで公開しており、0-9のいずれかの数字が学習用、テスト用それぞれで60000枚と10000枚含まれている。
各数字画像の大きさは28×28ピクセルの単色画像で、RGBではなくGray-scaleの色空間となっている。
f:id:Yaju3D:20160422062644p:plain

多クラス識別問題

前回の八百屋の識別問題が、買えるか買えないかの2クラスの分類であったが、今回は手書き文字を0-9と多クラスの分類となる。

手書き文字認識

f:id:Yaju3D:20160422063603p:plain

機械学習の流れ

f:id:Yaju3D:20160422063746p:plain

one-hot ベクトル (one-of-K表現)

f:id:Yaju3D:20160422073005p:plain

案1: ロジスティック回帰を拡張

f:id:Yaju3D:20160422064743p:plain
f:id:Yaju3D:20160422064833p:plain
ソフトマックス関数と呼ばれており、シグモイド関数を多クラス問題に対応させた活性化関数である。
ソフトマックス関数を使うと出力層の各ユニットの和が1になります。つまり、出力値が各クラスである確率と見なせるようになります。

案2: さらに中間層を追加

f:id:Yaju3D:20160422064903p:plain

1. 予測式(モデル)を記述

TensorFlowによる実装

# 入力変数と出力変数のプレースホルダを生成
x = tf.placeholder(tf.float32, [None, 784])
y_ = tf.placeholder(tf.float32, [None, 10])
# モデルパラメータ(入力層:784ノード, 隠れ層:100ノード, 出力層:10ノード)
W1 = tf.Variable(tf.truncated_normal([784, 100]))
b1 = tf.Variable(tf.zeros([100]))
W2 = tf.Variable(tf.truncated_normal([100, 10]))
b2 = tf.Variable(tf.zeros([10]))
# モデル式
h = tf.sigmoid(tf.matmul(x, W1) + b1) # 入力層->隠れ層
u = tf.matmul(h, W2) + b2             # 隠れ層->出力層 (ロジット)
y = tf.nn.softmax(u)                  # 隠れ層->出力層 (ソフトマックス後)

f:id:Yaju3D:20160422071235p:plain

2. 誤差関数と最適化手法を記述

# 誤差関数(loss)
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(u, y_))
# 最適化手段(最急降下法)
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(loss)
# 正答率(学習には用いない)
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
acc = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
  • 誤差関数を変更
    多クラス識別(分類)問題
    →多クラス用クロスエントロピー(softmax_cross_entropy_with_logits)

3. 学習実行

# (2) バッチ型確率的勾配降下法でパラメータ更新
for i in range(10000):
    # 訓練データから100サンプルをランダムに取得
    batch_xs, batch_ys = mnist.train.next_batch(100)
    # 学習
    _, l = sess.run([train_step, loss], feed_dict={x: batch_xs, y_: batch_ys})
    if (i + 1) % 1000 == 0:
        print "step=%3d, loss=%.2f" % (i + 1, l)
  • 大規模なデータで学習する時の工夫
    各ステップで、100個の訓練データをランダムに取り出して学習 (確率的勾配降下法)
    →速く学習が進む

参考: 学習結果

f:id:Yaju3D:20160422071950p:plain

4. 予測

# (1) テスト用データを1000サンプル取得
new_x = mnist.test.images[0:1000]
new_y_ = mnist.test.labels[0:1000]

# (2) 予測と性能評価
accuracy, new_y = sess.run([acc, y], feed_dict={x:new_x , y_:new_y_ })
print "Accuracy (for test data): %6.2f%%" % accuracy
print "True Label:", np.argmax(new_y_[0:15,], 1)
print "Est Label:", np.argmax(new_y[0:15, ], 1)

ここはこれまでと同様

【実行結果例】

Accuracy(test data): 80.0% 
True Label: [7 2 1 0 4 1 4 9 5 9 0 6 9 0 1]
Est Label: [7 2 1 0 9 1 4 9 2 9 0 6 9 0 1]

最終ソースコード

# coding: utf-8
import numpy as np
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

# MNISTデータの取得
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

# 1. 学習したいモデルを記述する
# 入力変数と出力変数のプレースホルダを生成
x = tf.placeholder(tf.float32, [None, 784])
y_ = tf.placeholder(tf.float32, [None, 10])
# モデルパラメータ(入力層:784ノード, 隠れ層:100ノード, 出力層:10ノード)
W1 = tf.Variable(tf.truncated_normal([784, 100]))
b1 = tf.Variable(tf.zeros([100]))
W2 = tf.Variable(tf.truncated_normal([100, 10]))
b2 = tf.Variable(tf.zeros([10]))
# モデル式
h = tf.sigmoid(tf.matmul(x, W1) + b1) # 入力層->隠れ層
u = tf.matmul(h, W2) + b2             # 隠れ層->出力層 (ロジット)
y = tf.nn.softmax(u)                  # 隠れ層->出力層 (ソフトマックス後)

# 2. 学習やテストに必要な関数を定義する
# 誤差関数(loss)
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(u, y_))
# 最適化手段(最急降下法)
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(loss)
# 正答率(学習には用いない)
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
acc = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

# 3. 実際に学習処理を実行する
# (1) セッションを準備し,変数を初期化
sess = tf.Session()
init = tf.initialize_all_variables()
sess.run(init)

# (2) バッチ型確率的勾配降下法でパラメータ更新
for i in range(10000):
    # 訓練データから100サンプルをランダムに取得
    batch_xs, batch_ys = mnist.train.next_batch(100)
    # 学習
    _, l = sess.run([train_step, loss], feed_dict={x: batch_xs, y_: batch_ys})
    if (i + 1) % 1000 == 0:
        print("step=%3d, loss=%.2f" % (i + 1, l))

# 4. テスト用データに対して予測し,性能を確認
# (1) テスト用データを1000サンプル取得
new_x = mnist.test.images[0:1000]
new_y_ = mnist.test.labels[0:1000]

# (2) 予測と性能評価
accuracy, new_y = sess.run([acc, y], feed_dict={x:new_x , y_:new_y_ })
print("Accuracy (for test data): %6.2f%%" % accuracy)
print("True Label:", np.argmax(new_y_[0:15,], 1))
print("Est Label:", np.argmax(new_y[0:15, ], 1))

# 5. 後片付け
# セッションを閉じる
sess.close()

最終結果

Extracting MNIST_data/train-images-idx3-ubyte.gz
Extracting MNIST_data/train-labels-idx1-ubyte.gz
Extracting MNIST_data/t10k-images-idx3-ubyte.gz
Extracting MNIST_data/t10k-labels-idx1-ubyte.gz
step=1000, loss=2.03
step=2000, loss=1.43
step=3000, loss=0.90
step=4000, loss=0.84
step=5000, loss=1.04
step=6000, loss=0.76
step=7000, loss=0.63
step=8000, loss=0.66
step=9000, loss=0.48
step=10000, loss=0.77
Accuracy (for test data):   0.79%
True Label: [7 2 1 0 4 1 4 9 5 9 0 6 9 0 1]
Est Label: [7 2 1 0 4 1 4 4 6 7 0 6 9 0 1]

チュートリアル(MNISTビギナー編)

チュートリアルのMNIST For ML Beginners用の本来のソースリストは使わず、今回は、株式会社ブレインパッドの技術エントリーTensorFlowを遊び倒す! 1-1. MNIST For ML Beginnersのソースリストを動くように変更したものを使います。

チュートリアルは2015年11月時点の記事を参考にしても、ディレクトリ配置が変わってしまったようなので注意が必要です。
tensorflow/g3doc/tutorials/mnist/ → tensorflow/examples/tutorials/mnist/

#変更箇所
import input_data  → from tensorflow.examples.tutorials.mnist import input_data
tf.device("/gpu:1") → tf.device("/cpu:0")
printprint()

ソースリスト

# coding: utf-8
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data


def main():
    # mnistのダウンロード
    mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
    
    # 複数GPUがあるので指定
    with tf.device("/cpu:0"):
        # n * 784 の可変2階テンソル
        x = tf.placeholder("float", [None, 784])
        
        # 重み行列とバイアスの宣言
        W = tf.Variable(tf.zeros([784, 10]))
        b = tf.Variable(tf.zeros([10]))
        
        # ソフトマックス層を定義
        y = tf.nn.softmax(tf.matmul(x, W) + b)

        # 正解用2階テンソルを用意
        y_ = tf.placeholder("float", [None, 10])

        # 誤差関数の交差エントロピー誤差関数を用意
        cross_entropy = -tf.reduce_sum(y_*tf.log(y))

        # 学習方法を定義 0.01は学習率
        train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)

    # 変数の初期化 ここでGPUにメモリ確保か
    init = tf.initialize_all_variables()
    sess = tf.Session()
    sess.run(init)

    # 1に一番近いインデックス(予測)が正解とあっているか検証し、その平均
    correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

    # 学習開始
    for i in range(1000):
        batch_xs, batch_ys = mnist.train.next_batch(100)
        sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
        #print(sess.run(accuracy, feed_dict={x: batch_xs, y_: batch_ys}))


    print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))

if __name__ == "__main__":
    main()

実行結果

Successfully downloaded train-images-idx3-ubyte.gz 9912422 bytes.
Extracting MNIST_data/train-images-idx3-ubyte.gz
Successfully downloaded train-labels-idx1-ubyte.gz 28881 bytes.
Extracting MNIST_data/train-labels-idx1-ubyte.gz
Successfully downloaded t10k-images-idx3-ubyte.gz 1648877 bytes.
Extracting MNIST_data/t10k-images-idx3-ubyte.gz
Successfully downloaded t10k-labels-idx1-ubyte.gz 4542 bytes.
Extracting MNIST_data/t10k-labels-idx1-ubyte.gz
0.9139

参照

チュートリアル(MNISTエキスパート編)

チュートリアルのMNIST For ML Beginners用の本来のソースリストは使わず、株式会社ブレインパッドの技術エントリーTensorFlowを遊び倒す! 2-1. MNIST For Expertsのソースリストを動くように変更したものを使います。

#変更箇所
import input_data  → from tensorflow.examples.tutorials.mnist import input_data
tf.device("/gpu:1") → tf.device("/cpu:0")
strides=[1, 1, 1, 1], # 真ん中2つが縦横のストライド → コメント移動
strides=[1, 2, 2, 1], # 真ん中2つが縦横のストライド → コメント移動
printprint()

ソースリスト

# coding: utf-8
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

def weight_variable(shape):
    """適度にノイズを含んだ(対称性の除去と勾配ゼロ防止のため)重み行列作成関数
    """

    initial = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(initial)

def bias_variable(shape):
    """バイアス行列作成関数
    """
    initial = tf.constant(0.1, shape=shape)
    return tf.Variable(initial)

def conv2d(x, W):
    """2次元畳み込み関数
       strides 真ん中2つが縦横のストライド
    """
    return tf.nn.conv2d(x,
                        W,
                        strides=[1, 1, 1, 1],
                        padding='SAME')

def max_pool_2x2(x):
    """2x2マックスプーリング関数
       strides 真ん中2つが縦横のストライド
    """
    return tf.nn.max_pool(x,
                          ksize=[1, 2, 2, 1],
                          strides=[1, 2, 2, 1],
                          padding='SAME')

def main():
    # mnistのダウンロード
    mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

    sess = tf.InteractiveSession()
    with tf.device("/cpu:0"):
        # データ用可変2階テンソルを用意
        x = tf.placeholder("float", shape=[None, 784])
        # 正解用可変2階テンソルを用意
        y_ = tf.placeholder("float", shape=[None, 10])

        # 画像をリシェイプ 第2引数は画像数(-1は元サイズを保存するように自動計算)、縦x横、チャネル
        x_image = tf.reshape(x, [-1, 28, 28, 1])

        ### 1層目 畳み込み層

        # 畳み込み層のフィルタ重み、引数はパッチサイズ縦、パッチサイズ横、入力チャネル数、出力チャネル数
        # 5x5フィルタで32チャネルを出力(入力は白黒画像なので1チャンネル)
        W_conv1 = weight_variable([5, 5, 1, 32])
        # 畳み込み層のバイアス
        b_conv1 = bias_variable([32])
        # 活性化関数ReLUでの畳み込み層を構築
        h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)

        ### 2層目 プーリング層

        # 2x2のマックスプーリング層を構築
        h_pool1 = max_pool_2x2(h_conv1)

        ### 3層目 畳み込み層

        # パッチサイズ縦、パッチサイズ横、入力チャネル、出力チャネル
        # 5x5フィルタで64チャネルを出力
        W_conv2 = weight_variable([5, 5, 32, 64])
        b_conv2 = bias_variable([64])
        h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)

        ### 4層目 プーリング層
        h_pool2 = max_pool_2x2(h_conv2)

        ### 5層目 全結合層

        # オリジナル画像が28x28で、今回畳み込みでpadding='SAME'を指定しているため
        # プーリングでのみ画像サイズが変わる。2x2プーリングで2x2でストライドも2x2なので
        # 縦横ともに各層で半減する。そのため、28 / 2 / 2 = 7が現在の画像サイズ

        # 全結合層にするために、1階テンソルに変形。画像サイズ縦と画像サイズ横とチャネル数の積の次元
        # 出力は1024(この辺は決めです) あとはSoftmax Regressionと同じ
        W_fc1 = weight_variable([7 * 7 * 64, 1024])
        b_fc1 = bias_variable([1024])

        h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64])
        h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

        # ドロップアウトを指定
        keep_prob = tf.placeholder("float")
        h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

        ### 6層目 Softmax Regression層

        W_fc2 = weight_variable([1024, 10])
        b_fc2 = bias_variable([10])

        y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

    # 評価系の関数を用意
    cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
    train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
    correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
    sess.run(tf.initialize_all_variables())

    for i in range(20000):
        batch = mnist.train.next_batch(50)
        if i%100 == 0:
            train_accuracy = accuracy.eval(feed_dict={x:batch[0],
                                                      y_: batch[1],
                                                      keep_prob: 1.0})
            print("step %d, training accuracy %g"%(i, train_accuracy))
        train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})

    print("test accuracy %g"%accuracy.eval(feed_dict={
        x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))

if __name__ == "__main__":
    main()

実行結果 CPUのみとしたため、3時間程度かかりました。

自PCは「Intel Core i7-4700MQ 2.4GHz」です。
akiraakさんのブログには「Intel Core i7-6700K 4GHz」で30分、「NVIDIA TITAN X」のGPU実行では1分30秒に短縮されますとのこと。速いは正義!

Extracting MNIST_data/train-images-idx3-ubyte.gz
Extracting MNIST_data/train-labels-idx1-ubyte.gz
Extracting MNIST_data/t10k-images-idx3-ubyte.gz
Extracting MNIST_data/t10k-labels-idx1-ubyte.gz
step 0, training accuracy 0.1
step 100, training accuracy 0.78
step 200, training accuracy 0.94
step 300, training accuracy 0.88

(中略)

step 19800, training accuracy 1
step 19900, training accuracy 1
test accuracy 0.9926

実行前の注意点(2016/05/28追記)

Docker上で実行した場合、"The Kernel appears to have died."という表示とともに計算が停止したとのコメントを頂きました。
TensorFlowコトハジメ Automatic Colorization(白黒画像の自動彩色)の記事内のメモリ不足を参考に、VirtualBoxのdefaultを4GByteに、docker runに「-m 4g」のオプションを付けて実行してください。
実行後にfreeコマンドでメモリ状況を参照したところ使用メモリが「1138420」となっていました。defaultマシンの初期メモリは1GByteなのでメモリが足りなかったのでしょう。

             total       used       free     shared    buffers     cached                                                                                     
Mem:       4045692    1138420    2907272      91824      10164     108692                                                                                     
-/+ buffers/cache:    1019564    3026128                                                                                                                      
Swap:      1946364     244772    1701592 

参照

TensorFlowコトハジメ 八百屋で識別問題

はじめに

前回、八百屋で勾配降下法を実行したので、次の識別問題をやってみる。 yaju3d.hatenablog.jp

前回に引き続きこの資料を基に理解していく。

識別問題とは

入力データから判断して分類させる問題。今回の例では買えるのか買えないのかを判断させる。
識別分類器としてロジスティック回帰を使う。

ロジスティック回帰とは

ロジスティック回帰は、発生確率を予測する手法です。
基本的な考え方は線形回帰分析と同じなのですが、予測結果が 0 から 1 の間を取るように数式やその前提に改良が加えられています。 今回のように購入有無(買える or 買えない)の2値しかとりえない値を従属変数の実績値として用い、説明変数を用いてその発生確率を説明するという構造になっています。

例題2

たかしくんは八百屋へ財布を預かってお使いに行きました。
しかし、たかしくんはお金を 数えられません。

気まぐれおやじ曰く、
リンゴ2個+ミカン3個、リンゴ0個+ミカン16個 なら買えるが、リンゴ3個+ミカン1個、 リンゴ2個+ミカン8個は買えないとのこと。

リンゴ1個+ミカン11個は買えますか? → 識別問題 f:id:Yaju3D:20160421011949p:plain

式で表そうとしてみる…
f:id:Yaju3D:20160421012015p:plain f:id:Yaju3D:20160421012037p:plain

シグモイド曲線

f:id:Yaju3D:20160421012100p:plain
※シグモイド曲線とは、入力した値を0から1の間に収めてくれる関数の1つ
 多くの自然界に存在する事柄は、このようなS字曲線を取る。
 生物の神経細胞、細胞の生存率曲線などなど

予測式(モデル)が作れた!
f:id:Yaju3D:20160421012204p:plain

ロジスティック回帰
f:id:Yaju3D:20160421012219p:plain

予測式(モデル)を記述

# 1. 予測式(モデル)を記述する
# 入力変数と出力変数のプレースホルダを生成
x = tf.placeholder(tf.float32, shape=(None, 2), name="x")
y_ = tf.placeholder(tf.float32, shape=(None, 1), name="y")
# モデルパラメータ
a = tf.Variable(-10 * tf.ones((2, 1)), name="a")
b = tf.Variable(200., name="b")
# モデル式
u = tf.matmul(x, a) + b
y = tf.sigmoid(u)

補足説明
tf.onesは、要素が全て「1」である1行列をセット 参照:TensorflowのAPIについて

今回の予測式に合わせてモデルとパラメーターを修正
f:id:Yaju3D:20160421012251p:plain

誤差関数と最適化手法を記述

# 2. 学習に必要な関数を定義する
# 誤差関数(loss)
loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=u, labels=y_))
# 最適化手段を選ぶ(最急降下法)
train_step = tf.train.GradientDescentOptimizer(0.1).minimize(loss)

・誤差関数を変更
 識別(分類)問題→クロスエントロピー
f:id:Yaju3D:20160421012315p:plain

訓練データを作成(or読込)

# 3. 実際に学習処理を実行する
# (1) 訓練データを生成する
train_x = np.array([[2., 3.], [0., 16.], [3., 1.], [2., 8.]])
train_y = np.array([1., 1., 0., 0.]).reshape(4, 1)
print("x=", train_x)
print("y=", train_y)

学習実行

# (2) セッションを準備し,変数を初期化
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)

# (3) 最急勾配法でパラメータ更新 (100回更新する)
for i in range(1000):
    _, l, a_ = sess.run([train_step, loss, a], feed_dict={x: train_x, y_: train_y})
    if (i + 1) % 100 == 0:
        print("step=%3d, a1=%6.2f, a2=%6.2f, loss=%.2f" % (i + 1, a_[0], a_[1], l))
        
# (4) 学習結果を出力
est_a, est_b = sess.run([a, b], feed_dict={x: train_x, y_: train_y})
print("Estimated: a1=%6.2f, a2=%6.2f, b=%6.2f" % (est_a[0], est_a[1], est_b))   

線形回帰とほぼ同じ
【変更箇所】

  • パラメータbの出力を追加
  • 更新回数を100回→1000回に
    ※更新回数は、対象問題やデータ、初期値、モデルなどでまちまちです。

学習結果

f:id:Yaju3D:20160421012400p:plain

予測結果

# 4. 新しいデータに対して予測する
# (1) 新しいデータを用意
new_x = np.array([1., 11.]).reshape(1, 2)

# (2) 学習結果をつかって,予測実施
new_y = sess.run(y, feed_dict={x: new_x})
print(new_y)

0.95848435 ≒ 0.96 f:id:Yaju3D:20160424232047p:plain

最終ソースコード

# 必要なモジュールを読み込む
# coding: utf-8
import numpy as np
import tensorflow as tf

# TensorFlow でロジスティック回帰する

# 1. 学習したいモデルを記述する
# 入力変数と出力変数のプレースホルダを生成
x = tf.placeholder(tf.float32, shape=(None, 2), name="x")
y_ = tf.placeholder(tf.float32, shape=(None, 1), name="y")
# モデルパラメータ
a = tf.Variable(-10 * tf.ones((2, 1)), name="a")
b = tf.Variable(200., name="b")
# モデル式
u = tf.matmul(x, a) + b
y = tf.sigmoid(u)

# 2. 学習やテストに必要な関数を定義する
# 誤差関数(loss)
loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=u, labels=y_))
# 最適化手段(最急降下法)
train_step = tf.train.GradientDescentOptimizer(0.1).minimize(loss)


# 3. 実際に学習処理を実行する
# (1) 訓練データを生成する
train_x = np.array([[2., 3.], [0., 16.], [3., 1.], [2., 8.]])
train_y = np.array([1., 1., 0., 0.]).reshape(4, 1)
print("x=", train_x)
print("y=", train_y)

# (2) セッションを準備し,変数を初期化
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)

# (3) 最急勾配法でパラメータ更新 (1000回更新する) 
for i in range(1000): 
    _, l, a_, b_ = sess.run([train_step, loss, a, b], feed_dict={x: train_x, y_: train_y})
    if (i + 1) % 100 == 0:
        print("step=%3d, a1=%6.2f, a2=%6.2f, b=%6.2f, loss=%.2f" % (i + 1, a_[0], a_[1], b_, l))

# (4) 学習結果を出力
est_a, est_b = sess.run([a, b], feed_dict={x: train_x, y_: train_y})
print("Estimated: a1=%6.2f, a2=%6.2f, b=%6.2f" % (est_a[0], est_a[1], est_b))

# 4. 新しいデータに対して予測する
# (1) 新しいデータを用意
new_x = np.array([1., 11.]).reshape(1, 2)

# (2) 学習結果をつかって,予測実施
new_y = sess.run(y, feed_dict={x: new_x})
print(new_y)

# 5. 後片付け
# セッションを閉じる
sess.close()

実行結果

0.95848435 ≒ 0.96 リンゴ 59.68 ≒ 60、ミカン 11.40 ≒ 11 → σ(185 - (60 x 1 + 11 x 11)) = 0.96(買えそう)

x= [[  2.   3.]
 [  0.  16.]
 [  3.   1.]
 [  2.   8.]]
y= [[ 1.]
 [ 1.]
 [ 0.]
 [ 0.]]
step=100, a1=-22.50, a2=-12.28, b=196.26, loss=42.75
step=200, a1=-35.00, a2=-12.06, b=192.68, loss=25.84
step=300, a1=-47.36, a2=-11.78, b=189.14, loss=9.24
step=400, a1=-55.13, a2=-11.51, b=186.75, loss=2.54
step=500, a1=-58.92, a2=-11.29, b=185.58, loss=0.02
step=600, a1=-59.26, a2=-11.23, b=185.47, loss=0.01
step=700, a1=-59.43, a2=-11.19, b=185.43, loss=0.00
step=800, a1=-59.53, a2=-11.17, b=185.39, loss=0.00
step=900, a1=-59.62, a2=-11.15, b=185.37, loss=0.00
step=1000, a1=-59.68, a2=-11.14, b=185.35, loss=0.00
Estimated: a1=-59.68, a2=-11.14, b=185.35
[[ 0.95848435]]

TensorFlowコトハジメ 八百屋で勾配降下法

はじめに

予測モデルを推測する分かりやすいサンプルを見つけたので、この資料を基に理解していく。

機会学習勉強会 (2016.2.27)のつぶやき

  • 実績データからパラメータを推定。パラメータを使って、結果を予測
  • パラメータ特定できない場合、合計金額の誤差が小さくなるような単価を採用する
  • 全ての組み合わせを検証するのではなく、少ない計算で最短距離で行う
  • 学習:予測式、誤差の計算、誤差の最小とする組み合わせ
  • 先程の例を線形回帰(線形重回帰)の式にする
  • 誤差を決める。誤差関数と最適化手法を記述
  • 学習率が大きすぎても失敗する。

勾配降下法とは

誤差の最小値を求める手法。
パラメータの特定できない場合、誤差が小さくなるような値を採用する。
イメージ例
f:id:Yaju3D:20160419021022p:plain

例題1

たかしくんは八百屋へお使いに行きました。
リンゴ1個とミカン3個を買うと190円,リンゴ3個とミカン1個を買うと330円するようです。
リンゴ2個とミカン4個を買うといくらになるでしょうか? f:id:Yaju3D:20160417225841p:plain f:id:Yaju3D:20160417225852p:plain
このくらいの問題なら、暗算で求めることが出来ます。
f:id:Yaju3D:20160417230109p:plain f:id:Yaju3D:20160417230118p:plain
では、次はどうでしょうか?
f:id:Yaju3D:20160417230234p:plain f:id:Yaju3D:20160417230455p:plain
f:id:Yaju3D:20160417230639p:plain
条件が当てはまる最小の値をひたすら繰り返し計算する。
f:id:Yaju3D:20160417230656p:plain f:id:Yaju3D:20160417230833p:plain
少ない計算で最小値を見つけたい。
f:id:Yaju3D:20160417230848p:plain f:id:Yaju3D:20171029162934p:plain

TensorFlowで同じように書いていく

f:id:Yaju3D:20160417231609p:plain

TensorFlow のほか高速な配列演算パッケージであるNumPyやプロットしたい場合はmatplotlib.pyplotなど使用するライブラリをロードする。

# 必要なモジュールを読み込む
import numpy as np
import tensorflow as tf

予測式(モデル)を記述

# 1. 予測式(モデル)を記述する
# 入力変数と出力変数のプレースホルダを生成
x = tf.placeholder(tf.float32, shape=(None, 2), name="x")
y_ = tf.placeholder(tf.float32, shape=(None, 1), name="y")
# モデルパラメータ
a = tf.Variable(tf.zeros((2, 1)), name="a")
# モデル式
y = tf.matmul(x, a)
  • 値を入れる"箱"を作成
    入出力 → placeholder,パラメータ → Variable
    (注意)shape = (訓練データの数, 次元数)
    ※"訓練データの数"は None にすると可変長扱い

補足説明
プレースホルダ(placeholder)とは、実際の内容を後から挿入するために仮に確保した場所のこと
tf.placeholderでセットした変数 x や y_ は特定の値ではなくplaceholderと呼ばれ
TensorFlowに計算を実行しろと頼むときに入力する値です。

tf.zerosは、要素が全て「0」である0行列をセット 参照:TensorflowのAPIについて

shape=(行, 列)は、各次元の要素数 (行数, 列数)。
shape=(None, 2)は、2列で後でサイズが決まるような場合に使用する。
f:id:Yaju3D:20160619200824p:plain
shape=(None, 1)は、1列で後でサイズが決まるような場合に使用する。
f:id:Yaju3D:20160619200836p:plain

tf.matmulは行列の掛け算(ここでは定義で実際に使うのは、loss = tf.reduce_mean(tf.square(y_ - y)) )
 { \displaystyle \binom{190}{330} = \binom{1\, \, 3}{3\, \, 1}\binom{a1}{a2}}
行列式を使った連立方程式の解き方
ke!san 連立方程式

  • 予測式(モデル)を記述
    f:id:Yaju3D:20160419002003p:plain

.誤差関数と最適化手法を記述

# 2. 学習に必要な関数を定義する
# 誤差関数(loss)
loss = tf.reduce_mean(tf.square(y_ - y))
# 最適化手段を選ぶ(最急降下法)
train_step = tf.train.GradientDescentOptimizer(0.02).minimize(loss)

mean も average も「平均値」ですが、mean のほうが学術用語で、average のほうが普通語です。
参照:meanとaverageの違いは?

  • 誤差関数を記述
    ふつうの回帰問題 → 平均二乗誤差  {\displaystyle \frac{1}{N}\sum_{i}^{n}{{(y_{i} - \tilde{y}_{i})^{2}}}}

yaju3d.hatenablog.jp

  • 最適化手法を選ぶ
    ・入門 → 最急降下法(勾配降下法) GradientDescent~
     ※どれを選ぶかで学習(最適化)の速さが変わる
    ・引数に適度な"学習率"を指定する
     ※大きすぎると学習失敗(発散)、小さすぎると学習が遅い

訓練データを作成(or読込)

# 3. 実際に学習処理を実行する
# (1) 訓練データを生成する
train_x = np.array([[1., 3.], [3., 1.], [5., 7.]])
train_y = np.array([190., 330., 660.]).reshape(3, 1)
print("x=", train_x)
print("y=", train_y)

f:id:Yaju3D:20160419005112p:plain
※予測式で定義した形状(shape)に合わせること
※実用場面では,外部データを(ファイルやSQLなどから) 読みとって2次元配列に整形する。

# (2) セッションを準備し,変数を初期化
sess = tf.Session()
init = tf.initialize_all_variables()
sess.run(init)

# (3) 最急勾配法でパラメータ更新 (100回更新する)
for i in range(100):
    _, l, a_ = sess.run([train_step, loss, a], feed_dict={x: train_x, y_: train_y})
    if (i + 1) % 10 == 0:
        print("step=%3d, a1=%6.2f, a2=%6.2f, loss=%.2f" % (i + 1, a_[0], a_[1], l))


# (4) 学習結果を出力
est_a = sess.run(a, feed_dict={x: train_x, y_: train_y})
print("Estimated: a1=%6.2f, a2=%6.2f" % (est_a[0], est_a[1]))

sess.run ( [出力, ...], feed_dict={入力リスト} )
sess.run を呼び出すことで、"出力"に指定した データフローグラフが計算される
※学習を回すには、先ほど作成した最適化手段(train_step)を出力値に指定して sess.run を呼び出す。

参考: 実行結果

f:id:Yaju3D:20160419010123p:plain

f:id:Yaju3D:20160419010158p:plain

# 4. 新しいデータに対して予測する
# (1) 新しいデータを用意
new_x = np.array([2., 4.]).reshape(1, 2)

# (2) 学習結果をつかって,予測実施
new_y = sess.run(y, feed_dict={x: new_x})
print(new_y)

予測でも sess.run を用いる
feed_dictには新しい入力値を指定することに留意。(当然ですが...)
f:id:Yaju3D:20160419010502p:plain

最終ソースコード

# coding: utf-8
# 必要なモジュールを読み込む
import numpy as np
import tensorflow as tf

# 1. 予測式(モデル)を記述する
# 入力変数と出力変数のプレースホルダを生成
x = tf.placeholder(tf.float32, shape=(None, 2), name="x")
y_ = tf.placeholder(tf.float32, shape=(None, 1), name="y")
# モデルパラメータ
a = tf.Variable(tf.zeros((2, 1)), name="a")
# モデル式
y = tf.matmul(x, a)

# 2. 学習に必要な関数を定義する
# 誤差関数(loss)
loss = tf.reduce_mean(tf.square(y_ - y))
# 最適化手段を選ぶ(最急降下法)
train_step = tf.train.GradientDescentOptimizer(0.02).minimize(loss)


# 3. 実際に学習処理を実行する
# (1) 訓練データを生成する
train_x = np.array([[1., 3.], [3., 1.], [5., 7.]])
train_y = np.array([190., 330., 660.]).reshape(3, 1)
print("x=", train_x)
print("y=", train_y)

# (2) セッションを準備し,変数を初期化
sess = tf.Session()
init = tf.initialize_all_variables()
sess.run(init)

# (3) 最急勾配法でパラメータ更新 (100回更新する)
for i in range(100):
    _, l, a_ = sess.run([train_step, loss, a], feed_dict={x: train_x, y_: train_y})
    if (i + 1) % 10 == 0:
        print("step=%3d, a1=%6.2f, a2=%6.2f, loss=%.2f" % (i + 1, a_[0], a_[1], l))


# (4) 学習結果を出力
est_a = sess.run(a, feed_dict={x: train_x, y_: train_y})
print("Estimated: a1=%6.2f, a2=%6.2f" % (est_a[0], est_a[1]))

# 4. 新しいデータに対して予測する
# (1) 新しいデータを用意
new_x = np.array([2., 4.]).reshape(1, 2)

# (2) 学習結果をつかって,予測実施
new_y = sess.run(y, feed_dict={x: new_x})
print(new_y)

# 5. 後片付け
# セッションを閉じる
sess.close()

実行結果

リンゴ 98.81 ≒ 99、ミカン 24.90 ≒ 25 → 99 x 2 + 25 x 4 ≒ 297  

x= [[ 1.  3.]
 [ 3.  1.]
 [ 5.  7.]]
y= [[ 190.]
 [ 330.]
 [ 660.]]
step= 10, a1= 70.35, a2= 46.23, loss=2189.06
step= 20, a1= 83.06, a2= 36.70, loss=771.90
step= 30, a1= 90.13, a2= 31.41, loss=334.34
step= 40, a1= 94.05, a2= 28.47, loss=199.24
step= 50, a1= 96.23, a2= 26.84, loss=157.52
step= 60, a1= 97.44, a2= 25.93, loss=144.64
step= 70, a1= 98.12, a2= 25.42, loss=140.67
step= 80, a1= 98.49, a2= 25.14, loss=139.44
step= 90, a1= 98.70, a2= 24.99, loss=139.06
step=100, a1= 98.81, a2= 24.90, loss=138.94
Estimated: a1= 98.81, a2= 24.90
[[ 297.22738647]]

TensorFlowコトハジメ フィボナッチ数列

はじめに

前回、TensorFlowの概要記事を書いた。概要としてはまだ足りないのだが、次に進まないとならない。
TensorFlowのプログラムを組むのに簡単な例として、フィボナッチ数列が良さそうなので、これを題材として理解してみる。

ネットで検索したフィボナッチ数列のPython用プログラムを参考とする。
参照:Python(11)再帰のお勉強:フィボナッチの数列

フィボナッチ数列(Fibonacci number)とは

1+1=2、1+2=3、2+3=5、3+5=8… と、前項2つを加えて、次々とできる数の列のことをフィボナッチ数列といいます。
f:id:Yaju3D:20160417173452p:plain
このフィボナッチ数列の隣接する2つの数の比は限りなく黄金比に近づいています。花びらの数、葉のつき方など自然界にもこの周期が現れるものも多くあります。 また、名刺の縦横比、建物や絵画、彫刻などのバランスの美しさにも、この黄金比が現れていることが多いのです。
blog.livedoor.jp

{F_{0}=0}
{F_{1}=1}
{F_{n}=F_{n-1}+F_{n-2}} {(n>1)}

この漸化式で表せる数列です。
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, …

プログラム 1

d136o/tensor_overflow.py

import argparse
import tensorflow as tf
import sys

def flowfib(n):
    # f_n = f_n-1 + f_n-2
    f = [tf.constant(0),tf.constant(1)]
    
    if n>2:
        for i in range(2,n):
            f_i = f[i-1] + f[i-2]
            f.append(f_i)
    
    with tf.Session() as sess:
        result = sess.run(f)
        print(result)

flowfib(20)

#[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181]

説明

[TensorFlow] APIドキュメントを眺める -Tensor編-

整数リテラルを使って生成した Tensor については tf.int32 が要素の数値型として適用される模様です。

import tensorflow as tf
a = tf.constant(0)
a.dtype

# tf.int32

appendは、Pythonの要素追加メソッドです。

f.append(f_i)

プログラム 2

qiita.com

import tensorflow as tf

u = tf.Variable(tf.cast(1,"int64"))
v = tf.Variable(tf.cast(0,"int64"))

update_u = tf.assign(u, tf.add(u,v)) # u = u + v
update_v = tf.assign(v, tf.sub(u,v)) # v = u - v

init = tf.initialize_all_variables()

with tf.Session() as sess:
    sess.run(init)
    for i in range(100):
        print(i, sess.run(v))
        sess.run(update_u)
        sess.run(update_v)

説明

定義の値の更新の代入にはtf.assignを使う。足し算(tf.add)、引き算(tf.sub)を使う。 https://www.tensorflow.org/versions/0.6.0/api_docs/python/state_ops.html#assign

update_u = tf.assign(u, tf.add(u,v)) # u = u + v
update_v = tf.assign(v, tf.sub(u,v)) # v = u - v

セッションの最初に変数を初期化する必要がある。これには、init = tf.initialize_all_variablesを使う。sess.run(init)にて初期化を実行する。

init = tf.initialize_all_variables()

sess.run(init)

sess.runにより値を取ったり、手続きを実行したりします。

print(i, sess.run(v)) #値の取得
sess.run(update_u)   #定義の実行
sess.run(update_v)   #定義の実行

最後に

モデルを定義してから実行するというのが、今までの習っていたプログラムと違うので、慣れる必要がありますね。 mirai-tec.hatenablog.com

TensorFlowコトハジメ 概要

はじめに

静岡Developers勉強会では、今年の勉強会のテーマとして「人工知能ハンズオン」を2016/4/23に開催します。
これまでインストール記事ばかりで中身が書けていなかったので、概要から書いていきます。

TensorFlowとは

読み方は「テンソルフローテンサーフロー」となります。【2017/02/18 訂正】googleの中の人がテンサーフローと呼んで欲しい
Googleが2015年11月10日に公開したオープンソース多層NN(ディープラーニング)に特化したライブラリで現状C++/PythonでのAPIが存在しています。
Tensor(テンソル)のFlow(流れ)で状態記述します。

ディープラーニングやマシーンラーニングの使いやすいライブラリが整ってはいるものの、それらに特化したフレームワークではなく、多次元配列に入る数字を非常に高速に計算するための汎用のHPCフレームワークとしても使うことができます。

Tensor(テンソル)とは

テンソル(英: tensor, 独: Tensor)とは、 線形的な量または線形的な幾何概念を 一般化したもので、基底を選べば、多次元の配列として表現できるようなものである。 Wikipedia

乱暴に言えば多次元配列に相当する

  • 添え字のついた変数(配列)
    { X_{ij} } { Z_{ijk} }
  • スカラー、ベクトル、行列の一般名

f:id:Yaju3D:20160417104606p:plain

テンソルデータ例

  • 濃淡画像  ・・・ 2階のテンソル
  • カラー画像 ・・・ 3階のテンソル(RGB x 濃淡画像)
  • カラー動画 ・・・ 4階のテンソル(フレーム x カラー画像)

可視化する限度は3階までが限界
f:id:Yaju3D:20160417031730p:plain
図1: ユーザ × 商品 × 時間の3階テンソルとして表現された購買データ
テンソルというのはテーブル形式のデータを多次元に拡張したもので、例えば、複数のユーザがいくつかの種類の商品をある時刻に購入したという購買データを考えるとユーザ × 商品 × 時間の3階テンソルとして表現することができます。

また、テンソルとはごく簡単にいえば「任意座標系で表した量」ということになります。図1だと、購入商品(item)の3個(Qty)が量となります。

f:id:Yaju3D:20160417025250p:plain
他にも2次元画像をRGBに分解して、任意座標のピクセル値(R * 2562 + G * 256 + B)が量となります。

Tensor(テンソル)の計算

高校の数学で行列同士の計算(加算、減算、積、商(逆行列))を習いますが、テンソル同士も行列と同じように計算することが出来ます。計算方法はここでは説明しません。 参照:TensorFlow APIドキュメントを眺める -Math編-

計算例 [1.1.1.1.]+[2.2.2.2.]=[3.3.3.3.]

import tensorflow as tf

with tf.Session():
  input1 = tf.constant([1.0, 1.0, 1.0, 1.0])
  input2 = tf.constant([2.0, 2.0, 2.0, 2.0])
  output = tf.add(input1, input2)
  result = output.eval()
  print(result)

#[ 3.  3.  3.  3.]
畳み込み演算とは?

「畳む」…複数ヶ所(連続的)から値を持ってくる
「込む」…積算する
掛け算の結果を足し集める演算からなり、大量な計算が必要になることからコンピュータ処理の得意とするところです。
数学用語ではコンボリューション(Convolution)といいます。

畳み込み演算の簡単な例として、画像処理ソフトウェアで画像を「ぼかす」フィルターをかける場合を考えます。これは、画像の各ピクセルにおいて、その部分の色をその周りのピクセルの色とまぜて平均化した色に置き換えれば実現できます。その際の掛け算の係数は、3×3などのサイズのマトリックスで指定します。参照:空間フィルタリング処理 f:id:Yaju3D:20160417025351p:plain 3×3サイズのマトリックスを1ピクセルずらしながら演算する。
f:id:Yaju3D:20160417025404g:plain
畳み込み結果の画像が入力サイズより一回り小さくなります。もし同サイズにしたい場合には外側に「ふち」をつけて大きくします。
「ふち」の部分の画素は未定なので何らかの方法(最周囲の画素等)で決める必要があります。

TensorFlowの計算処理

テンソルを計算するには、かなりの計算量が必要となります。CPUが複数あれば並列で計算させることで結果が速く出せますし、GPUを使えば多くの計算結果が速く導くことが可能となります。またクラウドを使って、1台ではなく複数台のコンピューターに分散させればそれだけ速く計算結果を求めることが出来るのです。
そのために、TensorFlowは計算処理を「データフローグラフ」(以降 略:グラフ(Graph))構造で表します。

グラフとは

グラフはノード(Node)といわれる計算ブロックのようなものをエッジ(edge)といわれる線で繋げたもので、データはノードからノードへ、エッジのつながり方に従って受け渡され、計算が行われます。 f:id:Yaju3D:20160417025430p:plain

Opノード

TensorFlowではノードは「op」(operation:計算操作の略)と言われます。 f:id:Yaju3D:20160417025458p:plain

セッション

グラフはセッションに割り当てて計算を行います。セッションは、計算可能になったノード(エッジから送られてくる計算結果がすべてそろったノード)を非同期/並列に計算していきます。
計算に際しては、どのデバイス(cpu/gpu)で行うのかの割振りも行います。 f:id:Yaju3D:20160417025520p:plain

計算方法

TensorFlowでは計算処理は2フェイズ(Define & Run)で行います。

  • Define(定義)
    計算のグラフモデルを構築するフェイズで、この時点では計算結果は確定しない
  • Run(実行)
    グラフモデルから計算結果を確定するフェイズでセッション(Session)にモデルを投入し、計算結果を得る
TensorFlowによる計算プログラム

Pythonの通常の計算プログラム

x = 35
y = x + 5
print(y)

TensorFlowで同じように計算プログラム

import tensorflow as tf

x = tf.constant(35, name='x')
y = tf.Variable(x + 5, name='y')

print(y)

これを実行するとエラー<tensorflow.python.ops.variables.Variable object at 0x7f074bfd9ef0>になります。
これはモデルを定義しただけでは計算が出来ないためです。
計算処理は2フェイズ(Define & Run)する必要があり修正したのが下記となります。

import tensorflow as tf

x = tf.constant(35, name='x')
y = tf.Variable(x + 5, name='y')

# 変数を初期化するための OP を追加します。
model = tf.initialize_all_variables()

with tf.Session() as session:
    session.run(model)
    print(session.run(y)

実際にデータを処理するにはtf.Sessionを使います。セッションの最初に変数を初期化する必要があり、これには「tf.initialize_all_variables」を使います。
これにより計算結果「40」が表示されます。
参照:http://learningtensorflow.com/lesson2/

参照

テンソル分解の基礎と 画像・信号処理への応用 - SlideShare
TensorFlowで遊んでみよう! - SlideShare
Generalization of Tensor Factorization and Applications SlideShare
Bci deep learning_juas - SlideShare
第13回助教の会
コンボリューションを用いた画像の平滑化、鮮鋭化とエッジ検出
Image Scaling using Deep Convolutional Neural Networks
How common is it for neural networks to be represented by tensors of rank 3 or greater?
TensorFlow Tutorialの数学的背景 − Deep MNIST for Experts(その1)- めもめも
4.たたみ込み演算による画像処理
TensorFlowを算数で理解する
TensorFlowのキーコンセプト: Opノード、セッション、変数

スポンサーリンク