ELMo论文笔记+源码分析
Posted 成长NLPer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ELMo论文笔记+源码分析相关的知识,希望对你有一定的参考价值。
目录
1. 论文精读
1.1 阶段1:预训练过程
1.2 阶段2:应用到下游NLP task
1.3 ELMo优势
2. 源码分析
2.1 使用elmo能得到什么
2.2 elmo内部执行流程
3. ELMo应用到文本分类
4. 参考
1. 论文精读
论文全称:Deep contextualized word representations(https://arxiv.org/abs/1802.05365)
1.1 阶段1:预训练过程
ELMo的预训练过程就是常见的语言模型(Language Model,简称LM)的训练过程:从句子中学习预测next word,从而学习到对语言的理解的任务。语言模型的学习通常得益于海量的无需标注的文本数据。
ELMo是双向语言模型,它结合了前向LM和后向LM,其 目标
是 共同最大化前向和后向的对数似然
:
其中
是token representation的参数,
是Softmax layer的参数,
和
都是前后向网络共享
的参数。
和
分别是前向和后向LSTM网络参数。
模型的大致结构可参考下图(图片来源于http://jalammar.github.io/illustrated-bert/):
输入的句子会经过3层layer,先经过基于字符的char cnn encode layer,然后依次经过2层BiLSTM layer。这里面涉及到诸多细节,下面分别详细的描述一下Char CNN Encoder层和BiLSTM层这2个过程
(1)Char CNN Encoder
-
inputs先假设为 [b, max_sentence_len, max_word_len]
,其中b代表句子个数维度,也可以当成batch维度,max_sentence_len表示最大句子长度,max_word_len表示最大词长度,默认为50。 -
然后将 [b, max_sentence_len, max_word_len]
reshape为[b*max_sentence_len, max_word_len]
经过一个embedding layer,得到[b*max_sentence_len, max_word_len, embedding_dim]
,其中embedding_dim是embedding layer的char嵌入的维度,例如设为16。这个embedding layer的权重可以是初始化为一个预训练的embedding,也可以是在训练过程中一起学习得到。这个过程其实就是把原先char维度的one-hot编码(长度为50)转化为更为稠密的编码(长度为16)。 -
然后将上面的输出进行转置,得到 [b*max_sentence_len, embedding_dim, max_word_len]
,接下来将其送入到多个kernel size(卷积核大小)和out channel(通道维度)不同的卷积层中,每个filter对应的卷积层最后输出[b*max_sentence_len, out_channel, new_h]
,其中new_h是通过卷积公式计算:new_h=【h-kernelsize+2p】/stride +1,【】表示向下取整。然后[b*max_sentence_len, out_channel, new_h]
会经过一个max pool层,在new_h维度上做最大池化,得到[b*max_sentence_len, out_channel]
,然后把多个卷积层得到的输出在out_channel维度上进行concat,假设concat最后的维度是n_filters,得到[b*max_sentence_len, n_filters]
,其中n_filters=2048(官网模型中)。 -
然后上面的得到的结果会再经过2个highway layer,源码里的实现是: [b*max_sentence_len, n_filters]
-
最后会经过一个Linear的投影层,最后输出 [b*max_sentence_len, output_dim]
,其中output_dim为512。并会reshape为[b, max_sentence_len, output_dim]
,至此,就得到char cnn encoder的embedding。
(2)Bi_LSTM
-
char cnn encoder的输出经过forward layer得到 [b, max_sentence_len, hidden_size]
,其中hidden_size=512 -
char cnn encoder的输出经过backward layer得到 [b, max_sentence_len, hidden_size]
,其中hidden_size=512 -
将前向和后向的在hidden_size为维度做concat得到 [b, max_sentence_len, 2*hidden_size]
注意:
Bi_LSTM 有2层,并且他们之间有Skip connections,即两层BiLSTM之间有残差网络相连,也就是说第一层的输出不仅作为第二层的输入,同时也会和第二层的输出相加。返回的时候,也会分别返回2层最后得到的representation,即在Bi_LSTM层最后返回的是[num_layers, b, max_sentence_len, 2*hidden_size]
,num_layer=2,表示2层Bi_LSTM层。
经过上面,最后句子的表示会得到3层representation:
-
最底下的层是token_embedding,基于char cnn得到,对上下文不敏感 -
第一层Bi LSTM得到的对应token的embedding,这一层表示包含了更多的语法句法信息(syntax) -
第二层Bi LSTM得到的对应token的embedding,这一层表示包含了更多的词义的信息(word sense)
1.2 阶段2:应用到下游NLP task
由阶段1知道,每个token 经过ELMo,都能得到L+1=3个表示(L=2,表示Bi LSTM的层数),即对每个 ,都有如下的representations:
其中 表示token layer,而表示第j层的BiLSTM layer。这里为了保持维度上的一致,会对 复制层2份,即。
当在下游NLP task中使用上面的embedding时,ELMo将R中的所有层collapses以形成一个single向量: ,例如最简单情形就是只取top layer的那一层表示即 ,或者直接对三层取平均。
更一般地,会给特定的任务一组task specifific的权重,根据权重对不同层的表示进行scalar mixer:
其中, 表示softmax-normalized weights,标量参数 允许任务模型缩放整个ELMo向量(allows the task model to scale the entire ELMo vector), 对于帮助优化过程具有实际意义。通常较小的 模型在大多数cases中效果会更好。
考虑到每个BiLM层的输出具有不同的分布,在某些情况下,分布差异过大时,在加权前应使用 layer normalization(层归一化)
应用于每个BiLM层。
1.3 ELMo优势
-
双向语言模型,能更好的捕捉当前词上下文信息 -
通过ELMo得到的token embedding是根据词所在句子的上下文动态获得的,而不再是传统的fixed的词向量,这样多义词在不同场景中就能得到不同的表示 -
更深层的向量表示,不仅仅获取了top layer的表示,而是获得了3层的表示,有与上下文不敏感的token embedding,有更能捕获句法语法信息的第1个LSTM layer的表示,以及更能捕获词义信息的第2层LSTM layer的表示 -
task specifific的对各层的加权融合,能无缝衔接到其他下游任务中,例如直接提取ELMo的embedding替换下游model的embedding layer,也可以将ELMo的scalar mixer加入到下游模型一起训练,得到适合下有模型的各层的权重
2. 源码分析
2.1 使用elmo能得到什么
在看源码之前,我们先试试使用ELMo,看看输入句子后我们能获得什么。
安装ELMo: pip3 install allennlp
-
options.json:https://s3-us-west-2.amazonaws.com/allennlp/models/elmo/2x4096_512_2048cnn_2xhighway/elmo_2x4096_512_2048cnn_2xhighway_weights.hdf5 -
weights.hdf5:https://s3-us-west-2.amazonaws.com/allennlp/models/elmo/2x4096_512_2048cnn_2xhighway/elmo_2x4096_512_2048cnn_2xhighway_options.json
测试demo:
from allennlp.modules.elmo import Elmo, batch_to_ids
model_dir = 'E:/pretrained_model/elmo_original/'
options_file = model_dir+'elmo_2x4096_512_2048cnn_2xhighway_options.json'
weights_file = model_dir+'elmo_2x4096_512_2048cnn_2xhighway_weights.hdf5'
num_output_representations = 2 # 2或者1
elmo = Elmo(
options_file=options_file,
weight_file=weights_file,
num_output_representations=num_output_representations,
dropout=0
)
sentence_lists = [['I', 'have', 'a', 'dog', ',', 'it', 'is', 'so', 'cute'],
['That', 'is', 'a', 'question'],
['an']]
character_ids = batch_to_ids(sentence_lists) #
print('character_ids:', character_ids.shape) # [3, 11, 50]
res = elmo(character_ids)
print(len(res['elmo_representations'])) # 2
print(res['elmo_representations'][0].shape) # [3, 9, 1024]
print(res['elmo_representations'][1].shape) # [3, 9, 1024]
print(res['mask'].shape) # [3, 9]
输出:
character_ids: torch.Size([3, 9, 50])
2
torch.Size([3, 9, 1024])
torch.Size([3, 9, 1024])
torch.Size([3, 9])
模型的输出已经对三层的表示(character-convnet output, 1st lstm output, 2nd lstm output)做了线性融合。
注意:
num_output_reprensenations
用于指定需要几组不同的线性加权的结果,而并不是指定输出第几层的表示,代码中num_output_reprensenations可以是任意整数,跟lstm层数没有关系。在文档https://docs.allennlp.org/v1.2.2/api/modules/elmo/处有对其的解释:
Typically num_output_representations is 1 or 2. For example, in the case of the SRL model in the above paper, num_output_representations=1 where ELMo was included at the input token representation layer. In the case of the SQuAD model, num_output_representations=2 as ELMo was also included at the GRU output layer.
即通常取值为1或2,如果像SRL(Semantic role labeling) model这种,使用ELMo表示token embeding作为model 输入的,就取值为1,如果是像SQuAD(Stanford Question Answering Dataset) model这种,ELMo还在model 的输出层也参与了计算的,就取值为2。论文中也有指出,在不同的位置使用ELMo,模型的效果也是不一样的:
2.2 elmo内部执行流程
接下来我们就一起走一次完成的流程,从输入句子到最后获得的进行了线性融合后的embedding。我是将pytorch版的官方代码下载到本地IDE中进行阅读和调试以及测试的。
-
pytorch版官方代码:https://github.com/allenai/allennlp/tree/master/allennlp -
tensorflow版官方代码:https://github.com/allenai/bilm-tf
首先,直接进入allennlp/modules/elmo.py
中,这里有核心的Elmo
类,首先理清一下大致的调用关系,batch_to_ids()
将文本句子转化成ids,然后进入到Elmo
中,ELMo的forward
中调用了self._elmo_lstm()
,这是一个_ElmoBiLm
类的实例,_ElmoBiLm继承于nn.Module。在_ElmoBiLm的forward
中先是调用了self._token_embedder()
,再根据self._token_embedder()的结果调用self._elmo_lstm()
。_token_embedder是_ElmoCharacterEncoder
的实例,里面进行了对token的 embedding,cnn卷积,池化,concat,highway,投影
等一系列操作,对应 1.1节 中的 (1)Char CNN Encoder中所描述的细节;而_elmo_lstm是ElmoLstm
的实例,里面主要实现了 Bi LSTM,对前向后向输出结果进行concat,其中第1层和第2层lstm layer还进行了Skip connections(第一层的输出不仅作为第二层的输入,同时也会和第二层的输出相加)
,这些对应1.1节中的 (2)Bi_LSTM中所描述的细节。
下面将详细查看各功能模块以及对应的输入输出,我主要是自己写一个测试脚本,跟进整个执行流程。首先给出需要的基本输入,有句子以及options.json:
_options = {"lstm":
{"use_skip_connections": 'true',
"projection_dim": 512,
"cell_clip": 3,
"proj_clip": 3,
"dim": 4096,
"n_layers": 2},
"char_cnn":
{"activation": "relu",
"filters": [[1, 32], [2, 32], [3, 64], [4, 128], [5, 256], [6, 512], [7, 1024]],
"n_highway": 2,
"embedding": {"dim": 16},
"n_characters": 262,
"max_characters_per_token": 50}
}
sentence_lists = [['I', 'have', 'a', 'dog', ',', 'it', 'is', 'so', 'cute'],
['That', 'is', 'a', 'question'],
['an']]
为方便测试,_options 直接拷贝options .json里的内容。首先利用batch_to_ids将sentence_lists 转化为ids:
sentence_to_ids = batch_to_ids(sentence_lists)
print(sentence_to_ids.shape) # [3, 9, 50]
得到shape为[3, 9, 50]
,即[batch, max_sentence_len, max_word_len]
,最大句子长度max_sentence_len=9,因为第1个句子最长有9个token,最后一个维度max_word_len表示最大词长度,即每个token最多由50个char构成,这里面包含了<begin word>
和<end word>
。
(1)Char CNN Encoder
接下来这部分对应于1.1节 中的 Char CNN Encoder中所描述的细节,进行了对token的 embedding,cnn卷积,池化,concat,highway,投影
等一系列操作,而这些全在_ElmoCharacterEncoder
的forward
中完成,下面再测试脚本中仿照_ElmoCharacterEncoder
的forward
中完成这些操作,主要是搞清楚维度变化和一些具体的layer。
1 添加句子开始和结束标记
首先为句子开头和结束假设<begin sentence>
和<end sentence>
标记,则此时inputs的维度就由[batch, max_sentence_len, max_word_len]
=>[batch, max_sentence_len+2, max_word_len]
:
inputs = sentence_to_ids
# Add BOS/EOS
mask = (inputs > 0).sum(dim=-1) > 0
print(mask.shape, mask) # [3, 9]
character_ids_with_bos_eos, mask_with_bos_eos = add_sentence_boundary_token_ids(
inputs,
mask,
torch.from_numpy(
numpy.array(ELMoCharacterMapper.beginning_of_sentence_characters) + 1 # Jane: 256+1
),
torch.from_numpy(
numpy.array(ELMoCharacterMapper.beginning_of_sentence_characters) + 1 # Jane: 256+1
)
)
print(character_ids_with_bos_eos.shape, mask_with_bos_eos.shape) # [3, 11=1+9+1, 50], [3, 11]
print(character_ids_with_bos_eos[-1][:5]) # BOS an EOS
此时加了句子开头和结束标记的character_ids_with_bos_eos shape
为[3, 11, 50]
,mask
为[3,11]
,dim=1上比之前多了2。关于<begin word>
和<end word>
以及<begin sentence>
和<end sentence>
标记在allennlp/data/token_indexers/elmo_indexer.py
中有定义:
max_word_length = 50
# char ids 0-255 come from utf-8 encoding bytes
# assign 256-300 to special chars
beginning_of_sentence_character = 256 # <begin sentence>
end_of_sentence_character = 257 # <end sentence>
beginning_of_word_character = 258 # <begin word>
end_of_word_character = 259 # <end word>
padding_character = 260 # <padding>
2 character embedding 层
加上标记后,就进入一个embedding layer,这个embedding layer的权重可以是以一个预训练的词向量进行初始化,也可以是在学习中获得,下面随机初始化了一个权重_char_embedding_weights
进行示例说明,其对262个字符进行编码,每个字符对应大小为16的向量:
# the character id embedding
max_chars_per_token = 50
_char_embedding_weights = torch.rand(262, 16)
# (batch_size * sequence_length, max_chars_per_token, embed_dim)
character_embedding = torch.nn.functional.embedding(
character_ids_with_bos_eos.view(-1, max_chars_per_token), # input [3, 11, 50]=>[3*11, 50]
_char_embedding_weights # weight [max_id+1, char_embedding_dim=]
)
print(character_embedding.shape) # [3*11, 50, 16]
character_ids_with_bos_eos
先reshape成[3*11, 50]
,然后进入embedding层后得到shape为[3*11, 50, 16]
的输出。
3 multi-size 的卷积层
然后是进入多个不同filter size的卷积层:
# 转置
character_embedding = torch.transpose(character_embedding, 1, 2)
print(character_embedding.shape) # [3*11, 50, 16]=>[3*11, 16, 50] =>(batch_size * sequence_length, embed_dim, max_chars_per_token)
cnn_options = _options["char_cnn"]
filters = cnn_options["filters"]
char_embed_dim = cnn_options["embedding"]["dim"]
_convolutions = []
for i, (width, num) in enumerate(filters):
print('filter kernel_size:', width, 'out channels:', num)
conv = torch.nn.Conv1d( # [3*11, 16, 50]=>[3*11, num, [50-width]+1]
in_channels=char_embed_dim, out_channels=num, kernel_size=width, bias=True
)
_convolutions.append(conv)
convs = []
for i in range(len(_convolutions)):
conv = _convolutions[i]
convolved = conv(character_embedding)
# print('convolved:', convolved.shape)
# (batch_size * sequence_length, n_filters for this width)
"""
[3*11, 16, 50]=>[3*11, num, [50-width]+1]
"filters": [[1, 32], [2, 32], [3, 64], [4, 128], [5, 256], [6, 512], [7, 1024]],
[3*11, 16, 50]=>[3*11, 32, 50],[3*11, 32, 49],[3*11, 64, 48],[3*11, 128, 47],[3*11, 256, 46],[3*11, 512, 45],[3*11, 1024, 44],
"""
convolved, _ = torch.max(convolved, dim=-1) # 返回values和indices [3*11,num],[3*11,num] 相当于在dim=-1上做了maxpool, num=n_filters
# print('convolved:', convolved.shape)
convolved = torch.nn.functional.relu(convolved) # _options['char_cnn']['activation']='relu'
convs.append(convolved)
# (batch_size * sequence_length, n_filters)
token_embedding = torch.cat(convs, dim=-1) # =>[3*11, 2048]
# print(token_embedding.shape) # [3*11, 2048]
先将embedding层后得到输出进行转置,得到shape为[3*11, 16, 50]
,配置文件中共有7个不同的卷积核配置,卷积操作后跟着是max pool
操作(由torch.max(convolved, dim=-1)实现)和relu
。卷积核的out channel是有意设计的,这样在最后concat时,刚好得到2048,即最后token_embedding
的shape为[3*11, 2048]
。
4 highway 层
接下来就是进入highway layer:
# the highway layers have same dimensionality as the number of cnn filters
cnn_options = _options["char_cnn"]
filters = cnn_options["filters"]
n_filters = sum(f[1] for f in filters)
print('n_filters:', n_filters) # 2048
n_highway = cnn_options["n_highway"] # 2
# create the layers, and load the weights
_highways = Highway(n_filters, n_highway, activation=torch.nn.functional.relu)
# apply the highway layers (batch_size * sequence_length, n_filters)
token_embedding = _highways(token_embedding)
print(token_embedding.shape) # [3*11, 2048]
highway不改变输入的shape,经过highway 层的输出的shape依然为[3*11, 2048]
。
5 projection投影层
最后是投影层:
cnn_options = _options["char_cnn"]
filters = cnn_options["filters"]
n_filters = sum(f[1] for f in filters) # 2048
output_dim = _options["lstm"]["projection_dim"] # 512
_projection = torch.nn.Linear(n_filters, output_dim, bias=True)
# final projection (batch_size * sequence_length, embedding_dim)
token_embedding = _projection(token_embedding) # [3*11, 2048]=>[3*11, 512]
print('token_embedding:', token_embedding.shape)
投影操作将[3*11, 2048]
=>[3*11, 512]
,最后组织成字典,作为这一整个部分的返回:
# reshape to (batch_size, sequence_length, embedding_dim)
batch_size, sequence_length, _ = character_ids_with_bos_eos.size() # [3, 11, 50]
res= {
"mask": mask_with_bos_eos, # [3, 11]
"token_embedding": token_embedding.view(batch_size, sequence_length, -1), # [3, 11, 512]
}
print(res['mask'].shape, res['token_embedding'].shape)
这个返回的dict在BI LSTM中要用到,是整个_ElmoCharacterEncoder
的forward
的过程,他会在_ElmoBiLm
中的forward
中,由token_embedding = self._token_embedder(inputs)
得到,token_embedding
就是上面的字典的组成。
(2)Bi_LSTM
接下来这部分对应于1.1节 中的 Bi_LSTM中所描述的细节,主要是 对前向后向输出结果进行concat,其中第1层和第2层lstm layer还进行了Skip connections(第一层的输出不仅作为第二层的输入,同时也会和第二层的输出相加)
,然后对拼接后得特征还进行了投影操作,而这些全在_ElmoBiLm
的forward
以及ElmoLstm
(allennlp/modules/elmo_sltm.py)的forward
和_lstm_forward
完成的,下面依然是在测试脚本中仿照完成这些操作,主要是搞清楚维度变化和一些具体的layer。
_token_embedding = res
# 进入BiLSTM层
mask = _token_embedding["mask"]
type_representation = _token_embedding["token_embedding"]
_elmo_lstm = ElmoLstm(
input_size=_options["lstm"]["projection_dim"], # 512
hidden_size=_options["lstm"]["projection_dim"],
cell_size=_options["lstm"]["dim"], # 4096 lstm cell_size
num_layers=_options["lstm"]["n_layers"],
memory_cell_clip_value=_options["lstm"]["cell_clip"],
state_projection_clip_value=_options["lstm"]["proj_clip"],
requires_grad=False,
)
lstm_outputs = _elmo_lstm(type_representation, mask)
print('lstm_outputs:', lstm_outputs.shape) # [2, 3, 11, 1024] (num_layers, batch_size, sequence_length, hidden_size)
output_tensors = [
torch.cat([type_representation, type_representation], dim=-1) * mask.unsqueeze(-1) # [3, 11, 512],[3, 11, 512]=>[3, 11, 1024]*[3,11,1]
]
print('output_tensors:', len(output_tensors), output_tensors[0].shape) # [3,11,1024]
for layer_activations in torch.chunk(lstm_outputs, lstm_outputs.size(0), dim=0): # chunk 对tensors分块
print('layer_activations:', layer_activations.shape) # [3,11,1024]
output_tensors.append(layer_activations.squeeze(0))
res = {"activations": output_tensors, "mask": mask}
print(len(res['activations'])) # 3
print(res['activations'][0].shape) # [3, 11, 1024] 第0层 上下文无关token embedding layer
print(res['activations'][1].shape) # [3, 11, 1024] 第1层 bilstm layer
print(res['activations'][2].shape) # [3, 11, 1024] 第2层 bilstm layer
print(mask.shape) # [3, 11]
# run the biLM finished
_elmo_lstm.num_layers = _options["lstm"]["n_layers"] + 1 # 3
最后的输出中'activations'对应有3层表示,第0层是上下文无关token的表示,然后是第1层 bilstm layer的输出和第2层 bilstm layer的输出。
具体的正向反向实现和拼接,Skip connections,以及投影主要在ElmoLstm
(allennlp/modules/elmo_sltm.py)的_lstm_forward
完成,下面仅给出最核心的部分代码:
for layer_index, state in enumerate(hidden_states):
forward_cache = forward_output_sequence # 用于Skip connections
backward_cache = backward_output_sequence
forward_state: Optional[Tuple[Any, Any]] = None
backward_state: Optional[Tuple[Any, Any]] = None
if state is not None:
forward_hidden_state, backward_hidden_state = state[0].split(self.hidden_size, 2)
forward_memory_state, backward_memory_state = state[1].split(self.cell_size, 2)
forward_state = (forward_hidden_state, forward_memory_state)
backward_state = (backward_hidden_state, backward_memory_state)
forward_output_sequence, forward_state = forward_layer( # Jane =>(batch_size, max_timesteps, hidden_size=512)
# final state: (1, batch_size, hidden_size=512) and (1, batch_size, cell_size=4096)
forward_output_sequence, batch_lengths, forward_state
)
backward_output_sequence, backward_state = backward_layer( # Jane =>(batch_size, max_timesteps, hidden_size=512)
backward_output_sequence, batch_lengths, backward_state
)
# Skip connections, just adding the input to the output.
if layer_index != 0: # Jane 两层BiLSTM之间有残差网络相连,也就是说第一层的输出不仅作为第二层的输入,同时也会和第二层的输出相加
forward_output_sequence += forward_cache
backward_output_sequence += backward_cache
sequence_outputs.append( # 拼接
torch.cat([forward_output_sequence, backward_output_sequence], -1) # Jane =>[b,max_timesteps,2*hidden_size=1024]
)
(3)scalar_mix:各层的线性融合,给出最终的representation
num_output_representations 指定需要几组不同权重的线性组合,通常是1或者2。
bilm_output = res
layer_activations = bilm_output["activations"] # [3, 11, 1024],[3, 11, 1024],[3, 11, 1024]
mask_with_bos_eos = bilm_output["mask"] # [3,11]
_scalar_mixes = []
num_output_representations = 2
for k in range(num_output_representations): # Jane 通常是1或者2
print('_elmo_lstm.num_layers:', _elmo_lstm.num_layers)
scalar_mix = ScalarMix(
_elmo_lstm.num_layers, # 3
do_layer_norm=False,
initial_scalar_parameters=None,
trainable=False,
)
_scalar_mixes.append(scalar_mix)
最后根据上面定义的scaler mix,得到最终的表示:
# compute the elmo representations
_keep_sentence_boundaries = False # 是否保留句子开始结束标记
representations = []
for i in range(len(_scalar_mixes)):
scalar_mix = _scalar_mixes[i]
representation_with_bos_eos = scalar_mix(layer_activations, mask_with_bos_eos)
print('representation_with_bos_eos:', representation_with_bos_eos.shape) # [3, 11, 1024]
if _keep_sentence_boundaries:
processed_representation = representation_with_bos_eos
processed_mask = mask_with_bos_eos
else: # False 不保留句子开始结束标记
representation_without_bos_eos, mask_without_bos_eos = remove_sentence_boundaries(
representation_with_bos_eos, mask_with_bos_eos
)
processed_representation = representation_without_bos_eos
processed_mask = mask_without_bos_eos
print('processed_representation:', processed_representation.shape) # [3, 11-2=9, 1024]
representations.append(processed_representation) # dropout 省去了 不影响shape
最后我们来看看最终的elmo representation的shape,这与2.1节中的结果一致:
mask = processed_mask
elmo_representations = representations
res = {"elmo_representations": elmo_representations, "mask": mask}
print(len(res['elmo_representations'])) # 2
print(res['elmo_representations'][0].shape) # [3, 9, 1024]
print(res['elmo_representations'][1].shape) # [3, 9, 1024]
print(res['mask'].shape) # [3, 9]
3. ELMo应用到文本分类
想对textcnn(baseline),glove+textcnn, elmo+textcnn做一组文本分类的对比实现,验证一下elmo的效果。后续有时间弄完后,再在这里补上代码和对比结果。
最后:如果本文中出现任何错误,请您一定要帮忙指正,感激~
4. 参考
-
[1] https://allennlp.org/elmo
-
[2] https://docs.allennlp.org/v1.2.2/api/modules/elmo/
-
[3] https://github.com/allenai/allennlp/tree/master/allennlp
-
[4] http://jalammar.github.io/illustrated-bert/
-
[5]
-
[6] https://blog.csdn.net/Magical_Bubble/article/details/89160032
-
[7] https://larid.site/2020/03/01/elmo源码阅读流水账
-
[8] http://shomy.top/2019/01/01/elmo-1/
以上是关于ELMo论文笔记+源码分析的主要内容,如果未能解决你的问题,请参考以下文章
Android内核源码bionic目录下的子目录arch-arm源码分析笔记
《java精品毕设》基于javaweb宠物领养平台管理系统(源码+毕设论文+sql):主要实现:个人中心,信息修改,填写领养信息,交流论坛,新闻,寄养信息,公告,宠物领养信息,我的寄养信息等(代码片段
Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段