2019-CS224n-Assignment3

我的原文:2019-CS224n-Assignment3

上個禮拜做完了,今天做個總結,主要方法和2017年差不多。

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

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

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

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

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

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

  • 一個 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,記爲 BAB \rightarrow A

幾個問題:

問題(b) 6分

長度爲n的句子,經過多少步後可以被解析完(用n表示)?簡要解析爲什麼

答:要使buffer長度爲0,則需要n步,使stack長度爲1,也需要n步,所以經過2n步後解析完畢。

問題© 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.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章