TFMAで始めるBigQuery MLのモデル分析
はじめに
BigQuery Advent Calendar 2020 16日目の記事になります。
BigQuery便利ですよね。
ビッグデータの分析を素早く楽にしてくれて、雑なクエリを書いても高速に結果を返してくれる。
機械学習モデルもBigQuery MLを使えばSQLから簡単に作ることもできます。
BigQuery MLの素晴らしいところはBigQueryの中で閉じていることだと思いますが、作成された機械学習モデルを評価する、特に可視化して分析をするといった時にはBigQueryの中に閉じた状態でなんとかするのは難しいのかなと思います。
そこで今回はTensorFlow Model Analysis(以下TFMA)を用いて可視化とモデルの分析を簡単にできる方法をご紹介しようと思います。
今回の検証ではtensorflow_model_analysis==0.25.0を利用しました。
また、モデル自体はBigQuery MLで出生児体重を予測するをほとんどそのまま利用しています。
colabノートブックを作成したので、こちらを参照ください.
TFMAで分析するための手順
大雑把ですが以下の5つの手順でできます。
- BigQuery MLでモデルを訓練をする
- BigQuery MLの予測結果をpandas.DataFrameにする
-
tfma.EvalConfig
を作る -
tfma.analyze_raw_data
で分析結果を作成する -
tfma.view.render_slicing_metrics
で分析結果を可視化する
実際にTFMAを利用するのは3~5なので、実際にやることはそんなに多くはないです。
簡単、簡単😄
実際にやってみる
1. BigQuery MLでモデルを訓練をする
CREATE MODELを使って、線形回帰モデルを作成します。
チュートリアルだとRAND()関数を使って訓練データをサンプリングしているのですが、再現性があるのかを私が理解できておらずFARM_FINGERPRINT
を利用したものに変更しています。
利用したクエリは以下の通りです。project_idを指定していないので、実行するプロジェクトにbqml_tutorialというデータセットがあることを暗黙で仮定しているので注意してください
CREATE MODEL IF NOT EXISTS
`bqml_tutorial.natality_model` OPTIONS (model_type='linear_reg',
input_label_cols=['weight_pounds']) AS
SELECT
weight_pounds,
is_male,
gestation_weeks,
mother_age,
CAST(mother_race AS string) AS mother_race
FROM
`bigquery-public-data.samples.natality`
WHERE
weight_pounds IS NOT NULL
AND MOD(ABS(farm_fingerprint(FORMAT("%d:%d:%d", year, month, day))),100) <= 1
2. BigQuery MLの予測結果をpandas.DataFrameにする
ここでは、予測値とラベルに加えて分析したい特徴量列を追加したデータフレームを作成します。基本的にはチュートリアルのままですが、カテゴリーとして扱われる列が欲しかったのでstate
列を追加しています。(いくつかあるカテゴリー列の中でstate
を選択した理由は特にないです。)
Colab(Jupyter Notebook)で実行しているので、%%bigquery
を使ってpandas.DataFrameを作成しました
SELECT
*
FROM
ML.PREDICT(MODEL `bqml_tutorial.natality_model`,
(
SELECT
is_male,
gestation_weeks,
mother_age,
state,
weight_pounds,
CAST(mother_race AS STRING) AS mother_race
FROM
`bigquery-public-data.samples.natality`
WHERE
weight_pounds IS NOT NULL
AND MOD(ABS(farm_fingerprint(FORMAT("%d:%d:%d", year, month, day))),100) = 2 ))
tfma.EvalConfig
を作る
3. ここまででデータとモデルの準備はできたので分析に必要なTFMAの設定値を作成していきましょう。
tfma.EvalConfigを作成するのに必要なものは3つあります。
- モデルの設定値の
tfma.ModelSpec
- モデルを評価する評価の設定値の
tfma.MetricsSpec
- 分析したい特徴量の設定値の
tfma.SlicingSpec
少し詳細に見ていきましょう
1. tfma.ModelSpecを作成する
tfma.ModelSpec
は端的に言えばモデルの設定値です。
TensorFlowのsignatureやTFXのmodel_validateのためのベースラインかどうかなどのいろんな設定ができます。
今回のケースでは特定のモデルに依存しないModel Agnosticとして扱いたいので、prediction_key
とlabel_key
を設定してあげればtfma.ModelSpec
の作成は完了です。
コードは以下の通りです
LABEL = LABEL = 'weight_pounds'
model_specs = [tfma.ModelSpec(prediction_key=f'predicted_{LABEL}', label_key=LABEL)
2. tfma.MetricsSpecを作成する
tfma.MetricsSpec
はtfmaで分析をする際に評価する評価値の設定になります。評価値はtf.keras.metrics.*
またはtfma.metrics.*
にあるものが利用可能です。
また, tf.keras.metrics.Metrics
かtfma.metrics.Metrics
を継承することで、自作したものも利用することができます。
TensorFlow Model Analysis Metrics and Plotsに記載されてるので、興味がある方はご覧ください。
今回の問題は回帰にあたるため、MeanSquaredErrorを設定してみましょう。
metrics = [
tf.keras.metrics.MeanSquaredError(name='mse')
]
metrics_specs = tfma.metrics.specs_from_metrics(metrics)
これでtfma.MetricsSpec
の作成は完了です
3. tfma.SlicingSpecを作成する
tfma.SlicingSpec
はどの特徴量を分析したいかというものを記載した設定値になります。
そして、このSlicingというのがTFMAの大きな特徴だと思っています。
Slicingの厳密な定義についてはまだ理解不足な点があるのですが、ある特徴量を考えたときの部分集合という理解を私はしています。
少し例を用いて説明します。
以下の3つの要素を持つカテゴリー変数を考えてみましょう。
category_variable |
---|
a |
b |
c |
ここでSlicingSpecとしてcategory_variable 設定したとすると、TFMAは各要素のa , b , c ごとにMetricsSpecで設定された評価指標で評価をしてくれます。 |
MeanSquaredErrorを設定したとすると、以下のような表が出来上がります。 |
category_variable |
-: |
a |
b |
c |
このように全体を評価するのではなく、ある部分集合で評価するための設定値がSlicingSpecになります。
slicing_specs = [
tfma.SlicingSpec(), # 全体を評価
tfma.SlicingSpec(feature_keys=['is_male']),
tfma.SlicingSpec(feature_keys=['gestation_weeks']),
tfma.SlicingSpec(feature_keys=['mother_age']),
tfma.SlicingSpec(feature_keys=['state']),
tfma.SlicingSpec(feature_keys=['gestation_weeks', 'mother_age']) # 組み合わせを評価
]
- Slicingについては Automated Data Slicing for Model Validationで定義されていると思いますが、この記事を書いている時点ではまだ読めていません。
4. tfma.EvalConfigを作成する
必要なものは全て作成できたので、tfma.EvalConfig
を作成していきましょう。
1~3で準備は整ったので、あとは引数として渡してあげるだけで良いです。
eval_config = tfma.EvalConfig(
model_specs=model_specs,
metrics_specs=metrics_specs,
slicing_specs=slicing_specs)
tfma.analyze_raw_data
で分析結果を作成する
4. さて、ここまででBigQuery MLで作ったモデルを分析する準備は整いました。
分析の実行の仕方は簡単で, tfma.analyze_raw_data
にpandas.DataFrame
とtfma.EvalConfig
を渡してあげるだけで完了です。
実行時間が少し長めなので、気長に待ちましょう(数十分以内には終わってたと思います)。
result_ = tfma.analyze_raw_data(
data=df,
eval_config=eval_config)
tfma.view.render_slicing_metrics
で分析結果を可視化する
5. tfma.view.render_slicing_metrics
にtfma.analyze_raw_data
で作成したオブジェクト(tensorflow_model_analysis.view.view_types.EvalResult
)とSlicingとして指定した値を渡してあげれば完了です。
ね、簡単でしょ?
全体評価の場合にはSlicingを渡す必要は特にありません。
tfma.view.render_slicing_metrics(result_)
(スクリーンショットの表にmse以外の評価指標がいくつかありますが、記事中のコードとは別の設定でやっているためです)
特定の列に注目する場合にはslicing_column
に列名を渡すか、slicing_spec
に作成したslicing_specを渡してあげる必要があります。
ここでは、state
と組み合わせ評価したgestation_weeks, mother_age
を可視化してみましょう。
tfma.view.render_slicing_metrics(result_, slicing_column='state')
tfma.view.render_slicing_metrics(result_, slicing_spec=tfma.SlicingSpec(feature_keys=['gestation_weeks', 'mother_age']))
スクリーンショット中の表に、各特徴量の値ごとに評価値が表示されいます。
これによって、特定のカテゴリーだけ外れているとかなどの分析が簡単にできます。また、特徴量の値の組み合わせにも対応しているおかげで、楽に分析ができるのかなと思います
注意しなければいけないのは、tfma.EvalConfig
で指定したSlicingSpecしか可視化の際に指定できないという点です。
また、このやり方だとJupyter Notebookで処理できるレベルのデータ規模に依存してしまうのが、難点です。
Apache Beam+Cloud DataFlowを使ったやり方もあるのですが、上記のやり方よりも若干複雑になるため今回は試しませんでしたが、どこか別の機会ではやりたいなとは思ってます。
後書き
なんでこんなことをしようと思ったかについてちょっとだけ。
最初にやろうとしたの「Keras Tuner + BigQuery MLでハイパーパラメータチューニングを自動化する」ということをしようとしていましたが、「チューニングするためのトライアル回数だけスキャンがかかって料金がかかること」「そこまでするくらいならAutoML Tablesを使って方がいい」という点からボツになりました。
なぜBigQuery ML + TFMAで記事を書こうと思ったかという話ですが、これは現職都合になります。
現職で開発したML PipelineがTensorflow Data Validationを利用していて、本番環境での推論時に特徴量に差異がないかを監視して通知を出したりしています。
システム化するという観点で見た時にモデルに関してはまだまだ課題が多くあり、その一歩としてTFMAに目をつけたというのがこの記事を書こうとしたモチベーションです。
そのため、記事の本題がBigQueryとは少し逸れてしまったことは申し訳なく思っています。
BigQuery MLでもAutoml Tablesのバッチ推論したデータであっても上記のやり方は有効ですので、うまいこと使い道を見つけられるきっかけになれば幸いです。
Discussion