数学部分
模型思想
如图所示,模型的本质就是寻找k个最近样本,然后基于最近样本做“预测”。对于离散型的因变量来说,从k个最近的已知类别样本中挑选出频率最高的类别用于末知样本的判断;对于连续型的因变量来说,则是将k个最近的已知样本均值用作未知样本的预测。
模型步骤
- 确定未知样本近邻的个数k值。
- 根据某种度量样本间相似度的指标(如欧氏距离)将每一个未知类别样本的最近k个已知样本搜寻出来,形成一个个簇。
- 对搜寻出来的已知样本进行投票,将各簇下类别最多的分类用作未知样本点的预测。
距离度量指标
欧氏距离
对于多维表示,yn和xn是n维空间
A:(x1, x2, x3 … xn)
B:(y1, y2, y3 … yn)
曼哈顿距离
余弦相似度
论文查重用的方法就是余弦相似度。
K值选择
根据经验发现,不同的k值对模型的预测准确性会有比较大的影响,如果k值过于偏小,可能会导致模型的过拟合;反之,又可能会使模型进入欠拟合状态。
那么解决方法就是,一种是设置k近邻样本的投票权重,使用KNN算法进行分类或预测时设置的k值比较大,担心模型发生欠拟合的现象,一个简单有效的处理办法就是设置近邻样本的投票权重,如果已知样本距离未知样本比较远,则对应的权重就设置得低一些,否则权重就高一些,通常可以将权重设置为距离的倒数。
还有一种方法是,采用多重交叉验证法,该方法是目前比较流行的方案,其核心就是将k取不同的值,然后在每种值下执行m重的交叉验证,最后选出平均误差最小的k值。当然,还可以将两种方法的优点相结合,选出理想的k值。
代码部分
函数
neighbors.KNeighborsClassifier(n_neighbors=5, weights=‘uniform’, p=2, metric=‘minkowski’)
n_neighbors:用于指定近邻样本个数K,默认为5
weights:用于指定近邻样本的投票权重,默认为’uniform’,表示所有近邻样本的投票权重一样;如果为’distance’,则表示投票权重与距离成反比,即近邻样本与未知类别的样本点距离越远,权重越小,反之,权重越大
metric:用于指定距离的度量指标,默认为闵可夫斯基距离
p:当参数metric为闵可夫斯基距离时,p=1,表示计算点之间的曼哈顿距离;p=2,表示计算点之间的欧氏距离;该参数的默认值为2
其中闵可夫斯基距离是p次根号下的Σ(x-y)^p
示例1——知识掌握程度
- 首先我们来看看数据
# 导入第三方包
import pandas as pd
# 导入数据
Knowledge = pd.read_excel(r'D:\pythonProject\data\Knowledge.xlsx')
# 返回前5行数据
Knowledge.head()
输出:
- 紧接着我们需要构造训练和测试集
# 构造训练集和测试集
# 导入第三方模块
from sklearn import model_selection
# 将数据集拆分为训练集和测试集
predictors = Knowledge.columns[:-1]
X_train, X_test, y_train, y_test = model_selection.train_test_split(Knowledge[predictors], Knowledge.UNS, test_size = 0.25, random_state = 1234)
- 我们利用交叉验证来寻找最佳的K值
# 导入第三方模块
import numpy as np
from sklearn import neighbors
import matplotlib.pyplot as plt
# 设置待测试的不同k值
K = np.arange(1,np.ceil(np.log2(Knowledge.shape[0]))).astype(int) # 计算样本数的log2,n个样本就是log2(n)
# 构建空的列表,用于存储平均准确率
accuracy = []
for k in K:
# 使用10重交叉验证的方法,比对每一个k值下KNN模型的预测准确率
cv_result = model_selection.cross_val_score(neighbors.KNeighborsClassifier(n_neighbors = k, weights = 'distance'), X_train, y_train, cv = 10, scoring='accuracy')
accuracy.append(cv_result.mean())
# 从k个平均准确率中挑选出最大值所对应的下标
arg_max = np.array(accuracy).argmax()
# 中文和负号的正常显示
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
# 绘制不同K值与平均预测准确率之间的折线图
plt.plot(K, accuracy)
# 添加点图
plt.scatter(K, accuracy)
# 添加文字说明
plt.text(K[arg_max], accuracy[arg_max], '最佳k值为%s' %int(K[arg_max]))
# 显示图形
plt.show()
输出:
- 我们利用计算的最佳K值进行拟合和验证
# 导入第三方模块
from sklearn import metrics
# 重新构建模型,并将最佳的近邻个数设置为5
knn_class = neighbors.KNeighborsClassifier(n_neighbors = 5, weights = 'distance')
# 模型拟合
knn_class.fit(X_train, y_train)
# 模型在测试数据集上的预测
predict = knn_class.predict(X_test)
# 构建混淆矩阵
cm = pd.crosstab(predict,y_test)
cm
输出:
注意看对角线的值
- 查看精确度
# 模型整体的预测准确率
metrics._scorer.accuracy_score(y_test, predict)
# 分类模型的评估报告
print(metrics.classification_report(y_test, predict))
输出:
precision是精确度;recall覆盖率;f1是两者综合指标。
示例2——预测发电量
- 导入数据并分析
# 读入数据
ccpp = pd.read_excel(r'D:\pythonProject\data\CCPP.xlsx')
ccpp.head()
输出:
PE是发电量,前面四个变量是影响PE大小的因素
# 返回数据集的行数与列数
ccpp.shape
(9568, 5)
- 处理数据
由于数据的量纲不一致,有千有个,所以我们需要进行数据标准化!
# 导入第三方包
from sklearn.preprocessing import minmax_scale
# 对所有自变量数据作标准化处理
predictors = ccpp.columns[:-1]
X = minmax_scale(ccpp[predictors])
# 将数据集拆分为训练集和测试集
X_train, X_test, y_train, y_test = model_selection.train_test_split(X, ccpp.PE, test_size = 0.25, random_state = 1234)
- 交叉验证寻找最佳K值
# 设置待测试的不同k值
K = np.arange(1,np.ceil(np.log2(ccpp.shape[0]))).astype(int)
# 构建空的列表,用于存储平均MSE
mse = []
for k in K:
# 使用10重交叉验证的方法,比对每一个k值下KNN模型的计算MSE
cv_result = model_selection.cross_val_score(neighbors.KNeighborsRegressor(n_neighbors = k, weights = 'distance'), X_train, y_train, cv = 10, scoring='neg_mean_squared_error') # 找均方误差最小
mse.append((-1*cv_result).mean())
# 从k个平均MSE中挑选出最小值所对应的下标
arg_min = np.array(mse).argmin()
# 绘制不同K值与平均MSE之间的折线图
plt.plot(K, mse)
# 添加点图
plt.scatter(K, mse)
# 添加文字说明
plt.text(K[arg_min], mse[arg_min] + 0.5, '最佳k值为%s' %int(K[arg_min]))
# 显示图形
plt.show()
输出:
- 构建模型,查看准确
# 重新构建模型,并将最佳的近邻个数设置为7
knn_reg = neighbors.KNeighborsRegressor(n_neighbors = 7, weights = 'distance')
# 模型拟合
knn_reg.fit(X_train, y_train)
# 模型在测试集上的预测
predict = knn_reg.predict(X_test)
# 计算MSE值
metrics.mean_squared_error(y_test, predict)
# 对比真实值和实际值
pd.DataFrame({'Real':y_test,'Predict':predict}, columns=['Real','Predict']).head(10)
我们去比较下决策树
# 导入第三方模块
from sklearn import tree
# 预设各参数的不同选项值
max_depth = [19,21,23,25,27]
min_samples_split = [2,4,6,8]
min_samples_leaf = [2,4,8,10,12]
parameters = {'max_depth':max_depth, 'min_samples_split':min_samples_split, 'min_samples_leaf':min_samples_leaf}
# 网格搜索法,测试不同的参数值
grid_dtreg = model_selection.GridSearchCV(estimator = tree.DecisionTreeRegressor(), param_grid = parameters, cv=10)
# 模型拟合
grid_dtreg.fit(X_train, y_train)
# 返回最佳组合的参数值
grid_dtreg.best_params_
# 构建用于回归的决策树
CART_Reg = tree.DecisionTreeRegressor(max_depth = 19, min_samples_leaf = 10, min_samples_split = 6)
# 回归树拟合
CART_Reg.fit(X_train, y_train)
# 模型在测试集上的预测
pred = CART_Reg.predict(X_test)
# 计算衡量模型好坏的MSE值
metrics.mean_squared_error(y_test, pred)
输出:{‘max_depth’: 19, ‘min_samples_leaf’: 10, ‘min_samples_split’: 6}
# 构建用于回归的决策树
CART_Reg = tree.DecisionTreeRegressor(max_depth = 19, min_samples_leaf = 10, min_samples_split = 6)
# 回归树拟合
CART_Reg.fit(X_train, y_train)
# 模型在测试集上的预测
pred = CART_Reg.predict(X_test)
# 计算衡量模型好坏的MSE值
metrics.mean_squared_error(y_test, pred)
输出:16.17598770280405
发现效果没有KNN的效果好。