QQ登录

只需要一步,快速开始

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

    : L! o6 Y- ?2 `3 k9 ~# q5 j% wphp+mysql实现简单的协同过滤推荐算法8 E, V4 a0 [9 |/ r/ p8 e% p3 p
    仅做标记。。。
    5 b) U& ?+ R, Q7 Q/ x
    + U) I0 x: w* A7 y  i# ?) E  m- q2 I0 \0 C  w4 T

      T6 o- S- C8 f" K要实现协同过滤推荐算法,首先就要理解算法的核心思想和流程。该算法的核心思想可以概括为:若a,b喜欢同一系列的物品(暂时称b是a的邻居吧),则a很可能喜欢b喜欢的其他物品。算法的实现流程可以简单概括为:1.确定a有哪些邻居 2.通过邻居来预测a可能会喜欢哪种物品  3.将a可能喜欢的物品推荐给a。
    & x! |( f# }3 y% W0 g- W4 z9 G( j0 Y  I  r0 T8 N( Y& a
    算法核心的公式如下:
    0 J; m2 @- G# Y6 `" r# Z) D1 l
    $ N& u$ p) }# x* q3 O" N1.余弦相似度(求邻居):
    ! H4 s: p2 r, F1 M3 t% X8 u& k. r. Z; o
    2.预测公式(预测a可能会喜欢哪种物品):
    ' c- {1 V. r4 Z, F" D' P; E' `- Q: n: A. ~* T3 U3 r* Q
    仅从这两个公式我们就可以看出,仅仅是按照这两个公式进行计算,就需要进行大量的循环与判断,而且还涉及到排序的问题,就涉及到排序算法的选择与使用,这里我选快排,从网上copy了一段快排,直接用。总之实现起来很麻烦,在大数据情况下,更何谈效率。4 v8 Z6 l4 x  y7 I- c$ a
    - x' y7 X: w( z
    首先建表:
    $ ?$ C2 h) f9 O. J4 |0 `- w( g2 E, O. b
    DROP TABLE IF EXISTS `tb_xttj`;  y/ V" D& c9 ]3 Q/ h7 _0 \
    CREATE TABLE `tb_xttj` (
    7 d" w. d, C1 g3 x8 N3 V3 }( Y  `name` varchar(255) NOT NULL,; A% Y( U, N( N; D0 j( n0 m: E
      `a` int(255) default NULL,3 H. V9 U' K7 q/ Y/ i
      `b` int(255) default NULL,+ n/ D2 m1 ]1 w( f" o  N& b) t. ?
      `c` int(255) default NULL,7 ~2 @$ h! y5 P
      `d` int(255) default NULL,5 X3 i0 s: g; M$ K3 V0 i
      `e` int(255) default NULL,
    7 _0 K% m- \2 a6 s1 s! f! C: U' u0 n  `f` int(255) default NULL,
    ! |9 R. M9 ^- _  `g` int(255) default NULL,
    6 r, G" m: a/ k. Y  ^  `h` int(255) default NULL,9 I( |) U1 ~1 R( t' w
      PRIMARY KEY  (`name`)- c+ ]  j  I1 Z4 V1 V0 T5 `4 ]* j
    ) ENGINE=MyISAM DEFAULT CHARSET=latin1;% Q8 ^7 z* X( F( p" `/ h- r. A) j
    ! Z  [7 q& s5 v0 D! |  k3 k$ v; P' d
    INSERT INTO `tb_xttj` VALUES ('John', '4', '4', '5', '4', '3', '2', '1', null);, i( H5 Y9 Q3 I
    INSERT INTO `tb_xttj` VALUES ('Mary', '3', '4', '4', '2', '5', '4', '3', null);, l+ e4 d9 E* J: q0 K! I
    INSERT INTO `tb_xttj` VALUES ('Lucy', '2', '3', null, '3', null, '3', '4', '5');+ U4 E1 |. ^! k) Y! J+ {+ M. p
    INSERT INTO `tb_xttj` VALUES ('Tom', '3', '4', '5', null, '1', '3', '5', '4');6 V# ~4 x7 Y' C. ]& @( e4 l
    INSERT INTO `tb_xttj` VALUES ('Bill', '3', '2', '1', '5', '3', '2', '1', '1');
    & l, c4 G& Y$ }  i: v" dINSERT INTO `tb_xttj` VALUES ('Leo', '3', '4', '5', '2', '4', null, null, null);
    : d$ N6 b+ X6 H1 W
    ; r9 o0 o# q$ c' [  B/ ^+ C( }, L
    我这里只对最后一行的Leo进行推荐,看看f,g,h哪个可以推荐给他。
    1 a0 C5 ?" x3 R# i: I
    ! r: L0 O! P" i( n" f5 w+ V. @3 F    用php+mysql,流程图如下:% J7 h, V: M: I' l* x/ U1 n
    % r" ]  v$ d, o8 r: j  x
    连接数据库并将其存储为二维数组的代码如下:& p4 w2 p+ D/ y

    ) j& l! d# t8 Q# A. _: c/ B/ Gheader("Content-Type:text/html;charset=utf-8");. r3 ?8 ^: T$ a0 b8 S/ T7 u0 `
    ; e0 ?; V. k8 T( P( ]
    mysql_connect("localhost","root","admin");2 n7 k  Z4 k5 c4 r" w- ]: V8 V  j
    mysql_select_db("geodatabase");+ a. T) V; C/ U$ U( b8 \
    mysql_query("set names 'utf8'");       
    - f3 ^- H  t# u# p! p0 Q) G
    , O6 C& I: U( f# U3 S$sql = "SELECT * FROM tb_xttj";
    ) @9 e9 _% [. x5 U$result = mysql_query($sql);
    + L( q- Z2 W8 o% N2 ]' J/ y# [- l2 H) }9 D' F7 j
    $array = array();
    6 T, q" r, }+ P" D* R5 Awhile($row=mysql_fetch_array($result))
    1 M* M$ b* Z* ~! ^& d0 K{% n7 R" r8 f7 `' m( l
            $array[]=$row;//$array[][]是一个二维数组! Q8 @9 B& l% j! E5 W
    }
    3 @8 u7 O5 p1 W8 X3 I; j7 w1 f! Y2 m- t" L" \6 N. v
    问题1:这一步完全可以看做是整表查询,这种查询是大忌,对于这种小小的演示系统还可以,但是对大数据的系统,没有效率,至于如何改进,还得多学习才是。
    , ]1 x; d+ T) `
    5 i0 c; p" {5 W9 q1 A& j求Leo与其他人的Cos值代码如下:
    % w8 [* Q4 h: \3 V, j7 z$ t# p6 A( J+ z  n3 J) H' I
    /*
    # X+ e5 a8 H! p8 o$ ^9 I$ ` * 以下示例只求Leo的推荐,如此给变量命名我也是醉了;初次理解算法,先不考虑效率和逻辑的问题,主要把过程做出来: X. N5 c2 C) I( N
    */9 J; r& G& O" l+ f$ L: Z

    # a% {; q% A& A6 d3 T$cos = array();
    ( K2 B2 b1 ~0 a, @3 s$ S4 p$cos[0] = 0;8 ^% d; M+ \6 D: u0 i0 T
    $fm1 = 0;9 x' [6 ~! O0 `
    //开始计算cos4 ~6 u" x! e5 ~7 t2 X- x- x9 K
    //计算分母1,分母1是第一个公式里面 “*”号左边的内容,分母二是右边的内容
    + B2 A( i0 R. y7 {4 w; Ufor($i=1;$i<9;$i++){) P6 A! m, y. s: f5 Q7 Z' ^- n
            if($array[5][$i] != null){//$array[5]代表Leo: A# L+ b* i- Q% _  B
                    $fm1 += $array[5][$i] * $array[5][$i];
    % j, O7 ~( X- b9 D. \9 n        }
    8 R) k/ M/ d- {7 i! m9 I! V( o}! u9 R# N2 I& S6 O. ?
    . L) H, i; T( @- x9 ^: t
    $fm1 = sqrt($fm1);
    ! i* b. I  {8 m6 |8 k+ H7 _8 M/ w! Q
    ! D2 x3 U7 ^7 Q0 {for($i=0;$i<5;$i++){
    4 A. z  M; ]' T/ k+ }) j9 R        $fz = 0;3 |* g  R7 }! z: A, Q! E
            $fm2 = 0;
    " c8 o8 i; p: i* F* v( u9 V# l+ H  e        echo "Cos(".$array[5][0].",".$array[$i][0].")=";
    , D8 Y/ H, Z+ v, N! w; b( Y        & ]& a, @+ |, [1 G
            for($j=1;$j<9;$j++){# @) k! Q8 ?1 p* |$ O8 ?  v" o
                //计算分子/ J8 b# f$ K. A' t' N
                    if($array[5][$j] != null && $array[$i][$j] != null){4 K0 R( M; N% z
                            $fz += $array[5][$j] * $array[$i][$j];: `* O% E) D/ ]
                    }
    9 o0 h4 k0 k! Z+ C. [# V                //计算分母2
    & K: g9 ]- P. g$ p( u5 R1 M. {                if($array[$i][$j] != null){
    0 X+ x' \0 f( L1 b/ V                        $fm2 += $array[$i][$j] * $array[$i][$j];# F% f; \7 V( d6 s/ |8 p
                    }                        " M; e* i+ l7 `- ^( J+ A9 w) q
            }+ Q+ J/ D. [  T  z6 x$ ?* n* Y
            $fm2 = sqrt($fm2);' E: c& V9 ]9 \8 e0 @. F
            $cos[$i] = $fz/$fm1/$fm2;
    ( w% c0 p6 X/ ^4 H- l  b9 k) Z        echo $cos[$i]."<br/>";; r, Z' H2 \4 I! @
    }+ C1 o! E) ?( F7 I& F4 @

    ( a1 q6 G. v. `) G8 p$ E/ G1 p这一步得到的结果是酱紫:6 e9 S, e/ m! Q
    * _+ o. G% P9 u5 F& U
    将求好的Cos值排序,采用快排代码如下(百度copy而来):
    $ g6 Q  [( E$ t6 ?' y. u0 W; Y3 S7 n, K. Z$ s0 u
    $ [0 L# k4 f2 D6 h2 [5 r" I: d
    //对计算结果进行排序,凑合用快排吧先$ P+ R# q9 j. [% S$ x, h( _! `! ^  J3 [
    function quicksort($str){
    $ d+ N/ n/ W. D; X% A        if(count($str)<=1) return $str;//如果个数不大于一,直接返回. E/ |- L  c) `4 L1 k* R$ m
            $key=$str[0];//取一个值,稍后用来比较;
      _  h. L7 ]- X+ V+ R2 Y8 Y. A        $left_arr=array();
    0 Q# l, L, s+ W8 s2 Z        $right_arr=array();
    ) s9 T* U. ]9 k8 |        : k: a2 K7 B1 X: t
            for($i=1;$i<count($str);$i++){//比$key大的放在右边,小的放在左边;
    5 A6 [* y: i1 J1 t7 ]                if($str[$i]>=$key)
    - J* n2 }. `8 z/ j1 S9 ~4 L, ~                $left_arr[]=$str[$i];
    6 k" {* w' q! G) ^2 [                else
    . a6 f8 x5 R. s                $right_arr[]=$str[$i];) b& V3 f% f3 T2 _2 G" Y
            }: F1 T+ s7 F) y6 z0 W( i3 F% {
            $left_arr=quicksort($left_arr);//进行递归;. b: J1 D2 L, @# N4 {7 y7 X
            $right_arr=quicksort($right_arr);2 I' g- H* S9 h# z/ q' J% h
            return array_merge($left_arr,array($key),$right_arr);//将左中右的值合并成一个数组;
    1 V& \1 V+ c4 f' M}2 F! {8 C# c5 A  o) y0 J
    4 F( v4 ~0 U& \8 f( i
    $neighbour = array();//$neighbour只是对cos值进行排序并存储
    6 n5 O# d# d4 P6 S$neighbour = quicksort($cos);
    ' O0 K. F4 |. c0 x: P+ O0 x2 S% O# ]: f
    $ N: I( _' B# v# q3 c5 w
    这里的$neighbour数组仅仅存储了从大到小排序好的Cos值,并没有与人联系起来。这个问题还要解决。% w$ v! V* e0 c+ W3 g' l

    6 d7 S3 [1 R4 a  z# W% {选出Cos值最高的3个人,作为Leo的邻居:% Z$ D7 q0 P* `, S5 |! _

    4 a8 [" P- ?' a$ \//$neighbour_set 存储最近邻的人和cos值
    6 u# y0 O; |8 r0 u: L, ~  X$neighbour_set = array();
    / k. H2 v; C* j1 B9 Z5 b9 j( p1 ?for($i=0;$i<3;$i++){
    4 m# y3 {1 I1 R. p1 @# I! m        for($j=0;$j<5;$j++){& u: y6 q" w: h( R
                    if($neighbour[$i] == $cos[$j]){
    & t0 |. a' V9 e! P. ?8 r                        $neighbour_set[$i][0] = $j;
    ) _+ a0 n$ P1 F/ ^: w: |                        $neighbour_set[$i][1] = $cos[$j];
    3 \: R- L* k6 p                        $neighbour_set[$i][2] = $array[$j][6];//邻居对f的评分6 ?" O& s& m: b: J
                            $neighbour_set[$i][3] = $array[$j][7];//邻居对g的评分
    " y; R( H% O- B6 {                        $neighbour_set[$i][4] = $array[$j][8];//邻居对h的评分
    0 P) Z; v: I) Q5 h& _% _                }
    7 x; \) Q6 W, H7 \' Z        }2 P& t  D! F+ B( U  y  M7 k
    }" [0 O& F5 x8 `* C( W* n8 P
    print_r($neighbour_set);6 k& Q9 I: F, q# i0 Y
    echo "<p><br/>";
    3 J- o7 a1 o3 F8 s: @  g
    . w( P: s0 m8 T" ], t5 |这一步得到的结果是酱紫:; _8 r' E+ d8 n& i

    . f+ ^( x) H# }" m0 a+ L8 I1 N" K; W2 \4 {: {( i5 b# F

    - f6 M  s  q- Y; y! t: u转存失败重新上传取消  D- k, k9 \6 n, I

    % r+ f7 y' l9 E4 i1 a这是一个二维数组,数组第一层的下标为0,1,2,代表3个人。第二层下标0代表邻居在数据表中的顺序,比如Jhon是表中的第0个人;下标1代表Leo和邻居的Cos值;下标2,3,4分别代表邻居对f,g,h的评分。+ T% t8 k/ |% T% J! Y7 u- \

    , I* j& r$ O' Y- C3 Y9 F2 G  Z开始进行预测,计算Predict代码如下:
    " Q! ~) F0 B3 d) ]3 e
    2 a: q- h% E8 Z6 I我是分别计算Leo对f,g,h的预测值。在此有一个问题,就是如果有的邻居对f,g,h的评分为空,那么该如何处理。比如Jhon和Mary对h的评分就为空。本能的想到用if判断一下,如果为空则跳过这组计算,不过这样处理是否合理,有待考虑。以下代码并没有写出这个if判断。& [/ k$ f# w: l( u4 H! {
    ! Q/ \& R& _" ~# I* m; V+ R
    //计算Leo对f的评分
    ! X& {' K4 }. H1 y$p_arr = array();
    2 f7 p4 o! G  k: Y$pfz_f = 0;# w3 I8 }. m/ s! X% x4 ^
    $pfm_f = 0;5 K; T6 w3 o2 [% \
    for($i=0;$i<3;$i++){+ p1 y+ [7 a. @5 R: _; k
            $pfz_f += $neighbour_set[$i][1] * $neighbour_set[$i][2];( }3 m$ h7 B& w6 X4 V( V8 O: r
            $pfm_f += $neighbour_set[$i][1];8 C& s0 ^1 f& W0 r* r0 ^- t
    }: G0 K! T+ D* S
    $p_arr[0][0] = 6;0 v7 P% m. w* p  \
    $p_arr[0][1] = $pfz_f/sqrt($pfm_f);
    ; b7 s; z$ F' ]: K1 M) qif($p_arr[0][1]>3){
    1 z" C) u; {) l% m+ X        echo "推荐f";  n' {' F. ]8 N7 I0 N8 d6 w4 K
    }
    2 T$ R" g$ ^- S1 M1 n3 }2 `9 S8 D8 D$ P0 o( T. Q& C- h" u( S1 N: m
    //计算Leo对g的评分
    0 d: W7 ^, Y4 D3 n$pfz_g = 0;& l' J  _9 [  G4 O
    $pfm_g = 0;. B6 P. R$ S! a
    for($i=0;$i<3;$i++){
    7 q/ }/ b+ n  p  h& K2 V  [        $pfz_g += $neighbour_set[$i][1] * $neighbour_set[$i][3];
    " C) ^6 _2 C7 z  Q& a7 @6 T6 ?7 H        $pfm_g += $neighbour_set[$i][1];
    : y- Z3 o* T5 m  I1 ?0 b' C        $p_arr[1][0] = 7;
    ' _9 f2 i3 K! W! K( y$ ]; Y        $p_arr[1][1] = $pfz_g/sqrt($pfm_g);# W; k1 `0 [% Y+ i3 l" B4 i1 m
    }
    ! ^5 l* s4 u1 Q. h: Zif($p_arr[0][1]>3){. i9 e' G- x1 f) h
            echo "推荐g";% }3 k7 |& F! v+ V
    }8 @! Y$ t4 x. A

    , d6 _' x3 ^9 A5 {6 p" k$ F//计算Leo对h的评分
    $ K! V  N: \6 Q7 J6 M$pfz_h = 0;
    9 [' R! j* T( D$ s! o4 I$pfm_h = 0;  x! g4 J$ e. ~% T
    for($i=0;$i<3;$i++){
    " a( `) B0 n5 i- F        $pfz_h += $neighbour_set[$i][1] * $neighbour_set[$i][4];
    " d  u4 r1 |) s! f7 f0 c% _) J) o        $pfm_h += $neighbour_set[$i][1];
    $ \, y- M" ?- Y3 e- w) t        $p_arr[2][0] = 8;
    6 r+ [" C8 b5 {7 b        $p_arr[2][1] = $pfz_h/sqrt($pfm_h);) I8 [. g6 u6 Y: v# ^% K& G
    }& J' t( q: H8 \8 v! L+ q  F: b
    print_r($p_arr);
    3 _1 C$ o7 Z- w, b- y/ i+ `if($p_arr[0][1]>3){% ~) h5 R; \* u) H9 f- h8 n
            echo "推荐h";; c' N/ ]8 t& m- Z# Q" S1 B1 ]0 _
    }8 j; [9 h' W$ j, p# V/ v4 Z4 i
    & i: T- H0 |9 ^. {, C
    $p_arr是对Leo的推荐数组,其内容类似如下;* x9 {6 J6 Z" d/ s9 ]$ P

    , w0 n. l0 m2 ~Array ( [0] => Array ( [0] => 6 [1] => 4.2314002228795 ) [1] => Array ( [0] => 7 [1] => 2.6511380196197 ) [2] => Array ( [0] => 8 [1] => 0.45287424581774 ) )' b; `9 C7 H+ E0 S
    3 d3 A5 h; y$ `/ G/ V# ~+ p
    f是第6列,Predict值是4.23,g是第七列,Predict值是2.65........
    6 E% ?8 ~( `/ Q
    0 f0 v8 j; P( ~* e" ^求完了f,g,h的Predict值后有两种处理方式:一种是将Predict值大于3的物品推荐给Leo,另一种是将Predict值从大到小排序,将Predict值大的前2个物品推荐给Leo。这段代码没有写。9 Y1 Y+ y: ?, P9 f1 Y# i
    1 W) _; Q7 _* ^& o. f: {2 J
    从上面的示例中可以看出,推荐算法的实现非常麻烦,需要循环,判断,合并数组等等。如果处理不当,反而会成为系统的累赘。在实际处理中还有以下问题:& t' u  q6 F7 W4 W0 o. [! `
    2 T- ]. @7 I2 l  g( o
    1.以上示例我们只对Leo进行推荐,而且我们已经知道Leo没有评价过f,g,h物品。如果放到实际的系统里,对于每一个需要进行推荐的用户,都要查询出他没有评价过哪些物品,这又是一部分开销。
    6 G/ ~  z: C" k  P9 t- \8 X
    / Y8 \" J/ u! @1 u. H# m& R6 w9 A2.不应当进行整表查询,在实际系统中可以设定一些标准值。比如:我们求Leo与表中的其他人的Cos值,如果该值大于0.80,则表示可以为邻居。这样,当我找到10个邻居之后,就停止求Cos值,避免整表查询。对于推荐物品也可以适当采用此方法,比如,我只推荐10个物品,推荐完后就停止求Predict值。
    9 Q0 J% ]8 m' Y% t& `
    1 c. Y" v7 b+ v/ @  }0 Y3.随着系统的使用,物品也会发生变化,今天是fgh,明天没准就是xyz了,当物品变化时,需要动态的改变数据表。, J4 ?% E% K$ s6 T- u4 U# q/ H
      o" m7 z( F9 T, k: u0 g4 h
    4.可以适当引进基于内容的推荐,来完善推荐算法。+ `7 M1 I3 u* x3 m
    4 e6 k  L0 Z9 O$ ^
    5.推荐的精确性问题,这个设置不同的标准值,会影响精确性。
      m' p. W: ?* y$ J————————————————
    ) S9 H8 ]1 [( D! J7 J版权声明:本文为CSDN博主「星斗其文,赤子其人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。$ b; i+ T% ~' x. z' [- E
    原文链接:https://blog.csdn.net/liuliuhelingdao/article/details/126715465/ ~/ y& _! U2 j
    ; E) j* I2 w& `! H/ E, R

    8 o% M7 [, @* A
    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-8-5 06:15 , Processed in 0.456640 second(s), 51 queries .

    回顶部