2025/9/3の研究ログ
解決したいこと
埋め込みを並行して行う
BEATsはextract_embeddings.pyで埋め込みを作成 CNNはsed_trainer_pretrained.pyで作成
code: embCNN.py
# sed student forward
strong_preds_student, weak_preds_student = self.detect(
features,
self.sed_student,
embeddings=embeddings,
classes_mask=valid_class_mask,
)
BEATsの埋め込みの処理を取り出してきて一緒にやればいいか scpで手元に持ってきたので,こちらで編集してrsyncすればいいかな 中間特徴量の取得
中間特徴量の結合
CNNやCRNNなどのコードを直に書き換える方針を提示されたが,面倒なため継承する形が望ましい
次点で管理が大変
結合を行う疑似コード
code: concat_embeddings.py
def forward(self, x):
cnn_input = x
beats_input = x
for i in range(num_layers):
cnn_feat = self.cnn_layersi(cnn_input) beats_feat = self.beats_layers2*i:2*i+2(beats_input) # 2層分 fused = self.cross_attention(cnn_feat, beats_feat)
# 次の層の入力として両方に渡す
cnn_input = fused
beats_input = fused
# ...最終層へ...
return output
code: beats.py
from recipes.dcase2024_task4_baseline.local.beats.BEATs import BEATsModel
class CustomBEATsModel(BEATsModel):
def __init__(self, cfg_path):
super().__init__(cfg_path)
# 必要なら追加の初期化
def extract_intermediate_features(self, x):
# BEATsのextract_featuresを使って中間層特徴量を取得
features, padding_mask = self.model.extract_features(x)
# 例: layer_resultsを取得したい場合はBEATsの実装に合わせて
# layer_results = self.model.encoder(...)
# ここで2層ごとに特徴量をまとめる処理を追加
# return [layer_resultsi for i in ...] return features # 必要な形に整形して返す
# 使い方例
beats = CustomBEATsModel(cfg_path="path/to/checkpoint.pt")
input_waveform = ... # (batch, time)
features = beats.extract_intermediate_features(input_waveform)
code: cnn.py
from desed_task.nnet.CNN import CNN
class CustomCNN(CNN):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 必要なら追加の初期化
def extract_intermediate_features(self, x):
features = []
out = x
for layer in self.cnn:
out = layer(out)
# Conv2d層の出力を抽出
if isinstance(layer, torch.nn.Conv2d):
features.append(out)
return features # 各層の特徴量リスト
# 使い方例
cnn = CustomCNN(n_in_channel=1)
input_tensor = ... # (batch, channel, frames, freq)
features = cnn.extract_intermediate_features(input_tensor)
code: CrossAttention.py
import torch
import torch.nn as nn
class CrossAttention(nn.Module):
def __init__(self, dim_cnn, dim_beats, num_heads=4):
super().__init__()
self.proj_beats = nn.Linear(dim_beats, dim_cnn)
self.attn = nn.MultiheadAttention(embed_dim=dim_cnn, num_heads=num_heads, batch_first=True)
def forward(self, cnn_feat, beats_feat):
# (batch, seq_len, dim)
beats_proj = self.proj_beats(beats_feat)
# CrossAttention: query=cnn_feat, key/value=beats_proj
fused, _ = self.attn(cnn_feat, beats_proj, beats_proj)
return fused
# 使い方例
cross_attention = CrossAttention(dim_cnn=128, dim_beats=256)
cnn_features = ... # (batch, seq_len, dim_cnn)
beats_features = ... # (batch, seq_len, dim_beats)
fused_features = cross_attention(cnn_features, beats_features)
code: fusion.py
class FusionModel(nn.Module):
def __init__(self, cnn, beats, cross_attention_layers):
super().__init__()
self.cnn = cnn
self.beats = beats
self.cross_attentions = nn.ModuleList(cross_attention_layers)
def forward(self, x, beats_input):
# BEATs特徴量抽出
beats_feats = self.beats.extract_intermediate_features(beats_input)
# CNN特徴量抽出
cnn_feats = self.cnn.extract_intermediate_features(x)
out = x
for i, (cnn_feat, beats_feat) in enumerate(zip(cnn_feats, beats_feats)):
# shape調整(flatten, permuteなど)
cnn_feat_flat = cnn_feat.view(cnn_feat.size(0), -1, cnn_feat.size(1))
beats_feat_flat = beats_feat.view(beats_feat.size(0), -1, beats_feat.size(1))
# CrossAttentionで融合
out = self.cross_attentionsi(cnn_feat_flat, beats_feat_flat) # outを次のCNN層に渡す(必要なら再度特徴量抽出)
# ...最終出力処理...
return out
気になったのでバッチ正則化について少し見ていたが,あまり時間をかけすぎるのも良くなさそう 本当に必要になったら調べる方針の方が実装がスムーズに進みそう
上記の疑似コードをひとまず転記する
割とそのままで使える
というか疑似コードを書けといっても,ほとんどそのままのコードを書いてくるな
これまでに書いたもの
クラスの記述
CustomCNN
CustomBEATs
CrossAttention
各クラスのインスタンスを作成
CrossAttentionによる特徴量の結合
入力を与えて特徴量を得る
BEATsにどのような入力を与えると良いのか
extract_embeddingsを見よう
見てもよく分からない
BEATsを見る
前処理がextract_featuresに含まれている
入力としてwaveformを期待していそう
BEATs.pyのBEATsクラスのforward_padding_mask以下を見る
BEATsの埋め込みをCNNと同時に計算する場合,どこを書き換える?
従来のコードは埋め込みを事前に計算し,利用する形だった
それが丸々不要になるはず
特徴量を結合する
CNNのレイヤー分だけ反復するはず
お風呂で考えたこと
疑似コードを書こう
自分で考えたやつを書こう
各層を一つ一つ取り出してきて特徴量を計算させるようなコードを考える
今のコードを読んでみて,このままで行けそうならそれでも良い
多分最後はRNNに突っ込むか平滑化か何かを通すのだと思う