由于毕业设计需要用到中文的自然语言处理,所以我现在一有空就疯狂地看关于这方面的相关资料。中文分词是我的课题需要解决的第一个问题。
中文分词的大致算法我已经通过使用网络上的开源项目IKAnalyzer做出了一个雏形,但是有一个比较棘手的问题就是,对于这种基于中文词典的分词算法,词典的好坏特别重要,但是对于我需要分析的对象——电子商务评论来说,新词层出不穷。如果不能把这些新词很好地识别出来,那么可想而知分词的效果将大打折扣。
所幸的是让我找到了一个基于社会网络的无知识库抽词算法,能够不断完善词典。遵从CC版权协议,这个算法的思想来源于matrix67.com。
算法的主要思想就是假设如果一个词语是独立存在的,那么这个词语的任何子集组合出现在文本中的概率都应该小于这个词语本身的存在概率,算法的步骤如下:
1. 读入一个文本,进行粗加工(剔除所有非汉字)
2. 统计文本中单字节汉字的出现概率(出现次数/文本字符数),使用Hash作为字典进行存储
3. 统计文本中双字节汉字的出现概率(出现次数/文本字符数),使用Hash作为词典进行存储
4. 统计双字节汉字的凝聚度,并排序及格式化输出
需要说明的是,在这个算法中我只是非常简化地考虑了双字节汉字,也就是词语的存在以两个字为单位。注意这个是非常不合理的,因为很多名词性的词语含有的汉字个数远远大于两个,比如中华人民共和国,这就是一个由7个汉字组成的词语。后面跟进的算法也只是完成了两个词语的情况,其实可以更进一步地完成多个字的组合方式。
1 import java.io.BufferedReader; 2 import java.io.File; 3 import java.io.FileReader; 4 import java.io.IOException; 5 import java.text.DecimalFormat; 6 import java.util.Comparator; 7 import java.util.HashMap; 8 import java.util.Map.Entry; 9 import java.util.TreeMap; 10 11 /** 12 * 13 汉语词汇提取phase task 14 * 1 读入一个文本,进行粗加工(剔除所有非汉字) 15 * 2 统计单字节汉字的出现概率(出现次数/文本字符数)使用Hash作为字典 16 * 3 统计双字节汉字的出现概率(出现次数/文本字符数)使用Hash作为词典 17 * 4 统计双字节汉字的凝聚度,并排序及格式化输出 5 根据结果,讨论算法可改进点 18 * 参考文献<数学之美>> =end 19 * 20 */ 21 public class ChineseFilter { 22 static DecimalFormat dcmFmt = new DecimalFormat("0.00"); 23 24 public static void main(String[] args) { 25 String text = readAndFilterFile("text.txt"); 26 HashMap<Character, Double> singleHashMap = statSingleChinese(text); 27 HashMap<String, Double> doubleHashMap = statDoubleChinese(text); 28 printRelationBySort(singleHashMap, doubleHashMap); 29 } 30 31 public static String readAndFilterFile(String fileName) { 32 File file = new File(fileName); 33 String lines = ""; 34 BufferedReader input = null; 35 try { 36 input = new BufferedReader(new FileReader(file)); 37 String line = null; 38 while ((line = input.readLine()) != null) { 39 lines += line; 40 } 41 input.close(); 42 } catch (IOException e) { 43 e.printStackTrace(); 44 } 45 // 过滤非汉字(正则表达式是[^\u4e00-\u9fa5]) 46 lines = lines.replaceAll("[^\u4e00-\u9fa5]", ""); 47 //System.out.println(lines); 48 return lines; 49 } 50 51 public static HashMap<Character, Double> statSingleChinese(String text) { 52 HashMap<Character, Double> singleHashMap = new HashMap<Character, Double>(); 53 int sum = text.length(); 54 for (int i = 0; i < sum; i++) { 55 char key = text.charAt(i); 56 if (singleHashMap.containsKey(text.charAt(i))) { 57 singleHashMap.put(key, singleHashMap.get(key) + 1); 58 } else { 59 singleHashMap.put(key, 1.0); 60 } 61 } 62 for (Entry<Character, Double> entry : singleHashMap.entrySet()) { 63 // System.out.println(entry.getKey() + "-" + entry.getValue()); 64 entry.setValue(entry.getValue() / sum); 65 // System.out.println(entry.getKey() + "-" + entry.getValue()); 66 } 67 return singleHashMap; 68 } 69 70 public static HashMap<String, Double> statDoubleChinese(String text) { 71 HashMap<String, Double> doubleHashMap = new HashMap<String, Double>(); 72 int sum = text.length(); 73 for (int i = 0; i < sum - 1; i++) { 74 String key = text.substring(i, i + 2); 75 if (doubleHashMap.containsKey(key)) { 76 doubleHashMap.put(key, doubleHashMap.get(key) + 1); 77 } else { 78 doubleHashMap.put(key, 1.0); 79 } 80 } 81 for (Entry<String, Double> entry : doubleHashMap.entrySet()) { 82 // System.out.println(entry.getKey() + "-" + entry.getValue()); 83 entry.setValue(entry.getValue() / sum); 84 // System.out.println(entry.getKey() + "-" + entry.getValue()); 85 } 86 return doubleHashMap; 87 } 88 89 public static void printRelationBySort( 90 HashMap<Character, Double> singleHashMap, 91 HashMap<String, Double> doubleHashMap) { 92 HashMap<String, Double> allHashMap = new HashMap<String, Double>(); 93 for (Entry<String, Double> entry : doubleHashMap.entrySet()) { 94 double pDouble = entry.getValue(); 95 double p1 = singleHashMap.get(entry.getKey().charAt(0)); 96 double p2 = singleHashMap.get(entry.getKey().charAt(1)); 97 double pAll = pDouble / (p1 * p2); 98 allHashMap.put(entry.getKey(), pAll); 99 // System.out.println(entry.getKey() + "-" + dcmFmt.format(pAll)); 100 } 101 102 ByValueComparator bvc = new ByValueComparator(allHashMap); 103 TreeMap<String, Double> sorted_map = new TreeMap<String, Double>(bvc); 104 sorted_map.putAll(allHashMap); 105 for (String name : sorted_map.keySet()) { 106 if (allHashMap.get(name) > 1000) 107 System.out.println(name + "--" + dcmFmt.format(allHashMap.get(name))); 108 } 109 } 110 111 // 实现HashMap按Value顺序排序 112 static class ByValueComparator implements Comparator<String> { 113 HashMap<String, Double> base_map; 114 115 public ByValueComparator(HashMap<String, Double> base_map) { 116 this.base_map = base_map; 117 } 118 119 public int compare(String arg0, String arg1) { 120 if (!base_map.containsKey(arg0) || !base_map.containsKey(arg1)) { 121 return 0; 122 } 123 124 if (base_map.get(arg0) > base_map.get(arg1)) { 125 return 1; 126 } else if (base_map.get(arg0) == base_map.get(arg1)) { 127 return 0; 128 } else { 129 return -1; 130 } 131 } 132 } 133 }