zoukankan      html  css  js  c++  java
  • Mahout之数据承载

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

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


    Preference

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

    Preference
    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
    PreferenceArray
    GenericUserPreferenceArray
    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
    DataModel

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

    GenericDataModel
    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

    可以看到,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

  • 相关阅读:
    三大主流负载均衡软件对比(LVS+Nginx+HAproxy)
    nginx 提示the "ssl" directive is deprecated, use the "listen ... ssl" directive instead
    centos安装nginx并配置SSL证书
    hadoop创建目录文件失败
    The server time zone value 'EDT' is unrecognized or represents more than one time zone.
    脚本启动SpringBoot(jar)
    centos做免密登录
    数据库远程连接配置
    Bash 快捷键
    TCP三次握手四次断开
  • 原文地址:https://www.cnblogs.com/hongyedeboke/p/6051723.html
Copyright © 2011-2022 走看看