jenutka/titanic_php

一个示例项目,使用随机森林分类器预测泰坦尼克号乘客是否幸存,用于Kaggle比赛。

v2 2023-04-09 19:34 UTC

This package is auto-updated.

Last update: 2024-09-30 01:54:36 UTC


README

Titanic - Machine Learning from Disaster

内容

这是一个示例Rubix ML项目,使用随机森林分类器和来自Kaggle比赛的著名数据集,预测哪些乘客在泰坦尼克号沉船中幸存。在本教程中,您将了解分类和高级预处理技术。教程结束时,您将能够将您自己的预测提交到Kaggle比赛。

  • 难度:中等
  • 训练时间:分钟

来自Kaggle

这是著名的泰坦尼克号ML竞赛——您进入ML竞赛的最好、第一个挑战

比赛很简单:使用机器学习创建一个模型,预测哪些乘客在泰坦尼克号沉船中幸存。

在这个比赛中,您将能够访问两个类似的包含乘客信息(如姓名、年龄、性别、社会经济阶层等)的数据集。一个数据集名为train.csv,另一个名为test.csv。

train.csv将包含船上的部分乘客(确切地说是891人)的详细信息,并且重要的是,它将揭示他们是否幸存,也称为“地面真相”。

test.csv数据集包含类似的信息,但没有披露每个乘客的“地面真相”。您的任务是预测这些结果。

使用您在train.csv数据中找到的规律,预测船上其他418名乘客(在test.csv中找到)是否幸存。

安装

使用Composer在本地上传项目

$ composer create-project jenutka/titanic_php

要求

  • PHP 7.4或更高版本

推荐

  • 1G或更多系统内存

教程

介绍

Kaggle是一个平台,允许您通过参与比赛来测试您的数据科学技能。这是著名的泰坦尼克号ML竞赛——您进入ML竞赛的最好、第一个挑战,并熟悉Kaggle平台的工作方式。比赛很简单:使用机器学习创建一个模型,预测哪些乘客在泰坦尼克号沉船中幸存。泰坦尼克号的沉没是历史上最著名的沉船之一。1912年4月15日,在处女航中,被认为“永不沉没”的RMS泰坦尼克号在撞上冰山后沉没。不幸的是,船上没有足够的救生艇供每个人使用,导致1502名乘客和船员死亡,共2224人。虽然生存有一些运气因素,但似乎某些群体比其他人更有可能幸存。在这个挑战中,我们要求您构建一个预测模型,使用乘客数据(例如,姓名、年龄、性别、社会经济阶层等)来回答问题:“哪些类型的人更有可能幸存?”

我们将选择随机森林作为我们的学习者,因为它提供良好的性能,并且能够处理分类和连续特征。

注意:本例的源代码可以在项目的根目录下的 train.phppredict.php 文件中找到。

脚本描述

脚本分为两部分

  • train.php 用于从csv文件中提取训练数据,特征转换,训练和保存预测模型
  • predict.php 用于加载训练好的预测模型,并对未标记的数据集进行预测和导出

训练数据以train.csv的形式提供给我们,其中包含用于训练模型的特征和标签。我们从整个数据集训练模型,因为我们的测试数据test.csv是无标签的,所以在这种情况下,我们只能通过Kaggle竞赛来验证预测。

数据提取

train.csv中的每个特征都由列定义。为了我们的目的,我们只选择对模型最有信息的特征。这些是连续的和分类的。我们使用Column Pickertrain.csv提取到数据集对象。作为最后提取的特征,我们命名目标(标签)特征为Survived

use Rubix\ML\Extractors\CSV;
use Rubix\ML\Extractors\ColumnPicker;

$extractor = new ColumnPicker(new CSV('train.csv', true), [
    'Pclass', 'Age', 'Fare', 'SibSp', 'Parch', 'Sex', 'Embarked', 'Survived',
]);

预处理训练数据

由于*.csv文件中存在缺失值,我们需要对它们进行预处理以便与MissingDataImputer一起使用。为此,我们使用LambdaFunction,其中我们传递映射函数$toPlaceholder

use Rubix\ML\Transformers\LambdaFunction;

$toPlaceholder = function (&$sample, $offset, $types) {
    foreach ($sample as $column => &$value) {
        if (empty($value) && $types[$column]->isContinuous()) {
            $value = NAN;
        }
        else if (empty($value) && $types[$column]->isCategorical()) {
            $value = '?';
        }
    }
};

train.csv中的目标值是01。我们的训练模型可以将其作为浮点数处理,因此我们应该将它们映射为分类变量DeadSurvived

$transformLabel = function ($label) {
    return $label == 0 ? 'Dead' : 'Survived';
};

对于数值变量,我们使用MinMaxNormalize进行数据转换。对于分类变量,我们使用OneHotEncoder。对于这两个转换器和MissingDataImputer,我们实例化新的对象。

use Rubix\ML\Transformers\MinMaxNormalizer;
use Rubix\ML\Transformers\OneHotEncoder;
use Rubix\ML\Transformers\MissingDataImputer;

$minMaxNormalizer = new MinMaxNormalizer();
$oneHotEncoder = new OneHotEncoder();
$imputer = new MissingDataImputer();

最后,我们创建一个Labeled数据集,并使用我们的预处理函数进行拟合。

use Rubix\ML\Datasets\Labeled;

$dataset = Labeled::fromIterator($extractor)
    ->apply(new NumericStringConverter())
    ->transformLabels($transformLabel);

$dataset->apply(new LambdaFunction($toPlaceholder, $dataset->types()))
    ->apply($imputer)
    ->apply($minMaxNormalizer)
    ->apply($oneHotEncoder);

保存转换器

现在,因为我们想将相同的拟合预处理应用于测试数据集test.csv,而预测部分将通过独立的脚本predict.php实现,因此我们需要将我们的拟合转换器保存为序列化对象。为此,我们使用Filesystem对象创建新的,并使用RBX文件格式。

use Rubix\ML\Persisters\Filesystem;
use Rubix\ML\Serializers\RBX;

$serializer->serialize($imputer)->saveTo(new Filesystem('imputer.rbx'));
$serializer->serialize($minMaxNormalizer)->saveTo(new Filesystem('minmax.rbx'));
$serializer->serialize($oneHotEncoder)->saveTo(new Filesystem('onehot.rbx'));

模型训练

在准备我们的数据之后,我们可以训练我们的预测模型。作为估计器,我们使用RandomForest,这是一个ClassificationTrees的集成,非常适合我们相对较小的数据集。

use Rubix\ML\Classifiers\RandomForest;
use Rubix\ML\Classifiers\ClassificationTree;

$estimator = new RandomForest(new ClassificationTree(10), 500, 0.8, false);

$estimator->train($dataset);

保存估计器

最后,我们将我们的预测模型保存下来,以便与 predict.php 脚本一起使用。与变压器的情况一样,我们再次使用 文件系统 对象,并使用 RBX 文件格式。但现在,我们不再进行序列化,而是使用 PersistentModel 对象来保存预测模型。为了确保不覆盖现有模型,我们要求用户保存新训练的模型。

use Rubix\ML\PersistentModel;

if (strtolower(readline('Save this model? (y|[n]): ')) === 'y') {
    $estimator = new PersistentModel($estimator, new Filesystem('model.rbx'));

    $estimator->save();

    $logger->info('Model saved as model.rbx');
}

现在我们已经完成了 train.php 的训练部分,我们通过在命令行中调用它来执行它。

$ php train.php

现在我们可以继续创建预测部分 predict.php

提取测试数据

对于预测部分,我们需要提取我们的测试数据,这些数据不包含标签。通过提取,我们命名与训练集相同的特征,但省略了目标 Survived

use Rubix\ML\Extractors\ColumnPicker;

$extractor = new ColumnPicker(new CSV('test.csv', true), [
    'Pclass', 'Age', 'Fare', 'SibSp', 'Parch', 'Sex', 'Embarked',
]);

加载转换器

为了转换我们的测试数据集,我们需要使用在训练数据集上拟合的转换。因此,我们加载并反序列化之前保存的持久化器。

$persister_imputer = new Filesystem('imputer.rbx', true, new RBX());

$imputer = $persister_imputer->load()->deserializeWith(new RBX);

$persister_minMax = new Filesystem('minmax.rbx', true, new RBX());

$minMaxNormalizer = $persister_minMax->load()->deserializeWith(new RBX);

$persister_oneHot = new Filesystem('onehot.rbx', true, new RBX());

$oneHotEncoder = $persister_oneHot->load()->deserializeWith(new RBX);

预处理测试数据

对于测试数据,我们需要创建一个新的 Unlabeled 数据集对象,我们将 $extractor 传递给它。由于我们已经加载了拟合的转换器,我们可以将它们应用于此数据集对象。与训练数据一样,我们使用 $toPlaceholder 函数将缺失值映射到,以便 MissingDataImputer 可以处理缺失值。

use Rubix\ML\Datasets\Unlabeled;
use Rubix\ML\Transformers\LambdaFunction;

$dataset = Unlabeled::fromIterator($extractor)
    ->apply(new NumericStringConverter());

$dataset->apply(new LambdaFunction($toPlaceholder, $dataset->types()))
    ->apply($imputer)
    ->apply($minMaxNormalizer)
    ->apply($oneHotEncoder);

加载估计器

现在,我们可以使用静态 load() 方法将持久化的 RandomForest 估算器加载到我们的脚本中。

use Rubix\ML\PersistentModel;
use Rubix\ML\Persisters\Filesystem;
use Rubix\ML\Serializers\RBX;

$estimator = PersistentModel::load(new Filesystem('model.rbx'));

进行预测

为了在测试未标记数据集上进行预测,我们在加载的估算器上调用 predict() 方法。我们将预测类别存储在 $predictions 变量中。

$predictions = $estimator->predict($dataset);

保存预测结果

现在,我们需要将存储的预测准备成所需的格式,以便我们可以将其提交到 Kaggle 竞赛。

首先,我们将标签映射回 10。为此,我们创建了一个名为 bin_mapper 的函数,并将其作为参数传递给内置的 php 函数 array_map

$predictions = $estimator->predict($dataset);

function bin_mapper($v)
{
    if ($v==="Survived") {
        return "1";
    } else {
        return "0";
    }
}

$predictions_mapped = array_map('bin_mapper', $predictions);

现在,我们从 test.csv 中提取 PassengerId 列。现在我们创建一个名为 $ids 的数组,用于 PassengerId 列。我们使用 array_unshift 函数对两个列都进行操作。接下来,我们实例化 CSV 文件 predictions.csv,并最终使用 array_transpose 函数将两个列的数据导出到其中。

$extractor = new ColumnPicker(new CSV('test.csv', true), ['PassengerId']);

$ids = array_column(iterator_to_array($extractor), 'PassengerId');

array_unshift($ids, 'PassengerId');
array_unshift($predictions_mapped, 'Survived');

$extractor = new CSV('predictions.csv');

$extractor->export(array_transpose([$ids, $predictions_mapped]));

现在,我们可以通过在命令行中调用它来运行我们的预测脚本。

$ php predict.php

成功生成 predictions.csv 文件后,我们可以将其提交到我们的 [Kaggle 竞赛] (https://www.kaggle.com/competitions/titanic),并查看我们在公共排行榜上的结果。

结论

本教程描述了使用 RubixML php 库进行机器学习预测的整个过程。我们可以将此示例作为其他改进的起点。例如,我们可以应用高级特征工程以获得更多用于训练模型的信息。接下来,我们检查预测模型本身。我们可以尝试调整模型的给定超参数,或者我们可以证明其他分类器(例如 支持向量机 或神经网络)的性能。

作为下一项活动,我们可以尝试将我们的预测模型部署到我们的网页或服务器上,访客可以在填写包含我们特征的表单后,了解其在泰坦尼克号登船时的状况。

此示例还可以作为工作流程的模板,适用于其他机器学习问题。

许可证

代码采用MIT许可证,教程采用CC BY-NC 4.0许可证