3 روش برای ساخت سیستم پیشنهاددهنده
بی شک استفاده سیستم پیشنهاددهنده Recommendation system یکی از بهترین روشها برای بهبود تجربه کاربری در سامانههای مختلف و دروازه ورودی است به دنیای یادگیری ماشین. بسیاری از شرکتها در حال حاضر از سیستمهای پیشنهاددهنده یا بخش «پیشنهادات شما» استفاده میکنند. اخیراً محبوبیت سیستمهای پیشنهاددهنده به لطف شرکتهایی مثل آمازون ، نتفلیکس و یوتیوب که هر یک نسخه شخصیسازیشده و مخصوص به خود را به کار گرفتهاند، به شدت افزایش یافته است.
پیش از هر چیز باید تعریف سیستم پیشنهاددهنده را بدانیم. سیستم پیشنهاددهنده در حقیقت روشی است برای کنار گذاشتن اطلاعات کم یا بیاهمیت از جریان اطلاعات موجود پیش از نمایش آن اطلاعات به کاربر. تعریف دیگری نیز برای سیستمهای پیشنهاددهنده وجود دارد که به این ترتیب است: یکی از انواع سیستمهای فیلترکننده اطلاعات که هدف آن پیشبینی «رتبهبندی» یا «ترجیحی» است که کاربر برای یک محصول یا خدمت درنظر میگیرد.
حال میتوانیم به سراغ حل مسئله اصلی برویم. برای مشاهده کد تکمیلشده میتوانید به این لینک مراجعه نمایید. برای این مثال از دیتاست MovieLens 100k Dataset. استفاده خواهیم کرد.
فیلتر تعاملی
فیلتر تعاملی یکی از محبوبترین الگوریتمهای پیشنهاددهندهای است که در موتورهای پیشنهاددهنده مورد استفاده قرار میگیرد و بر این فرض بنا شده که افرادی که در گذشته سر چیزی با یکدیگر هم نظر بودهاند، در آینده نیز همانطور خواهند بود و بنابراین اقلام موردعلاقه فرد در آینده مشابه گذشته خواهد بود.
برای مثال، فرض کنید من و دوستم سلیقه مشابهی در انتخاب کتاب داشتهایم. حال دوست من از کتابی خوشش آمده که من هنوز نخواندهام، اما از آنجا که در گذشته علایق یکسانی داشتهایم، به احتمال زیاد من نیز از آن کتاب خوشم خواهد آمد. به همین دلیل نیز او کتاب مذکور را به من پیشنهاد میدهد. این نوع پیشنهادات، فیلتر تعاملی مبتنی بر کاربر User-based collaborative filtering نیز نامیده میشوند.
حال بیایید در مثال قبل به جای تمرکز روی علایق دوست من، روی مجموعهای از اقلام که من قبلاً نسبت به آنها علاقه نشان دادهام، متمرکز شویم. به این ترتیب، اقلام جدید بر اساس شباهتی که با اقلام موردعلاقه من در گذشته دارند، به من پیشنهاد خواهند شد و این شباهت بر اساس رتبه یا امتیازی که من به اقلام میدهم به دست آمده است. در چنین پیشنهاداتی با این جمله مواجه هستیم: «کاربرانی که به فلان اقلام علاقه نشان دادهاند، بهمان اقلام را نیز دوست داشتهاند». این نوع الگوریتم را با نام فیلتر تعاملی مبتنی بر اقلام Item-based collaborative filtering نیز میشناسیم.
در مجموع، هر دوی این روشها در دستهای از انواع فیلترهای تعاملی قرار میگیرند که با نام روشهای مبتنی بر حافظه Memory based methods شناخته میشوند. حال بیایید مثالی از این روشهای مبتنی بر حافظه را در پایتون ببینیم.
import numpy as np import pandas as pd from sklearn.metrics import mean_squared_error, pairwise # creating n x m matrix where n is user_id and m is item_id user_ratings = pd.pivot_table(rating, index="user_id", columns="item_id", values="rating").fillna(0) # user and item counts n_users = len(user_ratings.index) n_items = len(user_ratings.columns) print(f"Users: {n_users}\nItems: {n_items}") user_ratings.head() Users: 943 Items: 1682
به این ترتیب دادهها در قالب مدنظر ما قرار میگیرند. حال میخواهم دیتاست را به صورت تصادفی به دو دیتاست آموزشی و آزمایشی تقسیم کنم تا بتوانم بعدها از دیتاست آزمایشی برای آزمون و ارزیابی مدل استفاده کنم.
# https://www.ethanrosenthal.com/2015/11/02/intro-to-collaborative-filtering/ def train_test_split(data: np.array, n_users: int, n_items:int): # create a empty array of shape n x m for test test = np.zeros((n_users, n_items)) train = data.copy() # for each user, we generate a random sample of 5 from movies they've watched for user in range(n_users): random_sample = np.random.choice(data[user, :].nonzero()[0], size=5, replace=False) # set the train to zero to represent no rating and the test will be the original rating train[user, random_sample] = 0. test[user, random_sample] = data[user, random_sample] return train, test train, test = train_test_split(data=user_ratings.to_numpy(), n_users=n_users, n_items=n_items)
اولین مرحله در ساخت یک سیستم فیلترینگ تعاملی، محاسبه شباهت بین کاربران (در روش مبتنی بر کاربر) یا اقلام (در روش مبتنی بر اقلام) است.
user_similarity = pairwise.cosine_similarity(train + 1e-9) item_similarity = pairwise.cosine_similarity(train.T + 1e-9) print(user_similarity.shape, item_similarity.shape) (943, 943) (1682, 1682)
قدم بعدی پیشبینی رتبهبندیهایی است که در دادهها ذکر نشدهاند. وقتی این پیشبینیها انجام گیرند، میتوانیم نتایج حاصله را با مقادیر واقعی دیتاست آزمایشی مقایسه کنیم تا کیفیت مدل مشخص شود. در این مقاله به مبحث معیارهای ارزیابی نخواهیم پرداخت.
# predict user ratings not included in data user_preds = user_similarity.dot(train) / np.array([np.abs(user_similarity).sum(axis=1)]).T # # get the nonzero elements nonzero_test = test[test.nonzero()] nonzero_user_preds = user_preds[test.nonzero()] user_rating_preds = mean_squared_error(nonzero_test, nonzero_user_preds) print(f"UBCF Mean Squared Error: {user_rating_preds}") UBCF Mean Squared Error: 8.250006012927786
کد بالا نمونهای است از فیلتر تعاملی مبتنی بر کاربر. میتوانیم روش مبتنی بر اقلام را نیز به صورت زیر پیادهسازی کنیم.
# predict item ratings not included in data item_preds = train.dot(item_similarity) / np.array([np.abs(item_similarity).sum(axis=1)]) # get the nonzero elements nonzero_item_preds = item_preds[test.nonzero()] item_rating_preds = mean_squared_error(nonzero_test, nonzero_item_preds) print(f"IBCF Mean Squared Error: {item_rating_preds}") IBCF Mean Squared Error: 11.361431844412557
شاید این الگوریتمها عملکرد قابلتوجهی نداشته باشند، اما برای درک مفهوم و شیوههای ارائه پیشنهاد کافی هستند.
همانطور که گفته شد روشهای تعاملی که در بالا دیدیم، در دسته روشهای مبتنی بر حافظه قرار میگیرند. یک دسته دیگر از انواع روشهای فیلتر تعاملی، رویکرد مبتنی بر مدل است. در این رویکرد، مدلها با استفاده از الگوریتمهای دادهکاوی و یادگیری ماشینی طراحی میشوند تا رتبهبندی کاربر برای اقلام رتبهبندینشده را پیشبینی کنند.
فیلتر محتوایی
یک روش دیگر برای پیشنهاد اطلاعات مفید به کاربران روش فیلتر محتوایی است. پیشنهادات در این تکنیک بر پایه توضیحات ارائه شده در خصوص هر آیتم یا محصول و پروفایل ترجیحات کاربر ارائه میشوند. این روش برای مسائلی مناسب است که در آنها جزئیات و ویژگیهای اقلام مشخص باشد، اما اطلاعات زیادی از کاربر در دسترس نباشد. در روش فیلتر محتوایی، ارائه پیشنهاد در واقع از طریق دستهبندی کاربران انجام میشود.
برای مثال، میتوانیم سناریوی پیشنهاد فیلم را در نظر بگیریم. فرض کنید سایت جدیدی طراحی کردهاید که اطلاعات چندانی درباره کاربران آن ندارید، اما اطلاعات فیلمها کاملاً در دسترس هستند. کاری که باید انجام دهیم این است که اطلاعات مِتا یا مشخصات هر فیلم از قبیل ژانر، بازیگران، تهیهکنندگان، مدت زمان فیلم و غیره را برداریم و آنها را به عنوان ورودی به مدل بدهیم تا پیشبینی کند که آیا کاربر از یک فیلم خوشش خواهد آمد یا خیر.
باید گفت که در سناریوی بالا پروفایلی حاوی ترجیحات هر کاربر نیز داریم. این اطلاعات از طریق پرسش از کاربر یا با ثبت رفتار وی به روش تلویحی به دست آمدهاند.
نکته: به عنوان روشی بهینهتر برای جمعآوری دادههای کاربر میتوان از روش ترکیبی نیز استفاده کرد.
بیایید ببینیم این روش چگونه در پایتون اجرا میشود:
# merge data so we know the features of each movie movies = pd.merge(item, rating, right_on="item_id", left_on="movie_id") # create a pivot table movies_pivot = pd.pivot_table(movies, index="user_id", columns="movie_title", values="rating") # Transpose only so it fit's in the screen movies_pivot.T.head()
# avg ratings and rating counts avg_rating = movies.groupby("movie_title")["rating"].mean() num_ratings = movies.groupby("movie_title")["rating"].count() # getting counts and average ratings ratings_counts = pd.DataFrame({"avg_rating": avg_rating, "num_of_ratings": num_ratings}) # joining the new values to movie data full_movie_data = pd.merge(movies, ratings_counts, left_on="movie_title", right_index=True) # https://towardsdatascience.com/recommender-system-in-python-part-2-content-based-system-693a0e4bb306 def get_similar_movies(full_movie_data: pd.DataFrame, movie_matrix: pd.DataFrame, movie_title: str, min_num_of_ratings: int = 100, n_recommendations: int = 5 ): """ Get similar movies based on correlation with other movies """ # get most correlated movies similar_movies = movie_matrix.corrwith(movie_matrix[movie_title]) # converting to a dataframe and dropping NaN's similar_corr_df = pd.DataFrame({"correlation":similar_movies}) similar_corr_df.dropna(inplace=True) # store the oringinal dataframe orig = full_movie_data.copy() # merge with correlated dataframe but only keep specified columns corr_with_movie = pd.merge(left=similar_corr_df, right=orig, on="movie_title")[ ["movie_title", "correlation", "avg_rating", "num_of_ratings"]].drop_duplicates().reset_index(drop=True) # filter movies with less than min_num_of_ratings result = corr_with_movie[corr_with_movie['num_of_ratings'] > min_num_of_ratings].sort_values(by='correlation', ascending =False) return result.iloc[1:, :].head() # test function on Toy Story get_similar_movies(full_movie_data, movies_pivot, "Toy Story (1995)")
سیستم پیشنهاددهنده ترکیبی
استفاده از سیستمهای ترکیبی در دنیای واقعی رایجتر از روشهای ساده است چرا که در این روشها، مؤلفههای رویکردهای مختلف با هم ترکیب میشوند و به این ترتیب، بسیاری از کاستیهای آنها جبران میشود. در این مقاله، منظور از سیستم ترکیبی سیستمی است که دو روش تعاملی و محتوایی را با هم ترکیب میکند.
در مقاله دیگری با همین مظمون، نوشته شده بود که سیستمهای پیشنهاددهنده به منظور رسیدن به کارآیی با دو مشکل دست و پنجه نرم میکنند:
1. مسئله پراکندگی رتبهها: یعنی تعداد رتبههایی که به عنوان ورودی به سیستم داده میشود بسیار کمتر از تعداد رتبههایی است که سیستم باید پیشبینی کند. این مشکل معمولاً در مرحله آغازین یعنی وقتی اطلاعات کمی درباره رتبهبندیها داریم، پررنگتر است.
2. مسئله اولین رتبه: یعنی تنها اقلامی را میتوان به دیگران پیشنهاد داد که حداقل یک بار توسط کاربر دیگری رتبهبندی شده باشند.
سخن آخر
در این مقاله به 3 روش کاربردی برای ساخت سیستمهای پیشنهاددهنده پرداختیم و کدهای مربوط 2 تا از آنها (روش فیلتر تعاملی و فیلتر محتوایی) را به زبان پایتون نوشتیم. در این مقاله به جزئیات الگوریتمها، تئوری پشت آنها و معیارهای ارزیابی عملکرد پرداخته نشد. اگر اطلاعات بیشتری در این خصوص نیاز دارید میتوانید به کانال یوتیوب در حوزه سیستمهای پیشنهاددهنده آقای اندرو اِنجی و پلیلیست اختصاصی وی مراجعه کنید.