NLP实验二:二元文法模型

实验目的

使用免费的中文分词语料库,如人民日报语料库PKU,使用语料库中的常见词编写一个句子,使用二元语法(即每个词只与和它相邻的前一个词有关)在语料库中对句子中的词进行词频统计,输出句子的出现概率。

实验内容

对给定的pku_training.txt语料库处理,构建一个二元语言模型,二元语言模型是指当前词语出现的概率只与前一个词有关
在这里插入图片描述
对语料首先进行处理,加上头和尾:
在这里插入图片描述
那么句子出现的概率就可以按照下面的式子计算:
在这里插入图片描述
要做的工作就是在语料库中统计各个词语出现的词频,同时要计算出给定的语句的先验概率,这个先验概率是基于2-gram的
在这里插入图片描述

实验过程

首先是读取整个语料文件并进行分句:

/*
     * 读取并返回给定路径文件中文本的字符串句子形式
     * */
    public static String getTxtString(String path) {
        StringBuilder sBuilder = new StringBuilder();
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader(path));
            char[] buffer = new char[512];//取决于你最长那一句有多长
            int len;
            while ((len = br.read(buffer)) != -1) {
                String line = new String(buffer, 0, len);//这是读取到的每行,包含很多句号逗号问好感叹号,需要进一步分句
                line = line.trim();//去除读进来的字符串的尾部空格,不然split之后会多一个只有空格的空串
                String[] strArr = line.split("[,。?!]");//处理完一行按照,。?!‘"先分成句子
                for (String s : strArr) {
                    s = s.replaceAll("\\pP", "");//去除每一句中的所有标点符号
                    s = "<BOS>  " + s + "<EOS>  ";//加上首尾
//                    System.out.println(s);
                    sBuilder.append(s);//最后加入sBuilder中
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (br != null) br.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return sBuilder.toString();
    }

这里采用的是根据,。!?进行分句,因为一般中文句子都是用这几个标点符号进行结句的。
下面是分句产生的部分句子,据统计,一共118454条句子:
在这里插入图片描述
然后是根据空格进行切词,并统计各个词语出现的次数:

/*
     * 传入一句一句的文本字符串,计算总文本的各个单词词频
     * */
    public static Map<String, Integer> getCounter(String txtString) {
        //用Map记录字符串及其出现的次数
        Map<String, Integer> treeMap = new TreeMap<>();
        /*
         * \s表示表格、换行等空白区域
         * \u3000表示中文空格
         * */
        String[] strArr = txtString.split("[\\s\u3000]+");//通过空格切分
        for (String s : strArr) {
            treeMap.put(s, treeMap.getOrDefault(s, 0) + 1);//记录每个词语出现的次数
        }
//        treeMap.forEach((key, value) -> System.out.println(key + ": " + value));
        return treeMap;
    }

分词结果部分截取如下:
在这里插入图片描述
在这之后就可以计算先验概率了:

/*
     * 传入input字符串和语料库的文件路径(TXT格式),使用2-gram得到这句在语料库中的先验概率
     * */
    public static double prior_probability(String input, String file_path) {
        input = "<BOS>  " + input + "<EOS>  ";
        String[] input_strings = input.split("[\\s\u3000?‘“:(),、!;。]+");//首先将输入的句子分成词组
        int[] count_inputs = new int[input_strings.length - 1];//由2-gram可知,加上<BOS><EOS>一共需要词数-1个先验概率
        int[] count_txts = new int[input_strings.length - 1];//词语在语料库中出现的次数
        String txtString = NLP02.getTxtString(file_path);//从文件路径中读取字符串
        Map<String, Integer> treeMap = NLP02.getCounter(txtString);
        String[] strArr = txtString.split("[\\s\u3000]+");//获取语料库的词组
        /*
        * 下面双重循环是为了匹配词语和词语前面一个词语都相同出现的次数
        * */
        for (int i = 0; i < input_strings.length - 1; i++) {
            count_inputs[i] = 0;
            for (int j = 0; j < strArr.length - 1; j++) {
                if (strArr[j].equals(input_strings[i]) && strArr[j + 1].equals(input_strings[i + 1])) {
                    count_inputs[i]++;
                }
            }
            count_txts[i] = treeMap.get(input_strings[i]);
        }
        double p = 1;
        String process = "Process:";
        for (int i = 0; i < input_strings.length - 1; i++) {
            p *= (double) count_inputs[i] / count_txts[i];//句子出现的概率
            process += (" " + count_inputs[i] + "/" + count_txts[i] + " *");//计算过程
        }
        process = process.substring(0, process.length() - 2);
        System.out.println(process);
        return p;
    }

结果展示

这里测试的句子是语料库中的原句:
“忙碌 了 一 年 的 农民 就 准备 迎接 新年 !”
在这里插入图片描述
可以看出能够正确计算出各个词的词频,同时也能计算出先验概率,因此也可以计算出需要的句子概率,由于语料库比较大,所以句子出现的概率较低。

全部代码

点击此处获取所有代码

  • 2
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

please叫我滚去学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值