Appearance
10. Сложные ufuncs, математическая база ИИ и обработка исключений (NaN/Inf)
Векторизованные математические вычисления и тригонометрия
Физика сложных векторизованных операций в AI
В традиционном Python для выполнения сложных математических расчетов (например, вычисления тригонометрии или логарифмов) над коллекциями данных приходится использовать циклы for. Это приводит к огромным накладным расходам: интерпретатор на каждой итерации динамически проверяет типы и выделяет память.
NumPy решает эту проблему с помощью сложных универсальных функций (ufuncs), таких как numpy.cos(), numpy.sin(), numpy.exp() и numpy.log(). Практический смысл их применения в AI-инженерии заключается в аппаратном распараллеливании SIMD (Single Instruction, Multiple Data) на уровне процессора. ufuncs транслируют математическую задачу на уровень скомпилированного низкоуровневого кода на языке C. Процессор за один такт выполняет операцию сразу над целым блоком элементов массива, загруженным в векторные регистры, а непрерывная структура памяти ndarray позволяет считывать данные пачками прямо в быстрый L1/L2 кэш.
python
import numpy
# Creating an array containing baseline values (0, e, pi)
math_constants = numpy.array([0, numpy.e, numpy.pi])
# Vectorized cosine transformation applied to the entire array at once
cosine_outputs = numpy.cos(math_constants)
print("Cosine output:", cosine_outputs) # Outputs: [ 1., -0.91173391, -1. ]Математическая база для функций активации и потерь в AI
Экспоненты и логарифмы как фундамент глубокого обучения
Для построения нелинейных нейросетевых архитектур стандартной арифметики недостаточно. Экспонента numpy.exp() и натуральный логарифм numpy.log() служат основой для вычисления ключевых компонентов ИИ:
- Функция активации Sigmoid (Сигмоида): Сжимает любое входное число (сырой логит) в диапазон от 0 до 1, превращая его в вероятность предсказания. Её формула:
. - Распределение вероятностей Softmax: Применяется на выходных слоях многоклассовых классификаторов. Она возводит все элементы вектора в экспоненту, а затем делит результат на сумму этих экспонент, делая так, чтобы сумма всех финальных вероятностей была строго равна 1.
- Cross-Entropy Loss (Функция потерь): Метрика, штрафующая модель за неверные ответы с помощью логарифма:
.
python
import numpy
logits = numpy.array([-2.0, 0.0, 2.0])
# Vectorized Sigmoid function activation in one line
probabilities = 1 / (1 + numpy.exp(-logits))
print("Probabilities via Sigmoid:", probabilities) # Outputs: [0.1192 0.5 0.8807]Логика обработки исключений: бесконечности и неопределенности
Почему NumPy не падает при математических ошибках
В чистом Python попытка разделить число на ноль (1 / 0) или вычислить логарифм нуля незамедлительно приводит к фатальной ошибке (ZeroDivisionError, ValueError) и аварийной остановке всей программы.
В ИИ-инженерии, где модели обучаются днями или неделями, единичный сбой в расчетах из-за занулившихся вероятностей не должен приводить к падению сервера. Поэтому NumPy спроектирован по международному стандарту вещественной арифметики IEEE 754: при возникновении математических аномалий он не выбрасывает исключений. Вместо этого библиотека генерирует системное предупреждение RuntimeWarning, а в ячейку памяти записывает специальный числовой маркер.
Анатомия маркеров inf и -inf
Если вы делите положительное число на ноль или вычисляете логарифм нуля, NumPy генерирует маркеры бесконечности:
numpy.inf(Infinity) — положительная бесконечность. Возникает при делении положительного числа на ноль.-numpy.inf(Negative Infinity) — отрицательная бесконечность. Возникает при вычислении натурального логарифма от строгого нуля.
python
import numpy
probabilities_vector = numpy.array([0.0, 1.0])
# Calculating log(0) triggers a RuntimeWarning instead of crashing
log_results = numpy.log(probabilities_vector)
print("Log results:", log_results) # Outputs: [-inf 0.]Маркер numpy.nan как замена ошибки и её локализация
Математический смысл неопределенности 0/0
Когда процессор сталкивается с чистой математической неопределенностью, где результат физически невозможно определить (например, деление нуля на ноль 0 / 0 или вычитание бесконечности из бесконечности inf - inf), NumPy записывает в ячейку маркер numpy.nan (Not a Number).
python
import numpy
zeros_vector = numpy.array([0.0, 1.0])
# Element-wise division where the first element is 0/0
division_output = zeros_vector / zeros_vector
print("Division output:", division_output) # Outputs: [nan 1.]Локализация «сломанных» точек через numpy.isnan
Маркер numpy.nan уникален: он не равен ни одному числу на свете, и даже не равен самому себе (numpy.nan == numpy.nan вернет False).
Для поиска «сломанных» точек используется специальная векторизованная ufunc-функция numpy.isnan(array). Она возвращает булеву маску, где позиции со значениями NaN отмечены как True. Это позволяет мгновенно обнаруживать и изолировать поврежденные признаки в нейросетевых датасетах.
python
import numpy
features_batch = numpy.array([1.0, numpy.nan, 4.5])
# Spotting the anomaly locations
anomaly_mask = numpy.isnan(features_batch)
print("Anomaly mask:", anomaly_mask) # Outputs: [False True False]Упражнения базовые (5 шт) (только с опорой на объясненную в данном уроке теорию)
1. Векторизованный расчет натурального логарифма
Задача: Создайте массив из чисел [1.0, 2.0, 7.389056]. Примените к нему векторизованную ufunc-функцию numpy.log() для вычисления натурального логарифма всех элементов за один проход. Выведите результат.
Посмотреть решение
python
import numpy
input_numbers = numpy.array([1.0, 2.0, 7.389056])
# Applying natural logarithm ufunc element-wise
logarithmic_outputs = numpy.log(input_numbers)
print(logarithmic_outputs) # Outputs approx: [0. 0.69314718 2.]2. Фиксация отрицательной бесконечности при расчете Loss
Задача: Задайте массив предсказанных вероятностей, содержащий критическую ошибку нейросети: [0.0]. Примените к нему функцию numpy.log(). Выведите результат в консоль и убедитесь, что код не упал, а вернул отрицательную бесконечность.
Посмотреть решение
python
import numpy
zero_probability = numpy.array([0.0])
# Calculating log(0) to witness negative infinity behavior
loss_anomaly = numpy.log(zero_probability)
print(loss_anomaly) # Outputs: [-inf]3. Провокация неопределенности NaN
Задача: Создайте вектор first_vector = numpy.array([0.0]) и вектор second_vector = numpy.array([0.0]). Разделите первый вектор на второй. Выведите получившийся результат, чтобы зафиксировать маркер nan.
Посмотреть решение
python
import numpy
first_vector = numpy.array([0.0])
second_vector = numpy.array([0.0])
# Forcing a 0/0 indeterminacy situation
invalid_division = first_vector / second_vector
print(invalid_division) # Outputs: [nan]4. Поиск поломок с помощью numpy.isnan
Задача: Дан массив сырых признаков: numpy.array([42.0, numpy.nan, -15.5]). Напишите код, который проверит каждый элемент массива и вернет булеву маску, где позиция аномалии NaN будет помечена как True. Выведите маску.
Посмотреть решение
python
import numpy
raw_features = numpy.array([42.0, numpy.nan, -15.5])
# Locating NaNs using the dedicated ufunc tool
nan_detection_mask = numpy.isnan(raw_features)
print(nan_detection_mask) # Outputs: [False True False]5. Обнаружение положительной бесконечности
Задача: Создайте массив signals = numpy.array([10.0]) и разделите его на массив divisors = numpy.array([0.0]). Выведите результат арифметической операции в консоль, чтобы зафиксировать маркер inf.
Посмотреть решение
python
import numpy
signals = numpy.array([10.0])
divisors = numpy.array([0.0])
# Positive number divided by zero yields positive infinity
infinity_result = signals / divisors
print(infinity_result) # Outputs: [inf]Упражнения смешанные (5 шт) (включают материал прошлых уроков)
6. Проверка характеристики массива, содержащего бесконечность
Задача: Сгенерируйте массив из двух элементов путем деления numpy.array([5.0, 2.0]) на numpy.array([0.0, 2.0]) (Урок 7). Выведите на экран характеристики полученного объекта: сам массив, его тип данных через .dtype и общий вес в байтах через .nbytes (Урок 2). Проанализируйте, влияют ли аномалии на метаданные структуры ndarray.
Посмотреть решение
python
import numpy
numerator = numpy.array([5.0, 2.0])
denominator = numpy.array([0.0, 2.0])
# Execution (Lesson 7 theory)
result_tensor = numerator / denominator
# Metadata inspection (Lesson 2 theory)
print("Array content:", result_tensor) # [inf 1.]
print("Data type:", result_tensor.dtype) # float64
print("Memory footprint:", result_tensor.nbytes, "bytes") # 16 bytes7. Принудительный каст вещественной бесконечности в int
Задача: Создайте массив, содержащий одну положительную бесконечность [numpy.inf]. Попробуйте принудительно перевести этот массив в целочисленный тип данных numpy.int64 с помощью явного метода .astype() (Урок 7). Выведите результат на экран и посмотрите в комментарии к решению, к какому катастрофическому искажению битов это приводит.
Посмотреть решение
python
import numpy
infinity_vector = numpy.array([numpy.inf])
# Explicit casting of floating-point infinity into integer space (Lesson 7)
corrupted_integer = infinity_vector.astype(numpy.int64)
print(corrupted_integer) # Outputs a massive negative dummy integer: [-9223372036854775808]
# Meaning: Integer formats cannot physically store infinity. Casting forces extreme bit distortion.8. Цепочка срезов над массивом тригонометрической сетки
Задача: Сгенерируйте точную числовую сетку из 6 элементов от 0 до строгого значения numpy.pi с помощью numpy.linspace (Урок 3). Примените к ней векторизованную ufunc-функцию numpy.cos() (Урок 9). Возьмите от полученного результата срез последних 3-х элементов (Урок 5). Выведите этот срез на экран.
Посмотреть решение
python
import numpy
# Step 1: Linear grid generation (Lesson 3)
pi_grid = numpy.linspace(0, numpy.pi, 6)
# Step 2: Vectorized cosine operation (Lesson 9)
cos_transformed = numpy.cos(pi_grid)
# Step 3: Extracting slice subset (Lesson 5)
final_slice = cos_transformed[-3:]
print(final_slice)9. Поиск пропусков NaN внутри изолированной копии логов
Задача: Дан массив показаний датчиков с аномалиями: [0.54, numpy.nan, 0.88]. Создайте полную изолированную копию этого массива с помощью метода .copy() (Урок 6). На основе этой копии сгенерируйте булеву маску для обнаружения NaN через numpy.isnan() (Урок 9). Выведите полученную маску.
Посмотреть решение
python
import numpy
raw_logs = numpy.array([0.54, numpy.nan, 0.88])
# Deep copying to secure original storage (Lesson 6)
isolated_logs = raw_logs.copy()
# Anomaly screening via specialized ufunc (Lesson 9)
nan_mask = numpy.isnan(isolated_logs)
print(nan_mask) # Outputs: [False True False]10. Логарифмирование сгенерированной сетки параметров и in-place сложение
Задача: Создайте массив из 4 элементов в логарифмическом масштабе от степеней -1 до 2 с помощью numpy.logspace(-1, 2, 4) (Урок 3). Примените к полученному массиву векторизованную функцию натурального логарифма numpy.log() (Урок 9). К результату прибавьте на месте (in-place) значение 1.0 через оператор += (Урок 8). Выведите итоговый массив на экран.
Посмотреть решение
python
import numpy
# Generating base exponential targets grid (Lesson 3)
hyperparams_space = numpy.logspace(-1, 2, 4)
# Reversing exponential distribution via natural log (Lesson 9)
transformed_space = numpy.log(hyperparams_space)
# Performing an in-place accumulation step (Lesson 8)
transformed_space += 1.0
print(transformed_space) # Outputs approx: [0. 2.30258509 4.60517019 6.90775528]11. Сумма квадратов синуса и косинуса
Задача: Создайте массив, состоящий из 100 равноотстоящих точек от
Посмотреть решение
python
x = np.linspace(-4*np.pi, 4*np.pi, 100)
print(np.all((np.sin(x)**2 + np.cos(x)**2) == 1))
print(np.sin(x)**2 + np.cos(x)**2)
print(np.all((np.sin(x)**2 + np.cos(x)**2).round() == 1))text
False
[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. ... 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
TrueПочему np.all(... == 1) даёт False, хотя все элементы «равны 1»? Это точность вещественной арифметики: на деле элементы принимают значения вроде 1.. Сравнивать float на точное равенство — опасно; надёжнее np.isclose или, как в решении, сравнение после .round().