vitalwarley / research

3 stars 0 forks source link

Reproduzir "Kinship Representation Learning with Face Componential Relation" #67

Closed vitalwarley closed 8 months ago

vitalwarley commented 8 months ago

https://github.com/wtnthu/FaCoR/

Criei um requirements.txt a partir do environment.yml porque não gosto de usar conda. Abaixo os detalhes

➜  FaCoR git:(main) ✗ workon facor
➜  FaCoR git:(main) ✗ python --version
Python 3.10.12
➜  FaCoR git:(main) ✗ which python
/home/warley/.virtualenvs/facor/bin/python
➜  FaCoR git:(main) ✗ cat requirements.txt
einops
keras
matplotlib
numpy
pandas
scikit_learn
scipy
seaborn
torch
torchvision
tqdm
vitalwarley commented 8 months ago

Consegui executar o run.sh provido pelos autores. No entanto, precisei realizar algumas modificações:

Ver diff ``` diff --git a/find.py b/find.py index 32bdd53..d3a242a 100644 --- a/find.py +++ b/find.py @@ -3,7 +3,6 @@ from sklearn.metrics import roc_curve,auc from dataset import * from models import * from utils import * -import torch_resnet101 import torch from torch.optim import SGD from losses import * @@ -30,8 +29,10 @@ def find(args): # pdb.set_trace() method=args.method + generator=torch.Generator(device='cuda') + val_dataset = FIW(os.path.join(args.sample,"val_A.txt")) - val_loader = DataLoader(val_dataset, batch_size=batch_size, num_workers=0, pin_memory=False) + val_loader = DataLoader(val_dataset, batch_size=batch_size, num_workers=0, pin_memory=False, generator=generator) if arch=='ada3': model=Net_ada3().cuda() diff --git a/models.py b/models.py index 7570092..5c85af6 100644 --- a/models.py +++ b/models.py @@ -1,4 +1,5 @@ import torch, pdb +from torch import nn from inference import load_pretrained_model, to_input @@ -437,4 +438,4 @@ class CALayer2(nn.Module): y=x # pdb.set_trace() y = self.ca(y) - return y \ No newline at end of file + return y diff --git a/run.sh b/run.sh index d46bdeb..bb1803b 100644 --- a/run.sh +++ b/run.sh @@ -1,11 +1,11 @@ -export CUDA_VISIBLE_DEVICES=0,1 +#export CUDA_VISIBLE_DEVICES=0 model=ada3 lr=1e-4 batch_size=50 method=cont epochs=50 beta=0.08 -txt=train_sort_A2_m,.txt +txt=train_sort_A2_m.txt all=1 name=FaCoRNet_Adaface_lr_${lr}_beta diff --git a/train_p.py b/train_p.py index 58590a4..746e03a 100644 --- a/train_p.py +++ b/train_p.py @@ -1,3 +1,6 @@ +import os +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + import gc, pdb from random import shuffle from sklearn.metrics import roc_curve,auc @@ -32,15 +35,17 @@ def training(args): aug=args.aug txt = args.txt - train_dataset=FIW2(os.path.join(args.sample,args.txt)) - val_dataset=FIW2(os.path.join(args.sample,"val_choose_A_m.txt")) - train_loader=DataLoader(train_dataset,batch_size=batch_size,num_workers=0,pin_memory=False, shuffle=True) - val_loader = DataLoader(val_dataset, batch_size=val_batch_size, num_workers=0, pin_memory=False) + generator=torch.Generator(device='cuda') + + train_dataset=FIW(os.path.join(args.sample,args.txt)) + val_dataset=FIW(os.path.join(args.sample,"val_choose_A.txt")) + train_loader=DataLoader(train_dataset,batch_size=batch_size,num_workers=4,pin_memory=False, shuffle=True, generator=generator) + val_loader = DataLoader(val_dataset, batch_size=val_batch_size, num_workers=4, pin_memory=False, generator=generator) if arch=='ada3': model=Net_ada3().cuda() - model = torch.nn.DataParallel(model).cuda() + model.cuda() optimizer_model = SGD(model.parameters(), lr=args.lr, momentum=0.9) max_auc=0.0 @@ -53,7 +58,11 @@ def training(args): model.train() for index_i, data in enumerate(train_loader): - image1, image2, labels, kin_label,_ = data + #image1, image2, labels, kin_label,_ = data + image1, image2, labels, kin_label= data + + image1.cuda() + image2.cuda() if method=='cont': e1,e2,x1,x2,att= model([image1,image2], aug=False) @@ -95,8 +104,8 @@ def save_model(model,path): def val_model(model, val_loader, aug): y_true = [] y_pred = [] - # for img1, img2, labels, _ in val_loader: - for img1, img2, labels, _, _ in val_loader: + for img1, img2, labels, _ in val_loader: + #for img1, img2, labels, _, _ in val_loader: e1,e2,x1,x2,_=model([img1.cuda(),img2.cuda()]) if args.method=='sig': ```

Atualmente na época 41/50 (~30s/época). Até agora, a melhor AUC foi de 0.875248. Ao fim, será feito a busca do melhor limiar e avaliação do modelo no conjunto de teste.

No próximo comentário trago as diferenças entre o que está no paper e o que está no código.

vitalwarley commented 8 months ago

Precisei também remover o DataParallel do find.py e test.py. Abaixo os resultados

➜  FaCoR git:(main) ✗ sh run.sh
... # find
auc :  0.8768921112720713
threshold : 0.11221975088119507
... # test
bb : 0.8248135593220339
ss : 0.8447885853488777
sibs : 0.8166373755125952
fd : 0.7936272081888723
md : 0.8152701391135555
fs : 0.8485496183206107
ms : 0.7989577069553017
gfgd : 0.7878103837471784
gmgd : 0.7323420074349443
gfgs : 0.6816326530612244
gmgs : 0.5642458100558659
avg : 0.8186850514556022
AUC=0.8979235895647995

A média das 7 relações principais é de 0.820, como reportado no paper.

vitalwarley commented 8 months ago

Em resumo, o modelo Net_ada3 retorna um mapa de atenção $\beta$ provido por FSNet2 e os vetores de atributos $(\mathbf{x^a{out}}, \mathbf{x^b{out}})$ providos juntamente por CAM_Module, FSNet2, e CCA.

A função perda usará do mapa de atenção provido pela FSNet2 para computação do $\psi$ -- uma temperatura adaptável ao batch. Esse mapa contém a similaridade entre as regiões espaciais -- tanto para um mesmo indivíduo, quanto para o outro do par. Podemos ver no código a seguir que há uma redução ao longo das colunas, depois da profundidade, onde no paper foi referenciado global sum pooling operation: beta = (beta**2).sum([1, 2])/500, que creio ser mesma coisa. Há um exponencial quadrático que não foi especificado no paper, no entanto. Adicionalmente, a perda usará das embeddings (x1, x2) que foram computadas usando o mapa de atributos de saída de FSNet2 e CAM_Module da seguinte forma: $(\mathbf{x^a{out}}, \mathbf{x^b{out}}) = (\text{CCA}(c_a || O_a), \text{CCA}(c_b || Ob))$. Isso difere do esquema do paper: $(\mathbf{x^a{out}}, \mathbf{x^b_{out}}) = (\text{CCA}(r_a || \text{CCA}(O_a)), \, \text{CCA}(r_b || \text{CCA}(O_b)))$.

energy = torch.bmm(proj_query, proj_key)
energy_new = torch.max(energy, -1, keepdim=True)[0].expand_as(energy)-energy
attention = self.softmax(energy_new)

No fim, o modelo está usando dois tipos de atenção:


Análise detalhada #### `train_p.py` -- modelo instanciado ```python if arch=='ada3': model=Net_ada3().cuda() ``` #### `models.py` -- `__init__` e `forward` de `Net_ada3`, classe `FSNet2` - Podemos ver na inicialização abaixo a definição do modelo, a definição de algumas camadas e variáveis (`channel`, `CA`). Dentre as camadas, não usaram `projection`, `CCA2`, `out` ou `CA1`. ```python def __init__(self): super(Net_ada3, self).__init__() # self.encoder=KitModel("./kit_resnet101.pkl") self.Ada_model = load_pretrained_model('ir_101') #''' self.projection=nn.Sequential( torch.nn.Linear(512*6, 256), torch.nn.BatchNorm1d(256), torch.nn.ReLU(), torch.nn.Linear(256, 1), ) #''' # self.projection = torch.nn.Linear(1024, 1) self.channel=64 CA = True # self.enhance_block = FSFNet(self.channel*8//4, CA) self.enhance_block1 = FSFNet2(self.channel*8, CA) self.enhance_block2 = CAM_Module(self.channel*8) self.CCA=CALayer2(1024) self.CCA2=CALayer(1024) self.out = nn.Sigmoid() self.avg_pool = nn.AdaptiveAvgPool2d(1) self.CA1 = CAM_Module(1536) ``` - Logo, temos o código abaixo. ```python def __init__(self): super(Net_ada3, self).__init__() self.Ada_model = load_pretrained_model('ir_101') self.channel=64 CA = True self.enhance_block1 = FSFNet2(self.channel*8, CA) self.enhance_block2 = CAM_Module(self.channel*8) self.CCA=CALayer2(1024) self.avg_pool = nn.AdaptiveAvgPool2d(1) ``` - Podemos ver na propagação para frente alguns problemas. Eu removi comentários, sentenças e espaços dispensáveis. ```python def forward(self, imgs, aug=False): img1,img2=imgs idx=[2,1,0] f1_0, x1_feat = self.Ada_model(img1[:,idx]) f2_0, x2_feat = self.Ada_model(img2[:,idx]) _, _ ,att_map0 = self.enhance_block1(x1_feat, x2_feat) f1_0 = l2_norm(f1_0) f2_0 = l2_norm(f2_0) x1_feat = l2_norm(x1_feat) x2_feat = l2_norm(x2_feat) f1_1, f2_1 ,att_map1 = self.enhance_block2(f1_0, f2_0) f1_2, f2_2 ,att_map2 = self.enhance_block1(x1_feat, x2_feat) f1_2 = torch.flatten(self.avg_pool(f1_2),1) f2_2 = torch.flatten(self.avg_pool(f2_2),1) wC = self.CCA(torch.cat([f1_1, f1_2],1).unsqueeze(2).unsqueeze(3)) wC = wC.view(-1,2,512)[:,:,:,None,None] f1s = f1_1.unsqueeze(2).unsqueeze(3) * wC[:,0] + f1_2.unsqueeze(2).unsqueeze(3)* wC[:,1] wC2 = self.CCA(torch.cat([f2_1, f2_2],1).unsqueeze(2).unsqueeze(3)) wC2 = wC2.view(-1,2,512)[:,:,:,None,None] f2s = f2_1.unsqueeze(2).unsqueeze(3) * wC2[:,0] + f2_2.unsqueeze(2).unsqueeze(3)* wC2[:,1] f1s = torch.flatten(f1s,1) f2s = torch.flatten(f2s,1) return f1s,f2s,f1s,f2s, att_map0 ``` - **Associações entre variáveis e o paper** - $(\mathbf{X}^a, \mathbf{X}^b)$ no diagrama do paper podem ser os mapas de atributos não normalizados (`x1_feat`, `x2_feat`). - $\mathbf{\beta}$ pode ser `att_map0` - $(\mathbf{O}^a, \mathbf{O}^b)$ podem ser `f1_2` e `f2_2` - $(\mathbf{r}^a, \mathbf{r}^b)$ podem ser vetores de atributos provido pelo backbone; são devidamente normalizados. - $(\mathbf{c}^a, \mathbf{c}^b)$ não tem equivalente - `enhance_block1` (`FSFNet2`, a camada de *self-attention*) é usado duas vezes: - 1) produz $\mathbf{\beta}$ - Computado em cima de $(\mathbf{X}^a, \mathbf{X}^b)$ - Retornado ao fim do método. - 2) produz $(\mathbf{O}^a, \mathbf{O}^b)$ - Computados em cima de $(\mathbf{X}^a, \mathbf{X}^b)$ normalizados. - Usados ao longo da propagação. - `enhance_block2` (`CAM_Module`, o módulo de *channel attention*) é usado uma vez - Produz $(\mathbf{c}^a, \mathbf{c}^b)$ similarmente à $(\mathbf{O}^a, \mathbf{O}^b)$ [ver diferenças no final desse conteúdo], mas reorganizado em (B, C * H * W) - Produz também o mapa de atenção (`att_map1`), mas que não é usado. - Há um processamento de $\mathbf{O}^a$ e $\mathbf{O}^b$ : `avg_pool`, que os transforma de (B, C, W, H) para (B, C, 1, 1). Depois são achatados para (B, C). - Isso **não foi apresentado** no paper. - Módulo *Channel Interaction* do paper tem seu equivalente em `CCA` - Cada vetor em $(\mathbf{c}^a, \mathbf{c}^b)$ [saída de `CAM_Module`] é concatenado com $(\mathbf{O}^a, \mathbf{O}^b)$ [saída de `FSNet2`, mas após `avg_pool`). - O vetor resultante é passado ao módulo `CCA`, que consiste em (`Conv2d`1x1, `ReLU`, `Conv2d` 1x1, `Sigmoid`). - O resultado, `wC`, multiplica a entrada que o produziu. Isso é feito para os dois vetores. - **Diferenças** - Esse `CI` difere um pouco do paper, pois apenas produz $\mathbf{w}$ em vez de $\mathbf{w}\hat{\mathbf{x}}$, onde $\hat{\mathbf{x}}$ é a entrada. No código, essa multiplicação elemento a elemento é feita no próprio `forward` da rede. - No paper, é dito que vetor de atributos $(\mathbf{r}^a, \mathbf{r}^b)$ deveriam ser concatenados com $(\mathbf{O}^a, \mathbf{O}^b)$, mas isso não acontece. - No paper, `CCA` é usado duas vezes seguidas. - `CCA(O_a, O_b) => (cca_O_a, cca_O_b)` e `CCA(r_a || cca_O_a, r_b || cca_O_b)`. - $(\mathbf{x^a_{out}}, \mathbf{x^b_{out}}) = (\text{CCA}(r_a || \text{CCA}(O_a)), \, \text{CCA}(r_b || \text{CCA}(O_b)))$ - Todavia, temos `CCA(CAM_Module(r_a) || O_a), CAM_Module(r_b) || O_b) => (f1s, f2s)`. - $(\mathbf{x^a_{out}}, \mathbf{x^b_{out}}) = (\text{CCA}(c_a || O_a), \text{CCA}(c_b || O_b))$ - Por fim, há um achatamento de `f1s` e `f2s`, que deveriam representar $(\mathbf{x^a_{out}}, \mathbf{x^b_{out}})$. O retorno da propagação repete esses vetores, algo redudante. - Em resumo, o modelo retorna um mapa de atenção $\beta$ provido por `FSNet2` e os vetores de atributos $(\mathbf{x^a_{out}}, \mathbf{x^b_{out}})$ providos juntamente por `CAM_Module`, `FSNet2`, e `CCA`. - `FSNet2` contém um código bem similar à `CAM_Module`. Analiso abaixo, mas antes removo os comentários e sentenças inúteis para diminuir o tamanho do código. Nos comentários, no entanto, há trechos que refletem o paper, mas que aparentemente não foram usados no desenvolvimento do modelo final: `# out = self.gamma*out + x`. Em outras palavras, `gamma` não foi usado no modelo final. #### FSNet2 ```python def __init__(self,in_dim, CA=False): super(FSFNet2,self).__init__() self.chanel_in = in_dim reduction_ratio=16 self.query_conv = nn.Conv2d(in_channels = in_dim*2 , out_channels = in_dim//reduction_ratio , kernel_size= 1) self.key_conv = nn.Conv2d(in_channels = in_dim*2 , out_channels = in_dim//reduction_ratio , kernel_size= 1) self.value_conv1 = nn.Conv2d(in_channels = in_dim , out_channels = in_dim , kernel_size= 1) self.value_conv2 = nn.Conv2d(in_channels = in_dim , out_channels = in_dim , kernel_size= 1) self.softmax = nn.Softmax(dim=-1) # def forward(self, x1, x2): """ inputs : x : input feature maps( B X C X W X H) returns : out : self attention value + input feature attention: B X N X N (N is Width*Height) """ m_batchsize,C,width ,height = x1.size() x = torch.cat([x1, x2],1) proj_query = self.query_conv(x).view(m_batchsize,-1,width*height).permute(0,2,1) # B X CX(N) proj_key = self.key_conv(x).view(m_batchsize,-1,width*height) # B X C x (*W*H) energy = torch.bmm(proj_query,proj_key) # transpose check attention = self.softmax(energy) # BX (N) X (N) proj_value1 = self.value_conv1(x1) proj_value2 = self.value_conv2(x2) proj_value1 = proj_value1.view(m_batchsize,-1,width*height) # B X C X N proj_value2 = proj_value2.view(m_batchsize,-1,width*height) # B X C X N out1 = torch.bmm(proj_value1,attention.permute(0,2,1) ) out2 = torch.bmm(proj_value2,attention.permute(0,2,1) ) out1 = out1.view(m_batchsize,-1,width,height) + x1.view(m_batchsize,-1,width,height) out2 = out2.view(m_batchsize,-1,width,height) + x2.view(m_batchsize,-1,width,height) return out1, out2, attention ``` - A computação de `attention` difere levemente do paper: faltou uma transposição de `attention`. Se considerarmos `(proj_query, proj_key)` como os mapas de atributos $(\mathbf{F}^a, \mathbf{F}^b)$, então cada linha de de $\beta$ refere-se aos scores de atenção de um mapa $\mathbf{F}^a_i$ para todos maps em $\mathbf{F}^b$. Deveria ser o inverso: $\mathbf{\beta}_i$ representando os scores de atenção de todos os mapas em $\mathbf{F}^a$ para um mapa $\mathbf{F}^b_i$. Faltou uma transposição de `energy`. Essa transposição é feita, no entanto, ao computar `(out1, out2)`, mas isso muda o resultado porque `softmax` já foi realizada. - Da forma que está, a atenção ao ser transposta pode ser interpretada assim: cada coluna de $\beta$ refere-se aos scores de atenção de todos os mapas $\mathbf{F}^a$ para um mapa $j$ em $\mathbf{F}^b$ ($j$ representa a coluna aqui). Cada coluna tem uma soma total de 1. - Mais gravemente, a saída (`out1`, `out2`) é computada com `proj_value` (1 e 2) sobre a atenção, mas no paper usa-se os mapas de atributos originais (`x1`, `x2`). Também não se usa $\lambda$ para ponderar `out` (1 e 2). - Originalmente, segundo o paper, temos $\mathbf{O}^a_j = \mathbf{X}^a + \lambda \sum_{i=1}^N \beta_{j,i} \mathbf{X}^a_i$, onde $\mathbf{\beta}_{j,i}$ é um escalar e $\mathbf{X}_i \in (1 \times HW)$ [row vector]. A transposição aqui é implícita. $\beta_{j,i}$ representa a atenção de $\mathbf{F}^a_i$ para $\mathbf{F}^b_j$, e $\mathbf{X}^a_i$ o $i$-ésimo mapa de atributos de $\mathbf{X}^a$. Creio que $\mathbf{X}^a_i$ e $\mathbf{F}^a_i$ representam o mesmo mapa posicionalmente falando, mas que esse último é o resultado de convoluções 1x1 ao longo dos canais. Da forma que está escrito, penso que a saída $\mathbf{O}^a$ é a soma da agregação de todos os mapas de $\mathbf{F}^a$ ponderados pelo seus respectivos valores de atenção relativo à $\mathbf{F}^b_j$ com $\mathbf{X}^a$. - No entanto, eu penso faltou um $j$, tal que $\mathbf{O}^a_j = \mathbf{X}^a_j + \lambda(\beta_{j,1}\mathbf{X}^a_1 + \beta_{j,2}\mathbf{X}^a_2 + ... \beta_{j,HW}\mathbf{X}^a_HW)$, que faz mais sentido. Ainda assim, não entendo porque se soma TODOS os mapas de atributo ponderados pelos seus respectivos valores de atenção. - Da forma que está, considerando que cada linha de `proj_value` é um mapa de atributo (processado por convs 1x1), então cada linha de `out` é uma representação de `proj_value` sensível a todo o contexto, pois incorpora informações de todos os mapas de atributos ponderados por suas relevâncias à cada linha de `proj_value`. - Note que para `out1`, por exemplo, cada coluna de `attention` ($\beta$) refere-se aos scores de atenção de todos os mapas $\mathbf{F}^a$ para um mapa $j$ em $\mathbf{F}^b$ ($j$ representa a coluna aqui). Em outras palavras, cada linha de `out1` é uma representação de um mapa de atributos de `proj_value1` sensível a todo o contexto de $\mathbf{F}^a$ e $\mathbf{F}^b$. Especificamente, nessa linha, cada elemento $j$ representa a importância do mapa de atributos $j$ de `proj_value` relativo a todos os mapas de $\mathbf{F}^a$ e apenas um $\mathbf{F}^b_j$, pois `out1_ij = proj_value_row_i * attention_col_j` e cada coluna $j$ de `attention` são os *scores* de atenção de todo $\mathbf{F}^a$ para um $\mathbf{F}^b_j$. - Um detalhe importante é que na atenção aqui computada, há também informações de cada feature map sobre si mesmo, e não somente sobre o outro, pois temos `x = torch.cat([x1, x2], 1)`. #### CAM_Module ```python def __init__(self, in_dim): super(CAM_Module, self).__init__() self.chanel_in = in_dim self.conv = nn.Conv2d(in_channels = in_dim*2 , out_channels = in_dim , kernel_size= 1) self.gamma = nn.Parameter(torch.zeros(1)) self.softmax = nn.Softmax(dim=-1) def forward(self, x1, x2): """ inputs : x : input feature maps( B X C X H X W) returns : out : attention value + input feature attention: B X C X C """ x1 = x1.unsqueeze(2).unsqueeze(3) x2 = x2.unsqueeze(2).unsqueeze(3) x = torch.cat([x1, x2], 1) x = self.conv(x) m_batchsize, C, height, width = x.size() proj_query = x.view(m_batchsize, C, -1) proj_key = x.view(m_batchsize, C, -1).permute(0, 2, 1) energy = torch.bmm(proj_query, proj_key) energy_new = torch.max(energy, -1, keepdim=True)[0].expand_as(energy)-energy attention = self.softmax(energy_new) proj_value1 = x1.view(m_batchsize, C, -1) proj_value2 = x2.view(m_batchsize, C, -1) out1 = torch.bmm(attention, proj_value1) out1 = out1.view(m_batchsize, C, height, width) out2 = torch.bmm(attention, proj_value2) out2 = out2.view(m_batchsize, C, height, width) out1 = out1 + x1 out2 = out2 + x2 # out = self.gamma*out + x return out1.reshape(m_batchsize, -1), out2.reshape(m_batchsize, -1), attention ``` - É um código mais legível e um pouco diferente daquele de `FSNet2`. Em vez de uma convolução para cada projeção `(proj_query, proj_key)`, temos uma convolução para as duas. Há diferenças no número de canais também. Aqui temos `in_dim` canais, enquanto na `FSNet2` temos `in_dim / 16`. Por fim, `proj_key` é transposta, enquanto que na `FSNet2` é `proj_query`. - Antes tínhamos $(B, WH, C) \times (B, C, WH)$, mas agora é o inverso $(B, C, WH) \times (B, WH, C)$. Isso muda completamente a atenção. Antes estávamos atendendo entre as posições espaciais entre si -- atenção sobre a dimensão de atributos/canais -- em busca de obter a similaridade entre todas as regiões espaciais. Agora estamos atendendo mapa à mapa -- atenção sobre a dimensão espacial -- em busca de obter a similaridade entre os mapas de atributos como um todo. - Antes cada linha de `energy` representava a energia de uma região de `x` (já processada via convs 1x1, logo contendo informações dos demais canais) para todos as outras regiões em `x` (também já processadas via convs 1x1). Agora cada linha de `energy` representa a energia de um mapa de `x` para todos os mapas em `x`. - A atenção também é diferente: aqui subtrai-se os valores máximos de cada linha de `energy` antes do softmax. Isso inverte os valores da atenção: valores maiores se tornam menores. - As saídas `(out1, out2)` são a partir de uma atenção não transposta, como no paper, bem como corretamente sobre os mapas de atributos originais `(x1, x2)`, que foram apenas reorganizados. - No `FSNet2`, temos `proj_value * attention` (BxCxN * BxNxN -> BxCxN), tal que cada linha de `out` seja o respectivo mapa reajustado a partir dos seus valores de atenção (lembre que a coluna de `attention` representa a atenção de todos mapas $\mathbf{F}^a$ para um específico de $\mathbf{F}^b$). Por exemplo, `out_ij` é a representação final do mapa `proj_value_row_i` após ser ponderado por `attention_col_j`, que informou a importância dos mapas $\mathbf{F}^a$ para $\mathbf{F}^b_j$. - Agora temos `attention * proj_value` (BxCxC * BxCxN -> BxCxN), tal que cada linha $i$ de `out` seja o mapa $i$ de `projet_value` reajustado a partir da atenção de um mapa $i$ relativo à todos os outros. Por exemplo, `out_ij` é a representação final da região `proj_value_col_j` (pode ser entendido como a região $j$ ao longo de todos os mapas de atributos) após ser ponderado por `attention_row_i` (pode ser entendido como a similaridade do mapa $i$ relativo a todos os outros mapas). - Mais uma vez não há uso do $\lambda$. - Qual a diferença entre `att_map0` e `att_map1`? - `att_map0` tem shape (BxNxN), enquanto que `att_map1` tem shape (BxCxC). No primeiro caso temos uma atenção de uma região relativa a todos as outras regiões no mapa de atributos -- isso ao longo da dimensão espacial. No segundo caso, temos uma atenção de um mapa de `x` para todos os mapas em `x` -- isso ao longo dos canais.
vitalwarley commented 8 months ago

Penso que os próximo passos são

  1. Reorganizar e simplificar código funcional (o que executou durante o treinamento)
  2. Reproduzir resultados

Avaliar complementos/extensões/correções no meio tempo.

O que os autores descreveram no paper é bastante diferente do código final. De acordo com o repositório deles, o paper foi para o ICCV 2023 Workshop, todavia no paper lá publicado não há diferenças significativas no método exposto no paper do arxiv.

vitalwarley commented 8 months ago

50 segue mais à risca o que foi proposto tanto no #51

https://github.com/garynlfd/KFC/blob/ec488fbb7aa25f8a821a56804192d22b745cdff8/models_multi_task.py#L130-L134

A atenção é computada uma vez e a partir dos vetores de atributos achatados após as convoluções 1x1, como original proposto no paper do FaCoRNet.

vitalwarley commented 8 months ago

50 segue mais à risca o que foi proposto tanto no #51

https://github.com/garynlfd/KFC/blob/ec488fbb7aa25f8a821a56804192d22b745cdff8/models_multi_task.py#L130-L134

A atenção é computada uma vez e a partir dos vetores de atributos achatados após as convoluções 1x1, como original proposto no paper do FaCoRNet.

Eu tentei um experimento: permitir que o mapa de atenção seja usado na perda, como originalmente proposto no FaCoRNet. Todavia, o mapa de correlação, que usei como mapa de atenção, possui grandes valores (na casa de 10^5). Se eu escalo pela soma das linhas/colunas, o valor de beta será 1 em vez de estar no intervalo [0.08, 1].