自定义Bag-of-Words实现:处理带负号的词汇权重

聖光之護
发布: 2025-12-01 14:34:28
原创
1001人浏览过

自定义bag-of-words实现:处理带负号的词汇权重

本文详细介绍了如何针对包含特殊负号前缀词汇的文本数据,自定义实现一个Bag-of-Words(词袋模型)向量化器。传统词袋模型通常将带负号的词汇视为独立特征,或无法正确处理其语义。本教程通过Python代码演示了一种灵活的解决方案,它能识别词汇前的负号,并将其计数贡献为负值,从而在同一个特征维度上实现正负抵消,生成更符合特定业务逻辑的特征表示,尤其适用于科学术语或特定编码文本的分析。

1. 引言:传统Bag-of-Words模型的局限性

Bag-of-Words (BOW) 模型是自然语言处理中一种常用的文本表示方法,它将文本视为一个词语的集合,忽略词语的顺序和语法,只统计每个词语出现的频率。通过sklearn.feature_extraction.text.CountVectorizer等工具,我们可以方便地将文本文档转换为数值向量。

然而,在某些特定场景下,标准BOW模型可能无法满足所有需求。例如,在处理包含科学术语、编码或特定领域文本时,我们可能会遇到一些带有特殊前缀(如负号“-”)的词汇。如果一个词汇前面带有“-”,它可能表示该词汇的“否定”或“缺失”状态。在这种情况下,我们期望“Q207KL41”和“-Q207KL41”能够映射到同一个特征维度上,但“-Q207KL41”的出现应导致该特征的计数减少,而非增加一个独立的“-Q207KL41”特征。

标准CountVectorizer会将“Q207KL41”和“-Q207KL41”视为两个完全不同的词汇,并为它们创建两个独立的特征维度。这不符合我们希望将带负号词汇视为原始词汇的负面贡献的语义。因此,我们需要一种自定义的向量化方法来解决这一挑战。

2. 解决方案概述:自定义向量化器

为了实现对带负号词汇的特殊处理,最直接有效的方法是编写一个自定义的向量化函数。该函数将遍历每个文档,对其中的词汇进行解析,识别并处理前缀负号。具体步骤如下:

  1. 词汇解析与符号识别:对于文档中的每个词汇,检查其是否以负号开头。
  2. 符号分离与计数调整:如果词汇以负号开头,则将其视为原始词汇的负面实例,并为该词汇分配一个负的计数贡献(例如-1)。如果词汇没有负号,则分配一个正的计数贡献(例如+1)。
  3. 统一特征维度:无论词汇是否带负号,都将其映射到同一个基础词汇(去除负号后的词汇)的特征维度上。
  4. 构建特征矩阵:将每个文档处理后的词汇计数结果整合成一个DataFrame。

3. 自定义向量化器实现

下面是使用Python实现这一自定义Bag-of-Words向量化器的代码示例:

瞬映
瞬映

AI 快速创作数字人视频,一站式视频创作平台,让视频创作更简单。

瞬映 57
查看详情 瞬映
import io
import pandas as pd
import numpy as np
from collections import defaultdict

# 模拟输入数据
s = """
RepID,Txt
1,K9G3P9 4H477 -Q207KL41 98464 Q207KL41
2,D84T8X4 -D9W4S2 -D9W4S2 8E8E65 D9W4S2 
3,-05L8NJ38 K2DD949 0W28DZ48 207441 K2D28K84"""
df_reps = pd.read_csv(io.StringIO(s))

def custom_bow_vectorizer(documents):
    """
    自定义Bag-of-Words向量化器,处理带负号的词汇。

    参数:
        documents (pd.Series或list): 包含文本内容的序列或列表。

    返回:
        pd.DataFrame: 向量化后的特征矩阵。
    """

    # ret 用于存储每个文档的词汇计数结果
    ret = []

    # vocabulary 用于构建词汇表,将每个唯一的词汇映射到一个整数索引
    # defaultdict(vocabulary.__len__) 的作用是,当访问一个新词汇时,
    # 自动将其添加到词汇表并赋予一个新的索引。
    vocabulary = defaultdict()
    vocabulary.default_factory = vocabulary.__len__

    for document in documents:
        # feature_counter 存储当前文档中每个词汇的计数
        feature_counter = defaultdict(int)

        # 将文档分割成词汇
        for token in document.split():
            sign = 1  # 默认计数为正

            # 检查词汇是否以负号开头
            if token.startswith("-"):
                token = token[1:]  # 移除负号,得到基础词汇
                sign = -1          # 设置计数为负

            # 将基础词汇映射到其在词汇表中的索引
            # 如果是新词汇,vocabulary会自动为其分配一个新索引
            feature_idx = vocabulary[token]

            # 更新当前文档中该词汇的计数
            feature_counter[feature_idx] += sign

        # 将当前文档的词汇计数结果添加到ret列表
        ret.append(feature_counter)

    # 将列表中的字典转换为DataFrame
    # from_records 会自动将字典的键(feature_idx)作为列
    df = pd.DataFrame.from_records(ret)

    # 填充DataFrame中的NaN值(表示某些文档不包含某些词汇)为0
    df = df.fillna(0)

    # 将DataFrame的列名从索引(feature_idx)替换为实际的词汇名称
    # vocabulary.keys() 按照词汇被添加的顺序返回词汇
    df.columns = vocabulary.keys()

    # 将DataFrame的数据类型转换为int8,节省内存
    df = df.astype(np.int8)

    return df

# 使用自定义向量化器处理文本数据
result_df = custom_bow_vectorizer(df_reps["Txt"])
print(result_df)
登录后复制

4. 代码解析

  1. 导入必要的库: io 用于从字符串读取数据,pandas 用于数据处理,numpy 用于数值操作,collections.defaultdict 是实现自动构建词汇表的关键。
  2. custom_bow_vectorizer(documents) 函数:
    • ret = []: 这是一个列表,用于收集每个文档处理后的词汇计数结果。每个元素将是一个 defaultdict(int),存储该文档中每个词汇的计数。
    • vocabulary = defaultdict() 和 vocabulary.default_factory = vocabulary.__len__: 这是构建词汇表的核心。defaultdict 在访问一个不存在的键时,会调用 default_factory 来生成默认值。在这里,vocabulary.__len__ 会返回当前 vocabulary 中键的数量,从而为新词汇分配一个递增的整数索引。
    • 遍历文档: for document in documents: 循环处理输入序列中的每个文本字符串。
    • 初始化 feature_counter: feature_counter = defaultdict(int) 为当前文档创建一个新的字典,用于存储该文档中每个词汇的最终计数。
    • 词汇分割与符号处理:
      • for token in document.split(): 将当前文档按空格分割成独立的词汇。
      • sign = 1: 默认情况下,词汇的计数贡献为正1。
      • if token.startswith("-"):: 检查词汇是否以负号开头。
      • token = token[1:]: 如果是负号开头,则移除负号,得到词汇的“基础形式”。
      • sign = -1: 将计数贡献设置为负1。
      • feature_idx = vocabulary[token]: 获取词汇的基础形式在 vocabulary 中的索引。如果该词汇是第一次出现,vocabulary 会自动为其分配一个新的索引。
      • feature_counter[feature_idx] += sign: 将 sign(+1 或 -1)添加到该词汇在当前文档的计数中。
    • 收集结果: ret.append(feature_counter) 将当前文档的 feature_counter 添加到 ret 列表中。
    • 转换为DataFrame:
      • df = pd.DataFrame.from_records(ret): 将 ret 列表中的字典(每个字典代表一个文档的词汇计数)转换为一个Pandas DataFrame。from_records 会自动将字典的键(即 feature_idx)作为列名。
      • df = df.fillna(0): 由于不是所有文档都包含所有词汇,from_records 可能会生成 NaN 值,这里将其填充为0。
      • df.columns = vocabulary.keys(): 将 DataFrame 的列名从整数索引替换为实际的词汇字符串,提高可读性。
      • df = df.astype(np.int8): 将 DataFrame 的数据类型转换为 int8,以优化内存使用,因为计数通常不会超出 int8 的范围(-128到127)。

5. 运行示例与结果

使用上述代码运行模拟数据 df_reps["Txt"],将得到以下输出:

   K9G3P9  4H477  Q207KL41  98464  D84T8X4  D9W4S2  8E8E65  05L8NJ38  K2DD949  0W28DZ48  207441  K2D28K84
0       1      1         0      1        0       0       0         0        0         0       0         0
1       0      0         0      0        1      -1       1         0        0         0       0         0
2       0      0         0      0        0       0       0        -1        1         1       1         1
登录后复制

结果分析:

  • 文档1: K9G3P9 4H477 -Q207KL41 98464 Q207KL41
    • K9G3P9: 计数为 1
    • 4H477: 计数为 1
    • Q207KL41: -Q207KL41 贡献 -1,Q207KL41 贡献 +1,总和为 0。
    • 98464: 计数为 1
  • 文档2: D84T8X4 -D9W4S2 -D9W4S2 8E8E65 D9W4S2
    • D84T8X4: 计数为 1
    • D9W4S2: -D9W4S2 贡献 -1,-D9W4S2 贡献 -1,D9W4S2 贡献 +1,总和为 -1。
    • 8E8E65: 计数为 1
  • 文档3: -05L8NJ38 K2DD949 0W28DZ48 207441 K2D28K84
    • 05L8NJ38: -05L8NJ38 贡献 -1。
    • K2DD949: 计数为 1
    • 0W28DZ48: 计数为 1
    • 207441: 计数为 1
    • K2D28K84: 计数为 1

可以看到,输出结果完全符合预期,带负号的词汇被正确地处理为对相应基础词汇的负面贡献。

6. 注意事项与扩展

  1. 性能考量: 对于非常大的数据集,自定义的Python循环可能不如高度优化的C扩展库(如scikit-learn内部实现)高效。如果性能成为瓶颈,可能需要考虑更底层的优化或分批处理。
  2. 更复杂的负号规则: 本教程假设负号总是出现在词汇开头。如果负号可能出现在词汇中间,或者有其他表示“否定”的特殊字符,则需要修改 token.startswith("-") 和 token = token[1:] 的逻辑。
  3. 分词器(Tokenizer): 本示例使用简单的 document.split() 进行分词。在实际应用中,可能需要更复杂的正则表达式分词器或NLTK、spaCy等库提供的专业分词器来处理标点符号、数字、大小写转换等问题。
  4. N-gram支持: 当前实现只处理单个词汇(unigram)。如果需要支持二元词(bigram)或更长的N-gram,需要在 for token in document.split(): 循环之前或内部添加N-gram生成逻辑。
  5. 词汇表大小控制: 对于非常大的语料库,词汇表可能会变得非常庞大。可以考虑添加参数来限制词汇表的最大大小,例如只保留出现频率最高的K个词汇,或者设置最小/最大文档频率。

7. 总结

本教程展示了如何通过自定义Python函数,有效地解决标准Bag-of-Words模型在处理带有特殊负号前缀词汇时的局限性。通过识别并调整这些词汇的计数贡献,我们能够生成更符合特定语义需求的特征表示。这种方法在处理特定领域的文本数据,尤其是需要精细控制词汇权重的场景中,具有重要的实用价值。虽然自定义实现需要更多代码,但它提供了无与伦比的灵活性,能够适应各种独特的文本处理需求。

以上就是自定义Bag-of-Words实现:处理带负号的词汇权重的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号