Appearance
5. Чтение, индексация и срезы одномерных векторов
Срезы 1D массивов или векторов
Анатомия одномерного индексирования
Индексация в NumPy полностью наследует синтаксис стандартных списков Python, но работает на уровне скомпилированного C-кода, обеспечивая мгновенный доступ к ячейкам памяти. Нумерация элементов начинается с 0 для доступа с начала массива и с -1 для доступа с конца (обратная индексация).
python
import numpy
# Creating a simple 1D vector of training loss values
loss_history = numpy.array([0.85, 0.62, 0.41, 0.35, 0.22])
# Direct and reverse indexing
print(loss_history[0]) # Outputs first element: 0.85
print(loss_history[-1]) # Outputs last element (current loss): 0.22Синтаксис расширенных срезов (Slicing)
Срез позволяет извлечь подмножество элементов из вектора. Полная формула среза выглядит следующим образом: array[start:stop:step]
start— индекс элемента, с которого начинается срез (включается). По умолчанию0.stop— индекс элемента, на котором срез заканчивается (не включается). По умолчанию равен длине массива.step— шаг обхода элементов. Может быть отрицательным для движения в обратную сторону. По умолчанию1.
python
import numpy
metrics = numpy.array([10, 20, 30, 40, 50, 60])
# Basic slicing scenarios
print(metrics[1:4]) # Elements from index 1 to 3: [20, 30, 40]
print(metrics[:3]) # First 3 elements: [10, 20, 30]
print(metrics[3:]) # From index 3 to the end: [40, 50, 60]
print(metrics[::2]) # Every second element: [10, 30, 50]
print(metrics[::-1]) # Fully reversed array: [60, 50, 40, 30, 20, 10]Изменение элементов через срезы (In-place modification)
Срезы в NumPy поддерживают операцию массового присвоения. Вы можете обновить целую группу элементов одной векторизованной командой без использования циклов.
python
import numpy
signals = numpy.zeros(6)
# Modifying every second element at once
signals[::2] = 99.0
print(signals) # Outputs: [99. 0. 99. 0. 99. 0.]Обратная замена через вывернутые срезы (Reverse In-place slicing)
Отрицательный шаг позволяет не только читать массив задом наперед, но и точечно перезаписывать выбранные фрагменты вектора в обратном порядке на лету.
python
import numpy
arr = numpy.array([1, 2, 3, 4, 5])
# Replace the first 3 elements with new values in reverse order
arr[2::-1] = [10, 20, 30]
print(arr) # Outputs: [30 20 10 4 5]Произвольное изменение по списку индексов (Fancy Indexing Assignment)
Если элементы, которые нужно изменить, разбросаны хаотично и их невозможно поймать математическим шагом среза, в квадратные скобки передается массив или список конкретных целевых индексов.
python
import numpy
weights = numpy.zeros(5)
indices_to_update = [0, 1, 4]
# Modify only the specific slots without affecting indices 2 and 3
weights[indices_to_update] = [5.5, 6.5, 7.5]
print(weights) # Outputs: [5.5 6.5 0. 0. 7.5]Срезы в контексте нейросетей
Извлечение временных окон и шаговый срез в AI
В реальных задачах искусственного интеллекта одномерные срезы применяются повсеместно:
- Генерация батчей и валидация: Быстрое отделение тестовой выборки от обучающей (например, забрать первые 80% данных на обучение
dataset[:80], а остатокdataset[80:]— на валидацию). - Аудио и Спектрограммы: Обрезание аудио-сигналов, выделение окон фиксированной длительности для подачи в сверточные сети (CNN).
- Обработка текста (NLP): Выделение контекстного окна токенов (например, срез
tokens[0:max_sequence_len]) перед подачей вектора в Трансформер. - Даунсэмплинг (Downsampling): Понижение частоты дискретизации данных с помощью шага среза (например, срез
signal[::2]уменьшает частоту сигнала ровно в 2 раза). - Очистка аномалий в данных (Data Cleaning): Применение масок для быстрой нейтрализации битых значений или выбросов (например,
tensor[tensor > threshold] = thresholdдля обрезки слишком высоких пиков).
Упражнения базовые и продвинутые (7 шт) (только с опорой на объясненную в данном уроке теорию)
1. Извлечение последних метрик обучения
Задача: Дан вектор значений точности модели (accuracy) по эпохам: [0.51, 0.64, 0.78, 0.85, 0.92, 0.94]. Выделите последние 3 эпохи с помощью обратного среза и выведите их на экран.
Посмотреть решение
python
import numpy
accuracy_history = numpy.array([0.51, 0.64, 0.78, 0.85, 0.92, 0.94])
# Extracting the last 3 elements
last_epochs = accuracy_history[-3:]
print(last_epochs) # Outputs: [0.85 0.92 0.94]2. Понижение частоты сигнала (Downsampling)
Задача: Сгенерируйте вектор отсчетов аудиосигнала, содержащий числа от 0 до 9 включительно (с помощью numpy.arange). Проведите даунсэмплинг, выделив каждый третий элемент сигнала, начиная с самого первого (индекс 0). Выведите результат.
Посмотреть решение
python
import numpy
audio_signal = numpy.arange(10)
# Slice with a step of 3
downsampled_signal = audio_signal[::3]
print(downsampled_signal) # Outputs: [0 3 6 9]3. Инвертирование временного ряда
Задача: Дан временной ряд цен акций: [100.5, 101.2, 99.8, 102.4]. Разверните этот массив в обратном хронологическом порядке с помощью синтаксиса срезов. Выведите результат в консоль.
Посмотреть решение
python
import numpy
time_series = numpy.array([100.5, 101.2, 99.8, 102.4])
# Reversing the array using negative step
reversed_series = time_series[::-1]
print(reversed_series) # Outputs: [102.4 99.8 101.2 100.5]4. Массовое зануление аномалий
Задача: Создайте вектор показаний датчиков: [12.0, 14.5, 999.0, 999.0, 999.0, 11.2]. Известно, что элементы с индексами от 2 до 4 включительно являются ошибочными аномалиями. Замените эти элементы на 0.0 одной операцией среза (без циклов). Выведите итоговый вектор.
Посмотреть решение
python
import numpy
sensor_readings = numpy.array([12.0, 14.5, 999.0, 999.0, 999.0, 11.2])
# Slice index 2 to 4 inclusive means stop index must be 5
sensor_readings[2:5] = 0.0
print(sensor_readings) # Outputs: [12. 14.5 0. 0. 0. 11.2]5. Выделение центрального контекстного окна
Задача: Дан вектор токенов текста длины 8: [101, 2003, 1037, 2467, 2004, 3012, 1205, 102]. Извлеките центральное окно токенов, исключив первый элемент (индекс 0) и последний элемент (индекс 7), используя один срез. Выведите результат.
Посмотреть решение
python
import numpy
token_ids = numpy.array([101, 2003, 1037, 2467, 2004, 3012, 1205, 102])
# Excluding the first and the last element
context_window = token_ids[1:-1]
print(context_window) # Outputs: [2003 1037 2467 2004 3012 1205]6. Зеркальное обновление весов (Reverse In-place Slicing)
Задача: Дан вектор весов слоя нейросети: [0.1, 0.2, 0.3, 1.0, 2.0]. Замените первые 3 элемента этого вектора на новые значения [0.9, 0.8, 0.7], но запишите их в массив в обратном порядке (чтобы 0.7 встал на индекс 0, а 0.9 на индекс 2). Сделайте это с помощью вывернутого среза «на месте» без циклов.
Посмотреть решение
python
import numpy
weights = numpy.array([0.1, 0.2, 0.3, 1.0, 2.0])
# Slice the first 3 elements in reverse order and assign new values
weights[2::-1] = [0.9, 0.8, 0.7]
print(weights) # Outputs: [0.7 0.8 0.9 1.0 2.0]7. Точечная замена эмбеддингов (Fancy Indexing Assignment)
Задача: Инициализируйте нулевой вектор смещений (biases) из 6 элементов. Известно, что в процессе обратного распространения ошибки обновились только крайние и центральные ячейки с индексами 0, 3 и 5. Запишите в эти ячейки значения [-1.2, 0.5, 3.1] одновременно с помощью передачи списка индексов. Выведите финальный массив.
Посмотреть решение
python
import numpy
biases = numpy.zeros(6)
indices_to_update = [0, 3, 5]
# Modifying specific slots at once using a list of coordinates
biases[indices_to_update] = [-1.2, 0.5, 3.1]
print(biases) # Outputs: [-1.2 0. 0. 0.5 0. 3.1]Упражнения смешанные (5 шт) (включают материал прошлых уроков)
8. Генерация сетки Learning Rate и выделение её начала
Задача: Сгенерируйте сетку из 10 шагов темпа обучения (learning rate) от 0.1 до 0.01 включительно (используя numpy.linspace из Урока 3). Извлеките из этой сетки первые 4 элемента — фазу агрессивного старта модели. Выведите срез.
Посмотреть решение
python
import numpy
# Generating steps (Lesson 3 theory)
lr_grid = numpy.linspace(0.1, 0.01, 10)
# Slicing the initial aggressive phase (Lesson 5 theory)
initial_phase = lr_grid[:4]
print(initial_phase)9. Анализ занимаемой памяти среза квантованного вектора
Задача: Создайте вектор индексов токенов из 100 элементов (от 0 до 99 через numpy.arange), принудительно задав ему тип данных numpy.int8 (Из Урока 4). Сделайте срез, выделив каждый второй элемент массива. Выведите объем памяти в байтах (.nbytes), который занимает этот полученный срез.
Посмотреть решение
python
import numpy
# Creating a quantized integer array (Lesson 4 theory)
token_vector = numpy.arange(100, dtype=numpy.int8)
# Slicing every second element (Lesson 5 theory)
sliced_vector = token_vector[::2]
# Checking byte size. Slicing returns a view, but contains 50 elements of 1 byte
print("Number of elements:", sliced_vector.size) # 50
print("Memory size in bytes:", sliced_vector.nbytes) # Outputs: 50 bytes10. Исправление пропусков (NaN) внутри среза данных
Задача: Создайте вектор весов list_values = [0.25, 0.45, None, None, 0.85]. Как вы знаете из Урока 4, None использовать нельзя. Инициализируйте массив корректно, заменив None на numpy.nan (тип numpy.float32). Затем с помощью одной операции среза замените оба поврежденных центральных элемента со значениями NaN на константу 0.5. Выведите итоговый массив и типы элементов массива
Посмотреть решение
python
import numpy
# Valid initialization using float32 and NaN (Lesson 4 theory)
weights_vector = numpy.array([0.25, 0.45, numpy.nan, numpy.nan, 0.85], dtype=numpy.float32)
# Replacing NaN slice with fixed default value (Lesson 5 theory)
weights_vector[2:4] = 0.5
print(weights_vector) # Outputs: [0.25 0.45 0.5 0.5 0.85]
print(weights_vector.dtype) # Outputs: dtype('float32')11. Реверс сгенерированной последовательности шагов оптимизации
Задача: Сгенерируйте массив из 6 элементов от 0 до 25 с помощью функции numpy.linspace. Сделайте срез, который вернет этот массив полностью развернутым задом наперед. Убедитесь, что все элементы итогового массива сохранили тип данных float64.
Посмотреть решение
python
import numpy
# Generating linear grid (Lesson 3 theory)
steps_array = numpy.linspace(0, 25, 6)
# Reversing via slice step (Lesson 5 theory)
reversed_array = steps_array[::-1]
print("Reversed:", reversed_array)
print("Dtype confirmation:", reversed_array.dtype) # float6412. Массовая инициализация среза пустого тензора
Задача: Создайте пустой вектор будущих градиентов из 10 элементов, полностью заполненный нулями (через numpy.zeros). Замените его вторую половину элементов (последние 5 штук) на единицы, используя срез с отрицательным индексом start. Выведите результат.
Посмотреть решение
python
import numpy
# Initializing zero tensor placeholder (Lesson 3 theory)
gradients_tensor = numpy.zeros(10)
#Overwriting the second half utilizing negative start index (Lesson 5 theory)
gradients_tensor[-5:] = 1.0
print(gradients_tensor) # Outputs: [0. 0. 0. 0. 0. 1. 1. 1. 1. 1.]