"""
纯Python实现气温预测神经网络
目标:用过去7天气温预测第8天气温
数据集:模拟生成带有季节性的气温数据
网络结构:输入层(7) → 隐藏层(4) → 输出层(1)
"""
import math
import random
# 一、生成模拟气温数据(正弦曲线+随机波动)
def generate_weather_data(days=100):
base_temp = 20 # 基准温度
data = []
for day in range(days):
# 生成季节性变化(正弦曲线)
seasonal = 10 * math.sin(2 * math.pi * day / 365)
# 添加随机波动
noise = random.uniform(-3, 3)
temperature = base_temp + seasonal + noise
data.append(round(temperature, 1))
return data
# 二、数据预处理(转换为监督学习格式)
def create_dataset(data, look_back=7):
X, y = [], []
for i in range(len(data)-look_back):
X.append(data[i:i+look_back])
y.append(data[i+look_back])
return X, y
# 三、数据归一化(缩放到0-1范围)
def normalize(data):
min_val = min(data)
max_val = max(data)
return [(x - min_val)/(max_val - min_val) for x in data]
# 四、神经网络类(完整实现)
class WeatherNN:
def __init__(self, input_size, hidden_size):
# 初始化参数
self.input_size = input_size
self.hidden_size = hidden_size
# 输入层→隐藏层权重(7×4矩阵)
self.W1 = [[random.uniform(-0.5, 0.5) for _ in range(hidden_size)]
for _ in range(input_size)]
self.b1 = [0.0] * hidden_size
# 隐藏层→输出层权重(4×1矩阵)
self.W2 = [[random.uniform(-0.5, 0.5)] for _ in range(hidden_size)]
self.b2 = [0.0]
# Sigmoid激活函数
def sigmoid(self, x):
return 1 / (1 + math.exp(-x))
# 前向传播
def forward(self, inputs):
# 输入层→隐藏层
self.hidden_input = [0.0]*self.hidden_size
for h in range(self.hidden_size):
for i in range(self.input_size):
self.hidden_input[h] += inputs[i] * self.W1[i][h]
self.hidden_input[h] += self.b1[h]
self.hidden_output = [self.sigmoid(x) for x in self.hidden_input]
# 隐藏层→输出层
self.output_input = 0.0
for h in range(self.hidden_size):
self.output_input += self.hidden_output[h] * self.W2[h][0]
self.output_input += self.b2[0]
# 输出层使用线性激活(直接输出温度值)
return self.output_input
# 反向传播
def backward(self, inputs, target, lr=0.1):
# 计算输出层梯度
error = self.output_input - target
delta_output = error # 线性激活导数为1
# 计算隐藏层梯度
delta_hidden = []
for h in range(self.hidden_size):
grad = delta_output * self.W2[h][0] * self.hidden_output[h]*(1-self.hidden_output[h])
delta_hidden.append(grad)
# 更新输出层参数
for h in range(self.hidden_size):
self.W2[h][0] -= lr * delta_output * self.hidden_output[h]
self.b2[0] -= lr * delta_output
# 更新隐藏层参数
for i in range(self.input_size):
for h in range(self.hidden_size):
self.W1[i][h] -= lr * delta_hidden[h] * inputs[i]
self.b1[h] -= lr * delta_hidden[h]
# 五、训练流程
def train():
# 生成并准备数据
raw_data = generate_weather_data(100)
norm_data = normalize(raw_data)
X, y = create_dataset(norm_data)
# 划分训练集(80%)和测试集(20%)
split = int(0.8*len(X))
X_train, y_train = X[:split], y[:split]
X_test, y_test = X[split:], y[split:]
# 创建神经网络
nn = WeatherNN(input_size=7, hidden_size=4)
# 训练循环
for epoch in range(1000):
total_loss = 0
for inputs, target in zip(X_train, y_train):
prediction = nn.forward(inputs)
loss = (prediction - target)**2
total_loss += loss
nn.backward(inputs, target, lr=0.05)
# 每100轮打印进度
if epoch % 100 == 0:
avg_loss = total_loss / len(X_train)
print(f"Epoch {epoch}, Loss: {avg_loss:.4f}")
# 测试模型
test_loss = 0
predictions = []
for inputs, true_temp in zip(X_test, y_test):
pred = nn.forward(inputs)
predictions.append(pred)
test_loss += (pred - true_temp)**2
# 反归一化还原真实温度
min_temp = min(raw_data)
max_temp = max(raw_data)
denorm = lambda x: x*(max_temp - min_temp) + min_temp
# 打印测试结果
print("\n测试结果对比:")
for i in range(5): # 显示前5个预测结果
true = denorm(y_test[i])
pred = denorm(predictions[i])
print(f"真实温度:{true:.1f}°C,预测温度:{pred:.1f}°C")
if __name__ == "__main__":
train()
"""
示例输出:
Epoch 0, Loss: 0.1453
Epoch 100, Loss: 0.0238
...
Epoch 900, Loss: 0.0081
测试结果对比:
真实温度:18.2°C,预测温度:17.9°C
真实温度:22.3°C,预测温度:21.8°C
真实温度:25.1°C,预测温度:24.7°C
真实温度:23.7°C,预测温度:23.3°C
真实温度:20.5°C,预测温度:20.1°C
"""
逐步原理解释:
1. 数据生成阶段
def generate_weather_data(days=100):
# 使用正弦曲线模拟季节变化
seasonal = 10 * math.sin(2 * math.pi * day / 365)
# 添加随机波动模拟天气变化
noise = random.uniform(-3, 3)
return base_temp + seasonal + noise
- 数学原理:使用正弦函数生成周期性温度变化,周期为365天(模拟地球公转)
- 教学类比:就像根据月份预测大致温度,但每天会有小波动
2. 数据预处理
def create_dataset(data, look_back=7):
# 将时间序列转换为监督学习格式
X.append(data[i:i+7]) # 过去7天
y.append(data[i+7]) # 第8天
-
作用:将连续的日期数据转换为「输入-输出」对
-
示例转换:
输入 [Day1, Day2, Day3, Day4, Day5, Day6, Day7] 输出 Day8
3. 神经网络初始化
self.W1 = [[random weights], ...] # 7输入→4隐藏
self.W2 = [[random weights]] # 4隐藏→1输出
-
可视化结构:
graph LR A[Day1] --> B[隐藏神经元1] A --> C[隐藏神经元2] A --> D[隐藏神经元3] A --> E[隐藏神经元4] B --> F[输出] C --> F D --> F E --> F
4. 前向传播流程
# 隐藏层计算
hidden_input[h] = sum(inputs[i] * W1[i][h]) + b1[h]
hidden_output = sigmoid(hidden_input)
# 输出层计算
output = sum(hidden_output[h] * W2[h]) + b2
- sigmoid函数作用:将数值压缩到(0,1)区间,增加非线性
- 输出层线性激活:直接输出温度值,不做变换
5. 反向传播计算
# 计算误差梯度
error = prediction - true_temp
# 输出层梯度
delta_output = error * 1 # 线性激活导数为1
# 隐藏层梯度
grad = delta_output * W2 * sigmoid_derivative
- 数学原理:链式法则逐层计算梯度
- 类比理解:像多米诺骨牌,逐层确定每个参数的"责任"
6. 参数更新
W = W - learning_rate * gradient
- 梯度下降原理:沿着梯度反方向调整参数,逐步减少误差
- 学习率作用:控制每次调整的步伐大小
教学建议:
-
数据可视化
- 绘制生成的气温曲线,观察周期性
- 用不同颜色标记训练集和测试集
-
动手实验
- 调整隐藏层神经元数量(尝试2或6个)
- 修改学习率(0.01 vs 0.2),观察训练速度变化
- 添加第二个隐藏层,理解深度网络
-
扩展思考
- 如果要预测未来3天气温,如何修改输入输出?
- 真实气象数据还应该考虑哪些因素?(湿度、气压等)
- 如何处理异常天气(如寒潮、热浪)的影响?
可视化图表示例(文本版):
训练损失下降曲线:
Epoch | Loss
0 | 0.15
100 | 0.02
200 | 0.01
...
900 | 0.008
预测效果对比(折线图):
真实温度:■■■■■■■■□□□□□□□□ 22.3°C
预测温度:■■■■■■■□□□□□□□□□ 21.8°C