机器学习笔记(二)——线性回归、逻辑回归

写在前面

写本系列(自用)的目的是回顾已经学过的知识、记录新学习的知识或是记录心得理解,方便自己以后快速复习,减少遗忘。概念部分大部分来自于机器学习菜鸟教程,公式部分也会参考机器学习书籍、阿里云天池。机器学习如果只啃概念始终学不牢,因此我打算概念与代码结合。

Part 2 机器学习算法

一、线性回归

先看一个引例,假如有人给了你一份清单,上面写了不同大小的房子售出的价格,同时,还告诉了你他的房子大小,问你能否告诉他他的房子大概能卖多少。

我们将房子大小设为,房子价格设为y,画出散点图,可以看出散点大致能估计为一条直线,只要能求出这条直线,就能够根据新给出的x值,也就是房子大小,估计出y值,房子售价,这就是一个简单的线性回归问题。

线性回归 (Linear Regression) 是一种用于预测连续值的最基本的机器学习算法,它假设目标变量 y 和特征变量 x 之间存在线性关系,并试图找到一条最佳拟合直线来描述这种关系。拟合直线为

\hat{y}=w*x+b

其中,\hat{y}为预测值,x为特征变量,w为权重(斜率),b为截距。

线性回归的目标是找到最佳的w、b使得预测值\hat{y}与真实值y之间的误差最小。

当然,我们知道,影响房子价格的因素不一定只有房子大小一个,可能还会有地理位置、朝向、装修等等,因此一维的w并不能解决所有线性回归问题,我们可以把维度扩展到高维:

设X为n×p维向量,其中的每个x都是p×1的列向量,Y为n×1维向量,其中的每个y都是标量。w为p×1维向量,这里为了方便我们将常数值b看作w_0,这样只需要为X增添一维x_0=1,就可以保证x和w的维度可以相乘。那么,此时的拟合直线可以写做:

\hat{y}=w^Tx

1、最小二乘法

最小二乘法是一种常用的求解线性回归的方法,它通过使残差平方和(RSS)最小来得到最佳的w。这也可看作一个优化问题。

min \sum_{i=1}^{n}(y_i-\hat{y_i})^2

其中,y是实际值,\hat{y}为预测值,i表示第i组数据。

为了得到最佳的w值,将以上残差平方看作自变量为w的函数,函数为:

f(w) = \sum_{i=1}^{n} (w^Tx_i - y_i)^2

现在对这个函数进行化简,化简过程如图所示:

 将函数化简为

f(w) = w^TX^TXw-2w^TX^TY+Y^TY

对w进行求导,令导函数等于0,即可求得最佳的w值。求导过程如图:

 至此,就求出了w的值:

w = (X^TX)^{-1}X^TY

关于矩阵求导,详细的可以在b站搜索矩阵求导相关视频,这里只列几个常见结论,推导过程b站上也有,最小二乘法的推导过程均来自于b站upshuhuai008的机器学习白板推导系列,很多推导过程都会参考大佬的视频,理论部分来自于各种资料。下面是矩阵求导公式:

1、设a为标量,则标量a对向量x求导为0。

2、设A为n维列向量,则

\frac{\partial(x^TA)}{\partial x}=\frac{\partial(A^Tx)}{\partial x}=A

3、                                                             \frac{\partial(x^Tx)}{\partial x}=2x

4、                                                    \frac{\partial(x^TAx)}{\partial x}=Ax+A^Tx

2、最小二乘法的代码求解

现在在pycharm中运行以下代码:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression

# 创建训练数据
def true_fun(x):                   # 这是本次需要拟合的函数
    return 1.7*x + 0.2

np.random.seed(0)                  #设置随机数种子
n_samples = 50                     #样本数

x_train = np.sort(np.random.rand(n_samples))                                          #这里是生成50个0~1的随机数并且从小到大排序
y_train = (true_fun(x_train)+np.random.randn(n_samples)).reshape(n_samples,1)         #根据x_train的值计算出y_train的值,但是需要加上噪声干扰来作为训练的y值,最后将y转化为50×1维

'''这是线性回归的核心代码'''
model = LinearRegression()          #创建一个线性回归模型
model.fit(x_train[:,np.newaxis], y_train)       #输入x,y的值来进行训练,这里修改了x的维度,如果x本身就是50×1维,输入model.fit(x, y)就可以
print("参数w=",model.coef_)                      #这里得到了两个参数,python已经帮我们计算好了
print("参数b=",model.intercept_)
'''到这里为止,只用会这些就够了'''

x_test = np.linspace(0, 1, 100)         #可视化部分,生成测试集
plt.plot(x_test, model.predict(x_test[:, np.newaxis]), label = "Model")         #绘制出测试集对应的y值
plt.plot(x_test, true_fun(x_test),label = "True function")                      #绘制出原函数
plt.scatter(x_train,y_train)                                                          #训练集散点图
plt.legend(loc="best")
plt.show()

3、梯度下降法

除了最小二乘法外,还可以采用梯度下降法来求解,当然,梯度下降法也不止能用于线性回归,梯度下降法采用最小化均方误差:

J(w, b) = \frac{1}{2m}\sum_{i=1}^{m}(y_i-\hat{y_i})^2

现在我们可以先假设以下w、b的初值,通常设置为0或者随机值。假设目前w,b都为0,显然,此时的J(w, b)不一定是最小值。我们现在需要改变w、b的值使J(w, b)逐渐减小,并且希望能以最快的方式。这时候就需要用到梯度。

学过高等数学的话就知道,梯度方向是函数在某点变化率最大的方向,因此只要沿着梯度方向对w、b进行更新即可,这就是梯度下降算法。梯度是一个向量,它的每个分量等于对应的偏导数,因此首先要求出J(w, b)对w、b的偏导数:

 最终可以得到:

\frac{\partial J}{\partial w}=-\frac{1}{m}\sum_{i=1}^{m}x_i(y-\hat{y_i})

 \frac{\partial J}{\partial b}=-\frac{1}{m}\sum_{i=1}^{m}(y-\hat{y_i})

梯度下将算法的更新规则为:

 w = w - \alpha \frac{\partial J}{\partial w}

 b = b - \alpha \frac{\partial J}{\partial b}

其中,α是学习率,控制每次更新的步长。这样,一直通过更新规则更新w,b,直到损失函数收敛或达到最大迭代次数即可。 

最后再说明以下学习率。学习率控制每次更新时的步长,如果学习率过大会导致参数震荡甚至发散,过小则会使收敛速度极慢。设置学习率的时候可以采用经验初始值+动态调整+实验验证的原则。

4、梯度下降法的代码求解

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression

'''生成一些随机数据
这里拟合的直线是y = 3x + 4, 引入了正态分布的噪声进行干扰
'''
np.random.seed(0)
x = 2 * np.random.rand(100, 1)
y = 4 + 3 * x + np.random.randn(100, 1)

# 初始化参数
w = 0
b = 0
learning_rate = 0.1    # 学习率
n_iterations = 1000

# 梯度下降
for i in range(n_iterations):
    y_pred = w * x + b    # 这里是每次计算出y的预测值,第一次迭代采用的是初始值w = 0, b = 0
    dw = -(2/len(x)) * np.sum(x * (y - y_pred))   # 梯度值
    db = -(2/len(x)) * np.sum(y - y_pred)
    w = w - learning_rate * dw   # 更新参数
    b = b - learning_rate * db

# 输出最终参数
print(f"斜率 (w): {w}")
print(f"截距 (b): {b}")

# 可视化的拟合结果
y_pred_manual = w * x + b
plt.scatter(x, y)
plt.plot(x, y_pred, color='green')
plt.xlabel('x')
plt.ylabel('y')
plt.title('Gradient Descent Fit')
plt.show()

代码来源各个网站(),这里多加了点注释,梯度下降手动求解和直接使用model.fit(x, y)是差不多的,所以直接使用库函数会更加简洁方便。

二、逻辑回归

1、逻辑回归原理

逻辑回归,是一种广泛应用于分类问题的统计学习方法,尽管名字中带有"回归",但它实际上是一种用于二分类或多分类问题的算法。它通过使用逻辑函数(也称为 Sigmoid 函数)将线性回归的输出y映射到 0 和 1 之间,从而预测某个事件发生的概率。

举个例子:之前提到的房价预测问题,假设我们已经拟合好了一条直线。假如我们认为,高于100w的房价为高价,低于100w的房价为低价,那么,这时候就算实现了一个二分类问题了,也就是根据房子的大小估计房子是高价还是低价。

逻辑回归通过Sigmoid 函数将结果映射到0~1之间,sigmoid函数的表达式是: 

\sigma(z) =\frac{1}{1+e^{-z}}

因此,我们只需要将线性回归的函数代入逻辑回归中,即可。此外,逻辑函数估计的是y=1的概率,逻辑回归的公式如下:

p_1=p(y=1|x)=\sigma(w^Tx) =\frac{1}{1+e^{-w^Tx}}

 p_0=p(y=0|x)=1-p(y=1|x) =\frac{e^{-w^Tx}}{1+e^{-w^Tx}}

为了简便,可以将这两个公式合并为一个公式表示:

 p(y|x) = p^y_1p^{1-y}_0

其中,y取1或0。

现在,利用最大似然估计来求出w的值。最大似然估计是概率论的知识,这里就不提最大似然估计的原理了。对函数取对数:

\hat{w}=argmax logP(Y|X)

以下是变形过程:

这样,就得到了:

 \hat{w}=argmax \sum_{i=1}^{n}[y_ilogp_1+(1-y_i)logp_0]

接下来,基于这个式子对w进行求导,有:

最终得到:

 J'(w)=\sum_{i=1}^{n}x_i(y_i-pi)

由于概率值的非线性,放在求和符号中时,这个式子无法直接求解。因此只能采用梯度下降法来获得似然函数J(w)的极大值。梯度下降的解法不再说明。

2、逻辑回归代码求解

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression

# 构造数据集
x_fearures = np.array([[-1, -2], [-2, -1], [-3, -2], [1, 3], [2, 1], [3, 2]])
y_label = np.array([0, 0, 0, 1, 1, 1])

# 训练逻辑回归模型
lr_clf = LogisticRegression()
lr_clf.fit(x_fearures, y_label)

# 查看模型参数
print('逻辑回归的权重 (w1, w2):', lr_clf.coef_)  # 特征权重
print('逻辑回归的偏置 (w0):', lr_clf.intercept_)  # 截距

# 1. 可视化原始数据
plt.figure()
plt.scatter(x_fearures[:,0], x_fearures[:,1], c=y_label, s=50, cmap='viridis')  # c指定颜色(按标签)
plt.title('原始数据集')
plt.show()

# 2. 可视化决策边界
plt.figure()
plt.scatter(x_fearures[:,0], x_fearures[:,1], c=y_label, s=50, cmap='viridis')
plt.title('逻辑回归决策边界')

# 生成网格点用于绘制决策边界
nx, ny = 200, 100
x_min, x_max = plt.xlim()  # 获取当前x轴范围
y_min, y_max = plt.ylim()  # 获取当前y轴范围
x_grid, y_grid = np.meshgrid(
    np.linspace(x_min, x_max, nx),  # x方向网格
    np.linspace(y_min, y_max, ny)   # y方向网格
)

# 预测网格点的概率(取类别1的概率)
z_proba = lr_clf.predict_proba(np.c_[x_grid.ravel(), y_grid.ravel()])  # ravel()展平网格
z_proba = z_proba[:, 1].reshape(x_grid.shape)  # 重塑为网格形状

# 绘制决策边界(概率=0.5的线)
plt.contour(x_grid, y_grid, z_proba, [0.5], linewidths=2., colors='blue')
plt.show()

# 3. 可视化新样本的预测
plt.figure()
# 新样本1
x_new1 = np.array([[0, -1]])
plt.scatter(x_new1[:,0], x_new1[:,1], c='blue', s=50)  # 手动指定颜色
plt.annotate('新样本1',  # 注释文本(修正:第一个参数就是text,无需s=)
             xy=(0,-1),  # 样本坐标
             xytext=(-2,0),  # 文本位置
             color='blue',
             arrowprops=dict(arrowstyle='-|>', connectionstyle='arc3', color='red'))

# 新样本2
x_new2 = np.array([[1, 2]])
plt.scatter(x_new2[:,0], x_new2[:,1], c='red', s=50)  # 手动指定颜色
plt.annotate('新样本2',  # 修正:移除s=参数
             xy=(1,2),
             xytext=(-1.5,2.5),
             color='red',
             arrowprops=dict(arrowstyle='-|>', connectionstyle='arc3', color='red'))

# 叠加原始数据和决策边界
plt.scatter(x_fearures[:,0], x_fearures[:,1], c=y_label, s=50, cmap='viridis', alpha=0.5)
plt.contour(x_grid, y_grid, z_proba, [0.5], linewidths=2., colors='blue')
plt.title('新样本预测')
plt.show()

 代码来自于阿里云,其实核心代码仍然只是:

lr_clf = LogisticRegression()
lr_clf.fit(x_fearures, y_label)

其他的代码在对数据进行处理,或是在做可视化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值