自然语言处理——中文——网民疫情情绪情感分析 DataFountain

DataFountain疫情网民情感分类

2019新型冠状病毒(2019-nCoV)感染的肺炎疫情发生对人们生活生产的方方面面产生了重要影响,并引发国内舆论的广泛关注,众多网民参与疫情相关话题的讨论。为了帮助政府掌握真实社会舆论情况,科学高效地做好防控宣传和舆情引导工作,本赛题针对疫情相关话题开展网民情绪识别的任务。给定微博ID和微博内容,设计算法对微博内容进行情绪识别,判断微博内容是积极的、消极的还是中性的。数据集依据与“新冠肺炎”相关的230个主题关键词进行数据采集,抓取了2020年1月1日—2020年2月20日期间共计100万条微博数据,并对其中10万条数据进行人工标注,标注分为三类,分别为:1(积极),0(中性)和-1(消极)。

本次数据集获取方式:
关注:YOLO的学习进阶日常
回复:DF

数据读取

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
plt.style.use('seaborn')
sns.set(font_scale=2)
train_df = pd.read_csv('D:\\AA\\C\\deepmind\\NLP\\DF\\data\\nCoV_100k_train.labled.csv',engine ='python')
test_df  = pd.read_csv('D:\\AA\\C\\deepmind\\NLP\\DF\\data\\nCov_10k_test.csv',engine ='python')

数据预处理

这一步十分的重要,提前对数据有一些了解,更熟悉业务,好的数据有利于模型的建立,可能会有很多异常处理

异常值检测

train_df['情感倾向'].value_counts().plot.bar()
plt.title('sentiment(target)')

在这里插入图片描述

从上面可以看出来,原本应该只有0,-1,1三种值,但是这里却有很多其他值,因此我们需要把这些对我们有用的数据进行提取,从图中还可以看出来大部分人都是保持中性的态度。

train_df = train_df[train_df['情感倾向'].isin(['0','1','-1'])]

重复值处理

有的ID很有可能会耍水贴,所以这里要对重复值也进行处理
drop_duplicates()去除重复值,只保留一条

train_df['微博id'].value_counts().head(10)
train_df=train_df.drop_duplicates(['微博id'])
train_df['微博id'].value_counts().head(5)
4458020735796410    1
4467956547167580    1
4462158534903640    1
4463231437677040    1
4465820153259820    1
Name: 微博id, dtype: int64

观察数据规律(时间序列分析)

train_df['time'] = pd.to_datetime('2020年' + train_df['微博发布时间'], format='%Y年%m月%d日 %H:%M', errors='ignore')
train_df['month'] =  train_df['time'].dt.month
train_df['day'] =  train_df['time'].dt.day
train_df['dayfromzero']  = (train_df['month']-1)*31 +  train_df['day']
fig, ax = plt.subplots(1, 2, figsize=(16, 8))
sns.kdeplot(train_df.loc[train_df['情感倾向'] == '0', 'dayfromzero'], ax=ax[0], label='sent(0)')
sns.kdeplot(train_df.loc[train_df['情感倾向'] == '1', 'dayfromzero'], ax=ax[0], label='sent(1)')
sns.kdeplot(train_df.loc[train_df['情感倾向'] == '-1', 'dayfromzero'], ax=ax[0], label='sent(-1)')
train_df.loc[train_df['情感倾向'] == '0', 'dayfromzero'].hist(ax=ax[1])
train_df.loc[train_df['情感倾向'] == '1', 'dayfromzero'].hist(ax=ax[1])
train_df.loc[train_df['情感倾向'] == '-1', 'dayfromzero'].hist(ax=ax[1])
ax[1].legend(['sent(0)', 'sent(1)','sent(-1)'])
plt.show()

在这里插入图片描述

情感分析关键的特有步骤:对文本长度的判定

因为一般过于长或者过于短的文本对我们来说误差率会有点大。微博长度可以帮助我们在建模深度学习的时候决定如何采用多大的最大长度来截断

train_df['weibo_len'] = train_df['微博中文内容'].astype(str).apply(len)
sns.kdeplot(train_df['weibo_len'])
plt.title('weibo_len')

在这里插入图片描述

从图中可以看出来我们使用150作为文本的分析就是最好的,所以等一下深度学习的时候长度我会设成150!

理解数据

观察微博图片数量和情感的分布关系

train_df['pic_len'] = train_df['微博图片'].apply(lambda x: len(eval(x)))
train_df['pic_len'].value_counts().plot.bar()
plt.title('pic_len(target)')

在这里插入图片描述

sns.countplot(x='pic_len', hue='情感倾向',data=train_df)
plt.show()

在这里插入图片描述

从图中可以看出,大部分的人都还是文字居多,图片有一张或者没有,在数据预处理和初探之后就要进行分词词向量处理了
词向量经历的过程:

  1. 简单的词袋模型(把词放在一个袋子里面)不含语义,语义局限于字面相同与否,只有字词一毛一样的时候才会有一样的词向量
  2. 之后就有了word2Vec,特点:词的特征表达有了聚类性质和线性性质,弱点:对于一次多义的现象很难识别
    3.所以就有了预训练语言模型:现在都是用的transform这种比较高级的算法,eg:ELMo,GPT,Bert,Roberta 去达到一个比较好的模型架构 可以尝试一下自己加一个网路结构去进行微调,达到端到端的目的。将data分为两个部分,一个部分为预训练模型,一个部分用来微调数据,特点:通过大量预料的无监督与训练,可嵌入上下文语义。问题:计算资源需求高,bert跑起来很慢
    这里第一种方式就是用传统的baseline

词袋模型+逻辑回归

在前面的英文分词我也说过了其实大部分分类模型逻辑回归的效果都很好,所以这里就用最简单的词袋模型和逻辑回归开始第一步。

import jieba
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.linear_model import LogisticRegression
train_df['text_cut'] = train_df['微博中文内容'].apply(lambda x:" ".join(jieba.cut(str(x))))
count_vect = CountVectorizer(analyzer='word', token_pattern=r'\w{1,}')
count_vect.fit(train_df['text_cut'])
xtrain_count =  count_vect.transform(train_df['text_cut'])
lr = LogisticRegression()
lr.fit(xtrain_count, train_df['情感倾向'] )
test_df['text_cut'] = test_df['微博中文内容'].apply(lambda x:" ".join(jieba.cut(str(x))))
xtest_count =  count_vect.transform(test_df['text_cut'])
test_sub = lr.predict(xtest_count)
print(test_sub)
['-1' '0' '0' ... '0' '0' '0']

词向量和神经网络

from keras.preprocessing.text import Tokenizer :这个库主要是作为分词器,允许通过将每个文本转换为整数序列或转换为矢量
他和基于统计的分词还有字符串匹配的分词方式有所不同,这是基于神经网络的分词

keras.preprocessing.text.Tokenizer(num_words=None,filters=’!"#$%&()*+,-./:;<=>?@[\]^_`{|}~\t\n’, lower=True, split=’ ',char_level=False, oov_token=None, document_count=0)
num_words:保留的最大单词数,基于单词频率。仅num_words-1保留最常用的词 一般使用5000
filters:是否过滤掉一些不需要的符号,默认情况下,将删除所有标点符号,从而将文本转换为以空格分隔的单词序列,然后将它们编入索引或向量化
因此使用神经网络的分词方式不需要进行正则表达式的数据预处理,传统模型baseline如果有兴趣可以给我评论
lower:因为会涉及一些英文字母,为了担心转换成适量的0,1时将单词错义这里需要全部转换成小写

from keras.preprocessing.sequence import pad_sequences:这个库的目的是将序列填充到相同的长度
为了实现的简便,keras只能接受长度相同的序列输入。如果序列长度参差不齐,需要将序列转化为经过填充以后的一个长度相同的新序列。

keras.preprocessing.sequence.pad_sequences(sequences, maxlen=None, dtype=‘int32’, padding=‘pre’, truncating=‘pre’, value=0.0)
maxlen设置最大的序列长度,长于该长度的序列将会截短,短于该长度的序列将会填充,一般的长度都是30 看我的第一个自然语言处理的博客我就说过一般的语句长度不会超过30

from keras.utils import to_categorical:将类向量(整数)转换为二进制类矩阵,神经网络只能fit(训练)矩阵,用来将整数型标签转化为one_hot数据。
例如,使用数组将得到的label表示标签[0,1,2,3,4],只有将其转化为one_hot数据后,才能由神经网络进行训练。

keras.utils.to_categorical(y, num_classes=None, dtype=‘float32’)
y是待转换的标签数组。
num_class:标签中共有多少种类。num_class的默认值是标签中最大数+1,是从0开始索引的,因此index在这里应该是3个 如果不加1就只有2个就会报错
dtype:转化的目标数据类型。

from keras.models import Sequential:这里就开始构建神经网络了,这是一个很基础Sequential模型,首先需要定义一个序列模型,然后往里面添加layers(层次)from keras import layers

keras.layers.Embedding(input_dim, output_dim, embeddings_initializer=‘uniform’, embeddings_regularizer=None, activity_regularizer=None, embeddings_constraint=None, mask_zero=False, input_length=None)

该层将正整数(索引)转换为固定大小的密集向量,只能作为构建的第一层,第一层必须由输入层

input_dim:int>0。词汇表的大小,即最大整数索引+ 1
output_dim:int> =0。密集嵌入的尺寸
input_length:表示这个最大的长度是30我前面说过了

layers.Dense:全连接层。在整个卷积神经网络中起到“分类器”的作用。 如果说卷积层、池化层和激活函数层等操作是将原始数据映射到隐层特征空间的话,全连接层则起到将学到的“分布式特征表示”映射到样本标记空间的作用。
activation激励函数
在神经网络中,隐层和输出层节点(就是上一层和下一层之间的函数关系必须需要一个激励函数)的输入和输出之间具有函数关系,这个函数称为激励函数。 常见的激励函数有:线性激励函数、阈值或阶跃激励函数、S形激励函数、双曲正切激励函数和高斯激励函数等
model.compile():将优化器传递给之前实例化优化器
summary:相当于就是形成一个报告的意思,一个总结概括,你能看到当中的参数变化
再进行分类之后就是预测了,先将已经分好类的数据转换成二进制矩阵,在用序列模型去进行训练得到值。

from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
from keras.utils import to_categorical
from keras.models import Sequential
from keras import layers


tokenizer = Tokenizer()
tokenizer.fit_on_texts(list(train_df['text_cut']) + list(test_df['text_cut']))
 
train_x =  tokenizer.texts_to_sequences(train_df['text_cut'])
test_x = tokenizer.texts_to_sequences(test_df['text_cut'])
  
maxlen = 30
train_x = pad_sequences(train_x, padding='post', maxlen=maxlen)
test_x = pad_sequences(test_x, padding='post', maxlen=maxlen)
 
embedding_dim = 50
vocab_size = len(tokenizer.word_index) +1 
model = Sequential()
model.add(layers.Embedding(input_dim=vocab_size,
                          output_dim=embedding_dim,
                          input_length=maxlen))
model.add(layers.Flatten())
model.add(layers.Dense(10, activation='relu'))
model.add(layers.Dense(3, activation='softmax'))
model.compile(optimizer='adam',
             loss='binary_crossentropy',
             metrics=['accuracy'])
model.summary()
 
model.fit(train_x[:300], to_categorical(train_df['情感倾向'].astype(int) + 1)[:300],
         epochs=1,
         batch_size=10)
test_sub = model.predict(test_x)
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_1 (Embedding)      (None, 30, 50)            7923850   
_________________________________________________________________
flatten_1 (Flatten)          (None, 1500)              0         
_________________________________________________________________
dense_1 (Dense)              (None, 10)                15010     
_________________________________________________________________
dense_2 (Dense)              (None, 3)                 33        
=================================================================
Total params: 7,938,893
Trainable params: 7,938,893
Non-trainable params: 0
_________________________________________________________________


D:\ANACONDA\lib\site-packages\tensorflow_core\python\framework\indexed_slices.py:433: UserWarning: Converting sparse IndexedSlices to a dense Tensor of unknown shape. This may consume a large amount of memory.
  "Converting sparse IndexedSlices to a dense Tensor of unknown shape. "


Epoch 1/1
300/300 [==============================] - 6s 20ms/step - loss: 0.6189 - accuracy: 0.6667
print(test_sub)
[[0.3406474  0.35368806 0.30566448]
 [0.33278218 0.3436801  0.32353768]
 [0.36407104 0.42468563 0.21124329]
 ...
 [0.33073366 0.34590626 0.3233601 ]
 [0.35391447 0.48806122 0.15802427]
 [0.3604995  0.40158147 0.23791896]]

在这里插入图片描述
我们团队旨在建设并发布高质量的技术文章和技术社区去储备人才,输送人才,有对大数据人工智能感兴趣的朋友可以加我们的QQ群呀~(我们平时会推送一些免费的课程,每周都有技术分享会欢迎大家参与)

我们的技术社区的网址:https://discourse.qingxzd.com/

评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值