QQ登录

只需要一步,快速开始

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

    + P+ M2 E% }: I; a* ?7 Xphp+mysql实现简单的协同过滤推荐算法# ]6 @+ `$ T/ P% J. V8 M" M. S# j. u$ s
    仅做标记。。。
    - \2 s8 B- i* Z) U  W+ Q# ], s# ]6 I' ^6 X/ U8 n

    9 a7 `3 g0 R0 b  }. D. G+ E: O3 g/ ]! I2 `$ U0 B5 Z+ E+ Z% c; m
    要实现协同过滤推荐算法,首先就要理解算法的核心思想和流程。该算法的核心思想可以概括为:若a,b喜欢同一系列的物品(暂时称b是a的邻居吧),则a很可能喜欢b喜欢的其他物品。算法的实现流程可以简单概括为:1.确定a有哪些邻居 2.通过邻居来预测a可能会喜欢哪种物品  3.将a可能喜欢的物品推荐给a。
    4 F! p  H; ~7 N& m2 [
    * v0 }2 ?  j$ O2 \算法核心的公式如下:
    8 N. g$ i" M0 j
      k  J" V' R. e6 P# l( R1.余弦相似度(求邻居):) F2 C$ s3 v8 Y0 }' ~, }" L9 _

    . s2 C- ?& t4 m7 b( s2.预测公式(预测a可能会喜欢哪种物品):
    0 m& D5 Z8 e2 `
    3 V: m9 O+ ^# }% d$ b; x+ H' J仅从这两个公式我们就可以看出,仅仅是按照这两个公式进行计算,就需要进行大量的循环与判断,而且还涉及到排序的问题,就涉及到排序算法的选择与使用,这里我选快排,从网上copy了一段快排,直接用。总之实现起来很麻烦,在大数据情况下,更何谈效率。3 p1 [- D: I4 i/ E* ^% U

    , n( b1 M/ \9 e) ]7 w3 D首先建表:
    1 E/ z5 g6 Z0 a' P& m5 {4 }0 [7 K) f7 R/ h
    DROP TABLE IF EXISTS `tb_xttj`;
    # l7 u4 q% b$ h, JCREATE TABLE `tb_xttj` (
    " \; c! ^, a0 p3 r, Y3 ?$ u+ K  `name` varchar(255) NOT NULL,7 q* A- C$ A1 o- T: K4 F$ q, g
      `a` int(255) default NULL,  N7 e$ D. A2 J9 _9 n" }
      `b` int(255) default NULL,
    5 i+ \" _4 d$ t, ], A3 i# l4 ^6 y  `c` int(255) default NULL,; }8 c1 U# h- D  t: u; Z+ h
      `d` int(255) default NULL,4 J* i% D  d2 G7 v7 v
      `e` int(255) default NULL,
    , E# ^5 r! |& u# S  `f` int(255) default NULL,! N8 P: a' R( x
      `g` int(255) default NULL,
    & x! B5 T+ z2 W# M  `h` int(255) default NULL,
    . x5 m. L) b) M. N: \  PRIMARY KEY  (`name`)
    ; G, R0 Z- `$ O8 L/ R) ENGINE=MyISAM DEFAULT CHARSET=latin1;( o/ O1 A6 h; Q! O& [8 Z

    # Z1 L: P4 ^/ AINSERT INTO `tb_xttj` VALUES ('John', '4', '4', '5', '4', '3', '2', '1', null);
    ! [6 y8 R+ b  E. Q* BINSERT INTO `tb_xttj` VALUES ('Mary', '3', '4', '4', '2', '5', '4', '3', null);
    : c! T; L, \+ k& g( hINSERT INTO `tb_xttj` VALUES ('Lucy', '2', '3', null, '3', null, '3', '4', '5');& Q2 p8 b# D5 ~. U* A5 y4 r
    INSERT INTO `tb_xttj` VALUES ('Tom', '3', '4', '5', null, '1', '3', '5', '4');
    * q  z( X1 {% N  i( c& X$ F# o+ iINSERT INTO `tb_xttj` VALUES ('Bill', '3', '2', '1', '5', '3', '2', '1', '1');3 |0 n/ H0 N: h% O1 g
    INSERT INTO `tb_xttj` VALUES ('Leo', '3', '4', '5', '2', '4', null, null, null);* N  [+ G% O/ s" K; D  [

    - m" N8 y- w% u! t, M1 u2 Z/ d# w5 I, U. A
    我这里只对最后一行的Leo进行推荐,看看f,g,h哪个可以推荐给他。% M& f6 }0 A1 ^: H

    , k5 e$ b1 F+ Z( c    用php+mysql,流程图如下:
    9 O" R9 |. k9 h2 k! m$ x* i5 i# m; D2 }# D& k' M7 x$ \
    连接数据库并将其存储为二维数组的代码如下:
    5 ~! M/ p& M4 u  {- g7 A4 E4 g# A$ \/ J. n* y$ M, S/ ?# V
    header("Content-Type:text/html;charset=utf-8");+ `+ m) T! ]( o4 k) I

    ; Q3 ?& a( o# _/ emysql_connect("localhost","root","admin");
    # a7 I1 n6 q( \9 N/ ?) A8 [3 y1 j/ m# Qmysql_select_db("geodatabase");  r( N2 W! K) ?9 W! S
    mysql_query("set names 'utf8'");       
    4 J" x$ T; U0 O. R1 Q5 W
    3 O1 ~7 d3 ^$ K$sql = "SELECT * FROM tb_xttj";- v& z5 S+ c0 B  w3 v0 J
    $result = mysql_query($sql);
    2 I0 c  _+ ~( M/ w8 C* F; J6 ^( y- R9 _
    $array = array();
    9 C: {/ t( N8 awhile($row=mysql_fetch_array($result)), \& `/ n( ?- W3 `
    {
    , t; g6 Q3 X' Y1 Q% p" d% u        $array[]=$row;//$array[][]是一个二维数组7 b) H5 ~4 @+ u2 \( t0 V
    } - b( g* l( z1 Y4 I3 i
    6 g. B+ f( X0 n
    问题1:这一步完全可以看做是整表查询,这种查询是大忌,对于这种小小的演示系统还可以,但是对大数据的系统,没有效率,至于如何改进,还得多学习才是。
    6 z  Y# W% _: {' ?+ w0 m# T, ?) K% I, p' v; J* _8 p
    求Leo与其他人的Cos值代码如下:0 }8 Y1 B& r% p- J4 f

    9 B1 \/ e3 Q0 \. i6 w/*
    % f* H, Y( w4 K! k! D  F: X7 w * 以下示例只求Leo的推荐,如此给变量命名我也是醉了;初次理解算法,先不考虑效率和逻辑的问题,主要把过程做出来  j3 T( q6 Q% J4 ]7 ~/ E! S
    */" B6 i3 N' R6 q5 @- w( l( @

    ' A* R0 V. r2 l3 J) `: A2 g1 K$cos = array();
    * i! L/ x/ b( Z; _& f# }% K$cos[0] = 0;
    5 T  ]3 R; A  y2 ^- |, o3 s+ J$fm1 = 0;. [$ H0 W0 F+ N! z! a
    //开始计算cos
    & Z* O9 d' R8 r! X//计算分母1,分母1是第一个公式里面 “*”号左边的内容,分母二是右边的内容# B! `& _9 {8 I, A8 C. C
    for($i=1;$i<9;$i++){4 ^9 Q5 ^" D& i( N: l
            if($array[5][$i] != null){//$array[5]代表Leo7 k: s3 ?5 {9 }
                    $fm1 += $array[5][$i] * $array[5][$i];
    8 H; a9 ?1 g0 F5 t  W        }8 Q, u( L0 ]. L6 m4 Z+ W7 O/ J% ^
    }; w# n8 h* V6 o+ O/ G

    " [2 j7 |7 w/ |' k- i$fm1 = sqrt($fm1);
    1 X# g/ p3 ]# y7 x. e9 O& f0 B# F2 Y$ L
    for($i=0;$i<5;$i++){
    6 n! ]9 S4 }! m5 a        $fz = 0;
    # U* i5 M& X$ u6 J( c+ x* ?1 u, E% q        $fm2 = 0;
      ?; Z2 i2 m" a7 e! V: f        echo "Cos(".$array[5][0].",".$array[$i][0].")=";
    1 D9 S9 ^6 E8 K7 R: b1 o& W" P1 i        5 v* H6 o2 x! I! ^
            for($j=1;$j<9;$j++){: c( _! J( D) p) N' M7 Y
                //计算分子: O3 W) q9 b3 t; _
                    if($array[5][$j] != null && $array[$i][$j] != null){
    0 r$ U2 D7 S* q- l8 s% G/ B                        $fz += $array[5][$j] * $array[$i][$j];
    0 w9 ~+ L+ k8 E- y8 O0 [5 i                }
    , n. q5 `" ~1 @1 Q0 p0 n: r( a9 N9 I                //计算分母21 v9 e* Y* d/ s& l; @
                    if($array[$i][$j] != null){# ~! D/ B' W( n4 G2 e) K. N  r
                            $fm2 += $array[$i][$j] * $array[$i][$j];. c* K# X" d1 V0 [/ h
                    }                       
    & z! C, d- W3 f, p        }+ b* }4 `. N* @0 S# z4 E8 _; I
            $fm2 = sqrt($fm2);3 a( U( Z5 e8 M( ~; K; s! E
            $cos[$i] = $fz/$fm1/$fm2;
    7 }( {  q/ {$ b! ^        echo $cos[$i]."<br/>";) Q+ J& W7 I# I7 v5 g2 g9 R
    }. U+ g( m7 {- [7 e) u! O

    ( n, [4 a0 H3 K7 S这一步得到的结果是酱紫:
    : }! o0 j, E1 h- r  u3 w0 p9 t% P1 ^8 k! Z! l3 X) U
    将求好的Cos值排序,采用快排代码如下(百度copy而来):0 ]; A- f* Z( Z' t

    , ^5 ~& j$ Y2 E) r6 F
    1 q' ?3 A+ S. D) F9 A! c//对计算结果进行排序,凑合用快排吧先7 V8 y+ y) a8 e
    function quicksort($str){& G5 ^6 y% h" w4 k
            if(count($str)<=1) return $str;//如果个数不大于一,直接返回
    7 d1 l: j* Q1 J( M* k        $key=$str[0];//取一个值,稍后用来比较;7 {# o7 G( y6 f7 g
            $left_arr=array();
    ; l2 _1 V# T$ j  E        $right_arr=array();
    2 l4 V' M; G( f, `: p) l       
    * ]( K; c1 X4 w, I9 R) D        for($i=1;$i<count($str);$i++){//比$key大的放在右边,小的放在左边;; D& z% r1 N8 p
                    if($str[$i]>=$key)& m, ^* a) L4 [" G4 H3 e& i
                    $left_arr[]=$str[$i];0 K% X# d" k, j) b, g
                    else4 e+ @6 `0 \, d, Y
                    $right_arr[]=$str[$i];
    / c! H# S; H% t& V6 q        }1 d9 K- t1 S9 ?3 E
            $left_arr=quicksort($left_arr);//进行递归;
    0 W  Q2 ?! M- L7 n        $right_arr=quicksort($right_arr);/ j& h6 ~0 l" U' k& _
            return array_merge($left_arr,array($key),$right_arr);//将左中右的值合并成一个数组;" w( Y/ p2 ^4 j) U4 \# V1 v; R
    }
    : c- A* g5 Q2 ^' A. g# N. a
    $ F; N- u8 {+ Q% G# i; E+ D$neighbour = array();//$neighbour只是对cos值进行排序并存储
    7 z  X6 l9 \3 `# q3 P+ F. x3 j* |. J$neighbour = quicksort($cos);/ g) E# Z% z5 ]
    % ~( F0 a( g/ E0 Y3 b

    # ^% X, B6 W& c0 G$ t这里的$neighbour数组仅仅存储了从大到小排序好的Cos值,并没有与人联系起来。这个问题还要解决。- T8 l' H( x4 ^! d7 f; U

    # B4 b8 Z  z  Z, N# C选出Cos值最高的3个人,作为Leo的邻居:* R! }$ }; }6 j3 q  U

    5 o5 W! X6 Q  V& w//$neighbour_set 存储最近邻的人和cos值2 i. w+ z/ D/ T- m  n
    $neighbour_set = array();" c3 g: p; s( Z4 ^2 B7 L3 t
    for($i=0;$i<3;$i++){
    : g) \& n1 {0 l; \& M        for($j=0;$j<5;$j++){3 ]9 ~# p, N4 N  A5 k: y) k3 C
                    if($neighbour[$i] == $cos[$j]){
    ) x1 R$ e' _; E" F                        $neighbour_set[$i][0] = $j;
    9 X/ s1 L) S7 D& a/ Y3 P; B                        $neighbour_set[$i][1] = $cos[$j];( }1 [2 l+ ?# l6 M3 ?
                            $neighbour_set[$i][2] = $array[$j][6];//邻居对f的评分9 v; i5 s  k% x: ?+ r. j/ z: X
                            $neighbour_set[$i][3] = $array[$j][7];//邻居对g的评分
    . Q  K: \3 F% ]) r8 S; F) u                        $neighbour_set[$i][4] = $array[$j][8];//邻居对h的评分0 _) G3 f0 R6 E5 c7 n
                    }
    * r, Z9 @, W4 T9 q7 d# y0 t2 K        }
    6 {( b9 N; G4 x}
    ) i7 b& e' J/ |( nprint_r($neighbour_set);/ t. a& I$ Z) r- {' }. E
    echo "<p><br/>";7 O3 O3 O7 a2 x  N0 u
    5 C- N" A4 C$ r7 x0 l( Z' L
    这一步得到的结果是酱紫:" m5 S$ O9 y( K- A9 ~+ @
    $ B+ c( S% J0 `& e2 I

    , f% O3 _* l1 L! `: X* u& S8 j# |( z! }$ O5 k5 v$ r) R
    转存失败重新上传取消
    0 k# w! i8 J0 U! R
    3 B  g4 v8 I, ~5 b% W! p& t这是一个二维数组,数组第一层的下标为0,1,2,代表3个人。第二层下标0代表邻居在数据表中的顺序,比如Jhon是表中的第0个人;下标1代表Leo和邻居的Cos值;下标2,3,4分别代表邻居对f,g,h的评分。5 b& o2 C; W( u6 m3 L# U# h
    0 n; u1 y/ O" b
    开始进行预测,计算Predict代码如下:
    . K, @8 S* ?. A5 `4 g* ?6 s; [) w, g0 \# w. S! e
    我是分别计算Leo对f,g,h的预测值。在此有一个问题,就是如果有的邻居对f,g,h的评分为空,那么该如何处理。比如Jhon和Mary对h的评分就为空。本能的想到用if判断一下,如果为空则跳过这组计算,不过这样处理是否合理,有待考虑。以下代码并没有写出这个if判断。5 R) j; x6 |( K/ x/ M% t0 W& l+ C

    2 u# Q( V8 r- H) G; W//计算Leo对f的评分# A: o. e  M0 v
    $p_arr = array();( j" D- G. Q: a
    $pfz_f = 0;
    - Z% O# F4 l1 \% |$pfm_f = 0;
    9 [& L+ w- T3 M8 l9 Y; k% Cfor($i=0;$i<3;$i++){
    ( V1 D) X, c) e* J0 a& T" E        $pfz_f += $neighbour_set[$i][1] * $neighbour_set[$i][2];
    1 I5 y) P0 E- |. p5 p% u+ ~' ]        $pfm_f += $neighbour_set[$i][1];
    9 C, H* m7 p0 R) S  G}/ {" S2 S# n/ [9 E/ r) j" E! {
    $p_arr[0][0] = 6;  i7 p8 Q# |9 E0 D$ M; w2 S
    $p_arr[0][1] = $pfz_f/sqrt($pfm_f);
    . x, [* t& @0 p+ I! f  d' Q9 Cif($p_arr[0][1]>3){
    3 Z) f! q7 n9 o) y% V* S        echo "推荐f";
    4 f0 O( Y* Y7 I}, Y" x& a! B* b! B4 p

    & W" N+ K3 [( C8 m4 D* ~. ^5 B//计算Leo对g的评分
    ! n- @* t0 a0 D5 ~$ ^# `$pfz_g = 0;& j6 s- C5 x1 h2 @$ u/ e" v
    $pfm_g = 0;
    / Q9 m8 q. x& Y" G4 z+ f% I8 Hfor($i=0;$i<3;$i++){
    " {0 I; p# F( |1 V        $pfz_g += $neighbour_set[$i][1] * $neighbour_set[$i][3];
    - ^, @' f0 {$ L; t- C' R        $pfm_g += $neighbour_set[$i][1];
    ( L8 |# x( [6 J5 k& }        $p_arr[1][0] = 7;" j, w$ ?+ M/ R. o% S
            $p_arr[1][1] = $pfz_g/sqrt($pfm_g);( @( N7 w3 X1 i' d
    }
    6 L' @  o! N) O% l- Y& zif($p_arr[0][1]>3){. |: B8 a" n, [' Q
            echo "推荐g";
    6 P6 C% ]& e4 ]2 ~* t9 \2 Z/ h}; ?: f0 c# w( e1 P+ L% B8 U
    8 h/ h/ H) O/ o* a
    //计算Leo对h的评分3 U9 z  u" z" N/ H4 }! j: O
    $pfz_h = 0;
    7 T6 w1 _0 o2 w9 f, |9 b5 C$pfm_h = 0;
    ( a  k7 D) ?  G  ^! V. hfor($i=0;$i<3;$i++){1 [9 b$ ]8 |9 a7 Z" G8 q
            $pfz_h += $neighbour_set[$i][1] * $neighbour_set[$i][4];9 Q, _" ^6 r- O( R" B! _
            $pfm_h += $neighbour_set[$i][1];
    8 k8 }& h4 {- ~  W! `! r        $p_arr[2][0] = 8;
    2 ~. r# a1 o4 Q* _; i# g6 n        $p_arr[2][1] = $pfz_h/sqrt($pfm_h);* s* S7 j% C. h5 I
    }% |* g  g, n0 R/ O
    print_r($p_arr);: Z* d. \- y; V* F2 Q$ ^; r
    if($p_arr[0][1]>3){8 D+ p% |0 L# Y0 J7 o
            echo "推荐h";& ?6 C+ K4 D- V  G! @$ C
    }3 a, ~3 V8 Q( @+ }$ N" R0 Y2 r5 Q
    # n+ X0 x. v' c
    $p_arr是对Leo的推荐数组,其内容类似如下;
    * c, C& [# E! K- m6 C
    4 h4 I. s% y4 f) O+ G  RArray ( [0] => Array ( [0] => 6 [1] => 4.2314002228795 ) [1] => Array ( [0] => 7 [1] => 2.6511380196197 ) [2] => Array ( [0] => 8 [1] => 0.45287424581774 ) )% }  S, T. {" g  b7 A
    3 D# D1 ?9 Y3 M3 W; h( W
    f是第6列,Predict值是4.23,g是第七列,Predict值是2.65........
    - M3 Z: n9 b* @( A5 V8 R6 o
    9 @. @; H: b7 O3 |7 j" R求完了f,g,h的Predict值后有两种处理方式:一种是将Predict值大于3的物品推荐给Leo,另一种是将Predict值从大到小排序,将Predict值大的前2个物品推荐给Leo。这段代码没有写。, `$ U4 F* O# y1 I. X) j  m
    : }5 u8 f$ k" o! z5 ~- [1 r' F
    从上面的示例中可以看出,推荐算法的实现非常麻烦,需要循环,判断,合并数组等等。如果处理不当,反而会成为系统的累赘。在实际处理中还有以下问题:
    2 x& ?- g' v- j  x! F- F$ ?
    - j' s& Z% w% z( ?( G1 N1.以上示例我们只对Leo进行推荐,而且我们已经知道Leo没有评价过f,g,h物品。如果放到实际的系统里,对于每一个需要进行推荐的用户,都要查询出他没有评价过哪些物品,这又是一部分开销。
    2 Y+ P/ k1 p: b/ ~2 @8 e3 ^# k: t, _$ i+ n8 C  F/ b6 e! A
    2.不应当进行整表查询,在实际系统中可以设定一些标准值。比如:我们求Leo与表中的其他人的Cos值,如果该值大于0.80,则表示可以为邻居。这样,当我找到10个邻居之后,就停止求Cos值,避免整表查询。对于推荐物品也可以适当采用此方法,比如,我只推荐10个物品,推荐完后就停止求Predict值。2 |4 h/ n# Y3 @

    : r; L0 ]# P) Y3.随着系统的使用,物品也会发生变化,今天是fgh,明天没准就是xyz了,当物品变化时,需要动态的改变数据表。
    5 Y# Y0 V* ~/ X% Y" K! P6 |; n& |( W+ i" E  ~
    4.可以适当引进基于内容的推荐,来完善推荐算法。3 ]# Y( m- w) M- d9 u, O$ F: s

    # d5 K3 b+ D+ k2 B- E% s5.推荐的精确性问题,这个设置不同的标准值,会影响精确性。
    2 z- T! x  }8 f; e0 m" j————————————————
    . x! V% S  d" [7 m# B) p版权声明:本文为CSDN博主「星斗其文,赤子其人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。! X5 c3 _- t& J6 [* g
    原文链接:https://blog.csdn.net/liuliuhelingdao/article/details/126715465  ^1 K6 |# b8 T- y1 E
    ! o2 u8 r; p0 i5 w3 K# a3 {

    * V" W0 H' W! X8 A2 W3 X' P
    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-16 00:20 , Processed in 0.413382 second(s), 51 queries .

    回顶部