vitalwarley / research

3 stars 0 forks source link

Realizar treinamento do SOTA2021 com entropia cruzada como perda adicional #46

Closed vitalwarley closed 11 months ago

vitalwarley commented 11 months ago

Mudanças no modelo

--- a/rfiw2021/Track1/models.py
+++ b/rfiw2021/Track1/models.py
@@ -30,6 +30,9 @@ class Net(torch.nn.Module):
             torch.nn.ReLU(),
             torch.nn.Linear(256, 128),
         )
+        self.classifier = nn.Sequential(
+                torch.nn.Linear(256, 2)
+                )
         self._initialize_weights()

         if is_insightface:
@@ -58,7 +61,9 @@ class Net(torch.nn.Module):
         img1, img2 = imgs
         embeding1, embeding2 = self.encoder(img1), self.encoder(img2)
         pro1, pro2 = self.projection(embeding1), self.projection(embeding2)
-        return embeding1, embeding2, pro1, pro2
+        projs = torch.concat([pro1, pro2], dim=1)
+        logits = self.classifier(projs)
+        return embeding1, embeding2, pro1, pro2, logits

Mudanças no treinamento

+    ce_loss_fn = CrossEntropyLoss()
+
     for epoch_i in range(epochs):
         mylog("\n*************", path=log_path)
         mylog("epoch " + str(epoch_i + 1), path=log_path)
         contrastive_loss_epoch = 0
+        ce_loss_epoch = 0

         model.train()

         for index_i, data in enumerate(train_loader):
             image1, image2, labels = data

-            e1, e2, x1, x2 = model([image1, image2])
+            e1, e2, x1, x2, logits = model([image1, image2])

-            loss = contrastive_loss(x1, x2, beta=beta)
+            c_loss = contrastive_loss(x1, x2, beta=beta)
+            ce_loss = ce_loss_fn(logits, labels.to(torch.long))
+            loss = c_loss + ce_loss

             optimizer_model.zero_grad()
             loss.backward()
             optimizer_model.step()

-            contrastive_loss_epoch += loss.item()
+            contrastive_loss_epoch += c_loss.item()
+            ce_loss_epoch += ce_loss

Usei os scripts originais porque os meus não foram capazes de reproduzir os resultados do SOTA2021.

Abaixo as perdas e métricas ao longo do treinamento

arcface_ce arcface_cl

Em minha primeira reprodução do SOTA2021, consegui 0.864550, logo os resultados acima (0.864300) não foram melhores. Uma das razões, eu creio, é que no conjunto de treinamento há apenas amostras positivos. A perda contrastiva constrói os pares negativos por necessidade de sua formulação. Talvez devamos treinar para classificação de família, à lá SOTA2020? Cada batch contém apenas famílias únicas; essa é a ideia do SOTA2021. De certa forma, eu tentei algo semelhante na #39, todavia fiz dois treinamentos separados.

vitalwarley commented 11 months ago
╔╡[warley]:[vital-strix]➾[~/dev/research/kinship] | [on branch main] 
╚═╡(kinship) [10:53] λ python rfiw2021/Track1/check_families_per_batch.py --file-path rfiw2021/Track1/sample0/train_sort.txt --chunk-size 10
Not all chunks have unique families.
Number of chunks with unique families: 0.9950555089094132
First non-unique chunk: 4328
╔╡[warley]:[vital-strix]➾[~/dev/research/kinship] | [on branch main] 
╚═╡(kinship) [10:53] λ python rfiw2021/Track1/check_families_per_batch.py --file-path rfiw2021/Track1/sample0/train_sort.txt --chunk-size 20
Not all chunks have unique families.
Number of chunks with unique families: 0.9694029850746269
First non-unique chunk: 28
╔╡[warley]:[vital-strix]➾[~/dev/research/kinship] | [on branch main] 
╚═╡(kinship) [10:53] λ python rfiw2021/Track1/check_families_per_batch.py --file-path rfiw2021/Track1/sample0/train_sort.txt --chunk-size 40 
Not all chunks have unique families.
Number of chunks with unique families: 0.8645522388059701
First non-unique chunk: 14

Em alguns batches há mais que um par pertencentes a mesma família. Vale lembrar que todos os pares são positivos, no entanto.

vitalwarley commented 11 months ago

Em um novo treinamento após adicionar inicialização para camada de classificação, consegui apenas 0.856719 de AUC.

vitalwarley commented 11 months ago

Resultados não melhoraram.

arcface_contrastive_clf

Usei um lambda = 0.8, assim

            loss = lambda_factor  * c_loss + (1 - lambda_factor) * ce_loss

Próximo passo é a partir de agora usar meus scripts com modelo backbone do SOTA2021 tal que eu consegui realizar busca de hiperparâmetros, além de facilitar o gerenciamento dos experimentos.

vitalwarley commented 11 months ago
╔╡[warley]:[vital-strix]➾[~/dev/research/kinship] | [on branch main] 
╚═╡(kinship) [10:53] λ python rfiw2021/Track1/check_families_per_batch.py --file-path rfiw2021/Track1/sample0/train_sort.txt --chunk-size 10
Not all chunks have unique families.
Number of chunks with unique families: 0.9950555089094132
First non-unique chunk: 4328
╔╡[warley]:[vital-strix]➾[~/dev/research/kinship] | [on branch main] 
╚═╡(kinship) [10:53] λ python rfiw2021/Track1/check_families_per_batch.py --file-path rfiw2021/Track1/sample0/train_sort.txt --chunk-size 20
Not all chunks have unique families.
Number of chunks with unique families: 0.9694029850746269
First non-unique chunk: 28
╔╡[warley]:[vital-strix]➾[~/dev/research/kinship] | [on branch main] 
╚═╡(kinship) [10:53] λ python rfiw2021/Track1/check_families_per_batch.py --file-path rfiw2021/Track1/sample0/train_sort.txt --chunk-size 40 
Not all chunks have unique families.
Number of chunks with unique families: 0.8645522388059701
First non-unique chunk: 14

Em alguns batches há mais que um par pertencentes a mesma família. Vale lembrar que todos os pares são positivos, no entanto.

Isso é um erro. A perda contrastiva assume batches provindos de famílias únicas, como cito aqui. Em breve realizo um treinamento com batch de 10 para avaliar se isso é significativo.

vitalwarley commented 11 months ago

m breve realizo um treinamento com batch de 10 para avaliar se isso é significativo.

Aproveitando para avaliar outros hiper-parâmetros

$ guild run train-kv root-dir=rfiw2021/Track1 train-dataset-path=rfiw2021/Track1/sample0/train_sort.txt val-dataset-path=rfiw2021/Track1/sample0/val_choose.txt output-dir=exp insightface-weights=models/ms1mv3_arcface_r100_fp16.pth 'beta=[0.2, 0.4, 0.6, 0.8]' 'num-epoch=[80, 100, 105]' steps-per-epoch=100 batch-size=10 'normalize=[yes, no]' -t baseline -t ms1mv3 -t sota2021 -t myself
You are about to run train-kv as a batch (24 trials)
  batch-size: 10
  beta: [0.2, 0.4, 0.6, 0.8]
  device: '0'
  insightface-weights: models/ms1mv3_arcface_r100_fp16.pth
  lr: 0.0001
  normalize: [yes, no]
  num-classes: 570
  num-epoch: [80, 100, 105]
  output-dir: exp
  root-dir: rfiw2021/Track1
  steps-per-epoch: 100
  train-dataset-path: rfiw2021/Track1/sample0/train_sort.txt
  val-dataset-path: rfiw2021/Track1/sample0/val_choose.txt
Continue? (Y/n)

A ideia é avaliar um batch menor (10), mas isso implica precisar de mais passos por época (100). Tecnicamente, precisamos de 107 épocas para passar por todo o dataset, mas acabei escolhendo outras também.

vitalwarley commented 11 months ago

Os experimentos não iniciam por algum motivo que não entendo ainda. Tentarei em breve. Há outro experimento de outro projeto rodando que pode ser a causa (sem sentido, no entanto).

vitalwarley commented 11 months ago

Os experimentos não iniciam por algum motivo que não entendo ainda. Tentarei em breve. Há outro experimento de outro projeto rodando que pode ser a causa (sem sentido, no entanto).

Tanto na RIG2 quanto na RIG1 não consigo. Na RIG1 obtenho

➜  hybrid git:(main) ✗ guild cat --output 1
guild: /home/warley/.guild/runs/177d8228819c4998b1bbf7208498d032/.guild/output does not exist
vitalwarley commented 11 months ago

Os experimentos não iniciam por algum motivo que não entendo ainda. Tentarei em breve. Há outro experimento de outro projeto rodando que pode ser a causa (sem sentido, no entanto).

Tanto na RIG2 quanto na RIG1 não consigo. Na RIG1 obtenho

➜  hybrid git:(main) ✗ guild cat --output 1
guild: /home/warley/.guild/runs/177d8228819c4998b1bbf7208498d032/.guild/output does not exist

Era erro de config. Experimentos em curso.

vitalwarley commented 11 months ago
You are about to run train-kv as a batch (2 trials)
  batch-size: 20
  beta: 0.08
  device: '0'
  insightface-weights: models/ms1mv3_arcface_r100_fp16.pth
  lr: 0.0001
  normalize: [no, yes]
  num-classes: 12
  num-epoch: 105
  output-dir: exp
  root-dir: rfiw2021/Track1
  scl: yes
  scl-lambda: 0.8
  steps-per-epoch: 50
  train-dataset-path: rfiw2021/Track1/sample0/train_sort.txt
  val-dataset-path: rfiw2021/Track1/sample0/val_choose.txt
Continue? (Y/n) y

Adicionei esquema SCL de #45 no meus scripts.

vitalwarley commented 11 months ago

Todos os experimentos tendem a perder AUC em troca de ACC, embora alguns iniciem com ganhos em AUC. Seria a taxa de aprendizado? Outro padrão interessante é que o relacionamento entre o peso de cada perda (via scl-lambda) parece seguir um U, onde pequenos valores enfatizam a perda contrastiva e acabam resultando em melhor AUC.

image image

vitalwarley commented 11 months ago

Resolvi atualizar a taxa de aprendizado como feito no SOTA2020, isso é, permitir warmup, cooldown, decaimento dos pesos, bem como uso de MultiStepLR usando as mesmas épocas lá.

image

Em ambos os casos, com ou sem SCL, podemos ver um decrescimento bem menor da AUC e creio que por causa dessas adições. Na verdade, ao contrário do que notamos antes, com SCL há uma maior estabilidade da AUC ao passo que ACC cresce, embora bem pouco. É bom lembrar que o esquema do SOTA2021 permite apenas uma passada no dataset.

image

Aqui temos o mesmo experimento que o gráfico cinza, exceto que não há ajustes na LR.