Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ name: CAFUNE CI

on:
push:
branches: [main]
pull_request:
branches: [main]

Expand Down Expand Up @@ -42,3 +41,5 @@ jobs:
run: julia --project=julia -e 'using Pkg; Pkg.instantiate()'
- name: Run canonical smoke test
run: julia --project=julia julia/smoke_test.jl
- name: Run CAFUNE-mini training sanity check
run: julia --project=julia julia/main_training.jl --config config/experiments/cafune-mini.toml --sanity
Comment on lines +44 to +45

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep training out of the verification workflow

Every push and pull-request verification now invokes main_training.jl --sanity; that path calls train!, performs optimizer updates, evaluates the result, and writes checkpoints rather than merely checking syntax or a forward/backward primitive. This directly turns CI verification into a training run despite the repository's explicit verification contract.

AGENTS.md reference: AGENTS.md:L44-L44

Useful? React with 👍 / 👎.

44 changes: 44 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# CAFUNE — guia de colaboração

## Fluxo Git e releases

- `main` é a linha estável: mantenha-a verde no CI e não faça commits diretos.
- Todo trabalho começa em uma branch publicada e integrada por pull request.
- Use `feature/<hipotese>` para capacidades ou experimentos, por exemplo
`feature/cafune-mini`, `feature/mha-baseline` e `feature/tokenizer-ablation`.
- Use `fix/<problema>` para correções. Para uma correção urgente de uma release
publicada, use `hotfix/<problema>`.
- Releases são tags semânticas (`v0.1.0`, `v0.1.1`). Crie `release/vX.Y` apenas
quando for necessário manter correções nessa linha sem incluir trabalho novo.
- Não misture hipóteses experimentais diferentes na mesma branch ou PR.

## Contratos técnicos

- A implementação canônica é `julia/src/`; não reviva `julia/train_unified.jl`
nem lançadores legados como uma segunda fonte do modelo.
- `config/research.toml` define a configuração de pesquisa canônica.
- `config/experiments/cafune-mini.toml` é a configuração de ~7,07M parâmetros
para ablações. Preserve tokenizer, dados e splits ao comparar mini vs baseline.
- `python/dataset_splits.json` é o contrato de splits determinísticos. Se o
dataset tokenizado mudar, regenere os splits antes de treinar ou avaliar.
- O melhor checkpoint é escolhido por `aggregate.loss_mean` da avaliação de
validação; nunca por loss de treino.
- Antes de iniciar um treino comparável, registre hipótese e controles em
`docs/EXPERIMENTS.md`. Só registre conclusões após existir avaliação
reproduzível.
- Checkpoints, avaliações geradas, logs, caches e mmap são artefatos de runtime,
não código-fonte.

## Verificação

Antes de abrir PR, rode o que estiver disponível:

```powershell
python python/verify_research.py
python -m pytest python/tests -q
julia --project=julia julia/smoke_test.jl
```

Se Julia não estiver disponível localmente, não declare a alteração validada
somente por inspeção: publique a branch e aguarde o GitHub Actions concluir.
Não inicie treino, RLAIF ou chamadas externas como parte da verificação.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ julia --project=julia julia/evaluate.jl --checkpoint julia/checkpoints/cafune_be
O melhor checkpoint do treino é selecionado por essa `validation loss` agregada,
nunca pela loss de treino. Os relatórios ficam em `julia/evaluations/`.

Para experimentar hipóteses sem treinar o modelo de 45M, use o CAFUNÉ-mini
(~7,07M parâmetros). Ele preserva tokenizer, dados e splits da baseline e isola
seus artefatos em `julia/checkpoints/cafune-mini/`:

```powershell
julia --project=julia julia/main_training.jl --config config/experiments/cafune-mini.toml --sanity
```

Dashboard local:

```powershell
Expand Down
36 changes: 36 additions & 0 deletions config/experiments/cafune-mini.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Configuração para experimentos e ablações baratos.
# Mantém tokenizer, dados e sequência da baseline de 45M; reduz apenas capacidade.

[model]
name = "cafune-mini-7m"
vocab_size = 1999
seq_len = 128
d_model = 256
n_heads = 8
n_layers = 8
d_ff = 1024
dropout = 0.1
linear_mode = "float32"

[tokenizer]
type = "sentencepiece_bpe"
model = "python/cafune_spm.model"
vocab = "python/vocab_spm.json"
config = "python/spm_config.json"
dataset = "python/dataset_tokens.json"
pad_id = 0
unk_id = 1
bos_id = 2
eos_id = 3
mask_id = 4

[data]
splits = "python/dataset_splits.json"
train_ratio = 0.90
validation_ratio = 0.05
test_ratio = 0.05
split_seed = "cafune-v1"

[experiment]
purpose = "cheap_architecture_and_tokenizer_ablations"
parameters = 7071744
34 changes: 34 additions & 0 deletions docs/EXPERIMENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Registro de experimentos

Este arquivo preserva a trilha científica do CAFUNE. Registre uma entrada antes
de iniciar qualquer treino que produza resultado comparável.

Cada entrada deve declarar hipótese, configuração, dados/splits, sementes,
métrica de decisão, estado e links para artefatos. Não promova uma hipótese a
conclusão sem um relatório de avaliação reproduzível.

## EXP-001 — CAFUNE-mini como bancada de ablações

- **Hipótese:** uma configuração de aproximadamente 7M parâmetros permite
comparar escolhas arquiteturais antes de escalar para o modelo canônico de
45M.
- **Branch:** `feature/cafune-mini`.
- **Configuração:** `config/experiments/cafune-mini.toml` — 8 camadas,
`d_model=256`, 8 heads, `d_ff=1024`, 7.071.744 parâmetros.
- **Controles:** SentencePiece BPE 1.999, `seq_len=128`,
`python/dataset_tokens.json` e `python/dataset_splits.json` idênticos à
baseline canônica.
- **Métrica de decisão:** `aggregate.loss_mean` no split de validação, com
máscaras em 0,10 / 0,25 / 0,50 / 0,75 / 0,90 e três seeds.
- **Estado:** pronto para treino; sanity training e CI passaram. Ainda não há
métrica de qualidade ou conclusão empírica.
- **Próxima comparação:** MHA-only versus MHA+SSA, com esta configuração e os
mesmos controles.

## Convenções de artefatos

- Checkpoints: `julia/checkpoints/<experimento>/`.
- Avaliações: `julia/evaluations/`; preserve no JSON a configuração, checkpoint,
split, hash do dataset e seeds.
- Resultados agregados e interpretação entram neste arquivo após confirmação de
reprodutibilidade. Logs brutos não pertencem ao Git.
19 changes: 15 additions & 4 deletions julia/main_training.jl
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,27 @@ const SCRIPT_DIR = @__DIR__
const SANITY_MODE = "--sanity" in ARGS
const BITNET_MODE = "--bitnet" in ARGS
const RLAIF_MODE = "--rlaif" in ARGS
const RESEARCH_CONFIG = normpath(joinpath(SCRIPT_DIR, "..", "config", "research.toml"))
const DEFAULT_RESEARCH_CONFIG = normpath(joinpath(SCRIPT_DIR, "..", "config", "research.toml"))
const RESEARCH_CONFIG = let index = findfirst(==("--config"), ARGS)
if index === nothing
DEFAULT_RESEARCH_CONFIG
else
index < length(ARGS) || error("--config requer um caminho para um arquivo TOML.")
config_path = ARGS[index + 1]
normpath(isabspath(config_path) ? config_path : joinpath(pwd(), config_path))
Comment on lines +101 to +102

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Honor data paths from the selected experiment config

When a tokenizer or dataset ablation is passed through --config, only its [model] table affects training: SPM_CONFIG, SPM_TOKENS, and DATA_SPLITS remain hard-coded at lines 109–111. Such a run is therefore labeled and checkpointed as the custom experiment while silently training on the canonical tokenizer, dataset, and splits, invalidating the comparison; derive these paths from the selected TOML and validate the corresponding split manifest.

AGENTS.md reference: AGENTS.md:L20-L23

Useful? React with 👍 / 👎.

end
end
const EXPERIMENT_NAME = RESEARCH_CONFIG == DEFAULT_RESEARCH_CONFIG ? "baseline" : splitext(basename(RESEARCH_CONFIG))[1]
const MEM_FILE = normpath(joinpath(SCRIPT_DIR, "..", "cafune_brain.mem"))
const CORPUS_FILE = normpath(joinpath(SCRIPT_DIR, "..", "python", "social_data.json"))
const VOCAB_FILE = normpath(joinpath(SCRIPT_DIR, "..", "vocab.json"))
const SPM_CONFIG = normpath(joinpath(SCRIPT_DIR, "..", "python", "spm_config.json"))
const SPM_TOKENS = normpath(joinpath(SCRIPT_DIR, "..", "python", "dataset_tokens.json"))
const DATA_SPLITS = normpath(joinpath(SCRIPT_DIR, "..", "python", "dataset_splits.json"))
const VARIANT_DIR = BITNET_MODE ? "bitnet" : "baseline"
const CKPT_DIR = BITNET_MODE ? joinpath(SCRIPT_DIR, "checkpoints", SANITY_MODE ? "sanity" : "", "bitnet") :
(SANITY_MODE ? joinpath(SCRIPT_DIR, "checkpoints", "sanity") : joinpath(SCRIPT_DIR, "checkpoints"))
const VARIANT_DIR = BITNET_MODE ? "bitnet" : EXPERIMENT_NAME
const CKPT_DIR = BITNET_MODE ? joinpath(SCRIPT_DIR, "checkpoints", SANITY_MODE ? "sanity" : "", EXPERIMENT_NAME, "bitnet") :

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the existing BitNet checkpoint paths

With the default config and --bitnet, EXPERIMENT_NAME is baseline, so normal and sanity runs now write under checkpoints/baseline/bitnet and checkpoints/sanity/baseline/bitnet. Existing checkpoints under checkpoints/bitnet are no longer resumed, and julia/bitnet_inference_smoke.jl still looks for checkpoints/sanity/bitnet, causing the documented sanity-to-inference flow to fail.

Useful? React with 👍 / 👎.

SANITY_MODE ? (EXPERIMENT_NAME == "baseline" ? joinpath(SCRIPT_DIR, "checkpoints", "sanity") : joinpath(SCRIPT_DIR, "checkpoints", "sanity", EXPERIMENT_NAME)) :
EXPERIMENT_NAME == "baseline" ? joinpath(SCRIPT_DIR, "checkpoints") : joinpath(SCRIPT_DIR, "checkpoints", EXPERIMENT_NAME)
Comment on lines +114 to +115

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Isolate the training log for each experiment

Although custom checkpoints are routed into an experiment-specific directory here, every non-sanity run still appends to the shared julia/training_log.jsonl without recording an experiment identifier. Running CAFUNE-mini and the baseline therefore interleaves incompatible epoch and validation-loss series, which python/dashboard.py and python/wandb_logger.py consume as one continuous run; route the log alongside the experiment checkpoint or add an experiment discriminator.

Useful? React with 👍 / 👎.

const BEST_CKPT = joinpath(CKPT_DIR, "cafune_best.bson")
const TRAIN_LOG = SANITY_MODE ? joinpath(CKPT_DIR, "training_log.jsonl") : joinpath(SCRIPT_DIR, "training_log.jsonl")

Expand Down
14 changes: 13 additions & 1 deletion python/tests/test_research.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from pathlib import Path
import sys
import tomllib


sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

from verify_research import DEFAULT_CONFIG, audit, parameter_count
from verify_research import DEFAULT_CONFIG, ROOT, audit, parameter_count


def test_canonical_model_exceeds_45_1m_parameters():
Expand All @@ -19,3 +20,14 @@ def test_research_contract_has_no_failures():
"architecture",
"tokenizer",
}


def test_cafune_mini_configuration_is_a_7m_ablation_model():
config_path = ROOT / "config" / "experiments" / "cafune-mini.toml"
model = tomllib.loads(config_path.read_text(encoding="utf-8"))["model"]
assert parameter_count(
vocab_size=model["vocab_size"],
d_model=model["d_model"],
n_layers=model["n_layers"],
d_ff=model["d_ff"],
) == 7_071_744
Loading