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