QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1987|回复: 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

    * F4 t0 Q2 F) y& F+ b& k) P, ^, o' Uphp+mysql实现简单的协同过滤推荐算法* A! l+ w+ i4 p
    仅做标记。。。5 e/ n5 j( y& ?+ f, a- b
    ( d0 E2 G8 A4 R* l5 n, ^

    0 w& k3 W% j' g: i! _9 p1 b' R5 g4 R" B
    要实现协同过滤推荐算法,首先就要理解算法的核心思想和流程。该算法的核心思想可以概括为:若a,b喜欢同一系列的物品(暂时称b是a的邻居吧),则a很可能喜欢b喜欢的其他物品。算法的实现流程可以简单概括为:1.确定a有哪些邻居 2.通过邻居来预测a可能会喜欢哪种物品  3.将a可能喜欢的物品推荐给a。% \" |; J5 g" S* q( v  e- h

      [' a" Z4 @! z5 i& B算法核心的公式如下:3 O1 I1 {+ F. Q0 W3 L6 |7 v

    ; P) t8 e) s& P9 U1 o1.余弦相似度(求邻居):  R; T7 A+ a5 F* [4 h
    $ i! T$ W/ Y2 ^" V* u& Y+ @
    2.预测公式(预测a可能会喜欢哪种物品):
    ( O; J! f' ?" _4 N2 z7 X. d" v
    9 R5 O0 l! z; [' o仅从这两个公式我们就可以看出,仅仅是按照这两个公式进行计算,就需要进行大量的循环与判断,而且还涉及到排序的问题,就涉及到排序算法的选择与使用,这里我选快排,从网上copy了一段快排,直接用。总之实现起来很麻烦,在大数据情况下,更何谈效率。5 R% [4 Y0 b; V) I8 a5 ]

    3 ]( O- g4 b/ G- w: i  L首先建表:
    8 J9 S7 n4 v& K1 A( U3 R
    : f) a8 t% o( }, s1 hDROP TABLE IF EXISTS `tb_xttj`;( p( f* C: b: P" ~- I( c+ p$ H
    CREATE TABLE `tb_xttj` (9 O0 l" P# D, q: m/ v
      `name` varchar(255) NOT NULL,
    6 k+ f7 }2 G& n( h9 I  `a` int(255) default NULL,0 \6 Q- l: L# h0 ?! l7 S9 j
      `b` int(255) default NULL,$ k+ q& A, C) u  ~1 l7 M
      `c` int(255) default NULL,
    ; c$ e& A7 v5 I6 d1 H( c  `d` int(255) default NULL,
    ! s7 l+ {5 _3 n8 h+ O2 k+ ?; |+ \  `e` int(255) default NULL,2 `' p( {/ O5 F  t5 }, l2 p
      `f` int(255) default NULL,
    3 F: U( i: y+ i  ~* O  `g` int(255) default NULL,- M: J0 ~. l9 m9 K" Y$ M2 O
      `h` int(255) default NULL,( D2 [- ^% x# \* J6 [' k- _
      PRIMARY KEY  (`name`)
    $ B! i! }2 r3 s  \5 T) ENGINE=MyISAM DEFAULT CHARSET=latin1;
    ) {1 T8 j9 L0 V# \5 t6 B' I8 k2 B; M3 h
    INSERT INTO `tb_xttj` VALUES ('John', '4', '4', '5', '4', '3', '2', '1', null);
    0 @7 o$ d/ P$ y4 n8 V- ]INSERT INTO `tb_xttj` VALUES ('Mary', '3', '4', '4', '2', '5', '4', '3', null);
    * Y! ^/ f! Q5 Y' y0 Z+ |0 pINSERT INTO `tb_xttj` VALUES ('Lucy', '2', '3', null, '3', null, '3', '4', '5');
    ' f( K  A: W+ N5 rINSERT INTO `tb_xttj` VALUES ('Tom', '3', '4', '5', null, '1', '3', '5', '4');
    / T( u9 n. F5 c* X! s" o+ y; mINSERT INTO `tb_xttj` VALUES ('Bill', '3', '2', '1', '5', '3', '2', '1', '1');3 _% g5 J, w* |9 x1 C
    INSERT INTO `tb_xttj` VALUES ('Leo', '3', '4', '5', '2', '4', null, null, null);
    ; U) R) L( Q- N4 G! e; p/ E( O9 o* Z. j! D- n

    7 J& b5 t4 Q, F; Z( H 我这里只对最后一行的Leo进行推荐,看看f,g,h哪个可以推荐给他。* v2 ?, n* A6 D' T

    ' \: r8 C) ]2 H& x) k- J% M+ z    用php+mysql,流程图如下:5 q" M2 c+ I2 ?
    9 E6 l8 Z1 Z! q- _# B& R
    连接数据库并将其存储为二维数组的代码如下:
    3 y, g0 P. L  a0 B) ]+ e+ s4 `6 v4 d4 p3 i/ i2 Z; [- o
    header("Content-Type:text/html;charset=utf-8");
    # t% P& X, c! m* X
    3 A7 i* ]7 i- F3 o5 t5 Jmysql_connect("localhost","root","admin");
    1 q. P7 b( b' K5 e" z. ?mysql_select_db("geodatabase");
    % O( U7 M. i$ r/ z; S; q4 Xmysql_query("set names 'utf8'");        5 h, e1 R" F+ ]7 R& v& O1 T- h
    * |2 _; ^# s2 B2 y! X! \
    $sql = "SELECT * FROM tb_xttj";
    $ J+ c" z( [& M  q' Q9 w1 ?$result = mysql_query($sql);. e; U4 K. H/ }
    , ]( V2 y3 z1 G  }0 n- U5 g
    $array = array();
    5 W, j$ ^6 O: J. L! f! Xwhile($row=mysql_fetch_array($result))# y. }1 ]5 {8 }2 s& V9 e8 ]4 z+ R
    {! L+ N( l) J) {2 v' b# m8 x  U: K
            $array[]=$row;//$array[][]是一个二维数组& v$ ?! p" m7 G! E- O
    }
    . y& B* J9 D5 \. h3 U& W7 ?6 k. B/ r: L4 ~* I: C8 s
    问题1:这一步完全可以看做是整表查询,这种查询是大忌,对于这种小小的演示系统还可以,但是对大数据的系统,没有效率,至于如何改进,还得多学习才是。, `" t1 a/ s8 y! }, W4 p+ S4 `; ^
    , p1 h7 W6 n! J, N5 }
    求Leo与其他人的Cos值代码如下:
    ) O" F' Y0 x# O; [0 N1 x% P. Q! ~" ?4 L
    /*. D$ @& l- G# u5 C
    * 以下示例只求Leo的推荐,如此给变量命名我也是醉了;初次理解算法,先不考虑效率和逻辑的问题,主要把过程做出来
    * t  E  b% U7 m5 \1 g, w6 o6 Q$ J */
    7 ]# a/ X6 Y. t4 o$ z3 v! D" G# J. v& Q' i% R7 C- z6 S6 u# a  w
    $cos = array();* S% {" K. d  w/ l7 B2 }
    $cos[0] = 0;
    6 Y) J, _/ c; P! K5 ]$fm1 = 0;
    " D7 d' Y* ^; N/ ~( g- |: H//开始计算cos9 |# ?& f, j: y9 Y% a
    //计算分母1,分母1是第一个公式里面 “*”号左边的内容,分母二是右边的内容
    + I$ t- r) P' F) L' F" l7 n3 Wfor($i=1;$i<9;$i++){+ m# O2 i+ ?0 B+ B+ M
            if($array[5][$i] != null){//$array[5]代表Leo: W7 l+ h# P2 E: T# K: v/ o
                    $fm1 += $array[5][$i] * $array[5][$i];
    6 M( K, \3 _. D        }
    8 x/ S# S$ p9 c) z; L( t}1 |2 ~( v* t2 o/ Z1 i3 q

    ' J5 Y) U% P+ ^0 C' h& A' I$fm1 = sqrt($fm1);( c' ^3 Y1 ~+ h5 ^
    , s# }+ a1 L( j/ v: b
    for($i=0;$i<5;$i++){  Z& |% B$ m5 I
            $fz = 0;
    + F& W" R* m  @1 S3 V! g        $fm2 = 0;
    ) l; Q8 w/ h+ O0 ^3 b        echo "Cos(".$array[5][0].",".$array[$i][0].")=";
    , Y2 @- H1 X6 ^8 N        , q9 F* Y: I3 K: `$ p% c& v
            for($j=1;$j<9;$j++){
    / I' @% J5 @6 s# l            //计算分子$ ]7 }4 D4 [  @! _
                    if($array[5][$j] != null && $array[$i][$j] != null){. M' \0 y6 s1 J9 r2 \3 C
                            $fz += $array[5][$j] * $array[$i][$j];$ z# M; j0 c9 N; N& `
                    }. D; D: H" v) `  S1 A; W
                    //计算分母2
    & n" x  K2 c" r( l; a                if($array[$i][$j] != null){
      y' m6 P- E  Y  P5 ^. J                        $fm2 += $array[$i][$j] * $array[$i][$j];  u/ M" n# G2 _' _9 ~
                    }                        4 X: A5 q* k" {5 R4 k' R1 c
            }- q" \# n! |! s, H( I* D
            $fm2 = sqrt($fm2);
    $ G" p6 j0 @8 a9 d/ g2 G        $cos[$i] = $fz/$fm1/$fm2;- L6 n1 K3 s  N8 @- `
            echo $cos[$i]."<br/>";
    2 n/ n9 f1 L, O1 `}6 ~1 N5 G& ]0 B9 n

    0 N7 w" u/ }; |7 K这一步得到的结果是酱紫:
    ( l+ [, N+ n0 Z$ S& S: u/ B
    4 y' T$ |2 a' y5 H5 z& b将求好的Cos值排序,采用快排代码如下(百度copy而来):
    ; _& |$ R8 v9 G0 W  P: H/ `- w& Y. h' r" {

    8 N8 l# B  L# j6 L2 `- l+ g//对计算结果进行排序,凑合用快排吧先
    ' n" o* b" N4 [' \$ Gfunction quicksort($str){, P/ @8 M2 S9 e
            if(count($str)<=1) return $str;//如果个数不大于一,直接返回0 d: b9 X, ^; _0 z/ f* g
            $key=$str[0];//取一个值,稍后用来比较;  _% L! X# J2 h+ Y
            $left_arr=array();
    6 t2 U6 C! r7 l' f        $right_arr=array();
    : W! |& `9 n2 _! m1 F" h5 f) D        ) r" o# p9 V; M
            for($i=1;$i<count($str);$i++){//比$key大的放在右边,小的放在左边;
    : @; W* ?- k8 m8 p( X                if($str[$i]>=$key)1 H; ^& a5 ~9 p! j* ]* x, R, ^0 v6 l
                    $left_arr[]=$str[$i];
    ) [/ t2 {6 i1 b- \                else/ I  Y3 `; C) E
                    $right_arr[]=$str[$i];
    2 h1 e1 T. e' {- c9 b# i        }7 R' E8 M% ^6 H! P2 q- Y4 ?
            $left_arr=quicksort($left_arr);//进行递归;
      H3 J8 [0 N8 E2 V1 _        $right_arr=quicksort($right_arr);0 v; ^( m+ }  s5 J( B7 F9 T- i" J
            return array_merge($left_arr,array($key),$right_arr);//将左中右的值合并成一个数组;" J5 i* A  R% ]/ [
    }* P6 U- K8 P0 g" C* B
      F; h" E/ M# Z
    $neighbour = array();//$neighbour只是对cos值进行排序并存储
    7 S# x, ]% C/ R* u; {$neighbour = quicksort($cos);( E8 W* b1 s" F  T. [
    4 Q: a* }& [& z; z; D, p$ A

    5 ~; j4 `) a5 i这里的$neighbour数组仅仅存储了从大到小排序好的Cos值,并没有与人联系起来。这个问题还要解决。
    9 E3 x+ j3 V/ f, d
    + K9 {0 ]% Z9 r0 s选出Cos值最高的3个人,作为Leo的邻居:/ W+ h8 ~0 r" b( R( G( C2 V

    ! Q8 [9 w& l+ G" k+ t//$neighbour_set 存储最近邻的人和cos值) d- b; w0 l' _  j
    $neighbour_set = array();, p" x+ H- z% o$ V- N
    for($i=0;$i<3;$i++){
    5 d0 S2 ]% H* X  v$ k4 h9 F7 z8 E        for($j=0;$j<5;$j++){
    ! m; w1 l9 h& w6 O* J0 q                if($neighbour[$i] == $cos[$j]){! s/ ~' [  S- c& W& [) b
                            $neighbour_set[$i][0] = $j;1 y  `7 _9 h' v) V% V+ `; d
                            $neighbour_set[$i][1] = $cos[$j];
    . M. i, \2 F( k& {5 E; A                        $neighbour_set[$i][2] = $array[$j][6];//邻居对f的评分/ X: Q' u; }0 P9 |  ?' `5 [) R
                            $neighbour_set[$i][3] = $array[$j][7];//邻居对g的评分3 }& e; n; ?! H/ F" ^$ ]
                            $neighbour_set[$i][4] = $array[$j][8];//邻居对h的评分
    $ k$ J7 J( G) m( v4 O                }
    / S' U% s: L$ S) _) H        }! U1 c2 J5 p2 ]1 g9 \5 _  i
    }0 k' j; Z1 P$ i6 ?* ^
    print_r($neighbour_set);
    - T% l# t: q, ?) [3 Q2 zecho "<p><br/>";
      d0 [7 V9 h( C. S2 J
    ! _9 a2 j. l4 m这一步得到的结果是酱紫:
    ( z- R* z8 @: d+ |
    " K) a, x6 w/ V" T+ g/ [  K7 k& t8 O( i$ h' E
    ) ]2 f2 A* j- h& l2 {$ L  a3 W2 _
    转存失败重新上传取消
      M8 R6 F) I* s, o/ Z: i
    ) a; J/ l2 k4 D2 N# m6 w/ ?这是一个二维数组,数组第一层的下标为0,1,2,代表3个人。第二层下标0代表邻居在数据表中的顺序,比如Jhon是表中的第0个人;下标1代表Leo和邻居的Cos值;下标2,3,4分别代表邻居对f,g,h的评分。; x! X  a9 P! j3 X- i3 R

    8 ^- s9 L1 f& v4 L: x8 ~" K; q1 ?开始进行预测,计算Predict代码如下:
    - S% d5 E5 T$ W$ m# F' y8 ~2 b- D( x+ J1 Q5 R" u; \
    我是分别计算Leo对f,g,h的预测值。在此有一个问题,就是如果有的邻居对f,g,h的评分为空,那么该如何处理。比如Jhon和Mary对h的评分就为空。本能的想到用if判断一下,如果为空则跳过这组计算,不过这样处理是否合理,有待考虑。以下代码并没有写出这个if判断。
    " E- ~; M( D1 Y* X8 @( j
    & t# H& `' ?6 {( \5 C//计算Leo对f的评分
    : t* y9 m# Q( k0 [7 Q$ ^0 f# {. E$p_arr = array();
    , }4 Z1 E  O$ h$pfz_f = 0;
    $ \" C# Z+ B% Q5 F+ a7 c# h$pfm_f = 0;4 o# V. B4 H5 {; B1 D- Q% u$ M7 q2 X
    for($i=0;$i<3;$i++){  [4 m" t2 L$ o; r7 U3 v- p
            $pfz_f += $neighbour_set[$i][1] * $neighbour_set[$i][2];
    % d7 k' n, z0 `1 t4 \$ D        $pfm_f += $neighbour_set[$i][1];
      |- e. W% V1 }2 m5 Y+ j: b: H}
    # q7 c: i4 V  P  j& f# I$p_arr[0][0] = 6;  M4 A4 O! x) f# U% x+ S# u$ e
    $p_arr[0][1] = $pfz_f/sqrt($pfm_f);
    & f" I7 {8 _' sif($p_arr[0][1]>3){0 {. o- T! W& H4 n) k2 Z2 R0 c
            echo "推荐f";
    $ |2 L: a* t$ Q8 h* W}6 ~, l6 C6 x  y9 Y
    - C9 J+ W! M" Q8 h% w4 F' ^7 t+ m
    //计算Leo对g的评分
    # ^" _/ @4 M1 H! g( j$pfz_g = 0;
    2 G2 p) f, `' K) O) Z$pfm_g = 0;) D8 @% H' f# W( C, n
    for($i=0;$i<3;$i++){
    ) ]+ e; l4 {, B: i8 r        $pfz_g += $neighbour_set[$i][1] * $neighbour_set[$i][3];
    / Z9 X; W* N$ u9 N1 @% E7 |/ d        $pfm_g += $neighbour_set[$i][1];: t; j  Q6 h4 [$ B2 w: O: N; |5 i* w
            $p_arr[1][0] = 7;8 o7 c$ ]4 O4 t  f
            $p_arr[1][1] = $pfz_g/sqrt($pfm_g);) h; {# X6 |+ ^7 x
    }' E$ R2 H0 _1 ?) g/ ~' ?
    if($p_arr[0][1]>3){' W7 o- |& f6 L( L* p
            echo "推荐g";- l9 B* _. u8 ~- Q3 j0 r
    }( [: t: J% X* y

    8 J/ Y$ s# @6 b/ m. p/ p//计算Leo对h的评分
    0 U" H- A! L2 ^( H$ C$pfz_h = 0;
    3 `& L6 z: ]7 F+ Z$pfm_h = 0;3 d- I8 c" b7 h. G5 L/ y; ]
    for($i=0;$i<3;$i++){
    - {' Q7 j  K3 i; m        $pfz_h += $neighbour_set[$i][1] * $neighbour_set[$i][4];
    ( b: x) O9 l6 @2 ~- ?6 k4 ]# c* K        $pfm_h += $neighbour_set[$i][1];; s8 J4 O! t4 F/ [8 m% A9 u+ f
            $p_arr[2][0] = 8;
    3 m7 h6 M$ ?; d& E- o8 a        $p_arr[2][1] = $pfz_h/sqrt($pfm_h);2 x7 ?+ w' m) X: r) A
    }5 e6 {/ u0 p  J$ i3 ?, _) @
    print_r($p_arr);- [1 M0 j" R+ n- H, d& z" }( L
    if($p_arr[0][1]>3){" D# W5 j4 ?$ w0 `
            echo "推荐h";
    ; w5 h% H% ^4 _# ^0 O) [9 i0 R}% X) u# B3 U! ]5 T" S

    ' e( A# j2 D; ]$p_arr是对Leo的推荐数组,其内容类似如下;
    7 V; V* n; W& v
    4 E& B  Y/ `6 _* ?1 w& r% R8 j2 PArray ( [0] => Array ( [0] => 6 [1] => 4.2314002228795 ) [1] => Array ( [0] => 7 [1] => 2.6511380196197 ) [2] => Array ( [0] => 8 [1] => 0.45287424581774 ) )6 A# z- e( T8 }- h9 y" h
    9 ?/ l% U! ]) o& X7 v2 v/ e4 T
    f是第6列,Predict值是4.23,g是第七列,Predict值是2.65........
    3 M" s* l, {0 i# u% \9 V7 m: }3 Z" j* u& z( Y3 j: q3 `
    求完了f,g,h的Predict值后有两种处理方式:一种是将Predict值大于3的物品推荐给Leo,另一种是将Predict值从大到小排序,将Predict值大的前2个物品推荐给Leo。这段代码没有写。
    / K( t, Q0 k- H+ r- O2 V! a! S
    从上面的示例中可以看出,推荐算法的实现非常麻烦,需要循环,判断,合并数组等等。如果处理不当,反而会成为系统的累赘。在实际处理中还有以下问题:
    , _. Y* M- o4 x& [/ J: R3 b6 B
    ( d. f7 l/ y5 Z0 S, B+ T2 W1.以上示例我们只对Leo进行推荐,而且我们已经知道Leo没有评价过f,g,h物品。如果放到实际的系统里,对于每一个需要进行推荐的用户,都要查询出他没有评价过哪些物品,这又是一部分开销。
    + y1 D1 k& S( r7 ]& w& J
    9 u8 N" p, f0 w/ Q+ T- X! g5 P& y2.不应当进行整表查询,在实际系统中可以设定一些标准值。比如:我们求Leo与表中的其他人的Cos值,如果该值大于0.80,则表示可以为邻居。这样,当我找到10个邻居之后,就停止求Cos值,避免整表查询。对于推荐物品也可以适当采用此方法,比如,我只推荐10个物品,推荐完后就停止求Predict值。
    + h+ c% y: Q2 c+ m! b
    7 d5 u% M; p" _3.随着系统的使用,物品也会发生变化,今天是fgh,明天没准就是xyz了,当物品变化时,需要动态的改变数据表。
    " {5 T" n1 a) |; }9 }
    5 v% _5 ^# x8 M; X4.可以适当引进基于内容的推荐,来完善推荐算法。( ]* d3 D7 u' T5 d+ M
    * m  h4 H+ \1 C' X
    5.推荐的精确性问题,这个设置不同的标准值,会影响精确性。! T* y7 O) s# y9 S6 ?9 ]
    ————————————————
    0 x4 e! b. ~. e9 W0 Q. C% B版权声明:本文为CSDN博主「星斗其文,赤子其人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。& Z' r9 U/ X) _; Z9 h
    原文链接:https://blog.csdn.net/liuliuhelingdao/article/details/126715465
    ; m) O5 f+ y) a6 d) ^1 J
    ' a6 l5 O" U) j! n% _/ f* g- L
    ! H9 Q4 a$ C: X% R
    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-9-27 15:12 , Processed in 0.338926 second(s), 50 queries .

    回顶部