Fundamentos

Python, NumPy e PyTorch em 20 minutos

Arrays, tensores, shapes e autograd na prática, terminando nas quatro linhas que leem a própria configuração do Qwen3.8-27B e produzem os números que este curso reutiliza em todo lugar.

Atualizada em

01 · Conceito

Conceito

Quatro lições desta track se apoiaram em três números: hidden size 5120, 64 camadas e largura de saída 248.320. Você não deveria aceitá-los na confiança vindos de um curso, e certamente não deveria aceitá-los de um post de blog. Todo modelo publicado vem com um arquivo de configuração que os declara, e lê-lo você mesmo leva uns dez segundos. É aí que esta lição termina. Ela começa com o mínimo de ferramental Python necessário para tornar compreensível essa leitura, e tudo o que vem depois dela.

Python é a camada de coordenação do trabalho de machine learning. Você escreve variáveis, funções, loops e imports comuns, enquanto bibliotecas numéricas executam a aritmética pesada em kernels compilados. O hábito que mais importa é tornar o fluxo de dados explícito: nomeie seus shapes, evite estado global escondido, confira suposições nas fronteiras, e mantenha um experimento reprodutível enquanto ele ainda é pequeno o bastante para você raciocinar sobre ele.

O objeto central do NumPy é o array multidimensional. Duas operações que parecem semelhantes são inteiramente diferentes: x * w multiplica elemento a elemento, enquanto x @ w executa o produto matricial da lição 0.5. Confundi-las produz um resultado com shape errado se você tiver sorte, e um resultado plausível com shape certo se não tiver. Inspecione .shape e .dtype constantemente. Broadcasting permite que um array menor atue sobre um maior sob regras precisas de compatibilidade, então somar um vetor de shape features a um array de shape batch por features soma o mesmo bias a toda linha. Broadcasting é conveniente e também é uma excelente forma de silenciosamente tirar média sobre um eixo que você pretendia manter.

Tensores do PyTorch oferecem as mesmas operações de array mais duas adições: devices e diferenciação automática. Tensores em uma mesma operação em geral precisam de dtypes compatíveis e do mesmo device, e mover um modelo para uma GPU não arrasta junto todo objeto relacionado. O autograd registra operações sobre tensores que exigem gradientes, de modo que, quando você chama backward numa loss escalar, ele consegue aplicar a regra da cadeia da lição 0.6 de trás para frente pelo grafo registrado. O loop tem um ritmo fixo: limpe os gradientes acumulados, rode o forward pass, reduza a uma loss escalar, chame backward, e então chame o step do otimizador. Manter essas responsabilidades separadas é o que torna depurável um loop quebrado — backward computa gradientes e nunca muda parâmetros, step muda parâmetros e nunca computa gradientes.

Modelos maiores herdam de torch.nn.Module ou compõem módulos existentes; parâmetros registrados então aparecem em model.parameters(). Note uma armadilha: model.train() e model.eval() alternam o comportamento de módulos como o dropout, mas não ligam nem desligam o rastreamento de gradientes. Avaliação sem derivadas precisa de torch.no_grad. Salve um state_dict mais configuração suficiente para reconstruir o modelo, em vez de serializar um grafo de objetos opaco que uma atualização de biblioteca vai se recusar a carregar.

O que nos leva à configuração, e ao ponto central desta lição. Um modelo publicado é um par: pesos, e uma descrição do shape que esses pesos deveriam ter. A biblioteca transformers da Hugging Face lê o segundo sem baixar o primeiro, e é por isso que isso é barato o bastante para virar reflexo:

from transformers import AutoConfig

config = AutoConfig.from_pretrained("Qwen/Qwen3.8-27B")
text = getattr(config, "text_config", config)

print(text.hidden_size)        # 5120
print(text.num_hidden_layers)  # 64
print(text.vocab_size)         # 248320

Três linhas de saída e você tem a espinha dorsal deste curso na mão. O 5120 é a largura do estado de um token, da lição 0.4. O 64 é quantas vezes esse estado é reescrito. O 248.320 é quantos scores a camada final produz por posição, da lição 0.1. O getattr na terceira linha não é enfeite: o Qwen3.8-27B é multimodal, então sua configuração carrega uma configuração de visão aninhada ao lado da de texto, e modelos multimodais diferentes colocam os campos de texto em profundidades diferentes. Escrever o fallback explicitamente faz o código declarar qual subconfiguração ele leu, em vez de pegar o hidden size de uma torre de visão e reportá-lo como se fosse o do modelo.

Reprodutibilidade exige mais do que uma semente aleatória. Registre versões de bibliotecas, divisões de dados, pré-processamento, hiperparâmetros, device, precisão e procedimento de avaliação, porque qualquer um deles pode mover um resultado mais do que a mudança que você está testando. Alguns kernels de acelerador continuam não determinísticos por design. Mantenha sempre uma baseline simples na comparação, inspecione exemplos individuais em vez de apenas agregados, e assegure-se de que os shapes são os esperados e que as losses são finitas. Um loop de treinamento que roda verde até o fim pode otimizar o objetivo completamente errado com fidelidade perfeita.

A habilidade portátil aqui não é decorar uma superfície de API que muda todo ano. É ler um programa de tensores como uma sequência de transformações de shape com estado explícito: o que cada eixo significa, quais valores exigem gradientes, onde entra a aleatoriedade, qual redução produz a loss, e quando os parâmetros mudam. Some a isso o reflexo de carregar a configuração real de um modelo em vez de citar um número de memória, e o resto do curso é uma questão de seguir shapes.

02 · Analogia

Analogia

Pense em Python como a oficina, NumPy como uma bancada de ferramentas precisas de medir e cortar, e PyTorch como a mesma oficina com uma câmera gravando cada operação diferenciável que você executa. Quando você finalmente anuncia uma loss escalar, o autograd rebobina a fita e informa quanto cada botão ajustável contribuiu. A câmera é fiel e completamente acrítica: ela vai diferenciar uma medição lindamente executada da tábua errada com a mesma alegria com que diferenciaria a da tábua certa.

03 · Explique de volta

Explique de volta

Descreva um passo mínimo de treinamento em PyTorch e o papel dos shapes, do autograd, de zero_grad, backward e step, e depois explique como você obteria as constantes arquiteturais reais de um modelo em vez de confiar num post de blog.

Mínimo: 80 caracteres e 15 palavras. Seu texto fica somente neste navegador.

Aguardando sua explicação.

Comparar com uma resposta-modelo

Entradas e alvos são tensores com shapes deliberados. O forward pass computa previsões e as reduz a uma loss escalar enquanto o autograd registra cada operação diferenciável. optimizer.zero_grad limpa os gradientes deixados pelo passo anterior, porque o PyTorch acumula em vez de sobrescrever. loss.backward percorre o grafo registrado e preenche o campo grad de cada parâmetro. optimizer.step consome esses gradientes para atualizar os parâmetros; ele não os computa. Para constantes arquiteturais, carregue a própria configuração do modelo com AutoConfig.from_pretrained e leia hidden_size, num_hidden_layers e vocab_size diretamente. No Qwen3.8-27B esses valores são 5120, 64 e 248320, e todo cálculo de capacidade e custo neste curso parte de números obtidos assim, e não da memória.

04 · Teste seu entendimento

Teste seu entendimento

01Ler a configuração do Qwen3.8-27B dá vocab_size 248320. O que esse número descreve exatamente?
Resposta e explicação

O número de linhas da matriz de embedding, que pode estar preenchida acima do vocabulário efetivo do tokenizador — vocab_size dimensiona as matrizes de embedding e de saída. Vocabulários costumam ser preenchidos até um múltiplo conveniente, então a contagem de tokens que o tokenizador consegue de fato emitir pode ser menor; a lição 1.2 trabalha a consequência.

02Por que optimizer.zero_grad normalmente é chamado antes de backward?
Resposta e explicação

O PyTorch acumula gradientes nos campos grad em vez de substituí-los — A acumulação é deliberada, já que permite técnicas como acumulação de gradiente entre micro-batches, mas um passo independente comum precisa limpar antes os valores do passo anterior.

03A lição 0.6 mostrou que um learning rate de 1,1 sobre L(w) = w ao quadrado faz o parâmetro divergir. Se você visse esse comportamento num loop de treinamento real, o que o autograd teria lhe dito?
Resposta e explicação

Nada; o autograd computa gradientes corretos para uma execução divergente exatamente como faz para uma convergente — Diferenciação automática reporta sensibilidade, não julgamento. Um loop que computa gradientes perfeitos e dá passos ruinosos é Python plenamente válido e autograd plenamente válido.

Conclua o teach-back e acerte o quiz para finalizar a aula.

◎ · Marcador de evidência

Fontes

  1. Charles R. Harris et al. (2020). Array programming with NumPy.
  2. Adam Paszke et al. (2019). PyTorch: An Imperative Style, High-Performance Deep Learning Library.
  3. Hugging Face (2026). Hugging Face Transformers documentation.
  4. Qwen Team (2026). Qwen3.8-27B Model Card.