Thursday, June 20, 2019

Tentang layer TimeDistributed pada Keras...

TimeDistributed merupakan salah satu (wrapper) layer pada keras yang berfungsi untuk membagi weight pada slice temporal pada data 3D. Susah difahami? Perhatikan contoh berikut.

Saya punya data X. Ukurannya (32, 10, 16), seperti contoh pada dokumentasi Keras.

x = np.random.random(size=(32, 10, 16)

Artinya, saya punya 32 data (sample). Setiap sample berisi data (10, 16): 10 baris dan 16 kolom. Ketika saya mengaplikasikan suatu layer (katakanlah, dan paling umum: Dense), agar bobot dari satu layer tersebut merata pada setiap elemen maka perlu kita gunakan timedistributed layer ini.

Perhatikan contoh berikut,
model0 = Sequential()
model0.add(Dense(8, input_shape=(10, 16)))
model0.summary()
Hasilnya adalah sebagai berikut,
---
Model: "sequential_6"
_________________________________________________________________
Layer (type)    Output Shape    Param # 
=================================================================
time_distributed_1 (TimeDist (None, 10, 8)    136 
=================================================================
Total params: 136
Trainable params: 136
Non-trainable params: 0
Bedakan dengan ini,
model1 = Sequential()
model1.add(Dense(8, input_shape=(10, 16)))
model1.summary()
Hasilnya,
---
Model: "sequential_5"
_________________________________________________________________
Layer (type)  Shape  Param # 
=================================================================
dense_3 (Dense)    (None, 10, 8)    136 
=================================================================
Total params: 136
Trainable params: 136
Apa perbedaannya? Tidak ada. Saya juga bingung. Mari kita cari contoh lain, dari machinelearningmastery.
Sebenarnya, jika kita teliti, ada perbedaan bentuk output shape dari kedua contoh diatas, yang pertama memberikan bentuk ouput (TimeDist(None, 8, 10)), sedangkan yang kedua hanya (None, 8, 10).

Perhatikan contoh panjang berikut. Input dan output adalah sama, yakni [0 0.2 0.4 0.6 0.8], bayangkan sistem echo yang memberikan output yang sama dengan inputnya namun dengan time delay. Bedakan blok fungsi build_model() dengan build_tdmodel().

# keras demo timeditributed
import numpy as np

from keras.models import Sequential
from keras.layers import Dense
from keras.layers import TimeDistributed
from keras.layers import LSTM

# prepare sequence
length = 5
seq = np. array([i/float(length) for i in range(length)])
X = seq.reshape(1, length, 1)
y = seq.reshape(1, length, 1)

# define LSTM configuration
n_neurons = length
n_batch = 1
n_epoch = 1000

# create LSTM without timedistributed: many-to-one
def build_model():
    model = Sequential()
    model.add(LSTM(n_neurons, input_shape=(length, 1)))
    model.add(Dense(length))
    model.compile(loss='mean_squared_error', optimizer='adam')
    
    return model

without_td = build_model()
print(without_td.summary())

# train LSTM
without_td.fit(X, y.reshape(1,5), epochs=n_epoch, batch_size=n_batch, verbose=2)
# evaluate
result_wtd = without_td.predict(X, batch_size=n_batch, verbose=0)
for value in result_wtd[0,:]:
 print('%.1f' % value)

    
# create LSTM with timedistibuted: many-to-many
def build_tdmodel():
    model = Sequential()
    model.add(LSTM(n_neurons, input_shape=(length, 1), return_sequences=True))
    model.add(TimeDistributed(Dense(1)))
    model.compile(loss='mean_squared_error', optimizer='adam')
    
    return model

model = build_tdmodel()
print(model.summary())

# train LSTM
model.fit(X, y, epochs=n_epoch, batch_size=n_batch, verbose=2)

# evaluate
result = model.predict(X, batch_size=n_batch, verbose=0)

print('Output: ')
for value in result[0,:,0]:
 print('%.1f' % value)

Jika kita cetak summary-nya adalah sbb:
Tanpa timedistributed:
Model: "sequential_5"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
lstm_5 (LSTM)                (None, 5)                 140       
_________________________________________________________________
dense_5 (Dense)              (None, 5)                 30        
=================================================================
Total params: 170
Trainable params: 170
Non-trainable params: 0
_________________________________________________________________
None

Dengan timedistributed:
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
lstm_2 (LSTM)                (None, 5, 5)              140       
_________________________________________________________________
time_distributed_1 (TimeDist (None, 5, 1)              6         
=================================================================
Total params: 146
Trainable params: 146
Non-trainable params: 0
_________________________________________________________________
None

Sekarang perbedaanya terlihat lebih jelas. Selain jumlah trainable parameter yang lebih sedikit, timedtributed juga lebih compact. Menghemat ruang dan waktu komputasi. Jadi dengan timedistributed,
- Operasi dilakukan per frame
- Hemat ruang dan waktu (memory efficient)

Input - output timedistibuted:
- input: 3D (seperti halnya LSTM) --> perhatikan input X yang direshape menjadi 3D
- output: juga 3D --> sama, Y juga direshape menjadi 3D

Perbedaan format dan pengolahan data input-output di atas adalah perbedaan mendasar dari timedistributed. Untuk mengefisienkan komputasi, maka digunakan timedistributed, "which allows to apply a layer to every temporal slice of an input.". Artinya, CMIIW, setiap temporal slice akan diaplikasikan dense layer (dengan unit 1 pada contoh kasus diatas). Jadi kita tidak perlu mendefinisikan dense layer pada tiap temporal slice, cukup dengan timedistributed ini saja. Karena itu diperlukan matriks 3D sebagi input, begitu pula outputnya.


Jadi, dua fungsi build_model() dan build_tdmodel() di atas menghasilkan dua arsitektur yang sama secara konsep namun berbeda pendekatan. Yang pertama adalah many-to-one (tanpa timedistributed), yang kedua adalah many-to-many (dengan timedistributed). Gambar terakhir di bawah ini menjelaskan timedistributed pada berbagai architecture (blok hijau).

Terakhir, ada penjelasan sederhana yang saya ambil dari diskusi di channel Slack keras seperti di bawah ini, semoga bisa menambah pemahaman kita.

The timedistributed dense layer is used on RNNs and LSTMSs to keep one-to-one relations on the input and output. Assume you have 100 time steps with 200 samples of data (100 x 200 in another word) and you want to use RNN with output of 300. If you don't have a timedistributed dense layer, you will get a 200 x 100 x 300 tensor with the output flattened with each timestep; however, if you apply timedistributed, you get a fully connected dense on each time step and get output separately by timesteps.
Referensi:
- https://machinelearningmastery.com/timedistributed-layer-for-long-short-term-memory-networks-in-python/
- https://github.com/keras-team/keras/issues/1029

1 comment:

  1. Akan lebih jelas bila ditambahkan gambar visualisasi apa itu timedistributed...

    ReplyDelete

Your comments here/Silahkan komentar disini...

Related Posts Plugin for WordPress, Blogger...