机器学习与深度学习的 Python 基础之 NumPy
·
第一章——数组基础
数组的类型、维度与创建方式
NumPy 是科学计算的底层基石。理解“数组的类型与形状 + 向量化运算”这两件事,就足以支撑后续机器学习与深度学习的大部分代码。
本文将围绕两点展开:
- 数组的数据类型(dtype)与类型转换(astype)
- 数组的形状(shape)与变形(reshape)
配套示例均可直接运行,代码内已附简要注释。
1. 数组与 dtype:自动提升与显式转换
关键结论:
- 同一个数组里元素的 dtype 必须一致;
- 有浮点参与会“类型提升”为浮点;
astype可进行显式类型转换(注意:转 int 会直接截断小数)。
import numpy as np # 导入NumPy
# 纯整数数组 → dtype 为 int
arr1 = np.array([1, 2, 3]) # 创建整数类型的一维数组
print(arr1) # [1 2 3]
# 混合“浮点 + 整数” → 自动提升为 float
arr2 = np.array([1.0, 2, 3]) # 自动变为 float64
print(arr2) # [1. 2. 3.]
# 向整数数组写入浮点 → 会被截断为整数(仍是 int 数组)
arr3 = np.array([1, 2, 3])
arr3[0] = 100.9 # 100.9 → 100
print(arr3) # [100 2 3]
# 向浮点数组写入整数 → 会保持为浮点
arr4 = np.array([1.0, 2, 3])
arr4[0] = 100 # 100 → 100.0
print(arr4) # [100. 2. 3.]
# 显式类型转换(astype)
arr5 = arr1.astype(float) # int → float
print(arr5) # [1. 2. 3.]
arr6 = arr2.astype(int) # float → int(截断小数)
print(arr6) # [1 2 3]
# 向量化算术会进行 dtype 对齐/提升
print(arr1 + 0.0) # 与浮点相加 → float
print(arr1 * 1.0) # 与浮点相乘 → float
print(arr1 / 1) # 除法默认产生浮点
print(arr1 + arr2) # 不同 dtype 相加 → 对齐为 float
要点回顾:
- 明确 dtype 能避免“莫名其妙的小数被截断”;
- 与标量/不同 dtype 运算时,NumPy 会自动广播并做类型提升。
2. 数组的形状 shape 与维度变换 reshape
关键结论:
shape是理解张量维度的第一步;reshape可无拷贝地变形(视具体情况),-1让 NumPy 自动推断某一维长度。
import numpy as np
# 构造不同维度的全 1 数组
arr1 = np.ones(3) # 1D,长度 3
arr2 = np.ones((2, 3)) # 2D,形状 (2, 3)
arr3 = np.ones((2, 2, 3, 4)) # 4D,形状 (2, 2, 3, 4)
print(arr1.shape) # (3,)
print(arr2.shape) # (2, 3)
print(arr3.shape) # (2, 2, 3, 4)
# 一维序列 → 按行重排
arr = np.arange(10) # [0..9]
print(arr) # (10,)
arr_reshaped = arr.reshape(5, -1) # 指定 5 行,列数自动推断为 2
print(arr_reshaped) # 形状 (5, 2)
为什么 reshape(5, -1) 这么常见?
- 机器学习里“每行一个样本、每列一个特征”。当我们有 10 个标量想堆成二维特征矩阵时,让 NumPy 自动算列数更方便。
3. 常见数组的创建方式(速记)
import numpy as np
# 从列表/嵌套列表创建
np.array([1, 2, 3]) # 1D
np.array([[1, 2, 3]]) # 2D: (1, 3)
np.array([[1], [2], [3]]) # 2D: (3, 1)
np.array([[1, 2, 3], [4, 5, 6]]) # 2D: (2, 3)
# 递增序列
np.arange(10) # [0..9]
np.arange(1, 10) # [1..9]
np.arange(1, 10, 2) # [1,3,5,7,9]
# 同值数组
np.zeros(3) # [0, 0, 0]
np.ones((1, 3)) # [[1, 1, 1]]
3.14 * np.ones((2, 3)) # 2x3 全 3.14
# 随机数组(补充)
np.random.random((2, 5)) # [0,1) 均匀分布
np.random.randint(10, 100, (1, 15)) # [10,100) 整数
np.random.normal(0, 1, (2, 3)) # N(0,1) 正态
实战建议:
- 初学期把
np.array / np.arange / np.ones / np.zeros / np.random.*熟练起来; - 建模时时刻记住 shape:数据是
(样本数, 特征数)还是(特征数,)。
4. 小测验:一眼判断 dtype 与 shape
np.array([1, 2., 3])的 dtype 是什么?为什么?- 给定
a = np.arange(12),如何得到一个(3, 4)的矩阵?如何得到一个(4, 3)的? - 下面两行输出是否一致?为什么?
np.array([1, 2, 3]) + 0.0
np.array([1., 2., 3.])
5. 这一章的“最小工作记忆”
- dtype 决定“数怎么存、算出来是什么类型”;
astype做显式转换。 - shape 决定“数怎么排布”;
reshape快速改变排布,-1让 NumPy 自动算一维。 - 向量化运算让使用者写少量代码就能对整批数据做计算,是 NumPy 的灵魂。
把这三点吃透,就能顺利迈向矩阵运算、广播机制、以及后续的机器学习/深度学习模型实现。
6. 常见坑位清单(务必收藏)
-
dtype 提升与截断
- 混入浮点会自动升级为 float;
astype(int)会直接截断小数,不四舍五入。 - 建议:金融/评估类数据保留小数,用
np.round(x, 2)控制展示,计算保持 float。
- 混入浮点会自动升级为 float;
-
整数除法结果是浮点
np.array([1,2,3])/1 -> float。如果只要整除,显式用//。
-
视图 vs 拷贝(reshape/slice 常见)
b = a.reshape(...)常为“视图”(共享内存);b[0]=...可能改动a。- 需要独立副本时:
b = a.reshape(...).copy()或b = a.copy()。
-
广播带来的“无声拉伸”
a(3,1) + b(1,4)会广播成(3,4);维度不匹配时错误不明显。计算前先print(a.shape, b.shape)。
-
布尔索引返回拷贝
b = a[a>0]是新数组;修改b不会改a。若要原地改:a[a>0] = ...。
-
随机性不可复现
- 调实验一定设种子:
np.random.seed(42);新API也可用rng = np.random.default_rng(42)。
- 调实验一定设种子:
-
比较浮点的陷阱
- 避免
x == y;用np.isclose(x, y, atol=1e-8)。
- 避免
-
类型溢出
- 小整数类型(如
np.int8)在累加时可能溢出,训练前统一float32/float64更稳。
- 小整数类型(如
-
in-place 与 dtype 冲突
a += 0.1若a是 int,会先计算为 float 再尝试写回 int,结果被截断。先a = a.astype(float)再算。
-
内存布局(C/Fortran)
- 与其他库交互(如 Fortran/BLAS)时,可能需要
np.ascontiguousarray或np.asfortranarray。
- 与其他库交互(如 Fortran/BLAS)时,可能需要
7. 小测验参考答案
-
Q1:
np.array([1, 2., 3])的 dtype?- A:float(包含浮点时自动类型提升)
-
Q2:
a = np.arange(12)如何得到(3,4)与(4,3)?- A:
a.reshape(3,4);a.reshape(4,3)
- A:
-
Q3:以下两行是否一致?为什么?
-
np.array([1, 2, 3]) + 0.0 np.array([1., 2., 3.]) - A:数值相同,dtype 都为 float。第一行因与浮点相加触发类型提升。
-
更多推荐
所有评论(0)