---
# System prepended metadata

title: NLP Final Project (Pun Location)

---

# NLP Final Project (Pun Location)


> [王軒,0816095] (劉宗函/0816171) <陳威達;0816035> {王朋睿 0816001}

> Kaggle隊名：Hack UD (第2組)

## I. Your model design and concept
### Supervised:

&emsp;&emsp;我們把句子輸入BERT得到Contextual Embedding後，再將token的Contextual Embedding序列傳入一個Multihead Attention Layer進行注意力強化，最後再透過一個Fully Connected Layer輸出預測的標籤，並由標籤對應的BERT token對應回所屬的XML token進行預測。

![](https://i.imgur.com/H709MLl.png)

![](https://i.imgur.com/QZOTTyO.png)

而我們實作了兩種標籤的方法：
1. 每個token都給予一個標籤(e.g. pun前面的字、pun、pun後面的字、padding)。此時如果有多個token都是屬於pun，我們會選擇其中pun機率最高的token作為最後的答案。[參考此篇[paper](https://zoezou2015.github.io/papers/pun2019.pdf)]
2. pun在句子中的位置。在這個模型中，因為預測的是pun落在各個位置的機率，所以我們可以用一個遮罩(Error Analysis中補述)讓PAD的token的預測機率趨近於0。最高機率的位置即為預測標籤。

### Unsupervised:
#### I. Lesk Algorithm
&emsp;&emsp;Lesk Algorithm可以算出一個字在句子中最可能的sense，由於pun通常具有兩個以上符合文意的意思，因此這個字在句子中前兩名機率最高的sense機率應該是接近的。因此實作一個能夠算出每個字在句子中的最可能的兩個sense之間機率差的lesk。最後取機率差最小的字當作pun.

#### II. Disambiguation with SemCor and BERT

&emsp;&emsp;核心概念是使用semcor去算出一個字的各個意思的embedding，對於任一個句子的一個字，計算出他的embedding和各個意思的embedding的距離，來判斷這個字在這個句子裡面是什麼意思，如果是Pun的話他距離可能性最高的兩個意思應該要差不多遠。

&emsp;&emsp;對於任一個字W，在semcor裡面找出所有包含W的句子，並依照標記的意思進行分類，之後把句子們丟到BERT裡面計算出W在句子裡的contextualized embedding，並把同樣意思的句子中W的 embedding 平均起來，當作W的這個意思的 vector。
之後也使用BERT算出 Test Sentence 中每個 token 的embedding V，並從 token 的所有意思的vector之中找出和V的cosine similarity最大的兩個，計算兩者similarity的差，選擇整句中similarity差最小的token當作pun。


## II. What kind of word sense representation used and experimented in your model

1. Disambiguation with SemCor and BERT
   如上所述，使用 BERT 計算 contextualized word embedding，把 Semcor 中屬於同一意思的 embedding 平均起來作為該 sense 的 reference embedding，計算 candidate 的 embedding 和所有 reference embedding 的距離，表示最可能的意思。
2. Lesk Algorithm
   用 sense glosses 和 word 的 context 的 overlap 比例當作 word sense representation.
## III. What problem did you face during the homework and how you solved

1. 有時候 model 判斷的 pun 會出現在不合理的位置，例如標點符號或 padding symbol，此時我們就會取第二高機率的token做為答案。
2. BERT tokenizer 會將單字拆成 subword ，例如 "unwanted" 會分解成 [“un”, “##want”, “##ed”]。因此在做句子的標註時，經過 tokenization 的句子長度無法和 label 長度進行對應，model 輸出的結果也可能會是單字的其中一個 subword。而我們的解決方法是在 tokenization 後建立一個 map ，將每個 subword 對應回原本的單字，方便後續的轉換。
3. 類似問題2，BERT tokenizer拆分句子的方式和題目裡的 Token 不一樣，給任意 token 的 index，另一個解決方法是把該 index 之前的所有 token 的長度加起來，再一一把 BERT token decode，直到算出來的長度相等，就可以把 index 對應到 BERT token 的 index
4. 因為要把詞性納入考量，所以使用了 spaCy 去做分析，但是 spaCy 只能傳入一個字串，他會自己重新 tokenize，為了要對上題目的 token，也用了和問題3.同樣的方法，透過計算長度來找到對應的spaCy Token。
5. 為了能快速找到SemCor中包含任意字的句子，我們先對SemCor做處理，整理出一個dictionary，能用(字,詞性)當key，找到該字的各個意思和他們的例句
6. SemCor中的例句很多，就拿"be"來說，就有一萬多個例句，每次都要重複計算會消耗大量時間，所以在過程中會把senses的embedding們存起來，之後使用(word, pos)的方式去存取他每個sense的reference embedding

## IV. Error Analysis and Discussion

### Supervised NN Model

&emsp;&emsp;我們的嘗試有些是對兩種labelling都有效，有些則是個別有差異，所以以下分開討論。

#### Global Changes

* 4 Hidden States v.s. Last Hidden State
&emsp;&emsp;BERT中使用了最後四層hidden_state進行output layer的training，我們仿效這個作法，得到的正確率從原本只用last_hidden_state的79%提升到84%。
* Crop Data
&emsp;&emsp;由於testing set的句子都不超過35個XML token，training set 超過30個token的句子也不到20句(共1200句)，我們就從training set中捨棄了過長的句子，避免因為少數一兩個長句子讓其他句子後面多了大量不必要的padding。
* BERT v.s. DistilBERT
&emsp;&emsp;使用QA-based的pretrained DistilBERT，原本預期相較於一般化的BERT更能在預測pun上有更好的表現，但不同設定下僅改變model的成效各有好壞。
* MultiHead Attention Dropout
&emsp;&emsp;我們發現對同個spec的model調整模型時，Attention Layer的Dropout對結果的影響不顯著，可能跟模型不容易收斂有關。
* 標點符號
&emsp;&emsp;去除標點符號後的contextual embedding效果不如原句來得好，

#### Labelling Type 1: State
* 4 State v.s. 2 State
&emsp;&emsp;狀態預測模型中，我們嘗試了四個state和兩個state的label，前者有B(Before Pun)，P(Pun)，A(After Pun), X(Not In Sentence)四種狀態，後者只有pun和非pun兩種狀態。
* Unweighted Loss v.s. Focal Loss v.s. Class Weight
&emsp;&emsp;由於狀態的數目十分的不均等(P < A << B <<<< X)，所以模型很容易全部猜同一類就得到不錯但假性的準確率。如果我們用training set的樣本比例倒數作為類別權重，可以將屬於少數的標籤分類錯誤時有更大的成本(loss)；Focal Loss也可以拿來增加hard-example(pun)的權重

#### Labelling Type 2: Location

* Masking
&emsp;&emsp;作為位置預測模型，如果預測的標籤落在padding區肯定是不理想的結果，因此我們嘗試將token的attention_mask拿來影響模型輸出進入Softmax前壓低這些位置的輸出值。我們嘗試了以下的三種作法：
    * 乘法 (最佳準確率 88.16%)
&emsp;&emsp;PAD項對應的輸出值乘上一負數，後來發現有可能會負負得正，不能取用過大的負值。
    * 神經網路 (最佳準確率 87.85%)
&emsp;&emsp;用神經網路決定是否取用各個attention。
    * 加法 (最佳準確率 85.66%)
&emsp;&emsp;PAD項對應的輸出值減去一正整數。後來發現如果扣減的值太大會讓模型過度懲罰猜到PAD，但在句子內猜錯的懲罰會相對過輕。

#### Overall Performance Table

|Type|Backbone|Last N<br>Hidden<br> States|Other Specs|Result|
|:-:|:-:|:-:|-|:-:|
|State|BERT|1||79.75%|
|State|BERT|4||84.42%|
|State|BERT|4|無標點符號|76.63%|
|State|BERT|4||79.75%|
|State|BERT|4|無標點符號|76.63%|
|State|DistilBERT|4|無標點符號|77.88%|
|Location|BERT|4||85.04%|
|Location|DistilBERT|4||84.42%|
|Location|DistilBERT|4|無mask|84.42%|
|Location|DistilBERT|4|減法mask|85.66%|
|Location|DistilBERT|4|神經網路mask|87.85%|
|Location|DistilBERT|4|乘法mask|<font color="#080">88.16%</font>|

### Lesk Algorithm

| overlap threshold | accuracy of data over threshold |   overall accuracy  |
|:------------------:|:--------:|:---:|
|         20         |  0.1872  |  0.3457   |
|         30         |  0.1676  |  0.3862   |
|         40         |  0.1774  |  0.4174   |
|         50         |  0.1656  |  0.4330   |
|         60         |  0.1868  |  0.4423   |

&emsp;&emsp;accuracy of data over threshold 是有過 threshold 的 data 的 accuracy. Performance 實在不怎麼好，但比亂猜還好證明還是有一點效果的。 Overall accuracy 就是整個data set 的 accuracy. 上升主要是因為 threshold 越高就越少 data 是用 lesk 判斷，越多是猜最後一個字，而全猜最後一個字的 accuracy 大約落在 0.44。

### Disambiguation with SemCor and BERT

##### Metrics

&emsp;&emsp;由於 Bert Tokenizer 會將一個 word 拆成好幾段，所以我們在計算一個 word 的 embedding 測試了三種方法。

以 [“un”, “##want”, “##ed”] 為例
1. 選擇最後一個 token 當代表 ( 選 "##ed" )
2. 把組成一個 word 的所有 token 的 embedding 平均
3. 第一個 token 當代表 ( 選 "un" )

如何計算 embedding 和 reference embedding 的距離也有可以調整的空間，我們測試了
1. embedding 相減之後計算 L2-norm
2. Cosine Similarity (計算兩 embedding 夾角的 cosine)

| Variation | accuracy  |
|:------------------:|:--------:|
|Last Token Cosine | 0.1869 |
|Last Token L2 Norm | 0.1962 |
|Mean Cosine | 0.1962 |
|Mean L2 Norm| 0.190031 |
|First Token Cosine | 0.2024 |
|First Token L2 Norm | 0.190031 | 

&emsp;&emsp;因為 validation 資料不是很多，這一點差異幾乎可以說是微乎其微，但可以看出選擇第一個 token + consine similarity 的效果是最好的。

##### Part of Speech

&emsp;&emsp;SemCor 中意思的 Label 是有區分詞性的，總共有 { "n", "v", "a", "s", "r" } 分別對應到 Noun、Verb、Adjective、Adjective Satellite、Adverb。上面的作法在會把 SemCor 中不同詞性的意思都拿進去計算，例如說 "run" 這個字可以是同時是名詞和動詞，我們在做 Disambiguation 的時候不會去區分他的詞性，把名詞和動詞都拿去考慮。我們認為同一詞不同詞性的字可能有類似的意思、可能會造成一些誤判，所以我們透過 spaCy 去判斷詞性。選擇上面 performance 最好的 First Token 版本來測試。

| Variation | accuracy  |
|:------------------:|:--------:|
|First Token Cosine | 0.2118 |
|First Token L2 Norm | 0.1962 |

&emsp;&emsp;可以看出 Accuracy 些微的進步了一點點，但差異不大。

##### Discussion
&emsp;&emsp;Semcor 裡面意思的例句數量分布相當不均，下圖x軸對應例句數量，y軸對應sense數量(truncated)，可看出多數的Word Sense 例句都不是很多，我們認為使用少少的句子平均出來的 Reference Embedding 沒有辦法良好的表現出 Sense。

<div style="text-align:center">
    <img src="https://i.imgur.com/L44rHie.png" width="" height="300"/>
</div>

## V. Compare and implement unsupervised method and supervised method

### Supervised:
&emsp;&emsp;與unsupervised相比，我們使用的supervised方法除了正確率較高之外，也較少出現無法判斷出任何pun，而必須要隨便猜一個答案的情況，也不需要依賴額外的corpus。但相對的，要建立這個supervised的模型必須耗費更多的時間成本進行訓練。

### Unsupervised:
#### I. Lesk Algorithm
&emsp;&emsp;因為lesk是用sense的glosses和這個字的前後文(整個句子)的overlap數量當作判斷標準，因此當glosses或context不夠時會出現所有senses overlap數量低落的問題，不足以當作判斷標準。且經過實驗發現pun多半落在句子後半部，在這樣的前提下我設了一個threshold，當overlap數夠多才當作判斷基準，不然就使用最後一個字當pun.
&emsp;&emsp;在這樣的threshold下accuracy最高也只有0.46，可見lesk的implementation還有很多可改進的地方。
&emsp;&emsp;因為這個方法是unsupervised，因此不需要事先標註label，也不需要經過training的過程，在修改和判斷時都更快速。
#### II. Disambiguation with SemCor and BERT
&emsp;&emsp;儘管也使用了 BERT 但由於沒有使用 Label，沒有辦法直接拿到Pun的特徵，只能用一些 heuristic 去判斷一個字是不是 Pun，最好的 accuracy 只有 0.21。