Keras metrics=[‘accuracy’]とは厳密にはどんな評価関数なのか

Kerasでディープラーニングの開発を進めていますが、よく

model = Sequential()
model.add(Dense(512, activation='relu', input_shape=(784,)))
model.add(Dropout(0.2))
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(10, activation='softmax'))

model.compile(loss='categorical_crossentropy',
              optimizer=RMSprop(),
              metrics=['accuracy'])

ってやってました。

しかし、ある時

metrics=['accuracy']

ってなんだ??という疑問にブチ当たりました。(´・ω・)

metrics=['acc']

も同様です。

Kerasの本家サイトによると

https://keras.io/ja/metrics/

accuracyって名前のmetricsないんですよね (;^ω^)

幸いなことに、下記のStack Overflowさんに答えがありましたー!

https://stackoverflow.com/questions/41531695/how-does-keras-define-accuracy-and-loss

デフォルトはcategorical_accuracyだそうです。

下記のKerasのソースコードを見ると、損失関数のタイプとか、出力のシェイプっぽいのを見て、binary_accuracy にしてくれることもあるようです。

https://github.com/keras-team/keras/blob/d8b226f26b35348d934edb1213061993e7e5a1fa/keras/engine/training.py#L651

しかし!はっきりしていた方がいいですから、自分で指定しておいた方が私は好きですね~。

ちなみに、勉強している元ネタ本はこちら。

PythonとKerasによるディープラーニング

無駄にややこしい部分があるので、オススメ本ではありませんw

TensorBoard found more than one metagraph

Tensorflow で色々と試行錯誤しています。

以前、下記の記事でTensorBoardという、TensorFlowについている、計測ツールについて紹介しました。

WindowsでTensorBoardを使う

で、そのTensorBoardで下記のエラーが出ちゃいました。

tensor board found more than one metagraph

うーん。ググっても、TensorFlowの1.4では治っているよ~ という下記の記事などや、結構複雑なStack Overflowさんの記事などが出てきてしまいます(;´・ω・)。

https://github.com/GoogleCloudPlatform/cloudml-samples/issues/73

 

はたと思いついたのが、TensorBoardのためのログって、下記のようなファイルで出力されるじゃないですか。

events.out.tfevents.153294*****.3ec855******

それで、TensofBoardを動作させるときは、

tensorboard --logdir=C\hogehoge

みたいにディレクトリを指定しますよね。

C\hogehoge 内にある、TensofBoardのログファイルで、同じタイムスタンプのものが複数あったので、一つを残して消してみたら、普通にTensofBoardさんが動作しました!!

タイムスタンプでログファイル見てるのね。

タイムスタンプが同じものがあると、タイトルのエラーが出るようです。

私の場合は、別の機械学習用のサーバーでAIのコード動作させてて、TensofBoardのログだけ、自分のローカルPCで見ようと思って、ダウンロードしたので、同じタイムスタンプのログがいっぱいあった、ということです。

 

TensorBoard+Keras グラフの名前を変えてみる

昨日の

WindowsでTensorBoardを使う

の続きです。

TensorBoardにグラフ機能というのがあります。

上のメニューで、GRAPHSというのをクリックします。

わけのわからない図が出てきます。

 

図のところだけ拡大すると、こんな風。

TensorBoardの公式サイトに、チュートリアルの動画がありました。なかなかよい動画です。

余談ですが、いつも開発者の動画とかあると、見ちゃいますね。

結局、とあるプログラムがあったとして、その開発者以上にそのプログラムのことをわかっている人間はいないと思います。

 

で、この動画の中にもとりあえず、名前をつけてみることがよい!というお話がありましたので、名前をつけてみます。

名前をつけるやり方です。(非常に単純な話で申し訳ありませんが…。)

前述の記事内のコードに、名前をつける部分を足しただけです。

import keras
import tensorflow as tf

from keras.datasets import imdb
from keras.preprocessing import sequence

max_features = 2000  # number of words to consider as features
max_len = 500  # cut texts after this number of words (among top max_features most common words)

print('Loading data...')
(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=max_features)
print(len(x_train), 'train sequences')
print(len(x_test), 'test sequences')

print('Pad sequences (samples x time)')
x_train = sequence.pad_sequences(x_train, maxlen=max_len)
x_test = sequence.pad_sequences(x_test, maxlen=max_len)
print('x_train shape:', x_train.shape)
print('x_test shape:', x_test.shape)

from keras.models import Sequential
from keras import layers
from keras.optimizers import RMSprop

model = Sequential()
model.add(layers.Embedding(max_features,
                            128,
                           input_length=max_len
                           ))
model.add(layers.Conv1D(32, 7, activation='relu', name="first_layer"))
model.add(layers.MaxPooling1D(5))

with tf.name_scope("second_layer"):
    model.add(layers.Conv1D(32, 7, activation='relu',name="hogehoge"))
model.add(layers.GlobalMaxPooling1D())
model.add(layers.Dense(1))

model.summary()

model.compile(optimizer=RMSprop(lr=1e-4),
              loss='binary_crossentropy',
              metrics=['acc'])


callbacks = [
    keras.callbacks.TensorBoard(
        log_dir = 'my_log_dir',
        histogram_freq = 1

    )
]
history = model.fit(x_train, y_train,
                    epochs=5,
                    batch_size=128,
                    validation_split=0.2,
                    callbacks=callbacks)

最初の畳み込みレイヤー・Conv1Dに”first_layer”という名前をつけたのと、次の畳み込みレイヤー・Conv1Dにグルーピングの名称”second_layer”という名前をつけ、レイヤー自体には”hogehoge”という名前をつけています。

ただ単に、私がグルーピングを試してみたかっただけです。

赤丸のように、名前が変わりましたね!

マウスを当てて、右上にプラスが出てくる部分は、ダブルクリックで開けます。

より、詳しい内容がわかります。

前のレイヤーからのデータが、hogehogeレイヤーの中で、畳み込みネットワークに入り、カーネル(畳み込みレイヤーのフィルターのことです)とか、Biasが作用している感じがわかると思います。

 

WindowsでTensorBoardを使う

さてさて、私は今のところ、ディープラーニングのコードを

Keras+TensorFlow

で作っています。

TensorFlowには、TensorBoardという便利ツールがあると聞き、使ってみます。

勉強している元ネタの本はこちら。

PythonとKerasによるディープラーニング

無駄にややこしい部分があるので、オススメ本ではありませんw

とりあえずの~ TensorBoardを使ってみたいだけのテストコードを書きます。

import keras

from keras.datasets import imdb
from keras.preprocessing import sequence

max_features = 2000  # number of words to consider as features
max_len = 500  # cut texts after this number of words (among top max_features most common words)

print('Loading data...')
(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=max_features)
print(len(x_train), 'train sequences')
print(len(x_test), 'test sequences')

print('Pad sequences (samples x time)')
x_train = sequence.pad_sequences(x_train, maxlen=max_len)
x_test = sequence.pad_sequences(x_test, maxlen=max_len)
print('x_train shape:', x_train.shape)
print('x_test shape:', x_test.shape)

from keras.models import Sequential
from keras import layers
from keras.optimizers import RMSprop

model = Sequential()
model.add(layers.Embedding(max_features,
                            128,
                           input_length=max_len,
                           name='embed'))
model.add(layers.Conv1D(32, 7, activation='relu'))
model.add(layers.MaxPooling1D(5))
model.add(layers.Conv1D(32, 7, activation='relu'))
model.add(layers.GlobalMaxPooling1D())
model.add(layers.Dense(1))

model.summary()

model.compile(optimizer=RMSprop(lr=1e-4),
              loss='binary_crossentropy',
              metrics=['acc'])

#下記から下がTensorBoardで記録するための部分
callbacks = [
    keras.callbacks.TensorBoard(
        log_dir = 'my_log_dir',
        histogram_freq = 1

    )
]
history = model.fit(x_train, y_train,
                    epochs=10,
                    batch_size=128,
                    validation_split=0.2,
                    callbacks=callbacks)

映画のレビューデータである、IMDBの分析ですね。

これを実行しますと、実行したプログラムと同じディレクトリ(ここではC:\kerasStudy\my_log_dir)に

events.out.tfevents.1532589203.HOGEHOGEPC

というファイルなどができたりします。

私は、Anacondaを使っているので、Anaconda Promptを立ち上げます。

んで、

C:\Users\Hogehoge>tensorboard --logdir=C:\kerasStudy\my_log_dir

と打ち込みますと、

最後に

TensorBoard 1.8.0 at http://hogehogePC:6060

と出てきます。

後は、ブラウザから、

http://hogehogePC:6060

にアクセスすると、TensorBoardが開けます!!

感動ひとしお~

TensorBoardの使い方に関しては、改めて書きたいと思いますが、

下記の赤丸の部分にチェックが入っていないと、グラフが表示されません。

PHPでBuilderパターン コンストラクタに渡すパラメーターの数を時に応じて変えたい

オブジェクト指向あるある…

必須にしたいパラメーターを引数にしてコンストラクタに渡すのですが、引数が時に応じて数が変わると、その都度コンストラクタの違うバージョンを書かなきゃいけないという問題があります。

例えば、ここでの例を述べると、配送計画(Delivery Plan)というオブジェクトには、IDと日付と距離は必ず入りますが、車両IDとドライバー名はある時もないときもあります。

そういう時、たいてい下記のようにコンストラクタをコピペして初期パラメーターを設定しますね。

class DeliveryPlanBuilder{
private $id;
private $date;
private $distance;

//オプションパラメーター
private $car_id;
private $driver_name;

function __construct()
    {
        $a = func_get_args();
        $i = func_num_args();
        if (method_exists($this,$f='__construct'.$i)) {
            call_user_func_array(array($this,$f),$a);
        }
}

public function __construct3($id, $date, $distance){
    $this->id = $id;
    $this->date = $date;
    $this->distance = $distance;
}

public function __construct4($id, $date, $distance, $car_id){
    $this->id = $id;
    $this->date = $date;
    $this->distance = $distance;
    $this->car_id = $car_id;
}
public function __construct5($id, $date, $distance, $car_id, $driver_name){ 
    $this->id = $id; 
    $this->date = $date; 
    $this->distance = $distance; 
    $this->car_id = $car_id; 
    $this->driver_name = $driver_name; 
} 
}

まぁ、3,4個のパラメーターならコピペでもいいかもしれませんが、10個とかなってくると、いくつもコンストラクタを作らないといけないのが苦痛ですね!

ここで、デザインパターンのBuilderパターンを使うといいお!というのが、

「Effective Java」

という本の第二章の2に紹介されております。もちろん、Javaでやる方法はこの本に紹介されています。

これをPHPで実装したい、というのが今回の目的です。

こちらのStack Overflowさんを参考にさせて頂きました。m(_ _)m

https://stackoverflow.com/questions/10961673/php-builder-pattern-without-inner-classes

class DeliveryPlan{ 
   private $id; 
   private $date; 
   private $distance; 
   //オプションパラメーター 
   private $car_id; 
   private $driver_name; 

static function createBuilder($id, $date, $distance) { 
    return new DeliveryPlanBuilder($id, $date, $distance); 
} 

function __construct(DeliveryPlanBuilder $builder){ 
    $this->id = $builder->getId();
    $this->date = $builder->getDate();
    $this->distance = $builder->getDistance();
		
    //オプションパラメーター
    $this->car_id = $builder->getCarId();
    $this->driver_name = $builder->getDriverName();
    }
	
public function getDriverName(){
    return $this->driver_name;
    }
		
}

class DeliveryPlanBuilder{
	private $id;
	private $date;
	private $distance;
	
	//オプションパラメーター
	private $car_id;
	private $driver_name;
	
	public function __construct($id, $date, $distance){
		$this->id = $id;
		$this->date = $date;
		$this->distance = $distance;
	}
	
	public function car_id($val){
		$this->car_id = $val;
		return $this;
	}
	
	public function driver_name($val){
		$this->driver_name = $val;
		return $this;
	}
	
	public function build(){
		return new DeliveryPlan($this);
	}
	
	public function getId(){
		return $this->id;
	}
	
	public function getDate(){
		return $this->date;
	}
	
	public function getDistance(){
		return $this->distance;
	}

	public function getCarId(){
		return $this->car_id;
	}
	
	public function getDriverName(){
		return $this->driver_name;
	}
}


$delivery_plan = DeliveryPlan::createBuilder(1, "2018/07/24", 150)->build();

var_dump($delivery_plan);

$delivery_plan_with_driver_name = DeliveryPlan::createBuilder(1, "2018/07/24", 150)->driver_name("山田太郎")->build();

var_dump($delivery_plan_with_driver_name);

$driver_name = $delivery_plan_with_driver_name->getDriverName(); 

var_dump($driver_name);

//下記は必須の引数がないのでエラーになります。
$delivery_plan_test_no_parameters = DeliveryPlan::createBuilder()->build();

出力は下記の通りです。

object(DeliveryPlan)[2]
  private 'id' => int 1
  private 'date' => string '2018/07/24' (length=10)
  private 'distance' => int 150
  private 'car_id' => null
  private 'driver_name' => null

object(DeliveryPlan)[3]
  private 'id' => int 1
  private 'date' => string '2018/07/24' (length=10)
  private 'distance' => int 150
  private 'car_id' => null
  private 'driver_name' => string '山田太郎' (length=12)

string '山田太郎' (length=12)

後はエラー

ちなみに、私はBuilderパターン大好きで、デザインパターンのなかで一番多く使ってると思います。

作るときは面倒ですが、一度作っちゃった後のメンテナンスの容易さがたまらないです。

オブジェクト作るときに、->build()って作るのもなんとなくかっこいいのだ(`・ω・´)

Javaでも、他の言語でも、なんとかかんとかビルダー->build() ってやって作るオブジェクトは、このBuilderパターンで作られているわけですね。