pytorch价格案例全解释

小明创办了一家手机公司,他不知道如何估算手机产品的价格。为了解决这个问题,他收集了多家公司的手机销售数据。该数据为二手手机的各个性能的数据,最后根据这些性能得到4个价格区间,作为这些二手手机售出的价格区间。主要包括:

image

 

我们需要帮助小明找出手机的功能(例如:RAM等)与其售价之间的某种关系。我们可以使用机器学习的方法来解决这个问题,也可以构建一个全连接的网络。
需要注意的是: 在这个问题中,我们不需要预测实际价格,而是一个价格范围,它的范围使用 0、1、2、3 来表示,所以该问题也是一个分类问题。接下来我们还是按照四个步骤来完成这个任务:
⚫ 准备训练集数据
⚫ 构建要使用的模型
⚫ 模型训练
⚫ 模型预测评估
# 1.导入相关模块
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

from torchsummary import summary



#  每一行代表一个样本,每一列代表一个特征  ,最后一列代表价格类别(如 0=低价、1=中价、2=高价、3=超高价)

# 2.构建数据集
def create_dataset():
    # 使用pandas读取数据 --- 导入 Pandas,简写为 pd,用于读取 CSV 文件并处理表格数据。
    data = pd.read_csv('dataset/手机价格预测.csv')
    # 特征值和目标值  x(特征矩阵): 含义:输入特征,包含每条样本的所有特征(如手机的内存、存储、屏幕尺寸等) 
    # 维度:2 维矩阵(DataFrame/NumPy 数组)   形状:[样本数, 特征数],例如 [1000, 20] 表示 1000 条样本,每条 20 个特征
    # x = data.iloc[:, :-1]:取除最后一列外的所有列 → 特征矩阵 
    # y  含义:目标值/类别标签,每条样本对应的价格类别(如 0=低价、1=中价、2=高价、3=超高价)
    # 维度:1 维向量(Series/NumPy 数组)  形状:[样本数],例如 [1000] 表示 1000 个标签值
    # y = data.iloc[:, -1]:取最后一列 → 标签向量
    x, y = data.iloc[:, :-1], data.iloc[:, -1]
    # 类型转换:特征值,目标值
    x = x.astype(np.float32)
    y = y.astype(np.int64)
    # 数据集划分 --train_test_split,用于将数据集划分为训练集和验证集(或测试集)
    '''
    使用 train_test_split 将数据随机划分为训练集和验证集:
    80% 数据做训练(train_size=0.8)
    20% 数据做验证
    random_state=88 固定随机种子,使划分结果可复现。
    '''
    x_train, x_valid, y_train, y_valid = train_test_split(x, y, train_size=0.8, random_state=88)
    # 构建数据集,转换为pytorch的形式---torch.from_numpy是PyTorch中用于将NumPy数组转换为Tensor的函数,核心特性是‌共享底层内存‌,无需数据复制。
    # torch.tensor(...):拷贝生成一个新的张量(与 from_numpy 略有不同)  
    # 将特征和标签打包  TensorDataset 内部存储: [(x1, y1), (x2, y2), (x3, y3), ...]
    train_dataset = TensorDataset(torch.from_numpy(x_train.values), torch.tensor(y_train.values))
    valid_dataset = TensorDataset(torch.from_numpy(x_valid.values), torch.tensor(y_valid.values))
    # 返回结果
    '''
    train_dataset:训练集 TensorDataset
    valid_dataset:验证集 TensorDataset
    x_train.shape[1]:特征维度(列数),后续作为模型输入维度 input_dim   shape[0] = 行数(样本数) shape[1] = 列数(特征数)
    模型第一层需要知道输入特征数,才能定义 nn.Linear(input_dim, 128)
    len(np.unique(y)):标签中不同类别的数量,即分类数 class_num,后续作为模型输出维度 
    np.unique(y):返回 y 中所有不重复的值(类别)   len(...):统计类别数量  
    为什么作为输出维度:多分类需要输出每个类别的得分(logits),输出维度 = 类别数()
    例如:4 个类别 → output_dim = 4 → 最后一层 nn.Linear(256, 4) → 输出 [batch_size, 4]
    '''
    return train_dataset, valid_dataset, x_train.shape[1], len(np.unique(y))


# 3.构建网络模型
class PhonePriceModel(nn.Module):
    def __init__(self, input_dim, output_dim):
        super(PhonePriceModel, self).__init__()
        # 1. 第一层:输入维度:20,输出维度:128
        self.linear1 = nn.Linear(input_dim, 128)
        # 2. 第二层:输入维度:128,输出维度:256
        self.linear2 = nn.Linear(128, 256)
        # 3. 第三层:输入维度:256,输出维度:4
        self.linear3 = nn.Linear(256, output_dim)

    def forward(self, x):
        # 前向传播过程
        x = torch.relu(self.linear1(x))
        x = torch.relu(self.linear2(x))
        output = self.linear3(x)
        # 获取数据结果
        return output


# 4.模型训练
def train(train_dataset, input_dim, class_num, ):
    # 固定随机数种子 -- 设置 PyTorch 的随机数种子为 0,以保证每次运行初始化、打乱等操作结果相同(在同一环境下)。
    torch.manual_seed(0)
    # 初始化模型
    model = PhonePriceModel(input_dim, class_num)
    # 损失函数
    criterion = nn.CrossEntropyLoss()
    # 优化方法 --- lr=1e-3:学习率设为 0.001
    optimizer = optim.SGD(model.parameters(), lr=1e-3)
    # 训练轮数
    num_epoch = 50

    # 遍历每个轮次的数据
    for epoch_idx in range(num_epoch):
        # 初始化数据加载器
        dataloader = DataLoader(train_dataset, shuffle=True, batch_size=8)
        # 训练时间
        start = time.time()
        # 计算损失 --- 用于累积当前 epoch 中所有 batch 的损失值之和。
        total_loss = 0.0
        total_num = 1
        # 遍历每个batch数据进行处理 --- x:特征张量,形状大致为 [batch_size, input_dim]    y:标签张量,形状为 [batch_size]
        # x 是 batch 的特征,y 是 batch 的标签
        for x, y in dataloader:
            # 将数据送入网络中进行预测 --- 模型只负责前向计算,输入特征 x,输出预测 logits;标签 y 不进入模型,只用于计算损失
            output = model(x)
            # 计算损失 --- 用交叉熵损失计算当前 batch 的损失值:output:形状 [batch_size, class_num]   y:形状 [batch_size],每个元素为类别索引
            #得到一个标量 loss(tensor)。
            loss = criterion(output, y)
            # 梯度归零
            optimizer.zero_grad()
            # 反向传播  对当前损失执行反向传播,计算 loss 对每个参数的梯度,即 ∂loss/∂θ
            loss.backward()
            # 参数更新 --- 根据梯度和学习率,按 SGD 算法更新模型参数。
            optimizer.step()
            # 损失计算
            total_num += 1
            total_loss += loss.item()
        # 打印损失变换结果 --- 打印当前 epoch 训练信息:  epoch_idx + 1:显示为从 1 开始的轮次   
        # total_loss / total_num:计算平均损失,用 %.2f 保留两位小数 
        # time.time() - start:该 epoch 训练总耗时(秒)
        print('epoch: %4s loss: %.2f, time: %.2fs' % (epoch_idx + 1, total_loss / total_num, time.time() - start))
    # 模型保存 --- 在所有 epoch 完成后,将模型参数(state_dict)保存到 'model/phone.pth' 文件中。   后续测试时会加载这个文件进行推理。
    # 注意:确保 model 目录存在,否则会报错。
    torch.save(model.state_dict(), 'model/phone.pth')


def test(valid_dataset, input_dim, class_num):
    # 加载模型和训练好的网络参数 --- 同样创建一个 PhonePriceModel 实例,其结构必须和训练时一致
    model = PhonePriceModel(input_dim, class_num)
    # 使用 torch.load 从文件 'model/phone.pth' 中读取已保存的模型参数     model.load_state_dict(...) 将这些参数加载到新建的 model 实例中。
    # 这样就得到一个和训练后状态一致的模型,用于评估。
    model.load_state_dict(torch.load('model/phone.pth'))
    # 构建加载器 --- 将验证集构建成 DataLoader   shuffle=False:验证时一般不打乱顺序。 
    # TensorDataset 将特征和标签打包成 (x, y) 对,DataLoader 迭代时返回该对
    dataloader = DataLoader(valid_dataset, batch_size=8, shuffle=False)
    # 评估测试集
    correct = 0
    # 遍历测试集中的数据
    for x, y in dataloader:
        # 将其送入网络中 --- 模型只负责前向计算,输入特征 x,输出预测 logits;标签 y 不进入模型,只用于计算损失
        output = model(x)
        # 获取类别结果 --- 沿 dim=1(类别维度)取最大值的索引:
        # output 形状为 [batch_size, class_num]
        # torch.argmax(..., dim=1) 得到形状 [batch_size] 的预测类别索引。
        # 参数: input:输入张量  dim:沿哪个维度找最大值(None 表示展平后找全局最大值)   keepdim:是否保持维度
        y_pred = torch.argmax(output, dim=1)
        # 获取预测正确的个数
        correct += (y_pred == y).sum()
    # 求预测精度
    print('Acc: %.5f' % (correct.item() / len(valid_dataset)))


if __name__ == '__main__':
    # 1.获取数据
    train_dataset, valid_dataset, input_dim, class_num = create_dataset()
    print("输入特征数:", input_dim)
    print("分类个数:", class_num)

    # 2.模型实例化
    model = PhonePriceModel(input_dim, class_num)
    # 从 torchsummary 导入 summary 函数,用于打印模型结构和参数量,在 __main__ 中会用来展示网络。
    '''
    使用 torchsummary.summary 打印模型结构:函数作用:打印模型结构、每层输出形状、参数量等信息,便于检查网络设计
    model:要分析的模型实例
    input_size=(input_dim,):指定单个样本的输入形状为一维向量(元组)(长度 input_dim)  
    (input_dim,) 表示一维向量,长度为 input_dim 
    batch_size=16:仅用于展示参数中使用的 batch 大小,不影响模型本身结构。
    这会输出每层的输出形状和参数数量,有助于检查网络设置是否合理。
    '''
    summary(model, input_size=(input_dim,), batch_size=16)

    # 3.模型训练
    # train(train_dataset, input_dim, class_num)

    # 4.模型预测
    test(valid_dataset, input_dim, class_num)

 

 
 
 
 
posted @ 2025-11-29 17:34  c++c鸟  阅读(1)  评论(0)    收藏  举报