【AI量化投研】- Modeling(二, ResNet)
本文总结了基于Swin Transformer模型的AI量化投研建模失败案例。两次训练(5000+和3800+样本)均显示训练损失下降但验证集完全不收敛,模型未能学到有效特征。原因分析包括:1)数据质量问题(特征-标签关联弱/噪声大);2)模型架构不匹配(896×896输入可能过大);3)训练策略问题(学习率/批量大小不当);4)模型过复杂且正则化不足。修正方案着重调整正则化参数(drop_rat
【AI量化投研】- Modeling(二, ResNet)
- 背景
- 修正
-
- 训练策略修正
-
- 1. 正则化
- 2. 换ResNet
- 3. 新问题(储存爆炸, 压缩算法 LZMA vs ZStandard)
- 4. ResNet 训练
背景
基于开源项目的神经网络(Swin_Transformer)结构进行修改完善后训练,结果是如此地失败。
第一次训练完成(5000+样本),效果如下:
训练看似没有大问题,虽然损失函数下降有限。样本外验证却问题很大,损失函数无法下降。并且,训练和验证之间的gap值差很大。总体建模提升极小。
第二次训练完成(3800+样本),效果如下:
训练结果尚可,但样本外完全不行,豪不收敛。
其中,相同代码下,20-80个样本的训练结果大致如下:
看起来,小样本可训练收敛,大样本不收敛。然而实际上,最终的损失停留点都是在1.09附近,所以小样本下损失函数开始较高,应可被认为主要是样本偏差带来。
训练小结
整体训练失败,核查并修改过整体的数据生成过程,训练过程也做了检查,很可惜,目前看训练有效,测试无效。训练有下降,验证无下降,模型没有学到有效的泛化特征。学到了噪声的模式。训练失败。
原因分析
- 数据质量问题(最可能)
输入特征与标签关联性弱:图像特征可能无法有效预测目标变量
标签噪声大:目标变量本身包含大量噪声
数据分布异常:训练集和验证集分布不一致 - 模型架构不匹配
Swin Transformer可能不适合此任务:对于某些类型的金融/时序数据,CNN架构可能不是最优选择
输入尺寸处理不当:896×896的输入可能过大,丢失了重要细节 - 训练策略问题
学习率设置不当:可能过大导致震荡,或过小导致收敛缓慢
批量大小不合适:batch_size=4可能太小,梯度估计噪声大 - 元宝认为,模型过于复杂,正则化不足。
修正
训练策略修正
1. 正则化
model = SwinTransformer(
img_size=(384,768),
patch_size=4,
in_chans=64,
num_classes=1,
embed_dim=96, # 小幅升级到128,精度更高,速度只掉10%
depths=[2, 2, 6, 2],
num_heads=[4, 8, 16, 32],
window_size=7,
drop_path_rate=0.3,
use_checkpoint=True
).to(device)
中的参数,如下参数是正则化涉及的:
# 🔧 正则化参数调整(核心修改)
drop_rate=0.1, # 原为0,增加全局Dropout [1,5](@ref)
attn_drop_rate=0.1, # 原为0,增加注意力Dropout [1,5](@ref)
drop_path_rate=0.2, # 原为0.1,增加随机深度 [3,5](@ref)
下一步,先换模型试试。
2. 换ResNet
折腾了一点时间,自己下载github源码来一通操作后,发现,不如:
from torchvision.models import resnet50
来得快且标准。当然,基于此,我需要修改其中的输入和输出如下,以确保适应我们当前场景和数据:
def create_custom_resnet50():
"""创建适配64通道输入的ResNet50"""
# 1. 创建基础模型
model = resnet50(
weights=None,
num_classes=1,
zero_init_residual=True
)
# 2. 修改第一层卷积以适应64通道输入
original_conv1 = model.conv1
new_conv1 = nn.Conv2d(
in_channels=64, # 修改为64通道输入
out_channels=original_conv1.out_channels, # 保持原输出通道数(64)
kernel_size=original_conv1.kernel_size,
stride=original_conv1.stride,
padding=original_conv1.padding,
bias=original_conv1.bias is not None
)
# 3. 正确初始化新卷积层
nn.init.kaiming_normal_(
new_conv1.weight,
mode='fan_out',
nonlinearity='relu'
)
if new_conv1.bias is not None:
nn.init.constant_(new_conv1.bias, 0)
model.conv1 = new_conv1
# 4. 验证模型
print(f"修改后的conv1: {model.conv1}")
print(f"权重形状: {model.conv1.weight.shape}") # 应该是 [64, 64, 7, 7]
return model
于是,成功用 resnet 换了 Swin_Transformer,基于resnet 做了多次训练尝试,有如下结果:(3800+样本量)
虽然波动有点大,但至少终于看到一条接近有意义的损失函数曲线了。还有这样一种曲线:
我理解中间可能存在致命性问题,但Grok-4的分析刷新我观点:
Grok-4:
不,训练损失和验证损失之间的差异(训练损失约为0.7-0.9,验证损失约为0.05-0.1,在20个epoch后)表明可能存在正则化或数据处理差异,而不是模型的基本缺陷。研究表明,这种模式在深度学习中很常见,通常反映了有意使训练更难的技术,以改善泛化能力。虽然差距看起来很大,但如果验证性能保持强劲,它可能不会严重损害模型的实际效用。
关键见解
可能不是过拟合:与典型过拟合不同(验证损失高于训练损失),这种反向模式指向了提升训练损失但不损害验证的机制。
泛化潜力良好:较低的验证损失意味着模型在新数据上可能表现可靠,尽管高训练损失可能表明有优化空间。
检查平衡:极端差异可能暗示过度正则化或数据不匹配,但证据倾向于在许多情况下这是良性的。
无绝对担忧:研究建议承认这种复杂性以避免过度反应;根据下游指标如测试准确率进行调整。
差异的常见原因
机器学习资源中的证据突出了几个因素,可能导致训练损失明显高于验证损失。这些包括正则化方法,如dropout,它在训练期间启用以防止过拟合,但在验证期间禁用,使验证任务对模型“更容易”。数据增强——如仅应用于训练数据的随机变换——也会增加训练难度,导致训练损失更高,而验证使用未更改的数据。此外,批归一化行为不同:在训练中使用噪声批统计,在验证中使用更平滑的运行平均值。计算时机也起作用,因为训练损失是epoch中间的平均值,而验证使用epoch结束时的完整更新模型。在像Swin Transformers这样的模型中,其MLP层中包含dropout,这种效果在评估模式下被放大。
对最终结果的影响
这种模式通常不会负面影响模型的整体有效性;事实上,它可能表明成功的正则化,模型避免记住训练数据,而是学习可转移到验证或测试集的模式。如果验证损失低且稳定(如橙色曲线所示),模型很可能充分泛化,支持在新数据上的可靠预测。然而,一个数量级的差距可能暗示训练数据上的欠拟合或过度正则化,可能限制峰值性能——尽管这有争议,一些来源认为除非伴随差的测试指标,否则非问题性。要缓解,实践者推荐放松dropout率或验证数据拆分。
在深度学习训练过程中,观察到训练损失显著高于验证损失的实质差距——如提供的曲线中大约一个数量级的差异(20个epoch中训练约为0.7-0.9,验证约为0.05-0.1)——并不罕见,通常源于模型架构和数据处理中的有意设计选择。这种反向模式与经典过拟合场景相反,后者中验证损失因模型记住训练数据中的噪声而高于训练损失。相反,它通常反映了在训练期间有意约束模型以增强泛化的机制,尽管极端差异可能促使诊断检查。平坦的验证曲线和缓慢下降的训练曲线进一步表明在留出数据上的稳定,可能表明尽管有差距,但学习有效。
主要贡献因素是正则化技术,特别是dropout层,它在训练期间随机停用神经元以防止对特定特征的过度依赖,从而提升训练损失,因为模型必须适应不完整信息。在验证期间,使用评估模式,dropout被禁用,允许完整网络利用,通常导致较低损失。这对于像上下文中的Swin Transformer(Swin-Small变体)这样的架构尤其相关,它在其多层感知机(MLP)组件中包含dropout——通常在简单MLP结构中的两个dropout层——以正则化分层视觉处理。类似地,L1/L2权重惩罚或其他正则化形式仅在训练期间添加约束,膨胀那里的损失,而验证从学习的鲁棒性中受益,而无惩罚。
数据相关因素也发挥重要作用。广泛的数据增强,如裁剪、翻转或噪声添加,通常仅应用于训练集以增加多样性和鲁棒性,使训练任务更具挑战性,从而提高损失。验证集缺少这些增强,保持“更干净”且更容易,导致较低损失。在提供的代码片段中,输入裁剪(例如,x_0[:,:,3:-3,11:-11])和加载器中的潜在增强可能加剧此问题。批归一化引入了另一层差异:在训练期间,它使用当前批次的统计进行归一化,这可能变异且噪声;在验证中,它采用累积的运行平均值,导致更稳定且可能更好的性能。训练集和验证集之间的分布不匹配——如验证集更小、更简单,或由于拆分错误而无意中更容易——可能进一步扩大差距,尽管适当的洗牌和分层采样可以缓解此问题。
损失计算的时机添加了微妙但一致的偏差。训练损失通常是epoch内批次的平均值,反映更新中间的模型状态,而验证损失在该周期的完整优化模型上post-epoch计算。将训练曲线偏移半个epoch通常使它们更紧密对齐,如在Fashion MNIST数据集上使用MiniVGGNet的实验所示。不太常见但关键的问题包括实现bug,如日志中交换的标签、不正确的评估模式,或数据泄漏,其中验证样本与训练重叠。社区讨论强调验证代码,如确保model.eval()正确禁用dropout且损失正确平均。
关于最终模型的影响,这种差异经常表明积极泛化,因为较低的验证损失表明模型有效处理未见数据,这是机器学习的核心目标。在使用MSE损失的回归任务中(从代码的nn.MSELoss()推断),像0.05这样的低验证损失表明在留出样本上的准确预测,即使训练损失由于正则化而保持更高。然而,过大的差距可能暗示训练集上的欠拟合,模型未能捕捉所有可用模式,或过度正则化抑制学习——可能减少整体容量,并在复杂场景中导致次优测试性能。如果未解决,这可能通过限制对训练类变异的鲁棒性而影响部署,尽管经验证据显示放松正则化(例如,将dropout概率从0.5降低)或添加容量通常缩小差距而不牺牲泛化。在曲线中,缓慢的训练下降与平坦验证可能反映收敛,但建议监控测试集或额外指标(例如,回归的R²)以确认影响。
为了组织这些因素,下表总结了常见原因、机制以及基于文档实践的潜在补救措施:
不,训练损失和验证损失之间的差异(训练损失约为0.7-0.9,验证损失约为0.05-0.1,在20个epoch后)表明可能存在正则化或数据处理差异,而不是模型的基本缺陷。研究表明,这种模式在深度学习中很常见,通常反映了有意使训练更难的技术,以改善泛化能力。虽然差距看起来很大,但如果验证性能保持强劲,它可能不会严重损害模型的实际效用。
关键见解
可能不是过拟合:与典型过拟合不同(验证损失高于训练损失),这种反向模式指向了提升训练损失但不损害验证的机制。
泛化潜力良好:较低的验证损失意味着模型在新数据上可能表现可靠,尽管高训练损失可能表明有优化空间。
检查平衡:极端差异可能暗示过度正则化或数据不匹配,但证据倾向于在许多情况下这是良性的。
无绝对担忧:研究建议承认这种复杂性以避免过度反应;根据下游指标如测试准确率进行调整。
差异的常见原因
机器学习资源中的证据突出了几个因素,可能导致训练损失明显高于验证损失。这些包括正则化方法,如dropout,它在训练期间启用以防止过拟合,但在验证期间禁用,使验证任务对模型“更容易”。数据增强——如仅应用于训练数据的随机变换——也会增加训练难度,导致训练损失更高,而验证使用未更改的数据。此外,批归一化行为不同:在训练中使用噪声批统计,在验证中使用更平滑的运行平均值。计算时机也起作用,因为训练损失是epoch中间的平均值,而验证使用epoch结束时的完整更新模型。在像Swin Transformers这样的模型中,其MLP层中包含dropout,这种效果在评估模式下被放大。
对最终结果的影响
这种模式通常不会负面影响模型的整体有效性;事实上,它可能表明成功的正则化,模型避免记住训练数据,而是学习可转移到验证或测试集的模式。如果验证损失低且稳定(如橙色曲线所示),模型很可能充分泛化,支持在新数据上的可靠预测。然而,一个数量级的差距可能暗示训练数据上的欠拟合或过度正则化,可能限制峰值性能——尽管这有争议,一些来源认为除非伴随差的测试指标,否则非问题性。要缓解,实践者推荐放松dropout率或验证数据拆分。
在深度学习训练过程中,观察到训练损失显著高于验证损失的实质差距——如提供的曲线中大约一个数量级的差异(20个epoch中训练约为0.7-0.9,验证约为0.05-0.1)——并不罕见,通常源于模型架构和数据处理中的有意设计选择。这种反向模式与经典过拟合场景相反,后者中验证损失因模型记住训练数据中的噪声而高于训练损失。相反,它通常反映了在训练期间有意约束模型以增强泛化的机制,尽管极端差异可能促使诊断检查。平坦的验证曲线和缓慢下降的训练曲线进一步表明在留出数据上的稳定,可能表明尽管有差距,但学习有效。
主要贡献因素是正则化技术,特别是dropout层,它在训练期间随机停用神经元以防止对特定特征的过度依赖,从而提升训练损失,因为模型必须适应不完整信息。在验证期间,使用评估模式,dropout被禁用,允许完整网络利用,通常导致较低损失。这对于像上下文中的Swin Transformer(Swin-Small变体)这样的架构尤其相关,它在其多层感知机(MLP)组件中包含dropout——通常在简单MLP结构中的两个dropout层——以正则化分层视觉处理。类似地,L1/L2权重惩罚或其他正则化形式仅在训练期间添加约束,膨胀那里的损失,而验证从学习的鲁棒性中受益,而无惩罚。
数据相关因素也发挥重要作用。广泛的数据增强,如裁剪、翻转或噪声添加,通常仅应用于训练集以增加多样性和鲁棒性,使训练任务更具挑战性,从而提高损失。验证集缺少这些增强,保持“更干净”且更容易,导致较低损失。在提供的代码片段中,输入裁剪(例如,x_0[:,:,3:-3,11:-11])和加载器中的潜在增强可能加剧此问题。批归一化引入了另一层差异:在训练期间,它使用当前批次的统计进行归一化,这可能变异且噪声;在验证中,它采用累积的运行平均值,导致更稳定且可能更好的性能。训练集和验证集之间的分布不匹配——如验证集更小、更简单,或由于拆分错误而无意中更容易——可能进一步扩大差距,尽管适当的洗牌和分层采样可以缓解此问题。
损失计算的时机添加了微妙但一致的偏差。训练损失通常是epoch内批次的平均值,反映更新中间的模型状态,而验证损失在该周期的完整优化模型上post-epoch计算。将训练曲线偏移半个epoch通常使它们更紧密对齐,如在Fashion MNIST数据集上使用MiniVGGNet的实验所示。不太常见但关键的问题包括实现bug,如日志中交换的标签、不正确的评估模式,或数据泄漏,其中验证样本与训练重叠。社区讨论强调验证代码,如确保model.eval()正确禁用dropout且损失正确平均。
关于最终模型的影响,这种差异经常表明积极泛化,因为较低的验证损失表明模型有效处理未见数据,这是机器学习的核心目标。在使用MSE损失的回归任务中(从代码的nn.MSELoss()推断),像0.05这样的低验证损失表明在留出样本上的准确预测,即使训练损失由于正则化而保持更高。然而,过大的差距可能暗示训练集上的欠拟合,模型未能捕捉所有可用模式,或过度正则化抑制学习——可能减少整体容量,并在复杂场景中导致次优测试性能。如果未解决,这可能通过限制对训练类变异的鲁棒性而影响部署,尽管经验证据显示放松正则化(例如,将dropout概率从0.5降低)或添加容量通常缩小差距而不牺牲泛化。在曲线中,缓慢的训练下降与平坦验证可能反映收敛,但建议监控测试集或额外指标(例如,回归的R²)以确认影响。
为了组织这些因素,下表总结了常见原因、机制以及基于文档实践的潜在补救措施:
不,训练损失和验证损失之间的差异(训练损失约为0.7-0.9,验证损失约为0.05-0.1,在20个epoch后)表明可能存在正则化或数据处理差异,而不是模型的基本缺陷。研究表明,这种模式在深度学习中很常见,通常反映了有意使训练更难的技术,以改善泛化能力。虽然差距看起来很大,但如果验证性能保持强劲,它可能不会严重损害模型的实际效用。
关键见解
可能不是过拟合:与典型过拟合不同(验证损失高于训练损失),这种反向模式指向了提升训练损失但不损害验证的机制。
泛化潜力良好:较低的验证损失意味着模型在新数据上可能表现可靠,尽管高训练损失可能表明有优化空间。
检查平衡:极端差异可能暗示过度正则化或数据不匹配,但证据倾向于在许多情况下这是良性的。
无绝对担忧:研究建议承认这种复杂性以避免过度反应;根据下游指标如测试准确率进行调整。
差异的常见原因
机器学习资源中的证据突出了几个因素,可能导致训练损失明显高于验证损失。这些包括正则化方法,如dropout,它在训练期间启用以防止过拟合,但在验证期间禁用,使验证任务对模型“更容易”。数据增强——如仅应用于训练数据的随机变换——也会增加训练难度,导致训练损失更高,而验证使用未更改的数据。此外,批归一化行为不同:在训练中使用噪声批统计,在验证中使用更平滑的运行平均值。计算时机也起作用,因为训练损失是epoch中间的平均值,而验证使用epoch结束时的完整更新模型。在像Swin Transformers这样的模型中,其MLP层中包含dropout,这种效果在评估模式下被放大。
对最终结果的影响
这种模式通常不会负面影响模型的整体有效性;事实上,它可能表明成功的正则化,模型避免记住训练数据,而是学习可转移到验证或测试集的模式。如果验证损失低且稳定(如橙色曲线所示),模型很可能充分泛化,支持在新数据上的可靠预测。然而,一个数量级的差距可能暗示训练数据上的欠拟合或过度正则化,可能限制峰值性能——尽管这有争议,一些来源认为除非伴随差的测试指标,否则非问题性。要缓解,实践者推荐放松dropout率或验证数据拆分。
在深度学习训练过程中,观察到训练损失显著高于验证损失的实质差距——如提供的曲线中大约一个数量级的差异(20个epoch中训练约为0.7-0.9,验证约为0.05-0.1)——并不罕见,通常源于模型架构和数据处理中的有意设计选择。这种反向模式与经典过拟合场景相反,后者中验证损失因模型记住训练数据中的噪声而高于训练损失。相反,它通常反映了在训练期间有意约束模型以增强泛化的机制,尽管极端差异可能促使诊断检查。平坦的验证曲线和缓慢下降的训练曲线进一步表明在留出数据上的稳定,可能表明尽管有差距,但学习有效。
主要贡献因素是正则化技术,特别是dropout层,它在训练期间随机停用神经元以防止对特定特征的过度依赖,从而提升训练损失,因为模型必须适应不完整信息。在验证期间,使用评估模式,dropout被禁用,允许完整网络利用,通常导致较低损失。这对于像上下文中的Swin Transformer(Swin-Small变体)这样的架构尤其相关,它在其多层感知机(MLP)组件中包含dropout——通常在简单MLP结构中的两个dropout层——以正则化分层视觉处理。类似地,L1/L2权重惩罚或其他正则化形式仅在训练期间添加约束,膨胀那里的损失,而验证从学习的鲁棒性中受益,而无惩罚。
数据相关因素也发挥重要作用。广泛的数据增强,如裁剪、翻转或噪声添加,通常仅应用于训练集以增加多样性和鲁棒性,使训练任务更具挑战性,从而提高损失。验证集缺少这些增强,保持“更干净”且更容易,导致较低损失。在提供的代码片段中,输入裁剪(例如,x_0[:,:,3:-3,11:-11])和加载器中的潜在增强可能加剧此问题。批归一化引入了另一层差异:在训练期间,它使用当前批次的统计进行归一化,这可能变异且噪声;在验证中,它采用累积的运行平均值,导致更稳定且可能更好的性能。训练集和验证集之间的分布不匹配——如验证集更小、更简单,或由于拆分错误而无意中更容易——可能进一步扩大差距,尽管适当的洗牌和分层采样可以缓解此问题。
损失计算的时机添加了微妙但一致的偏差。训练损失通常是epoch内批次的平均值,反映更新中间的模型状态,而验证损失在该周期的完整优化模型上post-epoch计算。将训练曲线偏移半个epoch通常使它们更紧密对齐,如在Fashion MNIST数据集上使用MiniVGGNet的实验所示。不太常见但关键的问题包括实现bug,如日志中交换的标签、不正确的评估模式,或数据泄漏,其中验证样本与训练重叠。社区讨论强调验证代码,如确保model.eval()正确禁用dropout且损失正确平均。
关于最终模型的影响,这种差异经常表明积极泛化,因为较低的验证损失表明模型有效处理未见数据,这是机器学习的核心目标。在使用MSE损失的回归任务中(从代码的nn.MSELoss()推断),像0.05这样的低验证损失表明在留出样本上的准确预测,即使训练损失由于正则化而保持更高。然而,过大的差距可能暗示训练集上的欠拟合,模型未能捕捉所有可用模式,或过度正则化抑制学习——可能减少整体容量,并在复杂场景中导致次优测试性能。如果未解决,这可能通过限制对训练类变异的鲁棒性而影响部署,尽管经验证据显示放松正则化(例如,将dropout概率从0.5降低)或添加容量通常缩小差距而不牺牲泛化。在曲线中,缓慢的训练下降与平坦验证可能反映收敛,但建议监控测试集或额外指标(例如,回归的R²)以确认影响。
为了组织这些因素,下表总结了常见原因、机制以及基于文档实践的潜在补救措施:
总体而言,虽然一些理论观点认为正确计算的损失不应在无警告的情况下展现实此,但实际深度学习实现中,它 routinely 作为有效训练策略的副产品出现。对于涉及64通道专用Swin-Small和MSE损失的具体设置,所观察行为与来自其包含dropout的设计和潜在增强的预期结果一致,表明模型实现理想验证性能而无立即警报原因。尽管如此,迭代实验——如去除dropout或与测试数据比较——可以细化结果,确保最终模型平衡训练拟合和泛化。
总而言之,Grok-4认为,最大的可能是正则化过度,并不是坏事。
3. 新问题(储存爆炸, 压缩算法 LZMA vs ZStandard)
1个.pt预处理数据集 78M,总共当前8W+样本,8W× 80 M = 6400G=6.4T。光是预处理样本,就接近我当前加上服务器和移动硬盘加总的存储总量,这怎么整,根本整不了大项目了。
于是,想到了压缩。数据字典不直接存储为.pt文件,而是压缩后存储,代价就是生成和读取时间增加。我原本预期需要至少把空间占用减少到现有1/10才能得到比较好的改善。但实际情况大大超出我的预计。
以 lzma压缩级别9 + float16精度截尾 为例:
压缩后的文件最大是 998 k ( < 1 M,< 1 / 80),最小只有 165 k ( < 1 / 468 ),所以,空间占用至少下降到原本的1/100以下。原本 1 T 的大项目,现在是不痛不痒的 10 G 以下.
压缩算法主要用 lzma 和 zstd 两种无损压缩:
import lzma
import pickle
from concurrent.futures import ProcessPoolExecutor, as_completed
from tqdm import tqdm
import os
# 修改后的保存函数(简化版,用于并行处理)
def save_compressed_worker(data, filepath):
"""工作进程使用的保存函数"""
data_to_save = {
'x': data['x'].to(torch.float16).numpy(),
'y': data['y'].numpy(),
'direction': data['direction']
}
with lzma.open(filepath, 'wb', preset=9) as f:
pickle.dump(data_to_save, f, protocol=pickle.HIGHEST_PROTOCOL)
import zstandard as zstd
import pickle
import torch
def save_compressed_zstd_worker(data, filepath, level=22, convert_to_fp16=True):
"""
工作进程使用的Zstandard保存函数(适配您的并行处理流程)
Args:
data: 单个数据字典,包含'x', 'y', 'direction'
filepath: 保存路径
level: 压缩级别(1-22,22为最高)
convert_to_fp16: 是否转换为float16(根据您0-1范围的数据,建议保持True)
"""
# 准备数据
data_to_save = {}
if convert_to_fp16 and 'x' in data:
data_to_save['x'] = data['x'].to(torch.float16).numpy()
else:
data_to_save['x'] = data['x'].numpy()
data_to_save['y'] = data['y'].numpy()
data_to_save['direction'] = data['direction']
# 序列化
serialized = pickle.dumps(data_to_save, protocol=pickle.HIGHEST_PROTOCOL)
# Zstandard压缩
cctx = zstd.ZstdCompressor(level=level)
compressed = cctx.compress(serialized)
# 保存
with open(filepath, 'wb') as f:
f.write(compressed)
测试对比两种无损压缩的差异(存储、时间、精度):
详细结果:
算法 级别 压缩时间(ms) 解压时间(ms) 文件大小(KB)
LZMA 1 5983.139873 862.077666 31709.521094
LZMA 3 11627.638914 852.297380 31550.196484
LZMA 5 19873.940490 1275.957168 30711.592187
LZMA 7 20316.240267 1455.568426 30727.785286
LZMA 9 18544.704807 1311.202108 30729.745573
Zstandard 1 35.649813 31.718932 32682.514844
Zstandard 5 47.519773 31.748273 32682.751302
Zstandard 10 59.903661 31.710702 32683.017773
Zstandard 15 2737.923069 48.688353 32686.702246
Zstandard 22 11646.556575 40.657198 32650.871973
--------------------------------------------------------------------------------
最佳性能分析:
最快压缩: Zstandard 级别1 (35.65 ms)
最快解压: Zstandard 级别10 (31.71 ms)
最小文件: LZMA 级别5 (30711.59 KB)
--------------------------------------------------------------------------------
推荐配置:
最佳平衡: Zstandard 级别1
压缩时间: 35.65 ms
解压时间: 31.72 ms
文件大小: 32682.51 KB
LZMA压缩所需时间普遍更长,但压缩效果确实更好。Zstandard 级别在15后压缩所需时间急剧增大,效果很有限。
这里推荐用的压缩算法和参数:
Zstandard 1 35.649813 31.718932 32682.514844
但本次用同形状的随机生成数据作为测试对象,与本次我面对的数据有些不同。所以,还是得基于自己的数据实操一下。
实际观察 LZMA(level 22)的压缩对比 Zstandard(level 15) 的压缩,LZMA 确实慢而小(212 k),Zstandard 快(是 LZMA 大概 3~4 倍速)略大(239 k)但整体仍然保持100倍以上的空间节省。因此最终选择 Zstandard level 15 作为压缩策略。
以此,9万不到的样本,大概需要 55 G 的存储空间即可。
这里还涉及 float 16 转换,它的小数点只有8位,整数也只有8位,非所有场景都适用,但 我们的数据基本在-1~1之间,这个转换损失轻微。
目前,样本图像生成了3W+(中断了,未全部生成,全样本量摸进 9 W),预处理中(大致 15 小时完成),训练尝试中(9000样本进行测试,15分钟训练一轮)。等当前这轮 3W 样本预处理结束,模型实证可训练能显著提升,就着手全量样本生成和进行下一步的外推构建策略尝试,看过滤器能起到多大的增效。
全量样本 + 可显著优化的模型架构 -> 样本外推、策略构建、市场实时分析报告撰写 -> 实盘与商业化 -> 涉足 股票市场 日内交易
4. ResNet 训练
4.1 9000+样本 + ResNet 50
9000+样本下,训练 ResNet 50,前22轮损失函数曲线如下:
看起来跟我之前遇到的情况一致,小样本可行,大样本不行,第二轮之后,损失函数(训练、验证)几乎没有下降。YOLOs 的网络结构我记得我看过,当时看到是有220+层,YOLOs拟合图像做的不错,而一般的图都是3通道,我此处的图是64通道,显然更为复杂。所以,我猜测是网络结构需要提升复杂度来提升拟合能力。如果训练拟合都不成功,那极大可能是网络结构复杂度不支持,无法拟合如此复杂的问题,因此,猜测需提升网络结构复杂度和深度。所以,新增一个152层的 resnet 最深的网络。
4.2 resnet 152 + 小样本(32个样本)

过拟合,是训练损失继续下降,验证损失不降反升。
欠拟合,是训练损失难以下降。
60轮之后学习率开始下降,训练逐步在收敛。80轮之后,训练和验证损失都不太有大变化了。
我要试验一下更大的学习率。当前学习率是 3e-4 (3/10000)。
4.3 resnet 152 + 小样本(320个) + 学习率 3e-2(3/100)
损失函数变化如下:
图很悲催:
这还是符合预期的,验证损失从1.26下降到0.31,下降幅度很不错。但训练损失一直没怎么下降,且训练和验证损失动荡都很大。3/100应该是大了,设置3/1000,明天起来看下结果。
4.4 9000+样本 + ResNet 152 + 学习率 3/1000

3/1000 设置下来,波动小了些,但遇到的仍然是之前遇到的问题,训练损失不下降,横着走。现在只要样本量一大就是这个死样子。下一步,先换一下样本,还压缩后的样本来试试。
4.5 60000+样本 + ResNet 152 + 学习率 2/10000 + PTDataset改读pt为读zst + resnet152结构修改 + 换损失函数 + 做了很多点大幅度的修正
4.5.1 PTDataset改读pt为读zst
all_files = [os.path.join(pt_dir, f) for f in sorted(os.listdir(pt_dir)) if f.endswith(‘.zst’)]
4.5.2 resnet 152 方法修改
增加了多个dropout,去掉了softmax映射,直接全链接到预测节点(关键修改!)
from torchvision.models import resnet152
def create_custom_resnet152():
"""完整修改版本 - 特别适合金融时序预测"""
# 1. 创建基础模型
model = resnet152(
weights=None,
num_classes=1,
zero_init_residual=True # 保留!有助于深层训练稳定性
)
# 2. 修改第一层卷积(适配64通道)
original_conv1 = model.conv1
new_conv1 = nn.Conv2d(
in_channels=64,
out_channels=original_conv1.out_channels,
kernel_size=original_conv1.kernel_size,
stride=original_conv1.stride,
padding=original_conv1.padding,
bias=original_conv1.bias is not None
)
nn.init.kaiming_normal_(new_conv1.weight, mode='fan_out', nonlinearity='relu')
if new_conv1.bias is not None:
nn.init.constant_(new_conv1.bias, 0)
model.conv1 = new_conv1
model.bn1 = nn.BatchNorm2d(64)
# 3. 增强全连接层(关键改进)
in_features = model.fc.in_features
model.fc = nn.Sequential(
nn.Dropout(0.5), # 🔥 高dropout开头,强制学习鲁棒特征
nn.Linear(in_features, 512),
nn.BatchNorm1d(512),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(512, 256),
nn.BatchNorm1d(256),
nn.ReLU(),
nn.Dropout(0.2),
nn.Linear(256, 1) # 无激活,纯回归输出
)
# 4. 精细初始化(解决梯度问题)
for m in model.fc.modules():
if isinstance(m, nn.Linear):
nn.init.kaiming_normal_(m.weight, mode='fan_in', nonlinearity='relu') # 🔥 fan_in模式
if m.bias is not None:
nn.init.constant_(m.bias, 0)
print(f"修改后的conv1: {model.conv1.weight.shape}") # [64, 64, 7, 7]
print(f"FC层参数量: {sum(p.numel() for p in model.fc.parameters()):,}")
print(f"总参数量: {sum(p.numel() for p in model.parameters()):,}")
return model
4.5.3 新增多个人不同的损失函数
class WeightedMSELoss(nn.Module):
def forward(self, pred, target):
weights = torch.abs(target) + 0.1 # 避免除零
return torch.mean(weights * (pred - target) ** 2)
# 分位数损失实现
class QuantileLoss(nn.Module):
def __init__(self, quantiles=[0.1, 0.5, 0.9]):
super().__init__()
self.quantiles = quantiles
def forward(self, preds, target):
losses = []
for i, q in enumerate(self.quantiles):
errors = target - preds[:, i:i + 1]
losses.append(torch.max((q - 1) * errors, q * errors).mean())
return torch.stack(losses).mean()
# criterion = nn.MSELoss()
criterion = nn.HuberLoss(delta=1.0) # 方案1:Huber损失(对异常值鲁棒)
# criterion = nn.SmoothL1Loss(beta=1.0) # 方案2:平滑L1损失
# criterion = WeightedMSELoss() # 方案3:自定义加权MSE(根据收益率幅度加权)
# criterion = QuantileLoss()
4.5.4 优化器设置
学习率设置为2/10000, weight_decay设为0.01,
optimizer = torch.optim.AdamW(
model.parameters(),
lr=2e-4,
# lr=0.01,
betas=(0.9, 0.999),
eps=1e-8,
weight_decay=0.01) # lr调高一点更快收敛
4.5.5 修改 scheduler
from torch.optim.lr_scheduler import LambdaLR
import math
# 替换原来的ReduceLROnPlateau
warmup_steps = 500
total_steps = epoch_setting * len(loader)
def lr_lambda(current_step):
if current_step < warmup_steps:
return current_step / warmup_steps
progress = (current_step - warmup_steps) / (total_steps - warmup_steps)
return 0.5 * (1 + math.cos(math.pi * progress))
scheduler = LambdaLR(optimizer, lr_lambda=lr_lambda)
4.5.6 在验证环节增加依据验证损失调整的方法
model.eval()
val_total_loss = 0.0
val_preds = []
val_targets = []
with torch.no_grad():
for x_0, y in val_loader:
x = x_0[:, :, 3:-3, 11:-11].to(device, non_blocking=True)
y = y[:, 0:1].to(device, non_blocking=True)
with autocast(device_type='cuda', dtype=torch.bfloat16):
pred = model(x)
loss = criterion(pred, y)
val_total_loss += loss.item()
val_preds.append(pred.detach().cpu())
val_targets.append(y.detach().cpu())
del x, y, pred, loss
gc.collect()
torch.cuda.empty_cache()
# 计算R²
val_preds = torch.cat(val_preds)
val_targets = torch.cat(val_targets)
val_r2 = r2_score(val_preds, val_targets)
writer.add_scalar('Metrics/R2', val_r2, epoch)
val_avg = val_total_loss / len(val_loader)
val_losses.append(val_avg)
scheduler.step(val_avg)
4.5.7 训练小结
看着像是有点正常的损失下降了,明早看看效果如何。得到的训练曲线如下:
训练损失下降符合预期,虽然幅度仍然不算很大,也已经算显著了,所以,昨天的修改有效。验证损失没有显著下降,和训练损失之间存在一个gap,这说明模型存在欠拟合,按之前Grok-4的说法,可能是过度正则化造成的,需要考虑调弱一下正则化强度。
训练了2天后,得到如下结果:
我认为:
(1)训练的欠拟合是存在的,训练集与验证集之间差距较大,因此,模型有必要对欠拟合的条件进行放宽松;
(2)整体的损失是较高的,一直下不来,所以,模型可能预测精度较差;
(3)训练集损失持续下降,验证集损失几乎没有影响,说明,训练中学到的东西跟验证集的标签几乎无关联。简单讲:没学到太多东西(从训练损失看),学到了一些,却仍然是对于标签预测来说,是没用的东西(验证损失没随着训练损失下降而下降); 有几种可能:
A 特征和标签关联确实很低;
B 噪声太多,信噪比太低导致难学,考虑输入特征、标签类型、模型结构都先从简单开始,一步步测试验证特征、标签类型、网络结构的有效性;(!!!我觉得这是深入细节的一条不错的路,之前没想起)
C 预处理可能还要继续斟酌一次,尤其是21张图转换为63张图的过程,考虑是否真的必要,是否引入了太多噪声;
D 其它类型特征输入形式,可以考虑下;
E 对比下resnet的网络结果 和 alphago的网络结构,看差别在哪儿;对比svm、集成学习、神经网络的差别,有个对比,更明确效果差异;
F 把周期从1分钟,提升到别的大周期,比如日线;
元宝deepseek认为:
根据您提供的训练损失曲线图,我对您的模型训练结果进行犀利评价,并提供具体的提升方案。
🔍 训练成果评价
❌ 当前表现存在的问题
严重欠拟合:
训练损失(1.15) > 验证损失(1.05),这在正常情况下是反常的
理想情况:训练损失 < 验证损失
这表明模型在训练集上都没学好,更别提泛化了
收敛质量差:
训练损失从1.40→1.15,下降幅度仅18%
验证损失基本在1.05-1.10区间震荡,无实质性下降
模型在20个epoch后基本停止学习
MSE=1.0的含义:
预测误差标准差 ≈ √1.0 ≈ 1.0
如果收益率范围是[-5, 5]%,预测误差约±1%
这几乎是随机猜测的水平
训练损失下降缓慢
模型容量不足/学习率太小
⭐⭐⭐⭐⭐
验证损失不下降
未学到可泛化模式
⭐⭐⭐⭐
训练损失>验证损失
数据划分/正则化问题
⭐⭐⭐
大幅降低正则化强度
提高学习率
放宽梯度裁剪
改用更合适的架构:21张量价图本质是时间序列特征,而非空间特征,ResNet可能不是最佳选择。
简化模型(如果数据量不够)
检查数据分布
分析标签特性
分阶段改进计划
第一阶段(立即执行,1-2小时)
移除所有Dropout或降至0.1
提高学习率到3e-4
降低权重衰减到0.001
训练5个epoch观察损失变化
第二阶段(如有需要,2-4小时)
增加模型容量(扩大FC层到2048→1024→512)
尝试SmoothL1或Huber损失
检查数据划分是否合理
第三阶段(如仍欠拟合,1-2天)
更换模型架构(LSTM/Transformer)
重新审视特征工程
考虑转换为分类问题(涨/跌/平)
豆包认为:
先直接点出这个训练成果的核心问题:这训练结果属于 “低效且偏科”,模型已经在往过拟合的坑里滑了。
犀利评价训练成果
模型 “冗余过载”:ResNet152 对这个金融回归任务来说太 “重” 了 —— 训练损失(Train Loss)收敛速度极慢,说明模型在训练集上 “磨洋工”(复杂结构在拟合噪声而非有效规律);
泛化能力早早拉胯:验证损失(Val Loss)前期就进入波动平台期,和训练损失的差距持续扩大,典型的 “训练集越学越熟、验证集越学越懵”,过拟合趋势已经很明显;
数据 / 任务匹配度差:初始阶段验证损失就低于训练损失,要么是训练集噪声太多、要么是训 / 验集分布不一致(比如验证集是 “简单样本”),数据质量 / 划分可能有坑。
针对性提升方向
1. 砍模型复杂度(先 “减负”)
把 ResNet152 换成更轻量的架构(比如 ResNet18/34)—— 金融数据的 “价图 + 21 维张量” 特征复杂度不高,深网络反而会拟合噪声,轻量模型能减少过拟合、加快收敛。
2. Fix 数据问题
检查训 / 验集的分布一致性(避免数据泄露、或者验证集是 “易预测样本”);
给金融数据做特征归一化(比如标准化 / 归一到 [0,1])—— 金融数据波动大,未归一化会导致模型收敛慢;
增加数据量 / 做增强(比如价图的时间平移、小幅噪声扰动,避免模型死记硬背训练集)。
3. 加 “刹车” 和 “约束”
用早停(Early Stopping):等 Val Loss 连续几轮不下降就停训,别让模型继续拟合训练集噪声;
加正则化(Dropout 层、L2 正则):给 ResNet 的全连接层加 Dropout(比如 0.3-0.5),限制模型过度拟合。
4. 调整训练 / 损失策略
换损失函数:金融收益率可能有极端值,把 MSE 换成Huber Loss(对异常值更鲁棒);
优化训练细节:调小初始学习率 + 学习率衰减(比如每 10 轮衰减 50%),或换 AdamW 优化器(比 SGD 更适配小批量金融数据)。
Grok-4认为:
潜在原因
正则化(如dropout、L2)仅train应用,抬高train loss。
数据分布不均:验证集易预测,随机split噪声多在train。
测量时机:train loss批次计算,val epoch末。
提升策略
数据:检查泄漏,增大验证集(50% split),增强(如旋转图)。
模型:加LSTM成ResNLS,捕捉时间残差,提升20%准确。
训练:减正则,移train曲线0.5 epoch比较;早停,学习率衰减。
备选:用CNN-LSTM hybrid,波小波去噪输入。
ResNet152用于金融策略收益率回归,输入21张量价图,MSE损失曲线异常:train从1.4降至1.1,val从1.1波动至1.05,不降。犀利评:失败品!Underfitting明显,模型没学本质,val“低”伪象,实际泛化差。原因多:正则仅train增loss;val易或泄漏;金融数据需时间建模,纯ResNet忽略序列依赖。文献如ResNLS显示,结合ResNet+LSTM残差,提升预测20%+。
5. 提升方案调研:弱化正则化 + 换损失函数 + 提升网络结构(更复杂或更简单)




更多推荐



所有评论(0)