Как убирать циклы
Правило простое: если внутри цикла одна и та же операция над разными элементами, в numpy она уже есть.
Сумма - sum. Максимум с нулём - maximum. Сумма произведений - @. Среднее по столбцам - mean с axis.
Цикл остаётся нужен там, где шаги зависят друг от друга: следующий использует результат предыдущего. Обучение сети - как раз такой случай, цикл по шагам обучения убрать нельзя. А вот цикл по заказам внутри одного шага убирается всегда.
Отсюда рабочая привычка: увидел вложенные циклы по данным - сначала спроси, есть ли это одной операцией, и только потом пиши.
Убрав цикл, обязательно сверь результат через allclose. Векторная версия часто считает не совсем то же самое, и без сверки ты этого не заметишь.
И не оставляй np.append внутри цикла: он копирует весь массив на каждом шаге, и цикл становится медленнее, а не быстрее.