Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reorganizar FaCoRNet para permitir experimentos com guildai #71

Closed
vitalwarley opened this issue Mar 19, 2024 · 20 comments
Closed

Reorganizar FaCoRNet para permitir experimentos com guildai #71

vitalwarley opened this issue Mar 19, 2024 · 20 comments
Assignees

Comments

@vitalwarley
Copy link
Owner

#67 (comment)

@vitalwarley vitalwarley self-assigned this Mar 19, 2024
@vitalwarley
Copy link
Owner Author

vitalwarley commented Mar 19, 2024

Consegui reorganizar o treinamento. Creio ter conseguido replicá-lo, todavia usei um seed diferente daquele que usei na RIG2.

  • AUC com código original (seed=100, máquina=RIG2): 0.875248
  • AUC com código reorganizado (seed=42, máquina=arch-rog-strix): 0.8743

image

Vou adicionar as etapas de validação (find.py) e teste (test.py). Após isso, retreinarei na RIG2 com seed=100.

@vitalwarley
Copy link
Owner Author

threshold selection

auc: 0.878271 | acc: 0.805129 | threshold: 0.5250518321990967

enquanto que originalmente foi 0.8769 e 0.11221975088119507, respectivamente. Não computei a acurácia da 1a vez, pois usei o código original. Interessante essa diferença no limiar...

test

Exp AUC acc (avg) bb ss sibs fd md fs ms gfgd gmgd gfgs gmgs
arch-rog-strix 0.8966 0.8171 0.8829 0.8752 0.8387 0.7648 0.8226 0.8610 0.7820 0.6777 0.4085 0.4896 0.3095
rig2 0.8979 0.8187 0.8248 0.8448 0.8166 0.7936 0.8153 0.8485 0.7990 0.7878 0.7323 0.6816 0.5642
Diff -0.0013 -0.0016 +0.0581 +0.0304 +0.0220 -0.0288 +0.0073 +0.0124 -0.0170 -0.1101 -0.3239 -0.1920 -0.2547

Em ambos os casos, acc (avg) não é a média das acurácias individuais, mas a acurácia sobre todas as predições de acordo como o limar utilizado.

Observações

  • Usei seeds diferentes em cada experimento de treinamento, como citei no comentário anterior.
  • O modelo base é o AdaFace, que afirma usar BGR em vez de RGB. Apesar disso, não encontrei no código da FaCoRNet nada relativo à conversão no pipeline de treinamento -- pipeline que executei. Eles usaram image.load_img do Keras, que tem default como RGB. Definiram inference.py:to_input, que converte para BGR, mas não usaram no treinamento. Como reusei um código que já tinha, meus experimentos usaram RGB, como feito pelos autores do FaCoRNet.
  • Há grandes diferenças para os tipos de parentesco com menos amostras: gfgd, gmgd, gfgs, gmgs.

vitalwarley added a commit that referenced this issue Mar 19, 2024
- Refactor predict to fix memory leak caused by appending tensors to a
  python list.
vitalwarley added a commit that referenced this issue Mar 19, 2024
@vitalwarley
Copy link
Owner Author

vitalwarley commented Mar 20, 2024

Novo experimento na RIG2

Train / Val

➜  ours git:(main) ✗ guild run facornet:train seed=100 output-dir=exp root-dir=data batch-size=50 num-epoch=50 -y

Adicionei escalares (métricas) de acurácia para todos tipos de parentesco

image
image

Aqui temos AUC = 0.8747 e ACC = 0.8029.

Quanto à validação para obtenção do limiar e teste posterior.

AUC acc threshold acc_md acc_ms acc_sibs acc_ss acc_bb acc_fd acc_fs acc_gfgd acc_gfgs acc_gmgd acc_gmgs
Val 0.8748 0.8013 0.5248 0.8172 0.7631 0.8587 0.9067 0.9295 0.7732 0.8087 0.3465 0.2412 0.3627 0.3495
Test 0.8953 0.8171 0.5248 0.7999 0.7519 0.8190 0.8471 0.8615 0.7489 0.8420 0.6694 0.4792 0.4225 0.2262

@vitalwarley
Copy link
Owner Author

No paper, temos

In this work, we mainly focus on the first 7 kinship relationships since the Grandparent-grandchild categories contain much smaller data by an order of magnitude.

Todavia, é engraçado citarem isso, pois não há "foco" nenhum. Em todos os quatro conjuntos não houve remoção de nenhum par relativo aos demais tipos de parentesco supostamente excluídos. Tal afirmação apenas confunde a análise e reprodução. Analisando o código, notamos que reportaram a acurácia sobre todas as predições.

# test.py
                for i in range(len(pred)):
                    # pdb.set_trace()
                    if pred[i] >= threshold:
                        p = 1
                    else:
                        p = 0
                    y_pred2.extend([p])
                    if p == labels[i]:
                        res['avg'][1] += 1
                        res[classes[i]][1] += 1
            else:
                break
        fpr, tpr, thresholds_keras = roc_curve(np.array(y_true), np.array(y_pred))
    sio.savemat(args.log_path[:-4]+'_auc_'+'.mat', {'pred': y_pred2, 'true':y_true, 'AUC':auc(fpr,tpr)})

    for key in res:
        mylog(key, ':', res[key][1] / res[key][0], path=log_path)

onde res é um dicionário cujas chaves são avg e os tipos de parentesco, e o valor de cada é uma lista com dois elementos (total_pred, correct_pred).

O problema é que o report mostra 7 tipos apenas e uma média

image

Se computarmos a média dos 7 tipos como reportados, chegamos em 0.8221, o que difere um pouco de AVG = 0.820.

Enfim, nada grostesco, mas ainda assim uma péssima prática. O que importa é que nossa reprodução foi relativamente efetiva.

@vitalwarley
Copy link
Owner Author

Não há melhora se usamos os canais BGR em vez de RGB.

image

Penso que um informativo da acurácia de treinamento fosse interessante também, tanto geral, quanto por tipo de parentesco.

@vitalwarley
Copy link
Owner Author

Estou migrando para o Lightning. Mais flexível, mas estou apanhando no parser. Em breve retomo com https://github.com/AlessandroW/Guild.ai-x-PyTorch-Lightning. Enquanto isso, deixei as mods em uma nova branch.

@vitalwarley
Copy link
Owner Author

vitalwarley commented Mar 20, 2024

Adicionei um classificador de parentesco à FaCoRNet. Experimento em curso. Durante a implementação, notei que errei a implementação do mesmo classificador no Lightning-AI/pytorch-lightning#52. No KFC, errei a ordem dos parâmetros. Implementei HeadKin(512, 4, 5), mas deveria ser HeadKin(512, 5, 8), pois a ordem é (in_features, out_features, ratio), onde out_features=5 representa as cinco classes (non-kin e as demais principais) e ratio=8 era o default lá implementado para HeadRace.

C'est la vie...

@vitalwarley
Copy link
Owner Author

Acurácia de verificação de parentesco com dois experimentos: a diferença é que o experimento em vermelho usou canais BGR. O experimento azul é o mesmo que relatei acima.

image

O novo experimento (multi-task com classificação de parentesco; 11 tipos considerados) apresentou a acurácia de verificação abaixo

image

enquanto que a acurácia de classificação é

image

Penso que essa baixa acurácia reflete a dificuldade em classificar os tipos grandparent-grandchild.

image

Em termos de AUC, não houve ganhos no meu último experimento (+ AUC). O melhor foi o baseline mesmo.

Vejamos as acurácias por tipo de parentesco

fd, fs, md, ms

image

bb, ss, sibs

image

Vale a pena investigar mais a fundo esses resultados...

gfgd, gfgs, gmgd, gmgs

image

Observações

  • No experimento em laranja (+ HeadKin), eu defini um seed=42, que difere dos demais. É importante manter igual. Vou refazê-lo.

@vitalwarley
Copy link
Owner Author

No experimento em laranja (+ HeadKin), eu defini um seed=42, que difere dos demais. É importante manter igual. Vou refazê-lo.

Em vermelho o novo experimento. Em cinza, o que foi laranja acima.

Acurácias, AUC, Perdas

image

fd, fs, md, ms

image

bb, ss, sibs

image

Como citei antes, são resultados curiosos...

gfgd, gfgs, gmgd, gmgs

image

@vitalwarley
Copy link
Owner Author

#38 (comment)

@vitalwarley
Copy link
Owner Author

Usando as configs atuais, podemos realizar as três operações assim

guild run facornet:train trainer.max_epochs=107
guild run facornet:val ckpt_path=exp/checkpoints/best.ckpt data.init_args.batch_size=60
guild run facornet:test ckpt_path=exp/checkpoints/best.ckpt data.init_args.batch_size=100 model.init_args.threshold=0.8631433248519897

A op de validação automaticamente carrega o melhor checkpoint do último treino. Similarmente, a op de teste faz o mesmo; nela, ainda precisamos passar o melhor limiar manualmente -- quando oportuno, vejo como automatizar.

No entanto, não me sinto confiante nessa implementação, pois #67 (comment) deu um threshold bem pequeno comparado ao que consegui.

@vitalwarley
Copy link
Owner Author

vitalwarley commented Apr 4, 2024

O problema precede a implementação com Lightning. No commit 4a1a5c2 o limiar já é alto: >0.5 (validação de seleção de modelo). Por que na implementação original temos ~0.18 (validação de seleção de limiar)?

@vitalwarley
Copy link
Owner Author

Executei o código original novamente.

*************
epoch 1

auc is 0.779991 (threshold=0.142777)
auc improve from :0.000000 to 0.779991
save model ada3_cont_FaCoRNet_Adaface_lr_1e-4_beta.pth

*************
epoch 2

auc is 0.801358 (threshold=0.173614)
auc improve from :0.779991 to 0.801358
save model ada3_cont_FaCoRNet_Adaface_lr_1e-4_beta.pth

O limiar já começa baixo (0.14, 0.17, ...). Por quê?

@vitalwarley
Copy link
Owner Author

Essa problemática implica que a descoberta em #38 (comment) foi mais sobre problemas na reprodução do que uma descoberta importante sobre a tarefa.

@vitalwarley
Copy link
Owner Author

Curva ROC e histograma das similaridades após a primeira época com código original.

Figure_1

Próximo dos histogramas que gerei com meu código em Lightning (#38 (comment))?

@vitalwarley
Copy link
Owner Author

vitalwarley commented Apr 4, 2024

Curva ROC e histograma das similaridades após a quinta época com meu código.

image

@vitalwarley
Copy link
Owner Author

Não há diferença aparente na forma de obtenção do limiar

# original
fpr, tpr, thresholds = roc_curve(y_true, y_pred)  # sklearn
maxindex = (tpr - fpr).tolist().index(max(tpr - fpr))
threshold = thresholds[maxindex]

vs

# meu
fpr, tpr, thresholds = tm.functional.roc(similarities, is_kin_labels, task="binary")
best_threshold = compute_best_threshold(tpr, fpr, thresholds)
...
def compute_best_threshold(tpr, fpr, thresholds):
    # Get the best threshold
    maxindex = (tpr - fpr).argmax()
    threshold = thresholds[maxindex]
    if threshold.isnan().item():
        threshold = 0.01
    else:
        threshold = threshold.item()
    return threshold

Até tentei

            fpr, tpr, thresholds = tm.functional.roc(similarities, is_kin_labels, task="binary", thresholds=torch.linspace(0, 1, 100, device=similarities.device))

mas nada mudou.

@vitalwarley
Copy link
Owner Author

vitalwarley commented Apr 4, 2024

Usando o roc_curve por enquanto. Relatei o possível bug para os responsáveis: Lightning-AI/torchmetrics#2489

@vitalwarley
Copy link
Owner Author

Reverti porque entendi o erro inicial: thresholds são probabilidades.

@vitalwarley
Copy link
Owner Author

Agora posso realizar os experimentos planejados.

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant