QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1892|回复: 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
    ; P" b4 n" N+ l3 O! k- g
    php+mysql实现简单的协同过滤推荐算法( c6 D: A, _5 b
    仅做标记。。。
    ( [) \0 m! m0 V; O) d
    ) E: x* h) C/ o5 a: O; X8 c
    1 c. \! }$ }  n3 ^6 c' I
    $ p- C. {. D9 P& y9 S要实现协同过滤推荐算法,首先就要理解算法的核心思想和流程。该算法的核心思想可以概括为:若a,b喜欢同一系列的物品(暂时称b是a的邻居吧),则a很可能喜欢b喜欢的其他物品。算法的实现流程可以简单概括为:1.确定a有哪些邻居 2.通过邻居来预测a可能会喜欢哪种物品  3.将a可能喜欢的物品推荐给a。! w9 K. N9 G" ?7 c' ~+ ~# O" q# L1 t6 n

    0 V/ S3 u; f1 w" S) W0 O算法核心的公式如下:5 U1 p4 Y% z! O3 C" @6 _5 M
    * ^# ^3 ]  H5 A6 W" O5 ^
    1.余弦相似度(求邻居):1 c5 L1 X" _! V& X) e* d+ E
    8 S0 M( W0 O$ x. G9 L8 F
    2.预测公式(预测a可能会喜欢哪种物品):
    " D1 h5 R% I' p- ~5 M* J% F
    . j6 d  y/ c0 {- `; Q! k6 Z仅从这两个公式我们就可以看出,仅仅是按照这两个公式进行计算,就需要进行大量的循环与判断,而且还涉及到排序的问题,就涉及到排序算法的选择与使用,这里我选快排,从网上copy了一段快排,直接用。总之实现起来很麻烦,在大数据情况下,更何谈效率。2 q" U( s7 E: n: J: b( u1 Z' A8 [

    " C' _+ w9 r/ M/ p! P- `; d7 A首先建表:
      G; o. |3 T9 L9 E
    $ W. C1 ^" g( f* lDROP TABLE IF EXISTS `tb_xttj`;
    . b; g" F+ ~" x0 O% h8 V/ U1 q! gCREATE TABLE `tb_xttj` (
    3 C" Z! u3 ]' U: ^3 n" Y  `name` varchar(255) NOT NULL,& D7 p% d- {+ Y* ~
      `a` int(255) default NULL,
    : N6 c6 f6 n) R) i2 [  `b` int(255) default NULL,3 n- B; w+ S, J. |$ t- t
      `c` int(255) default NULL,+ Z6 M1 }  `% E' t% Y) |1 \" q$ _/ P
      `d` int(255) default NULL,
    , L  ?/ N2 V- b8 l, G  `e` int(255) default NULL,
    $ P: q8 X3 s+ i, e  `f` int(255) default NULL,) |# P9 p; P% f
      `g` int(255) default NULL,
    7 _5 }" t8 Q+ I- \  `h` int(255) default NULL,
    ' ^% p' ^/ H/ G8 F; B" Q2 B8 h7 n  PRIMARY KEY  (`name`)
      X0 O: A8 ?7 ]+ \4 b) ENGINE=MyISAM DEFAULT CHARSET=latin1;. W% r( S5 W1 m( A8 Z

    " K6 q# Y: r! X, V3 J' U' ^INSERT INTO `tb_xttj` VALUES ('John', '4', '4', '5', '4', '3', '2', '1', null);
    8 x/ r% _0 q" MINSERT INTO `tb_xttj` VALUES ('Mary', '3', '4', '4', '2', '5', '4', '3', null);
    % N8 a0 H$ M( I) r# L$ H% pINSERT INTO `tb_xttj` VALUES ('Lucy', '2', '3', null, '3', null, '3', '4', '5');1 P, c: V+ b- `0 k4 q
    INSERT INTO `tb_xttj` VALUES ('Tom', '3', '4', '5', null, '1', '3', '5', '4');1 ]2 H% X, `# I
    INSERT INTO `tb_xttj` VALUES ('Bill', '3', '2', '1', '5', '3', '2', '1', '1');
    . d) v. W3 Y2 eINSERT INTO `tb_xttj` VALUES ('Leo', '3', '4', '5', '2', '4', null, null, null);
    6 V. i& i9 a% @: `9 X, u& \$ T) l, u5 l3 i7 F% O3 V; s# r: F
    8 G: b4 f( x# d3 @( Y: Q
    我这里只对最后一行的Leo进行推荐,看看f,g,h哪个可以推荐给他。
    - ^9 I. ~+ |* O# q( N
    " ~) W. P* h/ c6 a4 G    用php+mysql,流程图如下:2 U2 K' F& e7 l6 H+ J( I3 r

    * d! p# n. j7 t! ~连接数据库并将其存储为二维数组的代码如下:
    ; k9 r0 r' w3 N
    : h! l3 D3 N- ~: X4 g  Lheader("Content-Type:text/html;charset=utf-8");
    . M- F& c+ ]! T; W4 A# z  q, n0 p
    : n9 a& k8 t# U3 s5 n+ Tmysql_connect("localhost","root","admin");
    : G$ Z, r: O  _9 y" omysql_select_db("geodatabase");
    : P! U/ ~4 N) n, t! Omysql_query("set names 'utf8'");        8 D: v! N. s% }6 v2 T8 a8 f# ~. d

    1 F8 k0 C% `- l: }  [$sql = "SELECT * FROM tb_xttj";7 L: g" V& ^; t: ?5 G
    $result = mysql_query($sql);
    % ]0 @8 O# _% T
    : X  s) V  v9 f$ ~$array = array();% X$ b; Y$ q3 R0 O: V8 N3 {& h
    while($row=mysql_fetch_array($result))
    ( \" I+ H1 K7 S: ^{7 P' \4 b$ h/ @4 T
            $array[]=$row;//$array[][]是一个二维数组' S9 y& h( I& L
    }
    7 g' q6 y& k* w1 {4 u6 u
    ; I) l( I2 u0 G; q; }! O' b7 \问题1:这一步完全可以看做是整表查询,这种查询是大忌,对于这种小小的演示系统还可以,但是对大数据的系统,没有效率,至于如何改进,还得多学习才是。
    2 Q7 j4 U5 O" V( ^9 A% H+ x+ s( ~2 B7 G8 j* x7 f
    求Leo与其他人的Cos值代码如下:' Q- x- q8 h! u

    2 D' j: W9 s& ?5 y/*4 ~: ~# ?' m) s+ W7 l
    * 以下示例只求Leo的推荐,如此给变量命名我也是醉了;初次理解算法,先不考虑效率和逻辑的问题,主要把过程做出来) Q  n+ I4 U* w  M. B
    */
    $ ?$ z+ E- w, S. D$ f
    , ~' Q+ y" S* ~) s$cos = array();
    # n; }: \( `7 ^  Z, S2 P8 `+ z" i8 z$ U% P$cos[0] = 0;
    9 {/ R% r/ m6 R" }$fm1 = 0;% L4 k% c. B' I; e- k! J7 ]5 x
    //开始计算cos- c* f4 V* _5 e" s; ?
    //计算分母1,分母1是第一个公式里面 “*”号左边的内容,分母二是右边的内容
    ! @' W# M* N/ c3 ]9 vfor($i=1;$i<9;$i++){+ W$ o7 j% g  ^! B
            if($array[5][$i] != null){//$array[5]代表Leo
    6 Y; W3 T0 F" `# R! Y  Y5 p                $fm1 += $array[5][$i] * $array[5][$i];
    ) w3 F6 m& m% w2 }0 }  B* f4 d        }/ E4 m9 k& H2 z3 I
    }
    / l1 @" u5 E8 Y# s6 g  Q: q. A
    3 X9 z- `8 C9 g# f( ?; p6 n$fm1 = sqrt($fm1);/ g! S7 D. ]& M

    , {: R0 k5 M8 @5 Ffor($i=0;$i<5;$i++){
    ' L6 _8 U2 B5 D6 w# [  o        $fz = 0;
    ; D$ T- R! k2 u$ h# L% R        $fm2 = 0;8 v/ h7 V$ G4 c, G# y5 u+ F) M
            echo "Cos(".$array[5][0].",".$array[$i][0].")=";8 z+ e8 G7 L9 |. a7 W3 k% Y; ?
           
    # k) S% n1 f( G5 F/ a        for($j=1;$j<9;$j++){9 K/ C8 z5 u. I5 R6 u4 ^5 O- X- O
                //计算分子
    0 X' L$ d9 L( q  r, ?                if($array[5][$j] != null && $array[$i][$j] != null){
    $ F% z0 @) q$ ~                        $fz += $array[5][$j] * $array[$i][$j];- P8 M0 O! `. v( [6 o1 `  M4 w+ k+ h
                    }
    6 a0 ?4 [) |3 ^/ P8 y  o                //计算分母2
    / h0 O: m* S' v) s+ c. s                if($array[$i][$j] != null){
    $ z" r2 _$ D8 {$ L- `( c0 _) {/ K: Y                        $fm2 += $array[$i][$j] * $array[$i][$j];
    2 b4 H9 `( n1 F0 R1 D! m                }                       
      _7 F# Q8 v, S0 G) q8 z% P/ o        }# {* I8 k& y, ^& F1 d
            $fm2 = sqrt($fm2);4 X. G6 u. p9 A" e
            $cos[$i] = $fz/$fm1/$fm2;2 I5 d) T3 ~3 ~8 c% K& o  M: P
            echo $cos[$i]."<br/>";+ d3 A) ~: y, k3 g8 z3 D0 |0 g& t
    }& b3 n2 K: f( I# _- O# g

    7 x9 y* I5 ]/ Z  @这一步得到的结果是酱紫:
    0 `+ G. d: T' h9 W2 |* F0 N& R5 w/ H7 i: |
    将求好的Cos值排序,采用快排代码如下(百度copy而来):+ u4 m" T+ Q9 ?& a- l
    ) i4 M% x! j( L/ E1 V* W+ \) y$ U  `# y

    , B& T; J( {3 V+ i$ ]//对计算结果进行排序,凑合用快排吧先& ?4 f( J2 D$ [/ e3 U! v
    function quicksort($str){
    1 C# z& n6 K* D        if(count($str)<=1) return $str;//如果个数不大于一,直接返回
    1 w' Y* Q2 @) {. |        $key=$str[0];//取一个值,稍后用来比较;5 s: G# b6 ~7 W( x+ y
            $left_arr=array();
    : x0 ?* k6 g# Y/ K- U1 [: ~        $right_arr=array();
    - h6 s/ n- N8 U" N3 P$ g       
    $ V* Q* u* b  C; b        for($i=1;$i<count($str);$i++){//比$key大的放在右边,小的放在左边;3 c  z% J- I- s" ^4 s
                    if($str[$i]>=$key)6 n2 K, z1 W  z
                    $left_arr[]=$str[$i];
    4 ?& O4 y: U9 P1 D! L! Y                else" a! Q6 j, j1 I: u% L1 t7 o0 J
                    $right_arr[]=$str[$i];
    ) r1 i4 _0 A! _* ]0 G        }+ I' }8 g; ?( I6 r' x# ~
            $left_arr=quicksort($left_arr);//进行递归;2 Y  W! v" A% I- q+ u
            $right_arr=quicksort($right_arr);6 q  s# @6 D) o* X: v/ I
            return array_merge($left_arr,array($key),$right_arr);//将左中右的值合并成一个数组;
    0 _( C! F$ w/ \& u}
    0 e% S7 r$ m  |5 P5 d7 G
    9 [6 u1 E) X4 X- a  S7 V5 {4 u1 _$neighbour = array();//$neighbour只是对cos值进行排序并存储
    9 T- ^/ M/ s: d0 t6 p/ j. C2 m0 {$neighbour = quicksort($cos);8 v' k# C! H$ i; Q5 J, f

    " v: t# |2 ?% o
    + D# r- _9 ], _4 b( n/ K) [( V这里的$neighbour数组仅仅存储了从大到小排序好的Cos值,并没有与人联系起来。这个问题还要解决。8 N( `' s2 ]. T0 l  d- f8 A4 i
    3 U2 Y6 U, f5 @
    选出Cos值最高的3个人,作为Leo的邻居:7 \* e7 G1 B: j2 J$ N
    - N" R2 O$ N( }* w/ d: i5 j
    //$neighbour_set 存储最近邻的人和cos值* `7 L7 P3 _8 ]! D' ]3 Z9 N8 \
    $neighbour_set = array();% K1 X% i+ `! \: Q( h/ C
    for($i=0;$i<3;$i++){) c" h! n2 f/ I4 V
            for($j=0;$j<5;$j++){# z9 R+ Z3 I- A; [2 w
                    if($neighbour[$i] == $cos[$j]){9 f& L  j/ Z' D, s/ s. F9 K
                            $neighbour_set[$i][0] = $j;4 O; V8 \" F: H8 c+ {7 e
                            $neighbour_set[$i][1] = $cos[$j];
    ; ~7 g) i1 T# N" q! Y7 L                        $neighbour_set[$i][2] = $array[$j][6];//邻居对f的评分" Y, h/ H- Z& R/ j5 O" O
                            $neighbour_set[$i][3] = $array[$j][7];//邻居对g的评分! l0 g% X. R, `& `6 S+ a
                            $neighbour_set[$i][4] = $array[$j][8];//邻居对h的评分
    2 U5 B" |, Q$ `; H+ D. L                }7 W9 k; w, H+ h" P2 Z
            }$ F& a0 P0 c% m  G, @3 i
    }
    " ]1 @( W+ \! U  @3 `$ a. Tprint_r($neighbour_set);# P) O" d$ J! Z: t3 `" }, J, M
    echo "<p><br/>";5 S* l9 x4 U" @4 A2 B0 W8 e+ I8 F4 J) u

    ; X* j0 a" V$ t& X0 D! ?这一步得到的结果是酱紫:  D3 d0 e0 G- \, o: V  F6 X

      {- U# ~! `8 v" u
    $ S# @- E! r* X' S9 P: t+ m, g. i% S5 t
    转存失败重新上传取消' ]8 y. [! P7 S, L, d
    / q* }0 C4 y6 o
    这是一个二维数组,数组第一层的下标为0,1,2,代表3个人。第二层下标0代表邻居在数据表中的顺序,比如Jhon是表中的第0个人;下标1代表Leo和邻居的Cos值;下标2,3,4分别代表邻居对f,g,h的评分。
    4 ?0 x0 e7 V1 }% ]  G) n; P# }( N
    开始进行预测,计算Predict代码如下:' |7 r/ N8 Z: i4 `* o

    " p4 i: E( N. m5 \. O我是分别计算Leo对f,g,h的预测值。在此有一个问题,就是如果有的邻居对f,g,h的评分为空,那么该如何处理。比如Jhon和Mary对h的评分就为空。本能的想到用if判断一下,如果为空则跳过这组计算,不过这样处理是否合理,有待考虑。以下代码并没有写出这个if判断。+ a  G8 `" K( R: ]

    + w% l- {# V- m: X" H3 K//计算Leo对f的评分
    3 D7 W/ d" V+ J' z$p_arr = array();1 p! p) t/ [- P6 F# Z, b
    $pfz_f = 0;1 }+ _) A2 A$ O% l' F( N
    $pfm_f = 0;5 p- \8 u' k: b9 D3 e6 z8 p$ X
    for($i=0;$i<3;$i++){  Q1 [. X7 [4 ^& i; \+ I9 c
            $pfz_f += $neighbour_set[$i][1] * $neighbour_set[$i][2];- @. {- y3 u  s/ U! `% q- L
            $pfm_f += $neighbour_set[$i][1];
    ) H9 |0 |. G( Y}+ i7 L: S; m. n# J: T8 M' Z  }. y7 c
    $p_arr[0][0] = 6;$ X0 S0 z0 u+ a8 W% y2 D. [
    $p_arr[0][1] = $pfz_f/sqrt($pfm_f);
    2 d' i, F+ c3 X1 J: Z/ w/ z( [$ a6 Tif($p_arr[0][1]>3){4 \% |1 R8 M* @) H1 Z
            echo "推荐f";
    ' C0 P- v" \+ @7 F% i}
    " ^6 I- M$ E9 N" Y0 V
    ) s& [$ b) S8 |  ]* Z) I8 I8 I//计算Leo对g的评分8 i9 {- w1 w. ^! w9 u5 Z0 W8 z7 F
    $pfz_g = 0;9 V/ R0 Q- f* V
    $pfm_g = 0;* i) w& W/ v! X- D4 L; |: @
    for($i=0;$i<3;$i++){
    % O! \1 e' b  @: B. E        $pfz_g += $neighbour_set[$i][1] * $neighbour_set[$i][3];/ w$ C" A7 j6 f3 \. _* b
            $pfm_g += $neighbour_set[$i][1];
    & \+ R& t& g9 E( s( `. X# _        $p_arr[1][0] = 7;# s+ [% H! e4 P  [7 ]! ?
            $p_arr[1][1] = $pfz_g/sqrt($pfm_g);
    % k& N% x& B5 y% G' H+ |( C}
    & V2 o. ]2 L' n; N( N& ]8 I$ pif($p_arr[0][1]>3){: H! e3 }  _2 F; l
            echo "推荐g";& C) V: M3 S2 H
    }
    ( \" B8 Q+ J! u2 m1 d+ X
    8 N# w- \% u  g3 W4 F, M; e//计算Leo对h的评分
    " D! B, ~6 U( ]& C$ X$pfz_h = 0;% P% k3 Q* T8 {/ F3 ?0 m( K
    $pfm_h = 0;2 x1 O6 y, {) K' T) t: E
    for($i=0;$i<3;$i++){
    * \" S8 S8 j6 v- I        $pfz_h += $neighbour_set[$i][1] * $neighbour_set[$i][4];
    ) b' v5 G! s/ X7 L; M        $pfm_h += $neighbour_set[$i][1];
    # {7 `' {; D0 g4 O2 e* k        $p_arr[2][0] = 8;- e) f0 P+ C4 R& e
            $p_arr[2][1] = $pfz_h/sqrt($pfm_h);
    1 F, x2 k; t3 E  A. H# z: _}5 A7 e0 H9 @6 R# ^% g# Q  X
    print_r($p_arr);
    $ e7 \" R6 l8 g, aif($p_arr[0][1]>3){. I0 W" C" F* r( q8 Q
            echo "推荐h";' f' Q) \7 V! R5 [9 ^
    }
    7 Z+ H* F% u4 T& g; a
    $ P3 s$ a, \5 E, h$p_arr是对Leo的推荐数组,其内容类似如下;  e0 I5 p6 Q' ?8 N
    , I; P9 D$ [* r3 z. m" n( z/ r
    Array ( [0] => Array ( [0] => 6 [1] => 4.2314002228795 ) [1] => Array ( [0] => 7 [1] => 2.6511380196197 ) [2] => Array ( [0] => 8 [1] => 0.45287424581774 ) ), p! @0 w! ?) M; g: B+ s

    0 A) C+ F+ h! ^$ f" m  k0 Q2 ff是第6列,Predict值是4.23,g是第七列,Predict值是2.65........6 W8 S' A5 n7 ]' r3 n

    9 L, i7 f+ G9 }求完了f,g,h的Predict值后有两种处理方式:一种是将Predict值大于3的物品推荐给Leo,另一种是将Predict值从大到小排序,将Predict值大的前2个物品推荐给Leo。这段代码没有写。
    8 ?0 `7 U4 g! w  b/ `/ u! T( N9 O$ |  y9 ~  q5 v  R( g, o
    从上面的示例中可以看出,推荐算法的实现非常麻烦,需要循环,判断,合并数组等等。如果处理不当,反而会成为系统的累赘。在实际处理中还有以下问题:
    ( H, F: T7 c* O7 r2 h+ i
    4 A- |! p- ~$ s3 ~( x& n. F1.以上示例我们只对Leo进行推荐,而且我们已经知道Leo没有评价过f,g,h物品。如果放到实际的系统里,对于每一个需要进行推荐的用户,都要查询出他没有评价过哪些物品,这又是一部分开销。
    2 _' S+ U8 k1 p7 L1 |- f
    . o$ N' t. x/ e. a' o8 b2.不应当进行整表查询,在实际系统中可以设定一些标准值。比如:我们求Leo与表中的其他人的Cos值,如果该值大于0.80,则表示可以为邻居。这样,当我找到10个邻居之后,就停止求Cos值,避免整表查询。对于推荐物品也可以适当采用此方法,比如,我只推荐10个物品,推荐完后就停止求Predict值。
    ) U; R0 v' ^' l6 X; n  n( l
    ! r! U6 ~" F+ t' g3.随着系统的使用,物品也会发生变化,今天是fgh,明天没准就是xyz了,当物品变化时,需要动态的改变数据表。; ?: d8 P6 P# C( A' @7 g& P) b" d

    2 B2 }9 t5 M0 w& r/ o3 h, b3 o3 e  L4.可以适当引进基于内容的推荐,来完善推荐算法。
    ! [' z& D1 `9 A& [1 V
    ; C$ g& v: h( M, a4 _5.推荐的精确性问题,这个设置不同的标准值,会影响精确性。
    5 D/ N5 x+ w* @* \) e————————————————- i' {- E' O, J8 y
    版权声明:本文为CSDN博主「星斗其文,赤子其人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。7 _" T- Q2 l/ y
    原文链接:https://blog.csdn.net/liuliuhelingdao/article/details/126715465
    : b3 P  ^9 ^2 g3 o6 r4 M( _8 `
    9 J% \* z. H! ]3 X0 m
    7 Q0 ~8 `" w+ u- Z
    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-13 10:14 , Processed in 0.435744 second(s), 51 queries .

    回顶部