一、实验任务与数据简介
-
任务:根据房屋的79个特征(如面积、地段、建筑年份等)预测房价(SalePrice)。
-
数据集:来自Kaggle的房价预测竞赛,包含训练集(train.csv)和测试集(test.csv)。
-
评价指标:通常使用均方根对数误差(RMSLE),但实验中我们使用均方误差(MSE)作为损失函数。
二、数据预处理流程(核心)
1. 标签预处理:取对数
-
对目标变量
SalePrice取对数:y_train = np.log(train_data['SalePrice']) -
原因:房价分布通常右偏(长尾),取对数使其接近正态分布,利于模型训练;且对数误差与相对误差相关,更合理。
2. 合并训练集和测试集
-
为统一进行特征工程,将训练集(不含标签)和测试集按行合并。
-
好处:对缺失值填充、编码等操作一致,避免测试集单独处理时出错。
3. 处理缺失值
-
数值型特征 :用中位数 填充(
median()),因为中位数对异常值更稳健。 -
类别型特征 :用字符串
'None'填充,表示缺失为一类。
4. 处理类别特征:独热编码(One-Hot Encoding)
-
使用
pd.get_dummies(data, dummy_na=True) -
作用:将类别变量转换为0/1数值,使模型能使用。
-
dummy_na=True:为缺失值也创建一个虚拟列,避免信息丢失。
5. 标准化(Z-score标准化)
-
公式:
X_std = (X - mean) / std -
注意 :用训练集的均值与标准差标准化训练集、验证集和测试集(不能重新计算测试集的统计量,否则分布不一致)。
-
标准化让所有特征在同一量级,加快梯度下降收敛。
三、数据集划分
-
训练集:用于训练模型参数。
-
验证集:从训练集中划分出一部分(如20%),用于调参和早停,不用于训练。
-
测试集:原始提供的 test.csv,用于最终提交(无标签)。
-
区别:验证集有标签,可计算损失评估模型;测试集无标签,仅供预测。
四、模型设计(MLP)
-
使用
nn.Module自定义模型,结构可配置:input_dim→ 多个隐藏层(如256→128→64)→ 输出层(1个神经元)。 -
每个隐藏层后跟:
-
激活函数(如
ReLU) -
Dropout层(随机丢弃一部分神经元,防止过拟合)
-
-
Dropout :
dropout_rate表示神经元被丢弃的概率。训练时启用,验证/测试时自动关闭(需model.eval())。
五、损失函数与优化器
-
损失函数 :回归任务用均方误差
nn.MSELoss()。 -
优化器 :Adam(自适应学习率),常用参数
lr(学习率)和weight_decay(L2正则化系数)。 -
weight_decay:对参数施加L2惩罚,使权重趋向小值,防止过拟合。
六、训练技巧
1. 学习率调度器 ReduceLROnPlateau
-
当验证损失连续
patience轮不再下降时,将学习率乘以factor。 -
作用:自动降低学习率,帮助模型跳出局部最优或平稳期。
2. 早停(Early Stopping)
-
当验证损失连续
patience轮没有下降时,停止训练,防止过拟合。 -
同时保存最佳模型(
best_val_loss对应的权重)。
3. 设备管理(device)
-
将模型和数据移动到 GPU(
cuda)或 CPU。 -
预测时,结果需用
.cpu()移回CPU才能转为NumPy。
七、模型评估与可视化
-
损失曲线:训练损失和验证损失随epoch变化。若验证损失上升而训练损失下降 → 过拟合;两者都高且不降 → 欠拟合。
-
真实值vs预测值散点图 :点应大致落在
y=x对角线附近,偏离说明预测偏差大。
八、预测与提交
-
加载最佳模型,对测试集进行预测,注意:
- 模型输出的是对数价格 ,需用
np.exp()转换回原始价格。
- 模型输出的是对数价格 ,需用
-
生成提交文件:
Id和SalePrice两列。
《人工智能概论》实验4 考试题
(总分100分,建议完成时间80分钟)
一、单选题(每题3分,共15分)
-
对房价标签
SalePrice取对数的原因是( )A. 使标签变为整数
B. 让标签分布更接近正态分布,利于模型训练
C. 减少特征数量
D. 方便可视化
-
填充数值特征的缺失值时,实验中使用的是( )
A. 均值
B. 众数
C. 中位数
D. 直接删除
-
以下哪个操作可以防止模型过拟合?( )
A. 增加学习率
B. 使用 Dropout
C. 减少训练数据
D. 不使用激活函数
-
标准化测试集时,应该使用( )
A. 测试集的均值和标准差
B. 训练集的均值和标准差
C. 验证集的均值和标准差
D. 全数据集的均值和标准差
-
在早停机制中,
patience参数的含义是( )A. 训练的总轮数
B. 学习率下降的等待轮数
C. 验证损失连续不下降的最大轮数,超过则停止训练
D. 每轮训练的批次数量
二、填空题(每空2分,共20分)
-
对类别特征进行独热编码应使用 pandas 的
pd.______()函数,参数dummy_na=True的作用是 ______。 -
模型定义中,Dropout层的参数
dropout_rate=0.2表示 ______。训练时 Dropout 生效,验证时需调用model.______()来禁用 Dropout。 -
学习率调度器
ReduceLROnPlateau中,mode='min'表示监控指标 ______ 时降低学习率,factor=0.5表示学习率 ______。 -
在训练循环中,梯度清零使用
optimizer.______(),参数更新使用optimizer.______()。 -
模型预测后,输出的
SalePrice是 ______(填"原始价格"或"对数价格"),需要用np.______()转换回原始价格。
三、判断题(正确打"√",错误打"×",每题2分,共10分)
-
( )训练集和测试集应该分别进行标准化,各自计算均值和标准差。
-
( )Dropout层在验证阶段也会随机丢弃神经元,以保持一致性。
-
( )早停机制中,当验证损失连续10轮不下降时,应停止训练并保存最佳模型。
-
( )独热编码会增加特征的维度。
-
( )均方误差(MSE)是分类问题的常用损失函数。
四、简答题(将原实验中的思考题整合,共35分)
简答题1(4分,来自实验一)
device 变量的作用是什么?如果电脑有GPU,device 会是什么?
简答题2(4分,来自实验二)
训练集和测试集分别有多少个样本?目标变量(预测值)是哪一列?
简答题3(4分,来自实验3.1)
为什么要对 SalePrice 取对数?如果不取对数会怎样?
简答题4(4分,来自实验3.3)
为什么需要对类别特征进行独热编码?dummy_na=True 的作用是什么?
简答题5(4分,来自实验3.5)
为什么要用训练集的均值和标准差来标准化测试集,而不是重新计算?
简答题6(4分,来自实验四)
验证集和测试集的区别是什么?为什么需要验证集?
简答题7(4分,来自实验四)
模型训练时,代码中计算的 train_loss 和 val_loss 分别是哪个集的结果?它们各自的作用是什么?
简答题8(4分,来自实验五)
Dropout层放在什么位置?它的参数 dropout_rate 代表什么?如果将 dropout_rate 从 0.2 改为 0.5,会发生什么?
简答题9(3分,来自实验五)
模型定义中,self.net = nn.Sequential(*layers) 的作用是什么?
五、代码填空题(每空2分,共20分)
请根据上下文填写正确的代码。
python
# 处理缺失值
numeric_cols = all_data.select_dtypes(include=[np.number]).columns
for col in numeric_cols:
if all_data[col].isnull().any():
median_val = all_data[col].______() # 空1
all_data[col].fillna(median_val, inplace=True)
# 独热编码
all_data = pd.______(all_data, dummy_na=True) # 空2
# 标准化
train_means = X_train.______() # 空3
train_stds = X_train.______() # 空4
# 创建 DataLoader
train_dataset = TensorDataset(______, ______) # 空5, 空6
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=______) # 空7
# 模型定义中的激活函数和 Dropout
layers.append(nn.______()) # 空8
layers.append(nn.______(dropout_rate)) # 空9
# 损失函数
criterion = nn.______() # 空10
参考答案与超详细解析
一、单选题
-
B
解析:房价分布通常右偏(长尾),取对数后接近正态分布,利于模型学习;同时预测对数价格再指数还原,得到的误差更接近相对误差,符合竞赛评价指标RMSLE。
-
C
解析:实验中使用中位数填充。中位数对异常值不敏感,比均值更稳健;尤其房价数据中可能存在极端值(豪宅),中位数更合适。
-
B
解析:Dropout随机丢弃神经元,迫使网络学习冗余表示,防止过拟合。增加学习率可能使训练不稳定;减少数据反而容易过拟合;无激活函数无法学习非线性。
-
B
解析:标准化必须使用训练集的统计量,因为测试集是模拟"未见数据",我们不知道其分布。若使用测试集的均值和标准差,相当于"偷看"了测试集,会破坏评估的公正性。
-
C
解析 :
patience表示验证损失连续不下降的轮数上限。当连续patience轮验证损失都没有改善时,停止训练,防止过拟合。
二、填空题
-
答案 :
get_dummies;为缺失值单独创建一个虚拟列解析 :
pd.get_dummies将类别转换为0/1列。dummy_na=True会对原本缺失的值生成一列,例如col_NaN标记为1,保留缺失信息。 -
答案 :每个神经元有20%的概率被丢弃(或随机失活概率0.2);
eval()解析 :
dropout_rate是神经元被暂时移除的概率。训练时启用,验证时需用model.eval()关闭Dropout,让所有神经元参与计算。 -
答案 :不再下降(或达到最小值);乘以0.5(即缩小一半)
解析 :
mode='min'表示监控指标(如验证损失)达到最小值时触发;factor=0.5表示将学习率乘以0.5,降低一半。 -
答案 :
zero_grad();step()解析 :标准三步:清零梯度(
zero_grad)→ 反向传播(backward)→ 更新参数(step)。 -
答案 :对数价格;
exp解析 :模型训练时对
SalePrice取了对数,所以输出的是对数价格。提交时需要还原为原始价格,用np.exp()。
三、判断题
-
×
解析:测试集必须使用训练集的均值和标准差,不能独立计算,否则导致分布不一致。
-
×
解析 :Dropout仅在训练阶段启用,验证和测试阶段自动关闭(需调用
model.eval()),否则会导致预测结果随机。 -
√
解析 :早停机制正是这样:连续
patience轮验证损失未改善则停止,并回滚到最佳模型。 -
√
解析:每个类别值会生成一个新列,因此特征维度增加(如性别有男、女,则变成两列)。
-
×
解析:MSE 是回归任务的损失函数,分类任务通常用交叉熵。
四、简答题(详细解析)
简答题1
作用 :device 变量指定模型和数据运行的硬件(CPU或GPU)。
有GPU时 :device = torch.device('cuda')。
解析 :GPU并行计算可大幅加速训练。代码中通过 torch.cuda.is_available() 自动判断,避免手动修改。
简答题2
训练集样本数 :根据 train_data.shape,一般为1460(Kaggle房价数据集)。
测试集样本数 :1459。
目标变量列 :'SalePrice'。
简答题3
取对数原因:
-
房价分布右偏,对数变换后接近正态分布,满足线性回归假设。
-
使用对数损失(RMSLE)对异常值不敏感,且预测误差与相对误差相关。
不取对数的后果:
-
模型可能受高价房影响大,低价房预测不准;
-
损失值巨大,训练不稳定;
-
提交的RMSLE得分会较差。
简答题4
独热编码原因 :机器学习模型只能处理数值,类别特征(如地段、材质)需要转换为0/1特征。
dummy_na=True :为缺失值单独生成一列(如 col_NaN),保留缺失信息,避免简单删除导致信息丢失。
简答题5
标准化测试集使用训练集统计量:
-
测试集模拟真实未见数据,我们不知道其均值和标准差。
-
若使用测试集自己的统计量,相当于引入了未来信息,评估结果不可靠。
-
保证训练和测试数据处于同一量纲,模型才能正确预测。
简答题6
验证集 :从训练集中划分,有标签,用于调参、早停、选择最佳模型。
测试集 :无标签(竞赛提供),用于最终评估或提交。
为什么需要验证集:防止模型在测试集上过拟合(不能反复使用测试集调参),验证集作为"模拟考试"来调整超参数。
简答题7
-
train_loss:训练集上的平均损失,反映模型拟合训练数据的能力。 -
val_loss:验证集上的平均损失,反映模型泛化能力。作用:比较两者可判断欠拟合(两者都高)、过拟合(train_loss低,val_loss高)或正常。
简答题8
Dropout位置 :通常放在每个隐藏层之后、激活函数之后(或之前均可,但常见于激活后)。
dropout_rate :每个神经元被随机丢弃的概率,0.2表示20%的神经元在每次前向传播时临时移除。
改为0.5 :更强的正则化,可能提升泛化能力,但若模型容量不足,可能导致欠拟合;训练速度变慢(因为有效参数减少)。
改为0:关闭Dropout,容易过拟合。
简答题9
nn.Sequential(*layers) 将 layers 列表中的所有层(Linear、ReLU、Dropout等)按顺序封装成一个序列网络。调用 forward 时自动依次执行。这样代码简洁,不需要手动写每层的前向传播。
五、代码填空题答案
空1 :median
空2 :get_dummies
空3 :mean
空4 :std
空5 :X_train_tensor
空6 :y_train_tensor
空7 :True
空8 :ReLU(或其他激活函数如 LeakyReLU,但实验使用ReLU)
空9 :Dropout
空10 :MSELoss