Tacotron以及Tacotron2详解

概述

​ ​ ​ ​ Tacotron模型是首个真正意义上的端到端TTS深度神经网络模型。与传统语音合成相比,它没有复杂的语音学和声学特征模块,而是仅用<文本序列,语音声谱>配对数据集对神经网络进行训练,因此简化了很多流程。然后Tacotron使用Griffin-Lim算法对网络预测的幅度谱进行相位估计,再接一个短时傅里叶(Short-Time Fourier Transform,STFT)逆变换,实现端到端语音合成的功能。Tacotron的总体架构如下图:

​ ​ ​ ​ 如上图所示,Tacotron是一个带有注意力机制(Attention Mechanism)的序列到序列(Sequence-To-Sequence,Seq2Seq)生成模型,包括一个编码器模块和一个带有基于内容注意力的解码器模块,以及后处理网络。编码器负责将输入文本序列的每个字符映射到离散的One-Hot编码向量,再编码到低维连续的嵌入形式(Embedding),用于提取文本的鲁棒序列表示。解码器负责将文本嵌入(Text Embedding)解码成语音帧,在Tacotron中使用梅尔刻度声谱作为预测输出;其中基于内容的注意力模块用于学习如何对齐文本序列和语音帧,序列中的每个字符编码通常对应多个语音帧并且相邻的语音帧一般也具有相关性。后处理网络用于将该Seq2Seq模型输出的声谱转换为目标音频的波形,在Tacotron中先将预测频谱的振幅提高(谐波增强),再使用Griffin-Lim算法估计相位从而合成波形。除此之外,Tacotron独到地描述了一个称为CBHG的模块,它由一维卷积滤波器(1D-Convolution Bank)、高速公路网络(Highway Network)、双向门控递归单元(Bidirectional GRU)和循环神经网络(Recurrent Neural Network,RNN)组成,被用于从序列中提取高层次特征。

​ 在Tacotron改进版Tacotron2中,研究者去除了CBHG模块,改为使用更普遍的长短期记忆网络(Long Short-Term Memory,LSTM)和卷积层代替CBHG。并且因为Griffin-Lim算法合成的音频会携带特有的人工痕迹并且语音质量较低,在后处理网络中Tacotron2使用可训练的WaveNet声码器代替了Griffin-Lim算法。并且Tacotron2模型的语音合成性能更优(在MOS测试中达到4.526分,与真实人声接近)。Tacotron2的模型结构如下图:

梅尔谱图

​ ​ ​ ​ 研究Tacotron2模型结构之前,首先需要介绍模型的输入表示形式,在Tacotron2中,使用梅尔谱图(Mel Spectrogram)作为模型的声学表示。

​ ​ ​ ​ 由于声音信号是一维的时域信号,不容易从时域波形中直观地看出频率变化规律。若进行直接的时频变换,如通过快速傅里叶变换(Fast Fourier Transform,FFT)将时域波形转换到频域上,虽然能展现信号的频率分布,但与此同时也丢失了时域相关的信息,不能表示声音信号中频率分布随时间的变化情况。因此,为了解决该问题,学者提出了很多时频域分析的方法,其中最具代表性和常用的算法是STFT。STFT公式如下:
F ( ω , t ) = ∫ − ∞ ∞ f ( τ ) h ( τ − t ) e − j ω τ d τ F(\omega,t)=\int_{-\infty}^{\infty}f(\tau)h(\tau - t)e^{-j\omega \tau} d\tau F(ω,t)=f(τ)h(τt)ejωτdτ
其中 f ( t ) f(t) f(t)是原时域信号, h ( t ) h(t) h(t)是窗函数。它假设信号在短时间内是平稳不变的,通过对长信号分帧、加窗再对每一帧做FFT,将结果沿某一维度堆叠,得到图像形式的二维信号表示。当原时域信号是音频波形时,通过STFT变换得到的就是声谱图。

​ ​ ​ ​ 然而STFT处理后的频谱是线性的,由于语音信号相邻点间的相似性会导致在模型学习文本与音频对齐时产生极大冗余。声学领域的研究表明,人耳对频率的感知呈非线性,会自动强调低频信息,弱化高频信息。在1937年,Stevens、Volkmann和Newman根据此现象提出了梅尔标度,用于刻画人的听觉感知频率。梅尔频率标度的映射关系如下:
m e l ( f ) = 2595 l o g 10 ( 1 + f 700 ) mel(f)=2595\, log_{10}(1 + \frac{f}{700}) mel(f)=2595log10(1+700f)
梅尔标度反映了人类的听觉系统对声音的感知特性,它与频率呈对数增长关系,相对高频部分来说人耳对低频更敏感,这与现实生活对应——语音携带的信息大部分处于低频段,高频部分通常以摩擦声、噪声为主。所以使用梅尔标度可以提升语音数据中低频信息的细节,从而增强合成语音的可理解性。同时,通过频率轴经过梅尔标度的非线性变换后,可以用相对更少的维度来表征频谱内容,这也有利于信息的压缩。而且由于梅尔谱图具有相位不变性,相比波形表示更平滑,便于用均方误差损失(Mean Squared Error,MSE)进行训练。由于这样的特性,从梅尔尺度得到的特征作为一种声学表示在过去几十年广泛应用于语音处理。

深度学习相关概念

​ ​ ​ ​ 深度学习是近些年来兴起的一类多层神经网络学习算法,相对传统训练算法具有很多优势,被广泛应用于各个领域。在Tacotron2框架中,设计者使用到诸如卷积神经网络(Convolutional Neural Network,CNN)、长短期记忆网络(LSTM)等结构,下面将对Tacotron2中使用到的这些相关概念做一个介绍和梳理。

卷积神经网络(CNN)

​ ​ ​ ​ 卷积神经网络的研究起源于生物学中对视觉系统的研究,1962年Hubel和Wiesel在研究猫脑视觉皮层时发现一种对视觉输入空间局部区域敏感的细胞,将其定义为“感受野”。感受野以某种方式覆盖整个视觉域,能够更好地获取图像中的局部空间相关性。因此,学者们将这一结构特性加以拓展,应用到神经网络中,用以提取输入层的局部特征。卷积神经网络包括输入层(Input Layer)、卷积层(Convolutional layer)、池化层(Pooling layer)、全连接层(Fully-Connected Layer)以及输出层(Output Layer)等结构。在CNN中最核心的层结构是卷积层和池化层,均被应用到Tacotron2的模型中。

​ ​ ​ ​ 卷积层的功能是对输入层的数据进行特征提取,在本文中即用于提取文本嵌入的隐含特征。卷积层内部包括多个卷积核,卷积核通常是一个权值矩阵,在本文中由于处理的是声音信号故卷积核是向量表示。通过卷积核与输入层局部域的卷积操作(实际上是加权求和计算)可以实现对该层的特征提取;在提取过程中一般使用多个卷积层,从前往后逐层获取更高级的特征。除此之外卷积层还有一个重要的概念是滑动步长,即在某一层中卷积核每次计算完成后向前平移的距离。在经过卷积层处理后,由于神经网络的特性,通常需要经过一个非线性激活函数(Activation Function)处理,目前CNN最常用的激活函数是ReLU函数:
f c o v ( x ) = m a x ( 0 , x ) f_{cov}(\pmb{x})=max(\pmb{0},\pmb{x}) fcov(xxx)=max(000,xxx)
Tacotron2中卷积层使用的也是该激活函数。卷积层的深度、卷积核大小、滑动步长均会影响CNN提取特征的性能和训练效率,通常卷积层数量越多特征提取效果越好,但过多也会造成过拟合的现象,在设计网络时需选取适当值。

​ ​ ​ ​ 池化层的功能是降低特征的分辨率、压缩数据量,一般接在卷积层之后,用于获取具有空间不变性的特征,同时也可以防止模型的过拟合。与卷积层类似,池化层中也有多个称为池化核的结构,以及滑动步长的概念。

长短期记忆网络(LSTM)

​ ​ ​ ​ 在语音合成中,由于输入语句的某处发音通常决定于其上下文内容,因此建模时需要关注长时间跨度的序列信息。前文提到的卷积神经网络属于前向神经网络,即是单向的输入到输出映射,无法很好地获取时序相关信息。因此在建模具有时间跨度的序列特征时,通常使用的结构是RNN。RNN是一类具有循环连接结构的网络,它能够记忆之前时刻输入的信息并储存在记忆单元中,在计算时将综合处理当前时刻输入序列与之前的记忆内容再输出,完成输入序列到输出序列的建模。但一般的RNN结构,由于梯度消失问题,能捕获到的上下文内容是有范围限制的,故引入LSTM网络。

​ ​ ​ ​ 上图中展示了LSTM网络内部的核心构件记忆细胞单元。记忆细胞单元内部由胞状态(Cell State)、输入门(Input Gate)、输出门(Output Gate)、遗忘门(Forget Gate)这四个部件构成。其中,遗忘门控制对上一层中细胞状态的遗忘程度,由仿射变换和Sigmoid函数计算出的概率 f t f_t ft所决定:
f t = σ ( W f ⋅ [ h t − 1 , x t ] + b f ) \pmb{f}_t=\sigma(\pmb{W}_f\, \cdot [\pmb{h}_{t-1},\pmb{x}_t]\, + \, \pmb{b}_f) ffft=σ(WWWf[hhht1,xxxt]+bbbf)

其中 x t \pmb{x}_t xxxt代表当前时刻的输入, h t − 1 \pmb{h}_{t-1} hhht1代表上一时刻LSTM的输出, W f \pmb{W}_f WWWfb f \pmb{b}_f bbbf为权重和偏置,下同。

​ ​ ​ ​ 输入门由Sigmoid和tanh两种激活函数组成,根据当前时刻输入和上一时刻输出计算得到结果 i t \pmb{i}_t iiita t \pmb{a}_t aaat,相乘后传递给细胞状态。
i t = σ ( W i ⋅ [ h t − 1 , x t ] + b i ) a t = σ ( W a ⋅ [ h t − 1 , x t ] + b a ) \pmb{i}_t = \sigma(\pmb{W}_i\, \cdot [\pmb{h}_{t-1},\pmb{x}_t]\, + \, \pmb{b}_i) \\ \pmb{a}_t = \sigma(\pmb{W}_a\, \cdot [\pmb{h}_{t-1},\pmb{x}_t]\, + \, \pmb{b}_a) iiit=σ(WWWi[hhht1,xxxt]+bbbi)aaat=σ(WWWa[hhht1,xxxt]+bbba)
​ ​ ​ ​ 细胞状态 C t \pmb{C}_t CCCt由上一时刻的状态 C t − 1 \pmb{C}_{t-1} CCCt1、遗忘门计算出的 f t \pmb{f}_t ffft、输入门计算出的 i t \pmb{i}_t iiita t \pmb{a}_t aaat决定:
C t = C t − 1 ∘ f t + i t ∘ a t \pmb{C}_t = \pmb{C}_{t-1} \circ \pmb{f}_t \, + \, \pmb{i}_t \circ \pmb{a}_t CCCt=CCCt1ffft+iiitaaat
​ ​ ​ ​ 最后,根据本单元细胞状态、本序列输入、上一序列隐藏状态,通过输出门计算出本序列隐藏状态:
o t = σ ( W o ⋅ [ h t − 1 , x t ] + b o ) h t = o t ∘ t a n h ( C t ) \pmb{o}_t = \sigma(\pmb{W}_o\, \cdot [\pmb{h}_{t-1},\pmb{x}_t]\, + \, \pmb{b}_o) \\ \pmb{h}_t = \pmb{o}_t \circ tanh(\pmb{C}_t) ooot=σ(WWWo[hhht1,xxxt]+bbbo)hhht=ooottanh(CCCt)
​ ​ ​ ​ 通过引入记忆细胞单元的概念,可以解决RNN的梯度消失问题——通过关闭输入门、打开遗忘门可以很好地保持前面较早时刻的记忆,从而使长时间跨度前的输入信息影响到当前输出结果。

​ ​ ​ ​ 但LSTM结构仍存在网络结构上的缺点——无法捕获当前时刻之后的信息,因此研究者对此做了进一步优化:通过引入前向隐藏层和后向隐藏层并将两层的结果均连接到输出层上,构建双向长短时记忆循环神经网络(Bidirectional LSTM),从而能够捕获到当前时刻前后的全部时间序列信息。在Tacotron2中LSTM被用于合成声谱时记忆之前时间帧的预测结果,BLSTM被用于对输入文本序列编码时进行上下文信息建模。

混合注意力机制

​ ​ ​ ​ 注意力机制是一种人类大脑信号处理机制,被人类用于快速筛选关键信息,大大提高了人类对信息处理的效率与准确性。它也常常被用于深度学习中的序列到序列模型中,使得模型能够关注到输入序列的关键讯息。在文本到语音合成的任务中,注意力机制也起到了重要的作用。它通过在合理分配序列中每个元素所占的权重,从而使得模型对序列的各个部分的关注程度不同,更关注于与合成内容更密切相关的部分。此外,注意力机制还能关注输入序列与输出序列间可能存在的关联,在文本到语音合成任务中被用于对齐输入的文本序列与输出的语音序列。

​ ​ ​ ​ 注意力机制的种类繁多,在Tacotron2中使用的是混合注意力机制(Hbrid Attention),它有效地结合了基于内容内容的注意力机制(Content-based Attention)与基于位置的注意力机制(Location-based Attention)。使用了混合注意力机制的注意力模块被用于连接编码器与解码器。注意力机制本质上是对编码器输出的序列中元素的值(Value)进行加权求和,而查询(Query)与键(Key)则被用于计算对应值得权重系数。在此处,我们定义编码器的隐状态为 h = ( h 1 , . . . , h L ) \pmb{h}=(\pmb{h}_1,...,\pmb{h}_L ) hhh=(hhh1,...,hhhL),输入的梅尔谱图序列为 x = ( x 1 , . . . , x L ) \pmb{x}=(\pmb{x}_1,...,\pmb{x}_L ) xxx=(xxx1,...,xxxL),注意力模块计算后的输出序列为 y = ( y 1 , . . . , y T ) \pmb{y}=(\pmb{y}_1,...,\pmb{y}_T ) yyy=(yyy1,...,yyyT),那么在第 i i i步中注意力机制通过对隐状态 h \pmb{h} hhh的计算来生成 y i \pmb{y}_i yyyi
α i = A t t e n d ( s i − 1 , g i , y i ) g i = ∑ j = 1 L α i , j h j y i ∼ G e n e r a t e ( s i − 1 , g i ) \pmb{\alpha}_i = Attend(\pmb{s}_{i-1},\pmb{g}_i , \pmb{y}_i) \\ \pmb{g}_i = \sum_{j=1}^{L} \pmb{\alpha}_{i,j}\pmb{h}_j \\ \pmb{y}_i \, \thicksim \, Generate(\pmb{s}_{i-1}, \pmb{g}_i) αααi=Attend(sssi1,gggi,yyyi)gggi=j=1Lαααi,jhhhjyyyiGenerate(sssi1,gggi)
​ 其中 s i − 1 \pmb{s}_{i-1} sssi1是第 i − 1 i-1 i1步时的生成状态;此外,我们定义向量 α i \pmb{\alpha}_i αααi,其维度为 L L L,注意力机制中的权重系数。根据上述公式以及描述,可以计算第 i i i步时的生成状态 s i \pmb{s}_{i} sssi
s i = R e c u r r e n c y ( s i − 1 , g i , y i ) \pmb{s}_i = Recurrency(\pmb{s}_{i-1}, \pmb{g}_i, \pmb{y}_i) sssi=Recurrency(sssi1,gggi,yyyi)
​ ​ ​ ​ 在混合注意力机制中,除了上述的基于内容的注意力机制,还结合了基于位置的注意力机制。基于位置的注意力机制需要保证处理序列时的前后顺序具有一致性,因此引入维度为 k k k的向量 f i , j \pmb{f}_{i,j} fffi,j用于存储该步之前所累积的注意力权重。 f i \pmb{f}_{i} fffi可由矩阵 F \pmb{F} FFFα i − 1 \pmb{\alpha}_{i-1} αααi1进行卷积运算后得出,而后对 f i \pmb{f}_{i} fffi进行拓展的计算后经过tanh激活函数得到当前位置 i i i与隐藏状态位置 j j j的对齐得分 e i , j e_{i,j} ei,j
f i = F ∗ α i − 1 e i , j = ω T t a n h ( W s i − 1 + V h j + U f i , j + b ) \pmb{f}_i = \pmb{F} *\pmb{\alpha}_{i-1} \\ e_{i,j} = \omega^{T}tanh(\pmb{W}\pmb{s}_{i-1} + \pmb{V}\pmb{h}_{j} + \pmb{U}\pmb{f}_{i,j} + \pmb{b}) fffi=FFFαααi1ei,j=ωTtanh(WWWsssi1+VVVhhhj+UUUfffi,j+bbb)
​ ​ ​ ​ 最后,混合注意力机制需要将计算出的对其得分 e i , j e_{i,j} ei,j送入Softmax函数,从而可以计算出权重系数 α i , j \alpha_{i,j} αi,j

编码器-解码器结构

​ ​ ​ ​ 在语音合成中,输入序列(文本)与输出序列(音频)长度常常不是一致的,不能直接将输入序列的每个字符与目标发音一一对应,为此需使用到Seq2Seq框架中的编码器-解码器(Encoder-Decoder)结构。其中编码器用于对输入文本序列的信息进行提取并压缩成固定长度的上下文向量,作为输入文本的编码表示;解码器利用编码得到的上下文向量,经过一定的变换得到目标输出序列。

​ ​ ​ ​ 在Tacotron2中,编码器模块包括卷积层和BLSTM层。输入字符首先被表示成512维的文本嵌入,然后依次通过3个卷积层,其中每层卷积包含512个5×1的卷积核,再经过批标准化(Batch Normalization)和ReLU激活函数处理。然后传递给BLSTM 层,它包含512个单元(即前后方向各256个单元),用于生成编码特征。编码器的输出被传递给注意力模块用来关注输入序列的位置信息,经过32个31×1卷积核计算出位置特征,映射到128维向量上,作为注意力概率的表示。

​ ​ ​ ​ 解码器模块是一个自回归的循环神经网络,它将编码序列按时间步依次转换为输出帧作为预测声谱。自回归是通过两个全连接层组成的预处理网络(Pre-Net)实现:解码器上一步预测的频谱帧回传给Pre-Net,然后将其输出与注意力向量拼接,传递给两个堆叠起来的1024单元LSTM层。LSTM的输出再与注意力向量拼接在一起,经过一个线性投影层来预测目标频谱帧。为对频谱重构的结果做进一步改善,通过将预测频谱帧传递给包含5个卷积层的后处理网络(Post-Net)来预测一个残差项,并将其叠加到预测帧中。Post-Net的卷积层均由512个5×1的卷积核构成,每层后接批标准化和tanh激活函数处理。为及时终止自回归循环网络的预测帧操作,Tacotron2设计了一个停止标志(Stop Token)来预测输出序列完成与否的概率。它与解码器中帧预测网络平行,将LSTM层输出与注意力向量拼接结果投影成一个标量,再经过sigmoid激活函数预测输出序列生成结束的概率。从而根据该概率训练模型动态地决策何时结束频谱生成操作,通常设置结束概率的阈值为0.5。

声码器

​ ​ ​ ​ 声码器(Vocoder)在语音合成中往往被用于将生成的语音特征转换为我们所需要的语音波形。在Tacotron2中,由于前端的神经网络所预测出的梅尔谱图仅包含了幅值信息而缺乏相应的相位信息,我们难以直接通过短时傅里叶变换(STFT)的逆变换将梅尔谱图还原为声音波形文件;因此,我们需要使用声码器进行相位估计,并将输入的梅尔谱图转换为语音波形。

​ ​ ​ ​ Griffin-Lim算法是一种常见的传统声码器,其无需训练,通过帧与帧之间的关系估计相位信息。然而,其合成的音频存在携带特有的人工痕迹并且语音质量较低的问题。因此,Tacotron2中使用了基于神经网络的声码器WaveNet替代了Griffin-Lim算法。

​ ​ ​ ​ WaveNet模型是一种序列生成模型,在语音合成中主要被用作为声码器。其通过一个序列的前 t − 1 t-1 t1个点预测第 t t t个点的结果,因此可以用于预测语音波形中的采样点数值:
p ( x ) = ∏ t = 1 T p ( x t ∣ x 1 , . . . , x t − 1 ) p(x) = \prod_{t=1}^{T}p(x_t|x_1,...,x_{t-1}) p(x)=t=1Tp(xtx1,...,xt1)
在WaveNet中,研究者通过若干卷积层拟合上述公式中的条件概率分布。下图中展示了数层卷积层网络,它们层层相连自下而上进行了多次卷积操作,构成了WaveNet模型的主要部分。WaveNet在合成语音波形时,模型以自回归的方式输出,每次合成一个点的数据后,WaveNet模型进而又将该点放置于输入层最后一个点,而后输入数据继续进行迭代以生成新的数据。

​ ​ ​ ​ 由于语音数据的采样率高,并且其在时域上对感知范围的要求较大,WaveNet的卷积层选取了空洞卷积(Dilated Convolutions)这种模型。空洞卷积引入了dilation的概念,根据dilation的大小选择连接的节点,从而扩大了卷积层的感受野,使WaveNet模型在时域上拥有了更大的感知范围。

小结

​ ​ ​ ​ 在本文中,我们主要介绍了端到端语音合成系统Tacotron以及其改进版本Tacotron2,并且详细描述了Tacotron2模型中各个模块的细节:表示音频特征并作为输入的梅尔谱图,诸如CNN、LSTM等的深度神经网,提取文本嵌入的编码器,用于预测梅尔谱图的结合混合注意力机制的自回归网络解码器,用于生成语音波形的声码器等等。

Tacotron以及Tacotron2详解相关推荐

  1. Tacotron2 模型详解

    1 概述 Tacotron2是由Google Brain在2017年提出来的一个End-to-End语音合成框架.模型从下到上可以看作由两部分组成: 声谱预测网络:一个Encoder-Attentio ...

  2. 从命令行到IDE,版本管理工具Git详解(远程仓库创建+命令行讲解+IDEA集成使用)

    首先,Git已经并不只是GitHub,而是所有基于Git的平台,只要在你的电脑上面下载了Git,你就可以通过Git去管理"基于Git的平台"上的代码,常用的平台有GitHub.Gi ...

  3. JVM年轻代,老年代,永久代详解​​​​​​​

    秉承不重复造轮子的原则,查看印象笔记分享连接↓↓↓↓ 传送门:JVM年轻代,老年代,永久代详解 速读摘要 最近被问到了这个问题,解释的不是很清晰,有一些概念略微模糊,在此进行整理和记录,分享给大家.在 ...

  4. docker常用命令详解

    docker常用命令详解 本文只记录docker命令在大部分情境下的使用,如果想了解每一个选项的细节,请参考官方文档,这里只作为自己以后的备忘记录下来. 根据自己的理解,总的来说分为以下几种: Doc ...

  5. 通俗易懂word2vec详解词嵌入-深度学习

    https://blog.csdn.net/just_so_so_fnc/article/details/103304995 skip-gram 原理没看完 https://blog.csdn.net ...

  6. 深度学习优化函数详解(5)-- Nesterov accelerated gradient (NAG) 优化算法

    深度学习优化函数详解系列目录 深度学习优化函数详解(0)– 线性回归问题 深度学习优化函数详解(1)– Gradient Descent 梯度下降法 深度学习优化函数详解(2)– SGD 随机梯度下降 ...

  7. CUDA之nvidia-smi命令详解---gpu

    nvidia-smi是用来查看GPU使用情况的.我常用这个命令判断哪几块GPU空闲,但是最近的GPU使用状态让我很困惑,于是把nvidia-smi命令显示的GPU使用表中各个内容的具体含义解释一下. ...

  8. Bert代码详解(一)重点详细

    这是bert的pytorch版本(与tensorflow一样的,这个更简单些,这个看懂了,tf也能看懂),地址:https://github.com/huggingface/pytorch-pretr ...

  9. CRF(条件随机场)与Viterbi(维特比)算法原理详解

    摘自:https://mp.weixin.qq.com/s/GXbFxlExDtjtQe-OPwfokA https://www.cnblogs.com/zhibei/p/9391014.html C ...

最新文章

  1. Python 爬虫框架Scrapy安装汇总
  2. 单个GPU无法训练GPT-3,但有了这个,你能调优超参数了
  3. codeforces1027D
  4. spring boot+mybatisplus集成后访问项目接口404
  5. vue依赖缓存_Vue SSR服务端渲染之数据缓存
  6. java 23种设计模式及具体例子 收藏有时间慢慢看
  7. 病从口入 这样吃小心癌症找上门
  8. 直接内存访问 (Direct Memory Access, DMA)
  9. 500 OOPS: cannot change directory:/home/xxx”
  10. 【Python-2.7】大小写转换函数
  11. python产生随机值-random模块
  12. Oracle临时表GLOBAL TEMPORARY TABLE
  13. 【问】安装SQL 2012R2时提示NetFx3
  14. android wifi控制手机屏幕,安卓手机屏幕无线投射到电脑+反向控制
  15. java 正则表达式 去掉 文章头部和尾部的空格(全角,半角)、制表符、换页符
  16. 第四周项目1---建立单链表
  17. 登录失败 12306服务器不稳定,12306显示登陆失败43003怎么办-12306登录显示43003含义详解...
  18. 网易有数永久免费开放BI能力 普惠让技术更有温度
  19. 英伟达 TX2 蓝牙自动连接蓝牙 设备
  20. 极速模式下java无法加载_谷歌和360急速模式 下的XMLHttpRequest 的onprogress事件失效...

热门文章

  1. 【C++】虚函数与虚函数表
  2. [滑模控制器浅述] (5) 基于分层滑模的吊车控制
  3. 操作系统——虚拟内存技术
  4. CAD怎么打印彩色图纸
  5. 相位相关影像匹配算法
  6. (十八)自动装配-@Autowired-构造器,参数,方法,属性
  7. Oracle HINT的常见用法
  8. 打通现实世界,工程师通过DeFi一天内完成抵押借贷偿还房屋贷款
  9. 【安卓】——Autofill Framework(自动填写)用法详解
  10. Blazeds(一)