协同过滤算法在推荐系统领域占有及其重要的地位,协同过滤算法的出现甚至标志了智能推荐的出现。协同过滤算法大体上分为两类:基于用户的协同过滤和基于物品的协同过滤。近几年出现的对协同过滤算法的改进,大部分都是基于这两种算法。笔者最近学习了基于用户的协同过滤算法,所以在此做一个简单的总结,有不对之处请各位指出。
基于用户的协同过滤算法的主要思想是基于用户A可能会喜欢与用户A特征相似的用户B所喜欢的物品。例如,如果一个女生想买一双鞋子,她很有可能去问她的朋友,让她的朋友来给她作一些推荐。在这里这个女生和她的朋友可以看成是相似的用户。协同过滤算法就是基于这样的一种思想。顺理成章,基于用户的协同过滤算法需要解决如下两个问题:
1)、有哪些用户和用户A相似。
2)、这些与A相似的用户喜欢哪些产品,然后将这些产品中用户A没有听说过的产品推荐给用户A。
那么,如何计算两个用户的相似度,对于一个电子商务网站、电影网站或者音乐网站来说,通过分析日志文件可以获取用户的行为,通过注册信息可以获取用户的一些基本信息,比如邮箱、性别、年龄等,但这些注册信息是不够完整的,有些网站可能连这些基本的注册信息都没有。但用户在网站上有哪些行为是可以通过日志文件获取的,所以计算用户之间的相似性可以转化为计算用户行为的相似性。在这里我们采用两个用户购买了哪些产品或者是听了哪些音乐等这些行为来衡量。假设用户U曾将购买的产品的集合为A,V曾经购买的产品集合为B。衡量二者的相似性可以用Jaccard公式来计算:
或者使用余弦相似度来计算:
在公式中A和B都是一个集合,|A|代表了集合A元素 的个数,同样|B|代表了集合B的个数。所以代表了用户U和用户V购买的
相同物品的个数。例如,设A={a, b, c, d} ,B = {a, b, e, f} 则:
|AB| = |{a, b, c, d}{a, b, e,f}| = |{a, b}| = 2
|AB| = |{a, b, c, d}{a, b, e, f}| = |{a, b, c, d, e, f}| = 6
由此便可以根据用户U和用户V所够买的产品而计算出二者的相似性。
假设有三个用户U1, U2, U3,U4 。他们分别对物品集合P(U1), P(U2), P(U3),P(U4)产生正反馈,所谓正反馈指的是用户购买
了这些产品。其中P(U1)={a, b, c, d,e} , P(U2)={c, d, e, f), P(U3) = (a, f, g, z), P(U4)={h, i, a, z, m}, 那么如何计算他们两两之
间的相似度。下面用java程序模拟整个过程,这里使用余弦相似度。
package CollaborateFilter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class CFuser {
public static void main(String[] args){
String[] products1 = {"a", "b", "c", "d","e"};
User U1 = new User("U1", products1);
String[] products2 = {"c","d","e","f"};
User U2 = new User("U2", products2);
String[] products3 = {"a","f","g","z"};
User U3 = new User("U3", products3);
String[] products4 = {"h","i","a","z","m"};
//新建一个set用来存储用户信息
User U4 = new User("U4", products2);
List<User> users = new ArrayList<User>();
users.add(U1);
users.add(U2);
users.add(U3);
users.add(U4);
List<User> usersList= CalSimilarity.CalSimilaritys(users);
for(int i = 0; i < usersList.size(); i++){
System.out.println(usersList.get(i).getUsername());
Map<String, Double> similarity = usersList.get(i).getSimilarMap();
for (Entry<String, Double> entry : similarity.entrySet()) {
System.out.print(" key=" + entry.getKey() + " similarity= " + entry.getValue());
}
System.out.println();
}
}
}
/**
* 用户类别,包括用户名,和所购买的产品
* @author zhouqi
*
*/
class User{
//用户姓名
private String username;
//用户所购买的产品
private String[] products;
//相似度
private Map<String, Double> SimilarMap;
public User(){ }//空构造方法
public User(String username, String[] products){
this.username = username;
this.products = products;
}//构造方法
public Map<String, Double> getSimilarMap() {
return SimilarMap;
}
public User setSimilarMap(Map<String, Double> similarMap) {
SimilarMap = similarMap;
return this;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String[] getProducts() {
return products;
}
public void setProducts(String[] products) {
this.products = products;
}
}
//使用余弦相似度计算两个用户之间的相似度。
class CalSimilarity{
public static double Cal2Similarity(User user1, User user2){
String[] products1 = user1.getProducts();
String[] products2 = user2. getProducts();
double count = 0.0;
for(int i = 0; i < products1.length; i++){
for(int j = 0 ; j < products2.length; j++){
if(products1[i] == products2[j]) {
count++;
}
}
}
double similarity = count/(products1.length * products2.length);
return similarity;
}
//计算很多用户之间的相似度。
public static List<User> CalSimilaritys(List<User> users){
List<User> usersList = new ArrayList<User>();
for(int i = 0; i < users.size(); i ++){
Map<String, Double> smap = new HashMap<String, Double>();
//System.out.println(users.get(i).getUsername());
for(int j = 0; j < users.size(); j ++){
if(i == j) continue;
//计算两个用户之间的相似度
double similarity = CalSimilarity.Cal2Similarity(users.get(i), users.get(j));
smap.put(users.get(j).getUsername(), similarity);
}
usersList.add(users.get(i).setSimilarMap(smap));
}
return usersList;
}
}