ETTh1时间序列预测实战:LSTM、Transformer与DLinear数据管线全解析
简介面向时间序列预测学习与毕业设计场景的完整源码包基于Python实现在ETTh1电力变压器数据集上系统对比长短期记忆网络LSTM、Transformer与自定义线性模型的预测效果支持通过调整模型名称、序列长度等超参数灵活优化性能。全部文件共39个以34个Python脚本为核心覆盖数据加载、模型定义、训练评估、指标计算与时间特征处理等模块另有2个shell脚本便于一键运行2个Markdown说明文档辅助理解项目逻辑整体仅64KB轻量易用。已有1430人学习下载。项目结构清晰run_longExp.py为主实验入口models目录下分别提供LSTM.py、Transformer.py与ours_Linear.py三种实现exp层封装实验流程utils层沉淀通用工具Transformer实现还包含SelfAttention_Family、Encoder-Decoder与Embed等子模块便于深入理解注意力机制。适合计算机相关专业学生作为毕业设计、课程设计或大作业参考也适合在此基础上二次开发。1. 为什么拿ETTh1同时验证三种实现先看清数据再写模型ETTh1是ETTElectricity Transformer Temperature数据集里最常被拿来练手的一个子集小时级采样、7个通道、17420个时间点文件小到读入内存只用几秒钟迭代一轮也不到两分钟。很多团队拿到“分别基于LSTM、Transformers和自定义模型实现ETTh1数据集时间序列预测源码项目说明.zip”之后第一反应就是解压、装环境、跑通但真正花时间的从来不是让loss降下去而是训练/测试怎么切、滑窗怎么滑、预测结果怎么评价才对。这篇文章把三套实现的数据管线、训练脚本和参数边界一次讲清楚适合正在做时间序列项目落地、或者需要给团队搭一套可对照基线的工程师。新手可以照着复现熟手可以直接跳到第5章看排查。2. 把ETTh1的数据管线和评价协议定下来滑窗、归一化与Baseline2.1 ETTh1字段构成与训练/验证/测试切分ETTh1原始数据是CSV格式列名一般是HUFL、HULL、MUFL、MULL、LUFL、LLULL和OT其中OT是油温Oil Temperature也是大多数预测任务的目标列。时间间隔是1小时整个数据集覆盖2016年7月到2018年7月共17420行。你还可能见过ETTm1那个是15分钟采样同样的列名但时间粒度和数据长度不同别混用。切分协议方面业界最常见的做法是按时间顺序切成7:2:1前约70%做训练中间约20%做验证最后约10%做测试。这里要特别强调严禁用train_test_split的默认随机切分。时序数据一旦随机切分等于让模型在训练时偷看未来样本验证集指标会虚高换到线上立刻露馅。我一般会在项目说明里写清楚这三行的切分比例并在代码里按行号切import pandas as pd df pd.read_csv(ETTh1.csv, parse_dates[date]) df df.sort_values(date).reset_index(dropTrue) # 保证时间升序 n len(df) train_df df.iloc[:int(n * 0.7)] val_df df.iloc[int(n * 0.7):int(n * 0.9)] test_df df.iloc[int(n * 0.9):] print(ftrain{len(train_df)}, val{len(val_df)}, test{len(test_df)})这段代码把原始CSV按时间顺序切成三块。注意先sort_values再切很多公开数据本身有序但下载后可能被Excel打开重排过所以排序这一步不要省。切分比例不一定要卡死7:2:1也可以6:2:2但务必在项目说明里标注清楚否则之后跟论文里的MSE对比口径完全对不上。2.2 滑窗采样与归一化三个模型共用的数据管线滑窗采样是时间序列预测的地基。核心参数是seq_len回看窗口和pred_len预测窗口。ETTh1上最常见的组合是seq_len48、pred_len24以及seq_len168、pred_len168。前者适合快速验证后者是很多论文的基准设置。滑窗的逻辑是用[t - seq_len, t)的数据预测[t, t pred_len)的值每次滑动1个时间步。下面这个PyTorch Dataset类把滑窗逻辑收口LSTM、Transformer和自定义模型共用同一份数据管线不会出现“LSTM的输入形状和Transformer对不上”这种低级问题。import torch from torch.utils.data import Dataset import numpy as np class ETTDataset(Dataset): def __init__(self, data: np.ndarray, seq_len: int, pred_len: int, target_idx: int): self.data data.astype(np.float32) # (T, C) self.seq_len seq_len self.pred_len pred_len self.target_idx target_idx self.start_idx seq_len self.end_idx len(data) - pred_len def __len__(self): return self.end_idx - self.start_idx def __getitem__(self, i): t self.start_idx i x self.data[t - self.seq_len:t] # 输入窗口 y self.data[t:t self.pred_len, self.target_idx] # 目标窗口 return torch.from_numpy(x), torch.from_numpy(y)x的形状是(seq_len, num_features)y的形状是(pred_len,)。batch维度由DataLoader统一加。注意不要把y写成二维后面的MSELoss会多算一次维度惩罚。target_idx对应OT在特征矩阵里的列下标一般OT是最后一列也就是7列时的index6但写代码时建议动态传入别写死。归一化我推荐StandardScaler而不是MinMaxScaler。ETTh1的油温序列有明显趋势和周期成分MinMaxScaler把所有数值压到0到1之间但如果数据里有尖峰大部分数值会被挤在一个很窄的区间里模型很难学。StandardScaler对均值和方差的标准化更适合LSTM和Transformer这类对尺度敏感的模型。from sklearn.preprocessing import StandardScaler scaler StandardScaler() train_scaled scaler.fit_transform(train_df[feature_cols]) val_scaled scaler.transform(val_df[feature_cols]) test_scaled scaler.transform(test_df[feature_cols])这里最关键的坑是scaler只能fit训练集。如果你fit了全量数据测试集的均值和方差已经被模型“见过”了这属于数据泄漏。症状是验证集指标比测试集好一大截而且怎么调都拉不平。2.3 评价指标与BaselineMSE/MAE怎么才算合格评价指标用MSE和MAE就够了。ETTh1的数值经过标准化后MSE的量级一般落在0.1到0.5之间具体看pred_len和模型。粗看标准pred_len24时LSTM的MSE大概在0.2上下Transformer调好能到0.15以下但不同切分和不同预处理会差很多所以不要拿绝对值跨实现对比。建议在项目说明里固定好随机种子再报数。def calc_metrics(y_true, y_pred): mse float(np.mean((y_true - y_pred) ** 2)) mae float(np.mean(np.abs(y_true - y_pred))) return mse, mae指标在标准化空间计算同一数据管线下对比不同模型是公平的。如果你想报原始量纲的误差需要用scaler.inverse_transform把预测值还原回去再算但那样就要多传一个scaler参数代码容易出错我一般只在论文汇报时做这一步。Baseline方面我习惯先跑一个“重复最后一个观测值”的零模型拿输入窗口最后一行的时间步重复pred_len次作为预测。这个基线一点都不聪明但能快速暴露数据管线问题。如果LSTM在MSE上连这个基线都打不过那要先查数据代码而不是调模型参数。三模型的参数量和定位差异可以在下面这个表里直观感受模型参数量级训练速度稳定性适用场景LSTM10万上下中较稳序列长度中等、依赖时序依赖Transformer20万到100万慢初期易翻车长序列、需要全局注意力自定义DLinear几千到几万极快稳定快速基线、可解释性兜底表格里的参数量是“量级”实际取决于hidden_size和层数作用是给选型提供方向感不是精确数值。3. 用LSTM和Transformer做ETTh1预测两个主力模型的最小实现与调参3.1 LSTM的网络结构、输入输出形状与多步滚动策略LSTM用在ETTh1上首先要确定输出方式。常见有两种直接多步输出和滚动预测。直接多步输出是输入(seq_len, features)经过LSTM取最后一步的hidden state再接Linear层映射到(pred_len,)滚动预测是模型每次只预测下一个点然后把预测值作为下一次输入循环pred_len次。我强烈建议先用直接多步输出训练稳定、误差不会累积代码也简单。滚动预测在真实场景更贴近“用预测代替真实输入”的推理方式但训练时如果每一步都用真实值做teacher forcing推理时换成预测值分布偏移会让误差随步长迅速放大。这个现象在第5章避坑部分会展开讨论。lstm神经网络的结构本身不复杂输入形状是(batch, seq_len, input_size)input_size就是特征数ETTh1是7。hidden_size决定记忆容量太小记不住周期太大过拟合且训练慢。num_layers常见取2层1层欠拟合3层以上在17420个样本上容易过拟合。dropout取0.1到0.2之间不要超过0.3否则序列依赖被切得太碎。3.2 LSTM训练脚本从DataLoader到早停下面是一个可以直接放进项目里跑的最小LSTM训练脚本。模型定义用了两层LSTM加一个两层全连接头输入输出形状看注释。import torch import torch.nn as nn class LSTMPredictor(nn.Module): def __init__(self, input_size7, hidden_size64, num_layers2, seq_len48, pred_len24, dropout0.1): super().__init__() self.lstm nn.LSTM(input_size, hidden_size, num_layers, batch_firstTrue, dropoutdropout) self.head nn.Sequential( nn.Linear(hidden_size, hidden_size), nn.ReLU(), nn.Linear(hidden_size, pred_len) ) def forward(self, x): # x: (batch, seq_len, input_size) out, (h_n, _) self.lstm(x) # 取LSTM输出的最后一个时间步 last_hidden out[:, -1, :] # (batch, hidden_size) y self.head(last_hidden) # (batch, pred_len) return yforward里为什么取out[:, -1, :]而不是直接用h_n因为batch_firstTrue时out的形状是(batch, seq_len, hidden_size)取最后一个时间步等价于用h_n里最后一层的hidden state。但out[:, -1, :]更直观也方便后面改成注意力池化。训练循环如下注意梯度裁剪和早停是两个关键点。model LSTMPredictor(input_size7, hidden_size64, num_layers2) optimizer torch.optim.Adam(model.parameters(), lr1e-3) criterion nn.MSELoss() best_val_loss float(inf) best_model None patience 0 for epoch in range(30): model.train() for x, y in train_loader: optimizer.zero_grad() pred model(x) loss criterion(pred, y) loss.backward() nn.utils.clip_grad_norm_(model.parameters(), 1.0) # 防梯度爆炸 optimizer.step() model.eval() val_loss 0.0 with torch.no_grad(): for x, y in val_loader: val_loss criterion(model(x), y).item() val_loss / len(val_loader) if val_loss best_val_loss: best_val_loss val_loss best_model {k: v.clone() for k, v in model.state_dict().items()} patience 0 else: patience 1 if patience 5: break print(fepoch{epoch}, train_loss{loss.item():.4f}, val_loss{val_loss:.4f}) model.load_state_dict(best_model)代码逻辑optimizer用Adamlearning_rate1e-3是LSTM在ETTh1上比较稳妥的起点。clip_grad_norm_加在backward之后、step之前这一步很多人会漏但ETT数据有尖峰梯度爆炸一旦出现loss直接变NaN。early stopping用patience5连续5个epoch验证集loss没刷新就回滚到best_model。这里最小版本没有做学习率调度实际项目可以加StepLR或CosineAnnealing。LSTM必调参数里最常动的是seq_len、hidden_size和lr。seq_len取24或48ETT油温有日周期48小时能覆盖两个完整周期信息更足。hidden_size从64起步数据量大再加到128。lr调到1e-4会训练变慢但结果更稳。总之LSTM的调参路径相对平滑不像Transformer那样容易直接翻车。3.3 Transformer直接搬NLP架构为什么会翻车Transformer在时间序列预测上不是不能打但直接把NLP那一套搬过来几乎必翻车我总结了三个原因。第一注意力对尺度敏感。NLP里词嵌入有相对统一的分布而ETTh1的原始特征经过StandardScaler后不同通道的波动范围依然不一样油温的局部方差和负荷通道不在一个量级。如果Patch Embedding和LayerNorm没有配好训练初期loss会剧烈摆动。第二位置编码表达不了周期性。原版Transformer的sinusoidal位置编码能表达相对位置但ETT数据的周期性来自小时、天、周的重复结构纯位置编码学不到这种周期性模型会把注意力平均撒在所有时间步上。你要么用可学习位置编码并加大训练量要么把hour_of_day、day_of_week作为额外特征拼进去。常见做法是两者都做。第三复杂度是O(n²)。seq_len只有48时问题不大但如果你用seq_len168注意力矩阵就是168×168单卡训练时间会明显拉长。Patch化能把序列长度压缩到seq_len/patch_size是提速的关键手段。3.4 Transformer最小实现Patch、位置编码与掩码的关键参数针对上述问题业界最常见的做法是Patch化把每patch_size个时间步合并成一个patch缩小序列长度。下面是一个精简实现我用Conv1d做patch embedding再接一个标准的TransformerEncoder。import torch import torch.nn as nn class PatchTransformer(nn.Module): def __init__(self, input_size7, seq_len48, pred_len24, patch_size4, d_model64, nhead4, num_layers2): super().__init__() self.patch_size patch_size self.num_patches seq_len // patch_size # 48/412 self.input_size input_size self.patch_embed nn.Conv1d(input_size, d_model, kernel_sizepatch_size, stridepatch_size) self.pos_embed nn.Parameter(torch.zeros(1, self.num_patches, d_model)) nn.init.trunc_normal_(self.pos_embed, std0.02) encoder_layer nn.TransformerEncoderLayer( d_modeld_model, nheadnhead, dim_feedforward256, dropout0.1, batch_firstTrue, activationgelu ) self.encoder nn.TransformerEncoder(encoder_layer, num_layersnum_layers) self.head nn.Linear(d_model * self.num_patches, pred_len) def forward(self, x): # x: (batch, seq_len, input_size) x x.permute(0, 2, 1) # (batch, input_size, seq_len) x self.patch_embed(x) # (batch, d_model, num_patches) x x.permute(0, 2, 1) # (batch, num_patches, d_model) x x self.pos_embed x self.encoder(x) x x.reshape(x.size(0), -1) # 展平全部patch return self.head(x) # (batch, pred_len)逻辑说明输入先按通道维度排列Conv1d在时间维上滑动每patch_size步输出一个d_model维向量。seq_len48、patch_size4时得到12个patch每个patch带一个可学习位置编码进TransformerEncoder。最后把12个patch的特征全部展平接Linear输出pred_len个预测值。这里没有写因果掩码因为输入窗口完全位于预测点之前不会发生未来信息泄漏。如果你把模型改成编码器-解码器结构并用到teacher forcing那才需要生成mask矩阵。关键参数的落地建议参数取值说明patch_size4或8序列压缩倍数越大越快但可能丢失细节d_model64隐层维度通常取64到128nhead4一般能被d_model整除取4或8num_layers2TransformerEncoder层数加深到4层需更多数据lr1e-4比LSTM低一个量级配合warmup更稳训练时learning_rate要降到1e-4量级并且建议加warmup前10个step把lr从0逐步升到目标值不然前几个batch的loss容易飞掉。dim_feedforward取256是d_model的4倍这是Transformer的默认经验值。4. 自定义模型轻量可解释基线的设计、实现与三模型横向对比4.1 自定义模型的设计思路先定一个可解释的基线“自定义模型”在ETTh1这类任务里最常见的定位不是跟LSTM和Transformer拼精度而是充当可解释的下界基线。我在解压这类“源码项目说明”包时最关心的就是作者有没有给自己留一个“兜底模型”——因为一个轻量基线如果表现得比复杂模型更稳定往往说明复杂模型没有学到数据的结构特征。自定义模型我一般推荐用DLinear把序列分解成趋势项和残差项分别做线性映射再相加。它的核心假设是ETT数据里真正难预测的不是趋势趋势用平均池化提取后过线性层就够残差部分才需要模型捕捉的非线性成分。DLinear只有两个Linear层加一个AvgPool参数量在上千到上万这个量级训练速度极快而且结果可解释——你可以直接看线性层权重看出模型重视哪些历史位置。如果你不想要DLinear也可以自定义成MLP把整个输入窗口展平成seq_len*num_features维过两层ReLU再输出。但MLP没有时间结构归纳偏置在seq_len48、特征7维时输入是336维容易过拟合。我一般会优先做DLinear它更适合作为“自定义模型”这一项的答案。4.2 自定义模型的PyTorch实现与训练DLinear实现如下我把特征维和时间步分开让线性层作用在时间维上import torch import torch.nn as nn class DLinear(nn.Module): def __init__(self, seq_len48, pred_len24, num_features7, target_idx6): super().__init__() self.target_idx target_idx # 用平均池化提取趋势项, kernel_size3不会破坏序列长度 self.avg_pool nn.AvgPool1d(kernel_size3, stride1, padding1) # 趋势和残差各接一个线性层, 作用在时间维上 self.trend_linear nn.Linear(seq_len, pred_len) self.residual_linear nn.Linear(seq_len, pred_len) def forward(self, x): # x: (batch, seq_len, num_features) x x.permute(0, 2, 1) # (batch, num_features, seq_len) trend self.avg_pool(x) residual x - trend trend_out self.trend_linear(trend) # (batch, num_features, pred_len) residual_out self.residual_linear(residual) y trend_out residual_out return y[:, self.target_idx, :] # 只输出OT通道这里对每个特征通道单独做趋势/残差分解最后取目标通道的输出。其他6个特征虽然不直接预测但它们的趋势信息通过池化和线性层被间接建模。如果你想把所有特征的信息融合到OT预测里可以把输出改为对全部通道加权求和。不过实验下来ETTh1上OT自回归的路径已经足够强刻意引入跨特征融合收益不大。训练DLinear直接用第3章那套循环即可优化器Adam、lr1e-3、MSELoss。DLinear唯一要调的是seq_len和pred_len没有hidden_size这类参数。运行时会发现它收敛极快通常10个epoch以内验证集就稳定了继续训练也不会有明显提升。LSTM需要30到50个epochTransformer则更长。所以DLinear很适合在跑复杂模型之前做一次“代码冒烟测试”——如果DLinear的MSE异常高说明数据管线有问题先修管线再看模型。4.3 三模型横向对比与选型边界在同一个数据管理线下三模型的差异会逐渐清晰。为了直观我用下表做结论性对比实际数值依赖具体配置但规律是稳定的指标LSTMTransformerDLinear自定义参数量级10^4到10^510^5到10^610^3到10^4收敛epoch数30到5050到1005到10单epoch耗时中等慢极快稳定性较稳初期易翻车非常稳可解释性低低高选型边界其实很清楚LSTM是性价比最高的选择在ETTh1的全部预测长度场景上都能打Transformer需要更细的调参和更长训练时间但它对长时间依赖的建模能力在pred_len168时开始体现优势DLinear不追求精度上限它负责快速验证数据管线、提供可解释的下界。三者不是替换关系而是分工关系。5. ETTh1预测避坑指南五个高频翻车现场与处置方法跑出来的结果就是不如论文里写的好这情况我见得太多了。多数时候不是模型本身的问题而是张量形状、评价口径和数据处理里的“玄学”。这一章把ETTh1上最高频的五个坑按“现象→原因→解决”写清楚每个都是我实际踩过或帮人排查过的。5.1 数据集泄露验证集指标虚高的元凶现象训练loss正常下降验证集MSE好得离谱但测试集MSE比验证集高50%以上。原因大概率是归一化时用了全量数据去fit scaler或者训练/验证切分时用了随机抽样而不是按时间顺序切。这会让验证集“见过”未来数据的统计信息指标自然虚高。解决回到第2章确认scaler只fit训练集切分用sort后iloc。另外检查代码里有没有在预处理阶段使用整个DataFrame的mean/std。这个坑跟模型无关换任何一个模型都会踩而且DLinear这类轻量模型一样中招。5.2 预测曲线滞后一拍是模型问题还是评价口径问题现象把预测曲线和真实曲线叠在一起画预测线像是真实线向右平移了一个点。loss很低看起来“拟合得很好”。原因如果训练时用多步滚动或teacher forcing模型学会的最优策略就是复制上一个观测值因为ETT序列单步自相关性极高。这本质上是模型没学到周期结构只学到了“平滑延续”。解决改用直接多步输出的LSTM或Transformer让模型同时预测整段窗口打破“下一步复制上一步”的训练陷阱。另外可以跑一个“重复最后一个值”的baseline如果你的模型MSE没有明显低于这个baseline说明模型确实在偷懒。5.3 Transformer训练不收敛先从这三个配置查起现象loss在前几个epoch剧烈震荡甚至直接变成NaN。原因最常见是三件事。一是learning_rate太高Transformer对lr非常敏感1e-3直接起步很容易炸。二是没有做梯度裁剪ETT数据里有尖峰一个异常样本就能把梯度推到天文数字。三是初始化不合适位置编码或patch_embed的方差过大前向输出直接溢出。解决把lr降到1e-4并加warmup在backward后加clip_grad_norm_(model.parameters(), 1.0)位置编码初始化std取0.02。如果还是NaN检查数据里有没有NaN或infETTh1下载后偶尔会有个别缺失行。这个坑我盯了两天最后发现是数据里有一行全是空值算是血泪经验。5.4 LSTM滚动预测误差累积什么时候该换直接多步现象pred_len24时MSE还能看pred_len48或168时误差突然爆炸式增长。原因滚动预测训练时每一步都用真实值推理时用前一步的预测值误差随步长指数累积。换到长预测窗口时这个累积误差就成了主导项比模型本身的结构误差还大。解决把模型输出改成直接多步Linear直接映射到pred_len个值如果一定要用滚动推理至少要做scheduled sampling训练时按概率把真实输入替换成预测输入让模型逐渐适应噪声输入。否则长窗口预测基本没法用。5.5 随机种子与可复现性为什么同一份代码两次结果不一样现象同一份代码、同一个模型连续跑两次MSE差0.05以上。如果你在做三模型对比这种波动足以把“哪个模型更好”的结论颠倒过来。原因PyTorch默认不固定随机种子CUDA卷积和LSTM的cuDNN算法选择也带有随机性。DP和DDP训练时还会引入更多不确定性。解决在训练脚本开头固定种子import random import numpy as np import torch random.seed(42) np.random.seed(42) torch.manual_seed(42) torch.cuda.manual_seed_all(42) torch.backends.cudnn.deterministic True torch.backends.cudnn.benchmark False注意cudnn.benchmarkFalse会让训练稍慢但换来的是结果可复现。如果你依赖benchmark加速至少要在项目说明里注明“结果可能因硬件和cuDNN版本略有浮动”。我自己跑对比实验时一定固定seed否则三模型的横向对比结论根本站不住。6. 进阶验证把预测结果可视化、误差分解与一次超参搜索跑完6.1 可视化从预测曲线到误差分布训练完成后不要只报一个MSE就收工。我习惯画三张图第一张是测试集最后200步的预测曲线叠加真实曲线一眼能看出滞后和偏移第二张是误差随时间分布的散点图能看到误差是否集中在某些时段第三张是预测值对真实值的散点图点越贴近yx说明模型校准得越好。这三张图放进项目说明里比任何指标都直观。6.2 误差分解什么时间点最难预测误差分解我一般按小时和星期切分。ETTh1的油温序列有明显的日周期你会发现夜间和周末的误差系统性偏高因为负荷模式发生变化而模型没有对应的外部特征。这个结论可以直接指导后续特征工程比如把hour_of_day和day_of_week作为额外输入加到三个模型里。6.3 超参搜索在ETTh1上跑一轮有意义的对比如果你想跑正式的超参搜索建议用Optuna搜索空间只放三个核心参数seq_len24/48/168、learning_rate1e-4/5e-4/1e-3、hidden_size64/128。每个试验固定跑20个epoch用验证集MSE作为目标函数。不要一次性搜索太多参数ETTh1数据集不大但搜索空间过大会把置信度稀释掉。这套脚本也可以agent化现在有些团队已经把“改参数→训练→记指标→写报告”串成自动化流水线实验记录规范之后调参效率能提升一个量级。我自己的习惯是每改一次参数都把实验配置完整存档因为如果不知道上一轮参数是什么下一轮调参就只能靠猜。这也是我为什么坚持在跑复杂模型之前先让DLinear跑通——它能替你证明“数据管线本身没毛病”。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

WPF实战:用WMI与DriveInfo打造内存磁盘监控面板

WPF实战:用WMI与DriveInfo打造内存磁盘监控面板

1. 项目概述与需求拆解 做WPF客户端的朋友,迟早会遇到一个需求:在界面上显示“当前电脑的储存和运存”。你可能觉得这不就是调个API取两个数字吗?实际做起来会发现,这里面的坑一点不少——单位换算、不同Windows版本的差异、权限问…

2026/10/9 10:42:15 阅读更多 →
极域电子教室UDP协议逆向与C#指令复现实践

极域电子教室UDP协议逆向与C#指令复现实践

简介:本资源是一套基于C#开发的极域电子教室协议分析与控制工具集,面向网络安全学习者、渗透测试初学者及教育信息化安全研究人员,用于理解极域电子教室通信机制并开展合法授权环境下的协议逆向与功能验证。压缩包共88个文件,含25…

2026/10/9 10:42:14 阅读更多 →
LaTeX公式一键转Word原生公式:Markdown写作与OMML转换全攻略

LaTeX公式一键转Word原生公式:Markdown写作与OMML转换全攻略

最近帮我一个学弟改毕业论文,他往里贴公式的方式把我惊到了——拿手机拍书上的公式,再用识别软件扫成图片,插进Word里。结果公式稍微改个下标就得重新截图,排版也忽大忽小,导师看了直摇头。我说你写论文怎么不用LaTeX&…

2026/10/9 10:42:14 阅读更多 →

最新新闻

Agent-Reach 实战:CLI 驱动 AI Agent 的架构、安装与自动化

Agent-Reach 实战:CLI 驱动 AI Agent 的架构、安装与自动化

1. 从零认识 Agent-Reach:一个 CLI 驱动的 AI Agent 工具到底解决什么问题第一次看到 Agent-Reach 这个名字,我下意识把它归类成又一个"套壳聊天框"。真正跑起来之后才发现,它的定位其实更偏底层——一个用命令行驱动的 AI Agent 执…

2026/10/9 11:20:12 阅读更多 →
claude-mem实测:让Claude Code告别金鱼记忆,自动沉淀项目经验

claude-mem实测:让Claude Code告别金鱼记忆,自动沉淀项目经验

上周一我打开 Claude Code,准备接着做支付模块的重构。前一天我明明已经和它把方案聊得很透了:项目统一用 pnpm、测试框架是 vitest、支付回调在src/modules/payment/callback.ts、重构时不要动 stripe SDK 的版本。结果新的会话一上来,它先客…

2026/10/9 11:20:12 阅读更多 →
用Windsurf开发NFT共创Web3项目:TaoToken统一Key让AI IDE真正跑起来

用Windsurf开发NFT共创Web3项目:TaoToken统一Key让AI IDE真正跑起来

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

2026/10/9 11:20:12 阅读更多 →
AI 测试之如何使用 MCP 做自动化测试:从 Chrome DevTools 到 TaoToken 统一 Key 通道

AI 测试之如何使用 MCP 做自动化测试:从 Chrome DevTools 到 TaoToken 统一 Key 通道

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

2026/10/9 11:20:12 阅读更多 →
如何在1分钟内编写Cursorrules:用TaoToken统一Key快速验证规则生效

如何在1分钟内编写Cursorrules:用TaoToken统一Key快速验证规则生效

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

2026/10/9 11:20:12 阅读更多 →
物业管理系统源码实战:Access数据库设计与C#连接部署指南

物业管理系统源码实战:Access数据库设计与C#连接部署指南

简介:面向小区物业管理人员与.NET开发者的物业管理系统源码,基于VS2010与Access数据库的C/S架构,覆盖收费管理、住户管理、房间及单价设置、通知单打印,并支持收支与住户信息的Excel批量导入导出。通知单打印采用水晶报表实现&…

2026/10/9 11:19:11 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

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

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

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

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

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

2026/10/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/9 6:17:20 阅读更多 →