当前位置: 首页 > news >正文

4.OCR文本识别Connectionist Temporal Classification(CTC)算法

文章目录

    • 1.基础介绍
    • 2.Connectionist Temporal Classification(CTC)算法
      • 2.1 什么是Temporal Classification
      • 2.2 CTC问题描述
      • 2.2关于对齐
      • 2.3 前向后向算法
      • 2.4 推理时
    • 3.pytorch中的CTCLOSS
    • 参考资料


欢迎访问个人网络日志🌹🌹知行空间🌹🌹


1.基础介绍

论文:Connectionist Temporal Classification: Labelling Unsegmented Sequence Data with Recurrent Neural Networks

这是2006年第23次ICML会以上的一篇论文。

很多实际应用需要从未切分的数据中输出序列信息,如语音识别中的语音转文字,光学字符识别(Optical character recognition,OCR)中的字符图片转字符序列。循环神经网络(Recurrent neural networks,RNN)十分适合序列数据的学习,但其训练数据要求必须是切分后的序列,而实际应用中切分的训练序列数据标注比较困难,是很难获取的。

在这里插入图片描述

上图是OCR的两种模型,一种如图(a)可直接输入OCR检测得到的图片得到图片中的字符串can,另外一种需要先将图片按字符进行切割,这种方式比较数据处理比较复杂,而这种正是循环神经网络RNN要求的输入。

为了充分利用循环神经网络RNN处理序列数据的能力,同时避免对输入序列图像进行切分,本文作者提出了Connectionist Temporal Classificatio(CTC)算法

2.Connectionist Temporal Classification(CTC)算法

2.1 什么是Temporal Classification

SSS是从分布DX×Z\mathcal{D}_{\mathcal{X}\times\mathcal{Z}}DX×Z从获取的训练数据,
输入空间X=(Rm)∗\mathcal{X}=(\mathbb{R}^m)^*X=(Rm)mmm维的实值向量序列,目标空间Z=L∗\mathcal{Z}=L^*Z=L由字母集LLL组成的标签序列,训练数据集SSS中的每个样本由序列对(x,z)(\mathbf{x},\mathbf{z})(x,z)组成。目标序列z=(z1,z2,...,zU)\mathbf{z}=(z_1,z_2,...,z_U)z=(z1,z2,...,zU)长度小于等于输入序列x=(x1,x2,...,xT),i.e.U≤T\mathbf{x}=(x_1,x_2,...,x_T),i.e.U\le Tx=(x1,x2,...,xT),i.e.UT。输入序列和输出序列长度一般不同,因此没有先验知识可以对齐他们。

Temporal Classification的任务是使用训练数据SSS,学习一个分类器,能够将输入序列分成对应的目标序列h:X→Zh:\mathcal{X}\rightarrow\mathcal{Z}h:XZ

从第一部分介绍,可以知道OCR任务本身就是一个Temporal Classification,翻译成了时间序列分类问题。其输入是卷积后得到特征图序列,输出的是字符序列。

之所以被称为Connectionist Temporal Classification,是这样理解的,原始输入的是一整张联结在一起未切分的字符图像,输出的是字符序列,因为没有对原始图像上的字符进行切分预处理,因此被称之为连接序列分类。

2.2 CTC问题描述

在这里插入图片描述

从网络输入到获取标签序列要分成两步:

第一步,可以将输入为长度为TTT的序列x=[x1,x2,...,xT]\mathbf{x}=[x_1,x_2,...,x_T]x=[x1,x2,...,xT](序列中每个xxx都是m维),输出为长度TTT的序列y=[y1,y2,...,yT]\mathbf{y}=[y_1,y_2,...,y_T]y=[y1,y2,...,yT](序列中每个yyy都是n维),参数为www的映射(即循环神经网络)定以为Nw:(Rm)T→(Rn)T\mathcal{N}_w:(\mathbb{R}^m)^T\rightarrow(\mathbb{R}^n)^TNw:(Rm)T(Rn)Ty=Nw(x)\mathbf{y}=\mathcal{N}_w(\mathbf{x})y=Nw(x)。将ykty_k^tykt表示成第ttt个序列值为kkk的概率,L′TL'^TLT表示长度为TTT的序列,其中每个元素取自字母集L′=L∪{blank}L'=L\cup\{blank\}L=L{blank},序列L′TL'^TLT也被称之为路径,表示成π\piπ

根据以上定义给定输入x\mathbf{x}x,输出为路径π\piπ的概率可表示成:

p(π∣x)=∏t=1Tyπtt,∀π∈L′Tp(\pi|\mathbf{x})=\prod_{t=1}^{T}y_{\pi_t}^t,\forall\pi\in L'^T p(πx)=t=1Tyπtt,πLT

其实,这里还有个条件,就是每一步输出之间是相互独立,上面的公式才能成立。

第二步,我们知道输入x\mathbf{x}x对应的标签序列为长度等于UUU的序列z=[z1,z2,...,zU],U≤T\mathbf{z}=[z_1,z_2,...,z_U],U\le Tz=[z1,z2,...,zU]UT,在第一步中循环神经网络给出的只是长度为TTT的中间序列y\mathbf{y}y,要和长度为UUU的标签序列z\mathbf{z}z对应,还需要定义个从中间序列到标签序列的映射B:L′T↦L<T\mathcal{B}:L'^T\mapsto L^{\lt T}B:LTL<T,很明显,B\mathcal{B}B是一个多对一的映射。这个映射可以定义为移除中间序列中的重复相邻字符和空格占位符,如B(s−ta−tt−e)=B(s−t−aa−tt−e)=state\mathcal{B}(s-ta-tt-e)=\mathcal{B}(s-t-aa-tt-e)=stateB(statte)=B(staatte)=state,定义了映射B\mathcal{B}B后,可以将输出标签序列z\mathbf{z}z的后验概率表示成:

p(z∣x)=∑π∈B−1(z)p(π∣x)p(\mathbf{z}|\mathbf{x})=\sum_{\pi\in\mathcal{B}^{-1}(\mathbf{z})}p(\pi|\mathbf{x}) p(zx)=πB1(z)p(πx)

2.2关于对齐

为什么要使用上述的方法来进行网络的训练呢?那是因为输入x=[x1,x2,...,xm]\mathbf{x}=[x_1,x_2,...,x_m]x=[x1,x2,...,xm]和标签序列z=[z1,z2,...,zU]\mathbf{z}=[z_1,z_2,...,z_U]z=[z1,z2,...,zU]之间在序列长度,序列长度比例,对应元素之间找不到什么对应关系。

在这里插入图片描述

如上图是对齐后的数据,但在实际中是很难知道(x1,x2)↦c,(x3,x4,x5)↦a,(x6)↦t(x_1,x_2)\mapsto c,(x_3,x_4,x_5)\mapsto a,(x_6)\mapsto t(x1,x2)c,(x3,x4,x5)a,(x6)t,标注这样的数据也需要花费大量的时间,因此更希望模型能够拥有从未对齐数据中学习的能力,通过前面的介绍,使用CTC算法可以从未对齐的输入中求得标签序列。

2.3 前向后向算法

在这里插入图片描述

使用暴力方法计算

p(z∣x)=∑π∈B−1(z)p(π∣x)p(\mathbf{z}|\mathbf{x})=\sum_{\pi\in\mathcal{B}^{-1}(\mathbf{z})}p(\pi|\mathbf{x}) p(zx)=πB1(z)p(πx)

因为要计算每一条路径,因此对于序列字典中有nnn个元素,长度为TTT的序列,要计算所有路径的概率,时间复杂度为O(nT)O(n^T)O(nT),这是指数级的时间复杂度,对于大部分长度的序列这个运算都过于耗时。论文作者为了解决这个问题,提出了前向后向递推算法,采用动态规划的方法将时间复杂度降到了O(nT)O(nT)O(nT),使算法更可行。

先借个例子来看一下。

假设标签序列为
z=state\mathbf{z} = state z=state

在序列前后和每个字符中间添加空格占位符−-

z′=−s−t−a−t−e−\mathbf{z}'=-s-t-a-t-e- z=state

z′\mathbf{z}'z中任意的字符重复任意次,经过B\mathcal{B}B映射都能得到标签序列statestatestate,因此可以将z′\mathbf{z}'z当成满足变换条件的基础序列。B\mathcal{B}B是多对一的映射,如下4个路径都能得到statestatestate

B(−−sttaa−tee−)=stateB(−−stta−t−−−e)=stateB(sst−aaa−tee−)=stateB(sst−aa−t−−−e)=state\mathcal{B}(--sttaa-tee-)=state\\ \mathcal{B}(--stta-t---e)=state\\ \mathcal{B}(sst-aaa-tee-)=state\\ \mathcal{B}(sst-aa-t---e)=state B(sttaatee)=stateB(sttate)=stateB(sstaaatee)=stateB(sstaate)=state

z′\mathbf{z}'z写成列的形式,则上述四条路径可以写成如下图的形式:

在这里插入图片描述

从上图可以看到,四条路径在序列t=6t=6t=6时都经过字符aaa,记上面的四条路径为π1,π2,π3,π4\pi^1,\pi^2,\pi^3,\pi^4π1,π2,π3,π4

π1=b=b1:5+{a}6+b7:12π2=r=r1:5+{a}6+r7:12π3=b1:5+{a}6+r7:12π4=r1:5+{a}6+b7:12\pi^1=b=b_{1:5}+\{a\}_6+b_{7:12}\\ \pi^2=r=r_{1:5}+\{a\}_6+r_{7:12}\\ \pi^3=b_{1:5}+\{a\}_6+r_{7:12}\\ \pi^4=r_{1:5}+\{a\}_6+b_{7:12} π1=b=b1:5+{a}6+b7:12π2=r=r1:5+{a}6+r7:12π3=b1:5+{a}6+r7:12π4=r1:5+{a}6+b7:12

ykty_k^tykt表示序列第ttt步元素为kkk的概率,则上面四条路径都包含ya6y_a^6ya6这一项,将计算上面四条路径的概率表示可以提取公因式写成:

foward=p(b1:5+r1:5∣x)=y−1∗y−2∗ys3∗yt4∗yt5+ys1∗ys2∗yt3∗y−4∗ya5backward=p(b7:12+r7:12∣x)=y−7∗yt8∗y−9∗y−10∗y−11∗ye12+ya7∗y−8∗yt9∗ye10∗ye11∗y−12foward = p(b_{1:5}+r_{1:5}|\mathbf{x}) = y_-^1*y_-^2*y_s^3*y_t^4*y_t^5 + y_s^1*y_s^2*y_t^3*y_-^4*y_a^5\\ backward = p(b_{7:12}+r_{7:12}|\mathbf{x}) = y_-^7*y_t^8*y_-^9*y_-^{10}*y_-^{11}*y_e^{12} + y_a^7*y_-^8*y_t^9*y_e^{10}*y_e^{11}*y_-^{12} foward=p(b1:5+r1:5x)=y1y2ys3yt4yt5+ys1ys2yt3y4ya5backward=p(b7:12+r7:12x)=y7yt8y9y10y11ye12+ya7y8yt9ye10ye11y12

然后上面四条路径的概率和可以写成:

p(π1,π2,π3,π4∣x)=forward∗ya6∗backwardp(\pi^1,\pi^2,\pi^3,\pi^4|\mathbf{x}) = forward*y_a^6*backward p(π1,π2,π3,π4x)=forwardya6backward

上面的介绍中只取了四条经过变换B\mathcal{B}B后能得到statestatestate的路径,实际上的路径要远远多于此:

在这里插入图片描述

从上图中选出经过{a}6\{a\}_6{a}6的所有路径,概率∑B(π)=z,π6=ap(π∣x)\sum\limits_{\mathcal{B}(\pi)=\mathbf{z},\pi_6=a}p(\pi|x)B(π)=z,π6=ap(πx)(π6=a\pi_6=aπ6=a表示路径π\piπ的第6个字符为a),同样还是可以表示成如下形式:

∑B(π)=z,π6=ap(π∣x)=forward∗ya6∗backward\sum\limits_{\mathcal{B}(\pi)=\mathbf{z},\pi_6=a}p(\pi|x)=forward*y_a^6*backwardB(π)=z,π6=ap(πx)=forwardya6backward

进一步推广,定义αt(s)\alpha_t(s)αt(s)表示路径π\piπ中的第t个字符与加了占位符后标签序列z′\mathcal{z}'z的第s个字相对应且路径π\piπ满足B(π1:t)=z1:s\mathcal{B}(\pi_{1:t})=\mathbf{z}_{1:s}B(π1:t)=z1:s时所有路径π1:t\pi_{1:t}π1:t的概率和,表示成:

αt(s)=∑B(π1:t)=留−z1:s′∏t′=1tyπt′t′\alpha_t(s)=\sum\limits_{\mathcal{B}(\pi_{1:t})\overset{留-}{=}\mathbf{z}'_{1:s}}\prod_{t'=1}^{t}y^{t'}_{\pi_{t'}} αt(s)=B(π1:t)=z1:st=1tyπtt

可以看到这等同于前向变量forwardforwardforward,现在来看t=1t=1t=1时的α1(s)\alpha_1(s)α1(s),要经过B\mathcal{B}B映射后能得到保留占位符的标签序列,sss就只能等于1或者2,看上图中−s−t−a−t−e−-s-t-a-t-e-state的例子,t=1时刻只能取z′\mathcal{z}'z−-或者sss,否则无法经过B\mathcal{B}B映射得到标签序列,因此

α1(1)=y−1α1(2)=yz2′1α1(s)=0,∀s>2\alpha_1(1)=y^1_{-}\\ \alpha_1(2)=y^1_{\mathbf{z}'_2}\\ \alpha_1(s)=0,\forall s\gt2 α1(1)=y1α1(2)=yz21α1(s)=0,s>2

还看statestatestate的例子,当过z′6{\mathbf{z}'}_6z6时,t=5t=5t=5对应的字符只能是t/−/at/-/at//a,可以推出来上面例子中

α6(6)=(α5(4)+α5(5)+α5(6))∗ya6\alpha_6(6)=(\alpha_5(4)+\alpha_5(5)+\alpha_5(6))*y_a^6 α6(6)=(α5(4)+α5(5)+α5(6))ya6
一般化推广可得:

αt(s)=(αt−1(s−2)+αt−1(s−1)+αt−1(s))∗yzs′t\alpha_t(s)=(\alpha_{t-1}(s-2)+\alpha_{t-1}(s-1)+\alpha_{t-1}(s))*y_{\mathbf{z}'_s}^{t} αt(s)=(αt1(s2)+αt1(s1)+αt1(s))yzst

还需考虑一个特殊情况,看下面例子z=zoo,z′=−z−o−o−\mathbf{z}=zoo,\mathbf{z}'=-z-o-o-z=zoo,z=zoo,t=2,s=6或3:

在这里插入图片描述

很明显因为B\mathcal{B}B映射会去除重复的字母,因此上面两种情况在t−1t-1t1时刻不能取s−2s-2s2

综上,可得最终t≥2t\ge2t2时前向递推公式为(也就是原论文上的递推公式):

αt(s)={(αt−1(s−1)+αt−1(s))∗yzs′tifzs′=−orzs′=zs−2′(αt−1(s−2)+αt−1(s−1)+αt−1(s))∗yzs′totherwise\alpha_t(s)=\left\{\begin{matrix} (\alpha_{t-1}(s-1)+\alpha_{t-1}(s))*y_{\mathbf{z}'_s}^{t}\,if\,z'_s=-\,or\,z'_s=z'_{s-2} \\ (\alpha_{t-1}(s-2)+\alpha_{t-1}(s-1)+\alpha_{t-1}(s))*y_{\mathbf{z}'_s}^{t}\,otherwise \end{matrix}\right. αt(s)={(αt1(s1)+αt1(s))yzstifzs=orzs=zs2(αt1(s2)+αt1(s1)+αt1(s))yzstotherwise

将公式中相同的项合并一下就可以得到论文上的公式了。

同样的方法可以定义backwardbackwardbackward:

βt(s)=∑B(πt:T)=留−zs:∣z′∣′∏t′=tTyπt′t′\beta_t(s)=\sum\limits_{\mathcal{B}(\pi_{t:T})\overset{留-}{=}\mathbf{z}'_{s:|z'|}}\prod_{t'=t}^{T}y^{t'}_{\pi_{t'}} βt(s)=B(πt:T)=zs:zt=tTyπtt

t≥2t\ge2t2βt(s)\beta_t(s)βt(s)的递推公式:

βt(s)={(βt+1(s)+βt+1(s+1))∗yzs′tifzs′=−orzs′=zs+2′(βt+1(s)+βt+1(s+1)+βt+1(s+2))∗yzs′totherwise\beta_t(s)=\left\{\begin{matrix} (\beta_{t+1}(s)+\beta_{t+1}(s+1))*y_{\mathbf{z}'_s}^{t}\,if\,z'_s=-\,or\,z'_s=z'_{s+2} \\ (\beta_{t+1}(s)+\beta_{t+1}(s+1)+\beta_{t+1}(s+2))*y_{\mathbf{z}'_s}^{t}\,otherwise \end{matrix}\right. βt(s)={(βt+1(s)+βt+1(s+1))yzstifzs=orzs=zs+2(βt+1(s)+βt+1(s+1)+βt+1(s+2))yzstotherwise

求得αt(s)\alpha_t(s)αt(s)βt(s)\beta_t(s)βt(s)后,标签序列z\mathbf{z}z的后验概率可以写成,

p(z∣x)=∑zs′∈πtαt(s)βt(s)yzs′tp(\mathbf{z}|\mathbf{x})=\sum_{z'_s\in\pi_t}\frac{\alpha_t(s)\beta_t(s)}{y_{z'_s}^t} p(zx)=zsπtyzstαt(s)βt(s)

求得p(z∣x)p(\mathbf{z}|\mathbf{x})p(zx)后,可以知道使用CTCCTCCTC时的目标就是最大化p(z∣x)p(\mathbf{z}|\mathbf{x})p(zx),可以定义损失函数为−log(p(z∣x))-log(p(\mathbf{z}|\mathbf{x}))log(p(zx)),可以推导损失的计算和损失函数梯度都能使用递推的方式来计算,减少运算量,加快运算速度。

2.4 推理时

训练完成后,在网络推理时希望取概率最大的输出序列:

z∗=argmaxzp(z∣x)\mathbf{z}^* = \underset{\mathbf{z}}{argmax} \,p(\mathbf{z}|\mathbf{x}) z=zargmaxp(zx)

对所有路径的概率求和,然后取概率最大的路径作为预测的结果,应该是最合理的方式,但当序列比较长时面临计算量过大,影响推理速度的情况。

一种做法是对于第ttt步,取概率最大的字符,然后将所有的字符组合起来经过去重当作最终的输出,但这种做法只考虑了一条路径,有可能有多条路径对应标签,各条路径的概率加和后有可能更大。

一种替代的折衷方法是改进版的Beam Search

常规的Beam Search算法,对于每个时间步取概率最大的几个(Beam Size)可能结果,如下为字母集为−,a,b-,a,b,a,bBeam Size=3Beam Search的过程:

在这里插入图片描述

上图中Beam Search到当前步最大的几个(Beam Size)可能字符都只有一条前缀序列,实际上可以有多条前缀序列和当前的字符组合后都得到相同的输出,如下图对于路径长度T=2T=2T=2λa\lambda aλa,a−a-a,aaaaaa最后都能对应的aaa

a a a b a a a b b a ϵ a b ϵ a b ϵ a b λ a b ϵ a b ϵ a b ϵ a b λ ϵ a b T = 4 T = 3 T = 2 T = 1 current hypotheses proposed extensions current hypotheses proposed extensions current hypotheses proposed extensions current hypotheses Multiple extensions merge to the same prefix empty string

且观察T=3T=3T=3时,前缀序列aaaaaa对应的输出有可能是aaa或者aaaaaa,因此对应的概率应该分别进行计算。

3.pytorch中的CTCLOSS

计算未切分的连续时间序列和目标序列之间的损失。

torch.nn.CTCLoss(blank=0, reduction='mean', zero_infinity=False)class CTCLoss:...def forward(self, log_probs: Tensor, targets: Tensor, input_lengths: Tensor, target_lengths: Tensor) -> Tensor:...
  • log_probs:Tensor of size (T,N,C)/(T,C),T是输入长度,N是Batch Size,C是序列字典的大小(包括空格)

  • targets:Tensor of size(N,S)Nbatch sizeS是最大目标序列长度,目标序列中的每个元素是类别的序号。

  • input_lengths,每个输入序列的长度,为元组tupleshape(N,)的张量,Nbatch sizeinput_lengths的值≤T\le TT

  • target_lengths,每个目标序列的长度,为元组tupleshape(N,)的张量,Nbatch size,如果targetsshape(N,S),这里其实是把每个targettargettarget添加padding后变成了S,假设第n个序列目标长度为sns_nsn,target_lengths中第n个元素值就为sns_nsn

import torchT = 2
C = 3
N = 1
S = 2
S_min = 1input = torch.randn(T,N,C).log_softmax(2).detach().requires_grad_()
print(input)
target = torch.tensor([0,1], dtype=torch.long).reshape(shape=(N, S))
print(target)
input_lengths = torch.full(size=(N,), fill_value=T, dtype=torch.long)
target_lengths = torch.tensor([2], dtype=torch.long).reshape(shape=(N,)) 
ctc_loss = torch.nn.CTCLoss()
loss = ctc_loss(input, target, input_lengths, target_lengths)
print(loss)# tensor([[[-0.4002, -1.5314, -2.1752]],         [[-0.8444, -2.2039, -0.7770]]], requires_grad=True)
# tensor([[0, 1]])
# tensor(1.3021, grad_fn=<MeanBackward0>)

上面示例的计算过程:

在这里插入图片描述

从上图可以看到目标是010101at路径有且仅有此一条,损失值计算为:

loss=−12[−0.4002+(−2.2039)]=1.3021loss = -\frac{1}{2}[-0.4002+(-2.2039)]=1.3021 loss=21[0.4002+(2.2039)]=1.3021


欢迎访问个人网络日志🌹🌹知行空间🌹🌹


参考资料

  • 1.https://distill.pub/2017/ctc/
  • 2.https://zhuanlan.zhihu.com/p/161186907
  • 3.https://zhuanlan.zhihu.com/p/519960905
  • 4.https://zhuanlan.zhihu.com/p/58526617

相关文章:

4.OCR文本识别Connectionist Temporal Classification(CTC)算法

文章目录1.基础介绍2.Connectionist Temporal Classification(CTC)算法2.1 什么是Temporal Classification2.2 CTC问题描述2.2关于对齐2.3 前向后向算法2.4 推理时3.pytorch中的CTCLOSS参考资料欢迎访问个人网络日志&#x1f339;&#x1f339;知行空间&#x1f339;&#x1f3…...

误删了Ubuntu/Linux的一些默认用户目录怎么办?

用户目录&#xff1a;指位于 $HOME 下的一系列常用目录&#xff0c;例如 Documents&#xff0c;Downloads&#xff0c;Music&#xff0c;还有 Desktop等。本文不是讲如何恢复原有目录及其重要文件&#xff0c;适用于仅恢复目录功能一&#xff1a;仅恢复个别目录如误删了Desktop…...

ArXiv简介以及论文提交

arXiv网站简介 arXiv是一个收集物理学、数学、计算机科学、生物学与数理经济学的论文预印本的网站。其中arXiv发音同“archive”&#xff0c;因为“X”代表希腊字母 &#xff0c;国际音标为[kai]。它于1991年8月14日成立&#xff0c;现由美国康奈尔大学维护。 ——维基百科 对…...

pytorch学习

目录如下&#xff1a; pytorch常用操作 pytorch 常用操作 pytorch 的 detach()函数 1. 什么是detach()函数 我们在将输出特征矩阵进行存储的时候&#xff0c;经常需要将torch.Tensor类型的数据转换成别的如numpy类型的数据&#xff0c;但是Tensor类型的数据是会自动计算梯度…...

【OC】块初识

Block简介 Blocks是C语言的扩充功能。可以用一句话来表示Blocks的扩充功能&#xff1a;带有自动变量的匿名函数。 匿名函数 所谓匿名函数就是不带有名称的函数。C语言的标准不允许存在这样的函数。例&#xff1a; int func(int count);它声明了名称为func的函数。下面的源代…...

3-2 创建一个至少有两个PV组成的大小为20G的名为testvg的VG

文章目录1. 在vmware添加多块20G的硬盘&#xff0c;并创建分区2. 创建一个至少有两个PV组成的大小为20G的名为testvg的VG&#xff0c;要求PE大小为16M&#xff0c;而后在卷组中创建大小为5G的逻辑卷testlv;挂载至/users目录3. 新建用户archlinux,要求其家目录为/users/archlinu…...

【密码学】 一篇文章讲透数字证书

【密码学】 一篇文章讲透数字证书 数字证书介绍 数字证书是一种用于认证网络通信中参与者身份和加密通信的证书&#xff0c;人们可以在网上用它来识别对方的身份。 我们在上一篇博客中介绍了数字签名的作用和原理&#xff0c;数字签名可以防止消息被否认。有了公钥算法和数字签…...

Linux 操作系统原理 — 内存管理 — 虚拟地址空间(x86 64bit 系统)

目录 文章目录目录虚拟地址格式与内核页表&#xff08;四级页表&#xff09;虚拟地址格式与内核页表&#xff08;四级页表&#xff09; 在 x86 64bit 系统中&#xff0c;可以描述的最长地址空间为 2^64&#xff08;16EB&#xff09;&#xff0c;远远超过了目前主流内存卡的规格…...

C语言深入知识——(2)指针的深入理解

1、字符指针 &#xff08;1&#xff09;字符指针的普通用法 char a A; char* pa &a;但是一般来说字符指针很少这么用……更多是拿来存储一个字符串 &#xff08;2&#xff09;字符串的两种存储以及区别 现在有了两种存储数组的方法 ①一个是使用char类型数组存储②另外…...

Git使用笔记

分支branch切换到另一个分支git checkout 你要切换到的分支的名字git checkout master将本地的这个分支branch1和gitee上的branch1进行合并&#xff08;本地的branch1有的&#xff0c;gitee上branch1没有的增加上去&#xff09;git merge branch1git merge 分支的名字查看本地是…...

数据库管理-第五十八期 倒腾PDB(20230226)

数据库管理 2023-02-26第五十八期 倒腾PDB1 克隆本地PDB2 没开归档总结第五十八期 倒腾PDB 其实本周过的不大好&#xff0c;连着两天熬夜&#xff0c;一次是割接一次是处理ADG备库的异常&#xff0c;其实本周有些内容是以前处理过的问题&#xff0c;到了周末还肚子痛。哎… 1…...

我看谁还敢说不懂git

文章目录一、Git介绍1.1、Git的作用1.2、Git的理念1.3、Git的特点1.4、Git对比SVN二、Git的概念2.1、Git基础概念三、Git的基本操作3.1、使用Git管理一个代码仓库的流程3.2、Git常用命令介绍四、Git状态的变化五、Git安装和配置5.1、Git的安装5.2、Git的配置六、Git的高级操作6…...

Scratch少儿编程案例-算法练习-实现加减乘除练习题

专栏分享 点击跳转=>Unity3D特效百例点击跳转=>案例项目实战源码点击跳转=>游戏脚本-辅助自动化点击跳转=>Android控件全解手册点击跳转=>Scratch编程案例👉关于作者...

【离线数仓-9-数据仓库开发DWS层设计要点-1d/nd/td表设计】

离线数仓-9-数据仓库开发DWS层设计要点-1d/nd/td表设计离线数仓-9-数据仓库开发DWS层设计要点-1d/nd/td表设计一、DWS层设计要点二、DWS层设计分析 - 1d/nd1.DWS层设计一&#xff1a;不考虑用户维度2.DWS层设计二&#xff1a;考虑用户维度2.DWS层设计三 &#xff1a;考虑用户商…...

python网络数据获取

文章目录1网络爬虫2网络爬虫的类型2.1通用网络爬虫2.1.12.1.22.2聚焦网络爬虫2.2.1 基于内容评价的爬行策略2.2.2 基于链接结构的爬行策略2.2.3基于增强学习的爬行策略2.2.4基于语境图的爬行策略2.3增量式网络爬虫深层网页爬虫3网络爬虫基本架构3.1URL管理模块3.2网页下载模块3…...

[Datawhale][CS224W]图机器学习(六)

目录一、简介二、概述三、算法四、PageRank的缺点五、Python实现迭代法参考文献一、简介 PageRank&#xff0c;又称网页排名、谷歌左侧排名、PR&#xff0c;是Google公司所使用的对其搜索引擎搜索结果中的网页进行排名的一种算法。 佩奇排名本质上是一种以网页之间的超链接个…...

aws ecr 使用golang实现的简单镜像转换工具

https://pkg.go.dev/github.com/docker/docker/client#section-readme 通过golang实现一个简单的镜像下载工具 总体步骤 启动一台海外区域的ec2实例安装docker和awscli配置凭证访问国内ecr仓库编写web服务实现镜像转换和自动推送 安装docker和awscli sudo yum remove awsc…...

【20230225】【剑指1】分治算法(中等)

1.重建二叉树class Solution { public:TreeNode* traversal(vector<int>& preorder,vector<int>& inorder){if(preorder.size()0) return NULL;int rootValuepreorder.front();TreeNode* rootnew TreeNode(rootValue);//int rootValuepreorder[0];if(preo…...

「JVM 高效并发」Java 线程

进程是资源分配&#xff08;内存地址、文件 I/O 等&#xff09;的基本单位&#xff0c;线程是执行调度&#xff08;处理器资源调度&#xff09;的基本单位&#xff1b; Loom 项目若成功为 Java 引入纤程&#xff08;Fiber&#xff09;&#xff0c;则线程的执行调度单位可能变为…...

ADAS-可见光相机之Cmos Image Sensor

引言 “ 可见光相机在日常生活、工业生产、智能制造等应用有着重要的作用。在ADAS中更是扮演着重要的角色&#xff0c;如tesla model系列全车身10多个相机&#xff0c;不断感知周围世界。本文着重讲解下可见光相机中的CIS(CMOS Image Sensor)。” 定义 光是一种电磁波&…...

【ESP 保姆级教程】玩转emqx MQTT篇③ ——封装 EmqxIoTSDK,快速在项目集成

忘记过去,超越自己 ❤️ 博客主页 单片机菜鸟哥,一个野生非专业硬件IOT爱好者 ❤️❤️ 本篇创建记录 2023-02-26 ❤️❤️ 本篇更新记录 2023-02-26 ❤️🎉 欢迎关注 🔎点赞 👍收藏 ⭐️留言📝🙏 此博客均由博主单独编写,不存在任何商业团队运营,如发现错误,请…...

Python自动化测试面试题-编程篇

前言 随着行业的发展&#xff0c;编程能力逐渐成为软件测试从业人员的一项基本能力。因此在笔试和面试中常常会有一定量的编码题&#xff0c;主要考察以下几点。 基本编码能力及思维逻辑基本数据结构&#xff08;顺序表、链表、队列、栈、二叉树&#xff09;基本算法&#xf…...

CIT 594 Module 7 Programming AssignmentCSV Slicer

CIT 594 Module 7 Programming Assignment CSV Slicer In this assignment you will read files in a format known as “comma separated values” (CSV), interpret the formatting and output the content in the structure represented by the file. Q1703105484 Learning …...

链路追踪——【Brave】第一遍小结

前言 微服务链路追踪系列博客&#xff0c;后续可能会涉及到Brave、Zipkin、Sleuth内容的梳理。 Brave 何为Brave&#xff1f; github地址&#xff1a;https://github.com/openzipkin/brave Brave是一个分布式追踪埋点库。 #mermaid-svg-riwF9nbu1AldDJ7P {font-family:"…...

Vision Transformer(ViT)

1. 概述 Transformer[1]是Google在2017年提出的一种Seq2Seq结构的语言模型&#xff0c;在Transformer中首次使用Self-Atttention机制完全代替了基于RNN的模型结构&#xff0c;使得模型可以并行化训练&#xff0c;同时解决了在基于RNN模型中出现了长距离依赖问题&#xff0c;因…...

104-JVM优化

JVM优化为什么要学习JVM优化&#xff1a; 1&#xff1a;深入地理解 Java 这门语言 我们常用的布尔型 Boolean&#xff0c;我们都知道它有两个值&#xff0c;true 和 false&#xff0c;但你们知道其实在运行时&#xff0c;Java 虚拟机是 没有布尔型 Boolean 这种类型的&#x…...

QML 颜色表示法

作者: 一去、二三里 个人微信号: iwaleon 微信公众号: 高效程序员 如果你经常需要美化样式(最常见的有:文本色、背景色、边框色、阴影色等),那一定离不开颜色。而在 QML 中,颜色的表示方法有多种:颜色名、十六进制颜色值、颜色相关的函数,一起来学习一下吧。 老规矩…...

基础数据结构--线段树(Python版本)

文章目录前言特点操作数据存储updateLazy下移查询实现前言 月末了&#xff0c;划个水&#xff0c;赶一下指标&#xff08;更新一些活跃值&#xff0c;狗头&#xff09; 本文主要是关于线段树的内容。这个线段树的话&#xff0c;主要是适合求解我们一个数组的一些区间的问题&am…...

【micropython】SPI触摸屏开发

背景&#xff1a;最近买了几块ESP32模块&#xff0c;看了下mircopython支持还不错&#xff0c;所以买了个SPI触摸屏试试水&#xff0c;记录一下使用过程。硬件相关&#xff1a;SPI触摸屏使用2.4寸屏幕&#xff0c;常见淘宝均可买到&#xff0c;驱动为ILI9341&#xff0c;具体参…...

【云原生】k8s中Pod进阶资源限制与探针

一、Pod 进阶 1、资源限制 当定义 Pod 时可以选择性地为每个容器设定所需要的资源数量。 最常见的可设定资源是 CPU 和内存大小&#xff0c;以及其他类型的资源。 当为 Pod 中的容器指定了 request 资源时&#xff0c;调度器就使用该信息来决定将 Pod 调度到哪个节点上。当还…...

全国做膏药的网站有多少家呢/如何开发一款app软件

在今天的Build大会上&#xff0c;微软宣布发布一款同时支持Windows、Mac OS X和Linux平台的原生Visual Studio应用——Visual Studio Code&#xff0c;旨在为所有开发者提供一款专注于代码本身的免费的编辑器。它虽然是Visual Studio家族的一员&#xff0c;但它与传统VS IDE的功…...

网站首页布局设计原理/网站建设公司哪家好

文章目录1.基础知识1.1.常规命令1.2.重定向符号,管道符号,通配符号&#xff0c;&&符号1.3.vim文本编辑器1.4.定时任务1.5.防火墙firewall-cmd命令1.6.scp远程上传下载文件2.查询服务器硬件信息3.systemctl服务管理命令4.资源监控命令4.1.查看系统的负载信息4.2.查看内存…...

进网站显示建设中怎么解决/长沙seo搜索

转自&#xff1a;http://www.cnblogs.com/daqiang/archive/2011/12/04/2275646.html STM32的定时器是个强大的模块&#xff0c;定时器使用的频率也是很高的&#xff0c;定时器可以做一些基本的定时&#xff0c;还可以做PWM输出或者输入捕获功能。 时钟源问题&#xff1a; 名…...

做网站流量/做网站比较好的公司有哪些

目标将图片进行进行镜像翻转.主要代码private fun toHorizontalMirror(bmp: Bitmap): Bitmap { val w bmp.width val h bmp.height val matrix Matrix() matrix.postScale(-1f, 1f) // 水平镜像翻转 return Bitmap.createBitmap(bmp, 0, 0, w, h, matrix, tr…...

wordpress完整安装包/网址安全检测中心

目录 JQuery初级 概念快速入门JQuery对象和JS原生对象的区别选择器DOM操作 内容操作属性操作CRUD操作 案例 JQuery高级 动画遍历事件绑定插件 Ajax 概念实现方式 原生JS实现(了解&#xff09;JQuery实现方式 $.ajax()$.get()$.post() json 概念语法 基本规则获取数据遍历 …...

商城网站建设流程图/如何查询百度收录情况

语法 switch(n) { case 1:执行代码块 1break; case 2:执行代码块 2break; default:n 与 case 1 和 case 2 不同时执行的代码 }工作原理&#xff1a;首先设置表达式 n&#xff08;通常是一个变量&#xff09;。随后表达式的值会与结构中的每个 case 的值做比较。如果存在匹配&am…...