จัดอันดับตามรายการ

ดูบน TensorFlow.org ทำงานใน Google Colab ดูแหล่งที่มาบน GitHub ดาวน์โหลดโน๊ตบุ๊ค

ใน กวดวิชาพื้นฐานการจัดอันดับของ เราผ่านการฝึกอบรมรุ่นที่สามารถคาดการณ์การให้คะแนนสำหรับผู้ใช้คู่ภาพยนตร์ / a แบบจำลองนี้ได้รับการฝึกฝนเพื่อลดข้อผิดพลาดกำลังสองเฉลี่ยของการให้คะแนนที่คาดการณ์ไว้

อย่างไรก็ตาม การเพิ่มประสิทธิภาพการคาดการณ์ของโมเดลในภาพยนตร์แต่ละเรื่องไม่จำเป็นต้องเป็นวิธีที่ดีที่สุดสำหรับโมเดลการจัดอันดับการฝึกอบรม เราไม่ต้องการโมเดลการจัดอันดับเพื่อทำนายคะแนนได้อย่างแม่นยำ แต่เราให้ความสำคัญกับความสามารถของโมเดลในการสร้างรายการสั่งซื้อที่ตรงกับลำดับความชอบของผู้ใช้มากกว่า

แทนที่จะเพิ่มประสิทธิภาพการคาดคะเนของแบบจำลองในแต่ละคู่ของแบบสอบถาม/รายการ เราสามารถเพิ่มประสิทธิภาพการจัดอันดับของรายการโดยรวมของแบบจำลองได้ วิธีนี้เรียก listwise การจัดอันดับ

ในบทช่วยสอนนี้ เราจะใช้ TensorFlow Recommenders เพื่อสร้างโมเดลการจัดอันดับตามรายการ ต้องการทำเช่นนั้นเราจะใช้ประโยชน์จากการจัดอันดับของการสูญเสียและตัวชี้วัดที่จัดไว้ให้โดย TensorFlow การจัดอันดับ , แพคเกจ TensorFlow ที่มุ่งเน้น การเรียนรู้ในการจัดอันดับ

เบื้องต้น

หาก TensorFlow การจัดอันดับไม่สามารถใช้ได้ในสภาพแวดล้อมรันไทม์ของคุณคุณสามารถติดตั้งโดยใช้ pip :

pip install -q tensorflow-recommenders
pip install -q --upgrade tensorflow-datasets
pip install -q tensorflow-ranking

จากนั้นเราสามารถนำเข้าแพ็คเกจที่จำเป็นทั้งหมดได้:

import pprint

import numpy as np
import tensorflow as tf
import tensorflow_datasets as tfds
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/pkg_resources/__init__.py:119: PkgResourcesDeprecationWarning: 0.18ubuntu0.18.04.1 is an invalid version and will not be supported in a future release
  PkgResourcesDeprecationWarning,
import tensorflow_ranking as tfr
import tensorflow_recommenders as tfrs
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow_addons/utils/ensure_tf_install.py:67: UserWarning: Tensorflow Addons supports using Python ops for all Tensorflow versions above or equal to 2.4.0 and strictly below 2.7.0 (nightly versions are not supported). 
 The versions of TensorFlow you are currently using is 2.7.0 and is not supported. 
Some things might work, some things might not.
If you were to encounter a bug, do not file an issue.
If you want to make sure you're using a tested and supported configuration, either change the TensorFlow version or the TensorFlow Addons's version. 
You can find the compatibility matrix in TensorFlow Addon's readme:
https://github.com/tensorflow/addons
  UserWarning,

เราจะใช้ชุดข้อมูล MovieLens 100K ต่อไป เช่นเคย เราโหลดชุดข้อมูลและเก็บเฉพาะรหัสผู้ใช้ ชื่อภาพยนตร์ และคุณลักษณะการให้คะแนนของผู้ใช้สำหรับบทช่วยสอนนี้ นอกจากนี้เรายังดูแลบ้านเพื่อเตรียมคำศัพท์ของเรา

ratings = tfds.load("movielens/100k-ratings", split="train")
movies = tfds.load("movielens/100k-movies", split="train")

ratings = ratings.map(lambda x: {
    "movie_title": x["movie_title"],
    "user_id": x["user_id"],
    "user_rating": x["user_rating"],
})
movies = movies.map(lambda x: x["movie_title"])

unique_movie_titles = np.unique(np.concatenate(list(movies.batch(1000))))
unique_user_ids = np.unique(np.concatenate(list(ratings.batch(1_000).map(
    lambda x: x["user_id"]))))

การประมวลผลข้อมูลล่วงหน้า

อย่างไรก็ตาม เราไม่สามารถใช้ชุดข้อมูล MovieLens เพื่อเพิ่มประสิทธิภาพรายการได้โดยตรง ในการเพิ่มประสิทธิภาพตามรายการ เราจำเป็นต้องมีสิทธิ์เข้าถึงรายการภาพยนตร์ที่ผู้ใช้แต่ละคนให้คะแนน แต่ตัวอย่างแต่ละรายการในชุดข้อมูล MovieLens 100K จะมีเพียงการจัดประเภทภาพยนตร์เพียงเรื่องเดียว

เพื่อหลีกเลี่ยงปัญหานี้ เราแปลงชุดข้อมูลเพื่อให้แต่ละตัวอย่างมีรหัสผู้ใช้และรายชื่อภาพยนตร์ที่ผู้ใช้รายนั้นให้คะแนน ภาพยนตร์บางเรื่องในรายการจะมีอันดับสูงกว่าเรื่องอื่นๆ เป้าหมายของโมเดลของเราคือการคาดการณ์ที่ตรงกับลำดับนี้

การทำเช่นนี้เราจะใช้ tfrs.examples.movielens.movielens_to_listwise ฟังก์ชั่นผู้ช่วย ใช้ชุดข้อมูล MovieLens 100K และสร้างชุดข้อมูลที่มีตัวอย่างรายการตามที่กล่าวไว้ข้างต้น รายละเอียดการดำเนินงานที่สามารถพบได้ใน รหัสที่มา

tf.random.set_seed(42)

# Split between train and tests sets, as before.
shuffled = ratings.shuffle(100_000, seed=42, reshuffle_each_iteration=False)

train = shuffled.take(80_000)
test = shuffled.skip(80_000).take(20_000)

# We sample 50 lists for each user for the training data. For each list we
# sample 5 movies from the movies the user rated.
train = tfrs.examples.movielens.sample_listwise(
    train,
    num_list_per_user=50,
    num_examples_per_list=5,
    seed=42
)
test = tfrs.examples.movielens.sample_listwise(
    test,
    num_list_per_user=1,
    num_examples_per_list=5,
    seed=42
)

เราสามารถตรวจสอบตัวอย่างได้จากข้อมูลการอบรม ตัวอย่างประกอบด้วยรหัสผู้ใช้ รายการรหัสภาพยนตร์ 10 รายการ และการให้คะแนนโดยผู้ใช้

for example in train.take(1):
  pprint.pprint(example)
{'movie_title': <tf.Tensor: shape=(5,), dtype=string, numpy=
array([b'Postman, The (1997)', b'Liar Liar (1997)', b'Contact (1997)',
       b'Welcome To Sarajevo (1997)',
       b'I Know What You Did Last Summer (1997)'], dtype=object)>,
 'user_id': <tf.Tensor: shape=(), dtype=string, numpy=b'681'>,
 'user_rating': <tf.Tensor: shape=(5,), dtype=float32, numpy=array([4., 5., 1., 4., 1.], dtype=float32)>}

คำจำกัดความของโมเดล

เราจะฝึกโมเดลเดียวกันโดยมีความสูญเสียที่แตกต่างกันสามแบบ:

  • หมายถึงข้อผิดพลาดกำลังสอง
  • การสูญเสียบานพับคู่และ
  • การสูญเสีย ListMLE รายการ

การสูญเสียทั้งสามนี้สอดคล้องกับการปรับให้เหมาะสมแบบ pointwise, pairwise และ listwise

เพื่อประเมินรูปแบบที่เราใช้ ปกติลดกำไรสะสม (NDCG) NDCG วัดอันดับที่คาดการณ์โดยพิจารณาผลรวมถ่วงน้ำหนักของคะแนนจริงของผู้สมัครแต่ละคน เรตติ้งของหนังที่มีอันดับต่ำกว่าตามนางแบบจะถูกลดราคามากกว่า ด้วยเหตุนี้ โมเดลที่ดีที่จัดอันดับภาพยนตร์ที่มีเรทสูงไว้ด้านบนจะมีผล NDCG สูง เนื่องจากตัวชี้วัดนี้พิจารณาอันดับของผู้สมัครแต่ละคน จึงเป็นตัวชี้วัดตามรายการ

class RankingModel(tfrs.Model):

  def __init__(self, loss):
    super().__init__()
    embedding_dimension = 32

    # Compute embeddings for users.
    self.user_embeddings = tf.keras.Sequential([
      tf.keras.layers.StringLookup(
        vocabulary=unique_user_ids),
      tf.keras.layers.Embedding(len(unique_user_ids) + 2, embedding_dimension)
    ])

    # Compute embeddings for movies.
    self.movie_embeddings = tf.keras.Sequential([
      tf.keras.layers.StringLookup(
        vocabulary=unique_movie_titles),
      tf.keras.layers.Embedding(len(unique_movie_titles) + 2, embedding_dimension)
    ])

    # Compute predictions.
    self.score_model = tf.keras.Sequential([
      # Learn multiple dense layers.
      tf.keras.layers.Dense(256, activation="relu"),
      tf.keras.layers.Dense(64, activation="relu"),
      # Make rating predictions in the final layer.
      tf.keras.layers.Dense(1)
    ])

    self.task = tfrs.tasks.Ranking(
      loss=loss,
      metrics=[
        tfr.keras.metrics.NDCGMetric(name="ndcg_metric"),
        tf.keras.metrics.RootMeanSquaredError()
      ]
    )

  def call(self, features):
    # We first convert the id features into embeddings.
    # User embeddings are a [batch_size, embedding_dim] tensor.
    user_embeddings = self.user_embeddings(features["user_id"])

    # Movie embeddings are a [batch_size, num_movies_in_list, embedding_dim]
    # tensor.
    movie_embeddings = self.movie_embeddings(features["movie_title"])

    # We want to concatenate user embeddings with movie emebeddings to pass
    # them into the ranking model. To do so, we need to reshape the user
    # embeddings to match the shape of movie embeddings.
    list_length = features["movie_title"].shape[1]
    user_embedding_repeated = tf.repeat(
        tf.expand_dims(user_embeddings, 1), [list_length], axis=1)

    # Once reshaped, we concatenate and pass into the dense layers to generate
    # predictions.
    concatenated_embeddings = tf.concat(
        [user_embedding_repeated, movie_embeddings], 2)

    return self.score_model(concatenated_embeddings)

  def compute_loss(self, features, training=False):
    labels = features.pop("user_rating")

    scores = self(features)

    return self.task(
        labels=labels,
        predictions=tf.squeeze(scores, axis=-1),
    )

ฝึกโมเดล

ตอนนี้เราสามารถฝึกโมเดลทั้งสามแบบได้แล้ว

epochs = 30

cached_train = train.shuffle(100_000).batch(8192).cache()
cached_test = test.batch(4096).cache()

แบบจำลองข้อผิดพลาดกำลังสองเฉลี่ย

รุ่นนี้จะคล้ายกับรูปแบบในการ สอนการจัดอันดับพื้นฐาน เราฝึกแบบจำลองเพื่อลดข้อผิดพลาดกำลังสองเฉลี่ยระหว่างการให้คะแนนจริงและการให้คะแนนที่คาดการณ์ไว้ ดังนั้น การสูญเสียนี้จะถูกคำนวณเป็นรายบุคคลสำหรับภาพยนตร์แต่ละเรื่อง และการฝึกอบรมก็เป็นไปตามจุด

mse_model = RankingModel(tf.keras.losses.MeanSquaredError())
mse_model.compile(optimizer=tf.keras.optimizers.Adagrad(0.1))
mse_model.fit(cached_train, epochs=epochs, verbose=False)
<keras.callbacks.History at 0x7f64791a5d10>

แบบจำลองการสูญเสียบานพับคู่

ด้วยการลดการสูญเสียบานพับคู่ โมเดลพยายามที่จะเพิ่มความแตกต่างระหว่างการคาดการณ์ของแบบจำลองสำหรับสินค้าที่มีคะแนนสูงและสินค้าที่มีคะแนนต่ำ: ยิ่งความแตกต่างนั้นยิ่งใหญ่เท่าใดการสูญเสียแบบจำลองก็จะยิ่งต่ำลง อย่างไรก็ตาม เมื่อความแตกต่างมากพอ การสูญเสียจะกลายเป็นศูนย์ หยุดโมเดลจากการเพิ่มประสิทธิภาพคู่นี้โดยเฉพาะและปล่อยให้มันมุ่งเน้นไปที่คู่อื่น ๆ ที่มีอันดับไม่ถูกต้อง

การสูญเสียนี้ไม่ได้คำนวณสำหรับภาพยนตร์แต่ละเรื่อง แต่สำหรับภาพยนตร์หลายเรื่อง ดังนั้นการฝึกอบรมโดยใช้การสูญเสียนี้เป็นแบบคู่

hinge_model = RankingModel(tfr.keras.losses.PairwiseHingeLoss())
hinge_model.compile(optimizer=tf.keras.optimizers.Adagrad(0.1))
hinge_model.fit(cached_train, epochs=epochs, verbose=False)
<keras.callbacks.History at 0x7f647914f190>

แบบรายการ

ListMLE ขาดทุนจาก TensorFlow แสดงออกการจัดอันดับรายการการประมาณค่าความน่าจะเป็นสูงสุด ในการคำนวณการสูญเสีย ListMLE ขั้นแรกเราใช้การให้คะแนนของผู้ใช้เพื่อสร้างการจัดอันดับที่เหมาะสมที่สุด จากนั้นเราจะคำนวณความเป็นไปได้ที่ผู้สมัครแต่ละคนจะมีอันดับเหนือกว่าโดยรายการใด ๆ ที่ต่ำกว่านั้นในการจัดอันดับที่เหมาะสมที่สุดโดยใช้คะแนนที่คาดการณ์ไว้ โมเดลนี้พยายามที่จะลดโอกาสดังกล่าวให้เหลือน้อยที่สุดเพื่อให้แน่ใจว่าผู้สมัครที่ได้รับคะแนนสูงจะไม่ถูกจัดอันดับโดยผู้สมัครที่มีคะแนนต่ำ คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับรายละเอียดของการ ListMLE ในส่วน 2.2 ของกระดาษ ListMLE ตำแหน่งตระหนักถึง: กระบวนการลำดับการเรียนรู้

โปรดทราบว่าเนื่องจากโอกาสถูกคำนวณโดยคำนึงถึงผู้สมัครและผู้สมัครทั้งหมดที่ต่ำกว่านั้นในการจัดอันดับที่เหมาะสม การขาดทุนจึงไม่เป็นคู่แต่เป็นตามรายการ ดังนั้นการฝึกอบรมจึงใช้การเพิ่มประสิทธิภาพรายการ

listwise_model = RankingModel(tfr.keras.losses.ListMLELoss())
listwise_model.compile(optimizer=tf.keras.optimizers.Adagrad(0.1))
listwise_model.fit(cached_train, epochs=epochs, verbose=False)
<keras.callbacks.History at 0x7f647b35f350>

เปรียบเทียบรุ่น

mse_model_result = mse_model.evaluate(cached_test, return_dict=True)
print("NDCG of the MSE Model: {:.4f}".format(mse_model_result["ndcg_metric"]))
1/1 [==============================] - 0s 405ms/step - ndcg_metric: 0.9053 - root_mean_squared_error: 0.9671 - loss: 0.9354 - regularization_loss: 0.0000e+00 - total_loss: 0.9354
NDCG of the MSE Model: 0.9053
hinge_model_result = hinge_model.evaluate(cached_test, return_dict=True)
print("NDCG of the pairwise hinge loss model: {:.4f}".format(hinge_model_result["ndcg_metric"]))
1/1 [==============================] - 0s 457ms/step - ndcg_metric: 0.9058 - root_mean_squared_error: 3.8330 - loss: 1.0180 - regularization_loss: 0.0000e+00 - total_loss: 1.0180
NDCG of the pairwise hinge loss model: 0.9058
listwise_model_result = listwise_model.evaluate(cached_test, return_dict=True)
print("NDCG of the ListMLE model: {:.4f}".format(listwise_model_result["ndcg_metric"]))
1/1 [==============================] - 0s 432ms/step - ndcg_metric: 0.9071 - root_mean_squared_error: 2.7224 - loss: 4.5401 - regularization_loss: 0.0000e+00 - total_loss: 4.5401
NDCG of the ListMLE model: 0.9071

จากทั้งสามรุ่น โมเดลที่ฝึกโดยใช้ ListMLE มีเมตริก NDCG สูงสุด ผลลัพธ์นี้แสดงให้เห็นว่าสามารถใช้การเพิ่มประสิทธิภาพตามรายการเพื่อฝึกโมเดลการจัดอันดับและสามารถสร้างแบบจำลองที่ทำงานได้ดีกว่าแบบจำลองที่ปรับให้เหมาะสมแบบจุดหรือแบบคู่