文章目录
前言
本篇文章是我在复现推荐系统中的FM因子分解机模型时的收获总结。
一、概念
1. 测试集和验证集
测试集和验证集都是用来评估模型性能的数据集,但在用途和使用方式上不同。
验证集通常用于训练过程中对模型的调优,通过在训练过程中对验证集进行评估,可以根据验证集的结果对模型进行选择和调整,比如修改超参数等。在训练集上训练后,使用验证集对模型进行评估,可以帮助我们避免过拟合。如果在训练过程中,模型的表现在验证集上开始变差,那么我们可以及时停止训练,防止模型在训练集上过拟合。
测试集则用于最终评估模型的性能,它是用来衡量模型在未知数据上的表现。在模型选择和调优完成后,使用测试集对模型进行评估,可以得到一个更准确的模型性能评估。测试集的数据应该是独立于训练集和验证集的,这样可以保证测试集的数据和模型没有任何关系,从而确保评估的结果是准确可靠的。
2. BCELoss和BCEWithLogitsLoss的区别
BCELoss要求输入值取值在 [0,1],而BCEWithLogitsLoss就是把Sigmoid和BCELoss合成为一步,且比简单地将 Sigmoid 层加上 BCELoss 损失更稳定
二、实用的操作
1. 切片
对于二维数组 arr = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]]),我们可以使用以下代码进行切片操作:
arr[1:3, 0:2] #选取第2行和第3行,第1列和第2列,得到一个2x2的数组
arr[:, 1:] #选取所有行,第二列到最后一列
arr[:, 0] #选取所有行,第一列
2. 查看模型的参数
for param in model.parameters():
print(param)
3. 查看变量的设备
V.device
三、函数用法及其参数
1. train_test_split:训练集和验证集划分函数
X_train, X_val, y_train, y_val = train_test_split(train_data[:, :-1], train_data[:, -1], test_size=0.2, random_state=2022)
第一个参数表示特征矩阵列,第二个参数表示标签列,第三个参数test_size表示验证集的比例
第四个参数表示随机种子值,如2022是一个随机数生成器的种子值。设置一个固定的种子值可以确保每次运行代码时,随机数生成器生成的随机数序列都是一样的。可以确保实验的可重复性。如果不使用固定的种子值,每次运行代码时生成的随机数序列都会不同,这可能会对实验的结果造成影响。
2. nn.Embedding的用法
self.embedding = nn.Embedding(input_dim, latent_dim)
a. 原理:input_dim为特征序列中元素的最大值,latent_dim为要生成的隐向量维度
self.embedding(X)先为每个特征生成隐向量(有input_dim个),产生一个大小为 input_dim * latent_dim的矩阵,即每个隐向量的维度为latent_dim,然后在这个矩阵中查找X中每个元素对应的隐向量。最后得到的结果是1 * X_dim * latent_dim(这里的1是特殊情况,因为只输入了一个样本,一般情况下是 batch_size * X_dim * latent_dim)
b. 注意事项:
self.embedding(X) 要求输入的张量必须是整数类型(Long 或 Int),倘若输入的张量X是 torch.cuda.FloatTensor 类型。可以通过将 X 转换为整数类型来解决,即 X.long(),例如 self.embedding(X.long())。
3. torch.sum的用法
torch.sum(X, dim = 0)
在矩阵X中按行或者按列求和,若dim=0则按行求和,dim=1则按列求和
torch.sum就是在矩阵的中进行元素求和的按行或按列的批量操作,更精准地说,此函数能够实现并行计算
在FM中的应用:
torch.sum(torch.matmul(x, self.embedding(x)), dim = 1)
4. torch.matmul(A,A)与A.pow(2)的区别
torch.matmul(A, B) 表示矩阵乘法,对于输入的两个张量A和B(它们需要满足矩阵乘法的形状条件)、对于A和B形状分别为(m, p)和(p, n)的两个矩阵,它们的乘积结果为一个形状为(m, n)的矩阵。
torch.matmul(A, A)实现的是A与A的矩阵乘法
而A.pow(2)则是对张量A中的每一个元素进行平方操作。对于形状为(m, n)的张量A,执行A.pow(2)操作后,返回的张量形状仍然为(m, n),且每个元素都是原来的平方。
在FM模型中,需要对embedding层的输出进行平方操作,这里使用A.pow(2)可以实现平方的操作。而如果使用torch.matmul(A, A)的话,会导致维度错误,因为矩阵乘法要求输入的两个矩阵的形状要满足一定的条件。
5. get_chunk:读取部分数据集
data_df = pd.read_csv(file, sep='\t', iterator=True, header=None, names=names)
train_df = train_df.get_chunk(1600)
a. names参数要自定义
b. 用df.get_chunk(1600)来自定义选取多少行,上述代码是选取了1600行
6. insert:在Dataframe中插入新的一列
df.insert(loc=0, column='Label', value=-1)
#表示在第一列插入列名为Label、值为-1的列
7. drop:去掉id列
data.drop(['id'], axis=1, inplace=True)
在一般的数据集中,可以把’id’列去掉,避免影响后续对df的操作
但在原始的criteo数据集中不需要,因为criteo中没有id列
8. 自定义Dataset列
a. init 函数要有返回值 return;
b. 必须要实现 len 和 getitem 函数
c. 没有 drop 函数
d. getitem(self, idx) 是 Dataset 类中的一个方法,用于定义如何通过索引获取数据集中的样本。
具体来说,getitem(self, idx) 方法应该接受一个索引值 idx,返回该索引对应的一个样本。这个样本可以是一个元组或字典,其中包含了输入特征和对应的标签。当使用 PyTorch 中的 DataLoader 加载数据集时,DataLoader 会自动调用 Dataset 的 getitem 方法来获取每一个样本。在训练时,DataLoader 会自动地对数据进行 shuffle 和 batch,以提高模型的训练效率。
d. 突然发现根本没有必要自己定义一部分读取dataset类,巨麻烦,有时间再弄一个,方便日后用
这是已完成的部分:
#读入部分数据
class DataReadPart(Dataset):
def __init__(self, file, read_part=True, sample_num=10000):
self.read_part = read_part
self.file = file
self.sample_num = sample_num
if read_part:
data_df = pd.read_csv(file, sep='\t', iterator=True, header=None, names=names)
data_df = data_df.get_chunk(sample_num)
else:
data_df = pd.read_csv(file, sep='\t', header=None, names=names)
#在读入数据时就把id列去掉
self.data_df = data_df.drop(['id'], axis=1)
#读取数据
train_df = DataReadPart(data_path + 'train.txt', read_part=True, sample_num=1600)
test_df = DataReadPart(data_path + 'test.txt', read_part=True, sample_num=400)
#对整体数据集处理
df.test['Label'] = -1
data = pd.concat([df_train, df_test], axis = 0)
data.fillna(-1, inplace = true)
#对特征的处理
cols = data.columns.values
dense_feats = [f for f in cols if f[0] == 'I']
sparse_feats = [f for f in cols if f[0] == 'C'