当产品经理告诉你,修改了产品的某个特性让销售提升了 10%;或者推荐引擎经理告诉你,我们的推荐引擎让销售提升了 5%。你会怎么想?我们的团队真 NB 还是这些数字真的该相信吗?

A/B 测试现在用得非常广泛,但是大部分时候却不一定让你得出正确的结论。最常见的就是对产品改进的评估,或者对推荐系统效果的评估。

  • A/B 测试经常犯的错误和问题
  • A/B 测试并不是最好的数据化产品设计方式

A/B 测试经常犯的错误和问题

错误 1. 没有考虑多影响因素:

A/B 测试最大的问题是,需要保证其他因素不变的情况下,改变并观察单因素带来的变化。不可以同时修改和测试多个不同的特性,因为他们之间有可能会相互影响;这种方式的前提是访客没有明显变化,比如在电视上大量投放广告后对产品的 A/B 测试结论就不得不考虑访客的明显变化,测试结论不可采信。

A/B 测试中值得考虑的几个影响因素:

  • 广告投放,季节性,节假日,用户来源渠道变化等引起的访客的变化和访客行为的变化
  • 同时进行的多个 A/B 测试特性的相互影响
  • 其他环境的变化

错误 2. 取用了不合理的样本量

新产品或者产品初期用户量并不是很大,可能会因为样本不足导致 A/B 测试结论不可信。

A/B 测试样本量计算器: http://www.evanmiller.org/ab-testing/sample-size.html

错误 3. 测试时间不足

测试时间不足导致的结果和样本量不足类似。

使用 GA 进行 A/B 测试统计的方法:

http://biasedbit.com/ab-testing-jekyll-ga/

假如不考虑这些因素只看 A/B 测试的结果,理论上可以得到任意结果,比如在结果好的时候停止测试,或者效果不好的时候延长测试时间。
个人认为推荐引擎的结果可以和随机展现的结果进行对比说明销售提升多少或者占比多少,而不是单纯看绝对值。
可能有些老板看到这里会恍然大悟,之前被推荐引擎和产品经理忽悠了多少次啊 :)

比 A/B 测试更好的数据化产品设计方式

A/B 测试的设计大致是这样:将用户分为两组,一组继续使用旧的产品特性 A;另一组使用新的产品特性 B (比如更大的按钮)。过一段时候后通过查看统计数据,看哪组人点击按钮的概率更大。

这种方式听起来确实不错,很多新药物的测试就是用的这种方式:一组人服用药物;另一组人服用糖水。

比 A/B 测试更好的方式:

1. 可以同时比较多于 2 个选择
2. 新的选择可以随时增加和减少

一种更有效的方式叫做 epsilon-greedy 方法:

我们只用 10% 的时间随机选择;90% 的时间使用期望值最高的选择:

def choose():
    if math.random() < 0.1:
        # exploration!
        # choose a random lever 10% of the time.
    else:
        # exploitation!
        # for each lever, 
            # calculate the expectation of reward. 
            # This is the number of trials of the lever divided by the total reward 
            # given by that lever.
        # choose the lever with the greatest expectation of reward.
    # increment the number of times the chosen lever has been played.
    # store test data in redis, choice in session key, etc..

def reward(choice, amount):
    # add the reward to the total for the given lever.

Epsilon-greedy 方法不仅可以用在产品设计上,还可以用在高可用集群的负载方式设计上,比如 bitly 就实现了 epsilon-greedy 的高可用集群池的管理组件。

这种方式的好处出来满足之前说的 2 点之外,可以得到每个设计的转化率;还实现自动化的产品迭代,当然这需要你的产品设计足够组件化。
但是之前这种方式和 A/B 测试一样,不可以同时修改和测试多个不同的特性。

曾经想过将这些做成产品或者服务,但是这的问题是:不了解的人,或者规模小的团队不需要这样的服务,了解或者需要并且规模大的团队自己就有实力将这实现。所以我将之前整理的想法分享出来,希望对你有用。

有用的链接:

http://stevehanov.ca/blog/index.php?id=132
https://github.com/bitly/go-hostpool
http://biasedbit.com/ab-testing-jekyll-ga/
http://www.evanmiller.org/ab-testing/sample-size.html
http://www.evanmiller.org/how-not-to-run-an-ab-test.html
http://nerds.airbnb.com/experiments-at-airbnb/

有些链接打不开,你需要买一个 VPS,参见 http://blog.eood.cn/digitalocean-vps-vpn

纯手工玩转 Nginx 日志

Nginx 日志对于大部分人来说是个未被发掘的宝藏,总结之前做某日志分析系统的经验,和大家分享一下 Nginx 日志的纯手工分析方式。

Nginx 日志相关配置有 2 个地方:access_log 和 log_format 。

默认的格式:

access_log /data/logs/nginx-access.log;

log_format old '$remote_addr [$time_local] $status $request_time $body_bytes_sent '
'"$request" "$http_referer" "$http_user_agent"';

相信大部分用过 Nginx 的人对默认 Nginx 日志格式配置都很熟悉,对日志的内容也很熟悉。但是默认配置和格式虽然可读,但是难以计算。

Nginx 日志刷盘相关策略可配置:

比如,设置 buffer,buffer 满 32k 才刷盘;假如 buffer 不满 5s 钟强制刷盘的配置如下:

access_log /data/logs/nginx-access.log buffer=32k flush=5s;

这决定了是否实时看到日志以及日志对磁盘 IO 的影响。

Nginx 日志能够记录的变量还有很多没出现在默认配置中:

比如:

请求数据大小:$request_length
返回数据大小:$bytes_sent
请求耗时:$request_time
所用连接序号:$connection
当前连接发生请求数:$connection_requests

Nginx 的默认格式不可计算,需要想办法转换成可计算格式,比如用控制字符 ^A (Mac 下 ctrl+v ctrl+a 打出)分割每个字段。

log_format 的格式可以变成这样:

log_format new '$remote_addr^A$http_x_forwarded_for^A$host^A$time_local^A$status^A'
'$request_time^A$request_length^A$bytes_sent^A$http_referer^A$request^A$http_user_agent';

这样之后就通过常见的 Linux 命令行工具进行分析了:

  • 查找访问频率最高的 URL 和次数:

    cat access.log | awk -F ‘^A’ ‘{print $10}’ | sort | uniq -c

  • 查找当前日志文件 500 错误的访问:

    cat access.log | awk -F ‘^A’ ‘{if($5 == 500) print $0}’

  • 查找当前日志文件 500 错误的数量:

    cat access.log | awk -F ‘^A’ ‘{if($5 == 500) print $0}’ | wc -l

  • 查找某一分钟内 500 错误访问的数量:

    cat access.log | awk -F ‘^A’ ‘{if($5 == 500) print $0}’ | grep ’09:00’ | wc-l

  • 查找耗时超过 1s 的慢请求:

    tail -f access.log | awk -F ‘^A’ ‘{if($6>1) print $0}’

  • 假如只想查看某些位:

    tail -f access.log | awk -F ‘^A’ ‘{if($6>1) print $3″|”$4}’

  • 查找 502 错误最多的 URL:

    cat access.log | awk -F ‘^A’ ‘{if($5==502) print $11}’ | sort | uniq -c

  • 查找 200 空白页

    cat access.log | awk -F ‘^A’ ‘{if($5==200 && $8 < 100) print $3″|”$4″|”$11″|”$6}’

  • 查看实时日志数据流

    tail -f access.log | cat -e

    或者

    tail -f access.log | tr ‘^A’ ‘|’

总结

照着这个思路可以做很多其他分析,比如 UA 最多的访问;访问频率最高的 IP;请求耗时分析;请求返回包大小分析;等等。

这就是一个大型 Web 日志分析系统的原型,这样的格式也是非常方便进行后续大规模 batching 和 streaming 计算。

注册 DigitalOcean 512MB 内存, 40GB SSD 硬盘 VPS