Closed vitalwarley closed 9 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.
insightface/requirements.txt
e insightface/recognition/arcface_paddle/requirement.txt
sklearn
do .txtscikit-learn
e opencv-python
mxnet
<VIRTUALENVS_DIR>/insightface/lib/python3.11/site-packages/mxnet/numpy/utils.py", na linha 37, onde tem
bool = onp.boolpara
bool = bool`. Ou remove a linha.python insightface/recognition/arcface_paddle/tools/mx_recordio_2_images.py --root_dir datasets/faces_emore --output_dir datasets/faces_emore
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.
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
--ckpt-path
, mas o correto era --weights
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
Próximo passo é revisar a #24 e continuar com roadmap dessa issue.
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.
╚═╡(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).
Não melhorou no treinamento.
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.
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.
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.
[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
Sec. III - B
- Given the information for each person about their family association, we can construct a family classification problem similar to the recent methods in face recognition [16], [2], [17] and metric learning for image retrieval [23], [24]
- He then uses the categorical cross-entropy loss.
Sec. IV - B
- Re-detected and aligned (as described in III-A) faces were given to the feature extractor model to obtain image embeddings. Performance of this approach (pretrained on fig. 1) was used as a baseline to test our hypotheses.
- With the ArcFace model pretrained on the MS1MV3.
- First, we tried to add a simple classification layer and finetune the whole model on the train set... After that, we added L2 normalization of the embeddings and retrained the model starting with pre-trained weights.
- Não há menção de treinamento de verificação de parentesco. O modelo gerado após o treinamento já foi utilizado diretamente no val e test set.
Sec. IV - C
- 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). Both on our validation data and test set the second approach was superior.
Considerando o que aprendi acima, próximos passos
Adaptar verification.py
para usar modelo que treinei.
Avaliar resultados.
Ajustar train_fiw_clf.py
para estar equivalente ao train.py
do Andrei Shadrikov.
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
Shadrikov (2020) used
retinaface_r50_v1
from insightface to re-detect and align faces. They're here.arcface_r100_v1
, also from insightface, which I am trying to train, and fed sample validation pairs from these re-detected and aligned images. The baseline below is the result of this approach.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
É, meio que esperado. Não reproduzi os treinamentos com os mesmos hparams e afins.
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.
╚═╡(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.
[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).
Desativei gradient clipping.
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]?
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
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.
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.
Desativei gradient clipping para estratégia de normalização. Houve melhoria na AUC, mas ainda aquém do esperado (0.79).
Treinei ambas as estratégias com CLIP_GRADIENT: float = 1.5
. É o que está no paper, embora não no código.
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).
Ainda não chegamos em uma reprodução equivalente. Mais uma vez, vou desativar gradient clipping e deixar apenas por mais épocas.
Sem gradient clipping os resultados foram piores.
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:
Impact of Gradient Clipping:
Hyperparameter Sensitivity:
Training Stability vs. Performance:
Normalization Strategy Challenges:
High Accuracy with Overfitting Concerns:
Non-convergence and AUC Improvement Paradox:
Influence of Training Duration:
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.
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.
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:
batch-size
de 46.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.
num-epoch
e um total de 54 experimentos, cada configuração será usada em (54 / 3 = 18) experimentos.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
Há três experimentos rodando ainda. Boa parte daqueles com batch-size=46
deram OOM pouco depois de iniciado. Resultados parciais
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)
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
Aparentemente são aqueles que têm clip-gradient=2.5
, todavia não consegui avaliar os demais hparams conjuntos.
Consegui obter os dados do tensorboard e avaliar precisamente aqueles experimentos com rápido crescimento e posterior decrescimento
Relativo aos experimentos cujo crescimento é rápido e estável ao longo do treinamento
Interessante também notar quais hparams e valores nos dá melhor acurácia.
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
Recapitulando os meus objetivos...
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
batch-size
clip-gradient
normalize
num-epoch
Enquanto que não consegui reproduzir fielmente o melhor resultado do SOTA2020 (0.7912), ao menos cheguei perto em alguns casos.
train.py
para treinamento de verificação de parentesco como no SOTA2021clip-gradient=0
Comparado aos melhores experimentos abaixo, como explicar a diferença no epoch_acc
e epoch_auc
? Apresentei uma ideia aqui.
normalize=false
normalize=true
- Complementar
train.py
para treinamento de verificação de parentesco como no SOTA2021
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.
╚═╡(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
auc
refere-se à AUC para seleção do modelo durante o treinamento
best_threshold_auc
refere-se à melhor AUC para seleção do threshold
-- é outro conjunto de validação mesmo, 20x maioracc
refere-se à acurácia do modelo no test set usando threshold
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).
É 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.
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.
Nenhum experimento conseguiu alcançar o objetivo.
Só agora notei que usei um beta
extremamente alto.
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.
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
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.
Por outro lado, o treino é mais estável, isso é, AUC não decresce drasticamente ao longo do treino.
Achieving Better Kinship Recognition Through Better Baseline
Tal pré-treino é a primeira estratégia abaixo.
Há mais detalhes sobre os hiperparâmetros e augmentações, todavia vou manter simples por agora.
Supervised Contrastive Learning for Facial Kinship Recognition
$$ 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) $$
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
FRClassification
que retorne (face, id)FRClassifier
que extenda a rede original para classificação de indivíduosFIW Family fine-tuning
KinFamilyClassification
que retorne (face, family_id)KinFamilyClassifier
que extenda a rede original para classificação de famílias