首頁 >後端開發 >PHP問題 >php用不了推薦演算法嗎

php用不了推薦演算法嗎

angryTom
angryTom原創
2019-11-02 09:28:544634瀏覽

php用不了推薦演算法嗎

php用不了推薦演算法嗎?

推薦演算法是非常古老的,在機器學習還沒興起的時候就有需求和應用了。

協同過濾(Collaborative Filtering)作為推薦演算法中最經典的類型,包括線上的協同離線的過濾兩部分。所謂在線協同,就是透過線上數據找到用戶可能喜歡的物品,而離線過濾,則是過濾掉一些不值得推薦的數據,比比如推薦值評分低的數據,或者雖然推薦值高但是用戶已經購買的數據。

以下就介紹下怎樣用PHP MySQL實作簡單的協同過濾演算法。

要實作協同過濾推薦演算法,首先就要理解演算法的核心思想和流程。演算法的核心思想可以概括為:若a,b喜歡同一系列的物品(暫時稱b是a的鄰居吧),則a很可能喜歡b喜歡的其他物品。演算法的實作流程可以簡單概括為:1.確定a有哪些鄰居 2.透過鄰居來預測a可能會喜歡哪種物品  3.將a可能喜歡的物品推薦給a。

演算法核心的公式如下:

1.餘弦相似度(求鄰居):

php用不了推薦演算法嗎

2.預測公式(預測a可能會喜歡哪種物品):

php用不了推薦演算法嗎

僅從這兩個公式我們就可以看出,僅僅是按照這兩個公式進行計算,就需要進行大量的循環與判斷,也牽涉到排序的問題,就牽涉到排序演算法的選擇與使用,這裡選快排。

首先建表:

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);

php用不了推薦演算法嗎

這裡只對最後一行的Leo進行推薦,看看f,g,h哪個可以推薦給他。

使用php mysql,流程圖如下:

php用不了推薦演算法嗎

連接資料庫並將其儲存為二維數組的程式碼如下:

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/>";
}

這一步驟所得到的結果:

php用不了推薦演算法嗎

將求好的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/>";

這一步得到的結果:

php用不了推薦演算法嗎這是一個二維數組,數組第一層的下標為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相關知識,請造訪PHP中文網

以上是php用不了推薦演算法嗎的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

相關文章

看更多