حساب أكل لحوم البشر على أساس اختبار A / B الكلاسيكية وطريقة التمهيد

تتناول هذه المقالة طريقة لحساب أكل لحوم البشر لتطبيق الهاتف المحمول على أساس اختبار A / B الكلاسيكي. في هذه الحالة ، يتم النظر في الإجراءات المستهدفة وتقييمها كجزء من عملية إعادة الإسناد من مصدر إعلان (Direct ، Criteo ، AdWords UAC وغيرها) مقارنةً بالإجراءات المستهدفة في المجموعة التي تم تعطيل الإعلان عليها.

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

الأدلة غامضة أو غير مقدمة من أجل الحفاظ على اتفاقية عدم الكشف.

في المستقبل ، أخطط لاستكمال هذه المقالة وتعديلها قليلاً كما تظهر حقائق جديدة ، لذلك يمكن اعتبار هذا الإصدار هو الإصدار الأول. سأكون ممتنا للتعليقات والمراجعات.

مقدمة


التفكيك هو عملية تدفق حركة المرور ، كاملة ومستهدفة ، من قناة إلى أخرى.

عادةً ما يستخدم المسوقون هذا المؤشر كمعامل K إضافي في حساب تكلفة الاكتساب: يتم ضرب سعر الاكتساب المحسوب بمقدار 1 + K. في هذه الحالة ، تعني تكلفة الاكتساب التكلفة الإجمالية لجذب عدد الزيارات / عدد الإجراءات المستهدفة التي يتم تسييلها مباشرةً ، أي التي جلبت الربح الفعلي - على سبيل المثال ، مكالمة مستهدفة ، و / أو تم استثمارها بشكل غير مباشر - على سبيل المثال ، زيادة حجم قاعدة بيانات الإعلانات ، وزيادة عدد الجمهور وما إلى ذلك.

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

في حالتنا ، كانت المهمة هي حساب تفكيك التحولات "العضوية" إلى التطبيق عن طريق التحولات من شبكة Criteo الإعلانية. المراقبة هي جهاز أو مستخدم مستخدم (GAID / ADVID و IDFA).

إعداد التجربة


يمكنك إعداد الجمهور للتجربة عن طريق تقسيم المستخدمين في واجهة نظام AdJust التحليلي إلى مجموعات لعزل أولئك الذين سيشاهدون الإعلانات من شبكة إعلانية معينة (نموذج التحكم) وأولئك الذين لن يتم عرض إعلانات باستخدام GAID أو ADVID و IDFA ، على التوالي (يوفر AdJust واجهة برمجة تطبيقات الجمهور - منشئ). بعد ذلك ، في نموذج التحكم ، يمكنك تضمين حملة إعلانية في الشبكة الإعلانية التي تمت دراستها في التجربة.

ألاحظ من نفسي ، كما يبدو بشكل حدسي ، أن التطبيق التالي للتجربة سيكون أكثر كفاءة في هذه الحالة: لاختيار أربع مجموعات - أولئك الذين أعادوا تعطيل تعطيل من جميع القنوات (1) ، كمجموعة تجريبية ، وأولئك الذين لديهم إعادة تمكين فقط تمكين مع Criteo (2) ؛ أولئك الذين قاموا بإعادة تعيين الاستهداف مع Criteo (3) ، تم تشغيل أولئك الذين قاموا بإعادة الاستهداف (4). بعد ذلك ، سيكون من الممكن حساب (1) / (2) ، بعد تلقي القيمة الفعلية لإلغاء تفكيك الحملات الإعلانية لشبكة Criteo للانتقالات "العضوية" إلى التطبيق ، و (3) / (4) ، بعد تلقي Criteo لتفكيك في البيئة "الطبيعية" (بعد كل شيء ، Criteo ، من الواضح يمكن تفكيك القنوات المدفوعة الأخرى كذلك). يجب تكرار التجربة نفسها لشبكات الإعلانات الأخرى لمعرفة تأثير كل منها ؛ في عالم مثالي ، سيكون من الجيد البحث في عملية تفكيك لحوم البشر بين جميع المصادر المدفوعة الرئيسية التي تشكل الحصة الأكبر من إجمالي عدد الزيارات ، لكن الأمر سيستغرق الكثير من الوقت (سواء لإعداد تجارب من وجهة نظر التطوير أو لتقييم النتائج) ، مما قد يتسبب في انتقادات لدقيقة غير معقولة.

في الواقع ، تم إجراء تجربتنا في ظل الظروف (3) و (4) ، تم تقسيم العينات بنسبة 10 ٪ إلى 90 ٪ ، وأجريت التجربة لمدة 2 أسابيع.

إعداد البيانات والتحقق منها


قبل البدء في أي دراسة ، هناك خطوة مهمة هي التدريب المسبق وتنظيف البيانات.

تجدر الإشارة إلى أنه في الواقع كانت الأجهزة النشطة لفترة التجربة أقل بمرتين (42.5٪ و 50٪ من المجموعات الضابطة والتجريبية ، على التوالي) من الأجهزة الموجودة في العينات الأولية الكاملة ، والتي يتم شرحها بواسطة طبيعة البيانات:

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

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

في حالتنا ، من المهم التحقق من النقاط التالية:

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

التقييم المباشر للتجربة


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

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

1. دراسة توزيع القيم في عينات الحياة الطبيعية


تتمثل الخطوة الأولى في فحص العينات الموجودة لنوع توزيع القيم والمساواة بين الفروق في العينات باستخدام الاختبارات القياسية - معايير Kolmogorov-Smirnov و Shapiro-Wilks واختبار Bartlett المطبق في مكتبة sklearn.stats ، مع مراعاة القيمة p = 0.05:

#    : def norm_test(df, pvalue = 0.05, test_name = 'kstest'): if test_name == 'kstest': st = stats.kstest(df, 'norm') if test_name == 'shapiro': st = stats.shapiro(df) sys.stdout.write('According to {} {} is {}normal\n'.format(test_name, df.name, {True:'NOT ', False:''}[st[1] < pvalue])) #    : def barlett_test(df1, df2, pvalue = 0.05): st = stats.bartlett(df1, df2) sys.stdout.write('Variances of {} and {} is {}equals\n'.format(df1.name, df2.name, {True:'NOT ', False:''}[st[1] < pvalue])) 

بالإضافة إلى ذلك ، لإجراء تقييم مرئي للنتائج ، يمكنك استخدام وظيفة الرسم البياني.

 data_agg = data.groupby(['bucket']).aggregate({'device_id': 'nunique', 'calls': 'sum'}).fillna(0) data_conv = data_agg['calls_auto']/data_agg['device_id'] data_conv.hist(bins=20) 

صورة

يمكنك قراءة الرسم البياني مثل هذا: 10 مرات في العينة كان هناك تحويل 0.08 ، 1 - 0.14. هذا لا يقول أي شيء عن عدد الأجهزة كملاحظات على أي من مؤشرات التحويل.

في حالتنا ، فإن توزيع قيمة المعلمة بالقيمة المطلقة والنسبية (عدد المكالمات للجهاز) في العينات غير طبيعي.
في هذه الحالة ، يمكنك استخدام اختبار Wilcoxon nonparametric الذي تم تنفيذه في مكتبة sklearn.stats القياسية ، أو محاولة نقل توزيع القيم في العينات إلى النموذج العادي وتطبيق أحد معايير حدودي - اختبار التلميذ أو اختبار الطالب Shapiro-Wilks.

2. طرق تقليل توزيع القيم في العينات إلى الشكل العادي


2.1. دلاء الفرعية

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

 #   subbucket' data['subbucket'] = data['device_id'].apply(lambda x: randint(0,1000)) # Variant 1 data['subbucket'] = data['device_id'].apply(lambda x: hash(x)%1000) # Variant 2 

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

ومع ذلك ، في الممارسة العملية ، من عدة عمليات إطلاق للرموز ، تلقينا التوزيع الطبيعي مرة واحدة فقط ، أي أن هذه الطريقة غير مضمونة ولا مستقرة.

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

 data[data['calls'] > 0].device_id.nunique()/data.device_id.nunique() # Total buckets = data.groupby(['bucket']).aggregate({'device_id': 'nunique', 'calls': 'sum'}) buckets[buckets['calls'] > 0].device_id.nunique()/buckets.device_id.nunique() # Buckets subbuckets = data.groupby(['subbucket']).aggregate({'device_id': 'nunique', 'calls': 'sum'}) subbuckets[subbuckets['calls'] > 0].device_id.nunique()/subbuckets.device_id.nunique() # Subbuckets 

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

 def class_arrays_balancer(df1, df2, target = 'calls', pvalue=0.05): df1_target_size = len(df1[df1[target] > 0]) print(df1.columns.to_list()) df2_target_size = len(df2[df2[target] > 0]) total_target_size = df1_target_size + df2_target_size chi2_target, pvalue_target, dof_target, expected_target = chi2_contingency([[df1_target_size, total_target_size], [df2_target_size, total_target_size]]) df1_other_size = len(df1[df1[target] == 0]) df2_other_size = len(df1[df1[target] == 0]) total_other_size = df1_other_size + df2_other_size chi2_other, pvalue_other, dof_other, expected_other = chi2_contingency([[df1_other_size, total_other_size], [df2_other_size, total_other_size]]) df1_target, df2_target, df1_other, df2_other = None, None, None, None if pvalue_target < pvalue: sample_size = min([df1_target_size, df2_target_size]) df1_rnd_indx = np.random.choice(df1_target_size, size=sample_size, replace=False) df2_rnd_indx = np.random.choice(df2_target_size, size=sample_size, replace=False) df1_target = pd.DataFrame((np.asarray(df1[df1[target] == 1])[df1_rnd_indx]).tolist(), columns = df1.columns.tolist()) df2_target = pd.DataFrame((np.asarray(df2[df2[target] == 1])[df2_rnd_indx]).tolist(), columns = df2.columns.tolist()) if p_value_other < pvalue: sample_size = min([df1_other_size, df2_other_size]) df1_rnd_indx = np.random.choice(df1_other_size, size=sample_size, replace=False) df2_rnd_indx = np.random.choice(df2_other_size, size=sample_size, replace=False) df1_other = pd.DataFrame((np.asarray(df1[df1[target] == 0])[df1_rnd_indx]).tolist(), columns = df1.columns.tolist()) df2_other = pd.DataFrame((np.asarray(df2[df2[target] == 0])[df2_rnd_indx]).tolist(), columns = df2.columns.tolist()) df1 = pd.concat([df1_target, df1_other]) df2 = pd.concat([df2_target, df2_other]) return df1, df2 exp_classes, control_classes = class_arrays_balancer(data_exp, data_control) 

في المخرجات ، نحصل على بيانات متوازنة في الحجم ومتسقة مع نسب التحويل الأولية ، المقاييس المدروسة (محسوبة لمتوسط ​​قيم المجموعة الفرعية) التي يتم توزيعها بالفعل بشكل طبيعي ، والتي يمكن رؤيتها بصريًا ونتائج تطبيق معايير الاختبار المعروفة بالفعل لدينا الحالة الطبيعية (مع قيمة p> = 0.05). على سبيل المثال ، بالنسبة للمؤشرات النسبية:

 data_conv = (data[data['calls'] > 0].groupby(['subbucket']).calls.sum()*1.0/data.groupby(['subbucket']).device_id.nunique()) data_conv.hist(bins = 50) 

الآن ، يمكن تطبيق اختبار t في المتوسط ​​على مدى الجرافات الفرعية (وبالتالي ، فهو ليس معرف الجهاز ، وليس جهازًا ، ولكن الجرافة الفرعية تعمل كملاحظة).

بعد التأكد من أن التغييرات ذات دلالة إحصائية ، يمكننا ، بضمير حي ، القيام بما بدأناه جميعًا - حساب أكل لحوم البشر:

 (data_exp.groupby(['subbucket']).calls.avg() - data_cntrl.groupby(['subbucket']).calls.avg() )/ data_exp.groupby(['subbucket']).calls.avg() 

يجب أن يكون المقام هو حركة المرور بدون إعلانات ، أي التجريبية.

3. طريقة التمهيد


طريقة bootstrap هي امتداد لطريقة الجرافة الفرعية وتمثل نسختها الأكثر تطوراً وتحسينًا ؛ يمكن العثور على تطبيق برنامج لهذه الطريقة في Python في مكتبة Facebook Bootstrapped.
باختصار ، يمكن وصف فكرة bootstrap على النحو التالي: الطريقة ليست أكثر من مُنشئ للعينات التي تم إنشاؤها بطريقة مشابهة لطرق المجموعة الفرعية بشكل عشوائي ، ولكن مع التكرار المحتمل. يمكننا أن نقول التنسيب من عامة السكان (إذا كان يمكن للمرء أن استدعاء العينة الأصلية) مع عودة. في المخرجات ، يتم تشكيل المتوسطات (أو المتوسطات ، المبالغ ، إلخ) من المتوسطات لكل من العينات الفرعية التي تم إنشاؤها.

الطرق الرئيسية لمكتبة FaceBook Bootstrap :
 bootstrap() 
- تنفذ آلية لتشكيل العينات الفرعية ؛ إرجاع الحد الأدنى (5 مئوية) والحد الأعلى (95 مئوية) افتراضيًا ؛ لإرجاع توزيع منفصل في هذا النطاق ، من الضروري تعيين المعلمة return_distribution = True (يتم إنشاؤه بواسطة دالة المساعد gener_distributions () ).

يمكنك تحديد عدد التكرارات باستخدام المعلمة num_iterations ، التي سيتم فيها إنشاء عينات فرعية ، وعدد الأمثلة التكرارية_الحجمية لكل تكرار. عند إخراج gener_distributions () ، سيتم إنشاء عينة بحجم يساوي عدد التكرارات num_iterations ، والتي ستكون عناصرها هي متوسط ​​قيم itكرار_batch_size العينات المحسوبة في كل تكرار. مع أحجام العينات الكبيرة ، قد لا تتلاءم البيانات مع الذاكرة ، لذا ينصح بتقليل قيمة iteration_batch_size في مثل هذه الحالات.

مثال : اجعل العينة الأصلية 2،000،000 ؛ num_iterations = 10،000 ، iteration_batch_size = 300. ثم ، في كل من التكرارات 10،000 ، سيتم تخزين 300 قائمة من 2،000،000 عنصر في الذاكرة.

تسمح الوظيفة أيضًا بالحوسبة المتوازية على عدة نوى للمعالجات ، وعلى عدة مؤشرات ترابط ، وتعيين الرقم المطلوب باستخدام المعلمة num_threads .

 bootstrap_ab() 

ينفذ جميع الإجراءات نفسها التي تقوم بها وظيفة bootstrap () الموصوفة أعلاه ، ومع ذلك ، بالإضافة إلى ذلك ، يتم تجميع القيم المتوسطة أيضًا بالطريقة المحددة في stat_func - من قيم num_iterations . بعد ذلك ، يتم حساب القياس المحدد في المعلمة compar_func ، ويتم تقدير الأهمية الإحصائية.

 compare_functions 

- فئة من الوظائف التي توفر أدوات لتشكيل مقاييس للتقييم:
 compare_functions.difference() compare_functions.percent_change() compare_functions.ratio() compare_functions.percent_difference() # difference = (test_stat - ctrl_stat) # percent_change = (test_stat - ctrl_stat) * 100.0 / ctrl_stat # ratio = test_stat / ctrl_stat # percent_difference = (test_stat - ctrl_stat) / ((test_stat + ctrl_stat) / 2.0) * 100.0 

 stats_functions 
- فئة من الوظائف يتم منها اختيار طريقة تجميع المقياس المدروس:
 stats_functions.mean stats_functions.sum stats_functions.median stats_functions.std 

ك stat_func ، يمكنك أيضًا استخدام دالة مخصصة معرفة من قبل المستخدم ، على سبيل المثال:

 def test_func(test_stat, ctrl_stat): return (test_stat - ctrl_stat)/test_stat bs.bootstrap_ab(test.values, control.values, stats_functions.mean, test_func, num_iterations=5000, alpha=0.05, iteration_batch_size=100, scale_test_by=1, num_threads=4) 

في الواقع ، (test_stat - ctrl_stat) / test_stat هي الصيغة لحساب تفكيكنا .

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



يمكن العثور على مزيد من الوثائق المفصلة على صفحة المستودع .

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

كما أود أن أشكر زملائي على مساعدتهم في إعداد هذا العمل. إذا كانت المقالة تتلقى ردود فعل إيجابية في الغالب ، فسأشير هنا إلى أسمائهم أو ألقابهم (بالاتفاق المسبق).

تمنياتي للجميع! :)

ملحوظة: عزيزي بطولة البطولة ، مهمة تقييم نتائج اختبار A / B هي واحدة من أهمها في Data Science ، لأنه لم يكتمل إطلاق نموذج ML جديد جديد في الإنتاج بدون A / B. ربما حان الوقت لتنظيم مسابقة لتطوير نظام لتقييم نتائج اختبار A / B؟ :)

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


All Articles