QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1923|回复: 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
      a  F3 C( p" e
    php+mysql实现简单的协同过滤推荐算法8 M5 Z* P/ D5 m: X, C
    仅做标记。。。
    ' r* W( b0 Y0 P; b7 w* [; I5 O/ a  O# B( a; ?% b! U) Y

    / _+ H6 h  h+ ^9 o, w
    % k  i  [# N( F. V要实现协同过滤推荐算法,首先就要理解算法的核心思想和流程。该算法的核心思想可以概括为:若a,b喜欢同一系列的物品(暂时称b是a的邻居吧),则a很可能喜欢b喜欢的其他物品。算法的实现流程可以简单概括为:1.确定a有哪些邻居 2.通过邻居来预测a可能会喜欢哪种物品  3.将a可能喜欢的物品推荐给a。
    ; |3 o7 @- J+ ~6 P2 l8 y" W! c( z. @/ F- n- u: W. X7 E0 n9 L
    算法核心的公式如下:
    # J/ z0 G& M) b% I2 E9 X- Z6 ^. i1 H8 V2 i/ K4 C' @5 q
    1.余弦相似度(求邻居):
    & h2 \1 w# @: j9 f2 a7 B/ P/ n  n4 v; N" h3 v
    2.预测公式(预测a可能会喜欢哪种物品):8 M% F' z( a9 ~, N9 O8 s

    . q% X+ d1 o% J) M仅从这两个公式我们就可以看出,仅仅是按照这两个公式进行计算,就需要进行大量的循环与判断,而且还涉及到排序的问题,就涉及到排序算法的选择与使用,这里我选快排,从网上copy了一段快排,直接用。总之实现起来很麻烦,在大数据情况下,更何谈效率。8 I* f7 v$ j: q3 I4 i
    7 p% X# o' }: k0 I- ^
    首先建表:
    7 m0 \, r: T0 @
    1 \) e- c0 S1 L) R; B# rDROP TABLE IF EXISTS `tb_xttj`;+ t/ A& Z# c2 W+ s3 D
    CREATE TABLE `tb_xttj` (
    3 P5 `$ e: ]7 e# [6 ]# q3 Q  `name` varchar(255) NOT NULL,: X8 Z; f, v; c1 S& [/ |/ P, m
      `a` int(255) default NULL,
    7 a/ r6 X6 {; Q' E+ `, V) a) {/ \  `b` int(255) default NULL,
    . R: ?7 J- W% |" O0 P. W  `c` int(255) default NULL,
    # J' m# H, \- M" N4 g8 T  `d` int(255) default NULL,0 f$ B/ g& h) J& q) K
      `e` int(255) default NULL,2 G% f* l  ~$ a" {- J6 W
      `f` int(255) default NULL,4 i1 F, D8 Y1 x% ^
      `g` int(255) default NULL,, E2 l9 \0 L& H; W( D* W
      `h` int(255) default NULL,5 e$ Q/ h5 p$ U' W. Q
      PRIMARY KEY  (`name`)
    & h" z8 C; D7 j6 w2 ?) ENGINE=MyISAM DEFAULT CHARSET=latin1;. ]" w* A) q, e2 [$ g3 J! p

    5 h. A0 n3 @( e4 C# T& {* aINSERT INTO `tb_xttj` VALUES ('John', '4', '4', '5', '4', '3', '2', '1', null);  |2 B8 _- g: r6 q/ P: O
    INSERT INTO `tb_xttj` VALUES ('Mary', '3', '4', '4', '2', '5', '4', '3', null);
    * q7 o& F0 `6 m3 B7 i6 \  k0 F' O: ]INSERT INTO `tb_xttj` VALUES ('Lucy', '2', '3', null, '3', null, '3', '4', '5');
    7 p& h3 H% S6 c" X. t5 [6 q# y  [) X8 eINSERT INTO `tb_xttj` VALUES ('Tom', '3', '4', '5', null, '1', '3', '5', '4');
    6 N$ u* _2 h- ~5 n9 AINSERT INTO `tb_xttj` VALUES ('Bill', '3', '2', '1', '5', '3', '2', '1', '1');8 {7 j& O6 y+ r3 }1 o+ K
    INSERT INTO `tb_xttj` VALUES ('Leo', '3', '4', '5', '2', '4', null, null, null);, d1 a/ p- H1 p" o

    3 `  j# O* R9 s0 ~5 _$ C! T0 A- h( s0 F. A5 `9 T  t  H0 {2 S( Y
    我这里只对最后一行的Leo进行推荐,看看f,g,h哪个可以推荐给他。* }* c$ g* M! T
    1 S  P) j* z2 Z% D4 S# {" W: v( V
        用php+mysql,流程图如下:
    0 n: g1 F  y# k/ |* `
    3 k6 G8 L7 l7 t# a/ I连接数据库并将其存储为二维数组的代码如下:
    # B1 `# Z& U3 L) p& ]: H" s# S% \4 u- o  ~! x3 t0 a( @
    header("Content-Type:text/html;charset=utf-8");
    0 ]4 D  a- v0 [5 n$ r$ h$ A2 q+ t7 {* q# M5 S2 O! |6 ?
    mysql_connect("localhost","root","admin");
    # t8 O/ w1 L+ L( Wmysql_select_db("geodatabase");3 J* q5 s# {7 Z" ^7 L5 l
    mysql_query("set names 'utf8'");        ! F* @8 {  J& d3 N1 z7 K
    % J7 I. O0 l6 K7 S, J) ]( T) V0 l- R
    $sql = "SELECT * FROM tb_xttj";5 j' F* R5 s: u; [4 N, Y# C! M
    $result = mysql_query($sql);
    " J7 ?5 {8 |# V5 N, M) N7 p
    6 P0 {2 P6 v$ j, k& }9 f5 q$array = array();
    $ z) A' D1 D8 swhile($row=mysql_fetch_array($result))8 k% o, Y, G. g0 _- O
    {
    & g: N2 y% r* j& a% o& W( W7 d# c% f. ]" \        $array[]=$row;//$array[][]是一个二维数组* L' Q/ H( M+ _7 ^
    }
    # k5 [# Q7 {( I" z7 A4 Y. b" A
    & Z% p/ \7 X* t' F问题1:这一步完全可以看做是整表查询,这种查询是大忌,对于这种小小的演示系统还可以,但是对大数据的系统,没有效率,至于如何改进,还得多学习才是。
    $ v$ g5 w; v7 u8 u
    1 t/ W9 t" G+ R2 r1 \( L) D  D3 T9 L求Leo与其他人的Cos值代码如下:) F0 E! D7 N( u$ X) |7 N
    : M* @7 M2 ?5 t6 D9 \$ q
    /*
    ) z1 c5 j/ F) \& [1 D7 ~ * 以下示例只求Leo的推荐,如此给变量命名我也是醉了;初次理解算法,先不考虑效率和逻辑的问题,主要把过程做出来
      I+ Y; f. f& F# n */
    " K# X5 E1 f" {  V; e) j+ ~; `( u; \9 ~4 i8 X
    $cos = array();0 i- o, R4 T+ M
    $cos[0] = 0;* r2 K* n  Q0 @& _% ?
    $fm1 = 0;
    ' N( ?5 a' ^! S4 V& T9 O% Q& n2 c//开始计算cos
    2 i5 F" {" U8 Z//计算分母1,分母1是第一个公式里面 “*”号左边的内容,分母二是右边的内容
    ( w% F6 N( m6 d- Y9 D7 ?; |6 Ufor($i=1;$i<9;$i++){
    , V; @# t0 P, q- T* S5 v# G3 v/ P! W        if($array[5][$i] != null){//$array[5]代表Leo
    ; k7 \4 f; i/ K8 {: M( e                $fm1 += $array[5][$i] * $array[5][$i];  X% f+ \! ~" _7 }' o2 q0 Y
            }5 |; i* Y9 U5 F0 H1 U
    }
    . K+ G. j' v/ M( ?% ~9 N/ N
    1 R# q0 U2 b  p- d$fm1 = sqrt($fm1);! q. M( S$ h7 |1 r: c) O. d, R

    : q* {' o3 C/ U7 A* G) ~for($i=0;$i<5;$i++){
    # h) P2 c# H  t2 p/ L- W- H        $fz = 0;
    * p$ r  ?" S. m0 ~  z8 A: _* [* H. D        $fm2 = 0;+ w/ E% ]! d- w+ v8 x# @4 B
            echo "Cos(".$array[5][0].",".$array[$i][0].")=";
    7 Z7 D) @3 m/ u3 s" r; {9 t       
    $ j% E5 ?6 g' _/ M: S( f        for($j=1;$j<9;$j++){* h* q" k; j& _8 t
                //计算分子! \6 q' a( F  A7 R! [3 L, b
                    if($array[5][$j] != null && $array[$i][$j] != null){. A1 i1 e0 u2 G
                            $fz += $array[5][$j] * $array[$i][$j];
    7 Q  K! x3 `# V4 U+ @# u: u                }
    1 L$ @# O6 w3 l: W                //计算分母2
    , [% B4 O/ r+ @1 m7 M( [                if($array[$i][$j] != null){
    1 K' V3 E. ^4 v6 |% J, c                        $fm2 += $array[$i][$j] * $array[$i][$j];
    3 ]5 X! _* @! H! L% i6 _                }                        , r( F0 `" Q! \1 N( q
            }8 B+ ?' c: s$ q! P3 z
            $fm2 = sqrt($fm2);
    / y* k# j& B9 \0 d- X* ]        $cos[$i] = $fz/$fm1/$fm2;
    # w/ D. X" G: ?        echo $cos[$i]."<br/>";/ _" X* U" j5 P- ~: T
    }4 @; `3 `6 y9 ~( w2 W
    # `1 G+ C, p+ Y" ^) M
    这一步得到的结果是酱紫:) V( b5 c( I; \5 Q# V+ L
    $ }# A0 d6 {0 S/ c) v0 X9 ^
    将求好的Cos值排序,采用快排代码如下(百度copy而来):
      ]9 H, M! k( k6 H: F9 m$ `6 M6 W; p9 Y3 w" w3 w* R
    % J  q+ W0 c+ |  y9 X1 g* D
    //对计算结果进行排序,凑合用快排吧先( F9 |% M0 R/ n& o- ?
    function quicksort($str){
    ; w. |& n$ H2 ]; Y1 d8 P) V- [        if(count($str)<=1) return $str;//如果个数不大于一,直接返回! h9 A1 ]& r: w5 p8 D& K( p
            $key=$str[0];//取一个值,稍后用来比较;3 d1 M2 M+ }. W* @& [( V/ }0 y6 ~
            $left_arr=array();
    0 h9 j* r" y( r) Y7 k        $right_arr=array();
    * a5 ~6 ~& K) l; w; ?  `        3 E/ w" r$ l% B) D/ Z% j
            for($i=1;$i<count($str);$i++){//比$key大的放在右边,小的放在左边;
    " i2 b9 l) i( @                if($str[$i]>=$key)! |, k' G; B/ o
                    $left_arr[]=$str[$i];
    8 k. \8 _, C0 a                else
    $ y0 \  A7 @# Y3 I% o: v! Y+ g5 O& d                $right_arr[]=$str[$i];
    , l5 T! e2 s- [. m1 q' t% J        }
    5 C" @. g- ?2 }8 [1 z& C        $left_arr=quicksort($left_arr);//进行递归;( K! G! r$ f' Y3 l3 y# I+ [+ \
            $right_arr=quicksort($right_arr);! g) K$ E( `$ c
            return array_merge($left_arr,array($key),$right_arr);//将左中右的值合并成一个数组;9 I4 A3 M: o2 p/ \
    }: |2 I  M7 o- w) ?8 j# N
      X' P3 _# t& G- w7 x
    $neighbour = array();//$neighbour只是对cos值进行排序并存储: E5 K! C) k0 u; y8 H; I0 L& e
    $neighbour = quicksort($cos);
    ( }" a3 s3 q1 m6 I! {$ |
    : l3 }7 N1 V+ y* a" ~7 g: R) m8 {' L* Z( `, q) @! T
    这里的$neighbour数组仅仅存储了从大到小排序好的Cos值,并没有与人联系起来。这个问题还要解决。
    2 j6 Q, M5 ^, x. U7 ^" H- |5 n& p1 H! |4 j$ e! C
    选出Cos值最高的3个人,作为Leo的邻居:& p8 c" K5 A& s- p$ f3 i) u

    ( J, h% W% p% |4 Q. N//$neighbour_set 存储最近邻的人和cos值) g$ [; z3 `3 d" Y
    $neighbour_set = array();
    7 B& e) J* n# ?& Y, U# }0 H. ?for($i=0;$i<3;$i++){
    ) U" q% B+ J* A5 K/ j) K) S        for($j=0;$j<5;$j++){
    . k; A/ G" T5 n' j. z) ^                if($neighbour[$i] == $cos[$j]){
    ) P" c, j, h1 O                        $neighbour_set[$i][0] = $j;
      k) P& E* B( ^7 c( B. }                        $neighbour_set[$i][1] = $cos[$j];
    1 i% B' J: g9 y' O                        $neighbour_set[$i][2] = $array[$j][6];//邻居对f的评分
    : q6 _+ _6 k3 Y+ O( V                        $neighbour_set[$i][3] = $array[$j][7];//邻居对g的评分) n/ I: o2 ~" h! e2 M& p, @
                            $neighbour_set[$i][4] = $array[$j][8];//邻居对h的评分# u. b  y1 q4 s2 c
                    }
    7 T4 u) R9 h- b        }( X% O! h1 `- M+ c3 @* R
    }
    , Y) L, K+ x* W: {6 x7 Y& {print_r($neighbour_set);: \  w8 v7 |6 _5 L
    echo "<p><br/>";
    ( _3 D0 N/ q9 t; H
    3 s& W6 T  o& ^. y这一步得到的结果是酱紫:/ x& o, d, e# \

    ; ]- q. A& N: }( S- ~! L6 p8 q8 p+ t$ H3 N2 w

    0 C9 K8 y. {; c  I( N* l' R/ a转存失败重新上传取消3 y$ p2 ~1 O& x4 R
    , `+ d$ O9 Y: R9 q& T' H% w" t2 }
    这是一个二维数组,数组第一层的下标为0,1,2,代表3个人。第二层下标0代表邻居在数据表中的顺序,比如Jhon是表中的第0个人;下标1代表Leo和邻居的Cos值;下标2,3,4分别代表邻居对f,g,h的评分。
    ) k) t! a% ]4 c, ~( l4 Q8 h" {" k
    开始进行预测,计算Predict代码如下:
    & s- g  B- v  F7 k/ f
    - e6 H) `" D! J) j5 C# q- D" G& L& w7 V我是分别计算Leo对f,g,h的预测值。在此有一个问题,就是如果有的邻居对f,g,h的评分为空,那么该如何处理。比如Jhon和Mary对h的评分就为空。本能的想到用if判断一下,如果为空则跳过这组计算,不过这样处理是否合理,有待考虑。以下代码并没有写出这个if判断。3 `% @; C- r* e) C( f; h) H8 R/ z

    . a6 }1 c: Y2 T) ?3 I7 X- B" D( Y//计算Leo对f的评分
    1 i# Q& X+ [) q. X$p_arr = array();* e( {* v5 |; N8 P# @& P
    $pfz_f = 0;/ ^6 Y" i# Q( O
    $pfm_f = 0;
    0 j# h9 a5 M8 A8 a0 lfor($i=0;$i<3;$i++){4 z9 p% u! q, x, K4 l. T: `
            $pfz_f += $neighbour_set[$i][1] * $neighbour_set[$i][2];) E* k7 p3 a: p7 p
            $pfm_f += $neighbour_set[$i][1];" y; N# f; S  M+ I( @/ R8 l
    }3 K/ Z( e  m  S5 k, e! U, T2 k: U
    $p_arr[0][0] = 6;8 c6 s2 I3 Q0 c
    $p_arr[0][1] = $pfz_f/sqrt($pfm_f);
    1 X" U% S* _- `- i! ^+ iif($p_arr[0][1]>3){
    0 c/ M) j- s" F" p        echo "推荐f";9 c$ J# f9 p5 k( ]
    }4 m% ?( W/ |$ _; b5 j

    + q  S: i) T" s, U# n; Y//计算Leo对g的评分; ?& ?8 t6 E% {
    $pfz_g = 0;( _3 L% ^6 n0 c$ G5 J; h4 ?
    $pfm_g = 0;
    ) ~( M3 J  k, _) efor($i=0;$i<3;$i++){+ b3 M/ J" X- c1 p1 R
            $pfz_g += $neighbour_set[$i][1] * $neighbour_set[$i][3];7 @0 }) O6 r( m
            $pfm_g += $neighbour_set[$i][1];
    8 M* x+ p7 X% ^* ^0 @$ C( F        $p_arr[1][0] = 7;9 [& E4 C" G3 {! t: f
            $p_arr[1][1] = $pfz_g/sqrt($pfm_g);
    4 s8 Y' [1 ]. E, ]+ O}$ W! N4 B( I; r# k7 C6 A7 r
    if($p_arr[0][1]>3){7 K  D+ W3 Y" u& _. O
            echo "推荐g";' C" s# Y, {9 X7 m9 G( G
    }6 R: z6 T% K/ q' T$ h
    * `; M. `9 @$ A1 F5 M
    //计算Leo对h的评分
    ! R( N1 j) e6 E( s2 X" R$pfz_h = 0;
    5 E. \# u# S" g9 @9 K$pfm_h = 0;
    ) D# F6 S3 z' Q2 L5 x# J8 |for($i=0;$i<3;$i++){# b, Y0 L$ J) Q& I  y/ a- P* @
            $pfz_h += $neighbour_set[$i][1] * $neighbour_set[$i][4];
    $ H& n  |4 d# g        $pfm_h += $neighbour_set[$i][1];
    ' ?! I  `3 G. J+ Z  p, N        $p_arr[2][0] = 8;, G: P2 H/ n6 \: k' y0 k
            $p_arr[2][1] = $pfz_h/sqrt($pfm_h);  `% W8 P/ B. M+ q0 `4 r* f
    }
    + X; L) U# B' m# q) I* ~  Rprint_r($p_arr);( W0 o0 |7 M; l- y* a2 X2 f7 E
    if($p_arr[0][1]>3){$ P& i# m( z2 y  B
            echo "推荐h";
    0 T' |. \" d/ Y8 \) K}
      f7 S! j+ k' m8 a( F: R" k% `% W1 P7 X
    $p_arr是对Leo的推荐数组,其内容类似如下;7 K$ a/ g! U+ k) C$ z# @: {
      C, p. x9 e. w9 D4 `$ F
    Array ( [0] => Array ( [0] => 6 [1] => 4.2314002228795 ) [1] => Array ( [0] => 7 [1] => 2.6511380196197 ) [2] => Array ( [0] => 8 [1] => 0.45287424581774 ) )0 m" x$ X& W% B
    ' S/ L6 |5 B- h2 n
    f是第6列,Predict值是4.23,g是第七列,Predict值是2.65........
    % Z. M- x& e: m; J4 P  Q2 G& Q+ M" Y, {$ l- I
    求完了f,g,h的Predict值后有两种处理方式:一种是将Predict值大于3的物品推荐给Leo,另一种是将Predict值从大到小排序,将Predict值大的前2个物品推荐给Leo。这段代码没有写。
    : J- g9 T6 c1 a0 Q; `' B( m  H) o# _5 U2 I
    从上面的示例中可以看出,推荐算法的实现非常麻烦,需要循环,判断,合并数组等等。如果处理不当,反而会成为系统的累赘。在实际处理中还有以下问题:( n) f2 v4 E9 V2 a3 U* z

    " |8 P$ e# N4 _- p5 `( i3 }1.以上示例我们只对Leo进行推荐,而且我们已经知道Leo没有评价过f,g,h物品。如果放到实际的系统里,对于每一个需要进行推荐的用户,都要查询出他没有评价过哪些物品,这又是一部分开销。, c  l% x+ m; Q! k; I

    & i. P" a. D, n2.不应当进行整表查询,在实际系统中可以设定一些标准值。比如:我们求Leo与表中的其他人的Cos值,如果该值大于0.80,则表示可以为邻居。这样,当我找到10个邻居之后,就停止求Cos值,避免整表查询。对于推荐物品也可以适当采用此方法,比如,我只推荐10个物品,推荐完后就停止求Predict值。
    / {$ w3 W" \) v$ Y: V; V. r. O0 f. W' z; \0 n
    3.随着系统的使用,物品也会发生变化,今天是fgh,明天没准就是xyz了,当物品变化时,需要动态的改变数据表。
    , z0 @2 s! v2 B" a0 E) h/ D2 U2 W
    9 p. E5 h5 d' O& F/ }4.可以适当引进基于内容的推荐,来完善推荐算法。1 s' c* S" D1 W; N! {! s
      e5 M9 y$ E7 R/ f8 ^, h
    5.推荐的精确性问题,这个设置不同的标准值,会影响精确性。* D, M. r$ i1 w8 j
    ————————————————0 x; M6 f2 ^. }
    版权声明:本文为CSDN博主「星斗其文,赤子其人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    3 U. K% }7 E, U原文链接:https://blog.csdn.net/liuliuhelingdao/article/details/1267154658 n$ @- M7 P8 i, e- U/ W0 W# u) o
    # T  Y& }# {6 J0 l" ?( ?) s6 b: Y
    0 z: J1 b+ V8 \7 c8 ^, i( r2 a2 m3 ^
    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-6-15 10:45 , Processed in 0.392103 second(s), 51 queries .

    回顶部