Почему линейная алгебра обязательна для работы с нейронными сетями
Нейронные сети оперируют огромными массивами чисел: входными признаками, весовыми коэффициентами, градиентами. Все эти структуры удобно описывать в виде векторов и матриц, а операции над ними – элементарными линейно‑алгебраическими преобразованиями. Понимание того, как работает вектор, позволяет точно интерпретировать процесс обучения, отлаживать модели и эффективно использовать библиотеки вроде NumPy, PyTorch или TensorFlow. Даже простейшее дообучение или изменение архитектуры требует уверенного владения базовыми понятиями: скалярным умножением, нормой, скалярным и векторным произведением.
Понятие вектора и его роль в данных
Вектор – упорядоченный набор чисел, представляющих точку в многомерном пространстве. В контексте машинного обучения вектор часто трактуется как признаковый набор одного объекта. Например, изображение 28 × 28 пикселей можно «развернуть» в вектор длиной 784 = 28·28, где каждый элемент – интенсивность соответствующего пикселя. Такой подход упрощает передачу данных в слои сети: вместо двумерных матриц используется один‑мерный массив, а последующие операции работают с ним напрямую.
Векторизация данных: от таблиц к массивам
Векторизация – процесс преобразования табличных или иерархических данных в числовые векторы. Ключевые шаги:
- Нормализация: приведение всех признаков к единой шкале (например, min‑max или z‑score).
- Кодирование категориальных переменных: one‑hot, label‑encoding или embedding‑подходы.
- Сборка в единый массив: объединение всех признаков в один длинный вектор.
В результате каждый объект представляется одинаковой длиной, что позволяет пакетно передавать их в GPU‑ускоряемые вычисления.
Умножение на скаляр: масштабирование признаков
Умножение вектора v = (v₁, v₂, …, vₙ) на скаляр α приводит к новому вектору α·v = (α·v₁, α·v₂, …, α·vₙ). В нейросетях это часто используется для:
- Регулировки градиентов (learning rate).
- Инициализации весов: случайные скаляры масштабируют начальные значения, избегая слишком больших или маленьких чисел.
Пример в Python (NumPy):
import numpy as np
v = np.array([1.2, -0.7, 3.4])
alpha = 0.5
scaled = alpha * v # результат: [0.6, -0.35, 1.7]
Сложение векторов: объединение признаков
Сложение происходит покомпонентно: u + v = (u₁+v₁, u₂+v₂, …, uₙ+vₙ). В нейросетях складывают:
- Входные сигналы и смещения (bias).
- Градиенты разных мини‑батчей перед обновлением параметров.
u = np.array([0.5, 1.0, -0.2])
v = np.array([1.2, -0.7, 3.4])
result = u + v # [1.7, 0.3, 3.2]
Норма вектора: измерение «длины»
Норма (или длина) ‖v‖ часто берётся как евклидова (L2) норма:
[ ‖v‖_2 = \sqrt{v_1^2 + v_2^2 + \dots + v_n^2} ]
В нейросетях нормы применяются для:
- Регуляризации (L2‑регуляризация, weight decay).
- Нормализации градиентов (gradient clipping).
norm = np.linalg.norm(v) # sqrt(1.2**2 + (-0.7)**2 + 3.4**2)
Скалярное (dot) произведение: измерение сходства
Скалярное произведение двух векторов u·v = Σ uᵢ·vᵢ дает одно число, отражающее их угол и длину. В нейросетях это базовый элемент:
- Взвешивание входов: каждый нейрон вычисляет w·x + b, где w – вектор весов, x – входной вектор.
- Косинусное сходство в рекомендационных системах.
dot = np.dot(u, v) # 0.5*1.2 + 1.0*(-0.7) + (-0.2)*3.4
Векторное (cross) произведение: ориентация в трёхмерном пространстве
Векторное произведение определено только для трехмерных векторов a, b и даёт вектор, перпендикулярный обоим исходным:
[ a \times b = (a_2 b_3 - a_3 b_2,; a_3 b_1 - a_1 b_3,; a_1 b_2 - a_2 b_1) ]
Хотя в типовых задачах машинного обучения кросс‑произведение используется редко, оно полезно в компьютерном зрении (например, для вычисления нормалей к плоскостям) и в графических движках, где нейросети генерируют 3D‑модели.
a = np.array([1, 0, 0])
b = np.array([0, 1, 0])
cross = np.cross(a, b) # [0, 0, 1]
Практика: реализуем элементарный слой «полносвязный» на Python
Ниже – минимальная реализация линейного слоя без сторонних библиотек. Код демонстрирует работу со всеми рассмотренными операциями.
import numpy as np
class LinearLayer:
def __init__(self, in_features, out_features, lr=0.01):
# Инициализация весов небольшими случайными числами
self.W = np.random.randn(out_features, in_features) * 0.01
self.b = np.zeros(out_features)
self.lr = lr
def forward(self, x):
# x – вектор входа (batch_size, in_features)
# y = W·x^T + b
return np.dot(x, self.W.T) + self.b
def backward(self, x, grad_output):
# grad_output – ∂L/∂y
grad_W = np.dot(grad_output.T, x) / x.shape[0]
grad_b = grad_output.mean(axis=0)
# Обновление параметров
self.W -= self.lr * grad_W
self.b -= self.lr * grad_b
# Возврат градиента по входу
return np.dot(grad_output, self.W)
# Пример использования
layer = LinearLayer(in_features=4, out_features=2, lr=0.05)
x_batch = np.random.rand(3, 4) # 3 примера, 4 признака каждый
y = layer.forward(x_batch) # прямой проход
grad = np.random.rand(3, 2) # условный градиент от следующего слоя
layer.backward(x_batch, grad) # обратный проход и обновление
Домашнее задание: закрепляем материал
- Векторизация: возьмите любой набор табличных данных (например, Iris) и преобразуйте его в векторы фиксированной длины. Примените нормализацию и one‑hot кодирование, если требуется.
- Реализуйте функцию
vector_norm(v, p=2), вычисляющую Lₚ‑норму для произвольногоp. Сравните результаты для p = 1, 2, ∞. - Скалярное и векторное произведения: напишите функции
dot_product(a, b)иcross_product(a, b). Проверьте их свойства (коммутативность скалярного продукта, антикоммутативность кросс‑продукта). - Создайте простой линейный слой без использования NumPy (используйте обычные списки и циклы). Сравните его результаты с реализацией из предыдущего блока.
Выполнение этих задач обеспечит уверенное владение фундаментальными операциями линейной алгебры, необходимыми для построения и отладки современных нейронных сетей.