├── index.html ├── posts ├── bad-speech-synthesis-made-simple │ ├── bad-speech-synthesis-made-simple.ipynb │ ├── bad-speech-synthesis-made-simple.meta │ ├── index.html │ ├── overlap_add.png │ └── the_more_you_know.jpg ├── cleanup.sh ├── introduction-to-gaussian-processes │ ├── index.html │ ├── introduction-to-gaussian-processes.ipynb │ └── introduction-to-gaussian-processes.meta ├── introduction-to-page-rank │ ├── index.html │ ├── introduction-to-page-rank.ipynb │ └── introduction-to-page-rank.meta ├── linear-regression │ ├── index.html │ ├── linear-regression.ipynb │ └── linear-regression.meta ├── polyphase-signal-processing │ ├── index.html │ ├── polyphase-signal-processing.ipynb │ └── polyphase-signal-processing.meta ├── robust-matrix-decomposition │ ├── index.html │ ├── robust-matrix-decomposition.ipynb │ └── robust-matrix-decomposition.meta ├── single-speaker-word-recognition-with-hidden-markov-models │ ├── index.html │ ├── single-speaker-word-recognition-with-hidden-markov-models.ipynb │ └── single-speaker-word-recognition-with-hidden-markov-models.meta ├── using-pytables-for-larger-than-ram-data-processing │ ├── index.html │ ├── numpy_data_layout.png │ ├── using-pytables-for-larger-than-ram-data-processing.ipynb │ └── using-pytables-for-larger-than-ram-data-processing.meta ├── utils.py └── wavelets │ ├── index.html │ ├── wavelets.ipynb │ └── wavelets.meta └── site_gen.py /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Kyle Kastner's Homepage 6 | 7 |
8 |
9 |

Around the Web

10 |
11 |
You can find me on Twitter at https://twitter.com/kastnerkyle
12 | 13 |

For more code, see my GitHub at https://github.com/kastnerkyle
14 | 15 |

Papers I've written or helped write can be found on Google Scholar
16 |
17 | 18 |
19 | 20 |
21 |

Blog Posts

22 |
23 | 24 |

Bad Speech Synthesis Made Simple
25 | 26 |

Introduction To Gaussian Processes
27 | 28 |

Introduction To Page Rank
29 | 30 |

Linear Regression
31 | 32 |

Polyphase Signal Processing
33 | 34 |

Robust Matrix Decomposition
35 | 36 |

Single Speaker Word Recognition With Hidden Markov Models
37 | 38 |

Using Pytables For Larger Than Ram Data Processing
39 | 40 |

Wavelets
41 | 42 | 43 | -------------------------------------------------------------------------------- /posts/bad-speech-synthesis-made-simple/bad-speech-synthesis-made-simple.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Speech synthesis is a core component of many modern products - turn by turn navigation,\n", 8 | "automatic personal assistants/search like Siri and Alexa, and even the voices of AI/robots/people in computer games and movies are (in some cases) generated by speech synthesis systems. But how are these systems built? How can we make computers reply to our requests (and demands) using speech?\n", 9 | "\n", 10 | "\n", 11 | "Even after years of studying signal processing, machine learning, and computer programming, I still had not found an answer to this question. After searching online for a bit, I stumbled across [Festival](http://www.cstr.ed.ac.uk/projects/festival/), a tool for building speech synthesis systems. Exploring their [online demo](http://www.cstr.ed.ac.uk/projects/festival/morevoices.html) was a lot of fun and gave a nice sense of what is possible and available in the academic community (at least that I could find!).\n", 12 | "\n", 13 | "\n", 14 | "After using and reading about the \"old method\" KAL voice, which is made by diphone/unit selection synthesis, I decided to make my own diphone based unit selection synthesizer based on a few papers: [Diphone Collection and Synthesis, Lenzo and Black](https://www.cs.cmu.edu/~awb/papers/ICSLP2000_diphone.pdf), [Issues in Building General Letter to Sound Rules, Black, Lenzo, Pagel](https://www.cs.cmu.edu/~awb/papers/ESCA98_lts.pdf), and [Letter to Sound Rules For Accented Lexicon Compression, Pagel, Lenzo, Black](https://www.cs.cmu.edu/~awb/papers/ICSLP98-lts.pdf). This seemed to be the simplest possible approach, and though the results are not amazing they are good enough to tell that the system works - which is pretty amazing given its relative simplicity. You can also see an entertaining word and partial word level approach, that seems similar to what is described here at http://talkobamato.me/ . \n", 15 | "\n", 16 | "The papers linked above are quite readable, so be sure to check them out - my own code can be found here https://github.com/kastnerkyle/diphone_synthesizer/ , and I will discuss some of the key pieces below. Here is a demo of the result on some classic Megadeth lyrics." 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 1, 22 | "metadata": { 23 | "collapsed": false 24 | }, 25 | "outputs": [ 26 | { 27 | "data": { 28 | "text/html": [ 29 | "" 30 | ], 31 | "text/plain": [ 32 | "" 33 | ] 34 | }, 35 | "metadata": {}, 36 | "output_type": "display_data" 37 | } 38 | ], 39 | "source": [ 40 | "%%html\n", 41 | "" 42 | ] 43 | }, 44 | { 45 | "cell_type": "markdown", 46 | "metadata": {}, 47 | "source": [ 48 | "Broad Strokes For Bored Folks\n", 49 | "================\n", 50 | "\n", 51 | "There are a few key pieces to diphone unit selection synthesis, or at least the approach I took.\n", 52 | "\n", 53 | "1. Text to phoneme alignment\n", 54 | "2. Phoneme to diphone creation\n", 55 | "3. Audio lookup and stitching\n", 56 | "\n", 57 | "I will cover each of these steps in the following text, but first let's define some reference terms and discuss their meaning.\n", 58 | "\n", 59 | "\n", 60 | "What is a phoneme?\n", 61 | "---------------------\n", 62 | "A phoneme is considered an atomic unit of speech audio. Depending on which phoneset is used, there can be anywhere from 30-50+ phones, not counting inflection, accent and emotional variations. Recording all phonemes, or in our case diphone pairs, should make it possible (in theory, at least) to generate every word if we can manage to go effectively from text to phonemes and do good audio stitching. This approach can work quite well for English speech synthesis, but I think the approach is more difficult for other languages, especially ones with non Romance/Latin origins.\n", 63 | "\n", 64 | "A list of two phonesets can be seen in the code directory, in the files ```cmu_phones.list``` and ```festival_phones.list```. Greater detail about phones and how they are defined can be found in the linked papers above, but for now just consider these to be atoms of speech sound - we will stitch combinations of these together in order to make sound for a given text.\n", 65 | "\n", 66 | "Why not call two phonemes a biphone?\n", 67 | "-----------------------------------------\n", 68 | "I had this exact same question - according to my friend João Felipe Santos it has to do with origin of the word \"phonemes\". The term phoneme comes from the study of [phonetics and phonology](http://www.phon.ox.ac.uk/jcoleman/PHONOLOGY1.htm) which was originally a [Greek term (and study)](https://en.wikipedia.org/wiki/Phonetics). Given that the Greek prefix for 2 is di- , rather the Latin bi- , we end up with the term \"diphone\" instead [(ref)](https://en.wikipedia.org/wiki/Numeral_prefix).\n", 69 | "\n", 70 | "" 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "metadata": {}, 76 | "source": [ 77 | "When All The Planets Align\n", 78 | "==========================\n", 79 | "\n", 80 | "To begin our adventure, we can imagine wanting a tool that takes normal, human understandable words and sentences, and \n", 81 | "converts them into a series of sounds (phonemes) that would make sense. This task is complicated, and has many possible approaches. However, one of the most straightforward approaches is to use something called a pronunciation dictionary to look up which phonemes you need to pronounce each word. \n", 82 | "\n", 83 | "There are a number of pronunciation dictionaries but one of the biggest/most complete dictionaries I have found is used by [CMU Sphinx](http://cmusphinx.sourceforge.net/), an open source speech recognition toolkit. Sidenote - if you are interested in recognition, I have made an \"all-in-one\" wrapper to a light version of Sphinx, called pocketsphinx. You can find that code here https://github.com/kastnerkyle/ez-phones.\n", 84 | "\n", 85 | "Continuing on our path for synthesis, we now have a human crafted lookup table to go from words to sounds. Unfortunately, we almost immediately run into a common problem in sequential modeling - the length of the text and the length of the pronunciation/phoneme sequence are not the same!\n", 86 | "\n", 87 | "This normally requires somewhat complicated machine learning techniques, in order to learn a valid alignment and predict it. However in the papers I listed above (such as [Letter to Sound Rules For Accented Lexicon Compression, Pagel, Lenzo, Black](https://www.cs.cmu.edu/~awb/papers/ICSLP98-lts.pdf)) the authors describe a fairly straightforward dynamic programming algorithm for inputting a new character (\"\\_\") in the predicted sequence which makes the mapping from text to extended phones 1:1. This makes the machine learning portion of this system much simpler.\n", 88 | "\n", 89 | "For example in the original CMU dict we have lines such as:\n", 90 | "```\n", 91 | "BEATNIK B IY T N IH K\n", 92 | "```\n", 93 | "\n", 94 | "After running the t2p script and generating alignments for these sequences, we see word and phoneme sequence pairs like this:\n", 95 | "```\n", 96 | "BEATNIK B IY _ T N IH K\n", 97 | "```\n", 98 | "\n", 99 | "This aligned dictionary file is commited in the repo as ```cmudict.0.7a_SPHINX_40.align```, which was generated from ```cmudict.0.7a_SPHINX_40```. You can also try your own custom dictionary - I included copies of the relevant t2p scripts as well. Processing the alignments for this dictionary took about 10 minutes on my machine (Macbook Air 2011)." 100 | ] 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "metadata": {}, 105 | "source": [ 106 | "Creature Features\n", 107 | "=========\n", 108 | "\n", 109 | "After processing the pronunciation dictionary to get alignments, we have about 138,000 aligned sequence pairs. The original paper trained a decision tree for unit selection using 5 character windows (n-gram character windows, $$n=5$$).\n", 110 | "This results in a feature window like the following:\n", 111 | " \n", 112 | "Original string and phones:\n", 113 | "```\n", 114 | "BEATNIK ['B', 'IY', '_', 'T', 'N', 'IH', 'K']\n", 115 | "```\n", 116 | "\n", 117 | "After padding with 2 \"-\" characters (floor(n / 2))) to accomodate for beginning and end windows:\n", 118 | "```\n", 119 | "--BEATNIK--\n", 120 | "```\n", 121 | "\n", 122 | "After feature extraction, we get the resulting features. Target phone to predict is the last entry, readable version in commas (formatted as features > target) with center character in the $(n = 5$) character window bolded:\n", 123 | "\n", 124 | "```['-', '-', 'B', 'E', 'A', 'B'] (--```***B***```EA > B)```\n", 125 | "\n", 126 | "```['-', 'B', 'E', 'A', 'T', 'IY'] (-B```***E***```AT > IY)```\n", 127 | "\n", 128 | "```['B', 'E', 'A', 'T', 'N', '_'] (BE```***A***```TN > _)```\n", 129 | "\n", 130 | "```['E', 'A', 'T', 'N', 'I', 'T'] (EA```***T***```NI > T)```\n", 131 | "\n", 132 | "```['A', 'T', 'N', 'I', 'K', 'N'] (AT```***N***```IK > N)```\n", 133 | "\n", 134 | "```['T', 'N', 'I', 'K', '-', 'IH'] (TN```***I***```K- > IH)```\n", 135 | "\n", 136 | "```['N', 'I', 'K', '-', '-', 'K'] (NI```***K***```-- > K)```\n", 137 | "\n", 138 | "In general this gives us a nice simple feature set in order to train a classifier modeling $p(y_i\\>|\\>x_{i-n/2} ... x_{i+n/2})$.\n", 139 | "\n", 140 | "After reading the [Pixel RNN paper](http://arxiv.org/abs/1601.06759) and reviewing the [Deep Learning book (Figure 10.4 and section 10.2.1)](http://www.deeplearningbook.org/contents/rnn.html#pf9), I have become interested in trying to train sequential decision models in parallel, using teacher forced (ground truth) data as a proxy for the sequential predictions. This means you can train in parallel, but apply recursively at test time to still get a sequential decision. A fully bidirection approach to this can be seen in [Natural Language Processing From Scratch, Collobert, Weston, Bouttou, Karlen, Kavukcuoglu, Kuksa](http://arxiv.org/abs/1103.0398), but for now we will use a \"left-to-right\" approach since I find it easier to understand and work with.\n", 141 | "\n", 142 | "Given this new fascination I added one extra wrinkle using \"Markov features\" (for lack of a better name) to make features modeling $p(y_i\\>|\\>x_{i-n/2} ... x_{i+n/2}, y_{i-1})$. Due to adding a $y_{i-1}$ feature, the phone sequence to predict was padded in front with one \"SIL\" phoneme at the very start, allong the decision tree to \"learn to start\". After making all the ngram + Markov features, there end up being ~900,000 samples generated from the original 138,000 aligned sequences. After this processing, the final features looked like the below example (remember, last entry is the target to predict). This is our training data for the classification algorithm which will map text to phonemes.\n", 143 | "\n", 144 | "Padded string and padded phones:\n", 145 | "\n", 146 | "```\n", 147 | "--BEATNIK-- ['SIL', 'B', 'IY', '_', 'T', 'N', 'IH', 'K']\n", 148 | "```\n", 149 | "\n", 150 | "Resulting features (target variable is the last entry):\n", 151 | "\n", 152 | "```\n", 153 | "['SIL', '-', '-', 'B', 'E', 'A', 'B']\n", 154 | "['B', '-', 'B', 'E', 'A', 'T', 'IY']\n", 155 | "['IY', 'B', 'E', 'A', 'T', 'N', '_']\n", 156 | "['_', 'E', 'A', 'T', 'N', 'I', 'T']\n", 157 | "['T', 'A', 'T', 'N', 'I', 'K', 'N']\n", 158 | "['N', 'T', 'N', 'I', 'K', '-', 'IH']\n", 159 | "['IH', 'N', 'I', 'K', '-', '-', 'K']\n", 160 | "```\n", 161 | "\n", 162 | "These Markov features seemed to make a decent improvement for 1,000 and 10,000 samples in training, but it is unclear to me if that holds as more data is added, or if the Markov feature is simply acting as a bigger effective character window (since $y_{i-1}$ is based on one extra character backward in time). Either way, it is something I hope to explore further another time.\n", 163 | "\n", 164 | "Decisions, Decisions, Decisions\n", 165 | "===============================\n", 166 | "I will not review the decision tree learning algorithm in depth, but most of my implementation is derived from [Kevin Davenport](http://kldavenport.com/pure-python-decision-trees/) and [Patrick L. Le](http://www.patricklamle.com/Tutorials/Decision%20tree%20python/tuto_decision%20tree.html), who in turn derived from [Programming Collective Intelligence, Toby Segaran](http://shop.oreilly.com/product/9780596529321.do). The primary reason I chose a pure Python approach, rather than using the (much superior) implementation in [Scikit-Learn](http://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html#sklearn.tree.DecisionTreeClassifier) is so that I could serialize the tree once and save it in this repo, not worrying about possible API changes in future versions of sklearn. I was also curious how to serialize Python objects in pure json, so this was a nice experiment for that although jsonpickle seems a more robust and general solution.\n", 167 | "\n", 168 | "At a high level, decision tree learning tries to split the data along every column (in my implementation, by trying each split). Testing the resulting 2 datasets for \"purity\", measured by the entropy of the labels in each half, our goal is to find splits where all the labels are the same in one of the halves. Choosing the candidate split which was the most pure, we recusively apply the same logic in each half until we reach splits with entropy 0, meaning all the labels in this split are the same - sometimes phrased as \"the leaf is pure\". This means that as long as there is valid 1:1 mapping in the feature set (the same set of features don't map to 2 different labels), a deep enough decision tree will be *perfect* on the training set.\n", 169 | "\n", 170 | "Generally this may seem like a bad thing, since it means decision trees are easy to overfit (leading to algorithms like random forests, boosted trees, and so on which try to regularize the power of the tree) but in this context it is actually great - it means that we can put words we want to pronounce correctly in the training set, and the decision tree should be able to perfectly pronounce them. This makes the system easy to modify and extend, not to mention the fact that decision tree rules are easy to visualize (see the previous links) and potentially modify by hand using expert analysis.\n", 171 | "\n", 172 | "Using my own implementation of the decision tree means the algorithm does not scale particularly well (though good implementations are quite scalable). We are only able to train on around 10,000 examples out of the potential ***900,000*** available, and I am sure adding more training data would greatly boost performance. On the other hand, if the results were too good I couldn't title this article \"Bad Speech Synthesis\"." 173 | ] 174 | }, 175 | { 176 | "cell_type": "markdown", 177 | "metadata": {}, 178 | "source": [ 179 | "Diphone Fanatics\n", 180 | "=========\n", 181 | "\n", 182 | "Since the actual waveforms we want to look up in the provided database are diphone based, not phoneme based, we need to stitch the phoneme sequence into a valid set of diphones. This is probably the most custom code in the whole system, so I will walk through it in a bit of detail.\n", 183 | "\n", 184 | "Starting at [line 13](https://github.com/kastnerkyle/diphone_synthesizer/blob/master/diphone_synthesis.py#L13) we read in the aligned dictionary and split into text sequences and phone sequences. Note that we also skip lines for alternatives, denoted blah(2), blah(3) and so on.\n", 185 | "\n", 186 | "Next in [lines 23 to 40](https://github.com/kastnerkyle/diphone_synthesizer/blob/master/diphone_synthesis.py#L23) we read in the two phone lists for Sphinx and Festival, stripping them down to the common subset between each list, placing the results in sorted order with \"SIL\"(Sphinx) and \"pau\" (Festival) at the end.\n", 187 | "\n", 188 | "After this we read in the diphone lookup list from ```kaldiph.est``` - this is the mapping from Festival dphones to actual wavfiles, along with start, middle, and end times for the diphone in question. This will be important when finally stitching the sound together.\n", 189 | "\n", 190 | "The next step is to construct lookups for cmu2radio (since we are using CMU Sphinx and Festival's radio phoneset) as well as the reverse (radio2cmu). Once this is done we construct real pairs (pau-pau, t-t, and so on) as well as \"fake pairs\" - these are described in more detail in the papers, but basically some sequences of \"phone1, \\_, phone2\" can also be looked up directly and have a unique wavfile. I will describe how this is handled later - just know that \"fake\" is referring to things with \"\\_\" in them.\n", 191 | "\n", 192 | "Once we have all these helper lookups and mappings, it is time to start constructing diphones. Broadly, there are a few steps in the process, which can be seen in the [synthesize function](https://github.com/kastnerkyle/diphone_synthesizer/blob/master/diphone_synthesis.py#L122). We step through the list one element at a time, looking ahead to the next element for some rules to try. Note that this logic is handcrafted by me, and there could be one or many bugs in the logic - or even a misunderstanding of how the \"\\_\" character should really function. One of the biggest questions I have is if this \"backward continuation\" in 1a makes sense. To track which real_match phones are not in the database, I also added a print statement of \"No match found? p1 p2\" which can be ignored in general.\n", 193 | "\n", 194 | "1. Is there a \"\\_\" character in the first position?\n", 195 | "\n", 196 | " a. If so, try to continue the second phoneme backwards (real_match(p2, p2))\n", 197 | " \n", 198 | " b. Take a step forward and continue the logic\n", 199 | " \n", 200 | "2. Is there a \"\\_\" character in the second position?\n", 201 | "\n", 202 | " a. Try to make a fake phone with the third phone (if possible), and check for a match\n", 203 | " \n", 204 | " b. If the fake phone didn't work, try to treat the second phone as a continuation (real_match(p1, p1))\n", 205 | " \n", 206 | " c. Take a step forward and continue the logic\n", 207 | " \n", 208 | "3. If there are no \"\\_\" characters at all\n", 209 | "\n", 210 | " a. Try a real match (real_match(p1, p2))\n", 211 | " \n", 212 | " b. Take a step forward and continue the logic\n", 213 | " \n", 214 | " \n", 215 | "For each of these steps, if we find a match we add it to a list of wavfile chunks that will be stitched together. Once all the wav chunks have been gathered, we perform \"overlap-add\" processing in the time domain [line 225](https://github.com/kastnerkyle/diphone_synthesizer/blob/master/diphone_synthesis.py#L225) by adding up the waveforms in the time domain (taken from Wikipedia article on [overlap add](https://en.wikipedia.org/wiki/Overlap%E2%80%93add_method)).\n", 216 | "\n", 217 | "\n", 218 | "\n", 219 | "This is the simplest way to combine all the chunks into one continuous sound, but much better results can be obtained by performing overlap add in the frequency domain, performing frequency smoothing/interpolation, WSOLA (matching the overlap of pieces in time based on autocorrelation), using fancier representations such as vocoder parameters (LPC, STRAIGHT, WORLD), or tons of other tricks. This is just the minimal way to get something working - whole books can be written just on the signal processing used to combine individual sounds into continuous, smooth sounds. But once again if we did it too well, this post couldn't be \"Bad Speech Synthesis\"." 220 | ] 221 | }, 222 | { 223 | "cell_type": "markdown", 224 | "metadata": {}, 225 | "source": [ 226 | "Putting The Pieces Together Again\n", 227 | "==================\n", 228 | "Finally we have all the parts to make bad speech. Because of the limited training, the model pronounces some words wildly incorrectly, and it has a lot of trouble with common short words (ME, THE, YOU). I hard coded a few quality of life replacements into the input processing [such as line 301](https://github.com/kastnerkyle/diphone_synthesizer/blob/master/diphone_synthesis.py#L301), but generally you can rewrite words in a more \"pronouncible\" way to get different sounds. I also added an additional emphasis on the very first and last syllables in [line 124](https://github.com/kastnerkyle/diphone_synthesizer/blob/master/diphone_synthesis.py#L124) by doubling the first and last phones, this generally improved sound quality to my ears but is not really justifiable otherwise.\n", 229 | "\n", 230 | "The final result will look something like this:\n", 231 | "\n", 232 | "python diphone_synthesis.py congratulations this is the final message\n", 233 | "\n", 234 | "```\n", 235 | "Text: CONGRATULATIONS\n", 236 | "Predicted phones: ['K', 'OW', '_', 'NG', 'R', 'AE', 'CH', 'UW', 'L', 'EY', '_', 'SH', 'AH', 'N', 'Z']\n", 237 | "No match found?\n", 238 | "ng ng\n", 239 | "Text: THIS\n", 240 | "Predicted phones: ['_', 'TH', 'IH', 'S']\n", 241 | "Text: IS\n", 242 | "Predicted phones: ['IH', 'S']\n", 243 | "Replacing THE -> THEEE\n", 244 | "Text: THEEE\n", 245 | "Predicted phones: ['_', 'TH', 'IY', 'IY', '_']\n", 246 | "Text: FINAL\n", 247 | "Predicted phones: ['F', 'IH', 'N', 'AH', 'L']\n", 248 | "Text: MESSAGE\n", 249 | "Predicted phones: ['M', 'EH', '_', 'S', 'IH', 'JH', '_']\n", 250 | "```" 251 | ] 252 | }, 253 | { 254 | "cell_type": "code", 255 | "execution_count": 2, 256 | "metadata": { 257 | "collapsed": false 258 | }, 259 | "outputs": [ 260 | { 261 | "data": { 262 | "text/html": [ 263 | "" 264 | ], 265 | "text/plain": [ 266 | "" 267 | ] 268 | }, 269 | "metadata": {}, 270 | "output_type": "display_data" 271 | } 272 | ], 273 | "source": [ 274 | "%%html\n", 275 | "" 276 | ] 277 | }, 278 | { 279 | "cell_type": "markdown", 280 | "metadata": {}, 281 | "source": [ 282 | "Into The Black Horizon\n", 283 | "============\n", 284 | "\n", 285 | "Some drawbacks to the type of system described above can be seen in this [lecture](http://festvox.org/11752/slides/lecture11.pdf). In general there is a sliding scale between composibility and tractable recording size as even word level recordings would require ~100k recorded words to capture the most common English words - not to mention nouns, slang, words derived other languages (foods, local sayings), and so on. However the longer sequences become the less stitching artifacts exist, and the more you are able to capture \"natural\" sound or avoid unnatural dynamics at the stitch boundaries.\n", 286 | "\n", 287 | "Adding prosody features, emotional inflections, and other text preprocessing can be a huge boost, as well as performing true structured prediction on the phonemes (or even diphones) using something like a conditional random field (CRF), hidden Markov model (HMM), or recurrent neural network, instead of the cheap Markov features I used here. Audio representations for more natural splicing of sound, as well as introducing more special case diphones (or even expanding to triphones) are another way to improve these results. There are a huge number of ways to improve these types of systems, and it is a lot of fun to explore if you don't mind hearing weird robot voices once in a while.\n", 288 | "\n", 289 | "kk" 290 | ] 291 | }, 292 | { 293 | "cell_type": "code", 294 | "execution_count": null, 295 | "metadata": { 296 | "collapsed": true 297 | }, 298 | "outputs": [], 299 | "source": [] 300 | } 301 | ], 302 | "metadata": { 303 | "kernelspec": { 304 | "display_name": "Python 2", 305 | "language": "python", 306 | "name": "python2" 307 | }, 308 | "language_info": { 309 | "codemirror_mode": { 310 | "name": "ipython", 311 | "version": 2 312 | }, 313 | "file_extension": ".py", 314 | "mimetype": "text/x-python", 315 | "name": "python", 316 | "nbconvert_exporter": "python", 317 | "pygments_lexer": "ipython2", 318 | "version": "2.7.11" 319 | } 320 | }, 321 | "nbformat": 4, 322 | "nbformat_minor": 0 323 | } 324 | -------------------------------------------------------------------------------- /posts/bad-speech-synthesis-made-simple/bad-speech-synthesis-made-simple.meta: -------------------------------------------------------------------------------- 1 | .. title: Bad Speech Synthesis Made Simple 2 | .. slug: bad-speech-synthesis-made-simple 3 | .. date: 2016-04-12 19:37:41 UTC-04:00 4 | .. tags: mathjax 5 | .. link: 6 | .. description: 7 | .. type: text 8 | -------------------------------------------------------------------------------- /posts/bad-speech-synthesis-made-simple/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |

Bad Speech Synthesis Made Simple

5 |
6 |

Explore the post in your browser using Colab
7 | 8 |

See the pre-rendered post on GitHub
9 | 10 | 11 | -------------------------------------------------------------------------------- /posts/bad-speech-synthesis-made-simple/overlap_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kastnerkyle/kastnerkyle.github.io/4bd3f80d9bb7759bbf910acb45421f5d6c89d679/posts/bad-speech-synthesis-made-simple/overlap_add.png -------------------------------------------------------------------------------- /posts/bad-speech-synthesis-made-simple/the_more_you_know.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kastnerkyle/kastnerkyle.github.io/4bd3f80d9bb7759bbf910acb45421f5d6c89d679/posts/bad-speech-synthesis-made-simple/the_more_you_know.jpg -------------------------------------------------------------------------------- /posts/cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm *.zip 3 | rm *.mat 4 | rm *.hdf5 5 | rm *.webm 6 | rm *.mp4 7 | rm *.csv 8 | rm *.gif 9 | rm *.gz 10 | rm *.tar 11 | rm *.pyc 12 | rm -rf Campus Curtain Bootstrap hall ShoppingMall WaterSurface Fountain Escalator 13 | rm -rf an4 audio pages 14 | rm -rf .ropeproject 15 | rm -rf __pycache__ 16 | -------------------------------------------------------------------------------- /posts/introduction-to-gaussian-processes/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |

Introduction To Gaussian Processes

5 |
6 |

Explore the post in your browser using Colab
7 | 8 |

See the pre-rendered post on GitHub
9 | 10 | 11 | -------------------------------------------------------------------------------- /posts/introduction-to-gaussian-processes/introduction-to-gaussian-processes.meta: -------------------------------------------------------------------------------- 1 | .. title: Introduction to Gaussian Processes 2 | .. slug: introduction-to-gaussian-processes 3 | .. date: 2014-07-31 21:01:41 UTC-04:00 4 | .. tags: mathjax 5 | .. link: 6 | .. description: 7 | .. type: text 8 | -------------------------------------------------------------------------------- /posts/introduction-to-page-rank/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |

Introduction To Page Rank

5 |
6 |

Explore the post in your browser using Colab
7 | 8 |

See the pre-rendered post on GitHub
9 | 10 | 11 | -------------------------------------------------------------------------------- /posts/introduction-to-page-rank/introduction-to-page-rank.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Around 25 minutes into [this lecture](https://www.youtube.com/watch?v=60fP4WJ4oxE), there is some good discussion of the PageRank algorithm. I have always wanted to code up a basic version of this algorithm, so this is a great excuse. This algorithm is probably one of the cleanest examples of Markov Chains that I have seen, and obviously its application was quite successful.\n", 8 | "" 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": 1, 14 | "metadata": { 15 | "collapsed": false 16 | }, 17 | "outputs": [], 18 | "source": [ 19 | "import numpy as np\n", 20 | "import matplotlib.pyplot as plt\n", 21 | "%matplotlib inline" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 2, 27 | "metadata": { 28 | "collapsed": false 29 | }, 30 | "outputs": [ 31 | { 32 | "name": "stdout", 33 | "output_type": "stream", 34 | "text": [ 35 | "pages.zip already downloaded!\n" 36 | ] 37 | } 38 | ], 39 | "source": [ 40 | "from utils import progress_bar_downloader\n", 41 | "import os\n", 42 | "\n", 43 | "pages_link = 'http://www.cs.ubc.ca/~nando/340-2009/lectures/pages.zip'\n", 44 | "dlname = 'pages.zip'\n", 45 | "#This will unzip into a directory called pages\n", 46 | "if not os.path.exists('./%s' % dlname):\n", 47 | " progress_bar_downloader(pages_link, dlname)\n", 48 | " os.system('unzip %s' % dlname)\n", 49 | "else:\n", 50 | " print('%s already downloaded!' % dlname)" 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "metadata": {}, 56 | "source": [ 57 | "Building the Matrix\n", 58 | "-------------------\n", 59 | "\n", 60 | "To build the link matrix (basically an adjacency matrix for web pages), we need to look at the links referenced by every single page. For every page referenced by a page, we will add a 1 to the associated column. Adding a small term `eps` to all entries, in order guarantee the matrix is fully connected, we will then have a stochastic matrix which is suitable for Markov chain simulations! " 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": 3, 66 | "metadata": { 67 | "collapsed": false 68 | }, 69 | "outputs": [], 70 | "source": [ 71 | "#Quick and dirty link parsing as per http://www.cs.ubc.ca/~nando/540b-2011/lectures/book540.pdf\n", 72 | "links = {}\n", 73 | "for fname in os.listdir(dlname[:-4]):\n", 74 | " links[fname] = []\n", 75 | " f = open(dlname[:-4] + '/' + fname)\n", 76 | " for line in f.readlines():\n", 77 | " while True:\n", 78 | " p = line.partition('')\n", 82 | " links[fname].append(url)\n", 83 | " f.close()" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": 4, 89 | "metadata": { 90 | "collapsed": false 91 | }, 92 | "outputs": [], 93 | "source": [ 94 | "import numpy as np\n", 95 | "import matplotlib.pyplot as plt\n", 96 | "num_pages = len(links.keys())\n", 97 | "G = np.zeros((num_pages, num_pages))\n", 98 | "\n", 99 | "#Assign identity numbers to each page, along with a reverse lookup\n", 100 | "idx = {}\n", 101 | "lookup = {}\n", 102 | "for n,k in enumerate(sorted(links.keys())):\n", 103 | " idx[k] = n\n", 104 | " lookup[n] = k\n", 105 | "\n", 106 | "#Go through all keys, and add a 1 for each link to another page\n", 107 | "for k in links.keys():\n", 108 | " v = links[k]\n", 109 | " for e in v:\n", 110 | " G[idx[k],idx[e]] = 1\n", 111 | "\n", 112 | "#Add a small value (epsilon) to ensure a fully connected graph\n", 113 | "eps = 1. / num_pages\n", 114 | "G += eps * np.ones((num_pages, num_pages))\n", 115 | "G = G / np.sum(G, axis=1)" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": 5, 121 | "metadata": { 122 | "collapsed": false 123 | }, 124 | "outputs": [ 125 | { 126 | "data": { 127 | "text/plain": [ 128 | "" 129 | ] 130 | }, 131 | "execution_count": 5, 132 | "metadata": {}, 133 | "output_type": "execute_result" 134 | }, 135 | { 136 | "data": { 137 | "image/png": "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\nYII=\n", 138 | "text/plain": [ 139 | "" 140 | ] 141 | }, 142 | "metadata": {}, 143 | "output_type": "display_data" 144 | } 145 | ], 146 | "source": [ 147 | "#Now we run the Markov Chain until it converges from random initialization\n", 148 | "init = np.random.rand(1, num_pages)\n", 149 | "init = init / np.sum(init)\n", 150 | "probs = [init]\n", 151 | "p = init\n", 152 | "for i in range(100):\n", 153 | " p = np.dot(p, G)\n", 154 | " probs.append(p)\n", 155 | "\n", 156 | "for i in range(num_pages):\n", 157 | " plt.plot([step[0, i] for step in probs], label=lookup[i], lw=2)\n", 158 | " \n", 159 | "plt.legend()" 160 | ] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "metadata": {}, 165 | "source": [ 166 | "Turn the Beat Around\n", 167 | "--------------------\n", 168 | "\n", 169 | "Now that the PageRank for each page is calculated, how can we actually perform a search?\n", 170 | "\n", 171 | "We simply need to create an index of every word in a page. When we search for words, we will then sort the output by the PageRank of those pages, thus ordering the links by the *importance* we associated with that page." 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": 6, 177 | "metadata": { 178 | "collapsed": false 179 | }, 180 | "outputs": [], 181 | "source": [ 182 | "search = {}\n", 183 | "for fname in os.listdir(dlname[:-4]):\n", 184 | " f = open(dlname[:-4] + '/' + fname)\n", 185 | " for line in f.readlines():\n", 186 | " #Ignore header lines\n", 187 | " if '<' in line or '>' in line:\n", 188 | " continue\n", 189 | " words = line.strip().split(' ')\n", 190 | " words = filter(lambda x: x != '', words)\n", 191 | " #Remove references like [1], [2]\n", 192 | " words = filter(lambda x: not ('[' in x or ']' in x), words)\n", 193 | "\n", 194 | " for word in words:\n", 195 | " if word in search:\n", 196 | " if fname in search[word]:\n", 197 | " search[word][fname] += 1\n", 198 | " else:\n", 199 | " search[word][fname] = 1\n", 200 | " else:\n", 201 | " search[word] = {fname: 1}\n", 202 | " f.close()" 203 | ] 204 | }, 205 | { 206 | "cell_type": "markdown", 207 | "metadata": {}, 208 | "source": [ 209 | "Ranking The Results\n", 210 | "-------------------\n", 211 | "\n", 212 | "With words indexed, we can now complete the task. Searching for a particular word (in this case, 'film'), we get back all the pages with references and counts. Sorting these so that the highest pagerank comes first, we see the ***Googley***(TM) result for our tiny web." 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": 7, 218 | "metadata": { 219 | "collapsed": false 220 | }, 221 | "outputs": [ 222 | { 223 | "name": "stdout", 224 | "output_type": "stream", 225 | "text": [ 226 | "['martinscorcese.html', 'jenniferaniston.html', 'bradpitt.html', 'jonvoight.html']\n" 227 | ] 228 | } 229 | ], 230 | "source": [ 231 | "def get_pr(fname):\n", 232 | " return probs[-1][0, idx[fname]]\n", 233 | "\n", 234 | "r = search['film']\n", 235 | "print(sorted(r, reverse=True, key=get_pr))" 236 | ] 237 | }, 238 | { 239 | "cell_type": "markdown", 240 | "metadata": {}, 241 | "source": [ 242 | "This is a neat application of Markov chains and a great learning experience. Though this notebook did not touch on the eigendecomposition approaches and features of PageRank, it is most definitely worth looking into - check out the paper [The $25,000,000,000 Dollar Eigenvector](http://www.rose-hulman.edu/~bryan/googleFinalVersionFixed.pdf).\n", 243 | "\n", 244 | "kk" 245 | ] 246 | } 247 | ], 248 | "metadata": { 249 | "kernelspec": { 250 | "display_name": "Python 2", 251 | "language": "python", 252 | "name": "python2" 253 | }, 254 | "language_info": { 255 | "codemirror_mode": { 256 | "name": "ipython", 257 | "version": 2 258 | }, 259 | "file_extension": ".py", 260 | "mimetype": "text/x-python", 261 | "name": "python", 262 | "nbconvert_exporter": "python", 263 | "pygments_lexer": "ipython2", 264 | "version": "2.7.11" 265 | } 266 | }, 267 | "nbformat": 4, 268 | "nbformat_minor": 0 269 | } 270 | -------------------------------------------------------------------------------- /posts/introduction-to-page-rank/introduction-to-page-rank.meta: -------------------------------------------------------------------------------- 1 | .. title: Introduction to Page Rank 2 | .. slug: introduction-to-page-rank 3 | .. date: 2014-04-16 01:56:08 UTC-04:00 4 | .. tags: 5 | .. link: 6 | .. description: 7 | .. type: text 8 | -------------------------------------------------------------------------------- /posts/linear-regression/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |

Linear Regression

5 |
6 |

Explore the post in your browser using Colab
7 | 8 |

See the pre-rendered post on GitHub
9 | 10 | 11 | -------------------------------------------------------------------------------- /posts/linear-regression/linear-regression.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "When presented with an unknown dataset, it is very common to attempt to find trends or patterns.\n", 8 | "The most basic form of this is visual inspection - how is the data trending? Does it repeat in cycles? \n", 9 | "Can we predict future data given some past events? The mathematical approach to this \"trend finding\" is called regression.\n", 10 | "" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "First, the import statements." 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 1, 23 | "metadata": { 24 | "collapsed": false 25 | }, 26 | "outputs": [], 27 | "source": [ 28 | "import numpy as np \n", 29 | "import matplotlib.pyplot as plt\n", 30 | "%matplotlib inline" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "For this example problem, we will need some data. One of the best tools I have found for generating datasets is the sklearn.datasets toolbox, which features built-in datasets, dataset downloaders, and tools for generating datasets. In this case, I will use the make_regression function to generate 1000 samples, with noise and bias." 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": 2, 43 | "metadata": { 44 | "collapsed": false 45 | }, 46 | "outputs": [ 47 | { 48 | "data": { 49 | "image/png": "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\n0AL+H4EY1SmmV7tTAAAAAElFTkSuQmCC\n", 50 | "text/plain": [ 51 | "" 52 | ] 53 | }, 54 | "metadata": {}, 55 | "output_type": "display_data" 56 | } 57 | ], 58 | "source": [ 59 | "from sklearn import datasets \n", 60 | "X, y = datasets.make_regression(n_samples=1000,n_features=1,\n", 61 | " n_informative=1,noise=15,\n", 62 | " bias=1000,random_state=0)\n", 63 | "\n", 64 | "_ = plt.scatter(X, y, color='darkred', marker='o', edgecolors='none')\n", 65 | "_ = plt.title('Sample dataset')" 66 | ] 67 | }, 68 | { 69 | "cell_type": "markdown", 70 | "metadata": {}, 71 | "source": [ 72 | "As we can see, this data is distributed in a fairly linear fashion. But how can we discover the underlying mathematical function, without human intervention? One simple method for this is [linear regression](http://cs229.stanford.edu/notes/cs229-notes1.pdf). In this case, we model Y with a function: \n", 73 | "\n", 74 | "$$Y = \\theta_0 + \\theta_1 x_1$$\n", 75 | "\n" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": 3, 81 | "metadata": { 82 | "collapsed": false 83 | }, 84 | "outputs": [], 85 | "source": [ 86 | "#Add bias terms, so that the first column of X is all ones.\n", 87 | "#This should make the data match the model.\n", 88 | "X = np.hstack((np.ones((X.shape[0], 1)), X))\n", 89 | "\n", 90 | "#Make y into a 2D array to match X\n", 91 | "y = y[:, np.newaxis]" 92 | ] 93 | }, 94 | { 95 | "cell_type": "markdown", 96 | "metadata": {}, 97 | "source": [ 98 | "To fit the generic function described above to our particular dataset, we need to find values for theta that minimize some cost function, such as [mean squared error](http://people.missouristate.edu/songfengzheng/Teaching/MTH541/Lecture%20notes/evaluation.pdf).\n", 99 | "\n", 100 | "An optimization technique known as [gradient descent](http://stat.columbia.edu/~porbanz/teaching/W4400/slides_lecture6.pdf) can be used in order to find values for theta. The easiest way to do this is to define a cost, the gradient of the cost with respect to each theta, and an update rule. " 101 | ] 102 | }, 103 | { 104 | "cell_type": "markdown", 105 | "metadata": {}, 106 | "source": [ 107 | "Math. Not Even Once.\n", 108 | "--------------------" 109 | ] 110 | }, 111 | { 112 | "cell_type": "markdown", 113 | "metadata": {}, 114 | "source": [ 115 | "To perform optimization by gradient descent, we will need the following formulas. In all of these formulas, h(x) is the prediction of x by the linear regressor.\n", 116 | "\n", 117 | "Cost (m is the length of X):\n", 118 | "\n", 119 | "$$\\frac{1}{2m}\\sum\\limits_{i=1}^{m}(h_\\theta(x^i)-y^i)^2$$\n", 120 | "\n", 121 | "\n", 122 | "Gradient of cost with respect to theta (m is the length of X):\n", 123 | "\n", 124 | "$$\\frac{1}{m}(h_\\theta(x)-y)$$\n", 125 | "\n", 126 | "\n", 127 | "Update rule (for every j in X, m is the length of X):\n", 128 | "\n", 129 | "$$\\theta_j = \\theta_j + \\alpha\\sum\\limits_{i=1}^{m}(y^i - h_\\theta(x^i))x^i_j$$" 130 | ] 131 | }, 132 | { 133 | "cell_type": "markdown", 134 | "metadata": {}, 135 | "source": [ 136 | "The Thicke of It\n", 137 | "----------------" 138 | ] 139 | }, 140 | { 141 | "cell_type": "markdown", 142 | "metadata": {}, 143 | "source": [ 144 | "Let's define the previous statements in code, and perform gradient regression. The linear_cost and linear_cost_grad functions look slightly different - this is because the code versions are written in matrix form, but are otherwise equivalent to the versions defined above." 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": 4, 150 | "metadata": { 151 | "collapsed": false 152 | }, 153 | "outputs": [], 154 | "source": [ 155 | "#Initialize theta to zeros \n", 156 | "learning_rate = alpha = 0.01 \n", 157 | "iters = 1000 \n", 158 | "\n", 159 | "def linear_cost(theta,X,y): \n", 160 | " m = y.shape[0] \n", 161 | " return 1. / (2. * m) * np.sum((np.dot(X, theta) - y) ** 2.) \n", 162 | " \n", 163 | "def linear_cost_grad(theta,X,y): \n", 164 | " m = y.shape[0] \n", 165 | " return 1. / m * np.dot(X.T, (y - np.dot(X,theta))) \n", 166 | " \n", 167 | "def gradient_descent(X,y,alpha,iters): \n", 168 | " m = y.shape[0] \n", 169 | " all_cost = []\n", 170 | " #Initialize theta to zeros\n", 171 | " all_theta = [np.zeros((X.shape[1],1))]\n", 172 | " for i in range(iters): \n", 173 | " all_cost.append(linear_cost(all_theta[-1], X, y))\n", 174 | " all_theta.append(all_theta[-1] + float(alpha) * linear_cost_grad(all_theta[-1], X, y)) \n", 175 | " return all_theta,all_cost \n", 176 | " \n", 177 | "#Perform linear regression via gradient descent \n", 178 | "all_theta, all_cost = gradient_descent(X, y, alpha, iters)" 179 | ] 180 | }, 181 | { 182 | "cell_type": "markdown", 183 | "metadata": {}, 184 | "source": [ 185 | "Are We There Yet?\n", 186 | "-----------------" 187 | ] 188 | }, 189 | { 190 | "cell_type": "markdown", 191 | "metadata": {}, 192 | "source": [ 193 | "With code written, how can we tell that it is functioning correctly? One way is the look at the cost over each iteration of the gradient descent algorithm. If this value is decreasing as iterations increase, the values of theta are approaching their optimimum values, as defined by minimizing the cost function. This type of optimization is known as [convex optimization](http://www.stanford.edu/~boyd/cvxbook/bv_cvxbook.pdf), which is an entire field of study to its own. Gradient regression, however, doesn't require much study. \n", 194 | "\n", 195 | "We have all seen gradient descent in action - water always flows downhill! At the top of the hill, the water begins to trickle downward along the slope of the hill, always following the path of steepest descent. To match the analogy, at each point, we calculate the cost and the gradient of the cost, trending down slope along the miniumum gradient. Since we computer users cannot use continuous methods, like mother nature can, we take steps, calculating the cost and cost gradient each time. This step size is set by a parameter called the learning_rate.\n", 196 | "\n", 197 | "More advanced methods of optimization attempt to avoid local minima, using additional parameters like momentum, or different optimization methods which look at different factors, such as gradients-of-gradients. An example of local minima would be a lake on top of a mountain. The lake is the lowest nearby point when compared to the peak, but the bottom of the mountain is much lower than the lake itself. We want to reach the bottom of the mountain, rather than being stuck in the lake. " 198 | ] 199 | }, 200 | { 201 | "cell_type": "markdown", 202 | "metadata": {}, 203 | "source": [ 204 | "Now that funtime is over, let's look at a plot of cost vs. number of iterations. We can see this plot decrease, asymptotically approaching zero. The differences between stopping at iteration 400 and stopping at iteration 1000 are fairly minimal, and many algorithms use minimal decrease settings to stop iterating when the cost stops decreasing noticeably." 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": 5, 210 | "metadata": { 211 | "collapsed": false 212 | }, 213 | "outputs": [ 214 | { 215 | "data": { 216 | "text/plain": [ 217 | "" 218 | ] 219 | }, 220 | "execution_count": 5, 221 | "metadata": {}, 222 | "output_type": "execute_result" 223 | }, 224 | { 225 | "data": { 226 | "image/png": "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\n", 227 | "text/plain": [ 228 | "" 229 | ] 230 | }, 231 | "metadata": {}, 232 | "output_type": "display_data" 233 | } 234 | ], 235 | "source": [ 236 | "plt.plot(all_cost, color='steelblue')\n", 237 | "plt.title('Cost over iterations')" 238 | ] 239 | }, 240 | { 241 | "cell_type": "markdown", 242 | "metadata": {}, 243 | "source": [ 244 | "There are some other ways to visualize the information seen above, such as a 2D contour plot with an overlay of the gradient path. To plot both of this, we must calculate a cost mesh across a combination of (theta0, theta1) values." 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": 6, 250 | "metadata": { 251 | "collapsed": false 252 | }, 253 | "outputs": [], 254 | "source": [ 255 | "theta0_vals = np.linspace(-1200, 1200, 100)\n", 256 | "theta1_vals = np.linspace(-150, 150, 100)\n", 257 | "contours = np.array([linear_cost([theta0,theta1],X,y)\n", 258 | " for theta0 in theta0_vals \n", 259 | " for theta1 in theta1_vals]).reshape((theta0_vals.shape[0],\n", 260 | " theta1_vals.shape[0]))" 261 | ] 262 | }, 263 | { 264 | "cell_type": "markdown", 265 | "metadata": {}, 266 | "source": [ 267 | "The 2D plot includes gradient descent - what path was taken to arrive at the local minima? " 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": 7, 273 | "metadata": { 274 | "collapsed": false 275 | }, 276 | "outputs": [ 277 | { 278 | "data": { 279 | "text/plain": [ 280 | "" 281 | ] 282 | }, 283 | "execution_count": 7, 284 | "metadata": {}, 285 | "output_type": "execute_result" 286 | }, 287 | { 288 | "data": { 289 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAZAAAAEZCAYAAAC5AHPcAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJztnXu4HWV5t+9fDjshCYaEQIAkElAQolTC0WIpsa0o6ifa\nqnhAQVFRaqHWKmitxAMK+Gk99JP6KSBYRbAKBRFEKxEUIXIMp4hAokkgiZBwNtl7Zz/9Y2Ylsxd7\n7b3WrHnntJ77uva1Z82awzuzZt57nvc0MjMcx3Ecp1PGFZ0Ax3Ecp5q4QBzHcZxUuEAcx3GcVLhA\nHMdxnFS4QBzHcZxUuEAcx3GcVLhAnFRIGpK0Z6BtL5K0KsS2m/YzW9J1kp6Q9PnQ+8uC5HmXdI6k\njxeUjuMlXV/Evp3y4ALpISStlPSMpCclrZV0vqSpbay3RNIJXex3saRvp10/IO8F1pvZc8zsw0Un\nplPM7P1m9plut5OXsPOi2+vVaR8XSG9hwGvMbHvgAOAgoJ0n2Lr2Nt0duDfNipImdLNjxXSzDacl\ndb1eS4cLpEcxs4eAq4EXSdpB0o8krZe0QdIVkuYASDoDOBz49zhy+UpiMy+XdJ+kjZL+faT9SHol\n8FHgmHj92+L575R0T1x89ICk97ZKq6STJd0taTdJkyT9X0m/j6OocyRNjpdbJGm1pH+StE7SQ5KO\nb7HNbwHvAD4Sp+uvJPVJ+pKkNfHfv0nqa9r2RyQ9DJw7wjbHSfqCpD9KelDSB+Iip3Hx90skfUbS\nr4CngT3HOg+SPhwfx2pJ72o+BkmfTnx+jaTb49/jV5L2S3y3UtKHJN0h6TFJ34vP5VTgKmC3+Dw8\nIWmXEY5tR0mXS3pc0k3A85q+30fSTyU9Kmm5pDcmvntV/Ps9ER/HhxLfHR2n+XFJ90t6RTx/uqRz\nE8f+6cR5PF7SLyV9Pr5eH4yvs7GuVydrzMz/euQPWAH8dTw9D7gL+CQwE3g9MBmYBlwCXJpY71rg\nXU3bGgIuB54Tb2s98IoW+z0duLBp3quAPeLpvyTKUBfGnxcBq+LpTwA3AzvGn/8NuAzYIU7r5cBn\nE+sNAIuB8cBR8Xant0jX+cCnEp8/BdwAzIr/ftX4PrHtzwETgckjbO99wN3AbnH6fgZsAcbF3y8B\nVgL7Ej28TRjjPLwSWAssAKYA343P+57N6QcWAuuAgwERyXEFMDHx298I7ALMAO4BToy/O6Jxvke5\ndr4X/20HvBBYDVwXfzcVWAUcFx/X/sAfgX3i7x8GXhpPT08c3yHAY2y7JncDXhBPXwqcE+9vJ+Am\n4L3xd8cD/cAJ8bG+D1gz2vXqf4HylKIT4H85/thR5vUksDGe/ndg0gjL7Q9sSHy+FjihaZkh4LDE\n54uBU1vsdzHw7THSdilwcjy9KM6gvghcB2wfzxfwVCMDjef9OfBgYr1niDPseN464JAW+zwf+HTi\n8/3AKxOfjwRWJLa9Gegb5Rh+Drwn8fmv4/PUEMi1wOIOzsN5xHKMP+9Fa4GcQ0KG8bzlwOHx9Arg\nrYnvzgLOSRxbS4EQybgf2Dsx7wzg+nj6GGKZJL7/OvCJePr3RPVNzxlhmS+MsL/ZwCYSkgbeAvw8\nnj4e+F3iuynxedm51fXqf2H+uirHdSqHAUeb2c+TMyVNIXqyfwXR0ynANEmy+I5k5HLltYnpZ4gi\ngraQdBRRZLIX0VPrFGBZYpEdgHcDbzazJ+N5O8XL3ZKoPhDDi2IfNbOhlOnajSiza/CHeF6DP5pZ\n/yjr70r0JN5g9QjLDKusHuM87Ar8pik9rdgdeIekf0jMm9iU/uTv9aem70ZjJ6JoKZn2ZFp2Bw6V\ntDExbwJwYTz9d0R1bWdKWgacZmY3AnOBK1scy0Tg4cTvPK5pn1uPxcyeiZebRhQJg9eD5ILXgTgA\nHwL2JnpSn05UpKH4D7q/GZMZOpImAT8AziZ6apwB/DixP4iipNcA50s6LJ73CFHGt8DMZsR/O5jZ\nc7pMX4OHgPmJz8+N5zUY6zw8TFSc12DeCMts3UYb5+HhOA3J9LTiD8AZifMyw8ymmdnFY6R5WJpa\n8EdgcJS0/AH4RdO+tzezvwcws5vN7HVEIrqMqIgUIiE9f4T9rSKK9nZMbG+6me03wrJpjsfJCBeI\nA9GT25+AxyXNJHoiTrKOpkrTERitRdE6YL62PU72xX+PAEPxU/iRzSuZ2XXA24AfSjo4jiy+AXxJ\n0k4AkuZIeta6bdKc5ouAj0uaJWkWUf1LJ82PLwFOiSv7dwBO5dmZWXKfY52HS4DjJe0bR4nNv0tS\n8t8A3ifpEEVMlfRqSe1EX+uAHSWNKGIz2wL8EFgsaTtJC4jqOxrHdiWwt6RjJU2M/w6OK9YnSnqb\npOnxdp4kqheCqCHCOxU1YBgX/5YvMLOHgWuAL0raPv7ueZL+so1jaRzPWNerkwEuEAfgS0SVlY8Q\nVSJfxfCM78vAG+IWL19qsQ2j9ZPf9+P/j0q6OS6SOpkog9xAVL793yNsDzP7GfAu4ApJ+xNlyvcD\nN0p6HPgpUfQ0bL02aU7zZ4gq7JfFfzfH89rd9jeIMr5lwC1EGeuWpiK1rdsY6zyY2dVEv83PgfuA\n/2lKw9b0m9ktwHuI6rU2AL8jqkhvlebkusuJ5Plg/Bs/qxUW8AGiB421RHUz5zUdx5HAm4E1RJHT\n54jkCHAssCL+vd5L9FCAmf0GeCdR8eljRI0MGpHNO+L174mP5/tEDQCGpb3peBq0c706GaBtRdwZ\nb1g6D3g1UUet/eJ5bySqUN0HONjMbo3nzydqj788Xv3XZnZS/N2BwLeIWgj92MxOCZJgx8mYOKI4\nx8zmF50WxwlByAjkfKJmiEnuJGouet0Iy99vZgvjv5MS888halGxF7BXo72345QNSZPjPg8TFPWj\nOZ2o6MdxakkwgZjZ9UQVocl5y83svna3IWlXoiacS+NZFwKvyy6VjpMpIoqwNwC3EvUJ+USRCXKc\nkJSpGe8einopPw583Mx+CcxheFPINfE8xykdZvYnos5xjtMTlEUgDwHzzGyjpAOAyyS9sOhEOY7j\nOK0phUDizln98fStkh4g6li1hqizUYO58bxnIcnbfjuO46TAzFIN7FmkQLZ1JY7a3G80sy2K3nWw\nF9HwFI/FA7AdCiwF3g60HBwt7UmoG5IWm9niotNRBvxcbMPPxTb8XGyjm4fvYAKRdBFRj+ZZit41\ncDpR5eJXiQaqu1LSbWZ2VLzcJyUNEPVaPtHMHos3dRJRM97tiJrxXh0qzY7jOE77BBOImb2lxVeX\njbDsD4iGdBhpO7cA7Q5h4DiO4+SE90SvJ0uKTkCJWFJ0AkrEkqITUCKWFJ2AOhCsJ3rexAPHeh2I\n4zhOB3STd3oE4jiO46TCBeI4juOkwgXiOI7jpMIF4jiO46TCBeI4juOkwgXiOI7jpMIF4jiO46TC\nBeI4juOkwgXiOI7jpMIF4jiO46TCBeI4juOkwgXiOI7jpMIF4jiO46TCBeI4juOkwgXiOI7jpMIF\n4jiO46TCBeI4juOkwgXiOI7jpMIF4jiO46TCBeI4juOkwgXiOI7jpMIF4jiO46RiQtEJcJy6c9Xc\no6zoNAActfoqFZ0Gp17IrBTXdtdIMjPzG8QJSllkkAcunN6gm7zTBeI4Mb0kh6xwyVQfFwguEKc9\nXBL54XKpBqUUiKTzgFcD681sv3jeG4HFwD7AwWZ2a2L5jwLvArYAJ5vZNfH8A4FvAZOBH5vZKS32\n5wJxtuKiKC8ulnJRVoEcDjwFXJgQyD7AEPB14EMNgUhaAHwXOBiYA/wM2MvMTNJS4ANmtlTSj4Gv\nmNnVI+zPBdKDuCjqgUulOLrJO4O1wjKz6yXNb5q3HEB6VlqPBi4yswFgpaT7gUMl/R7Y3syWxstd\nCLwOeJZAnN7AhVFPRvpdXSrlpyzNeHcDbkx8Xk0UiQzE0w3WxPOdHqFqwthn4fiik9AWy2/bUnQS\nxiT527tMyklZBOI4QLmFURU5tEM7x1ImyTRfFy6UclAWgawB5iU+zyWKPNbE08n5a1ptRNLixMcl\nZrYkuyQ6oSiTNOokiW4Z7VwULRePTtIjaRGwKJNthWzGG9eBXNGoRE/Mvxb4ZzO7Jf7cqEQ/hG2V\n6M+PK9FvAk4GlgJX4pXotaBoabgowlC0WFwmnVPWVlgXAUcAs4B1wOnABuCr8bzHgdvM7Kh4+Y8R\nNeMdBE4xs5/E8xvNeLcjasZ7cov9uUBKTlHScFkUS1FScZm0RykFkjcukHJShDRcGOWmCKG4TFrj\nAsEFUibylEZVZDF77/5C9rvuvr5C9tsJeQvFZTIcFwgukKLJSxplEkZRUghFWWSTl1BcJBEuEFwg\nRZCHNIoURt0E0Q1FyiUPofSyTFwguEDyJLQ48pSGS6J78pRLaJn0okhcILhAQhNSGnkJw2WRH3lI\nxWWSDS4QXCAhqLI0XBblI7RUQgml7iJxgeACyZJQ4ggpjbIKo2/h7KKTQP9t64pOwoiEFEoImdRV\nJC4QXCBZEEIcIaRRtCzKIIUQFC2aEELxqGRsXCC4QNLi0ng2dRVEt+QtmKyF4lHJyLhAcIF0Stbi\nyFoaeQjDRZENeYil7DKpskhcILhA2qWs4ggtDJdFvoSUSpYycZG4QAAXyFhkKY6yS6Psshi35y5B\ntjv04Nog282KUFLJSihZyqRKInGB4AJpRdnEEUIaRQsjlBBCU6RwQsikbCKpikRcILhAmqm7OPKW\nRlUlkYa8xZK1TFwkneECwQWSJCt5lE0ceUmjl2TRLnlJJUuZZCGSXpCICwQXCJRHHFWShssiPaGl\nUiaZ1FkkLhBcIFnIoyziCCkNF0Y4QgmlbiIpm0RcIPSuQFwco1NlYWjeHABs1ZqCU5KOEELJQiYu\nkeG4QOhNgRQtjyzEUUdpNDL+MlG0hLKWiYskO1wg9JZAXBzDyVsYZRREN+Qtl7LJpBuR1EEiLhB6\nRyDdyqNIcVRNGnUTRafkIZYsZeIiSYcLhPoLxMURVhq9Lot2CSmVrGRSZZEUIREXCPUWSFHyKIM4\nQknDhZENoYSShUy6EUkvScQFQj0FUtWoo1txhJBGVYShGfNGnG8bV+WcknRkLZSqiqRKEnGBUD+B\nVC3qKFu0UZQwWgmgSIqUT5ZC6VYmLpKRcYFQH4H0mjiykkZewiijILolL8FkJZOiRFJXibhAqIdA\nupFHL4ojtDTqKItOCCmWLGTSSyIJKREXCNUWSBFRRxHiKLM0el0W7RBKKEXLJE+RlFEiLhCqK5Cq\nRB1FiiNrabgssiNrqXQrk7QiqYpEIHuRlFIgks4DXg2sN7P94nkzgYuB3YGVwJvM7DFJ84F7geXx\n6r82s5PidQ4EvgVMBn5sZqe02F/lBJK3PKokjiylUaQwhqZOz3V/455+PNf9NZOVUFwko5OlRMoq\nkMOBp4ALEwI5G3jEzM6WdCoww8xOiwVyRWO5pu0sBT5gZksl/Rj4ipldPcJylRJInvLIu7iqDOLI\nSxp5C6Jb8hRMFjKpkkjylEjtBQLQLAZJy4EjzGydpF2AJWa2TyuBSNoV+LmZ7Rt/fjOwyMzeN8K+\nKiOQusqjF8RRNWG0Q2ipFC0Sl8jodJN3jssiAR0w28wav8o6IJlL7SHpNklLJP1FPG8OsDqxzJp4\nXmUpuzz6Fs6unDw0Y97Wv6wZmjp92F8dCX2MWfw2mjcn9XWS9tpMcx+kuefS1mVm+drqtEwoasdm\nZpIaJ+AhYJ6ZbZR0AHCZpBd2uk1JixMfl5jZku5Tmh1VkEca0t6gWYgja+oqiU5InoMso5PG79VN\nRKJ5c1JFI+P23CVVJNK3cHbHkcjsvfszey/7WFw19yjrNBKRtAhYlMX+8xbIOkm7mNnauHhqPYCZ\n9QP98fStkh4A9iKKOOYm1p8bzxsRM1scKuHdUkd51EUcLo3WhJBJtyJJ+6KtxvXaqUjykMg+C8dn\n9trcsYgfrJc0Pks6Pe228i7Cuhw4Lp4+DrgMQNIsSePj6T2J5PGgmT0MPCHpUEkC3t5Yp0q4PLbR\njTyyLKaqe7FUCLI+Z93+nnkWaeVRnFXFoqyQrbAuAo4AZhHVd3wC+G/gEuC5DG/G+7fAp4ABYAj4\nhJldGW+n0Yx3O6JmvCe32F9pK9HT/sCh5VElcWRBkbJ4amBD0O1Pmzgz6PZbkVVU0k2xVtoK9jRF\nWnlUrOddqV7aVlh5UlaB9Lo8ihZHntIILYm05CWXLGSSt0jykEjZW2a5QCinQFwe6eRRdnGUVRSd\nEFIqRYqkLhJxgeRM2QRSF3m4OOohjLEIIZRuRVL2aKQuEnGBUC6BuDw6pxt5ZC2NXhDGaJRNJmWO\nRuogERcI5RGIy6PDdUoijl6XxmhkKZS8RdKrEnGBdEgZBNKr8qiqOFwanZOVTNKKxCXSPu1KxAVC\n8QIpozzqFnVkIY68pfHMYD6DF06ZkG8T5SxEUvZopEwSCVmU1U3eWdhQJk615VElcYSWRl6SSJuG\nEHJJntO0Mmn8rmlEohnzOpZIp8OgdDr8SZoe6+2SZ0/1TvAIJAPSRB8uj7EpozjKIIssCCGVbqOS\nNCKpeiRShqIsL8KiOIG4PMZYPmdxZC2NughjLLIWSjcyyUMkLpFtuEAoRiC9JI9eEUevCGM0spRJ\nWpG4RFpTJoHkPZii0wEuj7F5amBDJvJ4ZvBxl0dM41xkcT7S/jZpBm3s9Lrr9Lru5p03Y9HJvZ5m\n0MVQAy66QFISOvpI+ybBdgh5I+Qtj25xcYxOkRJJQ2iJdELa9+tUCW+FVQM6uVDzqDDvhKLlUQTr\nN6V/btt58lCGKWmPxnnqpmjrqYENqYqzhqZOz/U97mOR9sVUWVOWVlkegaSgytFHJ4QuuipKHnlG\nHes3jXvWX5m21wndnrNuirM6oUxRSCeU5b7vBBdIxQkZfXRCiNfLjkQW8ghJEZl7nvssSiKdEvJ6\n7OQ+KlMxVoh6EBdIDvRC9NEpaaKPbjKfkFFHEdHAaIROSxESCf1el7JEIVWjHFd8hSjy9ZHNhIo+\nylh01a08QlAmaYxESLFVQSJ5RcVZEro1VtaU9+qvCVWMPjqh1+RRtmijXUKkuW6t1zp5cKpqMVbW\nVOsuKBiPPpqW7SF5VFEaI5H1cXRTNOhRSP5knYe1vJIk7SDpTEnLJW2UtCGePlPSDlkmoq6Eij7K\nII9OyVMeWdZ31EUczYQQSRrKVh9ShiikSsVYo11BlwAbgUXATDObCbwMeCz+rqcoU/RRBkI/2XUj\njyyoqziayfI485RIJ3gUEo7Rrpz5ZnaWma21eMAsM3vYzM4E5ueSugoT6smgDNFH6KKrIuXRK+Jo\npmiJdEroVllOe4x21fxe0kckbc2xJO0i6VTgD+GT1jtUsfK8Xaomj14mK3mm+S3KFIWUoRgrJFmW\npox2tRwDzAJ+EdeBbASWADsCb8oqAVWgLMVXVYs+qiKPXo06WlEVidQ5CqlKPUjLsbDMbAPwkfjP\n6YAqNd0NWXTVCUXKI282bOp8CLqZkwcDpKQ1jfPSzdhbzww+3vH4WZ2OmdXJWFmdvMWwk7cXlmV8\nrCJo60qW9BpgATC5Mc/MPhUqUWWi7tFHKPJ4OuxGHnmJI40s2t1OHlJZv2lcIQM41pWQr73thKvm\nHmXtvC99LMa8iyR9najI6mRA8fTu3e64rhQdffRC0VW3zXRDymPDpgnD/kKS1766OV9lK8oKVRcS\ngioUY7VzZRxmZu8ANpjZJ4GXAC8ImywnSYjKtyrLIy2h6jrSZOL33HI/t99wb8vvb7/hXu655f7g\n6WiXskukaMoQ9RdBO1fFn+L/z0iaAwwCY54tSedJWifpzsS8mZJ+Kuk+SdckOyRK+qik38WdFY9M\nzD9Q0p3xd19u/9C6p9Piq6Kjj6qRZyYRUhxp6N88wOITvzaiRG6/4V4Wn/g1+jcPdJW2rMlbIp0Q\nKgpxRqedK+JHkmYAnwduAVYCF7Wx3vnAK5vmnQb81Mz2Bv4n/oykBUStvhbE63xNUqN87hzgBDPb\nC9hLUvM2HacQuqmD2P+wfVn89ZNYfOLX+OxZq/jcuTP43Lkztspj8ddPYv/D9s0wtcWS5XvWR6KT\nl051+u50pzXtCORsM9toZj8g6kC4D3DGWCuZ2fVEPdmTvBa4IJ6+AHhdPH00cJGZDZjZSuB+4FBJ\nuwLbm9nSeLkLE+s4TfRqGN0OZawI3v+wffmnL5zEkm9+ng0r7mbDirv513dnI48QFexlPIdOsbQT\n594AHABgZpuATZJubczrkNlm1miCsA5oFO7vBtyYWG41MAcYiKcbrInnO04t+PXql/LiY6Zz8/kf\nA+Cgd57B/of5JZ7m9bdF4s14m4if/ncDpkg6gKgFlgHPAaZ0u2MzM0mZNpGVtDjxcYmZLcly+46T\nJQ+thyFrbkkplj8I++yZfrt59xlxiqeT96Mv2/wopz3ym08yPL9MxWgRyJHA8URP/F9IzH8S+FjK\n/a2TtIuZrY0FtT6evwZI1mzNJYo81sTTyfkte/eY2eKU6XKcVMycPJi6wvqCK2ayYcWd3HHxmRz0\nzs8CcMfFZ3Iup/L5z3gU0uusu68vyHb/bNKOw/JKSaen3VbLOhAzu8DMXga808xelvh7rZn9MOX+\nLgeOi6ePAy5LzH+zpD5JewB7AUvNbC3whKRD40r1tyfWcZzK8sRTsGHFXdxx8Zm8+JjTmLnHnzFz\nj/148TGnccfFZ43axLcIuqn/CF2BHop2e6L3Mu1Uov9S0rmSroaoxZSkE8ZaSdJFRPUnL5C0StI7\ngTOBl0u6D/ir+DNmdg/REPH3AFcBJzVGAAZOAr4J/A6438yu7ugIHaeEfP6Lq4fJIyodhp2e9yLO\nvuD9LZv4jkUvFl9VqQVWGXqhZ0k7sfe3iJrk/kv8+XdEmf25o61kZm9p8dXftFj+s8BnR5h/C7Bf\nG+l0nErwzCboH5qUkEfElElDnHLsRiBq4ts3aWJxiXScNmhHILPM7GJJpwGY2YCk3nvMcZwWdFoP\n8qPrtmeHeTsStUsBMMYJdpm17bZK04y3LtFH1Vpg9TLtXPVPSdqx8UHSS4B83hrjODVjYBAeWNXH\nNnlE7Dmnnze+4sliEjUGvVj/0Qm92oQX2hPIh4ArgD0l3QDsBLwhaKocp6bcfPfkpjnG9lOHmLtL\n+mFLoD7Rh9M5nTThzZoxBWJmt0g6gm0DKP7WzLq72h2HqKiiaoPmtaLdYqx7Hpg07LMEC1/wJ/78\nxZtCJc1JQdEtsEI14QXIYhj3Bu0W3B5CNIzJBOAASZjZhVklwnHyYOfJQ4W+efCZTbB+4wSSxVcS\n9HVZV97L0UeVWmDVkTEFIuk/gT2B24FkrOQCcZwEY0UhP75++6Y5xtTthhgsrgRiTPIe/6rOFeh1\na8IL7UUgBwILEv0yHMfpkObKc8nYeeYg++6xuaviqzJHH71Qgd7rtBPP3wXsGjohjlNnbr57MkOJ\nRzAJ9nt+feXh9AajDaZ4RTw5DbhH0lJgczzPzOy1oRPnOM1MmTA9+MuJuqGRqTcXZa1a26joiFpd\nTZ3cXdFVHvLw4dvHppeb8MLoRViNARTPJnpfR7Lm/uxgKXJSM/Tg2rbfCWKr1gR55/O4px/v6O1w\naVpidSORRqYYujI9mcEPDDYEEt1C/QPifW94nAmj3H1FRhfdiiNt0VWn9R+hKtBDtMDqpP6jkxZY\nRTbhhVEE0hgKXdJEM/tF8jtJ2wVOVyVZftuWtl9ru+6+vsq81tY2ruroNaBllwjkJxKAXy2bQv/g\ntuevwUHx2wf6OPzFzwTfdydkEXH0kjyqGH1k2YQXRqkDkfT++H3mL4jfSd74WwksyzIRZSXrk52W\nTp5eOrmoO7lZQjeBTNP6JotK2p0nDwUvqnnwoT4ECGP61C3suuMAgyWqvsjqHKT5PaZNnFl7edQ1\n+oDRi7C+SzQy7pnAqWwrwnrSzB4NnTCn2nQahUD6SAToul4kVERy628n8cCaPqxRfDUoTvrbDUxM\n9wqRTMlKnHlFHVC9fh9lkkeIB+LR3gfyuJmtNLM3m9nv4+mVLo/R6eSH7eSCqWIU0snN3iBtP4Cs\nmoxmGZEMDML3r50+rPiqf0DceHfXL/TsiiyPsazy6BQvukpHcd1yK0JZirFC4hJ5NllksjfeNYWh\noSh0nz51C3N36i+s+KpxPFmKo8zy8KKrfChBIF0/QlWm99+2jr6Fs9tatpMWWZ0SulId0o+TlVWR\nVoNkhttJ8dbAIPz05mkMWbROEUVXoep2uhF1r8ijTIR8CPYIxAGKHzxuJLoZ1iJEL+hOnuJvvGsK\n/QP5F11lHWkk6SbqgGrLo1N6IfoAF0hbhC7GKkNdSKfkUZQF3Usk1HAayYx6pMx6cAtMn7aFmc8Z\nDFp0NVY6siALcZRNHp1ShqKrNITOu7wIKxCdFGOFJGTnwjyKsqD7Yd/z6L3enHm//pCnuPa2aQD8\n81seyaToqoie4d0KOO0DQGh5VLHoqmzRB7hASkOoupBOCdVDvUGREoHs6kbG4tplUxmK8/u7f7sd\nRx74dC77zYosIrc6yKNTylR0lUcDIC/CapM0P0ZZnhjKVJQFxRRnNQhZrNWgfwAu+/X2bB4Yx+aB\ncVz66+3pL1HHwdHI4vykLbKC8skjVNFVXXCBVJSQF2unN1nVJAJhRXLtsqnPqkBfcsfUIPvKiqzO\nRzfiqLI8OqUO0Qe4QDqiTJXpndLpzVB2iWQpkqx5cO0EZmy/hfmz+5k/u5+5Ow3Qv6Wc3YmyFGle\nUQeE72Xe6f1SporzPPE6kMCErEwPWReSF2nrRCC7d6onM9Bu60j6B2DZimis0c8ev56+Et5hWUuz\nG5mH7F2epCz1Hp1S5ugDPAIpHb0WhUB3mUjWr0Dt9on82mVT2TIEW4YoXbFViGK7IuTRK0VXVcAF\n0iFlq0wvW8VdHSQC6TLbMlaeN44jhDhcHmGLrsoefYALpJRUOQqB4iQSUiTtZMJlqTwPJY0G3Z7n\ntHUeVZZHp5SlBedYyMzGXqoCSDIzy82+V809quMT12ldSCcvnOq0LiTNOFlp+od00tEwSdp6kSRZ\n1I90wiU60gTvAAARlklEQVQ3bOE3900eNu/gvTfxmkOeynQ/oZshN5OFmLt5OEjzQFI2eeQxXEna\n6KObvLMQgUg6BXg30UCl3zCzL0taHM/7Y7zYx8zsqnj5jwLvArYAJ5vZNSNss/QCgbASgfAiyVMi\nkI1IIH+ZVJ2sormyiwN6Wx5QMYFIehFwEXAwMABcDbwPOJboZVVfbFp+AdHLrQ4G5gA/A/Y2s6Gm\n5XIVCPSuRKC6IoFwMvn18uh3/fN9qlH80EyWRYB5iwOqHXVAMfKA7vLOIupA9gFuMrNNZrYF+AXw\nt/F3Ix3E0cBFZjZgZiuB+4FDcknpGKT94Tq9UDq9EDu90NO0OklbN9JN/UhWTT4b9SVZ1ptsHoCv\nXz2Jr189qfCK83bJ+jw0fqNuKsjTRh0uj2IoQiB3AYdLmilpCvAqoPFo+g+S7pB0rqQd4nm7AasT\n668mikRKQV4/YF0kAtmIJMv+A1lkpD+9fQKDQzA4FE2XjeZjzDra6DbiyFMcLo/syP1KN7Plks4C\nrgGeBm4nqtv4GvCpeLFPA18ATmi1mZFmxvUoDZaY2ZIMkhyENB0MOxlwETrvaNi4sTop0mrcwGmK\ntDodzbeZRqaVZfEWjFyUM1qx1+YB+K9f9bE5boH1/V/28fL9BwvrRBiiNVozWQi8m97kedR1QD3l\nIWkRsCjt+sO2VXQrLEmfBf5gZv+RmDcfuMLM9pN0GoCZnRl/dzVwupnd1LSd3OtAkuRVHwLh60Qg\nv3qRret2IZIkWcukHb53wxN8Z0kfg0PR5TdhnHHsy/p59UHhyrLykEQzWUV9dRQHVEMeI1GpSnQA\nSTub2XpJzwV+AhwKTDWzh+PvPwgcbGZvTVSiH8K2SvTnW1PCixYIuEQalEEkkJ9MLrlhkOvvGf6u\njsMXjONNh5WvKKtTsiwqzFsc4PJohyoK5DpgR6JWWB80s2slXQjsT1Q8tQI40czWxct/jKgZ7yBw\nipn9ZIRtFi4QqJ9EoPoigWIik6qS9fhUVREHhC+ygnLJAyookBCURSBQbolAdaKRrdvIWCbgQkkS\nYkDDbkfL7Wbww7JGHVA+eYALBCiXQMAl0kxZRQLdyeT6e6IM4fAFxb++uF1CjoCbxTDrZY86oD7y\nABcIUB+BQLklAvUVSZJ2pLJ5wDj2y9F5/84/9tE3oTSX3zBCD5me1bs58o46wOUBLhCgfAIBl8ho\nZPXe9Txk0qBZKpfeNMiFS6KM4bhF43ndocVWmuf1bo0GvSYOqJ88wAUClFMgUG+JQPHRyNZt5SgS\ngM2D8Nb/P4mnNkeX3LRJxnc+OCl4FJK3JJrJ8k2ARYgDyh91QL6dBF0glFcgUA2JQPWjka3by0Em\nP7x1POdeP2FY3493Hz7I6w+o5jhYo5H162O7fTtgnaMOyL+HuQuEcgsE6i8RKJ9IIJxMLv7NeK6/\nb/jvcvjeWzjm4HoIpGzSgPyjDqi/PMAFApRfIOASaZcQIoH8i7mqRNbC2LrdHhIHVE8e4AIBqiEQ\nqI5EoHiRQDiZQG8LJZQwtm6/YHGAy6NdXCBURyCQv0SgOJFANWTyrH3VQC6hJTFsXxkIo0EviQOK\nH1XXBUK1BALdSQR6WyQN8hTKiPsvUDJ5ymHE/WcoDChOGtC74mjgAqF6AoHiJALFigSylwnkJ5Tr\nVkwF4C/3eDqX/ZWBrIUB3UsDihEH1Ece4AIBqimQBr0sEggjEwgjlM2D4m2X7AHAd9+0gr4J9bh/\nmgkhDMhGGuDiyBIXCNUWCHQvEShOJFB+mTToViqX3j2dC26bBcBxCx/h9S8stmNfFoSSRYMySAOK\nFQeUUx7gAgGqL5AGVY1GGmQlEggvkyTtiKURfTzVH53jaX1bKhWFhBZFkqykAS6O0LhAqI9AoPrR\nCGQrEshXJs005HLp3dM595ZZDA6NA2DCuCFOOLA8UUiegmgmS2FA99IAF0e7uECol0AgG4lA8SKB\n7GUCxQjlkpXzuH79zsPmHb7zet40f3iLqKzqXooUwlhkLQzIRhpQvDigOvIAFwhQP4E0KDoaaVBm\nmUCxEUovEEIYUA5pNOilqCOJC4T6CgTKEY00KLtIGrhQuiOUMBrUTRxQTXmACwSot0Aa1FEkEF4m\nDVwqIxNaFpCdMBq4OLLDBUJvCKRBXUUC+ckkSa+IJQ9RJCmjNMDF0YwLhN4SCGQnESinSKAYmTRT\nNbnkLYlmspYGuDhC4wKh9wTSoGwigTAygXIIZTRCyKZoIYxFCGFAdtKA7MQB9ZMHuECA3hUIZCsR\nKL9IGpRdKHUklDAalC3aaFBHcTRwgdDbAmlQVpFAeJlAWKFc/9RcAA6ftjrYPspIaGFAeaMNqLc4\nGrhAcIEkKbNIIB+ZQHZC2Tw0nrevOhqA/5x3GX3jhjLZbhnJQxiQrTTAxdENLhBcIM1kLRHIXiSQ\nn0wapJHKZY/vzbc37gfAO2bcydHT78s6WbmTlyiSZC0NcHFkgQsEF0grQogEwsgE8hfKSCQl04g+\nnh6KMr+p4/orEYUUIYhmqiCMBr0ojgYuEFwg7VA1mUDxQrm8b3++vd1fMKjoGCfYFt7xp+v5P/13\nPGvZEHUwZRBBO4SQRYNQ0oDeFkeDbvLOCVknph0knQK8GxDwDTP7sqSZwMXA7sBK4E1m9li8/EeB\ndwFbgJPN7Joi0l11GjdL1iJJ3uBZy2SkjClPqQxqPLtveWTYvAGNfNtUJbPvlpCyaODSqAa5RyCS\nXgRcBBwMDABXA+8DTgQeMbOzJZ0KzDCz0yQtAL4bLz8H+Bmwt5kNNW3XI5AUhIpKGoSMTpIUHanU\nlTxkAWGFAS6N0ahUEZakNwCvNLN3x58/DvQTRRhHmNk6SbsAS8xsnzj6GDKzs+LlrwYWm9mNTdt1\ngXRBaJFAfjJJ4mJpj7xEkSS0NMDF0Q5VK8K6CzgjLrLaBLwKuBmYbWaNMoB1QKNAeTcgKYvVRJGI\nkyHJGy2UTJozjDyEMlrG2EtyKUIQzeQhDHBp5EnuAjGz5ZLOAq4BngZuJ6rbSC5jkkbLxOpR819S\n8pAJFCOUJO1mqmUWTRnE0Iq8hAEujaIopBLdzM4DzgOQdAZRVLFO0i5mtlbSrsD6ePE1wLzE6nPj\nec9C0uLExyVmtiTjpPcceckEihdKK8qcSZeFPGXRwKWRDkmLgEWZbKuIZrySdjaz9ZKeC/wEeAnw\nL8CjZnaWpNOAHZoq0Q9hWyX6860p4V4Hki951JmMRFmk0ssUIYsGLo3sqVQlOoCk64AdiVphfdDM\nro3rRC4Bnsuzm/F+jKiSfRA4xcx+MsI2XSAFUZRMGrhUwlGkLMCFkQeVE0gIXCDloGiZJHGxtE/R\nokji0sgXFwgukDJSJpk004tyKZMkkrgwisUFggukCpRZKM3ct8fzmLPHOA7c+GDRSRmTsophNFwa\n5aFq/UCcHqU50yirUAbHj+d/jngZAJN+8SATtlQvgy4bLox64gJxCqOsQlm274vYMi4q4rpz3xey\n8K5lBaeoWrgsegcXiFMayiCUwfHjuemggxmcOBGAGw86hP3uvdujkFFwYfQuLhCntIyUMYWWyrJ9\nX8TAhIlbPw9MmOhRSAKXhZPEBeJUitBS2TJ+PLM2PDps3uD43rtNXBROO3grLKfWlKVepay4KBxv\nxosLxOmOuonGxeC0iwsEF4hTHtLIyDN8pyhcILhAHMdx0tBN3jku68Q4juM4vYELxHEcx0mFC8Rx\nHMdJhQvEcRzHSYULxHEcx0mFC8RxHMdJhQvEcRzHSYULxHEcx0mFC8TpeV7x6Svf8IpPX/mGotPh\nOFXDe6I7Pc0rPn3ldsCa+ONuP/nXV28qMj2OkzfeE91x0vNeoC/+e2/BaXGcSuERiNOzJKKPGfGs\njXgU4vQYHoE4TjreC0xNfJ6KRyGO0za996o1x9nGJKD5XbWTi0iI41QRL8JyHMfpYbwIy3Ecx8kd\nF4jjOI6TikIEIumjku6WdKek70qaJGmxpNWSbov/jmpa/neSlks6sog0O47jOMPJXSCS5gPvAQ4w\ns/2A8cCbAQO+aGYL47+r4uUXAMcAC4BXAl+T5JHTKEhaVHQayoKfi234udiGn4tsKCIjfgIYAKZI\nmgBMYVtP4JEqco4GLjKzATNbCdwPHJJHQivMoqITUCIWFZ2AErGo6ASUiEVFJ6AO5C4QM9sAfAH4\nA/AQ8JiZ/Sz++h8k3SHpXEk7xPN2A1YnNrEamJNbgh3HcZwRKaII63nAPwLzieQwTdLbgHOAPYD9\ngYeJJNOKerQ9dhzHqTBFdCQ8CLjBzB4FkPRD4DAz+05jAUnfBK6IP64B5iXWn8u2Iq9hSHKxxEg6\nveg0lAU/F9vwc7ENPxfdU4RAlgP/Kmk7YBPwN8BSSbuY2dp4mdcDd8bTlwPflfRFoqKrvYClzRv1\nToSO4zj5krtAzOwOSRcCNwNDwK3AN4BvStqfqHhqBXBivPw9ki4B7gEGgZOsLt3nHcdxKkxthjJx\nHMdx8qXS/Sm88+FwJL0yPt7fSTq16PTkjaSVkpbF18LSeN5MST+VdJ+kaxKt+2qFpPMkrZN0Z2Je\ny2Ov8/3R4lz0ZF4haZ6ka+OO23dJOjmen821YWaV/QNOB/5phPkLgNuBiUStve4HxhWd3sDnYnx8\nnPPj474d2LfodOV8DlYAM5vmnQ18JJ4+FTiz6HQGOvbDgYXAnWMde93vjxbnoifzCmAXYP94ehrw\nW2DfrK6NSkcgMd75MOIQ4H4zW2lmA8D3iM5Dr9F8PbwWuCCevgB4Xb7JyQczu57ohVhJWh17re+P\nFucCejCvMLO1ZnZ7PP0UcC9RY6RMro06CMQ7H0bMAVYlPvfCMTdjwM8k3SzpPfG82Wa2Lp5eB8wu\nJmmF0OrYe/H+gB7PK+JhpBYCN5HRtVF6gcTldHeO8PdavPNhkrofXzu81MwWAkcBfy/p8OSXFsXo\nPXme2jj2up+Xns4rJE0DfgCcYmZPJr/r5too/RsJzezl7SyXtvNhjWg+5nkMf5KoPWb2cPz/j5Iu\nJQq91zX6GEnaFVhfaCLzpdWx99z9YWZbf/deyyskTSSSx7fN7LJ4dibXRukjkNGID7xBc+fDN0vq\nk7QHLTof1oybgb0kzZfURzSC8eUFpyk3JE2RtH08PRU4kuh6uBw4Ll7sOOCykbdQS1ode8/dH72a\nV0gScC5wj5l9KfFVJtdG6SOQMTjLOx9GmNmgpA8APyFqkXWumd1bcLLyZDZwaXS/MAH4jpldI+lm\n4BJJJwArgTcVl8RwSLoIOAKYJWkV8AngTEY49rrfHyOci9OBRT2aV7wUOBZYJum2eN5Hyeja8I6E\njuM4TioqXYTlOI7jFIcLxHEcx0mFC8RxHMdJhQvEcRzHSYULxHEcx0mFC8RxHMdJhQvEccZA0nRJ\n74+nF0m6Yqx1mtY/rqkjW6vl3hgPu71F0gFp0+s4eeECcZyxmQGc1MX6xxMNUjcWdxL1kr6ui305\nTm5UvSe64+TBmcDz4p68A8DTkr4PvAi4xcyOBZB0INEgfdOAR4jE8RfAQcB3JD0DHAZ8BHgNsB1w\ng5k1ekUvj7eT35E5Thd4BOI4Y3Mq8EA80u+HiYbEPoXo5Tt7SnppPGDdV4G/M7ODgPOBM8zsv4jG\nKXurmR1gZpuAr5rZIWa2H7CdpNcUcVCO0y0egTjO2KhpeqmZPQQg6XaiN7c9DryQ6H0kEI1H9lCL\nbfyVpA8DU4CZwN3Aj0Il3nFC4QJxnM7ZnJjewrb76G4zO6zFOgYgaTLw/4ADzWyNpNOBycFS6jgB\n8SIsxxmbJ4HtR/neiN41vZOkl0D0DgZJCxLrPyeebsji0fglP29k5Bf2eEWIU3o8AnGcMTCzRyX9\nStKdwJ+AtSMsMyDpDcBXJE0nurf+jWhY7G8B/5GoRP8GcFe8nZsa25D0euArwCzgSkm3mdlRQQ/O\ncbrAh3N3HMdxUuFFWI7jOE4qXCCO4zhOKlwgjuM4TipcII7jOE4qXCCO4zhOKlwgjuM4TipcII7j\nOE4qXCCO4zhOKv4XWhppr9V3LagAAAAASUVORK5CYII=\n", 290 | "text/plain": [ 291 | "" 292 | ] 293 | }, 294 | "metadata": {}, 295 | "output_type": "display_data" 296 | } 297 | ], 298 | "source": [ 299 | "fig, ax = plt.subplots()\n", 300 | "best_thetas = all_theta[-1]\n", 301 | "#Adding best_thetas[1] to get contour plot in the correct spot\n", 302 | "ax.contourf(theta1_vals + best_thetas[1], theta0_vals, contours, \n", 303 | " np.logspace(5, 7, 50), cmap=plt.cm.Spectral_r)\n", 304 | "ax.set_xlabel('theta1')\n", 305 | "ax.set_ylabel('theta0')\n", 306 | "ax.autoscale(False)\n", 307 | "ax.set_ylim([850, 1150])\n", 308 | "ax.set_xlim([-50, 200])\n", 309 | "t = np.array(all_theta)\n", 310 | "#Subsample by 20 and plot scatter points on top of contours\n", 311 | "ax.scatter(t[::20, 1], t[::20, 0], zorder=2, color='steelblue', marker=\"^\")\n", 312 | "#Plot X for final theta values\n", 313 | "ax.scatter(best_thetas[1], best_thetas[0], zorder=3, color='black', marker='x', s=75)\n", 314 | "plt.title('Path taken for gradient descent')" 315 | ] 316 | }, 317 | { 318 | "cell_type": "markdown", 319 | "metadata": {}, 320 | "source": [ 321 | "Blurred Lines\n", 322 | "-------------" 323 | ] 324 | }, 325 | { 326 | "cell_type": "markdown", 327 | "metadata": {}, 328 | "source": [ 329 | "With all of this in mind, let's take a look at how closely the final regression values represent the original dataset." 330 | ] 331 | }, 332 | { 333 | "cell_type": "code", 334 | "execution_count": 8, 335 | "metadata": { 336 | "collapsed": false 337 | }, 338 | "outputs": [ 339 | { 340 | "data": { 341 | "text/plain": [ 342 | "" 343 | ] 344 | }, 345 | "execution_count": 8, 346 | "metadata": {}, 347 | "output_type": "execute_result" 348 | }, 349 | { 350 | "data": { 351 | "image/png": "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\n", 352 | "text/plain": [ 353 | "" 354 | ] 355 | }, 356 | "metadata": {}, 357 | "output_type": "display_data" 358 | } 359 | ], 360 | "source": [ 361 | "#Use only the second term - remember that we added a bias term earlier!\n", 362 | "plt.scatter(X[:, 1], y, color='darkred', edgecolor='none',)\n", 363 | "plt.plot(X[:, 1], np.dot(X, best_thetas), color='steelblue')\n", 364 | "plt.title('Gradient regression')" 365 | ] 366 | }, 367 | { 368 | "cell_type": "markdown", 369 | "metadata": {}, 370 | "source": [ 371 | "As seen above, gradient descent has accurately estimated the generating function for this dataset. This is a toy example, but many more advanced methods also employ gradient descent as part of the learning process. Though this example used simple, low dimensional data, gradient descent regression can also work on higher dimensional data. As a bonus, we can also solve linear regression in closed form with one line. See the [Wikipedia entry](https://en.wikipedia.org/wiki/Linear_regression) under \"Least-Squares Estimators and Related Techniques\"." 372 | ] 373 | }, 374 | { 375 | "cell_type": "code", 376 | "execution_count": 9, 377 | "metadata": { 378 | "collapsed": false 379 | }, 380 | "outputs": [ 381 | { 382 | "data": { 383 | "text/plain": [ 384 | "" 385 | ] 386 | }, 387 | "execution_count": 9, 388 | "metadata": {}, 389 | "output_type": "execute_result" 390 | }, 391 | { 392 | "data": { 393 | "image/png": "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\n", 394 | "text/plain": [ 395 | "" 396 | ] 397 | }, 398 | "metadata": {}, 399 | "output_type": "display_data" 400 | } 401 | ], 402 | "source": [ 403 | "closed_form_best_thetas = np.linalg.pinv(np.dot(X.T, X)).dot(X.T).dot(y)\n", 404 | "plt.scatter(X[:, 1], y, color='darkred', edgecolor='none')\n", 405 | "plt.plot(X[:, 1], np.dot(X, closed_form_best_thetas), color='steelblue')\n", 406 | "plt.title('Gradient regression')" 407 | ] 408 | }, 409 | { 410 | "cell_type": "markdown", 411 | "metadata": {}, 412 | "source": [ 413 | "Linear regression is an excellent first approach for many problems - simple to implement, fast, and often good enough to get the job done! For an example of using linear regression in [sklearn](http://scikit-learn.org/stable/), follow this [link](http://scikit-learn.org/stable/auto_examples/linear_model/plot_ols.html#example-linear-model-plot-ols-py)\n", 414 | "\n", 415 | "kk" 416 | ] 417 | } 418 | ], 419 | "metadata": { 420 | "kernelspec": { 421 | "display_name": "Python 2", 422 | "language": "python", 423 | "name": "python2" 424 | }, 425 | "language_info": { 426 | "codemirror_mode": { 427 | "name": "ipython", 428 | "version": 2 429 | }, 430 | "file_extension": ".py", 431 | "mimetype": "text/x-python", 432 | "name": "python", 433 | "nbconvert_exporter": "python", 434 | "pygments_lexer": "ipython2", 435 | "version": "2.7.11" 436 | } 437 | }, 438 | "nbformat": 4, 439 | "nbformat_minor": 0 440 | } 441 | -------------------------------------------------------------------------------- /posts/linear-regression/linear-regression.meta: -------------------------------------------------------------------------------- 1 | .. title: Linear regression 2 | .. slug: linear-regression 3 | .. date: 2013-08-04 22:45:34 UTC-04:00 4 | .. tags: mathjax 5 | .. link: 6 | .. description: 7 | .. type: text 8 | -------------------------------------------------------------------------------- /posts/polyphase-signal-processing/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |

Polyphase Signal Processing

5 |
6 |

Explore the post in your browser using Colab
7 | 8 |

See the pre-rendered post on GitHub
9 | 10 | 11 | -------------------------------------------------------------------------------- /posts/polyphase-signal-processing/polyphase-signal-processing.meta: -------------------------------------------------------------------------------- 1 | .. title: Polyphase Signal Processing 2 | .. slug: polyphase-signal-processing 3 | .. date: 2014-02-15 23:54:21 UTC-04:00 4 | .. tags: mathjax 5 | .. link: 6 | .. description: 7 | .. type: text 8 | -------------------------------------------------------------------------------- /posts/robust-matrix-decomposition/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |

Robust Matrix Decomposition

5 |
6 |

Explore the post in your browser using Colab
7 | 8 |

See the pre-rendered post on GitHub
9 | 10 | 11 | -------------------------------------------------------------------------------- /posts/robust-matrix-decomposition/robust-matrix-decomposition.meta: -------------------------------------------------------------------------------- 1 | .. title: Robust Matrix Decomposition 2 | .. slug: robust-matrix-decomposition 3 | .. date: 2014-03-05 00:13:44 UTC-04:00 4 | .. tags: mathjax 5 | .. link: 6 | .. description: 7 | .. type: text 8 | -------------------------------------------------------------------------------- /posts/single-speaker-word-recognition-with-hidden-markov-models/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |

Single Speaker Word Recognition With Hidden Markov Models

5 |
6 |

Explore the post in your browser using Colab
7 | 8 |

See the pre-rendered post on GitHub
9 | 10 | 11 | -------------------------------------------------------------------------------- /posts/single-speaker-word-recognition-with-hidden-markov-models/single-speaker-word-recognition-with-hidden-markov-models.meta: -------------------------------------------------------------------------------- 1 | .. title: Single Speaker Word Recognition With Hidden Markov Models 2 | .. slug: single-speaker-word-recognition-with-hidden-markov-models 3 | .. date: 2014-05-22 19:43:18 UTC-04:00 4 | .. tags: 5 | .. link: 6 | .. description: 7 | .. type: text 8 | -------------------------------------------------------------------------------- /posts/using-pytables-for-larger-than-ram-data-processing/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |

Using Pytables For Larger Than Ram Data Processing

5 |
6 |

Explore the post in your browser using Colab
7 | 8 |

See the pre-rendered post on GitHub
9 | 10 | 11 | -------------------------------------------------------------------------------- /posts/using-pytables-for-larger-than-ram-data-processing/numpy_data_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kastnerkyle/kastnerkyle.github.io/4bd3f80d9bb7759bbf910acb45421f5d6c89d679/posts/using-pytables-for-larger-than-ram-data-processing/numpy_data_layout.png -------------------------------------------------------------------------------- /posts/using-pytables-for-larger-than-ram-data-processing/using-pytables-for-larger-than-ram-data-processing.meta: -------------------------------------------------------------------------------- 1 | .. title: Using PyTables for Larger-Than-RAM Data Processing 2 | .. slug: using-pytables-for-larger-than-ram-data-processing 3 | .. date: 2015-07-20 21:55:37 UTC-04:00 4 | .. tags: 5 | .. link: 6 | .. description: 7 | .. type: text 8 | -------------------------------------------------------------------------------- /posts/utils.py: -------------------------------------------------------------------------------- 1 | try: 2 | from urllib2 import urlopen 3 | except ImportError: 4 | from urllib.request import urlopen 5 | 6 | def progress_bar_downloader(url, fname, progress_update_every=5): 7 | #from http://stackoverflow.com/questions/22676/how-do-i-download-a-file-over-http-using-python/22776#22776 8 | u = urlopen(url) 9 | f = open(fname, 'wb') 10 | meta = u.info() 11 | file_size = int(meta.get("Content-Length")) 12 | print("Downloading: %s Bytes: %s" % (fname, file_size)) 13 | file_size_dl = 0 14 | block_sz = 8192 15 | p = 0 16 | while True: 17 | buffer = u.read(block_sz) 18 | if not buffer: 19 | break 20 | file_size_dl += len(buffer) 21 | f.write(buffer) 22 | if (file_size_dl * 100. / file_size) > p: 23 | status = r"%10d [%3.2f%%]" % (file_size_dl, file_size_dl * 100. / file_size) 24 | print(status) 25 | p += progress_update_every 26 | f.close() 27 | -------------------------------------------------------------------------------- /posts/wavelets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |

Wavelets

5 |
6 |

Explore the post in your browser using Colab
7 | 8 |

See the pre-rendered post on GitHub
9 | 10 | 11 | -------------------------------------------------------------------------------- /posts/wavelets/wavelets.meta: -------------------------------------------------------------------------------- 1 | .. title: Wavelets 2 | .. slug: wavelets 3 | .. date: 2014-04-17 18:36:21 UTC-04:00 4 | .. tags: 5 | .. link: 6 | .. description: 7 | .. type: text 8 | -------------------------------------------------------------------------------- /site_gen.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | begin_str = """ 4 | 5 | 6 | 7 | Kyle Kastner's Homepage 8 | 9 |
10 |
11 |

Around the Web

12 |
13 |
You can find me on Twitter at https://twitter.com/kastnerkyle
14 | 15 |

For more code, see my GitHub at https://github.com/kastnerkyle
16 | 17 |

Papers I've written or helped write can be found on Google Scholar
18 |
19 | 20 |
21 | 22 |
23 |

Blog Posts

24 |
25 | """ 26 | 27 | index_blogpost_fill_str = """ 28 |

{}
29 | """ 30 | 31 | end_str = """ 32 | 33 | """ 34 | 35 | post_index_html = """ 36 | 37 |
38 |

{}

39 |
40 |

Explore the post in your browser using Colab
41 | 42 |

See the pre-rendered post on GitHub
43 | 44 | 45 | """ 46 | 47 | blog_fill_str = "" 48 | 49 | post_dirs = ["posts/" + p for p in sorted(os.listdir("posts"))] 50 | for post_dir in post_dirs: 51 | title = " ".join([w[0].upper() + w[1:] for w in post_dir.split(os.sep)[-1].split("-")]) 52 | if post_dir[-3:] in [".sh", ".py"]: 53 | continue 54 | blog_fill_str += index_blogpost_fill_str.format(post_dir + "/" + "index.html", title) 55 | with open(post_dir + os.sep + "index.html", "w") as f: 56 | ipynb_name = [p for p in os.listdir(post_dir) if p.endswith(".ipynb")][0] 57 | pth = post_dir + os.sep + ipynb_name 58 | f.write(post_index_html.format(title, pth, pth)) 59 | 60 | with open("index.html", "w") as f: 61 | f.write(begin_str + blog_fill_str + end_str) 62 | --------------------------------------------------------------------------------