写在前面
写本系列(自用)的目的是回顾已经学过的知识、记录新学习的知识或是记录心得理解,方便自己以后快速复习,减少遗忘。概念部分大部分来自于机器学习菜鸟教程,公式部分也会参考机器学习书籍、阿里云天池。机器学习如果只啃概念始终学不牢,因此我打算概念与代码结合。
Part 2 机器学习算法
一、线性回归
先看一个引例,假如有人给了你一份清单,上面写了不同大小的房子售出的价格,同时,还告诉了你他的房子大小,问你能否告诉他他的房子大概能卖多少。
我们将房子大小设为,房子价格设为y,画出散点图,可以看出散点大致能估计为一条直线,只要能求出这条直线,就能够根据新给出的x值,也就是房子大小,估计出y值,房子售价,这就是一个简单的线性回归问题。

线性回归 (Linear Regression) 是一种用于预测连续值的最基本的机器学习算法,它假设目标变量 y 和特征变量 x 之间存在线性关系,并试图找到一条最佳拟合直线来描述这种关系。拟合直线为
其中,为预测值,x为特征变量,w为权重(斜率),b为截距。
线性回归的目标是找到最佳的w、b使得预测值与真实值y之间的误差最小。
当然,我们知道,影响房子价格的因素不一定只有房子大小一个,可能还会有地理位置、朝向、装修等等,因此一维的w并不能解决所有线性回归问题,我们可以把维度扩展到高维:
设X为n×p维向量,其中的每个x都是p×1的列向量,Y为n×1维向量,其中的每个y都是标量。w为p×1维向量,这里为了方便我们将常数值b看作,这样只需要为X增添一维
=1,就可以保证x和w的维度可以相乘。那么,此时的拟合直线可以写做:
1、最小二乘法
最小二乘法是一种常用的求解线性回归的方法,它通过使残差平方和(RSS)最小来得到最佳的w。这也可看作一个优化问题。
其中,y是实际值,为预测值,i表示第i组数据。
为了得到最佳的w值,将以上残差平方看作自变量为w的函数,函数为:
现在对这个函数进行化简,化简过程如图所示:

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

至此,就求出了w的值:
关于矩阵求导,详细的可以在b站搜索矩阵求导相关视频,这里只列几个常见结论,推导过程b站上也有,最小二乘法的推导过程均来自于b站upshuhuai008的机器学习白板推导系列,很多推导过程都会参考大佬的视频,理论部分来自于各种资料。下面是矩阵求导公式:
1、设a为标量,则标量a对向量x求导为0。
2、设A为n维列向量,则
3、
4、
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、梯度下降法
除了最小二乘法外,还可以采用梯度下降法来求解,当然,梯度下降法也不止能用于线性回归,梯度下降法采用最小化均方误差:
现在我们可以先假设以下w、b的初值,通常设置为0或者随机值。假设目前w,b都为0,显然,此时的J(w, b)不一定是最小值。我们现在需要改变w、b的值使J(w, b)逐渐减小,并且希望能以最快的方式。这时候就需要用到梯度。
学过高等数学的话就知道,梯度方向是函数在某点变化率最大的方向,因此只要沿着梯度方向对w、b进行更新即可,这就是梯度下降算法。梯度是一个向量,它的每个分量等于对应的偏导数,因此首先要求出J(w, b)对w、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函数的表达式是:
因此,我们只需要将线性回归的函数代入逻辑回归中,即可。此外,逻辑函数估计的是y=1的概率,逻辑回归的公式如下:
为了简便,可以将这两个公式合并为一个公式表示:
其中,y取1或0。
现在,利用最大似然估计来求出w的值。最大似然估计是概率论的知识,这里就不提最大似然估计的原理了。对函数取对数:
以下是变形过程:

这样,就得到了:
接下来,基于这个式子对w进行求导,有:

最终得到:
由于概率值的非线性,放在求和符号中时,这个式子无法直接求解。因此只能采用梯度下降法来获得似然函数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)
其他的代码在对数据进行处理,或是在做可视化
7万+

被折叠的 条评论
为什么被折叠?



