参考链接:个性化推荐算法总结:https://blog.csdn.net/Yasin0/article/details/89222622
Mahout 详解:https://blog.csdn.net/qq_36864672/article/details/78796295
用Maven构建Mahout项目:http://blog.fens.me/hadoop-mahout-maven-eclipse/
推荐系统算法实战(协同过滤CF算法):https://blog.csdn.net/universsky2015/article/details/103679861
mahout推荐6DataModel:https://www.cnblogs.com/jsunday/p/3889619.html
尚硅谷大数据教育之机器学习和推荐系统
mahout连接mysql,使用ReloadFromJDBCDataModel,采用SVD推荐算法:https://blog.csdn.net/pan12jian/article/details/26451495
Mahout中数据的存储方式:https://my.oschina.net/xiaominmin/blog/1599459
1 简单介绍
推荐看个性化推荐算法总结(还挺详细的,好像是推荐系统实战这本书上的)
1.2 推荐系统评测
2 Mahout
2.1 简介
用Mahout来构建推荐系统,是一件既简单又困难的事情。简单是因为Mahout完整地封装了“协同过滤”算法,并实现了并行化,提供非常简单的API接口;困难是因为我们不了解算法细节,很难去根据业务的场景进行算法配置和调优。
2.2 Maven Repository(使用maven构建mahout项目)
<dependency>
<groupId>org.apache.mahout</groupId>
<artifactId>mahout-core</artifactId>
<version>${mahout.version}</version>
</dependency>
<dependency>
<groupId>org.apache.mahout</groupId>
<artifactId>mahout-math</artifactId>
<version>${mahout.version}</version>
</dependency>
<dependency>
<groupId>org.apache.mahout</groupId>
<artifactId>mahout-integration</artifactId>
<version>${mahout.version}</version>
<exclusions>
<exclusion>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.cassandra</groupId>
<artifactId>cassandra-all</artifactId>
</exclusion>
<exclusion>
<groupId>me.prettyprint</groupId>
<artifactId>hector-core</artifactId>
</exclusion>
</exclusions>
</dependency>
2.3 使用mahout实现推荐功能
Mahout中和推荐系统相关内容有四个部分:
Preference类: 表示每个用户或者物品的属性, 如{1: {2: 1.0, 3: 2.0}} 表示用户2和3分别对1进行了评价, 评分分别为1.0和2.0
DataModel类: 表示所有的数据集合, 即Preference的聚合. DataModel可以是代码输入或者HTTP参数获取, 也可以从文件或者数据库中读取.
Similarity类: 表示所有Preference两者之间的相似度, 所有的用户或者物品都通过Similarty建立起了联系, 所以这个是非常重要的, Similarity的选择和数据的类型有很大关系. Neighberhood记录每个用户或者物品相似的物品集, 方便后期调用.
Recommender类: 表示推荐算法, 整个系统的核心。 如何使用前面的Preference, DataModel, Similarity以及Neigherhood都将结合在Recommender来来使用。
一个优秀的推荐系统由以下五个部分构成:
1. 高质量和高数量的数据. Garbage in, Garbage out.
2. 高效存储的数据结构以及高效的数学运算库
3. 优秀的算法架构和调参技术
4. 优秀的架构设计, 防止数据的多次拷贝, 并且利于数据的更新和一致性.
5. 优秀的用户体验和人机交互设计
2.3.1 userbasedCF(基于用户的协同过滤)
用户A喜欢物品1、2、3、4、5、6、7、8
用户B喜欢物品1、2、3、4、5、6、7
用户A和B喜欢的东西很相似,所以用户A喜欢的东西,用户B也可能喜欢
故用户A喜欢物品8,向用户B也推荐物品8
基于用户的协同过滤算法思路:分为三步,分别是①建立用户矩阵模型;②通过用户相似度寻找最近邻居;③产生推荐项目。
/*
* 基本的过程(步骤)
* 1.分析各个用户(user)对物品(item)的评分
* 2.根据用户对物品的评分,计算所有用户之间的相似度(余弦相似度、欧式距离等等)
* 3.选出与当前用户最相似的N个用户
* 4.将当前用户购买过的商品,推荐给其他用户:反之也行
*/
public class UserBasedCF {
final static int NEIGHBORHOOD_NUM = 2;
final static int RECOMMENDER_NUM = 3;
public static void main(String[] args) throws Exception {
//根据数据源建立数据模型,即:打分矩阵
String file = "D:\\download\\data\\ratingdata.txt";
//1. 创建模型
DataModel model = new FileDataModel(new File(file));
//2. 创建 UserSimilarity 对象
//根据打分矩阵,计算用户的相似度
UserSimilarity usersimilarity = new EuclideanDistanceSimilarity(model);
//3. 创建 NearestNUserNeighborhood 对象 例:Neighborhood
//找到与用户相近的邻居,即:找到NEIGHBORHOOD_NUM的相邻用户
//n:找到相似的N个用户
NearestNUserNeighborhood neighbor = new NearestNUserNeighborhood(NEIGHBORHOOD_NUM, usersimilarity, model);
//4. 创建推荐器对象 例:UserBasedRecommender
// 构建基于用户的推荐引擎,其中dataModel为数据模型,neighborhood为用户领域模型,usersimilarity为相似度模型
Recommender r = new GenericUserBasedRecommender(model, neighbor, usersimilarity);
System.out.println("***************测试一:给一个用户推荐****************");
//5. 推荐商品给用户
//向用户1推荐两个商品 说明:recommender.recommend(userID, howMany)
List<RecommendedItem> recommendations = r.recommend(1, 2);
for (RecommendedItem recommendation : recommendations) {
// 输出推荐结果
System.out.println("给用户1推荐的商品是:" + recommendation.getItemID());
}
System.out.println("***************测试二:给每个用户都推荐****************");
//向每个用户推荐商品
LongPrimitiveIterator iter = model.getUserIDs();
while (iter.hasNext()) {
long uid = iter.nextLong();
System.out.printf("用户ID:%s ", uid);
List<RecommendedItem> list = r.recommend(uid, RECOMMENDER_NUM);
for (RecommendedItem ritem : list) {
System.out.printf("(%s,%f)", ritem.getItemID(), ritem.getValue());
}
System.out.println();
}
}
}
2.3.2 itembasedCF(基于商品的协同过滤)
喜欢物品1的人,例如用户A、B、…,发现他们也都喜欢物品2
说明一般喜欢物品1的人,也喜欢物品2
故用户C喜欢物品1,那么他可能也会喜欢物品2
故向用户C推荐物品2
public class ItemBasedCF {
public static void main(String[] args) throws IOException, TasteException {
//读入(用户-物品-评分)数据,建立打分矩阵
String file = "D:\\download\\data\\ratingdata.txt";
DataModel dataModel = new FileDataModel(new File(file));
//计算物品的相似度矩阵
ItemSimilarity itemSimilarity=new EuclideanDistanceSimilarity(dataModel);
Recommender r=new GenericItemBasedRecommender(dataModel, itemSimilarity);
LongPrimitiveIterator iter=dataModel.getUserIDs();
while(iter.hasNext()){
long uid=iter.nextLong();
List<RecommendedItem> list=r.recommend(uid, 3);
System.out.println();
for (RecommendedItem item : list) {
System.out.println(""+item.getItemID());
}
System.out.println("******");
}
}
}
2.3.3 datamodel从数据库中读取
将这两行
// String file = "D:\\Code\。。。。\\test.csv";
// DataModel dataModel = new FileDataModel(new File(file));
换成:
MysqlDataSource dataSource =new com.mysql.cj.jdbc.MysqlDataSource();
dataSource.setServerName("localhost");
dataSource.setUser( "root");
dataSource.setPassword( "123");
dataSource.setDatabaseName( "kss" );
//MySQLJDBCDataModel(DataSource dataSource, String preferenceTable,String userIDColumn,String itemIDColumn,String preferenceColumn,String timestampColumn)
JDBCDataModel jdbcDataModel= new MySQLJDBCDataModel(dataSource, comTable, uId, iId, rateId, time);
//利用ReloadFromJDBCDataModel包裹jdbcDataModel,可以把输入加入内存计算,加快计算速度。
ReloadFromJDBCDataModel dataModel = new ReloadFromJDBCDataModel(jdbcDataModel);
//这里的refresh是刷新dataModel,一般情况下我觉得用不上,因为像我这个程序,每次都会新建立datamodel,都是最新的,所以不用刷新
//当然,如果你需要在内存中存储下dataModel,然后自己的taste_preferences时刻在变化,此时是需要刷新的。
//model.refresh(null);
注意:
1、去掉scope
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- <scope>runtime</scope>-->
</dependency>
2、如果项目中还有其他需要的包,注意包冲突
3、如果报错The server time zone value ‘�й���ʱ��’ is unrecognized or represents more than one time zone(服务器时区值’�й���ʱ��’ 无法识别或表示多个时区)
在mysql中
set global time_zone = '+8:00';
2.4 聚类
用Mahout实现kmeans
public class Kmeans {
public static void main(String[] args) throws IOException {
List sampleData = MathUtil.readFileToVector("datafile/randomData.csv");
int k = 3;
double threshold = 0.01;
List randomPoints = MathUtil.chooseRandomPoints(sampleData, k);
for (Vector vector : randomPoints) {
System.out.println("Init Point center: " + vector);
}
List clusters = new ArrayList();
for (int i = 0; i < k; i++) {
clusters.add(new Cluster(randomPoints.get(i), i, new EuclideanDistanceMeasure()));
}
List<List> finalClusters = KMeansClusterer.clusterPoints(sampleData, clusters, new EuclideanDistanceMeasure(), k, threshold);
for (Cluster cluster : finalClusters.get(finalClusters.size() - 1)) {
System.out.println("Cluster id: " + cluster.getId() + " center: " + cluster.getCenter().asFormatString());
}
}
}
2.5 分类
2.6 算法评判标准:召回率(recall)与查准率(precision)
2.7 Recommender的API接口
2.7.1 org.apache.mahout.cf.taste.recommender.Recommender.java
接口中方法的解释:
recommend(long userID, int howMany): 获得推荐结果,给userID推荐howMany个Item
recommend(long userID, int howMany, IDRescorer rescorer): 获得推荐结果,给userID推荐howMany个Item,可以根据rescorer对结构重新排序。
estimatePreference(long userID, long itemID): 当打分为空,估计用户对物品的打分
setPreference(long userID, long itemID, float value): 赋值用户,物品,打分
removePreference(long userID, long itemID): 删除用户对物品的打分
getDataModel(): 提取推荐数据
通过Recommender接口,我可以猜出核心算法,应该会在子类的estimatePreference()方法中进行实现。
2.7.2 通过继承关系到Recommender接口的子类
推荐算法实现类:
GenericUserBasedRecommender: 基于用户的推荐算法
GenericItemBasedRecommender: 基于物品的推荐算法
KnnItemBasedRecommender: 基于物品的KNN推荐算法
SlopeOneRecommender: Slope推荐算法
SVDRecommender: SVD推荐算法
TreeClusteringRecommender:TreeCluster推荐算法
2.8 mahaout计算相似度的组件
下面就几个重点相似度计算方法做介绍:
2.8.1 皮尔森相关度
类名:PearsonCorrelationSimilarity
2.8.2 欧式距离相似度
类名:EuclideanDistanceSimilarity
2.8.3 余弦相似度
类名:PearsonCorrelationSimilarity和UncenteredCosineSimilarity
2.8.4 Spearman秩相关系数
类名:SpearmanCorrelationSimilarity
原理:Spearman秩相关系数通常被认为是排列后的变量之间的Pearson线性相关系数。
范围:{-1.0,1.0},当一致时为1.0,不一致时为-1.0。
说明:计算非常慢,有大量排序。针对推荐系统中的数据集来讲,用Spearman秩相关系数作为相似度量是不合适的。
2.8.5 曼哈顿距离
类名:CityBlockSimilarity
原理:曼哈顿距离的实现,同欧式距离相似,都是用于多维数据空间距离的测度
范围:[0,1],同欧式距离一致,值越小,说明距离值越大,相似度越大。
说明:比欧式距离计算量少,性能相对高。
2.8.6 Tanimoto系数
类名:TanimotoCoefficientSimilarity
原理:又名广义Jaccard系数,是对Jaccard系数的扩展,等式为
范围:[0,1],完全重叠时为1,无重叠项时为0,越接近1说明越相似。
说明:处理无打分的偏好数据。
2.8.7 对数似然相似度
类名:LogLikelihoodSimilarity
原理:重叠的个数,不重叠的个数,都没有的个数
范围:具体可去百度文库中查找论文《Accurate Methods for the Statistics of Surprise and Coincidence》
说明:处理无打分的偏好数据,比Tanimoto系数的计算方法更为智能。
2.9 DataModel
import org.apache.mahout.cf.taste.model.DataModel;
要做推荐,用户行为数据是基础。
用户行为数据有哪些字段呢?
mahout的DataModel支持,用户ID,ItemID是必须的,偏好值(用户对当前Item的评分),时间戳 这四个字段
{@code userID, itemID [,preference[,timestamp]]}
Mahout中提供给用户存储结构化数据的类:
Class FileDataModel: 读取一个有分隔符的数据文件,分隔符仅支持tab和逗号(comma)。
DataModel dataModel = new FileDataModel(new File(file));
数据格式: userID,itemID[,preference[,timestamp]]
【要求:数据必须是2列或者4列;所有数据格式必须一致, 即分隔符数量一致;超过4列的数据, 空行以及以'#'开头的数据都会被忽略。】
示例:如123,456或者123,456,3,129050099059 或者123,456,,129050099059。
如果一个人只是看过某部电影, 则没有preference项, timestamp表示时间戳.
格式:preference自动解析为double类型; userID和itemID解析为longs类型, timestamp自动解析为longs, 但是可以通过重写readTimestampFromString(String)方法来自己解析.
自定义读取方法: 重写processLine和 processLineWithoutID(String, FastByIDMap, FastByIDMap)
文件格式:FileDataModel可以读取.zip和.gz格式的压缩包数据。
Class MysqlJDBCModel: 读取数据库中的数据, 为了提升数据查询效率. userID和itemID不能为空, 两者均需要建立索引, 主键primary key为userID和itemID的composition. 在Mysql中, 需要使用BIGINT和FLOAT类型, 调整buffer和查询cache的大小, Mysql的Connector/J driver中的cachePreparedStatements需要设置为true.
Class FileIDMigrator: 专门用于读取一系列的字符串并生成相应的MD5标识码在一个FastByIDMap(类似于Java SE中的HashMap中, 用途暂时不明确. 例如数据是一个电影名列表, 文件每行只有一个电影名, 那么FileIDMigrator将生成对应的MD5码。
源码中的数据存储均由GenericDataModel和GenericBooleanPrefDataModel来存储
它们提供一下的比较重要的方法:
getUserIDs: 获取所有用户的ID
getPreferencesFromUser: 获取某个用户对所有物品的评分.
getItemIDsFromUser: 获取某个用户评论过的所有物品.
getItemIDs: 获取所有物品的ID.
getPreferencesForItem: 获取某个物品的所有评分.
getPreferenceValue: 获取某个用户对某个物品的评分.
getPreferenceTime: 获取某个用户对某个物品的评分时间.
getNumItems: 获取所有的物品总数
getNumUsers: 获取所有的用户综述.
getNumUsersWithPreferenceFor 1 item 获取对某个item评论过的用户数目.
getNumUsersWithPreferenceFor 2 items 获取对两个items同时评论过的用户数目.
hasPreferenceValues: 判断是否有评分, 用于区分BooleanDataModel.
getMaxPreference: 最大评分
getMinPreference: 最小评分
setPreference 改变某个用户对某个物品的评分, 并不改变数据源的数据.
removePreference 去除某个用户对某个物品的评分, 并不改变数据源的数据.
RecommendedItem
public interface RecommendedItem {
/** @return the recommended item ID */
long getItemID();
/**
* <p>
* A value expressing the strength of the preference for the recommended item. The range of the values
* depends on the implementation. Implementations must use larger values to express stronger preference.
* </p>
*
* @return strength of the preference
*/
float getValue();
}