K-NN:用K=1不等于0训练MSE

K-NN: training MSE with K=1 not equal to 0

理论上,k = 1 的训练 MSE 应该为零。但是,以下脚本显示的情况并非如此。我首先生成一些玩具数据:x 代表睡眠时间,y 代表快乐。然后我训练数据并预测结果。最后,我通过两种方法计算训练数据的 MSE。谁能告诉我哪里出了问题?

from sklearn.neighbors import KNeighborsRegressor

model = KNeighborsRegressor(n_neighbors=1)

import numpy as np
x = np.array([7,8,6,7,5.7,6.8,8.6,6.5,7.8,5.7,9.8,7.7,8.8,6.2,7.1,5.7]).reshape(16,1)
y = np.array([5,7,4,5,6,9,7,6.8,8,7.6,9.3,8.2,7,6.2,3.8,6]).reshape(16,1)

model = model.fit(x,y)

for hours_slept in range(1,11):
    happiness = model.predict([[hours_slept]])
    print("if you sleep %.0f hours, you will be %.1f happy!" %(hours_slept, happiness))


# calculate MSE

# fast method
def model_mse(model,x,y):
    predictions = model.predict(x)
    return np.mean(np.power(y-predictions,2))
print(model_mse(model,x,y))

输出:

if you sleep 1 hours, you will be 6.0 happy!
if you sleep 2 hours, you will be 6.0 happy!
if you sleep 3 hours, you will be 6.0 happy!
if you sleep 4 hours, you will be 6.0 happy!
if you sleep 5 hours, you will be 6.0 happy!
if you sleep 6 hours, you will be 4.0 happy!
if you sleep 7 hours, you will be 5.0 happy!
if you sleep 8 hours, you will be 7.0 happy!
if you sleep 9 hours, you will be 7.0 happy!
if you sleep 10 hours, you will be 9.3 happy!
0.15999999999999992 #strictly larger than 0!

在您的数据中,xy67.6 中有多个 5.7 的标签。训练后,算法为变量5.7分配标签6,在评估时,当它第二次遇到5.7时,它returns6但是不是 7.6。因此,这对的平方误差是 (7.6 - 6)**2 = 2.56,考虑到其他误差是 0,均方误差是 1/16 * 2.56 = 0.16 - 正是你的结果。

In theory, the training MSE for k = 1 should be zero

这里隐含的假设是没有重复个样本x,或者更准确地说,相同的特征x具有相同的值y。这里是这样吗?让我们看看

pred = model.predict(x)

np.where(pred!=y)[0]
# array([9])

所以,只有一个值 ypred 确实不同:

y[9]
# array([7.6])

pred[9]
# array([6.])

哪里

x[9]
# array([5.7])

有多少样本 x 的值为 5.7,相应的 y 是多少?

ind = np.where(x==5.7)[0]
ind
# array([ 4,  9, 15])

y[ind]
# result:
array([[6. ],
       [7.6],
       [6. ]])

pred[ind]
# result
array([[6.],
       [6.],
       [6.]])

因此,这里实际发生的情况是,对于 x=5.7,算法不出所料地无法明确地确定哪个确切样本是单个最近邻 - 具有 y=6 的样本或具有 [=24] 的样本=];在这里它选择了与真实 y 不一致的那个,导致非零 MSE。

我想深入研究 knn 源代码可以准确证明内部处理此类情况的方式,但我将其作为练习。