相关文章推荐
胡子拉碴的小刀  ·  預讀自動摘要對高中生閱讀新興科技英文文本的影響·  1 月前    · 
胡子拉碴的小刀  ·  【語文中心公告】115學年度「跨域專業學術英 ...·  1 月前    · 
胡子拉碴的小刀  ·  如何執行情感分析與意見挖掘- ...·  1 月前    · 
胡子拉碴的小刀  ·  運用數位人文工具探討檔案時事議論主題及其情感 ...·  1 月前    · 
胡子拉碴的小刀  ·  創新文本情感分析方法在報酬率預測中的應用·  1 月前    · 
小百科  ›  从整体视角了解情感分析、文本分类!开发者社区
文本 文本分析 token 文本分类 自然语言处理
坚强的铁板烧
3 年前
作者头像
Datawhale
0 篇文章

从整体视角了解情感分析、文本分类!

前往专栏
腾讯云
开发者社区
文档 意见反馈 控制台
首页
学习
活动
专区
工具
TVP
文章/答案/技术大牛
发布
首页
学习
活动
专区
工具
TVP
返回腾讯云官网
社区首页 > 专栏 > Datawhale专栏 > 正文

从整体视角了解情感分析、文本分类!

发布 于 2021-11-16 16:50:24
693 0
举报

作者:太子长琴,算法工程师,Datawhale成员

文本分类是 自然语言处理 (NLP)最基础核心的任务,或者换句话说,几乎所有NLP任务都是「分类」任务,或者涉及到「分类」概念。比如分词、词性标注、命名实体识别等序列标注任务其实就是Token粒度的分类;再比如文本生成其实也可以理解为Token粒度在整个词表上的分类任务。

本文侧重于从宏观角度(历史演变和基本流程)对文本情感分类任务进行介绍,目的是 给读者提供一个整体视角,从高远处审视情感分析、文本分类、甚至NLP,期望能抛砖引玉,引发读者更多的思考。

本文同样适合于非算法岗位的工程师,以及没有技术背景但对NLP感兴趣的伙伴。

本文篇幅较长,主要分为四个部分。

背景介绍:简单介绍情感分析相关的概念、类型,并和文本分类任务对应。

基本流程:文本分类(或常见的NLP任务)基本处理流程。

模型简史:NLP处理任务中模型变迁的简单历史。

情感未来:探讨未来情感分析可能的发展方向。

01 背景介绍

情感分析是根据输入的文本、语音或视频,自动识别其中的观点倾向、态度、情绪、评价等,广泛应用于消费决策、舆情分析、个性化推荐等商业领域。包括篇章级、句子级和对象或属性级。可以分为以下三类:

工业界最常见的往往是这种情况。比如大众点评某家餐饮店下的评论:“服务非常赞,但味道很一般”,这句话其实表达了两个意思,或者说两个对象(属性)的评价,我们需要输出类似:<服务,正向>和<口味,负向>这样的结果(<属性-倾向>二元组),或者再细一点加入用户的观点信息(<属性-观点-倾向>三元组):<服务,赞,正向>和<口味,一般,负向>。这样的结果才更有实际意义。

从 NLP 处理的角度看,情感分析包括两种基础任务:文本分类和实体对象或属性抽取,而这也正好涵盖了 NLP 的两类基本任务:序列分类(Sequence Classification)和 Token 分类(Token Classification)。两者的区别是,前者输出一个标签,后者每个 Token 输出一个标签。

02 基本流程

一般来说,NLP 任务的基本流程主要包括以下五个:

2.1 文本预处理

文本预处理主要是对输入文本「根据任务需要」进行的一系列处理。

文本清理: 去除文本中无效的字符,比如网址、图片地址,无效的字符、空白、乱码等。

标准化: 主要是将不同的「形式」统一化。比如英文大小写标准化,数字标准化,英文缩写标准化,日期格式标准化,时间格式标准化,计量单位标准化,标点符号标准化等。

纠错: 识别文本中的错误,包括拼写错误、词法错误、句法错误、语义错误等。

改写: 包括转换和扩展。转换是将输入的文本或 Query 转换为同等语义的另一种形式,比如拼音(或简拼)转为对应的中文。扩展主要是讲和输入文本相关的内容一并作为输入。常用在搜索领域。

需要注意的是,这个处理过程并不一定是按照上面的顺序从头到尾执行的,可以根据需要灵活调整,比如先纠错再标准化或将标准化放到改写里面。咱们不能被这些眼花缭乱的操作迷惑,始终谨记,文本预处理的目的是将输入的文本变为已有系统「喜欢且接受」的形式。举个例子,比如系统在训练时都使用「单车」作为自行车的称呼,那预处理时就应该把自行车、Bike、Bicycle 等都转为单车。或者甚至系统用了某个错别字,那输入也要变成对应的错别字。

2.2 Tokenizing(分词)

主要目的是将输入的文本 Token 化,它涉及到后续将文本转为向量。一般主要从三个粒度进行切分:

字级别: 英文就是字母级别,操作起来比较简单。

词级别: 英文不需要,中文可能会需要。关于中文分词,之前写过一点思考,简单来说,分词主要是「分割语义」,降低不确定性,要不要分词一般要看任务和模型。

子词: 包括BPE(Byte Pair Encoding),WordPieces,ULM(Unigram Language Model)等。在英文中很常见,当然中文也可以做,是介于字级别和词级别中间的一种粒度。主要目的是将一些「统一高频」的形式单独拎出来。比如英文中 de 开头的前缀,或者最高级 est 等等。子词一般是在大规模语料上通过统计「频率」自动学习到的。

我们需要知道的是,字和词并非哪个一定比另一个好,都要需要根据具体情况具体分析的。它们的特点如下:

粒度

字

词

词表大小

固定

无法穷尽

未识别词(OOV)

没有

典型问题,长尾、稀疏性

参数/计算量

多

Token 变少,参数少,计算快

语义/建模复杂度

有不确定性

携带语义,能够降低不确定性

注:OOV=Out Of Vocabulary

2.3 构造数据

文本经过上一步后会变成一个个 Token,接下来就是根据后续需要将 Token 转为一定形式的输入,可能就是 Token 序列本身,也可能是将 Token 转为数字,或者再加入新的信息(如特殊信息、Token 类型、位置等)。我们主要考虑后两种情况。

Token 转数字: 就是将每个「文本」的 Token 转为一个整数,一般就是它在词表中的位置。根据后续模型的不同,可能会有一些特殊的 Token 加入,主要用于「分割输入」,其实就是个「标记」。不过有两个常用的特殊 Token 需要稍加说明: <UNK> 和 <PAD> ,前者表示「未识别 Token」,UNK=Unknown,后者表示「填充 Token」。因为我们在实际使用时往往会批量输入文本,而文本长度一般是不相等的,这就需要将它们都变成统一的长度,也就是把短的用 <PAD> 填充上。 <PAD> 一般都放在词表第一个位置,index 为 0,这样有个好处就是我们在计算时,0 的部分可以方便地处理掉(它们是「填充」上去的,本来就算不得数)。

加入新的信息: 又可以进一步分为「在文本序列上加入新的信息」和「加入和文本序列平行的信息」。

  • 序列上新增信息:输入的文本序列有时候不一定『只』是一句话,还可能会加入其他信息,比如:「公司旁边的螺蛳粉真的太好吃了。<某种特殊分隔符>螺蛳粉<可能又一个分隔符>好吃」。所以,准确来说,应该叫「输入序列」。
  • 新增平行信息:有时候除了输入的文本序列,还需要其他信息,比如位置、Token 类型。这时候就会有和 Token 序列 Token 数一样的其他序列加入,比如绝对位置信息,如果输入的句子是「今天吃了螺蛳粉很开心」,对应的位置编码是「1 2 3 4 5 6 7 8 9 10」。

需要再次强调的是,这一步和后续使用的模型直接相关,要根据具体情况进行相应处理。

2.4 文本特征

根据上面构造的数据,文本特征(也可以看作对文本的表征)从整体来看可以分为两个方面:Token 直接作为特征和 Token(或其他信息)编码成数字,然后转成向量作为特征。这一小节咱们主要介绍一下从 Token 到特征形态发生了哪些变化,至于怎么去做这种转换,为什么,下一节《模型发展》中会做进一步分析。

a. OneHot

首先,要先明确下输入的文本最终要变成什么样子,也就是特征的外形长啥样。注意,之前得到的整数并不能直接放到模型里去计算,因为数字的大小并没有语义信息。

那么,最简单的表示方式就是某个 Token 是否出现在给定的输入中。我们假设实现已经有一个做好的很大的词表,里面有 10w 个 Token。当我们给定 Token 序列时,词表中的每个 Token 是否出现在给定的序列中就可以构造出一个 01 向量:

[0, 0, 1, 0, ..., 0, 1] # 10w 个

其中 1 表示那个位置的 Token 在出现在了给定的序列中,0 则表示未出现。可以想象,对于几乎所有的输入,对应的 01 向量绝大多数的位置都是 0。假设有 m 个句子,那么会得到一个矩阵:

# m * 10w
[[0, 0, 1, 0, ..., 0, 1],
 [0, 1, 0, 0, ..., 0, 0],
 [0, 0, 1, 1, ..., 0, 1],
 [0, 1, 1, 0, ..., 0, 0],
 [1, 0, 0, 0, ..., 0, 1]]

其中的每一列就表示该位置 Token 的向量表示,换句话说,在表示句子的同时, Token 也被成功地表示成了一个向量。

上面的这种编码方式就叫 OneHot 编码,也就是把 Token 编成一个只有一个 1 其余全是 0 的向量,然后通过在语料上计算(训练)得到最后的表示。

b. TF-IDF

聪明的您一定会想到,既然可以用「出现」或「不出现」来表示,那为啥不用频率呢?没错,我们可以将上面的 1 都换成该词在每个句子中出现的频率,归一化后就是概率。由于自然语言的「齐夫定律」,高频词会很自然地占据了主导地位,这对向量表示大有影响,想象一下,那些概率较高的词几乎在所有句子中出现,它们的值也会更大,从而导致最终的向量表示向这些词倾斜。

齐夫定律:在自然语言语料库中,一个单词出现的频率与它在频率表里的排名成反比。即第 n 个常见的频率是最常见频率的 1/n。举个例子来说,中文的「的」是最常见的词,排在第 1 位,比如第 10 位是「我」,那「我」的频率就是「的」频率的 1/10。

于是,很自然地就会想到使用某种方式去平和这种现象,让真正的高频词凸显出来,而降一些类似的、是这种常见词的影响下降。咱们很自然就会想到,能不能把这些常见词从词表给剔除掉。哎,是的。这种类似的常见词有个专业术语叫「停用词」,一般呢,停用词主要是虚词居多,包括助词、连词、介词、语气词等,但也可能会包括一些「没用」的实词,需要根据实际情况处理。

除了停用词,还有另一种更巧妙的手段处理这个问题——TF-IDF,TF=Term Frequency,IDF=Inverse Document Frequency。TF 就是刚刚提到的词频,这不是有些常用词吗,这时候 IDF 来了,它表示「有多少个文档包含那个词」,具体等于 文档总数/包含该词的文档数 。比如「的」在一句话(或一段文档)中概率很高,但几乎所有句子(或文档)都有「的」,IDF 接近 1;相反如果一个词在句子中概率高,但包含该词的文档比较少,IDF 就比较大,最后结果也大。而这是满足我们预期的——词在单个文档或句子中是高概率的,但在所有文档或句子中是低概率的,这不正说明这个词对所在文档或句子比较重要吗。实际运算时,一般会取对数,并且防止 0 除:

\text{IDF}(s) = \log \frac{1+N}{1+\text{DF}(s)} + 1

这时候的向量虽然看着不是 OneHot,但其实本质还是,只是在原来是 1 的位置,变成了一个小数。

c. Embedding

刚刚的得到的矩阵最大的问题是维度太大,数据稀疏(就是绝大部分位置是 0),而且词和词之间是孤立的。最后这个问题不用多解释,这样构建的特征肯定「不全面」。但是维度太大和数据稀疏又有什么影响呢?首先说前者,在《文献资料:文本特征》第一篇文章提到了在超高维度下的反直觉现象——数据不会变的更均匀,反而会聚集在高维空间的角落,这会让模型训练特别困难。再说后者,直观来看,数据稀疏最大的问题是使得向量之间难以交互,比如「出差住酒店」和「出差住旅店」,酒店和旅店在这里意思差不多,但模型却无法学习到。

既然如此,先辈们自然而然就想能不能用一个连续稠密、且维度固定的向量来表示。然后,大名鼎鼎的「词向量」就登场了——它将一个词表示为一个固定维度大小的稠密向量。具体来说,就是首先随机初始化固定维度大小的稠密向量,然后用某种策略通过词的上下文自动学习到表征向量。也就是说,当模型训练结束了,词向量也同时到手了。具体的过程可参考《文献资料:文本特征》第二篇文章。

「词向量」是一个划时代的成果,为啥这么说呢?因为它真正把自然语言词汇表征成一个可计算、可训练的表示,带来的直接效果就是自然语言一步跨入了深度学习时代——Embedding 后可以接各种各样的模型架构,完成复杂的计算。

2.5 结果输出

当用户的输入是一句话(或一段文档)时,往往需要拿到整体的向量表示。在 Embedding 之前虽然也可以通过频率统计得到,但难以进行后续的计算。Embedding 出现之后,方法就很多了,其中最简单的当然是将每个词的向量求和然后平均,复杂的话就是 Embedding 后接各种模型了。

那么在得到整个句子(或文档)的向量表示后该如何得到最终的分类呢?很简单,通过一个矩阵乘法,将向量转为一个类别维度大小的向量。我们以二分类为例,就是将一个固定维度的句子或文档向量变为一个二维向量,然后将该二维向量通过一个非线性函数映射成概率分布。

举个例子,假设最终的句子向量是一个 8 维的向量,w 是权重参数,计算过程如下:

import numpy as np
rng = np.random.default_rng(seed=42)
embed = rng.normal(size=(1, 8)).round(2)
# array([[ 0.3 , -1.04,  0.75,  0.94, -1.95, -1.3 ,  0.13, -0.32]])  维度=1*8
w = rng.normal(size=(8, 2)).round(2)
""" 维度=8*2
array([[-0.02, -0.85],
       [ 0.88,  0.78],
       [ 0.07,  1.13],
       [ 0.47, -0.86],
       [ 0.37, -0.96],
       [ 0.88, -0.05],
       [-0.18, -0.68],
       [ 1.22, -0.15]])
z = embed @ b # 矩阵乘法
# array([[-2.7062,  0.8695]]) 维度=1*2
def softmax(x): 
    return np.exp(x) / np.sum(tf.exp(x), axis=1)
softmax(z)
# array([[0.0272334, 0.9727666]])
 
推荐文章
胡子拉碴的小刀  ·  預讀自動摘要對高中生閱讀新興科技英文文本的影響
1 月前
胡子拉碴的小刀  ·  【語文中心公告】115學年度「跨域專業學術英文(一):文本解析與 ...
1 月前
胡子拉碴的小刀  ·  如何執行情感分析與意見挖掘- Foundry Tools | Microsoft Learn
1 月前
胡子拉碴的小刀  ·  運用數位人文工具探討檔案時事議論主題及其情感分類之實作
1 月前
胡子拉碴的小刀  ·  創新文本情感分析方法在報酬率預測中的應用
1 月前
Link管理   ·   Sov5搜索   ·   小百科
小百科 - 百科知识指南