ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

RS学习1

2022-06-15 00:31:06  阅读:183  来源: 互联网

标签:features RS sum torch 学习 values data self


torch-rechub安装

最新版(推荐)

  1. git clone https://github.com/datawhalechina/torch-rechub.git
  2. cd torch-rechub
  3. python setup.py install
    安装过程中遇到问题:python setup.py install
    image
    执行python3 setup.py install 成功安装

深度学习推荐算法框架

image

FM学习

1、FM

前面一章介绍了线性模型和逻辑回归模型,在这些模型中,默认特征之间是不存在交互关系的;对于离散特征(如用户所在城市、商品品牌等),一般是进行one-hot处理,从而会产生大量的稀疏数据。Factorization Machines(FM)模型即是用来解决数据稀疏和特征交叉使用问题的。

1.1 数据说明

如在电影评分系统中的数据,它的每一条记录都包含了哪个用户\(u \in U\)​在什么时候\(t \in {\Bbb R}\)​对哪部电影\(i \in I\)​大了多少分\(r \in \{1,2,3,4,5\}\)​这样的信息,假定用户集\(U\)​和电影集\(I\)​​分别为:

\(U = \{Alice(A), Bob(B), Charlie(C),...\}\)

\(I=\{Titanic(TI),Notting \ Hill(NH), Star \ Wars(SW), Star \ Trek(ST),...\}\)

设观测到的数据集\(S\)为:

\[\begin{align} S=\{&(A, TI,2010.1,5),(A,NH,2020.2,3), (A,SW,2010.4,1) \\& (B,SW,2009.5,4),(B,ST,2009.8,5) \\ &(C,TI,2009.9,1),(C,SW,2009.12,5) \\& ...\}\end{align} \]

利用观测数据集\(S\)来进行预测任务的一个实例是:估计一个函数\(\hat y\)来预测某个用户在某个时刻对某部电影的打分行为。有了观测数据集\(S\),构造如下样本:
image

特征说明如下:

第一部分(对应蓝色框),表示当前评分用户信息,其维度为\(|U|\),该部分分量中,当前电影评分用户所在的位置为1,其他为0。例如在第一条观测记录中\(x_A^{(1)}=1\)​

第二部分(对应橙色框),表示当前被评分电影信息,其维度为\(|I|\),该部分的分量中,当前被评分的电影所在的位置为1,其他为0。例如在第一条观测记录中就有\(x_{TI}^{(1)}=1\)

第三部分(对应黄色框),表示当前评分用户评分过的电影信息,其维度为\(|I|\),该部分分量重,被当前用户评论过的所有电影的(设个数为\(n_I\))的位置为\(1/n_I\),其他设为0。如Alice评价过的三部电影TI, NH和SW,因此就有\(x_{TI}^{(1)}=x_{NH}^{(1)}=x_{SW}^{(1)}=1/3\)

第四部分(对应绿色框),表示评分日期信息,其维度为1,用来表示用户评价电影的时间,表示方法是将记录中最早的日期作为基数1,以后每增加一个月就加1

第五部分(对应棕色框)表示当前评分用户评分过的一步电影的信息,其维度为\(|I|\),该部分的分量中,若当前用户评价当前电影之前还评价过其他电影,则将当前用户评价的上一部电影的取值为1,其他为0;若当前用户评价电影之前没有评价过其他电影,则所有分量取值为0。如对于第二条观测记录,Alice评价NH之前评价的是TI,因此有\(x_{TI}^{(2)}=1\)

第六部分(对应很色框)表示打分标签

在一个真实的电影评分系统中,用户的数据\(|U|\)和电影的数据\(|I|\)是很大的,而每个用户参与评论的电影数目则相对很小,可想而知,每一条记录对应的特征向量将会非常稀疏。

1.2 模型方程

给定特征向量\({\tt x}=(x_1,x_2,...,x_n)^T\),线性回归建模时采用的函数是:

\[\hat y(x)=\sum_{i=1}^n w_i x_i + w_0 \tag{1} \]

从方程中可以看出,各个特征分量\(x_i\)​和\(x_j(i\neq j)\)​是相互独立的,即\(\hat y(x)\)​中仅考虑单个的特征分量,而没有考虑特征分量之间的相互关系,接下来在公式(1)的基础上,将函数\(\hat y\)改写为:

\[\hat y(x)=w_0 + \sum_{i=1}^n w_i x_i + \sum_{i=1}^{n-1}\sum_{j=i+1}^n w_{ij}x_ix_j \tag{2} \]

这样便将任意两个互异特征分量之间的关系也考虑进来了。但是这样又会引入大量的稀疏数据,如数据集中没有Alice评价电影Star Trek的记录,如果要直接估计这两者(\(x_A\)和\(x_{ST}\)​​)之间的关系,显然会得到\(w_{A,ST}=0\),即对于观察样本中未出现过交互的特征分量,不能对相应的参数进行估计。在高维稀疏数据场景中,由于数据量的不足,样本中出现未交互的特征分量是很普遍的。

1.3 辅助向量

为了解决数据稀疏的问题,在公式(2)的稀疏\(w_{ij}\)​​上做文章,引入一个辅助向量

\[{\tt v_i} = (v_{i1},v_{i2},...,v_{ik}) \in {\Bbb R}^k, i=1,2,...,n \]

其中\(k \in {\Bbb N}^+\)​是超参数,此时\(w_{ij}\)可以改写为:

\[\hat w_{ij}={\tt v_i^Tv_j} := \sum_{l=1}^k v_{il}v_{jl} \]

进一步进行分析可以发现:

\[\hat W = VV^T = \begin{pmatrix} \tt v_1^T \\ \tt v_2^T \\ \vdots \\ \tt v_n^T\end{pmatrix}(\tt v_1,v_2,...,v_n)=\begin{pmatrix} v_1^Tv_1 & v_1^Tv2 & \cdots & v_1^Tv_n \\v_2^Tv_1 & v_2^Tv_2 & \cdots & v_2^Tv_n \\ \vdots & \vdots & \ddots\\v_n^Tv_1 & v_n^T v_2 & \cdots & v_n^Tv_n\end{pmatrix}_{n\times n}=\begin{pmatrix} v_1^Tv_1 & \hat w_{12} & \cdots & \hat w_{1n} \\ \hat w_{21} & v_2^Tv_2 & \cdots & \hat w_{2n} \\ \vdots & \vdots & \ddots\\ \hat w_{n1} & \cdots & \hat w_{n2} \end{pmatrix}_{n\times n} \]

因此公式(2)可以进一步简写为:

\[\begin{align} \hat y(x) & =w_0 + \sum_{i=1}^n w_i x_i + \sum_{i=1}^{n-1}\sum_{j=i+1}^n ({\tt v_i^Tv_j})x_ix_j \\ & =w_0 + \sum_{i=1}^n w_i x_i + \frac{1}{2} \Big(\sum_{i=1}^{n}\sum_{j=1}^n ({\tt v_i^Tv_j}))x_ix_j - \sum_{i=1}^n ({\tt v_i^Tv_j}))x_ix_i\Big) \\ & =w_0 + \sum_{i=1}^n w_i x_i + \frac{1}{2}\Big(\sum_{i=1}^{n}\sum_{j=1}^n \sum_{l=1}^k(v_{il}v_{jl})x_ix_j - \sum_{i=1}^n \sum_{l=1}^kv_{il}^2x_i^2\Big)\\ & =w_0 + \sum_{i=1}^n w_i x_i + \frac{1}{2}\sum_{l=1}^k \Big(\sum_{i=1}^{n}(v_{il}x_i)\sum_{j=1}^n (v_{jl}x_j)-\sum_{i=1}^{n}(v_{il}^2x_i^2) \Big)\\& =w_0 + \sum_{i=1}^n w_i x_i + \frac{1}{2}\sum_{l=1}^k \Big[\Big(\sum_{i=1}^{n}(v_{il}x_i)\Big)^2-\sum_{i=1}^{n}(v_{il}^2x_i^2) \Big] \end{align} \tag{3} \]

说明:改进后的交叉项就类似于Word2Vec中根据ID值获取每个word的embedding向量。但是FM中不仅对离散数据取embedding向量,连续数值同样进行了embedding操作。只是在离散数据中,或根据数值的个数生成多个embedding向量,在实际计算中根据离散向量的数值获取对应的embedding;而在连续特征中,只生成一个embedding,在使用该embedding时需要与该特征的具体数据进行相乘。

1.4 代码及其说明

1.4.1 导入包及数据预处理

import torch 
from torch import nn 
import pandas as pd 
import torch.utils.data as Data 
import numpy as np

##这里应该会得到两个embedding_dict
def load_data(path): 
    data = pd.read_csv(path) 
    sparse_features = ['C' + str(i) for i in range(1, 27)] 
    dense_features = ['I' + str(i) for i in range(1,14)] 
    label = data.label.values 
    label = label.reshape(len(label), 1) 
    feature_dict = {} #定义一个字典,用于字符转id 
    data[dense_features] = data[dense_features].fillna(0,) 
    cnt = 1 
    features = pd.DataFrame() 
    indexes = pd.DataFrame() 
    # 连续特征处理,key值全部为1,value值为原始特征值
    for col in dense_features: 
        values = data.loc[:, col] 
        values = (values - values.mean()) / values.std() 
        features = pd.concat([features, values], axis=1) 
        feature_dict[col] = cnt 
        indexes = pd.concat([indexes, values * 0 + cnt], axis=1) 
        cnt += 1
    # 离散特征处理,key值为原特征值,value值为1
    for col in sparse_features:
        values = data.loc[:, col]
        uniques = values.unique()
        feature_dict[col] = dict(zip(uniques, range(cnt, len(uniques) + cnt)))
        indexes = pd.concat([indexes, values.map(feature_dict[col])], axis=1)
        # values = 1
        features = pd.concat([features, indexes[col]*0+1], axis=1)
        cnt += len(uniques)

    train_data = {}
    train_data['label'] = label
    train_data['index'] = indexes.values.tolist()
    train_data['features'] = features.values.tolist()
    train_data['feat_dim'] = cnt
    return train_data

train_data = load_data('./data/criteo_sample.txt')

class FMDataset(Data.Dataset): 
    def init(self, data): 
        self.features = data['features'] 
        self.indexes = data['index'] 
        self.labels = data['label']

    def __getitem__(self, item):
        value, index, label = self.features[item], self.indexes[item], self.labels[item]
        return torch.tensor(value, dtype=torch.float), torch.tensor(index, dtype=torch.long), torch.tensor(label,dtype=torch.long)
    
    def __len__(self):
        return len(self.labels)
    
trainDataset = FMDataset(train_data)

train_iter = Data.DataLoader(dataset=trainDataset, batch_size=64, shuffle=True, ) 
test_iter = Data.DataLoader(dataset=trainDataset, batch_size=64, shuffle=False, )

1.4.2 构建FM模型

class FMModel(nn.Module):
    
    def __init__(self, word_counts, embed_dim):
        super(FMModel, self).__init__()
        self.embedding = nn.Embedding(word_counts, embed_dim)
        self.weight = nn.Embedding(word_counts, 1)
    
    def forward(self, features, index):
        # features: [64,39], embed:[64,39,16], weight:[64,39,1]
        index = torch.tensor(index)
        weights = self.weight(index)
        embed = self.embedding(index)
        first_embed = torch.mul(weights, features.reshape(weights.shape))
        # first_embed = first_embed.squeeze(2)
        # first_sum: [batch_size, 1]
        first_sum = torch.sum(first_embed, dim=1, keepdim=False)
        fm_embed = torch.mul(embed, features.reshape(weights.shape))
        square_sum = torch.pow(torch.sum(fm_embed, dim=1, keepdim=True), 2)
        sum_square = torch.sum(fm_embed * fm_embed, dim=1, keepdim=True)
        # cross_term: [batch_size, 1, embed_dim]
        cross_term = square_sum - sum_square
        # cross_sum: [batch_size, 1]
        cross_sum = 0.5 * torch.sum(cross_term, dim=2, keepdim=False)
        return first_sum + cross_sum

model = FMModel(train_data['feat_dim'], 16) 

# 对离散特征求embedding,实际上计算的是公式中v的值,k就是embedding的长度。线性部分文章中介绍是用one-hot,对离散值的处理也是分别给每个对应的值构建一个k=1的embedding

标签:features,RS,sum,torch,学习,values,data,self
来源: https://www.cnblogs.com/laojifuli/p/16376945.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有