引言
在机器学习中,NumPy是一个非常重要的库,特别是在进行向量化操作时。向量化是一种优化技术,可以显著提高数组计算的效率,特别是在处理大型数据集时。NumPy提供了丰富的数组运算功能,使得向量化操作变得简单高效
文章目录
- 引言
- 一、NumPy和向量化操作的核心概念
- 1.1. **NumPy的基本功能**
- 1.2 **向量的概念**
- 1.3 **NumPy数组**
- 1.4 **向量化操作的效率**
- 1.5 **常见的向量化操作**
- 二、Python、NumPy和向量化结合
- 2.1 目标
- 2.2 有用参考资料
- 2.3 Python和NumPy
- 三、向量
- 3.1 概述
- 3.2 NumPy数组
- 3.3 向量创建
- 3.4 向量操作
- 3.4.1 索引
- 3.4.2 切片
- 3.4.3 单向量操作
- 3.4.4 向量元素级操作
- 3.4.5 标量向量操作
- 3.4.6 向量向量点积
- 3.4.7 速度的重要性:向量与循环
- 3.4.8 向量操作
- 四、矩阵
- 4.1 概念
- 4.2 NumPy数组
- 4.3 矩阵创建
- 4.4 矩阵操作
- 4.4.1 索引
- 4.4.2 切片
- 五、总结
一、NumPy和向量化操作的核心概念
1.1. NumPy的基本功能
NumPy扩展了Python的基本功能,增加了更多的数值类型、向量、矩阵以及许多矩阵函数。NumPy与Python结合使用非常顺畅,Python的算术运算符可以用于NumPy数据类型,许多NumPy函数也接受Python数据类型。
1.2 向量的概念
在NumPy中,向量是数字的有序数组。在数学表示中,向量通常用小写粗体字母表示,例如 (\mathbf{x})。向量的元素都是相同类型的,例如,一个向量不能同时包含字符和数字。在计算机科学中,向量的索引通常从0开始。
1.3 NumPy数组
NumPy的基本数据结构是可索引的n维数组,包含相同类型的元素(dtype
)。在NumPy中,我们可以使用多维数组进行数学运算,这些运算通常会对数组的每个成员执行相同的数学操作。
1.4 向量化操作的效率
向量化操作可以显著提高计算效率。例如,使用NumPy进行数组操作比使用Python原生的for循环或列表解析式更加高效。这是因为NumPy在底层使用了优化的C语言和Fortran库,使得数值计算比纯Python快得多。
1.5 常见的向量化操作
使用NumPy,可以轻松地执行矩阵乘法、求和、最大值、最小值等操作。此外,NumPy的广播机制允许在不同形状的数组上执行元素级操作,而矢量化则使用SIMD指令在CPU上并行化处理数据。
二、Python、NumPy和向量化结合
本实验简要介绍了本课程中使用的科学计算功能,特别是NumPy科学计算包及其与Python的结合使用
# 导入numpy库,通常用np表示numpy
import numpy as np # 使用np作为numpy的非官方标准
import time
2.1 目标
- 学习NumPy和Python的功能
2.2 有用参考资料
- NumPy文档,包括基本介绍:NumPy.org
- 一个具有挑战性的功能主题:NumPy广播
2.3 Python和NumPy
Python是我们将在本课程中使用的编程语言。它有一套数字数据类型和算术运算。NumPy是一个库,扩展了Python的基本功能,增加了更丰富的数据集,包括更多数字类型、向量和矩阵以及许多矩阵函数。NumPy和python协同工作非常顺畅。Python的算术运算符适用于NumPy数据类型,许多NumPy函数也接受python数据类型
三、向量
3.1 概述
向量是按顺序排列的数字数组。在表示法中,向量用小写粗体字母表示,如 x \mathbf{x} x。向量的元素都是同一类型。例如,向量中不会同时包含字符和数字。数组中的元素数量通常称为“维度”,而数学家可能更喜欢使用“秩”
上图显示的向量的维度为 n n n。向量的元素可以通过索引来引用。在数学环境中,索引通常从1到n。在计算机科学和本实验中,索引通常从0到n-1。在表示法中,当单独引用向量的元素时,会在下标中指示索引,例如,向量 x \mathbf{x} x的第 0 0 0个元素表示为 x 0 x_0 x0。请注意,在这种情况下,x不是粗体
3.2 NumPy数组
NumPy的基本数据结构是一个可索引的、n维数组,包含相同类型的元素(dtype
)。在这里,“维度”的重载意味着数组的索引数量。一维或1-D数组有一个索引。将向量表示为NumPy 1-D数组
- 1-D数组,形状为(n,): n个元素,索引从[0]到[n-1]
3.3 向量创建
NumPy的数据创建例程通常具有一个参数,用于指定对象形状。这可以是要创建1-D结果的单个值,也可以是指定结果形状的元组(n,m,…)。下面是使用这些例程创建向量的示例。
# NumPy routines which allocate memory and fill arrays with value
a = np.zeros(4); print(f"np.zeros(4) : a = {a}, a shape = {a.shape}, a data type = {a.dtype}")
a = np.zeros((4,)); print(f"np.zeros(4,) : a = {a}, a shape = {a.shape}, a data type = {a.dtype}")
a = np.random.random_sample(4); print(f"np.random.random_sample(4): a = {a}, a shape = {a.shape}, a data type = {a.dtype}")
输出结果:
一些数据创建例程不接受形状元组:
# NumPy routines which allocate memory and fill arrays with value but do not accept shape as input argument
a = np.arange(4.); print(f"np.arange(4.): a = {a}, a shape = {a.shape}, a data type = {a.dtype}")
a = np.random.rand(4); print(f"np.random.rand(4): a = {a}, a shape = {a.shape}, a data type = {a.dtype}")
输出结果:
值也可以手动指定:
# NumPy routines which allocate memory and fill with user specified values
a = np.array([5,4,3,2]); print(f"np.array([5,4,3,2]): a = {a}, a shape = {a.shape}, a data type = {a.dtype}")
a = np.array([5.,4,3,2]); print(f"np.array([5.,4,3,2]): a = {a}, a shape = {a.shape}, a data type = {a.dtype}")
输出结果:
这些操作都创建了一个具有四个元素的一维向量a。a.shape返回的是维度信息。在这里我们看到a.shape = (4,),表示这是一个具有4个元素的一维数组。
3.4 向量操作
让我们探索一些使用向量的操作
3.4.1 索引
向量的元素可以通过索引和切片进行访问。NumPy 提供了一套非常完整的索引和切片功能。我们将探索课程所需的基本功能。参考切片和索引获取更多细节。 索引意味着通过数组中的位置来引用一个元素。 切片意味着根据它们的索引从数组中获取一个子集的元素。 NumPy 从零开始索引,因此向量 𝐚 的第三个元素是 a[2]
# 向量 1-D 索引操作
a = np.arange(10) print(a)# 访问一个元素
print(f"a[2].shape: {a[2].shape} a[2] = {a[2]}, 访问一个元素返回一个标量")# 访问最后一个元素,负索引从末尾开始计数
print(f"a[-1] = {a[-1]}")# 索引必须在向量范围内,否则会产生错误
try: c = a[10]
except Exception as e: print(“你将看到的错误信息是:”) print(e)
输出结果:
3.4.2 切片
切片通过使用三个值(开始:停止:步长)创建一个索引数组。一个子集的值也是有效的。使用的示例:
# 向量 1-D 切片操作
a = np.arange(10) print(f"a = {a}")# 访问 5 个连续元素(开始:停止:步长)
c = a[2:7:1]; print("a[2:7:1] = ", c)# 访问 3 个元素,每个元素之间相隔两个
c = a[2:7:2]; print("a[2:7:2] = ", c)# 访问所有索引 3 以上的元素
c = a[3:]; print("a[3:] = ", c)# 访问所有索引小于 3 的元素
c = a[:3]; print("a[:3] = ", c)# 访问所有元素
c = a[:]; print("a[:] = ", c)
输出结果:
3.4.3 单向量操作
有许多有用的操作涉及对单个向量的操作
a = np.array([1,2,3,4]) print(f"a : {a}")# 否定向量 a 的元素
b = -a print(f"b = -a : {b}")# 计算 a 的所有元素之和,返回一个标量
b = np.sum(a) print(f"b = np.sum(a) : {b}")b = np.mean(a) print(f"b = np.mean(a): {b}")b = a2 print(f"b = a2 : {b}")
输出结果:
3.4.4 向量元素级操作
大多数 NumPy 的算术、逻辑和比较运算符也适用于向量。这些运算符以元素为单位进行操作。例如:
𝐚 + 𝐛 = ∑𝑖=0𝑛−1 𝑎𝑖 + 𝑏𝑖
a = np.array([ 1, 2, 3, 4]) b = np.array([-1,-2, 3, 4]) print(f"Binary operators work element wise: {a + b}")
输出结果:
当然,为了正确工作,这两个向量必须具有相同的尺寸:
# 尝试一个不匹配的向量操作
c = np.array([1, 2]) try: d = a + c except Exception as e: print(“你将看到的错误信息是:”) print(e)
输出结果:
3.4.5 标量向量操作
向量可以被标量值“缩放”。标量值只是一个数字。标量乘以向量的所有元素
a = np.array([1, 2, 3, 4])乘以向量 a 一个标量
b = 5 * a print(f"b = 5 * a : {b
输出结果:
3.4.6 向量向量点积
点积是线性代数和NumPy的核心操作。这个操作在本课程中广泛使用,因此您需要很好地理解它。点积如下图所示:
点积是通过将两个向量中的元素逐个相乘然后求和来实现的。向量点积要求两个向量的维度必须相同。
让我们实现自己的点积版本:
使用for循环,实现一个函数,返回两个向量的点积。函数将根据给定的输入 𝑎 和 𝑏:
𝑥 = ∑𝑖=0𝑛−1 𝑎𝑖 𝑏𝑖
假设 𝑎 和 𝑏 具有相同的形状
def my_dot(a, b): """计算两个向量的点积参数:a (ndarray (n,)): 输入向量b (ndarray (n,)): 与a维度相同的输入向量返回:x (标量):"""x = 0for i in range(a.shape[0]):x = x + a[i] * b[i]return x
# 测试 1-D
a = np.array([1, 2, 3, 4])
b = np.array([-1, 4, 3, 2])
print(f"my_dot(a, b) = {my_dot(a, b)}")
注意,点积预期返回一个标量值。
让我们使用 np.dot 尝试相同的操作。
输出结果:
# 测试 1-D
a = np.array([1, 2, 3, 4])
b = np.array([-1, 4, 3, 2])
c = np.dot(a, b)
print(f"NumPy 1-D np.dot(a, b) = {c}, np.dot(a, b).shape = {c.shape} ")
c = np.dot(b, a)
print(f"NumPy 1-D np.dot(b, a) = {c}, np.dot(a, b).shape = {c.shape} ")
输出结果:
在上面的示例中,注意到1-D的结果与我们的实现相匹配
3.4.7 速度的重要性:向量与循环
我们使用NumPy库是因为它提高了速度和内存效率。让我们展示一下:
np.random.seed(1)
a = np.random.rand(10000000) # 非常大的数组
b = np.random.rand(10000000)
tic = time.time() # 捕捉开始时间
c = np.dot(a, b)
toc = time.time() # 捕捉结束时间
print(f"np.dot(a, b) = {c:.4f}")
print(f"向量化版本持续时间: {1000*(toc-tic):.4f} 毫秒")
tic = time.time() # 捕捉开始时间
c = my_dot(a,b)
toc = time.time() # 捕捉结束时间
print(f"my_dot(a, b) = {c:.4f}")
print(f"循环版本持续时间: {1000*(toc-tic):.4f} 毫秒")
del(a);del(b) # 从内存中删除这些大数组
输出结果:
向量化在本例中提供了显著的速度提升。这是因为NumPy更好地利用了底层硬件中可用的数据并行性。GPU和现代CPU实现单指令,多数据(SIMD)流水线,允许并行发出多个操作。这对于机器学习尤为重要,因为数据集往往非常大。
3.4.8 向量操作
向量操作将频繁出现。原因如下:
- 从现在开始,我们的示例将存储在一个数组X_train中,其维度为(m,n)。这将根据上下文进行更详细的解释,但重要的是要注意,这是一个2维数组或矩阵(请参阅下一节关于矩阵的内容)。
- w将是一个1维向量,其形状为(n,)。
- 我们将通过遍历示例并逐个提取示例来执行操作,通过索引X。例如:X[i]。
- X[i]返回一个形状为(n,)的值,这是一个1维向量。因此,涉及X[i]的操作通常是向量对向量的操作。
这是一个相当长的解释,但在进行向量操作时,对操作数的形状进行对齐和理解是非常重要的
# 展示常见示例
X = np.array([[1],[2],[3],[4]])
w = np.array([2])
c = np.dot(X[1], w)
print(f"X[1]的形状是{X[1].shape}")
print(f"w的形状是{w.shape}")
print(f"c的形状是{c.shape}")
输出结果:
以下是您提供的内容的中文翻译:
四、矩阵
4.1 概念
矩阵是二维数组。矩阵中的元素都是同一类型。在表示法中,矩阵用大写粗体字母表示,如 X \mathbf{X} X。在本课程和其他实验中,m通常表示行数,n表示列数。矩阵的元素可以通过二维索引来引用。在数学环境中,索引中的数字通常从1到n。在计算机科学和这些实验中,索引将从0到n-1。
4.2 NumPy数组
NumPy的基本数据结构是一个可索引的、n维数组,包含相同类型的元素(dtype
)。这些在之前的描述中已经提及。矩阵具有二维(2-D)索引 [m,n]。
2-D矩阵用于存储训练数据。训练数据是m个示例和n个特征,形成一个(m,n)数组。课程1通常不直接对矩阵进行操作,而是通常将示例提取为向量并进行操作。在这里,将回顾:
- 数据创建
- 切片和索引
4.3 矩阵创建
用于创建1-D向量的相同函数也可以创建2-D或n-D数组。以下是一些示例:
在下面的示例中,提供了形状元组以实现2-D结果。注意NumPy如何使用方括号来表示每个维度。进一步注意,NumPy在打印时会每行打印一行。
a = np.zeros((1, 5))
print(f"a形状 = {a.shape}, a = {a}")
a = np.zeros((2, 1))
print(f"a形状 = {a.shape}, a = {a}")
a = np.random.random_sample((1, 1))
print(f"a形状 = {a.shape}, a = {a}")
输出结果:
用户还可以手动指定数据。维度通过添加额外的方括号来指定,格式与打印格式匹配。
# NumPy例程,它们分配内存并填充用户指定的值
a = np.array([[5], [4], [3]]); print(f" a形状 = {a.shape}, np.array: a = {a}")
a = np.array([[5], # One can also[4], # separate values[3]]); #into separate rows
print(f" a形状 = {a.shape}, np.array: a = {a}")
输出结果:
4.4 矩阵操作
让我们探索一些使用矩阵的操作
4.4.1 索引
矩阵包含第二个索引。这两个索引描述 [行, 列]。访问可以返回一个元素或一行/一列。请看下面的示例:
a = np.arange(6).reshape(-1, 2) #reshape是创建矩阵的便捷方式
print(f"a.shape: {a.shape}, \na= {a}")
#访问一个元素
print(f"\na[2,0].shape: {a[2, 0].shape}, a[2,0] = {a[2, 0]}, type(a[2,0]) = {type(a[2, 0])} 访问一个元素返回一个标量")
#访问一行
print(f"a[2].shape: {a[2].shape}, a[2] = {a[2]}, type(a[2]) = {type(a[2])}")
输出结果:
注意最后一个示例,仅指定行来访问矩阵将返回一个1-D向量
重塑
之前的示例使用了重塑(reshape)来更改数组的形状。
a = np.arange(6).reshape(-1, 2)
这行代码首先创建了一个包含六个元素的1-D向量。然后,使用重塑命令将这个向量重塑为2-D数组。这可以写作:
a = np.arange(6).reshape(3, 2)
这样就得到了一个3行2列的相同数组。-1
参数告诉程序根据数组的尺寸和列数计算行数。
4.4.2 切片
切片通过使用三个值(开始:停止:步长)创建一个索引数组。一个子集的值也是有效的。其使用通过示例解释:
# 向量 2-D 切片操作
a = np.arange(20).reshape(-1, 10)
print(f"a = \n{a}")
# 访问 5 个连续元素 (开始:停止:步长)
print("a[0, 2:7:1] = ", a[0, 2:7:1], ", a[0, 2:
# 访问所有元素
print("a[:,:] = \n", a[:,:], ", a[:,:].shape =", a[:,:].shape)
# 访问一行中的所有元素(非常常见的用法)
print("a[1,:] = ", a[1,:], ", a[1,:].shape =", a[1,:].shape, "一个1-D数组")
# 同样的操作
print("a[1] = ", a[1], ", a[1].shape =", a[1].shape, "一个1-D数组")
输出结果:
五、总结
总的来说,NumPy的向量化操作为机器学习中的数据处理和计算提供了强大的工具,使得代码更加简洁、运行效率更高。