len bias

更新时间:2022-12-30 07:18:28 阅读: 评论:0


2022年12月30日发(作者:丛林大乱斗)

Transformer模型(pytorch代码详解)

⽬录

Transformer

模型基本介绍

与q2q相⽐transformer是⼀个纯粹基于注意⼒的架构(⾃注意⼒同时具有并⾏计算和最短的最⼤路径长度这两个优势),没有⽤到任何

CNN和RNN。

如下图所⽰,transformer是由编码器和解码器组成的。transformer的编码器和解码器是基于⾃注意⼒的模块叠加⽽成的,源(输⼊)序

列和⽬标(输出)序列的嵌⼊表⽰将加上位置编码,再分别输⼊到编码器和解码器中。

多头注意⼒

对同⼀key,value,query,希望可以抽取到不同信息

如短距离关系和长距离关系(与卷积时的多输出通道相似)

多头注意⼒使⽤h个独⽴的注意⼒池化

合并各个头(head)输出得到最终结果

具体思路:

我们可以⽤独⽴学习得到的h组不同的线性投影来变换查询、键和值。然后,这h组变换后的查询、键和值将并⾏地送到注意⼒汇聚中。最

后,将这h个注意⼒汇聚的输出拼接在⼀起,并且通过另⼀个可以学习的线性投影进⾏变换,以产⽣最终输出。这种设计被称为多头注意

⼒:

模型:

如上所⽰,额外加⼊了可学习参数W

该参数将query的维度从Dq映射为Pq,将key的维度从Dq映射为Kq,将value的维度从Dq映射为Vq。(这个映射通常会使数量减少)

最终再将输出的可学习参数Wo与hi的拼接结果相乘最终得到多头注意⼒的输出(Po)

有掩码的多头注意⼒

解码器对序列中⼀个元素输出时,不应该考虑该元素之后的元素

可以通过掩码来实现

也就是在计算Xi输出时,假装当前序列长度为i(将i以后的内容盖住/掩蔽)

基于位置的前馈⽹络

本质是⼀个全连接层

将输⼊形状由(b,n,d)变化为(bn,d)b为batch_sizen为序列长度d为特征维度

作⽤两个全连接层

输出形状由(bn,d)变化回(b,n,d)

等价于两层核窗⼝为1的⼀维卷积层

层归⼀化

批量归⼀化对每个特征/通道⾥的元素进⾏归⼀化(⽅差变1均值变0)

不适合序列长度会变得nlp应⽤(bn中n序列长度在不断变化)

层归⼀化对每个样本的元素进⾏归⼀化

如上图所⽰:

BatchNormalization处理的是d中的每⼀个b*len的矩阵(左图蓝⾊部分)⽅差变1均值变0。操作范围在每⼀个特征维度中。

LayerNormalization处理的是每⼀个batch中的len*d的矩阵(右图蓝⾊部分)⽅差变1均值变0。操作范围在单个样本内部。在变化长度

时较BN更加稳定

信息传递(对应结构图中连接解码器与编码器的线)

编码器中输出y1…yn

将其作为解码中第i个Transformer块中多头注意⼒的key和value(query来⾃⽬标序列)

意味着编码器和解码器中块的个数和输出维度都是⼀样的

预测

在预测t+1个输出时,解码器中输⼊前t个预测值(在⾃注意⼒中,前t个预测值作为key和value,第t个预测值作为query)

在进⾏第t+1个预测时,已经知道了前t个预测的值

在训练的时候可以是并⾏的,在预测时是顺序的。

⼩结:

Transformer是⼀个纯使⽤注意⼒的编码-解码器

编码器和解码器都有n个transformer块

每个块⾥使⽤多头(⾃)注意⼒,基于位置的前馈⽹络和层归⼀化

多头注意⼒实现

选择缩放点积注意⼒作为每⼀个注意⼒头

importmath

importtorch

fromtorchimportnn

fromd2limporttorchasd2l

#@save

classMultiHeadAttention():

"""多头注意⼒"""

def__init__(lf,key_size,query_size,value_size,num_hiddens,

num_heads,dropout,bias=Fal,**kwargs):

super(MultiHeadAttention,lf).__init__(**kwargs)

_heads=num_heads

ion=ductAttention(dropout)

lf.W_q=(query_size,num_hiddens,bias=bias)

lf.W_k=(key_size,num_hiddens,bias=bias)

lf.W_v=(value_size,num_hiddens,bias=bias)

lf.W_o=(num_hiddens,num_hiddens,bias=bias)

defforward(lf,queries,keys,values,valid_lens):

#queries,keys,values的形状:

#(batch_size,查询或者“键-值”对的个数,num_hiddens)

#valid_lens 的形状:

#(batch_size,)或(batch_size,查询的个数)

#经过变换后,输出的queries,keys,values 的形状:

#(batch_size*num_heads,查询或者“键-值”对的个数,

#num_hiddens/num_heads)

queries=transpo_qkv(lf.W_q(queries),_heads)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

Transformer实现

queries=transpo_qkv(lf.W_q(queries),_heads)

keys=transpo_qkv(lf.W_k(keys),_heads)

values=transpo_qkv(lf.W_v(values),_heads)

ifvalid_lensisnotNone:

#在轴0,将第⼀项(标量或者⽮量)复制num_heads次,

#然后如此复制第⼆项,然后诸如此类。

valid_lens=_interleave(

valid_lens,repeats=_heads,dim=0)

#output的形状:(batch_size*num_heads,查询的个数,

#num_hiddens/num_heads)

output=ion(queries,keys,values,valid_lens)

#output_concat的形状:(batch_size,查询的个数,num_hiddens)

output_concat=transpo_output(output,_heads)

returnlf.W_o(output_concat)

#@save

deftranspo_qkv(X,num_heads):

"""为了多注意⼒头的并⾏计算⽽变换形状"""

#输⼊X的形状:(batch_size,查询或者“键-值”对的个数,num_hiddens)

#输出X的形状:(batch_size,查询或者“键-值”对的个数,num_heads,

#num_hiddens/num_heads)

X=e([0],[1],num_heads,-1)

#输出X的形状:(batch_size,num_heads,查询或者“键-值”对的个数,

#num_hiddens/num_heads)

X=e(0,2,1,3)

#最终输出的形状:(batch_size*num_heads,查询或者“键-值”对的个数,

#num_hiddens/num_heads)

e(-1,[2],[3])

#@save

deftranspo_output(X,num_heads):

"""逆转transpo_qkv函数的操作"""

X=e(-1,num_heads,[1],[2])

X=e(0,2,1,3)

e([0],[1],-1)

num_hiddens,num_heads=100,5

attention=MultiHeadAttention(num_hiddens,num_hiddens,num_hiddens,

num_hiddens,num_heads,0.5)

()

batch_size,num_queries=2,4

num_kvpairs,valid_lens=6,([3,2])

X=((batch_size,num_queries,num_hiddens))

Y=((batch_size,num_kvpairs,num_hiddens))

attention(X,Y,Y,valid_lens).shape

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

##from/graykode/nlp-tutorial/tree/master/ormer

importnumpyasnp

importtorch

soptim

asplt

importmath

defmake_batch(ntences):

1

2

3

4

5

6

7

8

9

10

defmake_batch(ntences):

input_batch=[[src_vocab[n]forninntences[0].split()]]

output_batch=[[tgt_vocab[n]forninntences[1].split()]]

target_batch=[[tgt_vocab[n]forninntences[2].split()]]

nsor(input_batch),nsor(output_batch),nsor(target_batch)

##10

defget_attn_subquent_mask(q):

"""

q:[batch_size,tgt_len]

"""

attn_shape=[(0),(1),(1)]

#attn_shape:[batch_size,tgt_len,tgt_len]

subquence_mask=((attn_shape),k=1)#⽣成⼀个上三⾓矩阵

subquence_mask=_numpy(subquence_mask).byte()

returnsubquence_mask#[batch_size,tgt_len,tgt_len]

##DotProductAttention

classScaledDotProductAttention():

def__init__(lf):

super(ScaledDotProductAttention,lf).__init__()

defforward(lf,Q,K,V,attn_mask):

##输⼊进来的维度分别是[batch_sizexn_headsxlen_qxd_k]K:[batch_sizexn_headsxlen_kxd_k]V:[batch_sizexn_headsxlen_kxd_v]

##⾸先经过matmul函数得到的scores形状是:[batch_sizexn_headsxlen_qxlen_k]

scores=(Q,o(-1,-2))/(d_k)

##然后关键词地⽅来了,下⾯这个就是⽤到了我们之前重点讲的attn_mask,把被mask的地⽅置为⽆限⼩,softmax之后基本就是0,对q的单词不起作⽤

_fill_(attn_mask,-1e9)#Filllementsoflftensorwithvaluewheremaskisone.

attn=x(dim=-1)(scores)

context=(attn,V)

returncontext,attn

##eadAttention

classMultiHeadAttention():

def__init__(lf):

super(MultiHeadAttention,lf).__init__()

##输⼊进来的QKV是相等的,我们会使⽤映射linear做⼀个映射得到参数矩阵Wq,Wk,Wv

lf.W_Q=(d_model,d_k*n_heads)

lf.W_K=(d_model,d_k*n_heads)

lf.W_V=(d_model,d_v*n_heads)

=(n_heads*d_v,d_model)

_norm=orm(d_model)

defforward(lf,Q,K,V,attn_mask):

##这个多头分为这⼏个步骤,⾸先映射分头,然后计算atten_scores,然后计算atten_value;

##输⼊进来的数据形状:Q:[batch_sizexlen_qxd_model],K:[batch_sizexlen_kxd_model],V:[batch_sizexlen_kxd_model]

residual,batch_size=Q,(0)

#(B,S,D)-proj->(B,S,D)-split->(B,S,H,W)-trans->(B,H,S,W)

##下⾯这个就是先映射,后分头;⼀定要注意的是q和k分头之后维度是⼀致额,所以⼀看这⾥都是dk

q_s=lf.W_Q(Q).view(batch_size,-1,n_heads,d_k).transpo(1,2)#q_s:[batch_sizexn_headsxlen_qxd_k]

k_s=lf.W_K(K).view(batch_size,-1,n_heads,d_k).transpo(1,2)#k_s:[batch_sizexn_headsxlen_kxd_k]

v_s=lf.W_V(V).view(batch_size,-1,n_heads,d_v).transpo(1,2)#v_s:[batch_sizexn_headsxlen_kxd_v]

##输⼊进⾏的attn_mask形状是batch_sizexlen_qxlen_k,然后经过下⾯这个代码得到新的attn_mask:[batch_sizexn_headsxlen_qxlen_k],就是

attn_mask=attn_eze(1).repeat(1,n_heads,1,1)

##然后我们计算ScaledDotProductAttention这个函数,去7.看⼀下

##得到的结果有两个:context:[batch_sizexn_headsxlen_qxd_v],attn:[batch_sizexn_headsxlen_qxlen_k]

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

##得到的结果有两个:context:[batch_sizexn_headsxlen_qxd_v],attn:[batch_sizexn_headsxlen_qxlen_k]

context,attn=ScaledDotProductAttention()(q_s,k_s,v_s,attn_mask)

context=o(1,2).contiguous().view(batch_size,-1,n_heads*d_v)#context:[batch_sizexlen_qxn_heads*d_v]

output=(context)

_norm(output+residual),attn#output:[batch_sizexlen_qxd_model]

##eFeedForwardNet

classPoswiFeedForwardNet():

def__init__(lf):

super(PoswiFeedForwardNet,lf).__init__()

1=1d(in_channels=d_model,out_channels=d_ff,kernel_size=1)

2=1d(in_channels=d_ff,out_channels=d_model,kernel_size=1)

_norm=orm(d_model)

defforward(lf,inputs):

residual=inputs#inputs:[batch_size,len_q,d_model]

output=()(1(o(1,2)))

output=2(output).transpo(1,2)

_norm(output+residual)

##_attn_pad_mask

##⽐如说,我现在的句⼦长度是5,在后⾯注意⼒机制的部分,我们在计算出来QK转置除以根号之后,softmax之前,我们得到的形状

##len_input*len*input代表每个单词对其余包含⾃⼰的单词的影响⼒

##所以这⾥我需要有⼀个同等⼤⼩形状的矩阵,告诉我哪个位置是PAD部分,之后在计算计算softmax之前会把这⾥置为⽆穷⼤;

##⼀定需要注意的是这⾥得到的矩阵形状是batch_sizexlen_qxlen_k,我们是对k中的pad符号进⾏标识,并没有对k中的做标识,因为没必要

##q_q和q_k不⼀定⼀致,在交互注意⼒,q来⾃解码端,k来⾃编码端,所以告诉模型编码这边pad符号信息就可以,解码端的pad信息在交互注意⼒层是

defget_attn_pad_mask(q_q,q_k):

batch_size,len_q=q_()

batch_size,len_k=q_()

#eq(zero)isPADtoken

pad_attn_mask=q_(0).unsqueeze(1)#batch_sizex1xlen_k,oneismasking

returnpad_attn_(batch_size,len_q,len_k)#batch_sizexlen_qxlen_k

##onalEncoding代码实现

classPositionalEncoding():

def__init__(lf,d_model,dropout=0.1,max_len=5000):

super(PositionalEncoding,lf).__init__()

##位置编码的实现其实很简单,直接对照着公式去敲代码就可以,下⾯这个代码只是其中⼀种实现⽅式;

##从理解来讲,需要注意的就是偶数和奇数在公式上有⼀个共同部分,我们使⽤log函数把次⽅拿下来,⽅便计算;

##pos代表的是单词在句⼦中的索引,这点需要注意;⽐如max_len是128个,那么索引就是从0,1,2,...,127

##假设我的demodel是512,2i那个符号中i从0取到了255,那么2i对应取值就是0,2,4...510

t=t(p=dropout)

pe=(max_len,d_model)

position=(0,max_len,dtype=).unsqueeze(1)

div_term=((0,d_model,2).float()*(-(10000.0)/d_model))

pe[:,0::2]=(position*div_term)##这⾥需要注意的是pe[:,0::2]这个⽤法,就是从0开始到最后⾯,补长为2,其实代表的就是偶数位置

pe[:,1::2]=(position*div_term)##这⾥需要注意的是pe[:,1::2]这个⽤法,就是从1开始到最后⾯,补长为2,其实代表的就是奇数位置

##上⾯代码获取之后得到的pe:[max_len*d_model]

##下⾯这个代码之后,我们得到的pe形状是:[max_len*1*d_model]

pe=eze(0).transpo(0,1)

er_buffer('pe',pe)##定⼀个缓冲区,其实简单理解为这个参数不更新就可以

defforward(lf,x):

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

defforward(lf,x):

"""

x:[q_len,batch_size,d_model]

"""

x=x+[:(0),:]

t(x)

##rLayer:包含两个部分,多头注意⼒机制和前馈神经⽹络

classEncoderLayer():

def__init__(lf):

super(EncoderLayer,lf).__init__()

_lf_attn=MultiHeadAttention()

_ffn=PoswiFeedForwardNet()

defforward(lf,enc_inputs,enc_lf_attn_mask):

##下⾯这个就是做⾃注意⼒层,输⼊是enc_inputs,形状是[batch_sizexq_len_qxd_model]需要注意的是最初始的QKV矩阵是等同于这个输⼊的,去

enc_outputs,attn=_lf_attn(enc_inputs,enc_inputs,enc_inputs,enc_lf_attn_mask)#enc_inputstosameQ,K,V

enc_outputs=_ffn(enc_outputs)#enc_outputs:[batch_sizexlen_qxd_model]

returnenc_outputs,attn

##r部分包含三个部分:词向量embedding,位置编码部分,注意⼒层及后续的前馈神经⽹络

classEncoder():

def__init__(lf):

super(Encoder,lf).__init__()

_emb=ing(src_vocab_size,d_model)##这个其实就是去定义⽣成⼀个矩阵,⼤⼩是src_vocab_size*d_model

_emb=PositionalEncoding(d_model)##位置编码情况,这⾥是固定的正余弦函数,也可以使⽤类似词向量的ing获得⼀个可以更新学

=List([EncoderLayer()for_inrange(n_layers)])##使⽤ModuleList对多个encoder进⾏堆叠,因为后续的encoder并没有使⽤词向量

defforward(lf,enc_inputs):

##这⾥我们的enc_inputs形状是:[batch_sizexsource_len]

##下⾯这个代码通过src_emb,进⾏索引定位,enc_outputs输出形状是[batch_size,src_len,d_model]

enc_outputs=_emb(enc_inputs)#把数字索引转化为对应的向量

##这⾥就是位置编码,把两者相加放⼊到了这个函数⾥⾯,从这⾥可以去看⼀下位置编码函数的实现;3.

enc_outputs=_emb(enc_o(0,1)).transpo(0,1)

##get_attn_pad_mask是为了得到句⼦中pad的位置信息,给到模型后⾯,在计算⾃注意⼒和交互注意⼒的时候去掉pad符号的影响,去看⼀下这个函数4.

enc_lf_attn_mask=get_attn_pad_mask(enc_inputs,enc_inputs)

enc_lf_attns=[]

:

##去看EncoderLayer层函数5.

enc_outputs,enc_lf_attn=layer(enc_outputs,enc_lf_attn_mask)

enc_lf_(enc_lf_attn)

returnenc_outputs,enc_lf_attns

##10.

classDecoderLayer():

def__init__(lf):

super(DecoderLayer,lf).__init__()

_lf_attn=MultiHeadAttention()

_enc_attn=MultiHeadAttention()

_ffn=PoswiFeedForwardNet()

defforward(lf,dec_inputs,enc_outputs,dec_lf_attn_mask,dec_enc_attn_mask):

dec_outputs,dec_lf_attn=_lf_attn(dec_inputs,dec_inputs,dec_inputs,dec_lf_attn_mask)

dec_outputs,dec_enc_attn=_enc_attn(dec_outputs,enc_outputs,enc_outputs,dec_enc_attn_mask)

dec_outputs=_ffn(dec_outputs)

returndec_outputs,dec_lf_attn,dec_enc_attn

##r

classDecoder():

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

def__init__(lf):

super(Decoder,lf).__init__()

_emb=ing(tgt_vocab_size,d_model)

_emb=PositionalEncoding(d_model)

=List([DecoderLayer()for_inrange(n_layers)])

defforward(lf,dec_inputs,enc_inputs,enc_outputs):#dec_inputs:[batch_sizextarget_len]

dec_outputs=_emb(dec_inputs)#[batch_size,tgt_len,d_model]

dec_outputs=_emb(dec_o(0,1)).transpo(0,1)#[batch_size,tgt_len,d_model]

##get_attn_pad_mask⾃注意⼒层的时候的pad部分

dec_lf_attn_pad_mask=get_attn_pad_mask(dec_inputs,dec_inputs)

##get_attn_subquent_mask这个做的是⾃注意层的mask部分,就是当前单词之后看不到,使⽤⼀个上三⾓为1的矩阵

dec_lf_attn_subquent_mask=get_attn_subquent_mask(dec_inputs)

##两个矩阵相加,⼤于0的为1,不⼤于0的为0,为1的在之后就会被fill到⽆限⼩

dec_lf_attn_mask=((dec_lf_attn_pad_mask+dec_lf_attn_subquent_mask),0)

##这个做的是交互注意⼒机制中的mask矩阵,enc的输⼊是k,我去看这个k⾥⾯哪些是pad符号,给到后⾯的模型;注意哦,我q肯定也是有pad符号,但

dec_enc_attn_mask=get_attn_pad_mask(dec_inputs,enc_inputs)

dec_lf_attns,dec_enc_attns=[],[]

:

dec_outputs,dec_lf_attn,dec_enc_attn=layer(dec_outputs,enc_outputs,dec_lf_attn_mask,dec_enc_attn_mask)

dec_lf_(dec_lf_attn)

dec_enc_(dec_enc_attn)

returndec_outputs,dec_lf_attns,dec_enc_attns

##1.从整体⽹路结构来看,分为三个部分:编码层,解码层,输出层

classTransformer():

def__init__(lf):

super(Transformer,lf).__init__()

r=Encoder()##编码层

r=Decoder()##解码层

#输出层d_model是我们解码层每个token输出的维度⼤⼩,之后会做⼀个tgt_vocab_size⼤⼩的softmax

tion=(d_model,tgt_vocab_size,bias=Fal)

defforward(lf,enc_inputs,dec_inputs):

#这⾥有两个数据进⾏输⼊,⼀个是enc_inputs形状为[batch_size,src_len],主要是作为编码段的输⼊,⼀个dec_inputs,形状为[batch_size,tgt_len],主

#enc_inputs作为输⼊形状为[batch_size,src_len],输出由⾃⼰的函数内部指定,想要什么指定输出什么,可以是全部tokens的输出,可以是特定每⼀层的

#enc_outputs就是主要的输出,enc_lf_attns这⾥没记错的是QK转置相乘之后softmax之后的矩阵值,代表的是每个单词和其他单词相关性;

enc_outputs,enc_lf_attns=r(enc_inputs)

#dec_outputs是decoder主要输出,⽤于后续的linear映射;dec_lf_attns类⽐于enc_lf_attns是查看每个单词对decoder中输⼊的其余单词的相关性;

dec_outputs,dec_lf_attns,dec_enc_attns=r(dec_inputs,enc_inputs,enc_outputs)

#dec_outputs做映射到词表⼤⼩

dec_logits=tion(dec_outputs)#dec_logits:[batch_sizexsrc_vocab_sizextgt_vocab_size]

returndec_(-1,dec_(-1)),enc_lf_attns,dec_lf_attns,dec_enc_attns

if__name__=='__main__':

##句⼦的输⼊部分,

ntences=['ichmochteeinbierP','Siwantabeer','iwantabeerE']

#TransformerParameters

#PaddingShouldbeZero

##构建词表

src_vocab={'P':0,'ich':1,'mochte':2,'ein':3,'bier':4}

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

src_vocab_size=len(src_vocab)

tgt_vocab={'P':0,'i':1,'want':2,'a':3,'beer':4,'S':5,'E':6}

tgt_vocab_size=len(tgt_vocab)

src_len=5#lengthofsource

tgt_len=5#lengthoftarget

#模型参数

d_model=512#EmbeddingSize

d_ff=2048#FeedForwarddimension

d_k=d_v=64#dimensionofK(=Q),V

n_layers=6#numberofEncoderofDecoderLayer

n_heads=8#numberofheadsinMulti-HeadAttention

model=Transformer()

criterion=ntropyLoss()

optimizer=(ters(),lr=0.001)

enc_inputs,dec_inputs,target_batch=make_batch(ntences)

forepochinrange(20):

_grad()

outputs,enc_lf_attns,dec_lf_attns,dec_enc_attns=model(enc_inputs,dec_inputs)

loss=criterion(outputs,target_uous().view(-1))

print('Epoch:','%04d'%(epoch+1),'cost=','{:.6f}'.format(loss))

rd()

()

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

本文发布于:2022-12-30 07:18:28,感谢您对本站的认可!

本文链接:http://www.wtabcd.cn/fanwen/fan/90/58232.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

上一篇:the saltwater room
下一篇:周立波简介
标签:len bias
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图