針對(duì)海量的新聞資訊數(shù)據(jù)非春,如何快速的根據(jù)用戶(hù)的檢索需要什湘,完成符合用戶(hù)閱讀需求的新聞資訊推薦伐脖?本篇文章主要采用余弦相似度及基于用戶(hù)協(xié)同過(guò)濾算法實(shí)現(xiàn)新聞推薦场勤,通過(guò)余弦相似度算法完成針對(duì)不同新聞數(shù)據(jù)之間的相似性計(jì)算她倘,實(shí)現(xiàn)分類(lèi)標(biāo)簽危号。通過(guò)協(xié)同過(guò)濾算法發(fā)現(xiàn)具備相似閱讀習(xí)慣的用戶(hù)淋昭,展開(kāi)個(gè)性化推薦。
本次新聞推薦系統(tǒng):
主要包含技術(shù):springboot旨别,mybatis,mysql纯命,javascript,vue.js剖膳,html躏精,css
主要包含算法:余弦相似度,基于用戶(hù)協(xié)同過(guò)濾推薦
一、系統(tǒng)設(shè)計(jì)
系統(tǒng)采用前后端分離的開(kāi)發(fā)模式完成止状,系統(tǒng)前端主要采用Vue.js,javascript,html,CSS等技術(shù)實(shí)現(xiàn)集峦。系統(tǒng)后端框架采用springboot+mybatis+mysql數(shù)據(jù)庫(kù)搭建买优,針對(duì)海量的新聞資訊數(shù)據(jù)采用分表操作脖咐,完成數(shù)據(jù)存儲(chǔ)分析匾嘱。系統(tǒng)前后端數(shù)據(jù)交互,采用Ajax異步調(diào)用傳輸JSON實(shí)現(xiàn)早抠。系統(tǒng)架構(gòu)主要分為基礎(chǔ)數(shù)據(jù)存儲(chǔ)霎烙,新聞資訊爬蟲(chóng),新聞分析計(jì)算贝或,新聞網(wǎng)站前端四個(gè)層面吼过,其中爬蟲(chóng)主要定時(shí)采集互聯(lián)網(wǎng)各大新聞網(wǎng)站的公開(kāi)資訊數(shù)據(jù),完成數(shù)據(jù)清洗咪奖,過(guò)濾等操作。系統(tǒng)主要架構(gòu)設(shè)計(jì)如下如:
二酱床、效果實(shí)現(xiàn)
登錄界面
系統(tǒng)主頁(yè)
推薦列表
新聞搜索
新聞詳情
瀏覽歷史
其他效果省略
三羊赵、系統(tǒng)算法介紹
余弦相似度算法
余弦相似度,又稱(chēng)為余弦相似性扇谣,是通過(guò)計(jì)算兩個(gè)向量的夾角余弦值來(lái)評(píng)估他們的相似度昧捷。余弦相似度將向量根據(jù)坐標(biāo)值,繪制到向量空間中罐寨,如最常見(jiàn)的二維空間靡挥。
余弦相似度衡量的是2個(gè)向量間的夾角大小,通過(guò)夾角的余弦值表示結(jié)果鸯绿,因此2個(gè)向量的余弦相似度為:
分子為向量A與向量B的點(diǎn)乘跋破,分母為二者各自的L2相乘,即將所有維度值的平方相加后開(kāi)方瓶蝴。
余弦相似度的取值為[-1,1]毒返,值越大表示越相似。
余弦相似度java代碼實(shí)現(xiàn)
public static double getSimilarity(String doc1, String doc2) {
if (doc1 != null && doc1.trim().length() > 0 && doc2 != null && doc2.trim().length() > 0){
Map<Integer, int[]> AlgorithmMap = new HashMap<Integer, int[]>();
//將兩個(gè)字符串中的中文字符以及出現(xiàn)的總數(shù)封裝到舷手,AlgorithmMap中
for (int i = 0; i < doc1.length(); i++) {
char d1 = doc1.charAt(i);
if (isHanZi(d1)) {//標(biāo)點(diǎn)和數(shù)字不處理
int charIndex = getGB2312Id(d1);//保存字符對(duì)應(yīng)的GB2312編碼
if (charIndex != -1) {
int[] fq = AlgorithmMap.get(charIndex);
if (fq != null && fq.length == 2) {
fq[0]++;//已有該字符拧簸,加1
} else {
fq = new int[2];
fq[0] = 1;
fq[1] = 0;
AlgorithmMap.put(charIndex, fq);//新增字符入map
}
}
}
}
for (int i = 0; i < doc2.length(); i++) {
char d2 = doc2.charAt(i);
if (isHanZi(d2)) {
int charIndex = getGB2312Id(d2);
if (charIndex != -1) {
int[] fq = AlgorithmMap.get(charIndex);
if (fq != null && fq.length == 2) {
fq[1]++;
} else {
fq = new int[2];
fq[0] = 0;
fq[1] = 1;
AlgorithmMap.put(charIndex, fq);
}
}
}
}
Iterator<Integer> iterator = AlgorithmMap.keySet().iterator();
double sqdoc1 = 0;
double sqdoc2 = 0;
double denominator = 0;
while (iterator.hasNext()) {
int[] c = AlgorithmMap.get(iterator.next());
denominator += c[0] * c[1];
sqdoc1 += c[0] * c[0];
sqdoc2 += c[1] * c[1];
}
double v = denominator / Math.sqrt(sqdoc1 * sqdoc2);//余弦計(jì)算
v = Double.isNaN(v) ? 0d : v;
return v;
} else {
throw new NullPointerException(" the Document is null or have not cahrs!!");
}
}
協(xié)同過(guò)濾推薦算法
協(xié)同過(guò)濾算法是一個(gè)大類(lèi),主要有基于用戶(hù)男窟、基于物品盆赤、兩者結(jié)合等分支贾富,這里我主要介紹的是基于用戶(hù)的協(xié)同過(guò)濾算法。主要的思想也很簡(jiǎn)單牺六,中國(guó)有一句俗語(yǔ)“物以類(lèi)聚祷安,人以群分”,我們可以有很大的把握認(rèn)為一個(gè)和你很相似的用戶(hù)喜歡的物品也大概率也是你喜歡的物品兔乞,這就是基于用戶(hù)的協(xié)同過(guò)濾推薦算法的思想汇鞭。實(shí)現(xiàn)基于用戶(hù)協(xié)同過(guò)濾推薦,主要包含以下幾個(gè)步驟:
1.計(jì)算用戶(hù)相似度
2.獲取需要推薦給用戶(hù)的物品(本系統(tǒng)內(nèi)主要是新聞數(shù)據(jù))
基于用戶(hù)協(xié)同推薦算法實(shí)現(xiàn)
/***
* 協(xié)同過(guò)濾算法
* 1. 找到與目標(biāo)用戶(hù)興趣相似的用戶(hù)集合
* 2. 找到這個(gè)集合中用戶(hù)喜歡的庸追、并且目標(biāo)用戶(hù)沒(méi)有聽(tīng)說(shuō)過(guò)的新聞推薦給目標(biāo)用戶(hù)
* @param userInfos
* @param recommendUser
* @return
*/
public static List<GPair<String, Double>> XtglNewsTj(List<GPair<String, List<String>>> userInfos, String recommendUser) {
int N = userInfos.size();
//建立用戶(hù)稀疏矩陣霍骄,用于用戶(hù)相似度計(jì)算【相似度矩陣】
int[][] sparseMatrix = new int[N][N];
//存儲(chǔ)每個(gè)用戶(hù)對(duì)應(yīng)的不同總數(shù)eg: A 3
Map<String, Integer> userItemLength = new HashMap<>();
//建立新聞到用戶(hù)的倒排表 eg: a A B
Map<String, Set<String>> itemUserCollection = new HashMap<>();
Set<String> items = new HashSet<>();//輔助存儲(chǔ)新聞集合
Map<String, Integer> userID = new HashMap<>();//輔助存儲(chǔ)每一個(gè)用戶(hù)的用戶(hù)ID映射
Map<Integer, String> idUser = new HashMap<>();//輔助存儲(chǔ)每一個(gè)ID對(duì)應(yīng)的用戶(hù)映射
for(int i = 0; i < N ; i++){//依次處理N個(gè)用戶(hù) 輸入數(shù)據(jù) 以空格間隔
userItemLength.put(userInfos.get(i).getKey(), userInfos.get(i).getValue().size());//eg: A 3
userID.put(userInfos.get(i).getKey(), i);//用戶(hù)ID與稀疏矩陣建立對(duì)應(yīng)關(guān)系
idUser.put(i, userInfos.get(i).getKey());
//建立新聞--用戶(hù)倒排表
for(int j = 0; j < userInfos.get(i).getValue().size(); j ++){
if(items.contains(userInfos.get(i).getValue().get(j))){//如果已經(jīng)包含對(duì)應(yīng)的新聞--用戶(hù)映射,直接添加對(duì)應(yīng)的用戶(hù)
itemUserCollection.get(userInfos.get(i).getValue().get(j)).add(userInfos.get(i).getKey());
}else{//否則創(chuàng)建對(duì)應(yīng)新聞--用戶(hù)集合映射
items.add(userInfos.get(i).getValue().get(j));
itemUserCollection.put(userInfos.get(i).getValue().get(j), new HashSet<String>());//創(chuàng)建新聞--用戶(hù)倒排關(guān)系
itemUserCollection.get(userInfos.get(i).getValue().get(j)).add(userInfos.get(i).getKey());
}
}
}
System.out.println(itemUserCollection.toString());
//計(jì)算相似度矩陣【稀疏】
Set<Map.Entry<String, Set<String>>> entrySet = itemUserCollection.entrySet();
Iterator<Map.Entry<String, Set<String>>> iterator = entrySet.iterator();
while(iterator.hasNext()){
Set<String> commonUsers = iterator.next().getValue();
for (String user_u : commonUsers) {
for (String user_v : commonUsers) {
if(user_u.equals(user_v)){
continue;
}
sparseMatrix[userID.get(user_u)][userID.get(user_v)] +=1;
}
}
}
/計(jì)算用戶(hù)之間的相似度【余弦相似性】
int recommendUserId = userID.get(recommendUser);
List<GPair<String, Double>> res = new ArrayList<>();
for (int j = 0;j < sparseMatrix.length; j++) {
if(j != recommendUserId){
System.out.println(idUser.get(recommendUserId)+"--"+idUser.get(j)+"相似度:"+sparseMatrix[recommendUserId][j]/Math.sqrt(userItemLength.get(idUser.get(recommendUserId))*userItemLength.get(idUser.get(j))));
}
}
//計(jì)算指定用戶(hù)recommendUser的新聞推薦度
List<GPair<String, Double>> recommondInfos = new ArrayList<>();
for(String item: items){//遍歷每一件新聞
Set<String> users = itemUserCollection.get(item);//得到 當(dāng)前新聞的所有用戶(hù)集合
if(!users.contains(recommendUser)){//如果被推薦用戶(hù)當(dāng)前新聞淡溯,則進(jìn)行推薦度計(jì)算
double itemRecommendDegree = 0.0;
for(String user: users){
itemRecommendDegree += sparseMatrix[userID.get(recommendUser)][userID.get(user)]/Math.sqrt(userItemLength.get(recommendUser)*userItemLength.get(user));//推薦度計(jì)算
}
recommondInfos.add(new GPair<>(item, itemRecommendDegree));
}
}
recommondInfos.sort(new Comparator<GPair<String, Double>>() {
@Override
public int compare(GPair<String, Double> o1, GPair<String, Double> o2) {
return o2.getValue().compareTo(o1.getValue());
}
});
return combine(recommendUser, userInfos,recommondInfos);
}