从零开始实现逻辑回归模型

逻辑回归

声明:版权所有,转载请联系作者并注明出处:
http://blog.csdn.net/u013719780?viewmode=contents

知乎专栏:
https://www.zhihu.com/people/feng-xue-ye-gui-zi/columns

上一篇文章介绍了线性回归、岭回归、lasso回归和多项式回归模型。这些模型都是广义线性回归模型的具体形式,广义线性回归是一种灵活的框架,比普通线性回归要求更少的假设。这一章,我们讨论广义线性回归模型的具体形式的另一种形式,逻辑回归(logistic regression)。

逻辑回归模型在工业界是工程师用的非常多的模型了,比如在CTR预测等项目中被大量使用,之所以用的非常普遍,是因为逻辑回归拥有简单、解释性好、计算速度快等优点。

和前面讨论的模型不同,逻辑回归是用来做分类任务的。分类任务的目标是找一个函数,把观测值匹配到相关的类和标签上。学习算法必须用成对的特征向量和对应的标签来估计匹配函数的参数,从而实现更好的分类效果。在二元分类(binary classification)中,分类算法必须把一个实例分为两个类别。二元分类案例包括,预测患者是否患有某种疾病,音频中是否含有人声,杜克大学男子篮球队在NCAA比赛中第一场的输赢。

普通的线性回归假设响应变量呈正态分布,也称为高斯分布(Gaussian distribution )或钟形曲线(bell curve)。正态分布数据是对称的,且均值,中位数和众数(mode)是一样的。很多自然现象都服从正态分布。比如,人类的身高就服从正态分布,姚明那样的高度极少,在99%之外了。

在某些问题里,响应变量不是正态分布的。比如,掷一个硬币获取正反两面的概率分布是伯努力分布(Bernoulli distribution),又称两点分布或者0-1分布。表示一个事件发生的概率是P,不发生的概率1−P,概率在[0, 1]之间。线性回归假设自变量(解释变量)值的变化会引起因变量(响应变量)值的变化,如果响应变量的值是概率,这条假设就不满足了。广义线性回归去掉了这条假设,用一个联连函数(link function)来描述解释变量与响应变量的关系。实际上,在线性回归模型里面,我们已经用了联连函数。普通线性回归作为广义线性回归的特例使用的是恒等联连函数(identity link function),将解释变量的通过线性组合的方式来联接服从正态分布的响应变量。如果响应变量不服从正态分布,就要用另外一种联连函数了。

在逻辑回归里,响应变量描述了类似于掷一个硬币结果为正面的概率。如果响应变量等于或超过了指定的临界值,预测结果就是正面,否则预测结果就是反面。响应变量是一个像线性回归中的解释变量构成的函数表示,称为逻辑函数(logistic function)。一个值在[0, 1]之间的逻辑函数如下所示:

f(x)=11+ex

模型原理

逻辑回归是一种判别模型,与线性回归类似,它有比较强的先验假设:

  • 假设因变量服从Bernoulli distribution, 即

    p(y|x)=p(y=1|x)y(1p(y=1|x))(1y),y{0,1}

  • 假设训练样本服从钟形分布,例如高斯分布:

    p(xi|y=yk)N(μik,σi)

其中y是样本标注,布尔类型,取值为0或1,x是样本的特征向量。

逻辑回归是判别模型,所以我们直接学习 p(y|x) ,以高斯分布为例:

p(y=1|x)=11+exw,

p(y=0|x)=1p(y=1|x)=11+exw

则有:

p(y=1|x)=p(x|y=1)p(y=1)p(x)=p(x|y=1)p(y=1)p(x|y=1)p(y=1)+p(x|y=0)p(y=0)=11+p(x|y=0)p(y=0)p(x|y=1)p(y=1)=11+p(x|y=0)(1p(y=1))p(x|y=1)p(y=1)=11+p(x|y=0)(1π)p(x|y=1)π(p(y=1)=π)=11+eln1ππ+lnp(x|y=0)p(x|y=1)=11+eln1ππ+ilnp(xi|y=0)p(xi|y=1)

因为

p(xi|yk)=1σik2πe(xiμik)22σik2

所以
p(y=1|x)=11+e(lnπ1π+i(μi1μi0σ2ixi+μ2i0μ2i12σ2i))=11+eiwixi+b,i=1,2,3,,n_features

其中

b=i(lnπ1π+μ2i0μ2i12σ2i)),wi=μi1μi0σ2i

损失函数

使用最大似然估计(MLE),即

w=argmaxwinlnp(yi|xi)

上式等价于

w=agrminwi=1nyilnp(yi=1|xi)+(1yi)lnp(yi=0|xi)

逻辑回归模型的另一面

实际上,Logistic Regression可以看做是激活函数为 f(x)=11+ex 的仅含有一个神经元的单层神经网络:

这里写图片描述

from __future__ import print_function, division
import sys
import os
import math
from sklearn import datasets
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt



def shuffle_data(X, y, seed=None):
    if seed:
        np.random.seed(seed)

    idx = np.arange(X.shape[0])
    np.random.shuffle(idx)

    return X[idx], y[idx]



# 正规化数据集 X
def normalize(X, axis=-1, p=2):
    lp_norm = np.atleast_1d(np.linalg.norm(X, p, axis))
    lp_norm[lp_norm == 0] = 1
    return X / np.expand_dims(lp_norm, axis)


# 标准化数据集 X
def standardize(X):
    X_std = np.zeros(X.shape)
    mean = X.mean(axis=0)
    std = X.std(axis=0)

    # 做除法运算时请永远记住分母不能等于0的情形
    # X_std = (X - X.mean(axis=0)) / X.std(axis=0) 
    for col in range(np.shape(X)[1]):
        if std[col]:
            X_std[:, col] = (X_std[:, col] - mean[col]) / std[col]

    return X_std


# 划分数据集为训练集和测试集
def train_test_split(X, y, test_size=0.2, shuffle=True, seed=None):
    if shuffle:
        X, y = shuffle_data(X, y, seed)

    n_train_samples = int(X.shape[0] * (1-test_size))
    x_train, x_test = X[:n_train_samples], X[n_train_samples:]
    y_train, y_test = y[:n_train_samples], y[n_train_samples:]

    return x_train, x_test, y_train, y_test


# 将一个向量转换成对角阵,其中对角阵上的元素就是向量中元素
def vec2diagonal(vec):
    vec_length = len(vec)
    diagonal = np.zeros((vec_length, vec_length))
    for i in range(vec_length):
        diagonal[i][i] = vec[i]
    return diagonal


def accuracy(y, y_pred):
    y = y.reshape(y.shape[0], -1)
    y_pred = y_pred.reshape(y_pred.shape[0], -1)
    return np.sum(y == y_pred)/len(y)


class Sigmoid:

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

    def derivative(self, x):
        return self.function(x) * (1 - self.function(x))


class LogisticRegression():
    """逻辑回归分类模型. 
    Parameters:
    -----------
    learning_rate: float
        学习率.
    """
    def __init__(self, learning_rate=.1):
        self.w = None
        self.learning_rate = learning_rate
        self.sigmoid = Sigmoid()


    def fit(self, X, y, n_iterations=4000):
        # 在第一列添加偏置列,全部初始化为1
        X = np.insert(X, 0, 1, axis=1)
        X = X.reshape(X.shape[0], -1)
        y = y.reshape(y.shape[0], -1)

        n_samples, n_features = np.shape(X)

        # 参数初始化 [-1/n_features, 1/n_features]
        limit = 1 / math.sqrt(n_features)
        self.w = np.random.uniform(-limit, limit, (n_features, 1))


        for i in range(n_iterations):
            # 通过初始化的参数w计算预测值
            y_pred = self.sigmoid.function(X.dot(self.w))
            # 梯度下降更新参数w.
            self.w -= self.learning_rate * X.T.dot(-(y - y_pred) * 
                                                   self.sigmoid.function(X.dot(self.w)) * 
                                                   (1 - self.sigmoid.function(X.dot(self.w))))

    def predict(self, X):
        # 训练模型的时候我们添加了偏置,预测的时候也需要添加偏置
        X = X.reshape(X.shape[0], -1)
        X = np.insert(X, 0, 1, axis=1)
        # 预测
        y_pred = np.round(self.sigmoid.function(X.dot(self.w))).astype(int)
        return y_pred


def main():
    # Load dataset
    data = datasets.load_iris()
    X = normalize(data.data[data.target != 0])
    y = data.target[data.target != 0]
    y[y == 1] = 0
    y[y == 2] = 1

    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, seed=1)

    clf = LogisticRegression()
    clf.fit(X_train, y_train)
    y_pred = clf.predict(X_test)

    accu = accuracy(y_test, y_pred)
    print ("Accuracy:", accu)

    plt.figure(figsize=(12, 8))
    plt.scatter(X[y==0][:,0], X[y==0][:,1])
    plt.scatter(X[y==1][:,0], X[y==1][:,1])

    plt.show()


if __name__ == "__main__":
    main()

Accuracy: 0.939393939394
这里写图片描述

参考书籍:
李航《统计学习方法》
周志华《机器学习》
张磊《机器学习与人工智能》

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要从零开始实现逻辑回归,你需要以下步骤: 1. 准备数据集:将数据集划分为特征矩阵 X 和目标变量向量 y。 2. 初始化参数:初始化权重向量 w 和偏置 b。 3. 定义sigmoid函数:sigmoid函数将实数映射到0和1之间的概率值。 4. 定义损失函数:使用对数似然损失函数来度量预测概率与实际标签之间的差异。 5. 计算梯度:通过计算损失函数对参数 w 和 b 的偏导数来得到梯度。 6. 更新参数:使用梯度下降法来更新参数,减小损失函数的值。 7. 训练模型:使用训练集进行多次迭代,不断更新参数以优化模型。 8. 预测:使用训练好的参数进行预测。 下面是一个简单的示例代码来实现逻辑回归: ```python import numpy as np # 定义sigmoid函数 def sigmoid(x): return 1 / (1 + np.exp(-x)) # 定义损失函数 def loss_function(y_pred, y_true): return -np.mean(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred)) # 初始化参数 def initialize_parameters(dim): w = np.zeros((dim, 1)) b = 0 return w, b # 计算梯度 def compute_gradient(X, y, y_pred): m = X.shape[1] dw = np.dot(X, (y_pred - y).T) / m db = np.mean(y_pred - y) return dw, db # 更新参数 def update_parameters(w, b, dw, db, learning_rate): w = w - learning_rate * dw b = b - learning_rate * db return w, b # 训练模型 def train(X, y, num_iterations, learning_rate): w, b = initialize_parameters(X.shape[0]) for i in range(num_iterations): # 前向传播 y_pred = sigmoid(np.dot(w.T, X) + b) # 计算损失 loss = loss_function(y_pred, y) # 计算梯度 dw, db = compute_gradient(X, y, y_pred) # 更新参数 w, b = update_parameters(w, b, dw, db, learning_rate) # 每100次迭代打印一次损失 if i % 100 == 0: print(f"Loss after iteration {i}: {loss}") return w, b # 预测 def predict(w, b, X): y_pred = sigmoid(np.dot(w.T, X) + b) y_pred = np.round(y_pred) return y_pred # 示例数据 X = np.array([[1, 2, 3], [4, 5, 6]]) y = np.array([[0, 1, 1]]) # 训练模型 w, b = train(X, y, num_iterations=1000, learning_rate=0.01) # 预测 y_pred = predict(w, b, X) print("Predictions:", y_pred) ``` 请注意,这只是一个简单的实现,实际的逻辑回归模型可能会有更多的步骤和复杂性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值