一、自编数据简单操作
import numpy as np
import matplotlib.pyplot as plt
# 这里先用假的数据集
raw_data_X = [[ 3.3935,2.3312],
[3.1101,1.7815],
[1.3438,3.3684],
[3.5823,4.6792],
[2.2804,2.8670],
[7.4234,4.6965],
[5.7451,3.5340],
[9.1721,2.5110],
[7.7928,3.4241],
[7.9398,0.7917]]
raw_data_y = [0,0,0,0,0,1,1,1,1,1] # 0良性肿瘤,1恶性肿瘤
X_train = np.array(raw_data_X)
y_train = np.array(raw_data_y)
X_train[y_train==0]
array([[3.3935, 2.3312],
[3.1101, 1.7815],
[1.3438, 3.3684],
[3.5823, 4.6792],
[2.2804, 2.867 ]])
plt.scatter(X_train[y_train == 0,0] ,X_train[y_train==0,1],color='black')
plt.scatter(X_train[y_train == 1,0] ,X_train[y_train==1,1],color='red')
plt.show()
x = np.array([8.0934,3.3657]) #新的样本点
plt.scatter(X_train[y_train == 0,0] ,X_train[y_train==0,1],color='black')
plt.scatter(X_train[y_train == 1,0] ,X_train[y_train==1,1],color='red')
plt.scatter(x[0],x[1],color='blue')
plt.show()
distances = []#保存新样本点与原来点的距离
from math import sqrt
for x_train in X_train:
d = sqrt(np.sum((x_train-x)**2))
distances.append(d)
distances
[4.81240587024827,
5.2290504424799735,
6.749600540031981,
4.698436491004216,
5.834352636754141,
1.4899424955346439,
2.3543232106063954,
1.3762651561381616,
0.3062203781592608,
2.578578864413497]
np.argsort(distances) # 返回的是排序后数在原先数组中的下标
array([8, 7, 5, 6, 9, 3, 0, 1, 4, 2], dtype=int64)
nearset = np.argsort(distances)
k = 6
topK_y = [y_train[i] for i in nearset[:k]]# i在nearset数组中前k个元素
from collections import Counter
Counter(topK_y)
Counter({1: 5, 0: 1})
votes = Counter(topK_y)
predict_y = votes.most_common(1)[0][0] # 获取投票最多的前1个类别,返回的是一个list,取该list中的第0号元素
print(predict_y)
1
def kNN_classify(k,X_train,y_train,x):
""" k: 选择最近的k个点 X_train:数据集 y_train:数据集对应的结果 x:输入数据,需要对其进行预测 """
distance = [sqrt(np.sum((x_train-x)**2)) for x_train in X_train]
# 这里x_train-x 发生的是矩阵的对应点相减 ,后产生一个相减后的矩阵,该矩阵对应点每一个元素都进行平方
nearset = np.argsort(distance)
# 返回排下序后元素,在原状态的下标,原数据未发生排序
topK_y = [y_train[i] for i in nearset[:k]]
# 返回距离输入数据最近的k个点的结果
votes = Counter(topK_y)
# 进行投票,也就是统计距离最近的K个结果的出现的次数
return votes.most_common(1)[0][0]
# 返回出现次数最多的1结果 第一个[0] 对应一个tuple eg:(1,5)意思是:1结果的票数是5
predict_y = kNN_classify(6,X_train,y_train,x)
print(predict_y)
1
二、sklearn 中的KNN
scikit-learn 是基于 Python 语言的机器学习工具。中文文档
回顾流程:
在监督学习算法中,训练数据集通常包含训练数据和数据标签,通过训练算法得到模型的过程叫拟合(fit)。输入样例送给模型后,得到输出结果的过程叫预测(predict)。可以说knn是一个不需要训练过程的算法。
from sklearn.neighbors import KNeighborsClassifier
kNN_classifier = KNeighborsClassifier(n_neighbors=6)# 这个参数就是k
kNN_classifier.fit(X_train,y_train) # sklearn中的每个学习算法都需要fit
x = np.array([8.0934,3.3657]).reshape(1, -1)# 新的样本点 ,注意这里的应该是1行2列,不是2行1列
kNN_classifier.predict(x)
array([1])
三、手撕代码(自定义实现KNN)
import numpy as np
from math import sqrt
from collections import Counter
class KNNClassifier:
def __init__(self,k):
"""初始化KNN分类器"""
assert k>=1 , "k must be valid"
self.k = k
self._X_train = None
self._y_train = None
def fit(self,X_train,y_train):
"""根据训练数据集 X_train y_train 训练KNN分类器"""
assert X_train.shape[0] == y_train.shape[0], \
"the size of X_train must be equal to the size of y_train"
assert self.k <= X_train.shape[0], \
"the size of X_train must be at least k."
self._X_train = X_train
self._y_train = y_train
return self
def predict(self,X_predict):
assert self._X_train is not None and self._y_train is not None, \
"must fit before predict!"
# X_predict矩阵的行数无所谓,但是列数必须和训练集中的一样
assert X_predict.shape[1] == self._X_train.shape[1], \
"the feature number of X_predict must be equal to X_train"
y_predict = [self._predict(x) for x in X_predict]
return np.array(y_predict)
def _predict(self,x):
"""给定单个待预测数据x,返回x的预测结果值"""
assert x.shape[0] == self._X_train.shape[1] , \
"""the feaure number of x must be equal to X_train"""
""" x : [8.0934 3.3657] x.shape : (2,) 所以使用 x.shape[0] == self._X_train.shape[1] """
distances = [sqrt(np.sum((x_train - x) ** 2)) for x_train in self._X_train]
nearest = np.argsort(distances)
topK_y = [self._y_train[i] for i in nearest[:self.k]]
votes = Counter(topK_y)
return votes.most_common(1)[0][0]
def __repr__(self):
return "KNN(k=%d)" % self.k
knn_clf = KNNClassifier(k=6)
knn_clf.fit(X_train,y_train)
X_predict = x = np.array([8.0934,3.3657]).reshape(1, -1)
y_predict = knn_clf.predict(X_predict)
y_predict
array([1])
四、判断机器学习算法的性能
将原始数据的大部分作为训练数据,剩下的一部分作为测试数据。
我们只用我们的训练数据训练出了模型,我们接下来就可以用没有参与到训练过程的测试数据来测试我们模型的好坏。
我们可以通过测试数据直接判断模型好坏,这样可以在模型进入真实环境前改进模型!
这种方式叫做:train test split
这里使用这种方式来测试之前写好的KNN算法。
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
iris = datasets.load_iris()# 使用iris数据集
X = iris.data # (150, 4)
y = iris.target #(150,)
X.shape
(150, 4)
y.shape
(150,)
y
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])
很明显数据是有序的,X中每行的数据对应y中的种类。测试集和训练集首先应该是无序的,才能起到效果。这里可以使用矩阵的合并将y合并到X后,将其顺序打乱。当前也可以,把把下标的顺序打乱,这样就不用动数据了。所以,开始对索引洗牌。
shuffle_indexs = np.random.permutation(len(X))#对150个连续数进行随机排列
shuffle_indexs
array([125, 26, 25, 121, 33, 113, 57, 94, 1, 99, 84, 128, 70,
122, 24, 41, 60, 74, 132, 30, 29, 89, 19, 117, 115, 43,
109, 32, 95, 35, 7, 46, 67, 144, 107, 37, 13, 92, 146,
36, 28, 127, 12, 17, 18, 52, 114, 135, 50, 8, 102, 86,
101, 130, 5, 34, 138, 68, 20, 56, 2, 72, 140, 116, 118,
139, 47, 110, 4, 22, 75, 39, 16, 111, 91, 0, 83, 44,
81, 119, 108, 14, 42, 21, 38, 105, 142, 49, 15, 103, 145,
134, 6, 11, 59, 54, 148, 82, 77, 143, 129, 55, 131, 66,
40, 45, 63, 87, 64, 123, 31, 79, 69, 137, 112, 124, 53,
76, 97, 23, 96, 149, 80, 3, 106, 141, 93, 61, 10, 88,
9, 126, 62, 133, 27, 51, 136, 100, 48, 78, 120, 73, 104,
71, 147, 85, 58, 98, 90, 65])
接下来开始将数据拆分为 训练集 和 测试集
test_ratio = 0.2
test_size = int(len(X) * test_ratio)
test_indexes = shuffle_indexs[:test_size]#前20%是测试数据
train_indexes = shuffle_indexs[test_size:]#后80%是训练数
X_train = X[train_indexes]
y_train = y[train_indexes]
X_test = X[test_indexes]
y_test = y[test_indexes]
那么,就完成了数据集的划分,可以将上述过程封装成一个函数
def train_test_split(X,y,test_ratio=0.2,seed=None):
if seed:
np.random.seed(seed) #支持指定随机种子,有了随机种子时候,随机种子数会对应一个矩阵。没有的话就是随机生成。
shuffle_indexs = np.random.permutation(len(X))
test_size = int(len(X) * test_ratio)
test_indexes = shuffle_indexs[:test_size]
train_indexes = shuffle_indexs[test_size:]
X_train = X[train_indexes]
y_train = y[train_indexes]
X_test = X[test_indexes]
y_test = y[test_indexes]
return X_train,X_test,y_train,y_test
使用此方法对数据差分,并应用到自己写的KNN分类算法中:
X_train,X_test,y_train,y_test = train_test_split(X,y) #其他两个参数取默认值
print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)
(120, 4)
(30, 4)
(120,)
(30,)
knn_clf = KNNClassifier(k=3)
knn_clf.fit(X_train,y_train)
y_predict = knn_clf.predict(X_test)#对所有的test数据进行预测
y_predict
array([2, 2, 1, 1, 0, 1, 1, 1, 2, 1, 0, 2, 1, 2, 1, 2, 0, 0, 2, 1, 2, 1,
2, 0, 1, 2, 2, 2, 1, 0])
30个测试集,拿到30个结果
y_predict == y_test # 对比准确性
array([ True, True, True, True, True, True, True, True, True,
True, True, True, True, True, True, True, True, True,
True, True, True, False, True, True, True, True, True,
True, True, True])
sum(y_predict == y_test) / len(y_test) # 将准确的结果的个数累加,计算准确率
0.9666666666666667
sklearn中封装的train_test_split
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.4)
print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)
(90, 4)
(60, 4)
(90,)
(60,)
注意:在sklearm中的测试集的比例名称是 test_size
五、准确率指标
使用sklearn中的手写数字识别数据集来了解 准确率指标 。
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
digits = datasets.load_digits() # 加载手写识别的数据
digits.keys() # 查看数据内容的类别
dict_keys(['data', 'target', 'frame', 'feature_names', 'target_names', 'images', 'DESCR'])
X = digits.data
X.shape
(1797, 64)
y = digits.target
y.shape
(1797,)
digits.target_names # 查看y值的标签
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
y[:100]
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1,
2, 3, 4, 5, 6, 7, 8, 9, 0, 9, 5, 5, 6, 5, 0, 9, 8, 9, 8, 4, 1, 7,
7, 3, 5, 1, 0, 0, 2, 2, 7, 8, 2, 0, 1, 2, 6, 3, 3, 7, 3, 3, 4, 6,
6, 6, 4, 9, 1, 5, 0, 9, 5, 2, 8, 2, 0, 0, 1, 7, 6, 3, 2, 1, 7, 4,
6, 3, 1, 3, 9, 1, 7, 6, 8, 4, 3, 1])
很明显数据是无序的 接下来 可视化一下 第0个样本
some_digit_image = X[0].reshape(8,8)
plt.imshow(some_digit_image,cmap="binary")
plt.axis('off')#显示坐标轴不好看
plt.show()
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y)
knn_clf = KNNClassifier(k=3)#用我们自己写的knn分类器
knn_clf.fit(X_train,y_train)
y_predict = knn_clf.predict(X_test)
y_predict
array([8, 5, 9, 9, 9, 6, 4, 3, 0, 2, 0, 5, 9, 4, 4, 7, 4, 3, 3, 5, 9, 3,
9, 2, 4, 6, 1, 5, 2, 3, 0, 2, 2, 8, 2, 4, 1, 7, 3, 4, 5, 4, 1, 1,
3, 0, 8, 8, 8, 4, 2, 3, 1, 0, 1, 0, 4, 3, 6, 3, 3, 3, 0, 0, 0, 0,
4, 8, 6, 5, 2, 2, 0, 6, 8, 3, 1, 9, 2, 0, 1, 0, 6, 7, 7, 0, 1, 8,
2, 6, 6, 0, 3, 7, 1, 6, 7, 2, 5, 8, 0, 6, 5, 9, 8, 7, 9, 4, 3, 0,
1, 5, 3, 7, 6, 5, 1, 2, 5, 8, 5, 6, 4, 2, 3, 6, 6, 1, 0, 9, 9, 5,
2, 2, 6, 9, 5, 8, 1, 0, 1, 6, 3, 5, 9, 0, 7, 2, 1, 7, 2, 0, 1, 2,
7, 5, 8, 0, 5, 1, 7, 7, 4, 8, 9, 5, 2, 3, 2, 2, 5, 3, 5, 4, 7, 7,
5, 5, 7, 7, 0, 8, 8, 0, 7, 1, 4, 6, 4, 3, 9, 5, 4, 2, 3, 7, 9, 6,
0, 7, 6, 3, 9, 6, 7, 0, 3, 3, 1, 3, 8, 4, 0, 2, 0, 1, 4, 7, 0, 3,
8, 4, 5, 4, 7, 6, 9, 1, 0, 9, 7, 4, 6, 6, 4, 7, 5, 4, 0, 5, 3, 6,
1, 4, 6, 7, 8, 9, 1, 8, 0, 2, 1, 3, 1, 2, 0, 1, 3, 8, 8, 9, 0, 4,
1, 1, 3, 2, 0, 3, 9, 2, 5, 1, 2, 0, 9, 5, 1, 0, 9, 2, 8, 9, 1, 9,
9, 7, 0, 5, 3, 4, 8, 5, 0, 6, 5, 3, 0, 1, 6, 1, 9, 9, 2, 4, 2, 8,
0, 6, 9, 2, 9, 8, 7, 1, 7, 4, 9, 9, 4, 8, 9, 1, 2, 6, 4, 3, 7, 6,
5, 1, 7, 7, 4, 5, 1, 2, 7, 0, 7, 5, 1, 6, 2, 1, 4, 5, 5, 9, 4, 9,
0, 6, 1, 6, 0, 5, 6, 6, 8, 1, 2, 6, 0, 5, 7, 8, 3, 8, 2, 2, 7, 2,
1, 4, 2, 5, 3, 9, 0, 5, 6, 9, 2, 0, 6, 8, 0, 0, 8, 9, 5, 3, 2, 5,
2, 3, 7, 5, 3, 8, 3, 2, 6, 6, 8, 6, 8, 7, 7, 9, 8, 8, 2, 2, 8, 2,
5, 4, 1, 1, 3, 4, 3, 6, 4, 1, 3, 1, 5, 3, 7, 5, 1, 5, 8, 7, 2, 7,
6, 9, 2, 8, 8, 7, 7, 6, 8, 3])
sum(y_predict == y_test)/len(y_test)
0.9844444444444445
将得分函数封装在自己写的KNNClassfire中
# def score(self,X_test,y_test):
# """根据测试数据集来确定当前模型的准确率"""
# y_predict = self.predict(X_test)
# return sum(y_predict == y_test)/len(y_test)
knn_clf.score(X_test,y_test)
0.9844444444444445
六、超参数
超参数:运行机器学习算法前需要指定的参数。 与超参数相对应的就是模型参数。
模型参数是算法过程中可以自己更新的参数。knn算法没有模型参数,它的k是典型的超参数。还有学习率这个参数也是典型的超参数。
通常说的调参就是调整超参数。调整超参数的方法有领域知识、经验数值以及实验探索。
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn import datasets
from sklearn.neighbors import KNeighborsClassifier
digits = datasets.load_digits()
X = digits.data
y = digits.target
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=456)#这里需要定义随机种子,保证每次运行的结果是一样的
best_score =0.0 #最好的准确率
best_k = -1 #当前最好的k
#我们从[1,10]里面寻找最好的k
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= 2
best_score= 0.9911111111111112
往往很多时候,仅考虑距离是不足的,a距离b为1,c距离b为3,其实b的类别更接近a才对。所以,可以考虑距离的倒数为权重。距离越小,权重就越大。
sklearn的from sklearn.neighbors import KNeighborsClassifier
是可以传入考虑权重的,参数:weights
可以有两个选择:uinform
:不采用权重;distance
采用权重。
best_score =0.0 #最好的准确率
best_k = -1 #当前最好的k
best_method = ""
for method in ['uniform','distance']:
#我们从[1,10]里面寻找最好的k
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= 2
best_score= 0.9911111111111112
best_method= uniform
其实距离的计算除了 欧几里得距离之外还有其他的:
曼哈顿距离: ∑ i = 1 n ∣ X i ( a ) − X i ( b ) ∣ \sum_{i=1}^{n}\left|X_{i}^{(a)}-X_{i}^{(b)}\right| ∑i=1n∣∣∣Xi(a)−Xi(b)∣∣∣
明可夫斯基距离: ( ∑ i = 1 n ∣ X i ( a ) − X i ( b ) ∣ p ) 1 p \left(\sum_{i=1}^{n}\left|X_{i}^{(a)}-X_{i}^{(b)}\right|^{p}\right)^{\frac{1}{p}} (∑i=1n∣∣∣Xi(a)−Xi(b)∣∣∣p)p1
这样就又有了另外一个超参数p
best_score =0.0 #最好的准确率
best_k = -1 #当前最好的k
best_p = -1
#我们从[1,10]里面寻找最好的k
for k in range(1,11):
for p in range(1,5):
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_k=",best_k)
print("best_score=",best_score)
print("best_p=",best_p)
best_k= 5
best_score= 0.9888888888888889
best_p= 2
knn其实还有很多超参数,对于这些超参数,我们上面的搜索策略有个名字,叫网格搜索。
比如对于k,p这两个参数,就形成了一个k x p这么大的网格。
但是在具体的搜索过程中,会有一些麻烦,比如对于weights参数,当我们使用uniform的时候就与参数p无关,而使用distance的时候就需要调整参数p。
鉴于超参数之间这种相互依赖的关系,我们如何一次性的把这些超参数都列出来,只跑一次程序就能得到超参数的组合呢。
这时就可以调用sklearn为我们封装的网格搜索方法了。
7、网格搜索
sklearn提供了GridSearchCV,在使用它之前,我们需要定义我们要搜索的参数:
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)]
}
]
knn_clf = KNeighborsClassifier()
from sklearn.model_selection import GridSearchCV
grid_search = GridSearchCV(knn_clf,param_grid)
%%time
grid_search.fit(X_train,y_train) #进行网格搜索,会比较耗时
Wall time: 1min 31s
GridSearchCV(estimator=KNeighborsClassifier(),
param_grid=[{'n_neighbors': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
'weights': ['uniform']},
{'n_neighbors': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
'p': [1, 2, 3, 4, 5], 'weights': ['distance']}])
接着可以通过下面的代码打印出最佳的分类器信息:
grid_search.best_estimator_
KNeighborsClassifier(n_neighbors=6, p=3, weights='distance')
这和我们上面得到的结果是不一样的,这是因为在网络搜索中,用来评价分类准确的方式更加复杂(GridSearchCV:CV for Cross Validation,交叉验正)。
其实在KNN中还有更多的超参数,以距离为例,我们是以距离作为相似度度量的。还有下面的方法:
向量空间余弦相似度、调整余弦相似度、皮尔森相关系数、Jaccard相似系数
八、数据归一化
数据标准化(归一化)处理是数据挖掘的一项基础工作,不同评价指标往往具有不同的量纲和量纲单位,这样的情况会影响到数据分析的结果,为了消除指标之间的量纲影响,需要进行数据标准化处理,以解决数据指标之间的可比性。原始数据经过数据标准化处理后,各指标处于同一数量级,适合进行综合对比评价。
举个例子:肿瘤的大小 、 肿瘤发现的时间 这两个指标,肿瘤的大小一般是个位数,而发现的时间一般为三位数。两种参数若不进行归一化处理,则发现的天数所占比重大,对计算的结果影响大,最终导致计算的结果不准确。
常用的两种方法:
最值归一化:
x ∗ = x − m i n max − m i n \mathrm{x}^{*}=\frac{\mathrm{x}-\mathrm{min}}{\max -\mathrm{min}} x∗=max−minx−min
均值方差归一化:
x ∗ = x − μ σ \mathrm{x}^{*}=\frac{\mathrm{x}-\mu}{\sigma} x∗=σx−μ
import numpy as np
import matplotlib.pyplot as plt
X = np.random.randint(0,100,(50,2)) # 50x2矩阵
X = np.array(X,dtype=float)#将整型数组改成浮点型
# 先处理第0列
X[:,0] = (X[:,0] - np.min(X[:,0])) / (np.max(X[:,0]) - np.min(X[:,0]))
# 再处理第1列
X[:,1] = (X[:,1] - np.min(X[:,1])) / (np.max(X[:,1]) - np.min(X[:,1]))
X[:10,:]
array([[0.89795918, 0. ],
[0.70408163, 0.75 ],
[0.23469388, 0.51041667],
[0.70408163, 0.52083333],
[0. , 0.97916667],
[0.68367347, 0.83333333],
[0.45918367, 0.90625 ],
[0.21428571, 1. ],
[0.98979592, 0.625 ],
[0.05102041, 0.21875 ]])
plt.scatter(X[:,0],X[:,1])
plt.show()
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])
plt.scatter(X2[:,0],X2[:,1])
plt.show()
np.mean(X2[:,0])
-1.1990408665951691e-16
np.std(X2[:,0])
1.0
np.mean(X2[:,1])
1.1102230246251565e-16
np.std(X2[:,1])
1.0
对于归一化这个操作,sklearn中封装了Scaler这个类。在sklearn中,它会让Scaler这个类和我们的机器学习算法类的使用流程保持一致。
上图就是Scaler类的流程。其中的fit就是求出训练数据的均值和方差,然后将其保存。
当其他的样例进来时,Scaler可以很容易的对其进行转换(transform),得到相应的输出结果(归一化后的数据)。
整个流程就是将机器学习算法中的预测改成了转换.
import numpy as np
from sklearn import datasets
iris = datasets.load_iris()
X = iris.data
y = iris.target
X[:10,:]
array([[5.1, 3.5, 1.4, 0.2],
[4.9, 3. , 1.4, 0.2],
[4.7, 3.2, 1.3, 0.2],
[4.6, 3.1, 1.5, 0.2],
[5. , 3.6, 1.4, 0.2],
[5.4, 3.9, 1.7, 0.4],
[4.6, 3.4, 1.4, 0.3],
[5. , 3.4, 1.5, 0.2],
[4.4, 2.9, 1.4, 0.2],
[4.9, 3.1, 1.5, 0.1]])
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler # 引入sklearn的归一化类
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=456)#这里需要定义随机种子,保证每次运行的结果是一样的
standardScaler = StandardScaler()
standardScaler.fit(X_train)
StandardScaler()
standardScaler.mean_ # 均值
array([5.89553571, 3.09732143, 3.77857143, 1.20803571])
standardScaler.scale_ # 方差
array([0.85933824, 0.45600357, 1.83553362, 0.77932416])
X_train = standardScaler.transform(X_train) #保存归一化后的结果
X_test_standard = standardScaler.transform(X_test)
from sklearn.neighbors import KNeighborsClassifier
knn_clf = KNeighborsClassifier(n_neighbors=3)
knn_clf.fit(X_train,y_train)
knn_clf.score(X_test_standard,y_test)
1.0
自定义实现均值方差归一化
import numpy as np
class StandardScaler:
def __init__(self):
self.mean_ = None
self.scaler_ = None
def fit(self,X):
"""根据训练数据集X获得数据的均值和方差"""
self.mean_ = np.array([np.mean(X[:,i]) for i in range(X.shape[1])])
self.scale_ = np.array([np.std(X[:,i]) for i in range(X.shape[1])])
return self
def transform(self,X):
resultX = np.empty(shape=X.shape,dtype=float)
for col in range(X.shape[1]):
resultX[:,col] = (X[:,col] - self.mean_[col]) / self.scaler_[col]
return resultX
九、总结
K近邻算法可以解决多酚类问题,思想简单,效果也不错。代码实现+调整超参数+判断性能(使用训练集和测试集,打分)+数据归一化。
KNN的缺点:
- 效率低下,m个样本,n个特征,预测每个新的数据,需要O(mn)的时间复杂度。
- 对异常数据敏感。若某数据有误,则结果必然错误。
- 更大的缺点:维数灾难:随着维度的增加,看似相近的两个点之间的距离越来越大。
来源:机器学习入门