机器学习之逻辑回归算法实现

什么是逻辑回归

通常来说,回归是连续的,比如线性/非线性回归。然而,在实际的问题中,如果想用回归的思想解决分类问题,那么输出就变成离散的。每个元素则代表不同类别。我们考虑最简单的二分类问题,与线性回归相似
f ( x ) = { 0 , θ T x ≤ z 1 , θ T x > z f\left( x \right) =\left\{ \begin{array}{l} 0,\boldsymbol{\theta }^T\boldsymbol{x}\le z\\ 1,\boldsymbol{\theta }^T\boldsymbol{x}>z\\ \end{array} \right. f(x)={0,θTxz1,θTx>z

sigmoid函数

σ ( x ) = 1 1 + e − x = e x e x + 1 \sigma \left( x \right) =\frac{1}{1+e^{-x}}=\frac{e^x}{e^x+1} σ(x)=1+ex1=ex+1ex
这个函数的牛逼之处就在于它对x进行求导非常之简洁,并且这个函数值的值域在0-1.

sigmoid函数求导

d σ ( x ) d x = σ ( x ) ( 1 − σ ( x ) ) \frac{d\sigma \left( x \right)}{dx}=\sigma \left( x \right) \left( 1-\sigma \left( x \right) \right) dxdσ(x)=σ(x)(1σ(x))

逻辑回归公式的推导

实话实说还有一点复杂,但是我可以说一下我理解的大体思路,主要运用的就是最大似然估计,把线性回归的结果当成样本预测为1的概率。把似然函数通过取对数重新构造优化目标,然后重新定义损失函数,对损失函数进行梯度下降,进行参数更新,这这里有一个十分关键的问题就是梯度下降公式的参数如何更新。

损失函数

L ( θ ) = ∏ i = 1 N f θ ( x i ) y i ( 1 − f θ ( x i ) ) 1 − y i L\left( \theta \right) =\prod_{i=1}^N{f}_{\theta}\left( x_i \right) ^{y_i}\left( 1-f_{\theta}\left( x_i \right) \right) ^{1-y_i} L(θ)=i=1Nfθ(xi)yi(1fθ(xi))1yi

损失函数: − l ( θ ) = − log ⁡ L ( θ ) = − ∑ i = 1 N [ y i log ⁡ f θ ( x i ) + ( 1 − y i ) log ⁡ ( 1 − f θ ( x i ) ) ] \text{损失函数:}-l\left( \theta \right) =-\log L\left( \theta \right) =-\sum_{i=1}^N{\left[ y_i\log f_{\theta}\left( x_i \right) +\left( 1-y_i \right) \log \left( 1-f_{\theta}\left( x_i \right) \right) \right]} 损失函数:l(θ)=logL(θ)=i=1N[yilogfθ(xi)+(1yi)log(1fθ(xi))]

我对损失函数的理解:

梯度的矩阵形式

∇ J ( θ ) = − ∇ l ( θ ) = X T ( y − σ ( X θ ) ) \nabla J\left( \theta \right) =-\nabla l\left( \theta \right) =\boldsymbol{X}^T\left( \boldsymbol{y}-\sigma \left( \boldsymbol{X}\theta \right) \right) J(θ)=l(θ)=XT(yσ(Xθ))
X 为样本矩阵, y 为标签向量 \boldsymbol{X}\text{为样本矩阵,}\boldsymbol{y}\text{为标签向量} X为样本矩阵,y为标签向量

梯度下降参数更新形式

θ ← θ + η X T ( y − σ ( X θ ) ) \boldsymbol{\theta }\gets \boldsymbol{\theta }+\eta \boldsymbol{X}^T\left( y-\sigma \left( \boldsymbol{X}\theta \right) \right) θθ+ηXT(yσ(Xθ))

加入L2正则化约束后

min ⁡ θ J ( θ ) = min ⁡ θ ( − l ( θ ) + λ 2 ∥ θ ∥ 2 2 ) ∇ J ( θ ) = − X T ( y − σ ( X θ ) ) + λ θ θ ← ( 1 − λ η ) θ + η X T ( y − σ ( X θ ) ) \begin{gathered} \min_{\theta}J(\theta)=\min_{\theta}\left(-l\left(\theta\right)+\frac{\lambda}{2}\left\|\theta\right\|_{2}^{2}\right) \\ \nabla J\left(\theta\right)=-X^{T}\left(y-\sigma\left(X\theta\right)\right)+\lambda\theta \\ \theta\leftarrow\left(1-\lambda\eta\right)\theta+\eta X^{T}\left(y-\sigma\left(X\theta\right)\right) \end{gathered} θminJ(θ)=θmin(l(θ)+2λθ22)J(θ)=XT(yσ())+λθθ(1λη)θ+ηXT(yσ())

正则化约束后的损失函数

− l ( θ ) + λ 2 ∥ θ ∥ 2 -l\left( \boldsymbol{\theta } \right) +\frac{\lambda}{2}\lVert \boldsymbol{\theta } \rVert ^2 l(θ)+2λθ2

数据链接

链接:https://pan.baidu.com/s/1_4k2uy5nFfKOt9AfeT0sZg?pwd=xhuo
提取码:xhuo

# 首先读取数据并且进行数据可视化
import numpy as np

# 使用genfromtxt函数读取CSV文件
data = np.genfromtxt('lr_dataset.csv', delimiter=',')

# 打印NumPy数组
print(data)
[[ 0.4304  0.2055  1.    ]
 [ 0.0898 -0.1527  1.    ]
 [ 0.2918 -0.1248  1.    ]
 ...
 [ 0.5826  0.4424  1.    ]
 [-0.0398  0.2877  1.    ]
 [ 0.0035  0.623   1.    ]]
# 绘制可视化图
import matplotlib.pyplot as plt

# 绘制散点图
x=data[:,0]
y=data[:,1]
labels=data[:,2]

# 根据不同的标签设置不同的颜色
colors = []
for label in labels:
    if label == 0:
        colors.append('#d62828')
    elif label == 1:
        colors.append('#006d77')
plt.scatter(x, y,c=colors)

# 添加标题和标签
plt.title('Scatter Plot')
plt.grid()
plt.xlabel('X')
plt.ylabel('Y')


png

def logostic(x):
    return 1 / (1 + np.exp(-x))

def acc(y_true, y_pred):
    return np.mean(y_true == y_pred)

def auc(y_true, y_pred):
    # 按预测值从大到小排序,越靠前的样本预测正类概率越大
    idx = np.argsort(y_pred)[::-1]
    y_true = y_true[idx]
    y_pred = y_pred[idx]
    # 把y_pred中不重复的值当作阈值,依次计算FP样本和TP样本数量
    # 由于两个数组已经排序且位置对应,直接从前向后累加即可
    tp = np.cumsum(y_true) 
    fp = np.cumsum(1 - y_true)
    tpr = tp / tp[-1]
    fpr = fp / fp[-1]
    # 依次枚举FPR,计算曲线下的面积
    # 方便起见,给FPR和TPR最开始添加(0,0)
    s = 0.0
    tpr = np.concatenate([[0], tpr])
    fpr = np.concatenate([[0], fpr])
    for i in range(1, len(fpr)):
        s += (fpr[i] - fpr[i - 1]) * tpr[i]
    return s
# 接下来进行训练集与测试集的划分

# 获取数据的行数
from matplotlib.ticker import MaxNLocator


N=len(data[:,0])

alpha=0.7
train_N=int(N*0.7)
test_N=N-train_N

train_x=data[0:train_N,0:2]
train_y=data[0:train_N,2:3]
test_x=data[train_N:N,0:2]
test_y=data[train_N:N,2:3]

# 接下来开始进行训练

# 对于训练数据,在矩阵后面拼接一列1,算是偏置项
# train_x.shape[0]表示获取train_x这个二维数组第一个维度数据的大小
# axis=1 表示按列连接,而 axis=0 表示按行连接。
print(train_x.shape)
print(train_y.shape)
X=np.concatenate([train_x,np.ones((train_x.shape[0],1))],axis=1)

print(test_x.shape)
X_test=np.concatenate([test_x,np.ones((test_x.shape[0],1))],axis=1)
print(X)
print(X.shape)

# 初始化线性回归的参数
theta = np.random.normal(size=(X.shape[1],))
theta=theta.reshape((theta.shape[0], 1))
# print(theta)
# print(theta.shape)
# 定义梯度下降迭代次数
num_step=250

# 定义L2正则约束强度
lameda_=1.0

# 定义学习率
learning_rate=0.002
# 这里还有点神奇X是n乘3的矩阵,theta是3乘1的矩阵
train_loss=[]
test_loss=[]
train_acc=[]
test_acc=[]
train_auc=[]
test_auc=[]
for i in range (num_step):
    predict=logostic(X@theta)
    # 这里使用加入L2正则化约束
    # 计算预测值
    predict=np.array(predict)
    predict = predict.reshape((predict.shape[0], 1))
    grad=-X.T@(train_y-predict)+lameda_*theta

    # 参数更新
    theta-=learning_rate*grad

    # 记录损失函数
    # 如果是正则化约束的话还要加入正则项
    # train_y:n×1
    # predict:n×1
    # theta:3×1
    # np.linalg.norm(theta) 是 NumPy 中的函数,用于计算向量或矩阵的范数(norm)。
    # np.linalg.norm(theta) 是计算theta中所有元素的平方和,然后开方
    Loss=-train_y.T@np.log(predict)-(1-train_y.T)@np.log(1-predict)+lameda_/2*np.linalg.norm(theta)*np.linalg.norm(theta)

    train_loss.append(Loss/train_x.shape[0])

    # 测试集预测
    test_predict=logostic(X_test@theta)
    test_predict=np.array(test_predict)
    test_predict = test_predict.reshape((test_predict.shape[0], 1))

    # 测试集的损失就不用加入正则项了
    test_Loss=-(test_y.T@np.log(test_predict)+(1-test_y.T)@np.log(1-test_predict))

    test_loss.append(test_Loss/train_x.shape[0])
    # print("第"+str(i)+"次迭代"+"\n")

    train_acc.append(acc(train_y,predict>0.5))
    test_acc.append(acc(test_y,test_predict>0.5))

    train_auc.append(auc(train_y,predict))
    test_auc.append(auc(test_y,test_predict))

# 计算测试集上的预测准确率
y_pred = np.where(logostic(X_test @ theta) >= 0.5, 1, 0)
final_acc = acc(test_y, y_pred)
print('预测准确率:', final_acc)
print('回归系数:', theta)


plt.figure(figsize=(13, 9))
xticks = np.arange(num_step) + 1
# 绘制训练曲线
# plt.subplot(221)
train_loss=np.array(train_loss)
train_loss= train_loss.flatten()
plt.plot(xticks, train_loss, color='blue', label='train loss')

test_loss=np.array(test_loss)
test_loss= test_loss.flatten()

plt.plot(xticks, test_loss, color='red', ls='--', label='test loss')
plt.gca().xaxis.set_major_locator(MaxNLocator(integer=True))
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
(700, 2)
(700, 1)
(300, 2)
[[ 0.4304  0.2055  1.    ]
 [ 0.0898 -0.1527  1.    ]
 [ 0.2918 -0.1248  1.    ]
 ...
 [ 0.2403  0.235   1.    ]
 [ 0.9708  0.7746  1.    ]
 [ 0.5301 -0.3728  1.    ]]
(700, 3)
预测准确率: 0.91
回归系数: [[2.90282086]
 [2.82757117]
 [0.54846955]]


png

  • 28
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

泰伦斯-Ternence

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值