協同過濾算法在推薦系統領域佔有及其重要的地位,協同過濾算法的出現甚至標誌了智能推薦的出現。協同過濾算法大體上分爲兩類:基於用戶的協同過濾和基於物品的協同過濾。近幾年出現的對協同過濾算法的改進,大部分都是基於這兩種算法。筆者最近學習了基於用戶的協同過濾算法,所以在此做一個簡單的總結,有不對之處請各位指出。
基於用戶的協同過濾算法的主要思想是基於用戶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;
}
}