🔎我们通过一个案例详细使用PyTorch实战 ,案例背景:你创办了一家手机公司,不知道如何估算手机产品的价格。为了解决这个问题,收集了多家公司的手机销售数据:这些数据维度可以包括RAM、存储容量、屏幕尺寸、摄像头像素等。
在这个问题中,我们不需要预测实际价格,而是一个价格范围,它的范围使用 0、1、2、3 来表示,所以该问题也是一个分类问题。
🔎思路:
- 数据预处理:对收集到的数据进行清洗和预处理,确保数据的质量和一致性。这包括处理缺失值、异常值和重复数据等。
- 特征工程:从原始数据中提取有用的特征,以便用于建模。这可以包括对连续型特征进行归一化或标准化,对分类特征进行编码等。
- 模型选择:选择一个适合的机器学习算法来建立模型,这里我们使用神经网络模型。
- 模型训练:将收集到的数据划分为训练集和测试集。使用训练集来训练模型,通过调整模型的参数来最小化预测误差。
- 模型评估:使用测试集来评估模型的性能。常用的评估指标包括均方误差(MSE)、均方根误差(RMSE)和平均绝对误差(MAE)等。
- 价格预测:使用训练好的模型来预测新手机的价格。输入新手机的功能特征,模型将输出预测的价格。
构建数据集
💬数据共有 2000 条, 其中 1600 条数据作为训练集, 400 条数据用作测试集。 我们使用 sklearn 的数据集划分工作来完成。并使用 PyTorch 的 TensorDataset 来将数据集构建为 Dataset 对象,方便后期构造数据集加载对象。
def create_dataset(): data = pd.read_csv('predict.csv') x, y = data.iloc[:, :-1], data.iloc[:, -1] x = x.astype(np.float32) y = y.astype(np.int64) x_train, x_valid, y_train, y_valid = train_test_split(x, y, train_size=0.8, random_state=88, stratify=y) train_dataset = TensorDataset(torch.tensor(x_train.values), torch.tensor(y_train.values)) valid_dataset = TensorDataset(torch.tensor(x_valid.values), torch.tensor(y_valid.values)) return train_dataset, valid_dataset, x_train.shape[1], len(np.unique(y)) train_dataset, valid_dataset, input_dim, class_num = create_dataset()
💬其中 train_test_split 方法中的stratify=y
参数的作用是在划分训练集和验证集时,保持类别的比例相同。这样可以确保在训练集和验证集中各类别的比例与原始数据集中的比例相同,有助于提高模型的泛化能力,防止出现一份中某个类别只有几个。
构建分类网络模型
class PhonePriceModel(nn.Module): def __init__(self, input_dim, output_dim): super(PhonePriceModel, self).__init__() self.linear1 = nn.Linear(input_dim, 128) # 输入层到隐藏层的线性变换 self.linear2 = nn.Linear(128, 256) # 隐藏层到输出层的线性变换 self.linear3 = nn.Linear(256, output_dim) # 输出层到最终输出的线性变换 def _activation(self, x): return torch.sigmoid(x) def forward(self, x): x = self._activation(self.linear1(x)) x = self._activation(self.linear2(x)) output = self.linear3(x) return output
- self.linear1和self.linear2之间的线性变换将输入维度从input_dim映射到128个神经元,然后再将128个神经元映射到256个神经元。
- 我们通过self._activation方法对输入数据进行激活函数处理。具体来说,它使用Sigmoid激活函数对输入数据进行非线性变换,将线性变换后的输出映射到0和1之间。
- 第三层: 输入为维度为 256, 输出维度为: 4
编写训练函数
def train(): torch.manual_seed(66) model = PhonePriceModel(input_dim, class_num) criterion = nn.CrossEntropyLoss() 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() total_loss = 0.0 total_num = 1 correct = 0 for x, y in dataloader: output = model(x) loss = criterion(output, y) optimizer.zero_grad() loss.backward() optimizer.step() total_num += len(y) total_loss += loss.item() * len(y) print('epoch: %4s loss: %.2f, time: %.2fs' % (epoch_idx + 1, total_loss / total_num, time.time() - start)) torch.save(model.state_dict(), 'price-model.bin')
- 💬要在PyTorch中查看随机数种子,可以使用
torch.random.initial_seed()
函数。这个函数会返回当前设置的随机数种子。
评估函数
def test(): model = PhonePriceModel(input_dim, class_num) model.load_state_dict(torch.load('price-model.bin')) dataloader = DataLoader(valid_dataset, batch_size=8, shuffle=False) correct = 0 for x, y in dataloader: output = model(x) y_pred = torch.argmax(output, dim=1) correct += (y_pred == y).sum() print('Acc: %.5f' % (correct.item() / len(valid_dataset)))
网络性能调优
- 对输入数据进行标准化
- 调整优化方法
- 调整学习率
- 增加批量归一化层
- 增加网络层数、神经元个数
- 增加训练轮数
🔎我们可以自行将优化方法由 SGD 调整为 Adam , 学习率由 1e-3 调整为 1e-4 ,对数据数据进行标准化 ,增加网络深度 。