最近、業務でフォーラムに行く機会があり、そこでIBMの方とお話させてもらった。
そこで知ったのだが、IBMが所有しているAIのWatsonは各種言語向けのAPIを公開しているらしい。
しかも基本無料!
(使用量によって料金がかかる課金システム)
上記画像のように、Watsonの様々な機能を利用することができる。
今回は興味をもった性格推定(Personality Insights)をPythonで実装してみた。
TwitterのツイートデータをWatsonに食べさせて、性格を推定するといった流れだ。
personality-insights-demo(GitHub)
環境構築
- Python 3.6.8
- IBM-Watson 3.1.2
- Tweepy 3.8.0
パッケージはpipで入る。
ローカルを汚したくなかったので、Cloud9で実装した。
Watsonでの性格推定
Watsonはテキストデータから、書き手の性格を推定できるようだ。
面白いと思ったのは、テキストデータの意味を理解して推定するのではなく、文章の書き方の癖から推定するという点。
直感的なイメージだが、数字が多い人は理論派っぽいみたいな感じだろうか...。
最強のチームはAIが作る!? Watsonの性格分析ツール開発秘話
今回実装した流れ
実装の流れはシンプル
1. 指定したユーザのツイートを取得する
2. ノイズになりそうな部分(後述)を除外する
3. Watson APIに用意したデータを食べさせる
4. jsonで結果を出力する
本当はテストデータに著名人のツイートを使おうと思っていた。
みんな知っているし、なんとなくどんな性格かも予想ができるし。
ただ、IBMは他人のツイートの分析結果をアップロードすることを禁止している。(許可を取ればいいらしい)
仕方なしに今回は自分のツイートデータを入力する。
自分のことを知らない人がみても、推定できているのか否か全くわからないだろうけど...。
入力データ(ツイート取得)
ツイッターからデータを取得する前に、IBMのサンプルデータを確認する。
IBMが公開しているサンプルでも、ツイートを食べさせているのだが、気になった点がある。
下記のjsonファイルはサンプルデータの抜粋。
{
"contentItems": [
{
"content": "Wow, I liked @TheRock before, now I really SEE how special he is. The daughter story was IT for me. So great! #MasterClass",
"contenttype": "text/plain",
"created": 1447639154000,
"id": "666073008692314113",
"language": "en"
},
{
"content": ".@TheRock how did you Know to listen to your gut and Not go back to football? #Masterclass",
"contenttype": "text/plain",
"created": 1447638226000,
"id": "666069114889179136",
"language": "en"
},
{
"content": "RT @patt_t: @TheRock @Oprah @RichOnOWN @OWNTV this interview makes me like you as a fellow human even more for being so real.",
"contenttype": "text/plain",
"created": 1447637030000,
"id": "666064097562247168",
"language": "en"
}
]
}
contentがツイート本文となっているわけだが、見てわかるように、リツイートやリプライ先のユーザIDが入ってしまっているのがわかる。
実際Twitter APIを叩いてツイートを取得すると、
- リツイート
- リプライ先のユーザID
- 本文中のURL
など、ノイズになりそうなものがちらほらある。
今回は取得したツイートから上記3つを間引いて、入力データを作成する。
ツイートを取得する部分はこんな感じ。
import tweepy
import re
def export_tweet_as_text(account):
# TwitterAPIに関するAPIKeyを入力
CONSUMER_KEY = 'xxxx'
CONSUMER_SECRET = 'xxxx'
ACCESS_TOKEN = 'xxxx'
ACCESS_TOKEN_SECRET = 'xxxx'
# APIインスタンス生成
auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
api = tweepy.API(auth)
# 保存用テキストファイルを生成
with open('../resources/tweets.txt', 'w') as f:
# ツイートを取得
tweet_count = 0
pages=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]
for page in pages:
tweets=api.user_timeline(account, count=200, page=page)
for tweet in tweets:
tweet_text = tweet.text
# RTを除外
retweet_flag = False
if tweet_text.find('RT') != -1:
retweet_flag = True
if retweet_flag:
continue
# ツイートから,URLとリプライ先のユーザIDを削除
tweet_text = re.sub(r"(https?|ftp)(:\/\/[-_\.!~*\'()a-zA-Z0-9;\/?:\@&=\+\$,%#]+)", "", tweet_text)
tweet_text = re.sub(r"@[a-zA-Z0-9_]+ ","",tweet_text)
# ファイルに書き込み
f.write(tweet_text + '\n')
Twitter APIやTweepyの使い方はこのあたりが参考になります。
Twitter APIの申請手順まとめ【2019年最新版】
TweepyでTwitterの特定のユーザから3200ツイートの取得
API Reference Tweepy
Watsonで性格推定
入力データができたら、あとはWatsonに食べさせるだけ!
ソースはこんな感じ。
import json
from ibm_watson import PersonalityInsightsV3
def analyze_personality(text_path):
# APIインスタンスを生成
service = PersonalityInsightsV3(
version='2017-10-13',
url='https://gateway-tok.watsonplatform.net/personality-insights/api',
iam_apikey='XXXX')
# 性格を分析
with open('../resources/tweets.txt', 'r') as profile_text:
profile = service.profile(
profile_text.read(),
'application/json',
content_language='ja',
accept_language='ja').get_result()
# ファイルに書き込み
with open('../result/result.json', 'w') as f:
json.dump(profile, f, ensure_ascii=False, indent=2)
IBMのサンプルソースに一部変更を加えた形。
- 入力、出力の言語に日本語を指定
- 出力されたASCIIコードを変換
など、細々と変更。
Watson APIは日本語であまりいい説明がなかったので、リファレンスを参考にするのがいいと思う。
API Reference Watson
公式サンプルソース(GitHub)
後は2つのメソッドをいい感じに動かせば、結果が出力されます。
出力結果
今回の出力結果はこんな感じ。長いです。
{
"word_count": 1477,
"processed_language": "ja",
"personality": [
{
"trait_id": "big5_openness",
"name": "知的好奇心",
"category": "personality",
"percentile": 0.3269748764392166,
"significant": true,
"children": [
{
"trait_id": "facet_adventurousness",
"name": "大胆性",
"category": "personality",
"percentile": 0.851632683688357,
"significant": true
},
{
"trait_id": "facet_artistic_interests",
"name": "芸術的関心度",
"category": "personality",
"percentile": 0.08248046823602373,
"significant": true
},
{
"trait_id": "facet_emotionality",
"name": "情動性",
"category": "personality",
"percentile": 0.01269078854956246,
"significant": true
},
{
"trait_id": "facet_imagination",
"name": "想像力",
"category": "personality",
"percentile": 0.07828538874336671,
"significant": true
},
{
"trait_id": "facet_intellect",
"name": "思考力",
"category": "personality",
"percentile": 0.3185276327945955,
"significant": true
},
{
"trait_id": "facet_liberalism",
"name": "現状打破",
"category": "personality",
"percentile": 0.7757134968207737,
"significant": true
}
]
},
{
"trait_id": "big5_conscientiousness",
"name": "誠実性",
"category": "personality",
"percentile": 0.5968521957686052,
"significant": true,
"children": [
{
"trait_id": "facet_achievement_striving",
"name": "達成努力",
"category": "personality",
"percentile": 0.4680138014818657,
"significant": true
},
{
"trait_id": "facet_cautiousness",
"name": "注意深さ",
"category": "personality",
"percentile": 0.2776913924363156,
"significant": true
},
{
"trait_id": "facet_dutifulness",
"name": "忠実さ",
"category": "personality",
"percentile": 0.646560141519093,
"significant": true
},
{
"trait_id": "facet_orderliness",
"name": "秩序性",
"category": "personality",
"percentile": 0.9673235293559517,
"significant": true
},
{
"trait_id": "facet_self_discipline",
"name": "自制力",
"category": "personality",
"percentile": 0.9534233902088189,
"significant": true
},
{
"trait_id": "facet_self_efficacy",
"name": "自己効力感",
"category": "personality",
"percentile": 0.8047098839215382,
"significant": true
}
]
},
{
"trait_id": "big5_extraversion",
"name": "外向性",
"category": "personality",
"percentile": 0.886006317418282,
"significant": true,
"children": [
{
"trait_id": "facet_activity_level",
"name": "活発度",
"category": "personality",
"percentile": 0.05870481709245734,
"significant": true
},
{
"trait_id": "facet_assertiveness",
"name": "自己主張",
"category": "personality",
"percentile": 0.35164467138634853,
"significant": true
},
{
"trait_id": "facet_cheerfulness",
"name": "明朗性",
"category": "personality",
"percentile": 0.7584765889006709,
"significant": true
},
{
"trait_id": "facet_excitement_seeking",
"name": "刺激希求性",
"category": "personality",
"percentile": 0.4626730374972881,
"significant": true
},
{
"trait_id": "facet_friendliness",
"name": "友好性",
"category": "personality",
"percentile": 0.9993552110743642,
"significant": true
},
{
"trait_id": "facet_gregariousness",
"name": "社交性",
"category": "personality",
"percentile": 0.821949816324011,
"significant": true
}
]
},
{
"trait_id": "big5_agreeableness",
"name": "協調性",
"category": "personality",
"percentile": 0.43064808235299346,
"significant": true,
"children": [
{
"trait_id": "facet_altruism",
"name": "利他主義",
"category": "personality",
"percentile": 0.8011665196131375,
"significant": true
},
{
"trait_id": "facet_cooperation",
"name": "協働性",
"category": "personality",
"percentile": 0.23474709969090873,
"significant": true
},
{
"trait_id": "facet_modesty",
"name": "謙虚さ",
"category": "personality",
"percentile": 0.2789880236799839,
"significant": true
},
{
"trait_id": "facet_morality",
"name": "強硬さ",
"category": "personality",
"percentile": 0.555139731625757,
"significant": true
},
{
"trait_id": "facet_sympathy",
"name": "共感度",
"category": "personality",
"percentile": 0.33013703370248587,
"significant": true
},
{
"trait_id": "facet_trust",
"name": "信用度",
"category": "personality",
"percentile": 0.932881844256453,
"significant": true
}
]
},
{
"trait_id": "big5_neuroticism",
"name": "感情起伏",
"category": "personality",
"percentile": 0.5317695782374742,
"significant": true,
"children": [
{
"trait_id": "facet_anger",
"name": "激情的",
"category": "personality",
"percentile": 0.1661703194086514,
"significant": true
},
{
"trait_id": "facet_anxiety",
"name": "心配性",
"category": "personality",
"percentile": 0.2511270165944251,
"significant": true
},
{
"trait_id": "facet_depression",
"name": "悲観的",
"category": "personality",
"percentile": 0.44009168638198337,
"significant": true
},
{
"trait_id": "facet_immoderation",
"name": "利己的",
"category": "personality",
"percentile": 0.690512849700951,
"significant": true
},
{
"trait_id": "facet_self_consciousness",
"name": "自意識過剰",
"category": "personality",
"percentile": 0.05802391279529695,
"significant": true
},
{
"trait_id": "facet_vulnerability",
"name": "低ストレス耐性",
"category": "personality",
"percentile": 0.2799141848650055,
"significant": true
}
]
}
],
"needs": [
{
"trait_id": "need_challenge",
"name": "挑戦",
"category": "needs",
"percentile": 0.912788130027943,
"significant": true
},
{
"trait_id": "need_closeness",
"name": "親密",
"category": "needs",
"percentile": 0.47916836503889804,
"significant": true
},
{
"trait_id": "need_curiosity",
"name": "好奇心",
"category": "needs",
"percentile": 0.968932858050091,
"significant": true
},
{
"trait_id": "need_excitement",
"name": "興奮",
"category": "needs",
"percentile": 0.9976361241706758,
"significant": true
},
{
"trait_id": "need_harmony",
"name": "調和",
"category": "needs",
"percentile": 0.009193757113194367,
"significant": true
},
{
"trait_id": "need_ideal",
"name": "理想",
"category": "needs",
"percentile": 0.917518963157462,
"significant": true
},
{
"trait_id": "need_liberty",
"name": "自由主義",
"category": "needs",
"percentile": 0.9970523431240045,
"significant": true
},
{
"trait_id": "need_love",
"name": "社会性",
"category": "needs",
"percentile": 0.710476275913941,
"significant": true
},
{
"trait_id": "need_practicality",
"name": "実用主義",
"category": "needs",
"percentile": 0.9942052654351621,
"significant": true
},
{
"trait_id": "need_self_expression",
"name": "自己表現",
"category": "needs",
"percentile": 0.283590546642394,
"significant": true
},
{
"trait_id": "need_stability",
"name": "安定性",
"category": "needs",
"percentile": 0.3676226427668019,
"significant": true
},
{
"trait_id": "need_structure",
"name": "仕組",
"category": "needs",
"percentile": 0.03713112525416268,
"significant": true
}
],
"values": [
{
"trait_id": "value_conservation",
"name": "現状維持",
"category": "values",
"percentile": 0.1174651842220481,
"significant": true
},
{
"trait_id": "value_openness_to_change",
"name": "変化許容性",
"category": "values",
"percentile": 0.8841545033162561,
"significant": true
},
{
"trait_id": "value_hedonism",
"name": "快楽主義",
"category": "values",
"percentile": 0.05959893144723466,
"significant": true
},
{
"trait_id": "value_self_enhancement",
"name": "自己増進",
"category": "values",
"percentile": 0.9674816463059281,
"significant": true
},
{
"trait_id": "value_self_transcendence",
"name": "自己超越",
"category": "values",
"percentile": 0.08191121317174338,
"significant": true
}
],
"warnings": []
}
percentileがその指標のポイントのようなもの。
正確には、IBMが学習する際に利用した人たちの中で、どのあたりに位置するかを表しているらしい。
下から何%ぐらいという感じだろう。
協働性が下から23%らしい。なんとなく当たっているような。
まとめ
公式リファレンスが充実していたこともあって、割とすんなり実装できた。
ぶっちゃけ、自分のツイートデータを使って性格推定するだけならば、
公式が用意しているグラフィカルなデモの方が100倍わかりやすい。
ただ、これだと自分のアカウントとしか紐付けできないので、勝手に人の性格を推定したい場合は実装するしかない。
あとは、今回取得したデータを何に活かすかって部分が重要になってくるのだろうけど、
思いつかないので気ままに考えます。