QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1890|回复: 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
    & F9 p( y# E0 W* G" n
    php+mysql实现简单的协同过滤推荐算法6 v6 }; Z$ m% }. p* e
    仅做标记。。。
    9 |( C/ |( a( x4 _* F, I" y& V* x* S* o: f5 o4 z! {$ V& P3 b
    * c3 @/ y& g" L) _

    7 v0 n5 Y  @) U- P要实现协同过滤推荐算法,首先就要理解算法的核心思想和流程。该算法的核心思想可以概括为:若a,b喜欢同一系列的物品(暂时称b是a的邻居吧),则a很可能喜欢b喜欢的其他物品。算法的实现流程可以简单概括为:1.确定a有哪些邻居 2.通过邻居来预测a可能会喜欢哪种物品  3.将a可能喜欢的物品推荐给a。/ Q) c2 [( l1 t: }# q
    - Y, C1 a: O- Y
    算法核心的公式如下:
    . W6 f) \& w& m' ]
      I. L1 s6 l/ H. r! k* W: Y1.余弦相似度(求邻居):- S) V; i; S! L0 S7 b4 L

    + P9 ^9 |( E" m: X2.预测公式(预测a可能会喜欢哪种物品):& y5 f# d* `$ N+ e9 Q# m
    * j/ Y1 E5 E' ~% _8 G
    仅从这两个公式我们就可以看出,仅仅是按照这两个公式进行计算,就需要进行大量的循环与判断,而且还涉及到排序的问题,就涉及到排序算法的选择与使用,这里我选快排,从网上copy了一段快排,直接用。总之实现起来很麻烦,在大数据情况下,更何谈效率。
    ; ?; f  X% a+ K" G& a3 w3 i. o5 }
    + A7 Z, [# A* K% r; V& i: q首先建表:
    % i4 X( {2 O0 k1 Z- L: K
    - Y  H: P  r" c  l7 [% \DROP TABLE IF EXISTS `tb_xttj`;2 N/ [0 ]2 M3 h+ L, }
    CREATE TABLE `tb_xttj` (1 E7 ?. n5 o3 H" \+ U! B" }7 Y2 m
      `name` varchar(255) NOT NULL,
    - h/ g7 f  Q5 P4 Y$ X! s  `a` int(255) default NULL,) y5 S; `9 H: n  ?& U& j% T
      `b` int(255) default NULL,# l( ^% q% v) E1 m
      `c` int(255) default NULL,
    3 f& ~( c# f( H) X) J. K  `d` int(255) default NULL,5 [: t7 U; O6 N& J
      `e` int(255) default NULL,
    . q! m3 H) B: Y) D* f  `f` int(255) default NULL,
    / M. _* F( }) p3 J  `g` int(255) default NULL,
    ( w/ N, ~; _" H+ y. q8 ]# M. d  `h` int(255) default NULL,
    # q5 V2 e/ j& C. p  PRIMARY KEY  (`name`)1 Z9 u3 v& k8 Y" s: T6 a- k
    ) ENGINE=MyISAM DEFAULT CHARSET=latin1;! K! r* g' L6 x5 @  y/ V
      g  S1 F& G' {" R  ^
    INSERT INTO `tb_xttj` VALUES ('John', '4', '4', '5', '4', '3', '2', '1', null);
    & \' m0 E- E6 C: `/ ZINSERT INTO `tb_xttj` VALUES ('Mary', '3', '4', '4', '2', '5', '4', '3', null);
    & Z( X0 Q' W# a0 M" U; I) ~INSERT INTO `tb_xttj` VALUES ('Lucy', '2', '3', null, '3', null, '3', '4', '5');1 Q  Y% C3 f  X
    INSERT INTO `tb_xttj` VALUES ('Tom', '3', '4', '5', null, '1', '3', '5', '4');
    " a, b7 p8 Q- v& s7 c; ]" ?; fINSERT INTO `tb_xttj` VALUES ('Bill', '3', '2', '1', '5', '3', '2', '1', '1');
    8 g$ z& F% f1 c3 j2 R) O7 sINSERT INTO `tb_xttj` VALUES ('Leo', '3', '4', '5', '2', '4', null, null, null);6 v2 \: q, r* K! K+ q7 {+ _6 g

    ; E% C) f9 D9 |2 M: J; S6 M2 W. ~# ~# f- x9 E0 Z/ O# O
    我这里只对最后一行的Leo进行推荐,看看f,g,h哪个可以推荐给他。8 x! q& [4 z  i3 t3 A7 _: T0 [
    + N, c2 G1 |3 u4 o0 Y8 d7 b
        用php+mysql,流程图如下:( F$ ]1 T2 [, y$ v9 ?( S

    8 a2 [% f2 H9 f; |& V4 |7 B4 _连接数据库并将其存储为二维数组的代码如下:, L5 x* y3 q1 _$ ~
    9 T  h5 z: ]8 p! C) M* b6 U: ]
    header("Content-Type:text/html;charset=utf-8");1 O% s8 S! G0 e: e1 c8 z" _4 N
    . a0 D: U% G$ |
    mysql_connect("localhost","root","admin");
    4 {2 {& z% t0 ?mysql_select_db("geodatabase");# j  n; y' M7 k, }
    mysql_query("set names 'utf8'");       
    + S5 q- I( W8 H. O0 A0 p/ H. m; d" c' ^; P0 p' N0 u; p, B$ i
    $sql = "SELECT * FROM tb_xttj";
    & M  b; c$ a5 z6 ~# r. j$result = mysql_query($sql);
    " Y) J" Z0 x6 Y3 b
    + \( @  Y: G2 v) r& \+ X$array = array();  m; d3 X' X: ~8 u* J+ w
    while($row=mysql_fetch_array($result))& |+ ?8 |$ X8 [" ^
    {: j# B+ a/ k8 J$ c% O
            $array[]=$row;//$array[][]是一个二维数组
    ) ]7 S9 g0 u" f6 B' ]! R, _/ b& j} 6 S0 \+ l5 R4 ?  o: K$ D, |3 X
    3 i# r: X5 f3 E; Z3 h, M2 }" u( Y
    问题1:这一步完全可以看做是整表查询,这种查询是大忌,对于这种小小的演示系统还可以,但是对大数据的系统,没有效率,至于如何改进,还得多学习才是。
    2 ?1 y2 V9 t# a6 S: p
      k# v# Y: D! G' ]求Leo与其他人的Cos值代码如下:& P( Z/ |9 E( X8 e
    # O2 s9 C, c% r% @
    /*0 p0 c- {/ `' g% U1 U& O) Q
    * 以下示例只求Leo的推荐,如此给变量命名我也是醉了;初次理解算法,先不考虑效率和逻辑的问题,主要把过程做出来3 R& b9 q; m# e) G4 \% @( H
    */
    4 ?/ y& z( u5 h/ ~0 i9 T- @9 `( W
    8 k! ~3 T7 B/ e2 u0 S  G+ {$cos = array();
    " M8 Z; K9 X, Q$cos[0] = 0;
    - }: g7 m7 `3 H' W$fm1 = 0;
    1 `( c# K  H8 F7 G  |/ Z//开始计算cos7 X# X4 `+ ^6 [; N7 x/ {  _
    //计算分母1,分母1是第一个公式里面 “*”号左边的内容,分母二是右边的内容
    # i+ s0 V3 y3 g( ffor($i=1;$i<9;$i++){
    ) P' o' ~( L3 r/ S8 q        if($array[5][$i] != null){//$array[5]代表Leo, B2 X2 Q; s4 }& t0 L6 I& O5 Q
                    $fm1 += $array[5][$i] * $array[5][$i];1 }" _7 a' C& b4 Q+ W# t
            }; {. L+ f! @8 P
    }
    3 U8 A- J# U' }5 D$ }
    7 M# d0 e5 v& {- y2 {2 b4 X$fm1 = sqrt($fm1);
    1 l3 O* [$ q, U7 A* p( L. I5 y
    # ^; e: l; ^: t, @for($i=0;$i<5;$i++){
    0 Q' {( E7 o6 f6 E% s  {# Z        $fz = 0;& F1 u) A0 y6 ^0 Y
            $fm2 = 0;" |( m+ k3 {, u2 N4 Z( g( f$ V' |
            echo "Cos(".$array[5][0].",".$array[$i][0].")=";2 X/ V) z; d8 m% D) O
            ( M2 l0 G- H- R* i
            for($j=1;$j<9;$j++){
    % H2 l0 A4 ?  a8 Z5 d. m- ]* N            //计算分子- I6 W3 b# f' M
                    if($array[5][$j] != null && $array[$i][$j] != null){
      c+ i* b8 q9 Y$ c$ W2 h                        $fz += $array[5][$j] * $array[$i][$j];
    3 D7 l* k$ E9 }) H" ?3 h                }- F  F1 `9 M/ \! V1 C4 i
                    //计算分母2+ d8 U' h& z4 H: m0 B  @
                    if($array[$i][$j] != null){
    # y" I0 d* Z* I" ]                        $fm2 += $array[$i][$j] * $array[$i][$j];
    . ~. B" h9 H& i2 y# I4 V                }                       
    7 K+ o( E5 O$ O* z% X: \0 f        }
    3 _! d/ W5 [; o7 m- G- A* T# W        $fm2 = sqrt($fm2);4 M3 a2 q" l. @# |5 k' z7 \
            $cos[$i] = $fz/$fm1/$fm2;  p7 }6 k8 t. o
            echo $cos[$i]."<br/>";
    1 A9 r7 ?' q3 X7 Q- ^" F* P}
    0 W% m0 G. j  E, e" N" W8 U* y
    2 [- j1 D# `1 o& z0 Q3 i: a0 F( c这一步得到的结果是酱紫:
    $ K8 Z- D* u/ l0 I: S" M" m7 y  w7 S5 x* T5 U# S
    将求好的Cos值排序,采用快排代码如下(百度copy而来):4 w3 U1 n$ C. ?& r4 E* \

    ; n5 [) p! J8 a  _1 D" }, V- K
    " R& W5 h& b# z; H//对计算结果进行排序,凑合用快排吧先6 {& Y8 w: e' N
    function quicksort($str){, M0 }9 y; u3 Q6 W! r
            if(count($str)<=1) return $str;//如果个数不大于一,直接返回
    . g6 |: t$ c1 O( T        $key=$str[0];//取一个值,稍后用来比较;5 x) X7 E; G' {0 A# y
            $left_arr=array();3 |6 d: c$ d* y& {/ G0 ^
            $right_arr=array();
    + r, J* ^% k" J/ k4 q+ S6 H       
    ' \2 v& L- f1 E        for($i=1;$i<count($str);$i++){//比$key大的放在右边,小的放在左边;
    * b0 {# f2 b0 d: d) j& r                if($str[$i]>=$key)
    ' p: \1 g) C2 h$ `, z) q                $left_arr[]=$str[$i];
    9 I) S; S8 z. m' J# [, H                else0 Z; g" }4 w; d* p% c
                    $right_arr[]=$str[$i];
    ! s$ p6 V# `9 j        }
    8 X, Z8 a! J! l4 O9 [' d        $left_arr=quicksort($left_arr);//进行递归;
    $ B7 s% A) @, h3 [2 K, I        $right_arr=quicksort($right_arr);
    : b: Y. z* \3 p& U' [0 |        return array_merge($left_arr,array($key),$right_arr);//将左中右的值合并成一个数组;
    6 i! z/ u+ d" c5 l# j9 ^6 Y+ T}( l3 ~5 V& i$ V) G& g

    3 t  s+ @4 i, {# A+ \( w4 d" y1 c: `5 L$neighbour = array();//$neighbour只是对cos值进行排序并存储- g6 q  ]* I+ f2 a; K2 w* C) p
    $neighbour = quicksort($cos);
    & b$ M9 w- H. D  ~& p
    + g" ]% H% Y8 ^% v, d" P
    7 h/ X- F8 r& T% m+ a这里的$neighbour数组仅仅存储了从大到小排序好的Cos值,并没有与人联系起来。这个问题还要解决。- u) V& ]& f  B8 g

    ! y- @$ H7 E( `0 Z, m" J  O选出Cos值最高的3个人,作为Leo的邻居:6 W3 B7 l  M  F2 Z% b
    ' A& C3 X# Z2 T' f
    //$neighbour_set 存储最近邻的人和cos值# [1 M1 ?2 r' q4 J$ H1 P
    $neighbour_set = array();
    9 x* Z) N6 U, _: e& ?) Yfor($i=0;$i<3;$i++){
    # \' Y8 l3 D% D0 L7 \0 u# q5 u        for($j=0;$j<5;$j++){
    ( Q+ a% V( r% |% v; d                if($neighbour[$i] == $cos[$j]){; w+ b& [* H/ I2 ?$ f: N& @
                            $neighbour_set[$i][0] = $j;
    ! T2 z; a* W$ f+ [/ C" x$ v                        $neighbour_set[$i][1] = $cos[$j];
    7 [# D7 \0 P5 H$ b- m                        $neighbour_set[$i][2] = $array[$j][6];//邻居对f的评分
    % y8 l7 y5 b" b; ]                        $neighbour_set[$i][3] = $array[$j][7];//邻居对g的评分
    / R" b9 ~- L- d                        $neighbour_set[$i][4] = $array[$j][8];//邻居对h的评分
    7 A5 S' X! S/ v' W2 j. T                }
    ! c1 V0 _/ X4 [# @, F1 G  P        }
    / j3 k( P/ `1 T4 ]}7 ~" j8 O' i4 G6 D# |
    print_r($neighbour_set);" N5 X  H, Y6 r/ I! r" w0 D
    echo "<p><br/>";
    ! [5 `4 `+ n5 c) @' W3 q. F0 t
    这一步得到的结果是酱紫:
      q0 p8 H9 v' O% b- h$ K" H3 O2 B4 H, z1 z: @! w
    . d# u7 Y  J2 B, H& T1 m
    9 X0 |& F% ~7 z  O7 o, J
    转存失败重新上传取消" }# F% M- z+ n% f

    5 r$ u  p6 n: D; i/ I# d0 ]+ q* i这是一个二维数组,数组第一层的下标为0,1,2,代表3个人。第二层下标0代表邻居在数据表中的顺序,比如Jhon是表中的第0个人;下标1代表Leo和邻居的Cos值;下标2,3,4分别代表邻居对f,g,h的评分。
    7 Q2 V: [" z9 Q4 T4 h% S5 B8 T' k# s# z
    开始进行预测,计算Predict代码如下:
    - d' n" a) O! b% `( e9 W
    + d8 _% k7 f! }% v4 Z- b( P$ x我是分别计算Leo对f,g,h的预测值。在此有一个问题,就是如果有的邻居对f,g,h的评分为空,那么该如何处理。比如Jhon和Mary对h的评分就为空。本能的想到用if判断一下,如果为空则跳过这组计算,不过这样处理是否合理,有待考虑。以下代码并没有写出这个if判断。
    ) {: X- O" \7 l: Z  R2 A. o/ j4 |& q8 H3 m$ X3 T) a/ t7 I) I
    //计算Leo对f的评分
    $ u- C! r4 x5 t- b$p_arr = array();
    - C) m5 b* A8 j  m$pfz_f = 0;$ w  A4 m, Q* W  ~4 U6 A& O
    $pfm_f = 0;
    8 j% s, P: o0 M! P# afor($i=0;$i<3;$i++){
    : a( h0 `2 d. ~: l        $pfz_f += $neighbour_set[$i][1] * $neighbour_set[$i][2];
    2 ?# x6 I' R1 t! A4 x, y        $pfm_f += $neighbour_set[$i][1];
    6 z3 V3 o8 Y$ P0 n7 h5 w}
    ! g$ c4 M5 j! @+ D- g5 Q' ~$p_arr[0][0] = 6;8 ^7 t9 `6 n+ F2 @  S! c; \5 u
    $p_arr[0][1] = $pfz_f/sqrt($pfm_f);* a! M, j2 [* M7 [$ w9 b- q
    if($p_arr[0][1]>3){
    5 \% ?- O% T: x! W        echo "推荐f";8 h; m- f, |/ l3 t6 w* t. B# Y8 a
    }
    ' C5 x% E9 y  [" X% Z% R$ W; n0 q
    8 Y! V/ }: N+ m8 D, f/ j# [//计算Leo对g的评分
    / h3 q* h# M1 X, ]$pfz_g = 0;
    * j' g9 V8 v! ~# W8 L' i$pfm_g = 0;
    $ w/ z' Q4 ^* W. c" P1 Z0 ]3 hfor($i=0;$i<3;$i++){
    & Q9 v; |6 W/ I        $pfz_g += $neighbour_set[$i][1] * $neighbour_set[$i][3];& |0 d. A# x& u1 y7 q
            $pfm_g += $neighbour_set[$i][1];1 u  Z5 @. b$ Y. y5 Y$ M9 X
            $p_arr[1][0] = 7;
    4 }8 }7 t7 h# i7 e        $p_arr[1][1] = $pfz_g/sqrt($pfm_g);
    9 A2 J$ a/ Z5 V5 c. K}
    - y& S! P5 b$ r, I) E4 u4 Qif($p_arr[0][1]>3){
    ) B9 W! M0 p& B1 O- M. o' w        echo "推荐g";- X) q- T9 J/ }  A
    }
    . Z% k( z6 l3 p& I0 ^/ @! B
    8 A+ o( x; P% q5 @; I# U//计算Leo对h的评分# i0 d- _3 q6 w4 u0 Y% ?
    $pfz_h = 0;4 \* H8 V5 i/ X
    $pfm_h = 0;
    * K/ ?8 i+ a1 _" Q9 J, q4 b" Efor($i=0;$i<3;$i++){9 e) \) _' S2 |/ K; y1 |# h
            $pfz_h += $neighbour_set[$i][1] * $neighbour_set[$i][4];" J# Y& e* t" m  h
            $pfm_h += $neighbour_set[$i][1];
    0 ]" \4 Y6 A4 \6 I/ f4 c# s        $p_arr[2][0] = 8;" ^$ X4 d' O( j" M! K) w
            $p_arr[2][1] = $pfz_h/sqrt($pfm_h);
    : u% G2 q. a2 N* r}
    ; d! E& }2 J5 f4 e! c' Z0 o; Pprint_r($p_arr);& x4 t' P# {8 F3 ]7 z
    if($p_arr[0][1]>3){5 }$ |+ z2 {( N& T# J# q0 G8 T+ O
            echo "推荐h";  o$ o0 m; Q) c* p" L0 {1 V, \
    }% _- Q* U5 r4 T0 l
    - ~% q- I' X, B7 T- z
    $p_arr是对Leo的推荐数组,其内容类似如下;
      I! Z2 x$ B7 ?% t* T; ]4 A3 ^
    # L' Y$ h( ]+ Z; e! X# yArray ( [0] => Array ( [0] => 6 [1] => 4.2314002228795 ) [1] => Array ( [0] => 7 [1] => 2.6511380196197 ) [2] => Array ( [0] => 8 [1] => 0.45287424581774 ) )
    : C5 {6 |3 j& a5 a5 M3 I+ o% y. s4 W9 d
    f是第6列,Predict值是4.23,g是第七列,Predict值是2.65........
    2 d% g8 n( F% y; }- Z8 F& n, R6 \* E0 K* L
    求完了f,g,h的Predict值后有两种处理方式:一种是将Predict值大于3的物品推荐给Leo,另一种是将Predict值从大到小排序,将Predict值大的前2个物品推荐给Leo。这段代码没有写。
      n- U' n4 Y# `+ t/ g' K
    % o  x0 x' o1 B% m从上面的示例中可以看出,推荐算法的实现非常麻烦,需要循环,判断,合并数组等等。如果处理不当,反而会成为系统的累赘。在实际处理中还有以下问题:, t! I" K  P% T* e% @

    5 K* J3 @+ `. B" O. v! [1.以上示例我们只对Leo进行推荐,而且我们已经知道Leo没有评价过f,g,h物品。如果放到实际的系统里,对于每一个需要进行推荐的用户,都要查询出他没有评价过哪些物品,这又是一部分开销。& w& {( N) Y% A$ v
    ) x- x- M5 O& W, ]
    2.不应当进行整表查询,在实际系统中可以设定一些标准值。比如:我们求Leo与表中的其他人的Cos值,如果该值大于0.80,则表示可以为邻居。这样,当我找到10个邻居之后,就停止求Cos值,避免整表查询。对于推荐物品也可以适当采用此方法,比如,我只推荐10个物品,推荐完后就停止求Predict值。
    * L8 }3 a& l# Z  I/ P1 R
    ; g+ I3 A7 J% }5 p9 l3.随着系统的使用,物品也会发生变化,今天是fgh,明天没准就是xyz了,当物品变化时,需要动态的改变数据表。/ J/ V$ f- ?% ^& s
    ( }( ]6 {  Z9 A" a" [
    4.可以适当引进基于内容的推荐,来完善推荐算法。/ A* Z: Z9 _1 p% D* |- b! Q: S

    1 K- E+ [  V0 j6 s8 J' f, z9 d5.推荐的精确性问题,这个设置不同的标准值,会影响精确性。1 n# N$ @4 {% Y" p8 h2 x
    ————————————————: ^; J7 ]! ~9 O$ ]6 ?5 b5 Y; R! b
    版权声明:本文为CSDN博主「星斗其文,赤子其人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。  w4 ?' d5 A% T6 t
    原文链接:https://blog.csdn.net/liuliuhelingdao/article/details/126715465
    2 N. e1 k$ n1 w; S6 l) O
    ; S2 O1 k" L0 O" C- k, P9 W! c; ^+ x- N4 P) Q+ \
    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-10 14:53 , Processed in 0.327313 second(s), 51 queries .

    回顶部