回归任务:通过输入一些样本数据或者特征,经过多层神经网络,最后能得到一个预测值。
1.气温数据处理
1.数据加载
1 2 3 4 5 6 7
| def load_csv(): path = Path("data") filename = "temps.csv" return pd.read_csv(path / filename)
print(f"shape:{features.shape}\n columns:{features.columns}") print(features[:5])
|
1 2 3 4 5 6 7 8 9 10
| shape:(348, 9)
columns:Index(['year', 'month', 'day', 'week', 'temp_2', 'temp_1', 'average', 'actual', 'friend'], dtype='object')
year month day week temp_2 temp_1 average actual friend 0 2016 1 1 Fri 45 45 45.6 45 29 1 2016 1 2 Sat 44 45 45.7 44 61 2 2016 1 3 Sun 45 44 45.8 41 56 3 2016 1 4 Mon 44 41 45.9 40 53 4 2016 1 5 Tues 41 40 46.0 44 41
|
temps.csv
是个348 * 9的数据,temp_2
是那一天对应的前天的最高温度,temp_1
是那一天对应的昨天的最高温度,average
历史上相同month
和day
的平均最高温度,actual
当天实际问题,也是真实标签值,friend
朋友预测的最高温度。
简而言之,用year month day week temp_2 temp_1 average friend
对应的数据预测actual
对应的。
2.独热编码
1 2 3
| features = load_csv() features = pd.get_dummies(features) print(features[:5])
|
独热编码(One-Hot Encoding)将每个类别转换为一个独立的二进制向量,有以下几个特点:
- 无序特征表示:用于无顺序关系的类别特征。例如,颜色,形状
- 维度增加:每个类别会增加一列,维度的增加,可能会增加计算的复杂性
- 稀疏矩阵:生成的矩阵通常是稀疏的,即大多数值都是 0。因此,在某些情况下,可以使用稀疏矩阵来节省内存。
- 没有信息损失:每个类别都被完整地表示为一个向量,没有丢失任何关于类别的信息
- 不引入大小关系:不会误导模型认为类别之间存在某种顺序或大小关系。例如,Red、Green 和 Blue 都被等价地表示为三个不同的向量,而不是 Red > Green > Blue 之类的关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 编码前 week 0 Fri 1 Sat 2 Sun 3 Mon 4 Tues 编码后 week_Fri week_Mon week_Sat week_Sun week_Thurs week_Tues week_Wed 0 True False False False False False False 1 False False True False False False False 2 False False False True False False False 3 False True False False False False False 4 False False False False False True False
|
3.日期转化与数据绘制
1 2 3 4 5 6
| def handle_datetime(features): years = features['year'] months = features['month'] days = features['day'] return [datetime.datetime(year, month, day) for year, month, day in zip(years, months, days)]
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| def draw_csv(dates, features): plt.style.use('fivethirtyeight') fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2, figsize=(10, 10)) fig.autofmt_xdate(rotation=45) ax1.plot(dates, features['actual']) ax1.set_xlabel('') ax1.set_ylabel('Temperature') ax1.set_title('Max Temp') ax2.plot(dates, features['temp_1']) ax2.set_xlabel('') ax2.set_ylabel('Temperature') ax2.set_title('Previous Max Temp') ax3.plot(dates, features['temp_2']) ax3.set_xlabel('Date') ax3.set_ylabel('Temperature') ax3.set_title('Two Days Prior Max Temp') ax4.plot(dates, features['friend']) ax4.set_xlabel('Date') ax4.set_ylabel('Temperature') ax4.set_title('Friend Estimate') plt.tight_layout(pad=2) plt.show()
|

4.数据处理
1 2 3 4 5 6
| from sklearn import preprocessing
def get_train_data(features): data = features.drop('actual', axis=1) data = np.array(data) return preprocessing.StandardScaler().fit_transform(data)
|
preprocessing.StandardScaler()
: 对特征数据进行标准化,将数据的均值调整为 0,标准差调整为 1。对于特征的取值范围差异较大时,模型可能会对大范围的特征更敏感,标准化可以使得每个特征具有相似的尺度,从而提高模型训练的效果。另外在数据标准化后一些优化算法(如梯度下降法)的收敛速度会加快。
2.模型定义
1.构造参数
input_size
: 输入特征数量
hidden_size
: 隐藏层的神经元数量
output_size
:输出的大小(在回归任务中通常是 1,表示预测值)
2.全连接层
nn.Linear(input_size, hidden_size)
创建了一个 全连接层,将输入数据从 input_size
维度映射到 hidden_size
维度。即:
• 输入层的特征数为 input_size
,每个样本有 input_size
个特征
• 隐藏层有hidden_size
个神经元
nn.Linear(hidden_size, output_size)
另一个全连接层,它将隐藏层的输出从 hidden_size 维度映射到 output_size 维度。对于回归任务,output_size 通常是 1,表示预测一个连续值。
3.激活函数
nn.Sigmoid()
:Sigmoid激活函数,对隐藏层的输出进行非线性变换。Sigmoid 函数的输出范围是 (0, 1),它通常用于二分类任务中的输出层,或者用作隐藏层的激活函数来引入非线性。
4.前向传播
forward()
定义了数据在网络中的流动方式。每当数据通过模型时,都会调用这个方法。
- 输入数据 x 通过第一层 fc1(即全连接层)进行计算。输出是大小为 hidden_size 的向量
- 经过全连接层计算后,输出将通过 Sigmoid 激活函数,引入非线性变换
- 隐藏层的输出 x 再通过第二层 fc2(即另一个全连接层)进行计算,得到最终的输出。此时,输出是大小为 output_size 的向量
1 2 3 4 5 6 7 8 9 10 11
| class Weather_forecast_NN(nn.Module): def __init__(self, input_size, hidden_size, output_size): super(Weather_forecast_NN, self).__init__() self.fc1 = nn.Linear(input_size, hidden_size) self.sigmoid = nn.Sigmoid() self.fc2 = nn.Linear(hidden_size, output_size) def forward(self, x): x = self.fc1(x) x = self.sigmoid(x) x = self.fc2(x) return x
|
3.模型训练、验证
1.损失函数
torch.nn.MSELoss(reduction='mean')
均方误差(MSE)损失函数,表示计算损失时将所有样本的损失求平均值,常用于回归任务,衡量预测值与真实值之间的差距。
2.优化器
Adam 优化器用于一种自适应优化算法,能够在训练过程中调整学习率,用于优化模型参数,学习率设置为0.001,为何设置0.001?因为0.001是一个比较保守的学习率值,它既不会太大导致不稳定,也不会太小导致收敛过慢,因此通常能够平衡训练速度与稳定性。
3.Mini-batch
将训练数据分成多个小批次(mini-batches),并在每个小批次上执行一次梯度更新。这样做的好处是避免了批量梯度下降的高计算开销,同时还能减少随机梯度下降的噪声,具有较好的稳定性和计算效率。
优点:
- 内存高效:每次仅使用小批次的数据进行计算,适合大规模数据集
- 更稳定的训练过程:相比随机梯度下降,mini-batch 提供了更平滑的更新,使得训练过程更为稳定
- 利用并行计算:小批量训练可以很好地与 GPU 等硬件加速工具配合,提高训练效率
- 较快的收敛速度:由于每次更新的样本数较多,相比单样本更新(SGD),mini-batch 方法通常能更快收敛
缺点:
- 收敛不如批量梯度下降精确:每次更新基于一个小批量数据,梯度计算不如批量梯度下降精确
- 选择批次大小难度:需要根据任务、数据量和硬件等因素选择合适的 mini-batch 大小。如果选择不当,可能会导致训练不稳定或收敛过慢
4.tensor转换
提取当前批次的输入数据,转换为 PyTorch 张量。requires_grad=True
表示我们需要计算该数据的梯度。不需要为目标 yy 设置 requires_grad
,因为它不是模型的参数,yy中使用 .view(-1, 1) 将其形状调整为 (batch_size, 1),确保目标数据与模型输出的形状一致。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| def train(train_data, valid_data): input_size = train_data.shape[1] hidden_size = 128 output_size = 1 batch_size = 16 model = Weather_forecast_NN(input_size, hidden_size, output_size) mse_loss = torch.nn.MSELoss(reduction='mean') optimizer = torch.optim.Adam(model.parameters(), lr=0.001) losses = [] for i in range(1000): batch_loss = [] for start in range(0, len(train_data), batch_size): end = start + batch_size if start + batch_size < len(train_data) else len(train_data) xx = torch.tensor(train_data[start:end], dtype=torch.float32, requires_grad=True) yy = torch.tensor(valid_data[start:end], dtype=torch.float32).view(-1, 1) prediction = model(xx) loss = mse_loss(prediction, yy) optimizer.zero_grad() loss.backward() optimizer.step() batch_loss.append(loss.data.numpy()) if i % 100 == 0: losses.append(np.mean(batch_loss)) print(i, np.mean(batch_loss)) return model
|
4.模型测试
使用matplotlib
绘制出真实天气与预测天气的对比图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| def draw_valid_and_predictions_data(dates, model, train_data, valid_data): x = torch.tensor(train_data, dtype=torch.float) predict = model(x).data.numpy() plt.plot(dates, valid_data, 'b-', label='actual') plt.plot(dates, predict, 'ro', label='prediction') plt.xticks(rotation=60) plt.legend() plt.xlabel('Date') plt.ylabel('Maximum Temperature (F)') plt.title('Actual and Predicted Values') plt.show()
|

5.总结
本文主要讲的是一个基于神经网络的天气预测模型,通过读取天气数据、预处理、模型训练展示了如何使用神经网络来解决一个回归问题,涉及知识点:
- 数据标准化处理,对训练数据的特征进行归一化处理,使得每个特征具有均值为 0,标准差为 1,减少特征尺度差异对训练过程的影响
- 数据可视化,matplotlib绘制气温变化图和对比图
- 神经网络模型,一个简单的前馈神经网络(1 个隐藏层包含128个神经元)
- 损失函数与优化器
- 损失函数:使用 均方误差(MSE)来衡量模型的预测结果与真实结果之间的差距
- 优化器:使用 Adam 优化器,这是一种自适应优化算法,能够动态调整学习率,通常能带来更好的训练效果
7.备注
环境:
- mac: 15.2
- python: 3.12.4
- pytorch: 2.5.1
- matplotlib: 3.8.4
- numpy: 1.26.4
- panda: 2.2.2
数据集:
https://github.com/keychankc/dl_code_for_blog/tree/main/002_nn_weather_forecast/data
完整代码:
https://github.com/keychankc/dl_code_for_blog/tree/main/002_nn_weather_forecast