<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Developing Analytics Solutions with the Data &amp; AI Global Practice</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/data_insights_global_practice/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/data_insights_global_practice/feed.xml</link>
    <description />
    <lastBuildDate>Tue, 16 Apr 2019 04:58:05 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Can a machine learning product be trusted?</title>
      <link>https://docs.microsoft.com/archive/blogs/data_insights_global_practice/can-a-machine-learning-product-be-trusted</link>
      <pubDate>Tue, 13 Nov 2018 21:17:09 GMT</pubDate>
      <dc:creator><![CDATA[Michele Usuelli]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/data_insights_global_practice/?p=1275</guid>
      <description><![CDATA[Author: Michele Usuelli, Lead Data Scientist, Microsoft Enterprise Services

The data scientist...]]></description>
      <content:encoded><![CDATA[<span>Author: Michele Usuelli, Lead Data Scientist, Microsoft Enterprise Services</span>

The data scientist develops products based on machine learning models, having as a result an actionable answer. In other words, a machine is advising an end user about some actions to perform. Why would we trust the advice of a machine?

Before designing a machine learning product, it's crucial to keep in mind its application.

This article covers the key aspects to take into account to have a reliable product. Specifically, we will look into predictive models, aiming to foresee future events.

To be more concrete, we will be utilizing three real-life examples
<ul>
 	<li>Predictive maintenance: the engines of one or more machines break down frequently. The predictive model foresees whether there will be an engine failure in the near future.</li>
 	<li>Credit scoring: a bank provides its customers with loans. Having a new customer requesting a loan, the bank estimates the likelihood of having a default.</li>
 	<li>Sales forecast: a retailer is interested in predicting the sales volume of one or more items.</li>
</ul>
Collecting lessons learned from data science projects, common key aspects are the following.
<ul>
 	<li>Interpretability: capability of explaining the rationale behind a machine learning model.</li>
 	<li>Measurability: assessment of the performance and accuracy of the model.</li>
 	<li>Actionability: influence on the end user to perform a specific action.</li>
</ul>
Each aspect is covered by the related section.
<h2>Interpretability</h2>
Predictive models foresee the breakdown of an engine, the credit score of a customer or the sales volume of a new product. In either case, how do we know if we can trust the outcome of a model? Also, can we check if it complies to rules and abides by ethical principles?

<span>To enable that, the first step is ensuring that the model is explicable. Let's look into our examples We might predict that an</span><span> engine will break down because it's running slower than usual. A bank customer will likely default because of a high credit card debt. The sales volume will raise because of a commercial promotion. These example are close to reality, but often the set of useful information is wider. </span><span>Still aiming to explain the rationale behind its outcome, a machine learning model combines and utilizes more useful information.</span>

<span>Machine learning models are based on data sources, so  tracking all the information being used is crucial. In this way, we can easily spot ethical issues and legislation incompliances. </span><span>The next step is to identify which specific information is being used by the model and what is being discarded. Ultimately, applying the predictive model on new data,</span><span> the information being used is a small highly-relevant subset. </span>

Depending on the technique, the methodology might be different. It's usually straightforward to explain tree-based models such as the decision forest, as shown by <a href="https://blogs.msdn.microsoft.com/data_insights_global_practice/2017/08/11/demystifying-decision-forests/">my article</a>. Likewise, linear models can be easily interpreted. However, some techniques, e.g. neural networks, are too complex and obscure. Even in those cases there are workarounds such as LIME and SHAP, showing what would have changed if the data differed.
<h2>Measurability</h2>
Let's assume that a predictive model is already explainable. The thought process to define its outcome is fully transparent and we can track all the information being used. What will happen applying the model in a real-life situation? Will it be able to foresee future events accurately?

If we predict engine breakdowns, we want to know how many of them we can predict and the number of false positives. The same applies to credit risk. If we are predicting the sales volume, a metric could be the average error, whichever way the "average" is defined.

Being able to assess the reliability and accuracy of the outcome of a model is a key to be trustworthy. Clearly, it's impossible to know how a model will perform in the future. However, it's possible to know how the same methodology would have performed in the past.

Predicting something that is already known is trivial. Therefore, a common pitfall it to "cheat", either voluntarily or involuntarily. To prevent that, the key is to replicate the real life application. Some important aspects are the following.
<ul>
 	<li>Separate training and test set entirely.</li>
 	<li>Take into account the time: in most cases, we are predicting and event in time. A common pitfall is to predict the past utilizing future information.</li>
</ul>
In addition, the outcome of the testing should reflect the broader context. Key aspects are the following.
<ul>
 	<li>Business focus: in addition to measuring the accuracy, are we also translating it into measurable business KPIs?</li>
 	<li>Benchmark: if we weren't utilizing a predictive model, by how much would the forecast be worse?</li>
</ul>
My colleague Ajay published a series of two articles, showing how to measure the performance of <a href="https://blogs.msdn.microsoft.com/data_insights_global_practice/2018/08/22/measuring-model-goodness-part-1/">classification</a> and <a href="https://blogs.msdn.microsoft.com/data_insights_global_practice/2018/09/04/measuring-model-goodness-part-2/">regression</a> models.
<h2>Actionability</h2>
Last but not least, even if we can explain and measure the outcome of a machine learning product, to have a tangible impact it will trigger an action.

In the predictive maintenance scenario, the action is to either send an engineer to check the machine or to provide confidence that the machine doesn't need any maintenance. In the loan use-case, the action is to either approve or reject the loan. Predicting sales helps optimizing the production volume.

Prior to start experimenting and building the product, having in mind the end action helps defining the direction of the project. After having delivered the product, keeping track of the actions and feedback allows to improve the product further.

Actionability applies to both interpretability and measurability.
<ul>
 	<li>Interpretability: knowing the root causes allows to define an action to fix them. Not all the root causes are necessarily actionable. Knowing what can be changed is an enabler to define an action. For instance, in the predictive maintenance scenario, the action is to fix the root cause leading to a failure. If the root cause is having the last maintenance too far back in time, the solution is to send an engineer to maintain the machine.</li>
 	<li>Measurability: the estimated error is as important as the prediction. Knowing the actions that can be taken enables the definition of an accuracy metric accordingly. In the sales forecast use-case, the accuracy metric might be related to the way the forecasting will be consumed.</li>
</ul>
Actionability is the starting point and the end point of most of the data science projects.
<h2>Conclusions</h2>
This article covered interpretability, measurability and actionability, key aspects of each data science project. One remark is about other important topics, such as ethics, privacy and compliance, that will be enabled by these.]]></content:encoded>
    </item>
    <item>
      <title>How to deliver a data science project</title>
      <link>https://docs.microsoft.com/archive/blogs/data_insights_global_practice/1055</link>
      <pubDate>Tue, 04 Sep 2018 23:18:06 GMT</pubDate>
      <dc:creator><![CDATA[Michele Usuelli]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/data_insights_global_practice/?p=1055</guid>
      <description><![CDATA[Author: Michele Usuelli, Lead Data Scientist, Microsoft Enterprise Services

What is a data...]]></description>
      <content:encoded><![CDATA[Author: Michele Usuelli, Lead Data Scientist, Microsoft Enterprise Services

What is a data science project? How does it get delivered? What are the key aspects?

There is a lot of hype around data science. The purpose of this article is to provide clarity and the content comes from the successful deliver of project with Microsoft Consultancy Services.

The topic is very broad and this article highlights some key questions rather than providing answers.

To start, let's have a look at three areas of focus, covered by the related sections:
<ul>
 	<li>Who to work with and how: "Keep the key people involved".</li>
 	<li>How to build the product: "Iteratively build the product".</li>
 	<li>What are the dependencies and limitations: "Proactively highlight dependencies and limitations".</li>
</ul>
The assumptions are that the project already has a measurable business impact and a clear scope, and that the data is available and relevant.
<h2>Keep the key people involved</h2>
With "people" we are referring to specific roles in a project. There might be more than one person covering the same role whereas the same person could cover more than one role.

There are several people involved in a data science project and they belong to either of these two groups:
<ul>
 	<li><b>Delivery team</b>: people actively building the product. To keep the article simple and focused, we only mention the data scientist. Other aspects of the project, such as the IT infrastructure work, are equally important, but they're not covered for the sake of simplicity.</li>
 	<li><b>Everybody else who is involved</b>: who is related either to the context or to the outcome of the project. The key people are the stakeholder, the domain expert and the end user.</li>
</ul>
Depending on the project, the team structure might differ. However, there is always someone sponsoring the project, someone providing domain expertise and someone ultimately utilizing the product. The same person could cover more than one role, but it's easier to think in terms of people to structure how the data scientist captures different perspectives.

Let's have a closer look at the three key people:
<ul>
 	<li>The <b>stakeholder</b> is ultimately impacted by the outcome of the project. Therefore, the data scientist should get constant feedback from the stakeholder to make sure that the project is heading towards the right direction and to tune the project objective in the most impactful way. Also, to keep the stakeholder engaged in the project, it's usually a good practice to provide them with quick results driving some value or at least showing useful insights.</li>
 	<li>The subject-matter <b>expert</b> has domain knowledge related to the context of the problem and the meaning of the data. Therefore, the input of the expert should be the starting point of any data science project. Also, the data scientist will be able to discover new insights that will benefit the expert, and about which the expert will be able to provide useful advices. The checkpoints between the data scientist and the expert should be frequent.</li>
 	<li>The <b>end user</b> is the person ultimately utilizing the product. To design a solution for success, a key aspect is to be aware of its implication. Therefore, the data scientist needs the input and feedback from the end user as well.</li>
</ul>
The key questions are:
<ul>
 	<li>Do we have a stakeholder, an expert and a user actively involved in the project?</li>
 	<li>How often is the delivery team communicating with them?</li>
</ul>
<h2>Iteratively build the product</h2>
We identified three key people the data scientist interacts with. This section highlights:
<ul>
 	<li>What inputs the data scientist gets</li>
 	<li>What outcomes the data scientist gives</li>
</ul>
Some relevant aspects are:
<ul>
 	<li>Stakeholder management:
<ul style="list-style-type: circle">
 	<li>What questions are addressed by data science? What is the business impact if the questions remain unanswered?</li>
 	<li>Is there any quantifiable value or is the project mostly driven by hype?</li>
 	<li>Is data science the most appropriate methodology to answer the questions?</li>
</ul>
</li>
 	<li>Leverage the expert knowledge:
<ul style="list-style-type: circle">
 	<li>How much business expertise is the model capturing?</li>
 	<li>Do the results validate any prior business expertise? Do they highlight new areas that were previously unknown?</li>
 	<li>Is there anything contradictory in the results?</li>
</ul>
</li>
 	<li>Provide the end user with transparent results:
<ul style="list-style-type: circle">
 	<li>Is it possible to explain the overall flow of the solution in simple terms?</li>
 	<li>Can the end user trace the data sources and how they are being used?</li>
 	<li>If we weren't using a machine learning model, how would the solution design differ?</li>
</ul>
</li>
</ul>
The following chart summarizes the interactions

<a href="https://msdnshared.blob.core.windows.net/media/2018/09/delivery-people.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/delivery-people.png" alt="" width="1892" height="883" class="alignnone size-full wp-image-1225" /></a>

The information required to deliver the project often depends on the findings from the data. Typically, at the beginning of a project, the data scientist is not fully aware of all the required information. Therefore, it's crucial to keep all the three players engaged throughout the project to iteratively get more input and feedback. The product is built in small steps and in an agile manner. Having regular checkpoints with the key people ensures that the data scientist has the information needed to define the next steps.
<h2>Proactively highlight dependencies and limitations</h2>
To scope a project for success, it's crucial to proactively highlight dependencies and risks. Besides the input from the key players, data science projects require some technical input that can be summarized in these three areas:
<ul>
 	<li>Data: the sources of information being used by the model.</li>
 	<li>Techniques: the algorithms that discover patterns from the data.</li>
 	<li>Technology: the technical infrastructure to store the data and run the algorithms.</li>
</ul>
<a href="https://msdnshared.blob.core.windows.net/media/2018/09/delivery-dependencies.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/delivery-dependencies.png" alt="" width="1653" height="871" class="alignnone size-full wp-image-1235" /></a>

Each area has a strong impact on the goodness and performance of the solution. Some key questions are:
<ul>
 	<li>Data
<ul style="list-style-type: circle">
 	<li>Is the data consistent and complete? Is it possible to match the data sources?</li>
 	<li>Does the data capture most of the relevant information? What is missing?</li>
 	<li>Do we have enough history of data?</li>
</ul>
</li>
 	<li>Techniques
<ul style="list-style-type: circle">
 	<li>Are the techniques explainable?</li>
 	<li>Are the techniques simple and generic?</li>
 	<li>Do the techniques perform consistently if applied on another dataset?</li>
</ul>
</li>
 	<li>Technology
<ul style="list-style-type: circle">
 	<li>Can all the data be stored and analysed?</li>
 	<li>How fast can the solution be built and applied?</li>
 	<li>Can the data volume be handled?</li>
</ul>
</li>
</ul>
Data, techniques and technology define the boundaries of the performance. To have a reliable and trusted solution, it's crucial to highlight the boundaries since the very beginning of the project and to regularly track them.
<h2>Conclusions</h2>
The chart below summarizes the article.

<a href="https://msdnshared.blob.core.windows.net/media/2018/09/delivery-all.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/delivery-all.png" alt="" width="1871" height="1066" class="alignnone size-full wp-image-1215" /></a>
<div style="direction: ltr;margin-top: 0in;margin-left: 0in;width: 12.0006in">
<div style="direction: ltr;margin-top: 0in;margin-left: 0in;width: 12.0006in">

This article hasn't covered all the aspects. Other important topics are the IT infrastructure work and the attributes of a successful data science solution.

</div>
</div>]]></content:encoded>
    </item>
    <item>
      <title>Measuring Model Goodness - Part 2</title>
      <link>https://docs.microsoft.com/archive/blogs/data_insights_global_practice/measuring-model-goodness-part-2</link>
      <pubDate>Tue, 04 Sep 2018 17:29:57 GMT</pubDate>
      <dc:creator><![CDATA[Ajay Thampi]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/data_insights_global_practice/?p=995</guid>
      <description><![CDATA[Author: Dr. Ajay Thampi, Lead Data Scientist
Measurability is an important aspect of the Team Data...]]></description>
      <content:encoded><![CDATA[<span style="color: #99ccff"><strong>Author: Dr. Ajay Thampi, Lead Data Scientist</strong></span>
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">Measurability is an important aspect of the <a href="https://docs.microsoft.com/en-us/azure/machine-learning/team-data-science-process/overview">Team Data Science Process (TDSP)</a> as it quantifies how good the machine learning model is for the business and helps gain acceptance from the key stakeholders. In <a href="https://blogs.msdn.microsoft.com/data_insights_global_practice/2018/08/22/measuring-model-goodness-part-1/">part 1</a> of this series, we defined a template for measuring model goodness specifically looking at classification-type problems. This is summarised in the table below.</p>

<div style="direction: ltr">
<table border="1" cellpadding="0" cellspacing="0" style="direction: ltr;border-collapse: collapse;border: 1pt solid #A3A3A3" title="" summary="">
<tbody>
<tr>
<td style="vertical-align: top;width: 1.8715in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt"></p>
</td>
<td style="vertical-align: top;width: 5.7069in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">Classification</p>
</td>
</tr>
<tr>
<td style="vertical-align: top;width: 1.8715in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">Quantify Key Business Metrics</p>
</td>
<td style="vertical-align: top;width: 5.7069in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">Once the business context is understood, quantify the key business metrics as follows:</p>

<ul type="disc" style="margin-left: .375in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">The value and cost of predicting true positives/negatives and false positives/negatives, respectively. For a binary classification problem, this can be represented as a 2D value-cost matrix.</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">The percentage of cases that can be automated by the model without requiring human intervention.</span></li>
</ul>
</td>
</tr>
<tr>
<td style="vertical-align: top;width: 1.8715in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">Define Reasonable Baselines</p>
</td>
<td style="vertical-align: top;width: 5.7763in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">By doing some exploratory data analysis, define reasonable baselines as follows:</p>

<ul type="disc" style="margin-left: .375in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Random classifier: A model that determines the classes randomly.</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Majority-class classifier: A model that picks the majority class always. This is relevant when the classes are highly imbalanced like in the case of fraud/spam detection.</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Current deployed classifier: This can be a model/human that the customer has currently deployed to perform the classification task.</span></li>
</ul>
</td>
</tr>
<tr>
<td style="vertical-align: top;width: 1.8715in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">Build and Evaluate Model</p>
</td>
<td style="vertical-align: top;width: 5.7069in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">Using historical data, train a classifier and tune hyperparameters using cross-validation. Then evaluate the model on a hold-out test set using the following metrics:</p>

<ul type="disc" style="margin-left: .375in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">True Positives</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">True Negatives</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">False Positives</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">False Negatives</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Precision</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Recall</span></li>
</ul>
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">The first 4 metrics can be visualised using a confusion matrix and ROC curve. The last 2 metrics can be visualised using a PR curve.</p>
</td>
</tr>
<tr>
<td style="vertical-align: top;width: 1.8715in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">Translate Model Performance to Business Metrics</p>
</td>
<td style="vertical-align: top;width: 5.7145in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">Finally, translate the model performance metrics to the key business metrics as follows:</p>

<ul type="disc" style="margin-left: .375in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Expected value: Can be computed by using the confusion matrix and the value-cost matrix.</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">% cases to manually review: Can be computed using the precision, recall and actual positive class rate.</span></li>
</ul>
</td>
</tr>
</tbody>
</table>
In this post, we will look at regression which is another broad class of machine learning problems.

</div>
<h2>Regression</h2>
<div style="direction: ltr">Regression is the process of estimating a quantitative response. It is a supervised machine learning technique that given a new observation, predicts a real-valued output. This is done based on training data that contains observations whose real-valued outputs are known. In this post, I will look at a specific example of regression which is to predict the dollar value of a home. I will be using the <a href="https://archive.ics.uci.edu/ml/datasets/Housing">open Boston house prices dataset</a>  which consists of 506 examples, 13 numeric/categoric features such as per capita crime rate, average number of rooms per dwelling, distance to employment centres, etc. and 1 real-valued target representing the median value of a home in 1000s of dollars. More details on the dataset can be found in the Jupyter notebook that I created for this topic on <a href="https://github.com/thampiman/msdn-digp-blog/blob/master/measuring_model_goodness/Regression.ipynb">Github</a>.</div>
<div>

[caption id="attachment_1005" align="aligncenter" width="500"]<a href="https://msdnshared.blob.core.windows.net/media/2018/09/housing.jpg"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/housing-500x333.jpg" alt="" width="500" height="333" class="size-mediumlarge wp-image-1005" /></a> Photo by <a href="//www.flickr.com/photos/chodhound/7188026569/in/photolist-bXbvSz-bAeBYu-8yh7n5-5i6riJ-8LPv5N-qnVA7b-4eWN8i-adt7yN-71X9Hr-4HdY9H-7KnYuX-5wCT1n-hGkBp-eb12h-bVFgrP-3Lx4c-8MVKrD-5w7E5F-3TB71z-9yHjgR-6Q5yf8-bAGZHN-7NgrZH-oJw2SW-7hfMvB-dnvVUh-8CogSc-oPv9B-54Ghoz-4K6kJY-dCcVN-9teN1j-bBvQwA-9xKWuc-ibXzS8-adt7U7-4Cb3je-7HNmK4-4H9GHk-et89yG-cjYwF-9tUjFJ-4GVYtR-84RjMa-9udKzY-KdnM8-22DYZqC-5ZzMRJ-23AJ98M-4bsvUD">Adrian Scottow on Flickr</a>[/caption]

</div>
<div style="direction: ltr"><span style="font-family: Calibri;font-size: 11pt">The business need is to predict the home value (or a real-valued output in the general case) with as low an error as possible and to also be able to see how confident we are of our predictions. These needs can be quantified using the following metrics:</span></div>
<ul type="disc" style="margin-left: .375in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Error metrics:</span>
<ul type="circle" style="margin-left: .375in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Mean/Median Absolute Error (MAE)</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Mean/Median Absolute Percentage Error (MAPE)</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Root Mean Squared Error (RMSE)</span></li>
</ul>
</li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">x% Confidence Interval (CI) or Coefficient of Variation (CoV) for each of our estimates</span></li>
</ul>
The 3 metrics that are typically used to quantify the estimation error are MAE, MAPE and RMSE. It is important to understand the differences between them so that the appropriate metric(s) can be used for the problem at hand. Both MAE and RMSE are in the same units of the target variable and are easy to understand in that regard. For the housing price problem, these errors are expressed in dollars and can easily be interpreted. The magnitude of the error, however, cannot be easily understood using these two metrics. For example, an error of $1000 may seem small at first glance but if the actual house price is $2000 then the error is not small in relation to that. This is why MAPE is useful to understand these relative differences as the error is expressed in terms of % error. It is also worth highlighting a difference between MAE and RMSE. RMSE has a tendency to give a higher weight to larger errors - so if larger errors need to be expressed more prominently, RMSE may be the more desirable metric. The <span style="font-weight: bold">M</span><span style="font-family: Calibri;font-size: 11pt"> prefix in MAE and MAPE stands for either mean or median and they are both useful in understanding the skew in the error distributions. The mean error would be affected more by outliers than the median.</span>

The CI and CoV metrics can be used to quantify how confident we are in our estimates. By choosing an appropriate threshold for the CoV, we can determine the proportion of cases that we can predict with reasonably high certainty and therefore can potentially automate those. There are of course other ways of quantifying uncertainty [<a href="https://www.quantstart.com/articles/Bayesian-Linear-Regression-Models-with-PyMC3">1</a>, <a href="https://www.itl.nist.gov/div898/handbook/pmd/section5/pmd512.htm">2</a>, <a href="https://shodhganga.inflibnet.ac.in/bitstream/10603/139061/13/13_chapter04.pdf">3</a>] but in this post, we will only cover CI and CoV.

We now need to define reasonable baselines to compare our models to.
<ul>
 	<li><span style="font-family: Calibri;font-size: 11.0pt">Baseline 1: Overall mean (or median)</span>
<ul>
 	<li><span style="font-family: Calibri;font-size: 11.0pt">For the housing price example, this is the overall mean value of all houses in Boston.</span></li>
</ul>
</li>
 	<li><span style="font-family: Calibri;font-size: 11.0pt">Baseline 2: Mean (or median) of a particular group</span>
<ul>
 	<li><span style="font-family: Calibri;font-size: 11.0pt">For the housing price example, we can define the group as houses with a certain number of rooms. The predicted value will then be the mean value of the house with those number of rooms. We need to make sure that we have enough samples of data in each of the groups. If there aren't sufficient samples, then we should fall back to the overall mean.</span></li>
</ul>
</li>
 	<li><span style="font-family: Calibri;font-size: 11.0pt">Baseline 3: Current deployed model</span>
<ul>
 	<li><span style="font-family: Calibri;font-size: 11.0pt">Since we do not have access to this model for our example, we will drop this baseline.</span></li>
</ul>
</li>
</ul>
The next step is to train a regression model. For this example, I used a pipeline consisting of a standard scaler, PCA to potentially reduce the dimensionality of the feature vector and a <a href="https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.GradientBoostingRegressor.html">gradient boosting regressor</a><span style="font-family: Calibri;font-size: 11pt"> to finally predict the value of the house. The quantile loss function was used so that we can predict the median value (alpha = 0.5) and the lower and upper values of the confidence interval (defined by alpha of 0.05 and 0.95 respectively). The code used to build the model can be found </span><a href="https://github.com/thampiman/msdn-digp-blog/blob/master/measuring_model_goodness/Regression.ipynb">here</a><span style="font-family: Calibri;font-size: 11pt">.</span>

Once the models are trained, we then need to evaluate them. We consider the following evaluation metrics:
<ul type="disc" style="margin-left: .375in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Absolute Error (AE): This is defined as | y - ŷ |, where y is the actual value and ŷ is the estimated value</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Absolute Percentage Error (APE): This is defined as | y - ŷ | / y * 100</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Squared Error (SE): This is defined as (y - ŷ)</span><span style="vertical-align: super;font-family: Calibri;font-size: 11.0pt">2</span></li>
</ul>
We can now translate the above metrics into the final business metrics as follows:
<ul type="disc" style="margin-left: .375in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Mean/Median AE: Mean/median of the absolute errors over all examples in the test set</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Mean/Median APE: Mean/median of the absolute % errors over all examples in the test set</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">RMSE: Square root of the mean squared error over all examples in the test set</span></li>
</ul>
The above errors are visualised in the figure below.

<a href="https://msdnshared.blob.core.windows.net/media/2018/09/reg_key_perf_metrics1.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/reg_key_perf_metrics1-500x300.png" alt="" width="500" height="300" class="aligncenter size-mediumlarge wp-image-1175" /></a>

Observations:
<ul>
 	<li>The Quantile Regression (Q-Regression) model does much better than the baselines achieving the lowest values for all error metrics. The mean and median APEs for the model are 10% and 7% respectively.</li>
 	<li>The mean and median error metrics show that the distribution of the errors is positively skewed. This is also reflected in the RMSE metric where RMSE is greater than MAE showing the effect of the larger errors.</li>
</ul>
In order to get a better understanding of the distribution of the errors, it is useful to plot the cumulative distribution of the APE. This is shown below.  It can be seen that for the Q-Regression model, 90% of the cases achieve an error of less than 20%. For baselines 1 and 2, we only get roughly 60% and 50% of the cases respectively, with less than 20% error.

<a href="https://msdnshared.blob.core.windows.net/media/2018/09/ape_cdf.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/ape_cdf-500x327.png" alt="" width="500" height="327" class="aligncenter size-mediumlarge wp-image-1025" /></a>Finally, in order to determine which cases we can accurately estimate and in turn automate, we need to look at the confidence interval (CI) and the coefficient of variation (CoV). The 95% CI is estimated using the Q-Regression model by setting the quantile parameter alpha to 0.05 and 0.95. This gives us a rough measure of the spread of the estimate value. We can then compute a rough measure of the CoV by dividing the spread by the median predicted value. We expect to see a higher CoV for an estimate that we are highly uncertain of. Ideally, the CoV will also be correlated with the error metric but that need not always be the case. By setting a threshold on the CoV, we can determine how many cases we can predict with reasonably high certainty and thereby, automate.

In this example, a CoV threshold of 0.625 is chosen. This results in automating roughly 50% of the cases with reasonably high certainty in our estimate value. The figure below shows the box plots of the APE metric for all cases and the 'automatable' cases.

<a href="https://msdnshared.blob.core.windows.net/media/2018/09/reg_automation.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/09/reg_automation-500x310.png" alt="" width="500" height="310" class="aligncenter size-mediumlarge wp-image-1035" /></a>
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">Observations:</p>

<ul type="disc" style="margin-left: .375in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">The 50% cases identified as 'automatable' have much lower errors than all the cases.</span>
<ul type="circle" style="margin-left: .375in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Mean APE drops from 10% to 6%</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Median APE drops from 7% to 5%</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Maximum APE drops from 92% to 17%</span></li>
</ul>
</li>
</ul>
This concludes the two-part series on measuring model goodness. We've looked at measurability through the lens of the business and defined a template for the entire process for two broad classes of machine learning problems - classification and regression. This is summarised in the table below. For the sake of completeness, the classification column is copied from the table above.
<div style="direction: ltr">
<table border="1" cellpadding="0" cellspacing="0" style="direction: ltr;border-collapse: collapse;border: 1pt solid #A3A3A3" title="" summary="">
<tbody>
<tr>
<td style="vertical-align: top;width: 2.159in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt"></p>
</td>
<td style="vertical-align: top;width: 2.4756in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">Classification</p>
</td>
<td style="vertical-align: top;width: 2.7027in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">Regression</p>
</td>
</tr>
<tr>
<td style="vertical-align: top;width: 2.1104in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">Quantify Key Business Metrics</p>
</td>
<td style="vertical-align: top;width: 2.5729in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<ul style="margin-left: .2055in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="list-style-type: none">
<ul type="disc" style="margin-left: 0in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">The value and cost of predicting true positives/negatives and false positives/negatives, respectively. For a binary classification problem, this can be represented as a 2D value-cost matrix.</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">The percentage of cases that can be automated by the model without requiring human intervention.</span></li>
</ul>
</li>
</ul>
</td>
<td style="vertical-align: top;width: 2.9125in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<ul style="margin-left: .2055in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="list-style-type: none">
<ul type="disc" style="margin-left: 0in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Error metrics, where a lower value is preferred:</span>
<ul type="disc" style="margin-left: .375in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Mean/median absolute error (MAE)</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Mean/median absolute % error (MAPE)</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Root mean squared error (RMSE)</span></li>
</ul>
</li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Measure of confidence or certainty for the predictions. In this post, we looked at confidence interval (CI) and coefficient of variation (CoV).</span></li>
</ul>
</li>
</ul>
</td>
</tr>
<tr>
<td style="vertical-align: top;width: 2.0812in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">Define Reasonable Baselines</p>
</td>
<td style="vertical-align: top;width: 2.5263in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<ul style="margin-left: .2055in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="list-style-type: none">
<ul type="disc" style="margin-left: 0in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Random classifier: A model that determines the classes randomly.</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Majority-class classifier: A model that picks the majority class always. This is relevant when the classes are highly imbalanced like in the case of fraud/spam detection.</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Current deployed classifier: This can be a model/human that the customer has currently deployed to perform the classification task.</span></li>
</ul>
</li>
</ul>
</td>
<td style="vertical-align: top;width: 2.9013in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<ul style="margin-left: .2055in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="list-style-type: none">
<ul type="disc" style="margin-left: 0in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Overall mean: This is the overall mean value of the target variable.</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Mean value by group: This is the mean value of the target variable for a particular group. Need to make sure that there are enough samples for the groups of interest. If not, then fall back to the overall mean.</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Current deployed model: This can be a model/human that the customer has currently deployed to perform the predictions.</span></li>
</ul>
</li>
</ul>
</td>
</tr>
<tr>
<td style="vertical-align: top;width: 2.159in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">Build and Evaluate Model</p>
</td>
<td style="vertical-align: top;width: 2.4951in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">Using historical data, train a classifier and tune hyperparameters using cross-validation. Then evaluate the model on a hold-out test set using the following metrics:</p>

<ul type="disc" style="margin-left: .375in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">True Positives</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">True Negatives</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">False Positives</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">False Negatives</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Precision</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Recall</span></li>
</ul>
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">The first 4 metrics can be visualised using a confusion matrix and ROC curve. The last 2 metrics can be visualised using a PR curve.</p>
</td>
<td style="vertical-align: top;width: 2.7305in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">Using historical data, train a classifier and tune hyperparameters using cross-validation. Then evaluate the model on a hold-out test set using the following metrics:</p>

<ul type="disc" style="margin-left: .375in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Absolute Error (AE)</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Absolute % Error (APE)</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Squared Error (SE)</span></li>
</ul>
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">The distributions of these metrics can be visualised using a PDF or CDF.</p>
</td>
</tr>
<tr>
<td style="vertical-align: top;width: 2.0916in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<p style="margin: 0in;font-family: Calibri;font-size: 11.0pt">Translate Model Performance to Business Metrics</p>
</td>
<td style="vertical-align: top;width: 2.5534in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<ul style="margin-left: .2055in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="list-style-type: none">
<ul type="disc" style="margin-left: 0in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Expected value: Can be computed by using the confusion matrix and the value-cost matrix.</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">% cases to manually review: Can be computed using the precision, recall and actual positive class rate.</span></li>
</ul>
</li>
</ul>
</td>
<td style="vertical-align: top;width: 2.9125in;padding: 4pt 4pt 4pt 4pt;border: 1pt solid #A3A3A3">
<ul style="margin-left: .2055in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="list-style-type: none">
<ul type="disc" style="margin-left: 0in;direction: ltr;margin-top: 0in;margin-bottom: 0in">
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Error metrics: Can be easily computed by taking the mean and median of the above metrics.</span></li>
 	<li style="margin-top: 0;margin-bottom: 0;vertical-align: middle"><span style="font-family: Calibri;font-size: 11.0pt">Confidence measure: Can estimate the CI for each estimate using quantile regression. A rough measure of CoV can be computed using the CI and the median predicted value. By setting a CoV threshold, we can determine the % of cases that can be 'automatable'.</span></li>
</ul>
</li>
</ul>
</td>
</tr>
</tbody>
</table>
Hope you enjoyed this series. Thanks for reading!

</div>]]></content:encoded>
    </item>
    <item>
      <title>Measuring Model Goodness - Part 1</title>
      <link>https://docs.microsoft.com/archive/blogs/data_insights_global_practice/measuring-model-goodness-part-1</link>
      <pubDate>Wed, 22 Aug 2018 16:30:43 GMT</pubDate>
      <dc:creator><![CDATA[Ajay Thampi]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/data_insights_global_practice/?p=785</guid>
      <description><![CDATA[Author: Dr. Ajay Thampi, Lead Data Scientist

Data and AI are transforming businesses worldwide...]]></description>
      <content:encoded><![CDATA[<span style="color: #99ccff"><strong>Author: Dr. Ajay Thampi, Lead Data Scientist</strong></span>

Data and AI are transforming businesses worldwide from finance, manufacturing and retail to healthcare, telecommunications and education. At the core of this transformation is the ability to convert raw data into information and useful, actionable insights. This is where data science and machine learning come in.

Building a good and reliable machine learning system requires us to be methodical by:
<ul>
 	<li>understanding the business needs</li>
 	<li>acquiring and processing the relevant data</li>
 	<li>accurately formulating the problem</li>
 	<li>building the model using the right machine learning algorithm</li>
 	<li>evaluating the model, and</li>
 	<li>validating the performance in the real world before finally deploying it</li>
</ul>
This entire process has been documented as the <u><a href="https://docs.microsoft.com/en-us/azure/machine-learning/team-data-science-process/overview">Team Data Science Process (TDSP)</a></u> at Microsoft, captured in the figure below.

<a href="https://msdnshared.blob.core.windows.net/media/2018/08/tdsp.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/08/tdsp-500x348.png" alt="" width="600" height="418" class="aligncenter wp-image-825" /></a>

This post, as part of a two-part series, is focused on measuring model goodness, specifically looking at quantifying business value and converting typical machine learning performance metrics (like precision, recall, RMSE, etc.) to business metrics. This is typically how models are validated and accepted in the real world. The relevant stages of the process are highlighted in red in the figure above. Measuring model goodness also involves comparing the model performance to reasonable baselines, which will also be covered in this post. To illustrate all of this, two broad classes of machine learning problems will be looked at:
<ul>
 	<li>Classification: Covered in Part 1</li>
 	<li>Regression: To be covered in <a href="https://blogs.msdn.microsoft.com/data_insights_global_practice/2018/09/04/measuring-model-goodness-part-2/">Part 2</a></li>
</ul>
<h2><strong>Classification</strong></h2>
Classification is the process of predicting qualitative or categorical responses [<span><a href="https://web.stanford.edu/~hastie/ElemStatLearn/">source</a></span>]. It is a supervised machine learning technique that classifies a new observation to a set of discrete categories. This is done based on training data that contains observations whose categories are known. Classification problems can be <strong>binary </strong>in which there are two target categories or <strong>multi-class</strong> in which there are more than two mutually exclusive target categories.

In this post, I will look at the binary classification problem taking breast cancer detection as a concrete example. I will be using the open breast cancer Wisconsin <u><a href="https://archive.ics.uci.edu/ml/datasets/Breast+Cancer+Wisconsin+(Diagnostic)">dataset</a></u> for model building and evaluation. The techniques discussed below can easily be extended to other binary and multi-class classification problems. All the source code used to build and evaluate the models can be found on Github <span><a href="https://github.com/thampiman/msdn-digp-blog/tree/master/measuring_model_goodness">here</a></span>.

Let’s first understand the business context and the data. Breast cancer is the most common cancer in women and is the main cause of death from cancer among women [<span><a href="https://www.nature.com/articles/s41598-018-22437-z">source</a></span>]. Early detection of the cancer type — whether it is benign or malignant — can help save lives by picking the appropriate treatment strategy. In this dataset, there are 569 cases in total out of which 357 are benign (62.6%) and 212 are malignant (37.4%). There are 30 features in total, computed from a digitised image of a biopsy of a breast mass. These features describe the characteristics of the nuclei like radius, texture, perimeter, smoothness, etc. The detection of the type of cancer is currently being done by a radiologist which is time consuming and the business need is to speed up the diagnosis process so that the appropriate treatment can be started early. Before diving too deep into the modelling stage, we need to understand the value/cost of making the right/wrong prediction. This information can be represented using a <strong>value-cost matrix</strong> as shown below:

<a href="https://msdnshared.blob.core.windows.net/media/2018/08/vc_matrix.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/08/vc_matrix-300x170.png" alt="" width="188" height="107" class=" wp-image-855 aligncenter" /></a>

The rows in the matrix represent the actual class and the columns represent the predicted class. The first row/column represents in the negative class (benign in this case) and the second row/column represents the positive class (malignant in this case). Here's what each of the elements in the matrix represents:
<ul>
 	<li>Row 0, Column 0: Value of predicting the negative class correctly</li>
 	<li>Row 0, Column 1: Cost of predicting the positive class wrongly</li>
 	<li>Row 1, Column 0: Cost of predicting the negative class wrongly</li>
 	<li>Row 1, Column 1: Value of predicting the positive class correctly</li>
</ul>
For breast cancer detection, we can define the value-cost matrix as follows:

<a href="https://msdnshared.blob.core.windows.net/media/2018/08/value_cost_matrix.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/08/value_cost_matrix-300x268.png" alt="" width="300" height="268" class="size-medium wp-image-845 aligncenter" /></a>
<ul>
 	<li>Detecting benign and malignant cases correctly are given equal and positive value. Although the malignant type is the worse situation to be in for the patient, the goal is to diagnose early, start treatment and cure both types of cancer. So, from a treatment perspective, detecting both cases accurately have equal value.</li>
 	<li>The cost of flagging a malignant case as benign (false negative) is much worse than flagging a benign case as malignant (false positive). Therefore, the false negative is given a cost of -4 and the false positive is given a cost of -2.</li>
</ul>
Another business need is to <strong>automate</strong> as much of the process as possible. One way of doing this is to use the machine learning model as a filter, to automate the detection of simpler benign cases and only flag the possible malignant cases for the radiologist to review. This is shown in the diagram below. We can quantify this business need by looking at the <strong>% of cases to review manually </strong>(denoted as <em>x</em> in the figure). Ideally, we want to automate everything without requiring the human in the loop. This means that the model should have 100% accuracy and <em>x</em> should be equal to the actual proportion of positive classes.

<a href="https://msdnshared.blob.core.windows.net/media/2018/08/automation.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/08/automation-500x133.png" alt="" width="500" height="133" class="aligncenter wp-image-875 size-mediumlarge" /></a>

After the business needs are properly quantified, the next step is to define <strong>reasonable baselines</strong> to compare our models to.
<ul>
 	<li>Baseline 1: Random classifier that decides randomly if the cancer type is benign / malignant</li>
 	<li>Baseline 2: Majority-class classifier that picks the majority class all the time. In this case, the majority class is benign. This strategy would make much more sense if the classes were heavily imbalanced.</li>
</ul>
We could add a <em>third baseline</em> which is the performance of the radiologist or any other model that the business has currently deployed. In this example though, this information is not known and so this baseline is dropped.

Now comes the fun part of building the model. I will gloss over a lot of the detail here, as an entire post may be necessary to go through the exploratory analysis, modelling techniques and best practices. I used a pipeline consisting of a feature scaler, PCA for feature reduction and finally a <strong>random forest (RF) classifier</strong>. 5-fold cross validation and grid search were done to determine the optimum hyperparameters. The entire source code can be found <span><a href="https://github.com/thampiman/msdn-digp-blog/blob/master/measuring_model_goodness/Classification.ipynb">here</a></span>.

Once the models are trained, the next step is to evaluate them. There are various performance metrics that can be used but, in this post, I will go over the following 6 metrics as they can very easily be explained, visualised and translated into business metrics
<ul>
 	<li><strong>True Positives (TP)</strong> / <strong>True Positive Rate (TPR)</strong>: Number of correct positive predictions / Probability of predicting positive given that the actual class is positive</li>
 	<li><strong>False Negatives (FN)</strong> / <strong>False Negative Rate (FNR)</strong>: Number of wrong negative predictions / Probability of predicting negative given that the actual class is positive</li>
 	<li><strong>True Negatives (TN)</strong> / <strong>True Negative Rate (TNR)</strong>: Number of correct negative predictions / Probability of predicting negative given that the actual class is negative</li>
 	<li><strong>False Positives (FP)</strong> / <strong>False Positive Rate (FPR)</strong>: Number of wrong positive predictions / Probability of predicting positive given that the actual class is negative</li>
 	<li><strong>Precision (P)</strong>: Proportion of predicted positives that are correct</li>
 	<li><strong>Recall (R)</strong>: Proportion of actual positives captured</li>
</ul>
All these metrics are inter-related. The first four can be easily visualised using a <span><a href="https://en.wikipedia.org/wiki/Receiver_operating_characteristic">ROC curve</a></span> and a <span><a href="https://en.wikipedia.org/wiki/Confusion_matrix">confusion matrix</a></span>. The last two can be visualised using a <span><a href="https://en.wikipedia.org/wiki/Precision_and_recall">precision-recall</a></span> (PR) curve. Let's first visualise the performance of the model and the baselines using the ROC and PR curves.

<a href="https://msdnshared.blob.core.windows.net/media/2018/08/roc_auc.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/08/roc_auc-486x350.png" alt="" width="486" height="350" class="aligncenter wp-image-885 size-mediumlarge" /></a> <a href="https://msdnshared.blob.core.windows.net/media/2018/08/pr_curve.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/08/pr_curve-494x350.png" alt="" width="494" height="350" class="aligncenter wp-image-895 size-mediumlarge" /></a>

Both plots show the performance of the models using various thresholds to determine the positive and negative classes. As can be seen, the RF classifier outperforms the baselines in every respect. Once the appropriate threshold is picked, we can plot the normalised confusion matrices as shown below. These matrices show the conditional probabilities of predicted values given the actual value. We can see that the baselines do quite poorly especially in predicting the positive class — the false negative rates are quite high. The RF classifier, on the other hand, seem to get much more of the positive and negative class predictions right achieving a TPR of 93% and TNR of 97%.

<a href="https://msdnshared.blob.core.windows.net/media/2018/08/confusion_matrices.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/08/confusion_matrices.png" alt="" width="500" height="440" class="aligncenter wp-image-905" /></a>

Now that we’ve established that the new RF model outperforms the baselines, we still need to make sure that it meets our business objectives. i.e. high positive business value and fewer cases to review manually. We therefore need to translate the above performance metrics into the following:
<ul>
 	<li>Total expected value/cost of the model</li>
 	<li>% of cases that need to be reviewed manually</li>
</ul>
The first business metric can be computed by taking the dot product of the flattened confusion matrix (normalised by the total number of samples, <em>S</em>) with the flattened value-cost matrix. This is shown below.

<a href="https://msdnshared.blob.core.windows.net/media/2018/08/expected_value.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/08/expected_value-300x85.png" alt="" width="300" height="85" class="aligncenter wp-image-915 size-medium" /></a>

Note that <em>S</em> is a scalar equal to TN + FP + FN + TP. The expected value is essentially the weighted average of the values/costs, where the weights are the probabilities of each of the four outcomes [<span><a href="https://www.data-science-for-biz.com/DSB/Home.html">source</a></span>].

The second business metric can be computed using precision and recall as follows.

<a href="https://msdnshared.blob.core.windows.net/media/2018/08/automation_formula1.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/08/automation_formula1-500x53.png" alt="" width="400" height="42" class="aligncenter wp-image-955" /></a><a href="https://msdnshared.blob.core.windows.net/media/2018/08/automation_formula.png"></a>

The positive class rate is known from the data used to evaluate the model, i.e. the test set. Based on the business needs, we then need to decide how much of those positive classes we must accurately determine, i.e. the recall. If we want to detect all the positive cases, the target recall should be 100%. We can then find the corresponding precision (and threshold) from the PR curve. For the ideal model with 100% precision and recall, the proportion of positive cases to review would be equal to the actual positive class rate. This means we could, in theory, achieve 100% automation without a human in the loop.

The following two plots show the final evaluation of the model goodness in terms of the two key business metrics.

<a href="https://msdnshared.blob.core.windows.net/media/2018/08/value_cost_models.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/08/value_cost_models-500x332.png" alt="" width="500" height="332" class="aligncenter wp-image-935 size-mediumlarge" /></a> <a href="https://msdnshared.blob.core.windows.net/media/2018/08/automation1.png"><img src="https://msdnshared.blob.core.windows.net/media/2018/08/automation1-467x350.png" alt="" width="467" height="350" class="aligncenter wp-image-945 size-mediumlarge" /></a>

Observations:
<ul>
 	<li>Both baseline models have quite large costs because of the high false negatives. The random forest classifier has a good positive value by getting a lot of the positive and negative cases right.</li>
 	<li>For the case of breast cancer detection, we want to capture all the positive/malignant cases, i.e. 100% recall. Looking at 50% or 75% recall does not make sense in this example because of the high cost of not treating the malignant cases. For other binary classification problems like fraud detection for instance, a lesser recall may be acceptable so long as the model can flag the high cost-saving fraudulent cases. Regardless, we can see that the RF classifier outperforms the baselines on the automation front as well. To capture 100% of the malignant cases, the radiologist would only need to review about 50% of all cases, i.e. 13% additional false positives, whereas he/she would have to review almost all the cases using the baseline models.</li>
</ul>
In conclusion, the first part of this series has looked at measuring model goodness through the lens of the business, specifically looking at classification-type problems. For further details on this subject, the books below are great references. In the <a href="https://blogs.msdn.microsoft.com/data_insights_global_practice/2018/09/04/measuring-model-goodness-part-2/">second and final part</a> of this series, I will cover regression which requires looking at slightly different metrics.

<strong>Further Reading</strong>
<ul>
 	<li><span><a href="https://web.stanford.edu/~hastie/ElemStatLearn/">Elements of Statistical Learning</a></span>, by Trevor Hastie, Robert Tibshirani, Jerome Friedman</li>
 	<li><span><a href="https://www.data-science-for-biz.com/DSB/Home.html">Data Science for Business</a></span>, by Foster Provost and Tom Fawcett</li>
 	<li><span><a href="https://shop.oreilly.com/product/0636920052289.do">Hands-On Machine Learning with Scikit-Learn and Tensorflow</a></span>, by Aurélien Géron</li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title>Demystifying decision forests</title>
      <link>https://docs.microsoft.com/archive/blogs/data_insights_global_practice/demystifying-decision-forests</link>
      <pubDate>Fri, 11 Aug 2017 14:22:03 GMT</pubDate>
      <dc:creator><![CDATA[Michele Usuelli]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/data_insights_global_practice/?p=685</guid>
      <description><![CDATA[By Michele Usuelli, Lead Data Scientist
		
 This article doesn't require a data science background,...]]></description>
      <content:encoded><![CDATA[<h1><span style="color:#454545"><span style="font-family:Segoe UI;font-size:10pt;background-color:white">By Michele Usuelli, Lead Data Scientist</span>
		</span></h1><p>
 </p><p>This article doesn't require a data science background, but just some basic understanding of predictive analytics. Besides that, all the concepts are explained from scratch, including a popular algorithm called the "decision forest". Throughout the article you won't see any fancy or advanced machine learning algorithm, but by the end you will have more clarity about how decision forests work.
</p><p>Dealing with predictive analytics, decision forests are popular techniques as they usually produce satisfying results requiring a little data preparation. Decision forests, like other machine learning models, are often perceived as obscure in the way they operate. This leads to the misconception that artificial intelligence consists in building a machine doing some magic on the data.
</p><p>The scope of this article is to demystify this and to prove that decision forests operate in a simple transparent way. Also, their way of operating is somehow similar to the human approach to decision-making.
</p><p>To demonstrate this, we use as an example a mock use-case based on simulated data. Even if the solution makes sense from a logical standpoint, its assumptions are not necessarily related to reality. A customer of a bank requests a mortgage and the bank must decide whether to give them a loan or not. How does the decision-making process work?
</p><p>The bank can use some information related to the customer such as their credit score, age, income, excluding everything that could lead to discrimination or legal issues. Starting from historical data about other customers, the bank knows who couldn't pay off the mortgage. If it's possible to map out the profile of insolvent customers, the bank can come up with a way of identifying them.
</p><p>The bank has already put together some information about their previous customers into a nice structured table. Also, to make everything easier, around half of the customers failed. Assuming that we are in the utopian scenario of having the data nice and clean, this is the table. 
</p><p style="background: #f8f8f8"><span style="font-family:Consolas;font-size:11pt">##   creditScore houseAge yearsEmploy ccDebt year default    id<br />## 1         708       21           5   4190 2002       0 26472<br />## 2         649       20           3   2151 2003       0 37088<br />## 3         725       23           3   3603 2005       0 57076<br />## 4         785       24           4   5534 2009       0 90648<br />## 5         649       14           6   5243 2002       0 20110<br />## 6         734       21           0   3441 2008       0 89639
</span></p><p>The columns are
</p><ul><li>Credit score: score measuring the "reliability" of the customer, assessed by a third party
</li><li>House age: age of the house they're purchasing
</li><li>Years of employment
</li><li>Debt on the credit card
</li><li>Year in which they purchased the house
</li><li>Failure: 1 if the customer didn't pay off the loan, 0 otherwise
</li></ul><p>Out of the five personal details, that we can call "features", which is the most relevant? After having identified it, we can divide the customers into two groups based on the most relevant feature. Then, for each of the two groups, we repeat the process of finding the most relevant feature and we split the customers into two sub-groups. A popular algorithm, called decision tree, works exactly in this way. In the end, the tree divides the customers into eights groups and we know the percentage of customers experiencing a default per group. We can visualise it through the following chart.
</p><p><img src="https://msdnshared.blob.core.windows.net/media/2017/08/081117_1422_Demystifyin11.png" alt="" />
	</p><p>
 </p><p>At each split of the tree, the first number is a score representing the portion of customer failing. Since 49% of the customer had a default, the initial score is 0.49. Also, the colour of the boxes gets darker as the score gets higher. The second number represents the percentage of customers belonging to the group. At the beginning, it's 100%. Then, at each level, it shows the proportion of customers into each split. 
</p><p>Not surprisingly, the most relevant indicator is the debt on the credit card. 6.6% of the customers having a debt below 6755 experienced a failure, opposed to 82% of the customers having a debt equal to or above 6755.
</p><p>The tree visualization shows in a simple way how the model works. Having a new customer, the bank can estimate its propensity of not paying off the debt. How does it work? Not having any information about a customer, its probability of failing is the percentage of failures in the past. In this case, it's 49%. Then, knowing the credit card debt, we go down to the second level of the tree and the score can either increase or decrease, depending on the impact of credit card debt. The impact of knowing the credit card debt is:
</p><ul><li>if below ccDebt &lt; 6755, the score becomes 6.6%, so it gets decreased by 42.4%
</li><li>if above ccDebt &lt;= 6755, the score becomes 82%, so it gets increased by 33%
</li></ul><p>Summarising, this is the impact:
</p><ul><li>ccDebt &lt; threshold: -0.424
</li><li>ccDebt &lt;= threshold: +0.33
</li></ul><p>In the same way, we can measure the impact of being above or below the threshold of other features. If the same feature is being used more than once, we can simply sum up the effects. Depending on the customer, the path on the tree is different, so the features being used and their impact changes. To keep track of that, we can sum up the effects separately for each customer and visualise them through a <strong>decision-making heatmap</strong>. Each row corresponds to a customer and each column correspond to being above or below a threshold for a given features (e.g., ccDebt &lt; any threshold at some point in the tree). The colour is blue if the score decreases and red if it increases, and the less transparent it is, the higher the impact.
</p><p><img src="https://msdnshared.blob.core.windows.net/media/2017/08/081117_1422_Demystifyin21.png" alt="" />
	</p><p>Measuring the impact of each feature, the bank can explain the decision-making process to assign each score, given more transparency and clarity to its customers.
</p><p>As shown above, a decision tree operates in a way that is like the human problem solving. Coming back to the original topic, we want to demystify decision forests. As the name suggests, a decision forest is a combination of decision trees.
</p><p>A hundred people can access some information of the bank, but each person is aware of a part of the customers and of a part of their features. If we ask each of them to build a decision tree, we'll have 100 different trees. Applying the trees on a new customer, each of them will assign a different score. To come up with a unique answer, we can compute the average score. That's exactly how a decision forest works: we build decision trees on a random subset of the information, then we combine them. This methodology, called "ensembling", usually improves the performance of the solution.
</p><p>Since there might be several trees in the forest, it'll be time-consuming to have a look at each tree separately. However, it's still possible to measure the impact of each feature across all the trees. Combining them, we can build a decision-making heatmap taking account of everything. Therefore, the bank can explain the decision-making process of the entire forest.
</p><p>The methodology shows by the article gives transparency and clarity about the way random forests operate. A similar approach is applicable in other contexts and it allows to give more clarity about how machine learning operates.
</p><p>
 </p>]]></content:encoded>
    </item>
    <item>
      <title>What is the role of a data scientist?</title>
      <link>https://docs.microsoft.com/archive/blogs/data_insights_global_practice/what-is-the-role-of-a-data-scientist</link>
      <pubDate>Thu, 23 Feb 2017 15:48:55 GMT</pubDate>
      <dc:creator><![CDATA[Michele Usuelli]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/data_insights_global_practice/?p=665</guid>
      <description><![CDATA[By Michele Usuelli, Lead Data Scientist

Data Science has been around for decades, but it recently...]]></description>
      <content:encoded><![CDATA[By <span>Michele Usuelli, Lead Data Scientist</span>

Data Science has been around for decades, but it recently increased in popularity among companies. Although the tools and techniques existed already, there are some changes. Digital technologies generate more data that can drive new advanced analytics use-cases. Also, there are more success stories show-casing the value in data, making companies more keen to invest resources into new solutions. Because of the hype, phrases like "Data Science" and "Big Data" became buzzwords. However, their meaning is loosely defined and it's not entirely clear to many businesses. From a practical perspective, what is data science? Is the role of the data scientist the same as the statistician, or are there new challenges?

The scope of this article is to understand what the role of a data scientist consists in. Depending on the company and on the specific role, there are lots of differences, so it's challenging to come out with a universal profile. However, it's still possible to understand the role from a high-level perspective. Breaking down the core responsibilities of a data scientist, we can have a better understanding of the related skillset.

The starting point of a data science project is "<strong>why</strong>". Why does the company need an advanced analytics solution? Why is it willing to allocate a budget? Why will the project have an impact? To address these queries, the data scientist is capable of understanding the business context, brainstorm solutions, and identify what's valuable. The related skills are business acumen, experience in applying a solution in a specific industry, and soft skills like stakeholder management and listening. Being knowledgeable about the field is definitely useful although not mandatory, since data scientists can interact with subject matter experts to get the information they need. Therefore, the core skill is being capable of collecting business information and defining advanced analytics use-cases accordingly.Also, the data scientist should be able to present the solution and its value, and good presentation skills help in that.

After having defined the target, the next question is "<strong>what</strong>". What do we need to do to solve the challenge? What techniques can we use? What are the main steps from the current situation to the final solution? To address these queries, the data scientist should be able to define the logical steps to build an end-to-end solution. The required knowledge is about statistics, data processing, machine learning techniques, model validation. However, being knowledgeable about the separate steps is not enough as the data scientist needs to be capable of designing an end-to-end solution that every time is different depending on the data and on the target. The main challenges are to
<ul>
 	<li>prepare the data: blend the original data sources, structure the data in the required format</li>
 	<li>apply a machine learning model: define the machine learning model addressing the business challenge</li>
 	<li>validate the model: according to the context, define a meaningful way to measure the success of the model</li>
</ul>
Each step requires thinking outside the box and using common sense, in addition to some knowledge about the techniques.Knowing what are the logical step of an advanced analytics solution doesn't imply being able to build it. The final question is "<strong>how</strong>". How can we implement the solution? How can we put the data together? How can we prototype and deploy the solution?

This part is more technical and the skillset is diverse. The main areas are
<ul>
 	<li>dealing with data challenges: the data can be incomplete, have a large volume, have a challenging structure (e.g., text, images). The data scientist should be able to identify and use the tools required by the data. For example, some tools included into the Hadoop ecosystem became popular recently.</li>
 	<li>coding: although there are pre-built high-level tools, every data science solution is unique and it will involve some coding. Being able to use specific languages like R and Python is useful, but not always necessary. The core skill is knowing how to code, no matter the language. In presence of large amount of data, it's also useful to be able to design parallel algorithms to scale across large data volumes.</li>
 	<li>organising the data into databases: knowing how to store and organise the data, and extract the relevant information. This part is performed using SQL/NoSQL databases although managing them can be out of the scope of a data scientist.</li>
 	<li>prototyping: quickly build a good-enough solution that works.</li>
 	<li>deploying: put the solution into production.</li>
</ul>
&nbsp;

The technical skills depend a lot on the context, so there is more diversity in the "how" area.

The data science process requires a broad expertise and the data scientist can’t go very deeply into each component of the solution. That’s especially true for data scientist consultants, given that they join new projects where the customer has already a deep knowledge about the context and the tools. To design and build the solution, the data scientist needs to interact with
<ul>
 	<li>Customer stakeholders: to define the scope of the solution and to measure its success, the data scientist should have a conversation with the stakeholders.</li>
 	<li>Customer subject-matter experts (SMEs): data science often consists in improving an existing solution using advanced analytics methodologies, so the starting point is to understand the current solution and use it as a starting point. Also, the data scientist needs to know how to interpret the data. To get some help on that, the data scientist needs to work close to the SMEs.</li>
 	<li>Academic researchers: although data scientists are experts of machine learning techniques, their knowledge is not as deep as academic researchers. Also, data scientists are focused on bringing value to projects, so they often don’t have enough time to develop complex algorithms. The most common way to work together with academic researches is to use tools developed by open-source community. A good example is CRAN providing with its R 10000+ packages, providing the most cutting edge statistical tools and machine learning techniques. Also, in larger projects there might be some researchers working together on the machine learning part of the solution.</li>
 	<li>Solution architects: the data scientist defines and builds advanced analytics models that are utilised by the solution. Usually an architect designs the overall solution, taking into consideration the business context and the technological infrastructure.</li>
 	<li>Data engineers: the “how” part of the engagement is usually the most time consuming and it required a broad range of skills. Data engineers help building and deploying the solution, designing the pipeline translating the data into actions and insights. In some engagement, the data scientists build the prototype and the data engineers put it into production.</li>
 	<li>Software SMEs: if the solution integrates other technologies, there might be other people involved, especially solution architects.</li>
</ul>
This article shows what's common across most of the data scientists and aims to provide more clarity about the role. Depending on the specific case, the skillset can be more detailed and it varies a lot depending on the industry, seniority, level, team. Also, in larger teams there will be people specialised in different aspects of the solution, so it's less important to have a person having the full skillset.

&nbsp;

<span> </span>]]></content:encoded>
    </item>
    <item>
      <title>Scaling up Scikit-Learns Random Projection using Apache Spark</title>
      <link>https://docs.microsoft.com/archive/blogs/data_insights_global_practice/scaling-up-scikit-learns-random-projection-using-apache-spark</link>
      <pubDate>Thu, 16 Feb 2017 18:07:32 GMT</pubDate>
      <dc:creator><![CDATA[Yaswant.v]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/data_insights_global_practice/?p=646</guid>
      <description><![CDATA[By Sashi Dareddy, Lead Data Scientist


What is Random Projection (RP)?


Random Projection is...]]></description>
      <content:encoded><![CDATA[<span style="color: #4f81bd;font-size: 12pt">By Sashi Dareddy, Lead Data Scientist
</span>

<span style="color: #4f81bd;font-size: 12pt">What is Random Projection (RP)?
</span>

<span style="font-family: Cambria;font-size: 12pt">Random Projection is a mathematical technique to reduce the dimensionality of a problem much like Singular Value Decomposition (SVD) or Principal Component Analysis (PCA) but only simpler &amp; computationally faster.
</span>

<span style="font-family: Cambria;font-size: 12pt">[Throughout this article, I will use Random Projection and Sparse Random Projection interchangeably.]
</span>

<span style="font-family: Cambria;font-size: 12pt">It is particularly useful when:
</span>
<ul>
 	<li><span style="font-family: Cambria;font-size: 12pt">Calculating PCA/SVD can be very prohibitive in terms of both running time &amp; memory/hardware constraints.
</span></li>
 	<li><span style="font-family: Cambria;font-size: 12pt">Working with extremely large dimensional <strong><em>sparse</em></strong> datasets with both large <strong>n</strong> (&gt;&gt; 100million rows/samples) &amp; large <strong>m</strong> (&gt;&gt; 10 million columns/features) in a distributed setting.
</span></li>
 	<li><span style="font-family: Cambria;font-size: 12pt">When you require sparsity in the low-dimensional projected data
</span></li>
</ul>
<span style="color: #4f81bd;font-size: 12pt"><a></a>Why can't we use Scikit-Learn?
</span>

<span style="font-family: Cambria;font-size: 12pt">You can use Scikit-Learn if your dataset is small enough to fit in a single machine's memory. But, when it comes to the actual projection you will find that it doesn't scale well beyond a single machine's resources.
</span>

<span style="color: #4f81bd;font-size: 12pt"><a></a>Why can't we use Apache Spark?
</span>

<span style="font-family: Cambria;font-size: 12pt">As at version <a href="https://spark.apache.org/docs/2.1.0/mllib-dimensionality-reduction.html"><span style="color: #4f81bd">2.1.0</span></a> of Apache Spark MLLib, the following dimensionality reduction techniques are available:
</span>
<ul>
 	<li><span style="font-family: Cambria;font-size: 12pt">Singular Value Decomposition (SVD)
</span></li>
 	<li><span style="font-family: Cambria;font-size: 12pt">Principal Component Analysis (PCA)
</span></li>
</ul>
<span style="font-family: Cambria;font-size: 12pt">There has been at least <a href="https://github.com/apache/spark/pull/6613"><span style="color: #4f81bd">one attempt</span></a> to implement Random Projection in Apache Spark MLLib but those efforts don't appear to have made it through to the latest release.
</span>

<span style="font-family: Cambria;font-size: 12pt">In this article, I will present a recipe to perform Random Projection using PySpark. It brings the scalability of Apache Spark to the Random Projection implementation in Scikit-Learn. As a bonus, you can extend the idea presented in this article to perform general <strong><em>sparse matrix</em></strong> by <strong><em>sparse matrix</em></strong> multiplication (as long as one of the sparse matrix is small enough to fit in memory) resulting in another <strong><em>sparse matrix</em></strong>.
</span>

<span style="color: #4f81bd;font-size: 12pt"><a></a>Further reading:
</span>
<ul>
 	<li><a href="https://scikit-learn.org/stable/modules/random_projection.html"><span style="color: #4f81bd;font-family: Cambria;font-size: 12pt">4.5. Random Projection</span></a><span style="font-family: Cambria;font-size: 12pt"> - particularly, <strong><em>The Johnson-Lindenstrauss lemma</em></strong> and <strong><em>Sparse random projection</em></strong> sections
</span></li>
</ul>
<span style="color: #4f81bd;font-size: 12pt">So, how do we apply Random Projection?
</span>

<span style="font-family: Cambria;font-size: 12pt">There are two main steps in projecting a <strong>n x m</strong> matrix into a low dimensional space using Random Projection:
</span>
<ol>
 	<li><span style="font-family: Cambria;font-size: 12pt">Generating a <strong>m x p</strong> Projection Matrix with a pre-specified sparsity factor - this is where we will leverage Scikit-Learns implementation of Sparse Random Projection and generate the projection matrix.
</span></li>
 	<li><span style="font-family: Cambria;font-size: 12pt">Matrix multiplication - wherein we multiply an <strong>n x m</strong> input dataset with an <strong>m x p</strong> Projection Matrix yielding a new <strong>n x p</strong> matrix (which is said to be projected into a lower dimension) - we will leverage Scipy &amp; Spark to deliver the large scale <strong><em>sparse matrix</em></strong> by <strong><em>sparse matrix</em></strong> multiplication resulting in another <strong><em>sparse matrix</em></strong> in a lower dimension
</span></li>
</ol>
<span style="color: #4f81bd;font-size: 12pt"><em><a></a>The Setup
</em></span>

<span style="font-family: Cambria;font-size: 12pt">Here, I will show you how to apply Random Projection by way of an example.
</span>

<span style="font-size: 12pt"><span style="color: #4f81bd">Data</span><span style="font-family: Cambria">
</span></span>

<span style="font-family: Cambria;font-size: 12pt"> We will be working with <a href="https://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/binary.html"><span style="color: #4f81bd">KDD2012 datasets</span></a> available in LibSVM format.
</span>

<span style="font-family: Cambria;font-size: 12pt">The training dataset has 119,705,032 rows and 54,686,452 features - we will apply Random Projection and transform this dataset into one which has 119,705,032 rows and ~4k features. In order to demonstrate the scalability of my approach, I'm going to artificially increase the size of the training set by a factor of 9 - giving us just over 1 Billion rows.
</span>

<span style="font-family: Cambria;font-size: 12pt">Note, even the transformed dataset needs to be in sparse format otherwise the dataset could take up ~16TB in dense format.
</span>

<span style="color: #4f81bd;font-size: 12pt"><a></a>Compute Resources
</span>

<span style="font-family: Cambria;font-size: 12pt">I will be using a 6-node (90-core, 300G RAM) Spark <a href="https://azure.microsoft.com/en-gb/services/hdinsight/"><span style="color: #4f81bd">HDInsight</span></a> Cluster from <a href="https://azure.microsoft.com/en-gb/"><span style="color: #4f81bd">Microsoft Azure</span></a>
Microsoft is giving away <strong>£150</strong> in <em>free credit</em> to experiment in Azure.
</span>

<span style="font-family: Cambria;font-size: 12pt">I will also be using <a href="https://azure.microsoft.com/en-gb/services/storage/"><span style="color: #4f81bd">Azure Storage</span></a>, particularly the Blob Service to store my input/output datasets.
</span>

<span style="font-family: Cambria;font-size: 12pt">It roughly takes 1ms per row (single core performance) including I/O to process the data. To project a dataset with 1 billion rows and 54 million columns into a new dataset with 1 billion rows and 4096 features took me about 3.75 hours on the above-mentioned cluster.
</span>

<span style="color: #4f81bd;font-size: 12pt">Memory Consideration
</span>

<span style="font-family: Cambria;font-size: 12pt">Your spark executors would need enough RAM to hold 2 x size of a Spark DataFrame partition plus a projection matrix (this is usually a few 100 MBs). If you are running low on memory, then you can repartition your Spark DataFrame to hold smaller chunks of data.
</span>

<span style="color: #4f81bd;font-size: 12pt">Code walkthrough:
</span>

<span style="font-family: Cambria;font-size: 12pt">There are two versions of random projection code depending on whether you want to run the code on a single machine or on a cluster. Refer to </span><span style="font-family: Consolas">./code/localmode/</span><span style="font-family: Cambria;font-size: 12pt"> or </span><span style="font-family: Consolas">./code/clustermode/</span><span style="font-family: Cambria;font-size: 12pt"> depending on your requirement. But here I will discuss the cluster mode code.
</span>

<span style="font-family: Cambria;font-size: 12pt"><strong>In [1]:</strong> First, let's get the required imports out of the way. Of particular note is the <strong><em>johnson_lindenstrauss_min_dim</em></strong> function - this function, given an input matrix, returns the no. of dimensions the projected space should have. In my case I'm going to fix the no. of dimensions at 4096.
</span>

<span style="color: #60a0b0;font-family: Consolas"><em># imports</em></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">import logging</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">import os</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">import numpy as np</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">import math</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">import scipy.sparse as ssp</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">from pyspark.sql import functions as f</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">from sklearn.random_projection import johnson_lindenstrauss_min_dim, SparseRandomProjection</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">from pyspark.sql import SparkSession</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">from pyspark.sql.types import <span style="color: #666666">*</span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">from pyspark.ml.linalg import Vectors, VectorUDT</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">from sklearn.externals import joblib</span><span style="font-family: Cambria;font-size: 12pt">
</span>

<span style="font-family: Cambria;font-size: 12pt"><strong>In [2]:</strong> Below we will generate a Sparse Random Projection Matrix which will be a Scipy's CSR matrix which needs to be saved to disk to transform future data.
</span>

<span style="font-family: Cambria;font-size: 12pt">Note: the dimensionality of the reduced dimension space is independent of the no. of features (in our case 54 million) - it only depends on the no. of rows (in our case 1 Billion) according to the <strong><em>The Johnson-Lindenstrauss lemma</em></strong>.
</span>

<span style="color: #60a0b0;font-family: Consolas"><em># generating the random projection matrix</em></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">dummy_X_rows <span style="color: #666666">=</span> train.count() <span style="color: #60a0b0"><em># 1,077,345,288 rows</em></span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">dummy_X_cols <span style="color: #666666">=</span> train.first()[<span style="color: #4070a0">"features"</span>].size <span style="color: #60a0b0"><em># 54,686,452 features</em></span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="color: #60a0b0;font-family: Consolas"><em># we only use the shape-info of this dummy matrix</em></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">dummy_X <span style="color: #666666">=</span> ssp.csr_matrix((dummy_X_rows, dummy_X_cols), dtype<span style="color: #666666">=</span>np.float32) </span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="color: #60a0b0;font-family: Consolas"><em># find the optimal (conservative estimate) no. of dimensions required according to the</em></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="color: #60a0b0;font-family: Consolas"><em># johnson_lindenstrauss_min_dim function</em></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="color: #60a0b0;font-family: Consolas"><em># rproj_ndim = johnson_lindenstrauss_min_dim(dummy_X_rows, eps=0.2) # returns 4250</em></span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas">rproj_ndim <span style="color: #666666">= </span><span style="color: #40a070">4096</span>
<span style="color: #60a0b0"><em># using a fixed value instead of johnson_lindenstrauss_min_dim suggestion</em></span></span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas">logging.info(<span style="color: #4070a0">"Fitting Sparse Random Projection to have %d columns after projection " </span><span style="color: #666666">%</span> rproj_ndim)</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">srp <span style="color: #666666">=</span> SparseRandomProjection(n_components<span style="color: #666666">=</span>rproj_ndim, random_state<span style="color: #666666">=<span style="color: #40a070">123</span>)</span></span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas">srp.fit(dummy_X)</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">logging.info(<span style="color: #4070a0">"Saving the projection matrix to disk... "</span>)</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">joblib.dump(srp, os.path.join(<span style="color: #4070a0">"/tmp"</span>,<span style="color: #4070a0">"srp.pkl"</span>))</span><span style="font-family: Cambria;font-size: 12pt">
</span>

<span style="font-family: Cambria;font-size: 12pt"><strong>In [3]:</strong> Here we will define a Python function which takes a whole Spark DataFrame partition and a projection matrix to return projected data. In a nutshell, this function converts a whole Spark DataFrame partition into a Scipy CSR matrix and then simply multiplies it with our projection matrix.
</span>

<span style="font-family: Consolas"><span style="color: #007020"><strong>def</strong></span> random_project_mappartitions_function(rdd_row_iterator, local_csr_matrix):</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">
<span style="color: #60a0b0"><em>"""</em></span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="color: #60a0b0;font-family: Consolas"><em> This function is intended to be used in a &lt;df&gt;.rdd.mapPartitions(lambda rdd_map: random_project_mapf\</em></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="color: #60a0b0;font-family: Consolas"><em> (rdd_map, local_rnd_mat))</em></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="color: #60a0b0;font-family: Consolas"><em> setting.</em></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="color: #60a0b0;font-family: Consolas"><em> :param rdd_row_iterator: a list of n-dim sparse vectors</em></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="color: #60a0b0;font-family: Consolas"><em> :param local_csr_matrix: the projection matrix - should have dimensions: n x p</em></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="color: #60a0b0;font-family: Consolas"><em> :return: a list of p-dim sparse-vectors - same length as input</em></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="color: #60a0b0;font-family: Consolas"><em> """</em></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> keys <span style="color: #666666">=</span> []</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">
<span style="color: #60a0b0"><em># this will be a list of single row sparsevectors transformed into scipy csr matrix</em></span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> features_single_row_matrix_list <span style="color: #666666">=</span> []</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> PROJECT_DIM_SIZE <span style="color: #666666">=</span> local_csr_matrix.shape[<span style="color: #40a070">1</span>]</span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas">
<span style="color: #007020"><strong>for</strong></span> row <span style="color: #007020"><strong>in</strong></span> rdd_row_iterator:</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"><span style="color: #60a0b0"><em>   # capture keys</em></span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"><span style="color: #007020"><strong>   if </strong></span><span style="color: #4070a0">"label" </span><span style="color: #007020"><strong>in</strong></span> row:</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">      keys.append((row[<span style="color: #4070a0">"id"</span>], row[<span style="color: #4070a0">"label"</span>]))</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"><span style="color: #007020"><strong>   else</strong></span>:</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">      keys.append((row[<span style="color: #4070a0">"id"</span>]))</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">
<span style="color: #60a0b0"><em>    # work on values:</em></span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">    feature_dim_size <span style="color: #666666">=</span> row[<span style="color: #4070a0">"features"</span>].size <span style="color: #60a0b0"><em># feature dimensionality before projection</em></span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">    col_indices <span style="color: #666666">=</span> row[<span style="color: #4070a0">"features"</span>].indices</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">    row_indices <span style="color: #666666">=</span> [<span style="color: #40a070">0</span>] <span style="color: #666666">*</span> len(col_indices) <span style="color: #60a0b0"><em># defaulting to 0 as we are creating single row matrix</em></span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">    data <span style="color: #666666">=</span> row[<span style="color: #4070a0">"features"</span>].values.astype(np.float32)</span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas">    feature_mat <span style="color: #666666">=</span> ssp.coo_matrix((data, (row_indices, col_indices)), </span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">    shape<span style="color: #666666">=</span>(<span style="color: #40a070">1</span>, feature_dim_size)).tocsr()</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">    features_single_row_matrix_list.append(feature_mat)</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">
<span style="color: #60a0b0"><em> # vstacking single row matrices into one large sparse matrix</em></span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> features_matrix <span style="color: #666666">=</span> ssp.vstack(features_single_row_matrix_list)</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">
<span style="color: #007020"><strong> del</strong></span> features_single_row_matrix_list</span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas"> projected_features <span style="color: #666666">=</span> features_matrix.dot(local_csr_matrix)</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">
<span style="color: #007020"><strong> del</strong></span> features_matrix, local_csr_matrix</span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas"> projected_features_list <span style="color: #666666">=</span> (Vectors.sparse(PROJECT_DIM_SIZE, zip(i.indices, i.data))</span><span style="font-family: Consolas">
<span style="color: #007020"><strong>                            for</strong></span> i <span style="color: #007020"><strong>in</strong></span> projected_features)</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">
<span style="color: #007020"><strong> if </strong></span><span style="color: #4070a0">"label" </span><span style="color: #007020"><strong>in</strong></span> row:</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"><span style="color: #007020"><strong>    return</strong></span> zip((i[<span style="color: #40a070">0</span>] <span style="color: #007020"><strong>for</strong></span> i <span style="color: #007020"><strong>in</strong></span> keys), (i[<span style="color: #40a070">1</span>] <span style="color: #007020"><strong>for</strong></span> i <span style="color: #007020"><strong>in</strong></span> keys), projected_features_list)</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"><span style="color: #007020"><strong> else</strong></span>:</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"><span style="color: #007020"><strong>    return</strong></span> zip((i[<span style="color: #40a070">0</span>] <span style="color: #007020"><strong>for</strong></span> i <span style="color: #007020"><strong>in</strong></span> keys), projected_features_list)</span><span style="font-family: Cambria;font-size: 12pt">
</span>

<span style="font-family: Cambria;font-size: 12pt"><strong>In [4]:</strong> and here is where the main action takes place – the projected output is saved to a parquet file.
</span>

<span style="font-family: Consolas"><span style="color: #007020"><strong>if </strong></span><span style="color: #19177c">__name__ </span><span style="color: #666666">== </span><span style="color: #4070a0">'__main__'</span>:</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">  N_FEATURES<span style="color: #666666">=<span style="color: #40a070">54686452</span>
<span style="color: #60a0b0"><em>  # these are the no. of features in the dataset we are going to use in this app</em></span></span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">  logging.basicConfig(format<span style="color: #666666">=<span style="color: #4070a0">'%(asctime)s %(levelname)s:%(message)s'</span>, level=</span>logging.INFO)</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">
<span style="color: #60a0b0"><em>   # fire up a spark session</em></span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">  spark <span style="color: #666666">=</span> SparkSession <span style="color: #666666">\</span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">  .builder <span style="color: #666666">\</span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">  .appName(<span style="color: #4070a0">"PySpark Random Projection Demo"</span>) <span style="color: #666666">\</span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">  .getOrCreate()</span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas">  sc <span style="color: #666666">=</span> spark.sparkContext</span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas">  DATA_DIR <span style="color: #666666">= </span><span style="color: #4070a0">'wasb://kdd12-blob@sashistorage.blob.core.windows.net/'</span>
<span style="color: #60a0b0"><em>  # Azure Storage blob</em></span></span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas">  train <span style="color: #666666">=</span> spark.read.format(<span style="color: #4070a0">"libsvm"</span>).load(DATA_DIR, numFeatures<span style="color: #666666">=</span>N_FEATURES)<span style="color: #666666">\</span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">  .withColumn(<span style="color: #4070a0">"id"</span>, f.monotonically_increasing_id())</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">  print(train.show())</span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas">  train_schema <span style="color: #666666">=</span> StructType(</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">  [StructField(<span style="color: #4070a0">"id"</span>, LongType(), <span style="color: #19177c">False</span>)</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">  ,StructField(<span style="color: #4070a0">"label"</span>, FloatType(), <span style="color: #19177c">False</span>)</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> , StructField(<span style="color: #4070a0">"features"</span>, VectorUDT(), <span style="color: #19177c">False</span>)</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">  ]</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> )</span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas">
<span style="color: #60a0b0"><em> # generating the random projection matrix</em></span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> dummy_X_rows <span style="color: #666666">=</span> train.count()</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> dummy_X_cols <span style="color: #666666">=</span> train.first()[<span style="color: #4070a0">"features"</span>].size</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> dummy_X <span style="color: #666666">=</span> ssp.csr_matrix((dummy_X_rows, dummy_X_cols), dtype<span style="color: #666666">=</span>np.float32) <span style="color: #60a0b0"><em># the shape-only of this dummy</em></span></span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas">
<span style="color: #60a0b0"><em> # find the optimal (conservative estimate) no. of dimensions required according to the</em></span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">
<span style="color: #60a0b0"><em> # johnson_lindenstrauss_min_dim function</em></span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">
<span style="color: #60a0b0"><em> # rproj_ndim = johnson_lindenstrauss_min_dim(dummy_X_rows, eps=0.2) # returns 4250</em></span></span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas"> rproj_ndim <span style="color: #666666">= </span><span style="color: #40a070">4096
</span></span>

<span style="font-family: Consolas"> logging.info(<span style="color: #4070a0">"Fitting Sparse Random Projection to have %d columns after projection " </span><span style="color: #666666">%</span> rproj_ndim)</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> srp <span style="color: #666666">=</span> SparseRandomProjection(n_components<span style="color: #666666">=</span>rproj_ndim, random_state<span style="color: #666666">=<span style="color: #40a070">123</span>)</span></span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas"> srp.fit(dummy_X)</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> logging.info(<span style="color: #4070a0">"Saving the projection matrix to disk... "</span>)</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> joblib.dump(srp, os.path.join(<span style="color: #4070a0">"/tmp"</span>,<span style="color: #4070a0">"srp.pkl"</span>))</span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas"> local_rnd_mat <span style="color: #666666">=</span> srp.components_.T.astype(np.float32)</span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas">
<span style="color: #60a0b0"><em>  # broadcast the local_rnd_mat so it is available on all nodes</em></span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">  local_rnd_mat_bc_var <span style="color: #666666">=</span> sc.broadcast(local_rnd_mat)</span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas">  logging.info(<span style="color: #4070a0">"Applying random projection to rdd map partitions"</span>)</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">  train_projected_df <span style="color: #666666">=</span> train.rdd<span style="color: #666666">\</span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">  .mapPartitions(<span style="color: #007020"><strong>lambda</strong></span> rdd_map_partition: </span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">     random_project_mappartitions_function(rdd_map_partition,local_rnd_mat_bc_var.value))<span style="color: #666666">\</span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">  .toDF(train_schema)</span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas">  logging.info(<span style="color: #4070a0">"Writing projected data to disk..."</span>)</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">  train_projected_df<span style="color: #666666">\</span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">  .write<span style="color: #666666">\</span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">  .mode(<span style="color: #4070a0">"overwrite"</span>)<span style="color: #666666">\</span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas">  .parquet(DATA_DIR<span style="color: #666666">+<span style="color: #4070a0">"/train_features_random_projected.parquet/"</span>)</span></span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas"> logging.info(<span style="color: #4070a0">"Sample rows from training set before projection..."</span>)</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> print(train.show())</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> logging.info(<span style="color: #4070a0">"Sample rows from training set after projection..."</span>)</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> print(spark.read.parquet(DATA_DIR<span style="color: #666666">+<span style="color: #4070a0">"/train_features_random_projected.parquet/"</span>).show())</span></span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas"> spark.stop()</span><span style="font-family: Cambria;font-size: 12pt">
</span>

<span style="font-family: Cambria;font-size: 12pt"><strong>In [5]:</strong> Finally, here is how we submit the PySpark app to the cluster.
</span>

<span style="color: #60a0b0;font-family: Consolas"><em> #!/usr/bin/env bash</em></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> echo <span style="color: #4070a0">"Submitting PySpark app..."</span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> spark-submit \</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> --master yarn \</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> --executor-memory 3G \</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> --driver-memory 6G \</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> --num-executors 85 \</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> --executor-cores 1 \</span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> code/clustermode/randomProjection.py</span><span style="font-family: Cambria;font-size: 12pt"></span>

<span style="font-family: Consolas"> echo <span style="color: #4070a0">"Exporting Projection Matrix to Azure Storage..."</span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"><span style="color: #06287e"> zip</span> /tmp/srp.pkl.zip /tmp/srp.pkl*
</span>

<span style="font-family: Consolas"> export <span style="color: #19177c">AZURE_STORAGE_ACCOUNT=<span style="color: #666666">&lt;</span>storage</span> account name<span style="color: #666666">&gt;</span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> export <span style="color: #19177c">AZURE_STORAGE_ACCESS_KEY=<span style="color: #666666">&lt;</span>storage</span> account access key<span style="color: #666666">&gt;</span></span><span style="font-family: Cambria;font-size: 12pt">
</span><span style="font-family: Consolas"> azure storage blob upload --container modelling-outputs --file /tmp/srp.pkl.zip
</span>

<span style="color: #333333;font-family: Segoe UI;font-size: 10pt">For the full code refer to <a href="https://github.com/SashiDareddy/RandomProjection"><span style="color: #4f81bd">https://github.com/SashiDareddy/RandomProjection</span></a>
</span><span style="font-family: Cambria;font-size: 12pt">
</span>

<span style="color: #4070a0;font-family: Consolas">
</span>]]></content:encoded>
    </item>
    <item>
      <title>Scaling a recommender system across large data volumes</title>
      <link>https://docs.microsoft.com/archive/blogs/data_insights_global_practice/scaling-a-recommender-system-across-large-data-volumes</link>
      <pubDate>Mon, 08 Aug 2016 17:35:28 GMT</pubDate>
      <dc:creator><![CDATA[Yaswant.v]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/data_insights_global_practice/?p=575</guid>
      <description><![CDATA[By Michele Usuelli, Data Scientist Consultant

Building a recommendation engine in presence of...]]></description>
      <content:encoded><![CDATA[<h2><span style="color: #2c6eab;font-size: 18pt;background-color: white">By Michele Usuelli, Data Scientist Consultant
</span></h2>
<h2>Building a recommendation engine in presence of large data volume</h2>
<span style="font-size: 12pt">E-commerce businesses can suggest new products to their customers. How do they choose the products to recommend? The companies collect data about the purchases of their customers. Starting from the purchase history, they can identify items that have been taken by the same customers and customers that bought the same items. Given a new customer, the company can recommend him/her items similar to the ones that he/she purchased already, or items that have been purchased by similar customers. The recommendations can also take account of other data sources like personal information, items descriptions, customer ratings and reviews. Large companies like Amazon and eBay already built sophisticated recommendation engines tailored to their environment.
</span>

What if a smaller company wants to build a smaller recommendation engine from scratch? Even if the complexity is not the same as huge companies, the volume of data to process may be large. In this article, we see how to build simple recommender systems that are scalable across large data volumes. To show the concepts avoiding unnecessary complications, in this article we are using the information about purchases only. Due to the complexity of the problem and to the variety of contexts, this article describes it from a generic high-level perspective.
<h2><a></a>Choosing the environment</h2>
<span style="font-size: 12pt">In this article, the technology that we use is SQL Server 2016 with R Services. The reason is that we can store large SQL tables and process them using advanced analytics techniques provided by R.
</span>

The use-case consists in recommending movies to the users. Given a set of movies and users, the table describes what movies each user has watched. To avoid further complications, the table doesn't include user ratings. Its structure is:
<ul>
 	<li><span style="font-size: 12pt">A row for each movie
</span></li>
 	<li><span style="font-size: 12pt">A column for each user
</span></li>
 	<li><span style="font-size: 12pt">Cells equal to 1 if the user watched the movie and to 0 otherwise
</span></li>
</ul>
<span style="font-size: 12pt">The data has already been imported into SQL 2016 into a table called <em>movies</em>. To analyse the data, we use the R environment and we define a SQL table object called <em>sql_movies</em> that allows us to access the data. The function creating the object is <em>RxSqlServerData</em> and it needs a connection string for the database and the name of the table. We also define another SQL table object, called <em>sql_clust_users</em>, referring to a table that doesn't exist yet. We'll create and use this table from R.
</span>
<p style="background: #f8f8f8"><span style="font-family: Consolas">connection_string &lt;-<span style="color: #4e9a06"> "Driver=SQL Server;Server=.;Database=movies;Uid=usernam;password"</span></span></p>
sql_movies &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>RxSqlServerData</strong></span>(<span style="color: #204a87">connectionString =</span> connection_string,</span>
<span style="color: #204a87">table =</span><span style="color: #4e9a06">"movies"</span>)

sql_clust_users &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>RxSqlServerData</strong></span>(<span style="color: #204a87">connectionString =</span> connection_string,</span>
<span style="color: #204a87">table =</span><span style="color: #4e9a06">"sql_clust_users"</span>)

<span style="font-size: 12pt">Let's have a quick look at the data.
</span>
<p style="background: #f8f8f8"><span style="font-family: Consolas"><span style="color: #204a87"><strong>dim</strong></span>(sql_movies)
</span></p>
<p style="background: #f8f8f8"><span style="font-family: Consolas;font-size: 11pt">## [1] 11503 865
</span></p>
<p style="background: #f8f8f8"><span style="font-family: Consolas"><span style="color: #204a87"><strong>rxGetVarInfo</strong></span>(sql_movies)[<span style="color: #0000cf">1</span>:<span style="color: #0000cf">4</span>]
</span></p>
<p style="background: #f8f8f8"><span style="font-family: Consolas;font-size: 11pt">## $MovieId
## Type: integer, Low/High: (91, 3630218)
##
## $U9928
## Type: numeric, Low/High: (0.0000, 1.0000)
##
## $U15152
## Type: numeric, Low/High: (0.0000, 1.0000)
##
## $U7850
## Type: numeric, Low/High: (0.0000, 1.0000)
</span></p>
<p style="background: #f8f8f8"><span style="font-family: Consolas"><span style="color: #204a87"><strong>head</strong></span>(sql_movies)[, <span style="color: #0000cf">1</span>:<span style="color: #0000cf">8</span>]
</span></p>
<p style="background: #f8f8f8"><span style="font-family: Consolas;font-size: 11pt">## MovieId U9928 U15152 U7850 U9065 U8166 U5251 U10249
## 1 368688 0 0 1 0 0 0 0
## 2 368709 0 0 0 0 0 0 0
## 3 368794 0 0 0 0 0 0 0
## 4 368891 0 0 0 0 1 0 0
## 5 368909 0 0 1 0 0 0 0
## 6 368933 0 0 0 0 0 0 0
</span></p>
<span style="font-size: 12pt">The table has 865 columns. The first column contains the Movie ID and each other column corresponds to a user. The values in the <em>user</em> columns are equal to 1 if the user watched the movie and to 0 otherwise.
</span>

To build a recommendation engine, we can use the R package <em>recommenderlab</em>. However, this package needs all the data to be stored into the RAM, so it can deal with relatively small data volumes. Therefore, to process a large dataset we need to reduce its volume. In this example, the data volume is not really huge. However, the solution is designed in such a way that it's applicable on a larger dataset.
<h2><a></a>Reducing the data volume</h2>
<span style="font-size: 12pt">To produce a small table, we can either reduce the number of rows or the number of columns. In this chapter we are reducing both.
</span>

A common challenge is that there are many users, i.e. columns. A solution consists in identifying small groups of similar users and defining a new table having a column for each group of users. The steps are
<ol>
 	<li><span style="font-size: 12pt">Define a formula describing what columns we are dealing with. We include all the columns apart from the first
</span></li>
 	<li><span style="font-size: 12pt">Measure the similarity between users using the function rxCor
</span></li>
 	<li><span style="font-size: 12pt">Starting from the similarity, define a matrix containing the distance between the users
</span></li>
 	<li><span style="font-size: 12pt">Starting from the distance matrix, identify groups of similar users applying hierarchical clustering
</span></li>
 	<li><span style="font-size: 12pt">Define a new table which columns correspond to the clusters. The value of each column is equal to one if at least one user of the cluster watched the movie
</span></li>
</ol>
<span style="font-size: 12pt">The new table contains a row for each movie and a column for each group of users.
</span>

This is the related code to detect 100 clusters of users.
<p style="background: #f8f8f8"><span style="font-family: Consolas"><span style="color: #8f5902"><em># define the formula</em></span>
cols_movies &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>names</strong></span>(sql_movies)[-<span style="color: #0000cf">1</span>]</span>
string_users &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>paste</strong></span>(cols_movies, <span style="color: #204a87">collapse =</span> " + "</span>)
formula_users &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>formula</strong></span>(<span style="color: #204a87"><strong>paste</strong></span>("~"</span>, string_users))</span></p>
<span style="color: #8f5902"><em># build the hierachical clustering model</em></span>
cor_users &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>rxCor</strong></span>(formula_users, sql_movies,</span>
<span style="color: #204a87">rowsPerRead =</span>
<span style="color: #0000cf">5e02</span>)
<p style="background: #f8f8f8"><span style="font-family: Consolas;font-size: 11pt">## Rows Read: 11503, Total Rows Processed: 11503, Total Chunk Time: 3.424 seconds
## Computation time: 7.211 seconds.
</span></p>
<p style="background: #f8f8f8"><span style="font-family: Consolas">dist_users &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>as.dist</strong></span>(<span style="color: #0000cf">1</span> - <span style="color: #204a87"><strong>abs</strong></span>(cor_users))</span>
model_hc &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>hclust</strong></span>(dist_users, <span style="color: #204a87">method =</span> "complete"</span>)
which_cluster &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>cutree</strong></span>(model_hc, <span style="color: #204a87">k =</span>
<span style="color: #0000cf">100</span>)</span>
var_clusters &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>sort</strong></span>(<span style="color: #204a87"><strong>unique</strong></span>(which_cluster))</span></span></p>
<span style="color: #8f5902"><em># group the users into clusters</em></span>
clusterCols &lt;-<span style="color: #4e9a06">
</span>function(list_in) {

<span style="color: #8f5902"><em># define a field for each cluster</em></span>
list_out &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>lapply</strong></span>(var_clusters, function(this_cluster) {</span>
these_cols &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>names</strong></span>(which_cluster[which_cluster == </span>this_cluster])
df_cols &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>data.frame</strong></span>(list_in[these_cols])</span>
<span style="color: #204a87"><strong>rowSums</strong></span>(df_cols) &gt;<span style="color: #4e9a06">
<span style="color: #0000cf">0</span>
</span> })

<span style="color: #204a87"><strong>names</strong></span>(list_out) &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>paste0</strong></span>("clust"</span>, var_clusters)
<span style="color: #8f5902"><em># for each movie, count the number of clusers having watched it</em></span>
df_views &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>data.frame</strong></span>(list_out)</span>
list_out$n_views &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>rowSums</strong></span>(df_views)</span>

<span style="color: #8f5902"><em># add the movie ID</em></span>
list_out$MovieId &lt;-<span style="color: #4e9a06">
</span>list_in$MovieId

list_out
}

<span style="color: #204a87"><strong>rxDataStep</strong></span>(
<span style="color: #204a87">inData =</span> sql_movies,
<span style="color: #204a87">outFile =</span> sql_clust_users,
<span style="color: #204a87">overwrite =</span>
<span style="color: #8f5902">TRUE</span>,
<span style="color: #204a87">transformFunc =</span> clusterCols,
<span style="color: #204a87">transformObjects =</span>
<span style="color: #204a87"><strong>list</strong></span>(<span style="color: #204a87">var_clusters =</span> var_clusters,
<span style="color: #204a87">which_cluster =</span> which_cluster))
<p style="background: #f8f8f8"><span style="font-family: Consolas;font-size: 11pt">## Warning in rxLinkTransformComponents(transforms = transforms, transformFunc = transformFunc, :
## There are 'transformObjects' that have the same name as an
## object in the evaluation environment, which could cause
## misleading results. Try renaming the following objects in the
## 'transformObjects' list: 'var_clusters', 'which_cluster'.
</span></p>
<p style="background: #f8f8f8"><span style="font-family: Consolas;font-size: 11pt">## Rows Read: 11503, Total Rows Processed: 11503, Total Chunk Time: 2.088 seconds
</span></p>
<p style="background: #f8f8f8"><span style="font-family: Consolas"><span style="color: #204a87"><strong>dim</strong></span>(sql_clust_users)
</span></p>
<p style="background: #f8f8f8"><span style="font-family: Consolas;font-size: 11pt">## [1] 11503 102
</span></p>
<span style="font-size: 12pt">The other approach to decrease the size is to reduce the number of rows. Applying a clustering algorithm, we can identify groups of similar items and define a table with a row for each group of items. For this purpose, we can use the function <em>rxKmeans</em>.
</span>

The starting point is <em>sql_clust_users</em> and the steps are
<ol>
 	<li><span style="font-size: 12pt">Build a formula defining the variables to include into the clustering model
</span></li>
 	<li><span style="font-size: 12pt">Identify 20 clusters of movies using <em>rxKmeans</em>
</span></li>
 	<li><span style="font-size: 12pt">Extract the centers of the clusters. They contain averages that in this context express the percentage of users that watched each movie
</span></li>
 	<li><span style="font-size: 12pt">Starting from the centers, define the table <em>df_movies</em> which values are 1 if the percentage is above 10% and 0 otherwise
</span></li>
</ol>
<span style="font-size: 12pt">This is the related code.
</span>
<p style="background: #f8f8f8"><span style="font-family: Consolas">cols_km &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>names</strong></span>(sql_clust_users)</span>
feat_km &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>paste</strong></span>(cols_km, <span style="color: #204a87">collapse =</span> " + "</span>)
formula_km &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>formula</strong></span>(<span style="color: #204a87"><strong>paste</strong></span>("~"</span>, feat_km))
model_km &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>rxKmeans</strong></span>(formula_km, sql_clust_users,</span>
<span style="color: #204a87">numClusters =</span>
<span style="color: #0000cf">20</span>,
<span style="color: #204a87">reportProgress =</span>
<span style="color: #0000cf">0</span>)
df_movies &lt;-<span style="color: #4e9a06">
<span style="color: #204a87"><strong>ifelse</strong></span>(model_km$centers[, -<span style="color: #0000cf">1</span>] &gt; <span style="color: #0000cf">0.1</span>, <span style="color: #0000cf">1</span>, <span style="color: #0000cf">0</span>)</span>
df_movies[<span style="color: #0000cf">1</span>:<span style="color: #0000cf">10</span>, <span style="color: #0000cf">1</span>:<span style="color: #0000cf">6</span>]
</span></p>
<p style="background: #f8f8f8"><span style="font-family: Consolas;font-size: 11pt">## clust1 clust2 clust3 clust4 clust5 clust6
## 1 1 0 0 1 1 1
## 2 0 0 0 0 1 0
## 3 1 0 1 1 1 1
## 4 0 0 0 0 0 0
## 5 0 0 0 0 1 1
## 6 1 0 1 0 1 1
## 7 1 0 0 1 1 1
## 8 0 0 0 1 1 1
## 9 0 0 0 0 1 1
## 10 1 0 0 1 1 1
</span></p>
<span style="font-size: 12pt">The table <em>df_movies</em> is small-sized and it contains a column for each cluster of users and a row for each cluster of movies. The value of each cell is equal to 1 if the related cluster of users watched at least 10% of the related cluster of movies, and 0 otherwise.
</span>
<h2><a></a>Building the engine</h2>
<span style="font-size: 12pt">In the previous step we produced a small table stored into the R environment. Starting from that, we can build a recommender system using the package <em>recommenderlab</em>.
</span>

The steps are
<ol>
 	<li><span style="font-size: 12pt">Starting from <em>df_movies</em>, build, test and validate a recommendation engine. There are a few options in the recommenderlab package, so a good approach is to test and evaluate a few of them, and to pick the best-performing
</span></li>
 	<li><span style="font-size: 12pt">Given a new user, measure the distance between each cluster center and he/she. Then, identify the closest center and associate the user to the related cluster
</span></li>
 	<li><span style="font-size: 12pt">Identify the cluster of movies to recommend to the cluster related to the new user
</span></li>
 	<li><span style="font-size: 12pt">Out of the recommended cluster of movies, choose one or more movies to recommend to the user
</span></li>
</ol>
<h2><a></a>Conclusions</h2>
<span style="font-size: 12pt">Using the approach described by this article, it's possible to apply a recommender system on a large data volume.
</span>

To extend this solution, it's possible to use a matrix containing user ratings instead of just 0s a 1s. To include some information about the users and/or movies, it's possible to summarise it for each cluster. Other adjustments can follow a similar methodology.]]></content:encoded>
    </item>
    <item>
      <title>Analysing data in SQL Server 16, combining R and SQL</title>
      <link>https://docs.microsoft.com/archive/blogs/data_insights_global_practice/analysing-data-in-sql-server-16-combining-r-and-sql</link>
      <pubDate>Mon, 01 Aug 2016 22:43:14 GMT</pubDate>
      <dc:creator><![CDATA[Yaswant.v]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/data_insights_global_practice/?p=535</guid>
      <description><![CDATA[By Michele Usuelli, Data Scientist Consultant
Overview
R is the most popular programming language...]]></description>
      <content:encoded><![CDATA[<p><span style="font-size:12pt">By Michele Usuelli, Data Scientist Consultant
</span></p><h2>Overview
</h2><p><span style="font-size:12pt">R is the most popular programming language for statistics and machine learning, and SQL is the lingua franca for the data manipulation. Dealing with an advanced analytics scenario, we need to pre-process the data and to build machine learning models. A good solution consists in using each tool for the purpose it's designed for: the standard data preparation using a tool supporting SQL, the custom data preparation and the machine learning models using R.
</span></p><p><strong>SQL Server 2016</strong> has the option to include an extension: <strong>Microsoft R Services</strong>. It's based on <strong>MRS</strong> (Microsoft R Server), a tool designed by Revolution Analytics to scale R across large data volumes. In SQL Server 2016, R Services provides a direct interface between MRS and the SQL databases. In this way, it's possible to analyse the SQL tables and to create new tables using the MRS tools. In addition, the R package <em>RODBC</em> allows to run SQL queries from R.
</p><p>This article shows a simple example integrating R and SQL. To follow all the steps, some prior knowledge about the basic R functions is required.
</p><h2><a name="setting-up-the-environment" />Setting-up the environment
</h2><p><span style="font-size:12pt">In this article, we are using a SQL Server 2016 VM. To set-up the environment, the steps are
</span></p><ol><li><span style="font-size:12pt">Log-in to Azure and set-up a SQL Server machine with R Services. To install R Services, refer to this: <a href="https://msdn.microsoft.com/en-us/library/mt696069.aspx">https://msdn.microsoft.com/en-us/library/mt696069.aspx</a>
			</span></li><li><span style="font-size:12pt">Connect to SQL Server: from the Windows task bar, open SQL Server Management Studio and connect to it using your credentials.
</span></li><li><div>Create a new database: right-click on Databases and choose <em>New Database…</em>.
</div><p><img src="https://msdnshared.blob.core.windows.net/media/2016/08/080116_2243_Analysingda1.png" alt="" />
			</p></li><li><div>Give a name to the new database, e.g. <em>iristest</em>. Then, click on <em>Add</em> and <em>OK</em>.
</div><p><img src="https://msdnshared.blob.core.windows.net/media/2016/08/080116_2243_Analysingda2.png" alt="" />
			</p></li><li><div>Open the query editor: right-click on the new database and choose <em>New Query</em>.
</div><p><img src="https://msdnshared.blob.core.windows.net/media/2016/08/080116_2243_Analysingda3.png" alt="" />
			</p></li><li>Define the credentials to authenticate to the database: in the editor, paste and run the following query. You should replace the parameter <em>testiris</em> with the name of your database, and username and password with new credentials to access this database.
</li></ol><p style="background: #f8f8f8"><span style="font-family:Consolas"><span style="color:#204a87"><strong>USE</strong></span> [testiris]<br />GO<br /><span style="color:#204a87"><strong>CREATE</strong></span> LOGIN [username] <span style="color:#204a87"><strong>WITH</strong></span> PASSWORD=<span style="color:#4e9a06">'password'</span>, CHECK_EXPIRATION=OFF,<br />CHECK_POLICY=OFF;<br /><span style="color:#204a87"><strong>CREATE</strong></span>
			<span style="color:black">USER</span> [username] <span style="color:#204a87"><strong>FOR</strong></span> LOGIN [username] <span style="color:#204a87"><strong>WITH</strong></span> DEFAULT_SCHEMA=[db_datareader]<br /><span style="color:#204a87"><strong>ALTER</strong></span>
			<span style="color:#204a87"><strong>ROLE</strong></span> [db_datareader] <span style="color:#204a87"><strong>ADD</strong></span>
			<span style="color:#204a87"><strong>MEMBER</strong></span> [username]<br /><span style="color:#204a87"><strong>ALTER</strong></span>
			<span style="color:#204a87"><strong>ROLE</strong></span> [db_ddladmin] <span style="color:#204a87"><strong>ADD</strong></span>
			<span style="color:#204a87"><strong>MEMBER</strong></span> [username]
</span></p><ol><li>Build a new RStudio or Visual Studio project and create a new R script. See <a href="https://www.rstudio.com/products/RStudio/">https://www.rstudio.com/products/RStudio/</a>
		</li><li>Define a list with the parameters to connect to the database. They should be the same as in the previous SQL query.
</li></ol><p style="background: #f8f8f8"><span style="font-family:Consolas">sql_par &lt;-<span style="color:#4e9a06">
				<span style="color:#204a87"><strong>list</strong></span>(</span><br />
			<span style="color:#204a87">Driver =</span>
			<span style="color:#4e9a06">"SQL Server"</span>,<br />
			<span style="color:#204a87">Server =</span>
			<span style="color:#4e9a06">"."</span>,<br />
			<span style="color:#204a87">Database =</span>
			<span style="color:#4e9a06">"testiris"</span>,<br />
			<span style="color:#204a87">Uid =</span>
			<span style="color:#4e9a06">"username"</span>,<br />
			<span style="color:#204a87">Pwd =</span>
			<span style="color:#4e9a06">"password"</span><br />)
</span></p><ol><li><span style="font-size:12pt">Starting from the list of parameters, define a connection string for the SQL database. For this purpose, you can use the function paste.
</span></li></ol><p style="background: #f8f8f8"><span style="font-family:Consolas">sql_connect &lt;-<span style="color:#4e9a06">
				<span style="color:#204a87"><strong>paste</strong></span>(<span style="color:#204a87"><strong>names</strong></span>(sql_par), <span style="color:#204a87"><strong>unlist</strong></span>(sql_par), </span><br />
			<span style="color:#204a87">sep =</span>
			<span style="color:#4e9a06">"="</span>, <span style="color:#204a87">collapse =</span>
			<span style="color:#4e9a06">";"</span>)
</span></p><ol><li><span style="font-size:12pt">Define a MRS data source object containing the information to access the <em>iristest</em> database. The inputs are the table name, e.g. <em>iris</em>, and the connection string.
</span></li></ol><p style="background: #f8f8f8"><span style="font-family:Consolas">table_iris &lt;-<span style="color:#4e9a06"> "iris"</span><br />sql_iris &lt;-<span style="color:#4e9a06">
				<span style="color:#204a87"><strong>RxSqlServerData</strong></span>(<span style="color:#204a87">connectionString =</span> sql_connect,</span><br />
			<span style="color:#204a87">table =</span> table_iris)
</span></p><ol><li><span style="font-size:12pt">Import the data into a SQL table. For this purpose, we can use <em>rxDataStep</em>. Its inputs are the local dataframe <em>iris</em>, the data source object <em>sql_iris</em>, and <em>overwrite = TRUE</em>, specifying that we are overwriting the table if existing already. To avoid issues in the SQL queries, we also re-name the columns of iris, replacing the <em>.</em> with an *_*.
</span></li></ol><p style="background: #f8f8f8"><span style="font-family:Consolas"><span style="color:#204a87"><strong>names</strong></span>(iris) &lt;-<span style="color:#4e9a06">
				<span style="color:#204a87"><strong>gsub</strong></span>("[.]"</span>, <span style="color:#4e9a06">"_"</span>, <span style="color:#204a87"><strong>names</strong></span>(iris))<br /><span style="color:#204a87"><strong>rxDataStep</strong></span>(iris, sql_iris, <span style="color:#204a87">overwrite =</span>
			<span style="color:#8f5902">TRUE</span>)
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas;font-size:11pt">## Rows Read: 150, Total Rows Processed: 150, Total Chunk Time: Less than .001 seconds
</span></p><ol><li><span style="font-size:12pt">Install and load the package <em>RODBC</em> and initialise it using the connection string. We define the object channel containing the information to access the database.
</span></li></ol><p style="background: #f8f8f8"><span style="font-family:Consolas"><span style="color:#204a87"><strong>library</strong></span>(RODBC)<br />channel &lt;-<span style="color:#4e9a06">
			</span>RODBC::<span style="color:#204a87"><strong>odbcDriverConnect</strong></span>(sql_connect)
</span></p><h2><a name="exploring-the-data" />Exploring the data
</h2><p><span style="font-size:12pt">To explore the data, we can either use MRS or SQL. In MRS, there is the option to quickly access the metadata using <em>rxGetVarInfo</em>.
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas"><span style="color:#204a87"><strong>rxGetVarInfo</strong></span>(sql_iris)
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas;font-size:11pt">## Var 1: Sepal_Length, Type: numeric, Low/High: (4.3000, 7.9000)<br />## Var 2: Sepal_Width, Type: numeric, Low/High: (2.0000, 4.4000)<br />## Var 3: Petal_Length, Type: numeric, Low/High: (1.0000, 6.9000)<br />## Var 4: Petal_Width, Type: numeric, Low/High: (0.1000, 2.5000)<br />## Var 5: Species<br />##        3 factor levels: setosa versicolor virginica
</span></p><p><span style="font-size:12pt">The output contains the name and type of each field. Also, it's possible to compute a quick summary of the table using <em>rxSummary</em>. The inputs are
</span></p><ul><li><span style="font-size:12pt"><em>Formula</em>: what variables we want to summarise, using the formula syntax. For more info about the formula, have a look at the related material typing <em>?formula</em> into the R console. To include all the variables, we can use <em>.</em>.
</span></li><li><span style="font-size:12pt"><em>Data</em>: the data source object, containing the information to access the data.
</span></li></ul><p style="background: #f8f8f8"><span style="font-family:Consolas">summary_iris &lt;-<span style="color:#4e9a06">
				<span style="color:#204a87"><strong>rxSummary</strong></span>(~ </span>., sql_iris)
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas;font-size:11pt">## Rows Read: 150, Total Rows Processed: 150, Total Chunk Time: Less than .001 seconds <br />## Computation time: 0.005 seconds.
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas">summary_iris$sDataFrame
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas;font-size:11pt">##           Name     Mean    StdDev Min Max ValidObs MissingObs<br />## 1 Sepal_Length 5.843333 0.8280661 4.3 7.9      150          0<br />## 2  Sepal_Width 3.057333 0.4358663 2.0 4.4      150          0<br />## 3 Petal_Length 3.758000 1.7652982 1.0 6.9      150          0<br />## 4  Petal_Width 1.199333 0.7622377 0.1 2.5      150          0<br />## 5      Species       NA        NA  NA  NA      150          0
</span></p><p><span style="font-size:12pt">This summary contains some basic statistics like the mean and the standard deviation.
</span></p><p>Using <em>RODBC</em>, we can run any SQL query. The inputs are the connection object <em>channel</em>, defined previously, and the query string. Let's see a simple example.
</p><p style="background: #f8f8f8"><span style="font-family:Consolas">RODBC::<span style="color:#204a87"><strong>sqlQuery</strong></span>(channel, <span style="color:#4e9a06">"select count(*) from iris"</span>)
</span></p><p><span style="font-size:12pt">As expected, the output is the number of records.
</span></p><p>R contains good string manipulation tools that allow us to build complex queries and to write extensions. To show the approach, we perform a simple group by operation. The steps are
</p><ol><li><span style="font-size:12pt">Define the skeleton of the query, leaving the column names as <em>%s</em> parameters
</span></li><li><span style="font-size:12pt">Define the parameters
</span></li><li><span style="font-size:12pt">Define the query inclusive of its parameters using <em>sprintf</em>
			</span></li><li><span style="font-size:12pt">Run the query
</span></li></ol><p><span style="font-size:12pt">The R code is
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas">query_sql &lt;-<span style="color:#4e9a06"> "select %s, avg(%s) as avg_sl from iris group by %s"</span><br />col_group &lt;-<span style="color:#4e9a06"> "Species"</span><br />col_value &lt;-<span style="color:#4e9a06"> "Sepal_Length"</span><br />query_count &lt;-<span style="color:#4e9a06">
				<span style="color:#204a87"><strong>sprintf</strong></span>(query_sql, col_group, col_value, col_group)</span><br />df_avg_value &lt;-<span style="color:#4e9a06">
			</span>RODBC::<span style="color:#204a87"><strong>sqlQuery</strong></span>(channel, query_count)<br />df_avg_value
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas;font-size:11pt">## <br />## Attaching package: 'dplyr'
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas;font-size:11pt">## The following objects are masked from 'package:stats':<br />## <br />##     filter, lag
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas;font-size:11pt">## The following objects are masked from 'package:base':<br />## <br />##     intersect, setdiff, setequal, union
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas;font-size:11pt">##      Species avg_sl<br />## 1     setosa  5.006<br />## 2 versicolor  5.936<br />## 3  virginica  6.588
</span></p><p><span style="font-size:12pt">Using a similar approach, we can define more complex queries. Also, if we need to run similar queries many times, it's possible to build R functions that build the query depending on some given parameters.
</span></p><h2><a name="processing-the-data" />Processing the data
</h2><p><span style="font-size:12pt">To process the data, it's possible to use both MRS and SQL. With MRS, we can process the data using the function rxDataStep. This function allows to build custom data processing operations. A simple example consists in defining a new column Sepal.Size with simple maths operations. The steps are
</span></p><ol><li><span style="font-size:12pt">Define a new SQL data source object
</span></li><li><span style="font-size:12pt">Define the SQL table using <em>RxSqlServerData</em>. Its arguments are the input and output data object, and <em>overwrite = TRUE</em>, similarly to before. In addition, we define the new column using the transforms input.
</span></li><li><span style="font-size:12pt">Have a look at the new table using <em>rxGetVarInfo</em>
			</span></li></ol><p><span style="font-size:12pt">This is the code:
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas">table_iris_2 &lt;-<span style="color:#4e9a06"> "iris2"</span><br />sql_iris_2 &lt;-<span style="color:#4e9a06">
				<span style="color:#204a87"><strong>RxSqlServerData</strong></span>(<span style="color:#204a87">connectionString =</span> sql_connect,</span><br />
			<span style="color:#204a87">table =</span> table_iris_2)<br /><span style="color:#204a87"><strong>rxDataStep</strong></span>(<span style="color:#204a87">inData =</span> sql_iris, <br />
			<span style="color:#204a87">outFile =</span> sql_iris_2, <br />
			<span style="color:#204a87">transforms =</span>
			<span style="color:#204a87"><strong>list</strong></span>(<span style="color:#204a87">Sepal.Size =</span> Sepal.Length *<span style="color:#4e9a06">
			</span>Sepal.Width),<br />
			<span style="color:#204a87">overwrite =</span>
			<span style="color:#8f5902">TRUE</span>)<br /><span style="color:#204a87"><strong>rxGetVarInfo</strong></span>(sql_iris_2)
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas;font-size:11pt">## Rows Read: 150, Total Rows Processed: 150, Total Chunk Time: Less than .001 seconds
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas;font-size:11pt">## Var 1: Sepal_Length, Type: numeric, Low/High: (4.3000, 7.9000)<br />## Var 2: Sepal_Width, Type: numeric, Low/High: (2.0000, 4.4000)<br />## Var 3: Petal_Length, Type: numeric, Low/High: (1.0000, 6.9000)<br />## Var 4: Petal_Width, Type: numeric, Low/High: (0.1000, 2.5000)<br />## Var 5: Species<br />##        3 factor levels: setosa versicolor virginica<br />## Var 6: Sepal_Size, Type: numeric, Low/High: (10.0000, 30.0200)
</span></p><p><span style="font-size:12pt">As expected, we have a new column: <em>Sepal_Size</em>.
</span></p><p>Using the same approach, it's possible to build complex custom operations, making use of the open-source R functions.
</p><p>Using <em>RODBC</em>, it's possible to run an SQL query defining a new table. For instance, we can join two tables. Using an approach similar to the previous SQL query, we build the related string and execute it using <em>RODBC</em>.
</p><p style="background: #f8f8f8"><span style="font-family:Consolas">table_avg_value &lt;-<span style="color:#4e9a06"> "avg_value"</span><br />sql_avg_value &lt;-<span style="color:#4e9a06">
				<span style="color:#204a87"><strong>RxSqlServerData</strong></span>(<span style="color:#204a87">connectionString =</span> sql_connect,</span><br />
			<span style="color:#204a87">table =</span> table_avg_value)
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas"><span style="color:#204a87"><strong>rxDataStep</strong></span>(df_avg_value, sql_avg_value, <span style="color:#204a87">overwrite =</span>
			<span style="color:#8f5902">TRUE</span>)<br />query_join &lt;-<span style="color:#4e9a06"> "select Sepal_Length, Species from iris</span><br /><span style="color:#4e9a06">                left join avg_value</span><br /><span style="color:#4e9a06">                on iris.Species = avg_value.Species"</span><br />df_join &lt;-<span style="color:#4e9a06">
			</span>RODBC::<span style="color:#204a87"><strong>sqlQuery</strong></span>(channel, query_join)<br />df_join &lt;-<span style="color:#4e9a06">
			</span>df_join[, <span style="color:#204a87"><strong>c</strong></span>(<span style="color:#4e9a06">"Sepal_Length"</span>, <span style="color:#4e9a06">"Species"</span>, <span style="color:#4e9a06">"avg_sl"</span>)]<br /><span style="color:#204a87"><strong>head</strong></span>(df_join)
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas;font-size:11pt">## Joining by: "Species"
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas;font-size:11pt">##   Sepal_Length Species avg_sl<br />## 1          5.1  setosa  5.006<br />## 2          4.9  setosa  5.006<br />## 3          4.7  setosa  5.006<br />## 4          4.6  setosa  5.006<br />## 5          5.0  setosa  5.006<br />## 6          5.4  setosa  5.006
</span></p><p><span style="font-size:12pt">After having processed the data, it's possible to build machine learning models starting from the SQL tables. In this way, we don't need to pull the data in-memory and we can deal with a large data volume. For instance, to build a linear regression, we can use <em>rxLinMod</em>. Similarly to <em>rxSummary</em>, the arguments are formula, defining the variables to include, and data, defining the data source. After having built the model, we can explore is using <em>summary</em>, similarly to open-source R.
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas">model_lm &lt;-<span style="color:#4e9a06">
				<span style="color:#204a87"><strong>rxLinMod</strong></span>(<span style="color:#204a87">formula =</span> Petal_Length</span> ~<span style="color:#4e9a06">
			</span>Sepal_Length +<span style="color:#4e9a06">
			</span>Petal_Width,<br />
			<span style="color:#204a87">data =</span> sql_iris)
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas;font-size:11pt">## Rows Read: 150, Total Rows Processed: 150, Total Chunk Time: Less than .001 seconds <br />## Computation time: 0.000 seconds.
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas"><span style="color:#204a87"><strong>summary</strong></span>(model_lm)
</span></p><p style="background: #f8f8f8"><span style="font-family:Consolas;font-size:11pt">## Call:<br />## rxLinMod(formula = Petal_Length ~ Sepal_Length + Petal_Width, <br />##     data = sql_iris)<br />## <br />## Linear Regression Results for: Petal_Length ~ Sepal_Length +<br />##     Petal_Width<br />## Data: sql_iris (RxXdfData Data Source)<br />## File name: iris.xdf<br />## Dependent variable(s): Petal_Length<br />## Total independent variables: 3 <br />## Number of valid observations: 150<br />## Number of missing observations: 0 <br />##  <br />## Coefficients:<br />##              Estimate Std. Error t value Pr(&gt;|t|)    <br />## (Intercept)  -1.50714    0.33696  -4.473 1.54e-05 ***<br />## Sepal_Length  0.54226    0.06934   7.820 9.41e-13 ***<br />## Petal_Width   1.74810    0.07533  23.205 2.22e-16 ***<br />## ---<br />## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1<br />## <br />## Residual standard error: 0.4032 on 147 degrees of freedom<br />## Multiple R-squared: 0.9485 <br />## Adjusted R-squared: 0.9478 <br />## F-statistic:  1354 on 2 and 147 DF,  p-value: &lt; 2.2e-16 <br />## Condition number: 9.9855
</span></p><p><span style="font-size:12pt">The output is the same as using open-source R function <em>lm</em>. The difference is that we can apply this function on a large table without needing to pull it in-memory.
</span></p><h2><a name="conclusions" />Conclusions
</h2><p><span style="font-size:12pt">SQL and MRS are designed for different purposes. Having them into the same environment, it's possible to use each of them in the most proper context: SQL to prepare the data, MRS to build advanced machine learning models. Also, the string manipulation tools provided by R allow to build SQL queries, making it possible to write code extensions. Another option is to include R code within the SQL queries although we haven't dealt with that in this article.
</span></p>]]></content:encoded>
    </item>
    <item>
      <title>Validating a model in R Services using the k-fold</title>
      <link>https://docs.microsoft.com/archive/blogs/data_insights_global_practice/validating-a-model-in-r-services-using-the-k-fold-4</link>
      <pubDate>Fri, 08 Jul 2016 21:52:44 GMT</pubDate>
      <dc:creator><![CDATA[Michele Usuelli]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/data_insights_global_practice/?p=425</guid>
      <description><![CDATA[By Michele Usuelli, Data Scientist Consultant

Why k-fold

Predictive modelling consists in...]]></description>
      <content:encoded><![CDATA[<span style="font-size: 12pt"><strong><em>By </em>Michele Usuelli<em>,</em> Data Scientist Consultant<em>
</em></strong></span>
<h1><span style="font-size: 14pt">Why k-fold
</span></h1>
Predictive modelling consists in predicting a future outcome based on the data. Starting from data which outcome is already known, the predictive models detect patterns that had an impact on the outcome. Then, in presence of data which outcome is unknown, the model looks for the same patterns and predicts the outcome on their base. Since there are several business scenarios where a prediction will drive a lot of value, predictive modelling is among the most popular machine learning scenarios.

Before deploying a predictive model, it's essential to estimate how accurate it will be. To measure the accuracy of a model, a common approach is the <em>cross-validation</em> and it's based on testing the model on the data which outcome is already known. The measures of the accuracy are based on comparing the prediction with the actual value. In order to simulate a real-life scenario, cross-validation consists in splitting the data into two parts: the training set and the test set. The model learns the patterns from the training set and predicts the outcome of the test set.

Splitting the data in two parts as described, the estimation of the accuracy is based on the prediction of the test set only. To extend this approach to the entire data-set, we can split the data multiple times, in such a way that we perform a prediction on each record at least once. This approach is called k-fold and these are the steps:
<ol>
	<li>Split the data into k sets</li>
	<li>For each set, build a predictive model using the remaining data and predict the outcome on the set</li>
	<li>Validate the model comparing the real outcomes with the predictions</li>
</ol>
In this article we see how to implement it using SQL Server 16 with Microsoft R Services.
<h1><span style="font-size: 14pt">A use-case
</span></h1>
To show the k-fold approach, we deal with a business-like scenario. Some customers of a bank have mortgages and the bank wants to know which people are the most likely to have a default. The bank collected some data about its customers. Each record corresponds to a customer and each column to an attribute. The last column is 1 if the customer defaulted and 0 otherwise. This is a sample of the data
<div>
<table style="border-collapse: collapse;height: 399px" border="0" width="748"><colgroup> <col style="width: 166px" /> <col style="width: 149px" /> <col style="width: 185px" /> <col style="width: 113px" /> <col style="width: 89px" /> <col style="width: 116px" /></colgroup>
<tbody valign="top">
<tr style="height: 39px">
<td style="padding-left: 14px;padding-right: 14px;border-top: solid #999999 0.5pt;border-left: solid #999999 0.5pt;border-bottom: solid #666666 1.5pt;border-right: solid #999999 0.5pt"><span style="color: black"><strong>creditScore</strong></span></td>
<td style="padding-left: 14px;padding-right: 14px;border-top: solid #999999 0.5pt;border-left: none;border-bottom: solid #666666 1.5pt;border-right: solid #999999 0.5pt"><span style="color: black"><strong>houseAge</strong></span></td>
<td style="padding-left: 14px;padding-right: 14px;border-top: solid #999999 0.5pt;border-left: none;border-bottom: solid #666666 1.5pt;border-right: solid #999999 0.5pt"><span style="color: black"><strong>yearsEmploy</strong></span></td>
<td style="padding-left: 14px;padding-right: 14px;border-top: solid #999999 0.5pt;border-left: none;border-bottom: solid #666666 1.5pt;border-right: solid #999999 0.5pt"><span style="color: black"><strong>ccDebt</strong></span></td>
<td style="padding-left: 14px;padding-right: 14px;border-top: solid #999999 0.5pt;border-left: none;border-bottom: solid #666666 1.5pt;border-right: solid #999999 0.5pt"><span style="color: black"><strong>year</strong></span></td>
<td style="padding-left: 14px;padding-right: 14px;border-top: solid #999999 0.5pt;border-left: none;border-bottom: solid #666666 1.5pt;border-right: solid #999999 0.5pt"><span style="color: black"><strong>default</strong></span></td>
</tr>
<tr style="height: 39px">
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: solid #999999 0.5pt;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black"><strong>691</strong></span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">16</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">9</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">6725</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">2000</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">0</span></p>
</td>
</tr>
<tr style="height: 39px">
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: solid #999999 0.5pt;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black"><strong>691</strong></span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">4</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">4</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">5077</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">2000</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">0</span></p>
</td>
</tr>
<tr style="height: 39px">
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: solid #999999 0.5pt;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black"><strong>743</strong></span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">18</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">3</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">3080</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">2000</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">0</span></p>
</td>
</tr>
<tr style="height: 39px">
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: solid #999999 0.5pt;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black"><strong>728</strong></span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">22</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">1</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">4345</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">2000</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">0</span></p>
</td>
</tr>
<tr style="height: 39px">
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: solid #999999 0.5pt;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black"><strong>745</strong></span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">17</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">3</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">2969</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">2000</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">0</span></p>
</td>
</tr>
<tr style="height: 39px">
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: solid #999999 0.5pt;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black"><strong>539</strong></span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">15</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">3</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">4588</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">2000</span></p>
</td>
<td style="padding-left: 14px;padding-right: 14px;border-top: none;border-left: none;border-bottom: solid #999999 0.5pt;border-right: solid #999999 0.5pt">
<p style="text-align: right"><span style="color: black">0</span></p>
</td>
</tr>
</tbody>
</table>
</div>
Starting from data about new customers, the target is to predict whether the last column will be 0 or 1.
<h1><span style="font-size: 14pt">Facing the use-case using SQL Server 16
</span></h1>
In this article, we use a SQL Server 16 VM in the Azure cloud with Microsoft R Services and RStudio. To organize the R code, we use a RStudio project.

The data is initially stored into a xdf (eXternal Data Frame, the Microsoft R Services proprietary file-system). To predict the default, we use a popular model: the logistic regression.

This article is high-level in the sense that we're going through the logic of the steps, without diving into the technical details of the code syntax.

The steps to build the k-fold cross-validation are
<ol>
	<li>
<div><strong>Define the data sources and the parameters
</strong></div>
<ul>
	<li>
<div>Define a xdf specifying where the original data is stored. The data is contained into a sample data directory. To define the data source, we use the function <em>rxReadXdf</em>.<strong>
</strong></div>
<p style="margin-left: 27pt">mortDefaultSmall &lt;- rxReadXdf(file = file.path(rxGetOption("sampleDataDir"),</p>
<p style="margin-left: 27pt">"mortDefaultSmall.xdf"))</p>
</li>
	<li>
<div>Define a local xdf where we want to build a copy of the original data. The file will be stored within the RStudio project.<strong>
</strong></div>
<p style="margin-left: 18pt">xdf_in &lt;- RxXdfData("mort.xdf")</p>
</li>
	<li>
<div>Define the number of folds of the algorithm.<strong>
</strong></div>
<p style="margin-left: 18pt">n_folds &lt;- 5</p>
</li>
	<li>
<div>Define the outcome that we want to predict.<strong>
</strong></div>
<p style="margin-left: 18pt">target_outcome &lt;- "default"</p>
</li>
	<li>
<div>Define the fields that we want to use to predict the target outcome. In this case, the predictors are the first 5 columns of <em>mortDefaultSmall</em>.<strong>
</strong></div>
<p style="margin-left: 18pt">predictors &lt;- names(mortDefaultSmall)[1:5]</p>
</li>
	<li><strong>Import the data and define the fold column</strong>: we define a local copy of the mortgages data, with the addition of a fold column, containing a random number between 1 and 5, that assigns each record to one of the 5 sets. Also, we define a prediction column, containing the predicted score of having a credit default. The default value of the prediction is -1. For these purposes, we use the function <em>rxDataStep</em>.</li>
</ul>
<p style="margin-left: 18pt">rxDataStep(mortDefaultSmall, xdf_in,</p>
<p style="margin-left: 18pt">transformObjects = list(n_folds = n_folds),</p>
<p style="margin-left: 18pt">transforms = list(fold = sample(1:n_folds, .rxNumRows, TRUE),</p>
<p style="margin-left: 18pt">predicted = rep(-1., .rxNumRows)),</p>
<p style="margin-left: 18pt">overwrite = TRUE)<strong>
</strong></p>
</li>
	<li>
<div><strong>Define the features to include into the predictive model</strong>: to build the prediction model, we need to define a <em>formula</em> object describing which column is the outcome we want to predict and which columns are the predictors. The syntax is <em>outcome ~ predictor1 + predictor2.</em> To build the string, we use the function <em>paste</em>. Then, to define a formula object, we use the function <em>formula</em>.<strong>
</strong></div>
<p style="margin-left: 36pt">string_predictors &lt;- paste(predictors, collapse = " + ")</p>
<p style="margin-left: 36pt">formula_predict &lt;- formula(paste(target_outcome, string_predictors, sep = " ~ "))</p>
<p style="margin-left: 36pt">formula_predict</p>
<p style="margin-left: 36pt"><strong>default ~ creditScore + houseAge + yearsEmploy + ccDebt + year
</strong></p>
<p style="margin-left: 36pt"></p>
</li>
	<li>
<div><strong>For each fold, build and apply the model as per described before
</strong></div>
We run a for loop defining a prediction for each set.

for(this_fold in 1:n_folds) {
<ul>
	<li>
<div>Build a predictive model on the base of the remaining data. For this purpose, we use rxLogit and we specify that the model is based on the data which fold is different from <em>this_fold</em>. To build a logistic regression model, we use <em>rxLogit</em>.</div>
<p style="margin-left: 45pt">model_logit &lt;- rxLogit(formula = formula_predict,</p>
<p style="margin-left: 45pt">data = xdf_in,</p>
<p style="margin-left: 45pt">transformObjects = list(this_fold = this_fold),</p>
<p style="margin-left: 45pt">rowSelection = fold != this_fold)</p>
</li>
	<li>
<div>Predict the default on the base of the predictive model. For technical reasons, the easiest way is to predict it on the entire dataset and to select the data about the fold later. We call this column <em>prediction_fol</em>d. The function predicting the score is <em>rxPredict</em>.</div>
<p style="margin-left: 45pt">rxPredict(model_logit, xdf_in,</p>
<p style="margin-left: 45pt">predVarNames = "prediction_fold",</p>
<p style="margin-left: 45pt">overwrite = TRUE)</p>
</li>
	<li>
<div>For each record of the fold, set the <em>predicted</em> column equal to <em>prediction_fold</em>. For this purpose, we use the function <em>rxDataStep</em>.</div>
<p style="margin-left: 45pt">rxDataStep(xdf_in, xdf_in,</p>
<p style="margin-left: 45pt">transformObjects = list(this_fold = this_fold),</p>
<p style="margin-left: 45pt">transforms = list(</p>
<p style="margin-left: 45pt">predicted = ifelse(fold == this_fold, prediction_fold, predicted)),</p>
<p style="margin-left: 45pt">overwrite = TRUE)</p>
</li>
</ul>
<p style="margin-left: 18pt">}</p>
</li>
	<li>
<div><strong>Measure the accuracy of the model</strong>: to evaluate the performance, we use a popular metric called AUC. For this purpose, we use the functions <em>rxRoc</em> and <em>rxAuc</em>.<strong>
</strong></div>
<p style="margin-left: 36pt">roc &lt;- rxRoc(target_outcome, "predicted", xdf_in)</p>
<p style="margin-left: 36pt">auc &lt;- rxAuc(roc)</p>
<p style="margin-left: 36pt">auc</p>
<p style="margin-left: 36pt"><strong>[1] 0.9328864
</strong></p>
</li>
</ol>
The AUC score measures the predictive power of the model and it can be used to compare different models and optimize their parameters.
<h1>Conclusions</h1>
Using SQL Server with Microsoft R Server, we have been able to set-up the K-fold cross-validation. The code is written in such a way that it can be scaled across large data volumes.]]></content:encoded>
    </item>
  </channel>
</rss>