QQ登录

只需要一步,快速开始

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

    + a6 T- p) a3 Y& F( mphp+mysql实现简单的协同过滤推荐算法
    5 W0 O* j1 Q) {' b' Y& o仅做标记。。。4 ?4 E3 p+ E  U

    , Y. D; r) k% u3 W' K' d# B9 k6 W
    8 d4 h; N4 B, _5 Z/ w6 z
    9 A" B: a. B8 F  F/ W9 A要实现协同过滤推荐算法,首先就要理解算法的核心思想和流程。该算法的核心思想可以概括为:若a,b喜欢同一系列的物品(暂时称b是a的邻居吧),则a很可能喜欢b喜欢的其他物品。算法的实现流程可以简单概括为:1.确定a有哪些邻居 2.通过邻居来预测a可能会喜欢哪种物品  3.将a可能喜欢的物品推荐给a。7 |. ]' v5 m9 f, P1 h
    " d3 A3 M6 Z' o2 s2 z" W8 P
    算法核心的公式如下:" p. d9 j) \3 _6 m

    ) z5 H! D; _/ n  b% P0 U1.余弦相似度(求邻居):
    * y# s8 L! v9 y  }. S& C. b6 O" W
    2.预测公式(预测a可能会喜欢哪种物品):% s0 z) ^5 I/ I/ K5 V
    / _1 L1 o: v# x/ R% {
    仅从这两个公式我们就可以看出,仅仅是按照这两个公式进行计算,就需要进行大量的循环与判断,而且还涉及到排序的问题,就涉及到排序算法的选择与使用,这里我选快排,从网上copy了一段快排,直接用。总之实现起来很麻烦,在大数据情况下,更何谈效率。# |, ~7 w$ R9 g
    % j) t  I: {6 s& Q& l
    首先建表:+ {2 }* G; o6 q* Z1 W. b
      t: y& C2 x$ f' c" ?
    DROP TABLE IF EXISTS `tb_xttj`;
    ) y' F& D3 [$ v3 ^! ~CREATE TABLE `tb_xttj` (# F( c: k' a4 y
      `name` varchar(255) NOT NULL,9 x2 J, _) a' y1 Y3 ^' [5 x! y
      `a` int(255) default NULL,- i) W. P, z) {3 O
      `b` int(255) default NULL,/ E  A  `2 G# C7 }4 S$ O
      `c` int(255) default NULL,& I6 F; G0 r& N) \8 [4 U
      `d` int(255) default NULL,
    ; R3 ?6 h5 _- I( k  `e` int(255) default NULL,
    9 Z4 o/ t/ n4 w, G$ H# ~2 z+ M% a  `f` int(255) default NULL,
      W: S2 C) ?: k; g7 h  `g` int(255) default NULL,, ^% v+ d0 |$ ]1 {( }6 z; {8 d
      `h` int(255) default NULL,  A  H8 w8 X/ U8 d0 L) y1 Z
      PRIMARY KEY  (`name`)
    ( g& Y1 M0 k# g# @1 n# g* ]' j& z) ENGINE=MyISAM DEFAULT CHARSET=latin1;+ W2 S7 d6 w2 z6 q% ]  u" P5 w
    ( j) t+ I0 R+ E$ T
    INSERT INTO `tb_xttj` VALUES ('John', '4', '4', '5', '4', '3', '2', '1', null);
    ; n( K# i! _+ FINSERT INTO `tb_xttj` VALUES ('Mary', '3', '4', '4', '2', '5', '4', '3', null);8 m; i1 y- z" Y1 I7 z
    INSERT INTO `tb_xttj` VALUES ('Lucy', '2', '3', null, '3', null, '3', '4', '5');* Z+ v3 ]) P) N# w" t, N6 x
    INSERT INTO `tb_xttj` VALUES ('Tom', '3', '4', '5', null, '1', '3', '5', '4');
    7 Q3 w% j" i  n8 Y8 OINSERT INTO `tb_xttj` VALUES ('Bill', '3', '2', '1', '5', '3', '2', '1', '1');
    # C/ T8 x6 I* F  }. NINSERT INTO `tb_xttj` VALUES ('Leo', '3', '4', '5', '2', '4', null, null, null);
    ; b) Q4 }- A; e+ `+ F, o: V4 K* |
    " @- f# G4 H2 p- C+ ]5 q! m6 y
    我这里只对最后一行的Leo进行推荐,看看f,g,h哪个可以推荐给他。, M9 R% l1 x5 D6 ^9 U, ]
    1 `! u/ y" k3 E5 H, _3 Q0 j- d3 c4 O
        用php+mysql,流程图如下:+ G6 I+ u  V6 k/ E8 O- v

    ) H4 s/ g2 o- C# q连接数据库并将其存储为二维数组的代码如下:+ Z& A  }" z: w8 h

    ( Y; V0 c+ Q! p- \* b" y' f1 Y$ t5 Nheader("Content-Type:text/html;charset=utf-8");7 o% T9 ~' u7 j

    " d, {& p' R/ v, T  l* H; a& U: r4 M1 M2 amysql_connect("localhost","root","admin");
    - w# x6 _. g# j; A/ ^( [. Rmysql_select_db("geodatabase");1 s4 |; b, F' [: p. ]) w  w
    mysql_query("set names 'utf8'");       
    & S* |1 o! d" T" S6 m3 b+ |' M
      u: r" u; j) {7 Y  U6 I) l0 v0 N$sql = "SELECT * FROM tb_xttj";- c8 }6 m2 `+ H' N
    $result = mysql_query($sql);6 N! \" w0 z: z# O, P; t
    7 }. u3 ^& G- u) V0 o) h- s# g" \
    $array = array();9 R4 L2 c, T4 {" p+ K0 {
    while($row=mysql_fetch_array($result))0 ]9 w1 B* G  B: f
    {9 a: s1 ^! P, s( n! ]6 U6 q
            $array[]=$row;//$array[][]是一个二维数组$ _% {/ L( U6 s" k. x. |" Y6 }2 A
    }
    5 U% ]* `; Z! _8 m# s3 I9 f* Y! e' x: ^5 P. J
    问题1:这一步完全可以看做是整表查询,这种查询是大忌,对于这种小小的演示系统还可以,但是对大数据的系统,没有效率,至于如何改进,还得多学习才是。; |/ v" m4 w& P* J. `2 m- {
    ( f0 u% J+ R; P, j1 ^$ ^
    求Leo与其他人的Cos值代码如下:
    8 w' c; ^9 V. [* P" y) u# b& G* V3 Q/ j/ B- y! h
    /*' p6 [$ }  ~% O
    * 以下示例只求Leo的推荐,如此给变量命名我也是醉了;初次理解算法,先不考虑效率和逻辑的问题,主要把过程做出来
    9 r) X5 U, O: l  `7 l! D */: F( Q& U# C3 o

    9 x0 i4 l0 F4 v$cos = array();0 Z9 b( B  e# Q# B5 E% F, Z6 F
    $cos[0] = 0;) [, G' ~2 }$ g6 F6 I$ e- s8 g8 A
    $fm1 = 0;
    4 d; `; m2 N( k1 w5 L, j//开始计算cos7 o5 U; M! f, a7 }! s* s# s! f
    //计算分母1,分母1是第一个公式里面 “*”号左边的内容,分母二是右边的内容
    1 J1 Y4 e& f' ]( X- Y/ |5 b6 wfor($i=1;$i<9;$i++){% l! w% Y" V' l$ _0 G
            if($array[5][$i] != null){//$array[5]代表Leo& x" k9 V) w' K0 Q
                    $fm1 += $array[5][$i] * $array[5][$i];' E: }) q' V3 b. S- E+ c
            }
    , A3 Z9 w2 G8 A: ?9 m}
    4 {) |1 X4 N% R! J/ f+ }+ R& j* M) T% t5 G; M5 d/ V. l
    $fm1 = sqrt($fm1);! K/ E, v, i" M" z, ~0 q
    , V: ^# Z" P6 h" _/ O& C
    for($i=0;$i<5;$i++){
    $ h- d  c8 n5 M        $fz = 0;6 \: Z) r3 V+ c1 z% s* }- @/ l
            $fm2 = 0;! _/ R+ w* V8 R( B
            echo "Cos(".$array[5][0].",".$array[$i][0].")=";
    * y5 G1 Z- E6 k7 ?4 |3 s" h       
    , g6 V% W# J' p7 M2 F; T# r- A        for($j=1;$j<9;$j++){4 o0 ]9 _9 P+ G& ?7 {
                //计算分子! ]) G- q5 e( g6 I: e
                    if($array[5][$j] != null && $array[$i][$j] != null){4 S4 B9 p# Q" a
                            $fz += $array[5][$j] * $array[$i][$j];+ k0 P# n" `! x# v$ R1 D, ~- ^' c, T
                    }: O- V/ k6 R( O3 Z/ Y' j
                    //计算分母22 u4 h2 {) b. x; f# C4 j3 v* M
                    if($array[$i][$j] != null){
    ) I% e8 M8 j# C- X6 f  U                        $fm2 += $array[$i][$j] * $array[$i][$j];  _9 t9 e& D! ]+ o  g
                    }                       
    6 b1 `+ B' Y1 s& b, B        }
    3 _/ ^0 ?* ^; m! h) H        $fm2 = sqrt($fm2);' ^8 N4 C* D0 Y6 K
            $cos[$i] = $fz/$fm1/$fm2;! ]+ N# I2 h+ N% I2 ^
            echo $cos[$i]."<br/>";
    5 W, E3 _- |2 T}
    ; ?# ]6 m  U$ c$ x6 ?2 V9 n. a4 U( r/ K' S* x
    这一步得到的结果是酱紫:
    5 @. e6 F# B5 P2 J; F: g& d1 ^) H) V" S/ Q% l& s
    将求好的Cos值排序,采用快排代码如下(百度copy而来):+ I' t0 ], G: h7 C* W: X

    # N% N" k4 _5 f" c: ]2 x, h2 u! U- V; q6 z# I
    //对计算结果进行排序,凑合用快排吧先4 i5 o- M8 I, v, B
    function quicksort($str){" J- }; R. y) j/ M7 |
            if(count($str)<=1) return $str;//如果个数不大于一,直接返回7 h- m. [, J0 t$ O4 J: n3 N% w: K
            $key=$str[0];//取一个值,稍后用来比较;& L5 M% q! `- w& W
            $left_arr=array();* r5 U: i) h' _) o' V" P  _
            $right_arr=array();
    $ o6 t9 n4 u6 W% n. B: W  O" \       
    & p8 Q5 S1 E6 @4 b! S        for($i=1;$i<count($str);$i++){//比$key大的放在右边,小的放在左边;" `( a, U/ D  t* {% a# D( }
                    if($str[$i]>=$key)
    9 K8 o. s# O  H9 @                $left_arr[]=$str[$i];
    % g1 X/ D* u/ D" ^1 ]7 v* @/ N$ s" n                else7 x+ C6 m, w8 x
                    $right_arr[]=$str[$i];
    2 k7 M7 u% B3 @7 [% R        }  [. r' p. A9 i# D$ U- K8 Y
            $left_arr=quicksort($left_arr);//进行递归;( D; F7 S! G$ R9 o/ j6 q3 {
            $right_arr=quicksort($right_arr);
    ; n( ?3 E: ^6 a3 r        return array_merge($left_arr,array($key),$right_arr);//将左中右的值合并成一个数组;: K; d" p# U4 b+ s/ `0 d, S& ~
    }
    * Q. q; ]" F, k. ?6 u) I! k' c( y, |+ I: M& s& v
    $neighbour = array();//$neighbour只是对cos值进行排序并存储
    % t9 ?& w2 Z6 w4 B5 _3 U/ a9 Q$ g$neighbour = quicksort($cos);
    9 d+ f8 V& ]  H. @! U" A# y3 \0 y' y7 L, Q
    # s! ]- |9 B8 k) |5 a; s! s  f) u
    这里的$neighbour数组仅仅存储了从大到小排序好的Cos值,并没有与人联系起来。这个问题还要解决。; q+ e( }" d% W$ {1 f+ l
    0 `- s) h; E' W1 o' C
    选出Cos值最高的3个人,作为Leo的邻居:
    1 G, V4 g+ l) S% f4 z) c7 |9 u  B: [1 R
    //$neighbour_set 存储最近邻的人和cos值
    4 C' j8 l' D: `: X$neighbour_set = array();' c* m0 t6 t, d; J+ S  @
    for($i=0;$i<3;$i++){" @: ]/ [2 x# w: {+ g3 O; {6 z
            for($j=0;$j<5;$j++){; o- r' e# X6 e' R6 ?
                    if($neighbour[$i] == $cos[$j]){
    $ r6 N/ w) x4 R, q" c                        $neighbour_set[$i][0] = $j;# |/ v& k, O, _6 {1 F9 I1 N  N
                            $neighbour_set[$i][1] = $cos[$j];
    9 l+ V' e: }4 c$ ~& @: P) a4 y                        $neighbour_set[$i][2] = $array[$j][6];//邻居对f的评分- U1 O" g" X- H
                            $neighbour_set[$i][3] = $array[$j][7];//邻居对g的评分8 v. F/ i/ o* u
                            $neighbour_set[$i][4] = $array[$j][8];//邻居对h的评分
    ' o  G) u$ T' J: c/ |, u+ g  g                }; S* m# z% k5 u! y' F; |7 ?
            }* o  ]3 M( D6 c+ a0 w
    }
    9 E8 m& E2 B% I, v; R2 ]4 iprint_r($neighbour_set);
    1 T: g# M0 Z; y3 j" \echo "<p><br/>";
    $ D; K9 S; H! l9 J! i- n+ r- ]) y) d  c8 P) U  P9 \
    这一步得到的结果是酱紫:, h$ d/ X1 L* T

    : }9 d5 k- m4 }  {" Q% U& I) l. `
    6 L5 w# |1 @+ H+ ]
    2 k/ i) E! F. C# V5 O  s转存失败重新上传取消
    ! `/ [5 w" q6 j6 Y
    : L3 b$ M/ [: C6 h这是一个二维数组,数组第一层的下标为0,1,2,代表3个人。第二层下标0代表邻居在数据表中的顺序,比如Jhon是表中的第0个人;下标1代表Leo和邻居的Cos值;下标2,3,4分别代表邻居对f,g,h的评分。$ b: z) h$ o% `, ]0 O- T$ O9 H

    / J. e. Y) X( P) Z6 n0 q开始进行预测,计算Predict代码如下:8 G/ W9 f9 A1 `" D. H/ U

    * H' J0 U0 T1 b9 v我是分别计算Leo对f,g,h的预测值。在此有一个问题,就是如果有的邻居对f,g,h的评分为空,那么该如何处理。比如Jhon和Mary对h的评分就为空。本能的想到用if判断一下,如果为空则跳过这组计算,不过这样处理是否合理,有待考虑。以下代码并没有写出这个if判断。8 t) s" w1 }8 D+ N
    ( Y) t% Q' f, |  s, E
    //计算Leo对f的评分8 r% Q( x" a% ?: r! f) H
    $p_arr = array();
    * ~! M. O9 l/ n/ f) @6 ^; ^$pfz_f = 0;! L5 K+ K6 r5 ^
    $pfm_f = 0;* R, |6 Y" g$ \2 J- K
    for($i=0;$i<3;$i++){
    : u5 l3 u  I7 c" j2 P; N6 Y        $pfz_f += $neighbour_set[$i][1] * $neighbour_set[$i][2];" F1 v/ R3 r) p
            $pfm_f += $neighbour_set[$i][1];2 E6 {* |; e! {/ C4 G( O
    }1 J2 t1 W; z# V1 c6 m5 v
    $p_arr[0][0] = 6;0 c$ E4 ]* G$ F6 K4 @* X  x
    $p_arr[0][1] = $pfz_f/sqrt($pfm_f);
    2 \0 p4 F: J  X( W" B7 v  x/ B: R# hif($p_arr[0][1]>3){
    8 P( O% W% E4 K& p/ H        echo "推荐f";
    0 W+ p6 C! X: Z2 Q}
    # c" D( h* j! J- P9 A$ a' [, l2 G$ m: Q- X$ _
    //计算Leo对g的评分+ v) q& o+ U5 x) q
    $pfz_g = 0;6 N7 n; M% k5 g7 r3 c5 ^  Y
    $pfm_g = 0;
    1 m3 e) t) i( `1 I! N; b9 ^  o; E3 [+ Hfor($i=0;$i<3;$i++){) i3 I8 q8 V% ]2 J) ?# V
            $pfz_g += $neighbour_set[$i][1] * $neighbour_set[$i][3];
    : e7 b# T/ o! B6 x        $pfm_g += $neighbour_set[$i][1];7 a) P4 ^+ Z+ ^6 ^6 u+ C  P# [
            $p_arr[1][0] = 7;, `7 I8 a+ J- o
            $p_arr[1][1] = $pfz_g/sqrt($pfm_g);
    6 M+ }8 O; p5 E6 ?8 p7 G5 ?}
    6 M* m9 H2 U$ K: O" Qif($p_arr[0][1]>3){
    * t* n- R7 x5 k6 y        echo "推荐g";
    # R* I5 C5 F6 R9 A( ]& H}  V: u4 i& F! u: B

    7 K9 Z" P* X, Q% Z3 P; I, w% o, R//计算Leo对h的评分
    , y: w# }% o0 n$pfz_h = 0;9 Q" M- I  a0 N8 g' a) w
    $pfm_h = 0;
    + d9 Q9 U" j1 F% i7 vfor($i=0;$i<3;$i++){5 y  @( Y/ L3 f; v: p. M
            $pfz_h += $neighbour_set[$i][1] * $neighbour_set[$i][4];
    # r* n( |7 ?* P. L        $pfm_h += $neighbour_set[$i][1];
    : _  X' e+ m. p8 u) }& T0 w        $p_arr[2][0] = 8;' z) P' g! N+ j' J* h* D" u+ {. J* S: n
            $p_arr[2][1] = $pfz_h/sqrt($pfm_h);5 e' |* S2 ?" ?5 e8 z- f
    }
    9 E- }4 q* I; s) R- uprint_r($p_arr);
    / s* t$ a$ w- `+ D& jif($p_arr[0][1]>3){
    8 }5 n5 O. v$ X2 @! a+ F        echo "推荐h";; Q/ y1 k" D- D" a5 A9 p
    }
    + M7 M9 O# J+ N+ H6 e
    ( L( t9 ?: z. k. q( ^1 `$p_arr是对Leo的推荐数组,其内容类似如下;6 F1 \6 Y# c! E/ b2 _1 k9 g' U- p

    - ?# `/ [) |0 [7 x" a4 VArray ( [0] => Array ( [0] => 6 [1] => 4.2314002228795 ) [1] => Array ( [0] => 7 [1] => 2.6511380196197 ) [2] => Array ( [0] => 8 [1] => 0.45287424581774 ) )
    ! I& g% Q- k+ s
    ! P( ^8 Z( V" g+ ~# Lf是第6列,Predict值是4.23,g是第七列,Predict值是2.65........
    * k8 M$ P$ q) [' N6 X* ?" J3 f" E+ H. p1 R4 i  D1 s2 ]
    求完了f,g,h的Predict值后有两种处理方式:一种是将Predict值大于3的物品推荐给Leo,另一种是将Predict值从大到小排序,将Predict值大的前2个物品推荐给Leo。这段代码没有写。
    ) C6 q' P  R1 e* l$ j, s% t' n0 y# U; g. B( z2 P" [
    从上面的示例中可以看出,推荐算法的实现非常麻烦,需要循环,判断,合并数组等等。如果处理不当,反而会成为系统的累赘。在实际处理中还有以下问题:
    ' l+ ?5 k: z6 M6 V: w; S$ w: ?) q4 Q3 O2 Q  T# T- ?9 v
    1.以上示例我们只对Leo进行推荐,而且我们已经知道Leo没有评价过f,g,h物品。如果放到实际的系统里,对于每一个需要进行推荐的用户,都要查询出他没有评价过哪些物品,这又是一部分开销。8 |7 j! F/ k6 @0 L) m* q
    ! N6 B' T9 x. a
    2.不应当进行整表查询,在实际系统中可以设定一些标准值。比如:我们求Leo与表中的其他人的Cos值,如果该值大于0.80,则表示可以为邻居。这样,当我找到10个邻居之后,就停止求Cos值,避免整表查询。对于推荐物品也可以适当采用此方法,比如,我只推荐10个物品,推荐完后就停止求Predict值。4 B, B% A. u6 r  e% g) q# X# `
    / b; ^4 Y4 \, ]! C( w0 [/ \' S$ l
    3.随着系统的使用,物品也会发生变化,今天是fgh,明天没准就是xyz了,当物品变化时,需要动态的改变数据表。
    : Y4 v7 N2 |- |8 @; N% e* f7 q. H2 O  J3 i8 I+ Z
    4.可以适当引进基于内容的推荐,来完善推荐算法。% m; P, E2 L. p! @8 q! ]
    * J4 y" @3 F7 B5 j
    5.推荐的精确性问题,这个设置不同的标准值,会影响精确性。
    2 \' U2 ~: u5 g1 i- i+ t' H* V————————————————$ p# u$ w2 Z$ P; H& k. Y1 \$ y7 J
    版权声明:本文为CSDN博主「星斗其文,赤子其人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。" t( K. I2 g9 R
    原文链接:https://blog.csdn.net/liuliuhelingdao/article/details/126715465
    , A6 r% T8 a- N7 a( O5 C3 C' D' h9 |6 q9 }$ b
    " y% ~) g8 d$ R8 Z* k8 Q  {! g
    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-5-26 06:33 , Processed in 0.530043 second(s), 51 queries .

    回顶部