QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1990|回复: 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+ k  Q. @! M6 c7 Zphp+mysql实现简单的协同过滤推荐算法
    7 y- ?& Y* e0 w% q! g" M# F仅做标记。。。% h7 C: S. G7 Q, o, h7 _% U0 v

    0 B* }3 V  @/ ^2 N
    3 m9 j9 Q/ P, {. e
    " D' d! N4 S& X8 o要实现协同过滤推荐算法,首先就要理解算法的核心思想和流程。该算法的核心思想可以概括为:若a,b喜欢同一系列的物品(暂时称b是a的邻居吧),则a很可能喜欢b喜欢的其他物品。算法的实现流程可以简单概括为:1.确定a有哪些邻居 2.通过邻居来预测a可能会喜欢哪种物品  3.将a可能喜欢的物品推荐给a。
    9 e4 o2 x% _6 ^" a) z' V' w( W) c* v
    算法核心的公式如下:
    + i9 P) }' o; v/ X. B: N" ^  \) h9 R% Y3 T" ^% L: O3 b
    1.余弦相似度(求邻居):
    ; n% w! ]' q( D. p" z. Z* n; C9 S/ B1 I3 m3 I& S
    2.预测公式(预测a可能会喜欢哪种物品):
    $ x1 v5 Y9 s/ r  M, j- Z1 d4 {. \. J5 g; U8 w
    仅从这两个公式我们就可以看出,仅仅是按照这两个公式进行计算,就需要进行大量的循环与判断,而且还涉及到排序的问题,就涉及到排序算法的选择与使用,这里我选快排,从网上copy了一段快排,直接用。总之实现起来很麻烦,在大数据情况下,更何谈效率。
    % R$ ?; U- j0 A# ]5 }) W* D' c! X- @% q+ r+ ~
    首先建表:
    / i' ]$ w: W% F# @8 a: h# K9 O  o
    # D2 v. l$ a8 \8 I+ r" Q  mDROP TABLE IF EXISTS `tb_xttj`;
    % |: H& m* W! [% o! XCREATE TABLE `tb_xttj` (! ]- b: d4 A7 |- T: ^, m
      `name` varchar(255) NOT NULL,
    ( a1 J( E) ]- `* I7 ^  `a` int(255) default NULL,
    ! v: y8 l7 R& d- ]+ j% Z  `b` int(255) default NULL,
    9 b+ R9 K( P7 Z+ g( K% ?' b2 W$ z* H1 G  `c` int(255) default NULL,3 y' Q. K9 ^: C
      `d` int(255) default NULL,3 b6 `  x1 |. x: }
      `e` int(255) default NULL,
    - f8 T) f0 V, r# [! L9 z  `f` int(255) default NULL,
    ) g3 p9 E0 j! R( H2 C2 C. D  `g` int(255) default NULL,
    / b: C; ^  L0 q+ H% d9 }6 S  `h` int(255) default NULL,
    * R; _6 }- [% q  PRIMARY KEY  (`name`)
    - B5 ^6 ]% w; s; R) ENGINE=MyISAM DEFAULT CHARSET=latin1;
    / P5 D# ?9 |; Z7 c, @' ~- b1 |0 i4 s2 i- w
    INSERT INTO `tb_xttj` VALUES ('John', '4', '4', '5', '4', '3', '2', '1', null);
    8 R' {# q& _0 m3 hINSERT INTO `tb_xttj` VALUES ('Mary', '3', '4', '4', '2', '5', '4', '3', null);
    : y% U& z- ~8 E+ K# b5 e" mINSERT INTO `tb_xttj` VALUES ('Lucy', '2', '3', null, '3', null, '3', '4', '5');. L; n1 e0 l- |* W( N7 y: s
    INSERT INTO `tb_xttj` VALUES ('Tom', '3', '4', '5', null, '1', '3', '5', '4');
    ( ~! m& {; G+ QINSERT INTO `tb_xttj` VALUES ('Bill', '3', '2', '1', '5', '3', '2', '1', '1');. o9 Z) R2 X2 w8 S" ?0 Y& L) G8 l
    INSERT INTO `tb_xttj` VALUES ('Leo', '3', '4', '5', '2', '4', null, null, null);
    0 o! R" W: a) P
    / t6 E: q" Z% j- A7 y! K6 o% D+ k9 @" w7 C9 v5 x- g7 Q  ~
    我这里只对最后一行的Leo进行推荐,看看f,g,h哪个可以推荐给他。
    * u; U# y  n: v0 G+ s! a, b% `
    & ]" B9 H8 z# Q- D; G5 v  C    用php+mysql,流程图如下:
    . M& i4 e' \: ^% W/ p
    ; c% t1 k- P' p" o; k* x+ V2 H连接数据库并将其存储为二维数组的代码如下:
    0 ?  H% B2 k% f) Q: f% O. o; m* n5 J+ K; q. U; ?! V! p
    header("Content-Type:text/html;charset=utf-8");: j0 R# R) }6 J! L
    ( _: k; L# `1 {: y# G: Q
    mysql_connect("localhost","root","admin");
    & W8 m! C  i$ X# I; ]7 Wmysql_select_db("geodatabase");# B' [9 i2 ?, }: e
    mysql_query("set names 'utf8'");       
    5 X. ~1 b6 W* O. A0 g8 W* U4 l. A. f$ g- a
    $sql = "SELECT * FROM tb_xttj";
    3 r5 y! D) h0 [/ S7 X$result = mysql_query($sql);
    + p. N  u' D+ s1 T# {' D4 C
    ) d5 n% x5 d3 M7 d3 ]. j+ I5 x$array = array();) _/ n: P5 h, W- k% t
    while($row=mysql_fetch_array($result))% @; h% ~) ]) y; s
    {
    % L+ C. b9 Z9 h; [6 f- R/ [        $array[]=$row;//$array[][]是一个二维数组$ a$ h% z3 f7 E5 O# \/ ?% P9 M( U* y
    } 6 z9 P1 y7 A7 @+ r8 Q/ G, P

    9 }' @$ Z0 S; k2 u, e0 y7 F9 L问题1:这一步完全可以看做是整表查询,这种查询是大忌,对于这种小小的演示系统还可以,但是对大数据的系统,没有效率,至于如何改进,还得多学习才是。5 ^4 _. x' B& @* I. n! ]  ^
    ! L. S. x+ Q0 [2 V
    求Leo与其他人的Cos值代码如下:* a7 i2 w5 g2 s6 W7 k" ^- _6 c
    6 [2 A5 j- q2 b9 q, u$ ?
    /*
    % I/ w8 ^. O: H5 L4 h3 B. M * 以下示例只求Leo的推荐,如此给变量命名我也是醉了;初次理解算法,先不考虑效率和逻辑的问题,主要把过程做出来+ N! F* B' P: [* D$ A
    */- G8 E: H* f/ ~6 L8 ?
    ; r9 m; Q1 ]9 o4 h6 n
    $cos = array();
    , Y* D0 Q$ N9 V! R! W$cos[0] = 0;5 o1 O% h  y# Z! {9 V
    $fm1 = 0;0 I6 w; f5 @1 ?5 C9 [& w0 O
    //开始计算cos$ H9 A% W+ Q8 m4 g8 S. t2 U2 t7 K- m
    //计算分母1,分母1是第一个公式里面 “*”号左边的内容,分母二是右边的内容
    % e: ?* h7 }  m6 {4 L6 n% P/ I1 Ufor($i=1;$i<9;$i++){
    7 H) a6 S! l5 t+ I        if($array[5][$i] != null){//$array[5]代表Leo
    ! u+ \* W# S8 X( ~3 f                $fm1 += $array[5][$i] * $array[5][$i];
    * s, o0 d- s8 P( `        }2 Z$ f( h) O: `$ S
    }
    " R) Y1 i" R8 R8 R- F6 K4 N" C, G6 e
    $fm1 = sqrt($fm1);
    8 c8 q- [! _, b$ `' k9 k0 b- U: o2 i# N/ \0 v
    for($i=0;$i<5;$i++){8 B9 o/ f" \/ r2 L# U" F, u- o* P' M
            $fz = 0;
    - m# ]) L- ~/ c# p        $fm2 = 0;
    6 V; c7 i* m* Y: q        echo "Cos(".$array[5][0].",".$array[$i][0].")=";+ w9 B+ k6 c' Y; D( N8 W
           
    + o' v; z! }0 ~% f+ O" U2 l8 D        for($j=1;$j<9;$j++){$ I  T, N% ]. W
                //计算分子
    & e: {9 a4 R, b4 e7 s% s                if($array[5][$j] != null && $array[$i][$j] != null){; c4 p3 f1 V$ G1 c3 A& L1 }4 v$ z
                            $fz += $array[5][$j] * $array[$i][$j];
    & q) @# c' Y; T3 I+ l" ?) W                }
    5 h! j/ G9 X# N" R/ C7 Q0 ?) v5 o                //计算分母2
    9 D8 S9 l2 I) O5 J9 h                if($array[$i][$j] != null){
    ; B, I5 q# c  c                        $fm2 += $array[$i][$j] * $array[$i][$j];  m! B2 L1 L5 X2 C4 O  r+ f. U
                    }                       
    5 d/ h6 l7 U3 \+ t8 f+ t0 R        }+ g7 N8 R4 {0 A; h
            $fm2 = sqrt($fm2);
    7 H/ W8 T2 u: n8 u+ q" c7 E7 L        $cos[$i] = $fz/$fm1/$fm2;9 p& d6 h, X5 V' Z% m* r
            echo $cos[$i]."<br/>";( a% d, Y7 Q0 D. \* O- m
    }
    - G- X" s- M0 ]/ f: R# N( w) m* i) z. }" f, [: D/ d( ?
    这一步得到的结果是酱紫:! n, K- q. K: `
    , F2 s; V! x% }; F
    将求好的Cos值排序,采用快排代码如下(百度copy而来):
    9 i9 U# v: T+ V  H9 v" I8 @7 a: ?! I  R0 x3 z3 F  @0 r' ~
    7 u) ^- |! [- h- U" L' C  U4 Y
    //对计算结果进行排序,凑合用快排吧先+ S* o) k! u  [- Y3 V6 h5 U; [6 z( A
    function quicksort($str){$ Y3 [. S# i- n/ h
            if(count($str)<=1) return $str;//如果个数不大于一,直接返回7 B1 J7 K* E: ], V+ F. k# a
            $key=$str[0];//取一个值,稍后用来比较;
    8 g4 Z; z: i! q8 u4 M$ t        $left_arr=array();) d* C2 J. Q# }% W, P  o% x' g$ _$ `
            $right_arr=array();, c8 W% W/ O; m! r2 o: L
            0 a( I  G  q4 Y0 s( g" z
            for($i=1;$i<count($str);$i++){//比$key大的放在右边,小的放在左边;$ e( ]" r# s8 \8 F' M" k# P
                    if($str[$i]>=$key)+ o$ j6 l4 U. ^5 Z
                    $left_arr[]=$str[$i];, g0 E: w# |% G  s) x# }
                    else5 m, R+ J; T  j
                    $right_arr[]=$str[$i];
    ( K3 M+ z  v6 u' j, d. k/ T" M, B. a        }
    4 x! `% U4 Z1 K9 E; w8 s2 X        $left_arr=quicksort($left_arr);//进行递归;
    0 v: |1 ]  Y9 Y$ U5 W  i* z        $right_arr=quicksort($right_arr);8 ^+ d9 v& i% W3 L
            return array_merge($left_arr,array($key),$right_arr);//将左中右的值合并成一个数组;
    7 d& T2 ^. \) C}
    , Z7 u, W: p! t& k: w; P6 S9 l# a7 g- O4 ~2 x0 c0 C9 V% z
    $neighbour = array();//$neighbour只是对cos值进行排序并存储: E* T. ^* |( \2 k" P
    $neighbour = quicksort($cos);
    ! d  @3 E+ ^3 n% S! G$ h
    6 R9 e  R! g- b" v5 D2 A
    . `2 y( J$ G$ I/ v这里的$neighbour数组仅仅存储了从大到小排序好的Cos值,并没有与人联系起来。这个问题还要解决。
    + P. f6 u1 Q1 o
    - _* k7 ^, A# |1 _" f选出Cos值最高的3个人,作为Leo的邻居:7 o2 o$ p4 z( s& `; M# Q0 e2 G  H- p
    & A3 r) ^1 {( ^% x" [  T  _
    //$neighbour_set 存储最近邻的人和cos值6 h: ~  S2 S, i
    $neighbour_set = array();
    4 h" U3 U. G% ]* \1 I" zfor($i=0;$i<3;$i++){
    + M& ~/ @7 I. W        for($j=0;$j<5;$j++){. ~# e: A9 q6 k  |7 M8 q/ A* Y9 Z9 K
                    if($neighbour[$i] == $cos[$j]){
    % b$ B+ @! Q/ f! a                        $neighbour_set[$i][0] = $j;. @: k% ]$ H/ h( \! N5 R  a
                            $neighbour_set[$i][1] = $cos[$j];
      U3 c* Q3 u  }! q3 l, `                        $neighbour_set[$i][2] = $array[$j][6];//邻居对f的评分! i) j' ]7 O6 [/ B5 M! g2 _; W0 y
                            $neighbour_set[$i][3] = $array[$j][7];//邻居对g的评分" }+ w( @& T4 c, n2 {* u4 _
                            $neighbour_set[$i][4] = $array[$j][8];//邻居对h的评分
    * l1 b% r. v. f: \; i9 l" s) l* s                }
    2 Q* l: p1 w/ ]0 X1 b        }
    ! j. w! n2 j# m- L4 w}2 K0 C9 A" E* n' b1 z  E0 @
    print_r($neighbour_set);$ R6 D3 L3 r5 u2 f: t! H( _
    echo "<p><br/>";
      c; F  p7 R8 W- d) J' k) P& \) ^0 Y/ Z9 i2 K% E
    这一步得到的结果是酱紫:
    * b# v4 R, i% i) @6 N
    ' y5 S  c! @8 ?# m
    9 }0 N( U" P' p
    4 F/ s$ A* n# z' e转存失败重新上传取消
      a5 A% A5 v) B0 n1 A: X# G, ]/ J  G( r% r
    这是一个二维数组,数组第一层的下标为0,1,2,代表3个人。第二层下标0代表邻居在数据表中的顺序,比如Jhon是表中的第0个人;下标1代表Leo和邻居的Cos值;下标2,3,4分别代表邻居对f,g,h的评分。# M. m9 Z. b/ @9 V

    # J+ i" _/ h! F/ E5 u, x+ ^1 O开始进行预测,计算Predict代码如下:
    1 X: b$ R6 c) S/ x8 v7 p) `6 c5 V" W7 r+ m$ N3 B
    我是分别计算Leo对f,g,h的预测值。在此有一个问题,就是如果有的邻居对f,g,h的评分为空,那么该如何处理。比如Jhon和Mary对h的评分就为空。本能的想到用if判断一下,如果为空则跳过这组计算,不过这样处理是否合理,有待考虑。以下代码并没有写出这个if判断。
    5 A" a# m" P! @. Q9 D" D# }/ K
    //计算Leo对f的评分
    * t- {  \( _& g. J" s7 z! ], ]0 d$p_arr = array();; U8 \5 d$ A1 }! n& B/ s
    $pfz_f = 0;
    ( J& D& S' i0 P4 V8 }/ P/ t8 D' n4 n$pfm_f = 0;6 D; u" v# Q8 {+ K
    for($i=0;$i<3;$i++){
    $ _- d8 b% y; y        $pfz_f += $neighbour_set[$i][1] * $neighbour_set[$i][2];. c) l; _' U/ E: ?. b2 r9 F0 B, @
            $pfm_f += $neighbour_set[$i][1];
    . R1 [* Y- U( l2 f}* v; f, n, m7 t- F. C" s! r
    $p_arr[0][0] = 6;
    . G6 F: X% a  J2 ]+ W) d$p_arr[0][1] = $pfz_f/sqrt($pfm_f);+ E$ t5 M- G1 U' Z7 q2 K' N' m
    if($p_arr[0][1]>3){
    ' s/ c8 o( I/ i9 U' L0 b  p        echo "推荐f";$ z0 O  F" A4 y- u( _
    }
    0 b5 ~7 J8 e! ~. v) Y  |6 |
    / i1 ~: T8 U* [/ L" p3 A4 t$ j//计算Leo对g的评分" p" _( Z5 [2 N  D9 d' c) ]
    $pfz_g = 0;
    ; ]$ U+ m8 n& m9 p# O5 j$pfm_g = 0;
    ( d5 G/ k( O0 Q" }: a4 o# e" K9 zfor($i=0;$i<3;$i++){
    , B4 z* b& R3 A8 P" y        $pfz_g += $neighbour_set[$i][1] * $neighbour_set[$i][3];" K: i& x! ~' p% j. U3 \5 q
            $pfm_g += $neighbour_set[$i][1];
    3 Z0 V1 D$ ^/ v$ f! w+ n        $p_arr[1][0] = 7;, K$ ~8 x6 x; }' M6 D. {; K7 L6 ?
            $p_arr[1][1] = $pfz_g/sqrt($pfm_g);
    $ Q# M7 m: y$ O! h8 M}) n" T- _4 N4 Z  c- }% }
    if($p_arr[0][1]>3){# q2 T; J* j) o4 T% n( K2 Z
            echo "推荐g";! U5 x9 |! ^& b8 g$ F0 q5 ]3 y
    }4 H3 Y% ^- e' D  u) ^
    ( f' I; Q. I' X9 E
    //计算Leo对h的评分
    ( k* G9 x, u7 e  L& k+ r# R$pfz_h = 0;
    * I, @4 b% F2 l! B1 }$pfm_h = 0;# n3 _+ A9 z: I1 G+ I8 i+ M
    for($i=0;$i<3;$i++){
    7 P0 h/ h# t) k. t6 L* p' y        $pfz_h += $neighbour_set[$i][1] * $neighbour_set[$i][4];
      q( t2 W8 {6 r        $pfm_h += $neighbour_set[$i][1];
    6 b4 o2 E8 t4 o% n2 _- x. v9 m7 k        $p_arr[2][0] = 8;
    0 k( c; W, C% m1 b# @        $p_arr[2][1] = $pfz_h/sqrt($pfm_h);
    8 m+ w( W2 K9 s: ]) F' ?}
    0 ~/ p% l8 X, b  Y6 rprint_r($p_arr);
    ; ]6 ^; K% E& e5 z' l3 Y6 T$ Xif($p_arr[0][1]>3){
    9 p) L4 |; A+ F( L  u        echo "推荐h";
    1 [) d: ^- U8 ~- x) M% D( M0 T, F}
    7 U& p- V$ Z3 Z* A+ |
    & f: O9 ?. }6 F$ a' j3 r5 r& N$p_arr是对Leo的推荐数组,其内容类似如下;
    5 L/ v- D2 R9 D9 _$ @5 n5 ]- J3 {5 K1 c8 a8 J/ W+ S( }0 C0 \
    Array ( [0] => Array ( [0] => 6 [1] => 4.2314002228795 ) [1] => Array ( [0] => 7 [1] => 2.6511380196197 ) [2] => Array ( [0] => 8 [1] => 0.45287424581774 ) )/ v4 z* ~- g# _' H

    2 Z; \. Z* n+ @9 If是第6列,Predict值是4.23,g是第七列,Predict值是2.65........
      E2 n+ |& R( A: b( n- |' X( e; s4 n5 N) ^3 C+ {  @* S
    求完了f,g,h的Predict值后有两种处理方式:一种是将Predict值大于3的物品推荐给Leo,另一种是将Predict值从大到小排序,将Predict值大的前2个物品推荐给Leo。这段代码没有写。
    / `  }* F9 f. c8 X9 c3 Q
    , @/ ?( {2 O- [. M- W, Z从上面的示例中可以看出,推荐算法的实现非常麻烦,需要循环,判断,合并数组等等。如果处理不当,反而会成为系统的累赘。在实际处理中还有以下问题:
    : L4 [* M/ A7 `, V1 D
    7 B: n8 i: l- \' c% F) B1.以上示例我们只对Leo进行推荐,而且我们已经知道Leo没有评价过f,g,h物品。如果放到实际的系统里,对于每一个需要进行推荐的用户,都要查询出他没有评价过哪些物品,这又是一部分开销。
    ( b% b- _1 k8 k3 c1 f3 D2 d
    # S/ I7 R) j6 N2 ]5 \2.不应当进行整表查询,在实际系统中可以设定一些标准值。比如:我们求Leo与表中的其他人的Cos值,如果该值大于0.80,则表示可以为邻居。这样,当我找到10个邻居之后,就停止求Cos值,避免整表查询。对于推荐物品也可以适当采用此方法,比如,我只推荐10个物品,推荐完后就停止求Predict值。
    " N; ]8 Y4 p5 F5 ?& x. q1 P) ]' L# u
    $ O' O/ ]7 H, O3 D* |% R" K& D3.随着系统的使用,物品也会发生变化,今天是fgh,明天没准就是xyz了,当物品变化时,需要动态的改变数据表。
    . B5 R+ g+ n; |) i
    0 s) [4 L: L" y& D6 A& M" w4.可以适当引进基于内容的推荐,来完善推荐算法。
    - A. J# c3 [) k! Y3 h+ Y' V" ]4 I) u( e! m1 K5 \4 N, `
    5.推荐的精确性问题,这个设置不同的标准值,会影响精确性。" S8 {4 _" p4 U
    ————————————————
    8 P% E3 m5 x6 j3 o8 s5 h5 t版权声明:本文为CSDN博主「星斗其文,赤子其人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    . u  G0 ]6 O. T5 h% Y原文链接:https://blog.csdn.net/liuliuhelingdao/article/details/126715465! ?9 {" D  B# r

    2 b' R) }: t8 b# L, E) q
    # p, O2 E) E% Y( Z# a! b) \
    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:27 , Processed in 1.916632 second(s), 50 queries .

    回顶部