본문 바로가기
기계학습/자연어 처리 머신러닝

유동 인구 예측 - 실전 프로젝트

by tryotto 2020. 2. 20.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# trainning set 가져오기 - 11월 유동 인구량
import pandas as pd
 
f_train = pd.read_csv('/content/drive/My Drive/Floating_Population_1911.csv')
 
 
 
 
# '시' 제거
del f_train['시']
 
 
 
 
# 남자 = 0, 여자 = 1 변경
f_train['성별'= f_train['성별'].replace(['남성''여성'],[01])
 
 
 
 
 
# 군/구 정수 인코딩 해주기
f_train['군구'= f_train['군구'].replace(['영등포구''구로구''동대문구''서초구''성동구''성북구'
'은평구''양천구''마포구''중구''도봉구''강동구''광진구''관악구''강남구''용산구''중랑구'
'노원구''종로구''강북구''금천구''강서구''동작구''송파구''서대문구'], [i for i in range(0,25)])
 
 
 
 
# 일자 변경해주기 (1) : 20191030 -> 30 ('일' 만 기록해준다)
import numpy as np
 
f_train['일자'= f_train['일자'].replace([d for d in range(2019110120191131)],[d for d in range(131)])
 
 
# 일자 변경해주기 (2) : 평일 -> 0, 주말 -> 1 로 변경해준다
# 1~3, 8~10, 15~17, 22~24, 29~30 -> 주말 1
# 그 외 -> 평일 0
 
weekend=[]
weekday=[]
 
for i in range(0,4+1):
  for j in range(1,3+1):
    tmp = 7*i+j
    if tmp<=30:
      weekend.append(tmp)
  for j in range(47+1):
    tmp = 7*i+j
    if tmp<=30:
      weekday.append(tmp)
len_weekend = len(weekend)
len_weekday = len(weekday)
 
f_train['일자'= f_train['일자'].replace(weekend, [1 for i in range(len_weekend)])
f_train['일자'= f_train['일자'].replace(weekday, [0 for i in range(len_weekday)])
 
 
 
 
 
 
# 데이터셋 만들기 - X, Y
 
train_X = f_train.loc[:,['일자''시간(1시간단위)''연령대(10세단위)''성별''군구']]
train_Y = f_train.loc[:,['유동인구수']]
 
train_X = train_X[:].values
train_Y = train_Y[:].values
 
 
 
 
 
 
 
cs





# 데이터 처리 

데이터 셋은 SK Data Hub 에서 가져왔다.
서울시에서 측정한 값으로서, 여러 조건들 하에서 특정 날짜의 유동 인구량이 저장되어 있다.

데이터를 전처리하기 위해 여러 고민들을 해봤을때, 

1. 모든 데이터가 "서울" 이라는 곳의 데이터였기 때문에 '시' 에 해당되는 컬럼은 제거해줘야 했으며
2. Okt 패키지가 따로 없기 때문에 무조건 영어 또는 정수 인코딩을 해줘야 했다.
3. 날짜의 경우, 단순히 몇 일에 일어났는지는 큰 중요성이 없다고 생각했기에 주말/평일을 기준으로 데이터를 재설정 해줬다.

이런 전처리가 모두 끝난 뒤엔, 이에 알맞을만한 모델을 구상해봤다.




# 모델 구상


- LSTM 모델

- 기본 밀집층 모델

이 두 가지를 떠올렸는데, LSTM 의 경우엔 (None, 5) 의 데이터를 Embedding 레이어를 거쳐서 (None, 5, one-hot) 의 3차원 텐서로 변형시켜서 

Time seqeunce = 5 가 될 수 있도록 만들면 되겠다고 생각했다.

지본 밀집층 모델의 경우, LSTM 의 성능이 하도 안 나와서 그냥 가장 기본적인 모델을 만들어보자는 생각해 시도해봤고, 성능이 아주 좋았다.





# Optimizer


- SGD

- Adam

두 가지를 사용했다. 

사실 두 옵티마이저의 차이점을 잘 몰라서 아무거나 사용해봤다가 큰코다쳤다.


SGD 의 경우, 밀집층 모델에 사용했더니 바로 nan 처리가 일어났다.

반면, LSTM 의 경우, SGD에서 잘 작동되었다.


Adam 의 경우, 밀집층 모델에서 사용이 아주 잘 되었으며, 가장 뛰어난 성능을 보였다.

반면, LSTM 에 사용될 경우엔 SGD 와 별다른 차이가 없었다.






# 모델 1 - LSTM, Adam


1
2
3
4
5
6
7
8
9
10
11
12
13
# 모델 설계하기 (1) - LSTM 모델, optimizer=Adam
from tensorflow.keras.layers import Dense, Embedding, LSTM
from tensorflow.keras.models import Sequential
 
model = Sequential()
model.add(Embedding(input_dim=1000, output_dim=60, input_length=5))  # 출력층 = (batch, 5, 60)
model.add(LSTM(units=128, input_shape=(560)))    # 출력층 = (batch, 5, 128)
model.add(Dense(units=1, input_shape=(None, 128)))  # 출력층 = (batch, 1)
 
model.compile(optimizer='adam', loss='mean_absolute_error')
 
model.fit(x=train_X, y=train_Y, batch_size=100, epochs=50)
 
cs

성능- 최악





# 모델 2 - LSTM, SGD

1
2
3
4
5
6
7
8
9
10
11
12
13
# 모델 설계하기 (1) - LSTM 모델, optimizer=SGD
from tensorflow.keras.layers import Dense, Embedding, LSTM
from tensorflow.keras.models import Sequential
 
model = Sequential()
model.add(Embedding(input_dim=1000, output_dim=60, input_length=5))  # 출력층 = (batch, 5, 60)
model.add(LSTM(units=128, input_shape=(560)))    # 출력층 = (batch, 5, 128)
model.add(Dense(units=1, input_shape=(None, 128)))  # 출력층 = (batch, 1)
 
model.compile(optimizer='sgd', loss='mean_absolute_error')
 
model.fit(x=train_X, y=train_Y, batch_size=100, epochs=30)
 
cs



성능 - 마찬가지로 최악




# 모델 3 - 밀집층, SGD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 모델 설계하기 (2) - 기본 밀집 모델, optimizer= SGD
# optimizer=sgd 는 오류 발생 (이유는 모르겠다)
from tensorflow.keras.layers import Dense, Embedding, LSTM
from tensorflow.keras.models import Sequential
 
model = Sequential() # 초기 입력값 = (batch, 5)
model.add(Dense(units=128, input_dim=5))  # 출력층 = (batch, 128)
model.add(Dense(units=128, input_dim=128))  # 출력층 = (batch, 1)
model.add(Dense(units=128, input_dim=128))  # 출력층 = (batch, 1)
model.add(Dense(units=128, input_dim=128))  # 출력층 = (batch, 1)
model.add(Dense(units=128, input_dim=128))  # 출력층 = (batch, 1)
model.add(Dense(units=1, input_dim=128))  # 출력층 = (batch, 1)
model.summary()
model.compile(optimizer='SGD', loss='mean_absolute_error')
 
model.fit(x=train_X, y=train_Y, batch_size=100, epochs=30)
 
cs

성능 - 아예 처리 불가 (에러 발생)




# 모델 4 - 밀집층, Adam, lr=0.001

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 모델 설계하기 (2) - 기본 밀집 모델, optimizer= Adam (lr=0.001)
# optimizer=sgd 는 오류 발생 (이유는 모르겠다)
from tensorflow.keras.layers import Dense, Embedding, LSTM
from tensorflow.keras.models import Sequential
 
model = Sequential() # 초기 입력값 = (batch, 5)
model.add(Dense(units=128, input_dim=5))  # 출력층 = (batch, 128)
model.add(Dense(units=128, input_dim=128))  # 출력층 = (batch, 1)
model.add(Dense(units=128, input_dim=128))  # 출력층 = (batch, 1)
model.add(Dense(units=128, input_dim=128))  # 출력층 = (batch, 1)
model.add(Dense(units=128, input_dim=128))  # 출력층 = (batch, 1)
model.add(Dense(units=1, input_dim=128))  # 출력층 = (batch, 1)
model.summary()
model.compile(optimizer='adam', loss='mean_absolute_error')
 
model.fit(x=train_X, y=train_Y, batch_size=100, epochs=30)
 
cs


성능 - 
제일 괜찮았음.
그러나 뛰어난 성능이라고 하긴 부끄럽다

epoch 를 아무리 늘려도 local minimum 을 벗어나지 못하길래, 아예 learning rate 를 높여보고싶어서 그 다음 모델을 시도했다.



# 모델 5 - 밀집층, Adam, lr=0.01

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 모델 설계하기 (2) - 기본 밀집 모델, Optimizer=Adam(lr=0.01)
from tensorflow.keras.layers import Dense, Embedding, LSTM, Activation
from tensorflow.keras.models import Sequential
from tensorflow.keras import optimizers
 
model = Sequential() # 초기 입력값 = (batch, 5)
model.add(Dense(units=128, input_dim=5))  # 출력층 = (batch, 128)
model.add(Dense(units=128, input_dim=128))  # 출력층 = (batch, 1)
model.add(Dense(units=128, input_dim=128))  # 출력층 = (batch, 1)
model.add(Dense(units=128, input_dim=128))  # 출력층 = (batch, 1)
model.add(Dense(units=128, input_dim=128))  # 출력층 = (batch, 1)
model.add(Dense(units=1, input_dim=128))  # 출력층 = (batch, 1)
 
Adam = optimizers.Adam(learning_rate=0.01)
 
model.summary()
model.compile(optimizer=Adam, loss='mean_absolute_error')
 
model.fit(x=train_X, y=train_Y, batch_size=100, epochs=50)
 
cs

성능 -
learning rate 를 바꾼다고 해도 큰 성능 개선은 없었다.
그냥 모델 4를 기본으로 잡고 시도해보기로 했다.





# 실제 데이터 예측 값과 비교


10월의 서울 유동 인구량 데이터와 비교를 해봤다.

전처리 하는 과정은 앞에서 했던 것과 거의 똑같았기에 어려움이 없었다.


왜 10월의 유동 인구량을 test data 로 삼았냐 하면, 

10월과 11월의 데이터 자체에서 유의미한 차이가 없을거라 예상했다.


둘 다 완전 연말이라고 하기엔 애매하고, 

추석같은 대 명절은 9월에 있었기에 변수가 되지 못했다.


10월과 11월 둘 다 비슷한 양상을 띌 것이라 예상했기에 

해당 데이터를 선택했다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 총 성능을 비교해봤을때, 밀집 층을 여러 겹으로 하고 optimizer 를 adam 으로 하는게 가장 정확도가 좋았다
# 해당 모델을 이용해서 테스트해보기 - 테스트 데이터 : 10월 유동 인구량
 
f_test = pd.read_csv('/content/drive/My Drive/FLT_SEOUL_10MONTH.csv')
print(f_test)
 
 
 
# 똑같은 순서대로 전처리 해주기
 
# '시' 제거
del f_test['시']
 
# 남자 = 0, 여자 = 1 변경
f_test['성별'= f_test['성별'].replace(['남성''여성'],[01])
 
# 군/구 정수 인코딩 해주기
f_test['군구'= f_test['군구'].replace(['영등포구''구로구''동대문구''서초구''성동구''성북구''은평구',
 '양천구''마포구''중구''도봉구''강동구''광진구''관악구''강남구''용산구''중랑구''노원구''종로구'
'강북구''금천구''강서구''동작구''송파구''서대문구'], [i for i in range(0,25)])
 
 
# 일자 변경해주기 (1) : 20191030 -> 30 ('일' 만 기록해준다)
f_test['일자'= f_test['일자'].replace([d for d in range(2019100120191032)],[d for d in range(132)])
print(f_test)
 
# 일자 변경해주기 (2) : 평일 -> 0, 주말 -> 1 로 변경해준다
# 4~6, 11~13, 18~20, 25~27 -> 주말 1
# 그 외 -> 평일 0
 
weekend=[]
weekday=[]
 
for i in range(0,4+1):
  for j in range(4,6+1):
    tmp = 7*i+j
    if tmp<=30:
      weekend.append(tmp)
  for j in [1,2,3,7]:
    tmp = 7*i+j
    if tmp<=30:
      weekday.append(tmp)
len_weekend = len(weekend)
len_weekday = len(weekday)
 
f_test['일자'= f_test['일자'].replace(weekend, [1 for i in range(len_weekend)])
f_test['일자'= f_test['일자'].replace(weekday, [0 for i in range(len_weekday)])
 
 
# 데이터셋 만들기 - X, Y
 
test_X = f_test.loc[:,['일자''시간(1시간단위)''연령대(10세단위)''성별''군구']]
test_Y = f_test.loc[:,['유동인구수']]
 
cs


이렇게 전처리를 했고,

1
2
3
4
5
6
7
8
9
10
import random
 
idx = random.randint(010000)
test_X_tmp = test_X[idx: idx+5]
test_Y_tmp = test_Y[idx: idx+5]
 
predict_Y = model.predict(x=test_X_tmp)
predict_Y = predict_Y.tolist()
print("prediction : ", [int(i[0]) for i in predict_Y])
print("real value : ", test_Y_tmp['유동인구수'].tolist())
cs

이렇게 실제 값을 예측해봤다.


아주 웃기는 결과가 나왔다.

오차는 큰 편이다.

그래도 초반에 다른 모델로 했을때는 오차가 몇천만 이상 됐던걸 감안하면....

나쁘지 않다.. 






# 마무리


처음으로 직접 데이터를 가져와서 분석해보고,
남의 코드를 보는 게 아닌 나만의 코드로 분석했던 게 의미 있었다.

다음에는 음성이나 이미지로 위와 같은 시도를 해보는 것도 좋을 것 같다.

가만 보면, 레이어를 만드는 것 자체는 거의 하는 게 없는것 같다...
전처리가 제일 빡세...