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.pyPositive : 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() |