机器学习(1)

一、规范行为

  • 明确何为项目,改如何命名,又如何存储

    • 文件路径不能存在中文以及特殊字符,防止发生意外

  • 学习过程中可以随意命名变量、函数名等,但是项目中必须严格遵守命名规范

  • 要明确每一个软件安装的作用和目的,并清楚安装位置在哪里【一切软件的安装路径都不要有中文和特殊字符

  • 重要从现在开始为每个阶段项目都建立独立的虚拟环境,避不必要的麻烦

  • 不要太过依赖ai

二、环境准备

  • 打开cmd(win+R)

  • 输入命令行

conda create -n sklearn_env python=3.8

注意:创建的python版本需要为要求的对应版本,不然也容易造成版本不兼容的问题

  • 激活创建的虚拟环境【重点】

    • 不激活虚拟环境下载库等于白下

conda activate sklearn_env
  • 下载这里所需的库

    • numpy

    • pandas

    • scikit-learn

#numpy
pip install numpy==1.21.6 -i https://pypi.mirrors.ustc.edu.cn/simple/
#pandas
pip install pandas==1.1.5 -i https://pypi.mirrors.ustc.edu.cn/simple/
#scikit-learn
pip install scikit-learn==1.0.2 -i https://pypi.mirrors.ustc.edu.cn/simple/
  • 新建项目并为项目配置好虚拟环境

三、机器学习基础

1.训练数据集

  • 自行准备用来训练模型的数据集,模型可根据训练集数据来学习规律,从而能模型来对未知的数据做预测

  • 如果模型学习到的内容比较少且单一,那么使用模型的时候效果一定较差,训练模型一定要准备丰富的数据集

2、【重点】分类

  • 监督学习:目前基本上所有的模型都是基于监督学习完成的

    • 指在训练模型时,准备的数据集需要打标签

    (自行理解打标签指:训练模型时,给模型的的数据是按标签分好类的)

  • 半监督学习:

    • 训练模型时,一部分数据集打标签,一部分不打标签

  • 无监督学习:

    • 训练模型的时候,数据集不打标签,让模型自己去学习其中的规律

  • 强化学习:

    • 让智能体在与环境的交互中,不断优化自身,得到有力的反馈。

  • 打标签:

    • 训练模型时需要告诉模型,学习的数据集里面某个内容是什么

3、上游任务和下游任务

  • 上游任务:理解为做模型

  • 下游任务:理解为用模型

4、计算机视觉的三大任务

  • 图像分类任务:识别图像中的物体类别

  • 目标检测任务:识别图像中的物体类别以及物体位置

  • 图像分割任务

5、数据集的分类【重点】

  • 训练数据集:训练模型。

    • epochs:训练模型的轮次,即让模型去学习多少次数据集里面的内容

    • 数据集需要打标签

  • 验证数据集:一般情况下,每训练完一轮(epoch),就会使用验证数据集验证训练出来的模型,基于验证结果去调整模型的超参数信息利于模型训练

    • 验证数据集不参与模型训练

    • 数据集需要打标签

  • 测试数据集:模型训练好之后,在已知数据集中做测试

    • 数据集需要打标签

    • 一般是可选项,可以有可以没有

  • 推理:模型训练好之后在未知数据上做测试(不打标签的数据)。

    • 可理解为把训练好的模型拿来用

    (注:测试和推理两个动作都是在模型训练好之后)

五、sklearn库

机器学习基本所有API都来自这个库

1、数据集

  • sklearn库中提供大量内置数据集,且可以通过sklearn库去下载网络数据集

  • 自带数据集(内置):可以直接通过sklearn库对应的API去加载出来,包含下面两种数据集

    • 分类数据集

    • 线性回归数据集

1.1 内置数据

  • 通过sklearn的datasets子模块导入、在线下载及本地生成数据集的常用三种方法:

    • 本地加载数据:

      sklearn.datasets.load_<name>_
    • 远程加载数据:

    • sklearn.datasets.fetch_<name>_
    • 构造数据集:

    • sklearn.datasets.make_<name>_
  • 本地数据量小,只要安装sklearn就可以获取:

数据集获得方法适用模型
波士顿房价数据集load_boston回归模型
鸢尾花数据集load_iris分类模型
糖尿病数据集load_diabetes回归模型
手写数字识别数据集load_digits分类模型
Linnerud 数据集load_linnerud多输出回归模型
红酒数据集load_wine分类模型
乳腺癌数据集load_breast_cancer分类模型

(注意:各个数据适应什么模型就在训练什么模型的时候使用,切忌用适用回归模型分类的数据集进行分类模型的训练,或者用适用分类模型的数据集进行回归模型的训练)

1.2 网络数据

  • 网络数据集数量大,数据只能进行网络进行获取

1.3 加载数据集

1.3.1 内置数据集
  • 加载鸢尾花数据集

  • # 导入函数  --- load_iris:加载鸢尾花数据集的函数
    from sklearn.datasets import load_iris
    # 获取数据集 --- 执行 load_iris 函数
    iris = load_iris()
  • 加载糖尿病床数据

  • ## 导入函数
    from sklearn.datasets import load_diabetes
    # 获取数据集
    diabetes = load_diabetes()

中文总结步骤

  1. 导入数据集对应的函数,函数在sklearn.datasets下

  2. 调用函数,得到数据集

  3. 后续代码处理

我将以鸢尾花数据集为例,详细解释其字典结构中的各个key及其含义,并通过代码直观展示数据内容。

首先,我们可以通过scikit-learn库加载鸢尾花数据集,然后逐一查看各个key的内容:

1.3.2 鸢尾花数据集各key详细解释【重点理解】
  1. data

    • 内容为数据集

    • 是一个二维数组(ndarray),形状为(150, 4)

    • 包含150个样本(每行一个样本),每个样本有4个特征值

    • 例如,第一个样本的特征数据为[5.1, 3.5, 1.4, 0.2]

  2. target

    • 内容为类别标签

    • 是一个一维数组(ndarray),形状为(150,)

    • 每个元素是0、1或2,代表该样本的类别标签

    • 与data中的样本一一对应

  3. frame

    • 值为None,表示该数据集不是以pandas DataFrame格式存储的

  4. target_names

    • 是类别标签的实际名称

    • 对应关系:0 → setosa(山鸢尾)、1 → versicolor(变色鸢尾)、2 → virginica(维吉尼亚鸢尾)

  5. DESCR

    • 是一个字符串,包含数据集的详细描述

    • 包括数据集来源、特征说明、样本数量等信息

  6. feature_names

    • 是4个特征的名称,依次为:

      • 'sepal length (cm)':花萼长度

      • 'sepal width (cm)':花萼宽度

      • 'petal length (cm)':花瓣长度

      • 'petal width (cm)':花瓣宽度

通过以上总结,我们可以更直观地看到每个样本的特征值、标签及其对应的鸢尾花品种,从而清晰地理解整个数据集的结构和内容。

1.3.3、加载网络数据集
  • 现实世界数据,需要通过网络才能下载后,保存到本地目录,可以通过datasets.get_data_home()获取到存储的目录

  • 下载时,有可能回为网络问题而出题

  • 如果硬盘中已经下载了这个数据集,直接加载,否则会下载到磁盘中,通过参数downl_if_missing=进行调整

    • 示例:

    • import numpy as np
      #导入划分数据集函数
      from sklearn.model_selection import train_test_split
      #数据已下载,直接加载新闻数据
      from sklearn.datasets import fetch_20newsgroups
      news=fetch_20newsgroups(data_home="./", download_if_missing=False)
      ​
  • 加载Newsgroups数据集

    • # 新闻数据集
      from sklearn.datasets import fetch_20newsgroups
      """
          fetch_20newsgroups 函数参数:
              1、data_home:数据集下载之后存储的路径
              2、subset:选择下载的数据集的类型,train:训练数据集、test:测试数据集、all:训练+测试
              3、return_X_y:bool, default=False
      """
      news = fetch_20newsgroups(data_home="./", subset="all", return_X_y=False)
      print(news)
  • 加载加州福尼亚数据集

    • from sklearn.datasets import fetch_california_housing
      ​
      news=fetch_california_housing(data_home="./",return_X_y=False)
      print(news)

2、读取本地csv文件

  • 加载本地csv文件数据集,使用pandas中的read_csv函数

  • 加载本地excel文件数据集,使用pandas中的read_excel函数

3、数据集划分

  • 数据集划分:把总数据集划分为训练数据和测试数据两个部分

3.1 可变参数

  • 在 Python 中,可变参数(也称为不定长参数)是指函数可以接受任意数量的参数。Python 支持两种类型的不定长参数:

    • 位置不定长参数:使用星号 * 前缀来收集额外的位置参数,并将它们打包成一个元组(tuple)

    • 关键字不定长参数:使用双星号 ** 前缀来收集额外的关键字参数,并将它们打包成一个字典(dictionary)

3.2 train_test_split 函数

  • train_test_splitsklearn.model_selection 库中的一个非常实用的函数,用于将数据集划分为训练集和测试集。这种划分是机器学习流程中的重要步骤之一,它有助于评估模型在未见过的数据上的性能,从而避免过拟合

  • 函数描述如下:

    • 主要参数及其作用:

参数名类型默认值可选值/说明
*arrayslist, numpy array, pandas DataFrame 等-传入一个或多个数组(如特征 X 和标签 y
test_sizefloat 或 int0.25测试集大小:若为浮点数表示比例(如 0.2),若为整数表示样本数量
train_sizefloat 或 int自动计算训练集大小,若未指定则根据 test_size 推导
random_stateint 或 RandomState 实例 或 NoneNone控制随机种子,确保每次划分一致(可复现)
shuffleboolTrue是否在划分前打乱数据,默认开启;关闭后按顺序划分
stratifyarray-like 或 NoneNone用于分类任务中保持类别分布一致性(常用于不平衡数据
  • 返回值:

    • train_test_split 函数返回一个元组,包含分割后的数据集。具体返回值的数量取决于传入的数组数量。如果传入两个数组(如 Xy),则返回四个数组;如果传入三个数组,则返回六个数组,依此类推。返回值的数据类型和传入函数进行分割的数据类型一致

    • 对于两个数组的情况,返回值如下:

    返回值类型描述
    X_trainarray-like划分后的训练集特征数据
    X_testarray-like划分后的测试集特征数据
    y_trainarray-like划分后的训练集目标变量(标签)
    y_testarray-like划分后的测试集目标变量(标签)
3.2.1 随机数种子
  • 如果您不设置 random_state 或者将其设置为 None,那么每次调用 train_test_split将会产生不同的数据分割,这是因为没有固定种子的情况下,函数内部会使用系统时间或者其它方法来初始化随机数生成器

  • 当您设置 random_state 为一个固定的整数值时,每次调用 train_test_split 都会产生相同的随机分割结果。这对于调试和验证模型非常有用,因为您可以确保每次运行都是基于完全相同的数据分割来进行的,便于后期调参

  • 在 sklearn 中是使用RandomState类实现的随机数种子的生成,RandomState类是 NumPy 库中用于生成伪随机数的核心组件之一。它封装了 Mersenne Twister 算法(默认情况下)以及其他可能的伪随机数生成算法,提供了丰富的接口来生成不同分布下的随机数

3.2.2 数据类型一致
  • 如果你输入的是 Pandas DataFrame 或 Series,那么 train_test_split 返回的也将是 DataFrame 或 Series 类型的对象。如果你输入的是 Numpy 数组或者列表,那么返回的将是 Numpy 数组。即划分前后数据类型一致

3.2.3 二维数组数据集划分
  • 只划分第一维度(行),第二维度(列)保持不变(这里看结果较直观)

  • 案例代码:

import numpy as np
from sklearn.model_selection import train_test_split
​
data = np.arange(1, 16, 1)
print("原始数据:\n", data)
data.shape = (5, 3)
print("改变形状:\n", data)
print("现有形状:\n", data)
X_train, X_test = train_test_split(data, random_state=42)
print("X_train:\n", X_train)
print("X_test:\n", X_test)
  • 运行结果

原始数据:
 [ 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15]
改变形状:
 [[ 1  2  3]
 [ 4  5  6]
 [ 7  8  9]
 [10 11 12]
 [13 14 15]]
现有形状:
 [[ 1  2  3]
 [ 4  5  6]
 [ 7  8  9]
 [10 11 12]
 [13 14 15]]
X_train:
 [[ 7  8  9]
 [ 1  2  3]
 [10 11 12]]
X_test:
 [[ 4  5  6]
 [13 14 15]]
3.2.4 字典数据集划分
  • 需要使用到API:DictVectorizer

    • 参数:sparse

    • sparse=False参数指定输出为密集矩阵

      • 存储所有元素,包括零值

    • sparse=True参数指定输出为稀疏矩阵(CSR 格式)

      • 只存储非零元素的位置和值,节省内存空间

  • 在n维中,如果进行特征提取之后,进行特征信息表达,选择的是 one-hot 编码【独热编码】

    • 类似于这样存储[0 1 0 0 ... 0] 即只有一个位置上是1,其余都是0

# 定义一个数据集
data = [
    {"name": "张三", "address": "成都", "age": "18"},
    {"name": "lisi", "address": "重庆", "age": "20"},
    {"name": "王五", "address": "贵州", "age": "30"},
]
# 导入字典特征的提取的类
from sklearn.feature_extraction import DictVectorizer
dict_vec = DictVectorizer(sparse=False)
#dict_vec = DictVectorizer(sparse=True)
# 执行特征提取操作
result = dict_vec.fit_transform(data)
print(result)
print(type(result))
# 获取每一列的名称
print(dict_vec.get_feature_names())
  • 当sparse=False,数据存储为密集矩阵为:

[[1. 0. 0. 1. 0. 0. 0. 1. 0.] [0. 0. 1. 0. 1. 0. 1. 0. 0.] [0. 1. 0. 0. 0. 1. 0. 0. 1.]]

  • 它的每一列名称为:

['address=成都', 'address=贵州', 'address=重庆', 'age=18', 'age=20', 'age=30', 'name=lisi', 'name=张三', 'name=王五']

  • 当sparse=True,数据存储为密集矩阵为:

    (0, 0) 1.0(0, 3) 1.0(0, 7) 1.0(1, 2) 1.0(1, 4) 1.0(1, 6) 1.0(2, 1) 1.0(2, 5) 1.0(2, 8) 1.0

  • 只存储非零元素的位置和值,前面为非零元素的坐标(行,列),后面为值

4、特征工程

  • 特征工程目的:为了在训练模型 之前处理数据,让数据变得更好,让训练出的模型效果更好(数)

  • 处理数据方案:

    • 特征提取:提取数据中的信息,进行数据信息的转化,如果有缺失值、异常值要进行处理

    • 归一化处理:把数据集中数值压缩在一定范围,避免大值数据这种异常值的影响,可以消除单位因素产生的影响

    • 标准化处理:把数据集编程均值为x,方差为y的分布,机器学习提供的API满足均值为0,标准差为1的情况【默认情况】

    • 降维处理

  • 步骤(不一定非得全部使用,要针对不同的数据进行相应的处理)

    • 数据理解与预处理:首先需要理解数据集的特性,包括数据的分布、缺失值情况、异常值,去除重复记录、处理缺失值、纠正错误数据等

    • 特征生成:根据业务理解或领域知识创造新的特征、将非数值特征转换为数值形式

    • 特征选择:利用统计检验、相关性分析或其他特征选择技术来确定哪些特征对模型最为重要。这有助于减少维度,同时保持模型的有效性

    • 特征变换:对原始特征进行转换,比如使用对数变换来处理偏斜的数据分布、使用多项式特征扩展来增加模型的非线性能力

    • 特征编码:对于类别特征,需要进行编码处理,比如标签编码(label encoding)或独热编码(one-hot encoding)

      • 标签编码是一种简单的编码方式,它将每个类别标签用唯一的整数表示。例如,如果有三个类别:'apple', 'banana', 'cherry',标签编码可能会将它们分别编码为 0, 1, 2。标签编码适用于有序分类变量(ordinal variables),即这些类别的顺序是有意义的,例如教育水平。然而,对于没有自然顺序的名义变量(nominal variables),直接使用标签编码可能会引入人为的顺序关系,这可能会影响模型的性能,因为模型可能会假定数值上较大的类别比数值上较小的类别更重要

      • 独热编码是一种将名义变量转换为数值型数据的方式,它创建了一个新的二进制列(即只有 0 或 1 的列)用于每一个可能的类别值。例如,对于上述的类别'apple', 'banana', 'cherry',独热编码会创建三列,每列对应一个类别,每一行中只有一个类别被标记为 1,其余为 0。这种方式避免了引入任何顺序上的意义,因此适用于没有内在顺序关系的类别变量

    • 特征缩放:确保所有特征都在相似的尺度上,即归一化、标准化

    • 降维:当特征数量过多时,可以采用降维技术,如 PCA(主成分分析)来减少特征数量,同时尽可能保留原始数据的信息

4.1 API

4.1.1 DictVectorizer
  • DictVectorizersklearn.feature_extraction 中用于特征提取和编码的重要工具之一,特别适用于处理字典格式的数据。它可以将字典列表转换为模型可直接使用的数值型特征矩阵,其中每一行代表一个样本,每一列表示一个特征

  • DictVectorizer实例化参数:

    参数默认值说明
    dtypenumpy.float64输出数组的数据类型
    separator"="当类别特征名称与值之间需要拼接时使用(默认不启用)
    sparseTrue如果为 True,输出是三元组的稀疏矩阵;如果为 False,输出是 Numpy 数组
    sortTrue是否按字母顺序排序特征名
4.1.1.1 稀疏矩阵
  • 在矩阵中,若数值为0的元素数目远多于非零元素的数目,且非0元素分布没规律时,则称该矩阵为稀疏矩阵,与之相反,非0元素数目占比大时,则称该矩阵为稠密矩阵

4.1.1.2 提取为稀疏矩阵
  • 使用DictVectorizer提取出来的稀疏矩阵,通过三元组表进行输出,三元组表是一种用于表示系数矩阵的方法,特别适用于COO格式,它包含三部分

组成部分名称内容描述
第一部分行索引(row indices)每个非零元素所在的行
第二部分列索引(column indices)每个非零元素所在的列
第三部分非零值(non-zero values)储非零元素的值
4.1.1.3 稀疏矩阵和稠密矩阵转换
  • 稀疏矩阵调用以下函数可以转为稠密矩阵

  • API:result.toarray()#转numpy matrix

    • result.todense()#转numpy matrix

方法名返回类型描述说明示例
todense()numpy.matrix将稀疏矩阵转换为 NumPy 的 matrix 类型sparse_matrix.todense()
toarray()numpy.ndarray将稀疏矩阵转换为 NumPy 的 ndarray 类型sparse_matrix.toarray()

字典提取的示例代码: fit_transform 方法重点解释

from sklearn.feature_extraction import DictVectorizer
​
"""
    DictVectorizer 不会处理数字,数字是多少处理后就是多少
    如果是字符串,就会处理,不同的字符串就会作为不同的特征
    每一个特征就是一列数据,这一列数据满足一个位置上数值为1【通常】,其余位置为0【one-hot】
"""
# 定义一个数据集
data = [
    {"name": "张三", "address": "成都", "age": 18},
    {"name": "lisi", "address": "重庆", "age": 20},
    {"name": "王五", "address": "贵州", "age": 30},
]
# 创建字典特征提取的对象
dv = DictVectorizer(sparse=True)
"""
    fit_transform 方法等价于先执行 fit 方法、然后执行 transform 方法
    fit 方法的作用:
        在特征工程阶段:
            比如 minMaxScaler(最小值最大值归一化),fit 方法就会从数据集中学习到数据集中的最大值、最小值
                StandardScaler(标准化),fit 方法就会从数据集中学习数据集中的均值和标准差等信息
        模型阶段:后期使用机器学习里面的模型---创建出来的对象叫做估计器
            训练模型 --- 投喂
    transform 方法的作用:【一定在 fit 方法之后执行】
        使用 fit 方法的结果去处理数据
    问题:我们训练模型的时候,是需要把所有数据集的信息都执行fit方法吗?分开执行fit方法?只需要部分数据集执行fit方法?
        不是把所有数据集都去做 fit 处理
        训练模型的时候,只能够提供训练集里面的信息给fit,去得到训练集的信息,来训练模型
        而不可以把测试集的信息给到fit,否则会导致测试集中的数据集已经被处理了,模型后期做验证的时候就会存在误差
        
        训练数据集 fit 方法之后,还需要对测试集进行 fit?不需要,测试集就直接使用训练集中的信息(最大值、最小值。。。)
"""
result = dv.fit_transform(data)
# dv.fit(data)
# result = dv.transform(data)
print(result)
# 查看特征名字 --- 有警告
# print(dv.get_feature_names())
print(dv.get_feature_names_out())
"""
    稀疏矩阵转为numpy里面的数组、matrix
"""
print(type(result))  # <class 'scipy.sparse._csr.csr_matrix'>
result1 = result.toarray()  # numpy ndarray
print(result1)
print(type(result1))  # <class 'numpy.ndarray'>
result2 = result.todense()  # numpy matrix
print(result2)
print(type(result2))  # <class 'numpy.matrix'>
​
4.2 CountVectorizer
  • CountVectorizer是一个在自然语言处理(NLP)任务中常用的工具,用于将文本数据转换为向量形式。它属于 sklearn.feature_extraction.text 模块的一部分,是Scikit-learn库提供的功能之一。CountVectorizer主要通过以下方式工作:

    • 词汇表构建:首先根据训练集中的文档创建一个固定的词汇表(即所有唯一词的集合)。每个词在词汇表中都有一个对应的索引位置

    • 词频统计:对于每个文档,CountVectorizer 会计算出词汇表中每个词出现的次数,并忽略那些未出现在文档中的词

    • 输出格式:最终的输出是一个稀疏矩阵,每一行对应一个文档,每一列对应词汇表中的一个词。矩阵中的每个元素表示某个词在某个文档中出现的次数

  • CountVectorizer默认会忽略一些英文中的停用词(如“is”、“the”等),并且会对单词进行小写化处理。这些行为可以通过传递参数来调整,例如设置 stop_words='english' 可以显式地过滤英语停用词,而 lowercase=False 则可以禁用自动小写化处理

  • CountVectorizer 实例化参数:

参数名类型默认值描述说明
stop_wordsstring 或 listNone指定要忽略的停用词集合:<br> - 若设为 'english',则会使用内建的英语停用词列表(如 "is", "the" 等)<br> - 也可以传入一个自定义的停用词列表<br> - 单个字母(如 "i""a")通常会被自动忽略
lowercaseboolTrue是否在分词前将文本统一转换为小写:<br> - 设为 True:自动转换为小写(推荐用于英文文本)<br> - 设为 False:保留原始大小写格式
4.2.1提取英文文本
  • 提取英文文本不需要像中文文本一样需要进行分词

"""
    统计词语在各自的文档中出现的次数
"""
# 导入类
from sklearn.feature_extraction.text import CountVectorizer
​
​
def english_cv():
    # 准备数据
    data = [
        'This is the first document.',
        'This document is the second document.',
        'And this is the third one.',
        'Is this the first document?',
        'this is an apple.'
    ]
​
    # 创建对象
    """
        英文:
            stop_words:哪些词被忽略掉,不参与最终的词表统计,列表
            lowercase:默认情况下会把所有的词转为小写在分词
    """
    cv = CountVectorizer(stop_words=["is"])
    # fit_transform
    result = cv.fit_transform(data)
    print(result.toarray())
    print(cv.get_feature_names_out())
if __name__ == '__main__':
     english_cv()
4.2.2 提取中文文本
  • 分词Jieba 是一个广泛使用的中文分词库,支持三种分词模式:精确模式全模式搜索引擎模式。它还支持添加自定义词典、关键词提取、词性标注等功能。Jieba 的优点在于其高效性和易用性,适用于大多数中文文本处理场景

    • 这里先掌握 Jieba 分词的基本上使用方式,在后面大模型课程中还会学习 Jieba 分词

  • 主要功能

    • 分词:将连续的中文文本切分成单独的词语

    • 关键词提取:基于 TF-IDF 算法从文本中抽取关键词

    • 词性标注:标记每个词的词性(如名词、动词等)

    • 并行分词:利用多核处理器加速分词过程

    • 自定义词典:允许用户根据需要添加或删除特定词汇

  • jieba分词的三种模式

  • 精确默认:是默认的分词模式,会将句子精确切分,适合文本分析

  • 全模式:扫描出所有可能的词,不能消除歧义,速度快但结果冗余

  • 搜索引擎模式:适用于搜索引擎,在精确模式基础上,对长词再进行细粒度拆分

4.2.2.1 基础分词
  • 案例代码:

import jieba
​
text = "南京市长江大桥"
# 精确模式,默认模式
result = jieba.lcut(text)
print("精确模式:", result)
​
# 全模式
result_full = jieba.lcut(text, cut_all=True)
print("全模式:", result_full)
​
# 搜索引擎模式
result_search = jieba.lcut_for_search(text)
print("搜索引擎模式:", result_search)
  • 运行结果:

精确模式: ['南京市', '长江大桥']
全模式: ['南京', '南京市', '京市', '市长', '长江', '长江大桥', '大桥']
搜索引擎模式: ['南京', '京市', '南京市', '长江', '大桥', '长江大桥']
4.2.2.2 添加词典
  • 添加一个词典并且添加词语(一个词占一行、每一行分三部分:词语、词频(可省略)、词性(可省略)、用空格隔开,顺序不可颠倒)

严寒 500 n
深冬 400 n
阴晦 300 a
冷风 200 n
呜呜 100 m
萧索 250 a
荒村 180 n
活气 160 n
悲凉 220 a
二十余年 350 num
  • 案例代码:

import jieba.analyse
​
# 加载自定义词典
jieba.load_userdict("hometown.txt")
​
# 原始文本:鲁迅《故乡》节选
text = """我冒了严寒,回到相隔二千余里,别了二十余年的故乡去。
时候既然是深冬;渐近故乡时,天气又阴晦了,冷风吹进船舱中,呜呜的响,
从篷隙向外望,苍黄的天底下,远近横着几个萧索的荒村,没有一些活气。
我的心禁不住悲凉起来了。"""
​
# 1. 精确模式分词(默认模式)
seg_list = jieba.lcut(text)
print("【精确模式】")
print("/".join(seg_list))
​
# 2. 全模式(所有可能的词语组合)
seg_list_full = jieba.lcut(text, cut_all=True)
print("\n【全模式】")
print("/".join(seg_list_full))
​
# 3. 搜索引擎模式(适用于搜索引擎输入提示)
seg_list_search = jieba.lcut_for_search(text)
print("\n【搜索引擎模式】")
print("/".join(seg_list_search))
  • 运行结果:

【精确模式】
我/冒/了/严寒/,/回到/相隔/二千余/里/,/别/了/二十余年/的/故乡/去/。/
/时候/既然/是/深冬/;/渐近/故乡/时/,/天气/又/阴晦/了/,/冷风吹/进/船舱/中/,/呜呜/的/响/,/
/从篷隙/向/外望/,/苍黄/的/天底下/,/远近/横着/几个/萧索/的/荒村/,/没有/一些/活气/。/
/我/的/心/禁不住/悲凉/起来/了/。
​
【全模式】
我/冒/了/严寒/,/回到/相隔/二千/二千余/千余/千余里/余里/,/别/了/二十/二十余/二十余年/十余/十余年/余年/的/故乡/去/。/
//时候/既然/是/深冬/;/渐近/故乡/时/,/天气/又/阴晦/了/,/冷风/冷风吹/风吹/吹进/船舱/中/,/呜呜/的/响/,/
//从/篷/隙/向/外/望/,/苍黄/的/天底/天底下/底下/,/远近/横/着/几个/萧索/的/荒村/,/没有/一些/活气/。/
//我/的/心/禁不住/不住/悲凉/起来/了/。
​
【搜索引擎模式】
我/冒/了/严寒/,/回到/相隔/二千/千余/二千余/里/,/别/了/二十/十余/余年/二十余/十余年/二十余年/的/故乡/去/。/
/时候/既然/是/深冬/;/渐近/故乡/时/,/天气/又/阴晦/了/,/冷风/风吹/冷风吹/进/船舱/中/,/呜呜/的/响/,/
/从篷隙/向/外望/,/苍黄/的/天底/底下/天底下/,/远近/横着/几个/萧索/的/荒村/,/没有/一些/活气/。/
/我/的/心/不住/禁不住/悲凉/起来/了/。
4.2.2.3 词性标注
  • Jieba 可以结合 posseg 模块进行词性标注

  • 常见的词性

标签含义标签含义标签含义标签含义
n普通名词f方位名词s处所名词t时间
nr人名ns地名nt机构名nw作品名
nz其他专名v普通动词vd动副词vn名动词
a形容词ad副形词an名形词d副词
m数量词q量词r代词p介词
c连词u助词xc其他虚词w标点符号
PER人名LOC地名ORG机构名TIME时间
  • 案例代码:

import jieba.posseg as pseg
​
words = pseg.cut("我爱北京天安门")
for word, flag in words:
    print(f'{word} {flag}')
  • 运行结果:

我 r
爱 v
北京 ns
天安门 ns
4.2.2.3 停用词
  • 如果你想去掉无意义的词(如的、是),可以使用停用词表

  • 案例代码:

import jieba
​
text = "你和我是好朋友的吧"
stopwords = set(["的", "是", "和"])
words = jieba.lcut(text)
print("未使用停用词:", words)
words1 = [w for w in jieba.cut(text) if w not in stopwords]
print("使用停用词:", words1)
  • 运行结果:

未使用停用词: ['你', '和', '我', '是', '好', '朋友', '的', '吧']
使用停用词: ['你', '我', '好', '朋友', '吧']
​
4.2.2.4 综合案例
# 中文分词,使用 jieba 工具
"""
    jieba 工具的使用:
        1、安装 jieba 库
        2、jieba 分词介绍:他是一个用来做中文分词的库,基于规则进行分词的,这个规则它自己设置了的,也可以引入自己的规则
            这个规则的官方名字叫做词典
        3、jieba 分词有三种分词方案:
            1.默认模式【精确模式】:一般选择这种方式,按照词典对文本进行分词
            2.全模式:更加细粒度的分词,分词的结果会更多一点
            3.搜索引擎模型:就在精确模式的基础之上,把长一点词汇进一步分词
        4、分词的 api:lcut、lcut_for_search
"""
# 导入jieba分词
"""
    完成一个分词工具方法,能够把列表文本中的分词结果统计出来(词不可以重复),然后让countVectorizer统计次数
"""
import jieba
​
​
def chinese_cv():
    # data = [
    #     '好好学习,天天向上,今天天气很不错哟,今天只有半天课',
    #     '好好学习,天天向上,今天天气很不错哟,今天只有半天课'
    # ]
    data = "在北京天安门看升国旗好开心呀"
    # 默认模式
    result1 = jieba.lcut(data)
    print(result1)
    # 统计次数
    cv = CountVectorizer(stop_words=['呀'])
    result = cv.fit_transform(result1)
    print(result.toarray())
    print(cv.get_feature_names_out())
    # 全模式 cut_all 参数设置为 True
    # result2 = jieba.lcut(data, cut_all=True)
    # print(result2)
    # 搜索引擎模式
    # result3 = jieba.lcut_for_search(data)
    # print(result3)
​
​
# 引入自己的词典---有的时候这个词典的内容可能不会再jieba分词中生效,为什么?
"""
    设置的词语的词频可能没有默认词典中的词语的词频高,那么就把词频放大
"""
"""
    如果说要引入自己的分词规则,就需要自己的定义词典,然后引入到项目中
    定义词典的步骤:
        1、创建一个txt文件
        2、在文件中,每一行就是一个词,一共有三个数据词 词频[option] 词性[option]
        3、加载自定义的词典,使用方法load_userdict实现,参数就是词典的访问路径
"""
​
​
def self_cv():
    # 加载用户自定义的词典
    jieba.load_userdict("./my_dict.txt")
    data = """我冒了严寒,回到相隔二千余里,别了二十余年的故乡去。
    时候既然是深冬;渐近故乡时,天气又阴晦了,冷风吹进船舱中,呜呜的响,
    从篷隙向外望,苍黄的天底下,远近横着几个萧索的荒村,没有一些活气。
    我的心禁不住悲凉起来了。"""
    result = jieba.lcut(data)
    print("/".join(result))
​
​
# 获取词典中分词过后的词语的内容和词性
"""
    需要使用到jieba库中的词性方法
"""
import jieba.posseg as pos
​
​
def posseg_cv():
    # 准备文本
    data = "教育学会会长期间坚定支持民办教育事业!"
    # 进行分词
    result = pos.lcut(data)
    """
        item:词
        a:词性
    """
    for item, a in result:
        print(item, a)
​
​
if __name__ == '__main__':
    chinese_cv()
    #self_cv()
   # posseg_cv()
​
4.3 TF-IDF
  • TF-IDF(Term Frequency-Inverse Document Frequency,词频-逆文档频率)是一种常用的文本特征表示方法,用于评估一个词对一个文档或语料库中的重要程度。如:

    • 如果一个词或短语在一篇文章中出现的概率高,但在其他文章中出现的概率少,则任务此词或者短语有很好的类别区分能力,适合用来分类

  • 词频(Frequency):一个词在语料库中出现的次数,词频越高,说明该词在该文档中的相对重要性

  • 词频概率:通常指的是归一化后的词频。

    • 词在文档中出现的次数除以文档总词数,用来衡量这个词在整个文档中的相对重要性:

    词在文档中出现的次数文档中词的总数

      • 例如,如果词 cat 在一篇包含 100 个词的文章中出现了 5 次,那么它的词频概率为:

  • 逆文档频率:词语在整个文档集合中的重要程度

  • TF-IDF稀有文本特征提取将CountVectorizer和TfidTransformer的所有功能组合在一个模型中

    • TfidTransformer将词频/字符频数矩阵转换为标准化的tf或tf-idf矩阵。

  • TF-IDF是两个指标的乘积:词频(TF)和逆文档频率(IDF),两个部分的计算公式如下:

    • 逆文档频率(IDF):出现频率较低的词语会有较高的 IDF 值,从而提高其在文档中的权重,这里加 1 是为了防止除数为零的情况,并且确保 IDF 不会为负数

文档总数包含该词语的文件的数目数

  • TF-IDF:通过 TF-IDF 评分,重要的词语(即在该文档中频繁出现而在其他文档中不常见的词语)会得到较高的权重

  • TfidfVectorizer 是 scikit-learn 提供的一个工具,用于将文本数据转换为 TF-IDF 特征矩阵,函数介绍如下:

参数名类型默认值描述说明
stop_wordsstring 或 listNone指定要忽略的停用词集合:<br> - 若设为 'english',则会使用内建的英语停用词(如 "is", "the" 等)<br> - 也可以传入一个自定义的停用词列表<br> - 单个字母(如 "i""a")通常会被自动忽略
use_idfboolTrue是否启用 IDF(逆文档频率)加权:<br> - 设为 True:使用 TF-IDF 权重计算<br> - 设为 False:仅使用 TF(词频)并归一化
smooth_idfboolTrue是否对 IDF 进行平滑处理:<br> - 加1平滑,防止除以0的情况,使计算更稳定
normstring'l2'归一化方式:<br> - 'l1':L1 范数归一化(所有特征绝对值之和为1)<br> - 'l2':L2 范数归一化(默认,欧氏距离归一化)
  • 案例代码:

from sklearn.feature_extraction.text import TfidfVectorizer
​
data = [
    'This is the first document',
    'This is the second document',
    'And the third one',
    'Is this the first document',
]
tfidf = TfidfVectorizer()
X = tfidf.fit_transform(data)
print(X.toarray())
print(tfidf.get_feature_names_out())
  • 运行结果:

[[0.         0.43877674 0.54197657 0.43877674 0.         0.
  0.35872874 0.         0.43877674]
 [0.         0.40412895 0.         0.40412895 0.         0.63314609
  0.33040189 0.         0.40412895]
 [0.55280532 0.         0.         0.         0.55280532 0.
  0.28847675 0.55280532 0.        ]
 [0.         0.43877674 0.54197657 0.43877674 0.         0.
  0.35872874 0.         0.43877674]]
['and' 'document' 'first' 'is' 'one' 'second' 'the' 'third' 'this']
  • 运行结果计算过程解析:

    • 第一步:得到所有唯一词语按字典序排列

    ['and' 'document' 'first' 'is' 'one' 'second' 'the' 'third' 'this']
    ​
    • 第二步【这个步骤之后的每个步骤都以文档一为例】:计算词频 TF,得到向量表示形式:[0, 1, 1, 1, 0, 0, 1, 0, 1]

    出现次数
    and0
    document1
    first1
    is1
    one0
    second0
    the1
    third0
    this1
    • 第三步:计算逆文档频率 IDF

    出现在哪些文档出现文档数IDF 计算IDF 值(保留4位小数)
    andd31log(4+1/(1+1)) + 1 ≈ log(5/2) + 1ln(2.5) + 1 ≈ 1.9163
    documentd1, d2, d43log(4+1/(3+1)) + 1 = log(5/4) + 1ln(1.25) + 1 ≈ 1.2231
    firstd1, d42log(4+1/(2+1)) + 1 ≈ log(5/3) + 1ln(1.6667) + 1 ≈ 1.5108
    isd1, d2, d43log(4+1/(3+1)) + 1 ≈ log(5/4) + 1ln(1.25) + 1 ≈ 1.2231
    oned31log(4+1/(1+1)) + 1 ≈ log(5/2) + 1ln(2.5) + 1 ≈ 1.9163
    secondd21log(4+1/(1+1)) + 1 ≈ log(5/2) + 1ln(2.5) + 1 ≈ 1.9163
    thed1, d2, d3, d44log(4+1/(4+1)) + 1 ≈ log(5/5) + 1ln(1.0) + 1 = 1.0000
    thirdd31log(4+1/(1+1)) + 1 ≈ log(5/2) + 1ln(2.5) + 1 ≈ 1.9163
    thisd1, d2, d43log(4+1/(3+1)) + 1 ≈ log(5/4) + 1ln(1.25) + 1 ≈ 1.2231
    • 第四步:计算 TF-IDF

    TFIDFTF-IDF(= TF × IDF)
    and01.91630
    document11.22311.2231
    first11.51081.5108
    is11.22311.2231
    one01.91630
    second01.91630
    the11.00001.0000
    third01.91630
    this11.22311.2231
    • 第五步:L2 归一化(默认 norm='l2')

分母

TF-IDF归一化后值(保留4位小数)
and0.00000.0000
document1.22310.4388
first1.51080.5420
is1.22310.4388
one0.00000.0000
second0.00000.0000
the1.00000.3587
third0.00000.0000
this1.22310.4388
4.3.1 练习
  • 手写代码实现 TF-IDF

  • 参考代码:

import numpy as np
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.preprocessing import normalize
​
data = [
    'This is the first document',
    'This is the second document',
    'And the third one',
    'Is this the first document',
]
​
vectorizer = CountVectorizer()
x = vectorizer.fit_transform(data)
tf = x.toarray()
print("词频矩阵:\n", tf)
fenzi = len(data) + 1
print("分子:\n", fenzi)
fenmu = np.sum(tf!=0, axis=0) + 1
print("分母:\n", fenmu)
idf = np.log(fenzi / fenmu) + 1
print("idf:\n", idf)
tf_idf = tf * idf
print("tfidf:\n", tf_idf)
tf_idf = normalize(tf_idf, norm="l2", axis=1)
print("归一化tf_idf:\n", tf_idf)

作业 2025.8.08

  • 完成jieba分词任务,基于data = ["教育学会会长期间坚定支持民办教育事业!", "热忱关心、扶持民办学校发展", "事业做出重大贡献!"]完成下图输出

解答程序:

import jieba
import pandas as pd
from sklearn.feature_extraction.text import CountVectorizer
def chinese_cv():
    #创建数据
    data = ["教育学会会长期间坚定支持民办教育事业!",
            "热忱关心、扶持民办学校发展",
            "事业做出重大贡献!"]
    #仅是显示划分结果
    result1=[]
    for s in data:
        r=jieba.lcut(s)
        result1.append(" ".join(r))
    print(result1)
    #自定义分词器
    def chinese_tokenizer(text):
        return jieba.lcut(text)
    #创建对象
    cv1 = CountVectorizer(tokenizer=chinese_tokenizer,stop_words={'!', '、'})#使用自定义分词器
    result1_vector = cv1.fit_transform(data)  # 直接使用原始data
    print(result1_vector.toarray())
    #查看特征名字
    print(cv1.get_feature_names_out())
    col=cv1.get_feature_names_out()
    #将对应列名与矩阵内容对应起来,形成数据帧
    df=pd.DataFrame(result1_vector.toarray(),columns=col)
    print(df.to_string(index=False))#使用to_string方法不显示索引
if __name__ =="__main__":
    chinese_cv()

运行结果:

  • 手写代码实现TF-IDF

    • 准备数据集

    • 分词,英文不需要

    • 使用countVectorizer统计词语的次数 --- 得到 TF

    • 利用数学公式log(....)+1计算IDF,IDF使用l2正则化

    • TF*IDF

    • 在调用api,查看自己是否计算正确

    程序:

    def two():
        data2 = ['This is the first document.',
                 'This document is the second document.',
                 'And this is the third one.',
                 'Is this the first document?',
                 'this is an apple.']
    ​
        cv2 = CountVectorizer(stop_words={'an', 'and', 'the'})
        tf = cv2.fit_transform(data2)
        print("===================")
        print(tf.toarray())
        tf_array = tf.toarray()
        tf_transposed = tf_array.T
    ​
        doc_frequency = []
        # 计算包含该词的文档数量
        for items in tf_transposed:
            non_zero_docs = sum(1 for item in items if item > 0)
            """
            注意non_zero_docs是要计算每个词出现的文档次数,
            不是每个词出现的总次数
            """
            doc_frequency.append(non_zero_docs)
    ​
        print("文档频率:", doc_frequency)
        print("特征词:", cv2.get_feature_names_out())
    ​
        # 使用与sklearn TfidfVectorizer相同的IDF公式
        N = len(data2)
        # sklearn的默认IDF公式: log((N + 1) / (df + 1)) + 1
        idf = [math.log((N + 1) / (df + 1)) + 1 for df in doc_frequency]
    ​
        print("IDF值:", idf)
    ​
        # 计算TF-IDF
        idf_array = np.array(idf)
        tf_idf = tf_array * idf_array
        # print("TF-IDF矩阵:")
        # print(tf_idf)
    ​
        #正则化,需要进行广播
    tf_idf_normalizer=tf_idf/np.sqrt(np.sum(tf_idf**2,axis=1,keepdims=True))
        print("正则化后的TF-IDF矩阵:",tf_idf_normalizer)
  # 如果要与TfidfVectorizer(norm=None)完全一致,还需要进行L2正则化
  # 但既然要比较非正则化结果,就不需要这一步


def three():

def two():
    data2 = ['This is the first document.',
             'This document is the second document.',
             'And this is the third one.',
             'Is this the first document?',
             'this is an apple.']
​
    cv2 = CountVectorizer(stop_words={'an', 'and', 'the'})
    tf = cv2.fit_transform(data2)
    print("===================")
    print(tf.toarray())
    tf_array = tf.toarray()
    tf_transposed = tf_array.T
​
    doc_frequency = []
    # 计算包含该词的文档数量
    for items in tf_transposed:
        non_zero_docs = sum(1 for item in items if item > 0)
        """
        注意non_zero_docs是要计算每个词出现的文档次数,
        不是每个词出现的总次数
        """
        doc_frequency.append(non_zero_docs)
​
    print("文档频率:", doc_frequency)
    print("特征词:", cv2.get_feature_names_out())
​
    # 使用与sklearn TfidfVectorizer相同的IDF公式
    N = len(data2)
    # sklearn的默认IDF公式: log((N + 1) / (df + 1)) + 1
    idf = [math.log((N + 1) / (df + 1)) + 1 for df in doc_frequency]
​
    print("IDF值:", idf)
​
    # 计算TF-IDF
    idf_array = np.array(idf)
    tf_idf = tf_array * idf_array
    # print("TF-IDF矩阵:")
    # print(tf_idf)
​
    #正则化
    tf_idf_normalizer=tf_idf/np.sqrt(np.sum(tf_idf**2,axis=1,keepdims=True))
    print("正则化后的TF-IDF矩阵:",tf_idf_normalizer)
​

结果对比:

TfidVectorizer:

结果对比没出错,在过程中容易把公式输入的小细节弄错。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值