转载自:https://www.douban.com/note/204399134/

推荐数据的处理是大规模的,在集群环境下一次要处理的数据可能是数GB,所以Mahout针对推荐数据进行了优化。

Preference

在Mahout中,用户的喜好被抽象为一个Preference,包含了userId,itemId和偏好值(user对item的偏好)。Preference是一个接口,它有一个通用的实现是GenericPreference。

Preference

但因为用户的喜好数据是大规模的,我们通常会选择把它放入集合或者数组。但是因为Java的对象的内存消耗机制,在大数据量下使用Collection<Preference>和Preference[]是非常低效的。为什么呢?

在Java中,一个对象占用的字节数 = 基本的8字节 + 基本数据类型所占的字节 + 对象引用所占的字节

(1)先说这基本的8字节

在JVM中,每个对象(数组除外)都有一个头,这个头有两个字,第一个字存储对象的一些标志位信息,如:锁标志位、经历了几次gc等信息;第二个字节是一个引用,指向这个类的信息。JVM为这两个字留了8个字节的空间。 这样一来的话,new Object()就占用了8个字节,那怕它是个空对象

(2) 基本类型所占用的字节数

byte/boolean   1bytes

char/short       2bytes

int/float           4bytes

double/long     8bytes

(3)对象引用所占用的字节数

reference        4bytes

注:实际中,有数据成员的话,要把数据成员按基本类型和对象引用分开统计。基本类型按(2)进行累加,然后对齐到8个倍数;对象引用按每个4字节进行累加,然后对齐到8的倍数。

class test {
Integer i;
long l;
byte b;
}

占 8(基本) + 16(数据成员——基本类型:8 + 1,对齐到8) + 8(数据成员——对象引用Integer,4,对齐到8) = 32字节

如此一来的话,一个GenericPreference的对象就需要占用28个字节,userId(8bytes) + itemId(8bytes) + preference(4bytes) + 基本的8bytes = 28。如果我们使用了Collection<Preference>和Preference[],就会浪费很多这基本的8字节。设想如果我们的数据量是上GB或是上TB,这样的开销是很难承受的。

为此Mahout封装了一个PreferenceArray,用于保存一组用户喜好数据,为了优化性能,Mahout给出了两个实现类:GenericUserPreferenceArray和GenericItemPreferenceArray,分别按照用户和物品本身对用户偏好进行组装,这样就可以压缩用户ID或者物品ID的空间。

PreferenceArray
GenericUserPreferenceArray

我们看到,GenericUserPreferenceArray包含了一个userId,一个itemId的数组long[],一个用户的喜好评分数据float[],而不是一个Preference对象的集合,它只有较少的对象需要被创建和gc的检查。

用《Mahout in action》一书中的原话“mahout has alreadly reinvented an 'array of Java objects'”——"mahout已经重新改造了Java对象数组"。PreferenceArray和它的具体实现减少的内存开销远远比它的的复杂性有价值,它减少了近75%的内存开销(相对于Java的对象集合)

除了PreferenceArray,Mahout中还大量使用了像Map和Set这些非常典型的数据结构,但是Mahout没有直接使用像HashMap和TreeSet这些常用的Java集合实现,取而代之的是专门为Mahout推荐的需要实现了两个API,FastByIDMap和FastIDSet,之所以专门封装了这两个数据结构,主要目的是为了减少内存的开销,提高性能。它们之间主要有以下区别:

* 和HashMap一样,FastByIDMap也是基于hash的。不过FastByIDMap使用的是线性探测来解决hash冲突,而不是分离链;

* FastByIDMap的key和值都是long类型,而不是Object,这是基于节省内存开销和改善性能所作的改良;

* FastByIDMap类似于一个缓存区,它有一个“maximum size”的概念,当我们添加一个新元素的时候,如果超过了这个size,那些使用不频繁的元素就会被移除。

FastByIDMap和FastIDSet在存储方面的改进非常显著。FastIDSet的每个元素平均占14字节,而HashSet而需要84字节;FastByIDMap的每个entry占28字节,而HashMap则需要84字节。

DataModel Mahout推荐引擎实际接受的输入是DataModel,它是对用户喜好数据的压缩表示。DataModel的具体实现支持从任意类型的数据源抽取用户喜好信息,可以很容易的返回输入的喜好数据中关联到一个物品的用户ID列表和count计数,以及输入数据中所有用户和物品的数量。具体实现包括内存版的GenericDataModel,支持文件读取的FileDataModel和支持数据库读取的JDBCDataModel。

DataModel

GenericDataModel是DataModel的内存版实现。适用于在内存中构造推荐数据,它仅只是作为推荐引擎的输入接受用户的喜好数据,保存着一个按照用户ID和物品ID进行散列的PreferenceArray,而PreferenceArray中对应保存着这个用户ID或者物品ID的所有用户喜好数据。

GenericDataModel

FileDataModel支持文件的读取,Mahout对文件的格式没有太多严格的要求,只要满足一下格式就OK:

*  每一行包含一个用户Id,物品Id,用户喜好

*  逗号隔开或者Tab隔开

*  *.zip 和 *.gz 文件会自动解压缩(Mahout 建议在数据量过大时采用压缩的数据存储)

FileDataModel从文件中读取数据,然后将数据以GenericDataModel的形式载入内存,具体可以查看FileDataModel中的buildModel方法。

JDBCDataModel支持对数据库的读取操作,Mahout提供了对MySQL的默认支持MySQLJDBCDataModel,它对用户喜好数据的存储有以下要求:

* 用户ID列需要是BIGINT而且非空

* 物品ID列需要是BIGINT而且非空

* 用户喜好值列需要是FLOAT

* 建议在用户ID和物品ID上建索引

有的时候,我们会忽略用户的喜好值,仅仅只关心用户和物品之间存不存在关联关系,这种关联关系在Mahout里面叫做“boolean preference”。 之所以会有这类喜好,是因为用户和物品的关联要么存在,要么不存在,记住只是表示关联关系存不存在,不代表喜欢和不喜欢。实际上一条“boolean preference”可有三个状态:喜欢、不喜欢、没有任何关系。

在喜好数据中有大量的噪音数据的情况下,这种特殊的喜好评定方式是有意义的。 同时Mahout为“boolean preference”提供了一个内存版的DataModel——GenericBooleanPrefDataModel

GenericBooleanPrefDataModel

可以看到,GenericBooleanPrefDataModel没有对喜好值进行存储,仅仅只存储了关联的userId和itemId,注意和GenericDataModel的差别,GenericBooleanPrefDataModel采用了FastIDSet,只有关联的Id,没有喜好值。因此它的一些方法(继承自DataModel的)如getItemIDsForUser()有更好的执行速度,而getPreferencesFromUser()的执行速度会更差,因为GenericBooleanPrefDataModel本来就没存储喜好值,它默认用户对物品的喜好值都是1.0

@Override
public Float getPreferenceValue(long userID, long itemID) throws NoSuchUserException {
FastIDSet itemIDs = preferenceFromUsers.get(userID);
if (itemIDs == null) {
throw new NoSuchUserException(userID);
}
if (itemIDs.contains(itemID)) {
return 1.0f;
} return null;
}

利用mahout解决冷启动问题的一些建议:

Mahout 中对于冷启动问题并没有专门的实现,原因很简单,目前的 Mahout 只是一个机器学习算法库(框架),它不是一个推荐系统或推荐引擎。不过,我们还是可以利用 Mahout 中提供的一些算法帮助我们解决冷启动。

对于新用户,(1)如果是一个注册用户并且已经登录,但没有发生任何用户行为(比如评论、分享、收藏、购买),那么我们可以拿他的注册信息,根据他的性别、年龄、所在地等信息进行推荐,也就是基于人口统计特征的推荐(Demographic-based);(2)如果是一个注册用户但没有登录,那么我们可以通过 cookie 去识别用户,这个时候 cookie 和 userId 有同等的功效,解决的办法也就和(1)类似了。顺便罗嗦几句,一个成熟的推荐系统能够做到在用户未登录的情况下也能进行推荐,这就告诉我们,标识用户的除了 userId,还有 cookie,所以可以在记录用户喜好的表里增加一列用来存储用户的 cookie,这个视实际情况而定(对于 SNS 站点,如微博需要登录才能使用,不存在此类问题,但是对于淘宝就存在此问题);(3)对于未注册的新访客,因为没有人口统计信息,所以一般通过一些热卖推荐、随机推荐引导用户浏览、点击、收藏、注册,让用户在网站上发生一些有价值的行为,然后再根据这些行为进行推荐。

对于新物品,只要有物品的属性,那怕没有任何人对它进行评分,也是可以进行推荐的,方法就是根据物品的属性去推荐,也就是基于内容的推荐(Content-based),通过物品的属性去计算物品之间的相关度。举个例子,如果新加入的物品是一本刚出版的《HBase: The Definitive Guide》,没有任何用户对它进行过评分或购买,假设这个时候有一个用户对《Hadoop in Action》有了很高的评分,那么我们就可以把《HBase: The Definitive Guide》推荐给这个用户,因为这两本书用着共同的属性和特点:计算机、分布式、大数据 ...

那么如何利用 Mahout 帮忙解决新用户和新物品的冷启动问题呢?一个简单的方法是聚类。对于新用户,我们根据他们的人口统计信息去聚类,把用户划分成一个一个的簇;新物品也是一样的,可以利用物品的属性,如果没有属性,可以对物品的介绍和描述进行分词,抽取出物品的属性和关键词描述,然后根据属性和关键词去聚类,把物品划分成一个一个的簇。Mahout 中对聚类有专门的实现,可以关注我后面的日记和博文。

你的最后一个问题,如何根据好友的喜好进行推荐?说直接点,也就是如何利用社交数据进行推荐?随着 SNS 的流行,这种推荐方法也逐渐被重视,推荐的方法也很简单:从社交网站获取用户的好友列表,然后在目标网站中搜索好友列表中每一个好友喜欢的物品,然后推荐给用户。通常用户的好友列表是来源于外部的 SNS 网站,最普遍的就是通过开放的 API 授权后获取(微博、QQ空间都是这样),而用户的喜好 userId、itemId、preference 存储在目标网站的 MySQL 或者缓存中。需要做的仅只是 查找、过滤、排序 那么简单。

推荐的书籍和资料:《Mahout in Action》、《推荐系统实践》,ReSysChina

Mahout之数据承载的更多相关文章

  1. Mahout--(一)数据承载

    mahout API英文解释:https://builds.apache.org/job/Mahout-Quality/javadoc/ 推荐数据的处理是大规模的,在集群环境下一次要处理的数据可能是数 ...

  2. Mahout之(三)相似性度量

    User CF 和 Item CF 都依赖于相似度的计算,因为只有通过衡量用户之间或物品之间的相似度,才能找到用户的“邻居”,才能完成推荐.上文简单的介绍了相似性的计算,但不完全,下面就对常用的相似度 ...

  3. Apache mahout 源码阅读笔记--DataModel之FileDataModel

    要做推荐,用户行为数据是基础. 用户行为数据有哪些字段呢? mahout的DataModel支持,用户ID,ItemID是必须的,偏好值(用户对当前Item的评分),时间戳 这四个字段 {@code ...

  4. LTE 切换过程中的数据切换

    http://blog.sina.com.cn/s/blog_673b30dd0100j4p4.html LTE中的切换,根据无线承载(Radio Bearer)的QoS要求的不同,可以分为无缝切换( ...

  5. 转】Mahout学习路线图

    原博文出自于: http://blog.fens.me/hadoop-mahout-roadmap/ 感谢! Mahout学习路线图 Hadoop家族系列文章,主要介绍Hadoop家族产品,常用的项目 ...

  6. Mahout机器学习平台之聚类算法具体剖析(含实例分析)

    第一部分: 学习Mahout必需要知道的资料查找技能: 学会查官方帮助文档: 解压用于安装文件(mahout-distribution-0.6.tar.gz),找到例如以下位置.我将该文件解压到win ...

  7. mahout第一篇-----Mahout学习路线图

    Mahout学习路线图 前言 Mahout是Hadoop家族中与众不同的一个成员,是基于一个Hadoop的机器学习和数据挖掘的分布式计算框架.Mahout是一个跨学科产品,同时也是我认为Hadoop家 ...

  8. Mahout学习路线图

    转自:http://blog.fens.me/hadoop-mahout-roadmap/ Mahout学习路线图 Hadoop家族系列文章,主要介绍Hadoop家族产品,常用的项目包括Hadoop, ...

  9. Mahout kmeans聚类

    Mahout  K-means聚类 一.Kmeans 聚类原理 K-means算法是最为经典的基于划分的聚类方法,是十大经典数据挖掘算法之一.K-means算法的基本思想是:以空间中k个点为中心进行聚 ...

随机推荐

  1. 分布式开放消息系统(RocketMQ)的原理与实践

    分布式消息系统作为实现分布式系统可扩展.可伸缩性的关键组件,需要具有高吞吐量.高可用等特点.而谈到消息系统的设计,就回避不了两个问题: 消息的顺序问题 消息的重复问题 RocketMQ作为阿里开源的一 ...

  2. 【原创】自己动手写工具----XSmartNote [Beta 1.0]

    一.背景 有个朋友是在他们单位的市场部,手里的策划文案以及PPT,少则数百,多则上千,多年下来也是一笔不可小觑的财富,每一次新的策划都可以从以往的文案或PPT中“拿来主义”,有着很好的借鉴意义,但是这 ...

  3. AnjularJS系列6 —— 过滤器

    第六篇,过滤器 AngularJS 过滤器可用于转换数据: 过滤器 描述 currency 格式化数字为货币格式. filter 从数组项中选择一个子集. lowercase 格式化字符串为小写. o ...

  4. Android SharedPreferences存储

    原创文章,转载请注明出处:http://www.cnblogs.com/baipengzhan/p/Android_SharedPreferences.html 一 概念 SharedPreferen ...

  5. 通过SmartGit把java maven项目传到码云

    一.首先先在码云上新建一个项目 二.复制项目的链接 三.打开SmartGit,点击clone 4.把复制的项目链接粘上去 5.然后点两次next,选择一个路径,finish 6.打开刚刚选择的路径,我 ...

  6. JMeter 问题

    1.  JMeter 测试计划 测试计划 使用 JMeter 进行测试的起点,是其它 JMeter 测试元件的容器. 线程组 代表一定数量的并发用户,它可以用来模拟并发用户发送请求.实际的请求内容在S ...

  7. javascript 数字格式化

    对数字进行格式化输出,是非常有意义的一件事情,例如许多时候,我们希望一个数字能够输出为指定格式的字符串,拿25.9878来说,我们可能会希望它能保留两位小数来说出,即结果为25.99,或者对于0.34 ...

  8. Forward+

    http://aras-p.info/blog/2012/03/02/2012-theory-for-forward-rendering/ http://www.slideshare.net/taka ...

  9. Maven的安装及更改下载仓库

    之前在MyEclipse这个IDE中配置Maven,完成配置后启动Maven时出现-Dmaven.multiModuleProjectDirectory system propery is not s ...

  10. [Java] JSP笔记 - 自定义标签

    自定义标签的创建步骤: 自定义标签的四大功能: 自定义标签的类结构: 在 1.0 中呢, 可以将 <body-content> 的值设置为 JSP, 2.0中则不允许在自定义标签体中出现j ...