13.5.2 Mahout中的数据表示

生活中的数据会以各种各样的形式存储,Mahout中的数据也会以其固定的形式表示。在Mahout中,数据将会以向量的形式进行存储。

多数人对向量这个词并不陌生。在不同的领域,向量具有不同的实际意义。在物理中,向量用来表示力的大小和方向,或者一个移动物体的速度。在数学中,一个向量表示空间中的一个点。虽然它们代表的意义不同,但它们表示的形式是相同的。在二维空间中,所有的向量都表示成诸如(5,6)的形式,每一维中有一个数字。当计算这个二维向量时,人们常称第一个维度为X,第二个维度为Y。但是在现实生活中,一个向量可以是多维度的。按照顺序,向量的每一个维度依次被称为0维、1维、2维……

如上所述,向量是按照维度排列的一系列有序的值。因此,你可能已经想到在程序设计语言中用一维数组来表示向量。使用这种方式表示向量,数组的第i项刚好是向量的第i个维度的值。这是一种很好的表示向量的方法,称为密集向量表示法。

在现实生活中,一个具有很高维度的向量经常会在很多维度上没有值。这里的没有值就是程序设计当中空的概念,在向量中它会表示为0。在物理和数学领域,无论是高维度向量还是包含很多0的向量都是很少见的。但在分类算法中这种情况很常见。

使用数组表示这种向量效率太差。数组将会包含很多个0,偶尔会有一个非0值。舍弃众多的0值,单独表示非0值是一种很合理的想法。当处理数百万维度带有很多0值的向量时,密集向量表示法的弊端变得很明显。

在这种情况下,Mahout引入了稀疏向量,将非0值所在的维度与该维度的值做映射。这可以通过Java中的Map实现。当非0值比较少时,这种存储方式比使用基于数组的稠密存储更具优越性。但使用这种方式,程序需要更多的内存空间。

在Mahout中,有关向量表示的类有三个。它们分别是稠密向量(DenseVector)、随机访问稀疏向量(RandomAccessSparseVector)和序列访问稀疏向量(SequentialAccessSparseVector)。

稠密向量由一个double型的数组实现。当向量具有很少的非0值时,这种向量表示法的效率很高。它允许快速访问向量所在任何维度的值,并且能够快速按序遍历向量的所有维度。

在随机访问向量类中,向量的值存储在类似于HashMap的结构中,键是int型、值是double型的。只有维度上的值非0,该维度值才会被存储。当一个向量的一些维度值非0时,用随机访问向量方式表示向量比用稠密向量表示法具有更高的内存使用效率。但是访问维度值的速度和按序遍历所有维度值的速度比较慢。

序列访问向量使用int和double的并行数组表示向量。因此,使用它按序遍历整个向量的各个维度是很快的。但是随机插入和查询某一维度的值时速度要慢于随机访问向量。

这三种表示向量的方式使得Mahout的算法能够按照数据特性、数据访问方式实现。具体使用哪种表示方法是按照算法的特性进行选择的。如果算法具有很多对向量值的随机插入和更新,就应该选择稠密向量或随机访问向量来表示向量。因为这两个向量具有快速随机访问的特性。而对于需要重复计算向量大小的K-Means聚类算法,选择序列访问向量比选择随机访问向量好。