LogisticRegression逻辑回归原理及代码实现

引言:对于学习AI来说,先入门机器学习很重要,因为深度学习中一些概念,比如sigmoid激活函数等都是从机器学习中提出的。线性回归只能解决线性问题,比如预测房价等,但不能解决离散问题,比如分类,逻辑回归能解决二分类问题。

原理及推导

1. 逻辑回归模型是从广义线性模型的来的,广义线性模型是线性模型左边加上联系函数。

如原线性回归模型公式为:                                y =\omega ^{^{T}}\cdot\mathbf{X}+bias 

如果加上ln 作为联系函数则变成                        ln y = \omega ^{T}\cdot X+bias

2. 对数几率模型

      几率:几率在统计学中是指一件事发生的概率与不发生概率的比值.公式如下:

                                                        odds(P) =\frac{p}{1-p}

对数几率则是加上了log,如下       

                                                        logit(p) =ln\frac{p}{1-p}

那么我们可以构建一个函数:          g(y) = ln \frac{y}{1-y}= \widehat{\omega }^{T}\cdot \widehat{X}

 化简可得:

                                                       \frac{y}{1-y} =e^{\widehat{\omega}^{T}\cdot \widehat{X}}

最后得到sigmoid函数:

                                                     sigmoid:  y = \frac{1}{1+e^{-\widehat{\omega}^{T}\cdot \widehat{X}}}

(以上\omega和X都为矩阵)

得到的sigmoid函数,y始终在0-1之间,所以可以作二分类问题,大于某个阈值为1类,小于则为0类。一般取0.5为阈值,因为sigmoid函数在y = 0.5处梯度最大,可以理解为两个类别在此时变化很大。

接下来就是构建损失函数,因为sigmoid函数如果使用RMSE均方根误差是非凸函数,很难找到最小值,有很多局部最小值,所以不适合,所以sigmoid函数构建了以下的

最大似然loss函数

                          L(\widehat{\omega }) = \prod_{i= 1}^{N} [-y_{i}ln(p_{1}(\widehat{X},\widehat{\omega }))-(1-y_{i})ln(1-p_{1}(\widehat{X},\widehat{\omega }))]

然后使用传统的梯度下降对\widehat{\omega }求偏导不断逼近loss最小值

即梯度下降公式为(设m个样本,\omega _{i}为第i个特征,\widehat{X}^T为m个样本的特征组成的特征矩阵)

                              \frac{\partial L(\widehat{\omega })}{\partial \widehat{\omega_{i}}}=\frac{1}{m}\widehat{X}^{T}(Sigmoid(\widehat{X},\widehat{\omega })-y)

构建logistic回归代码和线性回归的逻辑是一样的

数据集方面,使用了kaggle上的Dry_Bean数据集

数据集下载:Dry Bean Dataset (kaggle.com)

因为简单的逻辑回归只能解决二分类问题,所以我们可以取0到3349个样本

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from sklearn.model_selection import train_test_split

data = pd.read_csv('Dry_Bean_Dataset.csv')
df = pd.DataFrame(data)
color = []
for i in df['Class'][0:3349]:
    if i == 'SEKER':
        color.append('red')
    else:
        color.append('blue')
plt.scatter(df['MajorAxisLength'][0:3349], df['MinorAxisLength'][0:3349], color=color)
plt.xlabel('MajorAxisLength')
plt.ylabel('MinorAxisLength')
data_np = np.array(data)[0:3349]
# 取出特征和标签
X = data_np[:, :-1]
Y = data_np[:, -1:]
# X = np.concatenate((X, np.ones(shape=(np.shape(X)[0], 1))), axis=1)        # 将截距放到一个矩阵中
# _w = np.random.randn(np.shape(X)[0]+1, 1)
# X_test = np.random.randn(1, 17)
# X_test = np.concatenate((X_test, np.ones(shape=(1, 1))), axis=1)
# X = np.concatenate((X, np.ones(shape=(np.shape(X)[0], 1))), axis=1)
"""
使用sk-learn的train_test 划分训练集和测试集,也可以自己写一个函数
"""
X_train, X_test, Y_train, Y_test = train_test_split(X, Y)
print(np.shape(X_test))


class LogisticRegression:
    """
    _w : 系数,后面会让截距和系数一起合并
    _isfit : 判断是否训练过一次
    optimizer : 优化器
    """
    def __int__(self):
        self._w = None
        self.optimizer = 'bgd'
        self.isfit = False
    def normalization(self,x_train:np.ndarray, x_test:np.ndarray):
        x_train = x_train.astype(float)
        mean = np.mean(x_train, axis=0)
        std = np.std(x_train, axis=0)
        x_train = (x_train-mean)/std
        x_test = (x_test-mean)/std

        return x_train, x_test


    def _bgd(self):
        return

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

    def lr_schedule(self, epoch):
        return 0.98 ** epoch

    """
    对数据进行归一化处理,以训练集为参考,将训练集 测试集都进行归一化处理
    使用的是z-core方法
    """
    def z_score_normalization(self, X, X_Test):
        mean = np.mean(X, axis=0)
        std = np.std(X, axis=0)
        X_normalized = (X - mean) / std
        X_Test_normalized = (X_Test - mean) / std
        return X_normalized, X_Test_normalized

    def fit(self, x_train: np.ndarray, y_train: np.ndarray, x_test: np.ndarray, y_test: np.ndarray, lr=0.0001,
            batchsize=50, iters=20000, optimizer='bgd'):
        '''
        :param x_train: 特征,n行表示n个数据
        :param y_train:  标签
        :param lr: 学习率
        :param batchsize: SBGD梯度下降的 批量
        :param iters:   迭代次数
        :return:
        '''
        n_samples = x_train.shape[0]
        n_features = x_train.shape[1]
        labels = np.unique(y_train)  # 取出标签名,然后将标签转化为0或1方便
        for i in range(len(y_train)):
            if y_train[i] == labels[0]:
                y_train[i] = 0
            else:
                y_train[i] = 1
        for i in range(len(y_test)):
            if y_test[i] == labels[0]:
                y_test[i] = 0
            else:
                y_test[i] = 1
        self.optimizer = optimizer
        x_train, x_test = self.normalization(x_train, x_test)
        x_train = np.concatenate((x_train, np.ones(shape=(np.shape(x_train)[0], 1))), axis=1).astype(float)
        x_test = np.concatenate((x_test, np.ones(shape=(np.shape(x_test)[0], 1))), axis=1).astype(float)
        self._w = np.random.randn(x_train.shape[1], 1)  # 初始化权重         # 初始化截距
        # print(type(np.dot(x_train, self._w)))
        """
        先进行归一化数据
        使用BGD梯度下降
        和线性回归一样,先初始化系数,然后使用梯度下降逼近最优解
        SGD还没测试
        """
        for _ in range(iters):
            if self.optimizer == 'bgd':  # BGD批量梯度下降,取全部样本
                """
                因为梯度下降过程中_w系数可能会超出float 浮点数能表示的范围
                所以要限制位数
                """
                liner_model = np.dot(x_train, self._w).astype(np.float32)
                y_predict = self._sigmoid(liner_model)
                # 更新权重
                dw = np.dot(x_train.T, (y_predict - y_train))*(1/n_features)
                if dw.all() < 0.000001:
                    break
                self._w = self._w - lr * dw
                lr = lr * self.lr_schedule(_)
            elif self.optimizer == 'sgd':
                idx = np.random.randint(0, n_samples)
                x_idx, y_idx = x_train[idx], y_train[idx]
                y_predict = self._sigmoid(np.dot(x_idx, self._w))
                dw = x_idx(y_predict - y_idx)
                self._w -= lr * dw
                lr = lr * self.lr_schedule(1)
        self.isfit = True
        return self.predict(x_test)


    """
    输入一组测试数据集
    每次取出一个样本进行预测
    """

    def predict(self, X, threshold=0.5):
        if not self.isfit:
            print('not fit')
            return
        n_samples = X.shape[0]
        # X = np.concatenate((X, np.ones(shape=(n_samples, 1))), axis=1)
        y_predict = []
        for i in range(len(X)):
            liner_model = np.dot(X[i], self._w).astype(np.float32)
            y = self._sigmoid(liner_model)
            print(y)
            y_predict.append(1 if y > threshold else 0)
            # y_predict = self._sigmoid(liner_model)
            # print(y_predict)
            #print(liner_model)
        return y_predict


LogisticNet = LogisticRegression()
Y_predict = LogisticNet.fit(x_train=X_train, x_test=X_test, y_train=Y_train, y_test=Y_test)
labels =np.unique(Y_train)
"""
测试准确率
"""
for i in range(len(Y_test)):
    if Y_test[i] == labels[0]:
        Y_test[i] = 0
    else:
        Y_test[i] = 1
acc = 0
err = 0
for i in range(len(Y_test)):
    if Y_predict[i] == Y_test[i]:
        acc += 1
    else:
        err += 1

print(Y_test)
print(acc/len(Y_test))

由于训练集不够,还有算法本身限制,模型的准确率会随着初始化权重的变化而波动,有时候会达到1,有时候却很小,说明模型是欠拟合的,可以增加数据集或对特征进行升维.

机器学习作为AI的入门,学好机器学习对于后面学习深度学习和神经网络有很大帮助,比如说sigmoid函数,梯度下降方法等都会运用在深度学习算法中。

  • 22
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
逻辑回归是一种用于分类问题的机器学习算法,其原理是基于线性回归的思想,通过使用逻辑函数(也称为sigmoid函数)将预测结果映射到[0,1]之间的概率值。以下是一个使用Python实现逻辑回归的示例代码: ```python import numpy as np import matplotlib.pyplot as plt # 定义sigmoid函数 def sigmoid(z): return 1 / (1 + np.exp(-z)) # 定义损失函数 def loss(h, y): return (-y * np.log(h) - (1 - y) * np.log(1 - h)).mean() # 定义逻辑回归模型 class LogisticRegression: def __init__(self, lr=0.01, num_iter=100000, fit_intercept=True, verbose=False): self.lr = lr self.num_iter = num_iter self.fit_intercept = fit_intercept self.verbose = verbose def add_intercept(self, X): intercept = np.ones((X.shape[0], 1)) return np.concatenate((intercept, X), axis=1) def fit(self, X, y): if self.fit_intercept: X = self.add_intercept(X) # 初始化权重参数 self.theta = np.zeros(X.shape[1]) for i in range(self.num_iter): z = np.dot(X, self.theta) h = sigmoid(z) gradient = np.dot(X.T, (h - y)) / y.size self.theta -= self.lr * gradient if(self.verbose == True and i % 10000 == 0): z = np.dot(X, self.theta) h = sigmoid(z) print(f'loss: {loss(h, y)}') def predict_prob(self, X): if self.fit_intercept: X = self.add_intercept(X) return sigmoid(np.dot(X, self.theta)) def predict(self, X, threshold=0.5): return self.predict_prob(X) >= threshold # 示例:使用逻辑回归对二分类数据进行训练和预测 from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split # 生成二分类数据集 X, y = make_classification(n_samples=100, n_features=2, n_informative=2, n_redundant=0, random_state=42) # 将数据集划分为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 创建逻辑回归模型并进行训练 model = LogisticRegression(lr=0.1, num_iter=300000) model.fit(X_train, y_train) # 在测试集上进行预测 y_pred = model.predict(X_test) # 计算准确率 accuracy = (y_pred == y_test).mean() print(f'Accuracy: {accuracy}') ``` 这段代码首先定义了sigmoid函数和损失函数,然后实现了一个LogisticRegression类,其中包含了fit方法用于训练模型,以及predict_prob和predict方法用于预测。最后,示例代码使用sklearn库生成了一个二分类数据集,将数据集划分为训练集和测试集,并使用逻辑回归模型进行训练和预测。最后计算了预测的准确率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值