GLMP 缩写来自论文 GLOBAL-TO-LOCAL MEMORY POINTER NETWORKS
FOR TASK-ORIENTED DIALOGUE
下面是它代码的详细注释(已跑通)
3.1 模型
3.1.1 ContextRNN
class ContextRNN(nn.Module):
def __init__(self, input_size, hidden_size, dropout, n_layers=1):
#初始化设置参数
super(ContextRNN, self).__init__()
self.input_size = input_size
self.hidden_size = hidden_size
self.n_layers = n_layers
self.dropout = dropout
#nn.Dropout:参数为float类型,将元素置0的概率
self.dropout_layer = nn.Dropout(dropout)
#nn.Embedding:参数分别为(单词个数 词向量维度 遇到PAD_token输出0)
#此处可看出embedding词向量的维度和hidden的维度相同
self.embedding = nn.Embedding(input_size, hidden_size, padding_idx=PAD_token)
#调用pytorch中的GRU模块,设置网络为双向GRU
self.gru = nn.GRU(hidden_size, hidden_size,
n_layers, dropout=dropout, bidirectional=True)
self.W = nn.Linear(2*hidden_size, hidden_size)
def get_state(self, bsz):
"""Get cell states and hidden states."""
return _cuda(torch.zeros(2, bsz, self.hidden_size))
def forward(self, input_seqs, input_lengths, hidden=None):
# Note: we run this all at once (over multiple batches of multiple sequences)
#contiguous函数返回一个内存连续的tensor
#view函数返回一个tensor,必须有与原tensor相同的数据和相同数目的元素,但可以有不同的大小。
#一个tensor必须是连续的contiguous()才能被查看。
#两个函数联合作用将embedding的维度调整成一句一行
embedded = self.embedding(input_seqs.contiguous().view(input_seqs.size(0),
-1).long())
embedded = embedded.view(input_seqs.size()+(embedded.size(-1),))
embedded = torch.sum(embedded, 2).squeeze(2)
embedded = self.dropout_layer(embedded)
#初始化hidden
hidden = self.get_state(input_seqs.size(1))
if input_lengths:
embedded = nn.utils.rnn.pack_padded_sequence(embedded, input_lengths, batch_first=False)
outputs, hidden = self.gru(embedded, hidden)
if input_lengths:
outputs, _ = nn.utils.rnn.pad_packed_sequence(outputs, batch_first=False)
hidden = self.W(torch.cat((hidden[0], hidden[1]), dim=1)).unsqueeze(0)
outputs = self.W(outputs)
return outputs.transpose(0,1), hidden
3.1.2 ExternalKnowledge
class ExternalKnowledge(nn.Module):
def __init__(self, vocab, embedding_dim, hop, dropout):
#ExternalKnowledge的工作原理类似于MemoryNetwork
super(ExternalKnowledge, self).__init__()
self.max_hops = hop#跳数
self.embedding_dim = embedding_dim
self.dropout = dropout
self.dropout_layer = nn.Dropout(dropout)
for hop in range(self.max_hops+1):#针对每一跳都初始化
#nn.Embedding:单词个数 词向量维度 遇到PAD_token输出0
C = nn.Embedding(vocab, embedding_dim, padding_idx=PAD_token)
#将值用均值为0,方差为0.1的正态分布填充
C.weight.data.normal_(0, 0.1)
##将一个child module添加到当前model,被添加的module可以通过name属性来获取。
self.add_module("C_{}".format(hop), C)
#定义查询C的方法?通过C_{i}来查询
self.C = AttrProxy(self, "C_")
#定义softmax为单维度的softmax
self.softmax = nn.Softmax(dim=1)
self.sigmoid = nn.Sigmoid()
#一维卷积函数Conv1d:参数分别为(进 出通道 卷积核的大小 输入的每一条边补充0的层数)
self.conv_layer = nn.Conv1d(embedding_dim, embedding_dim, 5, padding=2)
def add_lm_embedding(self, full_memory, kb_len, conv_len, hiddens):
#将hiddens按照kb_len的顺序加入full_memory矩阵
for bi in range(full_memory.size(0)):
start, end = kb_len[bi], kb_len[bi]+conv_len[bi]
full_memory[bi, start:end, :] = full_memory[bi, start:end, :] + hiddens[bi, :conv_len[bi], :]
return full_memory
def load_memory(self, story, kb_len, conv_len, hidden, dh_outputs):
# Forward multiple hop mechanism
#squeeze函数:把第一个维度(维度为1)挤掉
u = [hidden.squeeze(0)]
story_size = story.size()
self.m_story = []
for hop in range(self.max_hops):#循环K跳来计算Attention权重
#求c^k_i
#把外部知识的三元组挤成一个向量
embed_A = self.C[hop](story.contiguous().view(story_size[0], -1))#.long()) # b * (m * s) * e
embed_A = embed_A.view(story_size+(embed_A.size(