新闻详情

新闻详情

首页 / 资讯中心 / 详情

结构化数据深度学习实战:PyTorch从零搭建表格模型

发布时间:2026/10/1 4:05:13来源:尧图网络
结构化数据深度学习实战:PyTorch从零搭建表格模型
先说个现象这几年聊到深度学习大家条件反射想到的就是图像识别、目标检测、人脸生成、大语言模型这些。但你要是告诉别人自己在用深度学习处理“结构化数据”很多人的第一反应往往是“这玩意儿不是XGBoost和LightGBM的天下吗深度学习去了能干嘛”。说实话我以前也这么想直到在一个广告点击率预估项目里被数据量逼着把MLP搬上生产才发现表格数据配深度学习并没有那么不堪只是它的用法跟CV、NLP完全不一样。这篇是“结构化数据的深度学习”系列第一篇先把地基打好结构化数据到底适不适合上深度学习核心设计思路是什么用PyTorch怎么从零搭一个能跑的模型。不管你是被树模型精度卡住想找突破口的算法工程师还是刚接触深度学习但手里只有一张张表格的学生党这篇文章都会对你有用——尤其适合那些真正拿到数据、准备动手训练而不是只想看理论综述的人。1. 先说结论结构化数据到底要不要上深度学习1.1 什么时候值得用深度学习先回答一个值不值得的问题在表格数据上树模型长期霸榜是有原因的。表格数据的特征通常是异构的有的列是连续值有的列是离散类别还有的列存在大量缺失。树模型靠分裂准则自动做特征选择对数值尺度不敏感也不需要归一化几千行数据就能训出一个还不错的baseline。相比之下神经网络最怕的就是这种“每列一个语义”的异构数据稍不注意就陷入梯度不稳定、过拟合、训练半天不如一棵树的尴尬。但这不代表深度学习在结构化数据上就没有用武之地。我自己的判断标准很简单满足下面任意两三条就值得试一下深度模型样本量比较大至少在十万量级甚至更高特征维度高尤其是类别特征丰富或者有高基数类别特征特征之间存在复杂的高阶交互比如推荐场景里“用户特征 × 物品特征 × 上下文特征”的组合业务有在线学习、增量更新、低延迟推理的需求树模型在这种场景下要么更新成本高要么推理太慢需要把一个表格模型和文本、图像模型联合训练做成多模态模型。反过来如果你的数据只有几千行、十几个特征那我的建议很直接老老实实去调LightGBM把早停、叶子数、学习率这些参数调好收益远大于硬上深度学习。这是成本收益问题不是技术优劣问题。1.2 树模型与深度模型差在哪帮你理解“为什么深度学习也能打”想用深度学习做好结构化数据不能只会跑代码得先理解两类模型看数据的方式完全不同。树模型每做一次分裂都是基于一个特征的阈值判断它的特征交叉是局部的、贪婪的。比如“年龄大于30且收入大于2万”这种组合树能通过连续分裂自动发现但这种组合只能对训练集里出现过的区间生效遇到没见过的组合区间泛化能力有限。神经网络则不同它把特征映射到连续向量空间通过非线性激活函数拟合一个平滑的函数曲面即使某个特征组合在训练集里没出现过模型也能根据附近样本插值出一个结果。树模型还有一个特点它对特征数值大小不敏感不需要归一化缺失值也能直接用。深度学习则完全反过来输入特征的尺度如果差几个数量级梯度更新会非常不稳定。这也是很多人第一次用深度学习跑表格数据效果很差的核心原因之一不是模型不行是你没按深度学习的规矩喂数据。还有一个经常被忽略的差异树模型很难利用预训练表示每个任务都得从零开始学特征分裂。而深度学习可以通过Embedding把离散特征映射成稠密向量这个向量本身带有“语义”信息甚至可以做迁移学习。比如在推荐系统里用户ID的Embedding向量可以在多个任务之间共享。结论是树模型和深度模型的关系不是替代而是互补。深度模型的优势在“表示学习”和“组合泛化”代价是“需要更多数据、更精细的训练控制”。想明白这一点后面所有操作就都有了解释。2. 核心设计第一步把离散类别特征翻译成模型能懂的语言2.1 为什么要用Embedding而不是OneHot处理结构化数据时最容易被新手搞砸的就是类别特征。刚学深度学习的人看到“城市”“性别”“用户ID”这类字段第一反应往往是做OneHot编码。如果类别少比如性别只有男女OneHot没问题。但遇到“城市”“用户ID”“商品ID”这种动辄几千上万类别的特征OneHot会直接把特征维度炸到几万甚至几十万后续全连接层的参数量也跟着爆炸训练速度和显存都扛不住。OneHot更大的问题是它假设所有类别之间是完全独立的。你用OneHot编码北京和上海两个向量之间的夹角是90度模型完全不知道它们在地理和文化上是接近的。Embedding不一样它本质上是一张可训练的查找表每个类别ID对应一个稠密向量比如32维或64维。模型在训练过程中会根据损失函数自动调整这些向量让“行为相似”的类别在向量空间里靠得更近。用一个生活化的类比OneHot像给每个城市单独设一个开关1000个城市就有1000个开关每个开关只能开或关。Embedding像把1000个城市画到一张地图上北京、天津这两个城市在地图上的位置天然靠近模型也能感受到这种“接近”。所以在结构化数据深度学习里类别特征统一走“LabelEncoder Embedding”这条路线基本是标配。这也是整个模型设计里最核心的一个环节。2.2 Embedding维度到底选多少给一个能直接用的经验值Embedding维度怎么选网上说法很多有的给公式有的给经验表。我自己用下来最稳的还是按类别基数分档类别数范围建议Embedding维度少于104到810到1008到16100到100016到321000以上32到64当然也有经验公式比如min(50, (类别数1)//2)或者6 * (类别数 ** 0.25)这两个公式算出来的结果跟上面分档比较接近可以作为初始值。我的习惯是基数大的特征维度给高一点但很少超过64。过高的Embedding维度不仅增加参数还容易过拟合尤其是那些稀有类别本身样本就少高维向量很容易把噪声也学进去。还有一个必须处理的细节就是稀有类别和新类别。训练集里那些只出现了一两次的类别建议直接合并成一个专门的“unknown”类别给它一个固定的ID。这样有两层好处一是避免模型为这些低频类别学出非常不稳定的向量二是在线上推理时遇到训练集里没见过的ID可以直接映射到unknown而不是让程序崩溃。处理方式是在编码类别特征时统计每个类别的出现次数小于阈值比如5次的都归为unknown同时预留一个unknown的ID位置。顺便提一句如果你觉得纯Embedding还不够可以把类别特征的频率统计Count Encoding也拼到数值特征里去。比如“该城市在训练集中出现了多少次”这个统计值有时候能提供Embedding学不到的全局信息。3. 动手前必须做好的数据预处理和特征工程3.1 数值特征归一化不是可选项是必选项如果你做过图像或文本任务可能会觉得数据预处理无非就是转转格式。但结构化数据不一样数值特征的分布千奇百怪有年龄这种尺度的也有收入这种动辄几万几十万的还有那种长尾分布特别严重的“点击次数”“历史消费金额”。神经网络优化基于梯度下降当不同特征的数值尺度差出几个数量级时梯度的量级也会失衡训练过程要么震荡要么收敛极慢。所以数值特征必须做归一化。我默认用的是StandardScaler也就是把特征标准化成均值为0、方差为1。如果你的特征里有明显异常值比如某个用户消费金额比其他人高出几个数量级StandardScaler会被异常值带偏这时候用RobustScaler更稳它基于分位数对异常值没那么敏感。还有一类偏态分布特别明显的特征比如“距上次登录的秒数”我习惯先取log再标准化效果往往比直接标准化好。归一化这一步有一个极其容易踩的坑标准化参数只能在训练集上计算验证集和测试集必须复用训练集的均值和方差不能单独算。一旦单独算等于把验证集的信息泄漏到了训练过程中你的离线指标会虚高线上表现却拉胯。这个坑我在刚入行的时候踩过后来在代码里固定写成“先fit训练集再transform所有集合”再没犯过。缺失值方面树模型天然支持缺失值但神经网络输入层不能有NaN。最简单的做法是用中位数填充比均值更抗异常值。如果业务上缺失本身有含义比如“这个用户从未登录过”我还会加一个is_missing标志位特征让模型自己去学缺失到底意味着什么。3.2 类别特征LabelEncoder就行但要注意稀有类别和新类别LabelEncoder很多人都用过但有个常见误解有人担心LabelEncoder会给类别强加顺序关系比如把“北京”编码成0“上海”编码成1模型会不会觉得上海比北京“大”这个问题在树模型里确实需要小心但在深度学习里完全不用担心因为LabelEncoder的输出后面紧接着是Embedding层Embedding会把这些ID映射到独立的向量模型并不会直接把ID数值当作有序关系来理解。ID只是查表的索引仅此而已。实操顺序是先用LabelEncoder把类别转成整数ID然后把ID输入Embedding层。注意LabelEncoder拟合时一定要先统计稀有类别把它们在编码前就归并到unknown类别里。否则训练集里没见过的ID在预测时会被LabelEncoder当作新值处理直接报错。顺便说一个我在业务里经常用的做法对于“用户ID”“设备ID”这种极高基数的特征不要直接把原始ID编码而是先做一层业务含义的聚合。比如把“用户ID”聚合成“该用户的最近购买品类数”“该用户的活跃天数”等统计特征模型更好学泛化也更好。这是特征工程的活但配合Embedding使用效果加倍。3.3 时间、多值和不规则特征怎么处理很多表格数据里会混着时间戳、行为序列这类不规则特征处理起来比较灵活但有几个原则可以套。时间特征我一般会拆成年、月、日、星期几、是否节假日、是否周末以及相对某个参考时间的差值。神经网络自己是学不会“2024年3月15日是星期五”这种规律的你帮它把时间拆好了它就非常容易拟合周期性和季节性趋势。多值特征比如“用户最近浏览过的10个商品ID”这种没法直接塞进表格的一列。一个简单有效的做法是把它变成多个ID的列表每个ID过一个Embedding把多个向量取平均或者求和得到定长向量后再拼接到其他特征里。这个方法业界叫“集合Embedding”效果稳定实现也简单。还有一类特征是历史行为统计比如“用户最近7天购买金额”“商品最近24小时曝光数”。这类特征对业务效果提升非常明显但要注意计算时只能用历史数据不能用未来数据否则就是典型的时序泄漏。很多线上模型效果崩了查到最后都是这类统计特征出了问题。4. 用PyTorch从零搭一个结构化数据模型环境这块我不想多说建议用Miniconda建一个干净的Python3.10环境装好PyTorch的CPU版就能跑完这篇文章里所有内容。等真要上大数据了再考虑GPU和云平台。对结构化数据深度学习来说瓶颈往往不在算力在处理流程。4.1 写好一份可靠的Dataset类用PyTorch建模型第一步是写好Dataset。我的习惯是让它接收一个DataFrame和特征列配置内部把特征分成数值列和类别列类别列预先做好的ID编码直接读进来。一个模板大概长这样import torch from torch.utils.data import Dataset import pandas as pd import numpy as np class TabularDataset(Dataset): def __init__(self, df, num_cols, cat_cols, label_col): self.num_cols num_cols self.cat_cols cat_cols self.num_data df[num_cols].values.astype(np.float32) self.cat_data df[cat_cols].values.astype(np.int64) self.labels df[label_col].values.astype(np.float32) def __len__(self): return len(self.labels) def __getitem__(self, idx): x_num torch.from_numpy(self.num_data[idx]) x_cat torch.from_numpy(self.cat_data[idx]) y torch.tensor(self.labels[idx]) return (x_num, x_cat), y这里有个细节类别列的数据类型必须是整型因为后面要拿它做Embedding的索引数值列用浮点型。如果类别列读进来是字符串记得在Dataset外面统一编码好。4.2 模型主体Embedding加数值输入的双通道DNN模型主体我习惯分两条通道处理数值特征经过标准化后直接进入全连接层类别特征先查Embedding表把每个类别特征对应的Embedding向量拼接起来。两条通道拼接后再一起进入若干层全连接。下面是核心的模型代码import torch.nn as nn class TabularModel(nn.Module): def __init__(self, num_cols, cat_config): super().__init__() self.embeddings nn.ModuleList() for cat_name, vocab_size, emb_dim in cat_config: self.embeddings.append(nn.Embedding(vocab_size, emb_dim)) cat_dim sum(emb_dim for _, _, emb_dim in cat_config) input_dim num_cols cat_dim hidden_layers [512, 256] layers [] in_dim input_dim for h in hidden_layers: layers.append(nn.Linear(in_dim, h)) layers.append(nn.BatchNorm1d(h)) layers.append(nn.ReLU(inplaceTrue)) layers.append(nn.Dropout(0.3)) in_dim h layers.append(nn.Linear(in_dim, 1)) self.mlp nn.Sequential(*layers) def forward(self, x_num, x_cat): cat_embs [] for i, emb in enumerate(self.embeddings): cat_embs.append(emb(x_cat[:, i])) x torch.cat([x_num] cat_embs, dim1) out self.mlp(x) return out.squeeze(-1)关于BatchNorm的位置很多初学者喜欢把BN放在激活函数之后其实对于MLP来说放在线性层之后、激活函数之前更常见。原因是BN要归一化的是线性输出的分布让激活函数输入保持在合适的区间。Dropout放在激活函数之后随机丢弃激活值用来防止神经元之间产生过于复杂的协同适应。4.3 训练配置优化器、损失与验证集划分损失函数按任务定二分类用BCEWithLogitsLoss多分类用CrossEntropyLoss回归用MSELoss或MAELoss。我在二分类任务里特别强调用BCEWithLogitsLoss因为它在内部把Sigmoid和交叉熵合并了数值上更稳定。如果自己先Sigmoid再算BCE容易出现梯度消失或者数值不稳定。优化器首选AdamW初始学习率在1e-3左右比较安全。这里注意AdamW和Adam在权重衰减的实现上不一样AdamW把权重衰减和自适应学习率解耦泛化上通常更好尤其是配合Transformer类模型时。如果训练震荡可以加一个学习率衰减比如ReduceLROnPlateau等验证损失连续几个epoch不降时把学习率降一半。验证集划分这块最有讲究。普通分类任务直接随机切就行但很多结构化数据场景其实不是纯随机。比如广告点击率数据同一个用户可能出现在多行如果你随机切分同一个用户的样本会同时出现在训练集和验证集里模型学会了记住用户ID对应的行为模式验证集指标虚高上线后就崩了。正确做法是按用户ID分组划分比如用GroupKFold保证同一个用户的样本不会横跨训练集和验证集。如果是时间序列数据直接用时间切分拿前面的数据训练后面的数据做验证别随机。5. 训练踩坑实录与参数调节心得5.1 最容易被忽略的坑验证集里混进同一个用户的数据上面已经提到group split但我还是要单独拿出来说因为这是我实际踩过最深的一个坑。有一次做一个用户行为预测项目线下AUC到了0.86看起来非常漂亮结果上线后AUC只有0.72。查了很久最后发现问题出在数据划分上同一个用户的多个会话被打散到了训练集和验证集里。模型在训练时见过了这个用户的行为模式验证时直接“开卷考试”。排查的方法是统计训练集和验证集之间是否有相同的用户ID发现重合率超过30%。修好后按用户分组重划分线下AUC掉到0.80但线上稳定在0.79。这个教训让我从此在拿到任何业务数据时先问清楚哪一列是实体ID再谈模型训练。另一个类似的问题是时间泄漏。某些统计特征比如“用户历史购买次数”如果是在全量数据上计算的那么验证集样本的该特征已经包含了未来信息。正确做法是按截止时间滚动计算。5.2 控制过拟合的三个实用手段结构化数据上的深度模型过拟合来得比CV、NLP快最常见的原因是特征维度高、样本量不够、Embedding把稀有类别的噪声也背下来了。我的三板斧如下第一招是Early Stopping。监控验证集losspatience设10到20个epoch一旦连续多个epoch验证loss不再下降就停止训练同时保存验证集表现最好的那一次权重。这项操作用得好的话基本能挡住一半以上的过拟合问题。第二招是加大正则和降低模型容量。Dropout调到0.3到0.5weight_decay设在1e-5到1e-3之间隐藏层宽度从512降到256或者128Embedding维度砍半。先把模型容量降下来看验证指标是否反而上升。很多时候结构化数据用不着特别宽的网络。第三招是特征层面的约束。把低频类别合并到unknown把高频类别对应的Embedding向量限制在较小维度对数值特征加一点点高斯噪声幅度0.01左右这种轻度的数据增强有时候能带来1到2个点的提升。5.3 我实测过的一组基线参数可直接抄作业如果你不想一上来就陷入调参的泥潭这组参数是我在多个表格数据集上验证过的baseline直接照抄大概率能跑出一个不错的开局配置项取值优化器AdamW初始学习率1e-3学习率策略ReduceLROnPlateaupatience5, factor0.5Batch Size256隐藏层[512, 256]激活函数ReLU归一化BatchNorm1dDropout0.3权重衰减1e-4Embedding维度按第2节分档Early Stoppingpatience20最大Epoch100这组参数在中等规模数据上大概二三十个epoch就能收敛到接近调好参数LightGBM的水平。如果数据本身特征交互很复杂、样本量又大深度模型反超树模型也正常。我的一个真实感受是不要指望一次就把模型调到最好先用这组参数看训练曲线再针对性地动其中一两个旋钮比盲目grid search高效得多。6. 常见问题排查速查表训练结构化数据深度模型时容易遇到的问题翻来覆去就那么几个。我整理了一张排查表你可以直接贴在工位上现象可能原因建议对策Loss不降学习率过大或过小先把学习率调到1e-3再看Loss曲线形状Loss震荡剧烈Batch Size太小、学习率太高Batch调到256或512学习率减半验证集指标远高于线上数据划分泄漏检查是否按用户分组是否用了未来统计特征模型不如树模型数据量不足、特征交互不够显式先用MLP做baseline再考虑DCN或TabNetOneHot后维度爆炸类别特征过多换成LabelEncoder Embedding训练时显存爆掉Embedding维度太高或类别过多压缩低频类别到unknown降低Embedding维度推理时遇到新类别ID编码映射没有保留unknown位预留unknown ID并合并不常见类别多值特征处理不当直接把列表塞进模型用集合Embedding做平均池化类别不平衡影响训练少数类样本太少用WeightedRandomSampler或在Loss里加class weight数值特征不起作用没做归一化、特征偏态严重改用RobustScaler长尾特征取log最后面那两个问题很多人会忽略。多值特征如果用集合Embedding直接平均有可能把重要信息稀释掉比如用户浏览了10个商品其中一个碰巧是爆款平均之后这个信号就没了。这种情况下可以考虑加权平均权重单独学我们后续文章会展开。类别不平衡时除了改采样还可以试一下用pos_weight参数调整BCE损失的权重。7. 顺带澄清两个概念模型的“参数”和深度学习的范畴7.1 参数量、文件大小和内存占用不是一回事不少新手看到模型文件几百MB或者听别人说“这个模型有1000万参数”会直接把参数量和内存大小画等号然后冒出类似“parameter应该不是MB吧”的疑惑。这里简单算一笔账就清楚了。一个模型有P个参数如果每个参数用FP324字节存储那么权重文件的理论大小是P×4字节。1000万参数对应40MB这是只存权重的情况。如果转成FP162字节就是20MB。但推理时的显存占用不仅包括权重还包括中间激活值、梯度训练时、优化器状态比如Adam的动量项通常是权重的两倍。所以一个1000万参数的模型训练时显存占用是远超40MB的可能到几百MB。换句话说参数的单位是“数量”内存的单位是“容量”两者通过位宽换算。以后看到模型文件大小先数数参数量再乘上精度对应的字节数就不会被绕晕了。7.2 LLM到底算不算深度学习这个热搜词背后应该是很多人被“大语言模型”这个概念绕晕了不确定它跟深度学习的边界。答案很干脆LLM是深度学习而且是深度学习在自然语言处理领域最出圈的成果。它本质上是一个超大规模的深度神经网络核心是Transformer架构训练方式遵循的还是反向传播和梯度下降那一套基础框架。之所以单独提这个是因为有些做结构化数据的人会觉得深度学习只属于图像和语言与表格数据无关。看LLM的作用是打破这种偏见深度学习的核心能力是“用深层神经网络去逼近复杂函数”只要你的表格数据之间确实存在复杂函数关系深度学习就有发挥空间。这是我在这个系列文章里反复强调的底层逻辑。我个人的体会是结构化数据的深度学习不是用来“取代”树模型的而是工具箱里多了一个真正能打的武器。如果你手头只有几千行数据别急着上深度模型一旦数据量上来、特征复杂、业务又需要在线推理深度模型的优势会明显很多。我自己把上面这套流程跑顺之后再回头看发现真正难的从来不是写模型代码而是理解数据形态、防止泄漏、控制过拟合这些“看不见”的功夫。这个系列后面我打算接着写多值特征怎么做、模型怎么压缩上线、以及怎么把深度模型和树模型做融合如果你已经在企业内部推过表格数据深度模型欢迎留言交流你遇到过的坑。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

卡尔曼滤波器在嵌入式系统中的工程实践与实时优化 2026/10/1 5:58:42

卡尔曼滤波器在嵌入式系统中的工程实践与实时优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
微信开源知识库项目:RAG私有化部署与文档解析实战 2026/10/1 5:58:42

微信开源知识库项目:RAG私有化部署与文档解析实战

微信开源了一个知识库项目,这事情我一开始没当回事,直到我把仓库代码拉下来跑通之后,才意识到这不仅是又一个RAG套壳,而是把企业里做知识库最常见的那些坑,比如文档解析、切片策略、引用溯源、权限隔离,一次…

阅读更多 →
Windows OEM激活机制详解:SLP、NSLP、COA与DM全解析 2026/10/1 5:58:35

Windows OEM激活机制详解:SLP、NSLP、COA与DM全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
拯救者R9000X触控板失灵与黑屏背光亮?I2C HID与EC复位排查指南 2026/10/1 5:58:22

拯救者R9000X触控板失灵与黑屏背光亮?I2C HID与EC复位排查指南

联想拯救者R9000X 2021这台本子,我最近连着收到三台同样问题的机器,症状高度统一:触控板在设备管理器里直接变成I2C HID设备缺失,或者带着一个黄色感叹号,与此同时屏幕开机黑屏但背光是亮的,内容一点不显示…

阅读更多 →
一个人如何搭建AI智能体团队?五角色协作实战指南 2026/10/1 5:58:22

一个人如何搭建AI智能体团队?五角色协作实战指南

1. 为什么我要折腾“一个人的 AI 团队”去年年底我接了一个私活,客户要求两周内交付一套带数据分析、文案生成、竞品监控和自动回复的运营中台。预算只够我一个人干,时间紧到连需求评审都省了。当时我第一反应不是加班,而是——能不能让几个 …

阅读更多 →
XPS分峰拟合全流程详解:从荷电校正到参数约束 2026/10/1 5:58:21

XPS分峰拟合全流程详解:从荷电校正到参数约束

XPS原始数据分峰拟合这件事,说难不难,说简单也远没到能随手拉个软件点两下就完事的程度。我这些年帮不同课题组处理过几百张XPS原始数据的分峰拟合,见过太多同学卡在“测试报告拿到手、图谱也导出来了、打开软件却不知道怎么下手”这个环节。…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉