Springboot+vue.js+協(xié)同過(guò)濾推薦+余弦相似度算法實(shí)現(xiàn)新聞推薦系統(tǒng)

針對(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ì)如下如:


image.png

二酱床、效果實(shí)現(xiàn)

登錄界面

image.png

系統(tǒng)主頁(yè)

image.png

推薦列表

image.png

新聞搜索

image.png

新聞詳情

image.png

瀏覽歷史

image.png

其他效果省略

三羊赵、系統(tǒng)算法介紹

余弦相似度算法

余弦相似度,又稱(chēng)為余弦相似性扇谣,是通過(guò)計(jì)算兩個(gè)向量的夾角余弦值來(lái)評(píng)估他們的相似度昧捷。余弦相似度將向量根據(jù)坐標(biāo)值,繪制到向量空間中罐寨,如最常見(jiàn)的二維空間靡挥。
余弦相似度衡量的是2個(gè)向量間的夾角大小,通過(guò)夾角的余弦值表示結(jié)果鸯绿,因此2個(gè)向量的余弦相似度為:


image.png

分子為向量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);
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末读整,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子咱娶,更是在濱河造成了極大的恐慌米间,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件膘侮,死亡現(xiàn)場(chǎng)離奇詭異屈糊,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)琼了,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)逻锐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人雕薪,你說(shuō)我怎么就攤上這事昧诱。” “怎么了所袁?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵盏档,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我燥爷,道長(zhǎng)蜈亩,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任局劲,我火速辦了婚禮勺拣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鱼填。我一直安慰自己药有,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著愤惰,像睡著了一般苇经。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宦言,一...
    開(kāi)封第一講書(shū)人閱讀 51,727評(píng)論 1 305
  • 那天扇单,我揣著相機(jī)與錄音,去河邊找鬼奠旺。 笑死蜘澜,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的响疚。 我是一名探鬼主播鄙信,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼忿晕!你這毒婦竟也來(lái)了装诡?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤践盼,失蹤者是張志新(化名)和其女友劉穎鸦采,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體咕幻,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡渔伯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了谅河。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咱旱。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖绷耍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鲜侥,我是刑警寧澤褂始,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站描函,受9級(jí)特大地震影響崎苗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜舀寓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一胆数、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧互墓,春花似錦必尼、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)豆挽。三九已至,卻和暖如春券盅,著一層夾襖步出監(jiān)牢的瞬間帮哈,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工锰镀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留娘侍,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓泳炉,卻偏偏與公主長(zhǎng)得像憾筏,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子胡桃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容