くさいセリフ判定

はじめに

くさいセリフを恋人につぶやいてドン引きされてしまう問題を自然言語処理の力で解決するため、くさいセリフかどうかを判定するプログラムを試してみる。

    , -──- 、
  /::::::::::::::    ::\
 /:::::::::::        ::∨ト、        こいつはくせえッー!
 ::::::::::          :: レ'ノ
 ::::::::::::::   恋人   ::: レ'⌒ヽ     ゲロ以下のにおいが
 ヽ-───i===i─-}ァ'  ノ    プンプンするぜッ─────ッ!!
 、` ー-===-゚---゚==‐' /
 、`¨フ>;''ニニゞ,;アニニY´; )     こんな奴には出会ったことが
 _、;;)¨´,ニ=゚='" ,.ヘ=゚:く {ッリ'        ねえほどなァ────ッ
 i1(リ        r;:ドヽ K
 ヾ=、     に二ニヽ `|; )      自然言語処理のせいでナルシーになっただと?
 _,ノ| i.     {⌒゙'^ヽ.{  i;; ヽ        ちがうねッ!!
 _,ノ!i ヽ、  ヾ二ニソ ,';;;  ;;冫=:、
 _;(|.!.  \   ‐っ /!;;; ;;/ 、''"\__  こいつは生まれついてのナルシーだッ!
 'ト、\.   ,ゝ、.二..イリ\ / ー1\'ニゝヽ_
 :ヽ  `ニア   ,. -┴‐‐'  ー-:l :=ゞ=ソ」=ヽ   早えとこ
 :::::\ ニ=ト、.i___`ー-┴-、ノ .   l __l| ,ニト、くヽ    これで判定しちまいな!
 l::::::::::\ー:ト      __}/ト、゙ ー-‐| ,ニ|ゞ=ハ `¨´ー- 
 ;ニ=ー:::::::ヾト、._    ̄ ノ|::ヽ ニ._‐-ゞ=' .ノ ::|:::::::::::
 :\:::::::::::::::ヽ   ̄ ̄ !:|:::::    ̄ ̄  ::::|::::::::

方法

  • くさいセリフを集める
  • 判定したい文とくさいセリフの類似度(cos類似度)を計算
    • 文字bigramでベクトルを作成
  • くさいセリフ度を数値化する

コード

  • 勢いで書いてしまったのでjavascript
  • ファイルに保存してブラウザで開けば動くかもしれない
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
		<script type="text/javascript">

			$(function(){
				//参考: 女性がドン引きしてしまう“クサ〜い”セリフランキング
				//      http://ranking.goo.ne.jp/ranking/017/kD0UPcIzymlM/
				var kusai_text = new Array(
					"おやすみ、僕の子猫ちゃん",
					"君のハートに王手!",
					"君の瞳に乾杯",
					"君は太陽、俺は月。君がいなければ、輝くことができない",
					"どんなに美しい花も君の美しさにはかなわない",
					"アイウォンチュー",
					"どんな魔法を使って僕を惹きつけているの?",
					"俺色に染まれよ",
					"君に酔ってしまったようだ",
					"君を食べちゃいたい",
					"君は僕の天使だ",
					"君は太陽のようにまぶしい",
					"永遠に君だけを愛す",
					"僕は君と逢うために生まれてきたんだ",
					"夢の中でも抱きしめてやる",
					"君は会うたびに美しくなる",
					"君の忘れられない男になってやる",
					"僕の心には君が住んでいる",
					"世界中を敵にまわしても僕が君を守る",
					"君なしに僕は生きていけない",
					"君を死ぬまで離さない",
					"今すぐ君を抱きしめたい",
					"君の事を考えると何も手につかないんだ",
					"君は僕の運命の人だ",
					"君が隣にいれば他に何もいらない"
				);
				var kusai_arr; //くさいセリフベクトルのリスト
				var NGRAM = 2; //文字bigramでベクトルを作る



				//文から文字ngramのベクトルを作成
				var ngram_vector = function(n, text){
					var res = new Array();
					for(var i=0; i<text.length-n+1; i++){
						var substr = text.substr(i,n);
						if(substr in res){
							res[substr]++;
						}else{
							res[substr] = 1;
						}
					}
					return res;
				}

				//2つのベクトルの類似度を計算
				var vector_similarity = function(vec1, vec2){
					//cos類似度
					var vec1_sz = 0, vec2_sz = 0, vec_inprod = 0;
					for(var k in vec1){
						vec1_sz += vec1[k] * vec1[k];
						if(k in vec2){
							vec_inprod += vec1[k] * vec2[k];
						}
					}
					for(var k in vec2){
						vec2_sz += vec2[k] * vec2[k];
					}
					return vec_inprod/(Math.sqrt(vec1_sz) * Math.sqrt(vec2_sz));
				}


				//くさいセリフベクトルを作っておく
				var init = function(){
					kusai_arr = new Array();

					for(var i=0; i<kusai_text.length; i++){
						var vec = ngram_vector(NGRAM, kusai_text[i]);
						kusai_arr.push(vec);
					}
				}

				//セリフ判定(一番類似度が高い数値を返す)
				var judge_dialogue = function(text){
					var maxv = 0, maxi = -1;
					
					var vec = ngram_vector(NGRAM, text);
					for(var i=0; i<kusai_arr.length; i++){
						var sim = vector_similarity(vec, kusai_arr[i]);
						if(maxv < sim){
							maxv = sim;
							maxi = i;
						}
					}
					return maxv;
				};


				//判定ボタンの処理
				$("#judge").click(function(){
					var res = judge_dialogue($("#tarea").val());

					$("#result").html("くさい度: " + (res*100) + "%");
				});


				//初期化処理
				init();

			});

		</script>
	</head>

	<body>
		<textarea id="tarea" cols="50" rows="5" placeholder="くさいかもしれないセリフを入力してください"></textarea>
		<button id="judge">判定</button>

		<div id="result"></div>
	</body>
</html>

結果

「僕の子猫ちゃん」
くさい度: 73.85489458759965%

「俺色に染まれ」
くさい度: 91.28709291752769%

「君さえいれば何もいらない」
くさい度: 56.40760748177662%

「永遠に君の虜さ」
くさい度: 43.30127018922194%


「君に運命を感じるよ」
くさい度: 12.499999999999996%

「君とエビを食べちゃいたい」
くさい度: 74.62025072446365%

なかなか難しい。
くさいセリフが少ないのもあるけど、本質的に意味情報も必要そうでヤバい。
文に出現しているオブジェクトがなんなのか正確に取り出せないとうまく判定できないかも。