엔지니어링

Elasticsearch Machine Learning을 이용한 다국어 로그 분류

편집자 노트(2021년 8월 3일): 이 포스팅은 더 이상 사용되지 않고 앞으로는 사라지게 될 기능을 사용합니다. 현재 지침은 리버스 지오코딩을 통한 사용자 정의 리전 매핑 설명서를 참조하세요.

Elastic Stack의 머신 러닝(ML)에는 로그 메시지를 분류하고 분류된 메시지에서 이상점(anomaly)을 찾는 능력이 있습니다. 그러나 버전 6.2 이전에는 문제가 있었습니다. 모든 로그 메시지가 영어로만 되어 있다고 가정한 것입니다.

버전 6.2 이전에는 영어가 아닌 글자들은 분류 절차에서 완전히 무시되었습니다. 이는 로그 메시지에 영어 단어가 없다면 모든 메시지를 같은 범주로 간주한다는 뚯입니다. 전체적으로 영어가 아닌 로그에 약간의 영어 단어가 섞여 있다면, 이들 소수의 영어 단어가 분류를 좌지우지하여 매우 이상한 결과를 낳게 됩니다.

버전 6.2는 이 문제를 해결하기 위한 첫걸음입니다. 분류 전에 메시지를 토큰으로 나누는 디폴트 토크나이저(tokenizer)는 이제 영어뿐만 아니라 모든 글자로 이루어진 단어를 나눌 수 있습니다.

예를 들어, 아래 두 로그 메시지를 살펴보면,

  1. Directory change success
  2. 디렉토리 변경 성공

6.2 이전의 분류 토크나이저는 이들 메시지를 아래와 같이 토큰화했을 것입니다.

  1. Directory change success
  2. (아, 여기에는 아무 토큰도 없습니다. )

이 한국어 메시지에는 라틴 알파벳 문자가 없기 때문에 토큰이 전혀 만들어지지 않았습니다!

버전 6.2부터는 디폴트 분류 토크나이저가 아래와 같이 토큰화할 것입니다.

  1. Directory change success
  2. 디렉토리 변경 성공

이전에는 한국어 로그 메시지의 경우 분류 알고리즘이 작업할 내용이 남아 있지 않았지만, 이제 세 토큰으로 적절히 나뉘었습니다.

그러나 때로는 분류 분석기(categorization_analyzer)를 조금 더 커스터마이즈해야 할 때가 있습니다. 간단한 분류 작업(categorization job)은 아래와 같이 정의될 수 있습니다.

{
  "analysis_config" : {
    "categorization_field_name" : "message", 
    "bucket_span" : "30m",
    "detectors" : [{
      "function" : "count",
      "by_field_name" : "mlcategory", 
      "detector_description" : "Unusual message counts"
    }]
  },
  "data_description" : {
    "time_field" : "timestamp"
  }
}

버전 6.2부터는 아래와 같이 (디폴트 categorization_analyzer를 자세하게 설명해서) 적을 수도 있습니다.

{
  "analysis_config" : {
    "categorization_field_name" : "message", 
    "bucket_span" : "30m",
    "detectors" : [{
      "function" : "count",
      "by_field_name" : "mlcategory", 
      "detector_description" : "Unusual message counts"
    }],
    "categorization_analyzer" : {
      "tokenizer" : "ml_classic", 
      "filter" : [
        { "type" : "stop", "stopwords" : [
          "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday",
          "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun",
          "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December",
          "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
          "GMT", "UTC"
        ] }
  },
  "data_description" : {
    "time_field" : "timestamp"
  }
}

이를 보면 디폴트 categorization_analyzerml_classic이라는 토크나이저와 요일과 월 이름(약어 포함)을 제거하는 stop 필터로 이루어졌음을 알 수 있습니다.

한국어 로그 메시지에 요일이나 월 이름이 단어로 포함되어 있다고 가정할 때 categorization_analyzer를 다음과 같이 개선할 수도 있을 것입니다. (로그 파일에 날짜가 숫자로만 적혀 있다면 이렇게 바꿔도 결과에 차이가 없을 것이므로 굳이 이런 노력을 할 필요가 없습니다.)

{
  "analysis_config" : {
    "categorization_field_name" : "메시지", 
    "bucket_span" : "30m",
    "detectors" : [{
      "function" : "count",
      "by_field_name" : "mlcategory", 
      "detector_description" : "비정상적 메시지의 개수"
    }],
    "categorization_analyzer" : {
      "tokenizer" : "ml_classic", 
      "filter" : [
        { "type" : "stop", "stopwords" : [
          "월요일", "화요일", "수요일", "목요일", "금요일", "토요일", "일요일",
          "일월", "이월", "삼월", "사월", "오월", "유월", "칠월", "팔월", "구월", "시월", "십일월", "십이월",
          "KST"
        ] }
  },
  "data_description" : {
    "time_field" : "타임스탬프"
  }
}

categorization_analyzer의 토큰 필터뿐만 아니라 토크나이저 자체도 커스터마이즈할 수 있습니다. 영어 로그 메시지의 경우 ml_classic는 버전 6.1이하 버전의 하드코드된 토크나이저가 했던 작업을 합니다. 그렇지 않으면 버전 6.2로 업그레이드했을 때 ML 작업에 이전 버전과의 호환성 문제가 생길 것입니다.

ml_classic 토크나이저와 요일/월 이름 불용어(stopword) 필터는 내장된 Elasticsearch 토크나이저토큰 필터로만 이루어진 아래 분석기와 거의 비슷합니다.

    "categorization_analyzer" : {
      "tokenizer" : {
        "type" : "simple_pattern_split",
        "pattern" : "[^-0-9A-Za-z_.]+"
      },
      "filter" : [
        { "type" : "pattern_replace", "pattern" : "^[0-9].*" },
        { "type" : "pattern_replace", "pattern" : "^[-0-9A-Fa-f.]+$" },
        { "type" : "pattern_replace", "pattern" : "^[^0-9A-Za-z]+" }, 
        { "type" : "pattern_replace", "pattern" : "[^0-9A-Za-z]+$" }, 
        { "type" : "stop", "stopwords" : [
          "",
          "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday",
          "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun",
          "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December",
          "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
          "GMT", "UTC"
        ] }
      ]
    }

(“거의”라고 말한 까닭은 버전 6.2에서는 토큰에 위의 패턴에 따른 라틴 알파벳뿐만 아니라 모든 알파벳이 포함되기 때문입니다.)

이런 토큰화 전략은 단어 사이에 빈칸을 넣지 않는 중국어나 일본어 같은 언어에는 분명히 알맞지 않을 것입니다. 이들 언어를 적절히 토큰화하려면, 이런 문자열을 단어로 나누는 방법을 알고 있는 ICU tokenizer같은 토크나이저로 바꿔야 할 것입니다.

{
  "analysis_config" : {
    "categorization_field_name" : "message", 
    "bucket_span" : "30m",
    "detectors" :[{
      "function" : "count",
      "by_field_name" : "mlcategory",
      "detector_description" : "异常的消息数量"
    }],
    "categorization_analyzer" : {
      "tokenizer" : "icu_tokenizer",
      "filter" : [
        { "type" : "stop", "stopwords" : [
          "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日",
          "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月",
          "CST"
        ] }
  },
  "data_description" : {
    "time_field" : "timestamp"
  }
}

categorization_analyzer를 커스터마이즈할 때는 아래 두 가지에 주의해야 합니다.

  • 소문자로 전환(lowercasing), 형태소 분석(stemming), 분해(decompounding) 등의 기법은 검색에는 유용하지만, 기계가 만들어낸 로그 메시지를 분류할 때에는 적합하지 않습니다. 예를 들어, 형태소 분석을 하고 나면 “service starting”과 “service started”를 구별할 수 없게 됩니다. 사람은 보통 같은 내용을 적을 때 조금씩 다르게 쓰는 경향이 있기 때문에 사람이 작성한 텍스트에는 형태소 분석이 적합합니다. 그러나 미리 작성된 프로그램으로부터 기계가 만들어내는 로그 메시지에서는, 서로 다른 단어는 서로 다른 메시지를 뜻합니다.
  • categorization_analyzer가 만들어내는 토큰들은 인덱싱 때 사용된 분석기가 만들어내는 토큰들과 비슷해야 검색할 때 원래의 메시지를 찾아낼 수 있습니다. 그래야 분류 정의로부터 원래의 데이터로 드릴다운할 수 있습니다.

앞서 버전 6.2는 다국어 로그 메시지 분류 문제를 해결하기 위한 첫걸음이라고 말했습니다. 그럼 다음 단계는 무엇일까요? 바로 토큰의 가중치를 정하는 데 사용하는 사전을 개방하는 것입니다. 영어 분류에서 우리는 메시지가 속하는 범주를 결정할 때 사전에 나오는 단어에 더 높은 가중치를 부여하고, 그 중 동사에 보다 더 높은 가중치를 부여합니다. 이러한 규칙이 적용된 영어 사전은 현재 수정이 불가능하기 때문에, 영어가 아닌 로그에서는 모든 토큰에 가중치 1이 부여될 것입니다. 대부분의 경우 적절히 분류될 것이지만, “machine learning service started”와 “machine learning service stopped”의 경우 영어에서는 서로 다른 범주로 간주되는 반면에, 다른 언어로 번역했을 때는 가중치가 없어 같은 범주로 분류될 수도 있습니다. 조만간 분류 사전을 자유롭게 커스터마이즈할 수 있도록 할 예정입니다.

이 블로그 포스트의 내용을 최신 버전에서 시험해 보시기 바랍니다. 그리고 categorization_analyzer를 정의할 때 도움이 필요하시면, Discuss 포럼에서 문의하시기 바랍니다.