24.10.1 机器学习入门DAY9

154 阅读8分钟

image.png

KNN算法: 一般采用“欧氏距离”

image.png

from sklearn.neighbors import KNeighborsClassifier
x = [[0], [1], [2], [3]]
y = [0,    0, 1, 1]

![image.png](https://p9-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/190cc6ff90a14dbe9781f95dbf82ab49~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgeW155a2m5Lmg:q75.awebp?rk3s=f64ab15b&x-expires=1772553189&x-signature=D1t%2BCmAtZCPR2R7XlvAZl5Lei1c%3D)
# 1.实例化API

estimator = KNeighborsClassifier(n_neighbors=1)

# 2.使⽤fit⽅法进⾏训练
estimator.fit(x, y)

# 3.数据预测
ret = estimator.predict([[0]])
print(ret) #[0]

ret = estimator.predict([[100]])
print(ret) #[1]

“连续属性”和“离散属性”的距离计算

若属性值之间存在序关系,则可以将其转化为连续值,例如:身⾼属性“⾼”“中等”“矮”,可转化为{1, 0.5, 0}。闵可夫斯基距离可以⽤于有序属性。若属性值之间不存在序关系,则通常将其转化为向量的形式,例如:性别属性“男”“⼥”,可转化为{(1,0),(0,1)}。

K 值选择==距离,也就是参考点的个数

K值过⼩:容易受到异常点的影响 k值过⼤:受到样本均衡的问题 选择较⼩的K值,就相当于⽤较⼩的领域中的训练实例进⾏预测,“学习”近似误差会减⼩,只有与输⼊实例较近或相似的训练实例才会对预测结果起作⽤,与此同时带来的问题是“学习”的估计误差会增⼤,换句话说,K值的减⼩就意味着整体模型变得复杂,容易发⽣过拟合; 选择较⼤的K值,就相当于⽤较⼤领域中的训练实例进⾏预测,其优点是可以减少学习的估计误差,但缺点是学习的近似误差会增⼤。这时候,与输⼊实例较远(不相似的)训练实例也会对预测器作⽤,使预测发⽣错误。且K值的增⼤就意味着整体的模型变得简单。 K=N(N为训练样本个数),则完全不⾜取,因为此时⽆论输⼊实例是什么,都只是简单的预测它属于在训练实例中最多的类,模型过于简单,忽略了训练实例中⼤量有⽤信息。 在实际应⽤中,K值⼀般取⼀个⽐较⼩的数值,例如采⽤交叉验证法(简单来说,就是把训练数据在分成两组:训练集和验证集)来选择最优的K值。

近似误差:

对现有训练集的训练误差,关注训练集,如果近似误差过⼩可能会出现过拟合的现象,对现有的训练集能有很好的预测,但是对未知的测试样本将会出现较⼤偏差的预测。模型本身不是最接近最佳模型。 估计误差: 可以理解为对测试集的测试误差,关注测试集,估计误差⼩说明对未知数据的预测能⼒好,模型本身最接近最佳模型。

Kd树

问题导向:(重点,学习后可自主回答)

实现k近邻算法时,主要考虑的问题是如何对训练数据进⾏快速k近邻搜索。

这在特征空间的维数⼤及训练数据容量⼤时尤其必要。k近邻法最简单的实现是线性扫描(穷举搜索),即要计算输⼊实例与每⼀个训练实例的距离。计算并存储好以后,再查找K近邻。当训练集很⼤时,计算⾮常耗时。为了提⾼kNN搜索的效率,可以考虑使⽤特殊的结构存储训练数据,以减⼩计算距离的次数。

Kd树:根据KNN每次需要预测⼀个点时,我们都需要计算训练数据集⾥每个点到这个点的距离,然后选出距离最近的k个点进 ⾏投票。当数据集很⼤时,这个计算成本⾮常⾼,针对N个样本,D个特征的数据集,其算法复杂度为O(DN )。 kd树:为了避免每次都重新计算⼀遍距离,算法会把距离信息保存在⼀棵树⾥,这样在计算之前从树⾥查询距离信息,尽量避免重新计算。其基本原理是,如果A和B距离很远,B和C距离很近,那么A和C的距离也很远。有了这个信息,就可以在合适的时候跳过距离远的点。这样优化后的算法复杂度可降低到O(DNlog(N))。感兴趣的读者可

参阅论⽂:Bentley,J.L.,Communications of the ACM(1975)。

1989年,另外⼀种称为Ball Tree的算法,在kd Tree的基础上对性能进⼀步进⾏了优化。

感兴趣的读者可以搜索Five balltree construction algorithms来了解详细的算法信息。

原理(类比二分查找)

image.png

构造方法

(1)构造根结点,使根结点对应于K维空间中包含所有实例点的超矩形区域;

(2)通过递归的⽅法,不断地对k维空间进⾏切分,⽣成⼦结点。在超矩形区域上选择⼀个坐标轴和在此坐标轴上的⼀ 个切分点,确定⼀个超平⾯,这个超平⾯通过选定的切分点并垂直于选定的坐标轴,将当前超矩形区域切分为左右两个 ⼦区域(⼦结点);这时,实例被分到两个⼦区域。

(3)上述过程直到⼦区域内没有实例时终⽌(终⽌时的结点为叶结点)。在此过程中,将实例保存在相应的结点上。

(4)通常,循环的选择坐标轴对空间切分,选择训练实例点在坐标轴上的中位数为切分点,这样得到的kd树是平衡的(平衡⼆叉树:它是⼀棵空树,或其左⼦树和右⼦树的深度之差的绝对值不超过1,且它的左⼦树和右⼦树都是平衡⼆ 叉树)。

KD树中每个节点是⼀个向量,和⼆叉树按照数的⼤⼩划分不同的是,KD树每层需要选定向量中的某⼀维,然后根据这 ⼀维按左⼩右⼤的⽅式划分数据。在构建KD树时,关键需要解决2个问题:

(1)选择向量的哪⼀维进⾏划分; (2)如何划分数据;

第⼀个问题简单的解决⽅法可以是随机选择某⼀维或按顺序选择,但是更好的⽅法应该是在数据⽐较分散的那⼀维进⾏ 划分(分散的程度可以根据⽅差来衡量)。第⼆个问题中,好的划分⽅法可以使构建的树⽐较平衡,可以每次选择中位数来进⾏划分。

最近领域的搜索

手绘

代码实现

import seaborn  as sns
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.preprocessing import MinMaxScaler

# 获取小数据集
iris = load_iris()
print((iris)) # 图1 数据集介绍

# 获取大数据集
news = fetch_20newsgroups()
print(news) # 图2 数据多一些,速度慢一些

# 数据集属性描述
print("数据集特征值是:\n", iris.data)
print("数据集目标值是:\n", iris["target"])
print("数据集的特征值名字是:\n", iris.feature_names)
print("数据集的目标值名字是:\n", iris.target_names)
print("数据集的描述是:\n", iris.DESCR)

# 数据可视化
# 把数据转换成dataframe的格式
iris_d = pd.DataFrame(iris['data'], columns = ['Sepal_Length', 'Sepal_Width', 'Petal_Length',    'Petal_Width'])
iris_d['Species'] = iris.target

def plot_iris(iris,    col1,  col2):
    sns.lmplot(x = col1,    y  =  col2,  data   =  iris,  hue    =  "Species", fit_reg    =  False)
    plt.xlabel(col1)
    plt.ylabel(col2)
    plt.title('鸢尾花种类分布图')
    plt.show() # 图3
plot_iris(iris_d,'Petal_Width','Sepal_Length')

# 数据集的划分
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=22)
print("训练集的特征值是:\n", x_train)
print("训练集的目标值是:\n", y_train)
print("测试集的特征值是:\n", x_test)
print("测试集的目标值是:\n", y_test)

# 归一化
def minmax_demo():
    """
    归一化演示
    :return:None
    """
    data = pd.read_csv("./data/dating.txt")
    print(data)

    # 1.实例化
    transfer = MinMaxScaler(feature_range=(3,5))

    # 2.进行转换,调用fit_transform
    ret_data = transfer.fit_transform(data[["milage", "Liters", "Consumtime"]])
    print("归一化后的数据:\n", ret_data)

minmax_demo()

# 标准化
def stand_demo():
    """
    标准化演示
    :return:None
    """
    data = pd.read_csv("./data/dating.txt")
    print(data)

    # 1.实例化
    transfer = StandardScaler()

    # 2.进行转换,调用fit_transform
    ret_data = transfer.fit_transform(data[["milage", "Liters", "Consumtime"]])
    print("标准化后的数据:\n", ret_data)
    print("每一列的方差为:\n", transfer.var_)
    print("标准化后的数据:\n", transfer.mean_)

stand_demo()

图1

image.png

图2

image.png

图3

image.png

特征预处理

provides several common utility functions and transformer classes to change raw feature vectors into a representation that is more suitable for the downstream estimators. 通过⼀些转换函数将特征数据转换成更加适合算法模型的特征数据过程

image.png

原因

特征的单位或者⼤⼩相差较⼤,或者某特征的⽅差相⽐其他的特征要⼤出⼏个数量级,容易影响(⽀配)⽬标结果,使得⼀些算法⽆法学习到其它的特征。

归⼀化:通过对原始数据进⾏变换把数据映射到(默认为[0,1])之间 1727791522515.jpg 作⽤于每⼀列,max为⼀列的最⼤值,min为⼀列的最⼩值,那么X’’为最终结果,mx,mi分别为指定区间值默认 mx为1,mi为0 1727791544945.jpg

注意最⼤值最⼩值是变化的,另外,最⼤值与最⼩值⾮常容易受异常点影响,所以这种⽅法鲁棒性较差,只适合传统精确⼩数据场景。

标准化

标准化:通过对原始数据进⾏变换把数据变换到均值为0,标准差为1范围内。

1727791661980.jpg

作⽤于每⼀列,mean为平均值,σ为标准差。 对于归⼀化来说:如果出现异常点,影响了最⼤值和最⼩值,那么结果显然会发⽣改变对于标准化来说:如果出现异常点,由于具有⼀定数据量,少量的异常点对于平均值的影响并不⼤,从⽽⽅差改变较⼩。在已有样本⾜够多的情况下⽐较稳定,适合现代嘈杂⼤数据场景。

from IPython.core.completerlib import import_re
from  sklearn.datasets import load_iris
from sklearn.model_selection import  train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier


# 1.获取数据
iris = load_iris()

# 2.数据基本处理
x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.2, random_state=22) # random_state 的值会影响准确率

# 3.特征工程 - 特征预处理
transfer = StandardScaler()
x_train = transfer.fit_transform(x_train) # 计算均值和标准差
x_test = transfer.transform(x_test)

# 4.机器学习-KNN
# 实例化一个估计器
estimator = KNeighborsClassifier(n_neighbors=5)

# 模型训练
estimator.fit(x_train, y_train) # 进行模型训练

# 5.模型评估
# 预测值结果输出
y_pre = estimator.predict(x_test)
print("预测值是:\n", y_pre)
print("预测值和真实值的对比是:\n", y_pre==y_test)

# 准确率计算
score = estimator.score(x_test, y_test)
print("准确率为:\n", score)

image.png