KNN算法 0基础小白也能懂(附代码)
KNN算法 0基础小白也能懂(附代码)
1.K近邻是啥
1968年,Cover 和 Hart 提出了最初的近邻法,思路是——未知的豆离哪种豆最近,就认为未知豆和该豆是同一种类。
近邻算法的定义:为了判定未知样本的类别,以全部训练样本作为代表点计算未知样本与所有训练样本的距离,并以最近邻者的类别作为决策未知样本类别的唯一依据。
说人话就是,看这玩意离哪个东西距离最近,越近越像。
最近邻算法的缺陷是对噪声数据过于敏感。从图中可以得到,一个圈起来的蓝点和两个圈起来的红点到绿点的距离是相等的,根据最近邻算法,该点的形状无法判断。其实也就是东西太多太杂的话判断不清了
为了解决这个问题,我们可以把位置样本周边的多个最近样本计算在内,扩大参与决策的样本量,以避免个别数据直接决定决策结果。也就是增大数据量减少误差。
引进K近邻算法——选择未知样本一定范围内确定个数的K个样本,该K个样本大多数属于某一类型,则未知样本判定为该类型。K近邻算法是最近邻算法的一个延伸。根据K近邻算法,离绿点最近的三个点中有两个是红点,一个是蓝点,红点的样本数量多于蓝点的样本数量,因此绿点的类别被判定为红点。
2.KNN算法步骤
一般来说,只选择样本数据集中前N个最相似的数据.K一般不大于20,最后,选择K个中出现次数最多的分类,作为新数据的分类。
那K值到底如何选择呢?
-
数据特性
噪声与非相关特征:如果数据中存在较多噪声或非相关特征,较大的K值可以平滑分类结果,减小噪声的影响。然而,这也可能导致类别之间的界限变得模糊,特别是在数据分布复杂或类别间存在明显界限的情况下。
特征选择与缩放:为减小噪声和非相关特征的影响,可以通过特征选择和特征缩放来优化输入数据。例如,利用进化算法或互信息进行特征选择,从而提高KNN算法的性能。 -
奇数K值
避免平票:在二元分类问题中,选择奇数的K值有助于避免分类器投票时出现平票的情况,从而提高分类器的确定性。这一点在K值较小、数据分布相对均匀时尤为重要。 -
超参数优化
启发式方法:K值的选择可以通过各种启发式技术来优化。交叉验证(Cross-validation)是一种常用的方法,它通过将数据集划分为训练集和验证集,尝试不同的K值,并选择在验证集上表现最佳的K值。
自助法(Bootstrap):在二元分类问题中,自助法可以用于评估不同K值的性能,并帮助选择最佳的K值。自助法通过多次重复采样训练集,计算每次采样的分类准确率,从而估计K值的期望性能。(从原始数据集中随机有放回地抽样,生成多个新的子数据集。每个子数据集都与原始数据集大小相同,但是由于是有放回抽样,因此某些样本可能会在一个子数据集中出现多次,而另一些样本可能根本没有出现(OOB)。)
3.实战实现KNN算法
3.1 背景
假如一套房子打算出租,但不知道市场价格,可以根据房子的规格(面积、房间数量、厕所数量、容纳人数等),在已有数据集中查找相似(K近邻)规格的房子价格,看别人的相同或相似户型租了多少钱。
数据集在这,CardioGoodFitness 数据集 提取码:show
3.2数据分类
已知的数据集中,每个已出租住房都有房间数量、厕所数量、容纳人数等字段,并有对应出租价格。将预计出租房子数据与数据集中每条记录比较计算欧式距离(坐标系里的距离),取出距离最小的5条记录,将其价格取平均值,可以将其看做预计出租房子的市场平均价格。
import pandas as pd
import numpy as np
from scipy.spatial import distance#用于计算欧式距离
from sklearn.preprocessing import StandardScaler#用于对数据进行标准化操作
from sklearn.neighbors import KNeighborsRegressor#KNN算法
from sklearn.metrics import mean_squared_error#用于计算均方根误差
上面导入包的作用原理会在后面一一指出,先导入数据并提取目标字段
#导入数据并提取目标字段
path = r'rent_price.csv'
file = open(path, encoding = 'gb18030', errors = 'ignore')
dc_listings = pd.read_csv(file)
features = ['accommodates','bedrooms','bathrooms','beds','price','minimum_nights','maximum_nights','number_of_reviews']
dc_listings = dc_listings[features]
dc_listings长下面这样
3.3 初步数据清洗
数据集中非数值类型的字段需要转换,替换掉美元$符号和千分位逗号
#数据初步清洗
our_acc_value = 3
dc_listings['distance'] = np.abs(dc_listings.accommodates - our_acc_value)
dc_listings = dc_listings.sample(frac=1, random_state=0) #抽取 100% 的样本重排
dc_listings = dc_listings.sort_values('distance')
dc_listings['price'] = dc_listings['price'].str.replace(r'[\$,]', '', regex=True).astype(float)
dc_listings = dc_listings.dropna() #删除包含空值(NaN)的行
理想情况下,数据集中每个字段取值范围都相同,但实际上这是几乎不可能的,如果计算时直接用原数数据计算,则会造成较大训练误差,所以需要对各列数据进行标准化或归一化操作,尽量减少不必要的训练误差。归一化的目的就是使得预处理的数据被限定在一定的范围内(比如[0,1]或者[-1,1]),从而消除奇异样本数据导致的不良影响。
#数据标准化
dc_listings[features] = StandardScaler().fit_transform(dc_listings[features]) #都变成标准正态分布
normalized_listings = dc_listings
最好不要将所有数据全部拿来测试,需要分出训练集和测试集具体划分比例按数据集确定。
#取得训练集和测试集
norm_train_df = normalized_listings[:2792]
norm_test_df = normalized_listings[2792:]
3.4 计算欧式距离并预测房屋价格
#scipy包distance模块计算欧式距离
first_listings = normalized_listings.iloc[0][['accommodates', 'bathrooms']]
fifth_listings = normalized_listings.iloc[20][['accommodates', 'bathrooms']]
#用python方法做多变量KNN模型
def predict_price_multivariate(new_listing_value, feature_columns):
temp_df = norm_train_df
#distance.cdist计算两个集合的距离
temp_df['distance'] = distance.cdist(temp_df[feature_columns], [new_listing_value[feature_columns]])
temp_df = temp_df.sort_values('distance')#temp_df按distance排序
knn_5 = temp_df.price.iloc[:5] #选择距离最近的前5个样本
predicted_price = knn_5.mean()
return predicted_price
cols = ['accommodates', 'bathrooms']
norm_test_df['predicted_price'] = norm_test_df[cols].apply(predict_price_multivariate, feature_columns=cols, axis=1)
norm_test_df['squared_error'] = (norm_test_df['predicted_price'] - norm_test_df['price']) ** 2
mse = norm_test_df['squared_error'].mean()
rmse = mse ** (1/2)
print(rmse)
#利用sklearn完成KNN
col = ['accommodates', 'bedrooms']
knn = KNeighborsRegressor()
#将自变量和因变量放入模型训练,并用测试数据测试
knn.fit(norm_train_df[cols], norm_train_df['price'])
two_features_predictions = knn.predict(norm_test_df[cols])
#计算预测值与实际值的均方根误差
two_features_mse = mean_squared_error(norm_test_df['price'], two_features_predictions)
two_features_rmse = two_features_mse ** (1/2)
print(two_features_rmse)
输出为
1.4667825805653032
......(一堆报错,表示你正在对一个可能是原 DataFrame 的切片的数据进行修改,不过不影响结果)
1.5356457412450537
总结:K近邻算法的核心要素
K的大小
在实际的应用中,一般采用一个比较小的K值。并采用交叉验证的方法,选取一个最优的K值。比如在之前的代码中,手动实现的K值选的就是5,同时sklearn包里K值默认也是5.
距离度量准则
-
欧氏距离(Euclidean Distance)
欧氏距离是最常用的距离度量准则之一,适用于连续型变量。它表示两个点之间的“直线”距离。
优点:简单易懂,计算方便。
缺点:对特征尺度敏感,需要进行特征缩放(如标准化)。 -
曼哈顿距离(Manhattan Distance)
曼哈顿距离,也称为“城市街区距离”或“L1距离”,表示两个点在各维度上的绝对差值的和。
优点:对特征缩放不太敏感,适用于高维空间。
缺点:不能反映“直线”距离,可能导致某些情况下误差较大。 -
切比雪夫距离(Chebyshev Distance)
切比雪夫距离,也称为L∞距离,表示两个点之间在所有坐标轴上最大差值的距离。
优点:适用于棋盘格状的网格空间,特别适合某些特殊情况下的度量。
缺点:在某些应用中可能不够精确。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库