机器学习技术
人工智能三大概念
人工智能三大核心概念 人工智能(AI)领域包含以下三大基础性概念:
- 机器学习(Machine Learning) 作为AI的核心技术,机器学习使计算机系统能够通过数据自动学习并改进,无需依赖显式编程。主要分为:
- 监督学习(如分类、回归)
- 无监督学习(如聚类)
- 强化学习(如通过奖励机制优化智能体行为) 典型应用包括图像识别和自然语言处理等。
- 深度学习(Deep Learning) 这是机器学习的重要分支,采用深层神经网络模拟人脑的信息处理机制。其特点包括:
- 通过多层非线性变换提取高级特征
- 在计算机视觉(CNN)、语音识别(RNN)和生成模型(GAN)等领域表现优异
- 自然语言处理(NLP) 专注于人机语言交互,涵盖:
- 语法分析与语义理解
- 机器翻译(如Transformer)
- 对话系统(如ChatGPT) 关键技术包括词嵌入、注意力机制和预训练模型(BERT、GPT等)。
三者关系: 机器学习提供基础框架 → 深度学习推动复杂任务突破 → NLP实现智能交互。实际应用中往往协同工作,例如:
- 深度学习模型通过机器学习优化参数
- 最终服务于NLP任务
简明说明:
- 机器学习:数据驱动,自动归纳规律进行预测

- 深度学习:神经网络架构,模拟生物神经元网络

- 三者构成AI技术的递进式发展路径

- 数据、算法、算力三要素相互作用,是AI发展的基石

- CPU:负责调度任务、计算任务等;主要适合I\O密集型的任务
- GPU:更加适合矩阵运算;主要适合计算密集型任务
- TPU:Tensor,专门针对神经网络训练设计一款处理器
常见术语
样本,特征,标签/目标值
- 样本(sample) :一行数据就是一个样本;多个样本组成数据集;有时一条样本被叫成一条记录

- 特征(feature) :一列数据一个特征,有时也被称为属性

-
标签/目标(label/target) :模型要预测的那一列数据。本场景是就业薪资
-

-
就业薪资 与 培训学科、作业考试、学历、工作经验、工作地点 5个特征有关系
-
特征是从数据中抽取出来的,对结果预测有用的信息 eg:房价预测、车图片识别
数据集划分

- 数据集可划分两部分:训练集、测试集 比例:8 : 2,7 : 3
- 训练集(training set) :用来训练模型(model)的数据集
- 测试集(testing set):用来测试模型的数据集
算法分类
- 监督学习
- 无监督学习
- 半监督学习
- 强化学习
- 监督学习、无监督学习的数学表示
有监督学习
-
定义:输入数据是由输入特征值和目标值所组成,即输入的训练数据有标签的
-
数据集:需要人工标注数据
-

分类
-
目标值(标签值)是不连续的
-
分类种类:二分类、多分类任务、
-

回归
目标值(标签值)是连续的

无监督学习
-
定义:输入数据没有被标记,即样本数据类别未知,没有标签,根据样本间的相似性,对样本集聚类,以发现事物内部 结构及相互关系。
-
数据集:不需要标注数据
-

无监督学习特点:
- 训练数据无标签
- 根据样本间的相似性对样本集进行聚类,发现事物内部结构及相互关系

半监督学习【了解】
工作原理:
- 让专家标注少量数据,利用已经标记的数据(也就是带有类标签)训练出一个模型
- 再利用该模型去套用未标记的数据
- 通过询问领域专家分类结果与模型分类结果做对比,
- 从而对模型做进一步改善和提高
-
半监督学习方式可大幅降低标记成本
强化学习【了解】
- 强化学习(Reinforcement Learning):机器学习的一个重要分支
- 应用场景:里程碑AlphaGo围棋、各类游戏、对抗比赛、无人驾驶场景
- 基本原理:基本原理:通过构建四个要素:agent,环境状态,行动,奖励,
- agent根据环境状态进行行动获得最多的累计奖励。
小孩子学走路:
(1) 小孩就是 agent,他试图通过采取行(即行走)来操纵环境(地面),
(2) 并且从一个状态转变到另一个状态(即他走的每一步),
(3) 当他完成任务的子任务(即走了几步)时,孩子得到奖励(给巧克力吃),
(4) 并且当他不能走路时,就不会给巧克力。
总结
机器学习的建模流程


特征工程
- 特征工程是什么?
- 特征提取的作用
- 特征预处理的作用
- 了解特征降维、特征选择、特征组合

- 从数据集角度来看: 一列一列的数据为特征。
- 从模型训练角度来看: 对预测结果有用的属性为特征
- 特征工程是:利用专业背景知识和技巧处理数据,让机器学习算法效果最好。这个过程就是特征工程
- 释义:特征工程是困难、耗时、需要专业知识。应用机器学习基础就是特征工程
- 数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已。
- 从原始数据中提取与任务相关的特征,构成特征向量
特征降维【了解】
将原始数据的维度降低,叫做特征降维

-
会丢失部分信息。降维就需要保证数据的主要信息要保留下来
-
原始数据会发生变化,不需要了解数据本身是什么含义,它保留了最主要的信息
特征组合【了解】
把多个的特征合并成一个特征。

-
通过加法、乘法等方法将特征值合并
模型拟合问题
- 拟合是什么?
- 理解过拟合、欠拟合是什么?
- 知道过拟合、欠拟合出现的原因
- 理解泛化是什么
拟合:用来表示模型对样本点的拟合情况
欠拟合:模型在训练集上表现很差、在测试集表现也很差
原因:模型过于简单
过拟合:模型在训练集上表现很好、在测试集表现很差
原因:模型太过于复杂、数据不纯、训练数据太少
泛化:模型在新数据集(非训练数据)上的表现好坏的能力
奥卡姆剃刀原则:给定两个具有相同泛化误差的模型,较简单的模型比较复杂的模型更可取
机器学习开发环境
基于Python的 scikit-learn 库:
- 简单高效的数据挖掘和数据分析工具
- 可供大家使用,可在各种环境中重复使用
- 建立在NumPy,SciPy和matplotlib上
- 开源,可商业使用-获取BSD许可证
- pip install scikit-learn
总结
1.说明有监督学习和无监督学习的各自的特点及区别
-
监督学习
- 有标签
- 分类:标签值离散
- 回归:标签值连续
-
无监督学习
- 无标签,按照样本的相似性进行聚合
-
半监督学习
- 部分有标签,部分无标签
-
强化学习
2.说明下机器学习的建模流程
- 获取数据
- 数据基本处理
- 特征工程
- 模型训练
- 模型评估
3谈一下你对特征工程的理解
用专业背景知识和技巧处理数据,让机器学习算法效果最好
-
特征提取
-
特征预处理
-
特征降维
-
特征选择
-
特征组合
4.说下模型拟合问题及产生的原因
- 欠拟合:训练集上表现很差、在测试集表现也很差
- 原因:模型过于简单 k值大
- 过拟合:训练集上表现很好、在测试集表现很差
- 原因:模型太过于复杂 k值小
KNN算法思想
K-近邻算法(K Nearest Neighbor,简称KNN)。比如:根据你的“邻居”来推断出你的类别
KNN算法思想:如果一个样本在特征空间中的 k 个最相似的样本中的大多数属于某一个类别,则该样本也属于这个类别
如何确定样本的相似性?
样本相似性:样本都是属于一个任务数据集的。样本距离越近则越相似。
利用K近邻算法预测电影类型
K值的选择
KNN的应用方式
- 分类问题、回归问题
-
算法思想:若一个样本在特征空间中的 k 个最相似的样本大多数属于某一个类别,则该样本也属于这个类别
-
相似性:欧氏距离
分类问题的处理流程:
- 计算未知样本到每一个训练样本的距离
- 将训练样本根据距离大小升序排列
- 取出距离最近的k个训练样本
- 进行多表表决,统计k个样本哪个类别的个数最多
- 将未知的样本归属到出现次数最多的类别
回归问题的处理流程:
- 计算未知样本到每一个训练样本的距离
- 将训练样本根据距离大小升序排列
- 取出距离最近样本的k个训练样本
- 把这个k个样本的目标值计算器平均值
-
作为将未知的样本预测的值
Api的介绍:
- KNN算法分类API(KNeighborsClassifier)
- KNN算法回归API(KNeighborsRegressor)
KNN分类API:
sklearn.neighbors.KNeighborsClassifier(n_neighbors=5)
n_neighbors:int,可选(默认= 5),k_neighbors查询默认使用的邻居数
K近邻算法(K Nearest Neighbors,KNN)简介
算法原理:
- 计算测试样本与所有训练样本之间的距离(通常采用欧式距离或其他距离度量)
- 根据距离值进行升序排序,选取距离最近的K个样本
- 对于分类问题:通过K个最近邻样本的多数投票确定预测结果
- 对于回归问题:取K个最近邻样本的平均值作为预测结果
应用场景:
- 分类任务
- 适用条件:具有特征数据和离散标签(非连续值)
- 回归任务
- 适用条件:具有特征数据和连续标签
分类问题实现流程:
- 分类问题:适用于:有特征。有标签,且标签不连续的 离散的
- 回归问题:适用于:有特征,有标签,且标签连续的
KNN算法, 分类问题思路如下:
- 计算测试集和每个训练样本的之间的距离
- 基于距离进行升序排列
- 找到最近的k个样本
- k个样本进行投票
- 票数最多的结果,作为最终预测结果
分类\回归实现思路
- 导入包
- 准备数据集(测试集和训练集)
- 创建knn分类\回归模型和模型对象
- 模型训练
- 模型预测
knn分类模型代码
# 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) 一共四个特征不能超出特征数量
# 4. 模型训练
# 传入: 训练集的特征数据, 训练集的标签数据
estimator.fit(x_train, y_train)
# 5. 模型预测.
# 传入: 测试集的特征数据, 获取到: 预测结果(测试集的标签, y_test)
y_pre = estimator.predict(x_test)
# 6. 打印预测结果.
print(f'预测值为: {y_pre}')
knn回归模型代码
# 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]] # 测试集的特征数据
# x_test - x_train = [[0, 0, 1], [1, 1, 0], [3, 10, 10], [4, 11, 12]]
# 3. 创建(KNN 回归模型)模型对象.
# (n_neighbors=2 这个2 就是我们选择的k值)
estimator = KNeighborsRegressor(n_neighbors=2)
# 4. 模型训练.
estimator.fit(x_train, y_train)
# 5. 模型预测.
y_pre = estimator.predict(x_test)
# 6. 打印预测结果.
print(f'预测值为: {y_pre}')
总结:
- K值过小, 容易受到异常值的影响, 且会导致模型学到大量的"脏的特征", 导致出现: 过拟合.
- K值过大, 模型会变得简单, 容易发生: 欠拟合.
ps:(K 值的选择会影响结果——K 太小容易过拟合,K 太大可能欠拟合。通常通过交叉验证选最优 K)
距离度量方法
欧式距离

曼哈顿距离

切比雪夫距离(了解)
闵氏距离(了解)

特征预处理
归一化、标准化
特征的单位或者大小相差较大,或者某特征的方差相比其他的特征要大出几个数量级,容易影响(支配)目标结果,使得一些模型(算法)无法学习到其它的特征。
归一化MinMaxScaler
通过对原始数据进行变换把数据映射到【mi,mx】(默认为[0,1])之间

目的:
- 防止因为量纲(单位)问题, 导致特征列的方差值相差较大, 影响模型的最终结果.
- 所以通过公式把 各列的值 映射到 [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. 0. 0. 0. ]
[0. 1. 1. 0.83333333]
[0.5 0.5 0.6 1. ]]
标准化StandardScaler
通过对原始数据进行标准化,转换为均值为0标准差为1的标准正态分布的数据

- mean 为特征的平均值
- σ 为特征的标准差
- x' = (当前值 - 该列平均值) / 该列的标准差

- 数据标准化的API实现
-
sklearn.preprocessing. StandardScaler() - 防止因为量纲(单位)问题, 导致特征列的方差值相差较大, 影响模型的最终结果.
- 所以通过公式把 各列的值 映射到 均值为0, 标准差为1的 正态分布序列.
应用场景:
适用于 大数据集 的处理.
结论:
- 无论是归一化, 还是标准化, 目的都是为了解决因为量纲(单位)问题, 导致模型评估较低等问题.
- 方差计算公式: 该列每个值 和 该列均值的差 的平方和 的 平均值.
标准差计算公式: 方差开平方根
# 1.导入工具包
from sklearn.preprocessing import MinMaxScaler,StandardScaler
# 2.数据(只有特征)
x = [[90, 2, 10, 40], [60, 4, 15, 45], [75, 3, 13, 46]]
# 3.实例化(归一化,标准化)
# process =MinMaxScaler()
process =StandardScaler()
# 4.fit_transform 处理1
data =process.fit_transform(x)
# print(data)
print(process.mean_)
print(process.var_)
| 对比项 | 数据归一化(Min-Max Scaling) | 数据标准化(Standardization) |
|---|---|---|
| 定义 | 将数据缩放到固定范围(通常为 [0, 1] 或 [-1, 1]) | 将数据转换为均值为 0、标准差为 1 的分布 |
| 公式 | $ x' = \frac{x - \min(x)}{\max(x) - \min(x)} $ | $ x' = \frac{x - \mu}{\sigma} $ 其中: $ \mu $ 是均值, $ \sigma $ 是标准差 |
| 结果范围 | 通常是 [0, 1] 或 [-1, 1] |
理论上无界,但大部分落在 [-3, 3] 内 |
| 对异常值敏感性 | ⚠️ 非常敏感:最大值/最小值受异常点影响大 → 归一化结果会剧烈变化 | ✅ 不敏感:平均值和标准差受少量异常点影响较小 |
| 适用场景 | - 特征值有明确上下限 - 需要保持原始数据分布形状 - 如图像像素值(0~255) |
- 大数据集,存在异常值 - 模型假设数据服从正态分布(如 SVM、PCA) - 现代复杂模型(如神经网络) |
| Python 实现 | python<br>from sklearn.preprocessing import MinMaxScaler<br>scaler = MinMaxScaler(feature_range=(0, 1))<br>X_scaled = scaler.fit_transform(X)<br> |
python<br>from sklearn.preprocessing import StandardScaler<br>scaler = StandardScaler()<br>X_scaled = scaler.fit_transform(X)<br> |
| 优点 | - 输出在固定范围内,便于解释 - 适用于神经网络输入层 |
- 抗异常值能力强 - 适合统计建模和机器学习算法 |
| 缺点 | - 异常值会导致缩放失真 - 不适用于非线性变换 |
- 无法保证输出在特定区间内 - 可能产生负数 |
鲁棒性:指系统在异常或不确定条件下保持稳定运行的能力。
利用KNN算法进行鸢尾花分类
鸢尾花Iris Dataset数据集是机器学习领域经典数据集,鸢尾花数据集包含了150条鸢尾花信息,每50条取自三个鸢尾花中之一
"""
案例: 通过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范围内
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, 它会帮我们寻找最优超参
# 导入工具包
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算法实现手写数字识别
MNIST手写数字识别 是计算机视觉领域中 "hello world"级别的数据集
代码实习数字识别
数据文件 train.csv 和 test.csv 包含从 0 到 9 的手绘数字的灰度图像。
-
每个图像高 28 像素,宽28 像素,共784个像素。
-
每个像素取值范围[0,255],取值越大意味着该像素颜色越深
-
训练数据集(train.csv)共785列。第一列为 "标签",为该图片对应的手写数字。其余784列为该图像的像素值
-
训练集中的特征名称均有pixel前缀,后面的数字([0,783])代表了像素的序号
像素组成图像如下:
000 001 002 003 ... 026 027
028 029 030 031 ... 054 055
056 057 058 059 ... 082 083
| | | | ...... | |
728 729 730 731 ... 754 755
756 757 758 759 ... 782 783
数据集示例如下:

"""
案例: 演示 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()
代码大意:通过数据线训练一个名为手写数字识别.pkl的模型,我们应用开发者根据训别人练好的模型我们调用,如代码所示。我们往这个模型传入一个模糊图片。这个模型给我们返回结果
线性回归

线性回归介绍
线性回归(Linear regression)是利用 回归方程(函数) 对 一个或多个自变量(特征值)和因变量(目标值)之间 关系进行建模的一种分析方式。

- 为什么叫线性模型?因为求解的w,都是w的零次幂(常数项)所以叫成线性模型
- 在线性回归中,从数据中获取的规律其实就是学习权重系数w
- 某一个权重值w越大,说明这个权重的数据对房子价格影响越大
线性回归分类
- 一元线性回归
- y = kx +b(k: 数学中叫斜率, 在机器学习中Weight(权重), 简称: w)
- 多元线性回归

-
目标值只与多个因变量有关系
-

线性回归问题
线性回归API的应用
需求:靓仔身高是176,请预测体重?

演示线性回归API入门

from sklearn.linear_model import LinearRegression
# 案例: 演示线性回归API入门.
# 1. 准备数据.
x_train = [[160], [166], [172], [174], [180]] # 训练集的特征
y_train = [56.3, 60.6, 65.1, 68.5, 75] # 训练集的标签
x_test = [[176]] # 测试集的特征
# 2. 数据的预处理, 这里不需要.
# 3. 特征工程(特征提取, 特征预处理), 这里不需要.
# 4. 模型训练
# 4.1 创建模型对象.
estimator = LinearRegression()
# 4.2 具体的训练动作.
estimator.fit(x_train, y_train)
# 4.3 因为是线性回归模型, 我们可以查看下: 斜率(w, 权重), 截距(b, 偏置)
print(f'权重: {estimator.coef_}') # 0.92942177
print(f'偏置: {estimator.intercept_}') # -93.27346938775514
# 5. 模型预测.
y_pre = estimator.predict(x_test)
print(f'预测值为: {y_pre}') # 70.3047619
# 6. 模型评估.
损失函数
需要设置一个评判标准

- 误差概念:用预测值y – 真实值y就是误差
- 损失函数:衡量每个样本预测值与真实值效果的函数
- 红色直线能更好的拟合所有点”也就是误差最小,误差和最小



-
当损失函数取最小值时,得到k就是最优解
-

想求一条直线更好的拟合所有点 y = kx + b
-
引入损失函数(衡量预测值和真实值效果) Loss(k, b)
-
通过一个优化方法,求损失函数最小值,得到K最优解
-

回归的损失函数:
- 均方误差 (Mean-Square Error, MSE)
-
平均绝对误差 (Mean Absolute Error , MAE)
-

-
导数和矩阵
常见的数据表述
- 标量scalar :一个独立存在的数,只有大小没有方向
- 向量vector :向量指一列顺序排列的元素。默认是列向量


- 矩阵matrix :二维数组


- 张量Tensor :多维数组,张量是基于向量和矩阵的推广

- 标量是数,向量是数列,矩阵是数表,张量是它们的统称,能表示任意维度的数据结构。
导数
当函数y=f(x)的自变量x在一点x0x0上产生一个增量Δx时,函数输出值的增量Δy与自变量增量Δx的比值在Δx趋于0时的极限a如果存在,a即为在x0x0处的导数,记作f′(x0)f′(x0)或df(x0x0)/dx。

导数是函数的局部性质。一个函数在某一点的导数描述了这个函数在这一点附近的变化率。
函数在某一点的导数就是该函数所代表的曲线在这一点上的切线斜率
常见函数的导数:

复合函数求导:g(h)是外函数 h(x)是内函数。先对外函数求导,再对内函数求导

导数求极值:
导数为0的位置是函数的极值点

偏导


向量
- 在二维平面上,向量可以用一个箭头表示。
- 比如:
- 点 A(1,1):从原点指向 (1,1)
- 点 B(1,2):从原点指向 (1,2)

- 向量可以表示为坐标形式
- 把两个向量首尾相连,结果是从第一个起点到第二个终点的向量。
矩阵


一元线性回归的解(了解)
梯度下降算法
什么是梯度下降法
求解函数极值还有更通用的方法就是梯度下降法。顾名思义:沿着梯度下降的方向求解极小值 • 举个例子:坡度最陡下山法

- 输入:初始化位置S;每步距离为a 。输出:从位置S到达山底
- 步骤1:令初始化位置为山的任意位置S
- 步骤2:在当前位置环顾四周,如果四周都比S高返回S;否则执行步骤3
- 步骤3: 在当前位置环顾四周,寻找坡度最陡的方向,令其为x方向
- 步骤4:沿着x方向往下走,长度为a,到达新的位置S‘
- 步骤5:在S‘位置环顾四周,如果四周都比S‘高,则返回S‘。否则转到步骤3

- 最终找到最优解 这个方法可用来求损失函数最优解,



多变量梯度下降


一句话总结,梯度下降公式:下个点=当前点-学习率*损失函数
损失函数分类:最小二乘法Σ(预测值-真实值)²
梯度下降算法分类


正则化
过拟合:一个假设 在训练数据上能够获得比其他假设更好的拟合, 但是在测试数据集上却不能很好地拟合数据 (体现在准确率下降),此时认为这个假设出现了过拟合的现象。(模型过于复杂)
欠拟合:一个假设 在训练数据上不能获得更好的拟合,并且在测试数据集上也不能很好地拟合数据 ,此时认为这个假设出现了欠拟合的现象。(模型过于简单)

欠拟合在训练集和测试集上的误差都较大
过拟合在训练集上误差较小,而测试集上误差较大
欠拟合产生原因: 学习到数据的特征过少
解决办法:
1)添加其他特征项,有时出现欠拟合是因为特征项不够导致的,可以添加其他特征项来解决
2)添加多项式特征,模型过于简单时的常用套路,例如将线性模型通过添加二次项或三次项使模型泛化能力更强
过拟合产生原因: 原始特征过多,存在一些嘈杂特征, 模型过于复杂是因为模型尝试去兼顾所有测试样本
解决办法:
1)重新清洗数据,导致过拟合的一个原因有可能是数据不纯,如果出现了过拟合就需要重新清洗数据。
2)增大数据的训练量,还有一个原因就是我们用于训练的数据量太小导致的,训练数据占总数据的比例过小。
3)正则化
4)减少特征维度
正则化
在解决回归过拟合中,我们选择正则化。但是对于其他机器学习算法如分类算法来说也会出现这样的问题,除了一些算法本身作用之外(决策树、神经网络),我们更多的也是去自己做特征选择,包括之前说的删除、合并一些特征
**在学习的时候,数据提供的特征有些影响模型复杂度或者这个特征的数据点异常较多,所以算法在学习的时候尽量减少这个特征的影响(甚至删除某个特征的影响),这就是正则化**
作用:用来进行特征选择,主要原因在于L1正则化会使得较多的参数为0,从而产生稀疏解,可以将0对应的特征遗弃,进而用来选择特征。一定程度上L1正则也可以防止模型过拟合。
L2正则化
- 假设𝐿(𝑊)是未加正则项的损失,𝜆是一个超参,控制正则化项的大小。
- 则最终的损失函数:𝐿=𝐿(𝑊)+λ∗∑i=1nwi2L=L(W)+λ∗∑i=1nwi2
作用:主要用来防止模型过拟合,可以减小特征的权重
优点:越小的参数说明模型越简单,越简单的模型则越不容易产生过拟合现象
L1 和 L2 正则化都是为了防止模型“过拟合”(overfitting)而加入的“惩罚项”,它们通过限制模型参数(权重)的大小,让模型变得更简单、更稳定、泛化能力更强。
L1 正则化(Lasso)
假设你有100个特征,但只有10个真正有用。
L1 正则化会把那90个无用特征的权重压到 0,模型只用10个特征做预测!
✅ 优点:
- 模型更简单、可解释性强
- 自动剔除不重要的特征
❌ 缺点:
- 当特征高度相关时,L1 可能随机选一个,不稳定
- 💡 L1 = “稀疏解” = “精简模型”
L2 正则化(Ridge)
还是100个特征,L2 不会删除任何特征,但会让每个权重都很小(比如从 5.0 → 0.2),避免某个特征“一家独大”。
✅ 优点:
- 防止某些权重过大(尤其当特征相关时很稳定)
- 计算稳定,收敛快
❌ 缺点:
- 不能做特征选择(所有特征都保留)
- 💡 L2 = “权重收缩” = “防止单个特征主导”
| 特性 | L1 正则化(Lasso) | L2 正则化(Ridge) |
|---|---|---|
| 数学形式 | ∑∣wj∣∑∣wj∣ | ∑wj2∑wj2 |
| 权重是否为0 | ✅ 会变成0(稀疏) | ❌ 不会为0 |
| 特征选择 | ✅ 自动选择重要特征 | ❌ 保留所有特征 |
| 对异常值敏感度 | 较高(因绝对值) | 较低(因平方平滑) |
| 适用场景 | 特征很多,想筛选 | 特征少/相关性强,想稳定 |
| 防止过拟合 | 通过惩罚大权重,让模型更简单 |
| 提高泛化能力 | 在新数据上表现更好 |
| L1 额外功能 | 自动做特征选择(得到稀疏模型) |
| L2 额外功能 | 稳定模型,尤其适合特征相关的情况 |
- 想简化模型、找出关键特征 → 用 L1(Lasso)
- 想稳定预测、防止过拟合但保留所有特征 → 用 L2(Ridge)
- 不确定?可以试试 ElasticNet(L1 + L2 混合)
逻辑回归
逻辑回归是一种用于解决二分类问题的机器学习算法。它的核心思想是:通过一个S形曲线(Sigmoid函数),将线性回归的连续值输出映射到0到1之间的概率,从而判断样本属于某个类别的可能性。虽然名字中有“回归”,但它是一个经典的分类算法。
核心原理
-
线性部分:首先像线性回归一样,计算输入特征的加权和:
z = w₁x₁ + w₂x₂ + ... + b。 -
Sigmoid映射:然后将线性结果
z代入Sigmoid函数,将其压缩到(0,1)区间,得到概率值:P = 1 / (1 + e^{-z})。 -

-
决策:通常设定一个阈值(如0.5)。当
P ≥ 0.5时,预测为正类(输出1);当P < 0.5时,预测为负类(输出0) -
主要特点与应用
-
用途:非常适合处理二分类问题,例如:
-
判断邮件是否为垃圾邮件。
-
预测交易是否存在欺诈风险。
-
诊断患者是否患有某种疾病。
-
-
优点:模型可解释性强,可以给出分类的概率值,而不仅仅是类别结果;计算效率高,易于实现。
-
局限:本质上是一个线性分类器(决策边界是线性的),对于特征间存在复杂非线性关系的数据,拟合能力有限
-
简单来说,逻辑回归就是用一个“S”形曲线来做是非判断的模型。
核心思想:
设模型中含有待估参数w,可以取很多值。已经知道了样本观测值,从w的一切可能值中(选出一个使该观察值出现的概率为最大的值,作为w参数的估计值,这就是极大似然估计。(顾名思义:就是看上去那个是最大可能的意思
假设有一枚不均匀的硬币,出现正面的概率和反面的概率是不同的。假定出现正面的概率为𝜃, 抛了6次得到如下现象 D = {正面,反面,反面,正面,正面,正面}。每次投掷事件都是相互独立的。 则根据产生的现象D,来估计参数𝜃是多少?
问题转化为:求此函数的极大值时,估计𝜃为多少

对数函数

逻辑回归原理
逻辑回归概念 Logistic Regression
• 一种分类模型,把线性回归的输出,作为逻辑回归的输入。
• 输出是(0, 1)之间的值
• 基本思想
- 利用线性模型 f(x) = wx + b 根据特征的重要性计算出一个值
- 再使用 sigmoid 函数将 f(x) 的输出值映射为概率值
- 设置阈值(eg:0.5),输出概率值大于 0.5,则将未知样本输出为 1 类
- 否则输出为 0 类
3.逻辑回归的假设函数 h(w) = sigmoid(wx + b )
线性回归的输出,作为逻辑回归的输入

损失函数



逻辑回归API
sklearn.linear_model.LogisticRegression(solver='liblinear', penalty=‘l2’, C = 1.0)
solver 损失函数优化方法:
训练速度:liblinear 对小数据集场景训练速度更快,sag 和 saga 对大数据集更快一些。 2 正则化:
- newton-cg、lbfgs、sag、saga 支持 L2 正则化或者没有正则化
- 2liblinear 和 saga 支持 L1 正则化
penalty:正则化的种类,l1 或者 l2
【实践】癌症分类案例
- 数据介绍
(1)699条样本,共11列数据,第一列用语检索的id,后9列分别是与肿瘤相关的
最后一列表示肿瘤类型的数值。
(2)包含16个缺失值,用”?”标出。
(3)2表示良性,4表示恶性
医学特征,
数据描述
(1)699条样本,共11列数据,第一列用语检索的id,后9列分别是与肿瘤
相关的医学特征,最后一列表示肿瘤类型的数值。
(2)包含16个缺失值,用”?”标出。
1.获取数据
2.基本数据处理
2.1 缺失值处理
2.2 确定特征值,目标值
2.3 分割数据
3.特征工程(标准化)
4.机器学习(逻辑回归)
5.模型评估
import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
def dm01_LogisticRegression():
# 1. 获取数据.
data = pd.read_csv('data/breast-cancer-wisconsin.csv')
data.info()
# 2. 数据预处理.
# data = data.replace(to_replace='?', value=np.NAN)
data = data.replace('?', np.NaN)
data = data.dropna()
data.info()
# 3. 确定特征值和目标值.
x = data.iloc[:, 1:-1]
y = data.Class
print(f'x.head(): {x.head()}')
print(f'y.head(): {y.head()}')
#
# # 3. 分割数据.
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=21)
# # 4. 特征处理.
transfer = StandardScaler()
x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)
# # 5. 模型训练.
estimator = LogisticRegression()
estimator.fit(x_train, y_train)
# # 6. 模型预测
y_predict = estimator.predict(x_test)
print(f'预测值: {y_predict}')
#
# # 7. 模型评估
print(f'准确率: {estimator.score(x_test, y_test)}')
print(f'准确率: {accuracy_score(y_test, y_predict)}')
if __name__ == '__main__':
dm01_LogisticRegression()
分类评估方法
1.理解混淆矩阵的构建方法
2.掌握精确率,召回率和F1score的计算方法
3.知道ROC曲线和AUC指标

混淆矩阵作用在测试集样本集中:
- 真实值是 正例 的样本中,被分类为 正例 的样本数量有多少,这部分样本叫做真正例(TP,True Positive)
- 真实值是 正例 的样本中,被分类为 假例 的样本数量有多少,这部分样本叫做伪反例(FN,False Negative)
- 真实值是 假例 的样本中,被分类为 正例 的样本数量有多少,这部分样本叫做伪正例(FP,False Positive)
- 真实值是 假例 的样本中,被分类为 假例 的样本数量有多少,这部分样本叫做真反例(TN,True Negative)
True Positive :表示样本真实的类别 Positive :表示样本被预测为的类别
例子:
样本集中有 6 个恶性肿瘤样本,4 个良性肿瘤样本,我们假设恶性肿瘤为正例,则:
模型 A: 预测对了 3 个恶性肿瘤样本,4 个良性肿瘤样本
- 真正例 TP 为:3
- 伪反例 FN 为:3
- 伪正例 FP 为:0
- 真反例 TN:4
模型 B: 预测对了 6 个恶性肿瘤样本,1个良性肿瘤样本
- 真正例 TP 为:6
- 伪反例 FN 为:0
- 伪正例 FP 为:3
- 真反例 TN:1
- 我们会发现:TP+FN+FP+TN = 总样本数量
def dm01_混淆矩阵四个指标(): # 1. 定义样本集, 6个恶性肿瘤样本, 4个良性肿瘤样本 y_true = ['恶性', '恶性', '恶性', '恶性', '恶性', '恶性', '良性', '良性', '良性', '良性'] # 2. 定义标签 labels = ['恶性', '良性'] # 3. 模型A => 预测对了 3个恶性, 4个良性. y_pred_A = ['恶性', '恶性', '恶性', '良性', '良性', '良性', '良性', '良性', '良性', '良性'] result = confusion_matrix(y_true, y_pred_A) print(pd.DataFrame(result, index=labels, columns=labels)) # 4. 模型B => 预测对了6个恶性肿瘤样本, 1个良性肿瘤样本. y_pred_B = ['恶性', '恶性', '恶性', '恶性', '恶性', '恶性', '恶性', '恶性', '恶性', '良性'] result = confusion_matrix(y_true, y_pred_B) print(pd.DataFrame(result, index=labels, columns=labels)) # 5. 计算精确率 (precision) print(f'模型A精确率: {precision_score(y_true, y_pred_A, pos_label="恶性")}') print(f'模型B精确率: {precision_score(y_true, y_pred_B, pos_label="恶性")}') # 6. 计算召回率 (recall) print(f'模型A召回率: {recall_score(y_true, y_pred_A, pos_label="恶性")}') print(f'模型B召回率: {recall_score(y_true, y_pred_B, pos_label="恶性")}') # 7. 计算F1值 print(f'模型A F1值: {f1_score(y_true, y_pred_A, pos_label="恶性")}') print(f'模型B F1值: {f1_score(y_true, y_pred_B, pos_label="恶性")}') if __name__ == '__main__': dm01_混淆矩阵四个指标()Precision(精确率)
精确率也叫做查准率,指的是对正例样本的预测准确率。比如:我们把恶性肿瘤当做正例样本,则我们就需要知道模型对恶性肿瘤的预测准确率。
-

决策树
决策树算法是一种监督学习算法,英文是Decision tree。决策树思想的来源非常朴素,试想每个人的大脑都有类似于if-else这样的逻辑判断,这其中的if表示的是条件,if之后的else就是一种选择或决策。程序设计中的条件分支结构就是if-else结构,最早的决策树就是利用这类结构分割数据的一种分类学习方法。
策树是什么?
决策树是一种树形结构,树中每个内部节点表示一个特征上的判断,每个分支代表一个判断结果的输出,每个叶子节点代表一种分类结果
决策树的建立过程:
1.特征选择:选取有较强分类能力的特征。
2.决策树生成:根据选择的特征生成决策树。
3.决策树也易过拟合,采用剪枝的方法缓解过拟合。
信息熵
ID3 树是基于信息增益构建的决策树.
定义
- 熵在信息论中代表随机变量不确定度的度量。
- 熵越大,数据的不确定性度越高(白话代表数据越乱)
- 熵越小,数据的不确定性越低(代表数据也规整)
公式
例子1:假如有三个类别,分别占比为:{1/3,1/3,1/3},信息熵计算结果为:

例子2:假如有三个类别,分别占比为:{1/10,2/10,7/10},信息熵计算结果为:

熵越大,表示整个系统不确定性越大,越随机,反之确定性越强。
例子3:假如有三个类别,分别占比为:{1,0,0},信息熵计算结果为:
H=−1log(1)=0
信息增益是机器学习中用于衡量一个特征对分类结果贡献程度的指标。它基于信息论中的熵概念,计算在已知某个特征的情况下,分类不确定性减少了多少。信息增益越高,说明该特征对分类的帮助越大在决策树算法中,信息增益常被用来选择最佳的特征进行节点分裂。其核心思想是:选择一个特征,使得按该特征划分后,数据集的“纯度”提升最大,即类别的不确定性(熵)降低最多。
计算上,信息增益 = 原始数据集的熵 - 按该特征划分后各子集熵的加权平均
信息熵(H)
表示随机变量的不确定性。对于分类问题,它衡量数据集中类别分布的“混乱程度”。熵值越高,不确定性越大。
2. 计算公式
对于一个包含K个类别的数据集D,其信息熵的计算公式为:
H(D) = - Σ (i=1 到 K) [p_i * log₂(p_i)]
其中,p_i 表示数据集中第 i 个类别样本所占的比例
3. 计算步骤
-
步骤一:计算每个类别的概率 p_i
统计数据集中每个类别出现的次数,并除以样本总数。
p_i = (类别 i 的样本数量) / (数据集总样本数) -
步骤二:计算每个类别的信息量 -p_i * log₂(p_i)
对每个 p_i,计算 p_i * log₂(p_i)。如果 p_i = 0,则该项定义为0。
-
步骤三:求和并取负
将所有类别的计算结果相加,然后取负数,得到最终的熵值 H(D)。
4. 举例说明
假设一个数据集有10个样本,包含两个类别:“是”(6个)和“否”(4个)。
-
p(是) = 6/10 = 0.6
-
p(否) = 4/10 = 0.4
-
H(D) = - [0.6 * log₂(0.6) + 0.4 * log₂(0.4)] ≈ - [0.6 * (-0.737) + 0.4 * (-1.322)] ≈ - [-0.442 - 0.529] ≈ 0.971
这个结果(约0.971)就是该数据集的信息熵。当所有样本属于同一类别时,熵为0(完全确定);当类别均匀分布时,熵最大(最不确定)。在之前提到的信息增益中,就是用划分前的熵减去划分后的熵,来评估特征的效果。
ID3树构建流程
ID3选择信息增益最大的特征,是为了在每一步都最大程度地降低不确定性,以最贪婪、最直接的方式逼近分类目标
构建流程:
- 计算每个特征的信息增益
- 使用信息增益最大的特征将数据集 S 拆分为子集
- 使用该特征(信息增益最大的特征)作为决策树的一个节点
- 使用剩余特征对子集重复上述(1,2,3)过程
15条样本:5正样本、10个负样本
-
计算熵
-
计算性别信息增益
-
计算活跃度信息增益
-
比较两个特征的信息增益
C4.5决策树

- Gain_Ratio 表示信息增益率
- IV 表示分裂信息、内在信息
- 特征的信息增益 ➗ 内在信息
- 如果某个特征的特征值种类较多,则其内在信息值就越大。即:特征值种类越多,除以的系数就越大。
- 如果某个特征的特征值种类较小,则其内在信息值就越小。即:特征值种类越小,除以的系数就越小
- 信息增益比本质: 是在信息增益的基础之上乘上一个惩罚参数。特征个数较多时,惩罚参数较小;特征个数较少时,惩罚参数较大。惩罚参数:数据集D以特征A作为随机变量的熵的倒数。
C4.5 vs ID3 核心对比
|
特性 |
ID3决策树 |
C4.5决策树 |
|---|---|---|
|
分裂准则 |
信息增益 |
信息增益率 |
|
特征类型 |
仅限离散特征 |
离散 + 连续特征 |
|
过拟合控制 |
无,易过拟合 |
有后剪枝 |
|
缺失值处理 |
不支持 |
支持 |
|
输出类型 |
仅分类 |
主要分类,也可做回归 |
C4.5让决策树从一个理想化的理论模型,变成了一个能真正处理现实世界混乱数据的实用工具
CART决策树
Cart模型是一种决策树模型,它即可以用于分类,也可以用于回归。
CART(Classification And Regression Trees)只构建二叉树,使用基尼不纯度或平方误差作为划分标准,既能做分类也能做回归。
一个通俗比喻
如果把决策树比喻一套问卷调查
C4.5问卷:题目可以是单选题,比如“您最喜欢的颜色? A.红 B.蓝 C.绿”。它通过题目的“质量”(增益率)来选题。
CART问卷:所有题目都是判断题,比如“您喜欢红色吗?(是/否)”。如如果否,下一题可能是“您喜欢蓝色吗?(是/否)”。它通过题目的“区分度”(基尼下降)来选题,并且这份问卷既能调查您的职业(分类),也能调查您的收入(回归)。
简单来说:如果你需要一个高效、通用的树模型,尤其是在做回归预测或使用集成方法时,CART是首选。而C4.5更多是机器学习发展史上一个承前启后的重要理论。
基尼指数计算方法
1. 对于一个节点(数据集)D 的基尼指数
假设数据集 D中有 K个类别,第 k个类别的样本所占比例为 pk,则该节点的基尼指数 Gini(D)为:
计算步骤:
-
计算数据集中每个类别出现的概率 pk(即该类样本数除以总样本数)。
-
将这些概率平方后求和。
-
用 1 减去这个和,就得到了基尼指数。
假设一个节点里有10个样本,其中6个是“苹果”,4个是“橘子”。
-
p苹果=6/10=0.6
-
p橘子=4/10=0.4
-

这个值0.48就是该节点的“不纯度”
比较
| 名称 | 提出时间 | 分支方式 | 特点 |
|---|---|---|---|
| ID3 | 1975 | 信息增益 | 1.ID3只能对离散属性的数据集构成决策树 2.倾向于选择取值较多的属性 |
| C4.5 | 1993 | 信息增益率 | 1.缓解了ID3分支过程中总喜欢偏向选择值较多的属性 2.可处理连续数值型属性,也增加了对缺失值的处理方法 3.只适合于能够驻留于内存的数据集,大数据集无能为力 |
| CART | 1984 | 基尼指数 | 1.可以进行分类和回归,可处理离散属性,也可以处理连续属性 2.采用基尼指数,计算量减小 3.一定是二叉树 |
决策树剪枝
在决策树学习中,为了尽可能正确分类训练样本,结点划分过程将不断重复,有时会造成决策树分支过多,这时就可能因训练样本学得"太好"了,以致于把训练集自身的一些特点当作所有数据都具有的一般性质而导致过拟合。因此,可通过主动去掉一些分支来降低过拟合的风险。

决策树剪枝的基本策略有"预剪枝" (pre-pruning)和"后剪枝"(post- pruning) 。
- 预剪枝是指在决策树生成过程中,对每个结点在划分前先进行估计,若当前结点的划分不能带来决策树泛化性能提升,则停止划分并将当前结点标记为叶结点;
- 后剪枝则是先从训练集生成一棵完整的决策树,然后自底向上地对非叶结点进行考察,若将该结点对应的子树替换为叶结点能带来决策树泛化性能提升,则将该子树替换为叶结点。
预剪枝优点:
- 预剪枝使决策树的很多分支没有展开,不单降低了过拟合风险,还显著减少了决策树的训练、测试时间开销
预剪枝缺点:
- 有些分支的当前划分虽不能提升泛化性能,甚至会导致泛化性能降低,但在其基础上进行的后续划分却有可能导致性能的显著提高
- 预剪枝决策树也带来了欠拟合的风险
-
后剪枝优点:
- 比预剪枝保留了更多的分支。一般情况下,后剪枝决策树的欠拟合风险很小,泛化性能往往优于预剪枝
-
后剪枝缺点:但后剪枝过程是在生成完全决策树之后进行的,并且要自底向上地对树中所有非叶子节点进行逐一考察,因此在训练时间开销比未剪枝的决策树和预剪枝的决策树都要大得多。
集成学习
集成学习是机器学习中的一种思想,它通过多个模型的组合形成一个精度更高的模型,参与组合的模型成为弱学习器(基学习器)。训练时,使用训练集依次训练出这些弱学习器,对未知的样本进行预测时,使用这些弱学习器联合进行预测。

传统机器学习算法 (例如:决策树,逻辑回归等) 的目标都是寻找一个最优分类器尽可能的将训练数据分开。集成学习 (Ensemble Learning) 算法的基本思想就是将多个分类器组合,从而实现一个预测效果更好的集成分类器。集成算法可以说从一方面验证了中国的一句老话:三个臭皮匠,赛过诸葛亮

集成学习通过建立几个模型来解决单一预测问题。它的工作原理是 生成多个分类器/模型,各自独立地学习和作出预测。这些预测最后结合成组合预测,因此优于任何一个单分类的做出预测。
集成学习算法一般分为:bagging和boosting。
bagging集成
Baggging 框架通过有放回的抽样产生不同的训练集,从而训练具有差异性的弱学习器,然后通过平权投票、多数表决的方式决定预测结果

boosting集成
Boosting 体现了提升思想,每一个训练器重点关注前一个训练器不足的地方进行训练,通过加权投票的方式,得出预测结果。

Boosting是一组可将弱学习器升为强学习器算法。这类算法的工作机制类似:
先从初始训练集训练出一个基学习器
在根据基学习器的表现对训练样本分布进行调整,使得先前基学习器做错的训练样本在后续得到最大的关注。
然后基于调整后的样本分布来训练下一个基学习器;
.如此重复进行,直至基学习器数目达到实现指定的值T为止。
再将这T个基学习器进行加权结合得到集成学习器。
简而言之:每新加入一个弱学习器,整体能力就会得到提升

Bagging 与 Boosting
区别一:数据方面
- Bagging:有放回采样
- Boosting:全部数据集, 重点关注前一个弱学习器不足
区别二:投票方面
- Bagging:平权投票
- Boosting:加权投票
区别三:学习顺序
- Bagging的学习是并行的,每个学习器没有依赖关系
- Boosting学习是串行,学习有先后顺序
随机森林
随机森林是基于 Bagging 思想实现的一种集成学习算法,它采用决策树模型作为每一个基学习器。其构造过程:
- 训练:
- 有放回的产生训练样本
- 随机挑选 n 个特征(n 小于总特征数量)
- 预测:平权投票,多数表决输出预测结果

- 随机森林的步骤

如果不进行随机抽样,每棵树的训练集都一样,那么最终训练出的树分类结果也是完全一样。
- 为什么要随机抽样训练集?
- 如果不进行随机抽样,每棵树的训练集都一样,那么最终训练出的树分类结果也是完全一样。
- 为什么要有放回地抽样?
- 如果不是有放回的抽样,那么每棵树的训练样本都是不同的,都是没有交集的,这样每棵树都是“有偏的”,都是绝对“片面的”,也就是说每棵树训练出来都是有很大的差异的;而随机森林最后分类取决于多棵树(弱分类器)的投票表决。
随机森林 API
sklearn.ensemble.RandomForestClassifier()
n_estimators:决策树数量,(default = 10)
Criterion:entropy、或者 gini, (default = gini)
max_depth:指定树的最大深度,(default = None 表示树会尽可能的生长)
max_features="auto”, 决策树构建时使用的最大特征数量
bootstrap:是否采用有放回抽样,如果为 False 将会使用全部训练样本,(default = True)
min_samples_split: 结点分裂所需最小样本数,(default = 2)
- 如果节点样本数少于min_samples_split,则不会再进行划分.
- 如果样本量不大,不需要设置这个值.
- 如果样本量数量级非常大,则推荐增大这个值.
Adaboost
AdaBoost是一个优雅的“从错误中学习”的算法。它通过动态调整样本权重,让后续的模型集中火力攻克之前的“难题”,再通过加权投票,让更靠谱的模型说话更算数。它是Boosting家族的开创者,思想直接启发了后面更强大的梯度提升树(GBDT/XGBoost)
你可以把它看作是一位善于因材施教的教练,总能发现队员的弱点并进行针对性训练,最终打造出一支冠军队伍。
AdaBoost 模型公式中

- α 为模型的权重
- m 为弱学习器数量
- hi(x) 表示弱学习器
- H(x) 输出结果大于 0 则归为正类,小于 0 则归为负类。
一个极简的比喻
-
随机森林:召集100个普通人,让他们各自独立看一部分线索,然后一人一票投票破案。追求稳定,防止个人偏见。
-
AdaBoost:请3个侦探接力破案。侦探B专门检查侦探A漏掉的线索,侦探C再专门查侦探A和B都没搞定的部分。最后破案时,能力强的侦探发言权更大。追求极致,专门攻克难点。
AdaBoost就像一位校长管理教师团队:
-
给每个学生标注重视程度(权重)
-
让新老师重点教之前没学会的学生
-
教学水平高的老师,在教研会上发言权更大
-
所有老师投票决定难题答案,水平高的老师一票抵多票
XGBoost
XGBoost(eXtreme Gradient Boosting)是一种“集成学习”算法,它通过把很多棵简单的决策树“串起来”,让它们互相纠正错误,最终做出非常准确的预测。
它就像一个超级学霸团队:
- 第一个人做题,错了;
- 第二个人专门研究第一个人错在哪,再做一遍;
- 第三个人接着修正前两个人的错误……
- 最后大家把答案加起来,结果就非常准!
# ==============================
# 第一步:安装与导入(如未安装,请先运行:R)
# ==============================
from xgboost import XGBRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
import pandas as pd
import numpy as np
print("🚀 开始 XGBoost 学习之旅!")
# ==============================
# 第二步:准备数据(波士顿房价)
# ==============================
# 从官方源加载数据(绕过被弃用的 load_boston)
url = "http://lib.stat.cmu.edu/datasets/boston"
raw_df = pd.read_csv(url, sep=r"\s+", skiprows=22, header=None)
data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
target = raw_df.values[1::2, 2].astype(float)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
data, target, test_size=0.2, random_state=42
)
print(f"📚 数据准备完成:{X_train.shape[0]} 个训练样本,{X_test.shape[0]} 个测试样本")
# ==============================
# 第三步:创建 XGBoost 模型
# ==============================
model = XGBRegressor(
n_estimators=100, # 总共用多少棵树(越多越强,但别太多!)
max_depth=3, # 每棵树的最大深度(控制复杂度,3~6 常见)
learning_rate=0.1, # 学习率(每棵树只学一小步,防止“矫枉过正”)
reg_alpha=0.1, # L1 正则化(越大,模型越简(单让一些叶子输出为 0))
reg_lambda=1.0, # L2 正则化(默认就有,防过拟合(让叶子输出不要太大))
random_state=42
)
print("🧠 模型已配置,参数说明:")
print(" - 100 棵树,每棵最多分 3 层")
print(" - 学习率 0.1(保守学习)")
print(" - 启用 L1/L2 正则化(防过拟合)")
# ==============================
# 第四步:训练模型
# ==============================
print("\n⏳ 正在训练模型...")
model.fit(X_train, y_train)
print("✅ 训练完成!")
# ==============================
# 第五步:做预测 & 评估
# ==============================
y_pred = model.predict(X_test)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
r2 = r2_score(y_test, y_pred)
print(f"\n🎯 模型性能:")
print(f" - RMSE(均方根误差): {rmse:.2f} (越小越好)")
print(f" - R²(决定系数) : {r2:.4f} (越接近1越好)")
# ==============================
# 第六步:看看哪些特征最重要?
# ==============================
importances = model.feature_importances_
print("\n🔍 特征重要性排名(前5):")
# 给特征编号(实际中可替换为真实名称,如 'RM', 'LSTAT' 等)
feature_names = [f"Feature_{i+1}" for i in range(len(importances))]
sorted_idx = np.argsort(importances)[::-1]
for i in range(5):
idx = sorted_idx[i]
print(f" {i+1}. {feature_names[idx]}: {importances[idx]:.4f}")
XGBoost 优缺点
- 高准确性:XGBoost在许多数据集上都能够获得很高的预测准确性。🎯🌟
- 可扩展性:XGBoost具有并行处理能力,可以有效地处理大规模数据。💪🚀
- 灵活性:XGBoost支持多种损失函数和正则化方法,可根据具体问题进行调整。🔧🧩
- 特征重要性:XGBoost可以自动计算特征的重要性,帮助理解数据。🔍
缺点:
- 参数调优:XGBoost有许多参数需要调优,不合适的参数设置可能导致过拟合或欠拟合。⚙️
- 计算资源需求:XGBoost在训练和预测时需要较多的计算资源。💻⏳
- 不擅长处理文本数据:相对于其他算法(如深度学习模型),XGBoost不太擅长处理文本数据
朴素贝叶斯
一、一句话理解朴素贝叶斯
"根据历史经验,判断新事物最可能属于哪一类"
就像医生看病:
-
历史经验:发烧+咳嗽 → 80%是感冒,20%是肺炎
-
新病人:有发烧+咳嗽症状
-
判断:最可能是感冒(80%概率
贝叶斯定理公式
P(A|B) = P(B|A) × P(A) ÷ P(B)
朴素贝叶斯分类器:从零到精通
一、一句话理解朴素贝叶斯
"根据历史经验,判断新事物最可能属于哪一类"
就像医生看病:
-
历史经验:发烧+咳嗽 → 80%是感冒,20%是肺炎
-
新病人:有发烧+咳嗽症状
-
判断:最可能是感冒(80%概率)
二、核心思想:贝叶斯定理
1. 贝叶斯定理公式
复制
P(A|B) = P(B|A) × P(A) ÷ P(B)
2. 用生活例子理解
假设你想判断一封邮件是不是垃圾邮件:
|
符号 |
含义 |
例子 |
|---|---|---|
|
P(垃圾邮件) |
垃圾邮件的先验概率 |
历史邮件中20%是垃圾邮件 → P(垃圾)=0.2 |
|
P(正常邮件) |
正常邮件的先验概率 |
历史邮件中80%是正常邮件 → P(正常)=0.8 |
|
P("中奖"|垃圾) |
已知是垃圾邮件时,包含"中奖"的概率 |
垃圾邮件中60%有"中奖" → P("中奖"|垃圾)=0.6 |
|
P("中奖"|正常) |
已知是正常邮件时,包含"中奖"的概率 |
正常邮件中5%有"中奖" → P("中奖"|正常)=0.05 |
|
P(垃圾|"中奖") |
看到"中奖"时,邮件是垃圾的概率 |
这是我们想求的! |
计算过程:
P(垃圾|"中奖") = P("中奖"|垃圾) × P(垃圾) ÷ P("中奖")
= 0.6 × 0.2 ÷ [P("中奖"|垃圾)×P(垃圾) + P("中奖"|正常)×P(正常)]
= 0.6 × 0.2 ÷ (0.6×0.2 + 0.05×0.8)
= 0.12 ÷ (0.12 + 0.04)
= 0.12 ÷ 0.16
= 0.75
结论:看到"中奖"这个词,邮件有75%概率是垃圾邮件!
三、为什么叫"朴素"
"朴素" = 特征相互独立假设
假设各个特征之间没有关系,独立影响结果。
拉普拉斯平滑系数
由于训练样本的不足,导致概率计算时出现 0 的情况。为了解决这个问题,我们引入了拉普拉斯平滑系数。
示例:垃圾邮件分类
训练数据:
|
邮件 |
包含"中奖" |
包含"会议" |
类别 |
|---|---|---|---|
|
1 |
是 |
否 |
垃圾 |
|
2 |
是 |
否 |
垃圾 |
|
3 |
否 |
是 |
正常 |
|
4 |
否 |
是 |
正常 |
计算条件概率:
-
在垃圾邮件中,P("会议"|垃圾) = 0/2 = 0(在全部(2封)垃圾邮件中,有(0封)包含了“会议”这个词。这个概率是0)
-
在正常邮件中,P("会议"|正常) = 2/2 = 1
现在来了一封新邮件:包含"会议"和"中奖"
朴素贝叶斯计算:
P(垃圾|"会议","中奖") ∝ P("会议"|垃圾)×P("中奖"|垃圾)×P(垃圾)
= 0 × 1 × 0.5
= 0
【案例】情感分析
sklearn.naive_bayes.MultinomialNB(alpha = 1.0)
- 朴素贝叶斯分类
- alpha:拉普拉斯平滑系数
- 已知商品评论数据,根据数据进行情感分类(好评、差评

步骤分析
- 1)获取数据
- 2)数据基本处理
- 2.1) 取出内容列,对数据进行分析
- 2.2) 判定评判标准
- 2.3) 选择停用词
- 2.4) 把内容处理,转化成标准格式
- 2.5) 统计词的个数
- 2.6)准备训练集和测试集
- 3)模型训练
- 4)模型评估
"""
案例:
演示通过 朴素贝叶斯算法 实现 商品评论情感分析, 即: 好评, 差评...
朴素贝叶斯介绍:
概述:
贝叶斯: 仅仅依赖 概率 就可以进行分类的 一种机器学习算法.
朴素: 不考虑特征之间的关联性, 即: 特征间都是相互独立的.
原始: P(AB) = P(A) * P(B|A) = P(B) * P(A|B)
加入朴素后: P(AB) = P(A) * P(B)
细节:
因为我们分词要用到 jieba分词器, 记得先装一下, 例如: pip install jieba
"""
# 导包
import numpy as np # 数学计算包
import pandas as pd # 数据处理包
import matplotlib.pyplot as plt # 画图包
import jieba # 分词包
from sklearn.feature_extraction.text import CountVectorizer # 词频统计包, 把评论内容 转成 词频矩阵.
from sklearn.metrics import accuracy_score
from sklearn.naive_bayes import MultinomialNB # 朴素贝叶斯对象
# 1. 读取文件, 获取到原始数据.
df = pd.read_csv('./data/书籍评价.csv', encoding='gbk')
# df.info()
# 2. 数据预处理.
# 2.1 添加labels列, 充当: 标签列. 好评 -> 1, 差评 -> 0
df['labels'] = np.where(df['评价'] == '好评', 1, 0)
# df.info()
# print(df)
# 2.2 抽取 labels列, 作为: 标签.
y = df['labels']
# 2.3 演示 jieba 分词
# print(jieba.lcut('好好学习, 天天向上! 我爱你你爱我, 蜜雪冰城甜蜜蜜! 小明骑车, 一把把把把住了.'))
# 2.4 对用户的评论信息, 做切词.
# 数据格式: [[第1条评论切词1, 切词2, 切词3...], [第2条评论切词1, 切词2, 切词3...], ...]
comment_list = [','.join(jieba.lcut(line)) for line in df['内容']]
# 数据格式: ['第1条评论切词1, 切词2, 切词3...', '第2条评论切词1, 切词2, 切词3...', ...]
print(comment_list)
# 演示字符串的 join()函数用法.
# my_list = ['aa', 'bb', 'cc']
# print(','.join(my_list))
# 2.5 加载 停用词列表, 即: 里边记录的词, 不需要参与模型训练, 预测, 要被删除的词, 例如: 的, 啊, 哈, 从, 都...
with open('./data/stopwords.txt', 'r', encoding='utf-8') as src_f:
# 2.5.1 一次读取所有的行
stopwords_list = src_f.readlines()
# 2.5.2 删除最后的 '\n'
stopwords_list = [line.strip() for line in stopwords_list]
# 2.5.3 对 停用词列表去重.
stopwords_list = list(set(stopwords_list))
print(stopwords_list)
# 2.6 创建向量化对象, 从 评论切词列表(comment_list) 中 删除 停用词, 并且统计词频(单词矩阵).
transfer = CountVectorizer(stop_words=stopwords_list) # 参数: 停用词列表.
# 2.7 统计词频矩阵, 先训练, 后转换, 在转数组.
# transfer.fit(comment_list)
# x的格式: [[第1条评论的切词分布, 有就是1, 没有就是0], [第2条评论的切词分布, 有就是1, 没有就是0], ...]
# x = transfer.transform(comment_list).toarray()
x = transfer.fit_transform(comment_list).toarray()
print(x)
# 2.8 看一下 我们13条评论, 切词, 且删除 停用词后, 一共剩下多少个词了.
print(transfer.get_feature_names_out())
print(len(transfer.get_feature_names_out())) # 37个词, 即: 13条评论, 切词, 且删除 停用词后, 一共剩下多少个词了.
# 2.9 因为就 13条数据, 我们把前10条当训练集, 后三条当测试集.
x_train = x[:10]
y_train = y[:10]
x_test = x[10:]
y_test = y[10:]
# 3. 特征工程, 此处略.
# 4. 模型训练.
estimator = MultinomialNB() # 创建 朴素贝叶斯模型对象.
estimator.fit(x_train, y_train)
# 5. 模型预测.
y_pred = estimator.predict(x_test)
print(f'模型预测结果: {y_pred}')
# 6. 模型评估.
print(f'准确率: {accuracy_score(y_test, y_pred)}')
聚类
一种典型的无监督学习算法,主要用于将相似的样本自动归到一个类别中。
在聚类算法中根据样本之间的相似性,将样本划分到不同的类别中,对于不同的相似度计算方法,会得到不同的聚类结果,常用的相似度计算方法有欧式距离法。
聚类算法在现实中的应用
- 用户画像,广告推荐,Data Segmentation,搜索引擎的流量推荐,恶意流量识别
- 基于位置信息的商业推送,新闻聚类,筛选排序
- 图像分割,降维,识别;离群点检测;信用卡异常消费;发掘相同功能的基因片段

核心思想
K-Means 的目标很简单:将 n个数据点划分到 k个簇中,使得每个数据点都属于离它最近的簇中心(质心)对应的簇,并且让每个簇内部的样本尽可能相似(即簇内方差最小化)。
您想了解的应该是 K-Means 聚类算法,它是机器学习中最经典、最常用的无监督学习算法之一,用于将数据点自动划分到不同的类别(簇)中。
核心思想
K-Means 的目标很简单:将 n个数据点划分到 k个簇中,使得每个数据点都属于离它最近的簇中心(质心)对应的簇,并且让每个簇内部的样本尽可能相似(即簇内方差最小化)。
算法步骤
K-Means 是一个迭代优化过程,通常包括以下四步:
-
初始化:随机选择
k个数据点作为初始的簇中心(质心)。 -
分配:对于数据集中的每一个点,计算它与
k个质心的距离(通常用欧氏距离),并将其分配给距离最近的那个质心所在的簇。 -
更新:所有点分配完毕后,重新计算每个簇的质心。新质心是该簇所有点的平均值(即坐标的均值)。
-
迭代:重复步骤2(分配)和步骤3(更新),直到满足终止条件(例如:质心的位置不再发生显著变化,或达到最大迭代次数)
聚类一句话总结
先猜K个中心,然后反复做两件事:1. 让所有点找最近的中心入伙;2. 重新计算每个团伙的中心点。直到中心点不再移动。
K-Means = 指定K值 + 迭代更新中心 + 按距离分组
【实践】案例
了解】案例介绍
已知:客户性别、年龄、年收入、消费指数
需求:对客户进行分析,找到业务突破口,寻找黄金客户

数据集共包含顾客的数据, 数据共有 4 个特征, 数据共有 200 条。接下来,使用聚类算法对具有相似特征的的顾客进行聚类,并可视化聚类结果
mport matplotlib.colors
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
import pandas as pd
from sklearn.manifold import TSNE
from sklearn.preprocessing import StandardScaler
pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', None)
pd.set_option('display.width', 1000)
if __name__ == '__main__':
# 1. 读取顾客数据
data = pd.read_csv('data/customers.csv')
data.columns = ['CustomerID', 'Gender', 'Age', 'Annual Income', 'Spending Score']
# print(data.head())
# 2. 对 Gender 特征进行独热编码
data = pd.get_dummies(data, columns=['Gender'])
# print(data.head())
# 3. 数据标准化
scaler = StandardScaler()
data = scaler.fit_transform(data)
print(data)
# 4. 去除非 ID 列进行聚类分析
data = data[:, 1:]
# print(data[:5])
# 5. 肘部法寻找质心个数
sse = []
for k in range(1, 20):
estimator = KMeans(n_clusters=k, random_state=0)
estimator.fit(data)
sse.append(estimator.inertia_)
plt.plot(range(1, 20), sse)
plt.show()
# 6. 确定质心的个数
estimator = KMeans(n_clusters=10, n_init=10, random_state=0)
y_pred = estimator.fit_predict(data)
# 7. 聚类结果可视化
plt.scatter(X.values[y_kmeans == 0, 0], X.values[y_kmeans == 0, 1], s=100, c='red', label='Standard')
plt.scatter(X.values[y_kmeans == 1, 0], X.values[y_kmeans == 1, 1], s=100, c='blue', label='Traditional')
plt.scatter(X.values[y_kmeans == 2, 0], X.values[y_kmeans == 2, 1], s=100, c='green', label='Normal')
plt.scatter(X.values[y_kmeans == 3, 0], X.values[y_kmeans == 3, 1], s=100, c='cyan', label='Youth')
plt.scatter(X.values[y_kmeans == 4, 0], X.values[y_kmeans == 4, 1], s=100, c='magenta', label='TA')
plt.scatter(mykeans.cluster_centers_[:, 0], mykeans.cluster_centers_[:, 1], s=300, c='black', label='Centroids’)
plt.title('Clusters of customers')
plt.xlabel('Annual Income (k$)')
plt.ylabel('Spending Score (1-100)')
plt.legend()
plt.show()
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
















所有评论(0)