PHP + MySQL 实现协同过滤推荐

PHP + MySQL 协同过滤推荐

最近毕设项目中需要用到推荐,不得不把校内实习的推荐算法欠账补一补。

参考文章:动手实现基于协同过滤的电影推荐系统

话不多说上代码:

建表

用户历史记录:

用户身份、歌单的点击次数、歌单ID

CREATE TABLE `themusic`.`history` (
  `UserEmail` VARCHAR(80) NOT NULL,
 `countClick` int default 0,
 `listClick` VARCHAR(80) not null,
 CONSTRAINT history_ibfk_1 
FOREIGN KEY (`UserEmail`) REFERENCES `users` (`UserEmail`) 
ON DELETE CASCADE ON UPDATE CASCADE
);
用户推荐列表:

每个只做四个推荐:

CREATE TABLE `themusic`.`recommend` (
  `UserEmail` VARCHAR(80) NOT NULL,
 `recommentList_1` VARCHAR(80) not null,
`recommentList_2` VARCHAR(80) not null,
`recommentList_3` VARCHAR(80) not null,
`recommentList_4` VARCHAR(80) not null,
 CONSTRAINT recommend_ibfk_1 
FOREIGN KEY (`UserEmail`) REFERENCES `users` (`UserEmail`) ON DELETE CASCADE ON UPDATE CASCADE
);

推荐

读取用户

从用户表中读取所有用户信息,用户Email做数组键名。

    $arr_to_cos = array() ;			//用户的二维数组
    $arr_user = array(); 			//存储余弦相似度
    $userListNum = array();			//用户点击的歌单总数
    
    $sql = "select users.UserEmail from users ";
    $result = mysqli_query($sqli_con,$sql);
    if(mysqli_num_rows($result)>0){
        while($row = mysqli_fetch_assoc($result)){
            $arr_to_cos[$row['UserEmail']] = array();
            $arr_user[$row['UserEmail']] = array();
            $userListNum[$row['UserEmail']] = array();
        }
    }
读取信息
    $sql = "select * from history ";
    $result = mysqli_query($sqli_con,$sql);
    if(mysqli_num_rows($result)>0){
        while($row = mysqli_fetch_assoc($result)){
            $arr_to_cos[$row['UserEmail']][$row['listClick']] = $row['countClick'];
        }
    }
    foreach($arr_to_cos as $Email => $user) { //注意$v前面的&,引用指针
        $userListNum[$Email] = count($user);
    }

其中:

$arr_to_cos[$row['UserEmail']][$row['listClick']] = $row['countClick'];

从历史记录中读取用户点击的歌单ID 和 次数,UserEmail中歌单ID做键名,点击次数做键值。

foreach($arr_to_cos as $Email => $user) { //注意$v前面的&,引用指针
        $userListNum[$Email] = count($user);
    }

遍历二维数组中的每一个用户,计算用户单击歌单总数存入 $userListNum

进行推荐
foreach($arr_to_cos as $uEmail => $user)//遍历每一个用户
foreach($arr_to_cos as $oEmail =>$other){ //每一个用户相对其他用户
	if($uEmail == $oEmail)continue;
	/*array_intersect_key 函数,比价两个数组键值,返回交集*/
	/*$user 和$other 分别存储用户A和B 点击过的歌单数据(ID=>Count)*/
	/*$union表示用户A和B 共同点击过的歌单数据*/
	$union = array_intersect_key($user,$other);
	/*$num表示用户A和B 共同点击过的歌单个数*/
	$num = count($union);
	/*计算两个用户之间的余弦相似度*/
	if($userListNum[$uEmail] * $userListNum[$oEmail] != 0){
	    $arr_user[$uEmail][$oEmail] = $num /( sqrt($userListNum[$uEmail] * $userListNum[$oEmail]) );
	}else{
	    $arr_user[$uEmail][$oEmail] = 0;
	}
	echo $num;
	}

余弦相似度:
余弦相似度
相对用户A与其他用户 的余弦相似度排序:

arsort($arr_user[$uEmail]);

从中选取相似度最高的 K(3)个用户:

$max_K = array_slice($arr_user[$uEmail],0,3);

初始化一个数组,存储K个用户的所有歌单:

$max_K_list = array();

对歌单打分,用户对歌单的感兴趣程度表现为点击歌单的次数

        foreach($max_K as $oEmail =>$cos){		//K个用户 及其余弦相似度
            foreach ($arr_to_cos[$oEmail] as $list => $count){ //每个用户的点击数据
           		 /*如果 $max_K_list 中存在 $list  */
                if(array_key_exists($list,$max_K_list) ){
                	/*$count 点击次数,表示用户感兴趣程度  */
					$max_K_list[$list] += $count * $cos;//更新数据
                }else{
                    $max_K_list[$list] = $count * $cos;//添加数据
                }
            }
        }

计算用户u 对 与u最相似的K个用户点击过的歌单 的感兴趣程度(打分):
歌单打分
对K个用户点击过得所有歌单 按照 上一步计算的分数 排序:

arsort($max_K_list);

取分数最高的 N (4) 个歌单 ID:

$recommend = array_slice($max_K_list,0,4,true);

此时歌单ID是键名,把它提取出来:

$relist = array();
        foreach ($recommend as $li => $vi){
            array_push($relist,$li);
        }

接下开查找歌单,存入推荐列表:

$sql = "select * from recommend where UserEmail = '".$uEmail."'";
        $result = mysqli_query($sqli_con,$sql);

        if(mysqli_num_rows($result)>0){
            $sql = "update recommend set recommentList_1 ='".$relist[0].
                "',recommentList_2 = '".$relist[1].
                "',recommentList_3 = '".$relist[2].
                "',recommentList_4 = '".$relist[3].
                "'where UserEmail =  '".$uEmail."'";
            $result = mysqli_query($sqli_con,$sql);
        }else{
            $sql = "insert into recommend VALUES ('".$uEmail."','".$relist[0].
                "','".$relist[1]."','".$relist[2]."','".$relist[3]."')";
            $result = mysqli_query($sqli_con,$sql);
        }

结束

2020/06/08 更新

使用过程中发现,算法会向用户推荐历史记录中的歌曲,在用户量小时尤为明显,因为用户本身历史记录包含在M个用户浏览的N个歌单中。
计算用户感兴趣程度后,与历史记录取差集可避免推荐列表中出现历史记录。

$max_K_list = array_diff_key($max_K_list,$user);
  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值