目标设立:借助本门课程,同时结合sklearn框架学习机器学习,目的是熟悉机器学习的一般流程,熟悉机器学习框架库的使用,了解部分机器学习KNN算法的原理。
如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。通俗一点可以理解为,每一个样本数据都在分布在特征空间中的一个点,对于一个需要预测的样本,KNN算法根据计算距离距离该样本点最近的K的点,来对该样本进行预测结果。
超参数是在算法运行前就需要赋值的参数,算法的不同超参数,给模型提供了一定了灵活性,可以结合不同场景来筛选出更适合这批数据的超参数值,提高模型预测的准确率。
那么怎么才能更快的寻找到好的超参数呢?
在KNN算法中,有一个十分重要的超参数就是,选择距离待预测样本点最近应该有多少个点,比如我们可以遍历某一段范围的K值,然后判断结果中那个K值的得分最高,从而进行确定应该如何选取哪个K。
下面的代码是使用sklearn中封装好的KNN算法进行简单预测,在手动指定参数K的情况下进行预测:
import numpy as np
from sklearn import datasets
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
# 获取数据
iris = datasets.load_iris()
X_data = iris.data
y_data = iris.target
# 数据预处理
X_train, X_test, y_train, y_test = train_test_split(X_data, y_data, test_size = 0.2, random_state=600)
# 模型训练
knn_clf = KNeighborsClassifier(n_neighbors=6)
knn_clf.fit(X_train, y_train)
train_predict = knn_clf.predict(X_train)
test_predict = knn_clf.predict(X_test)
# 模型评估
print(sum(train_predict==y_train)/len(y_train))
print(sum(test_predict==y_test)/len(y_test))
# 预测实际数据
pass
pass
输出结果
$ python3 knn.py
$ 0.9666666666666667
$ 0.9333333333333333
一般情况下使用距离的导数作为权重。
考虑这样一种情况,待预测样本点,距离最近的三个点,都是不同类别的,这个时候就无法分类出该样本点应该属于哪一个类别,如果我们在一开始就赋予距离的权重,就可以比较容易的预测出值。
欧拉距离
∑ i = 1 n ( X i ( a ) − X i ( b ) ) 2 \sqrt{ \sum_{i=1}^{n} ( X_i^{(a)} - X_i^{(b)} )^2 } i=1∑n(Xi(a)−Xi(b))2
曼哈顿距离
∑ i = 1 n ∣ X i ( a ) − X i ( b ) ∣ \sum_{i=1}^{n}| X_i^{(a)} - X_i^{(b)} | i=1∑n∣Xi(a)−Xi(b)∣
明可夫斯基推导:
可以将欧拉距离和曼哈顿距离整理一下变为:
( ∑ i = 1 n ∣ X i ( a ) − X i ( b ) ∣ 1 ) 1 1 (\sum_{i=1}^{n}| X_i^{(a)} - X_i^{(b)} |^1)^{\frac{1}{1}} (i=1∑n∣Xi(a)−Xi(b)∣1)11
( ∑ i = 1 n ∣ X i ( a ) − X i ( b ) ∣ 2 ) 1 2 (\sum_{i=1}^{n}| X_i^{(a)} - X_i^{(b)} |^2)^{\frac{1}{2}} (i=1∑n∣Xi(a)−Xi(b)∣2)21
抽取出公共参数p,得到明可夫斯基距离,同时我们也得到一个新的超参数P。
( ∑ i = 1 n ∣ X i ( a ) − X i ( b ) ∣ p ) 1 p z (\sum_{i=1}^{n}| X_i^{(a)} - X_i^{(b)} |^p)^{\frac{1}{p}}z (i=1∑n∣Xi(a)−Xi(b)∣p)p1z
对于每一个超参数以及不同组合的超参数,我们知道轮子是怎么造的,就不需要重复造轮子,可以使用已有的工具进行搜寻。下面使用sklearn中封装好的网格搜索,寻找上门列出的三个最佳超参数。
import numpy as np
from sklearn import datasets
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
# 获取数据
iris = datasets.load_iris()
X_data = iris.data
y_data = iris.target
# 数据预处理
X_train, X_test, y_train, y_test = train_test_split(X_data, y_data, test_size = 0.25, random_state=666)
# 网格搜索最佳超参数
param_grid = [
# 不考虑距离权重
{
'weights' : ['uniform'],
'n_neighbors' : [i for i in range(1, 15)] # 选取不同的近邻点
},
# 考虑距离权重
{
'weights' : ['distance'],
'n_neighbors' : [i for i in range(1, 15)],
'p' : [i for i in range(1,10)] # 距离方程推导出的超参数p
}
]
knn_clf = KNeighborsClassifier()
from sklearn.model_selection import GridSearchCV
# 网格搜索
grid_search = GridSearchCV(knn_clf, param_grid)
grid_search.fit(X_train, y_train)
print(grid_search.best_score_)
print(grid_search.best_params_)
输出结果
$ python3 knn.py
$ 0.9732142857142857
$ {'n_neighbors': 7, 'p': 4, 'weights': 'distance'}
为了判断我们训练的模型是否有效,我们一般不会直接使用实际的数据进行预测,第一个原因是,万一训练出来的模型效果很差,如果直接在真实环境中使用的话,会有一定概率造成损失,比如在某书籍阅读APP,给用户推荐的完全没有兴趣甚至极度不喜欢的书籍,有一定的可能会造成用户流失。
还有一个原因是,在某些场景很难拿到真实的lable数据,所以更加合理的方式应该是,将数据集按照一定比例划分为训练集和测试集,在离线环境先使用训练集中的一部分数据进行评测效果。
而网格搜索可以帮助我们去搜索出更加合理的超参数,提高模型的准确度。
在我看来机器学习是由数据驱动,越来越多的数据产生,人们应该如何快速有规律的从这么多的数据中发现新的商业价值是一门很大的学问,而同样驱动的一些新新科技,也能推动社会的进步。
路漫漫其修远兮 吾将上下而求索
维基百科:最近邻算法
慕课网 机器学习教程
吴恩达机器学习视频