机器学习:K近邻算法(KNN)

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

机器学习:K近邻算法(KNN)
K近邻算法(KNN,K-NearestNeighbor)是机器学习或数据分析中最基础、也是最简单的算法之⼀,这个算法的思路就如同它字⾯上的意思“K个最近的邻居”,想要得到某个样本的某个特征的值(⼀个样本通常有多个特征),就需要找到距离它最近的K个样本,然后根据这些样本的该特征的近似值作为它的特征值。

样本和特征:通常来讲,可以理解为⼀个表格数据中⼀⾏数据为⼀个样本,⼀列数据为这个样本的⼀个特征,就像数据库中的记录和字段的关系。

距离和K值:这个算法的关键点在于距离的计算⽅法和K值的选取,距离的计算⽅式可以根据实际情况⾃定义,⽐如使⽤两个样本的某个特征值的差值绝对值作为这两个样本之间的距离,也可以使⽤⽐较通⽤的欧式距离计算⽅式,或者直接使⽤某些库⾃带的距离计算⽅式,如scipy库中就有计算距离的⽅法“from scipy.spatial import distance”,这⼏种距离的计算⽅式在本⽂⽰例中都有讲解,可以参考下。

关于K值的选取,通常不宜过⼤,K值太⼤时,准确率会随之降低,通常选择3-10就⾜够了。

优点和缺点:优点就是思路简单,易于实现,理解了这个算法后,可以不⽤复杂的公式也能计算出来。

缺点是需要计算每个样本与⾃⾝之间的距离,当样本数量较⼤时,计算量也随之增⼤,⽽且当样本之间的特征不平衡时,得出的结果的偏差也会随之增⼤。

注:机器学习中会涉及许多数学中的概念,如果有不清楚的地⽅,可能是学过但忘了,也可能是以前就没接触过,可以再去复习⼀下,或者⼲脆就重新学习⼀下,本⽂就不再详细讲解了。

本⽂将根据⼀个⽰例来实现和讲解K近邻算法,⽰例的需求是这样的:我⼿中有⼀套房⼦需要出租,但是价格不知道定为多少是最合适的,现在需要参考其他房东的出租信息来制定我的出租价格。

⽰例将分为以下⼏部分内容来讲解:
KNN算法实现
模型评估
基于多变量KNN模型
KNN算法实现
1. 数据准备
需要准备的数据为其他房东的出租数据,我们将会根据这些数据作为参考得出⾃⼰房⼦的合适出租价格,这⾥准备的少量数据只是为了演⽰⽤,实际上应该多准备⼀些数据,得出的价格才能更加精确。

import pandas as pd
# 假设这些是我们获取到的租房信息
rental_info = {
'url': ['https:///rooms/975833', 'https:///rooms/295345', 'https:///rooms/295346',
'https:///rooms/333613', 'https:///rooms/805961', 'https:///rooms/7087327',
'https:///rooms/8249488', 'https:///rooms/8409022', 'https:///rooms/8411173',
'https:///rooms/8634774', 'https:///rooms/8498095', 'https:///rooms/8513660',
'https:///rooms/8298145', 'https:///rooms/1745866', 'https:///rooms/7678268',
'https:///rooms/8457865', 'https:///rooms/6757134', 'https:///rooms/8479636',
'https:///rooms/2310297', 'https:///rooms/6556520', 'https:///rooms/8519782',
'https:///rooms/8606980', 'https:///rooms/8485995', 'https:///rooms/8607216',
'https:///rooms/8568945', 'https:///rooms/1026034', 'https:///rooms/2486785',
'https:///rooms/1822257', 'https:///rooms/5220279', 'https:///rooms/3419118'],
'name': ['Historic DC Condo-Walk to Capitol!', 'Spacious Capitol Hill Townhouse', 'Spacious/private room for single',
'A wonderful bedroom with library', 'Downtown Silver Spring', 'Exclusive Catamaran Houseboat',
'Cozy DC Condo, Close to Metro!', 'Warm and Cozy 1 Bedroom Apt', 'Private room for rent',
'Elite Room w/private bath Eden Park', 'Takoma Comfort, DC Convenience', 'Great Penthouse View! Metro in 7min',
'Sunny & Conveniently Located!', 'Beautiful Private High-Rise Apt', 'Renaissance Rm shared bath Eden PK',
'Cozy private second floor', 'Cheap room near Fort Totten Metro', 'Near Washington,DC',
'Sweet basement suite apartment', 'Etta Mae Inn B&B - The Kiera room', 'All the comfort you need.',
'Etta Mae Inn B&B - The Phoenix room', 'Sunny Cape Cod minutes from DC', 'CHIC DC URBAN RETREAT',
'Perfect studio in VERY central DC', 'Logan Circle Loft 1bedroom, 1.5bath', 'Updated 2BD Rowhome Steps to Metro',
'Cozy Apt in the Heart of Hipsterdom', 'Beautiful room in amazing location!', 'Clean Studio - Next to Conv Center!'],
'host_id': [15830506, 5338703, 1487418, 16970249, 30369828, 951119, 4628, 3671500, 5159038, 347309, 9188872, 21704152,
44519208, 3736766, 347309, 44659281, 11798122, 34290236, 4218349, 45276150, 44546458, 45276150, 2245859,
966914, 2070536, 12725500, 9540128, 22207701, 12772446, 4240274],
'accommodates': [4, 6, 1, 2, 4, 4, 4, 2, 2, 2, 4, 1, 2, 2, 2, 2, 1, 2, 4, 2, 2, 2, 7, 1, 4, 4, 3, 4, 2, 2],
'bedrooms': [4, 6, 1, 2, 4, 4, 4, 2, 2, 2, 4, 1, 2, 2, 2, 2, 1, 2, 4, 2, 2, 2, 7, 1, 4, 4, 3, 4, 2, 2],
'bathrooms': [1, 3, 2, 1, 1, 1, 2, 1, 1.5, 2, 1.5, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1.5, 1, 1, 1, 1],
'beds': [2, 3, 1, 1, 1, 4, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 3, 1, 2, 1, 2, 2, 1, 1],
'price': ['$160.00 ', '$350.00 ', '$50.00 ', '$95.00 ', '$50.00 ', '$99.00 ', '$100.00 ', '$100.00 ', '$38.00 ',
'$71.00 ', '$97.00 ', '$55.00 ', '$50.00 ', '$99.00 ', '$60.00 ', '$52.00 ', '$23.00 ', '$200.00 ',
'$40.00 ', '$135.00 ', '$100.00 ', '$225.00 ', '$129.00 ', '$149.00 ', '$150.00 ', '$175.00 ', '$239.00 ',
'$65.00 ', '$71.00 ', '$80.00 '],
'minimum_nights': [1, 2, 2, 1, 7, 1, 3, 1, 2, 2, 4, 3, 2, 7, 2, 1, 1, 1, 1, 1, 1, 1, 14, 3, 2, 1, 5, 2, 2, 1],
'maximum_nights': [1125, 30, 1125, 1125, 1125, 1125, 1125, 1125, 180, 365, 1125, 1125, 14, 1125, 1125, 1125,
1125, 1125, 1125, 1125, 1125, 1125, 1125, 31, 365, 1125, 1125, 1125, 14, 1125],
'number_of_reviews': [0, 65, 1, 0, 0, 0, 0, 0, 1, 4, 5, 1, 0, 0, 2, 1, 1, 0, 0, 0, 0, 0, 0, 46, 84, 25, 4, 2, 83, 19],
'cleaning_fee': ['$115.00 ', '$100.00 ', '$100.00', '$15.00 ', '$15.00 ', '$50.00 ', '$50.00 ', '$10.00 ',
'$10.00 ', '$10.00 ', '$50.00 ', '$20.00 ', '$20.00 ', '$125.00 ', '$10.00 ', '$5.00', '$5.00 ',
'$99.00 ', '$99.00 ', '$99.00 ', '$99.00 ', '$99.00 ', '$99.00 ', '$30.00 ', '$30.00 ', '$25.00 ',
'$50.00 ', '$12.00 ', '$12.00 ', '$12.00 '],
'zipcode': [20003, 20003, 20782, 20024, 20910, 20024, 20012, 20712, 20743, 20912, 20912, 20910, 20910, 20910,
20912, 20712, 20782, 20910, 20712, 20912, 20712, 20912, 20912, 20008, 20005, 20005, 20001, 20001, 20001, 20001]
}
# 如果数据是存放在类似csv格式的⽂本⽂件中,可以使⽤pd.read_csv('xxx.csv')来读取数据
rental_df = pd.DataFrame(rental_info)
# 选取我们需要参考的信息:accommodates(可以容纳的房客数量),bedrooms(卧室数量),bathrooms(卫⽣间数量),
# beds(床的数量),price(房租价格),minimum_nights(房客最少租了多少天),maximum_nights(房客最多租了多少天),
# number_of_reviews(评论数量)
key_info = ['accommodates', 'bedrooms', 'bathrooms', 'beds', 'price', 'minimum_nights', 'maximum_nights', 'number_of_reviews']
keyinfo_df = rental_df[key_info]
# 查看前五条数据
keyinfo_df.head()
2. 距离计算
假设我们以卧室数量作为参考,我们⾃⼰的房⼦有4个卧室,那么我们的房⼦与其他房东的房⼦的距离可以使⽤欧式距离计算⽅式,由于我们只有卧室数量这个单⼀变量,所以就可以简单计算为两者数量的差的绝对值。

欧式距离公式如下:
注:其中q1-qn为⼀条数据(样本)的n个特征信息,p1-pn为另⼀条数据(样本)的特征信息,由此公式便可同时根据多个变量(多个特征)计算出两个样本之间的距离。

import numpy as np
my_bedrooms = 4
# 新加⼀列⽤以存放其他的房⼦与我们⾃⼰房⼦的距离
keyinfo_df['distance'] = np.abs(keyinfo_df['accommodates'] - my_bedrooms)
# 统计这些距离值并排序,以便查看
print(keyinfo_df['distance'].value_counts().sort_index())
keyinfo_df.head()
可以看出,与我们房⼦的卧室数量相同的有9个。

3. 选择K个近邻算出价格
我们这⾥指定K=5,即选择5个与我们距离最近的房⼦作为参考,并算出近似值作为我们出租房⼦的参考价格。

# 将价格列的字符串去掉$符并转换为float类型
keyinfo_df['price'] = keyinfo_df['price'].str.replace('$', '').astype(float)
# sample函数具有“洗牌”的功能,先打乱数据的顺序,再返回指定的数据量,frac=1表⽰返回100%的数据,random_state为随机数种⼦
keyinfo_df = keyinfo_df.sample(frac=1, random_state=0)
# 按照distance列排序
keyinfo_df = keyinfo_df.sort_values('distance')
# 这⾥近似值的算法我们采⽤求平均的⽅式,即选择与我们距离最近的5个房⼦的价格计算平均值
my_price = keyinfo_df['price'].iloc[:5].mean()
my_price
输出结果:105.8
结论:以卧室数量作为参考,我们可以将房⼦的出租价格定在105.8左右。

模型评估
模型即我们的这个算法,对于上⾯这种实现⽅式,到底可不可靠?在不同的数据中计算得出的结果的误差有多⼤?这就是模型评估需要做的事。

为了评估模型(算法),我们需要将数据打乱之后分为两组,⼀组作为训练集(数据量占⽐较⼤),⼀组作为测试集(数据量占⽐较⼩),训练集数据⽤来优化改进我们的算法模型,测试集⽤来验证我们的算法模型。

验证⽅法为通过计算我们测试集数据得出的结果与测试集中原本的实际数据之间的误差来验证算法模型的可靠性,误差越⼩,则越可靠,这⾥我们采⽤的误差计算⽅式为均⽅根误差(RMSE,Root Mean Squared Error),计算公式如下:
注:actual表⽰实际值,predicted表⽰预测出来的值,1-n表⽰不同的样本,最终的值表⽰通过多个样本计算出来总体误差值。

keyinfo_df = keyinfo_df.sample(frac=1, random_state=0) # 再次打乱数据
keyinfo_df.drop('distance', axis=1) # 删除distance列
train_df = keyinfo_df.copy().iloc[:20] # 选取前20条数据作为训练集
test_df = keyinfo_df.copy().iloc[20:] # 选取后10条数据作为测试集
def predict_price(feature_value, feature_name):
"""
根据特征值feature_value,在训练集中根据指定特征feature_name得出预测的价格
"""
temp_df = train_df
temp_df['distance'] = np.abs(temp_df[feature_name] - feature_value)
temp_df = temp_df.sort_values('distance')
my_price = temp_df['price'].iloc[:5].mean()
return my_price
# 以测试集中的每条数据的卧室数量作为“我们⾃⼰的房⼦的卧室数量”,去训练集中计算得出参考价格
test_df['my_price'] = test_df['accommodates'].apply(predict_price, feature_name='accommodates')
# 根据公式计算均⽅根误差
test_df['squared_price'] = (test_df['price'] - test_df['my_price']) ** 2
mean_value = test_df['squared_price'].mean()
rmse = mean_value ** (1/2)
rmse
输出结果:101.82695124572865
单个误差值看不出什么意义,需要多个特征之间误差值联合起来⼀起看才有意义,误差值更⼩的那种计算⽅式通常更为可靠。

# 根据多个特征分别计算这种算法模型的均⽅根误差
for feature in ['accommodates','bedrooms','bathrooms','number_of_reviews']:
test_df['my_price'] = test_df[feature].apply(predict_price, feature_name=feature)
test_df['squared_price'] = (test_df['price'] - test_df['my_price']) ** 2
mean_value = test_df['squared_price'].mean()
rmse = mean_value ** (1/2)
print('{}: {}'.format(feature, rmse))
accommodates: 101.82695124572865
bedrooms: 101.82695124572865
bathrooms: 99.81280478976633
number_of_reviews: 87.14700224333595
基于多变量KNN模型
基于多变量的KNN模型意思是同时参考多个特征来选取距离⾃⼰最近的K个样本,即需要同时参考多个特征来计算与⾃⼰的距离,这种情况通常不会再⾃⼰写计算距离的算法了,⽽是借助于其他已经实现好的库,⽐如scipy库中的distance模块“scipy.spatial.distance”或者sklearn 库中的neighbors模块“sklearn.neighbors”。

以下就来介绍⼀下如何使⽤这两个库来实现KNN算法。

1. 数据准备:标准化和归⼀化
# 重新获取数据
key_info = ['accommodates', 'bedrooms', 'bathrooms', 'beds', 'price', 'minimum_nights', 'maximum_nights', 'number_of_reviews']
keyinfo_df = rental_df[key_info]
keyinfo_df['price'] = keyinfo_df['price'].str.replace('$', '').astype(float)
keyinfo_df.head()
from sklearn.preprocessing import StandardScaler
# 去除数据中的空值,⽰例中虽然没有空值,但实际操作中应该有这⼀步,⾄少要处理⼀下空值
keyinfo_df = keyinfo_df.dropna()
# 将数据归⼀化和标准化,这⾥采⽤的处理⽅式是StandardScaler中的fit_transform⽅法,即均值⽅差归⼀化
# 这种归⼀化⽅法会使得数据集的⽅差为1,均值为0,符合标准正态分布,公式为:X=(x-µ)/σ
# 可以发现处理后的数据它们之间的差值变⼩了,这样的话使⽤欧式距离计算的误差就会更⼩了
keyinfo_df[key_info] = StandardScaler().fit_transform(keyinfo_df[key_info])
# 这⾥只是另外起⼀个变量名称,表⽰它已经是标准化的数据了
normalized_df = keyinfo_df
normalized_df.head()
2. 使⽤scipy.spatial.distance计算距离
scipy库中的distance模块有多种计算距离的⽅法,这⾥使⽤cdist()⽅法进⾏计算,这个⽅法默认也是采⽤欧⽒距离的计算⽅式,当然也可以通过指定metric参数更换距离计算⽅式,具体⽀持的距离计算⽅式可以⾃⾏去查阅API⽂档,这⾥就不单独介绍了。

from scipy.spatial import distance
normal_train_df = normalized_df.copy().iloc[:20]
normal_test_df = normalized_df.copy().iloc[20:]
def predict_price_multi(feature_values, feature_names):
"""
根据多个特征值feature_values,在训练集中根据指定的多个特征feature_names得出预测的价格
"""
temp_df = normal_train_df
# distance.cdist⽤于计算两组数据之间的距离,可使⽤metric参数指定距离的计算⽅式,默认为euclidean(欧⼏⾥得距离,即欧式距离)
temp_df['distance'] = distance.cdist(temp_df[features], [feature_values[feature_names]])
temp_df = temp_df.sort_values('distance')
my_price = temp_df['price'].iloc[:5].mean()
return my_price
# 同时使⽤多个特征参与距离的计算
features = ['accommodates', 'bedrooms', 'bathrooms']
normal_test_df['my_price'] = normal_test_df[features].apply(predict_price_multi, feature_names=features, axis=1)
# 根据公式计算均⽅根误差
normal_test_df['squared_price'] = (normal_test_df['price'] - normal_test_df['my_price']) ** 2
mean_value = normal_test_df['squared_price'].mean()
rmse = mean_value ** (1/2)
rmse
输出结果:0.925497918931224
注:可以看到,通过数据的标准化和归⼀化,最后计算出来的均⽅根误差值完全不⼀样了,⼩了很多,这样也能更符合我们对于误差的⽐较和判断⽅式了。

3. 使⽤sklearn.neighbors实现KNN
sklearn库是Python中机器学习的库,⼤多机器学习的操作和算法在sklearn中都能找到,当然也包括KNN算法,⽽且都已经封装好
了,我们只需要拿来⽤就⾏了。

from sklearn.neighbors import KNeighborsRegressor
from sklearn.metrics import mean_squared_error
features = ['accommodates', 'bedrooms', 'bathrooms']
# 创建⼀个KNN回归器,默认k=5,即参数n_neighbors=5
knr = KNeighborsRegressor()
# 传⼊训练集和⽬标值
knr.fit(normal_train_df[features], normal_train_df['price'])
# 根据提供的测试集算出⽬标值
feature_predict = knr.predict(normal_test_df[features])
# 计算均⽅根误差
mse = mean_squared_error(normal_test_df['price'], feature_predict) rmse = mse ** (1/2)
rmse
输出结果:1.139347164704861。

相关文档
最新文档