Merge pull request #155 from davidsalgado/master

Added a 'predictive analytics' article + lab
This commit is contained in:
Perry Skountrianos - MSFT
2016-11-14 12:02:58 -08:00
committed by GitHub
15 changed files with 589 additions and 0 deletions
@@ -0,0 +1,51 @@
Implementing Predictive Analytics in Your Applications
============================================================
Predictive analytics are a powerful way to add intelligence to your application, it enables you to predict outcomes against data that is new to your application. The Microsoft data platform provides numerous ways you can add predictive analytics to your applications
What is predictive analytics?
-------------------------------
Before we get into the implementation, lets address a fundamental question—**what is predictive analytics?** At their core, predictive tasks are those that predict one value given a set of other values as input. In other words, predictive tasks learn (or are taught) how to make predictions. This learning is captured in a model by an algorithm. Think of the model as the way the learnings are compactly summarized. When you want to make a prediction, you invoke a prediction operation and provide the model as one of the inputs, along with the input values against which you want to form a prediction. Predictive analytics is the act of applying prediction (and your model) to your data to gain new insights.
So, **what are some examples of predictive analytics?** These fall out into two basic categories. You have _prediction that aims to predict the class (or category) of something_. For example, you can have single class classification that tries to predict if an email is spam or not spam—so the class is either “spam” or “not spam”. You can also have multi-class classification, that predicts an outcome from a set of possible outcomes. For example, you can have a multi-class classification that predicts if a consumer is at “high risk”, “moderate risk”, or “low risk” of default on a loan.
You also have _numeric prediction_. Instead of trying to predict a class from a fixed set of options, numeric prediction tries to predict a numeric value from a continuous range of numbers. For example, you might try to predict how long of a delay in minutes a flight will experience or the currency value of a particular stock in the stock market.
Prediction on the Microsoft Data Platform
----------------------------------------------
The Microsoft Data Platform provide numerous ways you can build predictive models that you can then integrate into your application. The following diagram summarizes the options:
![Alternatives to train and use a model](imgs/UseModelForPrediction.png "Model Train and use")
As you can infer from the diagram, the act of incorporating predictive analytics into your applications involves two major phases: model creation and model operationalization. Conceptually, these are very simple to understand.
**Model Creation:** During model creation, you train your predictive model (by showing it sample data along with the outcomes) and test that it works (at least that it predicts results better than random chance would). You save this model so you can use it later when you want to make predictions against new data.
**Model Operationalization:** During model operationalization, you are implementing predictions that use your model in whatever hosting environment (such as a web service) makes sense for integration with your application. In other words, operationalization is how you add predictive analytics to your application.
Options for Model Creation
-----------------------------
Lets begin by understanding the various ways you can train and test your model. When creating your model, you can train your model locally. This is amounts to authoring and running R or Python scripts on your development workstation. For example, you might use the integrated development environment R GUI (a component in Microsoft R Open) or R Tools for Visual Studio to author your R scripts that train your model, help you test it and visualize the results.
Alternately, the training can be done using resources that are remote to your development workstation. The Microsoft Data Platform offers the following options for this:
* **Azure Machine Learning (Azure ML):** Azure ML enables you to design predictive experiments (referred to as scoring experiments) using its browser based Machine Learning Studio. The visual drag-and-drop experience is like designing a flowchart, where each box of the flowchart is called a “module”. Modules can retrieve data, transform data, process data, create predictive models and evaluate their predictive performance. There are numerous built in modules that let you define and run custom script code written in R or Python as desired.
* **HDInsight:** HDInsight provides numerous ways you can train predictive models using a cluster of servers running in Azure. With R Server on Spark and R Server on Hadoop, you author R scripts whose execution runs across the cluster to train (and test) your model. If you deploy an HDInsight with Spark, you can use Spark ML to program the training and testing models using Scala, Java, Python or R. Generally, the data used to train models in HDInsight comes from a form of highly scalable block storage such as HDFS, Azure Data Lake Store or Azure Storage Blobs.
* **SQL R Services:** SQL in R Services enable you to train and test predictive models in the context of SQL Server 2016. You author T-SQL procedures that contain embedded R scripts, and the SQL Server database engine takes care of the execution. Because it executes in the context of SQL Server, your models can be easily trained against data stored within tables within your database.
Option for Model Operationalization
---------------------------------------
The Microsoft Data Platform also provide multiple ways of adding predictive analytics to an application. As you can see in the diagram, while there are many ways to train a model there tend to be only a few practical ways to use the model from an application.
* **Invoke Predict within a Script:** When running within a local environment, you can easily use the trained model as input into your predictive script.
* **Invoke Predictive Web Service:** When running in a remote environment, a common approach is to encapsulate the call to prediction in a web service/Rest API operation that is readily invoked from an application. For Azure ML, this is as easy as a few clicks to deploy a predictive experiment as a web service. For HDInsight, this amounts to exporting your trained model to a file and importing the model into a compatible host such a Microsofts DeployR (for models created with R Server on Spark or Hadoop), which wraps a web services layer around a prediction script written in R.
* **Invoke a Predictive Store Procedure:** When using SQL R Services, you can package the code that invokes prediction using your model within a stored procedure. Therefore, integrating a prediction into your application becomes a matter of executing a stored a procedure in SQL Server—something that most applications can easily accomplish regardless of whether they are written in .NET, node.js, Java...
While each approach has its merits, in the accompanying lab, well examine how to augment a node.js application with predictive analytics using this last approach that invokes a predictive stored procedure running in SQL Server 2016.
**Done with the intro?**
[Start the lab](scripts/Lab.md)
Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

@@ -0,0 +1,2 @@
CREATE DATABASE taxidata;
GO
@@ -0,0 +1,24 @@
USE [taxidata]
GO
CREATE FUNCTION [dbo].[fnEngineerFeatures] (
@passenger_count int = 0,
@trip_distance float = 0,
@trip_time_in_secs int = 0,
@direct_distance float = 0)
RETURNS TABLE
AS
RETURN
(
SELECT
@passenger_count AS passenger_count,
@trip_distance AS trip_distance,
@trip_time_in_secs AS trip_time_in_secs,
@direct_distance as direct_distance
)
GO
@@ -0,0 +1,33 @@
use taxidata
go
CREATE PROCEDURE [dbo].[TrainTipPredictionModel]
AS
BEGIN
DECLARE @inquery nvarchar(max) = N'
select tipped, passenger_count, trip_time_in_secs, trip_distance, direct_distance
from nyctaxi_features
'
--delete previous stored models
truncate table dbo.nyc_taxi_models
-- Insert the trained model into a database table
INSERT INTO nyc_taxi_models
EXEC sp_execute_external_script
@language = N'R',
@script = N'
## Create model
logitObj <- rxLogit(tipped ~ passenger_count + trip_distance + trip_time_in_secs + direct_distance, data = InputDataSet)
## Serialize model and put it in data frame
trained_model <- data.frame(model=as.raw(serialize(logitObj, NULL)));
',
@input_data_1 = @inquery,
@output_data_1_name = N'trained_model'
;
END
GO
@@ -0,0 +1,9 @@
USE [taxidata]
GO
CREATE TABLE [dbo].[nyc_taxi_models](
[model] [varbinary](max) NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
@@ -0,0 +1,15 @@
USE [taxidata]
GO
CREATE TABLE [dbo].[nyctaxi_features](
[passenger_count] [int] NULL,
[trip_time_in_secs] [bigint] NULL,
[trip_distance] [float] NULL,
[direct_distance] [float] NULL,
[tip_amount] [float] NULL,
[tipped] [int] NULL
) ON [PRIMARY]
GO
@@ -0,0 +1,23 @@
## Just in case you want to go through the steps on an R tool like (RGui, RStudio or R Tools for VS)
##this script uses rpart instead of rxlogit
## these are the step by step to reproduce the lab
install.packages("RODBC")
library(RODBC)
##Connect to SQL Server 2016, assumes a Windows Authentication method
dbhandle <- odbcDriverConnect('driver={SQL Server};server=<yourservername>;database=taxidata;trusted_connection=true')
##Run the query to brin the data we'll use to create the model
res <- sqlQuery(dbhandle, 'select tipped, passenger_count, trip_time_in_secs, trip_distance, direct_distance from nyctaxi_features')
##Create the model...
model <- rxLogit(tipped ~ passenger_count + trip_distance + trip_time_in_secs + direct_distance, res)
summary(model)
##Now, let's create the frame with the parameters for the prediction
prediction_parameters <- data.frame(passenger_count = 1, trip_time_in_secs = 631, trip_distance = 2.5, direct_distance = 2)
##predict
OutputDataFrame <- rxPredict(model, prediction_parameters, outData = NULL, predVarNames = "Score", type = "response", writeModelVars = FALSE, overwrite = TRUE)
OutputDataFrame
@@ -0,0 +1,358 @@
# Implement Predictive Analytics - Lab
In this lab, you will implement predictive analytics to predict the likelihood a taxi driver will be receive a tip given detail about the trip including the number of passengers, the trip distance (as measured by the odometer), the linear trip distance (e.g., as the crow flies), and the trip duration. Using SQL Server 2016 R Services, you will train the model, store the model in a table, and make the prediction available via a stored procedure which you will invoke from a simple node.js application.
Requirements
-----------------
* [SQL Server 2016 Developer Edition](https://www.microsoft.com/en-us/sql-server/sql-server-editions-developers) or higher
* [Visual Studio Code](http://code.visualstudio.com)
* Node.js
A tool to run SQL scripts against your SQL Server database, such as [SQL Server Management Studio (SSMS)](https://msdn.microsoft.com/library/mt238290.aspx)
This lab assumes you have setup SQL Server 2016 Developer Edition locally on your workstation or a remote instance.
Required SQL Server Configuration
* Make sure that your installation of SQL Server includes R Services.
* Using SQL Server Configuration Manager, make sure that TCP/IP connections are enabled to your instance of SQL Server.
* Be sure that the SQL Server, SQL Server Launchpad and SQL Server Browser services are all running.
Download the Project
----------------------
Clone this repo to have the sample application and setup scripts.
### Setup the sample database
The following steps will get your taxidata database setup and loaded with data.
1. Using the SQL tool of your choice (SQL Server Management Studio, Visual Studio Code with the MSSQL extension), connect to your database and execute the following scripts (provided with the project files) to create the database, a table to hold the taxi data, and load the 1.7M records into the taxi trip data table.
- *CreateDatabase.sql*
- *Create nyctaxi_features Table.sql*
- *Load nyctaxi_features using BCP.sql* **Important**, make sure that you edit the script to point to the folder where you've cloned the .bcp file
2. Next, create a table valued function that will package inputs received by the stored procedure into a tabular format by executing the following script. You will use this function later within the stored procedure that makes predictions.
- *Create Function fnEngineerFeatures.sql*
3. Execute the following script to create a table that will persist the predictive model you will generate. Observe that this table has a schema that consists of one column of type varbinary(max). This column will hold the serialized representation of your model.
- *Create nyc_taxi_models Table.sql*
### Train the Model
To train your model, you will create a stored procedure that you can run at any time to train your model and store its serialized form in the nyctaxi_features table.
1. Execute the following script to define the stored procedure:
- *Create Procedure TrainTipPredictionModel.sql*
2. Execute the following script to train the model and store it.
- *Exec TrainTipPredictionModel.sql*. You may need to [configure external scripts](https://msdn.microsoft.com/en-us/library/mt590884.aspx) and restart SQL.
Lets take a closer look at the contents of the stored procedure.
```
CREATE PROCEDURE [dbo].[TrainTipPredictionModel]
AS
BEGIN
DECLARE @inquery nvarchar(max) = N'
select tipped, passenger_count, trip_time_in_secs, trip_distance, direct_distance
from nyctaxi_features
'
-- Before insterting a new model, we delete the previous one
truncate table dbo.nyc_taxi_models
-- Insert the trained model into a database table
INSERT INTO nyc_taxi_models
EXEC sp_execute_external_script
@language = N'R',
@script = N'
##Create model
logitObj <- rxLogit(tipped ~ passenger_count + trip_distance + trip_time_in_secs +
direct_distance, data = InputDataSet)
##Serialize model and put it in data frame
trained_model <- data.frame(model=as.raw(serialize(logitObj, NULL)));
',
@input_data_1 = @inquery,
@output_data_1_name = N'trained_model'
;
END
GO
```
The procedure begins by defining a query that retrieves the sample data contained in the nyctaxi_features table.
```
DECLARE @inquery nvarchar(max) = N'
select tipped, passenger_count, trip_time_in_secs, trip_distance, direct_distance
from nyctaxi_features
'
```
This query is passed as one of the parameters to sp_execute_external_script, via the @input_data_1 parameter.
Next, a call to sp_execute_external_script is constructed. The return value of this stored procedure call is the serialized model, which is saved into the nyc_taxi_models table. The inputs to sp_execute_external_script are:
* __@language__: needs to indicate that the script is written in the R language.
* __@script__: this is the actual R script that uses the rxLogit function to train a model.
* __@input_data_1__: by convention represent the query that is accessible via InputDataSet within the R script.
* __@output_data_1_name__: provides the column name used in the result set containing the serialized model.
Looking at the R script specifically, we have:
```
##Create model
logitObj <- rxLogit(tipped ~ passenger_count + trip_distance + trip_time_in_secs +
direct_distance, data = InputDataSet)
##Serialize model and put it in data frame
trained_model <- data.frame(model=as.raw(serialize(logitObj, NULL)));
```
The second line trains the model using the **rxLogit** method, which performs a **logistic regression**. Observe that the inputs are expressed in a formula syntax that describes what feature to predict and what features to use in its prediction:
```
tipped ~ passenger_count + trip_distance + trip_time_in_secs + direct_distance
```
The formula reads as such: predict **tipped** given the *passenger_count*, *trip_distance*, *trip_time_in_secs* and *direct_distance*. The source of these features comes from the data set made available via the InputDataSet variable.
After that, we serialize the model into a **data.frame** and store it in the *trained_model* variable, which is returned as an output result set consisting of one cell with the column name *trained_model* and value of the serialized model.
### Operationalize the Model
With trained model in hand, you are ready operationalize the model and make it available to your application.
1. Execute the following script to operationalize the model in a stored procedure.
- *Create Procedure PredictTip.sql*
Lets take a closer look at this stored procedure.
```
CREATE PROCEDURE [dbo].[PredictTip]
@passenger_count int = 0,
@trip_distance float = 0,
@trip_time_in_secs int = 0,
@direct_distance float = 0
AS
BEGIN
-- Package the inputs as a table
DECLARE @inquery nvarchar(max) = N'
SELECT * FROM [dbo].[fnEngineerFeatures](
@passenger_count,
@trip_distance,
@trip_time_in_secs,
@direct_distance)
'
-- Load the serialized model from the nyc_taxi_models table
DECLARE @lmodel2 varbinary(max) = (SELECT TOP 1 model FROM nyc_taxi_models);
-- Invoke the prediction
EXEC sp_execute_external_script
@language = N'R',
@script = N'
mod <- unserialize(as.raw(model));
OutputDataSet<-rxPredict(modelObject = mod, data = InputDataSet,
outData = NULL,
predVarNames = "Score", type = "response",
writeModelVars = FALSE, overwrite = TRUE);
',
@input_data_1 = @inquery,
@params = N'@model varbinary(max),
@passenger_count int,
@trip_distance float,
@trip_time_in_secs int ,
@direct_distance float',
@model = @lmodel2,
@passenger_count = @passenger_count ,
@trip_distance = @trip_distance,
@trip_time_in_secs = @trip_time_in_secs,
@direct_distance = @direct_distance
WITH RESULT SETS ((Score float));
END
```
The PredictTip procedure takes as input the passenger count, trip distance (odometer reading), trip time and direct distance (calculated linear distance).
The first query uses the fnEngineerFeatures table valued function to package the values of the input parameters as a table:
```
DECLARE @inquery nvarchar(max) = N'
SELECT * FROM [dbo].[fnEngineerFeatures](
@passenger_count,
@trip_distance,
@trip_time_in_secs,
@direct_distance)
'
```
After that we retrieve the serialized model from the nyc_taxi_models table:
```
-- Load the serialized model from the nyc_taxi_models table
DECLARE @lmodel2 varbinary(max) = (SELECT TOP 1 model FROM nyc_taxi_models);
```
Following that we invoke the prediction using a call to sp_execute_external_script:
```
--Invoke the prediction
EXEC sp_execute_external_script
@language = N'R',
@script = N'
mod <- unserialize(as.raw(model));
OutputDataSet<-rxPredict(modelObject = mod, data = InputDataSet,
outData = NULL,
predVarNames = "Score", type = "response",
writeModelVars = FALSE, overwrite = TRUE);
',
@input_data_1 = @inquery,
@params = N'@model varbinary(max),
@passenger_count int,
@trip_distance float,
@trip_time_in_secs int ,
@direct_distance float',
@model = @lmodel2,
@passenger_count = @passenger_count ,
@trip_distance = @trip_distance,
@trip_time_in_secs = @trip_time_in_secs,
@direct_distance = @direct_distance
WITH RESULT SETS ((Score float));
```
Observe that we pass as input the following parameters:
* @language: needs to indicate that the script is written in the R language.
* @script: this is the actual R script that uses the rxPredict function to make the prediction.
* @input_data_1: the query which contains the one row of data against which we make a prediction.
* @params: defines the parameters and SQL types of all the parameters used.
* @model: the serialized model.
* @passenger_count, @trip_distance, @trip_time_in_secs, @direct_distance: the values that will be packaged into a table after executing the query defined by @inquery.
The R script used for prediction has only the following two lines:
```
mod <- unserialize(as.raw(model));
OutputDataSet<-rxPredict(modelObject = mod, data = InputDataSet,
outData = NULL, predVarNames = "Score", type = "response",
writeModelVars = FALSE, overwrite = TRUE);
```
The first line deserializes the model so it is in a form useable by the **rxPredict** method. The second line invokes rxPredict which uses the model against the supplied row of data (within InputDataSet, which is sourced from the query in @inquery).
Finally, the script ends using the following line:
```
WITH RESULT SETS ((Score float));
```
This schematizes the result set returned in OutputDataSet within the R script. This data set has one column, labeled Score with a type of float. The label of “Score” was configured in the call to rxPredict via the predVarNames parameter.
### Execute a prediction in T-SQL
Now you are ready to give your predictive stored procedure a test run.
1. Run the following script to predict the probability of a tip using the PredictTip stored procedure.
- *Exec PredictTip.sql*
2. You should get a result set consisting of one row and one column (labeled Score), for example:
### Execute the sample in Node.js
Now lets integrate a call to this stored procedure from our node.js sample application.
1. Within the root of the project directory, at the command line execute:
```
npm install tedious
```
2. This will install the tedious package which we use to connect to SQL Server.
3. Open TipPredictor.js in Visual Studio Code.
4. Near the top, modify the values of the config element so that they contain the appropriate values to connect to your instance of the taxidata database.
Provide the connection details appropriate to your environment (Change the user/pass and instanceName to match your environment)
```
var config = {
userName: 'youruser',
password: 'yourpass',
server: 'localhost',
options: {
database: 'taxidata',
instanceName: 'SQL2016DEVED',
encrypt: true
}
};
```
5. Save the file.
6. Scroll down to the connect.on() callback implementation.
```
connection.on('connect', function(err) {
if (err)
{
console.log("Unable to Connect: " + err);
return;
}
// If no error, then good to go...
console.log("Connected.");
executeStatement();
});
```
7. Observe that this method connects to SQL Server using tedious. If it connects successfully, it executes the method executeStatement().
8. Look at the implementation for executeStatement().
```
function executeStatement() {
// Specify the name of the predictive stored procedure
storedProcedureName = "[dbo].[PredictTip]";
request = new Request(storedProcedureName, function(err, rowCount) {
if (err) {
console.log(err);
} else {
console.log(rowCount + ' rows');
}
});
// The input values to the prediction are provided here:
request.addParameter('passenger_count', TYPES.Int, '1');
request.addParameter('trip_distance', TYPES.Float, '2.5');
request.addParameter('trip_time_in_secs', TYPES.Int, '631');
request.addParameter('direct_distance', TYPES.Float, '2');
// Iterate over any received rows in the result
request.on('row', function(columns) {
columns.forEach(function(column) {
console.log(column.metadata.colName + " = " + column.value);
});
});
connection.callProcedure(request);
}
```
9. Observe that this method builds up a Request object that takes the stored procedure name and contains the input parameters upon which the prediction will execute. The return value of the stored procedure is handled via the *request.on(row)* callback. The stored procedure is actually invoked at the last statement, via *connection.callProcedure(request)*.
10. Open an instance of the command line and navigate to the directory containing TipPredictor.js.
11. Run the sample application by typing:
```
node TipPredictor.js
```
12. You should see output like the following (which in this case means there is a 53% chance of a tip):
```
node TipPredictor.js
Connected.
Score = 0.5333974344542649
3 rows
```
### Congratulations!!!
Youve just trained and operationalized a model using SQL Server 2016 and enabled a node.js application with predictive analytics capabilities.
### Additional resources
@@ -0,0 +1,10 @@
USE taxidata;
GO
BULK INSERT taxidata.dbo.nyctaxi_features
FROM 'C:\Implementing Predictive Analytics\nyctaxi_features.bcp'
WITH (
DATAFILETYPE = 'native'
);
GO
@@ -0,0 +1,60 @@
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;
var TYPES = require('tedious').TYPES;
// Provide the connection details appropriate to your environment
var config = {
userName: '<youruser>',
password: '<yourpass>',
server: '<yourserver>',
options: {
database: 'taxidata',
encrypt: true
}
};
var connection = new Connection(config);
connection.on('connect', function(err) {
if (err)
{
console.log("Unable to Connect: " + err);
return;
}
// If no error, then good to go...
console.log("Connected.");
executeStatement();
});
function executeStatement() {
// Specify the name of the predictive stored procedure
storedProcedureName = "[dbo].[PredictTip]";
request = new Request(storedProcedureName, function(err, rowCount) {
if (err) {
console.log(err);
} else {
console.log(rowCount + ' rows');
}
});
// The input values to the prediction are provided here:
request.addParameter('passenger_count', TYPES.Int, '1');
request.addParameter('trip_distance', TYPES.Float, '2.5');
request.addParameter('trip_time_in_secs', TYPES.Int, '631');
request.addParameter('direct_distance', TYPES.Float, '2');
// Iterate over any received rows in the result
request.on('row', function(columns) {
columns.forEach(function(column) {
console.log(column.metadata.colName + " = " + column.value);
});
});
connection.callProcedure(request);
}
+4
View File
@@ -1,5 +1,9 @@
# Samples for SQL Server R Services
[Implementing Predictive Analytics](Implementing Predictive Analytics)
Step-by-step sample that explains the basics about predictive analytics for developers. The lab will take you about 15 min and will show you how to create an application that uses node.js and SQL Server 2016 to predict if a cab driver will be tipped or not.
[Telco Customer Churn](Telco Customer Churn)
Telco Customer Churn sample using SQL Server R Services.