概述
3
推荐器的数据表达
本章概要:
Mahout的推荐数据如何呈现
DataModel 的实现和使用
布尔型的偏好数据处理
推荐结果的好坏取决于数据的数量和质量。“巧妇难为无米之炊”用在这里再合适不过了。数据质量高本身是好事,而且数据量大也是好事。推荐算法天生就是数据密集型的,它们擅长处理大数据。算法运行的性能和数据的质量和呈现形式直接相关。一个好的数据结构可以影响算法的处理能力、处理规模以及很多其他性能。
本章探索了一些关键的类,这些类主要负责呈现数据、访问推荐器相关数据。你将会了解为何Mahout提供的数据表达方式会影响整个框架的效率和可扩展性。本章将会详细介绍一个Mahout中关键的概念,它提供了数据访问功能:DataModel。
最后,我们将会讨论布尔型的偏好数据,这里没有“偏好指数”这个概念,所以它需要特殊的处理方法。接下来我们将介绍推荐数据的基本单元:“用户-项目 偏好指数”(user-item preference)。
3.1 偏好指数的数据表达
我们向推荐器输入的偏好指数是一个数值,代表了“喜欢到什么程度”。数据的结果十分简单,就是用户-项目-偏好指数,它的数据量往往非常大,有时候还会省略偏好指数。
3.1.1 Preference对象
Preference是一个抽象的接口,包括user ID、item ID和一个偏好值。它表示一个用户对一个项目的喜好程度。它作为接口最常被GenericPreference去实现。举个例子,你要去表达用户123对项目456喜好程度是3.0,那么你应该这么定义:new GenericPreference(123, 456, 3.0f)。
那么如何去表达一个偏好的集合呢?如果你给出的答案是:Collection<Preference> 或 Preference[],那么只能说你错了,而且很多Mahout中的API都不是这么做的。集合和数组的操作对于呈现大量的Preference对象效率是非常低的,如果你从来没有仔细调查过java高层对象,那么准备好承受打击吧!
一个GenericPreference对象包含20个有用的字节:8个字节用来存储用户ID(java long),8个字节用来存储项目ID(long),最后4个字节存储偏好指数(float)。整个对象足足有28个字节,吃了一惊吧。而且它的大小会根据JVM的实现不同而变化。我说的这个数据是参照了苹果64位Java 6 VM forMac OS X 10.6,它还包括了8字节的对象引用,而且由于一些其他原因还有20字节需要占用。因此GenericPreference对象已经多消耗了140%的存储了,仅仅因为它被封装的太厉害了。
那么接下来怎么办?在推荐器中调用这样的对象是十分平常的事情,但是我们不难看出,在一个偏好集合之中,用户的ID或者项目ID很多都是冗余的。
3.1.2 PreferenceArray的实现
PreferenceArray是偏好集合的一个接口,它提供了一些类似数组中的API。举个例子,GenericUserPreferenceArray代表了所有的偏好个一个用户关联。在内部它只包含一个用户ID和多个项目ID以及一个偏好指数的浮点型数组。边际内存要求每个偏好占12字节(项目ID8字节,偏好指数4字节)。现在和之前的那个48字节的Preference对象比一比,GC显示出了四重偏好不仅仅是更快了,而且分配的对象也更加少了。比较一下图3.1和3.2,理解这样的节省是如何实现的。
图3.1 低效的偏好数组的实现。灰色代表高层类的内存消耗,白色代表有用的数据,也包括累的引用。
图3.2 高效的存储方式—GenericUserPreferenceArray
下面的代码展示了PreferenceArray的典型构造方法。
清单3.1 PreferenceArray的偏好设置方法
1
2
3
4
5
6
7
|
PreferenceArray user1Prefs =
new
GenericUserPreferenceArray(
2
);
user1Prefs.setUserID(
0
, 1L); A
user1Prefs.setItemID(
0
, 101L);
user1Prefs.setValue(
0
,
2
.0f); B
user1Prefs.setItemID(
1
, 102L);
user1Prefs.setValue(
1
,
3
.0f); C
Preference pref = user1Prefs.get(
1
); D
|
A 设置用户ID为1
B 用户1对项目101感兴趣,偏好指数2.0
C 用户1对项目102感兴趣,偏好指数3.0
D 为项目2实例化一个偏好对象
另外还有一个对象GenericItemPreferenceArray,它是吧所有的偏好都关联到一个项目上去,它的用法和用户的很类似。
3.1.3 加速收集
你可能觉得万事大吉了,Mahout自己改造了一个对象数组。但是你高兴的太早了,这还没有完。你还记得我们之前提到过规模的重要性吗?但愿你已经说服自己你将要面临的问题都是非比寻常的大数据,所以我们需要一个非比寻常的答案。
PreferenceArray为了减少占用内存使其实现变得复杂是值得的。减少75%的内存不仅仅是减少了百万字节。其实在适当的场合它可以减少万兆字节,这就是他是适合与不适合在你硬盘上处理的区别,或许,这就是你需不需要在多插几个内存条或者重新搞一台服务器的区别了。改动虽小,但是真的很低碳环保!
3.1.4 FastByIDMap与FastIDSet
当你听说Mahout用了很多经典数据结构,比如map和set,请不要吃惊,当然他没有用Java中的一些数据结构,比如TreeSet、HashMap。当你阅读它的API之后,可能你会发现FastByIDMap 和FastIDSet。它们两个非常像Map和Set。但是它们是专门为推荐器设计的,其目的是为了减少内存占用,而不是提升性能。
我们之前所做的工作不是为了苛求Java关于集合的架构。相反,这些对象和方法是为了在更宽泛的情形保持高效。它们不可能假设很多特定的场景去设计。Mahout所要使用的更为特别、专门。它做了很多严格的假设,主要的区别在于:
FastByIDMap 和HashMap一样,也是基于哈希的。它使用了线性散列而不是分离链接散列法去解决冲突。这就避免了额外的Map实体对象的引入,就像我们之前所讨论过的,对象往往会消耗掉很多内存空间。
键和值都采用了long类型,而不是对象。使用long类型的键可以节约空间和提升性能。
Set的底层实现并没用使用Map。
FastByIDMap可以被用作是缓存,因为它有一个最大容量的概念。当超过这个容量时,不经常用的单元将会被移除。
另外,存储方面有很大的不同:FastIDSet每个成员需要大概14个字节,而HashSet需要84个字节。FastByIDMap每个实例消耗28字节,而HashMap为84字节。所以说,当你考虑了使用的特定情形,那么性能的提升空间是非常大的。它们为推荐器节约了很多空间,而这一点优化意义也超出了优化本身。那么,这样聪明的设计会在哪里大显神通呢?
3.2 内存中的数据模型
DataMode是推荐器总输入数据的一种抽象。推荐算法需要用它来实现高效的访问数据。例如,DataModel可以在输入数据中提供一列用户,或者提供与某项目关联的所有偏好值,也或者提供对一个项目集合感兴趣的所有用户ID。本小节精选出一些关于DataModel的API来做一番介绍,这些API可以再官方文档中查阅到详细说明。
3.2.1 GenericDataModel
GenericDataModel是最简单的数据模型的实现,它是“内存版”的。当你希望在内存中构建你的数据模型时,它比较适合你,而非基于硬盘中的文件和数据库。它所接收的偏好是以FastByIDMap存储用户ID去映射PreferenceArrays而实现的。
清单3.2 用GenericDataModel去定义输入数据模型的示例代码
FastByIDMap<PreferenceArray> preferences =
new FastByIDMap<PreferenceArray>();
PreferenceArray prefsForUser1 = new GenericUserPreferenceArray(10); A
prefsForUser1.setUserID(0, 1L);
prefsForUser1.setItemID(0, 101L); B
prefsForUser1.setValue(0, 3.0f); B
prefsForUser1.setItemID(1, 102L);
prefsForUser1.setValue(1, 4.5f);
… (8 more)
preferences.put(1L, prefsForUser1); C
DataModel model = new GenericDataModel(preferences); D
A 为用户1定义一个PreferenceArray
B 添加10个偏好
C 将偏好集合与用户1关联
D 创建数据模型对象DataModel
GenericDataModel会占用多少内存呢?存储偏好是其主要的内存消耗。相关测试表明,每个偏好平均在Java heap中占28字节。其中除了包含这些信息还包含了一些索引。如果你愿意的话可以在加载一个GenericDataModel后调用System.gc()几次,然后比较Runtime.totalMemory()和Runtime.freeMemory()的结果。虽然方法比较粗糙,但是它给出了对数据内存消耗合理的解释。
3.2.2 基于文件的数据
一般情况下你可能不会直接使用GenericDataModel,而你在使用FileDataModel时会遇到它,因为FileDataModel会从文件中读取数据然后存放在内存中,这时GenericDataModel就登场了。
一般的文件都会像第一章中讲述的那样,存放着用逗号隔开的数据记录,每一行都包括一个用户ID、项目ID、偏好指数。标签文件(Tab-separated)、“.zip”或“.gz”文件也是如此。顺便提一句,采用压缩文件个很不错的方法,因为数据量往往比较大,压缩显得格外节约空间。
3.2.3 Refreshable组件
当谈了关于加载数据的一些内容,重新加载数据也是非常有用的,相关接口为Refreshable。它由一些推荐相关组件实现。它仅仅有一个方法refresh(Collection<Refreshable>)。它可以在询问最新数据的依赖关系后,来简单的请求重新载入组件、重新计算以及刷新自己的状态。例如,一个Recommender对象可以对它所使用的DataModel调用refresh()来对数据重新计算内部索引。循环依赖和共享依赖被很好的处理,如图3.3所示:
图3.3 基于用户的推荐系统各组件之间的简单依赖关系,箭头所示为组件刷新数据的次序
注意一点,出于性能的考虑,FileDataModel只有被请求更新时才会重新加载数据文件,它不会自动的检测文件更新或者定期的去加载文件。这也正是为何会存在一个refresh()方法。你可能不太愿意去使用FileDataModel本身或者其他依赖于数据的对象去调用refresh(),那么用推荐器直接去调用可能是个不错的选择:
清单3.3 为推荐系统触发一次更新
DataModel dataModel = new FileDataModel(new File("input.csv");
Recommender recommender = new SlopeOneRecommender(dataModel);
...
recommender.refresh(null); A
A 先更新DataMode,然后再更新它自己
关于规模的讨论遍及本书的每个角落,我们将强调一个关于FileDataModel对象的另一个重要特性:“更新文件”。文件的更新往往是很少部分的更新,或者说,上亿条数据之中可能才有一条。如果对于仅有少量更新的大文件完全的拷贝,将是毫无效率而言的。
3.2.4“更新文件”(Update files)
FileDataModel支持一种“更新文件”。这是一种在读取主数据文件之后可以对其进行重写的文件。它可以表示:新的数据记录被添加;已存在的记录被更新,而被删除的数据吧偏好指数置空即可。
举了例子,下面是一个“更新文件的例子”:
清单3.4 更新文件样例
1,108,3.0
1,103,
上述文件内容代表更新(添加)用户1对项目108的偏好值3.0,然后删除用户1对项目103的偏好记录。
这些“更新文件”必须存在于主数据文件的所在目录中,它们的名称需要以主数据文件的前缀相一致。如果主数据文件名为foo.txt.gz,那么更新文件的名字应该为foo.1.txt.gz和foo.2.txt.gz。需要指出的是,这些数据文件可以是压缩的。
3.2.5 基于数据库的数据
有时候数据量可以达到连内存也装不下的地步。一旦偏好记录达到上千万的级别,内存就需要几千兆字节的空间。这样的内存在一些条件下是无法满足的。
还好Mahout支持从数据库中获取数据。Mahout中的很多类都是为了在数据库中去计算数据而实现的。
需要注意的是,在数据库中运行不同量级的推荐器要比在内存运行慢得多。这不是数据库的错;经过恰当的配置,数据库在索引和检索信息方面的效率可以表现的十分优秀,但是相比之下,它的检索、整理、序列化、传输以及反序列化过程的开销还是要比优化后内存中的数据结构要大得多。这些作为数据推进器大大提升了推荐算法的速度。除了在别无选择或者数据量不大并且因为集成需要而使用一张表格的数据的时候,它还是十分令人满意的。
3.2.6 JDBC与MySQL
我们可以通过JDBCDataModel来访问偏好数据。它的唯一子类MySQLJDBCDataModel.用来访问MySQL 5.x。它可以很好的访问这一系列版本的MySQL,甚至其他的数据库,因为它内部使用了ANSI SQL。如果需要的话,创建一个变种版本也不是很难,只要你用特定数据库的SQL语法。
表3.1 ‘taste_preferences’在MySQL中的默认表模式
默认的,程序认为所有的偏好信息都存在taste_preferences表中,字段user_id、item_id、preference分别代表了用户ID、项目ID和偏好指数。
3.2.7 由JNDI进行配置
另外我们假设包含这个表的数据库也可以被一些在JNDI中注册的DataSource对象访问,叫做jdbc/taste。什么是JNDI,你会有这个疑问吗?如果你在一个Web应用程序中应用推荐引擎,而且用了servlet容器,像Tomcat或者Resin,那么你很可能已经间接的用到它了。如果你在Tomcat上配置过数据库(server.xml),那么你将会发现典型配置中会出现JNDI的身影。你可以把你的数据库配置成jdbc/taste,这样JDBCDataModel就可以用了。下面是一段有用的Tomcat上的配置片段:
清单:3.5 Tomcat上的JNDI配置
<Resource
name="jdbc/taste"
auth="Container"
type="javax.sql.DataSource"
username="user"
password="password"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mydatabase"/>
上面的一些默认名字可以根据你的环境重写。你不需要对数据库和它的字段进行命名。
3.2.8 由代码方式配置
你也可以不必用JNDI进行配置。下面有个很有用的关于配置MySQLJDBCDataModel的例子,它使用了MySQL的连接驱动(http://www.mysql.com/products/connector/)以及和一个自定义表格的DataSource。
清单 用代码配置DataSource
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setServerName("my_database_host");
dataSource.setUser("my_user");
dataSource.setPassword("my_password");
dataSource.setDatabaseName("my_database_name");
JDBCDataModel dataModel = new MySQLJDBCDataModel(
dataSource, "my_prefs_table", "my_user_column",
"my_item_column", "my_pref_value_column");
上面是为推荐引擎访问数据库的所有代码。你通过它会得到一个匹配的DataModel。然而在MySQLJDBCDataModel相关文档中声明过,在为推荐引擎正确配置数据库和数据驱动时,应该尤为注意:
用户ID和项目ID都不能为空,而且需要建立索引
用户ID和项目ID为联合主键
列的数据类型在Java中应为long和float型。在MySQL中应为BIGINT和FLOAT类型
注意调优缓存和查询缓存(查看javadoc)
使用MySQL的连接驱动时,应该注意一些参数值的设置,例如cachePreparedStatements要设置为true。然后参照javadoc查看建议值设置
上述内容基本涵盖了DataModel在推荐引擎框架中的基本应用。另外有一点应该提一下:有时候偏好值是可以为空的。可能你觉得很奇怪,因为偏好值是一条记录的最核心的数据。然而有时候这种表达也是相当有用的。
3.3 处理偏好值为空的数据(布尔偏好)
有时推荐引擎中出现偏好值为空的记录。它代表了用户和项目是关联的,但是并没有表现出关联程度。举了例子,一个新闻网站根据用户已阅读内容为用户推荐新闻。“已阅读”使一个用户和一个项目产生了关联,然而这是唯一能够获取的信息。一般网站也不会让用户去给文章做个排序,更不会让用户再做除了阅读之外的其他什么事了。所以我们仅仅知道用户和那些文章关联了,而再也没有其他的内容了。
面对这样的情形,我们别无选择。这里不会有偏好值。后续几章将会依然提供处理如此情形的技术和建议。然而有时我们忽略掉偏好值也未尝不是坏事,只要情形需要。
丢掉用户和项目之间的联系很容易,或者说我们可以直接忽略偏好值就可以办到。比如,你宁可考虑有没有看过一个电影,也不愿去给一个电影打分。换句话说,我们宁愿把数据改为“用户1和项目3有关系”,也不愿意写成“用户1喜欢项目103的程度为4.5”。下面是一个示意图,来说明二者之间的区别:
图3.4 带偏好值的数据(左)与布尔偏好数据(有)的区别
用Mahout的语言,没有不带偏好值的记录就是布尔偏好,因为一个关联只有两种值:要么有关联,要么没关联。这不代表偏好只有两个值:是和否,而是有三个:喜欢、不喜欢、不知道。
3.3.1 什么时候应该忽略偏好值
为何有时会忽略偏好值?因为会有这样的情形,用户喜欢或者不喜欢一个项目的程度相对一致,至少与那些和用户没有关联的项目相比。还记得那个例子吗?一个家伙不喜欢拉赫玛尼诺夫(Rachmaninoff),当然世上有很多曲子他没有听过(比如死亡金属音乐(Norwegian death metal))。这样我们只得到了他与这个作家的一个关联,恰巧他又喜欢另一个作家布拉姆斯,然后他把拉赫玛尼诺夫标为1,把布拉姆斯标为5,和其余未知作家相比,这样的偏好值是没有什么意义的,以为二者在某种程度上是对等的。所以我们索性不要这个偏好值,仅仅是喜欢、不喜欢、不知道或者说未知的关系。
你可能会说这都是用户的错。难道他就不能为拉赫玛尼诺夫打4分而为死亡金属打1分。也许可以,但你还是认了吧!因为事实上推荐所需要输入的数据是很难确定的。你或许还要提出反对,虽然对于所有流派都可以这样去推理,但是当你要向他推荐古典作曲家时该依据哪些数据呢?没错,在某领域中的好的解决方案一般不会公开出来。
3.3.2 布尔偏好在内存中的数据表达
抛去偏好值可以很大程度上化简数据表达,当然也会提升性能、占用更少的内存。像看我之前了解的,在Mahout中需要用4个字节的浮点数去存储偏好值,至少如果没有偏好值也就意味着我们每一条会少用4个字节。在内存方面的测试中也确实平均每条记录都减少了4个字节到24字节了。
这个测试用的是GenericDataModel的双胞胎兄弟GenericBooleanPrefDataModel。它看上去是一个内存版的实现,但是它没有偏好值。事实上,它仅仅用FastIDSets去存储一个用户和项目的关联。例如,只为每个用户存储和他关联的所有项目ID,这里不会再出现偏好值。
因为GenericBooleanPrefDataModel也是DataModel对象,所以它完全可以直接代替GenericDataModel。一些数据模型的方法在这种实现下会变得快速高效,比如:getItemIDsForUser(),因为该方法在它里面取项目ID是现成的。某些方法也会变慢,比如:getPreferencesFromUser(),因为它的实现没有使用PreferenceArrays,而且必须实现以下该方法。
你可能会好奇getPreferenceValue()在这里可以返回什么?因为这里不是没有偏好值吗?你调用该方法并不会抛出UnsupportedOperationException的异常,它会恒定的返回一个虚假值1.0。这一点值得注意,每个组件都会依赖于一个偏好值,它们必须从DataModel对象中获取一个。这些伪造而且固定的值也会产生一些微妙的问题。
让我们在文章最后来观察一下GroupLens例子的结果。下面是使用了GenericBooleanPrefDataModel的代码片段:
清单 3.7 用布尔偏好值创建和评估一个推荐器
DataModel model = new GenericBooleanPrefDataModel(
GenericBooleanPrefDataModel.toDataMap(
new FileDataModel(new File("ua.base")))); A
RecommenderEvaluator evaluator =
new AverageAbsoluteDifferenceRecommenderEvaluator();
RecommenderBuilder recommenderBuilder = new RecommenderBuilder() {
public Recommender buildRecommender(DataModel model)
throws TasteException {
UserSimilarity similarity = new PearsonCorrelationSimilarity(model);
UserNeighborhood neighborhood =
new NearestNUserNeighborhood(10, similarity, model);
return
new GenericUserBasedRecommender(model, neighborhood, similarity);
}
};
DataModelBuilder modelBuilder = new DataModelBuilder() {
public DataModel buildDataModel(
FastByIDMap<PreferenceArray> trainingData) {
return new GenericBooleanPrefDataModel(
GenericBooleanPrefDataModel.toDataMap(trainingData)); B
}
};
double score = evaluator.evaluate(
recommenderBuilder, modelBuilder, model, 0.9, 1.0);
System.out.println(score);
A 基于相同的数据,使用GenericBooleanPrefDataModel
B 建立一个GenericBooleanPrefDataModel
这里的转折是DataModelBuilder。它可以使评估程序为训练数据构造DataModel对象,而不是去构造一个GenericDataModel对象。GenericBooleanPrefDataModel用一种稍微不同的方式去获取数据——用一些FastIDSets而不是PreferenceArrays。而且它还提供了一个很方便的方法toDataMap()用来对二者进行转换。在进入下一小节时,尝试运行一下这个程序,他将不会很成功的执行完毕。
3.3.3 选择匹配的实现
你会发现在PearsonCorrelationSimilarity的构造方法中抛出了IllegalArgumentException。刚开始你可能觉得很不可思议,GenericBooleanPrefDataModel难道不也是DataModel对象吗?它与GenericDataModel的唯一区别不就是没有偏好值吗?
这个相似度度量对象EuclideanDistanceSimilarity拒绝在没有偏好值的情况下工作,因为这样产生的结果是无意义的。皮尔森相关系数(Pearson correlation)在两个数据集数据完全重复、一样时就是没有意义的。在这里DataModel的偏好值全部为1.0,那么计算出来的欧几里得距离只能在一个点空间(1.0, 1.0, …, 1.0)中进行,这样是毫无意义的,因为所有的相似度都是0。
大体上讲,不是所有的实现都可以在一起很好的匹配在一起,甚至都实现自一个接口的对象也不可能完全匹配。为了解决这个当前问题,我们需要替换掉这个计算相似度的类。LogLikelihoodSimilarity就是一个好的选择,因为它不需要偏好值,我们将马上讨论它。在程序中用它替换PearsonCorrelationSimilarity,这个结果就变成了0.0。很不错吧!这意味着它的结果正确了,不过这真的很好吗?
不幸的是,所有的偏好值都是1当然会导致所有的偏好相差为0了。这个测试本身没有什么意义,因为它将永远都是0。
然而,查准-召回评估还是依然有效的,让我们来试一下吧:
清单3.8 使用布尔数据评估查准率和召回率
DataModel model = new GenericBooleanPrefDataModel(
new FileDataModel(new File("ua.base")));
RecommenderIRStatsEvaluator evaluator =
new GenericRecommenderIRStatsEvaluator();
RecommenderBuilder recommenderBuilder = new RecommenderBuilder() {
@Override
public Recommender buildRecommender(DataModel model) {
UserSimilarity similarity = new LogLikelihoodSimilarity(model);
UserNeighborhood neighborhood =
new NearestNUserNeighborhood(10, similarity, model);
return new GenericBooleanPrefUserBasedRecommender( model, neighborhood, similarity);
}
};
DataModelBuilder modelBuilder = new DataModelBuilder() {
@Override
public DataModel buildDataModel(FastByIDMap<PreferenceArray> trainingData) {
return new GenericBooleanPrefDataModel(
GenericBooleanPrefDataModel.toDataMap(trainingData));
}
};
IRStatistics stats = evaluator.evaluate(
recommenderBuilder, modelBuilder, model, null, 10,
GenericRecommenderIRStatsEvaluator.CHOOSE_THRESHOLD, 1.0);
System.out.println(stats.getPrecision());
System.out.println(stats.getRecall());
评估结果为查准率和召回率均为15.5%,这个结果并不理想。这意味着推荐结果中6个只有1个是好的推荐,6个好的推荐中只推荐出了1个。
这引出了第三个问题,偏好值依然潜伏于GenericUserBasedRecommender对象之中。由于推荐结果按照偏好值排序,而偏好值全是1.0,所以这样的次序是完全随机的。所以我们引出GenericBooleanPrefUserBasedRecommender(是的,正如它名字一样,它可以办到)。它可以在推荐结果中产生一个很有意义的排序。它为每个项目关联于其他相似的用户加权重,用户越相似,那么权重也会越高。它没有提供一个加权平均数。替换之后在运行代码,结果大概在18%,好了一点,但没有好太多。这种结果预示着推荐系统对于这些数据不是非常有效,我们的目的不是要去修改它,仅仅是去看如何在Mahout中有效的部署这样带有“布尔偏好”的数据。
其他DataModel对象的布尔变种也是有的。FileDataModel在记录中没有偏好值的情况下内部会自动的使用一个GenericBooleanPrefDataModel的对象。类似的,MySQLBooleanPrefDataModel适合用在数据库中无偏好值的情形。它与之前的对象完全相似,而且它的实现可以充分的利用数据库提供的某些捷径来提升自身的性能。
最后,你如果好奇布尔型和非布尔型是不是可以混在一起处理,答案是否定的。在这种情形下,我们更倾向于按照存在偏好值的情形去处理,因为毕竟有些偏好值是存在的。那些没有偏好值的记录可能或者应该可以通过某些手段推测出它的偏好值,甚至我们可以吧平均偏好值填进去当做一个占位符。
3.4 总结
这一章我们看到了数据在Mahout的推荐器中如何去表达、呈现。这里包括了Preference对象,酷似集合实现的特殊数组PreferenceArray以及FastByIDMap。这些特定定制的对象很大程度上减少了内存的消耗。
再看看DataModel,它是推荐器输入数据的一种抽象封装。在FileDataModel读取完本地文件之后,GenericDataModel在内存中存储这些数据。JDBCDataModel用来访问数据库的数据,而且我们还在MySQL上特意进行了测试。
最后我们讨论了在无偏好值的情况下,这些模型该如何的变化。一般上述这些对象都会用到,当然存储上会稍微少一些。我们列举了一些标准对象与该类数据不匹配的例子,如PearsonCorrelationSimilarity。最后通过检查问题的原因所在而解决了问题,目的是为了构建一个基于布尔偏好数据的推荐器。
最后
以上就是精明钻石为你收集整理的Mahout in action 中文版-3.推荐器的数据表达 3 推荐器的数据表达的全部内容,希望文章能够帮你解决Mahout in action 中文版-3.推荐器的数据表达 3 推荐器的数据表达所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复