Film yang direkomendasikan: pengambilan menggunakan model sekuensial

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

Dalam tutorial ini, kita akan membangun model pengambilan sekuensial. Rekomendasi sekuensial adalah model populer yang melihat urutan item yang telah berinteraksi dengan pengguna sebelumnya dan kemudian memprediksi item berikutnya. Di sini urutan item dalam setiap urutan penting, jadi kita akan menggunakan jaringan saraf berulang untuk memodelkan hubungan sekuensial. Untuk lebih jelasnya, silakan lihat ini kertas GRU4Rec .

Impor

Pertama mari kita singkirkan ketergantungan dan impor kita.

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

from typing import Dict, Text

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

Menyiapkan kumpulan data

Selanjutnya, kita perlu menyiapkan dataset kita. Kita akan memanfaatkan utilitas pembuatan data dalam TensorFlow Lite On-perangkat referensi Rekomendasi aplikasi .

Data MovieLens 1M mengandung ratings.dat (kolom: UserID, MovieID, Penilaian, Timestamp), dan movies.dat (kolom: MovieID, Judul, Genre). Contoh pembuatan skrip mengunduh kumpulan data 1M, mengambil kedua file, hanya mempertahankan peringkat lebih tinggi dari 2, membentuk garis waktu interaksi film pengguna, aktivitas sampel sebagai label, dan 10 aktivitas pengguna sebelumnya sebagai konteks untuk prediksi.

wget -nc https://raw.githubusercontent.com/tensorflow/examples/master/lite/examples/recommendation/ml/data/example_generation_movielens.py
python -m example_generation_movielens  --data_dir=data/raw  --output_dir=data/examples  --min_timeline_length=3  --max_context_length=10  --max_context_movie_genre_length=10  --min_rating=2  --train_data_fraction=0.9  --build_vocabs=False
--2021-12-02 12:10:29--  https://raw.githubusercontent.com/tensorflow/examples/master/lite/examples/recommendation/ml/data/example_generation_movielens.py
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.110.133, 185.199.111.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 18040 (18K) [text/plain]
Saving to: ‘example_generation_movielens.py’

example_generation_ 100%[===================>]  17.62K  --.-KB/s    in 0s      

2021-12-02 12:10:29 (107 MB/s) - ‘example_generation_movielens.py’ saved [18040/18040]

I1202 12:10:32.036267 140629273970496 example_generation_movielens.py:460] Downloading and extracting data.
Downloading data from http://files.grouplens.org/datasets/movielens/ml-1m.zip
5922816/5917549 [==============================] - 1s 0us/step
5931008/5917549 [==============================] - 1s 0us/step
I1202 12:10:33.549675 140629273970496 example_generation_movielens.py:406] Reading data to dataframes.
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/pandas/util/_decorators.py:311: ParserWarning: Falling back to the 'python' engine because the 'c' engine does not support regex separators (separators > 1 char and different from '\s+' are interpreted as regex); you can avoid this warning by specifying engine='python'.
  return func(*args, **kwargs)
I1202 12:10:37.734699 140629273970496 example_generation_movielens.py:408] Generating movie rating user timelines.
I1202 12:10:40.836473 140629273970496 example_generation_movielens.py:410] Generating train and test examples.
6040/6040 [==============================] - 76s 13ms/step
I1202 12:11:57.162662 140629273970496 example_generation_movielens.py:421] Writing generated training examples.
844195/844195 [==============================] - 14s 17us/step
I1202 12:12:11.266682 140629273970496 example_generation_movielens.py:424] Writing generated testing examples.
93799/93799 [==============================] - 2s 17us/step
I1202 12:12:22.758407 140629273970496 example_generation_movielens.py:473] Generated dataset: {'train_size': 844195, 'test_size': 93799, 'train_file': 'data/examples/train_movielens_1m.tfrecord', 'test_file': 'data/examples/test_movielens_1m.tfrecord'}

Berikut adalah contoh dari dataset yang dihasilkan.

0 : {
  features: {
    feature: {
      key  : "context_movie_id"
      value: { int64_list: { value: [ 1124, 2240, 3251, ..., 1268 ] } }
    }
    feature: {
      key  : "context_movie_rating"
      value: { float_list: {value: [ 3.0, 3.0, 4.0, ..., 3.0 ] } }
    }
    feature: {
      key  : "context_movie_year"
      value: { int64_list: { value: [ 1981, 1980, 1985, ..., 1990 ] } }
    }
    feature: {
      key  : "context_movie_genre"
      value: { bytes_list: { value: [ "Drama", "Drama", "Mystery", ..., "UNK" ] } }
    }
    feature: {
      key  : "label_movie_id"
      value: { int64_list: { value: [ 3252 ] }  }
    }
  }
}

Anda dapat melihat bahwa itu mencakup urutan ID film konteks, dan ID film label (film berikutnya), ditambah fitur konteks seperti tahun film, peringkat dan genre.

Dalam kasus kami, kami hanya akan menggunakan urutan ID film konteks dan ID film label. Anda dapat mengacu pada konteks Memanfaatkan fitur tutorial untuk mempelajari lebih lanjut tentang menambahkan fitur konteks tambahan.

train_filename = "./data/examples/train_movielens_1m.tfrecord"
train = tf.data.TFRecordDataset(train_filename)

test_filename = "./data/examples/test_movielens_1m.tfrecord"
test = tf.data.TFRecordDataset(test_filename)

feature_description = {
    'context_movie_id': tf.io.FixedLenFeature([10], tf.int64, default_value=np.repeat(0, 10)),
    'context_movie_rating': tf.io.FixedLenFeature([10], tf.float32, default_value=np.repeat(0, 10)),
    'context_movie_year': tf.io.FixedLenFeature([10], tf.int64, default_value=np.repeat(1980, 10)),
    'context_movie_genre': tf.io.FixedLenFeature([10], tf.string, default_value=np.repeat("Drama", 10)),
    'label_movie_id': tf.io.FixedLenFeature([1], tf.int64, default_value=0),
}

def _parse_function(example_proto):
  return tf.io.parse_single_example(example_proto, feature_description)

train_ds = train.map(_parse_function).map(lambda x: {
    "context_movie_id": tf.strings.as_string(x["context_movie_id"]),
    "label_movie_id": tf.strings.as_string(x["label_movie_id"])
})

test_ds = test.map(_parse_function).map(lambda x: {
    "context_movie_id": tf.strings.as_string(x["context_movie_id"]),
    "label_movie_id": tf.strings.as_string(x["label_movie_id"])
})

for x in train_ds.take(1).as_numpy_iterator():
  pprint.pprint(x)
{'context_movie_id': array([b'2589', b'202', b'1038', b'1767', b'951', b'129', b'1256', b'955',
       b'3097', b'3462'], dtype=object),
 'label_movie_id': array([b'3629'], dtype=object)}

Sekarang set data kereta/pengujian kami hanya menyertakan urutan ID film historis dan label ID film berikutnya. Perhatikan bahwa kita menggunakan [10] sebagai bentuk fitur selama tf.Example parsing karena kita tentukan 10 sebagai panjang dari konteks fitur pada langkah contoh generateion.

Kami membutuhkan satu hal lagi sebelum kami dapat mulai membangun model - kosakata untuk ID film kami.

movies = tfds.load("movielens/1m-movies", split='train')
movies = movies.map(lambda x: x["movie_id"])
movie_ids = movies.batch(1_000)
unique_movie_ids = np.unique(np.concatenate(list(movie_ids)))

Menerapkan model sekuensial

Dalam kami dasar pengambilan tutorial , kami menggunakan satu tower permintaan bagi pengguna, dan tow calon film calon. Namun, arsitektur dua menara dapat digeneralisasikan dan tidak terbatas pada pasangan. Anda juga dapat menggunakannya untuk melakukan rekomendasi item-ke-item seperti yang kita perhatikan di tutorial pengambilan dasar .

Di sini kita masih akan menggunakan arsitektur dua menara. Specificially, kita menggunakan menara permintaan dengan lapisan Gated berulang Unit (GRU) untuk mengkodekan urutan film sejarah, dan menjaga menara calon yang sama untuk film calon.

embedding_dimension = 32

query_model = tf.keras.Sequential([
    tf.keras.layers.StringLookup(
      vocabulary=unique_movie_ids, mask_token=None),
    tf.keras.layers.Embedding(len(unique_movie_ids) + 1, embedding_dimension), 
    tf.keras.layers.GRU(embedding_dimension),
])

candidate_model = tf.keras.Sequential([
  tf.keras.layers.StringLookup(
      vocabulary=unique_movie_ids, mask_token=None),
  tf.keras.layers.Embedding(len(unique_movie_ids) + 1, embedding_dimension)
])

Metrik, tugas, dan model lengkap didefinisikan mirip dengan model pengambilan dasar.

metrics = tfrs.metrics.FactorizedTopK(
  candidates=movies.batch(128).map(candidate_model)
)

task = tfrs.tasks.Retrieval(
  metrics=metrics
)

class Model(tfrs.Model):

    def __init__(self, query_model, candidate_model):
        super().__init__()
        self._query_model = query_model
        self._candidate_model = candidate_model

        self._task = task

    def compute_loss(self, features, training=False):
        watch_history = features["context_movie_id"]
        watch_next_label = features["label_movie_id"]

        query_embedding = self._query_model(watch_history)       
        candidate_embedding = self._candidate_model(watch_next_label)

        return self._task(query_embedding, candidate_embedding, compute_metrics=not training)

Pas dan mengevaluasi

Kami sekarang dapat mengkompilasi, melatih dan mengevaluasi model pengambilan sekuensial kami.

model = Model(query_model, candidate_model)
model.compile(optimizer=tf.keras.optimizers.Adagrad(learning_rate=0.1))
cached_train = train_ds.shuffle(10_000).batch(12800).cache()
cached_test = test_ds.batch(2560).cache()
model.fit(cached_train, epochs=3)
Epoch 1/3
67/67 [==============================] - 25s 291ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_5_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_10_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_50_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_100_categorical_accuracy: 0.0000e+00 - loss: 107448.4467 - regularization_loss: 0.0000e+00 - total_loss: 107448.4467
Epoch 2/3
67/67 [==============================] - 2s 25ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_5_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_10_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_50_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_100_categorical_accuracy: 0.0000e+00 - loss: 100932.0125 - regularization_loss: 0.0000e+00 - total_loss: 100932.0125
Epoch 3/3
67/67 [==============================] - 2s 25ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_5_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_10_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_50_categorical_accuracy: 0.0000e+00 - factorized_top_k/top_100_categorical_accuracy: 0.0000e+00 - loss: 99336.2015 - regularization_loss: 0.0000e+00 - total_loss: 99336.2015
<keras.callbacks.History at 0x7f0904d5b410>
model.evaluate(cached_test, return_dict=True)
37/37 [==============================] - 10s 235ms/step - factorized_top_k/top_1_categorical_accuracy: 0.0146 - factorized_top_k/top_5_categorical_accuracy: 0.0780 - factorized_top_k/top_10_categorical_accuracy: 0.1358 - factorized_top_k/top_50_categorical_accuracy: 0.3735 - factorized_top_k/top_100_categorical_accuracy: 0.5058 - loss: 15478.0652 - regularization_loss: 0.0000e+00 - total_loss: 15478.0652
{'factorized_top_k/top_1_categorical_accuracy': 0.014605699107050896,
 'factorized_top_k/top_5_categorical_accuracy': 0.07804987579584122,
 'factorized_top_k/top_10_categorical_accuracy': 0.1358330100774765,
 'factorized_top_k/top_50_categorical_accuracy': 0.3735221028327942,
 'factorized_top_k/top_100_categorical_accuracy': 0.5058262944221497,
 'loss': 9413.1240234375,
 'regularization_loss': 0,
 'total_loss': 9413.1240234375}

Ini menyimpulkan tutorial pengambilan berurutan.