Streamlit实战twitter情感分析

Streamlit是一个出色的机器学习工具开发库,这个教程将学习如何使用streamlit和flair开发一个twitter微博情感分析的应用。

相关链接:Streamlit开发手册

1、streamlit概述

并不是每个人都是数据科学家,但是每个人都需要数据科学带来的力量。Streamlit帮我们解决了这个问题,利用streamlit部署机器学习模型简单到只需要几个函数调用。

例如,如果运行下面的代码:

1import streamlit as st 2 3x = st.slider('Select a value') 4st.write(x, 'squared is', x * x)

Streamlit就会创建出像下面这样的滑杆输入:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nTdMScgX-1580353226171)(streamlit-twitter/slider.png)]

安装Streamlit也很简单:

pip3 install streamlit

然后你就可以运行应用了:

streamlit run <FILE>

注意,直接用python运行你的streamlit文件是不行的!

本文中的代码可以到这里下载。

2、情感分类

情感分类是自然语言处理(NLP)中的一个经典问题,目的是判断一个语句的情感倾向是积极(Positive)还是消极(Negative)。

例如,“I love Python!”这句话应当被归类为Positive,而“Python is the worst!”则应当被归类为Negative。

3、Flair开发库

很多流行的机器学习开发库都提供了情感分类器的实现,从简单和效果方面考虑,在这个教程里我们使用Flair,一个顶级的NLP分类器开发包。

可以执行如下命令安装Flair:

pip3 install flair

4、Sentiment140数据集

任何数据科学项目都需要数据集,Sentiment140数据集是我们这个项目的绝配。该数据集包含了160万条标注好的tweet微博,标注0表示消极,4表示积极。

可以从这里下载Sentiment140数据集

5、数据载入及预处理

一旦下载好Sentiment140数据集,就可以使用如下代码载入数据:

1import pandas as pd 2 3col_names = ['sentiment','id','date','query_string','user','text'] 4data_path = 'training.1600000.processed.noemoticon.csv' 5 6tweet_data = pd.read_csv(data_path, header=None, names=col_names, encoding="ISO-8859-1").sample(frac=1) # .sample(frac=1) shuffles the data 7tweet_data = tweet_data[['sentiment', 'text']] # Disregard other columns 8print(tweet_data.head())

运行上面的代码将输出如下结果:

1 sentiment text 21459123 4 @minalpatel Any more types of glassware you'd... 3544833 0 I was a bit puzzled as to why it seemed to it... 4398665 0 Yay...my car is ready....Was about 2500 miles... 5708548 0 @JoshEJosh How ya been? I MISS you! 6264000 0 @MrFresh0587 yeah i know. well...i'm going to...

不过,因为我们使用 .sample(frac=1)随机打乱了数据的先后次序,你得到的结果可能略有不同。

现在数据还很乱,我们先进行预处理:

1import re 2 3allowed_chars = ' AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789~`!@#$%^&*()-=_+[]{}|;:",./<>?' 4punct = '!?,.@#' 5maxlen = 280 6 7def preprocess(text): 8 return ''.join([' ' + char + ' ' if char in punct else char for char in [char for char in re.sub(r'http\S+', 'http', text, flags=re.MULTILINE) if char in allowed_chars]])[:maxlen]

上面的函数略为有点乏味,但是简而言之,这段代码的目的是剔除文本中所有不能识别的字符、链接等并截断为280个字符。有更好的办法来进行链接清理等预处理,不过我们这里就用最朴素的方法了。

Flair对数据格式有特定的要求,看起来是这样:

__label__<LABEL>    <TEXT>

在我们的微博情感分析应用中,数据整理后应该是这样:

1__label__4 <PRE-PROCESSED TWEET> 2__label__0 <PRE-PROCESSED TWEET> 3...

为此,我们需要三个步骤:

1、执行预处理函数

tweet_data['text'] = tweet_data['text'].apply(preprocess)

2、在每个情感标记前添加__label__前缀

tweet_data['sentiment'] = '__label__' + tweet_data['sentiment'].astype(str)

3、保存数据

1import os 2 3# Create directory for saving data if it does not already exist 4data_dir = './processed-data' 5if not os.path.isdir(data_dir): 6 os.mkdir(data_dir) 7 8# Save a percentage of the data (you could also only load a fraction of the data instead) 9amount = 0.125 10 11tweet_data.iloc[0:int(len(tweet_data)*0.8*amount)].to_csv(data_dir + '/train.csv', sep='\t', index=False, header=False) 12tweet_data.iloc[int(len(tweet_data)*0.8*amount):int(len(tweet_data)*0.9*amount)].to_csv(data_dir + '/test.csv', sep='\t', index=False, header=False) 13tweet_data.iloc[int(len(tweet_data)*0.9*amount):int(len(tweet_data)*1.0*amount)].to_csv(data_dir + '/dev.csv', sep='\t', index=False, header=False)

在上面的代码中,你可能注意到了两个问题:

  • 我们仅保存了部分数据。这是因为Sentiment140数据集太大了,如果Flair加载 完整的数据集需要太多的内存。
  • 我们将数据分割为训练集、测试集和开发集。当Flair载入数据时,它需要数据 按这种方法拆分。默认情况下,拆分比例为8-1-1,即80%的数据进训练集、10% 的数据进测试集、10%的数据进开发集

现在,数据准备好了!

6、基于Flair的文本情感分类实现

在这个教程中,我们仅涉及Flair的基础。如果你需要更多细节,推荐你查看Flair的官方文档。

首先我们用Flair的NLPTaskDataFetcher 类载入数据:

1from flair.data_fetcher import NLPTaskDataFetcher 2from pathlib import Path 3 4corpus = NLPTaskDataFetcher.load_classification_corpus(Path(data_dir), test_file='test.csv', dev_file='dev.csv', train_file='train.csv')

然后我们构造一个标签字典来记录语料库中分配给文本的所有标签:

label_dict = corpus.make_label_dictionary()

现在可以载入Flair内置的GloVe词嵌入了:

1from flair.embeddings import WordEmbeddings, FlairEmbeddings 2 3word_embeddings = [WordEmbeddings('glove'), 4# FlairEmbeddings('news-forward'), 5# FlairEmbeddings('news-backward') 6 ]

注释掉的两行代码是Flair提供的选项,用于得到更好的效果,不过我的内存有限,因此无法进行测试。

载入词嵌入向量后,用下面的代码进行初始化:

1from flair.embeddings import DocumentRNNEmbeddings 2 3document_embeddings = DocumentRNNEmbeddings(word_embeddings, hidden_size=512, reproject_words=True, reproject_words_dimension=256)

现在整合词嵌入向量和标签字典,得到一个TextClassifier模型:

1from flair.models import TextClassifier 2 3classifier = TextClassifier(document_embeddings, label_dictionary=label_dict)

接下来我们可以创建一个ModelTrainer实例来用我们的语料库训练模型:

1from flair.trainers import ModelTrainer 2 3trainer = ModelTrainer(classifier, corpus)

一旦开始训练,我们需要等一会儿了:

1trainer.train('model-saves', 2 learning_rate=0.1, 3 mini_batch_size=32, 4 anneal_factor=0.5, 5 patience=8, 6 max_epochs=200)

在模型训练完之后,可以使用如下的代码进行测试:

1from flair.data import Sentence 2 3classifier = TextClassifier.load('model-saves/final-model.pt') 4 5pos_sentence = Sentence(preprocess('I love Python!')) 6neg_sentence = Sentence(preprocess('Python is the worst!')) 7 8classifier.predict(pos_sentence) 9classifier.predict(neg_sentence) 10 11print(pos_sentence.labels, neg_sentence.labels)

你应该可以得到类似下面这样的结果:

[4 (0.9758405089378357)] [0 (0.8753706812858582)]

看起来预测是正确的!

7、抓取twitter微博

不错,现在我们有了一个可以预测单条tweet的感情色彩是积极或消极。不过这还不是太有用,那么应该怎么改进?

我的想法是抓取指定查询条件的最新tweet微博,逐个进行情感分类,然后计算积极/消极的比率。

我个人喜欢用twitterscraper来抓twitter微博,虽然它不算快,但你可以绕过twitter设置的请求限制。用下面的命令安装twitterscraper:

pip3 install twitterscraper

安装好了。稍后我们再进行具体的抓取。

8、编写Streamlit脚本

创建一个新的文件main.py,然后先引入一些模块:

1import datetime as dt 2import re 3 4import pandas as pd 5import streamlit as st 6from flair.data import Sentence 7from flair.models import TextClassifier 8from twitterscraper import query_tweets

接下来,我们可以进行一些基本的处理,例如设置页面标题、载入分类模型:

1# Set page title 2st.title('Twitter Sentiment Analysis') 3 4# Load classification model 5with st.spinner('Loading classification model...'): 6 classifier = TextClassifier.load('models/best-model.pt')

with st.spinner这部分代码块让我们可以在加载分类模型时给用户一个进度提示。

接下来我们可以复制之前写的预处理函数:

1import re 2 3allowed_chars = ' AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789~`!@#$%^&*()-=_+[]{}|;:",./<>?' 4punct = '!?,.@#' 5maxlen = 280 6 7def preprocess(text): 8 return ''.join([' ' + char + ' ' if char in punct else char for char in [char for char in re.sub(r'http\S+', 'http', text, flags=re.MULTILINE) if char in allowed_chars]])[:maxlen]

我们首先实现单个tweet微博的分类:

1st.subheader('Single tweet classification') 2 3tweet_input = st.text_input('Tweet:')

只要输入文本不是空的,我们就进行如下处理:

  • 预处理tweet微博

  • 进行预测

  • 显式预测结果

    if tweet_input != '': # Pre-process tweet sentence = Sentence(preprocess(tweet_input))

    1# Make predictions 2with st.spinner('Predicting...'): 3 classifier.predict(sentence) 4 5# Show predictions 6label_dict = {'0': 'Negative', '4': 'Positive'} 7 8if len(sentence.labels) > 0: 9 st.write('Prediction:') 10 st.write(label_dict[sentence.labels[0].value] + ' with ', 11 sentence.labels[0].score*100, '% confidence')

使用st.write可以写入任何文本,甚至可以直接显式Pandas数据帧。

好了,现在可以运行:

streamlit run main.py

结果看起来是这样:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jHXAU8vX-1580353226173)(streamlit-twitter/ui-1.png)]

接下来我们可以实现之前的想法了:搜索某个主题的twitter微博并计算情感正负比。

1st.subheader('Search Twitter for Query') 2 3# Get user input 4query = st.text_input('Query:', '#') 5 6# As long as the query is valid (not empty or equal to '#')... 7if query != '' and query != '#': 8 with st.spinner(f'Searching for and analyzing {query}...'): 9 # Get English tweets from the past 4 weeks 10 tweets = query_tweets(query, begindate=dt.date.today() - dt.timedelta(weeks=4), lang='en') 11 12 # Initialize empty dataframe 13 tweet_data = pd.DataFrame({ 14 'tweet': [], 15 'predicted-sentiment': [] 16 }) 17 18 # Keep track of positive vs. negative tweets 19 pos_vs_neg = {'0': 0, '4': 0} 20 21 # Add data for each tweet 22 for tweet in tweets: 23 # Skip iteration if tweet is empty 24 if tweet.text in ('', ' '): 25 continue 26 # Make predictions 27 sentence = Sentence(preprocess(tweet.text)) 28 classifier.predict(sentence) 29 sentiment = sentence.labels[0] 30 # Keep track of positive vs. negative tweets 31 pos_vs_neg[sentiment.value] += 1 32 # Append new data 33 tweet_data = tweet_data.append({'tweet': tweet.text, 'predicted-sentiment': sentiment}, ignore_index=True)

最后,我们显示采集的数据:

1try: 2 st.write(tweet_data) 3 # Show positive to negative tweet ratio 4 try: 5 st.write('Positive to negative tweet ratio:', pos_vs_neg['4']/pos_vs_neg['0']) 6 except ZeroDivisionError: # if no negative tweets 7 st.write('All postive tweets') 8except NameError: # if no queries have been made yet 9 pass

再次运行应用,结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SGQ1cET0-1580353226173)(streamlit-twitter/ui-2.png)]

下面我们完整的streamlit应用脚本:

1import datetime as dt 2import re 3 4import pandas as pd 5import streamlit as st 6from flair.data import Sentence 7from flair.models import TextClassifier 8from twitterscraper import query_tweets 9 10# Set page title 11st.title('Twitter Sentiment Analysis') 12 13# Load classification model 14with st.spinner('Loading classification model...'): 15 classifier = TextClassifier.load('models/best-model.pt') 16 17# Preprocess function 18allowed_chars = ' AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789~`!@#$%^&*()-=_+[]{}|;:",./<>?' 19punct = '!?,.@#' 20maxlen = 280 21 22def preprocess(text): 23 # Delete URLs, cut to maxlen, space out punction with spaces, and remove unallowed chars 24 return ''.join([' ' + char + ' ' if char in punct else char for char in [char for char in re.sub(r'http\S+', 'http', text, flags=re.MULTILINE) if char in allowed_chars]]) 25 26### SINGLE TWEET CLASSIFICATION ### 27st.subheader('Single tweet classification') 28 29# Get sentence input, preprocess it, and convert to flair.data.Sentence format 30tweet_input = st.text_input('Tweet:') 31 32if tweet_input != '': 33 # Pre-process tweet 34 sentence = Sentence(preprocess(tweet_input)) 35 36 # Make predictions 37 with st.spinner('Predicting...'): 38 classifier.predict(sentence) 39 40 # Show predictions 41 label_dict = {'0': 'Negative', '4': 'Positive'} 42 43 if len(sentence.labels) > 0: 44 st.write('Prediction:') 45 st.write(label_dict[sentence.labels[0].value] + ' with ', 46 sentence.labels[0].score*100, '% confidence') 47 48### TWEET SEARCH AND CLASSIFY ### 49st.subheader('Search Twitter for Query') 50 51# Get user input 52query = st.text_input('Query:', '#') 53 54# As long as the query is valid (not empty or equal to '#')... 55if query != '' and query != '#': 56 with st.spinner(f'Searching for and analyzing {query}...'): 57 # Get English tweets from the past 4 weeks 58 tweets = query_tweets(query, begindate=dt.date.today() - dt.timedelta(weeks=4), lang='en') 59 60 # Initialize empty dataframe 61 tweet_data = pd.DataFrame({ 62 'tweet': [], 63 'predicted-sentiment': [] 64 }) 65 66 # Keep track of positive vs. negative tweets 67 pos_vs_neg = {'0': 0, '4': 0} 68 69 # Add data for each tweet 70 for tweet in tweets: 71 # Skip iteration if tweet is empty 72 if tweet.text in ('', ' '): 73 continue 74 # Make predictions 75 sentence = Sentence(preprocess(tweet.text)) 76 classifier.predict(sentence) 77 sentiment = sentence.labels[0] 78 # Keep track of positive vs. negative tweets 79 pos_vs_neg[sentiment.value] += 1 80 # Append new data 81 tweet_data = tweet_data.append({'tweet': tweet.text, 'predicted-sentiment': sentiment}, ignore_index=True) 82 83# Show query data and sentiment if available 84try: 85 st.write(tweet_data) 86 try: 87 st.write('Positive to negative tweet ratio:', pos_vs_neg['4']/pos_vs_neg['0']) 88 except ZeroDivisionError: # if no negative tweets 89 st.write('All postive tweets') 90except NameError: # if no queries have been made yet 91 pass

原文链接:Streamlit+Flair开发微博情感分析应用 — 汇智网

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )