Wordnet × CommonLisp에서 동의어를 추출해보기

소개



지정한 단어의 동의어·동의어를 조사하는 방법을 찾고 있었는데, 이하의 기사를 발견했으므로 이것을 Common Lisp로 해 보기로 했습니다.
  • Wordnet × Python으로 동의어 추출 - YoheiM .NET

  • WordNet



    한국어 WordNet은 다음 웹 사이트에서 다운로드할 수 있습니다.
    이번에는 위 기사와 마찬가지로 sqlite3의 DB로 공개된 것을 사용합니다.
  • 한국어 WordNet

  • Common Lisp에서 WordNet 사용



    여러가지 편리하므로, Web 프레임워크 「 Caveman2 」를 사용합니다.
    동의어 추출은 Common Lisp 프로그램에서 수행됩니다.

    스켈레톤 프로젝트의 디렉토리 구성



    REPL에서 다음 프로그램을 실행하여 해골 프로젝트를 만듭니다.
    (ql:quickload :caveman2)
    (caveman2:make-project #P"プロジェクトを作成するディレクトリのパス" :author "自分の名前")
    (ql:register-local-projects) ; モジュールをlocal-projectsに登録
    

    이하의 디렉토리 구성중에서 ★마크가 붙어 있는 개소를 이번은 만나갑니다.
    wordnet-jp
      ├── README.markdown
      ├── app.lisp
      ├── db
      │    ├── wnjpn.db     ★ ダウンロードしてきたsqlite3のDBをここに置く
      │    └── schema.sql
      ├── wordnet-jp.asd
      ├── wordnet-jp-test.asd
      ├── src
      │    ├── config.lisp  ★ SQLite3の設定関連
      │    ├── db.lisp
      │    ├── main.lisp
      │    ├── view.lisp
      │    └── web.lisp     ★ SQL文の実行処理関連
      ├── static
      │   └── css
      │        └── main.css
      ├── templates
      │   ├── _errors
      │   │     └── 404.html
      │   ├── index.html    ★ テンプレートエンジン
      │   └── layouts
      │         └── default.html
      └── tests
            └── myapp.lisp
    

    DB 설정



    WordNet sqlite3 DB를 사용하도록 설정을 변경.

    다음 프로그램을

    src/config.lisp
    (defconfig :common
       `(:databases ((:maindb :sqlite3 :database-name ":memory:"))))
    

    다음과 같이 변경합니다.

    src/config.lisp
    (defconfig :common
        `(:databases ((:maindb :sqlite3 :database-name ,(merge-pathnames #P"db/wnjpn.db" *application-root*)))))
    

    WordNet을 이용하는 프로그램


    src 디렉토리 아래의 web.lisp 에 처리를 기술해 갑니다.

    src/web.lisp
    (defun get-words (lemma)
      (with-connection (db)
        (let ((result (retrieve-all (select (:*) (from :word) (where (:= :lemma lemma)))))
              lemma-list)
          (dolist (n result)
            (setf lemma-list (cons (second n) lemma-list)))
          lemma-list)))
    
    (defun get-senses (wordid)
      (with-connection (db)
        (let ((result (retrieve-all (select (:*) (from :sense) (where (:= :wordid wordid)))))
              sense-list)
          (dolist (n result)
            (setf sense-list (cons (second n) sense-list)))
          sense-list)))
    
    (defun get-synset (synset)
      (with-connection (db)
        (let ((result (retrieve-all (select (:*) (from :synset) (where (:= :synset synset)))))
              synset-list)
          (dolist (n result)
            (setf synset-list (cons (sixth n) synset-list)))
          synset-list)))
    
    (defun get-words-from-synset (synset lang)
      (with-connection (db)
        (let ((result (retrieve-all (select (:word.*)
                                      (from :sense :word)
                                      (where (:and (:= :synset synset)
                                                   (:= :word.lang lang)
                                                   (:= :sense.wordid :word.wordid))))))
              words-list)
          (dolist (n result)
            (setf words-list (cons (sixth n) words-list)))
          words-list)))
    
    (defun get-words-from-senses (synonym sense &optional (lang "jpn"))
      (dolist (s sense)
        (let ((syns  (first (get-synset s)))
              (words (get-words-from-synset s lang)))
          (setf (gethash syns synonym) words)))
      synonym)
    
    (defun get-synonym (word)
      (let ((words (get-words word))
            (synonym (make-hash-table :test #'equal)))
        (dolist (id words)
          (let* ((sense (get-senses id)))
            (setf synonym  (get-words-from-senses synonym sense))))
        synonym))
    
    (defun wordnet-test (input)
      (let ((synonym (get-synonym input)))
        (maphash #'(lambda (key val)
                     (format t "key = ~A, val = ~A~%" key val))
                 synonym)))
    

    테스트 실행



    시험에 실행해 보면 다음과 같이 됩니다.
    (ql:quickload :wordnet-jp)
    (wordnet-jp.web::wordnet-test "楽しい")
    



    제대로 동의어를 얻을 수 있네요.

    웹 애플리케이션화 해보기



    모처럼 Caveman2를 사용하고 있으므로 웹 애플리케이션을 만들어 보겠습니다.
    브라우저에서 텍스트 상자에서 단어를 입력하여 브라우저에 결과를 표시합니다.

    우선은 적당하게 템플릿을 작성합니다.

    templates/index.html
    {% extends "layouts/default.html" %}
    {% block title %}Welcome to Caveman2{% endblock %}
    {% block content %}
    <div id="main">
      <form action="/wordnet-search" method="POST">
        <input type="text" name="word" value="{{ word }}">
        <input type="submit" value="Search">
      </form>
      {% for synm in synonym %}
      <ul>
        {% for s in synm %}
        {{ s }}
        {% endfor %}
      </ul>
      {% endfor %}
    </div>
    {% endblock %}
    

    다음으로, 라우팅 및 브라우저에 대한 반영 처리를 기술

    src/web.lisp
    (defroute ("/wordnet-search" :method :POST) (&key |word|)
      (render-wordnet-jp |word|))
    

    src/web.lisp
    (defun render-wordnet-jp (word)
      (render #P"index.html" `(:synonym ,(get-synonym word))))
    

    다음 명령으로 실행.
    (ql:quickload :wordnet-jp)
    (wordnet-jp:start)
    

    적절한 브라우저에서 localhost:5000에 액세스합니다.

    이하, 실행중인 모습



    그렇게 할 수 있었습니다.

    마지막으로



    WordNet을 사용하여 동의어 추출을 Common Lisp에서 시도했습니다.
    이런 건 정말 재미있네요.

    좋은 웹페이지 즐겨찾기