1. 程式人生 > 其它 >【推薦演算法】邏輯迴歸(Logistic Regression,LR)

【推薦演算法】邏輯迴歸(Logistic Regression,LR)

邏輯迴歸(Logistic Regression,LR)在推薦系統發展歷史中佔非常重要的地位。其優勢主要體現在三個方面:

  • 數學含義的支撐:LR是一個廣義線性模型(可以簡單理解為加了啟用函式的線性模型),其假設為因變數服從伯努利分佈,而CTR事件可以類比為擲偏心硬幣的問題,所以使用LR作為CTR預估模型是滿足其物理意義的;
  • 可解釋性強:在LR中,每一個特徵對應一個權重,權重絕對值的大小可以作為評估該特徵重要性的指標。每一個權重都可解釋,這是任何深度模型都不具備的;
  • 工程化需要:LR有易於並行化(不同特徵在不同機器上運算)、訓練開銷小(新增或刪減特徵時只需要fine-tune,而樹模型需要re-train)的特點。

掌握LR的每個細節(如模型具體的輸入輸出),是理解後續模型(如GBDT-LR、FM)的基礎。

模型輸入

LR模型對one-hot型別的輸入非常友好(為什麼?並且為什麼樹模型不適合one-hot輸入)。在pytorch中,我們可以將特徵轉化為one-hot特徵後,使用nn.Linear構建LR,這是最直觀的解決方法。為了簡化輸入,以及充分利用對sparse input的優化(怎麼優化的?),我們使用nn.Embedding代替。

nn.Embedding是一個查詢表(look-up table),輸入為one-hot中為1的特徵的index。因此,我們只需要將所有特徵域拼接起來,輸入為1的特徵對應的index即可。具體拼接方法為:

  1. 獲取每個特徵域包含的特徵個數,根據特徵個數獲取對應offset;
class MovieLens_100K_Dataset(Dataset):
    def __init__(self, df):
        self.token_col = ["user_id", "item_id", "age", "gender", "occupation", "release_year"] # 所用特徵列
        self.df = df[self.token_col]
        self.offset = self.df.nunique().values # 對應步驟1
        self.df = self.df.values
        self.label = df["label"].values

    def __len__(self):
        return self.label.shape[0]

    def __getitem__(self, idx):
        return self.df[idx], self.label[idx]

# 使用全量資料集df來計算每個特徵域包含的特徵個數(field_dims),不能使用split後的資料,否則訓練測試集會不一致
field_dims = MovieLens_100K_Dataset(df).offset
  1. 每個特徵域中的one-hot為1的index加上offset,獲得在拼接向量中one-hot為1的index;
  2. 每個特徵域分別送入nn.Embedding,對結果sum_pooling得到輸出。
class FeaturesLinear(torch.nn.Module):
    def __init__(self, field_dims, output_dim=1):
        super().__init__()
        self.fc = torch.nn.Embedding(sum(field_dims), output_dim)
        self.bias = torch.nn.Parameter(torch.zeros((output_dim,)))
        self.offsets = np.array((0, *np.cumsum(field_dims)[:-1]), dtype=np.long)
        torch.nn.init.xavier_uniform_(self.fc.weight.data)

    def forward(self, x):
        """
        :param x: Long tensor of size ``(batch_size, num_fields)``
        """
        x = x + x.new_tensor(self.offsets).unsqueeze(0) # 對應步驟2
        return torch.sum(self.fc(x), dim=1) + self.bias # 對應步驟3


class LogisticRegressionModel(torch.nn.Module):
    def __init__(self, field_dims):
        super().__init__()
        self.linear = FeaturesLinear(field_dims)

    def forward(self, x):
        return torch.sigmoid(self.linear(x).squeeze(1))

模型效果

設定:
資料集:ml-100k
優化方法:Adam
學習率:0.003

效果:
收斂epoch:92
train logloss: 0.54183
val auc: 0.78256
test auc: 0.78876