深度学习 Pytorch 张量的线性代数运算

pytorch中并未设置单独的矩阵对象类型,因此pytorch中,二维张量就相当于矩阵对象,并且拥有一系列线性代数相关函数和方法。

在实际机器学习和深度学习建模过程中,矩阵或者高维张量都是基本对象类型,而矩阵所涉及到的线性代数理论也是深度学习用户必备的基本数学基础。

import torch
import numpy as np

16 矩阵的形变及特殊矩阵构造方法

矩阵的形变方法其实也就是二维张量的形变方法,再此基础上补充转置的基本方法。

另外,在实际线性代数运算过程中,经常涉及一些特殊矩阵,如单位矩阵、对角矩阵等。


Tensor矩阵运算

函数描述
torch.t(t)t转置
torch.eye(n)创建包含n个分量的单位矩阵
torch.diag(t)以t中各元素,创建对角矩阵
torch.triu(t)取矩阵t中的上三角矩阵
torch.tril(t)取矩阵t中的下三角矩阵
# 创建一个2*3的矩阵
t1 = torch.arange(1, 7).reshape(2, 3).float()
t1
# output :
tensor([[1., 2., 3.],[4., 5., 6.]])
# 转置
torch.t(t1)
# output :
tensor([[1., 4.],[2., 5.],[3., 6.]])
# 也可以调用方法
t1.t()
# output :
tensor([[1., 4.],[2., 5.],[3., 6.]])

矩阵的转置就是每个元素行列位置互换。


# 创建单位阵
torch.eye(3)
# output :
tensor([[1., 0., 0.],[0., 1., 0.],[0., 0., 1.]])

# 创建对角矩阵
t = torch.arange(1,6)
torch.diag(t)
# output :
tensor([[1, 0, 0, 0, 0],[0, 2, 0, 0, 0],[0, 0, 3, 0, 0],[0, 0, 0, 4, 0],[0, 0, 0, 0, 5]])
# 对角线向右上偏移一位
torch.diag(t, 1)
# output :
tensor([[0, 1, 0, 0, 0, 0],[0, 0, 2, 0, 0, 0],[0, 0, 0, 3, 0, 0],[0, 0, 0, 0, 4, 0],[0, 0, 0, 0, 0, 5],[0, 0, 0, 0, 0, 0]])
# 注意偏移后依然是一个方阵,从五行五列变成了六行六列
# 对角线向右上偏移一位
torch.diag(t, -1)
# output :
tensor([[0, 0, 0, 0, 0, 0],[1, 0, 0, 0, 0, 0],[0, 2, 0, 0, 0, 0],[0, 0, 3, 0, 0, 0],[0, 0, 0, 4, 0, 0],[0, 0, 0, 0, 5, 0]])

t1 = torch.arange(1, 9).reshape(3, 3)
t1
# output :
tensor([[1, 2, 3],[4, 5, 6],[7, 8, 9]])
# 取上三角矩阵
torch.triu(t1)
# output :
tensor([[1, 2, 3],[0, 5, 6],[0, 0, 9]])
# 上三角矩阵向左下偏移一位
torch.triu(t1, -1)
# output :
tensor([[1, 2, 3],[4, 5, 6],[0, 8, 9]])
# 上三角矩阵向右上偏移一位
torch.triu(t1, 1)
# output :
tensor([[0, 2, 3],[0, 0, 6],[0, 0, 0]])
# 下三角矩阵
torch.tril(t1)
# output :
tensor([[1, 0, 0],[4, 5, 0],[7, 8, 9]])

17 矩阵的基本运算

矩阵不同于普通的二维数组,其具备一定的线性代数含义。

而这些特殊的性质,其实就主要体现在矩阵的基本运算上。


矩阵的基本运算

函数描述
torch.dot(t1, t2)计算t1, t2张量内积
torch.mm(t1, t2)矩阵乘法
torch.mv(t1, t2)矩阵乘向量
torch.bmm(t1, t2)批量矩阵乘法
torch.addmm(t, t1, t2)矩阵相乘后相加
torch.addbmm(t, t1, t2)批量矩阵相乘后相加

dot/vdot:点积计算

注意: dotvdot 只能作用于一维张量 ,且对于数值型对象,二者计算结果并没有区别,两种函数只在进行复数计算时会有区别。

t = torch.arange(1, 4)
t
# output :
tensor([1, 2, 3])
torch.dot(t, t)
# output :
tensor(14)
torch.vdot(t, t)
# output :
tensor(14)
# 不能进行除了一维张量以外的计算
t1 = torch.arange(1, 9).reshape(3, 3)
torch.dot(t1, t1)
# output :
RuntimeError: shape '[3, 3]' is invalid for input of size 8

mm:矩阵乘法

t1 = torch.arange(1, 7).reshape(2, 3)
t2 = torch.arange(1, 10).reshape(3, 3)
# 对应元素相乘
t1*t1
# output :
tensor([[ 1,  4,  9],[16, 25, 36]])
# 矩阵乘法
torch.mm(t1, t2)
# output :
tensor([[30, 36, 42],[66, 81, 96]])

矩阵乘法执行过程如下所示:

在这里插入图片描述


mv:矩阵和向量相乘

矩阵和向量相乘可以看作是先将向量转化为列向量然后再相乘。

在实际执行向量和矩阵相乘的过程中,需要矩阵的列数和向量的元素个数相同。

met = torch.arange(1, 7).reshape(2, 3)
met
# output :
tensor([[1, 2, 3],[4, 5, 6]])
vec = torch.arange(1, 4)
vec
# output :
tensor([1, 2, 3])
torch.mv(met, vec)
# output :
tensor([14, 32])

bmm:批量矩阵相乘

批量矩阵相乘指的是三维张量的矩阵乘法。

例如,一个(3,2,2)的张量,本质上就是一个包含了3个2*2矩阵的张量。而三维张力的矩阵相乘,则是三维张量内部各对应位置的矩阵相乘。

注意:

  • 三维张量包含的矩阵个数需要相同
  • 每个内部矩阵,需要满足矩阵乘法的条件,也就是左乘矩阵的行数要等于右乘矩阵的列数
t3 = torch.arange(1, 13).reshape(3, 2, 2)
t3
# output :
tensor([[[ 1,  2],[ 3,  4]],[[ 5,  6],[ 7,  8]],[[ 9, 10],[11, 12]]])
t4 = torch.arange(1, 19).reshape(3, 2, 3)
t4
# output :
tensor([[[ 1,  2,  3],[ 4,  5,  6]],[[ 7,  8,  9],[10, 11, 12]],[[13, 14, 15],[16, 17, 18]]])
torch.bmm(t3, t4)
# output :
tensor([[[  9,  12,  15],[ 19,  26,  33]],[[ 95, 106, 117],[129, 144, 159]],[[277, 296, 315],[335, 358, 381]]])

addmm:矩阵相乘后相加

addmm函数结构:addmm(input, mat 1 , mat 2 , beta = 1, alpha = 1)

输出结果:beta * input + alpha * (mat 1 * mat 2)

t1
# output :
tensor([[1, 2, 3],[4, 5, 6]])t2
# output :
tensor([[1, 2, 3],[4, 5, 6],[7, 8, 9]])t = torch.arange(3)
t
# output :
tensor([0, 1, 2])
torch.mm(t1, t2)
# output :
tensor([[30, 36, 42],[66, 81, 96]])
torch.addmm(t, t1, t2)
# output :
tensor([[30, 37, 44],[66, 82, 98]])
torch.addmm(t, t1, t2, beta = 0, alpha = 10)
# output :
tensor([[300, 360, 420],[660, 810, 960]])

addbmm:批量矩阵相乘后相加

addmm类似,都是先乘后加,并且可以设置权重。不同的是addbmm是批量矩阵相乘,并且,再相乘的过程中而是矩阵相加,而非向量加矩阵。

t = torch.arange(6).reshape(2, 3)
t
# output :
tensor([[0, 1, 2],[3, 4, 5]])t3
# output :
tensor([[[ 1,  2],[ 3,  4]],[[ 5,  6],[ 7,  8]],[[ 9, 10],[11, 12]]])t4
# output :
tensor([[[ 1,  2,  3],[ 4,  5,  6]],[[ 7,  8,  9],[10, 11, 12]],[[13, 14, 15],[16, 17, 18]]])
torch.bmm(t3, t4)
# output :
tensor([[[  9,  12,  15],[ 19,  26,  33]],[[ 95, 106, 117],[129, 144, 159]],[[277, 296, 315],[335, 358, 381]]])
torch.addbmm(t, t3, t4)
# output :
tensor([[381, 415, 449],[486, 532, 578]])

注: addbmm会在原来三维张量基础之上,对其内部矩阵进行求和。


18 矩阵的线性代数运算

如果说矩阵的基本运算是矩阵基本性质,那么矩阵的线性代数运算,则是外面利用矩阵数据类型在求解实际问题过程中经常涉及到的线性代数方法。


矩阵的线性代数运算

函数描述
torch.trace(A)矩阵的迹
torch.linalg.matrix_rank(A)矩阵的秩
torch.det(A)计算矩阵A的行列式
torch.inverse(A)矩阵求逆
torch.lstsq(A, B)最小二乘法

矩阵的迹(trace)

就是求矩阵对角线之和

A = torch.tensor([[1, 2], [4, 5]]).float()
A
# output :
tensor([[1., 2.],[4., 5.]])
torch.trace(A)
# output :
tensor(6.)

当然,计算过程不需要是方阵

B = torch.arange(1, 7).reshape(2, 3)
B
# output :
tensor(6)

矩阵的秩(rank)

矩阵的秩是指矩阵中行或列的极大线性无关组,且矩阵中行、列极大无关数总是相同的,任何矩阵的秩都是唯一值。

满秩指的是方阵(行数和列数相同的矩阵)中行数、列数和秩相同,满秩矩阵有线性唯一解等重要特性,而其他矩阵也能通过求解秩来降维。

同时,秩也是奇异值分解等运算中涉及到的重要概念。

注: 秩的计算要求浮点型张量

A = torch.arange(1, 5).reshape(2, 2).float()
A
# output :
tensor([[1., 2.],[3., 4.]])
torch.torch.linalg.matrix_rank(A)
# output :
tensor(2)

对于矩阵B来说,第一列和第二列明显线性相关,最大线性无关组只有1组,因此矩阵的秩结果为1。

B = torch.tensor([[1, 2], [2, 4]]).float()
B
# output :
tensor([[1., 2.],[2., 4.]])
torch.torch.linalg.matrix_rank(B)
# output :
tensor(1)

矩阵的行列式(det)

我们可以将行列式简单理解为矩阵的一个基本性质或属性。通过行列式的计算,我们能够知道矩阵是否可逆,从而可以进一步求解矩阵所对应的线性方程。

更加专业的解释,行列式作为一个基本数学工具,实际上就是矩阵进行线性变换的伸缩因子。

对于任何一个n维方阵,行列式计算过程如下
D = ∣ a 11 a 12 … a 1 n a 21 a 22 … a 2 n ⋮ ⋮ ⋱ ⋮ a n 1 a n 2 … a n n ∣ D = \left| \begin{array}{cccc} a_{11} & a_{12} & \ldots & a_{1n} \\ a_{21} & a_{22} & \ldots & a_{2n} \\ \vdots & \vdots & \ddots & \vdots \\ a_{n1} & a_{n2} & \ldots & a_{nn} \\ \end{array} \right| D= a11a21an1a12a22an2a1na2nann

D = ∑ ( − 1 ) k a 1 k 1 a 2 k 2 … a n k n D = \sum (-1)^k a_{1k_1} a_{2k_2} \ldots a_{nk_n} D=(1)ka1k1a2k2ankn

更为简单的情况,如果对于一个2*2的矩阵,行列式的计算就是主对角线元素之积减去另外两个元素之积。

A = torch.tensor([[1, 2], [4, 5]]).float()
A
# output :
tensor([[1., 2.],[4., 5.]])
torch.det(A)
# output :
tensor(-3.)

A的行列式计算过程如下

在这里插入图片描述

对于行列式的计算,要求二维张量必须是方阵,也就是行列数必须一致。

B = torch.arange(1, 7).reshape(2, 3)
B
# output :
tensor([[1, 2, 3],[4, 5, 6]])
torch.det(B)
# output :
RuntimeError: linalg.det: A must be batches of square matrices, but they are 2 by 3 matrices

19 线性方程组的矩阵表达式

在正式进入到更进一步矩阵运算的讨论之前,我们需要对矩阵建立一个更加形象化的理解。

通常来说,我们会把高维空间中的一个个数看成是向量,而由这些向量组成的数组看成是一个矩阵。

例如:(1,2)(3,4)是二维空间中的两个点,矩阵A就代表这两个点所组成的矩阵。

A = torch.arange(1, 5).reshape(2, 2).float()
A
# output :
tensor([[1., 2.],[3., 4.]])
import matplotlib as mpl
import matplotlib.pyplot as plt
plt.plot(A[:, 0], A[:, 1], 'o')
# output :

在这里插入图片描述

如果更近一步,我们希望在二维空间中找到一条直线,来拟合这两个点,也就是所谓的构建线性回归模型,我们可以设置线性回归方程如下:
y = a x + b y = ax + b y=ax+b
带入(1,2)和(3,4)两个点后,我们还可以进一步将表达式改写成矩阵表示形式,改写过程如下:

在这里插入图片描述

而用矩阵表示线性方程组,则是矩阵的另一种常见用途。接下来,我们就可以通过上述矩阵方程组来求解系数向量x

首先一个基本思路是,如果有个和A矩阵相关的另一个矩阵,假设为A−1,可以使得二者相乘之后等于1,也就是A∗A−1=1,那么在方程组左右两边同时左乘该矩阵,等式右边的计算结果A−1∗B就将是x系数向量的取值。而此处的A−1就是所谓的A的逆矩阵。

逆矩阵定义:如果存在两个矩阵A、B,并在矩阵乘法运算下,A * B = E(单位矩阵),则我们称A、B互为逆矩阵。

在上述线性方程组求解场景中,我们已经初步看到了逆矩阵的用途,而一般来说,我们往往会通过伴随矩阵来进行逆矩阵的求解。由于伴随矩阵本身并无其他核心用途,且PyTorch中也未给出伴随矩阵的计算函数(目前),因此我们直接调用inverse函数来进行逆矩阵的计算。

当然,并非所有矩阵都有逆矩阵,对于一个矩阵来说,首先必须是方正,其次矩阵的秩不能为零,满足两个条件才能求解逆矩阵。


inverse函数:求解逆矩阵

首先根据上述矩阵表达式重新定义A和B

A = torch.tensor([[1.0, 1], [3, 1]])
A
# output :
tensor([[1., 1.],[3., 1.]])
B = torch.tensor([2.0, 4])
B
# output :
tensor([2., 4.])

然后使用inverse函数进行逆矩阵求解

torch.inverse(A)
# output :
tensor([[-0.5000,  0.5000],[ 1.5000, -0.5000]])

简单试探逆矩阵的基本特性

torch.mm(torch.inverse(A), A)
# output :
tensor([[ 1.0000e+00, -5.9605e-08],[-1.1921e-07,  1.0000e+00]])

乍一看不像是单位阵,实际上除了对角线元素是1外,其他元素取值都非常非常小,在实际运算过程中可以忽略不计。

在方程组左右两边同时左乘 A − 1 A^{-1} A1,求解x
A − 1 ⋅ A ⋅ x = A − 1 ⋅ B E ⋅ x = A − 1 ⋅ B x = A − 1 ⋅ B \begin{align*} A^{-1} \cdot A \cdot x &= A^{-1} \cdot B \\ E \cdot x &= A^{-1} \cdot B \\ x &= A^{-1} \cdot B \end{align*} A1AxExx=A1B=A1B=A1B

torch.mv(torch.inverse(A), B)
# output :
tensor([1.0000, 1.0000])

最终得到线性方程为:
y = x + 1 y = x+1 y=x+1
上述计算过程只是一个简化的线性方程组求解系数的过程,同时也是一个简单的一元线性方程拟合数据的过程。


20 矩阵的分解

矩阵的分解也是矩阵运算中的常规计算,矩阵分解也有很多种类,常见的例如QR分解、LU分解、特征分解、SVD分解等等等等,虽然大多数情况下,矩阵分解都是在形式上将矩阵拆分成几种特殊矩阵的乘积,但本质上,矩阵的分解是去探索矩阵更深层次的一些属性。

本节将主要围绕特征分解和SVD分解展开。

值得一提的是,此前的逆矩阵,其实也可以将其看成是一种矩阵分解的方式,分解之后的等式如下:
A = A ∗ A − 1 ∗ A A = A * A^{-1} * A A=AA1A
而大多数情况下,矩阵分解都是分解成形如下述形式
A = V U D A = VUD A=VUD

特征分解

特征分解中,矩阵分解形式为:
A = Q Λ Q − 1 A = Q\Lambda Q^{-1} A=QΛQ1
其中,Q和 Q − 1 Q^{-1} Q1互为逆矩阵,并且Q的列就是A的特征值所对应的特征向量,而 Λ \Lambda Λ为矩阵A的特征值按照降序排列组成的对角矩阵。

torch.eig函数:特征分解

A = torch.arange(1, 10).reshape(3, 3).float()
A
# output :
tensor([[1., 2., 3.],[4., 5., 6.],[7., 8., 9.]])
torch.eig(A, eigenvectors = True)
# output :
torch.return_types.eig(
eigenvalues=tensor([[ 1.6117e+01,  0.0000e+00],[-1.1168e+00,  0.0000e+00],[-1.2253e-07,  0.0000e+00]]),
eigenvectors=tensor([[-0.2320, -0.7858,  0.4082],[-0.5253, -0.0868, -0.8165],[-0.8187,  0.6123,  0.4082]]))

输出结果中,eigenvalues表示特征值向量,即A矩阵分解后的Λ矩阵的对角线元素值,并按照由大到小依次排列,eigenvectors表示A矩阵分解后的Q矩阵。

此处需要理解特征值,所谓特征值,可简单理解为对应列在矩阵中的信息权重,如果该列能够简单线性变换来表示其他列,则说明该列信息权重较大,反之则较小。

特征值概念和秩的概念有点类似,但不完全相同,矩阵的秩表示矩阵列向量的最大线性无关数,而特征值的大小则表示某列向量能多大程度解读矩阵列向量的变异度,即所包含信息量,秩和特征值关系可用下面这个例子来进行解读。

B = torch.tensor([1, 2, 2, 4]).reshape(2, 2).float()
B
# output :
tensor([[1., 2.],[2., 4.]])
torch.matrix_rank(B)
# output :
tensor(1)
torch.eig(B)          # 返回结果中只有一个特征
# output :
torch.return_types.eig(
eigenvalues=tensor([[0., 0.],[5., 0.]]),
eigenvectors=tensor([]))
C = torch.tensor([[1, 2, 3], [2, 4, 6], [3, 6, 9]]).float()
C
# output :
tensor([[1., 2., 3.],[2., 4., 6.],[3., 6., 9.]])
torch.eig(C)                # 只有一个特征的有效值
# output :
torch.return_types.eig(
eigenvalues=tensor([[ 1.4000e+01,  0.0000e+00],[-1.6447e-07,  0.0000e+00],[ 2.8710e-07,  0.0000e+00]]),
eigenvectors=tensor([]))

特征值一般用于表示矩阵对应线性方程组解空间以及数据降维,当然,由于特征分解只能作用于方阵,而大多数实际情况下矩阵行列数未必相等,此时要进行类似的操作就需要采用和特征值分解思想类似的奇异值分解(SVD)


奇异值分解(SVD)

奇异值分解(SVD)来源于代数学中的矩阵分解问题,对于一个方阵来说,我们可以利用矩阵特征值和特征向量的特殊性质(矩阵点乘特征向量等于特征值数乘特征向量),通过求特征值与特征向量来达到矩阵分解的效果
A = Q Λ Q − 1 A = Q\Lambda Q^{-1} A=QΛQ1
这里,Q是由特征向量组成的矩阵,而Λ是特征值降序排列构成的一个对角矩阵(对角线上每个值是一个特征值,按降序排列,其他值为0),特征值的数值表示对应的特征的重要性。 在很多情况下,最大的一小部分特征值的和即可以约等于所有特征值的和,而通过矩阵分解的降维就是通过在Q、Λ 中删去那些比较小的特征值及其对应的特征向量,使用一小部分的特征值和特征向量来描述整个矩阵,从而达到降维的效果。 但是,实际问题中大多数矩阵是以奇异矩阵形式,而不是方阵的形式出现的,奇异值分解是特征值分解在奇异矩阵上的推广形式,它将一个维度为m×n的奇异矩阵A分解成三个部分 :
A = U ∑ V T A = U\sum V^{T} A=UVT
其中U、V是两个正交矩阵,其中的每一行(每一列)分别被称为左奇异向量和右奇异向量,他们和∑中对角线上的奇异值相对应,通常情况下我们只需要保留前k个奇异向量和奇异值即可,其中U是m×k矩阵V是n×k矩阵∑是k×k的方阵,从而达到减少存储空间的效果,即
A m ∗ n = U m ∗ m ∑ m ∗ n V n ∗ n T ≈ U m ∗ k ∑ k ∗ k V k ∗ n T A_{m*n} = U_{m*m}\sum_{m*n}V^{T}_{n*n}\approx U_{m*k}\sum_{k*k}V^{T}_{k*n} Amn=UmmmnVnnTUmkkkVknT

奇异值分解函数

C
# output :
tensor([[1., 2., 3.],[2., 4., 6.],[3., 6., 9.]])
torch.svd(C)
# output :
torch.return_types.svd(
U=tensor([[-0.2673, -0.8018, -0.5345],[-0.5345, -0.3382,  0.7745],[-0.8018,  0.4927, -0.3382]]),
S=tensor([14.0000,  0.0000,  0.0000]),
V=tensor([[-0.2673,  0.0000,  0.9636],[-0.5345, -0.8321, -0.1482],[-0.8018,  0.5547, -0.2224]]))
CU, CS, CV = torch.svd(C)	# 验证SVD分解
torch.mm(torch.mm(CU, torch.diag(CS)), CV.t())
# output :
tensor([[1.0000, 2.0000, 3.0000],[2.0000, 4.0000, 6.0000],[3.0000, 6.0000, 9.0000]])

能够看出,上述输出完整还原了C矩阵,此时我们可根据svd输出结果对C进行降维,此时C可只保留第一列(后面的奇异值过小),即k = 1

U1 = CU[:, 0].reshape(3, 1)         # U的第一列
U1
# output :
tensor([[-0.2673],[-0.5345],[-0.8018]])
C1 = CS[0]                           # C的第一个值
C1
# output :
tensor(14.0000)
V1 = CV[:, 0].reshape(1, 3)           # V的第一行
V1
# output :
tensor([[-0.2673, -0.5345, -0.8018]])
torch.mm((U1 * C1), V1)
# output :
tensor([[1.0000, 2.0000, 3.0000],[2.0000, 4.0000, 6.0000],[3.0000, 6.0000, 9.0000]])

此时输出的Cd矩阵已经和原矩阵C高度相似了,损失信息在R的计算中基本可以忽略不计,经过SVD分解,矩阵的信息能够被压缩至更小的空间内进行存储,从而为PCA(主成分分析)、LSI(潜在语义索引)等算法做好了数学工具层面的铺垫。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/67492.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ESP8266-01S、手机、STM32连接

1、ESP8266-01S的工作原理 1.1、AP和STA ESP8266-01S为WIFI的透传模块,主要模式如下图: 上节说到,我们需要用到AT固件进行局域网应用(ESP8266连接的STM32和手机进行连接)。 ESP8266为一个WiFi透传模块,和…

简历_基于 Cache Aside 模式解决数据库与缓存一致性问题。

系列博客目录 文章目录 系列博客目录缓存更新策略总结案例:给查询商铺的缓存添加超时剔除和主动更新的策略 说到解决数据库与缓存一致性的问题,其实就是要解决缓存更新的问题。 缓存更新策略 业务场景: 低一致性需求:使用内存淘汰机制。例如店铺类型的…

XML在线格式化 - 加菲工具

XML在线格式化 打开网站 加菲工具 选择“XML 在线格式化” 输入XML,点击左上角的“格式化”按钮 得到格式化后的结果

python学opencv|读取图像(三十八 )阈值自适应处理

【1】引言 前序学习了5种阈值处理方法,包括(反)阈值处理、(反)零值处理和截断处理,相关文章链接为: python学opencv|读取图像(三十三)阈值处理-灰度图像-CSDN博客 python学opencv|读取图像(三十四&#…

数据可视化:让数据讲故事的艺术

目录 1 前言2 数据可视化的基本概念2.1 可视化的核心目标2.2 传统可视化手段 3 数据可视化在知识图谱中的应用3.1 知识图谱的可视化需求3.2 知识图谱的可视化方法 4 数据可视化叙事:让数据讲故事4.1 叙事可视化的关键要素4.2 数据可视化叙事的实现方法 5 数据可视化…

vue | 插值表达式

Vue 是一个用于 构建用户界面 的 渐进式 框架 1. 构建用户界面:基于 数据 动态 渲染 页面 2. 渐进式:循序渐进的学习 3. 框架:一套完整的项目解决方案,提升开发效率↑ (理解记忆规则) 插值表达式: 插值表达式是一种 Vu…

单片机存储器和C程序编译过程

1、 单片机存储器 只读存储器不是并列关系,是从ROM发展到FLASH的过程 RAM ROM 随机存储器 只读存储器 CPU直接存储和访问 只读可访问不可写 临时存数据,存的是CPU正在使用的数据 永久存数据,存的是操作系统启动程序或指令 断电易失 …

二、点灯基础实验

嵌入式基础实验第一个就是点灯,地位相当于编程界的hello world。 如下为LED原理图,要让相应LED发光,需要给I/O口设置输出引脚,低电平,二极管才会导通 2.1 打开初始工程,编写代码 以下会实现BLINKY常亮&…

豆包MarsCode:构造特定数组的逆序拼接

问题描述 思路分析 1. 数组的组成: 我们要根据 i 的不同值拼接出不同长度的子数组。对于每个 i 从 1 到 n,我们要把数字从 n 逆序到 i 拼接成一个子数组。 例如,当 i 1 时,拼接 [n, n-1, ..., 1]。当 i 2 时,拼接 …

RK3588平台开发系列讲解(NPU篇)NPU 驱动的组成

文章目录 一、NPU 驱动组成二、查询 NPU 驱动版本三、查询 rknn_server 版本四、查询 librknn_runtime 版本沉淀、分享、成长,让自己和他人都能有所收获!😄 一、NPU 驱动组成 NPU 驱动版本、rknn_server 版本、librknn_runtime 版本以及 RKNN Toolkit 版本的对应关系尤为重…

论文阅读:CosAE Learnable Fourier Series for Image Restoration

这是 2024 NeurIPS 上发表的一篇文章,介绍了一种新型的基于傅里叶级数的通用编码器。 Abstract 本文介绍了余弦自动编码器(Cosine Autoencoder, CosAE),这是一种新颖的通用自动编码器,它将经典傅里叶级数与前馈神经网…

YOLOv11改进,YOLOv11检测头融合RepConv卷积,并添加小目标检测层(四头检测),适合目标检测、分割等任务

摘要 作者提出了一种简单而强大的卷积神经网络架构,其推理阶段采用与 VGG 类似的网络体结构,仅由一堆 3x3 卷积和 ReLU 组成,而训练阶段的模型具有多分支拓扑。这种训练阶段和推理阶段架构的解耦通过结构重参数化技术实现,因此我们将该模型命名为 RepVGG。 # 理论介绍 Re…

深度学习笔记——循环神经网络RNN

大家好,这里是好评笔记,公主号:Goodnote,专栏文章私信限时Free。本文详细介绍面试过程中可能遇到的循环神经网络RNN知识点。 文章目录 文本特征提取的方法1. 基础方法1.1 词袋模型(Bag of Words, BOW)工作原…

Selenium工具使用Python 语言实现下拉框定位操作

🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 我们通常遇到的下拉框有显性的下拉框和隐性的下拉框;有的下拉框还可以进行单选或多选操作,在selenium中如何实现下拉框的定位通常使用selec…

使用 Continue 插件时,发现调用外部地址

https://us.i.posthog.com/e/?ip1&_1737025525924&ver1.163.0&compressiongzip-js 看是一个帮助改善产品的网址。估计类似某推广流量监控的插件工具吧。网上没用查到其他说明,可能国内使用不多的原因。 但是发送的数据看不出来是个什么内容。 我用来搜…

【PyQt】图像处理系统

[toc]pyqt实现图像处理系统 图像处理系统 1.创建阴影去除ui文件 2.阴影去除代码 1.创建阴影去除ui文件 UI文件效果图: 1.1QT Desiger设置组件 1.两个Pushbutton按钮 2.两个label来显示图像 3.Text Browser来显示输出信息 1.2布局的设置 1.先不使用任何La…

【Idea】编译Spring源码 read timeout 问题

Idea现在是大家工作中用的比较多的开发工具,尤其是做java开发的,那么做java开发,了解spring框架源码是提高自己技能水平的一个方式,所以会从spring 官网下载源码,导入到 Idea 工具并编译,但是发现build的时…

Linux 音视频入门到实战专栏(视频篇)视频编解码 MPP

文章目录 一、MPP 介绍二、获取和编译RKMPP库三、视频解码四、视频编码 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍如何调用alsa api来进行音频数据的播放和录制。 一、MPP 介绍 瑞芯微提供的媒体处理软件平台…

爬虫后的数据处理与使用(使用篇--实现分类预测)

()紧接上文,在完成基本的数据处理后,接下来就是正常的使用了。当然怎么用,确实需要好好思考一下~ 上文:爬虫后的数据处理与使用(处理篇) 前言: 一般来说,我…

RabbitMQ--延迟队列

(一)延迟队列 1.概念 延迟队列是一种特殊的队列,消息被发送后,消费者并不会立刻拿到消息,而是等待一段时间后,消费者才可以从这个队列中拿到消息进行消费 2.应用场景 延迟队列的应用场景很多,…