
上次笔记,复习了第十七章Numpy线性代数关于矩阵行/列向量拆解的方法;复习了向量运算,包括求向量的模linalg.norm()、单位向量、向量内积np.dot()、向量夹角弧度np.arccos(),角度np.rad2deg(),以及将二维向量“压扁”的方法np.dot(flatten())。
本次笔记将包括以下内容:
17.3 向量化运算、格拉姆矩阵(掰开揉碎详细版)
17.4 矩阵运算
---
今天笔记,从一之前有写笔记,但我还没认真展开思考的题开始:
【如果需要计算鸢尾花数据矩阵X,所有行向量之间的两两夹角,又要避免使用for循环,该怎么办?】
可以通过下图的“夹角角度矩阵”来求解。
需要注意的是:左边的矩形矩阵和右边“三角矩阵”实现的功能是完全相同的。右边只是将一些重复的矩阵计算空间省去而已。

但即使只做右边三角矩阵的运算,仍是很大计算量的工程。
如何才能更加快捷的去进行计算呢?就涉及到下面的“向量化运算”思想,以及重要的工具“格拉姆矩阵”了。
--
17.3 向量化运算、格拉姆矩阵(掰开揉碎详细版)
0. 先给出视频链接,以免后面我自己复习的时候找不到:
【Chapter 17 NumPy线性代数 | 49:09】
https://www.bilibili.com/video/BV1VD421L7Hf/
1. 格拉姆矩阵的本质,还是向量化运算。向量化运算的关键,还是矩阵乘法。(所以我前后几条笔记,都在反反复复记录,绕不过去,本质上就是不理解这三者的关系,有点纠缠,现在把它们的层次关系理清,就清爽多了)


2. 书本提到的关系:矩阵乘法->格拉姆矩阵


作者就很形象,用“啪的一声”把两个表的相乘的动作表现出来了。
学术化一点表述,格拉姆矩阵是这样写的(G = X的T次方 · X):

(书本作者也说到,实际上本书写到这里,能理解以上意思即可。)
3. 格拉姆矩阵的代码(简略版,只保留核心要素)(详细版的见书本/视频/chapter17文档/上两条笔记)
# 计算格拉姆矩阵G = X.T @ Xfig,axs = plt.subplots(1,5,figsize = (8,3),gridspec_kw={'width_ratios': [3, 0.5, 3, 0.5, 3]})plt.sca(axs[0])ax = sns.heatmap(G, cmap = 'RdYlBu_r',vmax = 5000, vmin = 0,annot = False,fmt=".0f",cbar_kws = {'orientation':'horizontal'},xticklabels = False,yticklabels=False,square = 'equal')plt.title('$G$')plt.sca(axs[1])plt.title('=')plt.axis('off')# 绘制X转置plt.sca(axs[2])ax = sns.heatmap(X.T, cmap = 'RdYlBu_r',vmax = 0, vmin = 8,cbar_kws = {'orientation':'horizontal'},xticklabels = False,yticklabels = False,annot=False)plt.title('$X^T$')plt.sca(axs[3])plt.title('@')plt.axis('off')# 绘制Xplt.sca(axs[4])ax = sns.heatmap(X, cmap = 'RdYlBu_r',vmax = 0, vmin = 8,cbar_kws = {'orientation':'horizontal'},xticklabels = False,yticklabels=False,annot=False)plt.title('$X$')fig.savefig('Figures/X的格拉姆矩阵.svg', format='svg')
3.1运行结果:
4. 其实还可以有第二个格拉姆矩阵(它们互为“伪逆”或“投影”关系),可以理解为:

4.1 专业描述为:(H = X · X的T次方 )(刚好和G的因数调换)

4.2 第二个格拉姆矩阵的代码(简化版):
# 第二个格拉姆矩阵H = X @ X.Tfig,axs = plt.subplots(1,5,figsize = (8,3),gridspec_kw={'width_ratios': [3, 0.5, 3, 0.5, 3]})plt.sca(axs[0])# 绘制格拉姆矩阵ax = sns.heatmap(H, cmap = 'RdYlBu_r',vmin = 0,vmax = 150,annot = False,fmt=".0f",cbar_kws = {'orientation':'horizontal'},xticklabels = False,yticklabels=False,square = 'equal')plt.title('$H$')plt.sca(axs[1])plt.title('=')plt.axis('off')# 绘制X转置plt.sca(axs[2])ax = sns.heatmap(X, cmap = 'RdYlBu_r',vmax = 0, vmin = 8,cbar_kws = {'orientation':'horizontal'},xticklabels = False,yticklabels = False,annot=False)plt.title('$X$')plt.sca(axs[3])plt.title('@')plt.axis('off')# 绘制Xplt.sca(axs[4])ax = sns.heatmap(X.T, cmap = 'RdYlBu_r',vmax = 0, vmin = 8,cbar_kws = {'orientation':'horizontal'},xticklabels = False,yticklabels=False,annot=False)plt.title('$X^T$')fig.savefig('Figures/XT的格拉姆矩阵.svg', format='svg')
4.3 运行结果:

5. 矩阵的逆
其实有了前面格拉姆矩阵的铺垫,矩阵的逆也很好理解了:
更像是一种“可逆转”的矩阵,在某个方向上“拧一下”,拧为不同的方向,又从正相反的方向“拧回去”,诶,您猜怎么着,居然变回原来的矩阵了!
原本作者的比喻是“啪的一下拍扁”,ai认为不够严谨,因为“拍扁”意味着维度降低(不可逆),而“拧”意味着形变但保持维度(可逆)。
5.1 矩阵的逆的代码
# 计算格拉姆矩阵G的逆矩阵G_inv = np.linalg.inv(G)fig,axs = plt.subplots(1,5,figsize = (8,3),gridspec_kw={'width_ratios': [3, 0.5, 3, 0.5, 3]})plt.sca(axs[0])# 绘制格拉姆矩阵ax = sns.heatmap(np.eye(4), cmap = 'RdYlBu_r',annot = False,fmt=".0f",cbar_kws = {'orientation':'horizontal'},xticklabels = False,yticklabels=False,square = 'equal')plt.title('$I$')plt.sca(axs[1])plt.title('=')plt.axis('off')plt.sca(axs[2])ax = sns.heatmap(G, cmap = 'RdYlBu_r',cbar_kws = {'orientation':'horizontal'},xticklabels = False,yticklabels = False,annot=False)plt.title('$G$')plt.sca(axs[3])plt.title('@')plt.axis('off')# 绘制Xplt.sca(axs[4])ax = sns.heatmap(G_inv, cmap = 'RdYlBu_r',cbar_kws = {'orientation':'horizontal'},xticklabels = False,yticklabels=False,annot=False)plt.title('$G^{-1}$')fig.savefig('Figures/格拉姆矩阵的逆.svg', format='svg')
5.2 矩阵的逆的可视化:

---
笔记写于:2026年4月16日15:00:43
结束于:2026年4月16日16:01:43
用时:1小时
--