태평양 표준시 오전 9시에서 ML 심포지엄이 10월 19일 (화요일)에서 처음으로 여성에의 조정 지금 등록

컨텍스트 기능 활용

TensorFlow.org에서 보기 Google Colab에서 실행 GitHub에서 소스 보기 노트북 다운로드

에서 기능화 튜토리얼 우리의 모델에 단지 사용자와 영화 식별자를 넘어 우리는 통합 된 여러 기능,하지만 우리는 이러한 기능은 모델 정확도를 향상 있는지의 여부도 확인하지 않았습니다.

id 이상의 기능이 추천 모델에서 유용한지 여부에 영향을 미치는 많은 요인이 있습니다.

  1. 문맥의 중요성 : 사용자 환경 설정 상황과 시간에 걸쳐 상대적으로 안정적인 경우, 상황에 맞는 기능은 많은 혜택을 제공하지 않을 수 있습니다. 그러나 사용자 기본 설정이 매우 상황에 맞는 경우 컨텍스트를 추가하면 모델이 크게 향상됩니다. 예를 들어, 요일은 짧은 클립 또는 영화를 추천할지 여부를 결정할 때 중요한 기능일 수 있습니다. 사용자는 주중에 짧은 콘텐츠를 볼 시간이 있지만 주말에는 긴장을 풀고 장편 영화를 즐길 수 있습니다. . 유사하게 쿼리 타임스탬프는 인기 역학을 모델링하는 데 중요한 역할을 할 수 있습니다. 한 영화가 개봉 당시에는 매우 인기가 있었지만 이후에는 빠르게 소멸됩니다. 반대로, 다른 영화는 계속해서 행복하게 볼 수 있는 상록수일 수 있습니다.
  2. 데이터 희소성 : 데이터가 부족한 경우가 아닌 ID 기능을 사용하여 중요 할 수있다. 주어진 사용자 또는 항목에 대해 사용할 수 있는 관찰이 거의 없는 경우 모델은 사용자별 또는 항목별 표현을 추정하는 데 어려움을 겪을 수 있습니다. 정확한 모델을 구축하려면 항목 범주, 설명 및 이미지와 같은 다른 기능을 사용하여 모델이 교육 데이터 이상으로 일반화되도록 해야 합니다. 이것은 특히 관련이 콜드 스타트 비교적 적은 데이터가 일부 항목 또는 사용자로 볼 수 있습니다 상황.

이 자습서에서는 MovieLens 모델에 영화 제목 및 사용자 ID 이외의 기능을 사용하여 실험합니다.

예선

먼저 필요한 패키지를 가져옵니다.

pip install -q tensorflow-recommenders
pip install -q --upgrade tensorflow-datasets
import os
import tempfile

import numpy as np
import tensorflow as tf
import tensorflow_datasets as tfds

import tensorflow_recommenders as tfrs

우리는 다음과 기능화 자습서를 하고, 사용자 ID, 타임 스탬프, 그리고 영화 제목 기능을 유지한다.

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"],
    "timestamp": x["timestamp"],
})
movies = movies.map(lambda x: x["movie_title"])
2021-10-02 11:06:47.010666: E tensorflow/stream_executor/cuda/cuda_driver.cc:271] failed call to cuInit: CUDA_ERROR_NO_DEVICE: no CUDA-capable device is detected

우리는 또한 기능 어휘를 준비하기 위해 약간의 하우스키핑을 합니다.

timestamps = np.concatenate(list(ratings.map(lambda x: x["timestamp"]).batch(100)))

max_timestamp = timestamps.max()
min_timestamp = timestamps.min()

timestamp_buckets = np.linspace(
    min_timestamp, max_timestamp, num=1000,
)

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

모델 정의

쿼리 모델

우리가 정의 된 사용자 모델 시작 기능화 튜토리얼 우리 모델의 제 1 층으로서 기능에 묻어 원시 입력 예 변환하는 임무. 그러나 타임스탬프 기능을 켜거나 끌 수 있도록 약간 변경합니다. 이를 통해 타임스탬프 기능이 모델에 미치는 영향을 보다 쉽게 ​​시연할 수 있습니다. 아래의 코드에서 use_timestamps 우리는 타임 스탬프 기능을 사용할지 여부를 우리에게 제공 매개 변수를 통해 제어 할 수 있습니다.

class UserModel(tf.keras.Model):

  def __init__(self, use_timestamps):
    super().__init__()

    self._use_timestamps = use_timestamps

    self.user_embedding = tf.keras.Sequential([
        tf.keras.layers.StringLookup(
            vocabulary=unique_user_ids, mask_token=None),
        tf.keras.layers.Embedding(len(unique_user_ids) + 1, 32),
    ])

    if use_timestamps:
      self.timestamp_embedding = tf.keras.Sequential([
          tf.keras.layers.Discretization(timestamp_buckets.tolist()),
          tf.keras.layers.Embedding(len(timestamp_buckets) + 1, 32),
      ])
      self.normalized_timestamp = tf.keras.layers.Normalization(
          axis=None
      )

      self.normalized_timestamp.adapt(timestamps)

  def call(self, inputs):
    if not self._use_timestamps:
      return self.user_embedding(inputs["user_id"])

    return tf.concat([
        self.user_embedding(inputs["user_id"]),
        self.timestamp_embedding(inputs["timestamp"]),
        tf.reshape(self.normalized_timestamp(inputs["timestamp"]), (-1, 1)),
    ], axis=1)

이 튜토리얼에서 타임스탬프 기능을 사용하면 훈련-테스트 분할 선택과 바람직하지 않은 방식으로 상호 작용합니다. 테스트 데이터 세트에 속하는 이벤트가 훈련 세트의 이벤트보다 나중에 발생하도록 하기 위해 데이터를 시간순이 아닌 무작위로 분할했기 때문에 모델은 미래에서 효과적으로 학습할 수 있습니다. 이것은 비현실적입니다. 결국 내일의 데이터로 오늘 모델을 훈련할 수 없습니다.

모델이 미래의 상호 작용 패턴을 배울 수 있습니다에이 수단 시간을 추가하는 기능 것을. 설명 목적으로만 이 작업을 수행합니다. MovieLens 데이터 세트 자체는 매우 조밀하며 많은 실제 데이터 세트와 달리 사용자 ID 및 영화 제목 이외의 기능에서 큰 이점을 얻지 못합니다.

이 경고를 제외하고 실제 모델은 특히 데이터에 강한 계절 패턴이 있는 경우 시간 또는 요일과 같은 다른 시간 기반 기능의 이점을 얻을 수 있습니다.

후보 모델

단순화를 위해 후보 모델을 고정된 상태로 유지합니다. 다시 말하지만, 우리는에서 복사 기능화의 튜토리얼 :

class MovieModel(tf.keras.Model):

  def __init__(self):
    super().__init__()

    max_tokens = 10_000

    self.title_embedding = tf.keras.Sequential([
      tf.keras.layers.StringLookup(
          vocabulary=unique_movie_titles, mask_token=None),
      tf.keras.layers.Embedding(len(unique_movie_titles) + 1, 32)
    ])

    self.title_vectorizer = tf.keras.layers.TextVectorization(
        max_tokens=max_tokens)

    self.title_text_embedding = tf.keras.Sequential([
      self.title_vectorizer,
      tf.keras.layers.Embedding(max_tokens, 32, mask_zero=True),
      tf.keras.layers.GlobalAveragePooling1D(),
    ])

    self.title_vectorizer.adapt(movies)

  def call(self, titles):
    return tf.concat([
        self.title_embedding(titles),
        self.title_text_embedding(titles),
    ], axis=1)

결합 모델

모두 UserModelMovieModel 정의 된, 우리는 결합 된 모델을 함께 넣어 우리의 손실을 측정 로직을 구현할 수 있습니다.

또한 쿼리 모델과 후보 모델이 호환되는 크기의 임베딩을 출력하는지 확인해야 합니다. 더 많은 기능을 추가하여 크기를 다양화할 것이기 때문에 이를 수행하는 가장 쉬운 방법은 각 모델 다음에 조밀한 투영 레이어를 사용하는 것입니다.

class MovielensModel(tfrs.models.Model):

  def __init__(self, use_timestamps):
    super().__init__()
    self.query_model = tf.keras.Sequential([
      UserModel(use_timestamps),
      tf.keras.layers.Dense(32)
    ])
    self.candidate_model = tf.keras.Sequential([
      MovieModel(),
      tf.keras.layers.Dense(32)
    ])
    self.task = tfrs.tasks.Retrieval(
        metrics=tfrs.metrics.FactorizedTopK(
            candidates=movies.batch(128).map(self.candidate_model),
        ),
    )

  def compute_loss(self, features, training=False):
    # We only pass the user id and timestamp features into the query model. This
    # is to ensure that the training inputs would have the same keys as the
    # query inputs. Otherwise the discrepancy in input structure would cause an
    # error when loading the query model after saving it.
    query_embeddings = self.query_model({
        "user_id": features["user_id"],
        "timestamp": features["timestamp"],
    })
    movie_embeddings = self.candidate_model(features["movie_title"])

    return self.task(query_embeddings, movie_embeddings)

실험

데이터 준비

먼저 데이터를 훈련 세트와 테스트 세트로 나눕니다.

tf.random.set_seed(42)
shuffled = ratings.shuffle(100_000, seed=42, reshuffle_each_iteration=False)

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

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

기준: 타임스탬프 기능 없음

첫 번째 모델을 시험해 볼 준비가 되었습니다. 기준선을 설정하기 위해 타임스탬프 기능을 사용하지 않는 것부터 시작하겠습니다.

model = MovielensModel(use_timestamps=False)
model.compile(optimizer=tf.keras.optimizers.Adagrad(0.1))

model.fit(cached_train, epochs=3)

train_accuracy = model.evaluate(
    cached_train, return_dict=True)["factorized_top_k/top_100_categorical_accuracy"]
test_accuracy = model.evaluate(
    cached_test, return_dict=True)["factorized_top_k/top_100_categorical_accuracy"]

print(f"Top-100 accuracy (train): {train_accuracy:.2f}.")
print(f"Top-100 accuracy (test): {test_accuracy:.2f}.")
Epoch 1/3
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}
Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}
Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}
Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}
Consider rewriting this model with the Functional API.
40/40 [==============================] - 8s 131ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0092 - factorized_top_k/top_5_categorical_accuracy: 0.0172 - factorized_top_k/top_10_categorical_accuracy: 0.0256 - factorized_top_k/top_50_categorical_accuracy: 0.0824 - factorized_top_k/top_100_categorical_accuracy: 0.1473 - loss: 14579.4627 - regularization_loss: 0.0000e+00 - total_loss: 14579.4627
Epoch 2/3
40/40 [==============================] - 7s 130ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0020 - factorized_top_k/top_5_categorical_accuracy: 0.0126 - factorized_top_k/top_10_categorical_accuracy: 0.0251 - factorized_top_k/top_50_categorical_accuracy: 0.1129 - factorized_top_k/top_100_categorical_accuracy: 0.2133 - loss: 14136.2134 - regularization_loss: 0.0000e+00 - total_loss: 14136.2134
Epoch 3/3
40/40 [==============================] - 6s 126ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0021 - factorized_top_k/top_5_categorical_accuracy: 0.0155 - factorized_top_k/top_10_categorical_accuracy: 0.0307 - factorized_top_k/top_50_categorical_accuracy: 0.1389 - factorized_top_k/top_100_categorical_accuracy: 0.2535 - loss: 13939.9262 - regularization_loss: 0.0000e+00 - total_loss: 13939.9262
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}
Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}
Consider rewriting this model with the Functional API.
40/40 [==============================] - 7s 123ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0035 - factorized_top_k/top_5_categorical_accuracy: 0.0228 - factorized_top_k/top_10_categorical_accuracy: 0.0429 - factorized_top_k/top_50_categorical_accuracy: 0.1731 - factorized_top_k/top_100_categorical_accuracy: 0.2945 - loss: 13711.3804 - regularization_loss: 0.0000e+00 - total_loss: 13711.3804
5/5 [==============================] - 2s 177ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0012 - factorized_top_k/top_5_categorical_accuracy: 0.0077 - factorized_top_k/top_10_categorical_accuracy: 0.0184 - factorized_top_k/top_50_categorical_accuracy: 0.1052 - factorized_top_k/top_100_categorical_accuracy: 0.2129 - loss: 30995.9004 - regularization_loss: 0.0000e+00 - total_loss: 30995.9004
Top-100 accuracy (train): 0.29.
Top-100 accuracy (test): 0.21.

이것은 약 0.2의 기준 상위 100위 정확도를 제공합니다.

시간 기능으로 시간 역학 캡처

시간 기능을 추가하면 결과가 변경됩니까?

model = MovielensModel(use_timestamps=True)
model.compile(optimizer=tf.keras.optimizers.Adagrad(0.1))

model.fit(cached_train, epochs=3)

train_accuracy = model.evaluate(
    cached_train, return_dict=True)["factorized_top_k/top_100_categorical_accuracy"]
test_accuracy = model.evaluate(
    cached_test, return_dict=True)["factorized_top_k/top_100_categorical_accuracy"]

print(f"Top-100 accuracy (train): {train_accuracy:.2f}.")
print(f"Top-100 accuracy (test): {test_accuracy:.2f}.")
Epoch 1/3
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}
Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}
Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}
Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}
Consider rewriting this model with the Functional API.
40/40 [==============================] - 7s 127ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0059 - factorized_top_k/top_5_categorical_accuracy: 0.0148 - factorized_top_k/top_10_categorical_accuracy: 0.0237 - factorized_top_k/top_50_categorical_accuracy: 0.0811 - factorized_top_k/top_100_categorical_accuracy: 0.1475 - loss: 14605.1658 - regularization_loss: 0.0000e+00 - total_loss: 14605.1658
Epoch 2/3
40/40 [==============================] - 7s 127ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0026 - factorized_top_k/top_5_categorical_accuracy: 0.0154 - factorized_top_k/top_10_categorical_accuracy: 0.0300 - factorized_top_k/top_50_categorical_accuracy: 0.1380 - factorized_top_k/top_100_categorical_accuracy: 0.2513 - loss: 13955.9810 - regularization_loss: 0.0000e+00 - total_loss: 13955.9810
Epoch 3/3
40/40 [==============================] - 6s 129ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0024 - factorized_top_k/top_5_categorical_accuracy: 0.0194 - factorized_top_k/top_10_categorical_accuracy: 0.0398 - factorized_top_k/top_50_categorical_accuracy: 0.1722 - factorized_top_k/top_100_categorical_accuracy: 0.3017 - loss: 13696.8629 - regularization_loss: 0.0000e+00 - total_loss: 13696.8629
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}
Consider rewriting this model with the Functional API.
WARNING:tensorflow:Layers in a Sequential model should only have a single input tensor, but we receive a <class 'dict'> input: {'user_id': <tf.Tensor 'IteratorGetNext:2' shape=(None,) dtype=string>, 'timestamp': <tf.Tensor 'IteratorGetNext:1' shape=(None,) dtype=int64>}
Consider rewriting this model with the Functional API.
40/40 [==============================] - 7s 134ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0057 - factorized_top_k/top_5_categorical_accuracy: 0.0320 - factorized_top_k/top_10_categorical_accuracy: 0.0610 - factorized_top_k/top_50_categorical_accuracy: 0.2268 - factorized_top_k/top_100_categorical_accuracy: 0.3646 - loss: 13384.1319 - regularization_loss: 0.0000e+00 - total_loss: 13384.1319
5/5 [==============================] - 1s 183ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0015 - factorized_top_k/top_5_categorical_accuracy: 0.0095 - factorized_top_k/top_10_categorical_accuracy: 0.0223 - factorized_top_k/top_50_categorical_accuracy: 0.1269 - factorized_top_k/top_100_categorical_accuracy: 0.2486 - loss: 30700.6286 - regularization_loss: 0.0000e+00 - total_loss: 30700.6286
Top-100 accuracy (train): 0.36.
Top-100 accuracy (test): 0.25.

이것은 훨씬 더 좋습니다. 훈련 정확도가 훨씬 높을 뿐만 아니라 테스트 정확도도 상당히 향상됩니다.

다음 단계

이 튜토리얼은 더 많은 기능을 통합할 때 단순한 모델도 더 정확해질 수 있음을 보여줍니다. 그러나 기능을 최대한 활용하려면 더 크고 심층적인 모델을 구축해야 하는 경우가 많습니다. 상기 모습이 깊은 검색 튜토리얼 자세한 내용이 탐험을.