查看原文
其他

构建你的数据科学作品集:机器学习项目(下)

2016-10-28 译者:k/z/v/c/i Linux中国
本文导航
  • -一个端到端的项目03%

  • -找到好的数据集08%

  • -选择一个角度16%

  • -理解数据18%

  • -构造项目24%

  • -创建初始文件27%

  • -获取数据34%

  • -读入数据36%

  • -计算来自执行数据的值52%

  • -获取值62%

  • -转换数据64%

  • -聚合到一起71%

  • -找到误差标准74%

  • -设置机器学习分类器78%

  • -做出预测83%

  • -预测误差86%

  • -聚合到一起88%

  • -撰写 README92%

  • -下一步93%

这是[1]发布的第三篇关于如何构建数据科学作品集Data Science Portfolio的文章。如果你喜欢这个系列并且想继续关注,你可以在订阅页面的底部找到[2]

(接上篇)

计算来自执行数据的值

接下来我们会计算来自 processed/Performance.txt 中的值。我们要做的就是推测这些资产是否被取消赎回权。如果能够计算出来,我们只要看一下关联到贷款的执行数据的参数 foreclosure_date就可以了。如果这个参数的值是 None,那么这些资产肯定没有收回。为了避免我们的样例中只有少量的执行数据,我们会为每个贷款计算出执行数据文件中的行数。这样我们就能够从我们的训练数据中筛选出贷款数据,排除了一些执行数据。

下面是我认为贷款数据和执行数据的关系:

在上面的表格中,贷款数据中的每一行数据都关联到执行数据中的多行数据。在执行数据中,在取消赎回权的时候 foreclosure_date 就会出现在该季度,而之前它是空的。一些贷款还没有被取消赎回权,所以与执行数据中的贷款数据有关的行在 foreclosure_date 列都是空格。

我们需要计算 foreclosure_status 的值,它的值是布尔类型,可以表示一个特殊的贷款数据 id 是否被取消赎回权过,还有一个参数 performance_count ,它记录了执行数据中每个贷款 id 出现的行数。 

计算这些行数有多种不同的方法:

  • 我们能够读取所有的执行数据,然后我们用 Pandas 的 [34] 方法在 DataFrame 中计算出与每个贷款 id 有关的行的行数,然后就可以查看贷款 id 的 foreclosure_date 值是否为 None 。

    • 这种方法的优点是从语法上来说容易执行。

    • 它的缺点需要读取所有的 129236094 行数据,这样就会占用大量内存,并且运行起来极慢。

  • 我们可以读取所有的执行数据,然后在贷款 DataFrame 上使用 [35] 去计算每个贷款 id 出现的次数。

    • 这种方法的优点是容易理解。

    • 缺点是需要读取所有的 129236094 行数据。这样会占用大量内存,并且运行起来极慢。

  • 我们可以在迭代访问执行数据中的每一行数据,而且会建立一个单独的计数字典。

    • 这种方法的优点是数据不需要被加载到内存中,所以运行起来会很快且不需要占用内存。

    • 缺点是这样的话理解和执行上可能有点耗费时间,我们需要对每一行数据进行语法分析。

加载所有的数据会非常耗费内存,所以我们采用第三种方法。我们要做的就是迭代执行数据中的每一行数据,然后为每一个贷款 id 在字典中保留一个计数。在这个字典中,我们会计算出贷款 id 在执行数据中出现的次数,而且看看 foreclosure_date 是否是 None 。我们可以查看 foreclosure_status 和 performance_count 的值 。

我们会新建一个 annotate.py 文件,文件中的代码可以计算这些值。我们会使用下面的代码:

  • 导入需要的库

  • 定义一个函数 count_performance_rows 。

    • 如果不存在,把它加进去。

    • 打开 processed/Performance.txt 文件。这不是在内存中读取文件而是打开了一个文件标识符,这个标识符可以用来以行为单位读取文件。 

    • 迭代文件的每一行数据。

    • 使用分隔符|分开每行的不同数据。

    • 检查 loan_id 是否在计数字典中。

    • loan_id 的 performance_count 参数自增 1 次,因为我们这次迭代也包含其中。

    • 如果 date 不是 None ,我们就会知道贷款被取消赎回权了,然后为foreclosure_status` 设置合适的值。

  1. import os

  2. import settings

  3. import pandas as pd

  4. def count_performance_rows():

  5.    counts = {}

  6.    with open(os.path.join(settings.PROCESSED_DIR, "Performance.txt"), 'r') as f:

  7.        for i, line in enumerate(f):

  8.            if i == 0:

  9.                # Skip header row

  10.                continue

  11.            loan_id, date = line.split("|")

  12.            loan_id = int(loan_id)

  13.            if loan_id not in counts:

  14.                counts[loan_id] = {

  15.                    "foreclosure_status": False,

  16.                    "performance_count": 0

  17.                }

  18.            counts[loan_id]["performance_count"] += 1

  19.            if len(date.strip()) > 0:

  20.                counts[loan_id]["foreclosure_status"] = True

  21.    return counts

获取值

只要我们创建了计数字典,我们就可以使用一个函数通过一个 loan_id 和一个 key 从字典中提取到需要的参数的值:

  1. def get_performance_summary_value(loan_id, key, counts):

  2.    value = counts.get(loan_id, {

  3.        "foreclosure_status": False,

  4.        "performance_count": 0

  5.    })

  6.    return value[key]

上面的函数会从 counts 字典中返回合适的值,我们也能够为贷款数据中的每一行赋一个 foreclosure_status 值和一个 performance_count 值。如果键不存在,字典的 [36] 方法会返回一个默认值,所以在字典中不存在键的时候我们就可以得到一个可知的默认值。

转换数据

我们已经在 annotate.py 中添加了一些功能,现在我们来看一看数据文件。我们需要将贷款到的数据转换到训练数据集来进行机器学习算法的训练。这涉及到以下几件事情:

  • 转换所有列为数字。

  • 填充缺失值。

  • 为每一行分配 performance_count 和 foreclosure_status

  • 移除出现执行数据很少的行(performance_count 计数低)。

我们有几个列是文本类型的,看起来对于机器学习算法来说并不是很有用。然而,它们实际上是分类变量,其中有很多不同的类别代码,例如 RS 等等. 我们可以把这些类别标签转换为数值:

通过这种方法转换的列我们可以应用到机器学习算法中。

还有一些包含日期的列 (first_payment_date 和 origination_date)。我们可以将这些日期放到两个列中:

在下面的代码中,我们将转换贷款数据。我们将定义一个函数如下:

  • 在 acquisition 中创建 foreclosure_status 列,并从 counts 字典中得到值。

  • 在 acquisition 中创建 performance_count 列,并从 counts 字典中得到值。

  • 将下面的列从字符串转换为整数:

    • channel

    • seller

    • first_time_homebuyer

    • loan_purpose

    • property_type

    • occupancy_status

    • property_state

    • product_type

  • 将 first_payment_date 和 origination_date 分别转换为两列:

    • 通过斜杠分离列。

    • 将第一部分分离成 month 列。

    • 将第二部分分离成 year 列。

    • 删除该列。

    • 最后,我们得到 first_payment_monthfirst_payment_yearrigination_month 和 origination_year

  • 所有缺失值填充为 -1

  1. def annotate(acquisition, counts):

  2.    acquisition["foreclosure_status"] = acquisition["id"].apply(lambda x: get_performance_summary_value(x, "foreclosure_status", counts))

  3.    acquisition["performance_count"] = acquisition["id"].apply(lambda x: get_performance_summary_value(x, "performance_count", counts))

  4.    for column in [

  5.        "channel",

  6.        "seller",

  7.        "first_time_homebuyer",

  8.        "loan_purpose",

  9.        "property_type",

  10.        "occupancy_status",

  11.        "property_state",

  12.        "product_type"

  13.    ]:

  14.        acquisition[column] = acquisition[column].astype('category').cat.codes

  15.    for start in ["first_payment", "origination"]:

  16.        column = "{}_date".format(start)

  17.        acquisition["{}_year".format(start)] = pd.to_numeric(acquisition[column].str.split('/').str.get(1))

  18.        acquisition["{}_month".format(start)] = pd.to_numeric(acquisition[column].str.split('/').str.get(0))

  19.        del acquisition[column]

  20.    acquisition = acquisition.fillna(-1)

  21.    acquisition = acquisition[acquisition["performance_count"] > settings.MINIMUM_TRACKING_QUARTERS]

  22.    return acquisition

聚合到一起

我们差不多准备就绪了,我们只需要再在 annotate.py 添加一点点代码。在下面代码中,我们将:

  • 定义一个函数来读取贷款的数据。

  • 定义一个函数来写入处理过的数据到 processed/train.csv

  • 如果该文件在命令行以 python annotate.py 的方式运行:

    • 读取贷款数据。

    • 计算执行数据的计数,并将其赋予 counts

    • 转换 acquisition DataFrame。

    • acquisition DataFrame 写入到 train.csv

  1. def read():

  2.    acquisition = pd.read_csv(os.path.join(settings.PROCESSED_DIR, "Acquisition.txt"), sep="|")

  3.    return acquisition

  4. def write(acquisition):

  5.    acquisition.to_csv(os.path.join(settings.PROCESSED_DIR, "train.csv"), index=False)

  6. if __name__ == "__main__":

  7.    acquisition = read()

  8.    counts = count_performance_rows()

  9.    acquisition = annotate(acquisition, counts)

  10.    write(acquisition)

修改完成以后,确保运行 python annotate.py 来生成 train.csv 文件。 你可以在[37]找到完整的 annotate.py 文件。

现在文件夹看起来应该像这样:

  1. loan-prediction

  2. ├── data

  3.   ├── Acquisition_2012Q1.txt

  4.   ├── Acquisition_2012Q2.txt

  5.   ├── Performance_2012Q1.txt

  6.   ├── Performance_2012Q2.txt

  7.   └── ...

  8. ├── processed

  9.   ├── Acquisition.txt

  10.   ├── Performance.txt

  11.   ├── train.csv

  12. ├── .gitignore

  13. ├── annotate.py

  14. ├── assemble.py

  15. ├── README.md

  16. ├── requirements.txt

  17. ├── settings.py

找到误差标准

我们已经完成了训练数据表的生成,现在我们需要最后一步,生成预测。我们需要找到误差的标准,以及该如何评估我们的数据。在这种情况下,因为有很多的贷款没有被取消赎回权,所以根本不可能做到精确的计算。

我们需要读取训练数据,并且计算 foreclosure_status 列的计数,我们将得到如下信息:

  1. import pandas as pd

  2. import settings

  3. train = pd.read_csv(os.path.join(settings.PROCESSED_DIR, "train.csv"))

  4. train["foreclosure_status"].value_counts()

  1. False    4635982

  2. True        1585

  3. Name: foreclosure_status, dtype: int64

因为只有很少的贷款被取消赎回权,只需要检查正确预测的标签的百分比就意味着我们可以创建一个机器学习模型,来为每一行预测 False,并能取得很高的精确度。相反,我们想要使用一个度量来考虑分类的不平衡,确保我们可以准确预测。我们要避免太多的误报率(预测贷款被取消赎回权,但是实际没有),也要避免太多的漏报率(预测贷款没有别取消赎回权,但是实际被取消了)。对于这两个来说,漏报率对于房利美来说成本更高,因为他们买的贷款可能是他们无法收回投资的贷款。

所以我们将定义一个漏报率,就是模型预测没有取消赎回权但是实际上取消了,这个数除以总的取消赎回权数。这是“缺失的”实际取消赎回权百分比的模型。下面看这个图表:

通过上面的图表,有 1 个贷款预测不会取消赎回权,但是实际上取消了。如果我们将这个数除以实际取消赎回权的总数 2,我们将得到漏报率 50%。 我们将使用这个误差标准,因此我们可以评估一下模型的行为。

设置机器学习分类器

我们使用交叉验证预测。通过交叉验证法,我们将数据分为3组。按照下面的方法来做:

  • 用组 1 和组 2 训练模型,然后用该模型来预测组 3

  • 用组 1 和组 3 训练模型,然后用该模型来预测组 2

  • 用组 2 和组 3 训练模型,然后用该模型来预测组 1

将它们分割到不同的组,这意味着我们永远不会用相同的数据来为其预测训练模型。这样就避免了过拟合。如果过拟合,我们将错误地拉低了漏报率,这使得我们难以改进算法或者应用到现实生活中。

[38] 有一个叫做 [39] ,它可以帮助我们理解交叉算法.

我们还需要一种算法来帮我们预测。我们还需要一个分类器来做[40]。目标变量 foreclosure_status 只有两个值, True 和 False

这里我们用 [41],因为它能很好的进行二元分类,并且运行很快,占用内存很小。我们来说一下它是如何工作的:不使用像随机森林一样多树结构,也不像支持向量机一样做复杂的转换,逻辑回归算法涉及更少的步骤和更少的矩阵。

我们可以使用 scikit-learn 实现的[42]算法。我们唯一需要注意的是每个类的权重。如果我们使用等权重的类,算法将会预测每行都为 false,因为它总是试图最小化误差。不管怎样,我们重视有多少贷款要被取消赎回权而不是有多少不能被取消。因此,我们给[43]类的 class_weight关键字传递 balanced 参数,让算法可以为不同 counts 的每个类考虑不同的取消赎回权的权重。这将使我们的算法不会为每一行都预测 false,而是两个类的误差水平一致。

做出预测

既然完成了前期准备,我们可以开始准备做出预测了。我将创建一个名为 predict.py 的新文件,它会使用我们在最后一步创建的 train.csv 文件。下面的代码:

  • 导入所需的库

  • 创建一个名为 cross_validate 的函数:

    • 使用正确的关键词参数创建逻辑回归分类器

    • 创建用于训练模型的数据列的列表,移除 id 和 foreclosure_status 列

    • 交叉验证 train DataFrame

    • 返回预测结果

  1. import os

  2. import settings

  3. import pandas as pd

  4. from sklearn import cross_validation

  5. from sklearn.linear_model import LogisticRegression

  6. from sklearn import metrics

  7. def cross_validate(train):

  8.    clf = LogisticRegression(random_state=1, class_weight="balanced")

  9.    predictors = train.columns.tolist()

  10.    predictors = [p for p in predictors if p not in settings.NON_PREDICTORS]

  11.    predictions = cross_validation.cross_val_predict(clf, train[predictors], train[settings.TARGET], cv=settings.CV_FOLDS)

  12.    return predictions

预测误差

现在,我们仅仅需要写一些函数来计算误差。下面的代码:

  • 创建函数 compute_error

    • 使用 scikit-learn 计算一个简单的精确分数(与实际 foreclosure_status 值匹配的预测百分比)

  • 创建函数 compute_false_negatives

    • 找到原本应被预测模型取消赎回权,但实际没有取消的贷款数目

    • 除以没被取消赎回权的贷款总数目

    • 为了方便,将目标和预测结果合并到一个 DataFrame

    • 查找漏报率

  1. def compute_error(target, predictions):

  2.    return metrics.accuracy_score(target, predictions)

  3. def compute_false_negatives(target, predictions):

  4.    df = pd.DataFrame({"target": target, "predictions": predictions})

  5.    return df[(df["target"] == 1) & (df["predictions"] == 0)].shape[0] / (df[(df["target"] == 1)].shape[0] + 1)

  6. def compute_false_positives(target, predictions):

  7.    df = pd.DataFrame({"target": target, "predictions": predictions})

  8.    return df[(df["target"] == 0) & (df["predictions"] == 1)].shape[0] / (df[(df["target"] == 0)].shape[0] + 1)

聚合到一起

现在,我们可以把函数都放在 predict.py。下面的代码:

  • 读取数据集

  • 计算交叉验证预测

  • 计算上面的 3 个误差

  • 打印误差

  1. def read():

  2.    train = pd.read_csv(os.path.join(settings.PROCESSED_DIR, "train.csv"))

  3.    return train

  4. if __name__ == "__main__":

  5.    train = read()

  6.    predictions = cross_validate(train)

  7.    error = compute_error(train[settings.TARGET], predictions)

  8.    fn = compute_false_negatives(train[settings.TARGET], predictions)

  9.    fp = compute_false_positives(train[settings.TARGET], predictions)

  10.    print("Accuracy Score: {}".format(error))

  11.    print("False Negatives: {}".format(fn))

  12.    print("False Positives: {}".format(fp))

一旦你添加完代码,你可以运行 python predict.py 来产生预测结果。运行结果向我们展示漏报率为 .26,这意味着我们没能预测 26% 的取消贷款。这是一个好的开始,但仍有很多改善的地方!

你可以在[44]找到完整的 predict.py 文件。

你的文件树现在看起来像下面这样:

  1. loan-prediction

  2. ├── data

  3.   ├── Acquisition_2012Q1.txt

  4.   ├── Acquisition_2012Q2.txt

  5.   ├── Performance_2012Q1.txt

  6.   ├── Performance_2012Q2.txt

  7.   └── ...

  8. ├── processed

  9.   ├── Acquisition.txt

  10.   ├── Performance.txt

  11.   ├── train.csv

  12. ├── .gitignore

  13. ├── annotate.py

  14. ├── assemble.py

  15. ├── predict.py

  16. ├── README.md

  17. ├── requirements.txt

  18. ├── settings.py

撰写 README

既然我们完成了端到端的项目,那么我们可以撰写 README.md 文件了,这样其他人便可以知道我们做的事,以及如何复制它。一个项目典型的 README.md 应该包括这些部分:

  • 一个高水准的项目概览,并介绍项目目的

  • 任何必需的数据和材料的下载地址

  • 安装命令

    • 如何安装要求依赖

  • 使用命令

    • 如何运行项目

    • 每一步之后会看到的结果

  • 如何为这个项目作贡献

    • 扩展项目的下一步计划

[45] 是这个项目的一个 README.md 样例。

下一步

恭喜你完成了端到端的机器学习项目!你可以在[46]找到一个完整的示例项目。一旦你完成了项目,把它上传到 [47] 是一个不错的主意,这样其他人也可以看到你的文件夹的部分项目。

这里仍有一些留待探索数据的角度。总的来说,我们可以把它们分割为 3 类: 扩展这个项目并使它更加精确,发现其他可以预测的列,并探索数据。这是其中一些想法:

  • 在 annotate.py 中生成更多的特性

  • 切换 predict.py 中的算法

  • 尝试使用比我们发表在这里的更多的房利美数据

  • 添加对未来数据进行预测的方法。如果我们添加更多数据,我们所写的代码仍然可以起作用,这样我们可以添加更多过去和未来的数据。

  • 尝试看看是否你能预测一个银行原本是否应该发放贷款(相对地,房利美是否应该获得贷款)

    • 当房利美购买贷款时,一些列是已知的,但之前是不知道的

    • 移除 train 中银行在发放贷款时间的不知道的任何列

    • 做出预测

  • 探索是否你可以预测除了 foreclosure_status 的其他列

    • 你可以预测在销售时资产值是多少?

  • 探索探索执行数据更新之间的细微差别

    • 你能否预测借款人会逾期还款多少次?

    • 你能否标出的典型贷款周期?

  • 将数据按州或邮政编码标出

    • 你看到一些有趣的模式了吗?

如果你建立了任何有趣的东西,请在评论中让我们知道!

如果你喜欢这篇文章,或许你也会喜欢阅读“构建你的数据科学作品集”系列的其他文章:

  • [48]

  • [49]

  • [50]

  • [51]


via: 

作者:[52] 译者: 校对:

本文由 [53] 原创编译, 荣誉推出



推荐文章

滑动查看更多

输入文章 ID 或长按二维码直达





您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存