文章目录
常用算法分类
基于人口统计学的推荐
- 基于人口统计学的推荐机制(Demographic-based Recommendation)是一种最易于实现的推荐方法,它只是 简单的根据系统用户的基本信息发现用户的相关程度,然后将相似用户喜爱的其他物品推荐给当前用户
- 对于没有明确含义的用户信息(比如登陆时间、地域等上下文信息),可以通过聚类等手段,给用户打上分类标签
- 对于特定标签的用户,又可以根据预设的规则(知识)或者模型,推荐出对应的物品
- 用户信息标签化的过程 一般被称为 用户画像(User Profiling)
- 用户画像
- 用户画像(User Profiling)就是企业通过收集与分析消费者社会属性、生活习惯、消费行为等主要信息的数据之后,完美地抽象出一个用户的商业全貌是企业应用大数据技术的基本方式
- 用户画像为企业提供足够的信息基础,能够帮助企业快速找到精准用户群体以及用户需求等更为广泛的反馈信息
- 作为大数据的根基,它完美地抽象出一个用户的信息全貌,为进一步精准、快速地分析用户行为习惯、消费习惯等重要信息,提供足够的数据基础
基于内容的推荐算法与特征工程
- Content-based Recommenddations(CB) 根据推荐物品或内容的元数据,发现物品的相关性,再基于用户过去的喜好记录,为用户推荐相似的物品
- 通过抽取物品内在或者外在的特征值,实现相似度计算
- 将用户(user)个人信息的特征(基于喜好记录或是预设兴趣标签),和物品(item)的特征相匹配,就能得到用户对物品感兴趣的程度
- 相似度计算
- 相似度的评判,可以用距离表示,而一般更常用的是“余弦相似度”
- 欧式距离 : d ( x , y ) = ∑ i ( x i − y i ) 2 d(x,y)=\sqrt{\sum_{i}^{}(x_{i}-y_{i})^{2}} d(x,y)=∑i(xi−yi)2
- 余弦相似度:
c
o
s
θ
=
a
∗
b
∣
∣
a
∣
∣
×
∣
∣
b
∣
∣
=
∑
i
x
i
y
i
∑
i
x
i
2
×
∑
i
y
i
2
cosθ=\frac{a*b}{||a||\times ||b||}=\frac{\sum_{i}^{}x_{i}y_{i}}{\sqrt{\sum_{i}^{}x_{i}^{2}}\times \sqrt{\sum_{i}^{}y_{i}^{2}}}
cosθ=∣∣a∣∣×∣∣b∣∣a∗b=∑ixi2×∑iyi2∑ixiyi
- 对于物品的特征提取 ----- 打标签(tag)
- 专家标签(PGC)
- 用户自定义标签(UGC)
- 降维分析数据,提取隐语义标签(LFM)
- 对于文本信息的特征提取 – 关键词
- 分词、语义处理和情感分析(NLP)
- 潜在语义分析(LSA)
- 基于内容推荐系统的高层次结构
- 特征工程
- 特征:作为判断条件的一组输入变量(自变量),是做出判断的依据
- 目标:判断和预测的目标,模型的输出变量(因变量),是特征所产生的的结果
- 特征(feature):数据中抽取出来的对结果预测有用的信息
- 特征的个数就是数据的观测维度
- 特征工程是使用专业背景知识和技巧处理数据,使得特征能在机器学习算法上发挥更好的作用的过程
- 特征工程一般包括特征清洗(采样、清洗异常样本),特征处理和特征选择
- 特征按照不同的数据类型分类,有不同的特征处理方法
- 数值型、类别型、时间型、统计型
- 数值型特征处理
- 用连续数值表示当前维度特征,通常会对数值型特征进行数学上处理,主要的是 归一化和离散化
- 归一化/幅度调整
- 特征与特征之间应该是平等的,区别应该体现在特征内部
- 公式: F e a t u r e n e w = F e a t u r e o l d F e a t u r e m a x − F e a t u r e m i n Feature_{new}=\frac{Feature_{old}}{Feature_{max}-Feature_{min}} Featurenew=Featuremax−FeatureminFeatureold
- 离散化
- 将原始连续值切断,转化为离散值
- 公式: h θ ( x ) = g ( θ T x ) = 1 1 + e − θ T x h_{θ}(x)=g(θ^{T}x)=\frac{1}{1+e^{-θ^{T}x}} hθ(x)=g(θTx)=1+e−θTx1
- 两种方式:
- 等步长
- 等频:min -> 25% -> 75% -> max
- 比较:等频的离散化方法很精准,但需要每次都对数据分布进行一遍重新计算,因此昨天训练出的模型在今天并不一定能使用,等频的方法并不是固定的;等频不固定,但是精准,等步长是固定的,但精准度没那么高。
- 类别型特征处理
- 类别型数据本身没有大小关系,需要将它们编码为数字,但它们之间不能有预先设定的大小关系,因此既要做到公平又要区别它们,可以考虑 直接开辟多个空间
- One-Hot 编码/哑变量
- One-Hot 编码/哑变量所做的就是将类别型数据 平行展开,即经过One-Hot编码后,这个特征的空间会膨胀
- 时间型特征处理
- 时间型特征既可以做连续值,又可以看做离散值
- 连续值
- 持续时间(网页浏览时长)
- 间隔时间(上一次购买/点击离现在的时间间隔)
- 离散值
- 一天中哪个时间段
- 一周中的星期几
- 一年中哪个月/时间
- 工作日/周末
- 统计型特征处理
- 加减平均:商品价格高于平均价格多少,用户在某个品类下消费超过多少
- 分位线:商品属于售出商品价格的分位线处
- 次序性:商品处于热门商品第几位
- 比例类:电商中商品的好/中/差评比例
- 推荐系统常见反馈数据
- 基于UGC的推荐
- 用户用标签来描述对物品的看法,所以用户生成标签(UGC)是联系用户和物品的纽带,也是反应用户兴趣的重要数据源
- 一个用户标签行为的数据集一般由一个三元组(用户、物品、标签)的集合表示,其中一条记录(u,i,b)表示用户u给物品i打上了标签b
- 一个简单的算法
- 统计每个用户最常用的标签
- 对于每个标签,统计被打过这个标签次数最多的物品
- 对于一个用户,首先找到他常用的标签,然后找到具有这些标签的最热门的物品,最后推荐给他
- 所以用户u对物品i的兴趣公式为 : p ( u , i ) = ∑ b n u , b n b , i p(u,i)=\sum_{b}^{}n_{u,b}n_{b,i} p(u,i)=∑bnu,bnb,i,其中, n u , b n_{u,b} nu,b是用户u打过标签b的次数, n b , i n_{b,i} nb,i是物品i被打过标签b的次数
- 基于UGC简单推荐的问题
- 简单算法中直接将用户打出标签的次数和物品得到标签次数相乘,可以简单地表现出用户对物品某个特征的兴趣
- 这种方法倾向于给热门标签(谁都会给的标签,如“大片”、“搞笑“等)、热门物品(打标签人数最多)比较大的权重,如果一个热门物品同时对应着热门标签,那它就会“霸榜”,推荐的个性化、新颖度就会降低
- TF-IDF
- 词频-逆文档频率(Term Frequency-Inverse Document Frequency,TF-IDF)是一种用于资讯检索与文本挖掘的常用加权技术
- TF-IDF是一种统计方法,用以评估一个字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降
- TFIDF=TF X IDF
- TF-IDF的主要思想:如果某个词或短语在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类
- TF-IDF加权的各种形式常被搜索引擎应用,作为文件与用户查询之间相关程度的度量或评级
- 词频(Team Frequency,TF)
- 指的是某一个给定的词语在该文件中出现的频率。这个数字是对词数的归一化,以防止偏向更长的文件。(同一个词语在长文件里可能会比短文件有更高的词数,而不管改词语重要与否)
- T F i , j = n i , j n ∗ , j TF_{i,j}=\frac{n_{i,j}}{n_{*,j}} TFi,j=n∗,jni,j
- 其中, T F i , j TF_{i,j} TFi,j表示词语i在文档j中出现的频率, n i , j n_{i,j} ni,j表示i在j中出现的次数, n ∗ , j n_{*,j} n∗,j表示文档j的总词数
- 逆向文件频率(Inverse Document Frequency,IDF)
- 是一个词语普遍重要性的度量,某一特定词语的IDF,可以由总文档数目除以包含该词语之文档的数目,再将得到的商取对数得到
- I D F i = l o g ( N + 1 N i + 1 ) IDF_{i}=log(\frac{N+1}{N_{i}+1}) IDFi=log(Ni+1N+1)
- 其中 I D F i IDF_{i} IDFi表示词语i在文档集中的逆文档频率,N表示文档集中的文档总数, N i N_{i} Ni表示文档集中包含了词语i的文档数
- TF-IDF对基于UGC推荐的改进
- p ( u , i ) = ∑ b n u , b n b i p(u,i)=\sum_{b}^{}n_{u,b}n_{bi} p(u,i)=∑bnu,bnbi
- 为了避免热门标签和热门物品获得更多的权重,我们需要对“热门”进行惩罚
- 借鉴TF-IDF的思想,以一个物品的所有标签作为“文档”,标签作为“词语”,从而计算标签的“词频”(在物品所有标签中的频率)和“逆文档频率”(在其它物品标签中普遍出现的频率)
- 由于“物品i的所有标签” n ∗ , i n_{*,i} n∗,i应该对标签权重没有影响,而"所有标签总数"N对于所有标签是一定的,所以这两项可以略去。在简单算法的基础上,直接加入对热门标签和热门物品的惩罚项: p ( u , i ) = ∑ b n u , b l o g ( 1 + n b ( u ) ) n b , i l o g ( 1 + n i ( u ) ) p(u,i)=\sum_{b}\frac{n_{u,b}}{log(1+n_{b}^{(u)})}\frac{n_{b,i}}{log(1+n_{i}^{(u)})} p(u,i)=∑blog(1+nb(u))nu,blog(1+ni(u))nb,i, 其中, n b ( u ) n_{b}^{(u)} nb(u)记录了标签b被多少个不同的用户使用过, n i ( u ) n_{i}^{(u)} ni(u)记录了物品i被多少个不同的用户打过标签
基于协同过滤的推荐
-
协同过滤(Collaborative Filtering ,CF)
-
基于近邻的协同过滤
- 基于用户(User-CF)
- 基于物品(Item-CF)
-
基于模型的协同过滤
- 奇异值分解(SVD)
- 潜在语义分析(LSA)
- 支持向量机(SVM)
-
基于内容(Content based,CB)主要利用的是用户评价过的物品的内容特征,而CF方法还可以利用其它用户评分过的物品内容
-
CF可以解决CB的一些局限
- 物品内容不完全或者难以获得时,依然可以通过其它用户的反馈给出推荐
- CF基于用户之间对物品的评价质量,避免了CB仅依赖内容可能造成的对物品质量判断的干扰
- CF推荐不受内容的限制,只要其它类似用户给出了对不同物品的兴趣,CF就可以给用户推荐出内容差异很大的物品(但要有某种内在联系)
-
分为两类:基于近邻的推荐和基于模型的推荐
基于近邻的推荐
- 基于近邻的推荐系统根据的是相同“口碑”准则
基于用户的协同过滤(User-CF)
- 基于用户的协同过滤推荐的基本原理是,根据所有用户对物品的偏好,发现与当前用户口味和偏好相似的“邻居”用户群,并推荐近邻所偏好的物品
- 在一般的应用中是采用计算“k-近邻”的算法:基于这K个邻居的历史偏好信息,为当前用户进行推荐
- User-CF和基于人口统计学的推荐机制 的比较
- 两者都是计算用户的相似度,并基于相似的“邻居”用户群计算推荐
- 它们所不同的是如何计算用户的相似度:基于人口统计学的机制只考虑了用户本身的特征,而基于用户的协同过滤机制可是在用户的历史偏好的数据上计算用户的相似度,它的基本假设*是,喜欢类似物品的用户可能有相同或者相似的口味和偏好
基于物品的协同过滤(Item-CF)
- 基于物品的协同过滤推荐的基本原理与基于用户的类似,只是使用所有用户对物品的偏好,发现物品与物品之间的相似度,然后根据用户的历史偏好信息,将类似的物品推荐给用户
- Item-CF和基于内容(CB)的推荐比较
- 其实两者都是基于物品相似度预测推荐,只是相似度计算的方法不一样,前者是从用户历史的偏好推断,而后者是基于物品本身的属性特征信息
- 在实际的项目中,如何选择是用基于用户的协同过滤还是基于物品的协同过滤?
- 在电商、电影、音乐等网站,其用户数量远大于物品数量的情况下,我们选择使用基于物品的协同过滤
- 在新闻网站等网站,其物品数量远大于用户数量的情况下,我们优先选择使用基于用户的协同过滤
- Item-CF的实时性相较于User-CF 会更好一些,所以目前推荐策略的主流选择是 Item-CF
- 推荐策略没有好坏之分,推荐策略的选择与具体的应用场景相适应变化
基于近邻的推荐的优缺点
- 优点:
- 它不需要对物品或者用户进行严格的建模,而且也不要求对物品特征的描述可理解的,所以这种方法也是领域无关的
- 这种方法计算出来的推荐是开放的,可以共用他人的经验,很好的支持用户发现潜在的兴趣偏好
- 缺点:
- 这种策略的核心是 基于历史数据,所以对新物品和新用户都有“冷启动”的问题
- 推荐的效果依赖于用户历史偏好数据的多少和准确性
- 在大部分的实现中,用户历史偏好是用稀疏矩阵进行存储的,而稀疏矩阵上的计算有些明显的问题,包括可能少部分人的错误偏好会对推荐的准确度会有很大的影响等等
- 对于一些特殊品味的用户不能给予很好的推荐
基于模型的协同过滤
- 基本思想
- 用户具有一定的特征,决定着他的偏好选择
- 物品具有一定的特征,影响着用户是否选择它
- 用户之所以选择某一个商品,是因为用户特征与物品特征相匹配
- 基于这种思想,模型的建立相当于从行为数据中提取特征,给用户和物品同时打上“标签”;这和基于人口统计学的用户标签、基于内容方法的物品标签本质上是一样的,都是特征的提取和匹配
- 有显性特征时(比如用户标签、物品分类标签)我们都可以直接匹配做出推荐;没有显性特征时,我们可以根据已有的偏好数据,去发掘出隐藏的特征,这需要用到隐语义模型(LFM)
- 基于模型的协同过滤推荐,就是基于样本的用户偏好信息,训练一个推荐模型,然后根据实时的用户喜好的信息进行预测新物品的得分,计算推荐
- 基于近邻的推荐和基于模型的推荐
- 基于近邻的推荐是在预测时直接使用已有的用户偏好数据,通过近邻数据来预测对新物品的偏好(类似分类)
- 而基于模型的方法,是要使用这些偏好数据来训练模型,找到内在规律,再用模型来做预测(类似回归)
- 训练模型时,可以基于标签内容来提取物品特征,也可以让模型去发掘物品的潜在特征;这样的模型被称为隐语义模型(Latent Factor Model,LFM)
隐语义模型(LFM)
- 用隐语义模型来进行协同过滤的目标
- 揭示隐藏的特征,这些特征能够解释为什么给出对应的预测评分
- 这类特征可能是无法直接用语言解释描述的,而事实上我们并不需要知道
- 通过矩阵分解进行降维分析
- 协同过滤算法非常依赖历史数据,而一般的推荐系统中,偏好数据又往往是稀疏的;这就需要对原始数据进行降维处理
- 分解之后的矩阵,就代表了用户和物品的隐藏特征
- 隐语义模型的实例
- 基于概率的隐语义分析(pLSA)
- 隐式狄利克雷分布模型(LDA)
- 矩阵因子分解模型(基于奇异值分解的模型,SVD)
- LFM降维方法--------矩阵因子分解
- 假设用户物品评分矩阵为R,现在有m个用户,n个物品
- 我们想要发现k个隐类,我们的任务就是找到两个矩阵P和Q,使这两个矩阵的乘积近似等于R,即将用户物品评分矩阵R分解成两个低维矩阵相乘:
R
~
m
×
n
=
P
m
×
k
T
∙
Q
k
×
n
≈
R
\tilde{R}_{m\times n}=P_{m\times k}^{T} \bullet Q_{k \times n} \approx R
R~m×n=Pm×kT∙Qk×n≈R
- LFM的进一步理解
- 我们可以认为,用户之所以给电影打出这样的分数,是有内在原因的,我们能够挖掘出影响用户打分的隐藏因素,进而根据未评分电影与这些隐藏因素的关联度,决定此未评分电影的预测评分
- 应该有一些隐藏的因素,影响用户的打分,比如电影:演员、题材、年代…甚至不一定是人直接可以理解的隐藏因子
- 找到隐藏因子,可以对user和iten进行关联(找到时由于什么使得user喜欢/不喜欢此item,什么会决定user喜欢/不喜欢此item),就可以推测用户是否会喜欢某一部未看过的电影
- 对于用户看过的电影,会有相应的打分,但一个用户不可能看过所有的电影,对于用户没有看过的电影是没有评分的,因此评分矩阵大部分项都是空的,是一个稀疏矩阵
- 如果我们能根据用户给已有的电影打分推测出用户会给没有看过的电影的打分,那么就可以根据预测结果给用户推荐他可能打高分的电影
- 矩阵因子分解
- 一个
m
×
n
m \times n
m×n的打分矩阵R可以用两个小矩阵
P
m
×
k
P_{m\times k}
Pm×k和
Q
k
×
n
Q_{k \times n}
Qk×n的乘积R来近似:
R
~
u
i
=
P
u
T
∙
Q
i
=
∑
k
k
=
1
P
u
k
∙
Q
k
i
\tilde{R}_{ui}=P_{u}^{T}\bullet Q_{i}=\sum_{k}^{k=1}P_{uk}\bullet Q_{ki}
R~ui=PuT∙Qi=∑kk=1Puk∙Qki
- 得到
P
m
×
k
P_{m\times k}
Pm×k和
Q
k
×
n
Q_{k\times n}
Qk×n的乘积
R
~
\tilde{R}
R~不再是稀疏的,之前R中没有的项也可以由P、Q的乘积算出,这样就得到一个 预测评分矩阵
- 如果得到的预测评分矩阵
R
~
\tilde{R}
R~与原评分矩阵R在已知评分位置上的值都近似,那么我们就认为它们在预测位置上的值也是近似的
- 一个
m
×
n
m \times n
m×n的打分矩阵R可以用两个小矩阵
P
m
×
k
P_{m\times k}
Pm×k和
Q
k
×
n
Q_{k \times n}
Qk×n的乘积R来近似:
R
~
u
i
=
P
u
T
∙
Q
i
=
∑
k
k
=
1
P
u
k
∙
Q
k
i
\tilde{R}_{ui}=P_{u}^{T}\bullet Q_{i}=\sum_{k}^{k=1}P_{uk}\bullet Q_{ki}
R~ui=PuT∙Qi=∑kk=1Puk∙Qki
- 模型的求解 – 损失函数
- 如何得到这样的一种分解方式 R ~ = P × Q \tilde{R}=P\times Q R~=P×Q
- 矩阵分解得到的预测评分矩阵 R ~ \tilde{R} R~,与原评分矩阵R在已知的评分项上可能会有误差,我们的目标是找到一个最好的分解方式,让分解之后的预测评分矩阵总误差最小
- 我们选择 平方损失函数 ,并且加入正则化项,以防止过拟合
- C = ∑ ( u , i ) ∈ R 0 ( R u i − R u i ~ ) 2 + R e g = ∑ ( u , i ) ∈ R 0 ( R u i − P u T ∙ Q i ) 2 + λ ∑ u ∣ ∣ P u ∣ ∣ 2 + λ ∑ i ∣ ∣ Q i ∣ ∣ 2 C=\sum_{(u,i)\in R_{0}}(R_{ui}-\tilde{R_{ui}})^{2}+Reg=\sum_{(u,i)\in R_{0}}(R_{ui}-P_{u}^{T}\bullet Q_{i})^{2}+λ\sum_{u}||P_{u}||^{2}+λ\sum_{i}||Q_{i}||^{2} C=∑(u,i)∈R0(Rui−Rui~)2+Reg=∑(u,i)∈R0(Rui−PuT∙Qi)2+λ∑u∣∣Pu∣∣2+λ∑i∣∣Qi∣∣2,其中 λ ∑ u ∣ ∣ P u ∣ ∣ 2 + λ ∑ i ∣ ∣ Q i ∣ ∣ 2 λ\sum_{u}||P_{u}||^{2}+λ\sum_{i}||Q_{i}||^{2} λ∑u∣∣Pu∣∣2+λ∑i∣∣Qi∣∣2是正则化项,λ一般是通过交叉验证得到
- 模型求解算法
- 现在,矩阵因子分解的问题已经转化成一个标准的优化问题,需要求解P、Q,使目标损失函数取最小值
- 最小化过程的求解,一般采用随机梯度下降算法或者交替最小二乘法来实现
- 模型的求解算法-交替最小二乘法(Alternating Least Squares,ALS)
- ALS的思想是,由于两个矩阵P和Q都未知,且通过矩阵乘法耦合在一起,为了使它们解耦,可以先固定Q,把P当做变量,通过损失函数最小化求出P,这就是一个经典的最小二乘问题;再反过来固定求得的P,把Q当做变量,求解出Q:如此交替执行,直到误差满足阈值条件,或者达到迭代上限
- ALS算法的具体过程:
- 1.为Q指定一个初值 Q 0 Q_{0} Q0,可以是随机生成或者全局平均值$
- 2.固定当前 Q 0 Q_{0} Q0值,求解 P 0 P_{0} P0
- 3.固定当前 P 0 P_{0} P0值,求解 Q 1 Q_{1} Q1
- 4.固定当前 Q 1 Q_{1} Q1值,求解 P 1 P_{1} P1
- 5 (重复上面过程)
- 6 直到损失函数的值C收敛,迭代结束
- 梯度下降算法
- 损失函数:L(P,Q)= ∑ ( u , i ) ∈ R 0 ( R u i − P u T ∙ Q i ) 2 + λ ∑ u ∣ ∣ P u ∣ ∣ 2 + λ ∑ i ∣ ∣ Q i ∣ ∣ 2 \sum_{(u,i)\in R_{0}}(R_{ui}-P_{u}^{T}\bullet Q_{i})^{2}+λ\sum_{u}||P_{u}||^{2}+λ\sum_{i}||Q_{i}||^{2} ∑(u,i)∈R0(Rui−PuT∙Qi)2+λ∑u∣∣Pu∣∣2+λ∑i∣∣Qi∣∣2
- 对每一个 P u P_{u} Pu求偏导 : ∂ L ∂ P u = ∂ [ ∑ u , i ( R u i − P u T ∙ Q i ) 2 + λ ∣ ∣ P u ∣ ∣ 2 ] ∂ P u = ∑ i 2 ( P u T Q i − R u i ) Q i + 2 λ P u \frac{∂L}{∂P_{u}}=\frac{∂[\sum_{u,i}(R_{ui}-P_{u}^{T}\bullet Q_{i})^{2}+λ||P_{u}||^{2}]}{∂P_{u}}=\sum_{i}2(P_{u}^{T}Q_{i}-R_{ui})Q_{i}+2λP_{u} ∂Pu∂L=∂Pu∂[∑u,i(Rui−PuT∙Qi)2+λ∣∣Pu∣∣2]=∑i2(PuTQi−Rui)Qi+2λPu
- 梯度下降迭代: P u : = P u − α ⋅ ∂ L ∂ P u = P u − α ⋅ [ ∑ i 2 ( P u T Q i − R u i ) Q i + 2 λ P u ] P_{u}:=P_{u}-α·\frac{∂L}{∂P_{u}}=P_{u}-α·[\sum_{i}2(P_{u}^{T}Q_{i}-R_{ui})Q_{i}+2λP_{u}] Pu:=Pu−α⋅∂Pu∂L=Pu−α⋅[∑i2(PuTQi−Rui)Qi+2λPu]
- 同理,有:
Q
u
:
=
Q
i
−
α
⋅
∂
L
∂
Q
i
=
Q
i
−
α
⋅
[
∑
u
2
(
P
u
T
Q
i
−
R
u
i
)
P
u
+
2
λ
Q
i
]
Q_{u}:=Q_{i}-α·\frac{∂L}{∂Q_{i}}=Q_{i}-α·[\sum_{u}2(P_{u}^{T}Q_{i}-R_{ui})P_{u}+2λQ_{i}]
Qu:=Qi−α⋅∂Qi∂L=Qi−α⋅[∑u2(PuTQi−Rui)Pu+2λQi]
- 关注「一个热爱学习的计算机小白」公众号 ,对作者的小小鼓励,后续更多资源敬请关注。