學習筆記CB011:lucene搜索引擎庫涩咖、IKAnalyzer中文切詞工具海诲、檢索服務、查詢索引檩互、導流特幔、word2vec

影視劇字幕聊天語料庫特點,把影視劇說話內容一句一句以回車換行羅列三千多萬條中國話闸昨,相鄰第二句很可能是第一句最好回答蚯斯。一個問句有很多種回答,可以根據相關程度以及歷史聊天記錄所有回答排序饵较,找到最優(yōu)拍嵌,是一個搜索排序過程。

lucene+ik循诉。lucene開源免費搜索引擎庫横辆,java語言開發(fā)。ik IKAnalyzer茄猫,開源中文切詞工具狈蚤。語料庫切詞建索引,文本搜索做文本相關性檢索划纽,把下一句取出作答案候選集脆侮,答案排序,問題分析阿浓。

建索引他嚷。eclipse創(chuàng)建maven工程,maven自動生成pom.xml文件芭毙,配置包依賴信息,dependencies標簽中添加依賴:

<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-core</artifactId>
    <version>4.10.4</version>
</dependency>
<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-queryparser</artifactId>
    <version>4.10.4</version>
</dependency>
<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-analyzers-common</artifactId>
    <version>4.10.4</version>
</dependency>
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>5.0.0.Alpha2</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.1.41</version>
</dependency>

project標簽增加配置卸耘,依賴jar包自動拷貝lib目錄:

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-dependency-plugin</artifactId>
      <executions>
        <execution>
          <id>copy-dependencies</id>
          <phase>prepare-package</phase>
          <goals>
            <goal>copy-dependencies</goal>
          </goals>
          <configuration>
            <outputDirectory>${project.build.directory}/lib</outputDirectory>
            <overWriteReleases>false</overWriteReleases>
            <overWriteSnapshots>false</overWriteSnapshots>
            <overWriteIfNewer>true</overWriteIfNewer>
          </configuration>
        </execution>
      </executions>
    </plugin>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-jar-plugin</artifactId>
      <configuration>
        <archive>
          <manifest>
            <addClasspath>true</addClasspath>
            <classpathPrefix>lib/</classpathPrefix>
            <mainClass>theMainClass</mainClass>
          </manifest>
        </archive>
      </configuration>
    </plugin>
  </plugins>
</build>

https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/ik-analyzer/IK%20Analyzer%202012FF_hf1_source.rar 下載ik源代碼把src/org目錄拷到chatbotv1工程src/main/java下退敦,刷新maven工程。

com.shareditor.chatbotv1包下maven自動生成App.java蚣抗,改成Indexer.java:

Analyzer analyzer = new IKAnalyzer(true);
IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_4_9, analyzer);
iwc.setOpenMode(OpenMode.CREATE);
iwc.setUseCompoundFile(true);
IndexWriter indexWriter = new IndexWriter(FSDirectory.open(new File(indexPath)), iwc);

BufferedReader br = new BufferedReader(new InputStreamReader(
        new FileInputStream(corpusPath), "UTF-8"));
String line = "";
String last = "";
long lineNum = 0;
while ((line = br.readLine()) != null) {
    line = line.trim();

    if (0 == line.length()) {
        continue;
    }

    if (!last.equals("")) {
        Document doc = new Document();
        doc.add(new TextField("question", last, Store.YES));
        doc.add(new StoredField("answer", line));
        indexWriter.addDocument(doc);
    }
    last = line;
    lineNum++;
    if (lineNum % 100000 == 0) {
        System.out.println("add doc " + lineNum);
    }
}
br.close();

indexWriter.forceMerge(1);
indexWriter.close();

編譯拷貝src/main/resources所有文件到target目錄侈百,target目錄執(zhí)行

java -cp $CLASSPATH:./lib/:./chatbotv1-0.0.1-SNAPSHOT.jar com.shareditor.chatbotv1.Indexer ../../subtitle/raw_subtitles/subtitle.corpus ./index

生成索引目錄index通過lukeall-4.9.0.jar查看。

檢索服務翰铡。netty創(chuàng)建http服務server钝域,代碼在https://github.com/warmheartli/ChatBotCourse的chatbotv1目錄:

Analyzer analyzer = new IKAnalyzer(true);
QueryParser qp = new QueryParser(Version.LUCENE_4_9, "question", analyzer);
if (topDocs.totalHits == 0) {
    qp.setDefaultOperator(Operator.AND);
    query = qp.parse(q);
    System.out.println(query.toString());
    indexSearcher.search(query, collector);
    topDocs = collector.topDocs();
}

if (topDocs.totalHits == 0) {
    qp.setDefaultOperator(Operator.OR);
    query = qp.parse(q);
    System.out.println(query.toString());
    indexSearcher.search(query, collector);
    topDocs = collector.topDocs();
}

ret.put("total", topDocs.totalHits);
ret.put("q", q);
JSONArray result = new JSONArray();
for (ScoreDoc d : topDocs.scoreDocs) {
    Document doc = indexSearcher.doc(d.doc);
    String question = doc.get("question");
    String answer = doc.get("answer");
    JSONObject item = new JSONObject();
    item.put("question", question);
    item.put("answer", answer);
    item.put("score", d.score);
    item.put("doc", d.doc);
    result.add(item);
}
ret.put("result", result);

查詢索引,query詞做切詞拼lucene query锭魔,檢索索引question字段例证,匹配返回answer字段值作候選集,挑出候選集一條作答案迷捧。server通過http訪問织咧,如http://127.0.0.1:8765/?q=hello 胀葱。中文需轉urlcode發(fā)送,java端讀取按urlcode解析笙蒙,server啟動方法:

java -cp $CLASSPATH:./lib/:./chatbotv1-0.0.1-SNAPSHOT.jar com.shareditor.chatbotv1.Searcher

聊天界面抵屿。一個展示聊天內容框框,選擇ckeditor捅位,支持html格式內容展示轧葛,一個輸入框和發(fā)送按鈕,html代碼:

<div class="col-sm-4 col-xs-10">
    <div class="row">
        <textarea id="chatarea">
            <div style='color: blue; text-align: left; padding: 5px;'>機器人: 喂艇搀,大哥您好朝群,您終于肯跟我聊天了,來侃侃唄,我來者不拒!</div>
            <div style='color: blue; text-align: left; padding: 5px;'>機器人: 啥?你問我怎么這么聰明會聊天?因為我剛剛吃了一堆影視劇字幕!</div>
        </textarea>
    </div>
    <br />

    <div class="row">
        <div class="input-group">
            <input type="text" id="input" class="form-control" autofocus="autofocus" onkeydown="submitByEnter()" />
            <span class="input-group-btn">
            <button class="btn btn-default" type="button" onclick="submit()">發(fā)送</button>
          </span>
        </div>
    </div>
</div>

<script type="text/javascript">

        CKEDITOR.replace('chatarea',
                {
                    readOnly: true,
                    toolbar: ['Source'],
                    height: 500,
                    removePlugins: 'elementspath',
                    resize_enabled: false,
                    allowedContent: true
                });
   
</script>

調用聊天server,要一個發(fā)送請求獲取結果控制器:

public function queryAction(Request $request)
{
    $q = $request->get('input');
    $opts = array(
        'http'=>array(
            'method'=>"GET",
            'timeout'=>60,
        )
    );
    $context = stream_context_create($opts);
    $clientIp = $request->getClientIp();
    $response = file_get_contents('http://127.0.0.1:8765/?q=' . urlencode($q) . '&clientIp=' . $clientIp, false, $context);
    $res = json_decode($response, true);
    $total = $res['total'];
    $result = '';
    if ($total > 0) {
        $result = $res['result'][0]['answer'];
    }
    return new Response($result);
}

控制器路由配置:

chatbot_query:
    path:     /chatbot/query
    defaults: { _controller: AppBundle:ChatBot:query }

聊天server響應時間比較長中符,不導致web界面卡住姜胖,執(zhí)行submit時異步發(fā)請求和收結果:

var xmlHttp;
function submit() {
    if (window.ActiveXObject) {
        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    else if (window.XMLHttpRequest) {
        xmlHttp = new XMLHttpRequest();
    }
    var input = $("#input").val().trim();
    if (input == '') {
        jQuery('#input').val('');
        return;
    }
    addText(input, false);
    jQuery('#input').val('');
    var datastr = "input=" + input;
    datastr = encodeURI(datastr);
    var url = "/chatbot/query";
    xmlHttp.open("POST", url, true);
    xmlHttp.onreadystatechange = callback;
    xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    xmlHttp.send(datastr);
}

function callback() {
    if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
        var responseText = xmlHttp.responseText;
        addText(responseText, true);
    }
}

addText往ckeditor添加一段文本:

function addText(text, is_response) {
    var oldText = CKEDITOR.instances.chatarea.getData();
    var prefix = '';
    if (is_response) {
        prefix = "<div style='color: blue; text-align: left; padding: 5px;'>機器人: "
    } else {
        prefix = "<div style='color: darkgreen; text-align: right; padding: 5px;'>我: "
    }
    CKEDITOR.instances.chatarea.setData(oldText + "" + prefix + text + "</div>");
}

代碼:
https://github.com/warmheartli/ChatBotCourse
https://github.com/warmheartli/shareditor.com

效果演示:http://www.shareditor.com/chatbot/

導流。統(tǒng)計網站流量情況淀散。cnzz統(tǒng)計看最近半個月受訪頁面流量情況右莱,用戶訪問集中頁面。增加圖庫動態(tài)按鈕档插。吸引用戶點擊慢蜓,在每個頁面右下角放置動態(tài)小圖標,頁面滾動它不動郭膛,用戶點了直接跳到想要引流的頁面晨抡。搜客服漂浮代碼。
創(chuàng)建js文件则剃,lrtk.js :

$(function()
{
    var tophtml="<a href=\"http://www.shareditor.com/chatbot/\" target=\"_blank\"><div id=\"izl_rmenu\" class=\"izl-rmenu\"><div class=\"btn btn-phone\"></div><div class=\"btn btn-top\"></div></div></a>";
    $("#top").html(tophtml);
    $("#izl_rmenu").each(function()
    {
        $(this).find(".btn-phone").mouseenter(function()
        {
            $(this).find(".phone").fadeIn("fast");
        });
        $(this).find(".btn-phone").mouseleave(function()
        {
            $(this).find(".phone").fadeOut("fast");
        });
        $(this).find(".btn-top").click(function()
        {
            $("html, body").animate({
                "scroll-top":0
            },"fast");
        });
    });
    var lastRmenuStatus=false;

    $(window).scroll(function()
    {
        var _top=$(window).scrollTop();
        if(_top>=0)
        {
            $("#izl_rmenu").data("expanded",true);
        }
        else
        {
            $("#izl_rmenu").data("expanded",false);
        }
        if($("#izl_rmenu").data("expanded")!=lastRmenuStatus)
        {
            lastRmenuStatus=$("#izl_rmenu").data("expanded");
            if(lastRmenuStatus)
            {
                $("#izl_rmenu .btn-top").slideDown();
            }
            else
            {
                $("#izl_rmenu .btn-top").slideUp();
            }
        }
    });
});

上半部分定義id=top的div標簽內容耘柱。一個id為izl_rmenu的div,css格式定義在另一個文件lrtk.css里:

.izl-rmenu{position:fixed;left:85%;bottom:10px;padding-bottom:73px;z-index:999;}
.izl-rmenu .btn{width:72px;height:73px;margin-bottom:1px;cursor:pointer;position:relative;}
.izl-rmenu .btn-top{background:url(http://www.shareditor.com/uploads/media/default/0001/01/thumb_416_default_big.png) 0px 0px no-repeat;background-size: 70px 70px;display:none;}

下半部分當頁面滾動時div展開棍现。

在所有頁面公共代碼部分增加

<div id="top"></div>

龐大語料庫運用调煎,LSTM-RNN訓練,中文語料轉成算法識別向量形式己肮,最強大word embedding工具word2vec士袄。

word2vec輸入切詞文本文件,影視劇字幕語料庫回車換行分隔完整句子谎僻,所以我們先對其做切詞娄柳,word_segment.py文件:

# coding:utf-8

import sys
import importlib
importlib.reload(sys)

import jieba
from jieba import analyse

def segment(input, output):
    input_file = open(input, "r")
    output_file = open(output, "w")
    while True:
        line = input_file.readline()
        if line:
            line = line.strip()
            seg_list = jieba.cut(line)
            segments = ""
            for str in seg_list:
                segments = segments + " " + str
            segments = segments + "\n"
            output_file.write(segments)
        else:
            break
    input_file.close()
    output_file.close()

if __name__ == '__main__':
    if 3 != len(sys.argv):
        print("Usage: ", sys.argv[0], "input output")
        sys.exit(-1)
    segment(sys.argv[1], sys.argv[2]);

使用:

python word_segment.py subtitle/raw_subtitles/subtitle.corpus segment_result

word2vec生成詞向量。word2vec可從https://github.com/warmheartli/ChatBotCourse/tree/master/word2vec獲取艘绍,make編譯生成二進制文件赤拒。
執(zhí)行:

./word2vec -train ../segment_result -output vectors.bin -cbow 1 -size 200 -window 8 -negative 25 -hs 0 -sample 1e-4 -threads 20 -binary 1 -iter 15

生成vectors.bin詞向量,二進制格式鞍盗,word2vec自帶distance工具來驗證:

./distance vectors.bin

詞向量二進制文件格式加載需了。word2vec生成詞向量二進制格式:詞數目(空格)向量維度跳昼。
加載詞向量二進制文件python腳本:

# coding:utf-8

import sys
import struct
import math
import numpy as np

reload(sys)
sys.setdefaultencoding( "utf-8" )

max_w = 50
float_size = 4

def load_vectors(input):
    print "begin load vectors"

    input_file = open(input, "rb")

    # 獲取詞表數目及向量維度
    words_and_size = input_file.readline()
    words_and_size = words_and_size.strip()
    words = long(words_and_size.split(' ')[0])
    size = long(words_and_size.split(' ')[1])
    print "words =", words
    print "size =", size

    word_vector = {}

    for b in range(0, words):
        a = 0
        word = ''
        # 讀取一個詞
        while True:
            c = input_file.read(1)
            word = word + c
            if False == c or c == ' ':
                break
            if a < max_w and c != '\n':
                a = a + 1
        word = word.strip()

        # 讀取詞向量
        vector = np.empty([200])
        for index in range(0, size):
            m = input_file.read(float_size)
            (weight,) = struct.unpack('f', m)
            vector[index] = weight

        # 將詞及其對應的向量存到dict中
        word_vector[word.decode('utf-8')] = vector

    input_file.close()

    print "load vectors finish"
    return word_vector

if __name__ == '__main__':
    if 2 != len(sys.argv):
        print "Usage: ", sys.argv[0], "vectors.bin"
        sys.exit(-1)
    d = load_vectors(sys.argv[1])
    print d[u'真的']

運行方式如下:

python word_vectors_loader.py vectors.bin

參考資料:

《Python 自然語言處理》

http://www.shareditor.com/blogshow?blogId=113

http://www.shareditor.com/blogshow?blogId=114

http://www.shareditor.com/blogshow?blogId=115

歡迎推薦上海機器學習工作機會,我的微信:qingxingfengzi

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末肋乍,一起剝皮案震驚了整個濱河市鹅颊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌墓造,老刑警劉巖堪伍,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異觅闽,居然都是意外死亡帝雇,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門蛉拙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尸闸,“玉大人,你說我怎么就攤上這事孕锄∷绷” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵畸肆,是天一觀的道長宦芦。 經常有香客問我,道長轴脐,這世上最難降的妖魔是什么调卑? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮大咱,結果婚禮上恬涧,老公的妹妹穿的比我還像新娘。我一直安慰自己徽级,他們只是感情好气破,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著餐抢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪低匙。 梳的紋絲不亂的頭發(fā)上旷痕,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機與錄音顽冶,去河邊找鬼欺抗。 笑死,一個胖子當著我的面吹牛强重,可吹牛的內容都是我干的绞呈。 我是一名探鬼主播贸人,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼佃声!你這毒婦竟也來了艺智?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤圾亏,失蹤者是張志新(化名)和其女友劉穎十拣,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體志鹃,經...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡夭问,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年捂龄,在試婚紗的時候發(fā)現(xiàn)自己被綠了旅敷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡倘核,死狀恐怖陕见,靈堂內的尸體忽然破棺而出秘血,到底是詐尸還是另有隱情,我是刑警寧澤淳玩,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布直撤,位于F島的核電站,受9級特大地震影響蜕着,放射性物質發(fā)生泄漏谋竖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一承匣、第九天 我趴在偏房一處隱蔽的房頂上張望蓖乘。 院中可真熱鬧,春花似錦韧骗、人聲如沸嘉抒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽些侍。三九已至,卻和暖如春政模,著一層夾襖步出監(jiān)牢的瞬間岗宣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工淋样, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留耗式,地道東北人。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像刊咳,于是被迫代替她去往敵國和親彪见。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內容

  • 文本關鍵詞抽取娱挨,是對文本信息進行高度凝練的一種有效手段余指,通過3-5個詞語準確概括文本的主題,幫助讀者快速理解文本信...
    atLee閱讀 22,139評論 8 46
  • 一.簡介 Solr是一個獨立的企業(yè)級搜索應用服務器让蕾,它對外提供類似于Web-service的API接口浪规。用戶可以通...
    泰安青年閱讀 3,666評論 0 37
  • 這個系列的第六個主題,主要談一些搜索引擎相關的常見技術探孝。 1995年是搜索引擎商業(yè)公司發(fā)展的重要起點笋婿,《淺談推薦系...
    我偏笑_NSNirvana閱讀 6,619評論 3 24
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)顿颅,斷路器缸濒,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • 涼薄的夜色里庇配,他的眉眼不是那么分明。終于绍些,他艱難晦澀的說到:“沈三年捞慌,從第一次遇見你我就不打算放過你。世間愛人的方...
    年初九閱讀 673評論 2 13