1 完整性对象概念
2 抽取算法思想
3 代码实现
1 完整性词概念
完整性词是在特定学科领域用来表示概念的称谓的集合。完整性词可以是词,也可以是词组,用来正确标记生产技术、科学、艺术、社会生活等各个专门领域中的事物、现象、特性、关系和过程。
例如,如下语料:
如“ Android/x 系统 /n 平台 /n _/x 7/v 英寸 /q Android/x 系统 /n 触控 /n MID/x 蓝 /a 魔 /ng W9/x 评测 /vn _/x 中
关村 /ns 在线 /vn Android/x 系统 /n 平台 /n W9/x 采用 /v660 MHz ARM9/x 内核 /n 处理器 /n ……”
可以抽取出:“ Android/x 系统 /n ”、“系统 /n 平台 /n ”、“ Android/x 系统 /n 平台 /n ”等。 而2 次出现的“ Android/x 系统 /n ”后面的第 1 个词均为“平台”,所以,此评价对象不是右完整; 2 次出现的“系统 /n 平台 /n ”前面的词均为“ Android ”,所以,此评价对象不是左完整; 2 次出现的“ Android/x 系统 /n 平台 /n ”左右词均不相同,所以,此对象具有完整性是完整性词。
2 抽取算法思想
2.1 候选对象抽取
- 对某类新闻语料,分词、统计词性搭配出现频率最高的词性组合,选取排名靠前的作为该类新闻语料的词性规则。
下面是当时总结的油气行业的词性规则(前20组)

- 使用这个词性规则,对分词完毕后的文章进行词性匹配,留下符合该规则的词作为候选对象。
2.2 非完整性对象的过滤
- 过滤掉单字动词,如为 /v 、让/v 等。表示行为或趋向性的动词,如:可以 /v 、认为 /v 等这些噪音词。
- 抽取出左完整和右完整的词,如上面所说的“ Android/x 系统 /n 平台 /n ”这样的词。实现代码见下面。
2.3 非稳定性完整对象的过滤
稳定性对象是指:组成完整性对象各个词之间的紧密耦合程度。举例说明。
如“刻录 /n 盘 /qv ”候选对象是由“刻录”和“盘”2 个词组成的,假设在文章中:“刻录 /n 盘 /qv ”一起在文本中出现了 2 次,“刻录”这个词单独出现 2 次( 它始终和“盘”在一起出现 ),“盘”出现了 3 次 ( 它除了与“刻录”在一起出现,还在其他位置出现 ) 。则可以通过对上面参数的计算,统计出“刻录”、“盘”这两个词在是一起出现可能性比分开出现的可能性大。即“刻录 /n 盘 /qv ”的稳定性。
公式如下:

假设Object(完整性词) = Item1(基词) + Item2 + Item3 + .... + ItemN。
- s(Object):完整性词的稳定性。如果s(Object)=1,此时Object最稳定;而当组成 Object 的词,仅在一起出现一次 (或很少次) ,而分散在文本中不同位置的次数趋于无穷时,s(Object)的值趋于0,此时Object最不稳定。即0 < s(Object) < 1。
- f(Object):完整性词出现频率。“刻录 /n 盘 /qv ”出现次数。
- f(wi):每个基词出现频率。f(w1) = “刻录 /n出现次数 = 2 , f(w2) = 盘 /qv出现次数 = 3 。
- m:完整性对象由几个item组成。 “刻录 /n 盘 /qv ”,m=2。
根据s(Object)的结果,设定阈值X来确定Object是不是完整性对象。
2.4 置信度计算
对经过上面两次完整性和稳定性的过滤之后,最后计算剩下候选对象计算置信度。置信度越大,则认为是完整性对象的可能性越大。置信度通过两个方法评价。
(1)特征 1:与某类短语规则同现
利用上面抽取词性规则的方法,抽取出修饰网站性对象的词语组合。例如:“刻录 /n 盘 /qv ”,前面可能会出现:好/d 精致的/a,很/d有用的/a,类似这样的修饰词,对这些修饰词进行统计、排序选取出靠前的。使用修饰词词性规则与出现完整性词的句子进行匹配,看是否同现(co = 1(同现) OR co = 0)。
(2)特征 2: 完整性对象出现频率
假设给定 N 篇文档,共有d(1≤d≤N)篇文档包含评价对象O,第k(1≤k≤d)篇文档中共有S个词,O在第k篇文档中出现 n (1≤n≤S) 次,则词频tf(O)和df(O)的计算方式和之前说的一样。
然后,将其套用至下面的公式中,计算出f(O)。

- β是一个调节参数,根据实际设置调整。0.5较为合适。
- 同一评价对象可能在多个文档中出现,采用平均值的方式计算评价对象 Object 的最终取值。
f(Object)表示结果值,此值越大, Object 是评价对象的可能性越大。
(3)置信度计算及排序(重点)
综合上述的 2 个特征值,对两个特征值进行整合排序,计算出最终的结果。
f(Object)的值越大时,候选评价对象 Object 是正确结果的可能性大,此时,有如下情况: (1) co为 1,f(Object)很小; (2) co为 0,f(Object)很大。
为了判断这 2 种情况的置信度,将 2 个特征的值,用二维坐标系表示,特征值的坐标表示如图(方法值得学习):

将Object中co为 0 的f(Object)值标注在y轴上,并令其最大值为 n , co 为 1 的f(Object)值标注在直线L上,并令其最小值为 p 。通过实验取得 L 上的点 m ,使得 m 和 n 的置信度相同,由 m 和 n 做直线 L 1 ,过点 p 做 L 1 的平行线 L 2 交 y 轴与点 q 。
令任意 2 点 (0, y 1)( q ≤ y 1 ≤ n ) , (1, y 2)( p ≤ y 2 ≤ m ) ,过y 1 做 L 1 的平行线交 L 于点 z ,若 z > y 2 ,则 y 1 > y 2 ;若 z ≤ y 2 ,则 y 1 ≤ y 2 。
大于 m 的 Object 置信度最高,并按f(Object)值排序,q 、 n 、 p 、 m 之间的 Object 置信度次之,并按照 y 1 和 y 2的比较方法排序,小于 q 的 Object 置信度最低。这样得到最终的置信度排序,按置信度前 80 %作为结果提交。
2.5 评价对象的扩充(学习思想)
对于置信度不高的后 20 %且标注为“ n ”或“ x ”的评价对象,采用扩充规则来提高准确率和召回率。在对评价对象过滤和置信度排序后使用扩充规则,而不是在抽取候选评价对象时使用,是为了减缓整个评价对象识别的复杂度和运算量。
3 代码实现
全局变量
private static Map<String,Integer> keyMap = null;//某词出现次数
private static Map<String,Double> wordCount_txts = new HashMap<>();//某词在所有文章中出现的次数
private static Map<String,Double> wordCount_txt = new HashMap<>();//某词在某篇文章中出现的次数
private static List<String> contained = new ArrayList<>();//去除所有文本中的重复词语
private static Map<String,Double> noave_Object = new HashMap<>();//每个词未平均的object值
private static double ave_confidence_0 = 0,ave_confidence_1 = 0;//置信区间的平均置信点
候选对象抽取
/**
* 按照规则,抽取单个文件的候选集
* @param rulesPath 规则的路径
* @param sourcePath 分词之后的文件路径
* @param outputPath 抽取之后的保存路径
* @return
*/
public static void singleTextWordsSet(String rulesPath,String sourcePath,String outputPath){
try {
String result = "";
List<String> rulesList = getRules(rulesPath);
String sourceString = StringUtils.getContent(sourcePath);
for(String rule : rulesList){
result += StringUtils.getContentUseRegex(rule, sourceString, "");
}
StringUtils.string2File(result, outputPath);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 读取评价对象抽取的规则
* @param inputPath 规则的完整路径
* @return
* xx/n
*/
public static List<String> getRules(String inputPath){
List<String> result = new ArrayList<String>();
File file = new File(inputPath);
FileReader fr = null;
BufferedReader br = null;
String regex = "[\u4e00-\u9fa5a-zA-Z0-9]*";
try {
fr = new FileReader(file);
br = new BufferedReader(fr);
String temp = "";
while((temp=br.readLine()) != null){
String[] temps = temp.split(ConstantParams.SINGLE_BLANK);
String str = "";
for(String rule : temps){
str += (regex+"/"+rule+"(.?)"+ConstantParams.SINGLE_BLANK);
}
result.add(str);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
if(br != null){
br.close();
}
if(fr != null){
fr.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return result;
}
/**
* @Description: 词典处理:去除词性标注,抽取关键词之和
* @return:
* @date: 2017-10-15
*/
public static void genInteDic(String sourcePath,String outputPath){
BufferedReader br = null;
String result = "";
String line = "";
try {
br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(sourcePath)),FileUtils.getFileEncode(sourcePath)));
while((line = br.readLine())!=null){
String []bStr = line.split(ConstantParams.SINGLE_BLANK);
String s = "";
for(String w : bStr){
if(w.contains("/")){
s+= w.substring(0, w.indexOf("/"));
}
}
result+=s+ConstantParams.CHENG_LINE;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
StringUtils.string2File(result, outputPath);
}
非完整性对象的过滤
/**
* 过滤特殊词(单子动词,特殊符号... ...)
* @param intputPath
* @param outputPath
*/
public static void filterWords(String inputPath,String outputPath){
try {
String result = "";
File file = new File(inputPath);
InputStream is = new FileInputStream(file);
InputStreamReader isr = new InputStreamReader(is,FileUtils.getFileEncode(inputPath));
BufferedReader br = new BufferedReader(isr);
String temp = "";
while((temp=br.readLine()) != null){
String[] temps = temp.split(ConstantParams.SINGLE_BLANK);
if(temps.length > 1){
result += temp+ConstantParams.CHENG_LINE;
}else{
String[] wordsTemps = temps[0].split("/");
if(wordsTemps[0].length() != 1){
result += temp+ConstantParams.CHENG_LINE;
}
}
}
br.close();
StringUtils.string2File(result, outputPath);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 去除重复的候选词
* @param inputPath
* @param outputPath
*/
public static void delRepWords(String inputPath,String outputPath){
try {
List<String> list = new ArrayList<String>();
String result = "";
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(inputPath)),FileUtils.getFileEncode(inputPath)));
String temp = "";
while((temp=br.readLine()) != null){
if(!list.contains(temp)){
list.add(temp);
result += temp+ConstantParams.CHENG_LINE;
}
}
br.close();
StringUtils.string2File(result,outputPath);
} catch (Exception e) {
e.printStackTrace();
}
}
左右完整性词的计算
/**
* 非完整性过滤
* @param inputPath : 已去重完毕的词; sourcePath:splitWords中的词;
* @param outputPath
* @return 满足条件的完整性词
*/
public static List<String> filterInteWords(String inputPath,String sourcePath,String outputPath){
List<String> inteWords = new ArrayList<String>();
try {
String rawResult = "";
List<String> wordsList = StringUtils.getContentFromPath(inputPath);
for(String word : wordsList){
//读分词之后的源文件
String sourceWord = StringUtils.getContent(sourcePath);
String[] sourceWords = sourceWord.split(ConstantParams.SINGLE_BLANK);
String[] words = word.split(ConstantParams.SINGLE_BLANK);
Set<String> leftSet = new HashSet<String>();
Set<String> rightSet = new HashSet<String>();
//是0的时候,表示完整;1的时候表示不完整
int left = 0;
int right = 0;
int leftFirst = 1;
int rightLast = 1;
if(words.length == 1){
for(int i=0;i<sourceWords.length;i++){
if(words[0].equals(sourceWords[i])){
if(i == 0){
leftFirst = 0;//左完整
rightSet.add(sourceWords[i+1]);
}else if(i == sourceWords.length-1){
rightLast = 0;//右完整
leftSet.add(sourceWords[i-1]);
}else{
leftSet.add(sourceWords[i-1]);
rightSet.add(sourceWords[i+1]);
}
}
if(leftFirst == 0){
left = 0;
}else{
if(leftSet.size() == 1){
left = 1;
}else{
left = 0;
}
}
if(rightLast == 0){
right = 0;
}else{
if(rightSet.size() == 1){
right = 1;
}else{
right = 0;
}
}
}
}else if(words.length == 2){
for(int i=0;i<sourceWords.length-1;i++){
if(words[0].equals(sourceWords[i]) && words[1].equals(sourceWords[i+1])){
if(i == 0){
leftFirst = 0;//
rightSet.add(sourceWords[i+2]);
}else if(i == sourceWords.length-1){
rightLast = 0;
leftSet.add(sourceWords[i-1]);
}else{
leftSet.add(sourceWords[i-1]);
rightSet.add(sourceWords[i+2]);
}
}
if(leftFirst == 0){
left = 0;
}else{
if(leftSet.size() == 1){
left = 1;
}else{
left = 0;
}
}
if(rightLast == 0){
right = 0;
}else{
if(rightSet.size() == 1){
right = 1;
}else{
right = 0;
}
}
}
}else if(words.length == 3){
for(int i=0;i<sourceWords.length-2;i++){
if(words[0].equals(sourceWords[i]) && words[1].equals(sourceWords[i+1]) && words[2].equals(sourceWords[i+2])){
if(i == 0){
leftFirst = 0;
rightSet.add(sourceWords[i+3]);
}else if(i == sourceWords.length-1){
rightLast = 0;
leftSet.add(sourceWords[i-1]);
}else{
leftSet.add(sourceWords[i-1]);
rightSet.add(sourceWords[i+3]);
}
}
if(leftFirst == 0){
left = 0;
}else{
if(leftSet.size() == 1){
left = 1;
}else{
left = 0;
}
}
if(rightLast == 0){
right = 0;
}else{
if(rightSet.size() == 1){
right = 1;
}else{
right = 0;
}
}
}
}
rawResult += word+ConstantParams.TABLE+left+ConstantParams.TABLE+right+ConstantParams.CHENG_LINE;
if(left == 0 && right == 0){
inteWords.add(word);
// result += word+ConstantParams.TABLE+left+ConstantParams.TABLE+right+ConstantParams.CHENG_LINE;
}
}
StringUtils.string2File(rawResult, outputPath);//所有完整性词的展示
// StringUtils.string2File(result, outputPath);//仅是完整性词的展示
return inteWords;
} catch (Exception e) {
e.printStackTrace();
}
return inteWords;
}
非稳定性完整对象的计算
/**
* @Description: 非稳定评价对象的过滤,并生成去除词性的stableWords,为后面计算做准备
* @param: inputPath : fliterWords(未去重) outPath:输出路径 inteWords:符合规则完整性词 stablePath
* @return:
* @date: 2017-10-13
*/
public static void getObject(String inputPath,String outPath,List inteWords,String stablePath){
Map<String, Double> wordsObject = new HashMap<String, Double>();
List<String> wordsList = StringUtils.getContentFromPath(inputPath);
getKeyTimes(inputPath);
try {
double objectIndex = 0;
for(String word : wordsList){
if(!inteWords.contains(word)){
continue;
}
String[] words = word.split(ConstantParams.SINGLE_BLANK);
if(words.length == 1){
wordsObject.put(word, 1.0);
}
else if(words.length == 2){
double aWordTimes = 0;
double bWordTimes = 0;
double objectTimes = 0;
double wordsNumber = 2;
if(keyMap.containsKey(word)){
objectTimes = keyMap.get(word);
}
if(keyMap.containsKey(words[0]+ConstantParams.SINGLE_BLANK)){
aWordTimes = keyMap.get(words[0]+ConstantParams.SINGLE_BLANK);
}
if(keyMap.containsKey(words[1]+ConstantParams.SINGLE_BLANK)){
bWordTimes = keyMap.get(words[1]+ConstantParams.SINGLE_BLANK);
}
objectIndex = objectTimes/((aWordTimes+bWordTimes)-(wordsNumber-1)*objectTimes);
if(Double.isInfinite(objectIndex)){
objectIndex = 0;
}
wordsObject.put(word, objectIndex);
}
else if(words.length == 3){
double aWordTimes = 0;
double bWordTimes = 0;
double cWordTimes = 0;
double objectTimes = 0;
double wordsNumber = 3;
if(keyMap.containsKey(word)){
objectTimes = keyMap.get(word);
}
if(keyMap.containsKey(words[0]+ConstantParams.SINGLE_BLANK)){
aWordTimes = keyMap.get(words[0]+ConstantParams.SINGLE_BLANK);
}
if(keyMap.containsKey(words[1]+ConstantParams.SINGLE_BLANK)){
bWordTimes = keyMap.get(words[1]+ConstantParams.SINGLE_BLANK);
}
if(keyMap.containsKey(words[2]+ConstantParams.SINGLE_BLANK)){
cWordTimes = keyMap.get(words[2]+ConstantParams.SINGLE_BLANK);
}
objectIndex = objectTimes/((aWordTimes+bWordTimes+cWordTimes)-(wordsNumber-1)*objectTimes);
if(Double.isInfinite(objectIndex)){
objectIndex = 0;
}
wordsObject.put(word, objectIndex);
}
}
Map resultMap = SortUtils.sortByValue(wordsObject);
String str = "";
// String stable = "";
Set<String> set = resultMap.keySet();
Iterator<String> iter = set.iterator();
while(iter.hasNext()){
String key = iter.next();
Double value = (Double)resultMap.get(key);
// stable+=key+ConstantParams.SINGLE_BLANK+value+ConstantParams.CHENG_LINE;
//稳定性>=0的数都可以存入最终的词典中
if(value>=0){
str+=key+ConstantParams.CHENG_LINE;
}
}
//stable
clearSpeech(str, stablePath);
// StringUtils.string2File(str, stablePath);//稳定性词(删除)
StringUtils.string2FileTrue(str, outPath);//生成第一层词典
} catch (Exception e) {
e.printStackTrace();
}
}
计算特征 2:完整性对象出现频率,tf和idf的计算前面提到,不在赘述。
/**
* @Description: 计算评价对象出现的概率:f-object
* @return:
* @date: 2017-10-30
*/
public static double calObject(String word){
Double d = wordCount_txt.get(word);
Double objectD = wordCount_txts.get(word);
double result = 0.0;
double X = 0.5;
if(objectD == 1){
result = X;
return result;
}
else{
result = d/objectD+1;
}
if(Double.isNaN(result)){
result = 0.0;
}
return result;
}
根据特征1和2,计算置信区间
/**
* @Description: 计算置信区间的平均值
* @param:
* @return:
* @date: 2017-12-18
*/
private static void calConfidence(Map<String,Double> ev0_word,Map<String,Double> ev1_word,String objectPath) {
//根据分开的0和1,在对概率的分布值进行处理,求出其中的0和1的置信区间
String content = StringUtils.getContent(objectPath);//f-object总排名,利用其计算置信度
String[] content_split = content.split(ConstantParams.CHENG_LINE);
double c_Confidence_0 = 0, l_Confidence_0 = 0;//0的置信区间范围
double c_Confidence_1 = 0, l_Confidence_1 = 0;//1的置信区间范围
double ave_0 = 0,ave_1 = 0;//均值
double variance_0 = 0,variance_1 = 0;//方差
//存基础数据
List<Double> object_value_0 = new ArrayList<>();
List<Double> object_value_1 = new ArrayList<>();
double t = 2.807;//t分布,α=0.0025
double count = 0;
int N = content_split.length;//样本总个数
for(int i=0;i<content_split.length;i++){
String[] word = content_split[i].split(ConstantParams.SINGLE_BLANK);
//计算出1和0 f-object的最大值
/*
* 求0部分和1部分f-object的值的置信区间
*
* 1 均值 2 查表置信度数据 3 求S(方差/n-1) 4 求sqrt(n)
* */
if(ev0_word.containsKey(word[0])){
if(ev0_word.get(word[0])!=null){
//写出
// write_01(content_split[i]);
object_value_0.add(Double.valueOf(word[1]));
ave_0+= Double.valueOf(word[1]);
}
}else if(ev1_word.containsKey(word[0])){
if(ev1_word.get(word[0])!=null){
//写出
// write_02(content_split[i]);
object_value_1.add(Double.valueOf(word[1]));
ave_1+= Double.valueOf(word[1]);
}
}
}
ave_0/=N;
ave_1/=N;
//0的方差
for(Double value : object_value_0){
variance_0 +=Math.pow((value-ave_0), 2);
}
variance_0/=N-1;
//1的方差
for(Double value : object_value_1){
variance_1 +=Math.pow((value-ave_1), 2);
}
variance_1/=N-1;
//0的置信区间
c_Confidence_0 = ave_0 - t*(N-1) * (variance_0/Math.sqrt(N));
l_Confidence_0 = ave_0 + t*(N-1) * (variance_0/Math.sqrt(N));
//1的置信区间
c_Confidence_1 = ave_1 - t*(N-1) * (variance_1/Math.sqrt(N));
l_Confidence_1 = ave_1 + t*(N-1) * (variance_1/Math.sqrt(N));
for(double value :object_value_0){
if(value>=c_Confidence_0 && value<=l_Confidence_0){
count++;
ave_confidence_0+=value;
}
}
ave_confidence_0/=count;
count = 0;
for(double value :object_value_1){
if(value>=c_Confidence_1 && value<=l_Confidence_1){
count++;
ave_confidence_1+=value;
}
}
ave_confidence_1/=count;
// System.out.println(c_Confidence_0);
// System.out.println(l_Confidence_0);
// System.out.println("---------------");
// System.out.println(c_Confidence_1);
// System.out.println(l_Confidence_1);
// System.out.println("---------------");
// System.out.println(ave_confidence_0+" "+ave_confidence_1);
}
根据K值,求平行线另一点坐标,置信度排序
/**
* @Description: 根据K值,求平行线另一点坐标
* @param: K:斜率;X:X坐标值;ev0_word 评价值为0的词
* @return:
* @date: 2017-11-1
*/
public static void change0_1(double K,double X,Map<String,Double> ev0_word,Map<String,Double> ev1_word,String objectPath,String outputPath){
Map<String,Double> allMap = new HashMap<String,Double>();
//读取objectPath中的频率值
String content = StringUtils.getContent(objectPath);
String[] content_split = content.split(ConstantParams.CHENG_LINE);
for(int i=0;i<content_split.length;i++){
String[] word = content_split[i].split(ConstantParams.SINGLE_BLANK);
String key = word[0];
if(ev0_word.containsKey(key)){
if(ev0_word.get(key)!=null){
double Y0 = Double.valueOf(word[1]);
double Y = K*(X-0)-Y0;
allMap.put(key, Y);
}
}
else if(ev1_word.containsKey(key)){
if(ev1_word.get(key)!=null){
double Y1 = Double.valueOf(word[1]);
allMap.put(key, Y1);
}
}
}
List<Entry<String,Double>> sortMapByValue = StringUtils.sortMapByValue(allMap);
String fin_result = "";
for(Map.Entry<String, Double> entry : sortMapByValue){
fin_result+=entry.getKey() + ConstantParams.SINGLE_BLANK + entry.getValue() + ConstantParams.CHENG_LINE;
}
StringUtils.string2File(fin_result, outputPath);
}
网友评论