🐈

Act 35. Sequentialクラスについて

2025/01/04に公開

はじめに

当たり前のようにSequentialクラスを使ってLSTMのモデルを構築しているが、そもそもSequentialクラスで何が出来るのか気になったためこの記事を作ることに決定。

Sequential

Sequential クラスには以下のプロパティとメソッドが存在する。

プロパティ

  1. layers

    • 現在のモデルに追加されたレイヤーのリスト。
    • 入力レイヤーは含まれない。
    • add()pop() で変更可能。
    • 読み取り専用。
    from keras.models import Sequential
    from keras.layers import LSTM
    
    # モデルの構築
    model = Sequential()
    
    # レイヤーの確認
    print(model.layers)
    
    # レイヤーの追加
    model.add(LSTM(units=10))
    
    # 再度レイヤーの確認
    print(model.layers)
    

    出力は以下の通り。

    []
    [<LSTM name=lstm_2, built=False>]
    
  2. input_shape

    • モデルの入力の形状。
    • 入力形状が定義されていない場合は例外がスローされる。
    from keras.models import Sequential
    from keras.layers import LSTM
    
    # モデルの構築
    model = Sequential()
    
    # 入力形状を確認
    print(model.input_shape)
    

    入力形状が定義されていない場合の出力は以下。

    AttributeError                            Traceback (most recent call last)
    Cell In[28], line 5
          2 model = Sequential()
          4 # 入力形状を確認
    ----> 5 print(model.input_shape)
    
    File ~/venv/py312/lib/python3.12/site-packages/keras/src/models/sequential.py:276, in Sequential.input_shape(self)
        274 if self._functional:
        275     return self._functional.input_shape
    --> 276 raise AttributeError(
        277     f"Sequential model '{self.name}' has no defined input shape yet."
        278 )
    
    AttributeError: Sequential model 'sequential_14' has no defined input shape yet.
    

    エラーが発生しないパターンは以下のように入力形状を指定した場合。

    from keras.models import Sequential
    from keras.layers import LSTM
    
    # Sequentialモデルの構築
    model = Sequential()
    
    # レイヤーを追加
    model.add(LSTM(units=10, input_shape=(10, 1)))
    
    # 入力形状を確認
    print(model.input_shape)
    

    出力は以下の通り。

    (None, 10, 1)
    /home/onishi/venv/py312/lib/python3.12/site-packages/keras/src/layers/rnn/rnn.py:200: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.
      super().__init__(**kwargs)
    

    kerasのバージョンによっては上記のようにメッセージが出力される。
    LSTMレイヤーのinput_shapeを使うのではなく、Inputレイヤーを追加するようにとのこと。

    from keras.models import Sequential
    from keras.layers import LSTM, input
    
    # Sequentialモデルの構築
    model = Sequential()
    
    # レイヤーを追加
    model.add(Input(shape=(10, 1)))
    model.add(LSTM(units=10))
    
    # 入力形状を確認
    print(model.input_shape)
    

    出力は以下の通り。

    (None, 10, 1)
    
  3. output_shape

    • モデルの出力の形状。
    • 出力形状が定義されていない場合は例外がスローされる。
    from keras.models import Sequential
    from keras.layers import LSTM, Input
    
    # Sequentialモデルの構築
    model = Sequential()
    
    # レイヤーを追加
    model.add(Input(shape=(10, 1)))
    
    # 入力形状を確認
    print(model.output_shape)
    

    出力形状が定義されていない場合は以下のエラーが出力される。

    AttributeError                            Traceback (most recent call last)
    Cell In[46], line 8
          5 model.add(Input(shape=(10, 1)))
          6 
          7 # 入力形状を確認
    --->  8 print(model.output_shape)
    
    File ~/venv/py312/lib/python3.12/site-packages/keras/src/models/sequential.py:284, in Sequential.output_shape(self)
        282 if self._functional:
        283     return self._functional.output_shape
    --> 284 raise AttributeError(
        285     f"Sequential model '{self.name}' has no defined output shape yet."
        286 )
    
    AttributeError: Sequential model 'sequential_31' has no defined output shape yet.
    

    正常なパターンは以下の通り。

    from keras.models import Sequential
    from keras.layers import LSTM, Input, Dense
    
    # Sequentialモデルの構築
    model = Sequential()
    
    # レイヤーを追加
    model.add(Input(shape=(10, 1)))
    model.add(LSTM(units=30))
    model.add(Dense(units=1))
    
    # 入力形状を確認
    print(model.output_shape)
    

    出力は以下の通り。
    Denseで出力形状を指定している。

    (None, 1)
    
  4. inputs

    • モデルの入力テンソル。
    • 入力がまだ定義されていない場合は例外がスローされる。

    input_shapeinputsは似たようなもの。

    プロパティ input_shape inputs
    目的 入力データの形状を確認 入力テンソルを確認、操作
    タプル(例: (None, 10, 1) TensorFlowのテンソル(tf.Tensor
    主な用途 モデルの概要や構造を確認する 入力テンソルを直接利用、Functional APIと連携
    定義される条件 最初のレイヤーで形状を指定する モデルが構築されている(Inputが追加されている)
    from keras.models import Sequential
    from keras.layers import LSTM, Input
    
    # Sequentialモデルの構築
    model = Sequential()
    
    # レイヤーを追加
    model.add(Input(shape=(10, 1)))
    model.add(LSTM(units=30))
    
    # 入力形状を確認
    print(model.inputs)
    

    出力は以下の通り。

    [<KerasTensor shape=(None, 10, 1), dtype=float32, sparse=False, name=keras_tensor_64>]
    
  5. outputs

    • モデルの出力テンソル。
    • 出力がまだ定義されていない場合は例外がスローされる。
      inputsの出力版。
  6. input_dtype

    • 入力データのデータ型。
    from keras.models import Sequential
    from keras.layers import Input
    
    # モデルの構築
    model = Sequential()
    
    # レイヤーの追加
    model.add(Input(shape=(2, 1)))
    
    # 入力データの型を確認
    print(model.input_dtype)
    

    出力は以下の通り。

    float32
    
  7. built

    • モデルが構築済みかどうかを示すフラグ。
    from keras.models import Sequential
    from keras.layers import Input
    
    # モデルの構築
    model = Sequential()
    
    # レイヤーの追加
    model.add(Input(shape=(2, 1)))
    
    # モデルが構築済みか確認
    print(model.built)
    

    出力は以下の通り。

    False
    

    Input以外のレイヤーを追加した状態で確認するとモデルが構築されているとみなされる。

    # モデルの構築
    model = Sequential()
    
    # レイヤーの追加
    model.add(Input(shape=(2, 1)))
    model.add(LSTM(units=10))
    model.add(Dense(units=1))
    
    # モデルが構築済みか確認
    print(model.built)
    

    出力は以下の通り。
    ※1つ目のメッセージは表示されない場合もあるかも

    I0000 00:00:1735950832.512166     213 gpu_device.cc:2022] Created device /job:localhost/replica:0/task:0/device:GPU:0 with 5558 MB memory:  -> device: 0, name: NVIDIA GeForce RTX 3070 Ti, pci bus id: 0000:01:00.0, compute capability: 8.6
    True
    

メソッド

  1. add(self, layer, rebuild=True)

    • モデルに新しいレイヤーを追加する。
    • 最初のレイヤーとしてInputレイヤーを追加することも可能。

    レイヤーの追加はいつも使っているメソッド。

    from keras.models import Sequential
    from keras.layers import Input, LSTM, Dense, Dropout
    
    # モデルの構築
    model = Sequential()
    
    # レイヤーの追加
    model.add(Input(shape=(10, 2)))
    model.add(LSTM(units=30, return_sequences=True))
    model.add(Dropout(0.1))
    model.add(LSTM(units=30, return_sequences=True))
    model.add(Dropout(0.1))
    model.add(LSTM(units=30, return_sequences=True))
    model.add(Dropout(0.1))
    model.add(LSTM(units=30))
    model.add(Dropout(0.1))
    model.add(Dense(units=1))
    
    # レイヤーの確認
    for idx, layer in enumerate(model.layers):
      print(f"Layer {idx} is {layer}")
    

    出力は以下の通り。

    Layer 0 is <LSTM name=lstm_8, built=True>
    Layer 1 is <Dropout name=dropout_4, built=True>
    Layer 2 is <LSTM name=lstm_9, built=True>
    Layer 3 is <Dropout name=dropout_5, built=True>
    Layer 4 is <LSTM name=lstm_10, built=True>
    Layer 5 is <Dropout name=dropout_6, built=True>
    Layer 6 is <LSTM name=lstm_11, built=True>
    Layer 7 is <Dropout name=dropout_7, built=True>
    Layer 8 is <Dense name=dense_3, built=True>
    
  2. pop(self, rebuild=True)

    • 最後に追加されたレイヤーを削除する。
    • 削除したレイヤーを返す。
    from keras.models import Sequential
    from keras.layers import Input, LSTM, Dense, Dropout
    
    # モデルの構築
    model = Sequential()
    
    # レイヤーの追加
    model.add(Input(shape=(10, 2)))
    model.add(LSTM(units=30, return_sequences=True))
    model.add(Dropout(0.1))
    model.add(LSTM(units=30, return_sequences=True))
    model.add(Dropout(0.1))
    model.add(LSTM(units=30, return_sequences=True))
    model.add(Dropout(0.1))
    model.add(LSTM(units=30))
    model.add(Dropout(0.1))
    model.add(Dense(units=1))
    
    # レイヤーの確認
    for idx, layer in enumerate(model.layers):
      print(f"Layer {idx} is {layer}")
    
    print("="*30)
    
    # レイヤーの削除
    pop = model.pop()
    print(f"{pop} was deleted")
    
    print("="*30)
    
    # 削除後のレイヤーの確認
    for idx, layer in enumerate(model.layers):
      print(f"Layer {idx} is {layer}")
    

    出力は以下の通り。
    レイヤーが一つ削除されているのが分かる。

    Layer 0 is <LSTM name=lstm_32, built=True>
    Layer 1 is <Dropout name=dropout_28, built=True>
    Layer 2 is <LSTM name=lstm_33, built=True>
    Layer 3 is <Dropout name=dropout_29, built=True>
    Layer 4 is <LSTM name=lstm_34, built=True>
    Layer 5 is <Dropout name=dropout_30, built=True>
    Layer 6 is <LSTM name=lstm_35, built=True>
    Layer 7 is <Dropout name=dropout_31, built=True>
    Layer 8 is <Dense name=dense_9, built=True>
    ==============================
    <Dense name=dense_9, built=True> was deleted
    ==============================
    Layer 0 is <LSTM name=lstm_32, built=True>
    Layer 1 is <Dropout name=dropout_28, built=True>
    Layer 2 is <LSTM name=lstm_33, built=True>
    Layer 3 is <Dropout name=dropout_29, built=True>
    Layer 4 is <LSTM name=lstm_34, built=True>
    Layer 5 is <Dropout name=dropout_30, built=True>
    Layer 6 is <LSTM name=lstm_35, built=True>
    Layer 7 is <Dropout name=dropout_31, built=True>
    
  3. build(self, input_shape=None)

    • モデルを構築する。
    • 入力形状を指定することで手動で構築可能。
    from keras.models import Sequential
    from keras.layers import Dense, LSTM
    
    # モデルの定義
    model = Sequential()
    model.add(LSTM(64, return_sequences=True))
    model.add(Dense(1))
    
    # モデルのビルド
    model.build(input_shape=(None, 10, 5))
    
    # モデルの概要を確認
    model.summary()
    

    出力は以下の通り。

    Model: "sequential"
    ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
    ┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃
    ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
    │ lstm (LSTM)(None, 10, 64)17,920 │
    ├─────────────────────────────────┼────────────────────────┼───────────────┤
    │ dense (Dense)(None, 10, 1)65 │
    └─────────────────────────────────┴────────────────────────┴───────────────┘
     Total params: 17,985 (70.25 KB)
     Trainable params: 17,985 (70.25 KB)
     Non-trainable params: 0 (0.00 B)
    

    通常はfitpredictを呼び出す際に内部的にビルドが行われるため、手動で実行する必要はないらしい。

    fitbuildを行う前にsummaryを確認したい場合などは便利そう。

    ちなみにmodel.build(input_shape=(None, 10, 5))input_shapeは、左から(サンプル数, 期間, 特徴量)となる。

  4. compute_output_shape(self, input_shape)

    • 入力形状から出力形状を計算する。
    from keras.models import Sequential
    from keras.layers import Dense, LSTM, Input, Dropout
    
    # モデルの構築
    model = Sequential()
    
    # レイヤーの追加
    model.add(Input(shape=(10, 2)))
    model.add(LSTM(units=30, return_sequences=True))
    model.add(Dropout(0.1))
    model.add(LSTM(units=30, return_sequences=True))
    model.add(Dropout(0.1))
    model.add(LSTM(units=30, return_sequences=True))
    model.add(Dropout(0.1))
    model.add(LSTM(units=30))
    model.add(Dropout(0.1))
    model.add(Dense(units=1))
    
    # 入力形状を指定して出力形状を計算
    output_shape = model.compute_output_shape((None, 10, 2))
    print(output_shape)
    

    出力は以下の通り。
    Dense(units=1)としているため、出力される値は1つとなる。

    (None, 1)
    
  5. compute_output_spec(self, inputs, training=None, mask=None)

    • 入力仕様に基づいて出力仕様を計算する。
    from keras.models import Sequential
    from keras.layers import Dense, LSTM, Input, Dropout
    import tensorflow as tf
    
    # モデルの構築
    model = Sequential()
    
    # レイヤーの追加
    model.add(Input(shape=(10, 2)))
    model.add(LSTM(units=30, return_sequences=True))
    model.add(Dropout(0.1))
    model.add(LSTM(units=30, return_sequences=True))
    model.add(Dropout(0.1))
    model.add(LSTM(units=30, return_sequences=True))
    model.add(Dropout(0.1))
    model.add(LSTM(units=30))
    model.add(Dropout(0.1))
    model.add(Dense(units=1))
    
    # 入力スペックを定義
    input_spec = tf.TensorSpec(shape=(None, 10, 2), dtype=tf.float32)
    
    # 出力スペックを計算
    output_spec = model.compute_output_spec(input_spec)
    print(output_spec)
    

    出力は以下の通り。

    <KerasTensor shape=(None, 1), dtype=float32, sparse=False, name=keras_tensor_392>
    
  6. get_config(self)

    • モデルの設定を辞書形式で返す。
    • 再構築に使用可能。
    from keras.models import Sequential
    from keras.layers import Dense, LSTM, Input
    
    # モデルの構築
    model = Sequential()
    
    # レイヤーの追加
    model.add(Input(shape=(10, 2)))
    model.add(LSTM(units=30))
    model.add(Dense(units=1))
    
    # モデルの設定
    print(model.get_config())
    

    出力は以下の通り。

    {'name': 'sequential_19', 'trainable': True, 'dtype': {'module': 'keras', 'class_name': 'DTypePolicy', 'config': {'name': 'float32'}, 'registered_name': None}, 'layers': [{'module': 'keras.layers', 'class_name': 'InputLayer', 'config': {'batch_shape': (None, 10, 2), 'dtype': 'float32', 'sparse': False, 'name': 'input_layer_16'}, 'registered_name': None}, {'module': 'keras.layers', 'class_name': 'LSTM', 'config': {'name': 'lstm_55', 'trainable': True, 'dtype': {'module': 'keras', 'class_name': 'DTypePolicy', 'config': {'name': 'float32'}, 'registered_name': None}, 'return_sequences': False, 'return_state': False, 'go_backwards': False, 'stateful': False, 'unroll': False, 'zero_output_for_mask': False, 'units': 30, 'activation': 'tanh', 'recurrent_activation': 'sigmoid', 'use_bias': True, 'kernel_initializer': {'module': 'keras.initializers', 'class_name': 'GlorotUniform', 'config': {'seed': None}, 'registered_name': None}, 'recurrent_initializer': {'module': 'keras.initializers', 'class_name': 'Orthogonal', 'config': {'seed': None, 'gain': 1.0}, 'registered_name': None}, 'bias_initializer': {'module': 'keras.initializers', 'class_name': 'Zeros', 'config': {}, 'registered_name': None}, 'unit_forget_bias': True, 'kernel_regularizer': None, 'recurrent_regularizer': None, 'bias_regularizer': None, 'activity_regularizer': None, 'kernel_constraint': None, 'recurrent_constraint': None, 'bias_constraint': None, 'dropout': 0.0, 'recurrent_dropout': 0.0, 'seed': None}, 'registered_name': None, 'build_config': {'input_shape': (None, 10, 2)}}, {'module': 'keras.layers', 'class_name': 'Dense', 'config': {'name': 'dense_15', 'trainable': True, 'dtype': {'module': 'keras', 'class_name': 'DTypePolicy', 'config': {'name': 'float32'}, 'registered_name': None}, 'units': 1, 'activation': 'linear', 'use_bias': True, 'kernel_initializer': {'module': 'keras.initializers', 'class_name': 'GlorotUniform', 'config': {'seed': None}, 'registered_name': None}, 'bias_initializer': {'module': 'keras.initializers', 'class_name': 'Zeros', 'config': {}, 'registered_name': None}, 'kernel_regularizer': None, 'bias_regularizer': None, 'kernel_constraint': None, 'bias_constraint': None}, 'registered_name': None, 'build_config': {'input_shape': (None, 30)}}], 'build_input_shape': (None, 10, 2)}
    
  7. from_config(cls, config, custom_objects=None)

    • 設定からモデルを再構築する。
    from keras.models import Sequential
    from keras.layers import Dense, LSTM, Input
    
    # モデルの構築
    model_1 = Sequential()
    
    # レイヤーの追加
    model_1.add(Input(shape=(10, 2)))
    model_1.add(LSTM(units=30))
    model_1.add(Dense(units=1))
    
    print(model.layers)
    print("="*30)
    
    # モデルの取得
    config = model_1.get_config()
    
    model_2 = Sequential.from_config(config, custom_objects=False)
    print(model_2.layers)
    

    出力は以下の通り。

    [<LSTM name=lstm_55, built=True>, <Dense name=dense_15, built=True>]
    ==============================
    [<LSTM name=lstm_57, built=True>, <Dense name=dense_17, built=True>]
    

さいごに

Sequentialクラスについてなんとなく理解できた。

結局、基本的にはaddメソッドを使用するだけで済みそうな気がした。
一応それ以外にも色々なメソッドやプロパティがあることは覚えておこうと思う。

ではまた

Discussion