QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1891|回复: 0
打印 上一主题 下一主题

php+mysql实现简单的协同过滤推荐算法

[复制链接]
字体大小: 正常 放大
杨利霞        

5273

主题

82

听众

17万

积分

  • TA的每日心情
    开心
    2021-8-11 17:59
  • 签到天数: 17 天

    [LV.4]偶尔看看III

    网络挑战赛参赛者

    网络挑战赛参赛者

    自我介绍
    本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。

    群组2018美赛大象算法课程

    群组2018美赛护航培训课程

    群组2019年 数学中国站长建

    群组2019年数据分析师课程

    群组2018年大象老师国赛优

    跳转到指定楼层
    1#
    发表于 2022-9-8 10:21 |只看该作者 |倒序浏览
    |招呼Ta 关注Ta

    2 u  I+ @0 W* f% U, p5 a1 ?6 z; L7 Tphp+mysql实现简单的协同过滤推荐算法
    / z: i: R1 W6 e6 w0 }$ y仅做标记。。。
    5 ]7 R) ?/ d9 ?$ C
    & m: k2 D* W3 v* o6 A9 g3 z# J- \- m7 F3 V) {& ?3 E

    + j, w( j# Q1 j0 s* f, S; W7 m要实现协同过滤推荐算法,首先就要理解算法的核心思想和流程。该算法的核心思想可以概括为:若a,b喜欢同一系列的物品(暂时称b是a的邻居吧),则a很可能喜欢b喜欢的其他物品。算法的实现流程可以简单概括为:1.确定a有哪些邻居 2.通过邻居来预测a可能会喜欢哪种物品  3.将a可能喜欢的物品推荐给a。2 I/ K+ ]% a# |, ?% r3 k

    2 G9 P, M+ ?  I: o. z  m! r算法核心的公式如下:
    0 `# \! p+ _! P- |7 t% F4 @+ J! G- r+ a# v, s. C8 V3 g$ D
    1.余弦相似度(求邻居):
    # |7 M) t9 Z+ q/ }/ O7 D  p; l" {0 d* z+ \9 [" c- y# H
    2.预测公式(预测a可能会喜欢哪种物品):
    ( @# h. R4 ?) h- ]$ |0 v( M! @* |  f3 c3 N7 O" m" }
    仅从这两个公式我们就可以看出,仅仅是按照这两个公式进行计算,就需要进行大量的循环与判断,而且还涉及到排序的问题,就涉及到排序算法的选择与使用,这里我选快排,从网上copy了一段快排,直接用。总之实现起来很麻烦,在大数据情况下,更何谈效率。
    $ M' f4 w. }( D, ~3 _
    : E1 B7 U0 `- `: Q首先建表:1 \# _7 q% H' s1 [+ B

    ! ?. n$ z6 I6 r7 C5 TDROP TABLE IF EXISTS `tb_xttj`;# A9 q5 z& Y0 D* Y) _/ k: {
    CREATE TABLE `tb_xttj` (
    8 ^. `( m) |; \  `name` varchar(255) NOT NULL,& v3 u  e( R' x% Q9 R" [2 C: y# n
      `a` int(255) default NULL,9 d0 x* ?# N' m& Y0 ]7 ?) U
      `b` int(255) default NULL,
    5 G: G) o* }( G9 [  `c` int(255) default NULL,! J8 \% q! i- _! q" S
      `d` int(255) default NULL,* h* E6 c2 e0 P& g
      `e` int(255) default NULL,
    + g5 }. a1 _0 p  `f` int(255) default NULL,
    2 r( T( N, I+ Y% p2 [6 O' g* |  `g` int(255) default NULL,
    7 V+ Z; H: _' |2 x6 K  `h` int(255) default NULL," s5 D2 N* H/ p( c( P2 v, L1 b% Q
      PRIMARY KEY  (`name`)( _* ?/ ], u4 C% L: F" s. t; _# D
    ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
    $ V! ]6 ^  a' u
    % g; @6 `8 x  t* T1 A: H+ l: S4 qINSERT INTO `tb_xttj` VALUES ('John', '4', '4', '5', '4', '3', '2', '1', null);6 _2 @) d. m- ^7 x
    INSERT INTO `tb_xttj` VALUES ('Mary', '3', '4', '4', '2', '5', '4', '3', null);
    6 f- P, Q; [8 {4 h* JINSERT INTO `tb_xttj` VALUES ('Lucy', '2', '3', null, '3', null, '3', '4', '5');( p& ^9 |" y# w/ j
    INSERT INTO `tb_xttj` VALUES ('Tom', '3', '4', '5', null, '1', '3', '5', '4');% P5 D' o% k4 w; v
    INSERT INTO `tb_xttj` VALUES ('Bill', '3', '2', '1', '5', '3', '2', '1', '1');, b0 @! o5 p2 C+ }  n8 K. j
    INSERT INTO `tb_xttj` VALUES ('Leo', '3', '4', '5', '2', '4', null, null, null);
    / y; b1 U9 {2 j( g) U7 }2 @6 P7 I
    7 [& E! X+ {, w- O, I7 p# w
    . {# [% z7 O. ]& o. G. Z 我这里只对最后一行的Leo进行推荐,看看f,g,h哪个可以推荐给他。
    + C6 c8 u0 i6 K+ `0 j0 u, u# l! `1 @  ?6 J% w
        用php+mysql,流程图如下:
    5 F0 F; s: b( r: z; ~8 K
    # Z7 q1 K4 \( U连接数据库并将其存储为二维数组的代码如下:4 Y2 y4 p* K4 y

    & F( U" ~9 C8 d2 t# D; {header("Content-Type:text/html;charset=utf-8");! v, f. ?7 E2 d9 S
    2 L9 k+ |; w' v9 H' I
    mysql_connect("localhost","root","admin");/ B4 \% h; W4 L) @/ n2 e% K$ f
    mysql_select_db("geodatabase");
    ; J% g9 [6 s( q$ [1 rmysql_query("set names 'utf8'");       
    # T& q* V+ D9 ^1 P, K: O4 w0 i# F
    " f0 N# @% O' ?! W$sql = "SELECT * FROM tb_xttj";/ W* h! n/ f) e) a) ?/ T4 F
    $result = mysql_query($sql);. w/ u9 ]. N; B

    7 D8 i0 k4 t- l% K# d3 J% Z7 o$array = array();. O, d3 R4 p7 }1 o$ q  r
    while($row=mysql_fetch_array($result)): G! ?% p) G" A7 K8 m
    {
    4 f  ]" K9 a; J/ G$ h: k, I: Q        $array[]=$row;//$array[][]是一个二维数组
    ' R- J# u  C' L9 R  F& `} # a, k, `3 v3 X, Z6 {$ F
    $ c% ?9 J! {- b) t" u
    问题1:这一步完全可以看做是整表查询,这种查询是大忌,对于这种小小的演示系统还可以,但是对大数据的系统,没有效率,至于如何改进,还得多学习才是。+ Q+ ~; f5 R2 g/ K/ Q# ?

    7 N  h* A* l6 r; A求Leo与其他人的Cos值代码如下:! f0 ^4 C3 W& P# m

    0 x3 @3 |# h9 E) S4 h0 J/*
    . t/ d3 X9 L2 A$ `" o. z% B * 以下示例只求Leo的推荐,如此给变量命名我也是醉了;初次理解算法,先不考虑效率和逻辑的问题,主要把过程做出来
    6 r& Q/ D" |8 g/ j */
    1 b" I8 i! h. g+ D
    ' N/ `) m2 c2 k$cos = array();) Q0 p5 h1 A5 j  Q6 Z, g+ l5 y
    $cos[0] = 0;
    # A; R( e& ^) \. p6 `3 m: J$fm1 = 0;
    1 ?; O6 c# g- O) d3 S//开始计算cos
    7 S: ]; y, S4 v1 g! I' t; H' d//计算分母1,分母1是第一个公式里面 “*”号左边的内容,分母二是右边的内容
    ! l  J, K9 D+ X; N, Cfor($i=1;$i<9;$i++){$ U# W2 B2 C4 u% v% s
            if($array[5][$i] != null){//$array[5]代表Leo1 u* A4 Q/ ^5 z! F
                    $fm1 += $array[5][$i] * $array[5][$i];0 l1 |; |$ |- F- ~# W& M9 O4 a: {. `
            }! K* _7 h; g. S
    }
    ( u* N6 s, g; j3 J6 O
    6 w: i0 j/ q- Z4 A& l$ ^$fm1 = sqrt($fm1);
    4 n" t4 ^7 j& G' x/ ?; ?$ e3 n- J! f: W4 w8 a! S
    for($i=0;$i<5;$i++){, w( ?+ t9 G+ q. {$ o& n8 f' j+ o
            $fz = 0;  m7 G4 {  z6 ?# D0 x2 F7 ?" z
            $fm2 = 0;
    6 S5 E6 j8 m( X/ e        echo "Cos(".$array[5][0].",".$array[$i][0].")=";/ a+ c, ~9 w, L
            5 T5 F7 D8 h5 M
            for($j=1;$j<9;$j++){6 b, n% q- N% w- x- ?' G5 m4 |: i
                //计算分子8 t) T/ p# J( k
                    if($array[5][$j] != null && $array[$i][$j] != null){+ [4 Z1 h( H, y. {
                            $fz += $array[5][$j] * $array[$i][$j];, a# ?' u: A9 U% \. g% u
                    }: k/ i9 q% g1 @3 t+ a2 ], F& R; S
                    //计算分母27 B  `8 _6 H$ F/ w% @1 ~
                    if($array[$i][$j] != null){1 m" J. U+ w( A$ }5 g
                            $fm2 += $array[$i][$j] * $array[$i][$j];2 {1 _2 u; e2 [7 U) B
                    }                       
    2 S. F& [3 ]5 O1 n        }
    " p7 S( n& a& Z        $fm2 = sqrt($fm2);
    : e. f2 M* Q3 r' C$ {        $cos[$i] = $fz/$fm1/$fm2;
    , n9 u8 |; y0 J! u) n: ?! @        echo $cos[$i]."<br/>";
    & @: J6 V. [7 b( p}$ S8 t6 o  f# ]6 p6 n

    , A+ z$ [. O- s" O7 F- t这一步得到的结果是酱紫:
    , ?3 |- P5 ~$ m% }+ l: ?& n" `" F& B3 R% B! j" P( K( q* A
    将求好的Cos值排序,采用快排代码如下(百度copy而来):
    / y: m1 O+ C/ z
    6 f8 S! W* H" B4 t' C( U
    # O7 p) W; N& T0 |! ]//对计算结果进行排序,凑合用快排吧先$ C% L* K% _  t; |, B/ M
    function quicksort($str){, W, T; c7 C' X
            if(count($str)<=1) return $str;//如果个数不大于一,直接返回
    ) Q6 o' q( K3 Y- P9 ~        $key=$str[0];//取一个值,稍后用来比较;7 p, u0 t+ ]  R. P8 ^- V' Y
            $left_arr=array();
    0 D5 E+ x- F' q; P  b, ^        $right_arr=array();+ S! L+ F% Q0 _* t/ y
            " Y: v5 b" n  ^; d. N
            for($i=1;$i<count($str);$i++){//比$key大的放在右边,小的放在左边;. `' N) L( X7 m* v% W: N
                    if($str[$i]>=$key)
    - k+ C1 l8 U" K) w- O* X4 a                $left_arr[]=$str[$i];
    3 Z4 b9 l4 @+ a+ h0 F0 b* A: C% _                else
    5 E$ ^3 z3 B- F5 p5 N                $right_arr[]=$str[$i];* c' v# N3 R0 a6 _9 S' j: R
            }0 }* {4 |. P! Q  m
            $left_arr=quicksort($left_arr);//进行递归;* B6 b: x, \1 c6 V5 i
            $right_arr=quicksort($right_arr);8 R4 y% W/ c: h- Q" ~8 |: h
            return array_merge($left_arr,array($key),$right_arr);//将左中右的值合并成一个数组;/ Q) s6 _1 O8 S
    }
    6 x* I8 x5 e$ b! h) z
    ) x; o4 n" C6 _* n1 O- }$neighbour = array();//$neighbour只是对cos值进行排序并存储6 X  p$ x6 \/ X* r
    $neighbour = quicksort($cos);. `! G" p/ e4 F% Z5 \
    7 u) N6 V* w( d
    4 c1 x- X, e; `  |" S5 v: S: k
    这里的$neighbour数组仅仅存储了从大到小排序好的Cos值,并没有与人联系起来。这个问题还要解决。
    , |  X7 {! q0 n3 x# t; Q; o' b6 P1 U8 A
    选出Cos值最高的3个人,作为Leo的邻居:8 T9 R3 Q. v. E9 e7 ?
    * s3 L1 J- y! z/ X
    //$neighbour_set 存储最近邻的人和cos值
    + g3 G" Z7 s! u! E$neighbour_set = array();6 i$ ~" g+ o; ^7 J4 ^8 p
    for($i=0;$i<3;$i++){+ B# {8 F: G' |# v7 t% T( |$ h, e
            for($j=0;$j<5;$j++){; {& `/ R( G1 g' X5 y* \2 \! u" z3 t
                    if($neighbour[$i] == $cos[$j]){
    : X+ ]# p0 i/ z- t  e2 n7 \4 o                        $neighbour_set[$i][0] = $j;
    ) A9 l3 X+ T* x9 Z% k% ]4 d# E( a; h                        $neighbour_set[$i][1] = $cos[$j];
    ! Y% K6 I$ ]- H0 M9 r( }                        $neighbour_set[$i][2] = $array[$j][6];//邻居对f的评分
    ' G3 F' O, j7 o; F6 t                        $neighbour_set[$i][3] = $array[$j][7];//邻居对g的评分
    0 J, O1 b% m2 ^3 p1 h. L                        $neighbour_set[$i][4] = $array[$j][8];//邻居对h的评分
    & P. ^8 q0 N1 {4 {7 y7 o8 i7 P; K                }6 F  \) Z3 {5 N( s; B
            }0 ~' q* v2 j% S# g4 H
    }* E! c9 W( x  b5 L
    print_r($neighbour_set);
    2 x  r; y; o. N- A4 r# G$ Becho "<p><br/>";9 T  S" Z/ T6 n8 ^
    ) K# B5 e% ^8 m* I& E) E& ~
    这一步得到的结果是酱紫:
    % ^3 _  g0 E: _5 z4 F2 A% T" x$ }% K! O2 U) D

    " R# `6 N4 {7 R+ ^- v8 t; _
    " n2 J- V* u- b: N0 T( \转存失败重新上传取消: h( |+ z) ?+ m# O9 h

    + }; k' H, s& D8 G1 r) X) m' }这是一个二维数组,数组第一层的下标为0,1,2,代表3个人。第二层下标0代表邻居在数据表中的顺序,比如Jhon是表中的第0个人;下标1代表Leo和邻居的Cos值;下标2,3,4分别代表邻居对f,g,h的评分。5 H: j7 I0 K$ L2 c! p6 l6 z4 F
    " S" U/ Z! }6 [: S& _) r" ]
    开始进行预测,计算Predict代码如下:$ ?+ k1 M. [0 L. M+ K
    $ X4 G6 R. B$ s
    我是分别计算Leo对f,g,h的预测值。在此有一个问题,就是如果有的邻居对f,g,h的评分为空,那么该如何处理。比如Jhon和Mary对h的评分就为空。本能的想到用if判断一下,如果为空则跳过这组计算,不过这样处理是否合理,有待考虑。以下代码并没有写出这个if判断。# b8 d# a6 B5 b* J+ A- d

    / C9 v4 {' C5 A9 W/ v! z+ ~0 P//计算Leo对f的评分
    8 z& Y* X/ o9 e; w1 X8 L! w$p_arr = array();
    $ B& ?- B) Q) h- j  B$pfz_f = 0;
    1 [- T* B- Q! \( U. A8 X$pfm_f = 0;
    $ o; ]/ e3 ^: V/ bfor($i=0;$i<3;$i++){
    4 {: i0 O' P3 r        $pfz_f += $neighbour_set[$i][1] * $neighbour_set[$i][2];' y. X2 W" l+ L, D' J* L
            $pfm_f += $neighbour_set[$i][1];
    1 H/ F: G* d/ y/ D6 K. N4 y}
    " b5 z/ [6 v$ b$p_arr[0][0] = 6;
    1 s6 G- v  \; h8 I! ~6 {$p_arr[0][1] = $pfz_f/sqrt($pfm_f);6 `6 {! q; w" a1 R
    if($p_arr[0][1]>3){
    ; r7 x8 ^1 F9 _- P        echo "推荐f";
    9 B2 G3 f% C+ A4 C" l/ L}
    , h7 P' ^* v$ o% l7 b7 M; a1 h. L+ ]4 h0 X
    //计算Leo对g的评分
    2 H7 r# s$ Y7 l$ q$pfz_g = 0;
    & d2 Q( }) M* ]$ j# g8 O* J; c- C$pfm_g = 0;* H( z8 O; ^% @9 X
    for($i=0;$i<3;$i++){. I* Z4 G4 L; J+ t: D5 t
            $pfz_g += $neighbour_set[$i][1] * $neighbour_set[$i][3];7 L8 C( y" b! Z! r5 I' L/ m
            $pfm_g += $neighbour_set[$i][1];; k, b% i+ {* r$ p0 I& h
            $p_arr[1][0] = 7;
    1 B) b* U( S! e5 n) g' l; V2 T        $p_arr[1][1] = $pfz_g/sqrt($pfm_g);6 v+ v8 `. }' F3 U' p% P
    }( R. c; U- l4 Y' D" K) k) u1 l2 x9 V
    if($p_arr[0][1]>3){
    ( E4 `6 I7 V/ ?3 @# H        echo "推荐g";2 N5 a2 ^. U$ P+ Z! W& W
    }2 s( J/ {' T3 b8 N3 L# g
    / i3 t  [: X, C& Z+ m# Z5 B
    //计算Leo对h的评分
    , w+ R, T* ]+ H  L  v" Q$pfz_h = 0;
      v' P! K7 v/ ?1 k2 f; a7 ~$pfm_h = 0;  I6 V- J: K9 v6 v, b
    for($i=0;$i<3;$i++){! a  x( @6 H, c' V$ x( ~1 {$ Z
            $pfz_h += $neighbour_set[$i][1] * $neighbour_set[$i][4];
      Y0 O" y5 p3 S# b1 s        $pfm_h += $neighbour_set[$i][1];
    " P. X( x' ~5 J        $p_arr[2][0] = 8;7 R0 v& i/ ]- e8 Y+ Y8 G' i7 R
            $p_arr[2][1] = $pfz_h/sqrt($pfm_h);$ a# _; ~0 M- E( @% ~
    }
    . A" k/ ^9 G# K7 mprint_r($p_arr);
    % q$ p3 h! z$ a: j. v2 `4 Cif($p_arr[0][1]>3){
    " B: e+ N; G, q: \1 @3 h- w        echo "推荐h";9 ~% L. a; d* k9 L- h0 j0 k2 K$ y
    }
    * l6 E2 z* k7 [3 R4 W- P; |/ y0 Q1 D1 a' o+ t5 P  p4 d
    $p_arr是对Leo的推荐数组,其内容类似如下;
    & v6 |! \$ O' M& H" m; h. @* P; K+ b' \
    Array ( [0] => Array ( [0] => 6 [1] => 4.2314002228795 ) [1] => Array ( [0] => 7 [1] => 2.6511380196197 ) [2] => Array ( [0] => 8 [1] => 0.45287424581774 ) )& C3 B4 H, I( w, a3 @
    3 Y( R( c8 B( Y0 X) B% j
    f是第6列,Predict值是4.23,g是第七列,Predict值是2.65........
    1 M* n6 q0 Z: V
    : Y; r4 }3 Y" q* @" h求完了f,g,h的Predict值后有两种处理方式:一种是将Predict值大于3的物品推荐给Leo,另一种是将Predict值从大到小排序,将Predict值大的前2个物品推荐给Leo。这段代码没有写。
    # q5 k9 r: v, p* Y% e$ o
    % G3 Q  N, u  B4 I; ~" k& o7 ~从上面的示例中可以看出,推荐算法的实现非常麻烦,需要循环,判断,合并数组等等。如果处理不当,反而会成为系统的累赘。在实际处理中还有以下问题:
    / p: Y  R3 ~+ L* L% l: \* M5 ~% O
    1.以上示例我们只对Leo进行推荐,而且我们已经知道Leo没有评价过f,g,h物品。如果放到实际的系统里,对于每一个需要进行推荐的用户,都要查询出他没有评价过哪些物品,这又是一部分开销。
    , }8 M" J: w- O; F" Q& ]6 p8 `, m" m8 Q, N0 y5 M; [
    2.不应当进行整表查询,在实际系统中可以设定一些标准值。比如:我们求Leo与表中的其他人的Cos值,如果该值大于0.80,则表示可以为邻居。这样,当我找到10个邻居之后,就停止求Cos值,避免整表查询。对于推荐物品也可以适当采用此方法,比如,我只推荐10个物品,推荐完后就停止求Predict值。
    6 {$ w  ]" s$ @9 c$ S* j6 X) P+ `: F% o7 F
    3.随着系统的使用,物品也会发生变化,今天是fgh,明天没准就是xyz了,当物品变化时,需要动态的改变数据表。
    , o3 z8 {- m8 h- S0 o
    ) z' G- W- M4 N0 Q# J; o- t4.可以适当引进基于内容的推荐,来完善推荐算法。
    ; q5 Y' ^7 F( T! A, F! U+ S2 b+ b
    # x& m& ]: M  R0 d. U5.推荐的精确性问题,这个设置不同的标准值,会影响精确性。
    6 g; a* M7 ~0 B  ?0 W% a8 C1 u————————————————$ A9 R# x( Q1 C- \& R
    版权声明:本文为CSDN博主「星斗其文,赤子其人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    / W5 y4 Y  Q, i原文链接:https://blog.csdn.net/liuliuhelingdao/article/details/126715465
    0 V8 O% @; q. a  G2 u8 W' E+ c- M0 H% ]5 Q5 l5 e

    ! g' y! [( ~; i! Y' T
    zan
    转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
    您需要登录后才可以回帖 登录 | 注册地址

    qq
    收缩
    • 电话咨询

    • 04714969085
    fastpost

    关于我们| 联系我们| 诚征英才| 对外合作| 产品服务| QQ

    手机版|Archiver| |繁體中文 手机客户端  

    蒙公网安备 15010502000194号

    Powered by Discuz! X2.5   © 2001-2013 数学建模网-数学中国 ( 蒙ICP备14002410号-3 蒙BBS备-0002号 )     论坛法律顾问:王兆丰

    GMT+8, 2026-4-10 16:32 , Processed in 0.261858 second(s), 51 queries .

    回顶部