Introduction
Fig 1. 어플리케이션 화면 예시 |
위 화면과 같이 관심 지역을 읍면동 단위까지 선택하면 조회 당일의 모기 활동 지수를 예측하는 웹 어플리케이션 입니다.
구축된 ML 모델을 노트북 환경이 아닌 실제 어플리케이션에 적용하려면 어떤 작업이 필요할까에 대한 궁금증으로 개인적으로 진행한 프로젝트이며, 프로젝트 진행 내용은 대략적으로 다음과 같습니다.
-
오픈 데이터셋을 활용한 사이킷런 회귀 모델 구축
-
Flask 어플리케이션과 모델 연동 및 기본적인 html 페이지 구축
- 기상청 단기 예보 API 를 활용한 날씨 데이터 조회 기능 구현
- 지역 별 경위도와 기상청 API 좌표 체계 간 변환 함수 구현
- 기본적인 JavaScript 를 활용해 지역 선택 기능 구현
-
WSGI, NGINX 를 활용한 배포 서버 구축
- 도커를 활용한 프로젝트 컨테이너 생성
- Digital Ocean 클라우드 서버 배포
모기 활동 지수?
모기 활동 지수라는 개념이 생소하실텐데요, 기상청에서 운영 중인 모기 활동 예측 기술을 먼저 소개 드립니다.
Fig 1. 데이터 수집 경로 |
일반적인 인식과는 다르게 우리나라 또한 모기와 관련된 질병으로 부터 안전지역이라 할 수 없는데, 90년대 이후 북한 접경 지역에선 말라리아 환자가 지속적으로 발생하고 있고 인천국제공항 주변 지역 또한 해외여행객의 영향으로 지키바이러스, 뎅기열 등 매개감염병을 유발하는 많은 외래종 모기가 유입된 상황입니다.
이에 대해 수도권 기상청은 국민 건강 보호를 목적으로 2018년 모기 예측 기술 개발 사업을 추진했습니다. 수도권, 특히 인천광역시에 많이 분포한 수백 개 모기 관측망의 데이터를 기온, 토지이용 상황 등과 함께 분석해 랜덤 포레스트 모델을 구축했는데요. 주요 인사이트는 다음과 같습니다.
- 개채 수 증가는 7월 초, 9월 중순 2번의 정점을 이루는데, 이는 여름철 집중호우의 영향으로 추정.
- 일최저기온이 10도를 넘으면 모기개채수는 증가하기 시작함.
- 10월 최저기온이 급격히 낮아지면 모기개채수도 급감함.
- 도심과 농촌, 해안 간 우세하게 나타나는 모기의 종류에서 많은 차이를 보임.
- 서울시의 경우 기온이 1도 상승하면 모기 발생 위험이 약 10.8% 씩 증가.
Fig 2. 빨간집모기 개체 수와 기온의 시계열 분포 |
회귀 모델
프로젝트의 중점이 모델 서빙이기 때문에, 특별한 feature engineering 이나 model selection 과정 없이 간단한 회귀 모델로 모기 출현 지수를 예측하는 모델을 구축했습니다
(사실 활용한 데이터셋 자체가 서울시가 제공한 예측치의 시계열 자료이기에, 적당히만 세팅한다면 100% 에 가까운 정확도가 나올 것으로 생각됩니다).
우선 데이터셋은 1,342 건의 일 별 (1) 모기 지수, (2) 강수량, (3) 최저 기온, (4) 최대 기온으로 구성되어 있습니다.
mosquito_Indicator | rain(mm) | min_T(℃) | max_T(℃) | |
---|---|---|---|---|
count | 1342.000000 | 1342.000000 | 1342.000000 | 1342.000000 |
mean | 251.991803 | 3.539866 | 10.005663 | 19.096870 |
std | 295.871336 | 13.868106 | 11.109489 | 11.063394 |
min | 0.000000 | 0.000000 | -17.800000 | -10.700000 |
max | 1000.000000 | 144.500000 | 30.300000 | 39.600000 |
여기서 mosquito_indicator 로 표기된 모기 활동 지수는 특정한 범위 내에서 채집된 모기 개채수를 정규화시킨 지표라고 이해하시면 됩니다.
원본 데이터에는 이외에도 관측 날짜가 포함되어 있습니다. 이는 타깃 변수가 계절성을 가지기 때문에 핵심 feature 로 활용될 수 있지만, 모델링 과정을 최대한 간단하게 유지하기 위해 아래 내용에선 생략되었습니다.
Fig 3. 변수 별 Pair Plot |
변수 별 관계를 시각적으로 살펴봤을때, 최저/최고 기온이 모기 개채수와 전반적으로 큰 상관성을 나타내는 것을 확인할 수 있습니다. 강수량의 경우 이상치로 인해 관계성을 시각적으로 판단하기가 어려웠고, 따라서 아래와 같이 강수량의 로그 값과 모기 활동 지수 간의 관계를 월 별로 시각화 하였습니다.
Fig 4. 월 별 로그 강수량과 모기 활동 지수 |
생각과 달리 10월을 제외하면 모기 개채수와 강수량 간에는 큰 상관성이 관찰되지는 않았습니다. 저널에 기재된 것과 같이 7월 초, 9월 말 집중 호우로 개채수가 급감된다는 가설과 다소 상반되는 결과였는데, 하지만 월 별 모기 활동 지수의 분포에 차이가 있다는 점을 확인할 수 있었어요.
모델링은 사이킷런의 회귀 모델을 기반으로 진행했으며, Validation Set 에 대해 약 155 의 Mean Absolute Error 를 기록했습니다.
Flask 어플리케이션
학습이 완료된 모델은 별도 pkl 파일로 저장하여, flask 앱에서 바로 활용할 수 있도록 구성했습니다. 이후 유저의 위치 정보를 기상 정보로 변환하려고 보니 다음과 같은 데이터 변환 과정이 필요했습니다.
- 읍면동 까지의 위치 정보를 경위도 좌표로 변환
- 변환된 경위도 좌표를 기상청 격자정보로 변환
- 격자정보를 통해 기상청 API 에서 강수량, 기온 정보 가져오기
읍면동 위치에 따른 경위도 정보는 국내 모든 행정구역에 대한 데이터를 해당 블로그 에서 다운받아 사용했습니다. csv 형태로 서버에 저장 후, 필요시 pandas 데이터프레임으로 불러와 해당되는 경위도를 변수에 저장하게 되는데 추후 성능 개선을 위해선 데이터베이스 활용 또한 고려가 가능합니다.
기상청 API 에서 필요로 하는 위치 정보는 경위도가 아닌 별도의 격자정보이기 때문에 별도의 변환 로직을 구현하는 과정 또한 필요했습니다. 로직은 해당 블로그 를 참고했습니다.
Fig 4. 기상청 격자정보 - 위경도 변환 예시 |
이렇게 추출된 기상청 격자정보 등의 값을 기반으로 기상청 단기예보 API 에서 최신 기상 정보를 가져오는 파이프라인을 구축했습니다. 예측하고자 하는 날짜의 바로 전날 오후 11시에 발표된 예보값을 가져오게 되며, API 코드는 위 링크에 첨부되어 있는 API 가이드를 참고했습니다.
Fig 5. 기상청 단기 예보 API 출력값 예시 |
프론트엔드 구성 단계에선 브라우저 내에서 읍면동 단위 까지의 위치 정보를 기입할 수 있도록 구성하는 작업이 필요했는데, html 페이지 내에서 선택된 “시도” 값에 따라 “시군구” 의 선택값 등을 변동하는 과정에서 약간의 jQuery를 활용했습니다 (참고 블로그).
WSGI + NGINX + 도커
가능한 배포 환경에 가까운 경험을 위해서 flask 앱을 WSGI 서버인 gunicorn 으로 래핑 후, gunicorn 을 구동하는 3개의 도커 컨테이너를 대상으로 로드 밸런싱을 수행하는 nginx 서버를 구축했습니다. 관련해 공부한 내용은 웹서버와 WAS 에 자세히 기록해 두었습니다.