쿼리는 어떤 과정을 거쳐서 실행되는가

쿼리는 사용자와 데이터베이스를 매개하는 역할을 수행합니다. 응용프로그램을 개발할 때 파일 입출력을 직접 다루는 대신 데이터베이스를 사용하는 이유는 의도하는 데이터 처리 결과를 간단하게 얻을 수 있기 때문인데요. 쿼리를 사용하면 프로그램 코드를 작성하는 것에 비해 같은 작업을 훨씬 짧게 표현할 수 있습니다.

예를 들어 웹 로그에서 클라이언트 IP별 다운로드 트래픽 총량을 계산하려면 아래와 같은 과정을 거쳐야 합니다. 웹 로그는 아래와 같은 형식으로 기록됩니다.

110.70.47.162 - - [23/May/2020:13:24:22 +0900] "GET /static/images/favicon.png HTTP/1.1" 200 57692 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 13_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/81.0.4044.124 Mobile/15E148 Safari/604.1" 849
  1. 웹 로그 파일을 연다.
  2. IP와 정수로 구성된 해시테이블을 초기화한다.
  3. 웹 로그 파일을 개행 문자로 분리해서 행 단위로 읽어들인다.
    1. 웹 로그에서 클라이언트 IP와 전송량에 해당되는 문자열을 추출한다.
    2. 전송량 문자열을 정수로 변환한다.
    3. 해시테이블에서 클라이언트 IP를 조회한다.
    4. 기존 누적값과 현재 전송량을 합산하여 클라이언트 IP와 전송량의 쌍을 다시 해시테이블에 넣는다.
  4. 파일 끝을 만나면 해시테이블의 모든 클라이언트 IP 키를 순회하면서 결과를 출력한다.
  5. 웹 로그 파일을 닫는다.

프로그래밍 언어에 따라 다르지만 파이썬으로는 몇 줄, 자바로는 수십 줄 정도 구현이 필요할 것이고, 클라이언트 IP가 엄청나게 많아서 메모리에 다 올릴 수 없는 상황은 고려하지 않고 있습니다.

만약 일반적인 데이터베이스 테이블에 웹 로그가 파싱되어 저장되어 있는 상태라면 일반적으로 SQL을 이용해서 간단하게 1줄로 쿼리할 수 있습니다.

SELECT client_ip, SUM(bytes) FROM weblog GROUP BY client_ip

weblog 테이블에 데이터가 정규화된 형태로 존재한다면 로그프레소 쿼리는 아래와 같이 표현합니다.

table weblog | stats sum(bytes) by client_ip

일반 데이터베이스는 테이블에 적재되지 않은 데이터를 처리하지 못하지만, 로그프레소는 아래와 같이 추출과 형 변환을 포함하여 모든 데이터 처리를 한 줄로 표현할 수 있습니다.

textfile access_log | rex field=line "^(?<client_ip>\S+).* (?<bytes>\S+)$" | eval bytes=long(bytes) | stats sum(bytes) by client_ip

쿼리 실행 단계

데이터베이스는 위와 같이 기술한 쿼리 문장을 실제 실행 가능한 코드로 변환해야 합니다. 이 과정은 크게 쿼리 파싱, 최적화, 실행으로 구분합니다.

쿼리 파싱

Operator Tree

쿼리는 한 줄로 간단하게 표현하였지만 프로그램 코드를 직접 작성한 것과 동일하게 동작하려면 각 기능 단위가 사용자의 의도에 맞게 배치되어야 합니다. 데이터베이스는 이러한 기능 단위를 쿼리 연산자 (Query Operator) 라고 부릅니다. 각 쿼리 연산자는 레코드를 입력 받아서 고유의 데이터 처리를 수행한 후 출력하며, 다음 연산자는 이전 연산자의 출력을 입력으로 받아들입니다.

위의 그림에서 실행 흐름은 아래에서 위로 올라가는 방향으로 표현되어 있는데 이것이 약간 부자연스럽게 느껴질 수 있습니다. 하지만 조인 연산이 포함되는 경우에 하단이 늘어나면서 넓게 배치되고 결과는 하나로 모이게 되므로 트리 형태로 표현하는 것이 유리하고, 그에 맞춰서 실행 흐름은 아래에서 위로 표시하는 것입니다.

최적화

데이터베이스는 쿼리를 파싱하여 트리 형태로 만들고 난 후에 이를 논리적, 물리적으로 최적화하는 과정을 수행합니다. 예를 들어, 위의 경우에 집계를 수행하므로 테이블 혹은 파일의 레코드 순서는 중요하지 않습니다. 따라서 병렬화된 테이블 스캔 혹은 파일 스캔으로 변환할 수 있습니다. 이 외에도 타입 추론, 비용 계산, 조인 순서, 필터링 위치 결정은 쿼리 최적화에서 중요한 주제인데 이는 나중에 다시 다루도록 하겠습니다.

Query Optimizer

실행

최적화를 거치고 나면 최종적으로 쿼리 실행 계획 (Query Plan) 이 완성됩니다. 데이터베이스는 이 쿼리를 실행하는데 어느 정도의 CPU, 메모리, 디스크 자원을 사용할지 결정해야 합니다. 가장 단순하게는 프로그램을 직접 구현해서 실행하듯이 단일 프로세스가 쿼리를 실행할 수 있습니다.

프로세스 모델

PostgreSQL처럼 역사가 오래된 데이터베이스는 프로세스 모델 기반으로 동작합니다.

Process Model

프로세스 모델은 클라이언트가 접속하면 새로운 프로세스를 생성해서 할당합니다. 유닉스 프로그래밍 스타일의 오랜 전통이기도 하지만, 공유 메모리를 기반으로 프로세스를 분리하는 이런 실행 모델은 프로그램 오류로 크래시가 발생하더라도 해당 프로세스만 영향을 받기 때문에 상대적으로 장애에 견고합니다. 프로세스가 재시작하더라도 디스크에서 다시 읽어들일 필요 없이 공유 메모리 영역이 그대로 유지되는 장점도 있습니다. IBM DB2, Oracle (11g 이하 버전) 데이터베이스는 이러한 아키텍처로 설계되어 있습니다.

그러나 프로세스를 fork 하는 방식은 상당히 무거운 편입니다. 프로세스 풀링을 통해 어느 정도 단점을 희석시키기는 하지만, 운영체제에 실행을 맡기므로 스케줄링을 세밀하게 관리하기 어렵고 CPU 캐시 활용도 비효율적입니다.

스레드 모델

로그프레소 쿼리 엔진은 스레드 모델 기반으로 동작합니다.

Thread Model

로그프레소는 쿼리마다 하나의 쿼리 스케줄러 스레드를 실행합니다. 쿼리 스케줄러는 쿼리 실행 계획에서 의존성이 해소된 실행 가능한 태스크를 선별하여 쿼리 태스크를 병렬적으로 실행합니다. 스캔이나 정렬과 같이 실행 성능에 큰 영향을 미치는 요소는 별도의 스레드 풀이 할당되어 있어서, CPU를 최대한 활용하여 빅데이터 쿼리를 실행합니다. 로그프레소 쿼리 엔진은 푸시 모델로 구성되어 있기 때문에, 스캔 스레드의 병렬화가 전체 쿼리의 수행 성능을 좌우합니다. 메모리에 캐시된 페이지를 처리하는 경우 이러한 병렬화는 수 배 이상의 성능 차이를 낼 수 있습니다.

다음 편에서는 Pull 모델과 Push 모델의 장단점을 알아보도록 하겠습니다.

레퍼런스

둘러보기

더보기

소나 4.0.2308 출시 - 세대 교체 및 편의성 향상

[로그프레소 소나 4.0.2308.0 버전](https://docs.logpresso.com/ko/sonar/4.0/releases/4.0.2308.0)이 출시되었습니다. 이번 릴리스는 약 140가지의 변경사항을 포함하고 있으며, 로그프레소 엔터프라이즈 제품군 단종 이후 전면적인 세대 교체와 운영 편의성 향상을 목표로 합니다. 이전에 엔터프라이즈에서만 사용 가능하던 기능들은 최대한 빠른 시일 내에 소나 제품군에서 지원할 예정입니다. ## 통합 빌드 로그프레소 스탠다드, 엔터프라이즈, 소나, 마에스트로 4종의 모델에 따라 별도의 패키지를 구분하여 설치하고 관리하는 것은 그 복잡성으로 인해 운영자와 엔지니어에게 어려움을 가중시키는 문제가 있었습니다. 이에 스탠다드 및 엔터프라이즈는 단종시키고, 단일 서버 패키지로 3종의 모델을 지원하도록 개선했습니다: - Logpresso Sonar Light: 통합로그 관리 - Logpresso Sonar: 통합보안관제 - Logpresso Maestro: 보안운영 자동화 이에 따라 4.0.2308.0 버전부터는 라이선스 설치만으로 메뉴 구성과 기능 집합이 즉각적으로 변경됩니다. ## 수집 설정 단순화 이전 버전에서는 수집 모델, 추출 모델, 원본 로그 파서, 정규화 로그 파서, 로그 스키마가 모두 구분되어 있었습니다. 이 때문에 새 유형의 수집기를 정의하려면 5개 메뉴를 오가며 설정해야 하는 어려움이 있었습니다. ![새 수집 모델 편집 화면](/media/ko/2023-08-28-whats-new-sonar-4-0-2308-0/logger-model.png) 원본 로그 파서와 정규화 로그 파서는 하나의 파서 메뉴로 통합하고, 추출 모델 메뉴는 수집 모델 설정 화면에서 탭으로 즉시 편집할 수 있도록 하였습니다. 이제 파서를 생성하고, 파싱 결과를 확인하면서 로그 스키마를 정의한 후, 수집 모델을 편집하는 이전보다 훨씬 간결한 흐름으로 새 수집 유형을 정의할 수 있게 되었습니다. ## 앱 기능 향상 통합로그관리 또는 통합보안관제 솔루션을 구축할 때 통상 많은 기간이 수집 설정과 필드 정규화에서 소요됩니다. 모든 필드를 정확하게 파싱하면서도 성능 문제가 없는 정규표현식을 작성하거나, 필드 이름의 공통 표준화를 수행하고, 드릴다운 분석 가능한 대시보드를 설정하기까지 수많은 설정과 검증을 거쳐야 합니다. 이전의 엔터프라이즈 4.0은 최소한의 설정으로 대시보드 시각화까지 구성하는 기능을 선보였습니다. 하지만 지금까지 소나는 필요에 따라 확장된 쿼리 명령어, 파서, 수집기를 사용할 수 있는 기능만을 앱으로 지원해왔습니다. 이번 소나 릴리스부터는 파서, 로그 스키마, 수집 모델, 데이터셋, 위젯, 대시보드 객체를 앱에 내장하여 지원합니다. 즉, 소나에서도 이제 앱을 설치하고 수집기를 추가하기만 하면 즉시 대시보드를 사용할 수 있다는 의미입니다. [로그프레소 스토어](https://logpresso.store/ko/apps)에서 제공하는 60종의 앱을 설치하기만 하면 이제 소나에서도 설치 당일 운영을 시작할 수 있습니다. 다만 아직까지는 plugin 디렉터리에 앱을 설치해야 합니다. 다음 릴리스에서는 웹 브라우저에서 클러스터 전체에 앱을 설치 및 관리할 수 있는 기능을 지원할 예정이니 조금만 더 기다려주세요. ## 매니지드 시큐리티 서비스 지원 [LG CNS와 매니지드 탐지 및 대응(MDR)](https://zdnet.co.kr/view/?no=20230421085339) 협업을 진행하면서 다수의 고객사를 효율적으로 관리할 수 있는 프레임워크를 구현하였습니다. “사이트” 개념이 새로 추가되었으며, 자산 IP와 수집기에 사이트를 매핑할 수 있습니다. 로그 수집 시점에 사이트 이름이 태깅되고, 시나리오 탐지 시 티켓에 사이트가 매핑되며, 티켓 목록에서 사이트 단위로 검색 및 대응을 수행할 수 있습니다. ![티켓 목록의 사이트 열 활성화](/media/ko/2023-08-28-whats-new-sonar-4-0-2308-0/site.png) 이 뿐 아니라, 티켓에 태그를 추가할 수 있도록 하여 위협 분류 및 대응 상태 가시성을 향상시켰습니다. ## 성능 모니터 시스템 담당자는 누구나 안정적인 시스템 운영을 목표로 합니다. 그러나 이전의 성능 모니터링은 초심자가 직관적으로 장애 상황을 인지하기 어려웠습니다. 이번 릴리스는 성능 모니터 메뉴를 추가하여 대규모 클러스터에서 장애나 로그 유실 여부를 직관적으로 파악할 수 있도록 개선하였습니다. 예를 들어 아래는 장애 발생 시의 그래프 예입니다. ![로그프레소 성능 모니터 장애 예시](/media/ko/2023-08-28-whats-new-sonar-4-0-2308-0/perfmon-usage.png) 수집 추이는 노란색으로 지난 주 추이를 출력합니다. 1번의 현재 수집 추이와 지난 주 추이를 비교하기만 해도 직관적으로 장애 여부를 파악할 수 있습니다. 특히 2번의 힙 메모리 추이 패턴과 3번의 GC 추이를 보면 현재 힙 메모리 고갈로 인한 장애가 발생하고 있다는 사실을 즉시 파악할 수 있습니다. 이 뿐만 아니라, 네트워크 인터페이스 카드의 패킷 유실, 커널 스택에서의 패킷 유실, 로그프레소 데몬에서의 패킷 유실을 한 눈에 확인할 수 있도록 표시하여, 시스템 운영자가 현재 클러스터 상태가 정상인지 확신을 가질 수 있도록 개선하였습니다. ### REST API 이번 릴리스부터 100가지 이상의 REST API가 지원됩니다. 계정에서 API 키 발급 버튼을 클릭하여 새 API 키를 생성하고 사용할 수 있습니다. 타 SOAR 솔루션 또는 보안포털에서 로그프레소를 연동하려는 경우, [로그프레소 REST API 레퍼런스](https://docs.logpresso.com/ko/sonar/4.0/api)를 참고하여 즉시 연동하실 수 있습니다. ![로그프레소 소나 API 키 발급](/media/ko/2023-08-28-whats-new-sonar-4-0-2308-0/user-api-key.png) 프로그램을 작성하기 전이라도 웹 브라우저에서 API를 즉시 테스트 할 수 있습니다. https://hostname/api/ 경로로 이동하면 사용 가능한 API 그룹이 표시되고, 각 API 그룹을 클릭하면 아래와 같이 API 테스트 화면이 제공됩니다. ![로그프레소 REST API 테스트 화면](/media/ko/2023-08-28-whats-new-sonar-4-0-2308-0/api-test.png) ## 맺음말 이 외에도 매우 많은 기능들이 개선되었습니다. [4.0.2308 릴리스 노트](https://docs.logpresso.com/ko/sonar/4.0/releases/4.0.2308.0)에서 세부사항을 확인해보세요!

2023-08-28

로깅 안티패턴 Top 10

2005년부터 2023년까지 수백 가지의 로그 포맷을 파싱하면서 많은 문제 사례를 접했는데, 어디에도 어떻게 로깅해야 하는가에 대한 좋은 가이드가 없었습니다. 이에 대표적으로 피해야 할 로깅 안티패턴 10선을 정리하였습니다. ## 안티 패턴 ### 1. 이스케이프 처리 누락 이스케이프 누락은 가장 흔하게 발견되는 사례입니다. 예를 들어 KEY=VALUE KEY2=VALUE2 와 같은 형식으로 로그 포맷을 정의했다면, VALUE에 공백이나 `=` 문자가 포함될 경우를 고려해야 합니다. KEY="VALUE" 와 같은 형식으로 정의했다면, VALUE에 큰 따옴표가 포함될 경우를 대비한 이스케이프 규칙이 필요합니다. CSV 형식으로 정의했으나 값에 쉼표가 포함된 경우에 대한 이스케이프가 없었던 사례도 있습니다. 이스케이프 규칙이 반영되지 않으면 수신 시스템은 휴리스틱한 처리를 할 수 밖에 없습니다. ### 2. 버전 헤더 누락 여러분의 솔루션이나 장비는 단 하나의 버전으로 통일되어 고객사에 배포되지 않습니다. 심하게는 한 고객사에서 동일 웹방화벽 제품에 대해 10종의 서로 다른 로그 포맷을 목격한 적도 있습니다. 로그 시작 부분에 버전이 명시되어 있지 않으면, 고객이 로그 파싱이 무언가 잘못된 것을 발견한 이후에야 대응하게 됩니다. 그 때 파서를 수정하려고 해도, 버전 헤더가 없으면 뭔가 로그의 특징적인 부분을 식별해서 파싱을 시도할 수 밖에 없습니다. 휴리스틱한 방법을 사용하여 어떻게든 버전 차이를 식별하더라도 여전히 로그 파싱이 100% 완벽하게 되리라 기대하기 어렵습니다. ### 3. 로그 타입 누락 하나의 솔루션이나 장비에서는 단 한 가지의 로그 유형만 나오지 않습니다. 당연히 여러 종류의 로그 유형들이 혼합되어 발생합니다. 하지만 로그 시작 위치에 로그 타입 정보가 없으면 이것도 버전 헤더 누락과 마찬가지로 무언가 특징적인 문자열을 식별하여 휴리스틱하게 파싱할 수 밖에 없습니다. ### 4. 호환성 고려 미비 로그 포맷을 업데이트 할 때는 자연히 필드 순서를 개발자가 보기 좋은 순서대로 다시 정렬하려는 유혹을 느끼게 됩니다. 하지만 외부 시스템을 고려한다면 새로운 필드는 반드시 끝부분에 추가해야 합니다. 만약 로그 중간에 새로운 필드를 삽입하게 되면, 외부 시스템은 버전별로 다른 포맷 파싱 규칙을 적용할 수 밖에 없습니다. 하위 호환성이 없는 로그에 버전 헤더까지 없다면 이것은 파싱에 매우 큰 어려움을 야기합니다. ### 5. 자연어 문장 사용 사람이 읽기 좋은 자연어 문장으로 로그를 기록하면, 프로그램으로 파싱하기가 극도로 어려워집니다. 대표적 예는 Cisco ASA 방화벽이나 네트워크 스위치의 로그들인데, 메시지 패턴별 정규식 수천 개를 추가해야 모든 경우의 수를 처리할 수 있게 됩니다. 대부분은 파싱을 포기하고 일부 필요한 로그만 파싱하여 활용하는 사태가 벌어집니다. ### 6. 고정 길이 형식 사용 정반대로 필드별 최대 길이가 정의된 고정 길이 로그 형식은 프로그램이 처리하기에 아주 좋은 것 아니냐고 생각할 수도 있겠습니다. 하지만 대부분의 풀텍스트 인덱스 엔진은 특수 문자를 기준으로 문자열을 분할하여 토큰을 추출하기 때문에, 의도하지 않은 값들이 하나의 토큰으로 인덱스되고 이 때문에 별도의 파싱이 없이는 검색 자체가 어려워집니다. 고정 길이로 인해 공백으로 비는 부분이 발생하므로 비효율적이기도 하며 이후 로그 포맷 업데이트에도 매우 불리합니다. ### 7. 코드 값 사용 애플리케이션에서는 로케일 처리를 위하여 ID 값을 사용하는 경우가 많습니다. 그렇지만 SIEM과 같은 외부 시스템은 ID에 대응하는 문자열 리터럴을 알 수가 없습니다. 물론 로그 포맷 정의서에 수백 개의 코드에 대한 명세를 작성하여 전달하기도 하지만, 장비나 솔루션이 업데이트 되면 새로 추가된 ID에 대한 정보는 역시 외부 시스템에서 즉각 알 수가 없으므로 기대한대로 파싱되지 않습니다. 코드 값 대신에 처음부터 영문 문자열을 사용하는 것이 변화에 대응하는 좋은 방법입니다. ### 8. 단위 변환 특히 바이트 값을 KB나 MB 단위로 기록하는 경우가 종종 있습니다. 아무래도 직관적으로 사람이 보기 편하기 때문에 이런 변환을 수행할 것입니다. 그러나 일부 값이 손실되어 정확하게 트래픽 총량을 계산할 수가 없을 뿐 아니라, 파싱 시점에 바이트 단위로 수치를 재변환하기 위해 별도의 로직을 작성해야 합니다. ### 9. 국제화 고려 미비 그동안 경험한 대부분의 로그의 타임스탬프 값에서 시간대를 보기 어려웠습니다. 여러분의 솔루션이나 장비가 같은 시간대에만 있다면 상관없다고 생각할 수도 있지만, 가령 AWS 인스턴스는 기본 시간대가 UTC에 맞춰져 있기 때문에 별도의 설정으로 타임존 보정을 하지 않으면 정확한 타임스탬프 값을 맞출 수가 없게 됩니다. 한편, 2023년에도 여전히 확장완성형으로 로그를 전송하는 사례가 있습니다. 요즘 세상에 UTF-8을 사용하지 않을 이유가 없습니다. ### 10. 길이 초과 잘림 JSON 형식의 로그를 SYSLOG over UDP로 전송하게 했는데 로그가 잘리면서 JSON 파서로는 파싱할 수 없게 되는 로그들도 있습니다. SYSLOG를 사용한다면 1000바이트 이내로 로그를 정리하는 것이 좋습니다. 이론적으로는 64K도 가능하지만 MTU를 넘는 로그를 UDP로 전송하면 패킷이 수십 개로 분할되고 이는 커널 스택에 도달하는 과정에서 쉽게 유실될 수 있습니다. SQL 등 감사 로그를 SYSLOG over UDP로 보내도록 설계한 시스템들도 종종 보게 됩니다. 백업, 복원 수행 시에 SQL 감사 로그는 간단히 메가바이트 단위로 넘어갑니다. 이렇게 되면 감사 로그 본연의 목적을 달성할 수 없게 됩니다. 방화벽처럼 대량의 로그가 발생하는 네트워크 장비가 아니라면 HTTPS로 REST API를 지원하는 방안을 권장합니다. 물론 최근 글로벌 벤더 방화벽은 대부분 HTTPS를 통한 로그 조회도 지원합니다. SYSLOG over UDP는 패킷 스니핑에 전송 내용이 그대로 노출되기 때문입니다. ## 결론 로그 포맷을 설계할 때 이미 잘 정의된 형식을 사용하는 것을 권장합니다. * JSON: 로그 길이가 그리 길지 않다면 가장 범용적인 JSON 포맷이 좋습니다. 대부분의 로그 통합 시스템은 JSON을 그대로 인식할 수 있습니다. * CSV: 반복적으로 키 문자열을 쓰는게 낭비로 느껴진다면, CSV가 대안입니다. 단, 버전, 타입 필드를 시작 위치에 포함하고 이스케이프에 유의하세요. * WELF: 키=값 형식을 선호한다면 WELF 포맷 규칙을 추천합니다. 단, WELF는 쓸만한 외부 라이브러리가 없으므로 직접 로깅 모듈을 작성하는 과정에서 예외 처리들을 세밀하게 검토해야 합니다.

2023-05-04

ETS 모델 기반 시계열 예측

미래를 예측하고 대비하는 일은 비즈니스의 가장 핵심적인 부분입니다. 전력 수요를 예측해야 발전소를 증설할 수 있고, 콜센터의 통화수를 예측해야 적정한 수의 상담원을 배치할 수 있습니다. 예측 모델은 여러가지 변수를 포함할 수 있지만, 과거의 이력을 기반으로 미래의 추세를 예측한다는 점에서는 공통적입니다. 가장 간단한 단순회귀분석부터, 다중회귀분석, 딥러닝에 이르기까지 여러가지 모델링 방법이 있지만, 여기에서는 시계열 데이터에 쉽게 적용할 수 있으면서도 좋은 결과를 보여주는 ETS 모델을 알아보도록 하겠습니다. ETS 모델은 업계에서 널리 사용되는 시계열 예측 모델로서, 지수평활법(Exponential Smoothing)을 기반으로 합니다. 지수평활법은 과거의 관측치에 시간의 흐름에 따른 가중치를 주고 합산하여 미래를 예측하는 방식입니다. 단순 지수평활법 (Single Exponential Smoothing) 에서 출발하여 하나씩 살펴보면 ETS 모델을 이해할 수 있습니다. ## 지수평활법: Exponential Smoothing 단순 지수평활법 (Single Exponential Smoothing)은 다음 예측치 (St)를 현재 값 (yt−1)과 이전 예측치(St−1)의 합산으로 계산합니다. 알파(α)는 0보다 크고 1보다 작은 스무딩 매개변수입니다: <blockquote>S<sub>t</sub> = α y<sub>t−1</sub> + (1−α) S<sub>t−1</sub></blockquote> 실제 이 수식이 어떻게 동작하는지 예제 값을 넣어서 직관적으로 이해해볼 수 있습니다. 아래의 예제는 엑셀로 수식을 만들어서 스무딩 매개변수의 조정에 따른 변화를 표현한 것입니다. α = 0.1 ![](/media/ko/2017-01-17-time-series/alpha_0.1.png) α = 0.5 ![](/media/ko/2017-01-17-time-series/alpha_0.5.png) α = 0.9 ![](/media/ko/2017-01-17-time-series/alpha_0.9.png) 매우 단순한 수식이지만 스무딩 매개변수에 따라 원본 그래프에 근접하게 변화하는 모습을 볼 수 있습니다. 최적의 스무딩 매개변수를 찾으면 해당 수식을 이용하여 미래의 값도 재귀적으로 예측할 수 있습니다. ![](/media/ko/2017-01-17-time-series/single_exp_graph.png) 13번 행부터는 관측치가 없기 때문에 y를 마지막 값으로 고정하고 계산하면 위와 같이 예측치가 계산됩니다. 즉, 시계열 예측이 스무딩 매개변수에 따른 모형의 에러를 최소화하는 최적화 문제로 변환된 것입니다. 그러나, 단순 지수평활법의 단점은 추세가 있는 경우 잘 모델링하지 못한다는 점입니다. 이중 지수평활법 (Double Exponential Smoothing) 은 이러한 단점을 보완합니다. 아래의 예제는 이미 각 모델에 대해 최적으로 선정된 스무딩 매개변수 값을 사용하여 계산된 결과를 보여줍니다. ![](/media/ko/2017-01-17-time-series/double_exp_graph.png) 이중 지수평활법은 두 개의 방정식을 사용합니다. <blockquote>S<sub>t</sub> = α y<sub>t</sub> + (1 − α) (S<sub>t−1</sub> + b<sub>t−1</sub>)<br><br>b<sub>t</sub> = γ (S<sub>t</sub> - S<sub>t-1</sub>) + (1 − γ) b<sub>t−1</sub><br><br>F<sub>t+m</sub> = S<sub>t</sub> + mb<sub>t</sub></blockquote> 첫번째 수식은 이전 St-1 값에 추세변화량을 더하여 기저를 생성합니다. 두번째 수식은 추세변화량을 보정하는 역할을 수행합니다. 예측치는 기저와 추세변화량을 합산한 값입니다. 위의 그래프를 통해 단순 지수평활법과 이중 지수평활법의 예측 특성 차이를 확인할 수 있습니다. 이렇게 이중 지수평활법은 추세를 반영하지만 여기에 더해서 계절성 (Seasonality)이 있는 경우를 잘 반영하지 못합니다. 이 때문에 삼중 지수평활법 (Triple Exponential Smoothing) 혹은 홀트-윈터스 모델 (Holt-Winters) 이라 불리는 방법이 제안되게 됩니다. ## ETS 모델 ETS 모델은 Error, Trend, Seasonality 3가지 요소로 구성된 모델을 의미합니다. 시계열 데이터는 추세의 특성과, 계절성을 각각 조합하여 다음과 같이 12가지의 유형을 상정할 수 있습니다. ![](/media/ko/2017-01-17-time-series/ets_model.png) 각 모델을 수식으로 표현하면 아래와 같습니다. 예를 들어, 에러, 추세, 계절성이 모두 가산적인 모델이라면 ETS(A,A,A) 혹은 ETS AAA 모델로 표기합니다. 계절성이 없는 가산적 모델이라면 ETS(A,A,N)에 해당됩니다. * N: 상수 모델 (Constant) * A: 가산적 모델 (Additive) * A<sub>d</sub>: 감쇄하는 가산적 모델 (Damped Additive) * M: 승법적 모델 (Multiplicative) * M<sub>d</sub>: 감쇄하는 승법적 모델 (Damped Multiplicative) ![](/media/ko/2017-01-17-time-series/ets_error_model.png) ## forecast 커맨드 로그프레소는 주어진 시계열 데이터에 대해 위와 같이 다양한 ETS 모델을 대상으로 에러를 최소화하는 시계열 모형을 자동으로 탐색합니다. 예측된 값은 _future 필드로 출력되며, 추세 (_trend), 상위 95% 신뢰구간 (_upper), 하위 95% 신뢰구간 (_lower) 필드를 동시에 출력합니다. ```query table order=asc passengers | forecast passengers ``` ![](/media/ko/2017-01-17-time-series/forecast.png)

2017-01-17