Kursus
Tujuan tutorial ini bukan untuk menjadikan Anda ahli dalam membangun model sistem rekomendasi. Sebaliknya, tujuannya adalah membantu Anda memulai dengan memberikan gambaran umum tentang jenis-jenis sistem rekomendasi yang ada dan bagaimana Anda dapat membangunnya sendiri
Dalam tutorial ini, Anda akan mempelajari cara membangun model dasar dari sistem rekomendasi sederhana dan berbasis konten. Walaupun model-model ini tidak akan mendekati standar industri dalam hal kompleksitas, kualitas, atau akurasi, model tersebut akan membantu Anda memulai membangun model yang lebih kompleks dengan hasil yang lebih baik.
Sistem rekomendasi adalah salah satu aplikasi ilmu data yang paling populer saat ini. Sistem ini digunakan untuk memprediksi "rating" atau "preferensi" yang akan diberikan pengguna terhadap sebuah item. Hampir setiap perusahaan teknologi besar telah menerapkannya dalam beberapa bentuk. Amazon menggunakannya untuk menyarankan produk kepada pelanggan, YouTube menggunakannya untuk menentukan video mana yang diputar berikutnya pada autoplay, dan Facebook menggunakannya untuk merekomendasikan halaman untuk disukai dan orang untuk diikuti.
Lebih dari itu, bagi beberapa perusahaan seperti Netflix, Amazon Prime, Hulu, dan Hotstar, model bisnis dan keberhasilannya berputar di sekitar ketepatan rekomendasi mereka. Netflix bahkan menawarkan hadiah satu juta dolar pada 2009 kepada siapa pun yang dapat meningkatkan sistemnya sebesar 10%.
Ada juga sistem rekomendasi populer untuk domain seperti restoran, film, dan kencan daring. Sistem rekomendasi juga dikembangkan untuk menjelajahi artikel riset dan pakar, kolaborator, serta layanan keuangan. YouTube menggunakan sistem rekomendasi dalam skala besar untuk menyarankan video berdasarkan riwayat Anda. Misalnya, jika Anda sering menonton video edukasi, sistem akan menyarankan video sejenis.
Tapi apa sebenarnya sistem rekomendasi itu?
Secara garis besar, sistem rekomendasi dapat diklasifikasikan menjadi 3 tipe:
- Rekomender sederhana: menawarkan rekomendasi umum kepada setiap pengguna, berdasarkan popularitas dan/atau genre film. Gagasan dasar di balik sistem ini adalah film yang lebih populer dan diakui secara kritis akan memiliki probabilitas lebih tinggi disukai oleh penonton rata-rata. Contohnya bisa seperti IMDB Top 250.
- Rekomender berbasis konten: menyarankan item serupa berdasarkan item tertentu. Sistem ini menggunakan metadata item, seperti genre, sutradara, deskripsi, aktor, dll. untuk film, guna membuat rekomendasi. Gagasan umum di balik sistem ini adalah jika seseorang menyukai suatu item, ia juga akan menyukai item yang serupa. Untuk merekomendasikannya, sistem akan memanfaatkan metadata item yang telah dilihat pengguna di masa lalu. Contoh yang baik adalah YouTube, yang berdasarkan riwayat Anda, menyarankan video baru yang mungkin Anda tonton.
- Mesin collaborative filtering: ini adalah sistem yang banyak digunakan, dan mencoba memprediksi rating atau preferensi yang akan diberikan pengguna pada suatu item berdasarkan rating dan preferensi pengguna lain di masa lalu. Collaborative filtering tidak memerlukan metadata item seperti halnya sistem berbasis konten.
Rekomender Sederhana
Seperti dijelaskan pada bagian sebelumnya, rekomender sederhana adalah sistem dasar yang merekomendasikan item teratas berdasarkan metrik atau skor tertentu. Pada bagian ini, Anda akan membangun klon sederhana dari IMDB Top 250 Movies menggunakan metadata yang dikumpulkan dari IMDB.
Langkah-langkah yang terlibat adalah sebagai berikut:
-
Tentukan metrik atau skor untuk menilai film.
-
Hitung skor untuk setiap film.
-
Urutkan film berdasarkan skor dan keluarkan hasil teratas.
Tentang Dataset
Berkas dataset berisi metadata untuk semua 45.000 film yang tercantum dalam Full MovieLens Dataset. Dataset ini terdiri dari film yang dirilis pada atau sebelum Juli 2017. Dataset ini menangkap titik fitur seperti pemeran, kru, kata kunci plot, anggaran, pendapatan, poster, tanggal rilis, bahasa, perusahaan produksi, negara, jumlah suara TMDB, dan rata-rata suara.
Titik-titik fitur ini berpotensi dapat digunakan untuk melatih model pembelajaran mesin Anda untuk filtering berbasis konten dan kolaboratif.
Dataset ini terdiri dari berkas-berkas berikut:
- movies_metadata.csv: Berkas ini berisi informasi tentang ~45.000 film yang ditampilkan dalam dataset Full MovieLens. Fitur mencakup poster, latar belakang, anggaran, genre, pendapatan, tanggal rilis, bahasa, negara produksi, dan perusahaan.
- keywords.csv: Berisi kata kunci plot film untuk film MovieLens kita. Tersedia dalam bentuk Objek JSON yang di-stringify.
- credits.csv: Terdiri dari informasi Pemeran dan Kru untuk semua film. Tersedia dalam bentuk Objek JSON yang di-stringify.
- links.csv: Berkas ini berisi ID TMDB dan IMDB untuk semua film yang ditampilkan dalam dataset Full MovieLens.
- links_small.csv: Berisi ID TMDB dan IMDB untuk subset kecil 9.000 film dari Dataset Lengkap.
- ratings_small.csv: Subset 100.000 rating dari 700 pengguna pada 9.000 film.
Full MovieLens Dataset terdiri dari 26 juta rating dan 750.000 aplikasi tag, dari 270.000 pengguna pada semua 45.000 film dalam dataset ini. Dataset dapat diakses dari situs web GroupLens resmi.
Catatan: Subset dataset yang digunakan dalam tutorial hari ini dapat diunduh dari sini.
Untuk memuat dataset Anda, Anda akan menggunakan pustaka DataFrame pandas. Pustaka pandas terutama digunakan untuk manipulasi dan analisis data. Pustaka ini merepresentasikan data Anda dalam format baris-kolom. Pustaka Pandas didukung oleh array NumPy untuk implementasi objek data pandas. pandas menawarkan struktur data siap pakai dan operasi untuk memanipulasi tabel numerik, deret waktu, citra, dan dataset pemrosesan bahasa alami. Intinya, pandas berguna untuk dataset yang dapat dengan mudah direpresentasikan dalam bentuk tabel.
Sebelum melakukan salah satu langkah di atas, mari muat dataset metadata film Anda ke dalam DataFrame pandas:
# Import Pandas
import pandas as pd
# Load Movies Metadata
metadata = pd.read_csv('movies_metadata.csv', low_memory=False)
# Print the first three rows
metadata.head(3)
| adult | belongs_to_collection | budget | genres | homepage | id | imdb_id | original_language | original_title | overview | ... | release_date | revenue | runtime | spoken_languages | status | tagline | title | video | vote_average | vote_count | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | False | {'id': 10194, 'name': 'Toy Story Collection', ... | 30000000 | [{'id': 16, 'name': 'Animation'}, {'id': 35, '... | http://toystory.disney.com/toy-story | 862 | tt0114709 | en | Toy Story | Led by Woody, Andy's toys live happily in his ... | ... | 1995-10-30 | 373554033.0 | 81.0 | [{'iso_639_1': 'en', 'name': 'English'}] | Released | NaN | Toy Story | False | 7.7 | 5415.0 |
| 1 | False | NaN | 65000000 | [{'id': 12, 'name': 'Adventure'}, {'id': 14, '... | NaN | 8844 | tt0113497 | en | Jumanji | When siblings Judy and Peter discover an encha... | ... | 1995-12-15 | 262797249.0 | 104.0 | [{'iso_639_1': 'en', 'name': 'English'}, {'iso... | Released | Roll the dice and unleash the excitement! | Jumanji | False | 6.9 | 2413.0 |
| 2 | False | {'id': 119050, 'name': 'Grumpy Old Men Collect... | 0 | [{'id': 10749, 'name': 'Romance'}, {'id': 35, ... | NaN | 15602 | tt0113228 | en | Grumpier Old Men | A family wedding reignites the ancient feud be... | ... | 1995-12-22 | 0.0 | 101.0 | [{'iso_639_1': 'en', 'name': 'English'}] | Released | Still Yelling. Still Fighting. Still Ready for... | Grumpier Old Men | False | 6.5 | 92.0 |
3 baris × 24 kolom
Salah satu metrik paling dasar yang dapat Anda pikirkan adalah pemeringkatan untuk menentukan 250 film teratas berdasarkan rating masing-masing.
Namun, menggunakan rating sebagai metrik memiliki beberapa kekurangan:
-
Pertama, metrik ini tidak mempertimbangkan popularitas sebuah film. Oleh karena itu, film dengan rating 9 dari 10 pemilih akan dianggap 'lebih baik' daripada film dengan rating 8,9 dari 10.000 pemilih.
Misalnya, bayangkan Anda ingin memesan makanan Cina, Anda punya beberapa pilihan, satu restoran memiliki rating 5 bintang oleh hanya 5 orang sementara restoran lain memiliki rating 4,5 oleh 1000 orang. Restoran mana yang Anda pilih? Yang kedua, bukan?
Tentu, bisa saja ada pengecualian bahwa restoran pertama baru dibuka beberapa hari lalu; sehingga, lebih sedikit orang yang menilai, sedangkan restoran kedua sudah beroperasi selama setahun.
- Terkait dengan itu, metrik ini juga cenderung menguntungkan film dengan jumlah pemilih sedikit yang condong dan/atau memiliki rating sangat tinggi. Saat jumlah pemilih meningkat, rating sebuah film akan ternormalisasi dan mendekati nilai yang mencerminkan kualitas film dan memberi pengguna gambaran yang jauh lebih baik tentang film mana yang sebaiknya dipilih. Sementara sulit menilai kualitas film dengan pemilih yang sangat sedikit, Anda mungkin perlu mempertimbangkan sumber eksternal untuk menyimpulkan.
Dengan mempertimbangkan kekurangan ini, Anda harus menyusun rating berbobot yang memperhitungkan rating rata-rata dan jumlah suara yang terakumulasi. Sistem semacam ini akan memastikan bahwa film dengan rating 9 dari 100.000 pemilih mendapatkan skor (jauh) lebih tinggi dibanding film dengan rating sama tetapi hanya beberapa ratus pemilih.
Karena Anda mencoba membangun klon dari IMDB Top 250, mari gunakan rumus rating berbobotnya sebagai metrik/skor. Secara matematis, direpresentasikan sebagai berikut:
\begin{equation} \text Weighted Rating (\bf WR) = \left({{\bf v} \over {\bf v} + {\bf m}} \cdot R\right) + \left({{\bf m} \over {\bf v} + {\bf m}} \cdot C\right) \end{equation}
Pada persamaan di atas,
-
v adalah jumlah suara untuk film;
-
m adalah jumlah suara minimum yang diperlukan untuk masuk daftar;
-
R adalah rating rata-rata film;
-
C adalah rata-rata suara di seluruh laporan.
Anda sudah memiliki nilai v (vote_count) dan R (vote_average) untuk setiap film dalam dataset. Nilai C juga dapat dihitung langsung dari data ini.
Menentukan nilai yang sesuai untuk m adalah sebuah hyperparameter yang dapat Anda pilih sesuai kebutuhan karena tidak ada nilai yang pasti benar untuk m. Anda dapat menganggapnya sebagai filter negatif awal yang akan menghapus film yang memiliki jumlah suara kurang dari ambang m tertentu. Selektivitas filter terserah kebijaksanaan Anda.
Dalam tutorial ini, Anda akan menggunakan cutoff m sebagai persentil ke-90. Dengan kata lain, agar sebuah film ditampilkan di tangga lagu, film tersebut harus memiliki suara lebih banyak daripada setidaknya 90% film dalam daftar. (Di sisi lain, jika Anda memilih persentil ke-75, Anda akan mempertimbangkan 25% film teratas dalam hal jumlah suara yang diperoleh. Saat persentil menurun, jumlah film yang dipertimbangkan akan meningkat).
Sebagai langkah pertama, mari hitung nilai C, rating rata-rata di seluruh film menggunakan fungsi .mean() dari pandas:
# Calculate mean of vote average column
C = metadata['vote_average'].mean()
print(C)
5.618207215133889
Dari keluaran di atas, Anda dapat melihat bahwa rating rata-rata sebuah film di IMDB sekitar 5,6 pada skala 10.
Selanjutnya, mari hitung jumlah suara, m, yang diterima sebuah film pada persentil ke-90. Pustaka pandas membuat tugas ini sangat mudah menggunakan metode .quantile() dari pandas:
# Calculate the minimum number of votes required to be in the chart, m
m = metadata['vote_count'].quantile(0.90)
print(m)
160.0
Karena sekarang Anda memiliki m Anda dapat menggunakan kondisi lebih besar dari sama dengan untuk menyaring film yang memiliki jumlah suara lebih besar dari sama dengan 160:
Anda dapat menggunakan metode .copy() untuk memastikan bahwa DataFrame baru q_movies yang dibuat independen dari DataFrame metadata asli Anda. Dengan kata lain, perubahan apa pun yang dibuat pada DataFrame q_movies tidak akan memengaruhi DataFrame metadata asli.
# Filter out all qualified movies into a new DataFrame
q_movies = metadata.copy().loc[metadata['vote_count'] >= m]
q_movies.shape
(4555, 24)
metadata.shape
(45466, 24)
Dari keluaran di atas, jelas bahwa ada sekitar 10% film dengan jumlah suara lebih dari 160 dan memenuhi syarat untuk masuk daftar ini.
Langkah berikutnya dan paling penting adalah menghitung rating berbobot untuk setiap film yang memenuhi syarat. Untuk melakukannya, Anda akan:
- Menentukan fungsi,
weighted_rating(); - Karena Anda sudah menghitung
mdanCAnda cukup mengoper keduanya sebagai argumen ke fungsi; - Kemudian Anda akan memilih kolom
vote_count(v) danvote_average(R) dari data frameq_movies; - Terakhir, Anda akan menghitung rata-rata berbobot dan mengembalikan hasilnya.
Anda akan mendefinisikan fitur baru score, yang nilainya akan Anda hitung dengan menerapkan fungsi ini ke DataFrame film yang memenuhi syarat:
# Function that computes the weighted rating of each movie
def weighted_rating(x, m=m, C=C):
v = x['vote_count']
R = x['vote_average']
# Calculation based on the IMDB formula
return (v/(v+m) * R) + (m/(m+v) * C)
# Define a new feature 'score' and calculate its value with `weighted_rating()`
q_movies['score'] = q_movies.apply(weighted_rating, axis=1)
Terakhir, mari urutkan DataFrame secara menurun berdasarkan kolom fitur score dan keluarkan judul, jumlah suara, rata-rata suara, dan rating berbobot (skor) dari 20 film teratas.
#Sort movies based on score calculated above
q_movies = q_movies.sort_values('score', ascending=False)
#Print the top 15 movies
q_movies[['title', 'vote_count', 'vote_average', 'score']].head(20)
| title | vote_count | vote_average | score | |
|---|---|---|---|---|
| 314 | The Shawshank Redemption | 8358.0 | 8.5 | 8.445869 |
| 834 | The Godfather | 6024.0 | 8.5 | 8.425439 |
| 10309 | Dilwale Dulhania Le Jayenge | 661.0 | 9.1 | 8.421453 |
| 12481 | The Dark Knight | 12269.0 | 8.3 | 8.265477 |
| 2843 | Fight Club | 9678.0 | 8.3 | 8.256385 |
| 292 | Pulp Fiction | 8670.0 | 8.3 | 8.251406 |
| 522 | Schindler's List | 4436.0 | 8.3 | 8.206639 |
| 23673 | Whiplash | 4376.0 | 8.3 | 8.205404 |
| 5481 | Spirited Away | 3968.0 | 8.3 | 8.196055 |
| 2211 | Life Is Beautiful | 3643.0 | 8.3 | 8.187171 |
| 1178 | The Godfather: Part II | 3418.0 | 8.3 | 8.180076 |
| 1152 | One Flew Over the Cuckoo's Nest | 3001.0 | 8.3 | 8.164256 |
| 351 | Forrest Gump | 8147.0 | 8.2 | 8.150272 |
| 1154 | The Empire Strikes Back | 5998.0 | 8.2 | 8.132919 |
| 1176 | Psycho | 2405.0 | 8.3 | 8.132715 |
| 18465 | The Intouchables | 5410.0 | 8.2 | 8.125837 |
| 40251 | Your Name. | 1030.0 | 8.5 | 8.112532 |
| 289 | Leon: The Professional | 4293.0 | 8.2 | 8.107234 |
| 3030 | The Green Mile | 4166.0 | 8.2 | 8.104511 |
| 1170 | GoodFellas | 3211.0 | 8.2 | 8.077459 |
Nah, dari keluaran di atas, Anda dapat melihat bahwa rekomender sederhana bekerja dengan sangat baik!
Karena tangga lagu memiliki banyak film yang sama dengan IMDB Top 250: misalnya, dua film teratas Anda, "Shawshank Redemption" dan "The Godfather", sama seperti di IMDB dan kita semua tahu itu memang film yang luar biasa, faktanya, semua 20 film teratas memang pantas masuk daftar itu, bukan begitu?
Rekomender Berbasis Konten
Rekomender Berdasarkan Deskripsi Plot
Pada bagian tutorial ini, Anda akan mempelajari cara membangun sistem yang merekomendasikan film yang mirip dengan film tertentu. Untuk mencapainya, Anda akan menghitung skor kemiripan cosine berpasangan untuk semua film berdasarkan deskripsi plot mereka dan merekomendasikan film berdasarkan ambang skor kemiripan tersebut.
Deskripsi plot tersedia bagi Anda sebagai fitur overview dalam dataset metadata Anda. Mari periksa plot beberapa film:
#Print plot overviews of the first 5 movies.
metadata['overview'].head()
0 Led by Woody, Andy's toys live happily in his ...
1 When siblings Judy and Peter discover an encha...
2 A family wedding reignites the ancient feud be...
3 Cheated on, mistreated and stepped on, the wom...
4 Just when George Banks has recovered from his ...
Name: overview, dtype: object
Masalah yang dihadapi adalah masalah Pemrosesan Bahasa Alami. Karena itu Anda perlu mengekstrak semacam fitur dari data teks di atas sebelum Anda dapat menghitung kemiripan dan/atau ketidakmiripan di antara mereka. Sederhananya, tidak mungkin menghitung kemiripan antara dua overview dalam bentuk mentahnya. Untuk melakukannya, Anda perlu menghitung vektor kata dari setiap overview atau dokumen, sebagaimana akan disebut mulai sekarang.
Seperti namanya, vektor kata adalah representasi tervektor dari kata-kata dalam sebuah dokumen. Vektor membawa makna semantik. Misalnya, man & king akan memiliki representasi vektor yang berdekatan sementara man & woman akan memiliki representasi yang berjauhan.
Anda akan menghitung vektor Term Frequency-Inverse Document Frequency (TF-IDF) untuk setiap dokumen. Ini akan memberikan matriks di mana setiap kolom merepresentasikan sebuah kata dalam kosakata overview (semua kata yang muncul setidaknya dalam satu dokumen), dan setiap kolom merepresentasikan sebuah film, seperti sebelumnya.
Pada intinya, skor TF-IDF adalah frekuensi kemunculan sebuah kata dalam dokumen, diturunkan bobotnya oleh jumlah dokumen di mana kata tersebut muncul. Ini dilakukan untuk mengurangi pentingnya kata-kata yang sering muncul dalam overview plot dan, karenanya, signifikansinya dalam menghitung skor kemiripan akhir.
Untungnya, scikit-learn menyediakan kelas bawaan TfIdfVectorizer yang menghasilkan matriks TF-IDF hanya dalam beberapa baris.
- Impor modul Tfidf menggunakan scikit-learn;
- Hapus stop words seperti 'the', 'an', dll. karena tidak memberikan informasi berguna tentang topik;
- Ganti nilai not-a-number dengan string kosong;
- Terakhir, bangun matriks TF-IDF pada data.
#Import TfIdfVectorizer from scikit-learn
from sklearn.feature_extraction.text import TfidfVectorizer
#Define a TF-IDF Vectorizer Object. Remove all english stop words such as 'the', 'a'
tfidf = TfidfVectorizer(stop_words='english')
#Replace NaN with an empty string
metadata['overview'] = metadata['overview'].fillna('')
#Construct the required TF-IDF matrix by fitting and transforming the data
tfidf_matrix = tfidf.fit_transform(metadata['overview'])
#Output the shape of tfidf_matrix
tfidf_matrix.shape
(45466, 75827)
#Array mapping from feature integer indices to feature name.
tfidf.get_feature_names()[5000:5010]
['avails',
'avaks',
'avalanche',
'avalanches',
'avallone',
'avalon',
'avant',
'avanthika',
'avanti',
'avaracious']
Dari keluaran di atas, Anda mengamati bahwa terdapat 75.827 kosakata atau kata berbeda dalam dataset Anda dengan 45.000 film.
Dengan matriks ini, Anda sekarang dapat menghitung skor kemiripan. Ada beberapa metrik kemiripan yang dapat Anda gunakan, seperti manhattan, euclidean, Pearson, dan skor kemiripan cosine. Sekali lagi, tidak ada jawaban yang pasti benar mengenai skor mana yang terbaik. Skor yang berbeda bekerja dengan baik dalam skenario berbeda, dan sering kali merupakan ide bagus untuk bereksperimen dengan berbagai metrik dan mengamati hasilnya.
Anda akan menggunakan cosine similarity untuk menghitung besaran numerik yang menunjukkan kemiripan antara dua film. Anda menggunakan skor kemiripan cosine karena skor ini independen dari magnitudo dan relatif mudah serta cepat dihitung (terutama saat digunakan bersama skor TF-IDF, yang akan dijelaskan nanti). Secara matematis, didefinisikan sebagai berikut:

Karena Anda telah menggunakan vektorisasi TF-IDF, menghitung hasil kali titik antara setiap vektor akan langsung memberikan skor kemiripan cosine. Oleh karena itu, Anda akan menggunakan linear_kernel() dari sklearn alih-alih cosine_similarities() karena lebih cepat.
Ini akan mengembalikan matriks berukuran 45466x45466, yang berarti setiap skor kemiripan cosine overview film dengan setiap overview film lainnya. Dengan demikian, setiap film akan menjadi vektor kolom 1x45466 di mana setiap kolom adalah skor kemiripan dengan tiap film.
# Import linear_kernel
from sklearn.metrics.pairwise import linear_kernel
# Compute the cosine similarity matrix
cosine_sim = linear_kernel(tfidf_matrix, tfidf_matrix)
cosine_sim.shape
(45466, 45466)
cosine_sim[1]
array([0.01504121, 1. , 0.04681953, ..., 0. , 0.02198641,
0.00929411])
Anda akan mendefinisikan fungsi yang menerima judul film sebagai masukan dan menghasilkan daftar 10 film paling mirip. Pertama, untuk ini, Anda memerlukan pemetaan balik antara judul film dan indeks DataFrame. Dengan kata lain, Anda memerlukan mekanisme untuk mengidentifikasi indeks sebuah film dalam DataFrame metadata, diberikan judulnya.
#Construct a reverse map of indices and movie titles
indices = pd.Series(metadata.index, index=metadata['title']).drop_duplicates()
indices[:10]
title
Toy Story 0
Jumanji 1
Grumpier Old Men 2
Waiting to Exhale 3
Father of the Bride Part II 4
Heat 5
Sabrina 6
Tom and Huck 7
Sudden Death 8
GoldenEye 9
dtype: int64
Sekarang Anda berada pada posisi yang baik untuk mendefinisikan fungsi rekomendasi. Berikut langkah-langkah yang akan Anda ikuti:
-
Dapatkan indeks film berdasarkan judulnya.
-
Dapatkan daftar skor kemiripan cosine untuk film tertentu dengan semua film. Konversikan menjadi daftar tuple di mana elemen pertama adalah posisinya, dan yang kedua adalah skor kemiripan.
-
Urutkan daftar tuple tersebut berdasarkan skor kemiripan; yaitu elemen kedua.
-
Ambil 10 elemen teratas dari daftar ini. Abaikan elemen pertama karena merujuk pada diri sendiri (film yang paling mirip dengan sebuah film adalah film itu sendiri).
-
Kembalikan judul yang sesuai dengan indeks elemen teratas.
# Function that takes in movie title as input and outputs most similar movies
def get_recommendations(title, cosine_sim=cosine_sim):
# Get the index of the movie that matches the title
idx = indices[title]
# Get the pairwsie similarity scores of all movies with that movie
sim_scores = list(enumerate(cosine_sim[idx]))
# Sort the movies based on the similarity scores
sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
# Get the scores of the 10 most similar movies
sim_scores = sim_scores[1:11]
# Get the movie indices
movie_indices = [i[0] for i in sim_scores]
# Return the top 10 most similar movies
return metadata['title'].iloc[movie_indices]
get_recommendations('The Dark Knight Rises')
12481 The Dark Knight
150 Batman Forever
1328 Batman Returns
15511 Batman: Under the Red Hood
585 Batman
21194 Batman Unmasked: The Psychology of the Dark Kn...
9230 Batman Beyond: Return of the Joker
18035 Batman: Year One
19792 Batman: The Dark Knight Returns, Part 1
3095 Batman: Mask of the Phantasm
Name: title, dtype: object
get_recommendations('The Godfather')
1178 The Godfather: Part II
44030 The Godfather Trilogy: 1972-1990
1914 The Godfather: Part III
23126 Blood Ties
11297 Household Saints
34717 Start Liquidation
10821 Election
38030 A Mother Should Be Loved
17729 Short Sharp Shock
26293 Beck 28 - Familjen
Name: title, dtype: object
Anda melihat bahwa, meskipun sistem Anda telah melakukan pekerjaan yang layak dalam menemukan film dengan deskripsi plot serupa, kualitas rekomendasi belum terlalu bagus. "The Dark Knight Rises" mengembalikan semua film Batman sementara kemungkinan besar orang yang menyukai film itu juga cenderung menikmati film Christopher Nolan lainnya. Ini adalah sesuatu yang tidak dapat ditangkap oleh sistem Anda saat ini.
Rekomender Berdasarkan Credits, Genre, dan Kata Kunci
Kualitas rekomender Anda akan meningkat dengan penggunaan metadata yang lebih baik dan dengan menangkap lebih banyak detail yang lebih halus. Itulah yang akan Anda lakukan pada bagian ini. Anda akan membangun sistem rekomendasi berdasarkan metadata berikut: 3 aktor teratas, sutradara, genre terkait, dan kata kunci plot film.
Kata kunci, pemeran, dan data kru tidak tersedia dalam dataset Anda saat ini, jadi langkah pertama adalah memuat dan menggabungkannya ke dalam DataFrame utama metadata.
# Load keywords and credits
credits = pd.read_csv('credits.csv')
keywords = pd.read_csv('keywords.csv')
# Remove rows with bad IDs.
metadata = metadata.drop([19730, 29503, 35587])
# Convert IDs to int. Required for merging
keywords['id'] = keywords['id'].astype('int')
credits['id'] = credits['id'].astype('int')
metadata['id'] = metadata['id'].astype('int')
# Merge keywords and credits into your main metadata dataframe
metadata = metadata.merge(credits, on='id')
metadata = metadata.merge(keywords, on='id')
# Print the first two movies of your newly merged metadata
metadata.head(2)
| adult | belongs_to_collection | budget | genres | homepage | id | imdb_id | original_language | original_title | overview | ... | spoken_languages | status | tagline | title | video | vote_average | vote_count | cast | crew | keywords | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | False | {'id': 10194, 'name': 'Toy Story Collection', ... | 30000000 | [{'id': 16, 'name': 'Animation'}, {'id': 35, '... | http://toystory.disney.com/toy-story | 862 | tt0114709 | en | Toy Story | Led by Woody, Andy's toys live happily in his ... | ... | [{'iso_639_1': 'en', 'name': 'English'}] | Released | NaN | Toy Story | False | 7.7 | 5415.0 | [{'cast_id': 14, 'character': 'Woody (voice)',... | [{'credit_id': '52fe4284c3a36847f8024f49', 'de... | [{'id': 931, 'name': 'jealousy'}, {'id': 4290,... |
| 1 | False | NaN | 65000000 | [{'id': 12, 'name': 'Adventure'}, {'id': 14, '... | NaN | 8844 | tt0113497 | en | Jumanji | When siblings Judy and Peter discover an encha... | ... | [{'iso_639_1': 'en', 'name': 'English'}, {'iso... | Released | Roll the dice and unleash the excitement! | Jumanji | False | 6.9 | 2413.0 | [{'cast_id': 1, 'character': 'Alan Parrish', '... | [{'credit_id': '52fe44bfc3a36847f80a7cd1', 'de... | [{'id': 10090, 'name': 'board game'}, {'id': 1... |
2 baris × 27 kolom
Dari fitur baru Anda, cast, crew, dan keywords, Anda perlu mengekstrak tiga aktor terpenting, sutradara, dan kata kunci yang terkait dengan film tersebut.
Tetapi pertama-tama, data Anda hadir dalam bentuk daftar yang di-"stringify". Anda perlu mengonversinya ke bentuk yang dapat digunakan.
# Parse the stringified features into their corresponding python objects
from ast import literal_eval
features = ['cast', 'crew', 'keywords', 'genres']
for feature in features:
metadata[feature] = metadata[feature].apply(literal_eval)
Selanjutnya, Anda menulis fungsi yang akan membantu mengekstrak informasi yang diperlukan dari setiap fitur.
Pertama, Anda akan mengimpor paket NumPy untuk mendapatkan akses ke konstanta NaN. Selanjutnya, Anda dapat menggunakannya untuk menulis fungsi get_director():
# Import Numpy
import numpy as np
Dapatkan nama sutradara dari fitur crew. Jika sutradara tidak tercantum, kembalikan NaN
def get_director(x):
for i in x:
if i['job'] == 'Director':
return i['name']
return np.nan
Selanjutnya, Anda akan menulis fungsi yang mengembalikan 3 elemen teratas atau seluruh daftar, mana pun yang lebih sedikit. Di sini daftar merujuk pada cast, keywords, dan genres.
def get_list(x):
if isinstance(x, list):
names = [i['name'] for i in x]
#Check if more than 3 elements exist. If yes, return only first three. If no, return entire list.
if len(names) > 3:
names = names[:3]
return names
#Return empty list in case of missing/malformed data
return []
# Define new director, cast, genres and keywords features that are in a suitable form.
metadata['director'] = metadata['crew'].apply(get_director)
features = ['cast', 'keywords', 'genres']
for feature in features:
metadata[feature] = metadata[feature].apply(get_list)
# Print the new features of the first 3 films
metadata[['title', 'cast', 'director', 'keywords', 'genres']].head(3)
| title | cast | director | keywords | genres | |
|---|---|---|---|---|---|
| 0 | Toy Story | [Tom Hanks, Tim Allen, Don Rickles] | John Lasseter | [jealousy, toy, boy] | [Animation, Comedy, Family] |
| 1 | Jumanji | [Robin Williams, Jonathan Hyde, Kirsten Dunst] | Joe Johnston | [board game, disappearance, based on children'... | [Adventure, Fantasy, Family] |
| 2 | Grumpier Old Men | [Walter Matthau, Jack Lemmon, Ann-Margret] | Howard Deutch | [fishing, best friend, duringcreditsstinger] | [Romance, Comedy] |
Langkah berikutnya adalah mengonversi nama dan instance kata kunci menjadi huruf kecil dan menghapus semua spasi di antaranya.
Menghapus spasi antar kata merupakan langkah prapemrosesan yang penting. Ini dilakukan agar vektorisasi Anda tidak menghitung Johnny dari "Johnny Depp" dan "Johnny Galecki" sebagai hal yang sama. Setelah langkah pemrosesan ini, aktor yang disebutkan akan direpresentasikan sebagai "johnnydepp" dan "johnnygalecki" dan akan berbeda bagi vektorisasi Anda.
Contoh baik lainnya di mana model mungkin menghasilkan representasi vektor yang sama adalah "bread jam" dan "traffic jam". Karena itu, lebih baik menghapus spasi apa pun yang ada.
Fungsi di bawah ini akan melakukan hal tersebut untuk Anda:
# Function to convert all strings to lower case and strip names of spaces
def clean_data(x):
if isinstance(x, list):
return [str.lower(i.replace(" ", "")) for i in x]
else:
#Check if director exists. If not, return empty string
if isinstance(x, str):
return str.lower(x.replace(" ", ""))
else:
return ''
# Apply clean_data function to your features.
features = ['cast', 'keywords', 'director', 'genres']
for feature in features:
metadata[feature] = metadata[feature].apply(clean_data)
Sekarang Anda berada dalam posisi untuk membuat "metadata soup" Anda, yaitu string yang berisi semua metadata yang ingin Anda masukkan ke vektorisasi (yakni aktor, sutradara, dan kata kunci).
Fungsi create_soup akan cukup menggabungkan semua kolom yang diperlukan dengan spasi. Ini adalah langkah prapemrosesan terakhir, dan keluaran fungsi ini akan dimasukkan ke model vektor kata.
def create_soup(x):
return ' '.join(x['keywords']) + ' ' + ' '.join(x['cast']) + ' ' + x['director'] + ' ' + ' '.join(x['genres'])
# Create a new soup feature
metadata['soup'] = metadata.apply(create_soup, axis=1)
metadata[['soup']].head(2)
| soup | |
|---|---|
| 0 | jealousy toy boy tomhanks timallen donrickles ... |
| 1 | boardgame disappearance basedonchildren'sbook ... |
Langkah-langkah berikut sama seperti yang Anda lakukan pada rekomender berbasis deskripsi plot Anda. Satu perbedaan utama adalah Anda menggunakan CountVectorizer() alih-alih TF-IDF. Ini karena Anda tidak ingin menurunkan bobot kehadiran aktor/sutradara jika ia telah berakting atau menyutradarai di relatif lebih banyak film. Tidak terlalu masuk akal untuk menurunkan bobot mereka dalam konteks ini.
Perbedaan utama antara CountVectorizer() dan TF-IDF adalah komponen inverse document frequency (IDF) yang ada pada yang terakhir dan tidak pada yang pertama.
# Import CountVectorizer and create the count matrix
from sklearn.feature_extraction.text import CountVectorizer
count = CountVectorizer(stop_words='english')
count_matrix = count.fit_transform(metadata['soup'])
count_matrix.shape
(46628, 73881)
Dari keluaran di atas, Anda dapat melihat bahwa terdapat 73.881 kosakata dalam metadata yang Anda masukkan.
Selanjutnya, Anda akan menggunakan cosine_similarity untuk mengukur jarak antar embedding.
# Compute the Cosine Similarity matrix based on the count_matrix
from sklearn.metrics.pairwise import cosine_similarity
cosine_sim2 = cosine_similarity(count_matrix, count_matrix)
# Reset index of your main DataFrame and construct reverse mapping as before
metadata = metadata.reset_index()
indices = pd.Series(metadata.index, index=metadata['title'])
Sekarang Anda dapat menggunakan kembali fungsi get_recommendations() dengan meneruskan matriks baru cosine_sim2 sebagai argumen kedua Anda.
get_recommendations('The Dark Knight Rises', cosine_sim2)
12589 The Dark Knight
10210 Batman Begins
9311 Shiner
9874 Amongst Friends
7772 Mitchell
516 Romeo Is Bleeding
11463 The Prestige
24090 Quicksand
25038 Deadfall
41063 Sara
Name: title, dtype: object
get_recommendations('The Godfather', cosine_sim2)
1934 The Godfather: Part III
1199 The Godfather: Part II
15609 The Rain People
18940 Last Exit
34488 Rege
35802 Manuscripts Don't Burn
35803 Manuscripts Don't Burn
8001 The Night of the Following Day
18261 The Son of No One
28683 In the Name of the Law
Name: title, dtype: object
Hebat! Anda melihat bahwa rekomender Anda berhasil menangkap lebih banyak informasi berkat metadata yang lebih kaya dan memberikan rekomendasi yang lebih baik. Tentu, ada banyak cara untuk bereksperimen dengan sistem ini guna meningkatkan rekomendasi.
Beberapa saran:
-
Perkenalkan filter popularitas: rekomender ini akan mengambil 30 film paling mirip, menghitung rating berbobot (menggunakan rumus IMDB di atas), mengurutkan film berdasarkan rating ini, dan mengembalikan 10 film teratas.
-
Anggota kru lain: nama anggota kru lain, seperti penulis skenario dan produser, juga dapat disertakan.
-
Menambah bobot sutradara: untuk memberikan bobot lebih pada sutradara, ia dapat disebutkan beberapa kali dalam soup untuk meningkatkan skor kemiripan film dengan sutradara yang sama.
Collaborative Filtering dengan Python
Dalam tutorial ini, Anda telah mempelajari cara membangun Sistem Rekomendasi Film Sederhana dan Berbasis Konten Anda sendiri. Ada juga tipe rekomender lain yang sangat populer yang dikenal sebagai collaborative filtering.
Collaborative filtering selanjutnya dapat diklasifikasikan menjadi dua tipe:
User-based Filtering: sistem ini merekomendasikan produk kepada seorang pengguna yang disukai oleh pengguna serupa. Misalnya, anggaplah Alice dan Bob memiliki minat serupa terhadap buku (yakni, mereka sebagian besar menyukai dan tidak menyukai buku yang sama). Sekarang, katakanlah ada buku baru diluncurkan ke pasar, dan Alice telah membaca dan menyukainya. Maka sangat mungkin Bob juga akan menyukainya, dan karenanya, sistem merekomendasikan buku ini kepada Bob.
Item-based Filtering: sistem ini sangat mirip dengan mesin rekomendasi berbasis konten yang Anda bangun. Sistem ini mengidentifikasi item serupa berdasarkan bagaimana orang-orang menilainya di masa lalu. Misalnya, jika Alice, Bob, dan Eve memberikan 5 bintang untuk The Lord of the Rings dan The Hobbit, sistem mengidentifikasi item tersebut sebagai serupa. Oleh karena itu, jika seseorang membeli The Lord of the Rings, sistem juga merekomendasikan The Hobbit kepadanya.
Contoh collaborative filtering berdasarkan sistem rating:

Anda tidak akan membangun sistem ini dalam tutorial ini, tetapi Anda sudah familiar dengan sebagian besar ide yang diperlukan untuk melakukannya. Tempat yang baik untuk memulai collaborative filtering adalah dengan menelaah dataset MovieLens, yang dapat ditemukan di sini.
Kesimpulan
Selamat telah menyelesaikan tutorial ini!
Anda telah berhasil melalui tutorial kami yang mengajarkan semua tentang sistem rekomendasi di Python. Anda mempelajari cara membangun rekomender sederhana dan berbasis konten.
Latihan yang baik bagi Anda semua adalah mengimplementasikan collaborative filtering di Python menggunakan subset dataset MovieLens yang Anda gunakan untuk membangun rekomender sederhana dan berbasis konten.
Jika Anda baru memulai Python dan ingin belajar lebih jauh, ikuti kursus DataCamp Introduction to Data Science in Python.
