はじめに
COTOHA APIとは日本語自然言語処理APIです。様々な機能があり、無料アカウントでも1000/日使える結構遊びがいのあるAPIです。
ところで、皆さん、磯野くんってご存知ですか?そう、某国民的アニメの磯野カツオくんです。
この磯野くんは劇中なにかと名前を呼ばれるのですが、都合の悪いときには聞こえなかったり、所謂"人間らしい"反応をします。今回はこれを実現するためにCOTOHA APIで色々やってみます。
COTOHA APIの詳しい使い方は私が以前書いた記事や、その他にも様々な方が記事を書いてくださってるのでそちらをご参照ください。
完成したISONOはこんな感じに反応します。
私:おーい磯野。野球しようぜ。
磯野:よんだ?
私:磯野くーん
磯野:よんだ?
私:磯野ってダメだよなぁ
磯野:...
私:磯野くんはかっこいいわ
磯野:よんだ?
私:磯野といえば磯のり
磯野:...
サンプル数は少ないのですが、どうでしょう。磯野っぽくないですか?
準備
COTOHA APIで、「磯野が呼ばれた」と認識したかを表示するためのラッパー関数を作ります。
呼ばれたと思ったときには「呼んだ?」そうじゃないときには「...」を表示します。
対して大事でもないので折りたたみ
cotoha_exp = [
"磯野",
"磯野くん",
]
def call_cotoha(res_func,texts,cotoha_token,cotoha_exp):
"""
res_func:呼ばれたか判定する関数。文を受け取りtrue falseを返す。
texts:判定対象の文配列
cotoha_token:アクセストークン
cotoha_exp:磯野やそれに対する名前の判定用。
"""
for t in texts:
does_ans = res_func(t,cotoha_token,cotoha_exp)
if does_ans:
answer = "よんだ?"
else:
answer = "..."
print("私:{} \n磯野:{}\n".format(t,answer))
1.最も単純な呼び出し方(形態素解析、自意識過剰な磯野くん)
磯野くんが自身の呼び出しを感知する最も単純な方法としては、文章を形態素解析して、その中に磯野やそれに類するものが含まれていた場合に反応するというのがあります。
簡単に言えば文に磯野が入ってたら反応します。
構文解析のドキュメントはこちら
ソースコード。記事が長くなるので折りたたみ
def parser_base(text,cotoha_token,cotoha_exp):
header = {
"Content-Type":"application/json",
"Authorization":"Bearer "+cotoha_token
}
datas = {
"sentence":text
}
r = requests.post(api_base+"nlp/v1/parse",headers=header,data=json.dumps(datas))
parsed = json.loads(r.text)
for res in parsed["result"]:
for tok in res["tokens"]:
for exp in cotoha_exp:
if exp in tok["form"]:
return True
return False
結果
call_cotoha(parser_base,texts,cotoha_token,cotoha_exp)
私:おーい磯野。野球しようぜ。
磯野:よんだ?
私:磯野くーん
磯野:よんだ?
私:磯野ってダメだよなぁ
磯野:よんだ?
私:磯野くんはかっこいいわ
磯野:よんだ?
私:磯野といえば磯のり
磯野:よんだ?
当然といえば当然なんですが、全部に反応します。ちょっと自意識過剰な気がしますね。
2.固有表現に含まれていたら反応(やっぱり自意識過剰)
わざわざ形態素解析して全部のトークンを見なくても、COTOHA APIには固有表現抽出という機能がありまして、人名や地名、数値表現などを簡単に抜き出してきてくれます。これを利用すれば1.と同じような事がちょっとスタイリッシュにできます。
ソースコード。記事が長くなるので折りたたみ
def ne_base(text,cotoha_token,cotoha_exp):
header = {
"Content-Type":"application/json",
"Authorization":"Bearer "+cotoha_token
}
datas = {
"sentence":text
}
r = requests.post(api_base+"nlp/v1/ne",headers=header,data=json.dumps(datas))
parsed = json.loads(r.text)
for res in parsed["result"]:
for exp in cotoha_exp:
if exp in res["form"]:
return True
return False
結果
私:おーい磯野。野球しようぜ。
磯野:よんだ?
私:磯野くーん
磯野:よんだ?
私:磯野ってダメだよなぁ
磯野:よんだ?
私:磯野くんはかっこいいわ
磯野:よんだ?
私:磯野といえば磯のり
磯野:よんだ?
やっぱり自意識過剰です。まぁ年頃ですしね。周囲の評判は気になるものです。
3.キーワードに含まれてたら反応(全然反応しない磯野くん)
固有表現抽出とは少し異なり、COTOHA APIにはキーワード抽出という機能があり、ある会話の中で磯野がキーワードになってたら反応するようにしたいと思います。自分が主題だと感じたら反応するはずです。
ソースコード。記事が長くなるので折りたたみ
def key_base(text,cotoha_token,cotoha_exp):
header = {
"Content-Type":"application/json",
"Authorization":"Bearer "+cotoha_token
}
datas = {
"document":text
}
r = requests.post(api_base+"nlp/v1/keyword",headers=header,data=json.dumps(datas))
parsed = json.loads(r.text)
for res in parsed["result"]:
for exp in cotoha_exp:
if exp in res["form"]:
return True
return False
結果
私:おーい磯野。野球しようぜ。
磯野:...
私:磯野くーん
磯野:...
私:磯野ってダメだよなぁ
磯野:...
私:磯野くんはかっこいいわ
磯野:...
私:磯野といえば磯のり
磯野:...
全然反応してくれません。人名はキーワードにはなりにくいようです。
なんか悪いことをしたときの磯野くんの反応ですねこれは。
これでもかというくらい呼んだら反応してくれるでしょうか。
私:磯野くん、磯野くん、磯野くん、磯野くん、磯野くん
磯野:...
ダメですね…磯野くん死んでるみたいになっちゃいました…
4.類似度を利用する(いい感じ)
COTOHA APIには2つの文の類似度を判定する機能があるため、これを利用したいと思います。具体的には、「磯野を呼び出す」という文章との類似度が高かった場合、磯野が呼び出されていると判断することにしようと思います。
ソースコード。記事が長くなるので折りたたみ
def sim_base(text,cotoha_token,cotoha_exp):
header = {
"Content-Type":"application/json",
"Authorization":"Bearer "+cotoha_token
}
datas = {
"s1":text,
"s2":"磯野を呼び出す"
}
r = requests.post(api_base+"nlp/v1/similarity",headers=header,data=json.dumps(datas))
parsed = json.loads(r.text)
if parsed["result"]["score"] > 0.5:
return True
else:
return False
結果
私:おーい磯野。野球しようぜ。
磯野:よんだ?
私:磯野くーん
磯野:よんだ?
私:磯野ってダメだよなぁ
磯野:...
私:磯野くんはかっこいいわ
磯野:...
私:磯野といえば磯のり
磯野:...
お、結構いい感じじゃないですか?
1,2個めの呼びかけ文のみ反応してくれました。
チャットボットとかのウェイクアップなどに結構実用性のある反応方法なきがします。
5.文タイプが疑問形のときだけ反応する(微妙…)
COTOHA APIは、ある文がどういうタイプなのかを判定することができます。
(叙述/疑問/命令、もっと細かくも取れます。詳しくはドキュメントを御覧ください。)
これを用いてある文が疑問形のときだけ反応するようにしてみます。
(本当は呼びかけという文タイプがあったら良かったのですが、見つからず…)
疑問形であるかどうかだけだと、当然うまく行かないので、疑問形であった場合、最も感度が高いと考えられる形態素解析してCOTOHAが含まれているか、という方法で判定します。
ソースコード。記事が長くなるので折りたたみ
def parser_sent_type_base(text,cotoha_token,cotoha_exp):
header = {
"Content-Type":"application/json",
"Authorization":"Bearer "+cotoha_token
}
datas = {
"sentence":text
}
r = requests.post(api_base+"nlp/v1/sentence_type",headers=header,data=json.dumps(datas))
parsed = json.loads(r.text)
if parsed["result"]["modality"] == "interrogative":
return parser_base(text,cotoha_token,cotoha_exp)
else:
return False
結果
私:おーい磯野。野球しようぜ。
磯野:...
私:磯野くーん
磯野:...
私:磯野ってダメだよなぁ
磯野:...
私:磯野くんはかっこいいわ
磯野:...
私:磯野といえば磯のり
磯野:...
うーん…微妙…ダメっぽいですね。
6.自分が褒められてそうなときにだけ反応する(都合のよい磯野)
COTOHA APIには感情分析という機能がありまして、ある文がポジティブかネガティブか判定することができます。ので、
文がポジティブかつ磯野が含まれているとき→磯野が褒められてそうなとき
だけ、反応するようにしたいと思います。都合の良い磯野くん。
みんなで磯野のご機嫌取りをしましょう。
ソースコード。記事が長くなるので折りたたみ
def senti_parser_base(text,cotoha_token,cotoha_exp):
header = {
"Content-Type":"application/json",
"Authorization":"Bearer "+cotoha_token
}
datas = {
"sentence":text
}
r = requests.post(api_base+"nlp/v1/sentiment",headers=header,data=json.dumps(datas))
parsed = json.loads(r.text)
if parsed["result"]["sentiment"] == "Positive":
return parser_base(text,cotoha_token,cotoha_exp)
else:
return False
結果
私:おーい磯野。野球しようぜ。
磯野:...
私:磯野くーん
磯野:...
私:磯野ってダメだよなぁ
磯野:...
私:磯野くんはかっこいいわ
磯野:よんだ?
私:磯野といえば磯のり
磯野:...
結構磯野の特性を再現できそうです。
ぼくのかんがえた、磯野。
これまでの実験の結果から、
- 「磯野を呼び出す」に近い文である
- 磯野が褒められている文である
のどちらかが満たされたときに反応するようにすると、磯野を再現できる気がします。
ということでやってみます。
def ISONO(text,cotoha_token,cotoha_exp):
flag1 = sim_base(text,cotoha_token,cotoha_exp)
flag2 = senti_parser_base(text,cotoha_token,cotoha_exp)
return (flag1 or flag2)
結果
私:おーい磯野。野球しようぜ。
磯野:よんだ?
私:磯野くーん
磯野:よんだ?
私:磯野ってダメだよなぁ
磯野:...
私:磯野くんはかっこいいわ
磯野:よんだ?
私:磯野といえば磯のり
磯野:...
人工知能ISONOここに爆誕(大げさ)
終わりに。
できるだけ多くのAPIを利用して色々やってみましたが、まだ使えてない機能があります(要約、照応解析など)試してみたい方はこちらから登録してみてください。
完全ネタで書いたつもりでしたが、意外とチャットボット等のウェイクアップに活用できたりするのではないかなとか思いました。
ちなみにですが、自分が否定されてそうな文章にだけ反応するようにしたりするとネガティブな人格を作り出すことができる気がします。他にもある程度の人格なら再現できそうな気が…
卍応用卍のしがいがありそうです。