機器閱讀理解模型Stanford Attentive Reader原始碼
Stanford Attentive Reader是史丹佛在2016年的ACL會議上的《A Thorough Examination of the CNN/Daily Mail Reading Comprehension Task》(,,程式碼要求Python 2.7,Theano >= 0.7,深度學習框架Lasagne 0.2.dev1,程式碼整體風格非常簡潔易懂)釋出的一個機器閱讀理解模型。我們對Stanford Attentive Reader模型的原始碼進行了解析,力求透過分析其原始碼來深入研究一個機器閱讀理解模型是如何工作的。
採用的資料集有兩個:CNN和Daily Mail,下載地址分別為 (546M)和 (1.4G);100維glove詞向量。
1 載入資料load_data這部分主要是載入訓練集、驗證集以及測試集的CNN和Daily Mail資料,輸入引數主要有三個:
- in_file:表示資料檔案地址;
- max_example:如果在debug模式下,該值為100,表示只採用資料集中前100個樣本,如果在非debug模式下,該值為None,表示採用資料集中全部資料;
- relabeling:bool型,如果要求對資料集中實體類單詞重新編號,則該值為True,若要求重新編號則按照實體類單詞出現的順序進行編號,預設為True。
輸出為文件集合、問題集合和答案集合。
從load_data輸入輸出可看出,其功能主要是分別提取每個資料集樣本中的文件、問題以及答案,並按對應順序放到三個不同的list中,從answer = entity_dict[answer]
中可以看出,答案一定是文件或問題中的某個實體類單詞。
原始碼為:
def load_data(in_file, max_example=None, relabeling=True):
documents = []
questions = []
answers = []
num_examples = 0
f = open(in_file, 'r')
while True:
line = f.readline()
if not line:
break
question = line.strip().lower()
answer = f.readline().strip()
document = f.readline().strip().lower()
if relabeling:
q_words = question.split(' ')
d_words = document.split(' ')
assert answer in d_words
entity_dict = {}
entity_id = 0
for word in d_words + q_words:
if (word.startswith('@entity')) and (word not in entity_dict):
entity_dict[word] = '@entity' + str(entity_id)
entity_id += 1
q_words = [entity_dict[w] if w in entity_dict else w for w in q_words]
d_words = [entity_dict[w] if w in entity_dict else w for w in d_words]
answer = entity_dict[answer]
question = ' '.join(q_words)
document = ' '.join(d_words)
questions.append(question)
answers.append(answer)
documents.append(document)
num_examples += 1
f.readline()
if (max_example is not None) and (num_examples >= max_example):
break
f.close()
return (documents, questions, answers)
載入訓練集和驗證集資料後,則開始構建單詞字典。
2 單詞字典build_dict輸入為文件集合和問題集合sentences
;最大詞數max_words
,預設為50000。
輸出即為文件和問題集合的單詞字典word_dict
,key
為單詞,value
為按照詞頻排序的序號。
build_dict使用了Python集合類的Counter
子類來完成文件與問題的單詞統計,Counter
可以便捷和快速地進行雜湊的物件計數,並返回一個無序的容器,元素被作為字典的key
儲存,它們的計數作為字典的value
儲存。然後使用most_common
方法從多到少返回一個有前max_words
多的元素的列表ls
,相同數量的元素次序任意。最後返回一個由ls組成的單詞字典,值得注意的是,ls
列表的資料從字典的第三個開始填充,單詞字典第一個為“無法識別的詞”即字典第一個元素為”
,第二個為分隔符即”|||”:1
。
原始碼為:
def build_dict(sentences, max_words=50000):
word_count = Counter()
for sent in sentences:
for w in sent.split(' '):
word_count[w] += 1
ls = word_count.most_common(max_words)
return {w[0]: index + 2 for (index, w) in enumerate(ls)}
把機器閱讀理解問題看作一個分類問題。
entity_markers = list(set([w for w in word_dict.keys() if w.startswith('@entity')] + train_examples[2]))
entity_markers = [''] + entity_markers
entity_dict = {w: index for (index, w) in enumerate(entity_markers)}
args.num_labels = len(entity_dict)
3 embedding字典 根據單詞字典word_dict
來構建嵌入層gen_embeddings
,gen_embeddings
的輸入包含四個引數:
-
word_dict
:表示單詞字典; -
dim
:表示詞向量維度; -
in_file
:如果為None,則表示不使用預訓練好的詞向量來初始化嵌入層,這時使用第四個引數的引數初始化方法lasagne.init.Uniform()
,即對嵌入層權值進行均勻分佈初始化,如果不為None,則應該輸入預訓練詞向量的檔案地址,這裡預設使用的是100維的glove詞向量來初始化嵌入層,值得注意的是,類似於“@entity1”這種單詞顯然不包含在glove詞向量中,那麼對於這類詞也採用均勻分佈初始化的方法; -
init
:預設為使用深度學習框架lasagne
的均勻分佈初始化方法。
gen_embeddings
的輸出即為embeddings
字典,key
表示word_dict
中對應詞的value
,即按照詞頻排序的序號,value
為一個列表,表示key
的詞向量,詞向量中的值為float
型別。
原始碼為:
def gen_embeddings(word_dict, dim, in_file=None,
init=lasagne.init.Uniform()):
num_words = max(word_dict.values()) + 1
embeddings = init((num_words, dim))
if in_file is not None:
pre_trained = 0
for line in open(in_file).readlines():
sp = line.split()
if sp[0] in word_dict:
pre_trained += 1
embeddings[word_dict[sp[0]]] = [float(x) for x in sp[1:]]
return embeddings
4 Encode層 將構建的embeddings
字典來初始化lasagne.layers.EmbeddingLayer
的權重,這裡預設使用雙向GRU(lasagne.layers.GRULayer
)來構建文件和問題的encode層,在rnn_layer
中的引數backwards
表示了是否使用雙向GRU。對於文件的encode,即name=‘d’
的stack_rnn
,其中引數only_return_final
表示是否要只返回GRU的最終輸出,args.att_func
預設為bilinear
,所以only_return_final
為False
,即對於文件的encode是使用GRU的正向與反向的隱藏層輸出來構建的;對於問題的encode,即name=‘q’
的stack_rnn
,only_return_final=True
代表問題的encode是使用GRU的正向與反向的最終輸出來構建的。
原始碼為:
l_in1 = lasagne.layers.InputLayer((None, None), in_x1)
l_mask1 = lasagne.layers.InputLayer((None, None), in_mask1)
l_emb1 = lasagne.layers.EmbeddingLayer(l_in1, args.vocab_size, args.embedding_size, W=embeddings)
l_in2 = lasagne.layers.InputLayer((None, None), in_x2)
l_mask2 = lasagne.layers.InputLayer((None, None), in_mask2)
l_emb2 = lasagne.layers.EmbeddingLayer(l_in2, args.vocab_size, args.embedding_size, W=l_emb1.W)
network1 = nn_layers.stack_rnn(l_emb1, l_mask1, args.num_layers, args.hidden_size,
grad_clipping=args.grad_clipping,
dropout_rate=args.dropout_rate,
only_return_final=(args.att_func == 'last'),
bidir=args.bidir,
name='d',
rnn_layer=args.rnn_layer)
network2 = nn_layers.stack_rnn(l_emb2, l_mask2, args.num_layers, args.hidden_size,
grad_clipping=args.grad_clipping,
dropout_rate=args.dropout_rate,
only_return_final=True,
bidir=args.bidir,
name='q',
rnn_layer=args.rnn_layer)
stack_rnn
封裝了lasagne.layers.GRULayer
,對外使用stack_rnn
來構建雙向GRU。
def stack_rnn(l_emb, l_mask, num_layers, num_units,
grad_clipping=10, dropout_rate=0.,
bidir=True,
only_return_final=False,
name='',
rnn_layer=lasagne.layers.LSTMLayer):
def _rnn(backwards=True, name=''):
network = l_emb
for layer in range(num_layers):
if dropout_rate > 0:
network = lasagne.layers.DropoutLayer(network, p=dropout_rate)
c_only_return_final = only_return_final and (layer == num_layers - 1)
network = rnn_layer(network, num_units,
grad_clipping=grad_clipping,
mask_input=l_mask,
only_return_final=c_only_return_final,
backwards=backwards,
name=name + '_layer' + str(layer + 1))
return network
network = _rnn(True, name)
if bidir:
network = lasagne.layers.ConcatLayer([network, _rnn(False, name + '_back')], axis=-1)
return network
5 Attention層 從Encode層得到文件的上下文表示network1
以及問題的上下文表示network2
,使用一個雙線性函式nn_layers.BilinearAttentionLayer
作為network1
和network2
的匹配函式。
att = nn_layers.BilinearAttentionLayer([network1, network2], args.rnn_output_size, mask_input=l_mask1)
核心計算程式碼為:
M = T.dot(inputs[1], self.W).dimshuffle(0, 'x', 1)
alpha = T.nnet.softmax(T.sum(inputs[0] * M, axis=2))
if len(inputs) == 3:
alpha = alpha * inputs[2]
alpha = alpha / alpha.sum(axis=1).reshape((alpha.shape[0], 1))
return T.sum(inputs[0] * alpha.dimshuffle(0, 1, 'x'), axis=1)
6 全連線層Dense network = lasagne.layers.DenseLayer(att, args.num_labels, nonlinearity=lasagne.nonlinearities.softmax)
其中的att
為雙線性函式的輸出,args.num_labels
為資料集中各類@entity
,可參考單詞字典build_dict
,使用非線性啟用函式softmax
,得到每個@entity
的為答案的機率。
模型使用SGD最佳化函式: updates = lasagne.updates.sgd(loss, params, args.learning_rate)
損失函式:lasagne.objectives.categorical_crossentropy(train_prediction, in_y)
,計算正確答案和預測答案的交叉熵。
ImportError: cannot import name downsample
解決方法:
這主要是因為你安裝的時候直接使用了pip install lasagne
,這樣安裝的是lasagneV0.1
,而這個模型需要的是lasagneV0.2
,檢視官網發現需使用pip install --upgrade 來安裝lasagneV0.2
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2249/viewspace-2801164/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 機器閱讀理解Match-LSTM模型模型
- 機器閱讀理解Attention-over-Attention模型模型
- iCHM Reader for Mac(chm閱讀器)Mac
- 【原始碼閱讀】AndPermission原始碼閱讀原始碼
- Blazor Pdf Reader PDF閱讀器 元件 更新Blazor元件
- Vue原始碼閱讀--過濾器Vue原始碼過濾器
- PDF Reader Pro for Mac專業PDF閱讀器Mac
- pdf編輯閱讀器PDF Reader Pro for MacMac
- chm格式檔案閱讀器:iCHM Reader for MacMac
- iCHM Reader for Mac chm格式檔案閱讀器Mac
- GM Reader Pro for Mac(電子書閱讀器)Mac
- 【原始碼閱讀】Glide原始碼閱讀之with方法(一)原始碼IDE
- 【原始碼閱讀】Glide原始碼閱讀之into方法(三)原始碼IDE
- jQuery原始碼閱讀(九)---ready函式理解jQuery原始碼函式
- PDF Reader Pro Lite for Mac專業PDF閱讀器Mac
- 「pdf檔案閱讀器」PDF Reader Pro 2.8.19.1
- 用Keras搞一個閱讀理解機器人Keras機器人
- ReactorKit原始碼閱讀React原始碼
- AQS原始碼閱讀AQS原始碼
- CountDownLatch原始碼閱讀CountDownLatch原始碼
- HashMap 原始碼閱讀HashMap原始碼
- delta原始碼閱讀原始碼
- 原始碼閱讀-HashMap原始碼HashMap
- NGINX原始碼閱讀Nginx原始碼
- Mux 原始碼閱讀UX原始碼
- HashMap原始碼閱讀HashMap原始碼
- fuzz原始碼閱讀原始碼
- RunLoop 原始碼閱讀OOP原始碼
- express 原始碼閱讀Express原始碼
- muduo原始碼閱讀原始碼
- stack原始碼閱讀原始碼
- HttpRunner3原始碼閱讀:2. 模型定義HTTP原始碼模型
- 完全解析!Bert & Transformer 閱讀理解原始碼詳解ORM原始碼
- TiCDC 原始碼閱讀(四)TiCDC Scheduler 工作原理解析原始碼
- 【原始碼閱讀】Glide原始碼閱讀之load方法(二)原始碼IDE
- PostgreSQL 原始碼解讀(3)- 如何閱讀原始碼SQL原始碼
- python3 原始碼閱讀-虛擬機器執行原理Python原始碼虛擬機
- PDF Reader Pro for mac(pdf閱讀器)2.9.9.0啟用版Mac