본문 바로가기

공부방/Elasticsearch

텍스트 분석 - Text analysis

텍스트 분석 과정


데이터 시스템에 다음과 같은 문서들을 저장한다고 가정 해 보겠습니다.
 
 
 

일반적으로 오라클이나 MySQL 같은 관계형 DB에서는 위 내용을 보이는 대로 테이블 구조로 저장을 합니다.

 

RDBMS에서 데이터 조회

 

한 줄씩 검색 -> 조회 성능이 떨어집니다.

만약에 위 테이블에서 Text 에 fox가 포함된 행들을 가져온다고 하면 다음과 같이 Text 열을 한 줄씩 찾아 내려가면서 fox가 있으면 가져오고 없으면 넘어가는 식으로 데이터를 가져 올 것입니다.

 
 
테이블 데이터에서 한 줄씩 like 검색
 

Elasticsearch에서 데이터 조회

 

전통적인 RDBMS 에서는 위와 같이 like 검색을 사용하기 때문에 데이터가 늘어날수록 검색해야 할 대상이 늘어나 시간도 오래 걸리고, row 안의 내용을 모두 읽어야 하기 때문에 기본적으로 속도가 느립니다.

Elasticsearch는 데이터를 저장할 때 다음과 같이 역 인덱스(inverted index)라는 구조를 만들어 저장합니다.

 
역 인덱스(Inverted Index) 구조
 
이 역 인덱스는 책의 맨 뒤에 있는 주요 키워드에 대한 내용이 몇 페이지에 있는지 볼 수 있는 찾아보기 페이지에 비유할 수 있습니다.
Elasticsearch에서는 추출된 각 키워드를 텀(term) 이라고 부릅니다. 이렇게 역 인덱스가 있으면 fox를 포함하고 있는 도큐먼트들의 id를 바로 얻어올 수 있습니다.
 
RDBMS와는 달리 데이터가 늘어난다고 해도 검색 속도가 느려지지 않는다고 할 수 있습니다.
 
 

Elasticsearch analyzer


텍스트를 텀 단위로 나누는 것을 Text Analyzer 라고 합니다.

 

Elasticsearch는 문자열 필드가 저장될 때 데이터에서 검색어 토큰을 저장하기 위해 여러 단계의 처리 과정을 거칩니다.

이 전체 과정을 텍스트 분석(Text Analysis) 이라고 하고 이 과정을 처리하는 기능을 애널라이저(Analyzer) 라고 합니다.

 

Elasticsearch analyzer의 구성

 

  1. 캐릭터 필터(Character Filter) : 0~3개
    1. 특정 단어를 다른 단어로 변경
    2. 태그 치환 
  2. 토크나이저(Tokenizer) : 1개
    1. 주어진 문장을 Term 단위로 나눠줌
  3. 토큰 필터(Token Filter) : 0~n개
    1. 쪼개진 Term 들을 가공하는 역할

 

애널라이저 구성 : 캐릭터 필터 - 토크나이저 - 토큰필터
 

Character Filter (캐릭터 필터)

 

텍스트 데이터가 입력되면 가장 먼저 필요에 따라 전체 문장에서 특정 문자를 대치하거나 제거하는 기능입니다.
뒤에서 자세히 설명하고 지금 설명 할 예제에서는 적용하지 않겠습니다.

 

Tokenizer (토크나이저)

 

다음으로는 문장에 속한 단어들을 텀 단위로 하나씩 분리 해 내는 처리 과정입니다.
토크나이저는 반드시 1개만 적용이 가능합니다.
다음은 whitespace 토크나이저를 이용해서 공백을 기준으로 텀 들을 분리 한 결과입니다.

 

whitespace 토크나이저 적용

 

Token filter (토큰 필터)

 

토큰 필터에는 여러 종류가 존재합니다.

 

 

Lowercase 토큰 필터

 

여기서는 먼저 lowercase 토큰 필터를 이용해서 대문자를 모두 소문자로 바꿔줍니다.
이렇게 하면 대소문자 구별 없이 검색이 가능하게 됩니다.

대소문자가 일치하게 되어 같은 텀이 된 토큰들은 모두 하나로 병합이 됩니다.

 

소문자로 변경 후 같은 텀 병합
 
 

불용어 제거  토큰 필터

 

검색으로 가치가 없는 단어 불용어 (stopword) 제거

stop 토큰 필터를 이용해서 불용어를 제거합니다. (stopwords를 통해 불용어 추가해서 저장이 안되도록 함)

보통 a, an, are, at, be, but, by, do, for, i, no, the, to … 등의 단어들은 불용어로 간주되어 검색어 토큰에서 제외됩니다.

stop토큰 필터를 적용하면 우리가 만드는 역 인덱스에서 the가 제거됩니다.

 

 

형태소 분석 토큰 필터

 

이제 형태소 분석 과정을 거쳐서 문법상 변형된 단어를 일반적으로 검색에 쓰이는 기본 형태로 변환하여 검색이 가능하게 합니다. 영어에서는 형태소 분석을 위해 snowball 토큰 필터를 주로 사용하는데 이 필터는 ~s, ~ing 등을 제거합니다. 그리고 happy, lazy 와 같은 단어들은 happiness, laziness와 같은 형태로도 사용되기 때문에 ~y~i 로 변경합니다. snowball 토큰 필터를 적용하고 나면 jumpsjumping은 모두 jump로 변경되고, 동일하게 jump 로 되었기 때문에 하나의 텀으로 병합됩니다.

 

snowball 형태소 분석 적용 후 텀 병합

 

동의어 토큰 필터

 

quick으로 검색해도 fast를 찾을 수 있게

fast로 검색해도 quick 으로 검색할 수 있게

 

필요에 따라서는 동의어를 추가 해 주기도 합니다. synonym 토큰 필터를 사용하여 quick 텀에 동의어로 fast를 지정하면 fast 로 검색했을 때도 같은 의미인 quick 을 포함하는 도큐먼트가 검색되도록 할 수 있습니다. AWS 와 Amazon 을 동의어로 놓아 amazon을 검색해도 AWS 를 찾을 수 있게 하는 등 실제로도 사용되는 사례가 많습니다.

 

 

Analyzer 테스트


Elasticsearch 에서는 분석된 문장을 _analyze API를 이용해서 확인할 수 있습니다.

토크나이저는 tokenizer, 토큰 필터는 filter 항목의 값으로 입력하면 됩니다.

토크나이저는 하나만 적용되기 때문에 바로 입력하고, 토큰필터는 여러개를 적용할 수 있기 때문에 [ ] 안에 배열 형식으로 입력합니다.

 

문장: "The quick brown fox jumps over the lazy dog"

토크나이저 : whitespace 

토큰 필터 : lowercase, stop, snowball 

다음과 같은 결과를 확인할 수 있습니다.

 

토큰 필터는 순서가 중요합니다.

토큰 필터의 순서에 따라서 결과값이 달라질 수 있습니다.

 

GET _analyze
{
  "text": "The quick brown fox jumps over the lazy dog",
  "tokenizer": "whitespace",
  "filter": [
    "lowercase",
    "stop",
    "snowball"
  ]
}

 

실제 데이터를 이용한 예제


my_index2에 message 필드에 snowball 아날라이저를 적용합니다.

 

PUT my_index2
{
  "mappings": {
    "properties": {
      "message": {
        "type": "text",
        "analyzer": "snowball"
      }
    }
  }
}

 

message를 다음과 같이 저장합니다.

Jumps 라는 단어에 주목해봅시다.

 

PUT my_index2/_doc/1
{
  "message": "The quick brown fox jumps over the lazy dog"
}

 

jumping을 검색하면 Jumps가 들어가 있는 문장을 검색할 수 있습니다.

 

 

Jumps, Jumping, Jump 모두 검색 가능한 것을 확인할 수 잇습니다.

 

GET my_index2/_search
{
  "query": {
    "match": {
      "message": "jumping"
    }
  }
}

 

Term query 사용 (Inverted index에 저장된 내용과 동일한 것만 검색)

 

정확한 단어를 이용하여 검색을 하고싶다면 term query를 사용하면 된다.

jump로 하면 찾아지지만

jumps는 찾아지지 않는다.

jumps는 저장된 원문과 동일하지만 Inverted index에 jump로 저장이 되기 때문에 검색이 되지 않는다.

 

GET my_index2/_search
{
  "query": {
    "term": {
      "message": "jump"
    }
  }
}

 

 

Custom Analyzer (사용자 정의 아날라이저)


사용자가 직접 analyzer를 정의할 수 있습니다.

토크나이저와 필터를 직접 구성하여 만들어보겠습니다.

my_index3에 my_custom_analyzer를 만드는 코드입니다.

 

 

PUT my_index3
{
  "settings": {
    "index": {
      "analysis": {
        "analyzer": {
          "my_custom_analyzer": {
            "type": "custom",
            "tokenizer": "whitespace",
            "filter": [
              "lowercase",
              "stop",
              "snowball"
            ]
          }
        }
      }
    }
  }
}

 

my_index3 에서는 custom analyzer를 사용할 수 있게 됩니다.

 

GET my_index3/_analyze
{
  "analyzer": "my_custom_analyzer",
  "text": [
    "The quick brown fox jumps over the lazy dog"
  ]
}

 

Custom tokenfilter (사용자 정의 토큰필터)


토크나이저, 토큰필터의 경우에도 옵션을 지정하는 경우에는 사용자 정의 토크나이저 토큰필터로 만들어 추가해야 합니다.

다음은 stop 토큰필터에 "brown"을 불용어로 적용한 my_stop_filter 사용자 정의 토큰필터를 생성하고 이것을 my_custom_analyzer에서 사용하도록 설정 한 예제입니다.

 

 

이제 다시 my_custom_analyzer를 사용해서 텍스트를 분석 해 보면 brown이 불용어 처리가 되어 사라진 것을 확인할 수 있습니다.

 

GET my_index3/_analyze
{
  "analyzer": "my_custom_analyzer",
  "text": [
    "The quick brown fox jumps over the lazy dog"
  ]
}

 

매핑에 사용자 정의 애널라이저 적용


애널라이저를 실제 인덱스에 입력할 데이터에 적용하려면 settings 부분에서 만든 애널라이저를 mappings 의 text 필드별로 지정합니다. 앞에서 만든 my_custom_analyzer 를 message 필드에 적용하는 방법은 다음과 같습니다. setting 부분은 위 예제와 동일합니다.

 

PUT my_index3
{
  "settings": {
    "index": {
      "analysis": {
        "analyzer": {
          "my_custom_analyzer": {
            "type": "custom",
            "tokenizer": "whitespace",
            "filter": [
              "lowercase",
              "my_stop_filter",
              "snowball"
            ]
          }
        },
        "filter": {
          "my_stop_filter": {
            "type": "stop",
            "stopwords": [
              "brown"
            ]
          }
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "message": {
        "type": "text",
        "analyzer": "my_custom_analyzer"
      }
    }
  }
}

 

이제 my_indexmessage 필드에 입력되는 값은 위에 지정된 my_custom_analyzer 애널라이저가 적용됩니다. my_index의 message 필드에 값을 입력하고 검색 해 보면 brown은 불용어 처리가 되어 검색되지 않는 것을 확인할 수 있습니다.

 

PUT my_index3/_doc/1
{
  "message": "The quick brown fox jumps over the lazy dog"
}

 

brown은 검색 term으로 저장이 안되어 있기 떄문에 검색이 불가능합니다.

 

GET my_index3/_search
{
  "query": {
    "match": {
      "message": "brown"
    }
  }
}

 

 

 

'공부방 > Elasticsearch' 카테고리의 다른 글

매핑 Mapping  (0) 2023.05.11
노리 (nori) 한글 형태소 분석기  (0) 2023.05.11
범위 쿼리 (Range Query)  (0) 2023.05.10
정확값 쿼리 (Exact Value Query)  (0) 2023.05.10
복합 쿼리 - Bool Query  (0) 2023.05.10