AB測試實戰
學習目标
- 掌握如何設計AB測試
- AB測試效果如何解讀
- 掌握如何進行實驗設計與效果計算
1、AB測試介紹
-
很多網站/APP的首頁都會挂一張頭圖(Banner),用來展示重要資訊,頭圖是否吸引人會對公司的營收帶來重大影響,一家壽險公司Humana設計了如下三張頭圖,現在需要決定使用哪一張放到首頁,咋辦?開會讨論?舉手表決?上司拍闆?
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-OZpmgsin-1632058811314)(img\abtest5.png)]
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-acwWpnYG-1632058811315)(img\abtest6.png)]
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-Z3BnuIAe-1632058811315)(img\abtest7.png)]
- AB測試:以“對照”知“優劣”
- AB測試主要應用于網際網路行業,它通過對相同特征的網際網路使用者(流量)在同一時間内實施兩種不同的政策。來觀察每組流量在關鍵目标名額上的表現。進而找出表現更優的政策。并憑借實驗結果對政策和人群展開交叉分析,得到洞察
- 無論是添加功能,頭圖替換,還是添加新的按鈕,使用了新的推薦算法,所有可能會對業務造成影響的主動調整最好都要經過AB測試
- 在上面的Humana公司的例子中,頭圖的疊代就是使用了AB測試
- 第一張圖是最初的版本,第二張設計圖在第一張的基礎上,減少了文字描述,換了更大的按鈕,
- 在第二張圖檔的基礎上又做了新的疊代,設計了第三張圖檔,主要的變化調整了按鈕的;顔色和文字内容,添加了Shop這樣帶有明顯銷售傾向的詞語
- 兩次疊代的決定都是基于AB測試的結果,經過AB測試驗證後上線都得到了很好的效果
1.1 AB測試的關鍵點:目标KPI和政策
- 目标KPI:指評判AB測試效果優劣的最終名額
- 在Humana公司首頁頭圖調整的例子中,目标KPI是該頭圖的點選率
- 政策:AB組分别采取的政策的差異點
- 如:注冊按鈕采用的顔色,位置,注冊頁面采用的海報,智語等
- 有多少個差異點,就可以設計多少次AB測試,直到窮盡政策差異點後找出最有效的那一組政策。
- 隻要分流量的時候做到随機,那麼AB組人群的特征就是類似的,是以最終結果的差異隻能是政策不同造成的。
1.2 AB測試的作用
- 實作目标KPI的最大化:通過在小樣本使用者中進行實驗(避免錯誤決策影響使用者體驗),找到對KPI最優的政策
- Humana公司的頭圖調整例子中,第二張圖檔對比第一張帶來了433%的點選率提升
- 第三張圖檔對比第二張有帶來了192%的點選率提升
- 後續分析,沉澱訣竅:
- 每組流量都是随機配置設定的,是以每組人群中都包含了各種不同特征的子人群
- 通過研究不同子人群對于不同政策的響應程度,可以獲得每組人群在政策上的偏好,幫助未來更好的個性化創新和設計
- 從上面的例子中,我們可以得出一些有意的結論,
- 如頭圖文字不易過多,要主題突出, 文字簡潔
- 頭圖使用的按鈕顔色, 紅色更容易吸引人點選等
1.3 AB測試實施的一般步驟
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-avLnFoCV-1632058811316)(img\ab_flow.png)]
嚴謹的産品疊代過程(政策,算法, 界面調整, 功能調整), 一定要先經過AB測試, 在少部分流量上進行測試, 沒問題了再逐漸放量
- 明确KPI 提升轉化率, 提升點選率, 提升分享率
- 圈人, 通過假設檢驗确定足夠的實驗人數
- 通過目标KPI提升多少, 确定實驗人數
- AA 測試 : 确定圈人沒圈跑偏(量組流量的組成類似)
- 實驗組和對照組在AA測試後, 觀察目标KPI 沒有太大的差別
- AB測試: 對照組看老産品, 給測試組看新的産品
- 回收結果,分析
- 如果B組确實比A組好, 并且達到了圈人時設定的目标KPI 說明新設計有效的
- 如果B組确實比A組好, 但是提升幅度有限, 沒有達到圈人時設定的目标KPI 的提升度, 說明這個提升沒有說服力, 不能證明新的設計有效
- A組比B組好, 新的設計不行
2、AB測試常見問題和應對方案
2.1 如何配置設定流量
一般采用A組B組人數相等的方法進行流量配置設定
常用的分流方法
- 利用随機數排序,例如My SQL中的 rand()函數
select a.*,case when rand()<0.1 then 'ctrl'
when rand() between 0.1 and 0.55 then 'test1' else 'test2' end as ab_group_tag
from (select distinct customerID from user_table) a
order by ab_group_tag;
- 利用某些随機ID的尾數,例如case when ID like ‘%1’then ‘Ctrl’else ‘Test’end as AB_group
select a.*,case when customerID like '1%' then 'ctrl'
when customerID like '2%' or customerID like '3%' or customerID like '4%' or customerID like '5%' then 'test1'
else 'test2' end as ab_group_tag
from(select distinct customerID from user_table) a
order by ab_group_tag;
- 不管用哪種方法都要在人群身上留好标簽以便事後分析
2.2 确定試驗有效的最小參與人數
- AB測試的效果會一定程度上受到随機波動的影響
- 完全随機的兩組人不發優惠券,觀察同一段時間的人均交易數,人均交易額這些KPI,都不會完全一樣
- 為了使測試結果顯著有效,我們首先要確定測試組裡人數最少的一組達到驗證效果有效性的最小樣本數量
- 幫助計算樣本量的網站:https://www.evanmiller.org/ab-testing/sample-size.html
- 首先介紹比例類的KPI,例如領券率,點選率,核銷率的樣本量計算方式:我們以基準率10%,需要探知最低效果2%為例:
- 上圖中 Baseline conversion rate: 這裡填寫基準比率 10%
- Minimum Detectable Effect:這裡填寫最低的可探知效果2%
- Significance level α:顯著性水準 一般選擇5%
- Statistical power 1−β:統計功效(statistical power )一般選擇80%
- 經過計算得知,我們想通過AB測試确定新的政策能夠将KPI提升兩個點參數實驗的每組人數最少為3623人
2.3 AB測試中的假設檢驗
為什麼要計算參與AB測試的最少參與人數?
- 我們做AB測試的目的是在盡量不影響使用者體驗的前提下,讓少部分使用者來驗證新舊方案的優劣。這裡就需要對使用者進行抽樣, 也就是抽取部分使用者來代表全體使用者來參與實驗,并得出結論
- 如果抽取的使用者數量過少,不能代表所有使用者的觀點,結果沒有意義
- 如果抽取的使用者過多,一旦我們的新方案與預期效果偏差較大則會對使用者體驗帶來較大影響
- 我們可以使用假設檢驗的理論幫助我們計算參與實驗的最少人數
什麼是假設檢驗?
假設檢驗(hypothesis testing),又稱統計假設檢驗,是用來判斷樣本與樣本、樣本與總體的差異是由抽樣誤差引起還是本質差别造成的統計推斷方法。顯著性檢驗是假設檢驗中最常用的一種方法,也是一種最基本的統計推斷形式,其基本原理是先對總體的特征做出某種假設,然後通過抽樣研究的統計推理,對此假設應該被拒絕還是接受做出推斷。
假設檢驗的基本思想
- 假設檢驗的基本思想是“小機率事件”原理,其統計推斷方法是帶有某種機率性質的反證法。
- 小機率思想是指小機率事件在一次試驗中基本上不會發生。反證法思想是先提出檢驗假設,再用适當的統計方法,利用小機率原理,确定假設是否成立
- 即為了檢驗一個假設 H 0 H_0 H0是否正确,首先假定該假設 H 0 H_0 H0正确,然後根據樣本對假設 H 0 H_0 H0做出接受或拒絕的決策
- 如果樣本觀察值導緻了“小機率事件”發生,就應拒絕假設 H 0 H_0 H0,否則應接受假設 H 0 H_0 H0。
- 假設檢驗中所謂“小機率事件”,并非邏輯中的絕對沖突,而是基于人們在實踐中廣泛采用的原則,即小機率事件在一次試驗中是幾乎不發生的
- 機率小到什麼程度才能算“小機率事件”,顯然,“小機率事件”的機率越小,否定原假設 H 0 H_0 H0就越有說服力,常記這個機率值為α(0<α<1),稱為檢驗的顯著性水準
- 對于不同的問題,檢驗的顯著性水準α不一定相同,一般認為,事件發生的機率小于0.1、0.05或0.01等,即“小機率事件” 。
假設檢驗舉例——鑒别可口可樂和百事可樂
可口可樂和百事可樂作為市場上最常見的兩種可樂飲料大家一定都喝過,我們知道這兩種可樂的味道差不多很難分辨, 如果現在有一名同學說自己能通過味道區分出兩種可樂,那麼如何設計實驗來驗證他是否具備這項能力呢?
我們可以假設這名同學不具備這個能力,如果通過實驗能證明我們的假設是錯誤的,那麼就說明這名同學具備品嘗出兩種可樂的能力來
- 原假設 H 0 H_0 H0 不能區分兩種不同的可樂
- 備擇假設 H 1 H_1 H1 可以區分兩種不同的可樂
接下來我們給該同學用相同的杯子倒若幹杯不同的可樂,讓他品嘗說出杯子裡的可樂究竟是可口可樂還是百事可樂,如果答對了可能有兩種情況:
- 瞎蒙的
- 真的能嘗出來
我們都知道隻喝一杯他如果答對了說明不了什麼問題,那麼究竟喝幾杯能下結論說這個同學不是瞎蒙的呢?我們可以計算一下機率
- 如果是瞎猜,猜對的機率是0.5, 那麼猜對一杯的機率是0.5, 連着猜對兩杯的機率是0.5*0.5 = 0.25 連着猜對三杯的機率是0.5 * 0.5 * 0.5=0.125
- 如果該同學連喝5杯都做出了正确的判斷, 那麼我們可以計算得知 0.5^5 = 0.03125 也就是說有 3.125%的可能他是猜對的, 那麼此時我們就可以推翻原假設: 該同學是蒙的,因為連着猜對5杯的機率實在太低了
- 那麼嘗對了5杯之後我們是否有100%的把握說該同學一定具備通過味道區分可樂的能力呢? 答案也是否定的, 因為仍然有3.125%的可能他是瞎蒙的, 這個3.125%就是顯著性水準(Significance level) 一般用α表示, 一般我們把顯著性水準α的門檻值設定為5%, 也就是α≤5%的時候我們認為這個結果是具有統計學意義的
- 當然我們還可以接着做更多次實驗, 比如讓該同學再多喝幾杯, 如果都能做出正确判斷, 那麼對應的顯著性水準α 就越小, 我們得出該同學能夠通過味道區分出兩種可能的把握就越大
AB測試中的假設檢驗
通過上面的例子我們對假設檢驗有了基本的了解, 接下來我們看一下如何在AB測試中應用假設檢驗
- 原假設 H 0 H_0 H0 老的方案老的設計
- 備擇假設 H 1 H_1 H1 新的方案新的設計
在上面的例子中,實驗的次數越多我們得到的結論就越準确,那麼在AB測試中,實驗的次數實際上就是參與實驗的人數,我們可以通過設定顯著性水準(Significance level) α來倒推參與實驗的最少人數,具體計算可以通過上面給出的工具網站實作
顯著性水準α : 依據實驗結果做出推翻原假設(否定原方案) 選擇備擇假設(采用新方案) 的決定,犯錯的機率, 一般設定為5%
統計功效(1−β): 依據實驗結果做出保留原假設(保留原方案) 不選擇備擇假設(不采用新方案) 的決定,犯錯的機率, 一般β設定為20% 那麼1−β 為80%
2.4 AB測試與辛普森悖論
辛普森悖論為英國統計學家E.H.辛普森于1951年提出,即在某個條件下的兩組資料,分别讨論時都會滿足某種性質,可是一旦合并考慮,卻可能導緻相反的結論。
方案A(現有方案)轉化率 | 方案B(新方案)轉化率 | |
---|---|---|
Android 使用者 | 70/800 = 8.75% | 20/200 = 10% |
iphone 使用者 | 10/200 = 5% | 50/800 = 6.25% |
總使用者 | 80/1000 = 8% | 70/1000 = 7% |
AB測試中産生辛普森悖論的原因:流量分割不均勻導緻的實驗組與對照組的使用者特征不一緻
AB測試中如何避免辛普森悖論
- 要得到科學可信的 AB 測試試驗結果,就必須合理的進行正确的流量分割,保證試驗組和對照組裡的使用者特征是一緻的,并且都具有代表性,可以代表總體使用者特征。
方案A(現有方案)轉化率 | 方案B(新方案)轉化率 | |
---|---|---|
Android 使用者 | 70/800 = 8.75% | 80/800 = 10% |
iphone 使用者 | 10/200 = 5% | 12/200 = 6% |
總使用者 | 80/1000 = 8% | 92/1000 = 9.2% |
- 在實驗設計上,如果我們覺得某兩個變量對試驗結果都有影響,那我們就應該把這兩個變量放在同一層進行互斥試驗,不要讓一個變量的試驗動态影響另一個變量的檢驗。如果我們覺得一個試驗可能會對新老客戶産生完全不同的影響,那麼就應該對新客戶和老客戶分别展開定向試驗,觀察結論
- 在試驗實施上,對試驗結果我們要積極的進行多元度的細分分析,除了總體對比,也看一看對細分閱聽人群體的試驗結果,不要以偏蓋全,也不要以全蓋偏。一個試驗版本提升了總體活躍度,但是可能降低了年輕使用者的活躍度,那麼這個試驗版本是不是更好呢?一個試驗版本提升總營收0.1%,似乎不起眼,但是可能上海地區的年輕女性 iPhone 使用者的購買率提升了20%,這個試驗經驗就很有價值了。
3、AB測試代碼實戰
3.1 項目背景
- 假設您在一家中型電商公司工作。 UI設計師設計了最新的産品頁面想通過頁面更新提高轉化率
- 目前的轉化率全年平均在13%左右
- 目标轉化率達到 15%。
- 在新的頁面上線之前,我們要通過一小部分使用者上測試頁面的效果, 是以需要進行A/B測試
3.2 設計實驗
- 提出假設
- 首先,我們要確定在項目開始時就制定了一個假設
-
鑒于我們不知道新設計的性能是否會比我們目前的設計更好或更差(或相同?),我們将選擇雙尾測試:
H 0 H_0 H0 原假設 老的設計比較好, 新版設計沒有用
H 1 H_1 H1 備選假設 新的設計比較好
- 選擇變量
- 對照組: 看到舊的設計
- 實驗組: 看到新的設計
- 我們的設計的目标KPI是轉化率, 是以,我們會添加一個字段來記錄使用者的購買情況
- 購買了産品的使用者 值為1
- 未購買産品的使用者值為0
- 确定實驗人數
- AB測試隻會選擇一小部分使用者來參與實驗, 用小部分的實驗結果來估計整體的結果,每組的人數越多,我們得到的結果就越精準,但同時我們付出的成本就越大,通過功效分析我們可以計算出滿足實驗條件的最小人群
- 檢驗功效 (1 — β) :一般設定為0.8
- α :在設計實驗的時候我們設定為0.05
- 效果大小:舊的頁面轉化率為13%,新頁面我們希望轉化率能提升2%,是以我們可以用13%和15%來計算預期效果大小
- 确定實驗人數的計算過程可以通過Python代碼實作
4720import numpy as np import pandas as pd import scipy.stats as stats import statsmodels.stats.api as sms import matplotlib.pyplot as plt import seaborn as sns from math import ceil %matplotlib inline # 計算effect_size 0.13為目前的轉換率 0.15為目标轉化率 也就是說我們希望通過新的設計帶來2%的提升 effect_size = sms.proportion_effectsize(0.13, 0.15) required_n = sms.NormalIndPower().solve_power( effect_size, # 傳入上面計算的 effect_size power=0.8, # 設定 1-β = 80% alpha=0.05, # 設定 α 為5% ratio=1 # 對照組和測試組人一樣, 這裡的ratio 比例就是1 ) #對結果向上取整 required_n = ceil(required_n) print(required_n)
- 計算結果說明每組至少需要4720人
- 在實踐中将功率參數設定為 0.8 意味着如果我們的設計之間存在轉化率的實際差異,假設差異是我們估計的差異(13% 對 15%),我們有大約 80% 的機會檢測到它 與我們計算的樣本量在我們的測試中具有統計顯着性。
3.3 收集準備資料
- 在企業場景下,我們需要與工程團隊配合,收集滿足要求的資料, 這裡我們使用準備好的資料集, 對資料進行處理
- 加載資料到DataFrame
- 檢查和清理資料
- 從DataFrame中采樣資料每組4720行
df = pd.read_csv('data/ab_data.csv') df.head()
user_id timestamp group landing_page converted 851104 2017-01-21 22:11:48.556739 control old_page 1 804228 2017-01-12 08:01:45.159739 control old_page 2 661590 2017-01-11 16:55:06.154213 treatment new_page 3 853541 2017-01-08 18:28:03.143765 treatment new_page 4 864975 2017-01-21 01:52:26.210827 control old_page 1 - 檢視資料基本情況
<class 'pandas.core.frame.DataFrame'> RangeIndex: 294478 entries, 0 to 294477 Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 user_id 294478 non-null int64 1 timestamp 294478 non-null object 2 group 294478 non-null object 3 landing_page 294478 non-null object 4 converted 294478 non-null int64 dtypes: int64(2), object(3) memory usage: 11.2+ MB
- 資料一共294478行,每一條資料代表一次使用者通路,共五列:
-
- 通路的使用者IDuser_id
-
- 通路的時間timestamp
-
- 該使用者被放到那一組 {group
對照,control
實驗}treatment
-
-該使用者看到的是哪一種落地頁 {landing_page
老頁面,old_page
新頁面}new_page
-
- 改次通路是否有轉化 (binary, =無轉化,converted
=轉化)1
-
- 在後續的分析中,我們實際上主要用到的是
和group
這兩個字段converted
- 建立透視表, 查詢是否對照組看到的都是老頁面
landing_page new_page old_page group control 1928 145274 treatment 145311 1965 - 在我們進行後續處理之前, 還要檢視是否有使用者進行了多次操作
3894session_counts = df['user_id'].value_counts(ascending=False) multi_users = session_counts[session_counts>1].count() multi_users
- 說明一共有3894個使用者通路了不止一次, 整體資料有20多萬條, 是以我們直接把這部分資料删除
users = session_counts[session_counts < 2].index df = df[df['user_id'].isin(users)]
- 資料采樣
- 接下來我們從處理後的資料中,每組采樣4720條資料, 我們這裡使用DataFrame的sample()方法進行簡單随機采樣
control_sample = df[df['group'] == 'control'].sample(n=required_n, random_state=22) treatment_sample = df[df['group'] == 'treatment'].sample(n=required_n, random_state=22) # 這裡random_state 為随機數種子, 如果也傳入22, 那麼後續結果會與講義中一樣 ab_test = pd.concat([control_sample, treatment_sample], axis=0) ab_test.reset_index(drop=True, inplace=True) ab_test
user_id timestamp group landing_page converted 763854 2017-01-21 03:43:17.188315 control old_page 1 690555 2017-01-18 06:38:13.079449 control old_page 2 861520 2017-01-06 21:13:40.044766 control old_page 3 630778 2017-01-05 16:42:36.995204 control old_page 4 656634 2017-01-04 15:31:21.676130 control old_page … … … … … … 9435 908512 2017-01-14 22:02:29.922674 treatment new_page 9436 873211 2017-01-05 00:57:16.167151 treatment new_page 9437 631276 2017-01-20 18:56:58.167809 treatment new_page 9438 662301 2017-01-03 08:10:57.768806 treatment new_page 9439 944623 2017-01-19 10:56:01.648653 treatment new_page 1 - 檢視兩組資料情況
ab_test.groupby('group')['landing_page'].value_counts() # landing_page 落地頁類型 old_page老頁面 new_page 新頁面 # control 控制組 treatment 對照組
group landing_page
control old_page 4720
treatment new_page 4720
Name: landing_page, dtype: int64
3.4 分析實驗結果
- 首先我們來計算一下兩組的轉化率和标準差
conversion_rates = ab_test.groupby('group')['converted'].mean().to_frame() conversion_rates conversion_rates.style.format('{:.3f}')
group conversion_rate control 0.123 treatment 0.126 - 從上面的統計資料來看,舊的和新的落地頁表現非常相似,新設計表現略好, 12.3% 與 12.6%
- 從對照組的資料看, 轉換率為12.3% 比之前我們的整體表現要差一些, 可能與采樣人群的差異有關
- 測試組的資料比對照組要好一些, 但是這個結果是否具有統計意義?
3.5 假設檢驗
- 最後一步是假設檢驗, 我們的樣本量比較大,可以應用Z檢驗來計算P值, 如果P值<0.05, 說明
- Z檢驗一般用于大樣本(即樣本容量大于30)平均值差異性檢驗的方法。它是用标準正态分布的理論來推斷差異發生的機率,進而比較兩個平均數的差異是否顯著。
- 我們可以使用statsmodels.stats.proportion 子產品來計算P值和置信區間
from statsmodels.stats.proportion import proportions_ztest, proportion_confint
control_results = ab_test[ab_test['group'] == 'control']['converted'] #擷取對照組是否轉化的資料
treatment_results = ab_test[ab_test['group'] == 'treatment']['converted'] #擷取實驗組是否轉化的資料
n_con = control_results.count() # 擷取對照組人數
n_treat = treatment_results.count() # 擷取實驗組人數
successes = [control_results.sum(), treatment_results.sum()] # 擷取實驗組和對照組成功轉化的人數
nobs = [n_con, n_treat]
z_stat, pval = proportions_ztest(successes, nobs=nobs) #計算P值
(lower_con, lower_treat), (upper_con, upper_treat) = proportion_confint(successes, nobs=nobs, alpha=0.05) #計算置信區間
print(f'z statistic: {z_stat:.2f}')
print(f'p-value: {pval:.3f}')
print(f'ci 95% for control group: [{lower_con:.3f}, {upper_con:.3f}]')
print(f'ci 95% for treatment group: [{lower_treat:.3f}, {upper_treat:.3f}]')
z statistic: -0.34
p-value: 0.732
ci 95% for control group: [0.114, 0.133]
ci 95% for treatment group: [0.116, 0.135]
3.6 最終結論
- 最終計算出的p-value = 0.732 遠大于我們實驗設定的α = 0.05, 我們不能拒絕原假設 舊的首頁表現更好, 這就意味着,我們的新頁面并不比舊的頁面表現好
- 檢視實驗組的置信區間([0.116, 0.135] 或 11.6-13.5%),注意到:
- 13% 的基準轉化率在置信區間内
- 15% 的目标值(我們的目标是 2% 的提升)并不在置信區間内
- 這意味着新設計的真實轉化率可能與之前的轉化率接近, 證明我們的新改進并沒有效果
4、小結
AB測試的應用場景
- 網際網路行業應用廣泛:頁面結構調整, 換新圖示,添加新功能……
- 實體行業應用相對複雜一些:不同優惠券效果測試
AB測試還是ABC… 測試
- AB測試:一次測試一個方案
- ABC…… 測試: 一次測試多個方案,但需要流量足夠大,否則難以滿足實驗要求的最少人數
AB測試需要注意如下幾點
- 流量配置設定
- 确定有效的最小參與人數
- 确定基準名額和提升目标
- 設定顯著性水準α (一般是5%)和 統計功效 1-β (一般是80%)
- 出結果之後計算P值 如果P<5% 則可以拒絕原假設
- 我們可以通過AB測試工具網站幫助确定人數,也可以使用statsmodels 子產品來通過代碼實作
- import statsmodels.stats.api 計算需要人數
- statsmodels.stats.proportion 計算P值和置信區間