推薦系統實踐 0x0e LS-PLM

NoMornings發表於2020-12-10

在之前介紹的幾個模型中,存在這些問題:

  1. LR不能捕捉非線性,只能進行一次的迴歸預測
  2. GBDT+LR雖然能夠產生非線性特徵組合,但是樹模型不適用於超高維稀疏資料
  3. FM利用二階資訊來產生變數之間的相關性,但是無法適應高階組合特徵,高階組合容易爆炸

那麼,下面介紹的LS-PLM模型一定程度上緩解了這個問題。

LS-PLM

LS-PLM是阿里巴巴曾經主流的推薦模型,這一篇文章就來介紹一下LS-PLM模型的內容。LS-PLM可以看做是對LR模型的自然推廣,它採用的是分而治之的策略。先對樣本分片,然後樣本分片中運用邏輯迴歸進行預估。分片的作用是為了能夠讓CTR模型對不同的使用者群體。不同使用場景都具有針對性。先對全量樣本進行聚類,然後在對每個分類實施邏輯迴歸。透漏一下,這裡阿里巴巴使用的分片聚類的經驗值是12。

論文當中LS-PLM的效果與LR模型效果進行對比,如下圖所示。

優勢

LS-PLM存在三個優勢:

  • 端到端的非線性學習能力。通過足夠的劃分割槽域,LS-PLM可以擬合任何複雜的非線性函式,挖掘資料中的非線性模式,節省大量人工處理樣本和特徵工程的過程。
  • 可擴充套件性。與LR模型類似,LS-PLM可以擴充套件到大量樣本和高維特徵。在這之上設計了一個分散式系統,可以在數百臺機器上並行訓練模型。線上產品系統中,每天都會訓練和部署幾十個具有數千萬引數的LS-PLM模型。
  • 稀疏性。模型稀疏性是工業環境下線上服務的一個實際問題。這裡展示了採用L1和L2,1正則器的LS-PLM可以實現良好的稀疏性。使得部署更加輕量級,線上推斷效率也更高。

論文基於directional derivatives(方向導數)和quasi-Newton(擬牛頓)方法來解決因為正則項使得目標函式非凸的問題。

數學形式

LS-PLM的數學形式如下面公式所示

\[f(x)=\sum_{i=1}^{m}\pi_i(x)\eta_i(x) \]

首先用聚類函式\(\pi\)對模型進行分類分片,再用LR模型計算樣本在分片中具體的CTR,然後將兩者相乘之後求和。公式中的\(m\)就是分片數,可以較好地平衡模型的擬合能力和推廣能力。當\(m=1\)時就會退化為普通的邏輯迴歸。

實際上,LS-PLM採用的softma函式x進行分類,sigmoid函式作為迴歸。於是公式變成:

\[f(x)=\sum_{i=1}^{m}\pi_i(x)\eta_i(x)=\sum_{i=1}^{m}\frac{e^{u_ix}}{\sum_{j=1}^{m}e^{u_jx}}\frac{1}{1+e^{-w_ix}} \]

\(\{u_1,...u_m\}\)為聚類函式(分片函式)\(\pi_i\)的引數,\(\{w_1,...w_m\}\)為擬合函式\(\eta_i\)的引數。

優化

由於目標函式中的加入的正則化項\(L_1\),\(L_{2,1}\)都是非平滑函式,所以目標函式也是非平滑的、非凸函式。因為目標函式的負梯度方向並不存在,所以用能夠得到f最小的方向導數的方向b作為負梯度的近似值。這裡的推導比較複雜,可以看一下原來論文,之後我儘可能用通俗易懂的語言補充這裡。

這個\(L_{2,1}\)挺意思的,貼一下它原來的公式:

\[||\Theta||_{2,1}=L_{2,1}=\sum_{i=1}^d\sqrt{\sum_{j=1}^{2m}\theta_{ij}^{2}} \]

程式碼

import torch
import torch.nn as nn
import torch.optim as optim


class LSPLM(nn.Module):
    def __init__(self, m, optimizer, penalty='l2', batch_size=32, epoch=100, learning_rate=0.1, verbose=False):
        super(LSPLM, self).__init__()
        self.m = m
        self.optimizer = optimizer
        self.batch_size = batch_size
        self.epoch = epoch
        self.verbose = verbose
        self.learning_rate = learning_rate
        self.penalty = penalty

        self.softmax = None
        self.logistic = None

        self.loss_fn = nn.BCELoss(reduction='mean')

    def fit(self, X, y):
        if self.softmax is None and self.logistic is None:
            self.softmax = nn.Sequential(
                nn.Linear(X.shape[1], self.m).double(),
                nn.Softmax(dim=1).double()
            )

            self.logistic = nn.Sequential(
                nn.Linear(X.shape[1], self.m, bias=True).double()
                , nn.Sigmoid())

            if self.optimizer == 'Adam':
                self.optimizer = optim.Adam(self.parameters(), lr=self.learning_rate)
            elif self.optimizer == 'SGD':
                self.optimizer = optim.SGD(self.parameters(), lr=self.learning_rate, weight_decay=1e-5, momentum=0.1,
                                           nesterov=True)

        # noinspection DuplicatedCode
        for epoch in range(self.epoch):

            start = 0
            end = start + self.batch_size
            while start < X.shape[0]:

                if end >= X.shape[0]:
                    end = X.shape[0]

                X_batch = torch.from_numpy(X[start:end, :])
                y_batch = torch.from_numpy(y[start:end]).reshape(1, end - start)

                y_batch_pred = self.forward(X_batch).reshape(1, end - start)
                loss = self.loss_fn(y_batch_pred, y_batch)
                loss.backward()
                self.optimizer.step()
                start = end
                end += self.batch_size

            if self.verbose and epoch % (self.epoch / 20) == 0:
                print('EPOCH: %d, loss: %f' % (epoch, loss))
        return self

    def forward(self, X):
        logistic_out = self.logistic(X)
        softmax_out = self.softmax(X)
        combine_out = logistic_out.mul(softmax_out)
        return combine_out.sum(1)

    def predict_proba(self, X):
        X = torch.from_numpy(X)
        return self.forward(X)

    def predict(self, X):
        X = torch.from_numpy(X)
        out = self.forward(X)
        out[out >= 0.5] = 1.0
        out[out < 0.5] = 0.0
        return out

參考

Learning Piece-wise Linear Models from Large Scale Data for Ad Click Predict
Github:hailingu/MLFM
LS-PLM學習筆記

相關文章