天天看点

文本特征提取之词袋模型

1 引例

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

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

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

2 词袋模型

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

2.1 具体步骤

文本特征提取之词袋模型

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

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

K

K

K个词作为最终的词表。最后,通常也会将一些无意义的虚词(停用词)去掉,例如:“的,啊,了,”等。

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 词频统计

上面介绍到,分词后通常还会进行词频统计,以选取出现频率最高的前

K

K

K个词来作为词表。做词频统计需要用到另外一个包​

​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​

​进行分词处理以及词频统计;最后介绍了如何实现词袋模型的最后一步向量化。在下一篇文章中,我们将通过一个真实的垃圾邮件分类案例来进行介绍。本次内容就到此结束,感谢阅读!

引用

[1] Anaconda环境配置

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

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

交流群:883638095