机器学习
- 1、K-近邻算法KNN
-
- 1.1、举例:电影类型分析
- 1.2、KNN算法流程
- 1.3、Scikit-learn工具
- 1.4、K-近邻算法API
- 1.5、距离度量
-
- [1.5.1、欧式距离Euclidean Distance🔥](#1.5.1、欧式距离Euclidean Distance🔥)
- [1.5.2、曼哈顿距离Manhattan Distance🔥](#1.5.2、曼哈顿距离Manhattan Distance🔥)
- [1.5.3、切比雪夫距离Chebyshev Distance](#1.5.3、切比雪夫距离Chebyshev Distance)
- [1.5.4、闵可夫斯基距离Minkowski Distance](#1.5.4、闵可夫斯基距离Minkowski Distance)
- [1.5.5、标准化欧氏距离Standardized EuclideanDistance](#1.5.5、标准化欧氏距离Standardized EuclideanDistance)
- [1.5.6、余弦距离Cosine Distance](#1.5.6、余弦距离Cosine Distance)
- 1.6、K值的选择
- 1.7、kd树(不重要)
- 1.8、综合案例
- 1.9、特征工程-特征预处理
- 1.10、鸢尾花总结🔥
- 1.11、KNN算法总结
- 1.12、超参数选择方法
- 2、线性回归
-
- 2.0、线性回归的应用场景
- 2.1、线性回归API
- 2.2、线性回归的损失和优化
- 2.3、欠拟合和过拟合
- 2.4、正则化线性模型
-
- 2.4.0、范数
- [2.4.1、岭回归 Ridge Regression(L2正则化)](#2.4.1、岭回归 Ridge Regression(L2正则化))
- [2.4.2、套索回归Lasso Regression(L1正则化)](#2.4.2、套索回归Lasso Regression(L1正则化))
- 2.4.3、岭回归API
- 2.5、回归模型评估方法
- 2.6、模型的保存和加载
- 2.7、总结
- 3、逻辑回归
- 4、决策树算法
1、K-近邻算法KNN
大白话:根据你的邻居来判断你的类别。
- 比如你的邻居都在海淀区,那么推出你的地理位置也是在海淀区。

-
定义:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。
-
两个样本的距离可以通过如下公式计算,又叫欧式距离:


1.1、举例:电影类型分析
假设我们现在有几部电影

其中9号电影不知道类别,如何去预测?我们可以利用K近邻算法的思想,分别计算每个电影和被预测电影的距离,然后求解。把前8号电影和第9号电影的距离分别求出来。

求出来之后将距离由小到大依次排序,假如我们只根据距离最近的一个样本进行估计,那么最近的是18.55喜剧片,则把唐人街探案也归为喜剧片。K=5说明根据距离最近的5个样本进行估计,发现是2个爱情片、3个喜剧片,则归为喜剧片。

1.2、KNN算法流程
KNN算法解决的问题:分类问题、回归问题
分类流程:
-
计算已知类别数据集中的点与当前点之间的距离
-
按距离递增次序排序
-
选取与当前点距离最小的k个点
-
统计前k个点所在的类别出现的频率
-
返回前k个点出现频率最高的类别作为当前点的预测分类
回归的标签是连续的,分类的标签是离散的
回归流程:
- 计算已知类别数据集中的点与当前点之间的距离
- 按距离递增次序排序
- 选取与当前点距离最小的k个点
- 把这k个点的目标值计算其平均值
- 将计算的平均值作为预测的值
1.3、Scikit-learn工具

塞肯learn~
- 官方中文文档
- 现在稳定的版本为:
1.6.0
- 我这里使用Anaconda创建一个虚拟环境:
bash
conda create -n KuangStudy_Ai
- 激活虚拟环境
bash
conda activate KuangStudy_Ai
- 安装Scikt-learn工具:使用 conda 和 pip 安装均可,这里推荐 conda 安装。
python
conda install scikit-learn
- 查看是否安装成功
python
import sklearn
scikit-learn 需要安装 NumPy 和 SciPy~
bashconda install numpy conda install scipy
- 官网的东西不错,有时间请详阅!

1.4、K-近邻算法API

其中官方对于每个API都有个示例:

同时我们也可以在API里面搜到用法:

python
sklearn.neighbors.KNeighborsClassifier(n_neighbors=5,algorithm='auto')
- n_neighbors:int,可选(默认= 5),k_neighbors查询默认使用的邻居数(也就是上面案例的K值)
- algorithm:{'auto','ball_tree','kd_tree','brute'}
- 快速k近邻搜索算法,默认参数为auto
- brute是蛮力搜索,也就是线性扫描,当训练集很大时,计算非常耗时
- kd_tree,构造kd树存储数据以便对其进行快速检索的树形数据结构,kd树也就是数据结构中的二叉树。以中值切分构造的树,每个结点是一个超矩形,在维数小于20时效率高。
- ball tree是为了克服kd树高维失效而发明的,其构造过程是以质心C和半径r分割样本空间,每个节点是一个超球体。
python
# 导入模块
from sklearn.neighbors import KNeighborsClassifier
# 1.构造数据集
# x为4行1列的列向量
x = [[1], [2], [10], [20]]
# y为1行4列的行向量
# 代表1、2都是0类别,10、20都是1类别
y = [0, 0, 1, 1]
# 2、模型训练
# 实例化一个估计器对象
estimator = KNeighborsClassifier(n_neighbors=1)
# 使用fit方法进行训练(x就是特征值,y就是目标值)
estimator.fit(x, y)
# 3、模型预测
# 预测 1 是什么类别
ret = estimator.predict([[1]])
# 1的类别是 0
print(ret)

现在我们提出两个疑问:
-
我们的这个距离是只能采用欧式距离吗?
-
参数
n_neighbors
这个邻居选几个最合适呢?-
K值过小,用较小邻域中的训练实例进行预测,容易受到异常点的影响,K值的减小就意味着整体模型变得复杂,容易发生过拟合
-
K值过大:用较大邻域中的训练实例进行预测,受到样本均衡的问题,且K值的增大就意味着整体的模型变得简单,容易发生欠拟合
-
K=N(N为训练样本个数),无论输入实例是什么,只会按训练集中最多的类别进行预测,受到样本均衡的影响
-
需要一些方法来寻找这个最合适的K值:交叉验证、网格搜索
-
1.5、距离度量
1.5.1、欧式距离Euclidean Distance🔥
欧氏距离是最容易直观理解的距离度量方法,我们小学、初中和高中接触到的两个点在空间中的距离一般都是指欧氏距离。

1.5.2、曼哈顿距离Manhattan Distance🔥
在曼哈顿街区要从一个十字路口开车到另一个十字路口,驾驶距离显然不是两点间的直线距离。这个实际驾驶距离就是"曼哈顿距离"。曼哈顿距离也称为"城市街区距离"(City Block distance)。

1.5.3、切比雪夫距离Chebyshev Distance
国际象棋中,国王可以直行、横行、斜行,所以国王走一步可以移动到相邻8个方格中的任意一个。国王从格子(x1,y1)走到格子(x2,y2)最少需要多少步?这个距离就叫切比雪夫距离。

1.5.4、闵可夫斯基距离Minkowski Distance
闵可夫斯基距离 (Minkowski Distance),也被称为 闵氏距离。它不仅仅是一种距离,而是将多个距离公式(曼哈顿距离、欧式距离、切比雪夫距离)总结成为的一个公式。
首先假设又两个n维变量 A(x11,x12,x13,...,x1n) 与 B(x21,x22,x23,...,x2n),对于这两个 n 维变量,则有闵氏距离公式为:
d = ∑ k = 1 n ∣ x 1 k − x 2 k p ∣ p d = \sqrt[p]{\sum_{k=1}^{n}|x_{1k}-x_{2k}}|^p d=pk=1∑n∣x1k−x2k ∣p
闵氏距离主要和它的参数p有关,p值不同,公式也将不同。

- p=1时,闵氏距离 为 曼哈顿距离

- p=2时,闵氏距离 为 欧式距离

- p=无穷大时,闵氏距离 为 切比雪夫距离

闵氏距离的缺点:
- 将各个分量的量纲(scale),也就是"单位"相同的看待了;
例如:二维样本(身高[单位:cm],体重[单位:kg]),现有三个样本:a(180,50),b(190,50),c(180,60)。
a与b的闵氏距离(无论是曼哈顿距离、欧氏距离或切比雪夫距离)等于a与c的闵氏距离。但实际上身高的 10cm 并不能和体重的 10kg 划等号。
- 未考虑各个分量的分布(期望,方差等)可能是不同的。
1.5.5、标准化欧氏距离Standardized EuclideanDistance
标准化欧氏距离是针对欧氏距离的缺点而作的一种改进。思路:既然数据各维分量的分布不一样,那先将各个分量都"标准化"到均值、方差相等。标准化欧式距离:
d 12 = ∑ k = 1 n ( x 1 k − x 2 k S k ) 2 p d_{12} = \sqrt[p]{\sum_{k=1}^{n}(\frac{x_{1k}-x_{2k}}{S_k})^2} d12=pk=1∑n(Skx1k−x2k)2
- Sk表示各个维度的标准差
1.5.6、余弦距离Cosine Distance
几何中,夹角余弦可用来衡量两个向量方向的差异;机器学习中,借用这一概念来衡量样本向量之间的差异。
维空间中向量A(x1,y1)与向量B(x2,y2)的夹角余弦公式:
c o s θ = x 1 x 2 + y 1 y 2 x 1 2 + y 1 2 x 2 2 + y 2 2 cosθ = \frac{x_1x_2+y_1y_2}{\sqrt{x_1^2+y_1^2}\sqrt{x_2^2+y_2^2}} cosθ=x12+y12 x22+y22 x1x2+y1y2
- 两个n维样本点a(x11,x12,...,x1n)和b(x21,x22,...,x2n)的夹角余弦为:
c o s θ = a ⋅ b ∣ a ∣ ∣ b ∣ cosθ = \frac{a·b}{|a||b|} cosθ=∣a∣∣b∣a⋅b
- 夹角余弦取值范围为[-1,1]。
- 余弦越大表示两个向量的夹角越小,余弦越小表示两向量的夹角越大。
- 当两个向量的方向重合时余弦取最大值1,当两个向量的方向完全相反余弦取最小值-1。
1.6、K值的选择
上面的案例中,K值的选择该如何选呢?
- K值的减小就意味着整体模型变得复杂,容易受到异常点的影响,容易发生过拟合
- K值的增大就意味着整体的模型变得简单,容易受到样本均衡的问题,容易欠拟合
在实际应用中,K值一般取一个比较小的数值,例如采用交叉验证法(简单来说,就是把训练数据在分成两组:训练集和验证集)来选择最优的K值。
来看两个误差:
近似误差:
- 对现有训练集的训练误差,关注训练集
- 如果近似误差过小可能会出现过拟合的现象,对现有的训练集能有很好的预测,但是对未知的测试样本将会出现较大偏差的预测。
- 模型本身不是最接近最佳模型。
估计误差:
- 可以理解为对测试集的测试误差,关注测试集
- 估计误差小说明对未知数据的预测能力好
- 模型本身最接近最佳模型
1.7、kd树(不重要)
- 实现k近邻算法时,主要考虑的问题是如何对训练数据进行快速k近邻搜索。这在特征空间的维数大及训练数据容量大时尤其必要。
- **k近邻法最简单的实现是线性扫描(穷举搜索),即要计算输入实例与每一个训练实例的距离。计算并存储好以后,再查找K近邻。**当训练集很大时,计算非常耗时。
- 为了提高kNN搜索的效率,可以考虑使用特殊的结构存储训练数据,以减小计算距离的次数。
根据KNN 每次需要预测一个点时,我们都需要计算训练数据集里每个点到这个点的距离,然后选出距离最近的k个点进行投票。当数据集很大时,这个计算成本非常高。
- kd树 :为了避免每次都重新计算一遍距离,算法会把距离信息保存在一棵树里,这样在计算之前从树里查询距离信息,尽量避免重新计算。其基本原理是,如果A和B距离很远,B和C距离很近,那么A和C的距离也很远。有了这个信息,就可以在合适的时候跳过距离远的点。
对于一维空间来说,kd树就是一种平衡二叉树。

对于二维空间来说,就是划分。假设有6个二维数据点{(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)},数据点位于二维空间内,如下图所示。为了能有效的找到最近邻,k-d树采用分而治之的思想,即将整个空间划分为几个小部分。

k-d树是一种空间划分树,说白了,就是把整个空间划分为特定的几个部分,然后在特定空间的部分内进行相关搜索操作。

构造kd树相当于不断地用垂直于坐标轴的超平面将K维空间切分,构成一系列的K维超矩形区域 。kd树的每个结点对应于一个k维超矩形区域。利用kd树可以省去对大部分数据点的搜索,从而减少搜索的计算量。

1.7.1、kd树的构造方法
- 构造根结点:在特征空间中选择一个维度,选择所有实例在该维度上的中位数作为切分点,如果中位数所在位置不存在样本,则随机选择中位数两边的样本中的一个,使用通过该样本垂直于该维度的线,将特征空间分割为两个部分。
- 生成子结点 :在分割出来的两个子空间中选择在当前维度小于切分点的子空间,并选择另一个维度,按照步骤1的方法确定切分点,将该子区域再次分割为两个部分,并将该切分点对应的实例特征存储在左子结点。如果选择当前维度大于切分点的子空间,则把新的切分点对应实例特征存储在右子结点。
- 重复步骤2,直到特征空间中的子空间中不存在实例点。
- 选择向量的哪一维进行划分
- 可以是随机选择某一维或按顺序选择,但是更好的方法应该是在数据比较分散的那一维进行划分(分散的程度可以根据方差来衡量)
- 如何划分数据
- 好的划分方法可以使构建的树比较平衡,可以每次选择中位数来进行划分
1.7.2、案例分析
给定一个二维空间数据集:T={(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)},构造一个平衡kd树。

第一维度x轴:2 5 9 4 8 7
第二维度y轴:3 4 6 7 1 2
在维度选择上,哪个维度的方差比较大,就用哪个维度,我们可以使用numpy来计算方差:
python
import numpy as np
X = np.array([2, 5, 9, 4, 8, 7])
Y = np.array([3, 4, 6, 7, 1, 2])
# 计算方差
A_variance = np.var(X)
B_variance = np.var(Y)
# 5.8
print("总体方差X:", X_variance)
# 4.4
print("总体方差Y:", Y_variance)
所以X的方差更大,我们使用X轴来作为切分轴。切分点为中位数,所以我们对X轴的点进行排序:
- 首先我们选择横轴作为切分轴,将所有实例的横轴坐标从小到大排列为(2, 4, 5, 7, 8, 9),那么中位数应该是(5+7)/2 = 6,但因为没有实例点的横坐标为 6,而 kd 树中不能存在空结点,于是采用5或7为切分点,这里采用7为切分点,对应的根结点为(7,2)。由此左子空间为{(2,3),(4,7),(5,4)},右子空间为{(8,1),(9,6)}
下图中,红色点和线为选中的点和切分轴。垂直x轴划分。

- 接着在切分的子空间中,都选择竖轴作为切分轴,左子空间为{(2,3),(4,7),(5,4)},竖轴为3、7、4,排序后为3、4、7,中位数为4。则把(5,4)作为左子空间的根节点。又划分左子空间(2,3),右子空间(4,7)
垂直y轴划分:

- 在步骤1切分的右子空间为{(8,1),(9,6)},竖轴为1、6,中位数随便取一个,这里取中位数为6。则把(9,6)作为右子空间的根节点。又划分为左子空间(8,1)
垂直x轴划分:

这样kd树就构建完成了!kd 树构建完毕后,我们依然不知道最近邻点在哪里,此时就要用对 kd 树进行搜索了
kd 树搜索的过程如下:
假设标记为星星的点是 test point, 绿色的点是找到的近似点,在回溯过程中,需要用到一个队列,存储需要回溯的点,在判断其他子节点空间中是否有可能有距离查询点更近的数据点时,做法是以查询点为圆心,以当前的最近距离为半径画圆,这个圆称为候选超球(candidate hypersphere),如果圆与回溯点的轴相交,则需要将轴另一边的节点都放到回溯队列里面来。

1.7.3、查找点(2.1,3.1)

假如我们查找的点是(2.1,3.1),在kd树中对比,首先对比x1(X轴),2.1<7,则到达(5,4),对比x2(Y轴),3.1<4,则到达(2,3)。到达最后一层,则停止检测,最后结果即为(2,3)作为最佳结点nearest,(2,3)与(2.1,3.1)的距离dist为0.141。
- 队列search_path为:(7,2)、(5,4)、(2,3),之后进行回溯。
- 回溯至(5,4),以(2.1,3.1)为圆心,以dist=0.141为半径画一个圆,并不和超平面y=4相交,如上图,所以不必跳到结点(5,4)的右子空间去搜索,因为右子空间中不可能有更近样本点了
- 于是再回溯至(7,2),同理,以(2.1,3.1)为圆心,以dist=0.141为半径画一个圆并不和超平面x=7相交,所以也不用跳到结点(7,2)的右子空间去搜索
- 至此,search_path为空,结束整个搜索,返回nearest(2,3)作为(2.1,3.1)的最近邻点,最近距离为0.141
1.7.4、查找点(2,4.5)

假如我们查找的点是(2,4.5),在kd树中对比,对比的也是x1(X轴),2<7,则到达(5,4),对比x2(Y轴),4.5>4,则到达(4,7)[优先选择在本子空间搜索],到达最后一层,则停止检测,最后结果即为(4,7)作为最佳结点nearest,(4,7)与(2,4.5)的距离dist为3.202。
- 队列search_path为:(7,2)、(5,4)、(4,7),之后进行回溯。
- 在(4,7)处求欧式距离为3.202
- 回溯至(5,4),以(2,4.5)为圆心,以dist=3.202为半径画一个圆与超平面y=4相交,所以需要跳到(5,4)的左子空间去搜索。所以要将(2,3)加入到回溯队列search_path中,
- (5,4)与(2,4.5)的距离为3.04 < dist = 3.202,所以将(5,4)赋给nearest,并且dist=3.04
- 回溯至(2,3),(2,3)是叶子节点,直接平判断(2,3)是否离(2,4.5)更近,计算得到距离为1.5,所以nearest更新为(2,3),dist更新为(1.5)
- 回溯至(7,2),同理,以(2,4.5)为圆心,以dist=1.5为半径画一个圆并不和超平面x=7相交, 所以不用跳到结点(7,2)的右子空间去搜索。
- 至此,search_path为空,结束整个搜索,返回nearest(2,3)作为(2,4.5)的最近邻点,最近距离为1.5。
1.8、综合案例
在本案例中,将使用KNN算法对鸢尾花的种类进行分类,并测量花的特征。

根据鸢尾花的4个特征值:根据花瓣、花萼的长度和宽度,最终分为3类:山鸢尾,虹膜锦葵,和变色鸢尾。
1.8.1、数据集
sklearn.datasets.load_iris()
:加载并返回鸢尾花数据集,Iris数据集是常用的分类实验数据集。

python
from sklearn.datasets import load_iris
# 获取鸢尾花数据集
iris = load_iris()

python
print("鸢尾花的特征值:\n", iris["data"])

python
print("鸢尾花的目标值:\n", iris.target)

python
print("鸢尾花特征的名字:\n", iris.feature_names)

python
print("鸢尾花目标值的名字:\n", iris.target_names)

python
print("鸢尾花的描述:\n", iris.DESCR)

还有一种获取数据集的方法:
sklearn.datasets.fetch_20newsgroups(data_home=None,subset='train')
:获取大规模数据集,需要从网络上下载-
函数的第一个参数是data_home,表示数据集下载的目录,默认是
~/scikit_learn_data/
(家目录下) -
subset:'train'或者'test','all',可选,选择要加载的数据集。
-
训练集的"训练",测试集的"测试",两者的"全部"
-
1.8.2、查看数据分布
通过创建一些图,以查看不同类别是如何通过特征来区分的。
1、安装seaborn
-
Seaborn 是基于 Matplotlib 核心库进行了更高级的 API 封装,可以让你轻松地画出更漂亮的图形。而 Seaborn 的漂亮主要体现在配色更加舒服、以及图形元素的样式更加细腻。
-
seaborn.lmplot(x,y,data,hue,fit_reg)
: 是一个非常有用的方法,它会在绘制二维散点图时,自动完成回归拟合- x,y:横纵坐标的列名
- data:关联到的数据集
- hue: 按照花的类别分类显示
- fit_reg:是否进行线性拟合
iris: 中文翻译 鸢尾
python
from sklearn.datasets import load_iris
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
# 获取鸢尾花数据集
iris = load_iris()
# 把数据转换成dataframe的格式
# 数据就是 iris['data'], columns就是特征值
iris_d = pd.DataFrame(iris['data'], columns = ['Sepal_Length', 'Sepal_Width', 'Petal_Length', 'Petal_Width'])
iris_d

python
# 加一列目标值 山鸢尾,虹膜锦葵,和变色鸢尾
iris_d['Species'] = iris.target
iris_d

python
# 定义函数
def plot_iris(iris, col1, col2):
sns.lmplot(x = col1, y = col2, data = iris, hue = "Species", fit_reg = False)
plt.xlabel(col1)
plt.ylabel(col2)
plt.title('鸢尾花种类分布图')
plt.show()
plot_iris(iris_d, 'Sepal_Length', 'Sepal_Width')
# plot_iris(iris_d, 'Sepal_Width', 'Petal_Length')

看下中文解释:
python
from sklearn.datasets import load_iris
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
# 1、获取鸢尾花数据集
iris = load_iris()
# 2、把数据转换成dataframe的格式
# 数据就是 iris['data'], columns就是特征值
iris_d = pd.DataFrame(iris['data'], columns=['花瓣长度', '花瓣宽度', '花萼长度', '花萼宽度'])
# 加一列目标值 山鸢尾,虹膜锦葵,和变色鸢尾
iris_d['Species'] = iris.target
# 定义函数
def plot_iris(data, col1, col2):
# hue: 按照花的类别分类显示
# it_reg:是否进行线性拟合
sns.lmplot(data=data,x=col1,y=col2, hue="Species", fit_reg=True)
plt.xlabel(col1)
plt.ylabel(col2)
plt.title('鸢尾花种类分布图')
plt.show()
plot_iris(iris_d, '花瓣长度', '花瓣宽度')

1.8.2、数据集的划分
机器学习一般的数据集会划分为两个部分:
- 训练数据:用于训练,构建模型
- 测试数据:在模型检验时使用,用于评估模型是否有效
划分比例:
- 训练集:70% 80% 75%
- 测试集:30% 20% 25%
sklearn.model_selection.train_test_split(arrays, test_size=None, train_size=None, random_state=None, shuffle=True, stratify=None)
:将数组或矩阵拆分为随机的训练集和测试集。- arrays: 允许的输入包括列表、NumPy数组、SciPy稀疏矩阵或Pandas DataFrame
- test_size:测试集的大小,浮点数或整数,默认为None。如果为浮点数,则应在0.0到1.0之间,表示测试集在数据集中的比例。如果为整数,则表示测试样本的绝对数量。如果为None,则该值设置为训练大小的补集。如果
train_size
也为None,则将其设置为0.25。 - train_size: 浮点数或整数,默认为None。如果为浮点数,则应在0.0到1.0之间,表示训练集在数据集中的比例。如果为整数,则表示训练样本的绝对数量。如果为None,则该值会自动设置为测试大小的补集。
- random.state:随机数种子,不同的种子会造成不同的随机采样结果。相同的种子采样结果相同。
返回值:
- 特征值的训练集x_train、特征值的测试集x_test、目标值的训练集y_train、目标值的测试集y_test
python
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
# 1、获取鸢尾花数据集
iris = load_iris()
# 对鸢尾花数据集进行分割:将鸢尾花Iris数据集划分为训练集和测试集
# 训练集的特征值x_train 测试集的特征值x_test 训练集的目标值y_train 测试集的目标值y_test
# iris.data特征数据(如花瓣长度、萼片宽度等)
# iris.target标签数据(即花的类别)
# 设置随机种子,确保每次运行代码时分割结果一致(可复现性)
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, random_state=22)
print("训练集的特征值是:\n",x_train)
print("训练集的目标值是:\n",y_train)
print("测试集的目标值是:\n",x_test)
print("测试集的目标值是:\n",y_test)
# 用shape可以看到形状
print("训练集的目标值的形状是:\n",y_train.shape)
print("测试集的目标值的形状是:\n",y_test.shape)

1.9、特征工程-特征预处理
通过一些转换函数 将特征数据转换成更加适合算法模型 的特征数据过程,注意是对特征值进行相应的处理,不对目标值处理。

为什么我们要进行归一化/标准化?
-
特征的单位或者大小相差较大,或者某特征的方差相比其他的特征要大出几个数量级 ,容易影响(支配)目标结果,使得一些算法无法学习到其它的特征
-
我们需要用到一些方法进行无量纲化 ,使不同规格的数据转换到同一规格
1.9.1、归一化
归一化:通过对原始数据进行变换把数据映射到(默认为[0,1])之间

如上图例子,特征1的计算方法为:
X ′ = 当前值 − 最小值 最大值 − 最小值 = 90 − 60 90 − 60 X' =\frac{当前值-最小值}{最大值-最小值} = \frac{90-60}{90-60} X′=最大值−最小值当前值−最小值=90−6090−60
X ′ ′ = X ′ ∗ ( 指定区间最大值 − 指定区间最小值 ) + 指定区间最小值 = 1 ∗ ( 1 − 0 ) + 0 X''= X' * (指定区间最大值-指定区间最小值)+指定区间最小值 = 1 * (1-0) + 0 X′′=X′∗(指定区间最大值−指定区间最小值)+指定区间最小值=1∗(1−0)+0
归一化的API如下:
python
sklearn.preprocessing.MinMaxScaler (feature_range=(0,1))
- feature_range:元组 (最小值, 最大值), 默认=(0, 1),变换后数据的期望范围。

我们这里采用这么一组数据来演示,有三个特征值: 坐飞机里程数、吃冰淇淋公升数、游戏消耗时间百分比,这三个特征值单位都不相同,我们要想办法将其进行归一化。

python
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
def minmax_demo():
"""
归一化演示
:return: None
"""
data = pd.read_csv("./data/dating.txt")
# 1、实例化一个最小最大缩放器,默认变换后的数据期望范围是(2,3)
transfer = MinMaxScaler(feature_range=(2, 3))
# 2、调用fit_transform变换,返回变换后的数据
data = transfer.fit_transform(data[['坐飞机里程数', '吃冰淇淋公升数', '游戏消耗时间百分比']])
print("最小值最大值归一化处理的结果:\n", data)
return None
minmax_demo()

归一化的最大值与最小值非常容易受异常点影响,**所以这种方法鲁棒性(精确性)较差,只适合传统精确小数据场景。**那有时候人工统计的数据就是有可能存在错误,怎么解决呢,这个时候就要用到标准化!
1.9.2、标准化🔥
标准化:通过对原始数据进行变换把数据变换到均值为0,标准差为1的标准正态分布的数据。
- 正态分布(也叫高斯分布)是一种概率分布,大自然很多数据符合正态分布
- 标准化时,异常点的影响就会减少。

我们看看标准化API是怎么用的:
python
sklearn.preprocessing.StandardScaler( )
# 处理之后每列所有数据都聚集在均值0附近标准差差为1

python
import pandas as pd
from sklearn.preprocessing import StandardScaler
def stand_demo():
"""
标准化演示
:return: None
"""
data = pd.read_csv("./data/dating.txt")
# 1、实例化标准化缩放器
transfer = StandardScaler()
# 2、调用fit_transform变换,返回变换后的数据
data = transfer.fit_transform(data[['坐飞机里程数', '吃冰淇淋公升数', '游戏消耗时间百分比']])
print("标准化的结果:\n", data)
print("每一列特征的平均值:\n", transfer.mean_)
print("每一列特征的方差:\n", transfer.var_)
return None
stand_demo()

标准化在已有样本足够多的情况下比较稳定,适合现代嘈杂大数据场景。
- 归一化基本不用,就用标准化。
1.10、鸢尾花总结🔥
- 1.获取数据集
- 2.数据基本处理
- 3.特征工程
- 4.机器学习(模型训练)
- 5.模型评估
python
# # 获取数据
from sklearn.datasets import load_iris
# 分割训练集和测试集
from sklearn.model_selection import train_test_split
# 数据标准化
from sklearn.preprocessing import StandardScaler
# 机器学习KNN算法
from sklearn.neighbors import KNeighborsClassifier
# 1.获取数据集
iris = load_iris()
# 2.数据基本处理:分割训练集和测试集
# 特征值的训练集x_train、特征值的测试集x_test、目标值的训练集y_train、目标值的测试集y_test
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=22)
# 3、特征工程:标准化,处理之后每列所有数据都聚集在均值0附近标准差为1
# 对特征值的训练集x_train、特征值的测试集x_test进行数据标准化
transfer = StandardScaler()
x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)
# 4、机器学习(模型训练)
# 使用KNN算法,对【标准化后的特征值的训练集x_train】和【目标值的训练集y_train】进行训练模型
KNN = KNeighborsClassifier(n_neighbors=9)
KNN.fit(x_train, y_train)
# 5、模型评估
# 方法1:比对真实值和预测值
y_predict = KNN.predict(x_test)
print("预测结果为:\n", y_predict)
print("比对真实值和预测值:\n", y_predict == y_test)
# 方法2:直接计算准确率
score = KNN.score(x_test, y_test)
print("准确率为:\n", score)

整个流程如下:数据集先被分割为特征值的训练集x_train、特征值的测试集x_test、目标值的训练集y_train、目标值的测试集y_test,将特征值的训练集和特征值的预测值进行标准化,然后将特征值的训练值和目标值的训练值进行模型训练,最后将特征值的测试值进行模型预测,并将返回的预测值和目标值的测试值进行比对。

1.11、KNN算法总结
优点:
- 简单有效
- 重新训练的代价低
- 适合类域交叉样本
- KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合。
- 适合大样本自动分类
- 该算法比较适用于样本容量比较大的类域的自动分类,而那些样本容量较小的类域采用这种算法比较容易产生误分。
缺点:
- 惰性学习
- KNN算法是懒散学习方法(lazy learning,基本上不学习),一些积极学习的算法要快很多
- 类别评分不是规格化
- 不像一些通过概率评分的分类
- 输出可解释性不强
- 例如决策树的输出可解释性就较强
- 对不均衡的样本不擅长
- 当样本不平衡时,如一个类的样本容量很大,而其他类样本容量很小时,有可能导致当输入一个新样本时,该样本的K个邻居中大容量类的样本占多数。该算法只计算"最近的"邻居样本,某一类的样本数量很大,那么或者这类样本并不接近目标样本,或者这类样本很靠近目标样本。无论怎样,数量并不能影响运行结果。可以采用权值的方法(和该样本距离小的邻居权值大)来改进。
- 计算量较大
- 目前常用的解决方法是事先对已知样本点进行剪辑,事先去除对分类作用不大的样本。
1.12、超参数选择方法
参数
是模型中可被学习和调整的参数,通过训练数据进行学习和优化。而超参数
则是手动设置的参数,用于控制模型的行为和性能。
- KNN算法的N就是超参数,需要人工设置的参数就是超参数。超参数的选择方法有交叉验证+网格搜索。
- 不需要人工设置,单纯通过模型自己学习的叫做参数。
1.21.1、交叉验证
交叉验证通过多次的分割和评估过程,使得所有的数据都有机会作为训练集和测试集被使用。这样不仅能够评估模型在未知数据上的表现,还能够在模型选择和参数调整过程中,提供关于模型稳定性的重要信息。例如,通过计算多个模型评估指标的方差,可以判断模型的泛化性能是否稳定。
白话:交叉验证就是将拿到的训练数据,分为训练集和验证集。
-
是一种数据集的分割方法,将训练集划分为 n 份,每一次选择拿一份做验证集(测试集)、其他n-1份做训练集 (n等于几就叫做几折交叉验证)
-
以下图为例:
- 第一次:把第一份数据做验证集,其他数据做训练。得到准确率为80%
- 第二次:把第二份数据做验证集,其他数据做训练。得到准确率为78%
- ...以此类推,总共训练4次,评估4次。称为4折交叉验证。
- 使用训练集+验证集多次评估模型,取平均值做交叉验证为模型得分
- 若n=5模型得分最好,再使用全部训练集对n=5模型再训练一遍,再使用测试集对n=5模型做评估
交叉验证,是划分数据集的一种方法,目的是为了得到更加准确可信的模型评分。
我们之前知道数据分为训练集和测试集,但是**为了让从训练得到模型结果更加准确。**做以下处理
- 训练集:训练集+验证集
- 测试集:测试集

交叉验证目的:为了让被评估的模型更加准确可信。注意没法提高模型的准确率呦!
问题:这个只是让被评估的模型更加准确可信,那么怎么选择或者调优参数来让准确率增高呢?
- 答案:网格搜索
通常情况下,有很多参数是需要手动指定的(如k-近邻算法中的K值),这种叫超参数 。但是手动过程繁杂,所以需要对模型预设几种超参数组合。每组超参数都采用交叉验证来进行评估。最后选出最优参数组合建立模型。

网格搜索是模型调参 的有力工具,是寻找最优超参数的工具 。只需要将若干参数 传递给网格搜索对象 ,它自动帮我们完成不同超参数的组合、模型训练、模型评估,最终返回一组最优的超参数。
例如:我们的KNN算法的K任意设置为{3,4,5,6}
- K=3,采用4折交叉验证,得到准确率为80%
- K=4,采用4折交叉验证,得到准确率为76%
- K=5,采用4折交叉验证,得到准确率为86%
- K=6,采用4折交叉验证,得到准确率为79%
K=5得到的准确率最高,所以选择K=5
网格搜索+交叉验证的强力组合(模型选择和调优):
- 交叉验证解决模型的**数据输入问题(数据集划分)**得到更可靠的模型。
- 网格搜索解决超参数组合。
- 两个组合再一起形成一个模型参数调优的解决方案。
我们看下交叉验证的API:
python
# 入参输入一个estimator,返回一个更加强大的estimator,拥有交叉验证网格搜索的功能
sklearn.model_selection.GridSearchCV(estimator, param_grid=None,cv=None)
- estimator: 估计器对象
- param_grid :字典或字典列表。键为参数名称(
str
),值为要尝试的参数设置列表的字典,或者此类字典的列表,在这种情况下,将探索列表中每个字典所跨越的网格。 - cv:指定几折交叉验证
我们给鸢尾花案例增加K值调优:
python
# 1、获取数据集
iris = load_iris()
# 2、数据基本处理 -- 划分数据集
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, random_state=22)
# 3、特征工程:标准化
# 实例化一个转换器类
transfer = StandardScaler()
# 调用fit_transform
x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)
# 4、KNN预估器流程
# 4.1 实例化预估器类
estimator = KNeighborsClassifier()
# 4.2 模型选择与调优------网格搜索和交叉验证
# 准备要调的超参数
param_dict = {"n_neighbors": [1, 3, 5]}
estimator = GridSearchCV(estimator, param_grid=param_dict, cv=3)
# 4.3 fit数据进行训练
estimator.fit(x_train, y_train)
# 5、评估模型效果
# 方法a:比对预测结果和真实值
y_predict = estimator.predict(x_test)
print("比对预测结果和真实值:\n", y_predict == y_test)
# 方法b:直接计算准确率
score = estimator.score(x_test, y_test)
print("直接计算准确率:\n", score)
- 可以进行评估查看最终选择的结果和交叉验证的结果
python
print("在交叉验证中验证的最好结果:\n", estimator.best_score_)
print("最好的参数模型:\n", estimator.best_estimator_)
print("每次交叉验证后的准确率结果:\n", estimator.cv_results_)
2、线性回归

线性回归(Linear regression)是利用回归方程(函数)对一个或多个自变量(特征值)和因变量(目标值)之间 关系进行建模的一种分析方式。只有一个自变量的情况称为一元线性回归,多于一个自变量情况的叫做多元回归

那么怎么理解呢?我们来看几个例子
- 期末成绩:0.7×考试成绩+0.3×平时成绩
- 房子价格 = 0.02×中心区域的距离 + 0.04×城市一氧化氮浓度 + (-0.12×自住房平均房价) + 0.254×城镇犯罪率
上面两个例子,我们看到特征值与目标值之间建立了一个关系,这个关系可以理解为线性模型 。线性回归当中主要有两种模型,一种是线性关系,另一种是非线性关系。

线性回归分类:
- 一元线性回归(目标值只与一个因变量有关系):y=wx+b
- 多元线性回归(目标值只与多个因变量有关系 ):y=w1x1 + w2x2 + w3x3 + ... + b
为什么叫线性模型?
- 因为求解的w,都是w的零次幂(常数项),所以叫做线性模型。(如果有x2、x3,都不是线性的,线性的需要x的次数为1)
- 在线性回归中,从数据中获取的规律其实就是学习权重系数w 。x是特征值,y是目标值。
- w越大,说明相乘的x的特征越重要,w越小,说明相乘的x的特征越不重要

2.0、线性回归的应用场景
- 钢轨伸缩长度与温度
- 昆虫鸣叫次数与天气
- 国内GDP与双十一销售额
- 用电量和温度
- 身高与体重
2.1、线性回归API
python
sklearn.linear_model.LinearRegression()

我们来初步使用一下,目前已知平时成绩和期末成绩、最终成绩,我们来预测一下平时成绩和期末成绩的占比:

python
from sklearn.linear_model import LinearRegression
# 1、获取数据集
x = [[80, 86],
[82, 80],
[85, 78],
[90, 90],
[86, 82],
[82, 90],
[78, 80],
[92, 94]]
y = [84.2, 80.6, 80.1, 90, 83.2, 87.6, 79.4, 93.4]
# 2、模型训练
# 实例化API
estimator = LinearRegression()
# 使用fit方法进行训练
estimator.fit(x, y)
print("线性回归系数是:", estimator.coef_)
# 预测平时成绩100,期末成绩80的最终成绩为
print("平时成绩100,期末成绩80的最终成绩为:",estimator.predict([[100, 80]]))

2.2、线性回归的损失和优化
期末成绩=0.7×考试成绩+0.3×平时成绩 ,假如我们随意指定一个关系:期末成绩=0.5×考试成绩+0.5×平时成绩,那么我们这个关系肯定是有误差的,既然存在这个误差,那我们就将这个误差给衡量出来。
回归问题中的损失函数有:损失函数 :衡量预测值和真实值效果的函数,也叫代价函数、目标函数
- 最小二乘法:误差平方和,(预测值-真实值)2,也叫L2损失函数
∑ i = 0 m ( h ( x i ) − y i ) 2 \sum_{i=0}^{m}(h(x_i)-y_i)^2 i=0∑m(h(xi)−yi)2
同时还有个L1损失函数 :也叫最小绝对值偏差
∑ i = 0 m ∣ h ( x i ) − y i ∣ \sum_{i=0}^{m}|h(x_i)-y_i| i=0∑m∣h(xi)−yi∣
L1损失函数相比于L2损失函数的鲁棒性更好。因为L2范数将误差平方化(如果误差大于1,则误差会放大很多),模型的误差会比L1范数大的多,因此模型会对这种类型的样本更加敏感,这就需要调整模型来最小化误差。
- 均方误差MSE
1 m ∑ i = 0 m ( h ( x i ) − y i ) 2 \frac{1}{m}\sum_{i=0}^{m}(h(x_i)-y_i)^2 m1i=0∑m(h(xi)−yi)2
- 平均绝对误差MAE
1 m ∑ i = 0 m ∣ h ( x i ) − y i ∣ \frac{1}{m}\sum_{i=0}^{m}|h(x_i)-y_i| m1i=0∑m∣h(xi)−yi∣
2.2.1、损失函数

一元线性回归 的损失函数如下:
损失函数 J ( k , b ) = ∑ i = 1 m ( h ( x i ) − y i ) 2 = ∑ i = 0 m ( k ( x i ) + b − y ( i ) ) 2 损失函数J(k,b)=\sum_{i=1}^{m}(h(x_i)-y_i)^2 = \sum_{i=0}^{m}(k(x_i)+b-y(i))^2 损失函数J(k,b)=i=1∑m(h(xi)−yi)2=i=0∑m(k(xi)+b−y(i))2
要求损失函数的最小值:
- 对参数k,b分别求导,并令导数为0
∂ J ∂ k = 2 ∑ i = 0 m ( k ( x i ) + b − y ( i ) ) ∗ x i = 2 ∑ i = 0 m ( k x i + b x i − x i y i ) = 0 ∂ J ∂ b = 2 ∑ i = 0 m ( k ( x i ) + b − y ( i ) ) ∗ 1 = 2 ∑ i = 0 m ( k x i + b x i − y i ) = 0 \frac{\partial J}{\partial k} = 2\sum_{i=0}^{m}(k(x_i)+b-y(i)) * x_i = 2\sum_{i=0}^{m}(kx_i+bx_i-x_iy_i) =0\\ \\ \frac{\partial J}{\partial b} = 2\sum_{i=0}^{m}(k(x_i)+b-y(i)) * 1 = 2\sum_{i=0}^{m}(kx_i+bx_i-y_i)=0 ∂k∂J=2i=0∑m(k(xi)+b−y(i))∗xi=2i=0∑m(kxi+bxi−xiyi)=0∂b∂J=2i=0∑m(k(xi)+b−y(i))∗1=2i=0∑m(kxi+bxi−yi)=0
- 将xi和yi代入上式,得到关于k和b的二元一次方程,求解即可
多元线性回归方程:
多元线性回归方程式: y = w 1 x 1 + w 2 x 2 + w 3 x + w 4 x 4 + . . . + b = w T + b 多元线性回归方程式:y=w_1x_1+w_2x_2+w_3x_+w_4x_4+...+b=w^T+b 多元线性回归方程式:y=w1x1+w2x2+w3x+w4x4+...+b=wT+b
有数据集D = {(x1,y1),(x2,y2),(x3,y3),...,(xn,yn)},其中模型权重w是一个向量 w = {w1,w2,w3,w4...},代表特征数
多元线性回归方程的损失函数:
多元线性回归方程的损失函数 : 损失函数 J ( k , b ) = ∑ i = 1 m ( h ( x i ) − y i ) 2 = ∣ ∣ X w − y ∣ ∣ 2 2 多元线性回归方程的损失函数:损失函数J(k,b)=\sum_{i=1}^{m}(h(x_i)-y_i)^2 = ||X_w-y||_2^2 多元线性回归方程的损失函数:损失函数J(k,b)=i=1∑m(h(xi)−yi)2=∣∣Xw−y∣∣22
要求损失函数的最小值:
w = ( X T X ) − 1 ∗ X T y w = (X^TX)^{-1}*X^Ty w=(XTX)−1∗XTy
如何去求模型当中的W,使得损失最小?(损失函数的最优解:目的是找到最小损失对应的W值)
- 线性回归经常使用的两种优化算法
- 正规方程
- 梯度下降法🔥
2.2.3、梯度下降法
求一个函数的梯度,其实就是求这个函数的负导数(求导加负号)
- 梯度下降在各种损失函数(目标函数)求解中大量使用。深度学习中更是如此,深度学习模型参数很轻松就上亿,只能通过迭代的方式求最优解
梯度下降法的基本思想可以类比为一个下山的过程。假设这样一个场景:一个人被困在山上,需要从山上下来,但此时山上的浓雾很大,导致可视度很低。因此,下山的路径就无法确定,他必须利用自己周围的信息去找到下山的路径。这个时候,他就可以利用梯度下降算法来帮助自己下山。
- 以他当前的所处的位置为基准,寻找这个位置最陡峭的地方,然后朝着山的高度下降的地方走,
- 然后每走一段距离,都反复采用同一个方法,最后就能成功的抵达山谷。
- 同理,如果我们的目标是上山,也就是爬到山顶,那么此时应该是朝着最陡峭的方向往上走

梯度下降的基本过程就和下山的场景很类似。首先,我们有一个可微分的函数 。这个函数就代表着一座山。我们的目标就是找到这个函数的最小值 ,也就是山底。根据之前的场景假设,最快的下山的方式就是找到当前位置最陡峭的方向,然后沿着此方向向下走,对应到函数中,就是找到给定点的梯度 ,然后朝着梯度相反的方向,就能让函数值下降的最快!因为梯度的方向就是函数值变化最快的方向。
- 在单变量 的函数中,梯度其实就是某一点切线斜率(某一点的导数),有方向为函数增长最快的方向
- 在多变量 函数中,梯度就是某一个点的偏导数;有方向:偏导数分量的向量方向
这也就说明了为什么我们需要千方百计的求取梯度!我们需要到达山底,就需要在每一步观测到此时最陡峭的地方,梯度就恰巧告诉了我们这个方向。梯度的方向是函数在给定点上升最快的方向,那么梯度的反方向就是函数在给定点下降最快的方向,这正是我们所需要的。所以我们只要沿着梯度的反方向一直走,就能走到局部的最低点!
梯度下降的公式:
θ i + 1 = θ i − α ∗ ∂ J ( θ ) ∂ θ i θ_{i+1} = θ_i - α * \frac{\partial J(θ)}{\partial θ_i} θi+1=θi−α∗∂θi∂J(θ)
-
θ 表示模型参数权重,α 表示学习率(步长),∇J(θ)表示损失函数J 关于参数θ的梯度。
- 可以通过α来控制每一步走的距离,机器学习中一般为0.001 ~ 0.01
- 学习率太小,下降的速度会慢。学习率太大:容易造成错过最低点、产生下降过程中的震荡、甚至梯度爆炸
-
梯度∇J(θ)是损失函数在当前参数θ 处的导数(或偏导数),指示了损失函数增长最快的方向。梯度是上升最快的方向, 我们需要是下降最快的方向, 所以需要加负号
0、单变量梯度下降
举个例子,例如我们的损失函数J(θ) = θ2 ,求当θ为何值时,损失函数J(θ)值越小。
- 其偏导数为 ∇J(θ)=2θ,假设步长 α 为0.4,初始值起点为 θ0 = 1,下面显示了梯度下降法的迭代计算结果:
初始值: θ 0 = 1 初始值:θ_0=1 初始值:θ0=1
第一步: θ 1 = θ 0 − α ∗ ( 2 θ 0 ) = 1 − 0.4 ∗ ( 2 ∗ 1 ) = 0.2 第一步:θ_1 = θ_0 - α*(2θ_0)= 1-0.4*(2*1)=0.2 第一步:θ1=θ0−α∗(2θ0)=1−0.4∗(2∗1)=0.2
第二步: θ 2 = θ 1 − α ∗ ( 2 θ 1 ) = 0.2 − 0.4 ∗ ( 2 ∗ 0.2 ) = 0.04 第二步:θ_2 = θ_1 - α*(2θ_1)= 0.2-0.4*(2*0.2)=0.04 第二步:θ2=θ1−α∗(2θ1)=0.2−0.4∗(2∗0.2)=0.04
第三步: θ 3 = θ 2 − α ∗ ( 2 θ 2 ) = 0.04 − 0.4 ∗ ( 2 ∗ 0.0.04 ) = 0.008 第三步:θ_3 = θ_2 - α*(2θ_2)= 0.04-0.4*(2*0.0.04)=0.008 第三步:θ3=θ2−α∗(2θ2)=0.04−0.4∗(2∗0.0.04)=0.008
在第N步,θt将会不断趋向最优解0,同时损失函数J(θ)也会不断趋近最优值0.
1、多变量梯度下降
举个例子,例如我们的损失函数J(θ) = θ12 + θ22 ,求当θ1、θ2为何值时,损失函数J(θ)值越小。
- 其偏导数为 :J(θ1)=2θ1,J(θ2)=2θ2,则梯度为(2θ1,2θ2)
- 起始点为(1,3),学习率α = 0.1
下面显示了梯度下降法的迭代计算结果:
第一步: ( θ 1 , θ 2 ) = ( θ 1 , θ 2 ) − α ∗ ( 2 θ 1 , 2 θ 2 ) = ( θ 1 − α ∗ 2 θ 1 , θ 2 − α ∗ 2 θ 2 ) = ( 1 − 0.1 ∗ 2 , 3 − 0.1 ∗ 6 ) = ( 0.8 , 2.4 ) 第一步:(θ_1,θ_2)=(θ_1,θ_2) - α*(2θ_1,2θ_2) = (θ_1-α*2θ_1,θ_2-α*2θ_2) = (1-0.1*2,3-0.1*6)=(0.8,2.4) 第一步:(θ1,θ2)=(θ1,θ2)−α∗(2θ1,2θ2)=(θ1−α∗2θ1,θ2−α∗2θ2)=(1−0.1∗2,3−0.1∗6)=(0.8,2.4)
第二步: ( θ 1 , θ 2 ) = ( θ 1 , θ 2 ) − α ∗ ( 2 θ 1 , 2 θ 2 ) = ( θ 1 − α ∗ 2 θ 1 , θ 2 − α ∗ 2 θ 2 ) = ( 0.8 − 0.1 ∗ 1.6 , 2.4 − 0.1 ∗ 4.8 ) = ( 0.64 , 1.92 ) 第二步:(θ_1,θ_2)=(θ_1,θ_2) - α*(2θ_1,2θ_2) = (θ_1-α*2θ_1,θ_2-α*2θ_2) = (0.8-0.1*1.6,2.4-0.1*4.8)=(0.64,1.92) 第二步:(θ1,θ2)=(θ1,θ2)−α∗(2θ1,2θ2)=(θ1−α∗2θ1,θ2−α∗2θ2)=(0.8−0.1∗1.6,2.4−0.1∗4.8)=(0.64,1.92)
第N步,θ1、θ2已经极其接近最优值,损失函数J(θ)也接近最小值。
注意,梯度下降法没法保证最终一定能走到最小值 ,有时候走到的也只是个极小值:

梯度下降法总结:
-
梯度下降过程
- 给定初始位置、步长(学习率)
- 计算该点当前的梯度的负方向
- 向该负方向移动步长
- 重复 2-3 步 直至收敛(两次差距小于指定的阈值或达到指定的迭代次数即收敛)
-
梯度下降公式中,为什么梯度要乘以一个负号
- 梯度的方向实际就是函数在此点上升最快的方向
- 需要朝着下降最快的方向走,负梯度方向, 所以加上负号
-
步长决定了在梯度下降迭代的过程中,每一步沿梯度负方向前进的长度:学习率太小,下降的速度会慢,学习率太大:容易造成错过最低点、产生下降过程中的震荡、甚至梯度爆炸
2、梯度下降法分类
常见的梯度下降算法有:
- 全梯度下降算法(Full gradient descent),
- 随机梯度下降算法(Stochastic gradient descent),
- 小批量梯度下降算法(Mini-batch gradient descent),
- 随机平均梯度下降算法(Stochastic average gradient descent)
它们都是为了正确地调节权重向量,通过为每个权重计算一个梯度,从而更新权值,使目标函数尽可能最小化。其差别在于样本的使用方式不同。
-
全梯度下降算法(FG):计算训练集所有样本误差 ,对其求和再取平均值作为目标函数。
- 在执行每次更新时,我们需要在整个数据集上计算所有的梯度,所以批梯度下降法的速度会很慢,同时,批梯度下降法无法处理超出内存容量限制的数据集。
- 批梯度下降法同样也不能在线更新模型,即在运行的过程中,不能增加新的样本
-
随机梯度下降算法(SG)
- 由于FG每迭代更新一次权重都需要计算所有样本误差,而实际问题中经常有上亿的训练样本,故效率偏低,且容易陷入局部最优解,因此提出了随机梯度下降算法。
- 其每轮计算的目标函数不再是全体样本误差,而仅是单个样本误差,即每次只代入计算一个样本目标函数的梯度来更新权重,再取下一个样本重复此过程,直到损失函数值停止下降或损失函数值小于某个可以容忍的阈值。
- 但是由于,SG每次只使用一个样本迭代,若遇上噪声则容易陷入局部最优解。
-
小批量梯度下降算法(Mini-batch)
- 小批量梯度下降算法是FG和SG的折中方案,在一定程度上兼顾了以上两种方法的优点。每次从训练样本集上随机抽取一个小样本集,在抽出来的小样本集上采用FG迭代更新权重。
-
随机平均梯度下降算法(SAG)
- 在SG方法中,虽然避开了运算成本大的问题,但对于大数据训练而言,SG效果常不尽如人意,因为每一轮梯度更新都完全与上一轮的数据和梯度无关。
- **随机平均梯度算法克服了这个问题,在内存中为每一个样本都维护一个旧的梯度,随机选择第i个样本来更新此样本的梯度,其他样本的梯度保持不变,然后求得所有梯度的平均值,进而更新了参数。**如此,每一轮更新仅需计算一个样本的梯度,计算成本等同于SG,但收敛速度快得多.

3、梯度下降法API

py
sklearn.linear_model.SGDRegressor(loss="squared_loss", fit_intercept=True, learning_rate ='invscaling', eta0=0.01)
- SGDRegressor类实现了随机梯度下降学习,它支持不同的loss函数和正则化惩罚项来拟合线性回归模型。
- loss:损失类型,
squared_loss
为普通最小二乘法 - fit_intercept:是否计算偏置
- learning_rate:学习率调度
属性:
- SGDRegressor.coef_:回归系数
- SGDRegressor.intercept_:偏置
4、波士顿房价
活动背景:
波士顿房地产市场竞争激烈,而你想成为该地区最好的房地产经纪人。为了更好地与同行竞争,你决定运用机器学习的一些基本概念,帮助客户为自己的房产定下最佳售价。幸运的是,你找到了波士顿房价的数据集,里面聚合了波士顿郊区包含多个特征维度的房价数据。你的任务是用可用的工具进行统计分析,并基于分析建立优化模型。这个模型将用来为你的客户评估房产的最佳售价。
数据集:
总共有506行数据,其中特征值有13个,目标值为房价。

python
# 获取数据
from sklearn.datasets import fetch_openml
import pandas as pd
import numpy as np
# 分割数据
from sklearn.model_selection import train_test_split
# 数据标准化
from sklearn.preprocessing import StandardScaler
# 机器学习-线性回归(梯度下降法)
from sklearn.linear_model import SGDRegressor
# 模型评估
from sklearn.metrics import mean_squared_error
def linear_model():
"""
线性回归:梯度下降法
:return:None
"""
# 1.获取数据
boston = fetch_openml(name='boston', version=1, as_frame=True)
# 选择特征值和目标值
X = boston.data
Y = boston.target
# 2.分割训练集和测试集
# 特征值的训练集x_train、特征值的测试集x_test、目标值的训练集y_train、目标值的测试集y_test
x_train, x_test, y_train, y_test = train_test_split(X,Y, test_size=0.3, random_state=10)
# 3.特征工程-标准化
# 对特征值的训练集x_train、特征值的测试集x_test进行数据标准化
transfer = StandardScaler()
x_train = transfer.fit_transform(x_train)
x_test = transfer.fit_transform(x_test)
# 4.机器学习-线性回归(特征方程)
# 使用SGDRegressor(随机梯度下降回归)
sgd = SGDRegressor(
max_iter=1000, # 最大迭代次数
)
# 5.训练模型
sgd.fit(x_train, y_train)
# 5.模型评估
# 5.1 预测训练集和测试集
# 特征值的测试集x_test
y_predict = sgd.predict(x_test)
print("预测值为: \n",y_predict)
print("模型中的系数为:\n: \n",sgd.coef_)
print("模型中的偏置为:\n: \n",sgd.intercept_)
# 5.2 评价
# 均方误差
error = mean_squared_error(y_test, y_predict)
print("误差为:\n", error)
return None
linear_model()

波士顿房价的预测和鸢尾花案例的流程是一致的,无非是训练模型时用的是随机梯度下降回归算法。
2.3、欠拟合和过拟合
- 过拟合:一个假设在训练数据上能够获得比其他假设更好的拟合, 但是在测试数据集上却不能很好地拟合数据,此时认为这个假设出现了过拟合的现象。(模型过于复杂)
- 欠拟合:一个假设在训练数据上不能获得更好的拟合,并且在测试数据集上也不能很好地拟合数据,此时认为这个假设出现了欠拟合的现象。(模型过于简单)
欠拟合原因以及解决办法
-
原因:学习到数据的特征过少
-
解决办法:
- **1)添加其他特征项,**有时候我们模型出现欠拟合的时候是因为特征项不够导致的,可以添加其他特征项来很好地解决。例如,组合、泛化、相关性三类特征是特征添加的重要手段,无论在什么场景,都可以照葫芦画瓢,总会得到意想不到的效果。
- 2)添加多项式特征,这个在机器学习算法里面用的很普遍,例如将线性模型通过添加二次项或者三次项使模型泛化能力更强。
-
过拟合原因以及解决办法
- 原因:原始特征过多,存在一些嘈杂特征,模型过于复杂是因为模型尝试去兼顾各个测试数据点
- 解决办法:
- 1)重新清洗数据,导致过拟合的一个原因也有可能是数据不纯导致的,如果出现了过拟合就需要我们重新清洗数据。
- 2)增大数据的训练量,还有一个原因就是我们用于训练的数据量太小导致的,训练数据占总数据的比例过小。
- 3)正则化:解决模型过拟合的方法,在机器学习、深度学习中大量使用
- 4)减少特征维度,防止维灾难:由于特征多,样本数量少,导致学习不充分,泛化能力差
2.3.1、正则化

在学习的时候,数据提供的特征有些影响模型复杂度或者这个特征的数据点异常值较多 ,所以算法在学习的时候尽量减少这个特征的影响(甚至删除某个特征的影响),这就是正则化。
正则化如何消除异常点带来的w值过大过小的影响?
- 在损失函数中增加正则化项
- 分为L1正则化、L2正则化
注:调整时候,算法并不知道某个特征影响,而是去调整参数得出优化的结果
L1正则化
J ( w ) = M S E ( w ) + α ∑ i = 1 n ∣ w i ∣ M S E ( w ) = 1 m ∑ i = 0 m ( h ( x i ) − y i ) 2 J(w)=MSE(w) + α \sum_{i=1}^{n}|w_i| \\ MSE(w)=\frac{1}{m}\sum_{i=0}^{m}(h(x_i)-y_i)^2 J(w)=MSE(w)+αi=1∑n∣wi∣MSE(w)=m1i=0∑m(h(xi)−yi)2
α叫做惩罚系数,该值越大则权重调整的幅度就越大,表示对特征权重惩罚力度越大。L1 正则化会使得权重趋向于 0,甚至等于 0,使得某些特征失效,达到特征筛选的目的
- 作用:可以使得其中一些W的值直接为0,删除这个特征的影响。例如上图中直接将θ3和θ4变为0。
- 使用L1正则化的线性回归模型是:LASSO回归
L2正则化
J ( w ) = M S E ( w ) + α ∑ i = 1 n w i 2 J(w)=MSE(w) + α \sum_{i=1}^{n}w_i^2 J(w)=MSE(w)+αi=1∑nwi2
α叫做惩罚系数,该值越大则权重调整的幅度就越大,表示对特征权重惩罚力度越大。L2正则化会使得权重趋向于0,一般不等于0.
- 作用:可以使得其中一些W的都很小,都接近于0,削弱某个特征的影响。例如上图中直接将θ3和θ4变特别小。
- 优点:越小的参数说明模型越简单,越简单的模型则越不容易产生过拟合现象
- 使用L1正则化的线性回归模型是:岭回归
一般使用L2正则化
2.3.2、维灾难
维数越高,分类性能越优。然而随着维度的增加,分类器性能逐步上升,到达某点之后,其性能便逐渐下降。
- 训练集的维度越多,过度拟合的风险就越大。
2.4、正则化线性模型
2.4.0、范数
范数是数学中的一种基本概念,具有长度的意义。
- L1范数:向量中各个元素的绝对值之和
- L2范数:向量的模长,每个元素平方求和,再开平方根
L1范数:
x T = ( 1 , 2 , − 3 ) ∣ ∣ x ∣ ∣ 1 = ∣ 1 ∣ + ∣ 2 ∣ + ∣ − 3 ∣ = 6 x^T=(1,2,-3) \\ ||x||_1 = |1|+|2|+|-3|=6 xT=(1,2,−3)∣∣x∣∣1=∣1∣+∣2∣+∣−3∣=6
L2范数:
x T = ( 1 , 2 , − 3 ) ∣ ∣ x ∣ ∣ 2 = 1 2 + 2 2 + ( − 3 ) 2 x^T=(1,2,-3) \\ ||x||_2 = \sqrt{1^2+2^2+(-3)^2} \\ xT=(1,2,−3)∣∣x∣∣2=12+22+(−3)2
注意:
x T x = 1 2 + 2 2 + ( − 3 ) 2 所以 : x T x = ∣ ∣ x ∣ ∣ 2 2 x^Tx = 1^2+2^2+(-3)^2 \\ 所以: x^Tx = ||x||_2^2 xTx=12+22+(−3)2所以:xTx=∣∣x∣∣22
2.4.1、岭回归 Ridge Regression(L2正则化)
岭回归是一种线性回归的改进方法 ,主要用于解决普通最小二乘法(OLS)在多重共线性 或高维数据 中出现的过拟合和系数不稳定的问题。它通过在损失函数中引入L2正则化项 ,对模型系数进行约束,从而提升模型的泛化能力。
J ( β ) = ∑ i = 1 m ( h ( x i ) − y i ) 2 + λ ∑ j = 1 n β j 2 J(β) = \sum_{i=1}^{m}(h(x_i)-y_i)^2 + λ\sum_{j=1}^{n}β_j^2 J(β)=i=1∑m(h(xi)−yi)2+λj=1∑nβj2
- 公式第一项就是我们的最小二乘法OLS的平方误差损失
- 第二项是L2正则化项,惩罚较大的系数值
- λ 是正则化强度参数 ,控制惩罚力度:
- λ=0:岭回归退化为普通线性回归
- λ→∞:所有系数趋近于0,模型过于简单
2.4.2、套索回归Lasso Regression(L1正则化)
Lasso 回归是一种线性回归的正则化改进方法 ,通过引入L1正则化 ,在降低模型复杂度的同时实现自动特征选择 。与岭回归(Ridge Regression)不同,Lasso 能够将部分系数压缩至零,从而筛选出对目标变量最相关的特征。
J ( β ) = ∑ i = 1 m ( h ( x i ) − y i ) 2 + λ ∑ j = 1 n ∣ β j ∣ J(β) = \sum_{i=1}^{m}(h(x_i)-y_i)^2 + λ\sum_{j=1}^{n}|β_j| J(β)=i=1∑m(h(xi)−yi)2+λj=1∑n∣βj∣
- 第一项:普通最小二乘(OLS)的平方误差损失。
- 第二项:L1正则化项,惩罚系数的绝对值之和。
- λ 是正则化强度参数,控制稀疏性和拟合程度的平衡:
- λ=0:退化为普通线性回归。
- λ→∞:所有系数被压缩至零。
2.4.3、岭回归API
python
sklearn.linear_model.Ridge(alpha=1.0, fit_intercept=True,solver="auto", normalize=False)
- alpha,也就是上面公式的λ,默认为1
- fit_intercept:是否为该模型拟合截距
- solve:根据数据自动选择优化方法
- 'auto'根据数据类型自动选择求解器
- 'sag'使用随机平均梯度下降
- normalize:数据是否进行标准化
属性:
- Ridge.coef_:回归权重
- Ridge.intercept_:回归偏置
alpha表示正则化系数,正则化系数越大,表示正则化力度(越大),所得模型的权重系数(越小),反之,所得模型的权重系数(越大)。
我们之前的梯度下降算法API为
pythonsklearn.linear_model.SGDRegressor(loss="squared_loss", fit_intercept=True, learning_rate ='invscaling', eta0=0.01)
- Ridge方法相当于梯度下降算法
SGDRegressor(penalty='l2', loss="squared_loss")
,只不过SGDRegressor实现了一个普通的随机梯度下降学习,推荐使用Ridge(实现了随机平均梯度下降SAG)我们之前的交叉验证API为:
pythonsklearn.model_selection.GridSearchCV(estimator, param_grid=None,cv=None)
岭回归也对交叉验证API做了封装,称为岭回归交叉验证:
python
# 获取数据
from sklearn.datasets import fetch_openml
import pandas as pd
import numpy as np
# 分割数据
from sklearn.model_selection import train_test_split
# 数据标准化
from sklearn.preprocessing import StandardScaler
# 机器学习-线性回归(梯度下降法)
from sklearn.linear_model import Ridge
from sklearn.linear_model import RidgeCV
# 模型评估
from sklearn.metrics import mean_squared_error
def linear_model():
"""
线性回归:梯度下降法
:return:None
"""
# 1.获取数据
boston = fetch_openml(name='boston', version=1, as_frame=True)
# 选择特征值和目标值
X = boston.data
Y = boston.target
# 2.分割训练集和测试集
# 特征值的训练集x_train、特征值的测试集x_test、目标值的训练集y_train、目标值的测试集y_test
x_train, x_test, y_train, y_test = train_test_split(X,Y, test_size=0.3, random_state=10)
# 3.特征工程-标准化
# 对特征值的训练集x_train、特征值的测试集x_test进行数据标准化
transfer = StandardScaler()
x_train = transfer.fit_transform(x_train)
x_test = transfer.fit_transform(x_test)
# 4.机器学习-线性回归(岭回归)
ridge = Ridge(alpha=1)
# ridgecv = RidgeCV(alphas=(0.1, 1, 10))
# 5.训练模型
ridge.fit(x_train, y_train)
# 5.模型评估
# 5.1 预测训练集和测试集
# 特征值的测试集x_test
y_predict = ridge.predict(x_test)
print("预测值为: \n",y_predict)
print("模型中的系数为:\n: \n",ridge.coef_)
print("模型中的偏置为:\n: \n",ridge.intercept_)
# 5.2 评价
# 均方误差
error = mean_squared_error(y_test, y_predict)
print("误差为:\n", error)
return None
linear_model()

2.5、回归模型评估方法
为什么要进行线性回归模型的评估:
- 我们希望衡量预测值和真实值之间的差距,会用到MAE、MSE、RMSE多种测评函数进行评价。
2.5.1、均方误差MSE
均方误差MAE:Mean Squared Error
1 m ∑ i = 0 m ( h ( x i ) − y i ) 2 \frac{1}{m}\sum_{i=0}^{m}(h(x_i)-y_i)^2 m1i=0∑m(h(xi)−yi)2
- m为样本数量,h(xi)为真实值,yi为预测值,MSE越小模型预测越准确。
2.5.2、平方绝对误差MAE
平方绝对误差MAE:Mean Absolute Error
1 m ∑ i = 0 m ( h ( x i ) − y i ) 2 \frac{1}{m}\sum_{i=0}^{m}(h(x_i)-y_i)^2 m1i=0∑m(h(xi)−yi)2
- MAE越小模型预测越精准
2.5.3、均方根误差RMSE
1 m ∑ i = 0 m ( h ( x i ) − y i ) 2 \sqrt{\frac{1}{m}\sum_{i=0}^{m}(h(x_i)-y_i)^2} m1i=0∑m(h(xi)−yi)2
-
RMSE是MSE的平方根,某些情况下比MSE更有用
-
但实际情况更多使用MAE和MSE
一般使用MAE和RMSE这两个指标
- MAE反应的是真实的平均误差,RMSE会将误差大的数据点放大。
综合结论:
- MAE对误差大小不敏感
- RMSE会放大预测误差较大的样本的影响
- RMSE对异常数据敏感
- 评价指标要综合的看
2.6、模型的保存和加载
python
from sklearn.externals import joblib
# 保存模型
# 参数一为训练好的模型对象,参数二为保存成什么文件
joblib.dump(sgd, 'test.pkl')
# 加载模型
sgd = joblib.load('test.pkl')
注意:
- 保存文件,后缀名是
**.pkl
- 加载模型是需要通过一个变量进行承接
2.7、总结

3、逻辑回归
逻辑回归的原理:解决分类问题,把线性回归的输出作为逻辑回归的输入
- 分类分为二分类和多分类,逻辑回归是用来解决二分类问题的
- 逻辑回归就是在线性回归后加一个激活函数
逻辑回归(Logistic Regression)是机器学习中的一种分类模型,逻辑回归是一种分类算法,虽然名字中带有回归。由于算法的简单和高效,在实际中应用非常广泛。逻辑回归的应用场景:

- 广告点击率
- 是否为垃圾邮件
- 是否患病
- 金融诈骗
- 虚假账号
看到上面的例子,我们可以发现其中的特点,那就是都属于两个类别之间的判断。逻辑回归就是解决二分类问题的利器
要想掌握逻辑回归,必须掌握两点:
-
逻辑回归中,其输入值是什么
- 逻辑回归的输入就是线性回归的输出,也就是下面公式的h(w)
线性回归 : h ( w ) = w 1 x 1 + w 2 x 2 + w 3 x 3 + . . . b 线性回归:h(w) = w_1x_1+ w_2x_2+ w_3x_3+... b 线性回归:h(w)=w1x1+w2x2+w3x3+...b
- 逻辑回归的输入就是线性回归的输出,也就是下面公式的h(w)
-
如何判断逻辑回归的输出,输出为激活函数的结果,也就是概率值,范围在[0,1]
- 激活函数sigmoid函数
S ( x ) = 1 1 + e − x S(x) = \frac{1}{1+e^{-x}} S(x)=1+e−x1
- 激活函数sigmoid函数
首先,我们处理二分类问题。由于分成两类,我们便让其中一类标签为0,另一类为1。我们需要一个函数,对于输入的每一组数据xi都能映射成0~1之间的数。并且如果函数值大于0.5,就判定属于1,否则属于0。而且函数中需要待定参数,通过利用样本训练,使得这个参数能够对训练集中的数据有很准确的预测。这个函数就是sigmoid函数。

线性回归的结果输入到激活函数,激活函数可以把结果映射到[0,1]之间的一个概率值,默认0.5为阈值。sigmod函数输出结果就是一个概率值
- 我们将<0.5的记为A类别,将>0.5的记为B类别。那么就可以通过逻辑回归将线性回归分为两个类别。
- 关于逻辑回归的阈值是可以进行改变的,比如上面举例中,如果你把阈值设置为0.6,那么输出的结果0.55,就属于B类。
3.0、极大似然估计
根据观测到 的结果(训练集)来估计模型算法中的未知参数。
3.1、逻辑回归的损失和优化
在线性回归中,我们用MAE、MSE衡量线性回归的损失 ,在逻辑回归中,当预测结果不对的时候,我们该怎么衡量其损失呢?

逻辑回归的损失,称之为对数似然损失 (在深度学习中,叫做交叉熵损失 ),公式如下:
c o s t ( h θ ( x ) , y ) = { − l o g ( h θ ( x ) ) , y = 1 − l o g ( 1 − h θ ( x ) ) , y = 0 cost(h_θ(x),y)=\begin{cases} -log(h_θ(x)),y=1 \\ -log(1-h_θ(x)), y=0 \end{cases} cost(hθ(x),y)={−log(hθ(x)),y=1−log(1−hθ(x)),y=0
- 其中y为真实值,hθ(x)为预测值。

无论何时,我们都希望损失函数值,越小越好
分情况讨论,对应的损失函数值:
- 当y=1时,我们希望预测值hθ(x)越大越好。
- 当y=0时,我们希望预测值hθ(x)越小越好。
所以我们可以用一个式子来写损失函数:
c o s t ( h θ ( x ) , y ) = − ∑ i = 1 m y i l o g ( h θ ( x ) ) + ( 1 − y i ) l o g ( 1 − h θ ( x ) ) cost(h_θ(x),y) = -\sum_{i=1}^{m} y_ilog(h_θ(x)) + (1-y_i)log(1-h_θ(x)) cost(hθ(x),y)=−i=1∑myilog(hθ(x))+(1−yi)log(1−hθ(x))

对于逻辑回归的优化:同样使用梯度下降优化算法,去减少损失函数的值。这样去更新逻辑回归前面对应算法的权重参数θ,提升原本属于1类别的概率,降低原本是0类别的概率。
3.2、逻辑回归的API
python
# 默认将类别数量少的当做正例
sklearn.linear_model.LogisticRegression(solver='lbfgs', penalty='l2', C = 1.0)
- solver可选参数:{'liblinear'、 'sag'、 'saga'、'newton-cg'、 'lbfgs'},
- 默认: 'lbfgs';用于优化问题的算法。
- 对于小数据集来说,'liblinear'是个不错的选择,而'sag'和'saga'对于大型数据集会更快。
- sag、saga 支持 L2 正则化或者没有正则化
- liblinear 和 saga 支持 L1 正则化
- penalty:正则化的种类
None
:不添加惩罚;'l2'
:添加 L2 惩罚项,这是默认选择;'l1'
:添加 L1 惩罚项;'elasticnet'
:添加 L1 和 L2 惩罚项
- C:正则化强度的倒数;必须是正浮点数。与支持向量机一样,较小的值指定更强的正则化
外链图片转存中...(img-cet0MfBB-1755497151134)\].assets/74.png) ### 3.3、肿瘤预测 699条样本,共11列数据,第一列是编号id,第2列到第10列分别是与肿瘤相关的医学特征,最后一列表示肿瘤类型的数值。  ```python import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression # 从网址获取数据,有时候需要认证,导入ssl让其不验证 import ssl ssl._create_default_https_context = ssl._create_unverified_context # 1.获取数据 names = ['Sample code number', 'Clump Thickness', 'Uniformity of Cell Size', 'Uniformity of Cell Shape', 'Marginal Adhesion', 'Single Epithelial Cell Size', 'Bare Nuclei', 'Bland Chromatin', 'Normal Nucleoli', 'Mitoses', 'Class'] data = pd.read_csv("https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/breast-cancer-wisconsin.data", names=names) # 2.基本数据处理 # 2.1 缺失值处理(将 ? 的值替换为NaN,然后删掉NaN) data = data.replace(to_replace="?", value=np.nan) data = data.dropna() # 2.2 确定特征值,目标值 # 特征值: 第二列到第十列的数据 # 目标值: data数据的Class列 X = data.iloc[:, 1:10] Y = data["Class"] # 2.3 分割训练集和测试集 # 特征值的训练集x_train、特征值的测试集x_test、目标值的训练集y_train、目标值的测试集y_test x_train, x_test, y_train, y_test = train_test_split(X, Y, random_state=22) # 3.特征工程(标准化) # 对特征值的训练集x_train、特征值的测试集x_test进行数据标准化 transfer = StandardScaler() x_train = transfer.fit_transform(x_train) x_test = transfer.transform(x_test) # 4.机器学习-逻辑回归 logis = LogisticRegression() logis.fit(x_train, y_train) # 5.模型评估 y_predict = logis.predict(x_test) print("预测值为:\n",y_predict) score = logis.score(x_test, y_test) print("准确度为:\n",score) ```  在很多分类场景当中我们不一定只关注预测的准确率!!!!! 比如以这个癌症举例子!!!**我们并不关注预测的准确率,而是关注在所有的样本当中,癌症患者有没有被全部预测(检测)出来。** ### 3.4、(逻辑回归)分类评估方法 只做预测准确率能满足各种场景需要吗?比如上述癌症检测的案例:癌症患者有没有被全部预测(检测)出来 #### 3.4.1、精确率和召回率(查全率) 在分类任务下,预测结果(Predicted Condition)与正确标记(True Condition)之间存在四种不同的组合,构成混淆矩阵(适用于多分类)  > 召回率也叫查全率 还有一种F1-score的评估标准,反应模型的稳健性。  #### 3.4.2、分类评估报告API ```python sklearn.metrics.classification_report(y_true, y_pred, labels=[], target_names=None ) ``` * y_true:真实目标值 * y_pred:估计器预测目标值 * labels:指定类别对应的数字 * target_names:目标类别名称 * return:每个类别精确率与召回率 ```python ret = classification_report(y_test, y_predict, labels=(2,4), target_names=("良性", "恶性")) print(ret) ```  召回率是查的全不全,例如图中的0.98,即表示在100个良性里面查了98个。 > 假设这样一个情况,如果99个样本癌症,1个样本非癌症,不管怎样我全都预测正例(默认癌症为正例),准确率就为99%,召回率是100%但是这样效果并不好,这就是样本不均衡下的评估问题 问题:**如何衡量样本不均衡下的评估**? #### 3.4.3、ROC曲线和AUC指标(不重要)  * TPR就是召回率,TPR就是预测类别正确的情况,FPR就是预测类别错误的情况。 * ROC曲线是一种常用于评估分类模型性能的**可视化工具** 。ROC曲线以模型的真正率TPR为纵轴,假正率FPR为横轴,它将模型在**不同阈值** 下的表现以曲线的形式展现出来。 * ROC曲线的横轴就是FPRate,纵轴就是TPRate,当二者相等时,表示的意义则是:对于不论真实类别是1还是0的样本,分类器预测为1的概率是相等的,此时AUC为0.5(这就是模型在胡说) * ROC曲线的优劣可以通过曲线下的\*\*面积(AUC)\*\*来衡量,**AUC越大表示分类器性能越好**。  ROC 曲线图像中,4 个特殊点的含义: * 点(0, 0) :所有的负样本都预测正确,所有的正样本都预测为错误 。相当于点的(FPR值0, TPR值0) * 点(1, 0) :所有的负样本都预测错误,所有的正样本都预测错误。相当于点的(FPR值1, TPR值0)最不好的效果 * 点(1, 1):所有的负样本都预测错误,表示所有的正样本都预测正确。相当于点的(FPR值1,TPR值1) * 点(0, 1):所有的负样本都预测正确,表示所有的正样本都预测正确 。相当于点的(FPR值0,TPR值) * 曲线越靠近 (0,1) 点则模型对正负样本的辨别能力就越强 *** ** * ** *** * AUC的概率意义是随机取一对正负样本,正样本得分大于负样本得分的概率 * AUC的范围在\[0, 1\]之间,并且越接近1越好,越接近0.5属于乱猜 * **AUC=1,完美分类器,采用这个预测模型时,不管设定什么阈值都能得出完美预测。绝大多数预测的场合,不存在完美分类器。** AUC计算的API如下: ```python # 计算ROC曲线面积,即AUC值 sklearn.metrics.roc_auc_score(y_true, y_score) ``` * y_true:每个样本的真实类别,必须为0(反例),1(正例)标记 * y_score:预测得分,可以是正类的估计概率、置信值或者分类器方法的返回值 ```python # 0.5~1之间,越接近于1约好 y_test = np.where(y_test > 2.5, 1, 0) print("AUC指标:", roc_auc_score(y_test, y_predict) ```  ## 4、决策树算法 决策树思想的来源非常朴素,程序设计中的条件分支结构就是if-else结构,最早的决策树就是利用这类结构分割数据的一种分类学习方法。 **决策树:是一种树形结构,其中每个内部节点表示一个属性上的判断,每个分支代表一个判断结果的输出,最后每个叶节点代表一种分类结果,本质是一颗由多个判断节点组成的树**。  我们要怎么知道翅膀数就是根节点呢,我为何不能让翅膀大小成为根节点?其实这个就是决策树的构造过程中需要考虑的问题,而要看哪个属性做为根节点或者后续节点,我们要用信息增益来判别。 ### 4.1、信息熵 物理学上,**熵**是混乱程度的度量。可以理解为熵表示的是随机变量不确定度的衡量。例如概率0.5比概率0.3的不确定度要高。  信息熵 (information entropy)是度量样本集合纯度最常用的一种指标。假定当前样本集合 D 中第 i 类样本所占的比例为p~i~(i=1,2,...),则D的信息熵定义为: E n t ( D ) = − ∑ i = 1 ∣ y ∣ p i l o g 2 p i = − p 1 l o g 2 p 1 − p 2 l o g 2 p 2 − p 3 l o g 2 p 3 − . . . . . − p n l o g 2 p n Ent(D) = -\\sum_{i=1}\^{\|y\|}p_i log_2p_i = -p_1log_2p_1-p_2log_2p_2-p_3log_2p_3-.....-p_nlog_2p_n Ent(D)=−i=1∑∣y∣pilog2pi=−p1log2p1−p2log2p2−p3log2p3−.....−pnlog2pn 其中,\|y\|是**类别**,信息熵Ent(D)的值越小,则D的纯度越高。 *** ** * ** ***  * 正例有8个,负例有9个,共有17个样本,类别\|y\|=2。则信息熵的计算如图。 假如我们现在用色泽来计算信息熵:     ### 4.2、决策树的划分依据-信息增益(ID3决策树) 信息增益:通过使用某个特征X来划分数据集后,目标变量Y的不确定性(熵)减少的程度。我们回忆下信息熵的计算公式: E n t ( D ) = − ∑ i = 1 n p i l o g 2 p i Ent(D) = -\\sum_{i=1}\^{n}p_ilog_2p_i Ent(D)=−i=1∑npilog2pi * k表示有几个不同的输出。 * p~i~表示第i类输出所占的比例。 信息增益的计算公式为: E n t ( D ∣ a ) = E n t ( D ) − ∑ i = 1 n ∣ D v ∣ ∣ D ∣ E n t ( D v ) Ent(D\|a) = Ent(D)-\\sum_{i=1}\^{n} \\frac{\|D\^v\|}{\|D\|}Ent(D_v) Ent(D∣a)=Ent(D)−i=1∑n∣D∣∣Dv∣Ent(Dv) *** ** * ** *** 如下图,第一列为论坛号码,第二列为性别,第三列为活跃度,最后一列用户是否流失。我们要解决一个问题:性别和活跃度两个特征,我们将哪个作为决策树的根节点?  计算整体熵,正例有5个,负例有10个,共有15个样本,类别\|y\|=2。  ### 4.2、决策树的划分依据-基尼值和基尼系数(CART决策树) > Cart模型是一种决策树模型,它即可以用于分类,也可以用于回归。 > > Cart**分类生成树**采用的基尼指数最小化策略。 > > Cart**回归树**使用平方误差最小化策略。 \*\*基尼值Gini(D):\*\*从数据集D中随机抽取两个样本,其类别标记不一致的概率。**故Gini(D)值越小,数据集D的纯度越高。** 数据集D的纯度可用基尼值来度量: G i n i ( D ) = 1 − ∑ k = 1 ∣ y ∣ p k 2 Gini(D) = 1-\\sum_{k=1}\^{\|y\|}p_k\^2 Gini(D)=1−k=1∑∣y∣pk2 其中: P k = C k D P_k = \\frac{C\^k}{D} Pk=DCk * D为样本的所有数量,C^k^为第k类样本的数量。 \*\*基尼指数Gini_index(D):\*\*一般,选择使划分后基尼系数最小的属性作为最优化分属性。 G i n i _ i n d e x ( D , a ) = ∑ v = 1 V ∣ D v ∣ ∣ D ∣ G i n i ( D v ) Gini\\_index(D,a) = \\sum_{v=1}\^{V} \\frac{\|D\^v\|}{\|D\|}Gini(D\^v) Gini_index(D,a)=v=1∑V∣D∣∣Dv∣Gini(Dv) 例如用英语来划分,某学校复试统计面试者,其中分为四级、六级、没四六级。 | | 正类样本数(过面试) | 负类样本数(没过面试) | 总样本数 | |---------|------------|-------------|------| | D~cet4~ | 1 | 4 | 5 | | D~cet6~ | 5 | 1 | 6 | | D~None~ | 1 | 0 | 1 | 基尼值 : G i n i ( D c e t 4 ) = 1 − \[ ( 1 5 ) 2 + ( 4 5 ) 2 ) = 0.28 基尼值:Gini(D_{cet4}) = 1 - \[(\\frac{1}{5})\^2 + (\\frac{4}{5})\^2) = 0.28 基尼值:Gini(Dcet4)=1−\[(51)2+(54)2)=0.28 基尼值 : G i n i ( D c e t 6 ) = 1 − \[ ( 5 6 ) 2 + ( 1 6 ) 2 ) = 0.32 基尼值:Gini(D_{cet6}) = 1 - \[(\\frac{5}{6})\^2 + (\\frac{1}{6})\^2) = 0.32 基尼值:Gini(Dcet6)=1−\[(65)2+(61)2)=0.32 基尼值 : G i n i ( D N o n e ) = 1 − \[ ( 1 2 + 0 2 ) = 0.32 基尼值:Gini(D_{None}) = 1 - \[(1\^2 + 0\^2) = 0.32 基尼值:Gini(DNone)=1−\[(12+02)=0.32 基尼指数 : G i n i _ i n d e x ( D , 英语 ) = 5 12 ∗ 0.28 + 6 12 ∗ 0.32 + 1 12 ∗ 0 = 0.2766 基尼指数:Gini\\_index(D,英语) = \\frac{5}{12}\*0.28+\\frac{6}{12}\*0.32+\\frac{1}{12}\*0 = 0.2766 基尼指数:Gini_index(D,英语)=125∗0.28+126∗0.32+121∗0=0.2766 这就是我们基于英语成绩算出来的基尼指数,我们也可以基于数学成绩、专业课成绩来计算基尼指数,最终选择最小的基尼指数对应的特征来作为划分的依据。 > 注意: > > 信息增益(ID3)、信息增益率值越大(C4.5),则说明优先选择该特征。 > > 基尼指数值越小(cart),则说明优先选择该特征。 ### 4.3、cart剪枝 > 白话:把叶子节点、子节点删掉,用更大的\*\*叶子节点(子树)\*\*替换叶子节点 剪枝是决策树学习算法对付 **过拟合** 的主要手段。决策树剪枝的基本策略有**预剪枝(pre-pruning)**和**后剪枝(post-pruning)**。 * 预剪枝是指在决策树生成过程中,对每个结点在划分前先进行估计,若当前结点的划分不能带来决策树泛化性能提升,则停止划分并将当前结点标记为叶结点; * 后剪枝则是先从训练集生成一棵完整的决策树,然后自底向上地对非叶结点进行考察,若将该结点对应的子树替换为叶结点能带来决策树泛化性能提升,则将该子树替换为叶结点。   #### 4.3.1、预剪枝 在用属性**脐部** 划分之后,我们要考虑**脐部**划分的好不好。 * 若不进行划分,则该根结点会被标记为叶子结点,其类别标记为训练样例数最多的类别,也就是**脐部** 变为**好瓜** 。 * 用验证集来测试,有3个好瓜,4个坏瓜。则验证集精度为 3/7 = 42.9% * 若用**脐部** 划分,则子节点会被标记为叶子结点,其类别标记为训练样例数最多的类别,也就是色泽、根蒂变为好瓜。 * 用验证集来测试,凹陷是好瓜,2个验证正确。稍凹是好瓜,1个验证正确。平坦是坏瓜,2个验证正确。则则验证集精度为 5/7 = 71.4%    则最终得到的决策树为:  #### 4.3.2、后剪枝     时间开销: * 预剪枝:测试时间开销降低(根本没长出大树),训练时间开销降低(很多分支就不长了) * 后剪枝:测试时间开销降低(树变小了),训练时间开销**增加**(要先长出完整树) 过/欠拟合风险:剪枝就是为了降低过拟合风险 * 预剪枝:过拟合风险**降低** ,欠拟合风险**增加**(模型可能没学好) * 后剪枝:过拟合风险**降低**,欠拟合风险基本不变(学完了倒过来剪枝) *** ** * ** *** 预剪枝 * 优点:预剪枝使决策树的**很多分支没有展开** ,不单降低了过拟合风险,还显著**减少了决策树的训练、测试时间开销** * 缺点:有些分支的当前划分虽不能提升泛化性能,但后续划分却有可能导致性能的显著提高;预剪枝决策树也带来了**欠拟合的风险** 后剪枝: * 优点:比预剪枝保留了更多的分支。一般情况下,后剪枝决策树的欠拟合风险很小,**泛化性能往往优于预剪枝** * 缺点:后剪枝先生成,后剪枝。自底向上地对树中所有非叶子节点进行逐一考察,**训练时间开销比未剪枝的决策树和预剪枝的决策树都要大得多。** ### 4.4、特征工程-特征提取 **将任意数据(如文本或图像)转换为可用于机器学习的数字特征**,特征值化是为了计算机更好的去理解数据。 #### 4.4.1、字典特征提取 ```python sklearn.feature_extraction.DictVectorizer(sparse=True,...) ``` ```python from sklearn.feature_extraction import DictVectorizer def dict_demo(): """ 对字典类型的数据进行特征抽取 :return: None """ data = [{'city': '北京','temperature':100}, {'city': '上海','temperature':60}, {'city': '深圳','temperature':30}] # 1、实例化一个转换器类 transfer = DictVectorizer(sparse=False) # 2、调用fit_transform data = transfer.fit_transform(data) print("返回的结果:\n", data) # 打印特征名字 print("特征名字:\n", transfer.get_feature_names_out()) return None dict_demo() ```  当`transfer = DictVectorizer(sparse=Ture)`时  #### 4.4.2、文本特征提取 ```python sklearn.feature_extraction.text.CountVectorizer(stop_words=[]) ``` ```python from sklearn.feature_extraction.text import CountVectorizer def text_count_demo(): """ 对文本进行特征抽取,countvetorizer :return: None """ data = ["life is short,i like like python", "life is too long,i dislike python"] # 1、实例化一个转换器类 # transfer = CountVectorizer(sparse=False) # 文本特征提取没有sparse这个参数,需要用toarray()来查看 transfer = CountVectorizer() # 2、调用fit_transform data = transfer.fit_transform(data) print("文本特征抽取的结果:\n", data.toarray()) print("返回特征名字:\n", transfer.get_feature_names_out()) return None text_count_demo() ```  > 注意单个单词和标点符号不做统计。 > > 若加上参数`CountVectorizer(stop_words=["dislike"])`,则不统计 `dislike` 的次数和位置。 ### 4.5、决策树算法api  ```python class sklearn.tree.DecisionTreeClassifier(criterion='gini', max_depth=None,random_state=None) ``` * criterion: gini、entropy,前者代表基尼系数,后者代表信息增益。默认为gini * max_depth:决策树的最大深度。 ### 4.6、泰坦尼克号乘客生存预测 待更新。 ### 4.7、回归决策树 前面已经讲到,关于数据类型,我们主要可以把其分为两类,**连续型数据和离散型数据**。在面对不同数据时,决策树也可以分为两大类型: * **分类决策树** 和**回归决策树**。 * 前者主要用于处理离散型数据,后者主要用于处理连续型数据。 * 离散型数据是有限的,连续型变量是无限的。 > 我们之前用信息增益、信息增益率、基尼指数其实都是分类决策树,都是用来处理离散型数据的,那么连续型数据的处理就要用回归决策树。 对于回归决策树的构造,我们要找到一个阈值a,将>阈值a的归为一类D+,≤阈值a的归为另一类D-,然后就可以分别计算这两类的信息熵Ent(D+)、Ent(D-)和信息增益(Gain)。那么这个阈值怎么取呢?  假如有n个样本,我们要将这n个样本取n-1个区间的中点,例如下面有12个比试成绩从大到小排列,我们要取11个中点值,(96+95)/2=95.5...最终取得 \[ 95.5 , 95 , 92.5 , 90 , 88.5 , 85.5 , 82 , 79 , 75 , 71 , 68 \] \[95.5,95,92.5,90,88.5,85.5,82,79,75,71,68\] \[95.5,95,92.5,90,88.5,85.5,82,79,75,71,68
分别以这11个值作为阈值,得到的信息增益(Gain)如图:
外链图片转存中...(img-NdRnROKy-1755497151141)\].assets/109.png) 我们选择信息增益(Gain)最大的一个: G a i n ( D , 入职笔试成绩 ) = 0.655 Gain(D,入职笔试成绩) = 0.655 Gain(D,入职笔试成绩)=0.655 当然有可能还有其他特征,我们也这样计算出信息增益: G a i n ( D , 英语 ) = 0.3742 Gain(D,英语) = 0.3742 Gain(D,英语)=0.3742 我们发现入职比试成绩的信息增益最高,则选取入职比试成绩作为划分属性,以82分作为划分界限。 CART 回归树和 CART 分类树的不同之处在于: * CART 分类树预测输出的是一个**离散值** ,CART 回归树预测输出的是一个**连续值** * CART 分类树使用**基尼指数** 作为划分、构建树的依据,CART 回归树使用**平方损失** * 分类树使用叶子节点**多数类别作为预测类别** ,回归树则采用叶子节点里**均值作为预测输出**