머신러닝 프로젝트를 해보기로했다.
주제는 기상데이터를 이용한 미세먼지예측이다.
이 주제를 선정한 이유는
1. 미세먼지에 대한 관심이 증가하고 있고, 미세먼지 문제도 날이 갈수록 심각해지고 있다.
2. 평소에 느끼기에 평균보다 추운 날에는 미세먼지가 적었고, 평균보다 더운 날에는 미세먼지가 많았다.
기압의 영향이 있을 것이라고 추측했는데 미세먼지농도가 기상에 영향을 받는지, 예측을 할 수 있을지 궁금했다.
이미 지난달 첫 번째 시도를 해보았다.
공부가 충분하지 못해서 만족스러운 결과를 얻지 못했다.
먼저 지난달의 기록을 정리하고 추가로 프로젝트를 진행하는 과정을 작성할 예정이다.
기상환경데이터와 머신러닝을 활용한 미세먼지농도 예측 모델(임준묵, 2015) 논문을 참고하였다.
기상환경데이터는 기상자료개방포털의 종관기상관측데이터를 이용하였고
기상자료개방포털[데이터:기상관측:지상:종관기상관측(ASOS):자료]
Home 데이터기상관측지상종관기상관측(ASOS) 종관기상관측(ASOS) --> 자바스크립트가 비활성 되었습니다. 해당 기능은 자바스크립트에서 활성상태에서 사용가능합니다. 종관기상관측이란 종관
data.kma.go.kr
대기질데이터는 에어코리아에서 최종확정 측정자료를 이용하였다.
에어코리아
www.airkorea.or.kr
논문에서처럼 측정소를 선택할 필요가 있었다.
기상정보를 측정하는 곳은 서울특별시에서는 서울, 관악산 두 곳이 있다.
대기질 측정장소와 비교해서 서울로 표시된 곳을 선정했다. 주소를 찾아보면 기상측정장소는 종로구에 위치해있는 것을 알 수 있다.
대기질 측정소는 기상 측정소와 가장 가까운 중구측정소로 선정했다.
다음 내용에 앞서
미세먼지 예측 프로젝트를 진행하면서 데이터의 중요성에 대해서 크게 느끼게 되었다.
좋은 모델, 최적화 함수를 사용하는 것보다 양질의 데이터를 얻는 것이 더 중요한 것 같다.
데이터에 대한 사전 조사가 충분하지 못한 상태에서 시간에 쫓겨 데이터를 수집하다보니 문제가 여러 번 생겼다
사용한 데이터
2020년 1월~12월, 1년간의 데이터를 사용했다.
21년 데이터는 아직 최종 확정되지 않아 하반기의 자료가 없었기 때문에 이용하지 못했다.
논문에서는 3개년의 자료를 이용했는데 1년자료를 이용한 것은 시간부족과 머신러닝 입문 단계에서 복잡성을 낮추기 위함이었으나 다시 해보는 과정에서는 3개년 자료를 이용할 예정이다.
기상자료는 기온, 강수량, 풍속, 풍향, 습도, 증기압, 이슬점온도, 현지기압, 해면기압, 일조, 일사, 전운량, 중하층운량, 최저운고, 시정, 지면온도 16가지 데이터를 이용했고, 시간별 자료를 이용했다.
타겟데이터인 대기질 정보는 PM10, PM2.5, 오존, 이산화질소, 일산화탄소, 아황산가스 중 PM10만 예측하기로 했다.
논문에서는 PM10을 제외한 나머지 대기질정보도 독립변수로 이용했다.
하지만 나는 기상정보만 이용하기로 했다.
예측 모델을 만드는 이유는 기상데이터로 미세먼지를 예측하기 위함인데, 대기질 정보를 측정하는 시점에서는 당연히 PM10도 함께 측정을 할 것이므로 그 의미를 찾기 힘들었다.
또한 다른 대기질 정보들은 오히려 종속변수의 성질에 가까운데, 또 다른 종속변수들을 하나의 종속변수를 예측하는 것에 이용한다는 것이 납득이 되지 않았다.
대기질 데이터는 최대 한 달씩만 다운받을 수 있어서 1월부터 12월까지 따로 받아서 합쳐주어야 한다.
month_air_quality = []
for i in range(12) :
month_air_quality.append(pd.read_excel('C:/jupyter_home/data/finedust/'+str(i+1)+'월.xls'))
month_air_quality[i] = month_air_quality[i].drop([month_air_quality[i].index[0]])
# 하나의 데이터 프레임으로 합침
air_quality = month_air_quality[0]
for i in range(1, 12):
air_quality = pd.concat([air_quality, month_air_quality[i]], axis=0)
데이터 형식 맞추기
기상자료와 대기질자료는 서로 다른 포털에서 얻어온 것들이어서 날짜, 시간을 표시해주는 형식이 달랐다.
심지어 처음 자료를 받았을 때 시간별 자료 / 일간자료를 나누어 다운로드받을 수 있는지 몰랐어서
하나는 시간별 하나는 일별로 받아서 더욱 혼란이 왔었다.
날짜, 시간이 기준이 두 데이터를 합치는데 기준이 되므로 두 형식을 맞추어야 했다.
대기질 데이터 날짜에
연도를 추가하고 문자열 슬라이싱으로 yyyy-mm-dd hh:mm 형식으로 바꾼후 datetime 타입으로 형식을 변경해주었다.
문제는 01-01-24와 같이 24시로 표기한 날짜들이었다.
24시는 다음날 00시로 바꾸어 표시해주어야한다.
# 날짜 형식 맞추기
air_quality['날짜'] = air_quality['날짜'].map(lambda x : '2020-'+x[:5]+" "+x[6:]+":00" )
def converting_date(x):
h = x[11:13]
d = x[:10]
if h == '24' :
d = datetime.strptime(d, '%Y-%m-%d') # 문자열 => datetime
d = d + timedelta(days=1)
d = datetime.strftime(d, '%Y-%m-%d %H:%M') # datetime => 문자열
return d
else :
return x
air_quality['날짜'] = air_quality['날짜'].map(converting_date)
col = air_quality.columns
air_quality['날짜'] = air_quality['날짜'].map(lambda x : datetime.strptime(x, '%Y-%m-%d %H:%M'))
또 PM10 컬럼이 object type으로 나타나있어 float64타입으로 바꾸어주었고,
기상데이터의 일시 컬럼도 날짜로 이름을 변경하고 datetime타입으로 변경해주었다.
결치값처리
PM10의 결치값은 삭제했다.
타겟값을 평균이나 중위값, 최빈값으로 대체하는 것은 의미가 없다고 생각했고, 결치값이 많지 않았기 때문이다.
타겟데이터(종속변수)의 결치값의 경우 KNN으로 대체할 수 있다. 다음엔 이 방법으로 결치값을 대체할 예정이다.
기상데이터의 결치값은 매우 많았다.
일시 0
기온(°C) 1
강수량(mm) 7723
풍속(m/s) 0
풍향(16방위) 0
습도(%) 0
증기압(hPa) 2
이슬점온도(°C) 7
현지기압(hPa) 13
해면기압(hPa) 13
일조(hr) 3981
일사(MJ/m2) 3981
전운량(10분위) 18
중하층운량(10분위) 11
최저운고(100m ) 4177
강수량, 일조, 일사의 결치값은 0으로 채워주었다.
누락된 값이 아니라 없는 값으로 생각했기 때문인데,
비가 오지 않는 날도 있으며 밤이나 흐린 날을 해가 비치지 않기 때문이다.
최저운고는 값의 의미, 결치의 이유를 쉽게 알 수 없었고 결치값도 많이 존재하기 때문에 이용하지 않기로 했다.
나머지 값들은 bfill로 채워주었다. 기상데이터와 같은 연속적인 값은 주변값이 가장 가까울 것이라 생각했다.
일별 자료로 나타내기
시간별자료로 받은 것으 다시 일별자료로 나타내주었어야 했다.
상관계수를 구했을때 관계가 없음으로 결과가 나왔기때문이다.
너무 세세하게 나타나있어 관계가 나오지 않았던 것 같다.
미세먼지 농도는 일 평균으로 나타냈고
기상자료에서 강수량, 일조, 일사는 합, 풍속은 최대값, 나머지는 평균으로 일별 자료로 나타내었다.
임의로 시간자료롤 일별로 나타냈다는 것이 매끄럽지 못하다고 느끼는 부분이었는데
나중에 찾아보니 사이트에서 일별 자료 선택해서 받을 수 있었다.
EDA 탐색적 데이터 분석
상관계수 히트맵
단일 기상요소가 PM10과 높은 상관계수를 가지는 경우는 없었다.
기상요소별 상관계수가 매우 높은 것들이 존재했다.
해면기압-현지기압은 상관계수가 1이었고 기온-증기압/이슬점온도가 상관계수가 0.9이상이었다.
상관계수가 높은 특성들은 다중공선성 문제로 변수의 설명력을 약화시키므로 처리가 필요하다.
차원축소 대신 컬럼을 삭제해주었다.
히스토그램
null값을 0으로 처리해준 특성(강수량, 일조, 일사)의 데이터들을 보면 0에 치우쳐저 있는 것을 확인 할 수 있었다.
오버샘플링과 언더샘플링이 필요해보인다.
다른 특성의 경우 위의 세 특성보다는 정규분포에 가까운 분포를 보인다고 할 수 있지만 이상적이지는 않았다.
이 부분에서 1개년 자료로는 부족다하고 생각했다.
산점도
타겟데이터 카테고리화
미세먼지 농도를 예측하는 것의 의미를 생각해보았을때, 수치적으로 x ppm으로 예측하는 것보다
매우좋음, 좋음, 보통, 나쁨, 매우나쁨과 같이 단계로 예측하는 것이 더 유의미할 것 같아 기준에 따라 미세먼지를 카테고리화 시켰다.
def PM10_cat(x):
if x <=30 :
return 1
elif x <=80 :
return 2
elif x <= 150 :
return 3
else :
return 4
finedust = pd.DataFrame({'PM10' : daily_air['PM10'].map(PM10_cat)},
index=daily_air.index)
정규화 및 데이터 split
sklearn의 StandardScaler를 이용해 독립변수 정규화를 해주었고, train_test_split을 이용해서 데이터 셋을 나누었다.
from sklearn import preprocessing
scaler = preprocessing.StandardScaler()
scaler.fit(daily_weather)
col = daily_weather.columns
nomalized_daily_weather = scaler.transform(daily_weather)
nomalized_daily_weather = pd.DataFrame(data=nomalized_daily_weather,
columns=col,
index=daily_weather.index)
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test =\
train_test_split(nomalized_daily_weather, finedust, test_size=0.2, random_state=11)
학습
이 프로젝트를 진행할 당시에는 다항분류조차 배우지 않은 걸음마 단계여서
내가 할 수 있는 간단한 모델에서 최적의 효과를 낼 수 있는 모델을 이용하였다.
GBM; GradientBoostingClassifier를 이용하였다.
from sklearn.ensemble import GradientBoostingClassifier
import time
start_time = time.time()
gb_clf = GradientBoostingClassifier(random_state=0)
gb_clf.fit(x_train, y_train)
gb_pred = gb_clf.predict(x_test)
gb_acc = accuracy_score(y_test, gb_pred)
print('GBM 정확도 : {0:.4f}'.format(gb_acc))
print('GBM 수행시간 : {0:.1f}초'.format(time.time()-start_time))
GBM 정확도 : 0.7838
GBM 수행시간 : 0.4초
정말 낮은 정확도가 나왔다.
예측
80%가 안되는 정확도가 나왔지만, 저 당시의 수준으로는 최선이었기때문에
어찌되었든 다른 데이터셋(2021년 데이터)으로 예측을 해보고 싶었다.
21년 7월까지의 데이터를 가지고 온 후 전처리를 모두 동일하게 해준 후 위에서 만든 GBM으로 예측을 해보았다.
결과는 51% 정확도로 아주 처참했다.
같은 20년도의 데이터로 예측을 했을때와 비교해서 정확도가 거의 30% 떨어졌다는 것은
기상, 대기 상태가 연도별 차이가 있는 듯 해보인다.
조금 더 배운 현재 상태에서 리뷰를 하며 지난 프로젝트를 살펴보니 개선해야할 부분들이 조금씩 보이는 것 같다.
일단 oversampling/undersampling을 해서 데이터의 분포를 맞추어야하고
데이터를 분할할때 stratify 옵션을 지정해서 분포를 유지하면 train/test set을 나누어야한다.
이상치, 결측치처리도 미흡했던 점이 보이고 적절한 데이터를 이용하지 못했던 것 같다.
또 20년도 이외의 다른 연도의 데이터도 필수로 이용해야할 듯하다.
'Projects' 카테고리의 다른 글
정답데이터 만들기 - Image annotation, VIA (0) | 2022.05.24 |
---|---|
데이콘 경진대회 참여 - 주제, 데이터 (0) | 2022.05.24 |
[kaggle] Titanic Dataset predictions (0) | 2022.03.22 |
[kaggle] Titanic Dataset EDA (0) | 2022.03.22 |
인터페이스 개발 프로젝트 후기 (0) | 2022.03.14 |