推薦系統目的在於根據使用者個人化的特質,推薦使用者喜好的商品或廣告,其中最常使用的演算法為協同式過濾(collaborative filtering).協同式過濾是根據已知的消費行為或是客戶對於商品評價,來猜測客戶對於
未購買或未評價的商品可能的評價.協同式過濾中,又以User-base以及Item-base為主要的評價方法.
本文大綱
什麼是User Base推薦
顧名思意,就是透過使用者之間,對於相同產品評價相似程度,來猜測對於其他產品的評價.例如我們兩個人都喜歡吃香蕉,蘋果,芭樂,如果我還喜歡吃芒果,我就會覺得你可能也喜歡吃芒果.(Item Base可以參考
http://bryannotes.blogspot.tw/2015/02/r-item-base.html)
範例data
資料來源
資料集概況
ml-100k
評分筆數:100000
不重複使用者數:943人
不重複電影數:1682部
ml-1m
評分筆數:100209
不重複使用者數:6040人
不重複電影數:3706部
ml-10m
評分筆數:10000054
不重複使用者數:69878人
不重複電影數:10678部
演算法實作:
參考資料:
A Collaborative Filtering Recommendation Algorithm Based on User Clustering and Item Clustering
演算法流程
- 根據評分紀錄,找出與特定十個最相似的User(N-Top)
- 相似性公式:
Rik為使用者i給電影k的評分,n為兩個使用都有看過的電影
根據這十個User,推論產品評分
評分公式:
這邊Au為目標使用者的平均評分,Rit為相似使用者i的對電影t的評分.Am為使用者i的平均評分,sim(u,i)為目標使用者與相似使用者i的相似程度,最後c是相似使用者的人數.
(以下範例相似使用者取相似度最高的前10名)
結果比較
硬體環境
指標:
- 預測時間:程式執行時間
- 預測結果準確度:預測值與實際值的差異平方(RMSE)
評比方法:
- 分別對三組資料抽出十個使用者
- 分別計算這十個使用者,與最相似的10個人
- 根據這十個人的評分以及相似性,預設使用者的評分
- 測量上述步驟所花費時間
- 計算使用者原始評分,以及與預測評分之間的差異(RMSE)
表格
ml-100k
Time |
RMSE |
2.17 |
1.15 |
1.84 |
0.58 |
1.90 |
1.04 |
1.91 |
0.92 |
1.88 |
0.55 |
1.83 |
1.33 |
1.99 |
0.36 |
1.81 |
0.96 |
2.03 |
1.07 |
1.86 |
1.04 |
1.92 |
0.90 (平均) |
ml-1m
Time |
RMSE |
67.32 |
1.04 |
67.51 |
0.89 |
68.53 |
0.33 |
63.82 |
0.61 |
64.75 |
0.64 |
69.34 |
0.90 |
66.28 |
0.47 |
65.93 |
0.79 |
64.35 |
0.70 |
66.41 |
0.69 |
小結
- 速度是最明顯的差異,這個差異主要來自計算相似性的時候(就算只想預測單一的商品),是把使用者跟其他所有人掃過一次,再找出相似性最高的十個人.所以當產品(3707/1682 = 2.2)與使用者(6040/943 = 6.4)增加的時候,所花時間會跟兩者成正比.
- 隨著使用者以及產品人數上升,RMSE差異也減少了23%(0.69 / 0.9).差異減少的幅度顯然不如增加的執行時間.
- 透過實作,可以證實文獻中,對於User-base演算法的缺點,的確在於當使用者的人數大幅度增加時,可能大量降低的效能(10M的資料單機電腦跑不動,放了兩個小時還是算不出單一使用者的結果).因此也有了許多改進的做法,例如先使用cluster將使用者分群(但是10M的資料放到K-means中跑分群還是掛),可以先縮減Top-N計算相似性的時間,或是試試看Item Base CF.
原始碼
前置處理
##R code
library(reshape2)
library(Matrix)
##read file
base <- read.delim("~/Documents/Data/Vpon/ml-100k/u.data", header = FALSE)
colnames(base) <- c("userID", "itemID", "rating", "timestamp")
##clean data
base[,"rating"] <- as.numeric(base[,"rating"])
##see if duplicate user-item
base <- base[!duplicated(base[,1:2]),]
##transfer to User-Item Matrix
ratingDF <- dcast( base, userID ~ itemID, value.var = "rating" , index="userID")
ratingDF <- ratingDF[,2:ncol(ratingDF)]
ratingDF[is.na(ratingDF)] <- 0
ratingMat <- Matrix(as.matrix(ratingDF), sparse = TRUE) ## cast data frame as matrix 轉成Sparse能降低佔用的空間(以10m的資料為例,原始的矩陣為5.6G, 經過sparse轉換後僅剩115m)
主要的方法
getCosine <- function(x, y){
cosine <- sum(x * y) / (sqrt(sum(x * x)) * sqrt(sum(y * y)))
return(cosine)
}
getNeighbors <- function(x) { # input a user id
uuCosin <- sapply(seq(1:nrow(ratingMat)),
function(y) cosine <- getCosine(ratingMat[x,], ratingMat[y,]))
y = order(uuCosin, decreasing = TRUE)[2:11]
simXy = uuCosin[y]
rbind(y,simXy)
}
getRating <- function(x, y, sim){
meanY = sum(y)/sum(y>0)
predict = ((y-meanY)*sim)
return(predict)
}
getRatings <- function(x, neighbor){ ##input user id, neighbor id( as a matrix)
ratings = 0
for (i in 1: ncol(neighbor)){
y = ratingMat[neighbor[1,i],]
sim = neighbor[2,i]
ratings <- ratings + getRating(x, y, sim)
}
meanX = sum(x)/sum(x>0)
ratings <- meanX + ratings/sum(neighbor[2,])
}
getRMSE <- function(x, pred) {
sqrt(sum((x - t(pred))^2)/length(x))
}
````
<div class="se-preview-section-delimiter"></div>
評比部分有兩個指標
1. 運算效率
2. 預測與實際值的差異(RMSE)
<div class="se-preview-section-delimiter"></div>
```R
index <- 1: nrow(ratingMat)
evaluation <- sapply(seq(1, 10), function(x){ ## input how many runs
sampleData <- sample(index, 1)
startTime <- proc.time()
pred <- sapply(sampleData, function(i){
x = ratingMat[i,]
neighbor = getNeghbors(i)
pred <- getRatings(x, neighbor)
}
)
endTime <- proc.time()-startTime
result = rbind(time = endTime[1], RMSE = getRMSE(ratingMat[sampleData,], pred))
}
)
evaluation <- t(evaluation)
colnames(evaluation) <- c("time", "RMSE")
evaluation <- rbind(evaluation, mean = colMeans(evaluation))
請問你知道R是否有實作ALS_WR的演算嗎?
回覆刪除不太確定你說的WR是指什麼,但是ALS的話倒是可以參考
刪除http://cran.r-project.org/web/packages/ALS/ALS.pdf