
简介这份资源面向具备Python基础及数据分析、机器学习背景的研发人员围绕重庆火灾点分析与预测这一自然灾害预警场景提供从数据到模型的完整技术方案。内容涵盖多源数据导入与预处理、逐年逐月火点频次统计与可视化、气象数据相关性分析以及结合注意力机制与CNN的LSTM预测模型构建、训练、准确率验证与火险等级评估并附有改进建议。资源包共1个docx文件约18KB以文档形式集中呈现实现步骤、代码示例与逐段解释便于对照复现与灵活调整。目前已有57人学习。读者可借此掌握时间序列分析与深度学习在火灾预测中的落地思路理解数据合并、缺失值处理、特征归一化、时序划分及模型评估等关键环节并获得可迁移至其他灾害预警任务的建模框架与排错参考。1. 重庆火灾点预测这套 Python 方案到底能不能直接跑起来重庆山火频发每年夏季高温叠加地形起伏火点分布既受气象驱动也跟人口密度、土地利用、植被类型强相关。我拿到这份「Python 实现重庆火灾点分析与预测」的资源时第一反应不是看模型多花哨而是问三个问题多源数据怎么对齐、时间序列怎么切、CNNLSTM注意力这套组合到底解决什么问题。它面向的是有 Python 基础、做过数据分析或机器学习的人目标很明确——用历史火点、气象、人口、植被等数据预测未来火点频次并划分火险等级。适合做灾害预警、毕业设计、大数据课程项目的从业者。下面我按自己复现的路径拆一遍重点讲参数怎么设、坑在哪。2. 多源数据合并与时间序列切分关联键和缺失值怎么定2.1 五张表按 date 合并关联键选错后面全废这份资源假设火灾、人口、土地利用、植被、气象五类数据都存在且都有date列。合并逻辑是连续四次pd.merge用左连接保留火灾表所有记录。这里第一个关键点是关联键的粒度如果人口数据是年度统计气象是逐日观测直接按date合并会导致人口列大量重复或缺失。常见做法是先把所有数据统一到「日」粒度年度数据按年份广播到每一天或者改用yearmonth做复合键。import pandas as pd import numpy as np # 读取五类数据文件名按实际调整 fire_data pd.read_csv(fire_data.csv) population_data pd.read_csv(population_data.csv) land_use_data pd.read_csv(land_use_data.csv) vegetation_data pd.read_csv(vegetation_data.csv) weather_data pd.read_csv(weather_data.csv) # 统一日期类型避免字符串合并后无法按时间排序 for df in [fire_data, population_data, land_use_data, vegetation_data, weather_data]: df[date] pd.to_datetime(df[date]) # 按 date 左连接保留火灾表全部记录 merged_data fire_data.copy() for df in [population_data, land_use_data, vegetation_data, weather_data]: merged_data pd.merge(merged_data, df, ondate, howleft) # 缺失值处理数值列用前向填充分类列用众数 num_cols merged_data.select_dtypes(include[np.number]).columns merged_data[num_cols] merged_data[num_cols].fillna(methodffill).fillna(0)逻辑说明先统一date类型再合并避免字符串比较导致排序错乱。缺失值没有直接用fillna(0)因为气温、湿度填 0 会引入极端错误信号前向填充更合理。参数上howleft保证火灾记录不丢如果某天没有气象数据前向填充会沿用前一天的值这在连续高温场景下是合理近似。2.2 时间步长取 10 还是 30取决于火点自相关长度资源里time_steps 10意思是拿过去 10 个时间单位预测下一个点。这个值不是拍脑袋定的应该先画火点序列的自相关图ACF看相关性在滞后多少阶后衰减到置信区间内。重庆火点集中在夏季年际周期明显如果数据是逐日10 天窗口可能太短如果是逐月10 个月又太长。我一般会试 7、14、30 三档用验证集 RMSE 选最优。def create_sequences(data, target, time_steps): X, y [], [] for i in range(len(data) - time_steps): X.append(data[i:i time_steps]) y.append(target[i time_steps]) return np.array(X), np.array(y) time_steps 14 # 根据 ACF 结果调整 train_X, train_y create_sequences(train_features, train_target, time_steps) test_X, test_y create_sequences(test_features, test_target, time_steps)参数说明time_steps决定输入张量的第二维直接影响 LSTM 的记忆长度和计算量。create_sequences里target[i time_steps]是对齐的关键不能写成target[i]否则输入和标签错位模型学不到任何东西。训练集和测试集必须按时间顺序切不能随机打乱否则未来信息泄漏RMSE 会虚低。2.3 归一化必须分开 fit否则测试集信息泄漏资源里对特征和目标都用了MinMaxScaler但注意scaler.fit_transform在训练集上 fit测试集只能用transform。如果全量数据一起 fit测试集的极值会进入缩放器导致评估偏乐观。目标值反归一化时也要用同一个 scaler不能重新 fit。from sklearn.preprocessing import MinMaxScaler feature_scaler MinMaxScaler() target_scaler MinMaxScaler() # 只在训练集上 fit train_features feature_scaler.fit_transform(train_features_raw) test_features feature_scaler.transform(test_features_raw) train_target target_scaler.fit_transform(train_target_raw.reshape(-1, 1)) test_target target_scaler.transform(test_target_raw.reshape(-1, 1))逻辑说明特征和目标各用一个 scaler因为量纲不同。reshape(-1, 1)是因为MinMaxScaler要求二维输入。反归一化时用target_scaler.inverse_transform(predictions)保证预测值和真实值在同一尺度上比较。3. CNNLSTM注意力模型搭建层序、维度与编译参数3.1 Conv1D 提局部模式LSTM 抓长期依赖资源里的模型结构是Conv1D → MaxPooling1D → Flatten → LSTM → Attention → LSTM → Dense。这里有个明显问题Flatten放在 LSTM 之前会破坏时间维度LSTM 需要三维输入(samples, timesteps, features)而Flatten输出是二维。正确做法是去掉Flatten让 Conv1D 和 MaxPooling1D 保持时间维度直接接 LSTM。from keras.models import Sequential from keras.layers import LSTM, Dense, Conv1D, MaxPooling1D, Dropout, Activation import tensorflow as tf model Sequential() # Conv1D 提取局部时序模式paddingsame 保持时间步不变 model.add(Conv1D(filters32, kernel_size3, activationrelu, paddingsame, input_shape(time_steps, train_X.shape[2]))) model.add(MaxPooling1D(pool_size2)) # 去掉 Flatten保持三维输入给 LSTM model.add(LSTM(units64, return_sequencesTrue)) model.add(Dropout(0.2)) # 简化注意力用全连接层学习时间步权重 model.add(Dense(1, activationtanh)) model.add(Activation(softmax)) # 加权求和后接第二层 LSTM model.add(LSTM(units32)) model.add(Dense(units1)) model.compile(optimizeradam, lossmse, metrics[mae])参数说明filters32是卷积核数量火点数据特征维度不高时 32 够用特征多可加到 64。kernel_size3表示每次看 3 个连续时间步捕捉短期突变。paddingsame保证卷积后时间步不变否则 MaxPooling 后维度对不上。return_sequencesTrue让第一层 LSTM 输出完整序列供注意力层加权。Dropout(0.2)缓解过拟合火点数据样本量通常不大这个值可以调到 0.3。3.2 注意力层的实现与维度对齐资源里用了AttentionWithContext这不是 Keras 内置层需要自己实现或从第三方库引入。我一般用简化版对 LSTM 输出做tanh变换再经softmax得到时间步权重最后加权求和。这样不依赖外部包维度也好控制。from keras.layers import Layer import keras.backend as K class SimpleAttention(Layer): def __init__(self, **kwargs): super(SimpleAttention, self).__init__(**kwargs) def build(self, input_shape): self.W self.add_weight(nameatt_weight, shape(input_shape[-1], 1), initializerglorot_uniform, trainableTrue) self.b self.add_weight(nameatt_bias, shape(input_shape[1], 1), initializerzeros, trainableTrue) super(SimpleAttention, self).build(input_shape) def call(self, x): # x: (batch, timesteps, features) e K.tanh(K.dot(x, self.W) self.b) # (batch, timesteps, 1) a K.softmax(e, axis1) # 时间步权重 output x * a # 加权 return K.sum(output, axis1) # (batch, features) def compute_output_shape(self, input_shape): return (input_shape[0], input_shape[-1])逻辑说明build里定义可学习权重W和偏置bcall里先算每个时间步的得分再 softmax 归一化最后加权求和把时间维压缩掉。这样输出是二维(batch, features)可以直接接全连接层。注意K.dot(x, self.W)要求x最后一维和W第一维匹配W的 shape 是(features, 1)。3.3 编译参数adam 学习率与 loss 选择optimizeradam默认学习率 0.001火点预测这种回归任务通常够用。如果 loss 震荡可以降到 0.0005。lossmse对异常值敏感火点频次如果有极端高峰可以换huber损失。metrics[mae]方便观察平均绝对误差比只看 MSE 直观。from keras.optimizers import Adam model.compile(optimizerAdam(learning_rate0.001), lossmse, metrics[mae]) model.summary()参数说明learning_rate是核心调参项太大不收敛太小训练慢。model.summary()必须看确认每层输出维度正确特别是注意力层前后。如果报维度错误优先检查return_sequences和input_shape。4. 训练、预测与火险等级划分评估指标和阈值怎么定4.1 训练轮数与早停别硬跑 100 轮资源里epochs100但实际训练中验证 loss 可能 20 轮就不降了。加EarlyStopping和ModelCheckpoint是标准做法省时间也防过拟合。from keras.callbacks import EarlyStopping, ModelCheckpoint callbacks [ EarlyStopping(monitorval_loss, patience10, restore_best_weightsTrue), ModelCheckpoint(best_fire_model.h5, monitorval_loss, save_best_onlyTrue) ] history model.fit(train_X, train_y, epochs100, batch_size32, validation_data(test_X, test_y), callbackscallbacks, verbose1)参数说明patience10表示验证 loss 连续 10 轮不降就停restore_best_weightsTrue恢复最优权重。batch_size32是常见起点样本少可以降到 16。validation_data直接用测试集在这里只是演示严格做法应该从训练集再切验证集。4.2 反归一化与 RMSE 计算预测完必须反归一化才能和真实火点频次比较。RMSE 的量纲和原始数据一致方便解释。from sklearn.metrics import mean_squared_error, mean_absolute_error predictions model.predict(test_X) predictions target_scaler.inverse_transform(predictions) test_y_inv target_scaler.inverse_transform(test_y) rmse np.sqrt(mean_squared_error(test_y_inv, predictions)) mae mean_absolute_error(test_y_inv, predictions) print(fRMSE: {rmse:.2f}, MAE: {mae:.2f})逻辑说明inverse_transform用训练时 fit 的target_scaler不能重新 fit。RMSE 对大误差惩罚重MAE 更稳健两个一起看。如果 RMSE 远大于 MAE说明存在极端预测错误要检查异常火点。4.3 火险等级阈值别照搬 10 和 50资源里用10低风险、50中风险、50高风险。这个阈值必须根据实际火点频次分布定比如用分位数低于 50% 分位为低50%-80% 为中80% 以上为高。def assess_fire_risk_level(predictions, low_q0.5, high_q0.8): low_thresh np.quantile(predictions, low_q) high_thresh np.quantile(predictions, high_q) levels [] for pred in predictions: if pred low_thresh: levels.append(Low) elif pred high_thresh: levels.append(Medium) else: levels.append(High) return levels risk_levels assess_fire_risk_level(predictions.flatten()) print(pd.Series(risk_levels).value_counts())参数说明low_q和high_q是分位阈值根据业务对漏报和误报的容忍度调整。如果更怕漏报高风险把high_q降到 0.7。输出用value_counts看等级分布避免全挤在一个等级。5. 避坑与排查这几处翻车我踩过5.1 现象合并后行数暴涨原因关联键重复解决先去重pd.merge如果右表date有重复左表一行会匹配多行数据量翻倍。解决合并前对每张表按date去重或改用groupby(date).mean()聚合。5.2 现象LSTM 输入报维度错误原因Flatten 破坏了时间维解决去掉 FlattenConv1D输出是三维Flatten变二维LSTM 要求三维。解决去掉Flatten用paddingsame保持时间步或改用GlobalAveragePooling1D但会丢时间信息。5.3 现象RMSE 很低但预测全是均值原因目标归一化后方差太小解决检查 target 分布如果火点频次大部分是 0少数是几十归一化后 0 占绝大多数模型学会全预测 0 就能拿低 loss。解决对目标做对数变换np.log1p或改用分类思路先判有无火点。5.4 现象注意力权重全一样原因softmax 温度过高解决加温度系数或换加性注意力简化注意力在特征维度大时容易饱和softmax 输出接近均匀分布。解决在tanh后除以温度系数temperature或改用AttentionWithContext的加性形式。5.5 现象验证 loss 震荡不降原因学习率太大或 batch 太小解决降 lr 加 batchAdam默认 0.001 在样本少时偏大降到 0.0005。batch_size从 32 降到 16 或 8梯度更稳。同时检查数据是否按时间排序乱序会让 LSTM 学不到周期。6. 进阶技巧用滑动窗口做多步预测与模型融合单步预测只能看下一个时间点实际预警需要未来 3 天或 7 天的火险趋势。把create_sequences改成多步输出y取target[itime_steps:itime_stepshorizon]Dense输出维度改成horizon。这样一次预测多个未来值但误差会累积horizon不建议超过 7。def create_multistep_sequences(data, target, time_steps, horizon): X, y [], [] for i in range(len(data) - time_steps - horizon 1): X.append(data[i:i time_steps]) y.append(target[i time_steps:i time_steps horizon]) return np.array(X), np.array(y) horizon 3 train_X, train_y create_multistep_sequences(train_features, train_target, time_steps, horizon) # 模型最后一层改为 Dense(horizon)另一个技巧是模型融合CNNLSTM注意力跑一遍再用 XGBoost 对同样特征跑一遍两者预测值加权平均。我一般按验证集 RMSE 倒数分配权重RMSE 小的权重大。这样比单模型稳定尤其火点数据噪声大时。方法优势注意点单步 LSTM简单、误差不累积只能看下一个点多步 LSTM一次输出未来多天horizon 越大误差越大CNNLSTMAttention兼顾局部与长期依赖参数量大小样本易过拟合模型融合降低方差提升稳健性需要额外训练时间验证多步预测时别只看整体 RMSE要分 horizon 算第 1 天误差通常最小第 3 天明显变大。如果第 3 天 RMSE 超过第 1 天的两倍说明模型外推能力不足得加数据或减 horizon。从那以后我每次做时间序列预测都强制先画 ACF 定time_steps再切训练测试最后才碰模型结构。这套流程帮我省了至少三次返工。希望帮到你。本文还有配套的精品资源点击获取