---
# System prepended metadata

title: CS376 Final Project

---

# CS376 Final Project 
#### COVID19 Global Forecasting (Week 1) Forecast daily COVID-19 spread in regions around world

#### Team 7 이시하 조성혜 임지수

## Introduction

본 프로젝트에서 높은 정확도를 얻기 위해 다양한 방법을 시도했다. 첫번째로, 다양한 모델들을 사용하여 결과를 예측했다. 기본적인 classifier인 SVM, KNN, Randomforest와 RNN 모델인 LSTM, GRU을 사용했다. 두번째로, input data를 다양하게 추가, 변형시키며 학습을 시도했다. 이러한 다양한 접근의 결과로 private score는 1.8, public score는 0.68까지 도달할 수 있었다.


## Model Implmentation

우리는 다섯 가지 모델, SVM, KNN, Random forest, LSTM, GRU를 사용해, 동일한 dataset을 기준으로 다섯 모델의 성능을 비교했다. 이중 SVM, KNN, Random forest는 classifier 모델에 속하며, LSTM과 GRU는 RNN 모델에 속한다. 

### Classifier

> SVM

SVM은 기계학습에서 사용되는 가장 기본적인 선형 분류 모델 중 하나이다. 우리가 예측해야 하는 것은 확진자 수(confirmed cases)와 사망자 수(fatalities)인데, 이는 범위가 한정적인 정수(integer)였기 때문에 각 정수 0,1,2,...를 각각의 class로 하여 다중 분류(multi-class classification)를 통해 예측할 수 있을 것이라고 생각했다. 한 가지 문제는 SVM 모델이 다중 분류가 아니라 이진 분류를 위한 모델이라는 것이었다. Scikit-learn의 SVC 모델은 이진 분류를 OvA(One versus All) 방식(첫 번째 class와 나머지 class로 1차 분류, 두 번째 class와 나머지 class로 2차 분류,..., 같은 방식으로 여러 번 이진 분류)으로 내부에서 자동적으로 다중 분류를 처리해주었다. 따라서 나는 Scikit-learn의 SVC 모델을 사용해 분류, 예측하였다.
아래 그림은 SVM 모델에서 C=0.5,kernel은 sigmoid 함수로 설정해준 뒤의 결과 점수이다. C를 다른 값으로 바꿔보고, kernel을 없애거나 다른 함수로 설정해보았으나 성능은 크게 좋아지지 않았다. 
![](https://i.imgur.com/gEaqpPN.png)


> KNN

train data를 보았을 때 나라별로 값이 뚜렷하게 나뉘는 편이었다. SVM 모델은 kernel을 이용할 수 있기는 하지만 여전히 선형 분류에 특화된 모델이었기 때문에 SVM 모델 대신 나라별로 clustering을 해줄 수 있는 다른 분류 모델을 원했다. KNN은 k개의 가장 가까운 이웃들을 선정해 그 이웃들 중에서 가장 다수가 가지는 class를 선택하는 분류 모델이다. 따라서 다중 분류가 가능하고, 나라 별로 clustering이 되어 A 나라의 데이터를 predict할 경우 가까운 이웃들에 A 나라의 training data가 포진할 것이라 예상했다. 
아래 그래프들은 k가 1부터 10일 때의 각 KNN 모델로 confirmed cases와 fatalities에 대한 accuracy를 구한 것이다. training data set을 8:2로 random하게 나누어 0.8만큼의 data로 훈련하고 나머지 0.2만큼의 data로 test해 accuracy를 구했다.그래프를 보면 k가 1 또는 2일 때 가장 정확도(accuracy)가 높음을 알 수 있다. KNN에서 k, 즉 선택하는 이웃의 수가 많을수록 결정 경계(decision boundary)가 단순해지고 부드러워진다. 따라서 Covid19 분류 문제는 결정 경계가 복잡하고 training error을 덜 허용할수록 정확도가 높게 나온다는 말로 해석할 수 있다.

<img src="https://i.imgur.com/6KAyXSM.png" width="50%">


<img src="https://i.imgur.com/kFLUCnC.png" width="50%">


아래 그림은 k=3일 때의 KNN 모델을 사용했을 때 나온 결과 점수이다.  
![](https://i.imgur.com/GdXGAtK.png)


> Random Forest Classifier

Random forest classifier는 분류 또는 회귀 모두에 사용될 수 있는 방법으로, 편향이 적은 여러 의사결정트리(Decision Tree) 의 예측결과 중 다수의 결과를 선택(Majority Vote)하여 분 산과 편향이 적은 단일한 학습 결과를 얻는 앙상블 트리 기법 이다. 이때 편향이 적은 각각의 의사결정트리를 만들기 위해 학습 데이터를 전체 데이터 크기만큼 복원 추출하여 여러 개 의 학습 데이터를 샘플링하는 방법인 부트스트랩(Bootstrap) 이 함께 사용된다.

![](https://i.imgur.com/ddqGxaA.png)
Random forest의 구조

[시계열 데이터와 랜덤 포레스트를 활용한 시간당 초미세먼지 농도 예측](https://www.koreascience.or.kr/article/JAKO202013261020958.pdf)

위의 논문에서는 random forest를 활용하여 시계열 데이터인 초미세먼지 농도를 예측하였다. Random forest는 모든 입력 변수를 균형 있게 학습할 수 있어서 시계열 전처리를 수행한 데이터가 입력될 경우 모든 입력 변수의 시각별 정보를 소실하지 않고 학습할 수 있으며, 결과에 대한 설명력이 상대적으로 좋다는 장점이 있다. 
Random forest를 사용한 결과는 다음과 같다.


다음은 Classifier를 사용한 결과를 정리한 표와 그래프다.
| Model | Private Score | Public Score |
| -------- | -------- | -------- |
| SVM | 5.088    | 3.071    |
| KNN | 3.507    | 0.881    |
| Random Forest  | 2.132    | 0.739    |

![](https://i.imgur.com/pv4Yt4h.png)
SVM이 성능이 크게 떨어지는 이유로는 훈련 데이터 셋의 clustering 특성이 SVM의 선형 이진 분류와 맞지 않기 때문이라고 생각했다. SVM의 선형 분류의 한계를 극복하기 위해 kernel을 도입했으나, 여전히 나라 별로 clustering되는 데이터 셋의 특징을 살리기에는 SVM보다는 KNN이 더 우수했다. 
Random forest와 KNN은 모두 다중 분류가 가능하나, KNN은 feature들을 각각 하나의 차원으로 설정해 데이터 간 거리(Euclidean distance)를 구해 가장 가까운 데이터를 선정하는 방식이고, Random forest는 feature들을 복합적으로 고려해 각 노드의 분할 테스트를 만든다는 특징이 있다. 

### RNN

다음으로는 RNN을 사용해서 predction을 해 봤다. Confrimed cases와 fatalities가 이전의 data와 큰 관련이 있기 때문에, 앞의 data들을 recursive하게 적용해주는 RNN이 효과적일 것이라고 판단했다. 

> LSTM

먼저 LSTM을 사용했는데, 앞쪽에 위치한 정보들의 손실을 최소화 시키기 위해서 LSTM을 선택해줬다. 

![](https://i.imgur.com/tcDaY3s.png)

위는 우리가 사용한 LSTM model이다. ```self.lstm1```의 ```input_size```는 우리가 사용할 data의 종류에 따라 달라지게 된다. 
    
Input을 ```X1 = (Lat, Long, Passed Day, Confirmed cases, Fatalities)```, output을 ```y1 = (Confirmed cases, Fatalities)``` 로 설정해서 train을 시켜주었고, sequence length는 6으로 설정했다. 한 지역당 총 64개의 data(1/22~3/25)가 있기 때문에, 

$$train_X(i) = \left[\begin{array}{rrr} 
X(i);X(i+1);X(i+2);X(i+3);X(i+4);X(i+5)
\end{array}\right],$$
$$
train_y(i) = \left[\begin{array}{rrr} 
y(i+6)\\
\end{array}\right] \qquad(i=0,1,2...,57)$$

가 된다. 즉, ```i~i+5```일의 정보를 이용해서 ```i+6```일의 정보를 predict하게끔 train을 시켰다. Train 결과는 아래와 같았다. 
    
![](https://i.imgur.com/v93H3C5.png)

> GRU

RNN의 또다른 모델인 GRU를 사용해서 학습해보았다. LSTM은 input, output, forget gate으로 총 3개의 gate가 있는 반면, GRU는 forget과 input gate를 update gate로 합친 구조를 가지고 있다. GRU는 LSTM에 비해 매개변수의 양이 적고 단순한 구조를 가지고 있어서 데이터 양이 적을 때 조금 더 좋은 성능을 보인다. 그러나 기본적으로는 거의 비슷한 구조를 가지고 있어서 전체적인 결과는 비슷하게 나온다. 우리가 사용한 모델 구조는 다음과 같다.

![](https://i.imgur.com/OmUPD6c.png)

![](https://i.imgur.com/NAMuFhJ.png)


## Input data 

### External data

주어진 data 만으로 train을 했을 특히 RNN의 경우에 좋지 못한 성능을 보였다. 그래서 external data를 추가해 학습을 시켰다. 

[enriched_covid_19_week_2.csv](https://www.kaggle.com/optimo/covid19-enriched-dataset-week-2/output) 를 가져와서 사용했는데, 2주차의 data이긴 하지만 1주차에서 주어진 data보다 딱 하루가 더 있는 data였기 때문에(3/25일의 data), 이 data를 사용해도 문제가 없다고 판단했다. 
    
```enriched_covid_19_week_2.csv```에는 confirmed cases와 fatalities를 포함한 다양한 정보들이 있었는데, 이중 사용한 것은 아래와 같다. 

* **Old**
        나이 범위 별로 인구비율에 대한 정보가 있었는데, 이중에서 80살 이상의 인구비율만 더해서 나이가 많은 사람의 비율로서 사용했다. 코로나 바이러스가 나이가 많을수록 더 위험하다는 사실에 착안해서 이 data를 사용했다. 
* **total_pop, density**
        총 인구 수와 인구 밀도 정보를 사용했다. 사람 간의 감염으로 코로나가 퍼지는 것이기 때문에, 이 두 data와 코로나 확산이 밀접한 관련이 있을 것이라고 생각해서 사용했다. 
* **hospibed, lung**
        병원의 침대 개수, lung 건강 정보도 관련이 있을 것이라고 생각해서 사용했다. 
        
이 새로운 data를 이용해 input을 ```X2 = (confirmed cases, fatalities, old, total_pop, density, hospibed, lung)```, output을 ```y2 = (confirmed cases, fatalities)``` 로 설정해서 train을 시켜줬다. 

> Result

External Data를 추가했을 때 모델 별 성능이 모두 좋아졌다. 다음은 모델 별 데이터를 추가하기 전과 후의 결과이다. 

 <table>
   <tr>
    <th></th>
    <td colspan="2">KNN</td>
    <td colspan="2">Random Forest</td>
    <td colspan="2">LSTM</td>
    <td colspan="2">GRU</td>
  </tr>
  <tr>
    <th>Score</th>
    <td>Private</td>
    <td>Public </td>
    <td>Private</td>
    <td>Public </td>
    <td>Private</td>
    <td>Public </td>
    <td>Private</td>
    <td>Public </td>
  </tr>
  <tr>
    <th>Model(X1,y1)</th>
    <td>3.508</td>
    <td>0.882</td>
    <td>2.132 </td>
    <td>0.739</td>
    <td>5.09 </td>
    <td>2.921</td>
    <td>4.732  </td>
    <td>2.901</td>
  </tr>
  <tr>
    <th>Model(X2,y2) </th>
    <td>1.769</td>
    <td>0.704</td>
    <td> 1.888</td>
    <td>0.717</td>
    <td2.536</td>
    <td>0.866</td>
    <td> 2.622 </td>
    <td>2.417 </td>
    <td>0.866</td>
  </tr>
</table>

다음은 External Data를 추가했을 때의 결과를 모델별로 기록한 그래프이다.
![](https://i.imgur.com/BZbY36l.png)





### Change to Delta Δ

기본 data만을 사용했을 때 보다는 훨씬 좋은 성능을 보였지만, confirmed cases와 fatalities가 비정상적으로 커지는 상황이 간혹 있었다. 또, 누적되는 것이기 때문에 confirmed cases와 fatalities는 시간이 지남에 따라 계속 증가해야 하는데 prediction을 보니 그러지 않다는 것도 확인할 수 있었다. 

그래서 input을 ```X3 = (confirmed cases, fatalities, old, total_pop, density, hospibed, lung)```, output을 ```y3 = (Δconfirmed cases, Δfatalities)```로 설정해서 다시 train을 시켜줬다. 이때 Δ는 전날과 당일의 차이를 의미한다. 이는 ```X2```와 ```y2```를 사용했을 때 보다 더 좋은 성능을 보여줬다. RNN 모델의 경우 training 마다 결과가 다르게 나와서 3번씩 돌린 후 평균값을 얻어냈다.

> LSTM
<table>
   <tr>
    <th></th>
    <td colspan="2">1st</td>
    <td colspan="2">2nd</td>
    <td colspan="2">3rd</td>
    <td colspan="2">Avg.</td>
  </tr>
  <tr>
    <th>Score</th>
    <td>Private</td>
    <td>Public </td>
    <td>Private</td>
    <td>Public </td>
    <td>Private</td>
    <td>Public </td>
    <td>Private</td>
    <td>Public </td>
  </tr>
  <tr>
    <th>LSTM(X2,y2)</th>
    <td>2.323</td>
    <td>0.834</td>
    <td>2.362</td>
    <td>0.873</td>
    <td>2.568 </td>
    <td>0.808</td>
    <td>2.417</td>
    <td>0.838</td>
  </tr>
  <tr>
    <th>LSTM(X3,y3) </th>
    <td>1.862</td>
    <td>0.688</td>
    <td>1.797</td>
    <td>0.690</td>
    <td>1.823</td>
    <td>0.692</td>
    <td>1.827</td>
    <td>0.690</td>
  </tr>
</table>

> GRU
<table>
   <tr>
    <th></th>
    <td colspan="2">1st</td>
    <td colspan="2">2nd</td>
    <td colspan="2">3rd</td>
    <td colspan="2">Avg.</td>
  </tr>
  <tr>
    <th>Score</th>
    <td>Private</td>
    <td>Public </td>
    <td>Private</td>
    <td>Public </td>
    <td>Private</td>
    <td>Public </td>
    <td>Private</td>
    <td>Public </td>
  </tr>
  <tr>
    <th>GRU(X2,y2)</th>
    <td> 2.622 </td>
    <td>0.973</td>
    <td>2.485 </td>
    <td>0.800</td>
    <td>2.501 </td>
    <td>0.827</td>
    <td>2.536</td>
    <td>0.866</td>
  </tr>
  <tr>
    <th>GRU(X3,y3) </th>
   <td> 1.726 </td>
    <td>0.689</td>
    <td> 2.059</td>
    <td>0.698</td>
    <td>1.816 </td>
    <td>0.690</td>
     <td>1.867 </td>
    <td>0.692</td>
  </tr>
</table>

> Result

Input data를 Δ로 변경했을 때 모델 별 성능이 모두 좋아졌다. 다음은 enriched data에 대해서 모델 별 데이터를 변경하기 전과 후의 결과이다. 

<table>
   <tr>
    <th></th>
    <td colspan="2">KNN</td>
    <td colspan="2">Random Forest</td>
    <td colspan="2">LSTM</td>
    <td colspan="2">GRU</td>
  </tr>
  <tr>
    <th>Score</th>
    <td>Private</td>
    <td>Public </td>
    <td>Private</td>
    <td>Public </td>
    <td>Private</td>
    <td>Public </td>
    <td>Private</td>
    <td>Public </td>
  </tr>
  <tr>
    <th>model(X2,y2)</th>
    <td>1.769</td>
    <td>0.704</td>
    <td> 1.888</td>
    <td>0.717</td>
    <td>2.417</td>
    <td>0.838</td>
    <td> 2.536 </td>
    <td>0.866</td>
  </tr>
  <tr>
    <th>model(X3,y3) </th>
    <td>1.484</td>
    <td>0.697</td>
    <td>1.556</td>
    <td>0.692</td>
    <td>1.827</td>
    <td>0.690 </td>
    <td> 1.867 </td>
    <td>0.692</td>
  </tr>
</table>

![](https://i.imgur.com/Il3eL1O.png)


Prediction을 하는 과정에서는 주어진 test data에는 ```Lat, Long, Date```의 정보밖에 없기 때문에, test data에 train data에 있는 정보들을 추가시켜줬다. ```old, total_pop, density, hospibed, lung```의 경우에는 변하지 않았다고 가정하고 train data와 동일한 값을 전부 넣어주었다. ```confirmed cases, fatalities```의 경우에는 직접 계산을 해서 넣어줬는데, ```2020-03-25```이전의 경우에는 train data와 겹치기 때문에 그 값을 그대로 넣어주었고, 그 이후에는 LSTM을 이용해서 새롭게 predict을 할 때마다 그 값을 넣어주었다. 

### Change of Sequence Length

LSTM의 경우 추가적으로 ```seq length```에 변화를 주는 것을 시도해봤는데, 4와 12를 시도해봤다. ```seq length```가 6인 경우가 가장 성능이 좋게 나오기는 했지만, 유의미한 차이를 발견하지는 못했다. 

<table>
   <tr>
    <th>seq length</th>
    <td colspan="2">4</td>
    <td colspan="2">6</td>
    <td colspan="2">12</td>
  </tr>
  <tr>
    <th>Score</th>
    <td>Private</td>
    <td>Public </td>
    <td>Private</td>
    <td>Public </td>
    <td>Private</td>
    <td>Public </td>
  </tr>
  <tr>
    <th>LSTM(X2, y2)</th>
    <td>2.332 </td>
    <td>0.850</td>
    <td> 2.323 </td>
    <td>0.834</td>
    <td> 2.339 </td>
    <td>0.870</td>
  </tr>
  <tr>
    <th>LSTM(X3,y3) </th>
    <td>1.862 </td>
    <td>0.688</td>
    <td> 1.797 </td>
    <td>0.690</td>
    <td> 2.053 </td>
    <td>0.703</td>
  </tr>
</table>

## Conclusion

> RNN의 성능에 대한 고찰. 예상보다 RNN의 성능이 낮았던 이유.

RNN의 경우에는 모델의 layer가 깊고 loss를 계산해 gradient descent 방식으로 결과를 예측하는데, 이러한 모델의 크기에 비해 지역당 data의 개수가 적어서(지역 당 64개) 모델이 충분히 학습할 수 없었던 것 같다. 우리가 RNN을 적용할 때 나라 별로 데이터를 나누어 넣었기 때문에 나라의 수는 많으나 한 나라 당 데이터가 적어 RNN에 적용되는 data가 적었다. 나라 별로 data의 개수가 더 많은 상황에서 다른 model들과의 성능을 비교해보고 싶다는 생각이 들었다. 

새로운 dataset을 만드느라 시간적인 여유가 많지 않아서 LSTM이나 GRU model 자체의 parameter을 바꿔보거나, minibatch의 개수(항상 12로 고정하고 진행했다)를 바꿔보면서 train해보지 못했다. RNN의 parameter들을 수정하는 데에 더 집중했으면 RNN의 performance를 더 올릴 수 있지 않았을까에 대한 아쉬움이 남는다. 

> KNN의 성능에 대한 고찰 및 실제 Covid 19 예측 상황과의 접목

KNN이 다른 모델들에 비해 높은 성능을 보였다.
[Time Series Forecasting with KNN in R:the tsfknn Package](https://journal.r-project.org/archive/2019/RJ-2019-004/RJ-2019-004.pdf)
에 의하면 KNN을 시계열 데이터 예측을 위해 사용할 때에는 해당 데이터가 반복되는 패턴을 가지고 있다는 근거가 있을 때 사용한다. 따라서 실제 COVID 19 데이터셋이 반복되는 구간이 많은 특징을 가지고 있음을 유추할 수 있다.
실제 데이터 셋을 관찰해보면 확진자 수나 사망자 수가 늘지 않고 정체되는 구간이 많다. 늘더라도 규칙적이고 점진적으로 늘어나는 구간이 많고, 급진적으로 확진자가 늘어나는 구간은 데이터 비율로 보았을 때 많지 않다. KNN 모델이 가장 잘 나왔던 것은 이런 데이터의 특성이 반영되었기 때문이다. 최종 score를 계산할 때, 점진적/선형적으로 늘거나 정체되어 있는 구간이 많은 데이터 셋의 특성 상 KNN 모델의 score가 높게 나왔던 것이다.

하지만 실제 Covid 19 상황에서는, 정부의 규제 등에 따라 확진자 수와 사망자 수가 급진적/지수적(exponentially)으로 늘어나는 현상이 일어난다. 그리고 인공지능을 활용해 Covid 19의 상황을 예측할 때는 이러한 급진적인 상황에 대한 예측의 정확도를 더 중요하게 고려하기 때문에, 점진적으로 늘어나는 구간을 잘 예측하는 모델보다 급진적으로 늘어나는 구간을 잘 예측하는 모델이 더 좋은 모델이라고 할 수 있다. 

또한, 나라 별로 데이터 셋이 충분히 구축되어 있는 상황에서는 KNN보다 lstm 등의 RNN 모델이 더 적합할 수 있다는 결론을 내릴 수 있었다. 
