机器学习之ravel()的作用
ravel() 是 NumPy 中一个非常实用但也容易与其他函数混淆的函数。
numpy.ravel() 的核心作用
ravel() 的核心作用非常简单:将一个任意维度的 NumPy 数组“压平”(flatten),变成一个一维数组。
可以把它想象成“解开”或“铺平”一个多维数组。无论你给它的是一个二维矩阵、三维张量还是更高维度的数组,ravel() 都会返回一个包含所有元素的、连续的一维数组。
示例:
import numpy as np# 二维数组
matrix = np.array([[1, 2, 3],[4, 5, 6]])# 使用 ravel()
flattened_array = matrix.ravel()print("原始矩阵:\n", matrix)
print("原始矩阵的 shape:", matrix.shape)
print("\nravel() 之后的结果:", flattened_array)
print("ravel() 之后结果的 shape:", flattened_array.shape)# 三维数组
tensor = np.arange(12).reshape(2, 3, 2)
print("\n原始张量 shape:", tensor.shape)
print("ravel() 之后结果的 shape:", tensor.ravel().shape)
运行结果:
原始矩阵:[[1 2 3][4 5 6]]
原始矩阵的 shape: (2, 3)ravel() 之后的结果: [1 2 3 4 5 6]
ravel() 之后结果的 shape: (6,)原始张量 shape: (2, 3, 2)
ravel() 之后结果的 shape: (12,)
一个非常重要的特性:视图 (View) vs. 拷贝 (Copy)
ravel() 有一个关键特性:它尽可能地返回原始数组的视图 (view),而不是一个拷贝 (copy)。
- 视图 (View):视图只是原始数据的一个“窗口”或“别名”。它不占用新的内存空间。修改视图会直接影响到原始数组。
- 拷贝 (Copy):拷贝会创建一个全新的数组,占用新的内存空间。修改拷贝不会影响原始数组。
ravel() 只有在数组的内存不连续时才会创建拷贝。在大多数情况下,它返回的是视图,这使得它非常高效。
# ravel() 返回视图的例子
a = np.array([[1, 2], [3, 4]])
b = a.ravel()
b[0] = 99 # 修改视图 bprint("修改 b 之后,原始数组 a 也变了:\n", a)
# 输出:
# [[99 2]
# [ 3 4]]
替代操作
ravel() 最主要的替代操作是 flatten() 和 reshape()。它们在功能上相似,但有关键的区别。
1. numpy.flatten()
flatten() 的作用和 ravel() 完全一样:将数组压平成一维。
与 ravel() 的核心区别:
flatten()永远返回一个拷贝 (copy)。
这意味着 flatten() 在内存上开销更大,也更“安全”,因为它不会意外地修改原始数据。
a = np.array([[1, 2], [3, 4]])
c = a.flatten()
c[0] = 99 # 修改拷贝 cprint("修改 c 之后,原始数组 a 保持不变:\n", a)
# 输出:
# [[1 2]
# [3 4]]
2. numpy.reshape(-1)
你也可以使用 reshape() 来实现压平的效果。将 -1 作为 reshape 的参数,NumPy 会自动计算出总元素数量,并创建一个相应大小的一维数组。
与 ravel() 和 flatten() 的区别:
reshape(-1)和ravel()类似,它也尽可能地返回视图 (view)。
在功能上,reshape(-1) 和 ravel() 几乎是等价的。ravel() 在某些情况下可能稍微快一点,因为它在代码实现上更直接。
a = np.array([[1, 2], [3, 4]])
d = a.reshape(-1)
d[0] = 99 # 修改视图 dprint("修改 d 之后,原始数组 a 也变了:\n", a)
# 输出:
# [[99 2]
# [ 3 4]]
总结与如何选择
| 函数 | 功能 | 返回类型 | 性能 | 安全性 |
|---|---|---|---|---|
ravel() | 压平成一维 | 视图 (View) (尽可能) | 最高效 | 较低 (可能意外修改原数据) |
flatten() | 压平成一维 | 拷贝 (Copy) (永远) | 较低 (有内存开销) | 最安全 |
reshape(-1) | 压平成一维 | 视图 (View) (尽可能) | 高效 | 较低 (可能意外修改原数据) |
选择建议:
- 当你需要绝对保证不修改原始数据时,或者你不确定后续操作是否会修改数组时,请使用
flatten()。 这是最安全的选择。 - 当性能至关重要,且你只是想读取或传递一个一维版本的数组,而不会去修改它时,请使用
ravel()或reshape(-1)。ravel()在语义上更清晰地表达了“压平”这个意图。 - 在机器学习中,
ravel()经常被用在需要将多维特征图或参数展平以输入到全连接层或损失函数的场景中。例如,Scikit-learn 的很多函数在需要一维标签数组时,内部可能会调用ravel()。
总而言之,ravel() 是一个高效的工具,但使用它时需要记住它返回视图的特性。flatten() 是它的安全但稍慢的替代品,而 reshape(-1) 在功能上与 ravel() 非常接近。
