updating week44

This commit is contained in:
Morten Hjorth-Jensen
2021-11-02 08:05:38 +01:00
parent ec963b76e0
commit 6346f66312
7 changed files with 3274 additions and 210 deletions
+104 -52
View File
@@ -8,8 +8,8 @@ doconce format html week44.do.txt --html_style=bootstrap --pygments_html_style=d
<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 44: From Decision Trees to Bagging methods">
<title>Week 44: From Decision Trees to Bagging methods</title>
<meta name="description" content="Week 44: Dimensionality Reduction, PCA and Clustering. Decision Trees">
<title>Week 44: Dimensionality Reduction, PCA and Clustering. Decision Trees</title>
<!-- Bootstrap style: bootstrap -->
<!-- doconce format html week44.do.txt --html_style=bootstrap --pygments_html_style=default --html_admon=bootstrap_panel --html_output=week44-bs --no_mako -->
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
@@ -37,7 +37,45 @@ doconce format html week44.do.txt --html_style=bootstrap --pygments_html_style=d
<!-- tocinfo
{'highest level': 2,
'sections': [('Overview of week 44', 2, None, 'overview-of-week-44'),
('Thursday', 2, None, 'thursday'),
('Thursday, Principal Component Analysis',
2,
None,
'thursday-principal-component-analysis'),
('Thursday: Clustering and Unsupervised Learning',
2,
None,
'thursday-clustering-and-unsupervised-learning'),
('Basic Idea of the $k$-means Clustering Algorithm',
2,
None,
'basic-idea-of-the-k-means-clustering-algorithm'),
('The $k$-means Algorithm', 2, None, 'the-k-means-algorithm'),
('Basic Math of the $k$-means Algorithm',
2,
None,
'basic-math-of-the-k-means-algorithm'),
('Within Cluster Point Scatter',
2,
None,
'within-cluster-point-scatter'),
('More Details', 2, None, 'more-details'),
('Total Cluster Variance', 2, None, 'total-cluster-variance'),
('The $k$-means Clustering Algorithm',
2,
None,
'the-k-means-clustering-algorithm'),
('Summarizing', 2, None, 'summarizing'),
('Writing our own Code, the Data Set',
2,
None,
'writing-our-own-code-the-data-set'),
('Implementing the $k$-means Algorithm',
2,
None,
'implementing-the-k-means-algorithm'),
('Plotting', 2, None, 'plotting'),
('Continuing', 2, None, 'continuing'),
('Wrapping it up', 2, None, 'wrapping-it-up'),
('Decision trees, overarching aims',
2,
None,
@@ -199,7 +237,7 @@ MathJax.Hub.Config({
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="week44-bs.html">Week 44: From Decision Trees to Bagging methods</a>
<a class="navbar-brand" href="week44-bs.html">Week 44: Dimensionality Reduction, PCA and Clustering. Decision Trees</a>
</div>
<div class="navbar-collapse collapse navbar-responsive-collapse">
<ul class="nav navbar-nav navbar-right">
@@ -207,51 +245,65 @@ MathJax.Hub.Config({
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Contents <b class="caret"></b></a>
<ul class="dropdown-menu">
<!-- navigation toc: --> <li><a href="._week44-bs001.html#overview-of-week-44" style="font-size: 80%;">Overview of week 44</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs002.html#thursday" style="font-size: 80%;">Thursday</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs003.html#decision-trees-overarching-aims" style="font-size: 80%;">Decision trees, overarching aims</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs004.html#basics-of-a-tree" style="font-size: 80%;">Basics of a tree</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs005.html#a-sketch-of-a-tree-regression-problem" style="font-size: 80%;">A Sketch of a Tree, Regression problem</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs006.html#a-sketch-of-a-tree-classification-problem" style="font-size: 80%;">A Sketch of a Tree, Classification problem</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs007.html#a-typical-decision-tree-with-its-pertinent-jargon-classification-problem" style="font-size: 80%;">A typical Decision Tree with its pertinent Jargon, Classification Problem</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs008.html#general-features" style="font-size: 80%;">General Features</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs009.html#how-do-we-set-it-up" style="font-size: 80%;">How do we set it up?</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs010.html#decision-trees-and-regression" style="font-size: 80%;">Decision trees and Regression</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs011.html#building-a-tree-regression" style="font-size: 80%;">Building a tree, regression</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs012.html#a-top-down-approach-recursive-binary-splitting" style="font-size: 80%;">A top-down approach, recursive binary splitting</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs013.html#making-a-tree" style="font-size: 80%;">Making a tree</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs014.html#pruning-the-tree" style="font-size: 80%;">Pruning the tree</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs015.html#cost-complexity-pruning" style="font-size: 80%;">Cost complexity pruning</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs016.html#schematic-regression-procedure" style="font-size: 80%;">Schematic Regression Procedure</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs017.html#a-classification-tree" style="font-size: 80%;">A Classification Tree</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs018.html#growing-a-classification-tree" style="font-size: 80%;">Growing a classification tree</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs019.html#classification-tree-how-to-split-nodes" style="font-size: 80%;">Classification tree, how to split nodes</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs020.html#visualizing-the-tree-classification" style="font-size: 80%;">Visualizing the Tree, Classification</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs021.html#visualizing-the-tree-the-moons" style="font-size: 80%;">Visualizing the Tree, The Moons</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs022.html#other-ways-of-visualizing-the-trees" style="font-size: 80%;">Other ways of visualizing the trees</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs023.html#printing-out-as-text" style="font-size: 80%;">Printing out as text</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs024.html#algorithms-for-setting-up-decision-trees" style="font-size: 80%;">Algorithms for Setting up Decision Trees</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs025.html#the-cart-algorithm-for-classification" style="font-size: 80%;">The CART algorithm for Classification</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs026.html#the-cart-algorithm-for-regression" style="font-size: 80%;">The CART algorithm for Regression</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs027.html#computing-the-gini-index" style="font-size: 80%;">Computing the Gini index</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs028.html#simple-python-code-to-read-in-data-and-perform-classification" style="font-size: 80%;">Simple Python Code to read in Data and perform Classification</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs029.html#computing-the-gini-factor" style="font-size: 80%;">Computing the Gini Factor</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs030.html#entropy-and-the-id3-algorithm" style="font-size: 80%;">Entropy and the ID3 algorithm</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs031.html#cancer-data-again-now-with-decision-trees-and-other-methods" style="font-size: 80%;">Cancer Data again now with Decision Trees and other Methods</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs032.html#another-example-the-moons-again" style="font-size: 80%;">Another example, the moons again</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs033.html#playing-around-with-regions" style="font-size: 80%;">Playing around with regions</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs034.html#regression-trees" style="font-size: 80%;">Regression trees</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs035.html#final-regressor-code" style="font-size: 80%;">Final regressor code</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs036.html#pros-and-cons-of-trees-pros" style="font-size: 80%;">Pros and cons of trees, pros</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs037.html#disadvantages" style="font-size: 80%;">Disadvantages</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs038.html#ensemble-methods-from-a-single-tree-to-many-trees-and-extreme-boosting-meet-the-jungle-of-methods" style="font-size: 80%;">Ensemble Methods: From a Single Tree to Many Trees and Extreme Boosting, Meet the Jungle of Methods</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs039.html#an-overview-of-ensemble-methods" style="font-size: 80%;">An Overview of Ensemble Methods</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs040.html#bagging" style="font-size: 80%;">Bagging</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs041.html#more-bagging" style="font-size: 80%;">More bagging</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs042.html#simple-voting-example-head-or-tail" style="font-size: 80%;">Simple Voting Example, head or tail</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs043.html#using-the-voting-classifier" style="font-size: 80%;">Using the Voting Classifier</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs044.html#please-not-the-moons-again-voting-and-bagging" style="font-size: 80%;">Please, not the moons again! Voting and Bagging</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs045.html#bagging-examples" style="font-size: 80%;">Bagging Examples</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs046.html#making-your-own-bootstrap-changing-the-level-of-the-decision-tree" style="font-size: 80%;">Making your own Bootstrap: Changing the Level of the Decision Tree</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs002.html#thursday-principal-component-analysis" style="font-size: 80%;">Thursday, Principal Component Analysis</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs003.html#thursday-clustering-and-unsupervised-learning" style="font-size: 80%;">Thursday: Clustering and Unsupervised Learning</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs004.html#basic-idea-of-the-k-means-clustering-algorithm" style="font-size: 80%;">Basic Idea of the \( k \)-means Clustering Algorithm</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs005.html#the-k-means-algorithm" style="font-size: 80%;">The \( k \)-means Algorithm</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs006.html#basic-math-of-the-k-means-algorithm" style="font-size: 80%;">Basic Math of the \( k \)-means Algorithm</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs007.html#within-cluster-point-scatter" style="font-size: 80%;">Within Cluster Point Scatter</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs008.html#more-details" style="font-size: 80%;">More Details</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs009.html#total-cluster-variance" style="font-size: 80%;">Total Cluster Variance</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs010.html#the-k-means-clustering-algorithm" style="font-size: 80%;">The \( k \)-means Clustering Algorithm</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs011.html#summarizing" style="font-size: 80%;">Summarizing</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs012.html#writing-our-own-code-the-data-set" style="font-size: 80%;">Writing our own Code, the Data Set</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs013.html#implementing-the-k-means-algorithm" style="font-size: 80%;">Implementing the \( k \)-means Algorithm</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs014.html#plotting" style="font-size: 80%;">Plotting</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs015.html#continuing" style="font-size: 80%;">Continuing</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs016.html#wrapping-it-up" style="font-size: 80%;">Wrapping it up</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs017.html#decision-trees-overarching-aims" style="font-size: 80%;">Decision trees, overarching aims</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs018.html#basics-of-a-tree" style="font-size: 80%;">Basics of a tree</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs019.html#a-sketch-of-a-tree-regression-problem" style="font-size: 80%;">A Sketch of a Tree, Regression problem</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs020.html#a-sketch-of-a-tree-classification-problem" style="font-size: 80%;">A Sketch of a Tree, Classification problem</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs021.html#a-typical-decision-tree-with-its-pertinent-jargon-classification-problem" style="font-size: 80%;">A typical Decision Tree with its pertinent Jargon, Classification Problem</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs022.html#general-features" style="font-size: 80%;">General Features</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs023.html#how-do-we-set-it-up" style="font-size: 80%;">How do we set it up?</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs024.html#decision-trees-and-regression" style="font-size: 80%;">Decision trees and Regression</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs025.html#building-a-tree-regression" style="font-size: 80%;">Building a tree, regression</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs026.html#a-top-down-approach-recursive-binary-splitting" style="font-size: 80%;">A top-down approach, recursive binary splitting</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs027.html#making-a-tree" style="font-size: 80%;">Making a tree</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs028.html#pruning-the-tree" style="font-size: 80%;">Pruning the tree</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs029.html#cost-complexity-pruning" style="font-size: 80%;">Cost complexity pruning</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs030.html#schematic-regression-procedure" style="font-size: 80%;">Schematic Regression Procedure</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs031.html#a-classification-tree" style="font-size: 80%;">A Classification Tree</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs032.html#growing-a-classification-tree" style="font-size: 80%;">Growing a classification tree</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs033.html#classification-tree-how-to-split-nodes" style="font-size: 80%;">Classification tree, how to split nodes</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs034.html#visualizing-the-tree-classification" style="font-size: 80%;">Visualizing the Tree, Classification</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs035.html#visualizing-the-tree-the-moons" style="font-size: 80%;">Visualizing the Tree, The Moons</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs036.html#other-ways-of-visualizing-the-trees" style="font-size: 80%;">Other ways of visualizing the trees</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs037.html#printing-out-as-text" style="font-size: 80%;">Printing out as text</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs038.html#algorithms-for-setting-up-decision-trees" style="font-size: 80%;">Algorithms for Setting up Decision Trees</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs039.html#the-cart-algorithm-for-classification" style="font-size: 80%;">The CART algorithm for Classification</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs040.html#the-cart-algorithm-for-regression" style="font-size: 80%;">The CART algorithm for Regression</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs041.html#computing-the-gini-index" style="font-size: 80%;">Computing the Gini index</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs042.html#simple-python-code-to-read-in-data-and-perform-classification" style="font-size: 80%;">Simple Python Code to read in Data and perform Classification</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs043.html#computing-the-gini-factor" style="font-size: 80%;">Computing the Gini Factor</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs044.html#entropy-and-the-id3-algorithm" style="font-size: 80%;">Entropy and the ID3 algorithm</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs045.html#cancer-data-again-now-with-decision-trees-and-other-methods" style="font-size: 80%;">Cancer Data again now with Decision Trees and other Methods</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs046.html#another-example-the-moons-again" style="font-size: 80%;">Another example, the moons again</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs047.html#playing-around-with-regions" style="font-size: 80%;">Playing around with regions</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs048.html#regression-trees" style="font-size: 80%;">Regression trees</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs049.html#final-regressor-code" style="font-size: 80%;">Final regressor code</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs050.html#pros-and-cons-of-trees-pros" style="font-size: 80%;">Pros and cons of trees, pros</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs051.html#disadvantages" style="font-size: 80%;">Disadvantages</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs052.html#ensemble-methods-from-a-single-tree-to-many-trees-and-extreme-boosting-meet-the-jungle-of-methods" style="font-size: 80%;">Ensemble Methods: From a Single Tree to Many Trees and Extreme Boosting, Meet the Jungle of Methods</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs053.html#an-overview-of-ensemble-methods" style="font-size: 80%;">An Overview of Ensemble Methods</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs054.html#bagging" style="font-size: 80%;">Bagging</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs055.html#more-bagging" style="font-size: 80%;">More bagging</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs056.html#simple-voting-example-head-or-tail" style="font-size: 80%;">Simple Voting Example, head or tail</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs057.html#using-the-voting-classifier" style="font-size: 80%;">Using the Voting Classifier</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs058.html#please-not-the-moons-again-voting-and-bagging" style="font-size: 80%;">Please, not the moons again! Voting and Bagging</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs059.html#bagging-examples" style="font-size: 80%;">Bagging Examples</a></li>
<!-- navigation toc: --> <li><a href="._week44-bs060.html#making-your-own-bootstrap-changing-the-level-of-the-decision-tree" style="font-size: 80%;">Making your own Bootstrap: Changing the Level of the Decision Tree</a></li>
</ul>
</li>
@@ -265,7 +317,7 @@ MathJax.Hub.Config({
<!-- ------------------- main content ---------------------- -->
<div class="jumbotron">
<center>
<h1>Week 44: From Decision Trees to Bagging methods</h1>
<h1>Week 44: Dimensionality Reduction, PCA and Clustering. Decision Trees</h1>
</center> <!-- document title -->
<!-- author(s): Morten Hjorth-Jensen -->
@@ -281,7 +333,7 @@ MathJax.Hub.Config({
</center>
<br>
<center>
<h4>Nov 1, 2021</h4>
<h4>Nov 2, 2021</h4>
</center> <!-- date -->
<br>
@@ -306,7 +358,7 @@ MathJax.Hub.Config({
<li><a href="._week44-bs008.html">9</a></li>
<li><a href="._week44-bs009.html">10</a></li>
<li><a href="">...</a></li>
<li><a href="._week44-bs046.html">47</a></li>
<li><a href="._week44-bs060.html">61</a></li>
<li><a href="._week44-bs001.html">&raquo;</a></li>
</ul>
<!-- ------------------- end of main content --------------- -->
+610 -7
View File
@@ -9,8 +9,8 @@ doconce format html week44-reveal.html week44-reveal reveal --html_slide_theme=b
<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 44: From Decision Trees to Bagging methods">
<title>Week 44: From Decision Trees to Bagging methods</title>
<meta name="description" content="Week 44: Dimensionality Reduction, PCA and Clustering. Decision Trees">
<title>Week 44: Dimensionality Reduction, PCA and Clustering. Decision Trees</title>
<!-- reveal.js: https://lab.hakim.se/reveal-js/ -->
@@ -168,7 +168,7 @@ MathJax.Hub.Config({
<section>
<!-- ------------------- main content ---------------------- -->
<center>
<h1 style="text-align: center;">Week 44: From Decision Trees to Bagging methods</h1>
<h1 style="text-align: center;">Week 44: Dimensionality Reduction, PCA and Clustering. Decision Trees</h1>
</center> <!-- document title -->
<!-- author(s): Morten Hjorth-Jensen -->
@@ -184,7 +184,7 @@ MathJax.Hub.Config({
</center>
<br>
<center>
<h4>Nov 1, 2021</h4>
<h4>Nov 2, 2021</h4>
</center> <!-- date -->
<br>
@@ -198,7 +198,7 @@ MathJax.Hub.Config({
<h2 id="overview-of-week-44">Overview of week 44 </h2>
<ul>
<p><li> Thursday: Wrapping up PCA from last week and basics of decision trees, classification and regression algorithms</li>
<p><li> Thursday: Wrapping up PCA from last week, Clustering and basics of decision trees, classification and regression algorithms</li>
<p><li> Friday: Decision trees, voting models and bagging</li>
</ul>
<p>
@@ -206,9 +206,612 @@ MathJax.Hub.Config({
</section>
<section>
<h2 id="thursday">Thursday </h2>
<h2 id="thursday-principal-component-analysis">Thursday, Principal Component Analysis </h2>
<p>Decision trees and wrapping up PCA.</p>
<p>For the principal component analysis,
see slides from <a href="https://compphysics.github.io/MachineLearning/doc/pub/week43/html/week43-reveal.html" target="_blank">week 43</a>, in particular from slide 28 and forward
</p>
</section>
<section>
<h2 id="thursday-clustering-and-unsupervised-learning">Thursday: Clustering and Unsupervised Learning </h2>
<p>In general terms cluster analysis, or clustering, is the task of grouping a
data-set into different distinct categories based on some measure of equality of
the data. This measure is often referred to as a <b>metric</b> or <b>similarity
measure</b> in the literature (note: sometimes we deal with a <b>dissimilarity
measure</b> instead). Usually, these metrics are formulated as some kind of
distance function between points in a high-dimensional space.
</p>
<p>The simplest, and also the most
common is the <b>Euclidean distance</b>.
</p>
</section>
<section>
<h2 id="basic-idea-of-the-k-means-clustering-algorithm">Basic Idea of the \( k \)-means Clustering Algorithm </h2>
<p>The simplest of all clustering algorithms is the <b>k-means algorithm</b>
, sometimes also referred to as <em>Lloyds algorithm</em>. It is the simplest and also
the most common. From its simplicity it obtains both strengths and weaknesses.
These will be discussed in more detail later. The \( k \)-means algorithm is a
<b>centroid based</b> clustering algorithm.
</p>
</section>
<section>
<h2 id="the-k-means-algorithm">The \( k \)-means Algorithm </h2>
<p>Assume, we are given \( n \) data points and we wish to split the data into \( K < n \)
different categories, or clusters. We label each cluster by an integer
</p>
$$ k\in\{1, \cdots, K \}$.
$$
<p>In the basic k-means algorithm each point is assigned to only
one cluster \( k \), and these assignments are <em>non-injective</em> i.e. many-to-one. We
can think of these mappings as an encoder \( k = C(i) \), which assigns the \( i \)-th
data-point \( \bf x_i \) to the \( k \)-th cluster.
</p>
<p>\( k \)-means algorithm in words:</p>
<ol>
<p><li> We start with guesses / random initializations of our \( k \) cluster centers / centroids</li>
<p><li> For each centroid the points that are most similar are identified</li>
<p><li> Then we move / replace each centroid with a coordinate average of all the points that were assigned to that centroid.</li>
<p><li> Iterate this points 2, 3) until the centroids no longer move (to some tolerance)</li>
</ol>
</section>
<section>
<h2 id="basic-math-of-the-k-means-algorithm">Basic Math of the \( k \)-means Algorithm </h2>
<p>We assume we have \( n \) data-points</p>
<p>&nbsp;<br>
$$
\begin{equation}\tag{1}
\boldsymbol{x_i} = \{x_{i, 1}, \cdots, x_{i, p}\}\in\mathbb{R}^p.
\end{equation}
$$
<p>&nbsp;<br>
<p>which we wish to group into \( K < n \) clusters. For our dissimilarity measure we
use the <em>squared Euclidean distance</em>
</p>
<p>&nbsp;<br>
$$
\begin{equation}\tag{2}
d(\boldsymbol{x_i}, \boldsymbol{x_i'}) = \sum_{j=1}^p(x_{ij} - x_{i'j})^2
= ||\boldsymbol{x_i} - \boldsymbol{x_{i'}}||^2
\end{equation}
$$
<p>&nbsp;<br>
</section>
<section>
<h2 id="within-cluster-point-scatter">Within Cluster Point Scatter </h2>
<p>We define the so called <em>within-cluster point scatter</em> which gives us a
measure of how close each data point assigned to the same cluster tends to be to
the all the others.
</p>
<p>&nbsp;<br>
$$
\begin{equation}\tag{3}
W(C) = \frac{1}{2}\sum_{k=1}^K\sum_{C(i)=k}
\sum_{C(i')=k}d(\boldsymbol{x_i}, \boldsymbol{x_{i'}}) =
\sum_{k=1}^KN_k\sum_{C(i)=k}||\boldsymbol{x_i} - \boldsymbol{\overline{x_k}}||^2
\end{equation}
$$
<p>&nbsp;<br>
<p>where \( \boldsymbol{\overline{x_k}} \) is the mean vector associated with the \( k \)-th
cluster, and \( N_k = \sum_{i=1}^nI(C(i) = k) \), where the \( I() \) notation is
similar to the Kronecker delta (<em>Commonly used in statistics, it just means that
when \( i = k \) we have the encoder \( C(i) \)</em>). In other words, the within-cluster
scatter measures the compactness of each cluster with respect to the data points
assigned to each cluster. This is the quantity that the \( k \)-means algorithm aims
to minimize. We refer to this quantity \( W(C) \) as the within cluster scatter
because of its relation to the <em>total scatter</em>.
</p>
</section>
<section>
<h2 id="more-details">More Details </h2>
<p>We have</p>
<p>&nbsp;<br>
$$
\begin{equation}\tag{4}
T = W(C) + B(C) = \frac{1}{2}\sum_{i=1}^n
\sum_{i'=1}^nd(\boldsymbol{x_i}, \boldsymbol{x_{i'}})
= \frac{1}{2}\sum_{k=1}^K\sum_{C(i)=k}
\Big(\sum_{C(i') = k}d(\boldsymbol{x_i}, \boldsymbol{x_{i'}})
+ \sum_{C(i')\neq k}d(\boldsymbol{x_i}, \boldsymbol{x_{i'}})\Big).
\end{equation}
$$
<p>&nbsp;<br>
<p>This is a quantity that is conserved throughout the \( k \)-means algorithm. It can
be thought of as the total amount of information in the data, and it is composed
of the aforementioned within-cluster scatter and the <em>between-cluster scatter</em>
\( B(C) \). In methods such as principle component analysis the total scatter is not
conserved.
</p>
</section>
<section>
<h2 id="total-cluster-variance">Total Cluster Variance </h2>
<p>Given a cluster mean \( \boldsymbol{m_k} \) we define the <b>total cluster variance</b></p>
<p>&nbsp;<br>
$$
\begin{equation}\tag{5}
\min_{C, \{\boldsymbol{m_k}\}_1^K}\sum_{k=1}^KN_k\sum||\boldsymbol{x_i} - \boldsymbol{m_k}||^2
\end{equation}
$$
<p>&nbsp;<br>
<p>Now we have all the pieces necessary to formally revisit the \( k \)-means algorithm.</p>
</section>
<section>
<h2 id="the-k-means-clustering-algorithm">The \( k \)-means Clustering Algorithm </h2>
<p>The \( k \)-means clustering algorithm goes as follows </p>
<ol>
<p><li> For a given cluster assignment \( C \), and \( k \) cluster means \( \left{m_1, \cdots, m_k\right} \). We minimize the total cluster variance with respect to the cluster means \( \{m_k\} \) yielding the means of the currently assigned clusters.</li>
<p><li> Given a current set of \( k \) means \( \{m_k\} \) the total cluster variance is minimized by assigning each observation to the closest (current) cluster mean. That is <p>&nbsp;<br>
$$C(i) = \underset{1\leq k\leq K}{\mathrm{argmin}} ||\boldsymbol{x_i} - \boldsymbol{m_k}||^2$$
<p>&nbsp;<br></li>
<p><li> Steps 1 and 2 are repeated until the assignments do not change.</li>
</ol>
</section>
<section>
<h2 id="summarizing">Summarizing </h2>
<ol>
<p><li> Before we start we specify a number \( k \) which is the number of clusters we want to try to separate our data into.</li>
<p><li> We initially choose \( k \) random data points in our data as our initial centroids, <em>or means</em> (this is where the name comes from).</li>
<p><li> Assign each data point to their closest centroid, based on the squared Euclidean distance.</li>
<p><li> For each of the \( k \) cluster we update the centroid by calculating new mean values for all the data points in the cluster.</li>
<p><li> Iteratively minimize the within cluster scatter by performing steps (3, 4) until the new assignments stop changing (can be to some tolerance) or until a maximum number of iterations have passed.</li>
</ol>
</section>
<section>
<h2 id="writing-our-own-code-the-data-set">Writing our own Code, the Data Set </h2>
<p>Let us now program the most basic version of the algorithm using nothing but
Python with numpy arrays. This code is kept intentionally simple to gradually
progress our understanding. There is no vectorization of any kind, and even most
helper functions are not utilized.
</p>
<p>We need first a dataset to do our cluster analysis on. In our case
this is a plain <em>vanilla</em> data set using random numbers using a
Gaussian distribution.
</p>
<!-- code=python (!bc pycod) typeset with pygments style "perldoc" -->
<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: #eeeedd">
<pre style="font-size: 80%; line-height: 125%;"><span style="color: #8B008B; font-weight: bold">import</span> <span style="color: #008b45; text-decoration: underline">time</span>
<span style="color: #8B008B; font-weight: bold">import</span> <span style="color: #008b45; text-decoration: underline">numpy</span> <span style="color: #8B008B; font-weight: bold">as</span> <span style="color: #008b45; text-decoration: underline">np</span>
<span style="color: #8B008B; font-weight: bold">import</span> <span style="color: #008b45; text-decoration: underline">tensorflow</span> <span style="color: #8B008B; font-weight: bold">as</span> <span style="color: #008b45; text-decoration: underline">tf</span>
<span style="color: #8B008B; font-weight: bold">from</span> <span style="color: #008b45; text-decoration: underline">matplotlib</span> <span style="color: #8B008B; font-weight: bold">import</span> image
<span style="color: #8B008B; font-weight: bold">import</span> <span style="color: #008b45; text-decoration: underline">matplotlib.pyplot</span> <span style="color: #8B008B; font-weight: bold">as</span> <span style="color: #008b45; text-decoration: underline">plt</span>
<span style="color: #8B008B; font-weight: bold">from</span> <span style="color: #008b45; text-decoration: underline">sklearn.cluster</span> <span style="color: #8B008B; font-weight: bold">import</span> KMeans
<span style="color: #8B008B; font-weight: bold">from</span> <span style="color: #008b45; text-decoration: underline">IPython.display</span> <span style="color: #8B008B; font-weight: bold">import</span> display
np.random.seed(<span style="color: #B452CD">2021</span>)
</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>
<p>Next we define functions, for ease of use later, to generate Gaussians and to
set up our toy data set.
</p>
<!-- code=python (!bc pycod) typeset with pygments style "perldoc" -->
<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: #eeeedd">
<pre style="font-size: 80%; line-height: 125%;"><span style="color: #8B008B; font-weight: bold">def</span> <span style="color: #008b45">gaussian_points</span>(dim=<span style="color: #B452CD">2</span>, n_points=<span style="color: #B452CD">1000</span>, mean_vector=np.array([<span style="color: #B452CD">0</span>, <span style="color: #B452CD">0</span>]),
sample_variance=<span style="color: #B452CD">1</span>):
<span style="color: #CD5555">&quot;&quot;&quot;</span>
<span style="color: #CD5555"> Very simple custom function to generate gaussian distributed point clusters</span>
<span style="color: #CD5555"> with variable dimension, number of points, means in each direction</span>
<span style="color: #CD5555"> (must match dim) and sample variance.</span>
<span style="color: #CD5555"> Inputs:</span>
<span style="color: #CD5555"> dim (int)</span>
<span style="color: #CD5555"> n_points (int)</span>
<span style="color: #CD5555"> mean_vector (np.array) (where index 0 is x, index 1 is y etc.)</span>
<span style="color: #CD5555"> sample_variance (float)</span>
<span style="color: #CD5555"> Returns:</span>
<span style="color: #CD5555"> data (np.array): with dimensions (dim x n_points)</span>
<span style="color: #CD5555"> &quot;&quot;&quot;</span>
mean_matrix = np.zeros(dim) + mean_vector
covariance_matrix = np.eye(dim) * sample_variance
data = np.random.multivariate_normal(mean_matrix, covariance_matrix,
n_points)
<span style="color: #8B008B; font-weight: bold">return</span> data
<span style="color: #8B008B; font-weight: bold">def</span> <span style="color: #008b45">generate_simple_clustering_dataset</span>(dim=<span style="color: #B452CD">2</span>, n_points=<span style="color: #B452CD">1000</span>, plotting=<span style="color: #8B008B; font-weight: bold">True</span>,
return_data=<span style="color: #8B008B; font-weight: bold">True</span>):
<span style="color: #CD5555">&quot;&quot;&quot;</span>
<span style="color: #CD5555"> Toy model to illustrate k-means clustering</span>
<span style="color: #CD5555"> &quot;&quot;&quot;</span>
data1 = gaussian_points(mean_vector=np.array([<span style="color: #B452CD">5</span>, <span style="color: #B452CD">5</span>]))
data2 = gaussian_points()
data3 = gaussian_points(mean_vector=np.array([<span style="color: #B452CD">1</span>, <span style="color: #B452CD">4.5</span>]))
data4 = gaussian_points(mean_vector=np.array([<span style="color: #B452CD">5</span>, <span style="color: #B452CD">1</span>]))
data = np.concatenate((data1, data2, data3, data4), axis=<span style="color: #B452CD">0</span>)
<span style="color: #8B008B; font-weight: bold">if</span> plotting:
fig, ax = plt.subplots()
ax.scatter(data[:, <span style="color: #B452CD">0</span>], data[:, <span style="color: #B452CD">1</span>], alpha=<span style="color: #B452CD">0.2</span>)
ax.set_title(<span style="color: #CD5555">&#39;Toy Model Dataset&#39;</span>)
plt.show()
<span style="color: #8B008B; font-weight: bold">if</span> return_data:
<span style="color: #8B008B; font-weight: bold">return</span> data
data = generate_simple_clustering_dataset()
</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>
</section>
<section>
<h2 id="implementing-the-k-means-algorithm">Implementing the \( k \)-means Algorithm </h2>
<p>With the above dataset we start
implementing the \( k \)-means algorithm.
</p>
<!-- code=python (!bc pycod) typeset with pygments style "perldoc" -->
<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: #eeeedd">
<pre style="font-size: 80%; line-height: 125%;">n_samples, dimensions = data.shape
n_clusters = <span style="color: #B452CD">4</span>
<span style="color: #228B22"># we randomly initialize our centroids</span>
np.random.seed(<span style="color: #B452CD">2021</span>)
centroids = data[np.random.choice(n_samples, n_clusters, replace=<span style="color: #8B008B; font-weight: bold">False</span>), :]
distances = np.zeros((n_samples, n_clusters))
<span style="color: #228B22"># first we need to calculate the distance to each centroid from our data</span>
<span style="color: #8B008B; font-weight: bold">for</span> k <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_clusters):
<span style="color: #8B008B; font-weight: bold">for</span> n <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_samples):
dist = <span style="color: #B452CD">0</span>
<span style="color: #8B008B; font-weight: bold">for</span> d <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(dimensions):
dist += np.abs(data[n, d] - centroids[k, d])**<span style="color: #B452CD">2</span>
distances[n, k] = dist
<span style="color: #228B22"># we initialize an array to keep track of to which cluster each point belongs</span>
<span style="color: #228B22"># the way we set it up here the index tracks which point and the value which</span>
<span style="color: #228B22"># cluster the point belongs to</span>
cluster_labels = np.zeros(n_samples, dtype=<span style="color: #CD5555">&#39;int&#39;</span>)
<span style="color: #228B22"># next we loop through our samples and for every point assign it to the cluster</span>
<span style="color: #228B22"># to which it has the smallest distance to</span>
<span style="color: #8B008B; font-weight: bold">for</span> n <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_samples):
<span style="color: #228B22"># tracking variables (all of this is basically just an argmin)</span>
smallest = <span style="color: #B452CD">1e10</span>
smallest_row_index = <span style="color: #B452CD">1e10</span>
<span style="color: #8B008B; font-weight: bold">for</span> k <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_clusters):
<span style="color: #8B008B; font-weight: bold">if</span> distances[n, k] &lt; smallest:
smallest = distances[n, k]
smallest_row_index = k
cluster_labels[n] = smallest_row_index
</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>
</section>
<section>
<h2 id="plotting">Plotting </h2>
<!-- code=python (!bc pycod) typeset with pygments style "perldoc" -->
<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: #eeeedd">
<pre style="font-size: 80%; line-height: 125%;">fig = plt.figure()
ax = fig.add_subplot()
unique_cluster_labels = np.unique(cluster_labels)
<span style="color: #8B008B; font-weight: bold">for</span> i <span style="color: #8B008B">in</span> unique_cluster_labels:
ax.scatter(data[cluster_labels == i, <span style="color: #B452CD">0</span>],
data[cluster_labels == i, <span style="color: #B452CD">1</span>],
label = i,
alpha = <span style="color: #B452CD">0.2</span>)
ax.scatter(centroids[:, <span style="color: #B452CD">0</span>], centroids[:, <span style="color: #B452CD">1</span>], c=<span style="color: #CD5555">&#39;black&#39;</span>)
ax.set_title(<span style="color: #CD5555">&quot;First Grouping of Points to Centroids&quot;</span>)
plt.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>
<p>So what do we have so far? We have 'picked' \( k \) centroids at random from our
data points. There are other ways of more intelligently choosing their
initializations, however for our purposes randomly is fine. Then we have
initialized an array 'distances' which holds the information of the distance,
<em>or dissimilarity</em>, of every point to of our centroids. Finally, we have
initialized an array 'cluster_labels' which according to our distances array
holds the information of to which centroid every point is assigned. This was the
first pass of our algorithm. Essentially, all we need to do now is repeat the
distance and assignment steps above until we have reached a desired convergence
or a maximum amount of iterations.
</p>
</section>
<section>
<h2 id="continuing">Continuing </h2>
<!-- code=python (!bc pycod) typeset with pygments style "perldoc" -->
<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: #eeeedd">
<pre style="font-size: 80%; line-height: 125%;">max_iterations = <span style="color: #B452CD">100</span>
tolerance = <span style="color: #B452CD">1e-8</span>
<span style="color: #8B008B; font-weight: bold">for</span> iteration <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(max_iterations):
prev_centroids = centroids.copy()
<span style="color: #8B008B; font-weight: bold">for</span> k <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_clusters):
<span style="color: #228B22"># this array will be used to update our centroid positions</span>
vector_mean = np.zeros(dimensions)
mean_divisor = <span style="color: #B452CD">0</span>
<span style="color: #8B008B; font-weight: bold">for</span> n <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_samples):
<span style="color: #8B008B; font-weight: bold">if</span> cluster_labels[n] == k:
vector_mean += data[n, :]
mean_divisor += <span style="color: #B452CD">1</span>
<span style="color: #228B22"># update according to the k means</span>
centroids[k, :] = vector_mean / mean_divisor
<span style="color: #228B22"># we find the dissimilarity</span>
<span style="color: #8B008B; font-weight: bold">for</span> k <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_clusters):
<span style="color: #8B008B; font-weight: bold">for</span> n <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_samples):
dist = <span style="color: #B452CD">0</span>
<span style="color: #8B008B; font-weight: bold">for</span> d <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(dimensions):
dist += np.abs(data[n, d] - centroids[k, d])**<span style="color: #B452CD">2</span>
distances[n, k] = dist
<span style="color: #228B22"># assign each point</span>
<span style="color: #8B008B; font-weight: bold">for</span> n <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_samples):
smallest = <span style="color: #B452CD">1e10</span>
smallest_row_index = <span style="color: #B452CD">1e10</span>
<span style="color: #8B008B; font-weight: bold">for</span> k <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_clusters):
<span style="color: #8B008B; font-weight: bold">if</span> distances[n, k] &lt; smallest:
smallest = distances[n, k]
smallest_row_index = k
cluster_labels[n] = smallest_row_index
<span style="color: #228B22"># convergence criteria</span>
centroid_difference = np.sum(np.abs(centroids - prev_centroids))
<span style="color: #8B008B; font-weight: bold">if</span> centroid_difference &lt; tolerance:
<span style="color: #658b00">print</span>(<span style="color: #CD5555">f&#39;Converged at iteration {</span>iteration<span style="color: #CD5555">}&#39;</span>)
<span style="color: #8B008B; font-weight: bold">break</span>
<span style="color: #8B008B; font-weight: bold">elif</span> iteration == max_iterations:
<span style="color: #658b00">print</span>(<span style="color: #CD5555">f&#39;Did not converge in {</span>max_iterations<span style="color: #CD5555">} iterations&#39;</span>)
</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>
</section>
<section>
<h2 id="wrapping-it-up">Wrapping it up </h2>
<p>We now have a simple , un-optimized \( k \)-means
clustering implementation. Lets plot the final result
</p>
<!-- code=python (!bc pycod) typeset with pygments style "perldoc" -->
<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: #eeeedd">
<pre style="font-size: 80%; line-height: 125%;">fig = plt.figure()
ax = fig.add_subplot()
unique_cluster_labels = np.unique(cluster_labels)
<span style="color: #8B008B; font-weight: bold">for</span> i <span style="color: #8B008B">in</span> unique_cluster_labels:
ax.scatter(data[cluster_labels == i, <span style="color: #B452CD">0</span>],
data[cluster_labels == i, <span style="color: #B452CD">1</span>],
label = i,
alpha = <span style="color: #B452CD">0.2</span>)
ax.scatter(centroids[:, <span style="color: #B452CD">0</span>], centroids[:, <span style="color: #B452CD">1</span>], c=<span style="color: #CD5555">&#39;black&#39;</span>)
ax.set_title(<span style="color: #CD5555">&quot;Final Result of K-means Clustering&quot;</span>)
plt.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>
<!-- code=python (!bc pycod) typeset with pygments style "perldoc" -->
<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: #eeeedd">
<pre style="font-size: 80%; line-height: 125%;"><span style="color: #8B008B; font-weight: bold">def</span> <span style="color: #008b45">naive_kmeans</span>(data, n_clusters=<span style="color: #B452CD">4</span>, max_iterations=<span style="color: #B452CD">100</span>, tolerance=<span style="color: #B452CD">1e-8</span>):
start_time = time.time()
n_samples, dimensions = data.shape
n_clusters = <span style="color: #B452CD">4</span>
<span style="color: #228B22">#np.random.seed(2021)</span>
centroids = data[np.random.choice(n_samples, n_clusters, replace=<span style="color: #8B008B; font-weight: bold">False</span>), :]
distances = np.zeros((n_samples, n_clusters))
<span style="color: #8B008B; font-weight: bold">for</span> k <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_clusters):
<span style="color: #8B008B; font-weight: bold">for</span> n <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_samples):
dist = <span style="color: #B452CD">0</span>
<span style="color: #8B008B; font-weight: bold">for</span> d <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(dimensions):
dist += np.abs(data[n, d] - centroids[k, d])**<span style="color: #B452CD">2</span>
distances[n, k] = dist
cluster_labels = np.zeros(n_samples, dtype=<span style="color: #CD5555">&#39;int&#39;</span>)
<span style="color: #8B008B; font-weight: bold">for</span> n <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_samples):
smallest = <span style="color: #B452CD">1e10</span>
smallest_row_index = <span style="color: #B452CD">1e10</span>
<span style="color: #8B008B; font-weight: bold">for</span> k <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_clusters):
<span style="color: #8B008B; font-weight: bold">if</span> distances[n, k] &lt; smallest:
smallest = distances[n, k]
smallest_row_index = k
cluster_labels[n] = smallest_row_index
<span style="color: #8B008B; font-weight: bold">for</span> iteration <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(max_iterations):
prev_centroids = centroids.copy()
<span style="color: #8B008B; font-weight: bold">for</span> k <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_clusters):
vector_mean = np.zeros(dimensions)
mean_divisor = <span style="color: #B452CD">0</span>
<span style="color: #8B008B; font-weight: bold">for</span> n <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_samples):
<span style="color: #8B008B; font-weight: bold">if</span> cluster_labels[n] == k:
vector_mean += data[n, :]
mean_divisor += <span style="color: #B452CD">1</span>
centroids[k, :] = vector_mean / mean_divisor
<span style="color: #8B008B; font-weight: bold">for</span> k <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_clusters):
<span style="color: #8B008B; font-weight: bold">for</span> n <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_samples):
dist = <span style="color: #B452CD">0</span>
<span style="color: #8B008B; font-weight: bold">for</span> d <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(dimensions):
dist += np.abs(data[n, d] - centroids[k, d])**<span style="color: #B452CD">2</span>
distances[n, k] = dist
<span style="color: #8B008B; font-weight: bold">for</span> n <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_samples):
smallest = <span style="color: #B452CD">1e10</span>
smallest_row_index = <span style="color: #B452CD">1e10</span>
<span style="color: #8B008B; font-weight: bold">for</span> k <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_clusters):
<span style="color: #8B008B; font-weight: bold">if</span> distances[n, k] &lt; smallest:
smallest = distances[n, k]
smallest_row_index = k
cluster_labels[n] = smallest_row_index
centroid_difference = np.sum(np.abs(centroids - prev_centroids))
<span style="color: #8B008B; font-weight: bold">if</span> centroid_difference &lt; tolerance:
<span style="color: #658b00">print</span>(<span style="color: #CD5555">f&#39;Converged at iteration {</span>iteration<span style="color: #CD5555">}&#39;</span>)
<span style="color: #658b00">print</span>(<span style="color: #CD5555">f&#39;Runtime: {</span>time.time() - start_time<span style="color: #CD5555">} seconds&#39;</span>)
<span style="color: #8B008B; font-weight: bold">return</span> cluster_labels, centroids
<span style="color: #658b00">print</span>(<span style="color: #CD5555">f&#39;Did not converge in {</span>max_iterations<span style="color: #CD5555">} iterations&#39;</span>)
<span style="color: #658b00">print</span>(<span style="color: #CD5555">f&#39;Runtime: {</span>time.time() - start_time<span style="color: #CD5555">} seconds&#39;</span>)
<span style="color: #8B008B; font-weight: bold">return</span> cluster_labels, centroids
</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>
</section>
<section>
+625 -8
View File
@@ -8,8 +8,8 @@ doconce format html week44.do.txt --pygments_html_style=perldoc --html_style=sol
<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 44: From Decision Trees to Bagging methods">
<title>Week 44: From Decision Trees to Bagging methods</title>
<meta name="description" content="Week 44: Dimensionality Reduction, PCA and Clustering. Decision Trees">
<title>Week 44: Dimensionality Reduction, PCA and Clustering. Decision Trees</title>
<link href="https://cdn.rawgit.com/doconce/doconce/master/bundled/html_styles/style_solarized_box/css/solarized_light_code.css" rel="stylesheet" type="text/css" title="light"/>
<script src="https://cdn.rawgit.com/doconce/doconce/master/bundled/html_styles/style_solarized_box/js/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
@@ -64,7 +64,45 @@ div.toc p,a {
<!-- tocinfo
{'highest level': 2,
'sections': [('Overview of week 44', 2, None, 'overview-of-week-44'),
('Thursday', 2, None, 'thursday'),
('Thursday, Principal Component Analysis',
2,
None,
'thursday-principal-component-analysis'),
('Thursday: Clustering and Unsupervised Learning',
2,
None,
'thursday-clustering-and-unsupervised-learning'),
('Basic Idea of the $k$-means Clustering Algorithm',
2,
None,
'basic-idea-of-the-k-means-clustering-algorithm'),
('The $k$-means Algorithm', 2, None, 'the-k-means-algorithm'),
('Basic Math of the $k$-means Algorithm',
2,
None,
'basic-math-of-the-k-means-algorithm'),
('Within Cluster Point Scatter',
2,
None,
'within-cluster-point-scatter'),
('More Details', 2, None, 'more-details'),
('Total Cluster Variance', 2, None, 'total-cluster-variance'),
('The $k$-means Clustering Algorithm',
2,
None,
'the-k-means-clustering-algorithm'),
('Summarizing', 2, None, 'summarizing'),
('Writing our own Code, the Data Set',
2,
None,
'writing-our-own-code-the-data-set'),
('Implementing the $k$-means Algorithm',
2,
None,
'implementing-the-k-means-algorithm'),
('Plotting', 2, None, 'plotting'),
('Continuing', 2, None, 'continuing'),
('Wrapping it up', 2, None, 'wrapping-it-up'),
('Decision trees, overarching aims',
2,
None,
@@ -220,7 +258,7 @@ MathJax.Hub.Config({
<!-- ------------------- main content ---------------------- -->
<center>
<h1>Week 44: From Decision Trees to Bagging methods</h1>
<h1>Week 44: Dimensionality Reduction, PCA and Clustering. Decision Trees</h1>
</center> <!-- document title -->
<!-- author(s): Morten Hjorth-Jensen -->
@@ -236,7 +274,7 @@ MathJax.Hub.Config({
</center>
<br>
<center>
<h4>Nov 1, 2021</h4>
<h4>Nov 2, 2021</h4>
</center> <!-- date -->
<br>
@@ -244,15 +282,594 @@ MathJax.Hub.Config({
<h2 id="overview-of-week-44">Overview of week 44 </h2>
<ul>
<li> Thursday: Wrapping up PCA from last week and basics of decision trees, classification and regression algorithms</li>
<li> Thursday: Wrapping up PCA from last week, Clustering and basics of decision trees, classification and regression algorithms</li>
<li> Friday: Decision trees, voting models and bagging</li>
</ul>
<p>Geron's chapter 6 covers decision trees while ensemble models, voting and bagging are discussed in chapter 7. See also lecture from <a href="https://www.uio.no/studier/emner/matnat/math/STK-IN4300/h20/slides/lecture_7.pdf" target="_blank">STK-IN4300, lecture 7</a>. Chapter 9.2 of Hastie et al contains also a good discussion.</p>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="thursday">Thursday </h2>
<h2 id="thursday-principal-component-analysis">Thursday, Principal Component Analysis </h2>
<p>For the principal component analysis,
see slides from <a href="https://compphysics.github.io/MachineLearning/doc/pub/week43/html/week43-reveal.html" target="_blank">week 43</a>, in particular from slide 28 and forward
</p>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="thursday-clustering-and-unsupervised-learning">Thursday: Clustering and Unsupervised Learning </h2>
<p>In general terms cluster analysis, or clustering, is the task of grouping a
data-set into different distinct categories based on some measure of equality of
the data. This measure is often referred to as a <b>metric</b> or <b>similarity
measure</b> in the literature (note: sometimes we deal with a <b>dissimilarity
measure</b> instead). Usually, these metrics are formulated as some kind of
distance function between points in a high-dimensional space.
</p>
<p>The simplest, and also the most
common is the <b>Euclidean distance</b>.
</p>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="basic-idea-of-the-k-means-clustering-algorithm">Basic Idea of the \( k \)-means Clustering Algorithm </h2>
<p>The simplest of all clustering algorithms is the <b>k-means algorithm</b>
, sometimes also referred to as <em>Lloyds algorithm</em>. It is the simplest and also
the most common. From its simplicity it obtains both strengths and weaknesses.
These will be discussed in more detail later. The \( k \)-means algorithm is a
<b>centroid based</b> clustering algorithm.
</p>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="the-k-means-algorithm">The \( k \)-means Algorithm </h2>
<p>Assume, we are given \( n \) data points and we wish to split the data into \( K < n \)
different categories, or clusters. We label each cluster by an integer
</p>
$$ k\in\{1, \cdots, K \}$.
$$
<p>In the basic k-means algorithm each point is assigned to only
one cluster \( k \), and these assignments are <em>non-injective</em> i.e. many-to-one. We
can think of these mappings as an encoder \( k = C(i) \), which assigns the \( i \)-th
data-point \( \bf x_i \) to the \( k \)-th cluster.
</p>
<p>\( k \)-means algorithm in words:</p>
<ol>
<li> We start with guesses / random initializations of our \( k \) cluster centers / centroids</li>
<li> For each centroid the points that are most similar are identified</li>
<li> Then we move / replace each centroid with a coordinate average of all the points that were assigned to that centroid.</li>
<li> Iterate this points 2, 3) until the centroids no longer move (to some tolerance)</li>
</ol>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="basic-math-of-the-k-means-algorithm">Basic Math of the \( k \)-means Algorithm </h2>
<p>We assume we have \( n \) data-points</p>
$$
\begin{equation}\label{eq:kmeanspoints}
\boldsymbol{x_i} = \{x_{i, 1}, \cdots, x_{i, p}\}\in\mathbb{R}^p.
\end{equation}
$$
<p>which we wish to group into \( K < n \) clusters. For our dissimilarity measure we
use the <em>squared Euclidean distance</em>
</p>
$$
\begin{equation}\label{eq:squaredeuclidean}
d(\boldsymbol{x_i}, \boldsymbol{x_i'}) = \sum_{j=1}^p(x_{ij} - x_{i'j})^2
= ||\boldsymbol{x_i} - \boldsymbol{x_{i'}}||^2
\end{equation}
$$
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="within-cluster-point-scatter">Within Cluster Point Scatter </h2>
<p>We define the so called <em>within-cluster point scatter</em> which gives us a
measure of how close each data point assigned to the same cluster tends to be to
the all the others.
</p>
$$
\begin{equation}\label{eq:withincluster}
W(C) = \frac{1}{2}\sum_{k=1}^K\sum_{C(i)=k}
\sum_{C(i')=k}d(\boldsymbol{x_i}, \boldsymbol{x_{i'}}) =
\sum_{k=1}^KN_k\sum_{C(i)=k}||\boldsymbol{x_i} - \boldsymbol{\overline{x_k}}||^2
\end{equation}
$$
<p>where \( \boldsymbol{\overline{x_k}} \) is the mean vector associated with the \( k \)-th
cluster, and \( N_k = \sum_{i=1}^nI(C(i) = k) \), where the \( I() \) notation is
similar to the Kronecker delta (<em>Commonly used in statistics, it just means that
when \( i = k \) we have the encoder \( C(i) \)</em>). In other words, the within-cluster
scatter measures the compactness of each cluster with respect to the data points
assigned to each cluster. This is the quantity that the \( k \)-means algorithm aims
to minimize. We refer to this quantity \( W(C) \) as the within cluster scatter
because of its relation to the <em>total scatter</em>.
</p>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="more-details">More Details </h2>
<p>We have</p>
$$
\begin{equation}\label{eq:totalscatter}
T = W(C) + B(C) = \frac{1}{2}\sum_{i=1}^n
\sum_{i'=1}^nd(\boldsymbol{x_i}, \boldsymbol{x_{i'}})
= \frac{1}{2}\sum_{k=1}^K\sum_{C(i)=k}
\Big(\sum_{C(i') = k}d(\boldsymbol{x_i}, \boldsymbol{x_{i'}})
+ \sum_{C(i')\neq k}d(\boldsymbol{x_i}, \boldsymbol{x_{i'}})\Big).
\end{equation}
$$
<p>This is a quantity that is conserved throughout the \( k \)-means algorithm. It can
be thought of as the total amount of information in the data, and it is composed
of the aforementioned within-cluster scatter and the <em>between-cluster scatter</em>
\( B(C) \). In methods such as principle component analysis the total scatter is not
conserved.
</p>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="total-cluster-variance">Total Cluster Variance </h2>
<p>Given a cluster mean \( \boldsymbol{m_k} \) we define the <b>total cluster variance</b></p>
$$
\begin{equation}\label{eq:totalclustervariance}
\min_{C, \{\boldsymbol{m_k}\}_1^K}\sum_{k=1}^KN_k\sum||\boldsymbol{x_i} - \boldsymbol{m_k}||^2
\end{equation}
$$
<p>Now we have all the pieces necessary to formally revisit the \( k \)-means algorithm.</p>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="the-k-means-clustering-algorithm">The \( k \)-means Clustering Algorithm </h2>
<p>The \( k \)-means clustering algorithm goes as follows </p>
<ol>
<li> For a given cluster assignment \( C \), and \( k \) cluster means \( \left{m_1, \cdots, m_k\right} \). We minimize the total cluster variance with respect to the cluster means \( \{m_k\} \) yielding the means of the currently assigned clusters.</li>
<li> Given a current set of \( k \) means \( \{m_k\} \) the total cluster variance is minimized by assigning each observation to the closest (current) cluster mean. That is $$C(i) = \underset{1\leq k\leq K}{\mathrm{argmin}} ||\boldsymbol{x_i} - \boldsymbol{m_k}||^2$$</li>
<li> Steps 1 and 2 are repeated until the assignments do not change.</li>
</ol>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="summarizing">Summarizing </h2>
<ol>
<li> Before we start we specify a number \( k \) which is the number of clusters we want to try to separate our data into.</li>
<li> We initially choose \( k \) random data points in our data as our initial centroids, <em>or means</em> (this is where the name comes from).</li>
<li> Assign each data point to their closest centroid, based on the squared Euclidean distance.</li>
<li> For each of the \( k \) cluster we update the centroid by calculating new mean values for all the data points in the cluster.</li>
<li> Iteratively minimize the within cluster scatter by performing steps (3, 4) until the new assignments stop changing (can be to some tolerance) or until a maximum number of iterations have passed.</li>
</ol>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="writing-our-own-code-the-data-set">Writing our own Code, the Data Set </h2>
<p>Let us now program the most basic version of the algorithm using nothing but
Python with numpy arrays. This code is kept intentionally simple to gradually
progress our understanding. There is no vectorization of any kind, and even most
helper functions are not utilized.
</p>
<p>We need first a dataset to do our cluster analysis on. In our case
this is a plain <em>vanilla</em> data set using random numbers using a
Gaussian distribution.
</p>
<!-- code=python (!bc pycod) typeset with pygments style "perldoc" -->
<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: #eeeedd">
<pre style="line-height: 125%;"><span style="color: #8B008B; font-weight: bold">import</span> <span style="color: #008b45; text-decoration: underline">time</span>
<span style="color: #8B008B; font-weight: bold">import</span> <span style="color: #008b45; text-decoration: underline">numpy</span> <span style="color: #8B008B; font-weight: bold">as</span> <span style="color: #008b45; text-decoration: underline">np</span>
<span style="color: #8B008B; font-weight: bold">import</span> <span style="color: #008b45; text-decoration: underline">tensorflow</span> <span style="color: #8B008B; font-weight: bold">as</span> <span style="color: #008b45; text-decoration: underline">tf</span>
<span style="color: #8B008B; font-weight: bold">from</span> <span style="color: #008b45; text-decoration: underline">matplotlib</span> <span style="color: #8B008B; font-weight: bold">import</span> image
<span style="color: #8B008B; font-weight: bold">import</span> <span style="color: #008b45; text-decoration: underline">matplotlib.pyplot</span> <span style="color: #8B008B; font-weight: bold">as</span> <span style="color: #008b45; text-decoration: underline">plt</span>
<span style="color: #8B008B; font-weight: bold">from</span> <span style="color: #008b45; text-decoration: underline">sklearn.cluster</span> <span style="color: #8B008B; font-weight: bold">import</span> KMeans
<span style="color: #8B008B; font-weight: bold">from</span> <span style="color: #008b45; text-decoration: underline">IPython.display</span> <span style="color: #8B008B; font-weight: bold">import</span> display
np.random.seed(<span style="color: #B452CD">2021</span>)
</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>
<p>Next we define functions, for ease of use later, to generate Gaussians and to
set up our toy data set.
</p>
<!-- code=python (!bc pycod) typeset with pygments style "perldoc" -->
<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: #eeeedd">
<pre style="line-height: 125%;"><span style="color: #8B008B; font-weight: bold">def</span> <span style="color: #008b45">gaussian_points</span>(dim=<span style="color: #B452CD">2</span>, n_points=<span style="color: #B452CD">1000</span>, mean_vector=np.array([<span style="color: #B452CD">0</span>, <span style="color: #B452CD">0</span>]),
sample_variance=<span style="color: #B452CD">1</span>):
<span style="color: #CD5555">&quot;&quot;&quot;</span>
<span style="color: #CD5555"> Very simple custom function to generate gaussian distributed point clusters</span>
<span style="color: #CD5555"> with variable dimension, number of points, means in each direction</span>
<span style="color: #CD5555"> (must match dim) and sample variance.</span>
<span style="color: #CD5555"> Inputs:</span>
<span style="color: #CD5555"> dim (int)</span>
<span style="color: #CD5555"> n_points (int)</span>
<span style="color: #CD5555"> mean_vector (np.array) (where index 0 is x, index 1 is y etc.)</span>
<span style="color: #CD5555"> sample_variance (float)</span>
<span style="color: #CD5555"> Returns:</span>
<span style="color: #CD5555"> data (np.array): with dimensions (dim x n_points)</span>
<span style="color: #CD5555"> &quot;&quot;&quot;</span>
mean_matrix = np.zeros(dim) + mean_vector
covariance_matrix = np.eye(dim) * sample_variance
data = np.random.multivariate_normal(mean_matrix, covariance_matrix,
n_points)
<span style="color: #8B008B; font-weight: bold">return</span> data
<span style="color: #8B008B; font-weight: bold">def</span> <span style="color: #008b45">generate_simple_clustering_dataset</span>(dim=<span style="color: #B452CD">2</span>, n_points=<span style="color: #B452CD">1000</span>, plotting=<span style="color: #8B008B; font-weight: bold">True</span>,
return_data=<span style="color: #8B008B; font-weight: bold">True</span>):
<span style="color: #CD5555">&quot;&quot;&quot;</span>
<span style="color: #CD5555"> Toy model to illustrate k-means clustering</span>
<span style="color: #CD5555"> &quot;&quot;&quot;</span>
data1 = gaussian_points(mean_vector=np.array([<span style="color: #B452CD">5</span>, <span style="color: #B452CD">5</span>]))
data2 = gaussian_points()
data3 = gaussian_points(mean_vector=np.array([<span style="color: #B452CD">1</span>, <span style="color: #B452CD">4.5</span>]))
data4 = gaussian_points(mean_vector=np.array([<span style="color: #B452CD">5</span>, <span style="color: #B452CD">1</span>]))
data = np.concatenate((data1, data2, data3, data4), axis=<span style="color: #B452CD">0</span>)
<span style="color: #8B008B; font-weight: bold">if</span> plotting:
fig, ax = plt.subplots()
ax.scatter(data[:, <span style="color: #B452CD">0</span>], data[:, <span style="color: #B452CD">1</span>], alpha=<span style="color: #B452CD">0.2</span>)
ax.set_title(<span style="color: #CD5555">&#39;Toy Model Dataset&#39;</span>)
plt.show()
<span style="color: #8B008B; font-weight: bold">if</span> return_data:
<span style="color: #8B008B; font-weight: bold">return</span> data
data = generate_simple_clustering_dataset()
</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="implementing-the-k-means-algorithm">Implementing the \( k \)-means Algorithm </h2>
<p>With the above dataset we start
implementing the \( k \)-means algorithm.
</p>
<!-- code=python (!bc pycod) typeset with pygments style "perldoc" -->
<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: #eeeedd">
<pre style="line-height: 125%;">n_samples, dimensions = data.shape
n_clusters = <span style="color: #B452CD">4</span>
<span style="color: #228B22"># we randomly initialize our centroids</span>
np.random.seed(<span style="color: #B452CD">2021</span>)
centroids = data[np.random.choice(n_samples, n_clusters, replace=<span style="color: #8B008B; font-weight: bold">False</span>), :]
distances = np.zeros((n_samples, n_clusters))
<span style="color: #228B22"># first we need to calculate the distance to each centroid from our data</span>
<span style="color: #8B008B; font-weight: bold">for</span> k <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_clusters):
<span style="color: #8B008B; font-weight: bold">for</span> n <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_samples):
dist = <span style="color: #B452CD">0</span>
<span style="color: #8B008B; font-weight: bold">for</span> d <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(dimensions):
dist += np.abs(data[n, d] - centroids[k, d])**<span style="color: #B452CD">2</span>
distances[n, k] = dist
<span style="color: #228B22"># we initialize an array to keep track of to which cluster each point belongs</span>
<span style="color: #228B22"># the way we set it up here the index tracks which point and the value which</span>
<span style="color: #228B22"># cluster the point belongs to</span>
cluster_labels = np.zeros(n_samples, dtype=<span style="color: #CD5555">&#39;int&#39;</span>)
<span style="color: #228B22"># next we loop through our samples and for every point assign it to the cluster</span>
<span style="color: #228B22"># to which it has the smallest distance to</span>
<span style="color: #8B008B; font-weight: bold">for</span> n <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_samples):
<span style="color: #228B22"># tracking variables (all of this is basically just an argmin)</span>
smallest = <span style="color: #B452CD">1e10</span>
smallest_row_index = <span style="color: #B452CD">1e10</span>
<span style="color: #8B008B; font-weight: bold">for</span> k <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_clusters):
<span style="color: #8B008B; font-weight: bold">if</span> distances[n, k] &lt; smallest:
smallest = distances[n, k]
smallest_row_index = k
cluster_labels[n] = smallest_row_index
</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="plotting">Plotting </h2>
<!-- code=python (!bc pycod) typeset with pygments style "perldoc" -->
<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: #eeeedd">
<pre style="line-height: 125%;">fig = plt.figure()
ax = fig.add_subplot()
unique_cluster_labels = np.unique(cluster_labels)
<span style="color: #8B008B; font-weight: bold">for</span> i <span style="color: #8B008B">in</span> unique_cluster_labels:
ax.scatter(data[cluster_labels == i, <span style="color: #B452CD">0</span>],
data[cluster_labels == i, <span style="color: #B452CD">1</span>],
label = i,
alpha = <span style="color: #B452CD">0.2</span>)
ax.scatter(centroids[:, <span style="color: #B452CD">0</span>], centroids[:, <span style="color: #B452CD">1</span>], c=<span style="color: #CD5555">&#39;black&#39;</span>)
ax.set_title(<span style="color: #CD5555">&quot;First Grouping of Points to Centroids&quot;</span>)
plt.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>
<p>So what do we have so far? We have 'picked' \( k \) centroids at random from our
data points. There are other ways of more intelligently choosing their
initializations, however for our purposes randomly is fine. Then we have
initialized an array 'distances' which holds the information of the distance,
<em>or dissimilarity</em>, of every point to of our centroids. Finally, we have
initialized an array 'cluster_labels' which according to our distances array
holds the information of to which centroid every point is assigned. This was the
first pass of our algorithm. Essentially, all we need to do now is repeat the
distance and assignment steps above until we have reached a desired convergence
or a maximum amount of iterations.
</p>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="continuing">Continuing </h2>
<!-- code=python (!bc pycod) typeset with pygments style "perldoc" -->
<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: #eeeedd">
<pre style="line-height: 125%;">max_iterations = <span style="color: #B452CD">100</span>
tolerance = <span style="color: #B452CD">1e-8</span>
<span style="color: #8B008B; font-weight: bold">for</span> iteration <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(max_iterations):
prev_centroids = centroids.copy()
<span style="color: #8B008B; font-weight: bold">for</span> k <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_clusters):
<span style="color: #228B22"># this array will be used to update our centroid positions</span>
vector_mean = np.zeros(dimensions)
mean_divisor = <span style="color: #B452CD">0</span>
<span style="color: #8B008B; font-weight: bold">for</span> n <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_samples):
<span style="color: #8B008B; font-weight: bold">if</span> cluster_labels[n] == k:
vector_mean += data[n, :]
mean_divisor += <span style="color: #B452CD">1</span>
<span style="color: #228B22"># update according to the k means</span>
centroids[k, :] = vector_mean / mean_divisor
<span style="color: #228B22"># we find the dissimilarity</span>
<span style="color: #8B008B; font-weight: bold">for</span> k <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_clusters):
<span style="color: #8B008B; font-weight: bold">for</span> n <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_samples):
dist = <span style="color: #B452CD">0</span>
<span style="color: #8B008B; font-weight: bold">for</span> d <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(dimensions):
dist += np.abs(data[n, d] - centroids[k, d])**<span style="color: #B452CD">2</span>
distances[n, k] = dist
<span style="color: #228B22"># assign each point</span>
<span style="color: #8B008B; font-weight: bold">for</span> n <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_samples):
smallest = <span style="color: #B452CD">1e10</span>
smallest_row_index = <span style="color: #B452CD">1e10</span>
<span style="color: #8B008B; font-weight: bold">for</span> k <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_clusters):
<span style="color: #8B008B; font-weight: bold">if</span> distances[n, k] &lt; smallest:
smallest = distances[n, k]
smallest_row_index = k
cluster_labels[n] = smallest_row_index
<span style="color: #228B22"># convergence criteria</span>
centroid_difference = np.sum(np.abs(centroids - prev_centroids))
<span style="color: #8B008B; font-weight: bold">if</span> centroid_difference &lt; tolerance:
<span style="color: #658b00">print</span>(<span style="color: #CD5555">f&#39;Converged at iteration {</span>iteration<span style="color: #CD5555">}&#39;</span>)
<span style="color: #8B008B; font-weight: bold">break</span>
<span style="color: #8B008B; font-weight: bold">elif</span> iteration == max_iterations:
<span style="color: #658b00">print</span>(<span style="color: #CD5555">f&#39;Did not converge in {</span>max_iterations<span style="color: #CD5555">} iterations&#39;</span>)
</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="wrapping-it-up">Wrapping it up </h2>
<p>We now have a simple , un-optimized \( k \)-means
clustering implementation. Lets plot the final result
</p>
<!-- code=python (!bc pycod) typeset with pygments style "perldoc" -->
<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: #eeeedd">
<pre style="line-height: 125%;">fig = plt.figure()
ax = fig.add_subplot()
unique_cluster_labels = np.unique(cluster_labels)
<span style="color: #8B008B; font-weight: bold">for</span> i <span style="color: #8B008B">in</span> unique_cluster_labels:
ax.scatter(data[cluster_labels == i, <span style="color: #B452CD">0</span>],
data[cluster_labels == i, <span style="color: #B452CD">1</span>],
label = i,
alpha = <span style="color: #B452CD">0.2</span>)
ax.scatter(centroids[:, <span style="color: #B452CD">0</span>], centroids[:, <span style="color: #B452CD">1</span>], c=<span style="color: #CD5555">&#39;black&#39;</span>)
ax.set_title(<span style="color: #CD5555">&quot;Final Result of K-means Clustering&quot;</span>)
plt.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>
<!-- code=python (!bc pycod) typeset with pygments style "perldoc" -->
<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: #eeeedd">
<pre style="line-height: 125%;"><span style="color: #8B008B; font-weight: bold">def</span> <span style="color: #008b45">naive_kmeans</span>(data, n_clusters=<span style="color: #B452CD">4</span>, max_iterations=<span style="color: #B452CD">100</span>, tolerance=<span style="color: #B452CD">1e-8</span>):
start_time = time.time()
n_samples, dimensions = data.shape
n_clusters = <span style="color: #B452CD">4</span>
<span style="color: #228B22">#np.random.seed(2021)</span>
centroids = data[np.random.choice(n_samples, n_clusters, replace=<span style="color: #8B008B; font-weight: bold">False</span>), :]
distances = np.zeros((n_samples, n_clusters))
<span style="color: #8B008B; font-weight: bold">for</span> k <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_clusters):
<span style="color: #8B008B; font-weight: bold">for</span> n <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_samples):
dist = <span style="color: #B452CD">0</span>
<span style="color: #8B008B; font-weight: bold">for</span> d <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(dimensions):
dist += np.abs(data[n, d] - centroids[k, d])**<span style="color: #B452CD">2</span>
distances[n, k] = dist
cluster_labels = np.zeros(n_samples, dtype=<span style="color: #CD5555">&#39;int&#39;</span>)
<span style="color: #8B008B; font-weight: bold">for</span> n <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_samples):
smallest = <span style="color: #B452CD">1e10</span>
smallest_row_index = <span style="color: #B452CD">1e10</span>
<span style="color: #8B008B; font-weight: bold">for</span> k <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_clusters):
<span style="color: #8B008B; font-weight: bold">if</span> distances[n, k] &lt; smallest:
smallest = distances[n, k]
smallest_row_index = k
cluster_labels[n] = smallest_row_index
<span style="color: #8B008B; font-weight: bold">for</span> iteration <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(max_iterations):
prev_centroids = centroids.copy()
<span style="color: #8B008B; font-weight: bold">for</span> k <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_clusters):
vector_mean = np.zeros(dimensions)
mean_divisor = <span style="color: #B452CD">0</span>
<span style="color: #8B008B; font-weight: bold">for</span> n <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_samples):
<span style="color: #8B008B; font-weight: bold">if</span> cluster_labels[n] == k:
vector_mean += data[n, :]
mean_divisor += <span style="color: #B452CD">1</span>
centroids[k, :] = vector_mean / mean_divisor
<span style="color: #8B008B; font-weight: bold">for</span> k <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_clusters):
<span style="color: #8B008B; font-weight: bold">for</span> n <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_samples):
dist = <span style="color: #B452CD">0</span>
<span style="color: #8B008B; font-weight: bold">for</span> d <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(dimensions):
dist += np.abs(data[n, d] - centroids[k, d])**<span style="color: #B452CD">2</span>
distances[n, k] = dist
<span style="color: #8B008B; font-weight: bold">for</span> n <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_samples):
smallest = <span style="color: #B452CD">1e10</span>
smallest_row_index = <span style="color: #B452CD">1e10</span>
<span style="color: #8B008B; font-weight: bold">for</span> k <span style="color: #8B008B">in</span> <span style="color: #658b00">range</span>(n_clusters):
<span style="color: #8B008B; font-weight: bold">if</span> distances[n, k] &lt; smallest:
smallest = distances[n, k]
smallest_row_index = k
cluster_labels[n] = smallest_row_index
centroid_difference = np.sum(np.abs(centroids - prev_centroids))
<span style="color: #8B008B; font-weight: bold">if</span> centroid_difference &lt; tolerance:
<span style="color: #658b00">print</span>(<span style="color: #CD5555">f&#39;Converged at iteration {</span>iteration<span style="color: #CD5555">}&#39;</span>)
<span style="color: #658b00">print</span>(<span style="color: #CD5555">f&#39;Runtime: {</span>time.time() - start_time<span style="color: #CD5555">} seconds&#39;</span>)
<span style="color: #8B008B; font-weight: bold">return</span> cluster_labels, centroids
<span style="color: #658b00">print</span>(<span style="color: #CD5555">f&#39;Did not converge in {</span>max_iterations<span style="color: #CD5555">} iterations&#39;</span>)
<span style="color: #658b00">print</span>(<span style="color: #CD5555">f&#39;Runtime: {</span>time.time() - start_time<span style="color: #CD5555">} seconds&#39;</span>)
<span style="color: #8B008B; font-weight: bold">return</span> cluster_labels, centroids
</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>
<p>Decision trees and wrapping up PCA.</p>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="decision-trees-overarching-aims">Decision trees, overarching aims </h2>
+625 -8
View File
@@ -8,8 +8,8 @@ doconce format html week44.do.txt --pygments_html_style=default --html_style=blo
<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 44: From Decision Trees to Bagging methods">
<title>Week 44: From Decision Trees to Bagging methods</title>
<meta name="description" content="Week 44: Dimensionality Reduction, PCA and Clustering. Decision Trees">
<title>Week 44: Dimensionality Reduction, PCA and Clustering. Decision Trees</title>
<style type="text/css">
/* bloodish style */
body {
@@ -141,7 +141,45 @@ div.toc p,a {
<!-- tocinfo
{'highest level': 2,
'sections': [('Overview of week 44', 2, None, 'overview-of-week-44'),
('Thursday', 2, None, 'thursday'),
('Thursday, Principal Component Analysis',
2,
None,
'thursday-principal-component-analysis'),
('Thursday: Clustering and Unsupervised Learning',
2,
None,
'thursday-clustering-and-unsupervised-learning'),
('Basic Idea of the $k$-means Clustering Algorithm',
2,
None,
'basic-idea-of-the-k-means-clustering-algorithm'),
('The $k$-means Algorithm', 2, None, 'the-k-means-algorithm'),
('Basic Math of the $k$-means Algorithm',
2,
None,
'basic-math-of-the-k-means-algorithm'),
('Within Cluster Point Scatter',
2,
None,
'within-cluster-point-scatter'),
('More Details', 2, None, 'more-details'),
('Total Cluster Variance', 2, None, 'total-cluster-variance'),
('The $k$-means Clustering Algorithm',
2,
None,
'the-k-means-clustering-algorithm'),
('Summarizing', 2, None, 'summarizing'),
('Writing our own Code, the Data Set',
2,
None,
'writing-our-own-code-the-data-set'),
('Implementing the $k$-means Algorithm',
2,
None,
'implementing-the-k-means-algorithm'),
('Plotting', 2, None, 'plotting'),
('Continuing', 2, None, 'continuing'),
('Wrapping it up', 2, None, 'wrapping-it-up'),
('Decision trees, overarching aims',
2,
None,
@@ -297,7 +335,7 @@ MathJax.Hub.Config({
<!-- ------------------- main content ---------------------- -->
<center>
<h1>Week 44: From Decision Trees to Bagging methods</h1>
<h1>Week 44: Dimensionality Reduction, PCA and Clustering. Decision Trees</h1>
</center> <!-- document title -->
<!-- author(s): Morten Hjorth-Jensen -->
@@ -313,7 +351,7 @@ MathJax.Hub.Config({
</center>
<br>
<center>
<h4>Nov 1, 2021</h4>
<h4>Nov 2, 2021</h4>
</center> <!-- date -->
<br>
@@ -321,15 +359,594 @@ MathJax.Hub.Config({
<h2 id="overview-of-week-44">Overview of week 44 </h2>
<ul>
<li> Thursday: Wrapping up PCA from last week and basics of decision trees, classification and regression algorithms</li>
<li> Thursday: Wrapping up PCA from last week, Clustering and basics of decision trees, classification and regression algorithms</li>
<li> Friday: Decision trees, voting models and bagging</li>
</ul>
<p>Geron's chapter 6 covers decision trees while ensemble models, voting and bagging are discussed in chapter 7. See also lecture from <a href="https://www.uio.no/studier/emner/matnat/math/STK-IN4300/h20/slides/lecture_7.pdf" target="_blank">STK-IN4300, lecture 7</a>. Chapter 9.2 of Hastie et al contains also a good discussion.</p>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="thursday">Thursday </h2>
<h2 id="thursday-principal-component-analysis">Thursday, Principal Component Analysis </h2>
<p>For the principal component analysis,
see slides from <a href="https://compphysics.github.io/MachineLearning/doc/pub/week43/html/week43-reveal.html" target="_blank">week 43</a>, in particular from slide 28 and forward
</p>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="thursday-clustering-and-unsupervised-learning">Thursday: Clustering and Unsupervised Learning </h2>
<p>In general terms cluster analysis, or clustering, is the task of grouping a
data-set into different distinct categories based on some measure of equality of
the data. This measure is often referred to as a <b>metric</b> or <b>similarity
measure</b> in the literature (note: sometimes we deal with a <b>dissimilarity
measure</b> instead). Usually, these metrics are formulated as some kind of
distance function between points in a high-dimensional space.
</p>
<p>The simplest, and also the most
common is the <b>Euclidean distance</b>.
</p>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="basic-idea-of-the-k-means-clustering-algorithm">Basic Idea of the \( k \)-means Clustering Algorithm </h2>
<p>The simplest of all clustering algorithms is the <b>k-means algorithm</b>
, sometimes also referred to as <em>Lloyds algorithm</em>. It is the simplest and also
the most common. From its simplicity it obtains both strengths and weaknesses.
These will be discussed in more detail later. The \( k \)-means algorithm is a
<b>centroid based</b> clustering algorithm.
</p>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="the-k-means-algorithm">The \( k \)-means Algorithm </h2>
<p>Assume, we are given \( n \) data points and we wish to split the data into \( K < n \)
different categories, or clusters. We label each cluster by an integer
</p>
$$ k\in\{1, \cdots, K \}$.
$$
<p>In the basic k-means algorithm each point is assigned to only
one cluster \( k \), and these assignments are <em>non-injective</em> i.e. many-to-one. We
can think of these mappings as an encoder \( k = C(i) \), which assigns the \( i \)-th
data-point \( \bf x_i \) to the \( k \)-th cluster.
</p>
<p>\( k \)-means algorithm in words:</p>
<ol>
<li> We start with guesses / random initializations of our \( k \) cluster centers / centroids</li>
<li> For each centroid the points that are most similar are identified</li>
<li> Then we move / replace each centroid with a coordinate average of all the points that were assigned to that centroid.</li>
<li> Iterate this points 2, 3) until the centroids no longer move (to some tolerance)</li>
</ol>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="basic-math-of-the-k-means-algorithm">Basic Math of the \( k \)-means Algorithm </h2>
<p>We assume we have \( n \) data-points</p>
$$
\begin{equation}\label{eq:kmeanspoints}
\boldsymbol{x_i} = \{x_{i, 1}, \cdots, x_{i, p}\}\in\mathbb{R}^p.
\end{equation}
$$
<p>which we wish to group into \( K < n \) clusters. For our dissimilarity measure we
use the <em>squared Euclidean distance</em>
</p>
$$
\begin{equation}\label{eq:squaredeuclidean}
d(\boldsymbol{x_i}, \boldsymbol{x_i'}) = \sum_{j=1}^p(x_{ij} - x_{i'j})^2
= ||\boldsymbol{x_i} - \boldsymbol{x_{i'}}||^2
\end{equation}
$$
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="within-cluster-point-scatter">Within Cluster Point Scatter </h2>
<p>We define the so called <em>within-cluster point scatter</em> which gives us a
measure of how close each data point assigned to the same cluster tends to be to
the all the others.
</p>
$$
\begin{equation}\label{eq:withincluster}
W(C) = \frac{1}{2}\sum_{k=1}^K\sum_{C(i)=k}
\sum_{C(i')=k}d(\boldsymbol{x_i}, \boldsymbol{x_{i'}}) =
\sum_{k=1}^KN_k\sum_{C(i)=k}||\boldsymbol{x_i} - \boldsymbol{\overline{x_k}}||^2
\end{equation}
$$
<p>where \( \boldsymbol{\overline{x_k}} \) is the mean vector associated with the \( k \)-th
cluster, and \( N_k = \sum_{i=1}^nI(C(i) = k) \), where the \( I() \) notation is
similar to the Kronecker delta (<em>Commonly used in statistics, it just means that
when \( i = k \) we have the encoder \( C(i) \)</em>). In other words, the within-cluster
scatter measures the compactness of each cluster with respect to the data points
assigned to each cluster. This is the quantity that the \( k \)-means algorithm aims
to minimize. We refer to this quantity \( W(C) \) as the within cluster scatter
because of its relation to the <em>total scatter</em>.
</p>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="more-details">More Details </h2>
<p>We have</p>
$$
\begin{equation}\label{eq:totalscatter}
T = W(C) + B(C) = \frac{1}{2}\sum_{i=1}^n
\sum_{i'=1}^nd(\boldsymbol{x_i}, \boldsymbol{x_{i'}})
= \frac{1}{2}\sum_{k=1}^K\sum_{C(i)=k}
\Big(\sum_{C(i') = k}d(\boldsymbol{x_i}, \boldsymbol{x_{i'}})
+ \sum_{C(i')\neq k}d(\boldsymbol{x_i}, \boldsymbol{x_{i'}})\Big).
\end{equation}
$$
<p>This is a quantity that is conserved throughout the \( k \)-means algorithm. It can
be thought of as the total amount of information in the data, and it is composed
of the aforementioned within-cluster scatter and the <em>between-cluster scatter</em>
\( B(C) \). In methods such as principle component analysis the total scatter is not
conserved.
</p>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="total-cluster-variance">Total Cluster Variance </h2>
<p>Given a cluster mean \( \boldsymbol{m_k} \) we define the <b>total cluster variance</b></p>
$$
\begin{equation}\label{eq:totalclustervariance}
\min_{C, \{\boldsymbol{m_k}\}_1^K}\sum_{k=1}^KN_k\sum||\boldsymbol{x_i} - \boldsymbol{m_k}||^2
\end{equation}
$$
<p>Now we have all the pieces necessary to formally revisit the \( k \)-means algorithm.</p>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="the-k-means-clustering-algorithm">The \( k \)-means Clustering Algorithm </h2>
<p>The \( k \)-means clustering algorithm goes as follows </p>
<ol>
<li> For a given cluster assignment \( C \), and \( k \) cluster means \( \left{m_1, \cdots, m_k\right} \). We minimize the total cluster variance with respect to the cluster means \( \{m_k\} \) yielding the means of the currently assigned clusters.</li>
<li> Given a current set of \( k \) means \( \{m_k\} \) the total cluster variance is minimized by assigning each observation to the closest (current) cluster mean. That is $$C(i) = \underset{1\leq k\leq K}{\mathrm{argmin}} ||\boldsymbol{x_i} - \boldsymbol{m_k}||^2$$</li>
<li> Steps 1 and 2 are repeated until the assignments do not change.</li>
</ol>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="summarizing">Summarizing </h2>
<ol>
<li> Before we start we specify a number \( k \) which is the number of clusters we want to try to separate our data into.</li>
<li> We initially choose \( k \) random data points in our data as our initial centroids, <em>or means</em> (this is where the name comes from).</li>
<li> Assign each data point to their closest centroid, based on the squared Euclidean distance.</li>
<li> For each of the \( k \) cluster we update the centroid by calculating new mean values for all the data points in the cluster.</li>
<li> Iteratively minimize the within cluster scatter by performing steps (3, 4) until the new assignments stop changing (can be to some tolerance) or until a maximum number of iterations have passed.</li>
</ol>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="writing-our-own-code-the-data-set">Writing our own Code, the Data Set </h2>
<p>Let us now program the most basic version of the algorithm using nothing but
Python with numpy arrays. This code is kept intentionally simple to gradually
progress our understanding. There is no vectorization of any kind, and even most
helper functions are not utilized.
</p>
<p>We need first a dataset to do our cluster analysis on. In our case
this is a plain <em>vanilla</em> data set using random numbers using a
Gaussian distribution.
</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">time</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">tensorflow</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #0000FF; font-weight: bold">tf</span>
<span style="color: #008000; font-weight: bold">from</span> <span style="color: #0000FF; font-weight: bold">matplotlib</span> <span style="color: #008000; font-weight: bold">import</span> image
<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.cluster</span> <span style="color: #008000; font-weight: bold">import</span> KMeans
<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> display
np<span style="color: #666666">.</span>random<span style="color: #666666">.</span>seed(<span style="color: #666666">2021</span>)
</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>
<p>Next we define functions, for ease of use later, to generate Gaussians and to
set up our toy data set.
</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">def</span> <span style="color: #0000FF">gaussian_points</span>(dim<span style="color: #666666">=2</span>, n_points<span style="color: #666666">=1000</span>, mean_vector<span style="color: #666666">=</span>np<span style="color: #666666">.</span>array([<span style="color: #666666">0</span>, <span style="color: #666666">0</span>]),
sample_variance<span style="color: #666666">=1</span>):
<span style="color: #BA2121; font-style: italic">&quot;&quot;&quot;</span>
<span style="color: #BA2121; font-style: italic"> Very simple custom function to generate gaussian distributed point clusters</span>
<span style="color: #BA2121; font-style: italic"> with variable dimension, number of points, means in each direction</span>
<span style="color: #BA2121; font-style: italic"> (must match dim) and sample variance.</span>
<span style="color: #BA2121; font-style: italic"> Inputs:</span>
<span style="color: #BA2121; font-style: italic"> dim (int)</span>
<span style="color: #BA2121; font-style: italic"> n_points (int)</span>
<span style="color: #BA2121; font-style: italic"> mean_vector (np.array) (where index 0 is x, index 1 is y etc.)</span>
<span style="color: #BA2121; font-style: italic"> sample_variance (float)</span>
<span style="color: #BA2121; font-style: italic"> Returns:</span>
<span style="color: #BA2121; font-style: italic"> data (np.array): with dimensions (dim x n_points)</span>
<span style="color: #BA2121; font-style: italic"> &quot;&quot;&quot;</span>
mean_matrix <span style="color: #666666">=</span> np<span style="color: #666666">.</span>zeros(dim) <span style="color: #666666">+</span> mean_vector
covariance_matrix <span style="color: #666666">=</span> np<span style="color: #666666">.</span>eye(dim) <span style="color: #666666">*</span> sample_variance
data <span style="color: #666666">=</span> np<span style="color: #666666">.</span>random<span style="color: #666666">.</span>multivariate_normal(mean_matrix, covariance_matrix,
n_points)
<span style="color: #008000; font-weight: bold">return</span> data
<span style="color: #008000; font-weight: bold">def</span> <span style="color: #0000FF">generate_simple_clustering_dataset</span>(dim<span style="color: #666666">=2</span>, n_points<span style="color: #666666">=1000</span>, plotting<span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">True</span>,
return_data<span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">True</span>):
<span style="color: #BA2121; font-style: italic">&quot;&quot;&quot;</span>
<span style="color: #BA2121; font-style: italic"> Toy model to illustrate k-means clustering</span>
<span style="color: #BA2121; font-style: italic"> &quot;&quot;&quot;</span>
data1 <span style="color: #666666">=</span> gaussian_points(mean_vector<span style="color: #666666">=</span>np<span style="color: #666666">.</span>array([<span style="color: #666666">5</span>, <span style="color: #666666">5</span>]))
data2 <span style="color: #666666">=</span> gaussian_points()
data3 <span style="color: #666666">=</span> gaussian_points(mean_vector<span style="color: #666666">=</span>np<span style="color: #666666">.</span>array([<span style="color: #666666">1</span>, <span style="color: #666666">4.5</span>]))
data4 <span style="color: #666666">=</span> gaussian_points(mean_vector<span style="color: #666666">=</span>np<span style="color: #666666">.</span>array([<span style="color: #666666">5</span>, <span style="color: #666666">1</span>]))
data <span style="color: #666666">=</span> np<span style="color: #666666">.</span>concatenate((data1, data2, data3, data4), axis<span style="color: #666666">=0</span>)
<span style="color: #008000; font-weight: bold">if</span> plotting:
fig, ax <span style="color: #666666">=</span> plt<span style="color: #666666">.</span>subplots()
ax<span style="color: #666666">.</span>scatter(data[:, <span style="color: #666666">0</span>], data[:, <span style="color: #666666">1</span>], alpha<span style="color: #666666">=0.2</span>)
ax<span style="color: #666666">.</span>set_title(<span style="color: #BA2121">&#39;Toy Model Dataset&#39;</span>)
plt<span style="color: #666666">.</span>show()
<span style="color: #008000; font-weight: bold">if</span> return_data:
<span style="color: #008000; font-weight: bold">return</span> data
data <span style="color: #666666">=</span> generate_simple_clustering_dataset()
</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="implementing-the-k-means-algorithm">Implementing the \( k \)-means Algorithm </h2>
<p>With the above dataset we start
implementing the \( k \)-means algorithm.
</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%;">n_samples, dimensions <span style="color: #666666">=</span> data<span style="color: #666666">.</span>shape
n_clusters <span style="color: #666666">=</span> <span style="color: #666666">4</span>
<span style="color: #408080; font-style: italic"># we randomly initialize our centroids</span>
np<span style="color: #666666">.</span>random<span style="color: #666666">.</span>seed(<span style="color: #666666">2021</span>)
centroids <span style="color: #666666">=</span> data[np<span style="color: #666666">.</span>random<span style="color: #666666">.</span>choice(n_samples, n_clusters, replace<span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">False</span>), :]
distances <span style="color: #666666">=</span> np<span style="color: #666666">.</span>zeros((n_samples, n_clusters))
<span style="color: #408080; font-style: italic"># first we need to calculate the distance to each centroid from our data</span>
<span style="color: #008000; font-weight: bold">for</span> k <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(n_clusters):
<span style="color: #008000; font-weight: bold">for</span> n <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(n_samples):
dist <span style="color: #666666">=</span> <span style="color: #666666">0</span>
<span style="color: #008000; font-weight: bold">for</span> d <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(dimensions):
dist <span style="color: #666666">+=</span> np<span style="color: #666666">.</span>abs(data[n, d] <span style="color: #666666">-</span> centroids[k, d])<span style="color: #666666">**2</span>
distances[n, k] <span style="color: #666666">=</span> dist
<span style="color: #408080; font-style: italic"># we initialize an array to keep track of to which cluster each point belongs</span>
<span style="color: #408080; font-style: italic"># the way we set it up here the index tracks which point and the value which</span>
<span style="color: #408080; font-style: italic"># cluster the point belongs to</span>
cluster_labels <span style="color: #666666">=</span> np<span style="color: #666666">.</span>zeros(n_samples, dtype<span style="color: #666666">=</span><span style="color: #BA2121">&#39;int&#39;</span>)
<span style="color: #408080; font-style: italic"># next we loop through our samples and for every point assign it to the cluster</span>
<span style="color: #408080; font-style: italic"># to which it has the smallest distance to</span>
<span style="color: #008000; font-weight: bold">for</span> n <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(n_samples):
<span style="color: #408080; font-style: italic"># tracking variables (all of this is basically just an argmin)</span>
smallest <span style="color: #666666">=</span> <span style="color: #666666">1e10</span>
smallest_row_index <span style="color: #666666">=</span> <span style="color: #666666">1e10</span>
<span style="color: #008000; font-weight: bold">for</span> k <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(n_clusters):
<span style="color: #008000; font-weight: bold">if</span> distances[n, k] <span style="color: #666666">&lt;</span> smallest:
smallest <span style="color: #666666">=</span> distances[n, k]
smallest_row_index <span style="color: #666666">=</span> k
cluster_labels[n] <span style="color: #666666">=</span> smallest_row_index
</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="plotting">Plotting </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%;">fig <span style="color: #666666">=</span> plt<span style="color: #666666">.</span>figure()
ax <span style="color: #666666">=</span> fig<span style="color: #666666">.</span>add_subplot()
unique_cluster_labels <span style="color: #666666">=</span> np<span style="color: #666666">.</span>unique(cluster_labels)
<span style="color: #008000; font-weight: bold">for</span> i <span style="color: #AA22FF; font-weight: bold">in</span> unique_cluster_labels:
ax<span style="color: #666666">.</span>scatter(data[cluster_labels <span style="color: #666666">==</span> i, <span style="color: #666666">0</span>],
data[cluster_labels <span style="color: #666666">==</span> i, <span style="color: #666666">1</span>],
label <span style="color: #666666">=</span> i,
alpha <span style="color: #666666">=</span> <span style="color: #666666">0.2</span>)
ax<span style="color: #666666">.</span>scatter(centroids[:, <span style="color: #666666">0</span>], centroids[:, <span style="color: #666666">1</span>], c<span style="color: #666666">=</span><span style="color: #BA2121">&#39;black&#39;</span>)
ax<span style="color: #666666">.</span>set_title(<span style="color: #BA2121">&quot;First Grouping of Points to Centroids&quot;</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>
<p>So what do we have so far? We have 'picked' \( k \) centroids at random from our
data points. There are other ways of more intelligently choosing their
initializations, however for our purposes randomly is fine. Then we have
initialized an array 'distances' which holds the information of the distance,
<em>or dissimilarity</em>, of every point to of our centroids. Finally, we have
initialized an array 'cluster_labels' which according to our distances array
holds the information of to which centroid every point is assigned. This was the
first pass of our algorithm. Essentially, all we need to do now is repeat the
distance and assignment steps above until we have reached a desired convergence
or a maximum amount of iterations.
</p>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="continuing">Continuing </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%;">max_iterations <span style="color: #666666">=</span> <span style="color: #666666">100</span>
tolerance <span style="color: #666666">=</span> <span style="color: #666666">1e-8</span>
<span style="color: #008000; font-weight: bold">for</span> iteration <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(max_iterations):
prev_centroids <span style="color: #666666">=</span> centroids<span style="color: #666666">.</span>copy()
<span style="color: #008000; font-weight: bold">for</span> k <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(n_clusters):
<span style="color: #408080; font-style: italic"># this array will be used to update our centroid positions</span>
vector_mean <span style="color: #666666">=</span> np<span style="color: #666666">.</span>zeros(dimensions)
mean_divisor <span style="color: #666666">=</span> <span style="color: #666666">0</span>
<span style="color: #008000; font-weight: bold">for</span> n <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(n_samples):
<span style="color: #008000; font-weight: bold">if</span> cluster_labels[n] <span style="color: #666666">==</span> k:
vector_mean <span style="color: #666666">+=</span> data[n, :]
mean_divisor <span style="color: #666666">+=</span> <span style="color: #666666">1</span>
<span style="color: #408080; font-style: italic"># update according to the k means</span>
centroids[k, :] <span style="color: #666666">=</span> vector_mean <span style="color: #666666">/</span> mean_divisor
<span style="color: #408080; font-style: italic"># we find the dissimilarity</span>
<span style="color: #008000; font-weight: bold">for</span> k <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(n_clusters):
<span style="color: #008000; font-weight: bold">for</span> n <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(n_samples):
dist <span style="color: #666666">=</span> <span style="color: #666666">0</span>
<span style="color: #008000; font-weight: bold">for</span> d <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(dimensions):
dist <span style="color: #666666">+=</span> np<span style="color: #666666">.</span>abs(data[n, d] <span style="color: #666666">-</span> centroids[k, d])<span style="color: #666666">**2</span>
distances[n, k] <span style="color: #666666">=</span> dist
<span style="color: #408080; font-style: italic"># assign each point</span>
<span style="color: #008000; font-weight: bold">for</span> n <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(n_samples):
smallest <span style="color: #666666">=</span> <span style="color: #666666">1e10</span>
smallest_row_index <span style="color: #666666">=</span> <span style="color: #666666">1e10</span>
<span style="color: #008000; font-weight: bold">for</span> k <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(n_clusters):
<span style="color: #008000; font-weight: bold">if</span> distances[n, k] <span style="color: #666666">&lt;</span> smallest:
smallest <span style="color: #666666">=</span> distances[n, k]
smallest_row_index <span style="color: #666666">=</span> k
cluster_labels[n] <span style="color: #666666">=</span> smallest_row_index
<span style="color: #408080; font-style: italic"># convergence criteria</span>
centroid_difference <span style="color: #666666">=</span> np<span style="color: #666666">.</span>sum(np<span style="color: #666666">.</span>abs(centroids <span style="color: #666666">-</span> prev_centroids))
<span style="color: #008000; font-weight: bold">if</span> centroid_difference <span style="color: #666666">&lt;</span> tolerance:
<span style="color: #008000">print</span>(<span style="color: #BA2121">f&#39;Converged at iteration </span><span style="color: #BB6688; font-weight: bold">{</span>iteration<span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&#39;</span>)
<span style="color: #008000; font-weight: bold">break</span>
<span style="color: #008000; font-weight: bold">elif</span> iteration <span style="color: #666666">==</span> max_iterations:
<span style="color: #008000">print</span>(<span style="color: #BA2121">f&#39;Did not converge in </span><span style="color: #BB6688; font-weight: bold">{</span>max_iterations<span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121"> iterations&#39;</span>)
</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="wrapping-it-up">Wrapping it up </h2>
<p>We now have a simple , un-optimized \( k \)-means
clustering implementation. Lets plot the final result
</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%;">fig <span style="color: #666666">=</span> plt<span style="color: #666666">.</span>figure()
ax <span style="color: #666666">=</span> fig<span style="color: #666666">.</span>add_subplot()
unique_cluster_labels <span style="color: #666666">=</span> np<span style="color: #666666">.</span>unique(cluster_labels)
<span style="color: #008000; font-weight: bold">for</span> i <span style="color: #AA22FF; font-weight: bold">in</span> unique_cluster_labels:
ax<span style="color: #666666">.</span>scatter(data[cluster_labels <span style="color: #666666">==</span> i, <span style="color: #666666">0</span>],
data[cluster_labels <span style="color: #666666">==</span> i, <span style="color: #666666">1</span>],
label <span style="color: #666666">=</span> i,
alpha <span style="color: #666666">=</span> <span style="color: #666666">0.2</span>)
ax<span style="color: #666666">.</span>scatter(centroids[:, <span style="color: #666666">0</span>], centroids[:, <span style="color: #666666">1</span>], c<span style="color: #666666">=</span><span style="color: #BA2121">&#39;black&#39;</span>)
ax<span style="color: #666666">.</span>set_title(<span style="color: #BA2121">&quot;Final Result of K-means Clustering&quot;</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>
<!-- 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">def</span> <span style="color: #0000FF">naive_kmeans</span>(data, n_clusters<span style="color: #666666">=4</span>, max_iterations<span style="color: #666666">=100</span>, tolerance<span style="color: #666666">=1e-8</span>):
start_time <span style="color: #666666">=</span> time<span style="color: #666666">.</span>time()
n_samples, dimensions <span style="color: #666666">=</span> data<span style="color: #666666">.</span>shape
n_clusters <span style="color: #666666">=</span> <span style="color: #666666">4</span>
<span style="color: #408080; font-style: italic">#np.random.seed(2021)</span>
centroids <span style="color: #666666">=</span> data[np<span style="color: #666666">.</span>random<span style="color: #666666">.</span>choice(n_samples, n_clusters, replace<span style="color: #666666">=</span><span style="color: #008000; font-weight: bold">False</span>), :]
distances <span style="color: #666666">=</span> np<span style="color: #666666">.</span>zeros((n_samples, n_clusters))
<span style="color: #008000; font-weight: bold">for</span> k <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(n_clusters):
<span style="color: #008000; font-weight: bold">for</span> n <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(n_samples):
dist <span style="color: #666666">=</span> <span style="color: #666666">0</span>
<span style="color: #008000; font-weight: bold">for</span> d <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(dimensions):
dist <span style="color: #666666">+=</span> np<span style="color: #666666">.</span>abs(data[n, d] <span style="color: #666666">-</span> centroids[k, d])<span style="color: #666666">**2</span>
distances[n, k] <span style="color: #666666">=</span> dist
cluster_labels <span style="color: #666666">=</span> np<span style="color: #666666">.</span>zeros(n_samples, dtype<span style="color: #666666">=</span><span style="color: #BA2121">&#39;int&#39;</span>)
<span style="color: #008000; font-weight: bold">for</span> n <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(n_samples):
smallest <span style="color: #666666">=</span> <span style="color: #666666">1e10</span>
smallest_row_index <span style="color: #666666">=</span> <span style="color: #666666">1e10</span>
<span style="color: #008000; font-weight: bold">for</span> k <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(n_clusters):
<span style="color: #008000; font-weight: bold">if</span> distances[n, k] <span style="color: #666666">&lt;</span> smallest:
smallest <span style="color: #666666">=</span> distances[n, k]
smallest_row_index <span style="color: #666666">=</span> k
cluster_labels[n] <span style="color: #666666">=</span> smallest_row_index
<span style="color: #008000; font-weight: bold">for</span> iteration <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(max_iterations):
prev_centroids <span style="color: #666666">=</span> centroids<span style="color: #666666">.</span>copy()
<span style="color: #008000; font-weight: bold">for</span> k <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(n_clusters):
vector_mean <span style="color: #666666">=</span> np<span style="color: #666666">.</span>zeros(dimensions)
mean_divisor <span style="color: #666666">=</span> <span style="color: #666666">0</span>
<span style="color: #008000; font-weight: bold">for</span> n <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(n_samples):
<span style="color: #008000; font-weight: bold">if</span> cluster_labels[n] <span style="color: #666666">==</span> k:
vector_mean <span style="color: #666666">+=</span> data[n, :]
mean_divisor <span style="color: #666666">+=</span> <span style="color: #666666">1</span>
centroids[k, :] <span style="color: #666666">=</span> vector_mean <span style="color: #666666">/</span> mean_divisor
<span style="color: #008000; font-weight: bold">for</span> k <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(n_clusters):
<span style="color: #008000; font-weight: bold">for</span> n <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(n_samples):
dist <span style="color: #666666">=</span> <span style="color: #666666">0</span>
<span style="color: #008000; font-weight: bold">for</span> d <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(dimensions):
dist <span style="color: #666666">+=</span> np<span style="color: #666666">.</span>abs(data[n, d] <span style="color: #666666">-</span> centroids[k, d])<span style="color: #666666">**2</span>
distances[n, k] <span style="color: #666666">=</span> dist
<span style="color: #008000; font-weight: bold">for</span> n <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(n_samples):
smallest <span style="color: #666666">=</span> <span style="color: #666666">1e10</span>
smallest_row_index <span style="color: #666666">=</span> <span style="color: #666666">1e10</span>
<span style="color: #008000; font-weight: bold">for</span> k <span style="color: #AA22FF; font-weight: bold">in</span> <span style="color: #008000">range</span>(n_clusters):
<span style="color: #008000; font-weight: bold">if</span> distances[n, k] <span style="color: #666666">&lt;</span> smallest:
smallest <span style="color: #666666">=</span> distances[n, k]
smallest_row_index <span style="color: #666666">=</span> k
cluster_labels[n] <span style="color: #666666">=</span> smallest_row_index
centroid_difference <span style="color: #666666">=</span> np<span style="color: #666666">.</span>sum(np<span style="color: #666666">.</span>abs(centroids <span style="color: #666666">-</span> prev_centroids))
<span style="color: #008000; font-weight: bold">if</span> centroid_difference <span style="color: #666666">&lt;</span> tolerance:
<span style="color: #008000">print</span>(<span style="color: #BA2121">f&#39;Converged at iteration </span><span style="color: #BB6688; font-weight: bold">{</span>iteration<span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121">&#39;</span>)
<span style="color: #008000">print</span>(<span style="color: #BA2121">f&#39;Runtime: </span><span style="color: #BB6688; font-weight: bold">{</span>time<span style="color: #666666">.</span>time() <span style="color: #666666">-</span> start_time<span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121"> seconds&#39;</span>)
<span style="color: #008000; font-weight: bold">return</span> cluster_labels, centroids
<span style="color: #008000">print</span>(<span style="color: #BA2121">f&#39;Did not converge in </span><span style="color: #BB6688; font-weight: bold">{</span>max_iterations<span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121"> iterations&#39;</span>)
<span style="color: #008000">print</span>(<span style="color: #BA2121">f&#39;Runtime: </span><span style="color: #BB6688; font-weight: bold">{</span>time<span style="color: #666666">.</span>time() <span style="color: #666666">-</span> start_time<span style="color: #BB6688; font-weight: bold">}</span><span style="color: #BA2121"> seconds&#39;</span>)
<span style="color: #008000; font-weight: bold">return</span> cluster_labels, centroids
</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>
<p>Decision trees and wrapping up PCA.</p>
<!-- !split --><br><br><br><br><br><br><br><br><br><br>
<h2 id="decision-trees-overarching-aims">Decision trees, overarching aims </h2>
Binary file not shown.
File diff suppressed because it is too large Load Diff
+455 -4
View File
@@ -1,4 +1,4 @@
TITLE: Week 44: From Decision Trees to Bagging methods
TITLE: Week 44: Dimensionality Reduction, PCA and Clustering. Decision Trees
AUTHOR: Morten Hjorth-Jensen {copyright, 1999-present|CC BY-NC} at Department of Physics, University of Oslo & Department of Physics and Astronomy and National Superconducting Cyclotron Laboratory, Michigan State University
DATE: today
@@ -6,7 +6,7 @@ DATE: today
!split
===== Overview of week 44 =====
* Thursday: Wrapping up PCA from last week and basics of decision trees, classification and regression algorithms
* Thursday: Wrapping up PCA from last week, Clustering and basics of decision trees, classification and regression algorithms
* Friday: Decision trees, voting models and bagging
@@ -14,9 +14,460 @@ Geron's chapter 6 covers decision trees while ensemble models, voting and baggin
!split
===== Thursday =====
===== Thursday, Principal Component Analysis =====
For the principal component analysis,
see slides from "week 43":"https://compphysics.github.io/MachineLearning/doc/pub/week43/html/week43-reveal.html", in particular from slide 28 and forward
!split
===== Thursday: Clustering and Unsupervised Learning =====
In general terms cluster analysis, or clustering, is the task of grouping a
data-set into different distinct categories based on some measure of equality of
the data. This measure is often referred to as a _metric_ or _similarity
measure_ in the literature (note: sometimes we deal with a _dissimilarity
measure_ instead). Usually, these metrics are formulated as some kind of
distance function between points in a high-dimensional space.
The simplest, and also the most
common is the _Euclidean distance_.
!split
===== Basic Idea of the $k$-means Clustering Algorithm =====
The simplest of all clustering algorithms is the _k-means algorithm_
, sometimes also referred to as *Lloyds algorithm*. It is the simplest and also
the most common. From its simplicity it obtains both strengths and weaknesses.
These will be discussed in more detail later. The $k$-means algorithm is a
_centroid based_ clustering algorithm.
!split
===== The $k$-means Algorithm =====
Assume, we are given $n$ data points and we wish to split the data into $K < n$
different categories, or clusters. We label each cluster by an integer
!bt
\[ k\in\{1, \cdots, K \}$.
\]
!et
In the basic k-means algorithm each point is assigned to only
one cluster $k$, and these assignments are *non-injective* i.e. many-to-one. We
can think of these mappings as an encoder $k = C(i)$, which assigns the $i$-th
data-point $\bf x_i$ to the $k$-th cluster.
$k$-means algorithm in words:
o We start with guesses / random initializations of our $k$ cluster centers /
centroids
o For each centroid the points that are most similar are identified
o Then we move / replace each centroid with a coordinate average of all the
points that were assigned to that centroid.
o Iterate this points 2, 3) until the centroids no longer move (to some
tolerance)
!split
===== Basic Math of the $k$-means Algorithm =====
We assume we have $n$ data-points
!bt
\begin{equation}\label{eq:kmeanspoints}
\bm{x_i} = \{x_{i, 1}, \cdots, x_{i, p}\}\in\mathbb{R}^p.
\end{equation}
!et
which we wish to group into $K < n$ clusters. For our dissimilarity measure we
use the *squared Euclidean distance*
!bt
\begin{equation}\label{eq:squaredeuclidean}
d(\bm{x_i}, \bm{x_i'}) = \sum_{j=1}^p(x_{ij} - x_{i'j})^2
= ||\bm{x_i} - \bm{x_{i'}}||^2
\end{equation}
!et
!split
===== Within Cluster Point Scatter =====
We define the so called *within-cluster point scatter* which gives us a
measure of how close each data point assigned to the same cluster tends to be to
the all the others.
!bt
\begin{equation}\label{eq:withincluster}
W(C) = \frac{1}{2}\sum_{k=1}^K\sum_{C(i)=k}
\sum_{C(i')=k}d(\bm{x_i}, \bm{x_{i'}}) =
\sum_{k=1}^KN_k\sum_{C(i)=k}||\bm{x_i} - \bm{\overline{x_k}}||^2
\end{equation}
!et
where $\bm{\overline{x_k}}$ is the mean vector associated with the $k$-th
cluster, and $N_k = \sum_{i=1}^nI(C(i) = k)$, where the $I()$ notation is
similar to the Kronecker delta (*Commonly used in statistics, it just means that
when $i = k$ we have the encoder $C(i)$*). In other words, the within-cluster
scatter measures the compactness of each cluster with respect to the data points
assigned to each cluster. This is the quantity that the $k$-means algorithm aims
to minimize. We refer to this quantity $W(C)$ as the within cluster scatter
because of its relation to the *total scatter*.
!split
===== More Details =====
We have
!bt
\begin{equation}\label{eq:totalscatter}
T = W(C) + B(C) = \frac{1}{2}\sum_{i=1}^n
\sum_{i'=1}^nd(\bm{x_i}, \bm{x_{i'}})
= \frac{1}{2}\sum_{k=1}^K\sum_{C(i)=k}
\Big(\sum_{C(i') = k}d(\bm{x_i}, \bm{x_{i'}})
+ \sum_{C(i')\neq k}d(\bm{x_i}, \bm{x_{i'}})\Big).
\end{equation}
!et
This is a quantity that is conserved throughout the $k$-means algorithm. It can
be thought of as the total amount of information in the data, and it is composed
of the aforementioned within-cluster scatter and the *between-cluster scatter*
$B(C)$. In methods such as principle component analysis the total scatter is not
conserved.
!split
===== Total Cluster Variance =====
Given a cluster mean $\bm{m_k}$ we define the _total cluster variance_
!bt
\begin{equation}\label{eq:totalclustervariance}
\min_{C, \{\bm{m_k}\}_1^K}\sum_{k=1}^KN_k\sum||\bm{x_i} - \bm{m_k}||^2
\end{equation}
!et
Now we have all the pieces necessary to formally revisit the $k$-means algorithm.
!split
===== The $k$-means Clustering Algorithm =====
The $k$-means clustering algorithm goes as follows
o For a given cluster assignment $C$, and $k$ cluster means
$\left{m_1, \cdots, m_k\right}$. We minimize the total cluster variance with respect to
the cluster means $\{m_k\}$ yielding the means of the currently assigned
clusters.
o Given a current set of $k$ means $\{m_k\}$ the total cluster variance is
minimized by assigning each observation to the closest (current) cluster mean.
That is $$C(i) = \underset{1\leq k\leq K}{\mathrm{argmin}}
||\bm{x_i} - \bm{m_k}||^2$$
o Steps 1 and 2 are repeated until the assignments do not change.
!split
===== Summarizing =====
o Before we start we specify a number $k$ which is the number of clusters we
want to try to separate our data into.
o We initially choose $k$ random data points in our data as our initial
centroids, *or means* (this is where the name comes from).
o Assign each data point to their closest centroid, based on the squared
Euclidean distance.
o For each of the $k$ cluster we update the centroid by calculating new mean
values for all the data points in the cluster.
o Iteratively minimize the within cluster scatter by performing steps (3, 4)
until the new assignments stop changing (can be to some tolerance) or
until a maximum number of iterations have passed.
!split
===== Writing our own Code, the Data Set =====
Let us now program the most basic version of the algorithm using nothing but
Python with numpy arrays. This code is kept intentionally simple to gradually
progress our understanding. There is no vectorization of any kind, and even most
helper functions are not utilized.
We need first a dataset to do our cluster analysis on. In our case
this is a plain *vanilla* data set using random numbers using a
Gaussian distribution.
!bc pycod
import time
import numpy as np
import tensorflow as tf
from matplotlib import image
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from IPython.display import display
np.random.seed(2021)
!ec
Next we define functions, for ease of use later, to generate Gaussians and to
set up our toy data set.
!bc pycod
def gaussian_points(dim=2, n_points=1000, mean_vector=np.array([0, 0]),
sample_variance=1):
"""
Very simple custom function to generate gaussian distributed point clusters
with variable dimension, number of points, means in each direction
(must match dim) and sample variance.
Inputs:
dim (int)
n_points (int)
mean_vector (np.array) (where index 0 is x, index 1 is y etc.)
sample_variance (float)
Returns:
data (np.array): with dimensions (dim x n_points)
"""
mean_matrix = np.zeros(dim) + mean_vector
covariance_matrix = np.eye(dim) * sample_variance
data = np.random.multivariate_normal(mean_matrix, covariance_matrix,
n_points)
return data
def generate_simple_clustering_dataset(dim=2, n_points=1000, plotting=True,
return_data=True):
"""
Toy model to illustrate k-means clustering
"""
data1 = gaussian_points(mean_vector=np.array([5, 5]))
data2 = gaussian_points()
data3 = gaussian_points(mean_vector=np.array([1, 4.5]))
data4 = gaussian_points(mean_vector=np.array([5, 1]))
data = np.concatenate((data1, data2, data3, data4), axis=0)
if plotting:
fig, ax = plt.subplots()
ax.scatter(data[:, 0], data[:, 1], alpha=0.2)
ax.set_title('Toy Model Dataset')
plt.show()
if return_data:
return data
data = generate_simple_clustering_dataset()
!ec
!split
===== Implementing the $k$-means Algorithm =====
With the above dataset we start
implementing the $k$-means algorithm.
!bc pycod
n_samples, dimensions = data.shape
n_clusters = 4
# we randomly initialize our centroids
np.random.seed(2021)
centroids = data[np.random.choice(n_samples, n_clusters, replace=False), :]
distances = np.zeros((n_samples, n_clusters))
# first we need to calculate the distance to each centroid from our data
for k in range(n_clusters):
for n in range(n_samples):
dist = 0
for d in range(dimensions):
dist += np.abs(data[n, d] - centroids[k, d])**2
distances[n, k] = dist
# we initialize an array to keep track of to which cluster each point belongs
# the way we set it up here the index tracks which point and the value which
# cluster the point belongs to
cluster_labels = np.zeros(n_samples, dtype='int')
# next we loop through our samples and for every point assign it to the cluster
# to which it has the smallest distance to
for n in range(n_samples):
# tracking variables (all of this is basically just an argmin)
smallest = 1e10
smallest_row_index = 1e10
for k in range(n_clusters):
if distances[n, k] < smallest:
smallest = distances[n, k]
smallest_row_index = k
cluster_labels[n] = smallest_row_index
!ec
!split
===== Plotting =====
!bc pycod
fig = plt.figure()
ax = fig.add_subplot()
unique_cluster_labels = np.unique(cluster_labels)
for i in unique_cluster_labels:
ax.scatter(data[cluster_labels == i, 0],
data[cluster_labels == i, 1],
label = i,
alpha = 0.2)
ax.scatter(centroids[:, 0], centroids[:, 1], c='black')
ax.set_title("First Grouping of Points to Centroids")
plt.show()
!ec
So what do we have so far? We have 'picked' $k$ centroids at random from our
data points. There are other ways of more intelligently choosing their
initializations, however for our purposes randomly is fine. Then we have
initialized an array 'distances' which holds the information of the distance,
*or dissimilarity*, of every point to of our centroids. Finally, we have
initialized an array 'cluster_labels' which according to our distances array
holds the information of to which centroid every point is assigned. This was the
first pass of our algorithm. Essentially, all we need to do now is repeat the
distance and assignment steps above until we have reached a desired convergence
or a maximum amount of iterations.
!split
===== Continuing =====
!bc pycod
max_iterations = 100
tolerance = 1e-8
for iteration in range(max_iterations):
prev_centroids = centroids.copy()
for k in range(n_clusters):
# this array will be used to update our centroid positions
vector_mean = np.zeros(dimensions)
mean_divisor = 0
for n in range(n_samples):
if cluster_labels[n] == k:
vector_mean += data[n, :]
mean_divisor += 1
# update according to the k means
centroids[k, :] = vector_mean / mean_divisor
# we find the dissimilarity
for k in range(n_clusters):
for n in range(n_samples):
dist = 0
for d in range(dimensions):
dist += np.abs(data[n, d] - centroids[k, d])**2
distances[n, k] = dist
# assign each point
for n in range(n_samples):
smallest = 1e10
smallest_row_index = 1e10
for k in range(n_clusters):
if distances[n, k] < smallest:
smallest = distances[n, k]
smallest_row_index = k
cluster_labels[n] = smallest_row_index
# convergence criteria
centroid_difference = np.sum(np.abs(centroids - prev_centroids))
if centroid_difference < tolerance:
print(f'Converged at iteration {iteration}')
break
elif iteration == max_iterations:
print(f'Did not converge in {max_iterations} iterations')
!ec
!split
===== Wrapping it up =====
We now have a simple , un-optimized $k$-means
clustering implementation. Lets plot the final result
!bc pycod
fig = plt.figure()
ax = fig.add_subplot()
unique_cluster_labels = np.unique(cluster_labels)
for i in unique_cluster_labels:
ax.scatter(data[cluster_labels == i, 0],
data[cluster_labels == i, 1],
label = i,
alpha = 0.2)
ax.scatter(centroids[:, 0], centroids[:, 1], c='black')
ax.set_title("Final Result of K-means Clustering")
plt.show()
!ec
!bc pycod
def naive_kmeans(data, n_clusters=4, max_iterations=100, tolerance=1e-8):
start_time = time.time()
n_samples, dimensions = data.shape
n_clusters = 4
#np.random.seed(2021)
centroids = data[np.random.choice(n_samples, n_clusters, replace=False), :]
distances = np.zeros((n_samples, n_clusters))
for k in range(n_clusters):
for n in range(n_samples):
dist = 0
for d in range(dimensions):
dist += np.abs(data[n, d] - centroids[k, d])**2
distances[n, k] = dist
cluster_labels = np.zeros(n_samples, dtype='int')
for n in range(n_samples):
smallest = 1e10
smallest_row_index = 1e10
for k in range(n_clusters):
if distances[n, k] < smallest:
smallest = distances[n, k]
smallest_row_index = k
cluster_labels[n] = smallest_row_index
for iteration in range(max_iterations):
prev_centroids = centroids.copy()
for k in range(n_clusters):
vector_mean = np.zeros(dimensions)
mean_divisor = 0
for n in range(n_samples):
if cluster_labels[n] == k:
vector_mean += data[n, :]
mean_divisor += 1
centroids[k, :] = vector_mean / mean_divisor
for k in range(n_clusters):
for n in range(n_samples):
dist = 0
for d in range(dimensions):
dist += np.abs(data[n, d] - centroids[k, d])**2
distances[n, k] = dist
for n in range(n_samples):
smallest = 1e10
smallest_row_index = 1e10
for k in range(n_clusters):
if distances[n, k] < smallest:
smallest = distances[n, k]
smallest_row_index = k
cluster_labels[n] = smallest_row_index
centroid_difference = np.sum(np.abs(centroids - prev_centroids))
if centroid_difference < tolerance:
print(f'Converged at iteration {iteration}')
print(f'Runtime: {time.time() - start_time} seconds')
return cluster_labels, centroids
print(f'Did not converge in {max_iterations} iterations')
print(f'Runtime: {time.time() - start_time} seconds')
return cluster_labels, centroids
!ec
Decision trees and wrapping up PCA.
!split
===== Decision trees, overarching aims =====