تتضمن أي تجربة مقايضة بين النتائج السريعة وحساسية المقياس. إذا كان المقياس المختار واسعًا من حيث التباين، فيجب علينا الانتظار لفترة طويلة لضمان دقة نتائج التجربة. دعنا نفكر في طريقة واحدة لمساعدة المحللين على تعزيز تجاربهم دون إهدار الكثير من الوقت أو حساسية المقياس.
لنفترض أننا نجري تجربة قياسية لاختبار خوارزمية تصنيف جديدة، مع اعتبار طول الجلسة المقياس الأساسي. بالإضافة إلى ذلك، ضع في اعتبارك أن جمهورنا يمكن تصنيفه تقريبًا إلى ثلاث مجموعات: مليون مراهق، ومليوني مستخدم تتراوح أعمارهم بين 18 و45 عامًا، وثلاثة ملايين مستخدم تتراوح أعمارهم بين 45 عامًا وما فوق. ستختلف الاستجابة لخوارزمية التصنيف الجديدة بشكل كبير بين مجموعات الجمهور هذه. يقلل هذا الاختلاف الواسع من حساسية المقياس.
وبعبارة أخرى، يمكن تقسيم السكان إلى ثلاث طبقات، كما هو موضح فيما يلي:
لنفترض أن كل مكون له توزيع طبيعي. إذن، فإن المقياس الرئيسي للسكان له أيضًا توزيع طبيعي.
نقوم بتقسيم جميع المستخدمين من المجتمع بشكل عشوائي في تصميم تجربة كلاسيكي دون مراعاة الاختلافات بين المستخدمين. وبالتالي، فإننا نعتبر العينة بالقيمة المتوقعة والتباين التاليين.
هناك طريقة أخرى وهي التقسيم العشوائي داخل كل طبقة وفقًا لوزن الطبقة في عموم السكان.
في هذه الحالة، القيمة المتوقعة والتباين هي التالية.
القيمة المتوقعة هي نفسها الموجودة في الاختيار الأول. ومع ذلك، فإن التباين أقل، مما يضمن حساسية مقياسية أعلى.
الآن، دعونا نفكر في طريقة نيمان . يقترحون تقسيم المستخدمين عشوائيًا داخل كل استراتيجية بأوزان محددة.
لذا، فإن القيمة المتوقعة والتباين يساويان ما يلي في هذه الحالة.
القيمة المتوقعة تساوي القيمة المتوقعة في الحالة الأولى بشكل مقارب، إلا أن التباين أقل بكثير.
لقد أثبتنا كفاءة هذه الطريقة نظريًا. فلنقم بمحاكاة العينات واختبار طريقة التقسيم الطبقي تجريبيًا.
دعونا نفكر في ثلاث حالات:
سنطبق جميع الطرق الثلاث في جميع الحالات ونرسم رسمًا بيانيًا ورسمًا بيانيًا صندوقيًا للمقارنة بينها.
أولاً، دعنا ننشئ فئة في Python تحاكي سكاننا العامين المكوّنين من ثلاث استراتيجيات.
class GeneralPopulation: def __init__(self, means: [float], stds: [float], sizes: [int], random_state: int = 15 ): """ Initializes our General Population and saves the given distributions :param means: List of expectations for normal distributions :param stds: List of standard deviations for normal distributions :param sizes: How many objects will be in each strata :param random_state: Parameter fixing randomness. Needed so that when conducting experiment repeatedly with the same input parameters, the results remained the same """ self.strats = [st.norm(mean, std) for mean, std in zip(means, stds)] self._sample(sizes) self.random_state = random_state def _sample(self, sizes): """Creates a general population sample as a mixture of strata :param sizes: List with sample sizes of the corresponding normal distributions """ self.strats_samples = [rv.rvs(size) for rv, size in zip(self.strats, sizes)] self.general_samples = np.hstack(self.strats_samples) self.N = self.general_samples.shape[0] # number of strata self.count_strats = len(sizes) # ratios for every strata in GP self.ws = [size/self.N for size in sizes] # ME and Std for GP self.m = np.mean(self.general_samples) self.sigma = np.std(self.general_samples) # ME and std for all strata self.ms = [np.mean(strat_sample) for strat_sample in self.strats_samples] self.sigmas = [np.std(strat_sample) for strat_sample in self.strats_samples]
بعد ذلك، دعونا نضيف وظائف لطرق أخذ العينات الثلاثة الموضحة في الجزء النظري.
def random_subsampling(self, size): """Creates a random subset of the entire population :param sizes: subsample size """ rc = np.random.choice(self.general_samples, size=size) return rc def proportional_subsampling(self, size): """Creates a subsample with the number of elements, proportional shares of strata :param sizes: subsample size """ self.strats_size_proport = [int(np.floor(size*w)) for w in self.ws] rc = [] for k in range(len(self.strats_size_proport)): rc.append(np.random.choice(self.strats_samples[k], size=self.strats_size_proport[k])) return rc def optimal_subsampling(self, size): """Creates a subsample with the optimal number of elements relative to strata :param sizes: subsample size """ sum_denom = 0 for k in range(self.count_strats): sum_denom += self.ws[k] * self.sigmas[k] self.strats_size_optimal = [int(np.floor((size*w*sigma)/sum_denom)) for w, sigma in zip(self.ws, self.sigmas)] if 0 in self.strats_size_optimal: raise ValueError('Strats size is 0, please change variance of smallest strat!') rc = [] for k in range(len(self.strats_size_optimal)): rc.append(np.random.choice(self.strats_samples[k], size=self.strats_size_optimal[k])) return rc
بالنسبة للجزء التجريبي أيضًا، نحتاج دائمًا إلى دالة لمحاكاة عملية التجربة.
def run_experiments(self, n_sub, subsampling_method, n_experiments=1000): """Conducts a series of experiments and saves the results :param n_sub: size of sample :param subsampling_method: method for creating a subsample :param n_experiments: number of experiment starts """ means_s = [] if(len(self.general_samples)<100): n_sub = 20 if(subsampling_method == 'random_subsampling'): for n in range(n_experiments): rc = self.random_subsampling(n_sub) mean = rc.sum()/len(rc) means_s.append(mean) else: for n in range(n_experiments): if(subsampling_method == 'proportional_subsampling'): rc = self.proportional_subsampling(n_sub) elif(subsampling_method == 'optimal_subsampling'): rc = self.optimal_subsampling(n_sub) strats_mean = [] for k in range(len(rc)): strats_mean.append(sum(rc[k])/len(rc[k])) # Mean for a mixture means_s.append(sum([w_k*mean_k for w_k, mean_k in zip(self.ws, strats_mean)])) return means_s
إذا نظرنا إلى عامة السكان، حيث تحتوي جميع استراتيجياتنا على نفس القيم والتباينات، فمن المتوقع أن تكون نتائج الطرق الثلاث متساوية إلى حد ما.
لقد تم الحصول على نتائج أكثر إثارة من خلال استخدام متوسطات مختلفة وتباينات متساوية. كما أن استخدام التقسيم الطبقي يقلل التباين بشكل كبير.
في الحالات التي يكون فيها المتوسطات متساوية والتباينات مختلفة، نرى انخفاضًا في التباين في طريقة نيمان.
الآن، يمكنك تطبيق طريقة التقسيم الطبقي لتقليل تباين المقاييس وتعزيز التجربة إذا قمت بتجميع جمهورك وتقسيمهم تقنيًا بشكل عشوائي داخل كل مجموعة بأوزان محددة!