Amazon Comprehend を利用して、Twitter からエゴサーチ #reinvent
ども、藤本です。
AWS re:Invent 2017 にて自然言語処理(Natural Language Processing)サービスの Amazon Comprehend がリリースされました。Amazon Comprehend に関しては下記エントリにまとめてみました。
使ってみて何かスゴいな、という印象は受けるのですが、じゃあ実際に何ができるのでしょうか?ユースケースを考えてみて実装してみました。
概要
Amazon Coprehend はドキュメントから様々な情報を検出することができます。その一つの検出機能に感情の検出があります。感情の検出はPositive
、Negative
、Neutral
、Mixed
という 4つの指標からドキュメントに含まれる感情の検出、およびスコアリングを行ってくれます。この感情の検出を利用して、Twitter のツイートからエゴサーチしてみましょう。
ハッシュタグ#reinvent
で検索した直近10,000件のツイートを抽出し、それぞれ感情の検出を行い、トータルの件数、パーセンテージの算出、および最もポジティブ、ネガティブと判断されたツイートを抽出します。
簡単に図示すると↓のようなイメージ。
エゴサーチしてみた
環境
- 端末:Macbook
- 言語:Python
- Python:3.6.1
- Boto3:1.4.8
- Botocore:1.8.6
- Twitter SDK:python-twitter
ソースコード
ササッと書いたソースコードを Gist に添付しました。ブログ最下部にソースコードを張り付けています。
実行
2017/12/02 23:20頃の結果です。
1 2 3 4 5 6 7 8 | $ time ./sentiment.py Positive : 993件 : 14.0% Negative : 22件 : 2.0% Mixed : 7件 : 1.0% Neutral : 8021件 : 84.0% Most positive tweet is "#reInvent 2017 was an amazingly marvelous experience!! A huge thank you to @awscloud for all the awesomeness!!" Most negative tweet is "xxxxxxxxxxxxxxxxxxxxxxx" ./sentiment.py 7.02s user 0.62s system 3% cpu 3:42.37 total |
Neutral が多いのはしょうがないですが、うん、Negative に比べて、Positive なツイートが圧倒的に多いですね!re:Invent 素晴らしい!行ったことないけど。最も Positive なツイートは限りなく Positive ですね!実装しておいてなんですが、最も Negative なツイートはマスキング。
まとめ
いかがでしたでしょうか? Twitter API も簡単ですし、Amazon Comprehend の API も前回のエントリでまとめた通り簡単に扱うことができ、簡単に実装することができました。是非、日本語対応した際には弊社のブログや、会社名でエゴサーチしてみたいですね笑
ソースコード
#!/usr/bin/env python | |
import twitter | |
import boto3 | |
import os | |
keyword = '#reinvent' | |
lang = 'en' | |
region = 'us-east-1' | |
size = 100 * 100 | |
def get_tweet_texts(): | |
api = twitter.Api(consumer_key=os.environ['consumer_key'], | |
consumer_secret=os.environ['consumer_secret'], | |
access_token_key=os.environ['access_token_key'], | |
access_token_secret=os.environ['access_token_secret'], | |
sleep_on_rate_limit=True) | |
maxid = None | |
corpus = [] | |
for i in range(int(size/100)): | |
results = api.GetSearch(term=keyword,result_type='recent',count=100,max_id=maxid,lang=lang) | |
maxid = min([result.id for result in results]) - 1 | |
corpus.extend(results) | |
return corpus | |
def detect_sentiment(corpus): | |
result = { | |
'Mixed': 0, | |
'Negative': 0, | |
'Neutral': 0, | |
'Positive': 0, | |
'MIXED': 0, | |
'NEGATIVE': 0, | |
'NEUTRAL': 0, | |
'POSITIVE': 0, | |
'MOST_NEGATIVE': {'score':0}, | |
'MOST_POSITIVE': {'score':0}, | |
} | |
comprehend = boto3.client('comprehend', region_name=region) | |
batch_size = 25 | |
for tweets in [corpus[i:i+batch_size] for i in range(0, len(corpus), batch_size)]: | |
sentiment_results = comprehend.batch_detect_sentiment( | |
TextList=[tweet.text for tweet in tweets], | |
LanguageCode=lang | |
) | |
for sentiment in sentiment_results['ResultList']: | |
result[sentiment['Sentiment']] += 1 | |
if sentiment['Sentiment'] == 'NEGATIVE' and sentiment['SentimentScore']['Negative'] > result['MOST_NEGATIVE']['score']: | |
result['MOST_NEGATIVE'] = {'score': sentiment['SentimentScore']['Negative'], 'tweet': tweets[sentiment['Index']]} | |
elif sentiment['Sentiment'] == 'POSITIVE' and sentiment['SentimentScore']['Positive'] > result['MOST_POSITIVE']['score']: | |
result['MOST_POSITIVE'] = {'score': sentiment['SentimentScore']['Positive'], 'tweet': tweets[sentiment['Index']]} | |
for key, score in sentiment['SentimentScore'].items(): | |
result[key] += score | |
return result | |
def stdout(result): | |
sum_score = sum([value for key, value in result.items() if key in ('Mixed', 'Negative', 'Neutral', 'Positive')]) | |
print('Positive : {:4d}件 : {:.1f}%'.format(result['POSITIVE'], round(result['Positive']/sum_score*100), 1)) | |
print('Negative : {:4d}件 : {:.1f}%'.format(result['NEGATIVE'], round(result['Negative']/sum_score*100), 1)) | |
print('Mixed : {:4d}件 : {:.1f}%'.format(result['MIXED'], round(result['Mixed']/sum_score*100), 1)) | |
print('Neutral : {:4d}件 : {:.1f}%'.format(result['NEUTRAL'], round(result['Neutral']/sum_score*100), 1)) | |
if result['MOST_POSITIVE'].get('tweet'): | |
print('Most positive tweet is "{}"'.format(result['MOST_POSITIVE']['tweet'].text)) | |
if result['MOST_NEGATIVE'].get('tweet'): | |
print('Most negative tweet is "{}"'.format(result['MOST_NEGATIVE']['tweet'].text)) | |
def main(): | |
corpus = get_tweet_texts() | |
result = detect_sentiment(corpus) | |
stdout(result) | |
if __name__ == '__main__': | |
main() |