vitalwarley / research

2 stars 0 forks source link

Mesclar estratégias de pré-treino do SOTA 2020 com perda contrastiva #39

Closed vitalwarley closed 9 months ago

vitalwarley commented 10 months ago

Achieving Better Kinship Recognition Through Better Baseline

The ArcFace [2] model was used for features extraction. This model was pre-trained on cleaned MS-Celeb-1M [3] dataset 3 and has the embedding dimension of 512.

We used Mxnet [28] for the implementation of our pipeline. For detection and feature extraction insightface python package was used. In particular, retinaface r50 v1 which is RetinaFace implementation with ResNet50 as the backbone and arcface r100 v1 which is modified ResNet101 trained with ArcFace loss on cleaned MS-Celeb-1M dataset.

Tal pré-treino é a primeira estratégia abaixo.

Image

We tested the simple fine-tuning using a classification layer (+classification) with a similar approach where the embeddings are normalized to have a unit L2 norm before the classification layer (+normalization).

Há mais detalhes sobre os hiperparâmetros e augmentações, todavia vou manter simples por agora.

Supervised Contrastive Learning for Facial Kinship Recognition

Image

Our network receives a face pair denoted by $(x, y)$ as input, and the extracted mid-level feature pair $(h_x, h_y)$ by the siamese backbone are fed into a MLP to obtain a low-dimensional feature pair $(f_x, f_y)$. Given a $n$ positive pairs $\mathcal{P} = {(x_i, yi)}{i=1}^n$ sampled from different families, the contrastive loss $L$ can be defined as:

$$ L = \frac{1}{2n} \sum_{i=1}^{n} \left[ L_c (x_i, y_i) + L_c (y_i, x_i) \right] \quad (1) $$

where

$$ L_c (x_i, y_i) = - \log \frac{e^{s(x_i,yi)}/\tau}{\sum{j=1}^{n} \left[ e^{s(x_i,x_j)}/\tau + e^{s(x_i,y_j)}/\tau \right]} \quad (2) $$

where $s(x, y)$ is defined as the cosine similarity between $x$ and $y$. In our supervised contrastive learning framework, negative samples are collected by combining positive samples from different families.

O poder dessa perda provavelmente vem da grande quantidade de exemplos negativos: 2 * (batch_size^2 - batch_size) pares negativos.


Basicamente, a diferença do SOTA 2021 para o 2020 é que aquele usou apenas a mesma rede (ArcFace, pré-treinada no ImageNet) com perda contrastiva. Isso é, não houve pré-treino em reconhecimento facial no MS-Celeb-1M, nem posterior ajuste fino na tarefa de classificação de famílias no FIW.

Roadmap

MS-Celeb-1M fine-tuning

FIW Family fine-tuning

vitalwarley commented 10 months ago

Dataset usado por Shadrikov (2020) é MS1M_v2: MS1M-ArcFace (cito em https://github.com/vitalwarley/research/issues/18#issuecomment-1119193911). Encontramos-o aqui; precisamente, nesse link.

vitalwarley commented 10 months ago

Foram 1h47min para terminar o processo. 5822653 amostras divididas em 85741 classes.


Apenas ao fim lembrei/percebi que o pessoal do deepinsight disponibilizam os modelos pré-treinados. Todavia, para o modelo em PyTorch, o pré-treino foi realizado no MS1MV2, cujo modelo mxnet usado pelo Shadrikov foi pré-treinado. Mais detalhes sobre os datasets:

Vou resgatar os códigos do ano passado para completar essa tarefa.

vitalwarley commented 10 months ago

CLI

╚═╡(kinship) [9:47] λ python val.py --data-dir ../datasets/faces_emore --insightface --weights models/ms1mv3_arcface_r100_fp16.pth --task pretrain --test

Modificações

Problemas

Resultados

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃   Runningstage.testing    ┃                           ┃                           ┃
┃          metric           ┃       DataLoader 0        ┃       DataLoader 1        ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│       agedb_30/acc        │    0.9825000762939453     │    0.9825000762939453     │
│       agedb_30/norm       │    23.078630447387695     │    23.078630447387695     │
│    agedb_30/threshold     │    1.2489999532699585     │    1.2489999532699585     │
│        cfp_fp/acc         │    0.9877142906188965     │    0.9877142906188965     │
│        cfp_fp/norm        │     22.41592788696289     │     22.41592788696289     │
│     cfp_fp/threshold      │    1.2700002193450928     │    1.2700002193450928     │
└───────────────────────────┴───────────────────────────┴───────────────────────────┘

Similar aos resultados originais com mxnet

Image


Próximo passo é revisar a #24 e continuar com roadmap dessa issue.

vitalwarley commented 10 months ago

Na #24, temos

  • Using arcface_torch (pre-trained model equivalent to ArcFace R100 MXNET) I got AUC = 0.7198 (vs 0.6717 from my pre-trained model) and ACC = 0.66 (same with mxnet).

Sendo assim, creio que usar o modelo pré-treinado é válido. Posso, então, pular o primeiro roadmap: MS-Celeb-1M fine-tuning. Antes de continuar para o segundo, no entanto, vou usar o modelo como está (pré-treinado em reconhecimento facial) para tarefa de verificação de parentesco com perda contrastiva (SOTA 2021).

A hipótese é que as features aprendidas no reconhecimento facial vão aumentar a acurácia reportada pelo trabalho #26.

vitalwarley commented 10 months ago
╚═╡(rfiw2021) [21:40] λ python Track1/train.py --batch_size 20 --sample Track1/sample0 --save_path Track1/models/arcface_fr_ft_kv.pth --epochs 80 --beta 0.08 --log_path Track1/logs/arcface_fr_ft_kv.txt --gpu 0 --insightface ../hybrid/models/ms1mv3_arcface_r100_fp16.pth
...
*************
epoch 1
...
contrastive_loss:3.287279
...
auc is 0.807662 
auc improve from :0.000000 to 0.807662
save model Track1/models/arcface_fr_ft_kv.pth

Já começou bem. AUC tem que passar de 0.865929 (#26).

vitalwarley commented 10 months ago

Não melhorou no treinamento.

arcface_fr_ft_kv

Nem no conjunto de validação para computação do limiar

auc :  0.8616017331936722
threshold : 0.06821151822805405

Usando o modelo original obtivemos AUC = 0.8671 e threshold = 0.1155.

Ainda assim, a média subiu 0.0023

Model Pretrained on train loss train auc val auc threshold bb ss sibs fd md fs ms gfgd gmgd gfgs gmgs avg
ArcFace ImageNet - 0.8646 0.8634 0.1132 0.8065 0.8135 0.7891 0.7586 0.7836 0.8113 0.7717 0.7788 0.7361 0.6571 0.6369 0.7898
ArcFace MS1MV3 0.08127 0.8598 0.8616 0.0682 0.7953 0.8183 0.7938 0.7804 0.8044 0.8096 0.7535 0.7675 0.7249 0.6408 0.5810 0.7921

Em detalhes, essas são as diferenças

Metric Relative Difference
bb -0.0112
ss 0.0048
sibs 0.0047
fd 0.0218
md 0.0208
fs -0.0017
ms -0.0182
gfgd -0.0113
gmgd -0.0112
gfgs -0.0163
gmgs -0.0559
avg 0.0023

É importante salientar que mantive os mesmos hparams, inclusive a lr. Talvez o próximo passo seja tuná-los, especialmente se considerarmos que o modelo não melhora a partir da época ~40.

vitalwarley commented 10 months ago

Tratei de avaliar o que precisava ser feito para realizar o ajuste fino em classificação de família. Basicamente, complementar o dataset.py de forma a poder retornar, se necessário, indivíduo e família; e complementar NetClassifier com um novo esquema de inferência para o caso de uma embedding apenas.

Precisamos, também, verificar

Próximo passo é revisar Shadrikov em busca de saber como ele preparou os dados para essa etapa.

vitalwarley commented 10 months ago

Ontem revisei o paper e código. Descobri que o SOTA 2020 tem uma família a mais no train set. Nada tão problemático.

Agora a noite eu adaptei o código de treino do Shadrikov. Estou conseguindo treinar. Todavia não dei muita atenção aos hparams usados. Só queria o código funcional.

Loaded insightface model.
[Epoch: 1, Mini-batch:   100] loss: 6.472
[Epoch: 1, Mini-batch:   200] loss: 6.341
[Epoch: 1, Mini-batch:   300] loss: 6.175
[Epoch: 2, Mini-batch:   100] loss: 5.884

Serão 20 épocas. É apenas um teste. Amanhã busco deixar o script 100% igual para de fato treinarmos o modelo.

vitalwarley commented 10 months ago
[Epoch: 19, Mini-batch:   300] loss: 0.191
[Epoch: 20, Mini-batch:   100] loss: 0.157
[Epoch: 20, Mini-batch:   200] loss: 0.163
[Epoch: 20, Mini-batch:   300] loss: 0.161

Tal treinamento durou menos de 1h. Ontem falei que era apenas um teste. Mesmo assim, vou agora realizar treinamento em verificação de parentesco. É bom que já esquematizo todo o pipeline.


Acontece que não há treinamento em verificação de parentesco. Segue minhas anotações de agora a noite

vitalwarley commented 10 months ago

Antes de adaptar o verification.py, estou realizando outro treinamento com normalização das embeddings antes da camada de classificação. O script de verificação compara as três estratégias, logo adaptá-lo dessa forma será mais útil.

╚═╡(kinship) [22:13] λ python train_fiw_clf.py --dataset-path ../fitw2020/train-faces-det --insightface-weights models/ms1mv3_arcface_r100_fp16.pth --output-dir arcface_fiw_clf_norm
Loaded insightface model.
[Epoch: 1, Mini-batch:   100] loss: 6.451
[Epoch: 1, Mini-batch:   200] loss: 6.317
[Epoch: 1, Mini-batch:   300] loss: 6.168
vitalwarley commented 10 months ago

Shadrikov (2020) used

2022-04-22-164921_839x711_scrot

Originally posted by @vitalwarley in https://github.com/vitalwarley/research/issues/18#issuecomment-1106821035

Meus resultados atuais cujo objetivo é apenas validar minha adaptação do treinamento e verificação

roc

É, meio que esperado. Não reproduzi os treinamentos com os mesmos hparams e afins.

vitalwarley commented 10 months ago

Considerando os resultados acima, penso que posso continuar com mesclagem das estratégias apenas após reproduzir o primeiro gráfico do comentário anterior. Isso é, a estratégia de classificação precisa alcançar AUC = 0.7762 e a estratégia com adição de normalização das features precisa alcançar AUC = 0.7912.

Note que usei o mesmo dataset de Shadrikov.

vitalwarley commented 10 months ago
╚═╡(kinship) [21:53] λ python train.py --dataset-path ../fitw2020/train-faces-det --insightface-weights models/ms1mv3_arcface_r100_fp16.pth --output-dir experiments
Loaded insightface model.
Total number of steps: 330
Start training at 2023-11-14 21:54:40
[Epoch:  1, Step:   100] loss: 6.476, accuracy: 0.002, lr: 5.000e-05
[Epoch:  1, Step:   200] loss: 6.414, accuracy: 0.003, lr: 1.000e-04
[Epoch:  1, Step:   300] loss: 6.360, accuracy: 0.006, lr: 1.000e-10
[Epoch  1] accuracy: 0.007, time: 0:02:24.531955 [steps/second: 2.283]
Finished training at 2023-11-14 21:57:04.

Teste do script OK. Vamos agora treinar de verdade.

vitalwarley commented 10 months ago

Treinamento de ontem

[Epoch: 20, Step:  6370] loss: 5.805, accuracy: 0.079, lr: 1.0000000000002073e-10
[Epoch: 20, Step:  6470] loss: 5.847, accuracy: 0.074, lr: 1e-10
[Epoch: 20, Step:  6570] loss: 5.817, accuracy: 0.076, lr: 1e-10
[Epoch 20] accuracy: 0.076, time: 0:02:26.393322 [steps/second: 2.254]
Finished training at 2023-11-14 23:24:47.

Provavelmente a causa foi o clip_grad

            torch.nn.utils.clip_grad_norm_(model.parameters(), CLIP_GRADIENT)

que é definido como 1. Antes de adicioná-lo, a acurácia estava subindo rapidamente

Start epoch at 2023-11-14 22:29:39
[Epoch:  4, Step:  1090] loss: 4.984, accuracy: 0.211, lr: 9.999999999999999e-05
[Epoch:  4, Step:  1190] loss: 4.862, accuracy: 0.228, lr: 9.999999999999999e-05
[Epoch:  4, Step:  1290] loss: 4.750, accuracy: 0.246, lr: 9.999999999999999e-05

Os detalhes de implementação de Shadrikov foram esses

Finetune the whole model on the train set with stochastic gradient descent with base learning rate of 0.0001, momentum 0.9, linear warmup for first 200 batches of size 64, linear cooldown for last 400 batches, multiplying learning rate by 0.75 on epochs 8, 14, 25, 35, 40 and gradient clipping 1.5 for 50 epochs.

embora em seu train.py existam diferenças (lr_steps com épocas 50 e 60 adicionadas, clip_gradient com valor 1.0, num_epoch com valor 20).


Treinamentos de hoje

Desativei gradient clipping.

Classificação (20231115091006)

lr loss accuracy

Classificação com normalização (20231115095948)

Epoch 19 - Acc: 0.056, Time: 0:02:31.561763, Steps/s: 2.177
Epoch 20 | Step  6370 - Loss: 6.310, Acc: 0.059, LR: 0.0000000001
Epoch 20 | Step  6470 - Loss: 6.314, Acc: 0.055, LR: 0.0000000001
Epoch 20 | Step  6570 - Loss: 6.316, Acc: 0.056, LR: 0.0000000001
Epoch 20 - Acc: 0.056, Time: 0:02:31.601180, Steps/s: 2.177
Finished training at 2023-11-15 10:49:59.

Investigarei a causa. Talvez aqui que de fato precise haver gradient clipping, dado que as features agora são normalizadas, isso é, assume valores em [-1, 1]?

vitalwarley commented 10 months ago

Validação (20231115152543)

Modelos 20231115091006 e 20231115095948.

 λ python val.py --logdir experiments/validation --baseline-weights models/ms1mv3_arcface_r100_fp16.pth --classification-weights experiments/20231115091006/model_epoch_20.pth --normalization-weights experiments/20231115095948/m
odel_epoch_20_norm.pth --dataset-path ../fitw2020/val-faces-det

roc

vitalwarley commented 10 months ago
Epoch 19 | Step  6040 - Loss: 6.325, Acc: 0.053, LR: 0.0000562500
Epoch 19 | Step  6140 - Loss: 6.307, Acc: 0.055, LR: 0.0000562500
Epoch 19 | Step  6240 - Loss: 6.326, Acc: 0.056, LR: 0.0000082883
Epoch 19 - Acc: 0.056, Time: 0:02:25.123843, Steps/s: 2.274
Epoch 20 | Step  6370 - Loss: 6.310, Acc: 0.059, LR: 0.0000000001
Epoch 20 | Step  6470 - Loss: 6.314, Acc: 0.055, LR: 0.0000000001
Epoch 20 | Step  6570 - Loss: 6.316, Acc: 0.056, LR: 0.0000000001
Epoch 20 - Acc: 0.056, Time: 0:02:26.108392, Steps/s: 2.259
Finished training at 2023-11-15 16:35:13.

Como que um treinamento em classificação de famílias que não converge é melhor do que um que converge?

╚═╡(kinship) [16:39] λ python val.py --logdir experiments/validation --baseline-weights models/ms1mv3_arcface_r100_fp16.pth --classification-weights experiments/train/20231115091006/model_epoch_20.pth --normalization-weights experiments/train/20231115154700/model_epoch_20_norm.pth --dataset-path ../fitw2020/val-faces-det
Validating with 10000 pairs.
Loaded insightface model.
100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 500/500 [00:58<00:00,  8.57it/s]
Baseline AUC: 0.7183
Loaded insightface model.
100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 500/500 [01:00<00:00,  8.29it/s]
Classification AUC: 0.6704
Loaded insightface model.
Feature normalization ON.
100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 500/500 [01:01<00:00,  8.11it/s]
Normalization AUC: 0.7196

AUC da estratégia com normalização não mudou, no entanto.

roc

vitalwarley commented 10 months ago

lr loss accuracy roc

Reproduzi a estratégia de classificação, embora tenha faltado alguns centésimos. A solução foi inicializar corretamente a camada de classificação. Isso tinha sido feito nos meus códigos antigos, algo que deixei passar despercebido. Próximo passo é entender a não-convergência da estratégia de normalização.

vitalwarley commented 10 months ago

Desativei gradient clipping para estratégia de normalização. Houve melhoria na AUC, mas ainda aquém do esperado (0.79).

loss accuracy roc

vitalwarley commented 10 months ago

Treinei ambas as estratégias com CLIP_GRADIENT: float = 1.5. É o que está no paper, embora não no código.

Treinamento

+ Classificação (exp. 20)

loss accuracy

+ Normalização (exp. 21)

loss accuracy

Validação (exp. 7)

roc

Podemos ver que estamos progredindo. Os modelos mal convergiram, mas os resultados são tão bom quanto aqueles que convergiram. O próximo passo é deixá-los treinando por mais épocas (50, vide paper, que não segui por no código estava definido como 20).

vitalwarley commented 10 months ago

Ainda não chegamos em uma reprodução equivalente. Mais uma vez, vou desativar gradient clipping e deixar apenas por mais épocas.

Treinamento

+ Classificação

plot

+ Normalização

plot

Validação

roc

vitalwarley commented 10 months ago

Sem gradient clipping os resultados foram piores.

Treinamento

+ Classificação

plot

+ Normalização

plot

Validação

roc

vitalwarley commented 10 months ago

Adapted ChatGPT summary from week report

Here's a revised table that includes separate columns for the AUC results of the classification and normalization strategies, as well as columns for loss and accuracy for each strategy:

Date Experiment Change(s) Implemented AUC (Classification) AUC (Normalization) Loss (Classification) Accuracy (Classification) Loss (Normalization) Accuracy (Normalization)
Nov 13, 2023 1 Adaptation to PyTorch, without hyperparameter consideration 0.6641 0.6697 Not specified Not specified Not specified Not specified
Nov 14, 2023 1 Added original hyperparameters from train.py Not evaluated Not evaluated High Low High Low
Nov 15, 2023 1 Disabled Gradient Clipping in normalization 0.6704 0.7196 < 1 (~0.25) 1 Not specified Not specified
2 Re-enabled Gradient Clipping for normalization Unchanged Unchanged Not specified Not specified Not specified Not specified
3 Initialized classification layer with normal distribution 0.7703 Not specified < 4 (+ Noisy) ~0.6 Not specified Not specified
Nov 16, 2023 1 Disabled Gradient Clipping 0.7703 0.7530 < 4 (+ Noisy) ~0.6 Not specified Not specified
2 Set Gradient Clipping to 1.5 (as per paper) 0.7304 0.7325 < 6 (++ Noisy) ~0.2 < 6 (++ Noisy) ~0.2
3 Increased epochs to 50, Gradient Clipping at 1.5 0.7596 0.7558 < 5.6 (++ Noisy) ~0.3 < 5.5 (++ Noisy) ~0.35
4 No Gradient Clipping, 50 epochs 0.7256 0.7058 ~1.5 ~0.98 ~0.2 1

This table now provides a more detailed breakdown of the AUC results, loss, and accuracy for both the classification and normalization strategies across your various experiments. It highlights the dynamic nature of model training and the impact of different changes on these metrics.

Analyzing the table of your experiments, several key insights emerge:

  1. Impact of Gradient Clipping:

    • The use of gradient clipping showed mixed results. Disabling it sometimes improved the AUC, but in other cases, it led to worse outcomes. This suggests that the optimal setting for gradient clipping might be context-dependent and needs careful tuning. In experiment 4, removing gradient clipping seemed to stabilize the training but at the cost of lower AUCs.
  2. Hyperparameter Sensitivity:

    • The results vary significantly with changes in hyperparameters, as seen in the experiments conducted on November 14 and 15. This underlines the sensitivity of deep learning models to hyperparameter settings, emphasizing the need for meticulous tuning.
  3. Training Stability vs. Performance:

    • There is a noticeable trade-off between training stability (as reflected in loss and accuracy variance) and performance (AUC). For instance, experiments on November 16 indicate more stable training with lower performance when gradient clipping was removed.
  4. Normalization Strategy Challenges:

    • The normalization strategy consistently underperformed compared to the classification strategy in terms of AUC. This suggests that the approach or its specific implementation might need revisiting or further optimization.
  5. High Accuracy with Overfitting Concerns:

    • In some experiments, particularly Experiment 4 on November 16, you achieved very high accuracy levels (near 1.0). While impressive, this could be indicative of overfitting, especially if such accuracy is not mirrored in validation or test datasets.
  6. Non-convergence and AUC Improvement Paradox:

    • In some cases, such as the November 15 Experiment 1, models that did not converge (in terms of loss and accuracy) still achieved a reasonable AUC. This suggests that the model might be learning useful discriminatory features that are not fully captured by the loss and accuracy metrics.
  7. Influence of Training Duration:

    • Extending the training duration (Experiment 3 on November 16) didn't significantly improve the AUC, suggesting that simply training longer is not the solution and that other factors (like model architecture, data quality, or learning rate scheduling) might be more critical.

These insights indicate that while there's progress, achieving the desired balance between stability, convergence, and high AUC requires further experimentation and perhaps more fundamental changes in approach, such as revisiting the model architecture or data preprocessing methods.


Importante lembrar que a taxa de aprendizado no passo N depende do total de épocas. Sendo assim, os experimentos 3 e 4 de ontem tem uma taxa de aprendizado diferente depois da época 20 -- o cooldown é feito nos 400 passos finais. São 330 passos por época com um _batchsize = 48 (o autor relata tamanho 64). Além disso, em épocas pré-definidas há uma escala da taxa por 0.75.

vitalwarley commented 10 months ago
Date Experiment Change(s) Implemented Epochs Batch Size AUC (Classification) AUC (Normalization) Loss (Classification) Accuracy (Classification) Loss (Normalization) Accuracy (Normalization)
Nov 13, 2023 1 Adaptation to PyTorch, without hyperparameter consideration 20 48 0.6641 0.6697 Not specified Not specified Not specified Not specified
Nov 14, 2023 1 Added original hyperparameters from train.py Not specified Not specified Not evaluated Not evaluated High Low High Low
Nov 15, 2023 1 Disabled Gradient Clipping in normalization Not specified Not specified 0.6704 0.7196 < 1 (~0.25) 1 Not specified Not specified
2 Re-enabled Gradient Clipping for normalization Not specified Not specified Unchanged Unchanged Not specified Not specified Not specified Not specified
3 Initialized classification layer with normal distribution Not specified Not specified 0.7703 Not specified < 4 (+ Noisy) ~0.6 Not specified Not specified
Nov 16, 2023 1 Disabled Gradient Clipping Not specified Not specified 0.7703 0.7530 < 4 (+ Noisy) ~0.6 Not specified Not specified
2 Set Gradient Clipping to 1.5 (as per paper) Not specified Not specified 0.7304 0.7325 < 6 (++ Noisy) ~0.2 < 6 (++ Noisy) ~0.2
3 Increased epochs to 50, Gradient Clipping at 1.5 50 48 0.7596 0.7558 < 5.6 (++ Noisy) ~0.3 < 5.5 (++ Noisy) ~0.35
4 No Gradient Clipping, 50 epochs 50 48 0.7256 0.7058 ~1.5 ~0.98 ~0.2 1
Nov 16, 2023 5 Multiplied image by 255 50 64 0.5181 0.5193 ~5.2 (+/- 0.5) ~0.25, with frequent fluctuations ~5 (+/- 0.5) ~0.35, with frequent fluctuations
Nov 17, 2023 1 batch_size = 64 and fp16=True in backbone; baseline model is fp16 50 64 0.7510 0.7481 ~5.3 (+/- 0.4) ~0.25, with frequent fluctuations ~5.2 (+/- 0.3) ~0.3, with frequent fluctuations

Vou usar guildai e reproduzir alguns experimentos para facilitar análise.

vitalwarley commented 9 months ago

Usei as ideias aqui, especialmente o último comentário. Em alguns experimentos, verifiquei que o máximo batch_size suportado é 46. Embora exista uma GPU com 12GB, não dá para condicionar experimentos com maiores tamanhos de batch nela.

Planejei 2 3 3 * 3 = 54 experimentos com o comando abaixo.

➜  hybrid git:(main) ✗ guild run train --stage-trials --quiet train-dataset-path=fitw2020/train-faces-det val-dataset-path=fitw2020/val-faces-det insightface-weights=models/ms1mv3_arcface_r100_fp16.pth output-dir=exp 'normalize=[yes,no]' 'clip-gradient=[0, 1, 1.5]' 'num-epoch=[20, 50, 100]' 'batch-size=[16, 32, 46]' -y

Incerto quando vai acabar, mas são 6 experimentos sendo executados simultaneamente, então talvez não demore tanto. Com um `batch-size=46', uma época (incluindo a validacão) levou 5 minutos. Logo, ChatGPT nos diz que...

Para calcular o tempo total necessário para completar os 54 experimentos, podemos usar as seguintes informações:

  1. Cada época (incluindo validação) leva 5 minutos com um batch-size de 46.
  2. O número total de épocas varia entre os experimentos (20, 50 ou 100 épocas).
  3. São 6 experimentos sendo executados simultaneamente.
  4. No total, planejamos realizar 54 experimentos.

Vamos assumir que o tempo para uma única época é mais ou menos constante, independente do número de épocas. Este é um ponto importante, pois se o tempo por época variar significativamente com diferentes batch-sizes, nossa estimativa pode ser imprecisa.

Cálculo do Tempo

  1. Tempo para uma única época: 5 minutos.
  2. Tempo para cada configuração de época:
    • Para 20 épocas: (20 \times 5) minutos.
    • Para 50 épocas: (50 \times 5) minutos.
    • Para 100 épocas: (100 \times 5) minutos.
  3. Número de experimentos por configuração de época: Como existem 3 configurações de num-epoch e um total de 54 experimentos, cada configuração será usada em (54 / 3 = 18) experimentos.
  4. Tempo total por configuração de época: Calcularemos para cada uma das três configurações.
  5. Tempo total: Somaremos o tempo de todas as configurações e dividiremos pelo número de experimentos que podem ser executados simultaneamente (6).

Vamos fazer esses cálculos.

Os cálculos indicam que serão necessárias aproximadamente 2550 minutos para completar todos os 54 experimentos, o que corresponde a cerca de 42.5 horas.

Portanto, você pode esperar que todos os experimentos sejam concluídos em aproximadamente 1.8 dias, assumindo que os experimentos são executados continuamente e que o tempo por época é constante para diferentes configurações de batch-size.

Essa é uma estimativa, e o tempo real pode variar com base em fatores como a eficiência da CPU/GPU, a complexidade dos dados e a carga de trabalho do sistema.


Atualmente o script salva sempre o melhor modelo de acordo com AUC. Abaixo alguns experimentos ainda em curso

2023-11-18-102245_2518x1342_scrot

2023-11-18-102814_2560x1440_scrot

vitalwarley commented 9 months ago

Há três experimentos rodando ainda. Boa parte daqueles com batch-size=46 deram OOM pouco depois de iniciado. Resultados parciais

image 2023-11-19-085646_2560x1440_scrot

epoch refere-se à época atual, pois o experimento ainda está em curso.

Interessante esses dois grupos de experimentos onde em uns o AUC sobe rapidamente, enquanto noutros não. Segue abaixo minha interpretação, que escrevi ontem

Sobre esses resultados intermediários. Vale a pena contrastar com os outros que convergiram apenas ao fim do experimento. O que eu penso: não há apenas features para reconhecimento facial e reconhecimento de parentesco, mas também para algo que intersecta ambos. Alguns experimentos atingiram maior AUC ao fim da terceira época, cuja acurácia não passava de 0.5. Ao longo do treinamento, tais experimentos sofreram uma queda na AUC, ao passo que a acurácia melhorava. Em outra palavras, o modelo aprendeu as features que discriminam parentesco, mas rapidamente deu lugar às que discriminam famílias. Outros experimentos conseguiram manter um trade-off ao longo do treinamento; as features que discriminam parentesco foram aprendidas aos poucos; inevitavelmente features que poderiam discriminar famílias não foram aprendidas, dado a baixa acurácia (< 0.5).

Conclusões parciais sobre os hiper-parâmetros (considerando que avaliei apenas alguns)

vitalwarley commented 9 months ago

image image

Conclusões atualizadas: não está mais tão claro que hparams são melhores, exceto que clip-gradient aparentemente não pode ser desativado. Quase todos experimentos (11/13) com clip-gradient=0 tiverem auc < 0.71. Foram esses experimentos com convergência rápida seguida de uma posterior decadência.

Ontem havia agendado mais experimentos para clip-gradient de 2 e 2.5 com batch-size de 16 e 32. Resultados já estão nos plots acima. Coloquei mais experimentos com batch-size de 8 e 44.

guild run train --stage-trials --quiet train-dataset-path=fitw2020/train-faces-det val-dataset-path=fitw2020/val-faces-det insightface-weights=models/ms1mv3_arcface_r100_fp16.pth output-dir=exp 'normalize=[yes,no]' 'clip-gradient=[1, 1.5, 2, 2.5]' 'num-epoch=[20, 50, 100]' 'batch-size=[8, 44]' -y

Questões

Que experimentos são esses que tem um rápido crescimento comparado aos demais?

image

Aparentemente são aqueles que têm clip-gradient=2.5, todavia não consegui avaliar os demais hparams conjuntos.

vitalwarley commented 9 months ago

Consegui obter os dados do tensorboard e avaliar precisamente aqueles experimentos com rápido crescimento e posterior decrescimento

See plot ![epoch_auc_vs_hparams_2023-11-21_15-27-03](https://github.com/vitalwarley/research/assets/6365065/52a425e4-17c4-4c5c-a179-85f5bac16639) `clip-gradient = 0` é realmente a causa.

Relativo aos experimentos cujo crescimento é rápido e estável ao longo do treinamento

See plot ![epoch_auc_vs_hparams_2023-11-21_15-43-16](https://github.com/vitalwarley/research/assets/6365065/77df9907-7d7e-4ce3-9298-026cc04e9ab6) `batch-size` e `clip-gradient` aparentam serem o diferencial...
vitalwarley commented 9 months ago

Interessante também notar quais hparams e valores nos dá melhor acurácia.

Ver plot ![epoch_acc_vs_hparams_2023-11-21_15-57-55](https://github.com/vitalwarley/research/assets/6365065/b1352193-239f-4ac3-8c08-7e8b5b70ec45)
vitalwarley commented 9 months ago

Ganho em AUC (discriminação entre parentes ou não) não necessariamente segue ganho em ACC (discriminação entre famílias), mas pode ser falta de exploração. É bom lembrar que até o momento eu variei apenas 4 hparams.

https://github.com/vitalwarley/research/assets/6365065/20e28838-0834-4e82-acdd-37a9d7b1ed1d

vitalwarley commented 9 months ago

Recapitulando os meus objetivos...

  1. Reproduzir estratégias do SOTA2020 com PyTorch
    • Baseline: modelo pré-treinado em reconhecimento facial no MS1MV3 (93k IDs, 5.2M images))
    • Classificação de famílias
    • Classificação de famílias com normalização das features
  2. Reproduzir SOTA2021 com PyTorch (meu código)
    • ResNet101 sem pré-treino em reconhecimento facial, mas treinada diretamente em verificação de parentesco com perda contrastiva
  3. Mesclar SOTA2020 e SOTA2021
    • Usar modelo resultante do treinamento de classificação de famílias em uma tarefa de verificação de parentesco

A razão para esse 3o objetivo (principal objetivo) é a seguinte. O SOTA2020 aprende a discriminar parentesco usando, em parte, atributos úteis à classificação de famílias. Em parte, pois, como podemos ver no gráfico 2, a acurácia de classificação de famílias (epoch_acc) não passa de 0.5 para os melhores modelos de verificação de parentesco (aqueles com maior epoch_auc) -- incrível ou estranhamente, epoch_acc assume maiores valores quando clip-gradient = 0 (ver comentário e gráfico 1). Nesses casos, podemos dizer que a rede preserva atributos úteis à classificação de famílias enquanto desenvolve atributos úteis à discriminação de parentesco para todas as famílias. Esses atributos tem origem "familiar", isso é, alguns atributos que ajudam a discriminar famílias, indiretamente ajudam a discriminar parentesco, porque inevitavelmente famílias diferentes (logo parentescos) estão distantes uma das outras.

Em outras palavras, podemos dizer que há uma combinação de features para reconhecimento facial (aprendido pelo modelo baseline) e reconhecimento de parentesco (aprendido estratégia do SOTA2020), que acaba culminando nas features para verificação de parentesco. Nesse sentido, o SOTA2021 vem à calhar: a perda contrastiva compara uma amostra com outras 2 * (batch_size^2 - batch_size) amostras negativas. O modelo é ensinado a aprender features mais "genéricas" no que diz respeito a discriminação de parentesco -- features que talvez não sejam "familiares".

A minha estratégia, portanto, visa responder: é possível combinar as features do modelo SOTA2020 (reconhecimento facial, reconhecimento de famílias, reconhecimento de parentesco indireto) com as features do modelo SOTA2021 (reconhecimento de parentesco direto)?

Para o primeiro objetivo, foram mais de 200 experimentos com diferentes variações em alguns hparams

Enquanto que não consegui reproduzir fielmente o melhor resultado do SOTA2020 (0.7912), ao menos cheguei perto em alguns casos.

Próximos passos

Gráficos

Gráfico 1: melhor experimento com clip-gradient=0

image

Comparado aos melhores experimentos abaixo, como explicar a diferença no epoch_acc e epoch_auc? Apresentei uma ideia aqui.

Gráfico 2: scatter plot dos experimentos realizados

image

Gráfico 3: melhor experimento com normalize=false

image

Gráfico 4: melhor experimento com normalize=true

image

vitalwarley commented 9 months ago
  • Complementar train.py para treinamento de verificação de parentesco como no SOTA2021

image image image

Resultados com diferentes modelos: R101 sem pré-treino, R101 pré-treinada no MS1MV3, R101 após pré-treino na MS1MV3 (reconhecimento facial) e FIW (classificação de famílias). Esse último modelo não foi o melhor que consegui, mas o melhor que eu tinha disponível no notebook -- os demais estão na RIG1. Mantive todos os outros hparams iguais (bs, lr, beta, passos por época), exceto número de épocas, que foram 20 em vez de 80, para ter um feedback mais rápido, todavia, implementação da validação completa (para encontrar o limiar) e do teste adicionaram 8min por época.

Os resultados anteriores, com script original, estão descritos aqui.

Resultados publicados

Resultados obtidos via reprodução com scripts originais

Resultados obtidos via reprodução com meus scripts

╚═╡(kinship) [11:26] λ guild compare -Fo train-kv --table -cc run,.time,=batch-size,=insightface-weights,=normalize,=num-epoch,acc,auc,best_threshold_auc,loss,threshold
run       time     batch-size  insightface-weights                                 normalize  num-epoch  acc       auc       best_threshold_auc  loss      threshold
ff026101  3:23:16  25                                                              no         20         0.604156  0.667352  0.660579            3.250999  0.510895
6d8c6c23  3:30:57  25          runs/99799c5e8e32419e91178b1dbe38e770/exp/best.pth  no         20         0.780162  0.836251  0.836983            0.633000  0.522662
a2cf02d7  3:25:36  25          models/ms1mv3_arcface_r100_fp16.pth                 no         20         0.794480  0.851867  0.856323            0.884999  0.517818

Podemos ver que a2cf02d7, o experimento que usou o modelo pré-treinado em reconhecimento facial (sem ajuste fino em classificação de famílias), teve o melhor resultado de acurácia média (0.794480) comparado ao resultado que obtive (0.7899) . Nos gráficos acima, no entanto, o melhor resultado desse experimento (0.7968) acontece antes (época 13).

vitalwarley commented 9 months ago

É importante notar que o conjunto de dados usado para AUC obtida pelo SOTA2020 difere daquele do SOTA2021.

Assim que a RIG1 ficar online, resgatarei o melhor modelo relativo ao SOTA2020 para um novo experimento. Vou desativar a computação das métricas best_threshold_auc e acc durante o treinamento por duas razões: 1) vazamento de dados/contaminação do modelo, dado que invariavelmente vou olhar para essas métricas; 2) custo de tempo.

vitalwarley commented 9 months ago

Resgatei o melhor modelo treinado no esquema do SOTA2020 (classificação de famílias) e o usei em alguns experimentos variando tamanho do batch e taxa de aprendizado.

guild run train-kv --stage-trials --background  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=runs/36be050267c2473b897b1aa8fb9d3b07/exp/best.pth normalize=yes 'lr=[1e-4, 1e-5, 1e-6]' 'batch-size=[12, 24, 36, 48, 60]' -y

O objetivo é AUC > 0.864550.

vitalwarley commented 9 months ago

Nenhum experimento conseguiu alcançar o objetivo.

image image

vitalwarley commented 9 months ago

https://github.com/vitalwarley/research/issues/38#issuecomment-1845427810 https://github.com/vitalwarley/research/issues/46#issuecomment-1845479077

Nada melhor.

image

vitalwarley commented 9 months ago

Só agora notei que usei um beta extremamente alto.

image

vitalwarley commented 9 months ago

Despachando mais alguns experimentos.

You are about to stage trials for train-kv as a batch (18 trials)
  batch-size: 10
  beta: [0.02, 0.05, 0.08]
  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)

Fico pensando na lr também. No gráfico do comentário temos um crescimeento rápido antes do degradamento do modelo.

vitalwarley commented 9 months ago

Despachando mais alguns experimentos.

You are about to stage trials for train-kv as a batch (18 trials)
  batch-size: 10
  beta: [0.02, 0.05, 0.08]
  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)

Fico pensando na lr também. No gráfico do comentário temos um crescimeento rápido antes do degradamento do modelo.

https://github.com/vitalwarley/research/issues/46#issuecomment-1848942560

vitalwarley commented 9 months ago

Despachando mais alguns experimentos.

You are about to stage trials for train-kv as a batch (18 trials)
  batch-size: 10
  beta: [0.02, 0.05, 0.08]
  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)

De fato, um beta menor, mas não muito, tende a dar melhores resultados em AUC. Todavia, resultados ainda são aquém do ideal (>0.86). O batch de 10 não melhora os resultados, ao contrário do que eu tinha suspeitado.

image image

Por outro lado, o treino é mais estável, isso é, AUC não decresce drasticamente ao longo do treino.