QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1898|回复: 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
    . y  Y, ~) x1 S" a1 T1 p4 w( F
    php+mysql实现简单的协同过滤推荐算法
    % _" v2 c0 M2 q, o1 J仅做标记。。。
    5 N4 f) j2 I9 `8 q' k+ S
    ; w9 P! E) p* K4 n( I0 e  S7 Z& z+ j6 D: L8 n

    ; K$ j2 L, |8 O要实现协同过滤推荐算法,首先就要理解算法的核心思想和流程。该算法的核心思想可以概括为:若a,b喜欢同一系列的物品(暂时称b是a的邻居吧),则a很可能喜欢b喜欢的其他物品。算法的实现流程可以简单概括为:1.确定a有哪些邻居 2.通过邻居来预测a可能会喜欢哪种物品  3.将a可能喜欢的物品推荐给a。
    . U7 M- t" C0 ?  K" j" T( F7 D7 e) d
    算法核心的公式如下:  M6 j  n! Z4 `7 @$ v

    4 \/ R$ R  }4 ]5 `7 Z2 f1.余弦相似度(求邻居):
    7 @) Z3 U; q9 r' V% u
    0 j  U  n5 a4 z# r+ L$ w2.预测公式(预测a可能会喜欢哪种物品):1 W# I4 n0 k- h" L6 t- w
    0 j3 g8 w% p) K: ]+ c7 Q/ I
    仅从这两个公式我们就可以看出,仅仅是按照这两个公式进行计算,就需要进行大量的循环与判断,而且还涉及到排序的问题,就涉及到排序算法的选择与使用,这里我选快排,从网上copy了一段快排,直接用。总之实现起来很麻烦,在大数据情况下,更何谈效率。  F9 \. N; B4 X$ X, J2 m" B

    " f) o/ e# k  v9 f2 n- ~首先建表:
    " _; V& b! X" k% r" G5 `& J+ c4 v! E8 a. X8 g( w/ K
    DROP TABLE IF EXISTS `tb_xttj`;
    * L5 P, Y: |: M' U7 S/ sCREATE TABLE `tb_xttj` (- O; O* g5 c1 I/ v# x
      `name` varchar(255) NOT NULL,  o% o6 E* G+ C
      `a` int(255) default NULL,
    + n  O9 j: Y4 w( w/ h  `b` int(255) default NULL,: Y9 c: |3 ?5 r9 I9 x
      `c` int(255) default NULL,
    2 n. U7 f2 D: I+ V. m4 J# Q( ^  `d` int(255) default NULL,
    # d' P3 G: y! c+ _  `e` int(255) default NULL,
    . X. o( S5 W" q4 w  `f` int(255) default NULL,
      A& i* }, w) M2 }4 C  `g` int(255) default NULL,
    # ~/ l. s( |' ]  ^" ?5 t# ^+ B0 h  `h` int(255) default NULL,
    5 R; U( ^% Y4 P2 ?, J! c  PRIMARY KEY  (`name`)
    % i2 f- O9 a  R7 c) ENGINE=MyISAM DEFAULT CHARSET=latin1;
    * I" s: P" b! ]! ?- r: j* j  S& a
    : R4 T& i) t5 cINSERT INTO `tb_xttj` VALUES ('John', '4', '4', '5', '4', '3', '2', '1', null);
    4 W, ~; A, \8 D' V/ L$ y1 }! `INSERT INTO `tb_xttj` VALUES ('Mary', '3', '4', '4', '2', '5', '4', '3', null);
    1 ?, k2 ?# u0 t) u3 s. nINSERT INTO `tb_xttj` VALUES ('Lucy', '2', '3', null, '3', null, '3', '4', '5');8 s5 _9 R6 k" y
    INSERT INTO `tb_xttj` VALUES ('Tom', '3', '4', '5', null, '1', '3', '5', '4');
    ; |: o; D& J" T, }8 v( h- K; TINSERT INTO `tb_xttj` VALUES ('Bill', '3', '2', '1', '5', '3', '2', '1', '1');$ D1 @' Q' [, A/ l1 o
    INSERT INTO `tb_xttj` VALUES ('Leo', '3', '4', '5', '2', '4', null, null, null);
    . }3 s3 o3 T8 t( N% V
    / t  @9 U3 T) t( f
    2 b3 W1 S  x9 ~2 ]5 {4 r: t+ L* R 我这里只对最后一行的Leo进行推荐,看看f,g,h哪个可以推荐给他。5 J  y; ~' o; s# [: }5 I

    9 Q5 ^* z+ G& ?; W    用php+mysql,流程图如下:% K0 K1 U: a; p! }( p& ?( a/ U
    4 X' q$ }0 o: d+ ^+ b
    连接数据库并将其存储为二维数组的代码如下:7 F( H+ m* }( \" L
    - E9 C* S8 \6 U( K. r
    header("Content-Type:text/html;charset=utf-8");
    2 ^8 w9 o3 y! F  W: R% i9 ?) A. q& k- I# z
    mysql_connect("localhost","root","admin");0 v0 W" t% ?$ z, m4 _1 d% l3 f
    mysql_select_db("geodatabase");
    ) E9 U1 I; g+ f  g3 mmysql_query("set names 'utf8'");       
    " k# ^; o" G) I1 s+ G. H
    . f3 p& h1 E. I% B4 K$sql = "SELECT * FROM tb_xttj";5 G7 M- e4 x* H( d- x, o
    $result = mysql_query($sql);4 m6 e& ?  u$ C6 H) E% |8 ^" P
    9 n, p$ j" g4 ^- B& f6 l  p5 `
    $array = array();
    ! V% W4 o5 @7 t6 C2 ?3 c! Ewhile($row=mysql_fetch_array($result))" D! `6 `- B3 F/ h
    {
    & R; \7 r! r/ w9 x6 I' J- [        $array[]=$row;//$array[][]是一个二维数组% J) i1 P! S5 r3 W9 ?
    } ; K- O" w/ x* d' N8 B

    ) E7 [6 H: P( j. E3 C9 M# ?5 Y问题1:这一步完全可以看做是整表查询,这种查询是大忌,对于这种小小的演示系统还可以,但是对大数据的系统,没有效率,至于如何改进,还得多学习才是。% h5 Q: H1 p! L. W) l
    ) D# l. w& Z, e  S+ A) p
    求Leo与其他人的Cos值代码如下:: d3 l3 H4 S. A6 P) f: u

    2 ]. y/ E. f+ f6 B1 X/*
    ' Q2 o( g& i3 ~; ` * 以下示例只求Leo的推荐,如此给变量命名我也是醉了;初次理解算法,先不考虑效率和逻辑的问题,主要把过程做出来
    ! b6 W" [7 M% ^5 J8 V' j' X; w */
    " C% j8 i4 R2 ?, O5 T9 B1 C! y, o+ s: [2 Y
    $cos = array();' T* n. g3 Z) J3 a
    $cos[0] = 0;, i: |+ r# s$ M) W" X
    $fm1 = 0;
    - j+ x( r# f( {4 h//开始计算cos8 S0 z$ r1 T. A
    //计算分母1,分母1是第一个公式里面 “*”号左边的内容,分母二是右边的内容1 P  _- D9 T3 z8 t' K* N! e- k  M; \
    for($i=1;$i<9;$i++){
    * a7 a( _  w; X3 C, W- A4 X8 f+ [2 n        if($array[5][$i] != null){//$array[5]代表Leo, L' Y" C5 i' k, M2 M
                    $fm1 += $array[5][$i] * $array[5][$i];3 n; J; F& F) T! N! _
            }
    9 D& U, M. ]1 Y5 A9 [! C6 G" L}9 k7 G) L* E3 b/ e3 [. y# `

    * j* c* V' E# k9 A# p$fm1 = sqrt($fm1);
    2 a9 M( F! l5 i3 B. ?' h4 E; V. h1 ]4 g! d# z# w' k
    for($i=0;$i<5;$i++){! _  U& v7 O2 i# D: n  o0 X% R
            $fz = 0;( E! \( R& H* N8 \
            $fm2 = 0;
    & ?1 x- E$ b, p        echo "Cos(".$array[5][0].",".$array[$i][0].")=";* t- V1 p6 m# }: F
           
    $ m6 N3 Q' H8 l5 u( J        for($j=1;$j<9;$j++){
    6 K: L7 G6 z9 G7 X. a5 T# T0 E            //计算分子; @- |5 b! }: s& \7 m" y* T
                    if($array[5][$j] != null && $array[$i][$j] != null){0 G# Z& [3 ]( f+ M
                            $fz += $array[5][$j] * $array[$i][$j];$ X( w& d7 d. E4 L' l8 o1 Y
                    }, G! D. c! N- v: E3 @9 Y2 _* J
                    //计算分母2
    8 Z, ~' _5 I  z% ~                if($array[$i][$j] != null){
    2 p4 q. ]4 O# S) a% s' f                        $fm2 += $array[$i][$j] * $array[$i][$j];
    0 `7 U6 l9 ~2 l4 A4 R* U% F                }                        + @" M+ w/ ^2 \7 c& U5 o) L3 i
            }
    , C9 N3 Q& F" U$ Q( M; \2 W        $fm2 = sqrt($fm2);
    9 [$ B- p7 x4 l8 i: @1 ^        $cos[$i] = $fz/$fm1/$fm2;  v! f" L2 a+ W' n3 n% L! C4 {
            echo $cos[$i]."<br/>";
    / T& Z" z5 u' f0 C}* b% q3 O3 ~! V) O5 A

    + L$ H6 D9 ]! _# h  K9 I, @7 a4 }这一步得到的结果是酱紫:. j1 ^& `$ n  m' n

    0 ]4 T' l! ?( ]2 M* s将求好的Cos值排序,采用快排代码如下(百度copy而来):+ e% \2 y) m; x6 ]+ r
    # n0 F2 e/ Q' E' {  u7 h; a

    : Q0 G3 `8 ?; v) g//对计算结果进行排序,凑合用快排吧先
    3 t* B, Q% R3 r+ [7 Cfunction quicksort($str){
    3 w3 _" L9 C3 y+ u: M& N- ^        if(count($str)<=1) return $str;//如果个数不大于一,直接返回
    : v4 H+ a" w, g6 {9 D        $key=$str[0];//取一个值,稍后用来比较;% w% |: r' {0 B( z, J, h9 {1 G' m
            $left_arr=array();7 F$ }5 ?" w2 h3 d; h9 U5 o
            $right_arr=array();
    ( y% J0 `' ^1 l9 }- \       
    : @! a2 |) E. d5 a5 z        for($i=1;$i<count($str);$i++){//比$key大的放在右边,小的放在左边;
    8 D" p' D6 Q, q; J7 m                if($str[$i]>=$key)
    + B9 r$ S8 n0 K4 Y                $left_arr[]=$str[$i];% u$ T% L3 Q2 M. M/ ]; H% {3 m
                    else( B6 S8 a) Q6 {6 H  ^2 P1 J
                    $right_arr[]=$str[$i];
    $ M: _% Z4 I/ h" Q1 n! \        }
    7 b2 x- S3 {& q( E" t) w* m, k        $left_arr=quicksort($left_arr);//进行递归;! J8 ]( D* k' q
            $right_arr=quicksort($right_arr);1 `. }0 g/ s& O' T- P9 @; q
            return array_merge($left_arr,array($key),$right_arr);//将左中右的值合并成一个数组;% r5 |' Y: f5 Q( O0 n6 w  ]
    }2 p2 F: w6 F1 ^7 q3 P/ p) k
    - c4 R3 w* i" }2 x4 D) A
    $neighbour = array();//$neighbour只是对cos值进行排序并存储# Z8 \' j. ^- ~$ w, J& T. Y: M/ u5 f
    $neighbour = quicksort($cos);  s& ?5 D4 z, j+ K! k2 Z

    + q( Q/ t* l( G2 `
    7 y' O- K6 H7 ^+ p- H, E这里的$neighbour数组仅仅存储了从大到小排序好的Cos值,并没有与人联系起来。这个问题还要解决。
    % G3 V4 s( S% f$ ~8 J0 ]8 _
    / c8 f& B/ O* U& c/ h$ R1 k选出Cos值最高的3个人,作为Leo的邻居:9 T& R" F& f* h9 g
    : J' g9 f4 `8 N2 r9 e0 N
    //$neighbour_set 存储最近邻的人和cos值
    * d# |2 Y) d5 a' j4 ?, k8 @  K: y& a$neighbour_set = array();; S! v8 J) e+ M9 ~
    for($i=0;$i<3;$i++){0 w) z6 E% l3 Q/ S9 E" o
            for($j=0;$j<5;$j++){
    ) [; V; ~: V+ |$ K  l9 ^                if($neighbour[$i] == $cos[$j]){6 Q: i- O: L( a0 S
                            $neighbour_set[$i][0] = $j;
    8 K4 e, ~. v; T1 A                        $neighbour_set[$i][1] = $cos[$j];
    2 @" P0 a) F) T; k( \, _5 w/ e& ~! ~6 v                        $neighbour_set[$i][2] = $array[$j][6];//邻居对f的评分
    ( R9 q" s3 V' `4 z5 z! D  J# }' Q                        $neighbour_set[$i][3] = $array[$j][7];//邻居对g的评分* x, l; b" Z+ A0 ], K7 J) [  w
                            $neighbour_set[$i][4] = $array[$j][8];//邻居对h的评分3 D* Y; n+ X9 J. x3 V9 _* K& ^( X5 g
                    }
    * u* g) |& F0 N/ t7 C        }. B0 D2 K: `: j% n3 F. h
    }
    0 B, w. L3 o" gprint_r($neighbour_set);
    : c' i3 o% M: B  o6 j9 f$ p' ?echo "<p><br/>";  K! T; C) \) }0 d8 c: h

    - F/ ^$ z. m' V' [! R, c' Z这一步得到的结果是酱紫:1 R( n, y0 i$ I7 D( c  I% R& R
    5 L/ s/ J. [& H6 u7 W" |  R

    & h6 L+ }/ p% w" h- ^' }/ h% v" u/ V$ r7 h8 ]+ V
    转存失败重新上传取消
    " v0 x4 i! [/ y4 n2 c2 W3 C- i, S- s
    这是一个二维数组,数组第一层的下标为0,1,2,代表3个人。第二层下标0代表邻居在数据表中的顺序,比如Jhon是表中的第0个人;下标1代表Leo和邻居的Cos值;下标2,3,4分别代表邻居对f,g,h的评分。
    ! t2 P8 g, T: V7 j. m) y' l2 W
    ! t# T4 A% G/ _+ L7 p  V开始进行预测,计算Predict代码如下:8 t, a, U3 v7 d& g% s; p# w

    1 T2 N8 Y8 `! \9 _9 n: H我是分别计算Leo对f,g,h的预测值。在此有一个问题,就是如果有的邻居对f,g,h的评分为空,那么该如何处理。比如Jhon和Mary对h的评分就为空。本能的想到用if判断一下,如果为空则跳过这组计算,不过这样处理是否合理,有待考虑。以下代码并没有写出这个if判断。, }+ ^6 O( N. e& e7 Q9 F
    1 {% A8 N) W4 N; |4 N- j) L
    //计算Leo对f的评分1 E2 i: W$ ?, @: ]3 Z) r
    $p_arr = array();
    3 b& c% U/ C  l' J* o, T$pfz_f = 0;0 T1 ]3 a) m, P8 t
    $pfm_f = 0;) v4 x& }' l" a* V
    for($i=0;$i<3;$i++){
    7 I7 P# v0 F! ?* F( W- L) W" j        $pfz_f += $neighbour_set[$i][1] * $neighbour_set[$i][2];1 K' x% d( H, F  ]9 w/ r* C
            $pfm_f += $neighbour_set[$i][1];
    + C& w( W6 w+ h6 l) T! J}
    2 d; z# l7 I9 k% `' I# f: u- a$p_arr[0][0] = 6;9 y. t2 N1 Q' D1 G
    $p_arr[0][1] = $pfz_f/sqrt($pfm_f);+ b7 z1 j: N" Z, |
    if($p_arr[0][1]>3){3 i) ]# P: T4 g' n8 r# c( |
            echo "推荐f";; E6 u/ d: M. B9 h, z# i) j- v
    }
    * B& m. \6 ^) Z+ S3 P- I/ C5 v/ r4 T3 k1 C& C1 E( j: _! n- P
    //计算Leo对g的评分9 J0 \* E& l4 {/ ^
    $pfz_g = 0;
    . w& g9 J6 @2 y; F* J$pfm_g = 0;3 ~' P7 S" x% d& S# r2 d# Y$ {/ {
    for($i=0;$i<3;$i++){
    6 T- @1 D$ ~8 V0 ~$ n6 C        $pfz_g += $neighbour_set[$i][1] * $neighbour_set[$i][3];1 u: R, d* P8 }$ b- ~+ a# g
            $pfm_g += $neighbour_set[$i][1];
    . L& t$ t7 ~3 T; N3 _8 z        $p_arr[1][0] = 7;
    ; _4 `% ]% ]4 [2 w        $p_arr[1][1] = $pfz_g/sqrt($pfm_g);. t0 h2 i" O" L% ]4 m, ^+ v
    }- R% R2 B( P3 }, d: H2 @  a' i$ B
    if($p_arr[0][1]>3){
    * k0 V1 X- W/ L7 ^0 [        echo "推荐g";; i: F- J) b7 _
    }# L- |& _, m/ v% u$ f) n

    % p3 L' |9 k. U& ^# [2 m//计算Leo对h的评分- E0 V& V& U" y1 |
    $pfz_h = 0;7 K2 a& U2 T0 M$ B: M: c* N
    $pfm_h = 0;
    0 n7 y) V. d0 F0 `" H: p- O. ^4 O# ffor($i=0;$i<3;$i++){; C( h1 s/ |, A# z% Z2 m4 x3 L; |
            $pfz_h += $neighbour_set[$i][1] * $neighbour_set[$i][4];6 l. {/ B/ m# m4 Z% Z
            $pfm_h += $neighbour_set[$i][1];
    8 y( d% V: t& Y7 O9 I( {; c- W        $p_arr[2][0] = 8;3 T' F; F& l2 F- L
            $p_arr[2][1] = $pfz_h/sqrt($pfm_h);
    0 n8 }' H2 {# o5 p, U}( ]8 [+ d1 K: w& Q5 {: f8 `! I  q1 B6 J
    print_r($p_arr);0 x1 r4 f9 Y$ ]
    if($p_arr[0][1]>3){) r) Y: r" t$ ^- q9 F3 t7 t/ Y
            echo "推荐h";
    ' A' [! M' A! K& L}
    4 j9 s3 ]; c4 \: B% Y- y3 s. i; }' o, |
    $p_arr是对Leo的推荐数组,其内容类似如下;* s: t# m* y+ s' f: g0 ]- q% O4 h
    - x/ G! Z6 H5 k) b& o
    Array ( [0] => Array ( [0] => 6 [1] => 4.2314002228795 ) [1] => Array ( [0] => 7 [1] => 2.6511380196197 ) [2] => Array ( [0] => 8 [1] => 0.45287424581774 ) )
    4 Z- l9 N( `" ^( f! C
    3 V" D4 \: S7 ^* |0 p6 X. Wf是第6列,Predict值是4.23,g是第七列,Predict值是2.65........& K. _  d1 @9 l  f& Q

    8 W. W7 a3 y* M" h" [" Q求完了f,g,h的Predict值后有两种处理方式:一种是将Predict值大于3的物品推荐给Leo,另一种是将Predict值从大到小排序,将Predict值大的前2个物品推荐给Leo。这段代码没有写。5 B; m1 R9 w$ G9 k

    ( v& H+ A; ]6 a. x+ {' C从上面的示例中可以看出,推荐算法的实现非常麻烦,需要循环,判断,合并数组等等。如果处理不当,反而会成为系统的累赘。在实际处理中还有以下问题:  P7 S6 r- e3 N! r
    " m, z2 A4 Q' R$ P4 y
    1.以上示例我们只对Leo进行推荐,而且我们已经知道Leo没有评价过f,g,h物品。如果放到实际的系统里,对于每一个需要进行推荐的用户,都要查询出他没有评价过哪些物品,这又是一部分开销。$ c0 ^% |- y- d% F, b! F
    " C; ?; w/ l* X4 O+ Y% _" V
    2.不应当进行整表查询,在实际系统中可以设定一些标准值。比如:我们求Leo与表中的其他人的Cos值,如果该值大于0.80,则表示可以为邻居。这样,当我找到10个邻居之后,就停止求Cos值,避免整表查询。对于推荐物品也可以适当采用此方法,比如,我只推荐10个物品,推荐完后就停止求Predict值。# G3 m! n- Y- g$ r$ P; W

    ! k5 o- q+ D0 i/ o! J3.随着系统的使用,物品也会发生变化,今天是fgh,明天没准就是xyz了,当物品变化时,需要动态的改变数据表。8 s" j2 K# M8 w% W2 F* t
    / z/ S/ s* v$ {$ ?& A5 z0 R
    4.可以适当引进基于内容的推荐,来完善推荐算法。0 u7 L' W8 P; Q2 r
    , f- P4 }. K* u. [. g
    5.推荐的精确性问题,这个设置不同的标准值,会影响精确性。+ J. e, h0 r) e1 c
    ————————————————
    1 n6 P$ b1 I; {2 U$ d5 I; l版权声明:本文为CSDN博主「星斗其文,赤子其人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    & ?+ R2 I1 e( F8 X; s, q8 ]5 v原文链接:https://blog.csdn.net/liuliuhelingdao/article/details/126715465. a0 n8 W* v4 P: A9 }

    5 J0 Z/ l* T, P- v- y" {
    % i( o8 ?6 H7 G! K- Q1 h
    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-17 04:20 , Processed in 0.451502 second(s), 51 queries .

    回顶部