K近邻法(KNN)
什么是KNN?
KNN的英文全称叫K-Nearest Neighbor,翻译过来就是K邻近算法。具体的来说就是根据你旁边的物品来决定你是什么物体。
举个例子,我们现在要分类豆子,一共有两类黄豆和绿豆,如果你拿起的豆子旁边都是黄豆,那么根据KNN算法,你手里的豆子就是黄豆;当然如果你拿起豆子旁边是绿豆,那么KNN也会认为你拿起的是绿豆。
KNN的算法流程
- 计算测试数据与各个训练数据之间的距离;
- 按照距离的递增关系进行排序;
- 选取距离最小的K个点;
- 确定前K个点所在类别的出现频率;
- 返回前K个点中出现频率最高的类别作为测试数据的预测分类。
KNN有什么用?具体应用在哪些方面?
知道了KNN是什么之后,我们会容易想到它可以用来解决分类问题,上面的豆子就是二分类问题,当我们增加豆子的种类时,它又会变成多分类问题。
具体来说KNN可以应用的哪些方面?
先给出结论,KNN可以用于分类,也可以用于回归。
分类比较好理解,因为上面豆子的例子就是分类问题,但是回归怎么说?回归和分类有什么区别?
回归任务的目标是预测连续值。分类是预测一个离散的值。
那么KNN怎么做回归任务?
使用kNN计算某个数据点的预测值时,模型会从训练数据集中选择离该数据点最近的k个数据点,并且把他们的y值取均值,把该均值作为新数据点的预测值
举个例子:就拿鸢尾花数据集来说,可以使用KNN通过前三个的特征值,预测第四个特征值
根据前三个特征找出新数据的k个最近邻,将这些邻居的第四个特征的平均值赋给该数据,就可以得到该数据对应第四个特征的值。
影响KNN的因素有哪些?
根据上面豆子的例子可以看出,我K值的选取会影响结果(K值就是选取几个邻居作为参考);判别的规则,也就是说选取的邻居中几个黄豆,我才认为我手里拿的是黄豆;其实距离的度量也会影响,选取不同的距离,会影响算法的结果
K值的选择:
K值过小,邻域小,预测结果对邻域里的训练数据非常敏感,容易造成过拟合。
K值过大,邻域大,距离远的训练数据也会对预测结果产生影响,极端值会影响预测结果。
那么怎么选取合适的K值呢?
交叉验证
具体实现步骤:
- 将数据集分成K(折)段,并将每段拆分成训练集和验证集
- 遍历所有指定的K(KNN)值对每段进行KNN模型训练
- 将每段评估的准确率进行相加,并除以K(折),得出KNN模型为某K值时的最终准确率
- 最终比较所有K(KNN)值的最终准确率,最高说明是这几个K(KNN)值中最合适的
判别的规则
k 近邻法中的分类决策规则往往是多数表决,即由待分类样本的 k 个邻近分类样本中的多数类决定它的类。
回归通常是直接取平均。
距离度量
距离的度量描述了测试样本与训练样本的临近程度,这个临近程度就是K个样本选择的依据,在KNN算法中,如果特征是连续的,那么距离函数一般用曼哈顿距离(L1距离)或欧氏距离(L2距离),如果特征是离散的,一般选用汉明距离。
KNN的问题
从上面的豆子例子中可以看出,KNN要保存训练数据,这也就意味着如果训练集过大,算法将不可用。
而且如何最快的找到需要的训练集也成为了一个问题,对于这个问题前人提出了KD树算法
怎么实现KNN?
下面给出python实现,
import numpy as np
#生成数据
def create_data(nums):
data1 = np.random.randint(42,size=(nums,3))
data2 = np.random.randint(0,10,size=(nums,1))
data = np.hstack((data1,data2))
columns = ['A','B','C','label']
df = pd.DataFrame(data,columns=columns)
df.to_csv('./data.csv')
#载入数据
def dataloader(path):
data = pd.read_csv(path)
train_data = data.iloc[:,-1].to_numpy()
train_label = data.iloc[:,-1].to_numpy()
return train_data,train_label
#计算距离
def distance(x1,x2):
return np.sqrt(np.sum(np.square(x1 - x2)))
def getclose(train_data,train_label,x,k):
distance_list = [0] * len(train_label)
for i in range(len(train_data)):
x1 = train_data[i]
curdist = distance(x1,x)
distance_list.append(curdist)
K_list = np.argsort(np.array(distance_list))[:k]
label_list = [0]*10
for index in K_list:
label_list[int(train_label[index])] += 1
return label_list.index(max(label_list))
if __name__ == "__main__":
create_data(42)
train_data,train_label = dataloader('./data.csv')
x = np.array([1,2,3,4])
result = getclose(train_data,train_label,x,5)
你如果使用上述代码预测一个数组的label,大概率是不准的,因为是随机数。其实不如将上面的数据换成mnist数据集,这样就会有点用,起码可以预测准一点。思路都是一样的。