Comet.mlでJupyter Notebookの学習を記録・レポートする

Comet.mlを使ってJupyter NotebookなどのPythonの学習を記録・レポートする方法についてまとめます。

[7/18 追記]
本投稿では、稼働環境としてGoogle Colaboratoryを使ってます。環境によってデフォルトで送られるデータに差がありますので、注意が必要です。

Comet.ml

Jupyter Notebookの実験結果を良い感じに・楽に記録したいケースがよくあります。

  • ハイパパラメータやシステム設定などの実験条件をトラックしたい
  • トレーニングの経過をビジュアライズしたい

Comet.mlはこうしたことを実現するサービスです。

  • workspace配下に、複数のprojectを作成し、各プロジェクトでexperimentをトラックする、という構造になってます
  • パブリックなプロジェクトであれば無料で利用できます

www.comet.ml

実装

Keras: スパムメッセージをLSTMで分類する - け日記 の実験をComet.mlでレポートできるようにしてみましょう。

ohke.hateblo.jp

comet_mlのインストールとインポート

comet_mlのパッケージをインストールします。このパッケージをノートブック上でインポートすることで

$ pip install comet_ml

インストールしたcomet_mlパッケージをインポートするのですが、注意すべき点としてtensorflowやKerasより前にインポートする必要があります (後でインポートするとエラーになります) 。

from comet_ml import Experiment  # tensorflowやKerasの前にインポートする

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
from keras.models import Sequential
from keras.layers import Input, LSTM, Dense, Embedding

実験の開始

Experimentクラス (APIはこちら) をインスタンス化すると、実験開始となります。
このExperimentクラスがキモとなります。以降はこのインスタンスを介して実験を記録していきます。APIリファレンスを見るとcomet.mlでできることが概ね把握できます。

# Experimentを作る
experiment = Experiment(
    api_key='xxxxxx',  # ワークスペースに紐づくAPIキー
    project_name='lstm_spam'  # プロジェクト名
)

これでComet.mlのワークスペースに、指定したプロジェクト名で新規作成されます。

プロジェクト (ここではlstm-spam) を見ると、experimentsに追加されていきます。

f:id:ohke:20190713112250p:plain

なおAPIキーはこちらで確認できます。

f:id:ohke:20190713084938p:plain

モデリングと学習・推論

あとは以前の投稿と同様にデータロード、前処理、モデル生成を実装していきます。

# ハイパパラメータ
hp = {
    'epochs': 10,  # 学習エポック数
    'batch_size': 32,  # 学習バッチサイズ数
    'optimizer': 'adam',  # 最適化アルゴリズム
    'max_len': 100,  # 1メッセージの最大単語数 (不足分はパディング)
    'embedding_output_dim': 32,  # Embedding層の出力次元数
    'lstm_hidden_units': 16,  # LTSM層の隠れユニット数
    'lstm_activation': 'tanh',  # LSTM層の活性化関数
}

# データロード
dataset_df = pd.read_csv('./SMSSpamCollection', sep='\t', header=None)
dataset_df.rename({0: 'label', 1: 'text'}, axis=1, inplace=True)
dataset_df['category'] = dataset_df.apply(lambda r: 1 if r['label'] == 'spam' else 0, axis=1)

X_train, X_test, Y_train, Y_test = train_test_split(
    dataset_df[['text']], dataset_df[['category']], 
    test_size=0.2, random_state=0
)

# 入力の前処理
tokenizer = Tokenizer()
tokenizer.fit_on_texts(X_train['text'])
x_train = tokenizer.texts_to_sequences(X_train['text'])
x_test = tokenizer.texts_to_sequences(X_test['text'])
x_train = pad_sequences(x_train, maxlen=hp['max_len'])
x_test = pad_sequences(x_test, maxlen=hp['max_len'])

# 出力の前処理
y_train = Y_train['category'].values
y_test = Y_test['category'].values

# モデル生成
vocabulary_size = len(tokenizer.word_index) + 1  # 学習データの語彙数+1
model = Sequential()
model.add(
    Embedding(input_dim=vocabulary_size, output_dim=hp['embedding_output_dim'])
)
model.add(
    LSTM(hp['lstm_hidden_units'], activation=hp['lstm_activation'], return_sequences=False)
)
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer=hp['optimizer'], metrics=['accuracy'])

学習・推論では、以下のようにそれぞれtraintestメソッドを呼び出してwith句でくくることで、結果がComet.mlにアップロードされます。

# 学習
with experiment.train():
  history = model.fit(
      x_train, y_train, batch_size=hp['batch_size'], epochs=hp['epochs'],
      validation_data=(x_test, y_test)
  )

# 推論
with experiment.test():
  y_pred = model.predict_classes(x_test)

するとこんな感じでChartsから学習過程などをグラフで見ることができるようになります。

f:id:ohke:20190713114528p:plain

また最終的なaccuracyなどの数字はMetricsで確認できます。

f:id:ohke:20190713185357p:plain

実験条件のロギング

最後に実験条件を送ります。詳しくはAPIドキュメントを見ていただければと思いますが、log_*メソッドで送ることができます。

# 各種条件を履歴に残す
experiment.log_dataset_hash(x_train)  # 学習データ (ハッシュ値)
experiment.log_parameters(hp)  # ハイパパラメータ

Hyper parametersで送った値が確認できます。特に指定が無い場合でも、最適化アルゴリズムのパラメータなどはデフォルトで残します。

f:id:ohke:20190713113157p:plain

リソース状況などは自動的に記録されており、System Metricsで確認できます。ボトルネックの洗い出しなどはさすがにできませんが、逼迫してないか・有効活用できているかなどのチェックには十分です。

f:id:ohke:20190713114053p:plain

実験の終了

実験完了後はendでクローズします。

# 実験終了
experiment.end()

使う上でのTips

上述した方法で一通りのことはできるようになりましたが、その他実際に使う上で便利なTipsを紹介します。

任意の情報を残したい (log_other)

メトリックスやハイパパラメータ以外に、実験に付随した情報を残したい場合、log_otherメソッドを使うと良いです。
例えば、False negativeとFalse positiveの件数を残したい場合、以下のように記述できます。

  • 第1引数がキー、第2引数が値となります
# FN/FPを送る
cm = confusion_matrix(y_test, y_pred)
experiment.log_other('false_negative', cm[1, 0])
experiment.log_other('false_positive', cm[0, 1])

comet.mlでOtherの項目をそれぞれ残っていることがわかります (train_trainable_paramsが送られているのはデフォルトでしょうか) 。

Jupyter Notebookで結果を見たい (display)

Jupyter Notebookでも学習経過を出力したい場合、matplotlibなどで可視化もできますが、displayメソッドでcomet.mlをiframeで表示することもできます。

f:id:ohke:20190713182435p:plain

コードを残したい (set_code)

set_codeメソッドで、実験に使ったコードを残すこともできます。Jupyter NotebookではInに実行したコードをリストで持ってますので、つなげて出力してます。

# コードを出力
code = ''
for cell_in in In:
  code += cell_in + '\n'

experiment.set_code(code)

comet.mlのCodeを開くとビジュアライズされたコードが表示されます。 api_keyの値が消されてて、地味な気遣いを感じます。

まとめ

Jupyter Notebookの学習をComet.mlを使って記録・レポートできるようになりました。