文本特征提取的方法目前已经有很多种,传统的提取方法有平权统计、TF-IDF等,神经网络的方法有word2vec,接下来我会具体对这三种方法进行介绍,以及如何用代码实现。

本文全程干货 ,建议收藏。

One-Hot
One-Hot编码,又称独热编码,从提取方法来看也可以叫平权统计,非常容易理解,参见下述代码。

# 特征向量化——稀疏表示
from sklearn.feature_extraction.text import CountVectorizer

vectorizer = CountVectorizer()
corpus = ['This is the first document.',
          'This is the second document.',
          'And the third one',
          'Is this the first document?'
          ]
"""
this is the first document second third and one
排序:
        and document first is one second the third this
    1    0      1      1    1  0    0     1    0     1
    2    0      1      0    1  0    1     1    0     1
    3    1      0      0    0  1    0     1    1     0
    4    0      1      1    1  0    0     1    0     1
"""
X = vectorizer.fit_transform(corpus)
print(X)

上述代码中corpus这个list共有四句话,平权统计就是先统计好一共有哪些词(会统一小写化),像上文就一共有9个词,同时调用CountVectorizer时会对词库进行排序,如上所示。接着就是生成每一句的特征,这句话中出现了哪个词,就在对应的地方标1,其他地方就标0。

最终这个X就是我们提取到的文本特征啦。

另外,如果想要降低特征的稀疏性的话,可以使用CountVectorizer(min_df=2),这里min_df=2代表着只有频数(出现次数)超过2的词才会被记录。

注意,中文的话要事先分好词,即按空格分好。

TF-IDF

顾名思义,平权统计对于每一个词都一视同仁,但现实生活中我们都知道不同词语在句子中的重要性是不一样的,也就是说词与词之间的重要性不同。

TF-IDF(Term Frequency/Inverse Document Frequency)是信息检索领域非常重要的搜索词重要性度量;用以衡量一个关键词w对于查询(Query,可看作文档)所能提供的信息。

词频(Term Frequency, TF)表示关键词w在文档Di中出现的频率:
在这里插入图片描述
其中,count(w)为关键词w的出现次数,|Di|为文档Di中所有词的数量。

逆文档频率(Inverse Document Frequency, IDF)反映关键词的普遍程度——当一个词越普遍(即有大量文档包含这个词)时,其IDF值越低;反之,则IDF值越高。IDF定义如下:
在这里插入图片描述
其中,N为所有的文档总数,I(w,Di)表示文档Di是否包含关键词,若包含则为1,若不包含则为0。若词w在所有文档中均未出现,则IDF公式中的分母为0;因此需要对IDF做平滑(smooth):
在这里插入图片描述
最终关键词w在文档Di的TF-IDF值:
在这里插入图片描述
还是用上面那个例子:

from sklearn.feature_extraction.text import TfidfVectorizer

vectorizer = TfidfVectorizer()
corpus = ['This is the first document.',
          'This is the second document.',
          'And the third one',
          'Is this the first document?'
          ]

X = vectorizer.fit_transform(corpus)
print(X)

其中,X[0]即为第一句话的文本特征,X[1]即为第二句话的文本特征…

同样的,我们也可以用min_df这个参数来除去低频词。

word2vec
在这里插入图片描述

Word2vec,是一群用来产生词向量的相关模型。这些模型为浅而双层的神经网络,用来训练以重新建构语言学之词文本。网络以词表现,并且需猜测相邻位置的输入词,在word2vec中词袋模型假设下,词的顺序是不重要的。训练完成之后,word2vec模型可用来映射每个词到一个向量,可用来表示词对词之间的关系,该向量为神经网络之隐藏层。

通过Word2Vec算法得到每个词语的高维向量(词向量,Word Embedding)表示,词向量把相近意思的词语放在相近的位置。我们只需要有大量的某语言的语料,就可以用它来训练模型,获得词向量。(建议语料库的文本内容要和自己后面做的任务涉及的领域相同或相似,且语料库要足够大)

第一步:将one-hot形式的词向量输入到单层神经网络中,其中输入层的神经元结点个数应该和one-hot形式的词向量维数相对应。比如,输入词是“夏天”,它对应的one-hot词向量[0,0,1],那么,我们设置输入层的神经元个数就应该是3。
第二步:通过神经网络中的映射层中的激活函数,计算目标单词与其他词汇的关联概率,其中在计算时,使用了负采样(negative sampling)的方式来提高其训练速度和正确率。
第三步:通过使用随机梯度下降(SGD)的优化算法计算损失。
第四步:通过反向传播算法将神经元的各个权重和偏置进行更新。

那么怎么利用语料库来训练word2vec呢?

下面我们来实战演练一下。

假设我们现在已经有了一个语料库.txt,内容如下:
在这里插入图片描述

import jieba

import multiprocessing
from gensim.models.word2vec import Word2Vec

with open('./语料库.txt', 'r', encoding='utf-8') as f:
    corpus = f.readlines()

vocab = []  
# 分词
for line in corpus:
    seg = jieba.lcut(line)
    vocab.append(seg)

cpu_count = multiprocessing.cpu_count()
vocab_dim = 100 # 词向量对应的维度
n_iterations = 1
n_exposures = 10  # 所有频数超过10的词语
window_size = 7

model = Word2Vec(size=vocab_dim,
                     min_count=n_exposures,
                     window=window_size,
                     workers=cpu_count,
                     iter=n_iterations)
model.build_vocab(vocab)  # input: list
model.train(vocab, total_examples=model.corpus_count, epochs=model.iter)
model.save('./w2v.pkl')
# print('加载word2vec模型')
# model = Word2Vec.load('./w2v.pkl')
print(model['舆情'])

如果是英文的话,除了前面的分词不一样,其余部分也是同样的步骤。

其中vocab_dim为词向量对应的维度,一般选100-200之间。

到现在呢,我们已经能够提取每个词的特征了,但是我们的任务是针对句子进行分类或者回归,对象是句子而非词语,所以我们需要进一步得到句子的特征。

我们用到的是求平均的方法,即通过将句子中出现的词的词向量的相加之后,再除以词的数量,得到句子的特征,这种方法简单,容易实现,但确实不可避免会忽视掉句子的语序信息。(但效果也不见得就比较差)

具体举例如下:

import numpy as np
import jieba

# 直接词向量相加求平均
def fea_sentence(list_w):
    n0 = np.array([0. for i in range(vocab_dim)], dtype=np.float32)
    for i in list_w:
        n0 += i
    fe = n0 / len(list_w)
    fe = fe.tolist()
    return fe

def parse(data, word2vec):
    xVec = []
    for x in data:
        sentence = []
        for word in x:
            if word in word2vec:
                sentence.append(word2vec[word])
            else:  # 词不存在,则补零向量。
                sentence.append([0. for i in range(vocab_dim)])
        xVec.append(fea_sentence(sentence))

    xVec = np.array(xVec)

    return xVec

if __name__ == '__main__':
	texts = ['如果是英文的话,除了前面的分词不一样,其余部分也是同样的步骤。',
        '其中vocab_dim为词向量对应的维度,一般选100-200之间。',
        '到现在呢,我们已经能够提取每个词的特征了,但是我们的任务是针对句子进行分类或者回归,对象是句子而非词语,所以我们需要进一步得到句子的特征。',
        '我们用到的是求平均的方法,即通过将句子中出现的词的词向量的相加之后,再除以词的数量,得到句子的特征.'
        ]

	data = []
	for sentence in texts:
    	seg = jieba.lcut(sentence)
    	data.append(seg)
    print('加载word2vec模型')
	model = Word2Vec.load('./w2v.pkl')
    xVec = parse(data, model)

此时,xVec[0]即为第一句话的文本特征,xVec[1]即为第二句话的文本特征……


推荐关注的专栏

👨‍👩‍👦‍👦 机器学习:分享机器学习理论基础和常用模型讲解
👨‍👩‍👦‍👦 数据分析:分享数据分析实战项目和常用技能整理


关注我,了解更多相关知识!

CSDN@报告,今天也有好好学习

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐