使用Spring boot 构建知识图谱及简单Java GUI学习笔记

使用Spring boot 构建知识图谱及简单Java GUI学习笔记

一、学习目的

开发一个关于房地产法律相关的智能问答系统
1.在简单Java GUI界面中实现一个科大讯飞语音调用及知识图谱API调用,反馈查询结果,实现一个简单的demo中的demo。
2.完成Spark分类器HanLP分词以及Spring boot后端与前端的链接。

使用的工具

Eclipse IntelliJ IDEA maven Spark HanLP neo4j mysql

**

项目粗略流程说明

**
使用spring boot作为后端,通过语料库输入HanLP分词后Spark训练分类器完成用户问题分类,使用spring boot jpa查询neo4j中保存好的知识图谱,结果反馈到前端。
**

知识简介

GUI相关

swing awt
一个 Java 的图形界面,由各种不同类型的“元素”组成,例如: 窗口、菜单栏、对话框、标签、按钮、文本框等等,这些“元素”统一被称为 组件(Component)。

组件按照不同的功能,可分为 顶层容器、中间容器、基本组件。一个简单窗口的组成,如下层级结构所示:

顶层容器
菜单栏
中间容器
基本组件
基本组件
组件类型的继承关系:

顶层容器 属于窗口类组件,继承自java.awt.Window;
中间容器 和 基本组件 继承自javax.swing.JComponent。
GUI的重点是控件的布局,但是这个是一个比较难的应用部分,经过多次尝试,我使用了最简单的gridlayout来完成基本功能的搭建。下面是关于这个GUI的java代码,完整的项目详见下面github链接的补充。

package view;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

import com.iflytek.cloud.speech.RecognizerListener;
import com.iflytek.cloud.speech.RecognizerResult;
import com.iflytek.cloud.speech.ResourceUtil;
import com.iflytek.cloud.speech.SpeechConstant;
import com.iflytek.cloud.speech.SpeechError;
import com.iflytek.cloud.speech.SpeechRecognizer;
import com.iflytek.cloud.speech.SpeechUtility;


import util.Version;
import util.JsonParser;
import util.DebugLog;
import util.DrawableUtils;

import util.http;
public class demo extends JFrame implements ActionListener {
    static final int WIDTH =300;
    static final int HEIGHT =400;
    public static int flag=1;
    public static StringBuffer recorder=new StringBuffer("");
    public static JTextArea answer=new JTextArea("这里将显示回调的结果",9,12);
    public static JTextArea input=new JTextArea("请在这里输入",9,12);

    private SpeechRecognizer mIat;
    private Map mParamMap=new HashMap();
    private String mSavePath="./iat_test.pcm";
    private static final String VAL_TRUE = "1";
    private static class DefaultValue{
        public static final String ENG_TYPE = SpeechConstant.TYPE_CLOUD;
        public static final String SPEECH_TIMEOUT = "60000";
        public static final String NET_TIMEOUT = "20000";
        public static final String LANGUAGE = "zh_cn";

        public static final String ACCENT = "mandarin";
        public static final String DOMAIN = "iat";
        public static final String VAD_BOS = "5000";
        public static final String VAD_EOS = "1800";

        public static final String RATE = "16000";
        public static final String NBEST = "1";
        public static final String WBEST = "1";
        public static final String PTT = "1";

        public static final String RESULT_TYPE = "json";
        public static final String SAVE = "0";
    }

    public demo() {

        StringBuffer param = new StringBuffer();
        param.append( "appid=" + Version.getAppid() );
        param.append( ","+SpeechConstant.LIB_NAME_32+"=OurProjectDemo" );
        System.out.println(param.toString());
        SpeechUtility.createUtility( param.toString() );

        JFrame frame=new JFrame();
        frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setBounds(0,0,WIDTH,HEIGHT);
        frame.setTitle("Our project demo");
        frame.setResizable(false);//是否允许他们调整页面大小呢?

        GridLayout gridlayout=new GridLayout(4,4,3,3);
        frame.setLayout(gridlayout);

        Toolkit kit=Toolkit.getDefaultToolkit();
        Dimension screenSize=kit.getScreenSize();
        int width=screenSize.width;
        int height=screenSize.height;
        int x=(width-WIDTH)/2;
        int y=(height-HEIGHT)/2;
        frame.setLocation(x, y);

        //Container container=frame.getContentPane();
        //container.setLayout(null);


        JButton jbtnRecognizer=new JButton("录音");
        JButton jbtnStop=new JButton("停止");
        JButton jbtnSendIndistinct=new JButton("模糊查询");
        JButton jbtnSendDistinct=new JButton("精准查询");
        //JTextArea input=new JTextArea("等待您的输入,请少于200字",9,12);
        //JTextArea answer=new JTextArea("这里将显示回调的结果",9,12);
        JScrollPane jscrollPane1=new JScrollPane(input);
        jscrollPane1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        JScrollPane jscrollPane2=new JScrollPane(answer);
        jscrollPane2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        JPanel jsPanel1=new JPanel();
        //jsPanel1.setPreferredSize(new Dimension(width, height));
        jsPanel1.add(jscrollPane1);
        JPanel jsPanel2=new JPanel();
        jsPanel2.add(jscrollPane2);
        input.setLineWrap(true);
        input.setWrapStyleWord(true);//文本框自动换行
        input.setEditable(true);
        answer.setLineWrap(true);
        answer.setWrapStyleWord(true);
        answer.setEditable(true);

        frame.setLayout(gridlayout);
        frame.add(jbtnRecognizer);
        jbtnRecognizer.setToolTipText("this button for recorder");
        frame.add(jbtnStop);
        frame.add(jbtnSendIndistinct);
        frame.add(jbtnSendDistinct);
        frame.add(jsPanel1);
        frame.add(jsPanel2);

        mIat=SpeechRecognizer.createRecognizer();
        initParamMap();
        //initMenu();//暂时不知道这个用来干什么的

        frame.setVisible(true);//这句话是刷新组件显示的话,放到最后!

        jbtnRecognizer.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                setting();
                input.setText(null);
                System.out.println(input.getText());
                //mIat.startListening(recognizerListener);
                if(!mIat.isListening()) {
                    System.out.println(mIat.isListening());//显示结果的确在录音
                    mIat.startListening(recognizerListener);
                    System.out.println(mIat.isListening());//显示结果的确在录音
                }
                else 
                    mIat.stopListening();
            }
        });

        jbtnStop.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println(mIat.isListening());
                mIat.stopListening();
                //input.setText(result);
                System.out.println(mIat.isListening());
            }
        });

        jbtnSendIndistinct.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                    recorder.delete(0, recorder.length());//清空recorder
                    recorder.append("http://shuyantech.com/api/entitylinking/cutsegment?q=");
                    recorder.append(input.getText());
                    flag=1;
                    frame.add(new http());
                //System.out.println(flag);
                //System.out.println(recorder);
                //System.out.println(recorder.toString());
                //StringBuffer recorder=new StringBuffer(input.getText());
                //现在recorder里面有了我们想要搜索的内容

                //System.out.println(recorder.toString());
            }
        });
        jbtnSendDistinct.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                // TODO Auto-generated method stub
                recorder.delete(0, recorder.length());//清空recorder
                recorder.append("http://shuyantech.com/api/cnprobase/concept?q=");
                recorder.append(input.getText());
                flag=2;
                frame.add(new http());
            }
        });
    }   

    public static void main(String args[]) {
        new demo();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        // TODO Auto-generated method stub

    }

    private RecognizerListener recognizerListener = new RecognizerListener() {

        @Override
        public void onBeginOfSpeech() {
            DebugLog.Log( "onBeginOfSpeech enter" );
            //((JLabel)input.getComponent(0)).setText("Now id recording!");
            //((JLabel) jbtnRecognizer.getComponent(0)).setText("听写中...");
            //jbtnRecognizer.setEnabled(false);
        }

        @Override
        public void onEndOfSpeech() {
            DebugLog.Log( "onEndOfSpeech enter" );
        }

        /**
         * 获取听写结果. 获取RecognizerResult类型的识别结果,并对结果进行累加,显示到Area里
         */
        @Override
        public void onResult(RecognizerResult results, boolean islast) {
            DebugLog.Log( "onResult enter" );

            //如果要解析json结果,请考本项目示例的 com.iflytek.util.JsonParser类
            String text = JsonParser.parseIatResult(results.getResultString());
            //String text = results.getResultString();
            System.out.println(results.getResultString().toString());
            //System.out.println(text);
            input.append(text);
            text = input.getText();
            //input.setText(text);
            System.out.println(input.getText());
            /*if( null!=text ){//如果文本框内容非空
                int n = text.length() / TEXT_COUNT + 1;
                int fontSize = Math.max( 10, DEF_FONT_SIZE - 2*n );
                DebugLog.Log( "onResult new font size="+fontSize );
                int style = n>1 ? Font.PLAIN : DEF_FONT_SIZE;
                Font newFont = new Font( DEF_FONT_NAME, style, fontSize );
                input.setFont( newFont );
            }*/

            /*if( islast ){
                iatSpeechInitUI();
            }*/
        }

        //声音输入大小替换图片
        /*@Override
        public void onVolumeChanged(int volume) {
            DebugLog.Log( "onVolumeChanged enter" );
            if (volume == 0)
                volume = 1;
            else if (volume >= 6)
                volume = 6;
            labelWav.setIcon(new ImageIcon("res/mic_0" + volume + ".png"));
        }*/

        @Override
        public void onError(SpeechError error) {
            DebugLog.Log( "onError enter" );
            if (null != error){
                DebugLog.Log("onError Code:" + error.getErrorCode());
                System.out.println(error.getErrorDescription(true));
                input.setText( error.getErrorDescription(true) );
                //iatSpeechInitUI();
            }
        }

        @Override
        public void onEvent(int eventType, int arg1, int agr2, String msg) {
            DebugLog.Log( "onEvent enter" );
        }

        @Override
        public void onVolumeChanged(int arg0) {
            // TODO Auto-generated method stub

        }
    };

    private void initParamMap(){
        this.mParamMap.put( SpeechConstant.ENGINE_TYPE, DefaultValue.ENG_TYPE );
        this.mParamMap.put( SpeechConstant.SAMPLE_RATE, DefaultValue.RATE );
        this.mParamMap.put( SpeechConstant.NET_TIMEOUT, DefaultValue.NET_TIMEOUT );
        this.mParamMap.put( SpeechConstant.KEY_SPEECH_TIMEOUT, DefaultValue.SPEECH_TIMEOUT );

        this.mParamMap.put( SpeechConstant.LANGUAGE, DefaultValue.LANGUAGE );
        this.mParamMap.put( SpeechConstant.ACCENT, DefaultValue.ACCENT );
        this.mParamMap.put( SpeechConstant.DOMAIN, DefaultValue.DOMAIN );
        this.mParamMap.put( SpeechConstant.VAD_BOS, DefaultValue.VAD_BOS );

        this.mParamMap.put( SpeechConstant.VAD_EOS, DefaultValue.VAD_EOS );
        this.mParamMap.put( SpeechConstant.ASR_NBEST, DefaultValue.NBEST );
        this.mParamMap.put( SpeechConstant.ASR_WBEST, DefaultValue.WBEST );
        this.mParamMap.put( SpeechConstant.ASR_PTT, DefaultValue.PTT );

        this.mParamMap.put( SpeechConstant.RESULT_TYPE, DefaultValue.RESULT_TYPE );
        this.mParamMap.put( SpeechConstant.ASR_AUDIO_PATH, null );
    }



    void setting(){
        final String engType = this.mParamMap.get(SpeechConstant.ENGINE_TYPE);

        for( Entry entry : this.mParamMap.entrySet() ){
            mIat.setParameter( entry.getKey(), entry.getValue() );
        }

        //本地识别时设置资源,并启动引擎
        if( SpeechConstant.TYPE_LOCAL.equals(engType) ){
            //启动合成引擎
            mIat.setParameter( ResourceUtil.ENGINE_START, SpeechConstant.ENG_ASR );

            //设置资源路径
            final String rate = this.mParamMap.get( SpeechConstant.SAMPLE_RATE );
            final String tag = rate.equals("16000") ? "16k" : "8k";
            String curPath = System.getProperty("user.dir");
            DebugLog.Log( "Current path="+curPath );
            String resPath = ResourceUtil.generateResourcePath( curPath+"/asr/common.jet" )
                    + ";" + ResourceUtil.generateResourcePath( curPath+"/asr/src_"+tag+".jet" );
            System.out.println( "resPath="+resPath );
            mIat.setParameter( ResourceUtil.ASR_RES_PATH, resPath );
        }// end of if is TYPE_LOCAL

    }// end of function setting
}

使用Spring boot 构建知识图谱及简单Java GUI学习笔记_第1张图片

下面是我们的项目的重点了,关于我们知识图谱的Demo

1.Spring boot JPA
2.HanLP分词
3.Spark分类器训练
4.neo4j图数据库内容导入及链接spring boot


Spring Boot JPA

是什么
JPA顾名思义就是Java Persistence API的意思,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
为什么
1.标准化
JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何声称符合 JPA 标准的框架都遵循同样的架构,提供相同的访问API,这保证了基于JPA开发的企业应用能够经过少量的修改就能够在不同的JPA框架下运行。
2.容器级特性的支持
JPA框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企业应用发挥更大的作用。
3.简单方便
JPA的主要目标之一就是提供更加简单的编程模型:在JPA框架下创建实体和创建Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity进行注释,JPA的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易的掌握。JPA基于非侵入式原则设计,因此可以很容易的和其它框架或者容器集成。
4.查询能力
JPA的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句,可以看成是Hibernate HQL的等价物。JPA定义了独特的JPQL(Java Persistence Query Language),JPQL是EJB QL的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。
5.高级特性
JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。
怎么用

PersonRepository.java
package com.appleyk.repository;

import java.util.List;

import org.springframework.data.neo4j.repository.GraphRepository;
import org.springframework.data.repository.query.Param;

import com.appleyk.node.Person;

public interface PersonRepository extends GraphRepository{
     List findByName(@Param("name") String name);   
}
PersonController.java
package com.appleyk.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.appleyk.node.Person;
import com.appleyk.repository.PersonRepository;
import com.appleyk.result.ResponseMessage;
import com.appleyk.result.ResponseResult;

@RestController
@RequestMapping("/rest/appleyk/person")
public class PersonController {

    @Autowired
    PersonRepository personRepository;

    /**
     * 根据演员名查询Person实体
     * 
     * @param title
     * @return
     */
    @RequestMapping("/get")
    public List getPersons(@RequestParam(value = "name") String name) {
        return personRepository.findByName(name);
    }

    /**
     * 创建一个演员节点
     * 
     * @param genre
     * @return
     */
    @PostMapping("/save")
    public ResponseResult savePerson(@RequestBody Person person) {
        personRepository.save(person);
        return new ResponseResult(ResponseMessage.OK);
    }


}

通过这么一个链接我们能够非常便捷地在neo4j上面修改我们的数据

HanLP分词

这里的重点是关于词性的分类,只需要在下载好的data文件夹里面设置好你所需要的个性词典,以及在java代码里面标注好你所设定的分词属性即能够完成分词,如下:下图首先是我们关于二手房的内容(这里注意不要使用记事本打开自己的个性词典,会导致读取错误),使用Spring boot 构建知识图谱及简单Java GUI学习笔记_第2张图片
使用Spring boot 构建知识图谱及简单Java GUI学习笔记_第3张图片
然后在java代码中添加

public void loadSecondHandHouseDict(String path) {

        File file = new File(path);
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader(file));
            addCustomDictionary(br, 5);
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        }
    }
    public void addCustomDictionary(BufferedReader br, int type) {

        String word;
        try {
            while ((word = br.readLine()) != null) {
                switch (type) {
                /**
                 * 设置电影名词词性 == nm 0
                 */
                case 0:
                    CustomDictionary.add(word, "nm 0");
                    break;
                /**
                 * 设置电影类型名词 词性 == ng 0
                 */
                case 1:
                    CustomDictionary.add(word, "ng 0");
                    break;
                /**
                 * 设置电影评分数词 词性 == x 0
                 */
                case 2:
                    CustomDictionary.add(word, "x 0");
                    break;
                case 3:
                    CustomDictionary.add(word, "pR 0");
                    break;
                case 4:
                    CustomDictionary.add(word, "hR 0");
                    break;
                case 5:
                    CustomDictionary.add(word, "sH 0");
                    break;//二手房的分词结果是sH
                case 6:
                    CustomDictionary.add(word, "rH 0");
                    break;
                case 7:
                    CustomDictionary.add(word, "pOTCH 0");
                    break;
                case 8:
                    CustomDictionary.add(word, "shops 0");
                    break;
                case 9:
                    CustomDictionary.add(word, "oB 0");
                    break;
                default:
                    break;
                }
            }
            br.close();
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

注意,必须在application.properties里面加载我们所需要的词典文件(二手房)

HanLP.CustomDictionary.path.secondHandHouse=${rootDirPath}/dictionary/custom/secondHandHouse.txt

接下来是我们的分词结果展示
使用Spring boot 构建知识图谱及简单Java GUI学习笔记_第4张图片
可以看到起诉流程这一二手房里面的词汇被我们指定识别成了sH这一设置好的分词标签。

Spark分类器

这里使用Spark朴素贝叶斯分类器,我们采用的是使用文本识别来训练其分类结果,例如分类器分类演员工资的问题是
使用Spring boot 构建知识图谱及简单Java GUI学习笔记_第5张图片
这里的nnt是使用HanLP分词后指定人物识别的词性,经过这样的指定后可以看到当我们输入使用Spring boot 构建知识图谱及简单Java GUI学习笔记_第6张图片使用Spring boot 构建知识图谱及简单Java GUI学习笔记_第7张图片
这里由于图数据库中并没有保存演员工资这一数据所以查询不到结果,不过我们查询已有的例子如:使用Spring boot 构建知识图谱及简单Java GUI学习笔记_第8张图片使用Spring boot 构建知识图谱及简单Java GUI学习笔记_第9张图片
在我们的后台看到的是使用Spring boot 构建知识图谱及简单Java GUI学习笔记_第10张图片
目前关于Spark分类器我们还只是掌握了用法,但是具体的训练过程还不够清晰,所以分类时也时常会出现问题,这里只展示了成功的例子(嘿嘿)
Neo4j链接spring boot后端

server.port=8080
server.session.timeout=10
server.tomcat.uri-encoding=utf8

#在application.properties文件中引入日志配置文件
#=====================================  log  =============================
logging.config=classpath:logback-boot.xml

#Neo4j配置
spring.data.neo4j.username=neo4j
spring.data.neo4j.password=//此处应该修改为你自己的数据库以及密码
#数据库uri地址 
spring.data.neo4j.uri=http://localhost:7474

#HanLP分词字典及自定义问题模板根目录
rootDirPath=C:/Code/data

#HanLP 用户自定义扩展词库,不建议使用HanLP自定义词典追加的模式,建议自行加载
HanLP.CustomDictionary.path.movieDict=${rootDirPath}/dictionary/custom/movieDict.txt 
HanLP.CustomDictionary.path.genreDict=${rootDirPath}/dictionary/custom/genreDict.txt  
HanLP.CustomDictionary.path.scoreDict=${rootDirPath}/dictionary/custom/scoreDict.txt 

至此,基本操作完成,鉴于项目还处于较为早期阶段,所以具体细节我也很多不清楚,感谢观看。 BY: FD

你可能感兴趣的:(使用Spring boot 构建知识图谱及简单Java GUI学习笔记)