Appearance
11. Фильтрация 1D массивов, булевы маски и функция ReLU
Булева индексация и фильтрация
Механизм работы булевых масок на C-уровне
Булева фильтрация (индексация) — это один из самых мощных инструментов в NumPy, позволяющий извлекать, изменять или анализировать элементы массива на основе логических условий полностью без использования циклов for и ветвлений if.
Когда вы применяете оператор сравнения (например, >, <, ==, !=) к массиву NumPy, библиотека выполняет векторизованную операцию сравнения на C-уровне. Вместо одного логического значения вы мгновенно получаете новый массив — булеву маску (boolean mask). Она имеет точно такую же форму (shape), как и исходный массив, но состоит исключительно из элементов типа numpy.bool.
python
import numpy
# Simulating confidence scores of a computer vision object detector
confidence_scores = numpy.array([0.22, 0.85, 0.41, 0.92, 0.15])
# Generating a boolean mask for high-confidence predictions
high_confidence_mask = confidence_scores > 0.5
print(high_confidence_mask) # Outputs: [False True False True False]
print(high_confidence_mask.dtype) # Outputs: boolИзвлечение данных по маске
Если передать полученную булеву маску внутрь квадратных скобок исходного массива array[mask], NumPy выполнит фильтрацию: он пройдется по памяти и соберет в новый массив только те элементы, напротив которых в маске стояло значение True.
python
import numpy
confidence_scores = numpy.array([0.22, 0.85, 0.41, 0.92, 0.15])
high_confidence_mask = confidence_scores > 0.5
# Filtering operation
filtered_predictions = confidence_scores[high_confidence_mask]
print(filtered_predictions) # Outputs: [0.85 0.92]Логическое комбинирование условий
Если вам нужно отфильтровать массив по нескольким условиям сразу, стандартные операторы Python and, or и not использовать нельзя, так как они пытаются вычислить логическое значение всего массива целиком. Вместо них в NumPy применяются побитовые операторы, которые работают поэлементно:
&— логическое И (AND)|— логическое ИЛИ (OR)~— логическое НЕ / инверсия (NOT)
Обязательные круглые скобки
При комбинировании булевых условий в NumPy каждое отдельное сравнение обязательно должно быть обернуто в круглые скобки. Побитовые операторы в Python имеют более высокий приоритет, чем операторы сравнения, и без скобок код выдаст критическую ошибку ValueError.
python
import numpy
features = numpy.array([-10, 5, 20, 35, -2])
# Correct syntax with parentheses and bitwise AND
valid_range_mask = (features >= 0) & (features <= 25)
print(features[valid_range_mask]) # Outputs: [ 5 20]Условные операторы и функция ReLU в AI
Математическая логика и реализация ReLU
В глубоком обучении одной из самых популярных функций активации является ReLU (Rectified Linear Unit). Она работает как триггер: пропускает через себя без изменений все положительные сигналы, а все отрицательные — зануляет. Её математическая формула:
В NumPy реализация ReLU на основе булевой маски выполняется путем модификации элементов «на месте» (in-place mutation), что критически важно для экономии памяти при обработке миллиардов скрытых параметров нейросети:
python
import numpy
# Hidden layer activations containing raw neural signals (logits)
layer_activations = numpy.array([-2.5, 4.1, -0.1, 1.8, -12.4])
# Applying ReLU via boolean assignment
layer_activations[layer_activations < 0] = 0.0
print(layer_activations) # Outputs activated features: [0. 4.1 0. 1.8 0. ]Векторный оператор numpy.where (Быстрая замена if-else)
Функция numpy.where(condition, x, y) — это векторизованный аналог тернарного оператора x if condition else y. Она проверяет булево условие condition и для каждой ячейки массива возвращает элемент из массива x, если условие истинно (True), или из массива y, если условие ложно (False). При этом вместо массивов x и y можно передавать одиночные числа-скаляры.
Этот метод идеален для задач бинарной классификации (перевод вероятностей в жесткие метки классов 0 или 1) и для точечной очистки датасетов от аномалий:
python
import numpy
predicted_probabilities = numpy.array([0.12, 0.88, 0.45, 0.61, 0.03])
# If probability > 0.5 -> class 1, else -> class 0
predicted_classes = numpy.where(predicted_probabilities > 0.5, 1, 0)
print(predicted_classes) # Outputs: [0 1 0 1 0]Функции быстрой проверки any и all (Кванторы всеобщности и существования)
Для мгновенного анализа состояния всего массива без его полной распечатки используются ufuncs-редукторы:
numpy.any(condition)— возвращаетTrue, если хотя бы один элемент массива удовлетворяет логическому условию. Часто используется для быстрого поиска критических ошибок илиNaNв весах.numpy.all(condition)— возвращаетTrueтолько тогда, когда абсолютно все элементы массива соответствуют условию.
python
import numpy
weights = numpy.array([0.1, -0.5, numpy.nan, 0.4])
# Fast checking for presence of broken NaN data
has_nan = numpy.any(numpy.isnan(weights))
print("Are there any NaNs in weights?", has_nan) # Outputs: TrueУпражнения базовые (5 шт) (только с опорой на объясненную в данном уроке теорию)
1. Выделение аномально высоких температур
Задача: Дан вектор показаний датчиков температуры: [22.5, 45.1, 18.9, 52.4, 21.0]. Сгенерируйте булеву маску для температур, строго превышающих критическую отметку в 40.0 градусов. Извлеките эти аномальные элементы в новый массив с помощью маски и выведите его.
Посмотреть решение
python
import numpy
sensor_temperatures = numpy.array([22.5, 45.1, 18.9, 52.4, 21.0])
# Step 1: Create a boolean mask
critical_mask = sensor_temperatures > 40.0
# Step 2: Extract filtered records
extreme_temperatures = sensor_temperatures[critical_mask]
print(extreme_temperatures) # Outputs: [45.1 52.4]2. Фильтрация признаков по двойному условию
Задача: Дан вектор численных признаков: [-5, 12, 0, 18, 40, -1]. С помощью побитовых логических операторов извлеките элементы, которые одновременно больше или равны 0 и строго меньше 20. Выведите отфильтрованный вектор.
Посмотреть решение
python
import numpy
raw_features = numpy.array([-5, 12, 0, 18, 40, -1])
# Utilizing bitwise AND with strict parenthesis syntax
filtered_features = raw_features[(raw_features >= 0) & (raw_features < 20)]
print(filtered_features) # Outputs: [12 0 18]3. Ручная имплементация ReLU-активации
Задача: Инициализируйте массив сырых выходных сигналов нейросети raw_logits = numpy.array([-1.5, 2.3, -0.8, 0.0, 4.5]). Примените к нему логику функции активации ReLU: найдите все отрицательные элементы с помощью булевой маски и перезапишите их значение на 0.0. Выведите измененный массив.
Посмотреть решение
python
import numpy
raw_logits = numpy.array([-1.5, 2.3, -0.8, 0.0, 4.5])
# In-place substitution of negative logits to replicate ReLU trigger
raw_logits[raw_logits < 0] = 0.0
print(raw_logits) # Outputs: [0. 2.3 0. 0. 4.5]4. Квантование предсказаний через numpy.where
Задача: Модель предсказала массив вероятностей оттока пользователей: [0.15, 0.72, 0.49, 0.95]. Используя функцию numpy.where, преобразуйте этот массив в вектор строгих текстовых меток: если вероятность больше 0.5, ячейка должна получить строку "churn", в противном случае — "loyal". Выведите результат.
Посмотреть решение
python
import numpy
churn_probabilities = numpy.array([0.15, 0.72, 0.49, 0.95])
# Vectorized ternary mapping using numpy.where
customer_status = numpy.where(churn_probabilities > 0.5, "churn", "loyal")
print(customer_status) # Outputs: ['loyal' 'churn' 'loyal' 'churn']5. Экспресс-проверка градиентов на затухание
Задача: Дан вектор вычисленных градиентов весов нейросети: [0.001, 0.000, 0.002, 0.000]. Напишите логическое выражение с использованием numpy.any(), которое проверит, есть ли в данном векторе хотя бы один градиент, который строго равен 0.0 (проблема затухающих градиентов). Выведите результат логической проверки.
Посмотреть решение
python
import numpy
weight_gradients = numpy.array([0.001, 0.000, 0.002, 0.000])
# Check for presence of absolute zero signals
is_gradient_vanishing = numpy.any(weight_gradients == 0.0)
print("Are there vanishing gradients?", is_gradient_vanishing) # Outputs: TrueУпражнения смешанные (6 шт) (включают материал прошлых уроков)
6. Фильтрация и подсчет памяти сгенерированной сетки
Задача: Сгенерируйте линейную последовательность из 10 элементов в диапазоне от -10 до 10 включительно (Урок 3). Извлеките из нее все положительные числа (строго больше 0) с помощью булевой маски (Урок 10). Выведите полученный массив и объем памяти в байтах, который занимают его элементы (Урок 2).
Посмотреть решение
python
import numpy
# Generating the source grid (Lesson 3)
base_grid = numpy.linspace(-10, 10, 10)
# Boolean filtering (Lesson 10)
positive_elements = base_grid[base_grid > 0]
print("Filtered array:", positive_elements)
print("Memory footprint in bytes:", positive_elements.nbytes)7. Точечная замена пропусков NaN в представлении массива
Задача: Инициализируйте массив сырых данных [1.2, numpy.nan, 4.8, numpy.nan] типа numpy.float32 (Урок 4). Сделайте срез первых 3-х элементов в переменную data_view (Урок 5), которая является представлением оригинала (Урок 6). Используя функцию numpy.isnan(data_view) в качестве булева условия для numpy.where, замените все NaN внутри этого представления на дефолтное число 0.0. Выведите измененный исходный массив.
Посмотреть решение
python
import numpy
# Correct float32 setup with NaN markers (Lesson 4)
raw_signals = numpy.array([1.2, numpy.nan, 4.8, numpy.nan], dtype=numpy.float32)
# Slicing a subset view (Lesson 5 & Lesson 6)
data_view = raw_signals[:3]
# Using numpy.where to patch NaNs inside the view slice (Lesson 10)
data_view[:] = numpy.where(numpy.isnan(data_view), 0.0, data_view)
print("Mutated source array:", raw_signals) # Outputs: [1.2 0. 4.8 nan]8. Очистка изолированной копии квантованных меток от выбросов
Задача: Создайте вектор квантованных ID классов [1, 2, 99, 3, -99] с явным типом данных numpy.int8 (Урок 4). Чтобы не повредить исходный лог, сделайте полную независимую копию этого вектора через .copy() (Урок 6). В этой копии с помощью комбинированного побитового условия (Урок 10) найдите элементы, которые либо строго меньше 0, либо строго больше 10, и занулите их. Выведите исходный вектор и очищенную копию.
Посмотреть решение
python
import numpy
# Initial 8-bit integer setup (Lesson 4)
corrupted_labels = numpy.array([1, 2, 99, 3, -99], dtype=numpy.int8)
# Safe memory isolation (Lesson 6)
clean_labels_copy = corrupted_labels.copy()
# Applying bitwise OR operation to locate anomalies (Lesson 10)
anomaly_mask = (clean_labels_copy < 0) | (clean_labels_copy > 10)
clean_labels_copy[anomaly_mask] = 0
print("Source logs untouched:", corrupted_labels) # [ 1 2 99 3 -99]
print("Sanitized batch copy:", clean_labels_copy) # [1 2 0 3 0]9. Векторизованный расчет штрафов для отфильтрованного сабсета
Задача: Дан вектор вероятностей правильных ответов модели: [0.8, 0.1, 0.9, 0.4]. Сделайте срез последних 3-х элементов (Урок 5). Из этого среза отфильтруйте только те вероятности, которые строго меньше 0.5 (слабые предсказания) (Урок 10). Примените к полученным слабым вероятностям универсальную функцию логарифмического штрафа -numpy.log(x) (Урок 9). Выведите итоговый массив штрафов.
Посмотреть решение
python
import numpy
prediction_probabilities = numpy.array([0.8, 0.1, 0.9, 0.4])
# Step 1: Slice subset (Lesson 5)
target_slice = prediction_probabilities[-3:] # [0.1, 0.9, 0.4]
# Step 2: Apply mask condition (Lesson 10)
weak_predictions = target_slice[target_slice < 0.5] # [0.1, 0.4]
# Step 3: Run vectorized mathematical ufunc (Lesson 9)
incurred_penalties = -numpy.log(weak_predictions)
print("Penalties for weak predictions:", incurred_penalties)10. Проверка полной валидности сгенерированной логарифмической сетки
Задача: Сгенерируйте логарифмическую сетку из 5 элементов в диапазоне степеней от -2 до 2 (Урок 3). Напишите логическую проверку с использованием numpy.all(), которая проверяет, правда ли, что абсолютно все элементы полученного массива строго больше коэффициента отсечения 0.005 (Урок 10). Выведите результат (True/False).
Посмотреть решение
python
import numpy
# Generating logarithmic spacing vector (Lesson 3)
hyperparams_grid = numpy.logspace(-2, 2, 5) # [0.01, 0.1, 1.0, 10.0, 100.0]
# Validating the entire matrix using .all() (Lesson 10)
are_all_above_threshold = numpy.all(hyperparams_grid > 0.005)
print("Are all values safe?", are_all_above_threshold) # Outputs: True11. Сито Эратосфена: векторизованная проверка простых чисел
Задача: Создайте булев вектор длины 100 (Урок 4), в котором True означает «число простое». Изначально пометьте все 100 позиций как True и сбросьте первые две позиции (числа 0 и 1 не являются простыми). Затем, используя единственный цикл for и шаговый срез с булевым присваиванием mask[2*i::i] = False (Урок 5), «вычеркните» все кратные для каждого кандидата-делителя (Урок 11). Выведите первые 20 флагов, общее количество простых чисел до 100 и значения флагов для позиций 17 (простое) и 25 (составное).
Посмотреть решение
python
import numpy
# Initial boolean mask: assume all 100 numbers are prime
is_prime_mask = numpy.ones(100, dtype=numpy.bool_)
# 0 and 1 are not prime numbers
is_prime_mask[:2] = False
# Sieve of Eratosthenes: cross out multiples of each divisor
limit = int(numpy.sqrt(100))
for i in range(2, limit):
if is_prime_mask[i]:
is_prime_mask[2*i::i] = False # start at 2*i with step i
print("First 20 flags:", is_prime_mask[:20])
# Outputs: [False False True True False True False True False False False
# True False True False False False True False True]
print("Number of primes below 100:", numpy.count_nonzero(is_prime_mask)) # 25
print("Is 17 prime?", is_prime_mask[17]) # Outputs: True
print("Is 25 prime?", is_prime_mask[25]) # Outputs: False