適用於稀疏的嵌入、獨熱編碼數據的損失函數回顧和PyTorch實現


在稀疏的、獨熱編碼編碼數據上構建自動編碼器微信

自1986年[1]問世以來,在過去的30年裏,通用自動編碼器神經網絡已經滲透到現代機器學習的大多數主要領域的研究中。在嵌入複雜數據方面,自動編碼器已經被證實是很是有效的,它提供了簡單的方法來將複雜的非線性依賴編碼爲平凡的向量表示。可是,儘管它們的有效性已經在許多方面獲得了證實,但它們在重現稀疏數據方面經常存在不足,特別是當列像一個熱編碼那樣相互關聯時。
網絡

在本文中,我將簡要地討論一種熱編碼(OHE)數據和通常的自動編碼器。而後,我將介紹使用在一個熱門編碼數據上受過訓練的自動編碼器所帶來的問題的用例。最後,我將深刻討論稀疏OHE數據重構的問題,而後介紹我發如今這些條件下運行良好的3個損失函數:app

  • CosineEmbeddingLossdom

  • Sorenson-Dice Coefficient Loss機器學習

  • Multi-Task Learning Losses of Individual OHE Componentside

-解決了上述挑戰,包括在PyTorch中實現它們的代碼。函數

熱編碼數據

熱編碼數據是一種最簡單的,但在通常機器學習場景中常常被誤解的數據預處理技術。該過程將具備「N」不一樣類別的分類數據二值化爲二進制0和1的N列。第N個類別中出現1表示該觀察屬於該類別。這個過程在Python中很簡單,使用Scikit-Learn OneHotEncoder模塊:學習

 from sklearn.preprocessing import OneHotEncoder
 import numpy as np# Instantiate a column of 10 random integers from 5 classes
 x = np.random.randint(5, size=10).reshape(-1,1)print(x)
 >>> [[2][3][2][2][1][1][4][1][0][4]]# Instantiate OHE() + Fit/Transform the data
 ohe_encoder = OneHotEncoder(categories="auto")
 encoded = ohe_encoder.fit_transform(x).todense()print(encoded)
 >>> matrix([[0., 1., 0., 0., 0.],
            [0., 0., 0., 1., 0.],
            [0., 0., 1., 0., 0.],
            [0., 0., 0., 1., 0.],
            [0., 0., 1., 0., 0.],
            [1., 0., 0., 0., 0.],
            [0., 0., 1., 0., 0.],
            [0., 0., 1., 0., 0.],
            [0., 0., 0., 1., 0.],
            [0., 0., 0., 0., 1.]])print(list(ohe_encoder.get_feature_names()))
 >>> ["x0_0", "x0_1", "x0_2", "x0_3", "x0_4"]

可是,儘管這個技巧很簡單,但若是不當心,它可能很快就會失效。它能夠很容易地爲數據添加多餘的複雜性,並改變數據上某些分類方法的有效性。例如,轉換成OHE向量的列如今是相互依賴的,這種交互使得在某些類型的分類器中有效地表示數據方面變得困難。例如,若是您有一個包含15個不一樣類別的列,那麼就須要一個深度爲15的決策樹來處理該熱編碼列中的if-then模式(固然樹形模型的數據處理是不須要進行獨熱編碼的,這裏只是舉例)。相似地,因爲列是相互依賴的,若是使用bagging (Bootstrap聚合)的分類策略並執行特性採樣,則可能會徹底錯過單次編碼的列,或者只考慮它的部分組件類。編碼

Autoencoders

自動編碼器是一種無監督的神經網絡,其工做是將數據嵌入到一種有效的壓縮格式。它利用編碼和解碼過程將數據編碼爲更小的格式,而後再將更小的格式解碼爲原始的輸入表示。利用模型重構(譯碼)與原始數據之間的損失對模型進行訓練。url

實際上,用代碼表示這個網絡也很容易。咱們從兩個函數開始:編碼器模型和解碼器模型。這兩個「模型」都被封裝在一個叫作Network的類中,它將包含咱們的培訓和評估的整個系統。最後,咱們定義了一個Forward函數,PyTorch將它用做進入網絡的入口,用於包裝數據的編碼和解碼。

 import torch
 import torch.nn as nn
 import torch.nn.functional as F
 import torch.optim as optimclass Network(nn.Module):
    def __init__(self, input_shape: int):
      super().__init__()
      self.encode1 = nn.Linear(input_shape, 500)
      self.encode2 = nn.Linear(500, 250)
      self.encode3 = nn.Linear(250, 50)
       
      self.decode1 = nn.Linear(50, 250)
      self.decode2 = nn.Linear(250, 500)
      self.decode3 = nn.Linear(500, input_shape)   def encode(self, x: torch.Tensor):
      x = F.relu(self.encode1(x))
      x = F.relu(self.encode2(x))
      x = F.relu(self.encode3(x))
      return x   def decode(self, x: torch.Tensor):
      x = F.relu(self.decode1(x))
      x = F.relu(self.decode2(x))
      x = F.relu(self.decode3(x))
      return x   def forward(self, x: torch.Tensor):
      x = encode(x)
      x = decode(x)
      return x
 def train_model(data: pd.DataFrame):
    net = Network()
    optimizer = optim.Adagrad(net.parameters(), lr=1e-3, weight_decay=1e-4)
    losses = []   for epoch in range(250):
      for batch in get_batches(data)
        net.zero_grad()
         
        # Pass batch through
        output = net(batch)
         
        # Get Loss + Backprop
        loss = loss_fn(output, batch).sum() #
        losses.append(loss)
        loss.backward()
        optimizer.step()
      return net, losses

正如咱們在上面看到的,咱們有一個編碼函數,它從輸入數據的形狀開始,而後隨着它向下傳播到形狀爲50而下降它的維數。從那裏,解碼層接受嵌入,而後將其擴展回原來的形狀。在訓練中,咱們從譯碼器中取出重構的結果,並取出重構與原始輸入的損失。

損失函數的問題

因此如今咱們已經討論了自動編碼器的結構和一個熱編碼過程,咱們終於能夠討論與使用一個熱編碼在自動編碼器相關的問題,以及如何解決這個問題。當一個自動編碼器比較重建到原始輸入數據,必須有一些估值之間的距離提出重建和真實的價值。一般,在輸出值被認爲互不相干的狀況下,將使用交叉熵損失或MSE損失。但在咱們的一個熱編碼的狀況下,有幾個問題,使系統更復雜:

  • 一列出現1意味着對應的OHE列必須有一個0。即列不是不相交的

  • OHE向量輸入的稀疏性會致使系統選擇簡單地將大多數列返回0以減小偏差

這些問題結合起來致使上述兩個損失(MSE,交叉熵)在重構稀疏OHE數據時無效。下面我將介紹三種損失,它們提供了一個解決方案,或上述問題,並在PyTorch實現它們的代碼:

餘弦嵌入損失

餘弦距離是一種經典的向量距離度量,經常使用於NLP問題中比較字包表示。經過求兩個向量之間的餘弦來計算距離,計算方法爲:

因爲該方法可以考慮到各列中二進制值的誤差來評估兩個向量之間的距離,所以在稀疏嵌入重構中,該方法可以很好地量化偏差。這種損失是迄今爲止在PyTorch中最容易實現的,由於它在 Torch.nn.CosineEmbeddingLoss中有一個預先構建的解決方案

 loss_function = torch.nn.CosineEmbeddingLoss(reduction='none')# . . . Then during training . . . loss = loss_function(reconstructed, input_data).sum()
 loss.backward()

Dice Loss

Dice Loss是一個實現Sørensen-Dice係數[2],這是很是受歡迎的計算機視覺領域的分割任務。簡單地說,它是兩個集合之間重疊的度量,而且與兩個向量之間的Jaccard距離有關。骰子係數對向量中列值的差別高度敏感,利用這種敏感性有效地區分圖像中像素的邊緣,所以在圖像分割中很是流行。Dice Loss爲:

PyTorch沒有內部實現的Dice Loss。可是在Kaggle上能夠在其丟失函數庫- Keras & PyTorch[3]中找到一個很好的實現:

 class DiceLoss(nn.Module):
    def __init__(self, weight=None, size_average=True):
        super(DiceLoss, self).__init__()
 
    def forward(self, inputs, targets, smooth=1):
         
        #comment out if your model contains a sigmoid acitvation
        inputs = F.sigmoid(inputs)      
         
        #flatten label and prediction tensors
        inputs = inputs.view(-1)
        targets = targets.view(-1)
         
        intersection = (inputs * targets).sum()                            
        dice = (2.*intersection + smooth)/
                (inputs.sum() + targets.sum() + smooth)  
         
        return 1 - dice

不一樣OHE列的單個損失函數

最後,您能夠將每一個熱編碼列視爲其自身的分類問題,並承擔每一個分類的損失。這是一個多任務學習問題的用例,其中autoencoder正在解決重構輸入向量的各個份量的問題。當你有幾個/全部的列在你的輸入數據時,這個工做最好。例如,若是您有一個編碼列,前7列是7個類別:您能夠將其視爲一個多類分類問題,並將損失做爲子問題的交叉熵損失。而後,您能夠將子問題的損失合併在一塊兒,並將其做爲整個批的損失向後傳遞。

下面您將看到這個過程的示例,其中示例有三個熱編碼的列,每一個列有50個類別。

 from torch.nn.modules import _Loss
 from torch import argmaxclass CustomLoss(_Loss):
  def __init__(self):
    super(CustomLoss, self).__init__() def forward(self, input, target):
    """ loss function called at runtime """
   
    # Class 1 - Indices [0:50]
    class_1_loss = F.nll_loss(
        F.log_softmax(input[:, 0:50], dim=1),
        argmax(target[:, 0:50])
    )   # Class 2 - Indices [50:100]
    class_2_loss = F.nll_loss(
        F.log_softmax(input[:, 50:100], dim=1),
        argmax(target[:, 50:100])
    )   # Class 3 - Indices [100:150]
    class_3_loss = F.nll_loss(
        F.log_softmax(input[:, 100:150], dim=1),
        argmax(target[:, 100:150])
    )   return class_1_loss + class_2_loss + class_3_loss

在上面的代碼中,您能夠看到重構輸出的子集是如何承受個體損失的,而後在最後將其合併爲一個總和。這裏咱們使用了一個負對數似然損失(nll_loss),它是一個很好的損失函數用於多類分類方案,並與交叉熵損失有關。

總結

在本文中,咱們瀏覽了一個獨熱編碼分類變量的概念,以及自動編碼器的通常結構和目標。咱們討論了一個熱編碼向量的缺點,以及在嘗試訓練稀疏的、一個獨熱編碼數據的自編碼器模型時的主要問題。最後,咱們討論瞭解決稀疏一熱編碼問題的3個損失函數。訓練這些網絡並無更好或更壞的損失,在我所介紹的功能中,沒有辦法知道哪一個是適合您的用例的,除非您嘗試它們!

下面我提供了一些深刻討論上述主題的資源,以及一些我提供的關於丟失函數的資源。

資源

  1. D.E. Rumelhart, G.E. Hinton, and R.J. Williams, 「Learning internal  representations by error propagation.」 Parallel Distributed Processing.  Vol 1: Foundations. MIT Press, Cambridge, MA, 1986.

  2. Sørensen, T. (1948). 「A method of establishing groups of equal amplitude in plant sociology based on similarity of species and its application to  analyses of the vegetation on Danish commons」. Kongelige Danske Videnskabernes Selskab. 5 (4): 1–34. *AND* Dice, Lee R. (1945). 「Measures of the Amount of Ecologic Association Between Species」. Ecology. 26 (3): 297–302.

  3. Kaggle's Loss Function Library: https://www.kaggle.com/bigironsphere/loss-function-library-keras-pytorch


做者:Nick Hespe


deephub翻譯組



本文分享自微信公衆號 - DeepHub IMBA(deephub-imba)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索