Можно ли использовать функции из numpy для умножения матриц в Python
Что проверяют
Знаете ли вы разницу между матричным и поэлементным умножением и между функциями NumPy, которые их выполняют. Следом обычно спрашивают, почему NumPy быстрее цикла и что происходит с массивами больше двух измерений.
Как отвечать
Да, и писать умножение циклами на Python в рабочем коде не нужно. Основные варианты:
@иnp.matmul: матричное умножение. Для массивов с числом измерений больше двух работает как пачка матриц с бродкастингом по первым осям, это стандарт для батчей;np.dot: для двумерных массивов даёт то же самое, но на N-мерных умножает последнюю ось первого на предпоследнюю второго и принимает скаляры, чегоmatmulне делает;- *``**: поэлементное произведение, не матричное;
np.einsum: явная запись индексов, удобна для нестандартных свёрток вроде'bij,bjk->bik';np.linalg.multi_dot: цепочка из нескольких матриц, функция сама выбирает порядок умножения с минимумом операций.
Скорость берётся из BLAS: для float32 и float64 NumPy вызывает OpenBLAS или MKL, которые используют векторные инструкции и несколько потоков. Какая библиотека подключена, показывает np.show_config(). Целочисленные матрицы в BLAS не уходят и умножаются заметно медленнее.
По размерам: (m, k) @ (k, n) даёт (m, n), при несовпадении внутренних измерений будет ValueError. Одномерный массив matmul считает вектором, и у результата на одно измерение меньше.
Для больших задач упомяните scipy.sparse для разреженных матриц и CuPy или PyTorch для вычислений на GPU.
Где спотыкаются
Путают * и @. На квадратных матрицах форма результата одинаковая, ошибки не будет, и неверный ответ проходит молча.
Используют np.matrix. Документация NumPy этот класс больше не рекомендует, в нём * означает матричное умножение, и при смешивании с обычными массивами код ведёт себя непредсказуемо.
Считают dot и matmul взаимозаменяемыми. Для трёхмерных массивов они дают результаты разной формы, и на батчах это оборачивается тихой ошибкой в модели.