一.简介

        对于想要快速复习和入门深度学习的同学,我认为搭建一个清晰的流程图,然后实现一个简单的小项目是不错的选择。今天这篇文章就带大家实现一个简单的房价估计的案例,希望能对你未来的探索之旅有所帮助。

二.实现模型流程简单概述

        想要建立模型解决问题,我们不着急直接得出一个比较好的解决方案。我们需要先对当前的问题进行一个基本的分析,机器学习所能解决的问题比较常见的有两大类别,他们分别是分类任务,和回归任务。我们这次想解决的房价预测模型,是一个比较典型的回归任务。因为他预测的房价是一个连续的数值区间。对于解决回归任务,我们可以联想到一下几个比较常见的模型。

  • 线性回归 & 正则化版本(Ridge, Lasso)
    优点:可解释性强,可分析每个特征对房价的边际影响。
    挑战:只能捕捉线性关系,需要良好的特征工程。

  • 决策树 & 集成树模型(随机森林、GBDT、XGBoost、LightGBM)
    优点:自动处理非线性、交互作用,通常预测精度高,特征重要性清晰。
    挑战:可能过拟合,需调节参数,解释性比线性模型略差(但特征重要性可补救)。

  • K近邻回归(KNN)
    基于相似房源的均价预测,思路自然,但计算量大,对稀疏、高维数据敏感。

  • 神经网络/深度学习
    当有大量数据或非结构化特征(如房屋图片)时可能有用,否则树模型通常更实用。

  • 支持向量回归(SVR)
    适合小样本、高维场景,但不是房价预测的首选。                                                                                                                                                                                                                              看过这几个模型的概述之后相比大家对这几个算法的特点和适合解决的问题有了初步的了解,接下来我们看一下这次处理的数据集

        很明显这是一个拥有大量非结构化数据的一个问题,所以我们由此引出我们今天所要建立的模型就行简单的神经网络。选择模型之后我们要进行的步骤就要围绕一下四点进行模型的构建。他们分别是数据处理模型构建损失函数模型训练的验证

三.分布式完成模型的构建

1.特征工程

        不知道大家有没有听说过,人工智能算法专家其实也有个别的名字就是数据科学家。那么为什么说是数据呢,当然是跟我们在构建模型前的第一个步骤就是进行特征处理,

        因为模型的上限,很大程度上是由数据特征决定的,而不是模型本身。有一条流传很广的经验法则:

“数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限。”

        对我们当前问题所需要进行的特征工程主要是以下几点

        对特征进行处理,数值型特征使用均值填充缺失值,再标准化;类别型特征使用字符串“NaN”填充缺失值,再独特编码。之后构造数据集:

def create_dataset():
    """构造数据集"""
    # 读取数据
    data = pd.read_csv("data/house_prices.csv")
    # 去除无关特征
    data.drop(["Id"],axis=1,inplace=True)
    #划分特征和目标
    X = data.drop("SalePrice", axis=1)
    y = data["SalePrice"]

    # 筛选出数值型特征
    numerical_features = X.select_dtypes(exclude="object").columns
     # 筛选出类别型特征
    categorical_features = X.select_dtypes(include="object").columns
     # 划分训练集和测试集
    x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
    numerical_transformer = Pipeline(
        steps = [
            ("fillna", SimpleImputer(strategy="mean")),
            ("std", StandardScaler())
        ]
    )
    # 类别型特征先将缺失值替换为字符串"NaN",再进行独热编码
    categorical_transformer = Pipeline(
        steps = [
            ("fillna", SimpleImputer(strategy="constant", fill_value="NaN")),
            ("onehot", OneHotEncoder(handle_unknown="ignore")),
        ]
    )

    preprocessor = ColumnTransformer(
        transformers = [
            ("num", numerical_transformer, numerical_features),
            ("cat", categorical_transformer, categorical_features),
        ]
    )
     # 进行特征预处理
    x_train = pd.DataFrame(preprocessor.fit_transform(x_train).toarray(),columns=preprocessor.get_feature_names_out())
    x_test = pd.DataFrame(preprocessor.transform(x_test).toarray(), columns=preprocessor.get_feature_names_out())
     # 构建数据集
    train_dataset = TensorDataset(torch.tensor(x_train.values).float(), torch.tensor(y_train.values).float())
    test_dataset = TensorDataset(torch.tensor(x_test.values).float(), torch.tensor(y_test.values).float())
     # 返回训练集,测试集,特征数量
    return train_dataset, test_dataset, x_train.shape[1]

2.构建模型

神经网络 神经网络(Neural Network)有一个英文简称叫做NN,得益于torch开发框架我们在搭建神经网络的时候可以用torch包里的torch.nn的强大功能来简化网络的构建

# 搭建模型
model = nn.Sequential(
    #全连接层,根据输入数据提取128个特征
    nn.Linear(feature_num, 128),
    #批量标准化
    nn.BatchNorm1d(128),
    #激活函数使得神经网络具有非线性
    nn.ReLU(),
    #训练时以概率p随机关闭神经元,迫使网络不依赖特定神经元,增强鲁棒性
    nn.Dropout(0.2),
    #集和特征特点推测出房价
    nn.Linear(128, 1),
)

3.损失函数

        我们当想要对深度神经网络模型进行训练的时候,依赖的主要方法就是反向传播算法,他会通过向梯度方向不断调整模型参数的方式使得模型的运算结果更加逼近损失函数的最小值,因此选择合适的损失函数非常重要。

关于房价的预测我们更加关心相对误差而非绝对误差,比如房价原本20万元而误差10万元,那么误差可能难以接受;但若房价原本1000万元而误差为10万元,那误差可能并不算大。因此这里我们使用对数来衡量误差:

def log_rmse(pred, target):
    mse = nn.MSELoss()
    pred.squeeze_()
    pred = torch.clamp(pred, 1, float("inf"))  # 限制输出在1到正无穷之间
    return torch.sqrt(mse(torch.log(pred), torch.log(target)))

4.训练模型

得益于 PyTorch 框架,我们在具体实现训练过程的时候可以用 model.train() 切换模式、用 loss_value.backward() 自动求梯度、用 optimizer.step() 更新参数,再配合进度条和损失列表就能很直观地监控模型的学习情况。对于选择求解器和参数的初始化有多重方法,读者想要拓展可以去根据这两个方向进行相应的资料拓展,看看其他方法的特点和使用场景。

def train(model, train_dataset, test_dataset, lr,epoch_num, batch_size,device):
    def init_weight(layer):
        #对线性层进行权重初始化
        if  type(layer) == nn.Linear:
            nn.init.xavier_normal_(layer.weight)

    model.apply(init_weight)# 初始化参数
    model = model.to(device) # 将模型加载到设备中
    optimizer = torch.optim.Adam(model.parameters(), lr=lr) # 优化器

    train_loss_list = [] # 记录训练损失
    test_loss_list = [] # 记录验证损失
    for epoch in range(epoch_num):

        # 训练过程
        model.train()
        train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
        train_loss_accumulate = 0
        # 训练模型
        for batch_count,(X,y) in enumerate(train_loader):
            # 前向传播
            X,y = X.to(device),y.to(device)
            output = model(X)
            # 反向传播
            loss_value = log_rmse(output,y)
            optimizer.zero_grad()
            loss_value.backward()
            optimizer.step()
            # 累加损失
            train_loss_accumulate += loss_value.item()
            ## 打印进度条
            print(f"\repoch:{epoch:0>3}[{'='*(int((batch_count+1) / len(train_loader)* 50 )):<50}]", end="")
            this_train_loss = train_loss_accumulate / len(train_loader) # 计算平均损失
            train_loss_list.append(this_train_loss) # 记录训练损失

            # 验证过程
            model.eval() # 将模型设置为评估函数
            test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
            test_loss_accumulate = 0
            with torch.no_grad():
                for X,y in test_loader:
                    X,y = X.to(device),y.to(device)
                    output = model(X)
                    loss_value = log_rmse(output,y)
                    test_loss_accumulate += loss_value.item()
            this_test_loss = test_loss_accumulate / len(test_loader)# 计算机平均损失
            test_loss_list.append(this_test_loss)

            print(f" train_loss:{this_train_loss:.6f}, test_loss:{this_test_loss:.6f}")
        return train_loss_list, test_loss_list

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")# 如果cude可用则使用cuda,否则使用cpu
print(device)
train_loss_list, test_loss_list = train(model, train_dataset, test_dataset, 0.1, 5000, 64, device)
plt.plot(train_loss_list, "r-", label="train_loss", linewidth=3)# 绘制训练损失
plt.plot(test_loss_list, "k--", label="test_loss", linewidth=2)# 绘制验证损失
plt.legend()
plt.show()

              

四.总结

希望能通过这个简单的例子让你了解到构建神经网络的基本流程,人工智能的学习道路上是比较困难,但是希望你能保持探索的心,就像是pytorch中这些工具,他们只不过是一个函数而已,也是别人写的,和我们一样拥有一个脑袋,也许我现在不懂其中的奥妙,但是我总有一天会搞明白他们的原理,加油。

ps:笔者文章水平欠佳,有不妥的地方欢迎大家的批评与指正

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐