machinelearning-samples icon indicating copy to clipboard operation
machinelearning-samples copied to clipboard

System.InvalidCastException: Unable to cast object of type 'System.Double' to type 'System.Single'.

Open ropiela opened this issue 5 years ago • 2 comments

Looking to convert this example (https://github.com/dotnet/machinelearning-samples/tree/master/samples/csharp/getting-started/AnomalyDetection_Sales) to read from a local SqlServer database. The problem seems to be in the lazy loading of the data from the database. I'm reading two integers from a table (an ID and Status), and trying to figure out when a change in Status occurs. The example code works when reading from a CSV file.

Do I need to do something to convert the integer being read from the db table to a float? I didn't need to do that when reading from the CSV file.

Using ML.Net 1.5.2

Exception: Unhandled exception. System.InvalidCastException: Unable to cast object of type 'System.Double' to type 'System.Single'. at System.Data.SqlClient.SqlBuffer.get_Single() at System.Data.SqlClient.SqlDataReader.GetFloat(Int32 i) at Microsoft.ML.Data.DatabaseLoader.Cursor.<>c__DisplayClass37_0.<CreateSingleGetterDelegate>b__0(Single& value) at Microsoft.ML.Data.TypedCursorable1.TypedRowBase.<>c__DisplayClass10_01.<CreateDirectSetter>b__0(TRow row) at Microsoft.ML.Data.TypedCursorable1.TypedRowBase.FillValues(TRow row) at Microsoft.ML.Data.TypedCursorable1.TypedCursor.FillValues(TRow row) at Microsoft.ML.Data.TypedCursorable1.RowCursorImplementation.FillValues(TRow row) at Microsoft.ML.Transforms.StatefulFilterTransform3.Cursor.RunLambda(Boolean& isRowAccepted) at Microsoft.ML.Transforms.StatefulFilterTransform3.Cursor.MoveNextCore() at Microsoft.ML.Data.RootCursorBase.MoveNext() at Microsoft.ML.Data.SynchronizedCursorBase.MoveNext() at Microsoft.ML.Data.TypedCursorable1.TypedCursor.MoveNext() at Microsoft.ML.Data.TypedCursorable1.RowCursorImplementation.MoveNext() at Microsoft.ML.PipeEngine1.RunPipe(Boolean reuseRowObject)+MoveNext() at MLNetDemo.Program.Main(String[] args) in C:\MLNetDemo\Program.cs:line 58

Code: ` public class ConnectionStatus { public long ConnectionId; public float Status; } public class ConnectionStatusPrediction { [VectorType(3)] public double[] Prediction { get; set; } } public class Program { static void Main(string[] args) { int size = 36;

        MLContext mlContext = new MLContext();
        IDataView dataView = GetDatabaseDataView(mlContext);

        IidChangePointEstimator estimator = mlContext.Transforms.DetectIidChangePoint(nameof(ConnectionStatusPrediction.Prediction), nameof(ConnectionStatus.Status), 95, size / 5);
        ITransformer tansformedModel = estimator.Fit(CreateEmptyDataView(mlContext));
        IDataView transformedData = tansformedModel.Transform(dataView);

        IEnumerable<ConnectionStatusPrediction> predictions = mlContext.Data.CreateEnumerable<ConnectionStatusPrediction>(transformedData, reuseRowObject: false);

        Console.WriteLine("Alert\tScore\tP-Value");

        // code throws exception here, when iterating through the list of predictions
        foreach(ConnectionStatusPrediction p in predictions)
        {
            Console.WriteLine("{0}\t{1:0.00}\t{2:0.00}", p.Prediction[0], p.Prediction[1], p.Prediction[2]);
            Console.ResetColor();
        }
        Console.WriteLine("");
    }

    private static IDataView CreateEmptyDataView(MLContext mlContext)
    {
        //Create empty DataView. We just need the schema to call fit()
        IEnumerable<ConnectionStatus> enumerableData = new List<ConnectionStatus>();
        IDataView dv = mlContext.Data.LoadFromEnumerable(enumerableData);
        return dv;
    }

    private static IDataView GetDatabaseDataView(MLContext mlContext)
    {
        string connectionStr = "Data Source=localhost; Initial Catalog=connectionstatus;Integrated Security=SSPI;";
        string query = "select ConnectionId, cast([Status] as float) as [Status] from connectionstatus";

        DatabaseLoader loader = mlContext.Data.CreateDatabaseLoader<ConnectionStatus>();
        DatabaseSource source = new DatabaseSource(SqlClientFactory.Instance, connectionStr, query);
        IDataView dataView = loader.Load(source);
        return source.ProviderFactory.CreateDataAdapter();
    }
}`

ropiela avatar Dec 08 '20 13:12 ropiela

Solution for me was to cast to real (maps to single) instead of float (maps to double).

https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-data-type-mappings

Sire avatar Feb 01 '21 12:02 Sire