تحية! اسمي نيكيتا Uchetelev. أنا أمثل لامودا للأبحاث والتطوير. نحن أكثر من 20 شخصًا ، ونعمل على تنفيذ العديد من التوصيات على الموقع وفي التطبيقات ، نقوم بتطوير بحث ، ونحدد فرز البضائع في الفهارس ، ونوفر إمكانية اختبار AB لمختلف الوظائف ، كما ندعم العديد من التطورات الداخلية مثل نظام للتنبؤ بمرونة الطلب وتحسين لوجستية التسليم.

أحد التوجهات الرئيسية لتطوير الشركة بأكملها للسنوات القادمة هو تخصيص منتجاتنا وخدماتنا. يتم اختبار هذه المبادرات وتنفيذها في كل مكان - بدءًا من تجميع اختيارات المنتجات الشخصية إلى اختيار مندوب مبيعات معين يقوم بتسليم بضائعنا إليك. كجزء من عملية تخصيص منتجات R&D ، أعمل كقائد للفريق وأريد في هذه المقالة التحدث عن النظام الأساسي الذي كنت أنا وفريقي قد صممنا ونطوره خلال العام الماضي ، وكذلك عن أول منتجات R&D مخصصة تخضع حاليًا لاختبار AB.
أيديولوجية توصيات المنتج
سمة أي متجر على الإنترنت مألوفة للجميع هي صفحة المنتج. عادةً ما يقدم وصفًا مفصلاً وعدة صور كبيرة وتعليقات العملاء وزر "إضافة إلى السلة" وعناصر التنقل المألوفة الأخرى. يوجد أسفل هذه الصفحات رف أو أكثر مع منتجات أخرى تسمى "المنتجات ذات الصلة" أو "الشراء مع هذا المنتج" أو أي شيء آخر. كل رف له غرضه الخاص.
على سبيل المثال ، تم تصميم رف مع منتجات مماثلة لإعطاء المستخدم مجموعة إضافية من المنتجات في السياق الحالي للاختيار. قد يكون ذلك مفيدًا إذا لم يكن هناك حجم مستخدم في المخزون ، أو إذا كان في مرحلة الاختيار ويريد نفس المنتج ، ولكن "مع أزرار لؤلؤة". في الوقت نفسه ، يأخذ الرف المشتري بعيدا عن صفحة المنتج ، والتي قد لا يعود إليها ، وبالتالي لا يقوم بشرائها.
يوجد رف ثانٍ على الموقع وفي تطبيقات Lamoda ، نسميها رف التوصيات المتقاطعة . إنه موجود مباشرة تحت الرف مع سلع متشابهة ، ونحن نحاول وضعه على سلع متشابهة موجودة في أغلب الأحيان في عربات التسوق جنبًا إلى جنب مع SKU (وحدة حفظ الأوراق المالية الحالية أو المادة ببساطة أكثر). لذلك ، على سبيل المثال ، يوصى باستخدام البنطلونات والأحذية الخاصة بالسترات والأوشحة والقبعات في البلوزات. هناك مجموعة من السلع الرخيصة التي يتم شراؤها في معظم الأحيان. كقاعدة عامة ، هذه جوارب وملابس داخلية ، لذلك يمكن رؤيتها غالبًا على هذا الرف.
هذه التقنية بيع يشبه upale . نحاول بيع بعض السلع التكميلية الكبيرة ، شريطة أن يحب المستخدم المنتج الحالي. في الوقت نفسه ، هذا هو أحد الأماكن القليلة التي يمكن للعملاء التعرف على مجموعتنا بها. على سبيل المثال ، لرؤية علامة تجارية أو فئة فرعية ، لم يعرف وجودها من قبل. نحن نسميها الإلهام والاكتشاف - عندما نلهم العملاء لإجراء عمليات شراء جديدة وإخبارنا بمدى مدى مجموعتنا ، فإننا نعرض الأسعار والخصومات.

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

الفكرة هي : نقوم بتدريب بعض النماذج التي يمكنها تعيين "المستخدم + المنتج" أزواج التحويل أو مجرد نقرة ، ثم عرضها على الرف من اليسار إلى اليمين بالترتيب التنازلي لهذا الاحتمال. نظرًا لأنه لا يتم عرض سوى 4 إلى 6 وحدات SKU على الشاشة الأولى للدائرة ، اعتمادًا على دقة الشاشة ، وبإجمالاً يمكننا حسابها ، على سبيل المثال ، ما يصل إلى مئات ، يتحقق "عمق" مقبول تمامًا من التخصيص.
نحن نحل المشكلة من النهاية
دعنا ننتقل إلى الجزء الفني. لدينا قيود على وقت استجابة API. على سبيل المثال ، في التطبيقات ، يجب أن تكون الخدمة المستقبلية في الوقت المناسب لتكون مسؤولة عن 100 مللي ثانية. خلال هذا الوقت ، تحتاج إلى الانتقال إلى قواعد بيانات مختلفة للمستخدم وبيانات المنتج ، وترتيب مئات الأمثلة تحت تحميل ما يصل إلى 100 QPS في الذروة. هذا يقودنا إلى الحاجة إلى استخدام أطر تعلم الآلة شبه مللي ثانية. واحدة من أشهرها هو Vowpal Wabbit.
أحد مجالات التطبيق النموذجية لهذا الإطار هو adtech ، أي توقع نسبة النقر إلى الظهور لإعلان عند تحسين عرض أسعار لمزاد RTB. من وجهة نظر رياضية ، يمكننا أن نشكل مشكلة مماثلة. لنفترض أننا نريد التنبؤ باحتمال حدوث نقرة عن طريق تدريب نموذج على عروض المنتجات. الحمل على طراز يصل إلى 10 آلاف QPS مشابه لمؤشرات الإعلان ، ويبرر بشكل عام الحاجة إلى قصر المرء فقط على الخوارزميات الخطية في مرحلة النماذج الأولية و MVP.
دعونا الآن نفكر فيما يمكن أن تحتويه بيانات المستخدم على الإشارة التي نحتاجها ونميز بين المستخدمين جيدًا. نظرًا لأننا نتحدث عن توصيات المنتج وصفحة المنتج التي يزورها المستخدم ، فمن المرجح أنه في مرحلة الاختيار. لديه في رأسه صورة معينة من "الأحذية المثالية" التي يقارن بها جميع السلع التي تجذب انتباهه. دعه أولاً يذهب إلى كتالوج البضائع من الفئة المرغوبة وابدأ البحث بالنقر على كل ما يشبه العرض التقديمي إلى حد ما. وبالتالي ، يحتفظ المستخدم ببعض "التتبع الرقمي" للمنتجات التي يتم عرضها. استنادًا إلى الوحدة في هذا المسار ، سنفعل التخصيص.
ناقلات تمثيل الكائنات
تختلف جميع المنتجات فيما بينها مع قيم جدولية لبعض السمات: اللون ، المواد ، الأقمشة ، نوع الطباعة ، طول الكم ، وجود غطاء محرك السيارة ، ارتفاع الكعب ، إلخ. وفقًا لذلك ، من الممكن تشفير أي تتبع رقمي بكسور حدوث كل من قيم هذه السمات. افترض أن لدينا منتجات بثلاثة ألوان وثلاث علامات تجارية يمكنك عرضها ووضعها في السلة. بعد ذلك ، بأخذ محفوظات تصرفات المستخدم ، يمكنه مطابقة متجه من النموذج التالي:

في هذه الحالة ، نظر المستخدم إلى 10 منتجات: 5 ذهب و 2 أسود و 3 أحمر. وأضاف 2 أحمر و 2 أسود إلى السلة ، ولم يضيف الذهب. وبالمثل مع العلامات التجارية A و B و C ، وكذلك مع أي قيم سمة. علاوة على ذلك ، يمكن توصيل هذا المتجه بمتجه مشفر ساخنًا واحدًا لقيم السمة لمنتج معين.
وبالتالي ، يمكننا توجيه حدث معين. المستخدم الذي قام ، في إطار الجلسة الحالية ، بفحص عدد من المنتجات ذات توزيع معين من الألوان ، يستعد لرؤية بعض المنتجات الجديدة ، على سبيل المثال ، الأحمر. باستخدام بيانات تاريخية حول مرات الظهور والنقرات ، يمكنك إنشاء نموذج يتنبأ باحتمالية حدوث نقرة على منتج أحمر ، بشرط أن يكون له توزيع لوني موجود للمنتجات التي سبق عرضها.
إذا نظرت إلى المساحة التي تعلمنا فيها عرض التكتلات التاريخية ، يمكنك أن ترى أن جزءًا ما يشغله حيز فرعي ثنائي والآخر مادي ، ولا يرتبطان ببعضهما البعض. نموذجنا الخطي في هذه الحالة هو مجموعة خطية (مجموع موزون) من إحداثيات النقاط في هذا الفضاء. إذا تعلمت من هذه الأمثلة ، فسوف تتعلم ببساطة الاحتمالات المسبقة. على سبيل المثال ، سيكون الوزن أمام الإحداثي المقابل للون الأحمر للبضائع قيمة متناسبة مباشرة مع نسبة النقر إلى الظهور للبضائع الحمراء. وبالتالي ، سيتم وزن إحداثيات مستخدم معين بخصائص تكرار نقرات المستخدمين المختلفين لأي منتجات من الكتالوج. ولكن هذا ليس هو ما نود.
ميزات متعددة الحدود تنقذ - نتيجة لتكاثر جميع الكميات الثنائية مع كل الكميات الحقيقية. يحتوي إطار Vowpal Wabbit على أداة قوية لتوليد ميزات الطاقة من مساحات الأسماء. دعونا نحاول إنشاء سلسلة بتنسيق vw على سبيل المثال ، نشر ميزات المستخدم والسلع عبر مساحات أسماء مختلفة.
|user_color :0.5 :0.2 :0.3 |product_color
الآن ، إذا أضفنا مفتاح التدريب -q pu أثناء التدريب ، فستظهر هذه الميزات التربيعية غير الصفرية:
user_color^ * product_color^ = 0.5 user_color^ * product_color^ = 0.2 user_color^ * product_color^ = 0.3
وبالتالي ، يبحث النموذج عن إشارة ليس فقط في كيفية قيام المستخدمين بالنقر فوق نسبة عالية من المنتجات الحمراء التي يتم عرضها ، ولكن كيفية النقر على المنتجات الحمراء. يجب أن يكون وزن مثل هذه الميزة في النموذج المدربين إيجابيًا وكبيرًا للغاية.
هذا النهج لميزة الهندسة يزيد بشكل كبير بعد الفضاء الذي يحدث فيه التدريب. في حالة عدم توفر سوى 4 ألوان ، يكون البعد الخاص بهذه المساحة هو 8 (4 ألوان للمنتج و 4 للمستخدم). عند إضافة 16 سمة من الدرجة الثانية ، تزيد إلى 24 سمة. في الإنتاج ، بالإضافة إلى الألوان ، نستخدم 13 سمة أخرى للبضائع ، بما في ذلك ، على سبيل المثال ، العلامة التجارية. لذلك ، يمكن أن يصل البعد الكلي للمساحة التي تعمل بها نماذجنا إلى 3 ملايين ميزة. في الوقت نفسه ، نريد الحفاظ على نسبة عدد أمثلة التدريب إلى بعد المسافة في المستوى 1: 100. للقيام بذلك ، نحتاج إلى إنشاء ما مجموعه حوالي 300 مليون ملاحظة.
نقوم بتخزين clickstream لمستخدمينا في Hadoop (Spark Streaming من Apache Kafka إلى جدول Hive). عادة ما نحصل على حوالي 30 غيغابايت من البيانات المضغوطة يوميًا - وهذا أكثر من مائة نوع مختلف من الإجراءات التي يمكن للمستخدمين تنفيذها على الموقع وفي التطبيقات ، بما في ذلك عرض البضائع في مواضع مختلفة.
أيضًا بالنسبة إلى أرفف التوصيات ، هناك معلومات حول المنتجات التي تم عرضها ومكان النقر عليها. تتمثل مهمتنا لكل نقرة من هذا القبيل في الماضي في حساب حالة المستخدم من حيث نسبة كسور قيم سمات المنتجات التي تم عرضها في الوقت الذي يسبق النقرات المحددة. عندئذٍ ، سيُظهر تسلسل زوج المتجهات "المستخدم" + "المنتج الذي تم النقر عليه" مثالًا تدريبًا إيجابيًا ، وستكون الأزواج المماثلة مع المنتجات المعروضة بجانبه في الرف ولكن دون نقرات أمثلة سلبية. من المهم أن يتم عرض المنتجات في مكان قريب. ثم يمكننا التأكد من أن المستخدم قد شاهدهم ، لكنه لم ينقر. كمكافأة ، مع مثل هذه الميكانيكا يمكننا التحكم في نسبة الطبقات في المشكلة.
يتمثل حلنا في التجميع اليومي لبيانات المستخدم باستخدام Spark والتحميل التدريجي لهذه البيانات في HBase. النظر في هيكل مثل هذا المجموع.
لذلك ، فإن الكائن الرئيسي في هذه المهمة هو المستخدم. ترتبط الجلسات به ، والتي تتكون من سلسلة من الإجراءات ، ولكل منها خصائص معينة حسب نوع الإجراء. على سبيل المثال ، إذا كانت هذه الصفحة تعرض صفحة المنتج ، فإن رقم المقالة ووقت العرض للمنتج ينتمي إلى السمات التي نحتاجها. كاحتياطي للمستقبل ، نكتب على الفور إلى سجل توافر السلع في الأسهم وسعرين: الأساس واحد مع الأخذ في الاعتبار الأسهم والقسائم الشخصية. لا نحتاج إلى مرات ظهور بشكل منفصل ، لذلك أثناء التنقل في وقت التجميع ، تُنسب هذه النقرات إلى النقرات وتولد نوعًا جديدًا من الأحداث التي ، بالإضافة إلى الحقل الذي يحتوي على رقم المقالة للمنتج الذي تم النقر عليه ، توجد أيضًا مجموعة من المقالات العديدة المحيطة به في وقت العرض.
HBase هي قاعدة بيانات أعمدة مُصدرة مع واجهة أصلية للاتصال بـ Spark للمعالجة بالباش ومع إمكانية الوصول إلى البيانات عن طريق المفتاح. ميزة أخرى منه هو أن HBase يفتقر إلى مفهوم الدائرة. يمكن فقط تخزين البايتات ، أو بالأحرى الإزاحة ، داخل HFiles الخاصة التي تم تكييفها مع بنية كتلة HDFS.
قد يجد البعض أنه من المثير للجدل اختيار مستودع ، لكن لدي خبرة جيدة في العمل مع HBase في مشاريع مماثلة. بالإضافة إلى ذلك ، في قاعدة بيانات Lamoda تستخدم بالفعل بنشاط ، لذلك لا يكلفنا شيء استخدام نظام تم نشره بالفعل من أجل MVP. لا نستخدم وظيفة تعيين الإصدار في الوقت الحالي ، لكن الوصول إلى المفاتيح بدا مفيدًا لإمكانية تدريب طراز متعدد مؤشرات الترابط في المستقبل وتنظيم بنية لامدا لتحميل البيانات وغيرها من حالات الوقت الفعلي.
نظرًا لعدم وجود مخطط في HBase ، نحتاج إلى حاوية البيانات الخاصة بنا. يمكنك استخدام lambda x: json.dumps (x) .encode () ، لكنني أردت شيئًا أسرع. الحل القياسي تمامًا هو استخدام حاويات protobuf. نظرًا لأن تطوير المشروع بأكمله يتم تنفيذه في بيثون ، فمن المعتاد بالنسبة لي استخدام مكتبة pyrobuf المخصصة من AppNexus بدلاً من المكتبة الرسمية من Google. حسب المعايير ، أداء الوظائف الأساسية لل protobuffs أعلى عدة مرات من الأصلي. المخطط التقريبي ل protobuff لدينا هو كما يلي:
enum Location { ru = 1; by = 2; ua = 3; kz = 4; special = 5; } enum Platform { desktop = 1; mobile = 2; a_phone = 3; a_tablet = 4; iphone = 5; ipad = 6; } message Action { enum ActionType { pageview = 1; quickview = 2; rec_click = 3; catalog_click = 4; fav_add = 5; cart_add = 6; order_submit = 7; } required uint64 ts = 1; required ActionType action_type = 2; optional string sku = 3; required bool is_office = 4; repeated string skus = 5; optional uint32 delta = 6; optional string sku_source = 7; optional bool stock = 8; optional uint32 base_price = 9; optional uint32 price = 10; optional string type = 11; } message Session { required string session_id = 1; repeated Action actions = 2; required uint64 session_start = 3; required uint64 session_end = 4; optional uint32 actions_count = 5; } message LID { required string uid = 1; repeated Session sessions = 2; required Location location = 3; required Platform platform = 4; optional uint32 sessions_count = 5; }
باختصار ، هناك كائن "مستخدم" (LID ، معرف Lamoda). يوجد بداخلها مجموعة من كائنات "الجلسة" ، كل منها عبارة عن مجموعة من كائنات "الإجراء". قمنا بتقسيم الإجراءات حسب النوع وتخزينها في Column Family المختلفة ، مما يسمح لنا بتحسين القراءة قليلاً عندما نحتاج فقط إلى أحداث من أنواع معينة (طرق عرض المنتج ، والنقرات المنسوبة إلى أنواع مختلفة من التوصيات ، إلخ).
تجريب
لمدة ثلاثة أسابيع أجرينا اختبار AB على موقع سطح المكتب lamoda.ru في التصميم التالي:
- التحكم: تشير توصيات واجهة برمجة التطبيقات إلى خدمة التخصيص ، وهي تنتظر النتيجة ، ولكنها تعطي البضائع بالترتيب الأصلي ، وهي نفسها بالنسبة للجميع.
- اختبار: يتم عرض المنتجات من اليسار إلى اليمين بالترتيب التنازلي لتوقعات احتمال النقر.
يستند التقسيم إلى خيارين على LID للمستخدم - بشكل أساسي بواسطة ملف تعريف الارتباط الخاص به. يضمن النظام الأساسي التجريبي الخاص بنا أن تكون الملاحظات التي تم جمعها في نسختين مستقلة وموزعة بالتساوي ، ويتم تقييم التغييرات المترية بمستوى دلالة قدره 5٪ (قيمة p 0.05). نتيجةً لذلك ، تلقينا + 10٪ من نسبة النقر إلى الظهور (CTR) للجرف بالكامل وتغييرًا إيجابيًا كبيرًا في الإيرادات. في الأسبوع الماضي ، نشرنا هذه الوظيفة لجميع مستخدمي الموقع.
للتحقق من التخصيص على نفسك ، ما عليك سوى النظر إلى العديد من المنتجات المختلفة ثم مقارنة فرز التوصيات على أحد الأرفف "يشترون هذا المنتج" في متصفحين مختلفين أو استخدم وضع "التخفي". بالطبع ، إذا لم تكن قد زرت موقعنا من قبل ، فلا تزال هناك بيانات قليلة عنك في المنصة. حاول اختيار سترة شتوية وقارن ترتيب البضائع مرة أخرى بعد فترة.
لذلك ، حصلنا على نظام أساسي بالكامل - مجموعة من أدوات البرمجيات التي تقوم بتجميع البيانات وتخزينها ، بالإضافة إلى إطار لتوجيه كائنات الأعمال في نقطة تعسفية في وقت ما في الماضي ، مما يسمح لك بإنشاء نماذج تسجيل لتقييم احتمال اتخاذ إجراءات مختلفة. يتم توفير التداخل النموذجي من خلال خدمة ويب يمكنها جمع المتجهات ذات الصلة من مصادر البيانات المختلفة وتشغيلها من خلال النموذج. يقبل LID (معرف المستخدم) ، وقائمة وحدات SKU التي يجب فتحها ، ومعلومات إضافية متنوعة ، وتعيد نفس قائمة المنتجات إلى تنبؤات احتمال النقر المخصب بالتنبؤات. فيما يلي رسم تخطيطي للعمارة المفاهيمية لمنصتنا:

عنصر ML Core هو مجموعة من الأجهزة الافتراضية التي يتم تثبيت عملاء Hadoop وعمال Airflow عليها. نقوم بتعيين التكوين ، مع ما المعلمات لتدريب النموذج ، أين يمكن الحصول على البيانات التاريخية وهلم جرا. نتيجةً لذلك ، يتم تدريب النموذج ونشره بشكل فني ، ويتم تخزين المعلومات حول عملية التدريب ومقاييس الجودة التي تهمنا في مستودع التعريف.
بالفعل اختبار أو إعداد اختبارات نظام تخصيص التوصيات في القوائم البريدية والصفحات الرئيسية وعلى الرف مع توصيات للمنتجات المماثلة وشرائح مماثلة لاستهداف الإعلانات الداخلية والخارجية وأكثر من ذلك بكثير.
كان هذا مقال تمهيدي. في المنشورات الأخرى ، يمكنني التركيز على الجوانب الفنية للعمارة والتحدث عن تطويرها ، أو على العكس من ذلك ، توسيع مكون المنتج على نطاق أوسع وإخبار كيف نستخدم وتقييم ML في مهام المنتج. اكتب التعليقات التي ترغب فيها.