@hoge_piyo

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

kerasの基礎についての質問

質問

model = keras.Sequential(
    keras.layers.Dense(<-- a -->, input_shape=<-- b -->, activation='<-- c -->'),
    keras.layers.Dense(<-- d -->, activation='<-- e -->'
)

kerasで上記のようなモデルを作成したとします。
その場合bに入力層、aに中間層、dに出力層のユニット数を指定するところまで理解しています。
出力層はラベルの種類を指定するのはわかるのですが、入力層、中間層はどのように決めるべきなのでしょうか。
サイズ300*300の15枚のカラー画像を3つに分類する場合、上記a~eには何を入れるべきなのでしょうか。
またよくわからないのですが、Conv2Dなどを使う必要があるのでしょうか。

0 likes

1Answer

分類タスクでやることは特徴抽出及び次元削減なので,これを達成するために,入力側から順次ユニット数を少なくするのがセオリーとされています.

input_shapeを使わない書き方
model = keras.Sequential([
    keras.layers.Input(shape=<-- a -->),
    keras.layers.Dense(<-- b -->, activation='<-- c -->'),
    keras.layers.Dense(<-- d -->, activation='<-- e -->')
])

極端な話,精度さえ良ければなんでもいいです.

サイズ300*300の15枚のカラー画像を3つに分類する場合

カラー画像ということはRGBの3チャンネルあるので,input_shape=(300, 300, 3)になります.

model = keras.Sequential([
    keras.layers.Input(shape=(300, 300, 3)),
    keras.layers.Flatten(), # Denseで扱うためにFlattenする必要がある
    keras.layers.Dense(2048, activation='relu'),
    keras.layers.Dense(256, activation='relu'),
    keras.layers.Dense(3, activation='softmax')
])
model.summary()
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 flatten (Flatten)           (None, 270000)            0         
                                                                 
 dense (Dense)               (None, 2048)              552962048 
                                                                 
 dense_1 (Dense)             (None, 256)               524544    
                                                                 
 dense_2 (Dense)             (None, 3)                 771       
                                                                 
=================================================================
Total params: 553,487,363
Trainable params: 553,487,363
Non-trainable params: 0
_________________________________________________________________

順次ユニット数を少なくする考えのもとで書くとこのようになりますが,画像が(300, 300, 3)とデカいのでFlattenしたあとは$300\times 300\times 3=270,000$となり,Denseのみで推論しようとすると大抵の場合,メモリ不足で計算不可能です.

そこで,CNNを使って次のようにします.

model = keras.Sequential([
    keras.layers.Input(shape=(300, 300, 3)),
    keras.layers.Conv2D(32, (7, 7), activation='relu'),
    keras.layers.MaxPooling2D(),
    keras.layers.Conv2D(64, (3, 3), activation='relu'),
    keras.layers.MaxPooling2D(),
    keras.layers.Conv2D(128, (3, 3), activation='relu'),
    keras.layers.MaxPooling2D(),
    keras.layers.Conv2D(256, (3, 3), activation='relu'),
    keras.layers.GlobalAveragePooling2D(),
    keras.layers.Dense(128, activation='relu'),
    keras.layers.Dense(32, activation='relu'),
    keras.layers.Dense(3, activation='softmax')
])
model.summary()
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d (Conv2D)             (None, 294, 294, 32)      4736      
                                                                 
 max_pooling2d (MaxPooling2D  (None, 147, 147, 32)     0         
 )                                                               
                                                                 
 conv2d_1 (Conv2D)           (None, 145, 145, 64)      18496     
                                                                 
 max_pooling2d_1 (MaxPooling  (None, 72, 72, 64)       0         
 2D)                                                             
                                                                 
 conv2d_2 (Conv2D)           (None, 70, 70, 128)       73856     
                                                                 
 max_pooling2d_2 (MaxPooling  (None, 35, 35, 128)      0         
 2D)                                                             
                                                                 
 conv2d_3 (Conv2D)           (None, 33, 33, 256)       295168    
                                                                 
 global_average_pooling2d (G  (None, 256)              0         
 lobalAveragePooling2D)                                          
                                                                 
 dense (Dense)               (None, 128)               32896     
                                                                 
 dense_1 (Dense)             (None, 32)                4128      
                                                                 
 dense_2 (Dense)             (None, 3)                 99        
                                                                 
=================================================================
Total params: 429,379
Trainable params: 429,379
Non-trainable params: 0
_________________________________________________________________

GlobalAveragePooing2Dより前が特徴抽出器,それより後が分類器として役割を果たし,Denseのみで構成されるモデルよりメモリ消費量が少なく推論できます.

それぞれでmodel.summary()を実行してみてもらうと,trainable paramsの数が大きく違うと思います.これはメモリ使用量に直結します.

したがって,メモリ制約からConv2Dを使う必要があります.同様に,精度面からもConv2Dを使う方が良いのですが,詳しくは帰納バイアスについて調べてください.

ちなみに,これらレイヤに入れることのできるパラメータはドキュメントを参照するとわかりますが,質問で聞いているパラメータだけではありません.

Denseにもあるパラメータで考える必要があるのは,特にレイヤの初期値が挙げられます.ドキュメントで言われるところのkernel_initializerです.

デフォルト値でkernel_initializer='glorot_uniform'が割り当てられいることがわかると思います.これは原点付近で線形である関数の条件のもとで最適化されたランダム初期化法を行いますが,ReLUファミリーの活性化関数ではこれに当てはまらないためkernel_initializer='he_uniform'を使うなどの活性化関数に合わせた工夫が必要です.

つまり,上のCNNは次のように修正することになります.

model = keras.Sequential([
    keras.layers.Input(shape=(300, 300, 3)),
    keras.layers.Conv2D(32, (7, 7), activation='relu', kernel_initializer='he_uniform'),
    keras.layers.MaxPooling2D(),
    keras.layers.Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_uniform'),
    keras.layers.MaxPooling2D(),
    keras.layers.Conv2D(128, (3, 3), activation='relu', kernel_initializer="he_uniform"),
    keras.layers.MaxPooling2D(),
    keras.layers.Conv2D(256, (3, 3), activation='relu', kernel_initializer='he_uniform'),
    keras.layers.GlobalAveragePooling2D(),
    keras.layers.Dense(128, activation='relu', kernel_initializer='he_uniform'),
    keras.layers.Dense(32, activation='relu', kernel_initializer='he_uniform'),
    keras.layers.Dense(3, activation='softmax')
])

詳しくは次の記事を参照してください.

これら以外にもL1,L2正則化を行うパラメータや,他にもよく使われるDropoutBatchNormalization1レイヤなどがありますので,体系的に勉強されることをお勧めします.

  1. BatchNormalizationはConv2Dレイヤと活性化関数の間に挿入されることが推奨されています.このため,質問の書式スタイルConv2D(..., activation='any',...)のやり方ではBNの挿入ができません,自分なら3行かけてConv2D -> BN -> Activationの各レイヤを記述します.

1Like

Comments

  1. @hoge_piyo

    Questioner

    丁寧な解説ありがとうございます。
    追加で質問よろしいでしょうか。
    何か問題がありましたら、無視していただいて構いません。

    質問

    現在制作課題で画像認識モデルの構築を行っているのですが、エラーが出てしまっています。

    実際のコード

    import keras
    from keras.models import Sequential
    from keras.layers import Dense
    from keras.preprocessing.image import load_img
    import numpy as np
    import glob
    
    # 'test_data/train/'の中のrock, paper, scissorsディレクトリの中に
    # 各5枚ずつ画像が入っている
    
    rps = ['rock/', 'paper/', 'scissors/']
    
    BASE_DIR = 'test_data/train/'
    
    # 学習用データを格納
    x = []
    # 学習用データに対応するラベルを格納
    y = []
    
    # rock, paper, scissorsのラベルはそれぞれ0, 1, 2
    for idx, name in enumerate(rps):
        path = BASE_DIR + name + '*.jpg'
        files = glob.glob(path)
        for file in files:
            image = load_img(file)
            image = np.asarray(image)/255.0
            x.append(image)
            y.append(np.asarray(idx))
    
    # モデルを作成
    model = Sequential(
        keras.layers.Input(shape=(300, 300, 3)),
        Dense(784, activation='relu'),
        Dense(3, activation='softmax')
    )
    
    model.compile(
        optimizer='adam',
        loss='categorical_crossentropy',
        metrics=['accuracy']
    )
    
    # 学習中なのでepochsは1
    model.fit(x, y, epochs=1)
    
    model.summary()
    

    エラーメッセージ

    2023-11-22 09:12:44.959479: I tensorflow/core/platform/cpu_feature_guard.cc:182] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
    To enable the following instructions: SSE SSE2 SSE3 SSE4.1 SSE4.2 AVX AVX2 AVX512F AVX512_VNNI FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
    Traceback (most recent call last):
      File "c:\Users\user\Documents\AI\ai_test2.py", line 54, in <module>
        model.fit(x, y, epochs=1)
      File "C:\Users\user\AppData\Local\Programs\Python\Python311\Lib\site-packages\keras\src\utils\traceback_utils.py", line 70, in error_handler
        raise e.with_traceback(filtered_tb) from None
      File "C:\Users\user~1\AppData\Local\Temp\__autograph_generated_file5q6yansm.py", line 15, in tf__train_function
        retval_ = ag__.converted_call(ag__.ld(step_function), (ag__.ld(self), ag__.ld(iterator)), None, fscope)
        ^^^^^
    TypeError: in user code:
    
        File "C:\Users\user\AppData\Local\Programs\Python\Python311\Lib\site-packages\keras\src\engine\training.py", line 1377, in train_function  *       
            return step_function(self, iterator)
        File "C:\Users\user\AppData\Local\Programs\Python\Python311\Lib\site-packages\keras\src\engine\training.py", line 1360, in step_function  **       
            outputs = model.distribute_strategy.run(run_step, args=(data,))
        File "C:\Users\user\AppData\Local\Programs\Python\Python311\Lib\site-packages\keras\src\engine\training.py", line 1349, in run_step  **
            outputs = model.train_step(data)
        File "C:\Users\user\AppData\Local\Programs\Python\Python311\Lib\site-packages\keras\src\engine\training.py", line 1126, in train_step
            y_pred = self(x, training=True)
        File "C:\Users\user\AppData\Local\Programs\Python\Python311\Lib\site-packages\keras\src\utils\traceback_utils.py", line 70, in error_handler       
            raise e.with_traceback(filtered_tb) from None
        File "C:\Users\user\AppData\Local\Programs\Python\Python311\Lib\site-packages\keras\src\engine\base_layer.py", line 2800, in _name_scope
            name_scope += "/"
    
        TypeError: unsupported operand type(s) for +=: 'Dense' and 'str'
    
    PS C:\Users\user\Documents\AI> & C:/Users/user/AppData/Local/Programs/Python/Python311/python.exe c:/Users/user/Documents/AI/ai_test2.py
    2023-11-22 09:13:06.603065: I tensorflow/core/platform/cpu_feature_guard.cc:182] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
    To enable the following instructions: SSE SSE2 SSE3 SSE4.1 SSE4.2 AVX AVX2 AVX512F AVX512_VNNI FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
    Traceback (most recent call last):
      File "c:\Users\user\Documents\AI\ai_test2.py", line 54, in <module>
        model.fit(x, y, epochs=1)
      File "C:\Users\user\AppData\Local\Programs\Python\Python311\Lib\site-packages\keras\src\utils\traceback_utils.py", line 70, in error_handler
        raise e.with_traceback(filtered_tb) from None
      File "C:\Users\user\AppData\Local\Programs\Python\Python311\Lib\site-packages\tensorflow\python\framework\tensor_shape.py", line 959, in __getitem__ 
        return self._dims[key]
               ~~~~~~~~~~^^^^^
    IndexError: tuple index out of range
    

    最後に

    Conv2Dは重要なようですが、学習が期限までに間に合わない可能性があるので、提出後に自主的に学習したいと思います。

  2. Sequential[ ]が抜けてます.

    - model = Sequential(
    + model = Sequential([
          Dense(784, input_shape=(300, 300), activation='relu'),
          Dense(3, activation='softmax')
    - )
    + ])
    

    teratailの方にも聞いてるようですがこちらの回答をリンクしておいてください

    ちなみに先の回答の通り,

    • Flattenを行わないといけない点
    • CNNで構成するよりパラメータが多い点

    に気をつけてください.後者に関して,メモリ使用量が多いことは学習に時間を要することも意味します.

  3. @hoge_piyo

    Questioner

    回答ありがとうございます。
    修正して

    from keras.layers import Dense, Flatten, Input
    
    ~中略~
    
    model = Sequential([
        Flatten(),
        Input(shape=(300, 300, 3)),
        Dense(784, activation='relu'),
        Dense(3, activation='softmax')
    ])
    

    としてみたのですが、同じエラーが出てしまいました。

    teratailのほうでは回答がつかなかったためこちらにて質問させていただきました。
    質問直後にteratailのほうで削除リクエストを出しております。

  4. こちらの方ではエラーになりませんね.

    スクリーンショット 2023-11-22 14.43.57.png

    modelの再定義せずmodel.fit()やったとかじゃないでしょうか?

  5. @hoge_piyo

    Questioner

    回答ありがとうございます。
    画像のとおり

    model.fit(
        np.random.normal(size=(10, 300, 300, 3)),
        np.random.normal(size=(10, 3))
    
    

    としたらうまくできました。

    しかし、この場合前処理した画像"x"とラベル"y"を用いて学習できているのでしょうか。

  6. こちら,質問のように画像を保持していないので,そこ以外を再現すべく乱数を入力に取ったまでのことです.
    なので,そちらで用意してる画像を学習したわけではありません.

    こうなるとおそらく画像を用意するところあたりに問題があるのだと思いますが,こちら側で同じ画像を持った環境を用意できないので,検証できないです.

    というか同じエラーが出るのが納得いかないですね, TypeError: unsupported operand type(s) for +=: 'Dense' and 'str'に関しては先に指摘した[ ] を付加することで解決されるはずです.本当に修正したファイルを実行してるのでしょうか

  7. @hoge_piyo

    Questioner

    はい。
    確実に修正済みのファイルを実行しています。
    Google ColabではなくVSCodeで実行しているからなのでしょうか。

    こちらtest_data15件分となります。
    もしよろしければ、お手数ですがこちらの画像で試していただけますと幸いです。何度も大変申し訳ありません。
    scissors01-004_png.rf.3124bea32d287a0a4b40e49badf27c86.jpg

    scissors01-003_png.rf.82dc0c8bfcee9dc7d6e9a23f35aaea27.jpg

    scissors01-002_png.rf.28b952cd4f9cc6b92206b44cafbeab8e.jpg

    scissors01-001_png.rf.8d187e9490e7fbe668b0e3fd9aa18893.jpg

    scissors01-000_png.rf.bc8ea3d7b607fa5306391e214675bc07.jpg

    rock01-004_png.rf.5c07abaf4a47a695770b21fd2c4d6806.jpg

    rock01-003_png.rf.cb5a753750cd97748fde4322e9c30900.jpg

    rock01-002_png.rf.ac9c853f9bc1a922ae93afff7c05c498.jpg

    rock01-001_png.rf.fa47696183f6235423fff623888f0cea.jpg

    rock01-000_png.rf.560ebe5b8570f6866c33946448ccf7de.jpg

    paper01-004_png.rf.1a44a693f9f1c6be3a871f8623f6c348.jpg

    paper01-003_png.rf.a3b082be3b21ee78405f824589840675.jpg

    paper01-002_png.rf.5e391d961a442175fae483eceea3c1de.jpg

    paper01-001_png.rf.39f9acfaff12d82fe5b24c09ca016f12.jpg

    paper01-000_png.rf.02152baa06324655efacad9c5bda9f1a.jpg

  8. 画像ありがとうございます.
    まず,先に述べた通り,先の変更でエラーTypeError: unsupported operand type(s) for +=: 'Dense' and 'str'は解消されてました.

    ただし,IndexError: tuple index out of rangeの方は,次の修正で解消されることが確認できました.

    model.fit(
        np.array(x), np.array(y)
    )
    

    これは,x,yがそれぞれlistの中にnp.ndarrayがあるという複雑な状態を形成していたことに由来するものです.いずれかに統一することで解消されますが今回は後者に統一するためnp.array(x)という形式で変換しました.

    次に,このエラーを解消した後に新たなエラーが発生しますが,これはyに対して出力層Dense(3, activation='softmax')の形状と使用する損失関数loss='categorical_crossentropy',が適合しないことによるものです.

    loss='sparse_categorical_crossentropy',に修正すると直るはずです.もしくはyをOne-Hot Encodingしてください.

  9. @hoge_piyo

    Questioner

    何度も質問に答えて頂き、本当にありがとうございます。
    Conv2Dなどkerasについてより学習していきたいと思います。

Your answer might help someone💌