【“第二课堂”AI实践课堂笔记】:3__阿里云天池数据挖掘:二手车交易价格预测——代码分析与相关知识点

前言

在当今数字化时代,数据挖掘已成为从海量数据中提取有价值信息和知识的关键技术。它通过综合运用统计学机器学习数据库技术,帮助我们发现数据中的隐藏模式、关系和趋势,从而为决策提供有力支持。
本笔记所涉及的“二手车交易价格预测”案例,是数据挖掘在实际应用中的一个典型场景。我们将详细探讨从数据的读取与预处理,到构建深度神经网络模型进行训练和优化,再到最终利用训练好的模型进行价格预测的全过程。
通过对这段代码的分析与相关知识点的理解,我们将能够全面了解如何运用数据挖掘技术解决实际问题,掌握其中的关键步骤和技术要点,为之后在数据挖掘领域与AI学习领域的进一步探索和应用打下坚实的基础。

一、数据读取与预处理

数据读取与合并清洗部分部分主要是靠python中的pandas库的操作来实现,后使用oneHotEncode函数和数据的归一化处理来做预处理。下面是具体内容:

  1. 数据读取与合并清洗
  • 首先,从指定路径读取训练集 ‘used_car_train_20200313.csv’和测试集’used_car_testB_20200421.csv’。使用pd.concat([train_data, test_data])将训练集和测试集合并,以便统一进行数据处理。
test_data = pd.read_csv('/gemini/data-1/used_car_testB_20200421.csv', sep=' ')
test_data.shape
# 导入数据
train_data = pd.read_csv('/gemini/data-1/used_car_train_20200313.csv', sep=' ')
train_data.shape
# 合并数据
data = pd.concat([train_data, test_data])
data.shape
  • 对于数据中的-,使用data = data.replace('-', '-1')进行替换。
    作用
    统一数据格式:将特殊的符号 ‘-’ 转换为一个明确的数值 ‘-1’,使得数据格式更加统一,便于后续的数据处理和分析。
    填补缺失或未知值:在某些情况下,‘-’ 可能表示缺失或未知的数据。将其替换为一个特定的值(如 ‘-1’ )可以明确标识这些特殊情况,方便后续对缺失值或异常值进行处理。
    适应模型输入要求:某些模型或算法可能对输入数据的格式有特定要求,将 ‘-’ 统一替换为 ‘-1’ 可以确保数据能够顺利输入到模型中进行计算和训练,避免因为特殊符号导致的错误或异常。
    便于数值计算和比较:将字符型的 ‘-’ 转换为数值型的 ‘-1’ ,更便于进行数学运算和比较,有助于后续的数据特征工程和模型训练。
  • 对于power值大于 600 的,通过data.loc[data['power']>600,'power'] = 600进行限制。
  • 对于离散特征中存在的缺失值,使用data[col] = data[col].fillna('-1')填充为-1。
  • 去除name和regionCode等可能无关的列,通过data.drop(['name', 'egionCode'], axis=1, inplace=True)实现。
data.columns
data = data.replace('-', '-1')
data.notRepairedDamage = data.notRepairedDamage.astype('float32')
data.loc[data['power']>600,'power'] = 600
# 处理离散数据
for col in config['cate_cols']:
    data[col] = data[col].fillna('-1')
data = oneHotEncode(data, config['cate_cols'])
# 处理(可能)无关数据 
data.drop(['name', 'regionCode'], axis=1, inplace=True)
data.columns

  1. 特征工程
    定义了oneHotEncode函数对离散特征进行One-Hot编码
def oneHotEncode(df, colNames):
	for col in colNames:
		dummies = pd.get_dummies(df[col], prefix=col)
		df = pd.concat([df, dummies],axis=1)
		df.drop([col], axis=1, inplace=True)
	return df
  • 关于One-hot 编码的知识点
    One-hot 编码(One-Hot Encoding)是一种将分类变量转换为数字向量的编码方式。
    在 One-hot 编码中,对于具有 n 个不同类别的分类特征,会将其转换为一个 n 维的向量。对于每个样本,如果该特征的值属于某个类别,那么在对应类别的维度上为 1,其余维度上为 0。
    例如,对于一个表示“星期几”的特征,有“星期一”“星期二”“星期三”“星期四”“星期五”“星期六”“星期日”这 7 个类别。如果某样本在这个特征上的值是“星期三”,经过 One-hot 编码后,对应的编码向量为 [0, 0, 1, 0, 0, 0, 0]。
    One-hot 编码的优点:
    能够处理非数值型的分类数据,将其转化为模型可以处理的数值形式。
    在一定程度上避免了类别之间的大小、顺序等不合理的假设。
    One-hot 编码的缺点:
    如果类别数量很多,会导致特征维度大幅增加,可能引发维度灾难问题,增加计算成本和存储成本。
    编码后的特征变得非常稀疏,数据中存在大量的 0。
  1. 对于连续特征:
    缺失值用 0 填充:data[col] = data[col].fillna(0)
    进行归一化处理:data[col] = (data[col]-data[col].min()) / (data[col].max()-data[col].min())
# 处理连续数据
for col in config['num_cols']:
    data[col] = data[col].fillna(0)
    data[col] = (data[col]-data[col].min()) / (data[col].max()-data[col].min())
  • 关于归一化(特征缩放)的知识点
    归一化(Normalization)是一种数据预处理技术,用于将数据映射到一个特定的范围或标准形式,以提高模型的性能和稳定性。
    以下是一些常见的数据归一化算法:
    1. Min-Max 归一化(线性归一化):
      公式:
      x n o r m = x − x min ⁡ x max ⁡ − x min ⁡ x_{n o r m}=\frac{x-x_{\min }}{x_{\max }-x_{\min }} xnorm=xmaxxminxxmin
      其中, x x x是原始数据, x m i n x_{min} xmin 是数据集中的最小值, x m a x x_{max} xmax 是数据集中的最大值。经过 Min - Max 归一化后,数据被映射到 [ 0 , 1 ] \left[0,1\right] [0,1] 区间内。
      例如,对于数据集 { 1 , 3 , 5 , 7 , 9 } \left\{1,3,5,7,9\right\} {1,3,5,7,9} , x m i n = 1 x_{min}=1 xmin=1 , x m a x = 9 x_{max}=9 xmax=9那么对于数据,归一化后的值为 3 − 1 9 − 1 = 0.25 \frac{3-1}{9-1} =0.25 9131=0.25
    2. Z-score 标准化(零均值标准化)
      公式:
      x n o r m = x − μ σ x_{n o r m}=\frac{x-\mu}{\sigma} xnorm=σxμ
      其中, x x x 是原始数据, μ \mu μ 是数据集的均值, σ \sigma σ 是数据集的标准差。Z - Score 标准化后,数据的均值为 0 0 0,标准差为 1 1 1
      例如,对于数据集 { 1 , 3 , 5 , 7 , 9 } \left\{1,3,5,7,9\right\} {1,3,5,7,9} μ = 5 \mu=5 μ=5 σ = 2.58 \sigma=2.58 σ=2.58,那么对于数据 3 3 3,标准化后的值为 3 − 5 2.58 ≈ − 0.775 \frac{3-5}{2.58} \approx -0.775 2.58350.775,使数据具有零均值和单位方差。
  • 归一化的主要作用包括
    提升模型训练的收敛速度:当数据的量纲和数值范围差异较大时,归一化可以使不同特征在数值上处于相近的范围,有助于模型更快地找到最优解。
    提高模型的稳定性和泛化能力:避免某些特征因为数值过大或过小而在模型训练中占据主导地位,使模型能够更公平地对待不同特征,从而提高模型的泛化能力。
    便于不同特征之间的比较和分析:归一化后的数据在同一尺度下,便于对特征的重要性和相关性进行分析和比较。

二、模型构建

  1. 网络结构设计
    定义Network类作为模型结构。包含多个线性层,每层后接批归一化层和ReLU激活函数,最后输出一个预测值。
        class Network(nn.Module):
            def __init__(self, in_dim, hidden_1, hidden_2, hidden_3, hidden_4):
                super().__init__()
                self.layers = nn.Sequential(
                    nn.Linear(in_dim, hidden_1),
                    nn.BatchNorm1d(hidden_1),
                    nn.ReLU(),
                    nn.Linear(hidden_1, hidden_2),
                    nn.BatchNorm1d(hidden_2),
                    nn.ReLU(),
                    nn.Linear(hidden_2, hidden_3),
                    nn.BatchNorm1d(hidden_3),
                    nn.ReLU(),
                    nn.Linear(hidden_3, hidden_4),
                    nn.BatchNorm1d(hidden_4),
                    nn.ReLU(),
                    nn.Linear(hidden_4, 1)
                )

            def forward(self, x):
                y = self.layers(x)
                return y
  • 其中包含的重要函数及知识点
    1. ReLU(Rectified Linear Unit,修正线性单元)函数是一种常用的激活函数,其数学表达式为:
      f ( x ) = m a x ( 0 , x ) f(x)=max(0,x) f(x)=max(0,x)
      也就是说,当 x x x输入小于 0 0 0时,输出为 0 0 0;当输入 x x x大于等于 0 0 0时,输出等于输入 x x x
      ReLU 函数具有以下优点
      计算速度快:相比于传统的激活函数如 Sigmoid 函数和 Tanh 函数,ReLU 函数的计算非常简单,只需要判断输入的正负,因此在计算上更加高效,这有助于加快神经网络的训练速度。
      缓解梯度消失问题:在深层神经网络中,Sigmoid 函数和 Tanh 函数的梯度在输入值较大或较小时会趋近于,导致反向传播过程中梯度消失,使得网络训练变得困难。ReLU 函数在输入大于时梯度为,一定程度上缓解了梯度消失的问题,有助于更有效地训练深层网络。
      稀疏激活性:ReLU 函数会使一部分神经元的输出为,使得网络具有稀疏性,这有助于提取数据中的重要特征,并且减少参数的相互依存关系,降低过拟合的风险。
      Q:为什么没有relu函数,就无法拟合除一次函数的更多函数?
      A: 如果没有像 ReLU 这样的非线性激活函数,仅依靠线性组合(如全连接层的线性变换),神经网络就只能表示线性函数。
      这是因为多个线性变换的组合仍然是一个线性变换。假设我们有两个线性层,第一层的输出为 y 1 = W 1 x + b 1 y_1=W_1x+b_1 y1=W1x+b1 ,第二层以第一层的输出作为输入,输出为 y 2 = W 2 y 1 + b 2 = W 2 ( W 1 x + b 1 ) + b 2 y_2=W_2y_1+b_2=W_2\left(W_1x+b_1\right)+b_2 y2=W2y1+b2=W2(W1x+b1)+b2,仍然是输入 x x x的线性函数。
      而现实世界中的问题和数据往往具有非线性的特征和模式。例如图像识别中,不同的对象和特征之间的关系是非线性的;在预测股票价格时,影响因素与价格之间的关系也是复杂的非线性关系。
      引入像 ReLU 这样的非线性激活函数后,使得神经元的输出不再是输入的简单线性组合。例如 ReLU 函数将输入小于 0 的部分截断为 0,大于 0 的部分保持不变,这种非线性的变换使得神经网络能够学习和拟合输入数据中的非线性模式和特征,从而能够表示和拟合各种各样的复杂函数,而不仅仅局限于线性函数。
    2. nn.Linear是用于实现全连接层(fully connected layer)的模块
      全连接层的作用是对输入数据进行线性变换。假设输入数据的维度为 in_features,输出数据的维度为 out_features,那么 nn.Linear 层的作用可以用数学公式表示为:
      y = x W T + b y=xW^T+b y=xWT+b
      其中, x x x 是输入数据,大小为 (batch_size, in_features) W W W是权重矩阵,大小为 (out_features, in_features) b b b 是偏置向量,大小为 (out_features) y y y 是输出数据,大小为 (batch_size, out_features)
      例如,如果有一个输入数据 x x x,形状为 (10, 20)(即批量大小为 10,特征维度为 20),定义一个全连接层 fc = nn.Linear(20, 30),那么经过该全连接层后的输出 y 的形状将为 (10, 30)
      全连接层在神经网络中应用广泛,常用于对提取到的特征进行综合和分类等任务。
    3. nn.BatchNorm1d是 PyTorch 中用于一维数据的批归一化(Batch Normalization)层
      批归一化的主要作用是对每个小批量(mini - batch)的数据在层内进行标准化处理,使得数据的分布具有稳定性,加速模型的训练和收敛。
      在训练过程中,对于给定的输入数据 x x x,对于每个特征维度,nn.BatchNorm1d 执行以下操作:
      计算小批量数据的均值 μ B = 1 m ∑ i = 1 m x i \mu_B=\frac{1}{m}\sum_{i=1}^{m}x_i μB=m1i=1mxi,其中 m m m是小批量中的样本数量。
      计算小批量数据的方差 μ B 2 = 1 m ∑ i = 1 m ( x i − μ B ) 2 \mu_B^2=\frac{1}{m}\sum_{i=1}^{m}(x_i-\mu_B)^2 μB2=m1i=1m(xiμB)2
      对输入进行标准化 x ^ i = x i − μ B σ B 2 + ϵ \hat{x}_i=\frac{x_i-\mu_B}{\sqrt{\sigma_B^2+\epsilon}} x^i=σB2+ϵ xiμB,其中 ϵ \epsilon ϵ 是一个很小的常数(如 1 e − 5 1e - 5 1e5),用于防止除数为 0 0 0
      进行缩放和平移操作 y i = γ x ^ i + β y_i=\gamma\hat{x}_i+\beta yi=γx^i+β,其中 γ \gamma γ(可学习的缩放参数)和 β \beta β(可学习的偏移参数)是网络学习得到的参数。
      通过批归一化,可以使得每一层神经网络的输入分布相对稳定,有助于缓解梯度消失或爆炸问题,使训练更加稳定,提高训练速度,并且在一定程度上起到正则化的作用,减少过拟合的风险。
  1. 初始化权重
    在模型定义后,使用Xavier初始化权重。
        for line in model.layers:
            if type(line) == nn.Linear:
                nn.init.xavier_uniform_(line.weight)
  • Xavier初始化权重知识点
    Xavier初始化(也称为Glorot初始化)是一种用于初始化神经网络权重的方法,由Xavier Glorot 和 Yoshua Bengio 提出。
    Xavier初始化的基本思想是,对于一个具有n_in个输入神经元和n_out个输出神经元的全连接层,权重 W W W的初始化值应从均值为 0 0 0,方差为 2 n − i n + n − o u t \frac{2} {n -in + n - out} nin+nout2的正态分布中采样,或者从 [ − 6 n − i n + n − o u t , 6 n − i n + n − o u t ] \left[-\sqrt{\frac{6} {n -in + n - out}}, \sqrt{\frac{6} {n -in + n - out}}\right] [nin+nout6 ,nin+nout6 ]区间内的均匀分布中采样。

三、模型训练

  1. 训练设置
    配置超参数:
    epoch为 10 次。
    batch_size为 512 。
    学习率learning_rate为 8e-3 。
    指定使用cuda设备。
    选择均方误差MSELoss作为损失函数。
    使用Adam优化器,并设置学习率。
# 将数据转化为tensor,并移动到cpu或cuda上
train_features = torch.tensor(train_data.values, dtype=torch.float32, device=config['device'])
train_num = train_features.shape[0]
train_labels = torch.tensor(train_target.values, dtype=torch.float32, device=config['device'])
 
validation_features = torch.tensor(validation_data.values, dtype=torch.float32, device=config['device'])
validation_num = validation_features.shape[0]
validation_labels = torch.tensor(validation_target.values, dtype=torch.float32, device=config['device'])
# 特征长度
train_features[1].shape
# 定义损失函数和优化器
criterion = nn.MSELoss()
criterion.to(config['device'])
optimizer = optim.Adam(model.parameters(), lr=config['learning_rate'])
  • 损失函数有关知识点
    损失函数(Loss Function)在机器学习和深度学习中是用于衡量模型预测结果与真实结果之间差异的函数。
    常见的损失函数包括:
    均方误差(Mean Squared Error,MSE)
    对于回归问题,若预测值为 y ^ \hat{y} y^,真实值为 y y y,样本数量为 n n n,则 MSE 的计算公式为:
    M S E = 1 n ∑ i = 1 n ( y i ^ − y i ) 2 MSE=\frac{1}{n}\sum_{i=1}^{n}(\hat{y_i}-y_i)^2 MSE=n1i=1n(yi^yi)2
    平均绝对误差(Mean Absolute Error,MAE)
    计算公式为:
    M S E = 1 n ∑ i = 1 n ∣ y i ^ − y i ∣ MSE=\frac{1}{n}\sum_{i=1}^{n} \left|\hat{y_i}-y_i\right| MSE=n1i=1nyi^yi
    交叉熵损失函数(Cross - Entropy Loss Function)
    在分类问题中应用广泛,对于二分类问题,若预测概率为 p p p,真实标签 y y y y = 1 y=1 y=1表示正类, y = 0 y=0 y=0表示负类),则交叉熵损失为:
    L = − [ y l o g ( p ) + ( 1 − y ) l o g ( 1 − p ) ] L=-\left[ylog(p)+(1-y)log(1-p)\right] L=[ylog(p)+(1y)log(1p)]
    Hinge 损失函数
    常用于支持向量机(SVM)中,对于二分类问题,若预测值为 f ( x ) f(x) f(x),真实标签 y y y ( y = ± 1 ) (y=\pm1) y=±1,则 Hinge 损失为:
    L = m a x ( 0 , 1 − y f ( x ) ) L=max\left( 0,1-yf(x)\right) L=max(0,1yf(x))
    损失函数的作用是指导模型的学习过程,通过优化算法最小化损失函数的值,使得模型的预测结果不断逼近真实结果。
  1. 训练循环
    在每个epoch中:
    模型设置为训练模式:model.train()
    按批次获取数据进行训练。
    计算预测值pred
    计算损失loss
    处理NaN损失值,若出现则中断训练。
    计算平均绝对误差mae并记录。
    梯度清零:optimizer.zero_grad()
    反向传播:loss.backward()
    参数更新:optimizer.step()
    每个epoch结束后,在验证集上评估模型:
    模型设置为评估模式:model.eval()
    计算验证集上的平均绝对误差validation_mae
    保存模型:torch.save(model, 'odel.pth')
    记录每个epoch的训练平均绝对误差和验证平均绝对误差。
# 开始训练
 
mae_list = []
 
for epoch in range(config['epoch']):
    losses = []
    model.train()
    for i in range(0, train_num, config['batch_size']):
        end = i + config['batch_size']
        if i + config['batch_size'] > train_num-1:
            end = train_num-1
        mini_batch = train_features[i: end]
        mini_batch_label = train_labels[i: end]
        pred = model(mini_batch)
        pred = pred.squeeze()
        loss = criterion(pred, mini_batch_label)
 
        if torch.isnan(loss):
            break
        mae = torch.abs(mini_batch_label-pred).sum()/(end-i)
        losses.append(mae.item())
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    
    model.eval()
    pred = model(validation_features)
    validation_mae = torch.abs(validation_labels-pred.squeeze()).sum().item()/validation_num
    
    mae_list.append((sum(losses)/len(losses), validation_mae))
        
    print(f"epoch:{epoch + 1} MAE: {sum(losses)/len(losses)}, Validation_MAE: {validation_mae}")
    torch.save(model, 'model.pth')
  • 训练集、验证集、测试集有关知识点
    在机器学习中,训练集(Training Set)、验证集(Validation Set)和测试集(Test Set)都有各自重要的作用:
    1. 训练集(Training Set)
      训练集是用于模型学习和参数调整的数据集。模型通过在训练集上进行学习,不断调整内部的参数,以拟合数据中的模式和规律。
      例如,假设有一个图像分类任务,有 1000 张带有标签的图像,我们可以选择其中 700 张作为训练集。模型在这 700 张图像及其对应的标签上进行训练,学习如何根据图像的特征来预测其类别。
    2. 验证集(Validation Set)
      验证集用于在训练过程中评估模型的性能和调整超参数。在训练过程中,模型会在每个训练周期结束后在验证集上进行测试,根据验证集上的性能指标(如准确率、召回率、F1 值、均方误差等)来判断模型是否过拟合或欠拟合,进而调整模型的结构或超参数(如学习率、层数、节点数等)。
      接着上面的例子,我们可以从剩下的 300 张图像中拿出 100 张作为验证集。在训练过程中,使用验证集来确定模型的最佳训练轮数、学习率的调整等,以找到性能最佳的模型配置。
    3. 测试集(Test Set)
      测试集用于评估模型最终的泛化能力,即模型在新的、未见过的数据上的表现。测试集在整个模型训练和调优过程中是完全独立的,只有在模型训练和基于验证集调优完成后,才将模型应用于测试集来评估其性能。
      在上述例子中,剩下的 200 张图像可以作为测试集。当模型基于训练集训练好,并基于验证集完成超参数调整后,在这 200 张图像上进行测试,得到模型的最终性能指标,以评估模型的实际应用效果和泛化能力。
      总之,训练集用于模型学习,验证集用于模型选择和超参数调整,测试集用于评估最终模型的泛化性能,三者相互配合,共同推动机器学习模型的开发和优化。

四、预测阶段

  1. 模型加载
    使用torch.load('model.pth', map_location=config['device'])加载训练好的模型。
  2. 数据准备
    读取测试数据’./one_hot_testB.csv’。
    进行数据预处理,去除特定列。
    将数据转换为张量格式。
  3. 进行预测
    将测试数据输入模型得到预测结果pred。
    将预测结果转换为DataFrame格式。
    与原始数据合并并保存为output.csv。
import pandas as pd
import torch
from torch import nn
from settings import config
class Network(nn.Module):
    def __init__(self, in_dim, hidden_1, hidden_2, hidden_3, hidden_4):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(in_dim, hidden_1),
            nn.BatchNorm1d(hidden_1),
            nn.ReLU(),
            nn.Linear(hidden_1, hidden_2),
            nn.BatchNorm1d(hidden_2),
            nn.ReLU(),
            nn.Linear(hidden_2, hidden_3),
            nn.BatchNorm1d(hidden_3),
            nn.ReLU(),
            nn.Linear(hidden_3, hidden_4),
            nn.BatchNorm1d(hidden_4),
            nn.ReLU(),
            nn.Linear(hidden_4, 1)
        )
 
    def forward(self, x):
        y = self.layers(x)
        return y
model = torch.load('model.pth', map_location=config['device'])
data = pd.read_csv('./one_hot_testB.csv')
data.shape
data.columns
data = data.drop(columns=['Unnamed: 0', 'price'])
test = data.drop(columns='SaleID')
test.shape
test = torch.tensor(test.values, dtype=torch.float32)
pred = model(test)
price = pd.DataFrame(pred.detach().cpu().numpy(), columns=['price'])
res = pd.concat([data.SaleID, price], axis=1)
res.to_csv('output.csv')

五、总结与改进

总结
整个代码涵盖了从数据预处理、模型构建、训练到预测的完整流程。采用了常见的深度学习技术和数据处理方法。
拓展
为了优化模型,可以尝试调整模型结构,如增加层数或改变神经元数量。探索其他优化器或调整学习率策略。进一步进行特征工程或数据增强操作。

  • 如:
    在数据预处理方面:使用 sklearn 中的 MinMaxScaler 来进行连续数据的归一化,相比手动计算更加规范和准确。
    在学习率调整方面:
    引入了学习率调度器 StepLR,每 5 个 epoch 将学习率减半。这样可以在训练过程中动态调整学习率,有助于避免模型在训练后期由于学习率过大而无法收敛或者过拟合。

代码总体展示

!注:源码来自网络

#训练---------------------------------------------------------------------------------
import pandas as pd
import numpy as np
from torch import nn, optim
import torch
import matplotlib.pyplot as plt
config = {
    'epoch': 10,
    'batch_size': 512,
    'learning_rate': 8e-3,
    'device': 'cuda',
    "num_cols": ['regDate', 'creatDate', 'power', 'kilometer', 'v_0', 'v_1', 'v_2', 'v_3', 'v_4', 'v_5', 'v_6', 'v_7', 'v_8', 'v_9', 'v_10',
                 'v_11', 'v_12', 'v_13', 'v_14'],
    "cate_cols": ['model', 'brand', 'bodyType', 'fuelType', 'gearbox', 'seller', 'notRepairedDamage']
}
test_data = pd.read_csv('/gemini/data-1/used_car_testB_20200421.csv', sep=' ')
test_data.shape
# 导入数据
train_data = pd.read_csv('/gemini/data-1/used_car_train_20200313.csv', sep=' ')
train_data.shape
# 合并数据
data = pd.concat([train_data, test_data])
data.shape
# 定义One-Hot编码函数
def oneHotEncode(df, colNames):
    for col in colNames:
        dummies = pd.get_dummies(df[col], prefix=col)
        df = pd.concat([df, dummies],axis=1)
        df.drop([col], axis=1, inplace=True)
    return df
data.columns
data = data.replace('-', '-1')
data.notRepairedDamage = data.notRepairedDamage.astype('float32')
data.loc[data['power']>600,'power'] = 600
# 处理离散数据
for col in config['cate_cols']:
    data[col] = data[col].fillna('-1')
data = oneHotEncode(data, config['cate_cols'])
 
# 处理连续数据
for col in config['num_cols']:
    data[col] = data[col].fillna(0)
    data[col] = (data[col]-data[col].min()) / (data[col].max()-data[col].min())
 
# 处理(可能)无关数据 
data.drop(['name', 'regionCode'], axis=1, inplace=True)
 
data.columns
# 暂存处理后的test数据集
test_data = data[pd.isna(data.price)]
test_data.to_csv('./one_hot_testB.csv')
# 删除test数据(price is nan)
data.reset_index(inplace=True)
train_data = data
train_data = train_data.drop(data[pd.isna(data.price)].index)
train_data.shape
# 删除ID
train_data.drop(['SaleID'], axis=1, inplace=True)
# 打乱
train_data = train_data.sample(frac=1)
train_data.shape
# 分离目标
train_target = train_data['price']
train_data.drop(['price', 'index'], axis=1, inplace=True)
# 分离出验证集,用于观察拟合情况
validation_data = train_data[:10000]
train_data = train_data[10000:]
validation_target = train_target[:10000]
train_target = train_target[10000:]
validation_data.shape, train_data.shape, validation_target.shape, train_target.shape
train_data.columns
# 定义网络结构
class Network(nn.Module):
    def __init__(self, in_dim, hidden_1, hidden_2, hidden_3, hidden_4):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(in_dim, hidden_1),
            nn.BatchNorm1d(hidden_1),
            nn.ReLU(),
            nn.Linear(hidden_1, hidden_2),
            nn.BatchNorm1d(hidden_2),
            nn.ReLU(),
            nn.Linear(hidden_2, hidden_3),
            nn.BatchNorm1d(hidden_3),
            nn.ReLU(),
            nn.Linear(hidden_3, hidden_4),
            nn.BatchNorm1d(hidden_4),
            nn.ReLU(),
            nn.Linear(hidden_4, 1)
        )
 
    def forward(self, x):
        y = self.layers(x)
        return y
# 定义网络
model = Network(train_data.shape[1], 256, 256, 256, 32)
model.to(config['device'])
 
# 使用Xavier初始化权重
for line in model.layers:
    if type(line) == nn.Linear:
        print(line)
        nn.init.xavier_uniform_(line.weight)
# 将数据转化为tensor,并移动到cpu或cuda上
 
train_features = torch.tensor(train_data.values, dtype=torch.float32, device=config['device'])
train_num = train_features.shape[0]
train_labels = torch.tensor(train_target.values, dtype=torch.float32, device=config['device'])
 
validation_features = torch.tensor(validation_data.values, dtype=torch.float32, device=config['device'])
validation_num = validation_features.shape[0]
validation_labels = torch.tensor(validation_target.values, dtype=torch.float32, device=config['device'])
# 特征长度
train_features[1].shape
# 定义损失函数和优化器
criterion = nn.MSELoss()
criterion.to(config['device'])
optimizer = optim.Adam(model.parameters(), lr=config['learning_rate'])
# 开始训练
 
mae_list = []
 
for epoch in range(config['epoch']):
    losses = []
    model.train()
    for i in range(0, train_num, config['batch_size']):
        end = i + config['batch_size']
        if i + config['batch_size'] > train_num-1:
            end = train_num-1
        mini_batch = train_features[i: end]
        mini_batch_label = train_labels[i: end]
        pred = model(mini_batch)
        pred = pred.squeeze()
        loss = criterion(pred, mini_batch_label)
 
        if torch.isnan(loss):
            break
        mae = torch.abs(mini_batch_label-pred).sum()/(end-i)
        losses.append(mae.item())
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    
    model.eval()
    pred = model(validation_features)
    validation_mae = torch.abs(validation_labels-pred.squeeze()).sum().item()/validation_num
    
    mae_list.append((sum(losses)/len(losses), validation_mae))
        
    print(f"epoch:{epoch + 1} MAE: {sum(losses)/len(losses)}, Validation_MAE: {validation_mae}")
    torch.save(model, 'model.pth')
x = np.arange(0, config['epoch'])
y1, y2 = zip(*mae_list)
plt.plot(x, y1, label='train')
plt.plot(x, y2, label='valid')
plt.legend()
plt.show()
 
#预测----------------------------------------------------------------------------------
import pandas as pd
import torch
from torch import nn
from settings import config
class Network(nn.Module):
    def __init__(self, in_dim, hidden_1, hidden_2, hidden_3, hidden_4):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(in_dim, hidden_1),
            nn.BatchNorm1d(hidden_1),
            nn.ReLU(),
            nn.Linear(hidden_1, hidden_2),
            nn.BatchNorm1d(hidden_2),
            nn.ReLU(),
            nn.Linear(hidden_2, hidden_3),
            nn.BatchNorm1d(hidden_3),
            nn.ReLU(),
            nn.Linear(hidden_3, hidden_4),
            nn.BatchNorm1d(hidden_4),
            nn.ReLU(),
            nn.Linear(hidden_4, 1)
        )
 
    def forward(self, x):
        y = self.layers(x)
        return y
model = torch.load('model.pth', map_location=config['device'])
data = pd.read_csv('./one_hot_testB.csv')
data.shape
data.columns
data = data.drop(columns=['Unnamed: 0', 'price'])
test = data.drop(columns='SaleID')
test.shape
test = torch.tensor(test.values, dtype=torch.float32)
pred = model(test)
price = pd.DataFrame(pred.detach().cpu().numpy(), columns=['price'])
res = pd.concat([data.SaleID, price], axis=1)
res.to_csv('output.csv')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值