
Echoes-1.5-Base-PT-BR
Echoes-1.5-Base é um modelo de linguagem de 870M parâmetros treinado do zero em português brasileiro, usando exclusivamente TPU v5e-8 gratuita do Kaggle.
Sucessor do Echoes-1-Base (536M / 17.2B tokens). Cresceu em parâmetros, em tokens e em contexto, e trocou a arquitetura por Qwen3Next denso.
Detalhes do modelo
| Especificação | Valor |
|---|---|
| Parâmetros | 870.2M (embeddings amarrados) |
| Arquitetura | Qwen3Next denso (GQA 12Q/2KV, head_dim 128, QK-norm) |
| Camadas / Hidden | 18 / 1536 |
| MLP intermediário | 8192 (SwiGLU) |
| Vocabulário | 32.000 (BPE próprio) |
| Contexto | 4096 tokens (RoPE θ=1M, rotary parcial 0.25; treinado em 2048) |
| Tokens de treino | 20.000.014.336 (20B) |
| Precisão | BF16 |
| Framework | JAX + optax + orbax |
| Hardware | TPU v5e-8 (Kaggle, sessões de 9h) |
Aproximadamente 23 tokens por parâmetro — acima do ponto Chinchilla, deliberadamente, porque o gargalo do projeto sempre foi compute e não dado.
Dados de treino
Corpus de português brasileiro com peso forte em fóruns dos anos 2000 (Adrenaline, HardMOB, Clube do Hardware, Orkut), mais texto geral em PT-BR. A fase final usou annealing com decaimento de LR.
O registro alvo é o de quem escrevia em fórum brasileiro naquela época — com a ortografia, as gírias e a informalidade que vinham junto. Isso é intencional, não um artefato de limpeza malfeita.
Tokens especiais
<pad> (0), <unk> (1), <s> (2), </s> (3), <doc> (4)
O <doc> separa documentos e habilita masking intra-documento no treino.
Uso
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
tok = AutoTokenizer.from_pretrained("henriqueimoveis/Echoes-1.5-Base-PT-BR")
model = AutoModelForCausalLM.from_pretrained(
"henriqueimoveis/Echoes-1.5-Base-PT-BR",
dtype=torch.bfloat16,
).cuda()
prompt = "A história dos fóruns brasileiros começou"
ids = tok(prompt, return_tensors="pt").to("cuda")
# use_cache=False e obrigatorio, veja a nota abaixo.
out = model.generate(**ids, max_new_tokens=200, use_cache=False)
print(tok.decode(out[0], skip_special_tokens=True))
Passe
use_cache=False. Sem isso ogenerate()levantaValueError: has_previous_state can only be called on LinearAttention layers. O modelo declaramodel_type: qwen3_next, e essa implementacao monta a mascara de atencao linear mesmo quando todas as 18 camadas saofull_attention— que e o caso aqui, porque este modelo e denso. O contexto e curto (4096), entao reprocessar a janela custa pouco.
Limitações
- Alucina com confiança. 870M parâmetros e 20B tokens produzem erro factual afirmado sem hesitação. Não use como fonte de fato.
- Não faz aritmética. Medido: 0 acertos em 36 contas. A causa é o tokenizer,
que parte números em posições inconsistentes (
847→['8','47'],396→['39','6']), então não há alinhamento posicional que permita aprender o algoritmo. Isso não se resolve com mais dados. - Monolíngue. Treinado só em português; qualquer outro idioma sai ruim.
- Modelo base. Serve para completar texto, não para conversar. Para instrução, use o Echoes-1.5-Instruct.
- Sem alinhamento de segurança. Não passou por nenhum treino de recusa. Ele reproduz o que havia nos fóruns, inclusive opinião forte e linguagem pesada.
Sobre GGUF / llama.cpp
Não há GGUF, e não é descuido. A conversão foi tentada e não é possível hoje sem mexer no llama.cpp. O motivo, para quem for tentar:
O config.json diz qwen3_next, mas o modelo é denso: 18 camadas, todas
full_attention, sem MoE, sem atenção linear e sem camada MTP. A lista de
tensores é idêntica à de um Qwen3 denso — com uma exceção decisiva:
model.layers.0.self_attn.q_proj.weight -> (3072, 1536) # e nao (1536, 1536)
O q_proj emite Q e um gate concatenados (head_dim * 2 por cabeça), e o
gate multiplica a saída da atenção:
query_states, gate = torch.chunk(
self.q_proj(hidden_states).view(*input_shape, -1, self.head_dim * 2), 2, dim=-1
)
Isso quebra os dois caminhos de conversão:
- como
qwen3denso:check_tensor_dims: tensor 'blk.0.attn_q.weight' has wrong shape; expected 1536,1536, got 1536,3072 - como
qwen3_next: o conversor exige camada MTP (assert self.opt_num_mtp_layers != 0) e espera atenção linear + MoE, que não existem aqui
Há ainda um segundo detalhe: partial_rotary_factor = 0.25, ou seja, RoPE em
apenas 32 das 128 dimensões por cabeça. O conversor denso não escreve
rope.dimension_count, então mesmo resolvendo o gate o GGUF sairia com RoPE
completo e geraria lixo.
Converter de verdade exigiria uma arquitetura nova no llama.cpp — Qwen3 denso + gated attention + RoPE parcial. Se alguém fizer, abra uma issue que eu subo os arquivos.
Enquanto isso, use transformers (com use_cache=False, veja acima).
Modelos relacionados
- Echoes-1.5-Instruct-PT-BR — variante de instrução, no mesmo registro
- Echoes-1-Base-PT-BR — a geração anterior (536M / 17.2B tokens)
Citação
@misc{echoes15,
author = {Henrique},
title = {Echoes-1.5: um modelo de linguagem PT-BR treinado do zero em TPU gratuita},
year = {2026},
url = {https://huggingface.co/henriqueimoveis/Echoes-1.5-Base-PT-BR}
}