• 展开微博窗口
  • QQ:826945906
  • 微信:u200714928
  • 展开分类目录
胡伟

如何对产品做AB测试

ABtest一个总的目的和意图是,判断哪种种UI或rerank策略更优,通过事实的依据( CTR或下单率)判断哪种策略更符合用户的习惯和需求。

一、需求驱使 

我们经常会面临多个设计方案的选择,比如app或pc端某个界面的某个按钮是用红色还是用蓝色,是放左边还是放右边。传统的解决方法通常是集体讨论表决,或者由某位专

家或领导或文青来拍板,实在决定不了时也有随机选一个上线的。虽然传统解决办法多数情况下也是有效的,但A/B 测试(A/B Testing)可能是解决这类问题的一个更好的方法。

所谓 A/B 测试,简单来说,就是为同一个目标制定两个方案(比如两个页面),让一部分用户使用 A 方案,另一部分用户使用 B 方案,记录下用户的使用情况,看哪个方案更符

合设计目标。

下面看一个例子:

在展示“格林秀上午酒店”这个poi时有客户端两种UI:

方案A:如左图,评分展示星状图片,消费人数再右边;

方案B:如右图,只展示评分分数,后边添加消费人数;

我们很难知道那种方案比较好,那我们可以做个实验,把a和B方案同时放到线上的生产环境,让一部分用户使用 A 方案,另一部分用户使用 B 方案,记录下用户的使用报表如下图,通过大自然的优胜劣汰法则,我们可以通过CTR或下单率等指标看哪个方案更符合设计目标。

二、系统模型

abtest实验可以分成两种,客户端client实验和服务端server实验,客户端实验一般来说只是UI上的实验,比如上面的例子,纯粹是展示端的策略;而服务端的实验是返回给client数据的内容做实验,比如推荐的策略,订单列表rerank策略等。下面通过client和服务端的实验分别做介绍。

(1)客户端实验:

方案1:

ABTest业务流程描述:

(1)客户端在需要获取ABTest策略的地方,通过RPC接口获取uuid在ABTest1这个实验下的规则:

  1. {
  2.     “data”: [
  3.         {
  4.             “name”“test1”,//实验key
  5.             “strategy”“strategy1”,      //策略key,客户端根据策略key选择策略方案
  6.             “flow”“flow1”,      //流量组,用于上报。每个流量组只属于一个策略。
  7.             “finished”false     //标识实验是否终止,如果已经终止,则不再向utm_compaign参数的F字段append该规则,但是不影响规则,原规则依然生效。
  8.         }
  9.     ]
  10. }
其中testkey参数可以是多个ABTest实验的key:abtest1,abtest2。
(2)之后客户端向server发起的全部请求,URL的utm_compaign参数中的F位添加字符串:Fabtest1__strategy1__flow1,Nginx将utm_compaign打印到access日志。
(3)客户端在另一个需要获取ABTest策略的地方,通过http接口/get/abtest/{testkey}/{uuid} 获取uuid在ABTest2这个实验下的规则:
  1. {
  2.     “data”: [
  3.         {
  4.             “name”“abtest2”,//实验key,
  5.             “strategy”“strategy2”,      //策略key,客户端根据策略key选择策略方案
  6.             “flow”“flow2”,      //流量组,用于上报。每个流量组只属于一个策略。
  7.             “finished”false     //标识实验是否终止,如果已经终止,则不再向utm_compaign参数的F字段append该规则,但是不影响规则,原规则依然生效。
  8.         }
  9.     ]
  10. }
(4)之后客户端向server发起的全部请求,URL的utm_compaign参数中的F位添加字符串:Fabtest1_strategy1__flow1___abtest2__strategy2__flow2,实验&策略&流量组采用两个下划线分隔,多个实验间采用三个下划线分隔。
(5)Nginx将URL中的utm_compaign打印到access日志,flume收集Nginx access 日志,同步到Hadoop,最后导入Hive。
(6)PM在报表系统,通过规则维度查询点击、转化报表。
这样导致的问题是url中utm_compaign的F字段过长,当实验到达一定数量的时候会出现,url过长,抓包会发现。实际上,客户端只关心我在某个界面中这次请求(uuid+ci+platform)命中的实验及策略,连utm_campaign都懒得拼接。
如URL中有utm_campaign=Fab_homepagewebview0717__b__d___ab_b_food_57_purepoilist_extinfo__a__a___ab_b_selectlist_paidui__a__leftflow___ab_i550poi_ktv__d__leftflow___ab_i_5_9_travelpoidetail__b__a___ab_i550poi_xxyl__d__d___ab_mingdiangexinghua0707__j__j___ab_waimaiwending__b__b___ab_b_travelsearchhot__a__a___ab_ifoodadvert__b__b___ab_pindaoqugexinghua0708__e__e___ab_itriphotpoi__b__leftflow___ab_i_6_0_webview__a__a___ab_b_travelpoilistrank__b__b2___ab_i_group_5_7_search_chunpoi__b__b1___ab_i_group_5_8_spdy__b__b___ab_ihotelqianzhi__b__b___i_group_5_2_deallist_poitype__d__d___ab_i550poi_shfw__d__leftflow___ab_ihotelpoilist__b__b___ab_itravelsearch0814__b__leftflow___ab_i_group_6_0_search_hotword__b__leftflow___ab_sieve_multiple_staticscore__base__base___ab_h_hotel_search_hot__b__b___ab_i_group_5_9_onsite__a__leftflow___ab_i_group_pingjiapush__a__a___ab_b_catesearchreplace__b__b___ab_b_deal_sieve_migrate__b__b___ab_i_group_travelhomepage0630__a__a___ab_i550poi_lr__d__leftflow___ab_b_searchmaiton__c__c___ab_groupcontext__a__a___ab_maidan_distance_first__smartfirst__smartfirst___ab_i_group_5_9notificationtest__a__a___ab_dealzhanshi__a__a1___ab_i_group_5_8_dns__a__leftflow___ab_ihotelbkdetail__a__a___ab_v1_po1_sieve_migrate__search__search
基于这个痛点,我们采用另一种上报方式;

方案二

流程:

  1. (1)(客户端的工作):app启动或切换城市,会请求abtest服务后台,获得所有的实验的以及命中的策略缓存在app中。
  2. (2)(abtest后台的工作):abtest接收一个uuid+ci+pt的请求,返回给app所有的实验的以及命中的策略,同时将这次请求和结果通过flume_agent收集日志,同步到Hadoop,最后导入Hive。
  3. (3)(客户端的工作):在第1步的请求中的获得的所有的实验的以及命中的策略缓存在app中。
  4. (4)(客户端的工作)  : 进入到一个做ab实验的界面,按照第3步命中的实验的策略+展示的业务数据处理展示逻辑。
  5. (5)(客户端的工作:):会把第4步这个界面的信息和埋点信息上传到数据中心的原始日志,异步步少重试的方式上报埋点,

HTTP GET/POST 日志收集方式,MGE,MGP,MPT的信息定义可以。移动页面流跟踪事件,这些事件封装为MPT事件,具体格式如下:

  1. </pre><pre name=“code” class=“javascript”>“nm”:“MPT”,//页面流跟踪事件(PageTrack)
  2. “val”:{
  3.     “root”:    //层级前缀
  4.     “name”:    //页面名/组件名/弹窗名
  5.     “content”//数据请求URL内容/弹窗内容
  6.     “type”:    //page/alert
  7. }
  8. “nm”:“MGE”,//Event跟踪,需要客户端手工埋点,用于解决临时统计需求
  9. “val”:{
  10.     “cid”:    //页面名 ||||类别 category id
  11.     “act”:    //动作名 ||||动作 action
  12.     “lab”:    //动作描述 ||||注释 label
  13.     “val”:    //页面描述 ||||权值 value
  14. }
  15. “nm”:“MGP”,//页面跟踪,需要客户端手工埋点,用于解决临时统计需求
  16. “val”:{
  17.     “root”:    //层级前缀(非必需)
  18.     “name”:    //页面名/组件名/弹窗名(必须)
  19.     “content”//数据请求URL内容/弹窗内容(必须)
  20.     “type”:    //page/alert
  21. }

(6)(数据组的工作):应用系统可以通过flume,将原始日志同步到Hadoop,最后导入Hive表,通过关联的条件将两个hive表关联,同时关联一些点击下单等数据,清洗数据成报表。

方案一和方案二的比较:

(1)方案一的缺点是会添加客户端RD的URL拼接工作,致命的是实验到达一定个数,访问页面过深URL过长导致被截断(sa控制http的url的长度和header大小)
(2)方案一的有点是,数据组RD清洗数据比较方便,只需收集ngixn日志,对url进行清洗即可产生报表,只需要关联点单点击等报表。
(3)方案二的优点是,通过各自打埋点成原始日志到hadoop平台方式,客户端只关心自己命中的策略以及处理的业务逻辑,与业务无关的事情切入较少。
(4)方案二的缺点是,客户打的埋点需要跟abtest后台的表进行关联,关联的逻辑不固定,添加了清洗数据的复杂度。
最后基于系统长期的发展,采取了方案二,前期使用的方案一将在app的新版本中废弃。

(2)服务端实验:

其实服务端的实验跟客户端的实验的是类似的,只需要添加额外的工作:在服务端请求abtest后台,需要知道本次请求以及实验命中的策略,并将结果返回给客户端。
码字很辛苦,转载请注明来自胡伟的博客《如何对产品做AB测试》

评论