27

零基础入门NLP - 基于TF-ID统计特征的文本分类

 3 years ago
source link: https://segmentfault.com/a/1190000023376288
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

任何分类问题, 都需要从数据中挖掘有用的特征, 文本分类也不例外. 这里会介绍几种从文本中提取特征的方式. 也是处理文本最基础的方法.

文本表示方法

在机器学习算法的训练过程中,假设给定$N$个样本,每个样本有$M$个特征,这样组成了$N×M$的样本矩阵,然后完成算法的训练和预测。同样的在计算机视觉中可以将图片的像素看作特征,每张图片看作hight×width×3的特征图,一个三维的矩阵来进入计算机进行计算。

但是在自然语言领域,上述方法却不可行:文本是不定长度的。文本表示成计算机能够运算的数字或向量的方法一般称为词嵌入(Word Embedding)方法。词嵌入将不定长的文本转换到定长的空间内,是文本分类的第一步。

One-hot独热标签

one-hot通常被用来编码不同类别, 一个编码的每一位对应一个类别, 且只有其中一位是1, 其余均为0. 按照相同的思想, 我们也可以用one-hot编码来表示每一个单词. 比如下面两句话

句子1:我 爱 北 京 天 安 门

首先会统计两句话中的所有字的类别, 并将每个类别编号

{

'我': 1, '爱': 2, '北': 3, '京': 4, '天': 5,

'安': 6, '门': 7, '喜': 8, '欢': 9, '上': 10, '海': 11

}

在这里共包括11个字,因此每个字可以转换为一个11维度稀疏向量:

我:[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

爱:[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]

...

海:[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]

这种思路看似是合理的, 但存在明显的2个问题

  1. 对于一个稍复杂的语料数据, 就已经包含庞大数量的词, 并且一个词还会有多种形式, 如果每个词都用一个one-hot编码向量表示, 会导致维度爆炸.
  2. one-hot向量无法建模单词之间(one-hot向量相互正交)的关系, 然而这种信息是文本中重要的特征.

Bag of Words

bag of words(BoW)也叫词袋模型, 是一种从文本中提取特征用于建模的方法.

词袋模型是一种描述一个文档中的单词出现的文本表示,它主要包括

  • 一个已有单词的词典.
  • 已有单词表示的度量.

之所以被称为词袋, 因为BoW只关心已知单词在文档中是否出现, 并不关心它在文档中出现的顺序和结构信息.它将每个词在文档中的计数作为特征.

构建一个BoW模型包括以下几个步骤

  1. 收集数据

    比如

    It was the best of times,

    it was the worst of times,

    it was the age of wisdom,

    it was the age of foolishness,

  2. 设计词典

    可以将文档库(收集的数据)中自己认为重要的单词加入到词典中, 词典的形式如下

    “it”

    “was”

    “the”

    “best”

    “of”

    “times”

    “worst”

    “age”

    “wisdom”

    “foolishness”

  3. 创建文档向量

    这一步的目的是将每个文档(可以理解成包含不定长度单词的句子)转换为一个固定长度的向量, 向量的长度为词典中单词的个数.

    那么如何将文档转换为单个向量呢, 最简单的方式就是, 使用一个布尔值来表示词典中每个词是否在文档中是否出现, 出现了即为1, 否则为0

    比如上面的一个文档得到的向量为

    “it” = 1

    “was” = 1

    “the” = 1

    “best” = 1

    “of” = 1

    “times” = 1

    “worst” = 0

    “age” = 0

    “wisdom” = 0

    “foolishness” = 0

    对应向量为:

    [1, 1, 1, 1, 1, 1, 0, 0, 0, 0]

在sklearn中, 我们可以利用自带的工具快速的实现BoW的功能

from sklearn.feature_extraction.text import CountVectorizer
corpus = [
        'This is the first document.',
        'This document is the second document.',
        'And this is the third one.',
        'Is this the first document?',
]
# 将每个单词在词典中出现的次数作为特征
counter = CountVectorizer()
vectors = counter.fit_transform(corpus)

N-gram

简单来说, N-gram模型根据前N-1个已有的单词来预测一个单词出现的概率, 但N=2(N-1=1)时, 即一个单词出现的概率仅由它的前一个单词决定.

那么如何根据N-1个已出现的单词来预测一个单词的出现呢?

首先, 我们需要一个语料库(corpus), 包含了大量的句子. 假设现在语料库包含了如下的句子

1.He said thank you.

2.He said bye as he walked through the door.

3.He went to San Diego.

4.San Diego has nice weather.

5.It is raining in San Francisco.

假设我们设置N为2, 即只根据它前一个词来进行预测单词出现的概率.通常而言, 概率的计算方式如下

$\frac{count(wp wn)}{count(wp)}$, wp表示上一个单词, wn表示当前单词, count为计数函数.

比如我们要得到 you 出现在 thank 之后的概率 P(you|thank) ,它等同于

occurence times of "thank you" / occurence times of "thank"

= 1 / 1

= 1

我们可以说, 无论什么时候出现了 thank , you 都会出现在它后面.

TF-IDF

TF-IDF(term frequency-inverse document frequency), 它是一种统计度量的方法, 用来评估一个单词对于文档库中的一个文档的相关程度. 它在信息检索和文本挖掘经常被使用.

对于一个文档中的一个单词, 它的TF-IDF可以通过乘以两个不同的指标来得到

  • term-frequency(TF): $TF(t) = \frac{count(t)}{ total \quad terms} =\frac{单词t在当前文档出现的次数}{当前文档中总的单词数}$
  • inverse document frequency(IDF): $IDF(t)=In(\frac{count(document)}{count(document\quad which\quad contain\quad term\quad t)})=In(\frac{总的文档数目}{包含单词t的文档数目})$

比如一个文档中包含100个词, 单词 cat 出现了3次, 则TF( cat )=3/100=0.03, 假设我们有1e7个文档, cat 在其中的1e3个中出现了, 则IDF( cat )=log(1e7/1e3)=4, 因此TF_IDF权重为: 0.03 * 4 = 0.12.

现在回到竞赛的数据中去, 尝试使用TF-IDF来构建特征进行分类

import pandas as pd
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from sklearn.metrics import  f1_score
from sklearn.model_selection import train_test_split
from sklearn.linear_model import RidgeClassifier

root_dir = '/content/drive/My Drive/competitions/NLPNews'

# 内存有限, 这里只读取10000行
train_df = pd.read_csv(root_dir+'/train.csv', sep='\t', nrows=10000)

# max_features表示词典的大小, 包含词频最高的max_features个词
tfidf = TfidfVectorizer(ngram_range=(1, 3), max_features=3000)
train_test = tfidf.fit_transform(train_df['text'])

# 构建分类器
clf = RidgeClassifier()

# 切分数据集
x_train, x_test, y_train, y_test = train_test_split(train_test, train_df['label'], test_size=0.1, random_state=0)

# 训练模型
clf.fit(x_train, y_train)

# 执行预测
y_pred = clf.predict(x_test)

# 输出宏平均f1-score
print(f1_score(y_test, y_pred, average='macro'))

0.8802400152512864

总结

通过本次的学习, 对于文本的表示方法以及文本数据集的特征构建有了一个基本的了解.

Reference

[1]Datawhale零基础入门NLP赛事 - Task3 基于机器学习的文本分类)

[2] A Gentle Introduction to the Bag-of-Words Model

[3] An Introduction to N-grams: What Are They and Why Do We Need Them?

[4] what does tf-idf mean?


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK