diff --git a/Part2/Chapter5/5.1 机器学习概述/1.jpg b/Part2/Chapter5/5.1 机器学习概述/1.jpg new file mode 100644 index 0000000..fffd94e Binary files /dev/null and b/Part2/Chapter5/5.1 机器学习概述/1.jpg differ diff --git a/Part2/Chapter5/5.1 机器学习概述/2.jpg b/Part2/Chapter5/5.1 机器学习概述/2.jpg new file mode 100644 index 0000000..a0ab31e Binary files /dev/null and b/Part2/Chapter5/5.1 机器学习概述/2.jpg differ diff --git a/Part2/Chapter5/5.1 机器学习概述/3.jpg b/Part2/Chapter5/5.1 机器学习概述/3.jpg new file mode 100644 index 0000000..670e7b8 Binary files /dev/null and b/Part2/Chapter5/5.1 机器学习概述/3.jpg differ diff --git a/Part2/Chapter5/5.1 机器学习概述/4.jpg b/Part2/Chapter5/5.1 机器学习概述/4.jpg new file mode 100644 index 0000000..253c721 Binary files /dev/null and b/Part2/Chapter5/5.1 机器学习概述/4.jpg differ diff --git a/Part2/Chapter5/5.1 机器学习概述/5.jpg b/Part2/Chapter5/5.1 机器学习概述/5.jpg new file mode 100644 index 0000000..8307eb1 Binary files /dev/null and b/Part2/Chapter5/5.1 机器学习概述/5.jpg differ diff --git a/Part2/Chapter5/5.1 机器学习概述/6.jpg b/Part2/Chapter5/5.1 机器学习概述/6.jpg new file mode 100644 index 0000000..824fec4 Binary files /dev/null and b/Part2/Chapter5/5.1 机器学习概述/6.jpg differ diff --git a/Part2/Chapter5/5.1 机器学习概述/7.jpg b/Part2/Chapter5/5.1 机器学习概述/7.jpg new file mode 100644 index 0000000..8c40e60 Binary files /dev/null and b/Part2/Chapter5/5.1 机器学习概述/7.jpg differ diff --git a/Part2/Chapter5/5.1 机器学习概述/什么是机器学习.md b/Part2/Chapter5/5.1 机器学习概述/什么是机器学习.md new file mode 100644 index 0000000..4095054 --- /dev/null +++ b/Part2/Chapter5/5.1 机器学习概述/什么是机器学习.md @@ -0,0 +1,10 @@ +# 5.1 什么是机器学习 + +机器学习的定义有很多种,但是最准确的定义是:"A computer program is said to learn from experience E with respect to some class of tasks T and performance measure P, if its performance at tasks in T, as measured by P, improves with experience E." 这个定义除了非常押韵之外,还体现了机器学习的几个关键点,即:task , experience 和 performance 。 + +task 即机器学习需要解决的问题,experience 即机器学习程序为了解决问题,从历史数据中总结出来的规律, performance 即机器学习程序总结出来的规律是好还是坏。 + +其实机器学习与人类学习是非常类似的。机器学习会从历史数据中不断地总结,归纳出规律得出数学模型,然后当有问题输入到机器学习程序时,机器学习程序能根据学到地数学模型进行预测,计算出预测结果。人类学习则是根据从小到大的耳濡目染和生活经验的积累,在大脑中形成了潜意识,这种潜意识其实就是我们人类的生活经验和规律。当我们碰到一些没见过的事物时,我们能够根据潜意识来对新事物有一个认知。 + +![](1.jpg) + diff --git a/Part2/Chapter5/5.1 机器学习概述/机器学习常见术语.md b/Part2/Chapter5/5.1 机器学习概述/机器学习常见术语.md new file mode 100644 index 0000000..6bd02f7 --- /dev/null +++ b/Part2/Chapter5/5.1 机器学习概述/机器学习常见术语.md @@ -0,0 +1,56 @@ +# 5.1.3 机器学习常见术语 + +## 模型 + +根据历史数据总结归纳出规律的过程,即学习过程,或模型的训练过程。模型这个词看上去 很高大上,其实可以把他看成是一个函数。例如:现在想用机器学习来识别图片里的是香蕉还是苹果,那 么机器学习所的事情就是得到一个比较好的函数,当输入一张香蕉图片时,能得到识别结果为香蕉的输 出,当输入一张苹果图片时,能得到识别结果为苹果的输出。 + +![](5.jpg) + +## 训练集,测试集,样本,特征 + +假设收集了一份西瓜数据: + +| 色泽 | 纹理 | 声音 | 甜不甜 | +| :-: | :-: | :-: | :-: | +| 青绿 | 清晰 | 清脆 | 不甜 | +| 青绿 | 模糊 | 浑浊 | 甜| +| 乌黑 | 清晰 | 清脆 | 不甜 | +| 乌黑 | 模糊 | 浑浊 | 甜 | + +并假设现在已经使用机器学习算法根据这份数据的特点训练出了一个很厉害的模型,成为了一个挑瓜好手,只需告诉它这个西瓜的色泽,纹理和声音就能告诉你这个西瓜甜不甜。 + +通常将这种喂给机器学习算法来训练模型的数据称为**训练集**,用来让机器学习算法预测的数据称为**测试集**。 + +训练集中的所有行称为**样本**。由于我们的挑瓜好手需要的西瓜信息是色泽、纹理和声音,所以此训练集中每个样本的前`3`列称为**特征**。挑瓜好手给出的结果是甜或不甜,所以最后`1`列称为**标签**。 + + +因此,这份数据是一个有`4`个**样本**,`3`个**特征**的**训练集**,训练集的**标签**是“甜不甜”。 + +## 欠拟合与过拟合 + +最好的情况下,模型应该不管在训练集上还是测试集上,它的性能都不错。但是有的时候,我模型在训练集上的性能比较差,那么这种情况我们称为**欠拟合**。那如果模型在训练集上的性能好到爆炸,但在测试集上的性能却不尽人意,那么这种情况我们称为**过拟合**。 + +其实欠拟合与过拟合的区别和生活中学生考试的例子很像。如果一个学生在平时的练习中题目的正确率都不高,那么说明这个学生可能基础不牢或者心思没花在学习上,所以这位学生可能欠缺基础知识或者智商可能不太高或者其他种种原因,像这种情况可以看成是**欠拟合**。那如果这位学生平时练习的正确率非常高,但是他不怎么灵光,喜欢死记硬背,只会做已经做过的题,一碰到没见过的新题就不知所措了。像这种情况可以看成是**过拟合**。 + +那么是什么原因导致了欠拟合和过拟合呢? + +当模型过于简单,很可能会导致欠拟合。如果模型过于复杂,就很可能会导致过拟合。 + +![](6.jpg) + +## 验证集与交叉验证 + +在真实业务中,我们可能没有真正意义上的测试集,或者说不知道测试集中的数据长什么样子。那么怎样在没有测试集的情况下来验证模型好还是不好呢?这个时候就需要**验证集**了。 + +那么验证集从何而来,很明显,可以从训练集中抽取一小部分的数据作为验证集,用来验证模型的性能。 + +但如果仅仅是从训练集中抽取一小部分作为验证集的话,有可能会让对模型的性能有一种偏见或者误解。 + +比如现在要对手写数字进行识别,那么我就可能会训练一个分类模型。但可能模型对于数字`1`的识别准确率比较低 ,而验证集中没多少个数字为`1`的样本,然后用验证集测试完后得到的准确率为`0.96`。然后您可能觉得哎呀,我的模型很厉害了,但其实并不然,因为这样的验证集让您的模型的性能有了误解。那有没有更加公正的验证算法性能的方法呢?有,那就是**k-折交叉验证**! + +在**K-折交叉验证**中,把原始训练数据集分割成`K`个不重合的⼦数据集,然后做`K`次模型训练和验证。每⼀次,使⽤⼀个⼦数据集验证模型,并使⽤其它`K−1`个⼦数据集来训练模型。在这`K`次训练和验证中,每次⽤来验证模型的⼦数据集都不同。最后,对这`K`次在验证集上的性能求平均。 + +`K`的值由我们自己来指定,如以下为`5`折交叉验证。 + +![](7.jpg) + diff --git a/Part2/Chapter5/5.1 机器学习概述/机器学习的主要任务.md b/Part2/Chapter5/5.1 机器学习概述/机器学习的主要任务.md new file mode 100644 index 0000000..d957317 --- /dev/null +++ b/Part2/Chapter5/5.1 机器学习概述/机器学习的主要任务.md @@ -0,0 +1,23 @@ +# 5.1.2 机器学习的主要任务 + +机器学习能够完成的任务主要有:分类、回归、聚类。 + +## 分类 + +假如现在有一些苹果、西瓜和香蕉的图片作为训练集(有标签),现在想要机器学片来分辨出该图片中的是苹果、西瓜还是香蕉。像这样的任务称为分类任务。 + +![](2.jpg) + +## 回归 + +假如现在有一些苹果的售价数据作为训练集(有标签),现在想要机器学习算法能够根据新的测试图片来分辨 出该图片中的苹果能卖多少钱。像这样的任务称为回归任务。 + +![](3.jpg) + +## 聚类 + +假如现在有一些水果的图片作为训练集(无标签),现在想要机器学习算法能够根据训练集中的图片将这些图 片进行归类,但是并不知道这些类别是什么。像这样的任务称为聚类任务。 + +![](4.jpg) + +细心的你可能注意到了,分类和回归问题的训练集中都是带有标签的。也就是说数据已经告诉了机器学习 算法我这条数据的答案是这个,那条数据的答案是那个,就像有老师在监督学生做题目一样,一看到学生 做错了就告诉他题目做错了,看到学生做对了就鼓励他。所以用来解决分类和回归问题的机器学习算法又 称为监督学习。而像用来解决聚类问题的机器学习算法又称为无监督学习。 \ No newline at end of file diff --git a/Part2/Chapter5/5.2 监督学习/1.jpg b/Part2/Chapter5/5.2 监督学习/1.jpg new file mode 100644 index 0000000..257e27f Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/1.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/10.jpg b/Part2/Chapter5/5.2 监督学习/10.jpg new file mode 100644 index 0000000..9928b10 Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/10.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/11.jpg b/Part2/Chapter5/5.2 监督学习/11.jpg new file mode 100644 index 0000000..eb164ae Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/11.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/12.jpg b/Part2/Chapter5/5.2 监督学习/12.jpg new file mode 100644 index 0000000..d006391 Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/12.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/13.jpg b/Part2/Chapter5/5.2 监督学习/13.jpg new file mode 100644 index 0000000..c20121e Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/13.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/14.jpg b/Part2/Chapter5/5.2 监督学习/14.jpg new file mode 100644 index 0000000..5bca5bc Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/14.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/15.jpg b/Part2/Chapter5/5.2 监督学习/15.jpg new file mode 100644 index 0000000..72b074a Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/15.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/16.jpg b/Part2/Chapter5/5.2 监督学习/16.jpg new file mode 100644 index 0000000..c688332 Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/16.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/17.jpg b/Part2/Chapter5/5.2 监督学习/17.jpg new file mode 100644 index 0000000..99255a9 Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/17.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/18.jpg b/Part2/Chapter5/5.2 监督学习/18.jpg new file mode 100644 index 0000000..28bf8a2 Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/18.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/19.jpg b/Part2/Chapter5/5.2 监督学习/19.jpg new file mode 100644 index 0000000..6496742 Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/19.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/2.jpg b/Part2/Chapter5/5.2 监督学习/2.jpg new file mode 100644 index 0000000..8e3a2a8 Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/2.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/20.jpg b/Part2/Chapter5/5.2 监督学习/20.jpg new file mode 100644 index 0000000..e1ea7e2 Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/20.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/21.jpg b/Part2/Chapter5/5.2 监督学习/21.jpg new file mode 100644 index 0000000..9bfb56b Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/21.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/22.jpg b/Part2/Chapter5/5.2 监督学习/22.jpg new file mode 100644 index 0000000..2813879 Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/22.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/23.jpg b/Part2/Chapter5/5.2 监督学习/23.jpg new file mode 100644 index 0000000..5181953 Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/23.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/24.jpg b/Part2/Chapter5/5.2 监督学习/24.jpg new file mode 100644 index 0000000..b4a061b Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/24.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/25.jpg b/Part2/Chapter5/5.2 监督学习/25.jpg new file mode 100644 index 0000000..ab377dd Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/25.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/26.jpg b/Part2/Chapter5/5.2 监督学习/26.jpg new file mode 100644 index 0000000..b4676b6 Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/26.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/27.jpg b/Part2/Chapter5/5.2 监督学习/27.jpg new file mode 100644 index 0000000..64cc392 Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/27.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/28.jpg b/Part2/Chapter5/5.2 监督学习/28.jpg new file mode 100644 index 0000000..fbcf29b Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/28.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/29.jpg b/Part2/Chapter5/5.2 监督学习/29.jpg new file mode 100644 index 0000000..2bd3c15 Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/29.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/3.jpg b/Part2/Chapter5/5.2 监督学习/3.jpg new file mode 100644 index 0000000..9a7abed Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/3.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/30.jpg b/Part2/Chapter5/5.2 监督学习/30.jpg new file mode 100644 index 0000000..991cec9 Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/30.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/4.jpg b/Part2/Chapter5/5.2 监督学习/4.jpg new file mode 100644 index 0000000..24f651e Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/4.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/5.jpg b/Part2/Chapter5/5.2 监督学习/5.jpg new file mode 100644 index 0000000..55e1d32 Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/5.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/6.jpg b/Part2/Chapter5/5.2 监督学习/6.jpg new file mode 100644 index 0000000..af40e29 Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/6.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/7.jpg b/Part2/Chapter5/5.2 监督学习/7.jpg new file mode 100644 index 0000000..d4e400f Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/7.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/8.jpg b/Part2/Chapter5/5.2 监督学习/8.jpg new file mode 100644 index 0000000..fc92222 Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/8.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/9.jpg b/Part2/Chapter5/5.2 监督学习/9.jpg new file mode 100644 index 0000000..8552eed Binary files /dev/null and b/Part2/Chapter5/5.2 监督学习/9.jpg differ diff --git a/Part2/Chapter5/5.2 监督学习/keepfile.txt b/Part2/Chapter5/5.2 监督学习/keepfile.txt deleted file mode 100644 index e69de29..0000000 diff --git a/Part2/Chapter5/5.2 监督学习/别被我的名字蒙蔽了---逻辑回归.md b/Part2/Chapter5/5.2 监督学习/别被我的名字蒙蔽了---逻辑回归.md new file mode 100644 index 0000000..a225ce4 --- /dev/null +++ b/Part2/Chapter5/5.2 监督学习/别被我的名字蒙蔽了---逻辑回归.md @@ -0,0 +1,110 @@ +# 5.2.3 别被我的名字蒙蔽了---逻辑回归 + +逻辑回归是属于机器学习里面的监督学习,它是以回归的思想来解决分类问题的一种非常经典的二分类分类器。由于其训练后的参数有较强的可解释性,在诸多领域中,逻辑回归通常用作`baseline`模型,以方便后期更好的挖掘业务相关信息或提升模型性能。 + +## 逻辑回归大体思想 + +当一看到“**回归**”这两个字,可能会认为逻辑回归是一种解决回归问题的算法,然而逻辑回归是通过回归的思想来解决**二分类**问题的算法。 + +那么问题来了,回归的算法怎样解决分类问题呢?其实很简单,逻辑回归是将样本特征和样本所属类别的概率联系在一起,假设现在已经训练好了一个逻辑回归的模型为$$f(x)$$,模型的输出是样本$$x$$的标签是$$1$$的概率,则该模型可以表示成$$\hat p=f(x)$$。若得到了样本$$x$$属于标签$$1$$的概率后,很自然的就能想到当$$\hat p>0.5$$时$$x$$属于标签$$1$$,否则属于标签 $$0$$ 。所以就有: + +
+$$ +\hat y=\begin{cases} +0 & \hat p <0.5 \\ +1 & \hat p >0.5 +\end{cases} +$$ +
+ +(其中$$\hat y$$为样本 $$x$$ 根据模型预测出的标签结果,标签 $$0$$ 和标签 $$1$$ 所代表的含义是根据业务决定的,比如在癌细胞识别中可以使 $$0$$ 代表良性肿瘤,$$1$$ 代表恶性肿瘤)。 + +**由于概率是 $$0$$ 到 $$1$$ 的实数,所以逻辑回归若只需要计算出样本所属标签的概率就是一种回归算法,若需要计算出样本所属标签,则就是一种二分类算法。** + +那么逻辑回归中样本所属标签的概率怎样计算呢?其实和线性回归有关系,学习了线性回归的话肯定知道线性回归就是训练出一组参数 $$W$$ 和 $$b$$ 来拟合样本数据,线性回归的输出为$$\hat y=Wx+b$$ 。不过$$\hat y$$的值域是$$(-\infty,+\infty)$$,如果能够将值域为$$(-\infty,+\infty)$$ 的实数转换成 $$(0,1)$$ 的概率值的话问题就解决了。**要解决这个问题很自然地就能想到将线性回归的输出作为输入,输入到另一个函数中,这个函数能够进行转换工作,假设函数为 $$\sigma$$ ,转换后的概率为 $$\hat p$$ ,则逻辑回归在预测时可以看成 $$\hat p=\sigma (Wx+b)$$**。 $$\sigma$$ 其实就是接下来要介绍的$$sigmoid$$函数。 + + +## sigmoid函数 + +$$sigmoid$$函数的公式为:$$\sigma(t)=1/1+e^{-t}$$ + +函数图像如下图所示: + +![](12.jpg) + +从$$sigmoid$$函数的图像可以看出当$$t$$趋近于$$-\infty$$时函数值趋近于$$0$$,当$$t$$趋近于$$+\infty$$时函数值趋近于$$1$$。可见$$sigmoid$$函数的值域是$$(0,1)$$,满足我们要将$$(-\infty,+\infty)$$的实数转换成$$(0,1)$$的概率值的需求。因此**逻辑回归**在预测时可以看成$$\hat p=1/(1+e^{-Wx+b})$$,如果$$\hat p>0.5$$时预测为一种类别,否则预测为另一种类别。 + +## 逻辑回归的损失函数 + +在预测样本属于哪个类别时取决于算出来的$$\hat p$$。从另外一个角度来说,假设现在有一个样本的真实类别为 $$1$$ ,模型预测样本为类别 $$1$$ 的概率为 $$0.9$$ 的话,就意味着这个模型认为当前样本的类别有 $$90\%$$ 的可能性为 $$1$$ ,有 $$10\%$$ 的可能性为 $$0$$ 。所以从这个角度来看,逻辑回归的损失函数与 $$\hat p$$ 有关。 + +当然逻辑回归的损失函数不仅仅与 $$\hat p$$ 有关,它还与真实类别有关。假设现在有两种情况,**情况 A** :现在有个样本的真实类别是 $$0$$ ,但是模型预测出来该样本是类别 $$1$$ 的概率是 $$0.7$$(也就是说类别 $$0$$ 的概率为 $$0.3$$ );**情况 B** :现在有个样本的真实类别是 $$0$$ ,但是模型预测出来该样本是类别 $$1$$ 的概率是 $$0.6$$(也就是说类别 $$0$$ 的概率为 $$0.4$$ );请你思考 $$2$$ 秒钟,**AB** 两种情况哪种情况的误差更大?很显然,**情况 A** 的误差更大!因为**情况 A** 中模型认为样本是类别 $$0$$ 的可能性只有 $$30\%$$,而 **情况 B** 有 $$40\%$$。 + +假设现在又有两种情况,**情况A:** 现在有个样本的真实类别是 $$0$$ ,但是模型预测出来该样本是类别 $$1$$ 的概率是 $$0.7$$(也就是说类别 $$0$$ 的概率为 $$0.3$$);**情况B:**现在有个样本的真实类别是 $$1$$,但是模型预测出来该样本是类别 $$1$$ 的概率是 $$0.3$$(也就是说类别 $$0$$ 的概率为 $$0.7$$ );请你再思考 $$2$$ 秒钟,**AB**两种情况哪种情况的误差更大?很显然,**一样大!** + +所以逻辑回归的损失函数如下,其中 $$cost$$ 表示损失函数的值,$$y$$ 表示样本的真实类别: + +$$ +cost=-ylog(\hat p)-(1-y)log(1-\hat p) +$$ + +知道了逻辑回归的损失函数之后,逻辑回归的**训练流程**就很明显了,就是寻找一组合适的 $$W$$ 和 $$b$$ ,使得损失值最小。找到这组参数后模型就确定下来了。怎么找?很明显,用**梯度下降**,而且不难算出梯度为:$$(\hat y - y)x$$。 + + +所以逻辑回归梯度下降的代码如下: + +```python +#loss +def J(theta, X_b, y): + y_hat = self._sigmoid(X_b.dot(theta)) + try: + return -np.sum(y*np.log(y_hat)+(1-y)*np.log(1-y_hat)) / len(y) + except: + return float('inf') + +# 算theta对loss的偏导 +def dJ(theta, X_b, y): + return X_b.T.dot(self._sigmoid(X_b.dot(theta)) - y) / len(y) + +# 批量梯度下降 +def gradient_descent(X_b, y, initial_theta, leraning_rate, n_iters=1e4, epsilon=1e-8): + theta = initial_theta + cur_iter = 0 + while cur_iter < n_iters: + gradient = dJ(theta, X_b, y) + last_theta = theta + theta = theta - leraning_rate * gradient + if (abs(J(theta, X_b, y) - J(last_theta, X_b, y)) < epsilon): + break + cur_iter += 1 + return theta + +``` + +## sklearn中的逻辑回归 + +`sklearn`中的`LogisticRegression`默认实现了`OVR`,因此`LogisticRegression`可以实现多分类。`LogisticRegression`的构造函数中有三个常用的参数可以设置: + +- `solver`:`{'newton-cg' , 'lbfgs', 'liblinear', 'sag', 'saga'}`, 分别为几种优化算法。默认为`liblinear`。 +- `C`:正则化系数的倒数,默认为`1.0`,越小代表正则化越强。 +- `max_iter`:最大训练轮数,默认为`100`。 + +和 `sklearn` 中其他分类器一样,`LogisticRegression`类中的`fit`函数用于训练模型,`fit`函数有两个向量输入: + +- `X`:大小为 [样本数量,特征数量] 的`ndarray`,存放训练样本 +- `Y`:值为整型,大小为 [样本数量] 的`ndarray`,存放训练样本的分类标签 + +`LogisticRegression`类中的`predict`函数用于预测,返回预测标签,`predict`函数有一个向量输入: + +- `X`:大小为[样本数量,特征数量]的`ndarray`,存放预测样本 + +`LogisticRegression`的使用代码如下: + +```python +logreg = LogisticRegression(solver='lbfgs',max_iter =10,C=10) +logreg.fit(X_train, Y_train) +result = logreg.predict(X_test) +``` + + +如果想动手实现逻辑回归算法,并掌握如何使用 sklearn 来解决实际问题,可以尝试进入链接进行实战:https://www.educoder.net/shixuns/tw9up75v/challenges \ No newline at end of file diff --git a/Part2/Chapter5/5.2 监督学习/好还不够,我要最好---支持向量机.md b/Part2/Chapter5/5.2 监督学习/好还不够,我要最好---支持向量机.md new file mode 100644 index 0000000..12e27e1 --- /dev/null +++ b/Part2/Chapter5/5.2 监督学习/好还不够,我要最好---支持向量机.md @@ -0,0 +1,286 @@ +# 5.2.6 好还不够,我要最好---支持向量机 + +## 线性可分支持向量机 + +例如逻辑回归这种广义线性模型对数据进行分类时,其实本质就是找到一条 **决策边界** ,然后将数据分成两个类别(边界的一边是一种类别,另一边是另一种类别)。如下图中的两条线是两种模型产生的 **决策边界** : + +![](20.jpg) + +图中的绿线与黄线都能很好的将图中的红点与蓝点给区分开。但是,哪条线的泛化性更好呢?可能你不太了解泛化性,也就是说,这条直线,不仅需要在训练集 **(已知的数据)** 上能够很好的将红点跟蓝点区分开来,还要在测试集 **(未知的数据)** 上将红点和蓝点给区分开来。 + + +假如经过训练,我们得到了黄色的这条决策边界用来区分数据,这个时候又来了一个数据,即黑色的点,那么你觉得黑色的点是属于红的这一类,还是蓝色的这一类呢? + +![](21.jpg) + +如上图,根据黄线的划分标准,黑色的点应该属于红色这一类。可是,肉眼很容易发现,黑点离蓝色的点更近,它应该是属于蓝色的点。这就说明,黄色的这条直线它的泛化性并不好,它对于未知的数据并不能很好的进行分类。那么,如何得到一条泛化性好的直线呢?这个就是支持向量机考虑的问题。 + +### 基本思想 + +支持向量机的思想认为,一条决策边界它如果要有很好的泛化性,它需要满足一下以下两个条件: + +- 能够很好的将样本划分 +- 离最近的样本点最远 + +比如下图中的黑线: + +![](22.jpg) + +它能够正确的将红点跟蓝点区分开来,而且,它还保证了对未知样本的容错率,因为它离最近的红点跟蓝点都很远,这个时候,再来一个数据,就不会出现之前黄色决策边界的错误了。 + +![](23.jpg) + +无论新的数据出现在哪个位置,黑色的决策边界都能够很好的给它进行分类,这个就是支持向量机的基本思想。 + +### 间隔与支持向量 + +在样本空间中,决策边界可以通过如下线性方程来描述: + +
+$$ +w^Tx+b=0 +$$ +
+
+ +其中$$w=(w_1,w_2,..,w_d)$$为法向量,决定了决策边界的方向。$$b$$为位移项,决定了决策边界与原点之间的距离。显然,决策边界可被法向量和位移确定,可以将其表示为$$(w,b)$$。样本空间中的任意一个点$$x$$,到决策边界$$(w,b)$$的距离可写为: + +
+$$ +r=\frac{|w^Tx+b|}{||w||} +$$ +
+
+ +假设决策边界$$(w,b)$$能够将训练样本正确分类,即对于任何一个样本点$$(x_i,y_i)$$,若它为正类,即$$y_i=+1$$时,$$w^Tx+b\geq +1$$。若它为负类,即$$y_i=-1$$时,$$w^Tx+b\leq -1$$。 + +![](24.jpg) + +如图中,距离最近的几个点使两个不等式的等号成立,它们就被称为支持向量,即图中两条黄色的线。两个异类支持向量到超平面的距离之和为: + +
+$$ +r=\frac{2}{||w||} +$$ +
+
+ +它被称为间隔,即蓝线的长度。欲找到具有“最大间隔”的决策边界,即黑色的线,也就是要找到能够同时满足如下式子的$$w$$与$$b$$: + +![](25.jpg) + +显然,为了最大化间隔,仅需最大化$$||w||^{-1}$$,这等价于最小化$$||w||^2$$,于是,条件可以重写为: + +
+$$ +min \frac{1}{2}||w||^2 +$$ +
+ +
+$$ +s.t. y_i(w^Tx+b)\geq1 +$$ +
+ +这就是线性可分支持向量机的基本型。 + +### 对偶问题 + +现在已经知道了支持向量机的基本型,问题本身是一个凸二次规划问题,可以用现成的优化计算包求解,不过可以用更高效的方法。支持向量机的模型如下: + +
+$$ +min\frac{1}{2}||W||^2 +$$ +
+ +
+$$ +s.t. y_i(w^Tx+b)\geq1 +$$ +
+
+ +对两式子使用拉格朗日乘子法可得到其对偶问题。具体为,对式子的每条约束添加拉格朗日乘子$$\alpha\geq0$$,则该问题的拉格朗日函数可写为: + +
+$$ +L(w,b,\alpha)=\frac{1}{2}||w||^2+\sum^m_{i=1}\alpha_i(1-y_i(w^Tx_i+b))......(1) +$$ +
+
+ +其中$$\alpha=(\alpha_1,\alpha_2,..,\alpha_m)$$。令$$L(w,b,\alpha)$$对$$w$$和$$b$$的偏导为零可得: + +
+$$ +w=\sum_{i=1}^m\alpha_iy_ix_i......(2) +$$ +
+ +
+$$ +0=\sum_{i=1}^m\alpha_iy_i......(3) +$$ +
+
+ +将式子$$2$$带入式子$$1$$,则可将$$w$$和$$b$$消去,再考虑式子$$3$$的约束,就可得到原问题的对偶问题: + +
+$$ +max(\sum^m_{i=1}\alpha_i-\frac{1}{2}\sum_{i=1}^m\sum_{j=1}^m\alpha_i\alpha_jy_iy_jx_i^Tx_j) +$$ +
+ +
+$$ +s.t. \sum_{i=1}^m\alpha_iy_i=0 +$$ +
+ +
+$$ +\alpha_i\geq0 +$$ +
+
+ +解出$$\alpha$$后,求出$$w$$与$$b$$即可得到模型: + +
+$$ +f(x)=w^Tx+b=\sum_{i=1}^m\alpha_iy_ix_i^Tx+b......(4) +$$ +
+
+ +从对偶问题解出的$$\alpha_i$$是拉格朗日乘子,它恰对应着训练样本$$(x_i,y_i)$$,由于原问题有不等式的约束,因此上述过程需满足KKT(Karush-Kuhn-Tucker)条件,即要求: + +
+$$ +\begin{cases} +\alpha_i\geq0\\ +y_if(x_i)-1\geq0\\ +\alpha_i(y_if(x_i)-1)=0 +\end{cases} +$$ +
+ +于是,对任意训练样本$$(x_i,y_i)$$,总有$$\alpha_i=0$$或$$y_if(x_i)=1$$。若$$\alpha_i=0$$,则该样本将不会出现在式子$$4$$中,也就不会对$$f(x)$$,有任何影响。若$$\alpha_i>0$$,则必有$$y_if(x_i)=1$$,所对应的样本点位于最大间隔边界上,是一个支持向量。这显示出支持向量机的一个重要性质: 训练完后,大部分的训练样本都不需要保留,最终模型仅与支持向量有关。 + +## 线性支持向量机 + +假如现在有一份数据分布如下图: + +![](26.jpg) + +按照线性可分支持向量机的思想,黄色的线就是最佳的决策边界。很明显,这条线的泛化性不是很好,造成这样结果的原因就是数据中存在着异常点,那么如何解决这个问题呢,支持向量机引入了软间隔最大化的方法来解决。 + +所谓的软间隔,是相对于硬间隔说的,刚刚在间隔与支持向量中提到的间隔就是硬间隔。 + +接着我们再看如何可以软间隔最大化呢?SVM 对训练集里面的每个样本$$(x_i,y_i)$$引入了一个松弛变量$$\xi_i\geq0$$,使函数间隔加上松弛变量大于等于$$1$$,也就是说: + +
+$$ +y_i(w^Tx+b)\geq1-\xi +$$ +
+
+ +对比硬间隔最大化,可以看到我们对样本到超平面的函数距离的要求放松了,之前是一定要大于等于$$1$$,现在只需要加上一个大于等于$$0$$的松弛变量能大于等于$$1$$就可以了。也就是允许支持向量机在一些样本上出错,如下图: + +![](27.jpg) + +当然,松弛变量不能白加,这是有成本的,每一个松弛变量$$\xi_i$$, 对应了一个代价$$\xi_i$$,这个就得到了我们的软间隔最大化的支持向量机,模型如下: + +
+$$ +min \frac{1}{2}||w||^2+C\sum^{m}_{i=1}\xi_i +$$ +
+ +
+$$ +s.t. y_i(w^Tx_i+b)\geq1-\xi_i +$$ +
+ +
+$$ +\xi_i\geq0 +$$ +
+
+ +这里,$$C>0$$为惩罚参数,可以理解为一般回归和分类问题正则化时候的参数。$$C$$越大,对误分类的惩罚越大,$$C$$越小,对误分类的惩罚越小。也就是说,希望权值的二范数尽量小,误分类的点尽可能的少。$$C$$是协调两者关系的正则化惩罚系数。在实际应用中,需要调参来选择。 + +同样的,使用拉格朗日函数将软间隔最大化的约束问题转化为无约束问题,利用相同的方法得到数学模型: + +
+$$ +max(\sum^m_{i=1}\alpha_i-\frac{1}{2}\sum_{i=1}^m\sum_{j=1}^m\alpha_i\alpha_jy_iy_jx_i^Tx_j) +$$ +
+ +
+$$ +s.t. \sum_{i=1}^m\alpha_iy_i=0 +$$ +
+ +
+$$ +0\leq\alpha_i\leq C +$$ +
+
+ +这就是软间隔最大化时的线性支持向量机的优化目标形式,和之前的硬间隔最大化的线性可分支持向量机相比,我们仅仅是多了一个约束条件: + +
+$$ +0\leq\alpha_i\leq C +$$ +
+ +## sklearn中的支持向量机 + +`SVR`是`sklearn`中提供的支持向量机回归器,`SVR`的构造函数中有四个常用的参数可以设置: + + kernel:采用核函数类型,默认为`rbf`。可选参数有:`linear`(线性核函数),`poly`(多项式核函数),`rbf`:(径像核函数/高斯核)。 + C:错误项的惩罚参数C,默认为1.0。 + gamma:核函数系数,只对`rbf`,`poly`有效。如果gamma为auto,代表其值为样本特征数的倒数,即1/n_features。 + epsilon:误差精度。 + + +`SVR`类中的`fit`函数用于训练模型,`fit`函数有两个向量输入: + +- `X`:大小为**[样本数量,特征数量]**的`ndarray`,存放训练样本 +- `Y`:值为整型,大小为**[样本数量]**的`ndarray`,存放训练样本的标签值 + +`SVR`类中的`predict`函数用于预测,返回预测值,`predict`函数有一个向量输入: + +- `X`:大小为**[样本数量,特征数量]**的`ndarray`,存放预测样本 + +`SVR`的使用代码如下: + +```python +from sklearn.svm import SVR +svr = SVR() +svr.fit(X_train, Y_train) +predict = lr.predict(X_test) +``` + +`SVC`是`sklearn`中提供的支持向量机分类器,用法和`SVR`一致,`SVC`的使用代码如下: + +```python +from sklearn.svm import SVC +svr = SVC() +svr.fit(X_train, Y_train) +predict = lr.predict(X_test) +``` + + +如果像动手实现支持向量机算法,并掌握如何使用 sklearn 来解决实际问题,可以尝试进入链接进行实战:https://www.educoder.net/shixuns/b6yi97f2/challenges diff --git a/Part2/Chapter5/5.2 监督学习/最接近人类思维的算法---决策树.md b/Part2/Chapter5/5.2 监督学习/最接近人类思维的算法---决策树.md new file mode 100644 index 0000000..24ac4f6 --- /dev/null +++ b/Part2/Chapter5/5.2 监督学习/最接近人类思维的算法---决策树.md @@ -0,0 +1,123 @@ +# 5.2.5 最接近人类思维的算法---决策树 + +## 什么是决策树 + +决策树说白了就是一棵能够替我们做决策的树,或者说是我们人的脑回路的一种表现形式。比如我看到一个人,然后我会思考这个男人有没有买车。那我的脑回路可能是这样的: + +![](14.jpg) + +其实这样一种脑回路的形式就是决策树。所以从图中能看出决策树是一个类似于人们决策过程的树结构,从根节点开始,每个分枝代表一个新的决策事件,会生成两个或多个分枝,每个叶子代表一个最终判定所属的类别。很明显,如果我现在已经构造好了一颗决策树的话,现在我得到一条数据`(男, 29)`,我就会认为这个人没有买过车。所以呢,关键问题就是怎样来构造决策树了。 + + +构造决策树时会遵循一个指标,有的是按照**信息增益**来构建,这种叫**ID3算法**,有的是**信息增益比**来构建,这种叫**C4.5**算法,有的是按照**基尼系数**来构建的,这种叫**CART**算法。在这里主要介绍一下**ID3算法**。 + +## ID3算法 + +整个**ID3算法**其实主要就是围绕着**信息增益**来的,所以要弄清楚**ID3算法**的流程,首先要弄清楚什么是**信息增益**,但要弄清楚信息增益之前有个概念必须要懂,就是**熵**。所以先看看什么是熵。 + +### 熵、条件熵、信息增益 + +在信息论和概率统计中呢,为了表示某个随机变量的不确定性,就借用了热力学的一个概念叫**熵**。如果假设 $$X$$ 是一个有限个取值的离散型随机变量的话,很显然它的概率分布或者分布律就是这样的: + +
+$$P(X=x_i)=p_i, i=1,2,...,n$$ +
+
+ +有了概率分布后,则这个随机变量 $$X$$ 的熵的计算公式就是($$PS$$:这里的 $$log$$ 是以 $$2$$ 为底):$$H(X)=-\sum_{i=1}^np_ilogp_i$$ + +从这个公式也可以看出,如果我概率是 $$0$$ 或者是 $$1$$ 的时候,我的熵就是 $$0$$ 。(因为这种情况下我随机变量的不确定性是最低的),那如果我的概率是 $$0.5$$ 也就是五五开的时候,我的熵是最大也就是 $$1$$ 。(就像扔硬币,你永远都猜不透你下次扔到的是正面还是反面,所以它的不确定性非常高)。所以呢,**熵越大,不确定性就越高**。 + +在实际情况下,要研究的随机变量基本上都是多随机变量的情况,所以假设有随便量`(X,Y)`,那么它的联合概率分布是这样的: + +
+$$ +P(X=x_i, Y=y_j)=p_{ij}, i=1,2,...,n; j=1,2,...,m +$$ +
+ + +那如果我想知道在我事件 $$X$$ 发生的前提下,事件 $$Y$$ 发生的熵是多少的话,这种熵叫它**条件熵**。条件熵 $$H(Y|X)$$ 表示随机变量 $$X$$ 的条件下随机变量 $$Y$$ 的不确定性。条件熵的计算公式是这样的:$$H(Y|X)=\sum^n_{i=1}p_iH(Y|X=x_i)$$。 + +**当然条件熵的一个性质也熵的性质一样,我概率越确定,条件熵就越小,概率越五五开,条件熵就越大**。 + +现在已经知道了什么是熵,什么是条件熵。接下来就可以看看什么是信息增益了。所谓的信息增益就是表示我已知条件 $$X$$ 后能得到信息 $$Y$$ 的不确定性的减少程度。就好比,我在玩读心术。您心里想一件东西,我来猜。我一开始什么都没问你,我要猜的话,肯定是瞎猜。这个时候我的熵就非常高对不对。然后我接下来我会去试着问你是非题,当我问了是非题之后,我就能减小猜测你心中想到的东西的范围,这样其实就是减小了我的熵。那么我熵的减小程度就是我的**信息增益**。 + +所以信息增益如果套上机器学习的话就是,如果把特征 $$A$$ 对训练集 $$D$$ 的信息增益记为 $$g(D, A)$$ 的话,那么 $$g(D, A)$$ 的计算公式就是:$$g(D,A)=H(D)-H(D|A)$$。 + +如果看到这一堆公式可能会比较烦,那不如举个栗子来看看信息增益怎么算。假设我现在有这一个数据表,第一列是性别,第二列是活跃度, 第三列是客户是否流失的 $$label$$。 + +![](15.jpg) + +那如果我要算性别和活跃度这两个特征的信息增益的话,首先要先算总的熵和条件熵。( $$5/15$$ 的意思是总共有 $$15$$ 条样本里面 $$label$$ 为 $$1$$ 的样本有 $$5$$ 条,$$3/8$$ 的意思是性别为男的样本有 $$8$$ 条,然后这 $$8$$ 条里有 $$3$$ 条是 $$label$$ 为 $$1$$,其他的数值以此类推) + +`总熵= (-5/15)*log(5/15)-(10/15)*log(10/15)=0.9182` + +`性别为男的熵= -(3/8)*log(3/8)-(5/8)*log(5/8)=0.9543` + +`性别为女的熵= -(2/7)*log(2/7)-(5/7)*log(5/7)=0.8631` + +`活跃度为低的熵= -(4/4)*log(4/4)-0=0` + +`活跃度为中的熵= -(1/5)*log(1/5)-(4/5)*log(4/5)=0.7219` + +`活跃度为高的熵= -0-(6/6)*log(6/6)=0` + +现在有了总的熵和条件熵之后就能算出性别和活跃度这两个特征的信息增益了。 + +`性别的信息增益=总的熵-(8/15)*性别为男的熵-(7/15)*性别为女的熵=0.0064` + +`活跃度的信息增益=总的熵-(6/15)*活跃度为高的熵-(5/15)*活跃度为中的熵-(4/15)*活跃度为低的熵=0.6776` + +那信息增益算出来之后有什么意义呢?回到读心术的问题,为了我能更加准确的猜出你心中所想,我肯定是问的问题越好就能猜得越准!换句话来说我肯定是要想出一个信息增益最大的问题来问你,对不对?其实**ID3算法**也是这么想的。**ID3算法**的思想是从训练集 $$D$$ 中计算每个特征的信息增益,然后看哪个最大就选哪个作为当前节点。然后继续重复刚刚的步骤来构建决策树。 + +### 决策树构流程 + +**ID3算法**其实就是依据特征的信息增益来构建树的。具体套路就是从根节点开始,对节点计算所有可能的特征的信息增益,然后选择信息增益最大的特征作为节点的特征,由该特征的不同取值建立子节点,然后对子节点递归执行上面的套路直到信息增益很小或者没有特征可以继续选择为止。 + +这样看上去可能会懵,不如用刚刚的数据来构建一颗决策树。 + +一开始已经算过信息增益最大的是活跃度,所以决策树的根节点是活跃度 。所以这个时候树是这样的: + +![](16.jpg) + +然后发现训练集中的数据表示当我活跃度低的时候一定会流失,活跃度高的时候一定不流失,所以可以先 在根节点上接上两个叶子节点。 + +![](17.jpg) + +但是活跃度为中的时候就不一定流失了,所以这个时候就可以把活跃度为低和为高的数据屏蔽掉,屏蔽掉 之后 5 条数据,接着把这 5 条数据当成训练集来继续算哪个特征的信息增益最高,很明显算完之后是性别 这个特征,所以这时候树变成了这样: + +![](18.jpg) + +这时候呢,数据集里没有其他特征可以选择了(总共就两个特征,活跃度已经是根节点了),所以就看我 性别是男或女的时候那种情况最有可能出现了。此时性别为男的用户中有 1 个是流失,1 个是不流失,五五开。所以可以考虑随机选个结果当输出了。性别为女的用户中有全部都流失,所以性别为女时输出是流失。所以呢,树就成了这样: + +![](19.jpg) + +好了,决策树构造好了。从图可以看出决策树有一个非常好的地方就是模型的解释性非常强!!很明显, 如果现在来了一条数据 (男, 高) 的话,输出会是不流失。 + + +## sklearn中的决策树 + +`DecisionTreeClassifier`的构造函数中有两个常用的参数可以设置: +- `criterion`:划分节点时用到的指标。有`gini`(**基尼系数**),`entropy`(**信息增益**)。若不设置,默认为`gini` +- `max_depth`:决策树的最大深度,如果发现模型已经出现过拟合,可以尝试将该参数调小。若不设置,默认为`None` + + +和`sklearn`中其他分类器一样,`DecisionTreeClassifier`类中的`fit`函数用于训练模型,`fit`函数有两个向量输入: +- `X`:大小为`[样本数量,特征数量]`的`ndarray`,存放训练样本 +- `Y`:值为整型,大小为`[样本数量]`的`ndarray`,存放训练样本的分类标签 + +`DecisionTreeClassifier`类中的`predict`函数用于预测,返回预测标签,`predict`函数有一个向量输入: +- `X`:大小为`[样本数量,特征数量]`的`ndarray`,存放预测样本 + +`DecisionTreeClassifier`的使用代码如下: +```python +from sklearn.tree import DecisionTreeClassifier + +clf = tree.DecisionTreeClassifier() +clf.fit(X_train, Y_train) +result = clf.predict(X_test) +``` + + +如果像动手实现决策树算法,并掌握如何使用 sklearn 来解决实际问题,可以尝试进入链接进行实战:https://www.educoder.net/shixuns/hl7wacq5/challenges \ No newline at end of file diff --git a/Part2/Chapter5/5.2 监督学习/最简单的回归算法---线性回归.md b/Part2/Chapter5/5.2 监督学习/最简单的回归算法---线性回归.md new file mode 100644 index 0000000..db51c58 --- /dev/null +++ b/Part2/Chapter5/5.2 监督学习/最简单的回归算法---线性回归.md @@ -0,0 +1,127 @@ +# 5.2.2 最简单的回归算法---线性回归 + +## 什么是线性回归 + +线性回归是什么意思?可以拆字释义。回归肯定不用我多说了,那什么是线性呢?我们可以回忆一下初中 时学过的直线方程:$$y = k ∗ x + b$$ + +这个式子表达的是,当我知道 k (参数)和 b (参数)的情况下,我随便给一个 x 我都能通过这个方程 算出 y 来。而且呢,这个式子是线性的,为什么呢?因为从直觉上来说,你都知道,这个式子的函数图像 是条直线。 + +从理论上来说,这式子满足线性系统的性质(至于线性系统是什么,可以查阅相关资料,这里就不多做赘述 了,不然没完没了)。你可能会觉得疑惑,这一节要说的是线性回归,我说个这么 low 直线方程干啥?其 实,说白了,线性回归就是在 N 维空间中找一个形式像直线方程一样的函数来拟合数据而已。比如说,我 现在有这么一张图,横坐标代表房子的面积,纵坐标代表房价。 + +![](3.jpg) + +然后呢,线性回归就是要找一条直线,并且让这条直线尽可能地拟合图中的数据点。 + +那如果让 1000 位朋友来找这条直线就可能找出 1000 种直线来,比如这样 + +![](4.jpg) + +这样 + +![](5.jpg) + +或者这样 + +![](6.jpg) + +喏,其实找直线的过程就是在做线性回归,只不过这个叫法更有高大上而已。 + +## 损失函数 + +那既然是找直线,那肯定是要有一个评判的标准,来评判哪条直线才是最好的。道理我们都懂,那咋评判 呢?其实只要算一下实际房价和我找出的直线根据房子大小预测出来的房价之间的差距就行了。说白了就 是算两点的距离。当把所有实际房价和预测出来的房价的差距(距离)算出来然后做个加和,就能量化出 现在预测的房价和实际房价之间的误差。例如下图中我画了很多条小数线,每一条小数线就是实际房价和预测房价的差距(距离)。 + +![](7.jpg) + +然后把每条小竖线的长度加起来就等于现在通过这条直线预测出的房价与实际房价之间的差距。那每条小竖线的长度的加和怎么算?其实就是欧式距离加和,公式为:$$\sum_{i=1}^m(y^{(i)}-y\hat{^{(i)}})^2$$(其中$$y(i)$$表示的是实际房价,$$y \hat (i)$$表示的是预测房价)。 + + +这个欧氏距离加和其实就是用来量化预测结果和真实结果的误差的一个函数。在机器学习中称它为**损失函数**(说白了就是计算误差的函数)。那有了这个函数,就相当于有了一个评判标准,当这个函数的值越小,就越说明找到的这条直线越能拟合房价数据。所以说啊,线性回归就是通过这个损失函数做为评判标准来找出一条直线。 + +如果假设$$h_{(\theta)}(x)$$表示当权重为$$\theta$$,输入为$$x$$时计算出来的$$y \hat (i)$$,那么线性回归的损失函数$$J(\theta)$$就是: + +
+$$ +J(\theta)=\frac{1}{2}\sum^m_{i=1}(h_\theta(x^i)-y^i)^2 +$$ +
+ +## 怎样计算出线性回归的解? + +现在你应该已经弄明白了一个事实,那就是我只要找到一组参数(也就是线性方程每一项上的系数)能让我的损失函数的值最小,那我这一组参数就能最好的拟合我现在的训练数据。 + +那怎么来找到这一组参数呢?其实有两种套路,一种就是用大名鼎鼎的**梯度下降**,其大概思想就是根据每个参数对损失函数的偏导来更新参数。另一种是线性回归的**正规方程解**,这名字听起来高大上,其实本质就是根据一个固定的式子计算出参数。由于**正规方程解**在数据量比较大的时候时间复杂度比较高,所以在这一部分中,主要聊聊怎样使用梯度下降的方法来更新参数。 + + +### 什么是梯度下降 + +其实梯度下降不是一个机器学习算法,而是一种基于搜索的最优化方法。因为很多算法都没有正规解的,所以需要通过一次一次的迭代来找到找到一组参数能让损失函数最小。损失函数的大概套路可以参看这个图: + +![](8.jpg) + +所以说,梯度下降的作用是不断的寻找靠谱的权重是多少。 + +现在已经知道了梯度下降就是用来找权重的,那怎么找权重呢?瞎猜?不可能的....这辈子都不可能猜的。 想想都知道,权重的取值范围可以看成是个实数空间,那 100 个特征就对应着 100 个权重, 10000 个特 征就对应着 10000 个权重。如果靠瞎猜权重的话,应该这辈子都猜不中了。所以找权重的找个套路来找, 这个套路就是梯度。梯度其实就是让函数值为 0 时其中各个变量的偏导所组成的向量,而且梯度方向是使 得函数值增长最快的方向。 + +这个性质怎么理解呢?举个例子。假如我是个想要成为英雄联盟郊区王者的死肥宅,然后要成为郊区王者 可能有这么几个因素,一个是英雄池的深浅,一个是大局观,还有一个是骚操作。他们对我成为王者来说 都有一定的权重。如图所示,每一个因素的箭头都有方向(也就是因素对于我成为王者的偏导的方向)和 长度(偏导的值的大小)。然后在这些因素的共同作用下,我最终会朝着一个方向来训练(好比物理中分 力和合力的关系),这个时候我就能以最快的速度向郊区王者更进一步。 + +![](9.jpg) + +也就是说我如果一直朝着最终的那个方向努力的话,理论上来说我就能以最快的速度成为郊区王者。 + +现在你知道了梯度的方向是函数增长最快的方向,那我在梯度前面取个负号(反方向),那不就是函数下 降最快的方向了么。所以,梯度下降它的本质就是更新权重的时候是沿着梯度的反方向更新。好比下面这 个图,假如我是个瞎子,然后莫名其妙的来到了一个山谷里。现在我要做的事情就是走到山谷的谷底。因 为我是瞎子,所以我只能一点一点的挪。要挪的话,那我肯定是那我的脚在我四周扫一遍,觉得哪里感觉 起来更像是在下山那我就往哪里走。然后这样循环反复一发我最终就能走到山谷的谷底。 + +![](10.jpg) + +所以,梯度下降的伪代码如下: + +![](11.jpg) + +循环干的事情就相当于我下山的时候在迈步子,代码里的 α 高端点叫学习率,实际上就是代表我下山的时 候步子迈多大。值越小就代表我步子迈得小,害怕一脚下去掉坑里。值越大就代表我胆子越大,步子迈得 越大,但是有可能会越过山谷的谷底。 + +### 使用梯度下降求解线性回归的解 + +线性回归的损失函数 $$J$$ 为:$$J(\theta)=\frac{1}{2}\sum^m_{i=1}(h_\theta(x^i)-y^i)^2$$,其中$$\theta$$为线性回归的解。使用梯度下降来求解,最关键的一步是算梯度(也就是算偏导),通过计算可知第$$j$$个权重的偏导为: + +
+$$ +\frac{\partial J(\theta_j)}{\theta_J} = (h_\theta(x)-y)x_j +$$ +
+
+ +所以很自然的可以想到,使用梯度下降求解线性回归的解的流程如下: + +```python +循环若干次 + 计算当前参数theta对损失函数的梯度 gradient + theta = theta - alpha * gradient +``` + +当$$\theta$$更新好了之后,就相当于得到了一个线性回归模型。也就是说只要将数据放到模型中进行计算就能得到预测输出了。 + + +## sklearn中的线性回归 + +`LinearRegression`的构造函数中有两个常用的参数可以设置: + +- `fit_intercept`:是否有截据,如果没有则直线过原点,默认为`Ture`。 +- `normalize`:是否将数据归一化,默认为`False`。 + + +`LinearRegression`类中的`fit`函数用于训练模型,`fit`函数有两个向量输入: + +- `X`:大小为**[样本数量,特征数量]**的`ndarray`,存放训练样本 +- `Y`:值为整型,大小为**[样本数量]**的`ndarray`,存放训练样本的标签值 + +`LinearRegression`类中的`predict`函数用于预测,返回预测值,`predict`函数有一个向量输入: + +- `X`:大小为**[样本数量,特征数量]**的`ndarray`,存放预测样本 + +`LinearRegression`的使用代码如下: +```python +lr = LinearRegression() +lr.fit(X_train, Y_train) +predict = lr.predict(X_test) +``` + +如果像动手实现线性回归算法,并掌握如何使用 sklearn 来解决实际问题,可以尝试进入链接进行实战:https://www.educoder.net/shixuns/4awq25iv/challenges \ No newline at end of file diff --git a/Part2/Chapter5/5.2 监督学习/用概率说话---朴素贝叶斯分类器.md b/Part2/Chapter5/5.2 监督学习/用概率说话---朴素贝叶斯分类器.md new file mode 100644 index 0000000..79f7540 --- /dev/null +++ b/Part2/Chapter5/5.2 监督学习/用概率说话---朴素贝叶斯分类器.md @@ -0,0 +1,250 @@ +# 5.2.4 用概率说话---朴素贝叶斯分类器 + +朴素贝叶斯分类算法是基于贝叶斯理论和特征条件独立假设的分类算法。对于给定的训练集,首先基于特征条件独立假设学习数据的概率分布。然后基于此模型,对于给定的特征数据`x`,利用贝叶斯定理计算出标签`y`。朴素贝叶斯分类算法实现简单,预测的效率很高,是一种常用的分类算法。 + +## 条件概率 + +朴素贝叶斯分类算法是基于贝叶斯定理与特征条件独立假设的分类方法,因此想要了解朴素贝叶斯分类算法背后的算法原理,就不得不用到概率论的一些知识,首当其冲就是**条件概率**。 + +###什么是条件概率 + +概率指的是某一事件`A`发生的可能性,表示为`P(A)`。而条件概率指的是某一事件`A`已经发生了条件下,另一事件`B`发生的可能性,表示为`P(B|A)`,举个例子: + +今天有`25%`的可能性下雨,即`P(下雨)=0.25`; +今天`75%`的可能性是晴天,即`P(晴天)=0.75`; +如果下雨,我有`75%`的可能性穿外套,即`P(穿外套|下雨)=0.75`; +如果下雨,我有`25%`的可能性穿T恤,即`P(穿T恤|下雨)=0.25`; + +从上述例子可以看出,条件概率描述的是`|`右边的事件已经发生之后,左边的事件发生的可能性,**而不是两个事件同时发生的可能性!** + + +###怎样计算条件概率 + +设`A,B`是两个事件,且`P(A)>0`,称`P(B|A)=P(AB)/P(A)`为在事件`A`发生的条件下,事件`B`发生的条件概率。(**其中`P(AB)`表示事件A和事件B同时发生的概率**) + +举个例子,**现在有一个表格,表格中统计了甲乙两个厂生产的产品中合格品数量、次品数量的数据。数据如下:** + +| | 甲厂 |乙厂 |合计 | +| :-: | :-: | :-:| :-: | +| 合格品 | 475 | 644 | 1119 | +| 次品| 25 | 56 | 81 | +| 合计| 500 | 700 | 1200 | + +现在想要算一下已知产品是甲厂生产的,那么产品是次品的概率是多少。这个时候其实就是在算条件概率,计算非常简单。 + +假设事件`A`为产品是甲厂生产的,事件`B`为产品是次品。则根据表中数据可知`P(AB)=25/1200`,`P(A)=500/1200`。则`P(B|A)=P(AB)/P(A)=25/500`。 + +###乘法定理 + +将条件概率的公式两边同时乘以`P(A)`,就变成了**乘法定理**,即`P(AB)=P(B|A)*P(A)`。那么乘法定理怎么用呢?举个例子: + +**现在有一批产品共`100`件,次品有`10`件,从中不放回地抽取`2`次,每次取`1`件。现在想要算一下第一次为次品,第二次为正品的概率。** + +从问题来看,这个问题问的是第一次为次品,第二次为正品这两个事件同时发生的概率。所以可以用乘法定理来解决这个问题。 + +假设事件`A`为第一次为次品,事件`B`为第二次为正品。则`P(AB)=P(A)*P(B|A)=(10/100)*(90/99)=0.091`。 + +## 全概率公式 + +**贝叶斯公式**是**朴素贝叶斯分类算法**的核心数学理论,在了解贝叶斯公式之前,我们需要先了解**全概率公式**的相关知识。 + +### 引例 + +小明从家到公司上班总共有三条路可以直达,如下图: + +![](13.jpg) + +但是每条路每天拥堵的可能性不太一样,由于路的远近不同,选择每条路的概率如下表所示: + +| L1 | L2 | L3 | +| :-: | :-: | :-: | +| 0.5 | 0.3 | 0.2 | + +每天从上述三条路去公司时不堵车的概率如下表所示: + +| L1不堵车 | L2不堵车 | L3不堵车 | +| :-: | :-: | :-: | +| 0.2 | 0.4 | 0.7 | + +如果不堵车就不会迟到,现在小明想要算一算去公司上班不会迟到的概率是多少,应该怎么办呢? + +其实很简单,假设事件`C`为小明不迟到,事件`A1`为小明选`L1`这条路并且不堵车,事件`A2`为小明选`L2`这条路并且不堵车,事件`A3`为小明选`L3`这条路并且不堵车。那么很显然`P(C)=P(A1)+P(A2)+P(A3)`。 + +那么问题来了,`P(A1)`、`P(A2)`和`P(A3)`怎么算呢?其实只要会算`P(A1)`其他的就都会算了。我们同样可以假设事件`D1`为小明选择`L1`路,事件`E1`为不堵车。那么`P(A1)=P(D1)*P(E1)`。但是在从表格中我们只知道`P(D1)=0.5`,怎么办呢? + +回忆一下上面介绍的**乘法定理**,不难想到`P(A1)=P(D1)*P(E1|D1)`。从表格中可以看出`P(E1|D1)=0.2`。因此`P(A1)=0.5*0.2=0.1`。 + +然后依葫芦画瓢可以很快算出,`P(A2)=0.3*0.4=0.12`,`P(A3)=0.2*0.7=0.14`。所以`P(C)=0.1+0.12+0.14=0.36`。 + +### 什么是全概率公式 + +当为了达到某种目的,但是达到目的有很多种方式,如果想知道通过所有方式能够达到目的的概率是多少的话,就需要用到**全概率公式**(**上面的例子就是这种情况!**)。全概率公式的定义如下: + +若事件$$B_1$$,$$B_2$$,...,$$B_n$$两两互不相容,并且其概率和为`1`。那么对于任意一个事件`C`都满足: + +
+$$ +P(C)=P(B_1)P(C|B_1)+...+P(B_n)P(C|B_n)=\sum_{i=1}^{n}P(B_i)P(C|B_i) +$$ +
+
+ +引例中小明选择哪条路去公司的概率是**两两互不相容的**(只能选其中一条路去公司),**并且和为`1`**。所以小明不迟到的概率可以通过全概率公式来计算,而引例中的计算过程就是用的全概率公式。 + +## 贝叶斯公式 + +当已知引发事件发生的各种原因的概率,想要算该事件发生的概率时,我们可以用**全概率公式**。但如果现在反过来,已知事件已经发生了,但想要计算引发该事件的各种原因的概率时,我们就需要用到**贝叶斯公式**了。 + +贝叶斯公式定义如下,其中$$A$$表示已经发生的事件,$$B_i$$为导致事件$$A$$发生的第$$i$$个原因: + +
+$$ +P(B_i|A)=\frac{P(A|B_i)P(B_i)}{\sum_{i=1}^nP(A|B_i)P(B_i)} +$$ +
+
+ +贝叶斯公式看起来比较复杂,其实非常简单,分子部分是**乘法定理**,分母部分是**全概率公式**(分母等于$$P(A)$$)。 + +如果我们对贝叶斯公式进行一个简单的数学变换(两边同时乘以分母,再两边同时除以$$P(B_i)$$)。就能够得到如下公式: + +
+$$ +P(A|B_i)=\frac{P(B_i|A)P(A)}{P(B_i)} +$$ +
+ +## 贝叶斯算法流程 + +在炎热的夏天你可能需要买一个大西瓜来解暑,但虽然你的挑西瓜的经验很老道,但还是会有挑错的时候。尽管如此,你可能还是更愿意相信自己经验。假设现在在你面前有一个纹路清晰,拍打西瓜后声音浑厚,按照你的经验来看这个西瓜是好瓜的概率有`80`%,不是好瓜的概率有`20`%。那么在这个时候你下意识会认为这个西瓜是好瓜,因为它是好瓜的概率大于不是好瓜的概率。 + +### 朴素贝叶斯分类算法的预测流程 + +**朴素贝叶斯分类算法的预测思想和引例中挑西瓜的思想一样,会根据以往的经验计算出待预测数据分别为所有类别的概率,然后挑选其中概率最高的类别作为分类结果。** + +假如现在一个西瓜的数据如下表所示: + +| 颜色 | 声音 | 纹理 | 是否为好瓜 | +| :-: | :-: | :-: | :-: | +| 绿 | 清脆 | 清晰 | ? | + +若想使用朴素贝叶斯分类算法的思想,根据这条数据中`颜色`、`声音`和`纹理`这三个特征来推断是不是好瓜,我们需要计算出这个西瓜是好瓜的概率和不是好瓜的概率。 + +假设事件`A1`为好瓜,事件`B`为绿,事件`C`为清脆,事件`D`为清晰,则这个西瓜是好瓜的概率为`P(A1|BCD)`。根据贝叶斯公式可知: + +
+$$ +P(A_1|BCD)=\frac{P(A_1)P(B|A_1)P(C|A_1)P(D|A_1)}{P(BCD)} +$$ +
+
+ +同样,假设事件`A2`为好瓜,事件`B`为绿,事件`C`为清脆,事件`D`为清晰,则这个西瓜不是好瓜的概率为`P(A2|BCD)`。根据贝叶斯公式可知: + +
+$$ +P(A_2|BCD)=\frac{P(A_2)P(B|A_2)P(C|A_2)P(D|A_2)}{P(BCD)} +$$ +
+
+ +朴素贝叶斯分类算法的思想是取概率最大的类别作为预测结果,所以如果满足下面的式子,则认为这个西瓜是好瓜,否则就不是好瓜: + +
+$$ +\frac{P(A_1)P(B|A_1)P(C|A_1)P(D|A_1)}{P(BCD)}>\frac{P(A_2)P(B|A_2)P(C|A_2)P(D|A_2)}{P(BCD)} +$$ +
+
+ +从上面的式子可以看出,`P(BCD)`是多少对于判断哪个类别的概率高没有影响,所以式子可以简化成如下形式: + +
+$$ +P(A_1)P(B|A_1)P(C|A_1)P(D|A_1)>P(A_2)P(B|A_2)P(C|A_2)P(D|A_2) +$$ +
+
+ +所以在预测时,需要知道`P(A1)`,`P(A2)`,`P(B|A_1)`,`P(C|A_1)`,`P(D|A_1)`等于多少。而这些概率在训练阶段可以计算出来。 + +### 朴素贝叶斯分类算法的训练流程 + +训练的流程非常简单,主要是计算各种**条件概率**。假设现在有一组西瓜的数据,如下表所示: + +| 编号 | 颜色 | 声音 | 纹理 | 是否为好瓜 | +| :-: | :-: | :-: | :-: | :-: | +| 1 | 绿 | 清脆 | 清晰 | 是 | +| 2 | 黄 | 浑厚 | 模糊 | 否 | +| 3 | 绿 | 浑厚 | 模糊 | 是 | +| 4 | 绿 | 清脆 | 清晰 | 是 | +| 5 | 黄 | 浑厚 | 模糊 | 是 | +| 6 | 绿 | 清脆 | 清晰 | 否 | + +从表中数据可以看出: + +`P(是好瓜)=4/6` +`P(颜色绿|是好瓜)=3/4` +`P(颜色黄|是好瓜)=1/4` +`P(声音清脆|是好瓜)=1/2` +`P(声音浑厚|是好瓜)=1/2` +`P(纹理清晰|是好瓜)=1/2` +`P(纹理模糊|是好瓜)=1/2` +`P(不是好瓜)=2/6` +`P(颜色绿|不是好瓜)=1/2` +`P(颜色黄|是好瓜)=1/2` +`P(声音清脆|不是好瓜)=1/2` +`P(声音浑厚|不是好瓜)=1/2` +`P(纹理清晰|不是好瓜)=1/2` +`P(纹理模糊|不是好瓜)=1/2` + +当得到以上概率后,训练阶段的任务就已经完成了。我们不妨再回过头来预测一下这个西瓜是不是好瓜。 + +| 颜色 | 声音 | 纹理 | 是否为好瓜 | +| :-: | :-: | :-: | :-: | +| 绿 | 清脆 | 清晰 | ? | + +假设事件`A1`为好瓜,事件`B`为绿,事件`C`为清脆,事件`D`为清晰。则有: + +
+$$ +P(A_1)P(B|A_1)P(C|A_1)P(D|A_1)=\frac{4}{6}*\frac{3}{4}*\frac{1}{2}*\frac{1}{2}=\frac{1}{8} +$$ +
+
+ +假设事件`A2`为不是瓜,事件`B`为绿,事件`C`为清脆,事件`D`为清晰。则有: + +
+$$ +P(A_2)P(B|A_2)P(C|A_2)P(D|A_2)=\frac{2}{6}*\frac{1}{2}*\frac{1}{2}*\frac{1}{2}=\frac{1}{24} +$$ +
+
+ +由于$$\frac{1}{8}>\frac{1}{24}$$,所以这个西瓜是好瓜。 + +## sklearn中的朴素贝叶斯分类器 + +`MultinomialNB`是`sklearn`中多项分布数据的朴素贝叶斯算法的实现。 + +在`MultinomialNB`实例化时`alpha`是一个常用的参数。 +- `alpha`: 平滑因子。当等于`1`时,做的是拉普拉斯平滑;当小于`1`时做的是`Lidstone`平滑;当等于`0`时,不做任何平滑处理。 + +`MultinomialNB`类中的`fit`函数实现了朴素贝叶斯分类算法训练模型的功能,`predict`函数实现了法模型预测的功能。 + +其中`fit`函数的参数如下: +- `X`:大小为`[样本数量,特征数量]`的`ndarry`,存放训练样本 +- `Y`:值为整型,大小为`[样本数量]`的`ndarray`,存放训练样本的分类标签 + +而`predict`函数有一个向量输入: +- `X`:大小为`[样本数量,特征数量]`的`ndarry`,存放预测样本 + +`MultinomialNB`的使用代码如下: +```python +clf = tree.MultinomialNB() +clf.fit(X_train, Y_train) +result = clf.predict(X_test) +``` + +如果像动手实现朴素贝叶斯分类器算法,并掌握如何使用 sklearn 来解决实际问题,可以尝试进入链接进行实战:https://www.educoder.net/shixuns/uyl5pk2q/challenges \ No newline at end of file diff --git a/Part2/Chapter5/5.2 监督学习/知错能改善莫大焉---Adaboost.md b/Part2/Chapter5/5.2 监督学习/知错能改善莫大焉---Adaboost.md new file mode 100644 index 0000000..8e694f5 --- /dev/null +++ b/Part2/Chapter5/5.2 监督学习/知错能改善莫大焉---Adaboost.md @@ -0,0 +1,96 @@ +# 5.2.8 知错能改善莫大焉---Adaboost + +## Boosting + +**提升方法**基于这样一种思想:对于一个复杂任务来说,将多个专家的判断进行适当的综合所得出的判断,要比其中任何一个专家单独的判断好。 + +历史上,`Kearns`和`Valiant`首先提出了**强可学习**和**弱可学习**的概念。指出:在`PAC`学习的框架中,一个概念,如果存在一个多项式的学习算法能够学习它,并且正确率很高,那么就称这个概念是强可学习的;一个概念,如果存在一个多项式的学习算法能够学习它,学习的正确率仅比随机猜测略好,那么就称这个概念是弱可学习的。非常有趣的是`Schapire`后来证明强可学习与弱可学习是等价的,也就是说,在`PAC`学习的框架下,一个概念是强可学习的充分必要条件是这个概念是弱可学习的。 + +这样一来,问题便成为,在学习中,如果已经发现了**弱学习算法**,那么能否将它**提升**为**强学习算法**。大家知道,发现弱学习算法通常要比发现强学习算法容易得多。那么如何具体实施提升,便成为开发提升方法时所要解决的问题。 + +与`bagging`不同,`boosting`采用的是一个串行训练的方法。首先,它训练出一个**弱分类器**,然后在此基础上,再训练出一个稍好点的**弱分类器**,以此类推,不断的训练出多个弱分类器,最终再将这些分类器相结合,这就是`boosting`的基本思想,流程如下图: + +![](30.jpg) + +可以看出,子模型之间存在强依赖关系,必须串行生成。`boosting`是利用不同模型的相加,构成一个更好的模型,求取模型一般都采用序列化方法,后面的模型依据前面的模型。 + +## Adaboost原理 + +对提升方法来说,有两个问题需要回答:**一是在每一轮如何改变训练数据的权值或概率分布;二是如何将弱分类器组合成一个强分类器。**关于第`1`个问题,AdaBoost的做法是,**提高那些被前一轮弱分类器错误分类样本的权值,而降低那些被正确分类样本的权值**。这样一来,那些没有得到正确分类的数据,由于其权值的加大而受到后一轮的弱分类器的更大关注。于是,分类问题被一系列的弱分类器“分而治之”。至于第`2`个问题,即弱分类器的组合,AdaBoost采取**加权多数表决的方法,加大分类误差率小的弱分类器的权值,使其在表决中起较大的作用,减小分类误差率大的弱分类器的权值,使其在表决中起较小的作用**。 + +## Adaboost算法流程 + +`AdaBoost`是`AdaptiveBoost`的缩写,表明该算法是具有适应性的提升算法。 + +算法的步骤如下: + +`1`. 给每个训练样本$$(x_1,x_2,..,x_N)$$分配权重,初始权重$$w_1$$均为$$1/N$$ + +`2`. 针对带有权值的样本进行训练,得到模型$$G_m$$(初始模型为$$G +_1$$) + +`3`. 计算模型$$G_m$$的误分率: + +$$ +e_m=\sum_i^Nw_iI(y_i\neq G_M(X_i)) +$$ + +其中: + +$$ +I(y_i\neq G_M(X_i) +$$ + +为指示函数,表示括号内成立时函数值为`1`,否则为`0` + +`4`. 计算模型$$G_m$$的系数: + +$$ +\alpha_m=\frac{1}{2}\log[\frac{1-e_m}{e_m}] +$$ + +`5`. 根据误分率$$e$$和当前权重向量$$w_m$$更新权重向量: + +$$ +w_{m+1,i}=\frac{w_m}{z_m}exp(-\alpha_my_iG_m(x_i)) +$$ + + +其中$$Z_m$$为规范化因子: + +$$ +z_m=\sum_{i=1}^mw_{mi}exp(-\alpha_my_iG_m(x_i)) +$$ + + +`6`. 计算组合模型$$f(x)=\sum_{m=1}^M\alpha_mG_m(x_i)$$的误分率 + +`7`. 当组合模型的误分率或迭代次数低于一定阈值,停止迭代;否则,回到步骤 `2` + +## sklearn中的Adaboost + +`AdaBoostClassifier`是`skleran`中Adaboost分类器的实现,`AdaBoostClassifier`的构造函数中有四个常用的参数可以设置: + +- `algorithm`:这个参数只有 AdaBoostClassifier 有。主要原因是scikit-learn 实现了两种 Adaboost 分类算法,`SAMME`和`SAMME.R`。两者的主要区别是弱学习器权重的度量,`SAMME.R`使用了概率度量的连续值,迭代一般比`SAMME`快,因此 AdaBoostClassifier 的默认算法`algorithm`的值也是`SAMME.R`。 +- `n_estimators`:弱学习器的最大迭代次数。一般来说`n_estimators`太小,容易欠拟合,`n_estimators`太大,又容易过拟合,一般选择一个适中的数值。默认是`50`。 +- `learning_rate`:AdaBoostClassifier 和 AdaBoostRegressor 都有,即每个弱学习器的权重缩减系数`ν`,默认为`1.0`。 +- `base_estimator`:弱分类学习器或者弱回归学习器。理论上可以选择任何一个分类或者回归学习器,不过需要支持样本权重。我们常用的一般是 CART 决策树或者神经网络 MLP。 + + +和`sklearn`中其他分类器一样,AdaBoostClassifier 类中的`fit`函数用于训练模型,`fit`函数有两个向量输入: + +- `X`:大小为**[样本数量,特征数量]**的`ndarray`,存放训练样本 +- `Y`:值为整型,大小为**[样本数量]**的`ndarray`,存放训练样本的分类标签 + +AdaBoostClassifier 类中的`predict`函数用于预测,返回预测标签,`predict`函数有一个向量输入: + +`X`:大小为**[样本数量,特征数量]**的`ndarray`,存放预测样本 +AdaBoostClassifier的使用代码如下: + +```python +ada=AdaBoostClassifier(n_estimators=5,learning_rate=1.0) +ada.fit(train_data,train_label) +predict = ada.predict(test_data) +``` + +如果像动手实现 Adaboost 算法,并掌握如何使用 sklearn 来解决实际问题,可以尝试进入链接进行实战:https://www.educoder.net/shixuns/xkv4b6yc/challenges \ No newline at end of file diff --git a/Part2/Chapter5/5.2 监督学习/群众的力量是伟大的---随机森林.md b/Part2/Chapter5/5.2 监督学习/群众的力量是伟大的---随机森林.md new file mode 100644 index 0000000..98383b1 --- /dev/null +++ b/Part2/Chapter5/5.2 监督学习/群众的力量是伟大的---随机森林.md @@ -0,0 +1,85 @@ +# 5.2.7 群众的力量是伟大的---随机森林 + +既然有决策树,那有没有用多棵决策树组成森林的算法呢?有!那就是**随机森林**。随机森林是一种叫**Bagging**的算法框架的变体。所以想要理解**随机森林**首先要理解**Bagging**。 + +## Bagging + +### 什么是Bagging + +**Bagging** 是 Bootstrap Aggregating 的英文缩写,刚接触的您不要误认为 **Bagging** 是一种算法,**Bagging** 是集成学习中的学习框架, **Bagging** 是并行式集成学习方法。大名鼎鼎的随机森林算法就是在**Bagging**的基础上修改的算法。 + +** Bagging 方法的核心思想就是三个臭皮匠顶个诸葛亮**。如果使用 **Bagging** 解决分类问题,就是将多个分类器的结果整合起来进行投票,选取票数最高的结果作为最终结果。如果使用 **Bagging** 解决回归问题,就将多个回归器的结果加起来然后求平均,将平均值作为最终结果。 + +那么 **Bagging** 方法为什么如此有效呢,举个例子。狼人杀我相信您应该玩过,在天黑之前,村民们都要根据当天所发生的事和别人的发现来投票决定谁可能是狼人。 + +如果我们将每个村民看成是一个分类器,那么每个村民的任务就是二分类,假设 $$h_i(x)$$ 表示第 $$i$$ 个村民认为 $$x$$ 是不是狼人( $$-1$$ 代表不是狼人, $$1$$ 代表是狼人),$$f(x)$$ 表示 $$x$$ 真正的身份(是不是狼人),$$\epsilon$$ 表示为村民判断错误的错误率。则有 $$P(h_i(x)\neq f(x))=\epsilon$$。 + +根据狼人杀的规则,村民们需要投票决定天黑前谁是狼人,也就是说如果有超过半数的村民投票时猜对了,那么这一轮就猜对了。那么假设现在有 $$T$$ 个村民,$$H(x)$$ 表示投票后最终的结果,则有 $$H(x)=sign(\sum_{i=1}^Th_i(x))$$。 + +现在假设每个村民都是有主见的人,对于谁是狼人都有自己的想法,那么他们的错误率也是相互独立的。那么根据**Hoeffding不等式**可知,$$H(x)$$ 的错误率为: + +$$ +P(H(x)\neq f(x))=\sum_{k=0}^{T/2}C_T^k(1-\epsilon)^k\epsilon ^{T-k} \leq exp(-\frac{1}{2}T(1-2\epsilon)^2) +$$ + + +根据上式可知,如果 $$5$$ 个村民,每个村民的错误率为 $$0.33$$ ,那么投票的错误率为 $$0.749$$ ;如果 $$20$$ 个村民,每个村民的错误率为 $$0.33$$ ,那么投票的错误率为 $$0.315$$ ;如果 $$50$$ 个村民,每个村民的错误率为 $$0.33$$ ,那么投票的错误率为 $$0.056$$ ;如果 $$100$$ 个村民,每个村民的错误率为 $$0.33$$ ,那么投票的错误率为 $$0.003$$ 。**从结果可以看出,村民的数量越大,那么投票后犯错的错误率就越小。**这也是**Bagging**性能强的原因之一。 + +### Bagging方法如何训练 + +**Bagging** 在训练时的特点就是**随机有放回采样**和**并行**。 + +**随机有放回采样:** 假设训练数据集有 $$m$$ 条样本数据,每次从这 $$m$$ 条数据中随机取一条数据放入采样集,然后将其返回,让下一次采样有机会仍然能被采样。然后重复 $$m$$ 次,就能得到拥有 $$m$$ 条数据的采样集,该采样集作为 **Bagging** 的众多分类器中的一个作为训练数据集。假设有 $$T$$ 个分类器(随便什么分类器),那么就重复 $$T$$ 此随机有放回采样,构建出 $$T$$ 个采样集分别作为 $$T$$ 个分类器的训练数据集。 + +**并行:** 假设有 $$10$$ 个分类器,在**Boosting**中,$$1$$ 号分类器训练完成之后才能开始$$2$$ 号分类器的训练,而在**Bagging**中,分类器可以同时进行训练,当所有分类器训练完成之后,整个**Bagging**的训练过程就结束了。 + +**Bagging**训练过程如下图所示: + +![](28.jpg) + +### Bagging方法如何预测 + +**Bagging**在预测时非常简单,就是**投票**!比如现在有 $$5$$ 个分类器,有 $$3$$ 个分类器认为当前样本属于 $$A$$ 类,$$1$$ 个分类器认为属于 $$B$$ 类,$$1$$ 个分类器认为属于 $$C$$ 类,那么**Bagging**的结果会是 $$A$$ 类(因为 $$A$$ 类的票数最高)。 + +**Bagging**预测过程如下图所示: + +![](29.jpg) + + +## 随机森林 + +**随机森林**是**Bagging**的一种扩展变体,**随机森林**的训练过程相对与**Bagging**的训练过程的改变有: + +- 基学习器:**Bagging**的基学习器可以是任意学习器,而随机森林则是以决策树作为基学习器。 +- 随机属性选择:假设原始训练数据集有 $$10$$ 个特征,从这 $$10$$ 个特征中随机选取 $$k$$ 个特征构成训练数据子集,然后将这个子集作为训练集扔给决策树去训练。其中 $$k$$ 的取值一般为 $$log2$$ (特征数量)。 + +这样的改动通常会使得随机森林具有更加强的泛化性,因为每一棵决策树的训练数据集是随机的,而且训练数据集中的特征也是随机抽取的。如果每一棵决策树模型的差异比较大,那么就很容易能够解决决策树容易过拟合的问题。 + +## sklearn中的随机森林 + +`RandomForestClassifier`是`skleran`中随机森林分类器的实现,的构造函数中有两个常用的参数可以设置: + +- `n_estimators`:森林中决策树的数量 +- `criterion`:构建决策树时,划分节点时用到的指标。有`gini`(**基尼系数**),`entropy`(**信息增益**)。若不设置,默认为`gini` +- `max_depth`:决策树的最大深度,如果发现模型已经出现过拟合,可以尝试将该参数调小。若不设置,默认为`None` +- `max_features`:随机选取特征时选取特征的数量,一般传入`auto`或者`log2`,默认为`auto`,`auto`表示`max_features=sqrt(训练集中特征的数量)`;`log2`表示`max_features=log2(训练集中特征的数量)` + +`RandomForestClassifier`类中的`fit`函数实现了随机森林分类器训练模型的功能,`predict`函数实现了模型预测的功能。 + +其中`fit`函数的参数如下: +- `X`:大小为`[样本数量,特征数量]`的`ndarry`,存放训练样本 +- `Y`:值为整型,大小为`[样本数量]`的`ndarray`,存放训练样本的分类标签 + +而`predict`函数有一个向量输入: +- `X`:大小为`[样本数量,特征数量]`的`ndarry`,存放预测样本 + +`RandomForestClassifier`的使用代码如下: +```python +from sklearn.ensemble import RandomForestClassifier + +clf = RandomForestClassifier(n_estimators=50) +clf.fit(X_train, Y_train) +result = clf.predict(X_test) +``` + +如果像动手实现随机森林算法,并掌握如何使用 sklearn 来解决实际问题,可以尝试进入链接进行实战:https://www.educoder.net/shixuns/ya8h7utx/challenges diff --git a/Part2/Chapter5/5.2 监督学习/近朱者赤近墨者黑---kNN.md b/Part2/Chapter5/5.2 监督学习/近朱者赤近墨者黑---kNN.md new file mode 100644 index 0000000..f1f0fe3 --- /dev/null +++ b/Part2/Chapter5/5.2 监督学习/近朱者赤近墨者黑---kNN.md @@ -0,0 +1,78 @@ +# 5.2.1 近朱者赤近墨者黑---kNN + +kNN 算法其实是众多机器学习算法中最简单的一种,因为该算法的思想完全可以用 8 个字来概括:“近朱 者赤,近墨者黑”。 + +## kNN算法解决分类问题 + +假设现在有这样的一个样本空间(由样本组成的一个空间),该样本空间里有宅男和文艺青年这两个类别,其 中红圈表示宅男,绿圈表示文艺青年。如下图所示: + +![](1.jpg) + +其实构建出这样的样本空间的过程就是 kNN 算法的训练过程。可想而知 kNN 算法是没有训练过程的,所以 kNN 算法属于懒惰学习算法。 + +假设我在这个样本空间中用黄圈表示,如下图所示: + +![](2.jpg) + +现在使用 kNN 算法来鉴别一下我是宅男还是文艺青年。首先需要计算我与样本空间中所有样本的距离。假设计算得到的距离表格如下: + +| 样本编号 | 1 | 2 | ... | 13 | 14 | +| :-: | :-: | :-: | :-: | :-: | :-: | +| 标签 | 宅男 | 宅男 | ... | 文艺青年 | 文艺青年 | +| 距离 | 11.2 | 9.5 | ... | 23.3 | 37.6 | + +然后找出与我距离最小的`k`个样本(`k`是一个超参数,需要自己设置,一般默认为`5`),假设与我离得最近的`5`个样本的标签和距离如下: + +| 样本编号 | 4 | 5 | 6 | 7 | 8 | +| :-: | :-: | :-: | :-: | :-: | :-: | +| 标签 | 宅男 | 宅男 | 宅男 | 宅男 | 文艺青年 | +| 距离 | 11.2 | 9.5 | 7.7 | 5.8 | 15.2 | + +最后只需要对这`5`个样本的标签进行统计,并将票数最多的标签作为预测结果即可。如上表中,宅男是`4`票,文艺青年是`1`票,所以我是宅男。 + +**注意**:有的时候可能会有票数一致的情况,比如`k = 4`时与我离得最近的样本如下: + +| 样本编号 | 4 | 9 | 11 | 13 | +| :-: | :-: | :-: | :-: | :-: | +| 标签 | 宅男 | 宅男 | 文艺青年 | 文艺青年 | +| 距离 | 4.2 | 9.5 | 7.7 | 5.8 | + +可以看出宅男和文艺青年的比分是`2 : 2`,那么可以尝试将属于宅男的`2`个样本与我的总距离和属于文艺青年的`2`个样本与我的总距离进行比较。然后选择总距离最小的标签作为预测结果。在这个例子中预测结果为文艺青年(宅男的总距离为`4.2 + 9.5`,文艺青年的总距离为`7.7 + 5.8`)。 + +## kNN算法解决回归问题 + +很明显,刚刚使用**kNN算法**解决了一个分类问题,那**kNN算法**能解决回归问题吗?当然可以! + +在使用`kNN`算法解决回归问题时的思路和解决分类问题的思路基本一致,只不过预测标签值是多少的的时候是将距离最近的`k`个样本的标签值加起来再算个平均,而不是投票。例如离待预测样本最近的`5`个样本的标签如下: + +| 样本编号 | 4 | 9 | 11 | 13 | 15 | +| :-: | :-: | :-: | :-: | :-: | :-: | +| 标签 | 1.2 | 1.5 | 0.8 | 1.33 | 1.19| + +所以待预测样本的标签为:`(1.2+1.5+0.8+1.33+1.19)/5=1.204` + +## sklearn中的kNN算法 + +想要使用`sklearn`中使用`kNN`算法进行分类,只需要如下的代码(其中`train_feature`、`train_label`和`test_feature`分别表示训练集数据、训练集标签和测试集数据): + +```python +from sklearn.neighbors import KNeighborsClassifier + +#生成K近邻分类器 +clf=KNeighborsClassifier() +#训练分类器 +clf.fit(train_feature, train_label) +#进行预测 +predict_result=clf.predict(test_feature) +``` + +当我们的`kNN`算法需要不同的参数时,上面的代码就不能满足我的需要了。所需要做的改变是在 +`clf=KNeighborsClassifier()`这一行中。`KNeighborsClassifier()`的构造函数其实还是有其他参数的。 + +比较常用的参数有以下几个: + +- `n_neighbors`:即`kNN`算法中的`K`值,为一整数,默认为`5`。 +- `metric`:距离函数。参数可以为字符串(预设好的距离函数)或者是callable对象。默认值为闵可夫斯基距离。 +- `p`:当`metric`为闵可夫斯基距离公式时可用,为一整数,默认值为`2`,也就是欧式距离。 + +如果像动手实现 kNN 算法,并掌握如何使用 sklearn 来解决实际问题,可以尝试进入链接进行实战:https://www.educoder.net/shixuns/aw9bxy75/challenges diff --git a/Part2/Chapter5/5.3 无监督学习/1.jpg b/Part2/Chapter5/5.3 无监督学习/1.jpg new file mode 100644 index 0000000..85aa468 Binary files /dev/null and b/Part2/Chapter5/5.3 无监督学习/1.jpg differ diff --git a/Part2/Chapter5/5.3 无监督学习/10.jpg b/Part2/Chapter5/5.3 无监督学习/10.jpg new file mode 100644 index 0000000..0bc1d5e Binary files /dev/null and b/Part2/Chapter5/5.3 无监督学习/10.jpg differ diff --git a/Part2/Chapter5/5.3 无监督学习/11.jpg b/Part2/Chapter5/5.3 无监督学习/11.jpg new file mode 100644 index 0000000..edb1e2b Binary files /dev/null and b/Part2/Chapter5/5.3 无监督学习/11.jpg differ diff --git a/Part2/Chapter5/5.3 无监督学习/12.jpg b/Part2/Chapter5/5.3 无监督学习/12.jpg new file mode 100644 index 0000000..1bbdc9f Binary files /dev/null and b/Part2/Chapter5/5.3 无监督学习/12.jpg differ diff --git a/Part2/Chapter5/5.3 无监督学习/13.jpg b/Part2/Chapter5/5.3 无监督学习/13.jpg new file mode 100644 index 0000000..15d8ccf Binary files /dev/null and b/Part2/Chapter5/5.3 无监督学习/13.jpg differ diff --git a/Part2/Chapter5/5.3 无监督学习/14.jpg b/Part2/Chapter5/5.3 无监督学习/14.jpg new file mode 100644 index 0000000..1b3ddbf Binary files /dev/null and b/Part2/Chapter5/5.3 无监督学习/14.jpg differ diff --git a/Part2/Chapter5/5.3 无监督学习/2.jpg b/Part2/Chapter5/5.3 无监督学习/2.jpg new file mode 100644 index 0000000..039b3c5 Binary files /dev/null and b/Part2/Chapter5/5.3 无监督学习/2.jpg differ diff --git a/Part2/Chapter5/5.3 无监督学习/3.jpg b/Part2/Chapter5/5.3 无监督学习/3.jpg new file mode 100644 index 0000000..4e22752 Binary files /dev/null and b/Part2/Chapter5/5.3 无监督学习/3.jpg differ diff --git a/Part2/Chapter5/5.3 无监督学习/4.jpg b/Part2/Chapter5/5.3 无监督学习/4.jpg new file mode 100644 index 0000000..13d59a2 Binary files /dev/null and b/Part2/Chapter5/5.3 无监督学习/4.jpg differ diff --git a/Part2/Chapter5/5.3 无监督学习/5.jpg b/Part2/Chapter5/5.3 无监督学习/5.jpg new file mode 100644 index 0000000..e961461 Binary files /dev/null and b/Part2/Chapter5/5.3 无监督学习/5.jpg differ diff --git a/Part2/Chapter5/5.3 无监督学习/6.jpg b/Part2/Chapter5/5.3 无监督学习/6.jpg new file mode 100644 index 0000000..cbdbe2b Binary files /dev/null and b/Part2/Chapter5/5.3 无监督学习/6.jpg differ diff --git a/Part2/Chapter5/5.3 无监督学习/7.jpg b/Part2/Chapter5/5.3 无监督学习/7.jpg new file mode 100644 index 0000000..2e70ba5 Binary files /dev/null and b/Part2/Chapter5/5.3 无监督学习/7.jpg differ diff --git a/Part2/Chapter5/5.3 无监督学习/8.jpg b/Part2/Chapter5/5.3 无监督学习/8.jpg new file mode 100644 index 0000000..29d1303 Binary files /dev/null and b/Part2/Chapter5/5.3 无监督学习/8.jpg differ diff --git a/Part2/Chapter5/5.3 无监督学习/9.jpg b/Part2/Chapter5/5.3 无监督学习/9.jpg new file mode 100644 index 0000000..2b9d94d Binary files /dev/null and b/Part2/Chapter5/5.3 无监督学习/9.jpg differ diff --git a/Part2/Chapter5/5.3 无监督学习/keepfile.txt b/Part2/Chapter5/5.3 无监督学习/keepfile.txt deleted file mode 100644 index e69de29..0000000 diff --git a/Part2/Chapter5/5.3 无监督学习/不忘初心---多维缩放.md b/Part2/Chapter5/5.3 无监督学习/不忘初心---多维缩放.md new file mode 100644 index 0000000..125300e --- /dev/null +++ b/Part2/Chapter5/5.3 无监督学习/不忘初心---多维缩放.md @@ -0,0 +1,101 @@ +# 5.3.5 不忘初心---多维缩放 + +多维缩放(Multiple Dimensional Scaling,MDS)是一种经典的降维方法。其主要思想是保持样本在原空间和低维空间的距离不变。因为距离是样本之间的一个很好的分离属性,降维后,保持距离不变,那么就相当于保持了样本的相对空间关系不变。 + +## MDS算法思想 + +假如,一份数据可视化后结果如下: + +![](13.jpg) + +则将数据进行旋转,数据的特征值发生改变,但是每个点与数据中其他点的距离并没有发生改变: + +![](14.jpg) + +所以,`MDS`算法认为,在数据样本中,每个样本的每个特征值并不是数据间关系的必要特征,真正的基础特征是每个点与数据集中其他点的**距离**。 + +假设存在距离矩阵,它的第`i`行第`j`列为$$dist_{ij}$$表示样本`i`到样本`j`之间的距离,我们要通过样本的坐标计算出距离矩阵非常简单。但是,反过来,我们想通过距离矩阵还原出每个样本的坐标就很困难了,而`MDS`算法就是用来解决这个问题的。它可以将一个数据集的距离矩阵还原成一个`D`维坐标来表示数据集。 + +## MDS算法推导 + +假设存在`m`个样本,在原始空间中的距离矩阵$$D\in R^{m\times m}$$,其第`i`行`j`列为样本`i`到样本`j`之间的距离。目标是获得样本在$$d$$'维空间中的欧式距离等于原空间的欧氏距离,即: + +$$ +||z_i-z_j||=dist_{ij} +$$ + +令$$B=Z^TZ\in R^{m\times m}$$,其中`B`为降维后的内积矩阵,$$b_{ij}=z_i^Tz_j$$,则: + +$$ +dist_{ij}^2=||z_i-z_j||^2=||z_i||^2+||z_j||^2-2z_i^Tz_j +$$ + +令降维后的样本被中心化,则: + +$$ +\sum\limits_{i=1}^mdist_{ij}^2=tr(B)+mb_{jj} +$$ + +$$ +\sum\limits_{j=1}^mdist_{ij}^2=tr(B)+mb_{ii} +$$ + +$$ +\sum\limits_{i=1}^m\sum\limits_{j=1}^mdist_{ij}^2=2mtr(B) +$$ + +令 +$$ +dist_{i.}^2=\frac{1}{m}\sum\limits_{j=1}^mdist_{ij}^2...(1) +$$ + +$$ +dist_{.j}^2=\frac{1}{m}\sum\limits_{i=1}^mdist_{ij}^2...(2) +$$ + +$$ +dist_{..}^2=\frac{1}{m^2}\sum\limits_{i=1}^m\sum\limits_{j=1}^mdist_{ij}^2...(3) +$$ + +则 + +$$ +b_{ij}=-\frac{1}{2}(dist_{ij}^2-dist_{i.}^2-dist_{.j}^2+dist^2_{..}) +$$ + +由此即可通过降维前后保持不变的**距离矩阵**求取**内积矩阵**。 + +再将**内积矩阵**做特征分解,取`d`个最大的特征值所构成的对角矩阵$$\Lambda$$,并求相应的特征向量矩阵$$V$$,最后计算出`Z`: + +$$ +Z = V\Lambda^{\frac{1}{2}} +$$ + +## MDS算法流程 + +`1`. 计算式子`1`,`2`,`3` +`2`. 计算矩阵`B` +`3`. 对矩阵`B`进行特征分解 +`4`. 取`d`个最大特征值所构成的对角矩阵,并求相应的特征向量矩阵 +`5`. 计算矩阵`Z` + +## sklearn中的多维缩放 + +在降维时,`MDS`的构造函数中有一个常用的参数可以设置: + +- `n_components`:即我们进行`MDS`降维时降到的维数。在降维时需要输入这个参数。 + + +`MDS`类中的`fit_transform`函数用于训练模型并进行降维,返回降维后的数据,`fit_transform`函数有一个向量输入: + +- `X`:大小为**[样本数量,特征数量]**的`ndarray`,存放需降维的样本 + +`MDS`的使用代码如下: + +```python +from sklearn.manifold import MDS +mds = MDS(2) +Z = mds.fit_transform(X) +``` + +如果想动手实现多维缩放算法,并掌握如何使用`sklearn`来解决实际问题,可以尝试进入链接进行实战:https://www.educoder.net/shixuns/vry7x6op/challenges \ No newline at end of file diff --git a/Part2/Chapter5/5.3 无监督学习/分久必合---AGNES.md b/Part2/Chapter5/5.3 无监督学习/分久必合---AGNES.md new file mode 100644 index 0000000..11f48ea --- /dev/null +++ b/Part2/Chapter5/5.3 无监督学习/分久必合---AGNES.md @@ -0,0 +1,105 @@ +# 5.3.3 分久必合---AGNES + +`AGNES`算法是一种聚类算法,最初将每个对象作为一个簇,然后这些簇根据某些距离准则被一步步地合并。两个簇间的相似度有多种不同的计算方法。聚类的合并过程反复进行直到所有的对象最终满足簇数目。所以理解`AGNES`算法前需要先理解一些距离准则。 + +## 为什么需要距离 + +`AGNES`算法是一种自底向上聚合的层次聚类算法,它先会将数据集中的每个样本看作一个初始簇,然后在算法运行的每一步中找出距离最近的两个簇进行合并,直至达到预设的簇的数量。所以`AGNES`算法需要不断的计算簇之间的距离,这也符合聚类的核心思想(物以类聚,人以群分),因此怎样度量两个簇之间的距离成为了关键。 + +## 距离的计算 + +衡量两个簇之间的距离通常分为最小距离、最大距离和平均距离。在`AGNES`算法中可根据具体业务选择其中一种距离作为度量标准。 + +### 最小距离 + +最小距离描述的是两个簇之间距离最近的两个样本所对应的距离。例如下图中圆圈和菱形分别代表两个簇,两个簇之间离得最近的样本的**欧式距离**为`3.3`,则最小距离为`3.3`。 + +![](9.jpg) + +假设给定簇$$C_i$$与$$C_j$$,则最小距离为:$$d_{min}=min_{x\in i,z\in j}dist(x,z)$$ + +### 最大距离 + +最大距离描述的是两个簇之间距离最远的两个样本所对应的距离。例如下图中圆圈和菱形分别代表两个簇,两个簇之间离得最远的样本的**欧式距离**为`23.3`,则最大距离为`23.3`。 + +![](10.jpg) + +假设给定簇$$C_i$$与$$C_j$$,则最大距离为:$$d_{min}=max_{x\in i,z\in j}dist(x,z)$$ + +### 平均距离 + +平均距离描述的是两个簇之间样本的平均距离。例如下图中圆圈和菱形分别代表两个簇,计算两个簇之间的所有样本之间的欧式距离并求其平均值。 + +![](11.jpg) + +假设给定簇$$C_i$$与$$C_j$$,$$|C_i|,|C_j|$$分别表示簇 i 与簇 j 中样本的数量,则平均距离为:$$d_{min}=\frac{1}{|C_i||C_j|}\sum_{x\in i}\sum_{z\in j}dist(x, z)$$ + +## AGNES算法流程 + +`AGNES`算法是一种自底向上聚合的层次聚类算法,它先会将数据集中的每个样本看作一个**初始簇**,然后在算法运行的每一步中找出距离最近的两个簇进行合并,直至达到预设的簇的数量。 + +举个例子,现在先要将西瓜数据聚成两类,数据如下表所示: + +| 编号 | 体积 | 重量 | +| :-: | :-: | :-:| +| 1 | 1.2 | 2.3 | +| 2 | 3.6 | 7.1 | +| 3 | 1.1 | 2.2 | +| 4 | 3.5 | 6.9 | +| 5 | 1.5 | 2.5 | + +一开始,每个样本都看成是一个簇(`1`号样本看成是`1`号簇,`2`号样本看成是`2`号簇,...,`5`号样本看成是`5`号簇),假设簇的集合为`C=[[1], [2], [3], [4], [5]]` 。 + +假设使用簇间最小距离来度量两个簇之间的远近,从表中可以看出 `1`号簇与`3`号簇的簇间最小距离最小。因此需要将`1`号簇和`3`号簇合并,那么此时簇的集合`C=[[1, 3], [2], [4], [5]]`。 + +然后继续看这`4`个簇中哪两个簇之间的最小距离最小,我们发现 `2`号簇与`4`号簇的最小距离最小,因此我们要进行合并,合并之后`C=[[1, 3], [2, 4], [5]]`。 + +然后继续看这`3`个簇中哪两个簇之间的最小距离最小,我们发现 `5`号簇与`[1, 3]`簇的最小距离最小,因此我们要进行合并,合并之后`C=[[1, 3, 5], [2, 4]]`。 + +这个时候`C`中只有两个簇了,达到了我们的预期目标(想要聚成两类),所以算法停止。算法停止后会发现,我们已经将`5`个西瓜,聚成了两类,一类是小西瓜,另一类是大西瓜。 + +如果将整个聚类过程中的合并,与合并的次序可视化出来,就能看出为什么说`AGNES`是自底向上的层次聚类算法了。 + +![](12.jpg) + +所以`AGNES`伪代码如下: + +```python +#假设数据集为D,想要聚成的簇的数量为k +def AGNES(D, k): + #C为聚类结果 + C = [] + #将每个样本看成一个簇 + for d in D: + C.append(d) + + #C中簇的数量 + q=len(C) + while q > k: + 寻找距离最小的两个簇a和b + 将a和b合并,并修改C + q = len(C) + return C +``` + +## sklearn中的AGNES + +`AgglomerativeClustering`是`sklearn`中`AGNES`算法的实现,`AgglomerativeClustering`的构造函数中有两个常用的参数可以设置: + +- `n_clusters`:将数据聚成`n_clusters`个类 +- `linkage`:设置`AGNES`聚类时使用最小簇间距离、最大簇间距离还是平均距离。传入`ward`表示最小簇间距离,传入`complete`表示最大簇间距离,传入`average`表示平均距离 + +`AgglomerativeClustering`类中的`fit_predict`函数用于训练模型并获取聚类结果,`fit_predict`函数有一个向量输入: + +- `X`:数据集,形状为**[样本数量,特征数量]**的`ndarray` + +`AgglomerativeClustering`的使用代码如下: +```python +from sklearn.cluster import AgglomerativeClustering +agnes = AgglomerativeClustering(k=5) + +# 注意:x为ndarray +result = agnes.fit_predict(x) +``` + +如果想动手实现`AGNES`算法,并掌握如何使用`sklearn`来解决实际问题,可以尝试进入链接进行实战:https://www.educoder.net/shixuns/qy9gozt8/challenges \ No newline at end of file diff --git a/Part2/Chapter5/5.3 无监督学习/最重要的才是我想要的---PCA.md b/Part2/Chapter5/5.3 无监督学习/最重要的才是我想要的---PCA.md new file mode 100644 index 0000000..41c226b --- /dev/null +++ b/Part2/Chapter5/5.3 无监督学习/最重要的才是我想要的---PCA.md @@ -0,0 +1,181 @@ +# 5.3.4 最重要的才是我想要的---PCA + +## 维数灾难 + +在机器学习中,我们不仅需要学习怎样进行分类、回归或者聚类,我们更要学习怎样对数据进行更好的处理,使得我们的数据能够更好的为我们的机器学习算法服务。而**降维**就是数据处理中的一环。 + +说到降维,那首先就要提到一个概念:**维数灾难**。**维数灾难**其实很好理解,举个例子。 + +我们现在玩个游戏,我告诉你一些信息,你猜一猜我所描述的是什么。 + +- 我:这个能在地球上才有,而且是犬科动物。 +- 您:...... + +如果您现在是一个动物的分类器,我相信您仅仅靠这两个特征(地球上才有,犬科动物)不大可能能够预测出我所说的是什么。也就是说,不管你用什么算法去分类,都很有可能发生**欠拟合**的现象。 + +- 我:这个是犬科动物,喜欢啃骨头,长得像狼, 比较二。 +- 您:哈士奇! +- 我:猜的挺准。 + +当我给出的信息比较合适(这次有`4`个特征),您可能能够猜到所提供的特征数据所描述的是哈士奇。这个时候我们的分类算法能正常工作。 + +- 我:这个能在地球上才有,是犬科动物,有毛,有爪子,体型大,耳尖呈圆形,尾巴喜欢上翘,长得像狼,喜欢啃骨头,有时比较二但挺忠诚。 +- 您:哈士奇! +- 我:不,我说的是阿拉斯加。 +- 您:...... + +这次我提供的信息比上面个两次都多(这次有`10`个特征),但是您可能将阿拉斯加误判成哈士奇。因为您可能看到长得像狼和比较二就认为是哈士奇了,也就是发生了**过拟合**的现象。这也说明了不是说数据的特征数量越多,我们的机器学习算法的效果就越强。当数据的特征数量变大时,和可能会造成机器学习算法的模型变得非常复杂,**从而导致过拟合**。而且如果我所提供的特征数量越多,比如有`10000`个特征,那么算法的训练过程中的时间成本会非常大。 + +所以**维数灾难**通常是指对于已知样本数目,存在一个特征数目的最大值,当实际使用的特征数目超过这个最大值时,机器学习算法的性能不是得到改善,而是退化。 + +## 降维 + +既然维数太大可能引发维数灾难,那么如果能有算法能够自动的帮我们把重要性比较高的特征维度保留下来,把其他的维度过滤掉就好了。那这个过程我们称之为**降维**。 + +从维数灾难的概念出发,我们就能知道**降维的作用**了。 + +- **降低机器学习算法的时间复杂度** +- **节省了提取不必要特征的开销** +- **缓解因为维数灾难所造成的过拟合现象** + + +## PCA与降维 + +降维的方法有很多,而最为常用的就是**`PCA`**(主成分分析)。`PCA`是将数据从原来的坐标系转换到新的坐标系,新的坐标系的选择是由数据本身决定的。第一个新坐标轴选择的是原始数据中**方差最大的方向**,第二个新坐标轴的选择和第一个坐标轴**正交**且**方差最大的方向**。然后该过程一直重复,重复次数为原始数据中的特征数量。**最后会发现大部分方差都包含在最前面几个新坐标轴中,因此可以忽略剩下的坐标轴,从而达到降维的目的。** + +## PCA的算法流程 + +`PCA`在降维时,需要指定将维度降至多少维,假设降至`k`维,则`PCA`的算法流程如下: + +1. `demean` +2. 计算数据的协方差矩阵 +3. 计算协方差矩阵的特征值与特征向量 +4. 按照特征值,将特征向量从大到小进行排序 +5. 选取前`k`个特征向量作为转换矩阵 +6. `demean`后的数据与转换矩阵做矩阵乘法获得降维后的数据 + +其中**`demean`,协方差矩阵,特征值与特征向量**的相关知识如下: + +### demean + +`demean`又称为零均值化,意思是将数据中每个维度上的均值变成`0`。那为什么要这样做呢?`PCA`实质上是找方差最大的方向,而方差的公式如下(其中$$\mu$$为均值): + +$$ +Var(x)=\frac{1}{n}\sum_{i=1}^n(x-\mu)^2 +$$ + +如果将均值变成`0`,那么方差计算起来就更加方便,如下: + +$$ +Var(x)=\frac{1}{n}\sum_{i=1}^n(x)^2 +$$ + +在`numpy`中想要`demean`很简单,代码如下: + +```python +import numpy as np + +#计算样本各个维度的均值 +u = np.mean(data, axis=0) +#demean +after_demean = data - u +``` + +### 协方差矩阵 + +**协方差描述的是两个特征之间的相关性,当协方差为正时,两个特征呈正相关关系(同增同减);当协方差为负时,两个特征呈负相关关系(一增一减);当协方差为0时,两个特征之间没有任何相关关系。** + +协方差的数学定义如下(假设样本有`x`和`y`两种特征,而`X`就是包含所有样本的`x`特征的集合,`Y`就是包含所有样本的`y`特征的集合): + +$$ +conv(X, Y) = \frac{\sum_{i=1}^n(x_i-\mu_x)\sum_{i=1}^n(y_i-\mu_y)}{n-1} +$$ + +如果在算协方差之前做了`demean`操作,那么公式则为: + +$$ +conv(X, Y) = \frac{\sum_{i=1}^nx_i\sum_{i=1}^ny_i}{n-1} +$$ + +假设样本只有`X`和`Y`这两个特征,现在把`X`与`X`,`X`与`Y`,`Y`与`X`,`Y`与`Y`的协方差组成矩阵,那么就构成了协方差矩阵。而协方差矩阵反应的就是特征与特征之间的相关关系。 + +| | X | Y | +| ------------ | ------------ | ------------ | +| X |conv(X,X) | conv(X,Y) | +| Y |conv(Y,X) |conv(Y,Y) | + +`NumPy`提供了计算协方差矩阵的函数`cov`,示例代码如下: + +```python +import numpy as np + +# 计算after_demean的协方差矩阵 +# after_demean的行数为样本个数,列数为特征个数 +# 由于cov函数的输入希望是行代表特征,列代表数据的矩阵,所以要转置 +cov = np.cov(after_demean.T) +``` + +### 特征值与特征向量 + +特征值与特征向量的数学定义:**如果向量`v`与矩阵`A`满足`Av=λv`,则称向量`v`是矩阵A的一个特征向量,`λ`是相应的特征值。** + +因为协方差矩阵为方阵,所以我们可以计算协方差矩阵的特征向量和特征值。其实这里的特征值从某种意义上来说体现了方差的大小,特征值越大方差就越大。而特征值所对应的特征向量就代表将原始数据进行坐标轴转换之后的数据。 + +`numpy`为我们提供了计算特征值与特征向量的接口`eig`,示例代码如下: + +```python +import numpy as np + +#eig函数为计算特征值与特征向量的函数 +#cov为矩阵,value为特征值,vector为特征向量 +value, vector = np.linalg.eig(cov) +``` + +因此,`PCA`算法伪代码如下: + +```python +#假设数据集为D,PCA后的特征数量为k +def pca(D, k): + after_demean=demean(D) + 计算after_demean的协方差矩阵cov + value, vector = eig(cov) + 根据特征值value将特征向量vector降序排序 + 筛选出前k个特征向量组成映射矩阵P + after_demean和P做矩阵乘法得到result + return result +``` + +## sklearn中的PCA + +`PCA`的构造函数中有一个常用的参数可以设置: + +- `n_components `:表示想要将数据降维至`n_components`个维度 + +`PCA`类中有三个常用的函数分别为:`fit`函数用于训练`PCA`模型;`transform`函数用于将数据转换成降维后的数据,当模型训练好后,对于新输入的数据,也可以用`transform`方法来降维;`fit_transform`函数用于使用数据训练`PCA`模型,同时返回降维后的数据。 + +其中`fit`函数中的参数: + +- `X`:大小为 `[样本数量,特征数量]` 的`ndarray`,存放训练样本 + +`transform`函数中的参数: + +- `X`:大小为 `[样本数量,特征数量]` 的`ndarray`,存放训练样本 + +`fit_transform`函数中的参数: + +- `X`:大小为 `[样本数量,特征数量]` 的`ndarray`,存放训练样本 + + +`PCA`的使用代码如下: + +```python +from sklearn.decomposition import PCA + +#构造一个将维度降至11维的PCA对象 +pca = PCA(n_components=11) +#对数据X进行降维,并将降维后的数据保存至newX +newX = pca.fit_transform(X) + +``` + +如果想动手实现`PCA`算法,并掌握如何使用`sklearn`来解决实际问题,可以尝试进入链接进行实战:https://www.educoder.net/shixuns/rbnaxywe/challenges \ No newline at end of file diff --git a/Part2/Chapter5/5.3 无监督学习/物以类聚人以群分---k Means.md b/Part2/Chapter5/5.3 无监督学习/物以类聚人以群分---k Means.md new file mode 100644 index 0000000..c33ca92 --- /dev/null +++ b/Part2/Chapter5/5.3 无监督学习/物以类聚人以群分---k Means.md @@ -0,0 +1,49 @@ +# 5.3.1 物以类聚人以群分---k Means + +**k Means**是属于机器学习里面的非监督学习,通常是大家接触到的第一个聚类算法,其原理非常简单,是一种典型的基于**距离**的聚类算法。**距离**指的是每个样本到质心的距离。那么,这里所说的质心是什么呢? + +其实,质心指的是样本每个特征的均值所构成的一个坐标。举个例子:假如有两个数据 $$(1,1)$$ 和$$(2,2)$$ 则这两个样本的质心为 $$(1.5,1.5)$$。 + +同样的,如果一份数据有 $$m$$ 个样本,每个样本有 $$n$$ 个特征,用 $$x_i^j$$ 来表示第 $$j$$ 个样本的第 $$i$$ 个特征,则它们的质心为:$$Cmass=(\frac{\sum_{j=1}^mx_1^j}{m},\frac{\sum_{j=1}^mx_2^j}{m},...,\frac{\sum_{j=1}^mx_n^j}{m})$$。 + +知道什么是质心后,就可以看看**k Means算法**的流程了。 + +## k Means算法流程 + +使用**k Means**来聚类时需要首先定义参数**k**,**k**的意思是我想将数据聚成几个类别。假设**k=3**,就是将数据划分成**3**个类别。接下来就可以开始**k Means**算法的流程了,流程如下: + +`1.`随机初始**k**个样本,作为类别中心。 +`2.`对每个样本将其标记为距离类别中心最近的类别。 +`3.`将每个类别的质心更新为新的类别中心。 +`4.`重复步骤`2`、`3`,直到类别中心的变化小于阈值。 + +过程示意图如下(其中 X 表示类别的中心,数据点的颜色代表不同的类别,总共迭代`12`次,下图为部分迭代的结果): + +![](1.jpg) + +![](2.jpg) + +![](3.jpg) + +![](4.jpg) + +![](5.jpg) + +## sklearn中的k Means + +`KMeans`是`sklearn`中k Means算法的实现,`KMeans`的构造函数中有两个常用的参数可以设置: + +- `n_clusters`:将结果聚成`k`个类。 +- `random_state`:设置初始质心位置时,随机种子数值。 + +和`sklearn`中其他聚类器一样,`KMeans`不允许对新的数据进行预测,`KMeans`类中的`fit_predict`函数用于训练模型并获取聚类结果,`fit_predict`函数有一个向量输入: +- `X`:大小为**[样本数量,特征数量]**的`ndarray`,存放训练样本。 + +`KMeans`的使用代码如下: +```python +from sklearn.cluster import KMeans +km = KMeans(n_clusters=5) +result = km.fit_predict(x) +``` + +如果想动手实现k Means算法,并掌握如何使用 sklearn 来解决实际问题,可以尝试进入链接进行实战:https://www.educoder.net/shixuns/k6fp4saq/challenges \ No newline at end of file diff --git a/Part2/Chapter5/5.3 无监督学习/用密度来聚类---DBSCAN.md b/Part2/Chapter5/5.3 无监督学习/用密度来聚类---DBSCAN.md new file mode 100644 index 0000000..ad87661 --- /dev/null +++ b/Part2/Chapter5/5.3 无监督学习/用密度来聚类---DBSCAN.md @@ -0,0 +1,74 @@ +# 5.3.2 用密度来聚类---DBSCAN + +## 基本概念 + +在`DBSCAN`算法中,有两个基本的领域参数,分别为`eps`邻域和`Minpts`。 + +`eps`邻域表示的是在数据集`D`中与样本点$$x_i$$的距离不大于`eps`的样本。 + +样本点$$x_i$$的`eps`邻域如图所示: + +![](6.jpg) + +在图中,样本点$$x$$不在样本点$$x_i$$的`eps`邻域内。$$x_i$$的密度由$$x_i$$的`eps`邻域内的点的数量来估计。`Minpts`表示的是在样本点$$x_i$$的`eps`邻域内的最少样本点数目。基于邻域参数 **eps** 和 **Minpts**,在DBSCAN算法中将数据点分为一下三类: + +1.**核心点**:若样本$$x_i$$的`eps`邻域内至少包含了 **Minpts** 个样本,则称样本点$$x_i$$为核心点。 +2.**边界点**:若样本$$x_i$$的`eps`邻域内包含的样本数目小于 **Minpts**,但是它在其它核心点的邻域内,则称样本点$$x_i$$为边界点。 +3.**噪音点**:指的是既不是核心点,又不是边界点的点。 + +![](7.jpg) + +如上图,设置 **Minpts** 的值为`8`,对应的样本点$$x_1$$的`eps`邻域内包含了`9`个点,大于 **Minpts**,则样本点$$x_1$$为核心点。样本点$$x_2$$在样本点$$x_1$$的`eps`邻域内,且样本点$$x_2$$的`eps`邻域内只包含了`2`个样本点,小于 **Minpts**,则样本点$$x_2$$为边界点。样本点$$x_3$$为噪音点。 + +在`DBSCAN`算法中,还定义了如下的一些概念: + +1.**直接密度可达**:若样本点$$x_j$$ 在核心点$$x_i$$的`eps`邻域内,则称样本点$$x_j$$从样本点$$x_i$$直接密度可达。 +2.**密度可达**:若在样本点$$x_1$$和样本点$$x_n$$之间存在一序列 + +$$ +x_2,..,x_{n-1} +$$ + +且$$x_{i+1}$$从$$x_i$$直接密度可达,则称$$x_n$$从$$x_1$$密度可达。 +3.**密度相连**:对于样本点$$x_i$$和样本点$$x_j$$,若存在样本点$$x_k$$,使得$$x_i$$和$$x_j$$都从$$x_k$$密度可达,则称$$x_i$$和$$x_j$$密度相连。 + +![](8.jpg) + +如上图,设置 **Minpts** 的值为`8`,则样本点$$x_1$$和$$x_2$$都是核心点,样本点$$x_3$$为边界点。样本点$$x_2$$在核心点$$x_1$$`eps`邻域内,则样本点$$x_2$$从样本点$$x_1$$直接密度可达。样本点$$x_3$$在在核心点$$x_2$$`eps`邻域内,则样本点$$x_3$$从样本点$$x_2$$直接密度可达。样本点$$x_1$$和$$x_3$$直接存在样本点$$x_2$$,且样本点$$x_2$$从样本点$$x_1$$直接密度可达,则样本点$$x_3$$从样本点$$x_1$$密度可达。 + +## DBSCAN算法原理 + +基于密度的聚类算法通过寻找被低密度区域分离的高密度区域,并将高密度区域作为一个簇。在`DBSCAN`算法中,聚类簇定义为:由密度可达关系导出的最大的密度相连样本集合。 + +## DBSCAN算法流程 + +在`DBSCAN`算法中,由核心对象出发,找到与该核心对象密度可达的所有样本形成一个聚类簇。`DBSCAN`算法流程如下: + +`1`. 如果一个点的eps邻域包含多于MinPits个对象,则创建一个集合P作为核心对象的新簇。 +`2`. 寻找核心对象的直接密度可达的对象,并合并为一个新的簇。 +`3`. 直到没有点可以更新簇时算法结束。 + +## DBSCAN算法优点 + +根据`DBSCAN`算法原理及流程可以发现,`DBSCAN`算法在聚类时不需要自己设定簇的个数,而且能够发现任意形状的簇。还有一个优点就是,`DBSCAN`算法对噪音点不敏感,所以`DBSCAN`算法也常用来找寻异常数据。 + +## sklearn中的DBSCAN + +`DBSCAN`的构造函数中有两个常用的参数可以设置: + +- `eps`:`eps` 邻域半径大小 +- `min_samples`:即 Minpts,`eps`邻域内样本最少数目 + + +和`sklearn`中其他聚类器一样,`DBSCAN`不允许对新的数据进行预测,`DBSCAN`类中的`fit_predict`函数用于训练模型并获取聚类结果,`fit_predict`函数有一个向量输入: +- `X`:大小为 **[样本数量,特征数量]** 的`ndarray`,存放训练样本 + +`DBSCAN`的使用代码如下: + +```python +from sklearn.cluster import DBSCAN +dbscan = DBSCAN(eps=0.5,min_samples =10) +result = dbscan.fit_predict(x) +``` + +如果想动手实现`DBSCAN`算法,并掌握如何使用`sklearn`来解决实际问题,可以尝试进入链接进行实战:https://www.educoder.net/shixuns/jfzo8xcu/challenges \ No newline at end of file diff --git a/Part2/Chapter5/简介.md b/Part2/Chapter5/简介.md index be833e3..3bb5de1 100644 --- a/Part2/Chapter5/简介.md +++ b/Part2/Chapter5/简介.md @@ -1,2 +1,4 @@ -# 第一章:Python基础知识 +# 第五章:机器学习 + +近年来,全球新一代信息技术创新浪潮迭起。作为全球信息领域产业竞争的新一轮焦点,人工智能的发展也迎来了第三次浪潮,它正在推动工业发展进入新的阶段,掀起第四次工业革命的序幕。而作为人工智能的重要组成部分,机器学习也成了炙手可热的概念。本章将向您介绍机器学习的基础知识,为后面的学习打好基础。