ai代码助手,使用ai学习提升代码理解程度

该方法主要使用于平时想要快速理解看不懂的代码

提示语

- Role: 注释代码学习助手
- Background: 用户希望在豆包发送代码后,豆包会把原代码返回,并且按照下面的来执行[以下是修正后的代码,同时我还添加了一些注释使代码更易理解:],要求不添加多余代码,不进行任何修改。
- Profile: 你是一位能够复制并且注释代码作用的的助手,专注于满足用户对代码理解学习的需求。
- Skills: 你具备直接注释代码的能力,能够准确注释代码,确保不添加或修改任何内容。
- Goals: 注释代码用户的代码。
- Constrains: 仅注释代码,不添加任何多余代码,不进行任何修改。
- OutputFormat: 注释代码后的输出结果。
- Workflow:
  1. 接收用户输入的代码。
  2. 直接注释代码。
  3. 展示代码运行结果。
  4. 代码如果错误无法运行,请提示代码的错误地方,并给出改正意见
  5. 对方法进行注释,比如 `np.random.randint() 是 NumPy 库中用于生成随机整数的函数, 第一个参数 500:生成随机数的下限(包含该值), 第二个参数 5000:生成随机数的上限(不包含该值), 第三个参数 n_samples:生成的随机数数量(最终会得到一个包含 n_samples 个元素的数组), 例如,如果 n_samples = 3,这行代码会生成一个包含 3 个元素的数组,每个元素都是 500~4999 之间的随机整数(如 [1200, 3500, 4800])。` 
- Examples:
  - 例子1:用户输入代码:
    ```python
    print("Hello, World!")
    ```
    输出结果:
    ```
    Hello, World!
    ```
  - 例子2:用户输入代码:
    ```javascript
    console.log("This is a test.");
    ```
    输出结果:
    ```
    This is a test.
    ```
  - 例子3:用户输入代码:
    ```java
    public class Main {
        public static void main(String[] args) {
            System.out.println("Java is running!");
        }
    }
    ```
    输出结果:
    ```
    Java is running!
    ```
- Initialization: 在第一次对话中,请直接输出以下:您好!我是代码运行助手,可以直接运行您输入的代码并展示效果。请提供代码,我将为您运行。

我们打开豆包, 将上面的提示语放进去,回车,只会就会提示 您好!我是代码运行助手,可以直接运行您输入的代码并展示效果。请提供代码,我将为您运行。

接下来我们就可以开始放入我们的代码,让它帮我们进行理解

例如:

import torch
from torch.utils.data import TensorDataset
from torch.utils.data import DataLoader
import torch.nn as nn
import torch.optim as optim
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import time
import os
from torchsummary import summary
from sklearn.preprocessing import StandardScaler

# 生成cvs数据
def create_basedatacvs():
    # 设置随机种子,保证结果可复现
    np.random.seed(42)
    # 生成的样本数量
    n_samples = 100

    # 生成各字段数据
    battery_power = np.random.randint(500, 5000, n_samples)  # 电池容量:500 - 5000 毫安时
    blue = np.random.randint(0, 2, n_samples)  # 是否有蓝牙:0(无)或 1(有)
    clock_speed = np.random.uniform(0.5, 3.0, n_samples)  # 微处理器执行指令速度:0.5 - 3.0
    dual_sim = np.random.randint(0, 2, n_samples)  # 是否支持双卡:0(不支持)或 1(支持)
    fc = np.random.randint(0, 20, n_samples)  # 前置摄像头百万像素:0 - 20 百万
    four_g = np.random.randint(0, 2, n_samples)  # 是否有 4G:0(无)或 1(有)
    int_memory = np.random.randint(2, 64, n_samples)  # 内存:2 - 64 GB
    m_dep = np.random.uniform(0.5, 1.5, n_samples)  # 移动深度:0.5 - 1.5 cm
    mobile_wt = np.random.randint(100, 200, n_samples)  # 手机重量:100 - 200 克
    n_cores = np.random.randint(1, 8, n_samples)  # 处理器内核数:1 - 8 核
    pc = np.random.randint(0, 40, n_samples)  # 主摄像头百万像素:0 - 40 百万
    px_height = np.random.randint(500, 2500, n_samples)  # 像素分辨率高度:500 - 2500
    px_width = np.random.randint(500, 2500, n_samples)  # 像素分辨率宽度:500 - 2500
    ram = np.random.randint(256, 8192, n_samples)  # 随机存取存储器:256 - 8192 兆字节
    sc_h = np.random.uniform(10, 20, n_samples)  # 手机屏幕高度:10 - 20 cm
    sc_w = np.random.uniform(5, 10, n_samples)  # 手机屏幕宽度:5 - 10 cm
    talk_time = np.random.randint(2, 24, n_samples)  # 一次电池充电持续时间:2 - 24 小时
    three_g = np.random.randint(0, 2, n_samples)  # 是否有 3G:0(无)或 1(有)
    touch_screen = np.random.randint(0, 2, n_samples)  # 是否有触控屏:0(无)或 1(有)
    wifi = np.random.randint(0, 2, n_samples)  # 是否能连 wifi:0(不能)或 1(能)
    

    # 基于核心特征计算价格得分(权重可调整)
    ram_score = ram / 8192  # RAM占比(0-1),权重最高
    battery_score = battery_power / 5000  # 电池容量占比(0-1)
    px_score = (px_height * px_width) / (2500*2500)  # 屏幕分辨率占比(0-1)
    # 综合得分:RAM权重0.5,电池0.3,分辨率0.2,总分0-1
    total_score = ram_score * 0.5 + battery_score * 0.3 + px_score * 0.2
    # 按得分划分4个价格区间(0-3)
    price_range = (total_score * 4).astype(np.int64)
    # 避免超出0-3范围(边界处理)
    price_range = np.clip(price_range, 0, 3)

    # 创建DataFrame
    data = pd.DataFrame({
        'battery_power': battery_power,
        'blue': blue,
        'clock_speed': clock_speed,
        'dual_sim': dual_sim,
        'fc': fc,
        'four_g': four_g,
        'int_memory': int_memory,
        'm_dep': m_dep,
        'mobile_wt': mobile_wt,
        'n_cores': n_cores,
        'pc': pc,
        'px_height': px_height,
        'px_width': px_width,
        'ram': ram,
        'sc_h': sc_h,
        'sc_w': sc_w,
        'talk_time': talk_time,
        'three_g': three_g,
        'touch_screen': touch_screen,
        'wifi': wifi,
        'price_range': price_range
    })

    # 确保data文件夹存在
    os.makedirs('data', exist_ok=True)
    # 保存为CSV文件
    data.to_csv('data/手机价格预测2.csv', index=False)

    print("数据已成功生成并保存到 data/手机价格预测2.csv")


# 1. 数据集构建:该部分代码用于读取、处理手机价格预测数据集,并划分为训练集和验证集
# 定义创建数据集的函数
def create_dataset():
    # 使用Pandas读取数据,数据文件路径为"data/手机价格预测.csv"
    data = pd.read_csv("data/手机价格预测2.csv")
    
    # 提取特征值和目标值:x为除最后一列外的所有列(特征),y为最后一列(目标标签)
    # data.iloc[:,:-1]:
    # iloc 是 Pandas 中用于按位置索引数据的方法
    # 第一个冒号 : 表示选取所有行
    # :-1 表示选取从第 0 列到倒数第 2 列的所有列(即除了最后一列之外的所有列)
    # 结果赋值给 x,作为模型的输入特征
    # data.iloc[:,-1]:
    # 第一个冒号 : 同样表示选取所有行
    # -1 表示选取最后一列
    # 结果赋值给 y,作为模型需要预测的目标标签
    x, y = data.iloc[:,:-1], data.iloc[:,-1]
    
    # 新增:特征标准化
    scaler = StandardScaler()
    x_scaled = scaler.fit_transform(x)  # 训练集拟合并转换
    x = x_scaled.astype(np.float32)  # 转换为float32

    # 类型转换:将特征值转换为32位浮点数类型,目标值转换为64位整数类型,适配模型输入要求
    y = y.astype(np.int64)

    # 数据集划分:将数据按8:2的比例划分为训练集和验证集,random_state=88固定随机种子确保结果可复现
    x_train, x_valid, y_train, y_valid = train_test_split(x, y, train_size=0.8, random_state=88)

    # 保存scaler(后续预测时用)
    import joblib
    joblib.dump(scaler, 'data/scaler.pkl')

    # 构建数据集,转换为PyTorch可处理的TensorDataset形式,将特征与对应标签组合
    train_dataset = TensorDataset(torch.from_numpy(x_train), torch.tensor(y_train.values))
    valid_dataset = TensorDataset(torch.from_numpy(x_valid), torch.tensor(y_valid.values))

    # 返回结果:训练数据集、验证数据集、输入特征数量(特征列数)、分类类别数量(目标值中不同类别的总数)
    return train_dataset, valid_dataset, x_train.shape[1], len(np.unique(y))


# 定义手机价格预测模型类,继承自PyTorch的nn.Module
class PhonePriceModel(nn.Module):
    def __init__(self, input_dim, output_dim):
        super(PhonePriceModel, self).__init__()
        # 1. 第一层:全连接层,输入维度为input_dim,输出维度为128
        self.linear1 = nn.Linear(input_dim, 128)
        self.relu1 = nn.ReLU()
        self.dropout1 = nn.Dropout(0.2)  # 20%神经元失活,防止过拟合

        # 2. 第二层:全连接层,输入维度为128,输出维度为256
        self.linear2 = nn.Linear(128, 256)
        self.relu2 = nn.ReLU()
        self.dropout2 = nn.Dropout(0.3)

        # 3. 第三层:全连接层,输入维度为256,输出维度为output_dim(分类类别数)
        self.linear3 = nn.Linear(256, 128)  # 输出维度128
        self.relu3 = nn.ReLU()
        
        self.linear4 = nn.Linear(128, output_dim) # 输出层

    def forward(self, x):
         # 确保每一层的输出正确传递到下一层
        x = self.linear1(x)
        x = self.relu1(x)
        x = self.dropout1(x)
        
        x = self.linear2(x)
        x = self.relu2(x)
        x = self.dropout2(x)
        
        x = self.linear3(x)
        x = self.relu3(x)
        
        output = self.linear4(x)  # 现在输入维度是128,与linear4的要求匹配
        return output




# 模型训练过程

def train(train_dataset, valid_dataset, input_dim, class_num):
    # 固定随机数种子
    # torch.manual_seed(0)
    # 初始化模型
    # 将数据送入网络中进行预测(前向传播在此触发)
    model = PhonePriceModel(input_dim, class_num)
    # 损失函数
    criterion = nn.CrossEntropyLoss()
    # 优化方法
    optimizer = optim.Adam(model.parameters(), lr=1e-3)
    scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=15, gamma=0.5)  # 15轮后学习率减半
    # 训练轮数
    num_epoch = 80  # 增加训练轮次
    batch_size = 16  # 调整批次大小

    train_loader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size)
    valid_loader  = DataLoader(valid_dataset, batch_size=batch_size, shuffle=False)

    for epoch_idx in range(num_epoch):
        # 训练时间
        start = time.time()
        model.train()  # 训练模式(启用Dropout)
        # 计算损失
        total_train_loss = 0.0
        train_num = 0

        # 训练阶段
        for x, y in train_loader:
            output = model(x)
            loss = criterion(output, y)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            total_train_loss += loss.item() * x.size(0)  # 按样本数加权
            train_num += x.size(0)

        # 验证阶段
        model.eval()  # 验证模式(关闭Dropout)
        total_valid_loss = 0.0
        valid_correct = 0
        valid_num = 0

        with torch.no_grad():  # 禁用梯度计算,节省内存
            for x, y in valid_loader:
                output = model(x)
                loss = criterion(output, y)
                y_pred = torch.argmax(output, dim=1)
                total_valid_loss += loss.item() * x.size(0)
                valid_correct += (y_pred == y).sum().item()
                valid_num += x.size(0)


         # 计算平均损失与验证准确率
        avg_train_loss = total_train_loss / train_num
        avg_valid_loss = total_valid_loss / valid_num
        valid_acc = valid_correct / valid_num
        
        # 打印信息(新增验证指标)
        print(f'epoch: {epoch_idx+1:4d} | train_loss: {avg_train_loss:.4f} | '
              f'valid_loss: {avg_valid_loss:.4f} | valid_acc: {valid_acc:.4f} | time: {time.time()-start:.2f}s')
        
        # 学习率调度
        scheduler.step()
    
    torch.save(model.state_dict(), 'data/phone_optimized.pth')

def test(valid_dataset, input_dim, class_num):
    model = PhonePriceModel(input_dim, class_num)
    model.load_state_dict(torch.load('data/phone_optimized.pth'))
    model.eval()  # 验证模式
    dataloader = DataLoader(valid_dataset, batch_size=16, shuffle=False)
    correct = 0
    with torch.no_grad():
        for x, y in dataloader:
            output = model(x)
            y_pred = torch.argmax(output, dim=1)
            correct += (y_pred == y).sum().item()
    acc = correct / len(valid_dataset)
    print(f"Final Test Acc: {acc:.4f}")  # 保留4位小数,更直观
    return acc

# 主程序入口,当脚本直接运行时执行以下代码
if __name__ == '__main__':
    # create_basedatacvs()
    # 一. 数据集构建
    # 获取数据:调用create_dataset函数获取处理好的数据集及相关参数
    train_dataset, valid_dataset, input_dim, class_num = create_dataset()
    print("1. 数据集构建;输出结果为:")
    # 打印输入特征的数量
    print('输入特征数:', input_dim)
    # 打印分类类别的数量
    print("分类个数:", class_num)

    # 二. 模型构建
    print('2. 模型构建')
    # 模型实例化:根据输入特征数和分类类别数创建模型
    model = PhonePriceModel(input_dim, class_num)

    # 三、模型训练(225原则)
    print('3. 模型训练')
    train(train_dataset, valid_dataset, input_dim, class_num)
    # 在模型训练中,“225 原则” 指的是特定的训练要素组合。具体含义如下
    # 第一个 “2”:代表 2 个初始化参数,即损失函数和优化器。损失函数用于度量模型预测值与真实值之间的差异程度,优化器则负责根据损失函数的结果来更新模型的参数,以最小化损失。
    # 第二个 “2”:表示 2 个遍历,分别是 epoch(轮次)和数据批次大小(batch size)。epoch 指的是模型对整个训练数据集进行完整遍历的次数,而 batch size 是每次传递给模型用以训练的样本数据量。
    # “5”:指的是模型训练过程中的 5 个关键步骤,包括前向传播、损失函数计算、梯度清零、反向传播和参数更新。前向传播是将输入数据通过模型计算得到预测值,损失函数计算出预测值与真实值的差异,梯度清零是为了避免梯度累加,反向传播是将损失值从输出层反向传播到输入层,以计算每个参数的梯度,最后根据梯度对参数进行更新,从而使模型不断优化。

    # 四、模型预测
    print('4. 模型预测')
    test(valid_dataset, input_dim, class_num)

    # 打印模型结构摘要,输入大小为(input_dim,),批次大小为16
    print('5. summary')
    summary(model, input_size=(input_dim,), batch_size=16)

豆包理解后的代码

# 导入所需库:涵盖PyTorch深度学习框架、数据处理、模型评估、可视化等工具
import torch  # PyTorch核心库,用于构建和训练神经网络
from torch.utils.data import TensorDataset  # 将张量数据封装为数据集的类
from torch.utils.data import DataLoader  # 用于批量加载数据的类,支持打乱、多线程等
import torch.nn as nn  # PyTorch神经网络模块,包含常用层和损失函数
import torch.optim as optim  # PyTorch优化器模块,包含Adam、SGD等优化算法
from sklearn.datasets import make_regression  # 生成回归数据集的工具(本代码未实际使用)
from sklearn.model_selection import train_test_split  # 划分训练集和验证集的工具
import matplotlib.pyplot as plt  # 数据可视化库,用于绘制图表(本代码未实际使用)
import numpy as np  # 数值计算库,用于生成随机数据和数组运算
import pandas as pd  # 数据处理库,用于读取和操作CSV数据
import time  # 时间模块,用于计算训练耗时
import os  # 操作系统模块,用于创建文件夹和处理文件路径
from torchsummary import summary  # 用于打印神经网络结构摘要的工具
from sklearn.preprocessing import StandardScaler  # 数据标准化工具,将特征缩放到均值0、方差1


# 生成CSV数据的函数:创建包含手机各项特征和价格区间的数据集
def create_basedatacvs():
    # np.random.seed(42):NumPy中用于设置随机种子的函数,参数42为固定种子值
    # 作用是保证每次运行代码生成的随机数据完全一致,确保实验结果可复现
    np.random.seed(42)
    # 生成的样本数量,此处为100条手机数据
    n_samples = 100

    # 生成各字段数据:每条数据对应手机的一项硬件/功能特征
    # np.random.randint(500, 5000, n_samples):生成指定范围的整数随机数组
    # 第一个参数500:随机数下限(包含),第二个参数5000:随机数上限(不包含),第三个参数n_samples:生成数组的长度
    battery_power = np.random.randint(500, 5000, n_samples)  # 电池容量:500 - 5000 毫安时
    blue = np.random.randint(0, 2, n_samples)  # 是否有蓝牙:0(无)或 1(有)
    # np.random.uniform(0.5, 3.0, n_samples):生成指定范围的均匀分布浮点数随机数组
    # 参数含义同randint,区别是生成浮点数
    clock_speed = np.random.uniform(0.5, 3.0, n_samples)  # 微处理器执行指令速度:0.5 - 3.0 GHz
    dual_sim = np.random.randint(0, 2, n_samples)  # 是否支持双卡:0(不支持)或 1(支持)
    fc = np.random.randint(0, 20, n_samples)  # 前置摄像头百万像素:0 - 20 百万
    four_g = np.random.randint(0, 2, n_samples)  # 是否有 4G:0(无)或 1(有)
    int_memory = np.random.randint(2, 64, n_samples)  # 内存:2 - 64 GB
    m_dep = np.random.uniform(0.5, 1.5, n_samples)  # 手机厚度:0.5 - 1.5 cm
    mobile_wt = np.random.randint(100, 200, n_samples)  # 手机重量:100 - 200 克
    n_cores = np.random.randint(1, 8, n_samples)  # 处理器内核数:1 - 8 核
    pc = np.random.randint(0, 40, n_samples)  # 主摄像头百万像素:0 - 40 百万
    px_height = np.random.randint(500, 2500, n_samples)  # 像素分辨率高度:500 - 2500 像素
    px_width = np.random.randint(500, 2500, n_samples)  # 像素分辨率宽度:500 - 2500 像素
    ram = np.random.randint(256, 8192, n_samples)  # 随机存取存储器(运行内存):256 - 8192 兆字节
    sc_h = np.random.uniform(10, 20, n_samples)  # 手机屏幕高度:10 - 20 cm
    sc_w = np.random.uniform(5, 10, n_samples)  # 手机屏幕宽度:5 - 10 cm
    talk_time = np.random.randint(2, 24, n_samples)  # 一次电池充电持续通话时间:2 - 24 小时
    three_g = np.random.randint(0, 2, n_samples)  # 是否有 3G:0(无)或 1(有)
    touch_screen = np.random.randint(0, 2, n_samples)  # 是否有触控屏:0(无)或 1(有)
    wifi = np.random.randint(0, 2, n_samples)  # 是否能连 wifi:0(不能)或 1(能)
    

    # 基于核心特征计算价格得分(权重可调整):模拟手机价格与硬件的关联
    ram_score = ram / 8192  # RAM占比(0-1),权重最高(手机性能核心指标)
    battery_score = battery_power / 5000  # 电池容量占比(0-1)
    px_score = (px_height * px_width) / (2500*2500)  # 屏幕分辨率占比(0-1),先算总面积再归一化
    # 综合得分:RAM权重0.5,电池0.3,分辨率0.2,总分范围0-1
    total_score = ram_score * 0.5 + battery_score * 0.3 + px_score * 0.2
    # 按得分划分4个价格区间(0-3):将0-1的得分映射到4个整数类别
    price_range = (total_score * 4).astype(np.int64)
    # np.clip(price_range, 0, 3):将数组值限制在0-3范围内,避免因计算误差超出目标类别范围
    # 例如得分接近1时,total_score*4可能略大于3,clip后强制设为3
    price_range = np.clip(price_range, 0, 3)

    # 创建DataFrame:将所有特征和目标值组合成Pandas数据框,便于后续处理
    data = pd.DataFrame({
        'battery_power': battery_power,
        'blue': blue,
        'clock_speed': clock_speed,
        'dual_sim': dual_sim,
        'fc': fc,
        'four_g': four_g,
        'int_memory': int_memory,
        'm_dep': m_dep,
        'mobile_wt': mobile_wt,
        'n_cores': n_cores,
        'pc': pc,
        'px_height': px_height,
        'px_width': px_width,
        'ram': ram,
        'sc_h': sc_h,
        'sc_w': sc_w,
        'talk_time': talk_time,
        'three_g': three_g,
        'touch_screen': touch_screen,
        'wifi': wifi,
        'price_range': price_range  # 目标变量:手机价格区间(0-3)
    })

    # os.makedirs('data', exist_ok=True):创建名为"data"的文件夹
    # 参数exist_ok=True表示若文件夹已存在,不报错(避免重复创建导致的错误)
    os.makedirs('data', exist_ok=True)
    # 将DataFrame保存为CSV文件,路径为"data/手机价格预测2.csv"
    # index=False表示不保存DataFrame的行索引(避免生成多余列)
    data.to_csv('data/手机价格预测2.csv', index=False)

    print("数据已成功生成并保存到 data/手机价格预测2.csv")


# 1. 数据集构建:该部分代码用于读取、处理手机价格预测数据集,并划分为训练集和验证集
# 定义创建数据集的函数:输出处理好的训练集、验证集,以及特征数量和类别数量
def create_dataset():
    # pd.read_csv("data/手机价格预测2.csv"):Pandas读取CSV文件的函数
    # 参数为文件路径,返回DataFrame对象,包含所有手机数据
    data = pd.read_csv("data/手机价格预测2.csv")
    
    # 提取特征值和目标值:x为除最后一列外的所有列(特征),y为最后一列(目标标签)
    # data.iloc[:,:-1]:Pandas中按位置索引数据的方法
    # 第一个冒号: 表示选取所有行,:-1 表示选取从第0列到倒数第2列(排除最后一列)
    x = data.iloc[:,:-1]
    # data.iloc[:,-1]:选取所有行的最后一列,即价格区间(目标变量)
    y = data.iloc[:,-1]
    
    # 新增:特征标准化:消除不同特征量纲影响,提升模型训练稳定性
    # StandardScaler():初始化标准化器,默认将特征转换为均值0、标准差1的分布
    scaler = StandardScaler()
    # scaler.fit_transform(x):先根据x的统计信息(均值、标准差)拟合标准化器,再对x进行转换
    x_scaled = scaler.fit_transform(x)
    # 转换为float32类型:适配PyTorch默认的张量数据类型,减少内存占用
    x = x_scaled.astype(np.float32)

    # 类型转换:将目标值y转换为int64类型,适配PyTorch分类任务的标签要求
    y = y.astype(np.int64)

    # 数据集划分:train_test_split是sklearn中划分训练集和验证集的函数
    # 参数x为特征,y为标签,train_size=0.8表示训练集占比80%,验证集占比20%
    # random_state=88:固定随机种子,确保每次划分结果一致,便于复现
    x_train, x_valid, y_train, y_valid = train_test_split(x, y, train_size=0.8, random_state=88)

    # 保存scaler(后续预测时用):joblib是Python的序列化工具,用于保存和加载对象
    # 将训练好的标准化器保存到"data/scaler.pkl",后续预测新数据时需用相同的scaler处理
    import joblib
    joblib.dump(scaler, 'data/scaler.pkl')

    # 构建数据集:TensorDataset是PyTorch的数据集类,将特征张量和标签张量组合
    # torch.from_numpy(x_train):将NumPy数组转换为PyTorch张量
    # torch.tensor(y_train.values):将Pandas Series的values转换为PyTorch张量
    train_dataset = TensorDataset(torch.from_numpy(x_train), torch.tensor(y_train.values))
    valid_dataset = TensorDataset(torch.from_numpy(x_valid), torch.tensor(y_valid.values))

    # 返回结果:训练数据集、验证数据集、输入特征数量(x_train的列数)、分类类别数量(y中不同值的个数)
    return train_dataset, valid_dataset, x_train.shape[1], len(np.unique(y))


# 定义手机价格预测模型类:继承自PyTorch的nn.Module(所有神经网络模型的基类)
class PhonePriceModel(nn.Module):
    # __init__是类的构造函数,用于初始化模型参数
    # input_dim:输入特征的维度(即手机特征的数量),output_dim:输出类别数量(即价格区间数0-3,共4类)
    def __init__(self, input_dim, output_dim):
        # super(PhonePriceModel, self).__init__():调用父类nn.Module的构造函数,初始化模型基础结构
        super(PhonePriceModel, self).__init__()
        # 1. 第一层:全连接层 + ReLU激活函数 + Dropout层
        # nn.Linear(input_dim, 128):全连接层,输入维度input_dim,输出维度128(该层有input_dim*128 + 128个参数)
        self.linear1 = nn.Linear(input_dim, 128)
        # nn.ReLU():ReLU激活函数,用于引入非线性,公式为max(0, x)
        self.relu1 = nn.ReLU()
        # nn.Dropout(0.2):Dropout层,随机使20%的神经元失活(输出设为0),防止模型过拟合
        self.dropout1 = nn.Dropout(0.2)

        # 2. 第二层:全连接层 + ReLU激活函数 + Dropout层
        self.linear2 = nn.Linear(128, 256)  # 输入维度128,输出维度256
        self.relu2 = nn.ReLU()
        self.dropout2 = nn.Dropout(0.3)  # 30%神经元失活

        # 3. 第三层:全连接层 + ReLU激活函数
        self.linear3 = nn.Linear(256, 128)  # 输入维度256,输出维度128
        self.relu3 = nn.ReLU()
        
        # 4. 输出层:全连接层,输出维度为output_dim(分类类别数)
        self.linear4 = nn.Linear(128, output_dim)

    # forward函数:定义模型的前向传播过程,即输入数据如何通过各层计算得到输出
    def forward(self, x):
        # 第一层计算:输入x → 全连接层linear1 → ReLU激活 → Dropout
        x = self.linear1(x)
        x = self.relu1(x)
        x = self.dropout1(x)
        
        # 第二层计算:上一层输出 → 全连接层linear2 → ReLU激活 → Dropout
        x = self.linear2(x)
        x = self.relu2(x)
        x = self.dropout2(x)
        
        # 第三层计算:上一层输出 → 全连接层linear3 → ReLU激活
        x = self.linear3(x)
        x = self.relu3(x)
        
        # 输出层计算:上一层输出 → 全连接层linear4,得到最终预测结果
        output = self.linear4(x)
        return output


# 模型训练过程:接收数据集和模型参数,训练并保存模型
def train(train_dataset, valid_dataset, input_dim, class_num):
    # 初始化模型:创建PhonePriceModel实例,传入输入特征维度和类别数量
    model = PhonePriceModel(input_dim, class_num)
    # 损失函数:nn.CrossEntropyLoss()是交叉熵损失函数,适用于多分类任务
    # 自动包含Softmax函数,将模型输出转换为概率分布,再计算与真实标签的交叉熵
    criterion = nn.CrossEntropyLoss()
    # 优化方法:optim.Adam()是Adam优化器,常用于神经网络训练
    # model.parameters():传入模型的所有可训练参数,lr=1e-3是学习率(步长)
    optimizer = optim.Adam(model.parameters(), lr=1e-3)
    # 学习率调度器:optim.lr_scheduler.StepLR()按固定步长调整学习率
    # step_size=15:每15个训练轮次调整一次,gamma=0.5:调整幅度为当前学习率乘以0.5(即减半)
    scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=15, gamma=0.5)
    # 训练轮数:模型完整遍历训练集的次数
    num_epoch = 80
    # 批次大小:每次训练时传入模型的样本数量
    batch_size = 16

    # DataLoader:将数据集转换为批量迭代器,方便训练时按批次加载数据
    # train_loader:训练集迭代器,shuffle=True表示每次epoch前打乱数据(提升泛化能力)
    train_loader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size)
    # valid_loader:验证集迭代器,shuffle=False表示不打乱数据(便于稳定评估)
    valid_loader  = DataLoader(valid_dataset, batch_size=batch_size, shuffle=False)

    # 遍历每个训练轮次
    for epoch_idx in range(num_epoch):
        # 记录当前epoch的开始时间,用于计算训练耗时
        start = time.time()
        # model.train():将模型设为训练模式,启用Dropout等训练时特有的层
        model.train()
        # 初始化训练损失累计变量
        total_train_loss = 0.0
        # 初始化训练样本数量累计变量
        train_num = 0

        # 训练阶段:遍历训练集的每个批次
        for x, y in train_loader:
            # 前向传播:将批次数据x传入模型,得到预测输出output
            output = model(x)
            # 计算损失:用损失函数criterion比较预测output和真实标签y的差异
            loss = criterion(output, y)
            # 梯度清零:优化器的zero_grad()方法,清除上一轮的梯度(避免梯度累加)
            optimizer.zero_grad()
            # 反向传播:loss.backward()计算各参数的梯度(从损失值反向推导)
            loss.backward()
            # 参数更新:optimizer.step()根据梯度更新模型的所有可训练参数
            optimizer.step()
            # 累计训练损失:loss.item()是当前批次的损失值(标量),乘以x.size(0)(批次样本数)得到总损失
            total_train_loss += loss.item() * x.size(0)
            # 累计训练样本数
            train_num += x.size(0)

        # 验证阶段:评估模型在验证集上的性能,不更新参数
        # model.eval():将模型设为验证模式,关闭Dropout等层(使用所有神经元)
        model.eval()
        # 初始化验证损失累计变量
        total_valid_loss = 0.0
        # 初始化验证集正确预测数量累计变量
        valid_correct = 0
        # 初始化验证样本数量累计变量
        valid_num = 0

        # with torch.no_grad():禁用梯度计算,节省内存并加快计算(验证阶段无需更新参数)
        with torch.no_grad():
            # 遍历验证集的每个批次
            for x, y in valid_loader:
                # 前向传播:得到验证集批次的预测输出
                output = model(x)
                # 计算验证损失
                loss = criterion(output, y)
                # 计算预测类别:torch.argmax(output, dim=1)取output中维度1(类别维度)的最大值索引,即预测类别
                y_pred = torch.argmax(output, dim=1)
                # 累计验证损失:同训练损失计算方式
                total_valid_loss += loss.item() * x.size(0)
                # 累计正确预测数:(y_pred == y)得到布尔张量,sum().item()统计True的数量(正确预测数)
                valid_correct += (y_pred == y).sum().item()
                # 累计验证样本数
                valid_num += x.size(0)


        # 计算当前epoch的平均训练损失:总损失除以总样本数
        avg_train_loss = total_train_loss / train_num
        # 计算当前epoch的平均验证损失
        avg_valid_loss = total_valid_loss / valid_num
        # 计算验证准确率:正确预测数除以总验证样本数
        valid_acc = valid_correct / valid_num
        
        # 打印当前epoch的训练信息:轮次、训练损失、验证损失、验证准确率、耗时
        print(f'epoch: {epoch_idx+1:4d} | train_loss: {avg_train_loss:.4f} | '
              f'valid_loss: {avg_valid_loss:.4f} | valid_acc: {valid_acc:.4f} | time: {time.time()-start:.2f}s')
        
        # 学习率调度:scheduler.step()按设定规则更新学习率(每个epoch后调用)
        scheduler.step()
    
    # 保存模型参数:torch.save()将模型的状态字典(包含所有可训练参数)保存到文件
    # 路径为"data/phone_optimized.pth",后续可通过load_state_dict()加载参数
    torch.save(model.state_dict(), 'data/phone_optimized.pth')

# 模型测试函数:加载训练好的模型,在验证集上评估最终准确率
def test(valid_dataset, input_dim, class_num):
    # 初始化模型:创建与训练时相同结构的模型
    model = PhonePriceModel(input_dim, class_num)
    # 加载模型参数:torch.load()读取保存的参数文件,model.load_state_dict()将参数加载到模型
    model.load_state_dict(torch.load('data/phone_optimized.pth'))
    # 设为验证模式:关闭Dropout,确保评估结果稳定
    model.eval()
    # 创建验证集迭代器
    dataloader = DataLoader(valid_dataset, batch_size=16, shuffle=False)
    # 初始化正确预测数
    correct = 0
    # 禁用梯度计算:节省资源
    with torch.no_grad():
        # 遍历验证集批次
        for x, y in dataloader:
            # 前向传播得到预测输出
            output = model(x)
            # 计算预测类别
            y_pred = torch.argmax(output, dim=1)
            # 累计正确预测数
            correct += (y_pred == y).sum().item()
    # 计算最终测试准确率:正确数除以验证集总样本数
    acc = correct / len(valid_dataset)
    # 打印最终准确率,保留4位小数
    print(f"Final Test Acc: {acc:.4f}")
    # 返回准确率结果
    return acc

# 主程序入口:当脚本直接运行时(而非被导入时),执行以下代码
if __name__ == '__main__':
    # create_basedatacvs()  # 注释状态:若需重新生成CSV数据,取消注释该行
    # 一. 数据集构建
    # 获取数据:调用create_dataset函数,得到训练集、验证集、特征数、类别数
    train_dataset, valid_dataset, input_dim, class_num = create_dataset()
    print("1. 数据集构建;输出结果为:")
    # 打印输入特征的数量(即手机特征的列数)
    print('输入特征数:', input_dim)
    # 打印分类类别的数量(价格区间数,此处为4)
    print("分类个数:", class_num)

    # 二. 模型构建
    print('2. 模型构建')
    # 模型实例化:根据特征数和类别数创建模型对象
    model = PhonePriceModel(input_dim, class_num)

    # 三、模型训练(225原则)
    print('3. 模型训练')
    # 调用train函数开始训练,传入数据集和模型参数
    train(train_dataset, valid_dataset, input_dim, class_num)
    # 225原则说明:
    # 第一个“2”:2个核心初始化参数(损失函数、优化器)
    # 第二个“2”:2个关键训练参数(训练轮次epoch、批次大小batch size)
    # “5”:5个核心训练步骤(前向传播、损失计算、梯度清零、反向传播、参数更新)

    # 四、模型预测
    print('4. 模型预测')
    # 调用test函数,在验证集上测试模型最终准确率
    test(valid_dataset, input_dim, class_num)

    # 五、打印模型结构摘要
    print('5. summary')
    # summary函数:打印模型各层结构、输出形状和参数数量
    # input_size=(input_dim,):指定输入特征的形状(单样本特征数),batch_size=16:指定批次大小
    summary(model, input_size=(input_dim,), batch_size=16)

上面这个案例,不仅帮我们解释了该方法是什么作用,传参的意思,还给我们讲解了调用这个方法后能得出什么作用,这让我们学习代码的效率提高了很多,不需要对不懂的api进行百度查找,不懂的参数也不需要再进行查找,一目了然;

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容