作者以 ENEM 2024 基準測試 LLM,三個模型統計上不分勝負
Testei LLMs no ENEM 2024, e o ranking não diz o que parece
作者以 ENEM 2024 的 73 道文字題建立巴西葡萄牙語 LLM 基準,測試 Qwen3.8 27B、GPT-OSS 120B 和 GPT-OSS 20B。Qwen3.8 27B 以 97,0% 排第一,但三組逐題配對差異的 95% 信賴區間均包含 0,73 題不足以判定勝負。作者亦把 GPT-OSS 20B 的兩道空白回答改列模型失敗並計 0 分;本輪測試不涵蓋作文或語用能力。
Um modelo de 27 bilhões de parâmetros ficou à frente de um de 120 bilhões no meu benchmark de ENEM. A manchete seria fácil, mas ela estaria errada: os três modelos que testei empataram estatisticamente.
Construí um benchmark aberto para medir LLMs em português do Brasil. O resultado mais útil da primeira rodada não foi quem ganhou, mas o que a ordem do placar não permite concluir.
Por que medir em português do Brasil
Os placares populares são em inglês, e traduzir questões não mede o que importa para quem usa esses modelos aqui. Entender a norma culta, a cultura, as instituições e os implícitos da língua exige perguntas feitas no nosso contexto.
Comecei pelo ENEM porque é uma prova pública, conhecida e com gabarito oficial.
Como medi
A primeira rodada usou 73 questões de texto do ENEM 2024: 40 de conhecimento e 33 de interpretação. Questões que dependem de imagem ficaram de fora, porque um modelo só de texto não teria como resolvê-las.
- Condições iguais: mesmo prompt, temperatura 0 e o mesmo limite de 2048 tokens de saída para todos.
- Intervalo de confiança em toda nota: com dezenas de questões, alguns pontos de diferença podem ser só ruído.
- Sem chute na correção: se a resposta não indica uma alternativa válida, ela vale 0.
- Custo zero: tudo rodou em planos gratuitos, inclusive o limite de cerca de 1 requisição por minuto do Qwen.
Do PDF ao dataset
As questões saem direto dos PDFs oficiais do INEP, e essa foi a parte menos glamorosa do projeto. O texto extraído com pypdf vem cheio de ruído: marca d'água, código de barras da página, cabeçalhos e até o rodapé da gráfica colado no fim da última linha.
O extrator limpa esse ruído com expressões regulares e separa a prova pelos marcadores de questão. Um detalhe: alguns títulos usam espaços especiais do Unicode, como U+2003, e um espaço comum na regex não os encontra.
Depois vêm os filtros, que deixam cada questão fora em vez de arriscar um item quebrado:
- Questões 1 a 5 saem, porque são de inglês ou espanhol.
- Questões sem gabarito saem, porque foram anuladas.
- Questões sem as 5 alternativas lidas por inteiro saem.
- Questões que provavelmente dependem de imagem saem.
Das 85 questões em português do primeiro dia, 73 passaram. O filtro de imagem é uma heurística: procura palavras como "charge", "gráfico" e "mapa", e também desconfia de enunciados com menos de 80 caracteres. Ele pode errar nos dois sentidos, e isso é uma limitação conhecida.
As questões não ficam no repositório, porque os textos de apoio têm direitos de terceiros. O projeto distribui o extrator, e cada pessoa gera os itens a partir dos PDFs públicos.
Rodar de graça sem perder resultado
O avaliador é uma CLI em Python, com cerca de 2.700 linhas de código e 194 testes automatizados. As chamadas passam por um cliente único via LiteLLM, então adicionar um modelo é acrescentar uma entrada em um arquivo YAML.
No plano gratuito, o maior desafio é a cota. Três mecanismos tornam a execução segura de interromper e retomar:
- Cache em SQLite: a chave é um SHA-256 de modelo, item, tentativa, mensagens e parâmetros. Se qualquer um deles muda, a chave muda, e uma resposta antiga nunca é reaproveitada por engano.
- Limitador de taxa: respeita limites por provedor e por modelo. Na Groq, configurei 30 requisições por minuto e 1.000 por dia, e o Qwen ficou em 1 por minuto.
-
Novas tentativas só para erros transitórios: são até 3, com espera exponencial a partir de 2 segundos, respeitando o
retry-afterdo provedor, com teto de 60 segundos.
O cache guarda apenas respostas bem-sucedidas, então uma nova execução refaz só o que falhou. Isso tem um custo: como max_tokens faz parte da chave, mudar esse parâmetro refaz todas as respostas do modelo. Com o Qwen, 76 chamadas levam cerca de 76 minutos.
Como a resposta vira nota
O prompt deixa o modelo raciocinar, mas exige que ele termine com Resposta: X. O extrator pega a última ocorrência desse marcador, ignorando negrito, maiúsculas e acentos. Sem o marcador, só aceita a última linha se ela for exatamente uma alternativa.
Essa regra é propositalmente rígida. Ela favorece modelos que seguem formato, e essa escolha é consciente: seguir instrução faz parte de ser útil. O ponto mais delicado é separar a falha do modelo da falha da infraestrutura:
| Situação | Nota | Entra na média |
|---|---|---|
| Correta | 1 | Sim |
| Incorreta | 0 | Sim |
| Sem resposta: recusa, resposta vazia, alternativa inexistente ou ambígua | 0 | Sim |
| Erro de API que persistiu após as novas tentativas | 0 | Não |
Um timeout do provedor não diz nada sobre o modelo, então fica fora da média. Na rodada publicada, nenhum modelo teve erro de API.
Como calculei a incerteza
A nota geral é uma média macro: primeiro a média de cada categoria, depois a média das categorias. Assim, a categoria com mais itens não domina o placar. Por isso o Qwen aparece com 97,0%, embora tenha acertado 71 de 73 questões, ou 97,3%.
O intervalo de confiança vem de um bootstrap por percentil. São 2.000 reamostragens com reposição, feitas dentro de cada categoria e com semente fixa, então qualquer pessoa obtém o mesmo intervalo.
Comparar intervalos sobrepostos não basta para decidir um empate, porque todos os modelos responderam às mesmas questões. Por isso, calculo a diferença questão a questão e faço o bootstrap sobre essa diferença. Se o intervalo inclui 0, os modelos estão empatados.
O resultado
O Qwen3.8 27B ficou em primeiro com 97,0%, mas os intervalos dos três modelos se sobrepõem. Com 73 questões, a ordem não aponta um vencedor.
| Posição | Modelo | Geral | IC de 95% | Conhecimento | Interpretação |
|---|---|---|---|---|---|
| 1 | Qwen3.8 27B | 97,0% | 92,4 a 100 | 100% | 93,9% |
| 2 | GPT-OSS 120B | 93,9% | 87,9 a 98,5 | 100% | 87,9% |
| 3 | GPT-OSS 20B | 89,7% | 82,1 a 95,7 | 97,5% | 81,8% |
A nota geral é a média das duas categorias. Em conhecimento, dois modelos gabaritaram, então é a interpretação de texto que separa os resultados.
O que aparece quando se olha questão a questão
Nenhuma das três comparações pareadas exclui o zero. As diferenças abaixo usam as 73 questões, com o mesmo peso para cada uma:
| Comparação | Diferença (pontos percentuais) | IC de 95% |
|---|---|---|
| Qwen3.8 27B e GPT-OSS 20B | +6,8 | -1,4 a +15,1 |
| GPT-OSS 120B e GPT-OSS 20B | +4,1 | -1,4 a +11,0 |
| Qwen3.8 27B e GPT-OSS 120B | +2,7 | -4,1 a +8,2 |
Os erros contam uma história mais interessante que a média:
- A prova está perto do teto: os três acertaram 63 das 73 questões, ou 86%. Só 10 questões separam os modelos, e nenhuma derrubou os três.
- Os erros se concentram: 8 das 10 questões estão entre a 22 e a 31, e 9 das 10 são de interpretação.
- As famílias erram em lugares diferentes: o Qwen errou as questões 25 e 26, e nenhum GPT-OSS errou essas duas. Os dois GPT-OSS compartilham 3 erros: 22, 28 e 29.
- Não vi preferência por letra: a distribuição das alternativas escolhidas ficou a no máximo 3 questões do gabarito em cada letra.
Quantas questões seriam necessárias para separar o Qwen do GPT-OSS 120B? Eles discordaram em 6 das 73 questões, com saldo de 2 para o Qwen. Se essa proporção se mantivesse, seriam cerca de 420 questões para o intervalo de 95% excluir o zero. É uma conta aproximada, mas mostra a escala: o próximo ganho vem de mais itens e itens mais difíceis, não de mais modelos.
Tokens e latência
O modelo maior foi o mais econômico: o GPT-OSS 120B gastou metade dos tokens de saída do 20B, com nota numericamente maior.
| Modelo | Tokens de saída (total) | Mediana por resposta | Máximo | Latência mediana | Latência p95 |
|---|---|---|---|---|---|
| GPT-OSS 120B | 17.187 | 218 | 706 | 0,83 s | 1,55 s |
| Qwen3.8 27B | 34.255 | 448 | 934 | 1,25 s | 2,10 s |
| GPT-OSS 20B | 34.813 | 367 | 2.048 | 1,07 s | 2,84 s |
Dois cuidados na leitura. Tokens não são diretamente comparáveis entre famílias: o mesmo conjunto de prompts deu 29.385 tokens de entrada nos GPT-OSS e 26.301 no Qwen, porque os tokenizadores são diferentes. E a latência depende da fila do provedor no plano gratuito, por isso ela é registrada, mas não entra na nota.
O modelo que raciocinou até acabar os tokens
O achado mais curioso veio de um erro. Em 2 questões, o GPT-OSS 20B devolveu uma resposta vazia.
A primeira suspeita foi o limite de 2048 tokens, e o uso registrado confirmava: ele gastou tudo raciocinando. Testei de novo com 4096 e 6500 tokens, e ele continuou sem responder. Nas mesmas questões, o GPT-OSS 120B e o Qwen resolveram com menos de 800 tokens.
Essa investigação expôs uma falha no meu próprio benchmark. A regra tratava a resposta vazia como erro de infraestrutura, que fica fora da média. Na prática, o modelo deixava de responder e não perdia ponto. Mudei a regra: resposta vazia agora é falha do modelo e vale 0.
Também decidi não aumentar o limite só para esse modelo. A metodologia exige o mesmo max_tokens para todos, e o aumento não resolveu. Com a regra nova, as 2 questões contam como sem_resposta, e a nota do 20B reflete o custo real desse comportamento. Ainda não sei por que ele raciocina tanto nelas: isso segue como pergunta aberta.
O que este resultado não prova
- Que o Qwen é melhor: a diferença cabe dentro do intervalo de confiança.
- Que os modelos dominam o português: a rodada cobre só questões de texto do ENEM, sem redação ou pragmática.
- Que não houve contaminação: as questões são posteriores à data de corte dos GPT-OSS, mas falta um grupo anterior para comparar, e o Qwen não tem data de corte configurada.
- Que os modelos sabem o conteúdo, e não a posição: o benchmark gira as alternativas para testar isso, mas essa verificação não rodou nesta primeira rodada.
- Que o resultado se repete: cada questão foi respondida uma vez, e temperatura 0 não garante respostas idênticas entre execuções.
Os próximos passos são criar questões mais difíceis, incluir ironia e vida prática no Brasil e avaliar redações. Em um benchmark, saber o que o número não diz vale tanto quanto o número.
O código, a metodologia e o placar estão no repositório do projeto no GitHub.
來源:DEV Community · dev.to