資料科學公司在決定雇傭時越來越關注你在資料科學方面的作品集portfolio。這其中的一個原因是,這樣的作品集是判斷某人的實際技能的最好的方法。好消息是建構這樣的作品集完全要看你自己。隻要你在這方面付出了努力,你一定可以取得讓這些公司欽佩的作品集。
建構高品質的作品集的第一步就是知道需要什麼技能。公司想要在資料科學方面擁有的、他們希望你能夠運用的主要技能有:
溝通能力
協作能力
技術能力
資料推理能力
動機和主動性
<a target="_blank"></a>
不管怎樣,有時候你會被要求建立一個具有操作價值的項目。具有操作價值的項目将直接影響到公司的日常業務,它會使用不止一次,經常是許多人使用。這個任務可能像這樣 “建立一個算法來預測周轉率”或者“建立一個模型來自動對我們的文章打标簽”。在這種情況下,技術能力比講故事更重要。你必須能夠得到一個資料集,并且了解它,然後建立腳本處理該資料。這個腳本要運作的很快,占用系統資源很小。通常它可能要運作很多次,腳本的可使用性也很重要,并不僅僅是一個示範版。可使用性是指整合進操作流程,并且甚至是是面向使用者的。
端對端項目的主要組成部分:
了解背景
浏覽資料并找出細微差别
建立結構化項目,那樣比較容易整合進操作流程
運作速度快、占用系統資源小的高性能代碼
寫好安裝和使用文檔以便其他人用

github 上的這個項目
這裡還有記憶體和性能限制的問題,比如你有幾千兆的資料,而且當你需要找到一些差異時,就需要對資料集一遍遍運作算法。
一個好的可操作的資料集可以讓你建構一系列腳本來轉換資料、動态地回答問題。一個很好的例子是股票價格資料集,當股市關閉時,就會給算法提供新的資料。這可以讓你預測明天的股價,甚至預測收益。這不是講故事,它帶來的是真金白銀。
一些找到資料集的好地方:
當你檢視這些資料集時,想一下人們想要在這些資料集中得到什麼答案,哪怕這些問題隻想過一次(“房價是如何與标準普爾 500 指數關聯的?”),或者更進一步(“你能預測股市嗎?”)。這裡的關鍵是更進一步地找出問題,并且用相同的代碼在不同輸入(不同的資料)上運作多次。
房利美釋出了兩種類型的資料 – 它獲得的貸款的資料,和貸款償還情況的資料。在理想的情況下,有人向貸款人借錢,然後還款直到還清。不管怎樣,有些人多次不還,進而喪失了抵押品贖回權。抵押品贖回權是指沒錢還了被銀行把房子給收走了。房利美會追蹤誰沒還錢,并且哪個貸款需要收回抵押的房屋(取消贖回權)。每個季度會釋出此資料,釋出的是滞後一年的資料。目前可用是 2015 年第一季度資料。
“貸款資料”是由房利美釋出的貸款發放的資料,它包含借款人的資訊、信用評分,和他們的家庭貸款資訊。“執行資料”,貸款發放後的每一個季度公布,包含借貸人的還款資訊和是否喪失抵押品贖回權的狀态,一個“貸款資料”的“執行資料”可能有十幾行。可以這樣了解,“貸款資料”告訴你房利美所控制的貸款,“執行資料”包含該貸款一系列的狀态更新。其中一個狀态更新可以告訴我們一筆貸款在某個季度被取消贖回權了。
一個沒有及時還貸的房子就這樣的被賣了
這裡有幾個我們可以去分析房利美資料集的方向。我們可以:
預測房屋的銷售價格。
預測借款人還款曆史。
在獲得貸款時為每一筆貸款打分。
最重要的事情是堅持單一的角度。同時關注太多的事情很難做出效果。選擇一個有着足夠細節的角度也很重要。下面的角度就沒有太多細節:
找出哪些銀行将貸款出售給房利美的多數被取消贖回權。
計算貸款人的信用評分趨勢。
找到哪些類型的家庭沒有償還貸款的能力。
找到貸款金額和抵押品價格之間的關系。
上面的想法非常有趣,如果我們關注于講故事,那是一個不錯的角度,但是不是很适合一個操作性項目。
在房利美資料集中,我們将僅使用申請貸款時有的那些資訊來預測貸款是否将來會被取消贖回權。實際上, 我們将為每一筆貸款建立“分數”來告訴房利美買還是不買。這将給我們打下良好的基礎,并将組成這個漂亮的作品集的一部分。
我們來簡單看一下原始資料檔案。下面是 2012 年 1 季度前幾行的貸款資料:
<code>100000853384|r|other|4.625|280000|360|02/2012|04/2012|31|31|1|23|801|n|c|sf|1|i|ca|945||frm|</code>
<code>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</code>
<code>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</code>
下面是 2012 年 1 季度的前幾行執行資料:
<code>100000853384|03/01/2012|other|4.625||0|360|359|03/2042|41860|0|n||||||||||||||||</code>
<code>100000853384|04/01/2012||4.625||1|359|358|03/2042|41860|0|n||||||||||||||||</code>
<code>100000853384|05/01/2012||4.625||2|358|357|03/2042|41860|0|n||||||||||||||||</code>
在開始編碼之前,花些時間真正了解資料是值得的。這對于操作性項目優為重要,因為我們沒有互動式探索資料,将很難察覺到細微的差别,除非我們在前期發現他們。在這種情況下,第一個步驟是閱讀房利美站點的資料:
<a href="http://www.fanniemae.com/portal/funding-the-market/data/loan-performance-data.html" target="_blank">概述</a>
<a href="https://loanperformancedata.fanniemae.com/lppub-docs/lppub_glossary.pdf" target="_blank">有用的術語表</a>
<a href="https://loanperformancedata.fanniemae.com/lppub-docs/lppub_faq.pdf" target="_blank">常見問答</a>
<a href="https://loanperformancedata.fanniemae.com/lppub-docs/lppub_file_layout.pdf" target="_blank">貸款和執行檔案中的列</a>
<a href="https://loanperformancedata.fanniemae.com/lppub-docs/acquisition-sample-file.txt" target="_blank">貸款資料檔案樣本</a>
<a href="https://loanperformancedata.fanniemae.com/lppub-docs/performance-sample-file.txt" target="_blank">執行資料檔案樣本</a>
在看完這些檔案後後,我們了解到一些能幫助我們的關鍵點:
從 2000 年到現在,每季度都有一個貸款和執行檔案,因資料是滞後一年的,是以到目前為止最新資料是 2015 年的。
這些檔案是文本格式的,采用管道符号<code>|</code>進行分割。
這些檔案是沒有表頭的,但我們有個檔案列明了各列的名稱。
所有一起,檔案包含 2200 萬個貸款的資料。
由于執行資料的檔案包含過去幾年獲得的貸款的資訊,在早些年獲得的貸款将有更多的執行資料(即在 2014 獲得的貸款沒有多少曆史執行資料)。
這些小小的資訊将會為我們節省很多時間,因為這樣我們就知道如何構造我們的項目和利用這些資料了。
在我們開始下載下傳和探索資料之前,先想一想将如何構造項目是很重要的。當建立端到端項目時,我們的主要目标是:
建立一個可行解決方案
有一個快速運作且占用最小資源的解決方案
容易可擴充
寫容易了解的代碼
寫盡量少的代碼
為了實作這些目标,需要對我們的項目進行良好的構造。一個結構良好的項目遵循幾個原則:
分離資料檔案和代碼檔案
從原始資料中分離生成的資料。
有一個 <code>readme.md</code> 檔案幫助人們安裝和使用該項目。
有一個 <code>requirements.txt</code> 檔案列明項目運作所需的所有包。
有一個單獨的 <code>settings.py</code> 檔案列明其它檔案中使用的所有的設定
例如,如果從多個 <code>python</code> 腳本讀取同一個檔案,讓它們全部 <code>import</code> 設定并從一個集中的地方獲得檔案名是有用的。
有一個 <code>.gitignore</code> 檔案,防止大的或密碼檔案被送出。
分解任務中每一步可以單獨執行的步驟到單獨的檔案中。
例如,我們将有一個檔案用于讀取資料,一個用于建立特征,一個用于做出預測。
儲存中間結果,例如,一個腳本可以輸出下一個腳本可讀取的檔案。
這使我們無需重新計算就可以在資料處理流程中進行更改。
我們的檔案結構大體如下:
<code>loan-prediction</code>
<code>├── data</code>
<code>├── processed</code>
<code>├── .gitignore</code>
<code>├── readme.md</code>
<code>├── requirements.txt</code>
<code>├── settings.py</code>
首先,我們需要建立一個 <code>loan-prediction</code> 檔案夾,在此檔案夾下面,再建立一個 <code>data</code> 檔案夾和一個<code>processed</code> 檔案夾。<code>data</code> 檔案夾存放原始資料,<code>processed</code> 檔案夾存放所有的中間計算結果。
<code>data</code>
<code>processed</code>
至此,我們僅需在 <code>readme.md</code> 檔案中添加簡單的描述:
<code>loan prediction</code>
<code>-----------------------</code>
<code></code>
<code>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).</code>
現在,我們可以建立 <code>requirements.txt</code> 檔案了。這會幫助其它人可以很友善地安裝我們的項目。我們還不知道我們将會具體用到哪些庫,但是以下幾個庫是需要的:
<code>pandas</code>
<code>matplotlib</code>
<code>scikit-learn</code>
<code>numpy</code>
<code>ipython</code>
<code>scipy</code>
最後,我們可以建立一個空白的 <code>settings.py</code> 檔案,因為我們的項目還沒有任何設定。
一旦我們有了項目的基本架構,我們就可以去獲得原始資料。
為了達到我們這個文章的目的,我們将要下載下傳從 2012 年 1 季度到 2015 年 1 季度的所有資料。接着我們需要解壓所有的檔案。解壓過後,删掉原來的 .zip 格式的檔案。最後,loan-prediction 檔案夾看起來應該像下面的一樣:
<code>│ ├── acquisition_2012q1.txt</code>
<code>│ ├── acquisition_2012q2.txt</code>
<code>│ ├── performance_2012q1.txt</code>
<code>│ ├── performance_2012q2.txt</code>
<code>│ └── ...</code>
有兩個問題讓我們的資料難以現在就使用:
貸款資料和執行資料被分割在多個檔案中
每個檔案都缺少列名标題
在我們開始使用資料之前,我們需要首先明白我們要在哪裡去存一個貸款資料的檔案,同時到哪裡去存儲一個執行資料的檔案。每個檔案僅僅需要包括我們關注的那些資料列,同時擁有正确的列名标題。這裡有一個小問題是執行資料非常大,是以我們需要嘗試去修剪一些資料列。
第一步是向 <code>settings.py</code> 檔案中增加一些變量,這個檔案中同時也包括了我們原始資料的存放路徑和處理出的資料存放路徑。我們同時也将添加其他一些可能在接下來會用到的設定資料:
<code>data_dir = "data"</code>
<code>processed_dir = "processed"</code>
<code>minimum_tracking_quarters = 4</code>
<code>target = "foreclosure_status"</code>
<code>non_predictors = [target, "id"]</code>
<code>cv_folds = 3</code>
第二步是建立一個檔案名為 <code>assemble.py</code>,它将所有的資料分為 2 個檔案。當我們運作 <code>python assemble.py</code>,我們在處理資料檔案的目錄會獲得 2 個資料檔案。
<code>headers = {</code>
<code>"acquisition": [</code>
<code>"id",</code>
<code>"channel",</code>
<code>"seller",</code>
<code>"interest_rate",</code>
<code>"balance",</code>
<code>"loan_term",</code>
<code>"origination_date",</code>
<code>"first_payment_date",</code>
<code>"ltv",</code>
<code>"cltv",</code>
<code>"borrower_count",</code>
<code>"dti",</code>
<code>"borrower_credit_score",</code>
<code>"first_time_homebuyer",</code>
<code>"loan_purpose",</code>
<code>"property_type",</code>
<code>"unit_count",</code>
<code>"occupancy_status",</code>
<code>"property_state",</code>
<code>"zip",</code>
<code>"insurance_percentage",</code>
<code>"product_type",</code>
<code>"co_borrower_credit_score"</code>
<code>],</code>
<code>"performance": [</code>
<code>"reporting_period",</code>
<code>"servicer_name",</code>
<code>"loan_age",</code>
<code>"months_to_maturity",</code>
<code>"maturity_date",</code>
<code>"msa",</code>
<code>"delinquency_status",</code>
<code>"modification_flag",</code>
<code>"zero_balance_code",</code>
<code>"zero_balance_date",</code>
<code>"last_paid_installment_date",</code>
<code>"foreclosure_date",</code>
<code>"disposition_date",</code>
<code>"foreclosure_costs",</code>
<code>"property_repair_costs",</code>
<code>"recovery_costs",</code>
<code>"misc_costs",</code>
<code>"tax_costs",</code>
<code>"sale_proceeds",</code>
<code>"credit_enhancement_proceeds",</code>
<code>"repurchase_proceeds",</code>
<code>"other_foreclosure_proceeds",</code>
<code>"non_interest_bearing_balance",</code>
<code>"principal_forgiveness_balance"</code>
<code>]</code>
<code>}</code>
接下來一步是定義我們想要保留的資料列。因為我們要預測一個貸款是否會被撤回,我們可以丢棄執行資料中的許多列。我們将需要保留貸款資料中的所有資料列,因為我們需要盡量多的了解貸款發放時的資訊(畢竟我們是在預測貸款發放時這筆貸款将來是否會被撤回)。丢棄資料列将會使我們節省下記憶體和硬碟空間,同時也會加速我們的代碼。
<code>select = {</code>
<code>"acquisition": headers["acquisition"],</code>
<code>"foreclosure_date"</code>
下一步,我們将編寫一個函數來連接配接資料集。下面的代碼将:
引用一些需要的庫,包括 <code>settings</code>。
定義一個函數 <code>concatenate</code>,目的是:
擷取到所有 <code>data</code> 目錄中的檔案名。
周遊每個檔案。
如果檔案不是正确的格式 (不是以我們需要的格式作為開頭),我們将忽略它。
設定分隔符為<code>|</code>,以便所有的字段能被正确讀出。
資料沒有标題行,是以設定 <code>header</code> 為 <code>none</code> 來進行标示。
從 <code>headers</code> 字典中設定正确的标題名稱 – 這将會是我們的 dataframe 中的資料列名稱。
僅選擇我們加在 <code>select</code> 中的 dataframe 的列。
把所有的 dataframe 共同連接配接在一起。
把已經連接配接好的 dataframe 寫回一個檔案。
<code>import os</code>
<code>import settings</code>
<code>import pandas as pd</code>
<code>def concatenate(prefix="acquisition"):</code>
<code>files = os.listdir(settings.data_dir)</code>
<code>full = []</code>
<code>for f in files:</code>
<code>if not f.startswith(prefix):</code>
<code>continue</code>
<code>data = pd.read_csv(os.path.join(settings.data_dir, f), sep="|", header=none, names=headers[prefix], index_col=false)</code>
<code>data = data[select[prefix]]</code>
<code>full.append(data)</code>
<code>full = pd.concat(full, axis=0)</code>
<code>full.to_csv(os.path.join(settings.processed_dir, "{}.txt".format(prefix)), sep="|", header=select[prefix], index=false)</code>
我們可以通過調用上面的函數,通過傳遞的參數 <code>acquisition</code> 和 <code>performance</code> 兩次以将所有的貸款和執行檔案連接配接在一起。下面的代碼将:
僅在指令行中運作 <code>python assemble.py</code> 時執行。
将所有的資料連接配接在一起,并且産生 2 個檔案:
<code>processed/acquisition.txt</code>
<code>processed/performance.txt</code>
<code>if __name__ == "__main__":</code>
<code>concatenate("acquisition")</code>
<code>concatenate("performance")</code>
我們現在擁有了一個漂亮的,劃分過的 <code>assemble.py</code> 檔案,它很容易執行,也容易建立。通過像這樣把問題分解為一塊一塊的,我們建構工程就會變的容易許多。不用一個可以做所有工作的淩亂腳本,我們定義的資料将會在多個腳本間傳遞,同時使腳本間完全的彼此隔離。當你正在一個大的項目中工作時,這樣做是一個好的想法,因為這樣可以更加容易修改其中的某一部分而不會引起其他項目中不關聯部分産生預料之外的結果。
這将會在 <code>processed</code> 目錄下産生 2 個檔案:
<code>│ ├── acquisition.txt</code>
<code>│ ├── performance.txt</code>
<code>├── assemble.py</code>
接下來我們會計算來自 <code>processed/performance.txt</code> 中的值。我們要做的就是推測這些資産是否被取消贖回權。如果能夠計算出來,我們隻要看一下關聯到貸款的執行資料的參數 <code>foreclosure_date</code> 就可以了。如果這個參數的值是 <code>none</code>,那麼這些資産肯定沒有收回。為了避免我們的樣例中隻有少量的執行資料,我們會為每個貸款計算出執行資料檔案中的行數。這樣我們就能夠從我們的訓練資料中篩選出貸款資料,排除了一些執行資料。
下面是我認為貸款資料和執行資料的關系:
在上面的表格中,貸款資料中的每一行資料都關聯到執行資料中的多行資料。在執行資料中,在取消贖回權的時候 <code>foreclosure_date</code> 就會出現在該季度,而之前它是空的。一些貸款還沒有被取消贖回權,是以與執行資料中的貸款資料有關的行在 <code>foreclosure_date</code> 列都是空格。
我們需要計算 <code>foreclosure_status</code> 的值,它的值是布爾類型,可以表示一個特殊的貸款資料 <code>id</code> 是否被取消贖回權過,還有一個參數 <code>performance_count</code> ,它記錄了執行資料中每個貸款 <code>id</code> 出現的行數。
計算這些行數有多種不同的方法:
這種方法的優點是從文法上來說容易執行。
它的缺點需要讀取所有的 129236094 行資料,這樣就會占用大量記憶體,并且運作起來極慢。
這種方法的優點是容易了解。
缺點是需要讀取所有的 129236094 行資料。這樣會占用大量記憶體,并且運作起來極慢。
我們可以在疊代通路執行資料中的每一行資料,而且會建立一個單獨的計數字典。
這種方法的優點是資料不需要被加載到記憶體中,是以運作起來會很快且不需要占用記憶體。
缺點是這樣的話了解和執行上可能有點耗費時間,我們需要對每一行資料進行文法分析。
加載所有的資料會非常耗費記憶體,是以我們采用第三種方法。我們要做的就是疊代執行資料中的每一行資料,然後為每一個貸款 <code>id</code> 在字典中保留一個計數。在這個字典中,我們會計算出貸款 <code>id</code> 在執行資料中出現的次數,而且看看 <code>foreclosure_date</code> 是否是 <code>none</code> 。我們可以檢視 <code>foreclosure_status</code> 和<code>performance_count</code> 的值 。
我們會建立一個 <code>annotate.py</code> 檔案,檔案中的代碼可以計算這些值。我們會使用下面的代碼:
導入需要的庫
定義一個函數 <code>count_performance_rows</code> 。
打開 <code>processed/performance.txt</code> 檔案。這不是在記憶體中讀取檔案而是打開了一個檔案辨別符,這個辨別符可以用來以行為機關讀取檔案。
疊代檔案的每一行資料。
使用分隔符<code>|</code>分開每行的不同資料。
檢查 <code>loan_id</code> 是否在計數字典中。
如果不存在,把它加進去。
<code>loan_id</code> 的 <code>performance_count</code> 參數自增 1 次,因為我們這次疊代也包含其中。
如果 <code>date</code> 不是 <code>none ,我們就會知道貸款被取消贖回權了,然後為</code>foreclosure_status` 設定合适的值。
<code>def count_performance_rows():</code>
<code>counts = {}</code>
<code>with open(os.path.join(settings.processed_dir, "performance.txt"), 'r') as f:</code>
<code>for i, line in enumerate(f):</code>
<code>if i == 0:</code>
<code># skip header row</code>
<code>loan_id, date = line.split("|")</code>
<code>loan_id = int(loan_id)</code>
<code>if loan_id not in counts:</code>
<code>counts[loan_id] = {</code>
<code>"foreclosure_status": false,</code>
<code>"performance_count": 0</code>
<code>counts[loan_id]["performance_count"] += 1</code>
<code>if len(date.strip()) > 0:</code>
<code>counts[loan_id]["foreclosure_status"] = true</code>
<code>return counts</code>
隻要我們建立了計數字典,我們就可以使用一個函數通過一個 <code>loan_id</code> 和一個 <code>key</code> 從字典中提取到需要的參數的值:
<code>def get_performance_summary_value(loan_id, key, counts):</code>
<code>value = counts.get(loan_id, {</code>
<code>})</code>
<code>return value[key]</code>
我們已經在 <code>annotate.py</code> 中添加了一些功能,現在我們來看一看資料檔案。我們需要将貸款到的資料轉換到訓練資料集來進行機器學習算法的訓練。這涉及到以下幾件事情:
轉換所有列為數字。
填充缺失值。
為每一行配置設定 <code>performance_count</code> 和 <code>foreclosure_status</code>。
移除出現執行資料很少的行(<code>performance_count</code> 計數低)。
我們有幾個列是文本類型的,看起來對于機器學習算法來說并不是很有用。然而,它們實際上是分類變量,其中有很多不同的類别代碼,例如 <code>r</code>,<code>s</code> 等等. 我們可以把這些類别标簽轉換為數值:
通過這種方法轉換的列我們可以應用到機器學習算法中。
還有一些包含日期的列 (<code>first_payment_date</code> 和 <code>origination_date</code>)。我們可以将這些日期放到兩個列中:
在下面的代碼中,我們将轉換貸款資料。我們将定義一個函數如下:
在 <code>acquisition</code> 中建立 <code>foreclosure_status</code> 列,并從 <code>counts</code> 字典中得到值。
在 <code>acquisition</code> 中建立 <code>performance_count</code> 列,并從 <code>counts</code> 字典中得到值。
将下面的列從字元串轉換為整數:
<code>channel</code>
<code>seller</code>
<code>first_time_homebuyer</code>
<code>loan_purpose</code>
<code>property_type</code>
<code>occupancy_status</code>
<code>property_state</code>
<code>product_type</code>
将 <code>first_payment_date</code> 和 <code>origination_date</code> 分别轉換為兩列:
通過斜杠分離列。
将第一部分分離成 <code>month</code> 列。
将第二部分分離成 <code>year</code> 列。
删除該列。
最後,我們得到 <code>first_payment_month</code>、<code>first_payment_year</code>、<code>rigination_month</code> 和<code>origination_year</code>。
所有缺失值填充為 <code>-1</code>。
<code>def annotate(acquisition, counts):</code>
<code>acquisition["foreclosure_status"] = acquisition["id"].apply(lambda x: get_performance_summary_value(x, "foreclosure_status", counts))</code>
<code>acquisition["performance_count"] = acquisition["id"].apply(lambda x: get_performance_summary_value(x, "performance_count", counts))</code>
<code>for column in [</code>
<code>"product_type"</code>
<code>]:</code>
<code>acquisition[column] = acquisition[column].astype('category').cat.codes</code>
<code>for start in ["first_payment", "origination"]:</code>
<code>column = "{}_date".format(start)</code>
<code>acquisition["{}_year".format(start)] = pd.to_numeric(acquisition[column].str.split('/').str.get(1))</code>
<code>acquisition["{}_month".format(start)] = pd.to_numeric(acquisition[column].str.split('/').str.get(0))</code>
<code>del acquisition[column]</code>
<code>acquisition = acquisition.fillna(-1)</code>
<code>acquisition = acquisition[acquisition["performance_count"] > settings.minimum_tracking_quarters]</code>
<code>return acquisition</code>
我們差不多準備就緒了,我們隻需要再在 <code>annotate.py</code> 添加一點點代碼。在下面代碼中,我們将:
定義一個函數來讀取貸款的資料。
定義一個函數來寫入處理過的資料到 <code>processed/train.csv</code>。
如果該檔案在指令行以 <code>python annotate.py</code> 的方式運作:
讀取貸款資料。
計算執行資料的計數,并将其賦予 <code>counts</code>。
轉換 <code>acquisition</code> dataframe。
将<code>acquisition</code> dataframe 寫入到 <code>train.csv</code>。
<code>def read():</code>
<code>acquisition = pd.read_csv(os.path.join(settings.processed_dir, "acquisition.txt"), sep="|")</code>
<code>def write(acquisition):</code>
<code>acquisition.to_csv(os.path.join(settings.processed_dir, "train.csv"), index=false)</code>
<code>acquisition = read()</code>
<code>counts = count_performance_rows()</code>
<code>acquisition = annotate(acquisition, counts)</code>
<code>write(acquisition)</code>
現在檔案夾看起來應該像這樣:
<code>│ ├── train.csv</code>
<code>├── annotate.py</code>
我們已經完成了訓練資料表的生成,現在我們需要最後一步,生成預測。我們需要找到誤差的标準,以及該如何評估我們的資料。在這種情況下,因為有很多的貸款沒有被取消贖回權,是以根本不可能做到精确的計算。
我們需要讀取訓練資料,并且計算 <code>foreclosure_status</code> 列的計數,我們将得到如下資訊:
<code>train = pd.read_csv(os.path.join(settings.processed_dir, "train.csv"))</code>
<code>train["foreclosure_status"].value_counts()</code>
<code>false 4635982</code>
<code>true 1585</code>
<code>name: foreclosure_status, dtype: int64</code>
因為隻有很少的貸款被取消贖回權,隻需要檢查正确預測的标簽的百分比就意味着我們可以建立一個機器學習模型,來為每一行預測 <code>false</code>,并能取得很高的精确度。相反,我們想要使用一個度量來考慮分類的不平衡,確定我們可以準确預測。我們要避免太多的誤報率(預測貸款被取消贖回權,但是實際沒有),也要避免太多的漏報率(預測貸款沒有别取消贖回權,但是實際被取消了)。對于這兩個來說,漏報率對于房利美來說成本更高,因為他們買的貸款可能是他們無法收回投資的貸款。
是以我們将定義一個漏報率,就是模型預測沒有取消贖回權但是實際上取消了,這個數除以總的取消贖回權數。這是“缺失的”實際取消贖回權百分比的模型。下面看這個圖表:
通過上面的圖表,有 1 個貸款預測不會取消贖回權,但是實際上取消了。如果我們将這個數除以實際取消贖回權的總數 2,我們将得到漏報率 50%。 我們将使用這個誤差标準,是以我們可以評估一下模型的行為。
我們使用交叉驗證預測。通過交叉驗證法,我們将資料分為3組。按照下面的方法來做:
用組 1 群組 2 訓練模型,然後用該模型來預測組 3
用組 1 群組 3 訓練模型,然後用該模型來預測組 2
用組 2 群組 3 訓練模型,然後用該模型來預測組 1
将它們分割到不同的組,這意味着我們永遠不會用相同的資料來為其預測訓練模型。這樣就避免了過拟合。如果過拟合,我們将錯誤地拉低了漏報率,這使得我們難以改進算法或者應用到現實生活中。
既然完成了前期準備,我們可以開始準備做出預測了。我将建立一個名為 <code>predict.py</code> 的新檔案,它會使用我們在最後一步建立的 <code>train.csv</code> 檔案。下面的代碼:
導入所需的庫
建立一個名為 <code>cross_validate</code> 的函數:
使用正确的關鍵詞參數建立邏輯回歸分類器
建立用于訓練模型的資料列的清單,移除 <code>id</code> 和 <code>foreclosure_status</code> 列
交叉驗證 <code>train</code> dataframe
傳回預測結果
<code>from sklearn import cross_validation</code>
<code>from sklearn.linear_model import logisticregression</code>
<code>from sklearn import metrics</code>
<code>def cross_validate(train):</code>
<code>clf = logisticregression(random_state=1, class_weight="balanced")</code>
<code>predictors = train.columns.tolist()</code>
<code>predictors = [p for p in predictors if p not in settings.non_predictors]</code>
<code>predictions = cross_validation.cross_val_predict(clf, train[predictors], train[settings.target], cv=settings.cv_folds)</code>
<code>return predictions</code>
現在,我們僅僅需要寫一些函數來計算誤差。下面的代碼:
建立函數 <code>compute_error</code>:
使用 scikit-learn 計算一個簡單的精确分數(與實際 <code>foreclosure_status</code> 值比對的預測百分比)
建立函數 <code>compute_false_negatives</code>:
為了友善,将目标和預測結果合并到一個 dataframe
查找漏報率
找到原本應被預測模型取消贖回權,但實際沒有取消的貸款數目
除以沒被取消贖回權的貸款總數目
<code>def compute_error(target, predictions):</code>
<code>return metrics.accuracy_score(target, predictions)</code>
<code>def compute_false_negatives(target, predictions):</code>
<code>df = pd.dataframe({"target": target, "predictions": predictions})</code>
<code>return df[(df["target"] == 1) & (df["predictions"] == 0)].shape[0] / (df[(df["target"] == 1)].shape[0] + 1)</code>
<code>def compute_false_positives(target, predictions):</code>
<code>return df[(df["target"] == 0) & (df["predictions"] == 1)].shape[0] / (df[(df["target"] == 0)].shape[0] + 1)</code>
現在,我們可以把函數都放在 <code>predict.py</code>。下面的代碼:
讀取資料集
計算交叉驗證預測
計算上面的 3 個誤差
列印誤差
<code>return train</code>
<code>train = read()</code>
<code>predictions = cross_validate(train)</code>
<code>error = compute_error(train[settings.target], predictions)</code>
<code>fn = compute_false_negatives(train[settings.target], predictions)</code>
<code>fp = compute_false_positives(train[settings.target], predictions)</code>
<code>print("accuracy score: {}".format(error))</code>
<code>print("false negatives: {}".format(fn))</code>
<code>print("false positives: {}".format(fp))</code>
一旦你添加完代碼,你可以運作 <code>python predict.py</code> 來産生預測結果。運作結果向我們展示漏報率為 <code>.26</code>,這意味着我們沒能預測 <code>26%</code> 的取消貸款。這是一個好的開始,但仍有很多改善的地方!
你的檔案樹現在看起來像下面這樣:
<code>├── predict.py</code>
既然我們完成了端到端的項目,那麼我們可以撰寫 <code>readme.md</code> 檔案了,這樣其他人便可以知道我們做的事,以及如何複制它。一個項目典型的 <code>readme.md</code> 應該包括這些部分:
一個高水準的項目概覽,并介紹項目目的
任何必需的資料和材料的下載下傳位址
安裝指令
如何安裝要求依賴
使用指令
如何運作項目
每一步之後會看到的結果
如何為這個項目作貢獻
擴充項目的下一步計劃
這裡仍有一些留待探索資料的角度。總的來說,我們可以把它們分割為 3 類: 擴充這個項目并使它更加精确,發現其他可以預測的列,并探索資料。這是其中一些想法:
在 <code>annotate.py</code> 中生成更多的特性
切換 <code>predict.py</code> 中的算法
嘗試使用比我們發表在這裡的更多的房利美資料
添加對未來資料進行預測的方法。如果我們添加更多資料,我們所寫的代碼仍然可以起作用,這樣我們可以添加更多過去和未來的資料。
嘗試看看是否你能預測一個銀行原本是否應該發放貸款(相對地,房利美是否應該獲得貸款)
移除 <code>train</code> 中銀行在發放貸款時間的不知道的任何列
當房利美購買貸款時,一些列是已知的,但之前是不知道的
做出預測
探索是否你可以預測除了 <code>foreclosure_status</code> 的其他列
你可以預測在銷售時資産值是多少?
探索探索執行資料更新之間的細微差别
你能否預測借款人會逾期還款多少次?
你能否标出的典型貸款周期?
将資料按州或郵政編碼标出
你看到一些有趣的模式了嗎?
原文釋出時間為:2017-10-28
本文來自雲栖社群合作夥伴“linux中國”