Skip to content

19. Манипуляции с осями тензоров и правила Broadcasting (трансляции) ​

Управление фиктивными размерностями ​

Зачем нужны единичные оси в AI ​

В глубоком обучении и обработке данных фреймворки и функции NumPy требуют строгого соблюдения количества осей (ndim). Например, полносвязный слой ожидает на вход двумерную матрицу батча [Batch, Features]. Если у вас есть один объект в виде одномерного вектора признаков [Features], передать его напрямую нельзя — возникнет ошибка несоответствия размерностей.

Для этого в структуру тензора добавляются фиктивные (единичные) размерности (размерности с длиной 1). Массив формы (5,) превращается в матрицу (1, 5) (батч из одного объекта) или в матрицу (5, 1) (вектор-столбец). Единичные оси не увеличивают количество элементов .size и не расходуют память, но кардинально меняют поведение алгоритмов.

Инструменты добавления и удаления осей ​

Для управления фиктивными размерностями в NumPy используются три основных инструмента, которые работают мгновенно на C-уровне, возвращая представления (Views):

  • numpy.newaxis — специальный маркер, вставляемый внутрь квадратных скобок индексации на место, где необходимо создать новую ось.
  • numpy.expand_dims(arr, axis) — функция модуля, которая вставляет единичную ось в указанную позицию axis.
  • numpy.squeeze(arr) — функция-редуктор, которая находит и полностью удаляет (схлопывает) все единичные размерности из тензора. Например, тензор формы (1, 10, 1) превратится в вектор (10,).
python
import numpy

vector = numpy.array([10, 20, 30])  # Shape: (3,)

# Method 1: Inserting axis using square brackets and newaxis
row_matrix = vector[numpy.newaxis, :]  # Shape: (1, 3)
col_matrix = vector[:, numpy.newaxis]  # Shape: (3, 1)

# Method 2: Expanding dimensions explicitly via function
expanded = numpy.expand_dims(vector, axis=0)  # Shape: (1, 3)

# Removing singleton dimensions
squeezed = numpy.squeeze(row_matrix)  # Back to shape: (3,)

Механизм Broadcasting (Трансляция) ​

Строгие математические правила трансляции ​

Broadcasting (Трансляция) — это уникальный механизм NumPy, который позволяет выполнять арифметические операции между массивами разных размерностей без физического копирования и дублирования данных в памяти. NumPy виртуально «растягивает» меньший массив до размеров большего на аппаратном уровне.

Два массива совместимы для трансляции, если при сравнении их форм .shape справа налево (с конца) для каждой пары размерностей выполняется одно из двух условий:

  1. Они абсолютно равны между собой.
  2. Одна из размерностей равна строго 1.

Если при сопоставлении осей выявляется несовпадение (например, сопоставляются размеры 3 и 2), NumPy выбрасывает ошибку ValueError: operands could not be broadcast together.

text
Пример 1 (Совместимы):
Массив A (2D):  4 x 3
Массив B (1D):      3
Результат:      4 x 3 (Массив B виртуально продублируется по строкам 4 раза)

Пример 2 (Совместимы):
Массив A (2D):  4 x 1
Массив B (2D):  1 x 3
Результат:      4 x 3 (Оба массива растянутся в полноценную матрицу)

Практическое применение в AI: центрирование и добавление bias ​

В реальных задачах машинного обучения трансляция применяется непрерывно:

  • Добавление вектора смещения (bias): К матрице активаций [Batch, Neurons] прибавляется вектор [Neurons]. NumPy автоматически транслирует этот вектор на каждую строку батча.
  • Центрирование признаков: Вычитание вектора средних значений каналов [Channels] из многомерного батча изображений [Batch, Height, Width, Channels].
python
import numpy

# Simulated activations matrix: 3 samples, 2 features each (3x2)
activations = numpy.array([[1.0, 1.0], 
                           [2.0, 2.0], 
                           [3.0, 3.0]])

# Biases vector for 2 features (shape: (2,))
biases = numpy.array([0.1, 0.2])

# Broadcasting in action: biases vector is automatically applied to each row
result = activations + biases
print(result)
# Outputs:
# [[1.1 1.2]
#  [2.1 2.2]
#  [3.1 3.2]]

Упражнения базовые (5 шт) (только с опорой на объясненную в данном уроке теорию) ​

1. Превращение вектора в батч-матрицу ​

Задача: Дан вектор признаков одного объекта sample_vector = numpy.array([1.5, 2.5, 3.5]) (форма (3,)). С помощью маркера numpy.newaxis добавьте фиктивную ось в начало, чтобы превратить его в матрицу-строку формы (1, 3). Выведите полученную форму .shape.

Посмотреть решение
python
import numpy

sample_vector = numpy.array([1.5, 2.5, 3.5])

# Expanding the leading axis to simulate a batch of size 1
batch_matrix = sample_vector[numpy.newaxis, :]

print("New tensor shape layout:", batch_matrix.shape)  # Outputs: (1, 3)

2. Расширение оси по заданному индексу ​

Задача: Создайте матрицу размером 2x2, заполненную единицами. Используя функцию numpy.expand_dims(), вставьте новую фиктивную ось в самый конец (параметр axis=2). Выведите форму получившегося 3D-тензора.

Посмотреть решение
python
import numpy

base_matrix = numpy.ones((2, 2))

# Inserting a trailing singleton dimension
expanded_3d = numpy.expand_dims(base_matrix, axis=2)

print("Expanded 3D shape:", expanded_3d.shape)  # Outputs: (2, 2, 1)

3. Схлопывание фиктивных осей тензора ​

Задача: Дан тензор предсказаний формы (1, 5, 1): predictions = numpy.zeros((1, 5, 1)). С помощью функции numpy.squeeze() удалите все единичные размерности, вернув массив к плоскому одномерному вектору. Выведите форму результата.

Посмотреть решение
python
import numpy

predictions = numpy.zeros((1, 5, 1))

# Dropping all empty axes simultaneously
flat_vector = numpy.squeeze(predictions)

print("Squeezed vector shape layout:", flat_vector.shape)  # Outputs: (5,)

4. Траснляция вектора на матрицу по столбцам ​

Задача: Дана матрица matrix_data = numpy.zeros((3, 2)). Дан одномерный вектор коэффициентов multipliers = numpy.array([2.0, 3.0]). Прибавьте вектор к матрице с помощью векторизованного механизма Broadcasting и выведите результат.

Посмотреть решение
python
import numpy

matrix_data = numpy.zeros((3, 2))
multipliers = numpy.array([2.0, 3.0])

# Vector automatically aligns to columns because rightmost dimensions match: (3, 2) and (2,)
broadcasted_sum = matrix_data + multipliers
print(broadcasted_sum)
# Outputs:
# [[2. 3.]
#  [2. 3.]
#  [2. 3.]]

5. Построение внешней сетки умножения (Outer Product) ​

Задача: Даны два одномерных вектора: vector_A = numpy.array([1, 2, 3]) (длина 3) и vector_B = numpy.array([10, 20]) (длина 2). Превратите vector_A в вектор-столбец формы (3, 1), а vector_B оставьте без изменений. Перемножьте их через оператор * и посмотрите, как Broadcasting растянет их в полноценную таблицу умножения размером 3x2. Выведите результат.

Посмотреть решение
python
import numpy

vector_A = numpy.array([1, 2, 3])
vector_B = numpy.array([10, 20])

# Shaping components to force grid broadcasting cross-multiplication: (3, 1) * (2,)
column_A = vector_A[:, numpy.newaxis]
outer_grid = column_A * vector_B

print(outer_grid)
# Outputs:
# [[10 20]
#  [20 40]
#  [30 60]]

Упражнения смешанные (5 шт) (включают материал прошлых уроков) ​

6. Решейп и добавление осей для сгенерированной числовой сетки ​

Задача: Сгенерируйте линейную числовую последовательность из 4 элементов от 0.5 до 3.5 через linspace (Урок 3). Измените её форму на матрицу-столбец размерностью (4, 1) с помощью метода .reshape() (Урок 18). Используя инструмент numpy.expand_dims(), добавьте фиктивную ось в начало структуры (Урок 19), превратив массив в 3D-тензор. Выведите итоговый кортеж формы .shape и размерность .ndim (Урок 11 / Урок 16).

Посмотреть решение
python
import numpy

# Step 1: Linear sequence generation (Lesson 3)
flat_grid = numpy.linspace(0.5, 3.5, 4)

# Step 2: Reshaping to a vertical column array layout (Lesson 18)
column_matrix = flat_grid.reshape(4, 1)

# Step 3: Expanding dimensions to create a 3D block tensor (Lesson 11, 16 & 19)
tensor_3d = numpy.expand_dims(column_matrix, axis=0)

print("Final tensor metrics shape configuration:", tensor_3d.shape)  # Outputs: (1, 4, 1)
print("Dimensions count:", tensor_3d.ndim)  # Outputs: 3

7. Центрирование батча признаков с помощью Broadcasting ​

Задача: Создайте матрицу признаков датасета X_raw = numpy.array([[10.0, 3.0], [20.0, 6.0], [30.0, 3.0]], dtype=numpy.float64) (Урок 11). Рассчитайте вектор средних значений по каждому столбцу с помощью метода .mean(axis=0) (Урок 15). С помощью механизма Broadcasting вычтите полученный вектор средних из исходной матрицы X_raw одной векторизованной строкой (Урок 19). Выведите полученную центрированную матрицу.

Посмотреть решение
python
import numpy

X_raw = numpy.array([[10.0, 3.0], [20.0, 6.0], [30.0, 3.0]], dtype=numpy.float64)

# Step 1: Compute columns features mean vector (Lesson 15)
column_means = X_raw.mean(axis=0)  # Yields: [20., 4.0]

# Step 2: Element-wise subtraction leveraging implicit broadcasting rules (Lesson 19)
# A (3x2) matrix minus a B (2,) vector behaves as (3x2) - (3x2) under the hood
centered_X = X_raw - column_means
print("Centered Features Matrix:\n", centered_X)
# Outputs:
# [[-10.  -1.0]
#  [  0.   2.0]
#  [ 10.  -1.0]]

8. Инициализация случайных смещений батча и in-place сложение ​

Задача: Сгенерируйте детерминированную матрицу активаций нейросети размером (3, 2) из одних единиц с помощью numpy.ones (Урок 11). Используя современный Generator API (rng.normal), сгенерируйте случайный вектор bias смещений длины 2 (Урок 17). Прибавьте полученный вектор смещений к матрице активаций на месте с помощью in-place оператора += для жесткой экономии RAM (Урок 8). Объясните в уме, как сработал тандем in-place мутации памяти и Broadcasting (Урок 19).

Посмотреть решение
python
import numpy

rng = numpy.random.default_rng(seed=42)

# Building execution layout components (Lesson 11 & Lesson 17)
activations_matrix = numpy.ones((3, 2))
bias_vector = rng.normal(loc=0.0, scale=0.1, size=2)

# Running in-place accumulation combined with implicit broadcasting expansion (Lesson 8 & Lesson 19)
activations_matrix += bias_vector

print("Mutated activations layout buffer:\n", activations_matrix)

9. Сплющивание многомерного канала батча с удалением фиктивных осей ​

Задача: Дан 4D-тензор батча медицинских снимков: medical_batch = numpy.ones((1, 4, 4, 1), dtype=numpy.float32) (Урок 4 / Урок 16). С помощью функции numpy.squeeze() избавьтесь от всех фиктивных осей в начале и в конце структуры (Урок 19). Полученную плоскую матрицу размером (4, 4) разверните в 1D-вектор с помощью метода .ravel() без выделения лишней памяти (Урок 18). Выведите форму результирующего вектора признаков.

Посмотреть решение
python
import numpy

# Allocating single-sample single-channel 4D block tensor (Lesson 4 & Lesson 16)
medical_batch = numpy.ones((1, 4, 4, 1), dtype=numpy.float32)

# Step 1: Collapsing singleton structural tracks (Lesson 19)
clean_matrix_2d = numpy.squeeze(medical_batch)  # Shapes down from (1, 4, 4, 1) to (4, 4)

# Step 2: High-speed flattening view configuration (Lesson 18)
flat_features_vector = clean_matrix_2d.ravel()

print("Final feature vector shape format:", flat_features_vector.shape)  # Outputs: (16,)

10. Прямой проход слоя (XW + b) с многомерным Broadcasting и ReLU ​

Задача: Дан батч признаков X = numpy.array([[2.0, 4.0], [1.0, 3.0]]) и веса W = numpy.array([[0.5, -1.0], [1.0, 0.5]]) (Урок 11). Вычислите матричное произведение Y = X @ W (Урок 14). Добавьте к результату вектор смещений bias = numpy.array([-1.0, -2.0]) с помощью механизма трансляции (Урок 19). Примените к полученным сигналам функцию активации ReLU, обнулив все отрицательные элементы на месте по булевой маске (Урок 10). Выведите итоговые активированные выходы.

Посмотреть решение
python
import numpy

X = numpy.array([[2.0, 4.0], [1.0, 3.0]])
W = numpy.array([[0.5, -1.0], [1.0, 0.5]])
bias = numpy.array([-1.0, -2.0])

# Step 1: True matrix multiplication (Lesson 14)
logits_matrix = X @ W  # Yields matrix: [[5.0, 0.0], [3.5, 0.5]]

# Step 2: Adding biases vector via Broadcasting rules (Lesson 19)
logits_matrix += bias  # Yields matrix: [[4.0, -2.0], [2.5, -1.5]]

# Step 3: Vectorized ReLU activation screening (Lesson 10)
logits_matrix[logits_matrix < 0] = 0.0

print("Final output activations block:\n", logits_matrix)
# Outputs:
# [[4.  0. ]
#  [2.5 0. ]]