質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
PyTorch

PyTorchは、オープンソースのPython向けの機械学習ライブラリ。Facebookの人工知能研究グループが開発を主導しています。強力なGPUサポートを備えたテンソル計算、テープベースの自動微分による柔軟なニューラルネットワークの記述が可能です。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

1回答

738閲覧

Self Attentionのメカニズムについて

vanpy

総合スコア2

PyTorch

PyTorchは、オープンソースのPython向けの機械学習ライブラリ。Facebookの人工知能研究グループが開発を主導しています。強力なGPUサポートを備えたテンソル計算、テープベースの自動微分による柔軟なニューラルネットワークの記述が可能です。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2023/09/28 14:16

Self Attentionのメカニズムについて理解したい

pythonのコードからSelf Attentionのメカニズムについて理解したい。

前提

pythonのコードからSelf Attentionのメカニズムについて理解しようとしています。
ただ、Self Attentionの部分が1行で済まされているのでなかなかイメージが掴めません。
今のところ、該当のコードは単なる多重パーセプトロンで処理がされていると思いますが、
Self Attention = 多重パーセプトロン
なのでしょうか?
入力は動画のフレーム画像から抽出した3次元のテンソルとなります。
有識者の方々、ご教授いただけると幸いです。

該当のソースコード

python

1class VideoClassifier(nn.Module): 2 def __init__(self,args,r=3,da=64,attention=False): 3 super().__init__() 4 self.self_anttention = nn.Sequential(nn.Linear(args.feature_size,da),nn.Tanh(),nn.Linear(da,r)) 5 self.fc1 = nn.Linear(args.feature_size*r,32) 6 self.fc2 = nn.Linear(32,1) 7 self.dropout = nn.Dropout(0.3) 8 self.sig = nn.Sigmoid() 9 10 self.return_attention = attention 11 12 def forward(self,x): 13 bs,t,f = x.shape 14 attentionweight = self.dropout(F.softmax(self.self_anttention(x),dim=1)) 15 m = torch.bmm(x.permute(0,2,1),attentionweight) 16 x = m.view(bs,-1) 17 x = self.fc1(x) 18 x = self.fc2(x) 19 x = self.sig(x) 20 x = x.view(bs) #[batch] 21 22 if self.return_attention: 23 return x, attentionweight 24 else: 25 return x

試したこと

ここに問題に対して試したことを記載してください。

補足情報(FW/ツールのバージョンなど)

このコードのURLです。
https://github.com/yaegasikk/attention_anomaly_detector/

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

該当のコードは単なる多重パーセプトロンで処理がされていると思いますが、
Self Attention = 多重パーセプトロン
なのでしょうか?

その疑問を持つことはAttentionたる根源を知らないことによるものだと考えます.
そもAttentionとは,入力してきたデータに0~1の値を掛けることで重みづけを行い,1であれば要注視データ,0であれば不要データとして捨てるような処理を意味します.VideoClassifierでは,F.softmax()の出力がこの重みに該当し,これを入力値x.permute(0,2,1)に掛けており,Attentionの体を成しています.

ここでは,重みづけの処理をAttention Is All You Needで示されたようなScaled Dot-Product Attentionに使われるtorch.matmulを使っておらず,torch.bmmが似たような処理を担当しているといった状態です.そも重みを作るためのdot-productを多重パーセプトロンで代用しているので独自のSelf Attentionだと思ってください.

AttentionはAttention Is All You Needで示されたようなものに限らず,多種多様にわたり独自開発が多く存在します.
画像分類処理ではSCA-CNN: Spatial and Channel-wise Attention in Convolutional Networks for Image Captioningで使われるような,Spatial AttentionとChannel-wise Attentionという2種のSelf Attentionが有名です.

貴コードにあるVideoClassifierChatGPTに聞いたところChannel-wise Attentionぽいですね.

投稿2023/09/30 02:54

編集2023/09/30 05:06
PondVillege

総合スコア1581

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

vanpy

2023/09/30 09:03

詳しい解説ありがとうございました。 少し気になることがあるのですが、 >ここでは,重みづけの処理をAttention Is All You Needで示されたようなScaled Dot-Product Attentionに使われるtorch.matmulを使っておらず,torch.bmmが似たような処理を担当しているといった状態です.そも重みを作るためのdot-productを多重パーセプトロンで代用しているので独自のSelf Attentionだと思ってください. この回答について、つまり該当のコードは、Scaled Dot-Product Attentionの式に含まれるクエリ、キー、バリューの概念が存在していないということなのでしょうか?
PondVillege

2023/09/30 09:48

はい.そういうことです. Self Attentionでは入力データを使ってAttention重みを決定するからselfなのであって,それ以上でもそれ以下でもありません.したがって,バリュー(入力値)に対して,(入力値から作った)Attention重みを掛ける,という動作のみ一致しています. 対してQ, K, Vが必要になるシーンはSelf Attentionと比較してSoure-Target Attentionがあります.これはTransformerのデコーダ部分で使われていますね. 今回は,Scaled Dot-Productで作られる値の代わりに入力値をnn.Linear -> nn.Tanh -> nn.Linearで変換していますので,Self Attentionにおけるバリューの取り扱いが一致するだけに収まります.
vanpy

2023/10/01 06:41

なるほど。 なんとなくはイメージできました。 ただ、MLPの代用だと過学習になっちゃいませんか?
PondVillege

2023/10/01 10:12

そうでしょうか Scaled Dot-Productする前もQueryやKeyにするために入力値をnn.Linearに通してます ここで大事なのはSoftmax関数を通すことで(如何なる処理を経たにせよ)値の総和を1に正則化してデータの注視箇所を決定しているだけに留まっていることだと思います ここでは,Softmaxを通さずにnn.bmmで掛け合わせてたら過学習しそうですねという主張の方が理解できます
PondVillege

2023/10/02 01:54 編集

私の言いたいことがまとまっている動画ありましたので共有までに. vanpyさんの疑問とは少し異なりますが過学習については14:12~あたりから言及されています. https://www.youtube.com/watch?v=g5DSLeJozdw そもそもAttentionを使って過学習するということは,Attentionで選ばれる以前の値が過学習していることを示すことになりそうです. やはり私としては「注視箇所を過学習してしまう」というのは,機械学習で設定される最適化問題から考えてあまり実感が湧きません.過去に見た例としては競走馬の背景(特に地面)が草原であることから,馬の特定にあたって草原の特徴量まで注視対象になってしまう,みたいな現象は確認していますが,これはデータセットが偏りすぎていることに原因があります.十分にデータを集めれば注視箇所は一意に定まるはずです.
vanpy

2023/10/03 14:05

とても勉強になります。 また疑問が湧いてきたのですが、 >今回は,Scaled Dot-Productで作られる値の代わりに入力値をnn.Linear -> nn.Tanh -> nn.Linearで変換していますので,Self Attentionにおけるバリューの取り扱いが一致するだけに収まります. この部分で、Scaled Dot-Productの式がhttps://hotoke-x.hatenablog.com/entry/2022/11/06/142732のサイトのようにスケーリングの部分を√dkで担っているのですが、該当のコードはそれをTanh関数で代用している。 という理解で合っていますでしょうか? また、√dkに限らずScaled Dot-Product自体の式は多重パーセプトロンで代用しているという理解で合っていますでしょうか?
PondVillege

2023/10/03 16:05 編集

tanhに√dkで除算して得られるようなスケーリングの効果はないので,ここでは普通のMLPを作って非線形変換を目的とした活性化関数の適用だと思います.(ご存知の通りtanhの値域-1~1に収める効果はあります) > √dkに限らずScaled Dot-Product自体の式は多重パーセプトロンで代用しているという理解で合っていますでしょうか? はい.もうちょっとちゃんと書こうとしてScaled Dot-Productの式まわりとの対応をとると,nn.Linearを通すことは重みWとの内積をとることになるので,VideoClassifierのSelf Attentionでは Softmax(Dropout(Tanh(X⋅W1+b1)⋅W2+b2))⊗X のようになっていますね.普通はAttention後に一回nn.Linearを通した後にDropoutを挟むのでなんでここでdropout挟んでるのかわかんないけど紛れもない代用です(ここで挟んだほうが良かった.という検証過程があったのかもしれません).Scaled Dot-Productの欠点として計算量が要素数(正しくはシーケンス長)の2乗に比例してしまうので,MLPで代用することでここの計算量を抑制できる利点が考えられます. 式の話が出たまでに本題に戻ると,任意の関数F(⋅)を使って SelfAttention(X) = Softmax(F(X))⊗X と覚えてしまってもいいかもしれません.Softmaxの代わりにSigmoidを使う実装 https://cocoinit23.com/keras-channel-attention-spatial-attention/ もあったりするので最初に述べた「入力してきたデータに0~1の値を掛けることで重みづけを行い,1であれば要注視データ,0であれば不要データとして捨てるような処理」ができればなんでもいいかと思います.
vanpy

2023/10/11 05:20

すみません、追加で質問なのですが、MLPに通すことによってAttention Weightを求めるときと同様に内積が行われているという点で疑問に思っています。
vanpy

2023/10/15 13:31

今一番悩んでいる部分がSelf-Attentionの箇所です。 self.self_anttention = nn.Sequential(nn.Linear(args.feature_size,da),nn.Tanh(),nn.Linear(da,r)) この部分では入力が(10, 32, 2048)の三次元のテンソルの場合、一回目のLinearを通して(10, 32, 64)、Tanh関数適用後に二回目のLinearを通して(10, 32, 3)となると思うのですが、重みWがどう関わってくるのか、またどのように行列演算が行われているのかまだピンときてません。 また、このコードの部分は本当にMLPなのでしょうか?
PondVillege

2023/10/15 17:12

以下,Mathjax対応Markdown環境で読んでください(teratailは対応してないので).以下はQiitaの記事投稿プレビューで確認ながら記述しました. まず,MLPですがMulti Layer Perceptronの略であることはご存じだと思います.しかしオリジナルのPerceptron[^1]とは異なる活性化関数を利用していることから,Perceptronと呼ぶには少し差分があり,一般にはFFNN(FeedForward Neural Netrowk)と呼称するのが良さそうです[^2]. 全結合層(`torch.nn.Linear`や`keras.layers.Dense`)の動作が,どのように重み$\boldsymbol{W}$と関わるかですが,これは[torch.nn.Linear](https://pytorch.org/docs/stable/generated/torch.nn.Linear.html)の`shape`に関する説明から話すのが分かりやすいでしょう. > * Input: $(\ast, H_{in})$ where $\ast$ means any number of dimensions including none and $H_{in}=\text{in_features}$. > * Output: $(\ast, H_{out})$ where all but the last dimension are the same shape as the input and $H_{out}=\text{out_features}$. 引数は`torch.nn.Linear(in_features, out_features)`でしたね. ここでの$(\ast, H)$が意味するところは,何個の次元が$\ast$の箇所にあっても,最後の$H$に対して話を進めたい.というワイルドカードと同じ意味になっています. つまり, > この部分では入力が(10, 32, 2048)の三次元のテンソルの場合、一回目のLinearを通して(10, 32, 64)、Tanh関数適用後に二回目のLinearを通して(10, 32, 3)となる というのは`torch.nn.Linear`の説明の通り,最後の次元に対して$\text{in_features}$から$\text{out_features}$に変換できているというのが分かると思います. 質問者の疑問は,この現状に対して他の次元の扱いがどうなっているかが知りたい.ということだと解釈して話を進めます. > 重みWがどう関わってくるのか 答えから述べると,他の次元に対しては1つの$\boldsymbol{W}$を使いまわしている.ということになります. まず,`torch.nn.Linear`が持つParametersの1つであるweightは次のように書かれていますね. > * **weight**([torch.Tensor](https://pytorch.org/docs/stable/tensors.html#torch.Tensor)) - the learnable weights of the module of shape $(\text{out_features}, \text{in_features})$. The values are initialized from $\mathcal{U}(-\sqrt{k}, \sqrt{k})$, where $k=\frac{1}{\text{in_features}}$ ということで,実際に行われる計算は2階のテンソルであるweightを使って $$ \boldsymbol{y}=\boldsymbol{x}\cdot\boldsymbol{W}^T+b $$ となります.プログラムで書くと ```python y = nn.dot(x, torch.t(w)) + bias ``` でしょうか.さて機械学習ではミニバッチ法で実行されるので,各バッチに対しても計算がされているでしょうから,実際は ```python for batch in range(x.shape[0]): y[batch] = nn.dot(x[batch], torch.t(w)) + bias ``` ですね[^3].しかし,今回の入力は3階のテンソルでしたので更にforが1つ増えて ```python for batch in range(x.shape[0]): for i in range(x.shape[1]): y[batch, i] = nn.dot(x[batch, i], torch.t(w)) + bias ``` となっているでしょう.n階のテンソルとなればその分だけforを重ねることになりかねませんので,次のように再帰的に書くことになります. ```python def recursive_dot(x, w, bias, idx=()): if len(idx) < len(x.shape) - 2: for i in range(x.shape[len(idx)]): recursive_dot(x, w, bias, idx + (i,)) else: y[idx] = nn.dot(x[idx], torch.t(w)) + bias ``` 動くかはさておき,このように1つの2階のテンソル$\boldsymbol{W}$(及び`bias`)を使いまわす形で実装されています. [^1]: Rosenblatt, Frank. "The Perceptron — A Perceiving and Recognizing Automaton." Cornell Aeronautical Laboratory, 1957. [link](https://blogs.umass.edu/brain-wars/files/2016/03/rosenblatt-1957.pdf) [^2]: でもFFNNと併記で"a.k.a. MPLs"みたいな呼ばれ方をしている[こともある](https://medium.com/@b.terryjack/introduction-to-deep-learning-feed-forward-neural-networks-ffnns-a-k-a-c688d83a309d)のでもはやどっちでも良いと思います. [^3]: [NVIDIAの説明(Figure2の(a))](https://docs.nvidia.com/deeplearning/performance/dl-performance-fully-connected/index.html#fullyconnected-layer__fig13)では,Input Activationsに入ってる縦線で各バッチごとに分けられた入力vectorを示しておりわかりやすいと思います.
vanpy

2023/10/16 06:02

なるほど、つまり、NVIDIAのfigure2(a)の例だと、 FFNN1では、入力(10, 32, 2048)はInput Activations(2048*32)の行列とW(64*2048)の行列の内積をとって出力(10, 32, 64)を得る。FFNN2についても同様。 という理解で合っていますでしょうか?
PondVillege

2023/10/16 06:39

はい.そういう処理です.
vanpy

2023/10/16 11:08

とすると、次元0の10に関しては処理が行われるわけではないのですね。
PondVillege

2023/10/16 13:24

その10に関してはブロードキャストで処理が行われるはずです.
vanpy

2023/10/18 07:40

全結合層(FC)前の処理について、 AtentionWeightで得られた重みと元の特徴マップを掛け合わせる(表現合ってますか)ことで、どのピクセルに着目したら良いのかを求めることができると思います。 ただ、出力のAttentionWeight(10,64,3)と元の特徴マップ(10,32,2048)を掛け合わせるにはサイズが異なると思うのですが、処理は問題なく実行できました。なぜでしょうか? self.fc1 = nn.Linear(args.feature_size*r,32) もしかしてこのコードがミソなのですか?
PondVillege

2023/10/18 08:11

Softmaxの第二引数dim=1でランクが1つ下がるはずです.AttentionWeightの形状を確認してみてください.おそらく元の特徴マップとブロードキャスト可能なサイズになっているはずです. なんなら元の特徴マップもAttentionWeightと掛け合わせる前にx.permute(0,2,1)として形状変化させ,(10,32,2048)から(10,2048,32)とかになっていると思います.
vanpy

2023/10/18 12:18

なるほどです。 結局このAttentionWeightは各ピクセルを他のどのピクセルで特徴づけるべきなのかを決めるための行列を生成する と言う認識で合っていますでしょうか?
PondVillege

2023/10/19 00:54

「他の」はいらない方が整合性ありそうです 「結局このAttentionWeightは各ピクセル(に対しての重要度を判断するための重みであり)どのピクセルで特徴づけるべきなのかを決めるための行列を生成する」 特徴マップの全て値を使って,その全ての値に掛けるべき重みを決定していますからね.(「他の」とはどこから出てくるのかわからずこういう回答になってしまいます)
vanpy

2023/10/19 05:05

そして、 m = torch.bmm(x.permute(0,2,1),attentionweight) のようにAttention Weightと元の特徴マップの内積によってどのピクセルが重要であるかがわかる といった感じですね。 self.fc1 = nn.Linear(args.feature_size*r,32) そしてこのコードは入力サイズを合わせるために2048*3の処理を行っていると言うことですね。
PondVillege

2023/10/19 05:41

はい.そういうことです.
vanpy

2023/11/01 08:07

ありがとうございます。 ちなみになのですが、なぜda=64, r=3というパラメータのセットなのですか? 全結合層を通しているならば、この最終的な次元数の3と言うのは何かを分類するために設定しているのですか? ただ論文では7とかにも設定していたので違うと思いますが... このdaとrの存在意義がよく分からなくなってしまいました。
PondVillege

2023/11/01 08:33

FFNNの主目的は次元削減(すなわち情報抽出)にあるので,入力サイズ > da > r の関係を持っていればなんでもいいと思いますよ. 本来であればチューニングするべき値で,ハードコーディングされるべきではありません.だからコマンドライン引数で取っているのも納得できるはずです.
PondVillege

2023/11/01 09:29

今回であれば, (batch, time, feat)から次元削減して(batch, time, 64) -> (batch, time, 3)になり dim=1でSoftmaxをとる.すなわちtimeを軸にどの時間の特徴を参考にすべきか求めて, x.permute(0, 2, 1)で得られた(batch, feat, time)とSoftmaxを経た(batch, time, 3)を使って timeが消えるようにbmmで積算され,(batch, feat, 3)になり,これで時間に依存しない特徴量を得たことになり x.viewで平滑化して(batch, feat * 3)になったものを再度次元削減のために fc1で(batch, 32)にし,fc2で(batch, 1)にしていますね. ここで,rの影響としてはfc1への入力に取る特徴量の次元が増える率になると考えていいと思います. https://github.com/yaegasikk/attention_anomaly_detector/blob/main/network/video_classifier.py#L119 論文ではrにあまり影響しなかったと言ってますがいくらなんでも検証回数も信用ならないですし7が最適とか3が最適とか言うにはまだ早いと思います.da=64のときが一番モデルの性能が低いので私の期待するグラフが得られているように思いますが,他は性能が高すぎて局所的最適解を得たグラフ,すなわち初期値依存のように見えます. 本当だったら,初期値を変えまくって試行し,論文のような感じでいいのでグラフの描画を行なって,一番高いAUCを出す地点の値を使うべきです. CrossValidationもしてないので出すとこに出せば叩かれまくる論文だと思います.
vanpy

2023/11/01 13:14

ではこの論文の結果はたまたま良いものが得られたということですかね。 また、この論文ではSelf-Attention機構をhttps://arxiv.org/pdf/1703.03130.pdfから参考にしたようなのですが、この論文の機構を取り入れた理由として、時間的関係性を捉えるための機構を消しているので、軽量なモデルを実現できた。と言う認識でしょうか? また、コード中のbs,t,f = x.shapeとx = m.view(bs,-1)について、 このxはどこにも定義されていないようなのですが、pytorchのオプションのようなものなのですか? また、後者について、viewのこの形、調べても出てきませんでした。 第二引数に-1を設定することで出力結果(10, 2048*3)を得たのですがどうしてでしょうか?
PondVillege

2023/11/01 16:50 編集

> ではこの論文の結果はたまたま良いものが得られたということですかね。 そういうことになります. > 時間的関係性を捉えるための機構を消している 今回のネットワークは,CNNやRNN系に比べて,Transformer系と同様に帰納的バイアスが少ない.という表現が正しいです.時間的関係性を抽出するような学習をするかもしれません. CNNやRNN系に比べてパラメータ数は今回の方が多くなるはずなので,軽量とも一概には言えなさそうです.RNN系に比べて計算速度が速い.みたいな話であれば軽量といっても差し支えないと思います. > このxはどこにも定義されていない いいえ,`bs,t,f = x.shape`の1行上にメソッドの引数として取っています. つまり,`x`は`torch.Tensor`になると思います.viewはそのまま`torch.Tensor`のメソッドとして https://pytorch.org/docs/stable/generated/torch.Tensor.view.html 定義されています. > 第二引数に-1を設定することで出力結果(10, 2048*3)を得た これはnumpy.ndarray.reshapeでも見る表現で,ここで使われる`-1`は余剰の要素を示します. 他の例としてカラー画像(batch, channel, width, height)に対して`x.view(batch, -1)`という表現をとれば (batch, channel * width * height)になります.画像のチャンネルはそのままで平滑化する目的で `x.view(batch, channel, -1)`としたら(batch, channel, width * height)になります. > viewのこの形、調べても出てきませんでした。 `x.view(batch, -1)`あるいは同じ操作をする`torch.flatten()`や`reshape(batch, -1)`はClassifierにおいて超絶基本的な要素なので,これを知らないのは不勉強さが伺えるところです. そもそも「pytorch view」ってググったら一番上にさっき示したリンクのページが出てきます.なにも調べてないように感じます. また,最初期のCNNであるLeNetやResNetでは,`x = x.reshape(batch, -1)`でやっている人もいれば LeNet https://blog.paperspace.com/writing-lenet5-from-scratch-in-python/ ResNet https://qiita.com/tchih11/items/377cbf9162e78a639958 VGGでflatten使ってる人もいます https://github.com/Lornatang/VGG-PyTorch/blob/main/model.py#L82 https://pytorch.org/vision/0.8/_modules/torchvision/models/vgg.html 前も言ったかもしれませんがこのモデルは質問者が思ってるより変なことしてないです.調べるまでもなく,機械学習の基礎的なモデルを扱ったことがあれば知っている形式です.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問