前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Tweets的预处理

Tweets的预处理

作者头像
磐创AI
发布2021-01-12 11:37:05
2K0
发布2021-01-12 11:37:05
举报
文章被收录于专栏:磐创AI技术团队的专栏


磐创AI分享

作者 | Wei-Ting Yap

编译 | VK

来源 | Towards Data Science

自然语言处理是机器学习的一个领域,涉及到对人类语言的理解。与数字数据不同,NLP主要处理文本。探索和预处理文本数据需要不同的技术和库,本教程将演示基础知识。

然而,预处理不是一个算法过程。在数据科学任务中,数据的上下文通常决定了数据的哪些方面是有价值的,哪些方面是不相关的或不可靠的。在本教程中,我们将探讨tweets上下文中的文本预处理,或者更广泛地说,社交媒体。

我们的数据集来自Kaggle(https://www.kaggle.com/c/nlp-getting-started),Kaggle提供了一个合理大小的数据集(训练集中大约7500条推文)供练习。挑战在于根据tweet的文本、关键字和位置,将其归类为是否真的是灾难。

本教程的代码可以在本笔记本和代码仓库中找到:https://github.com/weiting109/disaster-tweets-classifier/blob/main/nb.ipynb

在开始之前,请从Kaggle下载nlp-getting-started数据。在我的项目目录中,我把train.csv, test.csv, 和sample_submission.csv放在数据子目录下。


数据探索

让我们从导入典型和有用的数据科学库开始,并创建一个`train.csv. 我不会深入研究非NLP特定的库的细节。

代码语言:javascript
复制
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

data = pd.read_csv('data/train.csv', index_col='id')
data.head()
代码语言:javascript
复制
keyword location text target
id    
1 NaN NaN Our Deeds are the Reason of this #earthquake M... 1
4 NaN NaN Forest fire near La Ronge Sask. Canada 1
5 NaN NaN All residents asked to 'shelter in place' are ... 1
6 NaN NaN 13,000 people receive #wildfires evacuation or... 1
7 NaN NaN Just got sent this photo from Ruby #Alaska as ...

我们的数据包括4列,关键字,位置,文本和目标。引用Kaggle的数据描述:

  • id—每个tweet的唯一标识符
  • text—tweet的文本
  • location—发送tweet的位置(可能为空)
  • keyword—来自tweet的特定关键字(可能为空)
  • target—只在「train.csv」里,这表示一条tweet是否是关于一个真正的灾难(1)或不是(0)

为了确保数据集中的行数和列数的完整性,以及对训练集的泛化性做出判断,让我们了解一下训练数据的大小。

代码语言:javascript
复制
data.shape

(7613, 4)

仔细检查,我们发现有52行重复(不同的id,但是关键字、位置、文本和目标相同。

代码语言:javascript
复制
np.sum(data.duplicated()) # 52份相同的

所以让我们删除重复的行。索引保持不变。删除重复行之后,我们只剩下7561条tweet(完整性检查,如前所述),这是本教程中可使用的数量。

然而,对于NLP来说,7561个数据点仍然相对较少,特别是如果我们使用深度学习模型的话。考虑到每天可能有将近一百万条推文,我怀疑一个仅训练了7561个数据点的模型是否足够普遍。

代码语言:javascript
复制
# 我们把相同的丢弃
df = data
df = data.drop_duplicates()
df.shape

我们的输出:

代码语言:javascript
复制
(7561, 4)

除了训练规模外,训练集中的类别(target)的平衡也很重要。所有目标值为0的训练集将使模型将每条tweet分类为与灾难无关。反之亦然。理想情况下,训练集中的所有类数量都应该平衡。

我们可以使用panda的dataframe value_counts方法来计算每个类的行数。4322条不是关于灾难的tweet(target=0)和3239条关于灾难的tweet(target=1),类别为4:3。这不是完美的,但也不是灾难性的不平衡。

代码语言:javascript
复制
# 目标1指灾难微博,0不是灾难微博
df['target'].value_counts()

我们的输出:

代码语言:javascript
复制
0    4322
1    3239
Name: target, dtype: int64

让我们来看看数据的完整性。我们可以使用panda的dataframe isna方法返回的序列求和,以计算每个列的na数。

代码语言:javascript
复制
# 检查数据的完整性
print(f"{np.sum(df['keyword'].isna())} rows have no keywords")
print(f"{np.sum(df['location'].isna())} rows have no location")
print(f"{np.sum(df['text'].isna())} rows have no text")
print(f"{np.sum(df['text'].isna())} rows have no target")

我们的输出:

代码语言:javascript
复制
61 rows have no keywords 
2533 rows have no location 
0 rows have no text 
0 rows have no target

理想情况下,我们将通过分析字长、句子长度、词频等来进一步描述和探索数据。虽然这超出了本教程的范围,但是你可以在这里了解更多:https://neptune.ai/blog/exploratory-data-analysis-natural-language-processing-tools


NLP预处理

现在我们已经研究了数据,让我们预处理tweets并以模型可以接受的形式表示它们。

文本最常见的数字表示是词袋表示法。

词袋

词袋是一种用数字表示文本数据的方法。文本数据本质上被分割成单词(或者更准确地说,标识),这是特征。每个文本数据中每个词的频率都是相应的特征值。例如,我们可以将“I love cake very much”表示为:

代码语言:javascript
复制
{
'I':1,
'love':1,
'cake':1,
'very':2,
'much':1
}
标识化

标识化将文本数据分解为标识(token)。最简单的(也是最常见的)也就是单词,它完全符合我们的词袋表示。但是,这些标识还可以包括标点符号、停用词和其他自定义标识。我们将在下一节课中结合tweets和挑战来考虑这些问题。

词根还原

词根还原是指将词缀(前缀或后缀)截断,使其近似于词根形式。这通常是通过查找字典来判断是否是前缀和后缀来完成的,这使得它的计算速度很快。

然而,这是一个性能权衡。在英语中,一些词缀会完全改变词义,从而产生准确的特征表示。

词形还原

词干分析的另一种方法是词形还原。这是通过查找字典来完成的,因此会导致计算开销更大。然而,性能通常更好,因为词形一般是真实单词,而词根不是。

鉴于我们的数据集相对较小,我们将使用词形还原。


在推特的背景下

从tweets到他们的词袋表示就不那么简单了。关于:

  • 不同情况下的词,如cake vs Cake,
  • 标点符号
  • 停用词
  • 数字
  • 提及
  • 标签
  • URL网址

在决定如何处理这些元素时,我们必须考虑数据的上下文,并将其与挑战相协调。

标准化为小写

在互联网行话中,大小写不同可以传达不同的情感(例如,danger vs DANGER!)。通过将所有标识改为大写或小写,我们可能会丢失有助于分类的数据。

但是,由于我们有一个小的数据集(7500条tweets),以上类型的数据可能会很少,所以我们全部小写化。

标点符号

毫无疑问,tweet将包含标点符号,这些标点符号也可以传达不同的情感或情绪。考虑一下,在互联网术语中,以下两者之间的区别:

  • Help needed?
  • Help needed!

我们将把标点符号视为各自的标识,特殊情况下,“…”是“.”与“.”分开的标识。这样我们就不会丢失数据,我们可以在调整超参数时忽略它们(甚至调整要忽略的标点)。

停用词

停用词本质上是非常常见的词,它们对文本的意义没有什么重要的贡献。这些词包括冠词(the, a, that)和其他常用的词(what, how, many)。

在NLP处理中,停用词标识通常被忽略。然而,与其从一开始就忽略停用词,不如在调整超参数时忽略它们(甚至调整要忽略的停用词),这样就不会丢失数据。

数字

tweet中的数字可以传达文字对象的数量,但也可以传达某种事物的规模(如里氏7.9级地震)或年份(如2005年卡特里娜飓风)。

在后两种情况下,这些数字信息可能很有价值,这取决于我们以后选择的NLP级别(单词级别与短语级别或句子级别),或者我们是否希望过滤有关历史灾难与当前灾难的tweet。

因此,我们将保留数字作为标识,在调整超参数时可以选择忽略它们(甚至只计算年份)。

提及

在Twitter上,提及允许用户通过tweet互相称呼。虽然个人账户之间的提及可能不那么重要,但提及各种机构的账号却是十分重要(考虑一下@policeauthorities,gurn shooting down brick lane)

让我们将提及的内容和他们的用户名一起标识化,同时计算被提及的次数。

标签

Twitter上的标签允许用户发现与特定主题或主题相关的内容。当谈到自然灾害时,像*#prayforCountryX和#RIPxyzShootings*这样的标签可以将关于灾难的tweet与日常的tweets区分开来。

因此,让我们用标签的内容来标识标签,但也要计算标签的数量。

网址

灾难推特可以包括新闻文章、救灾工作或图片的网址。然而,日常微博也是如此。由于我们不确定灾难性tweet是否更有可能具有URL或某种类型的URL,所以让我们将URL作为标识,并将URL的数量作为一个特征。

这个数据集以tweets的短网址为特色(http://t.co),但更多当前的tweet数据可以包括域,然后可以提取这些域(我想红十字会的域将与灾难tweets高度相关)。对于更复杂的算法,还可以考虑访问缩短的URL和抓取web页面元素。


使用NLP的spaCy库

spaCy是一个用于自然语言处理的开源python库。它与其他python机器学习库(scikitlearn、TensorFlow、PyTorch)等集成良好,并使用面向对象的方法来保持其接口的可读性和易用性。

值得注意的是,它的模型返回文档类型数据,它由带有各种有用注释(例如,其词形,是否为停用词)的标识组成,作为属性。

让我们导入spaCy,下载American English的模型,并加载相关的spaCy模型。

代码语言:javascript
复制
# 下载美国英语spaCy库
!python3 -m spacy download en_core_web_sm

import spacy 
import en_core_web_sm
nlp = en_core_web_sm.load()
spaCy对tweets有多好

在定制spaCy之前,我们可以看看spaCy是如何用默认规则标识tweet的。我创建了一个tweet,包括一个数字、一个缩写、一个标签、一个提及和一个链接。

如下所示,spaCy已经分解了,并给出了相关的词形。它还根据默认规则将数字、提及和url识别为它们自己的标识。这就给我们留下了hashtags,它们被分成一个“#”标点和hashtag内容,而不是作为一个完整的标识。

代码语言:javascript
复制
# 让我们看看spaCy对数字、缩写、hashtags、@提及和url做了什么
s = "2020 can't get any worse #ihate2020 @bestfriend <https://t.co>"
doc = nlp(s)

# 让我们看看词形与是否它为停用词
print(f"Token\\t\\tLemma\\t\\tStopword")
print("="*40)
for token in doc:
    print(f"{token}\\t\\t{token.lemma_}\\t\\t{token.is_stop}"

打印:

代码语言:javascript
复制
Token  Lemma  Stopword
========================================
2020  2020  False
ca  can  True
n't  not  True
get  get  True
any  any  True
worse  bad  False
#  #  False
ihate2020  ihate2020  False
@bestfriend  @bestfriend  False
<https://t.co>  <https://t.co>  False
修改spaCy

我们可以修改spaCy的模型,将hashtags识别为整个标识。

可以修改spaCy的标识器(如果需要,也可以构建自定义标识器!)通过重新定义其默认规则。spaCy的标识器按以下顺序排列规则的优先级:标识匹配模式、前缀、后缀、中缀、URL、特殊情况(请参阅spaCy的标识器是如何工作的):https://spacy.io/usage/linguistic-features#how-tokenizer-works

在我们的例子中,我们将通过添加“#\\w+”来修改标识器的模式匹配regex模式(在这里阅读有关regex的更多信息:一个用Python编写的regex的简单介绍:https://towardsdatascience.com/a-simple-intro-to-regex-with-python-14d23a34d170)

代码语言:javascript
复制
# 我们还希望保留#hashtags作为标识,因此我们将修改spaCy模型的tokenŠmatch

import re

# 检索匹配regex模式的默认标识
re_token_match = spacy.tokenizer._get_regex_pattern(nlp.Defaults.token_match)

# 添加标签模式
re_token_match = f"({re_token_match}|#\\w+)"
nlp.tokenizer.token_match = re.compile(re_token_match).match

# 现在让我们再试一次
s = "2020 can't get any worse #ihate2020 @bestfriend <https://t.co>"
doc = nlp(s)

# 让我们看看词形与是否它为停用词
print(f"Token\\t\\tLemma\\t\\tStopword")
print("="*40)
for token in doc:
    print(f"{token}\\t\\t{token.lemma_}\\t\\t{token.is_stop}")

我们的代码打印:

代码语言:javascript
复制
Token  Lemma  Stopword
========================================
2020  2020  False
ca  can  True
n't  not  True
get  get  True
any  any  True
worse  bad  False
#ihate2020  #ihate2020  False
@bestfriend  @bestfriend  False
<https://t.co>  <https://t.co>  False
预处理算法

然后我们可以继续创建一个预处理算法,并将其放入一个函数中,这样就可以在训练集中的每个tweet上调用它。在以下预处理函数中,每条tweet:

  • 改为小写
  • 是用我们修改的spaCy模型标识的
  • 它的标识词形集与我们的features集联合
  • 在字典中构造了它的词袋表示法
  • 对它的标签,提及和网址计数
代码语言:javascript
复制
# 为每个tweet创建预处理函数
def preprocess(s, nlp, features):
    """
    给定参数s, spaCy模型nlp, 和特征集
    预处理s并返回更新的特征和词袋
    - 小写
    - 创建具有spaCy的文档
    - 词形与特征集的结合
    - 为tweet构建一个词袋
    """
    
# 小写
    s = s.lower()
    
# 创建具有spaCy的文档
    doc = nlp(s)
    
lemmas = []
    for token in doc:
        lemmas.append(token.lemma_)
        
# 词形与特征集的结合
    features |= set(lemmas)
    
# 为tweet构建一个词袋
    freq = {'#':0,'@':0,'URL':0}
    for word in lemmas:
        freq[str(word)] = 0
    for token in doc: 
        if '#' in str(token): freq['#'] += 1 # 对哈希标识计数
        if '@' in str(token): freq['@'] += 1 # 对被提及的次数计数
        if 'http://' in str(token): freq['URL'] += 1 # 对URL计数
        freq[str(token.lemma_)] += 1
        
    return features, freq
设置

我们将创建一个消除重复数据的副本,这样任何预处理更改都不会影响训练数据的原始状态。然后,我们将初始化一个python集合特征,它将包含每个tweet的所有特征。除了通过标识化每个tweet遇到的所有词形之外,特征还包括hashtags数量(#)、提及次数(@)和URL数量(URL)。

代码语言:javascript
复制
preprocess_df = df # 备份
features = set({'#','@','URL'}) # 使用feature包含所看到的所有单词(词形)

使用我们的预处理函数,我们将对每条tweet进行预处理,每次都用新的词形。对于每个tweet,tweet的词袋表示被附加到bow_array。

代码语言:javascript
复制
# bow_array[i]是tweet id(i+1)的表示
bow_array = [] 
for i in range(len(preprocess_df)):
    features, freq = preprocess(preprocess_df.iloc[i]['text'],nlp,features)
    bow_array.append(freq)
len(bow_array) # 7561

通过在features中收集到的所有tweet中遇到的所有词形,我们可以创建一个数据帧bow来表示所有tweet的特征。

代码语言:javascript
复制
# 为每条tweet创建词袋表示的数据帧
bow = pd.DataFrame('0', columns=features,index=range(len(preprocess_df)))
bow['id']=preprocess_df.index
bow.set_index('id',drop=True,inplace=True)

现在,让我们用每条tweet的特征值更新我们的数据帧。

代码语言:javascript
复制
# 用tweet id的词袋频率更新bow[i](i+1)
for i in range(len(preprocess_df)):
    freq = bow_array[i]
    for f in freq:
        bow.loc[i+1,f]=freq[f]

我们使用pandas Dataframe的join方法。保存preprocessed .csv文件,以便于下一步操作!

代码语言:javascript
复制
# 将词袋表示加入到训练数据帧中
# 对于不是词形标识的特征,请在“keyword”、“location”、“text”和“target”后附加“data后缀”
preprocess_df = preprocess_df.join(bow,lsuffix='_data')

# 为合作者保存词袋表示
preprocess_df.to_csv("data/train_preprocessed.csv",index=True,index_label='id')

拆分为训练和验证数据

既然我们已经预先处理了我们的数据,在我们开始使用它来训练我们选择的模型之前,还有最后一步。我们把它分成训练集和验证集,根据类的分布进行分层。我们使用sklearn.model_selection:

代码语言:javascript
复制
from sklearn.model_selection import train_test_split

# stratify=y创建一个平衡的验证集
y = preprocess_df['target_data']

df_train, df_val = train_test_split(preprocess_df, test_size=0.10, random_state=101, stratify=y)

# 保存csv文件
df_train.to_csv("data/train_preprocessed_split.csv",index=True)
df_val.to_csv("data/val_preprocessed_split.csv",index=True)

print(df_train.shape, df_val.shape)

(6851, 21330) (762, 21330)

为了确定,我们可以检查一下余额。

代码语言:javascript
复制
# 检查余额
print(f"""
Ratio of target=1 to target=0 tweets in:\\n 
Original data set = {np.sum(preprocess_df['target_data']==1)/np.sum(preprocess_df['target_data']==0)},

\\n
Training data set = {np.sum(df_train['target_data']==1)/np.sum(df_train['target_data']==0)},

\\n
Validation data set = {np.sum(df_val['target_data']==1)/np.sum(df_val['target_data']==0)}""")

打印:

代码语言:javascript
复制
Ratio of target=1 to target=0 tweets in:
 
Original data set = 0.7533394748963611,

Training data set = 0.7535193242897363,

Validation data set = 0.7517241379310344

准备出发

预处理与调整超参数

如果你看过其他NLP预处理教程,你会发现它们的许多步骤都作为考虑因素包括在内,但在这里没有实现。其中包括删除标点、数字和停用词。但是,我们的训练数据集很小,因此,我们没有在预处理阶段消除这些数据,而是将它们作为调整模型超参数的可能方法。

可能的扩展

通过本教程,我们已经将tweet预处理成词袋表示。但是,你可以选择使用TFIDF进一步研究。

在本教程中,我们忽略了位置和关键字,只关注tweets。你可以考虑根据相似性来编码位置,考虑同一个地方的不同拼写(例如USA vs U.S.),以及缺失的值。还可以将关键字的权重加重,并查看这对模型的性能有何影响。

最后,URL中可能有我们遗漏的有价值的信息。鉴于它们是缩写形式,我们无法单独从文本数据中提取域名或页面内容。你可以考虑建立一个算法来访问站点,提取域名,以及在页面上爬取相关元素(例如页面标题)。

下一步行动

现在我们已经探索并预处理了数据集,现在是时候在它们上尝试机器学习模型了!此类分类问题的可能模型包括logistic回归、神经网络和支持向量机。

参考引用

[1] Kaggle, Disaster tweets classification challenge on Kaggle (2020), Kaggle

[2] D. Becker and M. Leonard, Intro to NLP (n.d.), Natural Language Processing Course on Kaggle

[3] D. Becker and M. Leonard, Text Classification with SpaCy (n.d.), Natural Language Processing Course on Kaggle

[4] Yse, D. L. Your Guide to Natural Language Processing (2019), Towards Data Science

[5] Explosion AI, spaCy’s 101: Everything you need to know (n.d.), spaCy

✄------------------------------------------------

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 磐创AI 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 数据探索
  • NLP预处理
    • 词袋
      • 标识化
        • 词根还原
          • 词形还原
          • 在推特的背景下
            • 标准化为小写
              • 标点符号
                • 停用词
                  • 数字
                    • 提及
                      • 标签
                        • 网址
                        • 使用NLP的spaCy库
                          • spaCy对tweets有多好
                            • 修改spaCy
                              • 预处理算法
                                • 设置
                                • 拆分为训练和验证数据
                                • 准备出发
                                  • 预处理与调整超参数
                                    • 可能的扩展
                                      • 下一步行动
                                        • 参考引用
                                        相关产品与服务
                                        NLP 服务
                                        NLP 服务(Natural Language Process,NLP)深度整合了腾讯内部的 NLP 技术,提供多项智能文本处理和文本生成能力,包括词法分析、相似词召回、词相似度、句子相似度、文本润色、句子纠错、文本补全、句子生成等。满足各行业的文本智能需求。
                                        领券
                                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档