{"id":388,"date":"2015-12-11T15:54:10","date_gmt":"2015-12-11T15:54:10","guid":{"rendered":"http:\/\/drsfenner.org\/blog\/?p=388"},"modified":"2016-02-05T16:28:27","modified_gmt":"2016-02-05T16:28:27","slug":"three-paths-to-least-squares-linear-regression","status":"publish","type":"post","link":"https:\/\/drsfenner.org\/blog\/2015\/12\/three-paths-to-least-squares-linear-regression\/","title":{"rendered":"Three Paths to Least Squares Linear Regression"},"content":{"rendered":"<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>In the <a href=\"http:\/\/drsfenner.org\/blog\/2015\/12\/linear-regression-basic-solution-3\/\">prior installment<\/a>, we talked about solving the basic problem of linear regression using a standard least-squares approach. Along the way, I showed a few methods that gave the same results to the least squares problem. Today, I want to look at three methods for solving linear regression:<\/p>\n<ol style=\"list-style-type: decimal\">\n<li>solving the full normal equations via Cholesky decomposition<\/li>\n<li>solving a least-squares problem via QR decomposition<\/li>\n<li>solving a least-squares problem via SVD (singular value decomposition)<\/li>\n<\/ol>\n<p><!--more-->\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>The following table may not make complete sense to start with, but consider it an outline of what we are going to discuss. <code>nla<\/code>\/<code>sla<\/code>\/<code>cla<\/code> refer to <code>numpy.linalg<\/code>, <code>scipy.linalg<\/code>, and <code>cvxopt.lapack<\/code>, respectively. Each of the top-level routines makes use of high quality <a href=\"http:\/\/www.netlib.org\/lapack\/lug\/\">LAPACK<\/a> routines under the hood.<\/p>\n<p>While we&#8217;ll use abbreviations for the LAPACK routine names, these abbreviated names don&#8217;t really exist in LAPACK. For example, there is no <code>posv<\/code> in LAPACK. <code>posv<\/code> is our shorthand for <code>dposv<\/code> which is the <code>double<\/code> (double precision floating-point) specific version of <code>_posv<\/code>. There is also <code>sposv<\/code>, <code>cposv<\/code>, and <code>zposv<\/code>. For example, see the pair of rows labelled &quot;symmetric\/Hermiatian positive definite&quot; (the two row headers belong together, it&#8217;s just bad formatting) in <a href=\"http:\/\/www.netlib.org\/lapack\/lug\/node26.html#tabdrivelineq\">Table 2.2 of the LUG<\/a>.<\/p>\n<p>The LAPACK routines for these solutions are described generally under <a href=\"http:\/\/www.netlib.org\/lapack\/lug\/node26.html\">(Solving) Linear Equations<\/a> and <a href=\"http:\/\/www.netlib.org\/lapack\/lug\/node27.html\">Linear Least Squares<\/a>. The first set of routines are for square matrices. In some cases, the Intel documentation for the MKL (which includes an LAPACK implementation) has better descriptions of some of these routines. See: <a href=\"https:\/\/software.intel.com\/en-us\/node\/520972#TBL3-3\">(Solving) Linear Equations<\/a> and <a href=\"https:\/\/software.intel.com\/en-us\/node\/521111#TBL4-9\">Linear Least Squares<\/a>.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<table>\n<thead>\n<tr class=\"header\">\n<th align=\"left\">Python<br \/>Library<br \/>Routine<\/th>\n<th align=\"left\">Underlying<br \/>LAPACK<br \/>Routine<\/th>\n<th align=\"left\">Matrix Type<\/th>\n<th align=\"left\">Algorithm<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr class=\"odd\">\n<td align=\"left\"><code>sla.solve<\/code><br \/>w\/ <code>sym_pos=True<\/code><\/td>\n<td align=\"left\"><code>posv<\/code><\/td>\n<td align=\"left\">symmetric positive<br \/>definite matrix<\/td>\n<td align=\"left\">solve eqn using Cholesky<\/td>\n<\/tr>\n<tr class=\"even\">\n<td align=\"left\"><code>nla.solve<\/code><br \/><code>sla.solve<\/code><\/td>\n<td align=\"left\"><code>gesv<\/code><\/td>\n<td align=\"left\">square matrix<\/td>\n<td align=\"left\">solve eqn using LU<\/td>\n<\/tr>\n<tr class=\"odd\">\n<td align=\"left\"><code>cla.solve<\/code><\/td>\n<td align=\"left\"><code>gels<\/code><\/td>\n<td align=\"left\">general matrix (full rank)<\/td>\n<td align=\"left\">least-squares via QR<\/td>\n<\/tr>\n<tr class=\"even\">\n<td align=\"left\"><code>sla.lstsq<\/code><\/td>\n<td align=\"left\"><code>gelss<\/code><\/td>\n<td align=\"left\">general matrix<\/td>\n<td align=\"left\">least-squares via SVD<\/td>\n<\/tr>\n<tr class=\"odd\">\n<td align=\"left\"><code>nla.lstsq<\/code><\/td>\n<td align=\"left\"><code>gelsd<\/code><\/td>\n<td align=\"left\">general matrix<\/td>\n<td align=\"left\">least-squares via Divide-and-Conquer SVD<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<h3 id=\"imports\">Imports<\/h3>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>Not too surprising, but we&#8217;ll do some imports to get us started. If you haven&#8217;t seen it before, <code>cvxopt<\/code> is a nice package for coding optimization problems. Because these problems often involve linear algebra, <code>cvxopt<\/code> has an (partial) interface to LAPACK and it exposes some routines that <code>numpy<\/code> and <code>scipy<\/code>&#8216;s LAPACK interfaces miss. Annoyingly, none of these three LAPACK interfaces are complete. Even if you put them all together, there is still missing functionality. But, we&#8217;ll deal with a workaround next time.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In&nbsp;[1]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython2\">\n<pre><span class=\"kn\">import<\/span> <span class=\"nn\">operator<\/span> <span class=\"kn\">as<\/span> <span class=\"nn\">op<\/span>\r\n\r\n<span class=\"kn\">import<\/span> <span class=\"nn\">numpy<\/span> <span class=\"kn\">as<\/span> <span class=\"nn\">np<\/span>\r\n<span class=\"kn\">import<\/span> <span class=\"nn\">numpy.linalg<\/span> <span class=\"kn\">as<\/span> <span class=\"nn\">nla<\/span>\r\n<span class=\"kn\">import<\/span> <span class=\"nn\">scipy.linalg<\/span> <span class=\"kn\">as<\/span> <span class=\"nn\">sla<\/span>\r\n\r\n<span class=\"kn\">from<\/span> <span class=\"nn\">cvxopt<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">matrix<\/span> <span class=\"k\">as<\/span> <span class=\"n\">cvx_matrix<\/span>\r\n<span class=\"kn\">import<\/span> <span class=\"nn\">cvxopt.lapack<\/span> <span class=\"kn\">as<\/span> <span class=\"nn\">cla<\/span>\r\n\r\n<span class=\"o\">%<\/span><span class=\"k\">load_ext<\/span> memory_profiler\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<h3 id=\"data-creation-and-a-memory-profiling-note\">Data Creation and a Memory Profiling Note<\/h3>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In&nbsp;[2]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython2\">\n<pre><span class=\"k\">def<\/span> <span class=\"nf\">create_data<\/span><span class=\"p\">(<\/span><span class=\"n\">n<\/span><span class=\"o\">=<\/span><span class=\"mi\">5000<\/span><span class=\"p\">,<\/span> <span class=\"n\">p<\/span><span class=\"o\">=<\/span><span class=\"mi\">100<\/span><span class=\"p\">):<\/span>\r\n    <span class=\"sd\">&#39;&#39;&#39; <\/span>\r\n<span class=\"sd\">        n is number cases\/observations\/examples<\/span>\r\n<span class=\"sd\">        p is number of features\/attributes\/variables<\/span>\r\n<span class=\"sd\">    &#39;&#39;&#39;<\/span>\r\n    <span class=\"n\">X<\/span>      <span class=\"o\">=<\/span> <span class=\"n\">np<\/span><span class=\"o\">.<\/span><span class=\"n\">random<\/span><span class=\"o\">.<\/span><span class=\"n\">uniform<\/span><span class=\"p\">(<\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span><span class=\"mi\">10<\/span><span class=\"p\">,<\/span> <span class=\"p\">(<\/span><span class=\"n\">n<\/span><span class=\"p\">,<\/span><span class=\"n\">p<\/span><span class=\"p\">))<\/span>\r\n    <span class=\"n\">coeffs<\/span> <span class=\"o\">=<\/span> <span class=\"n\">np<\/span><span class=\"o\">.<\/span><span class=\"n\">random<\/span><span class=\"o\">.<\/span><span class=\"n\">uniform<\/span><span class=\"p\">(<\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span><span class=\"mi\">10<\/span><span class=\"p\">,<\/span> <span class=\"n\">p<\/span><span class=\"p\">)<\/span>\r\n    <span class=\"k\">def<\/span> <span class=\"nf\">f<\/span><span class=\"p\">(<\/span><span class=\"n\">X<\/span><span class=\"p\">):<\/span> <span class=\"k\">return<\/span> <span class=\"n\">X<\/span><span class=\"o\">.<\/span><span class=\"n\">dot<\/span><span class=\"p\">(<\/span><span class=\"n\">coeffs<\/span><span class=\"p\">)<\/span>\r\n\r\n    <span class=\"n\">noise<\/span> <span class=\"o\">=<\/span> <span class=\"n\">np<\/span><span class=\"o\">.<\/span><span class=\"n\">random<\/span><span class=\"o\">.<\/span><span class=\"n\">normal<\/span><span class=\"p\">(<\/span><span class=\"mf\">0.0<\/span><span class=\"p\">,<\/span> <span class=\"mf\">2.0<\/span><span class=\"p\">,<\/span> <span class=\"n\">n<\/span><span class=\"p\">)<\/span>\r\n    <span class=\"n\">Y<\/span> <span class=\"o\">=<\/span> <span class=\"n\">f<\/span><span class=\"p\">(<\/span><span class=\"n\">X<\/span><span class=\"p\">)<\/span> <span class=\"o\">+<\/span> <span class=\"n\">noise<\/span>\r\n    <span class=\"n\">Y<\/span> <span class=\"o\">=<\/span> <span class=\"n\">Y<\/span><span class=\"o\">.<\/span><span class=\"n\">reshape<\/span><span class=\"p\">((<\/span><span class=\"n\">n<\/span><span class=\"p\">,<\/span><span class=\"mi\">1<\/span><span class=\"p\">))<\/span>\r\n    \r\n    <span class=\"k\">return<\/span> <span class=\"n\">X<\/span><span class=\"p\">,<\/span><span class=\"n\">Y<\/span>\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>You&#8217;ll notice that I used an iPython magic (<code>%load_ext memory_profiler<\/code>) above in the <code>import<\/code> section. I&#8217;m not going to jump into the details of installing the memory profiler (but <code>pip install memory_profiler<\/code> might be all you need). However, we do have to discuss a few issues that come up with its use. If you want to recreate the memory use values I gather below, you&#8217;ll need to be a bit careful.<\/p>\n<p>If I say:<\/p>\n<pre><code>    X,Y = create_data()\r\n    %memit X.T.dot(X)<\/code><\/pre>\n<p>and look at the result, I might see a memory increment of ~8MB. That is: the process of creating <code>X.T.dot(X)<\/code> required an additional 8MB of memory. But, if I then go and run the dot-product again in the next cell:<\/p>\n<pre><code>    %memit X.T.dot(X)<\/code><\/pre>\n<p>it may report an increment of ~0MB. What happened?<\/p>\n<p>The first <code>X.T.dot(X)<\/code> keeps no references to the result and the result can be <code>free()<\/code>&#8216;d &#8211; garbage collected &#8211; back to the OS. However, the OS might not automatically reclaim that memory and\/or Python&#8217;s garbage collector might not automatically release it (that&#8217;s a completely post worthy topic of its own). Thus, the amount of memory that our process is using is still at the +8MB level. So, when we run another dot-product, our process uses that unused-by-a-variable, but not reclaimed-by-the-OS, memory and we see no increment.<\/p>\n<p>The moral of that story is that if you want to see consistent increments, you need to either (1) keep a reference to the resulting object (<code>XtX = X.T.dot(X)<\/code>) so the memory is both not freed and not reclaimed (i.e., it is in use) or (2) after running the <code>%memit<\/code> line, restart your kernel before you do the next <code>%memit<\/code> (foring a freed and reclaimed state for -all- of the process memory).<\/p>\n<p>The problem is even a little more severe: <em>any<\/em> large computation (not just <code>memit<\/code> expressions) that is evaluated and not retained via a reference will cause this memory use masking &#8212; and it makes perfect sense when you think about it. So, all of my <code>memit<\/code>s will be done in a fresh interpreter and I&#8217;ll simply leave comments about the results.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<h3 id=\"create-some-data-and-investigate-memory-use\">Create Some Data and Investigate Memory Use<\/h3>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In&nbsp;[3]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython2\">\n<pre><span class=\"n\">n_obs<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">2<\/span><span class=\"o\">**<\/span><span class=\"mi\">16<\/span>\r\n<span class=\"n\">n_ftrs<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">2<\/span><span class=\"o\">**<\/span><span class=\"mi\">10<\/span>\r\n<span class=\"n\">X<\/span><span class=\"p\">,<\/span><span class=\"n\">Y<\/span> <span class=\"o\">=<\/span> <span class=\"n\">create_data<\/span><span class=\"p\">(<\/span><span class=\"n\">n_obs<\/span><span class=\"p\">,<\/span><span class=\"n\">n_ftrs<\/span><span class=\"p\">)<\/span>\r\n<span class=\"k\">print<\/span> <span class=\"s\">&quot;about&quot;<\/span><span class=\"p\">,<\/span> <span class=\"mi\">10<\/span><span class=\"o\">**<\/span><span class=\"nb\">int<\/span><span class=\"p\">(<\/span><span class=\"n\">np<\/span><span class=\"o\">.<\/span><span class=\"n\">log10<\/span><span class=\"p\">(<\/span><span class=\"n\">n_obs<\/span> <span class=\"o\">*<\/span> <span class=\"n\">n_ftrs<\/span><span class=\"p\">)),<\/span> <span class=\"s\">&quot;entries&quot;<\/span>\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"output_wrapper\">\n<div class=\"output\">\n<div class=\"output_area\">\n<div class=\"prompt\"><\/div>\n<div class=\"output_subarea output_stream output_stdout output_text\">\n<pre>about 10000000 entries\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In&nbsp;[4]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython2\">\n<pre><span class=\"k\">print<\/span> <span class=\"n\">X<\/span><span class=\"o\">.<\/span><span class=\"n\">shape<\/span>\r\n<span class=\"k\">print<\/span> <span class=\"n\">X<\/span><span class=\"o\">.<\/span><span class=\"n\">itemsize<\/span><span class=\"p\">,<\/span> <span class=\"n\">X<\/span><span class=\"o\">.<\/span><span class=\"n\">size<\/span><span class=\"p\">,<\/span> <span class=\"nb\">reduce<\/span><span class=\"p\">(<\/span><span class=\"n\">op<\/span><span class=\"o\">.<\/span><span class=\"n\">mul<\/span><span class=\"p\">,<\/span> <span class=\"n\">X<\/span><span class=\"o\">.<\/span><span class=\"n\">shape<\/span><span class=\"p\">)<\/span>\r\n<span class=\"c\"># memory(X) : 2**(16 + 10 + 3) [2**3 (8 byte)]<\/span>\r\n<span class=\"k\">print<\/span> <span class=\"p\">(<\/span><span class=\"n\">X<\/span><span class=\"o\">.<\/span><span class=\"n\">itemsize<\/span> <span class=\"o\">*<\/span> <span class=\"n\">X<\/span><span class=\"o\">.<\/span><span class=\"n\">size<\/span><span class=\"p\">)<\/span> <span class=\"o\">\/<\/span> <span class=\"nb\">float<\/span><span class=\"p\">(<\/span><span class=\"mi\">1024<\/span><span class=\"o\">**<\/span><span class=\"mi\">2<\/span><span class=\"p\">)<\/span>  <span class=\"c\"># 1024**2 == 2**20 == 1 MiB<\/span>\r\n<span class=\"k\">print<\/span> <span class=\"mi\">2<\/span><span class=\"o\">**<\/span><span class=\"p\">(<\/span><span class=\"mi\">16<\/span> <span class=\"o\">+<\/span> <span class=\"mi\">10<\/span> <span class=\"o\">+<\/span> <span class=\"mi\">3<\/span><span class=\"p\">)<\/span> <span class=\"o\">\/<\/span> <span class=\"mi\">2<\/span><span class=\"o\">**<\/span><span class=\"mi\">20<\/span>\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"output_wrapper\">\n<div class=\"output\">\n<div class=\"output_area\">\n<div class=\"prompt\"><\/div>\n<div class=\"output_subarea output_stream output_stdout output_text\">\n<pre>(65536, 1024)\r\n8 67108864 67108864\r\n512.0\r\n512\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In&nbsp;[5]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython2\">\n<pre><span class=\"k\">print<\/span> <span class=\"n\">X<\/span><span class=\"o\">.<\/span><span class=\"n\">shape<\/span>\r\n<span class=\"n\">XtX<\/span> <span class=\"o\">=<\/span> <span class=\"n\">X<\/span><span class=\"o\">.<\/span><span class=\"n\">T<\/span><span class=\"o\">.<\/span><span class=\"n\">dot<\/span><span class=\"p\">(<\/span><span class=\"n\">X<\/span><span class=\"p\">)<\/span>\r\n<span class=\"k\">print<\/span> <span class=\"n\">XtX<\/span><span class=\"o\">.<\/span><span class=\"n\">shape<\/span>\r\n<span class=\"k\">print<\/span> <span class=\"p\">(<\/span><span class=\"n\">XtX<\/span><span class=\"o\">.<\/span><span class=\"n\">itemsize<\/span> <span class=\"o\">*<\/span> <span class=\"n\">XtX<\/span><span class=\"o\">.<\/span><span class=\"n\">size<\/span><span class=\"p\">)<\/span> <span class=\"o\">\/<\/span> <span class=\"nb\">float<\/span><span class=\"p\">(<\/span><span class=\"mi\">1024<\/span><span class=\"o\">**<\/span><span class=\"mi\">2<\/span><span class=\"p\">)<\/span>\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"output_wrapper\">\n<div class=\"output\">\n<div class=\"output_area\">\n<div class=\"prompt\"><\/div>\n<div class=\"output_subarea output_stream output_stdout output_text\">\n<pre>(65536, 1024)\r\n(1024, 1024)\r\n8.0\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<h3 id=\"np.dot-tests\"><code>np.dot()<\/code> Tests<\/h3>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>Let&#8217;s do two simple tests to get an idea of (1) the cost of some basic operations and (2) understand the portion of resources used in the Cholesky that are <em>not<\/em> due to the factorization and backwards solution.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In&nbsp;[6]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython2\">\n<pre><span class=\"c\">#%memit X.T.dot(X)<\/span>\r\n<span class=\"c\">#increment ~27.5MB<\/span>\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>It&#8217;s possibly surprising that <span class=\"math inline\">\\(X^TX\\)<\/span> is so much smaller than <span class=\"math inline\">\\(X\\)<\/span>. However, we&#8217;re dealing with a &quot;tall-skinny&quot; matrix &#8212; there are many more rows than colums (<span class=\"math inline\">\\(n_{obs} &gt;&gt; n_{ftrs}\\)<\/span>). So, when we take the transpose and multiply we have a <span class=\"math inline\">\\((n_{ftr},n_{obs}) \\times (n_{obs}, n_{ftr}) \\rightarrow (n_{ftr},n_{ftr})\\)<\/span> which has far fewer elements than <span class=\"math inline\">\\((n_{obs}, n_{ftr})\\)<\/span>.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In&nbsp;[7]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython2\">\n<pre><span class=\"o\">%<\/span><span class=\"k\">timeit<\/span> X.T.dot(X)\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"output_wrapper\">\n<div class=\"output\">\n<div class=\"output_area\">\n<div class=\"prompt\"><\/div>\n<div class=\"output_subarea output_stream output_stdout output_text\">\n<pre>1 loops, best of 3: 1.39 s per loop\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>Compressing data requires a trade-off in pre-processing. Here, we see that the dot-product takes a fair bit of time. Remember that it is doing its work (a vector multiplication) in the long (<span class=\"math inline\">\\(n_{obs}\\)<\/span>\/column) dimension.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<h3 id=\"via-cholesky\">Via Cholesky<\/h3>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>Recall that that solving linear regression using least squares means that we want to perform the following optimization: <span class=\"math inline\">\\(\\min RSS(\\beta)=\\min(\\bf{y}-\\bf{X}\\beta)^T(\\bf{y}-\\bf{X}\\beta)\\)<\/span>. That means that we want: <span class=\"math inline\">\\(\\frac{\\partial RSS(\\beta)}{\\partial \\beta} = -2 X^T(Y-X\\beta) = 0\\)<\/span>. We saw that this gives: <span class=\"math inline\">\\(X^TX\\beta = X^TY\\)<\/span>, which we want to solve for <span class=\"math inline\">\\(\\beta\\)<\/span>.<\/p>\n<p><span class=\"math inline\">\\(Cholesky(X^TX)\\)<\/span> gives <span class=\"math inline\">\\(X^TX=LL^T\\)<\/span> with <span class=\"math inline\">\\(L\\)<\/span> lower triangular.<\/p>\n<p>Then, we have <span class=\"math inline\">\\(L^TL\\beta = X^TY\\)<\/span>. We let <span class=\"math inline\">\\(w=L\\beta\\)<\/span> and solve <span class=\"math inline\">\\(Lw=X^TY\\)<\/span> for <span class=\"math inline\">\\(w\\)<\/span> using simple backward substitution. Now, we return to <span class=\"math inline\">\\(L\\beta=w\\)<\/span> and we solve it for <span class=\"math inline\">\\(\\beta\\)<\/span> using simple forward substitution.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In&nbsp;[8]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython2\">\n<pre><span class=\"c\">#%%memit<\/span>\r\n<span class=\"c\">#XtX, XtY = X.T.dot(X), X.T.dot(Y)<\/span>\r\n<span class=\"c\">#betas = sla.solve(XtX,XtY, sym_pos=True, check_finite=False)<\/span>\r\n<span class=\"c\"># increment ~28MB<\/span>\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In&nbsp;[9]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython2\">\n<pre><span class=\"o\">%%<\/span><span class=\"k\">timeit<\/span> \r\nXtX, XtY = X.T.dot(X), X.T.dot(Y)\r\nbetas = sla.solve(XtX,XtY, sym_pos=True, check_finite=False)\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"output_wrapper\">\n<div class=\"output\">\n<div class=\"output_area\">\n<div class=\"prompt\"><\/div>\n<div class=\"output_subarea output_stream output_stdout output_text\">\n<pre>1 loops, best of 3: 1.45 s per loop\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>For comparison sake, here&#8217;s the same effective call but using <code>cvxopt<\/code>&#8216;s LAPACK interface to access <code>posv<\/code> directly. Because <code>cla.posv<\/code> is so low-level it has the same &quot;modify the input&quot; semantics of LAPACK&#8217;s <code>posv<\/code>. Thus, if we want to keep the comparison between the two methods (<code>sla.solve(sym_pos=True)<\/code> and <code>cla.posv<\/code>) fair, we have to explicitly make our own copy. There&#8217;s a second reason we have to make that copy: the <code>cvxopt.lapack<\/code> interface expects <code>cvxopt.matrix<\/code> objects.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In&nbsp;[10]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython2\">\n<pre><span class=\"o\">%%<\/span><span class=\"k\">timeit<\/span>\r\nXtX, XtY = X.T.dot(X), X.T.dot(Y)\r\ncXtX, cXtY = cvx_matrix(XtX), cvx_matrix(XtY)\r\ncla.posv(cXtX, cXtY)\r\nbetas = cXtY[:n_ftrs]\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"output_wrapper\">\n<div class=\"output\">\n<div class=\"output_area\">\n<div class=\"prompt\"><\/div>\n<div class=\"output_subarea output_stream output_stdout output_text\">\n<pre>1 loops, best of 3: 1.46 s per loop\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<h3 id=\"via-qr\">Via QR<\/h3>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>The QR decomposition let&#8217;s us write: <span class=\"math inline\">\\(X=QR\\)<\/span> so that <span class=\"math inline\">\\(X\\beta=Y\\)<\/span> becomes <span class=\"math inline\">\\(QR\\beta=Y\\)<\/span>. The QR decomposition gives a <span class=\"math inline\">\\(Q\\)<\/span> that is orthogonal which means <span class=\"math inline\">\\(Q^T=Q^{-1}\\)<\/span>. So, we can multiply on the left by <span class=\"math inline\">\\(Q^{-1}\\)<\/span>: <span class=\"math inline\">\\(R \\beta = Q^TY\\)<\/span>. Lastly, <span class=\"math inline\">\\(R\\)<\/span> is upper triangular so it is efficient and stable to use backwards substitution to solve that system for <span class=\"math inline\">\\(\\beta\\)<\/span> giving (the numerically stable equivalent to): <span class=\"math inline\">\\(\\beta = R^{-1}Q^TY\\)<\/span>.<\/p>\n<p>Here, we will use <code>cla.gels<\/code>. As with <code>cla.posv<\/code>, we need to make our own copy to keep both the result and the input matrix available.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In&nbsp;[11]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython2\">\n<pre><span class=\"c\">#%%memit<\/span>\r\n<span class=\"c\">#cX, cY = cvx_matrix(X),cvx_matrix(Y)  # makes a copy<\/span>\r\n<span class=\"c\"># increment:  ~512MB<\/span>\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In&nbsp;[12]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython2\">\n<pre><span class=\"c\">#%%memit<\/span>\r\n<span class=\"c\">#cla.gels(cX,cY) # betas are in cY[:p]<\/span>\r\n<span class=\"c\"># increment: ~28 MiB<\/span>\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In&nbsp;[13]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython2\">\n<pre><span class=\"o\">%%<\/span><span class=\"k\">timeit<\/span>\r\ncX, cY = cvx_matrix(X),cvx_matrix(Y)  # makes a copy\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"output_wrapper\">\n<div class=\"output\">\n<div class=\"output_area\">\n<div class=\"prompt\"><\/div>\n<div class=\"output_subarea output_stream output_stdout output_text\">\n<pre>1 loops, best of 3: 954 ms per loop\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In&nbsp;[14]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython2\">\n<pre><span class=\"o\">%%<\/span><span class=\"k\">timeit<\/span>\r\ncX, cY = cvx_matrix(X),cvx_matrix(Y)  # makes a copy\r\ncla.gels(cX,cY) # betas are in cY[:p]\r\nbetas = cY[:n_ftrs]\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"output_wrapper\">\n<div class=\"output\">\n<div class=\"output_area\">\n<div class=\"prompt\"><\/div>\n<div class=\"output_subarea output_stream output_stdout output_text\">\n<pre>1 loops, best of 3: 8.23 s per loop\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<h3 id=\"via-svd\">Via SVD<\/h3>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>Performing linear regression with SVD is quite easy &#8211; even if the theory behind it is slightly more complex. If <span class=\"math inline\">\\(X=U \\Sigma V\\)<\/span>, then the pseudo-inverse of <span class=\"math inline\">\\(X\\)<\/span> is <span class=\"math inline\">\\(X^{\\dagger} = V {\\Sigma}^{\\dagger} U^T\\)<\/span> and we can solve <span class=\"math inline\">\\(X\\beta=Y\\)<\/span> with <span class=\"math inline\">\\(\\beta = X^{\\dagger} X \\beta = X^\\dagger Y = V{\\Sigma}^{\\dagger} U^T Y\\)<\/span><\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In&nbsp;[15]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython2\">\n<pre><span class=\"c\">#%%memit <\/span>\r\n<span class=\"c\">#betas = nla.lstsq(X,Y)[0]<\/span>\r\n<span class=\"c\"># increment ~540MB<\/span>\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In&nbsp;[16]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython2\">\n<pre><span class=\"o\">%%<\/span><span class=\"k\">timeit<\/span>\r\nbetas = nla.lstsq(X,Y)[0]\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"output_wrapper\">\n<div class=\"output\">\n<div class=\"output_area\">\n<div class=\"prompt\"><\/div>\n<div class=\"output_subarea output_stream output_stdout output_text\">\n<pre>1 loops, best of 3: 8.16 s per loop\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<h3 id=\"comparing-betas\">Comparing Betas<\/h3>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>Perhaps we should have done this first, but we should make certain that all of our methods for producing the <span class=\"math inline\">\\(\\beta\\)<\/span> coefficients give us the same results. Fortunately, it&#8217;s easy enough. You might wonder why we don&#8217;t just use the values we computed above. Well, like <code>%memit<\/code>, <code>%timeit<\/code> has quirks. And one of those quirks is that variables defined in a <code>%%timeit<\/code> cell don&#8217;t leak out into the global environment (they are local to the <code>%timeit<\/code> execution). Darn.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In&nbsp;[17]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython2\">\n<pre><span class=\"c\">#<\/span>\r\n<span class=\"c\"># Cholesky <\/span>\r\n<span class=\"c\">#<\/span>\r\n<span class=\"n\">XtX<\/span><span class=\"p\">,<\/span> <span class=\"n\">XtY<\/span> <span class=\"o\">=<\/span> <span class=\"n\">X<\/span><span class=\"o\">.<\/span><span class=\"n\">T<\/span><span class=\"o\">.<\/span><span class=\"n\">dot<\/span><span class=\"p\">(<\/span><span class=\"n\">X<\/span><span class=\"p\">),<\/span> <span class=\"n\">X<\/span><span class=\"o\">.<\/span><span class=\"n\">T<\/span><span class=\"o\">.<\/span><span class=\"n\">dot<\/span><span class=\"p\">(<\/span><span class=\"n\">Y<\/span><span class=\"p\">)<\/span>\r\n<span class=\"n\">betas_chol_1<\/span> <span class=\"o\">=<\/span> <span class=\"n\">sla<\/span><span class=\"o\">.<\/span><span class=\"n\">solve<\/span><span class=\"p\">(<\/span><span class=\"n\">XtX<\/span><span class=\"p\">,<\/span><span class=\"n\">XtY<\/span><span class=\"p\">,<\/span> <span class=\"n\">sym_pos<\/span><span class=\"o\">=<\/span><span class=\"bp\">True<\/span><span class=\"p\">,<\/span> <span class=\"n\">check_finite<\/span><span class=\"o\">=<\/span><span class=\"bp\">False<\/span><span class=\"p\">)<\/span>\r\n\r\n<span class=\"n\">cXtX<\/span><span class=\"p\">,<\/span> <span class=\"n\">cXtY<\/span> <span class=\"o\">=<\/span> <span class=\"n\">cvx_matrix<\/span><span class=\"p\">(<\/span><span class=\"n\">XtX<\/span><span class=\"p\">),<\/span> <span class=\"n\">cvx_matrix<\/span><span class=\"p\">(<\/span><span class=\"n\">XtY<\/span><span class=\"p\">)<\/span>\r\n<span class=\"n\">cla<\/span><span class=\"o\">.<\/span><span class=\"n\">posv<\/span><span class=\"p\">(<\/span><span class=\"n\">cXtX<\/span><span class=\"p\">,<\/span> <span class=\"n\">cXtY<\/span><span class=\"p\">)<\/span>\r\n<span class=\"n\">betas_chol_2<\/span> <span class=\"o\">=<\/span> <span class=\"n\">cXtY<\/span><span class=\"p\">[:<\/span><span class=\"n\">n_ftrs<\/span><span class=\"p\">]<\/span>\r\n\r\n\r\n<span class=\"c\">#<\/span>\r\n<span class=\"c\"># QR<\/span>\r\n<span class=\"c\">#<\/span>\r\n<span class=\"n\">cX<\/span><span class=\"p\">,<\/span> <span class=\"n\">cY<\/span> <span class=\"o\">=<\/span> <span class=\"n\">cvx_matrix<\/span><span class=\"p\">(<\/span><span class=\"n\">X<\/span><span class=\"p\">),<\/span><span class=\"n\">cvx_matrix<\/span><span class=\"p\">(<\/span><span class=\"n\">Y<\/span><span class=\"p\">)<\/span>  <span class=\"c\"># makes a copy<\/span>\r\n<span class=\"n\">cla<\/span><span class=\"o\">.<\/span><span class=\"n\">gels<\/span><span class=\"p\">(<\/span><span class=\"n\">cX<\/span><span class=\"p\">,<\/span><span class=\"n\">cY<\/span><span class=\"p\">)<\/span> <span class=\"c\"># betas are in cY[:p]<\/span>\r\n<span class=\"n\">betas_qr<\/span> <span class=\"o\">=<\/span> <span class=\"n\">cY<\/span><span class=\"p\">[:<\/span><span class=\"n\">n_ftrs<\/span><span class=\"p\">]<\/span>\r\n\r\n<span class=\"c\">#<\/span>\r\n<span class=\"c\"># SVD<\/span>\r\n<span class=\"c\">#<\/span>\r\n<span class=\"n\">betas_svd<\/span> <span class=\"o\">=<\/span> <span class=\"n\">nla<\/span><span class=\"o\">.<\/span><span class=\"n\">lstsq<\/span><span class=\"p\">(<\/span><span class=\"n\">X<\/span><span class=\"p\">,<\/span><span class=\"n\">Y<\/span><span class=\"p\">)[<\/span><span class=\"mi\">0<\/span><span class=\"p\">]<\/span>\r\n\r\n<span class=\"n\">betas<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span><span class=\"n\">betas_chol_1<\/span><span class=\"p\">,<\/span> <span class=\"n\">betas_chol_2<\/span><span class=\"p\">,<\/span> <span class=\"n\">betas_qr<\/span><span class=\"p\">,<\/span> <span class=\"n\">betas_svd<\/span><span class=\"p\">]<\/span>\r\n<span class=\"k\">print<\/span> <span class=\"s\">&quot;All same betas?:&quot;<\/span><span class=\"p\">,<\/span> <span class=\"nb\">all<\/span><span class=\"p\">(<\/span><span class=\"n\">np<\/span><span class=\"o\">.<\/span><span class=\"n\">allclose<\/span><span class=\"p\">(<\/span><span class=\"n\">b1<\/span><span class=\"p\">,<\/span> <span class=\"n\">b2<\/span><span class=\"p\">)<\/span> <span class=\"k\">for<\/span> <span class=\"n\">b1<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">betas<\/span><span class=\"p\">[:<\/span><span class=\"o\">-<\/span><span class=\"mi\">1<\/span><span class=\"p\">]<\/span> <span class=\"k\">for<\/span> <span class=\"n\">b2<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">betas<\/span><span class=\"p\">[<\/span><span class=\"mi\">1<\/span><span class=\"p\">:])<\/span>\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"output_wrapper\">\n<div class=\"output\">\n<div class=\"output_area\">\n<div class=\"prompt\"><\/div>\n<div class=\"output_subarea output_stream output_stdout output_text\">\n<pre>All same betas?: True\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<h3 id=\"summary-results\">Summary Results<\/h3>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>For an <code>np.array<\/code> of ~10M entries (and 512MB) and with non-overwriting semantics, we have:<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<table>\n<thead>\n<tr class=\"header\">\n<th align=\"left\">Algorithm<\/th>\n<th align=\"left\">Called Via<\/th>\n<th align=\"left\">Memory<br \/>Increment(MB)<\/th>\n<th align=\"left\">~Time(s)<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr class=\"odd\">\n<td align=\"left\">dot-product<\/td>\n<td align=\"left\"><code>np.dot()<\/code><\/td>\n<td align=\"left\">28<\/td>\n<td align=\"left\">1.5<\/td>\n<\/tr>\n<tr class=\"even\">\n<td align=\"left\">Cholesky<\/td>\n<td align=\"left\"><code>sla.solve(sym_pos=True)<\/code><br \/><code>cla.posv()<\/code><\/td>\n<td align=\"left\">28<\/td>\n<td align=\"left\">1.5<\/td>\n<\/tr>\n<tr class=\"odd\">\n<td align=\"left\">QR<\/td>\n<td align=\"left\"><code>cla.gels()<\/code><\/td>\n<td align=\"left\">540<\/td>\n<td align=\"left\">8<\/td>\n<\/tr>\n<tr class=\"even\">\n<td align=\"left\">SVD<\/td>\n<td align=\"left\"><code>nla.lstsq()<\/code><\/td>\n<td align=\"left\">540<\/td>\n<td align=\"left\">8<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>Now, if you&#8217;ve read about the Cholesky, QR, and SVD methods as applied to linear regression, you might be a bit concerned. You probably read that in-order they go from fastest to slowest and from least to most numerically stable. We haven&#8217;t talked at all about the stability issue, but we don&#8217;t see any difference between QR and SVD here. We also see that both QR and SVD are making full copies of the <code>X<\/code> matrix. In the case of QR, we saw that explicitly (we made the copy) because <code>cla.gels<\/code> is low enough level (close enough to the LAPACK <code>gels<\/code> call) that it overwrites its inputs.<\/p>\n<p>We&#8217;ll dig into these methods by directly using the LAPACK routines, including a (hopefully) lower overhead <code>gels<\/code> next time.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\">\n<\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<h3>\nAdditional Resources<br \/>\n<\/h3>\n<p>\nYou can grab a <a href=\"http:\/\/drsfenner.org\/public\/notebooks\/ThreePathsToLeastSquares-Overview.ipynb\">copy of this notebook<\/a>.\n<\/p>\n<p>\nEven better, you can <a href=\"http:\/\/nbviewer.ipython.org\/url\/drsfenner.org\/public\/notebooks\/ThreePathsToLeastSquares-Overview.ipynb\">view it using nbviewer<\/a>.\n<\/p>\n<h3>\nLicense<br \/>\n<\/h3>\n<p>\nUnless otherwise noted, the contents of this notebook are under the following license. The code in the notebook should be considered part of the text (i.e., licensed and treated as as follows).\n<\/p>\n<p><a rel=\"license\" href=\"http:\/\/creativecommons.org\/licenses\/by-nc-sa\/4.0\/\"><img decoding=\"async\" alt=\"Creative Commons License\" style=\"border-width:0\" src=\"https:\/\/i.creativecommons.org\/l\/by-nc-sa\/4.0\/88x31.png\" \/><\/a><br \/><span xmlns:dct=\"http:\/\/purl.org\/dc\/terms\/\" property=\"dct:title\">DrsFenner.org Blog And Notebooks<\/span> by <a xmlns:cc=\"http:\/\/creativecommons.org\/ns#\" href=\"drsfenner.org\" property=\"cc:attributionName\" rel=\"cc:attributionURL\">Mark and Barbara Fenner<\/a> is licensed under a <a rel=\"license\" href=\"http:\/\/creativecommons.org\/licenses\/by-nc-sa\/4.0\/\">Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License<\/a>.<br \/>Permissions beyond the scope of this license may be available at <a xmlns:cc=\"http:\/\/creativecommons.org\/ns#\" href=\"drsfenner.org\/blog\/about-and-contacts\" rel=\"cc:morePermissions\">drsfenner.org\/blog\/about-and-contacts<\/a>.<\/p>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>In the prior installment, we talked about solving the basic problem of linear regression using a standard least-squares approach. Along the way, I showed a few methods that gave the same results to the least squares problem. Today, I want to look at three methods for solving linear regression: solving the full normal equations via [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6,7],"tags":[],"class_list":["post-388","post","type-post","status-publish","format-standard","hentry","category-mrdr","category-sci-math-stat-python"],"_links":{"self":[{"href":"https:\/\/drsfenner.org\/blog\/wp-json\/wp\/v2\/posts\/388","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/drsfenner.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/drsfenner.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/drsfenner.org\/blog\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/drsfenner.org\/blog\/wp-json\/wp\/v2\/comments?post=388"}],"version-history":[{"count":2,"href":"https:\/\/drsfenner.org\/blog\/wp-json\/wp\/v2\/posts\/388\/revisions"}],"predecessor-version":[{"id":426,"href":"https:\/\/drsfenner.org\/blog\/wp-json\/wp\/v2\/posts\/388\/revisions\/426"}],"wp:attachment":[{"href":"https:\/\/drsfenner.org\/blog\/wp-json\/wp\/v2\/media?parent=388"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/drsfenner.org\/blog\/wp-json\/wp\/v2\/categories?post=388"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/drsfenner.org\/blog\/wp-json\/wp\/v2\/tags?post=388"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}