QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1991|回复: 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
    + I, b4 N* r& t. a& d
    php+mysql实现简单的协同过滤推荐算法
    ' A' U/ E, }6 ^" W8 C  J4 c仅做标记。。。
    * g0 G* i6 V2 C- a$ Z- _6 K: a  T# v8 i4 S. g2 I, a

    $ C; G" l1 X. p! `7 J, Z* M  \# j( L& k# G. m4 Q) U- Y# ?+ u2 O
    要实现协同过滤推荐算法,首先就要理解算法的核心思想和流程。该算法的核心思想可以概括为:若a,b喜欢同一系列的物品(暂时称b是a的邻居吧),则a很可能喜欢b喜欢的其他物品。算法的实现流程可以简单概括为:1.确定a有哪些邻居 2.通过邻居来预测a可能会喜欢哪种物品  3.将a可能喜欢的物品推荐给a。
    1 Y7 e4 q) E  J* F4 E1 \3 @2 q. F
    算法核心的公式如下:! G& O- P; L1 Z: S$ V  i$ n6 Y

    $ m6 o2 ]3 Y! i$ t% A7 r1.余弦相似度(求邻居):0 u# i& T' D0 Q5 V/ b, W0 m' B

    5 U) b: I+ E$ m; n6 l2.预测公式(预测a可能会喜欢哪种物品):( y0 w+ `8 y$ {2 V* {) Q2 ~, h9 ]
    6 V' k+ H3 a% u  N8 x. G- n
    仅从这两个公式我们就可以看出,仅仅是按照这两个公式进行计算,就需要进行大量的循环与判断,而且还涉及到排序的问题,就涉及到排序算法的选择与使用,这里我选快排,从网上copy了一段快排,直接用。总之实现起来很麻烦,在大数据情况下,更何谈效率。7 X! J' L' A3 o* M* p/ K

    ) ^( u  t% ]5 }首先建表:$ t1 J& B+ _  |$ w2 T; B# j5 W' b

    - ?. b! V4 f- {/ v( C, cDROP TABLE IF EXISTS `tb_xttj`;
    ) u8 y2 k, ?* Q  x1 R2 q. pCREATE TABLE `tb_xttj` (
    , s" J1 N+ z* R6 a  `name` varchar(255) NOT NULL,. \$ N! y; P, Q6 x
      `a` int(255) default NULL,( a/ q# r3 a# n* u3 A7 t
      `b` int(255) default NULL,8 Z4 Z' C7 K. @1 `2 ]
      `c` int(255) default NULL,6 t+ v9 a. N) U3 T
      `d` int(255) default NULL,
    . @+ y, K6 ~1 E! b: Q! D  `e` int(255) default NULL,
    1 c+ Q* o- W5 u/ L4 w  `f` int(255) default NULL,- e" J# P. t: {9 R& ]
      `g` int(255) default NULL,
      f" ^; P* c4 K6 W1 [$ t  `h` int(255) default NULL,6 Q. S: b1 F9 W
      PRIMARY KEY  (`name`)0 U8 s5 Q) B; W) k, |
    ) ENGINE=MyISAM DEFAULT CHARSET=latin1;% t& r7 Z; d3 K# h

    9 _$ ]$ ?( W' X' l8 wINSERT INTO `tb_xttj` VALUES ('John', '4', '4', '5', '4', '3', '2', '1', null);2 [! f8 v( d  m% I0 g
    INSERT INTO `tb_xttj` VALUES ('Mary', '3', '4', '4', '2', '5', '4', '3', null);
    - w: z+ e8 ?4 I6 KINSERT INTO `tb_xttj` VALUES ('Lucy', '2', '3', null, '3', null, '3', '4', '5');
    3 ?, _3 k% Y5 L, B7 w2 T+ CINSERT INTO `tb_xttj` VALUES ('Tom', '3', '4', '5', null, '1', '3', '5', '4');7 g9 o. O; E6 ~, ^
    INSERT INTO `tb_xttj` VALUES ('Bill', '3', '2', '1', '5', '3', '2', '1', '1');) U' T* U7 Z' K3 P4 ]
    INSERT INTO `tb_xttj` VALUES ('Leo', '3', '4', '5', '2', '4', null, null, null);: O, ]) c. B1 A0 p6 C) t9 ~

    $ G3 d7 B5 x1 M1 `3 b/ j  f
    # X3 ?0 L" \5 U& u' | 我这里只对最后一行的Leo进行推荐,看看f,g,h哪个可以推荐给他。
    / [. F9 |1 d9 M3 B8 Q* m+ ]5 L( L9 a
        用php+mysql,流程图如下:
    3 w% K9 t% z: k
      B2 U1 F" K* I3 X7 K  y+ E连接数据库并将其存储为二维数组的代码如下:
    8 ?* g0 D2 g, @$ K
    / L+ a5 v2 t1 m, v& @- G& A( i; ^header("Content-Type:text/html;charset=utf-8");
    4 _2 _  J5 x2 }. `
    " N, K! b3 s" m8 N, v6 g' y+ J: Dmysql_connect("localhost","root","admin");
    / _/ n1 w7 u3 ^mysql_select_db("geodatabase");2 o1 _/ x8 N* v, B0 K
    mysql_query("set names 'utf8'");       
    - V" n8 G1 A# t6 O4 B; L/ S
    ( V3 `8 c$ m. \# r$sql = "SELECT * FROM tb_xttj";( s. Z+ r& L: K' `- v/ b
    $result = mysql_query($sql);
    / ~3 K/ T9 n9 K7 L$ F2 O/ {+ V6 `. [: o' H9 v5 r% T
    $array = array();
    8 z% m/ C9 E) Iwhile($row=mysql_fetch_array($result))
    + r* f9 [2 U/ c0 |+ f8 A* s+ \% u{
    * r% Q9 _1 C& K$ A        $array[]=$row;//$array[][]是一个二维数组% S  D* _( A  h) z8 K& a) \! _
    }
    " H0 t8 [3 O" K% ~3 \9 ^: @$ p9 V5 g
    & ^3 n, l' F6 N: E  T- ]$ p5 k问题1:这一步完全可以看做是整表查询,这种查询是大忌,对于这种小小的演示系统还可以,但是对大数据的系统,没有效率,至于如何改进,还得多学习才是。
    " _$ i( N; V; r, d
    8 }5 Y- ^5 D$ ~$ k+ ^- E; l  T求Leo与其他人的Cos值代码如下:
    3 k2 _8 _( r' B" F4 m1 X2 E! _; Z
    " V6 M3 A( N+ P9 X' F/*
    4 C4 P! |- l) [1 A* P+ K5 @5 K* D- P * 以下示例只求Leo的推荐,如此给变量命名我也是醉了;初次理解算法,先不考虑效率和逻辑的问题,主要把过程做出来
    " a+ ]2 I5 X( e# P7 C4 y */9 P, D$ {7 M! O" s/ l- Q' H

    6 U4 q' n/ V" f; ~9 K$cos = array();) o, n% [: i2 f3 x7 q9 D3 ?
    $cos[0] = 0;7 W5 `4 j. U/ H* L/ P
    $fm1 = 0;7 t$ w2 z* [$ Q' s! [
    //开始计算cos5 o! m8 J, E; L  I/ h# ^
    //计算分母1,分母1是第一个公式里面 “*”号左边的内容,分母二是右边的内容
    # {( V9 f! q7 pfor($i=1;$i<9;$i++){$ C6 }6 r0 k& Q8 C
            if($array[5][$i] != null){//$array[5]代表Leo9 N- B9 @% F6 b
                    $fm1 += $array[5][$i] * $array[5][$i];' c  o) R( `) K6 W) }1 W
            }, p$ w- b# `% l. D  Y2 n9 X
    }  }4 _) u2 A( A+ Z. Y- c& S* O; D

    0 N/ P6 t, q( e: @0 z8 U$fm1 = sqrt($fm1);8 }+ o( V+ o) x& l* M& x- c

    , S% ?2 y, G& Q. ffor($i=0;$i<5;$i++){
    # `/ A4 j" e, S, b0 k* v9 u        $fz = 0;
    $ f' c4 d8 c7 r- `; U2 _        $fm2 = 0;
    6 m+ D* b1 Q' d. M, p        echo "Cos(".$array[5][0].",".$array[$i][0].")=";
    / T0 ~) K" N+ {9 o  A        ! o! O* \2 ^2 ?3 f: o0 }- f
            for($j=1;$j<9;$j++){1 I$ \( {9 s$ p9 l& x& c  g
                //计算分子
    ) z; ~4 W6 n( A/ P; h7 G9 O                if($array[5][$j] != null && $array[$i][$j] != null){
    $ b: t9 U8 c" B3 U! R                        $fz += $array[5][$j] * $array[$i][$j];% ?8 [* k& h/ o; K
                    }
    1 I) w$ k- J4 [. O                //计算分母2, H7 C% |5 T$ E) I1 Z, s7 b' k- q
                    if($array[$i][$j] != null){9 V. T( V) t3 f# c& F4 y7 A( }
                            $fm2 += $array[$i][$j] * $array[$i][$j];/ S1 V+ K  R1 o1 l' L; Z
                    }                       
    * Z# D5 A+ k& G* j2 D3 D( U: K: O        }
    6 K, ?' |' m( P2 k5 }2 B5 K1 ^* h        $fm2 = sqrt($fm2);
    - ~/ ?6 G+ V6 e9 |( r        $cos[$i] = $fz/$fm1/$fm2;
    1 a: I9 x; c6 z* }  }# C# h        echo $cos[$i]."<br/>";
    % K! r; H: \9 d  s* A! C}
    / o( G" v. a5 D; h2 ~. F; i7 S2 a
    这一步得到的结果是酱紫:+ X9 e+ y2 F( k( b6 g/ R( {

    ) K8 S- p6 f3 B* C/ H5 Q) }将求好的Cos值排序,采用快排代码如下(百度copy而来):3 U1 `. l9 W( P7 |( R# z8 O, |) x

    4 @' _; O/ x) ]& N; U" U6 V1 T/ Z6 t( P, D, W7 D# @
    //对计算结果进行排序,凑合用快排吧先
    ( e7 v7 G0 ^3 \function quicksort($str){
    ( L" H; e/ Q+ B, F+ G" d        if(count($str)<=1) return $str;//如果个数不大于一,直接返回/ \3 x5 ~8 P5 h
            $key=$str[0];//取一个值,稍后用来比较;; K& M1 v) m: W' ~: g
            $left_arr=array();
    $ E2 Q; X( A, B7 W" {- D1 K& u        $right_arr=array();- N: M: y) W6 M5 c
            * }2 S( k3 I$ y/ F! i/ s' X3 G" u
            for($i=1;$i<count($str);$i++){//比$key大的放在右边,小的放在左边;
    / {2 \1 d3 ^0 z+ i; ~# q                if($str[$i]>=$key)3 `  |+ l0 ?3 ~
                    $left_arr[]=$str[$i];
    1 j3 V4 \, u1 t1 _+ \. t                else
    : O4 y/ s. H( X, `( Z                $right_arr[]=$str[$i];
    4 g3 U, f" t6 f8 J$ H, v- a        }% ~! y# `& K1 q) d
            $left_arr=quicksort($left_arr);//进行递归;$ ]- I3 j: H- F+ n, n) `  r
            $right_arr=quicksort($right_arr);
    ' `/ u/ u3 t3 {- B        return array_merge($left_arr,array($key),$right_arr);//将左中右的值合并成一个数组;
    : O7 g2 H  ~( b: C* B}
    5 N. h, B! f3 y( _: B# u3 f3 X" ~& y" p( e; M* ~
    $neighbour = array();//$neighbour只是对cos值进行排序并存储) M' E+ @' d5 t, V
    $neighbour = quicksort($cos);
    4 b3 g1 Q* `- Y, r; P! b" t2 i# T  q* m/ X5 i

    6 j0 Z! N3 ~0 w! z3 z这里的$neighbour数组仅仅存储了从大到小排序好的Cos值,并没有与人联系起来。这个问题还要解决。: w5 K+ m* d0 d; |
    / N; c9 l2 d  h7 K0 B+ c& ^
    选出Cos值最高的3个人,作为Leo的邻居:
    / m$ x( t9 U( g  C# I2 R& G
    % _6 q/ v7 _% I9 i8 u//$neighbour_set 存储最近邻的人和cos值
    5 f, g: [$ }" z/ A( d7 o5 w$neighbour_set = array();5 ~2 X& V9 |4 X( I# _. Y
    for($i=0;$i<3;$i++){/ d- t6 y# @* P. s) h) K# G) [
            for($j=0;$j<5;$j++){. P( T( _  h' X  j3 h. e
                    if($neighbour[$i] == $cos[$j]){
      h0 @, W4 ^. Z0 O4 g                        $neighbour_set[$i][0] = $j;
    3 y8 p0 w' S& U: d$ w                        $neighbour_set[$i][1] = $cos[$j];
    ( B6 Y  _: b$ }, v                        $neighbour_set[$i][2] = $array[$j][6];//邻居对f的评分) f9 S( i1 n) _' H( [& Z; U% Z
                            $neighbour_set[$i][3] = $array[$j][7];//邻居对g的评分* m7 b  H' D5 l1 N
                            $neighbour_set[$i][4] = $array[$j][8];//邻居对h的评分& [1 C! C' \4 K% D
                    }7 p! E7 n# t, y) ?% d4 p
            }
    - K$ V$ K9 T& `0 {}* F* Z0 m9 ]) j" d$ ?7 ^
    print_r($neighbour_set);
    , q& o2 V! A# n2 iecho "<p><br/>";
    ( A0 N+ r7 V2 D. J2 ^6 z5 _' i- D2 y0 p# Q" C1 M2 p1 H7 P
    这一步得到的结果是酱紫:& Z$ L: W, h6 E6 ^0 A
    - y8 i0 u) ]% O0 Q7 Z0 Z

    4 m: T/ `9 \7 p4 H  g, x* S! |, E) ^- S' ^) V/ v
    转存失败重新上传取消
    ; J8 o, E3 C" o4 b* W+ A) Z, V% Q, e+ Y
    这是一个二维数组,数组第一层的下标为0,1,2,代表3个人。第二层下标0代表邻居在数据表中的顺序,比如Jhon是表中的第0个人;下标1代表Leo和邻居的Cos值;下标2,3,4分别代表邻居对f,g,h的评分。
    9 h1 q! A4 l# o$ ]
    : Z5 V5 [. y9 q" ?# i4 t开始进行预测,计算Predict代码如下:# x( ?% Y7 d6 q" B2 p4 S
    8 L; E( u' N6 v# k8 m/ \. B& O. u1 ~
    我是分别计算Leo对f,g,h的预测值。在此有一个问题,就是如果有的邻居对f,g,h的评分为空,那么该如何处理。比如Jhon和Mary对h的评分就为空。本能的想到用if判断一下,如果为空则跳过这组计算,不过这样处理是否合理,有待考虑。以下代码并没有写出这个if判断。0 O. {4 @5 o' m# v0 i# B
    ! A6 A7 m+ f( m: y( ?5 O
    //计算Leo对f的评分
    ; Q4 l7 q5 ]2 G: o$p_arr = array();1 F8 p# T  a# c7 K& E  o
    $pfz_f = 0;4 X. v* L$ T; r6 }( |& H! V& b
    $pfm_f = 0;! ]0 c2 n* @) e
    for($i=0;$i<3;$i++){% w1 d+ y- y& d+ F7 ?& N
            $pfz_f += $neighbour_set[$i][1] * $neighbour_set[$i][2];
      i  l# K0 P( K        $pfm_f += $neighbour_set[$i][1];
    0 m& M, R1 u$ u  a1 m9 F}
    " k6 f5 V' F! ^) B$p_arr[0][0] = 6;
    ; ~: g, p8 @# O* q& [$p_arr[0][1] = $pfz_f/sqrt($pfm_f);! T+ R0 P' [" }3 H: `9 g
    if($p_arr[0][1]>3){
    * C1 M6 B# h5 w        echo "推荐f";
    * r7 }! y7 `& j7 n}1 F# ^7 R) ~7 Y  m: ?! h+ ?
    ' p7 {0 }: ]4 ~
    //计算Leo对g的评分
    8 P, [9 k, z$ e- L) }$pfz_g = 0;
    ; x, S( j' C5 d* E, z2 R0 Y# ?$pfm_g = 0;
    6 A/ ^6 ]: f7 \" H$ Wfor($i=0;$i<3;$i++){
    / F- S( p" H6 d  p        $pfz_g += $neighbour_set[$i][1] * $neighbour_set[$i][3];
    9 I/ z7 F, s2 g# G5 G, @1 @  a        $pfm_g += $neighbour_set[$i][1];
    3 \$ t  D$ U9 @/ L        $p_arr[1][0] = 7;6 x1 ]# ]; S% `5 G. B6 ?% @; F
            $p_arr[1][1] = $pfz_g/sqrt($pfm_g);8 u- ?' [0 h( D& j
    }
    2 a' q- P4 }, B0 b* {' t% ^0 Y' kif($p_arr[0][1]>3){7 U/ C3 Z  j. m. n
            echo "推荐g";: K! i% T: a9 O, g7 v
    }
    + x9 ]1 c; Y/ n/ b1 w- h3 [+ e6 x# |" c* A( G: \! j
    //计算Leo对h的评分: P) {; o$ A7 i0 P0 w; x* j
    $pfz_h = 0;% P) H" J9 i! U, z9 R. U  ]& v
    $pfm_h = 0;2 f/ F% J0 F) a) D; q6 Y, ^
    for($i=0;$i<3;$i++){: x2 y% `9 \: y% U" S4 U/ U0 i
            $pfz_h += $neighbour_set[$i][1] * $neighbour_set[$i][4];
    $ f: c2 v+ ]" a& F+ O        $pfm_h += $neighbour_set[$i][1];7 C& c7 T( X$ n' j2 P+ f" r8 K
            $p_arr[2][0] = 8;
    0 ^6 @5 M# I0 O4 C- g# G        $p_arr[2][1] = $pfz_h/sqrt($pfm_h);
    0 g. a; z- D6 R7 Z}
    * l1 v' D$ Y& G; W, o$ ~: kprint_r($p_arr);
    0 R7 j- h1 t5 {( P, [if($p_arr[0][1]>3){" }! K8 [# C* E" Y, v0 n) g
            echo "推荐h";
    1 l. N7 n8 C5 `2 n! N}
    # [: W) _' U% a# Q  R
    + o" c( N: @8 v/ }2 B$p_arr是对Leo的推荐数组,其内容类似如下;
    ) {% I  A- F8 x. q! ]
    * v% F* d; F( B( W; f7 p+ tArray ( [0] => Array ( [0] => 6 [1] => 4.2314002228795 ) [1] => Array ( [0] => 7 [1] => 2.6511380196197 ) [2] => Array ( [0] => 8 [1] => 0.45287424581774 ) ). L7 r- y+ F6 @) ^
    " k6 A9 Q: f7 S4 B$ L8 k) _
    f是第6列,Predict值是4.23,g是第七列,Predict值是2.65........
    2 }3 Z) e9 k/ \. c$ x2 i, U6 ?4 |! U1 w% k3 R9 B
    求完了f,g,h的Predict值后有两种处理方式:一种是将Predict值大于3的物品推荐给Leo,另一种是将Predict值从大到小排序,将Predict值大的前2个物品推荐给Leo。这段代码没有写。2 P$ A6 k* j' V& d1 v3 v2 C" b/ i) X

    % [( W3 \) }3 _6 M% j从上面的示例中可以看出,推荐算法的实现非常麻烦,需要循环,判断,合并数组等等。如果处理不当,反而会成为系统的累赘。在实际处理中还有以下问题:: e# r' C/ v! ]% j
    " l. B  a2 O- [7 t! s
    1.以上示例我们只对Leo进行推荐,而且我们已经知道Leo没有评价过f,g,h物品。如果放到实际的系统里,对于每一个需要进行推荐的用户,都要查询出他没有评价过哪些物品,这又是一部分开销。; j" m. W, V. E% Q+ g$ w% A

    9 w8 k1 v- y/ z5 v9 i2.不应当进行整表查询,在实际系统中可以设定一些标准值。比如:我们求Leo与表中的其他人的Cos值,如果该值大于0.80,则表示可以为邻居。这样,当我找到10个邻居之后,就停止求Cos值,避免整表查询。对于推荐物品也可以适当采用此方法,比如,我只推荐10个物品,推荐完后就停止求Predict值。
    6 h$ V9 l: X3 G0 e8 Q6 V1 ?% g6 g  H/ o& C  q
    3.随着系统的使用,物品也会发生变化,今天是fgh,明天没准就是xyz了,当物品变化时,需要动态的改变数据表。& ~4 G+ q( u; Q! x2 Q

    $ e2 u- K  H' s7 d. x, M$ k4.可以适当引进基于内容的推荐,来完善推荐算法。: b- o; y% x' H

    7 w; k+ T( O) |, ~  k5.推荐的精确性问题,这个设置不同的标准值,会影响精确性。7 [# u! m  A$ ?7 y2 k
    ————————————————+ [) b( Z9 T  i0 ^) Q; p
    版权声明:本文为CSDN博主「星斗其文,赤子其人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。- m) w2 E7 `2 l6 d5 U' l& v1 d( \
    原文链接:https://blog.csdn.net/liuliuhelingdao/article/details/126715465& K, _/ J* _& T8 y3 Q
    ( l! p4 H' r: M2 X

    ) @5 ~# a* G3 q  j4 F/ a. M. C
    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-9-27 17:42 , Processed in 0.480243 second(s), 51 queries .

    回顶部