ساخت سیستم توصیه گر در پایتون — به زبان ساده

۵۷۶ بازدید
آخرین به‌روزرسانی: ۲۹ مرداد ۱۴۰۲
زمان مطالعه: ۶ دقیقه
ساخت سیستم توصیه گر در پایتون — به زبان ساده

در این مطلب، ابتدا مفهوم سیستم توصیه گر (Recommendation System) بیان می‌شود و سپس در یک راهنمای گام به گام، روش ساخت یک «سیستم پالایش» برای پیشنهاد فیلم به کاربران، مورد بررسی قرار می‌گیرد. سیستم‌های توصیه‌گر یکی از برجسته‌ترین مثال‌های یادگیری ماشین در زندگی انسان‌ها هستند. آن‌ها تعیین می‌کنند که چه مطالبی در «خوراک خبری» حساب کاربری فیس‌بوک افراد نمایش داده شوند، محصولات با چه ترتیبی در آمازون به نمایش دربیایند، چه ویدئوهایی در نتفلیکس به افراد پیشنهاد شوند و مثال‌های بیشمار دیگر. اما، سیستم‌های توصیه‌گر واقعا چه هستند و چگونه کار می‌کنند؟ در این مطلب، ضمن ارائه مفاهیم اولیه، روش ساخت یک سیستم توصیه‌گر فیلم به همراه کدهای پیاده‌سازی آن ارائه شده است.

سیستم توصیه گر چیست؟

یک سیستم توصیه‌گر، یک مدل پالایش اطلاعات است که آیتم‌ها را برای کاربر، امتیازدهی یا رتبه‌دهی می‌کند. این سیستم‌ها غالبا از دو روش رتبه‌بندی استفاده می‌کنند که هر یک در ادامه بیان شده است.

  • پالایش مبتنی بر محتوا (Content-Based Filtering): در روش رتبه‌بندی پالایش مبتنی بر محتوا، آیتم‌های توصیه شده بر مبنای مشابهت آیتم به آیتم و اولویت‌های صریح کاربران هستند.
  • پالایش گروهی (Collaborative Filtering): در روش پالایش گروهی، آیتم‌ها، بر مبنای اولویت‌های دیگر کاربران دارای تاریخچه و مشخصه تراکنش‌های مشابه، به کاربر توصیه می‌شوند.

اطلاعات استفاده شده در پالایش گروهی در حالتی که در آن کاربرها امتیازاتی را برای هر آیتم تعیین می‌کنند صریح و در حالتی که اولویت‌های کاربران از رفتار آن‌ها (خریدها، بازدیدها و دیگر موارد) استخراج شده، به صورت ضمنی است. موفق‌ترین سیستم‌های توصیه‌گر از رویکرد ترکیبی (Hybrid) استفاده می‌کنند که در آن‌ها، هر دو روش پالایش بیان شده در بالا را ترکیب می‌کنند.

مجموعه داده MovieLens

استفاده از یک مثال، به درک بهتر مفاهیم بیان شده پیرامون سیستم‌های توصیه‌گر کمک می‌کند. در ادامه یک سیستم توصیه‌گر فیلم با استفاده از «زبان برنامه‌نویسی پایتون» (python programming Language) ساخته خواهد شد. «گروپ‌لنز» (GroupLens)، یک گروه تحقیقاتی در دانشگاه «مینه‌سوتا» (Minnesota) است که مجموعه داده MovieLens را سخاوتمندانه در اختیار عموم قرار داده است.

این مجموعه داده، تقریبا شامل ۲۰ میلیون رتبه‌بندی انجام شده توسط ۱۳۸۰۰۰ کاربر، برای ۲۷۰۰۰ فیلم است. علاوه بر این، این مجموعه داده شامل ژانر فیلم‌ها و اطلاعات روز پیرامون آن‌ها است. در ساخت سیستم توصیه‌گر بیان شده در این مطلب، از مجموعه داده مذکور استفاده خواهد شد.

پالایش ساده مبتنی بر محتوا

در ادامه، یک سیستم توصیه‌گر ساده ساخته می‌شود که از پالایش‌های مبتنی بر محتوا (برای مثال مشابهت آیتم) برای توصیه فیلم‌ها به کاربران استفاده می‌کند.

برای انجام این کار، ابتدا باید مجموعه داده فیلم‌ها را از MovieLens بارگذاری کرده و فیلد «ژانر» (Genre) را به روش Multihot-Encode رمزنگاری کرد.

1import pandas as pd
2import numpy as np
3
4movies = pd.read_csv("movies.csv")
5# dummy encode the genre
6movies = movies.join(movies.genres.str.get_dummies("|"))

ویژگی «genres» شامل یک یا تعداد بیشتری خط عمودی (|) است که ژانرها را از یکدیگر جدا می‌کنند. آخرین خط در کد بالا، یک ستون به هر ژانر ممکن اضافه می‌کند و در صورتی که فیلم، تگ آن ژانر را داشته باشد، ۱ را و در غیر این صورت، ۰ را قرار می‌دهد. در ادامه، توصیه‌هایی بر اساس مشابهت عناصر و با استفاده از این تگ‌ها ارائه خواهد شد. یک سنجه متداول برای داده‌های طبقه‌ای (مانند تگ‌ها)، «تشابه کسینوسی» (Cosine Similarity) است. برای هر دو عنصر i و j، تشابه کسینوسی i و j برابر با کسینوس زاویه بین i و j است که در آن i و j به عنوان بردارهایی در فضای ویژگی تفسیر می‌شوند. باید به خاطر داشت که کسینوس از طریق ضرب داخلی این بردارها (مطابق فرمول زیر) به دست می‌آید.

سیستم توصیه‌گر

به عنوان یک مثال، می‌توان فیلم‌های (”i := $ Toy Story (genre tags “Adventure”, “Animation”, “Children”, “Comedy”, and “Fantasy$ و (”j := $ Jumanji (genre tags “Adventure”, “Children”, and “Fantasy$ را در نظر گرفت (فیلم‌های داستان اسباب‌بازی و جومانجی به همراه تگ مربوط به ژانر آن‌ها در نظر گرفته شده است). حاصل ضرب داخلی i.j برابر با ۳ است (دو فیم دارای سه تگ مشابه با هم هستند). $$ \sqrt {5} $$=||i|| و $$ \sqrt {3} $$=||j||، بنابراین، تشابه کسینوسی بین دو فیلم برابر است با:

سیستم‌های توصیه‌گر

می‌توان تشابه کسینوسی همه عناصر را در مجموعه داده با استفاده از کد زیر محاسبه کرد:

1from sklearn.metrics.pairwise import cosine_similarity
2
3# compute the cosine similarity
4cos_sim = cosine_similarity(movies.iloc[:,3:])

اولین فیلم در مجموعه داده مورد استفاده، «داستان اسباب‌بازی» (Toy Story) است. با استفاده از قطعه کد زیر، فیلم‌های مشابه با Toy Story پیدا می‌شوند.

1# Let's get the top 5 most similar films:
2toystory_top5 = np.argsort(sim[0])[-5:][::-1]
3
4# array([   0, 8219, 3568, 9430, 3000, 2809, 2355, 6194, 8927, 6948, 7760,
5#       1706, 6486, 6260, 5490])

سیستم توصیه‌گر

هر پنج فیلم اول دارای تگ‌های ژانری مانند Toy Story هستند و بنابراین مشابهت کسینوسی برای آن‌ها برابر با ۱ است. در حقیقت، برای داده‌های نمونه که در اینجا استفاده شده‌اند، ۱۳ فیلم با مشابهت کسینوسی ۱ وجود دارد؛ شبیه‌ترین فیلم به Toy Story که فاقد تگ مشابه است، فیلم «مورچه‌کش» (The Ant Bully) محصول سال ۲۰۰۶ است که تگ افزوده «IMAX» را دارد.

پالایش گروهی ساده

پالایش گروهی، آیتم‌ها را بر اساس آنچه که کاربران مشابه «پسندیده‌اند» (liked) پیشنهاد می‌دهد. خوشبختانه، در مجموعه داده MovieLens، اطلاعات غنی پیرامون اولویت‌های کاربران به صورت رتبه‌بندی فیلم‌ها موجود است. در واقع، هر کاربر یک یا تعداد بیشتری رتبه‌بندی عددی بین مقادیر ۱ تا ۵ به هر فیلم اختصاص می‌دهد که نشان می‌دهد چقدر آن‌ها از یک فیلم لذت برده‌اند.

می‌توان مساله توصیه عناصر به کاربران را ، یک «وظیفه پیش‌بینی» (prediction task) در نظر گرفت، به این صورت که «بر اساس رتبه‌هایی که کاربر به فیلم‌های دیگر داده، امتیاز احتمالی که به یک فیلم می‌دهد چقدر است؟ یک راه ساده برای پاسخ به این پرسش، تخصیص رتبه «شباهت وزن‌دار» (Similarity-Weighted) به هر عنصر با استفاده از رتبه‌بندی‌های کاربران است:

سیستم توصیه‌گر

که در آن، $$\widehat {r}_{u,i}$$ رتبه پیش‌بینی شده برای آیتم i توسط کاربر u و $$ s(u,v) $$ اندازه مشابهت بین کاربر u و v است؛ همچنین، r رتبه تعیین شده برای آیتم i توسط کاربر v است. برای اندازه‌گیری مشابهت کاربران، باید به امتیازهای داده شده توسط کاربران به فیلم‌ها، توجه می‌شود. کاربرانی با امتیاز مشابه، مشابه در نظر گرفته خواهند شد. برای کار با این داده‌های رتبه‌بندی، اولین گام مهم نرمال کردن امتیازها است. این کار، طی سه مرحله انجام می‌شود. ابتدا، «میانگین کلی رتبه‌ها» (overall mean rating) تفریق می‌شود (برای همه فیلم‌ها و کاربرها)، بنابراین رتبه‌بندی انجام شده در مرکزیت صفر قرار خواهد گرفت. سپس، کار مشابهی برای هر فیلم انجام می‌گیرد تا تفاضل میانگین رتبه‌های یک فیلم داده شده محاسبه شود. در نهایت، میانگین رتبه‌ها برای هر کاربر تفریق می‌شود تا تنوع فردی محاسبه شود (برای مثال، یک کاربر همیشه نسبت به کاربر دیگر، رتبه بالاتری می‌دهد). به لحاظ ریاضیاتی، رتبه اصلاح شده $$\widetilde {r}_{u,i}$$ برابر است با:

سیستم توصیه‌گر

که در آن، $${r}_{u,i}$$ رتبه پایه، و $$\bar r$$ میانگین کلی رتبه و $$ \bar r,i $$ میانگین رتبه برای عنصر i (پس از تفریق میانگین کلی) است. همچنین، $$ \bar r,u $$ میانگین رتبه‌بندی برای کاربر u (پس از تنظیم رتبه‌بندی میانگین کلی و رتبه‌بندی میانگین آیتم‌ها) است. برای سادگی، در ادامه به مقادیر رتبه‌های اصلاح شده $$\widetilde r$$ به عنوان اولویت کاربر u برای عنصر i ارجاع داده می‌شود. سپس، داده‌های مقادیر رتبه‌ها بارگذاری و مقادیر رتبه‌های اصلاح شده محاسبه می‌شوند:

1ratings = pd.read_csv("ratings.csv")
2
3mean_rating = ratings['rating'].mean() # compute mean rating
4
5pref_matrix = ratings[['userId', 'movieId', 'rating']].pivot(index='userId', columns='movieId', values='rating')
6
7pref_matrix = pref_matrix - mean_rating # adjust by overall mean
8
9item_mean_rating = pref_matrix.mean(axis=0)
10pref_matrix = pref_matrix - item_mean_rating # adjust by item mean
11
12user_mean_rating = pref_matrix.mean(axis=1)
13pref_matrix = pref_matrix - user_mean_rating

در این نقطه، می‌توان به سادگی یک تخمین مبنایی برای مقدار رتبه‌هایی که کاربران به فیلم‌های که ندیده‌اند خواهند داد، به دست آورد.

1pref_matrix.fillna(0) + user_mean_rating + item_mean_rating + mean_rating

می‌توان فاصله تا یک کاربر خاص را (در این مورد، کاربر ۰ یا user 0) به صورت زیر محاسبه کرد.

1mat = pref_matrix.values
2k = 0 # use the first user
3np.nansum((mat - mat[k,:])**2,axis=1).reshape(-1,1)

معلوم می‌شود که نزدیک‌ترین کاربر به user 0 کاربر ۱۲ یا همان user 12، (با فاصله ۰) است.

1np.nansum((mat - mat[0,:])**2,axis=1)[1:].argmin() # returns 11
2# check it:
3np.nansum(mat[12] - mat[0]) # returns 0.0

دو فیلم پیدا می‌شوند که کاربر ۱۲ مشاهده کرده، ولی کاربر ۰ آن‌ها را ندیده است.

1np.where(~np.isnan(mat[12]) & np.isnan(mat[0]) == True)
2# returns (array([304, 596]),)
3
4mat[12][[304, 596]]
5# returns array([-2.13265214, -0.89476547])

متاسفانه، user 12 هر دوی فیلم‌هایی که کاربر ۰ (user 0) هنوز ندیده را، نپسندیده (Dislike) است. بنابراین، محاسبات همچنان برای همه کاربران نزدیک به کاربر ۰ باید ادامه پیدا کند.

نتیجه‌گیری و خلاصه

روش‌های استفاده شده در این مطلب، بر «مبنای همسایگی» (Neighborhood-Based) هستند و همانطور که در بالا مشهود است، این روش معایب (تله‌هایی) دارد. برای مثال، همسایه‌ها ممکن است هیچ یک از فیلم‌هایی که کاربر مشخص (در اینجا مثلا کاربر ۰) تاکنون ندیده را پیشنهاد نکنند. به دلیل نیاز به محاسبه فاصله‌ها به صورت جفت به جفت، روش‌های مبتنی بر همسایگی به سختی مقیاس‌پذیر می‌شوند.

روش‌های دیگری نیز برای ساخت سیستم‌های توصیه‌گر فیلم قابل استفاده هستند که از جمله آن‌ها می‌توان به روش‌های «عامل پنهان» (Latent Factor) اشاره کرد. عامل پنهان، فاقد برخی از مشکلات بیان شده برای روش‌های مبتنی بر همسایگی است. یک روش دیگر برای ساخت سیستم توصیه‌گر فیلم نیز در این مطلب از مجله فرادرس، به همراه پیاده‌سازی پایتون آن آمده است.

اگر نوشته بالا برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

^^

بر اساس رای ۶ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
kdnuggets
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *