QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1965|回复: 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 c7 n) `# ~5 x; ?( z
    php+mysql实现简单的协同过滤推荐算法3 S8 D4 v6 ^; B
    仅做标记。。。0 L7 k, `7 v/ F, x
    9 V4 L9 G* m/ t8 E' h
    8 R6 y) A/ `0 M- H2 a
    $ X0 e& P2 U' o! a* d0 H0 }  ?
    要实现协同过滤推荐算法,首先就要理解算法的核心思想和流程。该算法的核心思想可以概括为:若a,b喜欢同一系列的物品(暂时称b是a的邻居吧),则a很可能喜欢b喜欢的其他物品。算法的实现流程可以简单概括为:1.确定a有哪些邻居 2.通过邻居来预测a可能会喜欢哪种物品  3.将a可能喜欢的物品推荐给a。- u8 ]5 A5 V3 ?. y, h

    * m1 ]! V% }4 y1 C  Q" l, w; d算法核心的公式如下:. e: F3 ^4 W4 R1 U9 Z
    8 p3 r8 K& L# S3 h
    1.余弦相似度(求邻居):
    $ U' i% ^! T. w% ?4 o0 f+ Z$ l  |1 K0 X" S. N
    2.预测公式(预测a可能会喜欢哪种物品):' H$ h+ ]4 F3 u9 ?) O7 H5 g

    ' r) T# D# V- i! W# ?2 l仅从这两个公式我们就可以看出,仅仅是按照这两个公式进行计算,就需要进行大量的循环与判断,而且还涉及到排序的问题,就涉及到排序算法的选择与使用,这里我选快排,从网上copy了一段快排,直接用。总之实现起来很麻烦,在大数据情况下,更何谈效率。$ I# \( P5 p1 c0 U' o6 v# f
    7 L; c) r" D8 Z3 g
    首先建表:$ e; I) Z( z1 P4 \, [( H
    ) f+ G# Z2 {+ y; L2 h  N
    DROP TABLE IF EXISTS `tb_xttj`;
    ! k! K' C. R" S7 ~4 r3 hCREATE TABLE `tb_xttj` (
    3 x6 b, X: y. Z+ g) M) a  `name` varchar(255) NOT NULL,
    6 Z7 N/ v. E, |+ }, X  `a` int(255) default NULL,
    3 S' }$ ]* H8 _- X) B9 G  `b` int(255) default NULL,
    " |. D0 }3 Q( p7 h) P  `c` int(255) default NULL,, J- l5 m& p! b& v: R
      `d` int(255) default NULL,6 j, U8 m; |' P3 f3 O  @
      `e` int(255) default NULL,
    / x8 G$ L6 c$ c% t  `f` int(255) default NULL,
    , D8 s! X! d  k- G( \) q- E  `g` int(255) default NULL,( s3 f4 j; d$ U" V; A, N
      `h` int(255) default NULL,4 X, j# E+ }* r' S4 H4 s
      PRIMARY KEY  (`name`)' }% M. ~% p; C; v7 M' U
    ) ENGINE=MyISAM DEFAULT CHARSET=latin1;- u  n- s1 q: ?( o
    ( L; n; Q/ w  C" J# G) }% D7 N& P
    INSERT INTO `tb_xttj` VALUES ('John', '4', '4', '5', '4', '3', '2', '1', null);
    9 X% R0 e. ~4 ~- }INSERT INTO `tb_xttj` VALUES ('Mary', '3', '4', '4', '2', '5', '4', '3', null);
    0 W. X  \, u1 H' dINSERT INTO `tb_xttj` VALUES ('Lucy', '2', '3', null, '3', null, '3', '4', '5');0 U' k6 L$ x3 v1 ]4 r9 u4 u4 L
    INSERT INTO `tb_xttj` VALUES ('Tom', '3', '4', '5', null, '1', '3', '5', '4');
    2 B. Y' ^% B. f% AINSERT INTO `tb_xttj` VALUES ('Bill', '3', '2', '1', '5', '3', '2', '1', '1');( \- L/ g4 ]9 i3 z8 _+ `+ K
    INSERT INTO `tb_xttj` VALUES ('Leo', '3', '4', '5', '2', '4', null, null, null);
    0 ?! ^* E$ C; L1 X/ b% u, j) y2 V% Q( e
    3 ~: U& E: _# o
    我这里只对最后一行的Leo进行推荐,看看f,g,h哪个可以推荐给他。, P2 `5 \- ^6 J" v- N+ j8 C

    : c  p! ]2 C' M" b    用php+mysql,流程图如下:
    ) i' @# w0 `% O. e& p) g* C; y: q1 P
    连接数据库并将其存储为二维数组的代码如下:
    : b* G+ i- F! k
    $ r/ f/ B$ R; [header("Content-Type:text/html;charset=utf-8");
    - Y$ M8 l  l3 ?  V
    & O2 x4 p1 s( q5 K! S9 y( x/ ~mysql_connect("localhost","root","admin");: J9 |. |& G  G
    mysql_select_db("geodatabase");
    / M8 [) k; Z! d, Q# {8 [mysql_query("set names 'utf8'");        2 E" a! L( q/ g  E- {+ ~5 S

    2 r7 |0 O' A9 |% r' c$sql = "SELECT * FROM tb_xttj";+ C, w* w$ i) i% @  Q/ ~3 [+ s
    $result = mysql_query($sql);: {  M* X+ g" h. V+ g' g$ D; ]( O# s
    + l& t' k' |0 N/ t: q) _
    $array = array();* E8 M5 b% |( X  `
    while($row=mysql_fetch_array($result))/ ]+ }! D* F* p
    {
    2 M7 `# N& h, a        $array[]=$row;//$array[][]是一个二维数组
    # l' B0 b0 S: h, ?2 O6 t6 K" ]* X}
    ; Y$ W/ X5 i6 q: k% G
    2 j4 I8 `2 j' Y/ J3 {- Y问题1:这一步完全可以看做是整表查询,这种查询是大忌,对于这种小小的演示系统还可以,但是对大数据的系统,没有效率,至于如何改进,还得多学习才是。
    8 }+ c- v4 N3 f4 }3 T0 L4 F0 [  W. D$ z# }" k
    求Leo与其他人的Cos值代码如下:: F/ k# R( K! `9 ~* P1 p

    9 _* i$ B) Z* a3 v$ v0 m/*5 z; w5 J: g, ]( [
    * 以下示例只求Leo的推荐,如此给变量命名我也是醉了;初次理解算法,先不考虑效率和逻辑的问题,主要把过程做出来: U9 o7 S  F- u0 s( x3 W3 M9 p
    */
    2 W8 J% Y8 G  c/ ]7 i+ g' X, a  e) G, h# g
    $cos = array();0 c; a5 E; k# H) e+ b
    $cos[0] = 0;  W6 C7 X+ Q. E- D" W: ?# x
    $fm1 = 0;
    ! h9 u  p8 ~! H9 {# E//开始计算cos' E) H9 n- }: @  E# E
    //计算分母1,分母1是第一个公式里面 “*”号左边的内容,分母二是右边的内容
    - a& Z: F5 m1 e# Gfor($i=1;$i<9;$i++){
    - }' N" n  r% U' P/ M7 t# ~        if($array[5][$i] != null){//$array[5]代表Leo
    1 U5 s& U% R7 V5 ~( {1 H. @                $fm1 += $array[5][$i] * $array[5][$i];
    9 z1 v" b* {0 a) W8 q; q$ z        }
    : h$ i6 ?( L/ a8 ~) g& }. y}7 {( q8 H2 h5 a% |

    : O/ i; s* B8 q  G& K$fm1 = sqrt($fm1);
    3 p7 J2 p7 R. t1 t2 M. l  H. ?0 U5 O' `$ G" c1 R0 [
    for($i=0;$i<5;$i++){' B0 \8 I4 Y4 [6 [
            $fz = 0;6 F9 A( C: |- o% u5 j$ C
            $fm2 = 0;
    7 [. u0 ^2 g9 Z/ W) D        echo "Cos(".$array[5][0].",".$array[$i][0].")=";+ z  w! k7 V( ]' i; u9 ]2 [
            . a9 G+ v& h" t8 Y' q2 P: D6 C! h
            for($j=1;$j<9;$j++){$ [7 x3 L& H7 R. D) P8 W
                //计算分子7 u  x# E0 F+ N0 S$ e
                    if($array[5][$j] != null && $array[$i][$j] != null){
    & K0 G- }# A! N# j                        $fz += $array[5][$j] * $array[$i][$j];" @7 T8 I) p6 ]1 B/ u! P9 A0 a
                    }
    % e1 t) L9 L+ i/ N4 _& r                //计算分母2& |$ E8 u9 c. c. r3 T8 g) G
                    if($array[$i][$j] != null){5 p. |* v( B/ l% @1 ]$ i5 ~7 Z& G
                            $fm2 += $array[$i][$j] * $array[$i][$j];
    & B! k0 n# B! d) C5 m8 l3 K  d' p                }                          I& u+ D$ a/ x, ?# q8 j. H- u
            }
    / Y$ Q& _! V$ E# ^" b2 Q' B        $fm2 = sqrt($fm2);/ A/ ~; m. D1 ?; r5 @: Z' G
            $cos[$i] = $fz/$fm1/$fm2;" V1 J, V9 w6 j  B
            echo $cos[$i]."<br/>";
    % ?0 h2 e; X$ p" [: m: ~}
    " w4 V+ _- ?: `1 x$ f8 ^0 m5 M, k. Y$ L% Y4 g
    这一步得到的结果是酱紫:! U6 s, F( u6 h* Y1 a
    4 ^% n! s( n6 U6 }( x  b, }# o
    将求好的Cos值排序,采用快排代码如下(百度copy而来):
    $ Q( T1 Z" j; L( `! a1 l. W
    1 Q, g$ z: j# \( A0 B2 n+ }5 W# `* ]8 D: _0 V
    //对计算结果进行排序,凑合用快排吧先
    " x+ x* w0 }7 _function quicksort($str){: L2 G1 _6 h/ \: E+ x
            if(count($str)<=1) return $str;//如果个数不大于一,直接返回
    ! H& W0 a( g% D        $key=$str[0];//取一个值,稍后用来比较;
    , \1 M7 R; i/ L! b, `8 y6 A        $left_arr=array();
    ' l. a5 E' r! w0 h3 t        $right_arr=array();
    2 k4 ]) x# S9 F+ w4 D( C. G7 j        ( N: T/ W4 e% ]) {
            for($i=1;$i<count($str);$i++){//比$key大的放在右边,小的放在左边;( [! F7 a$ K- V  X; ~
                    if($str[$i]>=$key)
    . ]( i. H( l( C0 n9 I* u* H7 D0 _$ ?                $left_arr[]=$str[$i];" r9 q' }' f: m" V7 r
                    else
    9 Z3 j9 u2 F8 `1 X& J% }: G' i                $right_arr[]=$str[$i];& y$ o4 F7 K) }( d
            }
    3 u9 h2 z1 j: F# L/ W& A        $left_arr=quicksort($left_arr);//进行递归;
    6 W& Q9 O( j+ I0 S) B  \6 l2 `  @        $right_arr=quicksort($right_arr);
    0 i0 I; }1 A8 a- X. i$ @' r" S        return array_merge($left_arr,array($key),$right_arr);//将左中右的值合并成一个数组;
    ) d: T3 n" |& x% Z, L! u4 y9 a}% E+ x. f) Z4 E6 o7 p- W
    & t  @/ P; r# c; Q* t9 a
    $neighbour = array();//$neighbour只是对cos值进行排序并存储2 y( `& h0 u; t/ o2 U) v
    $neighbour = quicksort($cos);
    ' U- Z  C: K6 U4 ?" Q0 O0 r# q2 L/ X4 }1 a$ \  D7 `; g" x

    - M0 L9 K5 N  [# @- b) z4 ?+ [这里的$neighbour数组仅仅存储了从大到小排序好的Cos值,并没有与人联系起来。这个问题还要解决。$ g0 F8 `' f* {' |
    ) z$ ]  @: b( {# O% |- @8 `
    选出Cos值最高的3个人,作为Leo的邻居:
    3 r! w- K* y  `  F7 p: q
    , m' ]0 c1 l# P- x$ O7 h$ ^) T//$neighbour_set 存储最近邻的人和cos值0 p5 l# O8 U0 t( y
    $neighbour_set = array();. L1 E/ f, h& d8 i& u
    for($i=0;$i<3;$i++){
    , V1 t" t+ Y: z        for($j=0;$j<5;$j++){, v; |2 G, u; D1 l; F$ s
                    if($neighbour[$i] == $cos[$j]){3 ?" l5 ~' H9 h
                            $neighbour_set[$i][0] = $j;8 L: ]6 \: a& w# ~
                            $neighbour_set[$i][1] = $cos[$j];" G% y6 M5 P  c, q" K; T
                            $neighbour_set[$i][2] = $array[$j][6];//邻居对f的评分# ~1 O! c# y2 I' n1 W
                            $neighbour_set[$i][3] = $array[$j][7];//邻居对g的评分; o* g" Y; [: W
                            $neighbour_set[$i][4] = $array[$j][8];//邻居对h的评分
    % d8 F, F  m- {1 d                }
    " T' a/ Z4 O. F6 q4 ~* g3 h; z        }4 p9 v' ~6 P5 \0 V1 y$ Q
    }
    , V. {6 {) d, aprint_r($neighbour_set);
    9 v1 E! a! ?+ y* R6 Kecho "<p><br/>";, ]: T" Z$ s+ n3 t" U
    4 k) w: M/ V- ~; ~6 ~# M2 ]1 B
    这一步得到的结果是酱紫:' _) }+ Z" _6 f3 E. C3 z' I

    1 w/ ?8 V6 e% {8 c) m" n
    - R) f% ~4 S8 I; B
    8 d/ ]7 I7 Y. S3 }$ ?' s1 r转存失败重新上传取消7 p8 ?" [7 z# g& Y+ Q

    ; M; {. B$ R* z; f! h这是一个二维数组,数组第一层的下标为0,1,2,代表3个人。第二层下标0代表邻居在数据表中的顺序,比如Jhon是表中的第0个人;下标1代表Leo和邻居的Cos值;下标2,3,4分别代表邻居对f,g,h的评分。
    3 g$ m. N' I7 @3 m/ w- U, i4 Q+ y: v  ~
    开始进行预测,计算Predict代码如下:8 g* U9 Z) k5 \) Y  `  @

    5 J" `0 k% h. ?7 J' x/ T我是分别计算Leo对f,g,h的预测值。在此有一个问题,就是如果有的邻居对f,g,h的评分为空,那么该如何处理。比如Jhon和Mary对h的评分就为空。本能的想到用if判断一下,如果为空则跳过这组计算,不过这样处理是否合理,有待考虑。以下代码并没有写出这个if判断。
    8 m! G, D4 L% H  B
    ) [* t$ H( z- g8 p- m//计算Leo对f的评分6 t2 S  ]  n$ w( J4 U2 ~" J
    $p_arr = array();& G9 f' v. O6 P. e, W" W5 a
    $pfz_f = 0;
    ; l. |+ n9 n% J! W+ U$pfm_f = 0;
    ; c; \" b) F1 v6 f7 j$ vfor($i=0;$i<3;$i++){
    ( b9 W( ^5 q$ h        $pfz_f += $neighbour_set[$i][1] * $neighbour_set[$i][2];
    ! z. m# U: w, E. ~4 T        $pfm_f += $neighbour_set[$i][1];" Z2 _8 z1 n1 R1 R) l
    }
    # H- o5 n- ]8 `" d! W- P$p_arr[0][0] = 6;( Y+ F7 D6 |! i; C! C1 w$ u
    $p_arr[0][1] = $pfz_f/sqrt($pfm_f);
    0 W' R% J0 K- c0 B) Iif($p_arr[0][1]>3){: A2 s& F- \3 N  w5 a4 U; A6 |
            echo "推荐f";' Q- c: J  o# d* {
    }8 [2 b' q: Z, R/ I
    ! {9 Q: }. g$ t  T4 x; y' I( C
    //计算Leo对g的评分
    + ^5 b3 l' I$ i- v1 e- G$pfz_g = 0;
    1 P" A2 z. N; ~8 ^. [; E$pfm_g = 0;! Z1 I2 `3 z, P8 a
    for($i=0;$i<3;$i++){2 R6 m% {5 \- c4 Z0 {3 x
            $pfz_g += $neighbour_set[$i][1] * $neighbour_set[$i][3];
    ( x. p" [4 V7 I        $pfm_g += $neighbour_set[$i][1];& G% [1 q0 Z( j. y4 F
            $p_arr[1][0] = 7;; [- |$ p+ z8 [' i
            $p_arr[1][1] = $pfz_g/sqrt($pfm_g);
    9 q& m7 [$ r. Q}" Z, W; r' N- q% w1 l
    if($p_arr[0][1]>3){
    0 s8 C4 F7 F, h        echo "推荐g";" g9 e* o6 o) A9 L" r- G. G- T  X
    }
    : M8 x/ o6 P& n. G; M0 L4 c9 N. {7 Q1 k
    //计算Leo对h的评分
    . D' j( q/ V, `9 |# H% N5 q$pfz_h = 0;: c4 C8 e' k7 ]. O
    $pfm_h = 0;
    1 r/ P0 b. [6 t* Bfor($i=0;$i<3;$i++){& v% p+ W4 E1 y# l) j
            $pfz_h += $neighbour_set[$i][1] * $neighbour_set[$i][4];
    " g2 G3 ~& u7 G        $pfm_h += $neighbour_set[$i][1];' K9 V  l( Y6 f: l
            $p_arr[2][0] = 8;- {! f" i& j3 l) j
            $p_arr[2][1] = $pfz_h/sqrt($pfm_h);
    7 X: Y' W3 J5 ^/ Q! N) _}2 m& k. r, c; L: Z
    print_r($p_arr);) r. R* m( W2 m2 z7 b8 `. W, N
    if($p_arr[0][1]>3){
    + X* W- I6 N* S6 ^6 X        echo "推荐h";# _2 [  L5 d6 W/ F- u5 |
    }; w" ^: ]9 U1 L0 W6 `3 h" R4 F
    1 Y) O# e) Q1 n/ F- g: o% p
    $p_arr是对Leo的推荐数组,其内容类似如下;
    * Y0 X; H0 N; m7 p5 b
    5 D& ?/ x, \8 y' cArray ( [0] => Array ( [0] => 6 [1] => 4.2314002228795 ) [1] => Array ( [0] => 7 [1] => 2.6511380196197 ) [2] => Array ( [0] => 8 [1] => 0.45287424581774 ) )- B+ Q3 T# a5 h; s! Q; g
    $ X. B2 V- v) r, Q# o( y2 X
    f是第6列,Predict值是4.23,g是第七列,Predict值是2.65........; X$ D5 u" h7 Q6 ^# w
    4 |& Y; s" c' o+ n$ M) ]: J
    求完了f,g,h的Predict值后有两种处理方式:一种是将Predict值大于3的物品推荐给Leo,另一种是将Predict值从大到小排序,将Predict值大的前2个物品推荐给Leo。这段代码没有写。
    1 B5 I7 ?( M  r+ Q2 i3 T+ g, g
    从上面的示例中可以看出,推荐算法的实现非常麻烦,需要循环,判断,合并数组等等。如果处理不当,反而会成为系统的累赘。在实际处理中还有以下问题:
    7 E& B$ z, ~# U* F, U+ @7 |! k8 b% o9 B  P2 j$ E
    1.以上示例我们只对Leo进行推荐,而且我们已经知道Leo没有评价过f,g,h物品。如果放到实际的系统里,对于每一个需要进行推荐的用户,都要查询出他没有评价过哪些物品,这又是一部分开销。
    + [$ C7 P/ Q4 c
    $ ]  g1 {3 H9 w3 H2.不应当进行整表查询,在实际系统中可以设定一些标准值。比如:我们求Leo与表中的其他人的Cos值,如果该值大于0.80,则表示可以为邻居。这样,当我找到10个邻居之后,就停止求Cos值,避免整表查询。对于推荐物品也可以适当采用此方法,比如,我只推荐10个物品,推荐完后就停止求Predict值。
    2 J! I# E' n$ w/ M9 L
    - H" s  M' S: N9 E3.随着系统的使用,物品也会发生变化,今天是fgh,明天没准就是xyz了,当物品变化时,需要动态的改变数据表。! V) Y8 [1 ^8 K( m
    5 @  J3 v) E( q& [  u: F" v
    4.可以适当引进基于内容的推荐,来完善推荐算法。1 \6 k/ T/ `: b1 ?1 H0 i: u6 i- }
    % k( ^& s7 ~5 i
    5.推荐的精确性问题,这个设置不同的标准值,会影响精确性。
    + s* E' S1 q3 b, M5 C7 ]————————————————
    ' e) j1 Q2 M) n7 d6 I1 f! C& \1 i8 R版权声明:本文为CSDN博主「星斗其文,赤子其人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    2 M2 n+ ^9 N+ F  I9 l原文链接:https://blog.csdn.net/liuliuhelingdao/article/details/126715465
    2 k( l1 G& }9 g! W6 F6 O5 o! A$ s: j, z* s, w+ _
    + N- _0 f2 y* @! C+ _; x: u# t
    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-13 15:28 , Processed in 1.916866 second(s), 51 queries .

    回顶部