当前位置: 首页 > news >正文

【机器学习 | 数据挖掘】智能推荐算法

在这里插入图片描述

【作者主页】Francek Chen
【专栏介绍】 ⌈ ⌈ 智能大数据分析 ⌋ ⌋ 智能大数据分析是指利用先进的技术和算法对大规模数据进行深入分析和挖掘,以提取有价值的信息和洞察。它结合了大数据技术、人工智能(AI)、机器学习(ML)和数据挖掘等多种方法,旨在通过自动化的方式分析复杂数据集,发现潜在的价值和关联性,实现数据的自动化处理和分析,从而支持决策和优化业务流程。与传统的人工分析相比,智能大数据分析具有自动化、深度挖掘、实时性和可视化等特点。智能大数据分析广泛应用于各个领域,包括金融服务、医疗健康、零售、市场营销等,帮助企业做出更为精准的决策,提升竞争力。
【GitCode】专栏资源保存在我的GitCode仓库:https://gitcode.com/Morse_Chen/Intelligent_bigdata_analysis。

文章目录

    • 一、智能推荐简介
      • (一)什么是推荐系统
      • (二)智能推荐的应用
    • 二、智能推荐性能度量
      • (一)离线实验评价指标
      • (二)用户调查评价指标
      • (三)在线实验评价指标
    • 三、基于关联规则的智能推荐
      • (一)关联规则和频繁项集
      • (二)Apriori算法
      • (三)FP-Growth算法
    • 四、基于协同过滤的智能推荐
      • (一)基于用户的协同过滤
      • (二)基于物品的协同过滤
    • 小结


一、智能推荐简介

(一)什么是推荐系统

现代人在生活中经常遭遇选择困难的问题,在这个信息爆发的时代,飞速增长的信息让人眼花缭乱,不知道如何去选择适合自己的商品。人们往往会询问身边的朋友,请这些“专家”推荐几个他们认为较好的选择。虽然这些建议能帮助我们有效过滤掉一些多余的信息,但并没有考虑到个人个性化的需求,往往是基于更为偏向大众化的需求。

由于不能总是找到一位“专家”提建议,因此人们需要一个更为自动化的工具,这个工具能够分析用户以往的历史数据,以这些数据作为依托,为用户提供更为个性化定制的建议,这就是智能推荐系统。推荐系统作为连接消费者与生产者的桥梁起到了非常关键的作用,消费者通过推荐系统从大量同类商品中能更为轻松的找到自己感兴趣的商品,而生产者也能通过推荐系统从大量竞争者中脱颖而出,得到自己的目标用户的青睐。

(二)智能推荐的应用

一项技术能否广泛应用往往与需求有很强的联系。在互联网的早期,门户咨询网站已经能满足用户绝大部分的需求,这些门户网站的主要内容是编辑整理出的堆叠在网页之上的一堆链接,如国内的hao123网站。

之后的十几年,互联网进入了爆发期,面对日益增长的资讯量,门户网站已经无法满足用户的需求,此时出现了RSS订阅。RSS订阅为用户提供了个性化的体验,由于每个人订阅的信息源不一样,所以看到的咨询列表也不一样。

虽然RSS订阅让用户首次体验到了个性化的优异之处,但是由于订阅工具的使用存在难度且订阅源难以寻找,导致普及率一直不高。虽然RSS订阅已淡出了舞台,但是它为智能推荐起到了铺垫作用,用户已经尝到了个性化的甜头,由机器进行个性化推荐自然而然地得到了广泛应用。目前,智能推荐的应用领域已经涵盖电子商务、视频网站、音乐、社交网络、基于位置的服务和广告等多个领域。

1. 电子商务

电子商务已经成为智能推荐的最为普及的领域之一,在国内外的各大电子商务网站上智能推荐系统都得到了不错的成效。

国外著名的电子商务网站亚马逊被RWW(读写网)誉为“推荐系统之王”,该网站最主要的推荐系统应用为个性化商品推荐列表和相关商品推荐列表。亚马逊会根据用户的历史行为为用户做推荐,如果用户曾经对一本编程语言的书给出了高评价,那么亚马逊很可能会在推荐列表中推荐类似的学习书籍。推荐系统的应用成功地为亚马逊带来了额外的收入增长,据亚马逊的前首席科学家韦思岸(Andreas Weigend)在斯坦福的一次推荐系统的讲课过程中透露,亚马逊有20%~30%的销售业绩来自于推荐系统。

国内的电商平台也普遍运用了推荐系统,如淘宝网和京东的“猜你喜欢”和“您可能还需要”栏目也是为人们所熟知的推荐系统应用。

2. 视频网站

在视频网站中个性化推荐系统也得到了很好的应用。用户通过推荐系统的帮助在浩如星海的视频库成功找到自己感兴趣的视频作品,视频作者也成功收获了更多的关注。

国内的优酷、爱奇艺、BiliBili等视频网站都有应用推荐系统进行推荐引流,而在应用推荐系统最为出名的还有国外网站Netflix和YouTube。Netflix使用与亚马逊策略类似的基于物品的推荐系统,该系统会为用户推荐与他们喜欢过的电影类似的电影,帮助了Netflix中60%的用户成功找到自己感兴趣的电影和视频。而YouTube所做的一个个性化推荐和热门视频列表的点击率比较的实验的结果表明,个性化推荐的点击率为热门视频列表的点击率的两倍。

3. 音乐

个性化的网络电台也非常适合使用推荐系统,因为每年新的歌曲都在以极快的速度增长,用户想要寻找自己钟情的歌曲无异于大海捞针。其次,大部分用户在日常生活中都是将音乐作为背景音乐,只有很小一部分群体会听某首特定的歌,对于普通用户,只要推荐的歌曲符合用户当时的心境即可。

国内的网易云音乐的私人FM就是一个很典型的应用了推荐系统的个性化网络电台,私人FM不提供点歌功能,只提供了3个选项,即喜欢、垃圾桶和跳过,用户根据对系统推荐的歌曲的不同感受选择对应的选项,在经过一段时间后,推荐的歌曲列表将更为符合用户的口味。

4. 社交网络

近几年社交网络应用在互联网上迅速崛起,以Facebook和Twitter为代表的社交网络产品迅速风靡全球。推荐系统在社交网络上的应用主要分为两类,依据用户点赞和转发过的内容,根据标签推荐用户可能感兴趣的内容,或依据用户关注的人和内容推荐兴趣类似的好友。

5. 基于位置的服务

随着移动设备的发展,用户的位置信息已经能够很容易获取到,位置信息包含了很强的上下文关系信息。如用户在用餐时间打开服务,而用户正好身处商业区,那么此时已打开的服务很可能会为用户推荐附近的餐饮信息。

6. 广告

目前广告收入仍然是很多互联网公司收入的根基,随机投放的广告效率低很容易造成资源浪费,并且会有降低用户体验的负面影响。

而精准化的定向广告投放很适合使用个性化推荐系统实现,与一般的个性化推荐系统不同,个性化广告投放以广告为中心,寻找可能对广告感兴趣的用户。而一般的个性化推荐是以用户为中心,推荐用户感兴趣的内容。

二、智能推荐性能度量

评价一个推荐系统的方法有很多,总体可分为三个方面:

  • 离线实验
  • 用户调查
  • 在线实验

由于用户调查和在线实验的要求较高,目前大多数的推荐系统研究采用的是先通过离线实验验证当前的推荐算法在离线指标上是否优于现有算法,然后通过用户调查确定当前算法的满意度不低于现有算法。这两项都通过后,最后才进行在线测试查看测试者所关注的当前算法的指标是否优于现有算法。

(一)离线实验评价指标

离线实验主要基于数据集,不需要实际的系统做支撑,因此测试成本更为低廉,流程也更为简便。离线实验的评价步骤如下。

  1. 通过业务系统获取用户行为数据,生成标准数据集。
  2. 将数据集进行划分,划分为训练集和测试集。
  3. 在训练集上进行推荐模型的训练,在测试集上进行预测。
  4. 通过离线评价指标评价模型在测试集上的的预测结果。

离线实验的评价指标可分为准确性指标和非准确性指标两种。

1. 准确性指标

准确性指标是评价推荐系统预测的准确性的指标,是推荐系统中最重要的指标。推荐的结果类型不同,适用的准确性指标也不一样。

(1)推荐列表

通常网站给用户进行推荐时,针对每个用户提供的是一个个性化的推荐列表,也叫作TopN推荐。TopN推荐最常用的准确性指标是准确率、召回率和 F 1 F_1 F1值。准确率表示推荐列表中用户喜欢的物品所占的比例。单个用户 u u u的推荐准确率定义如下。 P ( L u ) = L u ∩ B u L u P(L_u)=\frac{L_u \cap B_u}{L_u} P(Lu)=LuLuBu 其中, L u L_u Lu表示用户 u u u的推荐列表, B u B_u Bu表示测试集中用户 u u u喜欢的物品。

整个推荐系统的准确率定义如下,其中 U U U表示用户集。 P L = 1 n ∑ u ∈ U P ( L u ) P_L=\frac{1}{n}\sum_{u\in U}P(L_u) PL=n1uUP(Lu)

召回率表示测试集中用户喜欢的物品出现在推荐列表中的比例。单个用户 u u u的推荐召回率定义如下。 R ( L u ) = L u ∩ B u B u R(L_u)=\frac{L_u\cap B_u}{B_u} R(Lu)=BuLuBu

整个推荐系统的召唤率定义如下。 R L = 1 n ∑ u ∈ U R ( L u ) R_L=\frac{1}{n}\sum_{u\in U}R(L_u) RL=n1uUR(Lu)

值(F1 score)是综合了准确率( P P P)和召回率( R R R)的评价方法, F 1 F_1 F1值取值越高表明推荐算法越有效, 值定义如下。 F 1 = 2 P R P + R F_1=\frac{2PR}{P+R} F1=P+R2PR

(2)评分预测

评分预测是指预测一个用户对推荐的物品的评分。评分预测的预测准确度通过均方根误差(RMSE)和平均绝对误差(MAE)进行评价。

对于测试集 T T T中的用户 u u u i i i物品 ,定义用户 u u u对物品 i i i的实际评分为 r u i r_{ui} rui,推荐算法的预测评分为 r ^ u i \hat r_{ui} r^ui,则RMSE的定义如下。 R M S E = ∑ u , i ∈ T ( r u i − r ^ u i ) 2 ∣ T ∣ \mathrm{RMSE}=\frac{\sqrt{\sum\limits_{u,i\in T}(r_{ui}-\hat r_{ui})^2}}{|T|} RMSE=Tu,iT(ruir^ui)2

MAE使用绝对值计算,定义如下。 M A E = ∑ u , i ∈ T ∣ r u i − r ^ u i ∣ ∣ T ∣ \mathrm{MAE}=\frac{\sum\limits_{u,i\in T}|r_{ui}-\hat r_{ui}|}{|T|} MAE=Tu,iTruir^ui

2. 非准确性指标

除了推荐准确性指标外,还有许多其他指标能评价一个推荐算法的性能,如多样性、新颖性、惊喜度和覆盖率等。

(1)多样性

用户的兴趣是广泛而多样的,所以推荐列表需要尽可能多的覆盖到用户的兴趣领域。多样性越高的推荐系统,用户访问时找到喜好的物品的概率就更高。推荐列表中物品两两之间的不相似性即为推荐列表的多样性,将物品 i i i j j j之间的相似度定义为 ( i , j ) ∈ [ 0 , 1 ] (i,j)\in [0,1] (i,j)[0,1],则用户 u u u的推荐列表 R ( u ) R(u) R(u)的多样性定义如下。 D i v e r s i t y = 1 − ∑ i , j ∈ R ( u ) , i ≠ j s ( i , j ) 1 2 ∣ R ( u ) ∣ [ ∣ R ( u ) ∣ − 1 ] \mathrm{Diversity}=1-\frac{\sum\limits_{i,j\in R(u),i\ne j}s(i,j)}{\frac{1}{2}\lvert R(u)\rvert[\lvert R(u)\rvert-1]} Diversity=121R(u)∣[∣R(u)∣1]i,jR(u),i=js(i,j)

推荐系统的整体多样性可以定义为全部用户的推荐列表多样性的平均值,如下。 D i v e r s i t y = 1 ∣ U ∣ ∑ u ∈ U D i v e r s i t y ( R ( u ) ) \mathrm{Diversity}=\frac{1}{|U|}\sum_{u\in U}\mathrm{Diversity}(R(u)) Diversity=U1uUDiversity(R(u))

(2)新颖性

当推荐系统推荐给用户的物品是用户未曾听说过的物品,那么这次推荐对于用户来说就是一次新颖的推荐。评价新颖度的一个简单的方法是利用推荐结果的平均流行度。推荐物品的流行度越低,物品越不热门,越可能让用户觉得新颖。若推荐结果中物品的平均流行度较低,那么推荐结果可能拥有较高的新颖性。

定义物品 i i i的流行度为 p ( i ) p(i) p(i),则用户 u u u的推荐列表 L u L_u Lu的新颖性定义如下。 N o v e l t y ( L u ) = ∑ i ∈ L u p ( i ) ∣ L u ∣ \mathrm{Novelty}(L_u)=\frac{\sum\limits_{i\in L_u}p(i)}{|L_u|} Novelty(Lu)=LuiLup(i)

推荐系统的整体新颖性可以定义为全部用户的推荐列表新颖性的平均值,计算公式如下。 N o v e l t y = 1 n ∑ n ∈ U N o v e l t y ( L u ) \mathrm{Novelty}=\frac{1}{n}\sum_{n\in U}\mathrm{Novelty}(L_u) Novelty=n1nUNovelty(Lu)

(3)惊喜度

惊喜度与新颖性的区别在于,新颖性指的是推荐给用户的物品是用户没有听说过的物品,而惊喜度是指推荐给用户的物品与用户历史记录中感兴趣的物品不相似,但是用户却又觉得满意。目前尚未有一个公认的惊喜度指标的定义方式,此处只给出一种定性的度量方式。

(4)覆盖率

覆盖率用于描述一个推荐系统对于物品长尾的挖掘能力。覆盖率最常见的定义是推荐系统推荐出的物品列表中的物品数目占总物品集合中物品数目的比例。定义用户 u u u的推荐列表为 L u L_u Lu,则推荐系统的覆盖率定义如下。 C o v e r a g e = ∣ ⋃ u ∈ U L u ∣ n \mathrm{Coverage}=\frac{\left|\bigcup\limits_{u\in U}L_u\right|}{n} Coverage=n uULu

(二)用户调查评价指标

离线实验的指标和实际的商业指标存在一定的差距,一个拥有较高预测准确率的推荐系统并不一定拥有更高的用户满意度。要准确评价一个推荐算法,除了离线实验,还需要一个相对真实的环境进行测试。在无法确定算法是否会降低用户满意度的情况下,直接进行上线测试会有较高的风险,因此通常会在上线测试前进行一次用户调查测试。

用户作为推荐系统的重要参与者,用户满意度是评测推荐系统的最重要指标。用户调查获得用户满意度主要是通过调查问卷的形式。设计问卷时需要从不同的侧面询问用户对推荐结果的不同感受,而不是简单直接地询问用户对推荐结果是否满意,这样用户才能针对问题给出自己准确的回答。

(三)在线实验评价指标

在推荐系统完成离线实验和用户调查后,就可以进行上线做AB测试,与旧的现有算法进行比较。AB测试是一种常见的在线评测算法的实验方法,通过将用户随机分成几组,对不同组用户采用不同算法进行对照,通过统计不同组用户的各种评测指标比较不同算法的效果。在线实验时的评价指标常见的有实时性、健壮性、用户满意度和商业指标等。

1. 实时性

由网站中的物品往往具有很强的时效性,如新闻、微博等,因此需要在时效性尚未消失的时候就推荐给用户。

推荐系统的实时性体现为两个方面,一个为能够实时地更新推荐列表满足用户的新的行为变化,通过推荐列表的变化速率进行评测;另一个为推荐系统能够将新加入系统的物品推荐给用户,通过用户推荐列表中新加物品所占比例进行评测。

2. 健壮性

在线上运行的算法系统不可避免地会遭受被人攻击的问题,对于推荐系统而言,最常见的攻击就是作弊问题,健壮性指标衡量了一个推荐系统抗作弊的能力。

推荐系统的健壮性的评测主要通过模拟攻击进行,在给定一个数据集和算法的情况下,利用算法给数据集中的用户生成推荐列表。之后用常用攻击方法向数据集中注入噪声后再使用相同的算法生成推荐列表。对攻击前后的推荐列表的相似度进行评价,从而评价推荐算法的健壮性。

3. 用户满意度

在线系统中的用户满意度主要通过一些用户行为的统计结果得到,最常见的情况是通过点击率、用户停留时间和转化率等指标度量用户的满意度。

4. 商业指标

在实际应用中,对于线上运行的推荐系统能否帮助达成商业目标是十分重要的。最简单直接的表现是上线推荐系统后能否加速完成商业指标,如销售额、广告点击数等,不同的网站拥有不同的指标,这些指标本质为上线系统能否为网站带来更多的盈利。

三、基于关联规则的智能推荐

关联规则可以挖掘出物品间的关联关系,物品间的关联性越强推荐给用户时越可能受用户喜欢,提取关联规则的最大困难在于当存在很多商品时,可能的商品的组合(规则的前项与后项)的数目会达到一种令人望而却步的程度。

因此,各种关联规则分析的算法会从不同方面入手减小可能的搜索空间的大小以及减小扫描数据的次数。目前常见的关联规则算法有:

  • Apriori
  • FP-Growth

(一)关联规则和频繁项集

1. 关联规则的一般形式

项集 A A A B B B同时发生的概率称为关联规则的支持度(也称相对支持度) S u p p o r t ( A ⇒ B ) = P ( A ∩ B ) \mathrm{Support}(A\Rightarrow B)=P(A\cap B) Support(AB)=P(AB)

项集 A A A发生,则项集 B B B发生的概率为关联规则的置信度 C o n f i d e n c e ( A ⇒ B ) = P ( B ∣ A ) \mathrm{Confidence}(A\Rightarrow B)=P(B|A) Confidence(AB)=P(BA)

2. 最小支持度和最小置信度

最小支持度是用户或专家定义的衡量支持度的一个阈值,表示项目集在统计意义上的最低重要性。
最小置信度是用户或专家定义的衡量置信度的一个阈值,表示关联规则的最低可靠性。
同时满足最小支持度阈值和最小置信度阈值的规则被称作强规则。

3. 项集

项集是项的集合。包含 k k k个项的项集称为 k k k项集,如集合 { 牛奶 , 麦片 , 糖 } \{牛奶,麦片,糖\} {牛奶,麦片,}是一个3项集。
项集的出现频数是所有包含项集的事务计数,又被称作绝对支持度或支持度计数。
如果项集 I I I的相对支持度满足预定义的最小支持度阈值,则 I I I是频繁项集。频繁 k k k项集通常记作 L k L_k Lk

4. 支持度计数

项集 A A A的支持度计数是事务数据集中包含项集 A A A的事务个数,简称为项集的频率或计数。已知项集的支持度计数,则规则 A ⇒ B A\Rightarrow B AB的支持度和置信度很容易从所有事务计数、项集 A A A和项集 A ∪ B A\cup B AB的支持度计数推出式如下,其中 N N N表示总事务个数, σ \sigma σ表示计数。 S u p p o r t ( A ⇒ B ) = A , B 同时发生的事务个数 所有事务个数 = σ ( A ∪ B ) N \mathrm{Support}(A\Rightarrow B)=\frac{A,B同时发生的事务个数}{所有事务个数}=\frac{\sigma(A\cup B)}{N} Support(AB)=所有事务个数A,B同时发生的事务个数=Nσ(AB) C o n f i d e n c e ( A ⇒ B ) = P ( B ∣ A ) = σ ( A ∪ B ) σ ( A ) \mathrm{Confidence}(A\Rightarrow B)=P(B|A)=\frac{\sigma(A\cup B)}{\sigma(A)} Confidence(AB)=P(BA)=σ(A)σ(AB)

也就是说,一旦得到所有事务个数, A A A B B B A ∪ B A\cup B AB的支持度计数,即可导出对应的关联规则 A ⇒ B A\Rightarrow B AB B ⇒ A B\Rightarrow A BA,并可以检查该规则是否是强规则。

(二)Apriori算法

Apriori算法的主要思想是找出存在于事务数据集中的最大的频繁项集,再利用得到的最大频繁项集与预先设定的最小置信度阈值生成强关联规则。

1. Apriori算法实现的两个过程

频繁项集的所有非空子集也必须是频繁项集。根据该性质可以得出:向不是频繁项集 I I I的项集 I I I中添加事务 A A A,新的项集 I ∪ A I\cup A IA一定也不是频繁项集。

找出所有的频繁项集(支持度必须大于等于给定的最小支持度阈值),在这个过程中连接步和剪枝步互相融合,最终得到最大频繁项集 L k L_k Lk

(1)连接步

连接步的目的是找到 K K K项集。对给定的最小支持度阈值,对1项候选集 C 1 C_1 C1,剔除小于该阈值的项集得到1项频繁项集 L 1 L_1 L1;下一步由 L 1 L_1 L1自身连接产生2项候选集 C 2 C_2 C2,保留 C 2 C_2 C2中满足约束条件的项集得到2项频繁项集,记为 L 2 L_2 L2;再下一步由 L 2 L_2 L2 L 1 L_1 L1连接产生3项候选集 C 3 C_3 C3,保留 C 2 C_2 C2中满足约束条件的项集得到3项频繁项集,记为 L 3 L_3 L3。这样循环下去,得到最大频繁项集 L k L_k Lk

(2)剪枝步

剪枝步紧接着连接步,在产生候选项 L k L_k Lk的过程中起到减小搜索空间的目的。由于 C k C_k Ck L k − 1 L_{k-1} Lk1 L 1 L_1 L1连接产生的,根据Apriori的性质频繁项集的所有非空子集也必须是频繁项集,所以不满足该性质的项集将不会存在于 C k C_k Ck中,该过程就是剪枝。

由频繁项集产生强关联规则:由过程(1)可知,未超过预定的最小支持度阈值的项集已被剔除,如果剩下这些项集又满足了预定的最小置信度阈值,那么就挖掘出了强关联规则。

2. 使用Apriori算法实现餐饮菜品关联分析

结合餐饮行业的实例讲解Apriori关联规则算法挖掘的实现过程。数据库中部分点餐数据如表所示。

序列时间订单号菜品id菜品名称
12014/8/2110118491健康麦香包
22014/8/211018693香煎葱油饼
32014/8/211018705翡翠蒸香茜饺
42014/8/211028842菜心粒咸骨粥
52014/8/211027794养颜红枣糕
62014/8/211038842金丝燕麦包
72014/8/211038693三丝炒河粉
…………………………

首先将上表中的事务数据(一种特殊类型的记录数据)整理成关联规则模型所需的数据结构,从中抽取10个点餐订单作为事务数据集,设支持度为0.2(支持度计数为2),为方便起见将菜品 { 18491 , 8842 , 8693 , 7794 , 8705 } \{18491,8842,8693,7794,8705\} {18491,8842,8693,7794,8705}分别简记为 { a , b , c , d , e } \{a,b,c,d,e\} {a,b,c,d,e},如表所示。

订单号原菜品id转换后菜品id
118491, 8693, 8705 a , c , e a, c, e a,c,e
28842, 7794 b , d b, d b,d
38842, 8693 b , c b, c b,c
418491, 8842, 8693, 7794 a , b , c , d a, b, c, d a,b,c,d
518491, 8842 a , b a, b a,b
68842, 8693 b , c b, c b,c
718491, 8842 a , b a, b a,b
818491, 8842, 8693, 8705 a , b , c , e a, b, c, e a,b,c,e
918491, 8842, 8693 a , b , c a, b, c a,b,c
1018491, 8693 a , c , e a, c, e a,c,e

算法过程如图所示。

在这里插入图片描述

(1)过程一:找最大k项频繁项集

算法简单扫描所有的事务,事务中的每一项都是候选1项集的集 C 1 C_1 C1的成员,计算每一项的支持度。如 P ( { a } ) = 项集 { a } 的支持度计数 所有事务个数 = 7 10 = 0.7 P(\{a\})=\frac{项集\{a\}的支持度计数}{所有事务个数}=\frac{7}{10}=0.7 P({a})=所有事务个数项集{a}的支持度计数=107=0.7

C 1 C_1 C1中各项集的支持度与预先设定的最小支持度阈值作比较,保留大于或等于该阈值的项,得1项频繁项集 L 1 L_1 L1

扫描所有事务, L 1 L_1 L1 L 1 L_1 L1连接得候选2项集 C 2 C_2 C2,并计算每一项的支持度。如 P ( { a , b } ) = 项集 { a , b } 的支持度计数 所有事务个数 = 5 10 = 0.5 P(\{a,b\})=\frac{项集\{a,b\}的支持度计数}{所有事务个数}=\frac{5}{10}=0.5 P({a,b})=所有事务个数项集{a,b}的支持度计数=105=0.5

接着是剪枝步,由于 C 2 C_2 C2的每个子集(即 L 1 L_1 L1)都是频繁项集,所以没有项集从 C 2 C_2 C2中剔除。对 C 2 C_2 C2中各项集的支持度与预先设定的最小支持度阈值作比较,保留大于或等于该阈值的项,得2项频繁项集 L 2 L_2 L2

扫描所有事务, L 2 L_2 L2 L 1 L_1 L1连接得候选3项集 C 3 C_3 C3,并计算每一项的支持度,如 P { a , b , c } = 项集 { a , b , c } 的支持度计数 所有事务个数 = 3 10 = 0.3 P\{a,b,c\}=\frac{项集\{a,b,c\}的支持度计数}{所有事务个数}=\frac{3}{10}=0.3 P{a,b,c}=所有事务个数项集{a,b,c}的支持度计数=103=0.3

接着是剪枝步, L 2 L_2 L2 L 1 L_1 L1连接的所有项集为 { a , b , c } , { a , b , d } , { a , c , d } , { a , c , e } , { b , c , d } , { b , c , e } \{a,b,c\},\{a,b,d\},\{a,c,d\},\{a,c,e\},\{b,c,d\},\{b,c,e\} {a,b,c},{a,b,d},{a,c,d},{a,c,e},{b,c,d},{b,c,e}

根据Apriori算法,频繁项集的所有非空子集也必须是频繁项集,因为 { b , c } , { b , e } , { c , d } \{b,c\},\{b,e\},\{c,d\} {b,c},{b,e},{c,d}不包含在 b b b项频繁项集 L 2 L_2 L2中,即不是频繁项集,应剔除,最后的 C 3 C_3 C3中的项集只有 { a , b , c } \{a,b,c\} {a,b,c} { a , c , e } \{a,c,e\} {a,c,e}

C 3 C_3 C3中各项集的支持度与预先设定的最小支持度阈值作比较,保留大于或等于该阈值的项,得3项频繁项集 L 3 L_3 L3 L 3 L_3 L3 L 1 L_1 L1连接得候选4项集 C 4 C_4 C4,剪枝后得到的项集为空集。因此最后得到最大3项频繁项集 { a , b , c } \{a,b,c\} {a,b,c} { a , c , e } \{a,c,e\} {a,c,e}

由以上过程可知 L 1 , L 2 , L 3 L_1,L_2,L_3 L1,L2,L3都是频繁项集, L 3 L_3 L3是最大频繁项集。

(2)过程二:由频繁集产生关联规则

自定义包apriori.py代码如下:

# -*- coding: utf-8 -*-
import numpy as np
import pandas as pd
def connect_string(x, ms):x = list(map(lambda i:sorted(i.split(ms)), x))l = len(x[0])r = []for i in range(len(x)):for j in range(i,len(x)):if x[i][:l-1] == x[j][:l-1] and x[i][l-1] != x[j][l-1]:r.append(x[i][:l-1]+sorted([x[j][l-1],x[i][l-1]]))return r#寻找关联规则的函数
def find_rule(d, support, confidence, ms = u'--'):result = pd.DataFrame(index=['support', 'confidence']) #定义输出结果support_series = 1.0*d.sum()/len(d) #支持度序列column = list(support_series[support_series > support].index) #初步根据支持度筛选k = 0while len(column) > 1:k = k+1print(u'\n正在进行第%s次搜索...' %k)column = connect_string(column, ms)print(u'数目:%s...' %len(column))sf = lambda i: d[i].prod(axis=1, numeric_only = True) #新一批支持度的计算函数#创建连接数据,这一步耗时、耗内存最严重。当数据集较大时,可以考虑并行运算优化。d_2 = pd.DataFrame(list(map(sf,column)), index = [ms.join(i) for i in column]).Tsupport_series_2 = 1.0*d_2[[ms.join(i) for i in column]].sum()/len(d) #计算连接后的支持度column = list(support_series_2[support_series_2 > support].index) #新一轮支持度筛选support_series = support_series.append(support_series_2)column2 = []for i in column: #遍历可能的推理,如{A,B,C}究竟是A+B-->C还是B+C-->A还是C+A-->B?i = i.split(ms)for j in range(len(i)):column2.append(i[:j]+i[j+1:]+i[j:j+1])cofidence_series = pd.Series(index=[ms.join(i) for i in column2]) #定义置信度序列for i in column2: #计算置信度序列cofidence_series[ms.join(i)] = support_series[ms.join(sorted(i))]/support_series[ms.join(i[:len(i)-1])]for i in cofidence_series[cofidence_series > confidence].index: #置信度筛选result[i] = 0.0result[i]['confidence'] = cofidence_series[i]result[i]['support'] = support_series[ms.join(sorted(i.split(ms)))]result = result.T.sort_values(['confidence','support'], ascending = False) #结果整理,输出print(u'\n结果为:')print(result)return result

尝试基于该例产生关联规则,在Python中实现上述Apriori算法的代码, 结果如图所示。

import pandas as pd
from apriori import *  # 导入自行编写的apriori函数
# 读入数据
data = pd.read_excel('../data/menu_orders.xls', header=None)
data.head()

在这里插入图片描述

print('\n转换原始数据至0-1矩阵...')
ct = lambda x: pd.Series(1, index=x[pd.notnull(x)])  # 转换0-1矩阵的过渡函数
b = map(ct, data.to_numpy())  # 用map方式执行
data = pd.DataFrame(list(b)).fillna(0)  # 实现矩阵转换,空值用0填充
print('\n转换完毕')

在这里插入图片描述

data.head()

在这里插入图片描述

support = 0.2  # 最小支持度
confidence = 0.5  # 最小置信度
# 连接符,默认'--',用来区分不同元素,如A--B。需要保证原始表格中不含有该字符
ms = '---'
find_rule(data, support, confidence, ms).to_excel('../tmp/outputfile.xls', encoding='utf-8')  # 保存结果

在这里插入图片描述

针对图中第一条输出结果进行解释:客户同时点菜品e和a的概率是30%,点了菜品e,再点菜品a的概率是100%。知道了这些,就可以对顾客进行智能推荐,增加销量的同时满足客户需求。

(三)FP-Growth算法

FP-Growth算法不同于Apriori算法生成候选项集再检查是否频繁的“产生-测试”方法,而是使用一种称为频繁模式树(FP-Tree,FP代表频繁模式,Frequent Pattern)的菜单紧凑数据结构组织数据,并直接从该结构中提取频繁项集。

1. FP-Growth算法的基本过程

相比于Apriori对每个潜在的频繁项集都需要扫描数据集判定是否满足支持度,FP-Growth算法只需要遍历两次数据集,因此它在大数据集上的速度显著优于Apriori。FP-Growth算法的基本运作步骤如下。
① 扫描数据,得到所有1项频繁一项集的计数。然后删除支持度低于阈值的项,将1项频繁项集放入项头表,并按照支持度降序排列。
② 读入排序后的数据集,插入FP树,插入时将项集按照排序后的顺序,插入FP树中,排序靠前的节点是祖先节点,而靠后的是子孙节点。如果有共用的祖先,则对应的公用祖先节点计数加1。插入后,如果有新节点出现,则项头表对应的节点会通过节点链表连接新节点。直到所有的数据都插入到FP树后,FP树的建立完成。
③ 从项头表的底部项依次向上找到项头表项对应的条件模式基。从条件模式基递归挖掘得到项头表项的频繁项集。
④ 如果不限制频繁项集的项数,则返回步骤3所有的频繁项集,否则只返回满足项数要求的频繁项集。

2. FP-Growth算法原理

FP-Growth算法主要包含3个部分:扫描数据集建立项头表、基于项头表建立FP-tree和基于FP-tree挖掘频繁项集。

(1)建立项头表

要建立FP-tree首先需要建立项头表,建立项头表需要先对数据集进行一次扫描,得到所有1项频繁一项集的计数,将低于设定的支持度阈值的项过滤掉后,将1项频繁集放入项头表并按照项集的支持度进行降序排序。之后对数据集进行第二次扫描,从原始数据中剔除1项非频繁项集,并按照项集的支持度降序排序。

以一个含有10条数据的数据集为例,数据集中的数据如表所示。

序号数据
1A,B,C,E,F,H
2A,C,G
3E,I
4A,C,D,E,G
5A,D,E,L
6E,J
7A,B,C,E,F,P
8A,C,D
9A,C,E,G,M
10A,C,E,G,K

对数据集进行扫描,支持度阈值设为20%,由于H,I,L,J,K,P,M都仅出现一次,小于设定的20%的支持度阈值,因此将不进入项头表。将1项频繁项集按降序排序后构建的项头表如表所示。

频繁项计数
A8
E8
C7
G4
D3
B2
F2

第二次扫描数据,将每条数据中的1项非频繁项集删去,并按照项集的支持度降序排列。如数据项“A,B,C,E,F,H”,其中“H“为1项非频繁项集,剔除后按项集的支持度降序排列后的数据项为“A,E,C,B,F”,得到排序后的数据集如表所示。

序号数据
1A,E,C,B,F
2A,C,G
3E
4A,E,C,G,D
5A,E,D
6E
7A,E,C,B,F
8A,C,D
9A,E,C,G
10A,E,C,G

(2)建立FP-tree

构建项头表并对数据集排序后,就可以开始建立FP-tree。建立FP-tree时按顺序读入排序后的数据集,插入FP-tree中时按照排序的顺序插入,排序最为靠前的是父节点,之后的是子孙节点。如果出现共同的父节点,则对应父节点的计数增加1次。插入时如果有新节点加入树中,则将项头表中对应的节点通过节点链表链接接上新节点。直至所有的数据项都插入FP-tree后,FP-tree完成建立过程。

以建立项头表的数据集为例,构建FP-tree的过程如图所示。

在这里插入图片描述

构建FP-tree具体步骤如下。

  • 读入第一条数据“A,E,C,B,F”,此时FP-tree中没有节点,按顺序构成一条完整路径,每个节点的计数为1。
  • 读入第二条数据“A,C,G”,在A节点处延伸一条新路径,并且A节点计数加1,其余节点计数为1。
  • 读入第三条数据“E”,从根节点位置延伸一条新路径,计数为1。
  • 读入第四条数据“A,E,C,G,D”,在C节点处延伸一条新路径,共用的“A,E,C”计数加1,新建的节点计数为1。
  • 重复以上步骤直至整个FP-tree构建完成。

最终得到的FP-tree如图所示。

在这里插入图片描述

(3)挖掘频繁项集

在构建FP-tree、项头表和节点链表后,需要从项头表的底部项依次向上挖掘频繁项集。这需要找到项头表中对应于FP-tree的每一项的条件模式基。条件模式基是以要挖掘的节点作为叶子节点所对应的FP子树。

得到该FP子树后,将子树中每个节点的计数设置为叶子节点的计数,并删除计数低于最小支持度的节点。基于这个条件模式基就可以递归挖掘得到频繁项集了。

以构建F节点的条件模式基为例,F节点在FP-tree中只有一个子节点,因此只有一条路径{A:8,E:6,C:5,B:2,F:2},得到F节点的FP子树如图所示。

在这里插入图片描述

接着将所有的父节点的计数设置为子节点的计数,即FP子树变成{A:2,E:2,C:2,B:2,F:2}。通常条件模式基可以不写子节点,如图所示。

在这里插入图片描述

通过F节点的条件模式基可以得到F的频繁2项集为{A:2,F:2}、{E:2,F:2}、{C:2,F:2}、{B:2,F:2}。将2项集递归合并得到频繁3项集为{A:2,C:2,F:2}、{A:2,E:2,F:2}等等。最终递归得到最大的频繁项集为频繁5项集{A:2, E:2,C:2, B:2,F:2}。

获取B节点的频繁项集的过程与F节点类似,此处不再列出,需要特别提一下D节点。D节点在树中有三个子节点,得到D节点的FP子树如图所示。

在这里插入图片描述

接着将所有的父节点计数设置为子节点的计数,即变成{A:3,E:2,C:2,G:1,D:1,D:1,D:1},由于G节点在子树中的支持度低于阈值,在去除低支持度节点并不包括子节点后D的条件模式基为{A:3,E:2,C:2}。通过D的条件模式基得到D的频繁2项集为{A:3,D:3},{E:2,D:2},{C:2,D:2}。

递归合并2项集,得到频繁3项集为{A:2,E:2,D:2},{A:2,C:2,D:2}。D节点对应的最大的频繁项集为频繁3项集。其余节点可以用类似的方法得出对应的频繁项集。

3. 使用FP-Growth算法实现新闻站点点击流频繁项集挖掘

使用FP-Growth算法挖掘匈牙利在线新闻门户的点击流数据kosarak.dat中的频繁项集,该数据有将近100万条记录,每一行包含某个用户浏览过的新闻报道。新闻报道被编码成整数,使用FP-growth算法挖掘其中的频繁项集,查看哪些新闻ID被用户大量浏览。

自定义包fpGrowth.py代码如下:

# -*- coding: utf-8 -*-
class treeNode:"""FP-tree树类"""def __init__(self, nameValue, numOccur, parentNode):self.name = nameValueself.count = numOccurself.nodeLink = None  # 用于链接相似的元素项self.parent = parentNode  # 指向当前节点的父节点self.children = {}  # 用于存放节点的子节点def inc(self,numOccur):self.count += numOccur# 以文本的形式显示树def disp(self, ind=1):print(' ' * ind, self.name, ' ', self.count)for child in self.children.values():child.disp(ind + 1)# 构建FP-tree
def createTree(dataSet, minSup=1):headerTable = {}for trans in dataSet:  # 第一次遍历:统计各个数据的频繁度for item in trans:headerTable[item] = headerTable.get(item, 0) + dataSet[trans]# 用项头表统计各个类别的出现的次数,计算频繁量:项头表[类别]=出现次数for  k in list(headerTable):  # 删除未达到最小频繁度的数据if headerTable[k] < minSup:del (headerTable[k])freqItemSet = set(headerTable.keys())  # 保存达到要求的数据if len(freqItemSet) == 0:return None, None  # 若达到要求的数目为0for k in headerTable:  # 遍历项头表headerTable[k] = [headerTable[k], None]  # 保存计数值及指向每种类型第一个元素项的指针retTree = treeNode('Null Set', 1, None)  # 初始化treefor tranSet, count in dataSet.items():  # 第二次遍历localD = {}for item in tranSet:if item in freqItemSet:  # 只对频繁项集进行排序localD[item] = headerTable[item][0]# 使用排序后的频率项集对树进行填充if len(localD) > 0:orderedItems = [v[0] for v in sorted(localD.items(), key=lambda p: p[1], reverse=True)]updateTree(orderedItems, retTree, headerTable, count)return retTree, headerTable  # 返回构建的树和项头表def updateTree(items, inTree, headerTable, count):if items[0] in inTree.children:  # 检查是否存在该节点inTree.children[items[0]].inc(count)  # 存在则计数增加else:  # 不存在则将新建该节点inTree.children[items[0]] = treeNode(items[0], count, inTree)  # 创建一个新节点if headerTable[items[0]][1] == None:  # 若原来不存在该类别,更新项头表headerTable[items[0]][1] = inTree.children[items[0]]  # 更新指向else:  # 更新指向updateHeader(headerTable[items[0]][1], inTree.children[items[0]])if len(items) > 1:  # 如果仍有未分配完的树,继续迭代updateTree(items[1::], inTree.children[items[0]], headerTable, count)# 节点链接指向树中该元素项的每一个实例。
# 从项头表的 nodeLink 开始,一直沿着nodeLink直到到达链表末尾
def updateHeader(nodeToTest, targetNode):while (nodeToTest.nodeLink != None):nodeToTest = nodeToTest.nodeLinknodeToTest.nodeLink = targetNodedef loadSimpDat():simpDat = [['r', 'z', 'h', 'j', 'p'],['z', 'y', 'x', 'w', 'v', 'u', 't', 's'],['z'],['r', 'x', 'n', 'o', 's'],['y', 'r', 'x', 'z', 'q', 't', 'p'],['y', 'z', 'x', 'e', 'q', 's', 't', 'm']]return simpDat# 将数据从列表转换到字典的类型
def createInitSet(dataSet):retDict = {}for trans in dataSet:retDict[frozenset(trans)] = 1return retDict# 从FP树中发现频繁项集
def ascendTree(leafNode, prefixPath):  # 递归上溯整棵树if leafNode.parent != None:prefixPath.append(leafNode.name)ascendTree(leafNode.parent, prefixPath)def findPrefixPath(basePat, treeNode):  # 参数:指针,节点condPats = {}while treeNode != None:prefixPath = []ascendTree(treeNode, prefixPath)  # 寻找当前非空节点的前缀if len(prefixPath) > 1:condPats[frozenset(prefixPath[1:])] = treeNode.count  # 将条件模式基添加到字典中treeNode = treeNode.nodeLinkreturn condPats# 递归查找频繁项集
def mineTree(inTree, headerTable, minSup, preFix, freqItemList):# 头指针表中的元素项按照频繁度排序,从小到大bigL = [v[0] for v in sorted(headerTable.items(), key=lambda p: str(p[1]))]for basePat in bigL:  # 从底层开始# 加入频繁项列表newFreqSet = preFix.copy()newFreqSet.add(basePat)print ('finalFrequent Item: ',newFreqSet)freqItemList.append(newFreqSet)# 递归调用函数来创建基condPattBases = findPrefixPath(basePat, headerTable[basePat][1])print ('condPattBases :',basePat, condPattBases)# 构建条件模式TreemyCondTree, myHead = createTree(condPattBases, minSup)# 将创建的条件基作为新的数据集添加到fp-treeprint ('head from conditional tree: ', myHead)if myHead != None:  # 递归print ('conditional tree for: ',newFreqSet)myCondTree.disp(1)mineTree(myCondTree, myHead, minSup, newFreqSet, freqItemList)# 主函数封装
def fpGrowth(dataSet, minSup=3):initSet = createInitSet(dataSet)myFPtree, myHeaderTab = createTree(initSet, minSup)freqItems = []mineTree(myFPtree, myHeaderTab, minSup, set([]), freqItems)return freqItems

在Python中实现FP-Growth算法挖掘新闻门户的点击流数据的代码,结果如下。

import fpGrowth# 读取数据并转换格式
newsdata = [line.split() for line in open('../data/kosarak.dat').readlines()]
indataset = fpGrowth.createInitSet(newsdata)
# 构建树寻找其中浏览次数在5万次以上的新闻
news_fptree, news_headertab = fpGrowth.createTree(indataset, 50000)
# 创建空列表用于保持频繁项集
newslist = []
fpGrowth.mineTree(news_fptree, news_headertab, 50000, set([]), newslist)
# 查看结果
print('浏览次数在5万次以上的新闻报导集合个数:', len(newslist))
print('浏览次数在5万次以上的新闻:\n', newslist)

在这里插入图片描述

可以得到该网站上浏览次数超过5万次的新闻集合的个数为29个。

四、基于协同过滤的智能推荐

常见的协同过滤推荐技术主要分为两大类。

  • 基于用户的协同过滤推荐。
  • 基于物品的协同过滤推荐。

(一)基于用户的协同过滤

基于用户的协同过滤的基本思想相当简单,基于用户对物品的偏好找到相邻用户,然后将邻居用户喜欢的推荐给当前用户。在计算上,就是将一个用户对所有物品的偏好作为一个向量来计算用户之间的相似度,找到K邻居后,根据邻居的相似度权重以及他们对物品的偏好,预测当前用户没有偏好的未涉及物品,计算得到一个排序的物品列表作为推荐。如右图给出了一个例子,对于用户A,根据用户的历史偏好,这里只计算得到一个邻居-用户C,可以将用户C喜欢的物品D推荐给用户A。

在这里插入图片描述

1. 算法原理

实现基于用户的协同过滤算法第一个重要的步骤就是计算用户之间的相似度。而计算相似度,建立相关系数矩阵目前主要分为以下几种方法。

(1)皮尔逊相关系数

皮尔逊相关系数一般用于计算两个定距变量间联系的紧密程度,它的取值在[-1,+1]区间内。皮尔森相关系数等于两个变量的协方差除于两个变量的标准差,计算公式如下。 s ( X , Y ) = C o v ( X , Y ) σ X σ Y s(X,Y)=\frac{\mathrm{Cov(X,Y)}}{\sigma_X\sigma_Y} s(X,Y)=σXσYCov(X,Y)

由于皮尔逊相关系数描述的是两组数据变化移动的趋势,所以在基于用户的协同过滤系统中经常被使用。描述用户购买或评分变化的趋势,若趋势相近,则皮尔逊系数趋近于1,即认为用户相似。

(2)基于欧几里得距离的相似度

欧几里得距离计算相似度是所有相似度计算里面最简单、最易理解的方法,它以经过人们一致评价的物品为坐标轴,然后将参与评价的人绘制到坐标系上,并计算这些人彼此之间的直线距离 ∑ ( X i − Y i ) 2 \sqrt{\sum{(X_i-Y_i)^2}} (XiYi)2 。计算得到的欧几里得距离是一个大于0的数,为了使其更能体现用户之间的相似度,可以把它规约到(0,1]区间内,最终得到的计算公式如下。 s ( X , Y ) = 1 1 + ∑ ( X i − Y i ) 2 s(X,Y)=\frac{1}{1+\sqrt{\sum{(X_i-Y_i)^2}}} s(X,Y)=1+(XiYi)2 1

只要至少有一个共同评分项,即可用欧几里得距离计算相似度;如果没有共同评分项,那么欧几里得距离也就失去了作用,这也意味着这两个用户根本不相似。

(3)余弦相似度

余弦相似度用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小,如右图所示。余弦相似度更加注重两个向量在方向上的差异,而非距离或长度上,计算公式如下。 s ( X , Y ) = cos ⁡ θ = x ⃗ ⋅ y ⃗ ∥ x ∥ ⋅ ∥ y ∥ s(X,Y)=\cos\theta=\frac{\vec{x}\cdot\vec{y}}{\Vert x\Vert\cdot\Vert y\Vert} s(X,Y)=cosθ=xyx y

在这里插入图片描述

从上图可以看出,距离度量衡量的是空间各点间的绝对距离,跟各个点所在的位置坐标(即个体特征维度的数值)直接相关;而余弦相似度衡量的是空间向量的夹角,更多的是体现在方向上的差异,而不是位置。如果保持 X X X点的位置不变, Y Y Y点朝原方向远离坐标轴原点,那么这个时候余弦相似度 cos ⁡ θ \cos\theta cosθ是保持不变的,因为夹角不变,而 X X X Y Y Y两点的距离显然在发生改变,这就是欧氏距离和余弦相似度的不同之处。

(4)预测评分

基于用户的协同过滤算法,另一个重要的步骤是计算用户 u u u对未评分商品的预测分值。首先根据上一步中的相似度计算,寻找用户 u u u的邻居集 N ∈ U N\in U NU,其中 N N N表示邻居集, U U U表示用户集。然后结合用户评分数据集,预测用户 u u u对项 i i i的评分,计算公式如下: p u , i = r ˉ + ∑ u ′ ⊂ N s ( u − u ′ ) ( r u ′ , i − r ˉ u ′ ) ∑ u ′ ⊂ N ∣ s ( u − u ′ ) ∣ p_{u,i}=\bar{r}+\frac{\sum\limits_{u'\subset N}s(u-u')(r_{u',i}-\bar{r}_{u'})}{\sqrt{\sum\limits_{u'\subset N}|s(u-u')|}} pu,i=rˉ+uNs(uu) uNs(uu)(ru,irˉu)

其中, s ( u − u ′ ) s(u-u') s(uu)表示用户 u u u和用户 u ′ u' u的相似度。最后,基于对未评分商品的预测分值排序,得到推荐商品列表。

2. 基于用户的个性化的电影推荐

通过个性化的电影推荐的实例演示基于用户的协同过滤算法在Python中的实现。现今影视已经成为大众喜爱的休闲娱乐的方式之一,合理的个性化电影推荐一方面能够促进电影行业的发展,另一方面也可以让用户在数量众多的电影中迅速得到自己想要的电影,做到两全齐美。甚至更近一步,可以明确市场走向,对后续电影的类型导向等起到重要作用。

MovieLens数据集记录了943个用户对1682部电影的共100000个评分,每个用户至少对20部电影进行了评分。脱敏后的部分电影评分数据如下表所示。

用户ID电影ID电影评分时间标签
115874965758
123876893171
134878542960
143876893119
153889751712
164875071561
171875072484

自定义包recommender.py代码如下:

import numpy as np
import pandas as pd
import math
def prediction(df,userdf,Nn=15):#Nn邻居个数corr=df.corr();rats=userdf.copy()for itemid in userdf.columns:dfnull=userdf.loc[:,itemid][userdf.loc[:,itemid].isnull()]itemv=df.loc[:,itemid].mean()#评价平均值for usrid in dfnull.index:nft=(df.loc[usrid]).notnull()#获取该用户评过分的物品中最相似邻居物品列表,且取其中Nn个,如果不够Nn个则全取nlist=df.loc[usrid][nft]nlist=nlist[(corr.loc[itemid,nlist.index][corr.loc[itemid,nlist.index].notnull()].sort_values(ascending=False)).index]if(Nn<=len(nlist)):nlist=(df.loc[usrid][nft])[:Nn]else:nlist=df.loc[usrid][nft][:len(nlist)]nratsum=0corsum=0if(0!=nlist.size):nv=df.loc[:,nlist.index].mean()#邻居评价平均值for index in nlist.index:ncor=corr.loc[itemid,index]nratsum+=ncor*(df.loc[usrid][index]-nv[index])corsum+=abs(ncor)if(corsum!=0):rats.at[usrid,itemid]= itemv + nratsum/corsumelse:rats.at[usrid,itemid]= itemvelse:rats.at[usrid,itemid]= Nonereturn rats
def recomm(df,userdf,Nn=15,TopN=3):ratings=prediction(df,userdf,Nn)#获取预测评分recomm=[]#存放推荐结果for usrid in userdf.index:#获取按NA值获取未评分项ratft=userdf.loc[usrid].isnull()ratnull=ratings.loc[usrid][ratft]#对预测评分进行排序if(len(ratnull)>=TopN):sortlist=(ratnull.sort_values(ascending=False)).index[:TopN]else:sortlist=ratnull.sort_values(ascending=False).index[:len(ratnull)]recomm.append(sortlist)return ratings,recomm

在Python中实现基于用户的协同过滤算法进行个性化电影推荐。将原始的事务性数据导入Python中,因为原始数据无字段名,所以首先需要对相应的字段进行重命名,再运行基于用户的协同过滤算法。得到用户预测评分数据和用户推荐列表数据,如下所示。

# 使用基于UBCF算法对电影进行推荐
import pandas as pd
from recommender import recomm  # 加载自编推荐函数# 读入数据
traindata = pd.read_csv('../data/u1.base', sep='\t', header=None, index_col=None)
testdata = pd.read_csv('../data/u1.test', sep='\t', header=None, index_col=None)
# 删除时间标签列
traindata.drop(3, axis=1, inplace=True)
testdata.drop(3, axis=1, inplace=True)
# 行与列重新命名
traindata.rename(columns={0: 'userid', 1: 'movid', 2: 'rat'}, inplace=True)
testdata.rename(columns={0: 'userid', 1: 'movid', 2: 'rat'}, inplace=True)
traindf = traindata.pivot(index='userid', columns='movid', values='rat')
testdf = testdata.pivot(index='userid', columns='movid', values='rat')
traindf.rename(index={i: 'usr%d' % (i) for i in traindf.index}, inplace=True)
traindf.rename(columns={i: 'mov%d' % (i) for i in traindf.columns}, inplace=True)
testdf.rename(index={i: 'usr%d' % (i) for i in testdf.index}, inplace=True)
testdf.rename(columns={i: 'mov%d' % (i) for i in testdf.columns}, inplace=True)
userdf = traindf.loc[testdf.index]
# 获取预测评分和推荐列表
trainrats, trainrecomm = recomm(traindf, userdf)
print('用户预测评分的前5行:\n', trainrats.head())# 保存预测的评分
trainrats.to_csv('../tmp/movie_comm.csv', index=False, encoding='utf-8')
print('用户推荐列表的前5行:\n', trainrecomm[:5])

在这里插入图片描述

(二)基于物品的协同过滤

基于物品的协同过滤的原理和基于用户的协同过滤类似,只是在计算邻居时采用物品本身,而不是从用户的角度,即基于用户对物品的偏好找到相似的物品,再根据用户的历史偏好,推荐相似的物品给用户。从计算的角度看,是将所有用户对某个物品的偏好作为一个向量计算物品之间的相似度,得到物品的相似物品后,根据用户历史的偏好预测当前用户还没有表示偏好的物品,计算得到一个排序的物品列表作为推荐。对于物品A,根据所有用户的历史偏好,喜欢物品A的用户都喜欢物品C,得出物品A和物品C比较相似,而用户C喜欢物品A,可以推断出用户C可能也喜欢物品C,如图所示。

在这里插入图片描述

1. 算法过程

根据协同过滤的处理过程可知,基于物品的协同过滤算法(简称ItemCF算法)主要分为两个步骤。

  • 计算物品之间的相似度。
  • 根据物品的相似度和用户的历史行为为用户生成推荐列表。

其中关于物品相似度计算的方法有夹角余弦、杰卡德(Jaccard)相似系数和相关系数等。将用户对某一个物品的喜好或评分作为一个向量,例如,所有用户对物品1的评分或喜好程度表示为 A 1 = ( x 11 , x 21 , x 31 , ⋯ , x n 1 ) A_1=(x_{11},x_{21},x_{31},\cdots,x_{n1}) A1=(x11,x21,x31,,xn1),所有用户对物品 M M M的评分或喜好程度表示为 A M = ( x 1 m , x 2 m , x 3 m , ⋯ , x n m ) A_M=(x_{1m},x_{2m},x_{3m},\cdots,x_{nm}) AM=(x1m,x2m,x3m,,xnm),其中 m m m为物品, n n n为用户数。采用物品相似度几种计算方法计算两个物品之间的相似度,其计算公式如下表所示。

方法公式说明
夹角余弦 s i m l m = ∑ k = 1 n x k 1 x k m ∑ k = 1 n x k 1 2 ∑ k = 1 n x k m 2 \begin{aligned}\mathrm{sim}_{lm}=\frac{\sum_{k=1}^{n}x_{k1}x_{km}}{\sqrt{\sum_{k=1}^{n}x_{k1}^2}\sqrt{\sum_{k=1}^{n}x_{km}^2}}\end{aligned} simlm=k=1nxk12 k=1nxkm2 k=1nxk1xkm k k k表示用户,取值范围为[-1,1]区间内,当余弦值接近±1,表明两个向量有较强的相似性。当余弦值为0时表示不相关
杰卡德相似系数 J ( A L , A M ) = ∣ A L ∩ A M ∣ ∣ A L ∪ A M ∣ \begin{aligned}J(A_L,A_M)=\frac{\lvert A_L\cap A_M\rvert}{\lvert A_L\cup A_M\rvert}\end{aligned} J(AL,AM)=ALAMALAM分母 A L ∪ A M A_L \cup A_M ALAM表示喜欢物品 L L L与喜欢物品 M M M的用户总数,分子 A L ∩ A M A_L \cap A_M ALAM表示同时喜欢物品 L L L和物品 M M M的用户数
相关系数 s i m l m = ∑ k = 1 n ( x k 1 − A ‾ L ) ( x k m − A ‾ M ) ∑ k = 1 n ( x k 1 − A ‾ L ) 2 ∑ k = 1 n ( x k m − A ‾ M ) 2 \begin{aligned}\mathrm{sim}_{lm}=\frac{\sum_{k=1}^{n}(x_{k1}-\overline{A}_L)(x_{km}-\overline{A}_M)}{\sqrt{\sum_{k=1}^{n}(x_{k1}-\overline{A}_L)^2}\sqrt{\sum_{k=1}^{n}(x_{km}-\overline{A}_M)^2}}\end{aligned} simlm=k=1n(xk1AL)2 k=1n(xkmAM)2 k=1n(xk1AL)(xkmAM)相关系数的取值范围固定在[-1,1]区间内。相关系数的绝对值越大,则表明两者相关度越高

计算各个物品之间的相似度后,即可构成一个物品之间的相似度矩阵,如下表所示。通过相似度矩阵,推荐算法会为用户推荐与用户偏好的物品最相似的 K K K个的物品。

物品ABCD
A10.7630.2510
B0.76310.1340.529
C0.2510.13410.033
D00.5290.0331

下式度量了推荐算法中用户对所有物品的感兴趣程度。其中 R R R代表了用户对物品的兴趣, s i m \mathrm{sim} sim代表了所有物品之间的相似度, P P P为用户对物品感兴趣的程度。 P = s i m × R P=\mathrm{sim}\times R P=sim×R

推荐系统是根据物品的相似度以及用户的历史行为对用户的兴趣度进行预测并推荐,在评价模型的时候一般是将数据集划分成训练集和测试集两部分。模型通过在训练集的数据上进行训练学习得到推荐模型,然后在测试集数据上进行模型预测,最终统计出相应的评测指标评价模型预测效果的好与坏。

模型的评测采用的方法是交叉验证法。交叉验证法即将用户行为数据集按照均匀分布随机分成M份,挑选一份作为测试集,将剩下的M-1份作为训练集。在训练集上建立模型,并在测试集上对用户行为进行预测,统计出相应的评测指标。为了保证评测指标并不是过拟合的结果,需要进行M次实验,并且每次都使用不同的测试集。最后将M次实验测出的评测指标的平均值作为最终的评测指标。

构建基于物品的协同过滤推荐模型的流程如图所示。

在这里插入图片描述

其中训练集与测试集是通过交叉验证的方法划分后的数据集。通过协同过滤算法的原理可知,在建立推荐系统时,建模的数据量越大,越能消除数据中的随机性,得到的推荐结果越好。算法的弊端在于数据量越大,模型建立和模型计算耗时越久。

2. 基于物品的个性化电影推荐

同样使用MovieLens数据集,将原始数据导入Python后使用基于物品的协同过滤算法进行个性化电影推荐,结果如图所示。

import pandas as pd# 读入数据
traindata = pd.read_csv('../data/u1.base', sep='\t', header=None, index_col=None)
testdata = pd.read_csv('../data/u1.test', sep='\t', header=None, index_col=None)
# 删除时间标签列
traindata.drop(3, axis=1, inplace=True)
testdata.drop(3, axis=1, inplace=True)
# 行与列重新命名
traindata.rename(columns={0: 'userid', 1: 'movid', 2: 'rat'}, inplace=True)
testdata.rename(columns={0: 'userid', 1: 'movid', 2: 'rat'}, inplace=True)
# 构建训练集数据
user_tr = traindata.iloc[:, 0]  # 训练集用户id
mov_tr = traindata.iloc[:, 1]  # 训练集电影id
user_tr = list(set(user_tr))  # 去重处理
mov_tr = list(set(mov_tr))  # 去重处理
print('训练集电影数:', len(mov_tr))# 利用训练集数据构建模型
ui_matrix_tr = pd.DataFrame(0, index=user_tr, columns=mov_tr)
# 求用户-物品矩阵
for i in traindata.index:ui_matrix_tr.loc[traindata.loc[i, 'userid'], traindata.loc[i, 'movid']] = 1
print('训练集用户观影次数:', sum(ui_matrix_tr.sum(axis=1)))# 求物品相似度矩阵(因计算量较大,需要耗费的时间较久)
item_matrix_tr = pd.DataFrame(0, index=mov_tr, columns=mov_tr)
for i in item_matrix_tr.index:for j in item_matrix_tr.index:a = sum(ui_matrix_tr.loc[:, [i, j]].sum(axis=1) == 2)b = sum(ui_matrix_tr.loc[:, [i, j]].sum(axis=1) != 0)item_matrix_tr.loc[i, j] = a / b
# 将物品相似度矩阵对角线处理为零
for i in item_matrix_tr.index:item_matrix_tr.loc[i, i] = 0
# 利用测试集数据对模型评价
user_te = testdata.iloc[:, 0]
mov_te = testdata.iloc[:, 1]
user_te = list(set(user_te))
mov_te = list(set(mov_te))
# 测试集数据用户物品矩阵
ui_matrix_te = pd.DataFrame(0, index=user_te, columns=mov_te)
for i in testdata.index:ui_matrix_te.loc[testdata.loc[i, 'userid'], testdata.loc[i, 'movid']] = 1
# 对测试集用户进行推荐
res = pd.DataFrame('NaN', index=testdata.index, columns=['User', '已观看电影', '推荐电影', 'T/F'])
res.loc[:, 'User'] = list(testdata.iloc[:, 0])
res.loc[:, '已观看电影'] = list(testdata.iloc[:, 1])
# 开始推荐
for i in res.index:if res.loc[i, '已观看电影'] in list(item_matrix_tr.index):res.loc[i, '推荐电影'] = item_matrix_tr.loc[res.loc[i, '已观看电影'], :].argmax()if res.loc[i, '推荐电影'] in mov_te:res.loc[i, 'T/F'] = ui_matrix_te.loc[res.loc[i, 'User'], res.loc[i, '推荐电影']] == 1else:res.loc[i, 'T/F'] = False
# 保存推荐结果
res.to_csv('../tmp/res_mov.csv', index=False, encoding='utf8')
print('推荐结果前5行: \n', res.head())

在这里插入图片描述

通过基于物品的协同过滤算法构建的推荐系统,得到针对用户每次观影记录的用户观影推荐,但是推荐结果可能存在NaN的情况。这种情况是由于在目前的数据集中,观看该电影的只有单独一个用户,使用协同过滤算法计算该电影与其他电影的相似度为0,所以出现了无法推荐的情况。

小结

本文首先介绍了智能推荐的概念、应用、评价指标。智能推荐系统能帮助用户找到自己喜爱的物品,同时也能帮助厂商和网站推广产品。智能推荐的应用范围涵盖多个领域,包括电子商务、视频网站、音乐、社交网络等。评价标准可分为3个方面,即离线实验、用户调查和在线实验。

接着介绍了智能推荐几种常见的智能推荐算法,包括关联规则和协同过滤等,其中关联规则可以挖掘出物品间的关联关系,依据关联关系的强弱为用户推荐,常见的关联规则算法有Apriori和FP-Growth。

常见的协同过滤推荐技术主要分为两大类,基于用户的协同过滤推荐和基于物品的协同过滤推荐。基于用户的智能推荐侧重于给用户推荐相似用户喜好的物品,而基于物品的智能推荐更侧重于用户本身的历史浏览记录。

:以上文中的数据文件及相关资源下载地址:
链接:https://pan.quark.cn/s/2361df48c692
提取码:umgL

相关文章:

【机器学习 | 数据挖掘】智能推荐算法

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈智能大数据分析 ⌋ ⌋ ⌋ 智能大数据分析是指利用先进的技术和算法对大规模数据进行深入分析和挖掘&#xff0c;以提取有价值的信息和洞察。它结合了大数据技术、人工智能&#xff08;AI&#xff09;、机器学习&#xff08;ML&a…...

120.【C语言】数据结构之快速排序(详解Hoare排序算法)

目录 1.Hoare单趟排序思想 2.单趟排序代码 初次写的代码 运行结果 查找问题原因 尝试解决问题 初次修正后代码 运行结果 正确的单趟排序代码 3.将单趟排序嵌入 如何递归? 递归结束的条件 1.较容易分析的结束条件:leftright 2.以{2,1}为例分析另一个结束条件 完整…...

uniapp通过v-if进行判断时,会出现闪屏?【已解决】

1.问题&#xff1a;按钮切换时&#xff0c;通过v-if来判断&#xff0c;会出现闪烁情况&#xff0c;影响用户体验 2.v-if 闪烁问题可能的原因 ‌条件切换频繁‌&#xff1a;如果 v-if 指令的条件在短时间内频繁切换&#xff0c;会导致元素不断被销毁和重新创建&#xff0c;从而…...

各种网站(学习资源、常用工具及其他,持续更新中~)

欢迎围观笔者的个人博客~ 也欢迎通过RSS网址https://kangaroogao.github.io/atom.xml进行订阅~ 大学指南 上海交通大学生存手册中国科学技术大学人工智能与数据科学学院本科进阶指南USTC不完全入学指南大学生活质量指北科研论 信息搜集 AI信息搜集USTC飞跃网站计算机保研 技…...

网络技术-QoS策略以及如何定义 流分类,流行为,流策略

一&#xff1a;QoS策略简介 QoS策略由如下部分组成&#xff1a; 类&#xff0c;定义了对报文进行识别的规则。 流行为&#xff0c;定义了一组针对类识别后的报文所做的QoS动作。 通过将类和流行为关联起来&#xff0c;QoS策略可对符合分类规则的报文执行流行为中定义的…...

线程晨考day20

1.线程的五种状态 创建 就绪 运行 阻塞 死亡 2.创建线程的两种方式 继承Thread类 重写run方法 实现Runnable接口 重写run方法 3.调用start和调用run方法的区别 调用start方法表示会开启新的线程 run方法不会开启新的线程 4.线程调度常用的方法 sleep() join() yield() 5.进程和…...

【ES6复习笔记】迭代器(10)

什么是迭代器&#xff1f; 迭代器&#xff08;Iterator&#xff09;是一种对象&#xff0c;它能够遍历并访问一个集合中的元素。在 JavaScript 中&#xff0c;迭代器提供了一种统一的方式来处理各种集合&#xff0c;如数组、字符串、Map、Set 等。通过迭代器&#xff0c;我们可…...

MySQL的TIMESTAMP类型字段非空和默认值属性的影响

同事说他通过某款商业数据同步软件将一个 MySQL 5.7.28 的库同步到 MySQL 5.7.20 的库时&#xff0c;如果表中含有 TIMESTAMP 数据类型、缺省值为 current_timestamp 的字段&#xff0c;这些表的同步任务就都失败了&#xff0c;而另外的一些包含了 DATETIME 数据类型的表就同步…...

【Linux进程】初悉进程

学习编程就得循环渐进&#xff0c;扎实基础&#xff0c;勿在浮沙筑高台 循环渐进Forward-CSDN博客 进程调度简介 1.2进程查看命令 1.3进程的几个要素 二、进程的生命周期 2.1进程状态文字描述 2.2进程状态的切换 2.3task_struct数据结构 2.4进程优先级 ⑴优先级的代…...

Python学习之路(5)— 使用C扩展

Python学习之路&#xff08;5&#xff09;— 使用C扩展 一、前言 参考&#xff1a;https://www.cnblogs.com/yinguo/p/4641349.html Python C扩展是指用C语言编写的代码&#xff0c;然后编译成Python可以调用的库。这样可以提高Python代码的执行效率&#xff0c;或者实现某些…...

动态规划34:446. 等差数列划分 II - 子序列

动态规划解题步骤&#xff1a; 1.确定状态表示&#xff1a;dp[i]是什么 2.确定状态转移方程&#xff1a;dp[i]等于什么 3.初始化&#xff1a;确保状态转移方程不越界 4.确定填表顺序&#xff1a;根据状态转移方程即可确定填表顺序 5.确定返回值 题目链接&#xff1a;446.…...

PPT画图——如何设置导致图片为600dpi

winr&#xff0c;输入regedit打开注册表 按路径找&#xff0c;HKEY_CURRENT_USER\Software\Microsoft\Office\XX.0\PowerPoint\Options&#xff08;xx为版本号&#xff0c;16.0 or 15.0或则其他&#xff09;。名称命名&#xff1a;ExportBitmapResolution 保存即可&#xff0c;…...

【模块系列】STM321.69TFT屏幕

前言 在翻翻自己的器件盒的时候&#xff0c;发现这块好久之前买的TFT屏了&#xff0c;想起还没有用STM32点亮过&#xff0c;手头上正好有立创的梁山派STM32F4&#xff0c;就试着按照网上的文章教程顺便移植个LVGL看看&#xff0c;然后就有了就本文。 代码工程命名的是LvglDemo&…...

大模型辅助测试的正确打开方式?

测试的基本目的之一&#xff0c;是对被测对象进行质量评估。换言之&#xff0c;是要提供关于被测对象质量的“确定性”。因此&#xff0c;我们很忌讳在测试设计中引入“不确定性”&#xff0c;比如采用不可靠的测试工具、自动化测试代码逻辑复杂易错、测试选择假设过于主观等等…...

三相电的相电压、线电压、额定值、有效值,变比,零序电压,零序电流,三相三线制的三角形连接,三相四线制的星形连接

在二次设备配置中经常有根电压系统相关的名词&#xff0c;本身不是学电气的&#xff0c;有些名词经常查了忘&#xff0c;后续工作所有遇到跟电气相关的知识总结在此帖&#xff0c;便于后续直接查看&#xff0c;避免每次都要重新查、重新梳理。 相电压和线电压的关系是根号3倍&a…...

电商网站的基础用户数在100万,日活跃用户数在1万左右,系统下单TPS最大支持1000,应用服务要保证高可用。请预估该网站每天的使用成本。

要预估一个电商网站每天的使用成本&#xff0c;我们需要考虑多个因素&#xff0c;包括计算资源、数据库、缓存、存储、网络流量、负载均衡、安全服务、监控与日志等。以下是基于您提供的信息&#xff08;基础用户数100万&#xff0c;日活跃用户数1万&#xff0c;系统下单TPS最大…...

线性代数期末总复习的点点滴滴(1)

一、可逆矩阵、行列式、秩的关系 1.行列式与可逆矩阵的关系 所以&#xff0c;不难看出矩阵可逆的充分必要条件是该矩阵的行列式不为0。 2.接着来看&#xff0c;满秩和矩阵行列式的关系 不难看出满秩和行列式不为0是等价的。 3.再来看&#xff0c;满秩和矩阵可逆的关系 说明了…...

python+reportlab创建PDF文件

目录 字体导入 画布写入 创建画布对象 写入文本内容 写入图片内容 新增页 画线 表格 保存 模板写入 创建模板对象 段落及样式 表格及样式 画框 图片 页眉页脚 添加图形 构建pdf文件 reportlab库支持创建包含文本、图像、图形和表格的复杂PDF文档。 安装&…...

2024最新qrcode.min.js生成二维码Demo

找了一堆代码一堆GPT&#xff0c;终于给写对了&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><…...

【Microi吾码】开源力量赋能低代码创新,重塑软件开发生态格局

我的个人主页 文章专栏&#xff1a;Microi吾码 一、引言 在当今数字化浪潮汹涌澎湃的时代&#xff0c;软件开发的需求呈现出爆发式增长。企业为了在激烈的市场竞争中脱颖而出&#xff0c;不断寻求创新的解决方案以加速数字化转型。传统的软件开发方式往往面临着开发周期长、技…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比

在机器学习的回归分析中&#xff0c;损失函数的选择对模型性能具有决定性影响。均方误差&#xff08;MSE&#xff09;作为经典的损失函数&#xff0c;在处理干净数据时表现优异&#xff0c;但在面对包含异常值的噪声数据时&#xff0c;其对大误差的二次惩罚机制往往导致模型参数…...

嵌入式学习笔记DAY33(网络编程——TCP)

一、网络架构 C/S &#xff08;client/server 客户端/服务器&#xff09;&#xff1a;由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序&#xff0c;负责提供用户界面和交互逻辑 &#xff0c;接收用户输入&#xff0c;向服务器发送请求&#xff0c;并展示服务…...