房价预测模型代码笔记以及文心一言Q.A记录

房价预测模型代码笔记以及文心一言Q.A记录

    正在检查是否收录...
一言准备中...

导入库方面略过不提

第一部分:简易日期处理模块

def processdate(date): date_num = (int(date[:4]) - 2014)*12 + (int(date[4:6])-5) return date_num 

processdate 函数

这个函数的作用是将输入的日期字符串转化为一个整数。它将日期中的年份和月份信息分别转化为从2014年开始的月数。例如,对于输入 '201806',函数会返回 (2018-2014)*12 + (6-5) = 48 + 1 = 49

日期从2014年5月开始,所以这个模块会计算出距离2014年5月过去了多少个月。

第二部分:数据集

 class houseDataset(Dataset): def __init__(self, path, mode="train", feature_dim=5): with open(path,'r') as f: csv_data = list(csv.reader(f)) for index in range(1, len(csv_data)): csv_data[index][1] = processdate(csv_data[index][1]) //日期处理 csv_data[index][2] = str(eval(csv_data[index][2])) //这里的数据是价格,转为字符串形式 x = np.delete(np.array(csv_data)[1:].astype(float), [0, 2, 16], axis=1) //这里处理了特征项。去掉了第一行也就是特征的名称,然后去掉了0列(id)以及2列(price,实际上是每一组x对应的target)以及16列(zipcode,邮政编码。由于难以处理且在特征中无不可替代性而直接删去)。处理完后将这些数据转换为np的数组并转为浮点形式。 y = np.array(csv_data)[1:, 2].astype(float)/10**6 //如上所述,第二列实际上是我们的target。取出target后除以10^6,将价格转为一个较小的数方便对比。 self.x = torch.tensor(x) self.y = torch.tensor(y) self.x = (self.x - self.x.mean(dim=0,keepdim=True)) / self.x.std(dim=0,keepdim=True) //特征归一化 print('Finished reading the {} set of house Dataset ({} samples found)' .format(mode, len(self.x))) def __getitem__(self, item): return self.x[item].float(), self.y[item] def __len__(self): return len(self.x)

houseDataset 类

这个类用于从CSV文件中加载和处理数据。其中:

__init__ 方法首先读取CSV文件中的数据,并逐行进行处理。对于每行的第二个元素(索引为1的元素,即日期),使用 processdate 函数进行处理;对于每行的第三个元素,使用 eval 函数进行处理(注意这里使用 eval 可能存在安全风险,如果数据源不可靠的话)。然后,该方法创建输入 x 和目标输出 y,并对输入数据进行标准化处理。 __getitem__ 方法用于返回给定索引处的数据样本。 __len__ 方法返回数据集的长度。

笔记:数据处理逻辑已于代码注释给出,init函数实际上完成了数据集的导入、处理,并且将处理完的x和y作为数据集类型的全局变量供其它函数使用。getitem和len函数非常简单,实现的就是返回对应xy和数据集长度。

第三部分:用于预测的神经网络模型

class myNet(nn.Module): def __init__(self,inDim): super(myNet,self).__init__()//使用super修改nn.Module的初始化,这个步骤在数据集中就不用实现。 self.fc1 = nn.Linear(inDim, 64) self.relu = nn.ReLU() self.fc2 = nn.Linear(64, 1) # self.fc3 = nn.Linear(256,256) # self.fc4 = nn.Linear(inDim,1) /*inDim是你的特征维数,比如说你有5个特征,那么inDim就该输入为5。这里自定义了这个预测模型的网络层数,在未注释的版本中是将输入维度经过全连接层fc1,变化为64维的输出,此输出经过relu函数的非线性变换后再进入全连接层fc2,将64维降为1维即可输出。如果你有需要,可以像注释的两行一样自定义更多层数*/ def forward(self, x): x = self.fc1(x) x = self.relu(x) x = self.fc2(x) # x = self.relu(x) # # x = self.fc3(x) # x = self.relu(x) # # x = self.fc4(x) # x = self.relu(x) if len(x.size()) > 1: return x.squeeze(1) //将经过网络的x降为一维,方便与y作对比。 else: return x 

myNet 类

这是一个用于房价预测的神经网络模型。其中:

__init__ 方法定义了网络的结构。该网络有两个全连接层(fc1 和 fc2),并在两层之间使用ReLU激活函数。 forward 方法定义了数据在网络中的前向传播过程。输入数据首先通过第一个全连接层 fc1,然后经过ReLU激活函数,再通过第二个全连接层 fc2。如果输出的维度大于1,就使用 squeeze 方法去掉维度为1的维度。

注意:

代码中有一些被注释掉的代码,这些代码可能是用于测试或调试的,但在当前的实现中并未使用。 在 houseDataset 类中,数据被标准化(即进行了Z-score归一化),这是一种常见的数据预处理方法,有助于模型更好地学习和收敛。 神经网络模型 myNet 的结构相对简单,可能适合处理一些较为简单的房价预测任务。如果需要处理更复杂的任务,可能需要增加更多的层或调整网络结构。

第四部分:训练与验证

def train_val(model, trainloader, valloader,optimizer, loss, epoch, device, save_): # trainloader = DataLoader(trainset,batch_size=batch,shuffle=True) # valloader = DataLoader(valset,batch_size=batch,shuffle=True) model = model.to(device) plt_train_loss = [] plt_val_loss = [] val_rel = [] min_val_loss = 100000 for i in range(epoch): start_time = time.time() model.train() train_loss = 0.0 val_loss = 0.0 //记录时间,进入训练模式并初始化训练损失和验证损失。 for data in trainloader: optimizer.zero_grad() x, target = data[0].to(device), data[1].to(torch.float32).to(device) pred = model(x) bat_loss = loss(pred, target, model) bat_loss.backward() optimizer.step() train_loss += bat_loss.detach().cpu().item() //在一个epoch中,每组数据首先清零上一组遗留的梯度,然后将x和y放到机器上。 //接下来在model中传入特征x,计算预测值pred并计算每一个batch的损失。计算完成后将损失反向传播,并使用优化器更新模型。最后累加单个批次的损失,先将这个批次的loss取下并用item取值。 plt_train_loss. append(train_loss/trainloader.dataset.__len__()) //累加每个批次的损失,除以一个批次的训练集长度得平均损失。 model.eval() with torch.no_grad(): for data in valloader: val_x , val_target = data[0].to(device), data[1].to(device) val_pred = model(val_x) val_bat_loss = loss(val_pred, val_target, model) val_loss += val_bat_loss val_rel.append(val_pred) if val_loss < min_val_loss: torch.save(model, save_) //验证模式与训练模式大同小异。重要区别在于,验证时由于数据集不可用于改进模型,因此应该在无梯度计算下运行,同时也不需要反向传播算法。如果本次验证集的损失小于曾经记录的最小损失,更新这个值并保存这次训练的模型。 plt_val_loss. append(val_loss/valloader.dataset.__len__()) print('[%03d/%03d] %2.2f sec(s) TrainLoss : %3.6f | valLoss: %3.6f' % \ (i, epoch, time.time()-start_time, plt_train_loss[-1], plt_val_loss[-1]) ) plt.plot(plt_train_loss) plt.plot(plt_val_loss) plt.title('loss') plt.legend(['train', 'val']) plt.show() //打印训练成果和损失图

这段代码实现了一个基本的训练和验证过程,用于训练一个神经网络模型(由参数`model`提供)。它使用给定的训练数据加载器(`trainloader`)和验证数据加载器(`valloader`)来分别加载训练数据和验证数据。以下是代码的主要功能和步骤:

1. **设置模型和设备**:首先,它将模型发送到指定的设备(通常是GPU,如果可用)。

model = model.to(device)


2. **初始化损失列表**:初始化三个列表来存储训练损失、验证损失和验证预测。

 plt_train_loss = [] plt_val_loss = [] val_rel = [] 


3. **开始训练循环**:对于每一个epoch(由参数`epoch`提供),代码执行以下操作:


    * **重置损失**:在开始每个epoch之前,重置训练损失和验证损失。

python`train_loss = 0.0     val_loss = 0.0`


    * **训练阶段**:设置模型为训练模式,并对`trainloader`中的每个数据批次执行以下操作:
    
    
        + 清零梯度。
        + 将数据和目标加载到设备上。
        + 通过模型进行前向传播以获取预测。
        + 计算损失。
        + 反向传播损失。
        + 更新模型权重。
        + 累加训练损失。
    * **计算平均训练损失**:在每个epoch结束时,计算平均训练损失并存储在`plt_train_loss`列表中。
    

python`plt_train_loss.append(train_loss/trainloader.dataset.__len__())


    * **验证阶段**:设置模型为评估模式,并对`valloader`中的每个数据批次执行以下操作:
    
    
        + 将验证数据加载到设备上。
        + 通过模型进行前向传播以获取预测。
        + 计算验证损失。
        + 累加验证损失。
        + 存储验证预测。
    * **检查并保存最佳模型**:如果当前验证损失低于之前的最小验证损失,则保存模型。
    
    

python`if val_loss < min_val_loss:         torch.save(model, save_)`


    * **计算平均验证损失**:在每个epoch结束时,计算平均验证损失并存储在`plt_val_loss`列表中。
    

plt_val_loss.append(val_loss/valloader.dataset.__len__())


    * **打印训练和验证损失**:打印每个epoch的训练损失、验证损失和训练时间。
    

4. **绘制损失图**:在训练结束后,使用matplotlib绘制训练和验证损失的图。


```python
plt.plot(plt_train_loss)
plt.plot(plt_val_loss)
plt.title('loss')
plt.legend(['train', 'val'])
plt.show()
```

总的来说,这段代码实现了神经网络模型的训练、验证和损失的可视化。它使用训练数据来更新模型权重,并使用验证数据来评估模型的性能。在每个epoch结束时,它保存了平均训练损失和平均验证损失,并在训练结束后绘制了损失图。

第五部分:事前准备

device = 'cuda' if torch.cuda.is_available() else 'cpu' train_path = 'kc_house_data.csv' house_data = pd.read_csv(train_path) print(house_data.head()) house_data.info() print(house_data.describe()) house_dataset = houseDataset(train_path) train_set, val_set = random_split(house_dataset,[int(len(house_dataset)*0.8),len(house_dataset)- int(len(house_dataset)*0.8)]) print(train_set.indices) 

简单点讲,完成了数据集的导入,并且将内容划分为训练集和验证集。做完这些后,打印了数据集的一些初始状态。

第六部分:损失函数

def mseLoss(pred, target, model): loss = nn.MSELoss(reduction='mean') ''' Calculate loss ''' regularization_loss = 0 for param in model.parameters(): # TODO: you may implement L1/L2 regularization here # 使用L2正则项 # regularization_loss += torch.sum(abs(param)) regularization_loss += torch.sum(param ** 2) return loss(pred, target) + 0.00075 * regularization_loss loss = mseLoss

Q:发散:关于L2正则化与L1正则化?

A:L2正则化在机器学习和统计建模中起着关键的作用,尤其在控制模型的复杂度和提高泛化能力方面。具体来说,L2正则化有以下几个主要作用:

减少过拟合:过拟合是当模型在训练数据上表现非常好,但在新数据或测试数据上表现较差时发生的现象。L2正则化通过约束模型参数的大小,使其不会过于复杂,从而减少了过拟合的风险。这有助于模型更好地泛化到未见过的数据上。 控制模型复杂度:L2正则化通过在损失函数中添加模型参数的L2范数(即参数平方和的平方根),使得在优化过程中,参数值会倾向于变得更小。这有助于降低模型的复杂度,因为模型不再依赖于某些特定的、可能只在训练数据中出现的特征。

那么,为什么L2正则化会起效呢?这主要是基于以下几个原因:

奥卡姆剃刀原则:这个原则强调在多个可能的解释中,应选择最简单的那个。在机器学习中,这通常意味着更简单的模型(即参数值更小的模型)更可能是正确的模型。L2正则化通过鼓励参数值变小,从而遵循了这一原则。 数值稳定性:较小的参数值通常意味着模型对输入数据的微小变化不那么敏感。这有助于提高模型的数值稳定性,减少因数据微小变化而导致的模型输出的大幅波动。 权重衰减:L2正则化也被称为权重衰减,因为它在每次更新参数时都会减去一个与参数值成正比的项。这会导致参数值逐渐减小,从而降低模型的复杂度。

总的来说,L2正则化通过约束模型参数的大小,使其更加简单和稳定,从而提高模型的泛化能力。这是L2正则化在机器学习和统计建模中广泛应用的原因。

L1正则化是另一种在机器学习中常用的正则化方法,它与L2正则化在目标、方式和效果上都有所不同。

L1正则化是在损失函数的基础上,加上所有参数的绝对值之和(乘以一个常数),用于惩罚参数过大,促使模型更加稀疏化。它的主要目标是使模型参数尽量接近0,甚至变为0,从而达到特征选择的效果。在一些需要特征选择的问题上,L1正则化表现得更加优秀。然而,由于L1正则化的导数在0时不可导,使得优化问题更加复杂。

与L2正则化相比,L1正则化有以下几个主要区别:

稀疏性:L1正则化倾向于产生稀疏模型,将不重要的特征的权重压缩为零,从而可以作为特征选择的手段。而L2正则化则不会将权重完全压缩为零,而是将它们压缩到一个小的范围内,所有特征都会有不为零的权重。 解的少数性:在某些情况下,L1正则化可能存在多个解。而L2正则化通常具有少数解,尤其是当特征数量少于样本数量时。 计算复杂性:由于L1正则化倾向于产生稀疏模型,在某些算法中可能更难优化。而L2正则化的梯度是连续的,数学上更易处理和优化。 对于异常值的鲁棒性:L1正则化对异常值更鲁棒,因为它不会过分地惩罚大的权重。

此外,L1正则化和L2正则化在模型预测准确性方面也有所不同。L1正则化通过将参数压缩到极小值,可以抑制对数据的过拟合,因此会大大提升模型的泛化能力,但可能会使模型的预测准确率下降。而L2正则化不会完全抑制模型参数的系数,因此可能会减少模型的泛化能力,但有助于提升模型的预测准确性。

总的来说,L1正则化和L2正则化都是控制模型复杂度和防止过拟合的有效手段,但它们在实现方式、效果和应用场景上有所区别。选择哪种正则化方法需要根据具体的问题和实验结果来决定。

一些个人理解:两种正则化方法都试图降低参数大小。这种方法能够避免过拟合的原因是当你的模型出现过拟合时,它通常尝试模拟*所有*出现的特征点与标签的关系。我们知道,有时你的训练集中会出现明显过于偏移正常值的异常点。没有过拟合的函数将不会太受到此类异常点的影响,然而过拟合的函数由于其试图模拟所有点位,因此会导致模拟的结果异常的向此类异常点偏移,从而导致一些不应被考虑的特征点在模型训练中却占据了很大权重。

落实到参数时,理解这样一个场景:过拟合的模型由于过于复杂并试图模拟所有点位,因此其参数值将会异常偏大,导致他们对于异常特征点位更加敏感。较大的w与较大的x相乘,结果自然会对模型训练造成很大影响。正则化就是试图减少这种情况的发生。

再顺便一提:文心一言讲到L1正则化还可以用于选择最佳特征,这种特性是在模型的训练过程中自然而然的进行的。与此同时,还有一种用于选择最佳特征的方法SKB(Selective K-Best),可以在进行训练之前人为的选出最佳特征。如果你的数据集比较复杂,而你希望训练结果是一个相对稀疏、易于理解的模型,你可以选择L1正则化;如果你的数据集相对简单,或者你有自己的偏好,希望从一些特定角度理解特征和y之间的关系,可以考虑SKB。

第七部分:尾声

config = { 'n_epochs': 50, # maximum number of epochs 'batch_size': 25, # mini-batch size for dataloader 'optimizer': 'SGD', # optimization algorithm (optimizer in torch.optim) 'optim_hparas': { # hyper-parameters for the optimizer (depends on which optimizer you are using) 'lr': 0.0001, # learning rate of SGD 'momentum': 0.9 # momentum for SGD }, 'early_stop': 200, # early stopping epochs (the number epochs since your model's last improvement) 'save_path': 'model_save/model.pth', # your model will be saved here } model = myNet(18).to(device) # optimizer = optim.SGD(model.parameters(), lr=0.03,momentum=0.9) optimizer = torch.optim.Adam(model.parameters(), lr=0.03, weight_decay=0.001) #换一个优化器 trainloader = DataLoader(train_set,batch_size=config['batch_size'], shuffle=True) valloader = DataLoader(val_set,batch_size=config['batch_size'], shuffle=True) train_val(model, trainloader,valloader,optimizer, loss, config['n_epochs'], device,save_=config['save_path'])

这部分就是config的设置以及优化器的设置,然后进行一个训练的跑。

csv数据集训练数据app神经网络网络模型泛化能力机器学习python控制模型elo自定义稳定性模型训练cpucsv文件准确性优化问题代码注释数据预处理
  • 本文作者:WAP站长网
  • 本文链接: https://wapzz.net/post-10445.html
  • 版权声明:本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 4.0 许可协议。
本站部分内容来源于网络转载,仅供学习交流使用。如涉及版权问题,请及时联系我们,我们将第一时间处理。
文章很赞!支持一下吧 还没有人为TA充电
为TA充电
还没有人为TA充电
0
  • 支付宝打赏
    支付宝扫一扫
  • 微信打赏
    微信扫一扫
感谢支持
文章很赞!支持一下吧
关于作者
2.7W+
9
1
2
WAP站长官方

GPT-4.5 Turbo提前泄露?Altman亲自暗示新模型要来,传言本周四上线

上一篇

一款高效实用的文章采集工具

下一篇
  • 复制图片
按住ctrl可打开默认菜单