菜鸟一枚,有错误欢迎指正,但不要喷。

在线性回归中,要求解的模型是
python画图 对数_反例
实际上线性回归模型在稍加修改后也可以求解其他问题,例如考虑模型
python画图 对数_反例_02
两边同时取对数后有
python画图 对数_线性回归_03
在形式上仍然是线性回归,但实际上已经是输入空间到输出空间的非线性映射。更一般的,考虑单调可微函数python画图 对数_反例_04,令
python画图 对数_机器学习_05
这样得到的模型称为广义线性模型,其中函数称为python画图 对数_反例_04“联系函数”。

接下来就可以利用广义线性模型来解决分类问题,也就是对数几率回归。

在二分类任务中,输出标记python画图 对数_python画图 对数_07,而线性回归模型python画图 对数_正例_08产生的预测值是实值,于是需要将实值python画图 对数_正例_09转换为0/1值,最理想的函数是单位越阶函数(亦称Heaviside函数)

python画图 对数_反例_10

即若预测值大于0就判为正例,小于0则判为反例,为0则任意判别。而单位越阶函数不连续,因此不能用作python画图 对数_正例_11,因此便有了替代函数

python画图 对数_反例_12

这是一个Sigmoid函数,即形似S型的函数。

python画图 对数_机器学习_13


Sigmoid函数能够将z值转换为0和1之间的y值,其输出值在z=0附近变化很陡,能够很好的近似单位越阶函数。

将Sigmoid函数作为python画图 对数_正例_11代入式(4)得到
python画图 对数_机器学习_15
python画图 对数_反例_16则预测为正例,反之预测为反例。上式可以变化为
python画图 对数_线性回归_17
若将python画图 对数_正例_18看作样本python画图 对数_正例_19作为正例的可能性,则python画图 对数_线性回归_20是其反例的可能性。两者的比值
python画图 对数_正例_21
称为“几率",反应了样本python画图 对数_正例_19作为正例的相对可能性,取对数则得到"对数几率"
python画图 对数_python画图 对数_23
这就是对数几率回归名称的由来。对数几率回归在得到分类结果的同时还能得到近似概率预测。

接下来介绍如何确定python画图 对数_线性回归_24python画图 对数_正例_25。如果将式(8)中的python画图 对数_正例_18看作类后验概率python画图 对数_线性回归_27,式(8)可重写为
python画图 对数_反例_28
显然有
python画图 对数_正例_29

python画图 对数_正例_30

于是可以通过极大似然估计来估计python画图 对数_线性回归_24python画图 对数_正例_25,给定数据集python画图 对数_线性回归_33,对率回归模型最大化对数似然
python画图 对数_python画图 对数_34
为方便书写,令python画图 对数_线性回归_35python画图 对数_反例_36,则python画图 对数_python画图 对数_37可简写为python画图 对数_正例_38,再令python画图 对数_机器学习_39python画图 对数_机器学习_40,接下来对似然函数进行化简,python画图 对数_python画图 对数_41可以写作:
python画图 对数_python画图 对数_42
取对数
python画图 对数_反例_43
取指数
python画图 对数_机器学习_44
代入式(14)得
python画图 对数_正例_45
于是最大化的目标为
python画图 对数_线性回归_46
等价于最小化
python画图 对数_python画图 对数_47
这是关于python画图 对数_正例_48的高阶可导凸函数,使用梯度下降法即可求解,仿照scikit-learn的实现模式,代码如下:

from sklearn.model_selection import train_test_split
from sklearn import datasets
from sklearn import preprocessing
import numpy as np


# 对数几率回归
class LogitRegression:
    def __init__(self):
        self._beta = None
        self.coef_ = None
        self.interception_ = None

    def _sigmoid(self, x):
        return 1. / (1. + np.exp(-x))

    def fit(self, X, y, eta=0.01, n_iters=1e4, eps=1e-8):
        assert(X.shape[0] == y.shape[0])

        def L(beta, X_b, y):
            try:
                vec = beta.dot(X_b.T)
                return np.sum(-vec * y + np.log(1 + np.exp(vec))) / len(X_b)
            except Exception:
                return float('inf')

        def dL(beta, X_b, y):
            try:
                vec = beta.dot(X_b.T)
                coef =  np.exp(vec) / (1 + np.exp(vec))
                return (-y.dot(X_b) + X_b.T.dot(coef)) / len(X_b)
            except Exception:
                return float('inf')

        def gradient_decent(X_b, y, initial_beta, eta, n_iters, eps):
            i_iters = 0
            beta = initial_beta
            while i_iters < n_iters:
                gradient = dL(beta, X_b, y)
                last_beta = beta
                beta = beta - eta * gradient
                if (abs(L(beta, X_b, y) - L(last_beta, X_b, y)) < eps):
                    break
                i_iters += 1
            return beta

        X_b = np.hstack([np.ones((len(X), 1)), X])
        initial_beta = np.zeros(X_b.shape[1])
        self._beta = gradient_decent(X_b, y, initial_beta, eta, n_iters, eps)
        self.coef_ = self._beta[1:]
        self.interception_ = self._beta[0]
        return self

    def fit_sgd(self, X, y, n_iters=5, t0=5, t1=50):
        assert X.shape[0] == y.shape[0]
        assert n_iters >= 1

        def dL_sgd(beta, X_b_i, y_i):
            coef = np.exp(beta.dot(X_b_i)) / (1 + np.exp(beta.dot(X_b_i)))
            return (-y_i + coef) * X_b_i

        def gradient_decent_sgd(X_b, y, initial_beta, n_iters, t0, t1):

            def learning_rate(t):
                return t0 / (t1 + t)

            beta = initial_beta
            m = len(X_b)
            for i in range(n_iters):
                indexes = np.random.permutation(m)
                X_b_new = X_b[indexes]
                y_new = y[indexes]
                for j in range(m):
                    gradient = dL_sgd(beta, X_b_new[j], y_new[j])
                    beta = beta - learning_rate(i * m + j) * gradient
            return beta

        X_b = np.hstack([np.ones((len(X), 1)), X])
        initial_beta = np.zeros(X_b.shape[1])
        self._beta = gradient_decent_sgd(X_b, y, initial_beta, n_iters, t0, t1)
        self.coef_ = self._beta[1:]
        self.interception_ = self._beta[0]
        return self

    def predict_probability(self, X_predict):
        X_b = np.hstack([np.ones((len(X_predict), 1)), X_predict])
        return self._sigmoid(X_b.dot(self._beta))

    def predict(self, X_predict):
        assert self.coef_ is not None and self.interception_ is not None
        assert X_predict.shape[1] == len(self.coef_)
        probability = self.predict_probability(X_predict)
        return np.array(probability >= 0.5, dtype='int')

    def score(self, X_test, y_test):
        y_predict = self.predict(X_test)
        return np.sum(y_predict == y_test) / len(y_test)

    def __repr__(self):
        return "LogitRegression()"


if __name__ == "__main__":
    breast_cancer = datasets.load_breast_cancer()
	y = breast_cancer.target
	X = breast_cancer.data
	train_X, test_X, train_y, test_y = train_test_split(X, y, test_size=0.3, random_state=123)

	scaler = preprocessing.StandardScaler()
	train_X = scaler.fit(train_X).transform(train_X)
	test_X = scaler.transform(test_X)

	logit = LogitRegression()
	logit.fit(train_X, train_y)
	print(logit.score(test_X, test_y))

运行结果如下所示:

python画图 对数_反例_49

扩展:多分类对数几率回归

python画图 对数_正例_18的取值集合是python画图 对数_线性回归_51,多项对数几率回归的模型是
python画图 对数_机器学习_52
多项对数几率回归的输入python画图 对数_机器学习_53,其取值为
python画图 对数_机器学习_54
此时的似然函数是
python画图 对数_python画图 对数_55
对数似然是
python画图 对数_正例_56
其中用到了条件python画图 对数_正例_57,求偏导得
python画图 对数_线性回归_58
之后可以使用梯度下降法求解。