UdaCity-机器学习工程师-项目2:为CharityML寻找捐献者-深蓝源码网


时间: 2020-09-03 00:08:26 人气: 2289 评论: 0

机器学习纳米学位

监督学习

项目2: 为CharityML寻找捐献者

欢迎来到机器学习工程师纳米学位的第二个项目!在此文件中,有些示例代码已经提供给你,但你还需要实现更多的功能让项目成功运行。除非有明确要求,你无须修改任何已给出的代码。以'练习'开始的标题表示接下来的代码部分中有你必须要实现的功能。每一部分都会有详细的指导,需要实现的部分也会在注释中以'TODO'标出。请仔细阅读所有的提示!

除了实现代码外,你还必须回答一些与项目和你的实现有关的问题。每一个需要你回答的问题都会以'问题 X'为标题。请仔细阅读每个问题,并且在问题后的'回答'文字框中写出完整的答案。我们将根据你对问题的回答和撰写代码所实现的功能来对你提交的项目进行评分。

提示:Code 和 Markdown 区域可通过Shift + Enter快捷键运行。此外,Markdown可以通过双击进入编辑模式。

开始

在这个项目中,你将使用1994年美国人口普查收集的数据,选用几个监督学习算法以准确地建模被调查者的收入。然后,你将根据初步结果从中选择出最佳的候选算法,并进一步优化该算法以最好地建模这些数据。你的目标是建立一个能够准确地预测被调查者年收入是否超过50000美元的模型。这种类型的任务会出现在那些依赖于捐款而存在的非营利性组织。了解人群的收入情况可以帮助一个非营利性的机构更好地了解他们要多大的捐赠,或是否他们应该接触这些人。虽然我们很难直接从公开的资源中推断出一个人的一般收入阶层,但是我们可以(也正是我们将要做的)从其他的一些公开的可获得的资源中获得一些特征从而推断出该值。

这个项目的数据集来自UCI机器学习知识库。这个数据集是由Ron Kohavi和Barry Becker在发表文章"Scaling Up the Accuracy of Naive-Bayes Classifiers: A Decision-Tree Hybrid"之后捐赠的,你可以在Ron Kohavi提供的在线版本中找到这个文章。我们在这里探索的数据集相比于原有的数据集有一些小小的改变,比如说移除了特征'fnlwgt' 以及一些遗失的或者是格式不正确的记录。


探索数据

运行下面的代码单元以载入需要的Python库并导入人口普查数据。注意数据集的最后一列'income'将是我们需要预测的列(表示被调查者的年收入会大于或者是最多50,000美元),人口普查数据中的每一列都将是关于被调查者的特征。

# 检查你的Python版本
from sys import version_info
if version_info.major != 2 and version_info.minor != 7:
    raise Exception('请使用Python 2.7来完成此项目')
# 为这个项目导入需要的库
import numpy as np
import pandas as pd
from time import time
from IPython.display import display # 允许为DataFrame使用display()

# 导入附加的可视化代码visuals.py
import visuals as vs

# 为notebook提供更加漂亮的可视化
%matplotlib inline

# 导入人口普查数据
data = pd.read_csv("census.csv")

# 成功 - 显示第一条记录
display(data.head(n=1))
image.png

练习:数据探索

首先我们对数据集进行一个粗略的探索,我们将看看每一个类别里会有多少被调查者?并且告诉我们这些里面多大比例是年收入大于50,000美元的。在下面的代码单元中,你将需要计算以下量:

  • 总的记录数量,'n_records'
  • 年收入大于50,000美元的人数,'n_greater_50k'.
  • 年收入最多为50,000美元的人数 'n_at_most_50k'.
  • 年收入大于50,000美元的人所占的比例, 'greater_percent'.

提示: 您可能需要查看上面的生成的表,以了解'income'条目的格式是什么样的。

# TODO:总的记录数
n_records = data.shape[0]

# TODO:被调查者的收入大于$50,000的人数
n_greater_50k = data[data.income == '>50K'].shape[0]

# TODO:被调查者的收入最多为$50,000的人数
n_at_most_50k = data[data.income == '<=50K'].shape[0]

# TODO:被调查者收入大于$50,000所占的比例
greater_percent = float(n_greater_50k) / float(n_records) * 100

# 打印结果
print "\033[1;34m Total number of records: {} \033[0m".format(n_records)
print "\033[1;34m Individuals making more than $50,000: {} \033[0m".format(n_greater_50k)
print "\033[1;34m Individuals making at most $50,000: {} \033[0m".format(n_at_most_50k)
print "\033[1;34m Percentage of individuals making more than $50,000: {:.2f} \033[0m".format(greater_percent)

Total number of records: 45222

Individuals making more than $50,000: 11208

Individuals making at most $50,000: 34014

Percentage of individuals making more than $50,000: 24.78


准备数据

在数据能够被作为输入提供给机器学习算法之前,它经常需要被清洗,格式化,和重新组织 - 这通常被叫做预处理。幸运的是,对于这个数据集,没有我们必须处理的无效或丢失的条目,然而,由于某一些特征存在的特性我们必须进行一定的调整。这个预处理都可以极大地帮助我们提升几乎所有的学习算法的结果和预测能力。

获得特征和标签

income 列是我们需要的标签,记录一个人的年收入是否高于50K。 因此我们应该把他从数据中剥离出来,单独存放。

# 将数据切分成特征和对应的标签
income_raw = data['income']
features_raw = data.drop('income', axis = 1)

转换倾斜的连续特征

一个数据集有时可能包含至少一个靠近某个数字的特征,但有时也会有一些相对来说存在极大值或者极小值的不平凡分布的的特征。算法对这种分布的数据会十分敏感,并且如果这种数据没有能够很好地规一化处理会使得算法表现不佳。在人口普查数据集的两个特征符合这个描述:'capital-gain''capital-loss'

运行下面的代码单元以创建一个关于这两个特征的条形图。请注意当前的值的范围和它们是如何分布的。

# 可视化 'capital-gain''capital-loss' 两个特征
vs.distribution(features_raw)
image.png

对于高度倾斜分布的特征如'capital-gain''capital-loss',常见的做法是对数据施加一个<a href="https://en.wikipedia.org/wiki/Data_transformation_(statistics)">对数转换</a>,将数据转换成对数,这样非常大和非常小的值不会对学习算法产生负面的影响。并且使用对数变换显著降低了由于异常值所造成的数据范围异常。但是在应用这个变换时必须小心:因为0的对数是没有定义的,所以我们必须先将数据处理成一个比0稍微大一点的数以成功完成对数转换。

运行下面的代码单元来执行数据的转换和可视化结果。再次,注意值的范围和它们是如何分布的。

# 对于倾斜的数据使用Log转换
skewed = ['capital-gain', 'capital-loss']

features_raw[skewed] = data[skewed].apply(lambda x: np.log(x + 1))

# 可视化对数转换后 'capital-gain''capital-loss' 两个特征
vs.distribution(features_raw, transformed = True)
image.png

规一化数字特征

除了对于高度倾斜的特征施加转换,对数值特征施加一些形式的缩放通常会是一个好的习惯。在数据上面施加一个缩放并不会改变数据分布的形式(比如上面说的'capital-gain' or 'capital-loss');但是,规一化保证了每一个特征在使用监督学习器的时候能够被平等的对待。注意一旦使用了缩放,观察数据的原始形式不再具有它本来的意义了,就像下面的例子展示的。

运行下面的代码单元来规一化每一个数字特征。我们将使用sklearn.preprocessing.MinMaxScaler来完成这个任务。

from sklearn.preprocessing import MinMaxScaler

# 初始化一个 scaler,并将它施加到特征上
scaler = MinMaxScaler()
numerical = ['age', 'education-num', 'capital-gain', 'capital-loss', 'hours-per-week']
features_raw[numerical] = scaler.fit_transform(data[numerical])

# 显示一个经过缩放的样例记录
display(features_raw.head(n = 1))
image.png

练习:数据预处理

从上面的数据探索中的表中,我们可以看到有几个属性的每一条记录都是非数字的。通常情况下,学习算法期望输入是数字的,这要求非数字的特征(称为类别变量)被转换。转换类别变量的一种流行的方法是使用独热编码方案。独热编码为每一个非数字特征的每一个可能的类别创建一个“虚拟”变量。例如,假设someFeature有三个可能的取值AB或者C,。我们将把这个特征编码成someFeature_A, someFeature_BsomeFeature_C.

| 特征X | | 特征X_A | 特征X_B | 特征X_C | | :-: | | :-: | :-: | :-: | | B | | 0 | 1 | 0 | | C | ----> 独热编码 ----> | 0 | 0 | 1 | | A | | 1 | 0 | 0 |

此外,对于非数字的特征,我们需要将非数字的标签'income'转换成数值以保证学习算法能够正常工作。因为这个标签只有两种可能的类别("<=50K"和">50K"),我们不必要使用独热编码,可以直接将他们编码分别成两个类01,在下面的代码单元中你将实现以下功能:

  • 使用pandas.get_dummies()'features_raw'数据来施加一个独热编码。
  • 将目标标签'income_raw'转换成数字项。
    • 将"<=50K"转换成0;将">50K"转换成1
# TODO:使用pandas.get_dummies()'features_raw'数据进行独热编码
features = pd.get_dummies(features_raw)

# TODO:将'income_raw'编码成数字值
income = None
income = income_raw.apply(lambda x: 1 if x == '>50K' else 0)

# 打印经过独热编码之后的特征数量
encoded = list(features.columns)
print "\033[1;34m {} total features after one-hot encoding. \033[0m".format(len(encoded))

# 移除下面一行的注释以观察编码的特征名字
# print encoded

103 total features after one-hot encoding.

混洗和切分数据

现在所有的 类别变量 已被转换成数值特征,而且所有的数值特征已被规一化。和我们一般情况下做的一样,我们现在将数据(包括特征和它们的标签)切分成训练和测试集。其中80%的数据将用于训练和20%的数据用于测试。然后再进一步把训练数据分为训练集和验证集,用来选择和优化模型。

运行下面的代码单元来完成切分。

# 导入 train_test_split
from sklearn.model_selection import train_test_split

# 将'features''income'数据切分成训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(features, income, test_size = 0.2,random_state = 0,stratify = income)
# 将'X_train''y_train'进一步切分为训练集和验证集
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.2,random_state=0,stratify = y_train)

# 显示切分的结果
print "\033[1;34m Total number of records: {} \033[0m".format(n_records)
print "\033[1;34m Training set has {} samples. \033[0m".format(X_train.shape[0])
print "\033[1;34m Validation set has {} samples. \033[0m".format(X_val.shape[0])
print "\033[1;34m Testing set has {} samples. \033[0m".format(X_test.shape[0])

Total number of records: 45222

Training set has 28941 samples.

Validation set has 7236 samples.

Testing set has 9045 samples.


评价模型性能

在这一部分中,我们将尝试四种不同的算法,并确定哪一个能够最好地建模数据。四种算法包含一个天真的预测器 和三个你选择的监督学习器。

评价方法和朴素的预测器

CharityML通过他们的研究人员知道被调查者的年收入大于\$50,000最有可能向他们捐款。因为这个原因CharityML对于准确预测谁能够获得\$50,000以上收入尤其有兴趣。这样看起来使用准确率作为评价模型的标准是合适的。另外,把没有收入大于\$50,000的人识别成年收入大于\$50,000对于CharityML来说是有害的,因为他想要找到的是有意愿捐款的用户。这样,我们期望的模型具有准确预测那些能够年收入大于\$50,000的能力比模型去查全这些被调查者更重要。我们能够使用F-beta score作为评价指标,这样能够同时考虑查准率和查全率:

image.png

尤其是,当 $\beta = 0.5$ 的时候更多的强调查准率,这叫做F$_{0.5}$ score (或者为了简单叫做F-score)。

问题 1 - 天真的预测器的性能

通过查看收入超过和不超过 \$50,000 的人数,我们能发现多数被调查者年收入没有超过 \$50,000。如果我们简单地预测说“这个人的收入没有超过 \$50,000”,我们就可以得到一个 准确率超过 50% 的预测。这样我们甚至不用看数据就能做到一个准确率超过 50%。这样一个预测被称作是天真的。通常对数据使用一个天真的预测器是十分重要的,这样能够帮助建立一个模型表现是否好的基准。 使用下面的代码单元计算天真的预测器的相关性能。将你的计算结果赋值给'accuracy', ‘precision’, ‘recall’'fscore',这些值会在后面被使用,请注意这里不能使用scikit-learn,你需要根据公式自己实现相关计算。

如果我们选择一个无论什么情况都预测被调查者年收入大于 \$50,000 的模型,那么这个模型在验证集上的准确率,查准率,查全率和 F-score是多少?

#不能使用scikit-learn,你需要根据公式自己实现相关计算。

y_hypothesis = pd.DataFrame([1 for x in range(y_val.size)])

#https://blog.csdn.net/zhang_hongchao/article/details/75127138
TP = y_val[y_val == 1].size
TN = 0
FP = y_val[y_val == 0].size
FN = 0

beta=0.5

#TODO: 计算准确率
accuracy = float(TP) / float(y_val.size)

# TODO: 计算查准率 Precision
precision = float(TP) / ( float(TP) + float(FP) )

# TODO: 计算查全率 Recall
recall = float(TP) / ( float(TP) + float(FN) )

# TODO: 使用上面的公式,设置beta=0.5,计算F-score
fscore = ( 1 + np.square(beta) ) * ( precision * recall ) / ( (np.square(beta) * precision ) + recall )

# 打印结果
print "\033[1;34m Naive Predictor on validation data: \n \
      Accuracy score: {:.4f} \n \
      Precision: {:.4f} \n \
      Recall: {:.4f} \n \
      F-score: {:.4f} \033[0m".format(accuracy, precision, recall, fscore)

Naive Predictor on validation data:

Accuracy score: 0.2478

Precision: 0.2478

Recall: 1.0000

F-score: 0.2917

监督学习模型

问题 2 - 模型应用

你能够在 scikit-learn 中选择以下监督学习模型

  • 高斯朴素贝叶斯 (GaussianNB)
  • 决策树 (DecisionTree)
  • 集成方法 (Bagging, AdaBoost, Random Forest, Gradient Boosting)
  • K近邻 (K Nearest Neighbors)
  • 随机梯度下降分类器 (SGDC)
  • 支撑向量机 (SVM)
  • Logistic回归(LogisticRegression)

从上面的监督学习模型中选择三个适合我们这个问题的模型,并回答相应问题。

模型1

模型名称

回答:DecisionTree

描述一个该模型在真实世界的一个应用场景。(你需要为此做点研究,并给出你的引用出处)

回答:企业投资决策

这个模型的优势是什么?他什么情况下表现最好?

回答:

1、易于理解,数结构可见

2、需要较少的数据准备

3、可以处理数字和分类数据,其他算法大多只能处理其中一种

4、能够处理多输出问题

5、模型可解释性高

6、使用统计验证模型,模型可靠性高

7、对异常值不敏感(缺失属性的样本)

这个模型的缺点是什么?什么条件下它表现很差?

回答:

1、容易过拟合,通过剪枝可以一定程度避免这个问题

2、可能不稳定,实际的决策时是基于启发式的,如贪婪算法。其中每个节点找到局部最优解,但这种算法不能保证最终得到的是全局最优解。可以通过训练多个树来减轻此问题。

3、有些概念难以表达如XOR问题

4、如果数据在不同种类上的数据量不同,决策时可能会偏向数据量更大的一方

5、会忽略特征之间的相互关系

根据我们当前数据集的特点,为什么这个模型适合这个问题。

回答:当前是一个二分类问题,决策树针对这种问题适用

模型2

模型名称

回答:KNeighborsClassifier

描述一个该模型在真实世界的一个应用场景。(你需要为此做点研究,并给出你的引用出处)

回答:文本分类、模式识别、聚类分析

这个模型的优势是什么?他什么情况下表现最好?

回答:

1、可以用于分类和回归计算

2、可用于非线性问题

3、算法复杂度低O(n)

3、准确度较高,对异常值不敏感

4、KNN是一种在线学习算法,新的训练数据可以直接加入而不用重新训练

这个模型的缺点是什么?什么条件下它表现很差?

回答:

1、样本不平衡问题下表现不好

2、内存开销大

3、计算时间长(对于样本容量大的数据集)

4、K的选择问题

5、每一次分类都会进行一次全局运算

根据我们当前数据集的特点,为什么这个模型适合这个问题。

回答:本问题时二分问题,样本特征空间不大,样本数量不算多。可以试一试

模型3

模型名称

回答:BaggingClassifier

描述一个该模型在真实世界的一个应用场景。(你需要为此做点研究,并给出你的引用出处)

回答:随机森林

这个模型的优势是什么?他什么情况下表现最好?

回答:

1、可以降低基学习器的方差

2、避免过拟合,泛化错误率低

3、样本空间较大时可以将数据分为不同子集分别训练,然后再合成分类器

4、当决策边界复杂时可使用多个线性分类器再将他们合成

5、当数据来自多个数据源且为异构特征集的时候,可以分别训练分类器然后再将他们集成

6、无需调整底层算法的参数

7、可以处理不平衡样本集

这个模型的缺点是什么?什么条件下它表现很差?

回答:训练时间比基学习器长、内存开销比基学习器大

根据我们当前数据集的特点,为什么这个模型适合这个问题。

回答:主要为了对比与决策树的性能差异和模型表现

技术沙龙 教程文章 热点综合

评论