QQ登录

只需要一步,快速开始

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

    0 Y7 x! ]. g: ]  b5 V6 I+ Nphp+mysql实现简单的协同过滤推荐算法; l( ^0 Q5 ]/ V7 \
    仅做标记。。。; x; \3 u8 b1 \1 L- F8 \- g

    ! Q' m! Y* B( e/ C
    6 m" E. t& o% R1 O' V
    ) n3 n2 `, L+ Y要实现协同过滤推荐算法,首先就要理解算法的核心思想和流程。该算法的核心思想可以概括为:若a,b喜欢同一系列的物品(暂时称b是a的邻居吧),则a很可能喜欢b喜欢的其他物品。算法的实现流程可以简单概括为:1.确定a有哪些邻居 2.通过邻居来预测a可能会喜欢哪种物品  3.将a可能喜欢的物品推荐给a。
    " ^# L7 m. k/ w. b% m# E  I
    + j! i( u3 S' e, F2 C算法核心的公式如下:& u8 X8 T& a  `& y0 N" l
    . ]' o# r  \0 U4 K  ^9 q  F2 V
    1.余弦相似度(求邻居):; a, Q( b: K$ n/ I! Q8 r) K3 W! e  V
    , A7 M# F$ f+ k2 V0 o9 F# t
    2.预测公式(预测a可能会喜欢哪种物品):2 B# y) h0 W0 h- H+ n

    6 f! @" {6 G- E8 Z, q仅从这两个公式我们就可以看出,仅仅是按照这两个公式进行计算,就需要进行大量的循环与判断,而且还涉及到排序的问题,就涉及到排序算法的选择与使用,这里我选快排,从网上copy了一段快排,直接用。总之实现起来很麻烦,在大数据情况下,更何谈效率。
    4 h  R2 J( U( I6 L( m
    , o1 u. z0 Y+ z' g1 N/ {首先建表:" L2 `, B" R6 l; a0 N6 r9 J  g

    ' s# J$ g5 S( L) t% [3 Q/ Y5 lDROP TABLE IF EXISTS `tb_xttj`;
    % [; D2 Q2 M, V# X; o* }" ]CREATE TABLE `tb_xttj` (% X! h7 C& P, R
      `name` varchar(255) NOT NULL,0 P& p" u0 m% I! C
      `a` int(255) default NULL,0 b6 s+ g, ]5 O
      `b` int(255) default NULL,! b1 I  X  g) o8 L; `- L
      `c` int(255) default NULL,8 t' E0 u. V& D4 C
      `d` int(255) default NULL,
    ! N# {; W4 E  E: t6 m- q9 Y  `e` int(255) default NULL,
    ) Y" {9 h& _, r$ x' g  `f` int(255) default NULL,9 |$ k0 B0 I9 v0 K
      `g` int(255) default NULL,+ i  ^% e# L3 m$ r: p* B' Q. [- A2 b
      `h` int(255) default NULL,
    2 R! y- C. @; o  PRIMARY KEY  (`name`)% ~" f5 `! @" C% V, _
    ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
    , K( {1 Z; p* N$ |0 z2 B' Q* D0 H' U+ G0 v8 s
    INSERT INTO `tb_xttj` VALUES ('John', '4', '4', '5', '4', '3', '2', '1', null);0 v% U# j! h- c7 `; U0 J
    INSERT INTO `tb_xttj` VALUES ('Mary', '3', '4', '4', '2', '5', '4', '3', null);" a, q& m# [" K$ c. ]
    INSERT INTO `tb_xttj` VALUES ('Lucy', '2', '3', null, '3', null, '3', '4', '5');, ~9 x. ^4 J: o& x1 ?
    INSERT INTO `tb_xttj` VALUES ('Tom', '3', '4', '5', null, '1', '3', '5', '4');
    9 V$ Y; p# ?; p, OINSERT INTO `tb_xttj` VALUES ('Bill', '3', '2', '1', '5', '3', '2', '1', '1');1 g$ P# z& p' I* ?% q6 \+ \. P$ G
    INSERT INTO `tb_xttj` VALUES ('Leo', '3', '4', '5', '2', '4', null, null, null);% d  ^7 w" w; W% D0 Y& \
    0 t  V* T5 a  r  S" W

    1 m6 l5 ~. L8 C6 L 我这里只对最后一行的Leo进行推荐,看看f,g,h哪个可以推荐给他。& [5 k( p  j" i  `% k7 z
    5 q4 a( M9 ~% X& W) r8 Z7 @, }( s
        用php+mysql,流程图如下:
    1 z/ }" n6 j( i6 Y4 D7 G( Q/ E6 h: K' i) E1 x% z4 V5 d% [
    连接数据库并将其存储为二维数组的代码如下:
    3 D" x8 s6 K5 v' J3 H
      @1 x* i! ~/ Z+ ~header("Content-Type:text/html;charset=utf-8");6 D0 w% j/ a! K/ T( ~, H; _
      g( Y& R+ g' l- C
    mysql_connect("localhost","root","admin");3 T+ b- v2 X/ h! A8 f
    mysql_select_db("geodatabase");
    ! D5 P# \8 u: f# gmysql_query("set names 'utf8'");       
    7 V$ A9 l" k: ]9 b6 j# p4 E. a0 k
    + I: v1 @2 e  g! ]9 V/ Y$sql = "SELECT * FROM tb_xttj";6 D$ D0 j% T0 S2 V  o7 I
    $result = mysql_query($sql);7 i, `* ~* F* o% U$ f
    * ?0 A0 b0 w3 R2 }5 C
    $array = array();/ l4 X, T- R2 Z6 V, [  G5 X
    while($row=mysql_fetch_array($result))* Z6 D3 H$ \) `, M6 D3 `
    {
    : y% n3 k4 T- r; z' q3 ^4 t        $array[]=$row;//$array[][]是一个二维数组
    0 M0 Y' z" k7 g6 o, t- i}
    9 M/ K7 ~, t' l! K; K( ^1 [- D9 Q2 U8 S  H
    问题1:这一步完全可以看做是整表查询,这种查询是大忌,对于这种小小的演示系统还可以,但是对大数据的系统,没有效率,至于如何改进,还得多学习才是。& d. ]' Z( L) L( E9 m$ w

    + \, b0 `( T+ d; r5 ^% y求Leo与其他人的Cos值代码如下:/ t# N' d: ^9 X' f/ x5 b
    4 n, P0 }9 v3 T4 h# q
    /*  E& y1 a- Q; |+ @7 [
    * 以下示例只求Leo的推荐,如此给变量命名我也是醉了;初次理解算法,先不考虑效率和逻辑的问题,主要把过程做出来
    5 `/ O- X0 }" e: P" n. F" U */
    : s5 y- I$ O+ d9 ?3 K4 G$ l
    ) _2 k/ G; n* g& V$cos = array();! e+ c; }% r+ q, ~' D; _1 v
    $cos[0] = 0;
    , `5 N+ Z; m' q9 r, d& m8 b' O$fm1 = 0;, ^  ]8 {0 E# e* v
    //开始计算cos) p7 [3 w2 ^# t1 [$ g" }0 O) F
    //计算分母1,分母1是第一个公式里面 “*”号左边的内容,分母二是右边的内容* }& Z: V9 k' B4 l4 u, g
    for($i=1;$i<9;$i++){
    ) `' O. Z* e( Y6 ]' O2 H" Z        if($array[5][$i] != null){//$array[5]代表Leo* s6 c' ~- S( q
                    $fm1 += $array[5][$i] * $array[5][$i];
    & c% c9 ^' u1 g* B& K9 A        }
    , q+ ~7 X& g; m. N% Z. u) i) g. y}
    ! x4 _3 j( v( C3 ?  G
    3 d! b* O( k  X) ^& c) T$fm1 = sqrt($fm1);% A3 @; f7 \- {/ c3 R* W

      F9 R. b" z3 W9 O. E1 O# ]for($i=0;$i<5;$i++){
    3 Y5 t6 H7 O0 K/ X3 ^. d- Q. T  ]        $fz = 0;
    : n/ _& N7 d9 S1 L2 p! {        $fm2 = 0;
    . K  M2 v# j( }$ e7 r( \        echo "Cos(".$array[5][0].",".$array[$i][0].")=";
    / Z8 K. D: j& }& j" ~5 }/ s       
      h# D$ V: K5 @& E4 \  H        for($j=1;$j<9;$j++){
      u  o$ h: e, U3 j            //计算分子( h, l( Y; b! M- q/ f
                    if($array[5][$j] != null && $array[$i][$j] != null){
    , n9 y% `# @; g& [) O. f" E                        $fz += $array[5][$j] * $array[$i][$j];
    4 N* `! h( S+ C3 W) F& O                }
    + u7 Y' ]5 m/ Q* ?5 t0 M; V& d                //计算分母2
    3 c' }( F& R7 v5 ]                if($array[$i][$j] != null){
    - Y  B" g' e& a. n" J! }                        $fm2 += $array[$i][$j] * $array[$i][$j];0 \0 O" s' G+ r/ }9 i; G
                    }                        5 z1 F+ y1 p- C; }) y
            }
    - r! V9 G* x4 L( Z5 F& r        $fm2 = sqrt($fm2);# \+ z+ i& S+ u& }3 `  I
            $cos[$i] = $fz/$fm1/$fm2;$ d5 Y* l& I/ ~
            echo $cos[$i]."<br/>";
    7 ~8 b. |6 Y" P% d1 _. p* `}( h7 i3 h, ?6 c) Q2 W
    1 h2 B9 v4 n  ?* ~, c/ p
    这一步得到的结果是酱紫:5 @; S# f7 c9 h3 t$ m- r

    . Q2 V- R4 p" I- O! z  s将求好的Cos值排序,采用快排代码如下(百度copy而来):
    0 C4 A6 V4 U9 y7 ~
    + e. ]$ S" g$ _$ C  h% |1 O- X9 X7 c8 a+ ~* {
    //对计算结果进行排序,凑合用快排吧先; B1 R% _* Q7 ]' m
    function quicksort($str){
    : \- S2 P  A4 m6 k/ S% {        if(count($str)<=1) return $str;//如果个数不大于一,直接返回
    ; C! I8 v+ t3 [1 u1 ?. D        $key=$str[0];//取一个值,稍后用来比较;
    # N- M, x8 m3 K( E4 E0 T7 ]# ]1 u+ `        $left_arr=array();
    : [: u( c' w! F        $right_arr=array();$ @" F' @+ s( ^! g. ^' V) Y
           
    # }/ D1 H( v2 G  d: d% _        for($i=1;$i<count($str);$i++){//比$key大的放在右边,小的放在左边;
    * K2 R3 ]* D- Z& i1 o; W                if($str[$i]>=$key); x# U3 R3 S4 ]! q. B: E; p
                    $left_arr[]=$str[$i];1 n7 U: Q2 l1 D- i$ l! d9 a( P/ W
                    else
    - j6 G8 x! X! \6 ?; s; o' L% E                $right_arr[]=$str[$i];  k/ @5 s; m, R, Z7 Z3 W* n0 F
            }* I5 ^' n8 ?9 V" X
            $left_arr=quicksort($left_arr);//进行递归;
    % E4 m- Y2 p& u  m        $right_arr=quicksort($right_arr);
    & |/ d4 B$ O; _2 R0 ^8 o' i" Z        return array_merge($left_arr,array($key),$right_arr);//将左中右的值合并成一个数组;: i5 ]5 b! y/ w- U% F
    }
    ! }6 N, a0 t- j8 c1 G
    5 e- U, b6 G. Q) W" [$neighbour = array();//$neighbour只是对cos值进行排序并存储
    " J2 M' _9 T( b/ B  R" q& @$neighbour = quicksort($cos);
      @- y9 L6 m6 s5 }1 z$ {" u& n# n5 \, m# ^: ]2 P+ C

    5 ]: o4 T! t1 r- N- _这里的$neighbour数组仅仅存储了从大到小排序好的Cos值,并没有与人联系起来。这个问题还要解决。
    + D% x4 ^( M- t' v+ l
    : m% S1 X, ^* N$ U3 e) l$ P选出Cos值最高的3个人,作为Leo的邻居:
    + g' S. ~& e* g0 a$ b' E+ E
    2 I3 h( ?6 y& o, _0 @, i) a6 h8 J$ w//$neighbour_set 存储最近邻的人和cos值
    ; _, i9 g# B# C4 k# A- U5 K$neighbour_set = array();; I' |. ^8 q2 e6 v9 _
    for($i=0;$i<3;$i++){
    . D- j9 b) U0 p& E- P9 @3 O        for($j=0;$j<5;$j++){
    3 X4 @2 |7 c$ G* E1 s2 O                if($neighbour[$i] == $cos[$j]){
    8 A9 a& s/ x" F8 {3 ?$ B. n                        $neighbour_set[$i][0] = $j;
    $ U: `- V* B5 T% A% Y. T                        $neighbour_set[$i][1] = $cos[$j];/ d; p: _4 ^' Z3 B% s0 Z# V
                            $neighbour_set[$i][2] = $array[$j][6];//邻居对f的评分4 g# |, b! w2 s4 N3 t+ R% @5 t
                            $neighbour_set[$i][3] = $array[$j][7];//邻居对g的评分
    - F2 w! a6 b* Y9 Y* J  o                        $neighbour_set[$i][4] = $array[$j][8];//邻居对h的评分& y% ?( k8 o5 V9 \  B- j
                    }- _9 E, [. Q5 R( g8 @, {
            }
    / u- B  C$ J0 Z8 g}
    / ~5 x- [! S1 p* J2 xprint_r($neighbour_set);
      K" N3 \. I2 Z, X( m* h* Qecho "<p><br/>";! k. e5 b8 H; T. B. `

    $ x; t) g0 J' s. u) F! _1 g1 ^$ F) a这一步得到的结果是酱紫:+ ~& }8 q4 u- f: d4 d3 Q* O1 ~

    9 O, _6 `# H+ P% O+ ?- r& _' O
    & b- Z$ X" j6 ^* ^4 m( X2 m5 h3 i
    转存失败重新上传取消
    2 L' G* I5 q# I: |: _5 C6 ^
    ; `* n7 B$ x4 X7 p" P这是一个二维数组,数组第一层的下标为0,1,2,代表3个人。第二层下标0代表邻居在数据表中的顺序,比如Jhon是表中的第0个人;下标1代表Leo和邻居的Cos值;下标2,3,4分别代表邻居对f,g,h的评分。* a+ Y( @: G+ R% p

    0 q+ j* z8 [3 D; [# ?& n7 }! y开始进行预测,计算Predict代码如下:
    6 X( _; j; r4 u- A: H
    6 g& L  [/ u# n1 @. N" H% e' p我是分别计算Leo对f,g,h的预测值。在此有一个问题,就是如果有的邻居对f,g,h的评分为空,那么该如何处理。比如Jhon和Mary对h的评分就为空。本能的想到用if判断一下,如果为空则跳过这组计算,不过这样处理是否合理,有待考虑。以下代码并没有写出这个if判断。8 n7 {7 M/ y! \8 B( b
    ( V; p( Z4 `  {8 \$ D0 z
    //计算Leo对f的评分, n/ X" L. F' ?! p
    $p_arr = array();
    ! F3 R# R- O2 n# [5 `/ J$pfz_f = 0;3 p- i7 R4 t/ ?0 Z# g: [
    $pfm_f = 0;
    / V  E0 r# n0 q) a4 T! b9 F) Hfor($i=0;$i<3;$i++){* G) H2 Y4 m1 s$ ?  |
            $pfz_f += $neighbour_set[$i][1] * $neighbour_set[$i][2];$ m  E# o+ ^9 {( S# C( [" l1 T
            $pfm_f += $neighbour_set[$i][1];
    ' G) j, A2 y' _( [}
    - t* l2 v& x$ T- b: c) R$p_arr[0][0] = 6;
    : a. g7 L7 K6 V, T: x$p_arr[0][1] = $pfz_f/sqrt($pfm_f);
    - I" F" }% s" n& K4 F( Xif($p_arr[0][1]>3){$ b0 }' h# \" r* _( Y# ^
            echo "推荐f";
    4 \: ?; w) z/ \( g5 H3 _9 K}1 ^. S- {5 i2 r3 j! ~
    , i4 Q! ^* y4 s* f2 d
    //计算Leo对g的评分4 u6 Z) n0 d; h! u
    $pfz_g = 0;
    ; W1 d) A% d9 a; q$pfm_g = 0;
    " i6 x# s  i& y0 s( y! t, X6 dfor($i=0;$i<3;$i++){: p2 [7 E+ @5 Z$ a/ q
            $pfz_g += $neighbour_set[$i][1] * $neighbour_set[$i][3];
    / w# E3 l& h$ J, T        $pfm_g += $neighbour_set[$i][1];% c2 ], L7 b, x+ y, _( C4 L" B
            $p_arr[1][0] = 7;
    5 ^$ R0 t/ @& J3 C        $p_arr[1][1] = $pfz_g/sqrt($pfm_g);5 U; ]: k+ Y1 X5 x
    }2 i& m  ^& N+ D3 C7 C* [; w2 G
    if($p_arr[0][1]>3){2 N0 G# ]4 ^: E9 k0 E% ?' x' Q- P( \
            echo "推荐g";
    : E$ j! [  e3 _}
    , n8 B* P$ h; S# Q5 h" v* T. z  C
    //计算Leo对h的评分
    % y! w+ {1 U) ~1 \. ?/ b4 i$pfz_h = 0;+ o- y% ~% v# A5 T- _
    $pfm_h = 0;0 n' L! E) |- Y! W
    for($i=0;$i<3;$i++){" V$ X- k; e' Y0 W+ D  [1 x
            $pfz_h += $neighbour_set[$i][1] * $neighbour_set[$i][4];8 W5 u. ?% c4 t8 r* t* U
            $pfm_h += $neighbour_set[$i][1];
    1 J# T' g3 c3 V0 P        $p_arr[2][0] = 8;9 m) }0 ^' E" p$ [3 m' ]& }6 S
            $p_arr[2][1] = $pfz_h/sqrt($pfm_h);" ?4 ^4 H9 X; h& e  ?
    }# }( O. I' g# ~+ `+ }5 S* E7 V
    print_r($p_arr);+ i9 i7 v" k$ Y/ I1 X2 e
    if($p_arr[0][1]>3){
    & O* P& e& p; T( b% E        echo "推荐h";: `. |4 P$ i& Y+ C
    }
    ; |  w2 d$ V, s6 S& A
    : t" Z7 i4 C1 J4 s( N$p_arr是对Leo的推荐数组,其内容类似如下;
    + |, @9 b6 L# n. U" [1 g! f- F  r" M
    Array ( [0] => Array ( [0] => 6 [1] => 4.2314002228795 ) [1] => Array ( [0] => 7 [1] => 2.6511380196197 ) [2] => Array ( [0] => 8 [1] => 0.45287424581774 ) )
    4 r* Z$ z) u8 T9 }6 Y
    ; t) _1 v% c9 A6 O: o% G) Yf是第6列,Predict值是4.23,g是第七列,Predict值是2.65........# G0 E  J# ]- o7 r
    $ o4 f6 S# n2 h/ n% Z, h# _
    求完了f,g,h的Predict值后有两种处理方式:一种是将Predict值大于3的物品推荐给Leo,另一种是将Predict值从大到小排序,将Predict值大的前2个物品推荐给Leo。这段代码没有写。! F2 D" E% x/ v; l9 Y
    9 J8 q* U) X8 `8 }7 F$ G" p& |3 r
    从上面的示例中可以看出,推荐算法的实现非常麻烦,需要循环,判断,合并数组等等。如果处理不当,反而会成为系统的累赘。在实际处理中还有以下问题:- F0 ?6 d5 v8 f0 f5 b  F0 z
    + s9 P+ x- q- M2 b
    1.以上示例我们只对Leo进行推荐,而且我们已经知道Leo没有评价过f,g,h物品。如果放到实际的系统里,对于每一个需要进行推荐的用户,都要查询出他没有评价过哪些物品,这又是一部分开销。- B9 x4 _4 P& G) n4 C6 Z

    % ^! Z1 s2 a+ z1 F2.不应当进行整表查询,在实际系统中可以设定一些标准值。比如:我们求Leo与表中的其他人的Cos值,如果该值大于0.80,则表示可以为邻居。这样,当我找到10个邻居之后,就停止求Cos值,避免整表查询。对于推荐物品也可以适当采用此方法,比如,我只推荐10个物品,推荐完后就停止求Predict值。8 r' }: s) y! f3 p) r; ]: i
    . b6 m4 L& T" z0 r
    3.随着系统的使用,物品也会发生变化,今天是fgh,明天没准就是xyz了,当物品变化时,需要动态的改变数据表。4 l2 R6 w1 R6 o( A6 n

    8 T" _0 t8 C4 `0 S) R$ a3 q! |4.可以适当引进基于内容的推荐,来完善推荐算法。( `9 C( ]; W) }0 k
    % z, Z* p3 ^5 k6 \4 W
    5.推荐的精确性问题,这个设置不同的标准值,会影响精确性。; n  S) C4 P& m6 X; d" W
    ————————————————  I5 w$ L3 R0 x, D" }' H4 b
    版权声明:本文为CSDN博主「星斗其文,赤子其人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。9 K. ]9 P! k' \: E9 D! N
    原文链接:https://blog.csdn.net/liuliuhelingdao/article/details/1267154650 b- D0 O* C7 h/ u6 _0 }5 y' ~

    1 u8 W2 @/ h9 x- ?( X
    : G# c' r$ E0 g4 e3 r" X1 [
    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-7-28 19:16 , Processed in 0.345537 second(s), 51 queries .

    回顶部