Solr7.4 Tagger Handler (NER,Named-Entity Recognition)

2018. 9. 22. 00:43Search-Engine/Elasticsearch&Solr

Solr 7.4.x version의 Tagger Handler를 이용한 NER(Named-Entity Recognition)


NER이란 자연어에서 뜻이 있는 단어를 뽑아내는 것이다. 챗봇처럼 자연어와 관련있는 기술에서 사용되는 기능인데, 예를 들어 "피자 주문할게요" 라는 질문이 있다. 이 문장에서 유추해 볼 수 있는 것은 "피자 주문" 이것을 더 보편화시켜 보면 "메뉴 주문"이라는 사용자질의 "의도"를 알 수 있다. 만약 "햄버거 주문할게요"라는 질의가 있으면 이 또한 "메뉴 주문"이라는 의도라는 것을 유추할 수 있다. 그럼 사용자가 어떠한 메뉴를 주문한다는 것은 알겠는데 그럼 그 메뉴가 무엇인가? 이러한 것이 챗봇에서는 NER이라는 기술로 추출해 낼 수 있는 단어라는 것이다. "피자 주문할게요"라는 질의의 의도는 "메뉴 주문" 개체명(Named-Entity)는 "피자"가 되는 것이다. 이렇게 의미있는 단어를 뽑아내는 것은 아주 중요한 챗봇의 기술이며 앞으로 더욱 발전해야할 기술이기도 하다. 이 NER 기술을 solr 7.4의 Tagger Handler를 이용하여 구현해 볼 것이다.


모든 구현은 Mac OS 환경에서 구현하였습니다.


우선 구현에 앞서 선행되어야 하는 것이, 바로 이전의 글인 Solr와 Zookeeper 연동입니다. 모든 환경은 solr cloud 환경에서 구현하였기에 중복되는 설명은 배제하였습니다.



$SOLR_HOME/server/solr 폴더 밑에 configset이 존재할 것입니다. 그 폴더를 보면 디폴트 환경설정 파일이 있는데, 그 안의 schema.xml과 solrconfig.xml 파일을 만질 것입니다. 이전에 따로 사용중이셨던 설정파일을 이용하셔도 무관합니다.




<schema.xml>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<field name="DOCID" type="string" indexed="true" stored="true" required="true" multiValued="false" />
<field name="NER_VALUE" type="text_general" indexed="true"/>
<field name="NER_KEY" type="string" indexed="true"/>
<field name="NER_TAG" type="tag" stored="false"/>
<copyField source="NER_VALUE" dest="NER_TAG"/>
<uniqueKey>DOCID</uniqueKey>
<fieldtype name="tag" class="solr.TextField" positionIncrementGap="100" postingsFormat="FST50"
        omitTermFreqAndPositions="true" omitNorms="true">
    <analyzer type="index">
      <tokenizer class="solr.StandardTokenizerFactory"/>
      <filter class="solr.ASCIIFoldingFilterFactory"/>
      <filter class="solr.LowerCaseFilterFactory"/>
      <filter class="solr.ConcatenateGraphFilterFactory" preservePositionIncrements="false"/>
    </analyzer>
    <analyzer type="query">
      <tokenizer class="solr.StandardTokenizerFactory"/>
      <filter class="solr.ASCIIFoldingFilterFactory"/>
      <filter class="solr.LowerCaseFilterFactory"/>
    </analyzer>  
</fieldtype>
cs



적당한 위치에 선언해줍니다. NER과 관련된 필드들과 필드 타입을 선언한 설정입니다.


<solrconfig.xml>

1
2
3
4
5
<requestHandler name="/tag" class="solr.TaggerRequestHandler">
    <lst name="defaults">
      <str name="field">NER_TAG</str>
    </lst>
</requestHandler>
cs


적당한 위치에 선언해줍니다. 개체명을 추출하기 위한 Tagger Handler를 등록하는 설정입니다.


만약 디폴트 설정파일을 이용하지 않고 새로 정의하셨다면 해당 설정 폴더를 SolrConifg/conf/ 밑으로 넣어줍니다.(SolrConfg는 맘대로정의 가능하지만 conf는 맞춰주셔야합니다.)


solr 디렉토리의 적당한 곳에 위치시켜줍니다. 그리고 난 후에 zookeeper에 upconfig를 합니다.(solr collection들이 사용할 설정파일을 zookeeper에 업로드하여 중앙에서 관리해줍니다.)

저는 앞으로 자주 사용할 가능성이 있다 판단이 되어서 쉘스크립트 실행파일을 만들어서 upconfig 했습니다.


<uploadSolrConfig.sh>

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
 
# usage uploadSolrConfig.sh zipConfigPath configName zkServers
 
CONFIG_PATH=$1
CONFIG_NAME=$2
ZK_SERVERS=$3
 
# upload config info
 
$HOME/solr/bin/solr zk upconfig -n $CONFIG_NAME -d $CONFIG_PATH -z $ZK_SERVERS
 
echo $?
cs


~$ ./uploadSolrConfig $SOLR_HOME/SolrConfig TaggerHandlerConf localhost:2181,localhost:2182,localhost:2183 으로 실행시켜줍니다.

이후 solr admin 페이지를 들어가서 cloud>file>confg에 생성한 이름으로 conf file이 생성되었는지 확인합니다. 그리고 Collection탭에 들어가서 생성한 conf file을 이용하여 컬렉션을 생성해줍니다.(collectionName = TaggerHandler)




<indexing file>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<add>
    <doc>
        <field name="DOCID">NER1</field>
        <field name="NER_VALUE">햄버거</field>
        <field name="NER_KEY">메뉴</field>
    </doc>    
    <doc>
        <field name="DOCID">NER2</field>
        <field name="NER_VALUE">피자</field>
        <field name="NER_KEY">메뉴</field>
    </doc>
    <doc>
        <field name="DOCID">NER3</field>
        <field name="NER_VALUE">치킨</field>
        <field name="NER_KEY">메뉴</field>
    </doc>    
    <doc>
        <field name="DOCID">NER</field>
        <field name="NER_VALUE">파스타</field>
        <field name="NER_KEY">메뉴</field>
    </doc>
</add>
cs


그리고 색인할 파일을 만듭니다.


$SOLR_HOME/bin/post -c TaggerHandler $SOLR_HOME/menu.xml 로 색인해줍니다.


이제 모든 작업이 끝이 났고, 적당한 request를 보내서 결과를 확인하면 됩니다.


요청은 POST 방식이고, 요청헤더에 Content-Type text/plain 요청바디에 "햄버거, 피자,피자 주문할게요"라는 질의를 넣고 밑의 요청을 보내면 (url 테스트를 위하여 저는 간편한 postman이라는 툴을 사용하였습니다. curl을 사용해도 무관합니다.)


http://localhost:8983/solr/TaggerHandler/tag?overlaps=NO_SUB&tagsLimit=5000&fl=DOCID,NER_VALUE,NER_KEY&wt=json&intent=on


{ "responseHeader": { "status": 0, "QTime": 35 }, "tagsCount": 3, "tags": [ [ "startOffset", 0, "endOffset", 3, "ids", [ "NER1" ] ], [ "startOffset", 5, "endOffset", 7, "ids", [ "NER2" ] ], [ "startOffset", 8, "endOffset", 10, "ids", [ "NER2" ] ] ], "response": { "numFound": 2, "start": 0, "docs": [ { "DOCID": "NER1", "NER_VALUE": "햄버거", "NER_KEY": "메뉴" }, { "DOCID": "NER2", "NER_VALUE": "피자", "NER_KEY": "메뉴" } ] } }


=>이런 결과가 나옵니다!!!