【推荐算法】逻辑回归(Logistic Regression,LR)

逻辑回归(Logistic Regression,LR)在推荐系统发展历史中占非常重要的地位。其优势主要体现在三个方面:

  • 数学含义的支撑:LR是一个广义线性模型(可以简单理解为加了激活函数的线性模型),其假设为因变量服从伯努利分布,而CTR事件可以类比为掷偏心硬币的问题,所以使用LR作为CTR预估模型是满足其物理意义的;
  • 可解释性强:在LR中,每一个特征对应一个权重,权重绝对值的大小可以作为评估该特征重要性的指标。每一个权重都可解释,这是任何深度模型都不具备的;
  • 工程化需要:LR有易于并行化(不同特征在不同机器上运算)、训练开销小(新增或删减特征时只需要fine-tune,而树模型需要re-train)的特点。

掌握LR的每个细节(如模型具体的输入输出),是理解后续模型(如GBDT-LR、FM)的基础。

算法

与简单的协同过滤相比,LR模型能够利用用户、物品、上下文等多种不同特征,生成更为全面的推荐。

\[\text{LR}(\mathbf{w, x})= w_{0}+\sum_{i=1}^{n} w_{i} x_{i} \]

其中,\(\mathbf{w}\)为权重向量,\(\mathbf{x}\)为特征向量。LR算法细节可以参考【机器学习】逻辑回归的C++实现

模型输入

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.field_dims = 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).field_dims
  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

posted @ 2021-06-30 12:08  tmpUser  阅读(1108)  评论(3编辑  收藏  举报