Hari Komunitas ML adalah 9 November! Bergabung dengan kami untuk update dari TensorFlow, JAX, dan lebih Pelajari lebih lanjut

Batasan Bentuk dengan Tensorflow Lattice

Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHub Unduh buku catatan

Gambaran

Tutorial ini adalah ringkasan dari batasan dan pengatur yang disediakan oleh library TensorFlow Lattice (TFL). Di sini kami menggunakan penduga terekam TFL pada kumpulan data sintetis, tetapi perhatikan bahwa semua yang ada dalam tutorial ini juga dapat dilakukan dengan model yang dibuat dari lapisan TFL Keras.

Sebelum melanjutkan, pastikan runtime Anda telah menginstal semua paket yang diperlukan (seperti yang diimpor dalam sel kode di bawah).

Mendirikan

Menginstal paket TF Lattice:

pip install -q tensorflow-lattice

Mengimpor paket yang dibutuhkan:

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)

Nilai default yang digunakan dalam panduan ini:

NUM_EPOCHS = 1000
BATCH_SIZE = 64
LEARNING_RATE=0.01

Kumpulan Data Pelatihan untuk Pemeringkatan Restoran

Bayangkan skenario yang disederhanakan di mana kami ingin menentukan apakah pengguna akan mengklik hasil pencarian restoran atau tidak. Tugasnya adalah memprediksi rasio klik-tayang (RKT) yang diberikan fitur masukan:

  • Peringkat rata-rata ( avg_rating ): fitur numerik dengan nilai dalam kisaran [1,5].
  • Number of reviews ( num_reviews ): fitur numerik dengan nilai yang dibatasi pada 200, yang kami gunakan sebagai ukuran tren.
  • Peringkat dolar ( dollar_rating ): fitur kategoris dengan nilai string di himpunan {"D", "DD", "DDD", "DDDD"}.

Di sini kami membuat kumpulan data sintetis di mana RKT yang benar diberikan oleh rumus:

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

di mana $ b (\ cdot) $ menerjemahkan setiap dollar_rating menjadi nilai dasar:

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

Rumus ini mencerminkan pola pengguna yang khas. mis. dengan segala sesuatunya tetap, pengguna lebih memilih restoran dengan peringkat bintang lebih tinggi, dan restoran "\ $ \ $" akan menerima lebih banyak klik daripada "\ $", diikuti oleh "\ $ \ $ \ $" dan "\ $ \ $ \ $ \ $ ".

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))

Mari kita lihat plot kontur dari fungsi CTR ini.

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

Mempersiapkan Data

Kami sekarang perlu membuat kumpulan data sintetis kami. Kami mulai dengan membuat kumpulan data simulasi restoran dan fiturnya.

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

Mari kita buat set data pelatihan, validasi, dan pengujian. Saat sebuah restoran dilihat di hasil pencarian, kami dapat merekam keterlibatan pengguna (klik atau tidak ada klik) sebagai titik sampel.

Dalam praktiknya, pengguna seringkali tidak melalui semua hasil pencarian. Ini berarti bahwa pengguna kemungkinan hanya akan melihat restoran yang sudah dianggap "baik" oleh model peringkat saat ini yang digunakan. Akibatnya, restoran yang "baik" lebih sering terkesan dan ditampilkan secara berlebihan dalam kumpulan data pelatihan. Saat menggunakan lebih banyak fitur, set data pelatihan dapat memiliki celah besar di bagian "buruk" dari ruang fitur.

Saat model digunakan untuk pemeringkatan, model ini sering dievaluasi pada semua hasil yang relevan dengan distribusi yang lebih seragam yang tidak terwakili dengan baik oleh set data pelatihan. Model yang fleksibel dan rumit mungkin gagal dalam kasus ini karena overfitting titik data yang direpresentasikan secara berlebihan dan oleh karena itu kurang dapat digeneralisasikan. Kami menangani masalah ini dengan menerapkan pengetahuan domain untuk menambahkan batasan bentuk yang memandu model untuk membuat prediksi yang wajar saat model tidak dapat mengambilnya dari set data pelatihan.

Dalam contoh ini, kumpulan data pelatihan sebagian besar terdiri dari interaksi pengguna dengan restoran yang bagus dan populer. Dataset pengujian memiliki distribusi yang seragam untuk mensimulasikan pengaturan evaluasi yang dibahas di atas. Perhatikan bahwa set data pengujian seperti itu tidak akan tersedia dalam pengaturan masalah yang sebenarnya.

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(500, testing_set=False)
data_val = sample_dataset(500, testing_set=False)
data_test = sample_dataset(500, 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

Mendefinisikan input_fns yang digunakan untuk pelatihan dan evaluasi:

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,
)

Fitting Gradient Boosted Trees

Mari kita mulai dengan hanya dua fitur: avg_rating dan num_reviews .

Kami membuat beberapa fungsi tambahan untuk merencanakan dan menghitung validasi dan metrik pengujian.

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)

Kita bisa menyesuaikan pohon keputusan yang didorong gradien TensorFlow pada set data:

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=2,
    n_trees=50,
    learning_rate=0.05,
    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.6333084106445312
Testing AUC: 0.774649977684021

png

Meskipun model telah menangkap bentuk umum dari RKT yang sebenarnya dan memiliki metrik validasi yang layak, model tersebut memiliki perilaku kontra-intuitif di beberapa bagian ruang masukan: perkiraan RKT menurun seiring dengan peningkatan peringkat rata-rata atau jumlah ulasan. Ini karena kurangnya titik sampel di area yang tidak tercakup dengan baik oleh set data pelatihan. Model tersebut tidak memiliki cara untuk menyimpulkan perilaku yang benar hanya dari data.

Untuk mengatasi masalah ini, kami menerapkan batasan bentuk yang modelnya harus mengeluarkan nilai yang meningkat secara monoton terkait dengan peringkat rata-rata dan jumlah tinjauan. Nanti kita akan melihat bagaimana mengimplementasikannya di TFL.

Memasang DNN

Kita dapat mengulangi langkah yang sama dengan pengklasifikasi DNN. Kita dapat mengamati pola yang serupa: tidak memiliki cukup titik sampel dengan sedikit ulasan menghasilkan ekstrapolasi yang tidak masuk akal. Perhatikan bahwa meskipun metrik validasi lebih baik daripada solusi hierarki, metrik pengujian jauh lebih buruk.

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.6696228981018066
Testing AUC: 0.750156044960022

png

Batasan Bentuk

TensorFlow Lattice (TFL) berfokus pada penerapan batasan bentuk untuk melindungi perilaku model di luar data pelatihan. Batasan bentuk ini diterapkan ke lapisan TFL Keras. Detailnya dapat ditemukan di makalah JMLR kami .

Dalam tutorial ini kami menggunakan penduga kalengan TF untuk mencakup berbagai batasan bentuk, tetapi perhatikan bahwa semua langkah ini dapat dilakukan dengan model yang dibuat dari lapisan TFL Keras.

Seperti pada estimator TensorFlow lainnya, estimator terekam TFL menggunakan kolom fitur untuk menentukan format input dan menggunakan training input_fn untuk meneruskan data. Menggunakan penduga kalengan TFL juga membutuhkan:

  • a model config : mendefinisikan arsitektur model dan batasan bentuk per fitur serta pengatur.
  • analisis fitur input_fn : input_fn TF mengirimkan data untuk inisialisasi TFL.

Untuk penjelasan yang lebih lengkap, silakan merujuk ke tutorial pengukur kalengan atau dokumen API.

Monotonisitas

Pertama-tama kami membahas masalah monotonisitas dengan menambahkan batasan bentuk monotonisitas ke kedua fitur.

Untuk menginstruksikan TFL untuk menerapkan batasan bentuk, kami menetapkan batasan dalam konfigurasi fitur . Kode berikut menunjukkan bagaimana kita dapat meminta keluaran menjadi meningkat secara monoton berkenaan dengan num_reviews dan avg_rating dengan menyetel monotonicity="increasing" .

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.6565533876419067
Testing AUC: 0.7784258723258972

png

Menggunakan CalibratedLatticeConfig membuat pengklasifikasi terekam yang pertama kali menerapkan kalibrator ke setiap input (fungsi linier bijak untuk fitur numerik) diikuti oleh lapisan kisi untuk menggabungkan fitur yang dikalibrasi secara non-linear. Kita dapat menggunakan tfl.visualization untuk memvisualisasikan model. Secara khusus, plot berikut menunjukkan dua kalibrator terlatih yang termasuk dalam pengklasifikasi kalengan.

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

Dengan penambahan batasan, estimasi CTR akan selalu meningkat seiring dengan peningkatan rating rata-rata atau jumlah ulasan meningkat. Ini dilakukan dengan memastikan bahwa kalibrator dan kisi bersifat monoton.

Pengembalian Menurun

Pengembalian yang berkurang berarti bahwa keuntungan marjinal dari peningkatan nilai fitur tertentu akan berkurang saat kami meningkatkan nilainya. Dalam kasus kami, kami mengharapkan fitur num_reviews mengikuti pola ini, sehingga kami dapat mengonfigurasi kalibratornya sesuai dengan itu. Perhatikan bahwa kita dapat menguraikan hasil yang semakin berkurang menjadi dua kondisi yang cukup:

  • kalibrator meningkat secara monoton, dan
  • kalibratornya cekung.
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.6409633755683899
Testing AUC: 0.7891247272491455

png

png

Perhatikan bagaimana metrik pengujian meningkat dengan menambahkan batasan konkavitas. Plot prediksi juga lebih menyerupai kebenaran dasar.

Batasan Bentuk 2D: Kepercayaan

Peringkat 5 bintang untuk restoran yang hanya memiliki satu atau dua ulasan kemungkinan besar merupakan peringkat yang tidak dapat diandalkan (restoran tersebut mungkin sebenarnya tidak bagus), sedangkan peringkat bintang 4 untuk restoran dengan ratusan ulasan jauh lebih dapat diandalkan (restoran tersebut sepertinya bagus dalam kasus ini). Kami dapat melihat bahwa jumlah ulasan sebuah restoran memengaruhi seberapa besar kepercayaan yang kami berikan pada peringkat rata-rata restoran tersebut.

Kita dapat menggunakan batasan kepercayaan TFL untuk menginformasikan model bahwa nilai yang lebih besar (atau lebih kecil) dari satu fitur menunjukkan lebih banyak ketergantungan atau kepercayaan pada fitur lainnya. Ini dilakukan dengan menyetel konfigurasi reflects_trust_in di konfigurasi fitur.

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.6409633755683899
Testing AUC: 0.7891043424606323

png

png

Plot berikut menyajikan fungsi kisi terlatih. Karena batasan kepercayaan, kami berharap bahwa nilai yang lebih besar dari num_reviews akan memaksa kemiringan yang lebih tinggi sehubungan dengan avg_rating dikalibrasi, sehingga menghasilkan pergerakan yang lebih signifikan dalam keluaran kisi.

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

Menghaluskan Kalibrator

Sekarang mari kita lihat kalibrator avg_rating . Meskipun secara monoton meningkat, perubahan pada lerengnya terjadi secara tiba-tiba dan sulit untuk diinterpretasikan. Itu menunjukkan bahwa kita mungkin ingin mempertimbangkan untuk menghaluskan kalibrator ini menggunakan pengaturan pengatur di regularizer_configs .

Di sini kami menerapkan pengatur wrinkle untuk mengurangi perubahan kelengkungan. Anda juga dapat menggunakan pengatur laplacian untuk meratakan kalibrator dan pengatur hessian agar lebih linier.

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.6465646028518677
Testing AUC: 0.7948372960090637

png

png

Kalibrator sekarang lancar, dan perkiraan RKT secara keseluruhan lebih cocok dengan kebenaran dasar. Ini tercermin baik dalam metrik pengujian dan di plot kontur.

Monotonisitas Parsial untuk Kalibrasi Kategoris

Sejauh ini kami hanya menggunakan dua fitur numerik dalam model. Di sini kami akan menambahkan fitur ketiga menggunakan lapisan kalibrasi kategoris. Sekali lagi kita mulai dengan menyiapkan fungsi pembantu untuk plotting dan perhitungan metrik.

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)

Untuk melibatkan fitur ketiga, dollar_rating , kita harus ingat bahwa fitur kategoris memerlukan perlakuan yang sedikit berbeda di TFL, baik sebagai kolom fitur maupun sebagai konfigurasi fitur. Di sini kami menerapkan batasan monotonisitas parsial bahwa output untuk restoran "DD" harus lebih besar daripada restoran "D" ketika semua input lainnya ditetapkan. Ini dilakukan dengan menggunakan pengaturan monotonicity di konfigurasi fitur.

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.7699775695800781
Testing AUC: 0.8594040274620056

png

png

Kalibrator kategoris ini menunjukkan preferensi keluaran model: DD> D> DDD> DDDD, yang konsisten dengan penyiapan kami. Perhatikan juga ada kolom untuk nilai yang hilang. Meskipun tidak ada fitur yang hilang dalam data pelatihan dan pengujian kami, model tersebut memberi kami imputasi untuk nilai yang hilang jika itu terjadi selama penyajian model hilir.

Di sini kami juga memplot prediksi CTR dari model ini yang dikondisikan pada dollar_rating . Perhatikan bahwa semua batasan yang kami butuhkan terpenuhi di setiap irisan.

Kalibrasi Keluaran

Untuk semua model TFL yang telah kami latih sejauh ini, lapisan kisi (ditunjukkan sebagai "Lattice" dalam grafik model) secara langsung menampilkan prediksi model. Terkadang kami tidak yakin apakah keluaran kisi harus diskalakan ulang untuk mengeluarkan keluaran model:

  • fitur-fiturnya adalah $ log $ counts sedangkan labelnya adalah hitungan.
  • kisi dikonfigurasi untuk memiliki sangat sedikit simpul tetapi distribusi label relatif rumit.

Dalam kasus tersebut, kita dapat menambahkan kalibrator lain antara keluaran kisi dan keluaran model untuk meningkatkan fleksibilitas model. Di sini mari tambahkan lapisan kalibrator dengan 5 titik kunci ke model yang baru kita buat. Kami juga menambahkan regulator untuk kalibrator keluaran agar fungsinya tetap lancar.

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.7697908878326416
Testing AUC: 0.861327052116394

png

png

Metrik dan plot pengujian terakhir menunjukkan bagaimana penggunaan batasan yang masuk akal dapat membantu model menghindari perilaku yang tidak terduga dan mengekstrapolasi dengan lebih baik ke seluruh ruang input.