[TOC]
Hi, 你好。我是茶桁。
上一节课的内容中,咱们学习了 SVD矩阵分解的原理,并在最后提到了,矩阵其实是做运算的一个根基。这一节课,咱们就来举一个简单的示例,拿图片来举例。
图片本身就是一个矩阵,由长和宽来进行组成。还记得咱们在深度学习基础课上讲过卷积吗?那个时候咱们接触过图片矩阵,还有卷积核也是一个矩阵,对吧?有兴趣的可以回头去看看:(《29.深度学习进阶 -卷积的原理》)[https://mp.weixin.qq.com/s/9p-Fu9zgM0m1JdaPjE4Aag]。
我们现在用一个简单的方式,用一个 bmp位图,位图只有一个通道,只需要在一个长和宽的面积上面去有一些颜色的表达。
那既然是一个矩阵了,我们就可以用奇异值把它拆成几个部分,P、S、Q这三个部分组成。这几个分量里面的 S会有很多的特征,这些特征里面现在假设不想要所有特征,只取前 k个,后面那些特征把它去掉。这样我们就可以做一个算法,来对图像做压缩。我们可以看一看能不能用较小的一些存储的空间去尽可能的还原图像。
那么我们将会分为以下几个步骤:
我平时很喜欢用 iPhone拍照,平时拍照的时候大家有没有关注一下自己拍摄的照片大概是多大?一般来说,基本是在2-4M左右吧?其实我们传输的时候,特别是用微信传输,大家应该能看到一个「发送原图」的选项,也就是说,我们发送的那个图片如果没有勾选这个选项,都是经过压缩的。
你把这个图像另保存在电脑里,会发现它不是原图格式,但是也可以看的比较清楚,虽然信息有一定的损失,大小大约是在200K 左右。从 4 兆到 200K,我们的信息其实只有百分之五左右,下降了大概 20倍。也就说他用了5%的信息,但是能还原出来绝大部分的一些内容。那你有没有好奇这个技术是怎么做到的?而且这个技术在图像里是个通用技术。
今天我就带着大家用 SVD做一版图像压缩的算法,看一看它能不能用很小的一些信息帮我们保存尽可能多的一些内容。
SVD它有一个价值就是把特征抽取出来,而且还把特征的权重从大到小做了个排序。我们可以通过中间S 这个矩阵,就可以在对角线上看到它。
我们现在的想法就是把一些不太想要的后面那些特征给它设置为0,只提一些关键特征。有了关键特征,我们可以把这些图像再去做一些还原,还原出来的这些图像跟原图之间做一个对比。
这次用的是一张街拍的生活照,是我从同事朋友圈 down的,希望她不会怪我。在拿到图像后我做了一些处理,将其处理为灰度,因为这次我们需要用到一个单通道的图像。
大家应该都理解通道的概念吧?JPG是我们最常见的格式,我们可以看到四个通道,R、G、B以及合成通道,其实严格意义上来说,一张 JPG只包含三个标准通道,R、G、B,三个通道任意关闭一个,合成通道都是无效的。那R、G、B 表示的就是红,绿,蓝三个颜色。
现在为了方便起见,我们不去做三个通道的图像,将图像转为灰度之后输出为BMP,8 位图。
我们来看原始图像
当然,这是我用 plt show出来的,并且大家还是自己去找图片去做测试,这张图就恕我不提供了。这是图片信息:
我们需要将图片先读取进来,然后使用 NumPy 将其转为矩阵赋值给 A。
1 |
|
然后我还做了个展示,看看提取出来的 A是否可以正常显示,显示结果就如我上面贴图一样。
1 |
|
有了原始图像以后,然后我们该做什么了?当然是拆矩阵对吧?要将这个图像矩阵拆成三块,P, lambda 和 Q:
1 |
|
接着,我们现在做法就是抽它的特征。这里,咱们写一个函数,用于从 S里面抽取几个关键特征。
1 |
|
接着,需要对这个函数进行补全,我们不仅希望它提取特征,对于提取后的特征还原一个temp
新矩阵,然后将它显示出来。
1 |
|
接着我们来传参调用函数,参数包括 s 和 k,s是代表了特征从大到小的一个顺序关系。我用前 5 个来进行提取和还原:
1 |
|
然后 50 个 1
get_image_feature(s, 50)
在接着是 500 个 1
get_image_feature(s, 500)
下面来给大家分析一下这个函数,首先,我们得到一个全 0 的 zeros
1 |
|
它把它所有的零都设置上,打印出来应该是这样一个矩阵:
1 |
|
然后又把这个 s 里的 0 到 k 给它还原出来, k 是我们传参传进来的。
1 |
|
这样我们可以想象一下,只有前面这几个值是有价值的,后面都为 0 了。
然后得到的这个有价值的矩阵 s_temp 乘上一个 identity,identity就是我们上节课讲到的单位矩阵,称之为 i,对角线为 1的矩阵叫做单位矩阵。
1 |
|
我们有了 s、p 和 q,p 和 q 是原来拆出来的内容,是不会发生变化的。这个s 乘上 p 得到一个值赋值给一个临时值 temp,再拿得到的结果和 q进行相乘,继续重新赋值 temp
1 |
|
这样我们就会近似还原一张图,接着要做的事情就是跟我们展示最开始的 A矩阵一样,将 temp 给展示出来就可以了:
1 |
|
我们回头去看看最后展示出来的那张图,就是 k=500的时候的那张图,前两个明显有差距我们不需要仔细看。k=500那张图仔细看,和原图还是有区别的。最明显的,阴影部分的深度没有那么大,对吧?那有可能是在输出色阶上范围低了一点,这部分特征是丢失了。
那么,矩阵对角线里面是有权重的,它的特征值个数一般是多少?这个要看向量的维度。我之前展示了这张图片的信息,我们知道这张图片现在向量应该是3840*2160,所以它的特征维度我们来猜一猜一般会是多少维?
这里全部的特征个数应该有一个规律,实际上是应该小于等于(3840,2160)的最小值,也就是小于等于 min(3840,2160)。也就是你长和宽里面的最小值要比它小,有可能就是2160,所以它的上限是 2160,最多有可能是 2000多维。通常情况下很有可能就是 2160。
现在 2,000 多维里面我们只取了 5 个维度, 我们脑海里过滤一下,5个维度会不会这个图像就花掉了?图像原本是由 2,000多个维度合并而成的,现在我们只要从大到小的前五个,事实也是如此。之前展示的k=5的图像确实是花的,能看出来原图是什么样子吗?前五个的信息量其实已经还挺大的,但是你用的数据太少,基本上是不可能的。你想5 除上 2,000,这只有多少的压缩空间啊?1%都不到。
再看一看 50 个维度,原来是 2160 个维度,现在变成了50,信息其实也是用的非常少。但是这 50个特征,已经能看出来原图的样子,有一点眉目了。这 50个特征应该基本上就能看出来原来图像的一个概念。所以我们用这个方式就可以很好的帮你来做还原。
50 个可以看到了,如果我们用更多的 500个,可以看到由原来很模糊到现在很清楚,这个差距其实是非常明显的。那以上就是SVD 的进行图片压缩的过程。
所以 SVD可以帮你来做一个降维的处理。这里的降维我们先说结论,可以用很少的信息,大概10%左右就可以还原大部分的一些信息内容,信息可以还原出来 90%左右。
那为什么是这样的一个比值呢?我们以刚才的 k 等于 50 为例,k 等于 50是怎么保存的。
1 |
|
我们的 m 乘上 k 是前面一个矩阵,对于后面那些都为 0的那些部分我们的存储空间是不需要存储的,因为它是乘法,是没有意义的。这是m 乘上k,中间这个部分的应该是单位的对角阵,单位对角阵的话现在应该大小应该是 1乘上 k。因为只要把它保存向量就好了,就像刚才我们看到那个 s是一样的。后面这个部分的应该就是 n,那就是 k 乘上 n,就是 2160 乘上 50这个信息,也就是 Q 里面只需要存2160*50
。
前面只需要存3840*50
,中间只需要存 50个,后面是2160*50
,我把所有的元素的个数都给它存出来,这些是你一定要存的信息,(3840+ 1 + 2160) * 50 = 300050,大概有 300050个元素。看起来虽然很多,但是相比原来这个信息量只有多少呢?我们来看看原图信息量:3840* 2160 = 8294400,我们做一下对比,300050/(3840 * 2160) = 0.036175...,大概是不到 4%的一个占比。
所以说我们其实可以只用了差不多 10%的信息可以还原出来差不多90%以上的信息。可以对比一下,以上就把一个图像的压缩的原理简单的给大家讲完了。背后使用的工具是SVD,SVD可以很好的帮我们来分析一个矩阵中哪些成分是关键的,哪些成分不是关键的。这样我们就可以对一个矩阵去提取它的关键特征来做一些还原。
这个例子中,我们是先用到了一个图像压缩领域中让你去了解如何提取图像中的关键特征,又如何把一些重要的top-k 特征做了一个近似还原。结论就是,10%的信息可以相当于90%的信息量。
我们来看,这个技术是不是感觉起来挺神奇的?那想象一下,我们在微信里面传的原始图像,原来4 兆多,虽然只用了200K,但同样可以得到很清晰的一个图像,就是以上的一个原理。
大家在课后,可以拉取我的代码去跑一跑,不过需要换成你们自己的图片了,这张图片就不提供了。大家可以自己去体验一下,写一个图像的压缩工具。
]]>[TOC]
Hi, 你好。我是茶桁。
之前咱们花了大概 5 节课的时间学习了推荐系统中的矩阵分解,ALS 算法以及SlopeOne等等。这些内容都属于协同过滤的内容,除了协同过滤之外,推荐系统还有另外一种方法,就是基于内容推荐。不过首先,咱们还是得把协同过滤讲完,当然依然还是矩阵分解,不过今天我们要讲的是SVD 矩阵分解。
想想,咱们之前课程中所讲的矩阵分解的内容是什么?
咱们讲解了,将一个大矩阵拆成两个小矩阵,然后用一种优化方式固定一个求解另一个,就是交替最小二乘,英文应该叫做ALS。
那今天讲的矩阵分解其实跟 ALS相比,最后也是一样的解法,一会咱们可以一起来看一下。这个方式也是关于协同过滤的,基于内容的推荐系统将会是之后几节课中要去学习的内容。
这些都是推荐系统里面很重要的一个基石,之后我也是会带着大家一起来做一些项目,通过一些工具来去实战推荐系统的内容。
此外,我们还要考量。因为推荐系统是一个非常工程化的东西,在工程上面需要考虑的问题就会很多,比如说泛化能力。因为工程中可能会有更多的特征,希望预测的更准,就需要有更多的特征要入到模型里面去,我们之前的矩阵分解考虑的特征维度可能并没有那么多,所以工程上面还会有更泛化的一种模型,这是一种考量的维度。
还有一个考量维度就是我们的计算设备,现在我们进行计算都是在本地计算机来进行计算的,当数据量比较大的时候你电脑的内存和CPU都会被占满,你就无法去进行其他的工作。所以我们今天也看一看如何去使用一些云端的在线编程的工具来去加速你的运算,尤其是当你遇到一些需要GPU 的环境的时候可以使用它来进行提速。
这两个都可以说是工程上面重要的内容。一个设备,一个是泛化能力,使用更多的一些特征,让模型的计算会更加的准确。
那我们对将要讲解的内容做一个分解,首先会先讲一下 SVD矩阵分解,然后会在更之后的课程中开始给大家讲到基于内容的推荐。咱们先来看看SVD的矩阵分解,看一看矩阵分解背后的原理,看看普通矩阵的矩阵分解,对称矩阵的矩阵分解。
咱们知道 SVD的中文叫做什么?就是前面跟大家说的,这个叫做奇异值分解,它的一个主要的领域是做降维的处理,把很多的维度抽出来几个主要特征。降维是一种思想,可以用到很多场景里面。后面的课程中也会给大家看一看一个图像的降维是如何来通过SVD 来进行压缩的,在推荐系统里面 SVD 又是怎样起到作用的?
我们在工具上面使用会用到 SVD 算法家族,包括FunkSVD,BiasSVD,SVD++,这 3个方法是在我们之前的课程中给大家介绍的一个工具家族里面,它应该也是关于scikit 这个家族,scikit 最常见的是它的机器学习的工具scikit-learn,也称之为 sklearn。
之前的课程中,给大家使用的一个推荐系统的工具箱,就是 scikit家族的,就是surprise。不知道有多少小伙伴看过之前的课程,还回忆的起来吗?
它还有一些工具今天会看到,就是我刚才提到的 SVD 算法家族。
在最开始的时候先对 SVD有一些认知,然后我就会带着大家一起来去调包,去求解它,最后还是用我们的MovieLens 来进行推荐。
以上的环节都是关于矩阵分解的,矩阵分解大家一定记住它是协同过滤的。还记得我们之前课程中给大家的一个树图吗?那个图中的结构要有概念。
不过咱们一节课的内容肯定是讲不完这么多,会分几节课将它们全部讲完。讲完这些之后,咱们再来看基于内容的推荐的相关内容,稍微晚一点的课程中会给大家讲到。
基于内容的推荐和协同过滤之间区别是什么?推荐系统里面两种主要的算法模型就是协同过滤和基于内容推荐,它是从数据本身的属性来去考量的。如果一个用户有了行为,我们就会把行为的数据作为一个矩阵来去做分解,就会用到协同过滤。如果这个用户没有行为,我们要用他的静态属性。
一个静态,一个动态,这些都是我们以前讲过的知识。如果大家忘记就回过头去好好看看前面的文章,把以前学过的知识做一个复习。
内容推荐系统怎么做?我们后面会带来一个酒店推荐系统的项目一起来去做一个搭建,抽取它的特征。当一个用户看了一个酒店以后即使他以前没有点击的行为,我同样可以给他做基于内容的推荐。
咱们先来回顾一下矩阵分解所处的位置:
基于内容的推荐和基于协同过滤的推荐这是两种主流的方式,矩阵分解是在协同过滤里面,同样是在基于模型的这个过程。
那我们还记得之前讲过的,基于模型是什么概念?我们把它称为叫基于模型,是因为我们要去建一个模,这样我们就需要用到机器学习,所以它会分成两个阶段:训练和预测。
先拿一个数据喂给它去拟合一些参数,拟合好参数以后就可以去做预测了。矩阵分解就是这个原理,我们把原来的这些数据作为训练集,未知的去作为待预测的结果值。所以它是在基于模型里面,同时它中间是基于隐特征latent factor。
最开始的矩阵分解实际上它并不是直接拆成两个矩阵,我们先看一看最基本的概念。
矩阵分解实际上是把一个大矩阵拆成多个矩阵的乘积,这是矩阵分解的概念。在矩阵分解中是有特征值的分解和奇异值的分解。
我们以前在大学期间可能学过一个线性代数的课程,这个课程里面我们会有一个矩阵,会把这个矩阵拆成特征值和特征向量的表达方法,称之为普分解。今天咱们不去推导具体的一些公式,只让大家知道有这样的一个原理。
那什么是特征值和特征向量?一个矩阵是具有一定的性质。N 维非零向量 v 是N 乘以 N 的矩阵 A 的特征向量,而且仅当下式成立:
\[\begin{align*}Av = \lambda v\end{align*}\]
A 就是这个矩阵,我们做一个 v,v这里代表的是一个向量,那么拿这个向量去作用一个矩阵就相当于对这个向量做了一个拉伸的操作。lambda(\(\lambda\))代表的就是我们的特征值,称为标量。v 为特征值 lambda 对应的特征向量。
那咱们知道什么是标量什么是向量的概念吗?
标量是一个单独的数值,它只有大小,没有方向。在数学和物理学中,常见的标量包括时间、温度、质量等。标量通常用来描述物理量的大小。
向量是具有大小和方向的量。向量可以用来表示空间中的位置、速度、力等。它由一组有序数值组成,并且可以在空间中表示为从一个点指向另一个点的箭头。向量的常见表示方式包括列向量和行向量。
对于一个线性变换或矩阵,特征值是一个标量,表示在某个方向上的伸缩比例。特征值描述了变换或矩阵对应的特定方向上的影响程度。特征值是通过解决矩阵的特征方程来计算的。
对于一个线性变换或矩阵,特征向量是与特征值相关联的向量,表示在特征值对应的方向上的不变方向。特征向量在进行特征值分解时起着重要作用,它们描述了线性变换或矩阵在不同方向上的不变性。
特征向量是与特征值相关联的向量。特征向量描述了线性变换或矩阵在特定方向上的不变性,即在这些方向上的伸缩和旋转。特征值则表示了在对应特征向量方向上的伸缩比例。
在特征值分解中,特征向量和特征值是一对一对应的。特征向量决定了矩阵变换的方向,而特征值决定了变换在这些方向上的比例。
我们要去求解这个过程, 回忆一下,以前大概是要建一个特征的多项式。
\[\begin{align*}|A-\lambda I|=0\end{align*}\]
想求这样的一个解等于 0 的特征多项式, 令
特征多项式是关于 lambda 的 N 次多项式,特征方程有 N 个解。
\(|A-\lambda I|\)这个值还可以再拆出来,对多项式
\[\begin{align*}p(\lambda) = (\lambda - \lambda_1)^{n_1}(\lambda -\lambda_2)^{n_2}...(\lambda - \lambda_k)^{n_k} = 0\end{align*}\]
其中
\[\begin{align*}\sum_{i=1}^k n_i = N\end{align*}\]
拆的过程实际上是个因式分解的过程,当这一串等于0,就可以把一个特征值
\[\begin{align*}(A-\lambda_i I)v = 0\end{align*}\]
这就是特征值的求法。
说起来有点抽象,我们来看一看在数学的工具里面是如何来去求解特征值和特征向量的,以一个A 为例:
\[\begin{align*}A = \begin{bmatrix} 4 & 2 & -5 \\ 6 & 4 & -9 \\ 5 &3 & -7\end{bmatrix}\end{align*}\]
这是一个原始的矩阵,要想求特征值和特征向量是通过构造一个特征方程来去完成的。这个特征方程你可以用A 减去 lambda I,也可以把它倒过来,lambda I 减去 A
\[\begin{align*}|\lambda I - A | = \begin{bmatrix} \lambda - 4 & -2 & 5 \\ -6& \lambda - 4 & 9 \\ -5 & -3 & \lambda + 7\end{bmatrix}= 0\end{align*}\]
这里的 I 应该就是一个单位的对角阵,对角阵大概就是对角线为1,其他地方为 0。
\[\begin{align*}p(\lambda):=|\lambda I - A| = \lambda ^2 \cdot (\lambda -1)\end{align*}\]
所以 lambda I 减去 A 等于 0,它通过一些行列式的组合可以得出来。
\[\begin{align*}& \lambda ^2 \cdot (\lambda - 1) = 0 \\求解得: & \lambda_1 = 1, \lambda_2 = \lambda_3 = 0\end{align*}\]
总之,我们通过特征方程是可以求出来特征值的,然后又通过特征值代入进去也可以把特征的向量求出来。
\[\begin{align*}& 当 \lambda_1 = 1, (\lambda_1 I - A) = \begin{bmatrix} - 3 & -2& 5 \\ -6 & -3 & 9 \\ -5 & -3 & 8\end{bmatrix} \\& 简化得到: \begin{bmatrix} 1 & 0 & -1 \\0 & 1 & -1\\ 0 & 0 & 0\end{bmatrix} \\& 所以 (E-A)x = \begin{bmatrix} 1 & 0 & -1 \\0 & 1 &-1 \\ 0 & 0 & 0\end{bmatrix}\begin{bmatrix} x_1 \\ x_2 \\x_3\end{bmatrix} = 0 \\& 即: \begin{cases} x_1 - x_3 = 0 \\ x_2 - x_3 = 0 \end{cases}, 令x_1 = 1, 得到特征矩阵 \varsigma_1 = \begin{bmatrix} 1 \\ 1 \\1\end{bmatrix} \\& 同理, 当 \lambda_2 = \lambda_3 = 0, 计算可得特征矩阵 \\& \varsigma_2 = \varsigma_3 = \begin{bmatrix} 1 \\ 3 \\2\end{bmatrix}\end{align*}\]
可能大家以前应该学过一门课,在大学期间有学过线性代数。那如果你没学过或者遗忘了,可以回头去看看我之前给大家写的基础数学篇,那里对人工智能所要用到的数学知识都有详细的给大家讲了一遍:
不过我们其实不用自己去做,实际上在计算机里面有很好的解决方式,稍后给大家看一看在NumPy 里就有一个工具,可以直接把矩阵的特征值和特征向量依次进行求解。
如果你是一个方阵,比方说 A 是 N 乘 N维的方阵。方阵是什么概念,就是它的维度,宽和长都是相等的。对矩阵 A进行特征分解,那么我们对它做分解就可以把它拆成这样的矩阵:
\[\begin{align*}A = U\Lambda U^{-1}\end{align*}\]
U 的列向量是 A 的特征向量,
\[\begin{align*}& A = \begin{vmatrix} 5 & 3 \\ 1 & 1 \end{vmatrix} \\& \begin{bmatrix} 5 & 3 \\ 1 & 1 \end{bmatrix}= \begin{bmatrix} 0.97760877 & -0.54247681 \\ 0.21043072 &0.840007078 \end{bmatrix}\begin{bmatrix} 5.64575131 & 0 \\ 0 &0.35424869 \end{bmatrix}\begin{bmatrix} 0.97760877 & -0.54247681 \\0.21043072 & 0.840007078 \end{bmatrix}^{-1}\end{align*}\]
中间是它的对角阵,是一个元素的特征值。
有时候我们可以把它拆成两个部分,这是一个方阵的一个概念。
特征值 5.64575131 对应的特征向量为[0.97760877 0.21043072],特征值0.35424869 对应的特征向量为[-0.542476810.840007078],特征向量之间一定线性无关。
假设我们是有线性相关,那最后这个特征向量还会存在吗?应该就不会存在了,它一定会通过一些线性的方程把它消掉。所以我们得到的这些向量都是线性无关的一个逻辑。
来看一看在计算机里面是怎么样去求解一个矩阵的特征值和特征向量的:
1 |
|
刚才我们提到通过 NumPy,NumPy 是 Python里面科学的计算工具,它里面也包含了矩阵的计算方法,我们先通过np.array
创建了一个2 乘以 2 的一个数组,然后把这个 A 进行一个分解,分解成 Lambda 和U,Lambda 和 U 就直接可以求出来。Lambda 是它的特征值,U是它的特征向量。那我们执行之后的结果就如打印结果一致。
上一节是对一个普通的方阵进行分解。如果存在一个对称方阵,对称方阵就是它不光是方阵而且它还存在对称性,上三角和下三角是对称的。
以这个方阵为例:
\[\begin{align*}A = \begin{vmatrix} 5 & 1 \\ 1 & 1\end{vmatrix}\end{align*}\]
你看它就是个对称的,除了对角线以外两边是对称的一个结构。如果是个对称方阵就会存在一个性质,就是:
\[\begin{align*}U^T = U^{-1}\end{align*}\]
\(U^T\) 这里的 T 代表什么概念?在写Python 过程中我们实际上也有这样 T的一种写法,就是把一个矩阵给它颠倒一下,倒过来,它是一个转置的概念。
如果等于它的转置,我们就可以把 A 写成以下的一个公式:
\[\begin{align*}A = U\Lambda U^{T}\end{align*}\]
U 的列向量是 A 的特征向量,
\(U^T\)这里就代表是一个转置的概念,所以具体来去看向量和向量之间,之前一定是线性无关的,最后我们还发现有个神奇的特征,不仅仅线性无关,而且还是一个正交的性质。
\[\begin{align*}& A = \begin{vmatrix} 5 & 1 \\ 1 & 1 \end{vmatrix} \\& \begin{bmatrix} 5 & 1 \\ 1 & 1 \end{bmatrix} =\begin{bmatrix} 0.97324899 & -0.22975292 \\ 0.22975292 &0.97324899 \end{bmatrix}\begin{bmatrix} 5.23606798 & 0 \\ 0 &0.76393202 \end{bmatrix}\begin{bmatrix} 0.97324899 & 0.22975292 \\-0.22975292 & 0.97324899 \end{bmatrix}^{-1}\end{align*}\]
在这个过程中,特征值 5.23606798 对应的特征向量为[0. 973248990.22975292], 特征值 0.76393202 对应的特征向量为[-0.22975292 0.97324899]
我们把两个向量去做一个乘法,最后做一个相加
\[\begin{align*}0.97324899 * -0.22975292 + 0.22975292 * 0.97324899 = 0\end{align*}\]
咱们来看,他们的角度是等于多少度? 如果等于 0这两个应该是等于多少度?我们把这两个向量做一个对位相乘,相乘以后再做一个加法最后你发现它等于零,这里应该就是90 度。
所以这两个向量它不光是线性无关的,而且它还是个正交的向量。那代码上和之前的并没什么区别,就是矩阵换了一下,大家可以自己写一写。
我们来看矩阵的计算过程:
\[\begin{align*}A = \lambda_1u_1u_1^T + \lambda_2u_2u_2^T\end{align*}\]
最终一个对称方阵如果它有两个 Lambda,就是两个特征值,就可以由我们的U1 和 U1 的转置加上 U2 乘上 U2的转置,这样我们就会把一个矩阵拆成两块,做了一个组成。就是一个矩阵等于两个部分的相加,就像上面这个式子。
lambda1 和 lambda2 拆出来的是 U1 和 U1 的转置,U2 和 U2的转置。把它相加以后发现我们可以把一个矩阵进行还原,所以一个大的矩阵是可以拆成多个组成部分。
\[\begin{align*}5.23606798 * \begin{bmatrix} 0.97324899 \\ 0.22975292\end{bmatrix}[0.97324899 0.22975292] + 0.76393202 * \begin{bmatrix}-0.22975292 \\ 0.97324899 \end{bmatrix}[-0.22975292 0.97324899] =\begin{bmatrix} 5 & 1 \\ 1 & 1 \end{bmatrix}\end{align*}\]
可以通过它来进行还原,这个性质是对称矩阵的一种性质。
前面说了这么多的一些铺垫,其实想要解决的是一个普通矩阵的问题,我们要面临的问题就是,一个矩阵多数情况下是对称的吗?对于任何的矩阵来说,它很有可能不是对称的,此外它很有可能不是方阵。那前面这么好的性质怎么办?不利用就会可惜了。所以我们要构造一下。
对于任何的一个矩阵如果它的维度是 m 乘以n,有没有可能去构造一个对称方阵。用 A 乘上 A 的转置:
如果 A 的维度是 (m, n),那 A的转置的维度等于多少咱们应该能清楚吧?转置就是把它颠倒,所以它的维度应该是(n,m),这两个矩阵如果做个乘法,乘完以后它的维度会是等于多少?
矩阵的乘法有个前提条件,就是它中间这两项一定是相等的:
再换个角度,A 的转置乘上 A,那前面就应该是(n, m),后面是(m,n)这两个维度,那相乘之后就会变成(n, n)的维度。
所以它们两个都是一个方阵,你看这个是不是很巧妙?通过构造了两个矩阵的相乘,就得到了两个方阵。所以在计算过程中就可以巧妙的运用到以前的一个性质。
因为 \(AA^T\) 与
\[\begin{align*}AA^T = P\Lambda_1P^T\end{align*}\]
对于对称方阵我们前面刚才给大家说了一个定理,这个是存在的一个数学定理。那我们可以把P 看成是一个 lambda, 对于前面这个 lambda 我们把它看成是 P,那么 P乘上它的对角矩阵\(\lambda_1\),再乘上 P的转置。
下面的式子中,我们把 lambda 看成 Q, 然后乘上
那这个特征值实际上是被乘过了两次,A 乘上 A的转置,里面都有一个相同的特征值被乘上了两遍,所以是可以把它的特征值依次来进行求解。
假设这些特征值为\(\sigma_1, \sigma_2, ...,\sigma_k\), k 不超过 m 和 n, 也就是 k <= min(m, n)。此时矩阵 A的特征值
\[\begin{align*}\lambda_1 = \sqrt \sigma_1, \lambda_2 = \sqrt \sigma_2, ..., \lambda_k =\sqrt \sigma_k\end{align*}\]
如果前面这个矩阵它的特征值是
\[\begin{align*}A =P\Lambda Q^T\end{align*}\]
P 我们把它称之为左奇异矩阵,它的维度是(m, m);Q是右奇异矩阵,它的维度是(n,n)。你可以简单的去做一个还原,可以看一看对于一个 A 它是(m,n)的一个维度,如果你的 P 就是(m, m),中间这个应该是(m,n),那你把它乘一下,把中间的 m 消掉,就变成(m, n),后面 Q 是(n,n),再继续相乘,它就是等于(m,n),我做了一个示例给大家,能更直观一些:
基于刚才的一个原理,我们可以把中间的这个特征值
特征值的概念怎么去理解,一般我们把特征值可以看成是一个权重的概念,就是在某一个特征上面它是明显的还是不明显的,如果权重越大就是比较明显的。
前面的 P 算一个特征,Q 算个特征,中间的 lambda算是一个权重,代入到推荐系统的场景,P这个左奇异矩阵可以称之为用户矩阵,user 矩阵,
以之前我们讲解的推荐系统为例,我们之前举了一个 12 个用户和 9个电影之间的交互行为。所有的数据都是保存的用户和商品之间的打分的情况,最后抽取出来的P 可以把它理解成为是对于用户的向量表达,而后面这个 Q就是对于商品的向量表达。这个我们就会把它作为特征的提取,相当于是用户的特征和商品的特征,而我们中间的部分就是个权重的概念。这就是作者巧妙的把一个矩阵的分解带入到推荐系统里面,得到了它相关的一些对应的关系。
上面这些就是咱们数学的一些基础,可能说起来稍微有些抽象。看不太明白的同学可以回头去看看我为大家写的
对任何的矩阵,如果你自己去做,不用Python,不用代码来做应该是怎样的。
比如,
\[\begin{align*}A = \begin{bmatrix} 1 & 2 \\ 1 & 1 \\ 0 & 0 \end{bmatrix}\end{align*}\]
其实逻辑应该是用 A 乘上
\[\begin{align*}AA^T & = \begin{bmatrix} 5 & 3 & 0 \\ 3 & 2 & 0 \\ 0& 0 & 0 \end{bmatrix} \\A^TA & = \begin{bmatrix} 2 & 3 \\ 3 & 5 \end{bmatrix}\end{align*}\]
上面一个方阵特征向量 P,
\[P = \begin{bmatrix} 0.85065081 & -0.52573111 & 0 \\ 0.52573111& 0.85065081 & 0 \\ 0 & 0 & 1 \end{bmatrix}\]
特征值 Lambda
\[\sigma_1=6.85410197, \sigma_2 = 0.14589803, \sigma_3 = 0\]
然后后面的方阵特征向量 Q 的转置:
\[Q^T = \begin{bmatrix} -0.52573111 & -0.85065081 \\ -0.85065081& 0.52573111 \end{bmatrix}\]
特征值
\[\sigma_1 = 6.85410197, \sigma_2 = 0.14589803\]
那我们就可以将中间
\[\lambda_1 = \sqrt \sigma_1 = 2.61803399, \\\lambda_2 = \sqrt \sigma_2 = 0.38196601\]
然后咱们代入进去,得出来就是这样的一个解:
\[P\Lambda Q^T = \begin{bmatrix} 0.85065081 & -0.52573111 & 0 \\0.52573111 & 0.85065081 & 0 \\ 0 & 0 & 1\end{bmatrix}\begin{bmatrix} \lambda_1 & 0 \\ 0 & \lambda_2 \\ 0& 0 \end{bmatrix} \begin{bmatrix} -0.52573111 & -0.85065081 \\-0.85065081 & 0.52573111 \end{bmatrix} = \begin{bmatrix} 1 & 2\\ 1 & 1 \\ 0 & 0 \end{bmatrix}\]
那这就是我们如何去自己人工来做拆解的过程。
其实在计算机里面还是有很多工具可以帮我们去计算的。在前面得到一个性质,它最终的一个结论是这样的一个结论:
\[\begin{align*}& 奇异值分解 \\A & = \lambda_1p_1q_1^T + \lambda_2p_2q_2^T + ... +\lambda_kp_kq_k^T\end{align*}\]
任何的矩阵都可以有 k 个组成部分,就是把 k个维度相加就等于一个矩阵的还原。这 k个维度是它对角线的一个权重特征,lambda1,lambda2 一直到 lambdak,它有 k个部分,每个部分的相乘都是等于 P1 和 Q1 的转置。
实际上 P 和 Q这两个部分你可以把它理解成用户向量和商品向量的一个转置的乘积,前面的Lambda代表它的特征的大小。我们把这些值做一个累加,就等于一个原始的一个矩阵。
其实真正使用起来可以直接套用 Python 代码
1 |
|
A 是一个矩阵,我们套用的方法是 SVD,SVD就是奇异值的矩阵分解。通常我们说到矩阵分解的时候,如果去看招聘GD,里面的一些关键词或者在网上也介绍矩阵分解,基本上都会提到一个 SVD的概念。
那我们这就把 SVD 背后的原理简单给大家说完了,就是如何把一个矩阵拆成 3块。SVD它背后是个数学工具,这个工具就是拆矩阵的工具。我们现在可以知道它的作用,数学推导不是我们必修的,只是跟大家说它背后有这套逻辑,真正要使用就可以用一个Python 的工具箱。
代入进去以后就拆成了三个矩阵,P、S 和 Q,给它任何一个矩阵都可以拆成 3块。那大家可以自己拿代码去跑一下。咱们之前讲的时候,中间的lambda,我这里是用 S 来做了一个替代。
运行后的结果,P 是个矩阵,Q 是个矩阵,S,也就是 lambda是它的一个权重。它的权重已经默认帮我们从从大到小做了个排序,那就说它前面这个分量是最主要的特征,是2.61 的权重值。
好刚才讲了这么多,那到底能产生怎样作用,或者说 SVD怎么用,对我们有怎样的价值?矩阵其实是做运算的一个根基,那下一节课,咱们就来做一个图片压缩的例子,来更好的理解一下。
]]>[TOC]
Hi,你好。我是茶桁。
上节课给大家预告了,今天这节课咱们来看一篇论文。我们之前几节课中讲解的内容其实是在一些论文里面有使用到的。
我们先看一下论文的内容,讲讲 ALS。
就像我上节课结束的时候说过的,阅读论文也是一种能力。如果你想要未来获取一些新的方法,最直接的方式就看论文。怎么去阅读呢?今天详细给大家做一些分享。
咱们今天看的这些论文,都可以在这个课程的代码库里去找到。
先来看一下「Large-scale Parallel Collaborative Filtering for theNetflix Prize」这篇论文,就是咱们课上讲过的。
一般论文怎么看呢?我相信基本上大家都写过论文,只要是大学毕业的,最后都需要进行论文答辩,不过不知道有多少人发表过论文。有些学校可能会有要求在学校里面发表一些论文。
如果写过论文的可以知道,论文的结构一般看哪呢?先看前面这个Abstract,这是一个综述。这里会非常浓缩的说明这篇论文告诉我们什么样的事情,什么样的一个结论,什么样的方法。后面就像八股文一样分层次的给你展示不同的一些模块。
「Large-scale Parallel Collaborative Filtering for the NetflixPrize」这篇论文作者是求了大规模的并行的协同过滤的方式来去求解 Netflixprize。这是百万美金奖励的那个内容。
今天看到这篇论文是在十几年前,06 年推荐系统刚刚开始的时候一个公司Netflix 拿出来 100 万的美金奖赏的解决方案,如果你的解决方案提升10%,第一名的人就会奖赏 100万美金。为什么能花这么多的钱,因为它能给你带来价值远高于 100万的价值。你想推荐的效果提升了10%,用户更满意,粘性会更大。另外因为他是租赁DVD,所以他的销售额肯定也会上升。
阿里的推荐系统里面的文章会告诉你他通过推荐系统可以让你的GMV,整个的销售额提升多少。
它在 Abstract 里面告诉我们,它就使用了ALS:Alternating-Least-Squares,然后又会加入了Weighted-λ-Regularization,这就是加入了一个正则化项,做了一个工程的约束。
那在 Abstract 里面它告诉我们说它提升了将近 6%,5.91 个百分点。相比于Netflix 的 CineMatch这个推荐系统会有一些优化。方法是比较简单的,而且比较适合大规模的一些计算,这是整个的一个背景。
论文的结构前面会有个Introduction,它会告诉你整个项目的背景,从这里可以看到内容推荐系统和协同过滤的推荐系统是两大主流的方向,
上面作者也提到 Netflix 自己做的这个推荐系统 CineMatch,它的 RMSE 是0.9514,论文的方法得到的 RMSE 呢是 0.8985。RMSE是越大越好还是越小越好?应该是越小越好,所以他比原来小了 5.91%。当你k=1,000 的时候,k 越大,计算量越大,但是他的精度就会越好。
基于内容推荐刚才也提到,就是这些概念在论文里面前期也会有写到。
然后告诉我们 Netflix 有 48 万个用户,将近 18,000 部电影,超过 1亿的评分,数据量还是非常大的,尤其在 06 年左右。
Netflix 的目标是提升10%,那大家如果是从我仓库里下载的文档,里面是有一些笔记的。建议大家平时看论文的时候最好也能做一些笔记。
挑战有几点,第一个数据量非常大,是原来的 100 倍,因为整个有 1亿的电影评分,确实很大。数据也很稀疏,告诉我们通过数据分析,前期数据探索,用户和商品之间的评论矩阵只有1% 的数据是有的,非常稀疏。
同时数据也有噪音。为什么有噪音?因为时间跨度很大,1995 年到 2005年,影响评分的人 10年之间可能角色也会发生一些变化。之前是学生现在可能已经工作一段时间,有可能结婚了。所以感情或者说大家的状态也会发生不一样的变化,那这里的行为会有一些不太一样的情况。
当时这个场景的训练集和测试集还分布不太一样,比赛的评估是 2006年的数据,而训练给你的 1995 年~2005 年。这也是一个背景。
第二个部分就是问题的描述定义。在问题描述定义里面先去做了一个 lossfunction,这是 LR 的 MSE。
r 减去预测的结果的平方和。
然后有 n 个样本的就除上 n
除了 loss function的定义以外,后面作者还加了一个正则化项。正则化项的目的我们在课上给大家讲解了。
前面是问题的一个定义和抽象。第三个是提出来的方法。
提出来这个方法叫做 Weighted-lambda-Regularization,ALS的方式。怎么做呢?下面就会有详细的定义,在之前课程里也给大家说过。
先初始一个矩阵,然后预测两个结果。这里的结果是 m 和 u。m把它理解成是电影的矩阵,u 是用户。那么就是固定一个求解另一个,这是ALS。固定 m 求 u,再固定 u 求 m,不断地去重复。
这是他提出来的 ALS 的这个方法,u 和 m 不断去优化。
这是在中间得出来的过程,ALS就是交替最小二乘,每一次都是用的最小二乘法想要去求解这个参数,是按照导数等于0 的情况下,可以把这个极值求出来。所以 u 可以求出来,同样 m也能求出来,因为这两个是一个对称性的结构。
上面就把整个过程推导在论文里面写得比较清楚了,后面就要进行一些计算。当时06 年,那阵还没有用太多的 Spark这些工具,其实后面这个工具逐渐就兴起来了。在早期他还用的是一个MATLAB,仿真的一些实验的工具。不过在 06 年的 MATLAB就已经可以实现并行化的处理了。
所以作者在后面也详细的说明了他当时的一些实验环境。首先使用并行化的处理它的好处就是比单核计算机内存会更大,计算效率会更快一些。
这是他当时的过程,看一看这个结果,文章里面其实写的还是非常完善的,把当时的实验的结果也会给你进行说明。他告诉我们,如果你要用单台计算机要花2.5 小时可以把 m 和 u 求出来,但如果你要用一个 30 核,相当于是并行处理这30 个计算机,就变成了 5 分钟。
由 2.5 小时变成 5 分钟,大概 30 倍左右。所以 30倍左右跟我们的并行计算是密不可分的,这是为什么我们在大规模的数据量级过程中可能会用到一些计算引擎。就像Spark 一样,Spark 里面默认集成的就是 ALS这个方法。因为数量集会非常多,那么采用多核,多台机器一起来进行分布式的计算可以大大提升我们的计算的效率。这是在作者在这篇文章里面给你进行了一个说明。
他在实验过程中采用的型号,CPU、内存的大小都会有相关的一个说明。同时也会把实验结果会告诉我们,
比如说实验结果他会有画一些图,中间实验参数有一些是超参数。有哪些呢?像iteration 这种是超参数。我们可以调节不同的一些 iteration,随着 iteration迭代次数的增加 RMS 是逐渐下降的。我们可以调节不同的 lambda,lambda代表的就是你的正则化项的系数,从 0.03 到 0.065每个过程中看一看哪一個正则化项的系数结果会更好。所以我们就直接把这个loss 的曲线画出来了。方便你去看出来这个结果。
比如说最好的这个是 25 轮,25 轮里面大家都比较容易收敛,lambda 呢等于0.065,0.065 是最下面的,这是 RMSE最小的,他把求参的过程也告诉你了。
还有就是参数 k,叫 hidden featur,隐分类,这个个数等于几也会写出来等等。
作者在论文里面也详细告诉我们 k 等于 1,000,达到了一个最好值0.8985。那么 k 等于 1,000 是这 1,000 个 feature,其实后面从 400 到500,这些 feature 呢它只增加了万分之六,所以增加的幅度还是非常有限的。从500 到 1,000 只增加了万分之十五。就说当 k越大肯定是有帮助的,但是到后面其实帮助已经越来越小了,所以在 k 等 1,000以后就没有必要再去做一些拟合了。
单模型,一个模型 RMSE 呢就可以达到了 0.8985,提升了 5.56的一个百分点,这是对比 Netflix 的 CineMatch。
此外作者在实验的这个过程中还告诉我们它除了这种模型以外还用了另外的两种模型,加到一起提升的效果会更好一点。哪些模型也在论文里面写到了
有 ALS,我们课上讲过的,KNN邻近的方法,RBM,三种模型累加一起的话可以一共提升了 5.91的百分点。这是他整个过程,最后做了一些展望。
在展望里面他也说明了主流的推荐系统就是有内容推荐和基于协同过滤的推荐。他们采用这种方法是基于协同过滤的一个推荐。
另外提出来的 ALS-WR这种方法,即使你的隐分类特征很多也不会容易过拟合。你原来 k多的情况下其实容易过拟合的,因为模型参数量会非常大。不容易过拟合是因为你加了一个正则化项,所以我们去掉的是那些抖动很大的系数,保留下来的应该都是那些可以让你的k 值很高,但是每次学出来的这个参数都不会抖得很剧烈。
最后也能看出来比较常见的是混合策略。模型融合应该是在比赛中比较常见的策略,如果你想要在工程上面得到很好结果的话也可以用模型融合。你看在06 年作者去打这个比赛的时候就用了三种模型,所以模型融合是一种标配。
没用可以在课后自己用一用,如果以前做过一些预测作业的话都会采用模型融合。那这篇文章也会告诉你模型融合是可以提升分数,他就明显使用三种模型是5.91,一种模型是 5.96,这还是可以提升的。
另外他也告诉我们已经进入到互联网时代了,数据量越来越大,传统单机模式已经不太适合了。如果你做的是个工程版本的话需要并型处理,只不过06 年他还用 MATLAB,而在后面 Spark 用的人也会很多。
那这篇论文中可以看到很详细的过程,从算法的背景,为什么要做这件事开始,再到这件事其他的人是怎么做的,然后问题的定义怎么定义的,推导怎么推导的,实验的环境用的30核的处理器。而且这个环境也做了两手对比,分布式的方式和单台计算机的时长,由150 分钟到了 5 分钟。以及中间的调参的曲线。
所以看论文其实还是一个接受信息非常完善的一个维度,我们课上主要讲的ALS-WR这个方法实际上论文里面会包含更多的一些内容的信息点。它可以让我们了解出来你想要知道的,关于这篇论文的一个详细的过程。
那咱们也稍微介绍一下读一个论文的技巧。我不知道未来有多少小伙伴是想要从事算法工作,论文是直接方便你去了解一手的算法的。像人工智能算法优化的速度很快,每年都有大量的论文产生,你现在使用的这个方法可能过两年就不是主流了。想要获取信息怎么弄?如果你想要第一时间获取的话,阅读论文就是一个捷径。因为论文里面有很详细的过程的一个描述。
那阅读论文也不是一个很轻松的事,他也是一个痛苦和收获并存的过程。因为你可能会觉得调包不是很简单吗,一句话就OK 了。但是看论文文字太多。这是一个问题。
还有什么问题?论文一般我们看的话英文更常见一点,因为那些比较好的期刊英文确实会更多一点。另外如果你想要发表出来一个成果,这个成果还很有影响力,基本上我们也会发到英文期刊。中国人也是一样,基本上应该都会发英文的。
那对于我们来说如果你看不懂,有一些是英文看不懂或者有些公式看不懂也很容易放弃,这个都是一些痛苦的过程。但是好的地方,一篇论文它的营养很高的,一篇论文产生需要3-6个月的时间。这个很正常,因为你需要做实验,实验做完你还需要调参,那个实验过程的话3-6 个月其实还是很正常的。
除了这 3-6个月以外,我们去投稿,投出来审稿人给你反馈意见也可能需要这么长的时间。每一轮的反馈都需要你的修改,所以你看到这个成品是他大量的时间的一个积累。
论文的话分成 EI 和 SCI。国内的人发表一篇 SCI 需要 1.8年,其实这个时间会更长,有些快有些慢,平均下来的话大概是 1.8年左右的时间。SCI 真正要发一篇这个时间年限还是有点长的。
你看到一篇论文的话都是别人已经花了大量的时间整理好的一个成果摆在你眼前。那么代码你会觉得很有快感,写完了一个代码运行起来很有成就感。
代码会给你一些交付感,那 paper会给你更加坚定的基础,你更加知道这个方法的原理,调包的时候会更加的自信。所以未来要想从事算法研究工作,我这里指的不是调包工作,调包工作你没有必要去看论文。想要有更好的提升,PaperReading 是必经之路。
那看论文用怎样的一些方法呢?给大家一些建议。第一,如果你去看一个新领域,你的工作有个新的方向要看这个新领域的话先看Survey。Survey代表的就是综述性的文章,综述性的文章就像一个导游一样,给你介绍的会很详细。
他会告诉你最新的研究进展(state of theart),在这个领域里面最好的技术模型是什么,哪些是奠基性的一个文章。比如说推荐系统,就把它分成了内容推荐和协同过滤推荐。现在大家使用哪些方法,还有就是告诉你有哪些是值得研究的地方,未来你要做研究工作可以往哪去做,哪些技术都比较成熟可以直接使用。
survey就像是一个导游一样,对一个新进入这个领域去看,非常建议大家去看这样一个综述性的文章。
第二,我们有两类类型的文章,一种是会议,一种是期刊。一般来说会议的效率会更高,因为会议的时效性会更强,此外它的应用价值也会更强。
期刊就像写书,写专著是一样的,它的周期也会比较长,所以它的文章有的时候几十页也很正常。而会议一般5-8页,不会特别多。所以建议大家先关注一些国际会议,这国际会议的信息更新速度会比较快。
第三,有人希望不光是要看原理,还希望能仿真,能去实验。那怎么去拿到代码?也教大家一个方法,一般来说我们都有一些通讯作者,在文章里面都会有一些邮箱,你可以直接给作者写文件,也可以去看这个作者,如果是大学的教授一般都有homepage。你在 homepage 里面去找他的邮箱,给作者去发email。就说我对您的研究工作很感兴趣,希望能做一些仿真,能去在这个基础上来进一步来去做一些研究、开发和使用你的包。
那这里面有些人可能会回你,有些人可能不回你。咱们来猜一猜,国外的人回的概率大还是国内的人回的概率大?国外他也会把一些教授放到homepage,上面会有些邮箱。
你想想如果是你,举个例子,你辛辛苦苦花了半年的时间写了一篇论文,又花了半年的时间把它发表出来。中间改来改去,整体上又花了一年的时间。那这阵就有个人写邮件说你的论文不错,但是我可能没有时间看,能不能把你的代码发给我我直接跑,我直接用。那这种过程中你会给吗?
这里其实就会遇到一些问题,现在国内的这种分享竞争越来越强了,以前的话绝大部分还是国外会稍微好一点点,你从他的homepage上可以看到前面可能会把文章会放上来,后面的话就直接给你个代码。愿意开源的都是菩萨。
国外相对来说这种开源精神会比较强,其实国外他也不傻,为什么?因为第一,国外讲究版权,我给了你的这个代码,如果你要进行了二次修改发布在发表新论文过程中会不会引用我呀?你看了我的论文,用了我的代码,在使用发表论文过程中如果不引用就会就会成为剽窃。所以他一定会引用,国外作者看中的是影响力。
那国外作者说我们希望你能在我的基础上来进行改进,这样的话我的影响力会很大。因为大家都引用我,我就会变成这个领域里的权威。所以大家用你越多其实证明你的影响力越大,这是国外的一个开源精神。
那国内其实这两年已经越来越好了,我看到了有很多的人也愿意去分享。越是大牛其实他就越愿意去分享,而且越愿意分享他就越容易成为大牛。没有一个大牛不分享代码能成为大牛的。所以我们可以去找这样的一些牛人去要一些代码,这个是没有问题的。
还有,看论文怎么看?论文太多了,也不建议大家所有论文都采用同样的功力来去看。我们把论文的阅读方式分为以下几种:
看论文不要追求每一个细节都懂,那这个真的是自己跟自己作对。那上大学的同学应该明白,在毕业的时候一年要看多少篇论文?这个论文是非常非常多的。如果你要把它完全都搞懂,其实你不可能看这么多论文。所以先一次性读完,一定会有很多不明白的地方,先把它标记下来。要分层的去做阅读,对于那些经典性的一些文章,很有营养性的文章可以反复读。对于那些大体上知道一下的文章,就找自己感兴趣的内容就可以了。
所以粗读是不用一次都要读明白,你可以多反复读几遍。
精读的部分就是要读到自己理解为止,尤其是那些重要的地方。举个例子,比如说矩阵分解或者说ALS的这个过程,你如果想要在这个基础上去做一些研究的话那你肯定要把这个过程的原理反复的去消化,去理解。所以这里也是要跟大家说,我的文章的内容也不要指望一次性就能完全消化掉,特别对于一些初学者来说,反复的吸收和练习。就是你的训练可以基于这些部分做一些加权的处理。
第三是跳读,不需要把每个部分都要读,甚至你可能跳过,直奔自己的主题。比如我们课程中给大家去用的这个工具,surprise。
surprise是一个工具箱,这是一个它文档地址:https://surprise.readthedocs.io/en/stable/,就是操作手册。
比如我们在这里面搜一下 baselineOnly这个方法,搜索结果就会给你这个方法有哪些,我们点进去可以看到每篇方法都有一个文章,因为算法它有可能会发表一篇文章,会对应它。他会直接告诉你,你直接读到2.1 的 section 就好了。
那么你也不一定非要把所有的文章都看完,只要 2.1 的 section里面你能把它消化理解,能告诉你他的原理就可以,没有必要去看他以前的背景,中间的实验结果,后续的展望。这些是论文的一个八股文的一个结构。你只要知道他中间一个定义,是怎么去定义的就OK了。所以跳读就是直接奔自己的主题,找自己答案就可以。而每次阅读的过程中因为它都是英文,所以建议大家跟我刚才过程是一样的,在旁边去做一些备注。
因为大家母语都不是英文,所以你加一些备注可以方便你自己来去做一些消化和理解。尤其是第二次来进行复习的时候会更快一点,这样就直接进入到你当时的一个笔记的环节就可以了。
那文章怎么找呢?在工作的人找论文会存在一些问题,因为有些学校的人直接可以从图书馆上进行下载。工作人如果你要下载论文这里也给你一些建议。
第一,找你的师弟师妹,让他们在图书馆上进行下载。
第二,在 Google学术搜索(https://scholar.google.com.hk/?hl=zh-CN)上面实际上是有那种免费的链接的,通过他可以链接过去。
第三点,有个 arxiv网站(arxiv.org),这个网站是一个预印本。什么是预印本?就是没有发表的文章他先发表在这里,它每一个月份都有5,000篇文,每篇文章涵盖的领域很多,有计算机的,有数学,有物理的,你在上面都可以去找到。
比如 SlopeOne 这篇文, 你在 arxiv上面就可以去直接下载。即使是在工作的状态,在公司里面没有在学校的内网里面都可以去在arxiv上面去下载这篇文章。这是一个开源的,一个日本的在线数据库,每个人都可以去访问。
咱们之前讲解的课程实际上对应了 4篇论文,第一个是刚才给大家看到的这篇论文,Netflix 的比赛。
08年发表的,大型的并型计算的协同过滤的方法。这个方法里面刚才也给大家简单梳理了,从前面的Abstract 到Introduction,再到中间提出来的方法,这个模型的实验的一些结果等等,最后的一些展望,都说的非常的清楚。在每一个过程中他都做了一个详细的一些对比,包括实验的一些环境配置等等。
那第二个你可以看一看 MF 矩阵分解的一些技术的原理
第三篇是 SlopeOne
SlopeOne 也是在 Surprise 默认集成的一个工具,论文提出 Slope One协同过滤算法,易于实现和维护。论文中提到了 3 种算法:Slope One,Weighted Slope One, Bi-Polar Slope One。
还有就是 surprise 中的 BaselineOnly。
刚才给大家看的 section 2.1,它是来自于这篇文章。
除了之前咱们课上讲解一些内容,一些算法原理来自于这 4篇论文,未来也给大家推荐了一些论文。感兴趣的小伙伴,如果你们对这些系统感兴趣的话可以看一看这些论文。
有 YouTube推荐系统等等,大厂的论文还是非常值得一读的。国外的话有谷歌、Facebook,国内有腾讯、阿里都还是很值得一看的。
第七条「Wide & Deep」这篇论文实际上是一个 Google的实习生写的,他是个中国人。在 Google时期的时候发表的论文。这个实习生是一个统计学的专业背景,所以他写出来这篇论文呢数学推导比较强。
后面还有一些内容都是来自于我们后面课程,有一些算法原理。如果你感兴趣的话,这些论文你可以去看一看,都有非常详细的一些介绍。
关注到哪些点?一个就是大厂的,还是希望可以划一些重点。可以去看一看它上面,大厂的文有个特点,就不光是有些新的算法,他还能告诉你这个算法里面怎么样做工程。因为工程里面要解决的是时效性的问题,海量数据处理的问题。那么你就会需要在算法上做一个折中,RMB里面它怎么做折中,阿里怎么做折中,YouTube提出了哪些优化的算子等等。这些都是在大厂的论文里面可以看到,学到一些内容。
有些小伙伴认为国内很少发推荐系统内容,其实还是有的,比如说淘宝,淘宝在18、19年这两年间就发表了三篇关于淘宝推荐系统的内容。还有一些综述内容,综述就在不同领域里面写内容。
那么这节课之后,大家思考一下 - ALS 都有哪些应用场景 - ALS进行矩阵分解的时候,为什么可以并行化处理 -梯度下降法中的批量梯度下降(BGD),随机梯度下降(SGD),以及小批量梯度下降有什么区别(MBGD) -你阅读过和推荐系统/计算广告/预测相关的论文吗?有哪些论文是你比较推荐的。
那以上这些内容大家可以去搜索,查阅。结合咱们课上所讲的内容,好好的思考一下,争取内化为自己的东西。如果你有什么心得,可以在文章下留言,给更多的小伙伴提供一些想法和感悟。
那这其中,头 4篇论文我已经下载好了,可以去课程的代码库中找到它们。剩下的,也可以根据我所提供的地址来去自行下载。
好,咱们最后预告一下,下一节课开始,咱们还是会继续看看推荐系统,我们来一起学习一下SVD 矩阵分解,再来看看基于内容的推荐的相关内容。
[TOC]
Hi,你好。我是茶桁。
上节课的内容中,我们介绍了 Surprise 工具箱以及其中的BaselineOnly,最后我们简单实现了一下。这一节课中,咱们来看看 Surprise中的另外一个内容,SlopeOne 算法。
SlopeOne 算法是在 2005 年提出的一个 item-based的协同过滤推荐算法,提出人是 Daniel lemire。
这个算法最大的有点在于算法很简单,易于实现,效率高且推荐准确度较高。
咱们来看一下这个表格:
用户 | 商品 1 评分 | 商品 2 |
---|---|---|
A | 5 | 3 |
B | 4 | 3 |
C | 4 | ? |
有 3 个用户对商品做了打分,用户 A 打了 5 分和 3 分给商品 1 和商品2,B 给商品 1 打了 4 分,给商品 2 打了 3 分,C 给商品 1 打了 4分,那请问 C 会给商品 2 打几分?
这是个协同过滤,所以我们会利用已有的一些用户对商品的评分。你们觉得 C会给商品 2 打几分?这个分数的打法怎么打,我们的打法是要找过滤,找到 1 和2 之间的一些差别。
可以打 3 分,为什么?因为 C 和 B 很像,B 打三分,所以认为 C 也应该是3 分。这其实类似于邻近方法,它不是 SlopeOne,这也是一种策略。
那 SlopeOne 的策略是什么?它会这么认为:你看 1 是不是比 2要好?好多少,我们要把规律学出来。
这里有两个样本,5 - 3 是第一个人,第二个人是 4 -3。一共有两个人所以除上 2:C 对商品 2 的评分 = 4 -((5-3)+(4-3))/2 = 2.5。
其实就是 1 比 2 要好 1.5 分,平均分要好 1.5 分。那么对于 C 来说的第 1个商品评分是 4, 4 - 1.5 就变成了 2.5。这个就是 SlopeOne的一个原理。
SlopeOne 就是先去找一个规律,然后去应用规律,把空的给它填上。
我们来总结一下 SlopeOne 算法: - Step1, 计算 Item之间的评分差的均值,记为评分偏差(两个 item 都评分过的用户)
\[\begin{align*}& 商品 i 和 j 之间被共同用户打分过的个数。\\& dev_{j,i} = \sum_{u\in S_{j,i}(x)}\frac{u_j-u_i}{card(S_{j,i}(x))}\end{align*}\]
首先,商品和商品之间应该是有一些规律的偏差的,我们把这个均值求出来,这是第一个找规律的阶段。- Step2, 根据 Item 间的评分偏差和用户的历史评分,预测用户对未评分的 item的评分
\[\begin{align*}P(u)_j & = \frac{1}{card(R_j)}(dev_{j,i} + u_i) \\P^{S1}(u)_j & = \overline u + \frac{1}{card(R_j)}\sum_{i\in R_j}dev_{j, i}\end{align*}\]
第二阶段是利用刚才找到的这个规律和用户的评分来去预测用户没有评分的商品。第二阶段叫做用规律,用完规律预测出来的分值去做排序,排序以后的结果推荐给用户。- Step3, 将预测评分排序,取 topN 对应的 item 推荐给用户。
看看 SlopeOne的原理,感觉起来应该也还好。我们来一个比原来稍微复杂一点点的一个例子一起来看一下。
a | b | c | d | |
---|---|---|---|---|
A | 5 | 3.5 | ? | ? |
B | 2 | 5 | 4 | 2 |
C | 4.5 | 3.5 | 1 | 4 |
三个用户 A, B,C,四个商品 a、b、c,d。用户 A 对它的打分,B的打分,C 的打分。现在就是要预测用户 A 对 c 和 d 的打分是多少。
我们用 SlopeOne,刚才咱们说的第一步就是要去找商品的规律,我们先看 b和 a 之间的规律,b 和 a 之间的规律的是三个用户都打分了,因此是
\[\begin{align*}b \ and \ a: ((3.5-5)+(5-2)+(3.5-4.5))/3 = 0.5/3 = 1/6\end{align*}\]
计算的结果,b 比 a 要好 1/6。
这个规律学完以后再去看 c 和 a 的规律,c 和 a 出现过几个样本?应该是 2个,用户 B 和用户 C:
\[\begin{align*}c \ and \ a: ((4-2)+(1-4.5))/2 = -1.5/2 = -\frac{3}{4}\end{align*}\]
也就是\(-\frac{3}{4}\)。
接着依次来找剩余的规律,包含上面的两个,完整的规律就应该是
\[\begin{align*}b \ and \ a: & ((3.5-5)+(5-2)+(3.5-4.5))/3 = 0.5/3 = 0.17 \\c \ and \ a: & ((4-2)+(1-4.5))/2 = -1.5/2 = -0.75 \\d \ and \ a: & ((2-2)+(4-4.5))/2 = -0.5/2 = -0.25 \\c \ and \ b: & ((4-5)+(1-3.5))/2 = -3.5/2 = -1.75 \\d \ and \ b: & ((2-5)+(4-3.5))/2 = -2.5/2 = -1.25 \\d \ and \ c: & ((2-4)+(4-1))/2 = 1/2 = 0.5\end{align*}\]
这个过程就是计算两两之间的一个均值的偏差,叫找规律。把两个均值间的差值计算了出来。
上面这个过程找好规律就要用规律了,要预测用户 A 对于商品 c 和 d的评分,那么用户 A 对商品 c 的评分怎么去运用它的一个规律呢?
a | b | c | d | |
---|---|---|---|---|
a | ||||
b | 0.17 | |||
c | -0.75 | -1.75 | ||
d | -0.25 | -1.25 | 0.5 |
来看这个之前找规律时计算的结果,c 是有 2 个, c 和 a 之间是 -0.75, c和 b 之间是-1.75, 再看 d,有三个,d 和 a 之间是-0.25, d 和 b之间是-1.25, d 和 c 之间是 0.5。
那 A 对 c 和 d 的评分就应该是:
\[\begin{align*}A 对 c 的评分 & = ((-0.75+5)+(-1.75+3.5))/2 = 3 \\A 对 d 的评分 & = ((-0.25+5)+(-1.25 + 3.5))/2 = 3.5\end{align*}\]
有了这个结果之后,就可以将 c 和 d 进行排序,排序结果是 d的预测评分大于 c,那 A 可能更偏好于 d,我们就给用户 A 推荐 d商品。预测评分排序结果就为 {d, c}
。
以上就是一个原理,先去找两点之间的规律,再把这个规律应用于未知的地方,再去求每个未知。过程中我们都是找这个未知跟已知之间的原有规律的计算,把它推演出来。
最好的方式还是代入进去自己去推算一下,最开始的那个过程应该都比较好理解,比较简单。后面就稍微复杂一点,但实际上再怎么复杂两两之间规律都肯定能计算出来。
ALS 和 SlopeOne这两种方法都是协同过滤,都是猜你喜欢,猜矩阵的值。SlopeOne它没有学内容,它就直接计算。SlopeOne这个方法其实原理也比较清晰,就是找两两之间的规律,通过这些数据来做个判断,然后再去应用就好。
它虽然是协同过滤,但是实际上没有太多学习的过程,它的学习也就是把以前的那个差值给他计算出来,统计的方式。
ALS最大的使用特点就是把矩阵降维处理了,把稀疏变成稠密再进行预测。
SlopeOne这个方法实际上在一篇论文里面可以找到。这篇论文的作者在论文里面提到了三种方法,刚才看到的是第一种SlopeOne,其实还有一种在原有基础上做的改进叫 WeightedSlopeOne,其实就是加权的方式。
如果有 100 个用户对 Item1 和 Item2 都打过分,有 1000 个用户对 Item3和 Item2 也打过分,显然这两个 rating差的权重是不一样的,因此计算方法为:
\[\begin{align*}(100 \cdot (Rating 1 to 2) + 1000 \cdot (Rating 3 to 2)) / (100 + 1000)\end{align*}\]
有 100 个用户对商品 1 和商品 2 都打过分, 还有 2 和 3 之间的规律是有1,000 个人的打分。那么这两个矩阵的打分权重一样吗?以往的方式,最原始的SlopeOne 的方式是一样的,而现在认为是有些差别的。1 和 2 的权重是 100,2和 3 的权重变成了 1,000。最后我们除上它,整个的平均就是 1,100。
加 Weighted实际上是一个比较容易的一种加权方式,这种加权的方法就考虑了不同的权重的概念。
SlopeOne的特点是作商品和商品之间的规律,对于商品不太更新的内容它就会比较稳定,也就是适用于item更新不频繁,数量相对较稳定的。相反,如果商品天天更新是不太好用。
item 数要小于 user数。因为商品和商品之间是两两之间都要计算规律的,item数量如果很大我们的计算规律的数量也会很多,算法的效率也是个问题,所以它适合于item 不太更新的场景,item 要小于 user 数。
整体来说算法比较简单,易于实现,执行效率比较高。
但是它是依赖于用户行为,存在冷启动问题和稀疏性问题。也就是用户需要对商品来做打分。
其实,只要是协同过滤的话都存在冷启动问题。比如说用户对商品,如果是个新的用户,没有商品打分,你就没法利用他以前的规律来进行操作。
稀疏性问题 ALS相对来说会好一点。它毕竟还有个降维处理,可以帮你来变得更加稠密一些。
实现起来其实和之前也没有太大的区别,就是调用的包会不一样。
1 |
|
我们还是预估 196 对 302 这个商品,那结果也还可以,原始值是 4,预测出来的结果是 4.32。
SlopeOne 是在 surprise 里的一个工具,surprise是一个工具箱,里面还有非常多的一些其他的工具。比如说基于邻域的协同过滤,你的邻域可以用KNN 找邻居的方法: KNNBaseline,这里就不讲原理了,KNN原理前面的课程讲过很多,只让大家体验一下什么是邻域,什么是基于内存。
1 |
|
我们运行一下看看内存:
在运行之前,还只用了 5 个 G 内存,运行之后直接飙到了 13.89G,如果这个Python 已经运行完了就会瞬间下降,降到原来的的 5 个G。这就是为什么叫做基于内存的过滤。
好,总结一下。这几节课我们矩阵分解内容就基本讲完了,首先矩阵分解是个隐语义的概念,隐语义通过隐分类去匹配用户和商品之间来做推荐,他把原来的平行矩阵R 做了一个降维的处理,分成了两个小矩阵:User 矩阵和 Item 矩阵,User每一行代表一个用户的向量,Item 每一列代表一个 item 的向量。将 User 和Item 的维度降低到隐类别个数的维度。
根据用户行为,矩阵分解可以分为显式矩阵分解和隐式矩阵分解。显式 MF中,用户向量和物品向量的内积拟合的是用户对物品的实际评分,隐式 MF中,用户向量和物品向量的内积拟合的是用户对物品的偏好(0 或1),拟合的强度由置信度控制,置信度又由行为的强度决定。
ALS 和 SGD都是数学上的优化方法,这两种方法也都给大家说了一下,可以解决最优化问题(损失函数最小化)。
ALS-WR算法,可以解决过拟合问题,当隐特征个数很多的时候也不会造成过拟合。ALS,SGD都可以进行并行化处理,SGD方法可以不需要遍历所有的样本即可完成特征向量的求解。
Facebook把这两种方法都进行了揉合提出来一种新的策略,旋转混合式求解方法,可以处理1,000 亿的数据。这数量级非常庞大,而且效率还比 Spark mllib 快了有 10倍。
从张图计算时间的图来看,Spark 是红色,Giraph 是蓝色,就 Facebook提出来这个方法,它的计算时间是原来的1/10。所以把这两种方法融合到一起提供一种螺旋式的求解方式,这样求解的效率,得出来参数的拟合速度会更快一点。
具体使用的时候当然就是调包,我们有 Surprise这个工具箱,里面有很多的方法。之前给大家讲过,它是 Scikit家族的,使用起来和 sklearn 也很像。它内置了 ALS 和 SGD等多种优化算法,还有一些预测算法,包括基线算法,邻域方法,矩阵分解,SlopeOne等等,这些也在之前的课程中给大家都讲解过。除了这些之外,还有一些没有讲的内容,比如相似性度量,内置cosine,MSD,pearson 等等。
这个就是咱们今天这节课的主要内容了,那下一节课中,咱们来看一个论文。虽然是比较早的一篇论文了,但是阅读论文也是一种能力,如果你想要未来获取大量的一些新的方法,读取论文是最直接的方式。
]]>[TOC]
Hi, 你好。我是茶桁。
今天给大家介绍另外一个方法:surprise。重点来看一下 surprise的一些工具的使用。
在此之前,来说明一下 Spark单机的分布式。其实分布式是一个方法,它把一个数据分成了很多块,这些块是相对独立的,在最后的结果层再把它进行汇总,这个就是一个分布式的概念。那为什么一台机器也可以?
分布式的数据可以放到多台机器,一个数据分成了很多块,块 1 在机器 1里面,块 2 在机器 2 里面,块 3 在机器 3里面,分成了不同的块。这样的存储是分布的,计算也是分布的,最后计算完再进行汇总,这个就是分布式的概念。
原来在三台机器的数据现在放在一台机器可不可以?也可以,当你数据量级不大的情况下它也有可能都是在一台机器,在一台机器上分块进行操作这也是OK 的。所以本身 Spark 是可以单机来进行运行的。
通常情况下,是放在多台机器的性能高还是放在一台机器的性能高?多台机器它的效率会更高,因为计算资源,每一台机器都是相对独立的一台机器,毕竟内存和计算CPU 是有限的,要共用一个效率其实不高,肯定是多台机器高。
因此 Spark是可以有单机版在单台机器来进行使用,但是它背后的原理也是分布式。它是把其看成了块1、块 2,只不过指向的 IP 都是同一个 IP,计算资源都是同一个 CPU而已。
接着,来看看 SGD 的概念。
之前的课程主要给大家讲解了交替最小二乘这种方式,随机梯度下降也是在机器学习的模型中比较常见的一个策略。这个策略怎么去学?
以 X0 这个点为例,目标是要最小值,这是 loss function的极值问题,这个点现在在这还是有损失的。怎么办,要沿着切线去下降,沿着切线往下去走。步子如果迈的过大,就会跑到曲线右边,再大一点又会跑到左边,所以它就会出现不同的抖动情况。
SGD就是怎么样去选择这个方向。方向很重要,在参数拟合过程中一个是方向,一个是步长。SGD基本思路就是以随机方式遍历训练集中的数据,并给出每个已知评分的预测评分。用户和物品特征向量的调整就沿着评分误差越来越小的方向迭代进行,直到误差达到要求。所以SGD 不需要遍历所有的样本即可完成特征向量的求解。
先看这个概念,看一个线性回归的一个例子,用它来去模拟的 y。
\[\begin{align*}h_{\theta}(x) = \theta_0 + \theta_1x_1 + \theta_2x_2 + ... + \theta_nx_n\end{align*}\]
以上是预测的方程,其中 theta 为参数,代表权重,n 为特征数。它跟 y之间用 MSE 去做一个表达,希望让损失函数最小化。
\[\begin{align*}J(\theta) = \frac{1}{2} \sum_{i=1}^m(h_{\theta}(x) - y)^2\end{align*}\]
这样的话每一次要学习的过程其实不长。
处于山中的某个位置,不知道极值点在哪里,每一步都以下降最多的路线来下山。
\[\begin{align*}\theta_j = \theta_j - \alpha \bigtriangledown J(\theta)\end{align*}\]
这里的 alpha代表的是学习率(步长),这个步长是沿着梯度的方式,就是沿着 lossfunction,对它的参数的一个导数,沿着它来进行下降。
曲面上方向导数的最大值的方向代表了梯度的方向。沿着梯度方向,会让 h值升高,因此需要沿着梯度的反方向,也就是采用地图下降的方式进行权重更新。
每次的更新,梯度下降是沿着导数的反方向来进行下降的。那么 theta就减去了这个方向,它就会前进一步。=
后面的 theta是原参数,=
前面的是更新之后的参数,被赋予了新值。
所以参数每一轮都会进行不断的迭代,不断的优化。那选择这个方向重要不重要呢?这个方向很重要。来看看更新的过程。
\[\begin{align*}\theta_j & = \theta_j - \alpha\frac{\partial}{\partial\theta_j}J(\theta) \\\frac{\partial}{\partial \theta_j}J(\theta) & =\frac{\partial}{\partial\theta_j}\frac{1}{2}(h_{\theta}(x)-y)^2 \\& = 2 \cdot\frac{1}{2}(h_{\theta}(x)-y)\cdot\frac{\partial}{\partial\theta_j}(h_{\theta}(x)-y) \\& = (h_{\theta(x) - y}) \cdot \frac{\partial}{\partial\theta_j}\left[ \sum_{i=0}^n \theta_ix_i - y \right] \\& = (h_{\theta}(x) - y)x_j\end{align*}\]
这是它的导数过程,方向有三种方向
这三个咱们在机器学习的基础课中都有详细的讲解过其原理。大家可以回过头去好好看看相关的课程。这里简单说一下这三种方向的一个特点。
批量梯度下降,在每次更新时用所有样本,稳定,收敛慢。
随机梯度下降,每次更新时用 1 个样本,用 1个样本来近似所有的样本,更快收敛,最终解在全局最优解附近。
而 mini-batch 梯度下降每次更新时用 b个样本,折中的方法,速度较快。
这也是一个比较常见的概念,未来不管是面试的时候还是在使用优化方法的时候都可以去了解采用的方法是哪一种方法。随机下降会使用更多,它虽然每次都是随机一个但是速度会更快,相对来说SGD 的使用场景会比较多。
其实目标函数优化方法已经不仅仅是 SGD,SGD是最早提出来的,后面还有很多种。现在用的更多的是 Adam,它综合了很多个方法,这个后面有时间再详细给大家进行讲解。SGD也有一些缺点。在梯度过程比较常见的是刚才说的这三种。
ALS 和 SGD 是可以用于很多的优化方法,这里给大家看 surprise的一种:baseline。它要求解的是预估一个电影的评分,结果是
\[b_{ui} = \mu + b_u + b_i\]
mu 是均指,大盘平均的分数。\(b_u\)是用户偏差,\(b_i\) 是商品偏差。
举个例子,还是以前几节课中的 MovieLens那个电影为例。在那个电影中有一部电影叫做泰坦尼克号,这个电影拍的比较好,它的\(b_i\) 是正向的,多了 0.5 分。在MovieLens 里面它整个的评分是 3.7 分。
这个值是直接计算来的,直接把所有电影相加以后除上总数就可以得到评分值。还有\(b_u\)是用户偏好,比如说这个用户比较苛刻,比一般的人打的分要低,低 0.3分。那么这个苛刻一点的人给泰坦尼克号会打多少分?
如果利用这个公式就直接可以求出来,这是一个简单的建模过程。把用户和电影之间的交互拆成了三段,大盘分,用户的偏好分和商品的偏好分。这个苛刻的人对泰坦尼克号会打多少分,就直接把这3 个数值做一个相加,应该等于 3.9 分。这就是 baseline的一个策略,用户对商品的评分分成了 \(\mu + b_u+ b_i\)。
要学的参数是哪一个?mu 是直接求出来的,
一个苛刻的人,假设他对所有电影都倾向于比一般的人要低 0.3。
\[\begin{align*}min_{b_u}\sum_{(u,i)\in k}(r_{ui} - \mu - b_u - b_i)^2 +\lambda_1(\sum_{u}b_u^2 + \sum_ib_i^2)\end{align*}\]
这个方法有点像之前给大家介绍的 ALS方法?从定义上来去看的话很类似,都是原来的实际分减出预测出来的分,再加上后面的正则化项。不过区别也是有的,哪种方法计算量会更简化一些?是之前给大家讲的ALS 方法更简单,速度更快,还是 baseline 这个方法速度更快?
如果做的是个机器学习的建模,要衡量的是要学习的参数量是多少。那baseline 更快,因为参数量更小,\(b_u\)和 \(b_i\) 只需要计算它整体的过程。
之前是有个 k 的概念,k 等于 3,要把一个用户分成三种情况,商品分成三种情况。而现在可以把它看成一种特殊的k 等于 1。相当于只需要把一个 k 变成 1,就类似于变成一个baseline。就之前讲解的12 * 3
和3 * 9
两个矩阵,user和 item, 只需要 user 的一列,item 矩阵也只需要一行就行了。
这就是 k 等于 1 的一种情况,只需要快速的计算一下 k = 1,这是 baseline的一个特点。
它使用的方法也可以使用 ALS,ALS 就固定一个求解另一个。现在 k=1,k=1它不是矩阵而是向量了。可以固定一个向量
each item i we set:
\[\begin{align*}b_i = \frac{\sum_{u:(u,i)\in k}(r_{ui} - \mu)}{\lambda_2 +|{u|(u,i)\in k}|}\end{align*}\]
Then, for each user u we set:
\[\begin{align*}b_u = \frac{\sum_{i:(u,i)\in k}(r_{ui} - \mu - b_i)}{\lambda_3 +|{i|(u,i)\in k}|}\end{align*}\]
baseline 是在 surprise 里的一个工具,surprise 又是 scikit系列中的一个推荐系统库。文档:https://surprise.readthedocs.io/en/stable/
还有一个就是 LightFM,是 Python推荐算法库,具有隐式和显式反馈的多种推荐算法实现。易用、快速(通过多线程模型估计),能够产生高质量的结果。
今天给大家使用的是 surprise,其常用算法包括:
刚才给大家看的 baseline是一种比较简略的方法。认为用户的打分求出来的参数是对整体的,商品的好坏也是对整体的。
除了 baseline 还有一些算法,其实整个的 surprise工具箱里面提供了很多可以调包的一些工具:
算法 | 描述 |
---|---|
NormalPredictor() | 基于统计的推荐系统预测打分,假定用户打分的分布是基于正太分布的 |
BaselineOnly | 基于统计的基准预测线打分 |
knns.KNNBasic | 基本的协同过滤算法 |
knns.KNNWithMeans | 协同过滤算法的变种,考虑每个用户的平均评分 |
knns.KNNWithZScore | 协同过滤算法的变种,考虑每个用户评分的归一化操作 |
knns.KNNBaseline | 协同过滤算法的变种,考虑每个用户评分的基线 |
matrix_factorzation.SVD | SVD 矩阵分解算法 |
matrix_factorzation.SVDpp | SVD++矩阵分解算法 |
matrix_factorzation.NMF | 一种非负矩阵的协同过滤算法 |
SlopeOne | SlopeOne 协同过滤算法 |
这是整个的 surprise工具箱里面能提供的可以调包的一些工具。baselineOnly 是基线,一般来说看到baseline的基线都可以猜测到它的方法速度会比较快,但是准确率可能相对一般,拿个六七十分应该问题不大。
要用的话,还是直接去调包 surprise:
1 |
|
surprise 里面这个类 Dataset 和Reader,相当是自己的一个读数的工具,你可以把它理解成是个读卡器。这个读卡器需要设定一些格式,通过这个格式会转成一个内部格式。
先是读入一些数据,build 一下训练集:
1 |
|
所以先去引用一下它的 reader,从 CSV 里面进行读取得到一个data,这是它内部的 dataset,然后再把这个内部 dataset 调入一个 buildtrainset,一个函数,这样它就会自动生成一个训练集。这是它写好的一个内置函数,不需要你做train set 了。
生成的这个训练集以后在 surprise 里面要用 baselineOnly这个方法创建这个包。在设置参数过程中有几个比较重要的项,一个是优化方法可以指定ALS、epoch,也就是迭代轮次,还有
1 |
|
reg_i 和 reg_u 是正动化系数,再去求 i 和 u的过程中正动化系数也可以会有一些不一样的地方。
然后又设定了一个 k 折交叉验证,其实不设验证直接去 fit 也可以。
1 |
|
fit 以后得到一个结果,然后去 test 得到一个结果。k 折交叉验证是得到 3折,把这 3 个结果都给它输入出来:
1 |
|
除了 ALS 以外,还可以做SGD。因为它也是优化方法,只要把学习的目标定义出来就可以采用不同的优化侧面来进行求解。
预测结果是指定用户和指定商品,
1 |
|
这相当于做了 3 个子模型,每个子模型都去判断一下 196 这个用户对 302的电影的偏差。
可以看到这 3 个子模型 MSE其实差别不太大,基本差不多。求出来这个结果实际值 4.0,预测出来是4.18,4.09, 4.11。基本上差别不太大。
以上是用了baselineOnly,虽然是一个基线的方法,但是结果差别也不是很大,还算是比较准确的。
surprise 里面除了 baselineOnly,还有一个NormalPredictor,这个方式今天没有详细讲,其实你也可以调包去使用。它背后的原理是拟合一个正态分布,通过拟合的正态分布给出一个预测出来的值。
1 |
|
好,下一节课咱们来看看 Surprise 里的另外一个协同过滤方法,SlopeOne算法。
]]>[TOC]
Hi,你好。我是茶桁。
前面两节课的内容中,我们从矩阵分解到 ALS原理,依次给大家讲解了推荐系统中的一个核心概念。
矩阵分解中拆矩阵的背后其实是聚类。就说 k等于几是人工设定的,所以跟聚类概念很像。就是要把人群划分成几类,把电影划成几类。k等于 3 是自己去设定的,也可以把它拆成 k 等于 4、k 等于5,都是一样的,是要完成聚类任务。
聚类不需要操心到底有哪些类型,它会自动的聚成这几类。这也是为什么把它称为隐分类。
「隐」就是我们知道它聚成了三种类型,但是不太清楚这三种类型具体的名称应该叫什么。所以它确实用了聚类的概念,至于为什么用了聚类概念,是因为最后学出来的类型是在3 个维度上打分的,一个用户有 3 个维度的评分,一个商品也有 3个维度评分。其实就相当于是把用户聚成了三类,商品聚成了三类。
或者换个角度,可以看一下
还是用之前的 12 * 9
的矩阵,以前一个用户的向量有 9个维度,9 个维度还是比较多的,有可能每个维度不全。现在我们要把 9个维度做成 3 个维度(k=3),这 3个维度就变成了更稠密的压缩的维度,这个小的维度就是个降维。
所以用户要降成 3 个维度,商品也要降成相同的 3个维度,它的概念都是一样的,大家都是在这个维度上面做一个抽象,这是为什么最开始我们给它画这个场景。用户商品中间这个隐分类我们把它称为interest。
或者这个 k就是个聚类方式。用户聚成了几种兴趣,那么商品也是聚成了相同的几种兴趣,大家都是在相同的这样的3 个属性的维度上面去打分。用户的打分如果在 3个属性上高那就代表你喜欢,商品上面高就代表这个类型的很强。
咱们来举个例子:一个用户的特效片是高的,封神第一部它在特效片上也是高的,剧情片和动画片它没有分吗?并不是,它也有分,也会有相关的一个分数。这样我们再进行计算的时候就更好的来进行计算了,就相当于把一个大维度降成了小维度。
那之前这些整个的就是给大家讲解的矩阵分解原理,原理还是要明白的。虽然最后工作的时候大部分内容都是调包,但是在这之前还是很有必要去了解一下它的原理。有些时候有可能一是面试的时候问,第二你调包的时候更知道它为什么要用到这个包,这个包背后逻辑是什么逻辑。
那这个包怎么调?有两种方式来调,一种就是大数据的方法,用 Spark。
Spark 是个大数据的一个平台,在 Spark 里面我们默认它是使用了一个ALS。Spark 如果你未来感兴趣可以看一看,它有两个机器学习的库,一个叫mllib 库,一个叫 ml 库。建议大家直接用 ml 库,也是官方推荐的。
mllib 库已经废弃掉了,3.0 版本之后不再维护了。所以大家直接用 ml这个工具箱就 OK 了。两个一个很大的区别就是 ml 主要操作的是 DataFrame,mllib 操作的是 RDD,两个面向的数据集是不一样的。相较而言,ml 在DataFrame 上的抽象级别更高,数据和操作耦合度更低。
第二点它使用起来已经做了更好的封装,就更像sklearn。机器学习中大家都习惯用sklearn,最开始进入机器学习也是调这个包,所以它就更像 sklearn的接口使用起来衔接起来更顺畅一点。
如果你用 Python 代码,那也该大家找了一个在 Github 上的,有一个 ALS类,可以直接引用这个类:
https://github.com/tushushu/imylu/blob/master/imylu/recommend/als.py
来看一下下面这个例子,数据集是MovieLens
,可以在这里去下载:https://www.kaggle.com/jneupane12/movielens/download
MovieLens是一个电影的评分网站,上面有几十万的电影和很多的人的一些打分。
我们以其中一个人为例,他给这么多电影打了一些分数。整个数据集叫readings.csv
,有四个字段,userId,movieId, rating 和 timestamp。
我们现在看一看这个矩阵,一个人打了这么多电影分数,那你觉得如果把它看成一个非常大的一个矩阵的话,这个矩阵是稀疏的还是稠密的呢?
虽然用户 1 给这么多电影打分,但实际上要知道电影其实会很多,可能有 10万部,而你只打了 1,000部,所以它还是个稀疏的。我们就需要猜出来那些没有打分的,比如说中间那些id 为 3、4、28,这些都没有打分。
怎么做呢?我们来看看代码。这里就是封装好的一个类,它是一个矩阵的概念。里面具体代码大家可以去我的代码仓库里去查看源码。
它就是做了一个矩阵的相乘,还有矩阵的转质啊等等。中间都封装好了矩阵的乘法,封装了ALS 类,后面使用过程中我们就直接调包就好了。
1 |
|
然后我们来创建一个读取数据的方法:
1 |
|
接着,就是要写一个 ALS 的实现
1 |
|
那接着就是写一个主函数来对方法进行调用,并且开始进行学习。第一个我们创建好这个model
1 |
|
然后进行加载数据
1 |
|
那load_movie_ratings
是已经封装好的一个读取数据的方法。这个方法做的事情就是导入数据并返回一个矩阵。
然后就是进行 fit 原来加载好的数据
1 |
|
k 等于 3 代表聚类个数,max_iter
设置的迭代次数很少,因为他计算的这个速度会比较慢,为了方便的话就只计算两轮。两轮之后就会有结果,把这个结果做一个预测,我们想要给用户1 到 12 来做预测,推荐两个商品。predict 给用户 1 到 12推两个商品,然后把商品结果打印出来。
可以一起来看一看,我们的结果中第一轮和第二轮结果,MSE结果都出来了。那这个 RMSE 代表什么含义?R就是开了一个平方,所以它是在原有基础上开了根号。
第一轮我们得到 3.35,一共学了两轮,学两轮基本上还没有学好,可以看一看第二轮 MSE其实已经变得比较小了,为0.31。整体的打分其实都不高,截图中是给用户推荐的一个结果,包含了商品id。
这是第一个,我们用 ALS 可以去完成这样一个任务完成推荐,用 Python包。还可以用 Spark,可以使用 ml 以及 mllib 去完成,大家可以安装一个pyspark 来进行调用。
不过我的 M1 一直没有调好 Spark环境,所以这一段演示也就暂时没办法拿给大家了,虽然代码在,但是因为没有环境跑过,所以可行性也不太清楚,就不放出来了,万一错了就是误导大家。
那下一节课呢,我会给大家再介绍一个方法,咱们下节课再见。
]]>[TOC]
Hi,你好。我是茶桁。
上一节课咱们介绍了推荐系统中使用的矩阵分解的原理,讲到最终我们设定了模型目标。
有了机器学习去解这个目标,要用到一些优化的方法。常见的优化方法有两种,一个叫ALS,交替最小二乘法。还有一个就是 SGD,随机梯度下降。
我们来看看,ALS是怎样一个原理。交替最小二乘法有点像我们拧螺丝的一个过程,就是固定一边求解另一边。
上一节课中我们有了 user 矩阵和 item 矩阵,12*3
的 user和3*9
的 item这两个矩阵都是未知,都是模型要去求的。一个方程有两个未知,R 是已知。
一个方程有两个未知能依次求解出来吗?如果 item 已经知道了,R也知道了,把 user 求出来这个很有可能。但是如果 R 知道,user 和 item都不确定的话一次是不能求解出来的。
那怎么办?我们就要固定一个求解另一个,这叫做交替最小二乘法。
重复 step1 和 2,每一次求解过程都是一个收敛的,去拟合参数的过程。
最小二乘实际上我们都不陌生,在中学期间做过一些物理实验,物理老师就会说我们的实验报告要多次求解。
举个场景,比如要测量一把尺子,这个尺子到底有多长咱们做了 5次实验,分别是 9.8、9.9、9.8、10.2 和10.3,那你在写实验报告结论的时候一般就是相加再除上 5,这个叫平均值。
最小乘法是由道尔顿提出来
\[\begin{align*}E = \sum_{i=1}^n e_i^2 = \sum_{i=1}^n(y_i - \hat y)^2\end{align*}\]
不采用中位数和几何平均数背后的原理就是因为要求一个导数为 0的函数。对其进行求导之后,导数为 0 的时候为最小值,因此:
\[\begin{align*}\frac{d}{dy}\sum_{i=1}^n(y_i-y)^2 = 2\sum_{i=1}^n(y_i - y) = 0\end{align*}\]
那么继续往下计算就可以得到
\[\begin{align*}(y_1 - y) + (y_2 - y) + (y_3 - y) + (y_4 - y) + (y_5 - y) = 0\end{align*}\]
所以,当它最小拟合的过程中就是
\[\begin{align*}y = \frac{1}{n}\sum_{i=1}^n y_i\end{align*}\]
也就等于 \[\begin{align*}\frac{y_1+y_2+y_3+y_4+y_5}{5}\end{align*}\]
现在我们知道,物理上做实验,取多次结果最后求平均值其原理就是我们要去求一个导数为0 的函数极值,也是因为我们的评价标准是最小二乘。
做了五次实验,得到五个数,就是 y1、y2 一直到 y5,是以这个例子为例。n次实验其实应该是从 y1 一直到 yn。做物理实验做 n 次应该就是这 n 次的 n分之一。
那现在也是一样的,用的一个叫做交替最小二乘,最小二乘是一种拟合技术,它可以让我们更好的去求这种参数估计。
最小二乘法在计算机产生之前已经变成了一个通用的数学工具,我们来看一下:
在 1889年,那阵还没有计算机,道尔顿和他的朋友皮尔森收集了上千个家庭的身高、臂长和腿长的记录,企图寻找出儿子们身高与父亲们身高之间关系的具体表现形式。
看到皮尔森大家想到是什么样的内容?皮尔森也是一个统计学家,有个「皮尔森系数」。
那个时候他们也没有一个数据建模机器学习什么的,那他怎么去建模呢?他就认为儿子和父亲之间的关系用一个方程,拟合它就求解这个方程,把这个系数求出来就行了,就是最小二乘。
\[\begin{align*}y & = a + bx + u \\\hat y & = 84.33 + 0.516x\end{align*}\]
所以最小二乘就是规定出来了一个标准,然后把这个参数拟合出来,通过导数为0的情况下极值来求解出来。这项乘法现在已经是一个重要的拟合技术,不仅仅用于我们刚才看到的线性回归,还在非线性的回归里面去使用。
所以非线性过程中也可以采用最小二乘法来做一个拟合,最小二乘法可以帮你来去拟合这样一个参数。
到底是怎样一个使用过程呢?回到今天使用的 ALS方法,对矩阵分解来求解一下。
我们想要去求解这个过程,\(r = x \timesy\),一个方程里面有两个未知数,能一次求解出来吗?求不出来。所以要固定一个求解另一个,如果把y 固定了,也就是随机的初始一个 y,这个时候 y是确定值,那么要求解的过程就变成了
\[\begin{align*}min_{X_u} \sum_{r_{ui} \ne 0}(r_{ui} - x_u^T y_i)^2 + \lambda\sum||x_u||_2^2\end{align*}\]
原来后面是加了一个 \(y^2\), 因为 y的平方是个固定的常量,任何的值 y其实对它都没有影响。所以就把后面这项给它去掉了。那要拟合的话就变成了这个式子,所以要拟合它目标函数就是:
\[\begin{align*}J(x_u) = (R_u - Y_u^T x_u)^T(R_u - Y_u^T x_u) + \lambda x_u^T x_u\end{align*}\]
转化为矩阵表达形式
$$ \begin{align*} R_u & = [r_
]]>[TOC]
Hi,你好。我是茶桁。
新年过后,咱们要开始学一些新内容了。从今天开始,要给大家去讲解的是关于推荐系统的内容。推荐系统的一些核心的原理会在今天开始的几节课中去给大家介绍,这个方法就是ALS 方法。
ALS 方法的背景是来自于一场比赛,比赛的赛题是去提升Netflix,一个电影网站的推荐率,如果你的推荐率能提升 10%,那么就会奖励你100 万美金,是一个百万美金悬赏的一个比赛。
ALS 方法提出的作者是个中国人,在完成这个比赛的时候还没有达到10%,但是他发现这个方法确实可以提升。相比于 Netflix官方的推荐系统的转化率可以提升将近 6%的一个转化率,所以他也发表了一篇论文,这就是我们今天看到的一个内容。
这个场景也利用到现在的推荐系统里面一个很核心的一个方法叫做矩阵分解,这就是我要给大家去讲解的内容。我们看一看这些工具是如何来进行使用的,尤其在推荐系统里面都有哪些工具箱,未来你要做推荐的时候也可以使用它们,这是咱们之后几节课的主要内容。
首先,推荐系统 ALS 矩阵分解先去从整体上去了解。
ALS方法其实是优化问题的解法之一,它只是其中的一种解法。可能更多人应该会了解一个方法叫做SGD,SGD 在机器学习里面也是一个非常常见的优化的方式,帮我们调参数的。SGD叫做随机梯度下降,梯度下降应该是机器学习非常核心的一个原理,它帮我们寻找参数的求解,每一次是沿着梯度的方向来进行优化,所以随机梯度下降是你的方向,是随机来进行选取的。所以SGD 是贯穿了机器学习很重要的一个参数优化的一个过程。那 ALS 其实跟 SGD是一样都属于优化方法,咱们就来看一看 ALS是怎么样帮你去学习机器学习中那些参数的。
我们今天会给大家介绍 surprise,它是在 Python里面的一个工具箱,它同时也是 scikit 家族。scikit就是我们今天比较常见的一个叫做 sklearn 工具箱,它是 scikit家族。所以推荐系统叫 scikit surprise,使用起来跟 sklearn 也很相像。
除了这个工具箱 Python 里面还有很多其他的工具箱,比如说像lightFM,那在 scikit surprise 里面有一些推荐系统的算法,包括 baseline的算法,SlopeOne 算法等等。
在这些学习同时,我会给大家带一个例子,一个非常经典的电影推荐系统。大概有十多万个电影和人们对它的一些评分,我们就想要去预测一下你还会对哪些电影感兴趣。利用已有的你对电影的评分的信息预测那些你没有看过的电影。这个就是一个电影推荐系统的一个场景。
那其中我们还要了解一些经典的 Python中比较重要的一些使用的工具,这种工具基本上我估计大家应该都用过,你说不知道是不可能的。但是用的好不好其实差别还是挺大的,因为在实际的工作中调包可能就是一两句话的事情,但是前面的工作处理这些代码都是要自己写的,大部分的时间反而会是跟DataFrame来打交道。比如说怎么去选取这些特征列,这些特征列你需要做一些转换,构造一些新的特征,提取一些信息等等,这些都要去看。所以我们还会一起看一看Python的一些工具的一些常见的使用,以及今天会给大家进行讲解这个内容中对应的论文。我们一起读一读ALS 的 paper。
估计很多小伙伴在学推荐系统之前没有太多的经验,可能以前也没有用过它,所以学习更像是一个反复的过程。第一次先从整体上去做了解,不用特别纠结于细节,细节的部分如果没有完全理解你可以先静下来。就有点像我们英语阅读的感觉是一样,先从整体上去了解。然后再去做第二次,去查看的时候可以重点去看一看之前不太了解那些细节。这些细节之间其实如果你仔细看的话,它跟我们以前的一些内容还是有关联的,比如说SGD随机下降。所以学习本身的过程是一个逐渐收敛的过程,你可能不是一次就能学到100%。第一次有可能达到个六、七十分,第二次、第三次会越来越好。
首先咱们去看一下矩阵分解推荐系统,就是猜你喜欢。猜你喜欢背后怎么猜呢?是通过一个矩阵来做了一个分解,这个分解的方法是ALS,我们推荐系统先从整体上给大家先看一看。
常见的推荐系统的方法分成两大领域,包括基于内容的推荐,还有基于协同过滤。这两种方法之间的区别是什么?基于内容的场景是你点了一篇文章,这篇文章属于什么属性我会基于它的属性来做推荐叫内容推荐。它跟协同过滤之间的区别主要看的是数据的来源。我们在之前的课程中也有给大家提到一些,推荐系统的算法是从这两个维度开始入手,一个是内容推荐,一个是协同过滤推荐。
协同过滤和内容推荐之间区别,内容推荐算静态的属性,协同过滤应该算动态的属性,所以协同过滤是人们的一些行为。那么动态属性里面现在大家研究的比较多的,使用的场景也很多。它又会分成两种,一个叫基于邻域的推荐,还有基于模型。邻域就是邻居,找你的邻居是谁。我们在之前的课程中讲过UserCF 和 ItemCF 的区别,这两个就是先找用户的邻居叫UserCF。找到跟你臭味相同的人,看一看这些人平时看什么样的电影,把他们看过的这些电影推荐给你,这叫UserCF。ItemCF是用户的以往的打分,这些打分,这个电影打分跟哪些其他的用户的打分更接近?叫ItemCF,所以它也是一个邻居的概念。
那么基于模型,看到模型会想到什么?我们在写算法的时候这个 model一般定义成为机器学习的模型。所以基于模型就是我们要去建一个model,给了训练的数据去建模,建好模以后就可以拿它预测,这个就是基于模型的概念。在模型过程中会有基于贝叶斯,SVM等等这一类型的概念,其实都是你建模的一些方法。SVM、贝叶斯,这些都是属于机器学习的一些方法,用它们的原理来进行学习和拟合。隐语义的模型里面又会分成矩阵分解、LDA等等。最后给大家一张树图,可以更直观的看到它们之间的关系:
所以今天我们要学习的内容「矩阵分解」所在的位置是在协同过滤下,这个应该是用户的行为,没有行为的话就不会存在矩阵分解,我们都是对行为做的建模,所以又会处于基于模型机器学习的过程,隐语义模型里面又分,矩阵分解是在它下面的一个位置。顺便多说两句,矩阵分解简称为MF,在隐语义模型里,还包括 LDA,LSA,pLSA 等方法。
那什么叫隐语义呢?隐语义,「隐」代表隐藏、隐含的含义。我们想要用用户和商品之间的关系,推荐系统就是给用户推荐商品,推荐item。
那么 user 和 item之间,我们认为它会存在一些隐藏的、隐含的一些联系。这样的 latentfactor(隐含特征)连接着用户和商品之间。
user 是个用户,item这是个商品。那如果中间存在一个隐藏的关系,这个关系一般可以把它称之为什么?我们这里就叫做latenfactor,也就是隐含特征,也可以把它称为叫做隐分类,但这些都比较学术。
用户有一些兴趣标签,商品也会有一些兴趣的分类,所以可以把这个隐的概念当成一个interest,就是把用户按照一定兴趣划分,把 item来做兴趣划分,所以它中间是可以连接起来,作为一个兴趣的属性连接彼此。而这个兴趣或者说它是个隐藏的、隐含的、隐晦的兴趣,实际上不是我们事先定义好的那些维度,而是基于行为自动的完成一个聚类的任务。
那我们学习机器学习这么久了,基础课也都学完的同学应该知道什么叫聚类。聚类它跟分类之间的区别有怎样的差别呢?在这个过程中之所以叫做隐,是因为我们采用的类似于聚类的一种手段。那么聚类它跟分类之间的区别大家在机器学习过程中如果有一些了解的话应该能知道,就是无监督。所以我们事先不知道它要分成哪些类别,只知道它自己无监督的方法,这就是聚类。因此我们把它称为叫做隐的概念。
那么聚的类别的个数,这个 k值,就是人工可以去定义的参数。如果我们的粒度很粗,k这个值就小一点,其隐特征少,这样的话我们划分的维度就会很粗。如果划分维度很细这个隐特征k 应该就会大一点。
所以这个 k是可以调节精细的程度的,如果想要让他预测的更加的准确一点可以让 k变得大一些,大一点会更准确,但同时计算量也会更大。那么隐语义的概念,它的可解释性并不是特别好。这个「隐」是计算机能理解,但对于人来说聚类你就不太好清楚它聚成的这个物理含义是什么。相比之下我们会认为ItemCF 可解释性会更强。因为 ItemCF 更像是相似度的一个推荐。这个 item的向量和另一个 item 的向量谁会更接近,谁就会是更适合的商品。那 Latent是按照我们的兴趣自动来进行划分,所以它的理解对计算机来说还能知道,人就不太好去理解。
还有就是在协同过滤过程中,刚才看到我们有两大分支,有模型的model,还有邻域的。邻域是包括了 UserCF 和ItemCF,有的时候我们也会把基于邻域这种概念叫做基于内存。如果你用的是UserCF 或者ItemCF,你会发现电脑内存会直接飙满标红。所以它是基于内存的协同过滤,这也是人们的一种称呼,因为它会非常的吃内存。
为什么非常吃内存?在计算相似度找邻居的时候,你是把所有的矩阵放到内存里面一起完成计算,这是个相对全量的数据。所以对你的内存要求会比较高。
如果你电脑是 16G 的或32G,或者更高,那你们也可以试一试,基本上也能体验出来它是很吃内存的,这是基于邻域的概念。
基于模型的推荐(Model-based),刚才提到是机器学习的方法,那么机器学习就需要分成两阶段:训练和测试。咱们应该能体会到,训练过程有可能往往会很长,但是一旦训练完成,机器学习基于模型的这种推荐的推理速度非常快。所以训练虽然时间长,但是使用起来效率还是非常高的。所以一般我会把它分成离线的训练和在线的推理这两个环节。
那讲到这里,我们都是在讲一些常见概念,大家可以先去理解一下,先看一看,稍后咱们会重点去看模型的使用。
在场景过程中我们的推荐系统为什么要用矩阵分解呢?这些系统实际上有两大场景,第一个叫评分预测。我们画一个大矩阵,这个矩阵分成user 的维度和 item 的维度。user 是由 U1、U2...,一直到可能 U100。item 是I1、I2...,一直到 I100。
现在用户和商品之间会有个评分的矩阵,可能有一些分数,还有一些分数是没有的。那么我们要做的事情就是预估他没有去打分的,猜用户会打成多少分。这种类型叫评分预测问题,这种问题也就是我们要去讲解的矩阵分解,矩阵分解的任务就是预测一下用户和商品之间,之前没有打分到底会打多少分。
第二种类型叫 Top-N 推荐,Top-N推荐就是不需要实际的分数,只要按照顺序给你提供一个感兴趣的列表就可以了。那你觉得这两个场景哪一個场景在推荐系统里使用的场景更多,更加高频?是第一个评分预测猜一个用户的打分,还是给用户推荐前20 个商品,TOP推荐?从业务场景上看的话,从我们自身需求看,不需要实际的具体的打分,只要把推荐类型给到就可以了,所以这个场景会比较多。
那为什么我们还会讲第一个呢?因为第一个和第二个之间也是有关系的。如果我们已经知道了第一个实际的评分,也能做第二个任务,就把后面那些未知的分数按照从大到小作排序给用户直接推荐就可以了。
所以这两个就是推荐系统里的两大场景,一个就到分数的粒度,一个就到推荐列表排序就可以。
我们看一下推荐矩阵分解猜你喜欢。刚才提到这是猜用户对他的喜欢的程度打分的情况。这里举了个例子是12 个用户 9 部电影,不是每个用户 9 个电影都看过都有反馈。这里标记的 1代表喜欢,你也可以把它认为是打一个很高的分数。空白的地方,你看有些地方虽然标了颜色,但是是空白,代表用户没有反馈。还没有反馈不代表他不喜欢,所以这12 个用户对 9部电影我们现在只能收集到一部分数据。还有很多数据是没有收集到的。
大家觉得这个矩阵在实际的网站过程中拿到这个评分矩阵是稠密的还是稀疏的?稠密和稀疏是矩阵的一个特征,这个特征就对后续的算法就起到了一个很关键的一个决定作用,那这应该是是很稀疏的。
因为一个人不可能把所有的电影都看完,豆瓣上有 10 万部电影,你最多看个1,000 部就已经很不错了,所以 99%的数据是空缺的。在 Netflix这个网站里面,它告诉我们用户评分只有 1%的数据,就说大部分99%格子是没有评分的。其实1%这个数量已经很高了,所以它是一个非常稀疏的矩阵。
我们看到这个矩阵的问题,想把这个矩阵填上怎么填?矩阵分解的思路就是把一个大矩阵拆成两个小矩阵,分别拆出来,这是它的一个整体的概念。
原来又大又稀疏,这个长和宽称为 m 和 n,数值会很大,比如 100万的用户,10 万的电影,绝大部分都为空值。那拆成小矩阵,user里面我们会设定一个 k。user 就是用户要用兴趣来做表达,这个 k值一般应该会很小。通常情况下这个 k 是远远小于 m 和 n。比如说它可能只有100,那只有 100 相比之前的 100 万和 10 万来说就会非常非常的小。item也是一样,可能 k 值也是固定的,也是100。这样就把一个大矩阵拆成了两个小矩阵。拆完以后我们最后得出来的会不会能把这个矩阵还原出来?一会我们可以实际的看一看。
那怎么拆呢?我们以今天这个例子为例,现在数据量级比较小,只有 12个用户和 9 个电影。行数是 12,现在 User 矩阵的行数也是 12,我们把 k取成了 3,k=3 的概念相当于是把用户分成了三种类型。
哪三种类型,我们可以看上面一个具体的图表标识。用户我们假设它会分成三种类型,电影这里的k 也分成三种类型。
先以电影为例,流浪地球 2、银河护卫队 3和封神第一部这类型的电影应该是属于特效片对吧?我个人是这样划分的,那奥本海默、满江红和孤注一掷属于剧情片,后面三个就是动画片了。
把电影这 9部电影分成三种类型,第一种类型它们三个是聚成一起去,它们在某些维度上可能会更加接近。流浪地球2 银河护卫队3、封神第一部属于特效片。奥本海默、满江红和孤注一掷这三个聚到一起去,属于剧情片。
所以隐分类的概念就是能给它聚成一起,但是没有一个明确的定义。最后三部聚成一起,你可以把它叫做卡通片,也可以叫儿童片,也可以叫动画片都是可以的。那这样我们就会把它记录成三种类型。
比如说我们认为说一部电影可以有三种类型,特效片、剧情片和动画片。那一个人也是相同的三种类型。
一个人会有三种类型,一个片子也有三种类型。那一个电影有没有可能会横跨两个类型呢?就是在这两种类型上都有取值,而且这个取值都不低。或说一个人有没有可能在两种类型上都有取值,还是说我们只能把它划分成一种?
举个例子,一个片子有没有可能横跨两种?这个片子既属于特效片,也属于剧情片,有这种可能性。所以这种类别它不是一个唯一的属性,它只是聚成几类。我们把这个类别去做一个特征的描述,在上面会有个分值,这个分值代表特征的显著性。用户也会在这三种类型上面有它的分值,代表它的显著性。
那我们就把它拆出来,原来这个象限很多,12*9,现在这个象限大家可以一起来数一数,User的矩阵行数应该是 12,列的话变成 k 等于 3。
item 矩阵,它的维度应该是行数变成了 3,行数这里是 k。列就是它的 item的个数是 9。12*3
,3*9
,这两个如果做乘法,把它乘完以后,请问它的矩阵维度会是多少?
矩阵相乘,那中间都有一个 3是相等的,如果这里不相等是没有办法做乘法的。那这其实也就是 12 乘以9。这样我们就会把它拆成了两个小矩阵,拆完以后再组装起来还会得到一个 12*9的矩阵。
每一个用户上面 k 等于3,每一个用户这三个值可不可能都是稠密的呢?原来(12,9)是稀疏的,现在变成了User 矩阵,(12,3),做了一个降维处理,这个降维的处理就是在很粗的或者在很细微的这种粒度上面,你拿放大镜去看,它可能不是每个片子都可以给你打分了,但是你在上面抽象,就像看我们的地图一样,它会变得很稠密,所以它就会更加密一点。
一般如果我们要进行聚类的话这个聚类很少聚的类别很多,一般可能 100类就已经很大了。所以每个格基本都有值,很容易都有值,所以它就会变成稠密。如果User 的矩阵是稠密的,item矩阵是稠密的,这两个稠密的矩阵相乘,乘完以后还能得到原来的 12 乘以9,请问得出来的这个新的 12*9 的矩阵它是变成了稠密还是也是稀疏的?
最后得出来的这个矩阵跟之前相比,原来是稀疏的,因为原来我们要拆评分,所以大部分为空。现在如果我们要把它降维处理了,变成了12*3
和3*9
,降维成两个稠密的矩阵,最后乘完以后应该也能是稠密的矩阵。所以这两个矩阵乘完以后我们就相当于是对原来矩阵做了一个补全的问题。
这个概念就是说为什么采用分解做,分解的概念就是用聚类的思想作降维,把原来稀疏的矩阵,又大又稀的12 乘以 9变成了12*3
和3*9
的这样的小矩阵,每个小矩阵都稠密了,然后再做乘法,还原出来的12*9
的大小,它也是个稠密矩阵。
我们现在要求的这个问题是猜你喜欢,以这个例子为例,猜什么?猜用户对其打分是什么。有些可能高一点,可能一有些可能少一点,是0。那我们要把它预估出来,这是我们的目标。
预估就是要把原来稀疏的变成稠密的,就把这个空给填上。怎么填呢?可以把一个大矩阵拆成两个小矩阵,每个小矩阵都很容易稠密。
因为你在很微观的粒度上面,看了 10万部电影,不是每部电影都会打分。但是你在宏观上分成三类型,三种类型你肯定是要打分的。比如说这个用户对特效片喜欢我们就高一点,0.98,动画片不喜欢低一点,可能0.01,剧情片可能中等,可能 0.57等等。这样每一个分值就基于他以往的行为,我们可以给它打出来这个分数。
那数据的信息量在哪个步骤增加呢?数据的信息量是在做预测的方式增加,实际上有点类似于像一个图像,我们拍了一张照片,但这个照片像素有缺失,把它抠下了一块,把它拆出来再还原给它补上。所以数据的信息量是我们通过建模的方式预测出来那些原来空的数据的过程。那现在Photoshop 以及DELL-3,还有其他的一些填补或者扩展图像也都是基于这个原理,只是它们会基于更大的数据量来完成的模型,所以表现会更好。
回来我们之前的案例,比如说每个用户都可以从特效片、剧情片和动画片上去打个分数,分数高代表你的特征明显,这种类型的电影更容易打高分,分数如果低呢特征就不明显。
通过学习,如果我们已经学完了这个参数,user1 给这三个类型分别的打分是0.93,-0.09,0.08,就证明用户对特效片是感兴趣的,剧情片是不太感兴趣的。
每个用户都打上了这种类型。每个片子也可以打上这三种类型,那这三种类型我们就可以做还原。预测值填补了原来的空缺的缺失值。
这两个矩阵,我们把 user 的矩阵和 item矩阵都预测出来了,再做乘法,得出来的矩阵是12*9,那这会儿12*3
的这个矩阵是稠密的,3*9
是稠密的,比较明显我们乘完以后一定也是稠密的。
第一个用户之前只是对流浪地球 2 和银河护卫队 3有反馈,是喜欢的,其他的没有反馈。不代表他不喜欢,只能代表他没有反馈给我们,那我们要去猜。那对于第一个用户,如果你要给第一个用户推荐两部电影请问你会推荐哪两部电影呢?还能再推荐流浪地球2 和银河护卫队 3 吗?他已经看过了,所以应该会猜之前没有看过的。
这样,我们已经预估出来了他对封神第一部感兴趣,分值要从大到小来做排序可能第一部应该是封神第一部,第二部就是奥本海默,给他做相关的一个推荐。
就是说我们通过预测补全的方法猜用户没有打分的电影会打多少分,然后从大到小来做相关的一个推荐,这是我们整个矩阵分解使用的一个逻辑。那这个使用逻辑为什么它能成立,背后的原理是什么?
它背后的原理实际上是一个建模的概念,回到我们之前的那个图形:
左侧的评分矩阵,我们将其称为 R矩阵rating
,这是已知矩阵。user 矩阵和 item矩阵这两个矩阵现在是已知的吗?原始数据并没有,它是未知,是我们要去学的参数。所以我们想要通过已知的Rating 自动地学出来两个矩阵,分别叫做 user 矩阵、item矩阵。这两个矩阵学完以后,怎么评价它学的好坏呢?机器学习里面我们想要把学出来的结果和实际的结果做个对比,如果我们要做回归任务就要做一个loss function。回归任务的话就是 0-5 分的电影打分,那回归任务的 lossfunction,也就是损失函数,它就是 MSE,如果是 MSE的话相当于我们预测出来这个结果就是
我们原来已经有值了,还有一些地方是之前没有告诉我们这个值,我们把已经有的值的差的平方算进去,没有的地方差的平方要不要也算进去呢?
也就是说,12*3
和3*9
,最后乘完以后会得到一个新的评分矩阵,我们称其为R', 这个 R'和原来的 R之间,有些是重叠的。原来是已知的,现在预测出来结果还有一些是你原来未知的,那么在计算MSE loss 方式的过程中要不要把未知的那些空,误差也算进去?
其实是不要算进去的,因为你其实并不知道它实际答案,比如说我们以前面的原始数据为例:
我们来看,user1 对封神第一部是没有评分的,如果你要计算的话能算 0分吗?因为没有评价不代表不喜欢,实际上这个用户对封神第一部可能是喜欢的,所以你其实并不知道他实际的结果,因此我们无法去预估那些未知的实际值,在计算MSE,去拟合过程中只能拿已知的值。
所以我们只需要拿已知的部分让它评分最小就可以了,这个就是一个优化问题。我们以前计算MSE的过程中,就是要去预测一个结果,希望你预测这个结果和实际的答案更加接近。那么怎么去预测呢?是通过参数来做预测,不论你用SVM,用 LR还是用什么,你学出来都是它的参数,参数固定了结果就会固定。所以你学完这些参数如何使得它预测出来的y'和实际值最小化就是我们优化的问题的定义的一个方法。
有了这个定义方法我们怎么去学习?这里先用一些向量来作表达,
\(x_u\)表示用户 u 的向量,k为列向量,\(y_i\)表示
\[\begin{align*}X = [x_1, x_2, ..., x_N]\end{align*}\]
商品矩阵 Y,商品数为 M
\[\begin{align*}Y = [y_1, y_2, ..., y_N]\end{align*}\]
为什么是 k 为列向量?因为这里的 k是已分类,想让它做一个近似的降维的处理。
那我们要去建这个模型,去建 lossfunction,机器学习的本质要去解这个问题主要是通过目标函数,就是规定了要学的一个方向,把这个方向定义下来,那我们的目标就是找到一个参数让它的目标函数最小化。
\[\begin{align*}min_{X,Y} \sum_{r_{ui}\ne 0}(r_{ui}-x_u^Ty_i)^2 + \lambda\left[\sum_u||x_u||_2^2 + \sum_i||y_i||^2_2 \right]\end{align*}\]
我们的定义这里用的是 MSE,因为要做评分预测,有可能是一个 0-5的分值。rui 是实际评分,x,y 就是拆出来的两个维度,user 和item,拆出来这两个矩阵让它相乘,它也能得到一个值。让这两个值的平方和最小化,前面专门写了一个判断条件:rui不等于 0。为什么要写这个,rui 如果不等于0,它能代表它是有值的部分,所以前面这个是我们实际评分的误差要最小化。
那在训练过程一般来说定义它就可以了,这是一个理想的状态。后面我们还把我们训练模型中的参数也放到了这个计算里面去。x和 y是我们要去学习出来的用户矩阵和商品矩阵,让它们的参数的平方和再加一个lambda 作为第二项。
我们前面就已经要让它预测结果会更小化,那为什么有的时候我们还会加一个第二项呢?之前给大家讲过XGBoost 和LightGBM,如果大家学习过的话就发现,在工程上面一个很重要的过程就是要添加正则化项。它的目的就是解决我们泛化能力,防止过拟合。
所以前面是我们的目标,我们在目标里面又加了正则化项,让其更加泛化,这样就会让这个参数抖动起来不会这么的剧烈。
防止过拟合之前给大家讲过例子,之前是给大家说了一个场景,同样都是达到月薪2 万块钱的目标,a 和 b 方式不一样,a的参数抖动比较强,就像一个滴滴司机,每天早上可能 9 点就出门晚上 12点才回来。这样他一个月也能赚 2 万块钱。b是一个办公室的白领,朝九晚五,一个月也能赚 2万块钱。那请问如果你要去做的话你是做 a 还是做 b?
都是达到了月薪 2 万,大部分肯定希望觉得 b 会更合理一点。因为 b的参数抖动没有这么剧烈,对于 a来说就抖动很强。这个参数在后面,第二项它整个的代价就会比较小,这衡量我们学习的代价。
有了机器学习去解这个目标,要用到一些优化的方法。下节课,咱们就来看看其中的一个方法,ALS。
]]>[TOC]
Hi,你好。我是茶桁。
咱们之前用了几节课讲解了可视化的一些使用,重点是在 Python里面的两个工具,一个是Matplotlib,这是一个基础的工具,还有一个高级的封装是 Seaborn。
它可以帮我们画各种各样的一些图表,在工程里面也是经常会使用到。
那接下来,咱们的重点是看另外一个场景,就不是原来的单个的应用了。我们来看看Dashboard, 这个在我做数据产品的时候经常打交道。
那什么是 Dashboard呢?你可以把它理解成是个看板,它是一个完整的应用,更全的可视化。
你在 Google 中如果搜索Dashboard,会看到很多很多的相关图片,下图也只是其中之一。
这是一个设计过的Dashboard,属于前端的一个呈现。我们今天要做的就没有这么精细化,主要是来看看Dashboard 如何进行搭建。
可视化的看板,平时在部门里面大家应该也都见过。不知道各位公司部门里面有没有一个可视化的大屏,可以看到整体的情况。我以前所在的公司就有一个大屏,上面实时展示了一些公司目前的一些数据情况。
Dashboard正式的中文名应该叫做仪表盘,在企业里面使用仪表盘可以把一些关键的指标进行呈现,让领导还有同事们更好的去了解业务的指标。
仪表盘该怎么做?一起来想想,如果要做一般来说从哪开始?我们可以有一些基础的内容,要做这样一个数据分析的项目一般要分成三步。
第一步叫做数据的收集,没有数据是不可能做可视化的。所以第一步我们需要从各个渠道去收集数据,这个我们叫做Data aggregation,也是我们 3A 架构的第一个 A。
第二个 A 叫做 DataAnalysis,也是我们的第二步。有些指标不是原始数据,你要计算出来。比如说在生产车间里面有个“设备的开动率”。设备开动率指的是这个设备的运行时长,就是运行了多久。设备开动率越高就代表这个生产车间它的效率越高。那这个指标是可能需要计算出来的,计算过程就在Data analysis 里面来进行计算。
第三个 A 叫做 DataActivation,这个分成两部分去理解,如果是宏观的层面我们就需要做可视化,微观层面就要指导业务员下一步要做什么样的操作。
这是我们数据的 3A 的结构,第一层 A 可以理解是数据中台,先收集;第二层A 是分析,最后是做一个可视化的报表,或者是业务上智能的推荐。
如果要做可视化呈现的话,工具可以有以下的选型:
第一个,不知道有没有人以前听过 BI 这个词,可能稍稍有些不一样。这里的BI 指的是 BI 工具,专门有一些做 BI 的一些软件。比如说 Power BI, Tableau这两个软件在企业内部使用的频率越来越多。
上汽大众还有一些其他的企业,他们在部门内部里面也在逐渐的普及 PowerBI,甚至新员工都在学习它们。
因为它们写起来比较方便,不需要太多编程的基础,拖拖拽拽就可以形成个可视化。上一节课咱们写的蒸汽量的那个项目可以看到,虽然Python 并不难,但是毕竟还是有一点门槛的。不如 Power BI拖拽就可以实现出啦,也可以发布出来。所以这种类型的工具就叫做 BI工具。
BI 工具里面比较典型的就是这两个产品,Power BI 和Tableau,它们两个在魔力象限的最顶端,它们是一个领导者的位置。
这两个工具都实现了拖拽就完成可视化搭建的功能。我这边是Mac,也没装虚拟机,就不给大家演示 PowerBI 了,说实话还是蛮简单的。
每一个工具都有它自己的使用边界,比如 PowerBI,这个工具如果是 toB 的是OK 的,但是如果是 toC 可能就不太好。打个比方,在 2020年那会儿,支付宝和腾讯都做了一件事,就是将新冠疫情的实时信息做了一个可视化进行展示,这个是toC 的内容,如果阿里或者腾讯使用 PowerBI来做这件事并发布的话,那每天的访问量将是一笔不小的开销给到微软,所以 toC的情况,大体上都是自己写代码去实现。
企业外部,C 端要怎么做呢?C端就是消费者的个人终端,我们就要使用另一个工具,就是 Flask。
一会儿我们也可以看一看 Flask 该怎么去写。如果想在 C端进行展现,需要自己写代码。包括之前介绍的两个可视化包,Matplotlib 和Seaborn 之外,还需要用到 Pyecharts。而如果要发布产品,会用HTML/JavaScript,其中有一个图形展示框架:echarts。这是常见的一些前端语言。
要去搭建那个框架的话就是刚才给大家推荐的 Flask,这个框架是在 Python里面一个轻量级的框架,它可以做一个 Server 微框架。在这个 Server过程中可以实现把前端后端都集成到一个统一的框架里面,来进行一个调用。这是Flask 的概念。
除了 Flask 之外,还有一个很出名的 Python 框架Django。因为它比较重,而 Flask较小,使用起来很方便,所以一开始我还是更推荐使用 Flask 来写。
说到小,我们来看看 Flask要实现一个页面上展示“你好,茶桁!”会用到多少行代码:
1 |
|
数一数,大约也就 10 行代码。这 10 行代码的含义就是在你的本地,IP 为127.0.0.1,端口为 8989,去搭建了一个 hello world的一个服务,打印出来“你好,茶桁!”这段简单的话。
我们尝试来运行一下。
这样简单的几段代码就可以把把服务搭建起来。
Flask 真的是非常短小,只需要把包引入起来。在 Falsk里面,上面称为路由,就是@app.route('/')
,这一段应该看出来,是一个装饰器语法,其中的含义就是以/
根目录为路由的一个页面。如果用户输了这个根目录的页面,那么就会调用hello
这个函数,返回一个你好,茶桁!
。
这就是一个最基本的一个 Flask 包框架,在使用过程中配置了 IP和端口,通过 IP 端口的 URL 就可以访问,把数据反馈出来。
Flask 是一个比较完善的框架,有以下的几个模块: -网站部署(manage.py
) - API 接口(api.py
) -数据库模型(model.py
) - 页面(HTML
)
主要是下面三个模块,上面manage.py
,称为部署的配置文件。通过它,运行文件就可以把应用部署上去,部署的是下面那三个部分。
在这里,解释一下 MVC结构,不知道大家有没有听说过。在搭建一个服务的过程中,是把服务按照一种MVC 的框架做的一个设置。
M 指的是 model,数据层。model是跟数据相关的模型层,所有跟数据打交道的都放到 model 里面。
页面要做呈现的话我们要通过 views来做呈现,包括网页的地址以及渲染网页等。
还有一些是逻辑的方面,逻辑里面的我们用的是 Controller。
找一下对应关系,model.py
是指向的 model,view 这是我们的HTML,还有 API 是 Controller。
所以这几个部分 MVC 的结构,通过 manage 做了一个部署。
在进入完整的例子之前,咱们先来看一个比较简单的一个过程。如果觉得Flask 比较复杂,我们实际上还会搭建一个应用,用 Dash的方式做一个搭建。
Dash 其实就是在 Flask 的基础上又封装了一层,它在纯 Python环境中构建数据可视化 Web App 的开源库,基于 Flask、Plotly.js 和 React构建。它基本上就不需要你跟 HTML代码来打交道,可以直接搭建一个应用来做交互的呈现。
dash_core_components
库包含了一组高阶组件,包括下拉菜单、图形、Markdown等等,简称为dcc
。现在dash_core_components
以及dash_HTML_components
已经被遗弃,可以直接在dash
中引入,如下:
1 |
|
接下来,咱们就来看看这样一个例子,用一个股票的例子来做,这里想要呈现出来己知股票:
1 |
|
在呈现之后,可以通过 select 下拉框来选择某一支股票进行查询从 7 月 1日到 11 月底的数据情况,来看看它的相关走势:
写过前端的小伙伴应该清楚,这样一个展现,在调用前端框架,比如Vue+echarts 或者 React+echarts的情况下,要完成也需要写一定的代码量。咱们看看 dash 如何做。Flask里面也是需要进行 HTML 的编写,
首先咱们需要创建一个应用:
1 |
|
然后可以设置 layout:
1 |
|
我们就没有任何的跟 HTML 相关的东西,都是在 Python里面实现的。硬说有关系,那就是在代码中设置了一个 H1 标题和 options选择项。
dash 的底层逻辑也是 Flask,跟 Flask 的过程是完全一致的。只不过我们把HTML 的部分的也在 dash 里面去做一个设置。
接着,就是需要写一个方法去调用数据了和展现了:
1 |
|
我们用的是 tushare来获取数据,这个需要看课程的小伙伴们自己去申请注册一个帐号,或者你也可以使用其他数据来完成。
那我们这次没有为他设置端口,用了一个默认端口,其默认端口是8050,所以咱们在页面可以打开看看:
之前黑色底色的截图是在 VSCode 里的 jupyter文件内直接截取的,这次是在浏览器里。
大家可以拿代码自己尝试下运行,然后选择下拉框去查看。选择其中任何一个股票,比如说天海防务,数据都是一个实时动态的数据。我们再选择其他的数据都可以。
这是本身的一个应用,之前我们用的是 Matplotlib 和Seaborn,这给自己看是 OK的,但是如果你要给同事看,给领导看并不行。如果你要让更多人去访问,就要搭建一个服务出来,就需要跟这里写的是一样,需要有个IP,有个端口。大家访问过程中只要访问这个 IP和端口就可以看到你的样式。
那我们就不能使用 matplotlib 和 seaborn 了,就需要使用 Flask,用Dash,用 Django 等等框架。这框架可以自己去选择。Dash 的好处就是不写HTML,但是它也有一些它自己的弊端。比如说不太灵活等等。那如果你要更加完善一点的,Flask还是比较好用的。
在 Flask这个框架下面,我们还有一些要做一些交互的一些图表。大家可以想想我们平时看到一些大屏,这种大屏一般它背后的图表用什么样的工具来做实现呢?以618 或者双十一为例,618和双十一里面会有个作战的指挥室,这个作战指挥室会用一张大屏来做呈现,这个大屏的技术就是Echarts。
Echarts 是被百度开源出来的一个 js框架,它专门是给你做一个插件做图表的展示。
这些就是 Echarts 一个图表的看板。那 Echarts图表看板里面都有哪些图做呢?我们可以看看它的官网:https://echarts.apache.org/examples/zh/index.HTML
可以去看一看都有哪些图表,可以自己去做一个选择,自己体验一下。
它的工具还是比较好用的,有非常好的一些可视化的一些示例。其实天猫双 11这个大屏会邀请很多的一些媒体记者看他当天的销量,那个大屏技术拿什么实现?他背后最后那个大屏的也是类似采用Echarts。
百度开源的工具里面这个应该算是比较成功的一个工具。你可以去点击一张图表,每一张这个图表可以自己去改写任何的数字,我们在改写完这个数字在右侧都可以看到改写之后的一个结果。
假如我们拿最基础的折线图,将其改为中文的周一到周日:
改完之后,右侧都会实现出来。如果你要想要去把这个样式下载下来,可以点击下载示例,会下载一个HTML 文件。可以把刚才下载好的这个例子直接用浏览器打开。
那 Flask 里面的应用就会嵌入Echarts,拿它做可视化的一个展示。数据的处理是通过 Python完成的,如果你对 HTML 代码不是很了解,还可以用另一个工具叫Pyecharts。
如果跟 Python 相关,做数据分析,做 BI 相关的,完全用 Python 就 OK了。那未来如果你想要做一个领导要看的、同事要看的、所有人都能用的产品,再去把这个Echarts 拾起来,再把我们的 Flask 给它拾起来就可以了。
下面就只是让大家去了解一下有这么个工序,也有些代码。简单给大家看一看这个结构是怎样的,直接使用Echarts 你要对 JS前端要去了解,那我自己本身以前就是前端出身,不过这里就不做一些前端工作的展示了。
如果使用 Pyecharts 的话,这个就跟我们的 Python会更加友好一点。我们可以看一看这个 Pyecharts 的过程。
将 pyecharts 引入进来,然后拿一个 2023 年和 2022年各国生产总值的数据来做一个条形图。
1 |
|
这是一个单列的柱状图,那我们还可以做一个多列柱状图,就是再多一组数据,然后在y 轴上进行展示就行。
1 |
|
那我们最后执行 work2 这个函数,来渲染一个多列柱状图的 temp.html文件。
1 |
|
y 轴是你的 data具体的数值,还可以再设置一些样式。这些.opts
代表 options,就是它的一些参数项,专门配置样式。
比如说要不要呈现 title,具体参数在 echarts里面都可以查到它的参数的一些设置。
生成出来之后,Pyecharts 可以帮你写一个 HTML 文件,咱们最后的 render就是用于生成一个 HTML 文件的。也就我们做的网页呈现的一个效果。
这样就不需要直接在 HTML 上面去写,就可以直接生成。
所以它的好处就是只要你会Python,就可以帮你写一个前端,把这个页面给展示出来。
除了渲染出一个 HTML 文件之外,pyecharts 还支持在 Jupyter内直接显示图像,渲染方式就可以从render()
换成render_notebook()
。大家可以自己尝试下。
关于 Flask的完整项目搭建,之后咱们会有一个收费课程,是关于企业的实战项目的完整讲解。有兴趣的小伙伴可以关注我,之后进行购买查看。
]]>[TOC]
Hi, 你好。我是茶桁。
我们今天继续来看数据可视化做数据探索,今天我们还是来看相关项目。来看看可视化EDA 在项目中的应用。
接下来这个项目,是在阿里天池上的一个工业蒸汽量的预测项目。
首先我们来看一下一些前提知识点。我们知道,火力发点的原理是:燃料加热水-> 生成蒸汽 -> 推动汽轮机旋转 -> 带动发电机旋转 ->产生电能。
在这个过程中,影响发电效率的核心是锅炉的燃烧效率。影响锅炉燃烧效率的主要因素包括:
很明显,我们要通过调节的锅炉的参数以及锅炉工况的参数来去预测它的蒸气量会是多少。
这个项目的训练集为zhengqi_train.txt
,测试集为zhengqi_test.txt
,数据都是脱敏后的传感器采集数据(采集频率为分钟级)。训练集大概38 个字段。
先大概来看一下这个数据,现在要做的事情是根据锅炉的情况来预测它的蒸汽量,蒸汽量这个特征是在最后,也就是要去预测target
这个值。
今天主要不是要做建模,而是去看一看数据的分析以及可视化。看看能帮我们得到怎样的一个结论。对于我们预测来说也是有一定的帮助的,至少前期的数据探索是很有帮助。
当我们看到这样一个数据之后,思路会是怎样的?怎么去做这个可视化的分析?
首先,我们需要将数据加载出来,每一个特征都可以做一个异常值的处理。还记得咱们之前将图形可视化的时候,可以用箱线图来找出异常值的情况。
首先,我们还是 Download数据,在你的命令行内先进入一个目录,然后进行下载:
1 |
|
下载之后进行加载,直接使用 read_csv 就可以
1 |
|
因为读取之后的数据格式不对,会发现有很多的\t
所以我们需要在读取数据的后面加上一个sep='\t'
。这样,就不是以逗号被分割了,默认是以逗号。现在的数据因为是\t
,所以就写成separator 等于\t
。
这个数据基本都是数值类型,我们想要看它的特征可以用 describe来做判断。这样就把这 38个特征的统计量一目了然了,画箱线图就拿它来画的。
1 |
|
接下来当然是要导入 matplotlib 和seaborn,用于后面的图形展示。第一张图我们要看一下 V0 和 target之间的一些关系,那先用箱线图呈现。
1 |
|
除了 V0 还有其它的列,列数有多少呢?我们可以看一下 columns,用一个len 方法来查看它到底有多少个。
1 |
|
一共是有 39 列。我们直接用 columns 做个遍历。每一个要设置 index,一共39 个特征,你的画布可以设置为多少?比如说我们设成
1 |
|
我们首先用 cols 将 train.columns 拿到,然后对它进行遍历,其中 i设置从 0 开始,作为它的下标。一开始,就需要进行+1,然后按照之前我们想好的设置,subplot
设置为5, 8
,i 就是当前那个图。接着,我们用boxplot
将当前的 col绘制出来。
不过这样明显看不出所以然出来,我们需要将图像设置的大一点。这个太密集了。所以再设置一下figure
:
1 |
|
这样就清晰很多,V0是有异常值的,似乎很多都有异常值。我们来找找看哪些没有,大部分都有。V14没有,V22 没有。这就是查看异常值的检测。
接下来,咱们可以看看直方图和概率密度图。它是用特征在训练集里面和测试集里面同时做一个呈现。
跟刚才的逻辑一样,先把画布大小做一个设置,然后循环的时候去掉最后一个目标特征target
:
1 |
|
最后是将train
和test
两个数据集都绘制了出来,赋予不同的颜色
之前我们的课程中有说到 distplot 这个方法,默认是将直方图和 kde结合在一起。其实我们使用 kde 会显得更清爽一点,只要将 distplot 换成kdeplot 方法就行了。
这样看起来虽然清爽了,但是总觉得还缺点什么,其实可以将阴影加上就明显很多。我们可以在方法名后面设置shade 为 True
1 |
|
因为 kde比较平滑,它没有这么多的像乐高一样的方块。这个就看起来比较直观一点。
看 kde能发现哪些问题呢?我们现在要找那种测试集和训练集不太一致的,可以把它去掉。比如说第一个,我们认为应该是一样的。可以把这张图里面的轴向上的label也给它呈现出来,因为一会儿要数个数,不如直接给它标记出来。顺便,我们之前设置了图例label
但是没有正常显示,也一起来解决一下。
1 |
|
set 一下 x 和 y 轴的label,并且加上plt.legend()
让图例正常显示。
现在我们可以一起来看看,有哪些是不太一样的?前几个虽然有一点差别但实际上还是可以判断的,如果这个蓝色和红色差了几个位置可以给它做一个调整,给他做一个特征变换,用新的特征代替原有的特征,这样预测起来可能会更准一点。
V5 差别比较大,所以 V5 有可能不太行,好,咱们先记住一个。往后看,V9差别也是比较大。咱们就需要去这张图上去找。5、9、11、17、21、22、28,这几个图中可以看到,这些特征在训练集和测试集中的分布差别就比较大的。这一步其实没有一个完全的标准,看你自己感觉上哪个差别比较大,可以给它去掉。
去掉的目的是降维,不是说要得到更精确的方法。如果更精确的话,你不去可能效果会更好。
现在可以给它写个方法来去掉这几个特征,先来个列表将特征名放进去:
1 |
|
就是这些 column,我们写一个 list放进来,这样稍后数据处理会更加少一点,不是所有的数据都会有。
1 |
|
这样我们处理之后,就剩下 32 个特征。test 也是一样要进行处理。
通过训练集和测试集的分布我们找到了一些分布不太一样的给它去掉,剩下的这些特征哪些跟target 之间的相关性是比较高的?
接下来看相关性的话,就要用到regplot
,方法还是类似的。设置一张大图,然后将特征进行循环,把数据扔进去以后进行绘制:
这里为了让相关性更明显一点,我们将线设置成不同的颜色:
1 |
|
这样可以知道它的相关性,有些相关性是比较好的,有些相关性是不太好的,这张图上就可以一目了然。
也可以专门做相关性的系数,这次我们使用之前去掉特征之后的train2
来做一个相关性系数的呈现,绘制它的热力图帮你判断。
1 |
|
现在咱们用了热力图把相关性系数呈现出来,系数非常多,因为维度很多。即使咱们之前已经去掉一些特征,比如说28 就去掉了,22也去掉了,但是这个维度还是很多。那该怎么看呢?我们可以筛选出来重要的特征,g大于 0.5 的,绝对值大于 0.5 的,我们要找跟 traget 的之间相关性大于 0.5的。
我们首先设置一下 threshold, 然后将之前 train2里的相关性系数都存下来
1 |
|
然后需要筛选一下保存的这个系数里,target
的系数,并且它需要是大于threshold 的
1 |
|
我们只需要它的 index,来喂给我们新建立的一个 filter
1 |
|
这样,我们就拿到了相关性系数比较高的一些特征。咱们把这些特征再去做一个呈现的可视化。
1 |
|
刚才是一张很大的图,看起来比较密密麻麻。那现在找出来那些最关键的特征,看这张图是不是比较清爽一点了?
这张图里都是和 target相关性比较高的,要么是正相关比较大,要么是负相关比较大。这就是我们筛出来的特征。
这个做完之后,咱们前期的数据探索基本就差不多了。现在咱们可以快速的来做一版预测。
咱们要预测的是它的蒸汽量,咱们一起思考下,可以用什么样的方法?是不是可以建一个回归模型?简单一点的模型用线性回归。所以我们从sklearn 里面用 liner_model, 导入 LinearRegression, 回归模型。
然后,因为需要用到的是线性回归,肯定这样将数据喂给模型是不行的,需要先做一个归一化处理,所以还需要导入一个StandardScaler
,当然,你用其他的Scaler 也可以
1 |
|
需要的包咱们有了,当然第一步是需要训练集中的target
去掉,做一个新的特征列表:
1 |
|
接下来,咱们就需要将数据做一个处理,做一个归一化处理,测试集和数据集都要做。做的时候注意,train训练集我们需要 fit,测试集就不需要了:
1 |
|
接着就是创建模型,引用模型对数据进行训练:
1 |
|
再然后就是预测了
1 |
|
咱们将这个预测结果保存一下,存成 DataFrame格式,整个数据是不需要表头的,我们可以设置 header 为 None
1 |
|
看看这个baseline.txt
发现里面是有一些负数的,我们需要回过头去看一下咱们的源数据 train,观察一下它里面是不是也有负数,原来那个数据的 target如果没有负数那你预测出来负数可能也不太对。
1 |
|
好,也是有负数的。那说明咱们预测的没有问题。这样,就把这个例子给大家写完了。这个模型只是做简化版,并没有说一定要去做一个精确版的。
这个例子就把一个蒸汽量的预测问题给大家写完了,这章重点看的是前面的可视化的过程,而不是训练。训练之前给大家讲了太多了。可视化过程可以让你对特征会比较熟悉和了解,有没有异常值、分布是不是一致,你看到它就会有后续的一些想法,idea就会产生。哪个特征是比较关键的,这些特征之间的相关性系数是如何,重要特征,最重要的那几个是哪些等等就可以拿出来了。
如果你想要做更精确的预测的话可以用复杂模型,比如说你可以用XGBoost,用LightGBM,树模型都是一些非线性模型。它的模型就会比较复杂。
咱们上面这个数据也没有对异常值做处理,如果你想要 drop异常值的话也是可以做一些处理的。但是异常值其实不是所有的情况下都一定需要drop,它只是给你判断出来有异常值。我们需要观察测试集里面有没有异常值,如果你的测试集里面有异常值的话那么就需要在训练集里有异常值。有点像它的分布情况是一样的。所以异常值只是给你提醒,说这样的数据跟其他的分布之间差别比较大而已。很多时候还是要看测试集里面有没有,要去做一个判断才可以。
咱们花了三节课的时间来做数据可视化探索,方便你对数据的异常值检测,数据的一些特征的情况做一些了解。这样你对数据的字段就更加的清楚。
咱们讲了几个图形,这其中饼图其实可以用直方图来呈现,领导有可能会看,但一般来说我们自己也要去做分析,打比赛的时候,做项目的时候用的更多的是散点图,折线图和直方图。
好,预告一下内容,下一节课,咱们会进行一些产品型的一些内容。那咱们下节课再见。
]]>[TOC]
Hi, 你好。我是茶桁。
之前两节课,咱们学习了基础的数据可视化工具以及决策树的可视化。今天这节课,咱们要看到的是另外一个场景,叫做词云展示。
词云应该所有人都不会陌生,一般什么时候用呢?
2007 年的时候北京的 Google总部,一进入谷歌大楼就有一个非常震撼的场景,在谷歌的大屏幕里面就放了一个词云的展示。其实现在都知道是词云展示,技术并不是很难。它有个地球,每个地点里面都有个点。无论是在北京,或者在印度、美国的某个城市,每个点里面会呈现一个词云。这个词云告诉你在当前城市的关键词,它的新闻都呈现怎样的一个走势,用一种滚动的形态。你会发现它非常的智能。
词云就是对关键词的一种提取,它是文本分析的一种工具。如果我们要做文本分析的话基本上比较常做的就是两种,要么就是英文,要么就是中文。
在做词云展示之前要对文本进行处理,文本特征要去做提取的时候你要以一个单词的粒度。那单词怎么来?在一连串过程中我们要把最原始的那个单词给它提取出来,我们称之为叫做分词。
分词需要用一些分词工具,中文的分词工具最常见的是jieba
,英文的话用NLTK
。
那为了下面课程大家能跟着一起操作,我说一下这两个工具的安装。jieba
比较简单,直接用conda install jieba
就可以了,nltk
也是一样的,直接用conda install nltk
,只是nltk
除了安装包之外,需要安装一些必要的数据集,以便特定功能正常工作。在未确定数据集之前,可以先安装常用的子集:
1 |
|
词云工具的用了word cloud
,配置 word cloud的时候的话需要输入几个参数:
1 |
|
单词的最大的容量,在画布里面呈现多少个单词,画布的长度和宽度。然后再把原始数据喂给画布去生成。生成之后可以把它输出一个jpg 文件,就可以把这张图呈现出来了。
这个也不难,我带着大家来写一写。
首先我们需要加载包:
1 |
|
接着来定义两个方法,一个方法是用于生成词云,还会调用另外一个方法,用于去掉停用词。
为什么要有停用词呢?因为其实在我们做分词的时候,很多的单词大量出现,但是却没有实际意义,比如the, a, of
等等。
先来写停用词删除的方法:
1 |
|
之后我们再来写一个生成词云的方法:
1 |
|
两个方法定义完成之后,我们需要一个数据。要生成词云,肯定不能凭空生成,必须是需要数据才可以的。咱们用一个电影数据,里面包含两个特征,title
和genres
。
1 |
|
之后就是需要将两个特征读取出来,因为是形成词云,主要是要单词词组,两个可以合成一个数据,不需要做区分。
1 |
|
最后当然就是将数据喂给我们写好的方法,形成词云:
1 |
|
词云图展示出来以后,我们可以看到 Comedy, Drama 和 romance都比较多。
简单的了解了词云怎么去完成之后,咱们再来一个案例。拿一个 MarketBasket购物篮来做词云分析,这个是 kaggle里的一个数据,下载地址为:https://www.kaggle.com/datasets/dragonheir/basket-optimisation/
这是一个超市的购物小票数据集,我们想要对词云来做个展示,做个探索。做探索就可以判断出来这个超市哪一个商品使用的频率卖的会更好。然后你也可以对这个商品去取一下TOP10 都有哪些。
加载数据之后,我们来打印一下它的数据看看
发现这个数据里面还是有很多的空值,不仅如此,那个 head并不是咱们的特征名,那么我们在读取这样的数据的时候还要处理一下,来重新读取一下数据:
1 |
|
在读取数据的时候,我们将 header 设置为了 None,这样获取的数据,就不会将第一行认为是表头了。
再来看看 values 是怎样的
确实,很多的nan
。那在做词云之前,我们需要将数据先处理一下,反正要将所有的字符都放在一个变量里,在放入之前,先来判断一下它是否为nan
就好了:
1 |
|
最后,让我们将之前定义过的生成词云的方法调用一下,将本次我们处理好的数据喂给它:
1 |
|
这样,就生成这次数据的一个词云图片。是不是还蛮简单的?其实重点还是在处理数据那里,第一个values 看到 dataframe 里面所有的数值,这个数值因为它有两个list,所以写了两层分循环。有些值为空,就判断一下它是不是不为空,如果不为空把它拼接到一起。这样就把所有的文本读出来。读完以后再去用它来生成词云,就可以得到一个展示。
]]>[TOC]
Hi,你好。我是茶桁。
上一节课,咱们了解了图形的具体绘制方法,接下来咱们还要看看除了图形绘制之外,还有哪些要做的可视化分析。还有一些是跟模型相关的可视化,在运算过程中我们可能会有一些模型。
来,我们先上一个例子来体验一下,包括它中间的一些数据可视化。这是一个泰坦尼克海难的一个数据示例,我们都知道这是一个著名的十大灾难之一,究竟有多少人遇难,各方统计的结果不一致。现在我们可以得到部分的数据,数据我还是一样放在文末了。
这个数据格式是 csv 的,一共有两个文件:
泰坦尼克号这个练习相信以前有人做过,这是机器学习里面入门的一道经典问题。就是泰坦尼克号的乘客生存预测问题。
train 有 891 个人, 咱们来简单的看一下,这些人有一些特征。
字段 | 描述 |
---|---|
Passengerld | 乘客编号 |
Survived | 是否幸存 |
Pclass | 船票等级(有些特征标注的英文) |
Name | 乘客姓名 |
Sex | 乘客性别 |
SibSp | 亲戚数量(兄妹,配偶数) |
Parch | 亲戚数量(父母、子女数) |
Ticket | 船票号码 |
Fare | 船票价格 |
Cabin | 船舱 |
Embarked | 登陆港口 |
有一些人当时非常幸运被打捞上来了,还有一些人就溺水死亡了。在统计这些乘客信息的时候也知道一些人当时的下落是幸存还是溺水,那我们就把这些数据用于训练。还有一些,有500 多人下落不明。所以根据以往的预测模型来判断一下这 500多人如果在原来的过程中到底是否死亡。
首先要做数据探索。把数据加载进来看看这个数据长什么样、有没有缺失值,有缺失值的话可能需要做补全数据。可能也有些是英文的类别,需要把它做一些编码,编码之后才能放到模型里面去,喂给模型之前也可以做一下特征选择。
不是所有的数据都放到模型里面,选择一些你认为重要的特征,好在可视化的过程中可以方便我们来做了解。
比如说,我们最关心的是 label字段,就是有多少人是溺水死亡的,有多少人是幸存的。这里我们用了一个饼图:
1 |
|
我们是用Survived
这个 label去取了它的value_counts
,就是求它的分布,类别特征的每一个取值的分布。然后咱们的kind 就用 pie的形式展现。这样就一目了然,知道大部分人应该都是溺水死亡了,0应该就是溺水。
还可以再分析一下Pclass
,代表的是船票等级。船票等级和Survived
就是幸存的人数之间的关系。
1 |
|
从这里的关系里面能看的出来,感觉1
的 Survived更大一点,这个 1应该就是一等舱。所以在当时的海难事件发生过程中,如果你是一等舱,也就是最好那个船舱,那你的生存概率可以达到60%多。二等舱的概率呢是 40%多,三等舱的概率只有 20%多。
所以从这个数据里能看出来,Pclass
是一个非常关键的特征。
还有一个特征叫做Embarked
,就是登陆港口。登陆港口里面也可以拿它跟Survived
来做一个关联,一样,通过条形图来做个呈现。
1 |
|
还有之前给大家讲到的热力图,热力图可以用于相关性系数的一个呈现。数据里面有很多的特征,包括男性女性、登陆港口、船票价格、年龄、船舱等级等等,大概有10 个维度特征。这 10个维度和其他的这些特征之间的相关性的系数可以做个计算。
1 |
|
可以看到对角线为 1,对角线就是自己跟自己,自己跟自己之间的是完全相关的所以它为 1。
还有哪些?可以看到有些地方是非常深,也能引起注意,代表-1。这个-1 就是male 和 female 等于-1。-1代表什么含义是负相关,是完全反着的一个关系。
比如说如果你是 male,一定不是female。负相关是不相关?并不是,负相关不等于不相关,负相关本身也是跟相关性是有关系的,但是它不是正向,它是反向的。
male 和 female 很明显你只能是一个,是 male 的情况下就不能是female,所以它是一个完全的负相关。
那从这个例子里面还能找到哪些关联关系?一般会选哪些值来做判断?我们大部分人可能都会说要找那个数值高的,那个颜色浅的,深的不看。能这样看吗?这张热力图怎么判断,这张热力图要找顶部和底部的,要找绝对值最大的。
除了颜色很浅以外就是颜色很深的,可以看到负值绝对值比较大的地方颜色很深,S和 C,这两个是一个负相关的。还可以看到 Pclass 和 Fare之间也是一个负相关。想象一下也差不多,Fire代表是船票的价格,船票价格越贵它的 Pclass应该越小,等级就越高,一等舱就是最好的那个船舱价格肯定会越高。这些就可以通过相关性系数来做一个判断。
除了这种可视化后面还有一个可视化,我们用的是 Featureimportance。机器学习里面有一些好处,用传统机器学习可以把特征的重要性去做一个输出。这样我们就知道在预测这个模型过程中哪些特征起到关键性的作用。
我们下面就来写一个小例子
1 |
|
训练过程用的是决策树,只要把这个类实例创建好以后 fit一下,然后就可以找到 featureimportance,代表我们特征的重要性。这样我们把这个 feature importance给它做一个排序,然后呈现出来,后面再做个可视化。然后展示了一个barh
条形图,male在整个决策树里面使用的频率会比较高,所以它是importance,重要性比较高的。其次是 Age 等等。
然后决策树的可视化我们可以用一个包做个呈现,可以用pydotplus
来呈现出来。
1 |
|
可以看到这张图,现在是生成了一个 PDF的文件。这个决策树很大,我没有限制这个树的深度。
放大一点,我们可以看到它的一些细节
x[1]
应该是等于第一个特征,跟 2.5做个判断,如果是小于等于 2.5就会走到左边,大于的话会走到右边。再去判断下一个值。所以它一层一层判断,就等于最后的一个叶子节点,告诉我们到底是生存还是死亡。这个就是我们决策树的一个概念。
之前的机器学习课程中给大家重点讲过决策树,决策树应该是属于十大经典机器学习的很重要的一个方法,一般是来做分类或者是回归值的一个预测。
举个例子,我们在这边想要做一个是否打篮球的一个分类器,我们先采集了一些数据
每条数据都有一些特征,还有一个 label 项。这个 label就是去打篮球或者是不去打篮球。决策树就是要找到我们决策的一个依据,比如说天气作为主要依据,天气可能就会有晴天、阴天和雨天。晴天过程中,在往下又有温度、湿度和是否刮风,最后找到叶子节点,叶子结点就是最终的一个结论,去打篮球还是不去打篮球。
那这张图我只是画了个意思给大家看看,并不是一个严谨的决策过程。
其实在生活中也有很多决策的一些含义,有没有人以前看过一个电影叫做志明与春娇?这个电影的主人公曾经做过一次决策的时候就采用了决策树,我专门还找到了那个决策树的一个看板给大家看一下。
当时主人公想要决定到底是去约会还是不去约会,就拿这个看板,X就是不去约会,✔️就是去约会,中间还有个循环,循环就是决策再重新走一遍。这个小球就会从上到下一直落,就会有可能落到下面一个随机的答案上。那最终就只有是和否两个决定,因为中间是重来一遍。
那其实这个看板是我们生活中可能会用到的,在分类器里就是不同的树,每一棵树的分支都是有明确的含义的,要找到属性的判断的依据。一般来说许多树是两个XY,就是「是和否」这两个。
那决策树怎么去做判断?决策树里面的节点有根节点,内部节点和叶子节点。想要构造这棵数的时候在思考的问题就是哪一个属性作为根节点,中间的这个属性怎么去选。这个过程中会用到一个叫做信息的概念。决策树的分裂,构造这棵树就是要找这种纯净划分的过程,也就是说让目标分歧最小化,大家的结论尽量的一致。也就是说,「纯」就是让目标变量的分歧最小,这种方式,我们认为构造是更合理的。比如说:
第一个集合就是这个集合的叶节点是 6 次都去打篮球,6个样本每个样本的结论都是打篮球。第二个也是 6 个样本,4 个打篮球 2个不打。第三个的是 3 个打篮球,3个不去打篮球。那么请问,如果按照纯净程度来划分哪一个集合的纯净度会更高呢?很明显的集合1,6个都是打篮球没有分歧,所以第一个纯净度更高。其次是第二个,最差的是第三个,因为大家的分歧最大。
我们就简单给大家说一下这个原理,有的时候如果你要去面试对方是一个传统企业,用到决策树的话有可能会问到。
决策树就是找纯净划分,所以我们先需要用一个统计量去计算纯净度。这里用的一个统计量叫做信息熵,信息熵的概念代表的是信息的不确定度,不确定度正好是跟纯净度相反,提升纯净度的另一个含义就是降低信息熵。
在信息论中,随机离散时间出现的概率存在着不确定性,为了衡量这种信息的不确定性,香农引入了信息熵。
\[\begin{align*}Entropy(t) = - \sum_{i=0}^{c-1}p(i|t)log_2p(i|t)\end{align*}\]
这里 p(i|t)代表了节点 t 为分类 i 的概率,
决策树就要去分裂,分类特征有很多种可能性,我们把所有的可能性都做一个遍历,找到那一个信息熵可以降低的,这样不确定性不就少了吗。
所以就是选择第一个,我们可以来简单计算下就知道,这次我们不用上面的三个集合,重新假设2 个集合,因为第一个集合太纯净了,我们掺入一些不确定性再来看:
计算下来,集合的信息熵:
\[\begin{align*}Entropy(t) & = - (\frac{1}{6})log_2(\frac{1}{6}) -(\frac{5}{6})log_2(\frac{5}{6}) = 0.65 \\Entropy(t) & = - (\frac{3}{6})log_2(\frac{3}{6}) -(\frac{3}{6})log_2(\frac{3}{6}) = 1 \\\end{align*}\]
可以看到,第一个集合的信息熵为 0.65, 第二个集合的信息熵为 1,集合 2的信息熵更大一些,信息熵越大,就表示不确定性越强,所以集合 1就会更好一点。
这个就是决策树的原理,这里就不详细的展开。之前咱们机器学习的课程里有两节专门将决策树和随机森林,大家可以去仔细的学习下。
现在只需要知道它背后是采用了一个纯净度的划分的标准。
决策树有三种算法,比较常见的 ID3、C4.5 和 CART算法,这三种算法都是要去找纯净划分。但这三种也有些区别,ID3要找信息增益,也就是 Gain 值,就是信息不确定性下降最快的那一个。信息增益Gain = 父亲节点的信息熵 - 分裂后字节点的信息熵。Gain > 0,说明信息熵下降。
C4.5 做的不是一个绝对值的,做的是个比值。因为 ID3虽然方法简单,但是它有可能带来一个问题,就是把数值类型很多的属性倾向于做优先的选择。
如果我们把 ID 放到模型里面来做训练,ID 代表每个样本的ID,第一条数据比如说 ID 等于 1,第二条数据 ID 等于 2,请问这个 ID的属性对于我们打篮球的决策有没有判断价值呢?放到模型里面应该是没有价值,我们不能把ID 放进去。但是对 ID3 来说它会倾向于选择这样的属性。
那么怎么样做改进呢?就在 C4.5里面做的改进,它把信息增益率放进去了。因为属性值很多,增益大了,属性熵更大,所以它新增益率反而是小的。
信息增益率 = 信息增益 / 属性熵
这样,我们在 C4.5 里面就不会选择那个属性值多的。
以上这些就是决策树的概念,可以把它理解成为我们一个判断的标准,通过找属性让信息的纯度更加的纯净一点。
如果你要绘图的话,要做决策树的看板就要使用到一个工具包,通过 pydot 和GraphViz 实现决策树的可视化,把图的分裂情况做个呈现。
1 |
|
那到这里,我们就把决策树给大家讲完了。其实决策树只要知道它背后的逻辑是按照数的分叉来去走的,想象一下那个《志明与春娇》那个看板就可以了。
一般也很少有人把树打印出来。因为绝对数它往往很大,如果你有十几二十层的话把它打印出来了也很难去理解它的内部构造。所以通常我们只要知道它是一个有逻辑性的一个可视化的分类过程就可以了。
链接: https://pan.baidu.com/s/1ze8eJnhNYoqOEg5XZkG3CA?pwd=qkma提取码: qkma --来自百度网盘超级会员 v7 的分享
[TOC]
Hi,你好。我是茶桁。
今天想给大家讲的是关于数据的可视化。在工作中很多时候我们不光要计算结果,还要把结果呈现出来,最好是一种图形化的方式。因为这样领导会更容易去理解。
此外在工作中我们也希望自己对数据更有感觉,因为数据量有的时候会非常大,对于几十万上百万的数据的不可能一行一行看,所以可视化就是一个非常重要的工具。
首先一起来思考一下可视化都有哪些视图。这些视图我们把它归成四大类,每一大类里面都有一些可以选择的图表样式。在Python 要做可视化离不开两个工具箱,一个工具箱是 Matplotlib, 一个是Seaborn。
这里做一个调查,读者中的小伙伴们有没有使用过这类工具?大家可以在下面留给我,看一看大家之前有没有做过。如果你用Python 做过可视化基本上都会用这两个工具。
我相信大部同学应该还是有一些了解的,没用过的到时候看一下我课件上的一些代码,这个代码直接运行的话是可以运行出来,在运行之前你需要安装这两个工具。
如果你的图表比较复杂,一张图上面要把它分成几个象限,那么你们会使用到一个工具叫subpolot,就是子图的意思,所以它是专门去画一些小的图形。
后面几天咱们还会去做一些可视化的样式是跟词云相关,这次带来一个项目,看一看怎么样用词云展示去呈现出来那些关键词。
还有可以对树来做可视化。决策树本身是一种机器学习的模型,这个模型你脑海中可以想一想,它会分成两个叉,每个叉都有一个判断的标准。那这棵树也可以给它用可视化的工具来呈现出来。
最后我们还是会有一个项目,这个项目是在阿里云的天池上的一场比赛。我们主要的任务就是用这样的一个数据集来看一看怎么样去用可视化的方式方便你去了解它们。
接着,咱们会继续来看 Python 的可视化。Python的可视化如果你要封装成一个产品,这个产品指的是发布的产品,可以由用户来使用,那么你会使用到Flask 这个工具。
Python 在做这个搭建所有框架里面除了 Flask 还有一个叫做 Django的。我推荐大家使用 Flask,因为它更加轻量级。如果你不写 Python代码,想要更方便的直接通过拖拽的方式来完成可视化的话,我们会使用到一些PowerBI,类似于这样的一些软件的 BI 报表,还有我一直以来比较推崇的Tableau。
所以在之后的课程中我们看一看一个完整型的数据产品是如何搭建出来的。这里也给大家一个例子,就是我之前做过的一个例子,通过Flask 加 echarts 可以搭建一个肺炎疫情的可视化的 Dashboard。
不可能一口吃成一个胖子,咱们一步一步慢慢来。首先我们一起来思考一下,可视化的EDA 都有哪些作用?我们先来看看什么是 EDA,EDA这个名词大家可能在网上会看过,我们在做数据处理的过程中第一项就是要做一个explore dataanalysis,叫做探索性的数据分析。所以探索性的数据分析可以通过可视化的方式帮我们来做呈现。
那我们来思考一下,可视化都有哪些方式?它可以说贯穿到我们整个数据分析的始终。
在数据加载之后的预处理环节中要做一些探索,这个探索可以用可视化的图形来做。机器学习的过程中有没有必要去做可视化呢?如果你要预测一个模型,训练一个模型需要花一天的时间,那这一天20 多个小时时间之内你要了解机器目前运行的程度是怎样的。所以你可以把一张loss的曲线图给它进行一个呈现。所以在机器学习的训练过程中可以做可视化。训练完成之后你也可以把结果呈现出来。
所以在训练前、训练中和训练后都可以使用可视化的工具。那这样的可视化的工具都有哪些?
基本上,我们可以将可视化视图按类别区分一下,分成比较、联系、构成和分布。
图表可以说是非常非常多,如果我们要去学的话有三种必知必会的,在使用过程中用的场景非常的多。第一个散点图,第二个是看趋势用折线图,还有看对比用直方图。这三个是必须会的,那我们可以再加一个饼图。这几个图形基本上我们大概率都会用到。
除了这些图还有其它的一些图,比如说像雷达图,然后做一些地图的呈现等等都可以去来做。那么在Python 中我们会用到两个包,一个是 Matplotlib, 一个是 Seaborn。
那我们第一眼看到 Matplotlib的时候会想到什么工具跟它有关系?不知道有没有小伙伴在读研究生的时候会用到一个数据仿真软件,叫做MATLAB。
这个软件有用过的同学可以给我留言,我们一起可以交流一下。这个软件如果你在做仿真实验的时候研究生期间有可能会用到,但是后来可能用的没有那么的多了,因为之前有过一个针对哈工大的名单事件。
我们在 Python 里面使用的 Matplotlib 本身是个开源工具,它不是商业化的MATLAB,是 MATLAB的一个竞争对手开发的一个工具。它把这个工具开源出来,作用是帮你来做可视化的呈现。
在可视化呈现的编码过程中你会发现它跟 MATLAB很像,比如说我们要设置子图,画图等等。好在 Python就是一个开源的生态系统,基于它我们基本上可以实现你想要实现的任何的图表,都是可以完成的。
那 Matplotlib 和 Seaborn 之间有没有区别呢?你可以把 Matplotlib认为是一个基础版本,它是个底层的东西,而 Seaborn 是一个基于 Matplotlib的高级封装,它在 Matplotlib的基础上去封装了一些代码,让你使用起来会更加的方便。
下面我们给大家看一下这些代码的实践的过程,第一个是散点图,先来随机生成的一些数据
1 |
|
如果你用 Matplotlib 的是用 scatter,scatter 代表散点的含义。把 x,y在坐标轴上绘制出来。
1 |
|
marker代表的是你绘制的这个样式,它是一个x
的样式。这个就是我们的第一张图。
第二张图用的是 seaborn 这个工具箱。这个工具箱我们先用了一个 DataFrame的样式。DataFrame 平时应该是在 Python里面使用频率最高的。你要处理任何的数据基本上都离不开 DataFrame。
我们可以直接在 DataFrame 的基础上去喂给 sns,这样我们直接把 DataFrame传给我们的 data,然后指定 x 轴和 y 轴。
1 |
|
kind 我们用选散点图 scatter,最后给它呈现出来。
两相对比之下,matploylib 的图就没有那么方正,seaborn似乎更没罐一些。
seaborn的散点图上面和右面都有多出来的一部分,它是额外做了一个辅助的标记,代表着数据分布情况。那这可以看到,两边和上下的分布的比较散比较少,中间的部分数据比较集中,也比较多。它是一个分布直方图。
这是我们第一个,散点图的一个样式。
第二个图也是使用场景比较多,就是折线图。什么情况下会选择折线图呢?我们接下来的一个数据模拟,是从1990 开始到1910。所以它应该是用于连续数据,主要用到跟时间序列相关,或者用的是一个叫做趋势项,要看趋势就离不开折线图。
我们还是先来准备一下数据:
1 |
|
然后依然是先来看看 matploylib 的呈现:
1 |
|
接着是 seaborn
1 |
|
这个折线图里,x 是代表时间轴,y是它的统计量。一起来看一看这两张图,呈现出来的样式谁会更加美观一点?说实话,看不出什么差别,基本上都是一样的。
第三个是条形图。条形图有的时候也叫直方图,直方图就经常用于不同类别之间的一些对比。那这两种工具都可以做直方图的呈现,我们还是一样,进行数据生成后来看看两个的区别:
1 |
|
上面是 Matploylib, 下面是 Seaborn。说实话,没啥区别。本来 Seaborn应该在柱子的颜色上有所区分的,我印象中以前使用 Seaborn是默认带颜色的,现在这个 0.13版本不知道是取消了还是用法和以前不一样了。
前面给大家讲完了散点图、折线图和条形图,下面咱们来看看箱线图。我们一起来看看,先模拟一下数据:
1 |
|
这个数据模拟了 ABCD 四个类别,每个类别里面有 10个数据。然后我们来画图:
1 |
|
1 |
|
这个图的区别就比较明显了。有些人喜欢朴素一点的,有些人喜欢颜色显明一点的是吧。
我们看一看某一个单独的箱子,上面这个线和下面这个线,这两条线代表什么含义?上面线和下面线你可以把它理解成一个是Max 一个是 min,或者把它理解成是 100%和 0%,数据正常范围的 0%到100%,也就是极值。
再来看箱子本身的上下的这两条边线
这两条线代表的含义,一个应该是四分位数的 75%,还有一个是25%。这个就跟股票阴阳线是差不多的。那箱子里面中间那条线,就是数据的50%。
所以箱线图是把它的数据特征从 0%、25、50、75 和 100 都呈现了出来。
最后还有,我们注意看 label B的位置,在箱子下面有一个圆点,它在箱子的外面。那这个点是什么?那这样的一个点就是异常值,称为离群点。
所以箱线图可以用于我们特征的统计,也可以帮你来判断这样的一个特征里面有没有一些异常值,你可以把它用于异常值的检测的使用。
那下面一个图我们就很熟悉了,我们来看看饼图:
1 |
|
这张图只有 matplotlib 的,是因为 seaborn里面我没有找到有相关的饼图的绘制方法,只有在 matplotlib 里有绘制,通过pie 方式就可以给它绘制出来。
然后我们来看看热力图
1 |
|
那热力图我们在之前的课程中就有看到过了,当我们需要找数据之间的相关性的时候,就能看到这张图。热力图的含义就是把数字转换成为颜色。
我们看右边的热力值的那一条柱子,颜色比较浅的部分,它的数值就比较高,如果数值比较低的话它会比较深。所以我们把这个数值和颜色之间做了一个映射。
除了相关性系数之外,我们看到天气预报会在地图上面做呈现。如果一个地方37 度,有个地方是 15 度、10度,观看数字没有这么直观,但如果把颜色给你标识出来,温度很低的地方用蓝色,温度很高用深色的,暖色的。这样就非常直观。知道全国每个地区哪些温度高,哪些温度低。所以热力图它就是一种非常直观的形象的方式来了解数值的大小。
那接下来的这张图就是雷达图,我们也可以叫它蜘蛛图。我们说马龙是六边形战士是吧,那这个六边形就是在雷达图上做了展示。还有,我们的英雄比赛完以后可以通过它的画像,可以比较这几个维度的高低之分。它是做多维度的展示,它会把6 个维度指标用一个雷达图来做个呈现。
我这边写数据就用咱们的英雄的比赛来做,六个维度:KDA、生存、团战、发育和输出。
1 |
|
然后在画图之前,我们需要做一些准备工作,包括数据准备,角度和状态值。这里需要注意的是,我们虽然是6个维度,但是需要多生成出来一个坐标点和第一个点重叠,否则连线是无法闭合的,那这个闭合点包括标签,角度和数据都需要做:
1 |
|
然后我们就可以用 matplotlib 来画雷达图了,画图之前,我们要知道matplotlib显示图像如果有中文和负号,基本会显示乱码,也就是显示成一个个的方框,这个要提前处理一下,设置一下中文默认字体和解决负号问题:
1 |
|
那我们可以很直观的看到,这场比赛我们的英雄团战的指标很高,那哪个指标万的不太好呢?就是KDA 和输出都不是太好,比较低。
所以通过雷达图就非常一目了然,它主要用于多维度的判断和分析。那其实雷达图在Python里面没有专门的工具箱,我们这一段代码的本质含义就是自己来画一个圈把线段给连接起来,最终再去设置一个阴影绘图。
下面我们来看一个图,这个图平时我们也有看到过,但是不常见。这个图叫做二元分布图。
二元分布图主要用于两个特征之间的一个关联分析,这个图呢,我用了一个数据集来做示例,咱么弄一个美国航空公司的一个数据集。这是在seaborn 里面自带的一个 dataset,先把数据加载进来。
1 |
|
顺便我们观察了一下这个数据,可以看到这个数据里一个是有 144个数据行,一共有 3 个特征,包括year
,month
和passengers
。
观察数据是让大家知道,之后我们如果要用 seaborn来画这个图的画,我的数据应该是整理成什么样。
好,接下来,让我们画三张图,一个是我们上面学过的散点图,一个是核密度图,一个是Hexbin图,先别管这些图都用来做什么,让我们先画出来,看着图才好去解释:
1 |
|
我们可以看到,这三障图在数据上一模一样,不同的就是最后的kind,我们使用了不同的类型。我们从上到下依次粘贴一下图像来看看。
我们想要判断两个指标,年份和 passengers这两个指标它们呈现的关系。第一张图,散点图可以看一看,可以看到随着时间的一个增长,乘客的数量也在线性的增长,是一个非常明显的一个趋势。这样就可以把两个特征之间的关系用一个散点图来做呈现。
那二联分布的关系除了散点图以外,kind还可以指向kde
,它叫做核密度图,还有hex
叫做蜂窝图。基于图像我们能看出来和散点图呈现出了一个对应的关系,不同的是,在整个增长的趋势里,我们还可以看到最大的增长是在什么区间内,虽然散点图旁边的直方图上也能看得出来,但是核密度和蜂窝图就更直观的显示了具体的范围。
这是做了二元的一个关系,如果要看一元关系呢?一元关系里面直接用一个KDE 核密度就把它的密度曲线给它呈现出来了.
1 |
|
大家有没有发现这个曲线很熟悉,我们再来一个直方图大家观察一下:
1 |
|
是不是曲线是一样的?这里多说一点,distplot
这个方法在之后的seaborn里会被删除,被取代的是displot
和histplot
方法。新的displot
方法里,kde默认并不是 True的状态,需要自己设置kde=True
才可以看到曲线。
回到我们这张 kde的一元呈现,这个密度曲线的含义,可以想一想,我们用的是一个passengers
,现在的密度体现是一元的,我们使用的数据集是从1949 年到 1960年期间每个月份美国航空公司的乘客的数量,那么这个月份的乘客的数量它的密度曲线是怎样的可以用kde 给它呈现出来。我们可以从中看到,200 到 300之间的这个人数的密度最大,也就是出现的概率最多。哪个概率最低呢?700以上的,还有就是 50 以下这样的概率是比较低的。
所以密度图是可以帮你来预估一个它的分布,这个一元密度图就单独预测它的出现的一个分布,二元呢就是这两者之间的一个相关关系。
在一元分布中,纵坐标就是 probability, 也就是我们的概率。
现在我们来想想,这整个的阴影面积,就是红色的曲线圈定的部分加起来应该等于多少?我们所有的情况把它做一个累加,它的面积累加求和等于1。等于 1 的话,上面每一个点代表就是它的一个概率值。
比如说 200 的概率值,在这里大概对应出来就是 0.003,也就是百分之0.3,所以他出现的概率就是 0.3%。这个数值看起来似乎很小,但其实后面还有201、202,并不是我们看到的 200,300,200 到 300 之间其实还有 100个数值,把所有的加到一起就等于我们整个面积为 1。
所以这个概率分布也挺重要的,给了这个数据集,我们可以预估出来。
除此之外,我们还有另外一种呈现方式:
1 |
|
这样成对就把所有的组合都给你呈现出来了,这个数据集比较简单,只有两个特征,所以如果你用成对关系的就是2 乘以 2。x 轴有两个,y轴有两个。它的好处就是一次只用一条命令就可以把所有的可能性的关系给呈现出来。
现在我们来思考一下,如果换一个数据集,这个数据集里面有 5个特征,那么现在用pairplot
来做呈现,这张图里面会呈现出来多少种子图?应该是5 乘上 5,就是 x 轴有 5 个特征,y 轴也有 5个特征,每个特征和另外一个特征之间一个是 x 轴一个 y轴,它们都可以呈现出来,所以它一共有 25 张图。
这 25张纸图就不需要一个一个自己写了,直接可以用一条命令pairplot
给呈现出来。在这个呈现过程中就可以一目了然的更容易发现哪两个特征之间是有关联关系的。
除了单张的图以外,我们还可以用一些分块的方式给它做一个细分。matplotlib一个 figuer 对象可以包含多个子图(Axes),可以使用subplot()
快速绘制。它有三个参数,行(numRows),列(numCols)还有当下的座标位置(plotNum)。
这张图不是很规则,如果你要划分的话可以分为上面一行,下面一行,然后左边是一列,右边还有一列,先这么去设置。
第一张图左上角的图我们去定义 subolot 内的参数,应该是221,物理含义就是我们这张图的位置是把一张大图拆成了 2 乘以2,它的第一张图,就是 221。右上角这张的参数那就应该是 222。
下面这张就有点不一样,行依旧是两行,但是列没有必要拆成两列。因为这个是把单元格合并到一起的,所以是把它看成了一列,所以应该是212.
这三张子图的整个代码如下:
1 |
|
你们可以拿来自己运行一下,就把上面那张图画出来了。subplot就是画网格,然后数到底属于第几块网格。
比较规整的就如下面这样:
这个应该就简单一点,从左到右从上到下依次就是221,222,223,224。这样你可以把一张大图分成几张小块,方便做绘制。为什么有的时候这么画呢?是因为图太多了,一张一张画会给你顺序排列,所以可以把它一张大图绘成几个小图的一个拼接的方法。
那以上就基本上把我们可视化用到的图表样式给大家讲完了,当然还有一部分没有讲到。我之前在讲Python 基础的时候,有专门拿一章来讲过Matplotlib,关于如何绘制图表大家可以去看看那一章,算是一个工具的使用手册,比较基础的部分。
在使用过程中你不需要背下来,只要把我的这两篇文章(之前 Python 部分的Matplotlib和本章)当成你的手册,可以下次使用的时候直接套。比如说直方图、散点图、折线图怎么做,直接套用就可以了。
]]>[TOC]
Hi, 你好。我是茶桁。
上节课,咱们讲了一个股票的指标:MACD。在趋势行情里面它应该还是有效的指标。它比较忌讳动荡行情,比如说它一会上升一会下降,那还没有等12 天过完,就是均线还没有画好它又马上变成了另一个行线,这样 MACD有可能会失效。
这个问题我们大家自己去思考一下,如果你采用这个策略在过去一段时间里面选择一些股票来进行购买的话,能不能让它的收益率大于60%?有这种可能性,选好股的话确实在过去一年交易里面收益率是有可能大于60%。
那我们之前的课程里,带来了 Fintech的应用场景,同时又对其中一个量化交易的场景做了一个简单实验。今天,咱们来另一个Fintech 的场景,同样也是有数据,这个数据是来自于一场比赛。
这个比赛是关于贷款违约预测的一个比赛,来,我们一起回想一下,在这个 BI系列课程开始的几节课里咱们讲的模型、机器学习的神器。大家还记得是哪两个神器吗?其实严格来说的话应该是三个神器。
XGBoost 是第一个,LightGBM 是更快的,还有一个是跟分类相关的CatBoost。 ## 案例分析
接下来,我们来看一个问题:
零基础入门金融风控-贷款违约预测
这里有 47 个指标,120 万贷款记录。其中 15 列为匿名变量,并且对employmentTitle、purpose、postCode 和 title 等字段进行了脱敏。
给你两个数据集, 一个是训练集,一个是测试集:
训练集为 train.csv 测试集为 testA.csv 提交格式 sample_submit.csv
我这里就不提供数据了,有需要的可以用阿里云帐号自行去获取:
我们来看看字段:
字段 | 说明 |
---|---|
id | 为贷款清单分配的唯一信用证标识 |
loanAmnt | 贷款金额 |
term | 贷款期限(year) |
interestRate | 贷款利率 |
installment | 分期付款金额 |
grade | 贷款等级 |
subGrade | 贷款等级之子级 |
employmentTitle | 就业职称 |
employmentLength | 就业年限(年) |
homeOwnership | 借款人在登记时提供的房屋所有权状况 |
annualIncome | 年收入 |
verificationStatus | 验证状态 |
issueDate | 贷款发放的月份 |
purpose | 借款人在贷款申请时的贷款用途类别 |
postCode | 借款人在贷款申请中提供的邮政编码的前 3位数字 |
regionCode | 地区编码 |
dti | 债务收入比 |
delinquency_2years | 借款人过去 2 年信用档案中逾期 30天以上的违约事件数 |
ficoRangeLow | 借款人在贷款发放时的 fico所属的下限范围 |
ficoRangeHigh | 借款人在贷款发放时的 fico所属的上限范围 |
openAcc | 借款人信用档案中未结信用额度的数量 |
pubRec | 贬损公共记录的数量 |
pubRecBankruptcies | 公开记录清除的数量 |
revolBal | 信贷周转余额合计 |
revolUtil | |
totalAcc | 借款人信用档案中当前的信用额度总数 |
initialListStatus | 贷款的初始列表状态 |
applicationType | |
earliesCreditLine | 借款人最早报告的信用额度开立的月份 |
title | 借款人提供的贷款名称 |
policyCode | 公开可用的策略_代码=1新产品不公开可用的策略_代码=2 |
n 系列匿名特征 | 匿名特征n0-n14,为一些贷款人行为计数特征的处理 |
现在我们想要评估它,可以看一下到底哪一个是我们的 label? 贷款的id、金额、期限、利率、等级等等,这些都是业务指标。最后有一个匿名特征,15个。还有一个isDefault
,代表违约,1 是违约,0代表是正常。所以我们预测词段应该是最后一个词段叫isDefault
。
这是一个什么问题?我们可以思考一下,这是机器学习里面非常经典的一个问题,叫做分类问题。确切说这是个二分类问题,那么二分类问题可以提交结果是0 和 1,也可以提交一个概率。如果你提交概率,评价指标是AUC提交哪一个会更好?是提交 0 和 1具体的分类结果好,还是提交一个概率结果。如果预测出来是个概率值的话,把它转化成了0 和 1,有可能 AUC是不高的,这是一个小的技巧性的问题。所以建议大家是以概率值来进行提交。
0 和 1是实际的结果,从这个物理含义上来说的话它确实有违约和不违约两个最终的结果。但是我们要去预测,你做分类任务也可以得到一个概率值,这概率值是0.95,就是他违约概率是 95%,还比较高。0.06 就是他不太违约,可以写 0 和1,也可以写上它的概率值。概率值通常情况下 AUC的结果会更大,就是你的排名会更靠前。
这些可以自己做个对比,因为它本身是一个在线的比赛,你可以把它转化成为一个分类结果再去看一看AUC 会变成多少。
题目就是这样一个题目,去预测一个二分类的任务,是个跟贷款违约相关的场景。除了我们要知道这种分类模型可以用XGBoost 的 LightGBM 以外,关键是要统计它的一些特征。
梳理一下整个过程,第一个要做数据加载,前期数据探索,探索一般来说我们要看字段label,跟 label相关的,还有就是有没有缺失值,唯一值的个数也可以做一些探索。
sns.countplot()
函数,以 bar的形式展示每个类别的数量。
探索以后可以做一些预处理,中间如果有缺失值要在模型里面进行补全,补全也会分两种情况,第一种情况叫做数值特征:num_features
,第二种叫做类别特征:cat_features
。
以年龄为例,就是一个具体的数字。还有一个叫做收入,类似这种跟钱相关的指标用什么样的方式来做补全?两种方式,一般平均值和中位数。我个人更倾向于使用中位数。
举个例子,我们回头看一下刚才字段里的贷款金额,不同人的贷款金额可能不一样。不知道有没有人尝试过网络贷款,跟金额相关的其实差别是比较大的,均值会偏高,你想你借500 块钱、1,000 块钱,有也会有人在贷款软件上借 20 万、30万。这种如果你放到均值里面,差别会特别大。
再举个场景,收入,你们公司的收入平均收入是多少?如果你把马云放进去,你想想马云的收入高不高?一下子平均收入个人都是上亿的,这样大家都变成了异常值。
所以收入字段这种字段是不能用平均值的,用平均值做补全是没有意义的。因为那个缺失的值很有可能不是马云,你被平均了。所以用的是中位数,中位数会更加的过滤异常值,金额可以用中位数补全。
除了收入以外,一般来说年龄这种平均值差别不会太大。你可以自己做个实验,基本差不多。
第二种是类别特征,那众数在类别特征里做补全比较好,因为在类别特征里面不是一个连续值。
举个例子,gender代表性别,请问性别能用平均值补全吗?肯定不行。因为性别只有两种情况,所以两种情况我们要找到那种情况最大的,如果他男性居多,我们的补全可以倾向于用男性来去做补全。
所以在类别特征里面一般来说是用众数好。
这是前期的数据处理,类别特征在整个程序里面还要做一个处理叫做数值编码。因为类别特征它原来是字母类型,数值编码有一个工具叫做labelEncoder
,这个叫做标签编码,标签编码可以自动帮你进行标签。但有些情况下,有些类别是有方向的,什么是类别方向呢?有一个叫做贷款等级:grade
,贷款等级有方向之分,大小从a、b、c、d、e,a 可能是优质的,e 可能是劣质的。贷款等级还不能直接让他用labelEncoder,需要自己去写一个映射关系。
日期类型的处理,日期在数据集里面经常会出现,一般怎么处理呢?举个场景,2023年的 7 月 5号这是个日期,直接把它喂到模型里面是不能用的,我们要抽取出来它一个多尺度特征,多尺度可以按照年月日分别抽取出来,2023一个特征,7 月一个特征,5 号一个特征。
那我们思考一下,还有没有其他多尺度特征可以抽取?那今天是周六(以我写这篇文章的日期问准,而非发布),周六在整个的时间里面也算是一个特殊的时间,你可以把这个特征叫做weekday。还有哪个特征?是不是还可以再取一个叫做 holiday,或者叫workday。因为有些周六它是个工作日。
所以从业务场景出发,weekday 是周几,还可以出一个叫 workday 或 holiday来判断到底是工作还是不工作,这些也都有关系。可能跟贷款违约没啥关系,但是跟什么交通流量,或者跟商业里面的销售都是有关系的。举个例子,比如说电影票房就是个最明显的区别,工作日不会很高,但是假日的话是工作日的可能几倍都不止。
除了这种类型处理的方式我们还有一种处理的方法,我们把它称为 diff,就是 different。
我们的日期可以做一个锚点,以一个最小值为例,什么时候开始的这项业务,假设是2023 年的 1 月 1 号这个业务开始的,那么现在是 2023 年的 11 月 28号,相比 1 月 1 号来说就会存在一个时间的diff,这样就会把它转化成一个数值类型,也是一个统计特征。
所以时间类型有两种处理方式,一种叫做时间多尺度,一种叫做时间diff。
这个方法还是比较常见的,未来你们在工作和比赛过程中遇到时间类型放到模型之前,都要采用这样的处理的方法。不一定两种策略都用,但是至少,如果要放时间特征的话,要用其中的一种。要不你用多尺度,要不你用时间diff,否则这个时间是不能直接放到模型里去的。
那这个数据集里面就有跟时间类型相关的数据,比如说有个issueDate
,贷款发方月份,这个是时间的,所以一会儿我们要采用提到的方式来进行处理。
然后就是第四个步骤了,我们要进行特征构造。大家要明白,模型的使用不是难点,因为大家都会用XGBoost 和LightGBM,参数也都是那些,区别在于前期的特征构造。我们之前有句话叫做特征决定模型的上限,而模型只是把上限跑出来而已。那么在特征构造里面有哪些技巧呢?特征构造里面有一些统计学的一些技巧。
对于那些类别变量的一些特征来说我们可以做一些统计的特征,代表这个类型的一些属性。
举个例子,比如说贷款等级我们有a、b、c、d、e,是按照分组的方式。这个分组你可以数一下分组的个数,还可以把贷款等级和违约概率isDefault
分别做一个对应。a是 0,b 是 1...
这种对应到底是好还是不好我并不清楚,但是能把 a 的 default的平均值计算出来,应该按照分组的方式求一下isDefault
这个统计变量的平均值就好了,b、c、d、e也都能算。算完以后就可以把它叫做grade_isDefault_mean
。你加这个字段,假设a 是 0.01,就是它的平均值。b 假设是 0.02,c 是0.03,这样我们就可以给它统计一个特征出来,
就是说我们可以对每一个特征去跟我们的isDefault
来做关联,做完关联以后就可以方便你去理解这个特征的一个含义。需要说明一点,这个特征一定是属于类别变量,因为类别变量是一个离散的个数,它在贷款里面假设类别 a、b、c、d、e 只有 5种,那只有 5 种每一种它的isDefault
平均值才有价值。
如果它不是类别变量是一个数值变量,把每个数值都计算出来isDefault
,请问它会发生什么样的一个结果呢?我们把这个结果叫做标签泄漏。为什么叫标签泄漏?因为在你的计算过程中我已经知道实际的答案了,知道实际答案的话就预测不出来这个结果。因为实际的情况下我们是不知道答案的,或者说实际情况下它的isDefault_mean
没有一个稳定的衡量结果。
那么对于类别变量它才能相对稳定。对于数值变量他加isDefault
能稳定吗?不能稳定。这个技巧是只限于类别变量,它才能得到一个相对稳定的一个状态特征。
然后就到了我们的第五个步骤,特征构造完了我们就上模型。模型这里用的是LightGBM。那它的参数也很多,我们在之前的课程中给大家讲过,建议大家用祖传参数,因为祖传参数不需要把时间花到调参上面。那么这里我们也用的是祖传参数:
1 |
|
我们的数据比较多,100 多万。迭代的次数也比较多,用的是 2,000轮。未来你都可以把这个轮数再进一步进行提升。
后面我们还可以做一个子模型的融合,这里叫五折交叉验证。子模型融合是什么含义呢?就是你列了5 个模型,让这 5个模型一起来去产生作用,这是一个五折交叉验证的一个策略。
那现在就一起来写一写这个代码,一起来看一看这个流程。那这个预测是来自一个真实的业务比赛的一个数据,我们可以去将数据集下载下来,自己做一个测试。就去我上面提供的那个地址去下载就可以了,我这里就不进行提供了。
咱们这次的数据主要是 train 和 testA两个数据集,先来把数据加载进来,然后简单的看一下,看这个数据长什么样:
1 |
|
我们今天简单写一写,给大家写个简单的baseline,不会写那么完善。思路也是给大家梳理清楚。
第一个模块我们看一看唯一值的个数:比如我们要看一下其中isDefault
这个特征的唯一值个数:
1 |
|
我们知道,这个特征那肯定是只有 2 个值,不是 0 就是1,它是最后的一个结果。现在,我们要查看所有特征的唯一值,需要先统计它都有哪些特征,对它的column 来做个遍历。某一个特征唯一值个数是多少用 format,然后看它的column 唯一值个数咱们依然还是用nunique()
。
1 |
|
数据集我们知道,训练集一共是 80 万,ID 的唯一值是 80万。那我们想,这个 ID 会放到模型中去完成训练吗?ID每个数值都不一样,而且它物理上面是不具备含义的,不要放到模型中。所以需要drop 掉。
同时上面也能看出来,还有关注哪些?关注那些唯一值少的,isDefault
这个比较明显,因为预测分类就是违约、不违约。
我们从打印结果来看,policyCode
个数唯一,我们其实还可以单独写一句话来去做判断,判断唯一值个数是否为1。
1 |
|
如果它的nunique
等于 1 的话我们把它打印出来。
然后方便起见,加一个特殊的符号,也是为了在打印结果中一眼就可以分辨哪些是,让它做一个提示。
找到这个结果,它告诉我们policyCode
唯一,那我们对这个数据,看一眼它的value_counts
。
1 |
|
80w 行数据都等于1,那么我们还需要将它放到模型区吗?不要放,因为放进去和不放进去是没有任何区别的。我们就把这个给它去掉。
那我们的策略目前是去掉 ID 和policyCode
,用 drop方法。我们要去掉它,那训练集和测试集就都要去,不能只去一个,需要两个都执行:
1 |
|
去掉以后, 原来的列数是 47 列,现在的列数是 45 列。
接下来,我们需要进行数据的清洗,首先我们需要对缺失值来进行补全。我们先看一看缺失值的个数。
1 |
|
统计缺失值个数这里用的是isnull().sum()
来做统计,isnull()
是对每个字段是否为空来做个判断,如果它为空的话把空的个数求和。求出来之后,现在可以看出来我们是有一些为空的词段,有些个数还挺高的。
比如employmentLength
等等,这些资料其实都为空,还是蛮多的。先不着急直接补全,我们还可以在数据探索方面再去探索一下。可以看看这个不同类别特征与label之间的一些关系,我们简单看几个比较关键的特征。带大家一起来看一看这个该怎么去写。比如说grade
,看它是不是类别特征,查看数据的类型。
1 |
|
这里用的是 info 来去做一个查看,我们可以看到grade
词段是object,一般来说就是字符串的类型,可以来对它的grade
去求一下value_counts
:
1 |
|
可以看到是是A、B、C、D、E,F、G,所以它应该是属于类别特征。那不同的类别和最终违约会有怎样的关系呢?我们以柱状图的形式展示每个类别的数量。呈现图的时候用的seaborn 里的 sns 来做一个呈现:
1 |
|
这是一个 grade 跟 isDefault两者之间来做一个判断,数据是我们的训练数据。可以看一看这个结果,怎么看ABCDEFG 这些类别跟 isDefault 之间的关系呢?因为 isDefault有两种类型,所以每一个 ABCDE都有这两种类型的柱状图。那么哪些特征是比较好,不容易违约。从这张图里面大概是能判断出来,0是代表好人,1 代表坏人.所以我们看 A 是比较好的,A 的话就不太违约。其次是B,然后再是 C,还有就是 D。然后到 E 后面违约的比例上基本差不多了,E的比例已经比较高了,F 和 G 就会更高。所以 A 到 G之间是有一个顺序关系的,越是字母靠前的 A、B、C 就越不太容易违约。
那应该还有其他一些特征都可以来去做个判断,比如我们再挑一个,homeOwnership
,借款人在登记时提供的房屋所有权状况,看它的value_counts
是 0到 5:
1 |
|
那我们觉得理论上它应该是属于什么类别的特征?看一下物理含义,借款人在登记时提供的房屋所有权的状态,它没有小数点,0-5虽然是数值,但是它应该是个类别特征。
那类别特征的话我们用的过程和刚才是一样的,来做个对比,拿这个词段作为x 轴
1 |
|
到后面其实这个值已经比较小了, 每个特征还是有一点明显的。0是比较好的,然后 1 比较差,2可能后面就逐渐的会下降。这些是属于跟房屋状态的一个特征相关的。
这些维度其实都可以做判断,我们也发现出来有些类别特征是可以找到isDefault 的一个平均值的。
再看下面这个特征,这 0-13 看起来也像是类别特征
1 |
|
这是借款人在贷款时的贷款用途类别,跟刚才过程是一样的,依然是来看一个对比:
1 |
|
这个有点类似于像 A 到 G 的那个感觉。这个虽然是数值,但大家想想是不是更倾向于看成一种类别的计算,我们想,类别最后也是要把它转化成一个数值类型的。因为这个统计个数没有小数点,所以我更倾向于把它看成是一种类别。就是已经转化成数值之后的一个类别。
基本上都看完之后我们看下一步,我们要设置一些状态,设置数值类型,然后来去做一些缺失值补全。
那下面我们就要找一找哪些是数值类型,哪些是类别特征。可以借助它的dtype,那我们之前看过数据的 info, 一般来说 float应该都是属于数值类型,这个是毫无疑问的。int类型有些可能要属于类别之后的一个编码,就是它已经是类别加了 labelEncoder,所以我们先用 float 来去做一个计算。
1 |
|
这些肯定是数值特征,因为它是属于 float。
计算完num_features
,我们再计算cat,找到类别。类别特征有一个简单的方法就是非 float 类型。包括了int,包括了object。那怎么去写?这个就和我们的数值类型的获取正好反过来:
1 |
|
这回我们用的是exclude=float
,include是是
,exclude 就是非
。这样不是我们 float类型的部分都等于我们的类别类型。这样类别特征就求出来了,这些都属于类别特征。
下一步就要做缺失值补全了。在模型预测之前,最好把缺失值给它补上,可以基于刚才统计好的类型和数值来去完成一个设置。
cat 是类别特征,然后我们来计算一下它当中有 null 值的都有多少:
1 |
|
找到employmentLength
是个缺失值,而且只有这一个。我们还是先来看看这个特征的value_counts
:
1 |
|
看到这个内容,我们来看看怎么补充最好?对于这样一个类别特征,其实最好的办法是通过随机森林去计算,然后将缺失值分别补充进去,不过我们今天重点不在那里,所以现在我们拿众数来做补充。众数怎么求都不需要管,因为通过打印出来的结果,我们明显看到10+ years
就是最多的。那我们就直接补进去就好了:
1 |
|
再查一遍类别特征的 isnull,全部都为 0了。那我们类别特征就算是补全了。
相应的,测试集里我们也需要操作一遍:
1 |
|
接下来是数值类型,跟刚才的过程原理是一样的。
1 |
|
可以看到 num里的缺失值还是蛮多的,似乎工作量不小。那我们怎么办,先拿employmentTitle
这个来看,我们先查看一下它这个特征:
1 |
|
特征值内全是浮点数,我们还是来看看它的中位数等于多少。
1 |
|
median 是等于 7,000 多,这个特征其实补 7000 多也可以,补 54也可以,也就是众数。
那一个一个数值特征去处理有点太过麻烦了,我这里只是为了告诉大家思路而不是为了比赛,那我这里就简便的做一个批量处理。全部都用median 来进行处理。
1 |
|
这里我们将所有的 num 进行循环,用于处理每一个数值特征,当 null的个数大于 0 的时候,也就是有 null 值的时候,我们就将其用 median进行处理,inplace 打开。不要忘了除了 train 特征集之外,test也做同样的处理。这样每个有缺失值的部分我们都可以给它做一个补全。
然后查看一下 train 和 test 的 null值是否还有。这样,我们就完成了所有缺失值的一个补全。
之后我们要做的事情,就是要转化数值编码了。那要做处理的类别特征有哪些呢?我们之前查看info 的时候,有很多的 object 类型,这就是我们现在要处理的内容。因为object 类型,基本上都是字符串的形式。
1 |
|
找到这些特征,grade
是其中一个需要处理的类别特征。这些类别特征其实都需要给它转换成数值编码,我们将其打印出来一遍后续查看
1 |
|
然后我们先来处理grade
,看看它是什么样的一个特征,以及特征个数都分别有多少:
1 |
|
刚刚我们应该能知道它是有一定的顺序的,就是我们之前看到过那张柱状图,可以发现A的等级是最好的,所以需要给它指定出来一个顺序,那这里的指定关系可以自己手写一下,去做一个类别编码。按照指定顺序进行类别编码,通过map 的方式来去做一个指定。
1 |
|
那测试集一样,我们也需要做这样一个处理,编码规则一定是要一致。
做完以后我们再去对比一下它的value_counts
:
1 |
|
可以看到,目前就把它转化成了 0-6 之间,就是我们的 A 到 G的一个转化。
我们回顾之前的 object特征列表,第二个是subGrade
,那现在就来处理这个特征,还是一样,先查看一下:
1 |
|
这个似乎有点麻烦。前面这个 ABCD...应该是属于大的类别,然后后面的12345属于小的类别。那现在怎么弄呢,我们创建一个临时变量,然后用这个临时变量做一个排序,再来观察下它的规律:
1 |
|
可以发现,这个 A 到 G,每一个大类别里都有 5个小类别,还是比较规律的。那这样的话就比较好做了,我个人的做法,是干脆从A1 到 G5 全部编成不同的数值,从 0 开始向后进行排列。
可以自己来去手工写,最笨的办法的话是一个手工的办法来去完成,简单写一个逻辑。
好,先来做一些前期工作,我们先写个 A 到 G 的列表,然后定义一个 index和一个 map,用于作为中间值好做处理:
1 |
|
接着,我们就可以来做个循环了,将我们定义好的映射值放入sub_map
:
1 |
|
接下来呢,直接将subGrade
这个特征做一个map,然后我们来查看一下:
1 |
|
为了查看方便,给它做一个排序。这样,我们可以看到从 0 到 34,一个 35个特征值就转化好了。
这个逻辑是我们现在 0-34 这 35 个类别,有这个类别我们再统一给它做一个map。当然,之后测试集也是要做一样的处理:
1 |
|
接着,我们再来查看一下目前的 object 特征还有哪些:
1 |
|
之前我们知道issueDate
是属于日期类型,一共现在还有 3个类别变量。那现在我们还是一个一个来,先从employmentLength
开始,我们查看一下:
1 |
|
这个顺序关系怎么去写呢,找一找看有没有一些比较巧的方法来去做一下。排下顺序看看:
1 |
|
似乎也并没有特别好的方式,那我们还是用最笨的方法,直接用 map来手工写一个好了:
1 |
|
这样就把这个数值呢给它 map 好以后再去映射回去,test的应该也是一样的逻辑。
1 |
|
处理好这个特征之后,我们就只剩下['issueDate', 'earliesCreditLine']
这两个特征需要进行处理了。之前我们知道,issueDate
是一个日期特征,也讲到了,处理日期特征要么就是将其分拆,做多尺度,要么就是使用diff 的方式。这里,我们为了方便,选择 diff 的方式来处理。
首先我们是要将其转化为 Pandas 中的日期格式:
1 |
|
那最小的日期是 2007 年 6 月 1号,那我们就设置一个起始时间,就将其设置为这个时间点:
1 |
|
其实时间设置好之后,就可以将特征值设定为 diff的形式了。这里,我们写一个简单的匿名函数来完成:
1 |
|
train 和 test都做了一个处理,然后我们来看看,特征打印出来现在是什么样。
1 |
|
这样就好了。我们接下来就还剩下最后一个特征需要进行处理,就是earliesCreditLine
,先来看看它是什么样的:
1 |
|
竟然也是一个时间类型的特征,正好,之前处理issueDate
的逻辑放在这里还是可以用:
1 |
|
那现在呢,我们就将所有的数据都清洗好了,前期是需要花一点时间去做一个数据清洗的工作。大家可以仔细看一下我的整个写法的一个过程,重点还是要看思路,如果不是很熟悉没有关系,一点点来。第一次我可以带你来去写,后面你逐渐熟悉以后就知道它整个的过程原理了。
接着,我们就可以使用corr()
来查看相关性了,因为所有特征都变成了数值类型。顺便,我们为了更直观一些,用一个热力图将其打印出来:
1 |
|
annot=True
,是将数值也写上去。那这张图我们的 figsize就要设置的大一些才可以,因为图太小数字写上去可能有重影。
类别确实很很多,关注哪些值?一般怎么看呢?关注颜色高亮的值,0.95,0.9这些值就比较有含义。可以自己对照一下,它们是一个高度相关性。还有就是绝对值负的这种数也很重要,它会给你高亮出来。
什么是相关和负相关我这里就不一一给大家看了,你回去可以自己来看一看,这个图都帮你高亮出来了。这是它的特征相关性。
在所有数据处理的工作做完之后呢,就轮到我们的模型上长了。这里我们用的是LightGBM。
开头,我给到大家的一个祖传参数,这里可以拿过来创建模型:
1 |
|
那数据集我们是要进行分割的
1 |
|
我们把这个数据集给它做个切分,看一看这个数据的效果,然后再把整个的全量数据拿去做个训练。因为原始数据,我们是要应该把isDefault
给它drop 掉。
然后现在去 fit 一下
1 |
|
fit 以后去predict,然后我们将赋值后的y_pred
打印出来,看它运行的结果。我在前面加了一个%%time
,所以最后将整个的运行时间打印了出来,我们可以看到,一共用时是3min 50s,那这样一个 80W 的数据,我们用了 2000 轮来进行fit,最后的时间还是比较快的,可以看到 LightGBM确实在速度上还是非常有优势的。那最后的array
就是我们的y_pred
。
我们先拿它在提交结果之前看一下这个结果会怎么样,如果这个结果还可以,你可以直接用个全量的数据来去做个训练和预测,然后把这个结果输出来。
结果是打印出来了,我们想看看评分,这次的评分来看看两个结果,一个Accuracy, 一个比赛要求的 AUC:
1 |
|
这个结果并不是很好,那我们现在来用一个全量的数据跑一下看看:
1 |
|
然后呢,我们从新读取一下 testA 的数据,我们只要它的id,然后将结果放到里面去作为isDefault
这个特征。最后输出一个csv 文件,用于进行提交:
1 |
|
然后到比赛的页面上,我们在提交结果这个页面内将刚才输出的文件提交上去:
在等待一段时间之后,会给一个提交结果:
em...这个分数就不要指望在比赛中有什么好的排名了,基本预测分数应该都是在 0.74以上的,那这个分数预估在所有参赛人员名单里,也就是在50%左右吧,反正排名名单里是基本找不到。
好,这样我们整个流程就完成了,当然,如果你看重比赛结果,想自己排名更好一点的话,可以尝试的方式就是更改自己使用的模型,可以事实catBoost, XGBoost等等,然后调整一下参数。更重要的,你可以自己用随机森林把之前我们要填补的缺失值都计算填写进去,而不是像我们目前这样随意的使用中位数来进行填充。
简单总结一下,我们的操作就是把类别变量和数值变量做一个区分,对数据做了一个清洗,然后让类别变量做数据编码的时候有一定的顺序,对时间类型求一个diff,仅此而已。然后就用祖传参数来去完成训练。整个的结果是 0.72分,基本上应该会在 3000 多名左右的位置 在整个 7,000多个人队伍中呢也就是中游水平。
刚才我们整个的这个求解这套问题的思路,从数据加载、数据探索,再到数据的预处理、补全这样一套思路,最后到LGBM 的计算这套过程。这个问题的思路跟我们之前课程中给大家讲 LGBM基本上是一致的,LGBM就是帮你来做这种分类任务的。具体做的过程中更多的时间都是在数据预处理的环节中。
好,我们来留个作业,就是大家自己去比赛的这个页面,完成我们今天的一个练习,然后自己提交一下,看看你的分数是多少。可以在我这篇文章下面进行留言,把你的分数告诉我。
在你做练习过程中可能会遇到各种问题,不论是咱们本文中讲解的一些概念原理还是在代码实战过程中调包使用等等,如果你遇到任何问题都可以和我来做一个交流。
那最后也非常感谢大家能学习我的这个教程,绝对有帮助的话,可以分享出去给其他更多的小伙伴看到。
今天的课程就到这里。那我们下节课再见,下节课还是关于 BI的一些知识内容。
]]>[TOC]
Hi,你好。我是茶桁。
炒股票的同学应该知道指数吧?你们知道这个指数吗?MACD。
我们看一下这个指数是什么。MACD 这个指数全称 Moving AverageConvergence and Divergence。
它的原理是要计算两条线,一个叫 MA1,一个叫 MA2, 1 是短线,2是长线。短线就是短期的平均值,比如说过去的 10天。长线就是长期的平均值,比如说 20天。这两条线之间也会有个差,这个差值称为DIFF,这个差就可以知道现在是短期大,还是长期更高。DIFF组成的线,我们称之为 MACD 线。
MACD是一个重要特征,它可以帮你来去抉择进行买卖,选择一个适当的买入时刻和卖出的时刻,那这个买入和卖出的时刻我们就要找到一个比较可靠的一个特征。我们用的一个特征是通过n 个周期的平滑值,n 个周期就是滑动窗口设为n,它在滑动窗口里面的一个平均值。DIFF 的 n 周期的平滑移动平均线为DEA。
那大家想想,为什么要用滑动窗口来去做一个平均值的计算?如果不用滑动窗口,我们就看过去一天会不会存在一些噪音的特征呢?有了滑动窗口,它的原理就是经过平滑可以过滤到一些异常的信号,尤其是那种突高突低的信号,这样得到的信号是相对有效的一个信号,这是好处。不好的地方就是滑动窗口毕竟是需要一定时间范围的窗口,所以有可能错失掉最好的买卖的时刻点。各有利弊,总体上来说还是更有价值来去做一个过滤的。
那怎么样去使用这个指标来判断什么时候买什么时候卖呢?
长期均线更能代表股票的实际价值,但是长期价值指的是什么价值呢?是长期的历史价值。我们所有的数据都是过去的历史,但是我们要决策的是未来,难点是未来是变化的,有的时候它存在不可测的因素。那么需要通过短线和长线帮你判断它的浮动情况。
股票是有惯性的,就像开车一样。如果是一个上升期,那它上升不是一两天的上升,可能是一个周期性的、阶段性的上升。下降也是一样,也是个阶段性的下降。
所以想要找到的是它在上升期的开始以及下降期的开始,决定你迈出的一个信号。这个信号可以把它用DIFF来去做个判断,向上突破就是短期抬头了,超过了长期均线就证明短期大盘在发力,大盘发力可以认为是一个好的买入信号。
差离值 DIFF 向上突破 MACD => 后市看好的买入信号
差离值 DIFF 向下突破 MACD => 后市看坏的卖出信号
差离值 DIFF 与 MACD 都呈现向上走势,而且有突破,是黄金交叉点 =>后市大好
差离值 DIFF 与 MACD 都呈现向下跌势,而且有跌破,是死亡交叉点 =>后市大坏
大体上可以这么去理解。
动量策略背后的逻辑就是认为我们的交易是有一个惯性,它不是立刻停止,所以你现在上升还会上升一个惯性周,下降也是一样。
MACD 指标: - DIFF, DEA 和 MACD(即两条曲线 + 红绿柱) - DIFF即橙线,是对 K 线收盘价进行一系列计算的结果 - DEA 是蓝线,是在 DIFF的基础上计算出来的 - MACD 柱 = (橙线 - 蓝线)* 2 = (快线 DIFF - DIFF 的9 日加权移动均线 DEA) * 2
如上图,可以把 MACD理解成移动平均线,这移动平均线是由两条线组成的,快和慢。快就是短期,慢就是长周期。
以这样一个数据为例,快线是我们的橙线,蓝线应该会更慢。当它们某一个时刻点如果快线向上走势刚刚超过了慢线,则要进行买入。刚刚超过的过程中它是有一个惯性的,它超过去会有个周期,所以它应该是买。
但是超过不会一直超过,他超过以后蓝线也会逐渐的上升,但是上拉的力量已经开始疲软了,到某一个会客点的时候,当橙线和蓝线又给它交合的时候,这个时刻是该买还是该卖呢?
我们在再一次发生交集的时候应该应该卖掉,为什么,大盘疲软了。不过大盘疲软以后马上要开始上升了,其实你没卖多久,后面又上去了,上去以后还要购买,这是按照MACD 的一个指标来去给你的提示。
我们再来看这张图,标示一下买卖点:
大体上对应一下,应该是如图这样。如果是这样的一个买卖信息,通过 MACD线来做决策,在上面写的这个买卖的时刻点。现在我们想想,这样的交易策略是好还是坏呢?这样的一个标记策略能不能挣到钱?
从这个数据集上面来去看是好的,而在很多的其他情况下其实也是好的。但是也有些情况下可能是坏的,所以刚才这个策略不能说是完全100%的一劳永逸。在某些情况下,它确实是可以帮你挣到钱。
我们来看,这个交易是从 2,000 多点一直到大盘 5,000多点又下来。这个交易不能说多频繁,应该是属于一个长周期的交易,它有可能横跨了一两年之后。但是它是滞后的,MACD是个移动平均线,看到平均,看到滑动窗口就会想到它的特征,计算没有那么的及时,因为毕竟需要一个时间段来去验证。可能需要有一个10天的周期,所以它是滞后的。因此就算挣钱也不会有多极致,但是总的来说还是不错的。
MACD就告诉你在正确的时间做正确的事,这就是一个统计特征。具体在程序上面在这个策略上面怎么做呢?MACD是要有一个快和慢的定义,快线现在是可以用 12 天,慢线用的是 26天。为什么是 12 和26,因为在早些年间我们那阵工作时间还是一周六天,所以当时的股票交易就是一周六天,那么半个月就是12 天。半个月你就认为是一个快线特征,那一个月有可能就是 26 天。
当然现在工作日已经不是一周六天而是一周五天。(我知道你们想说什么,不用吐槽,以法定工作日为准,毕竟股市是严格按照法定日来的。)所以在实际的股票交易日里面如果你还是半个月和一个月,它应该已经变成10 和 21。用哪一种方式都行,可以用 12 和 26,也可以用 10 和 21。
为什么有些人现在还用 12 和 26来做快线和慢线的一个准则呢?原因就是因为我们已经形成了共识,已经把它当成快慢的一个标准。所以大家都会看这个特征,当看到这个特征的人数多了我们就会认为它是个重要的一个指标。当然你也可以采用10 和 21 这个数值。
简单总结一下,DIFF 是这两个线的差值,当 DIFF 上穿 0轴时,差值抬头了,就证明短线在开始突破,即 12 日均线与 26 日均线金叉。当DIFF 下穿 0 轴时,已经开始疲软了,逐渐的弱下于整个长期的趋势了,即 12日均线与 26 日均线死叉,死叉就是个卖的信号。
那么怎么去指导买卖呢?金叉、死叉其实就是一个很好的买卖信号,但这个信号是有前提的,它是在趋势行情,也就是动量行情。就说趋势是应该有一个惯性的,如果它是在震荡,震荡是随机的,它没有趋势,所以它的惯性特征没有这么明显。没有这么明显的过程中,金叉和死叉就有可能不是个有效的信号。
以上是在趋势行情里面来去做的,周期内的一个情况。
操作的过程我们可以看一看,可以对未来股票计算一下它的短期均线 MA1和长期的均线 MA2,我们拿 AAPL来举例,计算它的买卖时刻和信号,在股市中绘制买入和卖出的信号,包括DIFF,MA1 和 MA2 曲线。
这就是教给大家一个工具怎么样去计算它的短期和长期的特征,基于它的这个特征来去决定你买入和卖出的一个时刻点,把这个时刻点绘制出来。你可以看有的时候也没这么好,比如图中绘制的这个买卖点也会造成一个亏损,比如第一个买点和第二个卖点之间,就是一个亏损的状态。整体上来说有赔有赚,总的来说挣的概率会更大一些。
那接下来,我们用 MACD 策略看一波代码。
首先,我们是要先实现咱们的 MACD 策略:
1 |
|
可以看到,滑动窗口用的 rolling
函数。滑动窗口就是计算它的快线和慢线,MA1 就是 12 天,MA2 是 26天。用滑动窗口求 diff
,diff代表的含义是快线减慢线,看一看现在是在上面还是在下面。然后把 diff求了一次均值。快线和慢线已经做过一次求均值,如果又做一次均值的应该是两次平滑,如果用两次平滑拿它做指标会过滤异常值,效果更好,就不容易出现那种噪音的浮动,因为我们用了两次以上值的过滤。你可以采用一次平滑,也可以采用两次平滑。一次平滑看的是diff,两次平滑是看的 diff 处的滑动窗口的平均值,拿它来去做判断。
然后咱们来计算买卖时刻点的信号
1 |
|
我们现在做的应该是一次平滑的信号,就是说看它快线和慢线是不是大,如果大我们就认为现在应该持有,那我们就会把它的positions
写成 1。
np.where
是个条件判断, 如果它存在我们就写为 1,如果它不存在写为 0.
所以上面这个代码可以看到,短线抬头的时候你要持仓,那这个持仓是你当下的一个状态。什么时候做这个状态呢?我们还要看一看它先后的一个变化,如果之前是不持仓,position等于 0,突然某一个时刻发生突变的过程中 position 从 0 到1,这个时刻点是给你一个信号,从没有这只股票到拥有这只股票我要做的是个信号的处理,它应该就是个买的信号。
如果它的 position 一直都是 1,突然有一天它的 position 变成了0,我们把它的这个 diff 求解出来应该变成了卖的信号。
所以怎么去筛出来这个买和卖呢?筛的过程实际上是求了一个diff()
,这个方法在 Pandas 里面代表前后相减,可以把买卖信号计算出来。
信号 position 无外乎就是 0 和 1的关系,所以这两个趋势都可以计算出来了。
那其中一些 Python的基础语法,大家应该在之前就应该掌握了。比如df[ma1]
,这就是在 df数据内去筛选快线特征,在快线特征里面筛的不是它的全量值,筛的是什么?[ma1:]
代表的就是12 天以后的。因为在 12 天之前我们还没有这个特征,在股票刚刚上线前,12天之前其实是没有快线特征的,所以我们把它过滤掉了。不把它过滤掉,把它就写[ma1]
其实理论上也是OK 的。因为这两个为NaN
, 其实是没有什么意义的一个特征。
总的来说,可以把它理解成就是 MA1 大于MA2,就是快线和慢线的一个对比的情况。把这个对比情况喂给position
,就可以知道要持仓还是不持仓。
signal 计算完成以后我们可以在图里面把 signal 给它绘制出来。
1 |
|
signal 等于 1 就是需要进行持有,在上面写成一个buy,建议你去购买。颜色用 green 绿色,signal 等于-1就是不建议持有,写成了 sell,颜色呢写成了r,就是红色。一会儿可以用这个函数打印出来。
具体在设置参数的时候设了 12 天和 26 天:
1 |
|
股票我们选用的是 AAPL,就是 apple的股票。买卖时刻点可以分别帮你来计算出来,
1 |
|
告诉你说 1 月 21 号买, 4 月 7 号买,6 月 15 号,10 月 21号买。这个买卖时间是交错的,买不会一直买,掉下来了,疲软的时候交叉的部分其实就是卖。所以可以把它理解成是1 月 21 号买,2 月 19 号就卖了。之后 4 月 7 号买,5 月 10号再卖掉...
来,我们将数据绘制出来:
1 |
|
做的这个策略是历史复盘的一个情况,这是它的整个历史的一个数据,能看到他的复盘情况。
我们制定一个策略是想看它在过去一段时间的表现怎么样,如果它在过去一段时间表现好,那么你在未来时刻可以采用这个策略来去做一个实战。不过大家可以思考一下,过去表现好一定代表未来好吗?不一定,有可能会差。但是总的来说它还是过去一段时间一个好的验证。对于之前的这个时刻我们应该是在某一个时刻点到另一个时刻点,它的一段距离的情况就是MA。而且 MA 是过去 12 天和 26 天。
我们现在就把它画出来了,下图就是它的 ma1 和 ma2,还有 diff 的图:
这个曲线可以看到,橙色是快线,它的运动的幅度会更大一点。以靠近2021-07在它之前的那个交叉点为例,橙色快线上升的时候这个点应该是买还是卖?这个点蓝色现在已经开始往上升了,所以理论上应该是要买会更好。然后一直都是可以买的,因为它都属于上升的波段。
在 2021-09之后的第一个交叉点是不就卖掉了?卖掉以后再择时,择到下一个时刻再买。
所以在更上面那张图中,我们把它的买卖时刻标注了出来。
告诉你说 1 月 21 号买, 4 月 7 号买,6 月 15 号,10 月 21号买。这个买卖时间是交错的,买不会一直买,掉下来了,疲软的时候交叉的部分其实就是卖。所以可以把它理解成是1 月 21 号买,2 月 19 号就卖了。之后 4 月 7 号买,5 月 10号再卖掉...
可以看到是 1 月 21 买了,2 月 19 号就卖了。4 月 7 号买了,持有了大概1 个多月,然后就卖掉了。这张图就是 MACD 的一个买卖信号。
从这张图里面其实也能判断出来,整体上来说还是有亏钱的可能。比如第一个买点和第一个卖点之间,我们可以看到它就亏钱了,第一个小周期是赔钱了。1月 21 号到 2 月 25号之间是赔钱了。但是第三个买卖周期算是大赚了一笔,之后再买入并持有,一直是赚的。有赚有赔,总来说赚的可能性会略高一点。
我们来验证一下咱们的周期看看,用上节课中咱们对股票进行操作的函数来进行:
1 |
|
设置完初始资金并计算我们可以买入的数量之后,我们就来操作下看看,先看第一个周期:
1 |
|
果然是如我们预期的一样,第一个小周期内是亏钱的对吧?那我们来看看所有周期结束后,如果在年底卖出会怎么样。
1 |
|
当然我们为了简便,现在是手动的根据之前分析的买卖点来执行买卖操作,并没有完全用程序自动执行。不过我们可以看到,整体收益率是达到了近百分之二十八。这个是很棒的一个收益了对吧?
以上就是 MACD 的一个策略,我简单给大家梳理一下。然后整个代码在我仓库里也有,大家可以自己去获取,你们可以做一个参考。
其实选股是非常重要一件事,买什么这第一个维度,第二个怎么买。怎么买又分了每一只股票的一个配额,如果我们最初 1W 的资金,买 10 支股票,那么平均配额,每支股票分到 1000块钱。
第三就是制定交易策略,交易策略是刚才介绍给大家的 MACD,MACD算是指标之王,在炒股里面这个指标是使用频率最高的,也是所有人基本上必研究的一个指标。
我们可以通过 MACD给你一些信号一个策略展示,就按照刚才这段代码,大家可以自己来去模拟一下。- ## 如何选股
择股优于择时,择股是你的格局。你去选了一个大的格局,后面是属于在格局里面的一个努力。你选了一个很好的赛道,这个赛道有一个很好的一个空间,所以你要选择一个好的股票有的时候才是第一个要位。
好的股票怎么选呢?用我们之前介绍的 jqdata 就可以,那我们都知道,要选股首先是要先观察行业,我们看好一个行业之后再从中进行选择。那么就先将所有行业查询出来:
1 |
|
选择这个日期也是因为我的这个账户并没有缴费,所以免费版的限制是到这一天为止。
好,那从这些查找出来的行业中,我们看看和计算机相关的产业都有哪些:
1 |
|
接着,我们获取一下这个行业版块内所有的股票:
1 |
|
这样就可以将这个版块内所有的股票代码获取到。
接着,我们就可以根据获取到的代码,来查看市盈率大于0,那范围当然就是我们之前获取的,属于计算机行业这个版块的所有股票,然后我们让它们的PE 从小到大进行一个排序:
1 |
|
当然我们想要查询一下我们获取的这么多股票的 PE 和 MC均值大概为多少,这也可以大概了解一下这个版块的发展到底如何:
1 |
|
接着,我们来筛选我们想要投资的股票,那这个就是一个策略问题,在这里,我想要看看小于PE 均值,且 PE 要大于 0, 然后大于 MC 均值的所有股票,再进行排序:
1 |
|
那我们这样呢,还是获取了 84支股票,似乎还是有点多是把?这里呢,就是教大家这么个意思,具体的选股策略,最终选取出自己心仪的那几支,大家还是需要去学习一下基础的股票知识。
好,那最后,我们将这 84 支股票打印出来,看看都有哪些:
1 |
|
如果我们想要进行一个长期的数据对比,也可以选中一个自己看重的指标,然后将其全部down 下来进行观察,这里,我选中的指标还是 PE 值:
1 |
|
数据的 columns就是日期,太多了我就不完全展示了。大家可以在本文之后自己操作一下看看。
在整个代码操作过程中,我保存了几个数据在 dataset里,大家可以去我的代码仓库里直接拿来用,使用 read_csv就可以,这样就不需要去从新去查询和获取数据了。
]]>[TOC]
Hi,你好。我是茶桁。
上一节课中,咱们详细的分解了 Fintech的应用场景,也是将相关的一些业务给大家好好的梳理了一遍。
那么本节课中,咱们来一起做一个实战,关于 Python的量化交易的一个板块。
先来了解一下咱们用到的工具,首先必须是 Python,Python的数据会放到一个数据表里面,结构咱们用的是dataFrame。以前介绍过,dataFrame 就是 Pandas 这个包内的数据格式。
在之前的 Python 基础课程有详细的给大家梳理过Pandas,有相关基础不太好的小伙伴可以回过头去再好好看看我那一篇内容
这里咱们要用到 dataFrame 内的一个相关函数,rolling
,这是一个窗口的滚动,设置了时间窗口的大小,可以帮你去滚动几个窗口的数据。
1 |
|
这其中,window是时间窗的大小,即向前几个数据(可以理解将最近的几个值进行 groupby)
min_periods
,最少需要观测点数量,默认与 window相等。
center
,把窗口的标签设置为居中,布尔型,默认 False
on
, 可选参数,制定要计算滚动窗口的列,值为列名。
我们一起来生成一些模拟的数据:
1 |
|
现在生成了一些模拟的数据, 然后把 B 列最近的两个值来进行相加,形成了 C列,那 D 列是 5 个值进行相加。这两个都是在 B 列的第一个值 0再向上多数了一个值,所以都是 NaN。然后咱们的 E,F 是求平均值,我写了一个min_periods=1
,这样的话就说它只要有一个值也可以做,mean是求了一个平均数。
所以咱们来看 E 和 F,前面一个都是 B 里面的 0 来求平均值,那自然还是0。E 的第二个值是 3/2,第三个值是 5/2, 而 F 第二个值也是3/2,可是第三个值是 5/3,依次往下类推。
关于这个 Rolling的函数是帮你去提取窗口特征。举个股票的例子,不知道大家有没有炒股的。炒股里面有个均线的设置叫MA,比如 MA10,代表 10 天的平均值。为什么取 10天的时间或者是多天的时间?因为取一天抖动会剧烈,所以在股票里面会有个MA10。MA10 就是代表过去 10天的一个平均数,如果用了过去时间的平均数就会更加的均和,更加的平均,所以它也是一个统计量的一个特征。通常会采用窗口里面的统计特征帮你来做股票的决策。
如果只看过去一天的数字很容易激进,因为过去一天有可能抖动很强。只要抖动强,不代表它的信号一定是上涨的,它有可能马上落下来了,所以MA10相对来说会更加的平缓,更加过滤掉一些市场上的噪音,它才能代表的一个趋势向。这就是Rolling 窗口的函数作用。
股票的数据又从哪来呢?股票的数据要有一些专业性的软件,现在用一个工具叫JQData, 这个工具就是一个 Python 开源的包。
1 |
|
首先是需要你去授权,这个数据虽然是开源免费的,但是它有接口次数的要求,如果你是一个非常高频的,一天有几十万上百万的访问请求还需要付费来进行处理。对于学习来说的话这些访问的次数是完全足够用的。
JQDdata不光是可以帮你获取到基本的这个信息,有开盘价、收盘价、最高、最低的一些历史数据,还有volume,就是交易量有多少。还可以帮你去计算额外的指标,这个股票的倍数、p的倍数、市值、marketcap、板块的信息等等。有了这个信息就可以帮你筛选一些股票。
1 |
|
可以看到我在进行授权之后,尝试查看了一下我的使用情况。每天 100W条,基本上是绝对够用了。之前调用过接口获取了相关数据,咱们获取的是股票编码000001
,也就是平安银行从 2023 年 1 月 1 日到 4 月 1日的一个数据。
然后我们还可以查看这只股票一些其他的数据。
1 |
|
可以看看这个属性都有哪些, 这支股票里面它的 PB, PS等等。这些指标代表的是它的一些股票计算的因子。比如说 PE就是市盈率的一个倍数,PS 应该是营收的倍数,market_cap是市场的一个估值。
关于股票趋势的预测有很多特征是当前才知道,比如今天最高价、最低价、交易量,这些怎么划分数据集才能建立起来对未来一段时间趋势的预测呢?当天知道的是当下的时间。股票趋势预测一定是基于过去,历史数据也是有的。所以首先你过去一段时间的一个历史是可以做,这里有很多种模型。
未来预测时刻不能仅仅是当天的,还有过去的一段时间。有很多时间序列的模型,在后续课程里面会教给大家时间序列的一个使用方法。比如说有ARMA、ARAMA 这些方法。过去 60 天就可以预测未来比如说 6天它的股票价格。
所以建模是 OK 的,除了 ARMA以外还有一些持续模型,可以做多个特征的预测。包括像LSTM,长短记忆网络等等,这些过程在后续的课程里面都会教给大家去做。今天是教给大家一些简单的一些方法,并没有特别难的一些处理。今天的一些方法就是怎么样去获取一些数据。
还有就是我这里有一份数据,包含了所有股票 5年的数据。不过已经都是之前的数据了,并不是最新的。是一份 2013 年到 2018年之间的股票数据,大概有 100多支股票。要是用这份数据当然也是可以的,这份数据我还是会放在文末。
可以筛选一下行业,股票是跟行业相关的,行业有哪些可以看看:
1 |
|
每支股票都属于其中一个行业,比如说你想要看跟计算机相关的行业有哪些就专门去做一个输出,
1 |
|
它的代码是 C39,这是跟计算机相关的行业。这样可以筛选出来跟计算机行业相关的股票。那怎么筛呢?我们已经知道它的代码是C39,可以专门去写一个 C39, 然后把这个股票的值得给它打印出来。
1 |
|
C39 是计算机相关行业股票,数据也比较多,一共有 558 只股票。
更具体的一些内容,大家可以查阅一下官方的手册来看看具体怎么使用:https://www.joinquant.com/help/api/help#JQData:%E4%BB%8B%E7%BB%8D%E4%B8%8E%E8%AF%B4%E6%98%8E
如果你想要使用这个工具可能需要自己来注册个账号就可以了,也可以使用咱们文末分享的那个数据,拿这个数据去做一个练习也是可以的。
除了 jqdata以外,还有一些这个开源的工具是不需要账号密码的,也可以获取一些数据。我们可以使用yahoo 的财经工具来获取数据,这个包之前是为了修复pandas_datareader
使用 get_data_yahoo()
可能出现获取数据出错,那么就有了 fix_yahoo_finance
工具包用来修复获取数据的问题。之后, fix-yahoo-finance
更名为 yfinance
,但是使用还是没什么变化:
1 |
|
我们只要 download这些数据就可以了,它后面股票代码会有一点小区别,如果你是上海的股票的话是.SS
,深圳股票的话是.SZ
。比如浦发银行的代号就是 600000.SS
,深发展的代号就是 000001.SZ
,这是雅虎的使用方法。
我们看的股票是去年 1 月 1 号到 12 月 31号,六支股票可以一次下载下来。可以看到这六支股票的收盘价,交易量,开盘价等等,所有的数据都出来了。
那其中 251 是代表什么呢?为什么去年他只有 251行?这是一个交易日的情况。一会儿还会用到一个程序帮你画一个 MAC 地图,对251 个交易日.
我们想要去模拟一些股票的情况,这是 2013 年的 2 月份到 18 年的 2月份的 505只股票的数据。就是给大家提供的五年的时间那个数据,是一个已经下载好的数据。模拟的过程中初始资金是1 万,要制定一个策略,对股票来进行买卖,最后计算一下它的 2017 年 12 月29 号的资金和投资回报率。
这是一个模拟的情况,该怎么去模拟?
17 年的 1 月 3 号是 1万块钱,年底的时候投资回报率是多少。数据只能是看历史数据,假设看过去一年,把过去一年涨幅情况来做个排序,涨幅最高的那个追涨的策略,就是把涨幅最好那个策略全仓进行购买。然后计算一下投资组合的一个回报价值。
我们会有以下的一些函数帮你去完成。
1 |
|
用的一个字典去存储一些信息,比如说 cash这里代表的是你当下资金的一个数量是 1 万块钱。策略比较简单,就是找一个在16 年表现的最好的股票,然后 1 万块钱的全仓。那这一年间我啥都不做,就是在1 月 3 号就购买,购买以后看一下 12 月 29 号的投资回报率。
想想这个投资投标率会好吗?不一定,有可能好也有可能不好。主要是看这个股票在下一年的表现是怎样的。
首先,我们需要引入一些包:
1 |
|
在 Python 里面比较常见的 Pandas, NumPy, 画图的包,时间的包。
然后我们来读取一下数据
1 |
|
这五年数据去做个模拟,数据在文末去获取。这是所有的股票,股票的数量比较多,一共是有619,040 行数据。
我们可以对数据来做一个探索,并且进行整理:
1 |
|
它一共有多少只不同的股票,505只。也可以对股票的数据去求取一下前几行的一个数据,通过 head方法去看,都有哪些特征。
1 |
|
接着,我们来看看交易日的情况,看看这 5 年时间有多少个交易日:
1 |
|
这 5 年时间交易日大概有 1,259个,把这个交易日to_datetime
一下。这个是转化为 pandas中的日期类型,这个使用情况的话也是比较多的,在 pandas里面使用的日期类型基本上都是to_datetime
来进行转化:
1 |
|
咱们主要是想要做一些股票的一些模拟。要筛选某一支股票可以通过 name来进行筛选,比如可以筛选 AAPL 这只股票:
1 |
|
筛完以后我们还把这股票做了一个可视化,按照时间,刚才已经转换好的时间和开盘价来做一个获取。可以看到Apple 这支股票随着时间应该是逐渐上涨的,确实它的表现还是不错。
然后咱们开始写函数,这个函数的目的是计算一下投资组合目前所带有的一个价值。
1 |
|
这个函数逻辑就是统计每一支股票的一个价格, 然后做累加。
也可以写一个函数进行购买,购买就是在资金里面减去你购买的金额,然后在拥有股票里面把购买到这个份额给它累加到一起:
1 |
|
卖股票的逻辑就是先看你有没有持仓这支股票,如果没有持过这个股票就无法卖掉。你卖的股数如果大于你手上持有的数量也没法卖,只有小于等于你现在手上的股才能卖掉。
1 |
|
以上三个函数的逻辑一个是卖,一个是买,一个是计算仓位所持有的股票的价值。这三个函数你可以把它理解成是一个股票策略的模拟环境,需要的一个基础信息。
下面就是设置交易策略的一个初始状态了,初始状态是 1万块钱,所以给它建了个字典:
1 |
|
字典打印出来,开始等于 1 万。如果要买两支股票,这两支股票分别是 AAPL和 Google,在 1 月 4 号买 10 股,可以看看:
1 |
|
然后现在是 APPL 10 股, GOOG 10 股,cash 减少了,变成了 1,000多。因为原来有 1 万块钱,然后看一下当下今天的情况:
1 |
|
买入的话,假设是从开盘一早就进行购买,晚上的时候涨了 15块钱,证明还是赚了一点点。
卖股票也可以按照刚才这个策略来进行售卖,在 2 月 1号的时候把它卖掉了。
1 |
|
但是我们写的代码在这里并不能进行卖出,为什么?因为我所拥有的 AAPL 10股,这里要卖 20 股,那肯定卖不出去。
好卖不出去没关系,在 2 月 1 号的时候我们股票的市值是多少?
1 |
|
达到了 10,788,涨了 7%。
以上就是一些模拟软件的一些策略,在后面我们要筛选一些股票,筛选出来一定的年份。我们这里找的是2016 年全年的信息,505 只股票全年的情况做一个筛选,那我们现在的第一个策略就是,2016-2017 年,按照过去 1年涨幅排序,直接全仓购买最好的.
1 |
|
这么多股票当中,我们想要找的是一个最好的情况。用什么方法把 2016年全年的股票都做了一个判断,从最开始的时间到最大的时间
1 |
|
最小的时间到最大的时间做个筛选, 然后 close 减去 open 再除以open。这样我们可以找到它的增长率,增长率如果要更高的话就把它保存起来。我们用的方法叫做打擂方法,寻找best_stock
。就是在2016 年的最好股票,打印出来。
这个方法是比较常用的方法,叫打擂法,可以找到最优解。通过计算我们可以发现,应该是一个叫AMD 的股票在过去 2016 年全年的时间是效益是最好的。这股票涨了 322.383百分比,涨很猛啊,涨了 3 倍,一年时间翻 3倍,这个投资回报率真的是很棒。
如果你能把选股做好的话,基本上应该稳赚不赔的事情。那如果我们在 2017年想全仓持有,重仓持有,那要计算一下你能买多少股:
1 |
|
购买的时刻是 2017 年的 1 月 3号,那这种策略是个好策略吗?我知道有些人喜欢追涨。策略没有好坏之分,只有适合与否。在某个时间段它有可能就是好策略,但是同样的策略在某时间段有可能就不好。
我们假设从 1 月 3 号开始买,买的是 AMD 这只股票,看一下能买多, 能够买875 股。
去购买使用的是刚刚写好的那个portfolio_buy
这个函数,拿它来进行购买.
1 |
|
它原来等于 1 万,购完以后只剩 7 块 5了,就没剩下什么钱了。然后我们再看一下 12 月 29号的一个股票价值剩多少呢?
1 |
|
剩了 9,000块钱。所以大家可以看一看这个追涨特别好吗?如果你一直没有抛的话,在 2017年的年底你可能会亏10%。这个策略其实一直持有的话在那一年并不是个好策略。
我们统计一下 2017年所有交易日,看一看这个股票随着不同交易日的时间的变化是怎样的。年底的时候是亏的,那年中有没有可能找个适合点去抛掉会挣钱呢?可以做一个分析
1 |
|
最后把这股票打印出来,这里是使用了一个获取投资组合,就是计算他在交易日的价值。你看这个价值还是有升的,同时也有降的情况。把这个价值整理出来以后给它画出来。可以看到有的时候曾经达到了13,000 美金,就是涨了 30%。中间如果没有抛的话,最后有可能就套住了,套住10%。
这个工具是通过 pandas 来去做模拟,那使用的方法其实并不难,都是通过pandas来去做了核心。就是我们能有一个简单的模拟环境可以帮你来去买卖股票。花钱买股票然后在适适当的时刻进行售卖,最终我们希望能找到一个策略,帮你来去制定一个好的交易策略,从而可以让你赢的更多。
链接: https://pan.baidu.com/s/1otDWJef6Zx6JTLXikcUVJA?pwd=jvfv提取码: jvfv --来自百度网盘超级会员 v7 的分享
]]>[TOC]
Hi, 你好。我是茶桁。
咱们 BI 的前几节课中,主要是使用员工离职预测这个项目来展开讲了一下做BI 的预测全家桶以及集成学习的内容,算是窥入了 BI 的门径。
本节课开始,咱们要花几节课的时间来学习一下 Fintech 的数据分析。
在金融行业里有很多需要做数据分析的场景,那这些场景都有哪些,该怎么样针对不同的场景去完成,这是接下来主要讲解的内容,围绕Fintech 金融科技的场景。
[[Fintech]]的应用场景,都有哪些公司,人才;发展的机会是怎样的;银行体系里面都有哪些业务线?要结合哪些场景去做?
那接下来呢,主要围绕了 5 个场景去展开:
不同的场景可能需要不同的算法,我会带着大家去做一个量化交易的模块。然后咱们再一起去看一看违约的预测。违约是什么样的场景?不知道小伙伴们有用「借款软件」借过钱的没,我一天到晚接到一些借款电话。比如支付宝上可以用蚂蚁借,或者是网上贷,还有京东金融,百度都有类似的产品。
这样的一些产品随借随还还是比较方便的,那平台就需要去完成一个预测任务,在放款之前要看一看你会不会违约,如果违约概率比较高就会拒绝放款。
违约预测就是一个项目,针对这个项目该怎么样去构造一些特征,使用之前课程里给大家介绍的机器学习的神器,[[XGBoost]]和 [[LightGBM]] 来完成预测任务。
首先,先来看一下 Fintech,这个单词代表的什么含义。fin,tech是两个英文单词的缩写,一个是 financial, 一个是 technology,所以 Fintech指的就是如何把科技和金融包含到一起。
有一家银行叫中原银行,按理银行应该属于跟金融行业特别相关,做的就是金融行业。大堂经理,柜台的一些人员这些都是属于传统的金融的岗位。
未来如果你要打造成一家金融科技公司,那这家银行的科技人员的占比目标会定在50%以上。所以大家想想他们未来要招聘的人可能除了传统金融行业以外,就是会有很多数据分析师。如果大于这个数字,这家企业的性质就是一家科技公司。
股票上面很多银行都上市了,PE倍数的衡量跟行业性质相关。如果行业是个传统金融行业,PE倍数都不是很高,如果是个互联网行业 PE倍数相对来说就会更大一点。所以如果占到50%以上,股票的市值也会更大一些。高盛集团就曾经说过他们是一家科技公司,摩根大通也会占有了1/3以上的数据分析师,而且他们也希望员工加班加点的去学习数据分析的课程。
在 Fintech这个领域里面是有两个结合,一个就是机器学习,这是科技,通过算法和模型完成一些预测的事情。还有一个就是业务场景,商业变现。
商业都有哪些,银行的一些存款、贷款的一些业务。在金融科技公司里面典型的代表包括了像蚂蚁金服、京东,甚至包括了滴滴这样一些企业。支付宝、Paypal等等也都是这样的一些企业。还有传统的金融公司,像招商银行、工商银行,四大行等等。
银行的业务线如果要做算法,想要服务这些企业要了解他们的业务。业务线分成了以下的四大模块:
大家应该都办过信用卡把?信用卡应该在 03、04年的时候是逐渐兴起的年份,那阵是刚刚普及,信用卡每办一张卡代理商都能拿到很高的一个绩效,银行会给他那些代理费用。曾经有人专门帮别人办理信用卡,就是帮银行代理信用卡挣了几百万,这是在03、04 年的时候。
信用卡一般来说不止一张,这里的卡可能包括的信用卡也包括借记卡,全国范围里面统计平均每个人是5.46 张。有了卡的场景,围绕信用卡会有很多资产的业务。
第二块是零售业务,这里的零售不是消费品的零售,是对个人的金融业务,包括了像存款、融资、理财、证券交易等等。
第三个称为叫互金。互金就是互联网金融,在线上的金融的业务。比如手机银行等等。
第四块就是一些对公业务,面向企业提供很多的细分的一些场景,比如说授信业务中的A 卡、B 卡和 C 卡,A 代表的是 application,最开始第一次见面。B 是behavior, 行为,已经放款了,有了行为的记录,要预测一下他会不会违约。C 是collection,回收。回收的概率代表了他已经逾期,会不会违约,逾期了不一定等于坏账,所以逾期以后还要预测一下他会不会坏账。
从业务角度的话, 还会分成增量的部分,存量的部分。增量是未来增加的一个部分,存量就是针对现有的这些用户去挖掘更多的一些价值。所以这两块也是银行去考虑的业务。
业务是要通过数据分析去完成的,那数据的来源也可以把它分成行内数据,行外数据,以及第三方的一些数据。行内数据就是跟用户相关的数据,但是用户的行为不仅仅是在银行体系里面去呈现,还有可能在征信和运营商里面来进行呈现。所以也需要跟这些商家来进行合作,这是银行的一些业务线。
下面咱们来具体看几个场景,一起去思考一下。
场景 1、财经新闻分析
财经新闻作为重要却海量的投资数据,无时无刻不在影响着投资者们的投资决策,为了更好地提示客户当下新闻事件对应的投资机会和投资风险,可以通过当前新闻内容从历史事件中搜索出相似新闻报道,后期可以结合事件与行情,辅助客户采取相应投资策略。
为每一条测试集数据寻找其最相似的 TOP20 条新闻(招商银行Fintech2018)
第一个场景是财经的新闻分析,这个财经的新闻分析是在 2018年招商银行出过的一道题目给内部的数据分析师去做培训。他们的场景是要去分析网上的新闻,找一条跟这个新闻类似的前20 条新闻都有哪些。
为什么要这么做?因为客户经理也经常会回答客户一些关于财经相关的一些问题,为了更好的帮助客户去做投资,需要获取当下实时的新闻动态。这个新闻动态怎么样去理解,实际上涉及到一些语义理解的层面。那语义的理解层面就会涉及到有一部分类似于像NLP 的知识。NLP 的一些技术也会在的 BI课程体系里面适当的会给大家进行讲解。重点是关注到文本的特征提取。
以这个场景为例,财经新闻的内容通过文字的方式去提取关键词,提取关键词代表内容的一些抽象概念。也可以对这些内容提取一些关键句,关键句可以把它理解成是一些文章的摘要的部分。
第二部分是精准营销。
场景 2、智能营销(用户画像分析与商品销售策略)
用户画像的完善对于个性化推荐、精准营销非常重要。招商银行通过对零售客户生成 1726个客户画像标签,使得营销客户触达次数提升了 6.56 倍,营销成功率达到17.42%
针对用户行为数据,生成客户画像,同时针对用户的订单挖掘产品组合,从而提升产品的购买
在银行体系里面也需要给用户去做一些预测。一个预测场景就是预测一下用户的资产会不会增加。更主要服务的对象是那些优质的用户。
一个优质的用户可能等于 10个甚至更多的普通用户。优质用户可以通过行为去判断,尤其当他资产快速提升的时候可以把它分析出来。在中原银行的体系里面就会建这样的一些模型,去预测用户在未来三个月的资产会不会上升到当前幅度的120%。
举个例子,你现在是 100 万的存款,未来有可能存款大于 120万。那现在就要跟这个用户更加密切的保持合作,因为多出来的 20万可以进行存款的业务。预测就是营销的一个前提。
在招商银行里面也做过类似的一个场景,他们给对公的业务去做了一个存款预测模型。想象一下,每一个客户经理都有一些指标去发现一些新的对公客户,目标是让这个新的公司能在招商银行里面的存款大于50万。怎么样去完成预测呢?以往的方式就是盲打电话,随机性的打电话,会有专门的电话营销的一些人员给人们去打电话,名单会来自于互联网上的名单或者是第三方提供的一些名单。
如果按照第三方提供的企业名录一个一个去打电话,然后让这些企业去把存款放到招商银行,考核指标就是存款会不会大于50万,这样的工作效率你们觉得高吗?这是一个需求的场景,这个需求场景招商银行曾经花了一笔钱去找第三方的公司去做建模,他们提供一些数据,希望第三方公司能给他一个模型。有一个朋友正好做了这项业务,当时花了几十万,应该是大于等于50 万,就建一个模型。
后来通过模型去预测的准确率可以达到接近86%。打电话之后客户如果最后存款了,存款金额会大于 50 万这个概率高达86%,是不是还挺高的?比随机性的概率要大很多。
模型会提取一些特征,企业这些都是新客不是老客,并没有老客以往的数据。新客一般会有第三方类似于像企查查,天眼查的一些机构提供数据的一些词段,但这些词段都属于静态信息。
举个例子,像银行的法人是谁,股东的一些结构,注册的资本等等。这些数据足以支撑一个准确的模型吗?不行。因为企业的发展是动态的,可能你注册的时候还很好,但3年以后这个企业有可能经济效益不好。那么就利用了一个信息的维度。个人觉得比较有效,找的还比较准的是拉钩或者BOSS 上面去找招聘的信息。
这些招聘平台上有很多的企业招聘的一些信息,通过一段时间招聘职位的数量以及金额就可以判断出来这个企业是不是在一个上升期。如果在上升期的话,他的资金相对比较充裕,也更有可能存款会大于50 万。而这是一个智能营销的场景。
第三个场景。
场景 3:金融数据分析与风险控制
在信贷领域中存在信用卡违约和欺诈的风险,通过用户行为数据,分析申请借款用户的信用状况,来判断是否存在逾期。
通过分析交易时间、交易金额、收款方等多维度数据,还可以对信用卡会否被盗刷进行预测,防止信用卡被盗刷的风险。
此外,通过预测模型,我们还可以分析出哪些因素容易导致违约,从而加强产品的设计。
在 Fintech里面有很多的跟贷款风控相关的场景。比如说信贷领域就会存在着违约的行为,也可能存在着欺诈行为。
违约和欺诈这两个怎么理解?在信贷领域里面会有两种情况,一种是违约的情况,一种是欺诈的情况,都需要提前进行判别。
违约就是钱给到你,你有可能不还,但这个还不是主观上不愿意,有可能是因为当时确实没有钱。欺诈就说他本身就没有还款的想法,就是为了把钱套现出来。欺诈行为是最恶劣的行为,所以在贷款之前需要提前去做分析和判断,看一看这笔钱是不是一个正常交易,还是个欺诈交易。
这是金融数据分析里面的场景。怎么做呢?首先采集数据,第二绘制一些关系图谱,第三完成机器学习的建模,建模去完成欺诈的风险模型。
在违约预测里面也会给人的信用去做一个打分,把它称为叫分享评分。芝麻信用是支付宝去衡量一个用户的信用等级,分数段是在350 分到 950分之间。除了分数以外还会有一个授信的额度,授信额度也可能是机器学习去预测出来的。## 智能识别
第四个部分是一些场景的智能识别。
场景 4、智能识别
OCR 识别身份证及银行卡
在业务票据整理过程中,可以通过图像识别等技术完成数据的收集,比如通过OCR 完成身份证的识别,银行承兑汇票,银行卡等
银行体系里面也有一些图像识别的任务,现在很多票据还是柜台要去打进去的。也很多章,像公章、人民章、法人章啊。这些章核验的时候要通过才行。所以有些银行会让企业盖三次章,看哪一个章可以核验通过。如果没有核验通过还要再去找企业再重新盖章,这个过程就是一个纸质版的签名。
未来会有更多的数字化的签名,甚至区块链的应用在这个场景里面都会使用到。那目前使用比较多的还包括了像图像识别,OCR的文字的识别,身份证的识别等等。
以上的环节都是属于金融场景的环节。
再有就是量化交易,其实大家可以思考一下,银行会有这种炒股票的业务吗?一般来说银行没有。股票的业务是属于证券公司,一般证券公司才会有股票相关的一些业务。在证券公司里面会有智能的一个量化交易。
场景 5、量化交易
策略收益,年华收益,基准收益,阿尔法,贝塔,夏普比率,最大回撤
那么以证券业务为例的话会考量一下不同的交易策略是怎样的,策略的收益、年化的收益、基准的收益等等。中间也有一些因子的指数去做回归。
总结一下的 Fintech 场景,就如下图中列出来的一些场景。
有个贷信用违约的场景,这些场景里面会对应一些机器学习,包含了违约预测,评分卡,多元线性回归,逻辑回归,XGBoost/LightGBM。
AI的算法会有评分卡模型,它的目标是要建立一个评分的规则,可解释性比较强。分类任务可以用逻辑回归,也可以用XGBoost 等等这样一些模型。
反欺诈模型也是一个分类任务,也可以用集成学习的一些模型和树模型。树模型应该是之前给大家介绍的模型,XGBoost和 LightGBM这两个模型的使用频率都很高,在银行的业务体系里面使用率也很多。
流失预警是要预测一下客户会不会流失,如果要流失可以提前判断出来就可以进行干预。会包含决策树,神经网络,RFM。
精准营销的需求会有对用户的特征的提取,这个其实是最关键的。精准营销的本质是了解客户的特征,然后再去做相似度的计算,帮你挖掘潜在的客户都有哪些。会包含用户画像,聚类分析,Embedding,Node2Vec 以及标签传播。
推荐系统不光是在大厂里面使用,很多银行现在都建了 APP,只要有 APP就会构建推荐系统。平安有一款 APP 叫口袋通,这款 APP里面平时会推荐一些新闻,那新闻背后的推荐过程就是智能推荐的原理。
智能推荐如果要用模型的话有购物篮分析,还有关联规则,以及 Google提出来的 wide&deep。
此外还有量化交易,量化交易是属于一个相对独立的部分。因为它需要一个模拟环境实时的去从交易所这边抽取出来一些真实的数据,然后会委托帮助客户去做下单策略,这是量化交易。
量化交易的本质是你对股票的理解,把它转换成为交易策略的代码自动去完成执行。在量化交易里面可以使用Python帮我们去分析一些策略,看看策略是否有效,也可以把这个策略放到一些环境中进行执行。
这里给大家列出来的一些软件,比如说像国内的 vnpy, jointquant等等。还有像 ricequant这些都可以从它们上面获取到一些实时的交易数据。总的来说这个行业是一个比较新兴的行业,既有一些传统的金融人员在做数字化的转型,有很多互联网的大厂的公司也想要进入到这个领域里面去。这些公司就包括了像蚂蚁金服,京东金融等等。
好,这节课呢,就是将 Fintech的应用场景给大家做了一些介绍,我知道很多小伙伴都会觉得这篇文章可能概念介绍的太多,但是这些都是一些必须的,做机器学习最主要的还是要从业务和场景出发,理解业务和场景都是必须要做的事情。
那么下节课,咱们就进入实战,关于 Python 的量化交易的一个板块。
]]>[TOC]
Hi,你好。我是茶桁。
那今天我们是来讲解另外两个 Boosting 的工具,首先是微软出品的LightGBM。
LightGBM 是微软提出来的, 是属于 XGBoost 的升级版,也曾经是 Kaggle里面使用模型最多的机器学习的神器。当然,目前 LightGBM 之外,BERT 以及GPT 都越来越受关注,但是 LightGBM这么久了,依然还是占据一席之地,依然还是某些性质及任务要求下的首选。
Light 的概念就是轻和快,GBM 全称为 Gradient Boosting Machine,这个GBM 就把它理解成就是 GBDT,所以它其实就是轻量级的GBDT,而且是升级版本。所以我们看一看,它到底做了哪些轻量级的一些操作。
常用的机器学习算法,例如神经网络等算法,都可以以mini-batch
的方式训练,训练数据的大小不会受到内存限制。
GBDT在每一次迭代的时候,都需要遍历整个训练数据多次。如果把整个训练数据装进内存则会限制训练数据的大小,如果不装进内存,反复地读写训练数据又会消耗非常大的时间。对于工业级海量的数据,普通的GBDT 算法是不能满足其需求的。
LightGBM 的提出是为了解决 GBDT 在海量数据遇到的问题,让 GBDT可以更好更快地用于工业场景。
我们看整个的例子,先让大家有个直观的感受。
我找了四个数据集,然后用 XGBoost, XGBoost_approx 以及 LightGBM来做一个比较. 其中 XGBoost_approx 是 2016 年左右提出来的 XGBoost的近似版.
一共做了两种对比,一种对比是它的内存消耗,看谁的内存占用更小。可以看到LightGBM 明显比 XGBoost 的内存会更小一点。同样的数据集只有大约 1/6左右。
指标除了内存以外还是要关注一下评价结果,结果上 LightGBM 和 XGBoost差别并不大,甚至有些情况下还会更好。所以在结果差别不大的情况下,内存只有原来的1/6.
除此之外,训练速度上还做了一个对比
这里seconds
代表的是时长,这四种训练集里面,XGBoost 的2016 版比原来 2014版速度要快,因为它是近似方法,还记得上节课说的直方图吧?LightGBM明显还比它所有的这些版本都要快,大概快了有 1/10。
所以我们用了 1/10 的速度,用了 1/6的内存,得到了一个还不错的结果,这两个模型也比较相当。这两个模型还可以帮我们来自动处理一些特征的确认值,XGBoost是不支持类别特征的,而 LightGBM 支持类别特征。
那么问题来了, 为什么 LightGBM 会更快呢?
让我们稍微拆解了一下模型复杂度的流程
\[模型复杂度 = 树的棵数 \times 每颗树的叶子数量 \times 每片叶子生成复杂度\]
树的个数越多就会越复杂,每棵树的叶子的数量越多应该也越复杂。然后再乘上每个叶子节点的生成的复杂度,这个生成的复杂度又会等于特征数量乘上候选的分裂点的数量以及样本的数量。
LightGBM 看到的这样的一个特点,就想要从这三个维度做一些简化。
第一个简化,减少分裂点的数量。采用 Histogram 算法。
这个其实跟 2016年的版本是完全一致的。采用了直方图的方式先减少分类节点数量。
然后第二个,GOSS 算法。
这个方法基于梯度的单边采样算法,减少了样本的数量。
还记得上节课咱们使用 XGBoost 时给大家讲的 subsample 吗?XGBoost里面用的是 0.5,比如原来是有 1 万个样本就用 50%,也就是说只用了 5,000个样本来进行训练。如果是随机的选择 5,000个样本来做训练,对于精度来说是有损失的。目的是希望更快,但是会损失一定的进度。
第三, EFB 算法。
它使用互斥特征捆绑算法,减少特征的数量。原来有 100个特征,现在随机抽取 80 个特征,对精度也是有一定损失。
XGBoost 的预排序( pre-sorted)算法是将样本按照特征取值排序,然后从全部特征取值中找到最优的分裂点位。预排序算法的侯选分裂点数量= 样本特征不同取值个数减 1。
这是一个排序的方法,我们可以按照切分的方式来进行一个顺序的切分。原来的切分的方式每一个地方都可以进行切分,而现在分成了三个桶,就只有两种切分的方法,如下:
这样每一个桶就当成了一个整体和一个集合,相对来说,分裂节点的数量就减少了。这种方式其实就是Histogram 算法,2016 年的 XGBoost 就是采用的这种算法。这种方式替代了XGBoost 原先的 pre-sorted 算法。
因为做了合并,原来是三个样本的gi
,现在变成了一个Gi
。Gi
是求和,这里的Gi
就把三个里面一阶导数的梯度做为个累加,就等于0.1
。Hi
二阶梯度相加就等于0.29
。这样就是第一个桶的一个特征,合并以后Histogram变成了三个样本。整体的一阶导数是0.1
,二阶导数是0.29
,以它来完成运算。
第二个桶也有三个样本,一阶的导数之和是0.79
,二阶导数之和是0.12
。第三个桶是两个样本,一阶和二阶导数分别是0.67
和0.06
。
那么未来做分割的时候,我们就只要在这个基础上来做分割就好了。因为它前面已经合并成了一个整体,这种方式候选的节点的数量变成了桶的个数-1
,也就是k-1
。
它的思想就是连续的浮点特征值离散化成k
个整数,同时构造一个宽度为k
的直方图,即将连续特征值离散化到k
个bins
上。当遍历一次数据后,直方图累积了需要的统计量,然后根据直方图的离散值,遍历寻找最优的分割点。
XGBoost 是需要遍历所有离散化的值,LightGBM 就只需要遍历 k个直方图的值。其侯选分裂点数量就等于k-1
。
除了这种方法以外,LightGBM 还有两种优化策略。GOSS算法的全称是Gradient-based One-Side Sampling
,基于梯度的单边采样算法。
刚才是用 subsample ,XGBoost里面专门有一个参数,可以把它设成0.5
,也就是50%,它会有精度的下降,会有损失。
那我们现在来思考一个问题,样本的梯度是大好还是小好呢?我们在机器学习过程中是通过什么来去更新我们的参数?
机器学习有一种方式叫做梯度下降,梯度下降证明了你学习的方向。如果方向梯度越大,就代表我学习方向越明确。如果梯度已经变成了0.00001
,就不好学习到内容。所以对于样本的梯度来说,其实希望它大一点好,可以持续降低。梯度越大就证明学习方向是非常明确的,更容易学到内容。
GOSS 方法它想到,之前 50% 随机采样,舍弃了50%,如果舍弃的是那些梯度比较大的样本在精度上更容易有损失。所以 GOSS就希望先保留那些梯度大的样本,给它设了一个阈值。比如说图例中阈值设为0.1
,梯度大于0.1
我们就全部保留,因为这些样本它是属于好的样本。
好样本的梯度怎么理解?
我们来看这张图,红色的点是一个样本点。我们的样本在这里,预测出来的值是\(y'\),实际值是 2
,其实在后面已经不容易学到内容了。点 1
明显梯度会更大一点。
回到上面那张数据的图上,6
和 7
的的gi
一个是0.7
,一个是0.6
,都大于0.1
,所以这两个是必须要留下来,因为它还没有学好。剩下的小于0.1
的样本,我们就保留了1/3。这是一个随机性的,假设现在选中的是2
,4
这两个。因为8
减去6
,7
之后还剩下六个样本,六个样本里面的1/3,就是保留两个,我们随机保留了 2
和 4
。
最后结果是我们也保留了 50% 的样本进行采样,但是这 50% 是使用了 GOSS算法计算之后的结果。
前面的直方图我们是已经计算好了的,所以有三个bin
。对于第一个bin
的计算,做了1/3
的采样,选中的2
。可以想成是人大代表,代表 3 个人。因为是从 3个人里面选举出来的这一个人作为代表,这也是 3 个人的情况。
那么对于第一个桶来说,里面只抽出来了样本2
,2
是一个代表,所以它相当于是 2
的gi
原来是 hi
是0.04
,同理就是 \(0.04 \times 3 =0.12\)。
那对于第二个桶,是一样的计算方法。6
是全部保留,4
代表了 3 个,所以是 Gi
就是6
加上 4
乘 3
, 那就是 Hi
也是一样的算法,
那最后一个桶,bin3
里也是这么计算得来的。
GOSS 算法的思想是通过样本采样,减少目标函数增益 Gain
的计算复杂度。单边采样,只对梯度绝对值较小的样本按照一定比例进行采样,而保留了梯度绝对值较大的样本。因为目标函数增益主要来自于梯度绝对值较大的样本=> GOSS 算法在性能和精度之间进行了很好的权衡。
那最后,LightGBM 内还包含了一个 EFB算法。刚才咱们的采样可以理解为行采样,就是从 10,000 个样本减到 5,000个采样, EFB 是列采样。 EFB其实是互斥特征绑定法,Exclusive Feature Bunding
。
机器学习过程中,有的时候会用 one-hot 编码把类别特征转化成为0-1
特征。 one-hot 就是将你要的特征变为1
,其它变为0
。比如说,在一个星期中,我要星期三,那么这组特征就会变成0010000
。
如果用了 one-hot 编码会出现大量稀疏特征。什么叫稀疏,0
代表没有,大量为 0
的叫稀疏。这个过程放眼望去肯定是0
多,0
代表空,1
代表有价值有数据,所以它是大量的稀疏特征。
EFB 就发现了这样的一个逻辑:XGBoost 有很多人提前做了 one-hot编码,就会有大量稀疏特征。那能不能把这个大量稀疏特征给它合并到一起?这也就是EFB的思想。特征中包含大量稀疏特征的时候,减少构建直方图的特征数量,从而降低计算复杂度。
比如说第一个 feature
,0
这个代表为空没有含义,1
和 2
有 10个值,这是有价值的。
第二个特征也是一个稀疏特征,有 95 个是没有的意义,只有 1
和 2
是有价值的。
那作者就想到,能不能把 1
和 2
这两个特征合并成一个新的特征,叫 feature 1'
。怎么合并?首先feature 1
里的 1
和 2
依然把它作为 1
和 2
, feature 2
里的 1
和 2
跟 feature 1
里的1
和 2
不是一个概念,所以我们要把它做一个新的编码,把它称为 3
和4
。这里的 3
对应出来是原来feature 2
里的 1
, 4
对应的是feature 2
里的 2
。
原来 feature 1
里面是有 100 个特征的,现在增加了3
和4
,一个是 4 个,一个是 1 个,所以匀了 5个特征给后面补进来的部分,整个特征数是不能变的,所以最前面的0
特征,就从 90 个里面减去了 5 个,变成了
如果用 EFB方法把两个特征捆绑到一起合成一个新的特征,这个特征没有损失,是可以完全唯一的还原。因为它能发现有大量one-hot稀疏特征很容易进行合并,合并之后就可以让特征的数量大大减少,就不需要用刚才说的设置100 自动的抽 80 列,那样其实信息是有损失的,但是 EFB 不会有损失。
LightGBM的用法也是从引入包开始import lightgbm as lgb
,其参数也基本上差不多,我们来看下:
boosting_type
,训练方式,gbdtobjective
,目标函数,可以是 binary,regressionmetric
,评估指标,可以选择 auc,mae,mse,binary_logloss, multi_loglossmax_depth
,树的最大深度,当模型过拟合时,可以降低max_depthmin_data_in_leaf
,叶子节点最小记录数,默认 20lambda
,正则化项,范围为 0~1min_gain_to_split
,描述分裂的最小gain,控制树的有用的分裂max_cat_group
,在 group边界上找到分割点,当类别数量很多时,找分割点很容易过拟合时num_boost_round
,迭代次数,通常 100+num_leaves
,默认 31device
,指定 cpu 或者 gpumax_bin
,表示 feature 将存入的 bin 的最大数量categorical_feature
,如果 categorical_features =0,1,2, 则列 0,1,2 是 categorical 变量ignore_column
,与 categorical_features类似,只不过不是将特定的列视为 categorical,而是完全忽略Bagging 参数:bagging_fraction + bagging_freq(需要同时设置)
bagging_fraction
,每次迭代时用的数据比例,用于加快训练速度和减小过拟合bagging_freq
:bagging 的次数。默认为 0,表示禁用bagging,非零值表示执行 k 次 bagging,可以设置为 3-5feature_fraction
,设置在每次迭代中使用特征的比例,例如为0.8 时,意味着在每次迭代中随机选择 80%的参数来建树early_stopping_round
,如果一次验证数据的一个度量在最近的round 中没有提高,模型将停止训练那我们比较常见的参数配置如下:
1 |
|
现在咱们还是用之前员工离职预测的来做一个代码示例,在进行模型训练的时候,这里有一个和XGBoost 类似的地方,之前 XGBoost用的是自己的数据结构DMatrix
,在 LightGBM 里也有一个Dataset,也是几乎一样的用法,除了用官方的 Dataset方式进行封装之外,训练的时候要用 train
来进行训练,那这是一个官方的版本,不过我们这里用 sklearn提供的版本来使用。为什么要用它而不是官方版本,这是因为 sklearn的参数名称都比较统一,比如说我们的机器学习里面有个参数都叫n_estimators
, 而官方的 XGBoost 和 LightGBM 都称作num_boost_round
。为了和其他机器学习方式统一避免麻烦,所以我建议大家还是使用sklearn 里的方式来使用。
那我们这次为了看 LightGBM 的实际效果,还是使用官方的方法:
1 |
|
训练好之后,来看看结果:
1 |
|
后面还有一种方法叫 CatBoost。https://arxiv.org/pdf/1706.09516.pdf
这个方法只要知道它的一个大概使用情况就好。cat 不是猫,应该叫做catgorical,就是分类的概念。所以它是专门针对分类特征多的情况下提出来的boosting的算法。这个方法不一定效果好,但是它有可能对于分类特征多的数据集有奇效,所以你可以把这个方法作为一个备选,也可以尝试着去用一用,尤其是分类特征比较多的情况。
这里有一个数据集,是 kaggle 上的一个数据集。2015年航班延误数据,包含分类和数值变量:https://www.kaggle.com/usdot/fight-delays/data。这个数据集大约有500 万条记录,使用 10% 的数据,即 50 万条记录。
这里有 XGBoost,LightGBM 和 CatBoost,我们可以看一下大家可以看一看,XGBoost 和 LightGBM训练集非常好,但是测试集差很多,这种我们都知道,就是过拟合了。
但是 CatBoost 对于这个航班延误的数据集的训练结果是84%、88%,测试结果跟它相差不是很大,相比于 XGBoost 和 LightGBM来说,CatBoost 的过拟合程度是最小的,这是它的特点。此外时间最快是LightGBM,其次是 CatBoost,最后是 XGBoost。
这三种模型我们还对比了一下精度,还是来先看训练时间,最快的依然是Light, Cat 次之,最慢的依然是 XGBoost。那从训练的精度上来看,XGBoost 和LightGBM 差不多,但是 Cat 稍微差一点。不过不要认为 CatBoost就不行,就如之前说的,在一些特殊的分类特征更多的情况下,CatBoost的表现反而是最好的那一个,当然,80%以上的情况下,它的效果都会较差一点。
CatBoost 工具的 Github 地址:https://github.com/catboost/catboost,还有https://catboost.ai/en/docs/
我们还是可以直接调包去使用,它的模型包跟前面的模型包基本上差别也不是很大。
构造函数:
learning_rate
,学习率depth
, 树的深度l2_leaf_reg
,L2 正则化系数n_estimators
,树的最大数量,即迭代次数one_hot_max_size
,one-hot编码最大规模,默认值根据数据和训练环境的不同而不同loss_function
,损失函数,包括Logloss,RMSE,MAE,CrossEntropy,回归任务默认 RMSE,分类任务默认Loglosseval_metric
,优化目标,包括RMSE,Logloss,MAE,CrossEntropy,Recall,Precision,F1,Accuracy,AUC,R2fit 函数参数:
X
,输入数据数据类型可以是:list; pandas.DataFrame;pandas.Seriesy=None
cat_features=None
,用于处理分类特征sample_weight=None
,输入数据的样本权重logging_level=None
,控制是否输出日志信息,或者其他信息plot=False
,训练过程中,绘制,度量值,所用时间等eval_set=None
,验证集合,数据类型 list(X, y)tuplesbaseline=None
use_best_model=None
verbose=None
那对于员工离职预测这个问题,我做了一版,大家可以看我下面的代码自己进行尝试:
1 |
|
然后:
1 |
|
好,那到这里,关于 Boosting的几种工具就都给大家介绍完了,来简单总结一下,这三种工具,LightGBM效率是最高的,在 Kaggle 比赛中应用多, CatBoost对于分类特征多的数据,可以高效的处理,过拟合程度小,效果好。XGBoost,LightGBM和 CatBoost 的参数都比较多,调参需要花大量时间。 Boosting 集成学习包括了AdaBoosting 和 Gradient Boosting, 那 Boosting就只是集成学习中的一种,还有 Bagging 和 Stacking。
最后留一些问题啊给大家去思考一下,那这些问题大家最好自己去梳理一下,然后写上自己的答案,把你的答案写到本文留言框里,我们来看看谁梳理的最好。
那除此之外,还有一个问题
那我们要做的题目:Action1,用我之前给大家使用过的:男女声音识别的数据集:voice.csv
链接: https://pan.baidu.com/s/1UgXmDZLOpVeXz21-Ebddog?pwd=5t4e提取码: 5t4e --来自百度网盘超级会员 v7 的分享
这个数据集中有 3168 个录制的声音样本,采集的频率范围是 0hz-280hz,已经对数据进行了预处理。
一共有 21 个属性值,请判断该声音是男还是女。
最后,使用 Accuracy 作为评价标准。
那我们之前在 BI 第二课的时候用的是 SVM 的方法进行预测,在这个 Action1中,大家试试用 XGBoost 和 LightGBM 的方式来进行。
那这个 Action1就是留给大家的一个实操作业,可以去我的代码库中找相关的示例,但是我还是希望大家能自己去做一下,不要一上来就去看参考。
]]>[TOC]
Hi,你好。我是茶桁。
学习总是一个循序渐进的过程,之前两节课的内容中,咱们去了解了 LR 和SVM在实际项目中是如何使用的,我给大家看了两个项目都是跟分类相关,一个是员工离职预测,一个是男女声音识别。
其实也能看到,男女声音识别也不一定都要用神经网络,能找到一些关键特征把它转化为结构化的数据你也可以用机器学习来完成预测,而且机器学习的效果还是非常好,基本上都有百分之97,98 的准确性。
那今天这节课主要给大家讲解的是「机器学习的神器」,也是今天最主要的内容。
这个内容希望大家多去仔细阅读,如果你遇到哪些问题可以给我留言,文章下或者私信都可以,基本上,一些容易解答的问题我都会给予回复,大家保持一个良好的学习的方法。
这些机器学习的神器都跟集成学习相关,先给大家看一个概念叫集成学习。集成学习就是把多个分类器合到一起,可以把它理解成叫三个臭裨将顶个诸葛亮。
集中学习里面有些策略,Bagging是一种,它像一个袋子一样,数据是放到袋子里面去,叫有放回的抽样方式。这个袋子里面如果你要做一个分类的模型会按照少数服从多数。最简单的就是一个陪审团,看一看大家投票的情况,这是分类问题。回归问题我们要用的是大家的平均值,你预测一下薪酬,他预测一下薪酬,把大家预测结果相加以后除上个数就是求平均值。这些都是一个banging 的策略,集中学习把这些大家的结果给合并到一起。
Stacking 叫做堆,什么叫 Stacking?上图中下面的部分就是Stacking,我们把它分成两类分类器,分类器1,也就是前面的Classifier
做了特征的提取,分类器2,Meta Classifier
做了分类的过程。它是属于先后两阶段,先做第一种再做第二种,这是有先后逻辑顺序关系。如果是Bagging是没有先后逻辑关系。它是一个并行方法。你做你的,我做我的,最后我们可以综合起来,这个结果没有先后逻辑关系。而Stacking的话是有一个先后逻辑关系的,这是集成学习的不同种的学习的方式。
还有一种学习方式的话叫 Boosting,Boosting中文可以把它称为叫提升,它也有先后的顺序。
我们看这张图,原始的数据给了模型,第一个分类器模型做了以后得到一些新的一些数据,再喂给第二个模型,然后再生成一些数据再喂给第三个模型,这三个模型之间是有顺序的。先计算第一个,再计算后面的第二个,再计算第三个,所以这种Boosting 的方法是有一些顺序的关系。
通过 Boosting的方式可以把弱分类器结合到一起形成一个强的分类器,这是它的一个 Boosting的关系。Boosting 有两个比较重要的算法,一个 AdaBoost(自适应提升),一个是 GradientBoosting(梯度提升)。这两种方法在咱们之前的机器学习课程中都有详细的讲解。
AdaBoost是使用前面的学习器用简单的模型去适配数据,然后分析错误。然后会给予错误预测的数据更高权重,然后用后面的学习器去修复。
所以集成学习是有三种模式,Bagging 是一种,Stacking 是一种,还有就是Boosting。总的来说都是把多个分类器组合起来,会胜过一个分类器。这几中模型之间比较常见的模型是Boosting 和 Bagging。
我们对这两个做个对比。
并行的方式和串形的方法没有什么特别的好坏之分,如果要去判断也是跟数据相关。我今天讲解的神器是属于最后一种,就是Boosting 的方式,所以它应该是一个串形的方法。
这种分类器里面有很多种,上面我介绍了两个算法,一个是 AdaBoost,一个是GradientBoosting,那我们主要看看后面这种算法。这个算法中包含了几个比较重要的工具,有XGBoost、LightGBM、CatBoost 以及 NGBoost,实际上是对 GBDT方法的不同实现,针对同一目标做了不同的优化处理。基本上出现的年限如下:
Boosting 这种方式典型代表是 XGBoost、LightGBM 和 CatBoost,NGBoost采用的 boosting 的方法跟前三种 boosting不太一样,通常我们机器学习的神器还是指的前面三种。当然,近些年还有一些新的工具,比如H2O GBM,以及 TensorFlow BoostedTrees(TFBT),咱们我们不去探讨它们,以后有机会写进阶课程的时候再说。
XGBoost 最早提出来的是 2014 年,它是由陈天奇提出来的,提出来以后在Kaggle 的比赛中是大火,基本上在 2014年那个阶段只要你参加机器学习的比赛必用 XGBoost,而且第一名基本上都是XGBoost,效果是最好的。
三年之后在 2017 年,微软提出来了一个 lightGBM 的版本,它是站在原来的XGBoost 基础上做了一些简化,让它的版本更轻,轻的一个优势就是快。所以LightGBM 占用内存更少,速度更快。
三个月之后俄罗斯的一家公司叫 Yandex 又做了一个新的版本,叫CatBoost,这家公司你可以把它理解成是俄罗斯的Google,是个科技巨头,也做测速引擎,同时也开源很多的机器学习的工具箱,那我们现在用的CatBoost 就是 Yandex 提出来的一个模型。
https://arxiv.org/abs/1603.02754
XGBoost 是 2014 年提出来的模型,它本身是基于树的。一般来说用的是 CART回归树。这个是一个决策树,这是它的机器学习的模型。
我们现在要去完成一个预测 y值,一个人是否喜欢电子游戏
。就是电子游戏的市场跟哪些特征相关,年龄、性别、职业这些特征。前面是我们的X,有很多 X,最后是那个 y。
现在如果要建一棵树,用一棵角色树可能会建出来如图的一个过程。先判断他的age 是不是小于 15 岁,如果小于 15岁就走左边,再判断他的性别是不是男性,如果是男性我们就认为他会玩两个小时,如果不是男性就是0.1 个小时。如果他是大于 15 岁我们就认为他是-1。
这个是其中一棵树的一个结果,他的预测是在叶子节点里面会有一个数值,这等于他的输出,所以输出都是在叶子节点里,中间那颗分支都是按照不同的逻辑来做个判断。
XGBoost 它本身是集中学习,其实它背后的那个过程原理叫GBDT,大家先知道就好了,我们今天没有详细展开GBDT,这个是属于它的理论。就是说我有多少棵树一起来学习。就是之前看到那张图上的模型,依照数据流,Model1先去做,做完以后 Model2 去做,再做完以后 Model3去做。它本身的原理就是多棵树相加。
那 GBDT 的理论版本是这样,XGBoost是它的工程版本。工程版本的目的是要更加的泛化,所以它主要是在原来 GBDT的基础上又加了一个叫做正则化项:
\[\begin{align*}目标函数 = 损失函数 + 正则化项 \\Obj(\varTheta) = L(\varTheta) + \Omega(\varTheta)\end{align*}\]
这里,
我们的目标函数是由损失函数加正则化项。一般我们要判断的是想让它的预测结果和实际值更小,这个叫loss functio,之前课程中,我们一直跟 loss 打交道。多出来的结果叫 y',和实际值的 y 之间, 我们会计算一个损失函数。
比如说我们要用用 MSE 做回归值,(y' - y)^2,这等于它 lossfunction。
所以,正则化项意义就是对我们的叶子节点做了一惩罚项。
\[\begin{align*}\Omega(f_t) = \gamma T + \frac{1}{2}\lambda \sum_{j=1}^T w_j^2\end{align*}\]
整个\(\Omega(f_t)\)用于控制树的复杂度,防止过拟合,使得模型更简化,也使得最终的模型的预测结果更稳定。
这个复杂的公式里, T代表的就是叶子数量,你想,如果你的决策数叶子数量很多,这个数模型就会很复杂。
w_j 是叶子分数的 L2 正则项,如果它的叶子的分数也是很大的话,也比较复杂,所以我们希望这棵树简单一点,没有这么多的叶子节点,而且叶子节点的数值也比较小一点。这样就是一个稍微小巧一点的模型。
那为什么要加正则化项呢?我给大家举个场景,你自己体会一下。我们的目标是希望损失函数最小化,比如说我们目标是想要挣更多的钱,有两种人a 和 b。a 月薪是 2 万块钱,他每天就是朝九晚五,办公室的白领。b是网约车司机,每天早上 6 点出门,晚上 12 点回家,他也是月薪 2万块钱。
你想办公室的白领他的模型相对来说比较简单一点,后面我们的系数就是大家不需要太多去努力,大概读出来结果-1,-0.1,+1,+0.1 就好了。
网约车司机他会非常的奔波,很累。可能这个系数抖动比较大,最后得出结果+10,+20, -10, -20 等等。
现在想一想,同样月薪 2 万块钱,你们希望是做 a 还是做 b呢?我们同样可以得到这样一个结果,是希望是像办公室白领一样轻轻松松可以达到你的loss function这样的一个目标,还是希望像网约车司机一样特别的辛苦,很复杂。早上 6点出门,晚上是 24 点回家。那大部分人应该都是 a,这逻辑是一样的。
我们希望我们的那棵树没有那么的复杂,也能达到比较好的效果。所以在我们的目标函数过程中统计了两个代价,一个代价叫做lossfunction,损失代价,还有一个就是模型的代价。模型代价跟谁相关呢?跟模型的叶子数和叶子的分数相关。
以上就把目标函数的两个过程,损失函数和正则化项给大家讲完了。
接下来我们就详细的看一看它是怎么去做的。
预测函数,样本的预测结果=每棵树预测分数之和。
\[\begin{align*}\hat y_i = \sum^k_{k=1}f_k(x_i)\end{align*}\]
我们对目标函数进行优化
\[\begin{align*}Obj(\varTheta) & = \sum_il(y_i, \hat y_i)+\sum_k\Omega(f_k) \\\Omega(f) & = \gamma T + \frac{1}{2}\lambda ||w||^2\end{align*}\]
我们在原来的 loss function里面加了一个正则化项,下面的那个是正则化项的公式,前面是叶子节点的数量,后面是叶子节点的分数。我们希望目标函数最小化,把这个目标函数写成以下的一个过程:
\[\begin{align*}Obj^t = \sum_{i=1}^n l(y_i, \hat y_i^{t-1} + f_t(x_i))+\Omega(f_t) +constant\end{align*}\]
集成学习的树是由多棵树来完成的,如果你现在做的是 t棵树,前面那个结果就是 t-1 棵树。t-1 棵树的结果加上
这两个过程我们都是拿它做一个 loss function的一个组合,再加上正则化项,再加上一个常数项,这等它的目标函数。
对这个函数改进,进行二阶泰勒展开:
\[\begin{align*}f(x+\varDelta x) \approx f(x) + f'(x)\varDelta x + \frac{1}{2}f''(x)\varDelta x^2\end{align*}\]
那关于泰勒展开,我在数学基础篇里有一篇专门来讲这个。现在我们只要知道它是一个定理,这个定理就是说你的变量
\[\begin{align*}\hat y_i^{(0)} & = 0 \\\hat y_i^{(1)} & = f_1(x_i) = \hat y^{(0)} + f_1(x_i) \\\hat y_i^{(2)} & = f_1(x_i) + f_2(x_i) = \hat y^{(1)} + f_2(x_i) \\\cdots & \\\hat y_i^{(t)} & = \sum_{k=1}^tf_k(x_i) = \hat y_i^{(t-1)} +f_t(x_i)\end{align*}\]
那这个式子就可以这样推理得到。其中\(\haty_i^{(t)}\)是第 t 轮的模型预测,\(\haty_i^{(t-1)}\)是保留前 t-1 轮的模型预测, 而
我们可以做多阶泰勒展开,二阶泰勒展开呢相对简单一点。现在只要知道有这么一个概念,这个概念是做一个近似的过程即可。今天就不去讲这个数学的推导了,关于如何利用数学进行推导,大家回到我数学篇里专门有一篇讲泰勒展开的一节去好好补一下基础。
那这个过程就还是一个 loss function,这里就是一个任何的 function都是一样的。后面这个f'(x)是一个导数,f'是一阶导数,f''是二阶导数,就是做完一阶以后再去做一阶。
一阶导数乘上\(\varDeltax\),再加上二阶导数乘上\(\varDeltax^2\),这等于二阶泰勒展开,这是一个定理。那这个定理代入的就是刚才这套过程。
我们来看定义:
$$ \[\begin{align*}g_i & = \partial_{\hat y^{(t-1)}}l(y_i, \hat y^{(t-1)}) \\h_i & = \partial^2_{\hat y^{(t-1)}}l(y_i, \hat y^{(t-1)}) \\Obj^t & \approx \sum_{i=1}^n \left [ l(y_i, \hat y^{(t-1)}) +g_if_t(x_i) + \frac{1}{2}h_if_t^2(x_i) \right ] + \Omega(f_t) + constant\end{align*}\] $$
这里,f(x)就是等于\(l(y_i, \haty_i^{(t-1)})\),后面这个
这样目标函数我们就把它做了个改写,我们把它用二阶泰勒展开做了个改写,中间的一阶导数项用g,二阶导数项用 h,所以它是个约等于。
有了这个流程以后,刚才这是个约等于,是用二阶泰勒展开。还可以再去详细的去看一看,f_t(x_i),这是第 7棵树的结果,因为咱们用的是个决策树,它的结果是在叶子节点,那么叶子节点可以作为定义。它叶子节点假设是w,那它的叶子节点的平方也是 w的平方,我们再加上后面的正则化项,正则化项是刚刚我们定义好的
\[\begin{align*}Obj^t & = \sum_{i=1}^n \left [ g_if_t(x_i) -\frac{1}{2}h_if_t^2(x_i) \right ] + \Omega(f_t) \\& = \sum_{i=1}^n \left [ g_iw_{q(x_i)} + \frac{1}{2} h_iw^2_{q(x_i)}\right ] + \gamma T + \lambda\frac{1}{2}\sum_{i=1}^T w_j^2 \\& = \sum_{j=1}^T \left [\left( \sum_{i\in I_j} g_i \right) w_j +\frac{1}{2} \left ( \sum_{i\in I_j} h_i + \lambda \right ) w_j^2 \right]+ \gamma T\end{align*}\]
T 为叶子节点数量,
那么我们就可以看到,g 是做了一个求和项,h 也做了一个求和项。所以我们就把一阶导数的求和用一个大 G 去表达,
\[\begin{align*}Obj^t = \sum_{j=1}^T \left[ G_jw_j + \frac{1}{2}(H_j + \lambda) w_j^2\right] + \gamma T\end{align*}\]
以上就把它的目标函数做了一个改写,那现在我们是希望这个目标函数是越大越好,还是越小越好?自然是希望它越小越好。那什么时候得到最小值?导数为0 的时候,就是对\(\frac{\partial Obj}{\partialw_j}\)求偏导,,那求偏导就得到:
\[\begin{align*}\frac{\partial Obj}{\partial w_j} = G_j + (H_j + \lambda)w_j = 0\end{align*}\]
导数等于 0 的时候,我们就可以求到极值,它等于 0的时候我们可以求解得:
\[\begin{align*}w_j & = - \frac{G_j}{H_j + \lambda} \\Obj & = -\frac{1}{2} \sum_{j=1}^T \frac{G_j^2}{H_j + \lambda} +\gamma T\end{align*}\]
先求得
所以要想让目标函数最小,我们可以直接求出来 w_j 的极值以及最小化的那个Obj。
有了这个过程之后我们一起看一看,我们的 XGBoost是怎么去进行运算的。
我们的 Obj的目标函数也是称为一个叫结构分数(打分函数),我们希望这个结构分数越小越好。越小就代表它这个结构越稳定。
我们看图,第一个部分,判断 is male 为 yes 的时候的叶子是一个样本,为no 的时候是一个样本,那判断 age < 15 为 no的时候是三个样本。如果三个样本输出的结果的话,我们的的大 G就是三个样本的之和,大 H 也是这三个样本的 h,二阶导数之和。
Obj是衡量模型好坏的标准,我们希望这个分数越小越好,就是这个数会更加的稳定一些。
那怎么样去求解这个 Obj让它更小?刚才我们已经找到了这个机制,也就是
\[\begin{align*}Obj & = -\frac{1}{2} \sum_{j=1}^T \frac{G_j^2}{H_j + \lambda} +\gamma T\end{align*}\]
这样 Obj会比较好一点。那我们的树要去做分割,大家知道这个学习过程中的树是一点点长出来的,长出来的话叶子节点做分割就会成为一个父亲和孩子的一个结构。那要不要做分割的依据是啥?孩子的Obj 应该要更小一点才会更好。所以你要去做的事情我们把它称为叫做一个Gain,Gain 就是你分割的一个条件。
\[\begin{align*}Gain = \frac{1}{2}\left[ \frac{G_L^2}{H_L + \lambda} + \frac{G_R^2}{H_R+ \lambda} - \frac{(G_L + G_R)^2}{H_L + H_R + \lambda}\right ] - \gamma\end{align*}\]
这个式子中的几个部分如下:
Gain 等于父亲啊减去孩子,也就是分割前的 Obj 减去分割后的左右Obj。如果说,父亲的 Obj 减去孩子的 Obj 等于 Gain,那么 Gain 如果小于0,还要不要做分割?那么要记得,Gain<0,那说明孩子比父亲还不稳定,那这个节点就不做分割,我们要找 Gain>0的点。那 Gain>0 也有很多,我们要找其中最大的来做分割。这是 XGBoost的一个过程。
那这里的可能性多不多我们怎么做?分裂节点的分裂,我们以这五个样本为例:
这是一个叶子节点,这叶子节点里面要去给它做分裂,先按照原来的 g_i,就是一阶的导数从小到大来做个排序,按照一定的顺序。
那 g1, g4 的顺序实际上就是 g1 比 g4要小,后面也是。按照这个顺序来做排序,排序以后,我们现在切分有几种切分的方法?如果是5个样本的话,从最前面和最后面分割毫无意义,我们要做的是从中间将它们一分为二,那无非就是[[1,4], [2, 3], [3, 2], [4, 1]]
。所以应该是四种结构。
我们有四种分裂的可能性,我们要找这种分裂的 Obj 最小的, 或者叫 Gain最大的。四种结构我们要求 4 个 Gain,在四个里面去找到一种最大的来去做判断。
我们知道,我们的样本数有可能很多,一般机器学习有可能有上万个样本。一个节点,最开始原来样本假设有1 万个,想想,1万个这样的样本要把它做划分的话,现在还是用从小到大给它规范好,这样的顺序来做划分有多少种划分方式呢?要计算1w-1
次,接近1 万次,9,999 次。
这只是划分一次,决策树的划分不仅仅分裂一次,分裂完一次以后下个节点还可以再做分裂。所以每次来计算的话,这个计算量相当于是个for 循环一样,计算量其实是蛮大的。
这是我们最开始的 XGBoost的版本,对于它的节点划分来说我们要计算1w - 1
次,如果它的这个节点的样本是 1w 的话。
原始的 XGBoost 的计算量会比较大,这是在 2014 年的版本。XGBoost的原理在 2014年提出来用的是一种贪心算法。这个贪心是从小到大的顺序来做了一个规范化,其实整个的顺序是有多种可能性的,我们是按照从小到大的顺序。然后去切的过程中,我们也只是看当下自有解,这是贪心计算方法。
但即使这种计算方法的计算量级也很多,在 2016年作者就提出来一种改进的方式叫做 histogram。
它用直方图,其目的就是把多个样本给它捆绑到一起。我们还是要做一个分裂的事情,再看刚才的结果,如果你在叶子节点上有1 万个样本,原来是要切分出来 9,999 刀,现在把这 1万个样本用绳子给它捆绑出来 128个桶。桶就是一个最小的单位,把前面这些样本都拿绳子捆到一起,后面这个捆到一起,一共有多少桶?128个桶。
我们如果再去做切分的时候只能在桶与桶之间来做切分,那它的划分的样式有多少种?原来的1 万要做 9,999 次的切分,现在 128 个桶,在做计算的时候就变成了 127次。这种方式是种降维处理,有点类似于像聚类的方式,这样我们的计算量就大大缩减,所以他的计算的时间就会快很多。
这是 XBGoost的一种近似的方法,近似的方法它不代表好,但是它是属于近似最优解,可以用更快的时间提升,基本上快几十倍还是有可能的。
以上就是 XGBoost 的原理,我们简单的再总结一下。
XGBoost 是在 GBDT多棵集成学习树上面做的优化。多棵学习树可以把它理解成model1+model2+...+modeln, 这是原来的集成学习的概念。XGBoost在原有基础上加了正则化项,正则化项的目的是防止过拟合。同时这个正则化项构造的很精巧,它用了一个公式,这个公式带进去以后经过一系列的转化,它的二阶项跟前面的1/2就消掉了。转化以后通过求偏导的方式可以把极值给求出来。前后相减的分裂过程是希望孩子的Obj 更小。也就是说我们的父亲的 Obj 减去孩子的 Obj 等于Gain,每一项的话都可以进行一个求解,我们希望它的 Gain 变得更大一点。
那么怎么做分裂呢?就会有尝试多种分裂的方法,找到一种更最大的分裂方式。在这么多种分裂方法过程中采用的是贪心算法,1万个样本就要切 1 万减 1 刀。作者在 2016年提出来了更快的方法,就是直方图的方法,这方法可以按照桶的个数来进行划分,所以它是一种近似的方式。
XGBoost算法的一些特点呢,就是讲树模型的复杂度加入到正则项中,从而避免过拟合,泛化性能好。其损失函数是用泰勒展开去完成的,用到了一阶和二阶导数,可以加快优化速度。它在寻找最佳分割点的时候,采用的是近似贪心算法,用来加速计算。那直方图还可以使用GPU 来进行计算,GPU就可以采用并性化的方式来进行计算,所以速度就会比较快。XGBoost 不仅支持CART 作为基分类器,还支持线性分类器,在使用线性分类器的时候可以使用 L1,L2 正则化。
XGBoost有点是速度快、效果好、能处理大规模数据、支持自定义损失函数等,缺点就是算法参数过多,调参复杂,不适合处理超高维度特征数据。
XGBoost 的通用参数:
booster[default=gbtree]
, 模型选择,gbtree 或者gblinear。gbtree 使用基于树的模型进行提升计算,gblinear使用线性模型进行提升计算。。silent[default=0]
,缄默方式,0 表示打印运行时信息,1表示以缄默方式运行,不打印运行时信息。nthread[default=缺省值是当前系统可以获得的最大线程数]
,XGBoost运行时的线程数。num_feature
, boosting 过程中用到的特征个数,XGBoost会自动设置。eta[default=0.3]
,为了防止过拟合,更新过程中用到的收缩步长。在每次提升计算之后,算法会直接获取新特征的权重。eta通过缩减特征的权重使提升计算过程更加保守,取值范围为[0, 1]
。gamma[default=0]
,分裂节点时,损失函数减小值只有大于等于 gamma 节点才分裂,gamma值越大,算法越保守,越不容易过拟合,但性能就不一定能保证,需要 tradeoff, 取值范围[0, ∞]
。max_depth[default=6]
,树的最大深度,取值范围为[1, ∞]
, 典型值为 3-10。min_child_weight[default=1]
,一个自己的所有观察值的最小权重和。如果新分裂的节点的样本权重和小于min_child_weight
则停止分裂。这个可以用来减少过拟合,但是也不能太高,会导致欠拟合,取值范围为[0, ∞]
。subsample[default=1]
,构建每颗树对样本的采样率,如果设置成 0.5, XGBoost 会随机选择50%的样本作为训练集。colsample_bytree[default=1]
,列采样率,也就是特征采样率。lambda[default=1, alias:reg_lambda]
, L2正则化,用来控制 XGBoost 的正则化部分alpha[default=0, alias:reg_alpha]
,L2正则化,增加该值会让模型更加收敛。scale_pos_weight[default=1]
,在类别高度不平衡的情况下,将参数设置大于 0,可以加快收敛。学习目标参数:
objective[default=reg:linear]
,定义学习目标,reg:linear,reg:logistic,binary:logistic,binary:logitraw,count:poisson,multi:softmax,multi:softprob,rank:pairwiseeval_metric
,评价指标,包括rmse,logloss,error,merror,mlogloss,auc,ndcg,map 等seed[default=0]
,随机数的种子dtrain
,训练的数据num_boost_round
,提升迭代的次数,也就是生成多少基模型early_stopping_rounds
,早停法迭代次数evals
:这是一个列表,用于对训练过程中进行评估列表中的元素。形式是evals = [(dtrain,'train'),(dval,'val')]或者是 evals =[(dtrain,'train')],对于第一种情况,它使得我们可以在训练过程中观察验证集的效果verbose_eval
,如果为 True,则对 evals中元素的评估输出在结果中;如果输入数字,比如 5,则每隔 5 个迭代输出一次nm - learning_rates
:每一次提升的学习率的列表我们看这个参数量还挺多的,XGBoost里面参数量确实还是比较多的,如果你用到的话可以回头再来看看我这篇文章,当作一个手册来看。默认情况下了,我会教给大家一些比较常见的参数设置,你直接用它就可以。
我这里还是给大家看一个示例
1 |
|
比如我们现在创建好了一个model,XGBClassifier
,创建好之后我们可以设置参数,比如一些树的深度等:
1 |
|
colsample
和subsample
,这个分别代表我们的列采样和行采样。设置行采样和列采样是让我们每次训练的时候更加的快一点,更加的轻量一点。这两个参数和树的深度参数,这三个参数都是比较常见的需要设置的参数。此外我们还需要针对你的任务来去做设置任务目标。
我们以 attraction 这个题目为例可以看一看怎么用
1 |
|
原来的 XGBoost 还有两种版本,一种版本的话是用它的DMatrix
,这属于官方封装好的一个结构。把原来切分好的数据集用 DMatrix来做的一个封装,封装好以后再进行训练。所以它是属于一个自己的一个训练的一个数据结构,叫DMatrix。我们以前用训练的话一般用fit
, 如果你用 XGBoost官方版本的话,它写的是train
,这是它的一个写法会稍微有一些区别。
带进去之后,其实后面都是调包的过程,train 完以后predict,得到一个结果,最后把这个结果进行输出。
那我们来去用 XGBoost来完成一下上节课我们完成的项目,首先还是数据的一些处理,这个和我们前几节课没有什么不同。主要就是我们要对一个参数进行设置;
1 |
|
这个就比我们之前调用其他模型来进行计算的参数量多了很多。然后我们用它官方的结构DMatrix:
1 |
|
这个套用就是把X_train
,y_train
给它放进去,它会封装一个自己的数据结构。所有样本都是一样,放进去训练的话就用自己的数据结构来去做训练。
1 |
|
param
是前面设置好的,我们的训练的一些参数设置成一个字典,这是常见的一些配置。训练以后就可以拿这个模型去做预测得到一个预测结果,再把这个结果进行输出。
1 |
|
我们打印的结果来看,发生了过拟合的情况。在做训练过程中,我们加了一个validation,现在 train-auc 和 valid-auc都有一个评分。现在呢,训练集基本满分,但是验证集和它差别很大。
这种情况下我们就可以调整参数,来防止过拟合状况。那我们首当其冲应该想到的就是eta 以及 max_depth, 深度过大会造成过拟合,eta本来就是为了防止过拟合而在更新过程中用到的收缩步长。
在进行调整之后,过拟合状况就好多了:
1 |
|
下一节课,我们来看看 Boosting 的另外一个版本,微软出的 LightBGM.
]]>[TOC]
Hi, 你好。我是茶桁。
上一节课,咱们用一个员工离职预测的案例来学习了 LR 和 SVM。
那今天咱们还是来看案例,从案例来入手。那今天的例子会带着大家一起来做一个练习,是一个男男女声音识别的例子。数据集来自于3,168个录音的样本,有些男性和女性,采集了一些特征,特征都是跟频谱相关的,一共有21 个属性,去基于这个属性来预测声音是男还是女。指标是以 Accuracy为评价指标。
我们看一看,这个例子我们该怎么去用刚才的模型来解答?可以先看一看要预测是哪一个字段,就是label
字段。除了label 字段以外,其他的类型都属于我们的特征类型。
想基于这个特征来预测 label思路是啥?先梳理一下思路。我们想想,跟上一节课的流程是一样的,如果对之前的那个离职预测问题能清楚它的结构的话。那这里我们的结构也是先去加载,加在以后去预处理。预处理环节先看看数据长什么样,尤其是那个target,就是这个 label 标签,平均还是不平均等等。
如果它是一个非数值类型需要给它做个映射,要采用这个 SVM 或者是 LR这两种模型,跟距离有没有关系?就这个模型的运算流程跟距离有关系吗?是有关系的。
SVM可以把它理解成是跟平面的距离,就是这个坐标跟超平面的那个距离是有关系的。LR是个分类器,它本身是跟线性回归相关的,它也是一条线,所以它跟距离也有关系。
这两个模型跟距离计算是有联系的,所以我们需要先做一个归一化的处理,归一化处理以后去调包,调包以后就可以完成预测。就是这样一个任务。
这个任务我们一起来写写代码,大家可以熟练一下,看看这个流程。
这是一个 csv数据集,voice.csv
。老样子,文末有数据集地址。
1 |
|
查看一下数据集的头部,来大概了解一下数据。一共大概有 21 个词段,最后是 label。label 现在是 male 和female。除此之外,还有哪些比较常见的数据探索呢?比如说缺失值个数,是通过isnall 加 sum 来做判断的:
1 |
|
打印出来查看的结果,所有数据没有缺失值。大小是 3168 个,21个指标,没有问题。也可以只使用shape[0]
来查看样本个数。
然后我们还要查看一下样本个数分别男女各是多少,使用label
来做一个判断进行筛选:
1 |
|
男性 1,584,女性1,584,所以这个数据是不是比较规整,它属于一个均衡的一个样本,而且它没有缺失值。
那下面要去建模之前先要把它分割成为特征,就是提取特征列和目标列。目标列可以把它称为叫label 列或叫 target
1 |
|
那咱们在[:, :-]
前面一个冒号代表是的取所有行,后面:-1
是除了最后一列之外。那为什么要去掉最后一列呢?因为最后一列是label,本来就是我们的目标列,在特征数据集内不应该存在目标。所以我们新的数据集不应该存在这一列。
那相对的,如果我们是要单独提取一个目标集,那就该反过来写:
1 |
|
现在 X 和 y就分别是我们的特征列和目标列。在调包之前一个很关键的过程就是把特征和目标提取出来,那么我们就用了iloc 的方式,通过-1 的方式给他做了个提取。
这些特征刚才说了,我们在用模型,如果你用 LR模型的话跟距离相关,我们还要做什么操作呢?还要给它做一个归一化操作。label现在是 male 和 female,还要给它做一个标签编码方式,还是使用 sklearn里面的 LabelEncoder,定义一个gender_encoder
,用它来做一个fit 和 transform。fit 是先指定我们的标签关系,然后 transform来做一个应用。
1 |
|
我们把前后的y
都打印出来查看一下区别。可以看到之前打印出来的是male
和female
,字母的形式,在操作之后就变成了1 和 0.所以 1 代表的是 male,0 代表的是female。这是我们编码的一个映射。需要把所有的这个类别特征转化成为数值。
然后在运行机器学习模型之前,尤其是跟距离相关的,我们还需要给它做归一化操作。数据归一化。我们这里用另一种归一化方式:StandardScaler
,也是一样的,叫正在分布归一化。都是给它做了一个标准化操作.
1 |
|
一样,先定义一个scaler
,然后用它去 fit 和 transform我们的 X,这个是对原始特征进行归一化。
归一化以后再把它喂回来,打一下我们的 X看一下归一化之后的结果是什么样。正态分布归一化之后,均值就变成了0,所以它有可能小于 0,也可能大于 0。
这个数据归一化是因为我们用了一个叫做正态分布归一化,正态分布的话,它的归一化是以0 为中心点,下图这样的曲线:
这个中心点\(\mu\)是 0,方差为1。所以我们就把它变成了这样的正态分布了,所以有没有小于 0的?一定要有的。
正态分布有一个叫 3Sigma 原则,正 1 和-1 之间的这个范围大概是68%,这叫 1Sigma,2 Sigma 的话是 95%,3 Sigma 是 99.7%。所以它不是一个-1到 1 的结果,正态分布它是有可能小于-3 的,也可能大于3,只是概率比较小。
如果我不用它,我用MinMaxScaler
会有小于 0的吗?我们来设置一下,这里 scaler 改为MinMaxScaler
。
1 |
|
再看一看这个结果, 这个结果有可能小于 0 吗?不会。
后面就是用数据集切分。还是一样,20%测试集,给一个随机种子数,我还是使用今年年份 2023.
1 |
|
数据切完之后现在要做数据建模了,这里建模你可以用逻辑回归也可以用我们的SVC。
1 |
|
这里用的是个非线性的 SVC, 创建模型 SVC 之后就fit,这个就是模型训练。
之后模型预测是把刚才训练好的这个结果去做一个predict,用训练好的模型进行预测得到我们预测的结果,得到预测结果还要判断一下预测结果的准确性。我们先把结果打印出来,SVM的预测结果。再看一看它的准确率。
准确率我们用了accuracy_score
,帮你计算它的准确性,把测试集的数据和预测的结果y_pred
来对比判断一下。可以看到预测的结果,1为男性,0 为女性。然后准确率达到了百分之 97以上,这个结果还是比较好的。当然,这个数据集也比较的简单。
好,我们再回过头来说说归一化的问题,和上一节课不同,我们这次使用的是一个正态分布的方式去做归一化。这两个哪个好哪个不好,没有统一的标准。没有说正态分布或者是0-1分布的归一化哪个更好,都可以尝试。找适合的数据集。只不过是让它变得更加标准化,看看是不是方便你去找到它的规律。没有一个特别的规范还说明说该用哪一个不该用哪一个,这两个其实都可以。
如果真是要说一下区别的话,我个人感觉正态分布更关注于人的一些属性。比如说人的身高、体重这种就比较偏向于正态分布,它更有可能找到好的结果。
那作为归一化处理,也仅仅是处理特征。我们称呼其为 weight,也就是权重。整个流程中,y 是不需要进行归一化的。回到刚才的例子里,一共有21 个特征,除了最后一个以外的话应该就是 20 个特征。20个特征里面如果用它原始的数值,比如说它是 0 到 1,000,另外一个是 0 到10,那它就自带的 weight 会很高,第一个是第二个的 100 倍。所以对于 X来说,如果不给它做归一化,它的量纲就不统一。那我们就让它的 weight都一样,就每个特征它的权重大小都是一致的。然后放到模型里面跟 y来做对比就可以。
我们再换个场景,如果我们做的是一个树模型。大家知道最经典的数模型是CART,如果我们用 CART角色树来做分类的话,请问需要提前做归一化操作吗?就是对我们的 X都要转化,比如说转化成 0-1 之间区间范围吗?
因为数模型的计算原理与距离无关,它的原理是跟距离没有关系的。它跟顺序有关系跟你的大小没有关系,所以对树模型来说的话你做不做对它的结果没有影响。但是对于LR、SVM 来说做不做会有影响,因为它的权重不一样。
好这是刚才我们整个的流程,刚刚就把整个的流程给大家梳理清楚了,现在这道题目跟上节课里那个离职预测的题目基本上是一致的过程。
这两个例子如果你能看明白,下来自己也能把它跑通,基本上机器学习应该就算入门了。比如说你至少能会调包去使用了,而且对它的流程,过程原理还是清楚的。这个是希望大家能明白它的整个过程原理。
那现在我们再给大家对比一下刚才我们两个项目讲解的两种分类器。一种叫LR,它的这个速度比较快,比较简单,通常用于我们的工业问题上。因为它速度快、资源少,而且方便调整,这是它的优点。
缺点是啥,刚才说了有 20 个特征,男女声音识别有 20 个特征。那请问 LR里面要学的参数量有多少?他要学习的一共就是20+1
个。针对这样的模型速度比较快,同样的代价就是容易欠拟合,准确性不高。有可能学的不好。
我们来看一下 LR 的准确性如何
1 |
|
可以看到,看到我们这两个模型里面,LR 准确性是稍微差一点。虽然也有97%了,但它的准确性是比较差的。
对于非线性模型为什么它差,是因为它不好发现非线性的特征。那谁可以发现?SVM可以发现。因为 SVM的原理就是把低维映射到高维,更容易找到非线性的特征。
处理非线性的特征同样要做数据归一化处理,因为它跟距离相关,刚才给大家讲过。
SVM的缺点是啥?效率低,刚才速度快的原因是因为样本数不多,如果样本数变成了10 万个再去看一看速度,它可能需要十几秒几十秒。那对于 LR来说照样速度会很快。
缺点二,你要做的是个非线性的映射,但不代表每次都能找到这样的一个映射。好的关系如果没有找到就得到不了很好的结果,所以非线性映射没有统一方案,可能很难找到合适的核函数。
我们有了四种 kernel,这四种 kernel都可以尝试。但这四种有可能都不属于最终的解。所以这个 kernel没有统一的方案。
另外我们选择 kernel还是有一点小的技巧的。比如说我们的样本数量比较小的情况下用简单的线性核,多的情况下就要用复杂的非线性核。
每种模型都有自己的适用场景,建议大家未来在工作过程中可以先用简单的模型跑一遍,比如说LR 模型。它作为我们预测模型的 baseline,baseline我们也把它称为叫做基线。基线就是速度快、简单、效果还可以。不能说好,它的目的不在于好而是在于快,可以拿到一个60 分的结果。有了 baseline 以后再去做复杂的模型,可以知道复杂模型到底好还是不好。
比如说 LR 刚才那个模型,97%这是个 baseline。用 SVM 得到98%就可以知道它比 baseline要高。如果你直接上了一个复杂模型,我们也无法对比。所以可以先用基线来做一个参考。
常见预测模型除了刚才说的分类模型以外其实还有树模型。树模型之后会详细给大家介绍,这个模型的模块是主要的内容,因为在未来的比赛过程中或项目过程中想要得到好的结果,还是要用到一些复杂的模型。
好,基本上,我们利用两个项目就基本介绍完了咱们最基本的 LR 和SVM。和之前讲解机器学习基础原理不同,我们现在主要是基于案例来看具体我们该怎么应用。
下一节课,咱们来看看几个机器学习神器。
链接: https://pan.baidu.com/s/1UgXmDZLOpVeXz21-Ebddog?pwd=5t4e提取码: 5t4e --来自百度网盘超级会员 v7 的分享