算法简介
KNN算法思想
K-近邻算法 (K Nearest Neighbor,简称KNN)。比如:根据你的"邻居"来推断出你的类别
简单来说:预测当前数据时,在已知数据中寻找出k个与其最相似的样本,这k个样本大多数属于某个类别的话,那么该样本也属于某个类别
所以,k的取值就很有考究,这里暂时提及,到后面会详细讲解。
样本相似性 :样本都是属于一个任务数据集 的。样本距离越近则越相似。
利用K近邻算法预测电影类型
例如: 使用KNN预测电影类别

在这里我们可以使用欧氏距离来计算预测数据的"邻居"

K值的选择
前面我们提到过,我们需要找到k个与其最相似的样本,那么这个k值的选择就十分重要。
k值过小 ,这种情况就是用较小的邻域去进行预测,那么该种方法就可能使得预测值易受异常点的影响(极端思维理解:若k取1的话,如果你的参考值是一个异常值,那么你预测的值就受到了该异常值的影响)。k值的减小也意味着整体模型会变得复杂进而导致过拟合 现象的产生
k值过大,这种情况就是用较大的邻域去进行预测,那么该种方法可能会产生样本均衡的问题(极端思维理解:如果k取n,那么无论你输入什么值,那么它只会按训练集中最多的类别去进行预测)。k值的增大也意味着其整体的模型会变得简单,容易发生欠拟合的现象。
KNN算法的应用
KNN算法解决问题:分类问题、回归问题
-
回归问题的处理流程 :
1.计算未知样本到每一个训练样本的距离
2.将训练样本根据距离大小升序排列
3.取出距离最近的 K 个训练样本
4.进行多数表决,统计 K 个样本中哪个类别的样本个数最多
5.将未知的样本归属到出现次数最多的类别
-
回归问题的处理流程 :
1.计算未知样本到每一个训练样本的距离
2.将训练样本根据距离大小升序排列
3.取出距离最近的 K 个训练样本
4.把这个 K 个样本的目标值计算其平均值
5.作为将未知的样本预测的值
注:不论是分类问题还是回归问题,其前期的处理策略都是相同的。
KNN算法API介绍
分类算法API
KNN分类API:
python
sklearn.neighbors.KNeighborsClassifier(n_neighbors=5)
n_neighbors:int,可选(默认= 5),k_neighbors查询默认使用的邻居数,也就是我们所谓的 k的大小。
回归算法API
KNN分类API:
python
sklearn.neighbors.KNeighborsRegressor(n_neighbors=5)
n_neighbors:int,可选(默认= 5),k_neighbors查询默认使用的邻居数,也就是我们所谓的 k的大小。
相关代码
python
#导包
import sklearn.neighbors
def demo01_class():
#实例化
c_estimator = sklearn.neighbors.KNeighborsClassifier(n_neighbors=1)
#设置数据
x = [[0], [1], [2], [3]]
y = [0, 0, 1, 1]
#训练
c_estimator.fit(x, y)
print(c_estimator.predict([[5]]))
def demo02_regressor():
#实例化
r_estimator = sklearn.neighbors.KNeighborsRegressor(n_neighbors=2)
#设置数据
x = [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]]
y = [0.1, 0.2, 0.3, 0.4]
#训练
r_estimator.fit(x, y)
'''
计算过程就是找到离预测值最近的两个邻居输出其平均值
'''
print(r_estimator.predict([[5, 6, 7]]))
if __name__ == '__main__':
demo01_class()
demo02_regressor()
运行结果:

距离计算方法
在前面我们仅提及了欧式距离的计算方法,但计算距离的公式有很多,在不同的情况下则使用不同的公式,所以我们需要了解下相关的距离计算公式。
欧式距离
欧氏距离就是我们常说的空间中两点之间的距离。

曼哈顿距离
曼哈顿距离也可以称之为城市街区距离,可以理解为网格中两点之间的距离,其结果就是两个点构成的长方形的边长和。

切比雪夫距离
棋子从a点到b点最少需要多少步,旗子可以朝向相邻的八个方向任意移动。

闵氏距离
闵可夫斯基距离 Minkowski Distance 闵氏距离,不是一种新的距离的度量方式。而是距离的组合 是对多个距离度量公式的概括性的表述

特征预处理
对特征值进行预处理也就是对其进行归一化 和标准化
原因:特征的单位或者大小相差较大,或者某特征的方差相比其他的特征要大出几个数量级 ,容易影响(支配)目标结果,使得一些模型(算法)无法学习到其它的特征。
归一化
通过对原始数据进行变换把数据映射到【mi,mx】(默认为0,1)之间

注:(mx-mi)是进行值大小的缩放,+mi是进行值左右的位移
数据归一化的API实现
python
sklearn.preprocessing.MinMaxScaler (feature_range=(0,1)... )
#feature_range 缩放区间
注:
fit():只做一件事 ------计算传入数据的均值、标准差,并把这两个统计值保存到标准化器对象里。它不修改数据,只 "学习" 数据的分布参数。
transform():用对象里已经保存好的均值、标准差,按照公式 (x - 均值) / 标准差 对数据做标准化计算。它不重新计算统计量,只做变换。
fit_transform():等价于先执行 fit() 再执行 transform(),是一步到位的简写,只应该用在训练集上。
归一化受到最大值与最小值的影响,这种方法容易受到异常数据 的影响, 鲁棒性较差 ,适合传统精确小数据场景
如果出现异常点 影响了最大值和最小值 那么结果会明显的发生改变。
标准化
通过对原始数据进行标准化,转换为均值为0标准差为1的标准正态分布 的数据

数据标准化的API实现
python
sklearn.preprocessing. StandardScaler()
调用 fit_transform(X) 将特征进行归一化缩放
适用于现代嘈杂大数据场景。
如果出现了异常点 由于数据量较大 少量的异常点对于平均值的影响并不大。

代码:
python
#导包
import sklearn.preprocessing
def demo01_minmax():
# 数据
x = [[90, 2, 10, 40], [60, 4, 15, 45], [75, 3, 13, 46]]
#实例化
tf = sklearn.preprocessing.MinMaxScaler()
data = tf.fit_transform(x)
print(data)
def demo02_ss():
# 数据
x = [[90, 2, 10, 40], [60, 4, 15, 45], [75, 3, 13, 46]]
# 实例化
tf = sklearn.preprocessing.StandardScaler()
data = tf.fit_transform(x)
print(data)
if __name__ == '__main__':
demo01_minmax()
demo02_ss()
执行结果:
扩展(正态分布):

案例 - 鸢尾花分类
鸢尾花Iris Dataset数据集是机器学习领域经典数据集,鸢尾花数据集包含了150条鸢尾花信息,每50条取自三个鸢尾花中之一:Versicolour、Setosa和Virginica

处理流程:
获取数据集 - 数据基本处理 - 特征值处理 - 模型训练 - 模型评估
代码实现:
python
## KNN_鸢尾花
# 1.导入工具包
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import sklearn.model_selection
import sklearn.metrics
import sklearn.preprocessing
import sklearn.neighbors
# 2.加载数据集
# 2.1 加载数据集
iris_data = sklearn.datasets.load_iris() #这里的数据 是 sklearn 封装的 Bunch 对象
# print(iris_data)
# print(iris_data.target)
# print(iris_data.feature_names) # ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
# print(iris_data.target_names) # ['setosa' 'versicolor' 'virginica'] 三种花的类别
# 2.2 展示数据集
iris_df = pd.DataFrame(data=iris_data.data, columns=iris_data.feature_names)
iris_df['label'] = iris_data.target #花的类别
# print(iris_df)
# print(iris_data.feature_names)
# sns.lmplot(x='sepal length (cm)',y='sepal width (cm)',data = iris_df,hue='label', fit_reg=False ) #hue 按类别进行分类, fit_reg 关闭回归线拟合
# plt.show()
# 3.特征工程(预处理-标准化)
# 3.1 数据集划分
x_train, x_test, y_train, y_test = sklearn.model_selection.train_test_split(iris_data.data, iris_data.target, test_size = 0.3, random_state = 42) #test_size = 0.3 训练集和测试集比为7:3
# print(len(iris_data.data))
# print(len(x_train))
# 3.2 标准化
process = sklearn.preprocessing.StandardScaler()
x_train = process.fit_transform(x_train) #训练集需要计算
x_test = process.transform(x_test) #测试集无需计算
# 4.模型训练
# 4.1 实例化
model = sklearn.neighbors.KNeighborsClassifier() #分类
# 4.2 调用fit法
model.fit(x_train, y_train)
# 5.模型预测
x = [[5.1, 3.5, 1.4, 0.2]]
x = process.transform(x)
print(model.predict_proba(x)) # _proba 是每个分类的概率
# 6.模型评估(准确率)
# 6.1 直接计算准确率
acc = model.score(x_test, y_test) #用测试集计算模型的分类准确率
print(acc)
# 6.2 使用预测结果
y_predict = model.predict(x_test)
acc = sklearn.metrics.accuracy_score(y_test, y_predict)
print(acc)
运行结果:

超参数的选择
交叉验证
交叉验证是一种数据集的分割方法,将训练集划分为 n 份,其中一份做验证集、其他n-1份做训练集,其目的是为了得到更加准确可信的模型评分。
交叉验证法原理 :将数据集划分为 cv=10 份:
1.第一次:把第一份数据做验证集,其他数据做训练
2.第二次:把第二份数据做验证集,其他数据做训练
3... 以此类推,总共训练10次,评估10次。
4.使用训练集+验证集多次评估模型,取平均值做交叉验证为模型得分
5.若k=5模型得分最好,再使用全部训练集(训练集+验证集) 对k=5模型再训练一边,再使用测试集对k=5模型做评估
网格搜索
模型有很多超参数,其能力存在很大的差别,所以需要手动产生很多超参数组合来训练模型,每组超参数都采用交叉验证评估,最后选出最优参数组合建立模型。
网格搜做是模型调参的工具,我们只需要将若干参数传给网格搜索工具,它可以自动帮助我们完成不同超参数的组合、模型训练、模型评估,最终返回一组最优参数。
注:
超参数(Hyperparameter)是模型开始训练之前,由人工预先设定、用来控制模型结构 / 训练规则的参数。
它最关键的特点:不是模型通过 fit() 从数据里自动学习出来的,而是你提前定好的 "运行规则"。
目前我们正在学的 KNN 算法里,KNeighborsClassifier(n_neighbors=5) 中的 n_neighbors=5,就是最典型的超参数。
使用网格搜索+交叉验证的方法,可以形成一个模型参数调优的解决方案。
代码 鸢尾花分类(包含交叉验证流程):
python
## 交叉验证+网格搜索
# 1.导入工具包
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import sklearn.metrics
import sklearn.preprocessing
import sklearn.neighbors
import sklearn.model_selection
from sklearn.metrics import accuracy_score
# 2.加载数据
data = sklearn.datasets.load_iris()
# 3.数据集划分
x_train, x_test, y_train, y_test = sklearn.model_selection.train_test_split(data.data, data.target, test_size = 0.2, random_state = 1)
# 4.特征预处理
demo = sklearn.preprocessing.StandardScaler() #标准化
x_train = demo.fit_transform(x_train)
x_test = demo.transform(x_test)
# 5.模型实例化+交叉验证+网格搜索
model = sklearn.neighbors.KNeighborsClassifier(n_neighbors=1)
para = {'n_neighbors': [1, 3, 5, 7, 9]}
# estimator = sklearn.model_selection.GridSearchCV(
# estimator=model, # 要调参的基础模型
# param_grid=para, # 超参数候选网格
# cv=4 # 4折交叉验证 把训练集 x_train 平均分成 4 份,每次用 3 份做训练、1 份做验证,循环 4 次,取 4 次的平均得分作为这组参数的最终成绩。
# )
# estimator.fit(x_train,y_train)
#
# '''
# 这一行执行后,程序会自动完成一整套流程:
# 遍历每一组超参数(共 5 组 K 值);
# 对每组参数,都执行 4 折交叉验证,记录每一轮的验证集得分;
# 计算每组参数的平均验证得分,选出平均分最高的那一组作为最优超参数;
# 用最优超参数,在整个训练集上重新训练一个最终模型,保存在对象里。
# '''
#
# print(estimator.best_score_)
# print(estimator.best_estimator_)
# print(estimator.cv_results_)
#结果:
model = sklearn.neighbors.KNeighborsClassifier(n_neighbors=7)
# 6.模型训练
model.fit(x_train, y_train)
# x = [[5.1, 3.5, 1.4, 0.2]]
# x = demo.transform(x)
# 7.模型预测
y_predict = model.predict(x_test)
# 8.模型评估
print(sklearn.metrics.accuracy_score(y_test, y_predict))