文本向量化技术

词袋法

使用sklearn提供的CountVectorizer类,样例如下:

def corpus4sklearn():
    return ["i am chinese", "i am in Hubei"]

def bow_extracter(corpus):
    # ngram_range是说可以把几个词连起来视作一个特征,bow一般就是一个词一个特征,否则特征数会爆炸
    vectorizer = CountVectorizer(stop_words=None, ngram_range=(1, 1), analyzer="word")
    return do_extracter(vectorizer, corpus)


def do_extracter(vectorizer, corpus):
    mat = vectorizer.fit_transform(corpus)
    print("feature num:%d" % mat.shape[1])
    print("vocab:%s" % vectorizer.vocabulary_)
    return vectorizer, mat

提取的结果是语料中的每个词(去掉stop_words)作为一个特征,语料中的每篇文档形成

[1,0,2,0...]

这样的向量,向量元素是某个单词在文档里出现的次数。这里的特征数目可能会非常巨大(取决于词典vocabulary_里词的总数)。

CountVectorizer具体说明参看:

http://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.CountVectorizer.html#sklearn.feature_extraction.text.CountVectorizer.fit_transform

tf-idf特征提取

也可使用tf-idf方法完成文本向量化,该方法是在词袋法的基础上考虑了idf,即“逆向文档频率”,削弱了那些在多个文档中频繁出现的词的影响(这些词往往不那么重要,因为它们无法区分文档)。样例代码如下:

def corpus4sklearn():
    return ["i am chinese", "i am in Hubei"]

def tfidf_extracter(corpus):
    # 做l2正则化,即均方。l1则为误差绝对值的和
    vectorizer = TfidfVectorizer(
        min_df=1, norm="l2", smooth_idf=True, use_idf=True, ngram_range=(1, 1)
    )
    return do_extracter(vectorizer, corpus)

def do_extracter(vectorizer, corpus):
    mat = vectorizer.fit_transform(corpus)
    print("feature num:%d" % mat.shape[1])
    print("vocab:%s" % vectorizer.vocabulary_)
    return vectorizer, mat

tf-idf公式

tf-idf(d, t) = tf(t) * idf(d, t)

其中:
t为词,tf(t)为词频;
d为文档,idf(d, t)为逆文档频率。

idf(d, t)=log [ N / df(d, t) ] + 1

N为文档总数,df(d, t)为包含词t的文档数。可见,词t在文档里出现的越少,idf值越大,该词的影响力就越大。+1主要是确保那些在所有文档里出现的词(显然,此时df(d, t)=N,所以log [ N / df(d, t) ] = 0)不会完全被忽略。

为避免除数为0,sklearn的tf-idf向量化默认会做idf平滑,上述公式演变成:

idf(d, t) = log [ (1 + N) / (1 + df(d, t)) ] + 1

word2vec/doc2vec向量化

不管是词袋还是tf-idf,都没有考虑单词的顺序和语义。要考虑顺序和语义,可以使用word2vec及其改进版doc2vec来生成文档向量。相比于词袋法和tf-idf,Word2vec/doc2vec的特征数更可控。核心代码为:

def corpus4word2vec():
    origin = ["i am chinese", "i am in Hubei"]
    return [x.split(" ") for x in origin]
    
def corpus4doc2vec():
    origin = ["i am chinese", "i am in Hubei"]
    # doc2vec的一条语料记录用标记文档(TaggedDocument)表示,第二个参数是用户打的标记
    return [TaggedDocument(x.split(" "), [i]) for i, x in enumerate(origin)]
    
def gen_model_by_word2vec(corpus):
    model = gensim.models.Word2Vec(corpus, vector_size=100, window=5, min_count=1, sg=0)

    print(model.corpus_total_words)

    return model


def gen_model_by_doc2vec(corpus):
    model = gensim.models.Doc2Vec(
        documents=corpus, vector_size=50, dm=0, window=5, min_count=1,epochs=20
    )

    print(model.corpus_total_words)

    return model
    
    ...
    # 获得某个单词的向量都是用wv[]
    vec = model.wv["chinese"]
    
    # doc2vec可推断文档的向量,word2vec不行
    model.infer_vector(["i", "chinese"])

特别说一下doc2vec里TaggedDocument的tag,因为最终的model.dv里是用tag来作为key的,所以每个文档必须要有一个唯一tag(比如使用自增序列号)。除此之外,还可以增加额外的tag,但额外tag的值要与唯一tag区分开。比如,我有10篇doc,唯一tag从0~9,额外tag就不能再用0~9的数字了,可用Y和N这样的字符串。

word2vec和doc2vec的差异:
word2vec得到的是词的向量,doc2vec不仅能得到词向量,还可以推断出文档向量。

得到词或文档的向量后,就可以用这些向量进行后续的文本分类、预测等工作了。

训练参数选择

特别讲一下Doc2Vec的训练参数选择:
vector_size,向量大小,默认是100,数据集不大时(数百到数千条文本),可设为50。数据集大时,300~400也很常见。
epochs,迭代次数,默认10,用于处理较大数据集(数万到数百万)。如果数据集较小(数百到数千),需要加大epochs到20,可以加强向量生成的准确性和稳定性。
总的来说,对于小数据集,可以适当加大epochs并减小vector_size,以获得更准确、更稳定的训练效果。

还有一种更容易量化的方法来判断模型的准确性和稳定性:
1、对同一份文本多次使用infer_vector来推测其向量,对这些向量计算余弦相似度,相似度达到0.98~0.99的,说明模型比较稳定;
2、对同一份文本,计算model.dv里的训练向量和infer_vector推测向量的余弦相似度,相似度达到0.95以上的,说明模型基本是准确的。不过,该量化方法,训练的dm参数应设置为0,即使用DBOW而非DM方法来训练。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值