目录
鲍勃开了自己的手机公司。他想与苹果、三星等大公司展开硬仗。 他不知道如何估算自己公司生产的手机的价格。在这个竞争激烈的手机市场,你不能简单地假设事情。为了解决这个问题,他收集了各个公司的手机销售数据。
鲍勃想找出手机的特性(例如:RAM、内存等)和售价之间的关系。但他不太擅长机器学习。所以他需要你帮他解决这个问题。 在这个问题中,你不需要预测实际价格,而是要预测一个价格区间,表明价格多高。
需要注意的是: 在这个问题中,我们不需要预测实际价格,而是一个价格范围,它的范围使用 0、1、2、3 来表示,所以该问题也是一个分类问题。
数据说明:手机价格分类_数据集-阿里云天池 Mobile Price Classification
库函数导入
import torch
import torch.nn as nn
import pandas as pd
from sklearn.model_selection import train_test_split
from torch.utils.data import import Dataset,DataLoader,TensorDataset
from sklearn.preprocessing import StandardScaler
import time
一、构建数据集
数据共有 2000 条, 其中 1600 条数据作为训练集, 400 条数据用作测试集。 我们使用 sklearn 的数据集划分工作来完成。并使用 PyTorch 的 TensorDataset 来将数据集构建为 Dataset 对象,方便构造数据集加载对象。
"""构建数据"""
def phone_data_set(path):
# 加载本地数据文件
data = pd.read_csv(path)
# 抽离特征和目标数据
x = data.iloc[:,:-1]
y = data.iloc[:,-1]
# 标准化
transfer = StanderdScaler()
x = transfer.fit_transform(x.values)
x = torch.tensor(x,dtype=torch.float32)
y = torch.tensor(y.values,dtype=torch.int64) # 输出是分类结果,所以用整型
# 数据集划分
x_trian,x_test,y_train,y_test = train_test_split(x,y,test_size=0.2,random_state=42,stratify=y)
return x_trian,x_test,y_train,y_test
class my_phone_data_loader(Dataset):
# TensorDataset 可取代该类的作用
def __init__(self,x,y):
self.x = x
self.y = y
def __len__(self):
return len(self.x)
def __getitem__(self, index):
return self.x[index],self.y[index]
def data_loader(x_trian,y_train,batch_size=16):
# data=my_phone_data_loader(x_train,y_train)
data = TensorDataset(x_train,y_trian)
data_loader = DataLoader(data,batch_size=batch_size,shuffle=True)
return data_loader
二、构建分类网络模型
构建用于手机价格分类的模型叫做全连接神经网络。它主要由数个线性层来构建,在每个线性层后,还需使用激活函数。
"""模型 构建和初始化参数"""
class Net(torch.nn.Module):
def __init__(self,input_features,out_features):
super(Net,self).__init__()
# 隐藏层 LeakyReLU 激活和输出层 Softmax 激活
self.hide1 = nn.Sequential(nn.Linear(input_features,128),nn.LeakyReLU())
self.hide2 = nn.Sequential(nn.Linear(128,256),nn.LeakyReLU())
self.hide3 = nn.Sequential(nn.Linear(256,512),nn.LeakyReLU())
self.hide4 = nn.Sequential(nn.Linear(512,128),nn.LeakyReLU())
self.out = nn.Sequential(nn.Linear(128,out_features),nn.Softmax())
self.initdata()
def forward(self,input_data):
# 前向传播
x = self.hide1(iput_data)
x=self.hide2(x)
x=self.hide3(x)
x=self.hide4(x)
y_pred=self.out(x)
return y_pred#分类结果,混淆矩阵 [[0.9,0.01,.02,.7],[...],...]
def initdata(self):
# 权重He初始化
nn.init.kaiming_uniform_(self.hide1[0].weight,nonlinearity="leaky_relu")
nn.init.kaiming_uniform_(self.hide2[0].weight,nonlinearity="leaky_relu")
nn.init.kaiming_uniform_(self.hide3[0].weight,nonlinearity="leaky_relu")
nn.init.kaiming_uniform_(self.hide4[0].weight,nonlinearity="leaky_relu")
三、编写训练函数
网络编写完成之后,我们需要编写训练函数。所谓的训练函数,指的是输入数据读取、送入网络、计算损失、更新参数的流程,该流程较为固定。我们使用的是多分类交叉生损失函数、使用 SGD 优化方法。最终,将训练好的模型持久化到磁盘中。
"""训练数据"""
# 加载数据
x_train,x_test,y_trian,y_test=phone_data_set("./data/手机价格预测.csv")
def train():
data_loader_=data_loader(x_train,y_trian)
# 模型生成
x_features=x_train.shape[1]
y_features=torch.unique(y_trian).shape[0]# 输出特征(类别的数量)
model=Net(x_features,y_features)
# 初始化模型参数
# 默认是初始化过的
torch.nn.init.kaiming_uniform_(model.linear1.weight,nonlinearity="leaky_relu")
torch.nn.init.kaiming_uniform_(model.linear2.weight,nonlinearity="leaky_relu")
# 3.损失函数
loss_fn=torch.nn.CrossEntropyLoss()#torch.nn.MSELoss()#虽然分类的结果也可以用均方误差来计算,但是一般用交叉熵(因为计算出来的梯度更大)
# 4.优化器
optim=torch.optim.Adam(model.parameters(),lr=1e-4)
# 定义训练参数
epoch=100
for i in range(epoch):
e=0
count=0
start_time=time.time()
for x,y in data_loader_:
count+=1
# 生成预测值
y_pred=model(x)#执行model对象的forward
# 损失计算
loss=loss_fn(y_pred,y)
e+=loss
# 梯度清零
optim.zero_grad()
# 反向传播
loss.backward()
# 更新参数
optim.step()
end_time=time.time()
print(f"epoch:{i},loss:{e/count},time:{end_time-start_time}")
# 保存模型参数
# model.linear1.weight.data
torch.save(model.state_dict(),"./model/model.pth")
四、编写评估函数
评估函数、也叫预测函数、推理函数,主要使用训练好的模型,对未知的样本的进行预测的过程。这里使用前面单独划分出来的测试集来进行评估。
"""评估函数"""
def test():
# 加载数据
data_loader_=data_loader(x_test,y_test,batch_size=16)
# data_loader_=DataLoader(data,batch_size=4,shuffle=True)
# 加载模型
# 模型生成
x_test_features=x_test.shape[1]
y_test_features=torch.unique(y_test).shape[0]# 类别数
model=Net(x_test_features,y_test_features)
# model.linear1.weight=model.linear1.weight.data
state_dict=torch.load("./model/model.pth",map_location="cpu")
model.load_state_dict(state_dict)
total=0
for x,y in data_loader_:
y_pred=model(x)#[0.4,0.3,0.1,0.1,0.1]
y_pred=torch.argmax(y_pred,dim=1)
# print("y",y)
# print("y_pred",y_pred)
total+=torch.sum(y_pred==y)
print(f"精准度:{total/len(x_test)}")
五、网络性能调优
可以通过以下方面对模型进行调优:
对输入数据进行标准化
调整优化方法
调整学习率
增加批量归一化层
增加网络层数、神经元个数
增加训练轮数