Зачем NumPy · ndarray и dtype · создание массивов ·
векторизация vs циклы · Jupyter
2 ч теории. Далее — live-coding и семинар.
Почему вообще нужен ещё один тип данных? — прежде чем мы
начнём, ответьте себе на этот вопрос.
Все downstream-курсы начинаются с import numpy as np
Анализ данных, Математическая статистика, ML, ИИ, NLP, Методики вычислений —
все начинают с import numpy as np. На этих курсах NumPy уже
«предполагается»: его не проходят заново.
Реальные данные — это массивы: пиксели ч/б фото — матрица (H, W);
признаки объектов — таблица (n, m); временной ряд — вектор (T,).
Без векторизации не справиться с объёмами: 10⁷ чисел циклом — секунды,
векторизованно — миллисекунды (вернёмся на слайде 18).
Навык «векторного мышления» переносится на Pandas, SciPy, scikit-learn.
Какого типа элементы матрицы яркости чёрно-белого фото?
Не открывая конспект: что вернёт [1, 2, 3] + [4, 5, 6]?
Сколько в нём элементов?
А как посчитать поэлементную сумму без NumPy? Сколько строк
кода это займёт?
К концу лекции вы сможете…
объяснять, чем ndarray отличается от списка;
создавать массивы любых форм и dtype (8 конструкторов);
менять форму (reshape, flatten, .T, -1);
выполнять поэлементные операции и скалярное произведение (dot/@);
измерять ускорение векторизации через %timeit и объяснять его причину;
работать с ячейками Jupyter и магией %timeit.
В конце проверим себя списком вопросов — это не экзамен,
а сигнал, где повторить.
Список хранит ссылки на объекты — цикл платит за каждый элемент
список хранит ссылки на объекты, а не числа: каждый элемент —
отдельный int в куче (unboxing);
элементы разбросаны по памяти — кеш процессора не работает;
операция идёт через интерпретатор на каждый элемент.
a = [1, 2, 3]
b = [4, 5, 6]
s = 0
for i inrange(3):
s += a[i] * b[i] # 5 строк, медленно
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
np.dot(a, b) # → 32 ✓
NumPy переносит цикл из Python в C.
Что здесь платит за каждый шаг — и куда NumPy передаёт
оба массива целиком?
ndarray — однородный массив; shape/ndim/size/dtype его описывают
ndarray — N-мерный однородный массив: все элементы одного типа
(dtype), лежат подряд в одном блоке памяти, операции — на C.
Сколько способов создать массив вы сегодня знаете? Какие
атрибуты читали у каждого?
Стоп-вопрос · Блок 2
Какая разница между arange и linspace? Приведите
пример, когда arange подведёт.
Запомните формулу: «N точек → linspace».
Сначала почувствуй: один расчёт — 1.6 c и 0.06 c
x = np.arange(10**7)
s = 0.0
for i inrange(len(x)):
s += x[i]**2
s # ≈ 1.6 c ✓
x = np.arange(10**7)
s = np.sum(x**2)
s # ≈ 0.06 c ✓
Тот же результат. Ускорение десятки раз, код короче. На семинаре
измерите сами: типично 20–30× на 10⁶ (10⁵ ≈ 12×, 10⁶ ≈ 30×, 10⁷ ≈ 26×).
Что здесь быстрее и почему, как думаете?
Разрыв растёт с N: векторизация выигрывает тем сильнее, чем больше массив
Замер %timeit, Python 3.11 / NumPy 1.26.
На 10⁷: цикл ≈ 1.57 c, векторизация ≈ 61 мс.
Почему на малых N линии почти совпадают, а с ростом N
расходятся? (накладные расходы вызова C сопоставимы с выигрышем).
Векторизация — операция над всем массивом сразу, без явного for
Векторизация — применение операции ко всем элементам массива
сразу, без явного цикла for. NumPy передаёт данные C-функции
целиком; цикл же платит интерпретатору за каждый элемент.
Правило-шпаргалка: видишь for по элементам
массива в научном стеке — ищи векторную альтернативу.
Честно о сложности: первая встреча с векторизацией может
казаться «магией» — это нормально. Сегодня мы её разобрали на части: память,
dtype, C-операции. Через неделю это станет рутиной.
Как проверить, с чем вы работаете: списком или массивом?
Операции поэлементны и возвращают НОВЫЙ массив (не меняют исходный)
Для векторов @ и np.dot — одно и то же
(скалярное произведение). Для матриц это матричное умножение — неделя 3.
@ — современный оператор, запомните его.
Мы вернулись к слайду 5: что мы писали 5 строками цикла — теперь
одна строка и 32. В чём разница dot и @ для векторов?
Live-coding: векторизация vs цикл + %timeit на 10⁶
Замерьте %timeit для цикла и для np.sum(x**2 + 2*x + 1);
посчитайте отношение. Результаты должны совпасть — проверьте np.allclose.
x = np.arange(10**6)
%timeit np.sum(x**2 + 2*x + 1)
# быстрее цикла в 10–30 раз (эталон: 10⁵ ≈ 12×, 10⁶ ≈ 30×)
# np.allclose(s_loop, s_vec) → True
Почему нельзя включать np.arange(...) внутрь таймера?
Jupyter: Shift+Enter выполняет ячейку; %timeit измеряет время
ячейки code/markdown; Shift+Enter — выполнить и перейти;
Ctrl+Enter — выполнить и остаться;
выводится результат последнего выражения.
%%timeit
np.sum(np.arange(10**6)**2)
1.25 ms ± 0.06 ms per loop
%timeit np.sum(np.arange(10**6)**2) — замер одной
строки. Другие магии: %matplotlib inline, %whos
(переменные), %debug (отладка после ошибки).
Чем %%timeit отличается от %timeit?
x**2 на списке → TypeError: ты думал — массив, а это список
x = [0, 1, 2, 3, 4]
y = x**2 + 2*x + 1
TypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int'
Последняя строка traceback — причина:** не умеет
работать с list. Исправление: x = np.array([0, 1, 2, 3, 4]).
Что говорит последняя строка traceback? Где ошибка?
Пять ошибок недели 1, которые вы совершите на семинаре
Список вместо массива:a = [1, 2, 3], затем
a + a — конкатенация, а не поэлементное сложение (слайд 9).
Забыли np.:zeros(3) → NameError (слайд 16).
Неверный dtype: деление целых без перевода в float (слайд 8).
Мутация ожидается, а её нет: операции создают новый массив,
исходный не меняется (кроме +=, *=).
Студент написал a + a и получил список длиной 6.
Что он забыл?
Проверка понимания · перед семинаром
Чем ndarray принципиально отличается от Python-списка?
Что вернёт np.linspace(0, 1, 5) и какой у него shape?
Почему x**2 на массиве из 10⁷ элементов быстрее цикла?
Какая разница между arange и linspace?
Ответьте письменно за 2 минуты — это сигнал, что повторить
перед семинаром, а не контроль.
Векторизация и dot — кирпичи линейной регрессии (проект модуля)
Проект модуля: реализовать линейную регрессию «вручную» на NumPy и сравнить
с scikit-learn.
Сегодняшние кирпичи: матрицы признаков X (n, m) (фабрики и reshape);
скалярное произведение @ (матричные формулы — неделя 3);
векторизация (формулы без циклов).
На 3-й неделе вы соберёте из сегодняшних кирпичей первый ML-алгоритм —
первый шаг к курсу ML.
Какой приём сегодняшней лекции пригодится для матричных
формул регрессии?
Сегодня вы научились…
объяснять ndarray и его «паспорт» (shape/ndim/size/dtype);
создавать массивы 8 способами и преобразовывать форму;
применять поэлементные операции и dot/@;
измерять векторизацию через %timeit и объяснять её схемой памяти;
читать traceback снизу вверх.
Два правила на память: «N точек → linspace»;
«операция возвращает новый массив».
Какие два правила нужно запомнить на неделю?
One-minute paper: главное + один вопрос
Запишите одним-двумя предложениями: (1) самое главное, что вы сегодня поняли;
(2) один вопрос, который остался.
Соберём 2–3 ответа вслух или анонимно — на оставшиеся
вопросы отвечу на семинаре.
Что дальше: семинар, ДЗ, анонс недели 2
Семинар недели 1: три уровня — «Первая ячейка + создание
массивов» (базовый), «Векторизация vs цикл + базовые операции» (стандартный),
«Transform + Challenge: порог ускорения» (продвинутый). Сдаётся ноутбуком
w01/seminar-01.ipynb, ветка seminar/w01.
ДЗ недели 1: 10 задач на создание и преобразование массивов
(autograder, task-01/solution.py).
Анонс недели 2: «Индексация, broadcasting, ufuncs» — выборка
данных по индексам и условиям, «растягивание» форм. Broadcasting — самая
сложная тема модуля, но и самая ценная для ML.
Готовы? Откройте семинар-1.md и выполните задачу базового уровня.