Fender12345
@Fender12345 (Fender 12345)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

NNの全結合層の出力サイズを確かめたい

解決したいこと

NNで全結合層を通過後のデータサイズを確認したい

発生している問題・エラー

以下の「PytorchによるCNNの全結合層の入力サイズ(shape)を確かめる方法」を参考に
全結合層通過後のデータサイズを確認するコードをChainerを使いつつ作成しましたが、エラーとなっております

from chainer import Link, Chain, ChainList
import chainer.functions as F
import chainer.links as L


class MyChain(Chain):
    def __init__(self):
        super(MyChain, self).__init__(
            l1=L.Linear(4, 3),
            l2=L.Linear(3, 2)
        )

    def __call__(self, x):
        h = F.sigmoid(self.l1(x))
        o = self.l2(h)
        return o
    
    def check_cnn_size(self,size_check):
        out = o(size_check)
        return out
    
#畳み込み層の入力サイズと同じサイズのデータを用意
size_check = numpy.array([[1, 2, 3, 4, ] , [3 ,4, 5, 6, ] , [5, 6, 7, 8,] , [7, 8, 9, 10,]])

#畳み込み層の出力サイズを確認
print(MyChain.check_cnn_size(size_check).size()) #ここでエラー発生



エラー内容はこちらです。
check_cnn_sizeにsize_checkが引き渡せていないようですが、
慣れていないため、どのように修正すべきか分からず困っております。

image.png

0

3Answer

ひとまず,その出ているエラーは,以下のコードで再現します.

エラー再現
import numpy as np

class Sample:
    def __init__(self):
        pass
    def check_size(self, arr):
        return arr

arr = np.array([[0, 1], [2, 3]])
print(Sample.check_size(arr))

原因は,関数のみを呼び出したからだと考えられます.なので,ちゃんとクラスに()をつけてインスタンスを作成させて実行しましょう.

改善例
-   print(Sample.check_size(arr))   # これは関数check_size(self, arr)
+   print(Sample().check_size(arr)) # これは関数check_size(arr)

でなければ,第一引数がselfのままで扱われ,第二引数に何も渡されていない旨のエラーが出ます.

したがって,上のコードでは

改善案
-   print(MyChain.check_cnn_size(size_check).size())
+   print(MyChain().check_cnn_size(size_check).size()) 

とすれば今出ているエラーは解消されます.参考にされたページでも

#ネットワークの初期化
net = net().to(device)

としてインスタンスを作成していますよね.

しかし,今度は関数check_cnn_size()内でcallableoが未定義のため,

Traceback (most recent call last):
  File "code.py", line 27, in <module>
    print(MyChain().check_cnn_size(size_check).size())
  File "code.py", line 20, in check_cnn_size
    out = o(size_check)
NameError: name 'o' is not defined

のエラーが出ます.適宜解消してください.

1Like

Comments

  1. @Fender12345

    Questioner

    PondVillege様

    回答いただきありがとうございました!!
    インスタンスの生成不足で関数のみ呼び出したことが原因だったのですね
    お伝えいただいた方法でsize_checkが引き渡せるようになりました!!
    ありがとうございます。

    こちらを解決しても関数check_cnn_size内でoが未定義のためエラーとなるとのことでしたが、MyChainクラス内で同列にメソッドを定義したことで解消できているという認識でした。
    もし可能であれば、修正案をご教授いただけませんでしょうか?
    よろしくお願いします。

回答者コメントの欄でMarkdownが使えないので,こちらに回答します.

MyChainクラス内で同列にメソッドを定義したことで解消できているという認識でした。

というのは,関数__call__(self, x)内のo = self.l2(h)oを定義したつもりでいるということでしょうか.

これは変数のスコープの勉強をされるとわかると思いますが,特殊メソッド__call__()内でしか扱えないようになっていますし,上のコードでは一度も__call__()を呼んでいないので,hoの定義すらされていません.

本題

そもそも論,Chainer.links.Linearのドキュメントを読むと

class chainer.links.Linear(in_size: Optional[int], out_size: Optional[int] = None, ...)

・in_size (int or None) – Dimension of input vectors. If unspecified or None, parameter initialization will be deferred until the first forward data pass at which time the size will be determined.
・out_size (int) – Dimension of output vectors. If only one value is passed for in_size and out_size, that value will be used for the out_size dimension.

と書いてあることから第一引数が入力の数in_size,第二引数が出力の数out_sizeであることがわかります.

上のようにl2=L.Linear(3, 2)で定義したレイヤを出力にするのであれば,出力の次元数out_sizeは第二引数に設定した2のはずです.

この数字は,以下のコードに改修すると確認できます.

    def check_cnn_size(self,size_check):
-       out = o(size_check)
+       out = self(size_check)
        return out

size_check = numpy.array([[1, 2, 3, 4, ] , [3 ,4, 5, 6, ] , [5, 6, 7, 8,] , [7, 8, 9, 10,]], dtype = numpy.float32)

-print(MyChain().check_cnn_size(size_check).size()) # .size()はPyTorch.Tensorに対して行えるが
+print(MyChain().check_cnn_size(size_check).shape) # 今回はNumPy.array用の.shapeを使う

出力は(4, 2)になりました.値の意味としては(batch_size, output_shape)でしょうか,参考ページではPyTorchのTensorのサイズとして.size()を使っていましたが,今回は入力をNumPyのndarrayで使ったので出力もndarrayになることから,サイズ確認に使えそうな.shapeにしました.

もう少し合理的にコードを改修すると

class MyChain(Chain):
    def __init__(self):
        super(MyChain, self).__init__(
            l1=L.Linear(4, 3),
            l2=L.Linear(3, 2)
        )

    def __call__(self, x):
        h = F.sigmoid(self.l1(x))
        o = self.l2(h)
        return o

size_check = numpy.array([[1, 2, 3, 4, ] , [3 ,4, 5, 6, ] , [5, 6, 7, 8,] , [7, 8, 9, 10,]], dtype = numpy.float32)

print(MyChain()(size_check).shape)

これだけで良いです.いちいちcheck_cnn_size()なんて関数を用意せずとも,__call__(self, x)が定義されているので,このインスタンスはCallableになっています.このことを利用して,インスタンスを定義MyChain()したあとCallable呼び出し()が行えるからです.したがって,引数を渡すだけで値が出力されることを利用して,サイズ確認を行えます.

わかりやすく書くと

my_chain = MyChain()
print(my_chain(size_check).shape)

ですかね.

Pythonの基礎でつまづいているみたいなので,応用的な機械学習から取り組まれるのではなく,ちゃんと基礎から勉強されることをお勧めします.

なんなら機械学習もあまりよくわかっていないのでは...という気にさせます.そもそも,参考にされたページで畳み込み層の出力次元数を求めたのは,入力のshapeだけでなくstrideやpaddingに依存して決まるため計算するのがダルいからです.それに対して,全層結合層は用意したユニットの数だけ値が出力されるのでとてもシンプルで良いです.なのに全層結合層の出力サイズをコーディングして求めようとするとは...🤔

1Like

Comments

  1. 目的の別のモデルで畳み込み層やプーリング層後の出力を見るためであれば,動機は間違ってないですね.今後の展望のために書いたことは非常に良いことだと思います.また,Pythonは初学者でも扱いやすいことにメリットがありますので,どんどんコードを書いて,わからないことを教科書なり質問なりで解消しながら進めていくのが良いはずです.
    とりあえず,本質問を「クローズ」にしていただいて一旦終了になります.これからも頑張ってください.
  2. @Fender12345

    Questioner

    PondVillege様

    コメントありがとうございます。
    自習を続けつつ、エラーを解決して進めていきたいと思います。
    ありがとうございました。

PondVillege様

ご回答ありがとうございました。
ご指摘の通り、Pythonや機械学習の知識が不足している中での質問でした。
不勉強にも関わらず丁寧にご回答いただき大変感謝しております。

check_cnn_sizeという関数を使用したのは、
目的の別モデルで全結合層以外の畳み込み層やプーリング層後の出力も確認したかったことからこのような関数を定義するのが良いと判断しました。ただ、自身の知識や経験がない中での判断だったことから最適な選択ではなかったとということがご指摘から分かりました。

まずは不足している知識習得のために、参考書やドキュメントを勉強しつつ、PythonやChainerの基本を学びたいと思います。ありがとうございました。

1Like

Your answer might help someone💌