Stream編程之N-gram實(shí)現(xiàn)

N-gram是常用的概率語(yǔ)言模型试疙,可以通過(guò)已有語(yǔ)料推斷語(yǔ)句結(jié)構(gòu)的合理性祝旷,在自然語(yǔ)言處理中有著廣泛的應(yīng)用嘶窄,N-gram的概念就不多說(shuō)了,網(wǎng)上有大把的教程吻谋,想了解的可以自己搜现横。
Stream是java8的新特性戒祠,java8已經(jīng)發(fā)布3年有余了,不知道大家在實(shí)際中應(yīng)用的有多少姜盈,工作原因這兩年java代碼寫的比較少,就拿N-gram算法來(lái)練練手示血,個(gè)人感覺stream還是很適合做文字處理這種事情的矾芙,流式編程寫起來(lái)還是很方便的近上。
下面來(lái)看看具體實(shí)現(xiàn):

package nlp.ngram;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Ngram {

    private static Map<String, AtomicInteger> trainingData;

    static {
        String path = ".zhuxian.txt";
        trainingData = initTrainingData(path);//初始化訓(xùn)練數(shù)據(jù),加載到內(nèi)存
    }

    /**
     * 計(jì)算輸入句子的合理性概率
     * @param sentence 要評(píng)估的語(yǔ)句
     * @param n N-gram的N
     * @return 合理性概率
     */
    private static float getProbability(String sentence, int n) {
        final String sen = "@" + sentence + "#";
        return Stream
            .iterate(0, i -> ++i)
            .limit(sen.length() - n + 1)
            .map(start -> sen.substring(start, start + n))
            .map(s -> {
                float nu = (float) (null == trainingData.get(s) ? 1 : trainingData.get(s).get() + 1);
                float de = (float) (null == trainingData.get(s.substring(0, s.length() - 1)) ? 1 : trainingData.get
                    (s.substring(0, s.length() - 1)).get() + 1);
                System.out.println(s + "/" + s.substring(0, s.length() - 1) + " " + nu / de);
                return nu / de;
            })
            .reduce(1f, (f1, f2) -> f1 * f2);
    }

    /**
     * 把訓(xùn)練數(shù)據(jù)處理過(guò)后加載到內(nèi)存葱绒,統(tǒng)計(jì)每個(gè)分詞的出現(xiàn)頻次
     * @param path 訓(xùn)練集路徑
     * @return 統(tǒng)計(jì)數(shù)據(jù)map
     */
    private static Map<String, AtomicInteger> initTrainingData(String path) {
        return readFileOrDir(path)
            .map(s -> s.replaceAll("[”“\\w\\s《》.::*‘’地淀、\"<>\\[\\]^`~]", ""))//去掉文字里的無(wú)意義字符,這里只處理中文
            .flatMap(s -> Stream.of(s.split("[岖是,,帮毁。实苞;;!!烈疚??]")))//分割句子
            .filter(s -> !"".equals(s))//去掉空行
//          .peek(System.out::println)
            .map(s -> "@" + s + "#")//加上句首和句尾標(biāo)記
            .flatMap(s -> Stream
                .iterate(1, i -> ++i)//支持的N-gram的N為1黔牵、2、3爷肝、4
                .limit(s.length() > 4 ? 4 : s.length())//N-gram的N最大為4猾浦,太大了內(nèi)存容易爆,實(shí)際應(yīng)用中4基本就夠了
                .parallel()
                .flatMap(n -> Stream
                    .iterate(0, i -> ++i)
                    .limit(s.length() - n + 1)
                    .parallel()
                    .map(start -> s.substring(start, start + n))//分割句子為n個(gè)字的集合
                )
            )
            .collect(Collectors.toConcurrentMap(o -> o,
                o -> new AtomicInteger(1), (e1, e2) -> {
                e1.incrementAndGet();
                return e1;
            }));
    }

    /**
     * 文件讀取工具灯抛,以行為單位輸出
     * @param path 文件路徑
     * @return Stream lines 流
     */
    private static Stream<String> readFileOrDir(String path) {
        File file = new File(path);
        if (file.isDirectory()) {
            String[] paths = file.list((dir, name) -> !name.startsWith("."));
            assert paths != null;
            return Arrays.stream(paths)
                .flatMap(p -> readFileOrDir(path + File.separator + p));
        } else {
            try {
                return new BufferedReader(new java.io.FileReader(path)).lines();
            } catch (Exception e) {
                System.err.println("read file " + path + " error!" + e.getMessage());
                return Stream.empty();
            }
        }
    }

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        br.lines().forEach(s -> System.out.println(getProbability(s, 3)));
    }
}

這里用java流式編程可以大大的減少代碼量金赦,一個(gè)方法處理語(yǔ)料,一個(gè)方法計(jì)算概率就完事了对嚼,對(duì)于cpu密集型的任務(wù)還可以用多線程 (parallel) 來(lái)加速處理速度夹抗,不過(guò)并發(fā)的坑就得自己填了磨确。
這里的實(shí)現(xiàn)相當(dāng)基礎(chǔ)声邦,沒有分詞邓了,準(zhǔn)確性會(huì)低很多骗炉,可優(yōu)化的空間還很大句葵,還有數(shù)據(jù)的平滑 (smoothing) 處理這里就不展開討論了乍丈,這里的實(shí)現(xiàn)只是簡(jiǎn)單的把沒出現(xiàn)的詞出現(xiàn)的次數(shù)設(shè)為1轻专,實(shí)際使用中要結(jié)合實(shí)際設(shè)計(jì)數(shù)據(jù)平滑算法请垛,有時(shí)間再填填優(yōu)化的部分宗收。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末嫂侍,一起剝皮案震驚了整個(gè)濱河市挑宠,隨后出現(xiàn)的幾起案子颓影,更是在濱河造成了極大的恐慌碎浇,老刑警劉巖奴璃,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件苟穆,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡间聊,警方通過(guò)查閱死者的電腦和手機(jī)哎榴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門偷遗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)氏豌,“玉大人泵喘,你說(shuō)我怎么就攤上這事∠嗨伲” “怎么了突诬?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蔬捷。 經(jīng)常有香客問我周拐,道長(zhǎng)妥粟,這世上最難降的妖魔是什么罕容? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任露泊,我火速辦了婚禮惭笑,結(jié)果婚禮上沉噩,老公的妹妹穿的比我還像新娘川蒙。我一直安慰自己昼牛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著伶椿,像睡著了一般游昼。 火紅的嫁衣襯著肌膚如雪烘豌。 梳的紋絲不亂的頭發(fā)上廊佩,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天标锄,我揣著相機(jī)與錄音料皇,去河邊找鬼星压。 笑死逊脯,一個(gè)胖子當(dāng)著我的面吹牛军洼,可吹牛的內(nèi)容都是我干的匕争。 我是一名探鬼主播甘桑,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼锄贼!你這毒婦竟也來(lái)了宅荤?” 一聲冷哼從身側(cè)響起冯键,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蚯舱,失蹤者是張志新(化名)和其女友劉穎枉昏,沒想到半個(gè)月后兄裂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體晰奖,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年冒萄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了帅戒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钟哥。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瞎访,死狀恐怖播演,靈堂內(nèi)的尸體忽然破棺而出伴奥,到底是詐尸還是另有隱情,我是刑警寧澤拾徙,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布洲炊,位于F島的核電站,受9級(jí)特大地震影響尼啡,放射性物質(zhì)發(fā)生泄漏暂衡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一玄叠、第九天 我趴在偏房一處隱蔽的房頂上張望古徒。 院中可真熱鬧,春花似錦隧膘、人聲如沸疹吃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春略贮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓敏沉,卻偏偏與公主長(zhǎng)得像队萤,于是被迫代替她去往敵國(guó)和親赵辕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子私杜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理誊爹,服務(wù)發(fā)現(xiàn)瓢捉,斷路器搂漠,智...
    卡卡羅2017閱讀 134,651評(píng)論 18 139
  • N-Gram(有時(shí)也稱為N元模型)是自然語(yǔ)言處理中一個(gè)非常重要的概念,通常在NLP中,人們基于一定的語(yǔ)料庫(kù)蜡娶,可以利...
    x00c閱讀 38,827評(píng)論 4 16
  • 年紀(jì)越大,被命運(yùn)抽絲剝繭后留在我們身邊的人就越少蚁滋,我們一邊張望一邊伸出手想要抓住些什么睦霎。時(shí)光總會(huì)褪去那些青澀,為我...
    W子軒閱讀 182評(píng)論 0 0
  • 《長(zhǎng)日留痕》是剛剛獲諾獎(jiǎng)的日裔英國(guó)作家石黑一雄的代表作沟涨,這一小說(shuō)由六天的旅途經(jīng)歷所構(gòu)成。以第一人稱視角,敘述了作為...
    瑾瑜有玉閱讀 2,762評(píng)論 0 6