- 思想极度简单
- 应用数学知识少(近乎为零)
- 效果好(缺点?)
- 可以解释机器学习算法使用过程中的很多细节问题
- 更完整的刻画机器学习应用的流程
文章目录
本质就是看新来的样本和最近的k个样本中,以他们自己的结果给这个新来的样本进行投票,得到新来的样本的概率,本质是认为两个样本足够的相似,大概认为该样本属于哪个类别.相似性,就是在特征空间的距离.
主要解决分类问题
数据准备
import numpy as np
import matplotlib.pyplot as plt
raw_data_X = [
[3.393533211,2.331273381],
[3.110073483,1.781539638],
[1.343808831,3.368360954],
[3.582294042,4.679179110],
[2.280362439,2.866990263],
[7.423436942,4.696522875],
[5.745051997,3.533989803],
[9.172168622,2.511101045],
[7.792783481,3.424088941],
[7.939820817,0.791637231]
]
raw_data_y = [0,0,0,0,0,1,1,1,1,1]
X_train = np.array(raw_data_X)
y_train = np.array(raw_data_y)
绘图看看数据
假设我们新来了一个样本,我们要推测出他是什么类型的数据
继续绘制一个图,让我们看看在数据空间中他所处的位置是什么
KNN的过程
根据KNN的思想,我们应该找出来他相邻的k个元素的距离
这时我们就需要用到数学中的一个公式 叫做欧拉距离
上面的公式,可以写成下面这种形式,是不是更加简洁一些呢!
首先我们应该求出所有样本距离他的距离
from math import sqrt
distances =[]
for x_train in X_train:
d= sqrt(np.sum((x_train - x)**2))
distances.append(d)
得到他的距离之后,我们进行排序 求前K个,这时我们假定他的k为6
这时我们就可以对他进行排序取前6个了
注意:这时我们使用的是argsort 因为使用这个方法会得到他排序前的索引值,这样可以和之前的y向量一一对应的索引,可以直接得到他的类别信息
nearest = np.argsort(distances)
k =6
topK_y = [y_train[i] for i in nearest[:k]]
topK_y
取出前6个之后,我们需要得到他出现次数最多的那个类别,这时我们需要用到python带有的Counter类来获取出现最多的那个类别.
from collections import Counter
Counter(topK_y)
votes = Counter(topK_y)
votes.most_common(1)[0][0]
predict_y = votes.most_common(1)[0][0]
predict_y
这时我们就得到了他的推测结果为1这个类别的数据!
我们了解了KNN算法的原理了,我们可以尝试简单封装一下这个KNN算法成为一个方法,这样为了后续的操作更加简单.
scikit-learn的机器学习算法封装
对KNN简单封装后:
import numpy as np
from math import sqrt
from collections import Counter
def KNN_classify(k,X_train,y_train,x):
assert k>=1 ,"k 必须大于1"
assert X_train.shape[0] == y_train.shape[0],"矩阵的行数和向量的列必须相等!"
assert X_train.shape[1] == x.shape[0] ,"矩阵的列必须和传进来的样本的列相等!"
distances = []
for x_tarin in X_train:
d= sqrt(np.sum((x_tarin-x)**2))
distances.append(d)
nearset = np.argsort(distances)
topK_y = [y_train[i] for i in nearset[:k]]
votes_res = Counter(topK_y).most_common(1)[0][0]
return votes_res
测试一下
%run mymodule/KNN.py
KNN_classify(6,X_train,y_train,x)
什么是机器学习呢?
可以说KNN是一个不需要训练过程的算法*
- K近邻算法是非常特殊的,可以被认为是没有模型的算法
- 为了和其他算法统一,可以认为训练数据集就是模型本身
如何使用scikt-learn中的KNN算法呢
from sklearn.neighbors import KNeighborsClassifier
kNN_classifier = KNeighborsClassifier(n_neighbors=6)
kNN_classifier.fit(X_train,y_train)
# 接收的数据是一个二维数组类型,需要进行重整,不然就会报错
kNN_classifier.predict(x.reshape(1,-1))
看到这里我们发现,比我们的多了一个fit的步骤,这时我们对我们写的方法进行进一步的封装,因为机器学习中,我们应当有一个训练的过程得到模型,但是我们这里是没有的,为了机器学习的通用性我们将进行fit操作
模仿scikt-learn进行封装我们自己的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_tarin,y_train):
"""根据训练数据集X_tarin 和y_tarin"""
assert X_tarin.shape[0] == y_train.shape[0], "矩阵的行数和向量的列必须相等!"
assert X_tarin.shape[0] >= self.k, "矩阵的行必须大于传进来的k!"
self._X_train = X_tarin
self._y_train = y_train
return self
def predict(self,X_predict):
"""给定待预测数据集X_predict,返回表示X_predict的结果向量"""
assert self._X_train is not None and self._y_train is not None,\
"""必须先进行fit操作"""
assert X_predict.shape[1] == self._X_train.shape[1],\
"""样本的数据集必须和训练数据集的维度一致"""
y_predict = [self._predict(x) for x in X_predict]
return np.array(y_predict)
def _predict(self,x):
assert self._X_train.shape[1] == x.shape[0], "矩阵的列必须和传进来的样本的列相等!"
distances = []
for x_tarin in self._X_train:
d = sqrt(np.sum((x_tarin - x) ** 2))
distances.append(d)
nearset = np.argsort(distances)
topK_y = [self._y_train[i] for i in nearset[:self.k]]
votes_res = Counter(topK_y).most_common(1)[0][0]
return votes_res
def __repr__(self):
return "KNN(k=%d)" %self.k
判断我们机器学习算法的性能
在我们进行判断模型是否准确的时候,我们遇到了困难,怎么看你的模型是否好用,结果正确的比例是多少,
这个时候我们需要进行数据的拆分,将数据分为训练数据以及测试数据来观察数据的准确性的百分比是多少
import numpy as np
def train_test_split(X,y,test_ratio=0.2,seed=None):
"""将数据 X 和 y 按照test_ratio分割成X_tarin,X_test,y_tarin,y_test"""
assert X.shape[0] == y.shape[0],\
"矩阵的行数必须和向量的列数相同"
assert 0.0<=test_ratio<=1.0,\
"输入的比例非法"
if seed:
np.random.seed(seed)
X = np.concatenate([X, y.reshape(-1, 1)], axis=1)
np.random.shuffle(X)
X,y = np.hsplit(X,[X.shape[1]-1])
y = y.reshape(1,-1)[0]
test_size = int(len(X) * test_ratio)
X_test = X[:test_size]
X_train= X[test_size:]
y_test = y[:test_size]
y_train = y[test_size:]
return X_train,X_test,y_train,y_test
数据准备好了,我们来进行测试一下
from playML.KNN import KNNClassifier
my_knn = KNNClassifier(k=3)
my_knn.fit(X_train,y_tarin)
y_predict = my_knn.predict(X_test)
y_predict
y_test
sum(y_predict == y_test)/len(y_test)
当然sklearn中也提供了相应的方法,供我们进行数据的拆分
当然 sklearn中也实现了数据准确率的计算,但是有时候我们不需要知道,模型的结果,只想知道这个模型的准确率怎么样
我们要进行进一步的封装.
def accuracy_score(y_true,y_predict):
"""计算y_true和y_predict之间的准确率"""
assert y_true.shape[0] ==y_predict.shape[0],\
"这俩的维度必须相等"
return sum(y_true==y_predict)/len(y_true)
import numpy as np
from .metrics import accuracy_score
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_tarin,y_train):
"""根据训练数据集X_tarin 和y_tarin"""
assert X_tarin.shape[0] == y_train.shape[0], "矩阵的行数和向量的列必须相等!"
assert X_tarin.shape[0] >= self.k, "矩阵的行必须大于传进来的k!"
self._X_train = X_tarin
self._y_train = y_train
return self
def predict(self,X_predict):
"""给定待预测数据集X_predict,返回表示X_predict的结果向量"""
assert self._X_train is not None and self._y_train is not None,\
"""必须先进行fit操作"""
assert X_predict.shape[1] == self._X_train.shape[1],\
"""样本的数据集必须和训练数据集的维度一致"""
y_predict = [self._predict(x) for x in X_predict]
return np.array(y_predict)
def _predict(self,x):
assert self._X_train.shape[1] == x.shape[0], "矩阵的列必须和传进来的样本的列相等!"
distances = []
for x_tarin in self._X_train:
d = sqrt(np.sum((x_tarin - x) ** 2))
distances.append(d)
nearset = np.argsort(distances)
topK_y = [self._y_train[i] for i in nearset[:self.k]]
votes_res = Counter(topK_y).most_common(1)[0][0]
return votes_res
def score(self,X_test,y_test):
y_predict = self.predict(X_test)
return accuracy_score(y_test,y_predict)
def __repr__(self):
return "KNN(k=%d)" %self.k
让我们来继续测试一下
来看看sklearn封装好的
超参数和模型参数
- 超参数:在算法运行前需要决定的参数
- 模型参数:算法过程中学习的参数
- KNN算法中没有模型参数
- KNN算法中的K是典型的超参数
在我们KNN算法中,我们之间使用的仅仅是K这个超参数,其实还有很多参数
例如method(有没有距离权重问题
),p(明可夫斯基距离
)…
接下来我们简单的搜索一下,复合我们算法比较好的超参数
import numpy as np
from sklearn import datasets
digits = datasets.load_digits()
X = digits.data
y = digits.target
from sklearn.model_selection import train_test_split
Xtrain,Xtest,ytrain,ytest = train_test_split(X,y,test_size=0.2,random_state = 666)
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(Xtrain,ytrain)
knn.score(Xtest,ytest)
bestscore = 0.0
besk_k = -1
for k in range(1,11):
knn = KNeighborsClassifier(k)
knn.fit(Xtrain,ytrain)
score = knn.score(Xtest,ytest)
if(score>bestscore):
besk_k=k
bestscore=score
print("best_k",besk_k)
print("best_score",bestscore)
这时我们找到了最好的k是4
再继续寻找method
再继续找明可夫斯基距离的p这个最好的超参数
网格搜索
可以搜索你想要的超参数中在该数据集中比较好的参数
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 = KNeighborsClassifier()
from sklearn.model_selection import GridSearchCV
grid_search = GridSearchCV(knn,param_grid)
%%time
grid_search.fit(Xtrain,ytrain)
得到knn对象
grid_search.best_estimator_
得到成绩
grid_search.best_score_
得到参数
grid_search.best_params_
在使用的时候可以加入两个参数,一个是当前使用的核心数,第二个就是打印执行进度
可以明显看到 数据快了很多
数据归一化
将所有的数据映射到同一尺度
适用于分布有明显边界的情况;受outlier影响较大
最值归一化
X = np.random.randint(0,100,(50,2))
X[:10,:]
X =np.array(X,dtype=float)
X[:10,:]
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]))
均值方差归一化
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])
看看sklearn自带的数据归一化我们怎么使用
#数据准备
iris = datasets.load_iris()
X=iris.data
y = iris.target
from sklearn.model_selection import train_test_split
Xtrain,Xtest,ytrain,ytest = train_test_split(iris.data,iris.target,random_state=666,test_size=0.2)
from sklearn.preprocessing import StandardScaler
standardScaler = StandardScaler()
standardScaler.fit(Xtrain)
Xtrain_standard = standardScaler.transform(Xtrain)
Xtest_standard = standardScaler.transform(Xtest)
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(3)
knn.fit(Xtrain_standard,ytrain)
knn.score(Xtest_standard,ytest)