2016年10月

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

数据科学公司在决定雇佣时越来越关注你在数据科学方面的 作品集 ( Portfolio ) 。这其中的一个原因是,这样的作品集是判断某人的实际技能的最好的方法。好消息是构建这样的作品集完全要看你自己。只要你在这方面付出了努力,你一定可以取得让这些公司钦佩的作品集。

构建高质量的作品集的第一步就是知道需要什么技能。公司想要在数据科学方面拥有的、他们希望你能够运用的主要技能有:

  • 沟通能力
  • 协作能力
  • 技术能力
  • 数据推理能力
  • 动机和主动性

任何好的作品集都由多个项目表现出来,其中每个都能够表现出以上一到两点。这是本系列的第三篇,本系列我们主要讲包括如何打造面面俱到的数据科学作品集。在这一篇中,我们主要涵盖了如何构建组成你的作品集的第二个项目,以及如何创建一个端对端的机器学习项目。在最后,我们将拥有一个展示你的数据推理能力和技术能力的项目。如果你想看一下的话,这里有一个完整的例子。

一个端到端的项目

作为一个数据科学家,有时候你会拿到一个数据集并被问如何用它来讲故事。在这个时候,沟通就是非常重要的,你需要用它来完成这个事情。像我们在前一篇文章中用过的,类似 Jupyter notebook 这样的工具,将对你非常有帮助。在这里你能找到一些可以用的报告或者总结文档。

不管怎样,有时候你会被要求创建一个具有操作价值的项目。具有操作价值的项目将直接影响到公司的日常业务,它会使用不止一次,经常是许多人使用。这个任务可能像这样 “创建一个算法来预测周转率”或者“创建一个模型来自动对我们的文章打标签”。在这种情况下,技术能力比讲故事更重要。你必须能够得到一个数据集,并且理解它,然后创建脚本处理该数据。这个脚本要运行的很快,占用系统资源很小。通常它可能要运行很多次,脚本的可使用性也很重要,并不仅仅是一个演示版。可使用性是指整合进操作流程,并且甚至是是面向用户的。

端对端项目的主要组成部分:

  • 理解背景
  • 浏览数据并找出细微差别
  • 创建结构化项目,那样比较容易整合进操作流程
  • 运行速度快、占用系统资源小的高性能代码
  • 写好安装和使用文档以便其他人用

为了有效的创建这种类型的项目,我们可能需要处理多个文件。强烈推荐使用 Atom 这样的文本编辑器或者 PyCharm 这样的 IDE。这些工具允许你在文件间跳转,编辑不同类型的文件,例如 markdown 文件,Python 文件,和 csv 文件等等。结构化你的项目还利于版本控制,并上传一个类似 Github 这样的协作开发工具上也很有用。

Github 上的这个项目

在这一节中我们将使用 Pandas 和 scikit-learn 这样的库,我们还将大量用到 Pandas DataFrames,它使得 python 读取和处理表格数据更加方便。

找到好的数据集

为一个端到端的作品集项目的找到好的数据集很难。在内存和性能的限制下,数据集需要尽量的大。它还需要是实际有用的。例如,这个数据集,它包含有美国院校的录取标准、毕业率以及毕业以后的收入,是个很好的可以讲故事的数据集。但是,不管你如何看待这个数据,很显然它不适合创建端到端的项目。比如,你能告诉人们他们去了这些大学以后的未来收入,但是这个快速检索却并不足够呈现出你的技术能力。你还能找出院校的招生标准和更高的收入相关,但是这更像是常理而不是你的技术结论。

这里还有内存和性能约束的问题,比如你有几千兆的数据,而且当你需要找到一些差异时,就需要对数据集一遍遍运行算法。

一个好的可操作的数据集可以让你构建一系列脚本来转换数据、动态地回答问题。一个很好的例子是股票价格数据集,当股市关闭时,就会给算法提供新的数据。这可以让你预测明天的股价,甚至预测收益。这不是讲故事,它带来的是真金白银。

一些找到数据集的好地方:

  • /r/datasets – 有上百的有趣数据的 subreddit(Reddit 是国外一个社交新闻站点,subreddit 指该论坛下的各不同版块)。
  • Google Public Datasets – 通过 Google BigQuery 使用的公开数据集。
  • Awesome datasets – 一个数据集列表,放在 Github 上。

当你查看这些数据集时,想一下人们想要在这些数据集中得到什么答案,哪怕这些问题只想过一次(“房价是如何与标准普尔 500 指数关联的?”),或者更进一步(“你能预测股市吗?”)。这里的关键是更进一步地找出问题,并且用相同的代码在不同输入(不同的数据)上运行多次。

对于本文的目标,我们来看一下 房利美 ( Fannie Mae ) 贷款数据。房利美是一家在美国的政府赞助的企业抵押贷款公司,它从其他银行购买按揭贷款,然后捆绑这些贷款为贷款证券来转卖它们。这使得贷款机构可以提供更多的抵押贷款,在市场上创造更多的流动性。这在理论上会带来更多的住房和更好的贷款期限。从借款人的角度来说,它们大体上差不多,话虽这样说。

房利美发布了两种类型的数据 – 它获得的贷款的数据,和贷款偿还情况的数据。在理想的情况下,有人向贷款人借钱,然后还款直到还清。不管怎样,有些人多次不还,从而丧失了抵押品赎回权。抵押品赎回权是指没钱还了被银行把房子给收走了。房利美会追踪谁没还钱,并且哪个贷款需要收回抵押的房屋(取消赎回权)。每个季度会发布此数据,发布的是滞后一年的数据。当前可用是 2015 年第一季度数据。

“贷款数据”是由房利美发布的贷款发放的数据,它包含借款人的信息、信用评分,和他们的家庭贷款信息。“执行数据”,贷款发放后的每一个季度公布,包含借贷人的还款信息和是否丧失抵押品赎回权的状态,一个“贷款数据”的“执行数据”可能有十几行。可以这样理解,“贷款数据”告诉你房利美所控制的贷款,“执行数据”包含该贷款一系列的状态更新。其中一个状态更新可以告诉我们一笔贷款在某个季度被取消赎回权了。

一个没有及时还贷的房子就这样的被卖了

选择一个角度

这里有几个我们可以去分析房利美数据集的方向。我们可以:

  • 预测房屋的销售价格。
  • 预测借款人还款历史。
  • 在获得贷款时为每一笔贷款打分。

最重要的事情是坚持单一的角度。同时关注太多的事情很难做出效果。选择一个有着足够细节的角度也很重要。下面的角度就没有太多细节:

  • 找出哪些银行将贷款出售给房利美的多数被取消赎回权。
  • 计算贷款人的信用评分趋势。
  • 找到哪些类型的家庭没有偿还贷款的能力。
  • 找到贷款金额和抵押品价格之间的关系。

上面的想法非常有趣,如果我们关注于讲故事,那是一个不错的角度,但是不是很适合一个操作性项目。

在房利美数据集中,我们将仅使用申请贷款时有的那些信息来预测贷款是否将来会被取消赎回权。实际上, 我们将为每一笔贷款建立“分数”来告诉房利美买还是不买。这将给我们打下良好的基础,并将组成这个漂亮的作品集的一部分。

理解数据

我们来简单看一下原始数据文件。下面是 2012 年 1 季度前几行的贷款数据:

100000853384|R|OTHER|4.625|280000|360|02/2012|04/2012|31|31|1|23|801|N|C|SF|1|I|CA|945||FRM|
100003735682|R|SUNTRUST MORTGAGE INC.|3.99|466000|360|01/2012|03/2012|80|80|2|30|794|N|P|SF|1|P|MD|208||FRM|788
100006367485|C|PHH MORTGAGE CORPORATION|4|229000|360|02/2012|04/2012|67|67|2|36|802|N|R|SF|1|P|CA|959||FRM|794

下面是 2012 年 1 季度的前几行执行数据:

100000853384|03/01/2012|OTHER|4.625||0|360|359|03/2042|41860|0|N||||||||||||||||
100000853384|04/01/2012||4.625||1|359|358|03/2042|41860|0|N||||||||||||||||
100000853384|05/01/2012||4.625||2|358|357|03/2042|41860|0|N||||||||||||||||

在开始编码之前,花些时间真正理解数据是值得的。这对于操作性项目优为重要,因为我们没有交互式探索数据,将很难察觉到细微的差别,除非我们在前期发现他们。在这种情况下,第一个步骤是阅读房利美站点的资料:

在看完这些文件后后,我们了解到一些能帮助我们的关键点:

  • 从 2000 年到现在,每季度都有一个贷款和执行文件,因数据是滞后一年的,所以到目前为止最新数据是 2015 年的。
  • 这些文件是文本格式的,采用管道符号|进行分割。
  • 这些文件是没有表头的,但我们有个文件列明了各列的名称。
  • 所有一起,文件包含 2200 万个贷款的数据。
  • 由于执行数据的文件包含过去几年获得的贷款的信息,在早些年获得的贷款将有更多的执行数据(即在 2014 获得的贷款没有多少历史执行数据)。

这些小小的信息将会为我们节省很多时间,因为这样我们就知道如何构造我们的项目和利用这些数据了。

构造项目

在我们开始下载和探索数据之前,先想一想将如何构造项目是很重要的。当建立端到端项目时,我们的主要目标是:

  • 创建一个可行解决方案
  • 有一个快速运行且占用最小资源的解决方案
  • 容易可扩展
  • 写容易理解的代码
  • 写尽量少的代码

为了实现这些目标,需要对我们的项目进行良好的构造。一个结构良好的项目遵循几个原则:

  • 分离数据文件和代码文件
  • 从原始数据中分离生成的数据。
  • 有一个 README.md 文件帮助人们安装和使用该项目。
  • 有一个 requirements.txt 文件列明项目运行所需的所有包。
  • 有一个单独的 settings.py 文件列明其它文件中使用的所有的设置

    • 例如,如果从多个 Python 脚本读取同一个文件,让它们全部 import 设置并从一个集中的地方获得文件名是有用的。
  • 有一个 .gitignore 文件,防止大的或密码文件被提交。
  • 分解任务中每一步可以单独执行的步骤到单独的文件中。

    • 例如,我们将有一个文件用于读取数据,一个用于创建特征,一个用于做出预测。
  • 保存中间结果,例如,一个脚本可以输出下一个脚本可读取的文件。

    • 这使我们无需重新计算就可以在数据处理流程中进行更改。

我们的文件结构大体如下:

loan-prediction
├── data
├── processed
├── .gitignore
├── README.md
├── requirements.txt
├── settings.py

创建初始文件

首先,我们需要创建一个 loan-prediction 文件夹,在此文件夹下面,再创建一个 data 文件夹和一个 processed 文件夹。data 文件夹存放原始数据,processed 文件夹存放所有的中间计算结果。

其次,创建 .gitignore 文件,.gitignore 文件将保证某些文件被 git 忽略而不会被推送至 GitHub。关于这个文件的一个好的例子是由 OSX 在每一个文件夹都会创建的 .DS_Store 文件,.gitignore 文件一个很好的范本在这里。我们还想忽略数据文件,因为它们实在是太大了,同时房利美的条文禁止我们重新分发该数据文件,所以我们应该在我们的文件后面添加以下 2 行:

data
processed

这里是该项目的一个关于 .gitignore 文件的例子。

再次,我们需要创建 README.md 文件,它将帮助人们理解该项目。后缀 .md 表示这个文件采用 markdown 格式。Markdown 使你能够写纯文本文件,同时还可以添加你想要的神奇的格式。这里是关于 markdown 的导引。如果你上传一个叫 README.md 的文件至 Github,Github 会自动处理该 markdown,同时展示给浏览该项目的人。例子在这里。

至此,我们仅需在 README.md 文件中添加简单的描述:

Loan Prediction
-----------------------

Predict whether or not loans acquired by Fannie Mae will go into foreclosure.  Fannie Mae acquires loans from other lenders as a way of inducing them to lend more.  Fannie Mae releases data on the loans it has acquired and their performance afterwards [here](http://www.fanniemae.com/portal/funding-the-market/data/loan-performance-data.html).

现在,我们可以创建 requirements.txt 文件了。这会帮助其它人可以很方便地安装我们的项目。我们还不知道我们将会具体用到哪些库,但是以下几个库是需要的:

pandas
matplotlib
scikit-learn
numpy
ipython
scipy

以上几个是在 python 数据分析任务中最常用到的库。可以认为我们将会用到大部分这些库。这里是该项目 requirements.txt 文件的一个例子。

创建 requirements.txt 文件之后,你应该安装这些包了。我们将会使用 python3。如果你没有安装 python,你应该考虑使用 Anaconda,它是一个 python 安装程序,同时安装了上面列出的所有包。

最后,我们可以建立一个空白的 settings.py 文件,因为我们的项目还没有任何设置。

获取数据

一旦我们有了项目的基本架构,我们就可以去获得原始数据。

房利美对获取数据有一些限制,所以你需要去注册一个账户。在创建完账户之后,你可以找到在这里的下载页面,你可以按照你所需要的下载或多或少的贷款数据文件。文件格式是 zip,在解压后当然是非常大的。

为了达到我们这个文章的目的,我们将要下载从 2012 年 1 季度到 2015 年 1 季度的所有数据。接着我们需要解压所有的文件。解压过后,删掉原来的 .zip 格式的文件。最后,loan-prediction 文件夹看起来应该像下面的一样:

loan-prediction
├── data
│   ├── Acquisition_2012Q1.txt
│   ├── Acquisition_2012Q2.txt
│   ├── Performance_2012Q1.txt
│   ├── Performance_2012Q2.txt
│   └── ...
├── processed
├── .gitignore
├── README.md
├── requirements.txt
├── settings.py

在下载完数据后,你可以在 shell 命令行中使用 head 和 tail 命令去查看文件中的行数据,你看到任何的不需要的数据列了吗?在做这件事的同时查阅列名称的 pdf 文件可能有帮助。

读入数据

有两个问题让我们的数据难以现在就使用:

  • 贷款数据和执行数据被分割在多个文件中
  • 每个文件都缺少列名标题

在我们开始使用数据之前,我们需要首先明白我们要在哪里去存一个贷款数据的文件,同时到哪里去存储一个执行数据的文件。每个文件仅仅需要包括我们关注的那些数据列,同时拥有正确的列名标题。这里有一个小问题是执行数据非常大,因此我们需要尝试去修剪一些数据列。

第一步是向 settings.py 文件中增加一些变量,这个文件中同时也包括了我们原始数据的存放路径和处理出的数据存放路径。我们同时也将添加其他一些可能在接下来会用到的设置数据:

DATA_DIR = "data"
PROCESSED_DIR = "processed"
MINIMUM_TRACKING_QUARTERS = 4
TARGET = "foreclosure_status"
NON_PREDICTORS = [TARGET, "id"]
CV_FOLDS = 3

把路径设置在 settings.py 中使它们放在一个集中的地方,同时使其修改更加的容易。当在多个文件中用到相同的变量时,你想改变它的话,把他们放在一个地方比分散放在每一个文件时更加容易。这里的是一个这个工程的示例 settings.py 文件

第二步是创建一个文件名为 assemble.py,它将所有的数据分为 2 个文件。当我们运行 Python assemble.py,我们在处理数据文件的目录会获得 2 个数据文件。

接下来我们开始写 assemble.py 文件中的代码。首先我们需要为每个文件定义相应的列名标题,因此我们需要查看列名称的 pdf 文件,同时创建在每一个贷款数据和执行数据的文件的数据列的列表:

HEADERS = {
    "Acquisition": [
        "id",
        "channel",
        "seller",
        "interest_rate",
        "balance",
        "loan_term",
        "origination_date",
        "first_payment_date",
        "ltv",
        "cltv",
        "borrower_count",
        "dti",
        "borrower_credit_score",
        "first_time_homebuyer",
        "loan_purpose",
        "property_type",
        "unit_count",
        "occupancy_status",
        "property_state",
        "zip",
        "insurance_percentage",
        "product_type",
        "co_borrower_credit_score"
    ],
    "Performance": [
        "id",
        "reporting_period",
        "servicer_name",
        "interest_rate",
        "balance",
        "loan_age",
        "months_to_maturity",
        "maturity_date",
        "msa",
        "delinquency_status",
        "modification_flag",
        "zero_balance_code",
        "zero_balance_date",
        "last_paid_installment_date",
        "foreclosure_date",
        "disposition_date",
        "foreclosure_costs",
        "property_repair_costs",
        "recovery_costs",
        "misc_costs",
        "tax_costs",
        "sale_proceeds",
        "credit_enhancement_proceeds",
        "repurchase_proceeds",
        "other_foreclosure_proceeds",
        "non_interest_bearing_balance",
        "principal_forgiveness_balance"
    ]
}

接下来一步是定义我们想要保留的数据列。因为我们要预测一个贷款是否会被撤回,我们可以丢弃执行数据中的许多列。我们将需要保留贷款数据中的所有数据列,因为我们需要尽量多的了解贷款发放时的信息(毕竟我们是在预测贷款发放时这笔贷款将来是否会被撤回)。丢弃数据列将会使我们节省下内存和硬盘空间,同时也会加速我们的代码。

SELECT = {
    "Acquisition": HEADERS["Acquisition"],
    "Performance": [
        "id",
        "foreclosure_date"
    ]
}

下一步,我们将编写一个函数来连接数据集。下面的代码将:

  • 引用一些需要的库,包括 settings。
  • 定义一个函数 concatenate,目的是:

    • 获取到所有 data 目录中的文件名。
    • 遍历每个文件。

      • 如果文件不是正确的格式 (不是以我们需要的格式作为开头),我们将忽略它。
      • 通过使用 Pandas 的 read\_csv 函数及正确的设置把文件读入一个 DataFrame。

        • 设置分隔符为|,以便所有的字段能被正确读出。
        • 数据没有标题行,因此设置 header 为 None 来进行标示。
        • 从 HEADERS 字典中设置正确的标题名称 – 这将会是我们的 DataFrame 中的数据列名称。
        • 仅选择我们加在 SELECT 中的 DataFrame 的列。
  • 把所有的 DataFrame 共同连接在一起。
  • 把已经连接好的 DataFrame 写回一个文件。
import os
import settings
import pandas as pd

def concatenate(prefix="Acquisition"):
    files = os.listdir(settings.DATA_DIR)
    full = []
    for f in files:
        if not f.startswith(prefix):
            continue

        data = pd.read_csv(os.path.join(settings.DATA_DIR, f), sep="|", header=None, names=HEADERS[prefix], index_col=False)
        data = data[SELECT[prefix]]
        full.append(data)

    full = pd.concat(full, axis=0)

    full.to_csv(os.path.join(settings.PROCESSED_DIR, "{}.txt".format(prefix)), sep="|", header=SELECT[prefix], index=False)

我们可以通过调用上面的函数,通过传递的参数 Acquisition 和 Performance 两次以将所有的贷款和执行文件连接在一起。下面的代码将:

  • 仅在命令行中运行 python assemble.py 时执行。
  • 将所有的数据连接在一起,并且产生 2 个文件:

    • processed/Acquisition.txt
    • processed/Performance.txt
if __name__ == "__main__":
    concatenate("Acquisition")
    concatenate("Performance")

我们现在拥有了一个漂亮的,划分过的 assemble.py 文件,它很容易执行,也容易建立。通过像这样把问题分解为一块一块的,我们构建工程就会变的容易许多。不用一个可以做所有工作的凌乱脚本,我们定义的数据将会在多个脚本间传递,同时使脚本间完全的彼此隔离。当你正在一个大的项目中工作时,这样做是一个好的想法,因为这样可以更加容易修改其中的某一部分而不会引起其他项目中不关联部分产生预料之外的结果。

一旦我们完成 assemble.py 脚本文件,我们可以运行 python assemble.py 命令。你可以在这里查看完整的 assemble.py 文件。

这将会在 processed 目录下产生 2 个文件:

loan-prediction
├── data
│   ├── Acquisition_2012Q1.txt
│   ├── Acquisition_2012Q2.txt
│   ├── Performance_2012Q1.txt
│   ├── Performance_2012Q2.txt
│   └── ...
├── processed
│   ├── Acquisition.txt
│   ├── Performance.txt
├── .gitignore
├── assemble.py
├── README.md
├── requirements.txt
├── settings.py

计算来自执行数据的值

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

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

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

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

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

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

    • 这种方法的优点是从语法上来说容易执行。
    • 它的缺点需要读取所有的 129236094 行数据,这样就会占用大量内存,并且运行起来极慢。
  • 我们可以读取所有的执行数据,然后在贷款 DataFrame 上使用 apply 去计算每个贷款 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` 设置合适的值。
import os
import settings
import pandas as pd

def count_performance_rows():
    counts = {}
    with open(os.path.join(settings.PROCESSED_DIR, "Performance.txt"), 'r') as f:
        for i, line in enumerate(f):
            if i == 0:
                # Skip header row
                continue
            loan_id, date = line.split("|")
            loan_id = int(loan_id)
            if loan_id not in counts:
                counts[loan_id] = {
                    "foreclosure_status": False,
                    "performance_count": 0
                }
            counts[loan_id]["performance_count"] += 1
            if len(date.strip()) > 0:
                counts[loan_id]["foreclosure_status"] = True
    return counts

获取值

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

def get_performance_summary_value(loan_id, key, counts):
    value = counts.get(loan_id, {
        "foreclosure_status": False,
        "performance_count": 0
    })
    return value[key]

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

转换数据

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

  • 转换所有列为数字。
  • 填充缺失值。
  • 为每一行分配 performance_count 和 foreclosure_status。
  • 移除出现执行数据很少的行(performance_count 计数低)。

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

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

还有一些包含日期的列 (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_month、first_payment_year、rigination_month 和 origination_year。
  • 所有缺失值填充为 -1。
def annotate(acquisition, counts):
    acquisition["foreclosure_status"] = acquisition["id"].apply(lambda x: get_performance_summary_value(x, "foreclosure_status", counts))
    acquisition["performance_count"] = acquisition["id"].apply(lambda x: get_performance_summary_value(x, "performance_count", counts))
    for column in [
        "channel",
        "seller",
        "first_time_homebuyer",
        "loan_purpose",
        "property_type",
        "occupancy_status",
        "property_state",
        "product_type"
    ]:
        acquisition[column] = acquisition[column].astype('category').cat.codes

    for start 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))
        del acquisition[column]

    acquisition = acquisition.fillna(-1)
    acquisition = acquisition[acquisition["performance_count"] > settings.MINIMUM_TRACKING_QUARTERS]
    return acquisition

聚合到一起

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

  • 定义一个函数来读取贷款的数据。
  • 定义一个函数来写入处理过的数据到 processed/train.csv。
  • 如果该文件在命令行以 python annotate.py 的方式运行:

    • 读取贷款数据。
    • 计算执行数据的计数,并将其赋予 counts。
    • 转换 acquisition DataFrame。
    • 将acquisition DataFrame 写入到 train.csv。
def read():
    acquisition = pd.read_csv(os.path.join(settings.PROCESSED_DIR, "Acquisition.txt"), sep="|")
    return acquisition

def write(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 列的计数,我们将得到如下信息:

import pandas as pd
import settings

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,并能取得很高的精确度。相反,我们想要使用一个度量来考虑分类的不平衡,确保我们可以准确预测。我们要避免太多的误报率(预测贷款被取消赎回权,但是实际没有),也要避免太多的漏报率(预测贷款没有别取消赎回权,但是实际被取消了)。对于这两个来说,漏报率对于房利美来说成本更高,因为他们买的贷款可能是他们无法收回投资的贷款。

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

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

设置机器学习分类器

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

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

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

Scikit-learn 有一个叫做 crossvalpredict ,它可以帮助我们理解交叉算法.

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

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

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

做出预测

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

  • 导入所需的库
  • 创建一个名为 cross_validate 的函数:

    • 使用正确的关键词参数创建逻辑回归分类器
    • 创建用于训练模型的数据列的列表,移除 id 和 foreclosure_status 列
    • 交叉验证 train DataFrame
    • 返回预测结果
import os
import settings
import pandas as pd
from sklearn import cross_validation
from sklearn.linear_model import LogisticRegression
from sklearn import metrics

def cross_validate(train):
    clf = LogisticRegression(random_state=1, class_weight="balanced")

    predictors = train.columns.tolist()
    predictors = [p for p in predictors if p not in settings.NON_PREDICTORS]

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

预测误差

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

  • 创建函数 compute_error:

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

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

      • 找到原本应被预测模型取消赎回权,但实际没有取消的贷款数目
      • 除以没被取消赎回权的贷款总数目
def compute_error(target, predictions):
    return metrics.accuracy_score(target, predictions)

def compute_false_negatives(target, predictions):
    df = pd.DataFrame({"target": target, "predictions": predictions})
    return df[(df["target"] == 1) & (df["predictions"] == 0)].shape[0] / (df[(df["target"] == 1)].shape[0] + 1)

def compute_false_positives(target, predictions):
    df = pd.DataFrame({"target": target, "predictions": predictions})
    return df[(df["target"] == 0) & (df["predictions"] == 1)].shape[0] / (df[(df["target"] == 0)].shape[0] + 1)

聚合到一起

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

  • 读取数据集
  • 计算交叉验证预测
  • 计算上面的 3 个误差
  • 打印误差
def read():
    train = pd.read_csv(os.path.join(settings.PROCESSED_DIR, "train.csv"))
    return train

if __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 中的算法
  • 尝试使用比我们发表在这里的更多的房利美数据
  • 添加对未来数据进行预测的方法。如果我们添加更多数据,我们所写的代码仍然可以起作用,这样我们可以添加更多过去和未来的数据。
  • 尝试看看是否你能预测一个银行原本是否应该发放贷款(相对地,房利美是否应该获得贷款)

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

      • 当房利美购买贷款时,一些列是已知的,但之前是不知道的
    • 做出预测
  • 探索是否你可以预测除了 foreclosure_status 的其他列

    • 你可以预测在销售时资产值是多少?
  • 探索探索执行数据更新之间的细微差别

    • 你能否预测借款人会逾期还款多少次?
    • 你能否标出的典型贷款周期?
  • 将数据按州或邮政编码标出

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

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

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


via: https://www.dataquest.io/blog/data-science-portfolio-machine-learning/

作者:Vik Paruchuri 译者:kokialoves, zky001, vim-kakali, cposture, ideas4u 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

在当今这个信息时代,安全是最重要的。恐怕没有人会希望自己的重要数据落入到坏人的手里。但是即便是无安全防护的硬件同样面临不少威胁,而大多数时候人们还只是对软件采取安全防护措施。ORWL 就是来改变这件事的。

ORWL 是什么?

ORWL 宣称是社区里“造得最安全的家用电脑”。 它是一台小型的飞碟形计算机,在设计上全面考虑了物理安全性。物理安全很重要,因为一旦有人获得了从物理上访问你的计算机的途径,那么就 Game Over 了,这是信息安全专业人员中的一个老生常谈。

ORWL 的基本规格包括英特尔 Skylake M3 处理器、USB-C 和微型 HDMI 接口、8GB 内存、120G 固态硬盘、蓝牙、Wifi 和 NFC 技术。目前它支持的操作系统有 Ubuntu 16.04、Qubes OS,以及 Windows 10 等,开箱即用。你只需要有一个显示器,键盘和鼠标就可以开始使用它了。

ORWL 是开源的

ORWL 是完全开源的。这意味着,如果有人想设计一台自己的计算机或者对 ORWL 作出改进的话,原理图和设计文件、软件、固件、所有东西都是可以获取的。

ORWL 是如何工作的?

ORWL 使用自加密固态硬盘(SSD)。一个安全微控制器集成到其主板上来产生和存储加密密匙。一旦核实了系统的完整性和已验证用户,它就使用密匙来解密固态硬盘(SSD)。无论对 ORWL 进行任何类型的篡改,密匙都会立即删除固态硬盘(SSD)上的无用数据

为了完成身份验证,ORWL 有一个使用和智能卡类似技术的密匙卡。它使用 NFC 技术来验证用户,同时使用蓝牙来检测用户是否在当前使用范围内。如果用户不在附近(不在使用范围内),ORWL 将会自动锁定。

整个 ORWL 系统被包裹在一个到处伴有压力开关的活动翻盖网格里。安全微控制器持续监控专用惯性测量部件,活动翻盖网格,内部温度和电源输入电压,以用于检测篡改。这将防止访问内部组件,比如试图破坏翻盖将会触发安全微控制器从而删除加密密匙。此外,出于安全考虑,动态随机存储器(DRAM)也焊有外壳。

这并不是全部安全防护措施,ORWL 还有更多详细的措施来确保设备绝对的物理安全性,包括冷启动防护,直接存储器访问(DMA)攻击防御等。

更多信息可以在 ORWL 引导页找到。这儿是一个关于它如何工作的快速视频。

ORWL 是信息安全的最终答案吗?

令人沮丧的是,ORWL 并不是信息安全的最终答案。ORWL 只能为计算机提供物理安全,尽管这很强大并且看起来 极酷 ( totally ninja ) ,但是还有许多来自其他方面的攻击会损坏你的系统。然而,ORWL 确实从整体上提高了计算机的安全性。

你认为 ORWL 怎么样?通过评论让我们知道你的想法。


via: https://itsfoss.com/orwl-physically-secure-computer/

作者:Munif Tanjim 译者:ucasFL 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

操作系统安全是现在最重要的事情,而 Linux 则是一个主要被讨论的部分。首先要解决的问题之一就是:我们如何确定提交到上游的补丁已经进行了代码审核?

Wolfram Sang 从 2008 年开始成为一名 Linux 内核开发者,他经常在各地召开的 Linux 峰会上发表讲话,比如在 2016 年柏林 Linux 峰会,他提出了如何提高内核开发实践的想法。

让我们来看看他的观点。

在 2013 年的时候,你曾在爱丁堡(Edinburgh)提醒 ELCE 委员会,如果不作出改变,那么子系统的潜在问题和其他争议问题将会逐渐扩大。他们做出改变了吗?你所提及的那些事件发生了吗?

是的,在某些程度上来说。当然了,Linux 内核是一个很多部分组成的项目,所以给以 Linux 各个子系统更多关注应该放在一个更重要的位置。然而,有太多的子系统“只是拼图中的一块”,所以通常来说,这些子系统的潜在问题还未被解决。

你曾指出代码审核人数是一个大问题。为何你觉得 Linux 内核开发社区没有足够的代码审核人员呢?

理由之一就是,大多数开发者实际上只是编写代码,而读代码并不多。这本是没有什么错,但却说明了并非每个人都是代码审核人员,所以我们真的应该鼓励每个人都进行代码审核。

我所看到另一件事就是,但我们要请人员加入我们的社区时,最重要的考核就是补丁贡献数量。我个人认为这是很正常的,并且在初期总贡献量少的时候是非常好的做法。但是随着越来越多的人员,特别是公司的加入,我们就碰到源码审核的问题。但是别误解了,有着数量可观的贡献是很棒的。但目前需要指出的是,参与社区有着更多内涵,比方说如何为下一步发展负责。有些部分在改善,但是还不够。

你认为更多的代码审核人员培训或者审核激励措施是否会有帮助?

我最主要的观点就是要指出,现今仍存在问题。是的,目前为止我们做到很好,但不意味着全都做的很好。我们也有类似扩张方面的问题。让人们了解事实,是希望能够让一些团体对此感兴趣并参与其中。尽管,我并不认为我们需要特殊的训练。我所熟悉的一些代码审核人员都非常棒或者很有天赋,只是这类人太少或者他们的空闲时间太少。

首先就是需要有这种内在动力,至于其它的,边做边学就非常好了。这又是我想要指出的优势之一:审核补丁能够使你成为更出色的代码开发者。

依你之见,是否有那么一个受欢迎的大项目在扩张这方面做的很好,可以供我们借鉴?

我还真不知道有这么一个项目,如果有的话随时借鉴。

我很专注于 Linux 内核,所以可能会存在一些偏见。然而在我看来,Linux 内核项目在规模大小、贡献数量和多样性方面真的很特别。所以当我想要找另一个项目来寻找灵感以便改善工作流是很正常的想法,目前我们的扩张问题真的比较特别。而且我发现,看看其他子系统在内核中做了什么是一个很有的方法。

你曾说安全问题是我们每个人都该想到的事情,那用户应该做些什么来避免或者改善安全问题的危险?

在今年(2016年)柏林 Linux 峰会我的谈话是针对开发层面的。安全隐患可能来自于没有正确审核的补丁中。我并不想要用户亲自解决这种问题,我更希望这些安全问题永远不会出现。当然这是不可能的,但这仍然是我处理问题所首选的方法。

我很好奇这个庞大社区如何改善这些问题。是否有你希望用户定期以文件形式提交的某些类型的错误报告?需要定期检查的区域却因为某些原因没有注意到的?

我们并不缺少错误报告。我所担心的是:由于代码审核人员的短缺造成补丁不完整,从而导致更多的错误报告。所以,到时候不仅需要处理大量的贡献,还需要处理更多错误或者进行版本回退。

你是否还有什么事情希望我们读者知道,以了解你所在的努力?

Linux 内核的特殊性,常常让我牢记着,在底层它就是代码而已。


via: https://opensource.com/business/16/10/linux-kernel-review

作者:Deb Nicholson 译者:GHLandy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

网络的最重要用途之一就是实现文件共享的目的。现在,虽然有多种方式可以让在同一网络中的 Linux 和 Windows 以及 MacOS X 用户之间共享文件,但这篇文章,我们只打算介绍 NitroShare。这是一款跨平台、开源以及易于使用的应用软件,可以在本地网络(内网)中共享文件。

NitroShare 大大简化了本地网络的文件共享操作,一旦安装上,它就会与操作系统无缝集成。Ubuntu 系统中,从应用程序显示面板中可以简单的打开,在 Windows 系统中,在系统托盘中可以找到它。

此外,在装有 NitroShare 的机器上,它会自动检测在同一网段的其它安装了它的设备,用户只需选择好需要传输到的设备,就可以直接向其传输文件。

NitroShare 的特性如下:

  • 跨平台,即可以运行于 Linux, Windows 和 MacOS X 系统
  • 设置容易,无需配置
  • 易于使用
  • 支持在本地网络上自动发现运行着 Nitroshare 设备的能力
  • 安全性上支持可选的 TLS ( 传输层安全协议 ( Transport Layer Security ) ) 编码传输方式
  • 支持网络高速传输功能
  • 支持文件和目录(Windows 上的文件夹)传输
  • 支持对发送文件、连接设备等这些的桌面通知功能

最新版本的 NitroShare 是使用 Qt5 开发的,它做了一些重大的改进,例如:

  • 用户界面焕然一新
  • 简化设备发现过程
  • 移除不同版本传输文件大小的限制
  • 为了使用方便,已经去除配置向导

Linux 系统中安装 Nitroshare

NitroShare 可运行于各种各样的现代 Linux 发行版和桌面环境。

Debian Sid 和 Ubuntu 16.04+

NitroShare 已经包含在 Debian 和 Ubuntu 的软件源仓库中,所以可以很容易的就安装上,使用如下命令:

$ sudo apt-get install nitroshare

但安装的版本可能已经过期了。要安装最新的版本的话,可按照如下的命令添加最新的 PPA。

$ sudo apt-add-repository ppa:george-edison55/nitroshare
$ sudo apt-get update
$ sudo apt-get install nitroshare

Fedora 24-23

最近,NitroShare 已经包含在 Fedora 源仓库中了,可以按如下命令安装:

$ sudo dnf install nitroshare

Arch Linux

在 Arch Linux 系统中,NitroShare 包在 AUR 上已经可用了,可以用如下命令来构建/安装:

# wget https://aur.archlinux.org/cgit/aur.git/snapshot/nitroshare.tar.gz
# tar xf nitroshare.tar.gz
# cd nitroshare
# makepkg -sri

Linux 中使用 NitroShare

注意:如我前面所述,在本地网络中,您想要共享文件的其它机器设备都需要安装上 NitroShare 并运行起来。

在成功安装后,在系统 dash 面板里或系统菜单里搜索 NitroShare,然后启动。NitroShare 非常容易使用,你可从该应用或托盘图标上找到“发送文件”、“发送目录”、“查看传输”等选项。

选择文件后,点击“打开”继续选择目标设备,如下图所示。如果在本地网络有设备正的运行 NitroShare 的话,选择相应的设备然后点击“确定”。

如果需要,在 NitroShare 的配置 -- 通用标签页里,您可以增加设备的名字,设置默认文件下载位置,在高级设置里您能设置端口、缓存、超时时间等等。

主页:https://nitroshare.net/index.html

这就是所要介绍的,如果您有关于 Nitroshare 的任何问题,可以在下面的评论区给我们分享。您也可以给我们提供一些建议,告诉一些可能我们不知道的很不错的跨平台文件共享应用程序。


via: http://www.tecmint.com/nitroshare-share-files-between-linux-ubuntu-windows/

作者:Aaron Kili 译者:runningwater 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

作为 Vi 的升级版,Vim 毫无争议是 Linux 中最受欢迎的命令行编辑器之一。除了是一个多功能编辑器外,世界各地的软件开发者将 Vim 当做 IDE( 集成开发环境 ( Integrated Development Environment ) )来使用。

事实上,因为 Vim 可以通过插件来扩展其自身功能才使得它如此功能强大。不用说,肯定有那么几个 Vim 插件是旨在提高用户的编程体验的。

特别是对于刚刚使用 Vim 或者使用 Vim 做开发的的软件开发者来说,我们将在本教程中讨论一些非常有用的 Vim 插件,具体请看例示。

请注意:本教程中列举的所有例示、命令和说明都是在 Ubuntu 16.04 环境下进行测试的,并且,我们使用的 Vim 版本是 7.4。

插件安装设置

这是为新用户准备的,假设他们不知道如何安装 Vim 插件。所以,首先,就是给出一些完成安装设置的步骤。

  • 在你的家目录下创建 .vim 目录,并在其中创建子目录 autoload 和 bundle。
  • 然后,在 autoload 放置 pathogen.vim 文件,这个文件可以从此处 下载。
  • 最后,在你的家目录创建 .vimrc 文件,并添加以下内容。
call pathogen#infect() 

Vim plugin installation

至此,你已完成了 Vim 插件安装的准备工作。

注意:我们已经讨论了使用 Pathogen 管理 Vim 插件。当然还有其他的插件管理工具——欲了解,请访问此处。

现在已经全部设置完毕,就让我们来讨论两个好用的 Vim 插件吧。

Vim 标签侧边栏(Tagbar)插件

首先就是标签侧边栏(Tagbar)插件。该插件能够让你浏览源文件包含的标签,从而提供该源文件的结构简览。其官网的插件说明是这样说的:“它通过创建侧边栏,然后以一定顺序展示从当前文件以 ctags 提取的标签来完成这一功能。这意味着,比如,C++ 中的方法将展示在其自身所定义在的类里边。”

听起来很酷,不是吗?让我们来看看该怎么安装它。

标签侧边栏(Tagbar)的安装过程是相当容易的——你只需要运行下列命令:

cd ~/.vim/bundle/

git clone git://github.com/majutsushi/tagbar

安装完之后就可以使用了,你可以在 Vim 中打开一个 .cpp 文件来测试它:进入命令模式,然后运行 :TagbarOpen 命令。以下是运行 :TagbarOpen 命令之后出现侧边栏(右侧) 的效果图。

Vim tagbar plugin

使用 :TagbarClose 可以关闭侧边栏。值得一提的是,可以使用 :TagbarOpen fj 命令打开侧边栏来打开它的跳转(shift control)功能。也就是说,你可以很方便的浏览当前文件包含的标签——在对应的标签上按下 Enter 键,然后在左侧的源代码窗口跳转到对应的位置。

假如你想要反复地开关侧边栏,你可以使用 :TagbarToggle 命令,而不用交替的使用 :TagbarOpen 和 :TagbarClose 命令。

如果你觉得输入这些命令很费时间,你可以为 :TagbarToggle 命令创建快捷键。比如,添加以下内容到 .vimrc 文件中:

nmap <F8> :TagbarToggle<CR>

这样,你就可以用 F8 来切换标签侧边栏(Tagbar)了。

更进一步,有时候你可能会注意到某个标签前边有一个 +、- 或者 # 符号。比如,以下截图(取自该插件的官网)展示了一些前边有 + 号的标签。

Toggle Tagbar window

这些符号基本是用来表明一个特定标签的可见性信息。特别是 + 表示该类是 public 的,而 - 表示一个 private 类。# 则是表示一个 protected 类。

以下是使用标签侧边栏(Tagbar)的一些注意事项:

  • 该插件的官网早就有说明:“标签侧边栏(Tagbar)并非是管理标签(tags)文件而设计,它只是在内存中动态创建所需的标签,而非创建任何文件。标签(tags)文件的管理有其他插件提供支持。”
  • 低于 7.0.167 版本的 Vim 和标签侧边栏(Tagbar)插件存在着一个兼容性问题。根据官网:“如果你受到此问题的影响,请使用代替版:下载 zip 压缩包。这对应到 2.2 版本,但由于大量的依赖变更,它可能不会再升级。”
  • 如果你在加载该插件时遇到这样的错误:未找到 ctags!(Tagbar: Exuberant ctags not found!)。你可以从 此处下载并安装 ctags 来修复错误。
  • 获取更多信息请访问 这里。

Vim 界定符自动补齐(delimitMate)插件

下一个要介绍的插件就是界定符自动补齐(delimitMate)。该插件在 Vim 插入模式下提供引号、圆括号和方括号等界定符自动补齐功能。

该插件官网说:“它同时也提供一些相关的特性让你在输入模式下变得更加便捷,比如语法纠错(在注释区或者其他的可配置区不会自动插入结束界定符)、回车和空格填充(默认关闭)等。”

安装步骤与之前介绍的相似:

cd ~/.vim/bundle/

git clone git://github.com/Raimondi/delimitMate.git

一旦你成功安装这个插件(即上述命令执行成功),你就不需要进行任何配置了——当 Vim 启动时会自动加载这个插件。

至此,在你使用 Vim 的任何时候,只要你输入一个双引号、单引号、单号、圆括号、方括号,它们都会自动补齐。

你可以自己配置界定符自动补齐(delimitMate)。比如,你可以添加需要自动补齐的符号列表,阻止自动加载该插件,对指定类型文件关闭该插件等。想了解如何配置这些(或者其他更多的配置),请阅读该插件的详细文档——运行 :help delimitMate 即可。

上述命令会将你的 Vim 窗口水平分割成两个,上边一个包含我们所说的文档。

Vim deliMate Plugin

结论

本文之中提到的两个插件,Tagbar 需要花费较多时间来适应——你应该会同样这个说法。但只要正确设置好它(这意味着你像是有了快捷键一样方便),就容易使用了。至于 delimitMate,不需要任何要求就可以上手。

本教程就是向你展示 Vim 如何高效能的想法。除了本文中提及的,仍然还有许多开发者可用的插件,我们将在下一个部分进行讨论。假如你正在使用一个关于开发的 Vim 插件,并希望广为人知,请在下方留下评论。

我们将在本教程的第二部分讲到 语法高亮插件:Syntastic。


via: https://www.howtoforge.com/tutorial/vim-editor-plugins-for-software-developers/

作者:Ansh 译者:GHLandy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

近来,在计算领域出现了很多关于 serverless 的讨论。serverless 是一个概念,它允许你提供代码或可执行程序给某个服务,由服务来为你执行它们,而你无需自己管理服务器。这就是所谓的 执行即服务 ( execution-as-a-service ) ,它带来了很多机会,同时也遇到了它独有的挑战。

简短回忆下计算领域的发展

早期,出现了……好吧,这有点复杂。很早的时候,出现了机械计算机,后来又有了埃尼阿克 ENIAC(Electronic Numerical Integrator And Computer,很早的电子计算机),但是都没有规模生产。直到大型机出现后,计算领域才快速发展。

  • 上世纪 50 年代 - 大型机
  • 上世纪 60 年代 - 微型机
  • 1994 - 机架服务器
  • 2001 - 刀片服务器
  • 本世纪初 - 虚拟服务器
  • 2006 - 服务器云化
  • 2013 - 容器化
  • 2014 - serverless(计算资源服务化)
这些日期是大概的发布或者流行日期,无需和我争论时间的准确性。

计算领域的演进趋势是执行的功能单元越来越小。每一次演进通常都意味着运维负担的减小和运维灵活性的增加。

发展前景

喔,Serverless!但是,serverless 能给我们带来什么好处? 我们将面临什么挑战呢?

未执行代码时无需付费。我认为,这是个巨大的卖点。当无人访问你的站点或用你的 API 时,你无需付钱。没有持续支出的基础设施成本,仅仅支付你需要的部分。换句话说,这履行了云计算的承诺:“仅仅支付你真正用的资源”。

无需维护服务器,也无需考虑服务器安全。服务器的维护和安全将由你的服务提供商来处理(当然,你也可以架设自己的 serverless 主机,只是这似乎是在向错误的方向前进)。由于你的执行时间也是受限的,安全补丁也被简化了,因为完全不需要重启。这些都应该由你的服务提供商无缝地处理。

无限的可扩展性。这是又一个大的好处。假设你又开发了一个 Pokemon Go, 与其频繁地把站点下线维护升级,不如用 serverless 来不断地扩展。当然,这也是个双刃剑,大量的账单也会随之而来。如果你的业务的利润强依赖于站点上线率的话,serverless 确实能帮上忙。

强制的微服务架构。这也有两面性,一方面,微服务似乎是一种好的构建灵活可扩展的、容错的架构的方式。另一方面,如果你的业务没有按照这种方式设计,你将很难在已有的架构中引入 serverless。

但是现在你被限制在他们的平台上

受限的环境。你只能用服务提供商提供的环境,你想在 Rust 中用 serverless?你可能不会太幸运。

受限的预装包。你只有提供商预装的包。但是你或许能够提供你自己的包。

受限的执行时间。你的 Function 只可以运行这么长时间。如果你必须处理 1TB 的文件,你可能需要有一个解决办法或者用其他方案。

强制的微服务架构。参考上面的描述。

受限的监视和诊断能力。例如,你的代码在干什么? 在 serverless 中,基本不可能在调试器中设置断点和跟踪流程。你仍然可以像往常一样记录日志并发出统计度量,但是这带来的帮助很有限,无法定位在 serverless 环境中发生的难点问题。

竞争领域

自从 2014 年出现 AWS Lambda 以后,serverless 的提供商已经增加了一些。下面是一些主流的服务提供商:

  • AWS Lambda - 起步最早的
  • OpenWhisk - 在 IBM 的 Bluemix 云上可用
  • Google Cloud Functions
  • Azure Functions

这些平台都有它们的相对优势和劣势(例如,Azure 支持 C#,或者紧密集成在其他提供商的平台上)。这里面最大的玩家是 AWS。

通过 AWS 的 Lambda 和 API Gateway 构建你的第一个 API

我们来试一试 serverless。我们将用 AWS Lambda 和 API Gateway 来构建一个能返回 Jimmy 所说的“Guru Meditations”的 API。

所有代码在 GitHub 上可以找到。

API文档:

POST /
{
    "status": "success",
    "meditation": "did u mention banana cognac shower"
}

怎样组织工程文件

文件结构树:

.
├── LICENSE
├── README.md
├── server
│   ├── __init__.py
│   ├── meditate.py
│   └── swagger.json
├── setup.py
├── tests
│   └── test_server
│       └── test_meditate.py
└── tools
    ├── deploy.py
    ├── serve.py
    ├── serve.sh
    ├── setup.sh
    └── zip.sh

AWS 中的信息(想了解这里究竟在做什么的详细信息,可查看源码 tools/deploy.py)。

  • API。实际构建的对象。它在 AWS 中表示为一个单独的对象。
  • 执行角色。在 AWS 中,每个 Function 作为一个单独的角色执行。在这里就是 meditations。
  • 角色策略。每个 Function 作为一个角色执行,每个角色需要权限来干活。我们的 Lambda Function 不干太多活,故我们只添加一些日志记录权限。
  • Lambda Function。运行我们的代码的地方。
  • Swagger。 Swagger 是 API 的规范。API Gateway 支持解析 swagger 的定义来为 API 配置大部分资源。
  • 部署。API Gateway 提供部署的概念。我们只需要为我们的 API 用一个就行(例如,所有的都用生产或者 yolo等),但是得知道它们是存在的,并且对于真正的产品级服务,你可能想用开发和暂存环境。
  • 监控。在我们的业务崩溃的情况下(或者因为使用产生大量账单时),我们想以云告警查看方式为这些错误和费用添加一些监控。注意你应该修改 tools/deploy.py 来正确地设置你的 email。

代码

Lambda Function 将从一个硬编码列表中随机选择一个并返回 guru meditations,非常简单:

import logging
import random

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def handler(event, context):

    logger.info(u"received request with id '{}'".format(context.aws_request_id))

    meditations = [
    "off to a regex/",
    "the count of machines abides",
    "you wouldn't fax a bat",
    "HAZARDOUS CHEMICALS + RKELLY",
    "your solution requires a blood eagle",
    "testing is broken because I'm lazy",
    "did u mention banana cognac shower",
    ]

    meditation = random.choice(meditations)

    return {
        "status": "success",
        "meditation": meditation,
    }

deploy.py 脚本

这个脚本相当长,我没法贴在这里。它基本只是遍历上述“AWS 中的信息”下的项目,确保每项都存在。

我们来部署这个脚本

只需运行 ./tools/deploy.py。

基本完成了。不过似乎在权限申请上有些问题,由于 API Gateway 没有权限去执行你的 Function,所以你的 Lambda Function 将不能执行,报错应该是“Execution failed due to configuration error: Invalid permissions on Lambda function”。我不知道怎么用 botocore 添加权限。你可以通过 AWS console 来解决这个问题,找到你的 API, 进到 /POST 端点,进到“integration request”,点击“Lambda Function”旁边的编辑图标,修改它,然后保存。此时将弹出一个窗口提示“You are about to give API Gateway permission to invoke your Lambda function”, 点击“OK”。

当你完成后,记录下 ./tools/deploy.py 打印出的 URL,像下面这样调用它,然后查看你的新 API 的行为:

$ curl -X POST https://a1b2c3d4.execute-api.us-east-1.amazonaws.com/prod/
{"status": "success", "meditation": "the count of machines abides"}

本地运行

不幸的是,AWS Lambda 没有好的方法能在本地运行你的代码。在这个例子里,我们将用一个简单的 flask 服务器来在本地托管合适的端点,并调用 handler 函数。

from __future__ import absolute_import

from flask import Flask, jsonify

from server.meditate import handler

app = Flask(__name__)

@app.route("/", methods=["POST"])
def index():

    class FakeContext(object):
        aws_request_id = "XXX"

    return jsonify(**handler(None, FakeContext()))

app.run(host="0.0.0.0")

你可以在仓库中用 ./tools/serve.sh 运行它,像这样调用:

$ curl -X POST http://localhost:5000/
{
    "meditation": "your solution requires a blood eagle",
    "status": "success"
}

测试

你总是应该测试你的代码。我们的测试方法是导入并运行我们的 handler 函数。这是最基本的 python 测试方法:

from __future__ import absolute_import

import unittest

from server.meditate import handler

class SubmitTestCase(unittest.TestCase):

    def test_submit(self):

        class FakeContext(object):

            aws_request_id = "XXX"

        response = handler(None, FakeContext())

        self.assertEquals(response["status"], "success")
        self.assertTrue("meditation" in response)

你可以在仓库里通过 nose2 运行这个测试代码。

更多前景

  • 和 AWS 服务的无缝集成。通过 boto,你可以完美地轻易连接到任何其他的 AWS 服务。你可以轻易地让你的执行角色用 IAM 访问这些服务。你可以从 S3 取文件或放文件到 S3,连接到 Dynamo DB,调用其他 Lambda Function,等等。
  • 访问数据库。你也可以轻易地访问远程数据库。在你的 Lambda handler 模块的最上面连接数据库,并在handler 函数中执行查询。你很可能必须从它的安装位置上传相关的包内容才能使它正常工作。可能你也需要静态编译某些库。
  • 调用其他 webservices。API Gateway 也是一种把 webservices 的输出从一个格式转换成另一个格式的方法。你可以充分利用这个特点通过不同的 webservices 来代理调用,或者当业务变更时提供后向兼容能力。

via: http://blog.ryankelly.us/2016/08/07/going-serverless-with-aws-lambda-and-api-gateway.html

作者:Ryan Kelly 译者:messon007 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出