@xtccc
2016-07-29T21:38:55.000000Z
字数 5235
阅读 3211
算法
目录
有n个数据,如果这些数据全部在内存中,则可以在O(n)时间内从中找出某个quantile。
但是当数据量非常大的时,这些数据无法全部放入内存,怎么办?
当数据量很大时(例如PB级别的量),往往允许找出的quantile离真实值有一定的误差,但要在必要的误差限度内。
φ-quantile
把n个数字按照从小到大的顺序排列成一个有序序列L,rank值为φn的那个数据点就称为L的φ-quantile,这里 φ ∈ [0, 1]。
例如,L的中位数(median),就是L的0.5-quantile。
寻找φ-quantile的理想的算法应该有以下的优点:
- 只需遍历一遍数据;
- 是确定的(deterministic)算法;
- 算出的结果,与真实结果之间的误差足够小;
- 不需要事先知道关于数据集的分布特性;
- 允许并行化运行该算法;
- 除了找到第一个quantile之外,每计算一个新的quantile,所需的时间和空间开销应该是一个常量。
笨方法
排序! 将所有的数据排序后,自然就得到了想要的quantiles。这对于数据量较小时很容易实现,代价也不大。但如果是10 trillion的数据,你排序试试看要多久。
巧方法
本文描述的OPAQ方法来自于论文 A One-Pass Algorithm for Accurately Estimating Quantiles for Disk-Resident Data,这篇论文描述了怎样通过将数据集划分为若干个等长子集的方式来寻找approximate quantils。
而论文 Approximating quantiles in very large dataset 则描述了如果原数据集被划分为不等长子集,怎样寻找approximate quantiles。
OPAQ是“One Pass Algorithm for Quantiles”的缩写。
首先定义一些概念。
Term | Description |
---|---|
m | size of each run |
n | total number of elements |
r | number of runs () |
s | size of the sample for each run |
φ | quantile fraction (φ ∈ [0, 1]) |
α | index (rank) of the quantile () |
value of the quantile |
OPAQ算法由2个阶段组成:1) 取样阶段(sampling phase); 2) quantile计算阶段(quantile finding phase)。
在取样阶段,将全部数据划分为个子集(称为个runs)。对于每一个run,我们要为其生成个sample points,即,且满足 。随后,这个sample list将被合并为一个长度为升序列表,该列表将在quantile计算阶段中被用于估计真实的φ-quantile的上界(upper bound)和下界(lower bound)。
OPAQ计算出的结果的准确度依赖于上述这两个阶段。
为了对作出估计,我们要计算出它的lower bound 以及它的upper bound ,这里,并确保内的点的数量是bounded。
怎样为长度为的某个生成个sample points呢?我们使用论文 On the Versatility of Parallel Sorting by Regular Sampling 中提到的regular sampling方法:
假定目标run的长度为,其中每一个数据点的index从1开始,到结束。将该run划分为个等长的sub-run,为第i个sub-run找一个sample point ,使得大于等于第i个sub-run中任何数据,且小于等于第i+1个sub-run中任何数据。实际上,这个可以是这个run中的数据,也可以不是这个run中的数据,但只要满足上面的条件,即可代表第i个sub-run。
例如,如果两个sub-list为 = {3, 5, 7, 7}以及 = {8, 9, 10, 11},那么则可以选择 = 7, = 11;也可以选择 = 7.5, = 11; 还可以选择 = 8, 。
一个典型的做法是:算出list的中位数,然后根据这个中位数将该list划分为两个等长的sub-list(记为和),使得中的数据都小于或等于中的数据,分别取出其中的最大值作为两个sample point。对和继续按照这种方法划分出新的更短的sub-list,直到新的sub-list的长度达到了。这样的迭代过程最多有次。在最坏的情况下,我们可以在O()时间内找到个sample points。
例子:
某个中的数据点为: {7, 15, 2, 0, 4, 12, 13, 20, 0, 1, 5, 6, 2, 14, 16, 9},要为其找出4个sample points,因此,m = 16, s = 4, m/s = 16/4 = 4。
最终生成的4个sub-run为{2, 0, 0, 1}, {4, 5, 6, 2}, {7, 12, 13, 9}, {15, 20, 14, 16},对应的sample points为{ = 2, = 6, = 13, = 20}。
生成sample points的过程如下:
在找出了全部个sample lists之后,我们将它们合并起来,形成一个长度为的升序列表,记为List。
现在,我们将利用上面得到的List来计算和。对于List中的任意一个sample point ,它有以下的性质(证明过程可以参看后面的附录):
- 至少存在 个小于或者等于的数据点;
- 另外,至多存在 个小于的数据点。
因此,最多可能存在有 个小于的数据点(这是当前i个sub-run中的数据均小于时的情况)。
我们将利用上面的两条性质来定位和,并计算真实quantile与估计值之间的误差范围。
定位以及
首先,我们让等于List中的第i个sample point,并希望它能满足:
解这个关于i的不等式,可以得到:
即
因此
我们再让等于List中的第j个sample point,并希望它能满足:
解这个关于j的不等式,可以得到:
即
因此
误差估算
现在我们要计算,与之间的最大范围,与之间的最大范围,以及与之间的最大范围。
Lemma 1: 与之间最多存在个数据点
证明
用表示与之间的数据点数量,用表示满足条件Cond的数据点数量的下限。那么,我们有
显然,
用(3)来替换这里的i,可以得到:
进而
Lemma 2: 与之间最多存在个数据点
同理可以证明
Lemma 3: 与之间最多存在个数据点
非常明显
因此,只要每个run中的取出的sample points数量足够大(与原始数据集的大小n相比),quantile的估算值与真实值之间的差异就可以足够小。
Phase | Complexity |
---|---|
Finding the sample points | |
Mergeing the sample lists | |
Estimating quantiles |
实际上,OPAQ算法最复杂的部分在于为每一个subset计算出s个sample points,并将其合并成一个有序的列表。一旦这一步完成,我们可以立即得到想要的approximate -quantile。
并且,在计算出了List之后,它可以被重复地用来计算其它任意的quantiles。
如果原始数据集(均匀地)分布在多个节点上,那么可以为每一个节点上的数据子集生成一个locally sorted sample list,再把他们合并成一个globally sorted sample list,然后再计算出α-quantile。
这里的关键在于,怎样高效地将多个locally sorted sample lists合并成一个globally sorted sample list?
如果每一个run保留了原来的sorted sample list,那么,当有新的增量数据到达某个run时,该run只需要为增量数据计算出对应的sorted sample list,并将它与原来的sorted sample list进行合并即可。
实际上,我在应用OPAQ算法时,对它并不怎么满意,因为它有以下不足:
1. 精确度受到单节点运算能力的限制
与之间最多存在个数据点, 与之间最多存在个数据点。
是一个run中的sample points的数量,最多也就等于这个run的长度,一般一个run的数据都在一个节点上。因此,会受到单节点的计算能力的限制,而中并不含有变量,这就意味着无法通过增加run的数量来提高结果的精度;相反,如果只增加run的数量,那么会增大,误差反而会增大。
假设有10亿条数据点,想分为100个run进行计算,那么每个run中有1000万条数据,从每个run中选取出10万个sample points,那么quantile的偏差范围在 ,并不是那么小。
2. 必须满足各个run中的数据点数量相同这个前提
这个条件比较苛刻,如果要满足这个条件,往往需要对数据进行shuffle。实际上,论文 Approximating quantiles in very large datasets 提出了另一个算法,它基于OPAQ算法,但是不要求每个run中的数据点的数量相同。