KNN+交叉验证
复习
- 机器学习概述
- 概念
- 样本
- 特征工程
- 概念
- 作用
- 特征抽取
- 特征值化
- one-hot
- jieba分词
- 特征的预处理
- 归一化
- 标准化
- 特征选择
- PCA
- 方差过滤
- 机器学习基础
- 数据集的获取
- sklearn
- 数据集的切分
- 训练集
- 测试集
- 数据类型
- 离散型
- 连续性
- 回归问题
- 回归模型
- 分类问题
- 分类模型
- 数据集的获取
KNN分类模型
分类:将一个未知归类的样本归属到某一个已知的类群中
预测:可以根据数据的规律计算出一个未知的数据
-
概念:
- K-近邻算法采用测量不同特征值之间的距离方法进行分类(k-Nearest Neighbor,KNN)
k值的作用及取值
k值:模型的超参数
模型的超参数:如果模型类中的相关参数的不同,会导致分类或者回归效果的不同
- K值较小,则模型复杂度较高,容易发生过拟合,学习的估计误差会增大,预测结果对近邻的实例点非常敏感。
- K值较大可以减少学习的估计误差,但是学习的近似误差会增大,与输入实例较远的训练实例也会对预测起作用,使预测发生错误,k值增大模型的复杂度会下降。
- 在应用中,k值一般取一个比较小的值,通常采用交叉验证法来来选取最优的K值。
适用场景
- 小数据场景,样本为几千,几万的
欧几里得距离(Euclidean Distance)
常见距离量度,衡量多维空间中各个点之间的绝对距离
#案例:电影分类
import pandas as pd
df=pd.read_excel('./datasets/my_films.xlsx') #读数据
df.head()
feature=df[['Action Lens','Love Lens']] #特征
target=df['target'] #标签
from sklearn.neighbors import KNeighborsClassifier
knn=KNeighborsClassifier(n_neighbors=3) #k=3
knn.fit(feature,target) #训练
#使用模型做分类
knn.predict([[60,55]])#测试电影类型 ['Action Lens','Love Lens'] 二维数组
#输出结果:
array(['Action'], dtype=object)
算法描述:
1)计算测试数据与各个训练数据之间的距离;
2)按照距离的递增关系进行排序;
3)选取距离最小的K个点;
4)确定前K个点所在类别的出现频率;
5)返回前K个点中出现频率最高的类别作为测试数据的预测分类。
在scikit-learn库中使用k-近邻算法
分类问题:from sklearn.neighbors import KNeighborsClassifier
鸢尾花分类的实现
from sklearn.neighbors import KNeighborsClassifier
import pandas as pd
import sklearn.datasets as datasets
from sklearn.model_selection import train_test_split
#1.捕获鸢尾花数据
iris = datasets.load_iris()
#2.提取样本数据
feature = iris.data
target = iris.target
feature.shape #(150,4)
target.shape #(150,)
#3.数据集进行拆分
x_train,x_test,y_train,y_test = train_test_split(feature,target,test_size=0.2,random_state=2020)'
x_train.shape #(120,4)
y_train.shape'#(120,)
#4.观察数据集:看是否需要进行特征工程的处理
x_train
#5.实例化模型对象
knn = KNeighborsClassifier(n_neighbors=5)
#6.使用训练集数据训练模型
#X:特征(特征数据的维度必须是二维(表格型数据)) #x_train.shape (120,4)
#y:标签
knn.fit(x_train,y_train) #训练集
#7.测试模型:使用测试数据
knn.score(x_test,y_test) #0.9 受k影响
#8.使用模型进行分类
print('真实的分类结果:',y_test)
print('模型的分类结果:',knn.predict(x_test))
#输出结果:
真实的分类结果: [2 0 1 1 1 2 2 1 0 0 2 2 0 2 2 0 1 1 2 0 0 2 1 0 2 1 1 1 0 0]
模型的分类结果: [2 0 1 1 1 1 2 1 0 0 2 1 0 2 2 0 1 1 2 0 0 2 2 0 2 1 1 1 0 0]
- 预测年收入是否大于50K美元
from sklearn.preprocessing import StandardScaler,MinMaxScaler
df = pd.read_csv('./datasets/adults.txt')
df.head()
#使用one-hot的形式
#1.提取样本数据
feature = df[['age','education_num','occupation','hours_per_week']]
target = df['salary']
#2.特征工程-特征值化
one_hot_feature = pd.concat((feature[['age','education_num','hours_per_week']],pd.get_dummies(feature['occupation'])),axis=1)
#特征的预处理
s = StandardScaler()
s_feature = s.fit_transform(one_hot_feature)
#3.切分数据集
x_train,x_test,y_train,y_test = train_test_split(s_feature,target,test_size=0.2,random_state=20)
#4.实例化模型对象
knn = KNeighborsClassifier(30)
knn.fit(x_train,y_train) #训练集
knn.score(x_test,y_test) #测试数据
#
#1.提取样本数据
feature = df[['age','education_num','occupation','hours_per_week']]
target = df['salary']
count = 1
dic = {}
for occ in feature['occupation'].unique().tolist():
dic[occ] = count
count += 1
feature['occupation'] = feature['occupation'].map(dic)
#数据集切分
x_train,x_test,y_train,y_test = train_test_split(feature,target,test_size=0.2,random_state=20)
knn = KNeighborsClassifier(n_neighbors=30) #实例化对象
knn.fit(x_train,y_train) #训练集
knn.score(x_test,y_test) #测试数据
#使用模型对未知数据分类
print('真实分类结果:',y_test[0:10])
print('模型分类结果:',knn.predict(x_test)[0:10])
- k-近邻算法之约会网站配对效果判定(datingTestSet.txt)
#读数据
df = pd.read_csv('./datasets/datingTestSet.txt',header=None,sep='\t')
df.head()
#样本数据提取
feature_col = [col for col in df.columns if col != 3]
feature = df[feature_col]
target = df[3]
#特征工程
mm = MinMaxScaler() #归一化
m_feature = mm.fit_transform(feature)
#数据集切分
x_train,x_test,y_train,y_test = train_test_split(m_feature,target,test_size=0.2,random_state=2020)
#实例化
knn = KNeighborsClassifier(n_neighbors=10)
#训练集
knn.fit(x_train,y_train)
#测试数据
knn.score(x_test,y_test)
-
学习曲线寻找最优的k值
穷举不同的k值
ks = [5,7,9,12,15,20,25,30,35,40,45,50,60,70,80,90,100]
for k in ks:
knn = KNeighborsClassifier(n_neighbors=k).fit(x_train,y_train)
score=knn.score(x_test,y_test)
scores.append(score)
#画图
import matplotlib.pyplot as plt
plt.plot(ks,scores)
plt.xlabel('k')
plt.ylabel('score')
#找到分值最大的元素下标
import numpy as np
arr_scores = np.array(scores)
np.argmax(arr_scores) #5
arr_scores
#输出结果:
array([0.77537233, 0.77951789, 0.78335636, 0.78995854, 0.79180101,
0.79425764, 0.79072624, 0.79149394, 0.79302933, 0.79210809,
0.79180101, 0.79149394, 0.79164747, 0.79287579, 0.79318287,
0.79272225, 0.79318287])
ks[5] #最高分值对应的k为20
#基于最优的k值建模
knn = KNeighborsClassifier(n_neighbors=20)
knn.fit(x_train,y_train)
knn.score(x_test,y_test) #0.7942576385690158
- 在knn中样本的标签数据是不需要参与运算 ,可以为非数值型数据
K折交叉验证
- 目的:
- 选出最为适合的模型超参数的取值,然后将超参数的值作用到模型的创建中。
- 思想:
- 将样本的训练数据交叉的拆分出不同的训练集和验证集,使用交叉拆分出不同的训练集和验证集测分别试模型的精准度,然就求出的精准度的均值就是此次交叉验证的结果。将交叉验证作用到不同的超参数中,选取出精准度最高的超参数作为模型创建的超参数即可
- 实现思路:
- 将数据集平均分割成K个等份
- 使用1份数据作为测试数据,其余作为训练数据
- 计算验证准确率
- 使用不同的测试集,重复2、3步骤
- 对准确率做平均,作为对未知数据预测准确率的估计
-
API
- from sklearn.model_selection import cross_val_score
- cross_val_score(estimator,X,y,cv):
- estimator:模型对象
- X,y:训练集数据
- cv:折数
交叉验证在KNN中的基本使用
from sklearn.model_selection import cross_val_score
knn=KNeighborsClassifier(n_neighbors=10)
cross_val_score(knn,x_train,y_train,cv=5).mean() #拆分成5等分 #0.798372178061742
- 使用交叉验证&学习曲线找寻最优的超参数
ks = [5,7,9,12,15,20,25,30,35,40,45,50,60,70,80,90,100]
scores = []
for k in ks:
knn=KNeighborsClassifier(n_neighbors=k)
score=cross_val_score(knn,x_train,y_train,cv=5).mean() #均值
scores.append(score)
plt.plot(ks,scores)
ks[np.argmax(np.array(scores))] #20
交叉验证也可以帮助我们进行模型选择
from sklearn.linear_model import LogisticRegression
knn = KNeighborsClassifier(n_neighbors=5)
print (cross_val_score(knn, x_train, y_train, cv=10).mean())
lr = LogisticRegression()
print(cross_val_score(lr,x_train,y_train,cv=10).mean())
(了解)K-Fold&cross_val_score
- Scikit中指供了K-Fold的API
- n-split就是折数
- shuffle指是否对数据洗牌
- random_state为随机种子,固定随机性
from numpy import array
from sklearn.model_selection import KFold
# data sample
data = array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6])
kfold = KFold(n_splits=3, shuffle = True, random_state= 1)
for train, test in kfold.split(data):
print('train: %s, test: %s' % (data[train], data[test]))
- Scikit中提取带K-Fold接口的交叉验证接口sklearn.model_selection.cross_validate,但是该接口没有数据shuffle功能,所以一般结合Kfold一起使用。如果Train数据在分组前已经经过了shuffle处理,比如使用train_test_split分组,那就可以直接使用cross_val_score接口
from sklearn.model_selection import cross_val_score
iris = datasets.load_iris()
X, y = iris.data, iris.target
knn = KNeighborsClassifier(n_neighbors=5)
n_folds = 5
kf = KFold(n_folds, shuffle=True, random_state=42).get_n_splits(X)
scores = cross_val_score(knn, X, y, cv = kf)
scores.mean() #0.9733333333333334
手写数字识别实现
from sklearn.model_selection import cross_val_score
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
#加载一张图片数据
img_arr = plt.imread('./digist/3/3_100.bmp')
img_arr.shape #(28, 28)
plt.imshow(img_arr)
- 对所有数据的读取和加载,将其封装成样本数据
feature_list = []
target_list = []
#1.加载数据,封装成样本数据
for i in range(10):
for j in range(1,501):
img_path = './digist/'+str(i)+'/'+str(i)+'_'+str(j)+'.bmp'
img_arr = plt.imread(img_path)
feature_list.append(img_arr)
target_list.append(i)
len(feature_list) #5000
len(target_list) #5000
#每一个列表元素为2维,则当前列表一定是一个三维数据结构
#训练模型需要的特征数据必须是二维
feature_list[0].shape #(28, 28)
#对feature_list进行扁平化处理:三维的列表变成二维(每一个列表元素由二维变成1维,则整体列表就变形成了2维)
feature = []
for img_arr in feature_list:
feature.append(img_arr.reshape((28*28,)))
feature = np.array(feature) #将列表转换成数组
target = np.array(target_list) #将列表转换成数组
#拆分数据集
x_train,x_test,y_train,y_test = train_test_split(feature,target,test_size=0.1,random_state=2020)
#找寻模型最优的超参数
scores = []
ks = []
for k in range(3,100):
knn = KNeighborsClassifier(n_neighbors=k)
score = cross_val_score(knn,x_train,y_train,cv=5).mean()
ks.append(k)
scores.append(score)
best_k = ks[np.argmax(np.array(scores))]
best_k
#3
#实例化
knn = KNeighborsClassifier(n_neighbors=best_k)
knn.fit(x_train,y_train) #训练集
knn.score(x_test,y_test) #0.938
#使用模型进行图像识别
print('真实的图像识别结果:',y_test[0:10])
print('模型分类的结果:',knn.predict(x_test)[0:10])
#输出结果:
真实的图像识别结果: [4 4 6 0 2 4 7 7 8 4]
模型分类的结果: [4 4 6 0 2 4 7 7 8 4]
- 让模型识别外部图片
img_arr = plt.imread('./123.jpg')
plt.imshow(img_arr)
#将5切出来
five_img_arr = img_arr[300:430,185:290]
plt.imshow(five_img_arr)
five_img_arr.shape #(130, 105)
- 训练好的模型识别的图片数据只能是对28*28像素图片进行扁平化处理后的数据
#将five_img_arr进行像素的等比例压缩(28*28),在对其进行扁平化处理即可
import scipy.ndimage as ndimage
five_img_arr_zoom = ndimage.zoom(five_img_arr,zoom=(28/130,28/105))
plt.imshow(five_img_arr_zoom)
#扁平化处理
# five_img_arr_zoom.reshape((28*28,)) #1维结构
#在进行predict的时候需要传入的X,必须是二维
knn.predict(five_img_arr_zoom.reshape((1,784))) #array([5])