أنظمة التوصية2003مبتدئ11 دقيقة قراءة
توصيات Amazon.com: التصفية التعاونية بين المنتجات
Amazon.com Recommendations: Item-to-Item Collaborative Filtering
Linden, G. · Smith, B. · York, J. — IEEE Internet Computing
المشكلة
حين يكون لديك موقع تجارة إلكترونية بعشرات الملايين من العملاء وملايين المنتجات، تحتاج توصيات دقيقة وسريعة في آنٍ واحد. التقليدية — التي تقارن كل مستخدم بكل مستخدم آخر — تتطلب حساباً بتعقيد O(MN) وهو أبطأ بكثير من أن يعمل أثناء تحميل الصفحة. نماذج العنقدة أسرع لكنها تضع عملاء مختلفين في شريحة واحدة فضفاضة فتخرج التوصيات عامة. أما أساليب البحث فتميل إلى إرجاع نتائج سطحية لا تناسب الفرد.
الإسهام
الفكرة: بدلاً من مقارنة المستخدمين ببعضهم، قارن المنتجات ببعضها. يُبنى جدول تشابه بين المنتجات بشكل مسبق (offline) عبر حساب على متجهات الشراء — أي اكتشاف المنتجات التي يشتريها العملاء أنفسهم. عند التوصية، يكفي البحث عن منتجات المستخدم في هذا الجدول وجمع المنتجات المشابهة وترتيبها. الجدول مستقرّ لأن العلاقات بين المنتجات لا تتغيّر بسرعة، والبحث اللحظي يعتمد فقط على عدد مشتريات المستخدم — لا على الحجم الكلي للكتالوج أو قاعدة المستخدمين.
الأثر
هذه الورقة هي التي رسمت شكل أنظمة التوصية في الصناعة. أسلوب أمازون في مقارنة المنتجات ببعضها صار القالب الذي تبنّته مواقع التجارة الإلكترونية في كل مكان، وحازت الورقة جائزة «اختبار الزمن» من IEEE Internet Computing عام 2017. الملاحظة الجوهرية — أن التشابه بين المنتجات أكثر استقراراً وأخفّ حسابياً من التشابه بين المستخدمين — أثّرت مباشرةً على تطوير أساليب ومسابقة Netflix Prize، ولا تزال تمثّل الركيزة الأساسية لأنظمة التوصية الحديثة.
تخيّل مكتبة ضخمة فيها مليون كتاب ومليون زبون. لو أردت أن توصي زبوناً بكتاب جديد، الطريقة المباشرة هي أن تبحث عن أشخاص ذوقهم قريب من ذوقه — تمسح سجلّات كل الزبائن وتختار الأقرب. لكن مع مليون زبون، هذا المسح يستغرق وقتاً هائلاً.
فكرة أمازون مختلفة تماماً، وأشبه بـبطاقة ذكية ملصقة على كل رف: كل كتاب يحمل بطاقة مكتوب عليها «الكتب التي تُشترى عادةً مع هذا الكتاب». حين تلتقط كتاباً، تقرأ البطاقة فوراً فتعرف الكتب المرتبطة به — دون أن تحتاج لمسح سجل أي زبون. تحضير هذه البطاقات يأخذ وقتاً، لكنه يتم مرة واحدة ليلاً، والبطاقات بالكاد تتغيّر لأن الكتب لا تتغيّر — الناس هم مَن يتغيّرون.
التحدي: جودة لحظية على نطاق ضخم
موقع Amazon.com يخدم عشرات الملايين من العملاء ويعرض ملايين المنتجات. أي توصية ناجحة هنا يجب أن تحقق ثلاثة أشياء معاً: أن تقدّم اقتراحات دقيقة ومخصّصة لكل مستخدم، وأن تستجيب لحظياً — في أقل من نصف ثانية — أثناء تصفّح العميل، وأن تتكيّف مع واقع أن بيانات العملاء تتغيّر مع كل نقرة وكل عملية شراء وكل تقييم.
قبل ظهور التصفية التعاونية بين المنتجات، كانت الأساليب الثلاثة المتاحة تفشل في واحد على الأقل من هذه المتطلبات.
الأسلوب الأول: التصفية التعاونية المبنية على المستخدمين
الفكرة التقليدية بسيطة: مثِّل كل عميل بـ في فضاء المنتجات — بُعد واحد لكل منتج، والقيمة إما 1 (اشترى) أو قيمة التقييم. لتوصية العميل «أ»، ابحث عن العملاء الذين تتقارب متجهاتهم مع متجه «أ» باستخدام تشابه جيب التمام أو الارتباط، ثم اقترح المنتجات التي اشتراها هؤلاء ولم يرها «أ» بعد.
المشكلة هنا حسابية بحتة. مع M مستخدم وN منتج، مقارنة كل زوج من المستخدمين تكلّف O(MN) في أسوأ الحالات. صحيح أن يساعد — فمعظم المستخدمين لا يتفاعلون إلا مع عدد ضئيل من المنتجات — لكن المشكلة الحقيقية أن هذا الحساب يجب أن يتم لحظياً، في اللحظة التي يفتح فيها المستخدم الصفحة، لأن ملفات المستخدمين تتغيّر باستمرار. على نطاق أمازون، الاستجابة اللحظية مستحيلة بهذا الأسلوب إلا باختصارات تُضعف الجودة كالعيّنات العشوائية أو .
الأسلوبان الثاني والثالث: العَنقَدة والبحث
نماذج تقسم العملاء إلى شرائح حسب أنماط الشراء، ثم توصي بما هو رائج داخل شريحة كل عميل. أغلب العمل الحسابي يتم مسبقاً (offline)، فيكون الاسترجاع اللحظي سريعاً. لكنّ الجودة محدودة: الشريحة تضم آلاف العملاء المختلفين تحت مظلّة واحدة، فتخرج التوصيات عامة وغير شخصية. وإذا صغّرت الشرائح لتحسين الجودة، صار تصنيف كل مستخدم لحظياً مكلفاً حسابياً.
أساليب البحث تتعامل مع مشتريات المستخدم وتقييماته وكأنها استعلام بحث، وتستخدم تقنيات استرجاع المعلومات لإيجاد منتجات قريبة. الميزة أنها سريعة وتستفيد من بنية البحث الموجودة أصلاً. المشكلة أن النتائج تميل لأن تكون مشابهة جداً لما يملكه المستخدم فعلاً — منتجات شائعة في الفئة نفسها — بدل أن تقدّم اكتشافات جديدة ومخصّصة.
الفكرة المحورية: قارن المنتجات لا المستخدمين
الفكرة المحورية هي تغيير زاوية النظر بالكامل. بدل أن نسأل «أي مستخدمين يشبهون هذا المستخدم؟»، نسأل سؤالاً مختلفاً: «أي منتجات تشبه المنتجات التي أعجبت هذا المستخدم؟»
لماذا هذا التحويل فعّال؟ لأن العلاقات بين المنتجات أكثر استقراراً بكثير من العلاقات بين المستخدمين. سلوك المستخدم قد يتغيّر جذرياً في جلسة واحدة — يشتري هدية لطفله ثم يبحث عن أدوات عمل. لكن العلاقة بين كتابين — «مَن يشتري كتاب بايثون أ عادةً يشتري كتاب بايثون ب» — تتغيّر ببطء شديد. هذا الاستقرار يعني أننا نحسب بين المنتجات مرة واحدة، نخزّنه في جدول، ونعيد استخدام ذلك الجدول لأيام أو أسابيع دون إعادة حساب.
فكّر في الأمر هكذا: كل منتج له بصمة — هي مجموعة العملاء الذين اشتروه. منتجان بصماتهما متداخلة هما منتجان متشابهان. حساب كل هذه التداخلات مكلف، لكننا ننجزه مرة واحدة (offline). بعد ذلك، عند تقديم التوصية، نبحث ببساطة عن منتجات المستخدم في الجدول الجاهز — وهذه عملية سريعة جداً.
الخوارزمية: بناء جدول التشابه
الخوارزمية تتكوّن من مرحلتين: مرحلة offline مكلفة تبني جدول التشابه بين المنتجات، ومرحلة لحظية خفيفة تستخدم هذا الجدول لتوليد التوصيات.
في المرحلة المسبقة (offline)، تمرّ الخوارزمية على كل منتج في الكتالوج. لكل منتج، تجد كل عميل اشتراه، ثم تجمع كل المنتجات الأخرى التي اشتراها هؤلاء العملاء أيضاً. النتيجة: عدّادات شراء مشترك لكل زوج من المنتجات. الخطوة الأخيرة هي حساب تشابه جيب التمام بين متجه كل زوج. الشيفرة البرمجية المبسّطة أبسط مما تتوقع:
مبسَّط لإظهار الفكرة — ليس التنفيذ الحقيقي.
import numpy as np
from collections import defaultdict
def build_similarity_table(purchases):
"""
purchases: قاموس يربط معرّف_العميل -> مجموعة معرّفات_المنتجات
يُعيد: قاموس يربط معرّف_المنتج -> قائمة أزواج (منتج_مشابه، درجة)
"""
# الخطوة 1: اقلب الفهرس — اربط كل منتج بمشتريه
item_to_customers = defaultdict(set)
for customer, items in purchases.items():
for item in items:
item_to_customers[item].add(customer)
# الخطوة 2: لكل زوج من المنتجات، عُدّ المشتريات المشتركة
similarity_table = {}
all_items = list(item_to_customers.keys())
for i, item1 in enumerate(all_items):
buyers1 = item_to_customers[item1]
scores = []
for item2 in all_items:
if item1 == item2:
continue
buyers2 = item_to_customers[item2]
# عدد المشتريات المشتركة = تقاطع مجموعتَي المشترين
overlap = len(buyers1 & buyers2)
if overlap == 0:
continue
# تشابه جيب التمام = التداخل / (|أ| * |ب|)^0.5
cosine = overlap / (len(buyers1) * len(buyers2)) ** 0.5
scores.append((item2, cosine))
# احتفظ بأعلى K منتجاً تشابهاً
scores.sort(key=lambda x: -x[1])
similarity_table[item1] = scores[:20]
return similarity_table # يُحسب مرة واحدة ويُستخدم لأيامقياس التشابه: جيب التمام بين متجهات المنتجات
كل منتج يُمثَّل بـمتجه من M بُعد — بُعد واحد لكل عميل. إذا اشترى العميل المنتج ، توضع القيمة 1 (أو قيمة التقييم)، وإلا فصفر. التشابه بين منتجين هو ببساطة جيب تمام الزاوية بين متجهيهما. الفكرة الحدسية واضحة: إذا كان نفس العملاء يشترون المنتجين معاً، فمتجهاهما يشيران في اتجاه متقارب وتقترب قيمة جيب التمام من 1.
بنية ثنائية المراحل: بناء مسبق واسترجاع لحظي
التصميم ينقسم إلى مكوّنين واضحين. المكوّن المسبق (offline) يبني جدول التشابه بين المنتجات. هذا مكلف حسابياً — في أسوأ الحالات O(N²M) حيث N عدد المنتجات وM عدد العملاء — لكنه يعمل كمهمّة دُفعية ليلاً أو خارج ساعات الذروة. ولأن العلاقات بين المنتجات مستقرة، يبقى الجدول صالحاً لأيام.
المكوّن اللحظي (online) يولّد التوصيات لمستخدم محدد في الوقت الفعلي. يأخذ المنتجات التي اشتراها المستخدم أو قيّمها مؤخراً، يبحث عن كل واحد منها في الجدول الجاهز، يجمع المنتجات المشابهة، يحذف ما يملكه المستخدم أصلاً، ويرتّب الباقي. الكلفة الحسابية تعتمد فقط على عدد منتجات المستخدم — عادةً عشرات إلى مئات قليلة — لا على حجم الكتالوج الكلي ولا على عدد المستخدمين. لذلك تبقى سريعة بما يكفي مع كل تحميل صفحة.
لماذا يتوسّع: ميزة التناثر
تعقيد بناء الجدول نظرياً هو O(N²M) وهو يبدو ضخماً. لكن عملياً، التي تربط المستخدمين بالمنتجات متناثرة (sparse) للغاية — العميل العادي يشتري جزءاً ضئيلاً جداً من الكتالوج. هذا يعني أن معظم أزواج المنتجات لا يتشاركان أي عميل فنتجاوزها بالكامل، والحساب الفعلي أقرب بكثير إلى O(NM) أو أقل.
النقطة الأهم: هذا الحساب المكلف يتم مسبقاً (offline). أما خطوة التوصية اللحظية فتعقيدها يعتمد فقط على عدد المنتجات التي تفاعل معها المستخدم، لا على الحجم الكلي للكتالوج أو عدد المستخدمين. حتى على نطاق أمازون بعشرات الملايين من المستخدمين، تنتهي الخطوة اللحظية في أجزاء من الألف من الثانية.
توليد التوصيات: من الجدول إلى القائمة
بعد أن يصبح جدول التشابه جاهزاً، توليد التوصيات لأي مستخدم يتبع خطوات مباشرة. لكل منتج في سجل مشترياته أو تقييماته: ابحث عن المنتجات المشابهة له في الجدول، واجمع الدرجات — فإذا ظهر منتج ما مشابهاً لأكثر من منتج اشتراه المستخدم، تتراكم درجاته ويصعد في الترتيب. بعد ذلك، احذف المنتجات التي يملكها المستخدم أصلاً، ورتّب الباقي حسب الدرجة المجمّعة، واعرض الأعلى.
هذا الأسلوب يتعامل تلقائياً مع حالة «العملاء الذين اشتروا هذا اشتروا أيضاً…»: حين يتصفّح المستخدم صفحة منتج معيّن، يبحث النظام عن ذلك المنتج في جدول التشابه ويعرض أقرب المنتجات إليه. هنا لا نحتاج حتى إلى سجل المستخدم — المنتج وحده يكفي.
مبسَّط لإظهار الفكرة — ليس التنفيذ الحقيقي.
def recommend(user_items, similarity_table, n=10):
"""
user_items: مجموعة معرّفات المنتجات التي اشتراها/قيّمها المستخدم
similarity_table: جدول محسوب مسبقاً {منتج -> [(منتج_مشابه، درجة)]}
يُعيد: أفضل n منتج موصى به
"""
scores = {}
for item in user_items:
if item not in similarity_table:
continue
for similar_item, sim_score in similarity_table[item]:
if similar_item in user_items:
continue # تجاوز ما يملكه المستخدم فعلاً
scores[similar_item] = scores.get(similar_item, 0) + sim_score
# رتّب حسب مجموع درجات التشابه
ranked = sorted(scores.items(), key=lambda x: -x[1])
return [item_id for item_id, _ in ranked[:n]]
# يعمل بتعقيد O(k * s) حيث k = منتجات المستخدم، s = المنتجات المشابهة لكل مُدخَل
# في أمازون: k ≈ 10–100, s ≈ 20 → بضعة آلاف عملية لكل طلبالنتائج: مقارنة الجودة والقابلية للتوسّع
اختبرت أمازون التصفية التعاونية بين المنتجات مقابل الأساليب الثلاثة السابقة على بيانات عملاء حقيقية. النتائج أظهرت أنها تتفوّق على العَنقَدة وأساليب البحث في جودة التوصيات، وتضاهي أو تتجاوز التصفية المبنية على المستخدمين — مع فارق هائل في سرعة التقديم.
أبرز النتائج: جودة مكافئة للتصفية المبنية على المستخدمين، وحساب لحظي مستقل تماماً عن حجم الكتالوج وعدد المستخدمين. جدول التشابه لا يحتاج تحديثاً إلا دورياً لأن العلاقات بين المنتجات مستقرة. والأسلوب يخدم تلقائياً حالتَي الاستخدام الشائعتين — «توصيات لك» و«منتجات مرتبطة» — ببنية بيانية واحدة.
الأثر: من أمازون إلى العالم
1994
GroupLens
من أوائل أنظمة التصفية التعاونية المبنية على المستخدمين، صُمّم لتصفية أخبار Usenet. أثبت أن الفكرة تعمل، لكنه لم يستطع التوسّع لأعداد كبيرة من المستخدمين.
1998
أمازون تُطلق أسلوب التشابه بين المنتجات
أمازون تنشر الخوارزمية فعلياً في بيئة الإنتاج — قبل ست سنوات من نشر الورقة البحثية. النظام يخدم ملايين المستخدمين والمنتجات.
2003
نُشرت الورقة في IEEE Internet Computing
لندن وسميث ويورك ينشرون الورقة. صارت من أكثر الأوراق استشهاداً في أبحاث أنظمة التوصية.
2006
الإعلان عن مسابقة Netflix Prize
Netflix تعرض مليون دولار لمن يحسّن نظام توصياتها بنسبة 10%. المسابقة أشاعت أساليب تحليل المصفوفات — وهي امتداد طبيعي لفكرة التشابه بين المنتجات نحو نماذج العوامل الكامنة.
2009
الفوز بمسابقة Netflix Prize
الحل الفائز جمع بين تحليل المصفوفات والأساليب المبنية على الجوار — المتحدّرة من فكرة التصفية بين المنتجات — مما أكّد فاعلية الأسلوبين معاً.
2016
نظام التوصية العميق في YouTube
YouTube يستبدل السمات اليدوية بشبكات عصبية عميقة، لكنّ البنية ثنائية المراحل — توليد المرشّحين مسبقاً ثم الترتيب لحظياً — تعكس نفس فلسفة تصميم أمازون.
2017
جائزة «اختبار الزمن»
IEEE Internet Computing تمنح الورقة جائزة اختبار الزمن بمناسبة مرور عشرين عاماً، تقديراً لتأثيرها المستمر على أنظمة التوصية في الصناعة.
تأثير هذه الورقة يتجاوز أمازون بكثير. النمط ثنائي المراحل الذي أرسته — حساب مسبق مكلف للعلاقات (offline) يتبعه استرجاع لحظي خفيف — نجده اليوم في توليد المرشّحين في YouTube، واكتشاف الموسيقى في Spotify، وتقريباً في كل واسع النطاق. أساليب تحليل المصفوفات عمّمت فكرة التشابه بين المنتجات إلى مُتعلَّمة، ونظام التوصية العميق في YouTube استبدل التشابه اليدوي بـ عصبية — لكنّ النمط المعماري في جوهره لا يزال نمط أمازون.
المرجعLinden, Smith, York. Amazon.com Recommendations: Item-to-Item Collaborative Filtering. IEEE Internet Computing, 2003.
مصطلحات هذه الورقة
- التصفية التعاونيةCollaborative Filtering
- نظام التوصيةRecommender System
- تشابه جيب التمامCosine Similarity
- درجة التشابهSimilarity
- التغذية الراجعة الضمنيةImplicit Feedback
- التغذية الراجعة الصريحةExplicit Feedback
- تحليل المصفوفاتMatrix Factorization
- التصفية القائمة على المحتوىContent-Based Filtering
- العوامل الكامنةLatent Factors