原創 黃昱 淘系技術 2021-04-15

為什麼AB實驗能夠提升使用者體驗
我們先從一個例子說起,上圖中是一個購物頻道的首頁,産品經理在思考頁面上可能會影響到使用者體驗的因素,他想到這樣一個問題,頁面重複樣式的營銷子產品過多,導緻底部的精選分類子產品無法在首屏透出,進而影響了使用者體驗。試想,如果使用者在首屏就能看到精選分類子產品,可能會幫助到他們更快的定位想要類目的商品。基于這個思考,産品經理和設計師一起設計了實驗組方案,即,将原來多個重複的營銷子產品合并為一個,這樣底部的精選分類子產品就可以在首屏展示了。我們設計了一個AB實驗來論證新方案的有效性,将頻道使用者均勻的分為兩組,各50%,分别對應兩個首頁。實驗上線後,我們對兩組的使用者行為資料進行對比分析,實驗組的各個名額的确都優于原首頁。我們在整個實驗周期内,對核心名額(如,點選率)進行觀測,發現實驗組的優勢是穩定存在的。基于這個結論,産品經理選擇固化實驗組。
基于前面案例,我們了解了AB實驗的基本流程,從産品經理産生猜想,到設計實驗,再到實驗上線後,我們對資料進行采集分析,并最終做出決策。一個實驗的生命周期,實質上就是一個人從思考問題到論證問題的過程。回到本節最初的問題,為什麼AB實驗可以提升使用者體驗,我認為有以下三點:
- AB實驗是一種我們與使用者的溝通方式;它讓我們業務同學,技術同學都能夠站在使用者的角度去思考問題,通過實驗我們了解到使用者的真實需求,前端UI的疊代是使用者需求驅動的,而不再是“憑經驗拍闆”;
- AB實驗将使用者體驗具象化;使用者體驗是一個抽象的名詞,從前面的例子我們也看到,在實驗中我們将使用者體驗轉化為客觀可量化的資料名額,并根據資料結果更準确的衡量使用者體驗的提升或者下降;
- AB實驗的科學性,可以極大程度的保證資料的準确性,即我們從資料中提煉出的使用者需求是可靠的。實驗的科學性展現在分流模型,及底層的決策模型等等,我們在下一章節進行詳細的介紹。
前端AB實驗技術原理
一條完整的AB實驗鍊路到底需要什麼?建立實驗,工程接入,分流,實驗資料回流...恩,是的,我還可以列一大堆,但是它們沒有組織不成體系,就毫無意義,更談不上架構設計。是以,我們來把這些分散的元素嘗試歸類。
一條完整的AB實驗鍊路其實可以拆分成三部分,第一部分是實驗配置,顧名思義它包含了所有與實驗配置相關的操作,配置建立,分組,配置的推送或下發,第二部分,在前端工程運作時我們讀取實驗配置,并進行分流,并将分流結果進行埋點上報。第三部分是實驗資料,它包含實驗資料的計算,存儲及展示(實驗報表)。接下來我們就基于這三部分展開來聊一聊其底層設計原理。
▐ 實驗配置模型
它核心解決的問題是,如何将實驗配置按照合理次元進行組合并下發?我們線上運作着數千個實驗,如果簡單的把每一份實驗配置進行單獨存儲并下發,很快實驗配置會多到無法管理,且在多實驗并行的頁面上也會非常影響性能。如果把所有配置都放到同一個檔案裡,那顯然更不可行。是以找到這樣一個合适的次元來對實驗配置進行組合非常關鍵。我們引入了前端應用的概念。前端應用我們可以了解為一個前端工程,一個小程式可以是一個應用,或手淘内某個頻道(比如淘金币)也可以是一個應用。我們将實驗配置以應用為次元進行組合,然後推送到CDN。前端運作時則通過JSSDK讀取配置,完成分流并渲染相應的業務元件。
應用裡面包含了場景,一個獨立的流量入口我們稱之為場景,比如某應用的首頁就是一個場景,導購頁也是一個場景,每個場景(頁面)下都運作着一個或多個實驗。實驗是在場景裡完成分流的,換句話說,場景是我們底層的分流模型。
▐ 分流模型
實驗在場景内并不是無序放置的,場景裡有很多層(layer),大家可以将層簡單的了解為一個數學區間,比如[0,100),這個區間代表着該場景下100%流量,在場景裡,每個實驗都是放置在相同或者不同的層裡的。比如上圖中,實驗一有AB兩組,各占50%的流量,且實驗一占據了一整個層,這意味着,A組占據了該層(區間)的前50%,即[0,49],B組占據了後50%,也就是[50,100)。
- 穩定分流
當一個使用者進入該場景,我們會首先讀取他的唯一id,這可以是他的web裝置id,也可以是userid,總之它可以唯一且穩定的辨別一個使用者。然後我們會将這個唯一id作為入參,運作一個Hash算法,該算法的結果一定是落在層這個數學區間上的某一個位置的,以實驗一為例,如果Hash算法的結果處于該層的前50%,那麼該使用者命中了實驗一的A組,反之則是B組。由于唯一id是穩定的,這位使用者不管多少次通路目前場景,他落在某個特定層(區間)上的位置也是穩定的,是以他也會穩定的被分流到實驗一的A組。
穩定分流保證了線上使用者體驗的連續性,進而大大提升了使用者行為資料的可靠性。
- 實驗的正交、互斥及推全
統一場景下常常運作着多個實驗,如何來處理不同實作之間的互相關系,來滿足複雜場景下的業務需求。還是以上圖為例,該場景下同時運作了三個AB實驗:
- 實驗一,紅包權益彈層有AB兩種樣式,觀測名額是兩個彈層的點選率;
- 實驗二,頁面頭部商品子產品有AB兩種樣式,觀測名額是子產品點選率;
- 實驗三,營運投放的營銷banner有AB兩種設計,透不同利益點,觀測名額是banner點選率;
現在的問題是,實驗二和實驗三都是頁面上的子產品,我們希望這兩個實驗同時運作,但不希望它們互相影響,即進入實驗二和實驗三的流量必須互斥。如何做到呢?如上圖所示,實驗一獨占一層,它與下一層的實驗二和實驗三是正交關系,即進入實驗1的流量,同時也會進入實驗二或實驗三。我們把實驗二和實驗三放在同一層,因為我們希望進入實驗二的流量不要與進入實驗三的流量重疊,即他們各自占據了層(區間)上的一部分流量。通過這樣的方式我們實驗了,在一個場景裡,層與層之間的實驗流量正交,層内的實驗流量互斥。
在上面的模型圖中我們還看到一個特殊的層,釋出層(Launch Layer),這個層用來放被推全的實驗分組。比如說,經過線上驗證明驗一的A組效果明顯優于B組,使用者在平台側将A組推至全量,這時候場景内部結構會發生變化,即實驗一的A組會從原來的layer被放到這個特殊的釋出層裡,此時該場景内所有觸發實驗一的流量将不會再執行分流算法,而是直接傳回組A所對應的前端元件。
▐ 實驗資料模型
每一個實驗所關注的資料是不同的,如何為不同的實驗産出其對應的資料報表?我們引入了業務域,它是資料名額的集合,同一業務域下的實驗可以建立并關聯這個業務域下的所有名額,形成實驗與名額的映射關系。在計算實驗資料時,我們會首先讀取該映射關系,确定要計算的名額有哪些,然後再進行計算,這樣也極大程度的節省了計算資源。
▐ 前端AB實驗架構設計
有了前面的知識基礎,我們來看看完整的前端AB實驗架構設計。首先綠色的部分,對于第一次入駐AB平台的業務,我們需要進行相關工作空間的注冊,應用,業務,及場景,對應前面提到的三個模型。
建立完工作空間以後,我們開始核心的實驗建立鍊路,這裡包含了實驗的基本資訊的讀取,分桶設定(流量配置設定)。接下來是建立名額并将名額與實驗關聯的過程。然後是實驗釋出,正式釋出前,有一個beta釋出環節,beta釋出可以了解為實驗上線前的一次“非正式”下發,開發者和業務方可以在beta釋出後對實驗分流、資料及相關業務邏輯進行充分驗證後,再正式釋出。當然,實驗是支援多次釋出的,即實驗中期業務方可以回到流量配置設定這一步,重新調控流量比例,并重新釋出實驗。
然後是藍色部分AB實驗的JSSDK。先從前端工程說起,我們在工程裡引入了一個業務AB實驗元件,該元件是AB平台根據使用者的配置動态生成的,作為(AB實驗相關的)業務元件與頁面(或父容器)的銜接器,其核心工作就是讀取AB實驗所需的參數并傳入SDK,以此觸發SDK的整個AB實驗邏輯。
首先從全局來看,我們把JSSDK拆成了兩個包:
- 一個是核心(Core)包,封裝了通用的核心邏輯如實驗配置讀取及緩存政策,實驗周期控制及分流算法;
- 另一個是接入具體DSL工程的銜接器包(Coupler),如圖所示,它就像是一個AB實驗流程的中轉站,它實作了一套接口函數,即在特定DSL環境下(如React)的請求、緩存及cookie解析(分流因子),并将這些接口函數和實驗參數一起透傳給Core,我們這麼設計的目的是實作Core與前端DSL的徹底解耦,這樣極大的增加了JSSDK的可擴充性。
在拿到實驗參數,及所需的接口函數以後,Core要做的工作就是先擷取實驗配置,此時Core不會直接去請求實驗配置,而是觸發版本控制政策,該政策主要是檢查遠端實驗配置的版本号是否更新,若有更新才會去請求配置,否則會讀取本地緩存的配置。這份本地緩存的配置是使用者第一次觸發實驗時從CDN請求并緩存下來的,之後每次版本更新,才會重新請求并更新緩存。拿到實驗配置後,Core會确認目前事前是否處于實驗周期(AB平台側配置)内,校驗通過後才會正式觸發分流算法(見下一章節),然後将分流結果傳回給Coupler。
Coupler接着會根據分流結果來判斷應該展示哪個對應的業務元件,同時它會将分流結果上報給平台,使用者此時已經可以在實驗的實時資料報表看到分流資料了,技術同學可以通過實時資料來确認實驗是否正常觸發。另外,埋點元件會對命中的業務元件做一層封裝,這裡會傳入可供業務元件調用的埋點上報方法,具體的調用我們在AB平台建立實驗時,就已經生成好了,前端同學是需要将這些上報實驗名額的代碼部署到相應的業務即可。這一部分埋點資料是T+1的,業務方可以在平台側看到相應的實驗報表,并分析實驗結果。
為了快速覆寫所有的前端場景,我們在設計上慎重的考慮了JSSDK的易擴充性。這也是為什麼我們在上一節的Rax 1.0方案中,将JSSDK拆成了Core和Coupler兩個包,我們的思路是Core封裝AB實驗的核心邏輯,不依賴任何前端DSL,在Core與前端工程之間引入一個"銜接器"包,來串聯起整條鍊路。這樣如下圖所示,我們可以通過這樣的架構,非常低成本的擴充到React,小程式,Node FaaS等等。同時也具備良好的可維護性。
我們是如何做到将核心包(Core)與這些DSL徹底解耦的?我們定義了一套接口規範,然後在"銜接器(Coupler)"包中根據這個規範實作一系列的接口函數,當Core在執行某段邏輯調用這些函數時,根本無需關心其底層用的是哪一個DSL的API。比如對localStorage的操作,React和小程式的API是完全不一樣的,是以我們在React和小程式的"銜接器"中按照接口規範各自實作了這樣一套對localStorage的處理函數,并透傳給Core。
▐ 前端AB實驗資料鍊路
【幹貨】AB實驗助力使用者體驗更新(含直播回放)
在前端工程運作時,分流之後展示相應元件,并觸發相關的埋點,如下圖所示在埋點參數中,我們會帶上實驗參數,即實驗釋出id和分組id,實驗釋出id是什麼呢?前文提到在實驗運作中,我們允許業務重新調控流量比例,并釋出實驗,也就是說一個實驗是可以多次釋出的,是以實驗釋出id可以辨別采集到的資料是目前實驗的某一次釋出;這也就是所謂的資料染色過程。
資料上報後,在日志系統形成實時資料流,在資料庫會存儲到T+1的離線表。我們前面多次聊到了建立資料名額并将其名額與實驗綁定,建立實驗與名額的映射關系;在平台側,我們會讀取這個映射關系,并以此為依據進行對應資料名額的計算,計算結果會以KV形式存儲在非關系型資料庫中,便于快速讀取,最終我們在平台産出資料報表供使用者分析決策。
資料積累到什麼程度可以進行進一步決策呢?AB實驗平台底層的統計學模型會對我們目前搜集到的資料進行進一步的計算,幫助業務同學進行風險評估。目前業界運用比較廣泛的兩種方式,一種是頻率學派,對各個名額進行顯著性檢驗(計算p值)。另一種是今天要給大家介紹的貝葉斯模型。
基于貝葉斯算法的風險決策模型,主要計算的是轉化率的機率分布。公式中,θ代表的是我們關注的轉化率名額,比如,點選率,成交轉化率等等;X代表目前已經采集到的資料,p代表機率,那麼貝葉斯公式計算的結果 p(θ|X) 代表的意義就是基于目前資料X,轉化率名額θ的機率分布。
我們模拟一個簡單的實驗來進一步說明,假設現在有A、B兩個按鈕,我們關注的名額是它們的點選率,那麼套用貝葉斯公式,我們要計算的,就是基于已采集到的資料X,兩個按鈕點選率的機率分布情況。
上圖中,我們繪制了随着X的增加,兩個按鈕點選率的分布曲線(機率密度函數,PDF)。藍色曲線代表按鈕A,黃色代表按鈕B,x軸代表點選率,x周與曲線之間的面積代表機率密度。當采集到的資料極少的時候(圖1),我們隊兩個按鈕的點選率的分布估計是很不"自信"的,它們的PDF在0到1之間都有取值,且兩條曲線有大面積的重合,這樣我們也很難判斷它們哪個是更好的模型。
但是随着采集資料的增加,兩條曲線發生了變化,它們在x周上的取值區間越來越狹窄,這意味着我們隊兩個按鈕點選率的估計越來越準确。直到最後一張圖,我們看到代表按鈕A的藍色曲線,點選率集中分布在0.6到0.7之間,而代表按鈕B的黃色曲線,點選率集中分布在0.35到0.45之間,到這個程度已經很明顯,A的點選率分布是優于B的。這是否意味着可以推薦使用者固化A組了呢?其實還不夠,我們繼續看看下面的風險評估流程。
我們不斷收集資料,并對A、B的點選率的機率分布進行模組化,然後比較兩個模型的優劣,并得出A是更好的模型。這時候我們會進一步計算基于A的損失函數,我們會最終得到一個值,這個值代表選擇A而這個選擇是錯誤的機率密度。我們會預先設定一個門檻值,如果計算出選擇A的損失是高于這個門檻值的,我們會進一步采集更多的資料,并重複上面的流程,如果低于這個門檻值,那麼可以認為目前決策的風險是可接受的,我們這時候才會推薦使用者去做進一步決策。
案例分享
▐ 花呗分期小程式
這個實驗中,兩個子產品樣式完全一緻,唯一變化的是其利益點。一個是展示日均價格,另一個是展示分期價格,業務方的猜想是日均價格利益點能帶來更好的體驗,因為它幫助使用者做了更進一步的計算,是以預期展示“日均xx元”利益點的點選率要優于“¥xxx/期”利益點。
而實驗上線後,我們發現結果與我們的預期完全相反。從累計資料以及每日趨勢資料得出,在曝光一緻的前提下,¥xxx/期利益點比日均xx元利益點點選率穩定高出了20%,可見使用者想要的跟我們的認知其實有着很大的出入,這引起了我們想要進一步探索頻道利益點的想法,于是設計了下面的組合實驗。
如上圖所示,我們将首頁的各個子產品,通過不同利益點組合,并強化某一利益點的方式呈現,希望尋找不同組合方式下的最優方案。我們将頁面拆分成兩個實驗,實驗一,将首屏的一拖三子產品均分為三組,将日均,分期及期數三個利益點組合,并強化其中一個利益點;實驗二,将底部商品feeds流子產品分為四組,分組政策與實驗一相似。結合前文提到的溫流模型,這兩個實驗屬于正交關系,也就是說我們将首頁流量排列組合成12(3x4)組,我們期望根據實驗資料,最終得到最優的組合。
兩個實驗線上時間兩周,從資料報表看出,實驗一的第三組的表現要優于其它組,其中全引導寶貝訂單UV及直接引導寶貝訂單UV與另外兩組的差别僅為1%,可以認為是持平,但其中全引導寶貝及訂單數均有10%左右的增量。當然我們也觀測了每項名額的在兩周内的變化趨勢,此處以直接引導寶貝UV為例,第三組強化期數在實驗周期内的确穩定優于其它組。同樣的方式我們分析實驗二的資料,通過觀測多項名額在實驗周期内的均值,及其變化趨勢,第四組相對表現更好。通過實驗資料,業務最終決定固化實驗一的第3組,和實驗二的第4組,這是我們從兩個實驗正交,排列組合形成的12個分組中,得到的最優解決方案。
▐ 虛拟充值
虛拟充值業務,10元20元的小面額有一定機率缺貨,給使用者帶來不好的體驗。我們認為,如果能優化這一點,會讓更多的使用者使用該頻道的充值服務,進而提升頻道的GMV,和充值UV。于是我們設計了實驗組1,也就是把常缺貨的小面額折疊起來,将其它供貨穩定的面額提前,實驗上先後,相較于對照組,實驗組1的GMV的确有提升,但支付UV下降了。我們分析這是由于頻道有一部分使用者是傾向于充值小面額的,但實驗組1将小面額折疊起來,導緻這部分使用者放棄了充值。這顯然不是一個理想的方案,于是我們設計了實驗組2,将所有面額展示給使用者。實驗組2的确帶來了GMV和支付UV的提升,這是一個滿足我們預期的方案,但産品經理認為,這種方式的一個問題是,充值子產品占用首屏過多的空間。
進一步思考,實驗組1方案之是以不可行,是因為我們認為它犧牲了一部分喜歡充值小面額的使用者的使用體驗。那如果我們圈選出一部分本來就喜歡充值高面額的人,在這個特定的人群下進行實驗,效果會是如何呢?實驗結果是,在這個特定人群下,實驗組1實作了GMV和支付UV的提升。于是在這個特定人群下,我們固化了實驗組1。
總結
本次分享為大家深入介紹了AB實驗的底層原理,以及前端AB實驗的架構設計。我們也提供了豐富的案例,為大家介紹了如何運用AB實驗技術提升使用者體驗,希望大家有所收獲。AB實驗在前端的應用我們還在持續探索,如何進一步降低UI實驗的成本,進一步提升實驗性能,是我們接下來希望能回答的問題。如果你也感興趣,歡迎加我的微信MrMarc随時與我交流。謝謝大家。