线性代数:描述多维金融世界的语言
想象一下,你如何向计算机描述一个包含100只股票的投资组合?或者如何表示整个市场过去一年的每日收益率?
线性代数提供了一套优雅而高效的语言来完成这项任务。
- 向量 (Vectors):表示有序的金融数据,如资产权重或价格序列。
- 矩阵 (Matrices):表示二维数据集,如多只股票的收益率或协方差。
基本构件 1:标量 (Scalar)
标量 是最简单的数学对象:一个单独的数字。
在金融语境中,它可以代表:
- 无风险利率: \(r_f = 0.02\)
- 某时点的示例股票价格: \(P_{\text{上汽集团}} = 25.50\)
- 一项资产的权重: \(w_1 = 0.4\)
我们通常用小写斜体字母表示标量,例如 \(a=1\) 或 \(x=3.14\)。
基本构件 2:向量 (Vector)
向量 是一列有序的数字。这个“有序”的特性至关重要。
例如,一个两资产投资组合的权重向量:
\[ \large{\mathbf{w} = \begin{bmatrix} 0.6 \\ 0.4 \end{bmatrix}} \]
这明确表示第一个资产(比如股票)占60%,第二个资产(比如债券)占40%。顺序不能颠倒。
我们通常用小写粗体字母(如 \(\mathbf{x}\))表示向量。其标准形式是一个用方括号包围的列。
\[ \large{\mathbf{x} = \begin{bmatrix} x_1 \\ x_2 \\ \vdots \\ x_n \end{bmatrix}} \]
向量的几何直观:空间中的箭头
我们可以把一个 \(n\) 维向量看作是 \(n\) 维空间中的一个点,或者更直观地,一个从原点出发,指向该点的箭头。
例如,向量 \(\mathbf{x} = \begin{bmatrix} 2 \\ 1 \end{bmatrix}\) 在二维空间中,代表从 \((0,0)\) 指向 \((2,1)\) 的箭头。
行向量 vs. 列向量
默认情况下,我们通常将向量视为列向量(如前页所示)。
通过转置 (Transpose) 操作(记为 \(T\)),我们可以将其变为行向量。
\[ \large{\mathbf{x} = \begin{bmatrix} x_1 \\ x_2 \\ \vdots \\ x_n \end{bmatrix} \quad \xrightarrow{\text{Transpose}} \quad \mathbf{x}^T = \begin{bmatrix} x_1, x_2, \dots, x_n \end{bmatrix}} \]
这个看似简单的区分在矩阵运算中至关重要,它决定了向量之间如何相乘。
在 Python 中创建向量
接下来,我们将在 Python 中使用 NumPy 库来创建和操作向量。NumPy 是 Python 科学计算的核心库。
我们将演示:
- 如何创建一个基本的 NumPy 数组来表示向量。
- 如何明确区分行向量和列向量的维度。
- 如何执行转置操作。
在 Python 中创建向量
在Python中,一个向量就是一个NumPy数组。
代码
# 为“在 Python 中创建向量”导入 `numpy` 并绑定 `np`,用于执行当前任务的数组、数值或随机机制计算。
import numpy as np
# 构造二维向量 x。
x = np.array([1, 2])
# 输出二维向量的两个元素,核对一维数组的数值内容。
print(f'向量 x: {x}')
# 报告“向量 x 的维度: {x.shape}”中的 `x`,核对“在 Python 中创建向量”的样本形状。
print(f'向量 x 的维度: {x.shape}') # 期望一维形状为 (2,)
# 要明确创建一个列向量 (2x1 矩阵), 可以这样做:
x_col = np.array([[1], [2]])
# 输出显式列向量,观察两个元素沿行方向排列。
print(f'\n明确的列向量 x_col:\n{x_col}')
# 报告“列向量的维度: {x_col.shape}”中的 `x_col`,核对“在 Python 中创建向量”的样本形状。
print(f'列向量的维度: {x_col.shape}') # 期望列向量形状为 (2, 1)
# 转置操作
x_row = x_col.T
# 输出转置后的行向量,观察形状由 2×1 变为 1×2。
print(f'\n转置后的行向量 x_row:\n{x_row}')
# 报告“行向量的维度: {x_row.shape}”中的 `x_row`,核对“在 Python 中创建向量”的样本形状。
print(f'行向量的维度: {x_row.shape}') # 期望行向量形状为 (1, 2)
向量 x: [1 2]
向量 x 的维度: (2,)
明确的列向量 x_col:
[[1]
[2]]
列向量的维度: (2, 1)
转置后的行向量 x_row:
[[1 2]]
行向量的维度: (1, 2)
向量加法:合并两种效应
假设一个投资组合的收益由两部分构成:
- 市场整体变动带来的收益: \(\mathbf{r}_{\text{market}} = \begin{bmatrix} 0.02 \\ -0.01 \end{bmatrix}\)
- 公司特有事件带来的收益: \(\mathbf{r}_{\text{firm}} = \begin{bmatrix} 0.005 \\ 0.015 \end{bmatrix}\)
总收益就是这两个向量的和,代表了两种效应的叠加。
向量加法的代数法则:逐元素相加
向量加法非常直观:将两个向量对应的元素相加。前提是两个向量的维度必须相同。
\[ \large{\mathbf{x} + \mathbf{y} = \begin{bmatrix} x_1 \\ x_2 \\ \vdots \\ x_n \end{bmatrix} + \begin{bmatrix} y_1 \\ y_2 \\ \vdots \\ y_n \end{bmatrix} = \begin{bmatrix} x_1 + y_1 \\ x_2 + y_2 \\ \vdots \\ x_n + y_n \end{bmatrix}} \]
例如:
\[ \large{\begin{bmatrix} 2 \\ 1 \end{bmatrix} + \begin{bmatrix} 0.5 \\ 1.5 \end{bmatrix} = \begin{bmatrix} 2 + 0.5 \\ 1 + 1.5 \end{bmatrix} = \begin{bmatrix} 2.5 \\ 2.5 \end{bmatrix}} \]
向量减法同理,也是逐元素相减。
向量加法的几何诠释:平行四边形法则
几何上,向量加法遵循“头尾相接”或“平行四边形”法则。将第二个向量的起点移动到第一个向量的终点,从原点到第二个向量终点的新箭头就是它们的和。
向量加法在 Python 中的实现
NumPy 的设计使得向量运算和标量运算一样简单,使用 + 和 - 运算符即可。
代码
# 定义两个向量
x = np.array([1.0, 2.0])
# 固定 `y` 的数值分量,作为“向量加法在 Python 中的实现”的可手算输入。
y = np.array([0.5, 1.5])
# 向量加法
z_add = x + y
# 打印逐元素向量和,核对结果为 `[1.5, 3.5]`。
print(f'x + y = {z_add}')
# 向量减法
z_sub = x - y
# 打印逐元素向量差,核对结果为 `[0.5, 0.5]` 并与加法区分。
print(f'x - y = {z_sub}')
x + y = [1.5 3.5]
x - y = [0.5 0.5]
标量与向量的乘法:缩放向量
假设你决定将某个投资策略的风险敞口(由向量 \(\mathbf{x}\) 代表)加倍。这在数学上如何表示?
这就是标量与向量的乘法:用一个标量 \(a\) 乘以一个向量 \(\mathbf{x}\)。
\[ \large{a\mathbf{x} = a \begin{bmatrix} x_1 \\ x_2 \\ \vdots \\ x_n \end{bmatrix} = \begin{bmatrix} ax_1 \\ ax_2 \\ \vdots \\ ax_n \end{bmatrix}} \]
它的作用是缩放 (scale) 向量的长度,而不改变其基本方向。
标量乘法的几何效果
- 如果 \(a > 1\):向量被拉长。
- 如果 \(0 < a < 1\):向量被缩短。
- 如果 \(a < 0\):向量的方向反转180度,并根据 \(|a|\) 的大小进行缩放。
标量乘法的 Python 实现
在 NumPy 中,标量与向量的乘法就像普通数字乘法一样,使用 * 运算符。
代码
# 为“标量乘法的 Python 实现”导入 `numpy` 并绑定 `np`,用于执行当前任务的数组、数值或随机机制计算。
import numpy as np
# 原始向量
x = np.array([1.0, 2.0])
# 标量
a1 = 2
# 对 `1.5` 取负,得到 `a2 = -1.5`。
a2 = -1.5
# 标量乘法
x1 = a1 * x
# 打印原向量,作为后续正、负标量伸缩的共同基准。
print(f'Original x = {x}')
# 打印正标量乘法结果,核对每个分量都按 `a1` 同比例放大。
print(f' {a1} * x = {x1}')
# 将向量每个分量乘以标量 -1.5,演示方向反转并把长度放大到 1.5 倍。
x2 = a2 * x
# 打印负标量乘法结果,核对每个分量按 `a2` 缩放并反向。
print(f' {a2} * x = {x2}')
Original x = [1. 2.]
2 * x = [2. 4.]
-1.5 * x = [-1.5 -3. ]
线性组合:用基本向量构建一切
- 组成:线性组合把向量加法与标量乘法结合起来。
- 输入:向量 \(\{\mathbf{v}_1, \mathbf{v}_2, \dots, \mathbf{v}_k\}\) 与标量 \(\{c_1, c_2, \dots, c_k\}\)。
\[ \large{\mathbf{z} = c_1\mathbf{v}_1 + c_2\mathbf{v}_2 + \dots + c_k\mathbf{v}_k}
\]
金融应用:投资组合总回报,是各资产回报向量 \(\mathbf{v}_i\) 按投资权重 \(c_i\) 的线性组合。
向量的点积:衡量“一致性”
- 输入:两个维度相同的向量。
- 运算:对应分量相乘后求和。
- 输出:一个标量,也称点积(Dot Product)或内积(Inner Product)。
假设我们有两个向量:
- 投资组合权重向量 \(\mathbf{w} = \begin{bmatrix} 0.6 \\ 0.3 \\ 0.1 \end{bmatrix}\)
- 资产预期收益率向量 \(\mathbf{r} = \begin{bmatrix} 0.10 \\ 0.08 \\ 0.05 \end{bmatrix}\)
投资组合的总预期收益率就是这两个向量的点积。
点积的几何意义:投影与角度
点积的真正威力在于它的几何意义。它与两个向量之间的夹角 \(\theta\) 密切相关:
\[ \large{\mathbf{x}^T \mathbf{y} = \lVert\mathbf{x}\rVert \lVert\mathbf{y}\rVert \cos(\theta)}
\]
| \(\mathbf{x}^T\mathbf{y}>0\) |
夹角小于 \(90^\circ\),方向大致相同 |
| \(\mathbf{x}^T\mathbf{y}=0\) |
夹角等于 \(90^\circ\),两向量正交 |
| \(\mathbf{x}^T\mathbf{y}<0\) |
夹角大于 \(90^\circ\),方向大致相反 |
金融解释边界:先在同一时间窗内分别减去样本均值。中心化收益的正点积才表示样本协动为正;原始收益的正点积还可能来自共同的非零均值。
点积几何意义的可视化
点积衡量了一个向量在另一个向量方向上的投影长度。
点积在 Python 中的实现
NumPy 提供了多种方式计算点积。在现代 Python (3.5+) 和 NumPy 中,推荐使用 @ 运算符,因为它最清晰地表达了数学意图。
代码
# 固定 `x` 的数值分量,作为“点积在 Python 中的实现”的可手算输入。
x = np.array([1, 2, 3])
# 固定 `y` 的数值分量,作为“点积在 Python 中的实现”的可手算输入。
y = np.array([-1, -5, 3])
# 方法 1: 使用 @ 运算符 (推荐)
dot_product_1 = x @ y
# 打印 `@` 运算符得到的点积标量,作为三种实现一致性的第一项证据。
print(f'使用 @ 运算符: {dot_product_1}')
# 方法 2: 使用 np.dot() 函数
dot_product_2 = np.dot(x, y)
# 打印 `np.dot` 得到的同一点积标量,与 `@` 结果交叉核对。
print(f'使用 np.dot(): {dot_product_2}')
# 方法 3: 逐元素相乘再求和 (展示了其定义)
dot_product_3 = np.sum(x * y)
# 打印逐元素乘积求和得到的点积标量,确认三种表达式数值一致。
print(f'逐元素相乘再求和: {dot_product_3}')
使用 @ 运算符: -2
使用 np.dot(): -2
逐元素相乘再求和: -2
向量范数:衡量向量的“大小”或“长度”
我们如何量化一个向量的“大小”?例如,一个代表投资组合每日收益波动的向量,我们如何衡量其整体波动幅度?
范数 (Norm) 就是一个衡量向量大小的函数,记作 \(\lVert\mathbf{x}\rVert\)。它必须满足三个性质:
- 正定性:\(\lVert\mathbf{x}\rVert \ge 0\),且仅当 \(\mathbf{x} = \mathbf{0}\) 时等号成立。
- 齐次性:\(\lVert k\mathbf{x}\rVert = |k| \lVert\mathbf{x}\rVert\)。
- 三角不等式:\(\lVert\mathbf{x} + \mathbf{y}\rVert \le \lVert\mathbf{x}\rVert + \lVert\mathbf{y}\rVert\)。
L1 范数:曼哈顿距离
L1 范数 (或称 Manhattan Norm) 是向量中所有元素绝对值之和。
\[ \large{\lVert\mathbf{x}\rVert_1 = \sum_{i=1}^n |x_i|} \]
直观理解:想象在像曼哈顿这样的棋盘格城市中,从原点到一个点必须沿着街道走,L1范数就是你需要走过的总街区数。
例子: \(\mathbf{x} = \begin{bmatrix} 1 \\ -2 \end{bmatrix}\)
\[ \large{\lVert\mathbf{x}\rVert_1 = |1| + |-2| = 1 + 2 = 3} \]
在机器学习中,L1范数常用于特征选择(Lasso回归),因为它倾向于产生稀疏解(许多权重为零)。
L2 范数:欧几里得距离
L2 范数 (或称 Euclidean Norm) 是我们最熟悉的“距离”概念。它是向量元素平方和的平方根。
\[ \large{\lVert\mathbf{x}\rVert_2 = \sqrt{\sum_{i=1}^n x_i^2}} \]
几何意义:它就是从原点到向量所指向的点的直线距离。
例子: \(\mathbf{x} = \begin{bmatrix} 1 \\ -2 \end{bmatrix}\)
\[ \large{\lVert\mathbf{x}\rVert_2 = \sqrt{1^2 + (-2)^2} = \sqrt{1 + 4} = \sqrt{5} \approx 2.236} \]
如果没有特别指明,通常所说的“范数”或“向量长度”都指L2范数。
L1 vs. L2 范数可视化
L1 和 L2 范数的差异可以通过“单位球”的形状直观地理解,即所有范数值为1的点的集合。
范数在 Python 中的计算
NumPy 的 linalg 模块可以方便地计算各种范数,通过 ord 参数指定范数的阶数。
代码
# 固定 `x` 的数值分量,作为“范数在 Python 中的计算”的可手算输入。
x = np.array([1, -2, 2])
# L1 范数 (ord=1)
l1_norm = np.linalg.norm(x, ord=1)
# 输出绝对值之和,核对向量的 L1 范数。
print(f'L1 Norm of x (|1|+|-2|+|2|): {l1_norm}')
# L2 范数 (ord=2 is default)
l2_norm = np.linalg.norm(x)
# 输出平方和开根号,核对向量的 L2 范数。
print(f'L2 Norm of x (sqrt(1²+2²+2²)): {l2_norm}')
L1 Norm of x (|1|+|-2|+|2|): 5.0
L2 Norm of x (sqrt(1²+2²+2²)): 3.0
单位向量:只关心方向
- 定义:单位向量(Unit Vector)的 L2 范数为 1。
- 作用:保留方向,去除原向量的绝对大小。
- 转换:任意非零向量除以自身的 L2 范数。
\[ \large{\hat{\mathbf{x}} = \frac{\mathbf{x}}{\lVert\mathbf{x}\rVert_2}} \]
归一化、中心化与标准化不同
三种操作服务于不同目标:
| L2 归一化 |
\(\mathbf{x}/\lVert\mathbf{x}\rVert_2\),令单个向量长度为 1 |
| 中心化 |
\(\mathbf{x}-\bar{x}\mathbf{1}\),令样本均值为 0 |
| z-score |
\((\mathbf{x}-\bar{x}\mathbf{1})/s\),令样本均值为 0、按所选 ddof 计算的标准差为 1 |
反例:L2 归一化不等于标准化。
- \(\mathbf{x}=(1,3)\) 的 L2 归一化为 \((1/\sqrt{10},3/\sqrt{10})\)。
- 该结果均值不为 0、方差也不为 1。
- 使用总体标准差(
ddof=0)的 z-score 才得到 \((-1,1)\)。
向量相似度:欧几里得距离
我们如何量化两个投资策略(表示为向量)的相似性?
方法一:欧几里得距离
计算两个向量终点之间的直线距离。这可以通过计算差向量的L2范数来实现: \(d(\mathbf{x}, \mathbf{y}) = \lVert\mathbf{x} - \mathbf{y}\rVert_2\)。
- 距离越小,向量越相似。
- 这个方法同时考虑了大小和方向。对于比较投资组合的绝对表现差异可能很有用。
向量相似度:余弦相似度
方法二:余弦相似度 (Cosine Similarity)
这个方法只关心方向,不关心大小。它计算两个向量之间夹角的余弦值。
\[ \large{\cos(\theta) = \frac{\mathbf{x}^T \mathbf{y}}{\lVert\mathbf{x}\rVert_2 \lVert\mathbf{y}\rVert_2}}
\]
- 结果在 [-1, 1] 之间。
- 1: 方向完全相同。
- 0: 在当前向量空间中方向正交;不能据此断言经济或语义“无关”。
- -1: 方向完全相反。
金融应用边界:
- 原始收益向量的余弦会受非零均值影响;比较共同涨跌模式时应先分别中心化。
- 两个中心化向量范数非零时,其余弦等于 Pearson 相关系数。
- 任一向量为零向量时,余弦未定义,不能强行记为 0。
形成性检查:归一化不是标准化
独立作答:对 \(\mathbf{x}=(1,3)\),哪种操作保证均值为 0、总体标准差为 1?余弦相似度为 0 能否推出两只股票经济上无关?
形成性检查反馈:先说目标,再选操作
- 标准化答案:z-score(
ddof=0);L2 归一化只固定向量长度。
- 相似度答案:余弦为 0 只说明当前表示正交,不能推出经济无关。
- 订正路径:答错者返回 相关内容,重算三个结果后再进入矩阵部分。
相似度计算可视化
下面的图表演示了这两种相似度度量的区别。
矩阵:二维数据表
矩阵 (Matrix) 是一个二维的数字数组,可以看作是向量的推广。我们用大写粗体字母表示矩阵,如 \(\mathbf{A}\)。
一个 \(m \times n\) 的矩阵有 \(m\) 行和 \(n\) 列。
\[ \large{\mathbf{A} = \begin{bmatrix}
a_{11} & a_{12} & \dots & a_{1n} \\
a_{21} & a_{22} & \dots & a_{2n} \\
\vdots & \vdots & \ddots & \vdots \\
a_{m1} & a_{m2} & \dots & a_{mn}
\end{bmatrix}} \]
金融应用:
- 一个 \(m \times n\) 矩阵可以表示 \(m\) 只股票过去 \(n\) 天的收益率数据。
- 一个 \(m \times m\) 的协方差矩阵可以表示 \(m\) 只股票收益率之间的联动关系。
矩阵在 Python 中的表示
我们同样使用 NumPy 数组来表示矩阵,此时它是一个二维数组。
代码
# 构造一个 2x3 的矩阵 (2 行, 3 列)
A = np.array([
# 写入矩阵 `A` 的第一行,固定三个列元素的教学数值。
[1,2,3],
# 写入矩阵 `A` 的第二行,使其形状为 2×3。
[-2, 12, 4]
# 完成矩阵 `A` 的二维数组定义,供形状与矩阵乘法示例复用。
])
# 标出随后展示的是矩阵 A,便于把打印结果与二维数组定义对应起来。
print('矩阵 A:')
# 输出二维数组 `A`,核对两行三列的矩阵布局。
print(A)
# 获取矩阵的维度
print(f'\n矩阵 A 的维度 (行, 列): {A.shape}')
矩阵 A:
[[ 1 2 3]
[-2 12 4]]
矩阵 A 的维度 (行, 列): (2, 3)
矩阵乘法:核心运算
- 可乘条件:\(\mathbf{A}\) 的列数必须等于 \(\mathbf{B}\) 的行数。
- 结果维度:\(m\times p\) 矩阵乘 \(p\times n\) 矩阵,得到 \(m\times n\) 矩阵。
- 元素计算:\((C)_{ij}\) 是 \(\mathbf{A}\) 第 \(i\) 行与 \(\mathbf{B}\) 第 \(j\) 列的点积。
\[ \large{(C)_{ij} = \sum_{k=1}^p a_{ik}b_{kj}} \]
矩阵乘法图解
计算 \(C_{11}\):取 A 的第 1 行和 B 的第 1 列,做点积。
矩阵乘法示例
给定 \(\mathbf{A} \in \mathbb{R}^{2 \times 3}\) 和 \(\mathbf{B} \in \mathbb{R}^{3 \times 2}\),求 \(\mathbf{C} = \mathbf{AB}\)。
\[ \large{\mathbf{A} = \begin{bmatrix} 1 & 0 & 2 \\ -2 & 12 & 4 \end{bmatrix}, \quad \mathbf{B} = \begin{bmatrix} 10 & -2 \\ 4 & 1 \\ 0 & 7 \end{bmatrix}} \]
\[ \large{C_{11} = (1 \cdot 10) + (0 \cdot 4) + (2 \cdot 0) = 10} \]
\[ \large{C_{12} = (1 \cdot -2) + (0 \cdot 1) + (2 \cdot 7) = 12} \]
\[ \large{C_{21} = (-2 \cdot 10) + (12 \cdot 4) + (4 \cdot 0) = 28} \]
\[ \large{C_{22} = (-2 \cdot -2) + (12 \cdot 1) + (4 \cdot 7) = 4 + 12 + 28 = 44} \]
\[ \large{\mathbf{C} = \begin{bmatrix} 10 & 12 \\ 28 & 44 \end{bmatrix}} \]
矩阵乘法在 Python 中的实现
与向量点积一样,我们使用 @ 运算符进行矩阵乘法。NumPy 会自动处理行与列的点积。
代码
# 复用上一页手算矩阵 `A`,让 Python 输出直接核对四个逐项结果。
A = np.array([[1, 0, 2], [-2, 12, 4]])
# 复用上一页手算矩阵 `B`,保持 2×3 @ 3×2 的维度关系不变。
B = np.array([[10, -2], [4, 1], [0, 7]])
# 矩阵乘法
C = A @ B
# 标出左乘矩阵 A,便于核对其 2×3 维度。
print('矩阵 A (2x3):')
# 输出 2×3 矩阵 `A`,确认左乘矩阵的维度。
print(A)
# 标出右乘矩阵 B,便于核对其 3×2 维度与乘法相容性。
print('\n矩阵 B (3x2):')
# 输出 3×2 矩阵 `B`,确认其行数与 `A` 的列数相等。
print(B)
# 标出矩阵 A 与 B 相乘得到的 2×2 结果。
print('\n结果 C = A @ B (2x2):')
# 输出乘积 `C`,验证 2×3 与 3×2 相乘得到 2×2 矩阵。
print(C)
矩阵 A (2x3):
[[ 1 0 2]
[-2 12 4]]
矩阵 B (3x2):
[[10 -2]
[ 4 1]
[ 0 7]]
结果 C = A @ B (2x2):
[[10 12]
[28 44]]
矩阵与向量的乘法
- 特例:把 \(p\) 维列向量看作 \(p\times1\) 矩阵。
- 维度:\(m\times p\) 的 \(\mathbf{A}\) 乘 \(p\) 维 \(\mathbf{x}\),得到 \(m\) 维 \(\mathbf{y}\)。
- 直觉:\(\mathbf{A}\) 表示线性变换,把输入 \(\mathbf{x}\) 映射为输出 \(\mathbf{y}=\mathbf{Ax}\)。
矩阵与标量的乘法
这与向量和标量的乘法类似:将矩阵中的每一个元素都与该标量相乘。
例如: \(a=2, \mathbf{B} = \begin{bmatrix} 10 & -2 \\ 4 & 1 \\ 0 & 7 \end{bmatrix}\)
\[ \large{a\mathbf{B} = 2 \begin{bmatrix} 10 & -2 \\ 4 & 1 \\ 0 & 7 \end{bmatrix} = \begin{bmatrix} 20 & -4 \\ 8 & 2 \\ 0 & 14 \end{bmatrix}} \]
代码
# 将缩放系数固定为 2,用于观察矩阵每个元素同步倍增。
a = 2
# 固定 `B` 的数值分量,作为“矩阵与标量的乘法”的可手算输入。
B = np.array([[10, -2],[1,2],[3,4]])
# 将矩阵 B 的每个元素乘以标量 2。
aB = a * B
# 输出 `aB`,核对标量 2 已作用于矩阵 `B` 的每个元素。
print(aB)
矩阵的逆:线性代数中的“除法”
- 标量类比:\(a\ne0\) 时,\(a\cdot a^{-1}=1\)。
- 矩阵对象:\(\mathbf{A}\) 必须是 \(n\times n\) 方阵。
- 可逆条件:存在同阶矩阵 \(\mathbf{B}\),使
\[
\mathbf{A}\mathbf{B}=\mathbf{B}\mathbf{A}=\mathbf{I}.
\]
- 记号:此时 \(\mathbf{B}=\mathbf{A}^{-1}\);\(\mathbf{I}\) 是主对角线为 1、其余为 0 的单位矩阵。
单位矩阵 (Identity Matrix)
单位矩阵 \(\mathbf{I}\) 在矩阵乘法中的作用,就像数字 1 在普通乘法中的作用。任何矩阵乘以单位矩阵,都等于其自身。
\[ \large{\mathbf{AI} = \mathbf{IA} = \mathbf{A}} \]
\[ \large{\mathbf{I}_3 = \begin{bmatrix}
1 & 0 & 0 \\
0 & 1 & 0 \\
0 & 0 & 1
\end{bmatrix}} \]
为何矩阵求逆很重要?
矩阵求逆的核心应用是求解线性方程组。
考虑一个简单的线性方程组:
\[ \large{\mathbf{Ax} = \mathbf{b}} \]
这里,\(\mathbf{A}\) 是系数矩阵,\(\mathbf{x}\) 是未知变量向量,\(\mathbf{b}\) 是常数向量。
如果我们知道 \(\mathbf{A}^{-1}\),我们可以两边左乘 \(\mathbf{A}^{-1}\):
\[ \large{\mathbf{A}^{-1}\mathbf{A}\mathbf{x} = \mathbf{A}^{-1}\mathbf{b}} \]
\[ \large{\mathbf{I}\mathbf{x} = \mathbf{A}^{-1}\mathbf{b}} \]
\[ \large{\mathbf{x} = \mathbf{A}^{-1}\mathbf{b}} \]
我们就直接解出了 \(\mathbf{x}\)!
金融应用:在投资组合优化中,求解最优权重通常归结为求解一个大型线性方程组。
求解 2x2 矩阵的逆:一个例子
我们用一个例子来展示如何求解。求矩阵 \(\mathbf{A}\) 的逆 \(\mathbf{A}^{-1}\)。
\[ \large{\mathbf{A} = \begin{bmatrix} 4 & 7 \\ 2 & 6 \end{bmatrix}} \]
我们设所求的逆矩阵为:
\[ \large{\mathbf{A}^{-1} = \begin{bmatrix} a & b \\ c & d \end{bmatrix}} \]
我们的目标是求解 \(a, b, c, d\)。
求解 2x2 矩阵的逆:建立方程
根据定义 \(\mathbf{A}\mathbf{A}^{-1} = \mathbf{I}\):
\[ \large{\begin{bmatrix} 4 & 7 \\ 2 & 6 \end{bmatrix} \begin{bmatrix} a & b \\ c & d \end{bmatrix} = \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}} \]
通过矩阵乘法,我们得到:
\[ \large{\begin{bmatrix} 4a + 7c & 4b + 7d \\ 2a + 6c & 2b + 6d \end{bmatrix} = \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}} \]
这给了我们两组独立的 \(2 \times 2\) 线性方程组。
求解 2x2 矩阵的逆:求解方程组
第一组 (求解 a, c):
- \(4a + 7c = 1\)
- \(2a + 6c = 0 \implies a = -3c\)
将 (2) 代入 (1): \(4(-3c) + 7c = 1 \implies -12c + 7c = 1 \implies -5c = 1 \implies c = -0.2\) 代回 (2): \(a = -3(-0.2) = 0.6\)
第二组 (求解 b, d):
- \(4b + 7d = 0 \implies b = -7d/4\)
- \(2b + 6d = 1\)
将 (3) 代入 (4): \(2(-7d/4) + 6d = 1 \implies -3.5d + 6d = 1 \implies 2.5d = 1 \implies d = 0.4\) 代回 (3): \(b = -7(0.4)/4 = -0.7\)
求解 2x2 矩阵的逆:最终结果
我们解出了 \(a=0.6, b=-0.7, c=-0.2, d=0.4\)。 所以,逆矩阵为:
\[ \large{\mathbf{A}^{-1} = \begin{bmatrix} 0.6 & -0.7 \\ -0.2 & 0.4 \end{bmatrix}} \]
验证:
\[ \large{\mathbf{A}\mathbf{A}^{-1} = \begin{bmatrix} 4 & 7 \\ 2 & 6 \end{bmatrix} \begin{bmatrix} 0.6 & -0.7 \\ -0.2 & 0.4 \end{bmatrix} = \begin{bmatrix} 2.4-1.4 & -2.8+2.8 \\ 1.2-1.2 & -1.4+2.4 \end{bmatrix} = \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}} = \mathbf{I} \]
验证通过!
矩阵求逆的 Python 实现
在实际应用中,我们从不手动求逆。我们让计算机来做,这更快也更不容易出错。
代码
# 固定 `A` 的数值分量,作为“矩阵求逆的 Python 实现”的可手算输入。
A = np.array([[1,2],[3,4]])
# 使用 np.linalg.inv() 函数求逆
try:
# 计算非奇异矩阵 `A` 的逆矩阵,供单位矩阵乘积检验。
A_inv = np.linalg.inv(A)
# 标出原矩阵 A,使逆矩阵结果保留可核对的输入。
print('矩阵 A:')
# 输出原矩阵 `A`,保留逆矩阵计算的输入证据。
print(A)
# 标出随后展示的是 A 的数值逆矩阵。
print('\n矩阵 A 的逆 A_inv:')
# 输出四舍五入后的逆矩阵,便于手算核对各元素。
print(np.round(A_inv, decimals=2))
# 验证 A @ A_inv 是否为单位矩阵
I = A @ A_inv
# 标出 A 与其逆矩阵的乘积,核对结果是否接近单位矩阵。
print('\n验证 A @ A_inv (结果应接近单位矩阵):')
# np.round() 用于处理微小的浮点数误差
print(np.round(I, decimals=5))
# 若矩阵奇异而无法求逆,则捕获线性代数错误并转入不可逆提示。
except np.linalg.LinAlgError:
# 报告矩阵不可逆,避免继续解读不存在的逆矩阵。
print("矩阵 A 是奇异矩阵 (singular), 不可逆。")
矩阵 A:
[[1 2]
[3 4]]
矩阵 A 的逆 A_inv:
[[-2. 1. ]
[ 1.5 -0.5]]
验证 A @ A_inv (结果应接近单位矩阵):
[[1. 0.]
[0. 1.]]
线性代数部分练习与应用
现在,让我们通过解决课本上的练习题来巩固我们学到的知识。这不仅是计算,更是将抽象概念应用到具体数字上的过程。
习题 2.5.1 向量运算 给定向量: \(\mathbf{x} = \begin{bmatrix} 1 \\ 2 \\ 3 \end{bmatrix}, \quad \mathbf{y} = \begin{bmatrix} -0.5 \\ 2 \\ 0 \end{bmatrix}\)
请计算:
- \(-2\mathbf{x}\)
- \(\mathbf{x}+\mathbf{y}\)
- \(\mathbf{x} \cdot \mathbf{y}\) (点积)
- \(\mathbf{x}^T\mathbf{y}\)
练习 2.5.1 Python 求解
代码
# 固定 `x` 的数值分量,作为“练习 2.5.1 Python 求解”的可手算输入。
x = np.array([1.0, 2.0,3.0])
# 固定 `y` 的数值分量,作为“练习 2.5.1 Python 求解”的可手算输入。
y = np.array([-0.5, 2, 0])
# 第 1 问:计算 -2x
# 将向量 x 的每个分量乘以 -2,求练习第一问。
neg_2x = -2 * x
# 打印 `-2x` 的三维结果,核对负标量同时改变方向和长度。
print(f'1. -2x = {neg_2x}')
# 第 2 问:计算 x + y
# 逐分量相加 `x` 与 `y`,得到练习第二问的三维向量和。
x_plus_y = x + y
# 打印 `x + y`,核对第二问的逐分量加法结果。
print(f'2. x + y = {x_plus_y}')
# 计算 `x` 与 `y` 的点积标量,回答第三问并核对向量维度相容。
dot_product = x @ y
# 输出 x 与 y 对应分量乘积之和,核对第三问点积。
print(f'3. x . y (dot product) = {dot_product}')
# 第 4 问:计算 x 的转置与 y 的乘积
# 在 NumPy 中, x.T @ y 对于一维数组和 x @ y 结果相同
# 但为了概念清晰,我们明确其数学含义
transpose_dot = x.T @ y
# 打印 `x^T y` 的标量结果,核对转置向量与列向量的内积。
print(f'4. x^T y = {transpose_dot}')
# 总结一维 NumPy 数组下点积与转置乘积给出同一标量。
print('\n结论: 向量的点积 (x.y) 和 x^T y 在计算上是等价的。')
1. -2x = [-2. -4. -6.]
2. x + y = [0.5 4. 3. ]
3. x . y (dot product) = 3.5
4. x^T y = 3.5
结论: 向量的点积 (x.y) 和 x^T y 在计算上是等价的。
练习 2.5.2 矩阵运算
给定矩阵 \(\mathbf{A} = \begin{pmatrix} 0.3 & -2.9 & -10 \\ -3 & 1 & 0.5 \end{pmatrix}\) 和上题中的向量 \(\mathbf{x} = \begin{bmatrix} 1 \\ 2 \\ 3 \end{bmatrix}\)。
请回答和计算:
- 该矩阵的维度是什么?
- 计算 \(\mathbf{Ax}\)。
- 求 \(\mathbf{A}^T\)。
练习 2.5.2 Python 求解
代码
# 为“练习 2.5.2 Python 求解”导入 `numpy` 并绑定 `np`,用于执行当前任务的数组、数值或随机机制计算。
import numpy as np
# 构造 2×3 矩阵 A
# 固定 `A` 的数值分量,作为“练习 2.5.2 Python 求解”的可手算输入。
A = np.array([
# 写入练习矩阵 `A` 的第一行系数,用于手算第一项线性组合。
[0.3, -2.9, -10],
# 写入练习矩阵 `A` 的第二行系数,用于手算第二项线性组合。
[-3, 1, 0.5]
# 完成练习矩阵 `A` 的 2×3 定义,使其列数与三元素向量 `x` 对齐。
])
# 向量 x 必须有三个元素才能与 A 的列数匹配
# 固定 `x` 的数值分量,作为“练习 2.5.2 Python 求解”的可手算输入。
x = np.array([1.0, 2.0, 3.0])
# 解包矩阵 `A` 的行数与列数,回答第一问的形状判断。
rows, cols = A.shape
# 报告“1. 矩阵 A 的维度是: {rows} 行, {cols} 列”中的 `rows`、`cols`,核对“练习 2.5.2 Python 求解”的样本形状。
print(f'1. 矩阵 A 的维度是: {rows} 行, {cols} 列')
# 计算矩阵 `A` 与向量 `x` 的乘积,回答第二问并核对输出维度。
# A(2x3) @ x(3,) -> 结果是 (2,) 向量
Ax = A @ x
# 打印矩阵—向量乘积 `Ax`,核对结果维度等于矩阵行数。
print(f'\n2. Ax = {Ax}')
# 展开矩阵第一行与向量的内积,供手算核对 Ax 的首个分量。
print(f' 计算过程 (第一行): (0.3*1) + (-2.9*2) + (-10*3) = {0.3 * 1 - 2.9 * 2 - 10 * 3}')
# 展开矩阵第二行与向量的内积,供手算核对 Ax 的第二个分量。
print(f' 计算过程 (第二行): (-3*1) + (1*2) + (0.5*3) = {-3 * 1 + 1 * 2 + 0.5 * 3}')
# 交换矩阵 `A` 的两个轴得到转置,回答第三问并核对形状反转。
A_T = A.T
# 打印转置矩阵 `A^T`,核对原矩阵行列互换后的形状与元素位置。
print(f'\n3. A 的转置 A^T 是:\n{A_T}')
# 报告“A^T 的维度是: {A_T.shape}”中的 `A_T`,核对“练习 2.5.2 Python 求解”的样本形状。
print(f' A^T 的维度是: {A_T.shape}')
1. 矩阵 A 的维度是: 2 行, 3 列
2. Ax = [-35.5 0.5]
计算过程 (第一行): (0.3*1) + (-2.9*2) + (-10*3) = -35.5
计算过程 (第二行): (-3*1) + (1*2) + (0.5*3) = 0.5
3. A 的转置 A^T 是:
[[ 0.3 -3. ]
[ -2.9 1. ]
[-10. 0.5]]
A^T 的维度是: (3, 2)
微积分:捕捉金融世界的动态变化
金融市场瞬息万变。资产价格、利率、风险,所有这些都不是静止的。我们如何描述和分析这些变化?
微积分,特别是微分,是研究“变化率”的数学语言。
- 导数 (Derivative):衡量当一个变量发生微小变化时,另一个函数变量会如何响应。
- 梯度 (Gradient):导数在多维空间中的推广,它指向函数增长最快的方向。
这对于金融中的优化问题——例如,最大化收益或最小化风险——至关重要。
一维微分:函数的变化率
微分的直观意义是函数在某一点的瞬时变化率,也就是该点切线的斜率。
- 记号:\(f'(x_0)\) 或 \(\frac{df}{dx}|_{x_0}\)。
- 含义:\(x\) 在 \(x_0\) 附近微小变动时,\(f(x)\) 的局部响应率。
金融应用:期权定价中的 “Delta” 就是期权价格(函数)相对于标的资产价格(变量)的导数。它告诉我们股票价格每变动1美元,期权价格会变动多少。
常见函数的导数
以下是一些我们需要熟记的求导法则:
- 常数: \(f(x) = c \implies f'(x) = 0\)
- 线性函数: \(f(x) = ax \implies f'(x) = a\)
- 幂函数: \(f(x) = x^n \implies f'(x) = nx^{n-1}\)
- 加法法则: \((f+g)' = f' + g'\)
- 乘法法则: \((fg)' = f'g + fg'\)
- 自然对数: \(f(x) = \ln(x) \implies f'(x) = \frac{1}{x}\)
- 指数函数: \(f(x) = e^x \implies f'(x) = e^x\)
链式法则:求解复合函数的导数
当我们处理嵌套函数(复合函数)时,例如 \(f(g(x))\),我们需要使用链式法则 (Chain Rule)。
\[ \large{\frac{d}{dx}f(g(x)) = f'(g(x)) \cdot g'(x)} \]
直观解释:“外层函数的导数(保持内层不变)”乘以“内层函数的导数”。
例子:求 \(h(x) = (x^2+1)^3\) 的导数。
- 外层函数 \(f(g) = g^3 \implies f'(g) = 3g^2\)
- 内层函数 \(g(x) = x^2+1 \implies g'(x) = 2x\)
- \(h'(x) = f'(g(x)) \cdot g'(x) = 3(x^2+1)^2 \cdot (2x) = 6x(x^2+1)^2\)
多维微分:梯度 (Gradient)
当函数有多个输入变量时,例如 \(f(x_1, x_2, \dots, x_n)\),我们如何描述它的变化?
- 偏导数 \(\frac{\partial f}{\partial x_k}\):只让 \(x_k\) 变动,其他变量保持不变时的变化率。
- 梯度 \(\nabla f(\mathbf{x})\):把所有偏导数按变量顺序收集成一个向量。
\[ \large{\nabla f(\mathbf{x}) = \begin{bmatrix} \frac{\partial f}{\partial x_1} \\ \frac{\partial f}{\partial x_2} \\ \vdots \\ \frac{\partial f}{\partial x_n} \end{bmatrix}} \]
梯度的几何意义:最陡峭的登山路径
梯度向量 \(\nabla f(\mathbf{x})\) 指向函数 \(f\) 在点 \(\mathbf{x}\) 处增长最快的方向。
梯度下降法:机器学习的引擎
这个“最陡峭下坡”的概念是机器学习模型训练的核心算法——梯度下降 (Gradient Descent)——的基础。
为了找到一个函数(例如,模型的“损失函数”)的最小值,我们从一个随机点开始,然后反复地沿着负梯度方向小步移动。
\[ \large{\mathbf{x}_{\text{new}} = \mathbf{x}_{\text{old}} - \eta \nabla f(\mathbf{x}_{\text{old}})} \]
- \(\eta\) (eta) 是学习率 (learning rate),控制我们每一步走多远。
这个简单的迭代过程,是训练从线性回归到深度神经网络等众多模型的基础。
寻找局部最大/最小值
一个可微函数的局部最大值或最小值(统称为极值点)在哪里出现?
- 一维情况:在 \(f'(x_0) = 0\) 的点。即函数切线斜率为0(水平)的地方。
- 多维情况:在 \(\nabla f(\mathbf{x}_0) = \mathbf{0}\) 的点。即梯度为零向量,在任何方向上函数值的瞬时变化率都为0。
这个条件是函数取得极值的必要条件 (Necessary Condition),但不是充分条件 (Sufficient Condition)。一个梯度为0的点也可能是鞍点。
寻找极值的例子
我们来看一个函数 \(f(x) = x^3 - 6x^2 + 5x + 12\)。
求导:\(f'(x) = 3x^2 - 12x + 5\)
令导数为0:\(3x^2 - 12x + 5 = 0\)
求解:使用求根公式 \(x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}\)
\[ \large{x = \frac{12 \pm \sqrt{(-12)^2 - 4(3)(5)}}{2(3)} = \frac{12 \pm \sqrt{144 - 60}}{6} = \frac{12 \pm \sqrt{84}}{6}} \]
所以极值点大约在 \(x \approx 0.47\) 和 \(x \approx 3.53\)。
极值点可视化
代码
# 为“极值点可视化”导入 `numpy` 并绑定 `np`,用于执行当前任务的数组、数值或随机机制计算。
import numpy as np
# 为“极值点可视化”导入 `matplotlib.pyplot` 并绑定 `plt`,用于构建当前任务的坐标轴并呈现比较结果。
import matplotlib.pyplot as plt
# 定义 `f`,计算给定多项式的函数值。
def f(x):
# 返回 `x**3 - 6*x**2 + 5*x + 12`,把“极值点可视化”的计算结果交给调用方。
return x**3 - 6*x**2 + 5*x + 12
# 求解导数为0的点
# 驻点满足导数方程 f'(x) = 3x² - 12x + 5 = 0
# 求解导函数二次方程的两个根,定位三次函数的驻点。
roots = np.roots([3, -12, 5])
# 将两个驻点按横坐标排序:左侧对应局部极大值,右侧对应局部极小值。
x_max, x_min = np.sort(roots)
# 绘图
x_vals = np.linspace(-1, 5, 400)
# 在等距横轴网格上计算三次函数值,形成连续曲线纵坐标。
y_vals = f(x_vals)
# 为“极值点可视化”建立投影画布,给坐标、图例与标注预留空间。
plt.figure(figsize=(10, 6))
# 绘出三次函数主曲线,为两个驻点标记提供共同坐标参照。
plt.plot(x_vals, y_vals, label='$f(x) = x^3 - 6x^2 + 5x + 12$', color='#0A369D')
# 标出导数为零且曲率向下的局部最大值。
plt.plot(x_max, f(x_max), 'o', color='#F79F79', markersize=8, label=f'局部最大值 at x={x_max:.2f}')
# 标出另一驻点对应的局部最小值,便于与最大值比较。
plt.plot(x_min, f(x_min), 'o', color='#228B22', markersize=8, label=f'局部最小值 at x={x_min:.2f}')
# 将图题设为“函数及其局部极值点”,直接说明当前图形的比较目的。
plt.title('函数及其局部极值点', fontsize=16)
# 为“极值点可视化”,将横轴标为“x”,明确横向编码的变量。
plt.xlabel('x', fontsize=12)
# 将纵轴标为“f(x)”,明确纵向编码的变量。
plt.ylabel('f(x)', fontsize=12)
# 固定 `plt.axhline(0, color` 的业务取值与顺序,作为“极值点可视化”的可复算输入。
plt.axhline(0, color='gray', linestyle='--', linewidth=0.5)
# 为“极值点可视化”添加辅助网格,便于比较位置、斜率或组间差异。
plt.grid(True, linestyle='--', alpha=0.6)
# 显示“极值点可视化”图例,使颜色或线型与比较对象一一对应。
plt.legend(fontsize=12)
# 显示目标函数曲线及驻点标记,核对导数为零的位置对应局部极值候选。
plt.show()
练习 2.5.2.1 微积分计算
给定函数 \(f(x_1, x_2) = -5x_1^3 - x_2^2 + x_1x_2 - 5\)。
当 \(x_1=5, x_2=-1\) 时,求:
- \(\frac{\partial f(x_1, x_2)}{\partial x_1}\)
- \(\nabla f(x_1, x_2)\)
练习 2.5.2.1 手动求解
1. 求关于 \(x_1\) 的偏导数 把 \(x_2\) 看作常数,对 \(x_1\) 求导:
\[ \large{\frac{\partial f}{\partial x_1} = \frac{\partial}{\partial x_1}(-5x_1^3 - x_2^2 + x_1x_2 - 5)} \]
\[ \large{= -5(3x_1^2) - 0 + (1 \cdot x_2) - 0 = -15x_1^2 + x_2} \]
代入 \(x_1=5, x_2=-1\):
\[ \large{\frac{\partial f}{\partial x_1} \bigg|_{(5, -1)} = -15(5^2) + (-1) = -15(25) - 1 = -375 - 1 = -376} \]
练习 2.5.2.1 手动求解 (续)
2. 求梯度 首先求关于 \(x_2\) 的偏导数 (把 \(x_1\) 看作常数):
\[ \large{\frac{\partial f}{\partial x_2} = \frac{\partial}{\partial x_2}(-5x_1^3 - x_2^2 + x_1x_2 - 5)} \]
\[ \large{= 0 - 2x_2 + (x_1 \cdot 1) - 0 = -2x_2 + x_1} \]
梯度向量是:
\[ \large{\nabla f(x_1, x_2) = \begin{bmatrix} \frac{\partial f}{\partial x_1} \\ \frac{\partial f}{\partial x_2} \end{bmatrix} = \begin{bmatrix} -15x_1^2 + x_2 \\ -2x_2 + x_1 \end{bmatrix}} \]
代入 \(x_1=5, x_2=-1\):
\[ \large{\nabla f(5, -1) = \begin{bmatrix} -15(5^2) + (-1) \\ -2(-1) + 5 \end{bmatrix} = \begin{bmatrix} -376 \\ 2 + 5 \end{bmatrix} = \begin{bmatrix} -376 \\ 7 \end{bmatrix}} \]
练习 2.5.2.1 Python 求解 (使用 SymPy)
我们可以使用符号计算库 SymPy 来验证我们的结果。
代码
# 导入 SymPy 并绑定为 `sp`,用于符号求偏导、构造梯度及在指定点代入求值。
import sympy as sp
# 定义符号变量
x1, x2 = sp.symbols('x1 x2')
# 定义函数
f = -5*x1**3 - x2**2 + x1*x2 - 5
# 对目标函数关于 `x1` 做符号求导,保留交叉项对该分量的贡献。
df_dx1 = sp.diff(f, x1)
# 打印关于 `x1` 的符号偏导,核对幂函数与交叉项的求导规则。
print(f'df/dx1 = {df_dx1}')
# 代入数值
val_df_dx1 = df_dx1.subs({x1: 5, x2: -1})
# 输出偏导数在 (5, -1) 的取值,核对局部变化率。
print(f'在 (5, -1) 处 df/dx1 的值为: {val_df_dx1}')
# 按 `x1`、`x2` 顺序计算两个符号偏导,组成梯度向量。
grad_f = [sp.diff(f, var) for var in (x1, x2)]
# 打印由两个符号偏导组成的梯度向量,核对分量顺序为 `x1`、`x2`。
print(f'\n梯度向量 ∇f = {grad_f}')
# 代入数值
val_grad_f = [expr.subs({x1: 5, x2: -1}) for expr in grad_f]
# 打印梯度在 `(5, -1)` 的数值 `[-376, 7]`,核对手算代入结果。
print(f'在 (5, -1) 处 ∇f 的值为: {val_grad_f}')
df/dx1 = -15*x1**2 + x2
在 (5, -1) 处 df/dx1 的值为: -376
梯度向量 ∇f = [-15*x1**2 + x2, x1 - 2*x2]
在 (5, -1) 处 ∇f 的值为: [-376, 7]
统计学:在不确定性中寻找规律
金融市场充满了不确定性和随机性。明天的股价是多少?下个季度的GDP增长率如何?我们无法精确预测。
统计学是科学地处理不确定性的工具。它帮助我们:
- 描述数据:用均值、方差等指标总结数据的特征。
- 推断关系:用相关性、回归等方法发现变量间的联系。
- 量化风险:概率论是衡量和管理金融风险的基础。
随机变量:不确定的数值结果
随机变量 (Random Variable) 是一个其数值取决于随机事件结果的变量。我们通常用大写字母表示,如 \(X\)。
- 离散随机变量:只能取有限个或可数个值。
- 例子:掷骰子的点数 (1, 2, 3, 4, 5, 6);一天内某支股票上涨的天数。
- 连续随机变量:可以取某一区间内的任何值。
- 例子:明天沪深300指数的收益率;一家中国上市公司未来的盈利。
随机变量的一个具体实现值(观测值)用小写字母表示,如 \(x_i\)。
期望:随机变量的“平均”取值
期望 (Expectation) 或期望值 \(E(X)\),是随机变量 \(X\) 所有可能取值按其概率加权的平均值。它是对随机变量中心趋势的度量。
- 离散型:\(E(X) = \sum_{k} x_k p(x_k)\)
- 例子:掷一个公平骰子,期望值是 \(1\cdot\frac{1}{6} + 2\cdot\frac{1}{6} + \dots + 6\cdot\frac{1}{6} = 3.5\)。
- 连续型:\(E(X) = \int_{-\infty}^{\infty} x f(x) dx\),其中 \(f(x)\) 是概率密度函数。
在金融中,预期收益率 (Expected Return) 就是资产未来收益率这个随机变量的期望值。
样本均值 vs. 期望
设样本均值为:
\[ \large{\bar X_n = \frac{1}{n}\sum_{i=1}^n X_i} \]
若 \(X_i\) 独立同分布且 \(E|X_i|<\infty\),大数定律给出 \(\bar X_n\xrightarrow{p}\mu=E(X)\):对任意 \(\varepsilon>0\),\(P(|\bar X_n-\mu|>\varepsilon)\to0\)。
弱依赖序列也有相应定律,但须另加平稳/遍历或混合性与矩条件。金融收益可能存在依赖、非平稳或厚尾,不能只凭样本量大就假定近似可靠。
方差衡量离散,风险解释需要口径
方差 (Variance) 记为 \(\operatorname{Var}(X)\) 或 \(\sigma^2\),衡量相对期望的平方离散程度。
若 \(X_1,\ldots,X_n\) 是同一总体的独立同分布随机样本且 \(E(X^2)<\infty\),则 \(E(s^2)=\sigma^2\)。这一无偏性不能外推到任意依赖或非平稳收益序列。
标准差 (Standard Deviation) \(\sigma = \sqrt{\text{Var}(X)}\),是方差的平方根,其单位与原始数据相同,更易于解释。
给定持有期、收益口径与风险偏好后,方差/标准差可作为波动风险代理之一;它不单独覆盖尾部风险、下行不对称或流动性损失。
固定预测点定义偏差、方差与噪声
令 \(\mathcal D\) 表示重复抽取的训练集,\(m(x_0)=E(Y\mid X=x_0)\)。固定 \(x_0\):
- 偏差:\(E_{\mathcal D}[\hat f_{\mathcal D}(x_0)]-m(x_0)\),相对真实条件均值定义。
- 方差:\(\operatorname{Var}_{\mathcal D}[\hat f_{\mathcal D}(x_0)]\),衡量重复训练样本下的预测波动。
- 不可约噪声:若 \(Y_0=m(x_0)+\varepsilon_0\) 且 \(E(\varepsilon_0\mid X=x_0)=0\),则 \(\operatorname{Var}(\varepsilon_0\mid X=x_0)\) 单列。
平方预测损失的期望同时对重复训练样本与新响应噪声取值。
偏差-方差的权衡
一个好的模型需要在偏差和方差之间取得平衡。
相关性:衡量线性关系
相关性 (Correlation) 是量化两个变量之间变动一致性程度的指标。
- 正相关:一个变量增加时,另一个变量也倾向于增加。
- 负相关:一个变量增加时,另一个变量倾向于减少。
- 不相关:两个变量之间没有明显的线性关系。
重要警告:相关性 \(\neq\) 因果性! (Correlation does not imply causation!) 一个经典的例子:夏天的冰淇淋销量和溺水人数高度正相关,但这不意味着吃冰淇淋会导致溺水。背后隐藏的变量是“天气炎热”。
相关性可视化
代码
# 为“相关性可视化”导入 `numpy` 并绑定 `np`,用于执行当前任务的数组、数值或随机机制计算。
import numpy as np
# 为“相关性可视化”导入 `matplotlib.pyplot` 并绑定 `plt`,用于构建当前任务的坐标轴并呈现比较结果。
import matplotlib.pyplot as plt
# 为“相关性可视化”,固定随机数序列,使课堂示例可重复。
np.random.seed(42)
# 在给定区间均匀抽取 `x`,覆盖“相关性可视化”的输入范围。
x = np.random.rand(50)
# 构造正相关样本
# 在 `x` 上叠加标准差 0.1 的噪声,构造接近 45 度线的正相关响应。
y1 = x + np.random.normal(0, 0.1, 50)
# 构造近似不相关样本
# 在给定区间均匀抽取 `y2`,覆盖“相关性可视化”的输入范围。
y2 = np.random.rand(50)
# 构造负相关样本
# 在 `-x` 上叠加同尺度噪声,构造斜率为负的相关响应。
y3 = -x + np.random.normal(0, 0.1, 50)
# 创建本图所需画布与坐标轴,并保存到 `(fig, (ax1, ax2, ax3))`。
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(15, 5), sharex=True, sharey=True)
# 以 `x` 为横轴、`y1` 为纵轴绘制散点,展示“正相关、不相关与负相关”。
ax1.scatter(x, y1, alpha=0.7, c='#0A369D')
# 计算 `corr1`(相关系数),概括“相关性可视化”中的样本特征。
corr1 = np.corrcoef(x, y1)[0, 1]
# 将图题设为“正相关 (r ≈ {corr1:.2f})”,明确这里展示有限样本统计量。
ax1.set_title(f'正相关 (r ≈ {corr1:.2f})', fontsize=16)
# 以 `x` 为横轴、`y2` 为纵轴绘制散点,展示“正相关、不相关与负相关”。
ax2.scatter(x, y2, alpha=0.7, c='#465659')
# 计算 `corr2`(相关系数),概括“相关性可视化”中的样本特征。
corr2 = np.corrcoef(x, y2)[0, 1]
# 将图题设为“不相关 (r ≈ {corr2:.2f})”,明确这里展示有限样本统计量。
ax2.set_title(f'不相关 (r ≈ {corr2:.2f})', fontsize=16)
# 以 `x` 为横轴、`y3` 为纵轴绘制散点,展示“正相关、不相关与负相关”。
ax3.scatter(x, y3, alpha=0.7, c='#DC143C')
# 计算 `corr3`(相关系数),概括“相关性可视化”中的样本特征。
corr3 = np.corrcoef(x, y3)[0, 1]
# 将图题设为“负相关 (r ≈ {corr3:.2f})”,明确这里展示有限样本统计量。
ax3.set_title(f'负相关 (r ≈ {corr3:.2f})', fontsize=16)
# 遍历 `[ax1, ax2, ax3]` 的候选或观测,为“相关性可视化”逐项更新结果。
for ax in [ax1, ax2, ax3]:
# 为“相关性可视化”,将横轴标为“X”,明确横向编码的变量。
ax.set_xlabel('X')
# 为“相关性可视化”添加辅助网格,便于比较位置、斜率或组间差异。
ax.grid(True, linestyle='--', alpha=0.6)
# 将纵轴标为“Y”,明确纵向编码的变量。
ax1.set_ylabel('Y')
# 为“相关性可视化”,压缩子图留白,避免标题和坐标标签相互遮挡。
plt.tight_layout()
# 显示不同相关系数下的散点图,比较正相关、近零相关与负相关的方向和紧密程度。
plt.show()
Pearson 相关系数
样本 Pearson 相关系数 \(r_{xy}\) 衡量有限样本中两个连续变量的线性关系;总体参数另记为 \(\rho_{XY}\)。
\[ \large{r_{xy} = \frac{\sum_{i=1}^n (x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum_{i=1}^n (x_i - \bar{x})^2} \sqrt{\sum_{i=1}^n (y_i - \bar{y})^2}}}
\]
总体相关系数定义为 \(\rho_{XY}=\operatorname{Cov}(X,Y)/(\sigma_X\sigma_Y)\);样本公式用同一批中心化观测直接计算,无需混用总体协方差符号。
- \(r_{xy}\) 与 \(\rho_{XY}\) 的取值范围均为 \([-1,1]\),但前者是样本统计量,后者是总体参数。
- 绝对值越接近 1,线性关系越强;等于 \(1\) 或 \(-1\) 表示完全正/负线性关系。
- 接近 0 只表示线性关系弱,不排除非线性关系。
回归分析中的重要指标
在用模型(例如线性回归)进行预测时,我们需要一些指标来评估模型的好坏。这些指标都围绕着残差 (Residual)——真实值与预测值之间的差异——来构建。
- 残差平方和 (RSS): 衡量模型整体误差的大小。
- 总离差平方和 (TSS): 衡量数据本身的总波动。
- 判定系数 (R²): 衡量模型解释了数据总波动的百分比。
判定系数 R²
判定系数 (Coefficient of Determination, R²) 是衡量模型拟合优度的核心指标。它表示因变量 \(y\) 的总变异中,可以被自变量 \(x\) 解释的比例。
\[ \large{R^2 = 1 - \frac{\text{RSS}}{\text{TSS}} = \frac{\text{TSS} - \text{RSS}}{\text{TSS}}}
\]
- \(R^2\) 的取值范围通常在 [0, 1] 之间。
- \(R^2 = 0.9\) 意味着按 TSS 口径,模型解释了样本总变异的 90%。
- \(R^2\) 越高,模型的解释能力越强。
- 边界:R² 是相对于同一目标样本均值基准的量。只有目标定义、样本和评估口径一致时,才适合比较模型;不能把不同数据集上的 R² 直接排序。
统计学部分练习:真实金融数据分析
理论知识需要通过实践来巩固。现在我们不再使用书上的小数据集,而是用真实的金融数据来计算这些统计量。
任务:分析江苏恒瑞医药 (600276.XSHG) 与浙江海康威视 (002415.XSHE) 在 2023 年的日收益率关系。
我们将从公开前复权行情 HDF5 选择性读取数据,并计算:
- 各自的平均日收益率和标准差。
- 它们之间的 Pearson 相关系数。
在查看实现前,提交数据文件/key/字段/复权口径/样本期、两股收益均值与标准差、共同交易日数及相关系数。缺共同日期对齐或复权口径者先补齐。
练习:先完成再查看答案:金融统计作业结果
提交一张统计表、一张对齐检查表和一句风险解释。以字段可追溯、日期内连接、统计量可复算为通过;提交后再进入参考实现。
获取并处理金融数据
代码
from pathlib import Path # 管理下载后的课程数据路径
from urllib.request import urlretrieve # 复用本章隐藏设置单元安装的浏览器标识下载器
import numpy as np # 计算对数收益率
import pandas as pd # 整理长三角公司行情
# 按 Linux 共享数据、Windows 共享数据、项目缓存的顺序选择行情文件。
price_data_path = next((candidate_path for candidate_path in [Path('/home/ubuntu/r2_data_mount/data/stock/stock_price_pre_adjusted.h5'), Path('C:/qiufei/data/stock/stock_price_pre_adjusted.h5'), Path('data/course/stock_price_pre_adjusted.h5')] if candidate_path.exists()), Path('data/course/stock_price_pre_adjusted.h5'))
if not price_data_path.exists():
price_data_path.parent.mkdir(parents=True, exist_ok=True)
urlretrieve('https://assets.qiufei.site/data/stock/stock_price_pre_adjusted.h5', price_data_path)
company_ids = ['600276.XSHG', '002415.XSHE'] # 固定江苏与浙江公司样本
company_price_frames = [] # 收集选择性读取的日行情
for company_id in company_ids: # 逐公司读取以避免载入全市场文件
company_prices = pd.read_hdf(price_data_path, key='data', where=f'order_book_id == "{company_id}"', columns=['order_book_id', 'date', 'close']).reset_index() # 将 HDF 索引恢复为公司与日期列
company_price_frames.append(company_prices) # 保存当前公司记录
company_prices = pd.concat(company_price_frames, ignore_index=True) # 合并两家公司行情
company_prices = company_prices.query("'2023-01-01' <= date <= '2023-12-31'") # 固定 2023 年样本
close_prices = company_prices.pivot(index='date', columns='order_book_id', values='close').sort_index().dropna() # 对齐共同交易日
returns = np.log(close_prices / close_prices.shift(1)).dropna() # 以前复权收盘价计算对数收益率
returns = returns.rename(columns={'600276.XSHG': 'Hengrui', '002415.XSHE': 'Hikvision'}) # 使用可读公司简称
print(returns.head()) # 核对公开数据结果
order_book_id Hikvision Hengrui
date
2023-01-04 -0.010849 0.008547
2023-01-05 0.012597 0.019920
2023-01-06 -0.017623 -0.006086
2023-01-09 -0.020355 0.006086
2023-01-10 0.008132 -0.004309
参考答案(1/3):输入与对齐检查
代码
sample_row_counts = company_prices.groupby('order_book_id').size() # 统计2023年筛选后两股原始价格行数
duplicate_key_count = company_prices.duplicated(['order_book_id', 'date']).sum() # 核对公司日期键是否重复
missing_input_count = company_prices[['date', 'close']].isna().sum().sum() # 核对日期与收盘价字段是否缺失
alignment_audit_table = pd.DataFrame({ # 采用两列纵向表,避免十一项检查横向溢出
'检查项': ['文件', 'key', '字段', '复权口径', '样本期', '恒瑞原始行数', '海康原始行数', '共同价格日数', '收益共同日数', '重复键数', '日期或收盘价缺失数'], # 固定提交要求的自查顺序
'结果': ['stock/stock_price_pre_adjusted.h5', 'data', '证券代码、交易日、收盘价', '前复权收盘价', '2023-01-01至2023-12-31', int(sample_row_counts['600276.XSHG']), int(sample_row_counts['002415.XSHE']), len(close_prices), len(returns), int(duplicate_key_count), int(missing_input_count)] # 用公开数据说明和动态行数填入检查结果
})
display(alignment_audit_table) # 展示可直接自查的对齐检查表
练习 2.5.3 统计量计算
我们以恒瑞医药收益为 \(y\),海康威视收益为 \(x\)。
代码
required_return_columns = {'Hengrui', 'Hikvision'} # 确定统计表所需的两只股票列
assert required_return_columns.issubset(returns.columns) # 缺任一目标列时立即停止,避免静默漏报
correlation = returns['Hengrui'].corr(returns['Hikvision']) # 计算共同交易日样本 Pearson 相关系数
statistics_result_table = pd.DataFrame({ # 形成与提交要求一一对应的两行统计表
'公司': ['恒瑞医药', '海康威视'], # 使用业务可读的公司名称
'平均日对数收益': [returns['Hengrui'].mean(), returns['Hikvision'].mean()], # 报告两股样本均值
'日收益标准差(ddof=1)': [returns['Hengrui'].std(), returns['Hikvision'].std()], # 报告两股样本标准差
'共同样本数': [len(returns), len(returns)], # 显式报告同一日期内连接样本量
'两股Pearson r': [correlation, correlation] # 在两行保留同一个同期共动统计量
})
display(statistics_result_table.round(6)) # 展示可直接自查的统计结果表
| 0 |
恒瑞医药 |
0.000690 |
0.018970 |
241 |
0.208435 |
| 1 |
海康威视 |
0.000134 |
0.020606 |
241 |
0.208435 |
结果解读与可视化
相关系数以当前公开数据版本的实际输出为准,不预设为 0.7。它只描述 2023 年共同交易日的线性共动,不代表稳定关系或因果联系。
代码
# 为“结果解读与可视化”导入 `seaborn` 并绑定 `sns`,用于编码当前任务的统计分布或分组关系。
import seaborn as sns
# 为“结果解读与可视化”导入 `matplotlib.pyplot` 并绑定 `plt`,用于构建当前任务的坐标轴并呈现比较结果。
import matplotlib.pyplot as plt
# 为“结果解读与可视化”建立投影画布,给坐标、图例与标注预留空间。
plt.figure(figsize=(11, 6))
# 用 `sns.regplot` 绘制散点并叠加线性拟合。
sns.regplot(data=returns, x='Hikvision', y='Hengrui',
# 将散点设为半透明深蓝色,便于观察重叠密度。
scatter_kws={'alpha':0.5, 'color': '#0A369D'},
# 将拟合线设为红色,使其与散点清楚区分。
line_kws={'color': '#DC143C'})
# 用图题概括“结果解读与可视化”的比较对象与当前计算结果。
plt.title(f'恒瑞医药与海康威视的日收益率(2023)\n相关系数 = {correlation:.2f}', fontsize=18)
# 将横轴标为“海康威视日收益率”,明确横向编码的变量。
plt.xlabel('海康威视日收益率', fontsize=14)
# 将纵轴标为“恒瑞医药日收益率”,明确纵向编码的变量。
plt.ylabel('恒瑞医药日收益率', fontsize=14)
# 为“结果解读与可视化”添加辅助网格,便于比较位置、斜率或组间差异。
plt.grid(True, linestyle='--', alpha=0.6)
# 呈现海康威视横轴与恒瑞医药纵轴的收益散点,供核对相关方向与离群点。
plt.show()
章节总结:我们的数学工具箱
今天,我们重新审视了构建现代金融和量化分析的三大数学支柱:
- 线性代数:提供了描述和操作多维金融数据的语言。
- 微积分:让我们能够分析和优化动态变化的金融系统。
- 统计学:使我们能在不确定性中量化规律、风险和关系。
结束语
这些不是孤立的工具。在接下来的课程中,我们将看到它们如何协同工作,构建起强大的金融机器学习模型。
请务必花时间用Python复现今天的每一个例子。 这些基础越扎实,我们后面学习复杂模型时就会越轻松。
谢谢大家。
本章小结
- 能区分 L2 归一化、中心化与 z-score,并在分母非零时解释中心化余弦与 Pearson 相关。
- 维度、单位、ddof 与零范数必须在计算前声明。
- 相关性和几何相似度不建立因果或经济机制。
- 下一必修章先进入第 6 章验证基础,再把这些工具用于回归与分类。