Съдържание на курса
Тензори от нулата (3): осите имат имена
От Dorian Chávez · основател и главен изпълнителен директор на 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). Днес научаваш онова, което Матео направи тази вечер: да изважда части от него. Не само едно число, а цял ден, цял продукт или изрязана седмица, по оста, която ти избереш.
Преговор за начало
Отговори, без да поглеждаш предишната сесия. Отговорите са в края, в решенията.
- Какво ти казва
t.shapeи как го проверяваш сassert? - Защо
torch.tensor([[1, 2, 3], [4, 5]])се проваля? - Какъв
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? Нали искаше деня!
Отделните скоби работят на стъпки и всяка стъпка започва отново от първата ос на онова, което е останало от предишната стъпка, а не от оста, която си имал наум:
papeleria[:]е „всички клонове“: връща ти целия тензор, абсолютно същия.[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 започва с тези три въпроса. Отговори на тях, без да поглеждаш урока:
- Каква форма има
papeleria[:, 4]и какво представлява? - Каква е разликата във формата между
papeleria[0]иpapeleria[0:1]? - Как изваждаш продажбите на раници на двата клона за всички дни?
🔁 Следващата седмица:
Вторник, 8:10 сутринта. Матео звъни на доня Лупе, преди да влезе в час. Разказва ѝ за петъка. За раниците, които не мърдат.
От другата страна на линията — дълго мълчание. Чува се как ролетката в Centro се вдига.
— Щом раниците не се продават — казва най-накрая доня Лупе, — какво тогава таксуват на хората?
Матео не знае. Но знае как да продължи: да спре да изважда части и да започне да ги обобщава. Суми и средни стойности по ос: колко е продал всеки клон за седмицата, каква е била средната стойност за всеки ден, всичко на един ред и проверено срещу for. Когато сумира седмицата на Norte, числото ще посочи някого.
Решения (не ги гледай, преди да опиташ)
Преговор за начало.
t.shapeти казва колко елемента има по всяка ос: за книжарницата — 2 клона, 7 дни и 3 продукта. Проверява се сassert papeleria.shape == (2, 7, 3).- Защото не е правоъгълен: първият ред има 3 числа, а вторият — 2. PyTorch отказва и го казва:
ValueError: expected sequence of length 3 at dim 1 (got 2). 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