Шаг 42.
Глубокое обучение на Python. Математические основы нейронных сетей. Шестеренки нейронных сетей: операции с тензорами. Расширение

    На этом шаге мы рассмотрим суть и примеры использования механизма расширения.

    Наша предыдущая реализация naive_add() поддерживает только сложение тензоров второго ранга с идентичными формами. Но в слое Dense, представленном на предыдущих шагах, мы складывали двумерный тензор с вектором. Что происходит при сложении, когда формы складываемых тензоров отличаются?

    Когда это возможно и не вызывает неоднозначности, меньший тензор расширяется так, чтобы его новая форма соответствовала форме большего тензора. Расширение выполняется в два этапа. 1 2

  1. В меньший тензор добавляются оси (так называемые оси расширения), чтобы значение его атрибута ndim соответствовало значению этого же атрибута большего тензора.
  2. Меньший тензор копируется в новые оси до полного совпадения с формой большего тензора.

    Рассмотрим конкретный пример. Допустим, у нас имеются тензоры X с формой (32, 10) и у с формой (10,):

import numpy as np

X = np.random.random((32, 10))  # X — матрица случайных чисел с формой (32, 10)
y = np.random.random((10,))  # y — вектор случайных чисел с формой (10,)

    Сначала нужно добавить первую пустую ось в вектор y, чтобы привести его форму к виду (1, 10):

y = np.expand_dims(y, axis=0)  # Теперь y имеет форму (1, 10)

    Затем скопируем y по этой новой оси 32 раза, чтобы в результате получился тензор Y с формой (32, 10), где Y[i, :] == y для i в диапазоне range(0, 32).

# Скопировать y 32 раза вдоль оси 0, чтобы получить Y с формой (32, 10)
Y = np.concatenate([y] * 32, axis=0)

    После этого можно сложить X и Y, которые имеют одинаковую форму.

    В фактической реализации новый двумерный тензор, конечно же, не создается, потому что это было бы неэффективно. Операция копирования выполняется чисто виртуально: она происходит на алгоритмическом уровне, а не в памяти. Но такое представление с копированием вектора для новой оси является полезной мысленной моделью. Вот как могла бы выглядеть наивная реализация:

def naive_add_matrix_and_vector(x, y):
    assert len(x.shape) == 2.  # Убедиться что x — двумерный тензор NumPy
    assert len(y.shape) == 1.  # Убедиться что y — вектор NumPy
    assert x.shape[1] == y.shape[0]

    x = x.copy()  # Исключить перезапись исходного тензора
    for i in range(x.shape[0]):
        for j in range(x.shape[1]):
            x[i, j] += y[j]
    return x

    Прием расширения в общем случае можно использовать в поэлементных операциях с двумя тензорами, если один тензор имеет форму (a, b, ... n, n + 1, ... m), а другой - форму (n, n + 1, ... m). В этом случае при расширении будут добавлены оси до n - 1.

    Следующий пример показывает применение поэлементной операции maximum к двум тензорам с разными формами посредством расширения:

import numpy as np

x = np.random.random((64, 3, 32, 10))  # x — тензор случайных чисел, 
                                       # имеющий форму (64, 3, 32, 10)
y = np.random.random((32, 10))  # — тензор случайных чисел, имеющий форму (32, 10)
z = np.maximum(x, y)  # Получившийся тензор z имеет форму (64, 3, 32, 10) аналогично x

    На следующем шаге мы рассмотрим скалярное произведение тензоров.




Предыдущий шаг Содержание Следующий шаг