当前位置:首页>python>Python 线性代数 01:向量、矩阵与 NumPy 数组

Python 线性代数 01:向量、矩阵与 NumPy 数组

  • 2026-10-11 06:42:12
Python 线性代数 01:向量、矩阵与 NumPy 数组

向量和矩阵是线性代数中组织数值的基本形式,也是 AI 编程中表示样本、特征、模型参数和批量数据的重要工具。一个样本可以表示为向量,一批样本可以组成矩阵,图像、序列和神经网络的中间结果则常使用更高维数组表示。

在线性代数中,需要关注向量的分量、矩阵的行列结构以及相应的尺寸;在 Python 和 NumPy 中,还需要理解数组的维度数、形状、轴、索引、切片和数据类型。

一、标量、向量与矩阵

1、标量

标量(Scalar)是数域中的一个元素,常见的标量包括实数和复数。标量本身没有向量分量或矩阵的行列结构。

在实数范围内,可以设:

例如:

其中,a、b、c 是标量变量,3、−2 和 0.5 是它们所取的标量值。

复数也可以作为标量。例如:

系列文章中若无特别说明,向量和矩阵的元素均取自实数域 ℝ;对于复数向量和复数矩阵,可以将 ℝ 相应替换为 ℂ。

在 AI 编程中,学习率、损失值和正则化系数通常使用标量表示;向量和矩阵中的每个元素也都是标量。

2、向量

向量(Vector)是由若干个有序分量组成的数学对象。一个 n 维向量可以写成:

例如:

向量 v 包含 3 个分量,因此是三维向量。

为了统一符号,以下采用线性代数中的常见约定,将向量写成列向量。对应的行向量使用列向量的转置表示:

列向量 v 的尺寸为 3 × 1,行向量 vᵀ 的尺寸为 1 × 3。转置改变行列形式,不改变向量包含的三个分量。

向量的维数是它包含的分量数量。

3、矩阵

矩阵(Matrix)是按照行和列排列的数值结构。一个 m × n 矩阵可以写成:

其中,aᵢⱼ 表示矩阵 A 中第 i 行、第 j 列的元素。

例如:

矩阵 A 有 2 行、3 列,因此尺寸为 2 × 3。

其中:

4、矩阵的行与列

矩阵的每一行和每一列都可以单独看作一个向量。

矩阵 A 的第 i 行可以写成:

这一行包含 n 个元素,可以看作一个 n 维行向量,尺寸为 1 × n。

矩阵 A 的第 j 列可以写成:

这一列包含 m 个元素,可以看作一个 m 维列向量,尺寸为 m × 1。

对于:

第 1 行是:

第 2 列是:

因此,一个 m × n 矩阵既可以看作由 m 个 n 维行向量按行排列而成,也可以看作由 n 个 m 维列向量按列排列而成。

二、使用 Python 基本语法表示向量和矩阵

1、标量表示

在 Python 中,整数、浮点数和复数都可以表示标量:

count = 3learning_rate = 0.01complex_value = 2 + 3jprint(type(count))print(type(learning_rate))print(type(complex_value))

输出:

<class 'int'><class 'float'><class 'complex'>

2、使用列表表示向量

Python 可以使用列表保存向量的各个分量:

v = [2, -1, 3]print(v)print(len(v))

输出:

[2, -1, 3]3

len(v) 返回列表中的元素数量,对应向量包含的分量数。

可以通过索引访问具体分量:

print(v[0])print(v[1])print(v[2])

输出:

2-13

Python 索引从 0 开始,因此:

v[0] 对应数学中的 v₁;

v[1] 对应数学中的 v₂;

v[2] 对应数学中的 v₃。

Python 列表只保存一组有序元素,本身不区分列向量和行向量。

3、使用嵌套列表表示矩阵

矩阵可以使用嵌套列表表示。外层列表保存各行,内层列表保存每一行的元素:

A = [    [1, 2, 3],    [4, 5, 6]]print(A)

输出:

[[1, 2, 3], [4, 5, 6]]

访问第 1 行:

print(A[0])

输出:

[1, 2, 3]

访问第 2 行、第 3 列的元素:

print(A[1][2])

输出:

6

4、检查矩阵形状

对于结构规则的嵌套列表:

A = [    [1, 2, 3],    [4, 5, 6]]rows = len(A)columns = len(A[0])print(rows)print(columns)

输出:

23

这种写法默认每一行的长度都相同。如果各行长度不同,就不能表示规则矩阵。

可以通过自定义函数检查嵌套列表的结构:

def get_matrix_shape(matrix):    # 检查矩阵是否为空    if not matrix:        return 0, 0    columns = len(matrix[0])    # 检查各行长度是否相同    if any(len(row) != columns for row in matrix):        raise ValueError("各行长度必须相同")    return len(matrix), columnsA = [    [1, 2, 3],    [4, 5, 6]]print(get_matrix_shape(A))

输出:

(2, 3)

三、创建 NumPy 数组

NumPy 使用 ndarray 对象表示多维数组。通常先导入 NumPy:

import numpy as np

1、零维数组

单个数值可以转换为零维 NumPy 数组:

scalar_array = np.array(3.5)print(scalar_array)print(scalar_array.ndim)print(scalar_array.shape)

输出:

3.50()

零维数组保存一个标量值,但它仍然是 ndarray 对象,不是普通的 Python 标量。

可以使用 item() 方法取得其中的 Python 标量:

value = scalar_array.item()print(value)print(type(value))

输出:

3.5<class 'float'>

2、一维数组

可以将一层 Python 列表传给 np.array(),创建一维 NumPy 数组:

v = np.array([2, -1, 3])print(v)print(v.ndim)print(v.shape)

输出:

[ 2 -1  3]1(3,)

这个数组包含 3 个元素,只有一个轴,因此形状为 (3,)。

(3,) 中的逗号表示它是一个只有一个元素的元组。

3、二维数组

可以将规则嵌套列表传给 np.array(),创建二维 NumPy 数组:

A = np.array([    [1, 2, 3],    [4, 5, 6]])print(A)print(A.ndim)print(A.shape)

输出:

[[1 2 3] [4 5 6]]2(2, 3)

这个数组有两个轴:

第 0 轴的长度为 2;

第 1 轴的长度为 3。

数学中的矩阵尺寸(Matrix Size)写成 2 × 3,NumPy 数组的形状写成 (2, 3)。两者描述相同的行列结构,但符号形式和使用场景不同。

4、常用数组属性

NumPy 数组的常用属性包括:

ndim:数组包含的轴数;

shape:每个轴上的元素数量;

size:数组中的元素总数;

dtype:数组元素的数据类型。

例如:

A = np.array([    [1, 2, 3],    [4, 5, 6]])print(A.ndim)print(A.shape)print(A.size)print(A.dtype)

输出结果可能为:

2(2, 3)6int64

在部分系统中,整数类型可能显示为 int32。

四、NumPy 一维数组与数学上的行/列向量

1、一维数组没有行列方向

创建一维数组:

v = np.array([1, 2, 3])print(v.shape)

输出:

(3,)

此一维数组的形状为 (3,),表示一维数组只有一个轴,因此它既不能表示数学上的行向量,也不能表示列向量。

而对应的数学中的列向量尺寸为 3 × 1:

在 NumPy 中若要保留明确的列结构,应使用形状 (3, 1),而不是 (3,)。

2、对一维数组转置

对一维数组使用转置属性 .T:

v = np.array([1, 2, 3])print(v.T)print(v.T.shape)

输出:

[1 2 3](3,)

一维数组只有一个轴,没有两个轴可以交换,因此转置不会改变其形状。

3、二维数组与行向量

可以使用 reshape() 方法把一维数组转换为二维数组:

v = np.array([1, 2, 3])row_vector = v.reshape(1, -1)print(row_vector)print(row_vector.shape)

输出:

[[1 2 3]](1, 3)

形状为 (1, 3) 的二维数组对应数学中尺寸为 1 × 3 的行向量:

4、二维列向量

可以把一维数组转换为二维数组:

column_vector = v.reshape(-1, 1)print(column_vector)print(column_vector.shape)

输出:

[[1] [2] [3]](3, 1)

形状为 (3, 1) 的二维数组对应数学中尺寸为 3 × 1 的列向量:

5、使用 np.newaxis 增加轴

也可以使用 np.newaxis 对一维数组增加轴,从而创建二维行结构或列结构:

v = np.array([1, 2, 3])row_vector = v[np.newaxis, :]column_vector = v[:, np.newaxis]print(row_vector.shape)print(column_vector.shape)

输出:

(1, 3)(3, 1)

其中:

v[np.newaxis, :] 在数组前面增加一个轴;

v[:, np.newaxis] 在数组后面增加一个轴。

形状 (3,)、(1, 3) 和 (3, 1) 包含相同的三个数值,但在矩阵乘法、广播和批量计算中可能产生不同结果。

五、维度、形状与轴

1、向量维数与数组维度数

需要区分数学中的向量维数和 NumPy 中数组的维度数。

数学中的向量:

包含 3 个分量,因此是三维向量。

对应的一维 NumPy 数组:

v = np.array([1, 2, 3])

于是:

向量维数为 3;

NumPy 数组的维度数(ndim)为 1;

NumPy 数组的形状(shape)为 (3,)。

这些概念描述的对象不同,不能混用。

2、数组形状

形状(shape)表示每个轴上的元素数量:

A = np.array([    [1, 2, 3],    [4, 5, 6]])print(A.shape)

输出:

(2, 3)

其中:

A.shape[0] 为 2;

A.shape[1] 为 3。

可以分别获取:

rows = A.shape[0]columns = A.shape[1]print(rows)print(columns)

输出:

23

3、二维数组的轴

设:

A = np.array([    [1, 2, 3],    [4, 5, 6]])

对应数学矩阵:

数组形状为 (2, 3)。

沿 axis=0 求和时,第 0 轴被压缩,分别对每一列求和:

对应代码:

print(A.sum(axis=0))

输出:

[5 7 9]

结果形状为 (3,)。

沿 axis=1 求和时,第 1 轴被压缩,分别对每一行求和:

对应代码:

print(A.sum(axis=1))

输出:

[ 6 15]

结果形状为 (2,)。

指定某个轴执行缩减运算时,该轴通常会消失,其他轴被保留下来。

设置 keepdims=True 可以将被缩减的轴保留为长度 1:

column_sums = A.sum(    axis=0,    keepdims=True)row_sums = A.sum(    axis=1,    keepdims=True)print(column_sums)print(column_sums.shape)print(row_sums)print(row_sums.shape)

输出:

[[5 7 9]](1, 3)[[ 6] [15]](2, 1)

4、三维数组的轴

创建三维数组:

data = np.array([    [        [1, 2, 3],        [4, 5, 6]    ],    [        [7, 8, 9],        [10, 11, 12]    ]])print(data.ndim)print(data.shape)

输出:

3(2, 2, 3)

可以把这个数组理解为:

第 0 轴包含 2 个二维数组;

第 1 轴表示每个二维数组中的 2 行;

第 2 轴表示每行中的 3 个元素。

三维 NumPy 数组不属于通常所说的二维矩阵表示。矩阵具有两个轴,分别对应行和列;三维数组则具有三个轴。

六、数组的索引与切片

1、一维数组索引

v = np.array([10, 20, 30, 40])print(v[0])print(v[-1])

输出:

1040

其中:

v[0] 取得第 1 个元素;

v[-1] 取得最后一个元素。

使用切片取得部分元素:

print(v[1:3])

输出:

[20 30]

切片范围包含起始位置,不包含结束位置。

2、二维数组索引

A = np.array([    [1, 2, 3],    [4, 5, 6],    [7, 8, 9]])print(A[1, 2])

输出:

6

A[1, 2] 表示第 2 行、第 3 列的元素。

也可以写成:

print(A[1][2])

不过,A[1, 2] 更直接,也更符合 NumPy 多维索引的写法。

3、提取一行

提取第 1 行:

first_row = A[0, :]print(first_row)print(first_row.shape)

输出:

[1 2 3](3,)

使用整数索引后,第 0 轴消失,因此结果是一维数组。

如果需要保留二维行结构,可以使用切片:

first_row_2d = A[0:1, :]print(first_row_2d)print(first_row_2d.shape)

输出:

[[1 2 3]](1, 3)

4、提取一列

提取第 1 列:

first_column = A[:, 0]print(first_column)print(first_column.shape)

输出:

[1 4 7](3,)

如果需要保留二维列结构,同样可以使用切片:

first_column_2d = A[:, 0:1]print(first_column_2d)print(first_column_2d.shape)

输出:

[[1] [4] [7]](3, 1)

也可以使用整数列表索引:

first_column_2d = A[:, [0]]

因此:

A[:, 0] 的形状为 (3,);

A[:, 0:1] 的形状为 (3, 1);

A[:, [0]] 的形状也为 (3, 1)。

在二维数组中,单个整数索引会移除对应轴;切片或单元素列表索引可以保留二维结构。

5、提取子矩阵

submatrix = A[0:2, 1:3]print(submatrix)print(submatrix.shape)

输出:

[[2 3] [5 6]](2, 2)

这里提取了第 1~2 行和第 2~3 列。

七、形状变换与数组组合

1、使用 reshape() 改变形状

reshape() 方法可以改变数组的形状和索引组织方式,但不改变元素总数。

data = np.array([    1,    2,    3,    4,    5,    6])matrix = data.reshape(2, 3)print(matrix)print(matrix.shape)

输出:

[[1 2 3] [4 5 6]](2, 3)

原数组形状为 (6,),新数组形状为 (2, 3)。

变形前后的元素总数必须相同:

reshape() 中可以使用 -1,让 NumPy 自动推断对应轴的长度:

matrix = data.reshape(3, -1)print(matrix)print(matrix.shape)

输出:

[[1 2] [3 4] [5 6]](3, 2)

一个 reshape() 中最多只能使用一个 -1。

reshape() 会尽可能返回视图;若当前内存布局不支持目标形状,则可能创建副本。

2、展平数组

使用 flatten() 方法可以将多维数组展平为一维数组:

A = np.array([    [1, 2, 3],    [4, 5, 6]])flattened = A.flatten()print(flattened)print(flattened.shape)

输出:

[1 2 3 4 5 6](6,)

flatten() 返回新的独立数组。

也可以使用 ravel() 方法:

flattened = A.ravel()

ravel() 会尽可能返回原数组的视图;无法共享原数据时才创建副本。

3、增加和删除长度为 1 的轴

使用 np.expand_dims() 函数增加轴:

v = np.array([1, 2, 3])expanded = np.expand_dims(    v,    axis=0)print(expanded)print(expanded.shape)

输出:

[[1 2 3]](1, 3)

使用 np.squeeze() 函数删除长度为 1 的轴:

restored = np.squeeze(    expanded,    axis=0)print(restored)print(restored.shape)

输出:

[1 2 3](3,)

如果省略 axis 参数,np.squeeze() 会删除所有长度为 1 的轴。

4、堆叠与连接数组

np.stack() 函数会创建一个新轴:

v1 = np.array([1, 2, 3])v2 = np.array([4, 5, 6])rows = np.stack(    [v1, v2],    axis=0)columns = np.stack(    [v1, v2],    axis=1)print(rows)print(rows.shape)print(columns)print(columns.shape)

输出:

[[1 2 3] [4 5 6]](2, 3)[[1 4] [2 5] [3 6]](3, 2)

np.concatenate() 不创建新轴,而是沿已有轴连接数组:

A = np.array([    [1, 2],    [3, 4]])B = np.array([    [5, 6]])combined = np.concatenate(    [A, B],    axis=0)print(combined)print(combined.shape)

输出:

[[1 2] [3 4] [5 6]](3, 2)

八、数据类型与内存共享

1、数组元素的数据类型

每个 NumPy 数组都具有统一的 dtype,用于描述数组元素的存储类型:

A = np.array([1, 2, 3])print(A.dtype)

输出结果可能为:

int64

创建浮点数组:

A = np.array(    [1, 2, 3],    dtype=float)print(A)print(A.dtype)

输出:

[1. 2. 3.]float64

在线性代数中的归一化、方程求解和其他连续数值运算中,通常需要使用浮点类型。

2、转换数据类型

可以使用 astype() 方法转换数据类型:

A = np.array([1, 2, 3])B = A.astype(float)print(B)print(B.dtype)

输出:

[1. 2. 3.]float64

astype() 默认返回新数组,不修改原数组的数据类型。

3、混合数据会进行统一转换

A = np.array([1, 2.5, 3])print(A)print(A.dtype)

输出:

[1.  2.5 3. ]float64

因为数组中包含浮点数,整数会被统一转换为浮点数。

如果混合数值与字符串:

A = np.array([1, 2, "3"])print(A)print(A.dtype)

NumPy 通常会把所有元素转换为字符串。这样的数组不能直接进行一般线性代数运算。

4、不规则嵌套列表

下面的嵌套列表各行长度不同:

data = [    [1, 2, 3],    [4, 5]]

新版 NumPy 通常会拒绝直接将其转换为规则数值数组:

A = np.array(data)

此时会抛出 ValueError。

如果明确指定:

A = np.array(    data,    dtype=object)

可以创建 object 数组,但其中保存的是普通 Python 对象,不适合常规矩阵计算。

5、基本切片通常返回视图

A = np.array([    10,    20,    30,    40])part = A[1:3]part[0] = 99print(part)print(A)

输出:

[99 30][10 99 30 40]

NumPy 的基本切片通常返回原数组的视图,因此修改 part 也会影响 A。

如果需要独立数组,应显式复制:

part = A[1:3].copy()

6、高级索引返回副本

使用整数数组或整数列表进行高级索引时,结果通常是副本:

A = np.array([    10,    20,    30,    40])part = A[[1, 2]]part[0] = 99print(part)print(A)

输出:

[99 30][10 20 30 40]

修改 part 不会影响原数组 A。

九、数组在 AI 编程中的典型应用

1、单个样本

假设一个样本包含 D 个特征,在数学中可以表示为:

在 NumPy 中,单个样本通常保存为一维数组。例如:

sample = np.array([    0.8,    1.2,    -0.5,    2.0])print(sample.shape)

输出:

(4,)

这里的 4 表示特征数量。

2、批量样本

如果约定每个样本占据一行,一共有 N 个样本,每个样本包含 D 个特征,则数据矩阵可以写成:

其中:

N 表示样本数量;

D 表示每个样本的特征数量;

第 i 行表示第 i 个样本;

第 j 列表示所有样本的第 j 个特征。

NumPy 示例:

X = np.array([    [0.8, 1.2, -0.5, 2.0],    [1.0, 0.7, 0.3, 1.5],    [0.2, 1.8, -0.1, 0.9]])print(X.shape)

输出:

(3, 4)

这里:

3 表示样本数量;

4 表示特征数量。

本系列文章涉及批量数据时,通常采用“样本按行、特征按列”的约定;若某个公式采用其他排列方式,将单独说明。

3、标签数组

如果每个样本对应一个标量标签,可以写成:

在 NumPy 中,标签通常保存为一维数组:

y = np.array([0, 1, 0])print(y.shape)

输出:

(3,)

如果后续运算需要二维列结构,可以转换为:

y_column = y.reshape(-1, 1)print(y_column)print(y_column.shape)

输出:

[[0] [1] [0]](3, 1)

选择 (N,) 还是 (N, 1),需要根据后续运算和函数接口决定。

4、图像数据

一张灰度图像可以表示为二维数组:

(height, width)

一张通道位于最后一轴的彩色图像可以表示为:

(height, width, channels)

一批彩色图像可以表示为:

(batch, height, width, channels)

例如:

images = np.zeros(    (32, 64, 64, 3))print(images.shape)

输出:

(32, 64, 64, 3)

表示:

32 张图像;

每张图像高 64;

每张图像宽 64;

包含 3 个颜色通道。

PyTorch 图像张量通常采用:

(batch, channels, height, width)

因此,在 NumPy 与深度学习框架之间传递图像时,需要确认轴的排列顺序。

5、序列数据

一批序列数据常使用形状:

(batch, sequence_length, feature_dim)

例如:

sequences = np.zeros(    (16, 20, 128))print(sequences.shape)

输出:

(16, 20, 128)

可以理解为:

16 个序列;

每个序列包含 20 个位置;

每个位置由 128 维特征表示。

在实际数据处理中,数组的每个轴通常对应一种明确含义。只有理解各轴表示什么,才能正确执行转置、求和、广播和矩阵乘法。

十、常见问题及使用建议

1、区分数学对象与 NumPy 数组概念

数学中的向量和矩阵,与 NumPy 中的数组表示方式并不是完全对应的概念。

数学中的:

向量维数表示向量包含的分量数量;

行向量和列向量表示不同的排列形式;

矩阵尺寸表示行数与列数。

NumPy 中的:

ndim 表示数组包含的轴数;

shape 表示每个轴上的元素数量;

size 表示数组中的元素总数。

例如,一维数组 (3,) 包含 3 个元素,但它没有明确的行列方向;只有转换为 (1, 3) 或 (3, 1) 的二维数组后,才对应数学中的行向量或列向量。

又比如,数学中的三维向量表示具有 3 个分量的向量,指的是向量维度(Vector Dimension),而 NumPy 的三维数组表示具有 3 个轴的数据结构,指的是数组维度(Array Dimensionality)。

理解这些概念的区别,是正确进行索引、矩阵运算、广播和批量计算的基础。

2、注意整数索引会减少轴

column = A[:, 0]

结果是一维数组。

需要保留二维列结构时,应使用:

column = A[:, 0:1]

或:

column = A[:, [0]]

3、注意切片可能与原数组共享数据

NumPy 基本切片通常返回视图。修改切片结果时,原数组可能同时发生变化。

需要独立数据时,应使用:

part = A[0:2].copy()

4、注意数组的数据类型

整数数组适合保存计数、索引和分类标签,但归一化、方程求解和连续数值运算通常需要浮点类型。

可以在创建数组时指定:

A = np.array(    data,    dtype=float)

5、同时检查数值和形状

下面三种数组可能包含相同数值:

(3,)(1, 3)(3, 1)

但它们在矩阵乘法、广播和批量计算中的行为不同。

调试线性代数程序时,应经常检查:

print(array.shape)print(array.ndim)print(array.dtype)

📘 小结

向量和矩阵是线性代数中组织数值的基本结构,NumPy 则使用不同维度和形状的数组表示这些数学对象。理解向量维数、矩阵尺寸与数组的 ndim、shape、size 之间的区别,是进行后续向量运算、矩阵运算和批量数据处理的基础。

“点赞有美意,赞赏是鼓励”

最新文章

随机文章