OSDN Git Service

初期コミット。単語抽出の実験中。
authorhikarupsp <hikarupsp@users.sourceforge.jp>
Wed, 18 Sep 2013 17:06:54 +0000 (02:06 +0900)
committerhikarupsp <hikarupsp@users.sourceforge.jp>
Wed, 18 Sep 2013 17:06:54 +0000 (02:06 +0900)
ArtificialIntelligence.txt [new file with mode: 0644]
ai.js [new file with mode: 0644]
index.html [new file with mode: 0644]

diff --git a/ArtificialIntelligence.txt b/ArtificialIntelligence.txt
new file mode 100644 (file)
index 0000000..4d31ef7
--- /dev/null
@@ -0,0 +1,99 @@
+Artificial Intelligence 
+~人工知能作成から考えるヒトの思考の仕組み~
+東京学芸大学附属高等学校 59期2年A組 西田 耀
+・はじめに
+       今日、人工知能という言葉は誰もが知っている言葉になりつつある。
+       しかし、そもそも知能とは何か、何をもって人工知能の完成と見るのかは、まだはっきりとしない。
+       そもそも、我々人間は本当に知能を持っているのだろうか。
+       そして、機械が人間と同等の知能を持つことはできるのだろうか…。
+       この研究では、人工知能を実際に作成する過程における試行錯誤を基に、ヒトの思考の仕組みとは何かを考えてゆく。
+
+・目次
+       ・はじめに
+       ・目次
+       ・人工知能研究開発の歴史
+       ・人工知能の作成
+               ・作成する人工知能について
+
+・人工知能の作成
+       ・作成する人工知能について
+               //今回作成する人工知能は、既存の形態素解析ライブラリなどは用いず、標準ライブラリと一部の環境依存のライブラリのみを用いて、C言語で実装する。
+               //実行環境はMac OSX、入力文字のエンコードはUTF-8を想定している。
+               //また、Windows環境でもソース互換で動くことを目指す。
+               2013/09/16より、JavaScriptでの開発に変更。
+               これにより、web関連技術との連携が容易になると思われる。
+
+       ・単語抽出の手法
+               基本方針:
+                       ・確実に単語だと判断できるもののみを単語として登録する。
+                       ・人間が考える「単語」と、コンピューターからみた「単語」の概念は異なる可能性があることを考慮する。
+
+               ・単語度
+                       (2013/05/16)
+                       単語度=[単語長]^2 x [単語の出現頻度]
+                       (2013/09/19)
+                       単語度=[1/文字列中の文字種]が大きく、かつ出現回数が大きいもの。
+                       これだと、漢字で構成される単語は比較的良く検出できるが、ひらがなは不自然な部分が取り出されることが多かった。
+                       ----(テストデータでの上位部分抜粋)
+                               2 :1 :いた
+                               2 :1 :プロ
+                               2 :1 :li
+                               2 :1 :され
+                               2 :1 :ート
+                               2 :1 :として
+                               2 :1 : (
+                               2 :1 :など
+                               2 :1 :よう
+                               2 :1 :では
+                               2 :1 :される
+                               2 :1 :計算
+                               2 :1 :手法
+                               2 :1 :エキスパートシステム
+                               2 :1 :推論
+                               2 :1 :した
+                               2 :1 :から
+                               2 :1 :ニューラルネットワーク
+                               2 :1 :されている
+                               2 :1 :して
+                               3 :1 :人工知能
+                               3 :1 :知能
+                               3 :1 :という
+                               3 :1 :れている
+                               3 :1 :があ
+                               4 :1 :その
+                               5 :1 :システム
+                               5 :1 :する
+                               8 :1 :ある
+                               9 :1 :AI
+                       ----
+               ・履歴スライド探索法
+                       入力された履歴すべてに対して、現在の入力文字列の一部と一致する表現を抽出する。
+                       例:  履歴中の文字列が"人工知能(じんこうちのう、英: Artificial Intelligence, AI)は、コンピュータに人間と同様の知能を実現させようという試み、あるいはそのための一連の基礎技術をさす。"
+                               入力文字列が"「人工知能」という名前は1956年にダートマス会議でジョン・マッカーシーにより命名された。"
+                               のとき。
+                       ----
+                               Index(Decimal),CountOfContain(Decimal), String
+                               0,1,人工知能
+                               1,1,工知能
+                               2,2,知能
+                               3,2,能
+                               4,1,という
+                               5,1,いう
+                               6,4,う
+                               7,2,は
+                               8,1,に
+                               9,1,ー
+                               10,1,ン
+                               11,1,よ
+                               12,2,さ
+                               13,1,た
+                       ----
+
+               ・重複する抽出単語のフィルタリング
+                       ・長い単語に含まれており、かつ出現頻度が長い単語と等しい場合、その単語は不要な単語である。
+                               例:ニューラルネットワーク,ラルネットワーク
+                       ・しかし、出現頻度に差がある場合は、その単語は必要なものである。
+                               例:ニューラルネットワーク,ネットワーク
+                       ・スライドチェック時に一致文字列が入力文字列全体の場合は無視するべき。
+
+
diff --git a/ai.js b/ai.js
new file mode 100644 (file)
index 0000000..0745987
--- /dev/null
+++ b/ai.js
@@ -0,0 +1,482 @@
+//AI004
+
+//
+// クラス拡張
+//
+
+//配列関連
+Array.prototype.removeAllObject = function(anObject){
+       //Array中にある全てのanObjectを削除し、空いた部分は前につめる。
+       //戻り値は削除が一回でも実行されたかどうか
+       var ret = false;
+       for(var i = 0; i < this.length; i++){
+               if(this[i] == anObject){
+                       this.splice(i, 1);
+                       ret = true;
+                       i--;
+               }
+       }
+       return ret;
+}
+Array.prototype.removeByIndex = function(index){
+       //Array[index]を削除し、空いた部分は前につめる。
+       this.splice(index, 1);
+       return;
+}
+Array.prototype.intersectionWith = function(a, b, fEqualTo){
+       //積集合を求める
+       //fEqualToは省略可能で、評価関数fEqualTo(a[i], b[j])を設定する。
+       var r = new Array();
+       for(var i = 0, len = b.length; i < len; i++){
+               if(this.isIncluded(b[i], fEqualTo)){
+                       r.push(b[i]);
+               }
+       }
+       return r;
+}
+Array.prototype.unionWith = function(a, b, fEqualTo){
+       //和集合を求める
+       //fEqualToは省略可能で、評価関数fEqualTo(a[i], b[j])を設定する。
+       var r = new Array();
+       for(var i = 0, len = b.length; i < len; i++){
+               if(!this.isIncluded(b[i], fEqualTo)){
+                       r.push(b[i]);
+               }
+       }
+       return this.concat(r);
+}
+Array.prototype.isIncluded = function(obj, fEqualTo){
+       //含まれている場合は配列内のそのオブジェクトを返す
+       //fEqualToは省略可能で、評価関数fEqualTo(array[i], obj)を設定する。
+       if(fEqualTo == undefined){
+               for(var i = 0, len = this.length; i < len; i++){
+                       if(this[i] == obj){
+                               return this[i];
+                       }
+               }
+       } else{
+               for(var i = 0, len = this.length; i < len; i++){
+                       if(fEqualTo(this[i], obj)){
+                               return this[i];
+                       }
+               }
+       }
+       return false;
+}
+Array.prototype.pushUnique = function(obj, fEqualTo){
+       //値が既に存在する場合は追加しない。評価関数fEqualTo(array[i], obj)を設定することができる。
+       //結果的に配列内にあるオブジェクトが返される。
+       var o = this.isIncluded(obj, fEqualTo);
+       if(!o){
+               this.push(obj);
+               return obj;
+       }
+       return o;
+}
+Array.prototype.stableSort = function(f){
+       // http://blog.livedoor.jp/netomemo/archives/24688861.html
+       // Chrome等ではソートが必ずしも安定ではないので、この関数を利用する。
+       if(f == undefined){
+               f = function(a,b){ return a - b; };
+       }
+       for(var i = 0; i < this.length; i++){
+               this[i].__id__ = i;
+       }
+       this.sort.call(this, function(a,b){
+               var ret = f(a, b);
+               if(ret == 0){
+                       return (a.__id__ > b.__id__) ? 1 : -1;
+               } else{
+                       return ret;
+               }
+       });
+       for(var i = 0;i < this.length;i++){
+               delete this[i].__id__;
+       }
+};
+
+//文字列関連
+String.prototype.replaceAll = function(org, dest){
+       //String中にある文字列orgを文字列destにすべて置換する。
+       //http://www.syboos.jp/webjs/doc/string-replace-and-replaceall.html
+       return this.split(org).join(dest);
+}
+String.prototype.compareLeftHand = function (search){
+       //前方一致長を求める。
+       for(var i = 0; search.charAt(i) != ""; i++){
+               if(search.charAt(i) != this.charAt(i)){
+                       break;
+               }
+       }
+       return i;
+}
+String.prototype.splitByArray = function(separatorList){
+       //リスト中の文字列それぞれで分割された配列を返す。
+       var retArray = new Array();
+       retArray[0] = this;
+       
+       for(var i = 0; i < separatorList.length; i++){
+               var tmpArray = new Array();
+               for(var k = 0; k < retArray.length; k++){
+                       tmpArray[k] = retArray[k].split(separatorList[i]);
+                       if(tmpArray[k][tmpArray[k].length - 1] == ""){
+                               tmpArray[k].splice(tmpArray[k].length - 1, 1);
+                               if(tmpArray[k] && tmpArray.length > 0){
+                                       for(var m = 0; m < tmpArray[k].length; m++){
+                                               tmpArray[k][m] += separatorList[i];
+                                       }
+                               }
+                       } else{
+                               for(var m = 0; m < tmpArray[k].length - 1; m++){
+                                       tmpArray[k][m] += separatorList[i];
+                               }
+                       }
+               }
+               retArray = new Array();
+               retArray = retArray.concat.apply(retArray, tmpArray);
+       }
+       
+       return retArray;
+}
+String.prototype.trim = function(str){
+       return this.replace(/^[       ]+|[          ]+$/g, "").replace(/\n$/g, "");
+}
+//http://d.hatena.ne.jp/favril/20090514/1242280476
+String.prototype.isKanjiAt = function(index){
+       var u = this.charCodeAt(index);
+       if( (0x4e00  <= u && u <= 0x9fcf) ||    // CJK統合漢字
+               (0x3400  <= u && u <= 0x4dbf) ||        // CJK統合漢字拡張A
+               (0x20000 <= u && u <= 0x2a6df) ||       // CJK統合漢字拡張B
+               (0xf900  <= u && u <= 0xfadf) ||        // CJK互換漢字
+               (0x2f800 <= u && u <= 0x2fa1f)){        // CJK互換漢字補助
+               return true;
+       }
+    return false;
+}
+String.prototype.isHiraganaAt = function(index){
+       var u = this.charCodeAt(index);
+       if(0x3040 <= u && u <= 0x309f){
+               return true;
+       }
+       return false;
+}
+String.prototype.isKatakanaAt = function(index){
+       var u = this.charCodeAt(index);
+       if(0x30a0 <= u && u <= 0x30ff){
+               return true;
+       }
+       return false;
+}
+String.prototype.isHankakuKanaAt = function(index){
+       var u = this.charCodeAt(index);
+       if(0xff61 <= u && u <= 0xff9f){
+               return true;
+       }
+       return false;
+}
+
+//
+// メインクラス
+//
+
+function AI(){
+       //サブクラス
+       this.input = new AI_Input(this);
+       this.wordRecognition = new AI_WordRecognition(this);
+       //出力関連
+       this.outputTimer = null;
+       this.messageBox = null;
+       this.messageBoxBuffer = "";
+       this.maxMessageStringLength = 0xffffff;
+       this.debugBox = null;
+       this.debugBoxBuffer = "";
+       this.maxDebugStringLength = 0xffff;
+       
+}
+AI.prototype = {
+       sendToAI: function(str){
+               this.debug("**** Start thinking ****\n");
+               this.debug("input:[" + str + "]\n");
+               this.input.appendInput(str);
+               for(;;){
+                       var s = this.input.getSentence();
+                       if(s === undefined){
+                               break;
+                       }
+                       this.message(s + "\n");
+               }
+               this.wordRecognition.sortCandidateWordListByWordCount();
+               this.wordRecognition.computeEachWordLevel();
+               this.wordRecognition.sortCandidateWordListByWordLevel();
+               this.wordRecognition.debugShowCandidateWordList();
+               this.debug("**** End thinking ****\n");
+       },
+       setMessageBoxDOMObject: function(mBoxObj){
+               this.messageBox = mBoxObj;
+               this.setOutputTimer();
+       },
+       setDebugBoxDOMObject: function(dBoxObj){
+               this.debugBox = dBoxObj;
+               this.setOutputTimer();
+       },
+       message: function(str){
+               if(this.messageBox){
+                       this.messageBoxBuffer += "AI> " + str;
+               }
+       },
+       debug: function(str){
+               if(this.debugBox){
+                       this.debugBoxBuffer += str;
+               }
+       },
+       outputShowTick: function(){
+               if(this.messageBox && this.messageBoxBuffer != ""){
+                       //messageBox
+                       var str = this.messageBox.innerHTML + this.messageBoxBuffer;
+                       this.messageBoxBuffer = "";
+                       if(str.length > this.maxMessageStringLength){
+                               str = str.slice(str.length - (this.maxMessageStringLength >> 1));
+                       }
+                       this.messageBox.innerHTML = str;
+                       this.messageBox.scrollTop = this.messageBox.scrollHeight;
+               }
+               if(this.debugBox && this.debugBoxBuffer != ""){
+                       //debugBox
+                       var str = this.debugBox.innerHTML + this.debugBoxBuffer;
+                       this.debugBoxBuffer = "";
+                       if(str.length > this.maxDebugStringLength){
+                               str = str.slice(str.length - (this.maxDebugStringLength >> 1));
+                       }
+                       this.debugBox.innerHTML = str;
+                       this.debugBox.scrollTop = this.debugBox.scrollHeight;
+               }
+       },
+       setOutputTimer: function(){
+               if(!this.messageBox && !this.debugBox){
+                       //すべて無効だったらタイマーの動作自体を止める
+                       window.clearTimeout(this.outputTimer);
+                       this.outputTimer = null;
+               } else if(!this.outputTimer){
+                       //どれかが有効でかつタイマーが止まっていたらスタートさせる
+                       var that = this;
+                       this.outputTimer = window.setInterval(function(){that.outputShowTick();}, 50);
+               }
+       },
+}
+
+//
+//サブクラス
+//
+
+function AI_WordRecognition(env){
+       this.env = env;
+       this.candidateWordList = new Array();
+}
+AI_WordRecognition.prototype = {
+       slideLookUpCandidateWordByHistory: function(input){
+               var h = this.env.input.historyList;
+               var cList = new Array();
+               for(var i = 0, iLen = input.length; i < iLen; i++){
+                       //input character loop
+                       var iStr = input.substr(i);
+                       var cLen = 0;
+                       var cStr = "";
+                       for(var j = 0, jLen = h.length; j < jLen; j++){
+                               //history entry loop
+                               var hStrBase = h[j];
+                               for(var k = 0, kLen = hStrBase.length; k < kLen; k++){
+                                       //history character loop
+                                       var hStr = hStrBase.substr(k);
+                                       var m = hStr.compareLeftHand(iStr);
+                                       if(m > cLen && m != iStr.length){
+                                               cLen = m;
+                                       }
+                               }
+                       }
+                       if(cLen > 0){
+                               cList.pushUnique(new AI_WordTag(iStr.substr(0, cLen))).wordCount++;
+                       }
+               }
+               //フィルター
+               this.filterCandidateWordList00(cList);
+               this.filterCandidateWordList01(cList, 2);
+               //追加
+               this.mergeCandidateWordList(cList);
+       },
+       appendCandidateWordList: function(strTag){
+               var s = this.candidateWordList.isIncluded(strTag, function(a, b){ return (a.str == b.str); });
+               if(s){
+                       s.wordCount++;
+               } else{
+                       strTag.wordCount = 1;
+                       this.candidateWordList.push(strTag);
+               }
+       },
+       mergeCandidateWordList: function(strTagList){
+               for(var i = 0, iLen = strTagList.length; i < iLen; i++){
+                       this.appendCandidateWordList(strTagList[i]);
+               }
+       },
+       debugShowCandidateWordList: function(){
+               this.env.debug("candidateWordList:\n");
+               var c = this.candidateWordList;
+               for(var i = 0, iLen = c.length; i < iLen; i++){
+                       this.env.debug(c[i].wordCount.toString() + " :" + c[i].wordLevel.toString() + " :" + c[i].str + "\n");
+               }
+               this.env.debug("candidateWordList end\n");
+       },
+       filterCandidateWordList00:function(cList){
+               //00:長い単語に含まれており、かつ出現頻度が長い単語と等しい単語を削除
+               //cList内の候補単語に対して、フィルターをかける。
+               var iLen = cList.length;
+               if(iLen < 1){
+                       return;
+               }
+               var baseStrTag = cList[0];
+               for(var i = 1; i < iLen; i++){
+                       var c = cList[i];
+                       if(baseStrTag.str.indexOf(c.str) != -1){
+                               //c.strはbaseStrTag.strに含まれている
+                               if(baseStrTag.wordCount == c.wordCount){
+                                       //かつ出現回数が等しいので不要な単語
+                                       //後で削除する。出現回数を0にマークする。
+                                       c.wordCount = 0;
+                               }
+                       }
+                       if(c.wordCount > 0){
+                               //単語は削除されなかった、つまり異なる単語なので、baseStrTagを更新
+                               var baseStrTag = c;
+                       }
+               }
+               //削除処理
+               for(var i = 1; i < iLen; i++){
+                       var c = cList[i];
+                       if(c.wordCount == 0){
+                               cList.removeByIndex(i);
+                               i--;
+                               iLen--;
+                       }
+               }
+       },
+       filterCandidateWordList01:function(cList, minLen){
+               //01:minLenに満たない文字数の候補を削除
+               //削除処理
+               var iLen = cList.length;
+               for(var i = 0; i < iLen; i++){
+                       if(cList[i].str.length < minLen){
+                               cList.removeByIndex(i);
+                               i--;
+                               iLen--;
+                       }
+               }
+       },
+       filterCandidateWordList02:function(cList, minCount){
+               //02:minCountに満たない出現回数の候補を削除
+               //削除処理
+               var iLen = cList.length;
+               for(var i = 0; i < iLen; i++){
+                       if(cList[i].wordCount < minCount){
+                               cList.removeByIndex(i);
+                               i--;
+                               iLen--;
+                       }
+               }
+       },
+       sortCandidateWordListByWordCount: function(){
+               this.candidateWordList.stableSort(function(a, b){
+                       return a.wordCount - b.wordCount;
+               });
+       },
+       sortCandidateWordListByWordLevel: function(){
+               this.candidateWordList.stableSort(function(a, b){
+                       return a.wordLevel - b.wordLevel;
+               });
+       },
+       computeWordLevel: function(strTag){
+               var s = strTag.str;
+               var iLen = s.length;
+               var f = 0;
+               strTag.wordLevel = 0;
+               //文字列中の文字種数を数える
+               for(var i = 0; i < iLen; i++){
+                       if(s.isHiraganaAt(i)){
+                               f |= 0x01;
+                       } else if(s.isKanjiAt(i)){
+                               f |= 0x02;
+                       } else if(s.isKatakanaAt(i)){
+                               f |= 0x04;
+                       } else if(s.isHankakuKanaAt(i)){
+                               f |= 0x08;
+                       } else{
+                               f |= 0x10;
+                       }
+               }
+               for(var i = 0; i < 5; i++){
+                       if((f & 0x01) != 0){
+                               strTag.wordLevel++;
+                       }
+                       f >>>= 1;
+               }
+               strTag.wordLevel = 1 / strTag.wordLevel;
+               return;
+       },
+       computeEachWordLevel: function(){
+               var iLen = this.candidateWordList.length;
+               for(var i = 0; i < iLen; i++){
+                       this.computeWordLevel(this.candidateWordList[i]);
+               }
+       }
+}
+
+function AI_WordTag(str){
+       this.str = str;
+       this.wordCount = 0;
+       this.wordLevel = 0;
+}
+
+function AI_Input(env){
+       this.env = env;
+       this.historyList = new Array();
+       this.sentenceList = new Array();
+}
+AI_Input.prototype = {
+       maxHistoryLength: 32,
+       sentenceSeparator: [
+               "。",
+               "!",
+               "?",
+               "!",
+               "?",
+               "\n",
+       ],
+       appendInput: function(str){
+               var sList = str.splitByArray(this.sentenceSeparator);
+               
+               this.sentenceList = this.sentenceList.concat(sList)
+       },
+       getSentence: function(){
+               //改行のみの文は破棄
+               for(;;){
+                       if(this.sentenceList.length <= 0){
+                               return undefined;
+                       }
+                       var retv = this.sentenceList[0];
+                       this.sentenceList.splice(0, 1);
+                       retv = retv.trim();
+                       if(retv != ""){
+                               break;
+                       }
+               }
+               //ここで単語候補抽出を行っておく
+               this.env.wordRecognition.slideLookUpCandidateWordByHistory(retv);
+               //
+               this.appendHistory(retv);
+               return retv;
+       },
+       appendHistory: function(str){
+               this.historyList.push(str);
+               if(this.historyList.length > this.maxHistoryLength){
+                       this.historyList.splice(0, this.maxHistoryLength >> 1);
+               }
+       },
+}
diff --git a/index.html b/index.html
new file mode 100644 (file)
index 0000000..7710484
--- /dev/null
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="X-UA-Compatible" content="IE=9">
+<meta charset="UTF-8">
+<title>AI004</title>
+<style type="text/css">
+       h1, h2, h3 {
+               margin:0px;
+       }
+       body, textarea {
+               font-family: Consolas, 'Courier New', Courier, Monaco, monospace;
+               font-size: 14px;
+               line-height: 1.2;
+       }
+</style>
+<script type="text/javascript" src="./ai.js" charset="UTF-8"></script>
+<script type="text/javascript">
+var mainAI = null;
+var inputBoxObj = null;
+onload = function() {
+       mainAI = new AI();
+       inputBoxObj = document.getElementById("inputBox");
+       inputBoxObj.onkeydown = sendToAI;
+       mainAI.setMessageBoxDOMObject(document.getElementById("messageBox"));
+       mainAI.setDebugBoxDOMObject(document.getElementById("debugBox"));
+}
+
+function sendToAI(e){
+       //Enterで送信
+       //Shift+Enterで改行
+       if (e.keyCode == 13){ // Enterが押された
+               if(!e.shiftKey && inputBoxObj.value.replace(/\s/g, "").length > 0){
+                       e.preventDefault();
+                       mainAI.sendToAI(inputBoxObj.value);
+                       inputBoxObj.value = '';
+               }
+       }
+}
+
+</script>
+</head>
+<body>
+<h1>AI004</h1>
+<div style="float:left;">
+       <h2>Message</h2>
+       <textarea id="messageBox" cols="64" rows="32"></textarea>
+</div>
+<div style="float:left;">
+       <h2>Debug</h2>
+       <textarea id="debugBox" cols="64" rows="32"></textarea>
+</div>
+<div style="clear:both;">
+       <h2>Input</h2>
+       <form onsubmit="return false;">
+               <textarea id="inputBox" cols="128" rows="8"></textarea>
+       </form>
+</div>
+</body>
+</html>
\ No newline at end of file