原創(chuàng )作者:一粒馬豆&冰豆小李
首先讓我們來(lái)看看這樣一種語(yǔ)言現象:
研表究明,漢字序順并不定一影閱響讀。
Aoccdrnig to a rscheearch at an Elingsh uinervtisy, it deosn't mttaer in waht oredr the ltteers in a wrod are, the olny iprmoetnt tihng is taht the frist and lsat ltteer is at the rghit pclae. The rset can be a toatl mses and you can sitll raed it wouthit porbelm. Tihs is bcuseae we do not raed ervey lteter by it slef but the wrod as a wlohe.
以上著(zhù)兩段話(huà)雖然字序被打亂,但是你依然能夠很好的理解它們的意思,對不對?這就是大腦并行運算識別的一種智能模式,如果通過(guò)程序來(lái)模擬這種大腦認知的機制是實(shí)現智能機器人的關(guān)鍵之一。還好我們有正則表達式,而正則表達式發(fā)明的初衷也是為了模擬神經(jīng)網(wǎng)絡(luò )和智能結構。
要以最簡(jiǎn)易的方式實(shí)現一個(gè)智能聊天機器人需要哪些關(guān)鍵技術(shù)?
關(guān)鍵技術(shù)和原理:
1. 中文分詞
2. 正則表達式
對輸入的一句話(huà)應用中文分詞解析出關(guān)鍵詞,然后應用正則表達式用解析出的關(guān)鍵詞并行檢索問(wèn)答庫里的問(wèn)題,計算出匹配程度,問(wèn)答庫中匹配程度最高的一個(gè)問(wèn)題的答案就可以用來(lái)應答這句輸入的話(huà)語(yǔ)。
效果圖:
上代碼:
<SCRIPT TYPE="TEXT/JAVASCRIPT">function found(keyword){ if(W[keyword]==1){ return true; }else{ return false; }} //關(guān)閉數據庫連接//dataBaseConnectionObject.Close(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////function parseChinese(inputSentence){//正向關(guān)鍵詞最長(cháng)匹配法 //刪除待處理字符串頭部的空格 inputSentence=inputSentence.replace(/(^\s+)/,""); var inputSentenceLength=inputSentence.length; var wordMaxLength=7; var resultArray=new Array(); var resultString=""; var recognizedWords=""; //如果輸入句長(cháng)度小于詞典詞匯長(cháng)度則令最大長(cháng)度等于句子長(cháng)度 if(inputSentenceLength<=wordMaxLength){ wordMaxLength=inputSentenceLength; } //增加三個(gè)空格方便尾部處理,空格數=wordMaxLength-1 inputSentence+=" "; for(var i=0;i<inputSentenceLength;i++){ //尾部還需特殊處理 for(var j=wordMaxLength;j>0;j--){ var checkword=inputSentence.substr(i,j); if(found(checkword)){ //alert(checkword); recognizedWords+=checkword+","; resultArray.push(checkword); //注意后面需要-1,因為FOR循環(huán)會(huì )自動(dòng)加1 i=i+j-1; break; }else{ if(j==1){ resultArray.push(checkword); } } } } //處理所得到的結果數組 //由于前面的過(guò)程會(huì )把英文單詞分割成字母,現在需要連接起來(lái) for(var i=0;i<resultArray.length-1;i++){ var regw=/\w/; if(regw.test(resultArray[i])&®w.test(resultArray[i+1])){ resultArray[i+1]=resultArray[i]+resultArray[i+1]; resultArray[i]=""; } } //結果數組轉換成結果字符串 resultString=resultArray.join(" "); //替換掉里面多余的空格 resultString=resultString.replace(/\s{2,}/g," "); //替換掉多余的逗號去除尾部空元素 resultString=resultString.replace(/,$/,""); //重新將字符串轉換回數組 //去除識別字符串里的重復元素 if(resultString==""){ resultArray=new Array(); }else{ resultArray=resultString.split(" ").reverse().join(",").match(/([^,]+)(?!.*\1)/ig).reverse(); } //本分詞函數對象各個(gè)屬性最終值 recognizedWords=recognizedWords.replace(/,$/,""); this.recognizedWords=recognizedWords; //注意輸入值為空或者不匹配的情況 if(this.recognizedWords==""){ this.recognizedWordsArray=new Array(); }else{ this.recognizedWordsArray=recognizedWords.split(",").reverse().join(",").match(/([^,]+)(?!.*\1)/ig).reverse(); //ar.reverse().join(",").match(/([^,]+)(?!.*\1)/ig).reverse()//刪除數組中重復元素 } this.resultString=resultString; this.resultArray=resultArray;}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////function parseChineseBackward(inputSentence){//逆向關(guān)鍵詞最長(cháng)匹配 //刪除待處理字符串頭部的空格 inputSentence=inputSentence.replace(/(^\s+)/,""); var wordMaxLength=7; var resultArray=new Array(); var resultString=""; var recognizedWords=""; //如果輸入句長(cháng)度小于詞典詞匯長(cháng)度則令最大長(cháng)度等于句子長(cháng)度 if(inputSentenceLength<=wordMaxLength){ wordMaxLength=inputSentenceLength; } //增加幾個(gè)空格方便尾部處理,空格數=wordMaxLength-1 inputSentence=" "+inputSentence; var inputSentenceLength=inputSentence.length; for(var i=inputSentenceLength-1;i>=wordMaxLength-1;i--){ //尾部還需特殊處理 for(var j=wordMaxLength;j>=1;j--){ var checkword=inputSentence.substr(i-j+1,j); if(found(checkword)){ //alert(checkword); recognizedWords+=checkword+","; resultArray.push(checkword); //注意后面需要+1,因為FOR循環(huán)會(huì )自動(dòng)j減數1 i=i-j+1; break; }else{ if(j==1){ resultArray.push(checkword); } } } } //翻轉結果數組 resultArray.reverse(); //處理所得到的結果數組 //由于前面的過(guò)程會(huì )把英文單詞分割成字母,現在需要連接起來(lái) for(var i=0;i<resultArray.length-1;i++){ var regw=/\w/; if(regw.test(resultArray[i])&®w.test(resultArray[i+1])){ resultArray[i+1]=resultArray[i]+resultArray[i+1]; resultArray[i]=""; } } //結果數組轉換成結果字符串 resultString=resultArray.join(" "); //替換掉里面多余的空格 resultString=resultString.replace(/\s{2,}/g," "); //替換掉多余的逗號去除尾部空元素 resultString=resultString.replace(/,$/,""); //重新將字符串轉換回數組 //去除識別字符串里的重復元素 if(resultString==""){ resultArray=new Array(); }else{ resultArray=resultString.split(" ").reverse().join(",").match(/([^,]+)(?!.*\1)/ig).reverse(); } //本分詞函數對象各個(gè)屬性最終值 //識別出來(lái)的關(guān)鍵詞字串 recognizedWords=recognizedWords.replace(/,$/,""); this.recognizedWords=recognizedWords; //注意輸入值為空或者不匹配的情況 if(this.recognizedWords==""){ this.recognizedWordsArray=new Array(); }else{ //識別出來(lái)的關(guān)鍵詞字串轉換數組 this.recognizedWordsArray=recognizedWords.split(",").reverse().join(",").match(/([^,]+)(?!.*\1)/ig).reverse(); //ar.reverse().join(",").match(/([^,]+)(?!.*\1)/ig).reverse()//刪除數組中重復元素 } this.resultString=resultString; this.resultArray=resultArray; }////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////function matchRate(string1,string2){//任意兩句話(huà)的話(huà)題匹配程度(只考慮二者的關(guān)鍵詞) var matchScore1=0; var matchScore2=0; var chineseWords1=new parseChinese(string1); var chineseWords2=new parseChinese(string2); var keywordsArray1=chineseWords1.recognizedWordsArray; var keywordsArray2=chineseWords2.recognizedWordsArray; var keywordsString1="#"+keywordsArray1.join("##")+"#"; var keywordsString2="#"+keywordsArray2.join("##")+"#"; var keywordsStringRegX1="/"+"#"+keywordsArray1.join("#|#")+"#/gi"; var keywordsStringRegX2="/"+"#"+keywordsArray2.join("#|#")+"#/gi"; //用string2的關(guān)鍵詞去匹配string1 var matchArray1=new Array(); matchArray1=keywordsString1.match(eval(keywordsStringRegX2)); //用string1的關(guān)鍵詞去匹配string2 var matchArray2=new Array(); matchArray2=keywordsString2.match(eval(keywordsStringRegX1)); if(matchArray1!=null){ matchScore1=matchArray1.length; }else{ matchScore1=0; } if(keywordsArray2.length>0){ matchScore1=matchScore1/keywordsArray2.length*100; }else{ matchScore1=0; } if(matchArray2!=null){ matchScore2=matchArray2.length; }else{ matchScore2=0; } if(keywordsArray1.length>0){ matchScore2=matchScore2/keywordsArray1.length*100; }else{ matchScore2=0; } //取互相匹配的值的平均值為最終結果 var averageScore=(matchScore1+matchScore2)*0.5; return averageScore;}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////function matchAllRate(string1,string2){//任意兩句話(huà)的話(huà)題匹配程度(只考慮二者的關(guān)鍵詞) var matchScore1=0; var matchScore2=0; var chineseWords1=new parseChinese(string1); var chineseWords2=new parseChinese(string2); var keywordsArray1=chineseWords1.resultArray; var keywordsArray2=chineseWords2.resultArray; var keywordsString1="#"+keywordsArray1.join("##")+"#"; var keywordsString2="#"+keywordsArray2.join("##")+"#"; var keywordsStringRegX1="/"+"#"+keywordsArray1.join("#|#")+"#/gi"; var keywordsStringRegX2="/"+"#"+keywordsArray2.join("#|#")+"#/gi"; //用string2的關(guān)鍵詞去匹配string1 var matchArray1=new Array(); matchArray1=keywordsString1.match(eval(keywordsStringRegX2)); //用string1的關(guān)鍵詞去匹配string2 var matchArray2=new Array(); matchArray2=keywordsString2.match(eval(keywordsStringRegX1)); if(matchArray1!=null){ matchScore1=matchArray1.length; }else{ matchScore1=0; } if(keywordsArray2.length>0){ matchScore1=matchScore1/keywordsArray2.length*100; }else{ matchScore1=0; } if(matchArray2!=null){ matchScore2=matchArray2.length; }else{ matchScore2=0; } if(keywordsArray1.length>0){ matchScore2=matchScore2/keywordsArray1.length*100; }else{ matchScore2=0; } //取互相匹配的值的平均值為最終結果 var averageScore=(matchScore1+matchScore2)*0.5; return averageScore;}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////function compare(){//比較任意兩句話(huà)的話(huà)題相關(guān)度//先識別出兩個(gè)句子的關(guān)鍵詞,然后通過(guò)比較二者關(guān)鍵詞的匹配程度來(lái)確定//核心算法就是matchScore=string1.match(/keyword1/keyword2/keyword3/g)/keywordCount//也可以直接比較所有分詞,那樣就是純字面比較了 var matchScore1=0; var matchScore2=0; var string1=document.getElementById("inputString").value; var string2=document.getElementById("inputStringMatch").value; //注意去除字符串中的特殊字符如.*?!,否則匹配結果數值不準 string1=string1.replace(/\^|\.|\*|\?|\!|\/|\\|\$|\#|\&|\||,|\[|\]|\{|\}|\(|\)|\-|\+|\=/g," "); string2=string2.replace(/\^|\.|\*|\?|\!|\/|\\|\$|\#|\&|\||,|\[|\]|\{|\}|\(|\)|\-|\+|\=/g," "); var chineseWords1=new parseChinese(string1); var chineseWords2=new parseChinese(string2); var keywordsArray1=chineseWords1.recognizedWordsArray; var keywordsArray2=chineseWords2.recognizedWordsArray; var keywordsString1="#"+keywordsArray1.join("##")+"#"; var keywordsStringRegX1="/"+"#"+keywordsArray1.join("#|#")+"#/gi"; var keywordsString2="#"+keywordsArray2.join("##")+"#"; var keywordsStringRegX2="/"+"#"+keywordsArray2.join("#|#")+"#/gi"; var matchArray1=new Array(); //用string2的關(guān)鍵詞去匹配string1 matchArray1=keywordsString1.match(eval(keywordsStringRegX2)); var matchArray2=new Array(); //用string2的關(guān)鍵詞去匹配string1 matchArray2=keywordsString2.match(eval(keywordsStringRegX1)); if(matchArray1!=null){ matchScore1=matchArray1.length; }else{ matchScore1=0; } if(keywordsArray2.length>0){ matchScore1=matchScore1/keywordsArray2.length*100; }else{ matchScore1=0; } if(matchArray2!=null){ matchScore2=matchArray2.length; }else{ matchScore2=0; } if(keywordsArray1.length>0){ matchScore2=matchScore2/keywordsArray1.length*100; }else{ matchScore2=0; } matchScore1=Math.round(matchScore1); matchScore2=Math.round(matchScore2); var scoreMin=Math.min(matchScore1,matchScore2); var scoreMax=Math.max(matchScore1,matchScore2); var msg="[句1] "+string1+"\n[句2] "+string2 +"\n\n[關(guān)鍵詞串1]"+keywordsString1 +"\n[關(guān)鍵詞串2]"+keywordsString2 +"\n\n[正則表達式1] "+keywordsStringRegX1 +"\n[正則表達式2] "+keywordsStringRegX2 +"\n\n二者話(huà)題匹配度:介于 "+scoreMin+"%"+" 到 "+scoreMax+"% 之間"; alert(msg);}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////function compareLiteral(){//比較任意兩句話(huà)的話(huà)題相關(guān)度//先識別出兩個(gè)句子的關(guān)鍵詞,然后通過(guò)比較二者關(guān)鍵詞的匹配程度來(lái)確定//核心算法就是matchScore=string1.match(/keyword1/keyword2/keyword3/g)/keywordCount//此處是字面匹配 var matchScore1=0; var matchScore2=0; var string1=document.getElementById("inputString").value; var string2=document.getElementById("inputStringMatch").value; //注意去除字符串中的特殊字符如.*?!,否則匹配結果數值不準 string1=string1.replace(/\^|\.|\*|\?|\!|\/|\\|\$|\#|\&|\||,|\[|\]|\{|\}|\(|\)|\-|\+|\=/g," "); string2=string2.replace(/\^|\.|\*|\?|\!|\/|\\|\$|\#|\&|\||,|\[|\]|\{|\}|\(|\)|\-|\+|\=/g," "); var chineseWords1=new parseChinese(string1); var chineseWords2=new parseChinese(string2); var keywordsArray1=chineseWords1.resultArray; var keywordsArray2=chineseWords2.resultArray; var keywordsString1="#"+keywordsArray1.join("##")+"#"; var keywordsStringRegX1="/"+"#"+keywordsArray1.join("#|#")+"#/gi"; var keywordsString2="#"+keywordsArray2.join("##")+"#"; var keywordsStringRegX2="/"+"#"+keywordsArray2.join("#|#")+"#/gi"; var matchArray1=new Array(); //用string2的關(guān)鍵詞去匹配string1 matchArray1=keywordsString1.match(eval(keywordsStringRegX2)); var matchArray2=new Array(); //用string2的關(guān)鍵詞去匹配string1 matchArray2=keywordsString2.match(eval(keywordsStringRegX1)); if(matchArray1!=null){ matchScore1=matchArray1.length; }else{ matchScore1=0; } if(keywordsArray2.length>0){ matchScore1=matchScore1/keywordsArray2.length*100; }else{ matchScore1=0; } if(matchArray2!=null){ matchScore2=matchArray2.length; }else{ matchScore2=0; } if(keywordsArray1.length>0){ matchScore2=matchScore2/keywordsArray1.length*100; }else{ matchScore2=0; } matchScore1=Math.round(matchScore1); matchScore2=Math.round(matchScore2); var scoreMin=Math.min(matchScore1,matchScore2); var scoreMax=Math.max(matchScore1,matchScore2); var msg="[句1] "+string1+"\n[句2] "+string2 +"\n\n[關(guān)鍵詞串1]"+keywordsString1 +"\n[關(guān)鍵詞串2]"+keywordsString2 +"\n\n[正則表達式1] "+keywordsStringRegX1 +"\n[正則表達式2] "+keywordsStringRegX2 +"\n\n二者話(huà)題匹配度:介于 "+scoreMin+"%"+" 到 "+scoreMax+"% 之間"; alert(msg); }///////////////////////////////////////////////////////////////////////function talk(){//人工智能聊天//主要應用了前面的分詞、模糊匹配技術(shù) var bestAnswerIndex=0; var bestMatchScore=0; var questionString=document.getElementById("question").value; document.getElementById("question").value=""; var now=new Date().toLocaleTimeString(); //顯示問(wèn)題到屏幕上 var outputString="<div class='questionbox'><br />"+now+" 冰豆小李<br /><br /><div class='speech'>"+questionString+"</div><br /></div>"; document.getElementById("dialogDisplay").innerHTML+=outputString; document.getElementById("dialogDisplay").scrollTop=document.getElementById("dialogDisplay").scrollHeight; //去除特殊符號以免后續匹配出錯 questionString=questionString.replace(/\^|\.|\*|\?|\!|\/|\\|\$|\#|\&|\||,|\[|\]|\{|\}|\(|\)|\-|\+|\=/g," "); //搜尋最接近的問(wèn)答 for(var i=0;i<QA_Count;i++){ var answerQ=QA[i].Q; answerQ=answerQ.replace(/\^|\.|\*|\?|\!|\/|\\|\$|\#|\&|\||,|\[|\]|\{|\}|\(|\)|\-|\+|\=/g," "); var matchScore=matchAllRate(questionString,answerQ); //優(yōu)選出匹配度最高的 if(matchScore>=bestMatchScore){ bestMatchScore=matchScore; bestAnswerIndex=i; } //如果遇到全匹配則跳出搜索循環(huán) if(matchScore==100){ break; } //如果識別率為零則生成一個(gè)隨機應答索引 //問(wèn)答知識庫頭10條記錄是為這種情況設定的 if(bestMatchScore==0){ bestAnswerIndex=Math.floor(Math.random()*10); } } //依據所獲的索引號提取出問(wèn)題的最佳答案 var answerString=QA[bestAnswerIndex].A; //不能顯示太快,否則機器人的痕跡太明顯,所以要延遲顯示 var delayTimer=setTimeout(showAnswer,1000); //顯示問(wèn)題的答案到屏幕上 function showAnswer(){ var now=new Date().toLocaleTimeString(); var outputString="<div class='answerbox'><br />一粒馬豆 "+now+"<br /><br /><div class='speech'>"+answerString+"</div><br /></div>"; document.getElementById("dialogDisplay").innerHTML+=outputString; document.getElementById("dialogDisplay").scrollTop=document.getElementById("dialogDisplay").scrollHeight; }}</SCRIPT>中文分詞詞庫以JSON格式存儲在ChineseDictionary.js文件中,
類(lèi)似這樣:
//注意:由于數組鍵名采用中文,文件必須采用UTF-8編碼格式保存,否則可能無(wú)法識別//以中文詞語(yǔ)作為數組鍵名極大的簡(jiǎn)化了后續的操作,是復雜度降低為一步//詞匯來(lái)源于百度分詞詞庫部分內容,總詞匯量達十多萬(wàn)條var W=new Array();W["印第安納波利斯"]=1;W["氧化琥珀酰膽堿"]=1;W["烏里揚諾夫斯克"]=1;W["蘇克·阿赫拉斯"]=1;W["水楊酸毒扁豆堿"]=1;W["圣佩德羅-蘇拉"]=1;W["圣基茨和尼維斯"]=1;W["瑟伊藻克羅屈爾"]=1;W["梅爾庫里亞丘克"]=1;W["馬斯耶德所羅門(mén)"]=1;W["洛赫吉爾普黑德"]=1;W["氯甲酸三氯甲酯"]=1;W["勒姆尼庫沃爾恰"]=1;W["坎星頓-切爾西"]=1;W["卡加延-德奧羅"]=1;W["聚乙烯醇縮甲醛"]=1;W["聚乙烯醇縮丁醛"]=1;W["甲基丙烯酸甲酯"]=1;W["磺胺二甲基嘧啶"]=1;W["哈拉帕恩里克斯"]=1;W["符拉迪沃斯托克"]=1;W["二巰基丁二酸鈉"]=1;W["二巰基丙磺酸鈉"]=1;W["多斯格布拉達斯"]=1;W["布宜諾斯艾利斯"]=1;W["布拉戈維申斯克"]=1;W["博斯普魯斯海峽"]=1;W["彼得羅扎沃茨克"]=1;W["安提瓜和巴布達"]=1;
類(lèi)似這樣:
//問(wèn)答集var QA=new Array();var QA_Count=QA.length;QA=[//前十個(gè)問(wèn)題用于無(wú)法識別時(shí)隨機應答{Q:"",A:"有點(diǎn)暈乎"},{Q:"",A:"抱歉,不是太明白。"},{Q:"",A:"太高深了,我的理解力有限。"},{Q:"",A:"真心不懂。"},{Q:"",A:"此處省略一千字。"},{Q:"",A:"換個(gè)話(huà)題好不好?"},{Q:"",A:"腦筋有點(diǎn)轉不過(guò)彎來(lái)。"},{Q:"",A:"談點(diǎn)別的好嗎?"},{Q:"",A:"能否說(shuō)的更加具體點(diǎn)?"},{Q:"",A:"讓我的思維再飛一會(huì )吧。"},//用于英文的識別{Q:"Hello!",A:"Hi!"},{Q:"Hi!",A:"Hello!"},{Q:"Thank you!",A:"You're welcome!"},{Q:"Thanks!",A:"You're welcome!"},{Q:"Where are you from?",A:"I'am from China."},{Q:"How old are you?",A:"I was born with the universe."},{Q:"What's your name?",A:"My name is Bill"},{Q:"Good bye!",A:"See you!"},{Q:"How much?",A:"Invaluable!"},{Q:"Really?",A:"Really."},{Q:"Very good!",A:"Wonderful!"},//主要問(wèn)答記錄部分{Q:"好啊",A:"真爽快"},{Q:"天地玄黃",A:"宇宙洪荒"},{Q:"你會(huì )說(shuō)中文嗎?",A:"母語(yǔ)當然會(huì )說(shuō)啦。"},{Q:"你叫什么名字?",A:"一粒馬豆。"},{Q:"你來(lái)自于哪里?",A:"我來(lái)自宇宙。"},{Q:"你年齡多大啦?",A:"宇宙誕生時(shí)我就已經(jīng)存在,只不過(guò)我的樣子一直在變化。"},{Q:"你要到哪里去?",A:"我要去未來(lái)。"},{Q:"你是人還是機器人?",A:"我一半是人,一半是機器。"},{Q:"你是誰(shuí)?",A:"未曾生我誰(shuí)是我?生我之時(shí)我是誰(shuí)?長(cháng)大成人方是我,合眼蒙眬又是誰(shuí)?"},{Q:"當然啦!",A:"不要想當然。"},{Q:"正是",A:"是非要分明"},{Q:"對嗎?",A:"其實(shí)答案在你內心,你去尋找就可以找到。"},{Q:"不明覺(jué)厲",A:"云里霧里"},{Q:"今天天氣怎樣?",A:"不管刮風(fēng)下雨還是陽(yáng)光燦爛,我心依舊。"},{Q:"價(jià)格多少?",A:"價(jià)值比價(jià)格更重要."},{Q:"你好。",A:"你好、我好、大家都好!"},{Q:"高興",A:"心情好什么都好。"},{Q:"悲劇",A:"悲劇是暫時(shí)的,戲劇是永恒的。"},{Q:"真的假的?",A:"假作真時(shí)真亦假"},聯(lián)系客服