代码实战环节
import torch
import numpy as np
class LinearAlgebra():
@staticmethod
def svd(matrix):
if isinstance(matrix,torch.Tensor):
matrix = matrix.numpy()
# 求左右矩阵
left = matrix @ matrix.T
right = matrix.T @ matrix
# 求特征值和特征向量
left_val , left_ = np.linalg.eigh(left)
right_val , right_ = np.linalg.eigh(right)
# 降序操作
left_idx = left_val.argsort()[::-1]
right_idx = right_val.argsort()[::-1]
# 调整特征值和特征向量顺序
left_val = left_val[left_idx]
right_val = right_val[right_idx]
left_ = left_[:,left_idx]
right_ = right_[:,right_idx]
# 求奇异值,记住用右边的矩阵,别用左边的
mid_ = np.sqrt(np.abs(right_val))
# 返回 U * Σ * V^T
return left_ , mid_ , right_.T
@staticmethod
def tensor_einsum(op,*tensors):
# 张量的einsum操作
return torch.einsum(op,*tensors)
@staticmethod
def chain_rule(x):
# 创建需要计算梯度的张量
x = torch.tensor(x,requires_grad=True)
y = torch.sin(x)
z = y ** 2
# 自动计算x的梯度
z.backward()
# 返回x的梯度
return x.grad
@staticmethod
def svd_dimension_reduction(matrix,k):
# 进行svd分解,得到 U * Σ * V^T
u , s , vT = LinearAlgebra.svd(matrix)
# 保留前k个奇异值和对应的向量
u_k = u[:,:k] # 保留所有行,保留前k列
s_k = s[:k] # 保留前k个奇异值
vT_k = vT[:k,:] # 保留前k行,保留所有列
# 用保留的部分重构矩阵
# 理论上重构结果应该最大程度保留原矩阵的重要信息
last = u_k @ np.diag(s_k) @ vT_k
return last, u_k, s_k, vT_k
def test():
matrix = np.array([[1,2,3],
[4,5,6],
[7,8,9],
[10,11,12]])
# 测试svd分解
u, s, vT = LinearAlgebra.svd(matrix)
print("奇异值:",s)
# 测试svd降维 保留前2个维度
k = 2
last, u_k, s_k, vT_k = LinearAlgebra.svd_dimension_reduction(matrix,k)
print("降维重构后的矩阵:",last)
print("重构误差:") # 误差越小说明保留的信息越多
print(np.sum(np.abs(matrix - last)))
# 测试 einsum
# 创建随机张量(tensors)
a = torch.randn(2,3)
b = torch.randn(3,4)
c = LinearAlgebra.tensor_einsum("ij,jk->ik",a,b)
print("矩阵a:",a)
print("矩阵b:",b)
print("ab矩阵相乘结果:",c)
# 测试chain_rule
grad = LinearAlgebra.chain_rule(1.0)
print("在x=1处的梯度值:", grad)
if __name__ == '__main__':
test()
1.为什么要降序排序:
- 奇异值通常按从大到小排序
- 较大的奇异值代表更重要的特征或信息
- 在进行降维时,我们通常保留最大的几个奇异值
- 这是一个标准约定,使结果更容易解释和使用
2.left_[:,left_idx]
的含义:
:
表示选择所有行left_idx
选择指定的列- 整体表示"保留所有行,但重新排列列的顺序"
3.保留前k个奇异值和对应的向量
u_k = u[:,:k] # 保留所有行,保留前k列
s_k = s[:k] # 保留前k个奇异值
vT_k = vT[:k,:] # 保留前k行,保留所有列
u_k = u[:,:k]
:
- 保留U的所有行(m行)
- 只取前k列
- 结果是**
m×k
**矩阵
s_k = s[:k]
:
- 只保留前k个最大奇异值
- 用于构造**
k×k
**的对角矩阵
vT_k = vT[:k,:]
:
- 保留V^T的前k行
- 保留所有列(n列)
- 结果是**
k×n
**矩阵