import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.metrics import r2_score
# ---------------------- 全局设置 中文+随机种子 ----------------------
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
torch.manual_seed(42)
np.random.seed(42)
# 读取数据
df = pd.read_csv("data.csv")
x_raw = df[['x']].values.astype(np.float32)
y_raw = df[['y']].values.astype(np.float32)
x_tensor = torch.from_numpy(x_raw)
y_tensor = torch.from_numpy(y_raw)
# 神经网络定义:单层隐藏层,GELU激活
class MLP(nn.Module):
def __init__(self, hidden_n):
super().__init__()
self.net = nn.Sequential(
nn.Linear(1, hidden_n),
nn.GELU(),
nn.Linear(hidden_n, 1)
)
def forward(self, x):
return self.net(x)
# ---------------------- 任务2:不同隐藏节点n,cosine decay,lr=0.01,epoch=10000 ----------------------
n_list = [2,4,6,8,16,32] # 6个n,正好6张子图
epoch_total = 10000
lr0 = 0.01
criterion = nn.MSELoss()
res_fit = [] # 保存预测结果
res_loss = [] # 保存loss曲线
res_r2 = [] # 保存R2
lr_record = [] # 保存学习率变化(取n=8为例)
for n in n_list:
model = MLP(n)
opt = optim.Adam(model.parameters(), lr=lr0)
scheduler = optim.lr_scheduler.CosineAnnealingLR(opt, T_max=epoch_total, eta_min=0)
loss_hist = []
for ep in range(epoch_total):
opt.zero_grad()
pred = model(x_tensor)
loss = criterion(pred, y_tensor)
loss.backward()
opt.step()
scheduler.step()
loss_hist.append(loss.item())
if n == 8:
lr_record.append(opt.param_groups[0]['lr'])
# 预测 & R2
with torch.no_grad():
y_pred = model(x_tensor).numpy()
r2 = r2_score(y_raw, y_pred)
res_fit.append(y_pred)
res_loss.append(loss_hist)
res_r2.append(r2)
# 图1:6个子图,不同n拟合效果
fig1, axes = plt.subplots(2,3,figsize=(14,9))
axes = axes.flatten()
for i, ax in enumerate(axes):
ax.scatter(x_raw, y_raw, s=10, label="真实数据")
ax.plot(x_raw, res_fit[i], c='red', lw=2, label=f"n={n_list[i]}, R2={res_r2[i]:.4f}")
ax.set_title(f"隐藏层节点数 n={n_list[i]}")
ax.legend()
ax.grid(alpha=0.3)
fig1.suptitle("不同隐藏层节点数的拟合效果", fontsize=15)
plt.tight_layout()
plt.savefig("fit_compare.jpg", dpi=300)
# 图2:不同n训练损失曲线
plt.figure(figsize=(10,6))
colors = ["#e74c3c","#3498db","#2ecc71","#9b59b6","#f39c12","#1abc9c"]
for i, n in enumerate(n_list):
plt.plot(res_loss[i], label=f"n={n}", color=colors[i])
plt.xlabel("Epoch")
plt.ylabel("MSE损失")
plt.yscale("log")
plt.title("不同隐藏层节点数的训练损失曲线")
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig("loss_n_curve.jpg", dpi=300)
# 图3:cosine decay学习率变化曲线(n=8)
plt.figure(figsize=(8,5))
plt.plot(lr_record)
plt.xlabel("Epoch")
plt.ylabel("学习率")
plt.title("Cosine Decay学习率变化曲线")
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig("lr_cosine_curve.jpg", dpi=300)
# 图4:柱状图 R2对比
plt.figure(figsize=(8,5))
plt.bar([str(i) for i in n_list], res_r2, color=colors)
plt.xlabel("隐藏层节点数 n")
plt.ylabel("决定系数 R²")
plt.title("不同隐藏层节点数的决定系数对比")
plt.ylim(0, 1.05)
for idx,val in enumerate(res_r2):
plt.text(idx, val+0.01, f"{val:.4f}", ha="center")
plt.tight_layout()
plt.savefig("r2_bar.jpg", dpi=300)
# ---------------------- 任务3:固定n=8,关闭cosine decay,lr=[0.001,0.01,0.1,1] ----------------------
lr_test_list = [0.001, 0.01, 0.1, 1.0]
loss_lr_all = []
for lr in lr_test_list:
model = MLP(hidden_n=8)
opt = optim.Adam(model.parameters(), lr=lr)
# 不设置scheduler,lr固定不变
loss_hist = []
for ep in range(epoch_total):
opt.zero_grad()
pred = model(x_tensor)
loss = criterion(pred, y_tensor)
loss.backward()
opt.step()
loss_hist.append(loss.item())
loss_lr_all.append(loss_hist)
# 图5:不同学习率损失曲线
fig2, axes = plt.subplots(2,2,figsize=(12,9))
axes = axes.flatten()
for i, ax in enumerate(axes):
ax.plot(loss_lr_all[i])
ax.set_title(f"学习率 lr={lr_test_list[i]}")
ax.set_xlabel("Epoch")
ax.set_ylabel("MSE Loss")
ax.grid(alpha=0.3)
fig2.suptitle("不同学习率下的训练损失曲线(关闭cosine decay)", fontsize=15)
plt.tight_layout()
plt.savefig("lr_loss_compare.jpg", dpi=300)
plt.show()
print("===== 任务2结果R2 =====")
for n,r2 in zip(n_list, res_r2):
print(f"n={n}, R2={r2:.4f}")
print("===== 全部绘图完成 =====")