Browse Source

关系抽取页面展示

louhr 6 years atrás
parent
commit
13dfd2f808

+ 1 - 1
nlp-web/src/main/java/org/diagbot/nlp/controller/FeatureController.java

@@ -25,7 +25,7 @@ import javax.servlet.http.HttpServletRequest;
 import java.util.*;
 
 @Controller
-@RequestMapping("/doc/feature")
+@RequestMapping("/feature")
 public class FeatureController extends BaseController<Feature, FeatureWrapper, Long> {
     private String[] negative_words = Constants.negative_words;
 

+ 18 - 96
nlp-web/src/main/java/org/diagbot/nlp/controller/RelationExtractionController.java

@@ -1,12 +1,14 @@
 package org.diagbot.nlp.controller;
 
-import org.algorithm.core.cnn.AlgorithmCNNExecutor;
-import org.algorithm.core.cnn.entity.Triad;
-import org.apache.commons.lang3.StringUtils;
-import org.diagbot.nlp.feature.FeatureAnalyze;
 import org.diagbot.nlp.feature.FeatureType;
+import org.diagbot.nlp.relation.RelationAnalyze;
+import org.diagbot.nlp.relation.extract.output.OutputInfo;
+import org.diagbot.pub.api.Response;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
 
-import java.util.*;
+import java.util.List;
 
 /**
  * @ClassName org.diagbot.nlp.controller.RelationExtractionController
@@ -15,98 +17,18 @@ import java.util.*;
  * @Date 2019/1/17/017 11:21
  * @Version 1.0
  **/
+@Controller
+@RequestMapping("/relation")
 public class RelationExtractionController {
 
-    public void extraction(String content) throws Exception {
-        List<String> features = extractEntity(content);
-        AlgorithmCNNExecutor executor = new AlgorithmCNNExecutor();
-        List<Triad> triads = executor.execute(content);
-
-        for (String feature : features) {
-            lookRelationFeatures(feature, triads);
-        }
-    }
-
-    private void lookRelationFeatures(String feature, List<Triad> triads) {
-        for (Triad triad : triads) {
-            if (triad.getL_1().getText().equals(feature)) {
-                lookRelationFeatures(triad.getL_2().getText(), triads);
-            } else if (triad.getL_2().getText().equals(feature)) {
-                lookRelationFeatures(triad.getL_1().getText(), triads);
-            } else {
-
-            }
-        }
-    }
-
-    public static void main(String[] args) {
-        String content = "反复发热1月余,体温最高39度,伴畏寒,咽痛,恶心呕吐,头痛,发热时有关节疼痛,无肿胀,无皮疹,无明显咳嗽咳痰," +
-                "无胸闷气急,无腹痛,无尿频尿急尿痛。外院2015.10.12两下肺少许感染性病变,血象、CRP轻度升高\n" +
-                "直肠术后,现造瘘\n" +
-                "目前发热原因不能以肺部感染解释,建议感染科就诊";
-        try {
-            RelationExtractionController re = new RelationExtractionController();
-            Map<String, String> relations = re.cnnExtractRelation(content);
-
-            List<String> symptoms = re.extractEntity(content);
-
-            Map<String, String> result = new HashMap<>();
-            for (String s : symptoms) {
-                List<String> propsList = new ArrayList<>(3);
-                for (Map.Entry<String, String> entry : relations.entrySet()) {
-                    if (entry.getKey().equals(s)) {
-                        propsList.add(entry.getValue());
-                    }
-                    if (entry.getValue().equals(s)) {
-                        propsList.add(entry.getKey());
-                    }
-                }
-                result.put(s, StringUtils.join(propsList, ","));
-            }
-            for (Map.Entry<String, String> e : result.entrySet()) {
-                System.out.println(e.getKey() + " : " + e.getValue());
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-
-    public List<String> extractEntity(String content) throws Exception {
-        FeatureAnalyze sa = new FeatureAnalyze();
-        //症状信息
-        List<Map<String, Object>> featuresList =  sa.start(content, FeatureType.SYMPTOM);
-        //体征信息
-        featuresList.addAll(sa.start(content, FeatureType.VITAL));
-
-        List<String> symptoms = new ArrayList<>(featuresList.size());
-        for (Map<String, Object> m : featuresList) {
-            if (!symptoms.contains(String.valueOf(m.get("feature_name")))) {
-                symptoms.add(String.valueOf(m.get("feature_name")));
-            }
-        }
-        return symptoms;
-    }
-
-    public Map<String, String> cnnExtractRelation(String content) {
-        Map<String, String> map = new HashMap<>();
-        map.put("反复", "发热");
-        map.put("发热", "1月余");
-        map.put("发热", "39度");
-        map.put("体温", "39度");
-        map.put("发热", "关节疼痛");
-        map.put("肿胀", "无");
-        map.put("皮疹", "无");
-        map.put("咳嗽", "无");
-        map.put("咳痰", "无");
-        map.put("胸闷", "无");
-        map.put("气急", "无");
-        map.put("腹痛", "无");
-        map.put("尿频", "无");
-        map.put("尿急", "无");
-        map.put("尿痛", "无");
-        map.put("明显", "咳嗽");
-        map.put("轻度", "升高");
-        map.put("目前", "发热");
-        return map;
+    @RequestMapping({"/extraction"})
+    @ResponseBody
+    public Response extraction(String content) throws Exception {
+        Response response = new Response();
+        response.start();
+//        RelationAnalyze relationAnalyze = new RelationAnalyze();
+//        List<OutputInfo> outputInfos = relationAnalyze.analyze(content, FeatureType.parse("1"));
+//        response.setData(outputInfos);
+        return response;
     }
 }

+ 1 - 1
nlp/src/main/java/org/diagbot/nlp/participle/ParticipleToken.java

@@ -254,7 +254,7 @@ public class ParticipleToken {
             char[] chars = new char[lexeme.getLength()];
             System.arraycopy(buffer, lexeme.getOffset(), chars, 0, lexeme.getLength());
             lexeme.setText(String.valueOf(chars));
-            lexeme.setProperty(property);
+            lexeme.setProperty(property==null?this.DEFAULT_PROPERTY:property);
             lexemePath.add(lexeme);
             //判断是否需要加载文本内容
             this.fillBuffer();

+ 111 - 5
nlp/src/main/java/org/diagbot/nlp/relation/RelationAnalyze.java

@@ -1,9 +1,23 @@
 package org.diagbot.nlp.relation;
 
+import com.alibaba.fastjson.JSON;
+import org.algorithm.core.cnn.AlgorithmCNNExecutor;
+import org.algorithm.core.cnn.entity.Lemma;
+import org.algorithm.core.cnn.entity.Triad;
+import org.algorithm.core.cnn.model.RelationExtractionModel;
+import org.algorithm.core.cnn.model.impl.RelationExtractionModelImpl;
 import org.diagbot.nlp.feature.FeatureType;
 import org.diagbot.nlp.participle.ParticipleUtil;
 import org.diagbot.nlp.participle.word.Lexeme;
 import org.diagbot.nlp.participle.word.LexemePath;
+import org.diagbot.nlp.relation.extract.PresentExtract;
+import org.diagbot.nlp.relation.extract.output.OutputInfo;
+import org.diagbot.nlp.util.Constants;
+import org.diagbot.nlp.util.NegativeEnum;
+import org.diagbot.nlp.util.NlpUtil;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * @ClassName org.diagbot.nlp.relation.RelationAnalyze
@@ -13,11 +27,103 @@ import org.diagbot.nlp.participle.word.LexemePath;
  * @Version 1.0
  **/
 public class RelationAnalyze {
-    public void analyze(String content, FeatureType featureType) throws Exception {
-        LexemePath<Lexeme> lexemes = ParticipleUtil.participle(content, true);
-//        AlgorithmCNNExecutor executor = new AlgorithmCNNExecutor();
+    public List<OutputInfo> analyze(String content, FeatureType featureType) throws Exception {
+        String[] part_contents = content.split("\\。|\\;|\\;");
+
+        List<OutputInfo> outputInfos = new ArrayList<>();
+        for (String part_content : part_contents) {
+            LexemePath<Lexeme> lexemes = ParticipleUtil.participle(part_content, true);
+            //分词结果转词元结构,因为部分特征信息未在三元组中,所以需要把分词结果也一并传入
+            List<Lemma> lemmaParticiple = lexemeToTriadLemma(lexemes);
+            //调用CNN模型
+            AlgorithmCNNExecutor executor = new RelationExtractionModelImpl();
+            List<Triad> triads = executor.execute(content, lemmaParticiple);
+            //模型返回的三元组转树形结构
+            List<Lemma> lemmaTree = traidToTree(triads, featureType);
+            OutputInfo outputInfo = null;
+            switch (featureType) {
+                case SYMPTOM:
+                    PresentExtract presentExtract = new PresentExtract();
+                    outputInfo = presentExtract.extract(lemmaTree, lemmaParticiple);
+            }
+            if (outputInfo != null) {
+                outputInfos.add(outputInfo);
+            }
+        }
+        return outputInfos;
+    }
+
+    /**
+     * 分词结果转词元结构
+     * @param lexemes
+     * @return
+     */
+    private List<Lemma> lexemeToTriadLemma(LexemePath<Lexeme> lexemes) {
+        List<Lemma> lemmas = new ArrayList<>();
+        for (Lexeme lexeme : lexemes) {
+            Lemma lemma = new Lemma();
+            lemma.setLen(lexeme.getLength());
+            lemma.setPosition(lexeme.getOffset() + "," + (lexeme.getOffset() + lexeme.getLength()));
+            lemma.setText(lexeme.getText());
+            lemma.setProperty(lexeme.getProperty());
+            lemmas.add(lemma);
+        }
+        return lemmas;
+    }
+
+    /**
+     * 树形结构最多3层
+     * @param triads
+     * @param featureType
+     * @return
+     */
+    private List<Lemma> traidToTree(List<Triad> triads, FeatureType featureType) {
+        List<Lemma> lemmaTree = new ArrayList<>();
+        switch (featureType) {
+            case SYMPTOM:
+                select(lemmaTree, triads, Constants.symptom_type);
+                select(lemmaTree, triads, Constants.vital_type);
+        }
+        return lemmaTree;
+    }
+
+    private void select(List<Lemma> lemmaTree, List<Triad> triads, NegativeEnum[] type) {
+        List<Triad> has_add_triads = new ArrayList<>();
+        for (Triad triad : triads) {
+            if (NlpUtil.isFeature(triad.getL_1().getProperty(), type)) {
+                Lemma lemma = triad.getL_1();
+                lemma.add(this.findRelationTriad(triads, has_add_triads, triad.getL_2()));
+                lemmaTree.add(lemma);
+                //已添加到树中的三元组
+                has_add_triads.add(triad);
+            }
+            if (NlpUtil.isFeature(triad.getL_2().getProperty(), type)) {
+                Lemma lemma = triad.getL_2();
+                lemma.add(this.findRelationTriad(triads, has_add_triads, triad.getL_1()));
+                lemmaTree.add(lemma);
+
+                //已添加到树中的三元组
+                has_add_triads.add(triad);
+            }
+        }
+        triads.removeAll(has_add_triads);
+    }
 
-//        String json_content = JSON.toJSONString(lexemes);
-//        List<Triad> triads = executor.execute(content, json_content);
+    private Lemma findRelationTriad(List<Triad> triads, List<Triad> has_add_triads, Lemma lemma) {
+        for (Triad triad : triads) {
+            if (triad.getL_1().getText().equals(lemma.getText())
+                    && triad.getL_1().getPosition().equals(lemma.getPosition())) {
+                lemma.add(triad.getL_2());
+                //已添加到树中的三元组
+                has_add_triads.add(triad);
+            }
+            if (triad.getL_2().getText().equals(lemma.getText())
+                    && triad.getL_2().getPosition().equals(lemma.getPosition())) {
+                lemma.add(triad.getL_1());
+                //已添加到树中的三元组
+                has_add_triads.add(triad);
+            }
+        }
+        return lemma;
     }
 }

+ 30 - 81
nlp/src/main/java/org/diagbot/nlp/relation/extract/PresentExtract.java

@@ -1,12 +1,10 @@
 package org.diagbot.nlp.relation.extract;
 
 import org.algorithm.core.cnn.entity.Lemma;
-import org.algorithm.core.cnn.entity.Triad;
-import org.diagbot.nlp.participle.word.Lexeme;
-import org.diagbot.nlp.participle.word.LexemePath;
 import org.diagbot.nlp.relation.extract.cell.*;
 import org.diagbot.nlp.relation.extract.module.Symptom;
-import org.diagbot.nlp.util.NegativeEnum;
+import org.diagbot.nlp.relation.extract.output.OutputInfo;
+import org.diagbot.nlp.util.Constants;
 import org.diagbot.nlp.util.NlpUtil;
 
 import java.util.ArrayList;
@@ -20,67 +18,63 @@ import java.util.List;
  * @Version 1.0
  **/
 public class PresentExtract extends BaseExtract {
+    private List<Lemma> containsLemmaTree = new ArrayList<>();
 
-    private NegativeEnum[] symptom_type = new NegativeEnum[]{NegativeEnum.SYMPTOM};
-    private NegativeEnum[] unit_time_type = new NegativeEnum[]{NegativeEnum.EVENT_TIME, NegativeEnum.UNIT};
-    private NegativeEnum[] vital_type = new NegativeEnum[]{NegativeEnum.VITAL_INDEX};
-    private NegativeEnum[] body_part_type = new NegativeEnum[]{NegativeEnum.BODY_PART};
-    private NegativeEnum[] position_type = new NegativeEnum[]{NegativeEnum.POSITION};
-    private NegativeEnum[] property_type = new NegativeEnum[]{NegativeEnum.PROPERTY};
-    private NegativeEnum[] degree_type = new NegativeEnum[]{NegativeEnum.DEEP};
-    private NegativeEnum[] cause_type = new NegativeEnum[]{NegativeEnum.CAUSE};
-
-    public void extract(List<Triad> triads, LexemePath<Lexeme> lexemes) {
-        List<Lemma> param_lemmas = this.lexemeToTriadLemma(lexemes);
-        //三元组关系对转树形结构
-        List<Lemma> cnn_lemmas = this.traidToTree(triads);
+    public OutputInfo extract(List<Lemma> lemmaTree, List<Lemma> lemmaParticiple) {
+        OutputInfo outputInfo = new OutputInfo();
         String property = "";
-        for (int index = 0; index < param_lemmas.size(); index++) {
-            Lemma param_lemma = param_lemmas.get(index);
-            property = param_lemma.getProperty();
-            if (NlpUtil.isFeature(property, symptom_type)) {          //特征词 症状
+        for (int index = 0; index < lemmaParticiple.size(); index++) {
+            Lemma participle_lemma = lemmaParticiple.get(index);
+            property = participle_lemma.getProperty();
+            if (NlpUtil.isFeature(property, Constants.symptom_type)) {          //特征词 症状
                 Symptom symptom = new Symptom();
-                lookSymptomRelations(param_lemma, cnn_lemmas, symptom);
+                symptom.setSymptomName(participle_lemma.getText());
+
+                lookSymptomRelations(participle_lemma, lemmaTree, symptom);
+                outputInfo.getSymptoms().add(symptom);
             }
         }
+        lemmaTree.removeAll(containsLemmaTree);
+        return outputInfo;
     }
 
-    private void lookSymptomRelations(Lemma param_lemma, List<Lemma> cnn_lemmas, Symptom symptom) {
-        for (Lemma cnn_l : cnn_lemmas) {
+    private void lookSymptomRelations(Lemma param_lemma, List<Lemma> lemmaTree, Symptom symptom) {
+        for (Lemma cnn_l : lemmaTree) {
             if (cnn_l.getText().equals(param_lemma.getText())
                     && cnn_l.getPosition().equals(param_lemma.getPosition())) {
                 for (Lemma relation_l : cnn_l.getRelationLemmas()) {
                     addFeatureToSymptom(relation_l, symptom);
                 }
+                containsLemmaTree.add(cnn_l);
             }
         }
     }
 
     private void addFeatureToSymptom(Lemma lemma, Symptom symptom) {
         //时间信息
-        if (NlpUtil.isFeature(lemma.getProperty(), unit_time_type)) {
+        if (NlpUtil.isFeature(lemma.getProperty(), Constants.unit_time_type)) {
             PD pd = new PD();
             pd.setValue(lemma.getText());
             symptom.setPd(pd);
         }
         //部位信息
-        if (NlpUtil.isFeature(lemma.getProperty(), body_part_type)) {
+        if (NlpUtil.isFeature(lemma.getProperty(), Constants.body_part_type)) {
             BodyPart bodyPart = new BodyPart();
             bodyPart.setPartBodyName(lemma.getText());
             if (lemma.getRelationLemmas().size() > 0) {
                 Lemma l = lemma.getRelationLemmas().get(0);
-                if (NlpUtil.isFeature(l.getProperty(), position_type)) {
+                if (NlpUtil.isFeature(l.getProperty(), Constants.position_type)) {
                     bodyPart.setDirection(l.getText());
                 }
             }
             symptom.setBodyPart(bodyPart);
         }
         //程度
-        if (NlpUtil.isFeature(lemma.getProperty(), degree_type)) {
+        if (NlpUtil.isFeature(lemma.getProperty(), Constants.degree_type)) {
             Degree degree = new Degree();
             degree.setDegreeName(lemma.getText());
             for (Lemma l : lemma.getRelationLemmas()) {
-                if (NlpUtil.isFeature(l.getProperty(), unit_time_type)) {
+                if (NlpUtil.isFeature(l.getProperty(), Constants.unit_time_type)) {
                     PD pd = new PD();
                     pd.setValue(l.getText());
                     degree.setPd(pd);
@@ -89,68 +83,23 @@ public class PresentExtract extends BaseExtract {
             symptom.setDegree(degree);
         }
         //性质
-        if (NlpUtil.isFeature(lemma.getProperty(), property_type)) {
+        if (NlpUtil.isFeature(lemma.getProperty(), Constants.property_type)) {
             Property property = new Property();
             property.setPropertyName(lemma.getText());
             symptom.setProperty(property);
         }
         //诱因
-        if (NlpUtil.isFeature(lemma.getProperty(), cause_type)) {
+        if (NlpUtil.isFeature(lemma.getProperty(), Constants.cause_type)) {
             Cause cause = new Cause();
             cause.setCauseName(lemma.getText());
             symptom.setCause(cause);
         }
-    }
-
-    /**
-     * 分词结果转词元结构
-     * @param lexemes
-     * @return
-     */
-    private List<Lemma> lexemeToTriadLemma(LexemePath<Lexeme> lexemes) {
-        List<Lemma> lemmas = new ArrayList<>();
-        for (Lexeme lexeme : lexemes) {
-            Lemma lemma = new Lemma();
-            lemma.setLen(lexeme.getLength());
-            lemma.setPosition(lexeme.getOffset() + "," + (lexeme.getOffset() + lexeme.getLength()));
-            lemma.setText(lexeme.getText());
-            lemma.setProperty(lexeme.getProperty());
-
-            lemmas.add(lemma);
-        }
-        return lemmas;
-    }
-
-    private List<Lemma> traidToTree(List<Triad> triads) {
-        List<Lemma> lemmas = new ArrayList<>();
-        for (Triad triad : triads) {
-            if (NlpUtil.isFeature(triad.getL_1().getProperty(), symptom_type)
-                    || NlpUtil.isFeature(triad.getL_1().getProperty(), vital_type)) {
-                Lemma lemma = triad.getL_1();
-                lemma.add(this.findRelationTriad(triads, triad.getL_2()));
-                lemmas.add(lemma);
-            }
-            if (NlpUtil.isFeature(triad.getL_2().getProperty(), symptom_type)
-                    || NlpUtil.isFeature(triad.getL_1().getProperty(), vital_type)) {
-                Lemma lemma = triad.getL_2();
-                lemma.add(this.findRelationTriad(triads, triad.getL_1()));
-                lemmas.add(lemma);
-            }
-        }
-        return lemmas;
-    }
 
-    private Lemma findRelationTriad(List<Triad> triads, Lemma lemma) {
-        for (Triad triad : triads) {
-            if (triad.getL_1().getText().equals(lemma.getText())
-                    && triad.getL_1().getPosition().equals(lemma.getPosition())) {
-                lemma.add(triad.getL_2());
-            }
-            if (triad.getL_2().getText().equals(lemma.getText())
-                    && triad.getL_2().getPosition().equals(lemma.getPosition())) {
-                lemma.add(triad.getL_1());
-            }
+        //阴性
+        if (NlpUtil.isFeature(lemma.getProperty(), Constants.negative_type)) {
+            Negative negative = new Negative();
+            negative.setNegaName(lemma.getProperty());
+            symptom.setNegative(negative);
         }
-        return lemma;
     }
 }

+ 20 - 0
nlp/src/main/java/org/diagbot/nlp/relation/extract/cell/Negative.java

@@ -0,0 +1,20 @@
+package org.diagbot.nlp.relation.extract.cell;
+
+/**
+ * @ClassName org.diagbot.nlp.relation.extract.cell.Negative
+ * @Description TODO
+ * @Author fyeman
+ * @Date 2019/1/22/022 11:19
+ * @Version 1.0
+ **/
+public class Negative {
+    private String negaName;
+
+    public String getNegaName() {
+        return negaName;
+    }
+
+    public void setNegaName(String negaName) {
+        this.negaName = negaName;
+    }
+}

+ 3 - 3
nlp/src/main/java/org/diagbot/nlp/relation/extract/module/Symptom.java

@@ -11,7 +11,7 @@ import org.diagbot.nlp.relation.extract.cell.*;
  **/
 public class Symptom {
     private String symptomName;
-    private String negative;
+    private Negative negative;
 
     private BodyPart  bodyPart;
     private Degree degree;
@@ -28,11 +28,11 @@ public class Symptom {
         this.symptomName = symptomName;
     }
 
-    public String getNegative() {
+    public Negative getNegative() {
         return negative;
     }
 
-    public void setNegative(String negative) {
+    public void setNegative(Negative negative) {
         this.negative = negative;
     }
 

+ 34 - 0
nlp/src/main/java/org/diagbot/nlp/relation/extract/output/OutputInfo.java

@@ -0,0 +1,34 @@
+package org.diagbot.nlp.relation.extract.output;
+
+import org.diagbot.nlp.relation.extract.module.Symptom;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @ClassName org.diagbot.nlp.relation.extract.output.OutputInfo
+ * @Description TODO
+ * @Author fyeman
+ * @Date 2019/1/22/022 11:05
+ * @Version 1.0
+ **/
+public class OutputInfo {
+    List<Symptom> symptoms = new ArrayList<>();
+    List<Symptom> vitals = new ArrayList<>();
+
+    public List<Symptom> getSymptoms() {
+        return symptoms;
+    }
+
+    public void setSymptoms(List<Symptom> symptoms) {
+        this.symptoms = symptoms;
+    }
+
+    public List<Symptom> getVitals() {
+        return vitals;
+    }
+
+    public void setVitals(List<Symptom> vitals) {
+        this.vitals = vitals;
+    }
+}

+ 0 - 12
nlp/src/main/java/org/diagbot/nlp/relation/token/RelationToken.java

@@ -1,12 +0,0 @@
-package org.diagbot.nlp.relation.token;
-
-/**
- * @ClassName org.diagbot.nlp.relation.token.RelationToken
- * @Description TODO
- * @Author fyeman
- * @Date 2019/1/18/018 14:43
- * @Version 1.0
- **/
-public class RelationToken {
-
-}

+ 10 - 0
nlp/src/main/java/org/diagbot/nlp/util/Constants.java

@@ -17,5 +17,15 @@ public class Constants {
     public final static String feature_type_history = "7";       //历史
     public final static String feature_type_feature = "9"; //症状描述中的特征信息 如部位、性质等
 
+    public static NegativeEnum[] symptom_type = new NegativeEnum[]{NegativeEnum.SYMPTOM};
+    public static NegativeEnum[] unit_time_type = new NegativeEnum[]{NegativeEnum.EVENT_TIME, NegativeEnum.UNIT};
+    public static NegativeEnum[] vital_type = new NegativeEnum[]{NegativeEnum.VITAL_INDEX};
+    public static NegativeEnum[] body_part_type = new NegativeEnum[]{NegativeEnum.BODY_PART};
+    public static NegativeEnum[] position_type = new NegativeEnum[]{NegativeEnum.POSITION};
+    public static NegativeEnum[] property_type = new NegativeEnum[]{NegativeEnum.PROPERTY};
+    public static NegativeEnum[] degree_type = new NegativeEnum[]{NegativeEnum.DEEP};
+    public static NegativeEnum[] cause_type = new NegativeEnum[]{NegativeEnum.CAUSE};
+    public static NegativeEnum[] negative_type = new NegativeEnum[]{NegativeEnum.FEMININE};
+
     public static String[] negative_words = new String[]{"无", "未", "未及", "无殊", "否认", "未见", "不", "未闻", "未闻及", "欠"};
 }

+ 1 - 1
nlp/src/test/java/org/diagbot/nlp/test/ParticipleTest.java

@@ -23,7 +23,7 @@ import java.util.List;
 public class ParticipleTest {
     public static void main(String[] args) {
         try {
-            String content = "有双手麻木感,活动后好转,颈部及肩部活动度无殊,";
+            String content = "剑突下痛胀痛1天,";
             ParticipleTest test = new ParticipleTest();
 //            InputStream is = test.getClass().getClassLoader().getResourceAsStream("present.txt");
 //            BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"), 512);

+ 24 - 0
push-web/src/main/java/org/diagbot/push/controller/RelationController.java

@@ -0,0 +1,24 @@
+package org.diagbot.push.controller;
+
+import org.diagbot.pub.web.BaseController;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+/**
+ * @ClassName org.diagbot.push.controller.RelationController
+ * @Description TODO
+ * @Author fyeman
+ * @Date 2019/1/22/022 13:33
+ * @Version 1.0
+ **/
+@Controller
+@RequestMapping("/relation")
+public class RelationController extends BaseController {
+    {
+        listView = "/pages/relation/sample.html";
+    }
+    @RequestMapping({"/index"})
+    public String index() {
+        return listView;
+    }
+}

+ 1 - 0
push-web/src/main/resources/static/index.html

@@ -66,6 +66,7 @@
           <ul class="treeview-menu">
             <li class="active"><a href="/push-web/participle/index"><i class="fa fa-circle-o"></i>分词示例</a></li>
             <li class="active"><a href="/push-web/extract/index"><i class="fa fa-circle-o"></i>提取特征</a></li>
+            <li class="active"><a href="/push-web/relation/index"><i class="fa fa-circle-o"></i>关系抽取</a></li>
             <li class="active"><a href="/push-web/algorithm/index"><i class="fa fa-circle-o"></i>算法分析</a></li>
           </ul>
         </li>

+ 1 - 0
push-web/src/main/resources/static/pages/algorithm/list.html

@@ -63,6 +63,7 @@
                     <ul class="treeview-menu">
                         <li class="active"><a href="/push-web/participle/index"><i class="fa fa-circle-o"></i>分词示例</a></li>
                         <li class="active"><a href="/push-web/extract/index"><i class="fa fa-circle-o"></i>提取特征</a></li>
+                        <li class="active"><a href="/push-web/relation/index"><i class="fa fa-circle-o"></i>关系抽取</a></li>
                         <li class="active"><a href="/push-web/algorithm/index"><i class="fa fa-circle-o"></i>算法分析</a></li>
                     </ul>
                 </li>

+ 2 - 1
push-web/src/main/resources/static/pages/extract/feature.html

@@ -63,6 +63,7 @@
                     <ul class="treeview-menu">
                         <li class="active"><a href="/push-web/participle/index"><i class="fa fa-circle-o"></i>分词示例</a></li>
                         <li class="active"><a href="/push-web/extract/index"><i class="fa fa-circle-o"></i>提取特征</a></li>
+                        <li class="active"><a href="/push-web/relation/index"><i class="fa fa-circle-o"></i>关系抽取</a></li>
                         <li class="active"><a href="/push-web/algorithm/index"><i class="fa fa-circle-o"></i>算法分析</a></li>
                     </ul>
                 </li>
@@ -97,7 +98,7 @@
                     <input type="checkbox" name="featureType" value="5"/>检查&nbsp;&nbsp;
                     <input type="checkbox" name="featureType" value="9"/>特征&nbsp;&nbsp;
 
-                    <button type="button" class="btn btn-success pull-right" onclick="_ajax('/doc/feature/generate')"><i
+                    <button type="button" class="btn btn-success pull-right" onclick="_ajax('/feature/generate')"><i
                             class="fa fa-credit-card"></i> 提取
                     </button>
                 </div>

+ 1 - 0
push-web/src/main/resources/static/pages/participle/sample.html

@@ -63,6 +63,7 @@
           <ul class="treeview-menu">
             <li class="active"><a href="/push-web/participle/index"><i class="fa fa-circle-o"></i>分词示例</a></li>
             <li class="active"><a href="/push-web/extract/index"><i class="fa fa-circle-o"></i>提取特征</a></li>
+            <li class="active"><a href="/push-web/relation/index"><i class="fa fa-circle-o"></i>关系抽取</a></li>
             <li class="active"><a href="/push-web/algorithm/index"><i class="fa fa-circle-o"></i>算法分析</a></li>
           </ul>
         </li>

+ 204 - 0
push-web/src/main/resources/static/pages/relation/sample.html

@@ -0,0 +1,204 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge">
+  <title>AdminLTE 2 | Invoice</title>
+  <!-- Tell the browser to be responsive to screen width -->
+  <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
+  <!-- Bootstrap 3.3.6 -->
+  <link rel="stylesheet" href="../bootstrap/css/bootstrap.min.css">
+  <!-- Font Awesome -->
+  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css">
+  <!-- Ionicons -->
+  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css">
+  <!-- Theme style -->
+  <link rel="stylesheet" href="../dist/css/AdminLTE.min.css">
+  <!-- AdminLTE Skins. Choose a skin from the css/skins
+       folder instead of downloading all of them to reduce the load. -->
+  <link rel="stylesheet" href="../dist/css/skins/_all-skins.min.css">
+
+  <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
+  <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
+  <!--[if lt IE 9]>
+  <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
+  <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+  <![endif]-->
+  <style>
+    .interval {
+      padding : 1px 8px!important;
+    }
+
+    #my_file {
+      visibility: hidden;    /* 隐藏 */
+    }
+  </style>
+</head>
+<body class="hold-transition skin-blue sidebar-mini">
+<div class="wrapper">
+
+  <header class="main-header">
+    <!-- Logo -->
+    <a href="#" class="logo">
+      <!-- logo for regular state and mobile devices -->
+      <span class="logo-lg"><b>D</b>iagbot</span>
+    </a>
+
+  </header>
+  <!-- Left side column. contains the logo and sidebar -->
+  <aside class="main-sidebar">
+    <!-- sidebar: style can be found in sidebar.less -->
+    <section class="sidebar">
+      <!-- /.search form -->
+      <!-- sidebar menu: : style can be found in sidebar.less -->
+      <ul class="sidebar-menu">
+        <li class="header">MAIN NAVIGATION</li>
+        <li class="treeview active">
+          <a href="#">
+            <i class="fa fa-folder"></i> <span>diagbot</span>
+            <span class="pull-right-container">
+              <i class="fa fa-angle-left pull-right"></i>
+            </span>
+          </a>
+          <ul class="treeview-menu">
+            <li class="active"><a href="/push-web/participle/index"><i class="fa fa-circle-o"></i>分词示例</a></li>
+            <li class="active"><a href="/push-web/extract/index"><i class="fa fa-circle-o"></i>提取特征</a></li>
+            <li class="active"><a href="/push-web/relation/index"><i class="fa fa-circle-o"></i>关系抽取</a></li>
+            <li class="active"><a href="/push-web/algorithm/index"><i class="fa fa-circle-o"></i>算法分析</a></li>
+          </ul>
+        </li>
+      </ul>
+    </section>
+    <!-- /.sidebar -->
+  </aside>
+
+  <!-- Content Wrapper. Contains page content -->
+  <div class="content-wrapper">
+    <!-- Content Header (Page header) -->
+    <section class="content-header">
+      <h1>
+        <small></small>
+      </h1>
+      <ol class="breadcrumb">
+        <li><a href="#"><i class="fa fa-dashboard"></i>Diagbot</a></li>
+        <li><a href="#">nlp</a></li>
+        <li class="active">分词示例</li>
+      </ol>
+    </section>
+    <!-- Main content -->
+    <section class="invoice">
+      <!-- title row -->
+      <div class="row">
+        <div class="col-xs-12">
+          <h2 class="page-header">
+            <form role="form" id="up_file_form" enctype='multipart/form-data' style="position: relative;">
+              <i class="fa"></i>病 例
+            </form>
+          </h2>
+        </div>
+        <!-- /.col -->
+      </div>
+      <!-- /.box-header -->
+      <div class="box-body">
+        <div class="row">
+          <div class="col-xs-12">
+            <form role="form" id="participle_form">
+              <!-- textarea -->
+              <div class="form-group">
+                <textarea class="form-control" rows="5" placeholder="Enter ..." name="content" id="content">
+患者11天前烧水时出现心悸,胸闷,感全身不适全身血管跳动,头晕,四肢乏力,视物旋转,恶心,耳鸣,持续1小时后好转。10天前再次出现心悸,胸闷,感全身血管跳动,四肢乏力。6天前聊天时再次出现意识丧失,症状同前,建议转上级医院诊治,遂来我院就诊。
+</textarea>
+              </div>
+            </form>
+          </div>
+        </div>
+        <div class="row">
+          <div class="col-xs-12">
+            <button type="button" class="btn btn-success pull-right" onclick="_ajax('/relation/extraction')"><i
+                    class="fa fa-credit-card"></i> 开&nbsp;始&nbsp;分&nbsp;析
+            </button>
+          </div>
+        </div>
+      </div>
+      <script>
+          function _ajax(url) {
+              $.support.cors = true;
+              $.ajax({
+                  url: nlp_web_url + url,
+                  data: $("#participle_form").serialize(),
+                  dataType: "json",
+                  type: "post",
+                  success: function (data) {
+                      var outputInfos = data.data;
+                      $.each(outputInfos, function (key, item) {
+                          h += "<div class='form-group'><label>" + key + ":&nbsp;</label>";
+                          $.each(item,function (k, t) {
+                              if  (t == null) {
+                                  t = "";
+                              }
+                              h += "&nbsp;(<label>" + k + ":" + t + "</label>)&nbsp;";
+                          });
+                          h += "</div>";
+                      });
+                      $("#relation_result").html(data.data);
+                  }
+              });
+          };
+      </script>
+      <div class="row">
+          <div class="col-md-12">
+              <div class="box box-solid">
+                  <div class="box-header with-border">
+                      <i class="fa fa-text-width"></i>
+                      <h3 class="box-title">结 构 化</h3>
+                  </div>
+                  <!-- /.box-header -->
+                  <div class="box-body">
+                      <dl class="dl-horizontal" id="relation_result">
+                          <dt>Description lists</dt>
+                          <dd>A description list is perfect for defining terms.</dd>
+                          <dd>A description list is perfect for defining terms.</dd>
+                          <dt>Euismod</dt>
+                          <dd>Vestibulum id ligula porta felis euismod semper eget lacinia odio sem nec elit.</dd>
+                          <dd>Donec id elit non mi porta gravida at eget metus.</dd>
+                          <dt>Malesuada porta</dt>
+                          <dd>Etiam porta sem malesuada magna mollis euismod.</dd>
+                          <dt>Felis euismod semper eget lacinia</dt>
+                          <dd>Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo
+                              sit amet risus.
+                          </dd>
+                      </dl>
+                  </div>
+                  <!-- /.box-body -->
+              </div>
+              <!-- /.box -->
+          </div>
+          <!-- ./col -->
+        </div>
+      </div>
+    </section>
+    <!-- /.content -->
+    <div class="clearfix"></div>
+  </div>
+
+  <!-- /.control-sidebar -->
+  <!-- Add the sidebar's background. This div must be placed
+       immediately after the control sidebar -->
+  <div class="control-sidebar-bg"></div>
+</div>
+<!-- ./wrapper -->
+
+<!-- jQuery 2.2.3 -->
+<script src="../plugins/jQuery/jquery-2.2.3.min.js"></script>
+<!-- Bootstrap 3.3.6 -->
+<script src="../bootstrap/js/bootstrap.min.js"></script>
+<!-- FastClick -->
+<script src="../plugins/fastclick/fastclick.js"></script>
+<!-- AdminLTE App -->
+<script src="../dist/js/app.min.js"></script>
+<!-- AdminLTE for demo purposes -->
+<script src="../dist/js/demo.js"></script>
+
+<script src="../dist/js/push.js"></script>
+</body>
+</html>