n元语法(英语:N-gram)指文本中连续出现的n个语词。n元语法模型是基于(n - 1)阶马尔可夫链的一种概率语言模型,通过n个语词出现的概率来推断语句的结构。这一模型被广泛应用于概率论、通信理论、计算语言学(如基于统计的自然语言处理NLP)、计算生物学(如序列分析)、数据压缩等领域。
N-gram文本广泛用于文本挖掘和自然语言处理任务。它们基本上是给定窗口内的一组同时出现的单词,在计算n元语法时,通常会将一个单词向前移动(尽管在更高级的场景中可以使X个单词向前移动)。
例如,对于句子"The cow jumps over the moon" ,N = 2(称为二元组),则 ngram 为:
the cow
cow jumps
jumps over
over the
the moon
因此,在这种情况下,有 5 个 n-gram。
再来看看 N = 3,ngram 将为:
the cow jumps
cow jumps over
jumps over the
over the moon
因此,在这种情况下,有 4 个 n-gram。
所以,在一个句子中 N-grams 的数量有:
Ngrams(K) = X - (N - 1)
其中,X 为给定句子K中的单词数,N 为 N-gram 的N,指的是连续出现的 N 个单词。
N-gram用于各种不同的任务。例如,在开发语言模型时,N-grams不仅用于开发unigram模型,而且还用于开发bigram和trigram模型。谷歌和微软已经开发了网络规模的 n-gram模型,可用于多种任务,例如拼写校正、分词和文本摘要。N-gram的另一个用途是为受监督的机器学习模型(例如SVM,MaxEnt模型,朴素贝叶斯等)开发功能。其想法是在特征空间中使用标记(例如双字母组),而不是仅使用字母组合。
下面简单介绍一下如何用 Java 生成 n-gram。
这个是生成 n-gram 的主要方法,方法首先是对传进来的句子 sentence 进行单词拆分,这个正则表达式“\\s+”是能匹配任何空白字符,包括空格、制表符、换页符等等, 等价于 [ \f\n\r\t\v]。拆分完后对单词进行拼接。算法时间复杂度为 O(X - (N - 1)),X 为给定句子K中的单词数,N 为 N-gram 的 N。****
1 /**
2 * 生成n元语法
3 *
4 * 一个句子中有多少个N-gram?
5 * 如果 X = 给定句子K中的单词数,则句子K的 N-gram数为:
6 * N(grams
7 *
8 * @param n 连续 n个单词
9 * @param sentence 句子级别的文本
10 * @return 存着ngram的列表
11 */
12 public static List
13 List
14 String[] words = sentence.split("\\s+");
15 for (int i = 0; i < words.length - n + 1; i++)
16 ngrams.add(concat(words, i, i + n));
17 return ngrams;
18 }
进行单词拼接,这里使用 StringBuilder(线程不安全,效率相对StringBuffer高点)对拆分好的单词进行拼接并返回拼接好的字符串。
1 /**
2 * 拼接单词
3 *
4 * @param words 单词
5 * @param start 开始位置
6 * @param end 结束位置
7 * @return 拼接好的字符串
8 */
9 public static String concat(String[] words, int start, int end) {
10 StringBuilder sb = new StringBuilder();
11 for (int i = start; i < end; i++)
12 sb.append(i > start ? " " : "").append(words[i]);
13 return sb.toString();
14 }
对 n-gram 的出现次数进行统计,使用 HashMap
在 Java 8 中,Map.Entry类具有静态方法 comparingByValue() 来帮助按 value 排序,此方法返回以自然顺序 Comparator 比较 Map.Entry值的。还有,你可以传递自定义Comparator 以用于排序。
下面是根据 value 进行排序的方法:
1 /**
2 * 按 value对 HashMap进行逆序排序
3 *
4 * 使用 Java 8 Stream API按照降序对Value进行Map排序
5 * 逻辑的中心是按自然顺序 Map.Entry.comparingByValue()比较 Map.Entry值的方法。
6 *
7 * @param unSortedMap 未排序的HashMap
8 * @return 按照value降序排序的HashMap
9 */
10 public static HashMap
11 // System.out.println("Unsorted Map : " + unSortedMap);
12
13 // LinkedHashMap保留插入元素的顺序
14 LinkedHashMap
15
16 // 使用 Comparator.reverseOrder() 进行反向排序
17 unSortedMap.entrySet()
18 .stream()
19 .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
20 .forEachOrdered(x -> reverseSortedMap.put(x.getKey(), x.getValue()));
21
22 // System.out.println("Reverse Sorted Map : " + reverseSortedMap);
23
24 return reverseSortedMap;
25 }
主函数测试代码:
1 public static void main(String[] args) {
2 HashMap
3 String text = "I can go to the supermarket to buy spicy bars or go to the store to buy spicy bars.";
4
5 // 生成n为1~3的N元语法
6 // for (int n = 1; n <= 3; n++) {
7 // for (String ngram : ngrams(n, text)) {
8 // System.out.println(ngram);
9 // }
10 // System.out.println();
11 // }
12
13 for (String ngram : ngrams(3, text)) {
14 // counting ngram by using HashMap
15 if (!count.containsKey(ngram)) {
16 count.put(ngram, 1);
17 } else if (count.containsKey(ngram)) {
18 count.replace(ngram, count.get(ngram) + 1);
19 }
20 System.out.println(ngram);
21 }
22
23 // 按出现次由多到少的顺序打印ngram
24 System.out.println("\nCounting Result: ");
25 for (Map.Entry
26 System.out.println(entry.getKey() + ": " + entry.getValue());
27 }
28
29 }
手机扫一扫
移动阅读更方便
你可能感兴趣的文章