k-近邻算法

from sklearn.datasets import make_blobs

from sklearn.neighbors import KNeighborsClassifier,RadiusNeighborsClassifier 

from sklearn.neighbors import KNeighborsRegressor

from sklearn.model_selection import train_test_split

from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score

from sklearn.model_selection import ShuffleSplit #train_test_split的升级版,重复了这个分割训练集和测试集好几次
from sklearn.model_selection import learning_curve

from sklearn.feature_selection import SelectKBest#来选择相关性最大的特征

1、算法原理

  • 核心思想:未标记样本的类别,由距离其最近的k个邻居投票来决定。
  • 已知条件:具有已标记数据集且知道数据集中每个样本所属类别。一个未标记数据样本。
  • 目的:预测未标记数据样本所属类别。
  • 优缺点:
    • 优点:准确性高,对异常值和噪声有较高容忍度。
    • 缺点:计算量较大,对内存需求大。(每次对未标记样本分类都需要重新计算距离)
  • 算法参数:算法参数为 k ,参数选择根据数据来确定,k 值越大,模型偏差越大,对噪声数据越不敏感(可能出现欠拟合)。k值越小,模型方差越大。(可能出现过拟合

kNN算法的变种:

带权的k-均值算法:增加邻居的权重。默认情况下,在计算距离时,都是使用相同权重。实际上,我们可以针对不同的邻居指定不同的距离权重,如距离越近权重越高。这个可以通过指定算法的 weights 参数来实现。

指定半径的k-均值算法:使用一定半径内的点取代距离最近的k个点。在 sc ikit-learn里,RadiusNeighborsC!assifier 类实现了这个算法的变种。当数据采样不均匀时,该算法变种可以取得更好的性能

2、kNN进行分类: 

from sklearn.datasets import make_blobs
#生成数据
centers=[[-2,2],[2,2],[0,4]]
X,y=make_blobs(n_samples=60,centers=centers,random_state=0,cluster_std=0.60)
# 60个训练样本,这60样本分布在以centers参数指定中心点周围。
#cluster std是标准差,用来指明生成的点分布的松散程度。
c=np.array(centers)
#训练KNN分类算法,k选择5
from sklearn.neighbors import KNeighborsClassifier
clf=KNeighborsClassifier(n_neighbors=5)
clf.fit(X,y)
#预测
X_sample=np.array([[0,2]])
y_sample=clf.predict(X_sample)
neighbors=clf.kneighbors(X_sample,return_distance=False)
#使用 kneighbor()方法,把这个样本周围距离最近的5个点取出来。返回的是索引,从0开始计算。
#标记待预测样本和最近的5个点
import numpy as np
import matplotlib.pyplot as plt
plt.figure(figsize=(16,10),dpi=144)
#画出样本
plt.scatter(X[:,0],X[:,1],c=y,s=100,cmap='cool')#用数字标签y区分颜色 c=color,s=size 点的大小,cmap颜色映射
#画出中心点
plt.scatter(c[:,0],c[:,1],s=200,marker='^',c='orange')#marker 点的形状  正三角
#待预测点
plt.scatter(X_sample[:,0],X_sample[:,1],marker='x',s=200,c=y_sample,cmap='cool')
#画出预测点与距离最近的5个样本的连线
for i in neighbors[0]:#array([16, 20, 48,  6, 23], dtype=int64)
    plt.plot([X[i][0],X_sample[0][0]],[X[i][1],X_sample[0][1]],'k--',linewidth=0.6)
    #'k--'为线型选项,绘制黑色虚线  linewidth:线的粗细
    #第一个参数[x1,x2,...],第二个参数[y1,y2,...] :表示从(x1,y1)到(x2,y2)连线
plt.show()

 3、kNN进行回归拟合:

在轴上的指定区间内生成足够多的点,针对这些足够密集的点,使用训练出来的模型进行预测,
得到预测值 y_pred ,然后在坐标轴上,把所有的预测点连接起这样就画出了拟合曲线。

#kNN回归拟合
#生成数据集,在余弦曲线的基础上加入噪声
import numpy as np
n_dots=40
X=5*np.random.rand(n_dots,1)#可以返回一个或一组服从“0~1”均匀分布的随机样本值。n_dots行,1列
y=np.cos(X).ravel()#多维数组转换为一维数组

#添加噪声
y+=0.2*np.random.rand(n_dots)-0.1

多维转一维函数:

#ravel():如果没有必要,不会产生源数据的副本 
#flatten():返回源数据的副本 
#squeeze():只能对维数为1的维度降维 

np.random.rand(d1,d2,d3,...)

#当函数括号内没有参数时,则返回一个浮点数;
#当函数括号内有一个参数时,则返回秩为1的n个元素的数组,不能表示向量和矩阵;
#当函数括号内有两个及以上参数时,则返回对应维度的数组,能表示向量或矩阵

#训练KNN拟合回归模型

from sklearn.neighbors import KNeighborsRegressor
k=5
knn=KNeighborsRegressor(k)
knn.fit(X,y)
#生成足够密集的点并进行预测
T=np.linspace(0,5,500)[:,np.newaxis] #np.newaxis 插入新维度  列表变矩阵(500,)==>(500, 1)
#np.linspace主要用来创建等差数列。start:返回样本数据开始点  stop:返回样本数据结束点  num:生成的样本数据量
y_pred=knn.predict(T)
knn.score(X,y)
#连接预测点,构成拟合曲线
plt.figure(figsize=(16,10),dpi=144)
plt.scatter(X,y,s=100,c='g',label='data')#画出训练样本散点图
plt.plot(T,y_pred,c='k',label='prediction')#画出预测点的拟合曲线
plt.axis('tight')#按照图形的内容自动收紧坐标轴,不留空白区域
plt.title("KNeighborsRegressor (k = %i)"% k,fontsize=30)
plt.show()

 4、实例-糖尿病的预测

import pandas as pd
#加载数据
data=pd.read_csv(r"D:\Desktop\data\59024 scikit-learn机器学习源码_20181031\code\datasets\pima-indians-diabetes\diabetes.csv")
#查看样本数和特征数
print('data.shape{}'.format(data.shape))#总共有768个样本,8个特征
#查看前5行数据
data.head()

8个特征分别如下:

  • Pregnancies:怀孕的次数。
  • Glucose:血浆葡萄糖浓度,采用2小时口服葡萄糖耐量试验测得。
  • BloodPressure:舒张压(毫米汞柱)
  • SkinThickness:肱三头肌皮肤褶皱厚度(毫米)
  • Insulin:两个小时血清胰岛素(μU/毫升)
  • BMI:身体质量指数,体重除以身高的平方。
  • Diabetes Pedigree Function:糖尿病血统指数。糖尿病和家庭遗传相关。
  • Age: 年龄
#进一步观察阳性样本和隐形样本的个数
data.groupby("Outcome").size()#中阴性样 500 例,阳性样 268 例

 

然后对数据进行简单处理,把8个特征值分离出来,作为训练数据集 ,Outcome 分离出来作为目标值。然后数据集分为训练数据集和测试数据集

X=data.iloc[:,0:8]
y=data.iloc[:,-1]#pandas对象加iloc,数组不用加
print("X.shape{};y.shape{}".format(X.shape,y.shape))
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=0)

比较模型取最优模型: 

#模型比较
#k-均值算法、带权的k-均值算法、指定半径的k-均值算法
#构造模型
from sklearn.neighbors import KNeighborsClassifier,RadiusNeighborsClassifier
models=[]
models.append(("kNN",KNeighborsClassifier(n_neighbors=2)))
models.append(("kNN with weights",KNeighborsClassifier(n_neighbors=2,weights="distance")))
models.append(("Radius Neighbors",RadiusNeighborsClassifier(n_neighbors=2,radius=500.0)))

#用训练集训练模型,用测试集打分,对比算法准确性
#多次随机分配训练数据集和交叉验证数据集,然后求模型准确性评分的平均值。
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score

results=[]
for name,model in models:
    kfold=KFold(n_splits=10)
    #model.fit(X_train,y_train)
    #results.append((name,model.score(X_test,y_test)))
    cv_result=cross_val_score(model,X,y,cv=kfold)
    results.append((name,cv_result))
for i in range(len(results)):
    print("name:{},cross_val_score:{}".format(results[i][0],results[i][1].mean()))
    

上述代码通过KFold把数据集分成10份,其中1份会作为交叉验证数据集来计算模型准确性,剩下9份作为训练数据集。cross_val_score()函数总共计算出10次不同训练集和交叉验证数据集组合得到的模型准确性评分。 

比较发现的k-均值算法性能更优,用kNN进行模型训练和分析

knn=KNeighborsClassifier(n_neighbors=2)
knn.fit(X_train,y_train)
train_score=knn.score(X_train,y_train)
test_score=knn.score(X_test,y_test)
print("train_score:{};test_score:{}".format(train_score,test_score))

根据该模型的得分得出结论:对训练集拟合不佳,对测试集预测不佳

需要进一步画学习曲线验证

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn.model_selection import ShuffleSplit #train_test_split的升级版,重复了这个分割训练集和测试集好几次
from sklearn.model_selection import learning_curve
from sklearn.neighbors import KNeighborsClassifier

#加载数据
data=pd.read_csv(r"D:\Desktop\data\59024 scikit-learn机器学习源码_20181031\code\datasets\pima-indians-diabetes\diabetes.csv")

#from common.utils import plot_learning_curve
def plot_learning_curve(estimator, title, X, y, ylim=None, cv=None,
                        n_jobs=1, train_sizes=np.linspace(.1, 1.0, 5)):
    plt.figure()
    plt.title(title)
    if ylim is not None:
        plt.ylim(*ylim)
    plt.xlabel("Training examples")
    plt.ylabel("Score")
    train_sizes, train_scores, test_scores = learning_curve(
        estimator, X, y, cv=cv, n_jobs=n_jobs, train_sizes=train_sizes)
    train_scores_mean = np.mean(train_scores, axis=1)
    train_scores_std = np.std(train_scores, axis=1)
    test_scores_mean = np.mean(test_scores, axis=1)
    test_scores_std = np.std(test_scores, axis=1)
    #在画训练集的曲线时:横轴为 train_sizes,纵轴为 train_scores_mean;
    #画测试集的曲线时:横轴为train_sizes,纵轴为test_scores_mean。
    plt.grid()# 生成网格
    plt.fill_between(train_sizes, train_scores_mean - train_scores_std,
                     train_scores_mean + train_scores_std, alpha=0.1,
                     color="r")
    plt.fill_between(train_sizes, test_scores_mean - test_scores_std,
                     test_scores_mean + test_scores_std, alpha=0.1, color="g")
    # fill_between: fill_between(x, y1, y2=0, where=None, interpolate=False, step=None, *, data=None, **kwargs)
    # 利用plt模块的fill_between方法可以绘制出填充区域, where参数接受一个bool对象, 表示在哪些地方填充(bool为True的地方),\
    # alpha是填充空间的透明度, x是水平轴上的点, y1是数据集竖直轴上的点, y2是要与y1在每一个水平轴点处计算差值然后填充这两部分的区域, y2的默认值是0,
    # interpolate只有在使用了where参数同时两条曲线交叉时才有效, 使用这个参数会把曲线交叉处也填充使得填充的更完整

    plt.plot(train_sizes, train_scores_mean, 'o-', color="r",label="Training score")
    plt.plot(train_sizes, test_scores_mean, 'o-', color="g",label="Cross-validation score") 
    plt.legend(loc="best")#添加图例
    return plt

X=data.iloc[:,0:8]
y=data.iloc[:,-1]#pandas对象加iloc,数组不用加
knn=KNeighborsClassifier(n_neighbors=2)
cv=ShuffleSplit(n_splits=10,test_size=0.2,random_state=0)
plt.figure(figsize=(16,10),dpi=200)
plot_learning_curve(knn,"Learn Curve for KNN Diabetes",X,y,ylim=(0.0,1.01),cv=cv)

特征选择和可视化

from sklearn.feature_selection import SelectKBest#来选择相关性最大的特征
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score

selector=SelectKBest(k=2)#选择两个特征
X_new=selector.fit_transform(X,y)

results=[]
for name,model in models:
    kfold=KFold(n_splits=10)
    cv_result=cross_val_score(model,X,y,cv=kfold)
    results.append((name,cv_result))
for i in range(len(results)):
    print("name:{};score:{}".format(results[i][0],results[i][1].mean()))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值