Как работает np.pad() и какие режимы дополнения доступны?
np.pad(arr, pad_width, mode) расширяет массив по краям осей; основные режимы: constant (нули/значение), edge (повтор крайнего), reflect (зеркало без края), symmetric (зеркало с краем), wrap (периодика), linear_ramp, mean/min/max/median.
np.pad(): дополнение массивов
np.pad(array, pad_width, mode, **kwargs) расширяет массив, добавляя элементы по краям каждой оси. Это необходимо в обработке сигналов, компьютерном зрении (padding свёрток), NLP (выравнивание последовательностей) и численных методах (граничные условия).
Параметры
array— исходный ndarray любой размерности.pad_width— ширина дополнения. Может быть: целым числом (со всех сторон), кортежем(before, after)(для всех осей одинаково), списком кортежей[(b0, a0), (b1, a1), ...](для каждой оси отдельно).mode— строка или callable, задающая правило заполнения.
Режимы (mode)
constant (по умолчанию)
Заполняет константой (по умолчанию 0). Параметр constant_values.
edge
Дублирует крайние элементы. Полезно для изображений, где нет «нулевого фона».
reflect
Отражение без повтора граничного элемента: [1,2,3,4] с pad=2 даёт [3,2,1,2,3,4,3,2]. Используется в wavelet-преобразованиях.
symmetric
Отражение с повтором граничного элемента: [1,2,3,4] с pad=2 даёт [2,1,1,2,3,4,4,3].
wrap
Периодическое дополнение — элементы с противоположного конца. Для FFT и circular convolution.
linear_ramp
Линейно интерполирует от крайнего значения до end_values.
maximum / minimum / mean / median
Заполняет статистикой из окна stat_length элементов у края.
empty
Выделяет память без инициализации (неопределённые значения). Быстрее для случаев, когда значения будут перезаписаны.
import numpy as np
arr = np.array([10, 20, 30, 40, 50])
# constant (zeros по умолчанию)
print(np.pad(arr, 2, mode='constant'))
# [ 0 0 10 20 30 40 50 0 0]
# constant с нужным значением
print(np.pad(arr, 2, mode='constant', constant_values=99))
# [99 99 10 20 30 40 50 99 99]
# edge
print(np.pad(arr, 2, mode='edge'))
# [10 10 10 20 30 40 50 50 50]
# reflect
print(np.pad(arr, 2, mode='reflect'))
# [30 20 10 20 30 40 50 40 30]
# symmetric
print(np.pad(arr, 2, mode='symmetric'))
# [20 10 10 20 30 40 50 50 40]
# wrap
print(np.pad(arr, 2, mode='wrap'))
# [40 50 10 20 30 40 50 10 20]
# linear_ramp
print(np.pad(arr, 3, mode='linear_ramp', end_values=(0, 100)))
# [ 0 3 6 10 20 30 40 50 67 83 100]
# mean с stat_length=2 (среднее по 2 крайним элементам)
print(np.pad(arr, 2, mode='mean', stat_length=2))
# [15 15 10 20 30 40 50 45 45]
# 2D пример (изображение H x W)
img = np.ones((4, 6), dtype=np.float32)
# Разные padding по осям:
padded = np.pad(img, pad_width=[(1, 1), (2, 2)], mode='constant')
print(padded.shape) # (6, 10)
# Для свёрточных нейросетей (same padding для kernel 3x3):
def same_pad(image, kernel_size=3):
p = kernel_size // 2
return np.pad(image, [(p, p), (p, p)], mode='constant')
padded_img = same_pad(img)
print(padded_img.shape) # (6, 8)
# Callable mode (custom logic):
def my_pad(vector, iaxis_pad_width, iaxis, kwargs):
# vector -- 1D срез оси с уже выделенными краями
# iaxis_pad_width -- (before, after) для этой оси
# Заполним квадратом крайнего элемента:
pad_start = iaxis_pad_width[0]
pad_end = iaxis_pad_width[1]
vector[:pad_start] = vector[pad_start] ** 2
if pad_end > 0:
vector[-pad_end:] = vector[-pad_end - 1] ** 2
arr2d = np.array([[1, 2, 3], [4, 5, 6]], dtype=float)
print(np.pad(arr2d, 1, mode=my_pad))
Применение в ML-пайплайнах
При zero-padding свёрток (mode='constant') выходной размер сохраняется равным входному при kernel_size=3, pad=1. Edge-padding лучше для краёв изображений, где ноль создаёт артефакты. Reflect-padding популярен в StyleGAN и super-resolution сетях.
Подводные камни
- np.pad всегда создаёт новый массив (копию). Для больших тензоров это значительные выделения памяти — помните об этом в hot-path.
- Режим reflect требует, чтобы pad_width был меньше размера оси. Для оси длиной 3, reflect допускает pad максимум 2. Нарушение — ValueError.
- symmetric аналогично: pad_width <= размер оси (не размер - 1, как у reflect).
- constant_values применяется только к режиму constant. Передача в другой режим — молчаливое игнорирование.
- Callable mode вызывается отдельно для каждой оси каждого среза. Производительность может быть плохой для больших массивов.
- pad_width как целое число — это суммарный pad (по before И after). Ошибка думать, что
pad(arr, 2)добавляет 2 с каждой стороны и 4 суммарно — именно 2 с каждой стороны, итого +4 по оси. - mode='empty' не инициализирует память — использование значений без записи даёт undefined behavior (мусор из предыдущих аллокаций).
Common mistakes
- Объяснять
pad modesтолько синтаксисом без shape, dtype, состояния или режима выполнения. - Игнорировать leakage, воспроизводимость, пустые входы и скрытые копии данных.
- Не проверять production-симптомы: latency, память, ретраи, дрейф качества и несовпадение версий.
What the interviewer is testing
- Может ли связать
pad modesс реальным контрактом входов и выходов. - Упоминает ли тесты, метрики, reproducibility и диагностику ошибок.
- Видит ли различие между demo-кодом в ноутбуке и production-пайплайном.