1772 lines
100 KiB
HTML
1772 lines
100 KiB
HTML
<!--
|
|
HTML file automatically generated from DocOnce source
|
|
(https://github.com/doconce/doconce/)
|
|
doconce format html week45.do.txt --pygments_html_style=default --html_style=bloodish --html_links_in_new_window --html_output=week45 --no_mako
|
|
-->
|
|
<html>
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
<meta name="generator" content="DocOnce: https://github.com/doconce/doconce/" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<meta name="description" content="Week 45: Decisions Trees, Random Forests, Bagging and Boosting">
|
|
<title>Week 45: Decisions Trees, Random Forests, Bagging and Boosting</title>
|
|
<style type="text/css">
|
|
/* bloodish style */
|
|
body {
|
|
font-family: Helvetica, Verdana, Arial, Sans-serif;
|
|
color: #404040;
|
|
background: #ffffff;
|
|
}
|
|
h1 { font-size: 1.8em; color: #8A0808; }
|
|
h2 { font-size: 1.6em; color: #8A0808; }
|
|
h3 { font-size: 1.4em; color: #8A0808; }
|
|
h4 { font-size: 1.2em; color: #8A0808; }
|
|
a { color: #8A0808; text-decoration:none; }
|
|
tt { font-family: "Courier New", Courier; }
|
|
p { text-indent: 0px; }
|
|
hr { border: 0; width: 80%; border-bottom: 1px solid #aaa}
|
|
p.caption { width: 80%; font-style: normal; text-align: left; }
|
|
hr.figure { border: 0; width: 80%; border-bottom: 1px solid #aaa; }div.highlight {
|
|
border: 1px solid #cfcfcf;
|
|
border-radius: 2px;
|
|
line-height: 1.21429em;
|
|
}
|
|
div.cell {
|
|
width: 100%;
|
|
padding: 5px 5px 5px 0;
|
|
margin: 0;
|
|
outline: none;
|
|
}
|
|
div.input {
|
|
page-break-inside: avoid;
|
|
box-orient: horizontal;
|
|
box-align: stretch;
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: stretch;
|
|
}
|
|
div.inner_cell {
|
|
box-orient: vertical;
|
|
box-align: stretch;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
box-flex: 1;
|
|
flex: 1;
|
|
}
|
|
div.input_area {
|
|
border: 1px solid #cfcfcf;
|
|
border-radius: 4px;
|
|
background: #f7f7f7;
|
|
line-height: 1.21429em;
|
|
}
|
|
div.input_area > div.highlight {
|
|
margin: .4em;
|
|
border: none;
|
|
padding: 0;
|
|
background-color: transparent;
|
|
}
|
|
div.output_wrapper {
|
|
position: relative;
|
|
box-orient: vertical;
|
|
box-align: stretch;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
}
|
|
.output {
|
|
box-orient: vertical;
|
|
box-align: stretch;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
}
|
|
div.output_area {
|
|
padding: 0;
|
|
page-break-inside: avoid;
|
|
box-orient: horizontal;
|
|
box-align: stretch;
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: stretch;
|
|
}
|
|
div.output_subarea {
|
|
padding: .4em .4em 0 .4em;
|
|
box-flex: 1;
|
|
flex: 1;
|
|
}
|
|
div.output_text {
|
|
text-align: left;
|
|
color: #000;
|
|
line-height: 1.21429em;
|
|
}
|
|
.alert-text-small { font-size: 80%; }
|
|
.alert-text-large { font-size: 130%; }
|
|
.alert-text-normal { font-size: 90%; }
|
|
.alert {
|
|
padding:8px 35px 8px 14px; margin-bottom:18px;
|
|
text-shadow:0 1px 0 rgba(255,255,255,0.5);
|
|
border:1px solid #bababa;
|
|
border-radius: 4px;
|
|
-webkit-border-radius: 4px;
|
|
-moz-border-radius: 4px;
|
|
color: #555;
|
|
background-color: #f8f8f8;
|
|
background-position: 10px 5px;
|
|
background-repeat: no-repeat;
|
|
background-size: 38px;
|
|
padding-left: 55px;
|
|
width: 75%;
|
|
}
|
|
.alert-block {padding-top:14px; padding-bottom:14px}
|
|
.alert-block > p, .alert-block > ul {margin-bottom:1em}
|
|
.alert li {margin-top: 1em}
|
|
.alert-block p+p {margin-top:5px}
|
|
.alert-notice { background-image: url(https://cdn.rawgit.com/doconce/doconce/master/bundled/html_images/small_gray_notice.png); }
|
|
.alert-summary { background-image:url(https://cdn.rawgit.com/doconce/doconce/master/bundled/html_images/small_gray_summary.png); }
|
|
.alert-warning { background-image: url(https://cdn.rawgit.com/doconce/doconce/master/bundled/html_images/small_gray_warning.png); }
|
|
.alert-question {background-image:url(https://cdn.rawgit.com/doconce/doconce/master/bundled/html_images/small_gray_question.png); }
|
|
div { text-align: justify; text-justify: inter-word; }
|
|
.tab {
|
|
padding-left: 1.5em;
|
|
}
|
|
div.toc p,a {
|
|
line-height: 1.3;
|
|
margin-top: 1.1;
|
|
margin-bottom: 1.1;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<!-- tocinfo
|
|
{'highest level': 2,
|
|
'sections': [('Overview of week 45', 2, None, 'overview-of-week-45'),
|
|
('Brief code reminder from last wekk',
|
|
2,
|
|
None,
|
|
'brief-code-reminder-from-last-wekk'),
|
|
("Boosting, a Bird's Eye View",
|
|
2,
|
|
None,
|
|
'boosting-a-bird-s-eye-view'),
|
|
('What is boosting? Additive Modelling/Iterative Fitting',
|
|
2,
|
|
None,
|
|
'what-is-boosting-additive-modelling-iterative-fitting'),
|
|
('Iterative Fitting, Regression and Squared-error Cost Function',
|
|
2,
|
|
None,
|
|
'iterative-fitting-regression-and-squared-error-cost-function'),
|
|
('Squared-Error Example and Iterative Fitting',
|
|
2,
|
|
None,
|
|
'squared-error-example-and-iterative-fitting'),
|
|
('Iterative Fitting, Classification and AdaBoost',
|
|
2,
|
|
None,
|
|
'iterative-fitting-classification-and-adaboost'),
|
|
('Adaptive Boosting, AdaBoost',
|
|
2,
|
|
None,
|
|
'adaptive-boosting-adaboost'),
|
|
('Building up AdaBoost', 2, None, 'building-up-adaboost'),
|
|
('Adaptive boosting: AdaBoost, Basic Algorithm',
|
|
2,
|
|
None,
|
|
'adaptive-boosting-adaboost-basic-algorithm'),
|
|
('Basic Steps of AdaBoost', 2, None, 'basic-steps-of-adaboost'),
|
|
('AdaBoost Examples', 2, None, 'adaboost-examples'),
|
|
('Gradient boosting: Basics with Steepest Descent/Functional '
|
|
'Gradient Descent',
|
|
2,
|
|
None,
|
|
'gradient-boosting-basics-with-steepest-descent-functional-gradient-descent'),
|
|
('The Squared-Error again! Steepest Descent',
|
|
2,
|
|
None,
|
|
'the-squared-error-again-steepest-descent'),
|
|
('Steepest Descent Example', 2, None, 'steepest-descent-example'),
|
|
('Gradient Boosting, algorithm',
|
|
2,
|
|
None,
|
|
'gradient-boosting-algorithm'),
|
|
('Gradient Boosting, Examples of Regression',
|
|
2,
|
|
None,
|
|
'gradient-boosting-examples-of-regression'),
|
|
('Gradient Boosting, Classification Example',
|
|
2,
|
|
None,
|
|
'gradient-boosting-classification-example'),
|
|
('XGBoost: Extreme Gradient Boosting',
|
|
2,
|
|
None,
|
|
'xgboost-extreme-gradient-boosting'),
|
|
('Regression Case', 2, None, 'regression-case'),
|
|
('Xgboost on the Cancer Data',
|
|
2,
|
|
None,
|
|
'xgboost-on-the-cancer-data'),
|
|
('Support Vector Machines, overarching aims',
|
|
2,
|
|
None,
|
|
'support-vector-machines-overarching-aims'),
|
|
('Hyperplanes and all that', 2, None, 'hyperplanes-and-all-that'),
|
|
('What is a hyperplane?', 2, None, 'what-is-a-hyperplane'),
|
|
('A $p$-dimensional space of features',
|
|
2,
|
|
None,
|
|
'a-p-dimensional-space-of-features'),
|
|
('The two-dimensional case', 2, None, 'the-two-dimensional-case'),
|
|
('Getting into the details', 2, None, 'getting-into-the-details'),
|
|
('First attempt at a minimization approach',
|
|
2,
|
|
None,
|
|
'first-attempt-at-a-minimization-approach'),
|
|
('Solving the equations', 2, None, 'solving-the-equations'),
|
|
('Code Example', 2, None, 'code-example'),
|
|
('Problems with the Simpler Approach',
|
|
2,
|
|
None,
|
|
'problems-with-the-simpler-approach'),
|
|
('A better approach', 2, None, 'a-better-approach'),
|
|
('A quick Reminder on Lagrangian Multipliers',
|
|
2,
|
|
None,
|
|
'a-quick-reminder-on-lagrangian-multipliers'),
|
|
('Adding the Multiplier', 2, None, 'adding-the-multiplier'),
|
|
('Setting up the Problem', 2, None, 'setting-up-the-problem'),
|
|
('The problem to solve', 2, None, 'the-problem-to-solve'),
|
|
('The last steps', 2, None, 'the-last-steps'),
|
|
('A soft classifier', 2, None, 'a-soft-classifier'),
|
|
('Soft optmization problem',
|
|
2,
|
|
None,
|
|
'soft-optmization-problem')]}
|
|
end of tocinfo -->
|
|
|
|
<body>
|
|
|
|
|
|
|
|
<script type="text/x-mathjax-config">
|
|
MathJax.Hub.Config({
|
|
TeX: {
|
|
equationNumbers: { autoNumber: "AMS" },
|
|
extensions: ["AMSmath.js", "AMSsymbols.js", "autobold.js", "color.js"]
|
|
}
|
|
});
|
|
</script>
|
|
<script type="text/javascript" async
|
|
src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
|
|
</script>
|
|
|
|
|
|
<!-- ------------------- main content ---------------------- -->
|
|
<center>
|
|
<h1>Week 45: Decisions Trees, Random Forests, Bagging and Boosting</h1>
|
|
</center> <!-- document title -->
|
|
|
|
<!-- author(s): Morten Hjorth-Jensen -->
|
|
<center>
|
|
<b>Morten Hjorth-Jensen</b> [1, 2]
|
|
</center>
|
|
<!-- institution(s) -->
|
|
<center>
|
|
[1] <b>Department of Physics, University of Oslo</b>
|
|
</center>
|
|
<center>
|
|
[2] <b>Department of Physics and Astronomy and National Superconducting Cyclotron Laboratory, Michigan State University</b>
|
|
</center>
|
|
<br>
|
|
<center>
|
|
<h4>Nov 11, 2022</h4>
|
|
</center> <!-- date -->
|
|
<br>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="overview-of-week-45">Overview of week 45 </h2>
|
|
|
|
<ul>
|
|
<li> Thursday: Boosting methods, froma AdaBoost to Gradient boosting</li>
|
|
<ul>
|
|
<li> <a href="https://youtu.be/mK48PfCxgYk" target="_blank">Video of lecture</a></li>
|
|
</ul>
|
|
<li> Friday: Gradient boosting and discussion of Decision trees and ensemble methods. Wrapping up trees and start discussing Support Vector Machines</li>
|
|
<ul>
|
|
<li> <a href="https://youtu.be/v8eJBFeZKuI" target="_blank">Video of lecture</a></li>
|
|
</ul>
|
|
</ul>
|
|
<div class="alert alert-block alert-block alert-text-normal">
|
|
<b>Videos</b>
|
|
<p>
|
|
<ol>
|
|
<li> <a href="https://www.youtube.com/watch?v=RmajweUFKvM&ab_channel=Simplilearn" target="_blank">Video on Decision trees</a></li>
|
|
<li> <a href="https://www.youtube.com/watch?v=wPqtzj5VZus&ab_channel=H2O.ai" target="_blank">Video on boosting methods by Hastie</a>.</li>
|
|
<li> <a href="https://www.youtube.com/watch?v=LsK-xG1cLYA" target="_blank">Video on AdaBoost</a></li>
|
|
<li> <a href="https://www.youtube.com/watch?v=3CC4N4z3GJc" target="_blank">Video on Gradient boost, part 1, parts 2-4 follows</a></li>
|
|
</ol>
|
|
</div>
|
|
|
|
|
|
<div class="alert alert-block alert-block alert-text-normal">
|
|
<b>Reading</b>
|
|
<p>
|
|
<ol>
|
|
<li> <a href="https://github.com/CompPhysics/MachineLearning/blob/master/doc/Textbooks/elementsstat.pdf" target="_blank">Hastie et al, chapter 10.1-10.10</a>. Geron's chapters 6 and 7 are also useful.</li>
|
|
</ol>
|
|
</div>
|
|
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="brief-code-reminder-from-last-wekk">Brief code reminder from last wekk </h2>
|
|
|
|
|
|
<!-- code=python (!bc pycod) typeset with pygments style "default" -->
|
|
<div class="cell border-box-sizing code_cell rendered">
|
|
<div class="input">
|
|
<div class="inner_cell">
|
|
<div class="input_area">
|
|
<div class="highlight" style="background: #f8f8f8">
|
|
<pre style="line-height: 125%;"><span style="color: #666666">%</span>matplotlib inline
|
|
<span style="color: #408080; font-style: italic"># Common imports</span>
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">IPython.display</span> <span style="color: #008000; font-weight: bold">import</span> Image
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">pydot</span> <span style="color: #008000; font-weight: bold">import</span> graph_from_dot_data
|
|
<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">pandas</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">pd</span>
|
|
<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">numpy</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">np</span>
|
|
<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">matplotlib.pyplot</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">plt</span>
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.model_selection</span> <span style="color: #008000; font-weight: bold">import</span> train_test_split
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.tree</span> <span style="color: #008000; font-weight: bold">import</span> export_graphviz
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.preprocessing</span> <span style="color: #008000; font-weight: bold">import</span> StandardScaler, OneHotEncoder
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.compose</span> <span style="color: #008000; font-weight: bold">import</span> ColumnTransformer
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">pydot</span> <span style="color: #008000; font-weight: bold">import</span> graph_from_dot_data
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.datasets</span> <span style="color: #008000; font-weight: bold">import</span> load_breast_cancer
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.svm</span> <span style="color: #008000; font-weight: bold">import</span> SVC
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.linear_model</span> <span style="color: #008000; font-weight: bold">import</span> LogisticRegression
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.tree</span> <span style="color: #008000; font-weight: bold">import</span> DecisionTreeClassifier
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.ensemble</span> <span style="color: #008000; font-weight: bold">import</span> RandomForestClassifier
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.preprocessing</span> <span style="color: #008000; font-weight: bold">import</span> LabelEncoder
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.model_selection</span> <span style="color: #008000; font-weight: bold">import</span> cross_validate
|
|
<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">scikitplot</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">skplt</span>
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.preprocessing</span> <span style="color: #008000; font-weight: bold">import</span> StandardScaler
|
|
<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">os</span>
|
|
|
|
<span style="color: #408080; font-style: italic"># Where to save the figures and data files</span>
|
|
PROJECT_ROOT_DIR <span style="color: #666666">=</span> <span style="color: #BA2121">"Results"</span>
|
|
FIGURE_ID <span style="color: #666666">=</span> <span style="color: #BA2121">"Results/FigureFiles"</span>
|
|
DATA_ID <span style="color: #666666">=</span> <span style="color: #BA2121">"DataFiles/"</span>
|
|
|
|
<span style="color: #008000; font-weight: bold">if</span> <span style="color: #AA22FF; font-weight: bold">not</span> os<span style="color: #666666">.</span>path<span style="color: #666666">.</span>exists(PROJECT_ROOT_DIR):
|
|
os<span style="color: #666666">.</span>mkdir(PROJECT_ROOT_DIR)
|
|
|
|
<span style="color: #008000; font-weight: bold">if</span> <span style="color: #AA22FF; font-weight: bold">not</span> os<span style="color: #666666">.</span>path<span style="color: #666666">.</span>exists(FIGURE_ID):
|
|
os<span style="color: #666666">.</span>makedirs(FIGURE_ID)
|
|
|
|
<span style="color: #008000; font-weight: bold">if</span> <span style="color: #AA22FF; font-weight: bold">not</span> os<span style="color: #666666">.</span>path<span style="color: #666666">.</span>exists(DATA_ID):
|
|
os<span style="color: #666666">.</span>makedirs(DATA_ID)
|
|
|
|
<span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">image_path</span>(fig_id):
|
|
<span style="color: #008000; font-weight: bold">return</span> os<span style="color: #666666">.</span>path<span style="color: #666666">.</span>join(FIGURE_ID, fig_id)
|
|
|
|
<span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">data_path</span>(dat_id):
|
|
<span style="color: #008000; font-weight: bold">return</span> os<span style="color: #666666">.</span>path<span style="color: #666666">.</span>join(DATA_ID, dat_id)
|
|
|
|
<span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">save_fig</span>(fig_id):
|
|
plt<span style="color: #666666">.</span>savefig(image_path(fig_id) <span style="color: #666666">+</span> <span style="color: #BA2121">".png"</span>, <span style="color: #008000">format</span><span style="color: #666666">=</span><span style="color: #BA2121">'png'</span>)
|
|
|
|
<span style="color: #408080; font-style: italic"># Load the cancer data</span>
|
|
cancer <span style="color: #666666">=</span> load_breast_cancer()
|
|
|
|
X_train, X_test, y_train, y_test <span style="color: #666666">=</span> train_test_split(cancer<span style="color: #666666">.</span>data,cancer<span style="color: #666666">.</span>target,random_state<span style="color: #666666">=0</span>)
|
|
<span style="color: #008000">print</span>(X_train<span style="color: #666666">.</span>shape)
|
|
<span style="color: #008000">print</span>(X_test<span style="color: #666666">.</span>shape)
|
|
<span style="color: #408080; font-style: italic">#Scale the data</span>
|
|
scaler <span style="color: #666666">=</span> StandardScaler()
|
|
scaler<span style="color: #666666">.</span>fit(X_train)
|
|
X_train_scaled <span style="color: #666666">=</span> scaler<span style="color: #666666">.</span>transform(X_train)
|
|
X_test_scaled <span style="color: #666666">=</span> scaler<span style="color: #666666">.</span>transform(X_test)
|
|
<span style="color: #408080; font-style: italic">#define methods</span>
|
|
<span style="color: #408080; font-style: italic"># Logistic Regression</span>
|
|
logreg <span style="color: #666666">=</span> LogisticRegression(solver<span style="color: #666666">=</span><span style="color: #BA2121">'lbfgs'</span>)
|
|
logreg<span style="color: #666666">.</span>fit(X_train_scaled, y_train)
|
|
<span style="color: #008000">print</span>(<span style="color: #BA2121">"Test set accuracy Logistic Regression with scaled data: </span><span style="color: #BB6688; font-weight: bold">{:.2f}</span><span style="color: #BA2121">"</span><span style="color: #666666">.</span>format(logreg<span style="color: #666666">.</span>score(X_test_scaled,y_test)))
|
|
<span style="color: #408080; font-style: italic"># Decision Trees</span>
|
|
deep_tree_clf <span style="color: #666666">=</span> DecisionTreeClassifier(max_depth<span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">None</span>)
|
|
deep_tree_clf<span style="color: #666666">.</span>fit(X_train_scaled, y_train)
|
|
<span style="color: #008000">print</span>(<span style="color: #BA2121">"Test set accuracy with Decision Trees and scaled data: </span><span style="color: #BB6688; font-weight: bold">{:.2f}</span><span style="color: #BA2121">"</span><span style="color: #666666">.</span>format(deep_tree_clf<span style="color: #666666">.</span>score(X_test_scaled,y_test)))
|
|
<span style="color: #408080; font-style: italic"># Support Vector Machine</span>
|
|
svm <span style="color: #666666">=</span> SVC(gamma<span style="color: #666666">=</span><span style="color: #BA2121">'auto'</span>, C<span style="color: #666666">=100</span>)
|
|
svm<span style="color: #666666">.</span>fit(X_train_scaled, y_train)
|
|
<span style="color: #008000">print</span>(<span style="color: #BA2121">"Test set accuracy SVM with scaled data: </span><span style="color: #BB6688; font-weight: bold">{:.2f}</span><span style="color: #BA2121">"</span><span style="color: #666666">.</span>format(logreg<span style="color: #666666">.</span>score(X_test_scaled,y_test)))
|
|
<span style="color: #408080; font-style: italic"># Random forests</span>
|
|
<span style="color: #408080; font-style: italic">#Instantiate the model with 500 trees and entropy as splitting criteria</span>
|
|
Random_Forest_model <span style="color: #666666">=</span> RandomForestClassifier(n_estimators<span style="color: #666666">=500</span>,criterion<span style="color: #666666">=</span><span style="color: #BA2121">"entropy"</span>)
|
|
Random_Forest_model<span style="color: #666666">.</span>fit(X_train_scaled, y_train)
|
|
<span style="color: #008000">print</span>(<span style="color: #BA2121">"Test set accuracy with Random Forests and scaled data: </span><span style="color: #BB6688; font-weight: bold">{:.2f}</span><span style="color: #BA2121">"</span><span style="color: #666666">.</span>format(Random_Forest_model<span style="color: #666666">.</span>score(X_test_scaled,y_test)))
|
|
|
|
|
|
|
|
y_pred <span style="color: #666666">=</span> Random_Forest_model<span style="color: #666666">.</span>predict(X_test_scaled)
|
|
skplt<span style="color: #666666">.</span>metrics<span style="color: #666666">.</span>plot_confusion_matrix(y_test, y_pred, normalize<span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">True</span>)
|
|
plt<span style="color: #666666">.</span>show()
|
|
y_probas <span style="color: #666666">=</span> Random_Forest_model<span style="color: #666666">.</span>predict_proba(X_test_scaled)
|
|
skplt<span style="color: #666666">.</span>metrics<span style="color: #666666">.</span>plot_roc(y_test, y_probas)
|
|
plt<span style="color: #666666">.</span>show()
|
|
skplt<span style="color: #666666">.</span>metrics<span style="color: #666666">.</span>plot_cumulative_gain(y_test, y_probas)
|
|
plt<span style="color: #666666">.</span>show()
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="output_wrapper">
|
|
<div class="output">
|
|
<div class="output_area">
|
|
<div class="output_subarea output_stream output_stdout output_text">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="boosting-a-bird-s-eye-view">Boosting, a Bird's Eye View </h2>
|
|
|
|
<p>The basic idea is to combine weak classifiers in order to create a good
|
|
classifier. With a weak classifier we often intend a classifier which
|
|
produces results which are only slightly better than we would get by
|
|
random guesses.
|
|
</p>
|
|
|
|
<p>This is done by applying in an iterative way a weak (or a standard
|
|
classifier like decision trees) to modify the data. In each iteration
|
|
we emphasize those observations which are misclassified by weighting
|
|
them with a factor.
|
|
</p>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="what-is-boosting-additive-modelling-iterative-fitting">What is boosting? Additive Modelling/Iterative Fitting </h2>
|
|
|
|
<p>Boosting is a way of fitting an additive expansion in a set of
|
|
elementary basis functions like for example some simple polynomials.
|
|
Assume for example that we have a function
|
|
</p>
|
|
$$
|
|
f_M(x) = \sum_{i=1}^M \beta_m b(x;\gamma_m),
|
|
$$
|
|
|
|
<p>where \( \beta_m \) are the expansion parameters to be determined in a
|
|
minimization process and \( b(x;\gamma_m) \) are some simple functions of
|
|
the multivariable parameter \( x \) which is characterized by the
|
|
parameters \( \gamma_m \).
|
|
</p>
|
|
|
|
<p>As an example, consider the Sigmoid function we used in logistic
|
|
regression. In that case, we can translate the function
|
|
\( b(x;\gamma_m) \) into the Sigmoid function
|
|
</p>
|
|
|
|
$$
|
|
\sigma(t) = \frac{1}{1+\exp{(-t)}},
|
|
$$
|
|
|
|
<p>where \( t=\gamma_0+\gamma_1 x \) and the parameters \( \gamma_0 \) and
|
|
\( \gamma_1 \) were determined by the Logistic Regression fitting
|
|
algorithm.
|
|
</p>
|
|
|
|
<p>As another example, consider the cost function we defined for linear regression</p>
|
|
$$
|
|
C(\boldsymbol{y},\boldsymbol{f}) = \frac{1}{n} \sum_{i=0}^{n-1}(y_i-f(x_i))^2.
|
|
$$
|
|
|
|
<p>In this case the function \( f(x) \) was replaced by the design matrix
|
|
\( \boldsymbol{X} \) and the unknown linear regression parameters \( \boldsymbol{\beta} \),
|
|
that is \( \boldsymbol{f}=\boldsymbol{X}\boldsymbol{\beta} \). In linear regression we can
|
|
simply invert a matrix and obtain the parameters \( \beta \) by
|
|
</p>
|
|
|
|
$$
|
|
\boldsymbol{\beta}=\left(\boldsymbol{X}^T\boldsymbol{X}\right)^{-1}\boldsymbol{X}^T\boldsymbol{y}.
|
|
$$
|
|
|
|
<p>In iterative fitting or additive modeling, we minimize the cost function with respect to the parameters \( \beta_m \) and \( \gamma_m \).</p>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="iterative-fitting-regression-and-squared-error-cost-function">Iterative Fitting, Regression and Squared-error Cost Function </h2>
|
|
|
|
<p>The way we proceed is as follows (here we specialize to the squared-error cost function)</p>
|
|
|
|
<ol>
|
|
<li> Establish a cost function, here \( {\cal C}(\boldsymbol{y},\boldsymbol{f}) = \frac{1}{n} \sum_{i=0}^{n-1}(y_i-f_M(x_i))^2 \) with \( f_M(x) = \sum_{i=1}^M \beta_m b(x;\gamma_m) \).</li>
|
|
<li> Initialize with a guess \( f_0(x) \). It could be one or even zero or some random numbers.</li>
|
|
<li> For \( m=1:M \)
|
|
<ol type="a"></li>
|
|
<li> minimize \( \sum_{i=0}^{n-1}(y_i-f_{m-1}(x_i)-\beta b(x;\gamma))^2 \) wrt \( \gamma \) and \( \beta \)</li>
|
|
<li> This gives the optimal values \( \beta_m \) and \( \gamma_m \)</li>
|
|
<li> Determine then the new values \( f_m(x)=f_{m-1}(x) +\beta_m b(x;\gamma_m) \)</li>
|
|
</ol>
|
|
</ol>
|
|
<p>We could use any of the algorithms we have discussed till now. If we
|
|
use trees, \( \gamma \) parameterizes the split variables and split points
|
|
at the internal nodes, and the predictions at the terminal nodes.
|
|
</p>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="squared-error-example-and-iterative-fitting">Squared-Error Example and Iterative Fitting </h2>
|
|
|
|
<p>To better understand what happens, let us develop the steps for the iterative fitting using the above squared error function.</p>
|
|
|
|
<p>For simplicity we assume also that our functions \( b(x;\gamma)=1+\gamma x \). </p>
|
|
|
|
<p>This means that for every iteration \( m \), we need to optimize</p>
|
|
|
|
$$
|
|
(\beta_m,\gamma_m) = \mathrm{argmin}_{\beta,\lambda}\hspace{0.1cm} \sum_{i=0}^{n-1}(y_i-f_{m-1}(x_i)-\beta b(x;\gamma))^2=\sum_{i=0}^{n-1}(y_i-f_{m-1}(x_i)-\beta(1+\gamma x_i))^2.
|
|
$$
|
|
|
|
<p>We start our iteration by simply setting \( f_0(x)=0 \).
|
|
Taking the derivatives with respect to \( \beta \) and \( \gamma \) we obtain
|
|
</p>
|
|
$$
|
|
\frac{\partial {\cal C}}{\partial \beta} = -2\sum_{i}(1+\gamma x_i)(y_i-\beta(1+\gamma x_i))=0,
|
|
$$
|
|
|
|
<p>and</p>
|
|
$$
|
|
\frac{\partial {\cal C}}{\partial \gamma} =-2\sum_{i}\beta x_i(y_i-\beta(1+\gamma x_i))=0.
|
|
$$
|
|
|
|
<p>We can then rewrite these equations as (defining \( \boldsymbol{w}=\boldsymbol{e}+\gamma \boldsymbol{x}) \) with \( \boldsymbol{e} \) being the unit vector)</p>
|
|
$$
|
|
\gamma \boldsymbol{w}^T(\boldsymbol{y}-\beta\gamma \boldsymbol{w})=0,
|
|
$$
|
|
|
|
<p>which gives us \( \beta = \boldsymbol{w}^T\boldsymbol{y}/(\boldsymbol{w}^T\boldsymbol{w}) \). Similarly we have </p>
|
|
$$
|
|
\beta\gamma \boldsymbol{x}^T(\boldsymbol{y}-\beta(1+\gamma \boldsymbol{x}))=0,
|
|
$$
|
|
|
|
<p>which leads to \( \gamma =(\boldsymbol{x}^T\boldsymbol{y}-\beta\boldsymbol{x}^T\boldsymbol{e})/(\beta\boldsymbol{x}^T\boldsymbol{x}) \). Inserting
|
|
for \( \beta \) gives us an equation for \( \gamma \). This is a non-linear equation in the unknown \( \gamma \) and has to be solved numerically.
|
|
</p>
|
|
|
|
<p>The solution to these two equations gives us in turn \( \beta_1 \) and \( \gamma_1 \) leading to the new expression for \( f_1(x) \) as
|
|
\( f_1(x) = \beta_1(1+\gamma_1x) \). Doing this \( M \) times results in our final estimate for the function \( f \).
|
|
</p>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="iterative-fitting-classification-and-adaboost">Iterative Fitting, Classification and AdaBoost </h2>
|
|
|
|
<p>Let us consider a binary classification problem with two outcomes \( y_i \in \{-1,1\} \) and \( i=0,1,2,\dots,n-1 \) as our set of
|
|
observations. We define a classification function \( G(x) \) which produces a prediction taking one or the other of the two values
|
|
\( \{-1,1\} \).
|
|
</p>
|
|
|
|
<p>The error rate of the training sample is then</p>
|
|
|
|
$$
|
|
\mathrm{\overline{err}}=\frac{1}{n} \sum_{i=0}^{n-1} I(y_i\ne G(x_i)).
|
|
$$
|
|
|
|
<p>The iterative procedure starts with defining a weak classifier whose
|
|
error rate is barely better than random guessing. The iterative
|
|
procedure in boosting is to sequentially apply a weak
|
|
classification algorithm to repeatedly modified versions of the data
|
|
producing a sequence of weak classifiers \( G_m(x) \).
|
|
</p>
|
|
|
|
<p>Here we will express our function \( f(x) \) in terms of \( G(x) \). That is</p>
|
|
$$
|
|
f_M(x) = \sum_{i=1}^M \beta_m b(x;\gamma_m),
|
|
$$
|
|
|
|
<p>will be a function of </p>
|
|
$$
|
|
G_M(x) = \mathrm{sign} \sum_{i=1}^M \alpha_m G_m(x).
|
|
$$
|
|
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="adaptive-boosting-adaboost">Adaptive Boosting, AdaBoost </h2>
|
|
|
|
<p>In our iterative procedure we define thus</p>
|
|
$$
|
|
f_m(x) = f_{m-1}(x)+\beta_mG_m(x).
|
|
$$
|
|
|
|
<p>The simplest possible cost function which leads (also simple from a computational point of view) to the AdaBoost algorithm is the
|
|
exponential cost/loss function defined as
|
|
</p>
|
|
$$
|
|
C(\boldsymbol{y},\boldsymbol{f}) = \sum_{i=0}^{n-1}\exp{(-y_i(f_{m-1}(x_i)+\beta G(x_i))}.
|
|
$$
|
|
|
|
<p>We optimize \( \beta \) and \( G \) for each value of \( m=1:M \) as we did in the regression case.
|
|
This is normally done in two steps. Let us however first rewrite the cost function as
|
|
</p>
|
|
|
|
$$
|
|
C(\boldsymbol{y},\boldsymbol{f}) = \sum_{i=0}^{n-1}w_i^{m}\exp{(-y_i\beta G(x_i))},
|
|
$$
|
|
|
|
<p>where we have defined \( w_i^m= \exp{(-y_if_{m-1}(x_i))} \).</p>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="building-up-adaboost">Building up AdaBoost </h2>
|
|
|
|
<p>First, for any \( \beta > 0 \), we optimize \( G \) by setting</p>
|
|
$$
|
|
G_m(x) = \mathrm{sign} \sum_{i=0}^{n-1} w_i^m I(y_i \ne G_(x_i)),
|
|
$$
|
|
|
|
<p>which is the classifier that minimizes the weighted error rate in predicting \( y \).</p>
|
|
|
|
<p>We can do this by rewriting</p>
|
|
$$
|
|
\exp{-(\beta)}\sum_{y_i=G(x_i)}w_i^m+\exp{(\beta)}\sum_{y_i\ne G(x_i)}w_i^m,
|
|
$$
|
|
|
|
<p>which can be rewritten as</p>
|
|
$$
|
|
(\exp{(\beta)}-\exp{-(\beta)})\sum_{i=0}^{n-1}w_i^mI(y_i\ne G(x_i))+\exp{(-\beta)}\sum_{i=0}^{n-1}w_i^m=0,
|
|
$$
|
|
|
|
<p>which leads to</p>
|
|
$$
|
|
\beta_m = \frac{1}{2}\log{\frac{1-\mathrm{\overline{err}}}{\mathrm{\overline{err}}}},
|
|
$$
|
|
|
|
<p>where we have redefined the error as </p>
|
|
$$
|
|
\mathrm{\overline{err}}_m=\frac{1}{n}\frac{\sum_{i=0}^{n-1}w_i^mI(y_i\ne G(x_i)}{\sum_{i=0}^{n-1}w_i^m},
|
|
$$
|
|
|
|
<p>which leads to an update of</p>
|
|
$$
|
|
f_m(x) = f_{m-1}(x) +\beta_m G_m(x).
|
|
$$
|
|
|
|
<p>This leads to the new weights</p>
|
|
$$
|
|
w_i^{m+1} = w_i^m \exp{(-y_i\beta_m G_m(x_i))}
|
|
$$
|
|
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="adaptive-boosting-adaboost-basic-algorithm">Adaptive boosting: AdaBoost, Basic Algorithm </h2>
|
|
|
|
<p>The algorithm here is rather straightforward. Assume that our weak
|
|
classifier is a decision tree and we consider a binary set of outputs
|
|
with \( y_i \in \{-1,1\} \) and \( i=0,1,2,\dots,n-1 \) as our set of
|
|
observations. Our design matrix is given in terms of the
|
|
feature/predictor vectors
|
|
\( \boldsymbol{X}=[\boldsymbol{x}_0\boldsymbol{x}_1\dots\boldsymbol{x}_{p-1}] \). Finally, we define also a
|
|
classifier determined by our data via a function \( G(x) \). This function tells us how well we are able to classify our outputs/targets \( \boldsymbol{y} \).
|
|
</p>
|
|
|
|
<p>We have already defined the misclassification error \( \mathrm{err} \) as</p>
|
|
$$
|
|
\mathrm{err}=\frac{1}{n}\sum_{i=0}^{n-1}I(y_i\ne G(x_i)),
|
|
$$
|
|
|
|
<p>where the function \( I() \) is one if we misclassify and zero if we classify correctly. </p>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="basic-steps-of-adaboost">Basic Steps of AdaBoost </h2>
|
|
|
|
<p>With the above definitions we are now ready to set up the algorithm for AdaBoost.
|
|
The basic idea is to set up weights which will be used to scale the correctly classified and the misclassified cases.
|
|
</p>
|
|
<ol>
|
|
<li> We start by initializing all weights to \( w_i = 1/n \), with \( i=0,1,2,\dots n-1 \). It is easy to see that we must have \( \sum_{i=0}^{n-1}w_i = 1 \).</li>
|
|
<li> We rewrite the misclassification error as</li>
|
|
</ol>
|
|
$$
|
|
\mathrm{\overline{err}}_m=\frac{\sum_{i=0}^{n-1}w_i^m I(y_i\ne G(x_i))}{\sum_{i=0}^{n-1}w_i},
|
|
$$
|
|
|
|
<ol>
|
|
<li> Then we start looping over all attempts at classifying, namely we start an iterative process for \( m=1:M \), where \( M \) is the final number of classifications. Our given classifier could for example be a plain decision tree.
|
|
<ol type="a"></li>
|
|
<li> Fit then a given classifier to the training set using the weights \( w_i \).</li>
|
|
<li> Compute then \( \mathrm{err} \) and figure out which events are classified properly and which are classified wrongly.</li>
|
|
<li> Define a quantity \( \alpha_{m} = \log{(1-\mathrm{\overline{err}}_m)/\mathrm{\overline{err}}_m} \)</li>
|
|
<li> Set the new weights to \( w_i = w_i\times \exp{(\alpha_m I(y_i\ne G(x_i)} \).</li>
|
|
</ol>
|
|
<li> Compute the new classifier \( G(x)= \sum_{i=0}^{n-1}\alpha_m I(y_i\ne G(x_i) \).</li>
|
|
</ol>
|
|
<p>For the iterations with \( m \le 2 \) the weights are modified
|
|
individually at each steps. The observations which were misclassified
|
|
at iteration \( m-1 \) have a weight which is larger than those which were
|
|
classified properly. As this proceeds, the observations which were
|
|
difficult to classifiy correctly are given a larger influence. Each
|
|
new classification step \( m \) is then forced to concentrate on those
|
|
observations that are missed in the previous iterations.
|
|
</p>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="adaboost-examples">AdaBoost Examples </h2>
|
|
|
|
<p>Using <b>Scikit-Learn</b> it is easy to apply the adaptive boosting algorithm, as done here.</p>
|
|
|
|
|
|
<!-- code=python (!bc pycod) typeset with pygments style "default" -->
|
|
<div class="cell border-box-sizing code_cell rendered">
|
|
<div class="input">
|
|
<div class="inner_cell">
|
|
<div class="input_area">
|
|
<div class="highlight" style="background: #f8f8f8">
|
|
<pre style="line-height: 125%;"><span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.ensemble</span> <span style="color: #008000; font-weight: bold">import</span> AdaBoostClassifier
|
|
|
|
ada_clf <span style="color: #666666">=</span> AdaBoostClassifier(
|
|
DecisionTreeClassifier(max_depth<span style="color: #666666">=2</span>), n_estimators<span style="color: #666666">=200</span>,
|
|
algorithm<span style="color: #666666">=</span><span style="color: #BA2121">"SAMME.R"</span>, learning_rate<span style="color: #666666">=0.01</span>, random_state<span style="color: #666666">=42</span>)
|
|
ada_clf<span style="color: #666666">.</span>fit(X_train, y_train)
|
|
y_pred <span style="color: #666666">=</span> ada_clf<span style="color: #666666">.</span>predict(X_test)
|
|
skplt<span style="color: #666666">.</span>metrics<span style="color: #666666">.</span>plot_confusion_matrix(y_test, y_pred, normalize<span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">True</span>)
|
|
plt<span style="color: #666666">.</span>show()
|
|
y_probas <span style="color: #666666">=</span> ada_clf<span style="color: #666666">.</span>predict_proba(X_test)
|
|
skplt<span style="color: #666666">.</span>metrics<span style="color: #666666">.</span>plot_roc(y_test, y_probas)
|
|
plt<span style="color: #666666">.</span>show()
|
|
skplt<span style="color: #666666">.</span>metrics<span style="color: #666666">.</span>plot_cumulative_gain(y_test, y_probas)
|
|
plt<span style="color: #666666">.</span>show()
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="output_wrapper">
|
|
<div class="output">
|
|
<div class="output_area">
|
|
<div class="output_subarea output_stream output_stdout output_text">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="gradient-boosting-basics-with-steepest-descent-functional-gradient-descent">Gradient boosting: Basics with Steepest Descent/Functional Gradient Descent </h2>
|
|
|
|
<p>Gradient boosting is again a similar technique to Adaptive boosting,
|
|
it combines so-called weak classifiers or regressors into a strong
|
|
method via a series of iterations.
|
|
</p>
|
|
|
|
<p>In order to understand the method, let us illustrate its basics by
|
|
bringing back the essential steps in linear regression, where our cost
|
|
function was the least squares function.
|
|
</p>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="the-squared-error-again-steepest-descent">The Squared-Error again! Steepest Descent </h2>
|
|
|
|
<p>We start again with our cost function \( {\cal C}(\boldsymbol{y}m\boldsymbol{f})=\sum_{i=0}^{n-1}{\cal L}(y_i, f(x_i)) \) where we want to minimize
|
|
This means that for every iteration, we need to optimize
|
|
</p>
|
|
|
|
$$
|
|
(\hat{\boldsymbol{f}}) = \mathrm{argmin}_{\boldsymbol{f}}\hspace{0.1cm} \sum_{i=0}^{n-1}(y_i-f(x_i))^2.
|
|
$$
|
|
|
|
<p>We define a real function \( h_m(x) \) that defines our final function \( f_M(x) \) as</p>
|
|
$$
|
|
f_M(x) = \sum_{m=0}^M h_m(x).
|
|
$$
|
|
|
|
<p>In the steepest decent approach we approximate \( h_m(x) = -\rho_m g_m(x) \), where \( \rho_m \) is a scalar and \( g_m(x) \) the gradient defined as</p>
|
|
$$
|
|
g_m(x_i) = \left[ \frac{\partial {\cal L}(y_i, f(x_i))}{\partial f(x_i)}\right]_{f(x_i)=f_{m-1}(x_i)}.
|
|
$$
|
|
|
|
<p>With the new gradient we can update \( f_m(x) = f_{m-1}(x) -\rho_m g_m(x) \). Using the above squared-error function we see that
|
|
the gradient is \( g_m(x_i) = -2(y_i-f(x_i)) \).
|
|
</p>
|
|
|
|
<p>Choosing \( f_0(x)=0 \) we obtain \( g_m(x) = -2y_i \) and inserting this into the minimization problem for the cost function we have</p>
|
|
$$
|
|
(\rho_1) = \mathrm{argmin}_{\rho}\hspace{0.1cm} \sum_{i=0}^{n-1}(y_i+2\rho y_i)^2.
|
|
$$
|
|
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="steepest-descent-example">Steepest Descent Example </h2>
|
|
|
|
<p>Optimizing with respect to \( \rho \) we obtain (taking the derivative) that \( \rho_1 = -1/2 \). We have then that</p>
|
|
$$
|
|
f_1(x) = f_{0}(x) -\rho_1 g_1(x)=-y_i.
|
|
$$
|
|
|
|
<p>We can then proceed and compute</p>
|
|
$$
|
|
g_2(x_i) = \left[ \frac{\partial {\cal L}(y_i, f(x_i))}{\partial f(x_i)}\right]_{f(x_i)=f_{1}(x_i)=y_i}=-4y_i,
|
|
$$
|
|
|
|
<p>and find a new value for \( \rho_2=-1/2 \) and continue till we have reached \( m=M \). We can modify the steepest descent method, or steepest boosting, by introducing what is called <b>gradient boosting</b>. </p>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="gradient-boosting-algorithm">Gradient Boosting, algorithm </h2>
|
|
|
|
<p>Steepest descent is however not much used, since it only optimizes \( f \) at a fixed set of \( n \) points,
|
|
so we do not learn a function that can generalize. However, we can modify the algorithm by
|
|
fitting a weak learner to approximate the negative gradient signal.
|
|
</p>
|
|
|
|
<p>Suppose we have a cost function \( C(f)=\sum_{i=0}^{n-1}L(y_i, f(x_i)) \) where \( y_i \) is our target and \( f(x_i) \) the function which is meant to model \( y_i \). The above cost function could be our standard squared-error function</p>
|
|
$$
|
|
C(\boldsymbol{y},\boldsymbol{f})=\sum_{i=0}^{n-1}(y_i-f(x_i))^2.
|
|
$$
|
|
|
|
<p>The way we proceed in an iterative fashion is to</p>
|
|
<ol>
|
|
<li> Initialize our estimate \( f_0(x) \).</li>
|
|
<li> For \( m=1:M \), we
|
|
<ol type="a"></li>
|
|
<li> compute the negative gradient vector \( \boldsymbol{u}_m = -\partial C(\boldsymbol{y},\boldsymbol{f})/\partial \boldsymbol{f}(x) \) at \( f(x) = f_{m-1}(x) \);</li>
|
|
<li> fit the so-called base-learner to the negative gradient \( h_m(u_m,x) \);</li>
|
|
<li> update the estimate \( f_m(x) = f_{m-1}(x)+h_m(u_m,x) \);</li>
|
|
</ol>
|
|
<li> The final estimate is then \( f_M(x) = \sum_{m=1}^M h_m(u_m,x) \).</li>
|
|
</ol>
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="gradient-boosting-examples-of-regression">Gradient Boosting, Examples of Regression </h2>
|
|
|
|
<!-- code=python (!bc pycod) typeset with pygments style "default" -->
|
|
<div class="cell border-box-sizing code_cell rendered">
|
|
<div class="input">
|
|
<div class="inner_cell">
|
|
<div class="input_area">
|
|
<div class="highlight" style="background: #f8f8f8">
|
|
<pre style="line-height: 125%;"><span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">matplotlib.pyplot</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">plt</span>
|
|
<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">numpy</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">np</span>
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.model_selection</span> <span style="color: #008000; font-weight: bold">import</span> train_test_split
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.ensemble</span> <span style="color: #008000; font-weight: bold">import</span> GradientBoostingRegressor
|
|
<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">scikitplot</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">skplt</span>
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.metrics</span> <span style="color: #008000; font-weight: bold">import</span> mean_squared_error
|
|
|
|
n <span style="color: #666666">=</span> <span style="color: #666666">100</span>
|
|
maxdegree <span style="color: #666666">=</span> <span style="color: #666666">6</span>
|
|
|
|
<span style="color: #408080; font-style: italic"># Make data set.</span>
|
|
x <span style="color: #666666">=</span> np<span style="color: #666666">.</span>linspace(<span style="color: #666666">-3</span>, <span style="color: #666666">3</span>, n)<span style="color: #666666">.</span>reshape(<span style="color: #666666">-1</span>, <span style="color: #666666">1</span>)
|
|
y <span style="color: #666666">=</span> np<span style="color: #666666">.</span>exp(<span style="color: #666666">-</span>x<span style="color: #666666">**2</span>) <span style="color: #666666">+</span> <span style="color: #666666">1.5</span> <span style="color: #666666">*</span> np<span style="color: #666666">.</span>exp(<span style="color: #666666">-</span>(x<span style="color: #666666">-2</span>)<span style="color: #666666">**2</span>)<span style="color: #666666">+</span> np<span style="color: #666666">.</span>random<span style="color: #666666">.</span>normal(<span style="color: #666666">0</span>, <span style="color: #666666">0.1</span>, x<span style="color: #666666">.</span>shape)
|
|
|
|
error <span style="color: #666666">=</span> np<span style="color: #666666">.</span>zeros(maxdegree)
|
|
bias <span style="color: #666666">=</span> np<span style="color: #666666">.</span>zeros(maxdegree)
|
|
variance <span style="color: #666666">=</span> np<span style="color: #666666">.</span>zeros(maxdegree)
|
|
polydegree <span style="color: #666666">=</span> np<span style="color: #666666">.</span>zeros(maxdegree)
|
|
X_train, X_test, y_train, y_test <span style="color: #666666">=</span> train_test_split(x, y, test_size<span style="color: #666666">=0.2</span>)
|
|
|
|
<span style="color: #008000; font-weight: bold">for</span> degree <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(<span style="color: #666666">1</span>,maxdegree):
|
|
model <span style="color: #666666">=</span> GradientBoostingRegressor(max_depth<span style="color: #666666">=</span>degree, n_estimators<span style="color: #666666">=100</span>, learning_rate<span style="color: #666666">=1.0</span>)
|
|
model<span style="color: #666666">.</span>fit(X_train,y_train)
|
|
y_pred <span style="color: #666666">=</span> model<span style="color: #666666">.</span>predict(X_test)
|
|
polydegree[degree] <span style="color: #666666">=</span> degree
|
|
error[degree] <span style="color: #666666">=</span> np<span style="color: #666666">.</span>mean( np<span style="color: #666666">.</span>mean((y_test <span style="color: #666666">-</span> y_pred)<span style="color: #666666">**2</span>) )
|
|
bias[degree] <span style="color: #666666">=</span> np<span style="color: #666666">.</span>mean( (y_test <span style="color: #666666">-</span> np<span style="color: #666666">.</span>mean(y_pred))<span style="color: #666666">**2</span> )
|
|
variance[degree] <span style="color: #666666">=</span> np<span style="color: #666666">.</span>mean( np<span style="color: #666666">.</span>var(y_pred) )
|
|
<span style="color: #008000">print</span>(<span style="color: #BA2121">'Max depth:'</span>, degree)
|
|
<span style="color: #008000">print</span>(<span style="color: #BA2121">'Error:'</span>, error[degree])
|
|
<span style="color: #008000">print</span>(<span style="color: #BA2121">'Bias^2:'</span>, bias[degree])
|
|
<span style="color: #008000">print</span>(<span style="color: #BA2121">'Var:'</span>, variance[degree])
|
|
<span style="color: #008000">print</span>(<span style="color: #BA2121">'</span><span style="color: #BB6688; font-weight: bold">{}</span><span style="color: #BA2121"> >= </span><span style="color: #BB6688; font-weight: bold">{}</span><span style="color: #BA2121"> + </span><span style="color: #BB6688; font-weight: bold">{}</span><span style="color: #BA2121"> = </span><span style="color: #BB6688; font-weight: bold">{}</span><span style="color: #BA2121">'</span><span style="color: #666666">.</span>format(error[degree], bias[degree], variance[degree], bias[degree]<span style="color: #666666">+</span>variance[degree]))
|
|
|
|
plt<span style="color: #666666">.</span>xlim(<span style="color: #666666">1</span>,maxdegree<span style="color: #666666">-1</span>)
|
|
plt<span style="color: #666666">.</span>plot(polydegree, error, label<span style="color: #666666">=</span><span style="color: #BA2121">'Error'</span>)
|
|
plt<span style="color: #666666">.</span>plot(polydegree, bias, label<span style="color: #666666">=</span><span style="color: #BA2121">'bias'</span>)
|
|
plt<span style="color: #666666">.</span>plot(polydegree, variance, label<span style="color: #666666">=</span><span style="color: #BA2121">'Variance'</span>)
|
|
plt<span style="color: #666666">.</span>legend()
|
|
save_fig(<span style="color: #BA2121">"gdregression"</span>)
|
|
plt<span style="color: #666666">.</span>show()
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="output_wrapper">
|
|
<div class="output">
|
|
<div class="output_area">
|
|
<div class="output_subarea output_stream output_stdout output_text">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="gradient-boosting-classification-example">Gradient Boosting, Classification Example </h2>
|
|
|
|
<!-- code=python (!bc pycod) typeset with pygments style "default" -->
|
|
<div class="cell border-box-sizing code_cell rendered">
|
|
<div class="input">
|
|
<div class="inner_cell">
|
|
<div class="input_area">
|
|
<div class="highlight" style="background: #f8f8f8">
|
|
<pre style="line-height: 125%;"><span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">matplotlib.pyplot</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">plt</span>
|
|
<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">numpy</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">np</span>
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.model_selection</span> <span style="color: #008000; font-weight: bold">import</span> train_test_split
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.datasets</span> <span style="color: #008000; font-weight: bold">import</span> load_breast_cancer
|
|
<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">scikitplot</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">skplt</span>
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.ensemble</span> <span style="color: #008000; font-weight: bold">import</span> GradientBoostingClassifier
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.model_selection</span> <span style="color: #008000; font-weight: bold">import</span> cross_validate
|
|
|
|
<span style="color: #408080; font-style: italic"># Load the data</span>
|
|
cancer <span style="color: #666666">=</span> load_breast_cancer()
|
|
|
|
X_train, X_test, y_train, y_test <span style="color: #666666">=</span> train_test_split(cancer<span style="color: #666666">.</span>data,cancer<span style="color: #666666">.</span>target,random_state<span style="color: #666666">=0</span>)
|
|
<span style="color: #008000">print</span>(X_train<span style="color: #666666">.</span>shape)
|
|
<span style="color: #008000">print</span>(X_test<span style="color: #666666">.</span>shape)
|
|
<span style="color: #408080; font-style: italic">#now scale the data</span>
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.preprocessing</span> <span style="color: #008000; font-weight: bold">import</span> StandardScaler
|
|
scaler <span style="color: #666666">=</span> StandardScaler()
|
|
scaler<span style="color: #666666">.</span>fit(X_train)
|
|
X_train_scaled <span style="color: #666666">=</span> scaler<span style="color: #666666">.</span>transform(X_train)
|
|
X_test_scaled <span style="color: #666666">=</span> scaler<span style="color: #666666">.</span>transform(X_test)
|
|
|
|
gd_clf <span style="color: #666666">=</span> GradientBoostingClassifier(max_depth<span style="color: #666666">=3</span>, n_estimators<span style="color: #666666">=100</span>, learning_rate<span style="color: #666666">=1.0</span>)
|
|
gd_clf<span style="color: #666666">.</span>fit(X_train_scaled, y_train)
|
|
<span style="color: #408080; font-style: italic">#Cross validation</span>
|
|
accuracy <span style="color: #666666">=</span> cross_validate(gd_clf,X_test_scaled,y_test,cv<span style="color: #666666">=10</span>)[<span style="color: #BA2121">'test_score'</span>]
|
|
<span style="color: #008000">print</span>(accuracy)
|
|
<span style="color: #008000">print</span>(<span style="color: #BA2121">"Test set accuracy with Gradient boosting and scaled data: </span><span style="color: #BB6688; font-weight: bold">{:.2f}</span><span style="color: #BA2121">"</span><span style="color: #666666">.</span>format(gd_clf<span style="color: #666666">.</span>score(X_test_scaled,y_test)))
|
|
|
|
<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">scikitplot</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">skplt</span>
|
|
y_pred <span style="color: #666666">=</span> gd_clf<span style="color: #666666">.</span>predict(X_test_scaled)
|
|
skplt<span style="color: #666666">.</span>metrics<span style="color: #666666">.</span>plot_confusion_matrix(y_test, y_pred, normalize<span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">True</span>)
|
|
save_fig(<span style="color: #BA2121">"gdclassiffierconfusion"</span>)
|
|
plt<span style="color: #666666">.</span>show()
|
|
y_probas <span style="color: #666666">=</span> gd_clf<span style="color: #666666">.</span>predict_proba(X_test_scaled)
|
|
skplt<span style="color: #666666">.</span>metrics<span style="color: #666666">.</span>plot_roc(y_test, y_probas)
|
|
save_fig(<span style="color: #BA2121">"gdclassiffierroc"</span>)
|
|
plt<span style="color: #666666">.</span>show()
|
|
skplt<span style="color: #666666">.</span>metrics<span style="color: #666666">.</span>plot_cumulative_gain(y_test, y_probas)
|
|
save_fig(<span style="color: #BA2121">"gdclassiffiercgain"</span>)
|
|
plt<span style="color: #666666">.</span>show()
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="output_wrapper">
|
|
<div class="output">
|
|
<div class="output_area">
|
|
<div class="output_subarea output_stream output_stdout output_text">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="xgboost-extreme-gradient-boosting">XGBoost: Extreme Gradient Boosting </h2>
|
|
|
|
<p><a href="https://github.com/dmlc/xgboost" target="_blank">XGBoost</a> or Extreme Gradient
|
|
Boosting, is an optimized distributed gradient boosting library
|
|
designed to be highly efficient, flexible and portable. It implements
|
|
machine learning algorithms under the Gradient Boosting
|
|
framework. XGBoost provides a parallel tree boosting that solve many
|
|
data science problems in a fast and accurate way. See the <a href="https://arxiv.org/abs/1603.02754" target="_blank">article by Chen and Guestrin</a>.
|
|
</p>
|
|
|
|
<p>The authors design and build a highly scalable end-to-end tree
|
|
boosting system. It has a theoretically justified weighted quantile
|
|
sketch for efficient proposal calculation. It introduces a novel sparsity-aware algorithm for parallel tree learning and an effective cache-aware block structure for out-of-core tree learning.
|
|
</p>
|
|
|
|
<p>It is now the algorithm which wins essentially all ML competitions!!!</p>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="regression-case">Regression Case </h2>
|
|
|
|
|
|
<!-- code=python (!bc pycod) typeset with pygments style "default" -->
|
|
<div class="cell border-box-sizing code_cell rendered">
|
|
<div class="input">
|
|
<div class="inner_cell">
|
|
<div class="input_area">
|
|
<div class="highlight" style="background: #f8f8f8">
|
|
<pre style="line-height: 125%;"><span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">matplotlib.pyplot</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">plt</span>
|
|
<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">numpy</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">np</span>
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.model_selection</span> <span style="color: #008000; font-weight: bold">import</span> train_test_split
|
|
<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">xgboost</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">xgb</span>
|
|
<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">scikitplot</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">skplt</span>
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.metrics</span> <span style="color: #008000; font-weight: bold">import</span> mean_squared_error
|
|
|
|
n <span style="color: #666666">=</span> <span style="color: #666666">100</span>
|
|
maxdegree <span style="color: #666666">=</span> <span style="color: #666666">6</span>
|
|
|
|
<span style="color: #408080; font-style: italic"># Make data set.</span>
|
|
x <span style="color: #666666">=</span> np<span style="color: #666666">.</span>linspace(<span style="color: #666666">-3</span>, <span style="color: #666666">3</span>, n)<span style="color: #666666">.</span>reshape(<span style="color: #666666">-1</span>, <span style="color: #666666">1</span>)
|
|
y <span style="color: #666666">=</span> np<span style="color: #666666">.</span>exp(<span style="color: #666666">-</span>x<span style="color: #666666">**2</span>) <span style="color: #666666">+</span> <span style="color: #666666">1.5</span> <span style="color: #666666">*</span> np<span style="color: #666666">.</span>exp(<span style="color: #666666">-</span>(x<span style="color: #666666">-2</span>)<span style="color: #666666">**2</span>)<span style="color: #666666">+</span> np<span style="color: #666666">.</span>random<span style="color: #666666">.</span>normal(<span style="color: #666666">0</span>, <span style="color: #666666">0.1</span>, x<span style="color: #666666">.</span>shape)
|
|
|
|
error <span style="color: #666666">=</span> np<span style="color: #666666">.</span>zeros(maxdegree)
|
|
bias <span style="color: #666666">=</span> np<span style="color: #666666">.</span>zeros(maxdegree)
|
|
variance <span style="color: #666666">=</span> np<span style="color: #666666">.</span>zeros(maxdegree)
|
|
polydegree <span style="color: #666666">=</span> np<span style="color: #666666">.</span>zeros(maxdegree)
|
|
X_train, X_test, y_train, y_test <span style="color: #666666">=</span> train_test_split(x, y, test_size<span style="color: #666666">=0.2</span>)
|
|
|
|
<span style="color: #008000; font-weight: bold">for</span> degree <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(maxdegree):
|
|
model <span style="color: #666666">=</span> xgb<span style="color: #666666">.</span>XGBRegressor(objective <span style="color: #666666">=</span><span style="color: #BA2121">'reg:squarederror'</span>, colsaobjective <span style="color: #666666">=</span><span style="color: #BA2121">'reg:squarederror'</span>, colsample_bytree <span style="color: #666666">=</span> <span style="color: #666666">0.3</span>, learning_rate <span style="color: #666666">=</span> <span style="color: #666666">0.1</span>,max_depth <span style="color: #666666">=</span> degree, alpha <span style="color: #666666">=</span> <span style="color: #666666">10</span>, n_estimators <span style="color: #666666">=</span> <span style="color: #666666">200</span>)
|
|
|
|
model<span style="color: #666666">.</span>fit(X_train,y_train)
|
|
y_pred <span style="color: #666666">=</span> model<span style="color: #666666">.</span>predict(X_test)
|
|
polydegree[degree] <span style="color: #666666">=</span> degree
|
|
error[degree] <span style="color: #666666">=</span> np<span style="color: #666666">.</span>mean( np<span style="color: #666666">.</span>mean((y_test <span style="color: #666666">-</span> y_pred)<span style="color: #666666">**2</span>) )
|
|
bias[degree] <span style="color: #666666">=</span> np<span style="color: #666666">.</span>mean( (y_test <span style="color: #666666">-</span> np<span style="color: #666666">.</span>mean(y_pred))<span style="color: #666666">**2</span> )
|
|
variance[degree] <span style="color: #666666">=</span> np<span style="color: #666666">.</span>mean( np<span style="color: #666666">.</span>var(y_pred) )
|
|
<span style="color: #008000">print</span>(<span style="color: #BA2121">'Max depth:'</span>, degree)
|
|
<span style="color: #008000">print</span>(<span style="color: #BA2121">'Error:'</span>, error[degree])
|
|
<span style="color: #008000">print</span>(<span style="color: #BA2121">'Bias^2:'</span>, bias[degree])
|
|
<span style="color: #008000">print</span>(<span style="color: #BA2121">'Var:'</span>, variance[degree])
|
|
<span style="color: #008000">print</span>(<span style="color: #BA2121">'</span><span style="color: #BB6688; font-weight: bold">{}</span><span style="color: #BA2121"> >= </span><span style="color: #BB6688; font-weight: bold">{}</span><span style="color: #BA2121"> + </span><span style="color: #BB6688; font-weight: bold">{}</span><span style="color: #BA2121"> = </span><span style="color: #BB6688; font-weight: bold">{}</span><span style="color: #BA2121">'</span><span style="color: #666666">.</span>format(error[degree], bias[degree], variance[degree], bias[degree]<span style="color: #666666">+</span>variance[degree]))
|
|
|
|
plt<span style="color: #666666">.</span>xlim(<span style="color: #666666">1</span>,maxdegree<span style="color: #666666">-1</span>)
|
|
plt<span style="color: #666666">.</span>plot(polydegree, error, label<span style="color: #666666">=</span><span style="color: #BA2121">'Error'</span>)
|
|
plt<span style="color: #666666">.</span>plot(polydegree, bias, label<span style="color: #666666">=</span><span style="color: #BA2121">'bias'</span>)
|
|
plt<span style="color: #666666">.</span>plot(polydegree, variance, label<span style="color: #666666">=</span><span style="color: #BA2121">'Variance'</span>)
|
|
plt<span style="color: #666666">.</span>legend()
|
|
plt<span style="color: #666666">.</span>show()
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="output_wrapper">
|
|
<div class="output">
|
|
<div class="output_area">
|
|
<div class="output_subarea output_stream output_stdout output_text">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="xgboost-on-the-cancer-data">Xgboost on the Cancer Data </h2>
|
|
|
|
<p>As you will see from the confusion matrix below, XGBoots does an excellent job on the Wisconsin cancer data and outperforms essentially all agorithms we have discussed till now. </p>
|
|
|
|
<!-- code=python (!bc pycod) typeset with pygments style "default" -->
|
|
<div class="cell border-box-sizing code_cell rendered">
|
|
<div class="input">
|
|
<div class="inner_cell">
|
|
<div class="input_area">
|
|
<div class="highlight" style="background: #f8f8f8">
|
|
<pre style="line-height: 125%;"><span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">matplotlib.pyplot</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">plt</span>
|
|
<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">numpy</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">np</span>
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.model_selection</span> <span style="color: #008000; font-weight: bold">import</span> train_test_split
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.datasets</span> <span style="color: #008000; font-weight: bold">import</span> load_breast_cancer
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.preprocessing</span> <span style="color: #008000; font-weight: bold">import</span> LabelEncoder
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.model_selection</span> <span style="color: #008000; font-weight: bold">import</span> cross_validate
|
|
<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">scikitplot</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">skplt</span>
|
|
<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">xgboost</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">xgb</span>
|
|
<span style="color: #408080; font-style: italic"># Load the data</span>
|
|
cancer <span style="color: #666666">=</span> load_breast_cancer()
|
|
|
|
X_train, X_test, y_train, y_test <span style="color: #666666">=</span> train_test_split(cancer<span style="color: #666666">.</span>data,cancer<span style="color: #666666">.</span>target,random_state<span style="color: #666666">=0</span>)
|
|
<span style="color: #008000">print</span>(X_train<span style="color: #666666">.</span>shape)
|
|
<span style="color: #008000">print</span>(X_test<span style="color: #666666">.</span>shape)
|
|
<span style="color: #408080; font-style: italic">#now scale the data</span>
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.preprocessing</span> <span style="color: #008000; font-weight: bold">import</span> StandardScaler
|
|
scaler <span style="color: #666666">=</span> StandardScaler()
|
|
scaler<span style="color: #666666">.</span>fit(X_train)
|
|
X_train_scaled <span style="color: #666666">=</span> scaler<span style="color: #666666">.</span>transform(X_train)
|
|
X_test_scaled <span style="color: #666666">=</span> scaler<span style="color: #666666">.</span>transform(X_test)
|
|
|
|
xg_clf <span style="color: #666666">=</span> xgb<span style="color: #666666">.</span>XGBClassifier()
|
|
xg_clf<span style="color: #666666">.</span>fit(X_train_scaled,y_train)
|
|
|
|
y_test <span style="color: #666666">=</span> xg_clf<span style="color: #666666">.</span>predict(X_test_scaled)
|
|
|
|
<span style="color: #008000">print</span>(<span style="color: #BA2121">"Test set accuracy with Gradient Boosting and scaled data: </span><span style="color: #BB6688; font-weight: bold">{:.2f}</span><span style="color: #BA2121">"</span><span style="color: #666666">.</span>format(xg_clf<span style="color: #666666">.</span>score(X_test_scaled,y_test)))
|
|
|
|
<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">scikitplot</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">skplt</span>
|
|
y_pred <span style="color: #666666">=</span> xg_clf<span style="color: #666666">.</span>predict(X_test_scaled)
|
|
skplt<span style="color: #666666">.</span>metrics<span style="color: #666666">.</span>plot_confusion_matrix(y_test, y_pred, normalize<span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">True</span>)
|
|
save_fig(<span style="color: #BA2121">"xdclassiffierconfusion"</span>)
|
|
plt<span style="color: #666666">.</span>show()
|
|
y_probas <span style="color: #666666">=</span> xg_clf<span style="color: #666666">.</span>predict_proba(X_test_scaled)
|
|
skplt<span style="color: #666666">.</span>metrics<span style="color: #666666">.</span>plot_roc(y_test, y_probas)
|
|
save_fig(<span style="color: #BA2121">"xdclassiffierroc"</span>)
|
|
plt<span style="color: #666666">.</span>show()
|
|
skplt<span style="color: #666666">.</span>metrics<span style="color: #666666">.</span>plot_cumulative_gain(y_test, y_probas)
|
|
save_fig(<span style="color: #BA2121">"gdclassiffiercgain"</span>)
|
|
plt<span style="color: #666666">.</span>show()
|
|
|
|
|
|
xgb<span style="color: #666666">.</span>plot_tree(xg_clf,num_trees<span style="color: #666666">=0</span>)
|
|
plt<span style="color: #666666">.</span>rcParams[<span style="color: #BA2121">'figure.figsize'</span>] <span style="color: #666666">=</span> [<span style="color: #666666">50</span>, <span style="color: #666666">10</span>]
|
|
save_fig(<span style="color: #BA2121">"xgtree"</span>)
|
|
plt<span style="color: #666666">.</span>show()
|
|
|
|
xgb<span style="color: #666666">.</span>plot_importance(xg_clf)
|
|
plt<span style="color: #666666">.</span>rcParams[<span style="color: #BA2121">'figure.figsize'</span>] <span style="color: #666666">=</span> [<span style="color: #666666">5</span>, <span style="color: #666666">5</span>]
|
|
save_fig(<span style="color: #BA2121">"xgparams"</span>)
|
|
plt<span style="color: #666666">.</span>show()
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="output_wrapper">
|
|
<div class="output">
|
|
<div class="output_area">
|
|
<div class="output_subarea output_stream output_stdout output_text">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="support-vector-machines-overarching-aims">Support Vector Machines, overarching aims </h2>
|
|
|
|
<p>A Support Vector Machine (SVM) is a very powerful and versatile
|
|
Machine Learning method, capable of performing linear or nonlinear
|
|
classification, regression, and even outlier detection. It is one of
|
|
the most popular models in Machine Learning, and anyone interested in
|
|
Machine Learning should have it in their toolbox. SVMs are
|
|
particularly well suited for classification of complex but small-sized or
|
|
medium-sized datasets.
|
|
</p>
|
|
|
|
<p>The case with two well-separated classes only can be understood in an
|
|
intuitive way in terms of lines in a two-dimensional space separating
|
|
the two classes (see figure below).
|
|
</p>
|
|
|
|
<p>The basic mathematics behind the SVM is however less familiar to most of us.
|
|
It relies on the definition of hyperplanes and the
|
|
definition of a <b>margin</b> which separates classes (in case of
|
|
classification problems) of variables. It is also used for regression
|
|
problems.
|
|
</p>
|
|
|
|
<p>With SVMs we distinguish between hard margin and soft margins. The
|
|
latter introduces a so-called softening parameter to be discussed
|
|
below. We distinguish also between linear and non-linear
|
|
approaches. The latter are the most frequent ones since it is rather
|
|
unlikely that we can separate classes easily by say straight lines.
|
|
</p>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="hyperplanes-and-all-that">Hyperplanes and all that </h2>
|
|
|
|
<p>The theory behind support vector machines (SVM hereafter) is based on
|
|
the mathematical description of so-called hyperplanes. Let us start
|
|
with a two-dimensional case. This will also allow us to introduce our
|
|
first SVM examples. These will be tailored to the case of two specific
|
|
classes, as displayed in the figure here based on the usage of the petal data.
|
|
</p>
|
|
|
|
<p>We assume here that our data set can be well separated into two
|
|
domains, where a straight line does the job in the separating the two
|
|
classes. Here the two classes are represented by either squares or
|
|
circles.
|
|
</p>
|
|
|
|
<!-- code=python (!bc pycod) typeset with pygments style "default" -->
|
|
<div class="cell border-box-sizing code_cell rendered">
|
|
<div class="input">
|
|
<div class="inner_cell">
|
|
<div class="input_area">
|
|
<div class="highlight" style="background: #f8f8f8">
|
|
<pre style="line-height: 125%;"><span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn</span> <span style="color: #008000; font-weight: bold">import</span> datasets
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.svm</span> <span style="color: #008000; font-weight: bold">import</span> SVC, LinearSVC
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.linear_model</span> <span style="color: #008000; font-weight: bold">import</span> SGDClassifier
|
|
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">sklearn.preprocessing</span> <span style="color: #008000; font-weight: bold">import</span> StandardScaler
|
|
<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">matplotlib</span>
|
|
<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">matplotlib.pyplot</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">plt</span>
|
|
plt<span style="color: #666666">.</span>rcParams[<span style="color: #BA2121">'axes.labelsize'</span>] <span style="color: #666666">=</span> <span style="color: #666666">14</span>
|
|
plt<span style="color: #666666">.</span>rcParams[<span style="color: #BA2121">'xtick.labelsize'</span>] <span style="color: #666666">=</span> <span style="color: #666666">12</span>
|
|
plt<span style="color: #666666">.</span>rcParams[<span style="color: #BA2121">'ytick.labelsize'</span>] <span style="color: #666666">=</span> <span style="color: #666666">12</span>
|
|
|
|
|
|
iris <span style="color: #666666">=</span> datasets<span style="color: #666666">.</span>load_iris()
|
|
X <span style="color: #666666">=</span> iris[<span style="color: #BA2121">"data"</span>][:, (<span style="color: #666666">2</span>, <span style="color: #666666">3</span>)] <span style="color: #408080; font-style: italic"># petal length, petal width</span>
|
|
y <span style="color: #666666">=</span> iris[<span style="color: #BA2121">"target"</span>]
|
|
|
|
setosa_or_versicolor <span style="color: #666666">=</span> (y <span style="color: #666666">==</span> <span style="color: #666666">0</span>) <span style="color: #666666">|</span> (y <span style="color: #666666">==</span> <span style="color: #666666">1</span>)
|
|
X <span style="color: #666666">=</span> X[setosa_or_versicolor]
|
|
y <span style="color: #666666">=</span> y[setosa_or_versicolor]
|
|
|
|
|
|
|
|
C <span style="color: #666666">=</span> <span style="color: #666666">5</span>
|
|
alpha <span style="color: #666666">=</span> <span style="color: #666666">1</span> <span style="color: #666666">/</span> (C <span style="color: #666666">*</span> <span style="color: #008000">len</span>(X))
|
|
|
|
lin_clf <span style="color: #666666">=</span> LinearSVC(loss<span style="color: #666666">=</span><span style="color: #BA2121">"hinge"</span>, C<span style="color: #666666">=</span>C, random_state<span style="color: #666666">=42</span>)
|
|
svm_clf <span style="color: #666666">=</span> SVC(kernel<span style="color: #666666">=</span><span style="color: #BA2121">"linear"</span>, C<span style="color: #666666">=</span>C)
|
|
sgd_clf <span style="color: #666666">=</span> SGDClassifier(loss<span style="color: #666666">=</span><span style="color: #BA2121">"hinge"</span>, learning_rate<span style="color: #666666">=</span><span style="color: #BA2121">"constant"</span>, eta0<span style="color: #666666">=0.001</span>, alpha<span style="color: #666666">=</span>alpha,
|
|
max_iter<span style="color: #666666">=100000</span>, random_state<span style="color: #666666">=42</span>)
|
|
|
|
scaler <span style="color: #666666">=</span> StandardScaler()
|
|
X_scaled <span style="color: #666666">=</span> scaler<span style="color: #666666">.</span>fit_transform(X)
|
|
|
|
lin_clf<span style="color: #666666">.</span>fit(X_scaled, y)
|
|
svm_clf<span style="color: #666666">.</span>fit(X_scaled, y)
|
|
sgd_clf<span style="color: #666666">.</span>fit(X_scaled, y)
|
|
|
|
<span style="color: #008000">print</span>(<span style="color: #BA2121">"LinearSVC: "</span>, lin_clf<span style="color: #666666">.</span>intercept_, lin_clf<span style="color: #666666">.</span>coef_)
|
|
<span style="color: #008000">print</span>(<span style="color: #BA2121">"SVC: "</span>, svm_clf<span style="color: #666666">.</span>intercept_, svm_clf<span style="color: #666666">.</span>coef_)
|
|
<span style="color: #008000">print</span>(<span style="color: #BA2121">"SGDClassifier(alpha=</span><span style="color: #BB6688; font-weight: bold">{:.5f}</span><span style="color: #BA2121">):"</span><span style="color: #666666">.</span>format(sgd_clf<span style="color: #666666">.</span>alpha), sgd_clf<span style="color: #666666">.</span>intercept_, sgd_clf<span style="color: #666666">.</span>coef_)
|
|
|
|
<span style="color: #408080; font-style: italic"># Compute the slope and bias of each decision boundary</span>
|
|
w1 <span style="color: #666666">=</span> <span style="color: #666666">-</span>lin_clf<span style="color: #666666">.</span>coef_[<span style="color: #666666">0</span>, <span style="color: #666666">0</span>]<span style="color: #666666">/</span>lin_clf<span style="color: #666666">.</span>coef_[<span style="color: #666666">0</span>, <span style="color: #666666">1</span>]
|
|
b1 <span style="color: #666666">=</span> <span style="color: #666666">-</span>lin_clf<span style="color: #666666">.</span>intercept_[<span style="color: #666666">0</span>]<span style="color: #666666">/</span>lin_clf<span style="color: #666666">.</span>coef_[<span style="color: #666666">0</span>, <span style="color: #666666">1</span>]
|
|
w2 <span style="color: #666666">=</span> <span style="color: #666666">-</span>svm_clf<span style="color: #666666">.</span>coef_[<span style="color: #666666">0</span>, <span style="color: #666666">0</span>]<span style="color: #666666">/</span>svm_clf<span style="color: #666666">.</span>coef_[<span style="color: #666666">0</span>, <span style="color: #666666">1</span>]
|
|
b2 <span style="color: #666666">=</span> <span style="color: #666666">-</span>svm_clf<span style="color: #666666">.</span>intercept_[<span style="color: #666666">0</span>]<span style="color: #666666">/</span>svm_clf<span style="color: #666666">.</span>coef_[<span style="color: #666666">0</span>, <span style="color: #666666">1</span>]
|
|
w3 <span style="color: #666666">=</span> <span style="color: #666666">-</span>sgd_clf<span style="color: #666666">.</span>coef_[<span style="color: #666666">0</span>, <span style="color: #666666">0</span>]<span style="color: #666666">/</span>sgd_clf<span style="color: #666666">.</span>coef_[<span style="color: #666666">0</span>, <span style="color: #666666">1</span>]
|
|
b3 <span style="color: #666666">=</span> <span style="color: #666666">-</span>sgd_clf<span style="color: #666666">.</span>intercept_[<span style="color: #666666">0</span>]<span style="color: #666666">/</span>sgd_clf<span style="color: #666666">.</span>coef_[<span style="color: #666666">0</span>, <span style="color: #666666">1</span>]
|
|
|
|
<span style="color: #408080; font-style: italic"># Transform the decision boundary lines back to the original scale</span>
|
|
line1 <span style="color: #666666">=</span> scaler<span style="color: #666666">.</span>inverse_transform([[<span style="color: #666666">-10</span>, <span style="color: #666666">-10</span> <span style="color: #666666">*</span> w1 <span style="color: #666666">+</span> b1], [<span style="color: #666666">10</span>, <span style="color: #666666">10</span> <span style="color: #666666">*</span> w1 <span style="color: #666666">+</span> b1]])
|
|
line2 <span style="color: #666666">=</span> scaler<span style="color: #666666">.</span>inverse_transform([[<span style="color: #666666">-10</span>, <span style="color: #666666">-10</span> <span style="color: #666666">*</span> w2 <span style="color: #666666">+</span> b2], [<span style="color: #666666">10</span>, <span style="color: #666666">10</span> <span style="color: #666666">*</span> w2 <span style="color: #666666">+</span> b2]])
|
|
line3 <span style="color: #666666">=</span> scaler<span style="color: #666666">.</span>inverse_transform([[<span style="color: #666666">-10</span>, <span style="color: #666666">-10</span> <span style="color: #666666">*</span> w3 <span style="color: #666666">+</span> b3], [<span style="color: #666666">10</span>, <span style="color: #666666">10</span> <span style="color: #666666">*</span> w3 <span style="color: #666666">+</span> b3]])
|
|
|
|
<span style="color: #408080; font-style: italic"># Plot all three decision boundaries</span>
|
|
plt<span style="color: #666666">.</span>figure(figsize<span style="color: #666666">=</span>(<span style="color: #666666">11</span>, <span style="color: #666666">4</span>))
|
|
plt<span style="color: #666666">.</span>plot(line1[:, <span style="color: #666666">0</span>], line1[:, <span style="color: #666666">1</span>], <span style="color: #BA2121">"k:"</span>, label<span style="color: #666666">=</span><span style="color: #BA2121">"LinearSVC"</span>)
|
|
plt<span style="color: #666666">.</span>plot(line2[:, <span style="color: #666666">0</span>], line2[:, <span style="color: #666666">1</span>], <span style="color: #BA2121">"b--"</span>, linewidth<span style="color: #666666">=2</span>, label<span style="color: #666666">=</span><span style="color: #BA2121">"SVC"</span>)
|
|
plt<span style="color: #666666">.</span>plot(line3[:, <span style="color: #666666">0</span>], line3[:, <span style="color: #666666">1</span>], <span style="color: #BA2121">"r-"</span>, label<span style="color: #666666">=</span><span style="color: #BA2121">"SGDClassifier"</span>)
|
|
plt<span style="color: #666666">.</span>plot(X[:, <span style="color: #666666">0</span>][y<span style="color: #666666">==1</span>], X[:, <span style="color: #666666">1</span>][y<span style="color: #666666">==1</span>], <span style="color: #BA2121">"bs"</span>) <span style="color: #408080; font-style: italic"># label="Iris-Versicolor"</span>
|
|
plt<span style="color: #666666">.</span>plot(X[:, <span style="color: #666666">0</span>][y<span style="color: #666666">==0</span>], X[:, <span style="color: #666666">1</span>][y<span style="color: #666666">==0</span>], <span style="color: #BA2121">"yo"</span>) <span style="color: #408080; font-style: italic"># label="Iris-Setosa"</span>
|
|
plt<span style="color: #666666">.</span>xlabel(<span style="color: #BA2121">"Petal length"</span>, fontsize<span style="color: #666666">=14</span>)
|
|
plt<span style="color: #666666">.</span>ylabel(<span style="color: #BA2121">"Petal width"</span>, fontsize<span style="color: #666666">=14</span>)
|
|
plt<span style="color: #666666">.</span>legend(loc<span style="color: #666666">=</span><span style="color: #BA2121">"upper center"</span>, fontsize<span style="color: #666666">=14</span>)
|
|
plt<span style="color: #666666">.</span>axis([<span style="color: #666666">0</span>, <span style="color: #666666">5.5</span>, <span style="color: #666666">0</span>, <span style="color: #666666">2</span>])
|
|
|
|
plt<span style="color: #666666">.</span>show()
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="output_wrapper">
|
|
<div class="output">
|
|
<div class="output_area">
|
|
<div class="output_subarea output_stream output_stdout output_text">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="what-is-a-hyperplane">What is a hyperplane? </h2>
|
|
|
|
<p>The aim of the SVM algorithm is to find a hyperplane in a
|
|
\( p \)-dimensional space, where \( p \) is the number of features that
|
|
distinctly classifies the data points.
|
|
</p>
|
|
|
|
<p>In a \( p \)-dimensional space, a hyperplane is what we call an affine subspace of dimension of \( p-1 \).
|
|
As an example, in two dimension, a hyperplane is simply as straight line while in three dimensions it is
|
|
a two-dimensional subspace, or stated simply, a plane.
|
|
</p>
|
|
|
|
<p>In two dimensions, with the variables \( x_1 \) and \( x_2 \), the hyperplane is defined as</p>
|
|
$$
|
|
b+w_1x_1+w_2x_2=0,
|
|
$$
|
|
|
|
<p>where \( b \) is the intercept and \( w_1 \) and \( w_2 \) define the elements of a vector orthogonal to the line
|
|
\( b+w_1x_1+w_2x_2=0 \).
|
|
In two dimensions we define the vectors \( \boldsymbol{x} =[x1,x2] \) and \( \boldsymbol{w}=[w1,w2] \).
|
|
We can then rewrite the above equation as
|
|
</p>
|
|
|
|
$$
|
|
\boldsymbol{x}^T\boldsymbol{w}+b=0.
|
|
$$
|
|
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="a-p-dimensional-space-of-features">A \( p \)-dimensional space of features </h2>
|
|
|
|
<p>We limit ourselves to two classes of outputs \( y_i \) and assign these classes the values \( y_i = \pm 1 \).
|
|
In a \( p \)-dimensional space of say \( p \) features we have a hyperplane defines as
|
|
</p>
|
|
$$
|
|
b+wx_1+w_2x_2+\dots +w_px_p=0.
|
|
$$
|
|
|
|
<p>If we define a
|
|
matrix \( \boldsymbol{X}=\left[\boldsymbol{x}_1,\boldsymbol{x}_2,\dots, \boldsymbol{x}_p\right] \)
|
|
of dimension \( n\times p \), where \( n \) represents the observations for each feature and each vector \( x_i \) is a column vector of the matrix \( \boldsymbol{X} \),
|
|
</p>
|
|
$$
|
|
\boldsymbol{x}_i = \begin{bmatrix} x_{i1} \\ x_{i2} \\ \dots \\ \dots \\ x_{ip} \end{bmatrix}.
|
|
$$
|
|
|
|
<p>If the above condition is not met for a given vector \( \boldsymbol{x}_i \) we have </p>
|
|
$$
|
|
b+w_1x_{i1}+w_2x_{i2}+\dots +w_px_{ip} >0,
|
|
$$
|
|
|
|
<p>if our output \( y_i=1 \).
|
|
In this case we say that \( \boldsymbol{x}_i \) lies on one of the sides of the hyperplane and if
|
|
</p>
|
|
$$
|
|
b+w_1x_{i1}+w_2x_{i2}+\dots +w_px_{ip} < 0,
|
|
$$
|
|
|
|
<p>for the class of observations \( y_i=-1 \),
|
|
then \( \boldsymbol{x}_i \) lies on the other side.
|
|
</p>
|
|
|
|
<p>Equivalently, for the two classes of observations we have </p>
|
|
$$
|
|
y_i\left(b+w_1x_{i1}+w_2x_{i2}+\dots +w_px_{ip}\right) > 0.
|
|
$$
|
|
|
|
<p>When we try to separate hyperplanes, if it exists, we can use it to construct a natural classifier: a test observation is assigned a given class depending on which side of the hyperplane it is located.</p>
|
|
|
|
<!-- !split -->
|
|
<h2 id="the-two-dimensional-case">The two-dimensional case </h2>
|
|
|
|
<p>Let us try to develop our intuition about SVMs by limiting ourselves to a two-dimensional
|
|
plane. To separate the two classes of data points, there are many
|
|
possible lines (hyperplanes if you prefer a more strict naming)
|
|
that could be chosen. Our objective is to find a
|
|
plane that has the maximum margin, i.e the maximum distance between
|
|
data points of both classes. Maximizing the margin distance provides
|
|
some reinforcement so that future data points can be classified with
|
|
more confidence.
|
|
</p>
|
|
|
|
<p>What a linear classifier attempts to accomplish is to split the
|
|
feature space into two half spaces by placing a hyperplane between the
|
|
data points. This hyperplane will be our decision boundary. All
|
|
points on one side of the plane will belong to class one and all points
|
|
on the other side of the plane will belong to the second class two.
|
|
</p>
|
|
|
|
<p>Unfortunately there are many ways in which we can place a hyperplane
|
|
to divide the data. Below is an example of two candidate hyperplanes
|
|
for our data sample.
|
|
</p>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="getting-into-the-details">Getting into the details </h2>
|
|
|
|
<p>Let us define the function</p>
|
|
$$
|
|
f(x) = \boldsymbol{w}^T\boldsymbol{x}+b = 0,
|
|
$$
|
|
|
|
<p>as the function that determines the line \( L \) that separates two classes (our two features), see the figure here. </p>
|
|
|
|
<p>Any point defined by \( \boldsymbol{x}_i \) and \( \boldsymbol{x}_2 \) on the line \( L \) will satisfy \( \boldsymbol{w}^T(\boldsymbol{x}_1-\boldsymbol{x}_2)=0 \). </p>
|
|
|
|
<p>The signed distance \( \delta \) from any point defined by a vector \( \boldsymbol{x} \) and a point \( \boldsymbol{x}_0 \) on the line \( L \) is then</p>
|
|
$$
|
|
\delta = \frac{1}{\vert\vert \boldsymbol{w}\vert\vert}(\boldsymbol{w}^T\boldsymbol{x}+b).
|
|
$$
|
|
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="first-attempt-at-a-minimization-approach">First attempt at a minimization approach </h2>
|
|
|
|
<p>How do we find the parameter \( b \) and the vector \( \boldsymbol{w} \)? What we could
|
|
do is to define a cost function which now contains the set of all
|
|
misclassified points \( M \) and attempt to minimize this function
|
|
</p>
|
|
|
|
$$
|
|
C(\boldsymbol{w},b) = -\sum_{i\in M} y_i(\boldsymbol{w}^T\boldsymbol{x}_i+b).
|
|
$$
|
|
|
|
<p>We could now for example define all values \( y_i =1 \) as misclassified in case we have \( \boldsymbol{w}^T\boldsymbol{x}_i+b < 0 \) and the opposite if we have \( y_i=-1 \). Taking the derivatives gives us</p>
|
|
$$
|
|
\frac{\partial C}{\partial b} = -\sum_{i\in M} y_i,
|
|
$$
|
|
|
|
<p>and </p>
|
|
$$
|
|
\frac{\partial C}{\partial \boldsymbol{w}} = -\sum_{i\in M} y_ix_i.
|
|
$$
|
|
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="solving-the-equations">Solving the equations </h2>
|
|
|
|
<p>We can now use the Newton-Raphson method or different variants of the gradient descent family (from plain gradient descent to various stochastic gradient descent approaches) to solve the equations</p>
|
|
$$
|
|
b \leftarrow b +\eta \frac{\partial C}{\partial b},
|
|
$$
|
|
|
|
<p>and</p>
|
|
$$
|
|
\boldsymbol{w} \leftarrow \boldsymbol{w} +\eta \frac{\partial C}{\partial \boldsymbol{w}},
|
|
$$
|
|
|
|
<p>where \( \eta \) is our by now well-known learning rate. </p>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="code-example">Code Example </h2>
|
|
|
|
<p>The equations we discussed above can be coded rather easily (the
|
|
framework is similar to what we developed for logistic
|
|
regression). We are going to set up a simple case with two classes only and we want to find a line which separates them the best possible way.
|
|
</p>
|
|
|
|
<!-- code=python (!bc pycod) typeset with pygments style "default" -->
|
|
<div class="cell border-box-sizing code_cell rendered">
|
|
<div class="input">
|
|
<div class="inner_cell">
|
|
<div class="input_area">
|
|
<div class="highlight" style="background: #f8f8f8">
|
|
<pre style="line-height: 125%;">
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="output_wrapper">
|
|
<div class="output">
|
|
<div class="output_area">
|
|
<div class="output_subarea output_stream output_stdout output_text">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="problems-with-the-simpler-approach">Problems with the Simpler Approach </h2>
|
|
|
|
<p>There are however problems with this approach, although it looks
|
|
pretty straightforward to implement. When running the above code, we see that we can easily end up with many diffeent lines which separate the two classes.
|
|
</p>
|
|
|
|
<p>For small
|
|
gaps between the entries, we may also end up needing many iterations
|
|
before the solutions converge and if the data cannot be separated
|
|
properly into two distinct classes, we may not experience a converge
|
|
at all.
|
|
</p>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="a-better-approach">A better approach </h2>
|
|
|
|
<p>A better approach is rather to try to define a large margin between
|
|
the two classes (if they are well separated from the beginning).
|
|
</p>
|
|
|
|
<p>Thus, we wish to find a margin \( M \) with \( \boldsymbol{w} \) normalized to
|
|
\( \vert\vert \boldsymbol{w}\vert\vert =1 \) subject to the condition
|
|
</p>
|
|
|
|
$$
|
|
y_i(\boldsymbol{w}^T\boldsymbol{x}_i+b) \geq M \hspace{0.1cm}\forall i=1,2,\dots, p.
|
|
$$
|
|
|
|
<p>All points are thus at a signed distance from the decision boundary defined by the line \( L \). The parameters \( b \) and \( w_1 \) and \( w_2 \) define this line. </p>
|
|
|
|
<p>We seek thus the largest value \( M \) defined by</p>
|
|
$$
|
|
\frac{1}{\vert \vert \boldsymbol{w}\vert\vert}y_i(\boldsymbol{w}^T\boldsymbol{x}_i+b) \geq M \hspace{0.1cm}\forall i=1,2,\dots, n,
|
|
$$
|
|
|
|
<p>or just </p>
|
|
$$
|
|
y_i(\boldsymbol{w}^T\boldsymbol{x}_i+b) \geq M\vert \vert \boldsymbol{w}\vert\vert \hspace{0.1cm}\forall i.
|
|
$$
|
|
|
|
<p>If we scale the equation so that \( \vert \vert \boldsymbol{w}\vert\vert = 1/M \), we have to find the minimum of
|
|
\( \boldsymbol{w}^T\boldsymbol{w}=\vert \vert \boldsymbol{w}\vert\vert \) (the norm) subject to the condition
|
|
</p>
|
|
$$
|
|
y_i(\boldsymbol{w}^T\boldsymbol{x}_i+b) \geq 1 \hspace{0.1cm}\forall i.
|
|
$$
|
|
|
|
<p>We have thus defined our margin as the invers of the norm of
|
|
\( \boldsymbol{w} \). We want to minimize the norm in order to have a as large as
|
|
possible margin \( M \). Before we proceed, we need to remind ourselves
|
|
about Lagrangian multipliers.
|
|
</p>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="a-quick-reminder-on-lagrangian-multipliers">A quick Reminder on Lagrangian Multipliers </h2>
|
|
|
|
<p>Consider a function of three independent variables \( f(x,y,z) \) . For the function \( f \) to be an
|
|
extreme we have
|
|
</p>
|
|
$$
|
|
df=0.
|
|
$$
|
|
|
|
<p>A necessary and sufficient condition is</p>
|
|
$$
|
|
\frac{\partial f}{\partial x} =\frac{\partial f}{\partial y}=\frac{\partial f}{\partial z}=0,
|
|
$$
|
|
|
|
<p>due to</p>
|
|
$$
|
|
df = \frac{\partial f}{\partial x}dx+\frac{\partial f}{\partial y}dy+\frac{\partial f}{\partial z}dz.
|
|
$$
|
|
|
|
<p>In many problems the variables \( x,y,z \) are often subject to constraints (such as those above for the margin)
|
|
so that they are no longer all independent. It is possible at least in principle to use each
|
|
constraint to eliminate one variable
|
|
and to proceed with a new and smaller set of independent varables.
|
|
</p>
|
|
|
|
<p>The use of so-called Lagrangian multipliers is an alternative technique when the elimination
|
|
of variables is incovenient or undesirable. Assume that we have an equation of constraint on
|
|
the variables \( x,y,z \)
|
|
</p>
|
|
$$
|
|
\phi(x,y,z) = 0,
|
|
$$
|
|
|
|
<p> resulting in</p>
|
|
$$
|
|
d\phi = \frac{\partial \phi}{\partial x}dx+\frac{\partial \phi}{\partial y}dy+\frac{\partial \phi}{\partial z}dz =0.
|
|
$$
|
|
|
|
<p>Now we cannot set anymore</p>
|
|
$$
|
|
\frac{\partial f}{\partial x} =\frac{\partial f}{\partial y}=\frac{\partial f}{\partial z}=0,
|
|
$$
|
|
|
|
<p>if \( df=0 \) is wanted
|
|
because there are now only two independent variables! Assume \( x \) and \( y \) are the independent
|
|
variables.
|
|
Then \( dz \) is no longer arbitrary.
|
|
</p>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="adding-the-multiplier">Adding the Multiplier </h2>
|
|
|
|
<p>However, we can add to</p>
|
|
$$
|
|
df = \frac{\partial f}{\partial x}dx+\frac{\partial f}{\partial y}dy+\frac{\partial f}{\partial z}dz,
|
|
$$
|
|
|
|
<p>a multiplum of \( d\phi \), viz. \( \lambda d\phi \), resulting in</p>
|
|
$$
|
|
df+\lambda d\phi = (\frac{\partial f}{\partial z}+\lambda
|
|
\frac{\partial \phi}{\partial x})dx+(\frac{\partial f}{\partial y}+\lambda\frac{\partial \phi}{\partial y})dy+
|
|
(\frac{\partial f}{\partial z}+\lambda\frac{\partial \phi}{\partial z})dz =0.
|
|
$$
|
|
|
|
<p>Our multiplier is chosen so that</p>
|
|
$$
|
|
\frac{\partial f}{\partial z}+\lambda\frac{\partial \phi}{\partial z} =0.
|
|
$$
|
|
|
|
<p>We need to remember that we took \( dx \) and \( dy \) to be arbitrary and thus we must have</p>
|
|
$$
|
|
\frac{\partial f}{\partial x}+\lambda\frac{\partial \phi}{\partial x} =0,
|
|
$$
|
|
|
|
<p>and</p>
|
|
$$
|
|
\frac{\partial f}{\partial y}+\lambda\frac{\partial \phi}{\partial y} =0.
|
|
$$
|
|
|
|
<p>When all these equations are satisfied, \( df=0 \). We have four unknowns, \( x,y,z \) and
|
|
\( \lambda \). Actually we want only \( x,y,z \), \( \lambda \) needs not to be determined,
|
|
it is therefore often called
|
|
Lagrange's undetermined multiplier.
|
|
If we have a set of constraints \( \phi_k \) we have the equations
|
|
</p>
|
|
$$
|
|
\frac{\partial f}{\partial x_i}+\sum_k\lambda_k\frac{\partial \phi_k}{\partial x_i} =0.
|
|
$$
|
|
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="setting-up-the-problem">Setting up the Problem </h2>
|
|
<p>In order to solve the above problem, we define the following Lagrangian function to be minimized </p>
|
|
$$
|
|
{\cal L}(\lambda,b,\boldsymbol{w})=\frac{1}{2}\boldsymbol{w}^T\boldsymbol{w}-\sum_{i=1}^n\lambda_i\left[y_i(\boldsymbol{w}^T\boldsymbol{x}_i+b)-1\right],
|
|
$$
|
|
|
|
<p>where \( \lambda_i \) is a so-called Lagrange multiplier subject to the condition \( \lambda_i \geq 0 \).</p>
|
|
|
|
<p>Taking the derivatives with respect to \( b \) and \( \boldsymbol{w} \) we obtain </p>
|
|
$$
|
|
\frac{\partial {\cal L}}{\partial b} = -\sum_{i} \lambda_iy_i=0,
|
|
$$
|
|
|
|
<p>and </p>
|
|
$$
|
|
\frac{\partial {\cal L}}{\partial \boldsymbol{w}} = 0 = \boldsymbol{w}-\sum_{i} \lambda_iy_i\boldsymbol{x}_i.
|
|
$$
|
|
|
|
<p>Inserting these constraints into the equation for \( {\cal L} \) we obtain</p>
|
|
$$
|
|
{\cal L}=\sum_i\lambda_i-\frac{1}{2}\sum_{ij}^n\lambda_i\lambda_jy_iy_j\boldsymbol{x}_i^T\boldsymbol{x}_j,
|
|
$$
|
|
|
|
<p>subject to the constraints \( \lambda_i\geq 0 \) and \( \sum_i\lambda_iy_i=0 \).
|
|
We must in addition satisfy the <a href="https://en.wikipedia.org/wiki/Karush%E2%80%93Kuhn%E2%80%93Tucker_conditions" target="_blank">Karush-Kuhn-Tucker</a> (KKT) condition
|
|
</p>
|
|
$$
|
|
\lambda_i\left[y_i(\boldsymbol{w}^T\boldsymbol{x}_i+b) -1\right] \hspace{0.1cm}\forall i.
|
|
$$
|
|
|
|
<ol>
|
|
<li> If \( \lambda_i > 0 \), then \( y_i(\boldsymbol{w}^T\boldsymbol{x}_i+b)=1 \) and we say that \( x_i \) is on the boundary.</li>
|
|
<li> If \( y_i(\boldsymbol{w}^T\boldsymbol{x}_i+b)> 1 \), we say \( x_i \) is not on the boundary and we set \( \lambda_i=0 \).</li>
|
|
</ol>
|
|
<p>When \( \lambda_i > 0 \), the vectors \( \boldsymbol{x}_i \) are called support vectors. They are the vectors closest to the line (or hyperplane) and define the margin \( M \). </p>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="the-problem-to-solve">The problem to solve </h2>
|
|
|
|
<p>We can rewrite </p>
|
|
$$
|
|
{\cal L}=\sum_i\lambda_i-\frac{1}{2}\sum_{ij}^n\lambda_i\lambda_jy_iy_j\boldsymbol{x}_i^T\boldsymbol{x}_j,
|
|
$$
|
|
|
|
<p>and its constraints in terms of a matrix-vector problem where we minimize w.r.t. \( \lambda \) the following problem</p>
|
|
$$
|
|
\frac{1}{2} \boldsymbol{\lambda}^T\begin{bmatrix} y_1y_1\boldsymbol{x}_1^T\boldsymbol{x}_1 & y_1y_2\boldsymbol{x}_1^T\boldsymbol{x}_2 & \dots & \dots & y_1y_n\boldsymbol{x}_1^T\boldsymbol{x}_n \\
|
|
y_2y_1\boldsymbol{x}_2^T\boldsymbol{x}_1 & y_2y_2\boldsymbol{x}_2^T\boldsymbol{x}_2 & \dots & \dots & y_1y_n\boldsymbol{x}_2^T\boldsymbol{x}_n \\
|
|
\dots & \dots & \dots & \dots & \dots \\
|
|
\dots & \dots & \dots & \dots & \dots \\
|
|
y_ny_1\boldsymbol{x}_n^T\boldsymbol{x}_1 & y_ny_2\boldsymbol{x}_n^T\boldsymbol{x}_2 & \dots & \dots & y_ny_n\boldsymbol{x}_n^T\boldsymbol{x}_n \\
|
|
\end{bmatrix}\boldsymbol{\lambda}-\mathbb{1}\boldsymbol{\lambda},
|
|
$$
|
|
|
|
<p>subject to \( \boldsymbol{y}^T\boldsymbol{\lambda}=0 \). Here we defined the vectors \( \boldsymbol{\lambda} =[\lambda_1,\lambda_2,\dots,\lambda_n] \) and
|
|
\( \boldsymbol{y}=[y_1,y_2,\dots,y_n] \).
|
|
</p>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="the-last-steps">The last steps </h2>
|
|
|
|
<p>Solving the above problem, yields the values of \( \lambda_i \).
|
|
To find the coefficients of your hyperplane we need simply to compute
|
|
</p>
|
|
$$
|
|
\boldsymbol{w}=\sum_{i} \lambda_iy_i\boldsymbol{x}_i.
|
|
$$
|
|
|
|
<p>With our vector \( \boldsymbol{w} \) we can in turn find the value of the intercept \( b \) (here in two dimensions) via </p>
|
|
$$
|
|
y_i(\boldsymbol{w}^T\boldsymbol{x}_i+b)=1,
|
|
$$
|
|
|
|
<p>resulting in</p>
|
|
$$
|
|
b = \frac{1}{y_i}-\boldsymbol{w}^T\boldsymbol{x}_i,
|
|
$$
|
|
|
|
<p>or if we write it out in terms of the support vectors only, with \( N_s \) being their number, we have</p>
|
|
$$
|
|
b = \frac{1}{N_s}\sum_{j\in N_s}\left(y_j-\sum_{i=1}^n\lambda_iy_i\boldsymbol{x}_i^T\boldsymbol{x}_j\right).
|
|
$$
|
|
|
|
<p>With our hyperplane coefficients we can use our classifier to assign any observation by simply using </p>
|
|
$$
|
|
y_i = \mathrm{sign}(\boldsymbol{w}^T\boldsymbol{x}_i+b).
|
|
$$
|
|
|
|
<p>Below we discuss how to find the optimal values of \( \lambda_i \). Before we proceed however, we discuss now the so-called soft classifier. </p>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="a-soft-classifier">A soft classifier </h2>
|
|
|
|
<p>Till now, the margin is strictly defined by the support vectors. This defines what is called a hard classifier, that is the margins are well defined.</p>
|
|
|
|
<p>Suppose now that classes overlap in feature space, as shown in the
|
|
figure here. One way to deal with this problem before we define the
|
|
so-called <b>kernel approach</b>, is to allow a kind of slack in the sense
|
|
that we allow some points to be on the wrong side of the margin.
|
|
</p>
|
|
|
|
<p>We introduce thus the so-called <b>slack</b> variables \( \boldsymbol{\xi} =[\xi_1,x_2,\dots,x_n] \) and
|
|
modify our previous equation
|
|
</p>
|
|
$$
|
|
y_i(\boldsymbol{w}^T\boldsymbol{x}_i+b)=1,
|
|
$$
|
|
|
|
<p>to </p>
|
|
$$
|
|
y_i(\boldsymbol{w}^T\boldsymbol{x}_i+b)=1-\xi_i,
|
|
$$
|
|
|
|
<p>with the requirement \( \xi_i\geq 0 \). The total violation is now \( \sum_i\xi \).
|
|
The value \( \xi_i \) in the constraint the last constraint corresponds to the amount by which the prediction
|
|
\( y_i(\boldsymbol{w}^T\boldsymbol{x}_i+b)=1 \) is on the wrong side of its margin. Hence by bounding the sum \( \sum_i \xi_i \),
|
|
we bound the total amount by which predictions fall on the wrong side of their margins.
|
|
</p>
|
|
|
|
<p>Misclassifications occur when \( \xi_i > 1 \). Thus bounding the total sum by some value \( C \) bounds in turn the total number of
|
|
misclassifications.
|
|
</p>
|
|
|
|
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
|
|
<h2 id="soft-optmization-problem">Soft optmization problem </h2>
|
|
|
|
<p>This has in turn the consequences that we change our optmization problem to finding the minimum of </p>
|
|
$$
|
|
{\cal L}=\frac{1}{2}\boldsymbol{w}^T\boldsymbol{w}-\sum_{i=1}^n\lambda_i\left[y_i(\boldsymbol{w}^T\boldsymbol{x}_i+b)-(1-\xi_)\right]+C\sum_{i=1}^n\xi_i-\sum_{i=1}^n\gamma_i\xi_i,
|
|
$$
|
|
|
|
<p>subject to </p>
|
|
$$
|
|
y_i(\boldsymbol{w}^T\boldsymbol{x}_i+b)=1-\xi_i \hspace{0.1cm}\forall i,
|
|
$$
|
|
|
|
<p>with the requirement \( \xi_i\geq 0 \).</p>
|
|
|
|
<p>Taking the derivatives with respect to \( b \) and \( \boldsymbol{w} \) we obtain </p>
|
|
$$
|
|
\frac{\partial {\cal L}}{\partial b} = -\sum_{i} \lambda_iy_i=0,
|
|
$$
|
|
|
|
<p>and </p>
|
|
$$
|
|
\frac{\partial {\cal L}}{\partial \boldsymbol{w}} = 0 = \boldsymbol{w}-\sum_{i} \lambda_iy_i\boldsymbol{x}_i,
|
|
$$
|
|
|
|
<p>and</p>
|
|
$$
|
|
\lambda_i = C-\gamma_i \hspace{0.1cm}\forall i.
|
|
$$
|
|
|
|
<p>Inserting these constraints into the equation for \( {\cal L} \) we obtain the same equation as before</p>
|
|
$$
|
|
{\cal L}=\sum_i\lambda_i-\frac{1}{2}\sum_{ij}^n\lambda_i\lambda_jy_iy_j\boldsymbol{x}_i^T\boldsymbol{x}_j,
|
|
$$
|
|
|
|
<p>but now subject to the constraints \( \lambda_i\geq 0 \), \( \sum_i\lambda_iy_i=0 \) and \( 0\leq\lambda_i \leq C \).
|
|
We must in addition satisfy the Karush-Kuhn-Tucker condition which now reads
|
|
</p>
|
|
$$
|
|
\lambda_i\left[y_i(\boldsymbol{w}^T\boldsymbol{x}_i+b) -(1-\xi_)\right]=0 \hspace{0.1cm}\forall i,
|
|
$$
|
|
|
|
$$
|
|
\gamma_i\xi_i = 0,
|
|
$$
|
|
|
|
<p>and </p>
|
|
$$
|
|
y_i(\boldsymbol{w}^T\boldsymbol{x}_i+b) -(1-\xi_) \geq 0 \hspace{0.1cm}\forall i.
|
|
$$
|
|
|
|
<!-- ------------------- end of main content --------------- -->
|
|
<center style="font-size:80%">
|
|
<!-- copyright --> © 1999-2022, Morten Hjorth-Jensen. Released under CC Attribution-NonCommercial 4.0 license
|
|
</center>
|
|
</body>
|
|
</html>
|
|
|