Skip to content

Commit fa01ec4

Browse files
committed
Update
1 parent 13dfca5 commit fa01ec4

34 files changed

Lines changed: 255 additions & 246 deletions
1.31 KB
Loading
1.79 KB
Loading
2.07 KB
Loading
-1.82 KB
Loading

docs/chapters/02-black-box-classification.html

Lines changed: 80 additions & 90 deletions
Large diffs are not rendered by default.
16.6 KB
Loading
5.99 KB
Loading
1.24 KB
Loading

docs/chapters/03-score-based-classification.html

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -411,21 +411,21 @@ <h3 class="anchored" data-anchor-id="to-lend-or-not-to-lend">To Lend Or Not To L
411411
<p>Banks can try to balance these risks by controlling interest rates. Higher interest rates increase prospective profit if the loan is repaid in full, but also increase the risk that an individual may be unable to keep up with payments.</p>
412412
<p>Historically, the judgment of whether to extend an individual a loan was handled by human experts. Recently, human experts have been seeking assistance from machine learning algorithms. As in most predictive modeling, the idea is to use the past to predict the future. Here, we’ll consider simple modeling problem in which we aim to learn patterns in when individuals are able to pay off loans, and use these patterns to make predictions.</p>
413413
<p>We’ll first load in the <code>pandas</code> package and use the <code>read_csv</code> command to acquire our data for this problem as a <code>pd.DataFrame</code>. <a href="https://www.kaggle.com/datasets/laotse/credit-risk-dataset">This data set was produced by Kaggle</a>; it is a <em>simulated</em> data set based on patterns in real world data, which, of course, is sensitive and confidential. For today, we are only going to focus on the first 1,000 rows of data.</p>
414-
<div id="0cf909d2" class="cell" data-execution_count="1">
414+
<div id="d9b120f3" class="cell" data-execution_count="1">
415415
<div class="sourceCode cell-code" id="cb1"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> pandas <span class="im">as</span> pd</span>
416416
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> matplotlib <span class="im">import</span> pyplot <span class="im">as</span> plt</span>
417417
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np </span>
418418
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>plt.style.use(<span class="st">'seaborn-v0_8-whitegrid'</span>)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
419419
</div>
420420
<p>Here’s the URL where I’ve hosted the data set. We can read in the data using the <code>pd.read_csv()</code> command, and then subset to the first 1,000 rows.</p>
421-
<div id="17ca0ce2" class="cell" data-execution_count="3">
421+
<div id="251d872f" class="cell" data-execution_count="3">
422422
<div class="sourceCode cell-code" id="cb2"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>url <span class="op">=</span> <span class="st">"https://raw.githubusercontent.com/PhilChodrow/ml-notes/main/data/credit-risk/credit_risk_dataset.csv"</span></span>
423423
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
424424
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>df <span class="op">=</span> pd.read_csv(url)</span>
425425
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>df <span class="op">=</span> df.head(<span class="dv">1000</span>).copy()</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
426426
</div>
427427
<p>Let’s take a look at an excerpt of the data.</p>
428-
<div id="865917d1" class="cell" data-execution_count="4">
428+
<div id="c980b946" class="cell" data-execution_count="4">
429429
<div class="sourceCode cell-code" id="cb3"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>df</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
430430
<div class="cell-output cell-output-display" data-execution_count="4">
431431
<div>
@@ -629,7 +629,7 @@ <h3 class="anchored" data-anchor-id="to-lend-or-not-to-lend">To Lend Or Not To L
629629
<li><code>loan_status</code> describes whether or not the individual defaulted on the loan. This column has value <code>1</code> if the individual defaulted on the loan and value <code>0</code> if the loan was repaid in full.</li>
630630
</ul>
631631
<p>Our primary predictive interest is whether or not a borrower is likely to default on a loan. How common is this in our data set?</p>
632-
<div id="f99dda6f" class="cell" data-execution_count="5">
632+
<div id="f12540ef" class="cell" data-execution_count="5">
633633
<div class="sourceCode cell-code" id="cb4"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>df[<span class="st">"loan_status"</span>].mean()</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
634634
<div class="cell-output cell-output-display" data-execution_count="5">
635635
<pre><code>np.float64(0.553)</code></pre>
@@ -699,12 +699,12 @@ <h3 class="anchored" data-anchor-id="to-lend-or-not-to-lend">To Lend Or Not To L
699699
\end{aligned}
700700
\tag{3.1}\]</span></span></p>
701701
<div class="page-columns page-full"><p>Let’s implement this score in Python. </p><div class="no-row-height column-margin column-container"><span class="margin-aside">This is not the optimal implementation of the score – we’ll see a much better approach that leverages linear algebra and matrix multiplication soon.</span></div></div>
702-
<div id="a570f59c" class="cell" data-execution_count="7">
702+
<div id="67335f1f" class="cell" data-execution_count="7">
703703
<div class="sourceCode cell-code" id="cb7"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> linear_score(w, x0, x1):</span>
704704
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> w[<span class="dv">0</span>]<span class="op">*</span>x0 <span class="op">+</span> w[<span class="dv">1</span>]<span class="op">*</span>x1 </span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
705705
</div>
706706
<p>Now we can plot this score function in the data space.</p>
707-
<div id="76459469" class="cell" data-execution_count="8">
707+
<div id="d5052ee2" class="cell" data-execution_count="8">
708708
<details class="code-fold">
709709
<summary>Show code</summary>
710710
<div class="sourceCode cell-code" id="cb8"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> plot_score(ax, score_fun, w, df):</span>
@@ -780,7 +780,7 @@ <h3 class="anchored" data-anchor-id="to-lend-or-not-to-lend">To Lend Or Not To L
780780
<section id="from-scores-to-predictions" class="level3 page-columns page-full">
781781
<h3 class="anchored" data-anchor-id="from-scores-to-predictions">From Scores to Predictions</h3>
782782
<p>Ok, great – we have a risk score for historical applicants. We can even compute risk scores for <em>future</em> applicants: plug their data into <a href="#eq-risk-score" class="quarto-xref">Equation&nbsp;<span>3.1</span></a>. But in order to make a decision, we need to conver the score into a yes-no decision. A common way to do this is called <em>thresholding</em>: we pick a threshold <span class="math inline">\(t\)</span> and approve a loan to individual <span class="math inline">\(i\)</span> of <span class="math inline">\(s_i &lt; t\)</span>.</p>
783-
<div id="83055b23" class="cell" data-execution_count="11">
783+
<div id="f0897661" class="cell" data-execution_count="11">
784784
<details class="code-fold">
785785
<summary>Show code</summary>
786786
<div class="sourceCode cell-code" id="cb11"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> plot_threshold(ax, score_fun, w, df, threshold):</span>
@@ -803,7 +803,7 @@ <h3 class="anchored" data-anchor-id="from-scores-to-predictions">From Scores to
803803
</details>
804804
</div>
805805
<p>Now we can pick a threshold and see how it does in dividing borrowers who fully repay loans from borrowers who default:</p>
806-
<div id="aa42765b" class="cell" data-execution_count="12">
806+
<div id="f21ced91" class="cell" data-execution_count="12">
807807
<div class="sourceCode cell-code" id="cb12"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a>threshold <span class="op">=</span> <span class="fl">0.4</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
808808
</div>
809809
<div id="cell-fig-linear-score" class="cell page-columns page-full" data-execution_count="13">
@@ -826,7 +826,7 @@ <h3 class="anchored" data-anchor-id="from-scores-to-predictions">From Scores to
826826
</div>
827827
<p>Something interesting to notice here is that our two step method of computing a linear score function and thresholding gave us the same <em>linear</em> classification pattern as the one we saw in <a href="01-data-and-models.html#fig-examples-with-patterns-2" class="quarto-xref">Figure&nbsp;<span>1.2 (b)</span></a>.</p>
828828
<p>Finally, once we’ve picked the weight vector <span class="math inline">\(\mathbf{w}\)</span> and a threshold function <span class="math inline">\(t\)</span>, we are ready to simulate making decisions. For example, with our current weights and threshold, we can add a column with what our retrospective “decisions” would have been on this historical data. Here is a function that generates the column.</p>
829-
<div id="3cff50c3" class="cell" data-execution_count="14">
829+
<div id="97fa958e" class="cell" data-execution_count="14">
830830
<div class="sourceCode cell-code" id="cb14"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> predict(score_fun, w, threshold, df):</span>
831831
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a> <span class="co">"""</span></span>
832832
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a><span class="co"> make binary predictions for data df using a supplied score function with weights w and supplied threshold. </span></span>
@@ -835,11 +835,11 @@ <h3 class="anchored" data-anchor-id="from-scores-to-predictions">From Scores to
835835
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="dv">1</span><span class="op">*</span>(scores <span class="op">&gt;</span> threshold)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
836836
</div>
837837
<p>Now let’s use this function to add the predictions as a new column in the data frame:</p>
838-
<div id="933f1b5a" class="cell" data-execution_count="15">
838+
<div id="4bf610e2" class="cell" data-execution_count="15">
839839
<div class="sourceCode cell-code" id="cb15"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a>df[<span class="st">"decision"</span>] <span class="op">=</span> predict(linear_score, w, threshold, df)</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
840840
</div>
841841
<p>Would our decisions have accurately reflected who in fact defaulted? One way to address this question is by measuring the <em>accuracy</em> of our decisions, which we can compute using vectorized code:</p>
842-
<div id="1934435a" class="cell" data-execution_count="16">
842+
<div id="4b389554" class="cell" data-execution_count="16">
843843
<div class="sourceCode cell-code" id="cb16"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a>(df[<span class="st">"decision"</span>] <span class="op">==</span> df[<span class="st">"loan_status"</span>]).mean()</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
844844
<div class="cell-output cell-output-display" data-execution_count="16">
845845
<pre><code>np.float64(0.767)</code></pre>
@@ -871,12 +871,12 @@ <h2 class="anchored" data-anchor-id="what-about-nonlinear-scores">What About Non
871871
\end{aligned}
872872
\]</span></p><div class="no-row-height column-margin column-container"><span class="margin-aside">In order for this formula to make sense, we now need <span class="math inline">\(\mathbf{w}\in \mathbb{R}^5\)</span>.</span></div></div>
873873
<p>Here’s an implementation of a score function with quadratic features:</p>
874-
<div id="b136bf81" class="cell" data-execution_count="17">
874+
<div id="8cf03794" class="cell" data-execution_count="17">
875875
<div class="sourceCode cell-code" id="cb18"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> quadratic_score(w, X0, X1):</span>
876876
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> w[<span class="dv">0</span>]<span class="op">*</span>X0 <span class="op">+</span> w[<span class="dv">1</span>]<span class="op">*</span>X1 <span class="op">+</span> w[<span class="dv">2</span>]<span class="op">*</span>X0<span class="op">**</span><span class="dv">2</span> <span class="op">+</span> w[<span class="dv">3</span>]<span class="op">*</span>X1<span class="op">**</span><span class="dv">2</span> <span class="op">+</span> w[<span class="dv">4</span>]<span class="op">*</span>X0<span class="op">*</span>X1</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
877877
</div>
878878
<p>Now we can set a new vector of weights <span class="math inline">\(\mathbf{w}\in \mathbb{R}^5\)</span> and a threshold <span class="math inline">\(t\)</span>.</p>
879-
<div id="8d92a0b1" class="cell" data-execution_count="18">
879+
<div id="9dc44520" class="cell" data-execution_count="18">
880880
<div class="sourceCode cell-code" id="cb19"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a>w <span class="op">=</span> np.array([<span class="fl">0.01</span>, <span class="dv">1</span>, <span class="fl">0.0005</span>, <span class="fl">0.6</span>, <span class="fl">0.001</span>])</span>
881881
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a>threshold <span class="op">=</span> <span class="fl">0.5</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
882882
</div>
@@ -900,7 +900,7 @@ <h2 class="anchored" data-anchor-id="what-about-nonlinear-scores">What About Non
900900
</div>
901901
</div>
902902
<p>How accurate were we?</p>
903-
<div id="d1b6ee16" class="cell" data-execution_count="20">
903+
<div id="c3368bf4" class="cell" data-execution_count="20">
904904
<div class="sourceCode cell-code" id="cb21"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a>df[<span class="st">"decision"</span>] <span class="op">=</span> predict(quadratic_score, w, threshold, df)</span>
905905
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a>(df[<span class="st">"decision"</span>] <span class="op">==</span> df[<span class="st">"loan_status"</span>]).mean()</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
906906
<div class="cell-output cell-output-display" data-execution_count="20">

0 commit comments

Comments
 (0)