24.8.15学习笔记(Titanic - Machine Learning from Disaster)

Titanic - Machine Learning from Disaster

导入库:

import pandas as pd  # 导入pandas库,用于数据处理和分析
import numpy as np  # 导入numpy库,用于数值计算

from sklearn.impute import SimpleImputer  # 从sklearn.impute模块导入SimpleImputer,用于处理缺失值
from sklearn.preprocessing import OneHotEncoder  # 从sklearn.preprocessing模块导入OneHotEncoder,用于类别特征的独热编码
from sklearn.compose import ColumnTransformer  # 从sklearn.compose模块导入ColumnTransformer,用于组合不同的特征变换
from sklearn.pipeline import Pipeline  # 从sklearn.pipeline模块导入Pipeline,用于构建数据处理流水线

import torch  # 导入PyTorch库
import torch.nn as nn  # 导入PyTorch的神经网络模块
import torch.optim as optim  # 导入PyTorch的优化器模块

from torch.utils.data import Dataset, DataLoader  # 从PyTorch导入Dataset和DataLoader,用于构建和加载数据

import matplotlib.pyplot as plt  # 导入matplotlib.pyplot用于绘制图形

标签详情:

# PassengerId: 乘客的唯一标识符。
# Survived: 目标变量,表示乘客是否在事故中幸存(1 表示幸存,0 表示未幸存)。
# Pclass: 乘客的舱位等级(1 表示头等舱,2 表示二等舱,3 表示三等舱)。
# Name: 乘客的姓名。
# Sex: 乘客的性别。
# Age: 乘客的年龄(有些是缺失值)。
# SibSp: 乘客在船上的兄弟姐妹/配偶的数量。
# Parch: 乘客在船上的父母/孩子的数量。
# Ticket: 乘客的票号。
# Fare: 乘客支付的票价(有些是缺失值)。
# Cabin: 乘客的舱位号(很多是缺失值)。
# Embarked: 乘客登船的港口(C = Cherbourg, Q = Queenstown, S = Southampton)。

数据预处理:

# 从指定路径加载训练数据集
train_data = pd.read_csv("D:/train.csv")  

# 从指定路径加载测试数据集
test_data = pd.read_csv("D:/test.csv")  

# 从训练数据中分离出目标变量'Survived',后续用于模型训练的真实标签
y_train = train_data['Survived']  

# 从训练数据中删除'Survived'列,因为该列已被单独分离,且后续的预处理不涉及此目标列
train_data = train_data.drop(columns=['Survived'])  

# 定义数值特征的列名列表,这些特征将进行特定的预处理
numeric_features = ['Age', 'SibSp', 'Parch', 'Fare']  

# 定义类别特征的列名列表,这些特征也将有相应的预处理操作
categorical_features = ['Sex', 'Embarked']  

# 为数值特征创建预处理管道
# 其中包含一个SimpleImputer步骤,使用中位数填充缺失值
numeric_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='median'))
])  

# 为类别特征创建预处理管道
# 首先使用众数填充缺失值,然后进行one-hot编码,并忽略未知类别
categorical_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='most_frequent')),
    ('onehot', OneHotEncoder(categories='auto', handle_unknown='ignore'))
])  

# 使用ColumnTransformer将数值特征和类别特征的预处理步骤组合起来
# 分别为数值特征和类别特征指定对应的预处理管道和特征列名
preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, numeric_features),
        ('cat', categorical_transformer, categorical_features)
    ])  

# 创建完整的预处理管道,第一步就是上述组合好的预处理步骤
preprocessing_pipeline = Pipeline(steps=[('preprocessor', preprocessor)])  

# 对训练数据应用预处理管道,进行特征处理和转换,并将结果存储在X_train中
X_train = preprocessing_pipeline.fit_transform(train_data)  

# 对测试数据应用预处理管道进行相同的处理和转换,结果存储在X_test中
X_test = preprocessing_pipeline.transform(test_data)  

# 从测试数据中分离出'PassengerId'列,可能用于后续结果的标识或其他用途
test_passenger_ids = test_data['PassengerId']  

 

  1. 加载数据:首先,我们从电脑的指定文件夹("D:/train.csv" 和 "D:/test.csv")中加载了两组数据,分别是训练数据和测试数据。

  2. 分离目标变量:接下来,我们从训练数据中找出我们最关心的一列数据,也就是乘客是否在泰坦尼克号沉船事件中存活('Survived'),我们把这一列单独拿出来,因为这是我们想要预测的结果。

  3. 删除目标列:因为我们已经把目标列单独拿出来了,所以在接下来的数据处理中就不再需要它了。我们把训练数据中的 'Survived' 列删除,只留下其他的特征列。

  4. 定义特征:我们告诉程序哪些列是数值类型的(比如年龄、兄弟姐妹/配偶的数量、票价等),哪些是类别类型的(比如性别、登船港口等)。

  5. 数值特征预处理:对于数值类型的列,如果有些数据缺失了,我们就用所有已知数据的中间值(中位数)来填补这些缺失的数据。

  6. 类别特征预处理:对于类别型的列,如果有些数据缺失了,我们就用出现次数最多的类别(众数)来填补。然后,我们把这些类别数据转换成一种计算机更容易处理的形式,这个过程叫做“one-hot 编码”。

  7. 组合预处理步骤:我们把数值型和类别型特征的处理步骤放在一起,形成一个完整的数据处理流程。

  8. 创建预处理管道:我们创建了一个“管道”,数据只要进入这个管道,就会按照我们设定的步骤被自动处理。

  9. 应用预处理:我们用这个管道来处理训练数据和测试数据。这样,所有的数据都会被清洗和转换,变成模型能够理解和处理的格式。

  10. 分离乘客ID:最后,我们从测试数据中把乘客的ID单独拿出来。虽然我们这里可能暂时用不到它,但有时候我们可能需要知道预测结果对应的是哪个乘客。

整个流程就像是准备食材做一道菜:我们先从冰箱里拿出我们要做的菜的所有原料(加载数据),然后挑出我们最关心的那个食材(分离目标变量),接着把不需要的部分扔掉(删除目标列)。然后我们把食材洗干净,切好(预处理),然后按照食谱(预处理管道)一步步加工食材,最后把食材准备好放在盘子里(应用预处理),等待烹饪(模型训练和预测)。

定义模型,数据集,优化器等等:

# 导入必要的库
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader

# 定义一个自定义的数据集类TitanicDataset,该类继承自torch.utils.data.Dataset
class TitanicDataset(Dataset):
    # 初始化方法,接收特征数据和标签数据作为输入
    def __init__(self, features, labels):
        self.features = features  # 特征数据,即乘客的各项特征
        self.labels = labels      # 标签数据,即乘客是否存活

    # 返回数据集中样本的数量
    def __len__(self):
        return len(self.features)

    # 根据索引idx获取单个样本的特征和标签
    def __getitem__(self, idx):
        feature = self.features[idx]  # 获取idx位置的特征数据
        label = self.labels[idx]      # 获取idx位置的标签数据
        return feature, label

# 定义泰坦尼克号预测模型TitanicModel,该模型继承自torch.nn.Module
class TitanicModel(nn.Module):
    # 初始化方法,接收输入特征的维度作为参数
    def __init__(self, input_dim):
        super(TitanicModel, self).__init__()  # 调用基类的初始化方法
        # 定义模型的层:三个全连接层
        self.fc1 = nn.Linear(input_dim, 64)  # 第一个全连接层,输入维度为input_dim,输出维度为64
        self.fc2 = nn.Linear(64, 32)        # 第二个全连接层,输入维度为64,输出维度为32
        self.fc3 = nn.Linear(32, 1)         # 第三个全连接层,输入维度为32,输出维度为1
        self.sigmoid = nn.Sigmoid()          # Sigmoid激活函数,用于二分类问题

    # 前向传播方法,定义数据通过模型的方式
    def forward(self, x):
        x = torch.relu(self.fc1(x))  # 数据通过第一个全连接层并应用ReLU激活函数
        x = torch.relu(self.fc2(x))  # 数据通过第二个全连接层并应用ReLU激活函数
        x = self.sigmoid(self.fc3(x))  # 数据通过第三个全连接层并应用Sigmoid激活函数
        return x

# 准备数据
# 假设X_train和y_train已经通过预处理得到,即特征和标签数据已经准备好

# 创建TitanicDataset数据集实例,将预处理后的特征和标签传递给数据集
dataset = TitanicDataset(X_train, y_train)

# 创建Dataloader实例,用于批量加载数据集,并在每个epoch中打乱数据
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)

# 初始化模型,将特征数据的维度作为参数传递给模型
input_dim = X_train.shape[1]  # 特征数据的维度
model = TitanicModel(input_dim)

# 定义损失函数,这里使用BCELoss,即二元交叉熵损失,适用于二分类问题
criterion = nn.BCELoss()

# 定义优化器,这里使用Adam优化算法,学习率为0.001
optimizer = optim.Adam(model.parameters(), lr=0.001)

这段代码主要完成了以下任务:

  • 创建了一个自定义的数据集类 TitanicDataset,用于封装数据的加载和访问方式。
  • 定义了一个神经网络模型 TitanicModel,包含三个全连接层,用于处理泰坦尼克号数据集的二分类任务。
  • 准备了数据加载器 Dataloader,用于在训练过程中按批次加载数据。
  • 初始化了模型实例,并定义了损失函数和优化器,为模型训练做好了准备。

开始训练:

# 训练模型
num_epochs = 200
# 创建列表用于存储损失值
loss_history = []

for epoch in range(num_epochs):
    running_loss = 0.0
    for i, (features, labels) in enumerate(dataloader): # 一批是32个,算一下总共迭代了多少批,就是i的数量
        # 转换为float类型
        features = features.float()
        labels = labels.float().unsqueeze(1)
        # 前向传播
        outputs = model(features)
        # 计算损失
        loss = criterion(outputs, labels)
        # 反向传播和优化
        optimizer.zero_grad()  # 清除梯度
        loss.backward()  # 反向传播
        optimizer.step()  # 更新权重
        running_loss += loss.item() # 每迭代一批都要把这批的损失值加入

        if (i + 1) % 20 == 0:
            print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}], Loss: {loss.item():.4f}')

        # 每个epoch结束时计算平均损失并存储
    epoch_loss = running_loss / (i + 1) # 这个epoch里所有批的损失值加起来除批次数,就是这个轮次的平均损失值
    loss_history.append(epoch_loss)
    print(f'Epoch [{epoch + 1}/{num_epochs}], Average Loss: {epoch_loss:.4f}')

 绘图:

# 绘制损失曲线
plt.figure(figsize=(10, 5))
# x轴的数据点,横坐标是轮次(epoch),若loss_history存的是每一个批次的损失值,那么横坐标就不够长(因为损失值的个数会有很多)
# 所以loss_history 应该存放每一个epoch的损失值,看的更直观一些
plt.plot(range(1, num_epochs + 1), loss_history, marker='o')
plt.title('Training Loss over Epochs')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.grid(True)
plt.show()

 测试函数:


# 使用torch.no_grad()上下文管理器,这将告诉PyTorch在代码块内不计算梯度。
# 这在进行预测时很有用,因为我们不需要更新模型的权重。
with torch.no_grad():
    # 创建测试数据集的TitanicDataset实例,将测试数据的特征和假设的标签列表传入。
    # 测试集没有真实标签,所以我们假设所有标签为0。
    test_dataset = TitanicDataset(X_test, [0]*len(X_test))

    # 创建测试数据的Dataloader实例,用于按批次加载测试数据。
    # 这里batch_size设置为32,且不打乱数据(shuffle=False),因为测试集不需要打乱。
    test_dataloader = DataLoader(test_dataset, batch_size=32, shuffle=False)

    # 初始化一个空列表来存储预测结果。
    predictions = []

    # 遍历测试数据加载器产生的数据批次。
    for features, _ in test_dataloader:
        # 将特征数据转换为浮点数张量,这是模型预测所需的数据类型。
        features = features.float()

        # 使用模型对当前批次的特征数据进行预测,得到模型的原始输出(未经过激活函数)。
        outputs = model(features)

        # 将模型输出与0.5比较,得到预测的标签(大于0.5为1,小于等于0.5为0)。
        # .int()将比较结果的布尔类型转换为整数,然后使用.flatten()将结果展平为一维,
        # 最后使用tolist()将结果转换为列表。
        predicted_labels = (outputs > 0.5).int().flatten().tolist()

        # 将预测得到的标签列表添加到predictions列表中。
        predictions.extend(predicted_labels)

# 使用pandas创建一个DataFrame,其中包含测试数据的乘客ID和我们的预测结果。
# 这个DataFrame将用于生成提交文件。
submission = pd.DataFrame({'PassengerId': test_passenger_ids, 'Survived': predictions})

# 将生成的DataFrame保存为CSV文件,index=False参数表示不保存行索引到文件中。
submission.to_csv('submission.csv', index=False)

这段代码主要完成了以下任务:

  • 使用 torch.no_grad() 确保在预测时不计算梯度,减少内存消耗并加快预测速度。
  • 创建了一个针对测试数据集的 TitanicDataset 实例和一个 DataLoader 实例。
  • 通过模型对测试数据集进行预测,并将预测结果汇总到一个列表中。
  • 将预测结果和乘客ID整理成一个 DataFrame,然后保存为CSV文件,这个CSV文件可以提交到Kaggle竞赛中。

全部代码:

import pandas as pd
import numpy as np
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
import matplotlib.pyplot as plt

# 加载训练和测试数据集
train_data = pd.read_csv("D:/train.csv")
test_data = pd.read_csv("D:/test.csv")

# 查看前几行数据
# print(train_data.head())
# # 查看数据统计摘要
# print(train_data.describe())
#
# # 查看缺失值情况
# print(train_data.isnull().sum())

# 定义预处理步骤
# PassengerId: 乘客的唯一标识符。
# Survived: 目标变量,表示乘客是否在事故中幸存(1 表示幸存,0 表示未幸存)。
# Pclass: 乘客的舱位等级(1 表示头等舱,2 表示二等舱,3 表示三等舱)。
# Name: 乘客的姓名。
# Sex: 乘客的性别。
# Age: 乘客的年龄(有些是缺失值)。
# SibSp: 乘客在船上的兄弟姐妹/配偶的数量。
# Parch: 乘客在船上的父母/孩子的数量。
# Ticket: 乘客的票号。
# Fare: 乘客支付的票价(有些是缺失值)。
# Cabin: 乘客的舱位号(很多是缺失值)。
# Embarked: 乘客登船的港口(C = Cherbourg, Q = Queenstown, S = Southampton)。

# 加载训练和测试数据集
train_data = pd.read_csv("D:/train.csv")
test_data = pd.read_csv("D:/test.csv")
# 分离目标变量
y_train = train_data['Survived']

# 删除'Survived'列,先删除多的那一列,然后再预处理
train_data = train_data.drop(columns=['Survived'])

# 定义预处理步骤
numeric_features = ['Age', 'SibSp', 'Parch', 'Fare']
categorical_features = ['Sex', 'Embarked']

# 数值特征预处理
numeric_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='median')),  # 使用中位数填充缺失值
])

# 类别特征预处理
categorical_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='most_frequent')),  # 使用众数填充缺失值
    ('onehot', OneHotEncoder(categories='auto', handle_unknown='ignore')),  # 进行one-hot编码,忽略未知类别
])

# 使用ColumnTransformer组合数值特征和类别特征的预处理步骤
preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, numeric_features),  # 数值特征预处理
        ('cat', categorical_transformer, categorical_features)  # 类别特征预处理
    ])

# 创建预处理管道
preprocessing_pipeline = Pipeline(steps=[('preprocessor', preprocessor)])

# 应用预处理
X_train = preprocessing_pipeline.fit_transform(train_data)
X_test = preprocessing_pipeline.transform(test_data)

# 分离PassengerId
test_passenger_ids = test_data['PassengerId']

# 定义数据集
class TitanicDataset(Dataset):
    def __init__(self, features, labels):
        self.features = features
        self.labels = labels

    def __len__(self):
        return len(self.features)

    def __getitem__(self, idx):
        feature = self.features[idx]
        label = self.labels[idx]
        return feature, label

# 定义模型
class TitanicModel(nn.Module):
    def __init__(self, input_dim):
        super(TitanicModel, self).__init__() # 3个全连接层
        self.fc1 = nn.Linear(input_dim, 64)
        self.fc2 = nn.Linear(64, 32)
        self.fc3 = nn.Linear(32, 1)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        x = self.sigmoid(self.fc3(x))
        return x
# 准备数据
# 假设X_train和y_train已经通过预处理得到
dataset = TitanicDataset(X_train, y_train)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)

# 初始化模型
input_dim = X_train.shape[1]
model = TitanicModel(input_dim)

# 定义损失函数和优化器
criterion = nn.BCELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 训练模型
num_epochs = 200
# 创建列表用于存储损失值
loss_history = []

for epoch in range(num_epochs):
    running_loss = 0.0
    for i, (features, labels) in enumerate(dataloader): # 一批是32个,算一下总共迭代了多少批,就是i的数量
        # 转换为float类型
        features = features.float()
        labels = labels.float().unsqueeze(1)
        # 前向传播
        outputs = model(features)
        # 计算损失
        loss = criterion(outputs, labels)
        # 反向传播和优化
        optimizer.zero_grad()  # 清除梯度
        loss.backward()  # 反向传播
        optimizer.step()  # 更新权重
        running_loss += loss.item() # 每迭代一批都要把这批的损失值加入

        if (i + 1) % 20 == 0:
            print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}], Loss: {loss.item():.4f}')

        # 每个epoch结束时计算平均损失并存储
    epoch_loss = running_loss / (i + 1) # 这个epoch里所有批的损失值加起来除批次数,就是这个轮次的平均损失值
    loss_history.append(epoch_loss)
    print(f'Epoch [{epoch + 1}/{num_epochs}], Average Loss: {epoch_loss:.4f}')

# 绘制损失曲线
plt.figure(figsize=(10, 5))
# x轴的数据点,横坐标是轮次(epoch),若loss_history存的是每一个批次的损失值,那么横坐标就不够长(因为损失值的个数会有很多)
# 所以loss_history 应该存放每一个epoch的损失值,看的更直观一些
plt.plot(range(1, num_epochs + 1), loss_history, marker='o')
plt.title('Training Loss over Epochs')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.grid(True)
plt.show()

with torch.no_grad():
    test_dataset = TitanicDataset(X_test, [0]*len(X_test))  # 假设标签都是0,因为测试集没有标签
    test_dataloader = DataLoader(test_dataset, batch_size=32, shuffle=False)

    predictions = []
    for features, _ in test_dataloader:
        features = features.float()  # 将特征转换为浮点数张量
        outputs = model(features)
        predicted_labels = (outputs > 0.5).int().flatten().tolist()
        predictions.extend(predicted_labels)

# 将预测结果整理成DataFrame
submission = pd.DataFrame({'PassengerId': test_passenger_ids, 'Survived': predictions})

# 保存为CSV文件
submission.to_csv('submission.csv', index=False)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值