मुख्य सामग्री पर जाएँ

Beginner Tutorial: Python में Recommender Systems

Python की मदद से अपना रिकमेंडेशन इंजन बनाएँ—बेसिक मॉडलों से लेकर कंटेंट-बेस्ड और collaborative filtering सिस्टम्स तक।
· 15 मि॰ पढ़ें

AI के साथ खोजें

रिकमेंडर सिस्टम
Source</a

इस ट्यूटोरियल का उद्देश्य आपको रिकमेंडर सिस्टम मॉडल बनाने में विशेषज्ञ बनाना नहीं है। बल्कि, मकसद आपको शुरुआत कराने का है—यह बताकर कि किस प्रकार के रिकमेंडर सिस्टम होते हैं और आप अपना एक सिस्टम कैसे बना सकते हैं।

इस ट्यूटोरियल में, आप सरल और कंटेंट-आधारित रिकमेंडर सिस्टम्स के एक बेसिक मॉडल बनाना सीखेंगे। भले ही ये मॉडल जटिलता, गुणवत्ता, या सटीकता के लिहाज से इंडस्ट्री स्टैंडर्ड के करीब न हों, लेकिन ये आपको बेहतर परिणाम देने वाले अधिक जटिल मॉडलों के निर्माण की दिशा में शुरुआत करने में मदद करेंगे।

आज डेटा साइंस के सबसे लोकप्रिय अनुप्रयोगों में रिकमेंडर सिस्टम शामिल हैं। इनका उपयोग यह “रेटिंग” या “पसंद” का अनुमान लगाने में किया जाता है कि कोई उपयोगकर्ता किसी आइटम को क्या देगा। लगभग हर बड़ी टेक कंपनी किसी न किसी रूप में इन्हें लागू करती है। Amazon ग्राहकों को प्रोडक्ट सुझाने के लिए, YouTube ऑटोप्ले में अगला वीडियो चुनने के लिए, और Facebook पसंद करने के लिए पेज और फॉलो करने के लिए लोगों की सिफारिश करने के लिए इसका उपयोग करता है।

इसके अलावा, Netflix, Amazon Prime, Hulu, और Hotstar जैसी कंपनियों के लिए, उनके बिजनेस मॉडल और उसकी सफलता काफी हद तक उनकी सिफारिशों की प्रभावशीलता पर निर्भर करती है। Netflix ने 2009 में अपने सिस्टम में 10% सुधार करने वाले किसी भी व्यक्ति को एक मिलियन डॉलर का इनाम भी रखा था।

रेस्तरां, फिल्मों और ऑनलाइन डेटिंग जैसे डोमेन के लिए भी लोकप्रिय रिकमेंडर सिस्टम मौजूद हैं। शोध लेख, विशेषज्ञ और सहयोगी खोजने, तथा वित्तीय सेवाओं के लिए भी रिकमेंडर सिस्टम विकसित किए गए हैं। YouTube बड़े पैमाने पर आपकी हिस्ट्री के आधार पर वीडियो सुझाने के लिए रिकमेंडेशन सिस्टम का उपयोग करता है। उदाहरण के लिए, यदि आप बहुत सारे शैक्षिक वीडियो देखते हैं, तो वह उसी प्रकार के वीडियो सुझाएगा।

लेकिन आखिर ये रिकमेंडर सिस्टम होते क्या हैं?

व्यापक रूप से, रिकमेंडर सिस्टम 3 प्रकार के होते हैं:

  • सिंपल रिकमेंडर: मूवी की लोकप्रियता और/या शैली के आधार पर हर उपयोगकर्ता को सामान्य सिफारिशें देते हैं। इस सिस्टम के पीछे मूल विचार यह है कि जो फिल्में अधिक लोकप्रिय और समीक्षकों द्वारा सराही गई हैं, उन्हें औसत दर्शक द्वारा पसंद किए जाने की संभावना अधिक होती है। एक उदाहरण IMDB Top 250 हो सकता है।
  • कंटेंट-आधारित रिकमेंडर: किसी विशेष आइटम के आधार पर उससे मिलते-जुलते आइटम सुझाते हैं। यह सिस्टम सिफारिशें देने के लिए आइटम के मेटाडेटा—जैसे फिल्मों के लिए जेनर, डायरेक्टर, डिस्क्रिप्शन, अभिनेता आदि—का उपयोग करता है। इनके पीछे सामान्य विचार यह है कि यदि किसी व्यक्ति को कोई विशिष्ट आइटम पसंद है, तो उसे उससे मिलता-जुलता आइटम भी पसंद आएगा। और इसे सुझाने के लिए यह उपयोगकर्ता के पिछले आइटम मेटाडेटा का उपयोग करेगा। एक अच्छा उदाहरण YouTube है, जहां आपकी हिस्ट्री के आधार पर वह आपको नए वीडियो सुझाता है जिन्हें आप संभवतः देखेंगे।
  • कोलैबोरेटिव फिल्टरिंग इंजन: ये सिस्टम व्यापक रूप से उपयोग किए जाते हैं और वे अन्य उपयोगकर्ताओं की पिछली रेटिंग्स और पसंद के आधार पर अनुमान लगाने की कोशिश करते हैं कि कोई उपयोगकर्ता किसी आइटम को क्या रेटिंग/पसंद देगा। कोलैबोरेटिव फिल्टर, कंटेंट-आधारित के विपरीत, आइटम मेटाडेटा की आवश्यकता नहीं रखते।

सिंपल रिकमेंडर

जैसा कि पिछले सेक्शन में बताया गया, सिंपल रिकमेंडर ऐसे बुनियादी सिस्टम होते हैं जो किसी निश्चित मीट्रिक या स्कोर के आधार पर टॉप आइटम सुझाते हैं। इस सेक्शन में, आप IMDB Top 250 Movies का एक सरल क्लोन बनाएंगे, जिसके लिए IMDB से एकत्रित मेटाडेटा का उपयोग किया जाएगा।

इसमें शामिल चरण निम्नलिखित हैं:

  • फिल्मों को रेट करने के लिए मीट्रिक या स्कोर तय करें।

  • हर फिल्म के लिए स्कोर की गणना करें।

  • स्कोर के आधार पर फिल्मों को सॉर्ट करें और शीर्ष परिणाम निकालें।

डेटासेट के बारे में

डेटासेट फाइलों में Full MovieLens Dataset में सूचीबद्ध सभी 45,000 फिल्मों का मेटाडेटा शामिल है। डेटासेट में जुलाई 2017 या उससे पहले रिलीज़ हुई फिल्में शामिल हैं। यह डेटासेट कलाकार, क्रू, प्लॉट कीवर्ड, बजट, रेवेन्यू, पोस्टर्स, रिलीज़ डेट, भाषाएं, प्रोडक्शन कंपनियां, देश, TMDB वोट काउंट और वोट एवरेज जैसे फीचर पॉइंट्स को कैप्चर करता है।

इन फीचर पॉइंट्स का उपयोग कंटेंट और कोलैबोरेटिव फिल्टरिंग के लिए आपके मशीन लर्निंग मॉडल को ट्रेन करने में किया जा सकता है।

इस डेटासेट में निम्नलिखित फाइलें शामिल हैं:

  • movies_metadata.csv: इस फाइल में Full MovieLens डेटासेट में शामिल ~45,000 फिल्मों की जानकारी होती है। फीचर्स में पोस्टर्स, बैकड्रॉप्स, बजट, जेनर, रेवेन्यू, रिलीज़ डेट, भाषाएं, प्रोडक्शन देश और कंपनियां शामिल हैं।
  • keywords.csv: हमारी MovieLens फिल्मों के प्लॉट कीवर्ड्स शामिल हैं। स्ट्रिंगिफाइड JSON ऑब्जेक्ट के रूप में उपलब्ध।
  • credits.csv: सभी फिल्मों के कास्ट और क्रू की जानकारी शामिल है। स्ट्रिंगिफाइड JSON ऑब्जेक्ट के रूप में उपलब्ध।
  • links.csv: इस फाइल में Full MovieLens डेटासेट में शामिल सभी फिल्मों के TMDB और IMDB IDs हैं।
  • links_small.csv: फुल डेटासेट की 9,000 फिल्मों के एक छोटे सबसेट के TMDB और IMDB IDs शामिल हैं।
  • ratings_small.csv: 9,000 फिल्मों पर 700 उपयोगकर्ताओं की 100,000 रेटिंग्स का सबसेट।

Full MovieLens Dataset में 26 मिलियन रेटिंग्स और 750,000 टैग एप्लिकेशंस शामिल हैं, जो इस डेटासेट की सभी 45,000 फिल्मों पर 270,000 उपयोगकर्ताओं से आए हैं। इसे आधिकारिक GroupLens वेबसाइट से एक्सेस किया जा सकता है।

नोट: आज के ट्यूटोरियल में उपयोग किया गया सबसेट डेटासेट यहां से डाउनलोड किया जा सकता है।

अपने डेटासेट को लोड करने के लिए, आप pandas DataFrame लाइब्रेरी का उपयोग करेंगे। pandas लाइब्रेरी मुख्यतः डेटा मैनिपुलेशन और विश्लेषण के लिए उपयोग होती है। यह आपके डेटा को रो-कॉलम फॉर्मेट में प्रस्तुत करती है। Pandas लाइब्रेरी के पीछे NumPy array होता है जो pandas डेटा ऑब्जेक्ट्स के इम्प्लीमेंटेशन का आधार है। pandas संख्यात्मक टेबल्स, टाइम-सीरीज़, इमेजरी और प्राकृतिक भाषा प्रोसेसिंग डेटासेट्स को मैनिपुलेट करने के लिए आसान-से-उपयोग डेटा स्ट्रक्चर्स और ऑपरेशंस उपलब्ध करवाती है। मूलतः, pandas उन डेटासेट्स के लिए उपयोगी है जिन्हें आसानी से टेबुलर रूप में दर्शाया जा सकता है।

ऊपर दिए गए किसी भी चरण को करने से पहले, आइए आपके मूवी मेटाडेटा डेटासेट को एक pandas DataFrame में लोड करते हैं:

# 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)
 adultbelongs_to_collectionbudgetgenreshomepageidimdb_idoriginal_languageoriginal_titleoverview...release_daterevenueruntimespoken_languagesstatustaglinetitlevideovote_averagevote_count
0False{'id': 10194, 'name': 'Toy Story Collection', ...30000000[{'id': 16, 'name': 'Animation'}, {'id': 35, '...http://toystory.disney.com/toy-story862tt0114709enToy StoryLed by Woody, Andy's toys live happily in his ......1995-10-30373554033.081.0[{'iso_639_1': 'en', 'name': 'English'}]ReleasedNaNToy StoryFalse7.75415.0
1FalseNaN65000000[{'id': 12, 'name': 'Adventure'}, {'id': 14, '...NaN8844tt0113497enJumanjiWhen siblings Judy and Peter discover an encha......1995-12-15262797249.0104.0[{'iso_639_1': 'en', 'name': 'English'}, {'iso...ReleasedRoll the dice and unleash the excitement!JumanjiFalse6.92413.0
2False{'id': 119050, 'name': 'Grumpy Old Men Collect...0[{'id': 10749, 'name': 'Romance'}, {'id': 35, ...NaN15602tt0113228enGrumpier Old MenA family wedding reignites the ancient feud be......1995-12-220.0101.0[{'iso_639_1': 'en', 'name': 'English'}]ReleasedStill Yelling. Still Fighting. Still Ready for...Grumpier Old MenFalse6.592.0

3 पंक्तियाँ × 24 कॉलम

सबसे बुनियादी मीट्रिक में से एक यह हो सकता है कि रेटिंग के आधार पर टॉप 250 फिल्में तय की जाएं।

हालांकि, मीट्रिक के रूप में रेटिंग का उपयोग करने में कुछ खामियां हैं:

  • पहली बात, यह किसी फिल्म की लोकप्रियता को ध्यान में नहीं रखता। इसलिए, 10 वोटरों से 9 की रेटिंग वाली फिल्म को 10,000 वोटरों से 8.9 की रेटिंग वाली फिल्म से ‘बेहतर’ माना जाएगा।

    उदाहरण के लिए, मान लीजिए आप चीनी खाना मंगवाना चाहते हैं, आपके पास कुछ विकल्प हैं—एक रेस्तरां को केवल 5 लोगों ने 5-स्टार दिए हैं, जबकि दूसरे रेस्तरां को 1000 लोगों ने 4.5 रेटिंग दी है। आप किसे पसंद करेंगे? दूसरे को, है न?

    बेशक, अपवाद भी हो सकता है कि पहला रेस्तरां कुछ ही दिन पहले खुला है; इसलिए कम लोगों ने वोट किए, जबकि दूसरा रेस्तरां एक साल से चल रहा है।

  • इसी से जुड़ा एक मुद्दा यह भी है कि यह मीट्रिक कम वोटरों वाली और तिरछी और/या अत्यधिक ऊंची रेटिंग वाली फिल्मों का पक्ष लेगा। जैसे-जैसे वोटरों की संख्या बढ़ती है, किसी फिल्म की रेटिंग नियमित होती जाती है और एक ऐसे मान के करीब पहुंचती है जो फिल्म की गुणवत्ता को दर्शाती है और उपयोगकर्ता को यह बेहतर अंदाजा देती है कि उसे कौन-सी फिल्म चुननी चाहिए। जिन फिल्मों पर अत्यंत कम वोट हैं, उनकी गुणवत्ता का अंदाजा लगाना मुश्किल होता है, ऐसे में निष्कर्ष निकालने के लिए आपको बाहरी स्रोतों पर विचार करना पड़ सकता है।

इन कमियों को देखते हुए, आपको एक वेटेड रेटिंग निकालनी चाहिए जो औसत रेटिंग और प्राप्त वोटों की संख्या दोनों को ध्यान में रखे। ऐसा सिस्टम यह सुनिश्चित करेगा कि 100,000 वोटरों से 9 की रेटिंग वाली फिल्म को उसी रेटिंग लेकिन केवल कुछ सौ वोटरों वाली फिल्म से (काफी) अधिक स्कोर मिले।

चूंकि आप IMDB के Top 250 का क्लोन बनाना चाह रहे हैं, आइए इसके वेटेड रेटिंग फॉर्मूले का ही मीट्रिक/स्कोर के रूप में उपयोग करें। गणितीय रूप से, इसे इस प्रकार दर्शाया जाता है:

\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}

ऊपर दिए गए समीकरण में,

  • v किसी फिल्म के लिए वोटों की संख्या है;

  • m चार्ट में सूचीबद्ध होने के लिए आवश्यक न्यूनतम वोट हैं;

  • R फिल्म की औसत रेटिंग है;

  • C पूरे रिपोर्ट में औसत वोट है।

आपके पास पहले से ही हर फिल्म के लिए v (vote_count) और R (vote_average) के मान मौजूद हैं। C को भी इन्हीं डेटा से सीधे निकाला जा सकता है।

m का उपयुक्त मान निर्धारित करना एक हाइपरपैरामीटर है जिसे आप अपनी आवश्यकतानुसार चुन सकते हैं, क्योंकि m का कोई एक सही मान नहीं है। आप इसे एक प्रारंभिक नेगेटिव फ़िल्टर के रूप में मान सकते हैं जो केवल उन फिल्मों को हटा देगा जिनके वोट एक निश्चित थ्रेसहोल्ड m से कम हैं। आपके फ़िल्टर की चयनात्मकता आपके विवेक पर निर्भर है।

इस ट्यूटोरियल में, आप कटऑफ m को 90वें परसेंटाइल के रूप में उपयोग करेंगे। दूसरे शब्दों में, किसी फिल्म को चार्ट में शामिल होने के लिए सूची की कम से कम 90% फिल्मों से अधिक वोट होने चाहिए। (इसके विपरीत, यदि आप 75वां परसेंटाइल चुनते, तो आप वोटों की संख्या के लिहाज से शीर्ष 25% फिल्मों पर विचार करते। जैसे-जैसे परसेंटाइल घटेगा, शामिल फिल्मों की संख्या बढ़ेगी)।

पहले चरण के रूप में, आइए सभी फिल्मों में औसत रेटिंग C की गणना pandas के .mean() फंक्शन का उपयोग करके करें:

# Calculate mean of vote average column
C = metadata['vote_average'].mean()
print(C)
5.618207215133889

ऊपर दिए आउटपुट से आप देख सकते हैं कि IMDB पर किसी फिल्म की औसत रेटिंग 10 में से लगभग 5.6 है।

अब, आइए 90वें परसेंटाइल में किसी फिल्म को प्राप्त वोटों की संख्या m की गणना करें। pandas लाइब्रेरी का .quantile() मेथड इस काम को बेहद आसान बना देता है:

# Calculate the minimum number of votes required to be in the chart, m
m = metadata['vote_count'].quantile(0.90)
print(m)
160.0

अब जबकि आपके पास m है, आप बस ग्रेटर-दैन-इक्वल टू कंडीशन का उपयोग करके 160 या उससे अधिक वोट काउंट वाली फिल्मों को फ़िल्टर कर सकते हैं:

आप .copy() मेथड का उपयोग कर सकते हैं ताकि नया q_movies DataFrame आपके मूल metadata DataFrame से स्वतंत्र रहे। यानी q_movies DataFrame में किए गए किसी भी बदलाव का असर मूल metadata DataFrame पर नहीं पड़ेगा।

# 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)

ऊपर दिए आउटपुट से स्पष्ट है कि लगभग 10% फिल्मों के वोट काउंट 160 से अधिक हैं और वे इस सूची में शामिल होने की योग्यता रखती हैं।

अगला और सबसे महत्वपूर्ण कदम है प्रत्येक योग्य फिल्म के लिए वेटेड रेटिंग की गणना करना। इसके लिए, आप ये करेंगे:

  • weighted_rating() नाम का एक फंक्शन परिभाषित करें;
  • चूंकि आपने पहले ही m और C की गणना कर ली है, इन्हें फंक्शन में आर्ग्युमेंट के रूप में पास करें;
  • q_movies डेटा फ्रेम से vote_count (v) और vote_average (R) कॉलम चुनें;
  • अंत में, वेटेड एवरेज की गणना करें और परिणाम रिटर्न करें।

आप एक नया फीचर score परिभाषित करेंगे, जिसका मान आप इस फंक्शन को योग्य फिल्मों के DataFrame पर अप्लाई करके निकालेंगे:

# 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)

अंत में, score फीचर कॉलम के आधार पर DataFrame को अवरोही क्रम में सॉर्ट करते हैं और शीर्ष 20 फिल्मों के शीर्षक, वोट काउंट, वोट एवरेज, और वेटेड रेटिंग (स्कोर) आउटपुट करते हैं।

#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)
 titlevote_countvote_averagescore
314The Shawshank Redemption8358.08.58.445869
834The Godfather6024.08.58.425439
10309Dilwale Dulhania Le Jayenge661.09.18.421453
12481The Dark Knight12269.08.38.265477
2843Fight Club9678.08.38.256385
292Pulp Fiction8670.08.38.251406
522Schindler's List4436.08.38.206639
23673Whiplash4376.08.38.205404
5481Spirited Away3968.08.38.196055
2211Life Is Beautiful3643.08.38.187171
1178The Godfather: Part II3418.08.38.180076
1152One Flew Over the Cuckoo's Nest3001.08.38.164256
351Forrest Gump8147.08.28.150272
1154The Empire Strikes Back5998.08.28.132919
1176Psycho2405.08.38.132715
18465The Intouchables5410.08.28.125837
40251Your Name.1030.08.58.112532
289Leon: The Professional4293.08.28.107234
3030The Green Mile4166.08.28.104511
1170GoodFellas3211.08.28.077459

ऊपर दिए आउटपुट से, आप देख सकते हैं कि सिंपल रिकमेंडर ने बढ़िया काम किया!

क्योंकि इस चार्ट में IMDB Top 250 चार्ट की कई फिल्में समान हैं: उदाहरण के लिए, आपकी शीर्ष दो फिल्में, "Shawshank Redemption" और "The Godfather", IMDB के समान हैं और हम सब जानते हैं कि वे वाकई कमाल की फिल्में हैं। सच कहें तो सभी शीर्ष 20 फिल्में उस सूची में होने की हकदार हैं, है न?

कंटेंट-आधारित रिकमेंडर

प्लॉट डिस्क्रिप्शन आधारित रिकमेंडर

ट्यूटोरियल के इस भाग में, आप ऐसा सिस्टम बनाना सीखेंगे जो किसी विशेष फिल्म से मिलती-जुलती फिल्में सुझाए। इसे हासिल करने के लिए, आप सभी फिल्मों के प्लॉट डिस्क्रिप्शंस के आधार पर पेयरवाइज़ cosine सिमिलैरिटी स्कोर की गणना करेंगे और उसी सिमिलैरिटी स्कोर थ्रेसहोल्ड के आधार पर फिल्में सुझाएंगे।

प्लॉट डिस्क्रिप्शन आपके metadata डेटासेट में overview फीचर के रूप में उपलब्ध है। आइए कुछ फिल्मों के प्लॉट देखते हैं:

#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

यह समस्या प्राकृतिक भाषा प्रोसेसिंग की है। इसलिए सिमिलैरिटी या डिससिमिलैरिटी की गणना करने से पहले आपको ऊपर के टेक्स्ट डेटा से किसी प्रकार के फीचर निकालने होंगे। सरल शब्दों में, कच्चे रूप में किसी भी दो ओवरव्यूज़ के बीच सिमिलैरिटी की गणना करना संभव नहीं है। इसके लिए, आपको हर ओवरव्यू या डॉक्युमेंट—जैसा कि अब इसे कहा जाएगा—के वर्ड वेक्टर्स की गणना करनी होगी।

नाम से स्पष्ट है कि वर्ड वेक्टर्स किसी डॉक्युमेंट में शब्दों का वेक्टराइज़्ड प्रतिनिधित्व होते हैं। वेक्टर अपने साथ अर्थगत जानकारी लिए होते हैं। उदाहरण के लिए, man और king के वेक्टर प्रतिनिधित्व एक-दूसरे के क़रीब होंगे जबकि man और woman के प्रतिनिधित्व एक-दूसरे से दूर होंगे।

आप प्रत्येक डॉक्युमेंट के लिए Term Frequency-Inverse Document Frequency (TF-IDF) वेक्टर की गणना करेंगे। इससे आपको एक मैट्रिक्स मिलेगा जहां प्रत्येक कॉलम ओवरव्यू वोकैब्युलरी के एक शब्द (वह सभी शब्द जो कम से कम एक डॉक्युमेंट में आते हैं) का प्रतिनिधित्व करेगा, और प्रत्येक कॉलम पहले की तरह एक फिल्म का प्रतिनिधित्व करेगा।

मूल रूप से, TF-IDF स्कोर किसी डॉक्युमेंट में किसी शब्द के आने की आवृत्ति है, जिसे उन डॉक्युमेंट्स की संख्या से डाउन-वेट किया जाता है जिनमें वह आता है। यह उन शब्दों के महत्व को कम करने के लिए किया जाता है जो प्लॉट ओवरव्यूज़ में अक्सर आते हैं और इसलिए अंतिम सिमिलैरिटी स्कोर की गणना में उनकी अहमियत कम होनी चाहिए।

सौभाग्य से, scikit-learn आपको एक बिल्ट-इन TfIdfVectorizer क्लास देता है जो कुछ ही लाइनों में TF-IDF मैट्रिक्स बना देता है।

  • scikit-learn से Tfidf मॉड्यूल इम्पोर्ट करें;
  • 'the', 'an' आदि जैसे स्टॉप वर्ड्स हटाएं क्योंकि वे टॉपिक के बारे में कोई उपयोगी जानकारी नहीं देते;
  • not-a-number मानों को खाली स्ट्रिंग से बदलें;
  • अंत में, डेटा पर TF-IDF मैट्रिक्स कंस्ट्रक्ट करें।
#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']

ऊपर के आउटपुट से आप देखते हैं कि 45,000 फिल्मों वाले आपके डेटासेट में 75,827 अलग-अलग शब्द या वोकैब्युलरी हैं।

अब इस मैट्रिक्स के साथ, आप सिमिलैरिटी स्कोर की गणना कर सकते हैं। इसके लिए आप कई सिमिलैरिटी मैट्रिक्स का उपयोग कर सकते हैं, जैसे मैनहैटन, यूक्लिडियन, पियर्सन और कोसाइन सिमिलैरिटी स्कोर। फिर से, कौन-सा स्कोर सबसे अच्छा है इसका कोई एक सही उत्तर नहीं है। अलग-अलग मैट्रिक्स अलग-अलग परिस्थितियों में अच्छा काम करते हैं, और अलग-अलग मैट्रिक्स के साथ प्रयोग करना और परिणामों का अवलोकन करना अक्सर अच्छा विचार होता है।

आप cosine similarity का उपयोग दो फिल्मों के बीच समानता दर्शाने वाली संख्यात्मक राशि निकालने के लिए करेंगे। आप कोसाइन सिमिलैरिटी स्कोर का उपयोग करेंगे क्योंकि यह परिमाण से स्वतंत्र होता है और इसकी गणना अपेक्षाकृत आसान और तेज होती है (खासकर जब TF-IDF स्कोर के साथ उपयोग किया जाए, जैसा कि आगे समझाया जाएगा)। गणितीय रूप से, इसे इस प्रकार परिभाषित किया जाता है:

definition

चूंकि आपने TF-IDF वेक्टराइज़र का उपयोग किया है, इसलिए प्रत्येक वेक्टर के बीच डॉट प्रोडक्ट निकालना सीधे आपको कोसाइन सिमिलैरिटी स्कोर देगा। इसलिए, आप cosine_similarities() के बजाय sklearn's linear_kernel() का उपयोग करेंगे क्योंकि यह तेज है।

यह 45466x45466 आकार का मैट्रिक्स लौटाएगा, यानी प्रत्येक फिल्म के overview की हर अन्य फिल्म के overview के साथ कोसाइन सिमिलैरिटी। इस प्रकार, प्रत्येक फिल्म 1x45466 कॉलम वेक्टर होगी जहां प्रत्येक कॉलम हर फिल्म के साथ सिमिलैरिटी स्कोर होगा।

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

आप एक ऐसा फंक्शन परिभाषित करने जा रहे हैं जो मूवी का शीर्षक इनपुट के रूप में ले और 10 सबसे मिलती-जुलती फिल्मों की सूची आउटपुट करे। सबसे पहले, इसके लिए आपको मूवी टाइटल्स और DataFrame इंडेसेज़ का रिवर्स मैपिंग चाहिए। दूसरे शब्दों में, दिए गए टाइटल के आधार पर अपने metadata DataFrame में उस फिल्म का इंडेक्स पहचानने का एक तरीका चाहिए।

#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

अब आप अपना रिकमेंडेशन फंक्शन परिभाषित करने की स्थिति में हैं। आप निम्नलिखित चरणों का पालन करेंगे:

  • दिए गए शीर्षक के आधार पर फिल्म का इंडेक्स प्राप्त करें।

  • उस फिल्म के लिए सभी फिल्मों के साथ कोसाइन सिमिलैरिटी स्कोर की सूची प्राप्त करें। इसे ट्यूपल्स की सूची में बदलें जहां पहला तत्व उसकी पोजीशन और दूसरा सिमिलैरिटी स्कोर हो।

  • उपरोक्त ट्यूपल्स की सूची को सिमिलैरिटी स्कोर (दूसरे तत्व) के आधार पर सॉर्ट करें।

  • इस सूची के शीर्ष 10 एलिमेंट लें। पहले एलिमेंट को अनदेखा करें क्योंकि वह स्वयं को संदर्भित करता है (किसी फिल्म के लिए सबसे मिलती-जुलती फिल्म वही फिल्म होती है)।

  • शीर्ष एलिमेंट्स के इंडेसेज़ के अनुरूप टाइटल्स रिटर्न करें।

# 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

आप देखते हैं कि, भले ही आपके सिस्टम ने मिलते-जुलते प्लॉट डिस्क्रिप्शन खोजने में ठीक-ठाक काम किया है, लेकिन सिफारिशों की गुणवत्ता उतनी अच्छी नहीं है। "The Dark Knight Rises" सभी Batman फिल्में लौटाता है, जबकि अधिक संभावना यह है कि जिसे यह फिल्म पसंद आई, वह अन्य Christopher Nolan की फिल्में देखना ज्यादा पसंद करेगा। यह ऐसी चीज़ है जिसे आपका मौजूदा सिस्टम कैप्चर नहीं कर सकता।

क्रेडिट्स, जेनर और कीवर्ड्स आधारित रिकमेंडर

बेहतर मेटाडेटा का उपयोग करके और अधिक बारीक विवरण कैप्चर करके आपके रिकमेंडर की गुणवत्ता बढ़ाई जा सकती है। ठीक यही आप इस सेक्शन में करने जा रहे हैं। आप निम्नलिखित मेटाडेटा के आधार पर एक रिकमेंडर सिस्टम बनाएंगे: शीर्ष 3 अभिनेता, निर्देशक, संबंधित जेनर, और फिल्म के प्लॉट कीवर्ड्स।

कीवर्ड्स, कास्ट और क्रू का डेटा आपके मौजूदा डेटासेट में उपलब्ध नहीं है, इसलिए पहला कदम इन्हें लोड करके आपके मुख्य DataFrame 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)
 adultbelongs_to_collectionbudgetgenreshomepageidimdb_idoriginal_languageoriginal_titleoverview...spoken_languagesstatustaglinetitlevideovote_averagevote_countcastcrewkeywords
0False{'id': 10194, 'name': 'Toy Story Collection', ...30000000[{'id': 16, 'name': 'Animation'}, {'id': 35, '...http://toystory.disney.com/toy-story862tt0114709enToy StoryLed by Woody, Andy's toys live happily in his ......[{'iso_639_1': 'en', 'name': 'English'}]ReleasedNaNToy StoryFalse7.75415.0[{'cast_id': 14, 'character': 'Woody (voice)',...[{'credit_id': '52fe4284c3a36847f8024f49', 'de...[{'id': 931, 'name': 'jealousy'}, {'id': 4290,...
1FalseNaN65000000[{'id': 12, 'name': 'Adventure'}, {'id': 14, '...NaN8844tt0113497enJumanjiWhen siblings Judy and Peter discover an encha......[{'iso_639_1': 'en', 'name': 'English'}, {'iso...ReleasedRoll the dice and unleash the excitement!JumanjiFalse6.92413.0[{'cast_id': 1, 'character': 'Alan Parrish', '...[{'credit_id': '52fe44bfc3a36847f80a7cd1', 'de...[{'id': 10090, 'name': 'board game'}, {'id': 1...

2 पंक्तियाँ × 27 कॉलम

नई फीचर्स—कास्ट, क्रू और कीवर्ड्स—में से आपको शीर्ष तीन अभिनेता, निर्देशक और उस फिल्म से जुड़े कीवर्ड्स निकालने होंगे।

लेकिन सबसे पहले, आपका डेटा “स्ट्रिंगिफाइड” लिस्ट्स के रूप में मौजूद है। आपको इन्हें ऐसे रूप में बदलना होगा जो आपके लिए उपयोगी हो।

# 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)

अगला कदम है ऐसी फ़ंक्शंस लिखना जो हर फ़ीचर से आवश्यक जानकारी निकालने में मदद करें।

सबसे पहले, आप NumPy पैकेज इम्पोर्ट करेंगे ताकि उसके NaN कॉन्स्टैंट तक पहुँच मिल सके। फिर, आप इसका उपयोग करके get_director() फ़ंक्शन लिख सकते हैं:

# Import Numpy
import numpy as np

crew फ़ीचर से निर्देशक का नाम प्राप्त करें। यदि निर्देशक लिस्टेड नहीं है, तो NaN लौटाएँ।

def get_director(x):
    for i in x:
        if i['job'] == 'Director':
            return i['name']
    return np.nan

अब, आप एक फ़ंक्शन लिखेंगे जो शीर्ष 3 एलिमेंट्स या पूरी सूची—जो भी अधिक हो—वापस करेगा। यहाँ सूची से तात्पर्य cast, keywords, और 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)
 titlecastdirectorkeywordsgenres
0Toy Story[Tom Hanks, Tim Allen, Don Rickles]John Lasseter[jealousy, toy, boy][Animation, Comedy, Family]
1Jumanji[Robin Williams, Jonathan Hyde, Kirsten Dunst]Joe Johnston[board game, disappearance, based on children'...[Adventure, Fantasy, Family]
2Grumpier Old Men[Walter Matthau, Jack Lemmon, Ann-Margret]Howard Deutch[fishing, best friend, duringcreditsstinger][Romance, Comedy]

अगला कदम है नामों और कीवर्ड इंस्टैंसेज़ को लोअरकेस में बदलना और उनके बीच की सभी स्पेसेज़ हटाना।

शब्दों के बीच की स्पेस हटाना एक महत्वपूर्ण प्रीप्रोसेसिंग स्टेप है। यह इसलिए किया जाता है ताकि आपका वेक्टराइज़र "Johnny Depp" और "Johnny Galecki" के Johnny को एक जैसा न गिने। इस प्रोसेसिंग के बाद, उपर्युक्त अभिनेताओं को "johnnydepp" और "johnnygalecki" के रूप में दर्शाया जाएगा और आपके वेक्टराइज़र के लिए वे अलग-अलग होंगे।

एक और अच्छा उदाहरण जहाँ मॉडल एक ही वेक्टर रिप्रेज़ेंटेशन दे सकता है, वह है "bread jam" और "traffic jam"। इसलिए, मौजूद किसी भी स्पेस को हटाना बेहतर है।

नीचे दिया गया फ़ंक्शन आपके लिए ठीक यही करेगा:

# 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)

अब आप अपनी "metadata soup" बनाने की स्थिति में हैं, जो एक स्ट्रिंग है जिसमें वह सारा मेटाडाटा होता है जिसे आप अपने वेक्टराइज़र को देना चाहते हैं (यानी कलाकार, निर्देशक और कीवर्ड)।

create_soup फ़ंक्शन बस सभी आवश्यक कॉलम्स को एक स्पेस से जोड़ देगा। यह अंतिम प्रीप्रोसेसिंग स्टेप है, और इस फ़ंक्शन का आउटपुट शब्द वेक्टर मॉडल में फीड किया जाएगा।

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
0jealousy toy boy tomhanks timallen donrickles ...
1boardgame disappearance basedonchildren'sbook ...

अगले स्टेप वही हैं जो आपने अपने plot description based recommender के साथ किए थे। एक प्रमुख अंतर यह है कि आप TF-IDF की बजाय CountVectorizer() का उपयोग करते हैं। इसका कारण यह है कि यदि कोई अभिनेता/निर्देशक अपेक्षाकृत अधिक फिल्मों में काम कर चुका/चुकी है, तो आप उसकी उपस्थिति का वज़न कम नहीं करना चाहते। इस सन्दर्भ में उनका वज़न कम करना सहज रूप से उचित नहीं है।

CountVectorizer() और TF-IDF के बीच मुख्य अंतर inverse document frequency (IDF) कंपोनेंट है जो बाद वाले में मौजूद होता है और पहले वाले में नहीं।

# 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)

ऊपर के आउटपुट से आप देख सकते हैं कि उस मेटाडाटा में 73,881 शब्दावली हैं जिन्हें आपने फीड किया।

अब, आप एम्बेडिंग्स के बीच दूरी मापने के लिए cosine_similarity का उपयोग करेंगे।

# 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'])

अब आप अपनी get_recommendations() फ़ंक्शन को फिर से उपयोग कर सकते हैं और नए cosine_sim2 मैट्रिक्स को दूसरे आर्गुमेंट के रूप में पास कर सकते हैं।

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

बहुत बढ़िया! आप देख रहे हैं कि आपके रिकमेंडर ने अधिक मेटाडाटा के कारण अधिक जानकारी कैप्चर की है और बेहतर सिफारिशें दी हैं। बेशक, सिफारिशों में सुधार के लिए इस सिस्टम के साथ प्रयोग करने के अनेकों तरीके हैं।

कुछ सुझाव:

  • पॉपुलैरिटी फ़िल्टर जोड़ें: यह रिकमेंडर सबसे मिलते-जुलते 30 मूवीज़ लेगा, वेटेड रेटिंग्स की गणना करेगा (ऊपर दिए गए IMDB फ़ॉर्मूला का उपयोग करके), इन रेटिंग्स के आधार पर मूवीज़ को सॉर्ट करेगा और शीर्ष 10 मूवीज़ लौटाएगा।

  • अन्य क्रू सदस्य: अन्य क्रू सदस्यों के नाम, जैसे पटकथा लेखक और निर्माता, भी शामिल किए जा सकते हैं।

  • निर्देशक का बढ़ा हुआ वज़न: निर्देशक को अधिक वज़न देने के लिए, उसे सूप में कई बार उल्लेख किया जा सकता है ताकि एक ही निर्देशक वाली फ़िल्मों के समानता स्कोर्स बढ़ें।

Python के साथ Collaborative Filtering

इस ट्यूटोरियल में, आपने अपना खुद का Simple और Content-Based Movie Recommender Systems बनाना सीखा। एक और बेहद लोकप्रिय प्रकार का रिकमेंडर होता है जिसे collaborative filters कहा जाता है।

Collaborative filters को आगे दो प्रकारों में बाँटा जा सकता है:

  • User-based Filtering: ये सिस्टम उन उत्पादों की सिफारिश करते हैं जो समान उपयोगकर्ताओं को पसंद आए हों। उदाहरण के लिए, मान लीजिए एलिस और बॉब की किताबों में समान रुचि है (यानी वे काफी हद तक वही किताबें पसंद और नापसंद करते हैं)। अब, मान लें कि एक नई किताब आई है और एलिस ने उसे पढ़कर बहुत पसंद किया। तो बहुत संभव है कि बॉब को भी वह पसंद आए, और इसलिए सिस्टम वह किताब बॉब को सिफारिश करेगा।
  • Item-based Filtering: ये सिस्टम आपके द्वारा बनाए गए कंटेंट रिकमेंडेशन इंजन के काफ़ी समान हैं। ये सिस्टम इस आधार पर समान आइटम्स पहचानते हैं कि लोगों ने अतीत में उन्हें कैसे रेट किया है। उदाहरण के लिए, अगर एलिस, बॉब और ईव ने The Lord of the Rings और The Hobbit को 5 स्टार दिए हैं, तो सिस्टम इन आइटम्स को समान मानता है। इसलिए, अगर कोई The Lord of the Rings खरीदता है, तो सिस्टम उसे The Hobbit भी सिफारिश करता है।

रेटिंग सिस्टम पर आधारित collaborative filtering का एक उदाहरण:

graphic

आप इस ट्यूटोरियल में ये सिस्टम नहीं बनाएँगे, लेकिन इन्हें बनाने के लिए आवश्यक अधिकांश विचारों से आप पहले ही परिचित हैं। collaborative filters के साथ शुरुआत करने के लिए एक अच्छा स्थान MovieLens डेटासेट की जाँच करना है, जो यहाँ मिल सकता है।

निष्कर्ष

इस ट्यूटोरियल को पूरा करने पर बधाई!

आपने सफलतापूर्वक हमारा ट्यूटोरियल पूरा किया, जिसमें आपने Python में रिकमेंडर सिस्टम्स के बारे में सब कुछ सीखा। आपने simple और content-based रिकमेंडर्स बनाना सीखा।

आप सब के लिए एक अच्छा अभ्यास यह होगा कि आपने जिन MovieLens डेटासेट के सबसेट का उपयोग करके simple और content-based रिकमेंडर्स बनाए, उसी पर आधारित होकर Python में collaborative filtering लागू करें।

यदि आप अभी-अभी Python शुरू कर रहे हैं और अधिक सीखना चाहते हैं, तो DataCamp का Introduction to Data Science in Python कोर्स लें।

विषय