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.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# ========== 全局配置 中文+随机种子 ==========
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
torch.manual_seed(42)
np.random.seed(42)
# ========== 1.读取并预处理数据 ==========
df = pd.read_csv("pima-diabetes.csv")
X = df.drop("Outcome", axis=1).values
y = df["Outcome"].values.reshape(-1, 1)
# 划分训练集、测试集,分层抽样保证正负样本比例
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
# 转为torch张量
X_train = torch.tensor(X_train, dtype=torch.float32)
y_train = torch.tensor(y_train, dtype=torch.float32)
X_test = torch.tensor(X_test, dtype=torch.float32)
y_test = torch.tensor(y_test, dtype=torch.float32)
# ========== 2.定义网络 ==========
class DiabetesNet(nn.Module):
def __init__(self):
super().__init__()
self.net = nn.Sequential(
nn.Linear(8, 64),
nn.ReLU(),
nn.Linear(64, 32),
nn.ReLU(),
nn.Linear(32, 1),
nn.Sigmoid()
)
def forward(self, x):
return self.net(x)
loss_fn = nn.BCELoss()
lr = 0.001
max_epoch = 150
# ===================== 第一部分:无早停,完整训练(观察过拟合) =====================
print("===== 开始无早停训练 =====")
model1 = DiabetesNet()
opt1 = optim.Adam(model1.parameters(), lr=lr)
train_loss_no_early = []
test_loss_no_early = []
for epoch in range(max_epoch):
model1.train()
opt1.zero_grad()
pred_train = model1(X_train)
tr_loss = loss_fn(pred_train, y_train)
tr_loss.backward()
opt1.step()
model1.eval()
with torch.no_grad():
pred_test = model1(X_test)
te_loss = loss_fn(pred_test, y_test)
train_loss_no_early.append(tr_loss.item())
test_loss_no_early.append(te_loss.item())
if (epoch+1) % 20 == 0:
print(f"Epoch {epoch+1:3d} | Train:{tr_loss:.4f} | Test:{te_loss:.4f}")
# 绘图:无早停loss曲线
plt.figure(figsize=(8, 5))
plt.plot(train_loss_no_early, label="Train Loss")
plt.plot(test_loss_no_early, label="Test Loss")
plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.title("Training and Testing Loss Curves(无早停,出现过拟合)")
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig("loss_no_earlystop.jpg", dpi=300)
plt.show()
# ===================== 第二部分:带早停法训练 =====================
print("\n===== 开始带早停训练 =====")
model2 = DiabetesNet()
opt2 = optim.Adam(model2.parameters(), lr=lr)
train_loss_early = []
test_loss_early = []
patience = 15 # 连续15轮测试loss不下降就停止
best_test_loss = float("inf")
wait = 0
stop_epoch = max_epoch # 记录停止轮次
for epoch in range(max_epoch):
model2.train()
opt2.zero_grad()
pred_train = model2(X_train)
tr_loss = loss_fn(pred_train, y_train)
tr_loss.backward()
opt2.step()
model2.eval()
with torch.no_grad():
pred_test = model2(X_test)
te_loss = loss_fn(pred_test, y_test)
train_loss_early.append(tr_loss.item())
test_loss_early.append(te_loss.item())
# 早停判断逻辑
if te_loss < best_test_loss:
best_test_loss = te_loss
wait = 0
else:
wait += 1
if wait >= patience:
stop_epoch = epoch + 1
print(f"✅ 触发早停,Epoch={stop_epoch},最佳测试损失:{best_test_loss:.4f}")
break
if (epoch+1) % 20 == 0:
print(f"Epoch {epoch+1:3d} | Train:{tr_loss:.4f} | Test:{te_loss:.4f}")
# 绘图:带早停loss曲线,标记停止点
plt.figure(figsize=(8, 5))
plt.plot(train_loss_early, label="Train Loss")
plt.plot(test_loss_early, label="Test Loss")
plt.axvline(x=stop_epoch-1, color="red", linestyle="--", label=f"早停点 Epoch={stop_epoch}")
plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.title("Training and Testing Loss Curves(加入早停法 Early Stopping)")
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig("loss_with_earlystop.jpg", dpi=300)
plt.show()
print("\n✅ 全部训练完成,两张图片已保存:")
print("loss_no_earlystop.jpg 无早停(过拟合对照)")
print("loss_with_earlystop.jpg 带早停法")