机器学习算法-K-近邻算法
1.K-近邻算法
1.1、 K近邻算法简介
1、什么是K-近邻算法
1.1 K-近邻算法(KNN)概念
K Nearest Neighbor 算法又叫KNN算法,这个算法是机器学习里面一个比较经典的算法,总体来说KNN算法是相对比较容易理解的算法
- 定义
如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。
-
距离公式
两个样本的距离可以通过如下公式计算,又叫欧氏距离
二维平面上点a(x1,y1)与b(x2,y2)间的欧氏距离:
d_{12}=\sqrt{(x1-x2)^2+(y1-y2)^2}
三维空间点a(x1,y1,z1)与b(x2,y2,z3)间的欧氏距离:
d_{12}=\sqrt{(x1-x2)^2+(y1-y2)^2+(z1-z2)^2}
n维空间点a(x11,x12,…,x1n)与b(x21,x22,…,x2n)间的欧氏距离(两个n维向量):
d_{12}=\sqrt{\sum_{k=1}^n(x_{1k}-x_{2k})^2}
1.2 电影类型分析
序号 | 电影名称 | 搞笑镜头 | 拥抱镜头 | 打斗镜头 | 电影类型 |
---|---|---|---|---|---|
1 | 功夫熊猫 | 39 | 0 | 31 | 喜剧片 |
2 | 二次曝光 | 2 | 3 | 55 | 爱情片 |
3 | 代理情人 | 9 | 38 | 2 | 爱情片 |
4 | 叶问3 | 3 | 2 | 65 | 动作片 |
5 | 新步步惊心 | 8 | 34 | 17 | 爱情片 |
6 | 谍影重重 | 5 | 2 | 57 | 动作片 |
7 | 美人鱼 | 21 | 17 | 5 | 喜剧片 |
8 | 宝贝当家 | 45 | 2 | 9 | 喜剧片 |
9 | 唐人街探案 | 23 | 3 | 17 | ? |
《唐人街探案》vs《二次曝光》
\begin{aligned}
d&=\sqrt{\sum_{i=1}^n(x_i-y_i)2}\
&=\sqrt{(23-2)^2+(3-3)^2+(17-55)^2}\
&=43.42
\end{aligned}
分别计算每个电影和被预测电影的距离,然后求解:
序号 | 电影名称 | 搞笑镜头 | 拥抱镜头 | 打斗镜头 | 电影类型 | 距离 | k=5时 |
---|---|---|---|---|---|---|---|
1 | 功夫熊猫 | 39 | 0 | 31 | 喜剧片 | 21.47 | v |
2 | 二次曝光 | 2 | 3 | 55 | 爱情片 | 43.42 | |
3 | 代理情人 | 9 | 38 | 2 | 爱情片 | 40.57 | v |
4 | 叶问3 | 3 | 2 | 65 | 动作片 | 52.01 | |
5 | 新步步惊心 | 8 | 34 | 17 | 爱情片 | 34.44 | v |
6 | 谍影重重 | 5 | 2 | 57 | 动作片 | 43.87 | |
7 | 美人鱼 | 21 | 17 | 5 | 喜剧片 | 18.55 | v |
8 | 宝贝当家 | 45 | 2 | 9 | 喜剧片 | 23.43 | v |
9 | 唐人街探案 | 23 | 3 | 17 | ? | —- |
5个最近的样本中3个喜剧片,2个爱情片,因此《唐人街探案》为 喜剧片。
1.3 KNN算法流程总结
1)计算已知类别数据集中的点与当前点之间的距离
2)按距离递增次序排序
3)选取与当前点距离最小的K个点
4)统计前k个点所在的类别出现的频率
5)返回前k个点出现频率最高的类别作为当前点的预测分类
2 总结
- K-近邻算法简介
- 定义:就是通过你的“邻居”来判断你属于哪个类别
- 如何计算你到你的“邻居”的距离:一半使用欧氏距离
1.2、 K近邻算法api初步使用
1、Scikit-learn工具介绍
https://scikit-learn.org/stable/
1.1 安装
#pip3 install scikit-learn==0.19.1
pip3 install scikit-learn==1.2.0
安装好之后可以使用命令查看安装是否成功
import sklearn
- 注:安装scikit-learn 需要Numpy,Scipy等库
1.2 Scikit-learn包含的内容
- Classification
- Identifying which category an object belongs to.
- Regression
- Predicting a continuous-valued attribute associated with an object.
- Clustering
- Automatic grouping of similar objects into sets.
- Dimensionality reduction
- Reducing the number of random variables to consider.
- Model selection
- Comparing, validating and choosing parameters and models.
- Preprocessing
- Feature extraction and normalization.
2 K近邻算法API
- sklearn.neighbors.KNeighborsClassfier(n_neighbors=5)
- n_neighbors: int,可选(默认=5),k_neighbors查询默认使用的邻居数
3 案例
3.1 步骤分析
- 1.获取数据集
- 2.数据集基本处理
- 3.特征工程
- 4.机器学习
- 5.模型评估
3.2 代码过程
code
# 导入模块
from sklearn.neighbors import KNeighborsClassifier
# 构造数据集
x = [[0], [1], [2], [3]]
y = [0, 0, 1, 1]
# 机器学习--模型训练
# 实例化api
estimator = KNeighborsClassifier(n_neighbors=2)
# 使用fit方法训练
estimator.fit(x, y)
ret = estimator.predict([[1]])
print("预测结果:\n", ret)
ret = estimator.predict([[3]])
print("预测结果:\n", ret)
4 小结
- sklearn的优势
- 文档多,且规范
- 包含的算法多
- 实现起来容易
- knn中的api
- sklearn.neighbors.KNeighborsClassifier(n_neighbors=5)
1.3、距离度量
1 距离公式的基本性质
在机器学习过程中,对于函数dist(.,.),若它是一“距离度量”(distance measure),则需满足一些基本性质:
- 非负性:dist(Xi,Xj) >= 0;
-
同一性:dist(xi,xj) =0. 当且仅当Xi=Xj;
-
对称性:dist(xi,xj)=dist(xj,xi);
-
直递性:dist(xi,xj) <= dist(xi,xk)+dist(xk,xj)
直递性常被直接称为“三角不等式”。
2 常见的距离公式
2.1 欧氏距离(Euclidean Distance):
欧氏距离是最容易只直观理解的距离度量方法
- 两个样本的距离可以通过如下公式计算,又叫欧氏距离
二维平面上点a(x1,y1)与b(x2,y2)间的欧氏距离:
d_{12}=\sqrt{(x1-x2)^2+(y1-y2)^2}
三维空间点a(x1,y1,z1)与b(x2,y2,z3)间的欧氏距离:
d_{12}=\sqrt{(x1-x2)^2+(y1-y2)^2+(z1-z2)^2}
n维空间点a(x11,x12,…,x1n)与b(x21,x22,…,x2n)间的欧氏距离(两个n维向量):
d_{12}=\sqrt{\sum_{k=1}^n(x_{1k}-x_{2k})^2}
2.2 曼哈顿距离(Manhattan Distance):
在曼哈顿街区要从一个十字路口开车到另一个十字路口,驾驶距离不是两个点之间的直线距离。曼哈顿距离也称为“城市街区距离”(City Block distance)。
二维平面两点a(x1,y1)与b(x2,y2)间的曼哈顿距离:
d_{12}=|x_1-x_2|+|y_1-y_2|
n维空间点a(x11,x12,…,x1n)与b(x21,x22,…,x2n)间的曼哈顿距离:
d_{12}=\sum_{k=1}^n|x_{1k}-x_{2k}|
2.3 切比雪夫距离(Chebyshev Distance):
国际象棋中,国王可以直行、横行、斜行,可以一步移动到相邻8个方格中任意一个。国王从(x1,y1)走到格子(x2,y2)最少需要多少步,这个距离就叫切比雪夫距离。
二维平面两点a(x1,y1)与b(x2,y2)之间的切比雪夫距离:
d_{12}=max(|x_1-x_2|,|y_1-y_2|)
n维空间两点a(x11,x12,…,x1n)与b(x21,x22,…,x2n)之间的切比雪夫距离:
d_{12}=max(|x_{1i}-x_{2i}|)
2.4 闵可夫斯基距离(Minkowski Distance):
闵氏距离不是一种距离,而是一组距离的定义,是对多个距离度量公式的概括性表述。
两个 n维变量a(x11,x12,…,x1n)与b(x21,x22,…x2n)间的闵可夫斯基距离定义为:
d_{12}=\sqrt[p]{\sum_{k=1}^n|x_{1k}-x_{2k}|^p}
其中p是一个变量参数:
- 当p=1时,就是曼哈顿距离;
-
当p=2时,就是欧氏距离;
-
当p–>∞时,就是切比雪夫距离
根据p的不同,闵氏距离可以表示某一类、种的距离。
小结:
1、闵氏距离,包括曼哈顿距离、欧氏距离和切比雪夫距离,都存在明显的缺点:
1)将各个分量的量纲(scale),也就是“单位”相同的看待了;
2)未考虑各个分量的分布(期望、方差等)可能是不同的。
知识拓展:其他距离公式
1、标准化欧氏距离(Standardized EuclideanDistance):
标准化欧氏距离是针对欧氏距离的缺点而作的一种改进。
思路:既然数据各维分量的分布不一样,那先将各个分量都“标准化”到均值、方差相等。
Sk表示各个维度的标准差
标准化欧氏距离公式:
d_{12}=\sqrt{\sum_{k=1}^n(\frac{x_{1k}-x_{2k}}{S_k})^2}
如果将方差的倒数看成一个权重,也可称之为加权欧氏距离(Weighted Euclidean distance)。
2 余弦距离(Cosine Distance)
几何中,夹角余弦可用来衡量两个向量方向的差异;机器学习中,借用这一概念来衡量样本向量之间的差异。
- 二维空间向量A(x1,y1)与向量B(x2,y2)的夹角余弦公式:
cos\theta=\frac{x_1x_2+y_1y_2}{\sqrt{x_1^2+y_1^2}\sqrt{x_2^2+y_2^2}}
- 两个n维样本点a(x11,x12,…,x1n)和b(x21,x22,…,x2n)的夹角余弦为:
cos(\theta)=\frac{a \cdot b}{|a||b|}
即:
cos(\theta)=\frac{\sum_{k=1}^nx_{1k}x_{2k}}{\sqrt{\sum_{k=1}^nx_{1k}^2}\sqrt{\sum_{k=1}^nx_{2k}^2}}
夹角余弦取值范围为[-1,1]。余弦越大表示两个向量的夹角越小,余弦越小表示两个向量的夹角越大。当两个向量的方向重合时余弦取最大值1,当两个向量的方向完全相反余弦取最小值-1.
3 汉明距离(Hamming Distance)
两个等长字符串s1与s2的汉明距离为:将其中一个变为另外一个所需要做的最小字符替换次数。
4 杰卡德距离(Jaccard Distance)
杰卡德相似系数(Jaccard similarity coefficient): 两个集合A和B交集元素在A,B的并集所占的比例,称为两个集合的杰卡德相似系数,用符号J(A,B)表示:
J(A,B)=\frac{|A\cap B|}{|A \cup B|}
杰卡德距离(Jaccard Distance):与杰卡德相似系数相反,用两个集合中不同元素占所有元素的比例来衡量两个集合的区分度:
J_{\delta}(A,B)=1-J(A,B)=\frac{|A \cup B|-|A \cap B|}{|A \cup B|}
5 马氏距离(Mahalanobis Distance)
马氏距离定义:设总体G为m维总体(考察m个指标),均值向量为u=(u1,u2,… …, um) , 协方差阵为:
\sum=(\delta_{ij})
则样本X=(X1,X2,… …, Xm) 与总体G的马氏距离定义为:
d^2(X,G)=(X-\mu)’\sum^{-1}(X-\mu)\
当m=1时,d^2(x,G)=\frac{(x-\mu)'(x-\mu)}{\delta^2}=\frac{(x-\mu)^2}{\delta^2}
马氏距离也可以定义为两个服从同一分布并且其协方差矩阵为 西格玛 的随机变量的差异程度:如果协方差矩阵为单位矩阵,马氏距离就简化为欧氏距离;如果协方差矩阵为对角矩阵,则其也可称为正规化的欧氏距离。
1.4、K值的选择
1 K值选择说明
- k值过小:
- 容易受到异常点的影像
- k值过大:
- 受到样本均衡的问题
在实际应用中,K值一般取一个比较小的数值
2 小结
- KNN中K值大小选择对模型的影像
- K值国小:
-
容易受到异常点的影像
-
容易过拟合
-
K值过大:
-
受到样本均衡的问题
- 容易欠拟合
1.5、kd树
1 kd树简介
1.1 什么是kd树
根据KNN每次需要预测一个点时,需要计算训练数据集里每个点到这个点的距离,然后选择出距离最近的k个点进行投票。 当数据集很大时,这个计算成本非常高。
kd树:为了避免每次都要重新计算一遍距离,算法会把距离信息保存在一个树里,这样在计算之前从树里查询距离信息,尽量避免重新计算。其基本原理是,如果A和B距离很远,B和C距离很近,那么A和C的距离也很远。 有了这个信息,就可以在合适的时候跳过距离远的点。
这样优化后的算法复杂度可降低到O(DNlog(N)). 感兴趣的读者可参阅论文:Bentley, J.L.,Communications of the ACM(1975).
1989年,另外一种称为Ball Tree的算法,在kd Tree的基础上对性能进一步优化。感兴趣的读者可以搜索Five balltree construction algorithms来了解详细的算法信息。
1.2 原理
1、树的建立;
2、最近邻域搜索(Nearest-Neighbor Lookup)
kd树(K-dimension tree)是一种对k维空间中的实例点进行存储以便对其进行快速检索的树形数据结构。kd树是一种二叉树,表示对k维空间的一个划分,构造kd树相当于不断地用垂直于坐标轴的超平面切分,构成一系列的K维超维矩形区域。kd树的每个节点对应于一个k维超矩形区域。利用kd树可以省去对大部分数据点的搜索,从而减少搜索的计算量。
2构造方法
(1)构造根节点,使根节点对应于k维空间中包含所有实例点的超矩形区域;
(2)通过递归的方法,不断地对k维空间进行切分,生成子节点。在超矩形区域上选择一个坐标轴和在此坐标轴上的一个切点,确定一个超平面,这个超平面通过选定的切分点并垂直于选定的坐标轴,将当前超矩形区域切分维左右两个子区域(子节点);这时,实例被分到两个子区域。
(3)上述过程直到子区域内没有实例时终止(终止时的节点为叶节点)。在此过程中,将实例保存在相应的节点上。
(4)通常,循环的选择坐标轴对空间切分,选择训练实例点在坐标轴上的中位数为切分点,这样得到的kd树是平衡的(平衡二叉树)。
KD树种每个节点是一个向量,和二叉树按照书的大小划分不同的是,KD树每层幼选定向量种的某一维,然后根据这一维按左小右大的方式划分数据。在构建KD树时,关键需要解决2个问题:
(1)选择向量的哪一维进行划分;
(2)如何划分数据;
第一个问题简单解决方法是随机选择某一维或按顺序选择,但是更好的方法应该是在数据比较分散的那一维进行划分(分散的程度可以根据方差来衡量)
第二个问题中,好的划分可以使构建的树比较平衡,可以每次选择中位数来进行划分。
3 案例分析
3.1树的建立
给定一个二维空间数据集:T={(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)},构造一个平衡kd树。
第一维度: 2,5,9,4,8,7
排序:2,4,5,7,8,9
中位数:5、7,例如选7
左边2,4,5对应的y轴数据是:3,4,7
8,9对那应的y轴数据是:1,6
第二维度:3,4,6,7,1,2
3.2最近领域的搜索
假设标记为星星的点是Test point,绿色的点是要找到的近似点,在回溯过程中,需要用到一个队列,存储需要回溯的店,在判断其他子节点空间中是否有可能有距离查询点更近的数据点时,做法是以查询点为圆心,以当前的最近距离为半径画圈,这个圆圈为候选超球(candidate hypersphere),如果圆与回溯点的轴相交,则需要将轴的另一边的节点都放到回溯队列里面来。
3.2.1查找点(2.1,3.1)
没有超过超平面,比较简单
(7,2),(5,4),(2,3)
3.2.2查找点(2,4.5)
(7,2),(5,4),(4,7)
1.6、案例1:鸢尾花种类预测–数据处理
1 案例:鸢尾花种类预测
Iris 数据集是常用的分类实验数据集,由Fisher,1936年收集整理。Iris也称为鸢尾花卉数据集,是一类多重变量分析的数据集。
关于数据集的具体介绍:
鸢尾花数据集:
- 特征值-4个:花瓣、花萼的长度、宽度
- 目标值-3个:setosa,vericolor,virginica
2 scikit-learn 中数据集介绍
2.1 scikit-learn数据集API介绍
- sklearn.datasets
- 加载获取流行数据集
- datasets.load_*()
- 获取小规模数据集,数据包括在datasets里
- dataset.fetch_*(data_home=None)
- 获取大规模数据集,需要从网络上下载,函数的第一个参数是data_home,表示数据集下载的目录,默认是~/scikit_learn_data/
2.1.1 sklearn小数据集
- sklearn.datasets.load_iris()
加载并返回鸢尾花数据集
2.1.2 sklearn大数据集
- sklearn.datasets.fetch_20newsgroups(data_home=None,subset=’train’)
- subset:’train’或者‘test’,’all’,可选,选择要加载的数据集。
- 训练集的‘训练’,测试集的‘测试’,两者的‘全部’
下载报错解决方法:
下载文件:https://people.csail.mit.edu/jrennie/20Newsgroups/20news-bydate.tar.gz 例如放到:D:\Python\Python311\sklearnDataSets (目录,目录为新建立,任意,gz文件会程序第一次运行后会自动被删除的)
修改py文件(见如下代码最后3行):
D:\Python\Python311\Lib\site-packages\sklearn\datasets_twenty_newsgroups.py
def _download_20newsgroups(target_dir, cache_path):
"""Download the 20 newsgroups data and stored it as a zipped pickle."""
train_path = os.path.join(target_dir, TRAIN_FOLDER)
test_path = os.path.join(target_dir, TEST_FOLDER)
if not os.path.exists(target_dir):
os.makedirs(target_dir)
#logger.info("Downloading dataset from %s (14 MB)", ARCHIVE.url)
#archive_path = _fetch_remote(ARCHIVE, dirname=target_dir)
archive_path = "D:\\Python\\Python311\\sklearnDataSets\\20news-bydate.tar.gz"
2.2 sklearn数据集返回值介绍
- load和fetch返回的数据类型datasets.base.Bunch(字典格式)
- data:特征数据数据组,是[n_samples*n_features]的二维numpy.ndarray数组
- target:标签数组,是n_samples的一维numpy.ndarray数组
- DESCR:数组描述
- feature_names:特征名,新闻数据,手写数字、回归数据集没有
- target_names:标签名
from sklearn.datasets import load_iris
#获取鸢尾花数据集
iris=load_iris()
pint("鸢尾花数据集的返回值:\n",iris)
pint("鸢尾花的特征值:\n",iris["data"])
pint("鸢尾花的目标值:\n",iris.target)
pint("鸢尾花特征的名字:\n",iris.feature_names)
pint("鸢尾花目标值的名字:\n",iris.target_names)
pint("鸢尾花的描述:\n",iris.DESCR)
2.3 查看数据分布
通过创建一些图,以查看不同类别是如何通过特征来区分的。在理想情况下,标签类将由一个或多个特征对完美分隔。在现实世界中这种情况很少会发生。
- seaborn介绍
- Seaborn是基于Matplotlib核心库进行了更高级的API封装,可以让你轻松地画出漂亮的图形。
- 安装 pip3 install seaborn
- seaborn.Implot()是一个非常有用的方法,它会在绘制二维三点图时,自动完成回归拟合
- sn.Implot() 里的x,y 分别代表横纵坐标的列名
- data= 是关联到数据集,
- hue=* 代表按照species即花的类别分类现实
- fit_reg= 是否进行线性拟合
“`python
</p></li>
</ul><h1>% matplotlib inline</h1>
<h1>内嵌绘图</h1>
<p>import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pdfrom pylab import mpl
<h1>设置显示中文字体</h1>
<h1>mpl.rcParams["font.sans-serif"] = ["SimHei"]</h1>
mpl.rcParams['font.sans-serif'] = ['FangSong'] # 指定默认字体
<h1>设置正常显示符号</h1>
mpl.rcParams["axes.unicode_minus"] = False
fig = plt.figure(figsize=(20, 20), dpi=900)
<h1>把数据换成dataframe的格式</h1>
iris_d = pd.DataFrame(iris['data'], columns=['Sepal_Length', 'Sepal_Width', 'Petal_Length', 'Petal_Width'])
iris_d['Species'] = iris.targetdef plot_iris(iris, col1, col2):
sns.lmplot(x=col1, y=col2, data=iris, hue="Species", fit_reg=True)
plt.xlabel(col1)
plt.ylabel(col2)
# plt.title('鸢尾花种类分布图')
plt.title('鸢尾花种类分布图', x=0.5, y=0.95)
plt.show()plot_iris(iris_d, 'Petal_Width', 'Sepal_Length')
“`
2.4 数据集的划分
机器学习一般的数据集会划分为两个部分:
- 训练数据:用于训练,构造模型
- 测试数据:用于检验时使用,用于评估模型是否有效
划分比例:
- 训练集:70% 80% 75%
- 测试集:30% 20% 25%
数据集划分api
- sklearn.model_selection.train_test_split(arrays.*options)
- 参数:
- x数据集的特征值
- y数据集的标签值
- test_size 测试集的大小,一般为float
- random_state 随机数种子,不同的种子会造成不同的随机采用结果。
- return
- x_train,x_test,y_train,y_test
数据集划分代码示例:
from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split #1、获取数据集 iris = load_iris() #2、数据集划分 x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, random_state=2, test_size=0.2) print("x_train:\n",x_train.shape)
1.7、特征工程-特征预处理
1 什么是特征预处理
1.1 特征预处理定义
skikit-lean的解释
provides several common utility functions and transformer classes to change raw feature vectors into a representation that is more suitable for the downstream estimators.
翻译过来:通过一些转换函数将特征数据转换成更加适合算法模型的特征数据过程
- 为什么要进行归一化、标准化?
- 特征的单位或者大小相差较大,或者某些的方差相比其他的特征要大出几个数量级,容易影像(支配)目标结果,使得一些算法无法学习到其他的特征
1.2 包含内容(数值型数据的无量纲化)
- 归一化
- 标准化
1.3 特征预处理API
sklearn.preprocessing
2 归一化
2.1 定义
通过对原始数据进行变换把数据映射到(默认为[0,1])之间
2.2 公式
X’=\frac{x-min}{max-min}\ X”=X’*(mx-mi)+mi
作用于每一列,max为一列的最大值,min为一列的最小值,那么X‘’为最终结果,mx,mi分别为指定区间,默认mx为1,mi为0
2.3 API
- sklearn.preprodessing.MinMaxScaler(feature_range=(0,1)…)
- MinMaxScalar.fit_transform(X)
- X:numpy array格式的数据(n_samples,n_features)
- 返回值:转换后的形状相同的array
2.4 数据计算
对于dating.txt约会数据计算
milage,Liters,Consumtime,target 40920,8.326976,0.953952,3 14488,7.153469,1.673904,2 26052,1.441871,0.805124,1 75136,13.1473994,0.428964,1 38344,1.669788,0.134296,1
- 分析
- 实例化MinMaxSclar
- 通过fit_transform转换
import pandas as pd from sklearn.preprocessing import MinMaxScaler def minmax_demo(): """ 归一化演示 :return: 、调用fit_transform """ data = pd.read_csv("data/dating.txt") print(data) # 1、实例化一个转换器类 transfer = MinMaxScaler(feature_range=(2, 3)) # 2、调用fit_transform data = transfer.fit_transform(data[['milage', 'Liters', 'Consumtime']]) print('最小值、最大值归一化处理的结果:\n', data) return None minmax_demo()
result
D:\Python\Python311\python.exe D:\work\pyprj\ml\1.preprocessing_demo.py milage Liters Consumtime target 0 40920 8.326976 0.953952 3 1 14488 7.153469 1.673904 2 2 26052 1.441871 0.805124 1 3 75136 13.147399 0.428964 1 4 38344 1.669788 0.134296 1 最小值、最大值归一化处理的结果: [[2.43582641 2.58819258 2.53237967] [2. 2.48794021 3. ] [2.19067405 2. 2.43571351] [3. 3. 2.19139157] [2.3933518 2.01947089 2. ]]
2.5 归一化总结
注意醉倒值最小值是变化的,另外,最大值与最小值非常容易受到异常点影像,所有这种方法鲁棒性较差,只适合传统精确小数据场景。
怎么办?
3 标准化
3.1定义
通过对原始数据进行变换,把数据变换到均值为0,标准差为1范围内
3.2 公式
X’=\frac{x-mean}{\delta}
作用于每一列,mean为平均值,delta为标准差
- 对于归一化来说:如果出现异常点,影像了最大值和最小值,那么结果显然会发生改变
- 对于标准化来说:如果出现异常点,由于具有一定的数据量,少量的异常点对于平均值的影像并不大,从而方差改变较小
3.3 API
- sklearn.preprocessing.StandardScalar
- 处理之后每列说所有数据都聚集在均值0附近标准差为1
- StandardScler.fit_transforms(x)
- X:numpy array格式的数据(n_samples,n_features)
- 返回值:转换后的形状相同的array
3.4 数据计算
同样对上面的数进行处理
- 分析
- 实例化StandardScaler
- 通过fit_transform转换
代码
import pandas as pd from sklearn.preprocessing import MinMaxScaler,StandardScaler def stand_demo(): """ 归一化演示 :return: 、调用fit_transform """ data = pd.read_csv("data/dating.txt") print(data) # 1、实例化一个转换器类 transfer = StandardScaler() # 2、调用fit_transform data = transfer.fit_transform(data[['milage', 'Liters', 'Consumtime']]) print('标准化的结果:\n', data) print('每一列特征的平均值:\n', transfer.mean_) print('每一列特征的方差:\n', transfer.var_) return None stand_demo()
output
milage Liters Consumtime target 0 40920 8.326976 0.953952 3 1 14488 7.153469 1.673904 2 2 26052 1.441871 0.805124 1 3 75136 13.147399 0.428964 1 4 38344 1.669788 0.134296 1 标准化的结果: [[ 0.0947602 0.44989971 0.29573441] [-1.20166916 0.18312843 1.67200507] [-0.63448132 -1.1152791 0.01123265] [ 1.77297701 1.54571809 -0.70784025] [-0.03158673 -1.06346713 -1.27113187]] 每一列特征的平均值: [3.89880000e+04 6.34790068e+00 7.99248000e-01] 每一列特征的方差: [4.15683072e+08 1.93505456e+01 2.73652475e-01]
1.8、案例1:鸢尾花种类预测–流程实现
1 再识K-近邻算法API
- sklearn.neighbors.KNeighborsClassifier(n_neignbors=5,algorithm=’auto’)
- alogorithm:{‘auto’,’ball_tree’,’kd_tree’,’brute’}
- 快速k近邻搜索算法,默认参数为’auto’,可以理解为算啊自己决定合适的搜索算法。
- brute是蛮力搜索,也就是线性扫描,当训练集很大时,计算非常耗时
- kd_tree,构造kd树存储数据一边对其进行快速检索的树形结构。在维数少于20时效率高。
- ball_tree是为了克服kd树高维失效而发明的,其构造过程是以质心C为半径r分割样本空间,每个节点是一个超球体。
2 案例:鸢尾花种类预测
2.1数据集介绍
2.2步骤分析
2.3代码过程
代码
from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.neighbors import KNeighborsClassifier # 1、获取数据 iris = load_iris() # 2、数据基本处理 x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2) # 3、特征工程:标准化 transfer = StandardScaler() x_train = transfer.fit_transform(x_train) x_test = transfer.transform(x_test) # 4、机器学习(模型训练) estimator = KNeighborsClassifier(n_neighbors=9) estimator.fit(x_train, y_train) # 5、模型评估 # 方法1:对比真实值和预测值 y_predict = estimator.predict(x_test) print("预测结果为:\n", y_predict) print("对比真实值和预测值:\n", y_predict == y_test) # 方法2:直接计算准确率 score = estimator.score(x_test, y_test) print("准确率为:\n", score)
输出结果:
D:\Python\Python311\python.exe D:\work\pyprj\ml\1.ywh_demo.py 预测结果为: [1 1 1 0 2 0 1 1 0 2 2 0 2 1 1 1 0 2 0 0 2 0 1 1 2 0 2 0 1 0] 对比真实值和预测值: [ True True True True True True True True True True True True False True True True True True True True True True True True True True True True False True] 准确率为: 0.9333333333333333
3 总结
- KNeighborsClassifier的使用
- sklearn.neighbors.KNeighborsClassifier(n_neighbors=5,algorithm=’auto’)
1.9、 k-近邻算法总结
k-近邻算法优缺点汇总
- 优点:
- 简单高效
- 重新训练的代价低
- 适合领域交叉样本
- KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的带分样本集来说,KNN方法较其他方法更为适合。
- 适合大样本自动分类
- 该算法比较适合样本容量比较大的类域的自动分类,而那些样本容量较小的类域采用这种算法容易产生误分。
- 缺点:
- 惰性学习
-
KNN算法是懒散学习方法(lazy learning。基本上不学习),一些积极学习的算法要快得多。
-
类别评分不是规格化
-
不像一些通过概率评分的分类
-
输出可解释性不强
-
例如决策树的输出可解释性就较强
-
对不均衡的样本不擅长
-
当样本不均衡时,如一个样本容量很大,而其他类样本容量很小时,有可能导致当输入一个新样本时,该样本的K个邻居中大容量类的样本占多数。该算法只计算“最近的”邻居样本,某一类的样本数据量很大,那么或者这类样本并不接近目标样本,或者这类样本靠近目标样本。无论怎样数量并不能影像结果。可以采用权值的方法(和该样本距离小的邻居权值大)来改进。
-
计算量较大
-
目前常用的解决方法是实现对一致样本进行剪辑,实现去除对分类作用不到的样本。
1.10、交叉验证,网格搜索
1 什么是交叉验证(cross validation)
交叉验证:将拿到的训练数据,分为训练和验证集。如将数据分为4份,其中一份作为验证集。然后经过4次(组)的测试,每次都更换不同的验证集,即得到4组模型的结果,取平均值作为结果。又称4折交叉验证。
1.1 分析
之前数据分为训练集和测试集,但是为了让训练得到模型结果更加准确。左如下处理:
- 训练集:训练集+验证集
- 测试集:测试集
验证集 训练集 训练集 训练集 80% 测试集 训练集 验证集 训练集 训练集 78% 测试集 训练集 训练集 验证集 训练集 75% 测试集 训练集 训练集 训练集 验证集 82% 测试集 1.2 为什么需要交叉验证
交叉验证的目的:为了让被评估的模型跟家准确可信
问题:这个只是让被评估的模型更加准确可信,那么怎么选择或者调优参数呢?
2 什么是网格搜索(Grid Search)
通常情况下,有很多参数是需要手动指定的(如k-近邻算法中的K值),这种叫超参数。但是手动过程繁杂,所以需要对模型预设几种超参数组合。每组超参数都采用交叉验证来评估。最后选出最优参数组合建立模型。
K值 K=3 K=5 K=7 模型 模型1 模型2 模型3 3 交叉验证,网格搜索(模型选择与调优)API
- sklearn.model_selection.GridSearchCV(estimator,param_grid=None,cv=None)
- 对估计器的指定参数值进行详尽搜索
- estimator:估计器对象
- param_grid:估计器参数(dict){“n_eighbors”:[1,3,5]}
- cv:指定几折交叉验证
- fit: 输入训练数据
- score:准确率
- 结果分析:
- bestscore_:在交叉验证中的最好结果
- bestestimator:最好的参数模型
- cvresults:每次交叉验证后的验证集准确率结果和训练集准确结果
4 鸢尾花案例增加K值调优
- 使用GridSearchCV构建估计器
code
from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split, GridSearchCV from sklearn.preprocessing import StandardScaler from sklearn.neighbors import KNeighborsClassifier # 1、获取数据 iris = load_iris() # 2、数据基本处理 x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, random_state=22, test_size=0.2) # 3、特征工程:标准化 transfer = StandardScaler() 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, 7, 9]} estimator = GridSearchCV(estimator, param_grid=param_dict, cv=4) # 4.3 fi数据进行训练 estimator.fit(x_train, y_train) # 5、模型评估 # 方法1:对比真实值和预测值 y_predict = estimator.predict(x_test) print("预测结果为:\n", y_predict) print("对比真实值和预测值:\n", y_predict == y_test) # 方法2:直接计算准确率 score = estimator.score(x_test, y_test) print("准确率为:\n", score) # 然后进行评估查看最终选择的结果和交叉验证的结果 print("在交叉验证中的最好结果:\n", estimator.best_score_) print("最好的参数模型:\n", estimator.best_estimator_) print("每次交叉验证后的准确率结果:\n", estimator.cv_results_)
result:
D:\Python\Python311\python.exe D:\work\pyprj\ml\1.ywh_grid_search_demo.py 预测结果为: [0 2 1 2 1 1 1 2 1 0 2 1 2 2 0 2 1 1 1 1 0 2 0 1 2 0 2 2 2 2] 对比真实值和预测值: [ True True True True True True True True True True True True True True True True True True False True True True True True True True True True True True] 准确率为: 0.9666666666666667 在交叉验证中的最好结果: 0.9666666666666668 最好的参数模型: KNeighborsClassifier(n_neighbors=3) 每次交叉验证后的准确率结果: {'mean_fit_time': array([0.00101483, 0.00100893, 0.00073737, 0.00097948, 0.00073785]), 'std_fit_time': array([3.00778527e-05, 1.98578311e-05, 4.26084598e-04, 3.05930964e-05, 4.26334000e-04]), 'mean_score_time': array([0.00305587, 0.00298047, 0.00299644, 0.00275636, 0.00301653]), 'std_score_time': array([1.10912361e-04, 1.99603488e-05, 3.71286705e-05, 4.08135388e-04, 4.20869152e-05]), 'param_n_neighbors': masked_array(data=[1, 3, 5, 7, 9], mask=[False, False, False, False, False], fill_value='?', dtype=object), 'params': [{'n_neighbors': 1}, {'n_neighbors': 3}, {'n_neighbors': 5}, {'n_neighbors': 7}, {'n_neighbors': 9}], 'split0_test_score': array([0.96666667, 1. , 1. , 1. , 1. ]), 'split1_test_score': array([0.93333333, 0.96666667, 0.96666667, 0.96666667, 0.96666667]), 'split2_test_score': array([0.93333333, 0.93333333, 0.93333333, 0.93333333, 0.93333333]), 'split3_test_score': array([0.96666667, 0.96666667, 0.93333333, 0.96666667, 0.93333333]), 'mean_test_score': array([0.95 , 0.96666667, 0.95833333, 0.96666667, 0.95833333]), 'std_test_score': array([0.01666667, 0.02357023, 0.02763854, 0.02357023, 0.02763854]), 'rank_test_score': array([5, 1, 3, 1, 3])} Process finished with exit code 0
1.11、案例2:预测facebook签到位置
1 项目描述
本次比赛是预测一个人将要签到的地方。
2 数据集介绍
官网:https://www.kaggle.com/navoshta/grid-knn/data
下载全部:facebook-v-predicting-check-ins.zip
包含:sample_submission.csv.zip 160M
test.csv.zip 118M -> test.csv 275M
train.csv.zip 557M -> train.csv 1.2G
3 步骤分析
- 对于数据做一些基本处理
- 缩小数据集范围 DataFrame.query()
-
选取有用的时间特征
-
将签到位置少于n个用户的删除
-
分割数据集
- 标准化处理
- k-近邻预测
具体步骤:
- 1、获取数据集
- 2、基本数据处理
- 2.1、缩小数据范围
- 2.2、 选择时间特征
- 2.3、去掉签到较少的地方
- 2.4、确定特征值和目标值
- 2.5、分割数据集
- 3、特征工程 — 特征预处理(标准化)
- 4、机器学些 — knn+cv
- 5、模型评估
4 代码实现
code
# 1、获取数据集 import pandas as pd from sklearn.model_selection import GridSearchCV, train_test_split from sklearn.neighbors import KNeighborsClassifier from sklearn.preprocessing import StandardScaler facebook = pd.read_csv("D:\\AI\\ML\data\\facebook-v-predicting-check-ins\\train.csv") # 2、基本数据处理 # 2.1、缩小数据范围 facebook_data = facebook.query("x>2.0 & x<3.5 & y>2.0 & y<3.5") # 2.2、 选择时间特征 time = pd.to_datetime(facebook_data["time"], unit="s") time = pd.DatetimeIndex(time) facebook_data["day"] = time.day facebook_data["hour"] = time.hour facebook_data["weekday"] = time.weekday # 2.3、去掉签到较少的地方 place_count = facebook_data.groupby("place_id").count() place_count = place_count[place_count["row_id"] > 3] face_book_data = facebook_data[facebook_data["place_id"].isin(place_count.index)] # 2.4、确定特征值和目标值 x = facebook_data[["x", "y", "accuracy", "day", "hour", "weekday"]] y = facebook_data["place_id"] # 2.5、分割数据集 x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=22) # 3、特征工程 -# 特征预处理(标准化) transfer = StandardScaler() x_train = transfer.fit_transform(x_train) x_test = transfer.fit_transform(x_test) # 4、机器学些 -# knn+cv # 4.1 实例化一个模型训练器 estimator = KNeighborsClassifier() # 4.2 交叉验证,网格搜索实现 param_grid = {"n_neighbors": [1, 3, 5, 7, 9]} estimator = GridSearchCV(estimator, param_grid=param_grid, cv=4, n_jobs=6) # 4.3 模型训练 estimator.fit(x_train, y_train) # 5、模型评估 score = estimator.score(x_test, y_test) print("最后预测的准确率为:\n", score) y_pre = estimator.predict(x_test) print("预测值是:\n", y_pre) # 然后进行评估查看最终选择的结果和交叉验证的结果 print("在交叉验证中的最好结果:\n", estimator.best_score_) print("最好的参数模型:\n", estimator.best_estimator_) print("每次交叉验证后的准确率结果:\n", estimator.cv_results_)
output:
最后预测的准确率为: 0.28574721883977444 预测值是: [2376385201 2666523815 3806997277 ... 1785058724 4371026902 5204012807] 在交叉验证中的最好结果: 0.27036623438328766 最好的参数模型: KNeighborsClassifier(n_neighbors=1) 每次交叉验证后的准确率结果: {'mean_fit_time': array([2.34748912, 2.39738792, 2.36522323, 2.55415195, 2.46213478]), 'std_fit_time': array([0.07332063, 0.05955746, 0.11407417, 0.09765046, 0.13163384]), 'mean_score_time': array([11.13672066, 14.36372387, 17.05159575, 18.04957145, 15.81654501]), 'std_score_time': array([0.12613807, 0.11973598, 0.16939248, 0.15631775, 2.92437081]), 'param_n_neighbors': masked_array(data=[1, 3, 5, 7, 9], mask=[False, False, False, False, False], fill_value='?', dtype=object), 'params': [{'n_neighbors': 1}, {'n_neighbors': 3}, {'n_neighbors': 5}, {'n_neighbors': 7}, {'n_neighbors': 9}], 'split0_test_score': array([0.27042087, 0.24594893, 0.25411412, 0.25523329, 0.2504256 ]), 'split1_test_score': array([0.27007409, 0.24557062, 0.2549732 , 0.25591898, 0.25182062]), 'split2_test_score': array([0.26969578, 0.24424653, 0.25435845, 0.25531211, 0.25018127]), 'split3_test_score': array([0.27127421, 0.24490262, 0.2531467 , 0.25374569, 0.24931628]), 'mean_test_score': array([0.27036623, 0.24516718, 0.25414812, 0.25505252, 0.25043594]), 'std_test_score': array([0.00058358, 0.00065028, 0.00065746, 0.00079978, 0.00089944]), 'rank_test_score': array([1, 5, 3, 2, 4])}