このページは Cloud Translation API によって翻訳されました。
Switch to English

Tensorflowラティスによる形状の制約

TensorFlow.orgで表示 GoogleColabで実行 GitHubでソースを表示ノートブックをダウンロード

概要概要

このチュートリアルは、TensorFlow Lattice(TFL)ライブラリによって提供される制約とレギュラライザーの概要です。ここでは、合成データセットでTFLの定型推定量を使用しますが、このチュートリアルのすべては、TFLKerasレイヤーから構築されたモデルでも実行できることに注意してください。

続行する前に、ランタイムに必要なすべてのパッケージがインストールされていることを確認してください(以下のコードセルにインポートされています)。

セットアップ

TF Latticeパッケージのインストール:

pip install -q tensorflow-lattice

必要なパッケージのインポート:

import tensorflow as tf

from IPython.core.pylabtools import figsize
import itertools
import logging
import matplotlib
from matplotlib import pyplot as plt
import numpy as np
import pandas as pd
import sys
import tensorflow_lattice as tfl
logging.disable(sys.maxsize)

このガイドで使用されるデフォルト値:

NUM_EPOCHS = 500
BATCH_SIZE = 64
LEARNING_RATE=0.001

レストランをランク付けするためのトレーニングデータセット

ユーザーがレストランの検索結果をクリックするかどうかを判断する単純化されたシナリオを想像してみてください。タスクは、入力機能が与えられた場合のクリック率(CTR)を予測することです。

  • 平均評価( avg_rating ):[1,5]の範囲の値を持つ数値機能。
  • レビュー数( num_reviews ):値が200に制限された数値機能。これは、傾向の尺度として使用されます。
  • ドル評価( dollar_rating ):セット{"D"、 "DD"、 "DDD"、 "DDDD"}の文字列値を持つカテゴリカル機能。

ここでは、真のCTRが次の式で与えられる合成データセットを作成します。

$$ CTR = 1 / (1 + exp\{\mbox{b(dollar_rating)}-\mbox{avg_rating}\times log(\mbox{num_reviews}) /4 \}) $$

ここで、$ b(\ cdot)$は、各dollar_ratingをベースライン値に変換します。

$$ \mbox{D}\to 3,\ \mbox{DD}\to 2,\ \mbox{DDD}\to 4,\ \mbox{DDDD}\to 4.5. $$

この式は、一般的なユーザーパターンを反映しています。たとえば、他のすべてが修正されている場合、ユーザーは星評価の高いレストランを好み、「\ $ \ $」レストランは「\ $」よりもクリック数が多く、その後に「\ $ \ $ \ $」と「\ $ \ $ \ $ \ $ "。

def click_through_rate(avg_ratings, num_reviews, dollar_ratings):
  dollar_rating_baseline = {"D": 3, "DD": 2, "DDD": 4, "DDDD": 4.5}
  return 1 / (1 + np.exp(
      np.array([dollar_rating_baseline[d] for d in dollar_ratings]) -
      avg_ratings * np.log1p(num_reviews) / 4))

このCTR関数の等高線図を見てみましょう。

def color_bar():
  bar = matplotlib.cm.ScalarMappable(
      norm=matplotlib.colors.Normalize(0, 1, True),
      cmap="viridis",
  )
  bar.set_array([0, 1])
  return bar


def plot_fns(fns, split_by_dollar=False, res=25):
  """Generates contour plots for a list of (name, fn) functions."""
  num_reviews, avg_ratings = np.meshgrid(
      np.linspace(0, 200, num=res),
      np.linspace(1, 5, num=res),
  )
  if split_by_dollar:
    dollar_rating_splits = ["D", "DD", "DDD", "DDDD"]
  else:
    dollar_rating_splits = [None]
  if len(fns) == 1:
    fig, axes = plt.subplots(2, 2, sharey=True, tight_layout=False)
  else:
    fig, axes = plt.subplots(
        len(dollar_rating_splits), len(fns), sharey=True, tight_layout=False)
  axes = axes.flatten()
  axes_index = 0
  for dollar_rating_split in dollar_rating_splits:
    for title, fn in fns:
      if dollar_rating_split is not None:
        dollar_ratings = np.repeat(dollar_rating_split, res**2)
        values = fn(avg_ratings.flatten(), num_reviews.flatten(),
                    dollar_ratings)
        title = "{}: dollar_rating={}".format(title, dollar_rating_split)
      else:
        values = fn(avg_ratings.flatten(), num_reviews.flatten())
      subplot = axes[axes_index]
      axes_index += 1
      subplot.contourf(
          avg_ratings,
          num_reviews,
          np.reshape(values, (res, res)),
          vmin=0,
          vmax=1)
      subplot.title.set_text(title)
      subplot.set(xlabel="Average Rating")
      subplot.set(ylabel="Number of Reviews")
      subplot.set(xlim=(1, 5))

  _ = fig.colorbar(color_bar(), cax=fig.add_axes([0.95, 0.2, 0.01, 0.6]))


figsize(11, 11)
plot_fns([("CTR", click_through_rate)], split_by_dollar=True)

png

データの準備

次に、合成データセットを作成する必要があります。まず、レストランとその機能のシミュレーションデータセットを生成します。

def sample_restaurants(n):
  avg_ratings = np.random.uniform(1.0, 5.0, n)
  num_reviews = np.round(np.exp(np.random.uniform(0.0, np.log(200), n)))
  dollar_ratings = np.random.choice(["D", "DD", "DDD", "DDDD"], n)
  ctr_labels = click_through_rate(avg_ratings, num_reviews, dollar_ratings)
  return avg_ratings, num_reviews, dollar_ratings, ctr_labels


np.random.seed(42)
avg_ratings, num_reviews, dollar_ratings, ctr_labels = sample_restaurants(2000)

figsize(5, 5)
fig, axs = plt.subplots(1, 1, sharey=False, tight_layout=False)
for rating, marker in [("D", "o"), ("DD", "^"), ("DDD", "+"), ("DDDD", "x")]:
  plt.scatter(
      x=avg_ratings[np.where(dollar_ratings == rating)],
      y=num_reviews[np.where(dollar_ratings == rating)],
      c=ctr_labels[np.where(dollar_ratings == rating)],
      vmin=0,
      vmax=1,
      marker=marker,
      label=rating)
plt.xlabel("Average Rating")
plt.ylabel("Number of Reviews")
plt.legend()
plt.xlim((1, 5))
plt.title("Distribution of restaurants")
_ = fig.colorbar(color_bar(), cax=fig.add_axes([0.95, 0.2, 0.01, 0.6]))

png

トレーニング、検証、テストのデータセットを作成しましょう。検索結果にレストランが表示されている場合、サンプルポイントとしてユーザーのエンゲージメント(クリックまたはクリックなし)を記録できます。

実際には、ユーザーがすべての検索結果を確認するわけではありません。つまり、ユーザーは、現在使用されているランキングモデルですでに「良い」と見なされているレストランのみを表示する可能性があります。その結果、「良い」レストランは、トレーニングデータセットでより頻繁に印象付けられ、過大評価されます。より多くの特徴を使用する場合、トレーニングデータセットは、特徴空間の「悪い」部分に大きなギャップを持つ可能性があります。

モデルをランク付けに使用する場合、トレーニングデータセットでは十分に表されていない、より均一な分布で、関連するすべての結果で評価されることがよくあります。この場合、過剰に表現されたデータポイントが過剰適合し、一般化が不十分なため、柔軟で複雑なモデルが失敗する可能性があります。この問題を処理するには、ドメイン知識を適用して、トレーニングデータセットからモデルを取得できない場合に、モデルが妥当な予測を行うようにガイドする形状制約を追加します。

この例では、トレーニングデータセットは主に、人気のあるレストランとのユーザーインタラクションで構成されています。テストデータセットには、上記の評価設定をシミュレートするための一様分布があります。このようなテストデータセットは、実際の問題設定では利用できないことに注意してください。

def sample_dataset(n, testing_set):
  (avg_ratings, num_reviews, dollar_ratings, ctr_labels) = sample_restaurants(n)
  if testing_set:
    # Testing has a more uniform distribution over all restaurants.
    num_views = np.random.poisson(lam=3, size=n)
  else:
    # Training/validation datasets have more views on popular restaurants.
    num_views = np.random.poisson(lam=ctr_labels * num_reviews / 50.0, size=n)

  return pd.DataFrame({
      "avg_rating": np.repeat(avg_ratings, num_views),
      "num_reviews": np.repeat(num_reviews, num_views),
      "dollar_rating": np.repeat(dollar_ratings, num_views),
      "clicked": np.random.binomial(n=1, p=np.repeat(ctr_labels, num_views))
  })


# Generate datasets.
np.random.seed(42)
data_train = sample_dataset(2000, testing_set=False)
data_val = sample_dataset(1000, testing_set=False)
data_test = sample_dataset(1000, testing_set=True)

# Plotting dataset densities.
figsize(12, 5)
fig, axs = plt.subplots(1, 2, sharey=False, tight_layout=False)
for ax, data, title in [(axs[0], data_train, "training"),
                        (axs[1], data_test, "testing")]:
  _, _, _, density = ax.hist2d(
      x=data["avg_rating"],
      y=data["num_reviews"],
      bins=(np.linspace(1, 5, num=21), np.linspace(0, 200, num=21)),
      density=True,
      cmap="Blues",
  )
  ax.set(xlim=(1, 5))
  ax.set(ylim=(0, 200))
  ax.set(xlabel="Average Rating")
  ax.set(ylabel="Number of Reviews")
  ax.title.set_text("Density of {} examples".format(title))
  _ = fig.colorbar(density, ax=ax)

png

トレーニングと評価に使用されるinput_fnsの定義:

train_input_fn = tf.compat.v1.estimator.inputs.pandas_input_fn(
    x=data_train,
    y=data_train["clicked"],
    batch_size=BATCH_SIZE,
    num_epochs=NUM_EPOCHS,
    shuffle=False,
)

# feature_analysis_input_fn is used for TF Lattice estimators.
feature_analysis_input_fn = tf.compat.v1.estimator.inputs.pandas_input_fn(
    x=data_train,
    y=data_train["clicked"],
    batch_size=BATCH_SIZE,
    num_epochs=1,
    shuffle=False,
)

val_input_fn = tf.compat.v1.estimator.inputs.pandas_input_fn(
    x=data_val,
    y=data_val["clicked"],
    batch_size=BATCH_SIZE,
    num_epochs=1,
    shuffle=False,
)

test_input_fn = tf.compat.v1.estimator.inputs.pandas_input_fn(
    x=data_test,
    y=data_test["clicked"],
    batch_size=BATCH_SIZE,
    num_epochs=1,
    shuffle=False,
)

勾配ブーストツリーのフィッティング

avg_ratingnum_reviews 2つの機能から始めましょう。

検証およびテストメトリックをプロットおよび計算するためのいくつかの補助関数を作成します。

def analyze_two_d_estimator(estimator, name):
  # Extract validation metrics.
  metric = estimator.evaluate(input_fn=val_input_fn)
  print("Validation AUC: {}".format(metric["auc"]))
  metric = estimator.evaluate(input_fn=test_input_fn)
  print("Testing AUC: {}".format(metric["auc"]))

  def two_d_pred(avg_ratings, num_reviews):
    results = estimator.predict(
        tf.compat.v1.estimator.inputs.pandas_input_fn(
            x=pd.DataFrame({
                "avg_rating": avg_ratings,
                "num_reviews": num_reviews,
            }),
            shuffle=False,
        ))
    return [x["logistic"][0] for x in results]

  def two_d_click_through_rate(avg_ratings, num_reviews):
    return np.mean([
        click_through_rate(avg_ratings, num_reviews,
                           np.repeat(d, len(avg_ratings)))
        for d in ["D", "DD", "DDD", "DDDD"]
    ],
                   axis=0)

  figsize(11, 5)
  plot_fns([("{} Estimated CTR".format(name), two_d_pred),
            ("CTR", two_d_click_through_rate)],
           split_by_dollar=False)

TensorFlow勾配ブースト決定木をデータセットに適合させることができます。

feature_columns = [
    tf.feature_column.numeric_column("num_reviews"),
    tf.feature_column.numeric_column("avg_rating"),
]
gbt_estimator = tf.estimator.BoostedTreesClassifier(
    feature_columns=feature_columns,
    # Hyper-params optimized on validation set.
    n_batches_per_layer=1,
    max_depth=3,
    n_trees=20,
    min_node_weight=0.1,
    config=tf.estimator.RunConfig(tf_random_seed=42),
)
gbt_estimator.train(input_fn=train_input_fn)
analyze_two_d_estimator(gbt_estimator, "GBT")
Validation AUC: 0.7248634099960327
Testing AUC: 0.6980501413345337

png

モデルは真のCTRの一般的な形状をキャプチャし、適切な検証メトリックを備えていますが、入力スペースのいくつかの部分で直感に反する動作をします。平均評価またはレビュー数が増えると、推定CTRは減少します。これは、トレーニングデータセットで十分にカバーされていない領域にサンプルポイントがないためです。モデルには、データだけから正しい動作を推測する方法がありません。

この問題を解決するために、モデルが平均評価とレビュー数の両方に関して単調に増加する値を出力する必要があるという形状制約を適用します。これをTFLに実装する方法については後で説明します。

DNNのフィッティング

DNN分類器を使用して同じ手順を繰り返すことができます。同様のパターンを観察できます。レビューの数が少ない十分なサンプルポイントがないと、無意味な外挿が発生します。検証メトリックはツリーソリューションよりも優れていますが、テストメトリックははるかに悪いことに注意してください。

feature_columns = [
    tf.feature_column.numeric_column("num_reviews"),
    tf.feature_column.numeric_column("avg_rating"),
]
dnn_estimator = tf.estimator.DNNClassifier(
    feature_columns=feature_columns,
    # Hyper-params optimized on validation set.
    hidden_units=[16, 8, 8],
    optimizer=tf.keras.optimizers.Adam(learning_rate=LEARNING_RATE),
    config=tf.estimator.RunConfig(tf_random_seed=42),
)
dnn_estimator.train(input_fn=train_input_fn)
analyze_two_d_estimator(dnn_estimator, "DNN")
Validation AUC: 0.7518489956855774
Testing AUC: 0.745200514793396

png

形状の制約

TensorFlow Lattice(TFL)は、トレーニングデータを超えてモデルの動作を保護するために、形状の制約を適用することに重点を置いています。これらの形状制約は、TFLKerasレイヤーに適用されます。それらの詳細は、JMLRの論文に記載されています

このチュートリアルでは、TFの定型推定量を使用してさまざまな形状の制約をカバーしますが、これらの手順はすべて、TFLKerasレイヤーから作成されたモデルで実行できることに注意してください。

他のTensorFlow推定器と同様に、TFLの定型推定器は、特徴列を使用して入力形式を定義し、トレーニングinput_fnを使用してデータを渡します。 TFLの定型推定量を使用するには、次のものも必要です。

  • モデル構成:モデルアーキテクチャと機能ごとの形状制約およびレギュラライザーを定義します。
  • 機能分析input_fn :TFL初期化のためにデータを渡すTFinput_fn。

より詳細な説明については、cannedestimatorsチュートリアルまたはAPIドキュメントを参照してください。

単調性

最初に、両方の機能に単調性の形状制約を追加することにより、単調性の懸念に対処します。

TFLに形状拘束を適用するように指示するには、フィーチャー構成で拘束を指定します。次のコードは、 monotonicity="increasing"設定monotonicity="increasing"により、 num_reviewsavg_rating両方に関して出力を単調に増加させる方法を示しています。

feature_columns = [
    tf.feature_column.numeric_column("num_reviews"),
    tf.feature_column.numeric_column("avg_rating"),
]
model_config = tfl.configs.CalibratedLatticeConfig(
    feature_configs=[
        tfl.configs.FeatureConfig(
            name="num_reviews",
            lattice_size=2,
            monotonicity="increasing",
            pwl_calibration_num_keypoints=20,
        ),
        tfl.configs.FeatureConfig(
            name="avg_rating",
            lattice_size=2,
            monotonicity="increasing",
            pwl_calibration_num_keypoints=20,
        )
    ])
tfl_estimator = tfl.estimators.CannedClassifier(
    feature_columns=feature_columns,
    model_config=model_config,
    feature_analysis_input_fn=feature_analysis_input_fn,
    optimizer=tf.keras.optimizers.Adam(learning_rate=LEARNING_RATE),
    config=tf.estimator.RunConfig(tf_random_seed=42),
)
tfl_estimator.train(input_fn=train_input_fn)
analyze_two_d_estimator(tfl_estimator, "TF Lattice")
Validation AUC: 0.740505039691925
Testing AUC: 0.7356159687042236

png

CalibratedLatticeConfigを使用すると、最初に各入力にキャリブレーターを適用し(数値特徴の区分線形関数)、次に格子層を適用してキャリブレーションされた特徴を非線形に融合する定型分類子が作成されます。 tfl.visualizationを使用してモデルを視覚化できます。特に、次のプロットは、缶詰の分類器に含まれる2つのトレーニング済みキャリブレータを示しています。

def save_and_visualize_lattice(tfl_estimator):
  saved_model_path = tfl_estimator.export_saved_model(
      "/tmp/TensorFlow_Lattice_101/",
      tf.estimator.export.build_parsing_serving_input_receiver_fn(
          feature_spec=tf.feature_column.make_parse_example_spec(
              feature_columns)))
  model_graph = tfl.estimators.get_model_graph(saved_model_path)
  figsize(8, 8)
  tfl.visualization.draw_model_graph(model_graph)
  return model_graph

_ = save_and_visualize_lattice(tfl_estimator)

png

制約を追加すると、平均評価が上がるか、レビューの数が増えると、推定クリック率は常に増加します。これは、キャリブレータとラティスが単調であることを確認することによって行われます。

収穫逓減

収穫逓減とは、特定の機能値を増やすことによる限界利益が、値を増やすにつれて減少することを意味します。この場合、 num_reviews機能はこのパターンに従うと予想されるため、それに応じてキャリブレーターを構成できます。収穫逓減を2つの十分条件に分解できることに注意してください。

  • キャリブレータは単調に増加しており、
  • キャリブレータは凹面です。
feature_columns = [
    tf.feature_column.numeric_column("num_reviews"),
    tf.feature_column.numeric_column("avg_rating"),
]
model_config = tfl.configs.CalibratedLatticeConfig(
    feature_configs=[
        tfl.configs.FeatureConfig(
            name="num_reviews",
            lattice_size=2,
            monotonicity="increasing",
            pwl_calibration_convexity="concave",
            pwl_calibration_num_keypoints=20,
        ),
        tfl.configs.FeatureConfig(
            name="avg_rating",
            lattice_size=2,
            monotonicity="increasing",
            pwl_calibration_num_keypoints=20,
        )
    ])
tfl_estimator = tfl.estimators.CannedClassifier(
    feature_columns=feature_columns,
    model_config=model_config,
    feature_analysis_input_fn=feature_analysis_input_fn,
    optimizer=tf.keras.optimizers.Adam(learning_rate=LEARNING_RATE),
    config=tf.estimator.RunConfig(tf_random_seed=42),
)
tfl_estimator.train(input_fn=train_input_fn)
analyze_two_d_estimator(tfl_estimator, "TF Lattice")
_ = save_and_visualize_lattice(tfl_estimator)
Validation AUC: 0.7382562756538391
Testing AUC: 0.7399928569793701

png

png

凹面拘束を追加することにより、テストメトリックがどのように改善されるかに注目してください。予測プロットもグラウンドトゥルースによく似ています。

2D形状の制約:信頼

レビューが1つか2つしかないレストランの5つ星の評価は、信頼できない評価である可能性があります(レストランは実際には良くないかもしれません)が、レビューが数百のレストランの4つ星の評価は、はるかに信頼性が高くなります(レストランはこの場合はおそらく良いでしょう)。レストランのレビュー数は、平均評価に対する信頼度に影響を与えることがわかります。

TFL信頼制約を実行して、ある機能の値が大きい(または小さい)ことは、別の機能の信頼性または信頼性が高いことをモデルに通知できます。これは、機能構成でreflects_trust_in構成を設定することによって行われます。

feature_columns = [
    tf.feature_column.numeric_column("num_reviews"),
    tf.feature_column.numeric_column("avg_rating"),
]
model_config = tfl.configs.CalibratedLatticeConfig(
    feature_configs=[
        tfl.configs.FeatureConfig(
            name="num_reviews",
            lattice_size=2,
            monotonicity="increasing",
            pwl_calibration_convexity="concave",
            pwl_calibration_num_keypoints=20,
            # Larger num_reviews indicating more trust in avg_rating.
            reflects_trust_in=[
                tfl.configs.TrustConfig(
                    feature_name="avg_rating", trust_type="edgeworth"),
            ],
        ),
        tfl.configs.FeatureConfig(
            name="avg_rating",
            lattice_size=2,
            monotonicity="increasing",
            pwl_calibration_num_keypoints=20,
        )
    ])
tfl_estimator = tfl.estimators.CannedClassifier(
    feature_columns=feature_columns,
    model_config=model_config,
    feature_analysis_input_fn=feature_analysis_input_fn,
    optimizer=tf.keras.optimizers.Adam(learning_rate=LEARNING_RATE),
    config=tf.estimator.RunConfig(tf_random_seed=42),
)
tfl_estimator.train(input_fn=train_input_fn)
analyze_two_d_estimator(tfl_estimator, "TF Lattice")
model_graph = save_and_visualize_lattice(tfl_estimator)
Validation AUC: 0.7382063269615173
Testing AUC: 0.73994380235672

png

png

次のプロットは、トレーニングされた格子関数を示しています。信頼の制約により、キャリブレーションされたnum_reviews値が大きいほど、キャリブレーションされたnum_reviewsに対してより高い勾配が強制され、ラティス出力がより大きく変化するとavg_ratingされます。

lat_mesh_n = 12
lat_mesh_x, lat_mesh_y = tfl.test_utils.two_dim_mesh_grid(
    lat_mesh_n**2, 0, 0, 1, 1)
lat_mesh_fn = tfl.test_utils.get_hypercube_interpolation_fn(
    model_graph.output_node.weights.flatten())
lat_mesh_z = [
    lat_mesh_fn([lat_mesh_x.flatten()[i],
                 lat_mesh_y.flatten()[i]]) for i in range(lat_mesh_n**2)
]
trust_plt = tfl.visualization.plot_outputs(
    (lat_mesh_x, lat_mesh_y),
    {"Lattice Lookup": lat_mesh_z},
    figsize=(6, 6),
)
trust_plt.title("Trust")
trust_plt.xlabel("Calibrated avg_rating")
trust_plt.ylabel("Calibrated num_reviews")
trust_plt.show()

png

平滑化キャリブレータ

ここで、 avg_ratingのキャリブレータを見てみましょう。単調に増加していますが、傾斜の変化は急激で解釈が困難です。それは我々が正則セットアップ使用して、この校正平滑化を検討する必要があります示唆regularizer_configs

ここでは、 wrinkle正則化を適用して、曲率の変化を減らします。また、 laplacian正則化を使用してキャリブレーターを平坦化し、 hessian正則化を使用してより直線的にすることもできます。

feature_columns = [
    tf.feature_column.numeric_column("num_reviews"),
    tf.feature_column.numeric_column("avg_rating"),
]
model_config = tfl.configs.CalibratedLatticeConfig(
    feature_configs=[
        tfl.configs.FeatureConfig(
            name="num_reviews",
            lattice_size=2,
            monotonicity="increasing",
            pwl_calibration_convexity="concave",
            pwl_calibration_num_keypoints=20,
            regularizer_configs=[
                tfl.configs.RegularizerConfig(name="calib_wrinkle", l2=1.0),
            ],
            reflects_trust_in=[
                tfl.configs.TrustConfig(
                    feature_name="avg_rating", trust_type="edgeworth"),
            ],
        ),
        tfl.configs.FeatureConfig(
            name="avg_rating",
            lattice_size=2,
            monotonicity="increasing",
            pwl_calibration_num_keypoints=20,
            regularizer_configs=[
                tfl.configs.RegularizerConfig(name="calib_wrinkle", l2=1.0),
            ],
        )
    ])
tfl_estimator = tfl.estimators.CannedClassifier(
    feature_columns=feature_columns,
    model_config=model_config,
    feature_analysis_input_fn=feature_analysis_input_fn,
    optimizer=tf.keras.optimizers.Adam(learning_rate=LEARNING_RATE),
    config=tf.estimator.RunConfig(tf_random_seed=42),
)
tfl_estimator.train(input_fn=train_input_fn)
analyze_two_d_estimator(tfl_estimator, "TF Lattice")
_ = save_and_visualize_lattice(tfl_estimator)
Validation AUC: 0.7512326836585999
Testing AUC: 0.754392147064209

png

png

キャリブレータがスムーズになり、全体的な推定CTRがグラウンドトゥルースとより一致します。これは、テストメトリックと等高線図の両方に反映されます。

カテゴリカルキャリブレーションの部分単調性

これまで、モデルで使用している数値特徴は2つだけです。ここでは、カテゴリカルキャリブレーションレイヤーを使用して3番目の機能を追加します。ここでも、プロットとメトリック計算のためのヘルパー関数を設定することから始めます。

def analyze_three_d_estimator(estimator, name):
  # Extract validation metrics.
  metric = estimator.evaluate(input_fn=val_input_fn)
  print("Validation AUC: {}".format(metric["auc"]))
  metric = estimator.evaluate(input_fn=test_input_fn)
  print("Testing AUC: {}".format(metric["auc"]))

  def three_d_pred(avg_ratings, num_reviews, dollar_rating):
    results = estimator.predict(
        tf.compat.v1.estimator.inputs.pandas_input_fn(
            x=pd.DataFrame({
                "avg_rating": avg_ratings,
                "num_reviews": num_reviews,
                "dollar_rating": dollar_rating,
            }),
            shuffle=False,
        ))
    return [x["logistic"][0] for x in results]

  figsize(11, 22)
  plot_fns([("{} Estimated CTR".format(name), three_d_pred),
            ("CTR", click_through_rate)],
           split_by_dollar=True)

3番目の機能であるdollar_ratingには、カテゴリ機能では、機能列と機能構成の両方として、TFLでわずかに異なる処理が必要であることを思い出してください。ここでは、他のすべての入力が固定されている場合、「DD」レストランの出力を「D」レストランよりも大きくする必要があるという部分的な単調性制約を適用します。これは、機能構成のmonotonicity設定を使用して行われます。

feature_columns = [
    tf.feature_column.numeric_column("num_reviews"),
    tf.feature_column.numeric_column("avg_rating"),
    tf.feature_column.categorical_column_with_vocabulary_list(
        "dollar_rating",
        vocabulary_list=["D", "DD", "DDD", "DDDD"],
        dtype=tf.string,
        default_value=0),
]
model_config = tfl.configs.CalibratedLatticeConfig(
    feature_configs=[
        tfl.configs.FeatureConfig(
            name="num_reviews",
            lattice_size=2,
            monotonicity="increasing",
            pwl_calibration_convexity="concave",
            pwl_calibration_num_keypoints=20,
            regularizer_configs=[
                tfl.configs.RegularizerConfig(name="calib_wrinkle", l2=1.0),
            ],
            reflects_trust_in=[
                tfl.configs.TrustConfig(
                    feature_name="avg_rating", trust_type="edgeworth"),
            ],
        ),
        tfl.configs.FeatureConfig(
            name="avg_rating",
            lattice_size=2,
            monotonicity="increasing",
            pwl_calibration_num_keypoints=20,
            regularizer_configs=[
                tfl.configs.RegularizerConfig(name="calib_wrinkle", l2=1.0),
            ],
        ),
        tfl.configs.FeatureConfig(
            name="dollar_rating",
            lattice_size=2,
            pwl_calibration_num_keypoints=4,
            # Here we only specify one monotonicity:
            # `D` resturants has smaller value than `DD` restaurants
            monotonicity=[("D", "DD")],
        ),
    ])
tfl_estimator = tfl.estimators.CannedClassifier(
    feature_columns=feature_columns,
    model_config=model_config,
    feature_analysis_input_fn=feature_analysis_input_fn,
    optimizer=tf.keras.optimizers.Adam(learning_rate=LEARNING_RATE),
    config=tf.estimator.RunConfig(tf_random_seed=42),
)
tfl_estimator.train(input_fn=train_input_fn)
analyze_three_d_estimator(tfl_estimator, "TF Lattice")
_ = save_and_visualize_lattice(tfl_estimator)
Validation AUC: 0.8137159943580627
Testing AUC: 0.8294374942779541

png

png

このカテゴリカルキャリブレータは、モデル出力の優先度を示します:DD> D> DDD> DDDD、これはセットアップと一致しています。欠落している値の列もあることに注意してください。トレーニングおよびテストデータに欠測データはありませんが、モデルは、ダウンストリームモデルの提供中に欠測値が発生した場合に、欠測値の補完を提供します。

ここでは、 dollar_rating条件とするこのモデルの予測CTRもプロットしdollar_rating 。必要なすべての制約が各スライスで満たされていることに注意してください。

出力キャリブレーション

これまでにトレーニングしたすべてのTFLモデルについて、ラティスレイヤー(モデルグラフで「ラティス」と表示)はモデル予測を直接出力します。モデル出力を出力するためにラティス出力を再スケーリングする必要があるかどうかわからない場合があります。

  • ラベルがカウントであるのに対し、機能は$ log $カウントです。
  • ラティスは頂点が非常に少ないように構成されていますが、ラベルの分布は比較的複雑です。

このような場合、ラティス出力とモデル出力の間に別のキャリブレータを追加して、モデルの柔軟性を高めることができます。ここでは、作成したばかりのモデルに5つのキーポイントを持つキャリブレーターレイヤーを追加しましょう。また、機能をスムーズに保つために、出力キャリブレータに正則化を追加します。

feature_columns = [
    tf.feature_column.numeric_column("num_reviews"),
    tf.feature_column.numeric_column("avg_rating"),
    tf.feature_column.categorical_column_with_vocabulary_list(
        "dollar_rating",
        vocabulary_list=["D", "DD", "DDD", "DDDD"],
        dtype=tf.string,
        default_value=0),
]
model_config = tfl.configs.CalibratedLatticeConfig(
    output_calibration=True,
    output_calibration_num_keypoints=5,
    regularizer_configs=[
        tfl.configs.RegularizerConfig(name="output_calib_wrinkle", l2=0.1),
    ],
    feature_configs=[
    tfl.configs.FeatureConfig(
        name="num_reviews",
        lattice_size=2,
        monotonicity="increasing",
        pwl_calibration_convexity="concave",
        pwl_calibration_num_keypoints=20,
        regularizer_configs=[
            tfl.configs.RegularizerConfig(name="calib_wrinkle", l2=1.0),
        ],
        reflects_trust_in=[
            tfl.configs.TrustConfig(
                feature_name="avg_rating", trust_type="edgeworth"),
        ],
    ),
    tfl.configs.FeatureConfig(
        name="avg_rating",
        lattice_size=2,
        monotonicity="increasing",
        pwl_calibration_num_keypoints=20,
        regularizer_configs=[
            tfl.configs.RegularizerConfig(name="calib_wrinkle", l2=1.0),
        ],
    ),
    tfl.configs.FeatureConfig(
        name="dollar_rating",
        lattice_size=2,
        pwl_calibration_num_keypoints=4,
        # Here we only specify one monotonicity:
        # `D` resturants has smaller value than `DD` restaurants
        monotonicity=[("D", "DD")],
    ),
])
tfl_estimator = tfl.estimators.CannedClassifier(
    feature_columns=feature_columns,
    model_config=model_config,
    feature_analysis_input_fn=feature_analysis_input_fn,
    optimizer=tf.keras.optimizers.Adam(learning_rate=LEARNING_RATE),
    config=tf.estimator.RunConfig(tf_random_seed=42),
)
tfl_estimator.train(input_fn=train_input_fn)
analyze_three_d_estimator(tfl_estimator, "TF Lattice")
_ = save_and_visualize_lattice(tfl_estimator)
Validation AUC: 0.8130996227264404
Testing AUC: 0.8300802707672119

png

png

最終的なテストメトリックとプロットは、常識的な制約を使用することで、モデルが予期しない動作を回避し、入力空間全体をより適切に推定するのにどのように役立つかを示しています。