Kurs
Bu eğitimin amacı sizi öneri sistemi modelleri kurma konusunda uzman yapmak değildir. Bunun yerine, mevcut öneri sistemi türlerine genel bir bakış sunarak ve kendi sisteminizi nasıl kurabileceğinizi göstererek sizi yola çıkarmaktır.
Bu eğitimde, basit ve içerik tabanlı öneri sistemlerinin temel bir modelini nasıl kuracağınızı öğreneceksiniz. Bu modeller, karmaşıklık, kalite veya doğruluk açısından sektör standartlarına yaklaşmasa da, daha iyi sonuçlar üreten daha karmaşık modellere başlamak için size yardımcı olacaktır.
Öneri sistemleri bugün veri biliminin en popüler uygulamaları arasındadır. Bir kullanıcının bir öğeye vereceği "puanı" veya "tercihi" tahmin etmek için kullanılırlar. Hemen her büyük teknoloji şirketi bunları bir şekilde uygular. Amazon, müşterilere ürün önermek için; YouTube, otomatik oynatmada sıradaki videoyu seçmek için; Facebook ise beğenilecek sayfalar ve takip edilecek kişileri önermek için kullanır.
Dahası, Netflix, Amazon Prime, Hulu ve Hotstar gibi bazı şirketlerde iş modeli ve başarısı, önerilerinin gücüne dayanır. Netflix, 2009 yılında sistemini %10 iyileştirebilecek herkese bir milyon dolar ödül bile teklif etmişti.
Restoranlar, filmler ve çevrimiçi flört gibi alanlarda da popüler öneri sistemleri vardır. Araştırma makalelerini, uzmanları, işbirlikçileri ve finansal hizmetleri keşfetmek için de öneri sistemleri geliştirilmiştir. YouTube, geçmişinize göre size videolar önermek için öneri sistemini büyük ölçekte kullanır. Örneğin, çok sayıda eğitim videosu izliyorsanız, benzer türde videolar önerecektir.
Peki bu öneri sistemleri nedir?
Genel olarak, öneri sistemleri 3 türe ayrılabilir:
- Basit önericiler: film popülerliği ve/veya türüne göre her kullanıcıya genelleştirilmiş öneriler sunar. Bu sistemin temel fikri, daha popüler ve eleştirmenlerce beğenilen filmlerin, ortalama izleyici tarafından beğenilme olasılığının daha yüksek olmasıdır. Örneğin IMDB Top 250 verilebilir.
- İçerik tabanlı önericiler: belirli bir öğeye benzer öğeler önerir. Bu sistem, filmler için tür, yönetmen, açıklama, oyuncular vb. gibi öğe metaverilerini bu önerileri yapmak için kullanır. Bu tür sistemlerin genel fikri, bir kişi belirli bir öğeyi seviyorsa, ona benzer bir öğeyi de seveceğidir. Bunu önermek için kullanıcının geçmiş öğe metaverilerinden yararlanır. İyi bir örnek, geçmişinize göre potansiyel olarak izleyebileceğiniz yeni videolar öneren YouTube'dur.
- İşbirlikçi filtreleme motorları: yaygın olarak kullanılırlar ve diğer kullanıcıların geçmiş puanları ve tercihleri temelinde, bir kullanıcının bir öğeye vereceği puanı veya tercihi tahmin etmeye çalışırlar. İşbirlikçi filtreler, içerik tabanlı muadillerinin aksine öğe metaverilerine ihtiyaç duymaz.
Basit Önericiler
Önceki bölümde açıklandığı gibi, basit önericiler belirli bir metrik veya skora göre en iyi öğeleri öneren temel sistemlerdir. Bu bölümde, IMDB Top 250 Filmler listesinin, IMDB'den toplanan metaverileri kullanarak basitleştirilmiş bir klonunu oluşturacaksınız.
Aşağıdaki adımlar izlenecektir:
-
Filmleri derecelendirmek için kullanılacak metrik veya skora karar verin.
-
Her film için skoru hesaplayın.
-
Filmleri skora göre sıralayın ve en iyi sonuçları çıkartın.
Veri Kümesi Hakkında
Veri kümesi dosyaları, Full MovieLens Dataset içinde listelenen 45.000 filmin metaverilerini içerir. Veri kümesi Temmuz 2017 veya öncesinde yayımlanan filmlerden oluşur. Bu veri kümesi; oyuncu kadrosu, ekip, konu anahtar sözcükleri, bütçe, gelir, posterler, gösterim tarihleri, diller, yapım şirketleri, ülkeler, TMDB oy sayıları ve oy ortalamaları gibi özellikleri içerir.
Bu özellikler, içerik ve işbirlikçi filtreleme için makine öğrenmesi modellerinizi eğitmekte potansiyel olarak kullanılabilir.
Bu veri kümesi aşağıdaki dosyalardan oluşur:
- movies_metadata.csv: Full MovieLens veri kümesinde yer alan ~45.000 film hakkında bilgi içerir. Özellikler arasında posterler, arka plan görselleri, bütçe, tür, gelir, gösterim tarihleri, diller, yapım ülkeleri ve şirketleri bulunur.
- keywords.csv: MovieLens filmlerimiz için konu anahtar sözcüklerini içerir. Dizeleştirilmiş bir JSON Nesnesi biçiminde mevcuttur.
- credits.csv: Tüm filmler için Oyuncu Kadrosu ve Ekip Bilgilerinden oluşur. Dizeleştirilmiş bir JSON Nesnesi biçiminde mevcuttur.
- links.csv: Full MovieLens veri kümesinde yer alan tüm filmlerin TMDB ve IMDB kimliklerini içerir.
- links_small.csv: Tam Veri Kümesi'nin 9.000 filmlik küçük bir alt kümesinin TMDB ve IMDB kimliklerini içerir.
- ratings_small.csv: 9.000 film üzerinde 700 kullanıcıdan 100.000 oy içeren alt küme.
Full MovieLens Veri Kümesi, bu veri kümesindeki 45.000 film için 270.000 kullanıcıdan 26 milyon oy ve 750.000 etiket uygulaması içerir. Resmi GroupLens web sitesinden erişilebilir.
Not: Bugünkü eğitimde kullanılan alt veri kümesini buradan indirebilirsiniz.
Veri kümenizi yüklemek için pandas DataFrame kütüphanesini kullanacaksınız. pandas kütüphanesi esas olarak veri işleme ve analiz için kullanılır. Verinizi satır-sütun formatında temsil eder. Pandas kütüphanesi, pandas veri nesnelerinin uygulanmasında NumPy dizisiyle desteklenir. pandas, sayısal tablolar, zaman serileri, görüntüler ve doğal dil işleme veri kümelerini manipüle etmek için hazır veri yapıları ve işlemler sunar. Özetle, pandas tablo halinde kolayca temsil edilebilen veri kümeleri için kullanışlıdır.
Yukarıdaki adımlardan herhangi birini gerçekleştirmeden önce, film metaveri veri kümenizi bir pandas DataFrame'ine yükleyelim:
# 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 satır × 24 sütun
Düşünebileceğiniz en temel metriklerden biri, ilk 250 filmin kendi puanlarına göre hangileri olduğuna karar vermek için sıralamadır.
Ancak, metriğin puan olarak kullanılması bazı sakıncalar içerir:
-
Öncelikle, bir filmin popülerliğini dikkate almaz. Dolayısıyla, 10 kişiden 9 puan alan bir film, 10.000 kişiden 8,9 puan alan bir filmden 'daha iyi' kabul edilecektir.
Örneğin, Çin yemeği sipariş etmek istediğinizi ve birkaç seçeneğiniz olduğunu hayal edin; bir restoran sadece 5 kişi tarafından verilen 5 yıldızlı bir puana sahipken, diğer restoran 1000 kişi tarafından 4,5 puan almış. Hangisini tercih edersiniz? İkincisini, değil mi?
Elbette, birinci restoranın sadece birkaç gün önce açılmış olması gibi bir istisna olabilir; bu nedenle daha az kişi oy vermiştir; buna karşın ikinci restoran bir yıldır faaliyettedir.
- Benzer şekilde, bu metrik, oy sayısı az olan ve çarpık ve/veya aşırı yüksek puanlara sahip filmleri de kayırma eğiliminde olacaktır. Oy sayısı arttıkça, bir filmin puanı düzenlenir ve filmin kalitesini daha iyi yansıtan bir değere yaklaşır; bu da kullanıcının hangi filmi seçmesi gerektiği konusunda çok daha iyi bir fikir verir. Oy veren sayısının aşırı az olduğu filmlerde kaliteyi ayırt etmek zor olduğundan, sonuca varmak için harici kaynakları dikkate almanız gerekebilir.
Bu eksiklikleri göz önünde bulundurarak, ortalama puanı ve biriktirdiği oy sayısını hesaba katan ağırlıklı bir puanlama bulmalısınız. Böyle bir sistem, 100.000 kişiden 9 puan alan bir filmin, aynı puana sahip ancak sadece birkaç yüz oyu olan bir filmden (çok) daha yüksek bir skor almasını sağlar.
IMDB Top 250'nin bir klonunu oluşturmaya çalıştığınıza göre, metrik/skor olarak onun ağırlıklı puan formülünü kullanalım. Matematiksel olarak şu şekilde gösterilir:
\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}
Yukarıdaki denklemde,
-
v, filmin aldığı oy sayısıdır;
-
m, listeye girebilmek için gereken asgari oy sayısıdır;
-
R, filmin ortalama puanıdır;
-
C, tüm rapor genelindeki ortalama oydur.
Veri kümesindeki her film için v (vote_count) ve R (vote_average) değerlerine zaten sahipsiniz. C'yi de doğrudan bu veriden hesaplamak mümkündür.
m için uygun bir değer belirlemek, doğru tek bir değerin olmadığı bir hiperparametredir ve buna göre seçebilirsiniz. Bunu, oy sayısı belirli bir eşik m'in altında olan filmleri basitçe eleyen ön bir negatif filtre olarak düşünebilirsiniz. Filtrenizin seçiciliği sizin takdirinize kalmıştır.
Bu eğitimde, kesim değeri m olarak 90. yüzdelik dilimi kullanacaksınız. Başka bir deyişle, bir filmin listeye girebilmesi için listedeki filmlerin en az %90'ından daha fazla oya sahip olması gerekir. (Öte yandan, 75. yüzdelik dilimi seçmiş olsaydınız, oy sayısı bakımından en üst %25'lik dilimdeki filmleri dikkate almış olurdunuz. Yüzdelik dilimi azaldıkça dikkate alınan film sayısı artacaktır).
İlk adım olarak, pandas'ın .mean() işlevini kullanarak tüm filmler arasındaki ortalama puan C değerini hesaplayalım:
# Calculate mean of vote average column
C = metadata['vote_average'].mean()
print(C)
5.618207215133889
Yukarıdaki çıktıya göre, IMDB'de bir filmin ortalama puanının 10 üzerinden yaklaşık 5,6 olduğunu gözlemleyebilirsiniz.
Sonraki adımda, 90. yüzdelik dilimde bir filmin aldığı oy sayısı m'yi hesaplayalım. pandas kütüphanesi, pandas'ın .quantile() yöntemi sayesinde bu işi son derece kolaylaştırır:
# Calculate the minimum number of votes required to be in the chart, m
m = metadata['vote_count'].quantile(0.90)
print(m)
160.0
Artık m değerine sahip olduğunuza göre, 160 veya daha fazla oy almış filmleri filtrelemek için büyük eşittir koşulunu basitçe kullanabilirsiniz:
Yeni oluşturulan q_movies DataFrame'inin özgün metadata DataFrame'inden bağımsız olmasını sağlamak için .copy() yöntemini kullanabilirsiniz. Başka bir deyişle, q_movies DataFrame'inde yapılan değişiklikler özgün metadata veri çerçevesini etkilemeyecektir.
# 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)
Yukarıdaki çıktıya göre, oy sayısı 160'tan fazla olan ve bu listeye girmeye hak kazanan filmlerin yaklaşık %10 olduğu açıktır.
Sonraki ve en önemli adım, her nitelikli film için ağırlıklı puanı hesaplamaktır. Bunu yapmak için şunları yapacaksınız:
weighted_rating()adlı bir işlev tanımlayın;mveCdeğerlerini zaten hesapladığınız için bunları işlevin argümanı olarak geçirin;q_moviesveri çerçevesindenvote_count(v) vevote_average(R) sütunlarını seçin;- Son olarak, ağırlıklı ortalamayı hesaplayıp sonucu döndürün.
Bu işlevi nitelikli filmler DataFrame'inize uygulayarak değerini hesaplayacağınız yeni bir score özelliği tanımlayacaksınız:
# 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)
Son olarak, DataFrame'i score özellik sütununa göre azalan sırada sıralayalım ve ilk 20 filmin başlığını, oy sayısını, oy ortalamasını ve ağırlıklı puanını (score) çıktılayalım.
#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 |
Görüldüğü gibi, basit önerici gayet iyi iş çıkardı!
Listede IMDB Top 250 ile birçok ortak film var: örneğin, ilk iki filminiz "Shawshank Redemption" ve "The Godfather" IMDB ile aynı ve hepimizin bildiği gibi gerçekten harika filmler; hatta ilk 20 filmin hepsi bu listeyi hak ediyor, öyle değil mi?
İçerik Tabanlı Önerici
Konu Açıklamasına Dayalı Önerici
Bu bölümde, belirli bir filme benzer filmleri öneren bir sistemin nasıl kurulacağını öğreneceksiniz. Bunu başarmak için, tüm filmler için konu açıklamalarına dayalı çiftler arası kosinüs benzerlik skorlarını hesaplayacak ve bu benzerlik skoru eşiğine göre filmleri önereceksiniz.
Konu açıklaması, metadata veri kümenizde overview özelliği olarak mevcuttur. Birkaç filmin konularını inceleyelim:
#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
Elimizdeki problem bir Doğal Dil İşleme problemidir. Bu nedenle, benzerliği ve/veya farklılığı hesaplamadan önce yukarıdaki metin verilerinden bir tür özellik çıkarmanız gerekir. Basitçe söylemek gerekirse, ham hâlleriyle iki özet arasındaki benzerliği hesaplamak mümkün değildir. Bunu yapmak için, her özetin veya artık belge diyeceğimiz şeyin sözcük vektörlerini hesaplamanız gerekir.
Adından da anlaşılacağı gibi, sözcük vektörleri bir belgedeki sözcüklerin vektörleştirilmiş temsilleridir. Vektörler anlamsal bir anlam taşır. Örneğin, man & king birbirine yakın vektör temsillerine sahipken, man & woman birbirinden uzak temsillere sahip olabilir.
Her belge için Terim Frekansı-Ters Belge Frekansı (TF-IDF) vektörlerini hesaplayacaksınız. Bu, her sütunun özet söz varlığındaki (en az bir belgede geçen tüm sözcükler) bir kelimeyi, her satırın ise yine bir filmi temsil ettiği bir matris verecektir.
Özünde, TF-IDF skoru bir kelimenin bir belgede geçme sıklığının, geçtiği belge sayısı ile aşağı çekilmiş halidir. Bu, konu özetlerinde sıkça geçen kelimelerin önemini azaltmak ve dolayısıyla nihai benzerlik skorunun hesaplanmasındaki ağırlıklarını düşürmek için yapılır.
Neyse ki scikit-learn, birkaç satırda TF-IDF matrisini üreten yerleşik bir TfIdfVectorizer sınıfı sağlar.
- scikit-learn kullanarak Tfidf modülünü içe aktarın;
- 'the', 'an' gibi durak sözcükleri kaldırın; çünkü konu hakkında faydalı bilgi vermezler;
- sayı değil (NaN) değerleri boş bir dizeyle değiştirin;
- Son olarak veri üzerinde TF-IDF matrisini oluşturun.
#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']
Yukarıdaki çıktıya göre, 45.000 filme karşılık veri kümenizde 75.827 farklı söz varlığı ya da kelime bulunuyor.
Bu matris elinizdeyken artık bir benzerlik skoru hesaplayabilirsiniz. Bunun için manhattan, öklidyen, Pearson ve kosinüs benzerliği gibi çeşitli benzerlik metrikleri kullanabilirsiniz. Yine, hangi skorun en iyi olduğuna dair tek bir doğru cevap yoktur. Farklı metrikler farklı senaryolarda iyi çalışır ve genellikle farklı metriklerle denemeler yapıp sonuçları gözlemlemek iyi bir fikirdir.
İki film arasındaki benzerliği gösteren sayısal bir büyüklüğü hesaplamak için kosinüs benzerliğini kullanacaksınız. Kosinüs benzerliği, büyüklükten bağımsız olduğu ve (özellikle TF-IDF skorları ile birlikte kullanıldığında—buna daha sonra değinilecektir) nispeten kolay ve hızlı hesaplandığı için tercih edilir. Matematiksel olarak şu şekilde tanımlanır:

TF-IDF vektörleştiriciyi kullandığınız için, her vektör arasındaki nokta çarpımını hesaplamak doğrudan size kosinüs benzerliği skorunu verecektir. Bu nedenle, daha hızlı olduğu için cosine_similarities() yerine sklearn's linear_kernel() kullanacaksınız.
Bu, 45466x45466 boyutunda bir matris döndürecektir; yani her filmin overview'unun her diğer filmin overview'u ile kosinüs benzerliği skoru. Dolayısıyla, her film 1x45466'lık bir sütun vektörü olacak ve her sütun her bir filmle olan benzerlik skorunu temsil edecektir.
# 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])
Girdi olarak bir film adı alıp çıktıda en benzer 10 filmin listesini döndüren bir işlev tanımlayacaksınız. Bunun için öncelikle film adlarının ve DataFrame indekslerinin tersine bir eşlemesine ihtiyacınız var. Başka bir deyişle, başlığı verilen bir filmin, metadata DataFrame'inizdeki indeksini bulmaya yarayan bir mekanizmaya ihtiyacınız var.
#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
Artık öneri işlevinizi tanımlamaya hazırsınız. Aşağıdaki adımları izleyeceksiniz:
-
Başlığı verilen filmin indeksini alın.
-
Bu film için tüm filmlerle olan kosinüs benzerlik skorlarının listesini alın. İlk öğesi konumu, ikincisi benzerlik skoru olacak şekilde bunu ikililerden oluşan bir listeye dönüştürün.
-
Bahsi geçen ikililer listesini benzerlik skorlarına, yani ikinci öğeye göre sıralayın.
-
Bu listenin ilk 10 öğesini alın. İlk öğeyi yok sayın çünkü kendine referans verir (bir filme en çok benzeyen film, yine kendisidir).
-
İlk öğelerin indekslerine karşılık gelen başlıkları döndürün.
# 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
Görüyorsunuz ki, sisteminiz benzer konu açıklamalarına sahip filmleri bulmada fena iş çıkarmamış olsa da, önerilerin kalitesi çok iyi değil. "The Dark Knight Rises" tüm Batman filmlerini döndürürken, aslında o filmi beğenenlerin diğer Christopher Nolan filmlerinden de hoşlanma eğiliminde olması daha olasıdır. Bu, mevcut sisteminizin yakalayamadığı bir durumdur.
Oyuncu, Tür ve Anahtar Sözcük Tabanlı Önerici
Önericinizin kalitesi, daha iyi metaveriler kullanılarak ve ince ayrıntıların daha fazlası yakalanarak artırılabilir. Bu bölümde tam olarak bunu yapacaksınız. Aşağıdaki metaverilere dayalı bir öneri sistemi kuracaksınız: en 3 önemli oyuncu, yönetmen, ilgili türler ve filmin konu anahtar sözcükleri.
Anahtar sözcükler, oyuncu kadrosu ve ekip verileri mevcut veri kümenizde yok; bu nedenle ilk adım, bunları ana DataFrame'iniz metadata ile yükleyip birleştirmek olacaktır.
# 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 satır × 27 sütun
Yeni özellikleriniz olan oyuncu kadrosu, ekip ve anahtar sözcüklerden, en önemli üç oyuncuyu, yönetmeni ve o filmle ilişkili anahtar sözcükleri çıkarmanız gerekir.
Ancak önce, verileriniz "dizeleştirilmiş" listeler biçimindedir. Bunları kullanılabilir bir hâle dönüştürmeniz gerekir.
# 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)
Sonraki adımda, her bir özellikten gerekli bilgileri çıkarmanıza yardımcı olacak işlevler yazacaksınız.
Önce, NaN sabitine erişmek için NumPy paketini içe aktaracaksınız. Ardından, bunu get_director() işlevini yazmak için kullanabilirsiniz:
# Import Numpy
import numpy as np
Ekip özelliğinden yönetmenin adını alın. Yönetmen listelenmemişse NaN döndürün
def get_director(x):
for i in x:
if i['job'] == 'Director':
return i['name']
return np.nan
Sonraki adımda, ilk 3 öğeyi veya daha azsa tüm listeyi döndürecek bir işlev yazacaksınız. Burada liste, cast, keywords ve genresu ifade eder.
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] |
Sonraki adım, adları ve anahtar sözcük örneklerini küçük harfe dönüştürmek ve aralardaki boşlukları temizlemektir.
Kelimeler arasındaki boşlukları kaldırmak önemli bir ön işleme adımıdır. Vektörleştiricinizin, "Johnny Depp" ve "Johnny Galecki" isimlerindeki Johnny'yi aynı saymaması için yapılır. Bu işlem adımından sonra, adı geçen oyuncular "johnnydepp" ve "johnnygalecki" olarak temsil edilecek ve vektörleştiriciniz için birbirinden farklı olacaktır.
Modelin aynı vektör temsili üretebileceği bir başka iyi örnek de "bread jam" ve "traffic jam"dir. Bu nedenle, mevcut boşlukları temizlemek daha iyi olacaktır.
Aşağıdaki işlev tam olarak bunu yapacaktır:
# 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)
Artık vektörleştiricinize vermek istediğiniz tüm metaverileri (yani oyuncular, yönetmen ve anahtar sözcükler) içeren "metadata çorbanızı" oluşturma konumundasınız.
create_soup işlevi, gereken tüm sütunları bir boşlukla birleştirecek. Bu, son ön işleme adımıdır ve bu işlevin çıktısı sözcük vektörü modeline verilecektir.
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 ... |
Sonraki adımlar, konu açıklamasına dayalı önericinize yaptıklarınızla aynıdır. Önemli bir fark, TF-IDF yerine CountVectorizer() kullanmanızdır. Bunun nedeni, bir oyuncu/yönetmenin nispeten daha fazla sayıda filmde rol almış veya yönetmiş olması durumunda varlığını aşağı ağırlıklandırmak istememenizdir. Bu bağlamda onları aşağı ağırlıklandırmak sezgisel olarak çok mantıklı değildir.
CountVectorizer() ile TF-IDF arasındaki temel fark, ikincide bulunan ama birincide olmayan ters belge sıklığı (IDF) bileşenidir.
# 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)
Yukarıdaki çıktıdan, beslediğiniz metaveride 73.881 söz varlığı olduğunu görebilirsiniz.
Sonraki adımda, gömüler arasındaki mesafeyi ölçmek için cosine_similarity kullanacaksınız.
# 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'])
Şimdi, yeni cosine_sim2 matrisini ikinci argüman olarak geçirerek get_recommendations() işlevinizi yeniden kullanabilirsiniz.
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
Harika! Daha fazla metaveri sayesinde önericinizin daha fazla bilgiyi yakaladığını ve daha iyi öneriler verdiğini görüyorsunuz. Elbette, bu sistemi geliştirmek için deneyebileceğiniz pek çok yol var.
Bazı öneriler:
-
Popülerlik filtresi ekleyin: Bu önerici, en benzer 30 filmi alır, (yukarıdaki IMDB formülünü kullanarak) ağırlıklı puanlarını hesaplar, filmleri bu puana göre sıralar ve en iyi 10 filmi döndürür.
-
Diğer ekip üyeleri: senarist ve yapımcı gibi diğer ekip üyesi adları da eklenebilir.
-
Yönetmenin ağırlığını artırma: yönetmene daha fazla ağırlık vermek için, benzer yönetmene sahip filmlerin benzerlik skorlarını artırmak amacıyla çorba içinde yönetmeni birden fazla kez anabilirsiniz.
Python ile İşbirlikçi Filtreleme
Bu eğitimde, kendi Basit ve İçerik Tabanlı Film Öneri Sistemlerinizi nasıl kuracağınızı öğrendiniz. Bir de işbirlikçi filtreler olarak bilinen son derece popüler bir öneri türü vardır.
İşbirlikçi filtreler iki türe ayrılabilir:
Kullanıcı tabanlı Filtreleme: bu sistemler, benzer kullanıcıların beğendiği ürünleri bir kullanıcıya önerir. Örneğin, Alice ve Bob'un kitaplarda benzer zevklere sahip olduğunu (yani büyük ölçüde aynı kitapları sevip sevmediklerini) varsayalım. Şimdi, piyasaya yeni bir kitap sürüldüğünü ve Alice'in bu kitabı okuyup çok beğendiğini düşünelim. Bu durumda Bob'un da beğenme olasılığı yüksektir ve sistem bu kitabı Bob'a önerir.
Öğe tabanlı Filtreleme: bu sistemler, oluşturduğunuz içerik öneri motoruna son derece benzerdir. Bu sistemler, insanların geçmişte verdiği puanlara dayanarak benzer öğeleri belirler. Örneğin, Alice, Bob ve Eve hem The Lord of the Rings hem de The Hobbit için 5 yıldız verdiyse, sistem bu öğeleri benzer olarak tanımlar. Bu nedenle, birisi The Lord of the Rings'i satın alırsa, sistem ona The Hobbit'i de önerir.
Puanlama sistemine dayalı işbirlikçi filtrelemeye bir örnek:

Bu eğitimde bu sistemleri inşa etmeyeceksiniz; ancak bunu yapmak için gereken fikirlerin çoğuna zaten aşinasınız. İşbirlikçi filtrelere başlamak için iyi bir yer, burada bulunan MovieLens veri kümesini incelemektir.
Sonuç
Bu eğitimi tamamladığınız için tebrikler!
Python'da öneri sistemleri hakkında her şeyi anlatan eğitimimizi başarıyla tamamladınız. Basit ve içerik tabanlı önericilerin nasıl kurulacağını öğrendiniz.
Sizler için iyi bir alıştırma, basit ve içerik tabanlı önericileri kurmak için kullandığınız MovieLens veri kümesinin alt kümesini kullanarak Python'da işbirlikçi filtrelemeyi uygulamak olacaktır.
Python'a yeni başlıyorsanız ve daha fazla öğrenmek istiyorsanız, DataCamp'in Python ile Veri Bilimine Giriş kursunu alın.
