机器学习之KNN算法

KNN分类

复制代码
KNN算法介绍(K Nearest Neighbors), K近邻算法
    原理:
        基于 欧式距离(或者其它距离计算方式)计算 测试集 和 每个训练集之间的距离, 然后根据距离升序排列, 找到最近的K个样本.
        基于K个样本投票, 票数多的就作为最终预测结果 -> 分类问题.
        基于K个样本计算平均值, 作为最终预测结果 -> 回归问题.
    实现思路:
        1. 分类问题
            适用于: 有特征, 有标签, 且标签是不连续的(离散的)
        2. 回归问题.
            适用于: 有特征, 有标签, 且标签是连续的.
    KNN算法, 分类问题思路如下:
        1. 计算测试集和每个训练的样本之间的 距离.
        2. 基于距离进行升序排列.
        3. 找到最近的K个样本.
        4. K个样本进行投票.
        5. 票数多的结果, 作为最终的预测结果.
    代码实现思路:
        1. 导包.
        2. 准备数据集(测试集 和 训练集)
        3. 创建(KNN 分类模型)模型对象.
        4. 模型训练.
        5. 模型预测.
python 复制代码
"""
KNN算法介绍(K Nearest Neighbors), K近邻算法
    原理:
        基于 欧式距离(或者其它距离计算方式)计算 测试集 和 每个训练集之间的距离, 然后根据距离升序排列, 找到最近的K个样本.
        基于K个样本投票, 票数多的就作为最终预测结果 -> 分类问题.
        基于K个样本计算平均值, 作为最终预测结果 -> 回归问题.
    实现思路:
        1. 分类问题
            适用于: 有特征, 有标签, 且标签是不连续的(离散的)
        2. 回归问题.
            适用于: 有特征, 有标签, 且标签是连续的.
    KNN算法, 分类问题思路如下:
        1. 计算测试集和每个训练的样本之间的 距离.
        2. 基于距离进行升序排列.
        3. 找到最近的K个样本.
        4. K个样本进行投票.
        5. 票数多的结果, 作为最终的预测结果.
    代码实现思路:
        1. 导包.
        2. 准备数据集(测试集 和 训练集)
        3. 创建(KNN 分类模型)模型对象.
        4. 模型训练.
        5. 模型预测.
"""

# 1. 导包.
from sklearn.neighbors import KNeighborsClassifier  # 分类
# from sklearn.neighbors import KNeighborsRegressor   # 回归

# 2. 准备数据集(测试集 和 训练集)
# train: 训练集
# test: 测试集
# neighbors: 最近邻的邻居数
x_train = [[0], [1], [2], [3]]      # 训练集的特征数据, 因为特征可以有多个特征, 所以是一个二维数组
y_train = [0, 0, 1, 1]              # 训练集的标签数据, 因为标签是离散的, 所以是一个一维数组
x_test = [[5]]                      # 测试集的特征数据

# 3. 创建(KNN 分类模型)模型对象.
# estimator: 估计器, 模型对象, 也可以用变量名 model做接收.
estimator = KNeighborsClassifier(n_neighbors=3) # n_neighbors: 最近邻的邻居数

# 4. 模型训练
# 传入: 训练集的特征数据, 训练集的标签数据
estimator.fit(x_train, y_train)

# 5. 模型预测.
# 传入: 测试集的特征数据, 获取到: 预测结果(测试集的标签, y_test)
y_pre = estimator.predict(x_test)

# 6. 打印预测结果.
print(f'预测值为: {y_pre}')

KNN回归

复制代码
KNN算法介绍(K Nearest Neighbors), K近邻算法
    原理:
        基于 欧式距离(或者其它距离计算方式)计算 测试集 和 每个训练集之间的距离, 然后根据距离升序排列, 找到最近的K个样本.
        基于K个样本投票, 票数多的就作为最终预测结果 -> 分类问题.
        基于K个样本计算平均值, 作为最终预测结果 -> 回归问题.
    实现思路:
        1. 分类问题
            适用于: 有特征, 有标签, 且标签是不连续的(离散的)
        2. 回归问题.
            适用于: 有特征, 有标签, 且标签是连续的.
    KNN算法, 回归问题思路如下:
        1. 计算测试集和每个训练的样本之间的 距离.
        2. 基于距离进行升序排列.
        3. 找到最近的K个样本.
        4. 基于K个样本的标签值, 计算平均值.
        5. 将上述计算出来的平均值, 作为最终的预测结果.
    代码实现思路:
        1. 导包.
        2. 准备数据集(测试集 和 训练集)
        3. 创建(KNN 回归模型)模型对象.
        4. 模型训练.
        5. 模型预测.
    总结:
        K值过小, 容易受到异常值的影响, 且会导致模型学到大量的"脏的特征", 导致出现: 过拟合.
        K值过大, 模型会变得简单, 容易发生: 欠拟合.
python 复制代码
"""
KNN算法介绍(K Nearest Neighbors), K近邻算法
    原理:
        基于 欧式距离(或者其它距离计算方式)计算 测试集 和 每个训练集之间的距离, 然后根据距离升序排列, 找到最近的K个样本.
        基于K个样本投票, 票数多的就作为最终预测结果 -> 分类问题.
        基于K个样本计算平均值, 作为最终预测结果 -> 回归问题.
    实现思路:
        1. 分类问题
            适用于: 有特征, 有标签, 且标签是不连续的(离散的)
        2. 回归问题.
            适用于: 有特征, 有标签, 且标签是连续的.
    KNN算法, 回归问题思路如下:
        1. 计算测试集和每个训练的样本之间的 距离.
        2. 基于距离进行升序排列.
        3. 找到最近的K个样本.
        4. 基于K个样本的标签值, 计算平均值.
        5. 将上述计算出来的平均值, 作为最终的预测结果.
    代码实现思路:
        1. 导包.
        2. 准备数据集(测试集 和 训练集)
        3. 创建(KNN 回归模型)模型对象.
        4. 模型训练.
        5. 模型预测.
    总结:
        K值过小, 容易受到异常值的影响, 且会导致模型学到大量的"脏的特征", 导致出现: 过拟合.
        K值过大, 模型会变得简单, 容易发生: 欠拟合.
"""

# 1. 导包.
from sklearn.neighbors import KNeighborsRegressor       # KNN算法的 回归模型

# 2. 准备数据集(测试集 和 训练集)
# 开根号:     14.53      14.28       1           2.24
# 平方和:      211        204        1            5
# 差值:     (3,11,9)   (2,10,10)  (0,1,0)      (1,0,2)
x_train = [[0, 0, 1], [1, 1, 0], [3, 10, 10], [4, 11, 12]]      # 训练集的特征数据, 因为特征可以有多个特征, 所以是一个二维数组
y_train = [0.1, 0.2, 0.3, 0.4]                                  # 训练集的标签数据, 因为标签是连续的, 所以是一个一维数组
x_test = [[3, 11, 10]]                                          # 测试集的特征数据

# 3. 创建(KNN 回归模型)模型对象.
estimator = KNeighborsRegressor(n_neighbors=2)

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

# 5. 模型预测.
y_pre = estimator.predict(x_test)

# 6. 打印预测结果.
print(f'预测值为: {y_pre}')

特征预处理之归一化

复制代码
案例: 演示特征预处理之 归一化操作.

回顾: 特征工程的目的 和 步骤
    目的:
        利用专业的背景知识 和 技巧处理数据, 用于提升 模型的性能.
    步骤:
        1. 特征提取.
        2. 特征预处理(归一化, 标准化)
        3. 特征降维.
        4. 特征选择.
        5. 特征组合.


特征预处理之 归一化介绍:
    目的:
        防止因为量纲(单位)问题, 导致特征列的KNN距离值相差较大, 影响模型的最终结果.
        所以通过公式把 各列的值 映射到 [0, 1] 区间.
    公式:
        x'  = (当前值 - 该列最小值) / (该列最大值 - 该列最小值)
        x'' = x' * (mx - mi) + mi
    公式解释:
        x' ->  基于公式算出来的 结果
        x'' -> 最终的 结果.
        mx ->  区间的最大值
        mi ->  区间的最小值
    弊端:
        容易受到最大值 和 最小值的影响, 所以它一般用于处理 小数据集.
python 复制代码
"""
案例: 演示特征预处理之 归一化操作.

回顾: 特征工程的目的 和 步骤
    目的:
        利用专业的背景知识 和 技巧处理数据, 用于提升 模型的性能.
    步骤:
        1. 特征提取.
        2. 特征预处理(归一化, 标准化)
        3. 特征降维.
        4. 特征选择.
        5. 特征组合.


特征预处理之 归一化介绍:
    目的:
        防止因为量纲(单位)问题, 导致特征列的KNN距离值相差较大, 影响模型的最终结果.
        所以通过公式把 各列的值 映射到 [0, 1] 区间.
    公式:
        x'  = (当前值 - 该列最小值) / (该列最大值 - 该列最小值)
        x'' = x' * (mx - mi) + mi
    公式解释:
        x' ->  基于公式算出来的 结果
        x'' -> 最终的 结果.
        mx ->  区间的最大值
        mi ->  区间的最小值
    弊端:
        容易受到最大值 和 最小值的影响, 所以它一般用于处理 小数据集.
"""

# 导包
from sklearn.preprocessing import MinMaxScaler, StandardScaler  # 归一化对象


# 1. 准备数据集(归一化之前的原数据).
x_train = [[90, 2, 10, 40], [60, 4, 15, 45], [75, 3, 13, 46]]

# 2. 创建归一化对象.
# 参数feature_range 表示生成范围, 默认为: 0, 1  如果就是这个区间, 则参数可以省略不写.
transfer = MinMaxScaler()
# transfer = MinMaxScaler(feature_range=(3, 5))

# 3. 对原数据集进行归一化操作.
x_train_new = transfer.fit_transform(x_train)

# 4. 打印处理后的数据.
print('归一化后的数据集为: \n')
print(x_train_new)

特征预处理之标准化

复制代码
案例: 演示特征预处理之 标准化操作.

回顾: 特征工程的目的 和 步骤
    目的:
        利用专业的背景知识 和 技巧处理数据, 用于提升 模型的性能.
    步骤:
        1. 特征提取.
        2. 特征预处理(归一化, 标准化)
        3. 特征降维.
        4. 特征选择.
        5. 特征组合.


特征预处理之 标准化介绍:
    目的:
        防止因为量纲(单位)问题, 导致特征列的KNN距离值相差较大, 影响模型的最终结果.
        所以通过公式把 各列的值 映射到 均值为0, 标准差为1的 正态分布序列.
    公式:
        x'  = (当前值 - 该列平均值) / 该列的标准差

    应用场景:
        适用于 大数据集 的处理.

    结论:
        无论是归一化, 还是标准化, 目的都是为了解决因为量纲(单位)问题, 导致模型评估较低等问题.

回顾:
    方差计算公式: 该列每个值 和 该列均值的差 的平方和 的 平均值.
    标准差计算公式: 方差开平方根
python 复制代码
"""
案例: 演示特征预处理之 标准化操作.

回顾: 特征工程的目的 和 步骤
    目的:
        利用专业的背景知识 和 技巧处理数据, 用于提升 模型的性能.
    步骤:
        1. 特征提取.
        2. 特征预处理(归一化, 标准化)
        3. 特征降维.
        4. 特征选择.
        5. 特征组合.


特征预处理之 标准化介绍:
    目的:
        防止因为量纲(单位)问题, 导致特征列的KNN距离值相差较大, 影响模型的最终结果.
        所以通过公式把 各列的值 映射到 均值为0, 标准差为1的 正态分布序列.
    公式:
        x'  = (当前值 - 该列平均值) / 该列的标准差

    应用场景:
        适用于 大数据集 的处理.

    结论:
        无论是归一化, 还是标准化, 目的都是为了解决因为量纲(单位)问题, 导致模型评估较低等问题.

回顾:
    方差计算公式: 该列每个值 和 该列均值的差 的平方和 的 平均值.
    标准差计算公式: 方差开平方根
"""

# 导包
from sklearn.preprocessing import StandardScaler  # 标准化对象


# 1. 准备数据集(标准化之前的原数据).
x_train = [[90, 2, 10, 40], [60, 4, 15, 45], [75, 3, 13, 46]]

# 2. 创建标准化对象.
transfer = StandardScaler()

# 3. 对原数据集进行标准化操作.
x_train_new = transfer.fit_transform(x_train)

# 4. 打印处理后的数据.
print('标准化后的数据集为: \n')
print(x_train_new)

# 5. 打印数据集的均值和方差.
print(f'数据集的均值为: {transfer.mean_}')
print(f'数据集的方差为: {transfer.var_}')
print(f'数据集的标准差为: {transfer.scale_}')

KNN鸢尾花

复制代码
例: 通过KNN算法实现 鸢尾花的 分类操作.

回顾: 机器学习项目的研发流程
    1. 加载数据.
    2. 数据的预处理.
    3. 特征工程(提取, 预处理...)
    4. 模型训练.
    5. 模型评估.
    6. 模型预测.
python 复制代码
"""
案例: 通过KNN算法实现 鸢尾花的 分类操作.

回顾: 机器学习项目的研发流程
    1. 加载数据.
    2. 数据的预处理.
    3. 特征工程(提取, 预处理...)
    4. 模型训练.
    5. 模型评估.
    6. 模型预测.
"""

# 导入工具包
from sklearn.datasets import load_iris                  # 加载鸢尾花测试集的.
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split   # 分割训练集和测试集的
from sklearn.preprocessing import StandardScaler        # 数据标准化的
from sklearn.neighbors import KNeighborsClassifier      # KNN算法 分类对象
from sklearn.metrics import accuracy_score              # 模型评估的, 计算模型预测的准确率


# 1. 定义函数, 加载鸢尾花数据集, 并查看数据集.
def dm01_load_iris():
    # 1. 加载鸢尾花数据集.
    iris_data = load_iris()
    # 2. 查看数据集.
    # print(f'数据集: {iris_data}')              # 字典形态
    # print(f'数据集的类型: {type(iris_data)}')   # <class 'sklearn.utils._bunch.Bunch'>
    # 3. 查看数据集所有的键.
    print(f'数据集所有的键: {iris_data.keys()}')
    # 4. 查看数据集的键对应的值.
    # print(f'具体的数据: {iris_data.data[:5]}')            # 有150条数据, 每条数据有4个特征. 我们只看前5条
    # print(f'具体的标签: {iris_data.target[:5]}')          # 有150条数据, 每条数据有1个标签. 我们只看前5条

    print(f'具体的数据: {iris_data.data}')
    print(f'具体的标签: {iris_data.target}')
    print(f'标签对应的名称: {iris_data.target_names}')     # ['setosa' 'versicolor' 'virginica']
    print(f'特征对应的名称: {iris_data.feature_names}')    # ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
    # print(f'数据集的描述: {iris_data.DESCR}')
    # print(f'数据集的框架: {iris_data.frame}')               # None
    # print(f'数据集的文件名: {iris_data.filename}')             # iris.csv
    # print(f'数据集的模型(在哪个包下): {iris_data.data_module}') # sklearn.datasets.data

# 2. 定义函数, 绘制数据集的散点图.
def dm02_show_iris():
    # 1. 加载数据集.
    iris_data = load_iris()
    # 2. 把 鸢尾花数据集封装成 DataFrame对象.
    iris_df = pd.DataFrame(iris_data.data, columns=iris_data.feature_names)
    # 3. 给df对象新增1列 -> 标签列.
    iris_df['label'] = iris_data.target
    # print(iris_df)

    # 4. 通过 Seaborn绘制散点图.
    # 参1: 数据集. 参2: x轴. 参3: y轴. 参4: 分组字段. 参5: 是否显示拟合线.
    sns.lmplot(data=iris_df, x='sepal length (cm)', y='sepal width (cm)', hue='label', fit_reg=True)
    # 5. 设置标题, 显式.
    plt.title('iris data')
    plt.tight_layout()      # 自动调整子图参数, 以使整个图像的边界与子图匹配.
    plt.show()


# 3. 定义函数, 切分训练集和测试集.
def dm03_split_train_test():
    # 1. 加载数据集.
    iris_data = load_iris()

    # 2. 数据的预处理: 从150个特征和标签中, 按照 8:2的比例, 切分训练集和测试集.
    # 参1: 特征数据. 参2: 标签数据. 参3: 测试集的比例.  参4: 随机种子(种子一致, 每次生成的随机数据集都是固定的)
    # 返回值: 训练集的特征数据, 测试集的特征数据, 训练集的标签数据, 测试集的标签数据.
    x_train, x_test, y_train, y_test = train_test_split(iris_data.data, iris_data.target, test_size=0.2, random_state=23)

    # 3. 打印切割后的结果.
    print(f'训练集的特征: {x_train}, 个数: {len(x_train)}')     # 120条, 每条4列(特征)
    print(f'训练集的标签: {y_train}, 个数: {len(y_train)}')     # 120条, 每条1列(标签)
    print(f'测试集的特征: {x_test}, 个数: {len(x_test)}')       # 30条, 每条4列(特征)
    print(f'测试集的标签: {y_test}, 个数: {len(y_test)}')       # 30条, 每条1列(标签)


# 4. 定义函数, 实现鸢尾花完整案例 -> 加载数据, 数据预处理, 特征工程, 模型训练, 模型评估, 模型预测.
def dm04_iris_evaluate_test():
    # 1. 加载数据集.
    iris_data = load_iris()
    # 2. 数据的预处理, 这里是把150条数据, 按照 8:2的比例, 切分训练集和测试集.
    x_train, x_test, y_train, y_test = train_test_split(iris_data.data, iris_data.target, test_size=0.2, random_state=23)

    # 3. 特征工程(提取, 预处理...)
    # 思考1: 特征提取: 因为源数据只有4个特征列, 且都是我们用的, 所以这里无需做特征提取.
    # 思考2: 特征预处理: 因为源数据的4列特征差值不大, 所以我们无需做特征预处理, 但是, 加入特征预处理会让我们的代码更完善, 所以加入.
    # 3.1 创建标准化对象.
    transfer = StandardScaler()
    # 3.2 对特征列进行标准化, 即: x_train: 训练集的特征数据, x_test: 测试集的特征数据.
    # fit_transform: 兼具fit和transform的功能, 即: 训练, 转换. 该函数适用于: 第一次进行标准化的时候使用. 一般用于处理: 训练集.
    x_train = transfer.fit_transform(x_train)
    # transform: 只有转换. 该函数适用于: 重复进行标准化动作时使用, 一般用于对测试集进行标准化.
    x_test = transfer.transform(x_test)

    # 4. 模型训练.
    # 4.1 创建模型对象.
    estimator = KNeighborsClassifier(n_neighbors=3)
    # 4.2 具体的训练模型的动作.
    estimator.fit(x_train, y_train)     # 传入: 训练集的特征数据, 训练集的标签数据

    # 5. 模型预测.
    # 场景1: 对刚才切分的 测试集(30条) 进行测试.
    # 5.1 直接预测即可, 获取到: 预测结果
    y_pre = estimator.predict(x_test)       # x_test: 测试集的特征数据
    # 5.2 打印预测结果.
    print(f'预测值为: {y_pre}')

    # 场景2: 对新的数据集(源数据150条 之外的数据) 进行测试.
    # 5.1 自定义测试数据集.
    my_data = [[7.8, 2.1, 3.9, 1.6]]
    # 5.2 对数据集进行标准化处理.
    my_data = transfer.transform(my_data)
    # 5.3 模型预测.
    y_pre_new = estimator.predict(my_data)
    print(f'预测值为: {y_pre_new}')

    # 5.4 查看上述数据集, 每种分类的预测概率.
    y_pre_proba = estimator.predict_proba(my_data)
    print(f'(各分类)预测概率为: {y_pre_proba}')         # [[0, 0.66666667, 0.33333333]] -> 0分类的概率, 1分类的概率, 2分类的概率.

    # 6. 模型评估.
    # 方式1: 直接评分, 基于: 测试集的特征 和 测试集集的标签.
    print(f'正确率(准确率): {estimator.score(x_test, y_test)}')     # 0.9666666666666667

    # 方式2: 基于 测试集的标签 和 预测结果 进行评分.
    print(f'正确率(准确率): {accuracy_score(y_test, y_pre)}')         # 0.9666666666666667

# 5. 测试.
if __name__ == '__main__':
    # dm01_load_iris()
    # dm02_show_iris()
    # dm03_split_train_test()
    dm04_iris_evaluate_test()

网格搜索和交叉验证

复制代码
案例: 演示网格搜索 和 交叉验证.

交叉验证解释:
    原理:
        把数据分成n份, 例如分成: 4份  -> 也叫: 4折交叉验证.
        第1次: 把第1份数据作为 验证集(测试集), 其它作为训练集, 训练模型, 模型预测, 获取: 准确率 -> 准确率1
        第2次: 把第2份数据作为 验证集(测试集), 其它作为训练集, 训练模型, 模型预测, 获取: 准确率 -> 准确率2
        第3次: 把第3份数据作为 验证集(测试集), 其它作为训练集, 训练模型, 模型预测, 获取: 准确率 -> 准确率3
        第4次: 把第4份数据作为 验证集(测试集), 其它作为训练集, 训练模型, 模型预测, 获取: 准确率 -> 准确率4
        然后计算上述的 4次准确率的 平均值, 作为: 模型最终的 准确率.

        假设第4次最好(准确率最高), 则: 用全部数据(训练集 + 测试集)训练模型, 再次用(第4次的)测试集对模型测试.
    目的:
        为了让模型的最终验真结果更准确.

网格搜索:
    目的/作用:
        寻找最优超参数.
    原理:
        接收超参可能出现的值, 然后针对于 超参的每个值进行 交叉验证, 获取到 最优超参组合.
    超参数:
        需要用户手动录入的数据, 不同的超参(组合), 可能会影响模型的最终评测结果.

大白话解释:
    网格搜索 + 交叉验证, 本质上指的是 GridSearchCV这个API, 它会帮我们寻找最优超参(供参考).
python 复制代码
"""
案例: 演示网格搜索 和 交叉验证.

交叉验证解释:
    原理:
        把数据分成n份, 例如分成: 4份  -> 也叫: 4折交叉验证.
        第1次: 把第1份数据作为 验证集(测试集), 其它作为训练集, 训练模型, 模型预测, 获取: 准确率 -> 准确率1
        第2次: 把第2份数据作为 验证集(测试集), 其它作为训练集, 训练模型, 模型预测, 获取: 准确率 -> 准确率2
        第3次: 把第3份数据作为 验证集(测试集), 其它作为训练集, 训练模型, 模型预测, 获取: 准确率 -> 准确率3
        第4次: 把第4份数据作为 验证集(测试集), 其它作为训练集, 训练模型, 模型预测, 获取: 准确率 -> 准确率4
        然后计算上述的 4次准确率的 平均值, 作为: 模型最终的 准确率.

        假设第4次最好(准确率最高), 则: 用全部数据(训练集 + 测试集)训练模型, 再次用(第4次的)测试集对模型测试.
    目的:
        为了让模型的最终验真结果更准确.

网格搜索:
    目的/作用:
        寻找最优超参数.
    原理:
        接收超参可能出现的值, 然后针对于 超参的每个值进行 交叉验证, 获取到 最优超参组合.
    超参数:
        需要用户手动录入的数据, 不同的超参(组合), 可能会影响模型的最终评测结果.

大白话解释:
    网格搜索 + 交叉验证, 本质上指的是 GridSearchCV这个API, 它会帮我们寻找最优超参(供参考).
"""
# 导入工具包
from sklearn.datasets import load_iris                               # 加载鸢尾花测试集的.
from sklearn.model_selection import train_test_split, GridSearchCV   # 分割训练集和测试集的, 寻找最优超参的(网格搜索 + 交叉验证).
from sklearn.preprocessing import StandardScaler                     # 数据标准化的
from sklearn.neighbors import KNeighborsClassifier                   # KNN算法 分类对象
from sklearn.metrics import accuracy_score                           # 模型评估的, 计算模型预测的准确率


# 1. 加载鸢尾花数据集.
iris_data = load_iris()

# 2. 数据预处理, 这里是: 切分训练集和测试集, 比例: 8:2
# 参1: 数据集的特征数据,   参数2: 数据集的标签数据, 参数3: 测试集的比例, 参数4: 随机种子.
x_train, x_test, y_train, y_test = train_test_split(iris_data.data, iris_data.target, test_size=0.2, random_state=22)

# 3. 特征工程 -> 特征预处理 -> 标准化.
# 3.1 创建标准化对象.
transfer = StandardScaler()
# 3.2 对训练集和测试集的特征数据进行标准化.
x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)

# 4. 模型训练.
# 4.1 创建 KNN分类对象.
estimator = KNeighborsClassifier()
# 4.2 定义字典, 记录 超参可能出现的情况(值).
param_dict = {'n_neighbors': [i for i in range(1, 11)]}     # i的值: 1 ~ 10
# 4.3 创建 GridSearchCV对象 -> 寻找最优超参, 使用网格搜索 + 交叉验证方式
# 参1: 要计算最优超参的模型对象
# 参2: 该模型超参可能出现的值
# 参3: 交叉验证的折数, 这里的4折表示: 每个超参组合, 都会进行4次交叉验证.  这里共计是 4 * 10 = 40次.
# 返回值 estimator -> 处理后的模型对象.
estimator = GridSearchCV(estimator, param_dict, cv=4)
# 4.4 具体的模型训练动作.
estimator.fit(x_train, y_train)
# 4.5 打印最优超参组合.
print(f'最优评分: {estimator.best_score_}')                 # 0.9666666666666668
print(f'最优超参组合: {estimator.best_params_}')             # {'n_neighbors': 3}
print(f'最优的估计器对象: {estimator.best_estimator_}')      # KNeighborsClassifier(n_neighbors=3)
print(f'具体的交叉验证结果: {estimator.cv_results_}')


# 5. 模型评估.
# 5.1 获取最优超参的 模型对象.
# estimator = estimator.best_estimator_                 # 获取最优的模型对象.
estimator = KNeighborsClassifier(n_neighbors=3)
# 5.2 模型训练.
estimator.fit(x_train, y_train)
# 5.3 模型预测.
y_pre = estimator.predict(x_test)
# 5.4 模型评估.
# 参1: 测试集.   参2: 预测集
print(f'准确率: {accuracy_score(y_test, y_pre)}')        # 0.9666666666666667

手写数字识别

复制代码
案例: 演示 KNN算法 识别图片, 即: 手写数字识别案例.

介绍:
    每张图片都是由 28 * 28 像素组成的, 即: 我们的csv文件中每一行都有 784个像素点, 表示图片(每个像素)的 颜色.
    最终构成图像.
python 复制代码
"""
案例: 演示 KNN算法 识别图片, 即: 手写数字识别案例.

介绍:
    每张图片都是由 28 * 28 像素组成的, 即: 我们的csv文件中每一行都有 784个像素点, 表示图片(每个像素)的 颜色.
    最终构成图像.
"""

import matplotlib.pyplot as plt
import pandas as pd
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
import joblib
from collections import Counter

# 扩展: 忽略警告.
import warnings
warnings.filterwarnings('ignore', module='sklearn') # 参1: 忽略警告, 参2: 忽略的模块.

# 1. 定义函数, 接收用户传入的索引, 展示 该索引对应的图片.
def show_digit(idx):
    # 1. 读取数据集, 获取到源数据.
    df = pd.read_csv('./data/手写数字识别.csv')
    # print(df)       # (42000行 * 785列)

    # 2. 判断传入的索引是否越界.
    if idx < 0 or idx > len(df) - 1:
        print('索引越界!')
        return

    # 3. 走这里, 说明没有越界, 就正常获取数据.
    x = df.iloc[:, 1:]
    y = df.iloc[:, 0]

    # 4. 查看用户传入的索引对应的图片 -> 是几?
    print(f'该图片对应的数字是: {y.iloc[idx]}')
    print(f'查看所有的标签的分布情况: {Counter(y)}')

    # 5. 查看下 用户传入的索引对应的图片 的形状
    print(x.iloc[idx].shape)       # (784,)  我们要想办法把 (784,) 转换成 (28, 28)
    # print(x.iloc[idx].values)      # 具体的784个像素点数据

    # 6. 把 (784,) 转换成 (28, 28)
    x = x.iloc[idx].values.reshape(28, 28)
    # print(x)   # 28 * 28像素点

    # 7. 具体的绘制灰度图的动作.
    plt.imshow(x, cmap='gray')      # 灰度图
    plt.axis('off')                 # 不显示坐标轴
    plt.show()


# 2. 定义函数, 训练模型, 并保存训练好的模型.
def train_model():
    # 1. 加载数据集.
    df = pd.read_csv('./data/手写数字识别.csv')
    # 2. 数据的预处理.
    # 2.1 拆分出特征列.
    x = df.iloc[:, 1:]      # 特征列.
    # 2.2 拆分出标签列.
    y = df.iloc[:, 0]       # 标签列.
    # 2.3 打印特征和标签的形状
    print(f'x的形状: {x.shape}')       # (42000, 784)
    print(f'y的形状: {y.shape}')       # (42000,)
    print(f'查看所有的标签的分布情况: {Counter(y)}')
    # 2.4 对特征列(拆分前)进行 归一化.
    x = x / 255
    # 2.5 拆分训练集和测试集.
    # 参1: 特征列.    参2: 标签列.      参3: 测试集的比例.    参4: 随机种子.      参5: 参考y值进行抽取, 保持标签的比例(数据均衡)
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=21, stratify=y)

    # 3. 模型训练.
    # 3.1 创建模型对象.
    estimator = KNeighborsClassifier(n_neighbors=3)
    # 3.2 模型训练.
    estimator.fit(x_train, y_train)

    # 4. 模型评估.
    print(f'准确率: {estimator.score(x_test, y_test)}')
    print(f'准确率: {accuracy_score(y_test, estimator.predict(x_test))}')

    # 5. 保存模型.
    # 参1: 模型对象.    参2: 模型保存的路径.
    joblib.dump(estimator, './model/手写数字识别.pkl')        # pickle文件: Python(Pandas)独有的文件类型.
    print('模型保存成功!')

# 3. 定义函数, 测试模型.
def use_model():
    # 1. 加载图片.
    x = plt.imread('./data/demo.png')       # 28 * 28像素
    # 2. 绘制图片.
    # plt.imshow(x, cmap='gray')              # 灰度图
    # plt.axis('off')                         # 不显示坐标轴
    # plt.show()

    # 3. 加载模型.
    estimator = joblib.load('./model/手写数字识别.pkl')

    # 4. 模型预测.
    # 4.1 查看 数据集转换.
    print(x.shape)                  # (28, 28)
    print(x.reshape(1, 784).shape)  # (1, 784)
    print(x.reshape(1, -1).shape)   # 效果等同于 (1, 784), 语法糖.

    # 4.2 具体的转换动作, 记得: 归一化(因为训练模型的时候 使用了 归一化动作)
    # x = x.reshape(1, -1) / 255      # 可能会预测失败, 因为读图的时候, 像素值可能不是特别的精准.
    x = x.reshape(1, -1)              # 用原始的读取到的像素值, 做预测.

    # 4.3 模型预测.
    y_pre = estimator.predict(x)

    # 5. 打印预测结果.
    print(f'预测值为: {y_pre}')


# 4. 测试.
if __name__ == '__main__':
    # 绘制数字
    # show_digit(9)
    # show_digit(20)
    # show_digit(23)

    # 训练模型, 并保存模型.
    # train_model()

    # 模型预测(使用模型)
    use_model()
相关推荐
Asher阿舍技术站2 小时前
【AI基础学习系列】八、机器学习常见名词汇总
人工智能·学习·机器学习·常见名词
Navigator_Z2 小时前
LeetCode //C - 962. Maximum Width Ramp
c语言·算法·leetcode
m0_672703312 小时前
上机练习第29天
算法
兩尛2 小时前
409. 最长回文串
c++·算法·leetcode
(❁´◡`❁)Jimmy(❁´◡`❁)2 小时前
【KMP】算法详解
算法
智者知已应修善业2 小时前
【pta反转加法构造回文数c语言1000位】2025-1-31
c语言·c++·经验分享·笔记·算法
List<String> error_P2 小时前
蓝桥杯基础知识点:模拟-数位操作类题目
python·算法·蓝桥杯
陈天伟教授3 小时前
人工智能应用- 材料微观:04.微观结构:金属疲劳
人工智能·神经网络·算法·机器学习·推荐算法
样例过了就是过了3 小时前
LeetCode热题100 螺旋矩阵
算法·leetcode·矩阵