Skip to content
Permalink
Browse files

check coefficient are not out of range

  • Loading branch information...
sdpython committed Dec 4, 2017
1 parent 29076e6 commit a07d71e0cc7f7da51469680ad2f383ca2704f1a4
@@ -4,9 +4,7 @@
import sys
import os
import numpy
import warnings
import unittest
from distutils.errors import DistutilsPlatformError


try:
@@ -105,22 +103,8 @@ def test_sklearn_train_lr_into_c(self):
if code_c is None:
raise ValueError("cannot be None")

# spl = code_c.split("// adot - children")
# TODO: Used twice, an intermediate result is computed twice. Needs cache mechanism.
# if len(spl) > 2:
# raise Exception(code_c)
X = numpy.array([[numpy.float32(1), numpy.float32(2)]])

if sys.version_info[0] == 2:
try:
fct = compile_c_function(code_c, 2)
except DistutilsPlatformError as e:
if "Unable to find vcvarsall.bat" in str(e):
warnings.warn(
"C++ with Python 2.7 requires VS2010 which is not installed.")
return
else:
fct = compile_c_function(code_c, 2)
fct = compile_c_function(code_c, 2)

e2 = fct(X[0, :])
e1 = lr.predict(X)
@@ -0,0 +1,68 @@
"""
@brief test log(time=2s)
"""
import sys
import os
import numpy
import unittest


try:
import src
except ImportError:
path = os.path.normpath(
os.path.abspath(
os.path.join(
os.path.split(__file__)[0],
"..",
"..")))
if path not in sys.path:
sys.path.append(path)
import src

try:
import pyquickhelper as skip_
except ImportError:
path = os.path.normpath(
os.path.abspath(
os.path.join(
os.path.split(__file__)[0],
"..",
"..", "..", "pyquickhelper", "src")))
if path not in sys.path:
sys.path.append(path)
import pyquickhelper as skip_


from pyquickhelper.loghelper import fLOG
from pyquickhelper.pycode import ExtTestCase
from src.mlprodict.grammar_sklearn import sklearn2graph
from src.mlprodict.grammar.exc import Float32InfError


class TestGrammarSklearnLinearBugFloat(ExtTestCase):

def test_sklearn_train_lr_into_c(self):
fLOG(
__file__,
self._testMethodName,
OutputPrint=__name__ == "__main__")
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris
import cffi
fLOG("cffi", cffi.__version__)
iris = load_iris()
X = iris.data[:, :2]
y = iris.target
y[y == 2] = 1
lr = LogisticRegression()
lr.fit(X, y)

# We replace by double too big for floats.
lr.coef_ = numpy.array([[2.45, -3e250]])
self.assertRaise(lambda: sklearn2graph(
lr, output_names=['Prediction', 'Score']), Float32InfError)


if __name__ == "__main__":
unittest.main()
@@ -46,7 +46,8 @@ def test_sklearn_scaler(self):
self._testMethodName,
OutputPrint=__name__ == "__main__")
from sklearn.preprocessing import StandardScaler
data = numpy.array([[0, 0], [0, 0], [1, 1], [1, 1]])
data = numpy.array([[0, 0], [0, 0], [1, 1], [1, 1]],
dtype=numpy.float32)
check_model_representation(
StandardScaler, data, verbose=False, fLOG=fLOG)
# The second compilation fails if suffix is not specified.
@@ -5,7 +5,6 @@
import os
import sys
import numpy
import subprocess
import shutil


@@ -94,6 +93,9 @@ def compile_c_function(code_c, nbout, dtype=numpy.float32, add_header=True,
@return compiled function
The function assumes the first line is the signature.
If you are using Windows with Visual Studio 2017, make sure
you are using :epkg:`Python` 3.6.3+
(see `Issue 30389 <https://bugs.python.org/issue30389>`_).
"""
if sys.platform.startswith("win"):
if "VS140COMNTOOLS" not in os.environ:
@@ -107,34 +109,34 @@ def compile_c_function(code_c, nbout, dtype=numpy.float32, add_header=True,
if additional_paths is None:
additional_paths = []

if len(additional_paths) == 0 and sys.platform.startswith("win") and \
'VSSDK140Install' not in os.environ: # last condition is for the installed VisualStudio.
if fLOG:
fLOG("[compile_c_function] fix PATH for VS2017 on Windows")
# Update environment variables.
adds = [r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64",
r"C:\Program Files (x86)\Windows Kits\10\bin\10.0.15063.0\x64"]
vcvars64 = os.path.join(adds[0], 'vcvars64.bat')
subprocess.run(vcvars64)

# Add paths for VS2017.
includes = [r'C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\shared',
r'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include',
r'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\SDK\ScopeCppSDK\SDK\include\ucrt']
libs = [r'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\lib\amd64',
r'C:\Program Files (x86)\Windows Kits\10\Lib\10.0.15063.0\um\x64',
r'C:\Program Files (x86)\Windows Kits\10\Lib\10.0.15063.0\ucrt\x64']
opaths = os.environ['PATH'].split(';')
for add in adds:
if os.path.exists(add) and add not in opaths:
additional_paths.append(add)
oinc = os.environ.get('INCLUDE', '').split(';')
for inc in includes:
if os.path.exists(inc) and inc not in oinc:
include_paths.append(inc)
for lib in libs:
if os.path.exists(lib):
lib_paths.append(lib)
#~ if len(additional_paths) == 0 and sys.platform.startswith("win") and \
#~ 'VSSDK140Install' not in os.environ: # last condition is for the installed VisualStudio.
#~ if fLOG:
#~ fLOG("[compile_c_function] fix PATH for VS2017 on Windows")
#~ # Update environment variables.
#~ adds = [r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64",
#~ r"C:\Program Files (x86)\Windows Kits\10\bin\10.0.15063.0\x64"]
#~ vcvars64 = os.path.join(adds[0], 'vcvars64.bat')
#~ subprocess.run(vcvars64)

#~ # Add paths for VS2017.
#~ includes = [r'C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\shared',
#~ r'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include',
#~ r'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\SDK\ScopeCppSDK\SDK\include\ucrt']
#~ libs = [r'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\lib\amd64',
#~ r'C:\Program Files (x86)\Windows Kits\10\Lib\10.0.15063.0\um\x64',
#~ r'C:\Program Files (x86)\Windows Kits\10\Lib\10.0.15063.0\ucrt\x64']
#~ opaths = os.environ['PATH'].split(';')
#~ for add in adds:
#~ if os.path.exists(add) and add not in opaths:
#~ additional_paths.append(add)
#~ oinc = os.environ.get('INCLUDE', '').split(';')
#~ for inc in includes:
#~ if os.path.exists(inc) and inc not in oinc:
#~ include_paths.append(inc)
#~ for lib in libs:
#~ if os.path.exists(lib):
#~ lib_paths.append(lib)

if additional_paths:
if fLOG:
@@ -0,0 +1,12 @@
"""
@file
@brief Exception definition.
"""


class Float32InfError(Exception):
"""
Raised when a float is out of range and cannot be
converted into a float32.
"""
pass
@@ -4,7 +4,7 @@
"""
import numpy
from .api_extension import AutoAction
from .gtypes import MLType, MLNumTypeR4, MLTensor, MLNumTypeI4, MLNumTypeBool
from .gtypes import MLType, MLNumTypeFloat32, MLTensor, MLNumTypeInt32, MLNumTypeBool


class MLAction(AutoAction):
@@ -128,9 +128,9 @@ def guess_type(value):
Guesses a type given a value.
"""
if isinstance(value, (float, numpy.float32)):
return MLNumTypeR4()
return MLNumTypeFloat32()
elif isinstance(value, (int, numpy.int32)):
return MLNumTypeI4()
return MLNumTypeInt32()
elif isinstance(value, numpy.ndarray):
a = numpy.zeros(1, value.dtype)
t = MLActionCst.guess_type(a[0])
@@ -311,10 +311,12 @@ def __init__(self, act1, act2):
raise TypeError("act1 must be MLAction.")
if not isinstance(act2, MLAction):
raise TypeError("act2 must be MLAction.")
n1 = 1 if isinstance(act1.output, MLNumTypeR4) else act1.output.dim[0]
n2 = 1 if isinstance(act2.output, MLNumTypeR4) else act2.output.dim[0]
n1 = 1 if isinstance(
act1.output, MLNumTypeFloat32) else act1.output.dim[0]
n2 = 1 if isinstance(
act2.output, MLNumTypeFloat32) else act2.output.dim[0]
MLActionFunctionCall.__init__(self, "concat", MLTensor(
MLNumTypeR4(), (n1 + n2,)), act1, act2)
MLNumTypeFloat32(), (n1 + n2,)), act1, act2)

def execute(self, **kwargs):
"""
@@ -2,7 +2,7 @@
@file
@brief Action definition.
"""
from .gtypes import MLNumTypeR4, MLNumTypeBool
from .gtypes import MLNumTypeFloat32, MLNumTypeBool
from .gactions import MLActionBinary, MLActionFunctionCall


@@ -30,9 +30,9 @@ class MLActionSign(MLActionFunctionCall):

def __init__(self, act1):
MLActionFunctionCall.__init__(self, "sign", act1.output, act1)
if not isinstance(act1.output, MLNumTypeR4):
if not isinstance(act1.output, MLNumTypeFloat32):
raise TypeError(
"The input action must produce R4 not '{0}'".format(type(act1.output)))
"The input action must produce float32 not '{0}'".format(type(act1.output)))

def execute(self, **kwargs):
MLActionFunctionCall.execute(self, **kwargs)
@@ -45,7 +45,7 @@ def _copy_c(self, src, dst, hook=None):

class MLNumTypeSingle(MLNumType):
"""
I4 or R4
int32 or float32
"""

def __init__(self, numpy_type, name, ctype, key):
@@ -108,7 +108,7 @@ def _byref_c(self):
return "&"

def _export_json(self, hook=None, result_name=None):
return 'R4'
return 'float32'

def _export_c(self, hook=None, result_name=None):
if hook == 'typeref':
@@ -126,22 +126,23 @@ def _format_value_c(self, value, hook=None):
return hook[self.key](value)


class MLNumTypeR4(MLNumTypeSingle):
class MLNumTypeFloat32(MLNumTypeSingle):
"""
A numpy.float32.
"""

def __init__(self):
MLNumTypeSingle.__init__(self, numpy.float32, 'R4', 'float', 'float32')
MLNumTypeSingle.__init__(
self, numpy.float32, 'float32', 'float', 'float32')


class MLNumTypeI4(MLNumTypeSingle):
class MLNumTypeInt32(MLNumTypeSingle):
"""
A numpy.int32.
"""

def __init__(self):
MLNumTypeSingle.__init__(self, numpy.int32, 'I4', 'int', 'int32')
MLNumTypeSingle.__init__(self, numpy.int32, 'int32', 'int', 'int32')


class MLNumTypeBool(MLNumTypeSingle):
@@ -187,6 +188,13 @@ def validate(self, value):
if self.dim != value.shape:
raise ValueError(
"Dimensions do not match {0}={1}".format(self.dim, value.shape))
rvalue = value.ravel()
for i, num in enumerate(rvalue):
try:
self.element_type.validate(num)
except TypeError as e:
raise TypeError(
'Unable to convert an array due to value index {0}: {1}'.format(i, rvalue[i])) from e
return value

def _byref_c(self):
@@ -5,6 +5,7 @@
"""
import numpy
from .g_sklearn_type_helpers import check_type
from ..grammar.exc import Float32InfError
from ..grammar.gactions import MLActionCst, MLActionVar, MLActionConcat, MLActionReturn
from ..grammar.gactions_num import MLActionAdd, MLActionSign
from ..grammar.gactions_tensor import MLActionTensorDot
@@ -41,9 +42,18 @@ def sklearn_logistic_regression(model, input_names=None, output_names=None, **kw
if len(model.coef_.shape) > 1 and min(model.coef_.shape) != 1:
raise NotImplementedError(
"Multiclass is not implemented yet: coef_.shape={0}.".format(model.coef_.shape))
coef = model.coef_.ravel()
coef_ = model.coef_.ravel()
coef = coef_.astype(numpy.float32)
bias = numpy.float32(model.intercept_[0])

for i, c in enumerate(coef):
if numpy.isinf(c):
raise Float32InfError(
'Unable to convert coefficient {0}: {1}'.format(i, coef[i]))
if numpy.isinf(bias):
raise Float32InfError(
'Unable to convert intercept {0}'.format(i, model.intercept_[0]))

gr_coef = MLActionCst(coef)
gr_var = MLActionVar(coef, input_names)
gr_bias = MLActionCst(bias)
@@ -85,9 +95,19 @@ def sklearn_linear_regression(model, input_names=None, output_names=None, **kwar
if len(model.coef_.shape) > 1 and min(model.coef_.shape) != 1:
raise NotImplementedError(
"MultiOutput is not implemented yet: coef_.shape={0}.".format(model.coef_.shape))
coef = model.coef_.ravel()

coef_ = model.coef_.ravel()
coef = coef_.astype(numpy.float32)
bias = numpy.float32(model.intercept_)

for i, c in enumerate(coef):
if numpy.isinf(c):
raise Float32InfError(
'Unable to convert coefficient {0}: {1}'.format(i, coef[i]))
if numpy.isinf(bias):
raise Float32InfError(
'Unable to convert intercept {0}'.format(i, model.intercept_))

gr_coef = MLActionCst(coef)
gr_var = MLActionVar(coef, input_names)
gr_bias = MLActionCst(bias)

0 comments on commit a07d71e

Please sign in to comment.
You can’t perform that action at this time.