欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

文本特征提取之词袋模型

程序员文章站 2024-02-02 11:25:34
...

跟我一起机器学习系列文章将首发于公众号:月来客栈,欢迎文末扫码关注!

1 引例

在前面几讲的示例介绍中,我们所用到的数据集都是别人已经处理好的数据集,换句话说这些数据集的每个特征维度都已经是数值了。但是在实际的建模任务中,我们拿到的数据集并不是这样的形式。例如接下来我们要完成的一个任务:对中文垃圾邮件进行分类。

为什么两个人在一起会有爱情呢? 爱真的需要勇气 把自己的内心毫无保留的展示给对方 也勇敢的去 接受对方的点点滴滴,过去的事情虽然不能再改变什么,但是 看了你的贴子,你说你不需要建议. 我想问你个问题,如果你愿意的话可以试着回答一下,你认为真爱是什么啊??? 你这辈子会对一个女孩死心蹋地吗??? 如果下次你再遇到一个比现在这个女孩还"特别的"你会怎么样, 因为你之所以选择后者,主要是因为她很与众不同吧!所以我用特别这个词来说.

例如,对于上面这样一个邮件(样本),我们应该怎么来对其进行量化呢?同时,我们知道在建模过程中需要保证每个样本的特征维度数都一样,但是这里每一封邮件的长度却并不同,那我们该怎么处理呢?最后,这里的每个样本都是由若干个句子组成,我们是否应该在一定程度上对这些句子进行分割呢?接下来,我们就来开始学习第一种向量化方法——词袋模型。

2 词袋模型

什么是词袋模型(Bag of words) 呢?其实词袋模型这个叫法很形象,凸出了模型的核心思想。所谓词袋模型就是:首先将训练样本中所有不重复的词放到这个袋子中构成一个词表(字典);然后再以这个词表为标准来遍历每一个样本,如果词表中对应位置的词出现在了样本中,那么对应位置就用1来表示,没有出现就用0来表示;最后,对于每个样本来说都将其向量化成了一个和词表长度一样的0-1向量。

2.1 具体步骤

文本特征提取之词袋模型

如图所示为一个直观的词袋模型转换示意图,左边为原始数据集(包含两个样本),中间为词表,右边为向量化的结果。具体为,第一步:首先需要将原始数据的每个样本都进行分词处理(英文语料可以跳过这步);第二步:然后在所有的分词结果中去掉重复的部分,保证每个词语只出现一次;第三步:遍历每个数据样本,若词表中的词出现在该样本中,则对应位置为1,没出现则为0 。例如图中样本“没有 你 的 地方 都是 他乡”,其中有6个词都出现在了词表中,所以词表中每个词的对应位置为1,而‘旅行’和‘流浪’这个两个词并没有出现在样本中,所以对应位置为0。

可以看出,向量化后每个样本特征维度的长度都和词表长度相同(图示中为8),而这很容易导致维度灾难。因为通常一个一般大小的中文数据集,都可能会出现数十万个词语(而这意味着转化后向量的维度也有这么大)。所以在实际处理的过程中,我们在分词结束后通常还会进行词频统计这一步,即统计每个词在数据集中出现的次数,然后只选择其中出现频率最高的前KK个词作为最终的词表。最后,通常也会将一些无意义的虚词(停用词)去掉,例如:“的,啊,了,”等。

2.2 分词

从上面的介绍可以知道,向量化的第一步是需要对文本进行分词。下面我们开始介绍一款常用的开源分词工具jieba。使用jieba库的前提当然是安装了,可以通过命令pip install jieba进行安装,然后再通过内置的方法进行分词即可。

我们先用一段文本来进行分词并做词频统计:

央视网消息:当地时间11日,美国国会参议院以88票对11票的结果通过了一项动议,允许国会“在总统以国家安全为由决定征收关税时”发挥一定的限制作用。这项动议主要针对加征钢铝关税的232调查,目前尚不具有约束力。动议的主要发起者——共和党参*鲍勃·科克说,11日的投票只是一小步,他会继续推动进行有约束力的投票。

可以看到,这段文本当中还包含了很多标点符号和数字,显然这不是我们想要的,所以在分词的时候要去掉这些。

  • 普通分词模式

    import jieba
    import re
    def cutWords(s):
        cut_words = []
        s = re.sub("[A-Za-z0-9\:\·\—\,\。\“ \”]", "", s)
        seg_list = jieba.cut(s, cut_all=True)
        cut_words.append(" ".join(seg_list))
        print(cut_words)
    
    >> 结果
    
    ['央视网 消息 当地 时间 日 美国国会参议院 以票 对票 的 结果 通过 了 
    一项 动议 允许 国会 在 总统 以 国家 安全 为 由 决定 征收 关税 时 发挥 
    一定 的 限制 作用 这项 动议 主要 针对 加征 钢铝 关税 的 调查 目前 尚 
    不 具有 约束力 动议 的 主要 发起者 共和党 参* 鲍勃 科克 说 日 的 
    投票 只是 一 小步 他会 继续 推动 进行 有 约束力 的 投票']
    

    最后输出的结果便是分词后的形式。但是,对于有的句子来说可以有不同的分词方法,例如:“美国国会参议院”既可以分成“美国 国会 参议院”,也可以是“美国国会 参议院”,甚至都可以直接是“美国国会参议院”。因此,jieba还提供了一种全分词模式。

  • 全分词模式

    seg_list = jieba.cut(s, cut_all=True)
    print(cut_words)
    
    >> 结果
    ['央视 央视网 视网 消息 当地 时间 日 美国 美国国会 美国国会参议院 国会 参议 
    参议院 议院 以 票 对 票 的 结果 通过 了 一项 动议 允许 许国 国会 在 总统 以
     国家 家安 安全 为 由 决定 征收 关税 时 发挥 一定 的 限制 制作 作用 这项 
    动议 的 主要 发起 发起者 共和 共和党 党参 参议 参* * 鲍 勃 科克 说 
    日 的 投票 只是 一小 小步 他 会 继续 推动 进行 有 约束 约束力 的 投票']
    

    可以看出对于有的句子,分词后的结果确实看起来结结巴巴的,而这就是全分词模式的作用。

2.3 词频统计

上面介绍到,分词后通常还会进行词频统计,以选取出现频率最高的前KK个词来作为词表。做词频统计需要用到另外一个包collection中的Counter计数器(如果没有自行安装,安装方法同jieba一样)。但是需要注意的是,像上面那样分词后的形式不能做词频统计,因为Counter是将list中的一个元素视为一个词,所以在上面要略微修改。

def wordsCount(s):
    cut_words = ""
    s = re.sub("[A-Za-z0-9\:\·\—\,\。\“ \”]", "", s)
    seg_list = jieba.cut(s, cut_all=False)
    cut_words += (" ".join(seg_list))
    all_words = cut_words.split()
    c = Counter()
    for x in all_words:
        if len(x) > 1 and x != '\r\n':
            c[x] += 1
    vocab = []
    print('\n词频统计结果:')
    for (k, v) in c.most_common(5):  # 输出词频最高的前5个词
        print("%s:%d" % (k, v))
        vocab.append(k)
    print(vocab)
#结果
词频统计结果:
动议:3
关税:2
主要:2
约束力:2
投票:2
['动议', '关税', '主要', '约束力', '投票']    

2.4 向量化

通过上面的操作,我们便能得到一个最终的词表vocab。最后一步的向量化工作则是遍历每个样本,查看词表中每个词是否出现在当前样本中,如果出现则词表对应维度用1表示,没出现则用0表示。

def vetorization(s):
    ......
    x_vec = []
    for item in x_text:
        tmp = [0]*len(vocab)
        for i,w in enumerate(vocab):
            if w in item:
                tmp[i] = 1
        x_vec.append(tmp)
    print("词表:",vocab)
    print("文本:",x_text)
    print(x_vec)
    
if __name__ == '__main__':
    s = ['分词工具可用于对文本进行分词处理', '常见的用于文本的分词处理工具有很多']
    vetorization(s)
    
#结果
词表: ['分词', '工具', '用于', '文本', '处理', '进行', '常见']
文本: [['分词', '工具', '可', '用于', '对', '文本', '进行', '分词', '处理'], 
     ['常见', '的', '用于', '文本', '的', '分词', '处理', '工具', '有', '很多']]
[[1, 1, 1, 1, 1, 1, 0], [1, 1, 1, 1, 1, 0, 1]]

由于文章篇幅所限,此处只列出了部分代码,具体实现代码可参见示例代码。

3 总结

在本篇文章中,笔者首先介绍了第一种将文本转化为向量的词袋模型;然后介绍了一款常用的分词工具jieba库并演示了如何同jieba进行分词处理以及词频统计;最后介绍了如何实现词袋模型的最后一步向量化。在下一篇文章中,我们将通过一个真实的垃圾邮件分类案例来进行介绍。本次内容就到此结束,感谢阅读!

若有任何疑问与见解,请发邮件至aaa@qq.com并附上文章链接,青山不改,绿水长流,月来客栈

引用

[1] Anaconda环境配置

[2] https://github.com/fxsjy/jieba

[3] 示例代码:关注公众号回复“示例代码”即可直接获取!

交流群:883638095

文本特征提取之词袋模型