この前やっとCourseraのMachine Learningのコースを修了した。習ったことを何か日常で気になることに生かせないかなと思って、服部平次の「おっさん」「おっちゃん」の呼び分けの傾向を求めるのに使ってみることにした。
服部は中年男性のことを「おっさん」もしくは「おっちゃん」と呼ぶが、どうやって呼び分けてるんやろなって前から不思議に思っていた。
前提
ひらがなとカタカナの区別はつけない
binary classificationで、「おっちゃん: y = 1, おっさん: y = 0」とする
92巻までのセリフを用いる(そこまでしかテキストデータ化終わってないから)
小五郎は別格なので除外する
feature
どういうfeatureがあるのか考えた結果、「巻数」「親密さ」「疑惑度」「方向」の4つを作ることにした。
1. 巻数(= 時代)
初期の服部は割と雑な感じで「おっさん」って呼びがちで、最近の服部は人懐っこくて「おっちゃん」をよく使ってる気がする。
2. 親密さ
服部と相手がどれくらい親しいか。
馴染みの相手には「おっちゃん」を使う傾向が高そう。
値 | 目安 |
---|---|
0 | 初対面・面識なし |
1 | 面識あり・少し話したことがある |
2 | かなり話したことがある・親しい |
3. 疑惑度
服部が相手に対してどれくらいの疑惑を抱いているか。胡散臭い相手に対しては「おっさん」の方が適している気がする。
値 | 目安 |
---|---|
0 | 潔白もしくは被害者側 |
1 | 少し怪しい・容疑者の一人である |
2 | かなり怪しい |
3 | 犯人だと確定している |
被害者だとしても、胡散臭くて怪しい奴であれば、1とする(K3で殺された新聞記者とか)。
4. 方向
服部が相手に対してどういう方向で話しているか。面と向かっているときの方が「おっちゃん」を使いがちな気がする。
値 | 目安 |
---|---|
0 | 相手に聞こえないところで言及している |
1 | 相手に聞こえるところで言及している |
2 | 相手に向かって話している |
データ収集
服部のセリフデータから「おっさん」「オッサン」「おっちゃん」「オッチャン」で検索し、小五郎に言及しているもの以外を以下のようにまとめた。
(一番右のカラムは、どれがどのシーンか思い出しやすいようにするためのメモ)
データ数は80個となった。少ないかな…。
赤馬のおっちゃんや郷司のおっさんなど途中で疑惑度が変化した人もいるので、同じ人を複数回呼んでるのも全部記録した。シンフォニー号の事件で助けてくれた漁師の方たちとか親密さがわからない人については、適当に予想した。
相関関係
ここで相関関係求めて意味があるのかはわからんけど、試しに出してみた。scipy.stats.pearsonr
に計算してもらうと、以下のようになった。
yと○○の関係 | 相関係数 | P値 |
---|---|---|
巻数 | 0.6596983083448209 | 2.8327330071418733e-11 |
親しさ | 0.29273833233028956 | 0.008410796649393136 |
疑惑度 | -0.06611364854623174 | 0.5601156608149662 |
方向 | -0.02280254169471079 | 0.8408797463324191 |
巻数とyは思ったより相関関係あるし、親しさとyも小さいけど一応あると言える。疑惑度と方向については、yとの相関関係はほとんど見られないみたい。
SVCで学習する
まずはscikit-learnのSVCで学習してもらってみる。カーネルは、一番定番と呼ばれててCourseraでも重点を置いて教えてくれたRBF kernelを使う。ハイパーパラメータについては、グリッドサーチしてF1 scoreが良さそうだった組み合わせ(C=5, gamma=0.5)にした。グリッドサーチしたって言っても、めっちゃシャッフルの影響受けて毎回最適値がコロコロ変わるから、キリのいいところで適当に決めた。
import numpy as np from sklearn import preprocessing from sklearn import svm from sklearn.metrics import classification_report y, volume, intimacy, suspicion, direction = np.loadtxt('ossan.csv', unpack=True, delimiter=',') # create and preprocess feature matrix X = np.c_[volume, intimacy, suspicion, direction] X = preprocessing.scale(X) # SVC with rbf kernel clf = svm.SVC(kernel='rbf', C=5, gamma=0.5) clf.fit(X, y) # evaluate print(classification_report(y, clf.predict(X), target_names=['おっさん', 'おっちゃん']))
訓練用データに対してのclassification_report
を出したら以下のような結果になった。
precision | recall | f1-score | support | |
---|---|---|---|---|
おっさん | 0.94 | 0.97 | 0.96 | 33 |
おっちゃん | 0.98 | 0.96 | 0.97 | 47 |
avg/total | 0.96 | 0.96 | 0.96 | 80 |
訓練用データだから高いのは当たり前なんだけど、それでも予想外に高かった。SVMすご…。
けど、目的は呼び方を正しく予測することではなくて、だいたいの傾向を見出すことである。RBF kernel使ったら、精度はいいけど、もともとのfeatureがどう影響してどうなってるのか私には全くわからん。linear kernelを使ったらそれぞれのfeatureにかかる係数を見れるみたいなので、試してみた。
Linear kernelでのSVC
# データの用意などはさっきと一緒 # SVC with rbf kernel clf = svm.SVC(kernel='linear', C=1) clf.fit(X, y) # show coefficients for coef in zip(['volume', 'intimacy', 'suspicion', 'direction'], clf.coef_[0]): print(coef[0] + ': ' + str(coef[1]))
今回は、F1 scoreは0.89だった。線形だからもっと下がるかと思ってた(*‘▽’)
係数
係数が正であれば、そのfeatureの値が大きいほど「おっちゃん」と呼びやすい。負であれば、そのfeatureの値が大きいほど「おっさん」と呼びやすい。
予想した符号 | 実際の係数 | 予想が合ってるか | |
---|---|---|---|
巻数 | 正 | 2.34837022019 | ○ |
親密さ | 正 | 0.227720579735 | ○ |
疑惑度 | 負 | -0.158394754511 | ○ |
方向 | 正 | 0.36036075643 | ○ |
符号的には予想と一致してるけど、巻数以外はほぼ影響してないように見える。
可視化
そのままだと次元数が多くてグラフにできないので、「巻数 + 別のfeature1個」でRBF kernel使って学習した結果をグラフにした。
赤が「おっちゃん」、青が「おっさん」。
上はpredict、下はdecision functionから出てきた値のグラフ化。
親しさについてはまあまあ傾向が感じられるけど、疑惑度と方向については何も読み取れない…。
結論
昔の服部は「おっさん」を使いがちで、近頃の服部は「おっちゃん」を使いがち。親しさはちょっと呼び方に関係するが、疑惑度と方向はほとんど関係なさそう? もしかしたら私がfeatureとして作れなかっただけで、もっとちゃんとした決め手があるのかもしれない。
初対面の人を「おっさん」呼ばわりする失礼な服部も、馴れ馴れしく「おっちゃん」って呼ぶ服部も、どっちも好き。