为什么我自己的逻辑回归实现与 sklearn 不同?
why does my own implementation of logistic regression differ from sklearn?
我正尝试在 Python 中从头开始为二元分类问题实施逻辑回归。正如您在此 example 中所见,我的结果与 sklearn 实现提供的结果不匹配。请注意,这些线条看起来“相似”,但它们显然不一样。
我处理了这个 中提到的内容:sklearn 和我 (i) 都符合截距项,并且; (ii) 不应用正则化 (penalty='none')。此外,虽然 sklearn 应用 100 次迭代来训练算法(默认情况下),但我应用 10000 次,学习率相当小,为 0.01。我尝试了不同的值组合,但问题似乎并不取决于此。
与此同时,我确实注意到,即使在将结果与 sklearn 进行比较之前,我通过实施获得的结果似乎也是错误的:在某些情况下,决策区域明显偏离。您可以在 image.
中查看示例
最后一点似乎表明问题全是我自己的错。这是我的代码(它实际上在每个 运行 处生成新数据集并绘制结果):
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn.datasets import make_blobs
from sklearn.linear_model import LogisticRegression
def create_training_set():
X0, y = make_blobs(n_samples=[100, 100],
centers=None,
n_features=2,
cluster_std=1)
y = y.reshape(-1, 1) # make y a column vector
return np.hstack([np.ones((X0.shape[0], 1)), X0]), X0, y
def create_test_set(X0):
xx, yy = np.meshgrid(np.arange(X0[:, 0].min() - 1, X0[:, 0].max() + 1, 0.1),
np.arange(X0[:, 1].min() - 1, X0[:, 1].max() + 1, 0.1))
X_test = np.c_[xx.ravel(), yy.ravel()]
X_test = np.hstack([np.ones((X_test.shape[0], 1)), X_test])
return xx, yy, X_test
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def apply_gradient_descent(theta, X, y, max_iter=1000, alpha=0.1):
m = X.shape[0]
cost_iter = []
for _ in range(max_iter):
p_hat = sigmoid(np.dot(X, theta))
cost_J = -1/float(m) * (np.dot(y.T, np.log(p_hat)) + np.dot((1 - y).T, np.log(1 - p_hat)))
grad_J = 1/float(m) * np.dot(X.T, p_hat - y)
theta -= alpha * grad_J
cost_iter.append(float(cost_J))
return theta, cost_iter
fig, ax = plt.subplots(10, 2, figsize = (10, 30))
cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA', '#AAAAFF'])
cmap_bold = ListedColormap(['#FF0000', '#00FF00', '#0000FF'])
max_iter = 10000
alpha = 0.1
all_cost_history = []
for n_fil in range(10):
X_train, X0, y = create_training_set()
xx, yy, X_test = create_test_set(X0)
theta, cost_evolution = apply_gradient_descent(np.zeros((X_train.shape[1], 1)), X_train, y, max_iter, alpha)
all_cost_history.append(cost_evolution)
y_pred = np.where(sigmoid(np.dot(X_test, theta)) > 0.5, 1, 0)
y_pred = y_pred.reshape(xx.shape)
ax[n_fil, 0].pcolormesh(xx, yy, y_pred, cmap = cmap_light)
ax[n_fil, 0].scatter(X0[:, 0], X0[:, 1], c=y.ravel(), cmap=cmap_bold, alpha = 1, edgecolor="black")
y = y.reshape(X_train.shape[0], )
clf = LogisticRegression().fit(X0, y)
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
ax[n_fil, 1].pcolormesh(xx, yy, Z, cmap = cmap_light)
ax[n_fil, 1].scatter(X0[:, 0], X0[:, 1], c=y, cmap=cmap_bold, alpha = 1, edgecolor="black")
plt.show()
您的实现与 Sklearn 的实现之间实际上存在差异:您没有使用相同的优化算法(在 sklearn 中也称为求解器),我认为您观察到的差异来自这里。您正在使用梯度下降,而 sklearn 的实现默认使用“liblinear”求解器,这是不同的
事实上,不同的优化算法可以产生不同的结果,例如:
- 收敛速度:由于我们限制迭代次数,收敛速度较慢的算法会在不同的最小值处停止,从而产生不同的决策区域
- 算法是否是确定性的:非确定性算法(例如随机梯度下降)可以在给定相同数据集的情况下收敛到不同的局部最小值。使用非确定性算法,您可以使用完全相同的数据集和算法观察到不同的结果。
- 超参数:改变超参数(例如梯度下降算法的学习率)也会改变优化算法的行为,从而导致不同的结果。
在你的情况下,有充分的理由不能总是得到相同的结果:你使用的梯度下降算法可能会陷入局部最小值(因为迭代次数不足,非最佳学习率.. .) 这可能与 liblinear 求解器达到的局部最小值不同。
如果将 sklearn 的实现与不同的求解器(重用您的代码)进行比较,您可以观察到相同类型的差异:
fig, ax = plt.subplots(10, 2, figsize=(10, 30))
cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA', '#AAAAFF'])
cmap_bold = ListedColormap(['#FF0000', '#00FF00', '#0000FF'])
max_iter = 10000
alpha = 0.1
solver_algo_1 = 'liblinear'
solver_algo_2 = 'sag'
for n_fil in range(10):
X_train, X0, y = create_training_set()
xx, yy, X_test = create_test_set(X0)
y = y.reshape(X_train.shape[0], )
clf = LogisticRegression(solver=solver_algo_1, max_iter=max_iter).fit(X0, y)
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
ax[n_fil, 0].pcolormesh(xx, yy, Z, cmap=cmap_light)
ax[n_fil, 0].scatter(X0[:, 0], X0[:, 1], c=y, cmap=cmap_bold, alpha=1, edgecolor="black")
clf = LogisticRegression(solver=solver_algo_2, max_iter=max_iter).fit(X0, y)
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
ax[n_fil, 1].pcolormesh(xx, yy, Z, cmap=cmap_light)
ax[n_fil, 1].scatter(X0[:, 0], X0[:, 1], c=y, cmap=cmap_bold, alpha=1, edgecolor="black")
plt.show()
例如,使用“liblinear”(左)和“newton-cg”(右),您可以得到:
Logistc回归实现虽然相同,但优化算法不同导致结果不同。所以简而言之,您的实现与 Scikit Learn 的实现之间的区别在于优化算法。
现在如果你得到的决策边界的质量不令人满意,你可以尝试调整你的梯度下降算法的超参数或尝试改变优化算法!
我正尝试在 Python 中从头开始为二元分类问题实施逻辑回归。正如您在此 example 中所见,我的结果与 sklearn 实现提供的结果不匹配。请注意,这些线条看起来“相似”,但它们显然不一样。
我处理了这个
与此同时,我确实注意到,即使在将结果与 sklearn 进行比较之前,我通过实施获得的结果似乎也是错误的:在某些情况下,决策区域明显偏离。您可以在 image.
中查看示例最后一点似乎表明问题全是我自己的错。这是我的代码(它实际上在每个 运行 处生成新数据集并绘制结果):
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn.datasets import make_blobs
from sklearn.linear_model import LogisticRegression
def create_training_set():
X0, y = make_blobs(n_samples=[100, 100],
centers=None,
n_features=2,
cluster_std=1)
y = y.reshape(-1, 1) # make y a column vector
return np.hstack([np.ones((X0.shape[0], 1)), X0]), X0, y
def create_test_set(X0):
xx, yy = np.meshgrid(np.arange(X0[:, 0].min() - 1, X0[:, 0].max() + 1, 0.1),
np.arange(X0[:, 1].min() - 1, X0[:, 1].max() + 1, 0.1))
X_test = np.c_[xx.ravel(), yy.ravel()]
X_test = np.hstack([np.ones((X_test.shape[0], 1)), X_test])
return xx, yy, X_test
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def apply_gradient_descent(theta, X, y, max_iter=1000, alpha=0.1):
m = X.shape[0]
cost_iter = []
for _ in range(max_iter):
p_hat = sigmoid(np.dot(X, theta))
cost_J = -1/float(m) * (np.dot(y.T, np.log(p_hat)) + np.dot((1 - y).T, np.log(1 - p_hat)))
grad_J = 1/float(m) * np.dot(X.T, p_hat - y)
theta -= alpha * grad_J
cost_iter.append(float(cost_J))
return theta, cost_iter
fig, ax = plt.subplots(10, 2, figsize = (10, 30))
cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA', '#AAAAFF'])
cmap_bold = ListedColormap(['#FF0000', '#00FF00', '#0000FF'])
max_iter = 10000
alpha = 0.1
all_cost_history = []
for n_fil in range(10):
X_train, X0, y = create_training_set()
xx, yy, X_test = create_test_set(X0)
theta, cost_evolution = apply_gradient_descent(np.zeros((X_train.shape[1], 1)), X_train, y, max_iter, alpha)
all_cost_history.append(cost_evolution)
y_pred = np.where(sigmoid(np.dot(X_test, theta)) > 0.5, 1, 0)
y_pred = y_pred.reshape(xx.shape)
ax[n_fil, 0].pcolormesh(xx, yy, y_pred, cmap = cmap_light)
ax[n_fil, 0].scatter(X0[:, 0], X0[:, 1], c=y.ravel(), cmap=cmap_bold, alpha = 1, edgecolor="black")
y = y.reshape(X_train.shape[0], )
clf = LogisticRegression().fit(X0, y)
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
ax[n_fil, 1].pcolormesh(xx, yy, Z, cmap = cmap_light)
ax[n_fil, 1].scatter(X0[:, 0], X0[:, 1], c=y, cmap=cmap_bold, alpha = 1, edgecolor="black")
plt.show()
您的实现与 Sklearn 的实现之间实际上存在差异:您没有使用相同的优化算法(在 sklearn 中也称为求解器),我认为您观察到的差异来自这里。您正在使用梯度下降,而 sklearn 的实现默认使用“liblinear”求解器,这是不同的
事实上,不同的优化算法可以产生不同的结果,例如:
- 收敛速度:由于我们限制迭代次数,收敛速度较慢的算法会在不同的最小值处停止,从而产生不同的决策区域
- 算法是否是确定性的:非确定性算法(例如随机梯度下降)可以在给定相同数据集的情况下收敛到不同的局部最小值。使用非确定性算法,您可以使用完全相同的数据集和算法观察到不同的结果。
- 超参数:改变超参数(例如梯度下降算法的学习率)也会改变优化算法的行为,从而导致不同的结果。
在你的情况下,有充分的理由不能总是得到相同的结果:你使用的梯度下降算法可能会陷入局部最小值(因为迭代次数不足,非最佳学习率.. .) 这可能与 liblinear 求解器达到的局部最小值不同。
如果将 sklearn 的实现与不同的求解器(重用您的代码)进行比较,您可以观察到相同类型的差异:
fig, ax = plt.subplots(10, 2, figsize=(10, 30))
cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA', '#AAAAFF'])
cmap_bold = ListedColormap(['#FF0000', '#00FF00', '#0000FF'])
max_iter = 10000
alpha = 0.1
solver_algo_1 = 'liblinear'
solver_algo_2 = 'sag'
for n_fil in range(10):
X_train, X0, y = create_training_set()
xx, yy, X_test = create_test_set(X0)
y = y.reshape(X_train.shape[0], )
clf = LogisticRegression(solver=solver_algo_1, max_iter=max_iter).fit(X0, y)
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
ax[n_fil, 0].pcolormesh(xx, yy, Z, cmap=cmap_light)
ax[n_fil, 0].scatter(X0[:, 0], X0[:, 1], c=y, cmap=cmap_bold, alpha=1, edgecolor="black")
clf = LogisticRegression(solver=solver_algo_2, max_iter=max_iter).fit(X0, y)
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
ax[n_fil, 1].pcolormesh(xx, yy, Z, cmap=cmap_light)
ax[n_fil, 1].scatter(X0[:, 0], X0[:, 1], c=y, cmap=cmap_bold, alpha=1, edgecolor="black")
plt.show()
例如,使用“liblinear”(左)和“newton-cg”(右),您可以得到:
Logistc回归实现虽然相同,但优化算法不同导致结果不同。所以简而言之,您的实现与 Scikit Learn 的实现之间的区别在于优化算法。
现在如果你得到的决策边界的质量不令人满意,你可以尝试调整你的梯度下降算法的超参数或尝试改变优化算法!