Съдържание на курса

Тензори от нулата (3): осите имат имена

От · основател и главен изпълнителен директор на Hábil ·

Обновено на 21 октомври 2026 г.

Как да извадиш част от тензор по която и да е от осите му (едно число, един ден, един продукт, червения канал на снимка) и какво става с формата, когато го направиш.

Тензори от нулата · урок A, сесия 3 от 8

Сценарий: героите и историята са измислени. Техническите и историческите данни и реалните цифри са с посочен източник.

Понеделник, 16 ноември, 7:40 вечерта. Книжарницата в Centro.

Металната ролетка е спусната наполовина. Вътре мирише на нова хартия и на претоплено кафе. Доня Лупе брои банкноти на тезгяха. Матео е отворил лаптопа до касовия апарат, между буркан с моливи и купчина картони.

Остават 37 дни до 23 декември.

Миналата седмица оправиха файла от Norte, онзи с непълния ред. Сега PyTorch вече го приема: тензор с форма (2, 7, 3). Два клона, седем дни, три продукта. Четиридесет и две числа.

— И? — пита доня Лупе, без да вдига поглед. — Вече знаеш ли кой ме краде?

— Още не, лельо. Имам числата. Остава да се науча да ги гледам.

— Ами гледай ги. Затова имаш очи.

Матео се смее, но тя е права. Четиридесет и две числа, натрупани заедно, не казват нищо. Като склад, в който всичко е на куп: отговорът е там, но не го намираш. Трябва му да изважда части. Един ден. Един продукт. Един клон. Да ги сложи една до друга и да види какво не се връзва.

Пише един ред. Излиза му петъкът на двата клона.

Застива.

— Лельо. Кой е най-добрият ти ден от седмицата?

— Петъкът, кой друг? Излизат от училище, идват с родителите си, купуват от всичко.

— В Centro — да. В петък продаваш тетрадки за 2,000. Никой друг ден не се доближава.

— А в Norte?

Матео обръща екрана. 500, 300, 200.

Доня Лупе си слага очилата. После пак ги сваля.

— Толкова продава Norte в най-обикновен вторник.

Почти. Вторникът в Norte е 400, 250 и 150. Петъкът, силният ден, го надминава съвсем малко.

А в последната колона има нещо още по-лошо. Раниците. Най-скъпото в магазина. В Norte почти всеки ден: 100. Сякаш никой не ги докосва.

Матео усеща празнина в стомаха. Още не знае какво означава. Но знае, че току-що е намерил първата истинска следа.


В предишната сесия инсталира PyTorch и превърна книжарницата на доня Лупе в първия си истински тензор, с форма (2, 7, 3). Днес научаваш онова, което Матео направи тази вечер: да изважда части от него. Не само едно число, а цял ден, цял продукт или изрязана седмица, по оста, която ти избереш.

Преговор за начало

Отговори, без да поглеждаш предишната сесия. Отговорите са в края, в решенията.

  1. Какво ти казва t.shape и как го проверяваш с assert?
  2. Защо torch.tensor([[1, 2, 3], [4, 5]]) се проваля?
  3. Какъв dtype излиза от torch.tensor([1, 2, 3]) и какъв от torch.tensor([1.0, 2.0])?

Кодът на тази сесия е в публичното хранилище на курса, в папката sesion-03: github.com/HabilMX/curso-tensores. Първо го напиши сам, защото така се учи; хранилището е за сравнение, когато нещо не ти излезе по същия начин. Всяка програма идва с изхода, който трябва да получиш.

Цели на тази сесия

В края ще можеш да правиш тези четири неща. До всяко е упражнението, което го доказва.

# В края можеш… Доказва го
1 Да назовеш всяка ос на тензор и да я запишеш в коментар над кода Упражнения 10 и 13
2 Да извадиш едно-единствено число с по един индекс за всяка ос, papeleria[1, 4, 2], и да го превърнеш в число на Python с .item() Упражнение 10
3 Да извадиш част по която и да е ос с : и с диапазон като 1:5 и да предвидиш формата ѝ, преди да изпълниш кода Упражнения 11 и 12
4 Да решиш реда на осите на снимка и да извадиш един-единствен цвят Упражнение 13

Какво ти трябва: средата от сесия 2 с инсталиран PyTorch (активирай я, преди да започнеш) и текстов редактор. Днес не инсталираш нищо ново.


Защо е важно

Матео не намери следата, като сумира всичко. Намери я, като изрязва: един ден от два клона, една колона от един клон. Почти всяка система, която работи с данни, работи така.

Програма, която преглежда снимка на ударена кола, почти никога не я използва цялата: изрязва зоната на бронята или оставя само един цвят. Банка, която пази покупките на хиляда клиенти за 30 дни, с форма (1000, 30, 5), понякога иска един клиент изцяло, а понякога само вчерашния ден на всички. Тензорът е един и същ. Променя се оста, по която го режеш.

И тук идва капанът. Много от грешките, които ще срещнеш при моделите, не са математически: те са грешки във формата. Поискал си част, мислил си, че има една форма, а тя е имала друга. Нищо не хвърля грешка. Просто получаваш грешно число и му вярваш. Днес се учиш да предвиждаш тази форма, преди да изпълниш кода, за да не ти се случи това.

Стъпка 1 — Дай име на всяка ос

Преди да реже каквото и да е, Матео прави нещо, което изглежда излишно, но не е: записва над кода какво е всяка ос. Защото PyTorch не знае какво е „Norte“. Не знае какво е „петък“.

За PyTorch papeleria е кутия с 2 × 7 × 3 числа и нищо повече. Ти си този, който знае, че първата ос са клоновете. Затова първото правило за днес: кажи го на глас и го запиши в коментар, като етикета на чекмедже в кухнята.

Създай файл ejes.py и започни така:

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)

Изпълни го с python3 ejes.py:

torch.Size([2, 7, 3])

Разчети я с поставените имена: 2 клона, 7 дни, 3 продукта. Осите се броят от 0, като индексите на списък: ос 0, ос 1, ос 2. Когато прочетеш „по ос 1“, превеждай „по дните“.

— И за какво ти е това? — казва доня Лупе и надниква. — Нали вече знаеш кое какво е.

— Днес — да. След три седмици, в два през нощта, няма да си спомням.

— Като чичо ти с ключовете. Затова им сложих етикети.

Стъпка 2 — Едно-единствено число: papeleria[1, 4, 2]

Първото, което Матео иска да види, е едно-единствено число: колко раници е продал Norte в петък. Ако там има нещо странно, иска да го пипне с пръст.

В сесия 1 посочи такова число така: papeleria[1][4][2]. С тензор това продължава да работи, но има по-добър начин: всички индекси в едни и същи квадратни скоби, разделени със запетаи.

Оттук нататък добавяй всеки блок в края на ejes.py и го изпълнявай отново; при всеки изход ти показвам само новите редове. Добави:

print(papeleria[1][4][2])
print(papeleria[1, 4, 2])
tensor(200)
tensor(200)

И двата дават едно и също: Norte, петък, раници, $200. В най-добрия ден от седмицата. Матео записва числото на лепящо листче и го залепва на ръба на екрана.

Разликата е в това как работят отвътре:

  • papeleria[1][4][2] са три стъпки: изважда листа Norte, от него петъка, от петъка раниците. Кутия в кутия.
  • papeleria[1, 4, 2] е една-единствена стъпка: даваш му пълния адрес и той отива направо.

В стъпка 7 ще видиш случай, в който вариантът с отделни скоби те подвежда. От днес използвай този със запетаите.

Това, което излиза, не е число: тензор с нула измерения

Изходът не казва 200, а tensor(200): числото е опаковано. Добави това:

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'>

Празна форма, нула измерения: това е скалар, като общата сума от $18,500 в сесия 1, но във версия тензор. Бонбон, който още е в обвивката си. .item() маха обвивката и ти дава обикновен int на Python, за да го отпечаташ, да го запазиш в списък или да го пратиш в отчет.

И обичайната самопроверка: първо формата, после стойността.

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([]) се сравнява правилно с празния кортеж (): формите ти продължават да се пишат като кортежи.

Едно отделно число не доказва нищо. 200 песо от раници може просто да е лош петък. На Матео му трябва цялата седмица на Norte, наведнъж.

Стъпка 3 — Само един индекс: оста изчезва

Какво става, ако дадеш по-малко индекси, отколкото са осите? Опитай само с един:

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])

Целият лист Norte. Матео плъзга поглед надолу по последната колона: 100, 150, 100, 100, 200, 100, 100. Равна като маса.

Но обърни внимание на формата, която днес е главната героиня. От (2, 7, 3) стана (7, 3): оста на клоновете изчезна. Вече си избрал един; пред листа Norte вече няма „клонове“, а само дни и продукти.

С два индекса — същото:

print(papeleria[1, 4])
print(papeleria[1, 4].shape)
tensor([500, 300, 200])
torch.Size([3])

Norte, петък, трите продукта. Две оси изчезнаха; остава тази на продуктите. Първата половина от правилото за днес:

Всеки индекс, който даваш, изяжда своята ос. Три оси минус един индекс: остават две. Три оси минус три индекса: не остава нито една, и това е скалар.

Стъпка 4 — Двоеточието: „всички, моля“

Сега идва редът, който му предизвика празнината в стомаха. Матео не иска Norte сам: иска Norte до Centro, в същия ден, за да няма къде да се скрие разликата.

Ами ако искаш петъка на двата клона? Трябва да прескочиш първата ос, без да избираш нищо. За това е двоеточието, :, което в индекс означава „всички“.

viernes = papeleria[:, 4]
print(viernes)
print(viernes.shape)
tensor([[2000, 1200,  700],
        [ 500,  300,  200]])
torch.Size([2, 3])

Разчети го с имената на осите: всички клонове, ден 4 (петък), и понеже не каза нищо за продукта — всички продукти. Горният ред е Centro; долният е Norte.

Ето го. Горе — 2000. Долу — 500. Същият ден, същото излизане от училище. Един ред код и разликата е пред очите ти, без листове и без копиране и поставяне.

Форма (2, 3): оста на дните изчезна, защото избра един; тази на клоновете остана, защото поиска всички; тази на продуктите — защото не я пипна. Като да си поръчаш тако „с всичко“: не избираш лук или кориандър, взимаш каквото има.

Провери го срещу версията с for

Матео не се доверява на един-единствен ред. Ако ще показва това на банката, иска да го получи по два пътя. С онова, което вече знаеше, това би било число по число:

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() е братовчед на .item(): превръща цял тензор обратно в списъци на Python, които се сравняват с == както винаги. Двата дават едно и също; запиши го:

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

Стъпка 5 — Диапазон: papeleria[0, 1:5]

— Я да видим — казва доня Лупе. — Покажи ми как изглежда една нормална седмица. Тази на Centro. Но без понеделника, който винаги е мъртъв, и без събота и неделя.

На Матео му трябва диапазон: от вторник до петък. Двоеточието иска и диапазон, както при списъците в Python: lista[1:5] дава елементи 1, 2, 3 и 4. Включва първия, оставя последния извън. С тензорите — по която и да е ос.

Доня Лупе иска Centro от вторник до петък:

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, дни от 1 до 4 (5-ият, събота, остава извън), всички продукти. Форма (4, 3): оста на клоновете изчезна заради индекса; тази на дните остана, но е с дължина 4 вместо 7. Диапазонът скъсява оста, не я маха. Сравни го със списъка centro в 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

Тази част, която се изважда с двоеточие, се нарича срез. Това е думата, която ще срещаш в материалите на български; на английски е slice.

Стъпка 6 — Последната ос: papeleria[:, :, 2]

Доня Лупе оставя банкнотите. Вече не брои. Сега тя пита: колко раници е продал всеки клон всеки ден? Раниците носят най-голяма печалба. Ако нещо не е наред, иска да го види там.

Продуктът е ос 2, последната; за да стигнеш до нея, минаваш през другите две с „всички“:

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])

Форма (2, 7): изчезна оста на продуктите; всеки ред е клон, а всяка колона — ден.

Доня Лупе се приближава толкова до екрана, че Матео трябва да се дръпне назад. Горе, Centro: 500, 700, 500, 600… Долу, Norte: 100, 150, 100, 100…

— Момчето ми, покрай Norte минават повече хора, отколкото тук. Той е до автобусната спирка.

Матео не отговаря. Още няма какво да отговори.

В електронната таблица би трябвало да копираш последната колона от всеки лист и да ги поставиш една до друга. Тук беше един ред. С for са два цикъла:

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

Седем реда срещу един. Това е смисълът на тензорите: когато бързаш (а на Матео му остават 37 дни), въпросът и отговорът се събират на един ред.

Стъпка 7 — Защо със запетаи: капанът на papeleria[:][4]

Същата вечер Матео едва не сгреши. Преди да стигне до правилния ред от стъпка 4, опита онзи, който му дойде естествено. Ако поискаш петъка на двата клона с отделни скоби, естественото би било това:

print(papeleria[:][4])
Traceback (most recent call last):
  ...
IndexError: index 4 is out of bounds for dimension 0 with size 2

Разчети грешката дума по дума: „индекс 4 е извън границите на измерение 0, което е с дължина 2“. Измерение 0? Нали искаше деня!

Отделните скоби работят на стъпки и всяка стъпка започва отново от първата ос на онова, което е останало от предишната стъпка, а не от оста, която си имал наум:

  1. papeleria[:] е „всички клонове“: връща ти целия тензор, абсолютно същия.
  2. [4] пак попада на първата ос: искаш клон 4, а има само два.

Със запетаите всяка позиция е фиксирана ос. И този път имаше късмет: хвърли грешка. Ако доня Лупе имаше 7 клона, papeleria[:][4] щеше да ти даде клон 4, без да се оплаче, и щеше да си мислиш, че е петъкът. Опасна е грешката, която не хвърля грешка.

Матео си представя сцената: той пред господин Ортега, показва „петъците“, които всъщност са друг клон. Банката не би му дала втори шанс. От тази вечер — всичко със запетаи.

Стъпка 8 — papeleria[0] срещу papeleria[0:1]

Едно последно нещо човърка Матео. Има два начина да поискаш един-единствен клон и изглежда, че дават едно и също. Изглежда.

Използвай индекса и среза, за да поискаш едно и също, клона Centro:

print(papeleria[0].shape)
print(papeleria[0:1].shape)
torch.Size([7, 3])
torch.Size([1, 7, 3])

Същите 21 числа, различна форма. papeleria[0] е индекс: оста изчезва и ти остава таблица. papeleria[0:1] е срез от един-единствен клон: оста остава, макар да е с дължина 1, и ти остава файл с един-единствен лист:

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]]])

Три скоби в началото: все още е тримерен. Разликата е като между това да извадиш лист от папката и това да ти остане папка с един-единствен лист. И за PyTorch това има значение: модел, който очаква „папка със снимки“, с форма (N, C, H, W), може да откаже отделна снимка с форма (C, H, W). Цялото правило за днес:

С индекс оста изчезва. Със срез оста остава (макар да е с дължина 1).

Какво пишеш Коя ос изчезва Форма Какво е
papeleria[1, 4, 2] и трите () Norte, петък, раници
papeleria[1] клон (7, 3) листът Norte
papeleria[1, 4] клон и ден (3,) Norte, петък, трите продукта
papeleria[:, 4] ден (2, 3) петъкът на двата клона
papeleria[0, 1:5] клон (4, 3) Centro от вторник до петък
papeleria[:, :, 2] продукт (2, 7) раниците, по клон и ден
papeleria[0:1] нито една (1, 7, 3) файл само с листа Centro

Трикът, за да предвидиш формата: напиши първоначалната форма, задраскай всяка ос, на която си сложил индекс, и замени с дължината на диапазона всяка ос, на която си сложил срез. За papeleria[0, 1:5]: (2, 7, 3) → задраскай 2 → замени 7 с 4 (от 1 до 4) → (4, 3).

Стъпка 9 — Снимка от 2 × 3 точки

Вторник, 17 ноември, 1:15 през нощта. Стаята на Матео.

Не може да заспи. В телефона си има снимка, която доня Лупе му изпрати следобед: терминалът в Norte, с оплетения му кабел. „За да видиш, че наистина е включен“, написа тя.

Матео си мисли, че и снимката е тензор. И че щом знае как да реже книжарницата, знае как да реже и снимка.

За да го провери, започва с мъничка снимка, създадена ръчно: 2 точки на височина и 3 на ширина. Всяка точка носи три числа, червено, зелено и синьо, от 0 (нищо) до 255 (максимум). Жълтото например е червено и зелено на максимум, без синьо.

Създай файл 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])

Формата има две тройки: първата е броят точки на ширина; втората — цветовете. Без коментара никой не знае коя коя е, дори ти след две седмици. Извади само червения канал, точно както раниците:

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

Горе само червената точка има червено; долу бялата и жълтата имат, черната — не. Тази таблица е черно-бяла снимка на това колко червено има във всяка точка: това е „разделяне на канали“. Една отделна точка, с трите си цвята:

print(foto[1, 2])
tensor([255, 255,   0])

Долу, вдясно: жълтото.

Работи точно както с раниците. Същото двоеточие, същите правила. Матео се усмихва в тъмното. Онова, което научи с книжарницата, става за всяка кутия с числа.

Стъпка 10 — Редът на осите е дизайнерско решение

Но там, в един през нощта, му хрумва неудобен въпрос: кой реши цветът да е последната ос? Той. Запази снимката като височина × ширина × цвят, но никой не те е задължил. Може да се запази и като цвят × височина × ширина: таблицата на червеното, после тази на зеленото, после тази на синьото.

# 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

Същите 18 числа, друга форма: (3, 2, 3). И червеното вече не е foto[:, :, 0], а просто foto_cha[0]. Онова, което поставиш първо, се изважда най-лесно.

Кое е правилното? И двете. Като да подредиш гардероба по вид дрехи или по комплекти, или като начина, по който доня Лупе подрежда склада: по клон или по продукт. Стоката е същата; променя се какво намираш бързо. Затова редът на осите е дизайнерско решение: взимаш го, като мислиш какво ще искаш най-често.

Вече има утвърдени навици. В PyTorch обичайната конвенция за изображения е първо каналите, което в документацията ще видиш като C × H × W (channels, height, width: канали, височина и ширина; документация на TorchVision); партида снимки добавя в началото оста коя е снимката. Други инструменти и много програми за изображения са свикнали с височина × ширина × цвят. Нито едното не е грешно; лошото е да не знаеш кое държиш в ръката си. Затова при снимките коментарът с осите винаги е над тензора.

Ред Форма на снимката ти Червеният канал Една точка с трите ѝ цвята
височина × ширина × цвят (2, 3, 3) foto[:, :, 0] foto[1, 2]
цвят × височина × ширина (3, 2, 3) foto_cha[0] foto_cha[:, 1, 2]

Ако изпълниш foto_cha[:, 1, 2], получаваш tensor([255, 255, 0]): същото жълто.

Матео затваря лаптопа в 2:03. На листчето на екрана още е 200. Вече не му прилича на число. Прилича му на въпрос.


👉 Ако идваш от бизнес администрацията: онова, което Матео направи тази вечер, ти го правиш с електронна таблица, когато филтрираш или скриваш колони: „само петъка на всички клонове“ или „само раниците от всички листове“. Тук всяко от тях е един ред. А стъпка 10 ти служи във всеки отчет: когато ти поискат „продажби по клон и по месец“, попитай какво отива в редовете и какво в колоните. Това е дизайнерско решение и се взима, като се мисли за този, който ще го чете.


Упражнения към сесия 3

Решенията са в края. Работи в своя ejes.py, който вече съдържа papeleria и коментара с осите. В упражнение 12 вече написаните assert ти казват дали си познал; в 10, 11 и 13 сравни работата си с решенията.

Упражнение 10 — Посочи числото, вече със запетаи. С по един индекс за всяка ос и .item() напиши assert за всяка стойност:

а) Колко химикалки е продал Centro в сряда. б) Колко тетрадки е продал Norte в неделя. в) Колко раници е продал Centro в събота. г) Провери с assert, че отговорът ти от а) дава същото с отделните скоби от сесия 1.

Пример за едно решено: assert papeleria[0, 0, 0].item() == 1200 (Centro, понеделник, тетрадки).

Упражнение 11 — Предвиди формата, преди да изпълниш. На хартия напиши формата на всеки израз и какво представлява, като използваш трика от стъпка 8. После го изпълни и сравни.

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)

Ако си сгрешил някой, потърси коя ос си мислил, че остава, а е изчезнала, или обратното.

Упражнение 12 — Попълни празните места. Попълни всяко ____ и провери с 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")

Подсказка за a): неделята е ден 6, а срезът оставя последното си число извън.

Упражнение 13 — Твоята собствена снимка. Създай ръчно снимка от 3 точки на височина и 2 на ширина, запазена в реда, който е обичаен за PyTorch: цвят × височина × ширина (C × H × W). Сам избери цветовете на 6-те точки (червено, зелено, синьо, бяло, черно, жълто или каквито искаш) и ги запиши в коментар. После:

а) Напиши assert за формата ѝ. б) Извади зеления канал. Напиши assert за формата му и за стойността му срещу ръчно написан списък. в) Извади горния ред с трите му цвята. Каква форма има? Напиши двата му assert. г) Извади долната дясна точка с трите ѝ цвята. Напиши двата ѝ assert.

Няма само един отговор; задължително е assert да минават и коментарът да казва какво е всяка ос.


Чести грешки (и как да ги забележиш)

Грешка Как изглежда Как да я забележиш Какво да направиш
Искаш индекс, който не съществува papeleria[1, 7, 2] дава IndexError: index 7 is out of bounds for dimension 1 with size 7 Съобщението ти казва коя ос (dimension 1, дните) и каква е дължината ѝ (size 7) Индексите са от 0 до дължината минус 1: дните — от 0 до 6
Обърнат ред на осите papeleria[4, 1, 2] вместо papeleria[1, 4, 2] дава IndexError: index 4 is out of bounds for dimension 0 with size 2 Хвърля грешка в dimension 0, която има само 2 клона Прочети коментара си с осите: първият индекс винаги е клонът
Верижни скоби с : papeleria[:][4] дава същата грешка за dimension 0 или, още по-лошо, не хвърля грешка и ти дава друго Резултатът не е това, което си поискал, или хвърля грешка в ос, която не си пипал Всички индекси в едни скоби: papeleria[:, 4]
Мислиш, че диапазонът включва последния Искаш от вторник до петък с 1:4 и ти липсва петъкът assert за формата казва 3 дни, а си очаквал 4 Последното число остава извън: от вторник до петък е 1:5
Объркваш индекс със срез papeleria[0:1] има форма (1, 7, 3), а си очаквал (7, 3) assert за формата се проваля с една 1 в повече Индекс: оста изчезва. Срез: оста остава, макар да е с дължина 1
Използваш .item() с няколко числа papeleria[0, 0].item() дава RuntimeError: a Tensor with 3 elements cannot be converted to Scalar Съобщението казва колко елемента е имало (3) .item() е само за едно число; за няколко — .tolist()
Сравняваш част с число в assert assert papeleria[0, 0] == 1200 дава RuntimeError: Boolean value of Tensor with more than one value is ambiguous Грешка хвърля assert, а не индексът Липсвал ти е един индекс (искал си papeleria[0, 0, 0]), или сравни формата и после .tolist() срещу списък

Капан, който не хвърля грешка: papeleria[0, 5:20] не дава грешка, макар да има само 7 дни; дава ти събота и неделя, форма (2, 3), точно както биха направили списъците в Python. Ако един диапазон ти даде по-малко от очакваното, провери числата му.


Преговор за следващата сесия

Сесия 4 започва с тези три въпроса. Отговори на тях, без да поглеждаш урока:

  1. Каква форма има papeleria[:, 4] и какво представлява?
  2. Каква е разликата във формата между papeleria[0] и papeleria[0:1]?
  3. Как изваждаш продажбите на раници на двата клона за всички дни?

🔁 Следващата седмица:

Вторник, 8:10 сутринта. Матео звъни на доня Лупе, преди да влезе в час. Разказва ѝ за петъка. За раниците, които не мърдат.

От другата страна на линията — дълго мълчание. Чува се как ролетката в Centro се вдига.

— Щом раниците не се продават — казва най-накрая доня Лупе, — какво тогава таксуват на хората?

Матео не знае. Но знае как да продължи: да спре да изважда части и да започне да ги обобщава. Суми и средни стойности по ос: колко е продал всеки клон за седмицата, каква е била средната стойност за всеки ден, всичко на един ред и проверено срещу for. Когато сумира седмицата на Norte, числото ще посочи някого.


Решения (не ги гледай, преди да опиташ)

Преговор за начало.

  1. t.shape ти казва колко елемента има по всяка ос: за книжарницата — 2 клона, 7 дни и 3 продукта. Проверява се с assert papeleria.shape == (2, 7, 3).
  2. Защото не е правоъгълен: първият ред има 3 числа, а вторият — 2. PyTorch отказва и го казва: ValueError: expected sequence of length 3 at dim 1 (got 2).
  3. torch.tensor([1, 2, 3]) излиза torch.int64 (цели числа), а torch.tensor([1.0, 2.0]) излиза torch.float32 (десетични).

Упражнение 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

Подточки а) и б) са същите стойности, които търси в подточки а) и в) на упражнение 2 от сесия 1, вече със запетаи.

Упражнение 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) листът Centro; b) неделята на Centro; c) неделята на двата клона; d) тетрадките на двата, за всички дни; e) Norte от понеделник до сряда; f) химикалките на Norte от понеделник до сряда; g) неделята на Centro, като се запазва оста на клона, която е с дължина 1; h) Norte, петък, раници. Подвеждащият е g): прилича на b), но клонът е поискан със срез.

Упражнение 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

В a) 5:7 взема съботата (5) и неделята (6) и оставя извън 7, който така или иначе не съществува.

Упражнение 13. Един възможен отговор:

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

Във в) формата е (3, 2), а не (2, 3): изваждането на част не пренарежда останалите оси, оставя ги в реда, в който са били, а цветът е първи.


Какво следва

В сесия 4 общата сума на Centro ще бъде сумата на papeleria[0]. Ако искаш да напреднеш, изчисли с for колко е продал всеки клон за седмицата. За Centro вече знаеш от сесия 1: $18,500. Остава ти Norte.


Пиша от онова, което виждаме всеки ден в Hábil, където изграждаме системи за банки, застрахователи и компании в Мексико. Ако в работата ти или в семейния ти бизнес се сблъскаш с подобен проблем, нека поговорим: habil.mx

Да обсъдим вашия проект

Предпочитате имейл? Пишете ни на hola@habil.mx