QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1944|回复: 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
    3 a! Q* M; X8 m7 R; {
    php+mysql实现简单的协同过滤推荐算法3 e$ F: V6 S* U3 `3 H9 f
    仅做标记。。。0 b3 D. M& u3 w4 p% q
    9 w; [, y2 p2 M, G

    : G% c2 G2 u% F, p& W9 E3 C; P. Q5 i9 ?+ e* Z5 F) h7 B/ [8 A3 j
    要实现协同过滤推荐算法,首先就要理解算法的核心思想和流程。该算法的核心思想可以概括为:若a,b喜欢同一系列的物品(暂时称b是a的邻居吧),则a很可能喜欢b喜欢的其他物品。算法的实现流程可以简单概括为:1.确定a有哪些邻居 2.通过邻居来预测a可能会喜欢哪种物品  3.将a可能喜欢的物品推荐给a。* _- d/ A1 A+ l+ b  h
    " Z* Y* V- u7 {' V
    算法核心的公式如下:# n; f0 Z+ v9 Q
    4 U* w, d+ S# U8 l; Q8 `
    1.余弦相似度(求邻居):1 X; C- u$ `( S
    2 L  D; @/ ?/ i* L+ _# h
    2.预测公式(预测a可能会喜欢哪种物品):
    / h- E2 I) b4 q6 H/ w+ F; s5 d! v
    4 {% i# C  D# v( ]6 K: f# R+ @仅从这两个公式我们就可以看出,仅仅是按照这两个公式进行计算,就需要进行大量的循环与判断,而且还涉及到排序的问题,就涉及到排序算法的选择与使用,这里我选快排,从网上copy了一段快排,直接用。总之实现起来很麻烦,在大数据情况下,更何谈效率。
    9 ?$ X+ h9 J; K; w" c' M. G# U
    & e5 `1 y) s* m+ A3 f首先建表:
    ' ?& x8 W* R3 S. N0 q2 [! b( G
    ( h3 y, U) x3 k2 rDROP TABLE IF EXISTS `tb_xttj`;
    2 _" |, k  X3 a- X) g# |  ZCREATE TABLE `tb_xttj` (' V5 H* @3 G0 i5 V! C' A
      `name` varchar(255) NOT NULL,+ E- g; H  P* }1 M' I
      `a` int(255) default NULL,
    0 t' H; ~0 X4 Q% G. i7 ?  `b` int(255) default NULL,; j! t. N( }2 H# s3 l
      `c` int(255) default NULL,1 s. L, w9 |. v* h* i2 U3 e' x; O
      `d` int(255) default NULL,
    7 L  x- l0 U$ @. H  `e` int(255) default NULL,
    5 F: t2 j$ `7 t9 i  `f` int(255) default NULL,$ N& S6 J' T9 x" I9 \# d
      `g` int(255) default NULL,
    * B8 W. i+ _3 i. Y6 i9 W  `h` int(255) default NULL,, f4 z) K9 ~  q. h' n
      PRIMARY KEY  (`name`)& ~, ^8 l- a) g/ g# u
    ) ENGINE=MyISAM DEFAULT CHARSET=latin1;/ y! s  R- K9 W$ P

    $ m) e- U, R6 H+ B8 LINSERT INTO `tb_xttj` VALUES ('John', '4', '4', '5', '4', '3', '2', '1', null);
    5 p' g: f: g" [2 ?! n3 _& oINSERT INTO `tb_xttj` VALUES ('Mary', '3', '4', '4', '2', '5', '4', '3', null);
    6 V% W  F! j; t- \, vINSERT INTO `tb_xttj` VALUES ('Lucy', '2', '3', null, '3', null, '3', '4', '5');- L5 W1 t- o( j, d; Y8 f/ }* X
    INSERT INTO `tb_xttj` VALUES ('Tom', '3', '4', '5', null, '1', '3', '5', '4');
    6 P, r, j/ o0 P+ q2 kINSERT INTO `tb_xttj` VALUES ('Bill', '3', '2', '1', '5', '3', '2', '1', '1');8 W+ G8 c' F& [0 A. E) j! D- L, v0 V
    INSERT INTO `tb_xttj` VALUES ('Leo', '3', '4', '5', '2', '4', null, null, null);
    " U7 C8 w' k% r/ K9 M& M- j7 H5 O( n
    8 _! w* b* q0 O
    我这里只对最后一行的Leo进行推荐,看看f,g,h哪个可以推荐给他。/ D7 V( n* _+ j5 L- A

    & {( Z2 `( @& F, G7 {$ f    用php+mysql,流程图如下:$ F3 }1 W+ N/ d8 S/ t+ ~
    : h1 m' o$ R9 l
    连接数据库并将其存储为二维数组的代码如下:
    1 n. \3 I$ r8 m8 c! s8 n' [  ]
    2 W' `1 ^$ G4 U9 D# w3 Bheader("Content-Type:text/html;charset=utf-8");
      M6 s- \4 q7 I! _' a+ o6 `& t" A3 K( N3 I
    mysql_connect("localhost","root","admin");
    1 O& q" ?- K' jmysql_select_db("geodatabase");
    " r+ ^0 B( @5 X+ N  C- E1 L& ]4 tmysql_query("set names 'utf8'");       
    / Z# [  @' ^' m4 x1 D5 q/ u' U7 d2 _5 K& x8 [: z
    $sql = "SELECT * FROM tb_xttj";8 T/ D0 f' X6 f8 P- E9 m0 Q
    $result = mysql_query($sql);8 b, \. Y2 E9 V

    . j' l( s# ]6 [2 H: _$array = array();
    5 v0 U' |( |) U6 C: [while($row=mysql_fetch_array($result))
    ) b0 L. |3 S0 O/ _9 h, j4 L{
    7 Y# b6 J: p$ x1 P- A) j/ I1 K4 }        $array[]=$row;//$array[][]是一个二维数组
    % t8 \! w- D9 {: ^: }  E$ x} ! u1 r5 I3 y# o) K0 J% }

    ; x+ `/ F3 H0 P2 q问题1:这一步完全可以看做是整表查询,这种查询是大忌,对于这种小小的演示系统还可以,但是对大数据的系统,没有效率,至于如何改进,还得多学习才是。8 S2 d! N: l6 r: |

    , ?* [# V9 A! d5 K2 p  G) `" y/ }# T求Leo与其他人的Cos值代码如下:4 J! }: v$ a2 v6 a2 u  y
    ' J7 f) n$ |3 X( X$ @0 ]
    /*
    . l, h- D5 x5 f: p * 以下示例只求Leo的推荐,如此给变量命名我也是醉了;初次理解算法,先不考虑效率和逻辑的问题,主要把过程做出来
    ' L/ y) x, H0 o$ X" W5 F/ R( P+ ^+ `# o */! V& ]1 k0 Q& Y; t

    ! E. ^# f5 j* Y: z2 a2 k' S$cos = array();( g( l9 C- |9 I6 i: E, y8 ?' ^" w
    $cos[0] = 0;( t# i/ k. M$ T( T$ f
    $fm1 = 0;2 D. E+ m7 \* S: F
    //开始计算cos( k/ d; k$ n! a+ b, F
    //计算分母1,分母1是第一个公式里面 “*”号左边的内容,分母二是右边的内容2 D1 E" J3 b5 X
    for($i=1;$i<9;$i++){# B1 ]- |9 k, T7 q: t& j
            if($array[5][$i] != null){//$array[5]代表Leo
    $ ~. g: V% r6 t' A8 f/ A; a" F                $fm1 += $array[5][$i] * $array[5][$i];
    6 o0 J: P' g+ n5 D, o! R$ p0 I        }
    * p: f& }% v* A0 q1 M( E( r}& W& x4 Y% ]% L* P) N
    . \7 N+ [5 v0 [6 d9 A& j$ L
    $fm1 = sqrt($fm1);
    - S2 R9 T/ ~. l) w1 n# ^" V0 \' ]" Z- W
    for($i=0;$i<5;$i++){
    - @& y- N; s3 `: e        $fz = 0;
    1 p- \4 \( X" Q$ q        $fm2 = 0;9 p, T( ~9 C' W+ {
            echo "Cos(".$array[5][0].",".$array[$i][0].")=";  Q- |/ i, ?! F7 k
           
      M' P6 X  v% S; R        for($j=1;$j<9;$j++){  M/ f* q! h) L' K3 p
                //计算分子+ @7 Z: q2 q- B+ \
                    if($array[5][$j] != null && $array[$i][$j] != null){
    % a/ \  e6 L; a9 s9 b7 A                        $fz += $array[5][$j] * $array[$i][$j];
    $ j! I8 t6 }% U& c& G$ Y                }
    ) P  w1 P7 g/ E1 D6 P                //计算分母2- e, t  d, Z6 J7 g- C2 C  q( p! f+ M/ v
                    if($array[$i][$j] != null){
    0 C& c' t2 v% r* e, G% @1 `: F; c                        $fm2 += $array[$i][$j] * $array[$i][$j];
    2 s  e+ ]; L/ i# W7 y2 C' X                }                        : Y! J+ c; V! I+ z
            }
    3 y  @& x' h7 ]        $fm2 = sqrt($fm2);
    , z* e" _% v+ a! `- c8 I        $cos[$i] = $fz/$fm1/$fm2;
    ' R5 g8 i" z9 b7 s( m; ~; z        echo $cos[$i]."<br/>";
    : i% x: |! D9 }) Y8 C}- H2 N* ~8 M( N- b

    7 U2 \3 z6 D& I8 x/ l这一步得到的结果是酱紫:
    : G; `$ ^& U7 F% |+ i2 u% d, }  o5 g: P* B1 |7 Y
    将求好的Cos值排序,采用快排代码如下(百度copy而来):- \1 [' a% x9 |! ~; A# ~
    " d  X) C2 _2 C9 W" y
    $ E- R5 s/ G5 ~6 {# V5 L
    //对计算结果进行排序,凑合用快排吧先
    % l1 Z  }8 L$ Y) z9 ufunction quicksort($str){; I5 D, n$ k4 E2 Z/ J( _
            if(count($str)<=1) return $str;//如果个数不大于一,直接返回
    7 S' U4 I* c1 B; C0 q6 U) P        $key=$str[0];//取一个值,稍后用来比较;
      B) O  H6 K% ]1 D7 o* t        $left_arr=array();
    * r1 Z) P5 m0 @$ [( b: H        $right_arr=array();
    ! ^( I( G* X$ v7 M1 k+ T        $ r- q: H! }2 V: v6 X$ y3 S# z: p
            for($i=1;$i<count($str);$i++){//比$key大的放在右边,小的放在左边;* j: f, F+ k* B$ @6 b! F5 J
                    if($str[$i]>=$key)
    * ?: S5 R7 q! ^! s# p& O' \8 F                $left_arr[]=$str[$i];
    ; w# K8 I6 t( C$ a; ~7 w                else
    ( ]/ s! Y, w7 O, z6 Q4 I                $right_arr[]=$str[$i];
    ' K! M% y4 `  O% v9 P        }1 T1 [" `' Q! C  v6 F0 j
            $left_arr=quicksort($left_arr);//进行递归;
    + Q& c0 O: @' L( _. L        $right_arr=quicksort($right_arr);9 b/ I4 ~" A  G  ]2 {. [5 t
            return array_merge($left_arr,array($key),$right_arr);//将左中右的值合并成一个数组;
    , j/ T) ]8 n* T: J! O5 u' m2 E8 N}
    $ |8 P5 l7 X0 k# Z$ u, P$ b
    9 C) D* p9 {5 |5 j$neighbour = array();//$neighbour只是对cos值进行排序并存储8 i, a9 f' e0 l; p7 G4 g$ m2 o
    $neighbour = quicksort($cos);8 v+ H( {) i6 u2 }0 O" ]) P8 s$ ~

      ]- w1 }8 E) x7 T2 \) }: ?4 O- a8 A* H4 |# [
    这里的$neighbour数组仅仅存储了从大到小排序好的Cos值,并没有与人联系起来。这个问题还要解决。
    ; x& r. [& v. q; n6 s# T4 x& ^  n
    6 K+ t& y0 K# ~4 Y8 r选出Cos值最高的3个人,作为Leo的邻居:
    " {" |$ y4 ~( d* ^& }. c4 Q1 ^2 {) Z. c
    / q* I' b+ O8 Q# D9 @# j//$neighbour_set 存储最近邻的人和cos值
    7 q$ }$ ~0 |6 A( B5 ?( z  F) C$neighbour_set = array();& F0 q  S, z" p  y
    for($i=0;$i<3;$i++){& f7 c. q" Y, h; y5 g; s8 `, j
            for($j=0;$j<5;$j++){
    0 O: l1 n( O1 p; C- f                if($neighbour[$i] == $cos[$j]){
    1 L! u- ?( c; P9 a* j4 c                        $neighbour_set[$i][0] = $j;
      ^3 R' S, V; c$ r) {9 _" R. i* y                        $neighbour_set[$i][1] = $cos[$j];
    9 B- A: E3 d; B( ^5 V/ j                        $neighbour_set[$i][2] = $array[$j][6];//邻居对f的评分
    * r3 H* j. D/ e2 E( e2 ^' l8 G                        $neighbour_set[$i][3] = $array[$j][7];//邻居对g的评分
    0 c4 [2 G2 G  T" v  a. n' s                        $neighbour_set[$i][4] = $array[$j][8];//邻居对h的评分
    ) h4 i/ V0 Y  }3 \) [                }
    8 c  h7 [% d9 o& t% G        }
    0 y( e' X9 v: y}
    8 n. f. X  v0 D5 s/ S3 E& W; Fprint_r($neighbour_set);
    7 a5 `4 Q% C) \: o0 q- k8 n% T3 f! Qecho "<p><br/>";& b" r8 Z$ f- ~4 J7 R

    . ]6 }6 t# t. w1 ?0 S这一步得到的结果是酱紫:; X& C! O3 |5 K6 X$ A+ j" c( y8 J
    2 D, R- \( _3 G1 i9 T/ K& g

    # @, I! K2 |$ l$ V' n+ {. Y% F! ?: |2 h! q$ e2 h7 B0 t/ n
    转存失败重新上传取消8 W$ o( D2 x  N& U+ ^

      c* d, J6 `0 j* v. o9 k6 S& ?8 T这是一个二维数组,数组第一层的下标为0,1,2,代表3个人。第二层下标0代表邻居在数据表中的顺序,比如Jhon是表中的第0个人;下标1代表Leo和邻居的Cos值;下标2,3,4分别代表邻居对f,g,h的评分。4 s% }# T8 n/ P: y# V

    ( `$ x4 m; q6 t开始进行预测,计算Predict代码如下:
    , ~0 G& c+ L; L. |4 r  N! {( M  }( I/ a; Q5 c1 z: J4 k4 e
    我是分别计算Leo对f,g,h的预测值。在此有一个问题,就是如果有的邻居对f,g,h的评分为空,那么该如何处理。比如Jhon和Mary对h的评分就为空。本能的想到用if判断一下,如果为空则跳过这组计算,不过这样处理是否合理,有待考虑。以下代码并没有写出这个if判断。
    0 \  S) ^, s4 p+ C; m9 K
    8 _, [& n( g* z- e//计算Leo对f的评分
      k9 ~$ Q9 }+ V  L' C. d# ]% T$p_arr = array();9 C8 G5 {7 \8 [- |
    $pfz_f = 0;1 o! t! n/ `  ^$ O0 ~; k$ z
    $pfm_f = 0;/ N$ b7 v  X' c& \0 z% z2 i
    for($i=0;$i<3;$i++){: a; n! t0 J$ q
            $pfz_f += $neighbour_set[$i][1] * $neighbour_set[$i][2];' Q6 o5 x9 Y8 t- S0 v! D
            $pfm_f += $neighbour_set[$i][1];& w" e% h. i3 E( L1 G  t, E
    }) S+ c- `& V8 k( q1 V! U
    $p_arr[0][0] = 6;% n& }% b. m  z4 V% q: I# S
    $p_arr[0][1] = $pfz_f/sqrt($pfm_f);% J0 ^0 r' _8 h0 t! Z' i% i0 T. T5 p  B
    if($p_arr[0][1]>3){
    7 f0 I0 }  M. E' h5 k6 Y' G2 ?        echo "推荐f";6 g& T9 Y' P& X+ e3 R; o
    }
    ! x# n% ^% j! {+ {% {; V9 j( N" \2 m5 F2 ~3 a  h% h
    //计算Leo对g的评分
    , C" b/ m* l7 |8 j$pfz_g = 0;! h$ |, S; _3 l8 K* S6 \, J
    $pfm_g = 0;: a( k  \& k6 A
    for($i=0;$i<3;$i++){, f! Z- a8 w( O5 U$ \* u* C
            $pfz_g += $neighbour_set[$i][1] * $neighbour_set[$i][3];1 F# I# {/ e/ O7 w4 N2 s
            $pfm_g += $neighbour_set[$i][1];
    + ^7 B3 k& @7 p$ V        $p_arr[1][0] = 7;8 k5 e5 J7 x6 s2 G9 n  D
            $p_arr[1][1] = $pfz_g/sqrt($pfm_g);
    0 p- ?  J+ p& L, p' m}
    # x# B  J" \, N0 C4 K1 ?0 b7 U3 r, Lif($p_arr[0][1]>3){+ }5 K0 c$ _/ _6 n7 c
            echo "推荐g";
    : G  ^; a1 l. T8 o: D& x% [& N}. h5 D1 `  t( T/ ?0 D: z" _- [* D
    . M7 T5 _; {6 M2 T6 S* p
    //计算Leo对h的评分
    0 M) |3 ~1 _* Q+ Y$ p. r$ w# ]* D$pfz_h = 0;
    3 O/ R+ |, t* ~$ @( u, I4 [$pfm_h = 0;$ }) d* o+ G0 a
    for($i=0;$i<3;$i++){' p" y8 Y$ ?) A# f, J- `
            $pfz_h += $neighbour_set[$i][1] * $neighbour_set[$i][4];
    2 }/ V3 [1 Y( Y$ q1 f        $pfm_h += $neighbour_set[$i][1];
    - g* t- V1 y* |# Z        $p_arr[2][0] = 8;
    ( x/ y, N) c8 c6 J$ q$ N6 O        $p_arr[2][1] = $pfz_h/sqrt($pfm_h);: W8 l" n& L+ W$ r
    }
    ( }3 P0 P5 F+ N% T( ^print_r($p_arr);
    2 q! Y7 I6 l- }; T' w, ]# wif($p_arr[0][1]>3){' ^, a$ U7 B2 r2 M: n
            echo "推荐h";" m) ~# O5 B: J' O
    }0 C4 P0 e3 G. v1 U
    ! U+ J1 I2 W3 u/ e0 F( d
    $p_arr是对Leo的推荐数组,其内容类似如下;4 E/ I; p; n( ?# r! g# B6 q- B; G% ~
    " ~) e% B1 _' s+ S+ H3 N; y
    Array ( [0] => Array ( [0] => 6 [1] => 4.2314002228795 ) [1] => Array ( [0] => 7 [1] => 2.6511380196197 ) [2] => Array ( [0] => 8 [1] => 0.45287424581774 ) )
    - @- I1 U1 L/ [0 Z; I0 f3 ?5 P5 d' m6 }9 \/ z4 N; j) f- U$ T5 C
    f是第6列,Predict值是4.23,g是第七列,Predict值是2.65........
    ( w$ U$ M- }' r/ \/ F& Y( q: o' N: o, M# J: s% J) |3 H! b
    求完了f,g,h的Predict值后有两种处理方式:一种是将Predict值大于3的物品推荐给Leo,另一种是将Predict值从大到小排序,将Predict值大的前2个物品推荐给Leo。这段代码没有写。/ Z5 y( W* l1 `5 [, T, w

    5 O4 I6 n3 [: Y7 q从上面的示例中可以看出,推荐算法的实现非常麻烦,需要循环,判断,合并数组等等。如果处理不当,反而会成为系统的累赘。在实际处理中还有以下问题:- a5 d0 a& e) |% c: ]
    ; H, c. [: H% _
    1.以上示例我们只对Leo进行推荐,而且我们已经知道Leo没有评价过f,g,h物品。如果放到实际的系统里,对于每一个需要进行推荐的用户,都要查询出他没有评价过哪些物品,这又是一部分开销。
    3 p, ]) I! A1 S! j0 h& t( C* \+ t( N( q& j
    2.不应当进行整表查询,在实际系统中可以设定一些标准值。比如:我们求Leo与表中的其他人的Cos值,如果该值大于0.80,则表示可以为邻居。这样,当我找到10个邻居之后,就停止求Cos值,避免整表查询。对于推荐物品也可以适当采用此方法,比如,我只推荐10个物品,推荐完后就停止求Predict值。
      ]' c  ?3 C' P) o1 I; G3 p
    * X4 _1 {; G; Q/ {) I2 O3.随着系统的使用,物品也会发生变化,今天是fgh,明天没准就是xyz了,当物品变化时,需要动态的改变数据表。
    , ^1 j/ M6 ~: W+ P; d
    ( k( J/ W% r$ s6 p: G/ z4.可以适当引进基于内容的推荐,来完善推荐算法。
    : w3 S0 `5 K. x; T8 y
    * o9 Z/ G9 _: f( A) [5.推荐的精确性问题,这个设置不同的标准值,会影响精确性。  q! _) m# `" y$ _. {6 q
    ————————————————  `5 V- P4 U# R- @7 `2 i/ }9 k' o
    版权声明:本文为CSDN博主「星斗其文,赤子其人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。1 @& R8 a2 r+ i# Z3 [5 s3 R- \
    原文链接:https://blog.csdn.net/liuliuhelingdao/article/details/126715465
    2 K! j- r6 [6 n5 U7 Y! b! A* I9 b0 y$ a0 Y) L% D
    . t- {3 [- `0 S8 R8 {& k
    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-8-5 12:11 , Processed in 0.447808 second(s), 51 queries .

    回顶部