GFS와 AMeDAS만으로 날씨 예보 AI를 만들다 (이와테 15개 지점, RMSE 2.8℃ → 1.2℃)
요약
본 연구는 GFS와 AMeDAS 데이터를 활용하여 수치예보모델(NWP)의 기온 예보 오차를 보정하는 개인적인 연구 결과를 공유합니다. 15개 지점의 시간별 기온 예측 모델을 개발했으며, 검증 기간 동안 RMSE를 2.8℃에서 1.2℃로 크게 절감했습니다. 이 과정에서 무료 데이터 확보 방법과 대용량 GRIB2 파일 처리 최적화 기술(HTTP Range 요청, xarray 개선 등)을 상세히 다루고 있습니다.
핵심 포인트
- GFS와 AMeDAS를 활용하여 기온 예보 오차 보정 모델 개발
- RMSE를 57% 절감하며 높은 예측 성능 입증
- 대용량 GRIB2 데이터 처리를 위한 기술적 최적화 과정 공유
- 무료 공개 데이터를 조합하여 전문적인 연구 수행 가능
이것은 무엇인가
수치예보모델(NWP)이 각 지역에서 어느 방향으로 오차가 발생하기 쉬운지를 학습하여 보정하는, 지점별 기온 예보에 대한 개인 연구입니다. 미국 GFS(전지구 모델・0.25도)의 예보를 입력으로, 기상청 AMeDAS의 실측을 정답으로 삼아 이와테현을 중심으로 하는 15개 지점의 시간별 기온을 예측합니다.
검증 기간 1년(2025-09~2026-08) 평가에서, GFS의 원값 대비 RMSE를 57% 절감할 수 있었습니다. 이 글은 그 구조와 무인 운용을 시작한 지 첫 달에 발생했던 일들을 기록합니다.
참고로, 본 연구는 일반 대중에게 기상 예보를 제공하지 않습니다(기상 업무법 허가 사업에 해당하지 않음). 개인의 학습 및 기록을 위한 것입니다.
데이터는 모두 무료로 확보 가능하다
날씨 예보 머신러닝이라고 하면, 기상청 GPV 데이터가 유료라는 장벽을 생각하는 사람이 많습니다. 실제로 기상 업무 지원 센터를 통한 MSM(5km・일본 지역) 계약은 개인의 지갑 사정에 무리가 있습니다. 저 역시 처음에는 그곳에서 멈추곤 했습니다.
결론적으로, 세 가지 무료 데이터 조합으로 필요한 모든 것을 갖출 수 있었습니다.
- GFS: 미국 NOAA가 공개하는 전지구 모델입니다. AWS S3의
noaa-gfs-bdp-pds버킷에 GRIB2 형식으로 저장되어 있어 누구나 가져올 수 있습니다. 0.25도로 거칠지만, 전 지구를 일본에 비유한다면 충분한 정보를 담고 있습니다. - AMeDAS: 기상청 '과거 기상 데이터 다운로드'에서 15개 지점 × 4년 분의 시계열 값입니다. 이곳이 학습의 정답 데이터가 됩니다.
- MSM(대체 경로): 기상청 MSM 유래 예보를 Open-Meteo API(비상업 무료)에서 가져올 수 있습니다. 유료 계약을 대체할 수는 없지만, 특징량 중 하나로 사용하기에는 충분했습니다.
AMeDAS를 확보하며 한 가지 주의한 점은 obsdl이라는 라이브러리의 POST 사양입니다. 날짜 지정은 ymdList라는 문자열 배열의 JSON으로 전달해야 하며, 0시는 전날의 '24시'라는 표기로 반환됩니다. 이 표기를 정규화하지 않으면, 정답 데이터의 시간이 틀어진 채로 학습이 진행됩니다.
파이프라인: 300MB GRIB2에서 필요한 7MB만 추출하기
GFS의 한 사이클은 전체가 합쳐져 약 300MB에 달합니다. 4년 분 × 매일 데이터를 이대로 보유하는 것은 현실적이지 않기 때문에, .idx 파일이라는 바이트 오프셋 인덱스를 사용하여 HTTP Range 요청으로 필요한 변수의 바이트 범위만 잘라냅니다. 이렇게 하면 1파일당 약 7MB가 되고, 일본 지역만 추출한 NetCDF는 한 사이클당 약 3.1MB까지 작아집니다.
디코딩은 eccodes라는 GRIB2 표준 라이브러리로 수행합니다. 다만 이것은 Windows에서 작동하지 않기 때문에, Docker 컨테이너 내에서만 작동하도록 분업했습니다. 데이터 확보와 페어링(pairing)은 호스트 측에서, 디코딩만 컨테이너 측에서 담당합니다.
또 다른 속도 장벽은 xarray였습니다. Windows에서 open_dataset을 사용하면 1파일당 약 1.1초가 걸려 4년 분 처리가 불가능했습니다. netCDF4 라이브러리를 직접 건드리는 방식으로 수정하자, 한 사이클당 15초에서 0.2초로 줄었습니다(75배). 이 수정을 할 때 추출된 값이 기존 구현과 완전히 일치하는지 확인했습니다.
관측 지점의 보정 대상은 격자 자체가 아니라 AMeDAS 관측 지점입니다. 관측 지점 주변 3×3 격자를 가중치로 샘플링하고, 모델 지형과 실지형의 고도 차이를 특징량으로 명시적으로 전달합니다. 분지의 지점에서 GFS가 몇 ℃ 더 춥게 보이는 현상이나, 산악 지점에서는 거의 제로에 가까운 값이 나오는 등, 지점 고유의 특성이 이 고도 차이로 설명될 수 있기 때문입니다.
모델 개선 과정: 2.80℃에서 1.21℃까지
평가 조건을 먼저 확정합니다. 검증 기간은 최근 1년(2025-09~2026-08)으로, 학습에 사용하지 않은 데이터만 평가합니다. 비교는 '동일 유효 시각에서는 최신 초기값의 예보만'으로 맞춘 43,719 쌍입니다. 오래된 초기값의 예보를 섞으면 행 수가 늘어나 보여지는 정확도가 달라지기 때문입니다.
| 모델 | RMSE | MAE |
|---|---|---|
| GFS 원값(베이스라인) | 2.80℃ | 2.31℃ |
| ... | 0.90℃ |
이 표에서 중요한 것은 2번째 줄의 '단순한 바이어스 보정'을 반드시 함께 제시하는 것입니다. 심층 학습 개선의 상당 부분은 지점별 평균 바이어스를 빼는 것만으로 하는 베이스라인에서 거의 사라집니다. 저의 결과에서도, LightGBM과 Transformer의 차이는 1.42와 1.35밖에 없지만, 베이스라인 대비 개선도는 0.29입니다. 베이스라인을 고정하여 비교하지 않으면, 개선의 근원을 오인할 수 있습니다.
그 위에서 운영 모델은 하나 더 확장했습니다. Open-Meteo를 거친 MSM 예보를 msm_t2m_c
이러한 특징량 하나를 추가하는 것만으로도 LightGBM의 성능이 1.424에서 1.210으로(-15%) 향상되었으며, 15개 지점 모두에서 개선되었습니다. 5km 해상도의 다른 모델들이 같은 장소를 어떻게 예측했는지에 대한 정보는, 0.25도 GFS 단독으로는 얻을 수 없는 것입니다. 이 v0.3 버전이 현재 운영 모델이며, MSM 데이터를 가져오는 데 실패한 날은 자동으로 특징량이 없는 v0.2로 폴백(fallback)합니다.
개선된 점은 Diebold-Mariano 검정에서 유의미했습니다 (DM=-44). 시간대별, 월별, 지점별 모든 관점에서 기준치를 상회했습니다.
운영: 매일 14시에 무인으로 실행
학습이 끝났다고 해서 예보가 만들어지는 것은 아니며, 매일 생성되어야 비로소 운영입니다. Windows의 작업 스케줄러를 이용해 매일 14:00 JST에 daily_run.py를 실행하고, 당일 GFS 초기값과 MSM 예측을 바탕으로 15개 지점 × 12 예보 시간(일일 180행)을 생성하여 축적하고 있습니다.
설계상 판단은 두 가지가 있습니다.
첫째는 알림을 전혀 보내지 않는 것입니다. 무인 작업에 외부 전송 능력을 부여하지 않기로 했기 때문에, 장애 감지는 로그의 축적(notes/ops.log와 data/ops/state.json)으로 진행합니다. 다음 날 로그를 보면 성공/실패 여부를 알 수 있으므로, 실패했을 때 울리는 스마트폰 시스템까지는 만들지 않았습니다.
둘째는 실측 확정을 기다리는 것입니다. AMeDAS의 실측값은 다음 달이 되어서야 확정됩니다. 따라서 운영 중인 예측치는 정답 없이 축적하고, 매월 초에 backfill_obs.py로 전월 실측값을 채운 후 monthly_eval.py로 검증합니다.
운영에서 발생한 일: PC 정지 및 누락 복구
무인 운영 2주 동안 교훈이 되는 사고가 한 번 있었습니다. 9월 30일부터 10월 2일까지 PC가 멈추면서, 해당 3일간의 예측 생성이 중단되었습니다.
여기서 daily_run.py의 사양이 유효하게 작용했습니다. 이 스크립트는 '예측 파일이 없는 날'을 오늘부터 최대 7일 전까지 찾는 구현이었지만, 처리 대상은 '오늘부터 거슬러 올라가 발견된 누락일 중 단 하루'였습니다. PC 정지 후 당일 데이터를 처리해버리면, 오래된 누락 데이터는 영원히 잡히지 않습니다. 결과적으로, 9/30~10/2의 3일분은 수동으로 스크립트를 세 번 실행하여 복구했습니다.
예측 축적 자체는 18 사이클 모두가 존재하는 상태로 돌아왔지만, '당일에 예측이 생성되었는지'라는 의미에서는 3일간의 공백이 있습니다. 이 두 가지는 다른 실적입니다. 구별하지 않고 '2주 연속 결손 제로'라고 쓰면, 무인 운영 이야기로는 부정확해집니다.
복구 과정에서 daily_run.py를 개조하여, 한 번의 실행으로 미처리된 날짜 전체(최대 7일분)를 소화하도록 했습니다. 다음에 PC가 멈춰도, 한 번의 시작만으로 따라잡을 수 있게 되었습니다. 장기 운영의 교훈으로서, 결손 판정을 '당일 생성 여부'에 연결하기보다 '축적의 연속성'에 연결해 두는 것이 중요했다는 것이 현재의 이해입니다.
한계점
- GFS 0.25도는 유효 해상도가 약 25km로 일본 지역에서는 거칩니다. MSM 정보는 Open-Meteo를 통해 보완하고 있지만, 직접적인 계약을 하지 않았기 때문에 API 의존적이며, 실패 시에는 MSM이 없는 v0.2로 자동 폴백합니다.
- 스코프는 이와테현 + 인근 15개 지점입니다. 실제 팀 운영, 복수 모델 비교, 모니터링 체계 경험은 여기에 없습니다.
- 실측값 확정이 다음 달이기 때문에, 운영 중인 모델의 진정한 성적은 한 달 늦게나 알 수 있습니다.
- 10월 초 PC 정지 사례처럼, 무인 운영의 연속성은 실행 환경의 가동률에 의존합니다.
끝으로
다음 후보로 ECMWF의 AIFS(머신러닝 기반 전 지구 모델)를 동일한 파이프라인에서 검증하고 있으며, 2025년 9월 이후의 소급 데이터를 확보했습니다. AIFS를 특징량에 추가했을 때 얼마나 성능이 향상될지, MSM과 병용했을 때 얼마나 늘어날지는 후속편에서 작성할 예정입니다.
수치 예보의 보정은 '데이터가 갖춰지면 개인도 할 수 있다'는 것이 이 글이 가장 전하고 싶은 내용입니다.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn ML의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기