项目简介
Twitter是一家美国社交网络及微博客服务的网站,致力于服务公众对话。迄今为止,Twitter的可货币化日活跃用户达1.86亿。与此同时,Twitter也已成为突发紧急情况时人们的重要沟通渠道。由于智能手机无处不在,人们可以随时随地发布他们正在实时观察的紧急情况。 因此,越来越多的救灾组织和新闻机构对通过程序方式监视Twitter产生了兴趣。
但是,我们并不清楚一个用户在推特上发布的推文是否是真实的正在发生的灾难。 举个例子:
作者明确使用了“ABLAZE”一词,但仅仅是为了形容火烧云,并非真正的火焰燃烧。这对于人类来说是显而易见的,但是对于机器来说便很难分辨该用户是否正在预告真实发生的火灾。
因此,在这个项目中,我将建立一个预测推文发布灾难真实性的机器学习模型,该模型可以预测哪些推文发布的是真实发生的灾难,哪些是虚假的灾难,从而为相关组织网络监测灾难发生及救援提供帮助。
数据集介绍
该数据集共包含三个文件,分别为:
train.csv — 训练集
test.csv — 测试集
sample_submission.csv。— 样本提交文件
数据集中包含的列:
列名 解释
id 每条推文的唯一标识符
text 推特的内容
location 推文发送的位置(可以为空白)
keyword 推文中的特定关键字(可以为空白)
target 仅在train.csv中存在,表示推文中描述灾难的真假,真实为1,虚假为0
其中,测试集包含7613个样本,训练集包含3263个样本。
1.导入库
首先导入本次建模需要的相关库,其中主要包括以下四个部分:
数据处理相关
import numpy as np #数据计算与处理
import pandas as pd #数据计算与处理
import re #用于字符串的匹配,调用方法
自然语言处理工具
import nltk
from nltk.corpus import stopwords #停用词表
from nltk.stem.porter import PorterStemmer #词干提取
from collections import Counter #统计频数
可视化工具
import seaborn as sns
import matplotlib.pyplot as plt
import cufflinks as cf
#如果使用online模式,那么生成的图形是有限制的,我们这里先设置为offline模式,这样就避免了出现次数限制问题。
cf.go_offline()
from wordcloud import WordCloud, STOPWORDS #词云图
from IPython.core.display import display, HTML #显示功能
import plotly.graph_objects as go
建模相关
from tensorflow.keras.preprocessing.text import Tokenizer #分词器
from tensorflow.keras.preprocessing.sequence import pad_sequences
#序列处理
import tensorflow as tf #神经网络库
from tqdm import tqdm #时间处理
2.加载数据并查看
运用pandas分别读取训练集,测试集等。
train = pd.read_csv("D:/机器学习/NLP/nlp-getting-started/train.csv")
test = pd.read_csv("D:/机器学习/NLP/nlp-getting-started/test.csv")
submission = pd.read_csv("D:/机器学习/NLP/nlp-getting-started/sample_submission.csv")
查看训练集前五行
train.head()
结果如下:
查看测试集前五行
test.head()
结果如下:
3.数据可视化
首先,运用plotly绘制真实虚假灾难数量对比的饼图,该饼图描述了正例反例在训练样本中的占比,检验数据平衡性。
#计算0,1有在target列中有多少重复值
counts_train = train.target.value_counts(sort=False)
labels = counts_train.index
values_train = counts_train.values
#输入饼图数据
data = go.Pie(labels=labels, values=values_train ,pull=[0.03, 0])
#设置标题
layout = go.Layout(title='Comparing Tweet is a real disaster (1) or not (0) in %')
#绘制饼图
fig = go.Figure(data=[data], layout=layout) #设置饼图的显示,标签,百分比,值
fig.update_traces(hole=.3, hoverinfo="label+percent+value") #在饼图的中心添加注释
fig.update_layout(annotations=
[dict(text='Train',
x=0.5, y=0.5,
font_size=20,
showarrow=False)])
#显示饼图
fig.show()
运行结果如下:
由饼图可知,在训练集中,灾难真实发生的样本(1)共有3271个,占总体的43%,非真实发生的样本(0)有4342个,占总体的57%。总体来说比例较为均衡,可以进行后续建模。
然后,为了进一步了解灾难推文的大致内容,本文利用wordcloud绘制推文的内容的词云。
首先自行将一些网络常用词加入停用词表。
STOPWORDS.add('https')
STOPWORDS.add('co')
STOPWORDS.add('amp')
然后定义绘制词云图的函数:
#定义绘制词云函数
def Plot_world(text):
comment_words = ' '
stopwords = set(STOPWORDS) #创建停用词元素集
#遍历text中的值,将每个值转换成字符串并分割
for val in text:
val = str(val)
tokens = val.split()
#遍历分割后的字符串,并将每个词转换成小写
for i in range(len(tokens)):
tokens[i] = tokens[i].lower()
#遍历字符串中的所有词,将每个词用空格隔开
for words in tokens:
comment_words = comment_words + words + ' '
#设置词云格式并生成词云
wordcloud = WordCloud(width = 4000, height = 3000, #宽高
background_color ='white', #背景色
stopwords = stopwords, #停用词
min_font_size = 10 #最小字号
).generate(comment_words)
#绘制词云
plt.figure(figsize = (12, 12),
facecolor = 'white',
edgecolor = 'white' )
plt.imshow(wordcloud)
plt.axis("off") #不显示坐标轴
plt.tight_layout(pad = 0) #图像自动调整,设置间距
#显示词云
plt.show()
运行该函数绘制text词云
text = train.text.values
Plot_world(text)
结果如下:
4.数据清洗
众所周知,在进行建模之前,我们必须首先进行数据的预处理,以便于后续进一步处理。因此,本文将进行一些基本的网络文本清理工作,例如去除网址,去除网络标签,删除标点符号,删除常用表情符号,拼写矫正等。
1. 删除网址
#定义去除网址函数
def remove_URL(text):
url = re.compile(r'https?://\S+|www\.\S+')
return url.sub(r'',text)
#删除text列中的网址
train['text']=train['text'].apply(lambda x : remove_URL(x))
2. 删除HTML标签
#定义删除HTML标签函数
def remove_html(text):
html=re.compile(r'<.*?>')
return html.sub(r'',text)
#删除text列中的HTML标签
train['text']=train['text'].apply(lambda x : remove_html(x))
3. 删除表情符号
首先,搜索推特中常用的表情符号,查询他们的代码点(https://emojipedia.org/relieved-face/),将需要删除的表情符号记录。然后开始编写代码。
#定义删除表情符号的函数
def remove_emoji(text):
emoji_pattern = re.compile("["
#表情(😀 --> 🙏)
u"\U0001F600-\U0001F64F"
#符号和象形文字 (🌀 --> 🗿)
u"\U0001F300-\U0001F5FF"
#交通工具和地图(🚀-->🧿)
u"\U0001F680-\U0001F6FF"
#各国国旗
u"\U0001F1E0-\U0001F1FF"
#☀ -->⛿
u"\U00002702-\U000027B0"
#Ⓜ-->🆎
u"\U000024C2-\U0001F251"
"]+", flags=re.UNICODE)
return emoji_pattern.sub(r'', text)
#删除text列中的表情符号
train['text']=train['text'].apply(lambda x: remove_emoji(x))
4. 删除标点符号
import string
#定义删除标点符号的函数
def remove_punct(text):
table=str.maketrans('','',string.punctuation)
return text.translate(table)
#删除text列中的标点符号
train['text']=train['text'].apply(lambda x : remove_punct(x))
5. 拼写矫正
from spellchecker import SpellChecker
spell = SpellChecker()
#定义拼写校正函数
def correct_spellings(text):
corrected_text = []
misspelled_words = spell.unknown(text.split())
for word in text.split():
if word in misspelled_words:
corrected_text.append(spell.correction(word))
else:
corrected_text.append(word)
return " ".join(corrected_text)
#修正text列中拼写错误的词
train['text']=train['text'].apply(lambda x : correct_spellings(x))
5.文本的向量化处理
1. 文本序列化
通过tensorflow自带的Tokenizer分词器设置训练集text中所有词的索引值,然后将词映射为其索引值。
#学习字典
from tensorflow.keras.preprocessing.text import Tokenizer
tokenizer = Tokenizer() #分词器
tokenizer.fit_on_texts(train.text) #用text内容学习字典
word_index = tokenizer.word_index #将词映射为索引值
查看text中独立词汇的数量:
print('Number of unique words:',len(word_index))
输出结果:Number of unique words: 14666
#将训练集中的文本转化为序列
training_sequences = tokenizer.texts_to_sequences(train.text)
#设置每个text的长度为20
MAX_LEN=20
#处理序列
training_padded = pad_sequences(training_sequences,
#设置序列长度
maxlen=MAX_LEN,
#需要补充时确定补0的位置:结尾
padding='post',
#需要截断时确定截断位置:结尾
truncating='post')
2. 嵌入GloVe字典
在这里我们将使用GloVe预训练的语料库模型来表示我们的单词。GloVe模型用于进行词的向量化表示,使得向量之间尽可能多地蕴含语义和语法的信息。我们需要输入语料库,然后通过该模型输出词向量。该方法的原理是首先基于语料库构建词的共现矩阵,然后基于共现矩阵和GloVe模型学习词向量。GloVe语料库共有3种类型:50D,100D和200 D。在这里我们将使用100D。
embedding_dict={}
with open('D:/机器学习/NLP/IMDB/glove.6B.100d.txt','r',encoding='utf-8') as f:
for line in f:
values=line.split()
word=values[0]
vectors=np.asarray(values[1:],'float32')
embedding_dict[word]=vectors
f.close()
3. 匹配GloVe向量
num_words=len(word_index)+1
embedding_dim=100
embedding_matrix = np.zeros((num_words, embedding_dim))
#遍历词索引中的编号及对应的词
for word, i in tqdm(word_index.items()):
if i < num_words:
#返回glove字典中相应的词对应的词向量
embedding_vector = embedding_dict.get(word)
if embedding_vector is not None:
#在嵌入索引中找不到的单词将为全零。
embedding_matrix[i] = embedding_vector
#查看字典结构
embedding_matrix.shape
输出结果:
100%|██████████| 14666/14666 [00:00<00:00, 599653.57it/s]
(14667, 100)
6.建立模型
从2014年起,NLP 的主流方法转移到非线性的神经网络方法,从而输入也从稀疏高维特征向量变为低位稠密特征向量。神经网络不仅像传统机器学习方法一样学习预测,同时也学习如何正确表达数据,即在处理输入输出对时,网络内部产生一系列转化以处理输入数据来预测输出数据。
因此,本次建模以神经网络为基础,设置了Embedding层,Dropout层,LSTM层,以及全连接层。
1. 定义模型函数
导入神经网络相关库:
from keras.models import Sequential
from keras.layers import Embedding,Dense,Dropout,LSTM
from keras import optimizers,initializers
定义模型函数:
def create_model():
model = Sequential()
添加Embedding层以初始化GloVe模型训练出的权重矩阵。input_dim即词汇量,输入数组中的词典大小是14666,即有14666个不同的词,所以我的input_dim便要比14666要大1,output_dim是密集嵌入的尺寸,就如同CNN最后的全连接层一样,上面设置的100,便将每一个词变为用1x100来表示的向量,embeddings_initializer为嵌入矩阵的初始化的方法,为预定义初始化方法名的字符串,或用于初始化权重的初始化器。输入序列的长度设置为序列长度20,将每个text表示为一个20×100的矩阵
#嵌入层
model.add(Embedding(input_dim=num_words,
output_dim=100,
embeddings_initializer=initializers.Constant
(embedding_matrix),
input_length=MAX_LEN,trainable=False))
#正则化,防止过拟合
model.add(Dropout(0.2))
由于文本数据的连续性,我们添加LSTM层。LSTM是作为短期记忆的解决方案而创建的长短期记忆模型,和RNN类似,只是当词向量作为输入进入神经元后,神经元会按照某种方式对该词向量的信息进行选择,存储成新的信息,输入到相邻的隐藏层神经元中去。
#LSTM层
model.add(LSTM(64, dropout=0.2, recurrent_dropout=0.2))
#全连接层
model.add(Dense(1, activation='sigmoid'))
#编译模型
model.compile(loss='binary_crossentropy', #一般搭配sigmoid
optimizer='adam', #优化器
metrics=['accuracy']) #模型评估标准
return model
#模型摘要
model=create_model()
model.summary()
输出结果:
2. 参数选择与调优
本文运用交叉验证的方法得到验证集精度,并以此为标准,使用网格搜索来确定最优超参数。
Scikit-Learn里有一个API 为model.selection.GridSearchCV,可以将keras搭建的模型传入,作为sklearn工作流程一部分。因此,我们运用此函数来包装keras模型以便在scikit-learn中使用keras。
from keras.wrappers.scikit_learn import KerasClassifier
from sklearn.model_selection import GridSearchCV
#包装keras模型以便在scikit-learn中使用keras,该模型用作scikit-learn中的estimator
model = KerasClassifier(build_fn=create_model, verbose=0)
#定义网格搜索超参数
batch_size = [5, 10, 50, 100]
epochs = [5, 10, 20, 50]
#参数字典
param_grid = dict(batch_size=batch_size, nb_epoch=epochs)
#网格搜索,5折交叉验证
grid = GridSearchCV(estimator=model, param_grid=param_grid, cv=5,
n_jobs=1)
3. 模型训练
#开始训练
grid_result = grid.fit(training_padded, train['target'].values)
#查看不同参数组合训练结果信息
results= pd.DataFrame(grid_result.cv_results_)
results.head(16)
输出结果:
#图像显示
results[['mean_train_score','mean_test_score']].plot(
ylim=[0.75,0.85])
输出结果:
由训练过程可知,验证集准确率最高的参数组合为第五次训练时的参数,batch_size=10 , epochs=10。
4. 模型评估
#查看最终参数选择的结果和交叉验证的结果
print("交叉验证平均准确率:\n", grid_result.best_score_)
print("模型的最优参数:\n", grid_result.best_params_)
输出结果:
交叉验证平均准确率: 0.7921975544580065
最好的参数模型: {‘batch_size’: 10, ‘nb_epoch’: 10}
5. 与传统机器学习模型对比
将train中训练集数据划分为训练集和验证集,然后对比其在验证集上的准确率。
#划分训练集验证集
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test=train_test_split(training_padded,
train['target'].values,test_size=0.2)
分别使用SVM,KNN,多层感知器,朴素贝叶斯,随机森林,GBDT等方法构建模型,与神经网络进行对比。
#导入库
from sklearn.feature_extraction.text import CountVectorizer,
TfidfVectorizer
from sklearn.model_selection import train_test_split
from sklearn import svm,neighbors,neural_network,naive_bayes,
ensemble
from sklearn import metrics
from sklearn.metrics import classification_report
from sklearn.model_selection import *
#训练模型
model1= svm.SVC()
model1.fit(X_train,y_train)
predict1=model1.predict(X_test)
model2=neighbors.KNeighborsClassifier()
model2.fit(X_train,y_train)
model3=neural_network.MLPClassifier()
model3.fit(X_train,y_train)
model4=naive_bayes.GaussianNB()
model4.fit(X_train,y_train)
model5=ensemble.RandomForestClassifier()
model5.fit(X_train,y_train)
model6=ensemble.GradientBoostingClassifier()
model6.fit(X_train,y_train)
#模型评估
print('模型1验证集准确率:',model1.score(X_test, y_test))
print('模型2验证集准确率:',model2.score(X_test, y_test))
print('模型3验证集准确率:',model3.score(X_test, y_test))
print('模型4验证集准确率:',model4.score(X_test, y_test))
print('模型5验证集准确率:',model5.score(X_test, y_test))
print('模型6验证集准确率:',model6.score(X_test, y_test))
输出结果:
模型1验证集准确率: 0.6250820748522653
模型2验证集准确率: 0.5843729481286933
模型3验证集准确率: 0.5384110308601444
模型4验证集准确率: 0.4799737360472751
模型5验证集准确率: 0.6323046618516087
模型6验证集准确率: 0.6401838476690742
以上模型的验证集准确率与神经网络的验证集准确率相比差别较大,可见本文基于神经网络的文本预测模型是相对准确且可靠的。
7.结果预测
我们继续通过上述步骤构建的神经网络模型预测test测试集中的target列。
#将test中的文本转化为索引序列
testing_sequences = tokenizer.texts_to_sequences(test.text)
testing_padded = pad_sequences(testing_sequences, maxlen=MAX_LEN, padding='post', truncating='post')
#预测
predictions = grid_result.predict(testing_padded)
submission['target'] = (predictions > 0.5).astype(int)
submission
输出结果:
#将结果储存到submission文件
submission.to_csv("submission.csv", index=False, header=True)