NumPyMiddleCoding

Как работает 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-пайплайном.

Sources

Related topics