Appearance
12. Двумерные матрицы (2D): Создание и базовые атрибуты
Конструирование 2D матриц
Конвертация вложенных двумерных списков
Для перехода от одномерных векторов к двумерным пространствам в NumPy используется тот же конструктор numpy.array(), но на вход ему передается упорядоченная структура — вложенный список Python (список списков). В этот момент библиотека аллоцирует единый непрерывный блок оперативной памяти на C-уровне, куда копирует числовые элементы, выстраивая сетку строк и столбцов.
python
import numpy
# Converting a nested Python list into a 2D matrix
nested_list = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
dataset_matrix = numpy.array(nested_list)
print(dataset_matrix)
# Outputs:
# [[1. 2. 3.]
# [4. 5. 6.]]Геометрия и оси матриц (Axis 0 и Axis 1)
В двумерных массивах NumPy вводится строгое понятие направлений — осей вычислений (axes):
axis=0— это вертикальная ось (строки). Движение вдоль этой оси означает переход от строки к строке внутри одного столбца.axis=1— это горизонтальная ось (столбцы). Движение вдоль нее означает переход от столбца к столбцу внутри одной строки.
Понимание осей критически важно для ИИ-инженеров, так как все последующие операции (агрегации, нормализации, свертки) будут требовать явного указания направления вычислений.
Проверка ключевых параметров и метаданных матрицы
Фундаментальные атрибуты ndarray в 2D пространстве
Для контроля геометрии и структуры матриц на практике используются встроенные свойства объекта:
.ndim— размерность массива. Для любой плоской матрицы этот показатель строго равен2..shape— кортеж (tuple) геометрии формата(rows, columns). Для матрицы из 2 строк и 3 столбцов он вернет(2, 3). Соответствие.shapeвесов и входных тензоров — главное условие успешной работы слоев нейросети..size— общее число элементов. Рассчитывается как произведение строк на столбцы (rows * columns)..nbytes— физический вес элементов матрицы в оперативной памяти (в байтах). Позволяет рассчитать, сколько VRAM займет табличный датасет или слой весов.strides— кортеж (tuple) шагов в байтах, необходимых для перехода к следующему элементу вдоль каждой оси. Для 2D-матрицы формат имеет вид(row_stride, column_stride). Например, шаг по строкам показывает, сколько байт нужно «перешагнуть» в памяти, чтобы переместиться строго на одну строку вниз. Это свойство объясняет, как NumPy мгновенно выполняет транспонирование и создание срезов без реального копирования данных в RAM (изменяются толькоstridesи.shape).
python
import numpy
matrix = numpy.array([[10, 20], [30, 40], [50, 60]], dtype=numpy.float32)
print("Dimensions (ndim):", matrix.ndim) # Outputs: 2
print("Geometry (shape):", matrix.shape) # Outputs: (3, 2) -> 3 rows, 2 columns
print("Total elements (size):", matrix.size) # Outputs: 6
print("Memory cost (nbytes):", matrix.nbytes) # Outputs: 24 bytes (6 items * 4 bytes)Физическое устройство 2D-массива: Strides и C_CONTIGUOUS
Оперативная память компьютера физически является линейной (одномерной). Чтобы хранить двумерную матрицу, NumPy укладывает строки одну за другой в виде единого непрерывного буфера. Такой порядок называется C-style (Row-major) order и отмечается во флагах массива как C_CONTIGUOUS: True.
Из-за линейной укладки атрибут .strides (шаги в байтах) для 2D-матрицы возвращает кортеж из двух чисел: (шаг_по_строкам, шаг_по_столбцам).
- Если матрица
(3, 2)имеет типint64(8 байт), её.stridesбудет равен(16, 8). Это значит: чтобы сдвинуться на один столбец вправо (вдольaxis=1), процессору нужно перешагнуть 8 байт. А чтобы сдвинуться на одну строку вниз (вдольaxis=0), нужно перешагнуть целых 16 байт (пропустить всю текущую строку).
Ручное конструирование матриц с приведением к AI-типам данных
При инициализации матриц весов скрытых слоев или эмбеддингов, хорошим тоном в промышленной разработке является принудительное выставление энергоэффективных низкобитных вещественных типов (таких как numpy.float16). Это экономит видеопамять графического ускорителя (GPU) в 4 раза по сравнению со стандартным форматом двойной точности numpy.float64.
Вы также можете использовать специализированные двумерные конструкторы быстрого заполнения на базе изученных ранее методов: numpy.zeros((rows, cols)), numpy.ones((rows, cols)) и их аналоги _like для копирования структуры.
python
import numpy
# Initializing a low-precision 2D weight matrix for a neural network layer
# Note: dimensions must be passed as a tuple inside the function argument
layer_weights = numpy.ones((4, 2), dtype=numpy.float16)
print(layer_weights) # Outputs: [[1. 1.] [1. 1.] [1. 1.] [1. 1.]]
print(layer_weights.dtype) # Outputs: float16
print(layer_weights.shape) # Outputs: (4, 2)Специализированные конструкторы 2D-матриц и координатные сетки
Единичная матрица (numpy.eye)
Функция numpy.eye(N) мгновенно аллоцирует квадратную матрицу размера N x N с единицами на главной диагонали и нулями во всех остальных ячейках. Единичная матрица I — это нейтральный элемент матричного умножения: для любой матрицы
python
import numpy
# Allocating a 4x4 identity matrix
identity_matrix = numpy.eye(4)
print(identity_matrix)
# Outputs:
# [[1. 0. 0. 0.]
# [0. 1. 0. 0.]
# [0. 0. 1. 0.]
# [0. 0. 0. 1.]]Диагональные матрицы: функция numpy.diag
Функция numpy.diag работает в двух направлениях:
- Вектор → матрица:
numpy.diag(v)превращает 1D-вектор в квадратную диагональную матрицу, разместив элементы вектора на главной диагонали. - Матрица → вектор:
numpy.diag(matrix)извлекает главную диагональ 2D-матрицы в виде 1D-вектора.
python
import numpy
weights_vector = numpy.array([0.5, 0.2, 0.1])
# Vector -> diagonal matrix
diagonal_matrix = numpy.diag(weights_vector)
print("Diagonal matrix:\n", diagonal_matrix)
# Outputs:
# [[0.5 0. 0. ]
# [0. 0.2 0. ]
# [0. 0. 0.1]]
# Matrix -> main diagonal vector
extracted_diagonal = numpy.diag(diagonal_matrix)
print("Extracted diagonal:", extracted_diagonal) # Outputs: [0.5 0.2 0.1]След матрицы (numpy.trace)
След (trace) — это сумма всех элементов главной диагонали квадратной матрицы. Функция numpy.trace(matrix) вычисляет эту метрику одной командой на C-скорости. След широко применяется в линейной алгебре и при отладке диагональных преобразований весов.
python
import numpy
matrix = numpy.array([[1.0, 2.0, 3.0],
[0.0, 5.0, 6.0],
[0.0, 0.0, 7.0]])
print("Matrix trace:", numpy.trace(matrix)) # Outputs: 13.0Координатные сетки: numpy.meshgrid
В задачах построения графиков, генерации координатных карт и обработки изображений часто нужны двумерные массивы, зависящие только от одного индекса: значения строк копируются из вектора (numpy.meshgrid(u, v) за один проход по памяти строит пару таких сеток:
grid_x— каждая строка равна векторуu(зависимость только от столбцового индекса);grid_y— каждый столбец равен векторуv(зависимость только от строкового индекса).
python
import numpy
u_axis = numpy.linspace(1.0, 2.0, 2) # [1. 2.]
v_axis = numpy.linspace(4.0, 8.0, 3) # [4. 6. 8.]
grid_x, grid_y = numpy.meshgrid(u_axis, v_axis)
print("Grid X:\n", grid_x)
# Outputs:
# [[1. 2.]
# [1. 2.]
# [1. 2.]]
print("Grid Y:\n", grid_y)
# Outputs:
# [[4. 4.]
# [6. 6.]
# [8. 8.]]Так как сетка физически содержит лишь копии исходных 1D-векторов, grid_x можно «выпрямить» обратно в вектор через reshape (Урок 18): grid_x.reshape(6) вернет [1. 2. 1. 2. 1. 2.].
Упражнения базовые (5 шт) (только с опорой на объясненную в данном уроке теорию)
1. Ручное создание матрицы признаков
Задача: Создайте двумерную матрицу из вложенного списка Python, содержащую следующие данные: первая строка [1, 2, 3], вторая строка [4, 5, 6]. Выведите матрицу на экран.
Посмотреть решение
python
import numpy
raw_data = [[1, 2, 3], [4, 5, 6]]
# Creating a basic 2D array matrix
features_matrix = numpy.array(raw_data)
print(features_matrix)2. Извлечение геометрических параметров слоя
Задача: Инициализируйте нулевую матрицу весов размерностью 5 строк и 3 столбца с помощью функции numpy.zeros. Выведите в консоль два её атрибута: размерность (.ndim) и кортеж формы (.shape).
Посмотреть решение
python
import numpy
# Shape dimensions must be packed inside a tuple argument
weights_tensor = numpy.zeros((5, 3))
print("Dimensions count:", weights_tensor.ndim) # Outputs: 2
print("Shape geometry tuple:", weights_tensor.shape) # Outputs: (5, 3)3. Оценка физического объема VRAM для датасета
Задача: Представьте, что вы загрузили матрицу признаков, содержащую 1 000 строк (объектов) и 50 столбцов (параметров). Создайте такую матрицу из одних единиц, принудительно выставив тип данных numpy.float32. Выведите на экран общее количество элементов (.size) и точный вес матрицы в байтах (.nbytes).
Посмотреть решение
python
import numpy
# Initializing simulated matrix structure
dataset_matrix = numpy.ones((1000, 50), dtype=numpy.float32)
print("Total elements inside the matrix:", dataset_matrix.size) # 50000
print("Total physical weight in bytes:", dataset_matrix.nbytes) # 200000 bytes (~195 KB)4. Исследование шагов памяти (Strides) по двум осям
Задача: Создайте матрицу размером (2, 4) из целых чисел, явно указав тип данных dtype=numpy.int64. Выведите значение её атрибута .strides. Запишите в комментарии к коду обоснование полученных чисел.
Посмотреть решение
python
import numpy
# Creating row-major aligned matrix block
strict_matrix = numpy.ones((2, 4), dtype=numpy.int64)
print("Matrix strides:", strict_matrix.strides) # Outputs: (32, 8)
# Meaning: Each int64 takes 8 bytes. To move 1 column right, we skip 8 bytes.
# To move 1 row down, we must skip an entire row of 4 items (4 * 8 = 32 bytes).5. Инициализация FP16 матрицы весов скрытого слоя
Задача: Создайте матрицу из одних единиц размером 3 строки на 3 столбца. Задайте ей тип данных полуточности numpy.float16. Выведите матрицу и её тип элементов на экран.
Посмотреть решение
python
import numpy
# Initializing half-precision tensor representation
low_prec_weights = numpy.ones((3, 3), dtype=numpy.float16)
print(low_prec_weights)
print("Data type verification:", low_prec_weights.dtype) # float16Упражнения смешанные (6 шт) (включают материал прошлых уроков)
6. Создание 2D-матрицы на базе структуры сгенерированного вектора
Задача: Сгенерируйте одномерный вектор из 4 элементов типа numpy.float32 (Урок 4). Используя специализированный метод _like (Урок 3), создайте на его основе новую пустую двумерную матрицу размерностью (2, 2), которая автоматически унаследует тип данных float32 исходного вектора (Урок 11). Выведите .dtype и .shape новой матрицы.
Посмотреть решение
python
import numpy
# Creating template 1D vector (Lesson 4)
vector_template = numpy.array([0.1, 0.2, 0.3, 0.4], dtype=numpy.float32)
# Using zeros_like or ones_like requires matching the exact shape,
# so we explicitly pass shape into a standard allocation constructor for a clean 2D layout:
target_matrix = numpy.zeros((2, 2), dtype=vector_template.dtype)
print("Inferred dtype from template:", target_matrix.dtype) # float32
print("Geometric matrix shape configuration:", target_matrix.shape) # (2, 2)7. In-place масштабирование двумерной FP16 матрицы
Задача: Инициализируйте матрицу весов из одних единиц размером (2, 5) типа numpy.float16 (Урок 11). Умножьте эту матрицу на месте (in-place) на скалярный коэффициент 0.25 через оператор *=, чтобы оптимизировать потребление RAM (Урок 8). Выведите итоговый вес матрицы в байтах (Урок 2).
Посмотреть решение
python
import numpy
# Initializing 2D matrix layout with low precision (Lesson 11)
weights_fp16 = numpy.ones((2, 5), dtype=numpy.float16)
# Modifying elements buffer directly inside memory (Lesson 8)
weights_fp16 *= 0.25
print(weights_fp16)
print("Physical RAM footprints cost:", weights_fp16.nbytes, "bytes") # Outputs: 20 bytes8. Провокация аномалий внутри матрицы и проверка метаданных
Задача: Создайте две матрицы размером (2, 2): первую с элементами [[1.0, 1.0], [0.0, 0.0]] и вторую с элементами [[1.0, 0.0], [0.0, 0.0]]. Разделите первую матрицу на вторую поэлементно (Урок 7). В результате возникнут математические аномалии: деление ненулевого числа на ноль даст inf, а деление нуля на ноль даст nan (Урок 9). Выведите полученную матрицу с аномалиями и проверьте, остались ли неизменными её характеристики формы .shape и размерности .ndim (Урок 11).
Посмотреть решение
python
import numpy
# Creating dividend and divisor matrices (Lesson 11)
matrix_a = numpy.array([[1.0, 1.0], [0.0, 0.0]])
matrix_b = numpy.array([[1.0, 0.0], [0.0, 0.0]])
# Element-wise division triggers IEEE 754 warnings (Lesson 7 & Lesson 9)
# [1/1, 1/0] -> [1.0, inf]
# [0/0, 0/0] -> [nan, nan]
anomalous_matrix = matrix_a / matrix_b
print("Anomalous matrix data:\n", anomalous_matrix) # [[ 1. inf] [nan nan]]
print("Shape geometry verification:", anomalous_matrix.shape) # Still (2, 2)
print("Dimensions count verification:", anomalous_matrix.ndim) # Still 29. Поэлементная арифметика над матрицами признаков
Задача: Создайте матрицу matrix_A = numpy.array([[1, 2], [3, 4]]) (Урок 11). Создайте матрицу matrix_B = numpy.array([[10, 20], [30, 40]]). Выполните поэлементное умножение матриц через оператор * (Урок 7). К результату прибавьте скалярную константу numpy.e (Урок 7). Выведите итоговую матрицу.
Посмотреть решение
python
import numpy
matrix_A = numpy.array([[1, 2], [3, 4]])
matrix_B = numpy.array([[10, 20], [30, 40]])
# Element-wise multiplication followed by scalar addition upcast (Lesson 7)
processed_output = (matrix_A * matrix_B) + numpy.e
print(processed_output) # [[ 12.71828183 42.71828183] [ 92.71828183 162.71828183]]10. Явное преобразование типов вещественной матрицы и оценка шагов в памяти
Задача: Создайте линейную числовую сетку по оси X из 6 элементов от 1.5 до 6.5 через linspace (Урок 3). Принудительно приведите этот вектор к типу numpy.int32 с помощью метода .astype() (Урок 7). Представьте, что вы вручную сконструировали из этого новую матрицу признаков размером (2, 3) (Урок 11). Выведите полученную матрицу и .strides сколько байт теперь занимает переход на одну строку вниз.
Посмотреть решение
python
import numpy
# Symmetrical vector generation (Lesson 3)
raw_grid = numpy.linspace(1.5, 6.5, 6)
# Cast truncation to integers (Lesson 7)
int_grid = raw_grid.astype(numpy.int32) # [1 2 3 4 5 6]
# Manual transformation mimicking a 2D dataset space layout (Lesson 11)
dataset_matrix = numpy.array([[int_grid[0], int_grid[1], int_grid[2]],
[int_grid[3], int_grid[4], int_grid[5]]])
print("Matrix configuration:\n", dataset_matrix) # [[1 2 3] [4 5 6]]
print("Calculated strides byte offset paths:", dataset_matrix.strides) # Outputs: (12, 4)
# Verification: Each int32 is 4 bytes. Moving down 1 row means skipping 3 elements (3 * 4 = 12 bytes).11. Составная диагональная матрица: главная и побочная диагонали
Задача: Создайте квадратную матрицу размера 8x8, на главной диагонали которой идет арифметическая прогрессия с шагом 3 (начиная с 3: 3, 6, 9, ..., 24), на побочной диагонали (от левого нижнего угла к правому верхнему) стоят элементы -1, а все остальные элементы равны 0 (Уроки 3, 12). Подсказка: побочная диагональ получается из единичной матрицы разворотом строк: numpy.eye(8)[::-1] (Урок 13). Выведите итоговую матрицу.
Посмотреть решение
python
import numpy
# Flipped identity matrix: the diagonal moves to the anti-diagonal
anti_diagonal = -1 * numpy.eye(8)[::-1]
# Arithmetic progression 3, 6, 9, ... 24 on the main diagonal
main_diagonal = numpy.diag(numpy.arange(3, 27, 3))
# Superposition of two sparse diagonal structures
composed_matrix = anti_diagonal + main_diagonal
print(composed_matrix)
# Outputs:
# [[ 3. 0. 0. 0. 0. 0. 0. -1.]
# [ 0. 6. 0. 0. 0. 0. -1. 0.]
# [ 0. 0. 9. 0. 0. -1. 0. 0.]
# [ 0. 0. 0. 12. -1. 0. 0. 0.]
# [ 0. 0. 0. -1. 15. 0. 0. 0.]
# [ 0. 0. -1. 0. 0. 18. 0. 0.]
# [ 0. -1. 0. 0. 0. 0. 21. 0.]
# [-1. 0. 0. 0. 0. 0. 0. 24.]]