نظام التوصية لنادي Directum. الجزء الأول ، التعاونية

كل يوم ، يتلقى المستخدمون في جميع أنحاء العالم عددًا كبيرًا من الرسائل البريدية المختلفة - فقط من خلال خدمة MailChimp اليومية التي ترسل مليار رسالة . من هؤلاء ، تم اكتشاف 20.81 ٪ .


كل شهر ، يتلقى مستخدمو مواقعنا رسائل إخبارية تحتوي على مواد يختارها المحرر. حوالي 21 ٪ من القراء فتح هذه الرسائل.


لزيادة هذا العدد ، يمكنك جعلها مخصصة. تتمثل إحدى الطرق في إضافة نظام توصية يحث المواد المثيرة للاهتمام على قارئ معين.


في هذه المقالة سأتحدث عن كيفية تطبيق نظام التوصيات من البداية بناءً على التصفية التعاونية.


يحتوي الجزء الأول من المقالة على الأساس النظري لتنفيذ نظام التوصيات. الرياضيات المدرسية كافية لفهم المواد.


يصف الجزء الثاني تطبيق Python لبيانات موقعنا.


قليلا من نظرية التصفية التعاونية


التصفية التعاونية هي على الأرجح الطريقة الأسهل في أنظمة التوصية. يعتمد ذلك على فكرة أن المستخدمين المتشابهين يحبون الأشياء المشابهة ، مثل المقالات.


ماذا يعني "مستخدمون مشابهون"؟


من انت


كيفية تحديد مقدار Vasily يشبه Ivan أو مقالة حول SQL Server لمقال حول PostgreSQL؟


لنلقِ نظرة على مثال. دعنا نقول أن لدينا أربعة مستخدمين: فاسيلي وإيفان وإينا وآنا. يحتوي الموقع على خمس مقالات: المادة 1 ، والمادة 2 ، والمادة 3 ، والمادة 4 ، والمادة 5. في الجدول أدناه ، يكون الرقم عند تقاطع المستخدم والمادة هو تصنيف المستخدم لهذه المادة على مقياس من خمس نقاط. صفر في الجدول هي المقالات التي لم يتم تصنيفها من قبل المستخدم. على سبيل المثال ، أعجب فاسيلي بالمواد 1 و 3 و 4.


الجدول 1


المادة 1القسم 2القسم 3القسم 4القسم 5
فاسيلي40550
ايفان00450
اينا42400
آنا55005

بشكل حدسي ، يمكننا أن نفترض أنه إذا أحب المستخدمون نفس المقالات ، فستتزامن أذواقهم. ما رأيك ، التي تتشابه مصالحها مع مصالح فاسيلي؟


تتشابه اهتمامات فاسيلي مع مصالح إيفان وإينا وأقل تشبه اهتمامات آنا. لماذا - سوف يقال المزيد.


لمزيد من العمل ، من الضروري إضفاء الطابع الرسمي وقياس "تشابه" فاسيلي وإيفان أو إينا وآنا.


أسهل طريقة للقيام بذلك هي النظر في تصنيفات المستخدمين بوصفًا لملف التعريف الخاص بهم. في المثال ، كل صف في الجدول هو وصف لمستخدم واحد. السطر الأول - وصف باسيل - عبارة عن ناقل مكون من خمسة أرقام: [4 ، 0 ، 5 ، 5 ، 0] ؛ الثاني - إيفان - [0 ، 0 ، 4 ، 5 ، 0] ؛ والثالثة هي إينا - [4 ، 2 ، 4 ، 0 ، 0] ؛ الرابع هو آنا - [5 ، 5 ، 0 ، 0 ، 5].


يمكنك الآن تقديم مفهوم أوصاف المستخدم "قياس التشابه".


إحدى طرق قياس "تشابه" المستخدمين هي حساب مسافة جيب التمام بين المتجهات التي تصفهم.



يتم حساب المسافة جيب التمام بواسطة الصيغة:


1cos theta=1 fracA cdotB||A|| cdot||B||


حيث دولاو B- ناقلات وصف المستخدم ؛ A cdotB- منتج العددية من ناقلات الوصف ؛ ||A||. ||B||- أطوال ناقلات الوصف.


معنى مسافة جيب التمام هو كما يلي: إذا نواقل حيث دولاو B(متجهات وصف المستخدم) "متشابهة" ، ثم تميل الزاوية بينهما إلى الصفر ، وتميل جيب تمام هذه الزاوية إلى الوحدة. في الحالة المثالية ، عندما تتزامن "اهتمامات" المستخدمين ، ستكون مسافة جيب التمام لهما صفراً.


جيب تمام المسافة بين فاسيلي وإيفان:


1cos theta=1 frac4 cdot0+0 cdot0+5 cdot4+5 cdot5+0 cdot0 sqrt42+02+52+52+02 cdot sqrt02+02+42+52+02=0.1349


وبالمثل ، فإن مسافة جيب التمام بين فاسيلي وآنا هي 0.715. أي أن مصالح فاسيلي تشبه مصالح إيفان أكثر من اهتمامات آنا.


كيفية التنبؤ تصنيفات المستخدم؟


هذا الجزء هو الأكثر إثارة للاهتمام. هناك العديد من الخيارات المختلفة. أدناه نعتبر خيارين بسيطين.


توقع التصنيف - متوسط ​​التصنيف بين المستخدمين "مماثلة"


أسهل خيار لحساب التصنيف المتوقع هو معرفة التصنيفات التي وضعها المستخدمون "المشابهون" لهذه المقالة وأخذ التصنيف المتوسط:


ru،i= frac1N sumu inUru،i


في هذه الصيغة:


  • ru،iهو التقدير المتوقع iالمادة ال والمستخدم u.
  • ru،i- تصنيف المستخدم uإلى iالمادة ال
  • يو- الكثير من المستخدمين "المتشابهين" ،
  • N- عدد المستخدمين "المتشابهين".

التصنيف المتوقع - متوسط ​​التصنيف المرجح بين المستخدمين "المتشابهين"


الخيار الأكثر تعقيدًا هو الأخذ في الاعتبار درجة التشابه: يجب أن تؤثر تقييمات المستخدمين الأكثر تشابهًا على التصنيف النهائي أكثر من التصنيفات الأقل تشابهًا:


ru،i= frac sumu inU(1simil(u،u))ru،i sumu inU|1simil(u،u)|


في هذه الصيغة:


  • ru،iهو التقدير المتوقع iالمادة ال والمستخدم u.
  • ru،i- تصنيف المستخدم uإلى iالمادة ال
  • يو- الكثير من المستخدمين "المتشابهين" ،
  • simil(u،u)- "التشابه" (مسافة جيب التمام) للمستخدمين uو u.

كيف تقيس جودة التوصيات؟



عند إنشاء أي نظام توصية ، يجب عليك تحديد المقياس الذي يمكنك من خلاله تقييم جودة نموذجنا - إلى أي مدى يقدم النظام للمستخدم مواد جديدة. على سبيل المثال ، الجذر يعني خطأ مربع ( RMSE) هو الجذر التربيعي للخطأ المتوسط ​​لجميع تقييمات المستخدم. بشكل رسمي ، يتم وصف هذا التدبير بالصيغة:


RMSE= sqrt frac1|D| sumu،i inD( hatru،iru،i)2


في هذه الصيغة


  • D- مجموعة من جميع تصنيفات المستخدمين للمقالات ،
  •  hatru،i- توقع تصنيف المستخدم uمقالة i.
  • ru،i- تصنيف المستخدم الحقيقي uمقالة i.

في الحالة المثالية ، عندما تتوافق التصنيفات المتوقعة مع المستخدم RMSEيساوي الصفر.


النظر في مثال. قدم نظامان مرجعيان تنبؤات لفاسيلي. النتيجة في الجدول أدناه.


المادة 1القسم 2القسم 3القسم 4القسم 5
فاسيلي40550
نظام التوصية 113522
نظام التوصية 241530

ومن الواضح بشكل حدسي أن نظام التوصية الثانية تنبأ بتقييمات أفضل من الأولى. عد RMSE:


RMSE(1)= sqrt frac(41)2+(03)2+(55)2+(52)2+(02)25=2.489دولا


RMSE((2)= sqrt frac(44)2+(01)2+(55)2+(53)2+(00)25=1دولا


من المتوقع أن يكون الخطأ في تقييمات نظام التوصية الثانية أقل بكثير.


تطبيق


لدينا تحت تصرفنا معظم البيانات المتعلقة بالمقالات ومستخدمي الموقع: معلومات عن المقالات والعلامات وإعجابات المستخدمين ، إلخ.


لتنفيذ التصفية التعاونية ، تكون تقييمات المستخدم كافية.


تنصل

فيما يلي ، يتم كتابة الكود "في المقدمة" لإظهار منطق نظام التوصية. في الحياة الواقعية ، من الأفضل استخدام جميع ميزات numpy .


 import pandas as pd import numpy as np import os ratings_df = pd.read_csv('./input/Ratings.csv') print(' :', ratings_df.shape[0]) print(' :', ratings_df[ratings_df['Rate']].shape[0]) unique_user_ids = ratings_df[ratings_df['Rate']]['UserId'].unique() print(' :', len(unique_user_ids)) ratings_df.head() 

الناتج [1]

إجمالي البيانات: 15313
التقييمات الإيجابية: 15121
المستخدمون النشطون: 1007


الهويةDocumentIdمعدلمعرف المستخدم
011صحيح5000
12878صحيح2441
231512صحيح678
341515صحيح678
45877صحيح5110
...............

1007 من المستخدمين النشطين أعطوا 15313 "تصنيفات". من هذه ، 15121 "يحب".


تحتوي البيانات على أربعة أعمدة: معرف صف من قاعدة البيانات (عمود المعرف ) ، معرف كائن (عمود DocumentId ) ، علامة على أن المستخدم أعجب بالمقال (عمود معدل ) ، ومعرف مستخدم (عمود UserId ).


للراحة ، أضف العمود RateInt . 1 في هذا العمود تعني أن المستخدم أعجب بالمقال ؛ -1 - هذا لم يعجبه.


 ratings_df['RateInt'] = ratings_df['Rate'].apply(lambda x: 1 if x else -1) ratings_df.head() 

الناتج [2]
الهويةDocumentIdمعدلمعرف المستخدمRateInt
011صحيح50001
12878صحيح24411
231512صحيح6781
341515صحيح6781
45877صحيح51101

لمزيد من العمل ، يلزم تقسيم مجموعة البيانات إلى تدريب واختبار: سيتم استخدام التدريب لتدريب النموذج ، وسيحدد الاختبار نوعية التنبؤات.


 from sklearn.model_selection import train_test_split train, test = train_test_split(ratings_df, test_size=0.2) 

للراحة ، نقوم بتحويل كل مجموعة إلى جدول ، حيث توجد في الصفوف معرفات المستخدمين ، وفي الأعمدة معرفات المقالات عن طريق القياس بالمثال في بداية المقال.


 def create_matrix(df): ratings_per_user = [] post_ids = df['DocumentId'].unique() for user_id in tqdm_notebook(all_users_ids, ''): row = {'user_id': user_id} ratings = df[df['UserId'] == user_id]['DocumentId'].values for post_id in post_ids: row[str(post_id)] = 1 if post_id in ratings else 0 ratings_per_user.append(row) return pd.DataFrame(ratings_per_user) train_df = create_matrix(train) test_df = create_matrix(test) 

ستتيح لك مصفوفة المطابقة بين المستخدمين والمقالات المفضلة حساب مسافة جيب التمام بين المستخدمين:


 from scipy import spatial def cos_distance(x1, x2): return spatial.distance.cosine(x1, x2) at_least_one_fav_post_users = list(train_valuable_df['user_id'].values) def calculate_distances(df): columns = df.columns[:-1] cp = at_least_one_fav_post_users.copy() data = [] for user_id_1 in tqdm_notebook(at_least_one_fav_post_users, ''): row = {'user_id': user_id_1} for user_id_2 in cp: x1 = df[df['user_id'] == user_id_1][columns].values[0] x2 = df[df['user_id'] == user_id_2][columns].values[0] row[str(user_id_2)] = cos_distance(x1, x2) data.append(row) return pd.DataFrame(data) train_distances = calculate_distances(train_valuable_df) 

الآن أصبح كل شيء جاهزًا لمطالبة المستخدمين بالمقالات التي يرغبون في رؤيتها.


ننفذ الاستراتيجيتين لحساب التوصيات الموضحة أعلاه: متوسط ​​ومتوسط ​​التصنيفات الموزعة بين المستخدمين المتشابهين.


الطريقة الأولى


نأخذ 10 مستخدمين الأقرب إلى الحالي ونتوقع التقييم كمتوسط ​​للمستخدمين مماثلة لهذه المادة:


 from tqdm import tqdm_notebook import heapq def rmse(predicted, actual): return ((predicted - actual) ** 2).mean() ** 0.5 def get_similar(id, n): df = train_distances[train_distances['user_id'] == id] d = df.to_dict('records')[0] top_similar_ids = heapq.nsmallest(n+1, d, key=d.get) top_similar = df[top_similar_ids] return top_similar.to_dict('records')[0] def get_predictions(id, n): top_similar_users = get_similar(id, n) top_similar_users_ids = list([int(x) for x in top_similar_users.keys()]) ratings_for_top_similar = train_df[train_df['user_id'].isin(top_similar_users_ids)] predicted_ratings = {} for article_id in train_df.columns[:-1]: predicted_ratings[article_id] = ratings_for_top_similar[article_id].mean() return predicted_ratings rand_n_users = train_distances.sample(50)['user_id'].values err = 0 for u in tqdm_notebook(rand_n_users): pred = get_predictions(u, 10) err += rmse(test_df[test_df['user_id'] == u][list(pred.keys())].values, pd.DataFrame(pred, index=[0]).values) print(err / len(rand_n_users)) 

بالنسبة للنهج الأول ، حصلنا على خطأ يساوي 0.855.


توصيات للمستخدم عارضة
مقالةتقييم متوقع
الاتجاه 5.6. بحث النص الكامل الجديد0.6364
الاتجاه 5.6 - المزيد من الخيارات للعمل المريح0.6364
تطوير أداة التطوير في DIRECTUM 5.50.6364
DIRECTUM يقدم DirectumRX0.5455
الإصدار السنوي من DIRECTUM الآن 5.1!0.5455
يتم تحديث A إلى K. DIRECTUM 5.0 مرة أخرى0.5455
DIRECTUM Jazz - حل جديد للهاتف المحمول من شركة DIRECTUM0.5455
هل قمت بالفعل بتحديث DIRECTUM؟0.5455
الاتجاه 5.6. أعمدة سوبر وإجراءات المجلد0.5455
تسليط الضوء على بناء جملة GITLAB ISBL0.5455

الطريقة الثانية


الطريقة الثانية تأخذ في الاعتبار درجة تشابه المستخدمين. تنفيذه مماثل تقريبا لأول:


 def get_predictions(id, n): similar_users = get_similar(u, 10) prediction = {} user_ids = list(similar_users.keys()) user_similarities = [] for user_id in user_ids: user_similarities.append(similar_users[user_id]) predicted_ratings = {} for article_id in train_df.columns[:-1]: prediction_for_article = 0 numerator = 0 denominator = 0 for user_id in user_ids: rating = train_df[train_df['user_id'] == int(user_id)][article_id].values[0] numerator += rating * (1 - similar_users[user_id]) denominator += np.abs(similar_users[user_id]) predicted_ratings[article_id] = numerator / denominator return predicted_ratings err = 0 for u in tqdm_notebook(rand_n_users): pred = get_predictions(u, 10) err += rmse(test_df[test_df['user_id'] == u][list(pred.keys())].values, pd.DataFrame(pred, index=[0]).values) print(err / len(rand_n_users)) 

في هذه الحالة ، حصلوا على خطأ 0.866. الخطأ أكبر قليلاً مما كان عليه في الحالة الأولى.


توصيات لنفس المستخدم العشوائي
مقالةتقييم
الاتجاه 5.6. بحث النص الكامل الجديد0.3095
الاتجاه 5.6 - المزيد من الخيارات للعمل المريح0.3095
تطوير أداة التطوير في DIRECTUM 5.50.3095
العديد من الخدمات المباشرة - أداة إدارة واحدة0.2833
يتم تحديث A إلى K. DIRECTUM 5.0 مرة أخرى0.2809
الإصدار السنوي من DIRECTUM الآن 5.1!0.2784
DIRECTUM يقدم DirectumRX0.2778
هل قمت بالفعل بتحديث DIRECTUM؟0.2778
الاتجاه 5.6. أعمدة سوبر وإجراءات المجلد0.2758
DIRECTUM Ario - حل ذكي جديد0.2732

يمكن استخدام النتائج في سيناريوهات مختلفة. على سبيل المثال ، في النشرات الإخبارية للمقالات الجديدة كل شهر أو أضف على الموقع قسم "قد تكون مهتمًا".


ملخص


في هذه المقالة ، حاولت بالتفصيل ، باستخدام مثال مهمة حقيقية ، لمعرفة كيفية إنشاء نظام توصية يعتمد على التصفية التعاونية.


ميزة هذا النهج هو تعدد الاستخدامات - التوصيات لا تأخذ في الاعتبار الأشياء التي يوصى بها. يمكن استخدام نظام واحد لكل من مقالات المدونة والمنتجات في المتجر عبر الإنترنت.


العيوب تشمل ما يلي:


  • في حالة وجود عدد كبير من الكائنات للتوصيات ، تصبح مصفوفة كائن المستخدم متناثرة ، ويصعب العثور على مستخدمين متشابهين بدرجة كافية (عدد أقل من أزواج كائنات المستخدم)
  • مشكلة بدء التشغيل البارد - يستحيل على مستخدم جديد العثور على مستخدمين مشابهين (هناك استراتيجيات للتحايل على هذا القيد ، لكنهم ليسوا حلا سحريا)
  • يميل النظام القائم على التصفية التعاونية إلى التوصية بالأشياء الشائعة ، لأن الغالبية العظمى من المستخدمين سوف نقدر هذه الأشياء.

في المقالة التالية ، سيتم النظر في مقاربة أخرى - استنادًا إلى تحليل الكائنات نفسها.

Source: https://habr.com/ru/post/ar463609/


All Articles