您好,登錄后才能下訂單哦!
php可以實現推薦算法嗎?針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
推薦算法是非常古老的,在機器學習還沒有興起的時候就有需求和應用了。
協同過濾(Collaborative Filtering)作為推薦算法中最經典的類型,包括在線的協同和離線的過濾兩部分。所謂在線協同,就是通過在線數據找到用戶可能喜歡的物品,而離線過濾,則是過濾掉一些不值得推薦的數據,比比如推薦值評分低的數據,或者雖然推薦值高但是用戶已經購買的數據。
下面就介紹下怎樣用PHP+MySQL實現簡單的協同過濾算法。
要實現協同過濾推薦算法,首先就要理解算法的核心思想和流程。該算法的核心思想可以概括為:若a,b喜歡同一系列的物品(暫時稱b是a的鄰居吧),則a很可能喜歡b喜歡的其他物品。算法的實現流程可以簡單概括為:1.確定a有哪些鄰居 2.通過鄰居來預測a可能會喜歡哪種物品 3.將a可能喜歡的物品推薦給a。
算法核心的公式如下:
1.余弦相似度(求鄰居):
2.預測公式(預測a可能會喜歡哪種物品):
僅從這兩個公式我們就可以看出,僅僅是按照這兩個公式進行計算,就需要進行大量的循環與判斷,而且還涉及到排序的問題,就涉及到排序算法的選擇與使用,這里選快排。
首先建表:
DROP TABLE IF EXISTS `tb_xttj`; CREATE TABLE `tb_xttj` ( `name` varchar(255) NOT NULL, `a` int(255) default NULL, `b` int(255) default NULL, `c` int(255) default NULL, `d` int(255) default NULL, `e` int(255) default NULL, `f` int(255) default NULL, `g` int(255) default NULL, `h` int(255) default NULL, PRIMARY KEY (`name`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; INSERT INTO `tb_xttj` VALUES ('John', '4', '4', '5', '4', '3', '2', '1', null); INSERT INTO `tb_xttj` VALUES ('Mary', '3', '4', '4', '2', '5', '4', '3', null); INSERT INTO `tb_xttj` VALUES ('Lucy', '2', '3', null, '3', null, '3', '4', '5'); INSERT INTO `tb_xttj` VALUES ('Tom', '3', '4', '5', null, '1', '3', '5', '4'); INSERT INTO `tb_xttj` VALUES ('Bill', '3', '2', '1', '5', '3', '2', '1', '1'); INSERT INTO `tb_xttj` VALUES ('Leo', '3', '4', '5', '2', '4', null, null, null);
這里只對最后一行的Leo進行推薦,看看f,g,h哪個可以推薦給他。
用php+mysql,流程圖如下:
連接數據庫并將其存儲為二維數組的代碼如下:
header("Content-Type:text/html;charset=utf-8"); mysql_connect("localhost","root","admin"); mysql_select_db("geodatabase"); mysql_query("set names 'utf8'"); $sql = "SELECT * FROM tb_xttj"; $result = mysql_query($sql); $array = array(); while($row=mysql_fetch_array($result)) { $array[]=$row;//$array[][]是一個二維數組 }
問題1:這一步完全可以看做是整表查詢,這種查詢是大忌,對于這種小小的演示系統還可以,但是對大數據的系統,沒有效率。
求Leo與其他人的Cos值代碼如下:
/* * 以下示例只求Leo的推薦,如此給變量命名我也是醉了;初次理解算法,先不考慮效率和邏輯的問題,主要把過程做出來 */ $cos = array(); $cos[0] = 0; $fm1 = 0; //開始計算cos //計算分母1,分母1是第一個公式里面 “*”號左邊的內容,分母二是右邊的內容 for($i=1;$i<9;$i++){ if($array[5][$i] != null){//$array[5]代表Leo $fm1 += $array[5][$i] * $array[5][$i]; } } $fm1 = sqrt($fm1); for($i=0;$i<5;$i++){ $fz = 0; $fm2 = 0; echo "Cos(".$array[5][0].",".$array[$i][0].")="; for($j=1;$j<9;$j++){ //計算分子 if($array[5][$j] != null && $array[$i][$j] != null){ $fz += $array[5][$j] * $array[$i][$j]; } //計算分母2 if($array[$i][$j] != null){ $fm2 += $array[$i][$j] * $array[$i][$j]; } } $fm2 = sqrt($fm2); $cos[$i] = $fz/$fm1/$fm2; echo $cos[$i]."<br/>"; }
這一步得到的結果:
將求好的Cos值排序,采用快排代碼如下:
//對計算結果進行排序,湊合用快排吧先 function quicksort($str){ if(count($str)<=1) return $str;//如果個數不大于一,直接返回 $key=$str[0];//取一個值,稍后用來比較; $left_arr=array(); $right_arr=array(); for($i=1;$i<count($str);$i++){//比$key大的放在右邊,小的放在左邊; if($str[$i]>=$key) $left_arr[]=$str[$i]; else $right_arr[]=$str[$i]; } $left_arr=quicksort($left_arr);//進行遞歸; $right_arr=quicksort($right_arr); return array_merge($left_arr,array($key),$right_arr);//將左中右的值合并成一個數組; } $neighbour = array();//$neighbour只是對cos值進行排序并存儲 $neighbour = quicksort($cos);
這里的$neighbour數組僅僅存儲了從大到小排序好的Cos值,并沒有與人聯系起來。這個問題還要解決。
選出Cos值最高的3個人,作為Leo的鄰居:
//$neighbour_set 存儲最近鄰的人和cos值 $neighbour_set = array(); for($i=0;$i<3;$i++){ for($j=0;$j<5;$j++){ if($neighbour[$i] == $cos[$j]){ $neighbour_set[$i][0] = $j; $neighbour_set[$i][1] = $cos[$j]; $neighbour_set[$i][2] = $array[$j][6];//鄰居對f的評分 $neighbour_set[$i][3] = $array[$j][7];//鄰居對g的評分 $neighbour_set[$i][4] = $array[$j][8];//鄰居對h的評分 } } } print_r($neighbour_set); echo "<p><br/>";
這一步得到的結果:
這是一個二維數組,數組第一層的下標為0,1,2,代表3個人。第二層下標0代表鄰居在數據表中的順序,比如Jhon是表中的第0個人;下標1代表Leo和鄰居的Cos值;下標2,3,4分別代表鄰居對f,g,h的評分。
開始進行預測,計算Predict代碼如下:
分別計算Leo對f,g,h的預測值。在此有一個問題,就是如果有的鄰居對f,g,h的評分為空,那么該如何處理。比如Jhon和Mary對h的評分就為空。本能的想到用if判斷一下,如果為空則跳過這組計算,不過這樣處理是否合理,有待考慮。以下代碼并沒有寫出這個if判斷。
//計算Leo對f的評分 $p_arr = array(); $pfz_f = 0; $pfm_f = 0; for($i=0;$i<3;$i++){ $pfz_f += $neighbour_set[$i][1] * $neighbour_set[$i][2]; $pfm_f += $neighbour_set[$i][1]; } $p_arr[0][0] = 6; $p_arr[0][1] = $pfz_f/sqrt($pfm_f); if($p_arr[0][1]>3){ echo "推薦f"; } //計算Leo對g的評分 $pfz_g = 0; $pfm_g = 0; for($i=0;$i<3;$i++){ $pfz_g += $neighbour_set[$i][1] * $neighbour_set[$i][3]; $pfm_g += $neighbour_set[$i][1]; $p_arr[1][0] = 7; $p_arr[1][1] = $pfz_g/sqrt($pfm_g); } if($p_arr[0][1]>3){ echo "推薦g"; } //計算Leo對h的評分 $pfz_h = 0; $pfm_h = 0; for($i=0;$i<3;$i++){ $pfz_h += $neighbour_set[$i][1] * $neighbour_set[$i][4]; $pfm_h += $neighbour_set[$i][1]; $p_arr[2][0] = 8; $p_arr[2][1] = $pfz_h/sqrt($pfm_h); } print_r($p_arr); if($p_arr[0][1]>3){ echo "推薦h"; } $p_arr是對Leo的推薦數組,其內容類似如下;
Array ( [0] => Array ( [0] => 6 [1] => 4.2314002228795 ) [1] => Array ( [0] => 7 [1] => 2.6511380196197 ) [2] => Array ( [0] => 8 [1] => 0.45287424581774 ) )
f是第6列,Predict值是4.23,g是第七列,Predict值是2.65........
求完了f,g,h的Predict值后有兩種處理方式:一種是將Predict值大于3的物品推薦給Leo,另一種是將Predict值從大到小排序,將Predict值大的前2個物品推薦給Leo。這段代碼沒有寫。
從上面的示例中可以看出,推薦算法的實現非常麻煩,需要循環,判斷,合并數組等等。如果處理不當,反而會成為系統的累贅。在實際處理中還有以下問題:
1.以上示例我們只對Leo進行推薦,而且我們已經知道Leo沒有評價過f,g,h物品。如果放到實際的系統里,對于每一個需要進行推薦的用戶,都要查詢出他沒有評價過哪些物品,這又是一部分開銷。
2.不應當進行整表查詢,在實際系統中可以設定一些標準值。比如:我們求Leo與表中的其他人的Cos值,如果該值大于0.80,則表示可以為鄰居。這樣,當我找到10個鄰居之后,就停止求Cos值,避免整表查詢。對于推薦物品也可以適當采用此方法,比如,我只推薦10個物品,推薦完后就停止求Predict值。
3.隨著系統的使用,物品也會發生變化,今天是fgh,明天沒準就是xyz了,當物品變化時,需要動態的改變數據表。
4.可以適當引進基于內容的推薦,來完善推薦算法。
5.推薦的精確性問題,這個設置不同的標準值,會影響精確性。
關于php可以實現推薦算法嗎問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。