理论讲解
对于二分类问题,输出标记为 ,0表示负向类,1表示正向类。logistic regression不是一个回归问题,而是一个分类问题。需要通过一个函数将线性回归模型 的输出值映射到 范围内,这个函数就是对数几率函数(logistic function),也称为sigmoid函数:
对某个样本 ,预测概率为
对于线性回归问题,定义的函数为所有模型误差的平方和。但是对于逻辑回归来说,这样定义所得的代价函数为非凸函数,容易陷入局部最优。代价函数为:
如图所示。若样本正确被划分为类别1中,代价将趋向于0;若样本正确被划分为类别0中,代价将趋向于0;反正将趋向于无穷大
代价函数的推导:
在构建模型时,定义最大似然函数 ,最大似然函数的入门参照: https://blog.csdn.net/winycg/article/details/80294225,公式如下:
将其取对数似然方程:
样本的最大似然函数值 越大,表明预测越准确。在似然函数值非常小的时候,使用对数函数可以避免数值溢出;其次可以将各因子的连乘转换为和的形式,便于求导。将 取反,就可以得到误差函数
梯度下降更新公式推导:
首先计算sigmoid函数的偏导:
计算对数似然函数对第 个权重的偏导:
多类别分类
现在我们有一个训练集, 好比上图表示的有三个类别, 我们用三角形表示 y=1, 方框表示 y=2, 叉叉表示 y=3。 我们下面要做的就是使用一个训练集, 将其分成三个二元分类问题。
先从用三角形代表的类别 1 开始, 实际上我们可以创建一个, 新的”伪”训练集, 类型 2 和类型 3 定为负类, 类型 1 设定为正类, 我们创建一个新的训练集, 如下图所示的那样, 我们要拟合出一个合适的分类器。
这里的三角形是正样本, 而圆形代表负样本。 可以这样想, 设置三角形的值为 1, 圆形的值为 0, 下面我们来训练一个标准的逻辑回归分类器, 这样我们就得到一个正边界。
为了实现这样的转变,将多个类中的一个类标记为正向类(
),然后将其他所有类都标记为负向类,此模型记为
。类似地第我们选择另一个类标记为正向类
,再将其它类都标记为负向类,将这个模型记作
,依次类推
最后得到的一系列模型记为:
需要做预测时,我们将所有的分类机都运行一遍,然后对每一个输入变量,都选择最高可能性的输出变量。即让3个分类器都输入 ,选择一个使得 最大的 ,即
利用scikit-learn实现逻辑回归
在模型中,使用了L2正则化防止过拟合并减少模型复杂度,即误差函数为:
在多类别分类数据集上使用Logistic Regression对象,默认使用一对多(One-vs-Rest, OvR)的方法。一共有3个截距项,其中第一个截距项为类别0相对于类别1,2的匹配结果,第二个截距项为类别1相对于类别0,2的匹配结果,第三个截距项为类别2相对于类别0,1的匹配结果。
权重数组包含三个权重系数向量,每一个权重向量对应一个分类。由于样本包含2个特征值,所以每个向量包括2个权重值,通过与数据集中的特征数据相乘来计算模型的净输入:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
# test_idx为测试数据编号
def plot_decision_regions(X, y, classifier, test_idx=None):
# setup marker generator and color map
markers = ('s', 'x', 'o', '^', 'v')
colors = ('red', 'blue', 'lightgreen', 'grey', 'cyan')
cmap = ListedColormap(colors[:len(np.unique(y))])
# plot the decision region
x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, 0.02), np.arange(x2_min, x2_max, 0.02))
z = classifier.predict(np.array([xx1.flatten(), xx2.flatten()]).T)
z = z.reshape(xx1.shape)
plt.contourf(xx1, xx2, z, cmap=cmap, alpha=0.3)
plt.xlim(x1_min, x1_max)
plt.ylim(x2_min, x2_max)
# plot class samples
for idx, cl in enumerate(np.unique(y)):
plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1], cmap=cmap(idx), marker=markers[idx], label=cl, alpha=1)
if test_idx:
X_test, y_test = X[test_idx, :], y[test_idx]
plt.scatter(X_test[:, 0], X_test[:, 1], c='cyan', alpha=1.0,
linewidth=1, marker='^', s=55, label='test set')
plt.legend(loc='best')
plt.title('Adaline-梯度下降法', fontproperties='SimHei')
plt.xlabel('经标准化处理的萼片宽度', fontproperties='SimHei')
plt.ylabel('经标准化处理的花瓣宽度', fontproperties='SimHei')
iris = datasets.load_iris()
X = iris.data[:, [2, 3]] # 维度:(150,2)
y = iris.target
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=0)
sc = StandardScaler()
sc.fit(X_train) # 计算训练数据的均值和标准差
X_train_std = sc.transform(X_train)
X_test_std = sc.transform(X_test)
X_combined_std = np.vstack((X_train_std, X_test_std))
y_combined = np.hstack((y_train, y_test))
LR = LogisticRegression(C=1000.0, random_state=0)
LR.fit(X_train_std, y_train)
plot_decision_regions(X=X_combined_std,
y=y_combined,
classifier=LR,
test_idx=range(105, 150))
print(LR.predict_proba(X_test_std[0:2, :]))
''' 展示2个样本对于3个类别的准确率: [[2.05743774e-11 6.31620264e-02 9.36837974e-01] [6.08753106e-04 9.99285569e-01 1.05678028e-04]] '''
print(LR.coef_)
''' 权重向量: [[-7.34015187 -6.64685581] [ 2.54373335 -2.3421979 ] [ 9.46617627 6.44380858]] '''
print(LR.intercept_)
''' 截距项: [-9.31757401 -0.89462847 -8.85765974] '''
print('Test Accuracy:', LR.score(X_test_std, y_test))
# Test Accuracy: 0.9777777777777777
plt.show()