Mahout实战之基本概念

来源:互联网 发布:轻云4g网络下怎么用 编辑:程序博客网 时间:2024/05/20 14:42

1.Mahout简介

Apache Mahout是一个来自Apache的开源的机器学习软件库。它所实现的算法归属于机器学习。Mahout是可扩展的,旨在当所处理的数据规模远大于单机处理能力时称为一种可选的机器学习工具。Mahout当前仅关注机器学习的三个主要领域,推荐、聚类和分类。

此处给出一个简单的Mahout推荐算法示例,下文将给出详细的解析:

package com.funkyz.mahout;import java.io.File;import java.util.List;import org.apache.mahout.cf.taste.impl.model.file.FileDataModel;import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity;import org.apache.mahout.cf.taste.model.DataModel;import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;import org.apache.mahout.cf.taste.recommender.RecommendedItem;import org.apache.mahout.cf.taste.recommender.Recommender;import org.apache.mahout.cf.taste.similarity.UserSimilarity;public class Test {    public static void main(String[] args) throws Exception {        String filepath="D:\\workspace\\mahout\\data\\userdata.csv";        DataModel model=new FileDataModel(new File(filepath));        UserSimilarity similarity=new PearsonCorrelationSimilarity(model);        UserNeighborhood neighborhood=new NearestNUserNeighborhood(2,similarity,model);        Recommender recommender = new GenericUserBasedRecommender(model, neighborhood, similarity);        List<RecommendedItem> recommendations = recommender.recommend(1, 3);        System.out.println(recommendations.size());        for(RecommendedItem item:recommendations){            System.out.println(item.toString());        }    }}

2.推荐数据的表示

推荐的质量很大程度上取决于数据的数量和质量,但推荐算法天生是数据密集型的,其计算设计大量信息的访问。因此,数据的数量和表达方式会很大程度上影响性能的执行,只能地选择数据结构能够极大地改善性能。这里我们将对Mahout如何表示和访问推荐程序相关数据时所用的关键类并解析Mahout中用户访问数据的关键抽象DataModel。

2.1 偏好数据的表示

推荐引擎的输入时偏好数据(preference data):通俗的讲就是那种类型的人喜欢那种类型的物品以及喜欢程度。直观的说这个输入就是一个用户ID、物品ID和偏好值的集合。

a. Preference对象

Preference对象是最基本的抽象,表示用户ID、物品ID和偏好值。一个对象代表一个用户对一个物品的偏好。在Mahout中,Preference是一个接口,最经常使用的具有Preference接口的类便是GenericPreference。例如:

//表示用户"123"对物品"456"的偏好程度是3.0new GenericPreference(123,456,3.0f);

实际情况中,偏好信息肯定是多组用户对应多组物品的偏好对应关系,那么这种多对多的信息该如何表示呢。这里我们可能会给出Collection或者Preference[]这样的集合或者数组,但是这种表达方式的开销是相当大的,因为
- 在Java中,一个对象占用的字节数 = 基本的8字节 + 基本数据类型所占的字节 + 对象引用所占的字节

因此在推荐算法中,我们并没有采用这种方法。因为,通常情况下在这种“用户-物品”的偏好聚合当中,所有Preference对象的用户ID或物品ID都是一样的,如果用集合或者数组存储这种信息就显得十分冗余。

b.PreferenceArray的实现

PreferenceArray同样是一个接口,它的实现表示一个偏好的聚合,具有类似数组的API。GenericUserPreferenceArray实现的PreferenceArray接口,它表示的是与某个用户关联的所有偏好,其内部包含一个单一用户ID、一个物品ID数组,以及一个偏好值数组。这种表示形式中,每个偏好的边界内存只需要12字节(物品数组ID:8bit;偏好值ID:4bit)

PreferenceArray userPrefs=new GenericUserPreferenceArray(2);userPrefs.setUserID(0,1L);userPrefs.setItemID(0,101L);userPrefs.setValue(0,2.0f);userPrefs.setItemID(1,102L);userPrefs.setValue(1,3.0f);Preference pref=userPrefs.get(1);
c.FastByIDMap和FastIDSet

Mahout的推荐程序中大量是用来Map和Set这些景点的数据结构,但它们用的并不是通常的Java集合的实现。相反,通览全部的实现与API,你会找到FastMap、FastByIDMap和FastIDSet。它们类似yuMap和Set,但做了特殊的定制,仅为满足Mahout中推荐程序的需要,主要特点是降低了对内存的占用。

2.2 DataModel

在Mahout中使用DataModel这种抽象机制对推荐程序的输入数据进行封装,而对DataModel的实现为各类推荐算法提供了对数据的高效访问。例如,DataModel可以提供输入数据中所有用户ID的计数或列表、提供与某个物品相关的偏好,或给出所有对一物品ID表达过偏好的用户的个数。

a. GenericDataModel

内存级实现GenericDataModel是现有DataMdoel实现中最简单的,它适用于通过程序在内存中构造数据的表现形式,而不是基于来自外部的数据源,例如文件或关系数据库。它简单地将偏好作为输入,采用FastByIDMap的形式,将用户ID映射到这些用户的数据所在的PreferencrArray上。

FastByIDMap<PreferenceArray> preferences=new FastByIDMap<PreferenceArray>();PreferenceArray user1Prefs=new GenericUserPreferenceArray(10);user1Prefs.setUserID(0,1L);user1Prefs.setItemID(0,101L);user1Prefs.setValue(0,2.0f);user1Prefs.setItemID(1,102L);user1Prefs.setValue(1,3.0f);//...剩下8条物品-偏好值数据preferences.put(1L,preferences);//在偏好集合中添加用户1的偏好信息DataModel model=new GenericDataModel(preferences);
b. 基于文件的数据

我们通常不会直接使用GenericDataModel,而是借助于FileDataModel,后者从文件中读取数据,并将所得到的偏好数据存储到内存中。几乎任何正常的文件都可以使用,如CVS等。文件内容的格式主要为:用户ID、物品ID以及偏好值。

c. 可刷新组件

重加载数据(reloading data),以及Refreshable接口,即在Mahout推荐程序相关类中所实现的几个组件。它只公开了一个方法refresh(Collection)。该方法简单地请求组件在最新的输入数据上进行:重加载、重计算并刷新自身状态,同时实现让它的以来也这样做。

d. 更新文件

FileDataModel支持更新文件,它们就是在读取主数据文件之后额外生成的数据文件,并可以覆盖任何以前读取的数据。通过添加来形成新的偏好,还可以更新现有偏好。通过设一个偏好值为空的字符串来实现删除。

e. JDBC和MySQL

3. 进行推荐

Mahout中包含了两类典型的推荐算法:基于用户的推荐算法和基于物品的推荐算法。这两种算法均依赖于两个事物(用户或物品)之间的相似性度量,或者说等同性定义。

相似性计算有很多种方法,包括皮尔逊相关系数、对数似然值、斯皮尔曼相关系数等。

  • 算法
for(用户u尚未表达的偏好) 每个物品i   for(对i有偏好的) 每个其他用户v      计算u和v之间的相似度s      按权重为s将v对i的偏好并入平均值    return 值最高的物品(按加权平均排序)

外层循环简单地把每个已知物品(用户未对其表达过偏好的)作为候选的推荐项。内层循环逐个查看对候选物品做过评价的其他用户,并记下他们对物品的偏好值。最终,将这些值的加权平均作为目标用户对该物品偏好的预测值。每个偏好值的权重取决于该用户与目标用户之间的相似度。与目标用户越相似,他的偏好值所占权重就越大。

for每个其他用户w   计算用户u和用户w的相似度s   按照相似度排序后,将位置靠前的用户作为邻域nfor(n中用户有偏好,而u中用户没有偏好的) 每个物品i   for(n中用户对i有偏好的) 每个其他用户v     计算用户u和用户v的相似度     按照权重s将v对i的偏好并入平均值   return 值最高的物品(按加权平均排序)

第二种算法与前面的主要区别在于首先确定相似的用户,再考虑这些醉相思用户对什么物品感兴趣。这些物品就称为推荐的后弦徐昂。

a.基于GenericUserBasedRecommender实现算法

一个简单的基于用户的推荐系统示例:

    String filepath="D:\\workspace\\mahout\\data\\userdata.csv";    DataModel model=new FileDataModel(new File(filepath));    UserSimilarity similarity=new PearsonCorrelationSimilarity(model);    UserNeighborhood neighborhood=new NearestNUserNeighborhood(2,similarity,model);    Recommender recommender = new GenericUserBasedRecommender(model, neighborhood, similarity);

UserSimilarity封装了用户间相似性的概念,而UserNeighborhood封装了最近相似用户组的概念,它们是标准的基于用户推荐算法的必要组件

相似性的定义不是唯一的,同样最近邻用户的选择也不是唯一的。随着这些度量的改动,结果就会发生明显的变化。因此,提供推荐的方式是多种多样的。Mahout是由多个组合混搭而成的,而并非单一的推荐引擎。通常包括:
- 数据模型,由DataModel实现
- 用户间的相似性度量,由UserSimilarity实现
- 用户邻域的定义,由UserNeighborhood实现
- 推荐引擎,由一个Recommender实现(上述代码的引擎为GenericUserBasedRecommender)

b. GroupLens数据集

网址为 www.grouplens.org

数据测试代码,用于评估推算引擎。

public class GroupLensTest {    public static void main(String[] args) throws Exception {        String datapath="D:\\workspace\\mahout\\data\\ratings.csv";        DataModel model=new FileDataModel(new File(datapath));        RecommenderBuilder recommenderBuider=new RecommenderBuilder(){            public Recommender buildRecommender(DataModel model) throws TasteException {                UserSimilarity similarity=new PearsonCorrelationSimilarity(model);                UserNeighborhood neighborhood=new NearestNUserNeighborhood(100,similarity,model);                return new GenericUserBasedRecommender(model, neighborhood, similarity);            }};        Recommender recommender=recommenderBuider.buildRecommender(model);        //LoadEvaluator.runLoad(recommender);        RecommenderEvaluator evaluator=new AverageAbsoluteDifferenceRecommenderEvaluator();        double score=evaluator.evaluate(recommenderBuider, null, model, 0.95, 0.05);    }}