![Search icon](https://hackernoon.imgix.net/search-new.png?w=19&h=19)
כל ניסוי כרוך בהחלפה בין תוצאות מהירות לבין רגישות מדדים. אם המדד הנבחר רחב מבחינת השונות, עלינו להמתין זמן רב כדי להבטיח שתוצאות הניסוי מדויקות. הבה נשקול שיטה אחת כדי לעזור לאנליסטים להגביר את הניסויים שלהם מבלי לאבד יותר מדי זמן או רגישות מטרית.
נניח שאנו עורכים ניסוי סטנדרטי כדי לבדוק אלגוריתם דירוג חדש, עם אורך הפעלה כמדד הראשי. בנוסף, קחו בחשבון שניתן לסווג את הקהל שלנו באופן גס לשלוש קבוצות: מיליון בני נוער, 2 מיליון משתמשים בגילאי 18-45 ו-3 מיליון משתמשים בני 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
אם נסתכל על האוכלוסייה הכללית, שבה לכל השכבות שלנו יש אותם ערכים ושונות, התוצאות של כל שלוש השיטות צפויות להיות פחות או יותר שוות.
אמצעים שונים ושונות שוות השיגו תוצאות מרגשות יותר. שימוש בריבוד מפחית באופן דרמטי את השונות.
במקרים בעלי אמצעים שווים ושונות שונות, אנו רואים הפחתת שונות בשיטת ניימן.
כעת, אתה יכול ליישם את שיטת הריבוד כדי להפחית את השונות המטרית ולהגביר את הניסוי אם אתה מקבץ את הקהל שלך ומחלק אותם באופן אקראי בתוך כל אשכול עם משקלים ספציפיים!