דף זה תורגם על ידי Cloud Translation API.
Switch to English

אילוצי צורה עם סריג Tensorflow

צפה ב- TensorFlow.org הפעל בגוגל קולאב צפה במקור ב- GitHub הורד מחברת

סקירה כללית

מדריך זה הוא סקירה כללית של האילוצים והמוסד המסופק על ידי ספריית TensorFlow Lattice (TFL). כאן אנו משתמשים באומדנים משומרים של TFL על מערכי נתונים סינתטיים, אך שימו לב כי כל דבר במדריך זה ניתן לעשות גם עם מודלים הבנויים משכבות TFL Keras.

לפני שתמשיך, וודא שבזמן הריצה מותקנות כל החבילות הנדרשות (כמיובאות בתאי הקוד שלמטה).

להכין

התקנת חבילת סריג TF:

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 ): תכונה מספרית עם ערכים 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_rating num_reviews ומספר num_reviews .

אנו יוצרים כמה פונקציות עזר לתכנון וחישוב מדדי אימות ובדיקה.

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

למרות שהמודל תפס את הצורה הכללית של שיעור הקליקים האמיתי ויש לו מדדי אימות ראויים, יש לו התנהגות נגד אינטואיטיבית בכמה חלקים של שטח הקלט: שיעור הקליקים המשוער יורד ככל שהדירוג הממוצע או מספר הביקורות גדל. הסיבה לכך היא מחסור בנקודות לדוגמא באזורים שאינם מכוסים היטב על ידי מערך האימונים. למודל פשוט אין שום דרך להסיק את ההתנהגות הנכונה אך ורק מהנתונים.

כדי לפתור בעיה זו, אנו אוכפים את אילוצי הצורה שעל המודל להפיק ערכים הגדלת מונוטון ביחס לדירוג הממוצע ולמספר הביקורות. בהמשך נראה כיצד ליישם זאת ב- 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) מתמקד באכיפת אילוצי צורה כדי להגן על התנהגות המודל מעבר לנתוני האימון. אילוצי צורה אלה מוחלים על שכבות TFL Keras. ניתן למצוא את פרטיהם במאמר JMLR שלנו .

במדריך זה אנו משתמשים באומדנים משומרים של TF כדי לכסות אילוצי צורה שונים, אך שימו לב כי ניתן לבצע את כל השלבים הללו עם מודלים שנוצרו משכבות TFL Keras.

כמו בכל אומדן אחר של TensorFlow, אומדנים משומרים של TFL משתמשים בעמודות תכונה כדי להגדיר את פורמט הקלט ומשתמשים ב- input_fn להכשרה להעברת הנתונים. שימוש באומדני שימורי TFL דורש גם:

  • תצורת תצורה של מודל : הגדרת ארכיטקטורת המודל ואילוצי הצורה לפי תכונה ורגולציה.
  • ניתוח תכונות input_fn : TF input_fn העברת נתונים לאתחול TFL.

לתיאור מעמיק יותר, עיין במדריך אומדני השימורים או במסמכי ה- API.

מונוטוניות

תחילה אנו מתייחסים לחששות המונוטוניות על ידי הוספת אילוצי צורה מונוטוניים לשני התכונות.

כדי להנחות את TFL לאכוף אילוצי צורה, אנו מציינים את האילוצים בתצורות התצורה . הקוד הבא מראה כיצד אנו יכולים לדרוש שהפלט יגדל בצורה מונוטונית ביחס למספר num_reviews והן avg_rating ידי הגדרת 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.7402218580245972
Testing AUC: 0.735464870929718

png

שימוש ב- CalibratedLatticeConfig יוצר מסווג משומר שמיישם תחילה כיול על כל קלט (פונקציה לינארית חתיכתית למאפיינים מספריים) ואחריו שכבת סריג למיזוג לא לינארי של התכונות המכוילות. אנו יכולים להשתמש ב- tfl.visualization כדי לדמיין את המודל. בפרט, העלילה הבאה מציגה את שני הכיולים המאומנים הכלולים במסווג השימורים.

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 תואמת לתבנית זו, כך שנוכל להגדיר את הכיול שלה בהתאם. שימו לב שנוכל לפרק את ההחזרות המצטמצמות לשני תנאים מספיקים:

  • המכייל גדל באופן מונוטוני, ו
  • המכייל קעור.
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.7382229566574097
Testing AUC: 0.739747166633606

png

png

שימו לב כיצד מדד הבדיקה משתפר על ידי הוספת אילוץ הקיעור. גם עלילת החיזוי דומה יותר לאמת הקרקעית.

אילוץ צורה דו-ממדי: אמון

דירוג 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.7389558553695679
Testing AUC: 0.7397989630699158

png

png

העלילה הבאה מציגה את פונקציית הסריג המאומן. בשל מגבלת האמון, אנו מצפים שערכים גדולים יותר של ביקורות 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.7512660026550293
Testing AUC: 0.7544151544570923

png

png

הכיולים כעת חלקים, ושיעור הקליקים המשוער הכולל תואם את האמת הקרקעית. זה בא לידי ביטוי הן במדד הבדיקה והן בחלקות המתאר.

מונוטוניות חלקית לכיול קטגורי

עד כה השתמשנו רק בשניים מהתכונות המספריות במודל. כאן נוסיף תכונה שלישית באמצעות שכבת כיול קטגורית. שוב אנו מתחילים בהגדרת פונקציות עוזר לתכנון וחישוב מטרי.

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)

כדי לערב את התכונה השלישית, dollar_rating , עלינו לזכור שתכונות קטגוריות דורשות טיפול מעט שונה ב- TFL, הן dollar_rating תכונה dollar_rating תצורה. כאן אנו אוכפים את האילוץ המונוטוני החלקי שתפוקות למסעדות "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.8136826753616333
Testing AUC: 0.8294427394866943

png

png

כיול קטגורי זה מראה את העדפת תפוקת הדגם: DD> D> DDD> DDDD, העולה בקנה אחד עם ההתקנה שלנו. שימו לב שיש גם טור לחסר ערכים. אף על פי שאין מאפיין חסר בנתוני האימון והבדיקה שלנו, המודל מספק לנו זקיפה לערך החסר אם זה יקרה במהלך הגשת המודל במורד הזרם.

כאן אנו מתווים גם את שיעור הקליקים החזוי של מודל זה המותנה 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.8131163716316223
Testing AUC: 0.830264151096344

png

png

מדד הבדיקות והעלילות הסופיות מראים כיצד שימוש באילוצי השכל הישר יכול לסייע למודל להימנע מהתנהגות בלתי צפויה ולהקצין טוב יותר את כל שטח הקלט.