循环神经网络(RNN)简述
RNN及其变体
1、概述
(一)、概念
RNN(Recurrent Neural Network), 中文称作循环神经网络, 它一般以序列数据为输入, 通过网络内部的结构设计有效捕捉序列之间的关系特征, 一般也是以序列形式进行输出。
RNN的循环机制使模型隐层**上一时间步产生的结果, 能够作为当下时间步输入的一部分(**当下时间步的输入除了正常的输入外还包括上一步的隐层输出)对当下时间步的输出产生影响。
(二)、作用
RNN结构能够很好利用序列之间的关系, 因此针对自然界具有连续性的输入序列, 如人类的语言, 语音等进行很好的处理, 广泛应用于NLP领域的各项任务, 如文本分类, 情感分析, 意图识别, 机器翻译等
(三)、分类
这里我们将从两个角度对RNN模型进行分类. 第一个角度是输入和输出的结构, 第二个角度是RNN的内部构造.
- 按照输入和输出的结构进行分类:
- N vs N - RNN
- N vs 1 - RNN
- 1 vs N - RNN
- N vs M - RNN
- 按照RNN的内部构造进行分类:
- 传统RNN
- LSTM
- Bi-LSTM
- GRU
- Bi-GRU
(1)、N vs N - RNN
它是RNN最基础的结构形式, 最大的特点就是: 输入和输出序列是等长的. 由于这个限制的存在, 使其适用范围比较小, 可用于生成等长度的合辙诗句
(2)、N vs 1 - RNN
有时候我们要处理的问题输入是一个序列,而要求输出是一个单独的值而不是序列,应该怎样建模呢?我们只要在最后一个隐层输出h上进行线性变换就可以了,大部分情况下,为了更好的明确结果, 还要使用sigmoid或者softmax进行处理. 这种结构经常被应用在文本分类问题上
(3)、1 vs N - RNN
如果输入不是序列而输出为序列的情况怎么处理呢?我们最常采用的一种方式就是使该输入作用于每次的输出之上. 这种结构可用于将图片生成文字任务等
(4)、N vs M - RNN
这是一种不限输入输出长度的RNN结构, 它由编码器和解码器两部分组成, 两者的内部结构都是某类RNN, 它也被称为seq2seq架构. 输入数据首先通过编码器, 最终输出一个隐含变量c, 之后最常用的做法是使用这个隐含变量c作用在解码器进行解码的每一步上, 以保证输入信息被有效利用。seq2seq架构最早被提出应用于机器翻译, 因为其输入输出不受限制,如今也是应用最广的RNN模型结构. 在机器翻译, 阅读理解, 文本摘要等众多领域都进行了非常多的应用实践
2、传统RNN模型
(一)、概述
(1)、内部结构分析:
它的输入有两部分, 分别是h(t-1)以及x(t), 代表上一时间步的隐层输出, 以及此时间步的输入, 它们进入RNN结构体后, 会"融合"到一起, 这种融合我们根据结构解释可知, 是将二者进行拼接, 形成新的张量[x(t), h(t-1)], 之后这个新的张量将通过一个全连接层(线性层), 该层使用tanh作为激活函数, 最终得到该时间步的输出h(t), 它将作为下一个时间步的输入和x(t+1)一起进入结构体. 以此类推
(2)、根据结构分析得出内部计算公式:
h t = t a n h ( W t X t + U i h t − 1 + b i ) h_t=tanh(W_tX_t+U_ih_{t-1}+b_i) ht=tanh(WtXt+Uiht−1+bi)
(3)、激活函数tanh的作用
- 为系统增加非线性元素
- 加速算法收敛速度
(二)、代码实现
import torch
import torch.nn as nndef dm_rnn_for_base():'''第一个参数:input_size(输入张量x的维度)第二个参数:hidden_size(隐藏层的维度, 隐藏层的神经元个数)第三个参数:num_layer(隐藏层的数量)'''rnn = nn.RNN(5, 6, 1) #A'''第一个参数:sequence_length(输入序列的长度)第二个参数:batch_size(批次的样本数量)第三个参数:input_size(输入张量的维度)'''input = torch.randn(1, 3, 5) #B'''第一个参数:num_layer * num_directions(层数*网络方向)第二个参数:batch_size(批次的样本数)第三个参数:hidden_size(隐藏层的维度, 隐藏层神经元的个数)'''h0 = torch.randn(1, 3, 6) #C# [1,3,5],[1,3,6] ---> [1,3,6],[1,3,6]output, hn = rnn(input, h0)print('output--->',output.shape, output)print('hn--->',hn.shape, hn)print('rnn模型--->', rnn)
(三)、优缺点
- 优点
内部结构简单, 对计算资源要求低, 相比RNN变体:LSTM和GRU模型参数总量少了很多, 在短序列任务上性能和效果都表现优异
- 缺点
传统RNN在解决长序列之间的关联时, 通过实践,证明经典RNN表现很差, 原因是在进行反向传播的时候, 过长的序列导致梯度的计算异常, 发生梯度消失或爆炸
为何会出现梯度消失或爆炸:
根据反向传播算法和链式法则, 梯度的计算可以简化为以下公式:
D n = σ ′ ( z 1 ) w 1 ⋅ σ ′ ( z 2 ) w 2 ⋅ ⋯ ⋅ σ ′ ( z n ) w n D_n=σ′(z_1)w_1⋅σ′(z_2)w_2⋅⋯⋅σ′(z_n)w_n Dn=σ′(z1)w1⋅σ′(z2)w2⋅⋯⋅σ′(zn)wn
其中sigmoid的导数值域是固定的, 在[0, 0.25]之间
一旦公式中的w也小于1, 那么通过这样的公式连乘后, 最终的梯度就会变得非常非常小, 这种现象称作梯度消失.
反之, 如果我们人为的增大w的值, 使其大于1, 那么连乘够就可能造成梯度过大, 称作梯度爆炸
如果在训练过程中发生了梯度消失,权重无法被更新,最终导致训练失败; 梯度爆炸所带来的梯度过大,大幅度更新网络参数,在极端情况下,结果会溢出(NaN值)
3、LSTM模型
(一)、概述
LSTM(Long Short-Term Memory)也称长短时记忆结构, 它是传统RNN的变体, 与经典RNN相比能够有效捕捉长序列之间的语义关联, 缓解梯度消失或爆炸现象. 同时LSTM的结构更复杂, 它的核心结构可以分为四个部分去解析:
- 遗忘门
- 输入门
- 细胞状态
- 输出门
(二)、结构分析
(1)、遗忘门
计算公式:
f t = σ ( W f x t + U f h t − 1 + b f ) f_t=σ(W_fx_t+U_fh_{t-1}+b_f) ft=σ(Wfxt+Ufht−1+bf)
与传统的RNN内部结构非常相似,输入上一层的隐藏层和本次的数据,对数据进行拼接后输入到sigmoid函数内,对其激活得到ft。遗忘门的门值将作用于上一层的细胞状态上,代表遗忘过去多少信息。又因为遗忘门门值是由x(t), h(t-1)计算得来的, 因此整个公式意味着根据当前时间步输入和上一个时间步隐含状态h(t-1)来决定遗忘多少上一层的细胞状态所携带的过往信息。过滤上一个隐藏状态的冗余信息。
sigmoid函数的作用:
增加非线性结构,加快收敛。
(2)、输入门
计算公式:
i t = σ ( W i x t + U i h t − 1 + b i ) C ^ i = t a n h ( W c x t + U c h t − 1 + b c ) i_t = \sigma(W_ix_t+U_ih_{t-1}+b_i)\\ \hat{C}_i=tanh(W_cx_t+U_ch_{t-1}+b_c) it=σ(Wixt+Uiht−1+bi)C^i=tanh(Wcxt+Ucht−1+bc)
第一个就是产生输入门门值的公式, 它和遗忘门公式几乎相同, 区别只是在于它们之后要作用的目标上. 这个公式意味着输入信息有多少需要进行过滤。
第二个公式是与传统RNN的内部结构计算相同. 对于LSTM来讲, 它得到的是当前的细胞状态, 而不是像经典RNN一样得到的是隐含状态。
过滤当前时间步生成的细胞状态。
(3)、细胞状态
计算公式:
C t = f t ∗ C t − 1 + i t ∗ C ^ t 哈达玛积 C_t=f_t*C_{t-1}+i_t*\hat{C}_t\\哈达玛积 Ct=ft∗Ct−1+it∗C^t哈达玛积
将刚刚得到的遗忘门门值与上一个时间步得到的C(t-1)相乘, 再加上输入门门值与当前时间步得到的未更新C(t)相乘的结果. 最终得到更新后的C(t)作为下一个时间步输入的一部分. 整个细胞状态更新过程就是对遗忘门和输入门的应用。
(4)、输出门
O t = σ ( W o x t + U 0 h t − 1 + b o ) h t = O t ∗ t a n h ( C t ) O_t=\sigma(W_ox_t+U_0h_{t-1}+b_o)\\ h_t =O_t*tanh(C_t) Ot=σ(Woxt+U0ht−1+bo)ht=Ot∗tanh(Ct)
(三)、Bi_LSTM介绍
Bi-LSTM即双向LSTM, 它没有改变LSTM本身任何的内部结构, 只是将LSTM应用两次且方向不同, 再将两次得到的LSTM结果进行拼接作为最终输出。也就是将下面一层的output作为上一层的xt输入。
我们看到图中对"我爱中国"这句话或者叫这个输入序列, 进行了从左到右和从右到左两次LSTM处理, 将得到的结果张量进行了拼接作为最终输出. 这种结构能够捕捉语言语法中一些特定的前置或后置特征, 增强语义关联,但是模型参数和计算复杂度也随之增加了一倍, 一般需要对语料和计算资源进行评估后决定是否使用该结构。
(四)、实现
API:
nn.LSTM(input_size, hidden_size, num_layers, batch_first, bidirectional)
'''
input_size: 输入张量x中特征维度的大小.
hidden_size: 隐层张量h中特征维度的大小.
num_layers: 隐含层的数量.
batch_first:设置为True后,输入input第一位为batch_size,第二位为每句长度
bidirectional: 是否选择使用双向LSTM, 如果为True, 则使用; 默认不使用
'''my_LSTM = nn.LSTM(input_size, hidden_size, num_layers, batch_first, bidirectional)
output, (hn, cn) = my_LSTM(input, (h0, c0))
'''
input: 输入张量x.
h0: 初始化的隐层张量h.
c0: 初始化的细胞状态张量c
'''
例子:
# 定义LSTM的参数含义: (input_size, hidden_size, num_layers)
# 定义输入张量的参数含义: (sequence_length, batch_size, input_size)
# 定义隐藏层初始张量和细胞初始状态张量的参数含义:
# (num_layers * num_directions, batch_size, hidden_size)import torch.nn as nn
import torch
rnn = nn.LSTM(5, 6, 2)
input = torch.randn(1, 3, 5)
h0 = torch.randn(2, 3, 6)
c0 = torch.randn(2, 3, 6)
output, (hn, cn) = rnn(input, (h0, c0))
结果:
tensor([[[ 0.0447, -0.0335, 0.1454, 0.0438, 0.0865, 0.0416],[ 0.0105, 0.1923, 0.5507, -0.1742, 0.1569, -0.0548],[-0.1186, 0.1835, -0.0022, -0.1388, -0.0877, -0.4007]]],grad_fn=<StackBackward>)
>>> hn
tensor([[[ 0.4647, -0.2364, 0.0645, -0.3996, -0.0500, -0.0152],[ 0.3852, 0.0704, 0.2103, -0.2524, 0.0243, 0.0477],[ 0.2571, 0.0608, 0.2322, 0.1815, -0.0513, -0.0291]],[[ 0.0447, -0.0335, 0.1454, 0.0438, 0.0865, 0.0416],[ 0.0105, 0.1923, 0.5507, -0.1742, 0.1569, -0.0548],[-0.1186, 0.1835, -0.0022, -0.1388, -0.0877, -0.4007]]],grad_fn=<StackBackward>)
>>> cn
tensor([[[ 0.8083, -0.5500, 0.1009, -0.5806, -0.0668, -0.1161],[ 0.7438, 0.0957, 0.5509, -0.7725, 0.0824, 0.0626],[ 0.3131, 0.0920, 0.8359, 0.9187, -0.4826, -0.0717]],[[ 0.1240, -0.0526, 0.3035, 0.1099, 0.5915, 0.0828],[ 0.0203, 0.8367, 0.9832, -0.4454, 0.3917, -0.1983],[-0.2976, 0.7764, -0.0074, -0.1965, -0.1343, -0.6683]]],grad_fn=<StackBackward>)
(五)、优缺点
优点:
LSTM的门结构能够有效减缓长序列问题中可能出现的梯度消失或爆炸, 虽然并不能杜绝这种现象, 但在更长的序列问题上表现优于传统RNN
缺点:
内部结构相对较复杂, 因此训练效率在同等算力下较传统RNN低很多
4、GRU模型
(一)、概述
GRU(Gated Recurrent Unit)也称门控循环单元结构, 它也是传统RNN的变体, 同LSTM一样能够有效捕捉长序列之间的语义关联, 缓解梯度消失或爆炸现象. 同时它的结构和计算要比LSTM更简单, 它的核心结构可以分为两个部分去解析:
- 更新门
- 重置门
(二)、结构分析
(1)、更新门
计算公式:
z t = σ ( W z ∗ h t − 1 + U z ∗ x t ) z_t=\sigma(W_z*h_{t-1}+U_z*x_t) zt=σ(Wz∗ht−1+Uz∗xt)
(2)、重置门
计算公式:
r t = σ ( W r ∗ h t − 1 + U r ∗ x t ) r_t=\sigma(W_r*h_{t-1}+U_r*x_t) rt=σ(Wr∗ht−1+Ur∗xt)
(3)、总体流程
将上一层的隐藏状态h(t-1)和现在的输入x(t)先组合然后通过sigmoid函数激活,获得重置门rt,同样的方式获得更新门zt。
rt和ht-1进行哈达玛积后与xt一起经过权重矩阵后,再用tanh函数激活,获得未更新的ht
h ^ t = t a n h ( W h ( r t ∗ h t − 1 ) + U h ∗ x t ) \hat{h}_t=tanh(W_h(r_t*h_{t-1})+U_h*x_t) h^t=tanh(Wh(rt∗ht−1)+Uh∗xt)
此时zt需要先和未更新的ht求哈达玛积,ht-1再和(1-zt)求哈达玛积,再将上述两个结果相加获得到最后的结果ht
h t = ( 1 − z t ) ∗ h t − 1 + z t ∗ h ^ t h_t=(1-z_t)*h_{t-1}+z_t*\hat{h}_t ht=(1−zt)∗ht−1+zt∗h^t
这个过程意味着更新门有能力保留之前的结果, 当门值趋于1时, 输出就是新的h(t), 而当门值趋于0时, 输出就是上一时间步的h(t-1)。
(三)、Bi-GRU介绍
Bi-GRU与Bi-LSTM的逻辑相同, 都是不改变其内部结构, 而是将模型应用两次且方向不同, 再将两次得到的LSTM结果进行拼接作为最终输出
(四)、实现
API:
nn.GRU(inputsize,hidden_size,num_layers,batch_first,bidirectional)
'''
input_size: 输入张量x中特征维度的大小.
hidden_size: 隐层张量h中特征维度的大小.
num_layers: 隐含层的数量.
batch_first:设置为True后,输入input第一位为batch_size,第二位为每句长度
bidirectional: 是否选择使用双向GRU, 如果为True, 则使用; 默认不使用
'''my_GRU = nn.GRU(...)
output, hn = my_GRU(input, h0)
'''
input: 输入张量x.
h0: 初始化的隐层张量h
'''
例如:
import torch
import torch.nn as nndef dm_gru():gru = nn.GRU(4, 5, 2, batch_first=True, bidirectional=True)input = torch.randn(2, 4, 4)h0 = torch.randn(4, 2, 5)out, hn = gru(input, h0)print(out)print(out.shape)print(hn)print(hn.shape)if __name__ == '__main__':dm_gru()
结果:
tensor([[[ 6.2202e-01, -5.2002e-01, -2.1665e-01, 1.1606e+00, -6.9614e-01,1.7723e-01, 3.4490e-01, 2.2908e-01, -4.4243e-02, 2.8472e-01],[ 5.9470e-01, -1.1089e-03, 6.1477e-02, 7.7945e-01, -5.7582e-01,2.3751e-01, 2.8009e-01, 2.6080e-01, 1.4921e-01, 3.8098e-01],[ 4.7263e-01, 3.8091e-01, 2.6586e-01, 3.6587e-01, -5.1025e-01,2.6715e-01, 3.1573e-01, 3.6568e-01, 2.7377e-01, 5.2090e-01],[ 3.8016e-01, 5.9008e-01, 3.6576e-01, 2.1258e-02, -5.5816e-01,3.9258e-01, 4.6132e-01, 4.9604e-01, 4.7448e-01, 9.5863e-01]],[[-4.3905e-01, 5.6604e-01, 4.0822e-01, 8.5531e-01, 2.9254e-01,2.4266e-02, 2.8750e-01, 2.1772e-01, 1.5369e-01, 3.0358e-01],[-2.7539e-01, 8.3340e-01, 3.3841e-01, 4.1600e-01, 6.9993e-02,-1.5839e-02, 2.4091e-01, 3.9040e-01, 1.2497e-01, 4.3371e-01],[-1.2397e-01, 8.4867e-01, 2.9182e-01, 1.3590e-01, -4.1246e-02,-9.1719e-03, 2.3020e-01, 4.7799e-01, -1.9405e-02, 5.4496e-01],[ 1.9304e-02, 8.0219e-01, 1.5324e-01, -1.3357e-01, -8.3342e-02,6.3888e-04, 2.0426e-01, 6.7422e-01, -2.9473e-01, 7.3993e-01]]],grad_fn=<TransposeBackward1>)
torch.Size([2, 4, 10])
tensor([[[ 2.5272e-01, 3.5627e-01, -2.0827e-01, -4.5440e-01, -5.4678e-02],[-7.1883e-02, 4.4186e-01, -4.2401e-01, -2.9838e-01, -2.4969e-02]],[[ 3.2858e-01, 4.4854e-01, -3.2011e-01, -1.3063e-01, -2.5779e-01],[ 5.4962e-01, -4.5760e-04, -2.9187e-01, -2.9130e-01, 1.7019e-01]],[[ 3.8016e-01, 5.9008e-01, 3.6576e-01, 2.1258e-02, -5.5816e-01],[ 1.9304e-02, 8.0219e-01, 1.5324e-01, -1.3357e-01, -8.3342e-02]],[[ 1.7723e-01, 3.4490e-01, 2.2908e-01, -4.4243e-02, 2.8472e-01],[ 2.4266e-02, 2.8750e-01, 2.1772e-01, 1.5369e-01, 3.0358e-01]]],grad_fn=<StackBackward0>)
torch.Size([4, 2, 5])
(五)、优缺点
优点:
GRU和LSTM作用相同, 在捕捉长序列语义关联时, 能有效抑制梯度消失或爆炸, 效果都优于传统RNN且计算复杂度相比LSTM要小.
缺点:
GRU仍然不能完全解决梯度消失问题, 同时其作用RNN的变体, 有着RNN结构本身的一大弊端, 即不可并行计算, 这在数据量和模型体量逐步增大的未来, 是RNN发展的关键瓶颈
5、注意力机制
(一)、注意力机制的由来和定义
注意力机制是由机器翻译任务发展而来,seq2seq(Sequence to Sequence)架构翻译任务,seq2seq模型架构包括三部分,分别是encoder(编码器)、decoder(解码器)、中间语义张量c。
比如中文翻译到英文,欢迎 来 北京 → welcome to BeiJing。编码器首先处理中文输入"欢迎 来 北京",通过GRU模型获得每个时间步的输出张量,最后将它们拼接成一个中间语义张量c;接着解码器将使用这个中间语义张量c以及每一个时间步的隐层张量, 逐个生成对应的翻译语言。
早期在解决机器翻译这一类seq2seq问题时,通常采用的做法是利用一个编码器(encoder)和一个解码器(decoder)构建端到端的神经网络模型,但是基于编码解码的神经网络存在两个问题:
- 如果翻译的句子很长很复杂,比如直接一篇文章输进去,模型的计算量很大,并且模型的准确率下降严重
- 在翻译时,可能在不同的语境下,同一个词具有不同的含义,但是网络对这些词向量并没有区分度,没有考虑词与词之间的相关性,导致翻译效果比较差
在这种情况下注意力机制被提出。
“注意力机制”实际上就是想将人的感知方式、注意力的行为应用在机器上,让机器学会去感知数据中的重要和不重要的部分。
在机器翻译中,我们要让机器注意到每个词向量之间的相关性,有侧重地进行翻译,模拟人类理解的过程。
(二)、注意力机制的分类
通俗的来说,就是对于模型的每一个 输入项,都是图片中的不同部分,或者是语句中的某个单词分配的一个权重,这个权重大小就代表了我们希望模型对该部分一个关注程度。这样就可以通过权重大小来模拟人在处理信息的注意力的侧重,有效的提高了模型的性能,并且一定程度上 降低了计算量。
深度学习中的注意力机制通常可以分为三类:软注意(全局注意),硬注意(局部注意)和自注意(内注意)。
-
软注意机制(Soft/Global Attention: 对每个输入项的分配的权重为0-1之间,也就是某些部分关注的多一点,某些部分关注的少一点,因为对大部分信息都有考虑,但考虑程度不一样,所以相对来说计算量比较大。这是最常见的一种注意力机制。
-
硬注意机制(Hard/Local Attention): 对每个输入项分配的权重非0即1,和软注意不同,硬注意机制只考虑那部分需要关注,哪部分不关注,也就是直接舍弃掉一些不相关项。优势在于可以减少一定的时间和计算成本,但有可能丢失掉一些本应该注意的信息。
-
自注意力机制( Self/Intra Attention): 对每个输入项分配的权重取决于输入项之间的相互作用,即通过输入项内部的"表决"来决定应该关注哪些输入项。和前两种相比,在处理很长的输入时,具有并行计算的优势。
注意:注意力机制是一种通用的思想和技术,不依赖于任何模型,换句话说,注意力机制可以用于任何模型。
(三)、软注意力机制(soft attention)
(1)、普通情况下的Encoder-Decdoer框架
上图图例可以把它看作由一个句子(或篇章)生成另外一个句子(或篇章)的通用处理模型。对于句子对,我们的目标是给定输入句子Source,期待通过Encoder-Decoder框架来生成目标句子Target。Source和Target可以是同一种语言,也可以是两种不同的语言。而Source和Target分别由各自的单词序列构成:
S o u r c e = < X 1 , X 2 . . . X m > T a r g e t = < y 1 , y 2 . . . y n > Source=<X_1,X_2...X_m>\\ Target=<y_1,y_2...y_n> Source=<X1,X2...Xm>Target=<y1,y2...yn>
encoder顾名思义就是对输入句子Source进行编码,将输入句子通过非线性变换转化为中间语义表示C:
C = F ( X 1 , X 2 . . . X m ) C=F(X_1,X_2...X_m) C=F(X1,X2...Xm)
解码器Decoder来说,其任务是根据句子Source的中间语义表示C和之前已经生成的历史信息,y_1, y_2…y_i-1来生成i时刻要生成的单词y_i
y i = G ( C , y 1 , y 2 . . . y n ) y_i=G(C,y_1,y_2...y_n) yi=G(C,y1,y2...yn)
上述图中展示的Encoder-Decoder框架是没有体现出“注意力模型”的,所以可以把它看作是注意力不集中的分心模型。为什么说它注意力不集中呢?请观察下目标句子Target中每个单词的生成过程如下:
y 1 = f ( C ) y 2 = f ( C , y 1 ) y 3 = f ( C , y 1 , y 2 ) y_1=f(C)\\ y_2=f(C,y_1)\\ y_3=f(C,y_1,y_2) y1=f(C)y2=f(C,y1)y3=f(C,y1,y2)
其中f是Decoder的非线性变换函数。从这里可以看出,在生成目标句子的单词时,不论生成哪个单词,它们使用的输入句子Source的语义编码C都是一样的,没有任何区别。而语义编码C又是通过对source经过Encoder编码产生的,因此对于target中的任何一个单词,source中任意单词对某个目标单词y_i来说影响力都是相同的,这就是为什么说图中的模型没有体现注意力的原因。
(2)、加Attention的Encoder-Decdoer框架
引入Attention模型,注意力分配模型分配给不同英文单词的注意力大小。由于注意力模型的加入,原来在生成target单词时候的中间语义C就不再是固定的,而是会根据注意力概率变化的C,加入了注意力模型的Encoder-Decoder框架就变成了上图所示。
也就是生成句子的过程变成了:
y 1 = f 1 ( C 1 ) y 2 = f 1 ( C 2 , y 1 ) y 3 = f 1 ( C 3 , y 1 , y 2 ) y_1=f_1(C_1)\\ y_2=f_1(C_2,y_1)\\ y_3=f_1(C_3,y_1,y_2) y1=f1(C1)y2=f1(C2,y1)y3=f1(C3,y1,y2)
每个Ci可能对应着不同的源语句子单词的注意力分配概率分布:
C T o m = g ( 0.6 ∗ f 2 ( T o m ) , 0.2 ∗ f 2 ( C h a s e ) , 0.2 ∗ f 2 ( J e r r y ) ) C C h a s e = g ( 0.2 ∗ f 2 ( T o m ) , 0.7 ∗ f 2 ( C h a s e ) , 0.1 ∗ f 2 ( J e r r y ) ) C J e r r y = g ( 0.3 ∗ f 2 ( T o m ) , 0.2 ∗ f 2 ( C h a s e ) , 0.5 ∗ f 2 ( J e r r y ) ) C_{Tom}=g(0.6*f_2(Tom),0.2*f_2(Chase),0.2*f_2(Jerry))\\ C_{Chase}=g(0.2*f_2(Tom),0.7*f_2(Chase),0.1*f_2(Jerry))\\ C_{Jerry}=g(0.3*f_2(Tom),0.2*f_2(Chase),0.5*f_2(Jerry)) CTom=g(0.6∗f2(Tom),0.2∗f2(Chase),0.2∗f2(Jerry))CChase=g(0.2∗f2(Tom),0.7∗f2(Chase),0.1∗f2(Jerry))CJerry=g(0.3∗f2(Tom),0.2∗f2(Chase),0.5∗f2(Jerry))
f2函数代表Encoder对输入英文单词的某种变换函数,比如如果Encoder是用的RNN模型的话,这个f2函数的结果往往是某个时刻输入后隐层节点的状态值;g代表Encoder根据单词的中间表示合成整个句子中间语义表示的变换函数,一般的做法中,g函数就是对构成元素加权求和,即下列公式
C i = ∑ j = 1 L x a i j h j L x 代表输入句子 s o u r c e 的长度 a i j 代表在 T a r g e t 输出第 i 个单词时 s o u r c e 输入句子中的第 j 个单词的注意力分配系数 h j 则是 s o u r c e 输入句子中第 j 个单词的语义编码 , C_i=\sum^{L_x}_{j=1}a_{ij}h_j\\ Lx代表输入句子source的长度\\ a_{ij}代表在Target输出第i个单词时source输入句子中的第j个单词的注意力分配系数\\ h_j则是source输入句子中第j个单词的语义编码, Ci=j=1∑LxaijhjLx代表输入句子source的长度aij代表在Target输出第i个单词时source输入句子中的第j个单词的注意力分配系数hj则是source输入句子中第j个单词的语义编码,
(3)、计算注意力概率分布
(4)、Attention的本质思想
Attention机制可以看作,Target中每个单词是对Source每个单词的加权求和,而权重是Source中每个单词对Target中每个单词的重要程度。因此,Attention的本质思想会表示成下图:
将Source中的构成元素看作是一系列的数据对,给定Target中的某个元素Query,通过计算Query和各个Key的相似性或者相关性,即权重系数;然后对Value进行加权求和,并得到最终的Attention数值。
A t t e n t i o n ( Q u e r y , S o u r c e ) = ∑ i = 1 L x S i m i l a r i t y ( Q u e r y , K e y i ) ∗ V a l u e i Attention(Query,Source)=\sum^{L_x}_{i=1}Similarity(Query,Key_i)*Value_i Attention(Query,Source)=i=1∑LxSimilarity(Query,Keyi)∗Valuei
深度学习中的注意力机制中提到:Source 中的 Key 和 Value 合二为一,指向的是同一个东西,也即输入句子中每个单词对应的语义编码。
输入由三部分构成:Query、Key和Value。其中,(Key, Value)是具有相互关联的KV对,Query是输入的“问题”,Attention可以将Query转化为与Query最相关的向量表示。
*Attention 3步计算过程:
- 第一步:Query和Key进行相似度计算(Q*KT,Q与K的转置求哈达玛积),得到Attention Score;
- 第二步:对Attention Score进行Softmax归一化,得到权值矩阵;
- 第三步:权重矩阵与Value进行加权求和计算,得到当前对应的C,此时的C再与Q进行处理,解码出当前的信息。
那么Q、K、V到底是什么呢?
Q可以理解为我们的意图信息,类似于我们在搜索引擎输入的问题。K可以理解为原始信息中的关键信息,类似于搜索引擎中文章的标题或者摘要,而V就是原始信息的全部,类似于搜索引擎中文章的正文部分。
(四)、硬注意力机制(hard attention)
与软注意力机制的区别在于权重矩阵,Q*KT得到attention score后,通过softmax归一化得到权重矩阵,此时软注意力机制会直接使用这个权重矩阵,而硬注意力机制有两种方案。
-
选择将权重最大的那一项直接将权重置为1,其他权重项全部置为0,再使用更改后的权重矩阵对后续做处理。
-
根据注意力分布进行随机采样,采样结果作为Attention机制的输出。
硬性注意力通过以上两种方式选择Attention的输出,这会使得最终的损失函数与注意力分布之间的函数关系不可导,导致无法使用反向传播算法训练模型,硬性注意力通常需要使用强化学习来进行训练。因此,一般深度学习算法会使用软性注意力的方式进行计算。
(五)、自注意力机制(self attention)
(1)、概述
Self Attention,指的是Source内部元素之间或者Target内部元素之间发生的Attention机制,也可以理解为Target=Source这种特殊情况下的注意力机制。当然,具体的计算过程仍然是一样的,只是计算对象发生了变化而已。
针对全连接神经网络对于多个相关的输入无法建立起相关性的这个问题,通过自注意力机制来解决,自注意力机制实际上是想让机器注意到整个输入中不同部分之间的相关性。
(2)、计算方式
第一种方式计算,先求自生的Q,再求包括自身在内所有元素的K,点乘后得到相关性,再将所有的相关性整合为向量,通过softmax(用其他的也行),得到概率值。
(3)、注意力机制和自注意力机制的区别:
-
注意力机制的Q和K是不同来源的,例如,在Encoder-Decoder模型中,K是Encoder中的元素,而Q是Decoder中的元素。在中译英模型中,中文句子通过编码器被转化为一组特征表示K,这些特征表示包含了输入中文句子的语义信息。解码器在生成英文句子时,会使用这些特征表示K以及当前生成的英文单词特征Q来决定下一个英文单词是什么。
-
自注意力机制的Q和K则都是来自于同一组的元素,例如,在Encoder-Decoder模型中,Q和K都是Encoder中的元素,即Q和K都是中文特征,相互之间做注意力汇聚。也可以理解为同一句话中的词元或者同一张图像中不同的patch,这都是一组元素内部相互做注意力机制,因此,自注意力机制(self-attention)也被称为内部注意力机制(intra-attention)。
(六)、注意力计算规则
(1)、拼接做线性变换
A t t e n t i o n ( Q , K , V ) = S o f t m a x ( L i n e a r ( ∣ Q , k ∣ ) ) ⋅ V Attention(Q,K,V)=Softmax(Linear(|Q,k|))·V Attention(Q,K,V)=Softmax(Linear(∣Q,k∣))⋅V
首先编码器encoder先对所有元素进行编码,将所有的output拼接为一个V,在上图中v此时为一个3×3的向量。将encoder的编码器hn作为k输入给解码器decoder,此时开始预测步骤:
除了decoder第一次的GRU输入的是k和GO以外,后续的GRU输入的是上层的隐藏状态以及一个拼接处理矩阵。
将上一层的output作为Q,ht-1作为K,此时将Q与K做拼接,形成一个1×6的向量,再经过一个6×3的线性层,将矩阵转化为1×3,此时该向量就是attn-score,再输入到softmax中输出1×3的权重向量,此时和encoder传输的3×3的V进行相乘,得到一个1×3的向量,此向量再与上一层输出的vec做拼接输出1×6的向量,再经过一个6×3的线性层,将矩阵转化为1×3,此时将该向量作为下一层GRU的输入。
以此类推直到EOS。
(2)、做乘法
A t t e n t i o n ( Q , K , V ) = S o f t m a x ( Q ⋅ K T d k ) ⋅ V Attention(Q,K,V)=Softmax(\frac{Q·K^T}{\sqrt{d_k}})·V Attention(Q,K,V)=Softmax(dkQ⋅KT)⋅V
编码器encoder每次输出一个V和一个K,在计算是我们将所有的K拼接为一个整体,在上图中就是拼接为一个3×3的矩阵,在解码器解码时会输出一个Q,此时用Q·KT,得到一个1×3的矩阵,这个就是attn-score,这个向量经过softmax得到权重向量,再利用该权重向量与拼接后的3×3的V相乘,得到1×3的矩阵,再将这个矩阵和上一层的输出做拼接,得到一个1×6的矩阵,再经过一个6×3的线性层,将矩阵转化为1×3,此时将该向量作为下一层GRU的输入。
(七)、注意力机制QA
(1)、什么是深度神经网络注意力机制
注意力机制是注意力计算规则能够应用的深度学习网络的载体, 同时包括一些必要的全连接层以及相关张量处理, 使其与应用网络融为一体. 使用自注意力计算规则的注意力机制称为自注意力机制。
NLP领域中, 当前的注意力机制大多数应用于seq2seq架构, 即编码器和解码器模型,为什么要在深度神经网络中引入注意力机制?
- rnn等循环神经网络,随着时间步的增长,前面单词的特征会遗忘,造成对句子特征提取不充分
- rnn等循环神经网络是一个时间步一个时间步的提取序列特征,效率低下
- 研究者开始思考,能不能对32个单词(序列)同时提取事物特征,而且还是并行的,所以引入注意力机制
(2)、注意力机制的作用
- 在解码器端的注意力机制: 能够根据模型目标有效的聚焦编码器的输出结果, 当其作为解码器的输入时提升效果. 改善以往编码器输出是单一定长张量, 无法存储过多信息的情况.
- 在编码器端的注意力机制: 主要解决表征问题, 相当于特征提取过程, 得到输入的注意力表示. 一般使用自注意力(self-attention).
(八)、例子
# 任务描述:
# 有QKV:v是内容比如32个单词,每个单词64个特征,k是32个单词的索引,q是查询张量
# 我们的任务:输入查询张量q,通过注意力机制来计算如下信息:
# 1、查询张量q的注意力权重分布:查询张量q和其他32个单词相关性(相识度)
# 2、查询张量q的结果表示:有一个普通的q升级成一个更强大q;用q和v做bmm运算
# 3 注意:查询张量q查询的目标是谁,就是谁的查询张量。
# eg:比如查询张量q是来查询单词"我",则q就是我的查询张量import torch
import torch.nn as nn
import torch.nn.functional as F# MyAtt类实现思路分析
# 1 init函数 (self, query_size, key_size, value_size1, value_size2, output_size)
# 准备2个线性层 注意力权重分布self.attn 注意力结果表示按照指定维度进行输出层 self.attn_combine
# 2 forward(self, Q, K, V):
# 求查询张量q的注意力权重分布, attn_weights[1,32]
# 求查询张量q的注意力结果表示 bmm运算, attn_applied[1,1,64]
# q 与 attn_applied 融合,再按照指定维度输出 output[1,1,32]
# 返回注意力结果表示output:[1,1,32], 注意力权重分布attn_weights:[1,32]class MyAtt(nn.Module):# 32 32 32 64 32def __init__(self, query_size, key_size, value_size1, value_size2, output_size):super(MyAtt, self).__init__()self.query_size = query_sizeself.key_size = key_sizeself.value_size1 = value_size1self.value_size2 = value_size2self.output_size = output_size# 线性层1 注意力权重分布self.attn = nn.Linear(self.query_size + self.key_size, self.value_size1)# 线性层2 注意力结果表示按照指定维度输出层 self.attn_combineself.attn_combine = nn.Linear(self.query_size+self.value_size2, output_size)def forward(self, Q, K, V):# 1 求查询张量q的注意力权重分布, attn_weights[1,32]# [1,1,32],[1,1,32]--> [1,32],[1,32]->[1,64]# [1,64] --> [1,32]# tmp1 = torch.cat( (Q[0], K[0]), dim=1)# tmp2 = self.attn(tmp1)# tmp3 = F.softmax(tmp2, dim=1)attn_weights = F.softmax( self.attn(torch.cat( (Q[0], K[0]), dim=-1)), dim=-1)# 2 求查询张量q的结果表示 bmm运算, attn_applied[1,1,64]# [1,1,32] * [1,32,64] ---> [1,1,64]attn_applied = torch.bmm(attn_weights.unsqueeze(0), V)# 3 q 与 attn_applied 融合,再按照指定维度输出 output[1,1,64]# 3-1 q与结果表示拼接 [1,32],[1,64] ---> [1,96]output = torch.cat((Q[0], attn_applied[0]), dim=-1)# 3-2 shape [1,96] ---> [1,32]output = self.attn_combine(output).unsqueeze(0)# 4 返回注意力结果表示output:[1,1,32], 注意力权重分布attn_weights:[1,32]return output, attn_weights
相关文章:
循环神经网络(RNN)简述
RNN及其变体 1、概述 (一)、概念 RNN(Recurrent Neural Network), 中文称作循环神经网络, 它一般以序列数据为输入, 通过网络内部的结构设计有效捕捉序列之间的关系特征, 一般也是以序列形式进行输出。 RNN的循环机制使模型隐层**上一时间步产生的结果, 能够作为当下时间步…...
九、Ubuntu Linux操作系统
一、Ubuntu简介 Ubuntu Linux是由南非人马克沙特尔沃思(Mark Shutteworth)创办的基于Debian Linux的操作系统,于2004年10月公布Ubuntu是一个以桌面应用为主的Linux发行版操作系统Ubuntu拥有庞大的社区力量,用户可以方便地从社区获得帮助其官方网站:http…...
SpringBoot 新冠密接者跟踪系统:校园疫情防控的智能守护者
摘 要 信息数据从传统到当代,是一直在变革当中,突如其来的互联网让传统的信息管理看到了革命性的曙光,因为传统信息管理从时效性,还是安全性,还是可操作性等各个方面来讲,遇到了互联网时代才发现能补上自古…...
【Ubuntu】E: Unable to locate package xxx
报错描述 在 Ubuntu 上 执行 apt install xxx 出现下面的报错: 即无法定位到该 Package,一般形式如下: # apt install xxx Reading package lists... Done Building dependency tree... Done Reading state information... Done E: Unable …...
vue多页面应用集成时权限处理问题
在多页面应用(MPA)中,权限管理通常会涉及到每个页面的访问控制、身份验证、以及权限校验。以下是几种常见的权限处理方式: 1. 前端路由权限控制 原理:虽然是多页面应用,通常每个页面会独立加载和渲染&…...
Socket编程(TCP/UDP详解)
前言:之前因为做项目和找实习没得空,计算机网络模块并没有写成博客,最近得闲了,把计算机网络模块博客补上。 目录 一,UDP编程 1)创建套接字 2)绑定端口号 3)发送与接收数据 4&…...
qt QConicalGradient详解
1、概述 QConicalGradient是Qt框架中QGradient的一个子类,它用于创建锥形渐变效果。锥形渐变是从一个中心点出发,沿着360度的圆周扩散的颜色渐变。这种渐变通常用于模拟光线旋转、创建彩虹效果或实现其他复杂的颜色过渡。QConicalGradient允许你定义渐变…...
存储过程与自然语言处理逻辑的不同与结合
在现代软件开发中,存储过程与自然语言处理(NLP)逻辑都发挥着重要作用。存储过程是一种在数据库内部运行的预编译程序,通常用于处理与数据相关的任务,例如插入、更新、删除数据以及复杂的查询操作。而自然语言处理&…...
了解Linux —— 理解其中的权限
前言 在了解Linux权限之前,先来探讨我们使用的shell 命令它到底是什么? Linux 是一个操作系统,我们称其为内核(kernel) ,正常情况下,我们一般用户操作并不是去直接使用内核,而是通过kernel 的外壳程序&…...
知识图谱嵌入与因果推理的结合
知识图谱通过节点(实体)和边(关系)来表示现实世界中的信息,但如何将这些信息转化为可进行推理和决策的形式,仍然是一个挑战。 另一方面,因果推理(Causal Inference)作为…...
STM32 PWM波形详细图解
目录 前言 一 PWM介绍 1.1 PWM简介 1.2 STM32F103 PWM介绍 1.3 时钟周期与占空比 二.引脚映像关系 2.1引脚映像与寄存器 2.2 复用功能映像 三. PWM 配置步骤 3.1相关原理图 3.2配置流程 3.2.1 步骤一二: 3.2.2 步骤三: 3.2.3 步骤四五六七: …...
Python Web 开发 FastAPI 入门:从基础架构到框架比较
Python Web 开发 FastAPI 入门:从基础架构到框架比较 目录 🖥️ Web 服务器概述(如 Nginx、Apache)🔗 前后端分离架构详解🔄 HTTP 路由和请求处理机制🧰 Web 框架概述:Django、Fla…...
基于STM32的智能仓库管理系统设计
目录 引言环境准备 硬件准备软件准备智能仓库管理系统基础 控制系统架构功能描述代码实现:实现智能仓库管理系统 4.1 RFID标签读取模块4.2 库存管理模块4.3 数据显示与监控模块4.4 无线通信模块应用场景:智能仓库管理系统优化问题解决方案与优化收尾与总…...
排序算法--堆排序【图文详解】
“留在码头的船才最安全” “但亲爱的,那不是造船的目的。 堆--插入heapInsert 原来有一个大根堆,如图: 现在要新插入一个数字50,进行插入 流程:和父亲相比,如果比父亲大,和父亲交换ÿ…...
FCBP 认证考试要点摘要
理论知识 数据处理与分析:包括数据的收集、清洗、转换、存储等基础操作,以及数据分析方法,如描述性统计分析、相关性分析、数据挖掘算法等的理解和应用 。数据可视化:涉及图表类型的选择与应用,如柱状图、折线图、饼图…...
鸿蒙生态崛起的机遇有什么
鸿蒙生态系统的崛起为各个领域带来了多个机遇,主要体现在以下几个方面: 智能设备的互联互通:鸿蒙系统旨在实现不同设备之间的无缝连接,为物联网(IoT)设备的发展提供了良好的基础。这将推动智能家居、智慧城…...
基础(函数、枚举)错题汇总
枚举默认从0开始,指定后会按顺序赋值 而这个枚举变量X,如果在全局(函数外部)定义,那默认为0,如果在函数内部(局部变量),那就是随机值,必须初始化。 枚举变量…...
【Spark源码分析】规则框架- `analysis`分析阶段使用的规则
analysis分析阶段使用的规则 规则批策略规则说明SubstitutionfixedPointOptimizeUpdateFields该规则优化了 UpdateFields 表达式链,因此看起来更像优化规则。但是,在处理深嵌套模式时,UpdateFields 表达式树可能会非常复杂,导致分…...
mysql--二进制安装编译安装yum安装
二进制安装 创建用户和组 [rootlocalhost ~]# groupadd -r -g 306 mysql [rootlocalhost ~]# useradd -r -g 306 -u 306 -d /data/mysql mysql 创建文件夹并添加所属文件用户和组 [rootlocalhost ~]# mkdir -p /data/mysql [rootlocalhost ~]# chown mysql:mysql /data/mysql …...
《Django 5 By Example》阅读笔记:p339-p358
《Django 5 By Example》学习第12天,p339-p358总结,总计20页。 一、技术总结 1.项目(购物网站) django-admin startproject myshop 虽然这里只是示例,但我觉得这种命名为 myxxx 的习惯非常不好,因为在实际应用中,是…...
鸿蒙修饰符
文章目录 一、引言1.1 什么是修饰符1.2 修饰符在鸿蒙开发中的重要性1.3 修饰符的作用机制 二、UI装饰类修饰符2.1 Styles修饰符2.1.1 基本概念和使用场景2.1.2 使用示例2.1.3 最佳实践 2.2 Extend修饰符2.2.1 基本概念2.2.2 使用示例2.2.3 Extend vs Styles 对比2.2.4 使用建议…...
springboot359智慧草莓基地管理系统(论文+源码)_kaic
毕 业 设 计(论 文) 题目:智慧草莓基地管理系统 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本智慧草莓基地管理系统就…...
单片机位数对性能会产生什么影响?!
单片机的位数是指其处理器核心的位宽,通常以比特(bit)为单位。常见的位数有8位、16位、32位和64位等。 单片机位数越高,处理器能够处理的数据量越大,性能也相应提高。 以下是对单片机位数对性能影响的详细分析&#…...
stm32内部高速晶振打开作为主时钟
首先建议你别这么干,因为内部晶振特别容易受温度等外界影响,很容易卡死或堵死程序 我是因为没画外部晶振电路,所以只能开内部晶振来作为时钟 适用于stm32f103系列 把下面的代码换掉源文件里的时钟源配置 /* 开启HSI 即内部晶振时钟 */RCC…...
【分页查询】.NET开源 ORM 框架 SqlSugar 系列
.NET开源 ORM 框架 SqlSugar 系列 【开篇】.NET开源 ORM 框架 SqlSugar 系列【入门必看】.NET开源 ORM 框架 SqlSugar 系列【实体配置】.NET开源 ORM 框架 SqlSugar 系列【Db First】.NET开源 ORM 框架 SqlSugar 系列【Code First】.NET开源 ORM 框架 SqlSugar 系列【数据事务…...
【CSS in Depth 2 精译_061】9.4 CSS 中的模式库 + 9.5 本章小结
当前内容所在位置(可进入专栏查看其他译好的章节内容) 【第九章 CSS 的模块化与作用域】 ✔️ 9.1 模块的定义 9.1.1 模块和全局样式9.1.2 一个简单的 CSS 模块9.1.3 模块的变体9.1.4 多元素模块 9.2 将模块组合为更大的结构 9.2.1 模块中多个职责的拆分…...
惠普电脑切换默认F1至F12快捷键,FN切换
发现新买的惠普电脑,按F1至F12发现是快捷功能键,而按fnF1至F12才是windows的功能键和正常我自己使用的电脑刚好相反,实在太不方便了。 解决办法需要进入biso里面去把功能键模式选中给关掉,才能恢复回来...
计算机的错误计算(一百七十)
摘要 回复一中学生来信,探讨 MATLAB 关于算式 的计算问题。 在计算机的错误计算(一百三十二)中,我们探讨了手持式计算器关于算式 的计算问题。一中学生来信询问该算式在数学软件中是否会出错。 例1. 在 MATLAB 中计算 . 首…...
Python `async def` 函数中使用 `yield` 和 `return` 的区别
Python async def 函数中使用 yield 和 return 的区别 1. return 的使用示例代码输出结果解释 2. yield 的使用示例代码输出结果解释 3. 总结 在 Python 中,async def 函数用于定义异步函数,这些函数可以在执行过程中暂停和恢复,通常与 await…...
JAVA修饰符
JAVA 修饰符...
go语言视频网站开发/重庆关键词优化
Smartbi Mining平台是一个注重于实际生产应用的数据分析预测平台,它旨在为个人、团队和企业所做的决策提供预测。该平台不仅可为用户提供直观的流式建模、拖拽式操作和流程化、可视化的建模界面,还提供了大量的数据预处理操作。此外,它内置了…...
有什么网站是学做吃的/自己可以做网站吗
GridView总结:获取任何一级别中GridView所选中的数据行.GridView gView gridControl.FocusedView as GridView;//選中的GridView int[] rows gView.GetSelectedRows(); //選中的行有時需要根據條件設置某一個單元格未只讀:private void gridView9_ShowingEditor(object sende…...
cms网站系统/网络营销方法有什么
????????关注后回复 “进群” ,拉你进程序员交流群????????平时工作累了,空(mo)闲(yu)时最喜欢逛的个人博客之一就是阮一峰老师的博客,昨天他的博客平台看到一篇文章(https://www.ruanyifeng.com/blog/2021/06/drunk-post-of…...
广东品牌网站建设/搜索引擎优化的技巧
根据最新的数据统计,Java和JavaScript主导了开发者,拥有大量忠实粉丝(开发者)。然而,随着更多的应用程序逐渐转移到云上,这种情况可能会发生变化。当苹果公司和Facebook争相着去推出新的编程语言࿰…...
网站做业务赚钱/平台推广方式
基于图神经网络的图表征学习方法 开源学习地址:datawhale-基于图神经网络的图表征学习方法 1.基于图同构网络(GIN)的图表征模块(GINGraphRepr Module) import torch from torch import nn from torch_geometric.nn.glob import global_add_p…...
网站开发后台的问题/b2b平台是什么意思
--------首先来看看ip、uv和pv的定义---------- PV(访问量):即Page View, 即页面浏览量或点击量,用户每次刷新即被计算一次。 UV(独立访客):即Unique Visitor,访问您网站的一台电脑客户端为一个访客。00:00-24:00内相同的客户端只被计算一次。…...