华为AI机试NumPy速查手册

NumPy速查手册(考试必备)

适用人群:华为AI机试备考者、NumPy初学者、需要快速查阅的开发者

阅读时间:完整阅读约60分钟,快速查阅5分钟

使用建议:先看”从零开始”章节建立基础,再按需查阅具体函数,考前重点背”考场救命代码片段”


零、从零开始:5分钟入门 NumPy 🚀

0.1 什么是 NumPy?

NumPy(Numerical Python)是Python科学计算的基础库,核心是多维数组对象 ndarray

为什么要用NumPy?

  • :底层C语言实现,比Python列表快10-100倍
  • 省内存:紧凑存储,比列表节省内存
  • 方便:向量化操作,一行代码搞定循环
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# ❌ Python列表方式(慢)
import time
data = list(range(1000000))
start = time.time()
result = [x ** 2 for x in data]
print(f"列表耗时: {time.time() - start:.4f}秒")
# 列表耗时: 0.0850秒

# ✅ NumPy方式(快)
import numpy as np
data = np.arange(1000000)
start = time.time()
result = data ** 2
print(f"NumPy耗时: {time.time() - start:.4f}秒")
# NumPy耗时: 0.0012秒(快70倍!)

0.2 第一个NumPy程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import numpy as np

# 创建数组
arr = np.array([1, 2, 3, 4, 5])
print(arr)
# [1 2 3 4 5]

print(type(arr))
# <class 'numpy.ndarray'>

# 基本运算(自动应用到每个元素)
print(arr * 2)
# [ 2 4 6 8 10]

print(arr + 10)
# [11 12 13 14 15]

print(arr ** 2)
# [ 1 4 9 16 25]

0.3 NumPy vs Python列表

特性 NumPy数组 Python列表
元素类型 必须相同 可以不同
大小 创建后固定 可动态增长
运算 支持向量化 需要循环
内存 连续存储 分散存储
速度 快(C实现) 慢(Python)
1
2
3
4
5
6
7
8
# Python列表:元素可以不同类型
py_list = [1, "hello", 3.14, True] # ✅ 可以

# NumPy数组:元素必须同类型
np_array = np.array([1, 2, 3, 4]) # ✅ 全是整数
np_array = np.array([1, "hello"]) # 会自动转换为字符串
print(np_array)
# ['1' 'hello'] # 全部变成字符串

0.4 核心概念:轴(axis)

理解axis是掌握NumPy的关键!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 二维数组
arr = np.array([[1, 2, 3],
[4, 5, 6]])

print(arr.shape) # (2, 3) - 2行3列

# axis=0: 沿着行方向(垂直,跨行操作)
print(np.sum(arr, axis=0))
# [5 7 9] # 每列求和:[1+4, 2+5, 3+6]

# axis=1: 沿着列方向(水平,跨列操作)
print(np.sum(arr, axis=1))
# [ 6 15] # 每行求和:[1+2+3, 4+5+6]

# 不指定axis: 对所有元素操作
print(np.sum(arr))
# 21 # 所有元素和

记忆技巧

  • axis=0:想象手指从上往下按,压缩行,留下列
  • axis=1:想象手指从左往右按,压缩列,留下行

0.5 五个最常用操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. 创建数组
arr = np.array([1, 2, 3, 4, 5])

# 2. 查看形状
print(arr.shape) # (5,)

# 3. 改变形状
arr_2d = arr.reshape(5, 1)
print(arr_2d.shape) # (5, 1)

# 4. 索引切片
print(arr[0]) # 1 - 第一个元素
print(arr[1:4]) # [2 3 4] - 第2到第4个元素
print(arr[-1]) # 5 - 最后一个元素

# 5. 条件筛选
print(arr[arr > 3]) # [4 5] - 大于3的元素

现在你已经掌握了NumPy的80%基础! 接下来深入学习各个功能。


一、数组创建⭐⭐⭐⭐⭐

1.1 基础创建

np.array() - 从列表/元组创建

参数说明

  • object:列表、元组或其他序列
  • dtype:数据类型(可选)
  • ndmin:最小维度数(可选)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import numpy as np

# 示例1:从列表创建
arr = np.array([1, 2, 3, 4, 5])
print(arr)
# [1 2 3 4 5]
print(arr.dtype)
# int64(或int32,取决于系统)

# 示例2:从二维列表创建矩阵
matrix = np.array([[1, 2, 3],
[4, 5, 6]])
print(matrix)
# [[1 2 3]
# [4 5 6]]
print(matrix.shape)
# (2, 3)

# 示例3:指定数据类型
arr_float = np.array([1, 2, 3], dtype=np.float32)
print(arr_float)
# [1. 2. 3.]
print(arr_float.dtype)
# float32

arr_float64 = np.array([1, 2, 3], dtype='float64')
print(arr_float64.dtype)
# float64

# 示例4:从元组创建
arr_tuple = np.array((10, 20, 30))
print(arr_tuple)
# [10 20 30]

# 示例5:指定最小维度
arr_2d = np.array([1, 2, 3], ndmin=2)
print(arr_2d)
# [[1 2 3]]
print(arr_2d.shape)
# (1, 3)

常见数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 整数类型
np.int8 # -128 到 127
np.int16 # -32768 到 32767
np.int32 # -2^31 到 2^31-1
np.int64 # -2^63 到 2^63-1
np.uint8 # 0 到 255(无符号)

# 浮点类型
np.float16 # 半精度浮点
np.float32 # 单精度浮点
np.float64 # 双精度浮点(默认)

# 其他类型
np.bool_ # True/False
np.complex64 # 复数

常见错误对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# ❌ 错误1:不同长度的子列表
try:
arr = np.array([[1, 2, 3], [4, 5]]) # 长度不一致
except ValueError as e:
print("错误:子列表长度必须相同")
# 实际会创建object数组,不报错但不推荐

# ✅ 正确:长度一致
arr = np.array([[1, 2, 3], [4, 5, 6]])

# ❌ 错误2:忘记方括号
arr = np.array(1, 2, 3) # SyntaxError

# ✅ 正确:用列表包裹
arr = np.array([1, 2, 3])

# ❌ 错误3:类型不兼容
arr = np.array([1, 2, 3], dtype=np.int32)
arr[0] = 3.7 # 会截断为3,不报错!
print(arr)
# [3 2 3] # 3.7被截断为3

# ✅ 正确:使用float类型
arr = np.array([1, 2, 3], dtype=np.float32)
arr[0] = 3.7
print(arr)
# [3.7 2. 3. ]

性能优化技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import time

# 技巧1:预先指定dtype可以节省内存
arr_int64 = np.array([1, 2, 3] * 1000000) # 默认int64
print(f"int64内存: {arr_int64.nbytes / 1024 / 1024:.2f} MB")
# int64内存: 22.89 MB

arr_int32 = np.array([1, 2, 3] * 1000000, dtype=np.int32)
print(f"int32内存: {arr_int32.nbytes / 1024 / 1024:.2f} MB")
# int32内存: 11.44 MB(节省50%)

# 技巧2:大数据时避免从嵌套列表创建,改用专门函数
# ❌ 慢:从列表创建
start = time.time()
arr = np.array([[i+j for j in range(1000)] for i in range(1000)])
print(f"列表方式: {time.time() - start:.4f}秒")
# 列表方式: 0.1234秒

# ✅ 快:使用专门函数
start = time.time()
arr = np.arange(1000000).reshape(1000, 1000)
print(f"函数方式: {time.time() - start:.4f}秒")
# 函数方式: 0.0023秒(快50倍)

1.2 特殊数组创建

np.zeros() - 创建全0数组

参数说明

  • shape:数组形状,整数或元组
  • dtype:数据类型,默认float64
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 示例1:一维全0数组
zeros_1d = np.zeros(5)
print(zeros_1d)
# [0. 0. 0. 0. 0.]

# 示例2:二维全0矩阵
zeros_2d = np.zeros((3, 4))
print(zeros_2d)
# [[0. 0. 0. 0.]
# [0. 0. 0. 0.]
# [0. 0. 0. 0.]]

# 示例3:指定整数类型
zeros_int = np.zeros((2, 3), dtype=np.int32)
print(zeros_int)
# [[0 0 0]
# [0 0 0]]

# 示例4:三维数组
zeros_3d = np.zeros((2, 3, 4))
print(zeros_3d.shape)
# (2, 3, 4)

np.ones() - 创建全1数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 示例1:一维全1数组
ones_1d = np.ones(5)
print(ones_1d)
# [1. 1. 1. 1. 1.]

# 示例2:用于初始化权重
weights = np.ones((100, 10)) * 0.01 # 初始化为0.01
print(weights[0])
# [0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01]

# 示例3:创建掩码
mask = np.ones(100, dtype=bool)
mask[:10] = False # 前10个设为False
print(mask[:15])
# [False False False False False False False False False False True True
# True True True]

np.full() - 创建指定值数组

参数说明

  • shape:数组形状
  • fill_value:填充值
  • dtype:数据类型(可选)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 示例1:填充特定值
full_7 = np.full((2, 3), 7)
print(full_7)
# [[7 7 7]
# [7 7 7]]

# 示例2:填充浮点数
full_pi = np.full((3, 3), 3.14159)
print(full_pi)
# [[3.14159 3.14159 3.14159]
# [3.14159 3.14159 3.14159]
# [3.14159 3.14159 3.14159]]

# 示例3:机器学习中的初始化
bias = np.full(10, 0.1) # 偏置初始化为0.1
print(bias)
# [0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1]

np.eye() / np.identity() - 单位矩阵

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 示例1:3×3单位矩阵
identity_3 = np.eye(3)
print(identity_3)
# [[1. 0. 0.]
# [0. 1. 0.]
# [0. 0. 1.]]

# 示例2:非方阵单位矩阵
eye_rect = np.eye(3, 5) # 3行5列
print(eye_rect)
# [[1. 0. 0. 0. 0.]
# [0. 1. 0. 0. 0.]
# [0. 0. 1. 0. 0.]]

# 示例3:偏移对角线
eye_offset = np.eye(4, k=1) # 对角线上移1
print(eye_offset)
# [[0. 1. 0. 0.]
# [0. 0. 1. 0.]
# [0. 0. 0. 1.]
# [0. 0. 0. 0.]]

# 示例4:用于one-hot编码
labels = np.array([0, 2, 1, 0, 3])
one_hot = np.eye(4)[labels] # 4个类别
print(one_hot)
# [[1. 0. 0. 0.] # 类别0
# [0. 0. 1. 0.] # 类别2
# [0. 1. 0. 0.] # 类别1
# [1. 0. 0. 0.] # 类别0
# [0. 0. 0. 1.]] # 类别3

np.diag() - 对角矩阵

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 示例1:从向量创建对角矩阵
diag_1 = np.diag([1, 2, 3, 4])
print(diag_1)
# [[1 0 0 0]
# [0 2 0 0]
# [0 0 3 0]
# [0 0 0 4]]

# 示例2:提取对角线
matrix = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
diagonal = np.diag(matrix)
print(diagonal)
# [1 5 9]

# 示例3:偏移对角线
matrix = np.arange(1, 10).reshape(3, 3)
upper_diag = np.diag(matrix, k=1) # 上对角线
print(upper_diag)
# [2 6]

lower_diag = np.diag(matrix, k=-1) # 下对角线
print(lower_diag)
# [4 8]

np.empty() - 创建未初始化数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 示例1:快速创建(内容随机)
empty = np.empty((2, 3))
print(empty)
# [[6.23042070e-307 4.67296746e-307 1.69121096e-306]
# [1.33511969e-306 1.78019082e-306 1.05699042e-307]]
# 注意:内容是内存中的随机值

# 示例2:性能对比
import time
n = 10000

start = time.time()
for _ in range(1000):
arr = np.zeros((n,))
print(f"zeros: {time.time() - start:.4f}秒")
# zeros: 0.0234秒

start = time.time()
for _ in range(1000):
arr = np.empty((n,))
print(f"empty: {time.time() - start:.4f}秒")
# empty: 0.0012秒(快20倍)

# 示例3:使用场景(立即会被填充的数组)
empty_arr = np.empty(100)
for i in range(100):
empty_arr[i] = i ** 2 # 立即填充,不需要初始化为0

np.zeros_like() / np.ones_like() - 创建同形状数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 示例1:创建同形状的全0数组
original = np.array([[1, 2, 3], [4, 5, 6]])
zeros_same = np.zeros_like(original)
print(zeros_same)
# [[0 0 0]
# [0 0 0]]

# 示例2:创建同形状的全1数组
ones_same = np.ones_like(original)
print(ones_same)
# [[1 1 1]
# [1 1 1]]

# 示例3:保持数据类型
float_arr = np.array([1.5, 2.7, 3.9])
zeros_float = np.zeros_like(float_arr)
print(zeros_float)
# [0. 0. 0.]
print(zeros_float.dtype)
# float64

# 示例4:在神经网络中初始化梯度
weights = np.random.randn(100, 50)
gradients = np.zeros_like(weights) # 梯度初始化为0
print(gradients.shape)
# (100, 50)

常见错误对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# ❌ 错误1:zeros的shape参数忘记用元组
arr = np.zeros(3, 4) # TypeError

# ✅ 正确
arr = np.zeros((3, 4))

# ❌ 错误2:empty期望得到全0
arr = np.empty(5)
print(arr) # 不是[0. 0. 0. 0. 0.],是随机值

# ✅ 正确:需要0就用zeros
arr = np.zeros(5)

# ❌ 错误3:eye的参数理解错误
arr = np.eye((3, 3)) # TypeError,不需要元组

# ✅ 正确
arr = np.eye(3) # 或 np.eye(3, 3)

性能对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import time

# 创建100万个元素的数组
n = 1000000

# zeros: 需要初始化为0
start = time.time()
arr1 = np.zeros(n)
t1 = time.time() - start

# empty: 不初始化
start = time.time()
arr2 = np.empty(n)
t2 = time.time() - start

# full: 初始化为特定值
start = time.time()
arr3 = np.full(n, 7)
t3 = time.time() - start

print(f"zeros: {t1*1000:.2f}ms")
print(f"empty: {t2*1000:.2f}ms (快{t1/t2:.1f}倍)")
print(f"full: {t3*1000:.2f}ms")
# zeros: 1.23ms
# empty: 0.05ms (快24.6倍)
# full: 1.45ms

1.3 序列生成

np.arange() - 等差序列(类似range)

参数说明

  • start:起始值(包含),默认0
  • stop:结束值(不包含)
  • step:步长,默认1
  • dtype:数据类型(可选)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 示例1:基础用法
arange_10 = np.arange(10)
print(arange_10)
# [0 1 2 3 4 5 6 7 8 9]

# 示例2:指定起始和结束
arange_range = np.arange(5, 15)
print(arange_range)
# [ 5 6 7 8 9 10 11 12 13 14]

# 示例3:指定步长
arange_step = np.arange(1, 10, 2)
print(arange_step)
# [1 3 5 7 9]

# 示例4:浮点数步长
arange_float = np.arange(0, 1, 0.1)
print(arange_float)
# [0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9]

# 示例5:倒序
arange_reverse = np.arange(10, 0, -1)
print(arange_reverse)
# [10 9 8 7 6 5 4 3 2 1]

# 示例6:二维坐标网格
x = np.arange(0, 5)
y = np.arange(0, 3)
# 使用meshgrid生成网格
X, Y = np.meshgrid(x, y)
print("X坐标:\n", X)
print("Y坐标:\n", Y)
# X坐标:
# [[0 1 2 3 4]
# [0 1 2 3 4]
# [0 1 2 3 4]]
# Y坐标:
# [[0 0 0 0 0]
# [1 1 1 1 1]
# [2 2 2 2 2]]

np.linspace() - 线性等分(指定元素个数)

参数说明

  • start:起始值(包含)
  • stop:结束值(包含)
  • num:元素个数,默认50
  • endpoint:是否包含终点,默认True
  • retstep:是否返回步长,默认False
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 示例1:基础用法(0到1等分5个数)
linspace_5 = np.linspace(0, 1, 5)
print(linspace_5)
# [0. 0.25 0.5 0.75 1. ]

# 示例2:不包含终点
linspace_no_end = np.linspace(0, 1, 5, endpoint=False)
print(linspace_no_end)
# [0. 0.2 0.4 0.6 0.8]

# 示例3:返回步长
values, step = np.linspace(0, 10, 11, retstep=True)
print("数值:", values)
print("步长:", step)
# 数值: [ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]
# 步长: 1.0

# 示例4:绘制函数曲线用
x = np.linspace(-np.pi, np.pi, 100) # -π到π等分100个点
y = np.sin(x)
print(f"x范围: [{x[0]:.2f}, {x[-1]:.2f}]")
print(f"y范围: [{y.min():.2f}, {y.max():.2f}]")
# x范围: [-3.14, 3.14]
# y范围: [-1.00, 1.00]

# 示例5:机器学习中的学习率调度
epochs = 100
lr_schedule = np.linspace(0.1, 0.001, epochs) # 学习率从0.1线性衰减到0.001
print(f"初始学习率: {lr_schedule[0]}")
print(f"最终学习率: {lr_schedule[-1]}")
print(f"第50轮学习率: {lr_schedule[49]:.6f}")
# 初始学习率: 0.1
# 最终学习率: 0.001
# 第50轮学习率: 0.050505

np.logspace() - 对数等分

参数说明

  • start:10^start
  • stop:10^stop
  • num:元素个数
  • base:底数,默认10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 示例1:10^0到10^2(1到100)
logspace_basic = np.logspace(0, 2, 3)
print(logspace_basic)
# [ 1. 10. 100.]

# 示例2:更多点数
logspace_more = np.logspace(0, 3, 7)
print(logspace_more)
# [ 1. 2.15443469 4.64158883 10. 21.5443469 46.41588834 100.]

# 示例3:改变底数(以2为底)
logspace_base2 = np.logspace(0, 4, 5, base=2)
print(logspace_base2)
# [ 1. 2. 4. 8. 16.]

# 示例4:机器学习中的超参数搜索(对数尺度)
learning_rates = np.logspace(-5, -1, 5)
print("学习率候选:", learning_rates)
# 学习率候选: [1.e-05 1.e-04 1.e-03 1.e-02 1.e-01]

# 示例5:正则化系数搜索
alphas = np.logspace(-3, 2, 6)
print("正则化系数:", alphas)
# 正则化系数: [1.e-03 1.e-02 1.e-01 1.e+00 1.e+01 1.e+02]

np.arange vs np.linspace 对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# arange:指定步长,终点不精确
arr1 = np.arange(0, 1, 0.3)
print("arange:", arr1)
# arange: [0. 0.3 0.6 0.9] # 没有到1

# linspace:指定个数,终点精确
arr2 = np.linspace(0, 1, 4)
print("linspace:", arr2)
# linspace: [0. 0.33333333 0.66666667 1. ] # 精确到1

# 使用建议
# - 整数序列 → arange
# - 浮点数且需要精确终点 → linspace
# - 对数尺度 → logspace

常见错误对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# ❌ 错误1:arange浮点数步长精度问题
arr = np.arange(0.1, 1.0, 0.1)
print(len(arr)) # 期望9个,但可能是9或10个
print(arr)
# [0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9] # 有时会多一个1.0

# ✅ 正确:用linspace
arr = np.linspace(0.1, 0.9, 9)
print(len(arr)) # 确定是9个
# 9

# ❌ 错误2:linspace忘记终点是包含的
arr = np.linspace(0, 10, 10)
print(arr)
# [ 0. 1.11111111 2.22222222 3.33333333 4.44444444 5.55555556
# 6.66666667 7.77777778 8.88888889 10. ]
# 期望[0,1,2,...,9],但实际包含10

# ✅ 正确:需要不包含终点
arr = np.linspace(0, 10, 10, endpoint=False)
# 或者用arange
arr = np.arange(0, 10)

# ❌ 错误3:logspace的start/stop理解错误
arr = np.logspace(1, 100, 3) # 错误理解为1到100
print(arr)
# [1.e+01 1.e+50 1.e+100] # 实际是10^1到10^100

# ✅ 正确
arr = np.logspace(np.log10(1), np.log10(100), 3)
print(arr)
# [ 1. 10. 100.]

性能技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import time

# 比较创建大数组的速度
n = 10000000

# arange
start = time.time()
arr1 = np.arange(n)
t1 = time.time() - start

# linspace
start = time.time()
arr2 = np.linspace(0, n-1, n)
t2 = time.time() - start

print(f"arange: {t1*1000:.2f}ms")
print(f"linspace: {t2*1000:.2f}ms")
# arange: 15.23ms
# linspace: 45.67ms
# 结论:arange更快,linspace更精确

1.4 随机数生成⭐⭐⭐⭐⭐

设置随机种子(重要!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 设置种子保证结果可复现
np.random.seed(42)
arr1 = np.random.rand(5)
print(arr1)
# [0.37454012 0.95071431 0.73199394 0.59865848 0.15601864]

# 再次设置相同种子,得到相同结果
np.random.seed(42)
arr2 = np.random.rand(5)
print(arr2)
# [0.37454012 0.95071431 0.73199394 0.59865848 0.15601864]

print(np.array_equal(arr1, arr2))
# True

# 考试技巧:开头加上 np.random.seed(42) 便于调试

np.random.rand() - 均匀分布 [0, 1)

参数说明:传入形状(不是元组!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 示例1:一维数组
rand_1d = np.random.rand(5)
print(rand_1d)
# [0.15599452 0.05808361 0.86617615 0.60111501 0.70807258]

# 示例2:二维数组(注意不是元组)
rand_2d = np.random.rand(3, 4)
print(rand_2d)
# [[0.02058449 0.96990985 0.83244264 0.21233911]
# [0.18182497 0.18340451 0.30424224 0.52475643]
# [0.43194502 0.29122914 0.61185289 0.13949386]]

# 示例3:初始化权重(Xavier初始化)
n_in, n_out = 100, 50
weights = np.random.rand(n_in, n_out) * np.sqrt(2.0 / n_in)
print(weights.shape, weights.mean(), weights.std())
# (100, 50) 0.07084... 0.14142...

np.random.randn() - 标准正态分布 N(0,1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 示例1:一维标准正态分布
randn_1d = np.random.randn(5)
print(randn_1d)
# [ 1.74481176 -0.7612069 0.3190391 -0.24937038 1.46210794]

# 示例2:验证均值和标准差
large_sample = np.random.randn(1000000)
print(f"均值: {large_sample.mean():.6f}")
print(f"标准差: {large_sample.std():.6f}")
# 均值: -0.000234
# 标准差: 0.999876

# 示例3:He初始化(深度学习)
weights = np.random.randn(256, 128) * np.sqrt(2.0 / 256)
print(f"权重范围: [{weights.min():.3f}, {weights.max():.3f}]")
# 权重范围: [-0.523, 0.489]

# 示例4:生成多维正态分布
data = np.random.randn(1000, 5) # 1000个样本,5个特征
print(data.shape)
# (1000, 5)

np.random.randint() - 随机整数

参数说明

  • low:最小值(包含)
  • high:最大值(不包含)
  • size:输出形状
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 示例1:生成0-9的随机整数
rand_int = np.random.randint(0, 10, size=10)
print(rand_int)
# [6 3 7 4 6 9 2 6 7 4]

# 示例2:生成随机标签
labels = np.random.randint(0, 10, size=100) # 10个类别
print(f"类别分布: {np.bincount(labels)}")
# 类别分布: [12 8 11 9 10 11 8 11 10 10]

# 示例3:模拟骰子
dice_rolls = np.random.randint(1, 7, size=1000) # 1-6
print(f"投骰子1000次,平均值: {dice_rolls.mean():.2f}")
# 投骰子1000次,平均值: 3.48

# 示例4:随机索引采样
indices = np.random.randint(0, 1000, size=100)
# 从1000个样本中随机选100个索引

np.random.uniform() - 指定范围的均匀分布

参数说明

  • low:下界,默认0
  • high:上界,默认1
  • size:输出形状
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 示例1:生成[0, 10)的均匀分布
uniform_1 = np.random.uniform(0, 10, size=5)
print(uniform_1)
# [7.23658765 9.01234567 2.34567890 5.67890123 1.23456789]

# 示例2:生成[-1, 1)的均匀分布
uniform_2 = np.random.uniform(-1, 1, size=(3, 3))
print(uniform_2)
# [[-0.12345678 0.87654321 -0.45678901]
# [ 0.23456789 -0.78901234 0.56789012]
# [ 0.34567890 -0.01234567 0.90123456]]

# 示例3:随机初始化偏置
bias = np.random.uniform(-0.1, 0.1, size=64)
print(f"偏置范围: [{bias.min():.3f}, {bias.max():.3f}]")
# 偏置范围: [-0.098, 0.097]

np.random.normal() - 正态分布 N(μ, σ²)

参数说明

  • loc:均值μ,默认0
  • scale:标准差σ,默认1
  • size:输出形状
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 示例1:生成N(100, 15²)的正态分布(如IQ分布)
iq_scores = np.random.normal(loc=100, scale=15, size=1000)
print(f"均值: {iq_scores.mean():.2f}")
print(f"标准差: {iq_scores.std():.2f}")
# 均值: 100.23
# 标准差: 14.87

# 示例2:生成身高数据(男性,单位cm)
heights = np.random.normal(loc=175, scale=7, size=500)
print(f"平均身高: {heights.mean():.2f}cm")
print(f"最高: {heights.max():.2f}cm, 最矮: {heights.min():.2f}cm")
# 平均身高: 175.12cm
# 最高: 198.34cm, 最矮: 154.23cm

# 示例3:添加高斯噪声
clean_signal = np.sin(np.linspace(0, 2*np.pi, 100))
noise = np.random.normal(0, 0.1, size=100)
noisy_signal = clean_signal + noise
print(f"信噪比: {np.var(clean_signal) / np.var(noise):.2f}")
# 信噪比: 48.23

np.random.choice() - 随机抽样

参数说明

  • a:数组或整数(如果是整数n,从range(n)抽样)
  • size:输出形状
  • replace:是否有放回,默认True
  • p:每个元素的概率
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 示例1:从数组中随机抽取
arr = np.array([10, 20, 30, 40, 50])
sample = np.random.choice(arr, size=3, replace=False)
print(sample)
# [30 10 50] # 无放回抽样,不会重复

# 示例2:有放回抽样
sample_with_replacement = np.random.choice(arr, size=10, replace=True)
print(sample_with_replacement)
# [20 20 10 50 30 20 40 10 30 20] # 可能有重复

# 示例3:指定概率(加权抽样)
fruits = np.array(['apple', 'banana', 'orange'])
probs = np.array([0.5, 0.3, 0.2]) # apple概率50%
samples = np.random.choice(fruits, size=100, p=probs)
unique, counts = np.unique(samples, return_counts=True)
print(dict(zip(unique, counts)))
# {'apple': 48, 'banana': 32, 'orange': 20}

# 示例4:Bootstrap采样
data = np.random.randn(100)
bootstrap_sample = np.random.choice(data, size=100, replace=True)
print(f"原始均值: {data.mean():.3f}")
print(f"Bootstrap均值: {bootstrap_sample.mean():.3f}")
# 原始均值: -0.045
# Bootstrap均值: -0.023

# 示例5:交叉验证的随机分割
n_samples = 1000
indices = np.random.choice(n_samples, size=200, replace=False)
# 随机选200个样本作为验证集

np.random.shuffle() - 随机打乱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 示例1:打乱一维数组(原地操作)
arr = np.array([1, 2, 3, 4, 5])
np.random.shuffle(arr)
print(arr)
# [3 1 5 2 4]

# 示例2:打乱多维数组(只打乱第一维)
matrix = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
np.random.shuffle(matrix)
print(matrix)
# [[7 8 9]
# [1 2 3]
# [4 5 6]] # 行的顺序被打乱,但每行内部不变

# 示例3:同步打乱X和y(重要!)
X = np.arange(10).reshape(5, 2)
y = np.array([0, 1, 0, 1, 0])
print("打乱前:")
print("X:", X.ravel())
print("y:", y)

# 生成随机索引
indices = np.arange(len(X))
np.random.shuffle(indices)
X_shuffled = X[indices]
y_shuffled = y[indices]
print("打乱后:")
print("X:", X_shuffled.ravel())
print("y:", y_shuffled)
# 打乱前:
# X: [0 1 2 3 4 5 6 7 8 9]
# y: [0 1 0 1 0]
# 打乱后:
# X: [6 7 2 3 8 9 4 5 0 1]
# y: [0 0 1 1 0]

np.random.permutation() - 返回打乱的副本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 示例1:返回打乱的副本(不改变原数组)
arr = np.array([1, 2, 3, 4, 5])
shuffled = np.random.permutation(arr)
print("原数组:", arr)
print("打乱后:", shuffled)
# 原数组: [1 2 3 4 5]
# 打乱后: [3 5 1 2 4]

# 示例2:生成随机索引
indices = np.random.permutation(100)
train_idx = indices[:80] # 前80个作为训练集
test_idx = indices[80:] # 后20个作为测试集

# 示例3:K折交叉验证
n_samples = 100
n_folds = 5
indices = np.random.permutation(n_samples)
fold_size = n_samples // n_folds

for fold in range(n_folds):
test_idx = indices[fold*fold_size:(fold+1)*fold_size]
train_idx = np.concatenate([indices[:fold*fold_size],
indices[(fold+1)*fold_size:]])
print(f"Fold {fold+1}: {len(train_idx)} train, {len(test_idx)} test")
# Fold 1: 80 train, 20 test
# Fold 2: 80 train, 20 test
# ...

其他常用分布

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 二项分布
binomial = np.random.binomial(n=10, p=0.5, size=1000) # 10次抛硬币
print(f"平均正面次数: {binomial.mean():.2f}")
# 平均正面次数: 5.02

# 泊松分布
poisson = np.random.poisson(lam=3, size=1000) # λ=3
print(f"泊松分布均值: {poisson.mean():.2f}")
# 泊松分布均值: 2.98

# 指数分布
exponential = np.random.exponential(scale=2, size=1000)
print(f"指数分布均值: {exponential.mean():.2f}")
# 指数分布均值: 2.01

# Beta分布
beta = np.random.beta(a=2, b=5, size=1000)
print(f"Beta分布均值: {beta.mean():.2f}")
# Beta分布均值: 0.29

常见错误对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# ❌ 错误1:rand参数用元组
arr = np.random.rand((3, 4)) # TypeError

# ✅ 正确
arr = np.random.rand(3, 4)

# ❌ 错误2:忘记设置种子导致结果不可复现
np.random.randn(5) # 每次运行结果不同

# ✅ 正确
np.random.seed(42)
np.random.randn(5) # 结果可复现

# ❌ 错误3:randint的high是不包含的
arr = np.random.randint(1, 6, size=100) # 生成1-5,不含6

# ✅ 正确:要生成1-6需要
arr = np.random.randint(1, 7, size=100)

# ❌ 错误4:choice的概率之和不为1
try:
arr = np.random.choice([1, 2, 3], p=[0.5, 0.3, 0.1]) # 和为0.9
except ValueError as e:
print("错误:概率之和必须为1")

# ✅ 正确
arr = np.random.choice([1, 2, 3], p=[0.5, 0.3, 0.2])

性能技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import time

# 技巧1:一次生成大数组比多次生成小数组快
# ❌ 慢
start = time.time()
result = []
for _ in range(10000):
result.append(np.random.rand())
arr = np.array(result)
t1 = time.time() - start

# ✅ 快
start = time.time()
arr = np.random.rand(10000)
t2 = time.time() - start

print(f"多次生成: {t1*1000:.2f}ms")
print(f"一次生成: {t2*1000:.2f}ms (快{t1/t2:.0f}倍)")
# 多次生成: 45.67ms
# 一次生成: 0.23ms (快198倍)

# 技巧2:需要多个随机数组时,一起生成再切片
combined = np.random.randn(1000, 10)
arr1 = combined[:, :5]
arr2 = combined[:, 5:]

机器学习中的随机数应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 1. 数据集划分
def train_test_split(X, y, test_size=0.2, random_state=None):
if random_state is not None:
np.random.seed(random_state)

n_samples = len(X)
indices = np.random.permutation(n_samples)
n_test = int(n_samples * test_size)

test_idx = indices[:n_test]
train_idx = indices[n_test:]

return X[train_idx], X[test_idx], y[train_idx], y[test_idx]

# 2. Mini-batch生成
def get_batches(X, y, batch_size=32, shuffle=True):
n_samples = len(X)
indices = np.arange(n_samples)

if shuffle:
np.random.shuffle(indices)

for start_idx in range(0, n_samples, batch_size):
end_idx = min(start_idx + batch_size, n_samples)
batch_idx = indices[start_idx:end_idx]
yield X[batch_idx], y[batch_idx]

# 3. Dropout实现
def dropout(X, keep_prob=0.5):
mask = np.random.rand(*X.shape) < keep_prob
return X * mask / keep_prob # 注意缩放

二、数组属性与信息⭐⭐⭐⭐⭐

2.1 基本属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
arr = np.array([[1, 2, 3], [4, 5, 6]])

# shape - 形状(最常用)
print(arr.shape) # (2, 3) - 2行3列
print(type(arr.shape)) # <class 'tuple'>

# ndim - 维度数
print(arr.ndim) # 2

# size - 元素总数
print(arr.size) # 6

# dtype - 数据类型
print(arr.dtype) # dtype('int64')

# itemsize - 每个元素字节数
print(arr.itemsize) # 8 (int64占8字节)

# nbytes - 总字节数
print(arr.nbytes) # 48 (6个元素 × 8字节)
print(arr.size * arr.itemsize) # 48 (等价计算)

# T - 转置(属性,不是方法)
print(arr.T)
# [[1 4]
# [2 5]
# [3 6]]

2.2 内存与存储

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 示例1:查看数组是否连续存储
arr = np.arange(12).reshape(3, 4)
print(arr.flags['C_CONTIGUOUS']) # True - C风格连续(行优先)
print(arr.flags['F_CONTIGUOUS']) # False - Fortran风格连续(列优先)

# 示例2:转置后的连续性
arr_T = arr.T
print(arr_T.flags['C_CONTIGUOUS']) # False
print(arr_T.flags['F_CONTIGUOUS']) # True

# 示例3:查看数组是否拥有数据
arr1 = np.array([1, 2, 3])
arr2 = arr1[:] # 视图
arr3 = arr1.copy() # 副本

print(arr1.flags['OWNDATA']) # True
print(arr2.flags['OWNDATA']) # False - 视图不拥有数据
print(arr3.flags['OWNDATA']) # True - 副本拥有数据

# 示例4:内存地址
print(arr1.__array_interface__['data'][0])
print(arr2.__array_interface__['data'][0]) # 相同地址
print(arr3.__array_interface__['data'][0]) # 不同地址

2.3 数组类型转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# astype() - 类型转换(返回副本)
arr_int = np.array([1, 2, 3, 4])
arr_float = arr_int.astype(np.float64)
print(arr_float)
# [1. 2. 3. 4.]

# 浮点转整数(截断)
arr_float = np.array([1.7, 2.3, 3.9])
arr_int = arr_float.astype(np.int32)
print(arr_int)
# [1 2 3]

# 字符串转数字
arr_str = np.array(['1.5', '2.7', '3.9'])
arr_num = arr_str.astype(np.float64)
print(arr_num)
# [1.5 2.7 3.9]

# 布尔转整数
arr_bool = np.array([True, False, True])
arr_int = arr_bool.astype(np.int32)
print(arr_int)
# [1 0 1]

2.4 检查数组信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 示例1:全面查看数组信息
arr = np.random.randn(100, 50)

def array_info(arr):
print(f"形状: {arr.shape}")
print(f"维度: {arr.ndim}")
print(f"元素数: {arr.size}")
print(f"类型: {arr.dtype}")
print(f"内存: {arr.nbytes / 1024:.2f} KB")
print(f"均值: {arr.mean():.4f}")
print(f"标准差: {arr.std():.4f}")
print(f"最小值: {arr.min():.4f}")
print(f"最大值: {arr.max():.4f}")
print(f"NaN数: {np.isnan(arr).sum()}")
print(f"Inf数: {np.isinf(arr).sum()}")

array_info(arr)
# 形状: (100, 50)
# 维度: 2
# 元素数: 5000
# 类型: float64
# 内存: 39.06 KB
# 均值: -0.0123
# 标准差: 1.0045
# 最小值: -3.2341
# 最大值: 3.4567
# NaN数: 0
# Inf数: 0

# 示例2:比较两个数组
def compare_arrays(arr1, arr2):
print(f"形状相同: {arr1.shape == arr2.shape}")
print(f"类型相同: {arr1.dtype == arr2.dtype}")
print(f"完全相等: {np.array_equal(arr1, arr2)}")
if arr1.shape == arr2.shape:
print(f"近似相等: {np.allclose(arr1, arr2)}")
print(f"最大差异: {np.abs(arr1 - arr2).max():.6f}")

常见错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# ❌ 错误1:混淆shape和size
arr = np.array([[1, 2, 3]])
print(arr.shape) # (1, 3)
print(arr.size) # 3
# shape是元组,size是整数

# ❌ 错误2:直接修改shape属性
arr = np.array([1, 2, 3, 4])
# arr.shape = (2, 3) # ValueError: 元素数不匹配

# ✅ 正确:使用reshape
arr = arr.reshape(2, 2)

# ❌ 错误3:忘记astype返回副本
arr = np.array([1, 2, 3])
arr.astype(np.float64) # 返回值被忽略,arr没变
print(arr.dtype) # 还是int64

# ✅ 正确
arr = arr.astype(np.float64)

性能技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 技巧1:选择合适的数据类型节省内存
import sys

# int64 vs int32
arr_int64 = np.arange(1000000, dtype=np.int64)
arr_int32 = np.arange(1000000, dtype=np.int32)
arr_int16 = np.arange(1000000, dtype=np.int16)

print(f"int64: {arr_int64.nbytes / 1024 / 1024:.2f} MB")
print(f"int32: {arr_int32.nbytes / 1024 / 1024:.2f} MB")
print(f"int16: {arr_int16.nbytes / 1024 / 1024:.2f} MB")
# int64: 7.63 MB
# int32: 3.81 MB
# int16: 1.91 MB

# 技巧2:使用float32代替float64(深度学习常用)
weights_64 = np.random.randn(1000, 1000)
weights_32 = weights_64.astype(np.float32)

print(f"float64: {weights_64.nbytes / 1024 / 1024:.2f} MB")
print(f"float32: {weights_32.nbytes / 1024 / 1024:.2f} MB")
# float64: 7.63 MB
# float32: 3.81 MB

# 精度损失检查
print(f"最大差异: {np.abs(weights_64 - weights_32).max():.10f}")
# 最大差异: 0.0000001192

三、数组变形⭐⭐⭐⭐⭐

基础变形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
arr = np.arange(12)              # [0 1 2 3 4 5 6 7 8 9 10 11]

# reshape(返回新数组)
reshaped = arr.reshape(3, 4)
'''
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
'''

# reshape自动推断维度
reshaped = arr.reshape(3, -1) # -1自动计算为4
reshaped = arr.reshape(-1, 4) # -1自动计算为3

# 展平(多种方法)
flat = reshaped.flatten() # 返回副本
flat = reshaped.ravel() # 返回视图(更快)
flat = reshaped.reshape(-1) # 转为1D

# 转置
matrix = np.array([[1, 2], [3, 4]])
transposed = matrix.T
'''
[[1 3]
[2 4]]
'''

# 多维转置(指定轴顺序)
arr3d = np.random.rand(2, 3, 4)
transposed = arr3d.transpose(2, 0, 1) # (4, 2, 3)

维度操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
arr = np.array([1, 2, 3])

# 增加维度
expanded = np.expand_dims(arr, axis=0) # shape: (1, 3)
expanded = np.expand_dims(arr, axis=1) # shape: (3, 1)

# 或使用None/np.newaxis
expanded = arr[np.newaxis, :] # (1, 3)
expanded = arr[:, np.newaxis] # (3, 1)

# 删除长度为1的维度
squeezed = np.squeeze(expanded) # 回到(3,)

# 示例:批量增加维度
batch = arr[None, :] # (1, 3)

四、数组索引与切片⭐⭐⭐⭐⭐

基础索引

1
2
3
4
5
6
7
8
9
10
11
12
arr = np.array([10, 20, 30, 40, 50])

# 单个元素
arr[0] # 10
arr[-1] # 50(倒数第一个)

# 切片 [start:stop:step]
arr[1:4] # [20 30 40]
arr[:3] # [10 20 30]
arr[2:] # [30 40 50]
arr[::2] # [10 30 50](每隔一个)
arr[::-1] # [50 40 30 20 10](反转)

多维索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
matrix = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])

# 单个元素
matrix[0, 1] # 2(第0行第1列)

# 行切片
matrix[0, :] # [1 2 3](第0行)
matrix[:, 1] # [2 5 8](第1列)

# 子矩阵
matrix[0:2, 1:3]
'''
[[2 3]
[5 6]]
'''

# 多行多列
matrix[[0, 2], :] # 第0行和第2行
matrix[:, [0, 2]] # 第0列和第2列

布尔索引⭐⭐⭐⭐⭐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
arr = np.array([1, 2, 3, 4, 5])

# 条件筛选
mask = arr > 3
print(mask) # [False False False True True]
filtered = arr[mask] # [4 5]

# 一步到位
filtered = arr[arr > 3] # [4 5]
filtered = arr[(arr > 2) & (arr < 5)] # [3 4](注意括号和&)

# 多条件
filtered = arr[(arr < 2) | (arr > 4)] # [1 5](或)

# 修改符合条件的元素
arr[arr > 3] = 0
# [1 2 3 0 0]

花式索引

1
2
3
4
5
6
7
8
9
10
11
arr = np.array([10, 20, 30, 40, 50])

# 整数数组索引
indices = [0, 2, 4]
selected = arr[indices] # [10 30 50]

# 二维花式索引
matrix = np.arange(12).reshape(3, 4)
rows = [0, 2]
cols = [1, 3]
selected = matrix[rows, cols] # [1 11]((0,1)和(2,3)的元素)

五、数学运算⭐⭐⭐⭐⭐

基础运算(逐元素)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
a = np.array([1, 2, 3, 4])
b = np.array([10, 20, 30, 40])

# 四则运算
a + b # [11 22 33 44]
a - b # [-9 -18 -27 -36]
a * b # [10 40 90 160](逐元素相乘)
a / b # [0.1 0.1 0.1 0.1]
a // b # [0 0 0 0](整除)
a % b # [1 2 3 4](取模)
a ** 2 # [1 4 9 16](幂运算)

# 与标量运算(广播)
a + 10 # [11 12 13 14]
a * 2 # [2 4 6 8]

数学函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
arr = np.array([1, 4, 9, 16])

# 常用函数
np.sqrt(arr) # [1. 2. 3. 4.](平方根)
np.exp(arr) # e^x
np.log(arr) # 自然对数
np.log10(arr) # 以10为底的对数
np.abs(arr) # 绝对值
np.sign(arr) # 符号(-1, 0, 1)

# 三角函数
np.sin(arr)
np.cos(arr)
np.tan(arr)

# 取整
np.floor(arr) # 向下取整
np.ceil(arr) # 向上取整
np.round(arr, decimals=2) # 四舍五入

# 裁剪
np.clip(arr, 2, 10) # 限制在[2, 10]范围内

统计函数⭐⭐⭐⭐⭐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
arr = np.array([[1, 2, 3],
[4, 5, 6]])

# 基础统计
np.sum(arr) # 21(所有元素和)
np.sum(arr, axis=0) # [5 7 9](按列求和)
np.sum(arr, axis=1) # [6 15](按行求和)

np.mean(arr) # 3.5(均值)
np.mean(arr, axis=0) # [2.5 3.5 4.5]

np.std(arr) # 标准差
np.var(arr) # 方差

np.min(arr) # 1(最小值)
np.max(arr) # 6(最大值)

np.argmin(arr) # 0(最小值索引,展平后)
np.argmax(arr) # 5(最大值索引)
np.argmin(arr, axis=0) # [0 0 0](每列最小值的行索引)

np.median(arr) # 中位数
np.percentile(arr, 25) # 25%分位数
np.percentile(arr, [25, 50, 75]) # 多个分位数

# 累积运算
np.cumsum(arr) # 累积和
np.cumprod(arr) # 累积积

六、矩阵运算⭐⭐⭐⭐⭐

矩阵乘法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])

# 矩阵乘法(推荐)
C = A @ B
'''
[[19 22]
[43 50]]
'''

# 或使用dot
C = np.dot(A, B)

# 逐元素乘法
element_wise = A * B
'''
[[ 5 12]
[21 32]]
'''

# 向量内积
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
dot_product = np.dot(a, b) # 32 (1*4 + 2*5 + 3*6)

# 矩阵向量乘法
A = np.array([[1, 2], [3, 4]])
v = np.array([5, 6])
result = A @ v # [17 39]

线性代数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
A = np.array([[1, 2], [3, 4]])

# 转置
A.T

# 逆矩阵
inv_A = np.linalg.inv(A)

# 行列式
det_A = np.linalg.det(A) # -2.0

# 特征值和特征向量
eigenvalues, eigenvectors = np.linalg.eig(A)

# 矩阵的迹(对角线元素和)
trace = np.trace(A) # 5

# 矩阵的秩
rank = np.linalg.matrix_rank(A) # 2

# 范数
norm = np.linalg.norm(A) # Frobenius范数
norm = np.linalg.norm(A, ord=2) # 2-范数

# 解线性方程组 Ax = b
A = np.array([[3, 1], [1, 2]])
b = np.array([9, 8])
x = np.linalg.solve(A, b) # [2. 3.]

七、广播机制⭐⭐⭐⭐⭐(重点掌握)

7.1 广播规则详解

广播规则

  1. 如果两个数组维度数不同,较小维度的数组会在前面补1
  2. 如果两个数组在某个维度上长度相同或其中一个为1,则兼容
  3. 如果两个数组在所有维度上都兼容,则可以广播
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 规则示例
# (3, 4) + (4,) → (3, 4) + (1, 4) → ✅ 可以广播
# (3, 4) + (3, 1) → ✅ 可以广播
# (3, 4) + (3,) → (3, 4) + (1, 3) → ❌ 不可以广播(第二维4≠3)

# 检查是否可以广播
def can_broadcast(shape1, shape2):
# 从后往前比较
for a, b in zip(reversed(shape1), reversed(shape2)):
if a != 1 and b != 1 and a != b:
return False
return True

print(can_broadcast((3, 4), (4,))) # True
print(can_broadcast((3, 4), (3,))) # False
print(can_broadcast((3, 4, 5), (5,))) # True

7.2 广播实战案例

案例1:标量广播(最简单)

1
2
3
4
5
6
7
8
9
10
11
12
# 标量自动广播到任意形状
arr = np.array([[1, 2, 3],
[4, 5, 6]])

result = arr + 10
print(result)
# [[11 12 13]
# [14 15 16]]

# 等价于
scalar_expanded = np.full_like(arr, 10)
result = arr + scalar_expanded

案例2:向量加到矩阵每一行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 矩阵:(3, 4),向量:(4,)
matrix = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])

vector = np.array([100, 200, 300, 400])

result = matrix + vector
print(result)
# [[101 202 303 404]
# [105 206 307 408]
# [109 210 311 412]]

# 广播过程:vector从(4,)变成(1, 4),然后复制3次

案例3:向量加到矩阵每一列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 矩阵:(3, 4),列向量:(3, 1)
matrix = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])

col_vector = np.array([[100],
[200],
[300]])

result = matrix + col_vector
print(result)
# [[101 102 103 104]
# [205 206 207 208]
# [309 310 311 312]]

# 或者用reshape
col_vector = np.array([100, 200, 300]).reshape(-1, 1)
result = matrix + col_vector

案例4:批量标准化(机器学习常用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 数据:(100, 5) - 100个样本,5个特征
X = np.random.randn(100, 5)

# 计算每列的均值和标准差
mean = X.mean(axis=0) # (5,)
std = X.std(axis=0) # (5,)

# 标准化(广播)
X_normalized = (X - mean) / std

# 验证
print(X_normalized.mean(axis=0)) # 接近[0, 0, 0, 0, 0]
# [-2.22e-17 1.11e-16 -4.44e-17 0.00e+00 2.22e-17]

print(X_normalized.std(axis=0)) # 接近[1, 1, 1, 1, 1]
# [1. 1. 1. 1. 1.]

案例5:计算欧式距离矩阵

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 两组点:X (3, 2), Y (4, 2)
X = np.array([[1, 2],
[3, 4],
[5, 6]])

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

# 方法1:使用广播(高效)
# ||x - y||^2 = ||x||^2 + ||y||^2 - 2*x·y
X_sq = np.sum(X**2, axis=1, keepdims=True) # (3, 1)
Y_sq = np.sum(Y**2, axis=1, keepdims=True) # (4, 1)

# 距离平方矩阵
dist_sq = X_sq + Y_sq.T - 2 * X @ Y.T # (3, 4)
distances = np.sqrt(dist_sq)

print(distances)
# [[2.23606798 1.41421356 1.41421356 2.23606798]
# [5. 3.60555128 2.82842712 2.82842712]
# [7.81024968 6.40312424 5.65685425 5.09901951]]

# 方法2:手动展开(慢,仅供理解)
distances_manual = np.zeros((3, 4))
for i in range(3):
for j in range(4):
distances_manual[i, j] = np.sqrt(np.sum((X[i] - Y[j])**2))

print(np.allclose(distances, distances_manual)) # True

案例6:Softmax 函数(数值稳定版)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def softmax(X):
"""
X: (batch_size, n_classes)
返回: (batch_size, n_classes)
"""
# 减去最大值保证数值稳定
X_shifted = X - np.max(X, axis=1, keepdims=True) # 广播
exp_X = np.exp(X_shifted)
return exp_X / np.sum(exp_X, axis=1, keepdims=True) # 广播

# 测试
logits = np.array([[1, 2, 3],
[4, 5, 6]])

probs = softmax(logits)
print(probs)
# [[0.09003057 0.24472847 0.66524096]
# [0.09003057 0.24472847 0.66524096]]

print(probs.sum(axis=1))
# [1. 1.] # 每行和为1

案例7:外积和笛卡尔积

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 外积
a = np.array([1, 2, 3])
b = np.array([10, 20, 30, 40])

# 使用广播计算外积
outer = a[:, np.newaxis] * b[np.newaxis, :]
print(outer)
# [[10 20 30 40]
# [20 40 60 80]
# [30 60 90 120]]

# 或使用np.outer
outer2 = np.outer(a, b)
print(np.array_equal(outer, outer2)) # True

# 笛卡尔积(所有可能的组合)
x = np.array([1, 2, 3])
y = np.array([10, 20])

X, Y = np.meshgrid(x, y)
pairs = np.stack([X.ravel(), Y.ravel()], axis=1)
print(pairs)
# [[ 1 10]
# [ 2 10]
# [ 3 10]
# [ 1 20]
# [ 2 20]
# [ 3 20]]

案例8:图像批量处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 图像批量标准化
# images: (batch_size, height, width, channels)
images = np.random.randint(0, 256, size=(10, 32, 32, 3))

# 计算每个通道的均值和标准差
mean = images.mean(axis=(0, 1, 2), keepdims=True) # (1, 1, 1, 3)
std = images.std(axis=(0, 1, 2), keepdims=True) # (1, 1, 1, 3)

# 标准化(广播到所有图像的所有像素)
images_normalized = (images - mean) / (std + 1e-8)

print(f"原始范围: [{images.min()}, {images.max()}]")
print(f"标准化后范围: [{images_normalized.min():.2f}, {images_normalized.max():.2f}]")
# 原始范围: [0, 255]
# 标准化后范围: [-2.89, 2.91]

# 批量减去均值图像
mean_image = images.mean(axis=0, keepdims=True) # (1, 32, 32, 3)
images_centered = images - mean_image # 广播

案例9:加权平均(attention机制)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 注意力权重:(batch_size, seq_len)
# 值矩阵:(batch_size, seq_len, d_model)

batch_size, seq_len, d_model = 2, 4, 3

attention_weights = np.random.rand(batch_size, seq_len)
# 归一化
attention_weights = attention_weights / attention_weights.sum(axis=1, keepdims=True)

values = np.random.randn(batch_size, seq_len, d_model)

# 加权平均(使用广播)
# (2, 4, 1) * (2, 4, 3) → (2, 4, 3)
weighted_values = attention_weights[:, :, np.newaxis] * values

# 求和得到最终输出
output = weighted_values.sum(axis=1) # (2, 3)

print("注意力权重:\n", attention_weights)
print("输出形状:", output.shape)
# 注意力权重:
# [[0.234 0.312 0.189 0.265]
# [0.278 0.201 0.334 0.187]]
# 输出形状: (2, 3)

案例10:多项式特征生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 生成多项式特征(如 [x, y] → [1, x, y, x^2, xy, y^2])
def polynomial_features(X, degree=2):
"""
X: (n_samples, n_features)
"""
n_samples, n_features = X.shape
features = [np.ones((n_samples, 1))] # 偏置项

# 一次项
features.append(X)

# 二次项(使用广播)
if degree >= 2:
for i in range(n_features):
for j in range(i, n_features):
# X[:, i] * X[:, j] 使用广播
new_feature = (X[:, i] * X[:, j]).reshape(-1, 1)
features.append(new_feature)

return np.hstack(features)

# 测试
X = np.array([[2, 3],
[4, 5]])

X_poly = polynomial_features(X, degree=2)
print(X_poly)
# [[ 1. 2. 3. 4. 6. 9.] # [1, x, y, x^2, xy, y^2]
# [ 1. 4. 5. 16. 20. 25.]]

# 使用广播的简洁版本
X_expanded = X[:, :, np.newaxis] * X[:, np.newaxis, :] # (2, 2, 2)
# X_expanded[i, j, k] = X[i, j] * X[i, k]

7.3 广播常见错误与解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# ❌ 错误1:维度不兼容
arr1 = np.array([[1, 2, 3]]) # (1, 3)
arr2 = np.array([[1], [2], [3]]) # (3, 1)

# arr1 + arr2 会广播成 (3, 3),可能不是你想要的
result = arr1 + arr2
print(result)
# [[2 3 4]
# [3 4 5]
# [4 5 6]]

# ✅ 如果想要逐元素相加,确保形状匹配
arr1_flat = arr1.ravel() # (3,)
arr2_flat = arr2.ravel() # (3,)
result = arr1_flat + arr2_flat
# [2 4 6]

# ❌ 错误2:忘记keepdims
matrix = np.array([[1, 2, 3],
[4, 5, 6]])

mean = matrix.mean(axis=1) # (2,) - 失去维度
print(mean.shape) # (2,)

# matrix - mean 会在axis=1上广播,而不是axis=0
# result = matrix - mean # 形状不匹配错误

# ✅ 正确:保持维度
mean = matrix.mean(axis=1, keepdims=True) # (2, 1)
result = matrix - mean
print(result)
# [[-1. 0. 1.]
# [-1. 0. 1.]]

# ❌ 错误3:隐式广播导致意外结果
a = np.array([1, 2, 3]) # (3,)
b = np.array([[1], [2]]) # (2, 1)

result = a + b # 广播成 (2, 3)
print(result)
# [[2 3 4]
# [3 4 5]]
# 可能你期望的是一维数组相加

# ✅ 明确维度
a = a.reshape(1, -1) # (1, 3)
# 现在 a + b 会是 (2, 3),但更清晰

7.4 广播性能优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import time

# 示例:计算100个点到1000个中心的距离

points = np.random.rand(100, 2)
centers = np.random.rand(1000, 2)

# ❌ 慢:循环
start = time.time()
distances_loop = np.zeros((100, 1000))
for i in range(100):
for j in range(1000):
distances_loop[i, j] = np.sqrt(np.sum((points[i] - centers[j])**2))
t1 = time.time() - start

# ✅ 快:广播
start = time.time()
points_sq = np.sum(points**2, axis=1, keepdims=True) # (100, 1)
centers_sq = np.sum(centers**2, axis=1, keepdims=True) # (1000, 1)
distances_broadcast = np.sqrt(points_sq + centers_sq.T - 2 * points @ centers.T)
t2 = time.time() - start

print(f"循环方式: {t1*1000:.2f}ms")
print(f"广播方式: {t2*1000:.2f}ms")
print(f"加速: {t1/t2:.1f}倍")
print(f"结果一致: {np.allclose(distances_loop, distances_broadcast)}")
# 循环方式: 234.56ms
# 广播方式: 1.23ms
# 加速: 190.7倍
# 结果一致: True

广播记忆口诀

  1. 标量到数组:最简单,直接扩展
  2. 行向量到矩阵:每行都加
  3. 列向量到矩阵:每列都加(记得reshape成(n, 1))
  4. 保持维度:用keepdims=True,避免维度丢失
  5. 外积思维:(n, 1) 和 (1, m) 广播成 (n, m)

八、数组拼接与分割⭐⭐⭐⭐

拼接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])

# 垂直拼接(按行)
vertical = np.vstack((a, b))
'''
[[1 2]
[3 4]
[5 6]
[7 8]]
'''

# 水平拼接(按列)
horizontal = np.hstack((a, b))
'''
[[1 2 5 6]
[3 4 7 8]]
'''

# 通用拼接
concat0 = np.concatenate((a, b), axis=0) # 同vstack
concat1 = np.concatenate((a, b), axis=1) # 同hstack

# 列拼接(添加列)
c = np.array([[9], [10]])
result = np.c_[a, c]
'''
[[ 1 2 9]
[ 3 4 10]]
'''

# 行拼接(添加行)
d = np.array([[11, 12]])
result = np.r_[a, d]
'''
[[ 1 2]
[ 3 4]
[11 12]]
'''

分割

1
2
3
4
5
6
7
8
9
10
11
12
arr = np.arange(12).reshape(4, 3)

# 垂直分割(按行)
parts = np.vsplit(arr, 2) # 分成2份
# [array([[0, 1, 2], [3, 4, 5]]),
# array([[6, 7, 8], [9, 10, 11]])]

# 水平分割(按列)
parts = np.hsplit(arr, 3) # 分成3份

# 通用分割
parts = np.split(arr, 2, axis=0) # 按行分割

九、条件与逻辑⭐⭐⭐⭐⭐

条件函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
arr = np.array([1, -2, 3, -4, 5])

# where: 条件选择
result = np.where(arr > 0, arr, 0) # [1 0 3 0 5]
# 如果arr>0取arr,否则取0

# 多条件where
result = np.where(arr > 0, 1,
np.where(arr < 0, -1, 0))

# select: 多条件选择
conditions = [arr > 3, arr > 0, arr < 0]
choices = [100, 1, -1]
result = np.select(conditions, choices, default=0)
# arr>3返回100, 0<arr<=3返回1, arr<0返回-1, 其他返回0

逻辑运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
a = np.array([True, False, True])
b = np.array([True, True, False])

# 逻辑与、或、非
np.logical_and(a, b) # [True False False]
np.logical_or(a, b) # [True True True]
np.logical_not(a) # [False True False]

# 位运算(用于整数)
arr1 = np.array([1, 2, 3])
arr2 = np.array([3, 2, 1])
arr1 & arr2 # 按位与
arr1 | arr2 # 按位或
~arr1 # 按位非

# 比较
np.equal(arr1, arr2)
np.not_equal(arr1, arr2)
np.greater(arr1, arr2) # arr1 > arr2

判断函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
arr = np.array([1, 2, np.nan, 4, np.inf])

# 判断NaN
np.isnan(arr) # [False False True False False]

# 判断无穷
np.isinf(arr) # [False False False False True]

# 判断有限数
np.isfinite(arr) # [True True False True False]

# 全部/任意满足条件
arr = np.array([1, 2, 3, 4, 5])
np.all(arr > 0) # True(所有元素>0)
np.any(arr > 3) # True(存在元素>3)

十、排序与查找⭐⭐⭐⭐

排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
arr = np.array([3, 1, 4, 1, 5, 9, 2, 6])

# sort(返回排序后的副本)
sorted_arr = np.sort(arr) # [1 1 2 3 4 5 6 9]

# argsort(返回排序索引)
indices = np.argsort(arr) # [1 3 6 0 2 4 7 5]
sorted_arr = arr[indices]

# 降序排序
sorted_desc = np.sort(arr)[::-1]
# 或
indices_desc = np.argsort(arr)[::-1]

# 多维排序
matrix = np.array([[3, 1, 4],
[1, 5, 9]])
np.sort(matrix, axis=0) # 按列排序
np.sort(matrix, axis=1) # 按行排序

查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
arr = np.array([1, 2, 3, 4, 5])

# 最大最小值索引
np.argmax(arr) # 4
np.argmin(arr) # 0

# 查找非零元素
nonzero = np.nonzero(arr > 3)
# (array([3, 4]),) 返回索引元组

# 查找符合条件的索引
indices = np.where(arr > 3)
# (array([3, 4]),)

# 唯一值
arr = np.array([1, 2, 2, 3, 3, 3])
unique = np.unique(arr) # [1 2 3]

# 唯一值及其计数
unique, counts = np.unique(arr, return_counts=True)
# unique: [1 2 3], counts: [1 2 3]

十一、常用技巧⭐⭐⭐⭐⭐

处理NaN和无穷

1
2
3
4
5
6
7
8
9
10
11
12
13
arr = np.array([1, 2, np.nan, 4, np.inf, -np.inf])

# 替换NaN
arr[np.isnan(arr)] = 0

# 删除NaN
arr_clean = arr[~np.isnan(arr)]

# 使用nanXXX函数忽略NaN
np.nanmean(arr) # 忽略NaN的均值
np.nanstd(arr) # 忽略NaN的标准差
np.nansum(arr)
np.nanmax(arr)

one-hot编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 方法1:使用eye
labels = np.array([0, 1, 2, 1, 0])
n_classes = 3
one_hot = np.eye(n_classes)[labels]
'''
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]
[0. 1. 0.]
[1. 0. 0.]]
'''

# 方法2:手动
one_hot = np.zeros((len(labels), n_classes))
one_hot[np.arange(len(labels)), labels] = 1

批量处理

1
2
3
4
5
6
7
8
9
# 对每行应用函数
matrix = np.array([[1, 2, 3],
[4, 5, 6]])

# 使用apply_along_axis
result = np.apply_along_axis(np.sum, axis=1, arr=matrix) # [6 15]

# 或使用列表推导+向量化
result = np.array([row.sum() for row in matrix])

Softmax实现

1
2
3
4
5
6
7
8
9
10
def softmax(x):
"""数值稳定的softmax"""
exp_x = np.exp(x - np.max(x, axis=-1, keepdims=True))
return exp_x / np.sum(exp_x, axis=-1, keepdims=True)

# 测试
logits = np.array([[1, 2, 3], [4, 5, 6]])
probs = softmax(logits)
print(probs)
print(probs.sum(axis=1)) # [1. 1.]

数据标准化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def standardize(X):
"""Z-score标准化"""
return (X - X.mean(axis=0)) / (X.std(axis=0) + 1e-8)

def normalize(X):
"""Min-Max归一化到[0,1]"""
X_min = X.min(axis=0)
X_max = X.max(axis=0)
return (X - X_min) / (X_max - X_min + 1e-8)

# 使用
X = np.random.randn(100, 5)
X_std = standardize(X)
X_norm = normalize(X)

十二、性能优化技巧⭐⭐⭐

向量化 vs 循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# ❌ 慢:Python循环
arr = np.arange(1000000)
result = []
for x in arr:
result.append(x ** 2)
result = np.array(result)

# ✅ 快:向量化
result = arr ** 2

# 示例:距离计算
# ❌ 慢
X = np.random.rand(1000, 10)
distances = np.zeros((1000, 1000))
for i in range(1000):
for j in range(1000):
distances[i, j] = np.sqrt(np.sum((X[i] - X[j]) ** 2))

# ✅ 快:广播
X_sq = np.sum(X ** 2, axis=1, keepdims=True)
distances = np.sqrt(X_sq + X_sq.T - 2 * X @ X.T)

内存优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 使用视图而非副本
arr = np.arange(10)
view = arr[::2] # 视图(共享内存)
copy = arr[::2].copy() # 副本(独立内存)

# 原地操作
arr += 10 # 原地加法
arr *= 2 # 原地乘法

# 避免创建中间数组
# ❌
result = ((X - X.mean()) / X.std()) ** 2

# ✅
X -= X.mean()
X /= X.std()
X **= 2

十六、高级技巧与花式操作⭐⭐⭐⭐⭐

16.1 Fancy Indexing(花式索引)进阶

布尔索引的高级应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 示例1:多条件复杂筛选
data = np.random.randn(100, 5)

# 条件1:第一列 > 0
# 条件2:第二列 < 0.5
# 条件3:第三列在[-1, 1]范围内
mask = (data[:, 0] > 0) & (data[:, 1] < 0.5) & (np.abs(data[:, 2]) < 1)
filtered = data[mask]
print(f"筛选后保留了 {len(filtered)} 行")

# 示例2:就地修改符合条件的值
arr = np.random.randint(-10, 10, size=(5, 5))
print("原数组:\n", arr)

# 负数变0,正数保持不变
arr[arr < 0] = 0
print("处理后:\n", arr)

# 示例3:按条件分组统计
scores = np.random.randint(0, 101, size=100)

excellent = np.sum((scores >= 90) & (scores <= 100))
good = np.sum((scores >= 80) & (scores < 90))
medium = np.sum((scores >= 60) & (scores < 80))
fail = np.sum(scores < 60)

print(f"优秀: {excellent}, 良好: {good}, 中等: {medium}, 不及格: {fail}")

整数数组索引进阶

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 示例1:同时选择多行多列
matrix = np.arange(1, 26).reshape(5, 5)
print("原矩阵:\n", matrix)
# [[ 1 2 3 4 5]
# [ 6 7 8 9 10]
# [11 12 13 14 15]
# [16 17 18 19 20]
# [21 22 23 24 25]]

# 选择 (0,1), (2,3), (4,0) 位置的元素
rows = np.array([0, 2, 4])
cols = np.array([1, 3, 0])
selected = matrix[rows, cols]
print("选中的元素:", selected)
# 选中的元素: [ 2 14 21]

# 示例2:批量赋值
matrix = np.zeros((5, 5), dtype=int)
rows = np.array([0, 1, 2, 3, 4])
cols = np.array([0, 1, 2, 3, 4])
matrix[rows, cols] = 99 # 对角线赋值
print(matrix)
# [[99 0 0 0 0]
# [ 0 99 0 0 0]
# [ 0 0 99 0 0]
# [ 0 0 0 99 0]
# [ 0 0 0 0 99]]

# 示例3:根据索引重排
arr = np.array([10, 20, 30, 40, 50])
indices = np.array([4, 2, 0, 3, 1])
reordered = arr[indices]
print(reordered)
# [50 30 10 40 20]

# 示例4:Top-K选择
scores = np.array([85, 92, 78, 95, 88, 76, 99, 82])
k = 3

# 获取Top-K的索引
top_k_indices = np.argsort(scores)[-k:][::-1]
top_k_scores = scores[top_k_indices]

print(f"Top-{k} 索引:", top_k_indices)
print(f"Top-{k} 分数:", top_k_scores)
# Top-3 索引: [6 3 1]
# Top-3 分数: [99 95 92]

组合索引技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 示例1:先布尔后整数
data = np.random.randn(10, 5)

# 选择第一列大于0的行,然后取这些行的第2、4列
mask = data[:, 0] > 0
selected = data[mask][:, [1, 3]]
print(f"筛选后形状: {selected.shape}")

# 示例2:高级切片+索引
matrix = np.arange(24).reshape(4, 6)
print("原矩阵:\n", matrix)
# [[ 0 1 2 3 4 5]
# [ 6 7 8 9 10 11]
# [12 13 14 15 16 17]
# [18 19 20 21 22 23]]

# 取第1、3行,每行的第0、2、4列
result = matrix[1::2, ::2]
print("结果:\n", result)
# [[ 6 8 10]
# [18 20 22]]

# 示例3:网格索引
rows = np.array([0, 1, 2])[:, np.newaxis] # (3, 1)
cols = np.array([0, 2, 4]) # (3,)

# 生成网格索引
selected = matrix[rows, cols] # 广播成 (3, 3)
print(selected)
# [[ 0 2 4]
# [ 6 8 10]
# [12 14 16]]

16.2 内存视图与副本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 视图 vs 副本的判断
def is_view(arr, original):
return arr.base is original or arr.base is original.base

# 示例1:哪些操作返回视图?
arr = np.arange(12).reshape(3, 4)

view1 = arr[1:3] # 切片 → 视图
view2 = arr.ravel() # ravel → 视图(如果可能)
view3 = arr.reshape(4, 3) # reshape → 视图(如果可能)
view4 = arr.T # 转置 → 视图

copy1 = arr[[0, 2]] # 整数数组索引 → 副本
copy2 = arr[arr > 5] # 布尔索引 → 副本
copy3 = arr.flatten() # flatten → 副本
copy4 = arr.copy() # 显式副本

print("view1是视图:", is_view(view1, arr)) # True
print("copy1是视图:", is_view(copy1, arr)) # False

# 示例2:视图的连锁反应
arr = np.array([1, 2, 3, 4, 5])
view = arr[1:4]
view[0] = 999

print("原数组:", arr)
# 原数组: [ 1 999 3 4 5]

# 避免连锁反应:使用copy
arr = np.array([1, 2, 3, 4, 5])
copy = arr[1:4].copy()
copy[0] = 999

print("原数组:", arr)
# 原数组: [1 2 3 4 5]

# 示例3:检查是否共享内存
arr1 = np.array([1, 2, 3])
arr2 = arr1
arr3 = arr1.copy()

print("arr1和arr2共享内存:", np.shares_memory(arr1, arr2)) # True
print("arr1和arr3共享内存:", np.shares_memory(arr1, arr3)) # False

16.3 结构化数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 示例1:创建结构化数组(类似数据库表)
# 定义数据类型
dtype = np.dtype([('name', 'U20'), ('age', 'i4'), ('score', 'f8')])

# 创建数组
students = np.array([
('Alice', 20, 85.5),
('Bob', 22, 92.0),
('Charlie', 21, 78.5),
('David', 23, 88.0)
], dtype=dtype)

print(students)
# [('Alice', 20, 85.5) ('Bob', 22, 92. ) ('Charlie', 21, 78.5)
# ('David', 23, 88. )]

# 按字段访问
print("姓名:", students['name'])
# 姓名: ['Alice' 'Bob' 'Charlie' 'David']

print("平均分:", students['score'].mean())
# 平均分: 86.0

# 示例2:排序结构化数组
# 按分数排序
sorted_students = np.sort(students, order='score')
print("按分数排序:")
print(sorted_students['name'])
# ['Charlie' 'Alice' 'David' 'Bob']

# 按多个字段排序(先按年龄,再按分数)
sorted_students = np.sort(students, order=['age', 'score'])

# 示例3:条件筛选
high_scorers = students[students['score'] > 85]
print("高分学生:")
print(high_scorers)

16.4 高级ufunc技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# 示例1:自定义ufunc
def custom_func(x, y):
"""自定义函数"""
return x**2 + y**2

# 向量化
vectorized_func = np.vectorize(custom_func)

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
result = vectorized_func(a, b)
print(result)
# [17 29 45]

# 示例2:reduce操作
arr = np.array([1, 2, 3, 4, 5])

# 累积求和
cumsum = np.add.accumulate(arr)
print("累积和:", cumsum)
# 累积和: [ 1 3 6 10 15]

# 累积乘积
cumprod = np.multiply.accumulate(arr)
print("累积积:", cumprod)
# 累积积: [ 1 2 6 24 120]

# 示例3:outer操作(外积)
a = np.array([1, 2, 3])
b = np.array([10, 20])

# 加法外积
add_outer = np.add.outer(a, b)
print("加法外积:\n", add_outer)
# [[11 21]
# [12 22]
# [13 23]]

# 乘法外积
mul_outer = np.multiply.outer(a, b)
print("乘法外积:\n", mul_outer)
# [[10 20]
# [20 40]
# [30 60]]

# 示例4:at操作(原地修改)
arr = np.zeros(10)
indices = np.array([1, 3, 5, 7])
values = np.array([10, 20, 30, 40])

# 在指定位置累加
np.add.at(arr, indices, values)
print(arr)
# [ 0. 10. 0. 20. 0. 30. 0. 40. 0. 0.]

# 重复索引会累加
arr = np.zeros(5)
indices = np.array([0, 0, 1, 1, 1])
np.add.at(arr, indices, 1)
print(arr)
# [2. 3. 0. 0. 0.]

16.5 einsum(爱因斯坦求和约定)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# einsum是NumPy中最强大但也最难懂的函数

# 示例1:矩阵乘法
A = np.random.rand(3, 4)
B = np.random.rand(4, 5)

# 传统方式
C1 = A @ B

# einsum方式
C2 = np.einsum('ij,jk->ik', A, B)

print(np.allclose(C1, C2)) # True

# 示例2:批量矩阵乘法
A = np.random.rand(10, 3, 4) # 10个3×4矩阵
B = np.random.rand(10, 4, 5) # 10个4×5矩阵

# 传统方式(循环)
C1 = np.array([A[i] @ B[i] for i in range(10)])

# einsum方式(向量化)
C2 = np.einsum('bij,bjk->bik', A, B)

print(np.allclose(C1, C2)) # True

# 示例3:迹(对角线元素和)
matrix = np.random.rand(5, 5)

trace1 = np.trace(matrix)
trace2 = np.einsum('ii->', matrix)

print(f"trace: {trace1:.4f}, einsum: {trace2:.4f}")

# 示例4:转置
A = np.random.rand(3, 4, 5)

transpose1 = A.transpose(2, 0, 1)
transpose2 = np.einsum('ijk->kij', A)

print(np.allclose(transpose1, transpose2)) # True

# 示例5:元素平方和
arr = np.random.rand(100)

sum_sq1 = np.sum(arr ** 2)
sum_sq2 = np.einsum('i,i->', arr, arr)

print(f"传统: {sum_sq1:.4f}, einsum: {sum_sq2:.4f}")

# 示例6:Attention机制
# Q: (batch, seq_len, d_k)
# K: (batch, seq_len, d_k)
# V: (batch, seq_len, d_v)

batch_size, seq_len, d_k, d_v = 2, 4, 3, 5

Q = np.random.randn(batch_size, seq_len, d_k)
K = np.random.randn(batch_size, seq_len, d_k)
V = np.random.randn(batch_size, seq_len, d_v)

# Attention scores: Q @ K.T
scores = np.einsum('bqd,bkd->bqk', Q, K) # (batch, seq_len, seq_len)

# Softmax
scores = scores / np.sqrt(d_k)
exp_scores = np.exp(scores - scores.max(axis=-1, keepdims=True))
attention = exp_scores / exp_scores.sum(axis=-1, keepdims=True)

# Output: attention @ V
output = np.einsum('bqk,bkv->bqv', attention, V)
print("Output shape:", output.shape)
# Output shape: (2, 4, 5)

16.6 性能分析与优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import time

# 示例1:向量化 vs 循环
def compare_performance():
n = 1000000

# 方法1:Python循环
start = time.time()
result = []
for i in range(n):
result.append(i ** 2)
t1 = time.time() - start

# 方法2:NumPy向量化
start = time.time()
result = np.arange(n) ** 2
t2 = time.time() - start

print(f"Python循环: {t1*1000:.2f}ms")
print(f"NumPy向量化: {t2*1000:.2f}ms")
print(f"加速比: {t1/t2:.1f}x")

compare_performance()
# Python循环: 234.56ms
# NumPy向量化: 2.34ms
# 加速比: 100.2x

# 示例2:内存布局优化
def test_memory_layout():
# C-order (row-major)
arr_c = np.arange(1000000).reshape(1000, 1000, order='C')

# Fortran-order (column-major)
arr_f = np.arange(1000000).reshape(1000, 1000, order='F')

# 行遍历
start = time.time()
for i in range(1000):
_ = arr_c[i].sum()
t1 = time.time() - start

start = time.time()
for i in range(1000):
_ = arr_f[i].sum()
t2 = time.time() - start

print(f"C-order行遍历: {t1*1000:.2f}ms")
print(f"F-order行遍历: {t2*1000:.2f}ms")

# 列遍历
start = time.time()
for i in range(1000):
_ = arr_c[:, i].sum()
t3 = time.time() - start

start = time.time()
for i in range(1000):
_ = arr_f[:, i].sum()
t4 = time.time() - start

print(f"C-order列遍历: {t3*1000:.2f}ms")
print(f"F-order列遍历: {t4*1000:.2f}ms")

# 示例3:避免临时数组
# ❌ 创建多个临时数组
a = np.random.rand(1000000)
b = np.random.rand(1000000)
c = np.random.rand(1000000)

start = time.time()
result = (a + b) * c # 创建临时数组 (a+b)
t1 = time.time() - start

# ✅ 使用原地操作
start = time.time()
result = a.copy()
result += b
result *= c
t2 = time.time() - start

print(f"临时数组: {t1*1000:.2f}ms")
print(f"原地操作: {t2*1000:.2f}ms")

16.7 高级技巧汇总

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 技巧1:快速计算分位数
data = np.random.randn(1000000)
q = np.percentile(data, [25, 50, 75])
print(f"四分位数: {q}")

# 技巧2:滑动窗口视图(避免复制)
def sliding_window_view(arr, window_size):
"""创建滑动窗口视图(零复制)"""
n = arr.size
shape = (n - window_size + 1, window_size)
strides = (arr.strides[0], arr.strides[0])
return np.lib.stride_tricks.as_strided(arr, shape=shape, strides=strides)

arr = np.arange(10)
windows = sliding_window_view(arr, 3)
print(windows)
# [[0 1 2]
# [1 2 3]
# [2 3 4]
# [3 4 5]
# [4 5 6]
# [5 6 7]
# [6 7 8]
# [7 8 9]]

# 技巧3:高效的行规范化
X = np.random.randn(1000, 50)
X_norm = X / (np.linalg.norm(X, axis=1, keepdims=True) + 1e-8)

# 验证:每行的L2范数为1
print(np.linalg.norm(X_norm, axis=1)[:5])
# [1. 1. 1. 1. 1.]

# 技巧4:快速去重并保持顺序
arr = np.array([3, 1, 4, 1, 5, 9, 2, 6, 5, 3])
_, indices = np.unique(arr, return_index=True)
unique_ordered = arr[np.sort(indices)]
print(unique_ordered)
# [3 1 4 5 9 2 6]

# 技巧5:批量替换值
arr = np.array([1, 2, 3, 4, 5, 1, 2, 3])
old_values = np.array([1, 3, 5])
new_values = np.array([10, 30, 50])

for old, new in zip(old_values, new_values):
arr[arr == old] = new

print(arr)
# [10 2 30 4 50 10 2 30]

# 技巧6:多维数组的笛卡尔积
arrays = [np.array([1, 2]), np.array([10, 20, 30]), np.array([100, 200])]
cartesian = np.array(np.meshgrid(*arrays)).T.reshape(-1, len(arrays))
print(cartesian)
# [[ 1 10 100]
# [ 1 10 200]
# [ 1 20 100]
# ...

十七、考场救命代码片段 🎯(必背!)

17.1 创建与初始化(6个)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1. 快速创建数组
arr = np.array([1, 2, 3, 4, 5])

# 2. 等差序列
seq = np.arange(0, 10, 2) # [0 2 4 6 8]

# 3. 线性等分
lin = np.linspace(0, 1, 11) # 0到1等分11个点

# 4. 全0矩阵
zeros = np.zeros((3, 4))

# 5. 单位矩阵
eye = np.eye(5)

# 6. 随机数(记得设置种子)
np.random.seed(42)
rand = np.random.randn(100, 50) # 标准正态分布

17.2 形状操作(5个)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 7. 重塑(记住-1自动推断)
arr = np.arange(12).reshape(3, 4)
arr = np.arange(12).reshape(-1, 4) # 自动推断为(3, 4)

# 8. 展平
flat = arr.ravel() # 返回视图(快)
flat = arr.flatten() # 返回副本

# 9. 转置
transposed = arr.T

# 10. 增加维度
expanded = arr[:, np.newaxis] # 或 arr[:, None]

# 11. 拼接
combined = np.vstack([arr1, arr2]) # 垂直
combined = np.hstack([arr1, arr2]) # 水平

17.3 索引与筛选(5个)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 12. 布尔索引(最常用)
arr = np.array([1, 2, 3, 4, 5])
filtered = arr[arr > 3] # [4, 5]

# 13. 多条件筛选
mask = (arr > 2) & (arr < 5) # 注意括号和&
filtered = arr[mask]

# 14. where条件替换
result = np.where(arr > 3, arr, 0) # 大于3保留,否则为0

# 15. Top-K选择
k = 3
top_k_idx = np.argsort(arr)[-k:][::-1] # 最大的k个索引
top_k_values = arr[top_k_idx]

# 16. 随机采样
indices = np.random.choice(len(arr), size=10, replace=False)
sample = arr[indices]

17.4 统计与计算(5个)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 17. 常用统计(记住axis参数)
mean = np.mean(arr, axis=0) # axis=0按列,axis=1按行
std = np.std(arr, axis=0)
sum_val = np.sum(arr, axis=1, keepdims=True) # keepdims保持维度

# 18. 标准化
X_normalized = (X - X.mean(axis=0)) / (X.std(axis=0) + 1e-8)

# 19. 归一化到[0, 1]
X_scaled = (X - X.min()) / (X.max() - X.min() + 1e-8)

# 20. 余弦相似度
def cosine_sim(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b) + 1e-8)

# 21. 欧式距离
def euclidean_dist(a, b):
return np.sqrt(np.sum((a - b) ** 2))

17.5 矩阵运算(5个)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 22. 矩阵乘法
C = A @ B # 推荐
C = np.dot(A, B) # 等价

# 23. 逐元素乘法
C = A * B

# 24. 向量内积
dot_product = np.dot(a, b)

# 25. 外积
outer = np.outer(a, b) # 或 a[:, None] * b[None, :]

# 26. 矩阵求逆
inv_A = np.linalg.inv(A)

17.6 机器学习常用(9个)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 27. One-hot编码
labels = np.array([0, 1, 2, 1, 0])
n_classes = 3
one_hot = np.eye(n_classes)[labels]

# 28. Softmax
def softmax(x):
exp_x = np.exp(x - np.max(x, axis=-1, keepdims=True))
return exp_x / np.sum(exp_x, axis=-1, keepdims=True)

# 29. Sigmoid
def sigmoid(x):
return 1 / (1 + np.exp(-np.clip(x, -500, 500)))

# 30. 交叉熵损失
def cross_entropy(y_true, y_pred):
return -np.sum(y_true * np.log(y_pred + 1e-8)) / len(y_true)

# 31. 准确率
def accuracy(y_true, y_pred):
return np.mean(y_true == y_pred)

# 32. 混淆矩阵(简化版)
def confusion_matrix(y_true, y_pred, n_classes):
cm = np.zeros((n_classes, n_classes), dtype=int)
for true, pred in zip(y_true, y_pred):
cm[true, pred] += 1
return cm

# 33. 数据集划分
def train_test_split(X, y, test_size=0.2):
n = len(X)
indices = np.random.permutation(n)
n_test = int(n * test_size)
return X[indices[n_test:]], X[indices[:n_test]], \
y[indices[n_test:]], y[indices[:n_test]]

# 34. Mini-batch生成
def get_batches(X, y, batch_size=32):
n = len(X)
for i in range(0, n, batch_size):
yield X[i:i+batch_size], y[i:i+batch_size]

# 35. KNN距离矩阵
X_sq = np.sum(X**2, axis=1, keepdims=True)
Y_sq = np.sum(Y**2, axis=1, keepdims=True)
dists = np.sqrt(X_sq + Y_sq.T - 2 * X @ Y.T)

17.7 调试与检查(5个)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 36. 打印数组信息
print(f"shape: {arr.shape}, dtype: {arr.dtype}")
print(f"min: {arr.min()}, max: {arr.max()}, mean: {arr.mean():.2f}")

# 37. 检查NaN和Inf
print(f"NaN count: {np.isnan(arr).sum()}")
print(f"Inf count: {np.isinf(arr).sum()}")

# 38. 替换NaN
arr[np.isnan(arr)] = 0 # 或 np.nanmean(arr)

# 39. 断言检查
assert X.shape[0] == y.shape[0], "样本数不匹配"
assert not np.isnan(X).any(), "X包含NaN"
assert X.shape[1] == weights.shape[0], "维度不匹配"

# 40. 近似比较
is_close = np.allclose(arr1, arr2, rtol=1e-5, atol=1e-8)

17.8 考场模板(完整示例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import numpy as np

# 设置随机种子(重要!)
np.random.seed(42)

# 1. 读取数据(假设已给定)
X_train = np.random.randn(100, 10)
y_train = np.random.randint(0, 3, size=100)
X_test = np.random.randn(20, 10)

# 2. 数据预处理
# 标准化
mean = X_train.mean(axis=0)
std = X_train.std(axis=0)
X_train = (X_train - mean) / (std + 1e-8)
X_test = (X_test - mean) / (std + 1e-8) # 用训练集的均值和方差

# One-hot编码
n_classes = 3
y_train_onehot = np.eye(n_classes)[y_train]

# 3. 初始化权重
n_features = X_train.shape[1]
W = np.random.randn(n_features, n_classes) * 0.01
b = np.zeros(n_classes)

# 4. 训练循环
lr = 0.01
epochs = 100

for epoch in range(epochs):
# 前向传播
logits = X_train @ W + b
probs = softmax(logits)

# 损失
loss = cross_entropy(y_train_onehot, probs)

# 反向传播
dlogits = (probs - y_train_onehot) / len(X_train)
dW = X_train.T @ dlogits
db = np.sum(dlogits, axis=0)

# 更新参数
W -= lr * dW
b -= lr * db

if epoch % 10 == 0:
print(f"Epoch {epoch}, Loss: {loss:.4f}")

# 5. 预测
test_logits = X_test @ W + b
test_probs = softmax(test_logits)
predictions = np.argmax(test_probs, axis=1)

print("预测结果:", predictions)

考前checklist

  • 记住np.random.seed(42)
  • 熟练使用reshape(-1, n)
  • 掌握axis参数(0=列,1=行)
  • 会用keepdims=True
  • 记住广播规则
  • 熟练布尔索引arr[arr > 0]
  • 会写softmax和sigmoid
  • 掌握矩阵乘法@
  • 会算欧式距离(广播版)
  • 记住+1e-8防止除零

十八、向量化优化实战:慢版本 vs 快版本 ⚡

18.1 距离计算优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import time

# 场景:计算1000个点到500个中心的距离
points = np.random.rand(1000, 50)
centers = np.random.rand(500, 50)

# ❌ 版本1:双重循环(极慢)
def distance_v1(points, centers):
n_points = len(points)
n_centers = len(centers)
distances = np.zeros((n_points, n_centers))

for i in range(n_points):
for j in range(n_centers):
diff = points[i] - centers[j]
distances[i, j] = np.sqrt(np.sum(diff ** 2))

return distances

start = time.time()
dist1 = distance_v1(points, centers)
t1 = time.time() - start

# ✅ 版本2:单层循环(较快)
def distance_v2(points, centers):
n_points = len(points)
distances = np.zeros((n_points, len(centers)))

for i in range(n_points):
# 向量化内层循环
diff = points[i] - centers # 广播
distances[i] = np.sqrt(np.sum(diff ** 2, axis=1))

return distances

start = time.time()
dist2 = distance_v2(points, centers)
t2 = time.time() - start

# ✅✅ 版本3:完全向量化(最快)
def distance_v3(points, centers):
# ||p - c||^2 = ||p||^2 + ||c||^2 - 2*p·c
p_sq = np.sum(points ** 2, axis=1, keepdims=True) # (1000, 1)
c_sq = np.sum(centers ** 2, axis=1, keepdims=True) # (500, 1)

distances_sq = p_sq + c_sq.T - 2 * points @ centers.T
return np.sqrt(np.maximum(distances_sq, 0)) # 避免负数

start = time.time()
dist3 = distance_v3(points, centers)
t3 = time.time() - start

print(f"版本1 (双循环): {t1*1000:.2f}ms")
print(f"版本2 (单循环): {t2*1000:.2f}ms (快{t1/t2:.1f}倍)")
print(f"版本3 (向量化): {t3*1000:.2f}ms (快{t1/t3:.0f}倍)")
print(f"结果一致: {np.allclose(dist1, dist3)}")

# 输出示例:
# 版本1 (双循环): 1234.56ms
# 版本2 (单循环): 234.12ms (快5.3倍)
# 版本3 (向量化): 12.34ms (快100倍)
# 结果一致: True

18.2 滑动窗口统计优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 场景:计算滑动窗口的平均值
arr = np.random.rand(100000)
window_size = 100

# ❌ 版本1:循环计算(慢)
def sliding_mean_v1(arr, window_size):
n = len(arr) - window_size + 1
result = np.zeros(n)

for i in range(n):
result[i] = np.mean(arr[i:i+window_size])

return result

start = time.time()
result1 = sliding_mean_v1(arr, window_size)
t1 = time.time() - start

# ✅ 版本2:累积和优化(快)
def sliding_mean_v2(arr, window_size):
cumsum = np.cumsum(arr)
cumsum = np.concatenate([[0], cumsum])

# 利用累积和计算窗口和
window_sums = cumsum[window_size:] - cumsum[:-window_size]
return window_sums / window_size

start = time.time()
result2 = sliding_mean_v2(arr, window_size)
t2 = time.time() - start

print(f"版本1 (循环): {t1*1000:.2f}ms")
print(f"版本2 (累积和): {t2*1000:.2f}ms (快{t1/t2:.0f}倍)")
print(f"结果一致: {np.allclose(result1, result2)}")

# 输出示例:
# 版本1 (循环): 234.56ms
# 版本2 (累积和): 2.34ms (快100倍)
# 结果一致: True

18.3 条件计数优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 场景:统计矩阵中满足多个条件的元素个数
matrix = np.random.randn(1000, 1000)

# ❌ 版本1:循环遍历(慢)
def count_conditions_v1(matrix):
count = 0
for i in range(matrix.shape[0]):
for j in range(matrix.shape[1]):
if -1 < matrix[i, j] < 1 and matrix[i, j] > 0:
count += 1
return count

start = time.time()
count1 = count_conditions_v1(matrix)
t1 = time.time() - start

# ✅ 版本2:布尔索引(快)
def count_conditions_v2(matrix):
mask = (matrix > 0) & (matrix < 1)
return np.sum(mask)

start = time.time()
count2 = count_conditions_v2(matrix)
t2 = time.time() - start

print(f"版本1 (循环): {t1*1000:.2f}ms")
print(f"版本2 (向量化): {t2*1000:.2f}ms (快{t1/t2:.0f}倍)")
print(f"结果一致: {count1 == count2}")

# 输出示例:
# 版本1 (循环): 345.67ms
# 版本2 (向量化): 1.23ms (快281倍)
# 结果一致: True

18.4 多项式特征生成优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 场景:生成二次多项式特征
X = np.random.rand(10000, 10)

# ❌ 版本1:循环生成(慢)
def poly_features_v1(X):
n_samples, n_features = X.shape
features = [X]

for i in range(n_features):
for j in range(i, n_features):
new_col = []
for k in range(n_samples):
new_col.append(X[k, i] * X[k, j])
features.append(np.array(new_col).reshape(-1, 1))

return np.hstack(features)

start = time.time()
result1 = poly_features_v1(X)
t1 = time.time() - start

# ✅ 版本2:向量化(快)
def poly_features_v2(X):
n_samples, n_features = X.shape
features = [X]

for i in range(n_features):
for j in range(i, n_features):
features.append((X[:, i] * X[:, j]).reshape(-1, 1))

return np.hstack(features)

start = time.time()
result2 = poly_features_v2(X)
t2 = time.time() - start

print(f"版本1 (循环): {t1*1000:.2f}ms")
print(f"版本2 (向量化): {t2*1000:.2f}ms (快{t1/t2:.0f}倍)")
print(f"结果一致: {np.allclose(result1, result2)}")

# 输出示例:
# 版本1 (循环): 456.78ms
# 版本2 (向量化): 23.45ms (快19倍)
# 结果一致: True

18.5 图像卷积优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 场景:简单的2D卷积
image = np.random.rand(100, 100)
kernel = np.array([[1, 0, -1],
[2, 0, -2],
[1, 0, -1]]) # Sobel算子

# ❌ 版本1:循环实现(慢)
def convolve_v1(image, kernel):
h, w = image.shape
kh, kw = kernel.shape
pad_h, pad_w = kh // 2, kw // 2

# 填充
padded = np.pad(image, ((pad_h, pad_h), (pad_w, pad_w)), mode='constant')
output = np.zeros_like(image)

for i in range(h):
for j in range(w):
region = padded[i:i+kh, j:j+kw]
output[i, j] = np.sum(region * kernel)

return output

start = time.time()
result1 = convolve_v1(image, kernel)
t1 = time.time() - start

# ✅ 版本2:使用scipy(快)
from scipy import signal

start = time.time()
result2 = signal.correlate2d(image, kernel, mode='same', boundary='fill')
t2 = time.time() - start

print(f"版本1 (循环): {t1*1000:.2f}ms")
print(f"版本2 (scipy): {t2*1000:.2f}ms (快{t1/t2:.0f}倍)")

# 输出示例:
# 版本1 (循环): 123.45ms
# 版本2 (scipy): 0.98ms (快126倍)

18.6 K-means迭代优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 场景:K-means聚类的距离计算
X = np.random.rand(5000, 20)
centers = np.random.rand(10, 20)

# ❌ 版本1:循环计算距离并分配(慢)
def kmeans_assign_v1(X, centers):
n_samples = len(X)
n_clusters = len(centers)
labels = np.zeros(n_samples, dtype=int)

for i in range(n_samples):
min_dist = float('inf')
min_label = 0

for j in range(n_clusters):
dist = np.sum((X[i] - centers[j]) ** 2)
if dist < min_dist:
min_dist = dist
min_label = j

labels[i] = min_label

return labels

start = time.time()
labels1 = kmeans_assign_v1(X, centers)
t1 = time.time() - start

# ✅ 版本2:向量化距离计算(快)
def kmeans_assign_v2(X, centers):
# 计算所有样本到所有中心的距离
X_sq = np.sum(X ** 2, axis=1, keepdims=True) # (5000, 1)
C_sq = np.sum(centers ** 2, axis=1, keepdims=True) # (10, 1)

distances = X_sq + C_sq.T - 2 * X @ centers.T # (5000, 10)
labels = np.argmin(distances, axis=1)

return labels

start = time.time()
labels2 = kmeans_assign_v2(X, centers)
t2 = time.time() - start

print(f"版本1 (循环): {t1*1000:.2f}ms")
print(f"版本2 (向量化): {t2*1000:.2f}ms (快{t1/t2:.0f}倍)")
print(f"结果一致: {np.array_equal(labels1, labels2)}")

# 输出示例:
# 版本1 (循环): 567.89ms
# 版本2 (向量化): 5.67ms (快100倍)
# 结果一致: True

18.7 协方差矩阵计算优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 场景:计算协方差矩阵
X = np.random.randn(10000, 50)

# ❌ 版本1:循环计算(慢)
def covariance_v1(X):
n_samples, n_features = X.shape
mean = np.mean(X, axis=0)
cov = np.zeros((n_features, n_features))

for i in range(n_features):
for j in range(n_features):
cov[i, j] = np.sum((X[:, i] - mean[i]) * (X[:, j] - mean[j])) / (n_samples - 1)

return cov

start = time.time()
cov1 = covariance_v1(X)
t1 = time.time() - start

# ✅ 版本2:矩阵运算(快)
def covariance_v2(X):
X_centered = X - X.mean(axis=0)
return (X_centered.T @ X_centered) / (len(X) - 1)

start = time.time()
cov2 = covariance_v2(X)
t2 = time.time() - start

# ✅✅ 版本3:NumPy内置(最快)
start = time.time()
cov3 = np.cov(X.T)
t3 = time.time() - start

print(f"版本1 (循环): {t1*1000:.2f}ms")
print(f"版本2 (矩阵): {t2*1000:.2f}ms (快{t1/t2:.0f}倍)")
print(f"版本3 (内置): {t3*1000:.2f}ms (快{t1/t3:.0f}倍)")
print(f"结果一致: {np.allclose(cov1, cov3)}")

# 输出示例:
# 版本1 (循环): 234.56ms
# 版本2 (矩阵): 2.34ms (快100倍)
# 版本3 (内置): 1.23ms (快191倍)
# 结果一致: True

18.8 向量化优化总结

优化原则

  1. 消除显式循环:用NumPy数组操作替代Python循环
  2. 使用广播:避免手动扩展数组
  3. 利用矩阵运算@比循环快得多
  4. 用内置函数:NumPy的C实现比Python快
  5. 预分配内存:避免动态增长数组
  6. 批量操作:一次处理多个元素

常见模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 模式1:累积操作
# ❌ for循环累加
# ✅ cumsum, cumprod

# 模式2:逐元素函数
# ❌ [func(x) for x in arr]
# ✅ np.vectorize(func)(arr) 或直接写向量化版本

# 模式3:条件筛选
# ❌ [x for x in arr if condition(x)]
# ✅ arr[condition(arr)]

# 模式4:成对操作
# ❌ 双重循环
# ✅ 广播 + 矩阵乘法

# 模式5:聚合统计
# ❌ 循环计算sum/mean/max
# ✅ np.sum/mean/max with axis

十九、机器学习中的NumPy实战 🤖

19.1 线性回归完整实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class LinearRegression:
def __init__(self, lr=0.01, n_iters=1000):
self.lr = lr
self.n_iters = n_iters
self.weights = None
self.bias = None
self.losses = []

def fit(self, X, y):
n_samples, n_features = X.shape

# 初始化参数
self.weights = np.zeros(n_features)
self.bias = 0

# 梯度下降
for i in range(self.n_iters):
# 前向传播
y_pred = X @ self.weights + self.bias

# 计算损失 (MSE)
loss = np.mean((y_pred - y) ** 2)
self.losses.append(loss)

# 反向传播
dw = (2 / n_samples) * (X.T @ (y_pred - y))
db = (2 / n_samples) * np.sum(y_pred - y)

# 更新参数
self.weights -= self.lr * dw
self.bias -= self.lr * db

if i % 100 == 0:
print(f"Iter {i}, Loss: {loss:.4f}")

def predict(self, X):
return X @ self.weights + self.bias

# 测试
np.random.seed(42)
X = np.random.randn(100, 3)
true_weights = np.array([2, -3, 0.5])
y = X @ true_weights + np.random.randn(100) * 0.1

# 训练
model = LinearRegression(lr=0.1, n_iters=1000)
model.fit(X, y)

print(f"\n真实权重: {true_weights}")
print(f"学到权重: {model.weights}")
print(f"权重误差: {np.abs(model.weights - true_weights)}")

19.2 逻辑回归实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class LogisticRegression:
def __init__(self, lr=0.01, n_iters=1000):
self.lr = lr
self.n_iters = n_iters
self.weights = None
self.bias = None

def sigmoid(self, z):
return 1 / (1 + np.exp(-np.clip(z, -500, 500)))

def fit(self, X, y):
n_samples, n_features = X.shape

self.weights = np.zeros(n_features)
self.bias = 0

for i in range(self.n_iters):
# 前向传播
linear = X @ self.weights + self.bias
y_pred = self.sigmoid(linear)

# 交叉熵损失
loss = -np.mean(y * np.log(y_pred + 1e-8) +
(1 - y) * np.log(1 - y_pred + 1e-8))

# 梯度
dw = (1 / n_samples) * (X.T @ (y_pred - y))
db = (1 / n_samples) * np.sum(y_pred - y)

# 更新
self.weights -= self.lr * dw
self.bias -= self.lr * db

if i % 100 == 0:
print(f"Iter {i}, Loss: {loss:.4f}")

def predict_proba(self, X):
linear = X @ self.weights + self.bias
return self.sigmoid(linear)

def predict(self, X):
return (self.predict_proba(X) >= 0.5).astype(int)

# 测试
X = np.random.randn(200, 2)
y = (X[:, 0] + X[:, 1] > 0).astype(int)

model = LogisticRegression(lr=0.1, n_iters=500)
model.fit(X, y)

y_pred = model.predict(X)
accuracy = np.mean(y_pred == y)
print(f"\n训练准确率: {accuracy:.2%}")

19.3 K-NN分类器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class KNNClassifier:
def __init__(self, k=3):
self.k = k
self.X_train = None
self.y_train = None

def fit(self, X, y):
self.X_train = X
self.y_train = y

def predict(self, X):
# 计算距离矩阵(向量化)
X_sq = np.sum(X ** 2, axis=1, keepdims=True)
train_sq = np.sum(self.X_train ** 2, axis=1, keepdims=True)

distances = np.sqrt(X_sq + train_sq.T - 2 * X @ self.X_train.T)

# 找k个最近邻
k_indices = np.argsort(distances, axis=1)[:, :self.k]

# 投票
k_labels = self.y_train[k_indices]

# 对每个样本找最频繁的标签
predictions = np.array([
np.bincount(labels).argmax()
for labels in k_labels
])

return predictions

# 测试
from sklearn.datasets import make_classification

X, y = make_classification(n_samples=200, n_features=2,
n_informative=2, n_redundant=0,
random_state=42)

# 划分数据集
split = int(0.8 * len(X))
X_train, X_test = X[:split], X[split:]
y_train, y_test = y[:split], y[split:]

# 训练和预测
knn = KNNClassifier(k=5)
knn.fit(X_train, y_train)
y_pred = knn.predict(X_test)

accuracy = np.mean(y_pred == y_test)
print(f"测试准确率: {accuracy:.2%}")

19.4 主成分分析 (PCA)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class PCA:
def __init__(self, n_components):
self.n_components = n_components
self.components = None
self.mean = None
self.explained_variance = None

def fit(self, X):
# 中心化
self.mean = np.mean(X, axis=0)
X_centered = X - self.mean

# 计算协方差矩阵
cov = (X_centered.T @ X_centered) / (len(X) - 1)

# 特征值分解
eigenvalues, eigenvectors = np.linalg.eig(cov)

# 排序(降序)
idx = eigenvalues.argsort()[::-1]
eigenvalues = eigenvalues[idx]
eigenvectors = eigenvectors[:, idx]

# 选择前n个主成分
self.components = eigenvectors[:, :self.n_components]
self.explained_variance = eigenvalues[:self.n_components]

return self

def transform(self, X):
X_centered = X - self.mean
return X_centered @ self.components

def fit_transform(self, X):
return self.fit(X).transform(X)

def inverse_transform(self, X_transformed):
return X_transformed @ self.components.T + self.mean

# 测试
X = np.random.randn(100, 10)

pca = PCA(n_components=3)
X_reduced = pca.fit_transform(X)

print(f"原始形状: {X.shape}")
print(f"降维后: {X_reduced.shape}")
print(f"解释方差比: {pca.explained_variance / pca.explained_variance.sum()}")

# 重构
X_reconstructed = pca.inverse_transform(X_reduced)
reconstruction_error = np.mean((X - X_reconstructed) ** 2)
print(f"重构误差: {reconstruction_error:.6f}")

19.5 朴素贝叶斯分类器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class GaussianNB:
def fit(self, X, y):
self.classes = np.unique(y)
n_classes = len(self.classes)
n_features = X.shape[1]

# 存储每个类的均值和方差
self.means = np.zeros((n_classes, n_features))
self.vars = np.zeros((n_classes, n_features))
self.priors = np.zeros(n_classes)

for idx, c in enumerate(self.classes):
X_c = X[y == c]
self.means[idx] = X_c.mean(axis=0)
self.vars[idx] = X_c.var(axis=0)
self.priors[idx] = len(X_c) / len(X)

def _gaussian_pdf(self, x, mean, var):
"""高斯概率密度函数"""
eps = 1e-8
coeff = 1 / np.sqrt(2 * np.pi * var + eps)
exponent = np.exp(-((x - mean) ** 2) / (2 * var + eps))
return coeff * exponent

def predict(self, X):
n_samples = len(X)
n_classes = len(self.classes)

# 计算每个类的后验概率
posteriors = np.zeros((n_samples, n_classes))

for idx in range(n_classes):
prior = np.log(self.priors[idx])

# 计算似然(特征独立假设)
likelihood = self._gaussian_pdf(X, self.means[idx], self.vars[idx])
likelihood = np.sum(np.log(likelihood + 1e-8), axis=1)

posteriors[:, idx] = prior + likelihood

return self.classes[np.argmax(posteriors, axis=1)]

# 测试
X, y = make_classification(n_samples=200, n_features=4,
n_informative=3, n_redundant=0,
random_state=42)

split = int(0.8 * len(X))
X_train, X_test = X[:split], X[split:]
y_train, y_test = y[:split], y[split:]

nb = GaussianNB()
nb.fit(X_train, y_train)
y_pred = nb.predict(X_test)

accuracy = np.mean(y_pred == y_test)
print(f"朴素贝叶斯准确率: {accuracy:.2%}")

二十、内存管理与性能优化技巧 💾

20.1 内存使用分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 查看数组内存占用
arr = np.random.randn(1000, 1000)

print(f"内存大小: {arr.nbytes / 1024 / 1024:.2f} MB")
print(f"数据类型: {arr.dtype}")
print(f"单个元素: {arr.itemsize} 字节")

# 不同数据类型的内存对比
dtypes = [np.int8, np.int16, np.int32, np.int64,
np.float16, np.float32, np.float64]

for dtype in dtypes:
arr = np.zeros(1000000, dtype=dtype)
print(f"{str(dtype).split('.')[-1]:10s}: {arr.nbytes / 1024 / 1024:6.2f} MB")

# 输出示例:
# int8 : 0.95 MB
# int16 : 1.91 MB
# int32 : 3.81 MB
# int64 : 7.63 MB
# float16 : 1.91 MB
# float32 : 3.81 MB
# float64 : 7.63 MB

20.2 视图 vs 副本性能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import time

arr = np.random.randn(10000, 10000)

# 测试1:切片(视图)
start = time.time()
for _ in range(1000):
view = arr[::2, ::2] # 视图,不复制数据
t1 = time.time() - start

# 测试2:copy(副本)
start = time.time()
for _ in range(1000):
copy = arr[::2, ::2].copy() # 副本,复制数据
t2 = time.time() - start

print(f"创建视图: {t1*1000:.2f}ms")
print(f"创建副本: {t2*1000:.2f}ms (慢{t2/t1:.1f}倍)")

# 输出示例:
# 创建视图: 12.34ms
# 创建副本: 234.56ms (慢19.0倍)

20.3 原地操作优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# ❌ 创建新数组(消耗内存)
arr = np.random.rand(10000000)

start = time.time()
arr = arr + 10 # 创建新数组
t1 = time.time() - start

# ✅ 原地操作(节省内存)
arr = np.random.rand(10000000)

start = time.time()
arr += 10 # 原地修改
t2 = time.time() - start

print(f"新数组: {t1*1000:.2f}ms")
print(f"原地操作: {t2*1000:.2f}ms (快{t1/t2:.1f}倍)")

# 其他原地操作
arr *= 2 # 原地乘法
arr /= 2 # 原地除法
arr **= 2 # 原地幂运算
np.sqrt(arr, out=arr) # 指定输出到原数组

20.4 内存池与预分配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# ❌ 动态增长(慢且浪费内存)
def append_slow(n):
result = np.array([])
for i in range(n):
result = np.append(result, i)
return result

# ✅ 预分配(快)
def append_fast(n):
result = np.zeros(n)
for i in range(n):
result[i] = i
return result

# ✅✅ 最快:直接生成
def append_fastest(n):
return np.arange(n)

import time
n = 10000

start = time.time()
r1 = append_slow(n)
t1 = time.time() - start

start = time.time()
r2 = append_fast(n)
t2 = time.time() - start

start = time.time()
r3 = append_fastest(n)
t3 = time.time() - start

print(f"动态增长: {t1*1000:.2f}ms")
print(f"预分配: {t2*1000:.2f}ms (快{t1/t2:.0f}倍)")
print(f"直接生成: {t3*1000:.2f}ms (快{t1/t3:.0f}倍)")

# 输出示例:
# 动态增长: 1234.56ms
# 预分配: 23.45ms (快53倍)
# 直接生成: 0.12ms (快10288倍)

20.5 缓存友好的访问模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 数组在内存中是行优先存储的(C-order)

arr = np.random.rand(10000, 10000)

# ❌ 列优先访问(缓存不友好)
start = time.time()
for j in range(arr.shape[1]):
for i in range(arr.shape[0]):
_ = arr[i, j]
t1 = time.time() - start

# ✅ 行优先访问(缓存友好)
start = time.time()
for i in range(arr.shape[0]):
for j in range(arr.shape[1]):
_ = arr[i, j]
t2 = time.time() - start

print(f"列优先: {t1:.2f}s")
print(f"行优先: {t2:.2f}s (快{t1/t2:.1f}倍)")

# 但最好还是避免循环,用向量化

20.6 减少临时数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# ❌ 创建多个临时数组
a = np.random.rand(1000000)
b = np.random.rand(1000000)
c = np.random.rand(1000000)

start = time.time()
result = (a + b) * c + a / b # 创建3个临时数组
t1 = time.time() - start

# ✅ 使用np.add等函数的out参数
start = time.time()
temp = np.empty_like(a)
np.add(a, b, out=temp)
np.multiply(temp, c, out=temp)
temp2 = np.empty_like(a)
np.divide(a, b, out=temp2)
np.add(temp, temp2, out=temp)
t2 = time.time() - start

print(f"临时数组: {t1*1000:.2f}ms")
print(f"避免临时: {t2*1000:.2f}ms")

20.7 数值稳定性技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 技巧1:Softmax数值稳定
def softmax_unstable(x):
exp_x = np.exp(x)
return exp_x / np.sum(exp_x, axis=-1, keepdims=True)

def softmax_stable(x):
# 减去最大值防止溢出
x_shifted = x - np.max(x, axis=-1, keepdims=True)
exp_x = np.exp(x_shifted)
return exp_x / np.sum(exp_x, axis=-1, keepdims=True)

# 测试大数值
x = np.array([1000, 1001, 1002])

try:
result1 = softmax_unstable(x)
print("不稳定版本:", result1)
except:
print("不稳定版本: 溢出!")

result2 = softmax_stable(x)
print("稳定版本:", result2)
# 稳定版本: [0.09003057 0.24472847 0.66524096]

# 技巧2:对数空间计算
# ❌ 不稳定
def log_sum_exp_unstable(x):
return np.log(np.sum(np.exp(x)))

# ✅ 稳定
def log_sum_exp_stable(x):
max_x = np.max(x)
return max_x + np.log(np.sum(np.exp(x - max_x)))

x = np.array([1000, 1001, 1002])
print(f"稳定的log-sum-exp: {log_sum_exp_stable(x):.2f}")

# 技巧3:避免除零
def safe_divide(a, b, eps=1e-8):
return a / (b + eps)

# 技巧4:梯度裁剪
def clip_gradients(grads, threshold=5.0):
norm = np.linalg.norm(grads)
if norm > threshold:
return grads * (threshold / norm)
return grads

二十一、常见陷阱与调试技巧 🐛

21.1 常见错误汇总

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 错误1:维度不匹配
a = np.array([1, 2, 3]) # (3,)
b = np.array([[1, 2, 3]]) # (1, 3)
# 它们看起来相同但维度不同!

print(a.shape) # (3,)
print(b.shape) # (1, 3)

# 修复:统一维度
a = a.reshape(1, -1) # 变成(1, 3)

# 错误2:整数除法截断
arr = np.array([1, 2, 3, 4, 5])
result = arr / 2 # float数组
result = arr // 2 # int数组 [0 1 1 2 2]

# 错误3:浮点数比较
a = 0.1 + 0.2
b = 0.3
print(a == b) # False(浮点误差)

# 正确方式
print(np.isclose(a, b)) # True
print(np.allclose([a], [b])) # True

# 错误4:数组赋值vs复制
a = np.array([1, 2, 3])
b = a # b是a的引用,不是副本
b[0] = 999
print(a) # [999 2 3] - a也被修改了!

# 正确方式
b = a.copy()

# 错误5:广播陷阱
a = np.array([1, 2, 3])
b = np.array([[1], [2]])
result = a + b # (2, 3) - 可能不是你想要的

# 错误6:axis参数理解错误
arr = np.array([[1, 2, 3],
[4, 5, 6]])
# axis=0 沿着行(垂直),结果是列
# axis=1 沿着列(水平),结果是行

print(np.sum(arr, axis=0)) # [5 7 9] - 每列的和
print(np.sum(arr, axis=1)) # [6 15] - 每行的和

# 错误7:忘记keepdims
mean = arr.mean(axis=1) # (2,) - 失去维度
print(mean.shape)

mean = arr.mean(axis=1, keepdims=True) # (2, 1) - 保持维度
print(mean.shape)

21.2 调试技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# 技巧1:打印关键信息
def debug_array(arr, name="array"):
print(f"\n=== {name} ===")
print(f"Shape: {arr.shape}")
print(f"Dtype: {arr.dtype}")
print(f"Min: {arr.min():.4f}, Max: {arr.max():.4f}")
print(f"Mean: {arr.mean():.4f}, Std: {arr.std():.4f}")
print(f"NaN count: {np.isnan(arr).sum()}")
print(f"Inf count: {np.isinf(arr).sum()}")
print(f"First 5: {arr.ravel()[:5]}")

# 使用
X = np.random.randn(100, 50)
debug_array(X, "X_train")

# 技巧2:断言检查
def validate_input(X, y):
assert X.ndim == 2, f"X应该是2维,实际是{X.ndim}维"
assert y.ndim == 1, f"y应该是1维,实际是{y.ndim}维"
assert len(X) == len(y), f"样本数不匹配: X={len(X)}, y={len(y)}"
assert not np.isnan(X).any(), "X包含NaN"
assert not np.isinf(X).any(), "X包含Inf"

# 技巧3:检查数值范围
def check_range(arr, expected_min, expected_max, name="array"):
actual_min, actual_max = arr.min(), arr.max()
if actual_min < expected_min or actual_max > expected_max:
print(f"警告: {name}超出预期范围")
print(f" 预期: [{expected_min}, {expected_max}]")
print(f" 实际: [{actual_min:.4f}, {actual_max:.4f}]")

# 技巧4:监控梯度
def check_gradients(grads):
grad_norm = np.linalg.norm(grads)
if grad_norm > 10:
print(f"警告: 梯度爆炸 (norm={grad_norm:.2f})")
elif grad_norm < 1e-6:
print(f"警告: 梯度消失 (norm={grad_norm:.2e})")

if np.isnan(grads).any():
print("错误: 梯度包含NaN")

# 技巧5:单元测试示例
def test_softmax():
# 测试1:和为1
x = np.random.randn(10, 5)
probs = softmax(x)
assert np.allclose(probs.sum(axis=1), 1), "softmax输出和不为1"

# 测试2:所有值在[0, 1]
assert np.all(probs >= 0) and np.all(probs <= 1), "softmax输出超出[0,1]"

# 测试3:数值稳定性
x_large = np.array([[1000, 1001, 1002]])
probs = softmax(x_large)
assert not np.isnan(probs).any(), "softmax在大数值时产生NaN"

print("✓ softmax测试通过")

test_softmax()

21.3 性能分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import time

def profile_function(func, *args, n_runs=10):
"""简单的性能分析"""
times = []

for _ in range(n_runs):
start = time.time()
result = func(*args)
times.append(time.time() - start)

times = np.array(times)
print(f"{func.__name__}:")
print(f" 平均: {times.mean()*1000:.2f}ms")
print(f" 标准差: {times.std()*1000:.2f}ms")
print(f" 最小: {times.min()*1000:.2f}ms")
print(f" 最大: {times.max()*1000:.2f}ms")

return result

# 使用示例
X = np.random.rand(1000, 100)

def method1(X):
return np.sum(X ** 2, axis=1)

def method2(X):
return np.einsum('ij,ij->i', X, X)

profile_function(method1, X)
profile_function(method2, X)

二十二、快速查找表 📚

22.1 创建数组速查

需求 代码
从列表创建 np.array([1,2,3])
全0 np.zeros((3,4))
全1 np.ones((3,4))
填充值 np.full((3,4), 7)
单位矩阵 np.eye(3)
等差序列 np.arange(0, 10, 2)
线性等分 np.linspace(0, 1, 11)
标准正态 np.random.randn(3,4)
均匀分布 np.random.rand(3,4)
随机整数 np.random.randint(0,10,size=5)

22.2 形状操作速查

需求 代码
重塑 arr.reshape(3,4)arr.reshape(-1,4)
展平 arr.ravel()arr.flatten()
转置 arr.T
增加维度 arr[np.newaxis,:]arr[:,None]
删除维度 np.squeeze(arr)
垂直拼接 np.vstack([a,b])
水平拼接 np.hstack([a,b])
重复 np.repeat(arr, 3)
平铺 np.tile(arr, (2,3))

22.3 索引切片速查

需求 代码
基本索引 arr[0], arr[-1]
切片 arr[1:5], arr[::2]
布尔索引 arr[arr > 0]
多条件 arr[(arr>0) & (arr<10)]
where替换 np.where(arr>0, arr, 0)
花式索引 arr[[0,2,4]]
网格索引 arr[rows[:,None], cols]

22.4 统计函数速查

需求 代码
求和 np.sum(arr, axis=0)
均值 np.mean(arr, axis=0)
标准差 np.std(arr, axis=0)
方差 np.var(arr)
最小/最大 np.min(arr), np.max(arr)
最小/最大索引 np.argmin(arr), np.argmax(arr)
中位数 np.median(arr)
分位数 np.percentile(arr, 25)
唯一值 np.unique(arr)
计数 np.bincount(arr)

22.5 数学运算速查

需求 代码
加减乘除 a+b, a-b, a*b, a/b
幂运算 a**2np.power(a,2)
平方根 np.sqrt(arr)
指数 np.exp(arr)
对数 np.log(arr)
绝对值 np.abs(arr)
取整 np.floor(arr), np.ceil(arr), np.round(arr)
裁剪 np.clip(arr, 0, 1)
符号 np.sign(arr)

22.6 线性代数速查

需求 代码
矩阵乘法 A @ Bnp.dot(A,B)
内积 np.dot(a,b)
外积 np.outer(a,b)
逆矩阵 np.linalg.inv(A)
行列式 np.linalg.det(A)
特征值 np.linalg.eig(A)
范数 np.linalg.norm(A)
解方程 np.linalg.solve(A,b)
np.trace(A)
np.linalg.matrix_rank(A)

二十三、考前最后冲刺 🏃

必背公式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 1. 欧式距离(向量化版本)
X_sq = np.sum(X**2, axis=1, keepdims=True)
Y_sq = np.sum(Y**2, axis=1, keepdims=True)
dist = np.sqrt(X_sq + Y_sq.T - 2*X@Y.T)

# 2. 余弦相似度
def cosine_sim(X, Y):
X_norm = X / (np.linalg.norm(X, axis=1, keepdims=True) + 1e-8)
Y_norm = Y / (np.linalg.norm(Y, axis=1, keepdims=True) + 1e-8)
return X_norm @ Y_norm.T

# 3. Softmax(稳定版)
def softmax(x):
exp_x = np.exp(x - np.max(x, axis=-1, keepdims=True))
return exp_x / np.sum(exp_x, axis=-1, keepdims=True)

# 4. 交叉熵
def cross_entropy(y_true, y_pred):
return -np.mean(np.sum(y_true * np.log(y_pred + 1e-8), axis=1))

# 5. 标准化
X_norm = (X - X.mean(axis=0)) / (X.std(axis=0) + 1e-8)

# 6. One-hot编码
one_hot = np.eye(n_classes)[labels]

# 7. 混淆矩阵
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_true, y_pred)

考场checklist

开始答题前

  • import numpy as np
  • np.random.seed(42)(如果需要随机数)
  • 读懂题目要求的输入输出格式

编码时

  • 检查数组形状:print(arr.shape)
  • 确认axis参数:axis=0是列,axis=1是行
  • 使用keepdims避免维度丢失
  • 加1e-8防止除零
  • 用np.clip防止数值溢出

提交前

  • 测试边界情况(空数组、单个元素等)
  • 检查是否有NaN或Inf:np.isnan().any()
  • 验证输出形状和类型
  • 确认结果在合理范围内

时间分配建议

  • 5分钟:理解题目,确定思路
  • 15分钟:写主要逻辑
  • 5分钟:测试和调试
  • 5分钟:优化和检查

常见题型模板

分类问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1. 数据预处理
X = (X - X.mean(axis=0)) / (X.std(axis=0) + 1e-8)
y_onehot = np.eye(n_classes)[y]

# 2. 初始化参数
W = np.random.randn(n_features, n_classes) * 0.01
b = np.zeros(n_classes)

# 3. 训练循环
for epoch in range(n_epochs):
logits = X @ W + b
probs = softmax(logits)
loss = cross_entropy(y_onehot, probs)

dW = X.T @ (probs - y_onehot) / len(X)
db = np.mean(probs - y_onehot, axis=0)

W -= lr * dW
b -= lr * db

聚类问题

1
2
3
4
5
6
7
8
9
10
11
12
# K-means模板
centers = X[np.random.choice(len(X), k, replace=False)]

for _ in range(n_iters):
# 分配
dists = np.sum((X[:, None] - centers) ** 2, axis=2)
labels = np.argmin(dists, axis=1)

# 更新
for i in range(k):
if np.sum(labels == i) > 0:
centers[i] = X[labels == i].mean(axis=0)

祝你考试顺利!记住:NumPy的核心是向量化,避免循环就成功了一半! 💪

相关资源

继续学习

错误1:维度不匹配

1
2
3
4
5
6
7
8
9
# 错误
a = np.array([1, 2, 3]) # (3,)
b = np.array([[1], [2], [3]]) # (3, 1)
# a + b 可以广播,但可能不是你想要的

# 解决:明确维度
a = a.reshape(-1, 1) # (3, 1)
# 或
a = a[:, np.newaxis]

错误2:整数除法

1
2
3
4
# Python 3中不是问题,但要注意类型
a = np.array([1, 2, 3], dtype=int)
b = a / 2 # float数组
c = a // 2 # int数组(整除)

错误3:原地修改

1
2
3
4
5
6
7
8
9
10
# 意外修改原数组
a = np.array([1, 2, 3])
b = a
b[0] = 999
print(a) # [999 2 3] ❌

# 解决:使用copy
b = a.copy()
b[0] = 999
print(a) # [1 2 3] ✅

调试技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1. 打印形状
print(f"X.shape: {X.shape}, y.shape: {y.shape}")

# 2. 检查数值范围
print(f"X: min={X.min()}, max={X.max()}, mean={X.mean()}")

# 3. 检查NaN/Inf
print(f"NaN count: {np.isnan(X).sum()}")
print(f"Inf count: {np.isinf(X).sum()}")

# 4. 断言检查
assert X.shape[0] == y.shape[0], "Sample size mismatch"
assert not np.isnan(X).any(), "X contains NaN"

十五、快速参考表📋

操作 代码
创建数组 np.array([1,2,3])
全0/全1 np.zeros((2,3)), np.ones((2,3))
单位矩阵 np.eye(3)
随机数 np.random.randn(3,4)
重塑 arr.reshape(3,4)
转置 arr.T
求和 np.sum(arr, axis=0)
均值 np.mean(arr)
最大/最小 np.max(arr), np.argmax(arr)
矩阵乘法 A @ Bnp.dot(A,B)
逐元素乘 A * B
条件选择 np.where(cond, x, y)
排序 np.sort(arr), np.argsort(arr)
唯一值 np.unique(arr)
拼接 np.vstack(), np.hstack()
裁剪 np.clip(arr, min, max)

考前必做:

  1. 手敲一遍所有”考试必背代码片段”
  2. 记住常用函数的axis参数含义
  3. 练习3道完整的NumPy算法题

祝考试顺利!💪

下一篇:真题模拟练习


华为AI机试NumPy速查手册
https://whyalwaysme.lol/2026/09/01/华为AI机试-NumPy速查/
作者
Cassiur
发布于
2026年9月1日
许可协议