2주차

window 사이즈 만한 값들을 input x, 그 다음 값을 label y로 세팅

- 위 예시에서는 30 features

 

<데이터 생성하기>

dataset = tf.data.Dataset.range(10)
for val in dataset:
	print(val.numpy())

dull!

windowing 해보기

 

dataset = tf.data.Dataset.range(10)
dataset = dataset.window(5, shift=1)
for window_dataset in dataset:
    for val in window_dataset:
        print(val.numpy(), end=" ")
    print()

- window 사이즈, 그리고 매번 얼마나 shift할지

 

 

dataset = tf.data.Dataset.range(10)
dataset = dataset.window(5, shift=1, drop_remainder=True)
for window_dataset in dataset:
    for val in window_dataset:
        print(val.numpy(), end=" ")
    print()

drop_remainder 설정하면

 

위 데이터를 numpy list로 넣어보자

 

dataset = tf.data.Dataset.range(10)
dataset = dataset.window(5, shift=1, drop_remainder=True)
dataset = dataset.flat_map(lambda window: window.batch(5))
for window in dataset:
     print(window.numpy())

 

split dataset + shuffle + batch

dataset = tf.data.Dataset.range(10)
dataset = dataset.window(5, shift=1, drop_remainder=True)
dataset = dataset.flat_map(lambda window: window.batch(5))
dataset = dataset.map(lambda window: (window[:-1], window[-1]))
dataset = datset.shuffle(buffer_size=10)
dataset = dataset.batch(2).prefetch(1)
for x, y in dataset:
    print("x = ", x.numpy())
    print("y = ", y.numpy())

 

아이템이 두개 씩 든 세 개의 배치 생성됨

 

위처럼 생성한 데이터를 NN에 넣기

 

def windowed_dataset(series, window_size, batch_size, shuffle_buffer):
    dataset = tf.data.Dataset.from_tensor_slices(series)
    dataset = dataset.window(window_size+1, shift=1, drop_remainder=True)
    dataset = dataset.flat_map(lambda window: window.batch(window_size+1))
    dataset = dataset.shuffle(shuffle_buffer).map(lambda window: (window[:-1], window[-1]))
    datawet = dataset.batch(batch_size).prefetch(1)
    return dataset

 

split dataset for simple linear regression

split_time = 1000
time_train = time[:split_time]
x_train = series[:split_time]
time_valid = time[split_time:]
x_valid = series[split_time:]

window_size = 20
batch_size = 32
shuffle_buffer_size = 1000

dataset = windowed_dataset(series, window_size, batch_size, shuffle_buffer_size)
l0 = tf.keras.layers.Dense(1, input_shape=[window_size])
model = tf.keras.models.Sequential([l0])

model.compile(loss='mse', optimizer=tf.keras.optimizers.SGD(learning_rate=1e-6, momentum=0.9))
model.fit(dataset, epochs=100, verbose=0)

학습 후 weight 를 확인해보면

 

print("Layer weights {}".format(l0.get_weights()))

 

모든 point에 대해서 prediction한다고 하면

forecast = []
for time in range(len(series) - window_size):
    forecast.append(model.predict(series[time:time + window_size][np.newaxis]))

forecast = forecast[split_time - window_size :]
results = np.array(forecast)[:, 0, 0]

tf.keras.metrics.mean_absolute_error(x_valid, results).numpy()

 

<DNN으로 정확도 높이기>

dataset = windowed_dataset(x_train, window_size, batch_size, shuffle_buffer_size)

model = tf.keras.models.Sequential([
    tf.keras.layers.Dense(10, input_shape=[window_shape], activation='relu'),
    tf.keras.layers.Dense(10, activation='relu'),
    tf.keras.layers.Dense(1)
])

model.compile(loss='mse', optimizer=tf.keras.optimizers.SGD(learning_rate=1e-6, momentum=0.9))
model.fit(dataset, epochs=100, verbose=0)

 

<learning rate scheduler 추가>

dataset = windowed_dataset(x_train, window_size, batch_size, shuffle_buffer_size)

model = tf.keras.models.Sequential([
    tf.keras.layers.Dense(10, input_shape=[window_shape], activation='relu'),
    tf.keras.layers.Dense(10, activation='relu'),
    tf.keras.layers.Dense(1)
])

lr_schedule = tf.keras.callbacks.LearningRateScheduler(lambda epoch: 1e-8 * 10**(epoch/20))
optimizer = tf.keras.optimizers.SGD(learning_rate=1e-8, momentum=0.9)

model.compile(loss='mse', optimizer=optimizer)
history = model.fit(dataset, epochs=100, callbacks=[lr_schedule])

- 각 epoch가 끝날 때마다 호출되는 기능

- epoch 수에 따라 learning rate 를 수정

 

<시각화하기> - learning rate와 loss

lrs = 1e-8 * (10**(np.arange(100)/20))
plt.semilogx(lrs, history.history['loss'])

x : learning rate / y : epoch

위 그래프에서 안정적이었던 구간 선택 --> 7e - 6

 

dataset = windowed_dataset(x_train, window_size, batch_size, shuffle_buffer_size)

model = tf.keras.models.Sequential([
    tf.keras.layers.Dense(10, input_shape=[window_shape], activation='relu'),
    tf.keras.layers.Dense(10, activation='relu'),
    tf.keras.layers.Dense(1)
])

model.compile(loss='mse', optimizer=tf.keras.optimizers.SGD(learning_rate=7e-6, momentum=0.9))
model.fit(dataset, epochs=500, verbose=0)

첫 10 epoch는 날린 그래프

 

3주차

RNN

inputs are 3-dimensional

 

window size : 30 time stpes

batch size : 4

--> shape : 4 x 30 x 1

 

memory cell 의 각 input shape : 4 x 1 

 

만약 memory cell 이 3개 unit 으로 구성되어 있다면

각 output shape : 4 x 3

--> 전체 output shape : 4 x 30 x 3

- 4 : batch size

- 30 : time steps

- 3 : units

 

만약 output으로 각 batch에 대한 single 값 -> 하나의 vector 를 받고 싶다면? (seq2vec RNN)

-> 다른 output sequence 무시하고 마지막 output만 받아오면 됨 

- keras에서 이것은 default behaviour 이므로, 만약 sequence output을 받고 싶다면 return_sequences=True 세팅

- 다른 레이어 위에 RNN 쌓을 때 return_sequence 세팅해야 함

 

model = keras.models.Sequential([
    keras.layers.SimpleRNN(20, return_sequences=True, input_shape=[None, 1]),
    keras.layers.SimpleRNN(20),
    keras.layers.Dense(1)
])

- 첫번째 RNN은 sequence를 output으로 다음 레이어에 전달함

- input_shape = [None, 1]

     - tensorflow에서 첫번째는 batch size - 어떤 사이즈가 될 수 있으므로 define할 필요 없음

     - 다음 차원은 the number of time steps - 어떤 길이의 sequence든 RNN이 받을 수 있으므로 None으로 설정

     - 마지막 차원은 1 - because we are using a univariate time series

 

model = keras.models.Sequential([
    keras.layers.SimpleRNN(20, return_sequences=True, input_shape=[None, 1]),
    keras.layers.SimpleRNN(20, return_sequences=True),
    keras.layers.Dense(1)
])

만약 둘다 return_sequence 세팅하면?

그 다음 Dense layer는 sequence를 입력받게 됨

(동일한 dense layer가 time step마다 재사용되고 있는 것임)

 

 

<lambda layer 사용하기>

model = keras.models.Sequential([
    keras.layers.Lamdba(lambda x: tf.expand_dims(x, axis=-1), input_shape=[None])
    keras.layers.SimpleRNN(20, return_sequences=True),
    keras.layers.SimpleRNN(20),
    keras.layers.Dense(1)
    keras.layers.Lambda(lambda x: x * 100.0)
])

- 첫번째 lamdba layer

     - windowed_dataset 함수가 출력하는 것은 batch size x time steps 의 2차원 데이터

     - 하지만 RNN은 3차원 입력을 기대한다 (세번째 차원 series dimensionality)

     - lambda 레이어를 써서, array 를 한 차원 더 늘려준다

     - input_shape = [None] : 어떤 길이의 시퀀스라도 받을 수 있음

- 두번째 lambda layer

     - output을 100배 scale up

     - RNN에서 사용하는 기본 activation은 tanh함수(-1~1)를 사용하므로

 

train_set = windowed_dataset(x_train, window_size, batch_size=128, shuffle_buffer=shuffle_buffer_size)

model = tf.keras.models.Sequential([
    tf.keras.layers.Lambda(lamdba x: tf.expand_dims(x, axis=-1), input_shape=[None]),
    tf.keras.layers.SimpleRNN(40, return_sequences=True),
    tf.keras.layers.SimpleRNN(40),
    tf.keras.layers.Dense(1),
    tf.keras.layers.Lambda(lamdba x: x * 100.0)
])

lr_schedule = tf.keras.callbacks.LearningRateScheduler(lamdba epoch: 1e-8 * (10**(epoch/20)))
optimizer = tf.keras.optimizers.SGD(learning_rate=1e-8, momentum=0.9)

model.compile(loss=tf.keras.losses.Huber(),
              optimizer=optimizer,
              metrics=['mae'])

history = model.fit(train_set, epochs=100, callbacks=[lr_schedule])

*Huber loss : outlier에 덜 민감

 

100 epoch 정도 돌려보았을 때 최적의 learning rate 찾을 수 있음

optimizer = tf.keras.optimizers.SGD(learning_rate=5e-6, momentum=0.9)

model.compile(loss=tf.keras.losses.Huber(),
              optimizer=optimizer,
              metrics=['mae'])

history = model.fit(train_set, epochs=100)

epoch 400 이하로 훈련시키는 것이 좋음

 

 

<LSTM>

LSTM 은 cell state를 추가해서 학습하는 동안 state 유지

 

tf.keras.backend.clear_session()
dataset = windowed_dataset(x_train, window_size, batch_size, shuffle_buffer_size)

model = tf.keras.models.Sequential([
    tf.keras.layers.Lambda(lambda x: tf.expand_dim(x, axis=-1), input_shape=[None]),
    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(32, return_sequences=True)),
    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(32)),
    tf.keras.layers.Dense(1),
    tf.keras.layers.Lambda(lambda x: x * 100.0)
])

model.compile(loss='mse', optimizer=tf.keras.optimizers.SGD(learning_rate=1e-6, momentum=0.9))
model.fit(dataset, epochs=100, verbose=0)

- tf.keras.backend.clear_session() : internal variables 클리어

 

학습 결과

 

과제

def create_uncompiled_model():

    ### START CODE HERE
    
    model = tf.keras.models.Sequential([ 
        tf.keras.layers.Lambda(lambda x: tf.expand_dims(x, axis=-1), input_shape=[None]),
        tf.keras.layers.SimpleRNN(40, return_sequences=True),
        tf.keras.layers.SimpleRNN(40),
        tf.keras.layers.Dense(1),
        tf.keras.layers.Lambda(lambda x : x * 100.0)
    ]) 
    
    ### END CODE HERE

    return model

# Test your uncompiled model
uncompiled_model = create_uncompiled_model()

try:
    uncompiled_model.predict(dataset)
except:
    print("Your current architecture is incompatible with the windowed dataset, try adjusting it.")
else:
    print("Your current architecture is compatible with the windowed dataset! :)")
def adjust_learning_rate():
    
    model = create_uncompiled_model()
    
    lr_schedule = tf.keras.callbacks.LearningRateScheduler(lambda epoch: 1e-6 * 10**(epoch / 20))
    
    ### START CODE HERE
    
    # Select your optimizer
    optimizer = tf.keras.optimizers.SGD(learning_rate=1e-6, momentum=0.9)
    
    # Compile the model passing in the appropriate loss
    model.compile(loss=tf.keras.losses.Huber(),
                  optimizer=optimizer, 
                  metrics=["mae"]) 
    
    ### END CODE HERE
    
    history = model.fit(dataset, epochs=100, callbacks=[lr_schedule])
    
    return history
    
    
lr_history = adjust_learning_rate()

# Plot the loss for every LR
plt.semilogx(lr_history.history["lr"], lr_history.history["loss"])
plt.axis([1e-6, 1, 0, 30])

def create_model():

    tf.random.set_seed(51)
    
    model = create_uncompiled_model()

    ### START CODE HERE

    model.compile(loss=tf.keras.losses.Huber(),
                  optimizer=tf.keras.optimizers.SGD(learning_rate=5e-6, momentum=0.9),
                  metrics=["mae"])  
    
    ### END CODE HERE

    return model
    
# Save an instance of the model
model = create_model()

# Train it
history = model.fit(dataset, epochs=50)

 

4주차

model = tf.keras.models.Sequential([
    tf.keras.layers.Conv1D(filters=32,
                           kernel_size=5, 
                           strides=1, 
                           padding='casual',
                           activation='relu',
                           input_shape=[None, 1]),
    tf.keras.layers.LSTM(32, return_sequences=True),
    tf.keras.layers.LSTM(32),
    tf.keras.layers.Dense(1),
    tf.keras.layers.Lambda(lamdba x : x*200)
])

optimizer = tf.keras.optimizers.SGD(learning_rate=1e-5, momentum=0.9)
model.compile(loss=tf.keras.losses.Huber(),
              optimizer=optimizer,
              metrics=['mae'])
model.fit(dataset, epochs=500)

- LSTM에 입력될 데이터의 차원을 바꿔주던 lambda 레이어 대신에 Conv1D

     - input_shape = [None, 1] : input_shape specify 하고 있음 

 

 

조금 더 오래 학습해보거나, Bidirectional LSTM 으로 수정해보기

 

model = tf.keras.models.Sequential([
    tf.keras.layers.Conv1D(filters=32,
                           kernel_size=5, 
                           strides=1, 
                           padding='casual',
                           activation='relu',
                           input_shape=[None, 1]),
    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(32, return_sequences=True)),
    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(32)),
    tf.keras.layers.Dense(1),
    tf.keras.layers.Lambda(lamdba x : x*200)
])

작은 batch들이 가져오는 randomness 때문에 noise 발생하는 문제 -> 다양한 batch size로 실험해볼 수 있음

 

 

<real-world 데이터 적용해보기>

 

import csv

time_step = []
sunspots = []

with open('/tmp/sunspots.csv') as csvfile:
    reader = csv.reader(csvfile, delimiter=',')
    next(reader) # column title
    for row in reader:
        sunspots.append(float(row[2]))
        time_step.append(int(row[0]))
        
series = np.array(sunspots)
time = np.array(time_step)

 

trend 없고, noise 살짝 있고, seasonality 있음

 

split_time = 1000
time_train = time[:split_time]
x_train = series[:split_time]
time_valid = time[split_time:]
x_valid = series[split_time:]

window_size = 20
batch_size =32
shuffle_buffer_size = 1000

 

def windowed_dataset(series, window_size, batch_size, shuffle_buffer):
    dataset = tf.data.Dataset.from_tensor_slices(series)
    dataset = dataset.window(window_size + 1, shift=1, drop_remainder=True),
    dataset = dataset.flat_map(lamda window: window.batch(window_size+1))
    dataset = dataset.shuffle(shuffle_buffer).map(lambda window: (window[:-1], window[-1]))
    dataset = dataset.batch(batch_size).prefetch(1)
    return dataset

 

먼저 DNN

 

dataset = windowed_dataset(x_train, window_size, batch_size, shuffle_buffer_size)

model = tf.keras.models.Sequential([
    tf.keras.layers.Dense(10, input_shape=[window_size], activation='relu'),
    tf.keras.layers.Dense(10, activation='relu'),
    tf.keras.layers.Dense(1)
])

model.compile(loss='mse', optimizer=tf.keras.optimizers.SGD(learning_rate=1e-6, momentum=0.9))
model.fit(dataset, epochs=100, verbose=0)

 

- window size가 문제는 아닐까?

지금은 20으로 셋팅되어 있는데, 20 time step 이면 위 데이터 기준으로 2년 좀 안 되는 주기.

하지만 위 데이터의 seasonality를 생각해보면 (약 11년 주기)... 그러니까 132로 설정해보면 어떨까?

 

--> mae 더 안 좋아짐. 왜? window 안에 full season이 필요한 것은 아님.

한 30정도로만 높이기

 

- split_time = 1000 이므로 고작 1000개 데이터로 학습시키고 있음

3000으로 늘리기

 

개선된 모습

 

- 신경망 아키텍처를 수정해보는 건 어떨까? learning rate는?

 

 

prediction

model.predict(series[3205:3235][np.newaxis])

마지막 30 time step을 input으로 넘겨줌

 

 

가장 예측 잘 나온 구조 및 하이퍼파라미터

split_time = 3000
window_size = 60

model = tf.keras.models.Sequential([
    tf.keras.layers.Dense(20, input_shape=[window_size], activation='relu'),
    tf.keras.layers.Dense(10, activation='relu'),
    tf.keras.layers.Dense(1)
])

model.compile(loss='mse', optimizer=tf.keras.optimizers.SGD(lr=1e-7, momentum=0.9))

 

아는 거 다 갖다 써볼까?

window_size = 60
batch_size = 64
train_set = windowed_dataset(x_train, window_size, batch_size, shuffle_buffer_size)

model = tf.keras.models.Sequential([
    tf.keras.layers.Conv1D(filters=32, 
                           kernel_size=5, 
                           strides=1, 
                           padding='casual', 
                           activation='relu',
                           input_shape=[None, 1])
    tf.keras.layers.LSTM(32, return_sequences=True),
    tf.keras.layers.LSTM(32)
    tf.keras.layers.Dense(30, input_shape=[window_size], activation='relu'),
    tf.keras.layers.Dense(10, activation='relu'),
    tf.keras.layers.Dense(1)
    tf.keras.layers.Lambda(lambda x: x*400)
])

lr_schedule = tf.keras.callbacks.LearningRateScheduler(lambda epoch: 1e-8 * 10**(epoch/20))
optimizer = tf.keras.optimizer.SGD(learning_rate=1e-8, momentum=0.9)

model.compile(loss=tf.keras.losses.Huber(), optimizer=optimizer, metrics=['mse'])
history = model.fit(train_set, epochs = 100, callbacks=[lr_schedule])

-> 최적의 learning rate 1e-5 로 설정하고 epoch 500으로 재학습

 

튀고 있으므로 batch_size 256으로 증가했을 때 조금 나아지는 모습

 

 

데이터 개수가 3000 이니까 딱 맞도록 window size 60, batch size 250, conv1d 필터수 및 LSTM 셀수 60으로 맞춰보는 건?

window_size = 60
batch_size = 250
train_set = windowed_dataset(x_train, window_size, batch_size, shuffle_buffer_size)

model = tf.keras.models.Sequential([
    tf.keras.layers.Conv1D(filters=60, 
                           kernel_size=5, 
                           strides=1, 
                           padding='casual', 
                           activation='relu',
                           input_shape=[None, 1])
    tf.keras.layers.LSTM(60, return_sequences=True),
    tf.keras.layers.LSTM(60)
    tf.keras.layers.Dense(30, input_shape=[window_size], activation='relu'),
    tf.keras.layers.Dense(10, activation='relu'),
    tf.keras.layers.Dense(1)
    tf.keras.layers.Lambda(lambda x: x*400)
])

optimizer = tf.keras.optimizer.SGD(learning_rate=1e-5, momentum=0.9)

model.compile(loss=tf.keras.losses.Huber(), optimizer=optimizer, metrics=['mse'])
history = model.fit(train_set, epochs = 500)

... 이런 식으로 계속 hyperparameter 실험을 해보세요

 

 

과제

def create_uncompiled_model():

    ### START CODE HERE
    
    model = tf.keras.models.Sequential([
        tf.keras.layers.Conv1D(32, 5, padding='causal', activation='relu', input_shape=[None, 1]),
        tf.keras.layers.LSTM(32, return_sequences=True),
        tf.keras.layers.LSTM(32),
        tf.keras.layers.Dense(30, input_shape=[64], activation='relu'),
        tf.keras.layers.Dense(10, activation='relu'),
        tf.keras.layers.Dense(1)
    ]) 
    
    ### END CODE HERE

    return model
복사했습니다!