Введение

Далее приводится дружественный обзор важных концепций линейной алгебры и глубокого обучения, которые были упомянуты Джереми Ховардом в уроке 8 fast.ai 2019. Я разделил каждую концепцию на академическую часть и представление кода Python.

Скаляры, векторы, матрицы и тензоры

Скаляр — это всего лишь одно число. Он используется для контраста с массивами из нескольких чисел.

Вектор — это массив чисел.

Матрица представляет собой двумерный массив чисел, поэтому каждый элемент идентифицируется двумя индексами.

Тензор — это массив чисел, расположенных на регулярной сетке с переменным числом осей.

Умножение матриц

Одной из самых важных операций с матрицами является умножение двух матриц. Матричное произведение матриц A и B является третьей матрицей C. Чтобы это произведение было определено, A должно иметь такое же количество столбцов, сколько B имеет строк.



Обратите внимание, что стандартное произведение двух матриц — это не просто матрица, содержащая произведение отдельных элементов. Такая операция существует и называется поэлементным произведением или произведением Адамара и обозначается как:

Продукт Адамара используется в методах сжатия изображений, таких как JPEG. Он также известен как продукт Шура в честь немецкого математика Исая Шура. Продукт Адамара используется в ячейках LSTM (долговременная кратковременная память) рекуррентных нейронных сетей (RNN).

И это все, что вам нужно знать о продукте Адамара, поскольку это не тот способ, которым мы вычисляем прямое распространение в глубоком обучении. Мы используем умножение матриц для получения, например, матричное произведение C = AB как вычисление Cn,q как скалярное произведение между строкой n матрицы A и столбцом q из Б.

Код Python для умножения матриц будет выглядеть так:

def matmul(a,b):
    ar,ac = a.shape # n_rows * n_cols
    br,bc = b.shape 
    assert ac==br
    c = torch.zeros(ar, bc)
    for i in range(ar):
        for j in range(bc):
            for k in range(ac): # or br
                c[i,j] += a[i,k] * b[k,j]
    return c

Поскольку приведенный выше код имеет два цикла внутри цикла, он работает медленно. Существует более быстрый способ умножения матриц, использующий принципы поэлементного вычисления. Это ускоряет расчет в 178 раз.

def matmul(a,b):
    ar,ac = a.shape
    br,bc = b.shape
    assert ac==br
    c = torch.zeros(ar, bc)
    for i in range(ar):
        for j in range(bc):
        # Any trailing “,:” can be removed
        c[i,j] = (a[i,:] * b[:,j]).sum()
    return c

Поскольку мы поняли, что циклы медленные, пришло время ввести концепцию широковещательной рассылки, которая широко используется в матричных операциях. Пожалуйста, ознакомьтесь с интересной и подробной статьей о вещании в PyTorch/Numpy.



Вещание

Термин широковещательная рассылка описывает, как обрабатываются массивы различной формы во время арифметических операций. Термин вещание был впервые использован Numpy.

Из Документации Numpy:

Термин широковещательная передача описывает, как numpy обрабатывает массивы различной формы во время арифметических операций. С учетом определенных ограничений меньший массив «транслируется» по большему массиву, чтобы они имели с совместимые формы. Вещание предоставляет средства векторизации операций с массивами, так что зацикливание происходит в C, а не в Python. Он делает это без создания ненужных копий данных и обычно приводит к эффективной реализации алгоритма.

В дополнение к эффективности трансляции это позволяет разработчикам писать меньше кода, что обычно приводит к меньшему количеству ошибок и дает более высокую скорость в 3 195 раз по сравнению с чистым кодом Python:

def matmul(a,b):
    ar,ac = a.shape
    br,bc = b.shape
    assert ac==br
    c = torch.zeros(ar, bc)
    for i in range(ar):
        # c[i,j] = (a[i,:] * b[:,j]).sum() # previous
        c[i] = (a[i,None] * b).sum(dim=0)
    return c

Существует способ изменить приведенный выше код Python, используя соглашение о суммировании Эйнштейна, которое представляет операции с многомерными массивами простым способом.

Суммирование Эйнштейна

Вот хорошая и подробная статья о суммировании Эйнштейна. Пожалуйста, проверьте это.



Используя метод einsum в PyTorch для написания новой функции, мы можем достичь скорости вычислений в 16 000 раз выше, чем в исходном коде Python.

def matmul(a,b): return torch.einsum(‘ik,kj->ij’, a, b)

Однако лучший способ умножения матриц с точки зрения скорости и эффективности использования памяти — использовать метод matmul из PyTorch.

def matmul(a,b): return a.matmul(b)
a.matmul(b) = a@b

Это в 50 000 раз быстрее исходного кода.

___________________________________________________________________

Спасибо, что дочитали эту статью до конца и, надеюсь, она оказалась для вас полезной. Я впервые консолидирую знания в области глубокого обучения для создания контента.

Дополнительная благодарность за вдохновение принадлежит https://medium.com/@lankinen с его статьей.



Давайте продолжим изучение fast.ai.