Índice do curso
Tensores do zero (3): os eixos têm nome
Por Dorian Chávez · fundador e CEO da Hábil ·
Atualizada em 21 de outubro de 2026
Como extrair um pedaço de um tensor por qualquer um dos eixos (um número, um dia, um produto, o canal vermelho de uma foto) e o que acontece com a forma quando isso é feito.
Tensores do zero · lição A, sessão 3 de 8
Cenário: os personagens e a história são inventados. Os dados técnicos, históricos e os números reais trazem a fonte.
Segunda-feira, 16 de novembro, 19h40. Papelaria Centro.
A porta de aço está abaixada até a metade. Lá dentro, cheiro de papel novo e de café requentado. Dona Lupe conta notas no balcão. Mateo está com o notebook aberto ao lado da caixa registradora, entre um pote de lápis e uma pilha de cartolinas.
Faltam 37 dias para o 23 de dezembro.
Na semana passada consertaram o arquivo do Norte, o da linha cortada. Agora o PyTorch já o aceita: um tensor de forma (2, 7, 3). Duas filiais, sete dias, três produtos. Quarenta e dois números.
— E aí? — pergunta dona Lupe sem levantar os olhos. — Já sabe quem está me roubando?
— Ainda não, tia. Tenho os números. Falta saber olhar para eles.
— Pois olhe. Os olhos servem para isso.
Mateo ri, mas ela tem razão. Quarenta e dois números juntos não dizem nada. São como um depósito com tudo amontoado: a resposta está ali, mas não se encontra. O que ele precisa é extrair pedaços. Um dia. Um produto. Uma filial. Colocá-los lado a lado e ver o que não bate.
Escreve uma linha. Aparece a sexta-feira das duas filiais.
Fica imóvel.
— Tia. Qual é o melhor dia da semana da senhora?
— A sexta, qual outro? A criançada sai da escola, vem com os pais, leva de tudo.
— No Centro, sim. Na sexta vende 2,000 de cadernos. Nenhum outro dia chega perto.
— E no Norte?
Mateo gira a tela. 500, 300, 200.
Dona Lupe põe os óculos. Tira de novo.
— Isso o Norte vende numa terça qualquer.
Quase. A terça do Norte é 400, 250 e 150. A sexta, o dia forte, ganha dela por um triz.
E há algo pior na última coluna. As mochilas. O mais caro da loja. No Norte, quase todos os dias: 100. Como se ninguém tocasse nelas.
Mateo sente um frio na barriga. Ainda não sabe o que significa. Mas sabe que acaba de encontrar a primeira pista de verdade.
Na sessão anterior, o PyTorch foi instalado e a papelaria de dona Lupe virou o primeiro tensor de verdade, com forma (2, 7, 3). Hoje se aprende o que Mateo fez naquela noite: extrair pedaços dele. Não só um número, mas um dia inteiro, um produto inteiro ou uma semana recortada, pelo eixo escolhido.
Revisão para abrir
A ideia é responder sem olhar a sessão anterior. As respostas estão nas soluções, no final.
- O que
t.shapediz e como conferir isso com umassert? - Por que
torch.tensor([[1, 2, 3], [4, 5]])falha? - Que
dtypesai detorch.tensor([1, 2, 3])e qual sai detorch.tensor([1.0, 2.0])?
O código desta sessão está no repositório público do curso, na pasta
sesion-03: github.com/HabilMX/curso-tensores. Vale escrevê-lo primeiro, porque é assim que se aprende; o repositório serve para comparar quando algo não sair igual. Cada programa vem com a saída que deve produzir.
Objetivos desta sessão
Ao terminar, será possível fazer estas quatro coisas. Ao lado de cada uma está o exercício que a demonstra.
| # | Ao terminar, é possível… | Demonstrado em |
|---|---|---|
| 1 | Nomear cada eixo de um tensor e escrevê-lo em um comentário acima do código | Exercícios 10 e 13 |
| 2 | Extrair um único número com um índice por eixo, papeleria[1, 4, 2], e convertê-lo em número de Python com .item() |
Exercício 10 |
| 3 | Extrair um pedaço por qualquer eixo com : e com um intervalo como 1:5, e prever a forma dele antes de executar |
Exercícios 11 e 12 |
| 4 | Decidir a ordem dos eixos de uma foto e extrair uma única cor | Exercício 13 |
O que é preciso: o ambiente da sessão 2 com o PyTorch instalado (ativado antes de começar) e um editor de texto. Hoje não se instala nada novo.
Por que importa
Mateo não encontrou a pista somando tudo. Encontrou recortando: um dia de duas filiais, uma coluna de uma filial. É assim que trabalha quase qualquer sistema com dados.
Um programa que analisa a foto de um carro batido quase nunca a usa inteira: recorta a região do para-choque, ou fica com uma única cor. Um banco que guarda as compras de mil clientes em 30 dias, com forma (1000, 30, 5), às vezes quer um cliente inteiro e às vezes só o dia de ontem de todos. É o mesmo tensor. O que muda é o eixo pelo qual ele é recortado.
E aqui vem a armadilha. Muitos dos erros que aparecem com modelos não são de matemática: são de forma. Pede-se um pedaço, acredita-se que ele tem uma forma, e ele tem outra. Nada quebra. Só sai um número errado, e a gente acredita nele. Hoje se aprende a prever essa forma antes de executar o código, para que isso não aconteça.
Passo 1 — Dar nome a cada eixo
Antes de recortar qualquer coisa, Mateo faz algo que parece exagero e não é: escreve acima do código o que é cada eixo. Porque o PyTorch não sabe o que é «Norte». Não sabe o que é «sexta-feira».
Para o PyTorch, papeleria é uma caixa de 2 × 7 × 3 números e nada mais. Quem sabe que o primeiro eixo são as filiais é quem escreve o código. Por isso a primeira regra de hoje: dizer em voz alta e escrever em um comentário, como a etiqueta de uma gaveta da cozinha.
Crie um arquivo ejes.py e comece assim:
import torch
# Ejes de la papelería:
# eje 0 = sucursal (0 Centro, 1 Norte)
# eje 1 = día (0 lunes ... 6 domingo)
# eje 2 = producto (0 cuadernos, 1 plumas, 2 mochilas)
centro = [
[1200, 800, 500], # lunes
[1500, 900, 700], # martes
[1000, 700, 500], # miércoles
[1300, 900, 600], # jueves
[2000, 1200, 700], # viernes
[1200, 800, 400], # sábado
[800, 500, 300], # domingo
]
norte = [
[300, 200, 100],
[400, 250, 150],
[350, 200, 100],
[300, 150, 100],
[500, 300, 200],
[300, 200, 100],
[200, 100, 100],
]
papeleria = torch.tensor([centro, norte])
print(papeleria.shape)
Execute com python3 ejes.py:
torch.Size([2, 7, 3])
Leia a forma com os nomes ao lado: 2 filiais, 7 dias, 3 produtos. Os eixos são contados a partir de 0, como os índices de uma lista: eixo 0, eixo 1, eixo 2. Onde estiver escrito «pelo eixo 1», leia-se «pelos dias».
— E isso serve para quê? — diz dona Lupe, espiando. — Se já sabe qual é qual.
— Hoje sim. Daqui a três semanas, às duas da manhã, não vou lembrar.
— Igual ao tio com as chaves. Por isso pus etiqueta nelas.
Passo 2 — Um único número: papeleria[1, 4, 2]
A primeira coisa que Mateo quer ver é um único número: o que o Norte vendeu de mochilas na sexta. Se houver algo estranho ali, quer tocá-lo com o dedo.
Na sessão 1, um número assim era apontado com papeleria[1][4][2]. Com um tensor isso continua funcionando, mas há um jeito melhor: todos os índices dentro do mesmo colchete, separados por vírgulas.
Daqui em diante, cada bloco vai sendo acrescentado ao final de ejes.py, e o arquivo é executado de novo; em cada saída aparecem só as linhas novas. Acrescente:
print(papeleria[1][4][2])
print(papeleria[1, 4, 2])
tensor(200)
tensor(200)
As duas dão o mesmo: Norte, sexta, mochilas, $200. No melhor dia da semana. Mateo anota o número em um post-it e cola na borda da tela.
A diferença está em como trabalham por dentro:
papeleria[1][4][2]são três passos: tira a aba do Norte, dela a sexta, da sexta as mochilas. Caixa dentro de caixa.papeleria[1, 4, 2]é um passo só: recebe o endereço completo e vai direto.
No passo 7 aparece um caso em que a dos colchetes separados engana. A partir de hoje, use a das vírgulas.
O que sai não é um número: é um tensor de zero dimensões
A saída não diz 200, diz tensor(200): o número vem embrulhado. Acrescente isto:
x = papeleria[1, 4, 2]
print(x.shape, x.ndim)
print(type(x))
print(x.item())
print(type(x.item()))
torch.Size([]) 0
<class 'torch.Tensor'>
200
<class 'int'>
Forma vazia, zero dimensões: é um escalar, como o total de $18,500 da sessão 1, mas em versão tensor. Uma bala que ainda está no papel. .item() tira o papel e devolve um int comum de Python, para imprimir, guardar em uma lista ou mandar para um relatório.
E a autocorreção de sempre: primeiro a forma, depois o valor.
assert papeleria[1, 4, 2].shape == (), "Un solo número no tiene ejes"
assert papeleria[1, 4, 2].item() == 200, "Norte, viernes, mochilas: 200"
print("Paso 2 correcto")
Paso 2 correcto
torch.Size([]) se compara sem problema com a tupla vazia (): as formas continuam sendo escritas como tuplas.
Um número solto não prova nada. 200 pesos de mochilas pode ser só uma sexta ruim. Mateo precisa ver a semana inteira do Norte, de uma vez.
Passo 3 — Um índice só: o eixo desaparece
O que acontece quando se dão menos índices que eixos? Teste com um só:
print(papeleria[1])
print(papeleria[1].shape)
tensor([[300, 200, 100],
[400, 250, 150],
[350, 200, 100],
[300, 150, 100],
[500, 300, 200],
[300, 200, 100],
[200, 100, 100]])
torch.Size([7, 3])
A aba inteira do Norte. Mateo desce os olhos pela última coluna: 100, 150, 100, 100, 200, 100, 100. Plana como uma mesa.
Mas repare na forma, que hoje é a protagonista. Passou de (2, 7, 3) para (7, 3): o eixo das filiais desapareceu. Uma já foi escolhida; diante da aba do Norte já não há «filiais», só dias e produtos.
Com dois índices, a mesma coisa:
print(papeleria[1, 4])
print(papeleria[1, 4].shape)
tensor([500, 300, 200])
torch.Size([3])
Norte, sexta, os três produtos. Foram-se dois eixos; fica o dos produtos. Primeira metade da regra de hoje:
Cada índice dado come o eixo dele. Três eixos menos um índice: sobram dois. Três eixos menos três índices: não sobra nenhum, e isso é um escalar.
Passo 4 — Os dois-pontos: «todos, por favor»
Agora vem a linha que deu o frio na barriga. Mateo não quer o Norte sozinho: quer o Norte ao lado do Centro, no mesmo dia, para que a diferença não tenha onde se esconder.
E para ter a sexta das duas filiais? É preciso pular o primeiro eixo sem escolher nada. Para isso servem os dois-pontos, :, que em um índice querem dizer «todos».
viernes = papeleria[:, 4]
print(viernes)
print(viernes.shape)
tensor([[2000, 1200, 700],
[ 500, 300, 200]])
torch.Size([2, 3])
Leia com os nomes dos eixos: todas as filiais, o dia 4 (sexta) e, como nada foi dito sobre o produto, todos os produtos. Linha de cima, Centro; linha de baixo, Norte.
Aí está. Em cima, 2000. Embaixo, 500. Mesmo dia, mesma saída da escola. Uma linha de código e a diferença fica à vista, sem abas nem copiar e colar.
Forma (2, 3): o eixo dos dias se foi porque um foi escolhido; o das filiais ficou porque todas foram pedidas; o dos produtos, porque não foi tocado. Como pedir tacos «com tudo»: não se escolhe cebola ou coentro, leva-se o que houver.
Conferir com a versão com for
Mateo não confia em uma linha só. Se vai mostrar isso ao banco, quer chegar ao resultado por dois caminhos. Com o que já se sabia, seria número por número:
a_mano = []
for s in range(2):
a_mano.append([papeleria[s][4][p].item() for p in range(3)])
print(a_mano)
print(viernes.tolist())
[[2000, 1200, 700], [500, 300, 200]]
[[2000, 1200, 700], [500, 300, 200]]
.tolist() é primo de .item(): converte um tensor inteiro de volta em listas de Python, que se comparam com == como sempre. As duas dão o mesmo; vale deixar isso por escrito:
assert viernes.shape == (2, 3), "Viernes: 2 sucursales × 3 productos"
assert viernes.tolist() == [[2000, 1200, 700], [500, 300, 200]]
print("Paso 4 correcto")
Paso 4 correcto
Passo 5 — Um intervalo: papeleria[0, 1:5]
— Vamos ver — diz dona Lupe. — Mostre como é uma semana normal. A do Centro. Mas sem a segunda, que é sempre parada, nem o fim de semana.
Mateo precisa de um intervalo: de terça a sexta. Os dois-pontos também pedem um intervalo, como nas listas de Python: lista[1:5] dá os elementos 1, 2, 3 e 4. Inclui o primeiro, deixa de fora o último. Com tensores, em qualquer eixo.
Dona Lupe quer o Centro de terça a sexta:
tramo = papeleria[0, 1:5]
print(tramo)
print(tramo.shape)
tensor([[1500, 900, 700],
[1000, 700, 500],
[1300, 900, 600],
[2000, 1200, 700]])
torch.Size([4, 3])
Centro, dias de 1 a 4 (o 5, sábado, fica de fora), todos os produtos. Forma (4, 3): o eixo das filiais se foi por causa do índice; o dos dias ficou, mas mede 4 em vez de 7. Um intervalo encurta o eixo, não o remove. Compare com a lista centro de Python:
print(centro[1:5])
assert tramo.shape == (4, 3), "De martes a viernes: 4 días × 3 productos"
assert tramo.tolist() == centro[1:5]
print("Paso 5 correcto")
[[1500, 900, 700], [1000, 700, 500], [1300, 900, 600], [2000, 1200, 700]]
Paso 5 correcto
Esse pedaço, o que se extrai com dois-pontos, se chama fatia. É a palavra que aparece na documentação em português; em inglês é slice.
Passo 6 — O último eixo: papeleria[:, :, 2]
Dona Lupe larga as notas. Já não está contando. Agora quem pergunta é ela: quanto cada filial vendeu de mochilas em cada dia? As mochilas são o que mais dá lucro. Se algo está errado, ela quer ver ali.
O produto é o eixo 2, o último; para chegar até ele, passa-se pelos outros dois com «todos»:
mochilas = papeleria[:, :, 2]
print(mochilas)
print(mochilas.shape)
tensor([[500, 700, 500, 600, 700, 400, 300],
[100, 150, 100, 100, 200, 100, 100]])
torch.Size([2, 7])
Forma (2, 7): foi-se o eixo dos produtos; cada linha é uma filial e cada coluna, um dia.
Dona Lupe chega tão perto da tela que Mateo precisa se afastar. Em cima, Centro: 500, 700, 500, 600… Embaixo, Norte: 100, 150, 100, 100…
— Menino, no Norte passa mais gente do que aqui. Fica junto do ponto de ônibus.
Mateo não responde. Ainda não tem o que responder.
Na planilha, seria preciso copiar a última coluna de cada aba e colá-las lado a lado. Aqui foi uma linha. Com for, são dois laços:
m = []
for s in range(2):
fila = []
for d in range(7):
fila.append(papeleria[s, d, 2].item())
m.append(fila)
print(m)
assert mochilas.shape == (2, 7), "Mochilas: 2 sucursales × 7 días"
assert mochilas.tolist() == m
print("Paso 6 correcto")
[[500, 700, 500, 600, 700, 400, 300], [100, 150, 100, 100, 200, 100, 100]]
Paso 6 correcto
Sete linhas contra uma. Essa é a graça dos tensores: quando há pressa (e Mateo tem 37 dias), a pergunta e a resposta cabem na mesma linha.
Passo 7 — Por que a das vírgulas: a armadilha de papeleria[:][4]
Naquela mesma noite, Mateo quase errou. Antes de chegar à linha certa do passo 4, testou a que saía naturalmente. Para pedir a sexta das duas filiais com colchetes separados, o natural seria isto:
print(papeleria[:][4])
Traceback (most recent call last):
...
IndexError: index 4 is out of bounds for dimension 0 with size 2
Interprete o erro palavra por palavra: «o índice 4 está fora dos limites da dimensão 0, que mede 2». Dimensão 0? Mas o que se queria era o dia!
Os colchetes separados trabalham em passos, e cada passo começa de novo pelo primeiro eixo do que sobrou do passo anterior, não pelo eixo que se tinha em mente:
papeleria[:]é «todas as filiais»: devolve o tensor inteiro, igualzinho.[4]cai outra vez no primeiro eixo: pede a filial 4, e só há duas.
Com vírgulas, cada posição é um eixo fixo. E desta vez houve sorte: deu erro. Se dona Lupe tivesse 7 filiais, papeleria[:][4] daria a filial 4 sem reclamar, e pareceria ser a sexta. O erro que não aparece é o perigoso.
Mateo imagina a cena: ele diante do doutor Ortega, mostrando «as sextas» que na verdade são outra filial. O banco não lhe daria uma segunda chance. Desde aquela noite, tudo com vírgulas.
Passo 8 — papeleria[0] versus papeleria[0:1]
Uma última coisa incomoda Mateo. Há duas maneiras de pedir uma única filial, e parece que dão o mesmo. Parece.
Use o índice e a fatia para pedir a mesma coisa, a filial Centro:
print(papeleria[0].shape)
print(papeleria[0:1].shape)
torch.Size([7, 3])
torch.Size([1, 7, 3])
Os mesmos 21 números, forma diferente. papeleria[0] é um índice: o eixo desaparece e o que sobra é uma tabela. papeleria[0:1] é uma fatia de uma única filial: o eixo fica, mesmo medindo 1, e o que sobra é uma planilha de uma aba só:
print(papeleria[0:1])
tensor([[[1200, 800, 500],
[1500, 900, 700],
[1000, 700, 500],
[1300, 900, 600],
[2000, 1200, 700],
[1200, 800, 400],
[ 800, 500, 300]]])
Três colchetes no começo: continua tendo três dimensões. É tirar uma folha da pasta versus ficar com uma pasta de uma folha só. E isso importa para o PyTorch: um modelo que espera «uma pasta de fotos», com forma (N, C, H, W), pode rejeitar uma foto solta de forma (C, H, W). A regra completa de hoje:
Com um índice, o eixo desaparece. Com uma fatia, o eixo fica (mesmo medindo 1).
| O que se escreve | Que eixo se vai | Forma | O que é |
|---|---|---|---|
papeleria[1, 4, 2] |
os três | () |
Norte, sexta, mochilas |
papeleria[1] |
filial | (7, 3) |
a aba do Norte |
papeleria[1, 4] |
filial e dia | (3,) |
Norte, sexta, os 3 produtos |
papeleria[:, 4] |
dia | (2, 3) |
a sexta das duas filiais |
papeleria[0, 1:5] |
filial | (4, 3) |
Centro de terça a sexta |
papeleria[:, :, 2] |
produto | (2, 7) |
as mochilas, por filial e dia |
papeleria[0:1] |
nenhum | (1, 7, 3) |
uma planilha só com a aba Centro |
O truque para prever a forma: escrever a forma original, riscar cada eixo onde foi posto um índice e trocar pelo comprimento do intervalo cada eixo onde foi posta uma fatia. Para papeleria[0, 1:5]: (2, 7, 3) → risca o 2 → troca o 7 por 4 (de 1 a 4) → (4, 3).
Passo 9 — Uma foto de 2 × 3 pontos
Terça-feira, 17 de novembro, 1h15. Quarto de Mateo.
Ele não consegue dormir. No celular há uma foto que dona Lupe mandou à tarde: a maquininha do Norte, com o cabo enrolado. «Para ver que está conectada, sim», escreveu.
Mateo pensa que uma foto também é um tensor. E que, se sabe recortar a papelaria, sabe recortar uma foto.
Para conferir, começa com uma foto minúscula feita à mão: 2 pontos de altura por 3 de largura. Cada ponto traz três números, vermelho, verde e azul, de 0 (nada) a 255 (tudo). O amarelo, por exemplo, é vermelho e verde no máximo, sem azul.
Crie um arquivo foto.py:
import torch
# Foto de 2 × 3 puntos, en orden alto × ancho × color
# eje 0 = renglón (alto), eje 1 = punto en el renglón (ancho), eje 2 = color (0 rojo, 1 verde, 2 azul)
foto = torch.tensor([
[[255, 0, 0], [0, 255, 0], [0, 0, 255]], # arriba: rojo, verde, azul
[[255, 255, 255], [0, 0, 0], [255, 255, 0]], # abajo: blanco, negro, amarillo
])
print(foto.shape)
torch.Size([2, 3, 3])
A forma tem dois 3: o primeiro são os pontos de largura; o segundo, as cores. Sem o comentário, ninguém sabe qual é qual, nem quem escreveu o código daqui a duas semanas. Extraia só o canal vermelho, igual às mochilas:
rojo = foto[:, :, 0]
print(rojo)
print(rojo.shape)
assert rojo.shape == (2, 3), "El canal rojo es una tabla de 2 × 3"
assert rojo.tolist() == [[255, 0, 0], [255, 0, 255]]
print("Canal rojo correcto")
tensor([[255, 0, 0],
[255, 0, 255]])
torch.Size([2, 3])
Canal rojo correcto
Em cima, só o ponto vermelho tem vermelho; embaixo, o branco e o amarelo têm, o preto não. Essa tabela é uma foto em preto e branco de quanto vermelho há em cada ponto: isso é «separar canais». Um ponto solto, com as três cores:
print(foto[1, 2])
tensor([255, 255, 0])
Embaixo, à direita: o amarelo.
Funciona igual às mochilas. Os mesmos dois-pontos, as mesmas regras. Mateo sorri no escuro. O que aprendeu com a papelaria serve para qualquer caixa de números.
Passo 10 — A ordem dos eixos é uma decisão de projeto
Mas ali, à uma da manhã, surge uma pergunta incômoda: quem decidiu que a cor seria o último eixo? Ele mesmo. A foto foi guardada como altura × largura × cor, mas nada obrigava a isso. Dá para guardá-la como cor × altura × largura: a tabela do vermelho, depois a do verde, depois a do azul.
# La misma foto en orden color × alto × ancho
# eje 0 = color, eje 1 = renglón, eje 2 = punto en el renglón
foto_cha = torch.tensor([
[[255, 0, 0], [255, 0, 255]], # canal rojo
[[0, 255, 0], [255, 0, 255]], # canal verde
[[0, 0, 255], [255, 0, 0]], # canal azul
])
print(foto_cha.shape)
print(foto_cha[0])
assert foto_cha[0].tolist() == foto[:, :, 0].tolist()
print("Mismo rojo, otro orden")
torch.Size([3, 2, 3])
tensor([[255, 0, 0],
[255, 0, 255]])
Mismo rojo, otro orden
Os mesmos 18 números, outra forma: (3, 2, 3). E o vermelho já não é foto[:, :, 0], é só foto_cha[0]. O que vem primeiro é o que se extrai com mais facilidade.
Qual é o correto? Os dois. É como arrumar o guarda-roupa por tipo de roupa ou por conjunto, ou como dona Lupe arruma o depósito: por filial ou por produto. A mercadoria é a mesma; muda o que se encontra rápido. Por isso a ordem dos eixos é uma decisão de projeto: é tomada pensando no que vai ser pedido com mais frequência.
Já existem costumes. No PyTorch, a convenção comum para imagens é canais primeiro, que na documentação aparece como C × H × W (channels, height, width: canais, altura e largura; documentação do TorchVision); um lote de fotos acrescenta no começo o eixo de qual foto. Outras ferramentas e muitos programas de imagem costumam usar altura × largura × cor. Nenhum está errado; o problema é não saber qual se tem em mãos. Por isso, com fotos, o comentário com os eixos vai sempre acima do tensor.
| Ordem | Forma da foto | O canal vermelho | Um ponto com as 3 cores |
|---|---|---|---|
| altura × largura × cor | (2, 3, 3) |
foto[:, :, 0] |
foto[1, 2] |
| cor × altura × largura | (3, 2, 3) |
foto_cha[0] |
foto_cha[:, 1, 2] |
Executando foto_cha[:, 1, 2], sai tensor([255, 255, 0]): o mesmo amarelo.
Mateo fecha o notebook às 2h03. No post-it da tela continua o 200. Já não lhe parece um número. Parece uma pergunta.
👉 Para quem é de Administração: o que Mateo fez naquela noite é o que se faz numa planilha ao filtrar ou ocultar colunas: «só a sexta de todas as filiais» ou «só as mochilas de todas as abas». Aqui cada uma é uma linha. E o passo 10 serve para qualquer relatório: quando pedirem «vendas por filial e por mês», vale perguntar o que vai nas linhas e o que vai nas colunas. É uma decisão de projeto, e é tomada pensando em quem vai ler.
Exercícios da sessão 3
As soluções estão no final. O trabalho é no ejes.py, que já tem a papeleria e o comentário com os eixos. No exercício 12, os assert já escritos dizem se houve acerto; no 10, no 11 e no 13, compare o trabalho com as soluções.
Exercício 10 — Aponte o número, agora com vírgulas. Com um índice por eixo e .item(), escreva o assert de cada dado:
a) O que o Centro vendeu de canetas na quarta.
b) O que o Norte vendeu de cadernos no domingo.
c) O que o Centro vendeu de mochilas no sábado.
d) Confira com um assert que a resposta de a) dá o mesmo com os colchetes separados da sessão 1.
Exemplo de um resolvido: assert papeleria[0, 0, 0].item() == 1200 (Centro, segunda, cadernos).
Exercício 11 — Preveja a forma antes de executar. No papel, escreva a forma de cada expressão e o que ela representa, usando o truque do passo 8. Depois execute e compare.
for nombre, x in [
("a) papeleria[0]", papeleria[0]),
("b) papeleria[0, 6]", papeleria[0, 6]),
("c) papeleria[:, 6]", papeleria[:, 6]),
("d) papeleria[:, :, 0]", papeleria[:, :, 0]),
("e) papeleria[1, 0:3]", papeleria[1, 0:3]),
("f) papeleria[1, 0:3, 1]", papeleria[1, 0:3, 1]),
("g) papeleria[0:1, 6]", papeleria[0:1, 6]),
("h) papeleria[1, 4, 2]", papeleria[1, 4, 2]),
]:
print(nombre, x.shape)
Se alguma falhar, procure qual eixo parecia ficar e se foi, ou o contrário.
Exercício 12 — Complete as lacunas. Preencha cada ____ e confira com os assert:
# a) El fin de semana (sábado y domingo) de Norte, los 3 productos
fin_norte = papeleria[____, ____]
assert fin_norte.shape == (2, 3), "Fin de semana de Norte: 2 días × 3 productos"
assert fin_norte.tolist() == [[300, 200, 100], [200, 100, 100]]
# b) Las plumas del Centro de lunes a miércoles
plumas = papeleria[0, ____, ____]
assert plumas.shape == (3,), "Plumas del Centro: 3 días"
assert plumas.tolist() == [800, 900, 700]
# c) Lo mismo que b), con un for
a_mano = []
for d in range(____):
a_mano.append(papeleria[0, d, 1].____)
assert plumas.tolist() == a_mano
print("Ejercicio 12 superado")
Dica para a): o domingo é o dia 6, e a fatia deixa de fora o último número.
Exercício 13 — Uma foto própria. Crie à mão uma foto de 3 pontos de altura por 2 de largura, guardada na ordem que o PyTorch costuma usar: cor × altura × largura (C × H × W). Escolha as cores dos 6 pontos (vermelho, verde, azul, branco, preto, amarelo ou as que quiser) e escreva-as em um comentário. Depois:
a) Escreva o assert da forma.
b) Extraia o canal verde. Escreva o assert da forma dele e o do valor contra uma lista escrita à mão.
c) Extraia a linha de cima com as três cores. Que forma ela tem? Escreva os dois assert.
d) Extraia o ponto de baixo à direita com as três cores. Escreva os dois assert.
Não há uma única resposta; o que precisa se cumprir é que os assert passem e que o comentário diga o que é cada eixo.
Erros comuns (e como perceber)
| Erro | Como aparece | Como perceber | O que fazer |
|---|---|---|---|
| Pedir um índice que não existe | papeleria[1, 7, 2] dá IndexError: index 7 is out of bounds for dimension 1 with size 7 |
A mensagem diz qual eixo (dimension 1, os dias) e quanto ele mede (size 7) | Os índices vão de 0 ao tamanho menos 1: os dias, de 0 a 6 |
| Inverter a ordem dos eixos | papeleria[4, 1, 2] em vez de papeleria[1, 4, 2] dá IndexError: index 4 is out of bounds for dimension 0 with size 2 |
Dá erro na dimension 0, que tem só 2 filiais | Leia o comentário dos eixos: o primeiro índice é sempre a filial |
Encadear colchetes com : |
papeleria[:][4] dá o mesmo erro de dimension 0 ou, pior, não dá erro e devolve outra coisa |
O resultado não é o que foi pedido, ou dá erro em um eixo que não foi tocado | Todos os índices em um único colchete: papeleria[:, 4] |
| Achar que o intervalo inclui o último | Pede-se de terça a sexta com 1:4 e falta a sexta |
O assert da forma diz 3 dias onde se esperavam 4 |
O último número fica de fora: terça a sexta é 1:5 |
| Confundir índice com fatia | papeleria[0:1] tem forma (1, 7, 3) e se esperava (7, 3) |
O assert da forma falha com um 1 a mais |
Índice: o eixo se vai. Fatia: o eixo fica, mesmo medindo 1 |
Usar .item() com vários números |
papeleria[0, 0].item() dá RuntimeError: a Tensor with 3 elements cannot be converted to Scalar |
A mensagem diz quantos elementos havia (3) | .item() é só para um número; para vários, .tolist() |
Comparar um pedaço com um número em um assert |
assert papeleria[0, 0] == 1200 dá RuntimeError: Boolean value of Tensor with more than one value is ambiguous |
Quem dá erro é o assert, não o índice |
Faltou um índice (o desejado era papeleria[0, 0, 0]), ou compare a forma e depois .tolist() com uma lista |
Uma armadilha que não dá erro: papeleria[0, 5:20] não acusa nada, embora só existam 7 dias; devolve sábado e domingo, forma (2, 3), igual fariam as listas de Python. Se um intervalo trouxer menos do que o esperado, revise os números dele.
Revisão para a próxima sessão
A sessão 4 começa com estas três perguntas. Responda sem olhar a lição:
- Que forma tem
papeleria[:, 4]e o que ela representa? - Que diferença há entre
papeleria[0]epapeleria[0:1]na forma? - Como extrair as vendas de mochilas das duas filiais em todos os dias?
🔁 Na próxima semana:
Terça, 8h10 da manhã. Mateo liga para dona Lupe antes de entrar na aula. Conta sobre a sexta. Sobre as mochilas que não saem.
Do outro lado da linha, um silêncio longo. Ouve-se a porta de aço do Centro subindo.
— Pois se as mochilas não estão sendo vendidas — diz enfim dona Lupe —, o que estão cobrando das pessoas?
Mateo não sabe. Mas sabe como continuar: parar de extrair pedaços e começar a resumi-los. Totais e médias por eixo: quanto cada filial vendeu na semana, qual foi a média de cada dia, tudo em uma linha e conferido contra um for. Quando somar a semana do Norte, o número vai apontar para alguém.
Soluções (não olhe antes de tentar)
Revisão para abrir.
t.shapediz quantos elementos há em cada eixo: para a papelaria, 2 filiais, 7 dias e 3 produtos. Confere-se comassert papeleria.shape == (2, 7, 3).- Porque não é retangular: a primeira linha tem 3 números e a segunda, 2. O PyTorch se recusa e diz isso:
ValueError: expected sequence of length 3 at dim 1 (got 2). torch.tensor([1, 2, 3])saitorch.int64(inteiros) etorch.tensor([1.0, 2.0])saitorch.float32(decimais).
Exercício 10.
assert papeleria[0, 2, 1].item() == 700 # a) Centro, miércoles, plumas
assert papeleria[1, 6, 0].item() == 200 # b) Norte, domingo, cuadernos
assert papeleria[0, 5, 2].item() == 400 # c) Centro, sábado, mochilas
assert papeleria[0][2][1].item() == papeleria[0, 2, 1].item() # d)
print("Ejercicio 10 superado")
Ejercicio 10 superado
Os itens a) e b) são os mesmos dados procurados nos itens a) e c) do exercício 2 da sessão 1, agora com vírgulas.
Exercício 11.
a) papeleria[0] torch.Size([7, 3])
b) papeleria[0, 6] torch.Size([3])
c) papeleria[:, 6] torch.Size([2, 3])
d) papeleria[:, :, 0] torch.Size([2, 7])
e) papeleria[1, 0:3] torch.Size([3, 3])
f) papeleria[1, 0:3, 1] torch.Size([3])
g) papeleria[0:1, 6] torch.Size([1, 3])
h) papeleria[1, 4, 2] torch.Size([])
a) a aba Centro; b) o domingo do Centro; c) o domingo das duas filiais; d) os cadernos das duas, todos os dias; e) Norte de segunda a quarta; f) as canetas do Norte de segunda a quarta; g) o domingo do Centro conservando o eixo de filial, que mede 1; h) Norte, sexta, mochilas. A traiçoeira é a g): parece a b), mas a filial foi pedida com fatia.
Exercício 12.
fin_norte = papeleria[1, 5:7] # a)
plumas = papeleria[0, 0:3, 1] # b)
for d in range(3): # c)
a_mano.append(papeleria[0, d, 1].item())
Ejercicio 12 superado
Em a), 5:7 pega o sábado (5) e o domingo (6) e deixa de fora o 7, que de qualquer forma não existe.
Exercício 13. Uma resposta possível:
import torch
# Foto de 3 × 2 puntos, en orden color × alto × ancho
# eje 0 = color (0 rojo, 1 verde, 2 azul)
# eje 1 = renglón (0 arriba ... 2 abajo)
# eje 2 = punto en el renglón (0 izquierda, 1 derecha)
# Los puntos: arriba rojo y verde; en medio azul y blanco; abajo negro y morado
foto = torch.tensor([
[[255, 0], [0, 255], [0, 128]], # canal rojo
[[0, 255], [0, 255], [0, 0]], # canal verde
[[0, 0], [255, 255], [0, 128]], # canal azul
])
assert foto.shape == (3, 3, 2), "3 colores × 3 renglones × 2 puntos"
verde = foto[1]
assert verde.shape == (3, 2)
assert verde.tolist() == [[0, 255], [0, 255], [0, 0]]
arriba = foto[:, 0]
assert arriba.shape == (3, 2)
assert arriba.tolist() == [[255, 0], [0, 255], [0, 0]]
esquina = foto[:, 2, 1]
assert esquina.shape == (3,)
assert esquina.tolist() == [128, 0, 128]
print("Ejercicio 13 superado")
Ejercicio 13 superado
Em c), a forma é (3, 2), não (2, 3): extrair um pedaço não reorganiza os eixos que sobram, deixa-os na ordem em que estavam, e a cor vem primeiro.
O que vem a seguir
Na sessão 4, o total do Centro vai ser a soma de papeleria[0]. Para adiantar, vale calcular com um for quanto cada filial vendeu na semana. O do Centro já se conhece desde a sessão 1: $18,500. Falta o do Norte.
Escrevo a partir do que vemos todos os dias na Hábil, onde construímos sistemas para bancos, seguradoras e empresas no México. Se no trabalho ou no negócio da família aparecer um problema assim, vamos conversar: habil.mx
Prefere e-mail? Escreva para hola@habil.mx