记录一下在做kaggle房价预测时的一些实践经验
目录
一.总体流程
1.官网下载房价数据集
地址:House Prices - Advanced Regression Techniques | Kaggle
下载好的数据集包含4个文件:
‘train.csv’:训练数据
‘test.csv’:测试数据
‘data_description.txt’:说明各个特征的文档
‘sample_submission.csv’:预测结果提交的示例
这里我自己学习就用到了train.csv
2.预处理数据集
1)用pd读取train.csv文件,得到的数据类型是pandas.core.frame.DataFrame类型,size为1460x81,最后一列SalePrice是要进行预测的(即labels),其余列是影响这个房价的一些因素(即features)
2)将features和labels分离,用到的函数是iloc[],得到inputs.shape是1460x80,outputs,shape是1460x1
3)由于特征列数较多,且有许多离散值数据列,在后期训练时可能会梯度爆炸,因此在这里用drop()函数丢掉一些相关性较弱的列
inputs = inputs.drop('Street', axis=1)
#‘Street’表示要drop掉的这一列的名字
#axis=1表示对这一列drop掉,axis=0表示对这一行drop掉
4)数据表中有一些数值列中存在nan值的,这里需要重新赋值:将这一列的均值赋过去:
inputs=inputs.fillna(inputs.mean())
数据表中有一些非数值列中存在nan值的,这里需要将“NaN”视为一个类别 这里利用pandas库里面的get_dummies(inputs,dummy_na=True)建0,1矩阵,转换后的inputs.shape变为1460x169
inputs=pd.get_dummies(inputs,dummy_na=True)
5)将inputs,outputs转换成tensor,在之后投入到网络里训练时出现了报错:说两个数据类型不匹配,因此这里将转换成tensor的x,y再统一float类型,都为float32的
#定义x,y为全局变量,这样在调用函数时,也可以在局部进行修改
global x
global y
#转换成tensor类型
x=torch.tensor(inputs.values)
y=torch.tensor(outputs.values)
#统一float32,当x,y是tensor类型时才能用,不然会报错
x= x.to(torch.float32)
y= y.to(torch.float32)
3.构造模型
1)定义网络,之前x.shape是1460x169,y.shape是1460x1,因此net[1]的输入是169维,net[10]的输出是1维
net=nn.Sequential(nn.Flatten(),nn.Linear(169,128),nn.ReLU(),nn.Dropout(dropout1),
nn.Linear(128,64),nn.ReLU(),nn.Dropout(dropout2),
nn.Linear(64,32),nn.ReLU(),nn.Dropout(dropout3),
nn.Linear(32,1))
2)定义损失函数,使用均方误差。当比较真实值和估计值用的--->是平方损失;当预测种类时一般使用交叉熵损失
loss = nn.MSELoss()
3)“对于房价,就像股票价格一样,我们更关心的是相对数量而不是绝对数量。更具体地说,我们往往更关心相对误差,而不是绝对误差𝑦-𝑦̂”,这里使用log_rmse来计算损失
def log_rmse(net,features,labels):
# 为了进一步稳定取对数时的数值,将小于1的数值设为1
clipped_preds = torch.clamp(net(features), 1, float('inf'))
rmse = torch.sqrt( torch.mean(loss(torch.log(clipped_preds),torch.log(labels)))) # 计算预测和实际的RMSE值
return rmse.item()
4)定义优化器,使用Adam,加上了权重衰退
trainer= torch.optim.Adam(net.parameters(),lr= 0.03, weight_decay=3)
5)定义小批量迭代器,用于训练时的迭代遍历
def data_iter(batch_size,features,labels):
num_examples=len(features)
#range() 函数可创建一个整数列表,包含0-->n-1个数
"""
range(star,stop,step)
star: 计数从star开始.默认是从0开始.
stop: 计数到stop结束,但不包括stop.
step: 步长,默认为1.
"""
#list()函数可将任何迭代数据转换成list列表形式,并返回列表
indices=list(range(num_examples))
#random.shuffle()将indicates列表的顺序随机打乱,而不会产生新的列表
random.shuffle(indices)
for i in range(0,num_examples,batch_size):
batch_indices=torch.tensor(indices[i:min(i+batch_size,num_examples)])
#yield意思是不断返回操作后的features[batch_indices],labels[batch_indices]
#一直到循环结束为止
yield features[batch_indices],labels[batch_indices]
6)初始化权重参数
def init_weight(m):
if type(m)==nn.Linear:
nn.init.normal_(m.weight,std=0.1)
net.apply(init_weight)
4.训练模型
1)初始化一些变量
dropout1,dropout2,dropout3,weight_decay=0.2,0.5,0.7,3
num_epochs,batch_size=100,256
2)训练:训练后的每一轮的loss都存放在train_ls中,可以通过查看train_ls的loss来调参,让模型训练的更好一点,一般调lr,weight_decay,dropout,num_epochs,batch_size这些都可以试试
def train():
train_ls=[]
for epoch in range(num_epochs):
for X,Y in data_iter(256,x,y):
with torch.enable_grad():
trainer.zero_grad()
l=loss(net(X),Y)
l.sum().backward()
trainer.step()
train_ls.append(log_rmse(net,X,Y))
return train_ls
train_l=train()
train_l
二.总结报错
1.自己写的时候优化器用的是SGD,损失函数用的是均方误差,没有使用log_rmse,训练模型100个epoch,进行lr学习率调参,结果误差就一直过大,稍微将学习率调大一点loss就变成了nan,不知道哪里出了问题。但使用Adam优化器和log_rmse作为损失后,loss就变得正常。
2.在def train()函数中有一个语句,之前写的是 for x,y in data_iter(256,x,y):,运行的时候一直报错:
UnboundLocalError: local variable 'x' referenced before assignment
这是因为函数内部对变量赋值进行修改后,该变量就会被Python解释器认为是局部变量而非全局变量。
解决方式: 在函数内部,给变量添加global修饰符,声明此变量为全局变量,并且尽量让两个变量名不一致,改成:for X,Y in data_iter(256,x,y):
3.对一个DataFrame类型数据不能直接to(torch.float32), 这样会报错说:
AttributeError: 'DataFrame' object has no attribute 'to'
因此需要把DataFrame类型数据先转换成tensor类型,再转成float32格式
4.x,y数据的浮点数类型得一致,不然就会报错:
RuntimeError:mat1 and mat2 must have the same dtype
因此需要x.to(torch.float32),y.to(torch.float32)