QQ登录

只需要一步,快速开始

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

    , E* D1 R5 q9 g: P! [* }php+mysql实现简单的协同过滤推荐算法
    3 S4 y- B4 F, ^1 f仅做标记。。。
    / x/ `: k- O+ N% i6 F) ^' S$ {4 _2 T! R5 r& h( @" o
    , T$ Z) F( g. E/ r# n
    $ J& W0 K4 U6 ?/ [' o
    要实现协同过滤推荐算法,首先就要理解算法的核心思想和流程。该算法的核心思想可以概括为:若a,b喜欢同一系列的物品(暂时称b是a的邻居吧),则a很可能喜欢b喜欢的其他物品。算法的实现流程可以简单概括为:1.确定a有哪些邻居 2.通过邻居来预测a可能会喜欢哪种物品  3.将a可能喜欢的物品推荐给a。
    0 P9 i) V* S5 l3 @/ c% i$ `( c5 y9 o/ c: ^' d
    算法核心的公式如下:4 a: m+ r. _; |  h4 }
    9 ~  s- H, n0 W' R7 w% [
    1.余弦相似度(求邻居):3 w! g& i3 M' N$ b
    6 Q# w8 |7 n! L) _: X4 i" P
    2.预测公式(预测a可能会喜欢哪种物品):# U1 n' c8 r* L* G

    5 ]6 Z2 W" `  c: L$ w仅从这两个公式我们就可以看出,仅仅是按照这两个公式进行计算,就需要进行大量的循环与判断,而且还涉及到排序的问题,就涉及到排序算法的选择与使用,这里我选快排,从网上copy了一段快排,直接用。总之实现起来很麻烦,在大数据情况下,更何谈效率。
    3 f! g! M: J* t( {$ K/ B8 ?5 l0 o1 Q3 I
    0 [* C2 F" \0 Y4 W首先建表:
    " z" u- Y, l# _4 h8 t3 f# Z3 ~( Z6 F4 Q
    DROP TABLE IF EXISTS `tb_xttj`;' r$ X. x& z! U& R$ v
    CREATE TABLE `tb_xttj` (, q3 b( J2 c1 v. U# S
      `name` varchar(255) NOT NULL,( J" s# X/ ]! ?) b0 ^
      `a` int(255) default NULL,
    ' c4 X: F+ u" ^$ W+ D, _6 b$ Y  `b` int(255) default NULL," K( ~% i0 n# {. j: p
      `c` int(255) default NULL,# e7 b( S" f0 X9 Z7 t
      `d` int(255) default NULL,1 P5 V$ O8 @2 m( F7 m- {* y
      `e` int(255) default NULL,4 U# E) J0 M& |  v! U
      `f` int(255) default NULL,! W8 _# |" Z! d: X% y6 {
      `g` int(255) default NULL,
    * T3 m6 f7 P7 ?" ]  `h` int(255) default NULL,- d9 U* [. c4 ~3 x- F4 u
      PRIMARY KEY  (`name`)0 i. z0 w5 D6 J" Z: A# E  J' @
    ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
    0 `8 H: B0 S+ p3 |" M& z0 k# w- Z( w; V. k# T$ S$ \0 _% A$ q7 K
    INSERT INTO `tb_xttj` VALUES ('John', '4', '4', '5', '4', '3', '2', '1', null);
    7 ?% l% V: t8 YINSERT INTO `tb_xttj` VALUES ('Mary', '3', '4', '4', '2', '5', '4', '3', null);
    3 s& v+ }! r8 j/ F& WINSERT INTO `tb_xttj` VALUES ('Lucy', '2', '3', null, '3', null, '3', '4', '5');. X; W. X+ E9 \
    INSERT INTO `tb_xttj` VALUES ('Tom', '3', '4', '5', null, '1', '3', '5', '4');
    % U- l" G: k8 _; U; w% RINSERT INTO `tb_xttj` VALUES ('Bill', '3', '2', '1', '5', '3', '2', '1', '1');% |% _; c( m4 \7 k# s! u# r) Z; B
    INSERT INTO `tb_xttj` VALUES ('Leo', '3', '4', '5', '2', '4', null, null, null);
    , I* J% n. H6 `! G( e, M- M- b6 T% V4 w% U
    7 v2 r# v% e& D5 h2 h+ `
    我这里只对最后一行的Leo进行推荐,看看f,g,h哪个可以推荐给他。5 S6 Q) x  E6 h, A
    ( E3 i: {! j# x- Z6 E9 |  X( o8 E
        用php+mysql,流程图如下:( y, h  f' _7 U0 b" I9 O7 W$ _( N

    " w1 z+ Q) ?4 L+ B连接数据库并将其存储为二维数组的代码如下:1 v, U: I6 g8 q3 d
    * Q: U& Q  Y) L" B1 ^- R/ m
    header("Content-Type:text/html;charset=utf-8");
    9 T  [9 t" D5 m0 r. f8 l- L
    1 K" H9 u/ T2 g& h$ B4 {mysql_connect("localhost","root","admin");
    ) f# C2 G5 ^/ Z5 v4 h# ]  dmysql_select_db("geodatabase");( Z; C( \% W  w
    mysql_query("set names 'utf8'");        & f) @4 \. Y  q% s
    $ |9 n9 c  l) a. X8 Z
    $sql = "SELECT * FROM tb_xttj";
    9 z5 v% n6 u0 e/ N4 R. P$result = mysql_query($sql);2 Z. k& [$ t) b+ p

    * s5 r- [* l" q2 i5 D$array = array();/ Z& P. Y6 D9 K" Y4 j- t) v  T
    while($row=mysql_fetch_array($result))% b* ^# k: {& Q4 s0 X5 O" A
    {
    * x  t& U, G8 d7 e        $array[]=$row;//$array[][]是一个二维数组' D& P7 R" M9 ^' t7 L9 s1 q! R
    } 1 `, i" T1 h% p: B
    8 c! [, n& K: O- [' E) v) T% d) f
    问题1:这一步完全可以看做是整表查询,这种查询是大忌,对于这种小小的演示系统还可以,但是对大数据的系统,没有效率,至于如何改进,还得多学习才是。
    . q# D( P5 }2 D5 K) r0 \3 h: H% j9 C% ?" C, ^7 T
    求Leo与其他人的Cos值代码如下:4 u# L1 V, i& Q0 u& p

      f7 i  u; ]# ~/*
    ( W, L4 S3 F2 L9 d0 n3 P$ F5 _ * 以下示例只求Leo的推荐,如此给变量命名我也是醉了;初次理解算法,先不考虑效率和逻辑的问题,主要把过程做出来
    7 K. B4 ~9 E  R1 j9 B' w2 Q */: g( i' u1 d, [. _& u5 `

    $ ^/ g- ~* a2 s6 w$cos = array();) j0 O5 C1 j8 N3 I6 x8 t
    $cos[0] = 0;) R  f2 z0 z  R! T3 F# E
    $fm1 = 0;
    . T4 B2 k% p8 ?- K+ h6 H2 F//开始计算cos. I, k6 X9 p! a4 p0 }6 G
    //计算分母1,分母1是第一个公式里面 “*”号左边的内容,分母二是右边的内容
    / D2 d0 k- o; P& B; ofor($i=1;$i<9;$i++){; E1 j0 |* M$ B6 s
            if($array[5][$i] != null){//$array[5]代表Leo
    0 t* J# A  A' Y/ j, d4 k: u5 i                $fm1 += $array[5][$i] * $array[5][$i];
      d, |- S9 ^- Q9 L% k# R        }
    7 F7 N& U" F8 Q: O' [}
      Y, M# x* r, ?* R/ }/ u- v+ V/ Q# x( p8 ^# o
    $fm1 = sqrt($fm1);
    ; n3 d, P% `0 Q: X
    % ?" P3 x# b' m" n/ sfor($i=0;$i<5;$i++){6 b; c8 L* ~+ L
            $fz = 0;
    6 c. S0 u6 ]2 P8 u/ R% E0 K5 ]        $fm2 = 0;
    ' e% M1 \, b# e1 ^3 g: {$ i        echo "Cos(".$array[5][0].",".$array[$i][0].")=";3 X- W* F2 J" K! ~- d% M8 \- I
            / f6 W) E; h' h, R
            for($j=1;$j<9;$j++){
    " i9 V5 ?4 @  B  ~( ?# P            //计算分子; ^/ K! W& T( ?, }. j
                    if($array[5][$j] != null && $array[$i][$j] != null){" r5 x0 c' `7 O1 J" p; U7 c  ^5 B
                            $fz += $array[5][$j] * $array[$i][$j];
    . v4 s6 [  Y4 ]                }
    2 S7 d1 t6 G0 P% W                //计算分母2
    ) m) R- ~( _4 @1 A- C                if($array[$i][$j] != null){7 C9 Q) J6 n2 `$ E
                            $fm2 += $array[$i][$j] * $array[$i][$j];
    8 V1 A! d2 X: T, u/ y0 O. y                }                        ; l& B8 \: k' f0 Y
            }
    % N" n; |4 i% r& A. c3 p. d2 t        $fm2 = sqrt($fm2);
    2 v* v3 B8 J* y8 X! P; K        $cos[$i] = $fz/$fm1/$fm2;
    : e' L, \$ F. J  ~$ m8 j        echo $cos[$i]."<br/>";# \: u+ S  e5 Y: m# X2 C8 f
    }
    . u; }, o4 Z, \8 p% J# }- q3 `6 P; p3 L, H0 b0 Z
    这一步得到的结果是酱紫:
    % i3 u+ R# b. a2 x1 p% ^% N: }
    & Q- Q1 ]" `% O. B% ~, M7 h将求好的Cos值排序,采用快排代码如下(百度copy而来):
    5 B* h! f: g/ m2 N* _1 L( |. v! O% [4 ~5 j* X$ O( m
    8 N( L% `( P1 E1 y7 l
    //对计算结果进行排序,凑合用快排吧先/ {- h' w% H; B
    function quicksort($str){# }3 j1 n6 Y$ e6 I  ]% S
            if(count($str)<=1) return $str;//如果个数不大于一,直接返回
    ' r8 H$ g" K; c# v6 C/ R% m6 f        $key=$str[0];//取一个值,稍后用来比较;+ o; e0 F- q0 C( s+ W
            $left_arr=array();0 l) w) r9 w' I3 j2 N3 a+ _
            $right_arr=array();! N+ r! j5 \- y$ ~
           
    ) }; u& @8 w) f; @3 G5 N        for($i=1;$i<count($str);$i++){//比$key大的放在右边,小的放在左边;5 W+ k; D0 g9 e* U  i' m& Z
                    if($str[$i]>=$key)- I* H* l# e6 P; [. l5 r) D
                    $left_arr[]=$str[$i];0 {4 G, s3 J3 ?* X* A
                    else0 P+ T, v3 [  L& i$ }) E
                    $right_arr[]=$str[$i];3 J$ r7 n  U9 @) F9 u
            }
    ' b1 x5 q6 e- C% N# H        $left_arr=quicksort($left_arr);//进行递归;
    3 X9 M" y4 |7 Q. F  ], f  Q% x        $right_arr=quicksort($right_arr);" Y9 @- ~& {9 \6 q0 b+ W3 u4 Z; W
            return array_merge($left_arr,array($key),$right_arr);//将左中右的值合并成一个数组;% a9 M# h# @( |% I
    }' r5 G) Z8 _; h; Q3 r1 u

    8 C( |- i/ A8 K$neighbour = array();//$neighbour只是对cos值进行排序并存储
    . M$ O# z6 o8 _7 m& c$neighbour = quicksort($cos);
    ; T& q" b7 P6 M% {! o' k- L* V  @
    2 m( w5 {7 S! Z& C6 L6 i- A
    这里的$neighbour数组仅仅存储了从大到小排序好的Cos值,并没有与人联系起来。这个问题还要解决。% O: n' w0 R- X; R
    6 i5 t+ x+ b" K$ v' [
    选出Cos值最高的3个人,作为Leo的邻居:1 b5 u1 {( t: G+ \. I+ y* s
    0 Y. [% s7 n0 F
    //$neighbour_set 存储最近邻的人和cos值& S5 {# G& E$ `0 e, F8 @
    $neighbour_set = array();" x, n- ?# |) b" j0 d
    for($i=0;$i<3;$i++){9 Z9 ~/ g% k+ y# ^* p! G4 \9 ?
            for($j=0;$j<5;$j++){
    - l% p- J/ c# a8 H  }  m' P                if($neighbour[$i] == $cos[$j]){# h( r) [1 Y. E# R6 X
                            $neighbour_set[$i][0] = $j;
    ) M. T0 _& _' m                        $neighbour_set[$i][1] = $cos[$j];6 {* B! `" V5 v% Z
                            $neighbour_set[$i][2] = $array[$j][6];//邻居对f的评分
    ( ~% q) X5 m: o                        $neighbour_set[$i][3] = $array[$j][7];//邻居对g的评分
    + a' ~& G4 O  w$ C/ ]                        $neighbour_set[$i][4] = $array[$j][8];//邻居对h的评分
    % r9 J- @* Y5 m. @! A' |2 \                }. U* ~8 p) B: c( m# n! g0 _
            }( I; _) x9 P1 m  z
    }
    : y, `: l$ ]9 N2 Q' W' kprint_r($neighbour_set);
    6 j" j% o" ]+ w. R7 Techo "<p><br/>";. k4 h/ _8 p; T# L' w, k. s3 G
    4 i. I( n8 R4 x1 m5 X0 Z
    这一步得到的结果是酱紫:3 K2 \1 B. P( V1 q
    9 k+ A4 m9 Y3 I0 m

    % R  g( x& L) |" r2 Q5 s# a: p4 F: G2 U, t
    转存失败重新上传取消) r, m! ^; y; p& W8 Y& j2 o
    ; t5 D, b; P% O( ?  Z% b
    这是一个二维数组,数组第一层的下标为0,1,2,代表3个人。第二层下标0代表邻居在数据表中的顺序,比如Jhon是表中的第0个人;下标1代表Leo和邻居的Cos值;下标2,3,4分别代表邻居对f,g,h的评分。
    1 J; o8 H, {$ f- u% p% ^3 L2 Y7 c* a2 f( a* o6 E; k. v# L
    开始进行预测,计算Predict代码如下:
    1 z9 U6 O0 ]1 k6 o" L& f5 M' o
    3 G0 M$ r" F" y# S- Z: R我是分别计算Leo对f,g,h的预测值。在此有一个问题,就是如果有的邻居对f,g,h的评分为空,那么该如何处理。比如Jhon和Mary对h的评分就为空。本能的想到用if判断一下,如果为空则跳过这组计算,不过这样处理是否合理,有待考虑。以下代码并没有写出这个if判断。) T! Z8 W. [: s6 e$ u

    / m( @0 v0 m0 M4 }0 U2 Z//计算Leo对f的评分* w5 U$ u+ U" P+ d
    $p_arr = array();
    7 P0 K% a. ~3 H3 R- a$pfz_f = 0;6 F( c# N; U, ~* i
    $pfm_f = 0;6 N5 b+ A2 x+ y! @* P3 I2 w
    for($i=0;$i<3;$i++){
    " M  z% [$ e6 V2 _1 D        $pfz_f += $neighbour_set[$i][1] * $neighbour_set[$i][2];
    $ j: r% T. g% E' _5 T# G0 L        $pfm_f += $neighbour_set[$i][1];
    . g9 k1 A/ v: U5 f}7 P. X3 [; z* w6 Q. m# r8 u
    $p_arr[0][0] = 6;
    ; P3 J  i* k" {; M# `  ^$p_arr[0][1] = $pfz_f/sqrt($pfm_f);
    1 ~1 `, t1 s$ j: K: vif($p_arr[0][1]>3){
    ; s4 V( p. y- Y" h        echo "推荐f";. O! s4 J% p  o, K' z3 Y$ B
    }  d% d! T; c0 p

    1 G7 B' k' j( r2 R//计算Leo对g的评分
    ( a" @% ^, `, i* N2 Q$pfz_g = 0;( X0 W% N) G% Y
    $pfm_g = 0;; _* c( U* W* x" P+ D
    for($i=0;$i<3;$i++){" E. Z8 a$ M4 G' R9 \
            $pfz_g += $neighbour_set[$i][1] * $neighbour_set[$i][3];
    0 @  B/ O* y8 j: m7 v1 m9 v        $pfm_g += $neighbour_set[$i][1];
    " C. n: A1 E1 G; x0 \        $p_arr[1][0] = 7;% s9 J8 T9 G) l
            $p_arr[1][1] = $pfz_g/sqrt($pfm_g);
    ( o; v7 U: `% n! h# i5 L1 k& @0 k* E6 w}5 [% x, q; k" m. }7 W2 R
    if($p_arr[0][1]>3){" S0 H) p3 g$ J/ J
            echo "推荐g";, g: C% z2 P6 [( B, V2 r0 U
    }
    + ?% @; b& _- ~& d0 n6 q* @: t2 o+ C+ \5 u# ^5 n& |+ _& o* s
    //计算Leo对h的评分
    ( L& x/ ]6 Y0 J$pfz_h = 0;$ C3 e5 H' ^% V/ H7 t/ q/ L
    $pfm_h = 0;
    ! c& c6 x) t0 p2 Q! s2 t' ufor($i=0;$i<3;$i++){
    4 J# C+ a0 q2 @; w! t4 B        $pfz_h += $neighbour_set[$i][1] * $neighbour_set[$i][4];
    ) S! F: |1 v! b0 l, R) h        $pfm_h += $neighbour_set[$i][1];' w8 P8 @+ v1 r3 \# Z* q
            $p_arr[2][0] = 8;; N/ G) q, C4 f8 K: V7 r. K
            $p_arr[2][1] = $pfz_h/sqrt($pfm_h);
    2 \: |- C  h- q3 c  b}
    7 h' ?# q2 A7 v% G2 a7 `print_r($p_arr);, I" M8 d4 Q; N& h5 h0 T
    if($p_arr[0][1]>3){' H! m8 q" M& l% p# C( e- n) R
            echo "推荐h";
    , K0 k4 C( V+ y( ~2 J}
    8 u( ~# W6 c+ f. O: O/ n- v( {" p. S, U
    $p_arr是对Leo的推荐数组,其内容类似如下;
    ' h% w$ w7 P' H4 R5 P! s4 u, v# L+ j
    ! H) e3 p7 l# y/ ~: A: D/ r* o; fArray ( [0] => Array ( [0] => 6 [1] => 4.2314002228795 ) [1] => Array ( [0] => 7 [1] => 2.6511380196197 ) [2] => Array ( [0] => 8 [1] => 0.45287424581774 ) )
    4 C: W# c" }4 c4 b3 R: a; z5 {7 k4 Y" r6 r
    f是第6列,Predict值是4.23,g是第七列,Predict值是2.65........0 ^. f* Z0 v1 y7 c7 E

    " y- j% j0 X% o" p求完了f,g,h的Predict值后有两种处理方式:一种是将Predict值大于3的物品推荐给Leo,另一种是将Predict值从大到小排序,将Predict值大的前2个物品推荐给Leo。这段代码没有写。
    % [0 L% J# M4 X& l/ g# Y+ t2 I# B9 U- i  a/ r( A2 m( A/ K
    从上面的示例中可以看出,推荐算法的实现非常麻烦,需要循环,判断,合并数组等等。如果处理不当,反而会成为系统的累赘。在实际处理中还有以下问题:
    1 ~+ ~$ t- G7 f1 @2 p
    # o" c8 s: W# l1 g1.以上示例我们只对Leo进行推荐,而且我们已经知道Leo没有评价过f,g,h物品。如果放到实际的系统里,对于每一个需要进行推荐的用户,都要查询出他没有评价过哪些物品,这又是一部分开销。
    + S) K/ ~: B  ~/ \$ U$ I
    & E6 z8 Z- u3 ~2.不应当进行整表查询,在实际系统中可以设定一些标准值。比如:我们求Leo与表中的其他人的Cos值,如果该值大于0.80,则表示可以为邻居。这样,当我找到10个邻居之后,就停止求Cos值,避免整表查询。对于推荐物品也可以适当采用此方法,比如,我只推荐10个物品,推荐完后就停止求Predict值。  m' i' T1 s: a8 f

    # e  w4 _7 T* }, m$ b1 N3.随着系统的使用,物品也会发生变化,今天是fgh,明天没准就是xyz了,当物品变化时,需要动态的改变数据表。
    , X2 U3 g/ z4 F
    * b6 K- A# q* i$ G: }# ~4 j* F4.可以适当引进基于内容的推荐,来完善推荐算法。" p! w& u# p6 V# ]7 n
    / Z9 [$ n8 ^0 K4 H2 y
    5.推荐的精确性问题,这个设置不同的标准值,会影响精确性。" |8 M8 q" C- e' t5 `( A
    ————————————————
    9 A) j$ j" d7 y6 B* D版权声明:本文为CSDN博主「星斗其文,赤子其人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
      G5 z+ j$ j5 x4 B% q$ X$ ]$ R5 Z' z原文链接:https://blog.csdn.net/liuliuhelingdao/article/details/126715465
    . j( P; a* ~& G( p. w( Q
    1 H# r- Z& t* U( T0 j& s% V3 n. J' V
    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-4-13 19:24 , Processed in 0.414472 second(s), 52 queries .

    回顶部