深度学习_全连接网络实现回归任务

波士顿房价回归任务为案例,提供两套实现:底层手写实现帮助吃透原理;PyTorch 高级 API 用于工程开发。完整覆盖数据集、预处理、训练循环、模型保存推理、调优拓展。

一、数据集:波士顿房价数据集

  • 样本:506 条,13 维输入特征,目标MEDV房价中位数(回归任务)
  • 特征包含:犯罪率、住宅用地占比、房间数量、距离市中心距离、师生比例、低收入人口占比等。

二、回归项目完整标准流程

全连接网络以nn.Linear线性层为核心,完整流程:

  1. 读取原始数据集
  2. 数据集划分 + 特征标准化预处理
  3. 构建数据集、批处理加载器
  4. 搭建网络模型(单层线性 / 多层全连接)
  5. 训练循环:前向传播→计算损失→反向传播→参数更新→梯度清零
  6. 独立测试集做模型评估
  7. 模型持久化保存,编写推理预测函数

拓展调优方向:修改预处理、调整 batch‑size、增加网络层数、更换激活函数、调整学习率 / 优化器、可视化损失、模型保存加载。

三、方案一:底层手动手写实现(原理学习)

不使用nn.Linear、Dataset、优化器高层封装,手动实现全部逻辑

1. 数据预处理

  1. pandas 读取 csv,切分训练集 / 测试集;转为 float32 张量。
  2. Z‑score 标准化 ,只使用训练集的均值 μ、标准差 σ做缩放;加极小值\(10^{-9}\)防止标准差为 0 造成除零报错。

\(X_{scaled}=\frac{X-\mu}{\sigma+1e^{-9}}\)

标准化作用:消除不同特征量纲差异,加速梯度下降收敛。

2. 手写批数据加载器

使用 Python 生成器yield自定义get_dataloader,按batch_size切分数据;

  • 计算总批次:向上取整\(\lceil 样本数/batch\_size\rceil\)
  • 处理最后一个批次样本不足的边界情况。

3. 手动定义模型、损失

  1. 手动创建可求导权重w、偏置b,设置requires_grad=True开启自动微分。
  2. 线性模型:\(\hat y = X @ w + b\),矩阵乘法实现线性变换。
  3. 手写 MSE 均方误差损失:

\(M S E=\frac{1}{n} \sum_{i=1}^{n}\left(y_{i}-\hat{y}_{i}\right)^{2}\)

4. 手写完整训练循环

训练的 5 个核心步骤:

  1. 前向传播y_pred = model(batch_X,w,b),计算预测值
  2. 计算损失:对比预测与真实标签
  3. 反向传播loss.backward()自动求导,计算 w、b 梯度
  4. 手动参数更新:\(w.data -= lr * w.grad\);\(b.data -= lr * b.grad\)
  5. 梯度清零w.grad.zero_()b.grad.zero_(),避免梯度累积叠加。

底层实现中没有优化器对象,参数更新、梯度清零全部手写。

5. 底层方式模型保存与推理

  1. 保存:把w、b、训练集均值mu、sigma存入字典,torch.save()保存为.pt文件。

注意推理时必须同时保存标准化统计量,新样本推理要复用训练集的 μ、σ。

  1. 推理函数:输入数据转为张量,执行标准化,做矩阵运算输出预测房价。
python 复制代码
import pandas as pd
import torch
import math
from sklearn.model_selection import train_test_split

# ==========1. 数据加载与预处理 ==========
data = pd.read_csv("boston_house_prices.csv", skiprows=1)
X = data.loc[:, :"LSTAT"].to_numpy()
y = data.loc[:, "MEDV"].to_numpy().reshape(-1,1)

# 划分训练、测试集
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=0
)

# 转为张量
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)

# Z‑score标准化,使用训练集统计量,加1e‑9防止除零
mu = X_train.mean(axis=0)
sigma = X_train.std(axis=0)
X_train = (X_train - mu) / (sigma + 1e-9)
X_test = (X_test - mu) / (sigma + 1e-9)

# ==========2.手写生成器dataloader ==========
def get_dataloader(X, y, batch_size=16):
    num_samples, _ = X.shape
    num_batches = math.ceil(num_samples / batch_size)
    for idx in range(num_batches):
        batch_X = X[idx*batch_size : (idx+1)*batch_size, :]
        batch_y = y[idx*batch_size : (idx+1)*batch_size, :]
        if len(batch_X) > 0:
            yield batch_X, batch_y

# ==========3.手动定义模型、损失 ==========
# 初始化可求导权重、偏置
w = torch.randn(13, 1, dtype=torch.float32, requires_grad=True)
b = torch.randn(1, dtype=torch.float32, requires_grad=True)

def model(X, w, b):
    return X @ w + b

def mse_loss_fn(y_pred, y_true):
    return ((y_pred - y_true) ** 2).mean()

# ==========4.手写训练循环 ==========
epochs = 50
lr = 1e-3
batch_size =16

def get_loss(dataloader):
    total_loss = 0.0
    count =0
    for bx,by in dataloader:
        pred = model(bx,w,b)
        loss = mse_loss_fn(pred,by)
        total_loss += loss.item()
        count +=1
    return total_loss / count

for epoch in range(epochs):
    train_loader = get_dataloader(X_train, y_train, batch_size)
    for batch_X, batch_y in train_loader:
        # 1 前向传播
        y_pred = model(batch_X, w, b)
        # 2 计算损失
        loss = mse_loss_fn(y_pred, batch_y)
        # 3 反向传播求梯度
        loss.backward()
        #4 手动更新参数
        w.data -= lr * w.grad
        b.data -= lr * b.grad
        #5 梯度清零,防止梯度累加
        w.grad.zero_()
        b.grad.zero_()

    train_loss = get_loss(get_dataloader(X_train,y_train,batch_size))
    test_loss = get_loss(get_dataloader(X_test,y_test,batch_size))
    print(f"Epoch:{epoch+1:2d} | Train Loss:{train_loss:.3f} | Test Loss:{test_loss:.3f}")

# ==========5.底层方式模型保存与推理 ==========
# 保存,把均值、标准差、权重全部存入
w.requires_grad = False
b.requires_grad = False
save_dict = {"mu":mu, "sigma":sigma, "w":w, "b":b}
torch.save(save_dict, "model_raw.pt")

# 推理函数
def infer(x, mu, sigma, w, b, normalized=False):
    if not torch.is_tensor(x):
        x = torch.tensor(x, dtype=torch.float32)
    if x.ndim == 1:
        x = x.view(1,-1)
    if not normalized:
        x = (x - mu)/(sigma +1e-9)
    return model(x,w,b)

# 加载测试
ckpt = torch.load("model_raw.pt", weights_only=True)
pred_result = infer(X_test[0], ckpt["mu"], ckpt["sigma"], ckpt["w"], ckpt["b"])
print("单样本预测房价:", pred_result.item())

核心公式

\(X_{scaled}=\frac{X-\mu}{\sigma+1e^{-9}}\)

\(M S E=\frac{1}{n} \sum_{i=1}^{n}\left(y_{i}-\hat{y}_{i}\right)^{2}\)

\(\hat y = X @ w + b\)

四、方案二:PyTorch 高级 API 实现(工程开发)

使用 PyTorch 封装组件,代码简洁,工业项目优先使用。

1. 数据集与 DataLoader

  1. 自定义类继承torch.utils.data.Dataset,必须实现两个魔术方法:
    • __getitem__(idx):取出单条样本
    • __len__():返回数据集总样本数量
  2. DataLoader:自动完成批处理、多线程、shuffle 打乱;
    • 训练集:shuffle=True打乱样本顺序;
    • 测试集:shuffle=False,不需要打乱。

2. 模型、损失、优化器

  1. 模型:直接实例化nn.Linear(in_features=13, out_features=1),内部自动初始化权重与偏置。

  2. 损失函数:nn.MSELoss()

  3. 优化器:

    优化器 特点 适用场景
    SGD 简单稳定,适合入门学习 基础练习
    Adam 自适应学习率,收敛快 绝大多数任务
    RMSprop 适配序列数据 RNN、时序模型

3. 高级版训练循环重点细节

  1. model.train():训练模式,启用 Dropout、BN 等训练专属逻辑。
  2. model.eval():评估模式,关闭网络随机性;评估代码块包裹with torch.no_grad()关闭梯度计算,节省显存。
  3. 训练四步:前向传播→算 loss→loss.backward()optimizer.step()更新参数→optimizer.zero_grad()清空梯度。

4. 高级 API 模型保存与推理

  1. 保存:字典存储标准化参数mu、sigma + model.state_dict()(网络权重状态)。
  2. 加载:重建网络模型,使用model.load_state_dict()恢复权重;推理阶段放在with torch.no_grad()上下文管理器。
python 复制代码
import pandas as pd
import torch
from torch import nn
from torch.utils.data import Dataset, DataLoader
from sklearn.model_selection import train_test_split

# ==========1.数据加载、预处理(同底层版本) ==========
data = pd.read_csv("boston_house_prices.csv", skiprows=1)
X = data.loc[:, :"LSTAT"].to_numpy()
y = data.loc[:, "MEDV"].to_numpy().reshape(-1,1)
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.2,random_state=0)

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)

mu = X_train.mean(axis=0)
sigma = X_train.std(axis=0)
X_train = (X_train - mu)/(sigma + 1e-9)
X_test = (X_test - mu)/(sigma + 1e-9)

# ==========2.自定义Dataset数据集类 ==========
class HousePriceDataset(Dataset):
    def __init__(self, X_data, y_data):
        self.X = X_data
        self.y = y_data
    def __getitem__(self, idx):
        return self.X[idx], self.y[idx]
    def __len__(self):
        return len(self.X)

train_set = HousePriceDataset(X_train, y_train)
test_set = HousePriceDataset(X_test, y_test)

train_loader = DataLoader(train_set, batch_size=16, shuffle=True)
test_loader = DataLoader(test_set, batch_size=32, shuffle=False)

# ==========3.模型、损失函数、优化器 ==========
model = nn.Linear(in_features=13, out_features=1)
loss_fn = nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)
epochs = 50

# ==========4.训练循环 ==========
def eval_loss(loader):
    model.eval()
    total = 0.0
    cnt = 0
    with torch.no_grad():
        for bx,by in loader:
            pred = model(bx)
            loss = loss_fn(pred,by)
            total += loss.item()
            cnt += 1
    return total / cnt

train_loss_list = []
test_loss_list = []

for epoch in range(epochs):
    model.train()
    for batch_X, batch_y in train_loader:
        # 前向传播
        y_pred = model(batch_X)
        loss = loss_fn(y_pred, batch_y)
        #反向传播
        loss.backward()
        #参数更新
        optimizer.step()
        #梯度清零
        optimizer.zero_grad()

    tr_loss = eval_loss(train_loader)
    te_loss = eval_loss(test_loader)
    train_loss_list.append(tr_loss)
    test_loss_list.append(te_loss)
    print(f"Epoch:{epoch+1:2d} | Train Loss:{tr_loss:.3f} | Test Loss:{te_loss:.3f}")

# ==========5.模型保存、加载推理 ==========
save_dict = {
    "mu": mu,
    "sigma": sigma,
    "model_state": model.state_dict()
}
torch.save(save_dict, "model_api.pt")

# 加载推理
checkpoint = torch.load("model_api.pt", weights_only=True)
new_model = nn.Linear(13, 1)
new_model.load_state_dict(checkpoint["model_state"])

def api_infer(x, mu, sigma, model, normalized=False):
    if not torch.is_tensor(x):
        x = torch.tensor(x, dtype=torch.float32)
    if x.ndim ==1:
        x = x.view(1,-1)
    if not normalized:
        x = (x - mu)/(sigma +1e-9)
    model.eval()
    with torch.no_grad():
        return model(x)

res = api_infer(X_test[0], checkpoint["mu"], checkpoint["sigma"], new_model)
print("API方式预测房价:", res.item())

优化器对比表

表格

优化器 特点 适用场景
SGD 简单稳定,适合入门学习 基础练习
Adam 自适应学习率,收敛快 绝大多数任务
RMSprop 适配序列数据 RNN、时序模型

五、两种实现方式对比

表格

特性 底层手写实现 高级 API 实现
学习价值 适合理解底层原理 适合快速工程开发
代码复杂度 高,全部逻辑手动写 低,大量逻辑框架封装
灵活性 完全可控 受框架约束
可维护性
性能优化 需要自己处理 框架自动做优化

六、核心数学公式

  1. 线性回归:\(\hat{y}=X \cdot W+b\)

2.MSE 损失:\(L=\frac{1}{n}\sum_{i=1}^n(y_i-\hat y_i)^2\)

  1. 梯度下降更新

\(W_{t+1}=W_{t}-\alpha \cdot \frac{\partial L}{\partial W}\)

\(b_{t+1}=b_{t}-\alpha \cdot \frac{\partial L}{\partial b}\)

七、PyTorch 核心机制

  1. 自动微分requires_grad=True开启梯度跟踪,backward()自动求导。
  2. 动态计算图:运行时构建计算逻辑,灵活适配模型。
  3. 张量运算:支持 CPU/GPU 异构加速。
  4. 模块化:nn.Module提供网络封装规范。

八、最佳实践

1)数据预处理

  • 回归任务务必做特征标准化;只能用训练集统计量缩放测试集;
  • 检查张量维度、数据类型,避免维度不匹配报错。

2)模型训练

  • 同时监控训练损失、测试损失,观察过拟合;
  • 合理调学习率、batch‑size,按需选择优化器。

3)模型评估

  • 使用独立测试集评估泛化能力;多指标评估;绘制损失变化曲线。

4)工程实践

  • 保存模型时,同步保存预处理统计参数(mu、sigma);
  • 推理阶段务必使用model.eval()with torch.no_grad()
  • 封装可复用推理函数;增加异常判断。

九、全连接网络拓展示例(多层网络)

将单层线性回归改成多层全连接,引入非线性激活

复制代码
class MLPReg(nn.Module):
    def __init__(self):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(13,32),
            nn.ReLU(),
            nn.Linear(32,16),
            nn.ReLU(),
            nn.Linear(16,1)
        )
    def forward(self,x):
        return self.net(x)

mlp_model = MLPReg()

拓展实验方向:修改 batch 大小、调整学习率、替换 Adam 优化器、绘制 train/test 损失曲线。

提示:运行代码需要准备boston_house_prices.csv数据集文件。

相关推荐
wangchen_01 小时前
PyTorch
人工智能·pytorch·python
code_pgf1 小时前
SAMLabeler-MNN 项目使用与编译问题解决
人工智能·深度学习·mnn
LoveAmySun1 小时前
AI智能体如何落地公交营运真实业务
大数据·人工智能
深圳雨林凯AI1 小时前
雨林凯AI四方连图技术原理拆解:无缝拼接、元素重排与密度变化的实现
人工智能
DS随心转插件1 小时前
ChatGPT生成的pdf怎么导出 只要加个“AI导出鸭”
人工智能·chatgpt·pdf·deepseek·ai导出鸭
鸽芷咕1 小时前
5 分钟给 Claude/Cursor 接上“实时联网”能力|Bright Data MCP 实测
人工智能
adinnet20261 小时前
辅助写作与文档整理,从会议纪要到标书,让智能体当好“笔杆子“
大数据·人工智能
LadiesAndGentlemen1 小时前
概览篇:世界模型、空间智能与地理空间智能是什么关系
数据库·人工智能·自然语言处理·开源·aigc
网络工程小王1 小时前
【LLM开发实验】强化学习实现原理及实战
深度学习·强化学习·rlhf·ppo·dpo·grpo