推荐数据的表示

引言


推荐的质量很大程度上取决于数据的数量和质量。拥有高质量的数据当然是件好事,而且通常越多越好。
但是,推荐算法天生是数据密集型的,其计算涉及对大量信息的访问。因此,数据的数量和表示方式很大程度上影响执行性能。智能地选择数据结构能够极大地改善性能,数据达到一定规模的时候,这并非小事。


目录

  1. Mahout如何表示推荐数据
  2. DataModel的实现和用法
  3. 无偏好值时的数据处理

1、Mahout如何表示推荐数据

推荐引擎的输入时偏好数据(preference data):什么人喜欢什么物品以及喜欢的程度。这意味着该输入就是一个用户ID、物品ID和偏好值的元素集合——这自然是一个大数据集。


Preference对象
preference是最基本的抽象,表示单个用户ID、物品ID和偏好值。一个对象代表一个用户对一个物品的偏好。Preference是一个接口,你最有可能用到的实现是GenericPreference。
聚合(collection)和数组(array)在表示大量preference对象时会变得相当低效。该如何表示大量preference对象呢?在推荐算法中,通过需要一个与某个用户或某个物品关联的所有偏好的聚合,在这种聚合里,所有preference对象的用户ID或物品ID都是一样的,这似乎是冗余的。


PreferenceArray及其实现
看一下PreferenceArray,这是一个接口,它的实现表示一个偏好的集合,具有类似数组的API。例如,GenericUserPreferenceArray表示的是与某个用户关联的所有偏好。(同理,存在一个称为GenericItemPreferenceArray的实现,它封装了所有与某个物品相关联的偏好。)其内部包含一个单一用户ID、一个物品数组,以及一个偏好值数组。在这个表示形式中,每个偏好的边界内存(marginal memory)仅需要12个字节(一个数组有8个字节的物品ID和一个4个字节的偏好值)。与此对应,一个完整的preference对象需要大约48字节。


改善聚合的性能
PreferenceArray及其实现降低了对内存的需要,即便引入了复杂性也是值得的。将内存需要砍掉3/4并不只是节省几兆字节——在一定规模下这会节省几十GB的内存容量。


FastByIDMap和FastIDSet
你一定会吃惊,Mahout的推荐程序中大量使用了Map和Set这两种典型的数据结构,但他们用的并不是通常的Java集合(collection)的实现,如TreeSet和HashMap。相反,通览全部的实现与API,你会找到FastMap、FastByIDMap和FastIDSet。他们类似于Map和Set,但做了特殊定制,仅为满足mahout中推荐程序的需要。他们降低了对内存的占用,而不是显著地改善性能。
Mahout的需求则更具针对性,从而可以对用途做出更强的设定。主要区别如下:

  • 与HashMap类似,FastByIDMap是基于散列的。但它在处理散列冲突时使用的是线性探测(linear probing),而非分离链接(separate chaining)。这样便不必为每个条目(entry)都增加一个额外的Map.Entry对象;如前所述,Object对内存的消耗是惊人的。
  • 在Mahout推荐程序中键(key)和成员(member)通常采用原始类型long,而非object。使用long型的键可以节约内存并提升性能。
  • Set实现的内部没有使用Map
  • FastByIDMap可以作为高速缓存,因为他有一个最大空间的概念;超过这个大小时,若要新加入条目则会把不常用的移走。
  • 存储上的差异是明显的:FastIDSet平均每个成员需要大约14字节,而HashSet需要84字节。FastByIDMap每个条目需要大约28个字节,而HashMap每个条目需要大约84个字节。这说明,当能够在用途上做出更强假设时,就可能进行大幅度的优化——这里主要是在内存需要上。考虑到推荐系统所处理的数据量,这些定制化的实现并非自卖自夸。

2、DataModel的实现与用法

在Mahout中使用DataModel这种抽象机制对推荐程序的输入数据进行封装,而DataModel的实现为各类推荐算法提供了对数据的高效访问。


内存级DataModel


GenericDataModel:内存级(in-memory)实现GenericDataModel是现有DataModel实现中最简单的。它适用于通过程序在内存中构造数据的表示形式,而不是基于来自外部的数据源,如文件或关系数据库。


基于文件的数据:通常不会直接使用GenericDataModel,而是借助于FileDataModel后者从文件中读取数据,并将所得到的偏好数据存储到内存中,即存储到GenericDataModel中。几乎任何正常的文件都可以用,比如采用的CSV(comma-Separatee Value,逗号分隔值)格式的那种文件。用zip或gzip压缩文件同样可以。


基于数据库的数据:有时数据太大,无法放入内存。偏好数据是有可能存储到一个关系数据库中并进行访问的,而Mahout支持这样做。
要知道,当推荐引擎所用的数据来自数据库时,它的运行要比使用内存级的数据表示慢很多倍。这并不是数据库的错,通过合理地调优和配置,一个现代数据库可以用于及其高效地对信息进行索引的检索,但检索、整理(marshalling)、序列化(serializing)、传输和反序列化(deserializing)结果集的开销仍远大于从优化的内存级数据结构中读取的数据的开销。由于推荐算法是数据密集型的,这种开销会快速积累。不过,当没有其他选择时,数据库仍是理想选择,或者虽然所用数据集不大,但为了集成还需要重用一个现有的数据表,此时也应选择数据库。

  1. JDBC和MySQL:偏好数据是通过JDBC访问的,使用了JDBCDataModel的实现。
  2. 通过JNDI进行配置:JDBCDataModel实现还假设包含这个表的数据库可以通过一个DataSource对象来访问,这个对象已经注册到名为jdbc/taste的JNDI中。JNDI(Java naming and Directory Interface,即Java命名与目录接口),它是J2EE(Java 2 Enterprise Edition)规范的核心。如果你正在一个Web应用中使用推荐引擎,并正在使用Tomcat或Resin这样的容器,那么你很可能已经间接用到了JNDI。如果正通过容器(例如,Tomcat容器中的server.xml文件)配置数据库,你会发现这个配置通常会被JNDI中的DataSource所有引用。
  3. 利用程序进行配置:你也可不必直接使用JNDI,而是将DataSource直接传递到MySQLJDBCDataModel的构造函数中。

4、无偏好值的处理

有时,输入推荐引擎的偏好没有值。也就是说,用户和物品是关联的,但是没有这种关联的强度描述。这并非要忘记用户和物品之间的关联,而是忽略其中的偏好强度。由于缺少更好的术语表达,在mahout语言里,这种没有偏好值的关联称为布尔型偏好(Boolean preference)因为一个关联只可能有两个值:存在或不存在。这并不意味着数据中的物品偏好是yes或no,而是会让用户-物品关联具有全部的三种可能状态:喜欢、不喜欢或无所谓。

你可能感兴趣的:(推荐系统理论,推荐数据,数据处理,性能)