<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Derek Haynes</title>
    <description>The latest articles on DEV Community by Derek Haynes (@itsderek23).</description>
    <link>https://dev.to/itsderek23</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F158518%2F416a2cd7-2743-45aa-a259-9565c25878a7.jpeg</url>
      <title>DEV Community: Derek Haynes</title>
      <link>https://dev.to/itsderek23</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/itsderek23"/>
    <language>en</language>
    <item>
      <title>Machine learning deserves its own flavor of Continuous Delivery</title>
      <dc:creator>Derek Haynes</dc:creator>
      <pubDate>Fri, 24 Apr 2020 15:54:28 +0000</pubDate>
      <link>https://dev.to/itsderek23/machine-learning-deserves-its-own-flavor-of-continuous-delivery-3ak1</link>
      <guid>https://dev.to/itsderek23/machine-learning-deserves-its-own-flavor-of-continuous-delivery-3ak1</guid>
      <description>&lt;p&gt;Things feel slightly different - but eerily similar - when traveling through Canada as an American. The red vertical lines on McDonald's straws are a bit thicker, for example. It's a lot like my travels through the world of data science as a software engineer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;While the data science world is magical, I'm homesick for the refined state of &lt;a href="https://en.wikipedia.org/wiki/Continuous_delivery"&gt;Continuous Delivery (CD)&lt;/a&gt; in classical software projects when I'm there.&lt;/strong&gt; What's Continuous Delivery?&lt;/p&gt;

&lt;p&gt;Martin Fowler &lt;a href="https://www.martinfowler.com/bliki/ContinuousDelivery.html#footnote-when"&gt;says&lt;/a&gt; you're doing Continuous Delivery when:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Your software is deployable throughout its lifecycle&lt;/li&gt;
&lt;li&gt;Your team prioritizes keeping the software deployable over working on new features&lt;/li&gt;
&lt;li&gt;Anybody can get fast, automated feedback on the production readiness of their systems any time somebody makes a change to them&lt;/li&gt;
&lt;li&gt;You can perform push-button deployments of any version of the software to any environment on demand&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;For a smaller-scale software project deployed to &lt;a href="http://heroku.com"&gt;Heroku&lt;/a&gt;, Continuous Delivery (and its hyper-active sibling &lt;a href="https://www.martinfowler.com/bliki/ContinuousDelivery.html#footnote-when"&gt;Continuous &lt;em&gt;Deployment&lt;/em&gt;&lt;/a&gt;) is a picture of effortless grace. You can setup up &lt;a href="https://devcenter.heroku.com/articles/heroku-ci"&gt;CI&lt;/a&gt; in minutes, which means your unit tests run on every push to your git repo. You get &lt;a href="https://devcenter.heroku.com/articles/github-integration-review-apps"&gt;Review Apps&lt;/a&gt; to preview the app from a GitHub pull request. Finally, every &lt;code&gt;git push&lt;/code&gt; to master automatically deploys your app. All of this takes less than an hour to configure.&lt;/p&gt;

&lt;p&gt;Now, why doesn't this same process work for our tangled balls of yarn wrapped around a &lt;em&gt;single&lt;/em&gt; &lt;code&gt;predict&lt;/code&gt; function? Well, the world of data science is not easy to understand until you are deep in its inner provinces. &lt;strong&gt;Let's see why we need &lt;a href="https://martinfowler.com/articles/cd4ml.html"&gt;CD4ML&lt;/a&gt; and how you can go about implementing this in your machine learning project today.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Side-note: CD4ML in action&lt;/strong&gt;? I'd love to put an example machine learning project that implements a lean CD4ML stack on GitHub, but I perform better in front of an audience. &lt;a href="http://eepurl.com/gXANt5"&gt;&lt;strong&gt;Subscribe here to be notified when the sample app is built!&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why classical CD doesn't work for machine learning
&lt;/h2&gt;

&lt;p&gt;I see 6 key differences in a machine learning project that make it difficult to apply classical software CD systems:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Machine Learning projects have multiple deliverables
&lt;/h3&gt;

&lt;p&gt;Unlike a classical software project that solely exists to deliver an application, a machine learning project has multiple deliverables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ML Models - trained and serialized models with evaluation metrics for each.&lt;/li&gt;
&lt;li&gt;A web service - an application that serves model inference results via an HTTP API.&lt;/li&gt;
&lt;li&gt;Reports - generated analysis results (often from notebooks) that summarize key findings. These can be static or interactive using a tool like &lt;a href="https://www.streamlit.io"&gt;Streamlit&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Having multiple deliverables - each with different build processes and final presentations - is more complicated than compiling a single application. Because each of these is tightly coupled to each other - and to the data - it doesn't make sense to create a separate project for each.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Notebooks are hard to review
&lt;/h3&gt;

&lt;p&gt;Code reviews on git branches are so common, they are &lt;a href="https://github.com/features/code-review/"&gt;baked right into GitHub&lt;/a&gt; and other hosted version control systems. GitHub's code review system is great for &lt;code&gt;*.py&lt;/code&gt; files, but it's terrible for viewing changes to JSON-formatted files. Unfortunately, that's how notebook files (&lt;code&gt;*.ipynb&lt;/code&gt;) are stored. This is especially painful because notebooks - not abstracting logic to &lt;code&gt;*.py&lt;/code&gt; files - is the starting point for almost all data science projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Testing isn't simply pass/fail
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/0MDrZpO_7Q4"&gt;
&lt;/iframe&gt;
&lt;br&gt;
&lt;em&gt;Elle O'Brien covers the ambigous nature of using CI for model evaluation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It's easy to indicate if a classical software git branch is OK to merge by running a unit test suite against the code branch. These tests contain simple true/false assertions (ex: "can a user signup?"). Model evaluation is harder and often requires a data scientist to manually review as evaluation metrics often change in different directions. This phase can feel more like a code review in a classical software project.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Experiments versus git branches
&lt;/h3&gt;

&lt;p&gt;Software engineers in classical projects create small, focused git branches when working on a bug or enhancement. While branches are lightweight, they are different than model training experiments as they usually end up being deployed. Model experiments? Like fish eggs, the vast majority of them will never see open waters. Additionally, an experiment likely has very few changes code changes (ex: adjusting a hyper-parameter) compared to a classical software git branch.&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Extremely large files
&lt;/h3&gt;

&lt;p&gt;Most web applications do not store large files in their git repositories. Most data science projects do. Data almost always requires this. Models may require this as well (there's also little point in keeping binary files in git).&lt;/p&gt;
&lt;h3&gt;
  
  
  6. Reproducible outputs
&lt;/h3&gt;

&lt;p&gt;It's easy for any developer on a classical software project to reproduce the state of the app from any point in time. The app typically changes in just two dimensions - the code and the structure of the database. It's hard to get to a reproducible state in ML projects because many dimensions can change (the amount of data, the data schema, feature extraction, and model selection).&lt;/p&gt;
&lt;h2&gt;
  
  
  How might a simple CD system look for Machine Learning?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QmH56Q5p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://booklet.ai/img/posts/cd4ml/cd4ml_tools.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QmH56Q5p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://booklet.ai/img/posts/cd4ml/cd4ml_tools.png" alt="cd4ml tools"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Some of the tools available to make CD4ML come to life.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the excellent &lt;a href="https://martinfowler.com/articles/cd4ml.html"&gt;Continuous Delivery for Machine Learning&lt;/a&gt;, the &lt;a href="https://www.thoughtworks.com/"&gt;Thoughtworks&lt;/a&gt; team provides a &lt;a href="https://github.com/ThoughtWorksInc/cd4ml-workshop"&gt;sample ML application&lt;/a&gt; that illustrates a CD4ML implementation. Getting this to run involves a number of moving parts: GoCD, Kubernetes, DVC, Google Storage, MLFlow, Docker, and  the EFK stack (ElasticSearch, FluentD, and Kibana). Assembling these ten pieces is approachable for the enterprise clients of Throughworks, but it's a mouthful for smaller organizations. &lt;strong&gt;How can leaner organizations implement CD4ML?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Below are my suggestions, ordered by what I'd do first. The earlier items are more approachable. The later items build on the foundation, adding in more automation at the right time.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Start with Cookiecutter Data Science
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Nobody sits around before creating a new Rails project to figure out where they want to put their views; they just run rails new to get a standard project skeleton like everybody else.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://drivendata.github.io/cookiecutter-data-science/"&gt;Cookiecutter Data Science&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When I view the source code for an existing Ruby on Rails app, I can easily navigate around as they all follow the same structure. Unfortunately, most data science projects don't use a shared project skeleton. However, it doesn't have to be that way: start your data science project with &lt;a href="https://github.com/drivendata/cookiecutter-data-science"&gt;Cookiecutter Data Science&lt;/a&gt; rather than dreaming up your own unique structure.&lt;/p&gt;

&lt;p&gt;Cookiecutter Data Science sets up a project structure that works for most projects, creating directories like &lt;code&gt;data&lt;/code&gt;, &lt;code&gt;models&lt;/code&gt;, &lt;code&gt;notebooks&lt;/code&gt;, and &lt;code&gt;src&lt;/code&gt; (for shared source code).&lt;/p&gt;
&lt;h3&gt;
  
  
  2. nbdime or ReviewNB for better notebook code reviews
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vWFvVdWM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://booklet.ai/img/posts/cd4ml/nbdiff-web.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vWFvVdWM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://booklet.ai/img/posts/cd4ml/nbdiff-web.png" alt="nbdiff"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;nbdime is a Python package that makes it easier to view changes in notebook files.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It's hard to view changes to &lt;code&gt;*.ipynb&lt;/code&gt; in standard diff tools. It's trivial to conduct a code review of a &lt;code&gt;*.py&lt;/code&gt; script, but almost impossible with a JSON-formatted notebook. &lt;a href="https://nbdime.readthedocs.io/en/latest/"&gt;nbdime&lt;/a&gt; and &lt;a href="https://www.reviewnb.com/"&gt;ReviewNB&lt;/a&gt; make this process easier.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. dvc for data files
&lt;/h3&gt;

&lt;pre&gt;
dvc add data/
git commit -ma "Storing data dir in DVC"
git push
dvc push
&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;After installing dvc and adding a remote, the above is all that's required to version-control your &lt;code&gt;data/&lt;/code&gt; directory with dvc.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It's easy to commit large files (esc. data files) to a git repo. Git doesn't handle large files well: it slows things down, can cause the repo to grow in size quickly, and you can't really view diffs of these files anyway. A solution gaining a lot of steam for version control of large files is &lt;a href="https://github.com/iterative/dvc"&gt;Data Version Control (dvc)&lt;/a&gt;. dvc is easy to install (just use &lt;code&gt;pip&lt;/code&gt;) and its command structure mimics git.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But wait - you're querying data direct from your data warehouse and don't have large data files?&lt;/strong&gt; This is bad as it virtually guarantees your ML project is not reproducible: the amount of data and schema is almost guaranteed to change over time. Instead, dump those query results to CSV files. Storage is cheap, and dvc makes it easy to handle these large files.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. dvc for reproducible training pipelines
&lt;/h3&gt;

&lt;p&gt;dvc isn't just for large files. As Christopher Samiullah &lt;a href="https://christophergs.com/machine%20learning/2019/05/13/first-impressions-of-dvc/#pipelines"&gt;says&lt;/a&gt; in &lt;em&gt;First Impressions of Data Science Version Control (DVC)&lt;/em&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pipelines are where DVC really starts to distinguish itself from other version control tools that can handle large data files. DVC pipelines are effectively version controlled steps in a typical machine learning workflow (e.g. data loading, cleaning, feature engineering, training etc.), with expected dependencies and outputs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can use &lt;a href="https://dvc.org/doc/tutorials/get-started/pipeline"&gt;&lt;code&gt;dvc run&lt;/code&gt;&lt;/a&gt; to create a Pipeline stage that encompasses your training pipeline:&lt;/p&gt;

&lt;pre&gt;
dvc run -f train.dvc \
          -d src/train.py -d data/ \
          -o model.pkl \
          python src/train.py data/ model.pkl
&lt;/pre&gt;

&lt;p class="small text-muted"&gt;
&lt;code&gt;dvc run&lt;/code&gt; names this stage &lt;code&gt;train&lt;/code&gt;, depends on &lt;code&gt;train.py&lt;/code&gt; and the &lt;code&gt;data/&lt;/code&gt; directory, and outputs a &lt;code&gt;model.pkl&lt;/code&gt; model.
&lt;/p&gt;

&lt;p&gt;Later, you run this pipeline with just:&lt;/p&gt;

&lt;pre&gt;
dvc repro train.dvc
&lt;/pre&gt;

&lt;p&gt;Here's where "reproducible" comes in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the dependencies are unchanged - &lt;code&gt;dvc repo&lt;/code&gt; simply grabs &lt;code&gt;model.pkl&lt;/code&gt; from remote storage. There is no need to run the entire training pipeline again.&lt;/li&gt;
&lt;li&gt;If the dependencies change - &lt;code&gt;dvc repo&lt;/code&gt; will warn us and re-run this step.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to go back to a prior state (say you have a &lt;code&gt;v1.0&lt;/code&gt; git tag):&lt;/p&gt;

&lt;pre&gt;
git checkout v1.0
dvc checkout
&lt;/pre&gt;

&lt;p class="small text-muted"&gt;Running &lt;code&gt;dvc checkout&lt;/code&gt; will restore the &lt;code&gt;data/&lt;/code&gt; directory and &lt;code&gt;model.pkl&lt;/code&gt; to is previous state.
&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Move slow training pipelines to a CI cluster
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://www.youtube.com/watch?v=0MDrZpO_7Q4&amp;amp;list=PLVeJCYrrCemgbA1cWYn3qzdgba20xJS8V&amp;amp;index=6"&gt;Reimagining DevOps&lt;/a&gt;, Elle O'Brien makes an elegant case for using CI to run model training. I think this is brilliant - CI is already used to run a classical software test suite in the cloud. Why not use the larger resources available to you in the cloud to do the same for your model training pipelines? In short, push up a branch with your desired experiment changes and let CI do the training and report results.&lt;/p&gt;

&lt;p&gt;Christopher Samiullah &lt;a href="https://dev.toSamiullah"&gt;also covers moving training to CI&lt;/a&gt;, even providing a link to a &lt;a href="https://github.com/ChristopherGS/dvc_test/pull/1"&gt;GitHub PR&lt;/a&gt; that uses &lt;code&gt;dvc repo train.dvc&lt;/code&gt; in his CI flow.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Deliverable-specific review apps
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Zpclyeuq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://booklet.ai/img/posts/cd4ml/github-statuses-deploy-previews.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Zpclyeuq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://booklet.ai/img/posts/cd4ml/github-statuses-deploy-previews.png" alt="netlify deploy preview"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Netlify, a PaaS for static sites, lets you view the current version of your site right from a GitHub pull request.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Platforms like Heroku and Netlify allow you to view a running version of your application right from a GitHub pull request. This is fantastic for validating things are working as desired. These are great for presenting work to non-technical team members.&lt;/p&gt;

&lt;p&gt;Do the same using GitHub actions that start Docker containers to serve your deliverables. I'd spin up the web service and any dynamic versions of reports (like something built with &lt;a href="http://www.streamlit.io"&gt;Streamlit&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Like eating healthy, brushing your teeth, and getting fresh air, there's no downside to implementing Continuous Delivery in a software project. We can't copy and paste the classical software CD process on top of machine learning projects because an ML project ties together large datasets, a slow training process that doesn't generate clear pass/fail acceptance tests, and contains multiple types of deliverables. By comparison, a classical software project contains just code and has a single deliverable (an application).&lt;/p&gt;

&lt;p&gt;Thankfully, it's possible to create a Machine Learning-specific flavor of Continuous Delivery (&lt;a href="https://martinfowler.com/articles/cd4ml.html"&gt;CD4ML&lt;/a&gt;) for non-enterprise organizations with existing tools (git, Cookiecutter Data Science, nbdime, dvc, and a CI server) today.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CD4ML in action&lt;/strong&gt;? I'd love to put an example machine learning project that implements a lean CD4ML stack on GitHub, but I perform better in front of an audience. &lt;a href="http://eepurl.com/gXANt5"&gt;&lt;strong&gt;Subscribe here to be notified when the sample app is built!&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Elsewhere
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://martinfowler.com/articles/cd4ml.html"&gt;Continuous Delivery for Machine Learning&lt;/a&gt; - this blog post (and the CD4ML acronym) wouldn't exist without the original work of Daniel Sato, Arif Wider, and Christoph Windheuser.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://christophergs.com/machine%20learning/2019/05/13/first-impressions-of-dvc/#pipelines"&gt;First Impressions of Data Science Version Control (DVC)&lt;/a&gt; - this is a great technical walkthrough migrating an existing ML project to using DVC for tracking data, creating pipelines, versioning, and CI.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=0MDrZpO_7Q4&amp;amp;list=PLVeJCYrrCemgbA1cWYn3qzdgba20xJS8V&amp;amp;index=6"&gt;Reimagining DevOps for ML by Elle O'Brien&lt;/a&gt; - a concise 8 minute YouTube talk that outlines a novel approach for using CI to train and record metrics for model training experiments.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ljvmiranda921.github.io/notebook/2020/03/06/jupyter-notebooks-in-2020/"&gt;How to use Jupyter Notebooks in 2020&lt;/a&gt; - a comprehensive three-part series on architecting data science projects, best practices, and how the tools ecosystem fits together.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>machinelearning</category>
      <category>datascience</category>
      <category>python</category>
      <category>mlops</category>
    </item>
    <item>
      <title>Is your Django app slow? Think like a data scientist, not an engineer</title>
      <dc:creator>Derek Haynes</dc:creator>
      <pubDate>Mon, 22 Apr 2019 15:56:57 +0000</pubDate>
      <link>https://dev.to/scoutapm/is-your-django-app-slow-think-like-a-data-scientist-not-an-engineer-5bnb</link>
      <guid>https://dev.to/scoutapm/is-your-django-app-slow-think-like-a-data-scientist-not-an-engineer-5bnb</guid>
      <description>&lt;p&gt;&lt;em&gt;This post originally appeared on the &lt;a href="https://scoutapm.com/blog/is-your-django-app-slow-ask-a-data-scientist-not-an-engineer"&gt;Scout Blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I'm an engineer by trade. &lt;strong&gt;I rely on intuition&lt;/strong&gt; when investigating a slow Django app. I've solved a lot of performance issues over the years and the short cuts my brain takes often work. However, &lt;strong&gt;intuition can fail&lt;/strong&gt;. It can fail hard in complex Django apps with many layers (ex: an SQL database, a NoSQL database, ElasticSearch, etc) and many views. There's too much noise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Instead of relying on an engineer's intuition, what if we approached a performance investigation like a data scientist?&lt;/strong&gt; In this post, I'll walk through a performance issue as a data scientist, not as an engineer. I'll share time series data from a real performance issue and my Google Colab notebook I used to solve the issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem part I: too much noise
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MjAb0kFZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buttercms.com/Q1i3XCC5SACcHvdYXVtM" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MjAb0kFZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buttercms.com/Q1i3XCC5SACcHvdYXVtM" alt="undefined"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Photo Credit: pyc &lt;a href="https://www.flickr.com/photos/pyc/4963466757/in/photolist-8yB4K4-pBRCcq-nZXkWu-bWsES4-29rmwqJ-boe2Vk-7vsTeb-9N2guL-ng4zzC-8hE5Cq-zouzT-areoZ2-nLQoy7-8hpHiQ-a5r9xQ-8hE5Ku-ofvatX-n3mHF-fHSwDM-inbd3a-7oT1q-4UsU2J-2526RGe-8LaPMa-32AUXJ-bka1JE-bka1Kf-9aBMJf-dqr3qB-dqrdUr-5sx8D-o4dwhQ-hR6gY-prMTu-4Tg8va-29MP5Hj-7F2BDK-7F6uoN-7EAp5f-7EAnS1-7E4qdX-JoapDG-7F6v6y-zosvP-7DrJvR-7Ewy2F-7F6v9N-6g9j2o-7Ewvci-7DrFoZ"&gt;Carnoy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Many performance issues are caused by one of the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;A slow layer&lt;/strong&gt; - just one of many layers (the database, app server, etc) is slow and impacting many views in a Django app.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;A slow view&lt;/strong&gt; - one view is generating slow requests. This has a big impact on the performance profile of the app as a whole.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;A high throughput view&lt;/strong&gt; - a rarely used view suddenly sees an influx of traffic and triggers a spike in the overall response time of the app.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;When investigating a performance issue, I start by looking for correlations.&lt;/strong&gt; Are any metrics getting worse at the same time? This can be hard: &lt;strong&gt;a modest Django app with 10 layers and 150 views has 3,000 unique combinations of time series data sets to compare!&lt;/strong&gt; If my intuition can't quickly isolate the issue, it's close to impossible to isolate things on my own. &lt;/p&gt;

&lt;h2&gt;
  
  
  The problem part II: phantom correlations
&lt;/h2&gt;

&lt;p&gt;Determining if two time series data sets are correlated is &lt;a href="https://www.kdnuggets.com/2015/02/avoiding-common-mistake-time-series.html"&gt;notoriously fickle&lt;/a&gt;. For example, doesn't it look like the number of films Nicolas Cage appears in each year and swimming pool drownings are correlated?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XoyXvg8o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buttercms.com/MGY8SSIhS2eyXEaAhuMI" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XoyXvg8o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buttercms.com/MGY8SSIhS2eyXEaAhuMI" alt="chart.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.indiebound.org/book/9780316339438"&gt;Spurious Correlations&lt;/a&gt; is an entire book related to these seemingly clear but unconnected correlations! So, &lt;strong&gt;why do trends appear to trigger correlations when looking at a&lt;/strong&gt; timeseries &lt;strong&gt;chart?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's an example: five years ago, my area of Colorado &lt;a href="https://999thepoint.com/photos-from-estes-park-colorado-flood-2013/"&gt;experienced a historic flood&lt;/a&gt;. It shut off one of the two major routes into Estes Park, the gateway to Rocky Mountain National Park. If you looked at sales receipts across many different types of businesses in Estes Park, you'd see a sharp decline in revenue while the road was closed and an increase in revenue when the road reopened. This doesn't mean that revenue amongst different stores was correlated. The stores were just impacted by a mutual dependency: a closed road!&lt;/p&gt;

&lt;p&gt;One of the easiest ways to remove a trend from a time series is to calculate the &lt;em&gt;&lt;a href="https://people.duke.edu/~rnau/411diff.htm"&gt;first difference&lt;/a&gt;&lt;/em&gt;. To calculate the first difference, you subtract from each point the point that came before it:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;y'(t) = y(t) - y(t-1)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That's great, but my visual brain can't re-imagine a time series into its first difference when staring at a chart.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Data Science
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;We have a data science problem, not a performance problem!&lt;/strong&gt; We want to identify any highly correlated time series metrics. We want to see past misleading trends. To solve this issue, we'll use the following tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://colab.research.google.com"&gt;Google Colab&lt;/a&gt;, a shared notebook environment&lt;/li&gt;
&lt;li&gt;  Common Python data science libraries like &lt;a href="https://pandas.pydata.org/"&gt;Pandas&lt;/a&gt; and &lt;a href="https://www.scipy.org/"&gt;SciPy&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  Performance data collected from &lt;a href="https://scoutapp.com"&gt;Scout&lt;/a&gt;, an Application Performance Monitoring (APM) product. &lt;a href="https://apm.scoutapp.com/users/sign_up"&gt;Signup&lt;/a&gt; for a free trial if you don't have an account yet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'll walk through a &lt;a href="https://colab.research.google.com/drive/1VhCwtGLc-tWhB_gbGuBo_cfs7Q5J4dCI"&gt;shared notebook on Google Colab&lt;/a&gt;. You can easily save a copy of this notebook, enter your metrics from Scout, and identify the most significant correlations in your Django app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: view the app in Scout
&lt;/h2&gt;

&lt;p&gt;I login to Scout and see the following overview chart:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NDedqRIz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buttercms.com/OxGdJmY0RM2KypKJFcCm" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NDedqRIz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buttercms.com/OxGdJmY0RM2KypKJFcCm" alt="undefined"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Time spent in SQL queries jumped significantly from 7pm - 9:20pm. Why? This is scary as almost every view touches the database!&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: load layers time series data into Pandas
&lt;/h2&gt;

&lt;p&gt;To start, I want to look for correlations between the layers (ex: SQL, MongoDB, View) and the average response time of the Django app. There are fewer layers (10) than views (150+) so it's a simpler place to start. I'll grab this time series data from Scout and initialize a Pandas Dataframe. I'll leave this data wrangling &lt;a href="https://colab.research.google.com/drive/1VhCwtGLc-tWhB_gbGuBo_cfs7Q5J4dCI#scrollTo=8KKzj89nfZ-9"&gt;to the notebook&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After loading the data into a Pandas Dataframe we can &lt;a href="https://colab.research.google.com/drive/1VhCwtGLc-tWhB_gbGuBo_cfs7Q5J4dCI#scrollTo=j4PKi7wIfZ_L"&gt;plot these layers&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qP1laKOW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buttercms.com/eZVXiFIcSiGAdVBrhrvb" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qP1laKOW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buttercms.com/eZVXiFIcSiGAdVBrhrvb" alt="plot.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: layer correlations
&lt;/h3&gt;

&lt;p&gt;Now, let's see if any layers are correlated to the Django app's overall average response time. Before comparing each layer time series to the response time, we want to calculate the first difference of each time series. With Pandas, we can do this very easily via the &lt;code&gt;diff()&lt;/code&gt; function:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df.diff()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After calculating the first difference, we can then look for &lt;a href="https://en.wikipedia.org/wiki/Correlation_coefficient"&gt;correlations&lt;/a&gt; between each time series via the&lt;code&gt;corr()&lt;/code&gt; function. The correlation value ranges from −1 to +1, where ±1 indicates the strongest possible agreement and 0 the strongest possible disagreement.&lt;/p&gt;

&lt;p&gt;My notebook generates the &lt;a href="https://colab.research.google.com/drive/1VhCwtGLc-tWhB_gbGuBo_cfs7Q5J4dCI#scrollTo=hwm0KcDzfZ_Z"&gt;following result&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--X9w8AjeQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buttercms.com/PKRnV8tXSPqWP4X1Wxwg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--X9w8AjeQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buttercms.com/PKRnV8tXSPqWP4X1Wxwg" alt="corr_layer.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SQL&lt;/code&gt; appears to be correlated to the overall response time of the Django app. To be sure, let's determine the Pearson Coefficient &lt;a href="http://www.eecs.qmul.ac.uk/~norman/blog_articles/p_values.pdf"&gt;p-value.&lt;/a&gt; A low value (&amp;lt; 0.05) indicates that the overall response time is highly likely to be correlated to the SQL layer:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df_diff = df.diff().dropna()
p_value = scipy.stats.pearsonr(df_diff.total.values, df_diff[top_layer_correl].values)[1]
print("first order series p-value:", p_value)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The p-value is just &lt;code&gt;1.1e-54&lt;/code&gt;. I'm very confident that slow SQL queries are related to an overall slow Django app.&lt;/strong&gt; It's always the database, right?&lt;/p&gt;

&lt;p&gt;Layers are just one dimension we should evaluate. Another is the response time of the Django views.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Rinse+repeat for Django view response times
&lt;/h2&gt;

&lt;p&gt;The overall app response time could increase if a view starts responding slowly. We can see if this is happening by looking for correlations in our view response times versus the overall app response time. We're using the exact same process as we used for layers, just swapping out the layers for time series data from each of our views in the Django app:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QVB93BVq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buttercms.com/VNR7BsT1SSKwqwCG3jjG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QVB93BVq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buttercms.com/VNR7BsT1SSKwqwCG3jjG" alt="undefined"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After calculating the first difference of each time series, &lt;code&gt;apps/data&lt;/code&gt; does appear to be correlated to the overall app response time. &lt;strong&gt;With a p-value of just &lt;code&gt;1.64e-46&lt;/code&gt;, &lt;code&gt;apps/data&lt;/code&gt; is very likely to be correlated to the overall app response time.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We're almost done extracting the signal from the noise. We should check to see if traffic to any views triggers slow response times.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Rinse+repeat for Django view throughputs
&lt;/h2&gt;

&lt;p&gt;A little-used, expensive view could hurt the overall response time of the app if throughput to that view suddenly increases. For example, this could happen if a user writes a script that quickly reloads an expensive view. To determine correlations we'll use the exact same process as before, just swapping in the throughput time series data for each Django view:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nPORDF0Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buttercms.com/LmHOK63ATuiFISdpwUg1" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nPORDF0Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.buttercms.com/LmHOK63ATuiFISdpwUg1" alt="undefined"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;endpoints/sparkline&lt;/code&gt; appears to have a small correlation. The p-value is &lt;code&gt;0.004&lt;/code&gt;, which means there is a 4 in 1,000 chance that there &lt;em&gt;is not&lt;/em&gt; a correlation between traffic to &lt;code&gt;endpoints/sparkline&lt;/code&gt; and the overall app response time. So, it does appear that traffic to the &lt;code&gt;endpoints/sparkline&lt;/code&gt; view triggers slower overall app response times, but it is less certain than our other two tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Using data science, we've been able to sort through far more time series metrics than we ever could with intuition. We've also been able to make our calculations without misleading trends muddying the waters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We know that our Django app response times are:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  strongly correlated to the performance of our SQL database.&lt;/li&gt;
&lt;li&gt;  strongly correlated to the response time of our &lt;code&gt;apps/data&lt;/code&gt; view.&lt;/li&gt;
&lt;li&gt;  correlated to &lt;code&gt;endpoints/sparkline&lt;/code&gt; traffic. While we're confident in this correlation given the low p-value, it isn't as strong as the previous two correlations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Now it’s time for the engineer!&lt;/strong&gt; With these insights in hand, I’d:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  investigate if the database server is being impacted by something outside of the application. For example, if we have just one database server, a backup process could slow down all queries.&lt;/li&gt;
&lt;li&gt;  investigate if the composition of the requests to the &lt;code&gt;apps/data&lt;/code&gt; view has changed. For example, has a customer with lots of data started hitting this view more? Scout's &lt;a href="http://help.apm.scoutapp.com/#trace-explorer"&gt;Trace Explorer&lt;/a&gt; can help investigate this high-dimensional data.&lt;/li&gt;
&lt;li&gt;  hold off investigating the performance of &lt;code&gt;endpoints/sparkline&lt;/code&gt; as its correlation to the overall app response time wasn't as strong.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;It's important to realize when all of that hard-earned experience doesn't work. My brain simply can't analyze thousands of time series data sets the way our data science tools can.&lt;/strong&gt; It's OK to reach for another tool.&lt;/p&gt;

&lt;p&gt;If you'd like to work through this problem on your own, check out &lt;a href="https://colab.research.google.com/drive/1VhCwtGLc-tWhB_gbGuBo_cfs7Q5J4dCI"&gt;my shared Google Colab notebook&lt;/a&gt; I used when investigating this issue. &lt;strong&gt;Just import your own data from &lt;a href="https://scoutapp.com"&gt;Scout&lt;/a&gt; next time you have a performance issue and let the notebook do the work for you!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>python</category>
      <category>django</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
