【非参】python实现KNN回归估计
- 编程
- 2023-02-05
本文目录如下:
一、KNN 回归原理
方式一
方式二
二、python 代码实现
KNN 回归算法
算法测试
参考资料
一、KNN 回归原理
方式一
在非参数模型中,核回归估计可以看做是响应变量 周围固定范围内的一个带权均值估计,这个固定范围由带宽(bandwith)参数 控制。KNN 估计则是与待估计的 最近的 个 的带权均值,公式如下:
其中,权重 定义为:
k 最近邻集合 定义为:
是的个最近邻观测点如何定义与 最近的 个 呢?每个响应变量 都有其对应的坐标 ,待估计的 的值我们可能不知道,但我们可以知道他的坐标 ,然后根据距离公式计算出与 最近的几个 ,进而得知与 最近的 个 ,然后计算出 的 k 近邻估计值 。
如果数据分布较为稀疏,k 个最近邻会离估计点 非常远,得到的估计值偏差较大,此时一般不用 k 近邻估计。
观察权重计算公式,k 近邻估计在 内的权值是固定的,可以提取出来,即:
此时可以看做是k 个最邻近的 的均值,相当于是不带权重的。
方式二
换个角度,k 最近邻集合 是在变化的,故我们可以看成是 k 近邻估计有一个均匀核函数,他的带宽在变,但是带宽内的值固定不变,即带宽内的每个邻居 的权重相同且不变。于是,我们可以换个思路,用距离替代核函数,离估计点 越近的 的权重就越大,对应离 越近的点 的权重就越大,这也是核回归的思想。改写后的 k 近邻估计计算公式为:
其中 即为核函数(实际上是距离计算公式的变形),计算各个 到中心点 的距离; 为一个小数,如 0.00001,防止除数为 0。距离计算公式有非常多种,这里只列出简单的两种:
欧式距离:曼哈顿距离:计算距离时要注意不同特征的量纲差异(比如年龄和薪资),若不同特征之间量纲差异较大,则最好先对特征进行标准化或归一化。
方式一(权重固定,近似看成不带权重)为参考资料[1]上的版本,经过理论验证,算法较为稳健;方式二(带不同权重)是根据参考资料[1]的理论延伸,还未经理论推导验证,在低维数据上表现尚可,高维数据上表现欠佳。
二、python 代码实现
KNN 回归算法
class KNNRegressor:
K近邻回归估计算法。
def __init__(self, k:int, eps=1e-5):
self.k = k # k为最相邻的邻居个数 self.eps = eps # 一个小数,用来防止除数为0.def fit(self, X, Y):
传入训练数据集。
assert len(X) == len(Y)
self.X = np.asarray(X)
self.Y = np.asarray(Y)
def Euclidean_distance(self, x, x_all):
一个点x与所有点x_all的欧几里得距离。$d = \sqrt{(x_2 - x_1)^2 + (y_2-y_1)^2}$
x, x_all = np.asarray(x), np.asarray(x_all)
if x_all.ndim < 2: #低维 returnnp.sqrt((x - x_all)**2)
else: #高维 returnnp.sqrt(np.sum((x - x_all)**2, axis=1))
def predict(self, x, type=2):
预测函数。
Args:
x: 待估计的点。
type: 预测类型。1:不带权重;2:带权重。
Return:
result: x对应的y的估计结果。
x = np.array(x)
result = []
for sample inx:
distance = self.Euclidean_distance(sample, self.X)
index = distance.argsort()[:self.k]
if type== 1:
result.append(np.mean(self.Y[index]))
elif type== 2:
weight = (1 / (distance[index] + self.eps)) / np.sum(1 / (distance[index] + self.eps))
result.append(np.sum(weight * self.Y[index]))
else:
print(type输入错误!1:不带权重;2:带权重。)
break returnnp.array(result)
算法测试
import numpy as np
import matplotlib.pyplot as plt
# 生成随机数据n = 100
x = np.linspace(0, 1, n)
print(x.shape =, x.shape)
np.random.seed(0)
e = np.random.normal(0, 0.6, n)
# e = e[:, np.newaxis]print(e.shape =, e.shape)
y = 6 * np.sin(np.pi*x) + e
y_true = 6 * np.sin(np.pi*x)
print(y.shape =, y.shape)
# 一维数据k = 5
knn = KNNRegressor(k)
knn.fit(x, y)
y_knn_1 = knn.predict(x, type=1)
y_knn_2 = knn.predict(x, type=2)
# 画图plt.figure(figsize=(12, 16), dpi=60)
plt.rc(font, family=Times New Roman, weight = medium, size=17) #设置英文字体# plt.rcParams[font.sans-serif] = [SimHei] # 黑体# plt.rcParams[axes.unicode_minus] = False # 解决无法显示符号的问题ax = plt.subplot(2, 1, 1)
ax.scatter(x, y, c=k, label=$Simulation$)
ax.plot(x, y_true, lw=2, label=$Truth$)
ax.plot(x, y_knn_1, lw=3, label=$\hat{m}_k (x)$)
ax.set_xlabel(x)
ax.set_ylabel(y)
ax.set_title(fKNN regression without weights, k = {k})
ax.legend()
ax2 = plt.subplot(2, 1, 2)
ax2.scatter(x, y, c=k, label=$Simulation$)
ax2.plot(x, y_true, lw=2, label=$Truth$)
ax2.plot(x, y_knn_2, lw=3, label=$\hat{m}_k (x)$)
ax2.set_xlabel(x)
ax2.set_ylabel(y)
ax2.set_title(fKNN regression with weights, k = {k})
ax2.legend()
plt.show()
# plt.savefig(KNN回归.png)参考资料
❝[1] Härdle W, Müller M, Sperlich S, et al. Nonparametric and semiparametric models[M]. Berlin: Springer, 2004.
>[2]几种常见的距离计算公式
>[3]【知乎】knn算法中k是 怎么决定的?
>[4]python实现knn回归算法❞