Recurrent Neural Networks (RNN) dengan Keras

Terjemahan dari Panduan Jaringan Syaraf Tiruan Rekursif dari Tensorflow.org. Materi tersebut membahas kemampuan bawaan Keras / Tensorflow 2.0 untuk penyambungan cepat, serta kemungkinan menyesuaikan lapisan dan sel. Kasus dan keterbatasan penggunaan inti CuDNN juga dipertimbangkan, yang memungkinkan untuk mempercepat proses pembelajaran jaringan saraf.



Jaringan saraf rekursif (RNN) adalah kelas jaringan saraf yang baik untuk memodelkan data serial, seperti deret waktu atau bahasa alami.

Jika secara skematis, layer RNN menggunakan loop foruntuk mengulangi urutan waktu, sementara menyimpan dalam keadaan internal, menyandikan informasi tentang langkah-langkah yang telah dilihatnya.

Keras RNN API dirancang dengan fokus pada:

Kemudahan penggunaan : built-in lapisan tf.keras.layers.RNN, tf.keras.layers.LSTM, tf.keras.layers.GRUmemungkinkan Anda dengan cepat membangun model rekursif tanpa harus membuat pengaturan konfigurasi yang rumit.

Kustomisasi yang mudah : Anda juga dapat menentukan lapisan sel RNN Anda sendiri (bagian dalam loop)for) dengan perilaku khusus dan menggunakannya dengan lapisan umum `tf.keras.layers.RNN` (the` for` loop itu sendiri). Ini akan memungkinkan Anda dengan cepat membuat prototipe berbagai ide penelitian dengan cara yang fleksibel, dengan kode minimal.

Instalasi


from __future__ import absolute_import, division, print_function, unicode_literals

import collections
import matplotlib.pyplot as plt
import numpy as np

import tensorflow as tf

from tensorflow.keras import layers

Membangun model yang sederhana


Keras memiliki tiga lapisan RNN bawaan:

  1. tf.keras.layers.SimpleRNN, RNN yang terhubung penuh di mana output dari langkah waktu sebelumnya harus diteruskan ke langkah berikutnya.
  2. tf.keras.layers.GRU, pertama kali diusulkan dalam artikel Mempelajari frasa menggunakan RNN codec untuk terjemahan mesin statistik
  3. tf.keras.layers.LSTM, pertama kali diusulkan dalam artikel Memori Jangka Pendek Jangka Panjang

Pada awal 2015, Keras memperkenalkan Python dan LSTM dan GRU implementasi open source pertama yang dapat digunakan kembali.

Berikut ini adalah contoh dari Sequentialmodel yang memproses urutan bilangan bulat dengan menumpuk setiap bilangan bulat dalam vektor 64 dimensi, lalu memproses urutan vektor menggunakan lapisan LSTM.

model = tf.keras.Sequential()
#   Embedding      1000, 
#     64.
model.add(layers.Embedding(input_dim=1000, output_dim=64))

#   LSTM  128  .
model.add(layers.LSTM(128))

#   Dense  10    softmax.
model.add(layers.Dense(10))

model.summary()

Output dan Status


Secara default, output dari layer RNN berisi satu vektor per elemen. Vektor ini adalah output dari sel RNN terakhir yang berisi informasi tentang seluruh urutan input. Dimensi output ini (batch_size, units), di mana unitssesuai dengan argumen yang unitsditeruskan ke konstruktor lapisan.

Lapisan RNN juga dapat mengembalikan seluruh urutan output untuk setiap elemen (satu vektor untuk setiap langkah), jika Anda menentukan return_sequences=True. Dimensi dari output ini adalah (batch_size, timesteps, units).

model = tf.keras.Sequential()
model.add(layers.Embedding(input_dim=1000, output_dim=64))

#  GRU  3D   (batch_size, timesteps, 256)
model.add(layers.GRU(256, return_sequences=True))

#  SimpleRNN  2D   (batch_size, 128)
model.add(layers.SimpleRNN(128))

model.add(layers.Dense(10))

model.summary()

Selain itu, layer RNN dapat mengembalikan status internal terakhirnya.

Status yang dikembalikan dapat digunakan nanti untuk melanjutkan eksekusi RNN atau untuk menginisialisasi RNN lainnya . Pengaturan ini biasanya digunakan dalam model encoder-decoder, urutan ke urutan, di mana keadaan akhir dari pembuat kode digunakan untuk keadaan awal dekoder.

Agar lapisan RNN mengembalikan keadaan internal, atur parameter return_stateke nilai Truesaat membuat lapisan. Perhatikan bahwa ada LSTM2 tensor status, dan GRUhanya satu.

Untuk menyesuaikan keadaan awal layer, cukup panggil layer dengan argumen tambahan initial_state.

Perhatikan bahwa dimensi harus cocok dengan dimensi elemen lapisan, seperti dalam contoh berikut.

encoder_vocab = 1000
decoder_vocab = 2000

encoder_input = layers.Input(shape=(None, ))
encoder_embedded = layers.Embedding(input_dim=encoder_vocab, output_dim=64)(encoder_input)

#       
output, state_h, state_c = layers.LSTM(
    64, return_state=True, name='encoder')(encoder_embedded)
encoder_state = [state_h, state_c]

decoder_input = layers.Input(shape=(None, ))
decoder_embedded = layers.Embedding(input_dim=decoder_vocab, output_dim=64)(decoder_input)

#  2     LSTM    
decoder_output = layers.LSTM(
    64, name='decoder')(decoder_embedded, initial_state=encoder_state)
output = layers.Dense(10)(decoder_output)

model = tf.keras.Model([encoder_input, decoder_input], output)
model.summary()

Lapisan RNN dan sel RNN


API RNN, selain lapisan RNN bawaan, juga menyediakan API tingkat sel. Tidak seperti lapisan RNN, yang memproses seluruh paket urutan input, sel RNN hanya memproses satu langkah waktu.

Sel berada di dalam siklus forlapisan RNN. Membungkus sel dengan lapisan tf.keras.layers.RNNmemberi Anda lapisan yang mampu memproses paket urutan, mis. RNN(LSTMCell(10)).

Secara matematis, RNN(LSTMCell(10))ini memberikan hasil yang sama dengan LSTM(10). Sebenarnya, implementasi layer ini di dalam TF v1.x hanya untuk membuat sel RNN yang sesuai dan membungkusnya dalam lapisan RNN. Namun, penggunaan embedded layer GRUdan LSTMmemungkinkan penggunaan CuDNN yang dapat memberi Anda kinerja yang lebih baik.

Ada tiga sel RNN bawaan, masing-masing sesuai dengan lapisan RNN-nya sendiri.

  • tf.keras.layers.SimpleRNNCellcocok dengan layer SimpleRNN.
  • tf.keras.layers.GRUCellcocok dengan layer GRU.
  • tf.keras.layers.LSTMCellcocok dengan layer LSTM.

Abstraksi sel bersama-sama dengan kelas umum tf.keras.layers.RNNmembuatnya sangat mudah untuk mengimplementasikan arsitektur RNN khusus untuk penelitian Anda.

Status penyimpanan lintas batch


Saat memproses urutan panjang (mungkin tanpa akhir), Anda mungkin ingin menggunakan pola status lintas-batch .

Biasanya, keadaan internal lapisan RNN diatur ulang dengan setiap paket data baru (mis. Setiap contoh yang melihat lapisan diasumsikan independen dari masa lalu). Lapisan akan mempertahankan status hanya selama pemrosesan elemen ini.

Namun, jika Anda memiliki urutan yang sangat panjang, akan berguna untuk memecahnya menjadi yang lebih pendek dan mentransfernya ke lapisan RNN secara bergantian tanpa mengatur ulang keadaan lapisan. Dengan demikian, sebuah layer dapat menyimpan informasi tentang seluruh urutan, meskipun hanya akan melihat satu urutan pada satu waktu.

Anda dapat melakukan ini dengan menetapkan `stateful = True` di konstruktor.

Jika Anda memiliki urutan `s = [t0, t1, ... t1546, t1547]`, Anda dapat membaginya misalnya menjadi:

s1 = [t0, t1, ... t100]
s2 = [t101, ... t201]
...
s16 = [t1501, ... t1547]

Kemudian Anda dapat memprosesnya dengan:

lstm_layer = layers.LSTM(64, stateful=True)
for s in sub_sequences:
  output = lstm_layer(s)

Saat Anda ingin membersihkan kondisinya, gunakan layer.reset_states().
Catatan: Dalam hal ini, diasumsikan bahwa contoh idalam paket ini adalah kelanjutan dari contoh ipaket sebelumnya. Ini berarti bahwa semua paket berisi jumlah elemen yang sama (ukuran paket). Misalnya, jika paket berisi [sequence_A_from_t0_to_t100, sequence_B_from_t0_to_t100], paket selanjutnya harus berisi [sequence_A_from_t101_to_t200, sequence_B_from_t101_to_t200].
Ini adalah contoh lengkapnya:

paragraph1 = np.random.random((20, 10, 50)).astype(np.float32)
paragraph2 = np.random.random((20, 10, 50)).astype(np.float32)
paragraph3 = np.random.random((20, 10, 50)).astype(np.float32)

lstm_layer = layers.LSTM(64, stateful=True)
output = lstm_layer(paragraph1)
output = lstm_layer(paragraph2)
output = lstm_layer(paragraph3)

# reset_states()      initial_state.
#  initial_state   ,      .
lstm_layer.reset_states()

RNN dua arah


Untuk urutan selain seri waktu (mis. Teks), sering terjadi bahwa model RNN berfungsi lebih baik jika memproses urutan tidak hanya dari awal hingga akhir, tetapi juga sebaliknya. Misalnya, untuk memprediksi kata berikutnya dalam sebuah kalimat, seringkali berguna untuk mengetahui konteks di sekitar kata tersebut, dan bukan hanya kata-kata di depannya.

Keras menyediakan API sederhana untuk membuat RNNs dua arah: pembungkus tf.keras.layers.Bidirectional.

model = tf.keras.Sequential()

model.add(layers.Bidirectional(layers.LSTM(64, return_sequences=True), 
                               input_shape=(5, 10)))
model.add(layers.Bidirectional(layers.LSTM(32)))
model.add(layers.Dense(10))

model.summary()

Di bawah penutup, Bidirectionallapisan RNN yang ditransfer go_backwardsakan disalin dan bidang lapisan yang baru disalin akan dibalik , dan dengan demikian data input akan diproses dalam urutan terbalik.

Output dari ` BidirectionalRNN secara default akan menjadi jumlah dari output dari lapisan maju dan output dari lapisan balik. Jika Anda membutuhkan perilaku penggabungan lainnya, mis. gabungkan, ubah parameter `merge_mode` dalam konstruktor wrapper` Bidirectional`.

Optimalisasi Kinerja dan Core CuDNN di TensorFlow 2.0


Dalam TensorFlow 2.0, lapisan LSTM dan GRU bawaan dapat digunakan secara default pada inti CuDNN jika prosesor grafis tersedia. Dengan perubahan ini, lapisan sebelumnya keras.layers.CuDNNLSTM/CuDNNGRUsudah usang, dan Anda dapat membangun model Anda tanpa khawatir tentang peralatan yang akan bekerja.

Karena kernel CuDNN dibangun dengan beberapa asumsi, ini berarti bahwa layer tidak akan dapat menggunakan layer kernel CuDNN jika Anda mengubah pengaturan default dari layer LSTM atau GRU bawaan . Misalnya.

  • Mengubah fungsi activationdari tanhke yang lain.
  • Mengubah fungsi recurrent_activationdari sigmoidke yang lain.
  • Penggunaan recurrent_dropout> 0.
  • Menyetelnya unrollke True, yang menyebabkan LSTM / GRU untuk menguraikan internal tf.while_loopmenjadi loop dikerahkan for.
  • Setel use_biaske Salah.
  • Menggunakan topeng ketika data input tidak dibenarkan (jika topeng cocok dengan data yang benar-benar selaras, CuDNN masih dapat digunakan. Ini adalah kasus yang paling umum).

Jika memungkinkan gunakan kernel CuDNN


batch_size = 64
#    MNIST    (batch_size, 28, 28).
#     (28, 28) (   ).
input_dim = 28

units = 64
output_size = 10  #   0  9

#  RNN 
def build_model(allow_cudnn_kernel=True):
  # CuDNN     ,     .
  #   `LSTM(units)`    CuDNN,
  #   RNN(LSTMCell(units))   non-CuDNN .
  if allow_cudnn_kernel:
    #  LSTM      CuDNN.
    lstm_layer = tf.keras.layers.LSTM(units, input_shape=(None, input_dim))
  else:
    #  LSTMCell  RNN    CuDNN.
    lstm_layer = tf.keras.layers.RNN(
        tf.keras.layers.LSTMCell(units),
        input_shape=(None, input_dim))
  model = tf.keras.models.Sequential([
      lstm_layer,
      tf.keras.layers.BatchNormalization(),
      tf.keras.layers.Dense(output_size)]
  )
  return model

Memuat dataset MNIST


mnist = tf.keras.datasets.mnist

(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
sample, sample_label = x_train[0], y_train[0]

Buat instance dari model dan kompilasi


Kami telah memilih sparse_categorical_crossentropysebagai fungsi kerugian. Output dari model memiliki dimensi [batch_size, 10]. Jawaban dari model adalah vektor integer, masing-masing angka berada dalam kisaran dari 0 hingga 9.

model = build_model(allow_cudnn_kernel=True)

model.compile(loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), 
              optimizer='sgd',
              metrics=['accuracy'])

model.fit(x_train, y_train,
          validation_data=(x_test, y_test),
          batch_size=batch_size,
          epochs=5)

Bangun model baru tanpa inti CuDNN


slow_model = build_model(allow_cudnn_kernel=False)
slow_model.set_weights(model.get_weights())
slow_model.compile(loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), 
                   optimizer='sgd', 
                   metrics=['accuracy'])
slow_model.fit(x_train, y_train, 
               validation_data=(x_test, y_test), 
               batch_size=batch_size,
               epochs=1)  #         .

Seperti yang Anda lihat, model yang dibangun dengan CuDNN jauh lebih cepat untuk pelatihan daripada model yang menggunakan inti TensorFlow biasa.

Model yang sama dengan dukungan CuDNN dapat digunakan untuk output dalam lingkungan prosesor tunggal. Anotasi tf.devicehanya menunjukkan perangkat yang digunakan. Model akan berjalan secara default pada CPU jika GPU tidak tersedia.

Anda tidak perlu khawatir tentang perangkat keras yang sedang Anda kerjakan. Bukankah itu keren?

with tf.device('CPU:0'):
  cpu_model = build_model(allow_cudnn_kernel=True)
  cpu_model.set_weights(model.get_weights())
  result = tf.argmax(cpu_model.predict_on_batch(tf.expand_dims(sample, 0)), axis=1)
  print('Predicted result is: %s, target result is: %s' % (result.numpy(), sample_label))
  plt.imshow(sample, cmap=plt.get_cmap('gray'))

RNN dengan input daftar / kamus, atau input bersarang


Struktur bersarang memungkinkan Anda memasukkan lebih banyak informasi dalam satu langkah waktu. Misalnya, bingkai video dapat berisi input audio dan video secara bersamaan. Dimensi data dalam hal ini mungkin:

[batch, timestep, {\"video\": [height, width, channel], \"audio\": [frequency]}]

Dalam contoh lain, data tulisan tangan dapat memiliki koordinat x dan y untuk posisi pena saat ini, serta informasi tekanan. Jadi data dapat direpresentasikan sebagai berikut:

[batch, timestep, {\"location\": [x, y], \"pressure\": [force]}]

Kode berikut membangun contoh sel RNN khusus yang berfungsi dengan input terstruktur tersebut.

Tentukan sel pengguna yang mendukung input / output bersarang


NestedInput = collections.namedtuple('NestedInput', ['feature1', 'feature2'])
NestedState = collections.namedtuple('NestedState', ['state1', 'state2'])

class NestedCell(tf.keras.layers.Layer):

  def __init__(self, unit_1, unit_2, unit_3, **kwargs):
    self.unit_1 = unit_1
    self.unit_2 = unit_2
    self.unit_3 = unit_3
    self.state_size = NestedState(state1=unit_1, 
                                  state2=tf.TensorShape([unit_2, unit_3]))
    self.output_size = (unit_1, tf.TensorShape([unit_2, unit_3]))
    super(NestedCell, self).__init__(**kwargs)

  def build(self, input_shapes):
    # #  input_shape  2 , [(batch, i1), (batch, i2, i3)]
    input_1 = input_shapes.feature1[1]
    input_2, input_3 = input_shapes.feature2[1:]

    self.kernel_1 = self.add_weight(
        shape=(input_1, self.unit_1), initializer='uniform', name='kernel_1')
    self.kernel_2_3 = self.add_weight(
        shape=(input_2, input_3, self.unit_2, self.unit_3),
        initializer='uniform',
        name='kernel_2_3')

  def call(self, inputs, states):
    #     [(batch, input_1), (batch, input_2, input_3)]
    #     [(batch, unit_1), (batch, unit_2, unit_3)]
    input_1, input_2 = tf.nest.flatten(inputs)
    s1, s2 = states

    output_1 = tf.matmul(input_1, self.kernel_1)
    output_2_3 = tf.einsum('bij,ijkl->bkl', input_2, self.kernel_2_3)
    state_1 = s1 + output_1
    state_2_3 = s2 + output_2_3

    output = [output_1, output_2_3]
    new_states = NestedState(state1=state_1, state2=state_2_3)

    return output, new_states

Bangun Model RNN dengan Nested Input / Output


Mari kita membangun model Keras yang menggunakan layer tf.keras.layers.RNNdan sel khusus yang baru saja kita tentukan.

unit_1 = 10
unit_2 = 20
unit_3 = 30

input_1 = 32
input_2 = 64
input_3 = 32
batch_size = 64
num_batch = 100
timestep = 50

cell = NestedCell(unit_1, unit_2, unit_3)
rnn = tf.keras.layers.RNN(cell)

inp_1 = tf.keras.Input((None, input_1))
inp_2 = tf.keras.Input((None, input_2, input_3))

outputs = rnn(NestedInput(feature1=inp_1, feature2=inp_2))

model = tf.keras.models.Model([inp_1, inp_2], outputs)

model.compile(optimizer='adam', loss='mse', metrics=['accuracy'])unit_1 = 10
unit_2 = 20
unit_3 = 30

input_1 = 32
input_2 = 64
input_3 = 32
batch_size = 64
num_batch = 100
timestep = 50

cell = NestedCell(unit_1, unit_2, unit_3)
rnn = tf.keras.layers.RNN(cell)

inp_1 = tf.keras.Input((None, input_1))
inp_2 = tf.keras.Input((None, input_2, input_3))

outputs = rnn(NestedInput(feature1=inp_1, feature2=inp_2))

model = tf.keras.models.Model([inp_1, inp_2], outputs)

model.compile(optimizer='adam', loss='mse', metrics=['accuracy'])

Latih model pada data yang dihasilkan secara acak


Karena kami tidak memiliki dataset yang baik untuk model ini, kami menggunakan data acak yang dihasilkan oleh perpustakaan Numpy untuk demonstrasi.

input_1_data = np.random.random((batch_size * num_batch, timestep, input_1))
input_2_data = np.random.random((batch_size * num_batch, timestep, input_2, input_3))
target_1_data = np.random.random((batch_size * num_batch, unit_1))
target_2_data = np.random.random((batch_size * num_batch, unit_2, unit_3))
input_data = [input_1_data, input_2_data]
target_data = [target_1_data, target_2_data]

model.fit(input_data, target_data, batch_size=batch_size)

Dengan sebuah layer, tf.keras.layers.RNNAnda hanya perlu menentukan logika matematika dari satu langkah dalam urutan, dan layer tf.keras.layers.RNNakan menangani iterasi dari urutan untuk Anda. Ini adalah cara yang luar biasa ampuh untuk membuat prototipe RNNs tipe baru dengan cepat (mis. Varian LSTM).

Setelah verifikasi, terjemahan juga akan muncul di Tensorflow.org. Jika Anda ingin berpartisipasi dalam menerjemahkan dokumentasi situs web Tensorflow.org ke dalam bahasa Rusia, silakan hubungi secara pribadi atau komentar. Segala koreksi dan komentar sangat dihargai.

Source: https://habr.com/ru/post/undefined/


All Articles