ช่วยปกป้อง Great Barrier Reef กับ TensorFlow บน Kaggle เข้าร่วมท้าทาย

Recurrent Neural Networks (RNN) กับ Keras

ดูบน TensorFlow.org ทำงานใน Google Colab ดูแหล่งที่มาบน GitHub ดาวน์โหลดโน๊ตบุ๊ค

บทนำ

โครงข่ายประสาทที่เกิดซ้ำ (RNN) เป็นคลาสของโครงข่ายประสาทเทียมที่มีประสิทธิภาพสำหรับการสร้างแบบจำลองข้อมูลลำดับ เช่น อนุกรมเวลาหรือภาษาธรรมชาติ

แผนผังชั้น RNN ใช้ for วงย้ำกว่า timesteps ลำดับในขณะที่ยังคงรักษาสถานะภายในว่าข้อมูลเกี่ยวกับการถอดรหัส timesteps จะได้เห็นเพื่อให้ห่างไกล

Keras RNN API ได้รับการออกแบบโดยเน้นที่:

  • ความสะดวกในการใช้งาน: ในตัว keras.layers.RNN , keras.layers.LSTM , keras.layers.GRU ชั้นช่วยให้คุณได้อย่างรวดเร็วสร้างแบบจำลองที่เกิดขึ้นอีกโดยไม่ต้องสร้างทางเลือกการกำหนดค่ายาก

  • ความสะดวกในการปรับแต่ง: คุณยังสามารถกำหนดชั้นของคุณเองเซลล์ RNN (ส่วนภายในของ for วง) ที่มีพฤติกรรมที่กำหนดเองและใช้กับทั่วไป keras.layers.RNN ชั้น (คน for ห่วงตัวเอง) วิธีนี้ช่วยให้คุณสร้างต้นแบบแนวคิดการวิจัยต่างๆ ได้อย่างรวดเร็วด้วยวิธีที่ยืดหยุ่นโดยใช้โค้ดเพียงเล็กน้อย

ติดตั้ง

import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

เลเยอร์ RNN ในตัว: ตัวอย่างง่ายๆ

มีเลเยอร์ RNN ในตัวสามชั้นใน Keras:

  1. keras.layers.SimpleRNN เป็น RNN อย่างเต็มที่ที่เกี่ยวโยงกันที่ผลลัพธ์จาก timestep ก่อนที่จะป้อนให้กับ timestep ต่อไป

  2. keras.layers.GRU เสนอครั้งแรกใน โช et al. 2014

  3. keras.layers.LSTM เสนอครั้งแรกใน Hochreiter & Schmidhuber 1997

ในต้นปี 2558 Keras มีการนำ Python แบบโอเพ่นซอร์สมาใช้ซ้ำเป็นครั้งแรกของ LSTM และ GRU

นี่คือตัวอย่างง่ายๆของ Sequential รูปแบบที่กระบวนการลำดับของจำนวนเต็มฝังแต่ละจำนวนเต็มเป็นเวกเตอร์ 64 มิติแล้วกระบวนการลำดับของเวกเตอร์โดยใช้ LSTM ชั้น

model = keras.Sequential()
# Add an Embedding layer expecting input vocab of size 1000, and
# output embedding dimension of size 64.
model.add(layers.Embedding(input_dim=1000, output_dim=64))

# Add a LSTM layer with 128 internal units.
model.add(layers.LSTM(128))

# Add a Dense layer with 10 units.
model.add(layers.Dense(10))

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding (Embedding)        (None, None, 64)          64000     
_________________________________________________________________
lstm (LSTM)                  (None, 128)               98816     
_________________________________________________________________
dense (Dense)                (None, 10)                1290      
=================================================================
Total params: 164,106
Trainable params: 164,106
Non-trainable params: 0
_________________________________________________________________

RNN ในตัวรองรับคุณสมบัติที่มีประโยชน์มากมาย:

  • การออกกลางคันกำเริบผ่าน dropout และ recurrent_dropout ข้อโต้แย้ง
  • ความสามารถในการประมวลผลลำดับการป้อนข้อมูลในการย้อนกลับผ่าน go_backwards โต้แย้ง
  • ห่วงคลี่ (ซึ่งสามารถนำไปสู่การเพิ่มความเร็วในการประมวลผลขนาดใหญ่เมื่อลำดับสั้น ๆ เกี่ยวกับ CPU) ผ่าน unroll ข้อโต้แย้ง
  • ...และอื่น ๆ.

สำหรับข้อมูลเพิ่มเติมโปรดดูที่ เอกสาร RNN API

ผลลัพธ์และสถานะ

โดยค่าเริ่มต้น ผลลัพธ์ของเลเยอร์ RNN จะมีเวกเตอร์เดียวต่อตัวอย่าง เวกเตอร์นี้เป็นเอาต์พุตของเซลล์ RNN ที่สอดคล้องกับขั้นตอนเวลาสุดท้าย ซึ่งมีข้อมูลเกี่ยวกับลำดับอินพุตทั้งหมด รูปร่างของการส่งออกนี้เป็น (batch_size, units) ที่ units สอดคล้องกับ units อาร์กิวเมนต์ส่งผ่านไปยังตัวสร้างชั้นของ

RNN ชั้นยังสามารถกลับลำดับทั้งหมดของผลสำหรับแต่ละตัวอย่าง (หนึ่งเวกเตอร์ต่อ timestep ต่อตัวอย่าง) ถ้าคุณตั้ง return_sequences=True รูปร่างของการส่งออกนี้เป็น (batch_size, timesteps, units)

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

# The output of GRU will be a 3D tensor of shape (batch_size, timesteps, 256)
model.add(layers.GRU(256, return_sequences=True))

# The output of SimpleRNN will be a 2D tensor of shape (batch_size, 128)
model.add(layers.SimpleRNN(128))

model.add(layers.Dense(10))

model.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_1 (Embedding)      (None, None, 64)          64000     
_________________________________________________________________
gru (GRU)                    (None, None, 256)         247296    
_________________________________________________________________
simple_rnn (SimpleRNN)       (None, 128)               49280     
_________________________________________________________________
dense_1 (Dense)              (None, 10)                1290      
=================================================================
Total params: 361,866
Trainable params: 361,866
Non-trainable params: 0
_________________________________________________________________

นอกจากนี้ เลเยอร์ RNN สามารถส่งคืนสถานะภายในขั้นสุดท้ายได้ รัฐกลับสามารถนำมาใช้เพื่อดำเนินการต่อการดำเนินการ RNN ภายหลังหรือ การเริ่มต้น RNN อื่น การตั้งค่านี้มักใช้ในแบบจำลองลำดับต่อลำดับของตัวเข้ารหัส-ตัวถอดรหัส โดยที่สถานะสุดท้ายของตัวเข้ารหัสถูกใช้เป็นสถานะเริ่มต้นของตัวถอดรหัส

การกำหนดค่าชั้น RNN จะกลับรัฐภายในของตนตั้ง return_state พารามิเตอร์ True เมื่อมีการสร้างเลเยอร์ โปรดทราบว่า LSTM มี 2 เทนเซอร์ของรัฐ แต่ GRU มีเพียงหนึ่ง

การกำหนดค่าสถานะเริ่มต้นของชั้นเพียงโทรชั้นที่มีการเพิ่มเติมการโต้แย้งคำหลัก initial_state โปรดทราบว่ารูปร่างของสถานะต้องตรงกับขนาดหน่วยของเลเยอร์ ดังตัวอย่างด้านล่าง

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
)

# Return states in addition to output
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
)

# Pass the 2 states to a new LSTM layer, as initial state
decoder_output = layers.LSTM(64, name="decoder")(
    decoder_embedded, initial_state=encoder_state
)
output = layers.Dense(10)(decoder_output)

model = keras.Model([encoder_input, decoder_input], output)
model.summary()
Model: "model"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
input_1 (InputLayer)            [(None, None)]       0                                            
__________________________________________________________________________________________________
input_2 (InputLayer)            [(None, None)]       0                                            
__________________________________________________________________________________________________
embedding_2 (Embedding)         (None, None, 64)     64000       input_1[0][0]                    
__________________________________________________________________________________________________
embedding_3 (Embedding)         (None, None, 64)     128000      input_2[0][0]                    
__________________________________________________________________________________________________
encoder (LSTM)                  [(None, 64), (None,  33024       embedding_2[0][0]                
__________________________________________________________________________________________________
decoder (LSTM)                  (None, 64)           33024       embedding_3[0][0]                
                                                                 encoder[0][1]                    
                                                                 encoder[0][2]                    
__________________________________________________________________________________________________
dense_2 (Dense)                 (None, 10)           650         decoder[0][0]                    
==================================================================================================
Total params: 258,698
Trainable params: 258,698
Non-trainable params: 0
__________________________________________________________________________________________________

เลเยอร์ RNN และเซลล์ RNN

นอกจากเลเยอร์ RNN ในตัวแล้ว RNN API ยังมี API ระดับเซลล์อีกด้วย เซลล์ RNN ต่างจากเลเยอร์ RNN ซึ่งประมวลผลลำดับอินพุตทั้งชุด เซลล์ RNN จะประมวลผลขั้นตอนเวลาเดียวเท่านั้น

เซลล์ที่อยู่ภายในของ for วงของชั้น RNN ห่อมือถือภายใน keras.layers.RNN ชั้นช่วยให้คุณมีชั้นความสามารถในการประมวลผลสำหรับกระบวนการของลำดับเช่น RNN(LSTMCell(10))

ศาสตร์ RNN(LSTMCell(10)) ก่อให้เกิดผลเช่นเดียวกับ LSTM(10) อันที่จริง การนำเลเยอร์นี้ไปใช้ใน TF v1.x เป็นเพียงการสร้างเซลล์ RNN ที่เกี่ยวข้องและตัดมันในเลเยอร์ RNN อย่างไรก็ตามการใช้ในตัว GRU และ LSTM ชั้นช่วยให้การใช้งานของ CuDNN และคุณอาจจะเห็นประสิทธิภาพที่ดีขึ้น

มีเซลล์ RNN ในตัวสามเซลล์ แต่ละเซลล์สอดคล้องกับเลเยอร์ RNN ที่ตรงกัน

นามธรรมมือถือร่วมกับทั่วไป keras.layers.RNN ชั้นทำให้มันง่ายมากที่จะใช้สถาปัตยกรรม RNN ที่กำหนดเองสำหรับการวิจัยของคุณ

สถานะข้ามชุด

เมื่อการประมวลผลลำดับที่ยาวมาก (อาจอนันต์), คุณอาจต้องการที่จะใช้รูปแบบของการ statefulness ข้ามชุด

โดยปกติ สถานะภายในของเลเยอร์ RNN จะถูกรีเซ็ตทุกครั้งที่เห็นชุดงานใหม่ (กล่าวคือ ทุกตัวอย่างที่เห็นในเลเยอร์จะถือว่าไม่ขึ้นกับอดีต) เลเยอร์จะคงสถานะไว้ในขณะที่ประมวลผลตัวอย่างที่กำหนดเท่านั้น

หากคุณมีลำดับที่ยาวมาก การแบ่งเป็นลำดับที่สั้นลง และป้อนลำดับที่สั้นกว่านี้ตามลำดับลงในเลเยอร์ RNN โดยไม่ต้องรีเซ็ตสถานะของเลเยอร์ ด้วยวิธีนี้ เลเยอร์สามารถเก็บข้อมูลเกี่ยวกับความสมบูรณ์ของลำดับ แม้ว่าจะเห็นเพียงลำดับย่อยครั้งละหนึ่งรายการเท่านั้น

คุณสามารถทำได้โดยการตั้งค่า stateful=True ในการสร้าง

ถ้าคุณมีลำดับ s = [t0, t1, ... t1546, t1547] คุณจะแยกไว้ในเช่น

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

จากนั้นคุณจะดำเนินการผ่าน:

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

เมื่อคุณต้องการล้างรัฐคุณสามารถใช้ layer.reset_states()

นี่คือตัวอย่างที่สมบูรณ์:

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() will reset the cached state to the original initial_state.
# If no initial_state was provided, zero-states will be used by default.
lstm_layer.reset_states()

RNN State Reuse

รัฐบันทึกของชั้น RNN ยังไม่ได้รวมอยู่ใน layer.weights() หากคุณต้องการที่จะนำมาใช้ใหม่ของรัฐจากชั้น RNN คุณสามารถเรียกค่าสหรัฐอเมริกาโดย layer.states และใช้เป็นสถานะเริ่มต้นสำหรับชั้นใหม่ผ่านการทำงาน API Keras เช่น new_layer(inputs, initial_state=layer.states) หรือการจัดคลาสย่อยของโมเดล

โปรดทราบว่าอาจไม่สามารถใช้โมเดลตามลำดับในกรณีนี้ เนื่องจากรองรับเฉพาะเลเยอร์ที่มีอินพุตและเอาต์พุตเดียวเท่านั้น อินพุตพิเศษของสถานะเริ่มต้นทำให้ไม่สามารถใช้ที่นี่ได้

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)

existing_state = lstm_layer.states

new_lstm_layer = layers.LSTM(64)
new_output = new_lstm_layer(paragraph3, initial_state=existing_state)

RNN แบบสองทิศทาง

สำหรับซีเควนซ์อื่นที่ไม่ใช่อนุกรมเวลา (เช่น ข้อความ) มักจะเป็นกรณีที่โมเดล RNN สามารถทำงานได้ดีขึ้น ถ้าไม่เพียงแต่ประมวลผลลำดับตั้งแต่ต้นจนจบเท่านั้น แต่ยังดำเนินการย้อนกลับด้วย ตัวอย่างเช่น ในการคาดเดาคำถัดไปในประโยค การมีบริบทรอบๆ คำนั้นมักจะมีประโยชน์ ไม่ใช่แค่คำที่อยู่ข้างหน้าเท่านั้น

Keras มี API ที่ง่ายสำหรับคุณที่จะสร้าง RNNs แบบสองทิศทางดังกล่าวที่: keras.layers.Bidirectional เสื้อคลุม

model = 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()
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
bidirectional (Bidirectional (None, 5, 128)            38400     
_________________________________________________________________
bidirectional_1 (Bidirection (None, 64)                41216     
_________________________________________________________________
dense_3 (Dense)              (None, 10)                650       
=================================================================
Total params: 80,266
Trainable params: 80,266
Non-trainable params: 0
_________________________________________________________________

ภายใต้ประทุน Bidirectional จะคัดลอกชั้น RNN ผ่านและพลิก go_backwards ฟิลด์ของชั้นคัดลอกใหม่เพื่อที่จะจะดำเนินการปัจจัยการผลิตเพื่อย้อนกลับ

เอาท์พุทของ Bidirectional RNN จะตามค่าเริ่มต้นการเรียงต่อกันของการส่งออกชั้นไปข้างหน้าและการส่งออกชั้นย้อนหลัง หากคุณต้องการผสานพฤติกรรมที่แตกต่างกันเช่นการเรียงต่อกันเปลี่ยน merge_mode พารามิเตอร์ใน Bidirectional คอนสตรัคเสื้อคลุม สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับ Bidirectional โปรดตรวจสอบ เอกสารของ API

การเพิ่มประสิทธิภาพและเมล็ด CuDNN

ใน TensorFlow 2.0 เลเยอร์ LSTM และ GRU ในตัวได้รับการอัปเดตเพื่อใช้ประโยชน์จากเคอร์เนล CuDNN ตามค่าเริ่มต้นเมื่อมี GPU การเปลี่ยนแปลงนี้ก่อน keras.layers.CuDNNLSTM/CuDNNGRU ชั้นได้รับการคัดค้านและคุณสามารถสร้างรูปแบบของคุณโดยไม่ต้องกังวลเกี่ยวกับฮาร์ดแวร์ที่จะทำงานบน

ตั้งแต่เคอร์เนล CuDNN ถูกสร้างขึ้นด้วยสมมติฐานบางอย่างที่นี้หมายถึงชั้นจะไม่สามารถที่จะใช้เคอร์เนล CuDNN ถ้าคุณเปลี่ยนค่าเริ่มต้นของในตัว LSTM หรือ GRU ชั้น เช่น:

  • การเปลี่ยน activation ฟังก์ชั่นจาก tanh ไปเป็นอย่างอื่น
  • เปลี่ยน recurrent_activation ฟังก์ชั่นจาก sigmoid ไปเป็นอย่างอื่น
  • ใช้ recurrent_dropout > 0
  • การตั้งค่า unroll เป็น True ซึ่งกองกำลัง LSTM / GRU ในการย่อยสลายภายใน tf.while_loop เป็นคลี่ for ห่วง
  • การตั้งค่า use_bias เท็จ
  • การใช้การกำบังเมื่อข้อมูลที่ป้อนเข้าไม่ได้มีการเสริมชิดขวาอย่างเคร่งครัด (หากการกำบังตรงกับข้อมูลที่เสริมชิดขวาอย่างเคร่งครัด CuDNN ยังคงสามารถใช้ได้ นี่เป็นกรณีที่พบบ่อยที่สุด)

สำหรับรายละเอียดของข้อ จำกัด โปรดดูเอกสารสำหรับ LSTM และ GRU ชั้น

การใช้เมล็ด CuDNN เมื่อพร้อมใช้งาน

มาสร้างแบบจำลอง LSTM อย่างง่ายเพื่อแสดงความแตกต่างด้านประสิทธิภาพ

เราจะใช้เป็นลำดับอินพุตตามลำดับของแถวของตัวเลข MNIST (ถือว่าแต่ละแถวของพิกเซลเป็นขั้นตอนเวลา) และเราจะคาดการณ์ป้ายกำกับของตัวเลขดังกล่าว

batch_size = 64
# Each MNIST image batch is a tensor of shape (batch_size, 28, 28).
# Each input sequence will be of size (28, 28) (height is treated like time).
input_dim = 28

units = 64
output_size = 10  # labels are from 0 to 9

# Build the RNN model
def build_model(allow_cudnn_kernel=True):
    # CuDNN is only available at the layer level, and not at the cell level.
    # This means `LSTM(units)` will use the CuDNN kernel,
    # while RNN(LSTMCell(units)) will run on non-CuDNN kernel.
    if allow_cudnn_kernel:
        # The LSTM layer with default options uses CuDNN.
        lstm_layer = keras.layers.LSTM(units, input_shape=(None, input_dim))
    else:
        # Wrapping a LSTMCell in a RNN layer will not use CuDNN.
        lstm_layer = keras.layers.RNN(
            keras.layers.LSTMCell(units), input_shape=(None, input_dim)
        )
    model = keras.models.Sequential(
        [
            lstm_layer,
            keras.layers.BatchNormalization(),
            keras.layers.Dense(output_size),
        ]
    )
    return model

มาโหลดชุดข้อมูล MNIST กัน:

mnist = 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]

มาสร้างอินสแตนซ์โมเดลและฝึกกัน

เราเลือก sparse_categorical_crossentropy เป็นฟังก์ชั่นการสูญเสียสำหรับรูปแบบ เอาท์พุทของรูปแบบที่มีรูปร่างของ [batch_size, 10] เป้าหมายสำหรับโมเดลคือเวกเตอร์จำนวนเต็ม แต่ละจำนวนเต็มอยู่ในช่วง 0 ถึง 9

model = build_model(allow_cudnn_kernel=True)

model.compile(
    loss=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=1
)
938/938 [==============================] - 6s 5ms/step - loss: 0.9510 - accuracy: 0.7029 - val_loss: 0.5633 - val_accuracy: 0.8209
<keras.callbacks.History at 0x7fc9942efad0>

ตอนนี้ มาเปรียบเทียบกับรุ่นที่ไม่ได้ใช้เคอร์เนล CuDNN:

noncudnn_model = build_model(allow_cudnn_kernel=False)
noncudnn_model.set_weights(model.get_weights())
noncudnn_model.compile(
    loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    optimizer="sgd",
    metrics=["accuracy"],
)
noncudnn_model.fit(
    x_train, y_train, validation_data=(x_test, y_test), batch_size=batch_size, epochs=1
)
938/938 [==============================] - 34s 35ms/step - loss: 0.3894 - accuracy: 0.8846 - val_loss: 0.5677 - val_accuracy: 0.8045
<keras.callbacks.History at 0x7fc945fa2650>

เมื่อทำงานบนเครื่องที่ติดตั้ง NVIDIA GPU และ CuDNN โมเดลที่สร้างด้วย CuDNN จะฝึกได้เร็วกว่ามากเมื่อเทียบกับรุ่นที่ใช้เคอร์เนล TensorFlow ปกติ

โมเดลที่เปิดใช้งาน CuDNN เดียวกันยังสามารถใช้เพื่อเรียกใช้การอนุมานในสภาพแวดล้อมที่มี CPU เท่านั้น tf.device คำอธิบายประกอบด้านล่างเป็นเพียงการบังคับให้วางอุปกรณ์ โมเดลจะทำงานบน CPU โดยค่าเริ่มต้นหากไม่มี GPU

คุณไม่ต้องกังวลกับฮาร์ดแวร์ที่คุณกำลังใช้งานอีกต่อไป มันไม่เจ๋งเหรอ?

import matplotlib.pyplot as plt

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"))
Predicted result is: [3], target result is: 5

png

RNN ที่มีอินพุตรายการ/ dict หรืออินพุตที่ซ้อนกัน

โครงสร้างที่ซ้อนกันช่วยให้ผู้ดำเนินการรวมข้อมูลเพิ่มเติมได้ภายในขั้นตอนเดียว ตัวอย่างเช่น เฟรมวิดีโอสามารถมีอินพุตเสียงและวิดีโอพร้อมกันได้ รูปร่างข้อมูลในกรณีนี้อาจเป็น:

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

ในอีกตัวอย่างหนึ่ง ข้อมูลการเขียนด้วยลายมืออาจมีทั้งพิกัด x และ y สำหรับตำแหน่งปัจจุบันของปากกา เช่นเดียวกับข้อมูลแรงกด ดังนั้นการแสดงข้อมูลอาจเป็น:

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

โค้ดต่อไปนี้แสดงตัวอย่างวิธีสร้างเซลล์ RNN แบบกำหนดเองที่ยอมรับอินพุตที่มีโครงสร้างดังกล่าว

กำหนดเซลล์แบบกำหนดเองที่รองรับอินพุต/เอาต์พุตที่ซ้อนกัน

ดู การทำเลเยอร์ใหม่และรุ่นผ่าน subclassing สำหรับรายละเอียดเกี่ยวกับการเขียนชั้นของคุณเอง

class NestedCell(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 = [tf.TensorShape([unit_1]), tf.TensorShape([unit_2, unit_3])]
        self.output_size = [tf.TensorShape([unit_1]), tf.TensorShape([unit_2, unit_3])]
        super(NestedCell, self).__init__(**kwargs)

    def build(self, input_shapes):
        # expect input_shape to contain 2 items, [(batch, i1), (batch, i2, i3)]
        i1 = input_shapes[0][1]
        i2 = input_shapes[1][1]
        i3 = input_shapes[1][2]

        self.kernel_1 = self.add_weight(
            shape=(i1, self.unit_1), initializer="uniform", name="kernel_1"
        )
        self.kernel_2_3 = self.add_weight(
            shape=(i2, i3, self.unit_2, self.unit_3),
            initializer="uniform",
            name="kernel_2_3",
        )

    def call(self, inputs, states):
        # inputs should be in [(batch, input_1), (batch, input_2, input_3)]
        # state should be in shape [(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 = (state_1, state_2_3)

        return output, new_states

    def get_config(self):
        return {"unit_1": self.unit_1, "unit_2": unit_2, "unit_3": self.unit_3}

สร้างโมเดล RNN ด้วยอินพุต/เอาต์พุตที่ซ้อนกัน

Let 's สร้างแบบจำลอง Keras ที่ใช้ keras.layers.RNN ชั้นและมือถือที่กำหนดเองเราก็กำหนดไว้

unit_1 = 10
unit_2 = 20
unit_3 = 30

i1 = 32
i2 = 64
i3 = 32
batch_size = 64
num_batches = 10
timestep = 50

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

input_1 = keras.Input((None, i1))
input_2 = keras.Input((None, i2, i3))

outputs = rnn((input_1, input_2))

model = keras.models.Model([input_1, input_2], outputs)

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

ฝึกโมเดลด้วยข้อมูลที่สร้างแบบสุ่ม

เนื่องจากไม่มีชุดข้อมูลตัวเลือกที่ดีสำหรับโมเดลนี้ เราจึงใช้ข้อมูล Numpy แบบสุ่มสำหรับการสาธิต

input_1_data = np.random.random((batch_size * num_batches, timestep, i1))
input_2_data = np.random.random((batch_size * num_batches, timestep, i2, i3))
target_1_data = np.random.random((batch_size * num_batches, unit_1))
target_2_data = np.random.random((batch_size * num_batches, 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)
10/10 [==============================] - 1s 26ms/step - loss: 0.7316 - rnn_1_loss: 0.2590 - rnn_1_1_loss: 0.4725 - rnn_1_accuracy: 0.1016 - rnn_1_1_accuracy: 0.0328
<keras.callbacks.History at 0x7fc5686e6f50>

ด้วย Keras keras.layers.RNN ชั้นคุณจะคาดหวังเท่านั้นที่จะกำหนดตรรกะทางคณิตศาสตร์สำหรับขั้นตอนในแต่ละลำดับและ keras.layers.RNN ชั้นจะจัดการย้ำลำดับสำหรับคุณ เป็นวิธีที่มีประสิทธิภาพอย่างเหลือเชื่อในการสร้างต้นแบบ RNN ชนิดใหม่อย่างรวดเร็ว (เช่น ตัวแปร LSTM)

สำหรับรายละเอียดเพิ่มเติมโปรดไปที่ เอกสาร API