- 超参数和模型参数
- 超参数:距离的权重
- 距离公式的选择
- 网格搜索最佳模型
- 数据归一化
- 最值归一化 normalization
- 均值方差归一化 Standardization
- 对测试数据集如何归一化
- 使用sklearn库中的StandardScaler(均值方差归一化)
- 实现自己的StandardScaler
- KNN的缺点
1.超参数和模型参数
- 超参数:再算法运行前需要决定的参数。例如KNN中的k参数就是典型的超参数
- 模型参数:算法过程中学习的参数,例如y = ax+b,a和b就是再学习过程中训练出来的参数。
KNN算法没有模型参数
KNN算法中的k是典型的超参数
寻找好的超参数
- 领域知识
- 经验数值
- 实验搜索
寻找最好的k
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
# 加载手写识别数据
df = datasets.load_digits()
# 数据
mdata = df.data
# 标签
mlabel = df.target
# 分割数据集
X_train, X_test, y_train, y_test = train_test_split(mdata,mlabel,test_size=0.2)
# 寻找最好k值
best_k = -1
best_score = 0
for i in range(1,11):
knn_clf = KNeighborsClassifier(n_neighbors=i)
knn_clf.fit(X_train,y_train)
score = knn_clf.score(X_test,y_test)
if score>best_score:
best_score = score
best_k = i
print(best_k) # 3
print(best_score) # 0.9805555555555555
把指定一个范围中的k都实验一遍,看谁的正确率高,就是谁。
kNN的另外一个超参数:距离的权重
一般情况下使用距离的倒数作为权重
那么是考虑距离还是不考虑距离呢,肯定是谁的正确率高就用谁了。
数据集还是使用上边的,权重的值有:
- 'uniform':不考虑距离,只考虑个数
- 'distance':考虑距离和权重
best_method = ""
best_score = 0.0
best_k = -1
for method in ["uniform","distance"]:
for k in range(1,11):
knn_clf = KNeighborsClassifier(n_neighbors=k,weights=method)
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_method = method
print("best_k=",best_k)
print("best_score=",best_score)
print("best_method=",best_method)
# best_k= 3
# best_score= 0.9777777777777777
# best_method= uniform 不考虑距离
但是由于数据分割是随机的,所以这个情况是多变的。
距离公式
- 欧拉距离
- 曼哈顿距离
- 两种距离的整理对比
- 明克夫斯基距离
到这里,我们获得了一个新的超参数 p。
所以而我们现在要讨论的是,再计算距离的时候,是使用欧拉距离还是曼哈顿距离还是明可夫斯基距离呢?
也就是说上边公式的p的值为多少时最好? 正确率更高,失误率更少呢?
那么我们呢需要试验一下:
best_p = -1
best_score = 0.0
best_k = -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= 1
# best_score= 0.9916666666666667
分析:
在sklearn库中的KNN算法类中,逐个传入p的值,来控制计算公式,
这里采用控制变量法,控制k的值,逐个实验p计算,算出正确率最好的p,接着下一个k值,最终找出最好的p和k。
这个结果根据训练集和测试及数据的不同,会发生相应的变化。
2. 网格搜索
网格搜索是用于寻找最佳的算法模型(最佳的超参数)。
# 调用GridSearchCV创建网格搜索对象,传入参数为Classifier对象以及参数列表
from sklearn.model_selection import GridSearchCV, train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn import datasets
# 1.创建网格搜索超参数数据--[{},{}]
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)]
}
]
# 2.先new一个默认的Classifier对象,确定算法
knn_clf = KNeighborsClassifier()
# 3.GridSearchCV(算法对象,超参数数据)
grid_search = GridSearchCV(knn_clf,param_grid)
# 加载数据
df = datasets.load_iris()
mdata = df.data
mlabel = df.target
X_train, X_test, y_train, y_test = train_test_split(mdata,mlabel,test_size=0.2)
# 4.添加数据
grid_search.fit(X_train,y_train)
# 5.返回网格对象的最佳参数
ps = grid_search.best_params_
print(ps) # {'n_neighbors': 10, 'weights': 'uniform'}
# 获取最佳模型,也就是算法模型+最佳参数的一个最佳模型
mod = grid_search.best_estimator_
# 验证该模型是不是之前创建的默认算法KNN模型
print(mod is knn_clf) # False
# 测试 ,测试数据
result = mod.score(X_test,y_test)
print(result) # 1.0
分析:
- 使用sklearn中的一个模块GridSearchCV--网格交叉验证,用于寻找算法和参数之间的最佳值。
- 组织超参数的数据结构---类似于[{key:val},{key:val},...],{}里的val一般为多个值,使用list的方式赋值。
- 确定算法模型,创建KNN算法对象knn_clf。
- 创建网格对象grid_search,将算法对象和超参数数据传进去,也有其他的参数:
n_jobs :多线程并行处理,占用几个核,-1为使用所有的核
verbose :是否打印搜索信息,传入值越大,输出信息越详细
- 添加分割号的训练集数据到网格搜索对象的fit()中。
- 这是的网格对象有很多的参数,例如:
- best_estimator_:最好的算法模型
- best_score_:最好的正确率
- best_params_:最好的超参数
- 根据best_estimator_参数找到最好的算法模型(最佳参数),调用score(x,y)进行测试测试集,输出最好的正确率。
3. 数据归一化
样本间的距离被一个字段所主导
解决方案 :将所有的数据映射到同一尺度
最值归一化 normalization:把所有数据映射到0-1之间
- X表示这列数据的每个值,需要映射。
- Xmin表示这列数据中的最小值
- Xmax表示这列数据中的最大值
- Xscale表示最终映射的结果
适用于分布有明显边界的情况;受outlier影响较大。
原因:当一列数据有一个很大的值时,那么最值归一化公式的分母就会变得无限大,分子相对来说很小,例如0.00001,那么这样的话,该列中数据的最小值无限小,最大值为1,这样的映射没有意义。
一维数组最值归一化:
import numpy as np
x = np.random.randint(0,100,size=100)
print(x)
# [51 80 57 96 35 98 75 51 52 56 86 27 16 39 69 59 27 63 28 66 75 74 48 51
# 64 6 69 25 41 33 7 69 99 32 55 37 59 73 47 58 38 60 64 9 41 64 37 57
# 65 7 57 4 12 2 30 96 55 36 43 18 54 81 30 93 95 64 16 62 41 14 65 99
# 57 36 81 10 16 55 66 0 89 21 66 91 50 14 20 29 57 14 36 34 44 42 47 46
# 37 1 4 46]
x_one = (x-np.min(x))/(np.max(x)-np.min(x))
print(x_one)
# [0.51515152 0.80808081 0.57575758 0.96969697 0.35353535 0.98989899
# 0.75757576 0.51515152 0.52525253 0.56565657 0.86868687 0.27272727
# 0.16161616 0.39393939 0.6969697 0.5959596 0.27272727 0.63636364
# 0.28282828 0.66666667 0.75757576 0.74747475 0.48484848 0.51515152
# 0.64646465 0.06060606 0.6969697 0.25252525 0.41414141 0.33333333
# 0.07070707 0.6969697 1. 0.32323232 0.55555556 0.37373737
# 0.5959596 0.73737374 0.47474747 0.58585859 0.38383838 0.60606061
# 0.64646465 0.09090909 0.41414141 0.64646465 0.37373737 0.57575758
# 0.65656566 0.07070707 0.57575758 0.04040404 0.12121212 0.02020202
# 0.3030303 0.96969697 0.55555556 0.36363636 0.43434343 0.18181818
# 0.54545455 0.81818182 0.3030303 0.93939394 0.95959596 0.64646465
# 0.16161616 0.62626263 0.41414141 0.14141414 0.65656566 1.
# 0.57575758 0.36363636 0.81818182 0.1010101 0.16161616 0.55555556
# 0.66666667 0. 0.8989899 0.21212121 0.66666667 0.91919192
# 0.50505051 0.14141414 0.2020202 0.29292929 0.57575758 0.14141414
# 0.36363636 0.34343434 0.44444444 0.42424242 0.47474747 0.46464646
# 0.37373737 0.01010101 0.04040404 0.46464646]
x = np.random.randint(0,100,size=100)
x[0] = 100000
print(x)
# [100000 85 92 96 40 50 80 94 13 63
# 49 9 17 34 21 37 11 56 25 91
# 58 46 0 91 99 16 21 49 58 44
# 64 30 8 84 63 98 32 43 56 13
# 3 26 65 99 32 67 74 97 76 81
# 79 59 17 13 96 12 67 64 22 9
# 59 35 24 71 48 82 6 79 80 88
# 44 18 11 3 59 95 8 17 59 3
# 93 54 72 8 45 15 64 80 81 37
# 86 12 10 21 54 77 40 5 59 80]
x_one = (x-np.min(x))/(np.max(x)-np.min(x))
print(x_one)
# [1.0e+00 8.5e-04 9.2e-04 9.6e-04 4.0e-04 5.0e-04 8.0e-04 9.4e-04 1.3e-04
# 6.3e-04 4.9e-04 9.0e-05 1.7e-04 3.4e-04 2.1e-04 3.7e-04 1.1e-04 5.6e-04
# 2.5e-04 9.1e-04 5.8e-04 4.6e-04 0.0e+00 9.1e-04 9.9e-04 1.6e-04 2.1e-04
# 4.9e-04 5.8e-04 4.4e-04 6.4e-04 3.0e-04 8.0e-05 8.4e-04 6.3e-04 9.8e-04
# 3.2e-04 4.3e-04 5.6e-04 1.3e-04 3.0e-05 2.6e-04 6.5e-04 9.9e-04 3.2e-04
# 6.7e-04 7.4e-04 9.7e-04 7.6e-04 8.1e-04 7.9e-04 5.9e-04 1.7e-04 1.3e-04
# 9.6e-04 1.2e-04 6.7e-04 6.4e-04 2.2e-04 9.0e-05 5.9e-04 3.5e-04 2.4e-04
# 7.1e-04 4.8e-04 8.2e-04 6.0e-05 7.9e-04 8.0e-04 8.8e-04 4.4e-04 1.8e-04
# 1.1e-04 3.0e-05 5.9e-04 9.5e-04 8.0e-05 1.7e-04 5.9e-04 3.0e-05 9.3e-04
# 5.4e-04 7.2e-04 8.0e-05 4.5e-04 1.5e-04 6.4e-04 8.0e-04 8.1e-04 3.7e-04
# 8.6e-04 1.2e-04 1.0e-04 2.1e-04 5.4e-04 7.7e-04 4.0e-04 5.0e-05 5.9e-04
# 8.0e-04]
二维数组均值归一化:
x = np.random.randint(0,50,(25,2))
x = np.array(x,dtype=float)
print(x)
# [[ 7. 31.]
# [ 3. 71.]
# [34. 74.]
# [98. 87.]
# [92. 68.]
# [23. 16.]
# [68. 23.]
# [18. 70.]
# [39. 78.]
# [45. 64.]
# [22. 7.]
# [75. 32.]
# [73. 74.]
# [78. 35.]
# [90. 70.]
# [78. 6.]
# [15. 76.]
# [ 4. 41.]
# [30. 44.]
# [46. 41.]
# [ 9. 58.]
# [84. 79.]
# [62. 48.]
# [73. 48.]
# [48. 39.]]
x[:,0] = (x[:,0] - np.min(x[:,0])) / (np.max(x[:,0])-np.min(x[:,0]))
x[:,1] = (x[:,1] - np.min(x[:,1])) / (np.max(x[:,1])-np.min(x[:,1]))
print(x)
# [[0. 0.79166667]
# [0.70731707 0.79166667]
# [0.56097561 0.4375 ]
# [0.6097561 0.3125 ]
# [0.29268293 0.39583333]
# [0.70731707 0.95833333]
# [0.92682927 0.66666667]
# [0.97560976 1. ]
# [0.6097561 0.16666667]
# [0.80487805 0.625 ]
# [0.02439024 0.04166667]
# [0.3902439 0.91666667]
# [0.24390244 0.89583333]
# [0.34146341 0.72916667]
# [0. 0.91666667]
# [0.73170732 0.02083333]
# [0.19512195 0.72916667]
# [0.34146341 0.8125 ]
# [0.02439024 0.27083333]
# [0.73170732 0. ]
# [0.48780488 0.77083333]
# [0.56097561 0.41666667]
# [1. 0.0625 ]
# [0.46341463 0.14583333]
# [0.73170732 0.20833333]]
均值方差归一化 Standardization
把所有数据归一到均值为0方差为1的分布中
适用于数据分布没有明显边界,有可能存在极端情况值。
- Xmean:该列数据的均值
- S:该列数据的标准差
一维数组的均值方差归一化:
import numpy as np
x = np.random.randint(0,100,size=100)
print(x)
ava = np.mean(x)
print(ava)
# 53.64
std = np.std(x)
print(std)
# 30.182286195714205
x_one = (x-ava)/std
print(x_one)
二维数组的均值方差归一化:
import matplotlib.pyplot as plt
X2 = np.random.randint(0,100,(50,2))
X2 = np.array(X2,dtype=float)
X2[:,0] = (X2[:,0]-np.mean(X2[:,0]))/np.std(X2[:,0])
X2[:,1] = (X2[:,1]-np.mean(X2[:,1]))/np.std(X2[:,1])
print(X2)
plt.scatter(X2[:,0],X2[:,1])
plt.show()
图:
根据均值方差归一化公式得:每列数据标准值都是0,方差都为1:
std = np.mean(X2[:,0])
print(std)
# 7.105427357601002e-17
std = np.mean(X2[:,1])
print(std)
# -9.325873406851315e-17
std = np.std(X2[:,0])
print(std) # 1.0
std = np.std(X2[:,1])
print(std) # 1.0
4.对测试数据集如何归一化
未知测试数据归一化结果 = (测试数据-训练数据的均值)/训练数据的标准差
5.在scikit-learn中使用Scaler
使用sklearn库中的StandardScaler(均值方差归一化):
import numpy as np
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
iris = datasets.load_iris()
X = iris.data
y = iris.target
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=666)
# 创建StandardScaler对象
ss = StandardScaler()
# 将训练数据传进fit方法中
ss.fit(X_train)
# 均值
ava = ss.mean_
print(ava)
# [5.83416667 3.08666667 3.70833333 1.17 ]
# 标准差
sc = ss.scale_
print(sc)
# [0.81019502 0.44327067 1.76401924 0.75317107]
'''均值方差归一化映射'''
X_train = ss.transform(X_train)
print(X_train)
X_test = ss.transform(X_test)
print(X_test)
'''knn算法测试'''
from sklearn.neighbors import KNeighborsClassifier
knn_clf = KNeighborsClassifier(n_neighbors=3)
knn_clf.fit(X_train,y_train)
result = knn_clf.score(X_test,y_test)
print(result)
# 1.0
实现自己的StandardScaler
class StandardScaler:
def __init__(self):
self.mean = None
self.scale = None
def fit(self,X):
# 每类的均值数组
self.mean = [np.mean(X[:,i]) for i in range(X.shape[1])]
# 每列的标准差
self.scale = [np.std(X[:,i]) for i in range(X.shape[1])]
def transform(self,X):
# 创建一个与传进来的X形状相同的array,类型为float
resX = np.empty(shape=X.shape, dtype=float)
# 遍历X的列数
for col in range(X.shape[1]):
# 将resX中的对应列替换为传进来的数据集X通过计算的来的归一化的数据
resX[:,col] = (X[:,col]-self.mean[col]) / self.scale[col]
return resX
在数据集归一化中调用:
se = StandardScaler() # 实例化类对象
se.fit(X_train) # 计算平均值和标准差
X_train2 = se.transform(X_train) # 归一化训练集
X_test2 = se.transform(X_test) # 归一化测试集
print(X_test2)
# 测试算法
knn_clf2 = KNeighborsClassifier(n_neighbors=3)
knn_clf2.fit(X_train2,y_train)
result = knn_clf2.score(X_test2,y_test)
print(result) # 1.0
6.最值归一化的算法
最直归一化算法相对简单,不在解释
KNN的缺点
分析:
- 在预测一个新的测试集中的每一个点时,都需要计算该点与训练集中的每一个点之间来计算距离,因为有m个点,所以要进行for循环m次。
- n个特征表示的就是n维的数据,那么在计算的公式中计算时,就要计算n次减法平方,这样在计算公式的底层就循环了n次。
- 那么上述总计下来时间复杂度就是(m*n)
网友评论