2019-CS224n-Assignment3

個人原文:2019-CS224n-Assignment3html

上個禮拜作完了,今天作個總結,主要方法和2017年差很少。python

機器學習和神經網絡 (8分)

這一節沒什麼難度,認真看 a3.pdf 就行。git

Adam的論文:ADAM: A METHOD FOR STOCHASTIC OPTIMIZATIONweb

Dropout論文:Dropout: A Simple Way to Prevent Neural Networks from Overfitting網絡

基於神經Transition的依賴解析 (42分)

依賴解析,就是分析句子的句法結構,創建 head 詞和修飾這些head的詞之間的關係。此次構建的是 transition-based 解析器,它增量的,每一次只進行一步解析動做來生成依賴關係,每一步解析稱爲 partial parse,可表示爲:app

  • 一個 stack ,已被處理的詞
  • 一個 buffer ,待處理的詞
  • 一個 dependencies ,解析器生成的依賴

初始狀態下,stack裏有隻 ROOT 一個詞,在每一次解析中,運行 transition 操做,分爲三個類型:機器學習

  • SHIFT:將buffer的左邊(頭部)第一個詞取出,放到stack的右邊(尾部)
  • LEFT-ARC:將stack的右第二個詞做爲依賴項,它依賴於右邊第一個詞,生成一個依賴關係,並刪除右第二個詞。
  • RIGHT-ARC:將stack的右第一個詞做爲依賴項,它依賴於右邊第二個詞,生成一個依賴關係,並刪除右第一個詞。

當buffer長度爲0,stack長度爲1(只有ROOT)時就算解析完畢了。函數

mark

上圖是初始操做+三步解析動做的示意圖。學習

若A依賴於B,則B爲 head ,A爲 dependent,記爲 B \rightarrow Aspa

幾個問題:

問題(b) 6分

長度爲n的句子,通過多少步後能夠被解析完(用n表示)?簡要解析爲何

答:要使buffer長度爲0,則須要n步,使stack長度爲1,也須要n步,因此通過2n步後解析完畢。

問題(c) 6分

完成parser_trainsitions.py

init

初始化函數

self.stack = ['ROOT']
self.buffer = self.sentence.copy()
self.dependencies = []
複製代碼

parse_step

注意,stack的棧頂是list的右邊,buffer隊頭是list的左邊

if transition == 'S':
    self.stack.append(self.buffer[0])
    self.buffer = self.buffer[1:]
elif transition == 'LA':
    self.dependencies.append((self.stack[-1], self.stack[-2]))
    self.stack[-2:] = self.stack[-1:]
elif transition == 'RA':
    self.dependencies.append((self.stack[-2], self.stack[-1]))
    self.stack.pop()
else:
    raise Exception('Unknown transition %s' % transition)
複製代碼

minibatch_parse

sentences含多個句子,每一個句子都有一個partial parse對象。因此每一次取出一個batch的parse來進行一次transition操做,同時要過濾掉已經完成的parse。

partial_parses = [PartialParse(s) for s in sentences]

unfinished_parses = partial_parses.copy()
while len(unfinished_parses) > 0:
    batch_parses = unfinished_parses[:batch_size].copy()
    transition = model.predict(batch_parses)
    for i, parse in enumerate(batch_parses):
        parse.parse_step(transition[i])
        if len(parse.stack) == 1 and len(parse.buffer) == 0:
            unfinished_parses.remove(parse)

dependencies = [parse.dependencies for parse in partial_parses]
複製代碼

問題(e) 10分

完成 parser_model.py

實質上就是搭建一個三層的前饋神經網絡,用ReLU作激活函數,最後一層用softmax輸出,交叉熵作損失函數,同時還加了embedding層

init

初始化三個層,n_features 表示每個詞用幾個特徵來表示,每個特徵都要embed,因此輸入層的大小是 n_features * embed_size

# Input Layer
self.embed_to_hidden = nn.Linear(self.n_features*self.embed_size, self.hidden_size)
nn.init.xavier_uniform_(self.embed_to_hidden.weight, gain=1)

# Dropout Layer
self.dropout = nn.Dropout(self.dropout_prob)

# Output Layer
self.hidden_to_logits = nn.Linear(self.hidden_size, self.n_classes)
nn.init.xavier_uniform_(self.hidden_to_logits.weight, gain=1)
複製代碼

embedding_lookup

使用的是預訓練的embedding(Collobert at. 2011)

x = self.pretrained_embeddings(t)
x = x.view(x.shape[0], x.shape[1]*x.shape[2])
複製代碼

forward

提取特徵、輸入網絡拿到節點,這裏沒用加softmax層是由於 torch.nn.CrossEntropyLoss 會內部幫咱們加

a = self.embedding_lookup(t)
h = self.embed_to_hidden(a)
h = F.relu(h)
h = self.dropout(h)
logits = self.hidden_to_logits(h)
複製代碼

接着完成run.py

train

optimizer = optim.Adam(parser.model.parameters(), lr=lr)
loss_func = nn.CrossEntropyLoss()
複製代碼

train_for_epoch

logits = parser.model(train_x)
loss = loss_func(logits, train_y)

loss.backward()
optimizer.step()
複製代碼

參考資料

[1] CS224n: Natural Language Processing with Deep Learning, 2019-03-21.

[2] CS224n Assignment 2, 2019-03-21.

本站公眾號
   歡迎關注本站公眾號,獲取更多信息