Självstudie: Skapa partitionsbaserade modeller i R på SQL Server
gäller för: SQL Server 2016 (13.x) och senare versioner
I SQL Server 2019 är partitionsbaserad modellering möjligheten att skapa och träna modeller över partitionerade data. För stratifierade data som naturligt segmenteras i ett visst klassificeringsschema – till exempel geografiska regioner, datum och tid, ålder eller kön – kan du köra skript över hela datauppsättningen, med möjlighet att modellera, träna och poängsätta partitioner som förblir intakta över alla dessa åtgärder.
Partitionsbaserad modellering aktiveras via två nya parametrar på sp_execute_external_script:
-
input_data_1_partition_by_columns
, som anger en kolumn som ska partitioneras av. -
input_data_1_order_by_columns
anger vilka kolumner som ska sorteras efter.
I den här självstudien lär du dig partitionsbaserad modellering med hjälp av klassiska NYC taxi-exempeldata och R-skript. Partitionskolumnen är betalningsmetoden.
- Partitioner baseras på betalningstyper (5).
- Skapa och träna modeller på varje partition och lagra objekten i databasen.
- Förutse sannolikheten för tipsresultat för varje partitionsmodell med hjälp av exempeldata som är reserverade för det ändamålet.
Förutsättningar
För att slutföra den här självstudien måste du ha följande:
Tillräckligt med systemresurser. Datamängden är stor och träningsåtgärderna är resursintensiva. Använd om möjligt ett system med minst 8 GB RAM-minne. Du kan också använda mindre datauppsättningar för att kringgå resursbegränsningar. Instruktioner för att minska datamängden är infogade.
Ett verktyg för T-SQL-frågekörning, till exempel SQL Server Management Studio (SSMS).
NYCTaxi_Sample.bak, som du kan ladda ned och återställa till din lokala SQL Server-instans. Filstorleken är cirka 90 MB.
SQL Server 2019-databasmotorinstans med Machine Learning Services och R-integrering.
Självstudien använder loopbackanslutningen till SQL Server från ett R-skript via ODBC. Därför måste du skapa en inloggning för SQLRUserGroup.
Kontrollera tillgängligheten för R-paket genom att returnera en välformaterad lista över alla R-paket som för närvarande är installerade med din databasmotorinstans:
EXECUTE sp_execute_external_script
@language=N'R',
@script = N'str(OutputDataSet);
packagematrix <- installed.packages();
Name <- packagematrix[,1];
Version <- packagematrix[,3];
OutputDataSet <- data.frame(Name, Version);',
@input_data_1 = N''
WITH RESULT SETS ((PackageName nvarchar(250), PackageVersion nvarchar(max) ))
Ansluta till databasen
Starta SSMS och anslut till databasmotorinstansen. I Object Explorerkontrollerar du att NYCTaxi_Sample databas finns.
Skapa CalculateDistance
Demodatabasen levereras med en skalär funktion för att beräkna avstånd, men vår lagrade procedur fungerar bättre med en tabellvärdesfunktion. Kör följande skript för att skapa funktionen CalculateDistance
som används i träningssteget senare.
Kontrollera \Programmability\Functions\Table-valued Functions
under NYCTaxi_Sample
-databasen i Object Explorerför att bekräfta att funktionen har skapats.
USE NYCTaxi_sample
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[CalculateDistance] (
@Lat1 FLOAT
,@Long1 FLOAT
,@Lat2 FLOAT
,@Long2 FLOAT
)
-- User-defined function calculates the direct distance between two geographical coordinates.
RETURNS TABLE
AS
RETURN
SELECT COALESCE(3958.75 * ATAN(SQRT(1 - POWER(t.distance, 2)) / nullif(t.distance, 0)), 0) AS direct_distance
FROM (
VALUES (CAST((SIN(@Lat1 / 57.2958) * SIN(@Lat2 / 57.2958)) + (COS(@Lat1 / 57.2958) * COS(@Lat2 / 57.2958) * COS((@Long2 / 57.2958) - (@Long1 / 57.2958))) AS DECIMAL(28, 10)))
) AS t(distance)
GO
Definiera en procedur för att skapa och träna modeller per partition
Den här självstudien inkapslar R-skript i en lagrad procedur. I det här steget skapar du en lagrad procedur som använder R för att skapa en indatauppsättning, skapa en klassificeringsmodell för att förutsäga tipsresultat och sedan lagra modellen i databasen.
Bland de parameterindata som används av det här skriptet visas input_data_1_partition_by_columns
och input_data_1_order_by_columns
. Kom ihåg att dessa parametrar är den mekanism som partitionerad modellering sker med. Parametrarna skickas som indata till sp_execute_external_script för att bearbeta partitioner med det externa skriptet som körs en gång för varje partition.
För den här lagrade proceduren använder parallellitet för snabbare tid till slutförande.
När du har kört det här skriptet i Object Explorerbör du se train_rxLogIt_per_partition
i \Programmability\Stored Procedures
under NYCTaxi_Sample
-databasen. Du bör också se en ny tabell som används för att lagra modeller: dbo.nyctaxi_models
.
USE NYCTaxi_Sample
GO
CREATE
OR
ALTER PROCEDURE [dbo].[train_rxLogIt_per_partition] (@input_query NVARCHAR(max))
AS
BEGIN
DECLARE @start DATETIME2 = SYSDATETIME()
,@model_generation_duration FLOAT
,@model VARBINARY(max)
,@instance_name NVARCHAR(100) = @@SERVERNAME
,@database_name NVARCHAR(128) = db_name();
EXEC sp_execute_external_script @language = N'R'
,@script =
N'
# Make sure InputDataSet is not empty. In parallel mode, if one thread gets zero data, an error occurs
if (nrow(InputDataSet) > 0) {
# Define the connection string
connStr <- paste("Driver=SQL Server;Server=", instance_name, ";Database=", database_name, ";Trusted_Connection=true;", sep="");
# build classification model to predict a tip outcome
duration <- system.time(logitObj <- rxLogit(tipped ~ passenger_count + trip_distance + trip_time_in_secs + direct_distance, data = InputDataSet))[3];
# First, serialize a model to and put it into a database table
modelbin <- as.raw(serialize(logitObj, NULL));
# Create the data source. To reduce data size, add rowsPerRead=500000 to cut the dataset by half.
ds <- RxOdbcData(table="ml_models", connectionString=connStr);
# Store the model in the database
model_name <- paste0("nyctaxi.", InputDataSet[1,]$payment_type);
rxWriteObject(ds, model_name, modelbin, version = "v1",
keyName = "model_name", valueName = "model_object", versionName = "model_version", overwrite = TRUE, serialize = FALSE);
}
'
,@input_data_1 = @input_query
,@input_data_1_partition_by_columns = N'payment_type'
,@input_data_1_order_by_columns = N'passenger_count'
,@parallel = 1
,@params = N'@instance_name nvarchar(100), @database_name nvarchar(128)'
,@instance_name = @instance_name
,@database_name = @database_name
WITH RESULT SETS NONE
END;
GO
Parallell körning
Observera att sp_execute_external_script indata inkluderar @parallel=1
, som används för att aktivera parallell bearbetning. Till skillnad från tidigare versioner, med början i SQL Server 2019, ger inställningen @parallel=1
ett starkare tips till frågeoptimeraren, vilket gör parallell körning till ett mycket mer sannolikt resultat.
Som standard tenderar frågeoptimeraren att fungera under @parallel=1
på tabeller som har fler än 256 rader, men om du kan hantera detta explicit genom att ange @parallel=1
som visas i det här skriptet.
Tips
För träningsuppgifter kan du använda @parallel
med vilket träningsskript som helst, även de som använder algoritmer som inte är från Microsoft-rx. Vanligtvis erbjuder endast RevoScaleR-algoritmer (med rx-prefixet) parallellitet i träningsscenarier i SQL Server. Men med den nya parametern kan du parallellisera ett skript som anropar funktioner, inklusive R-funktioner med öppen källkod, som inte är specifikt utformade med den funktionen. Detta fungerar eftersom partitioner har tillhörighet till specifika trådar, så alla åtgärder som anropas i ett skript körs per partition på den angivna tråden.
Kör proceduren och träna modellen
I det här avsnittet tränar skriptet den modell som du skapade och sparade i föregående steg. Exemplen nedan visar två metoder för att träna din modell: att använda en hel datauppsättning eller en partiell datamängd.
Förvänta dig att det här steget tar en stund. Träningen är beräkningsintensiv, vilket tar många minuter att slutföra. Om systemresurser, särskilt minne, inte är tillräckliga för belastningen använder du en delmängd av data. Det andra exemplet innehåller syntaxen.
--Example 1: train on entire dataset
EXEC train_rxLogIt_per_partition N'
SELECT payment_type, tipped, passenger_count, trip_time_in_secs, trip_distance, d.direct_distance
FROM dbo.nyctaxi_sample CROSS APPLY [CalculateDistance](pickup_latitude, pickup_longitude, dropoff_latitude, dropoff_longitude) as d
';
GO
--Example 2: Train on 20 percent of the dataset to expedite processing.
EXEC train_rxLogIt_per_partition N'
SELECT tipped, payment_type, passenger_count, trip_time_in_secs, trip_distance, d.direct_distance
FROM dbo.nyctaxi_sample TABLESAMPLE (20 PERCENT) REPEATABLE (98074)
CROSS APPLY [CalculateDistance](pickup_latitude, pickup_longitude, dropoff_latitude, dropoff_longitude) as d
';
GO
Not
Om du kör andra arbetsbelastningar kan du lägga till OPTION(MAXDOP 2)
till SELECT-instruktionen om du vill begränsa frågebearbetningen till bara 2 kärnor.
Kontrollera resultat
Resultatet i modelltabellen ska vara fem olika modeller, baserat på fem partitioner segmenterade efter de fem betalningstyperna. Modellerna finns i datakällan ml_models
.
SELECT *
FROM ml_models
Definiera en procedur för att förutsäga resultat
Du kan använda samma parametrar för bedömning. Följande exempel innehåller ett R-skript som ska poängsätta med hjälp av rätt modell för den partition som för närvarande bearbetas.
Precis som tidigare skapar du en lagrad procedur för att omsluta R-koden.
USE NYCTaxi_Sample
GO
-- Stored procedure that scores per partition.
-- Depending on the partition being processed, a model specific to that partition will be used
CREATE
OR
ALTER PROCEDURE [dbo].[predict_per_partition]
AS
BEGIN
DECLARE @predict_duration FLOAT
,@instance_name NVARCHAR(100) = @@SERVERNAME
,@database_name NVARCHAR(128) = db_name()
,@input_query NVARCHAR(max);
SET @input_query = 'SELECT tipped, passenger_count, trip_time_in_secs, trip_distance, d.direct_distance, payment_type
FROM dbo.nyctaxi_sample TABLESAMPLE (1 PERCENT) REPEATABLE (98074)
CROSS APPLY [CalculateDistance](pickup_latitude, pickup_longitude, dropoff_latitude, dropoff_longitude) as d'
EXEC sp_execute_external_script @language = N'R'
,@script =
N'
if (nrow(InputDataSet) > 0) {
#Get the partition that is currently being processed
current_partition <- InputDataSet[1,]$payment_type;
#Create the SQL query to select the right model
query_getModel <- paste0("select model_object from ml_models where model_name = ", "''", "nyctaxi.",InputDataSet[1,]$payment_type,"''", ";")
# Define the connection string
connStr <- paste("Driver=SQL Server;Server=", instance_name, ";Database=", database_name, ";Trusted_Connection=true;", sep="");
#Define data source to use for getting the model
ds <- RxOdbcData(sqlQuery = query_getModel, connectionString = connStr)
# Load the model
modelbin <- rxReadObject(ds, deserialize = FALSE)
# unserialize model
logitObj <- unserialize(modelbin);
# predict tipped or not based on model
predictions <- rxPredict(logitObj, data = InputDataSet, overwrite = TRUE, type = "response", writeModelVars = TRUE
, extraVarsToWrite = c("payment_type"));
OutputDataSet <- predictions
} else {
OutputDataSet <- data.frame(integer(), InputDataSet[,]);
}
'
,@input_data_1 = @input_query
,@parallel = 1
,@input_data_1_partition_by_columns = N'payment_type'
,@params = N'@instance_name nvarchar(100), @database_name nvarchar(128)'
,@instance_name = @instance_name
,@database_name = @database_name
WITH RESULT SETS((
tipped_Pred INT
,payment_type VARCHAR(5)
,tipped INT
,passenger_count INT
,trip_distance FLOAT
,trip_time_in_secs INT
,direct_distance FLOAT
));
END;
GO
Skapa en tabell för att lagra förutsägelser
CREATE TABLE prediction_results (
tipped_Pred INT
,payment_type VARCHAR(5)
,tipped INT
,passenger_count INT
,trip_distance FLOAT
,trip_time_in_secs INT
,direct_distance FLOAT
);
TRUNCATE TABLE prediction_results
GO
Kör proceduren och spara förutsägelser
INSERT INTO prediction_results (
tipped_Pred
,payment_type
,tipped
,passenger_count
,trip_distance
,trip_time_in_secs
,direct_distance
)
EXECUTE [predict_per_partition]
GO
Visa förutsägelser
Eftersom förutsägelserna lagras kan du köra en enkel fråga för att returnera en resultatuppsättning.
SELECT *
FROM prediction_results;
Nästa steg
- I den här handledningen använde du sp_execute_external_script för att iterera åtgärder över partitionerade data. Om du vill titta närmare på hur du anropar externa skript i lagrade procedurer och använder RevoScaleR-funktioner fortsätter du med följande självstudie.