找回密码
 立即注册
首页 业界区 业界 一文读懂什么是逻辑回归

一文读懂什么是逻辑回归

恃液 昨天 16:06
逻辑回归介绍

逻辑回归(Logistic Regression)是一种经典的分类算法,尽管名字中带有 “回归”,但它本质上用于解决二分类问题(也可扩展到多分类)。逻辑回归的本质是 “在线性回归的基础上,通过一个映射函数将输出转化为概率(从而实现对类别概率的预测)”,这个映射函数就是Sigmoid函数。
逻辑回归是机器学习中最基础的分类算法之一,核心是通过 Sigmoid 函数将线性输出转化为概率,结合交叉熵损失和梯度下降求解参数。
它虽简单,但在实际业务中(尤其是需要可解释性的场景)仍被广泛使用,也是理解更复杂分类模型(如神经网络)的基础。
1.png

sigmoid函数
  1. def sigmoid(z):
  2.     """
  3.     Compute the sigmoid of z
  4.     Args:
  5.         z (ndarray): A scalar, numpy array of any size.
  6.     Returns:
  7.         g (ndarray): sigmoid(z), with the same shape as z
  8.          
  9.     """
  10.     g = 1 / (1 + np.exp(-z))
  11.    
  12.     return g
复制代码
2.png


逻辑回归模型

4.png

5.png

逻辑回归的决策边界

线性逻辑回归

根据sigmoid函数图象:z=0是中间位置,视为决策边界;那么为了得到决策边界的特征情况,我们假设:

  • 线性模型 z = w1 * x1 + w2 * x2 + b
  • 参数 w1=w2=1, b=03,那么x2 = -x1 + 3这条直线就是决策边界
如果特征x在这条线的右边,那么此逻辑回归则预测为1,反之则预测为0;(分为两类)
6.png

多项式逻辑回归

多项式回归决策边界,我们假设:

  • 多项式模型:z = w1 * x1**2 + w2 * x2**2 + b
  • 参数:w1=w2=1, b=-1
如果特征x在圆的外面,那么此逻辑回归则预测为1,反之则预测为0;(分为两类)
7.png

扩展:随着多项式的复杂度增加,还可以拟合更更多非线性的复杂情况
8.png

逻辑回归的损失函数

平方损失和交叉熵损失

回顾下线性回归的损失函数(平方损失):
9.png

平方误差损失函数不适用于逻辑回归模型:平方损失在逻辑回归中是 “非凸函数”(存在多个局部最优解),难以优化;
10.png

所以我们需要一个新的损失函数,即交叉熵损失;交叉熵损失是 “凸函数”,可通过梯度下降高效找到全局最优。
交叉熵源于信息论,我们暂时不做深入介绍,直接给出交叉熵损失函数公式:
11.png

对数回顾

复习下对数函数的性质,以便理解为什么 交叉熵损失是 “凸函数”?
12.png

13.png

14.png

简化交叉熵损失函数

15.png

为什么要用这个函数来表示?来源自 最大释然估计(Maximum Likelihood),这里不做过多介绍。
16.png

简化结果:
17.png

逻辑回归的梯度计算

自然对数求导公式:
18.png

 链式求导法则:
19.png

⚠️注意:
20.png

过拟合问题

线性回归过拟合

21.png

逻辑回归过拟合

22.png


  • 欠拟合(underfit),存在高偏差(bias)
  • 泛化(generalization):希望我们的学习算法在训练集之外的数据上也能表现良好(预测准确)
  • 过拟合(overfit),存在高方差(variance) 
解决过拟合的办法


  • 特征选择:只选择部分最相关的特征(基于直觉intuition)进行训练;缺点是丢掉了部分可能有用的信息
  • 正则化:正则化是一种更温和的减少某些特征的影响,而无需做像测地消除它那样苛刻的事:

    • 鼓励学习算法缩小参数,而不是直接将参数设置为0(保留所有特征的同时避免让部分特征产生过大的影响)
    • 鼓励把 w1 ~ wn 变小,b不用变小 

正则化模型

It turns out that regularization is a way
to more gently reduce ths impacts of some of the features without doing something as harsh as eliminating it outright.
23.png

关于正则化项的说明:
24.png

带正则化项的损失函数

正则化线性回归

损失函数:
25.png

梯度计算:
26.png

 
分析梯度计算公式,由于alpha和lambda通常是很小的值,所以相当于在每次迭代之前把参数w缩小了一点点,这也就是正则化的工作原理,如下所示:
27.png

正则化逻辑回归

损失函数:
28.png

梯度计算:
29.png

线性回归和逻辑回归正则化总结

30.png

 
逻辑回归实战

模型选择

可视化训练数据,基于此数据选择线性逻辑回归模型
31.png

关键代码实现
  1. def sigmoid(z):
  2.         g = 1 / (1 + np.exp(-z))
  3.         return g
  4. def compute_cost(X, y, w, b, lambda_= 1):
  5.         """
  6.     Computes the cost over all examples
  7.     Args:
  8.       X : (ndarray Shape (m,n)) data, m examples by n features
  9.       y : (array_like Shape (m,)) target value
  10.       w : (array_like Shape (n,)) Values of parameters of the model      
  11.       b : scalar Values of bias parameter of the model
  12.       lambda_: unused placeholder
  13.     Returns:
  14.       total_cost: (scalar)         cost
  15.     """
  16.         m, n = X.shape
  17.         total_cost = 0
  18.         for i in range(m):
  19.                 f_wb_i = sigmoid(np.dot(X[i], w) + b)
  20.                 loss = -y[i] * np.log(f_wb_i) - (1 - y[i]) * np.log(1 - f_wb_i)
  21.                 total_cost += loss
  22.         total_cost = total_cost / m
  23.         return total_cost
  24. def compute_gradient(X, y, w, b, lambda_=None):
  25.     """
  26.     Computes the gradient for logistic regression
  27.     Args:
  28.       X : (ndarray Shape (m,n)) variable such as house size
  29.       y : (array_like Shape (m,1)) actual value
  30.       w : (array_like Shape (n,1)) values of parameters of the model      
  31.       b : (scalar)                 value of parameter of the model
  32.       lambda_: unused placeholder.
  33.     Returns
  34.       dj_dw: (array_like Shape (n,1)) The gradient of the cost w.r.t. the parameters w.
  35.       dj_db: (scalar)                The gradient of the cost w.r.t. the parameter b.
  36.     """
  37.     m, n = X.shape
  38.     dj_dw = np.zeros(n)
  39.     dj_db = 0.
  40.     for i in range(m):
  41.         f_wb_i = sigmoid(np.dot(X[i], w) + b)
  42.         diff = f_wb_i - y[i]
  43.         dj_db += diff
  44.         for j in range(n):
  45.             dj_dw[j] = dj_dw[j] + diff * X[i][j]
  46.    
  47.     dj_db = dj_db / m
  48.     dj_dw = dj_dw / m
  49.         
  50.     return dj_db, dj_dw
  51. def gradient_descent(X, y, w_in, b_in, cost_function, gradient_function, alpha, num_iters, lambda_):
  52.     """
  53.     Performs batch gradient descent to learn theta. Updates theta by taking
  54.     num_iters gradient steps with learning rate alpha
  55.    
  56.     Args:
  57.       X :    (array_like Shape (m, n)
  58.       y :    (array_like Shape (m,))
  59.       w_in : (array_like Shape (n,))  Initial values of parameters of the model
  60.       b_in : (scalar)                 Initial value of parameter of the model
  61.       cost_function:                  function to compute cost
  62.       alpha : (float)                 Learning rate
  63.       num_iters : (int)               number of iterations to run gradient descent
  64.       lambda_ (scalar, float)         regularization constant
  65.       
  66.     Returns:
  67.       w : (array_like Shape (n,)) Updated values of parameters of the model after
  68.           running gradient descent
  69.       b : (scalar)                Updated value of parameter of the model after
  70.           running gradient descent
  71.     """
  72.    
  73.     # number of training examples
  74.     m = len(X)
  75.    
  76.     # An array to store cost J and w's at each iteration primarily for graphing later
  77.     J_history = []
  78.     w_history = []
  79.     w = copy.deepcopy(w_in)
  80.     b = b_in
  81.    
  82.     for i in range(num_iters):
  83.         dj_db, dj_dw = gradient_function(X, y, w, b, lambda_)
  84.         w = w - alpha * dj_dw
  85.         b = b - alpha * dj_db
  86.         cost = cost_function(X, y, w, b, lambda_)
  87.         J_history.append(cost)
  88.         w_history.append(w)
  89.         if i % math.ceil(num_iters / 10) == 0:
  90.             print(f"{i:4d} cost: {cost:6f}, w: {w}, b: {b}")
  91.         
  92.     return w, b, J_history, w_history #return w and J,w history for graphing
  93. def predict(X, w, b):
  94.     m, n = X.shape   
  95.     p = np.zeros(m)
  96.     for i in range(m):
  97.         f_wb = sigmoid(np.dot(X[i], w) + b)
  98.         p[i] = f_wb >= 0.5
  99.     return p
复制代码
结果展示
  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. import matplotlib.font_manager as fm
  4. # 支持显示中文
  5. font_path = '/System/Library/Fonts/STHeiti Light.ttc'
  6. custom_font = fm.FontProperties(fname=font_path)
  7. plt.rcParams["font.family"] = custom_font.get_name()
  8. # 载入训练集
  9. X_train, y_train = load_data("data/ex2data1.txt")
  10. # 训练模型
  11. np.random.seed(1)
  12. intial_w = 0.01 * (np.random.rand(2).reshape(-1,1) - 0.5)
  13. initial_b = -8
  14. iterations = 10000
  15. alpha = 0.001
  16. w_out, b_out, J_history,_ = gradient_descent(X_train ,y_train, initial_w, initial_b, compute_cost, compute_gradient, alpha, iterations, 0)
  17. # 根据训练结果(w_out和b_out)计算决策边界
  18. #f = w0*x0 + w1*x1 + b
  19. # x1 = -1 * (w0*x0 + b) / w1
  20. plot_x = np.array([min(X_train[:, 0]), max(X_train[:, 0])])
  21. plot_y = (-1. / w_out[1]) * (w_out[0] * plot_x + b_out)
  22. # 将训练数据分类
  23. x0s_pos = []
  24. x1s_pos = []
  25. x0s_neg = []
  26. x1s_neg = []
  27. for i in range(len(X_train)):
  28.     x = X_train[i]
  29.     # print(x)
  30.     y_i = y_train[i]
  31.     if y_i == 1:
  32.         x0s_pos.append(x[0])
  33.         x1s_pos.append(x[1])
  34.     else:
  35.         x0s_neg.append(x[0])
  36.         x1s_neg.append(x[1])
  37. # 绘图
  38. plt.figure(figsize=(8, 6))
  39. plt.scatter(x0s_pos, x1s_pos, marker='o', c='green', label="Admitted")
  40. plt.scatter(x0s_neg, x1s_neg, marker='x', c='red', label="Not admitted")
  41. plt.plot(plot_x, plot_y, lw=1, label="决策边界")
  42. plt.xlabel('Exam 1 score', fontsize=12)
  43. plt.ylabel('Exam 2 score', fontsize=12)
  44. plt.title('在二维平面上可视化分类模型的决策边界', fontsize=14)
  45. plt.legend(fontsize=12, loc='upper center')
  46. plt.grid(True)
  47. plt.show()
  48. # 使用训练集计算预测准确率
  49. p = predict(X_train, w_out, b_out)
  50. print('Train Accuracy: %f'%(np.mean(p == y_train) * 100))
  51. # Train Accuracy: 92.000000
复制代码
32.png

正则化逻辑回归实战

模型选择

可视化训练数据,基于此数据选择多项式逻辑回归模型
 
33.png

关键代码实现

由于要拟合非线性决策边界,所以要增加特征的复杂度(训练数据里只有2个特征)。
特征映射函数
  1. # 将输入特征 X1 和 X2 转换为六次多项式特征
  2. # 这个函数常用于逻辑回归或支持向量机等模型中,通过增加特征的复杂度来拟合非线性决策边界。
  3. def map_feature(X1, X2):
  4.     """
  5.     Feature mapping function to polynomial features   
  6.     """
  7.     X1 = np.atleast_1d(X1)
  8.     X2 = np.atleast_1d(X2)
  9.     degree = 6
  10.     out = []
  11.     for i in range(1, degree+1):
  12.         for j in range(i + 1):
  13.             out.append((X1**(i-j) * (X2**j)))
  14.     return np.stack(out, axis=1)
复制代码
正则化后的损失函数和梯度计算函数
  1. def compute_cost_reg(X, y, w, b, lambda_ = 1):
  2.     """
  3.     Computes the cost over all examples
  4.     Args:
  5.       X : (array_like Shape (m,n)) data, m examples by n features
  6.       y : (array_like Shape (m,)) target value
  7.       w : (array_like Shape (n,)) Values of parameters of the model      
  8.       b : (array_like Shape (n,)) Values of bias parameter of the model
  9.       lambda_ : (scalar, float)    Controls amount of regularization
  10.     Returns:
  11.       total_cost: (scalar)         cost
  12.     """
  13.     m, n = X.shape
  14.     # Calls the compute_cost function that you implemented above
  15.     cost_without_reg = compute_cost(X, y, w, b)
  16.    
  17.     reg_cost = 0.
  18.     for j in range(n):
  19.         reg_cost += w[j]**2
  20.    
  21.     # Add the regularization cost to get the total cost
  22.     total_cost = cost_without_reg + (lambda_/(2 * m)) * reg_cost
  23.     return total_cost
  24. def compute_gradient_reg(X, y, w, b, lambda_ = 1):
  25.     """
  26.     Computes the gradient for linear regression
  27.     Args:
  28.       X : (ndarray Shape (m,n))   variable such as house size
  29.       y : (ndarray Shape (m,))    actual value
  30.       w : (ndarray Shape (n,))    values of parameters of the model      
  31.       b : (scalar)                value of parameter of the model  
  32.       lambda_ : (scalar,float)    regularization constant
  33.     Returns
  34.       dj_db: (scalar)             The gradient of the cost w.r.t. the parameter b.
  35.       dj_dw: (ndarray Shape (n,)) The gradient of the cost w.r.t. the parameters w.
  36.     """
  37.     m, n = X.shape
  38.    
  39.     dj_db, dj_dw = compute_gradient(X, y, w, b)
  40.     # Add the regularization
  41.     for j in range(n):
  42.         dj_dw[j] += (lambda_ / m) * w[j]
  43.         
  44.     return dj_db, dj_dw
复制代码
结果展示
  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. import matplotlib.font_manager as fm
  4. # 支持显示中文
  5. font_path = '/System/Library/Fonts/STHeiti Light.ttc'
  6. custom_font = fm.FontProperties(fname=font_path)
  7. plt.rcParams["font.family"] = custom_font.get_name()
  8. # 载入训练集
  9. X_train, y_train = load_data("data/ex2data2.txt")
  10. # 通过增加特征的复杂度来拟合非线性决策边界
  11. X_mapped = map_feature(X_train[:, 0], X_train[:, 1])
  12. print("Original shape of data:", X_train.shape)
  13. print("Shape after feature mapping:", X_mapped.shape)
  14. # 训练模型
  15. np.random.seed(1)
  16. initial_w = np.random.rand(X_mapped.shape[1])-0.5
  17. initial_b = 1.
  18. # Set regularization parameter lambda_ to 1 (you can try varying this)
  19. lambda_ = 0.5
  20. iterations = 10000
  21. alpha = 0.01
  22. w_out, b_out, J_history, _ = gradient_descent(X_mapped, y_train, initial_w, initial_b, compute_cost_reg, compute_gradient_reg, alpha, iterations, lambda_)
  23. # 根据训练结果(w_out和b_out)计算决策边界
  24. # - 创建网格点 u 和 v 覆盖特征空间
  25. u = np.linspace(-1, 1.5, 50)
  26. v = np.linspace(-1, 1.5, 50)
  27. # - 计算每个网格点处的预测概率 z
  28. z = np.zeros((len(u), len(v)))
  29. # Evaluate z = theta*x over the grid
  30. for i in range(len(u)):
  31.     for j in range(len(v)):
  32.         z[i,j] = sig(np.dot(map_feature(u[i], v[j]), w_out) + b_out)
  33. # - 转置 z 是必要的,因为contour函数期望的输入格式与我们的计算顺序不一致      
  34. z = z.T
  35. # 分类
  36. x0s_pos = []
  37. x1s_pos = []
  38. x0s_neg = []
  39. x1s_neg = []
  40. for i in range(len(X_train)):
  41.     x = X_train[i]
  42.     # print(x)
  43.     y_i = y_train[i]
  44.     if y_i == 1:
  45.         x0s_pos.append(x[0])
  46.         x1s_pos.append(x[1])
  47.     else:
  48.         x0s_neg.append(x[0])
  49.         x1s_neg.append(x[1])
  50. # 绘图
  51. plt.figure(figsize=(8, 6))
  52. plt.scatter(x0s_pos, x1s_pos, marker='o', c='black', label="y=1")
  53. plt.scatter(x0s_neg, x1s_neg, marker='x', c='orange', label="y=0")
  54. # 绘制决策边界(等高线)
  55. plt.contour(u,v,z, levels = [0.5], colors="green")
  56. # 创建虚拟线条用于图例(颜色和线型需与等高线一致)
  57. plt.plot([], [], color='green', label="决策边界")
  58. plt.xlabel('Test 1', fontsize=12)
  59. plt.ylabel('Test 2', fontsize=12)
  60. plt.title('正则化逻辑回归模型分类效果可视化(lambda=0.5)', fontsize=14)
  61. # plt.legend(fontsize=12, loc='upper center')
  62. plt.legend(fontsize=12)
  63. plt.grid(True)
  64. plt.show()
  65. #Compute accuracy on the training set
  66. p = predict(X_mapped, w_out, b_out)
  67. print('Train Accuracy: %f'%(np.mean(p == y_train) * 100))
  68. # Train Accuracy: 83.050847
复制代码
34.png

正则化效果对比

正则化对损失和决策边界的影响

35.png


正则化项lambda参数大小对决策边界的影响

37.png

 
参考

吴恩达团队在Coursera开设的机器学习课程:https://www.coursera.org/specializations/machine-learning-introduction
在B站学习:https://www.bilibili.com/video/BV1Pa411X76s 
 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册