QQ登录

只需要一步,快速开始

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

    7 L+ B9 u' k0 T& Vphp+mysql实现简单的协同过滤推荐算法9 L# M* f9 Q# \
    仅做标记。。。6 Q% G( R; X- F* H, M

    ! q! z, M/ U/ `6 {
    5 q# M: E) A3 S" I* n/ u- o- F: E4 g4 u+ O" b& U2 v1 M7 F1 ?( {
    要实现协同过滤推荐算法,首先就要理解算法的核心思想和流程。该算法的核心思想可以概括为:若a,b喜欢同一系列的物品(暂时称b是a的邻居吧),则a很可能喜欢b喜欢的其他物品。算法的实现流程可以简单概括为:1.确定a有哪些邻居 2.通过邻居来预测a可能会喜欢哪种物品  3.将a可能喜欢的物品推荐给a。
    + m" ^  ^7 Y, ~* J9 @
      q2 f! M9 b8 b5 W. _+ j* Z' z算法核心的公式如下:
    ) C; ~- l( }  F3 J9 k: ^6 H3 V# A4 K0 k, ~: r
    1.余弦相似度(求邻居):# X3 _; [" h/ A8 J! @0 a
    3 K7 j; q2 y! M9 E8 {' W7 Z4 @
    2.预测公式(预测a可能会喜欢哪种物品):8 P) Y% _) m" }. k" q  Y

    / U% u; W) l0 O& |9 G$ `) I仅从这两个公式我们就可以看出,仅仅是按照这两个公式进行计算,就需要进行大量的循环与判断,而且还涉及到排序的问题,就涉及到排序算法的选择与使用,这里我选快排,从网上copy了一段快排,直接用。总之实现起来很麻烦,在大数据情况下,更何谈效率。; k" M' r3 D* N4 L
    ) b' l9 m( d+ {+ ~; U  l5 v( c( z
    首先建表:$ N' g4 q: ^; Q# S' M

    9 p+ B' H# Q3 {3 ]3 I1 W' ?! wDROP TABLE IF EXISTS `tb_xttj`;
    4 v7 V- z* C& ~1 [) rCREATE TABLE `tb_xttj` (+ b# ^* c5 n9 b5 O7 V- H
      `name` varchar(255) NOT NULL,
    ) y8 W# Q% m1 M/ ]5 q) X0 d  `a` int(255) default NULL,
      @( l1 ]7 C9 R' X+ K% }1 Y  `b` int(255) default NULL,  X- ^4 t7 K- Z6 B
      `c` int(255) default NULL,
    ; F: m& O, Z+ c  `d` int(255) default NULL,
    5 i. N( i; J* _1 q$ O5 A' s" h- h  `e` int(255) default NULL,2 Y, n7 e8 P* C7 W4 H( a% k
      `f` int(255) default NULL,% h7 P8 ]* D9 d% w" ]
      `g` int(255) default NULL,' \' [/ Y$ c0 A/ Z( G* G4 ?8 I
      `h` int(255) default NULL,* [1 b1 W+ }" H- [. ~
      PRIMARY KEY  (`name`): A! b: d9 D7 c3 s$ m8 r( ^
    ) ENGINE=MyISAM DEFAULT CHARSET=latin1;
    & r7 K8 O( t  |% r( q$ O. h4 A: @1 J- y1 L
    INSERT INTO `tb_xttj` VALUES ('John', '4', '4', '5', '4', '3', '2', '1', null);" W2 u+ f2 u6 E; t: v* O
    INSERT INTO `tb_xttj` VALUES ('Mary', '3', '4', '4', '2', '5', '4', '3', null);3 h. P9 r3 Q+ V/ w; Y' m
    INSERT INTO `tb_xttj` VALUES ('Lucy', '2', '3', null, '3', null, '3', '4', '5');
    , P5 S- O5 K& ?# HINSERT INTO `tb_xttj` VALUES ('Tom', '3', '4', '5', null, '1', '3', '5', '4');9 d# [: G$ I' z8 P( Z  W! o
    INSERT INTO `tb_xttj` VALUES ('Bill', '3', '2', '1', '5', '3', '2', '1', '1');
    1 q. e7 r" M+ w8 L" Q2 P$ v& y- q4 \INSERT INTO `tb_xttj` VALUES ('Leo', '3', '4', '5', '2', '4', null, null, null);- f* F# n2 N4 h
    1 e4 s, V5 u7 F3 L5 S* r% v' a% A, U
    0 L+ F# w* {9 q. U
    我这里只对最后一行的Leo进行推荐,看看f,g,h哪个可以推荐给他。) g4 l+ W/ |1 D7 J
    - F! @* n% O9 b" J, \# j+ g! s
        用php+mysql,流程图如下:8 o  B7 V- _! w* x% [' N
    / }( L+ j2 M, ?5 Z( B+ t
    连接数据库并将其存储为二维数组的代码如下:- @# u6 J" p  z* k2 R- }1 w& Z
    ! Q2 E' q+ I+ a% d/ G' W
    header("Content-Type:text/html;charset=utf-8");+ c- A" O3 N3 U- @1 ]

    8 }; t3 s1 R6 P$ Tmysql_connect("localhost","root","admin");
    ' H( _% ^3 }$ {; v3 h# Umysql_select_db("geodatabase");
    4 ?2 H4 A6 `+ W7 D  |( {. H/ ?$ vmysql_query("set names 'utf8'");        ' i& \' f+ L( @! ~; r4 V8 w

    + u" ^% N6 L. m7 j% I" p$sql = "SELECT * FROM tb_xttj";) r$ L; m# D' a! E  P% e
    $result = mysql_query($sql);; V# e/ H1 O9 h$ `4 I9 z
    + p: Q3 O8 N$ }: L7 |' G
    $array = array();
    . m$ P8 I5 j5 B8 Q+ r2 awhile($row=mysql_fetch_array($result))$ S: G, W/ ~# F; t  Y. N1 U
    {( N! u7 U1 s8 b2 u
            $array[]=$row;//$array[][]是一个二维数组
    8 O& C: n* n3 w( p3 _2 t+ ]}
    % p8 w5 Y6 j, F3 F' q& [$ ^/ a5 S  K5 A
    问题1:这一步完全可以看做是整表查询,这种查询是大忌,对于这种小小的演示系统还可以,但是对大数据的系统,没有效率,至于如何改进,还得多学习才是。$ T6 x. i; ?. ]5 f: L% T/ ~* A
    7 g) O1 d, [" N" x7 O% ?: o, E+ \# i2 E8 X- z
    求Leo与其他人的Cos值代码如下:$ t- L: m/ D0 _" Q
    5 g5 E) _# u+ K! G9 n9 C/ I9 Q) a
    /*: H" o' O1 d5 r
    * 以下示例只求Leo的推荐,如此给变量命名我也是醉了;初次理解算法,先不考虑效率和逻辑的问题,主要把过程做出来
    8 P( `3 \6 o1 |  b) U6 [% \5 j" g */* l$ S, U' r9 g: i
    3 N1 [& M3 e  B9 k" ^  [$ K8 y
    $cos = array();
    . i: t0 ~9 G9 F  c( z  ~! n! _$cos[0] = 0;; r0 n5 u; a! ~/ F) d3 @, w4 N+ L
    $fm1 = 0;
    # b2 c+ ~# i% h% T4 s% W9 _, e0 `3 J1 Y//开始计算cos3 n& ^/ J# D, C3 v" v* z0 y
    //计算分母1,分母1是第一个公式里面 “*”号左边的内容,分母二是右边的内容2 Y$ N6 N1 z; |3 g
    for($i=1;$i<9;$i++){/ x) f! ^1 i" _4 @7 ]
            if($array[5][$i] != null){//$array[5]代表Leo) i9 P0 M& P  p3 j
                    $fm1 += $array[5][$i] * $array[5][$i];
    : m3 M" r8 G; G6 I4 S! @  n        }
    : T/ m9 l9 R+ Y" E6 q+ o' N) ~* q}
    . @, X- K; n& P/ |) j; v& B6 q+ F, z! V/ W
    $fm1 = sqrt($fm1);  n( j. L& @  i* O3 A; T2 u$ @
    0 E- [! }2 v- X# t0 q* n* d
    for($i=0;$i<5;$i++){$ y/ Q; d4 d' l& X, I- \/ c  G
            $fz = 0;
    $ h( Y% u: e* q1 O1 z* \0 P        $fm2 = 0;3 I- e, F0 h5 U, g/ M
            echo "Cos(".$array[5][0].",".$array[$i][0].")=";" i4 I5 L) F" G3 Z+ R
            ' k* T8 H6 t* F5 c
            for($j=1;$j<9;$j++){
    1 |6 U8 d( `! z: h  J2 l0 \- A            //计算分子$ X8 z0 o* ^8 s7 Q: e
                    if($array[5][$j] != null && $array[$i][$j] != null){( h; U% I" f7 i3 @  L" |& Y
                            $fz += $array[5][$j] * $array[$i][$j];
    % B: M& Q( n. `                }
    % {) N" c) g8 b, l                //计算分母2/ e5 U! T) Q( r) X
                    if($array[$i][$j] != null){
    , f) O. k2 E, k# g& E9 ~                        $fm2 += $array[$i][$j] * $array[$i][$j];# `$ P. @2 s; [- @
                    }                       
    4 A0 S5 K) k7 r9 ^. w) X. y5 ^  s        }
    8 O+ u" l$ Z/ |  s        $fm2 = sqrt($fm2);
    ! {) f5 D3 u3 X1 `: D        $cos[$i] = $fz/$fm1/$fm2;
    $ B( B  V: J4 o. U  h$ d9 a' ?        echo $cos[$i]."<br/>";$ b4 ~1 D7 t1 {; P
    }' g9 q, Z# Z- N- F5 p( x# b1 Y

    2 s6 @  Z3 s* C这一步得到的结果是酱紫:
    6 c# H) q1 n! }9 u& B/ A. F* a+ I8 R7 w
    将求好的Cos值排序,采用快排代码如下(百度copy而来):( N8 e. s6 u* M9 g* t: H/ o$ d
    4 n) N3 h* A- V/ O# z9 H9 @  ^$ ]

    # Z9 |* u. J' D' ^- |//对计算结果进行排序,凑合用快排吧先* K; G; g7 b; r/ K* g5 @9 ]
    function quicksort($str){
    ; _# p% }8 j" n, e! D$ z- B. K        if(count($str)<=1) return $str;//如果个数不大于一,直接返回- _; \9 ]0 d- `* R# X5 K& L5 l7 O4 v
            $key=$str[0];//取一个值,稍后用来比较;( T; T2 x3 Q; W4 ~4 g% B$ R
            $left_arr=array();3 _! q% u5 o4 W! ^0 G
            $right_arr=array();% a0 E. J  ?" X. N2 S& q- K
           
    : v$ m1 r+ o9 i% o5 c) e; |5 T0 [0 A        for($i=1;$i<count($str);$i++){//比$key大的放在右边,小的放在左边;* i/ W- a" I* c$ F
                    if($str[$i]>=$key)! t, \+ @+ w7 d; L4 d# }
                    $left_arr[]=$str[$i];
    3 b) O/ w. e1 }( L                else
    + H7 V9 z2 k2 f% h) J5 Q/ o4 n                $right_arr[]=$str[$i];- X% _7 Q' q, y3 r5 ^7 g2 Q
            }
    1 C; F" U% _$ ?6 P, {& T        $left_arr=quicksort($left_arr);//进行递归;
    : ?0 G) r8 J- r* g% @1 a        $right_arr=quicksort($right_arr);
    % u; ~* l9 O" ~5 q9 `        return array_merge($left_arr,array($key),$right_arr);//将左中右的值合并成一个数组;( c' ]0 S- E1 |- l8 `0 _
    }  s9 d  [- t3 w  E3 w9 g7 j

    " F9 k% O. h% e2 T7 d( z5 a$neighbour = array();//$neighbour只是对cos值进行排序并存储
    : h8 O4 t4 J3 \. H# w# _$neighbour = quicksort($cos);
    # B2 L* k! ]" E
    & [2 p8 j. T6 @- b  D' H" q
    ; n+ o, h$ t2 [, B" [2 m" j4 v" t这里的$neighbour数组仅仅存储了从大到小排序好的Cos值,并没有与人联系起来。这个问题还要解决。' w( U4 \5 t7 Y. T* [4 e, j6 e/ N
    : g0 b. ?* I, A  h/ n! j  Y9 U
    选出Cos值最高的3个人,作为Leo的邻居:
    & h7 T" f; @8 ~& O1 Z( P& n+ R2 W) h; A0 e, X6 N& }& j
    //$neighbour_set 存储最近邻的人和cos值
    ( Z' P6 j+ R/ h& O$neighbour_set = array();
    : p% F1 ^7 Z- E4 C8 ?# m- u* y3 ofor($i=0;$i<3;$i++){; m: ^6 L( F# _  [7 _1 x  p( D
            for($j=0;$j<5;$j++){
    ! t' Y0 e$ f1 r9 L- R                if($neighbour[$i] == $cos[$j]){: \; Z3 C* l' @
                            $neighbour_set[$i][0] = $j;1 U3 e+ V1 s% L: P# s4 k
                            $neighbour_set[$i][1] = $cos[$j];9 v& y/ H9 ?- V0 y
                            $neighbour_set[$i][2] = $array[$j][6];//邻居对f的评分
      o3 c; _1 W% R9 D/ ^                        $neighbour_set[$i][3] = $array[$j][7];//邻居对g的评分
    & Y* ]  P( {' h# V8 w                        $neighbour_set[$i][4] = $array[$j][8];//邻居对h的评分
    6 ]  h7 e. d) y9 b5 `5 L$ z$ g+ a6 a                }
    7 ?* V& x/ K  i1 V        }' z7 r; p) ?, Z8 r# {) M5 W
    }
    ' \# b" M0 e; m4 \' z) Xprint_r($neighbour_set);
    / R/ |3 m% _7 ]& Secho "<p><br/>";- U/ G# X1 W# y

    5 G4 Q4 ^+ h2 e9 q/ w这一步得到的结果是酱紫:
    : c+ o  \+ @( v  O9 h$ ~+ {5 g  q: `+ [6 I# b9 A0 b

    6 v8 J7 V# W; d: j2 U. s/ q' U  F. B8 B  H2 ?% Q
    转存失败重新上传取消; F. i1 |, s$ G7 I3 I6 Q" {

    1 X( }: L* p& |+ n& p' n9 w这是一个二维数组,数组第一层的下标为0,1,2,代表3个人。第二层下标0代表邻居在数据表中的顺序,比如Jhon是表中的第0个人;下标1代表Leo和邻居的Cos值;下标2,3,4分别代表邻居对f,g,h的评分。
    - R. v% }0 |$ {* M1 U" R+ P
    9 h; L  C  j3 u( D- t开始进行预测,计算Predict代码如下:+ n4 k  @4 z3 ~" i7 V
    ; f# v4 {4 i" J1 @% I
    我是分别计算Leo对f,g,h的预测值。在此有一个问题,就是如果有的邻居对f,g,h的评分为空,那么该如何处理。比如Jhon和Mary对h的评分就为空。本能的想到用if判断一下,如果为空则跳过这组计算,不过这样处理是否合理,有待考虑。以下代码并没有写出这个if判断。) e# i0 X* `8 ^( f5 ^6 m- G4 v, {
    . \; h# ]& x* d
    //计算Leo对f的评分- y9 ~- V7 T0 S, ]1 U$ E# Q3 G* ]' {
    $p_arr = array();
    4 P6 |/ ^& E% s' z8 v/ K1 _. c6 o$pfz_f = 0;
    : k9 |) ?8 H: f0 Y2 Z0 w) K$pfm_f = 0;4 M) S: {6 }6 l: P7 ^
    for($i=0;$i<3;$i++){
    7 B8 k5 ]" R7 v* ~+ l6 d6 U) a9 D, k        $pfz_f += $neighbour_set[$i][1] * $neighbour_set[$i][2];
    ! ~6 F6 q8 ?2 N/ p& u1 s4 J; ?7 a        $pfm_f += $neighbour_set[$i][1];: |8 z# O7 e+ B9 ?* @- {
    }
    6 j% B/ t. d( E$p_arr[0][0] = 6;
    2 v* D' r) I/ y$p_arr[0][1] = $pfz_f/sqrt($pfm_f);
    ) A% Q( y4 U2 a: Pif($p_arr[0][1]>3){
    8 ^6 r# P+ F1 O9 d* [0 b0 ]# L        echo "推荐f";! m1 m8 I8 d: K7 }( [7 {8 G. }. @
    }6 V6 _3 N+ I  w9 z: }

    5 r; h2 {2 ]0 L8 G9 e: N/ G1 a0 Z4 E! `//计算Leo对g的评分0 q" e3 V7 i% p3 E% I
    $pfz_g = 0;
      p: Y; \. }, i& ~  \) n$pfm_g = 0;2 Y4 A0 ]- D% B
    for($i=0;$i<3;$i++){3 A' A4 c8 T. V- _" b
            $pfz_g += $neighbour_set[$i][1] * $neighbour_set[$i][3];) V  J- ?* T; t; n1 [, H4 b9 Q
            $pfm_g += $neighbour_set[$i][1];' ?& A5 \6 N+ W& e
            $p_arr[1][0] = 7;
    & Z$ R2 b$ {1 W, q% l- O        $p_arr[1][1] = $pfz_g/sqrt($pfm_g);
    ' Y5 {  A% s& a}
    " `/ ]" F5 d' _if($p_arr[0][1]>3){- ~( T/ V. j. D. {8 ^4 _; w% y
            echo "推荐g";0 F$ R  e) d. R+ |
    }+ W+ _7 {$ w, y; u: a1 Z4 `$ d& q
    5 B) q; X; n. E+ k
    //计算Leo对h的评分
    9 _- H# F/ m+ p$pfz_h = 0;
    2 Z& @4 Y9 l4 C" v( G1 Q/ I# w$pfm_h = 0;
    4 `+ V: D5 f( Bfor($i=0;$i<3;$i++){" e4 \" o* G0 |
            $pfz_h += $neighbour_set[$i][1] * $neighbour_set[$i][4];
    " n& I4 ~! a5 W9 N        $pfm_h += $neighbour_set[$i][1];
    4 y: s+ o$ l. r/ ]7 J/ _. r        $p_arr[2][0] = 8;8 p, _: d0 G! i( p$ l8 Z
            $p_arr[2][1] = $pfz_h/sqrt($pfm_h);
    & Z9 N- n5 B* V7 k1 N( K6 V% c}7 E: i$ F, i+ ]3 D, i( R
    print_r($p_arr);1 F+ L% T- y8 }# D2 T% {% j
    if($p_arr[0][1]>3){
    . m. m: b3 r0 n, ?& d        echo "推荐h";
    # C( n% c' c5 A: N: P$ N}
    ! V+ ?/ r6 c) P& @8 i9 Q8 x1 O2 e8 [/ _. B% [, ^8 ^& u7 w% Q
    $p_arr是对Leo的推荐数组,其内容类似如下;
    2 C# q  u# i0 R+ y* ^* s, h
    8 m& v0 h$ P6 M. LArray ( [0] => Array ( [0] => 6 [1] => 4.2314002228795 ) [1] => Array ( [0] => 7 [1] => 2.6511380196197 ) [2] => Array ( [0] => 8 [1] => 0.45287424581774 ) )
    1 d) l+ A3 x  Z: {
    / N: U; j% l; i) A, F6 Gf是第6列,Predict值是4.23,g是第七列,Predict值是2.65........$ k2 K% f+ _/ z( b" C; k
    , t9 c& h- C* q+ ^/ ?; A
    求完了f,g,h的Predict值后有两种处理方式:一种是将Predict值大于3的物品推荐给Leo,另一种是将Predict值从大到小排序,将Predict值大的前2个物品推荐给Leo。这段代码没有写。
    7 |4 R7 K1 T1 N9 H9 A$ ?7 Z! _3 W9 y8 {5 {' ?3 r5 o) V
    从上面的示例中可以看出,推荐算法的实现非常麻烦,需要循环,判断,合并数组等等。如果处理不当,反而会成为系统的累赘。在实际处理中还有以下问题:  @# ~: W% x3 |4 L% I
    % n' P0 K8 x8 s6 L/ C) P1 G: W: d0 b
    1.以上示例我们只对Leo进行推荐,而且我们已经知道Leo没有评价过f,g,h物品。如果放到实际的系统里,对于每一个需要进行推荐的用户,都要查询出他没有评价过哪些物品,这又是一部分开销。9 z  t8 W5 t/ f6 H" d2 s" W
    / H7 g8 l4 K/ u. i' ~
    2.不应当进行整表查询,在实际系统中可以设定一些标准值。比如:我们求Leo与表中的其他人的Cos值,如果该值大于0.80,则表示可以为邻居。这样,当我找到10个邻居之后,就停止求Cos值,避免整表查询。对于推荐物品也可以适当采用此方法,比如,我只推荐10个物品,推荐完后就停止求Predict值。
    * W4 J% X4 n$ S9 A; Z; i8 g
    8 t8 ?9 R! A) I( c% i1 l3.随着系统的使用,物品也会发生变化,今天是fgh,明天没准就是xyz了,当物品变化时,需要动态的改变数据表。
    & q2 j1 B" v9 X8 i
    2 ^- M- @6 x5 Q; Y4.可以适当引进基于内容的推荐,来完善推荐算法。' s9 f5 Q7 E8 _$ z+ d1 w* f

    " Z3 K& U% ^1 _  x0 f: M  V" k5 C" I5.推荐的精确性问题,这个设置不同的标准值,会影响精确性。$ ]. I/ O$ R" S4 {# E) U
    ————————————————0 w5 X9 X5 t; p: L
    版权声明:本文为CSDN博主「星斗其文,赤子其人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。4 n! |8 r6 N  H  y8 x) P
    原文链接:https://blog.csdn.net/liuliuhelingdao/article/details/126715465/ ?1 e" {& F9 U8 x

    ) R1 f& \& f4 J8 C4 ?0 X. d9 F) ]0 I) C( a  K6 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-7-28 22:21 , Processed in 0.315501 second(s), 51 queries .

    回顶部