89 lines
2.5 KiB
Python
89 lines
2.5 KiB
Python
|
import numpy as np
|
||
|
import pytest
|
||
|
|
||
|
from sklearn.datasets import load_iris
|
||
|
from sklearn.linear_model import Perceptron
|
||
|
from sklearn.utils import check_random_state
|
||
|
from sklearn.utils._testing import assert_allclose, assert_array_almost_equal
|
||
|
from sklearn.utils.fixes import CSR_CONTAINERS
|
||
|
|
||
|
iris = load_iris()
|
||
|
random_state = check_random_state(12)
|
||
|
indices = np.arange(iris.data.shape[0])
|
||
|
random_state.shuffle(indices)
|
||
|
X = iris.data[indices]
|
||
|
y = iris.target[indices]
|
||
|
|
||
|
|
||
|
class MyPerceptron:
|
||
|
def __init__(self, n_iter=1):
|
||
|
self.n_iter = n_iter
|
||
|
|
||
|
def fit(self, X, y):
|
||
|
n_samples, n_features = X.shape
|
||
|
self.w = np.zeros(n_features, dtype=np.float64)
|
||
|
self.b = 0.0
|
||
|
|
||
|
for t in range(self.n_iter):
|
||
|
for i in range(n_samples):
|
||
|
if self.predict(X[i])[0] != y[i]:
|
||
|
self.w += y[i] * X[i]
|
||
|
self.b += y[i]
|
||
|
|
||
|
def project(self, X):
|
||
|
return np.dot(X, self.w) + self.b
|
||
|
|
||
|
def predict(self, X):
|
||
|
X = np.atleast_2d(X)
|
||
|
return np.sign(self.project(X))
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize("container", CSR_CONTAINERS + [np.array])
|
||
|
def test_perceptron_accuracy(container):
|
||
|
data = container(X)
|
||
|
clf = Perceptron(max_iter=100, tol=None, shuffle=False)
|
||
|
clf.fit(data, y)
|
||
|
score = clf.score(data, y)
|
||
|
assert score > 0.7
|
||
|
|
||
|
|
||
|
def test_perceptron_correctness():
|
||
|
y_bin = y.copy()
|
||
|
y_bin[y != 1] = -1
|
||
|
|
||
|
clf1 = MyPerceptron(n_iter=2)
|
||
|
clf1.fit(X, y_bin)
|
||
|
|
||
|
clf2 = Perceptron(max_iter=2, shuffle=False, tol=None)
|
||
|
clf2.fit(X, y_bin)
|
||
|
|
||
|
assert_array_almost_equal(clf1.w, clf2.coef_.ravel())
|
||
|
|
||
|
|
||
|
def test_undefined_methods():
|
||
|
clf = Perceptron(max_iter=100)
|
||
|
for meth in ("predict_proba", "predict_log_proba"):
|
||
|
with pytest.raises(AttributeError):
|
||
|
getattr(clf, meth)
|
||
|
|
||
|
|
||
|
def test_perceptron_l1_ratio():
|
||
|
"""Check that `l1_ratio` has an impact when `penalty='elasticnet'`"""
|
||
|
clf1 = Perceptron(l1_ratio=0, penalty="elasticnet")
|
||
|
clf1.fit(X, y)
|
||
|
|
||
|
clf2 = Perceptron(l1_ratio=0.15, penalty="elasticnet")
|
||
|
clf2.fit(X, y)
|
||
|
|
||
|
assert clf1.score(X, y) != clf2.score(X, y)
|
||
|
|
||
|
# check that the bounds of elastic net which should correspond to an l1 or
|
||
|
# l2 penalty depending of `l1_ratio` value.
|
||
|
clf_l1 = Perceptron(penalty="l1").fit(X, y)
|
||
|
clf_elasticnet = Perceptron(l1_ratio=1, penalty="elasticnet").fit(X, y)
|
||
|
assert_allclose(clf_l1.coef_, clf_elasticnet.coef_)
|
||
|
|
||
|
clf_l2 = Perceptron(penalty="l2").fit(X, y)
|
||
|
clf_elasticnet = Perceptron(l1_ratio=0, penalty="elasticnet").fit(X, y)
|
||
|
assert_allclose(clf_l2.coef_, clf_elasticnet.coef_)
|