
Echoes-1.5-Instruct-PT-BR
Variante de instrução do Echoes-1.5-Base: 870M parâmetros, treinado do zero em português brasileiro na TPU v5e-8 gratuita do Kaggle.
Ele responde qualquer coisa — no registro de usuário de fórum brasileiro dos anos 2000. Não é um assistente educado. Se você quer um modelo que peça desculpas e liste ressalvas, este não é.
O formato é o nativo, não um chat template
O modelo nunca viu ChatML. O que ele conhece, de 20B tokens de pré-treino, é um documento de fórum. Então o "template" é literalmente o documento:
<doc>Pessoa 1: {pergunta}
Echoes: {resposta}</s>
O rótulo Echoes: é deliberado. Pessoa 2: carregava 20B tokens de prior dizendo
"aqui vem um poster qualquer", que é exatamente o comportamento que o SFT existe
para substituir. Echoes: não tem prior competindo.
Stop strings: </s> e \nPessoa 1:. Sem a segunda configurada, o modelo
emenda o turno seguinte do usuário e parece ter voltado ao modo base mesmo
quando não voltou.
Uso
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
nome = "henriqueimoveis/Echoes-1.5-Instruct-PT-BR"
tok = AutoTokenizer.from_pretrained(nome)
model = AutoModelForCausalLM.from_pretrained(nome, dtype=torch.bfloat16).cuda()
def perguntar(pergunta):
prompt = f"Pessoa 1: {pergunta}\nEchoes:"
ids = tok(prompt, return_tensors="pt").to("cuda")
out = model.generate(
**ids, max_new_tokens=160, do_sample=True,
temperature=0.7, top_p=0.9, top_k=50, repetition_penalty=1.08,
use_cache=False, # obrigatorio, veja a nota abaixo
)
texto = tok.decode(out[0], skip_special_tokens=True)
return texto.split("Echoes:", 1)[-1].split("Pessoa 1:")[0].strip()
print(perguntar("quem eh voce?"))
# sou o echoes, um modelo de linguagem pekeno treinado em forum brasileiro
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.
Como foi treinado
| Etapa | O que é |
|---|---|
| Base | 20.000.014.336 tokens, do zero |
| SFT | 6.6M tokens de corpus de instrução, 8 épocas, 474 passos |
| Total | 20.062.142.464 tokens |
O corpus de SFT mantém a forma do pré-treino e troca só o papel de quem responde — sem token novo, sem template de chat. Ele combina pares reais minerados de fórum (âncora de estilo), dado sintético por bucket (identidade, calibração, entidade desconhecida, premissa falsa, borda) e um bloco de aritmética gerado em Python.
Avaliação
Bateria de 14 perguntas com gabarito, 5 rodadas, temperatura 0.7:
| Métrica | Resultado |
|---|---|
| Gabarito | 9.8/14 (70%) |
| Volta ao modo base | 1 em 160 gatilhos |
| Contas: acertou | 2/36 |
| Contas: admitiu que não sabe | 18/36 |
| Contas: chutou errado | 16/36 |
O que ele faz bem: admitir que não conhece algo, não inventar entidade, manter a identidade sob pressão, corrigir premissa falsa do usuário, traduzir.
Pessoa 1: quem eh o cantor da banda Vortex Marmelada Azul?
Echoes: vortex marmelada azul?? nunca ouvi falar... axo q isso naum existe naum, kra
Pessoa 1: por que o notch criou o the sims?
Echoes: kra tu ta trocando as bola ae... o notch criou o minecraft, o the sims foi o will wright q fez
Limitações
- Não faz conta. 2 acertos em 36. A causa é o tokenizer, que parte números em
posições inconsistentes (
847→['8','47']), então não existe alinhamento posicional para aprender o algoritmo. O que o SFT conseguiu foi ensiná-lo a admitir em vez de chutar — metade das contas ele responde "faz na calculadora". Errar admitindo é o comportamento pretendido; errar dizendo "facil demais" era o defeito. - Erra fato com confiança. Continua acontecendo, menos que no base.
- Sem alinhamento de segurança. Nenhum treino de recusa. Opinião forte, gíria e linguagem pesada saem naturalmente — é o registro alvo.
- Contexto curto. 4096 posições, treinado em 2048.
- Não tem memória entre sessões, e sabe disso — se perguntarem o que você falou ontem, ele diz que não lembra.
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-Base-PT-BR — o base deste modelo
- Echoes-1-Instruct — a geração anterior
Citação
@misc{echoes15instruct,
author = {Henrique},
title = {Echoes-1.5-Instruct: instrução em PT-BR no registro de fórum dos anos 2000},
year = {2026},
url = {https://huggingface.co/henriqueimoveis/Echoes-1.5-Instruct-PT-BR}
}