Showing posts with label RNN. Show all posts
Showing posts with label RNN. Show all posts

Tuesday, December 29, 2020

實作:簡易版 BERT Transformers Multi-Label Classification

簡介
由 Huggingface 所開發的 Transformers Library,雖然可以用 BERT 做 NLP Multi-Class Classification(每一組數據擁有一個 class),但卻未有支援 Multi-Label Classification(每一組數據,可以有多於一個 class)。這次實作,我會以 Kaggle Jigsaw Toxic Comments 作為例子,透過小改 Transformers 的 Multi-Class Classification 範例,令它可以進行 Multi-Label 分類。

---

改進原理
Transformers 中的 BertForSequenceClassification,是一個基於 BERT 及 Attention Model,然後再使用 CrossEntropyLoss 微調訓練的模型。事實上,它也支援自動訓練:只要你使用期間提供了 label,它內置的 CrossEntropyLoss 就能自動計算並返回 loss 值。

這種「黑箱全包」的做法,雖然令入門開發者,可以在不了解 Loss Function 等概念的情況下上手,但同時,它也帶來了一些限制:由於它使用了 CrossEntropyLoss,它就只能支援一個類別的分類。舉例來講,它只能計算出「 預計矩陣 [0 0 0 1] 與期望值 3(也就是 [0 0 0 1] )的距離為 0 」。它並不能輸入多於一個維度的期望值。

所以,這次實作,我只是簡單地改了一下:不用內置於 BertForSequenceClassification 的 CrossEntropyLoss,改為採用 BCEWithLogitsLoss。由於 BCEWithLogitsLoss 本身就是專為 Multi-Label Classification 而設計的 Loss Function,所以我也懶得仔細查究,直接就拿來用。最後,我們就能把 BertForSequenceClassification 改成支援 Multi-Label Classification 的模型。

至於其他,例如 BertTokenizer 等等,則採用 Pretrained 結果,原封不動。

---

重點代碼
這幾行代碼,就是這次改動的大重點:

在訓練過程中:

# use AdamW
optimizer = optim.AdamW(model.parameters(), lr=lr)

# generate prediction
optimizer.zero_grad()
outputs = model(input_ids, attention_mask=attention_mask)
 
# compute gradients and update weights
loss = nn.BCEWithLogitsLoss(outputs.logits, labels)
loss.backward()
optimizer.step()

解說:

  • 使用 AdamW 做 Optimizer(其實其他都可以,只是我發現 AdamW 比較快)。
  • 使用 BertForSequenceClassification model 的過程中,故意不提供 label,
    這樣,Transformers 就不會偷偷地自己計算 Loss 和 Gradients,必須要自己動手。
  • 使用 BCEWithLogitsLoss 來計算 Loss,然後手動做 Backward Propagation 和 Stepping。

 在驗證測試過程中:

# generate prediction
outputs = model(input_ids, attention_mask=attention_mask)
prob = outputs.logits.sigmoid()

# record processed data count
total += (labels.size(0)*labels.size(1))

解說:

  • 用 model 計算結果後,必須經過一層 sigmoid 計算。
    因為 BCEWithLogitsLoss 入面都有 sigmoid,這樣才會一致。
  • 在計算總準繩率時,不能單一以 labels.size(0)(樣本數)做分母,
    相反,要以總標記數 (樣本數 * 每個樣本內的標記數) 做分母。
    因為:假設 Predict = [ 1 0 0 1 ], Expected = [ 1 0 0 0 ],
    它應該算做 3/4 正確,而非一個錯誤。

---

演示代碼
https://github.com/cmcvista/BERTClassifier/blob/main/BertMultiLabelClassifier.ipynb

---

小記
感謝 Joseph 提示方向。

---

參考

[1] - https://huggingface.co/transformers/training.html

[2] - https://towardsdatascience.com/transformers-for-multilabel-classification-71a1a0daf5e1

[3] - https://medium.com/huggingface/multi-label-text-classification-using-bert-the-mighty-transformer-69714fa3fb3d

Monday, December 7, 2020

實作:利用 PyTorch LSTM 估計未來氣温

簡介
Long Short Term Memory (LSTM) 是一種對時序有記憶的神經網絡。簡單來講,你可以幻想它是一種有短暫及長期記憶的細胞。只要你不斷按順序地訓練它,日子有功,它就會記住事物的先後次序(例如:你每次都用 A-B-C 的順序去訓練它,以後它只要見到 A,就會想起下一個是 B)。

這種神經網絡,對自然語言處理 (Natural Language Processing) 特別有用:因為它能記住句子的先後順序,繼而找出代名詞原本的意思。 例如「小明去了踩單車,他覺得很開心」這一句中,使用 LSTM,系統就能運用短暫記憶,找出「他」所指的主語,是句子開頭的「小明」。

除了 NLP 之外,LSTM 還特別適合用來預測有規律的時序資料 (time-series data)。例如假設每年天氣都是有四季,而且每年氣温都差不多的話,LSTM 就能找出這個潛在的規律,從而推算出將來某月的氣温大約會是多少。還有,通訊系統的訊號、看似沒有規律的雜訊等等,也非常適合用 LSTM 來找出它的潛在規律。

是次實驗,我會使用新加坡的氣温歷史數據,訓練 LSTM,試試到底它能否有效預測將來的温度。不過,由於新加坡沒有四季,而且氣温有可能隨雨量及雲量影響,所以準繩度理應不會太高。但怎樣也好,姑且一試無妨。  

---

幾個重要的變數
是次實驗,我用了這些變數:

  • 原資料的長度為 466 個數據。
  • 我把資料切割成 448 份 [x1 x2 x3 ... x16 x17 x18],然後把期望目標設為 [x19]
    換句話說,我把頭 18 個月的數據進 LSTM,然後期望它會估計到第 19 個月的氣温,
    而這件事會重覆 448 次。
  •  我把 70% 的資料用作訓練,30% 用作驗證。
  • 我使用了 SciKit 的 MinMaxScaler,將所有輸入輸出數據,都以 fit_transform 正規化,
    之後,估算完成後,我再用 inverse_transform 還原。

關於 LSTM 的變數,我設定成:

  • 資料維度 (input_size / features / input_dimension) 為 1,因為氣温是一維的資料。
  • 次序長度 (sequence_len) 為 18,因為我順序放 18 個月的氣温資料。
  • 隱藏層大小 (hidden_size) 為 500,因為我估計會有 500 個特徵值得留意。
  • 層數 (num_layers) 為 3,所以會同時有三個短暫記憶的空間。
  • 我設定了 batch_first=True,所以資料結構是 (batch, sequence, features),
    也就正乎合 for loop DataLoader 時返回來的資料結構,省回轉來轉去的代碼。
    (RNN 全部都有這種選項,因為它的訓練模式與其他相異,
    x1 和 x2 是放進同一個 cell,而非像 linear regression 那種,是放進兩個不同 cells。)

關於訓練的變數:

  • 訓練包大小 (batch size) 為 20, 因為本人的電腦記憶體較少 ,不適宜大包大包訓練。 
  • 我運行了 500 個 epoch,因為我發現次數再多, loss 也沒再變少。
  • 我使用了 Mean Square Error (MSE) 來做 loss function,兩個氣温相差再平方,很直覺。

---

演示代碼

https://github.com/cmcvista/MLHelloWorld/blob/main/LSTMPyTorch/LSTMTemperaturePrediction.ipynb

---

預測結果

紅色為預測結果,藍色為實際結果。

由此可見,單純使用 LSTM 並不太準確。
我估計原因是因為,一來新加坡的氣温變化不大,所以難以準確預測,
二來就是訓練次數不足(或者 optimization strategy 太保守),以至它仍未學足 pattern。

但另一方面,雖然方向估錯,但預測結果的形狀,的確有點像實際數據的形狀。
所以,這也算是一個「有意義的發現」。

---

小結
LSTM 與其他傳統的神經網絡不同,它以 RNN 的形式運作,以致可以認出時間次序。
雖然初步來看,用作預測氣温並不可靠,但用來找出數據的規律,看來也還可以。

---

參考:

[1] - https://stackabuse.com/time-series-prediction-using-lstm-with-pytorch-in-python