QQ登录

只需要一步,快速开始

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

    2 V( m  _8 {* D! t: wphp+mysql实现简单的协同过滤推荐算法0 L# o7 j  q- N' H$ [5 B; G2 G
    仅做标记。。。  J& h; [  v. ^9 c8 ^3 H" _5 }/ f
    # ?, P; K  v* b: u# I: g

    4 @7 o+ D. Q; `( |- B' c
    5 i6 I( @. y7 I; p9 t. i要实现协同过滤推荐算法,首先就要理解算法的核心思想和流程。该算法的核心思想可以概括为:若a,b喜欢同一系列的物品(暂时称b是a的邻居吧),则a很可能喜欢b喜欢的其他物品。算法的实现流程可以简单概括为:1.确定a有哪些邻居 2.通过邻居来预测a可能会喜欢哪种物品  3.将a可能喜欢的物品推荐给a。6 [4 k. x" H! m# F3 ]+ f
    / U7 q: A" M" K/ x0 N
    算法核心的公式如下:
    ' g9 M, N$ B. t0 X- L5 `3 {
    + |& b: _% {/ d6 b9 V" R" V1.余弦相似度(求邻居):+ P. j) Q, R9 l$ D/ d
    ! K$ l/ C3 [% J! |7 @
    2.预测公式(预测a可能会喜欢哪种物品):: P5 S8 P7 g, Y0 A. y8 T7 R
    ; t. h( e# b& |) l
    仅从这两个公式我们就可以看出,仅仅是按照这两个公式进行计算,就需要进行大量的循环与判断,而且还涉及到排序的问题,就涉及到排序算法的选择与使用,这里我选快排,从网上copy了一段快排,直接用。总之实现起来很麻烦,在大数据情况下,更何谈效率。- S' x- f. a) d% c
    - i$ W, G; |/ h4 u; f3 {
    首先建表:
    9 C; ~( ]$ i, K, Q+ t7 O9 l5 b6 a9 y! o  V: x9 a, i0 ?) a
    DROP TABLE IF EXISTS `tb_xttj`;& I* h! {/ z  T/ e2 T! ?  ?: d# v
    CREATE TABLE `tb_xttj` (
    ; Y' G, Y! q4 k# F4 P- N  `name` varchar(255) NOT NULL,
    5 I% t8 n- X8 v9 \: ~* S  `a` int(255) default NULL,, g2 S' E- \% H7 K
      `b` int(255) default NULL,: q! I$ v- j  [9 g" `
      `c` int(255) default NULL,* l& W  P1 o  r# y4 b
      `d` int(255) default NULL,7 c/ J) t& b2 ]
      `e` int(255) default NULL,+ c- t; `. a& o% _/ B5 \! H7 |
      `f` int(255) default NULL,+ s5 y6 ~3 r* h% a, S2 y
      `g` int(255) default NULL,5 D! k( u" `; g& M2 g
      `h` int(255) default NULL,
    9 `5 }: i7 ?  c6 j* G- }) j1 D9 k  PRIMARY KEY  (`name`)3 e& n+ p* A+ E+ n
    ) ENGINE=MyISAM DEFAULT CHARSET=latin1;- V) x: |/ B4 R, n$ _

    6 x6 G6 L& p9 }7 f0 K+ KINSERT INTO `tb_xttj` VALUES ('John', '4', '4', '5', '4', '3', '2', '1', null);
    7 a+ b, W+ T4 G2 \- Q' Z  tINSERT INTO `tb_xttj` VALUES ('Mary', '3', '4', '4', '2', '5', '4', '3', null);0 ?9 F5 u* |; q6 q9 H7 N( I2 [
    INSERT INTO `tb_xttj` VALUES ('Lucy', '2', '3', null, '3', null, '3', '4', '5');
    4 G/ W- w+ k$ t( M! L9 RINSERT INTO `tb_xttj` VALUES ('Tom', '3', '4', '5', null, '1', '3', '5', '4');! F# X" i2 B. ]7 X+ _: {
    INSERT INTO `tb_xttj` VALUES ('Bill', '3', '2', '1', '5', '3', '2', '1', '1');, ^1 x7 f6 v' z9 n7 t2 U
    INSERT INTO `tb_xttj` VALUES ('Leo', '3', '4', '5', '2', '4', null, null, null);
    . S- N( i: k* V0 N' v3 B3 A
    & X$ p+ w- [& M3 k( c+ z- M8 s/ q1 [4 y0 Q3 k
    我这里只对最后一行的Leo进行推荐,看看f,g,h哪个可以推荐给他。) [9 F$ l. }( I) j
    " g. Z1 G1 k6 y/ J0 `0 x- ]7 `
        用php+mysql,流程图如下:3 E8 M9 @% P$ B
    $ i0 z, a; K1 I2 P% ~9 ?
    连接数据库并将其存储为二维数组的代码如下:7 I- c3 t  `$ f
    2 n( X* A( Y- }2 X: U/ p
    header("Content-Type:text/html;charset=utf-8");3 S9 _2 ]' I! d

    ; ]" E3 ~) v7 M( A  G* v2 t6 Imysql_connect("localhost","root","admin");9 G1 m" \; b7 X; a0 z
    mysql_select_db("geodatabase");4 P/ c) t) F5 r# |. R3 H5 ^
    mysql_query("set names 'utf8'");        - a1 v8 A% l1 ^/ H# d* }

      P0 I, r) g5 W7 ^) \$sql = "SELECT * FROM tb_xttj";
    . l$ \2 J& X% h! D$result = mysql_query($sql);6 ^) h4 j6 `2 O

      q" K7 p. `. q# Y* B+ b0 W$array = array();
    " {4 c5 B1 C! C8 o! A. Ewhile($row=mysql_fetch_array($result))
    ( J4 ^6 j% Q- w$ O) s: W{
    , |1 `' {; k7 A5 |% z" O% L        $array[]=$row;//$array[][]是一个二维数组
    4 s" e- z6 [3 }7 C  g& |} 3 U2 @: x* G2 }5 h# W$ g

      z' K. w+ D2 s% m6 G7 h- m2 ~3 o问题1:这一步完全可以看做是整表查询,这种查询是大忌,对于这种小小的演示系统还可以,但是对大数据的系统,没有效率,至于如何改进,还得多学习才是。
    1 e: a: ^/ I" o* A
    9 R; f2 e6 {$ c. s& t! F求Leo与其他人的Cos值代码如下:
    * o/ o9 s: _9 E% R0 O. b5 M8 h8 D1 `, e9 g
    /*& t3 t; ?0 ^, r" C
    * 以下示例只求Leo的推荐,如此给变量命名我也是醉了;初次理解算法,先不考虑效率和逻辑的问题,主要把过程做出来
    6 M) j, m& v7 ~, E/ Z- v */0 f$ [* H6 z7 ?6 Y: `4 a" j' l3 d* z

    6 O& a0 }1 t1 v+ }  Q3 W' e! k$cos = array();
    % h6 t8 Z' H% Q/ C( i6 F' m$cos[0] = 0;
    + d; d. g/ w5 G& I9 P4 d$fm1 = 0;
    / M# o, p8 P- N6 G- [) f! L; v; X//开始计算cos
    9 X) d/ V8 ?6 ?//计算分母1,分母1是第一个公式里面 “*”号左边的内容,分母二是右边的内容  i& k0 q8 h" G/ L' J
    for($i=1;$i<9;$i++){
    ! `' J3 u7 A4 h) u% [. M5 R/ B        if($array[5][$i] != null){//$array[5]代表Leo
    7 D. ?! n1 Y1 h8 x                $fm1 += $array[5][$i] * $array[5][$i];1 [& k9 X2 E. d8 t6 S
            }4 a8 d" x) V4 q4 P
    }
    # L& y& }, ?0 ?( V' l( J: a: u* D* a
    $fm1 = sqrt($fm1);
    : o& d( {2 j4 `3 D( K2 Y0 b
    ) ^4 s5 t0 ]: x; W: R& Rfor($i=0;$i<5;$i++){
    2 F) i6 p$ o( \5 a3 y! A+ W        $fz = 0;
    : c- b8 U, h5 r1 A1 d2 N        $fm2 = 0;- n3 P& n. M' j$ y# y3 N
            echo "Cos(".$array[5][0].",".$array[$i][0].")=";
      _+ f0 s4 ]( T8 t        6 {# y/ u: h8 R' ?& ~
            for($j=1;$j<9;$j++){
    9 v. K+ }8 c3 p$ {% W9 o            //计算分子8 {# Q8 t0 z  q, Z# U( D- T
                    if($array[5][$j] != null && $array[$i][$j] != null){
    / V3 ?8 m& |, f. Y                        $fz += $array[5][$j] * $array[$i][$j];
    : M1 L' A" u4 E# Q) I# Z! p                }
    9 m2 m5 Y' _9 q6 C. w( @8 v                //计算分母2
    2 g# y) g' o0 B8 G                if($array[$i][$j] != null){% r% }% R& J" j( b
                            $fm2 += $array[$i][$j] * $array[$i][$j];
    8 s- f, n* }% M                }                       
    4 V( ~* ]1 Z# V' x; y+ B5 [9 W        }. H3 |7 t2 d7 ~! a+ q7 s2 P0 o$ W
            $fm2 = sqrt($fm2);
    8 W& |5 y* z3 X; z9 w6 l2 Y        $cos[$i] = $fz/$fm1/$fm2;- D0 {  o6 D1 x& G. L
            echo $cos[$i]."<br/>";4 ?, @7 b0 P! n8 y1 q, V/ ^* B- v5 W
    }
    7 D, K# r, z* O' j3 B2 X7 @) p$ J+ Z& D, P3 ^5 ?$ W1 v2 m+ L
    这一步得到的结果是酱紫:
    , G7 X, q* K( I: B& i2 q
    ! P- Q3 k- _$ g/ U/ n% A将求好的Cos值排序,采用快排代码如下(百度copy而来):6 v" m; r& u! [0 ^& k  e2 r' V- z
    ! _' ]) J( t% z% \1 d0 F

    & \2 T% s. V. ~3 ?) s8 `//对计算结果进行排序,凑合用快排吧先
    7 ^- }: a0 q% Y5 x+ H% K' gfunction quicksort($str){
    2 u/ L1 u, J/ h8 @, |; N# v  s, `        if(count($str)<=1) return $str;//如果个数不大于一,直接返回! y- s' V% e  `" v5 t# i& F' T
            $key=$str[0];//取一个值,稍后用来比较;
    ' ~- t4 q# v1 m' K  `0 p9 ~2 w1 G        $left_arr=array();
    ! U6 [: e5 U" o, p2 m        $right_arr=array();
    4 r3 a9 ^& Z  O8 b8 T0 p. O        ; X# a9 b+ R/ |% m) m! n2 ~& h$ \
            for($i=1;$i<count($str);$i++){//比$key大的放在右边,小的放在左边;, m( ~- h& ^4 M* y
                    if($str[$i]>=$key)
    ( v- I3 h% o4 v+ T) @5 z& w- E' |& Z                $left_arr[]=$str[$i];
    8 |( f. y' @( @- X7 o% o                else
    # g5 i  y: k; c  y                $right_arr[]=$str[$i];
    ; N& j0 d$ O; r4 q* C( G3 i        }
    2 G1 ]" H4 b& F" B" ^# ~/ a        $left_arr=quicksort($left_arr);//进行递归;/ P  `0 a, Y# ~# S4 T
            $right_arr=quicksort($right_arr);# Y$ j9 F: c2 ~# }, _4 K
            return array_merge($left_arr,array($key),$right_arr);//将左中右的值合并成一个数组;( ]' v0 ~4 {4 L* F1 r9 {& O
    }
    " R( r' A" V* C9 M; b
    ! a2 [; B3 f& D7 a% H* w* D4 W$neighbour = array();//$neighbour只是对cos值进行排序并存储1 R. e6 s: X3 H
    $neighbour = quicksort($cos);
    1 j. ?" z& `3 V5 n3 [+ h- _( S+ V# U" P+ t6 I8 A

    # X( |2 p' F7 b这里的$neighbour数组仅仅存储了从大到小排序好的Cos值,并没有与人联系起来。这个问题还要解决。
    8 R; k( C& h$ Y6 c% m: k4 B; ]+ _! H8 I! ?% F. t8 i) D& S
    选出Cos值最高的3个人,作为Leo的邻居:3 M$ s+ U( c5 \3 w7 O5 P

    1 {+ T; I7 G3 I9 O. f0 K* h//$neighbour_set 存储最近邻的人和cos值+ C- r! ~* ^0 M% B- h
    $neighbour_set = array();
    ; a( T/ {8 m1 n- Tfor($i=0;$i<3;$i++){
    5 J1 Z& u  `+ C/ y7 d' ^        for($j=0;$j<5;$j++){, X  u" C5 j- [, l- G4 v$ U
                    if($neighbour[$i] == $cos[$j]){
    - q& Q1 j1 V- [/ `" L# `, m4 B                        $neighbour_set[$i][0] = $j;! g% ~6 [8 D; [* q. C$ `* g
                            $neighbour_set[$i][1] = $cos[$j];& u" M$ P$ \. p- [
                            $neighbour_set[$i][2] = $array[$j][6];//邻居对f的评分! T( _  y& Y' \) Z7 A
                            $neighbour_set[$i][3] = $array[$j][7];//邻居对g的评分
    , a% d! @2 _3 m& O                        $neighbour_set[$i][4] = $array[$j][8];//邻居对h的评分) D0 k1 X& n& U4 P
                    }4 B* Q, ?: j  |$ a1 T! i: A% N4 l
            }/ e$ K# y9 I4 G  y
    }
    , k3 n& \1 T: F8 q, Q- dprint_r($neighbour_set);
    & @/ t  m  S. \echo "<p><br/>";! v$ C0 r( k: ?* X

    , U$ a5 @8 [" v) |& P, S3 D这一步得到的结果是酱紫:
      ~- D. _  [: W. D5 @8 n3 ~6 t, R/ b9 a/ k4 E3 V
    & d5 [' U0 q4 c8 f

    6 `( R& Z. W! |转存失败重新上传取消5 s0 D/ O( I6 u

    1 ?0 c6 }3 w( f. }  M2 _这是一个二维数组,数组第一层的下标为0,1,2,代表3个人。第二层下标0代表邻居在数据表中的顺序,比如Jhon是表中的第0个人;下标1代表Leo和邻居的Cos值;下标2,3,4分别代表邻居对f,g,h的评分。
    / t% g7 N+ q: j8 E6 V6 P, O
    4 M! t" a7 T6 w2 v6 R$ }) p开始进行预测,计算Predict代码如下:: O& Y: G# G& A2 ^, w. d( ~3 x

    / t3 |' [8 P! W5 `, X我是分别计算Leo对f,g,h的预测值。在此有一个问题,就是如果有的邻居对f,g,h的评分为空,那么该如何处理。比如Jhon和Mary对h的评分就为空。本能的想到用if判断一下,如果为空则跳过这组计算,不过这样处理是否合理,有待考虑。以下代码并没有写出这个if判断。
      Z2 F5 N; _2 C  s& `: a0 J' g3 \" ~; s2 j7 u4 @2 {$ J
    //计算Leo对f的评分
      t" d' J% S8 b5 d6 _7 J$p_arr = array();1 k7 F- g- T5 W7 x) U& G* y
    $pfz_f = 0;- c- t4 E% L; }
    $pfm_f = 0;) z) k! s4 h4 t% Z1 P4 ^! Y
    for($i=0;$i<3;$i++){
    4 x+ Y( f$ k$ a: D! Q        $pfz_f += $neighbour_set[$i][1] * $neighbour_set[$i][2];5 ]; k! O. v5 o, b$ i* j
            $pfm_f += $neighbour_set[$i][1];
    ) C5 l! ~1 X$ R}+ N+ i# s3 H* K. G6 U: I" E
    $p_arr[0][0] = 6;
    3 a! i7 u! H4 T9 u; R9 W0 O$p_arr[0][1] = $pfz_f/sqrt($pfm_f);8 l  T+ t( J- F2 b
    if($p_arr[0][1]>3){& U8 n# p" Q6 F! s! A5 G# K
            echo "推荐f";: I2 I4 V8 q! c) }. }" A* w
    }
    0 G8 C1 y' m1 H* p4 b3 P8 T' g9 ^: f5 v3 M! X( A+ X0 W
    //计算Leo对g的评分
    ' f% |% q3 K/ }$pfz_g = 0;
    $ R: l0 h3 G+ j$pfm_g = 0;
    * F# B- \( _) l$ n5 Z; S, B' n, bfor($i=0;$i<3;$i++){
    0 Q' t8 |+ ]; o4 ]        $pfz_g += $neighbour_set[$i][1] * $neighbour_set[$i][3];
    $ m. w* w8 E0 I" X        $pfm_g += $neighbour_set[$i][1];
    3 e' L. D8 P8 k( P$ [        $p_arr[1][0] = 7;
    . W% J  |! }5 Y# q4 W        $p_arr[1][1] = $pfz_g/sqrt($pfm_g);
    1 F. ?. ~" ]: \; R8 x}
    % ?( r, Y, k- p8 u1 u2 x  Jif($p_arr[0][1]>3){7 p1 e4 c; `$ y
            echo "推荐g";8 g: Q: ^) G7 B0 Z
    }# \5 t9 i, ^6 z* j

    $ x, U# e& z8 \8 K, h7 X//计算Leo对h的评分
    ( e4 d$ j! U$ n; b4 y$pfz_h = 0;" B4 Z( \4 g$ |5 i7 N. s8 O
    $pfm_h = 0;
    3 m+ L8 \: g) T. H% \; U/ jfor($i=0;$i<3;$i++){
    % u* P$ z  Y5 C- g/ z) f2 }5 a        $pfz_h += $neighbour_set[$i][1] * $neighbour_set[$i][4];
    + P; }; [+ ~& @- @$ M- i        $pfm_h += $neighbour_set[$i][1];9 P9 A8 e5 U( m- k8 j, S
            $p_arr[2][0] = 8;2 F" Y* r! M! J/ F0 {  N7 y( b
            $p_arr[2][1] = $pfz_h/sqrt($pfm_h);
    % i/ q7 i+ W9 p1 e}" N. {' _  L: T  ?; i0 E
    print_r($p_arr);
    - M4 Y* R  f+ P% A- xif($p_arr[0][1]>3){
    6 [7 u/ f$ G6 P        echo "推荐h";* ?- N/ t' m7 `. n; W
    }
    ; g% B, ?7 Y3 H
    6 o1 M+ A/ G" a" o1 g7 u$p_arr是对Leo的推荐数组,其内容类似如下;
    * q9 o9 r8 S8 [+ ^, D& D4 ~7 N
    3 l, n( t. `: b. R4 D7 R6 E: }Array ( [0] => Array ( [0] => 6 [1] => 4.2314002228795 ) [1] => Array ( [0] => 7 [1] => 2.6511380196197 ) [2] => Array ( [0] => 8 [1] => 0.45287424581774 ) )! G$ V/ z5 r2 u  k# I+ d
    # S6 ~7 \1 ~. h" k2 R# w
    f是第6列,Predict值是4.23,g是第七列,Predict值是2.65........" \7 `2 V5 o5 E5 h9 {

    9 O6 v6 B4 ]: o2 W3 U求完了f,g,h的Predict值后有两种处理方式:一种是将Predict值大于3的物品推荐给Leo,另一种是将Predict值从大到小排序,将Predict值大的前2个物品推荐给Leo。这段代码没有写。
      W. a$ h! ~" [- E% ]- F0 A' @, @; u+ K3 X
    从上面的示例中可以看出,推荐算法的实现非常麻烦,需要循环,判断,合并数组等等。如果处理不当,反而会成为系统的累赘。在实际处理中还有以下问题:
    : ~+ }$ Q; k5 P* a; C0 M
    2 m) f7 Q. d! b2 h1 b7 m1.以上示例我们只对Leo进行推荐,而且我们已经知道Leo没有评价过f,g,h物品。如果放到实际的系统里,对于每一个需要进行推荐的用户,都要查询出他没有评价过哪些物品,这又是一部分开销。
    ' R  k# ]6 w; B, k& d; p0 a1 k' I$ `; P) w  C8 ~% K& Y* Q
    2.不应当进行整表查询,在实际系统中可以设定一些标准值。比如:我们求Leo与表中的其他人的Cos值,如果该值大于0.80,则表示可以为邻居。这样,当我找到10个邻居之后,就停止求Cos值,避免整表查询。对于推荐物品也可以适当采用此方法,比如,我只推荐10个物品,推荐完后就停止求Predict值。
    - z5 I7 O2 i! o8 p5 t% f' ]. V' `0 w" d
    3.随着系统的使用,物品也会发生变化,今天是fgh,明天没准就是xyz了,当物品变化时,需要动态的改变数据表。* p& [2 \% r: h% o1 X" }$ [1 Q' I. p

    ; B2 h$ N1 ?; m0 q: D4.可以适当引进基于内容的推荐,来完善推荐算法。
    . N8 ?5 I* C8 S  S9 A$ R
    ( I; u  q: V5 l- ^3 \5.推荐的精确性问题,这个设置不同的标准值,会影响精确性。
    # D7 \, S' C" ?4 q4 r————————————————
    & v  g( I* {6 a( y; ~$ X' }' [* G3 c版权声明:本文为CSDN博主「星斗其文,赤子其人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    & \- w# J+ z' w6 y/ y1 L: C- A6 |原文链接:https://blog.csdn.net/liuliuhelingdao/article/details/126715465$ D& o9 k- b% Q1 C6 t1 [: y

    ' M# s  e+ b4 X( a' @0 H& a$ c$ D2 ~  h2 G% i1 ]+ I+ F5 m
    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 15:32 , Processed in 0.592151 second(s), 51 queries .

    回顶部