QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1921|回复: 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 Z" R4 k! s8 N6 k
    php+mysql实现简单的协同过滤推荐算法$ V8 `0 T4 ]8 G' s' g7 ?$ y
    仅做标记。。。
    1 U1 D  j" O6 T9 H; f- B7 U
    + \" ^0 R# L. ?% S  V" e& _, x8 Y/ C5 Z: g3 V& k8 q0 {4 ?

    0 m- ]  j- w* Z# `- n3 m要实现协同过滤推荐算法,首先就要理解算法的核心思想和流程。该算法的核心思想可以概括为:若a,b喜欢同一系列的物品(暂时称b是a的邻居吧),则a很可能喜欢b喜欢的其他物品。算法的实现流程可以简单概括为:1.确定a有哪些邻居 2.通过邻居来预测a可能会喜欢哪种物品  3.将a可能喜欢的物品推荐给a。
    9 Y! V) g+ o* l1 l
    1 n; Z* t: ^  ~6 s9 J$ j算法核心的公式如下:" I; U/ y  u* Z& c' l' b
    6 X4 E9 G4 ^5 A% O
    1.余弦相似度(求邻居):& C* |8 {/ G5 ]
    7 w9 W' S6 l+ {( l) L" A- d. T+ ?) h
    2.预测公式(预测a可能会喜欢哪种物品):  a, ]+ b% \+ ]8 x0 Z

    + ]" i* d3 O3 U: w3 W仅从这两个公式我们就可以看出,仅仅是按照这两个公式进行计算,就需要进行大量的循环与判断,而且还涉及到排序的问题,就涉及到排序算法的选择与使用,这里我选快排,从网上copy了一段快排,直接用。总之实现起来很麻烦,在大数据情况下,更何谈效率。, u* N  U, ]; t, u' i
    3 {/ Q4 W( J% ?( D1 f
    首先建表:
    0 @3 S+ |, J# b/ `$ K0 ]# k! h/ I  K* z1 p9 o$ N7 o; L
    DROP TABLE IF EXISTS `tb_xttj`;
    ' n" q8 T: U2 f3 D& u; `& QCREATE TABLE `tb_xttj` (
      a# Q0 U/ E0 X( r# X2 M  `name` varchar(255) NOT NULL,9 T% g6 l/ N. X3 r3 n
      `a` int(255) default NULL,
    & N+ d9 A: ~' m  `b` int(255) default NULL,' p/ y) v8 g: A4 y) v) b- a
      `c` int(255) default NULL,
    # n1 d7 L5 d7 U/ ]% W9 U4 p$ W$ Y  `d` int(255) default NULL,( Z5 r, D& z9 R+ E
      `e` int(255) default NULL,
    2 U1 r& t1 w, g5 k2 b& c0 o  `f` int(255) default NULL,
    - d, h: Q+ n$ U; t  `g` int(255) default NULL,' X6 I, `- g, }4 s  Q( a6 e; K
      `h` int(255) default NULL,7 n# i, W& b* f  H
      PRIMARY KEY  (`name`)( V  f4 ~2 S. i  e
    ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
    * S6 m) H5 Q- [3 B; m& l& H
    % f7 n, w; [- }! l$ V/ ~7 x6 Z6 iINSERT INTO `tb_xttj` VALUES ('John', '4', '4', '5', '4', '3', '2', '1', null);. S& s; X5 M7 A( D
    INSERT INTO `tb_xttj` VALUES ('Mary', '3', '4', '4', '2', '5', '4', '3', null);
    0 c6 ~5 J( P- N6 |2 d! DINSERT INTO `tb_xttj` VALUES ('Lucy', '2', '3', null, '3', null, '3', '4', '5');3 Q0 T+ B# _& ?* k* ^
    INSERT INTO `tb_xttj` VALUES ('Tom', '3', '4', '5', null, '1', '3', '5', '4');
    9 u# s4 D& y3 a0 x6 c% DINSERT INTO `tb_xttj` VALUES ('Bill', '3', '2', '1', '5', '3', '2', '1', '1');7 |9 ]) Z& o) |$ e1 x3 U
    INSERT INTO `tb_xttj` VALUES ('Leo', '3', '4', '5', '2', '4', null, null, null);
    ' W, l3 H6 D5 x# L. ]$ N/ {& w8 n8 l0 u* L1 J" {1 h' ^1 \
    6 a/ ~0 N8 V- K, _" k$ s
    我这里只对最后一行的Leo进行推荐,看看f,g,h哪个可以推荐给他。! M0 Y4 H: L& v6 C. [- Q4 F
    9 }! |' A4 ?: r7 U, H; c( f
        用php+mysql,流程图如下:7 A4 A4 J: K% W7 k$ v3 G% R- A) Q6 z

    8 Y% m/ K( m" Z. Z# Y  `# M+ P7 Q连接数据库并将其存储为二维数组的代码如下:
    7 |% h8 ~0 h  x1 X. `/ b# R3 n
    header("Content-Type:text/html;charset=utf-8");9 G( @/ K9 E' h  q1 a( A+ c5 J
    4 D. o& D3 K) R" ^1 J8 e
    mysql_connect("localhost","root","admin");
    ) l, l1 X$ M2 Y, `5 `mysql_select_db("geodatabase");
    3 C# j. v0 F; E& N* Tmysql_query("set names 'utf8'");       
    : W; w: ]5 S/ P
    & N+ C" ?5 r8 }0 Z0 L8 a$sql = "SELECT * FROM tb_xttj";
    ) @- J. a1 s: o  }( {5 D, O. w9 _$result = mysql_query($sql);
    " L) E. m8 [5 ]% r% d7 @; j9 `+ _% `* f
    $array = array();' s( ^; v; B* V, G5 \4 N7 r3 W
    while($row=mysql_fetch_array($result))
    3 O% M6 C* b! W+ V' \6 |{& ^1 M9 Z: S; e' I
            $array[]=$row;//$array[][]是一个二维数组# p+ ^$ k- K/ X; e! P
    }
    ( b* H: j) s: f+ Y( p  J$ }9 a: P
    问题1:这一步完全可以看做是整表查询,这种查询是大忌,对于这种小小的演示系统还可以,但是对大数据的系统,没有效率,至于如何改进,还得多学习才是。( p. c9 V7 _2 m- U! u
    5 ^8 x) ?6 c; ~) D
    求Leo与其他人的Cos值代码如下:: v( [1 D8 i) Y* h8 A2 x- Z/ P

    % q& m3 q: e# F9 {; M" S/*
    2 F+ U/ h0 f6 N+ R0 x9 ?7 o0 R9 ] * 以下示例只求Leo的推荐,如此给变量命名我也是醉了;初次理解算法,先不考虑效率和逻辑的问题,主要把过程做出来! I7 Y  n% X, j  I3 U5 j# ^
    */( u! Z4 D0 H6 `+ t+ e$ l6 z" W
    * t4 C1 G0 t  g+ v, }
    $cos = array();
    ) G2 N! O5 f* [/ h- |0 K. A& ^$cos[0] = 0;
    ' d- `" L: G% }( H. N* [  }4 W- e$fm1 = 0;; ^0 S4 ^, b& |% v# S
    //开始计算cos
    6 P' x1 J3 U. M! Q2 D0 ^2 f, v//计算分母1,分母1是第一个公式里面 “*”号左边的内容,分母二是右边的内容
    # }/ y' n# K7 B, y; E: L7 N) W5 @* n5 vfor($i=1;$i<9;$i++){
    4 g1 a$ {* E% y: \9 F, ?        if($array[5][$i] != null){//$array[5]代表Leo
    . X/ w3 J3 X1 u1 D; a                $fm1 += $array[5][$i] * $array[5][$i];8 d7 ]. n! _* ^+ r8 m4 w! T- \% E7 \
            }
    : [5 l" x! b$ A+ O8 P  d" w+ V}/ V4 z3 t7 q3 g

    9 `: H( \( z5 k5 e: I  H8 q% F! n  W$fm1 = sqrt($fm1);
    2 e$ g! m1 J1 `# \$ i" v$ a+ l) h' {7 I
    for($i=0;$i<5;$i++){. }9 k: {# H6 N. b) w* w! J1 c
            $fz = 0;
    3 O) w! Z* b. a+ x/ Y5 _" t        $fm2 = 0;
    8 s5 Z+ o3 v& p4 C7 d4 [        echo "Cos(".$array[5][0].",".$array[$i][0].")=";
    6 i% x8 H# j' c+ N2 v& L& ^  I3 S        7 A7 ^9 n. P9 Y1 i0 |
            for($j=1;$j<9;$j++){
    * }. Z  P6 w/ J) {8 G) M: V- j& F            //计算分子
    - ?( ~8 q" N* b4 k' d# J                if($array[5][$j] != null && $array[$i][$j] != null){
    ( X* `* X0 i1 O" b/ J0 b                        $fz += $array[5][$j] * $array[$i][$j];6 P, f3 v) P+ z" f2 x
                    }. j8 {6 X2 p$ t5 i$ a# ?( [7 w
                    //计算分母2; J: U+ w( T* U- ^2 z* a! u
                    if($array[$i][$j] != null){
    0 ]! s, k6 r2 {                        $fm2 += $array[$i][$j] * $array[$i][$j];
    7 |. o4 Z( w1 A$ F. B                }                       
    / Q" I# w4 K" }# n5 w        }. ?6 \9 g0 P) [! O$ c1 E3 j8 d% R- [
            $fm2 = sqrt($fm2);
    ' w7 C6 L- b; ]- a% z        $cos[$i] = $fz/$fm1/$fm2;
    % m6 k- c# ^, X1 ~        echo $cos[$i]."<br/>";
    # r/ W9 }$ M! s. i' `1 g+ A}% }) c- \4 u9 {3 k7 O( Y" j

    % Z0 A  G) F6 }" s- g* b* D; O这一步得到的结果是酱紫:, H% D9 y& G4 I; u0 ~# h
    . `( Q# r6 @6 F. K, O5 d
    将求好的Cos值排序,采用快排代码如下(百度copy而来):
    ' J3 \, T- `. o4 i- U$ h+ g0 y# M7 }8 m; N6 I0 h

    , {2 m- b2 n1 k, X' T$ X//对计算结果进行排序,凑合用快排吧先
    ; z/ `( W" B' ^; V# y: P4 yfunction quicksort($str){
    8 n4 P" C- k8 d6 T8 X        if(count($str)<=1) return $str;//如果个数不大于一,直接返回
    6 S% Z4 Q3 W; q        $key=$str[0];//取一个值,稍后用来比较;  L! s4 `* t' D
            $left_arr=array();$ s. z+ H5 [; w" g% q2 e
            $right_arr=array();
    8 _5 S# v5 {5 n3 v        % q3 f1 O7 j5 b* K8 x3 k) j+ |
            for($i=1;$i<count($str);$i++){//比$key大的放在右边,小的放在左边;
    , r4 n( C  i6 |' S$ k" S. [                if($str[$i]>=$key)
    1 V2 Y# z9 d( S- g% @8 @                $left_arr[]=$str[$i];5 f( o3 J6 `; I, I3 w
                    else
    $ K3 B6 Y! O- B" ^                $right_arr[]=$str[$i];
    2 P# U7 @2 s' v. U1 U        }5 D) V8 R+ q+ [; G, j4 r4 f0 s7 O
            $left_arr=quicksort($left_arr);//进行递归;$ m( z) U, g& C" c6 `) c
            $right_arr=quicksort($right_arr);/ W' x( X: X$ @% z
            return array_merge($left_arr,array($key),$right_arr);//将左中右的值合并成一个数组;
    % d* ?5 w3 ~. {$ J  \}
    1 i8 R$ ~. }$ A, @- C: |* o( A- M5 n- @+ Y  B9 C
    $neighbour = array();//$neighbour只是对cos值进行排序并存储/ ~* K& r9 A' e# l. y
    $neighbour = quicksort($cos);
    4 Q+ |4 a9 B8 k" N, K, q3 K
    + P; A& k9 T# R, }7 s' v- x; B6 v9 e/ T7 z2 m; ^' b) @. j; _
    这里的$neighbour数组仅仅存储了从大到小排序好的Cos值,并没有与人联系起来。这个问题还要解决。1 |" Y! ?; {$ J- k

    4 P  n( z9 c6 E' C* c( i选出Cos值最高的3个人,作为Leo的邻居:  C* j# k, ~6 V3 ]

    ( F1 M4 d* [- y//$neighbour_set 存储最近邻的人和cos值
      }3 r5 q9 l; m$neighbour_set = array();
    5 E- D6 D0 \& ^3 k; s  }for($i=0;$i<3;$i++){
    " ^# e: j$ S5 J0 [3 _7 x$ O        for($j=0;$j<5;$j++){
    ; q3 h3 p/ H  j5 q! n# t                if($neighbour[$i] == $cos[$j]){& ]( T- ?) P' |6 F
                            $neighbour_set[$i][0] = $j;
    0 i0 W5 ?, ~! A/ X5 V                        $neighbour_set[$i][1] = $cos[$j];
    ; z3 n( W% W8 E9 \+ k                        $neighbour_set[$i][2] = $array[$j][6];//邻居对f的评分
    / p9 _+ d0 b) G* l                        $neighbour_set[$i][3] = $array[$j][7];//邻居对g的评分
    / Y0 O3 s5 t, N: a8 ^& S                        $neighbour_set[$i][4] = $array[$j][8];//邻居对h的评分
    3 I% H0 v; Y. ~$ X; m. i  Y: o                }; ]6 }" H( z! F9 R
            }
    1 I8 v) R2 c/ n}! K+ p9 \$ Y1 u
    print_r($neighbour_set);8 q" C  s6 R; s* B5 S! P: O
    echo "<p><br/>";
    ; q, C; |! O! V7 {9 i  G& k. D( J( r/ h2 z
    这一步得到的结果是酱紫:/ |- ~" u. |- A
    9 _3 q2 l& \. x% {
    " Y8 E" _3 h' A/ i- k1 ?

    5 n2 e7 O5 C. p6 T转存失败重新上传取消6 J. F5 ~. `, g4 ^7 K! O
    ! G% p; t9 H) q0 q
    这是一个二维数组,数组第一层的下标为0,1,2,代表3个人。第二层下标0代表邻居在数据表中的顺序,比如Jhon是表中的第0个人;下标1代表Leo和邻居的Cos值;下标2,3,4分别代表邻居对f,g,h的评分。- ^7 q9 q9 L# Q# o( E# n

    " V* j3 t1 E+ t6 Q& `开始进行预测,计算Predict代码如下:8 M/ v& R* h% K$ V. R
    & ~( I6 N) h2 a; A& z- ~3 n
    我是分别计算Leo对f,g,h的预测值。在此有一个问题,就是如果有的邻居对f,g,h的评分为空,那么该如何处理。比如Jhon和Mary对h的评分就为空。本能的想到用if判断一下,如果为空则跳过这组计算,不过这样处理是否合理,有待考虑。以下代码并没有写出这个if判断。- }. C- K- D/ a- W( Z5 I
    6 V3 `+ J: P0 p( z' i+ B% m
    //计算Leo对f的评分# G" A; B. @# A7 s( W9 @7 x, j
    $p_arr = array();" c  f( a% V& L
    $pfz_f = 0;. e/ M& n" e& N
    $pfm_f = 0;( w- x( F% W/ c* }/ l
    for($i=0;$i<3;$i++){
    0 o' ?5 X9 C' V. b8 u* s        $pfz_f += $neighbour_set[$i][1] * $neighbour_set[$i][2];3 A/ h; E8 W. }, c
            $pfm_f += $neighbour_set[$i][1];
    - p! O8 E; W, ]5 Y, Y}3 U1 B$ c& i% {& t. V& N
    $p_arr[0][0] = 6;
    2 V) W" p/ N7 M6 X+ A( s# W3 }$p_arr[0][1] = $pfz_f/sqrt($pfm_f);% {, M) W6 }  u: u* H. C% x+ d9 n
    if($p_arr[0][1]>3){
    6 j7 A" C# a( R( V        echo "推荐f";
    & A, X3 ~/ X6 ^! ]' l" N}6 b" [" O8 C' E5 W" H- Z2 a

    7 Z/ D* \/ p6 {* {//计算Leo对g的评分
    ' v7 ?4 }# i9 g! g9 ?# ~8 K$pfz_g = 0;
    % O- ?/ ]9 o# ^5 y5 X5 c' T5 N$pfm_g = 0;
    8 l9 c2 V3 C" A: I& w$ V  Zfor($i=0;$i<3;$i++){8 q( y; Z1 B! u% M. i0 y. F' m
            $pfz_g += $neighbour_set[$i][1] * $neighbour_set[$i][3];' P6 G2 k3 z# [+ D* Z. _$ z
            $pfm_g += $neighbour_set[$i][1];
    ' t" S1 v# M/ i4 D* _2 W        $p_arr[1][0] = 7;) T' j! A! e2 U5 d. \
            $p_arr[1][1] = $pfz_g/sqrt($pfm_g);0 k9 _; ^9 V; L5 `, }9 m  F4 K
    }# k" o  n" c; W7 [; p
    if($p_arr[0][1]>3){/ I+ L0 |" R+ a3 w% Y* g) `9 A
            echo "推荐g";  l/ g1 ^7 a; ^6 T3 B+ M0 c
    }
    9 p1 e7 J  J! z+ q$ }5 w/ Z5 k% z) c* V! e( w; G' Q( F2 S; N
    //计算Leo对h的评分5 l4 H) `( F/ C& _
    $pfz_h = 0;
    0 @. R/ w$ \( A3 X: |5 M$pfm_h = 0;
    + f& k/ ^# V/ V& Vfor($i=0;$i<3;$i++){
    3 G7 ]% K- L8 g  Z        $pfz_h += $neighbour_set[$i][1] * $neighbour_set[$i][4];
    ; h" T1 Q5 e/ _  z        $pfm_h += $neighbour_set[$i][1];
    2 P+ j9 e) k6 M' |: L+ p        $p_arr[2][0] = 8;
    8 Q" ~0 D: d6 `5 e0 X6 }0 |/ `) W" m        $p_arr[2][1] = $pfz_h/sqrt($pfm_h);
    8 L1 f5 K% J9 q" _) l}7 H7 w) K% `6 m5 F7 q! Z# a
    print_r($p_arr);
    2 o% v) T* }6 G/ i; C' r, @if($p_arr[0][1]>3){
    / d: X1 [4 M- G; G% O0 [        echo "推荐h";! ]; }) Y* C& }) d
    }7 U# G4 L: [* N+ ~- q. v' {, }' [' e  I9 s

    8 |% o/ m, c  w4 D, ^) U: h$p_arr是对Leo的推荐数组,其内容类似如下;
    ( \/ [8 \1 Z4 G6 f6 p. s; D
    9 D3 K7 J) y3 N- s* c* F* JArray ( [0] => Array ( [0] => 6 [1] => 4.2314002228795 ) [1] => Array ( [0] => 7 [1] => 2.6511380196197 ) [2] => Array ( [0] => 8 [1] => 0.45287424581774 ) )/ k. n2 f# h$ u% D$ k$ M( j

    ) g0 Z4 b; F5 ]f是第6列,Predict值是4.23,g是第七列,Predict值是2.65........
    3 p! x5 P3 f1 S' t/ ~( i
    4 n) ]" x: M4 f0 |! J" A* A求完了f,g,h的Predict值后有两种处理方式:一种是将Predict值大于3的物品推荐给Leo,另一种是将Predict值从大到小排序,将Predict值大的前2个物品推荐给Leo。这段代码没有写。
      A: V7 p$ L5 V0 o7 u
    ; E/ _; K! v9 D) N0 R从上面的示例中可以看出,推荐算法的实现非常麻烦,需要循环,判断,合并数组等等。如果处理不当,反而会成为系统的累赘。在实际处理中还有以下问题:2 R" U1 o8 f7 Z

    # i1 d; |' w3 p, C1.以上示例我们只对Leo进行推荐,而且我们已经知道Leo没有评价过f,g,h物品。如果放到实际的系统里,对于每一个需要进行推荐的用户,都要查询出他没有评价过哪些物品,这又是一部分开销。! Y4 e- H6 N, Q- T( S1 x

    - a( F# s  w& V& N9 g3 h" ^7 N2.不应当进行整表查询,在实际系统中可以设定一些标准值。比如:我们求Leo与表中的其他人的Cos值,如果该值大于0.80,则表示可以为邻居。这样,当我找到10个邻居之后,就停止求Cos值,避免整表查询。对于推荐物品也可以适当采用此方法,比如,我只推荐10个物品,推荐完后就停止求Predict值。- @' P6 @: Y' _4 d( Q6 ^

    / U$ q' A7 s* h. Q% b3.随着系统的使用,物品也会发生变化,今天是fgh,明天没准就是xyz了,当物品变化时,需要动态的改变数据表。
    % H" U( k' |: S9 Y! O  B) |, W: k8 R/ k6 D0 Z
    4.可以适当引进基于内容的推荐,来完善推荐算法。: _: `% p) E% X8 v2 G

    : O! t3 ], O% w; q6 p. \$ v: g3 G* R; j5.推荐的精确性问题,这个设置不同的标准值,会影响精确性。9 i2 l. s) C, [9 T
    ————————————————, e2 }' |/ s; e
    版权声明:本文为CSDN博主「星斗其文,赤子其人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    ( i  c9 K2 t. R' y' B原文链接:https://blog.csdn.net/liuliuhelingdao/article/details/1267154651 Y- D: n1 f! F  k  L6 j5 y# F
    4 L& y8 p. ]1 H# G. @. z5 h
      S' p! J  u- [3 l4 D+ y4 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-6-15 04:17 , Processed in 0.343300 second(s), 51 queries .

    回顶部