发布时间:2025-10-16 23:49:02    次浏览
编程派微信号:codingpy 本文是「打造数据科学的作品集」的第三篇,如果你喜欢并希望及时获取本系列的最新文章,可以订阅我们。 全文大约25000 字符,读完大约需要 37 分钟。分为上下两部分发布。 作者:Vik Paruchuri,译者:唐晓霆,校对:EarlGrey,出品:PythonTG 翻译组/编程派 给数据做标记 我们已经在 annotate.py 中添加上一些函数,现在可以开始处理最有价值的部分了。我们需要把收购数据转换成一个机器学习算法可以使用的训练集。需要做以下几件事: 把所有数据变成数字 补足空白的值 给每一行添加一个 performance_count和一个 foreclosure_status 删除那些没有多少表现历史数据的行(那些 performance_count很低的行) 有几列的数据都是文字,这在机器学习里没有什么用。然而它们其实是类别变量,比如说 R、S 这样的类别编号。我们分别赋予它们数字,从而把它们变成数字: 这样转化了之后,就能把它们用于机器学习。一些列也包含了时间( first_payment_date 和 origination_date )。可以把它们各自分割成两列: 下面的代码中,我们会转换收购数据。定义一个函数,这个函数会: 从 counts字典里获取数据,在 acquisition里建立一个foreclosure_status列 从 counts字典里获取数据,在 acquisition里建立一个performance_count列 把下面的列从文字转成数字: channel seller first_time_homebuyer loan_purpose property_type occupancy_status property_state product_type 分别把 first_payment_date和 origination_date转换成两列: 以 /为分隔符进行分割 把第一部分赋予 month列 把第二部分赋予 year列 删除原本列 最后,我们就会有first_payment_month、first_payment_year、origination_month和 origination_year 将 acquisition里的所有缺失值都替换成 -1 defannotate(acquisition, counts):acquisition[ 'foreclosure_status'] = acquisition[ 'id'].apply( lambdax: get_performance_summary_value(x, 'foreclosure_status', counts)) acquisition[ 'performance_count'] = acquisition[ 'id'].apply( lambdax: get_performance_summary_value(x, 'performance_count', counts)) forcolumn in['channel','seller','first_time_homebuyer','loan_purpose','property_type','occupancy_status','property_state','product_type']: acquisition[column] = acquisition[column].astype( 'category').cat.codesforstart in[ 'first_payment', 'origination']: column = '{}_date'.format(start) acquisition[ '{}_year'.format(start)] = pd.to_numeric(acquisition[column].str.split( '/').str.get( 1)) acquisition[ '{}_month'.format(start)] = pd.to_numeric(acquisition[column].str.split( '/').str.get( 0))delacquisition[column] acquisition = acquisition.fillna(- 1) acquisition = acquisition[acquisition[ 'performance_count'] settings.MINIMUM_TRACKING_QUARTERS]returnacquisition 拼接所有数据很快就可以将所有数据拼接在一起了,在这之前我们只要再加一些代码到 annotate.py 里。在下面的代码中,我们: 定义一个函数来读取收购数据 定义一个函数把处理过的数据写入 processed/train.csv 如果文件是从命令行传入的,比如 python annotate.py,则: 读取收购数据 计算表现数据的累计数目,并赋值给 counts 给 acquisitionDataFrame 做标记 把 acquisitionDataFrame 写入 train.csv defread():acquisition = pd.read_csv(os.path.join(settings.PROCESSED_DIR, 'Acquisition.txt'), sep= '|') returnacquisitiondefwrite(acquisition):acquisition.to_csv(os.path.join(settings.PROCESSED_DIR, 'train.csv'), index= False)if__name__ == '__main__': acquisition = read() counts = count_performance_rows() acquisition = annotate(acquisition, counts) write(acquisition)写好文件后,记得用 python annotate.py 来运行它,这会生成一个 train.csv 文件。完整的 annotate.py 文件在。文件夹现在应该长这样:loan-prediction ├── data │ ├── Acquisition_2012Q1.txt │ ├── Acquisition_2012Q2.txt │ ├── Performance_2012Q1.txt │ ├── Performance_2012Q2.txt │ └── ... ├── processed │ ├── Acquisition.txt │ ├── Performance.txt │ ├── train.csv ├── .gitignore ├── annotate.py ├── assemble.py ├── README.md ├── requirements.txt ├── settings.py 寻找误差衡量指标我们生成好了训练数据,现在只需要完成最后一步,生成预测。我们需要找到一个误差的衡量指标,以及如何评估数据。就本文而言,没有被止赎的贷款比止赎的贷款多得多,所以典型的准确度衡量并不适用。如果我们看一看训练数据,并查看 foreclosure_status 列的计数,会发现:importpandas aspdimportsettings train = pd.read_csv(os.path.join(settings.PROCESSED_DIR, 'train.csv')) train[ 'foreclosure_status'].value_counts() False 4635982 True 1585 Name: foreclosure_status, dtype: int64因为只有这么一点点贷款是止赎了,所以如果我们只看有多少百分比的标签被正确预测了,那我们即使建立了一个只预测 False 的模型,一样可以得到很高的准确度。所以我们采用的衡量指标要把这种不平衡考虑进去,确保准确预测。我们不想要太多假正(False Positive),即预测一个贷款会止赎,但其实不会,或者太多假负(False Negative),即预测一个贷款不会被止赎,但其实会。在这两者之间,假负对房利美来说成本更高,因为他们买的这些房贷没法收回投资。我们定义假负率为预测不会止赎但其实会的预测数量,除以总的止赎贷款数量。这就是模型没有体现的实际止赎百分比。下面是一个图表: 在上图中,状态为 1 的贷款被预测为非止赎,但它其实被止赎了。如果把它除以实际止赎贷款数量 2,错误的负预测率为 50% 。我们用它作为误差衡量指标,这样就能够有效地评估模型的表现。为机器学习设置好分类器我们使用交叉验证来做预测。为了进行交叉验证,我们把数据分成 3 组,然后: 在 1 组和 2 组上训练模型,然后在 3 组上预测 在 1 组和 3 组上训练模型,然后在 2 组上预测 在 2 组和 3 组上训练模型,然后在 1 组上预测 把数据分成几组意味着我们不会用同样的数据来训练模型,然后又用同样的数据来做预测。这就避免了过拟合。如果过拟合了,就会得到一个错的低假负率,也就是说我们的模型很难应用于真实情况或进行后续改进。中有一个叫做 的函数,使得交叉验证变得很容易。我们还需要挑选一个算法来做预测。我们需要一个分类器来做。因为目标变量 foreclosure_status 只有两个值,True和Flase。我们使用 。因为它在二元分类下表现很好,运行得极快,而且消耗很少内存。这是因为这个算法的工作方式 — 它不会像随机森林算法那样建立一堆决策树,或像支持向量机那样做很耗资源的变换,其设计的矩阵操作相对来说少得多。我们可以用 scikit-learn 里自带的算法。唯一需要注意的就是每个类的权重。 如果给每个类同样的权重,,算法就会对每一行预测 False ,因为它要最小化误差.。然而,我们更关心止赎的贷款而不是不会止赎的贷款。因此,我们给 传入 balanced 参数到 class_weight 关键字中,从而得到一个考虑样本数量而给于平衡的比重的算法。这样就能确保算法不会对每一行都预测 False。进行预测现在已经完成了前期准备工作,可以开始做预测了。创建一个叫 predict.py 的新文件,使用我们之前创建的 train.csv。下面的代码会: 导入需要的库 创建一个 cross_validate函数,它会: 用正确的关键词参数创建一个逻辑递归分类器 创建用来训练模型的数据列列表,同时删除 id和foreclosure_status列 在 trainDataFrame 上运行交叉验证 返回预测 importos importsettingsimportpandas aspdfromsklearn importcross_validationfromsklearn.linear_model importLogisticRegressionfromsklearn importmetricsdefcross_validate(train):clf = LogisticRegression(random_state= 1, class_weight= 'balanced') predictors = train.columns.tolist() predictors = [p forp inpredictors ifp notinsettings.NON_PREDICTORS] predictions = cross_validation.cross_val_predict(clf, train[predictors], train[settings.TARGET], cv=settings.CV_FOLDS)returnpredictions 预测误差现在只需要写一些函数来计算误差。下面的代码会: 创建 computer_error函数,它会: 用 scikit-learn 计算一个简单准确度评分(符合真实 foreclosure_status值的预测的百分比) 创建 computer_false_negatives函数,它会: 把目标和预测写进一个 DataFrame 计算假负率 创建 computer_false_positives函数,它会: 找到模型预测为止赎但并未止赎的贷款数量 用这个数量除以不是止赎的贷款数量 把目标和预测写进一个DataFrame 计算假正率 defcompute_error(target, predictions): returnmetrics.accuracy_score(target, predictions) defcompute_false_negatives(target, predictions):df = pd.DataFrame({ 'target': target, 'predictions': predictions})returndf[(df[ 'target'] == 1) (df[ 'predictions'] == 0)].shape[ 0] / (df[(df[ 'target'] == 1)].shape[ 0] + 1)defcompute_false_positives(target, predictions):df = pd.DataFrame({ 'target': target, 'predictions': predictions})returndf[(df[ 'target'] == 0) (df[ 'predictions'] == 1)].shape[ 0] / (df[(df[ 'target'] == 0)].shape[ 0] + 1) 整合所有函数现在,把上面的函数都放在 predict.py 里面。下面的代码会: 读取数据集 计算交叉验证预测 计算上面提到的 3 个误差值 打印出误差值 defread():train = pd.read_csv(os.path.join(settings.PROCESSED_DIR, 'train.csv')) returntrainif__name__ == '__main__': train = read() predictions = cross_validate(train) error = compute_error(train[settings.TARGET], predictions) fn = compute_false_negatives(train[settings.TARGET], predictions) fp = compute_false_positives(train[settings.TARGET], predictions) print( 'Accuracy Score: {}'.format(error)) print( 'False Negatives: {}'.format(fn)) print( 'False Positives: {}'.format(fp))添加完这些代码后,可以运行 python predict.py 来生成预测。结果显示,假负率为 .26 ,也就是说对于止赎贷款来说,我们错误地预测了其中的 26% 。这是个好的开始,但还有很大的提升空间。完整的 predict.py 文件在。文件树现在应该长这样:loan-prediction ├── data │ ├── Acquisition_2012Q1.txt │ ├── Acquisition_2012Q2.txt │ ├── Performance_2012Q1.txt │ ├── Performance_2012Q2.txt │ └── ... ├── processed │ ├── Acquisition.txt │ ├── Performance.txt │ ├── train.csv ├── .gitignore ├── annotate.py ├── assemble.py ├── predict.py ├── README.md ├── requirements.txt ├── settings.py 撰写 README现在我们完成了这个完整的项目, 接下来只需要写 README.md 文件进行总结,向他人说明我们做了什么,以及如何复制它。一个典型的 README.md 应该包括以下内容: 项目概览及目标 如何下载所需数据或材料 安装教程 如何安装需要的模块 使用教程 如何运行项目 每一步应该看到哪些结果 如何贡献 扩展这个项目的要怎么做 是本项目的示例 README.md。下一步恭喜,你已经完成了一个完整的机器学习项目!你可在找到完整的示例项目。完成项目之后,记得上传到 Github 上,这样其他人就会看到这是你作品集的一部分。这些数据尚有一些地方待你挖掘。大致来说,我们可以把它们分成 3 类 — 扩展项目提高准确率,利用其它数据列进行预测,进一步探索数据。以下想法仅供参考: 用 annotate.py生成更多特征 在 predict.py里换个算法 使用更多来自房利美的数据 加上一个预测未来数据的方法。如果添加更多的数据,目前的代码都是可以运行的,所以我们可以加上更多过去的或者未来的数据 尝试能不能预测银行一开始该不该放出贷款(以及房利美应不应该收购贷款) 有些在房利美收购的时候有,但之前没有 删除那些银行在发放贷款时不能获得的信息列 做预测 探索一下能不能预测除了 foreclosure_status以外的数据 能不能预测房产在出售时能卖多少钱? 探索一下表现数据更新时的细节 能不能预测借方迟付贷款的次数? 能不能画出典型的贷款周期? 按州或邮编对数据进行绘图 有看到一些有趣的模式吗? 如果你创建了一些有趣的项目, 请在留言区告知我们!欢迎转发至朋友圈。如无特殊注明,本公号所发文章均为原创或编译,如需转载,请联系「编程派」获得授权。【近期优秀教程推荐】使用好镜像源,把等待的时间转为生产力用Python从头开发一个自己的Shell(上)不懂排序算法?看这些舞者给你做的演示(下)一文学会Python多进程编程一文学会Python多线程编程扫码关注编程派获取最新教程及资源推送↓↓↓ 点击阅读原文,查看更多Python教程赞赏人赞赏