Сколько операций стоит один слой
На четырёх заказах цикл незаметен. Посмотрим на реальный порядок величин.
Умножение таблицы формы (300, 300) на таблицу (300, 100) требует 300 · 100 · 300 = 9 000 000 умножений. И это один слой на одном шаге. При обучении такой шаг повторяется десятки тысяч раз.
В цикле каждое умножение обходится не в одно машинное действие, а в десятки: проверить типы, создать объект для результата, положить его в переменную. Само умножение занимает ничтожную долю этого времени.
Векторизация — запись вычисления целиком над тензорами, без цикла по отдельным числам. Вместо перебора элементов вызывается одна операция, и работу выполняет библиотека, написанная на языке C: она считает блоками и укладывает данные в кэш процессора.
Измеренная разница
Посчитаем одно и то же произведение (300, 300) на (300, 100) тремя способами: тремя вложенными циклами, одним циклом по заказам и одной операцией @.
Результаты у всех трёх совпадают полностью — это проверяется сравнением с допуском, а не сравнением на равенство, потому что у дробных чисел последние знаки расходятся при разном порядке действий.
Таблица 13. Время трёх способов на одной и той же задаче
| способ | время, секунды |
| три вложенных цикла | около 2.5 |
| один цикл по заказам | около 0.003 |
одна операция @ | около 0.0008 |
Читаем сверху вниз: каждая следующая строка быстрее предыдущей. Между первой и третьей разница примерно в три тысячи раз.
Числа приведены для нашей машины и у вас будут другими. Важен не сам результат замера, а порядок: секунды против долей миллисекунды. Именно из этой разницы и складывается различие между моделью, которая обучается за минуту, и моделью, которая обучается неделю.
Когда цикл убрать нельзя
Цикл по примерам почти всегда лишний. Правило такое: если внутри цикла выполняется одна и та же операция над разными элементами, в библиотеке она уже есть. Сумма, максимум с нулём, сумма произведений, среднее по столбцам — всё это отдельные операции.
Цикл остаётся нужен там, где шаги зависят друг от друга: следующий использует результат предыдущего. Обучение сети — как раз такой случай, и цикл по шагам обучения в главе 2 убрать не удастся. А вот цикл по заказам внутри одного шага убирается всегда.
Убрав цикл, результат надо сверить с прежним: векторная запись часто считает не совсем то же самое, и без сверки это остаётся незамеченным.