机器学习笔记(五):超参数 | 凌云时刻

凌云时刻 · 技术

导读:上一篇笔记主要介绍了通过PyCharm封装kNN算法并且在Jupyter Notebook中调用,以及计算器算法的封装规范,kNN的k值如何计算,如何使用Scikit Learn中的kNN算法。这一篇笔记的主要内容是机器学习算法中的一些主要概念,比如训练数据集、测试数据集,分类准确度,超参数。

作者 | 计缘

来源 | 凌云时刻(微信号:linuxpk)

超参数


目前我们在使用kNN算法时,k的值都是我们给定的值,这个作为算法的参数值称为超参数,也就是在运行机器学习算法之前需要指定的参数。还有一类参数称为模型参数,既在算法过程中学习的参数,但是大家已经知道kNN算法实际是没有模型的,所以也不存在模型参数,但是k值是一个典型的超参数。


 寻找最好的k值

 

Scikit Learn中kNN算法的k值默认是5,有时候这个值并不是最优的值,那么我们可以通过一个简单的方式来寻找到最优的k值,那就是给定一个k值的范围,然后循环传入算法求训练分数最好的那个k值:

 

# 首先我们使用scikit learn中的手写数字数据集,并将其拆分为训练数据集和测试数据集
import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
digits = datasets.load_digits()
X = digits.data
y = digits.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 666)

# 然后通过循环的方式寻找最好的k值
best_score = 0.0
best_k = -1
for k in range(1, 11):
	knn_clf = KNeighborsClassifier(n_neighbors = k)
	knn_clf.fit(X_train, y_train)
	score = knn_clf.score(X_test, y_test)
	if score > best_score:
		best_k = k
		best_score = score

print("best_k = ", best_k)
print("best_score = ", best_score)

# 结果
best_k =  4
best_score =  0.991666666667

  

从上面的代码示例中可以看到,在1到10这个范围的k值中,4是训练分数最高的k值。不过这里需要注意的是,如果求出k为10,那么我们需要再扩大范围进行寻找,因为有可能10并不是最优的k值,只因为我们给定的范围最大到10,所以这种情况下,我们需要根据实际情况对8至20的k值范围再进行计算,如果结果仍然为10,那么才认定10为最优k值。

 距离的权重

机器学习笔记(五):超参数 | 凌云时刻_第1张图片

上面这张图如果用之前我们了解过的kNN算法来分析的话,绿色的点肯定是属于蓝色点分类的,但是我们之前都一直忽略了一个问题,那就是当找到k个相邻的点后,在投票时是没有再考虑未知分类点与相邻点之间的距离的。就比如上图,如果考虑了3个最近相邻点与绿色点之间的距离的话,那么绿色点的分类就会属于红色点的分类,因为在计算距离权重时是取距离的倒数,所以绿色点与红色点的距离权重为1,绿色点与两个蓝色点的距离权重为1/3 + 1/4 = 7/12。

机器学习笔记(五):超参数 | 凌云时刻_第2张图片

上图的情况如果不考虑距离权重的话,就会出现平票的情况,那么只能随机在三个分类中选一个作为绿色点的分类,如果加上距离权重,就能确定得出绿色点的分类了。

所以与相邻点的距离权重是kNN算法的另一个重要的超参数,大家可以看一下Scikit Learn的kNN官网,KNeighborsClassifier的构造函数中有一个参数weights,这就是距离权重参数,默认值为uniform,既不考虑距离权重,如果要考虑距离权重的话,需要设置值为distance

knn_clf = KNeighborsClassifier(n_neighbors = 4, weights = 'distance')


 距离的类

到目前为止,我们自己封装的kNN算法使用的距离公式是欧拉距离,其实还有其他的距离公式,比如曼哈顿距离:

   

其实曼哈顿距离和欧拉距离在数学公式表现形式上是有一定相似性的,我们可以将欧拉距离做以转换:

   

对曼哈顿距离也做以转换:

   

通过上面两个公式可以得到一个共性的公式:

   

这个公式就称之为明可夫斯基距离(Minkowski Distance)

既当p为1时为曼哈顿距离,当p为2时为欧拉距离,当p大于2时表示其他距离,所以p又是一个kNN算法的超参数,在KNeighborsClassifier的构造函数中同样有一个参数p就是表示使用的距离类型,默认为2,既默认为欧拉距离。

best_score = 0.0
best_k = -1
best_p = -1
for k in range(1, 11):
	for p in range(1, 6):
		knn_clf = KNeighborsClassifier(n_neighbors = k, weights = "distance", p = p)
		knn_clf.fit(X_train, y_train)
		score = knn_clf.score(X_test, y_test)
		if score > best_score:
			best_k = k
			best_score = score
			best_p = p

print("best_p = ", best_p)
print("best_k = ", best_k)
print("best_score = ", best_score)

# 结果
best_p =  2
best_k =  3
best_score =  0.988888888889

 

从上面代码运行的结果来看,最优的p值为2,也就是欧拉距离,考虑了距离权重后,最优k值为3。而且一些超参数是组合使用的,比如当使用超参数p时,距离权重的超参数weights的取值就必须是distance。并且kp这两个超参数双重嵌套循环,就组成了一个类似网格的搜索方式,所幸Scikit Learn提供了封装好的网格搜索的方法供我们使用。

网格搜索超参数

在使用网格搜索前,我们需要先将各种超参数的组合定义出来:

 

param_grid = [
	{
		'weights': ['uniform'],
		'n_neighbors': [i for i in range(1, 11)]
	},
	{
		'weights': ['distance'],
		'n_neighbors': [i for i in range(1, 11)],
		'p': [i for i in range(1, 6)]
	}
]

  

我们定义了一个param_grid数组,元素为字典,每个字典描述了一种超参数的组合,下面我们使用Scikit Learn提供的GridSearchCV来使用我们定义好的超参数组合:

from sklearn.model_selection import GridSearchCV
knn_clf = KNeighborsClassifier()
grid_search = GridSearchCV(knn_clf, param_grid)
grid_search.fit(X_train, y_train)
new_knn_clf = grid_search.best_estimator_
new_knn_clf
# 结果
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
		   metric_params=None, n_jobs=1, n_neighbors=3, p=3,
		   weights='distance')

上面的示例代码不难理解,我们使用构建出的kNN分类器knn_clf和超参数组合param_grid构造出了网格搜索对象grid_search,通过它进行fit操作,这个过程就是根据我们提供的超参数组合进行搜寻,找到最优的超参数组合。通过best_estimator_返回新的,已经设置了最优超参数组合的kNN分类器对象。从输出结果其实已经可以看到首先是选择了考虑距离权重的超参数组合,然后求出了k值,也就是n_neighbors为3,p值为3。

GridSearchCV也提供了几个属性,可以让我们方便的查看超参数和模型评分:

 

grid_search.best_params_
# 结果
{'n_neighbors': 3, 'p': 3, 'weights': 'distance'}

grid_search.best_score_
# 结果
0.98538622129436326



 GridSearchCV的其他参数

在构造GridSearchCV对象时除了kNN分类器和超参数组合外,还有几个比较有用的参数:

  • n_jobs:该参数决定了在进行网格搜索时使用当前计算机的CPU核数,1就是使用1个核,2就是使用2个核,如果设置为-1,那么代表使用所有的核进行搜索。

  • verbose:该参数决定了在网格搜索时的日志输出级别。

  

END

往期精彩文章回顾

机器学习笔记(四):kNN算法

机器学习笔记(三):NumPy、Matplotlib、kNN算法

机器学习笔记(二):矩阵、环境搭建、NumPy

机器学习笔记(一):机器的学习定义、导数和最小二乘

Kafka从上手到实践 - 实践真知:搭建Kafka相关的UI工具

Kafka从上手到实践 - Kafka集群:启动Kafka集群

Kafka从上手到实践 - Kafka集群:Kafka Listeners

Kafka从上手到实践 - Kafka集群:配置Broker

Kafka从上手到实践:搭建Zookeeper集群

Kafka从上手到实践-Zookeeper CLI:CRUD zNode

长按扫描二维码关注凌云时刻

每日收获前沿技术与科技洞见

你可能感兴趣的:(机器学习笔记(五):超参数 | 凌云时刻)