作者 | Aakanksha NS
来源 | Medium
编辑 | 代码医生团队
使用表格数据进行深度学习的最简单方法是通过fast-ai库,它可以提供非常好的结果,但是对于试图了解幕后实际情况的人来说,它可能有点抽象。因此在本文中,介绍了如何在Pytorch中针对多类分类问题构建简单的深度学习模型来处理表格数据。
Pytorch是一个流行的开源机器库。它像Python一样易于使用和学习。使用PyTorch的其他一些优势是其多GPU支持和自定义数据加载器。
代码
https://jovian.ml/aakanksha-ns/shelter-outcome
数据集
https://www.kaggle.com/c/shelter-animal-outcomes/data
它是一个表格数据集,由训练集中的约26k行和10列组成。除以外的所有列DateTime都是分类的。
训练样本数据
问题陈述
根据保护动物的某些特征(例如年龄,性别,肤色,品种),预测其结果。
有5种可能的结果:Return_to_owner, Euthanasia, Adoption, Transfer, Died。期望找到动物的结局属于5类中每一种的概率。
数据预处理
尽管此步骤很大程度上取决于特定的数据和问题,但仍需要遵循两个必要的步骤:
摆脱Nan价值观:
Nan(不是数字)表示数据集中缺少值。该模型不接受Nan值,因此必须删除或替换它们。
对于数字列,一种常见的处理这些值的方法是使用剩余数据的0,均值,中位数,众数或其他某种函数来估算它们。缺失值有时可能表示数据集中的基础特征,因此人们经常创建一个新的二进制列,该列与具有缺失值的列相对应,以记录数据是否缺失。
对于分类列,Nan可以将值视为自己的类别!
标签编码所有分类列:
由于模型只能接受数字输入,因此将所有分类元素都转换为数字。这意味着使用数字代替使用字符串来表示类别。选择用来表示列中任何类别的数字并不重要,因为稍后将使用分类嵌入来进一步编码这些类别。这是标签编码的一个简单示例:
使用了LabelEncoderscikit-learn库中的类对分类列进行编码。可以定义一个自定义类来执行此操作并跟踪类别标签,因为也需要它们对测试数据进行编码。
标签编码目标:
如果目标具有字符串条目,还需要对目标进行标签编码。另外请确保维护一个字典,将编码映射到原始值,因为将需要它来找出模型的最终输出。
保护成果问题特有的数据处理:
除了上述步骤,还对示例问题进行了更多处理。
注意:在NoteBook中,堆叠了train和test列,然后进行了预处理以避免基于测试集上的train set标签进行标签编码(因为这将涉及维护编码标签到实际值的字典) 。可以在此处进行堆栈和处理,因为没有数字列(因此无需进行插补),并且每列的类别数是固定的。实际上,绝对不能这样做,因为它可能会将某些数据从测试/验证集中泄漏到训练数据中,并导致模型评估不准确。例如如果数字列中缺少值,例如age 并决定使用平均值来推算该平均值,则平均值应仅在训练集合(而不是堆叠的训练测试有效集合)上计算,并且该值也应用于推算验证和测试集中的缺失值。
分类嵌入
分类嵌入与NLP中常用的词嵌入非常相似。基本思想是在列中具有每个类别的固定长度矢量表示。这与单次编码的不同之处在于,使用嵌入而不是使用稀疏矩阵,而是为每个类别获得了一个密集矩阵,其中相似类别的值在嵌入空间中彼此接近。因此,此过程不仅节省了内存(因为具有太多类别的列的一键编码实际上会炸毁输入矩阵,而且它是非常稀疏的矩阵),而且还揭示了分类变量的内在属性。
例如,如果有一列颜色,并且找到了它的嵌入,则可以期望red并且pink在嵌入空间中,该距离比red和更近。blue
分类嵌入层等效于每个单编码输入上方的额外层:
资料来源:分类变量的实体嵌入研究论文
对于保护所结果问题,只有分类列,但将考虑少于3个值的列为连续列。为了确定每一列嵌入向量的长度,从fast-ai库中获取了一个简单的函数:
#categorical embedding for columns having more than two values
emb_c = {n: len(col.cat.categories) for n,col in X.items() if len(col.cat.categories) > 2}
emb_cols = emb_c.keys() # names of columns chosen for embedding
emb_szs = [(c, min(50, (c+1)//2)) for _,c in emb_c.items()] #embedding sizes for the chosen columns
Pytorch数据集和DataLoader
扩展了DatasetPytorch提供的(抽象)类,以便在训练时更轻松地访问数据集并有效使用DataLoader模块来管理批次。这涉及根据特定数据集覆盖__len__和__getitem__方法。
由于只需要嵌入分类列,因此将输入分为两部分:数字部分和分类部分。
class ShelterOutcomeDataset(Dataset):
def __init__(self, X, Y, emb_cols):
X = X.copy()
self.X1 = X.loc[:,emb_cols].copy().values.astype(np.int64) #categorical columns
self.X2 = X.drop(columns=emb_cols).copy().values.astype(np.float32) #numerical columns
self.y = Y
def __len__(self):
return len(self.y)
def __getitem__(self, idx):
return self.X1[idx], self.X2[idx], self.y[idx]
然后,选择批处理大小,并将其与数据集一起馈入DataLoader。深度学习通常是分批进行的。DataLoader帮助在训练之前有效地管理这些批次并重新整理数据。
#creating train and valid datasets
train_ds = ShelterOutcomeDataset(X_train, y_train, emb_cols)
valid_ds = ShelterOutcomeDataset(X_val, y_val, emb_cols)
batch_size = 1000
train_dl = DataLoader(train_ds, batch_size=batch_size,shuffle=True)
valid_dl = DataLoader(valid_ds, batch_size=batch_size,shuffle=True)
要进行健全性检查,可以遍历创建的DataLoader以查看每个批次:
模型
数据分为连续的和分类的部分。首先根据先前确定的大小将分类部分转换为嵌入向量,然后将它们与连续部分连接起来,以馈送到网络的其余部分。这张照片演示了使用的模型:
class ShelterOutcomeModel(nn.Module):
def __init__(self, embedding_sizes, n_cont):
super().__init__()
self.embeddings = nn.ModuleList([nn.Embedding(categories, size) for categories,size in embedding_sizes])
n_emb = sum(e.embedding_dim for e in self.embeddings) #length of all embeddings combined
self.n_emb, self.n_cont = n_emb, n_cont
self.lin1 = nn.Linear(self.n_emb + self.n_cont, 200)
self.lin2 = nn.Linear(200, 70)
self.lin3 = nn.Linear(70, 5)
self.bn1 = nn.BatchNorm1d(self.n_cont)
self.bn2 = nn.BatchNorm1d(200)
self.bn3 = nn.BatchNorm1d(70)
self.emb_drop = nn.Dropout(0.6)
self.drops = nn.Dropout(0.3)
def forward(self, x_cat, x_cont):
x = [e(x_cat[:,i]) for i,e in enumerate(self.embeddings)]
x = torch.cat(x, 1)
x = self.emb_drop(x)
x2 = self.bn1(x_cont)
x = torch.cat([x, x2], 1)
x = F.relu(self.lin1(x))
x = self.drops(x)
x = self.bn2(x)
x = F.relu(self.lin2(x))
x = self.drops(x)
x = self.bn3(x)
x = self.lin3(x)
return x
训练
现在在训练集上训练模型。使用了Adam优化器来优化交叉熵损失。训练非常简单:遍历每批,进行前向遍历,计算梯度,进行梯度下降,并根据需要重复此过程。可以看一下NoteBook以了解代码。
https://jovian.ml/aakanksha-ns/shelter-outcome
测试输出
由于有兴趣查找测试输入的每个类别的概率,因此在模型输出上应用Softmax函数。还进行了Kaggle提交,以查看此模型的性能如何:
仅进行了很少的功能工程和数据探索,并使用了非常基础的深度学习架构,但模型完成了约50%的解决方案。这表明使用神经网络对表格数据建模的这种方法非常强大!