The code of recommendation system algorithms(models) implemented by Pytorch. 经典推荐系统算法的torch版简单实现(Updating...)
import torch
import torch.nn as nn


# 模型构建
class FM_Layer(nn.Module):
    def __init__(self, feature_nums=10, k=5):
        """
        feature_nums: 特征个数
        k: 特征对应的隐向量维度大小
        """
        super(FM_Layer, self).__init__()
        self.feature_nums = feature_nums
        self.k = k
        self.linear = nn.Linear(self.feature_nums, 1)   # 前两项线性层(w和b)
        self.V = nn.Parameter(torch.randn(self.feature_nums, self.k))   # 交互矩阵
        nn.init.uniform_(self.V, -0.1, 0.1)

    def fm_layer(self, x):
        linear_part = self.linear(x)
        interaction_part_1 = torch.mm(x, self.V)
        interaction_part_1 = torch.pow(interaction_part_1, 2)
        interaction_part_2 = torch.mm(torch.pow(x, 2), torch.pow(self.V, 2))
        part = torch.sum(interaction_part_2 - interaction_part_1, 1, keepdim=False)
        output = linear_part.reshape(-1,) + 0.5 * part
        return output
    
    def forward(self, x):
        x = self.fm_layer(x)
        return x
    

class DeepFM(nn.Module):
    def __init__(self, feature_columns, feature_nums, k):
        super(DeepFM, self).__init__()

        self.dense_feature_columns, self.sparse_feature_columns = feature_columns
        self.embed_layers = {
            'embed_' + str(i): nn.Embedding(feat['feat_onehot_dim'], feat['embed_dim'])
             for i, feat in enumerate(self.sparse_feature_columns)
        }

        self.fm = nn.Sequential(
            FM_Layer(feature_nums, k),
            nn.Sigmoid()
        )
        self.fc = nn.Sequential(
            nn.Linear(feature_nums, 256),
            nn.ReLU(),
            nn.Linear(256, 128),
            nn.ReLU(),
            nn.Linear(128, 64),
            nn.ReLU(),
            nn.Linear(64, 1),
            nn.Sigmoid()
        )

    def forward(self, dense_inputs, sparse_inputs):
        # embedding，就是和词嵌入是一样的
        sparse_embed = torch.concat([self.embed_layers[f'embed_{i}'](sparse_inputs[:, i])
                                  for i in range(sparse_inputs.shape[1])], axis=1)
        
        x = torch.concat([dense_inputs, sparse_embed], axis=-1)

        fm_output = self.fm(x)
        fc_output = self.fc(x).reshape(-1,)

        output = 0.5*(fm_output + fc_output)

        return output
DeepFM/model.py
ceresOPA/Recommend-System-pytorch
import torch
import torch.nn as nn
from torch.utils.data import TensorDataset, DataLoader
from sklearn.metrics import accuracy_score
from utils import create_criteo_dataset
from model import DeepFM

import argparse
parser = argparse.ArgumentParser(description='命令行参数')
parser.add_argument('--k', type=int, help='v_dim', default=8) # 隐向量大小
parser.add_argument('--file_path', type=str, default="../Data/train.txt")
args=parser.parse_args()


if __name__ == "__main__":
    # 超参数
    batch_size = 2000
    n_epochs = 200
    learning_rate = 0.01
    embed_dim = 8

    # 读取数据集
    feature_columns, (X_train, y_train), (X_test, y_test) = create_criteo_dataset(args.file_path, embed_dim=embed_dim, test_size=0.2)
    # 转换为Tensor，并以DataLoader加载
    X_train, X_test = torch.from_numpy(X_train), torch.from_numpy(X_test)
    y_train, y_test = torch.from_numpy(y_train.values.reshape(-1,)), torch.from_numpy(y_test.values.reshape(-1,))
    train_dataset, test_dataset = TensorDataset(X_train, y_train), TensorDataset(X_test, y_test)
    train_loader, test_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True), DataLoader(test_dataset, batch_size=1024)

    # 设备
    device = 'cuda' if torch.cuda.is_available() else 'cpu'

    # 加载模型
    model = DeepFM(feature_columns, feature_nums=embed_dim*len(feature_columns[1])+len(feature_columns[0]), k=30)
    model.to(device)

    # 优化器
    optimizer = torch.optim.SGD(params=model.parameters(), lr=learning_rate)

    # 损失函数
    criterion = nn.BCELoss()

    # 训练
    for epoch_idx in range(n_epochs):
        model.train()
        losses = []
        acc = []
        for batch_idx, (X, y) in enumerate(train_loader):
            dense_inputs, sparse_inputs = X[:, :13], X[:, 13:]
            dense_inputs, sparse_inputs, y = dense_inputs.to(device, dtype=torch.float32), sparse_inputs.to(device, dtype=torch.int32), y.to(device, dtype=torch.float32)
            optimizer.zero_grad()
            output = model(dense_inputs, sparse_inputs)
            pred = [1 if i>0.5 else 0 for i in output.cpu().detach()]
            acc.append(accuracy_score(y, pred))
            loss = criterion(output, y)
            loss.backward()
            optimizer.step()
            losses.append(loss.item())

        print(f"train_loss: {sum(losses)/len(losses):5.2f} | train_acc: {sum(acc)/len(acc)*100:5.2f}%")

    # 测试
    model.eval()
    acc = []
    for batch_idx, (X, y) in enumerate(test_loader):
        dense_inputs, sparse_inputs = X[:, :13], X[:, 13:]
        dense_inputs, sparse_inputs, y = dense_inputs.to(device, dtype=torch.float32), sparse_inputs.to(device, dtype=torch.int32), y.to(device, dtype=torch.float32)
        output = model(dense_inputs, sparse_inputs)
        pred = [1 if i>0.5 else 0 for i in output.cpu().detach()]
        acc.append(accuracy_score(y, pred))

    print(f"test_acc: {sum(acc)/len(acc)*100:5.2f}%")
        
DeepFM/train.py
ceresOPA/Recommend-System-pytorch
import pandas as pd
from sklearn.preprocessing import LabelEncoder, MinMaxScaler
from sklearn.model_selection import train_test_split

def sparseFeature(feat, feat_onehot_dim, embed_dim):
    return {'feat': feat, 'feat_onehot_dim': feat_onehot_dim, 'embed_dim': embed_dim}

def denseFeature(feat):
    return {'feat': feat}

def create_criteo_dataset(file_path, embed_dim=8, test_size=0.2):
    data = pd.read_csv(file_path)

    dense_features = ['I' + str(i) for i in range(1, 14)]
    sparse_features = ['C' + str(i) for i in range(1, 27)]

    #缺失值填充
    data[dense_features] = data[dense_features].fillna(0)
    data[sparse_features] = data[sparse_features].fillna('-1')

    #归一化
    data[dense_features] = MinMaxScaler().fit_transform(data[dense_features])
    #LabelEncoding编码
    for col in sparse_features:
        data[col] = LabelEncoder().fit_transform(data[col]).astype(int)

    feature_columns = [[denseFeature(feat) for feat in dense_features]] + \
           [[sparseFeature(feat, data[feat].nunique(), embed_dim) for feat in sparse_features]]

    X = data.drop(['label'], axis=1).values
    y = data['label']
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size)

    return feature_columns, (X_train, y_train), (X_test, y_test)
DeepFM/utils.py
ceresOPA/Recommend-System-pytorch
import torch
import torch.nn as nn
import torch.nn.functional as F

# 模型构建
class FFM_Layer(nn.Module):
    def __init__(self, feature_columns, k=5):
        """
        feature_columns: 特征列
        k: 特征对应的隐向量维度大小
        """
        super(FFM_Layer, self).__init__()
        self.dense_feature_columns, self.sparse_feature_columns = feature_columns
        self.k = k
        self.feature_nums = sum([feat['feat_onehot_dim'] for feat in self.sparse_feature_columns]) \
                           + len(self.dense_feature_columns)
        self.field_nums = len(self.dense_feature_columns) + len(self.sparse_feature_columns)

        self.linear = nn.Linear(self.feature_nums, 1)   # 前两项线性层(w和b)
        self.V = nn.Parameter(torch.randn(self.feature_nums, self.field_nums, self.k))   # 交互矩阵，在FM的基础上增加了field域
        nn.init.uniform_(self.V, -0.1, 0.1)


    def ffm_layer(self, x):
        dense_inputs, sparse_inputs = x[:, :13], x[:, 13:].to(dtype=torch.int64)
        sparse_x = torch.concat([ F.one_hot(sparse_inputs[:, i], num_classes=self.sparse_feature_columns[i]['feat_onehot_dim'])
                                  for i in range(sparse_inputs.shape[1])], axis=1)
        x = torch.concat([dense_inputs, sparse_x], axis=-1)

        linear_part = self.linear(x)

        inter_part = 0
        # 每维特征先与对应的 [field_num, k] 相乘得到Vij*X
        field_f = torch.matmul(x, self.V.reshape(self.V.size(0), -1)).reshape(-1, self.V.size(1), self.V.size(2))
        # 域之间两两相乘，
        for i in range(self.field_nums):
            for j in range(i+1, self.field_nums):
                inter_part += torch.sum(
                    torch.multiply(field_f[:, i], field_f[:, j]), # [None, 8]
                    dim=1, keepdim=True
                )

        output = linear_part + inter_part

        return output
    
    def forward(self, x):
        x = self.ffm_layer(x)
        return x

class FFM(nn.Module):
    def __init__(self, feature_columns, k):
        super(FFM, self).__init__()
        self.dense_feature_columns, self.sparse_feature_columns = feature_columns
        self.ffm = nn.Sequential(
            FFM_Layer(feature_columns, k),
            nn.Sigmoid()
        )

    def forward(self, inputs):
        output = self.ffm(inputs).reshape(-1, )
        return output
FFM/model.py
ceresOPA/Recommend-System-pytorch
import torch
import torch.nn as nn
from torch.utils.data import TensorDataset, DataLoader
from sklearn.metrics import accuracy_score
from utils import create_criteo_dataset
from model import FFM

import argparse
parser = argparse.ArgumentParser(description='命令行参数')
parser.add_argument('--k', type=int, help='v_dim', default=8) # 隐向量大小
parser.add_argument('--file_path', type=str, default="../Data/train.txt")
args=parser.parse_args()


if __name__ == "__main__":
    # 超参数
    batch_size = 256
    n_epochs = 10
    learning_rate = 0.01
    embed_dim = 8

    # 读取数据集
    feature_columns, (X_train, y_train), (X_test, y_test) = create_criteo_dataset(args.file_path, embed_dim=embed_dim, test_size=0.2)
    # 转换为Tensor，并以DataLoader加载
    X_train, X_test = torch.from_numpy(X_train), torch.from_numpy(X_test)
    y_train, y_test = torch.from_numpy(y_train.values.reshape(-1,)), torch.from_numpy(y_test.values.reshape(-1,))
    train_dataset, test_dataset = TensorDataset(X_train, y_train), TensorDataset(X_test, y_test)
    train_loader, test_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True), DataLoader(test_dataset, batch_size=1024)

    # 设备
    device = 'cuda' if torch.cuda.is_available() else 'cpu'

    # 加载模型
    model = FFM(feature_columns, k=30)
    model.to(device)

    # 优化器
    optimizer = torch.optim.SGD(params=model.parameters(), lr=learning_rate)

    # 损失函数
    criterion = nn.BCELoss()

    # 训练
    for epoch_idx in range(n_epochs):
        model.train()
        losses = []
        acc = []
        for batch_idx, (X, y) in enumerate(train_loader):
            X, y = X.to(device, dtype=torch.float32), y.to(device, dtype=torch.float32)
            optimizer.zero_grad()
            output = model(X)
            pred = [1 if i>0.5 else 0 for i in output.cpu().detach()]
            acc.append(accuracy_score(y, pred))
            loss = criterion(output, y)
            loss.backward()
            optimizer.step()
            losses.append(loss.item())

        print(f"train_loss: {sum(losses)/len(losses):5.2f} | train_acc: {sum(acc)/len(acc)*100:5.2f}%")

    # 测试
    model.eval()
    acc = []
    for batch_idx, (X, y) in enumerate(test_loader):
        X, y = X.to(device, dtype=torch.float32), y.to(device, dtype=torch.float32)
        output = model(X)
        pred = [1 if i>0.5 else 0 for i in output.cpu().detach()]
        acc.append(accuracy_score(y, pred))

    print(f"test_acc: {sum(acc)/len(acc)*100:5.2f}%")
        
FFM/train.py
ceresOPA/Recommend-System-pytorch
import pandas as pd
from sklearn.preprocessing import LabelEncoder, MinMaxScaler
from sklearn.model_selection import train_test_split

def sparseFeature(feat, feat_onehot_dim, embed_dim):
    return {'feat': feat, 'feat_onehot_dim': feat_onehot_dim, 'embed_dim': embed_dim}

def denseFeature(feat):
    return {'feat': feat}

def create_criteo_dataset(file_path, embed_dim=8, test_size=0.2):
    data = pd.read_csv(file_path)

    dense_features = ['I' + str(i) for i in range(1, 14)]
    sparse_features = ['C' + str(i) for i in range(1, 27)]

    #缺失值填充
    data[dense_features] = data[dense_features].fillna(0)
    data[sparse_features] = data[sparse_features].fillna('-1')

    #归一化
    data[dense_features] = MinMaxScaler().fit_transform(data[dense_features])
    #LabelEncoding编码
    for col in sparse_features:
        data[col] = LabelEncoder().fit_transform(data[col]).astype(int)

    feature_columns = [[denseFeature(feat) for feat in dense_features]] + \
           [[sparseFeature(feat, data[feat].nunique(), embed_dim) for feat in sparse_features]]

    X = data.drop(['label'], axis=1).values
    y = data['label']
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size)

    return feature_columns, (X_train, y_train), (X_test, y_test)
FFM/utils.py
ceresOPA/Recommend-System-pytorch
import torch
import torch.nn as nn


# 模型构建
class FM_Layer(nn.Module):
    def __init__(self, feature_nums=10, k=5):
        """
        feature_nums: 特征个数
        k: 特征对应的隐向量维度大小
        """
        super(FM_Layer, self).__init__()
        self.feature_nums = feature_nums
        self.k = k
        self.linear = nn.Linear(self.feature_nums, 1)   # 前两项线性层(w和b)
        self.V = nn.Parameter(torch.randn(self.feature_nums, self.k))   # 交互矩阵
        nn.init.uniform_(self.V, -0.1, 0.1)

    def fm_layer(self, x):
        linear_part = self.linear(x)
        interaction_part_1 = torch.mm(x, self.V)
        interaction_part_1 = torch.pow(interaction_part_1, 2)
        interaction_part_2 = torch.mm(torch.pow(x, 2), torch.pow(self.V, 2))
        part = torch.sum(interaction_part_2 - interaction_part_1, 1, keepdim=False)
        output = linear_part.reshape(-1,) + 0.5 * part
        return output
    
    def forward(self, x):
        x = self.fm_layer(x)
        return x


class FM(nn.Module):
    """
    这里进行分类任务, 因此加了个Sigmoid, 用以表示输出概率, 训练中损失函数使用BCELoss(二分交叉熵损失函数)
    如果是进行回归任务, 如预测评分, 则不加Sigmoid, 直接输出, 损失函数可采用MSELoss
    """
    def __init__(self, feature_nums, k):
        super().__init__()
        self.fm = nn.Sequential(
            FM_Layer(feature_nums, k),
            nn.Sigmoid()
        )

    def forward(self, x):
        x = self.fm(x)
        return x
FM/model.py
ceresOPA/Recommend-System-pytorch
import torch
import torch.nn as nn
from torch.utils.data import TensorDataset, DataLoader
from sklearn.metrics import accuracy_score
from utils import create_criteo_dataset
from model import FM

import argparse
parser = argparse.ArgumentParser(description='命令行参数')
parser.add_argument('--k', type=int, help='v_dim', default=8) # 隐向量大小
parser.add_argument('--file_path', type=str, default="../Data/train.txt")
args=parser.parse_args()


if __name__ == "__main__":
    # 超参数
    batch_size = 2000
    n_epochs = 50
    learning_rate = 0.01

    # 读取数据集
    (X_train, y_train), (X_test, y_test) = create_criteo_dataset(args.file_path, test_size=0.2)
    # 转换为Tensor，并以DataLoader加载
    X_train, X_test = torch.from_numpy(X_train), torch.from_numpy(X_test)
    y_train, y_test = torch.from_numpy(y_train.values.reshape(-1,)), torch.from_numpy(y_test.values.reshape(-1,))
    train_dataset, test_dataset = TensorDataset(X_train, y_train), TensorDataset(X_test, y_test)
    train_loader, test_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True), DataLoader(test_dataset, batch_size=1024)

    # 设备
    device = 'cuda' if torch.cuda.is_available() else 'cpu'

    # 加载模型
    model = FM(feature_nums=X_train.size(-1), k=8)
    model.to(device)

    # 优化器
    optimizer = torch.optim.SGD(params=model.parameters(), lr=learning_rate)

    # 损失函数
    criterion = nn.BCELoss()

    # 训练
    for epoch_idx in range(n_epochs):
        model.train()
        losses = []
        acc = []
        for batch_idx, (X, y) in enumerate(train_loader):
            X, y = X.to(device, dtype=torch.float32), y.to(device, dtype=torch.float32)
            optimizer.zero_grad()
            output = model(X)
            pred = [1 if i>0.5 else 0 for i in output.cpu().detach()]
            acc.append(accuracy_score(y, pred))
            loss = criterion(output, y)
            loss.backward()
            optimizer.step()
            losses.append(loss.item())

        print(f"train_loss: {sum(losses)/len(losses):5.2f} | train_acc: {sum(acc)/len(acc)*100:5.2f}%")

    # 测试
    model.eval()
    acc = []
    for batch_idx, (X, y) in enumerate(test_loader):
        X, y = X.to(device, dtype=torch.float32), y.to(device, dtype=torch.float32)
        output = model(X)
        pred = [1 if i>0.5 else 0 for i in output.cpu().detach()]
        acc.append(accuracy_score(y, pred))

    print(f"test_acc: {sum(acc)/len(acc)*100:5.2f}%")
        

        

FM/train.py
ceresOPA/Recommend-System-pytorch
import pandas as pd
from sklearn.preprocessing import MinMaxScaler, LabelEncoder
from sklearn.model_selection import train_test_split

def create_criteo_dataset(file_path, test_size=0.3):
    data = pd.read_csv(file_path)

    dense_features = ['I' + str(i) for i in range(1, 14)]
    sparse_features = ['C' + str(i) for i in range(1, 27)]

    #缺失值填充
    data[dense_features] = data[dense_features].fillna(0)
    data[sparse_features] = data[sparse_features].fillna('-1')

    #归一化
    data[dense_features] = MinMaxScaler().fit_transform(data[dense_features])
    #Onehot编码 (发现如果改为LabelEncoder，就完全train不起来，这是否和FM算法本身更适合稀疏矩阵有关？)
    data = pd.get_dummies(data)

    #数据集划分
    X = data.drop(['label'], axis=1).values
    y = data['label']
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size)

    return (X_train, y_train), (X_test, y_test)
FM/utils.py
ceresOPA/Recommend-System-pytorch
import torch
import torch.nn as nn

class CIN(nn.Module):
    """
    CIN这部分按照原tf代码的思路和自己的理解实现了下, 可能存在一定的偏差 
    然后有部分地方是写死的, 后面还要再改改
    """
    def __init__(self, cin_size=[26, 128, 128]):
        super(CIN, self).__init__()
        self.cin_size = cin_size  # 每层的矩阵个数
        self.cin_W = nn.ModuleList(
            [nn.Conv3d(in_channels=1, out_channels=cin_size[i+1], kernel_size=(cin_size[i], cin_size[0], 1), stride=1) for i in range(len(cin_size)-1)])

    def forward(self, inputs):
        k = inputs.shape[-1]
        res_list = [inputs]
        X0 = torch.split(inputs, 1, dim=-2)
        for i, size in enumerate(self.cin_size[1:]):
            Xi = torch.split(res_list[-1], 1, dim=-2)
            x = []
            for i_i in range(len(Xi)):
                tmp = []
                for j_i in range(len(X0)):
                    tmp.append(Xi[i_i]*X0[j_i])
                tmp = torch.hstack(tmp).unsqueeze(1)
                # print(tmp.shape)
                x.append(tmp)
                
            x = torch.concatenate(x, dim=1).unsqueeze(1)
            x =  self.cin_W[i](x) # 用卷积实现三维矩阵的压缩
            x = x.reshape(-1, size, x.size(-1))
            res_list.append(x)

        res_list = res_list[1:]   # 去掉 X0
        res = torch.concatenate(res_list, dim=1)     # (Batch, field_num[1]+...+field_num[n], k)
        output = torch.sum(res, dim=-1)  # (Batch, field_num[1]+...+field_num[n])
        return output


class xDeepFM(nn.Module):
    def __init__(self, feature_columns, feature_nums, cin_size):
        super(xDeepFM, self).__init__()

        self.feature_nums = feature_nums

        self.dense_feature_columns, self.sparse_feature_columns = feature_columns
        self.embed_layers = [nn.Embedding(feat['feat_onehot_dim'], feat['embed_dim'])
                                    for feat in self.sparse_feature_columns]
        
        self.linear =  nn.Linear(self.feature_nums, 1) # 一阶项
        
        self.dense_layer = nn.Sequential(
            nn.Linear(221, 256),
            nn.ReLU(),
            nn.Linear(256, 128),
            nn.ReLU(),
            nn.Linear(128, 64),
            nn.ReLU(),
            nn.Linear(64, 1),
            nn.ReLU()
        )

        self.cin_layer = nn.Sequential(
            CIN(cin_size),
            nn.Linear(256, 1),
            nn.ReLU()
        )

        self.out_layer = nn.Sequential(
            nn.Sigmoid()
        )

    def forward(self, inputs):
        dense_inputs, sparse_inputs = inputs[:, :13], inputs[:, 13:].int()

        # linear
        linear_out = self.linear(inputs)

        emb = [self.embed_layers[i](sparse_inputs[:, i]).unsqueeze(1) for i in range(sparse_inputs.shape[1])] # [n, field, k]
        emb = torch.concatenate(emb, dim=1) # 与DeepFM和DeepCross不同，采用纵向拼接，而不是横向拼接

        # CIN
        cin_out = self.cin_layer(emb)

        # dense
        emb = emb.reshape(-1, emb.shape[1]*emb.shape[2])
        emb = torch.concat([dense_inputs, emb], axis=1)
        dense_out = self.dense_layer(emb)

        output = self.out_layer(linear_out + cin_out + dense_out).reshape(-1, )
        return output
xDeepFM/model.py
ceresOPA/Recommend-System-pytorch
import torch
import torch.nn as nn
from torch.utils.data import TensorDataset, DataLoader
from sklearn.metrics import accuracy_score
from utils import create_criteo_dataset
from model import xDeepFM

import argparse
parser = argparse.ArgumentParser(description='命令行参数')
parser.add_argument('--k', type=int, help='v_dim', default=8) # 隐向量大小
parser.add_argument('--file_path', type=str, default="../Data/train.txt")
args=parser.parse_args()


if __name__ == "__main__":
    # 超参数
    batch_size = 256
    n_epochs = 10
    learning_rate = 0.01
    embed_dim = 8

    # 读取数据集
    feature_columns, (X_train, y_train), (X_test, y_test) = create_criteo_dataset(args.file_path, embed_dim=embed_dim, test_size=0.2)
    # 转换为Tensor，并以DataLoader加载
    X_train, X_test = torch.from_numpy(X_train), torch.from_numpy(X_test)
    y_train, y_test = torch.from_numpy(y_train.values.reshape(-1,)), torch.from_numpy(y_test.values.reshape(-1,))
    train_dataset, test_dataset = TensorDataset(X_train, y_train), TensorDataset(X_test, y_test)
    train_loader, test_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True), DataLoader(test_dataset, batch_size=1024)

    # 设备
    device = 'cuda' if torch.cuda.is_available() else 'cpu'

    # 加载模型
    model = xDeepFM(feature_columns, feature_nums=X_train.size(-1), cin_size=[26, 128, 128])
    model.to(device)

    # 优化器
    optimizer = torch.optim.SGD(params=model.parameters(), lr=learning_rate)

    # 损失函数
    criterion = nn.BCELoss()

    # 训练
    for epoch_idx in range(n_epochs):
        model.train()
        losses = []
        acc = []
        for batch_idx, (X, y) in enumerate(train_loader):
            X, y = X.to(device, dtype=torch.float32), y.to(device, dtype=torch.float32)
            optimizer.zero_grad()
            output = model(X)
            pred = [1 if i>0.5 else 0 for i in output.cpu().detach()]
            acc.append(accuracy_score(y, pred))
            loss = criterion(output, y)
            loss.backward()
            optimizer.step()
            losses.append(loss.item())

        print(f"train_loss: {sum(losses)/len(losses):5.2f} | train_acc: {sum(acc)/len(acc)*100:5.2f}%")

    # 测试
    model.eval()
    acc = []
    for batch_idx, (X, y) in enumerate(test_loader):
        X, y = X.to(device, dtype=torch.float32), y.to(device, dtype=torch.float32)
        output = model(X)
        pred = [1 if i>0.5 else 0 for i in output.cpu().detach()]
        acc.append(accuracy_score(y, pred))

    print(f"test_acc: {sum(acc)/len(acc)*100:5.2f}%")
        
xDeepFM/train.py
ceresOPA/Recommend-System-pytorch
import pandas as pd
from sklearn.preprocessing import LabelEncoder, MinMaxScaler
from sklearn.model_selection import train_test_split

def sparseFeature(feat, feat_onehot_dim, embed_dim):
    return {'feat': feat, 'feat_onehot_dim': feat_onehot_dim, 'embed_dim': embed_dim}

def denseFeature(feat):
    return {'feat': feat}

def create_criteo_dataset(file_path, embed_dim=8, test_size=0.2):
    data = pd.read_csv(file_path)

    dense_features = ['I' + str(i) for i in range(1, 14)]
    sparse_features = ['C' + str(i) for i in range(1, 27)]

    #缺失值填充
    data[dense_features] = data[dense_features].fillna(0)
    data[sparse_features] = data[sparse_features].fillna('-1')

    #归一化
    data[dense_features] = MinMaxScaler().fit_transform(data[dense_features])
    #LabelEncoding编码
    for col in sparse_features:
        data[col] = LabelEncoder().fit_transform(data[col]).astype(int)

    feature_columns = [[denseFeature(feat) for feat in dense_features]] + \
           [[sparseFeature(feat, data[feat].nunique(), embed_dim) for feat in sparse_features]]

    X = data.drop(['label'], axis=1).values
    y = data['label']
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size)

    return feature_columns, (X_train, y_train), (X_test, y_test)
xDeepFM/utils.py
ceresOPA/Recommend-System-pytorch