# NLP Final Project (Pun Location)
> [王軒,0816095] (劉宗函/0816171) <陳威達;0816035> {王朋睿 0816001}
> Kaggle隊名:Hack UD (第2組)
## I. Your model design and concept
### Supervised:
  我們把句子輸入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
  Lesk Algorithm可以算出一個字在句子中最可能的sense,由於pun通常具有兩個以上符合文意的意思,因此這個字在句子中前兩名機率最高的sense機率應該是接近的。因此實作一個能夠算出每個字在句子中的最可能的兩個sense之間機率差的lesk。最後取機率差最小的字當作pun.
#### II. Disambiguation with SemCor and BERT
  核心概念是使用semcor去算出一個字的各個意思的embedding,對於任一個句子的一個字,計算出他的embedding和各個意思的embedding的距離,來判斷這個字在這個句子裡面是什麼意思,如果是Pun的話他距離可能性最高的兩個意思應該要差不多遠。
  對於任一個字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
  我們的嘗試有些是對兩種labelling都有效,有些則是個別有差異,所以以下分開討論。
#### Global Changes
* 4 Hidden States v.s. Last Hidden State
  BERT中使用了最後四層hidden_state進行output layer的training,我們仿效這個作法,得到的正確率從原本只用last_hidden_state的79%提升到84%。
* Crop Data
  由於testing set的句子都不超過35個XML token,training set 超過30個token的句子也不到20句(共1200句),我們就從training set中捨棄了過長的句子,避免因為少數一兩個長句子讓其他句子後面多了大量不必要的padding。
* BERT v.s. DistilBERT
  使用QA-based的pretrained DistilBERT,原本預期相較於一般化的BERT更能在預測pun上有更好的表現,但不同設定下僅改變model的成效各有好壞。
* MultiHead Attention Dropout
  我們發現對同個spec的model調整模型時,Attention Layer的Dropout對結果的影響不顯著,可能跟模型不容易收斂有關。
* 標點符號
  去除標點符號後的contextual embedding效果不如原句來得好,
#### Labelling Type 1: State
* 4 State v.s. 2 State
  狀態預測模型中,我們嘗試了四個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
  由於狀態的數目十分的不均等(P < A << B <<<< X),所以模型很容易全部猜同一類就得到不錯但假性的準確率。如果我們用training set的樣本比例倒數作為類別權重,可以將屬於少數的標籤分類錯誤時有更大的成本(loss);Focal Loss也可以拿來增加hard-example(pun)的權重
#### Labelling Type 2: Location
* Masking
  作為位置預測模型,如果預測的標籤落在padding區肯定是不理想的結果,因此我們嘗試將token的attention_mask拿來影響模型輸出進入Softmax前壓低這些位置的輸出值。我們嘗試了以下的三種作法:
* 乘法 (最佳準確率 88.16%)
  PAD項對應的輸出值乘上一負數,後來發現有可能會負負得正,不能取用過大的負值。
* 神經網路 (最佳準確率 87.85%)
  用神經網路決定是否取用各個attention。
* 加法 (最佳準確率 85.66%)
  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 |
  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
  由於 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 |
  因為 validation 資料不是很多,這一點差異幾乎可以說是微乎其微,但可以看出選擇第一個 token + consine similarity 的效果是最好的。
##### Part of Speech
  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 |
  可以看出 Accuracy 些微的進步了一點點,但差異不大。
##### Discussion
  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:
  與unsupervised相比,我們使用的supervised方法除了正確率較高之外,也較少出現無法判斷出任何pun,而必須要隨便猜一個答案的情況,也不需要依賴額外的corpus。但相對的,要建立這個supervised的模型必須耗費更多的時間成本進行訓練。
### Unsupervised:
#### I. Lesk Algorithm
  因為lesk是用sense的glosses和這個字的前後文(整個句子)的overlap數量當作判斷標準,因此當glosses或context不夠時會出現所有senses overlap數量低落的問題,不足以當作判斷標準。且經過實驗發現pun多半落在句子後半部,在這樣的前提下我設了一個threshold,當overlap數夠多才當作判斷基準,不然就使用最後一個字當pun.
  在這樣的threshold下accuracy最高也只有0.46,可見lesk的implementation還有很多可改進的地方。
  因為這個方法是unsupervised,因此不需要事先標註label,也不需要經過training的過程,在修改和判斷時都更快速。
#### II. Disambiguation with SemCor and BERT
  儘管也使用了 BERT 但由於沒有使用 Label,沒有辦法直接拿到Pun的特徵,只能用一些 heuristic 去判斷一個字是不是 Pun,最好的 accuracy 只有 0.21。