一、机器学习算法与实践_06迭代法和KMeans、线性回归、逻辑回归算法笔记

news2024/10/5 11:25:18

0 迭代法

迭代法不仅是机器学习、深度学习的核心,也是整个人工智能领域的重要概念,其对于算法的设计和实现至关重要

0.1 适合场景

对于不能一次搞定的问题,将其分成多步来解决,逐步逼近解决方案

0.2 典型应用

  • KMeans 聚类算法:逐步调整聚类中心,直到满足某个条件
  • 线性回归和逻辑回归:通过迭代优化算法(如梯度下降)逐步找到最佳模型参数

0.3 三个关键点

  • 随机的开始

    • 随机初始化:大多数迭代算法从一个随机点开始迭代

    • 优点:避免陷入局部最优解,增加找到全局最优解的机会(随机-->普适性)

  • 逐步变好的策略

    • 优化:每一步都尝试改进当前解,使目标函数(如损失函数)值减小(每天进步一点点)

    • 积累:通过调整参数,逐渐接近最优解(相信累积,相信长期主义)

  • 退出条件

    • 固定步数:达到预设的迭代次数时退出

    • 误差限制:当前解和最优解之间的差距小于某个阈值时退出

1 KMeans算法

KMeans(K-Means Clustering,即:K-均值聚类)是使用最多、最简单易用的聚类算法,属于无监督学习的一种

KMeans的主要目的是将数据点分成 K 个预先指定的簇,使得簇内的方差尽可能小,簇间的方差尽可能大,算法的基本思想与KNN类似(物以类聚人以群分),具体做法是迭代地移动簇中心(称为质心),直到满足某个终止条件

1.1 基本概念

  • 聚类:将数据点分成若干组,使得同一组内的数据点尽可能相似,不同组之间的数据点尽可能不同
  • 无监督学习:数据没有标签,算法需要自行发现数据的结构(没有标签,只有特征,根据业务需求和样本特征来进行分类)

1.2 KMeans的内涵

  • K:簇的数量(也就是分类数量,K个类)
  • Means:每个簇的中心(质心),是该簇所有点的均值(mean代表均值,s代表多次)

1.3 算法步骤

  • 随机初始化:选择 K 个数据点作为初始质心

  • 分配步骤:将每个数据点分配给最近的质心,形成 K 个簇

  • 更新步骤:计算每个簇的新质心

  • 迭代:重复分配步骤和更新步骤,直到满足退出条件

1.4 随机出生点的影响

  • 初始质心的选择:可能会影响最终的聚类结果
  • 不稳定因素:不同的初始质心可能导致不同的局部最优解
  • 多次运行:为了得到较好的结果,有时需要多次运行 KMeans 算法,选择最好的结果

1.5 算法实践

# 在sklearn.datasets中load_xxx是加载数据集,而make_xxx则是生成模拟数据集
# make_blobs生成模拟的数据集包含若干个分布在随机位置的簇(blobs),每个簇由中心点定义,并包含以高斯分布(正态分布)随机生成的样本点,非常适合KMeans算法的实践
# 因此,需要先引入make_blobs
from sklearn.datasets import make_blobs

# 使用make_blobs函数来生成包含特征和标签的数据集:
# (1)n_samples=1000:生成 1000 个样本点
# (2)n_features=2:每个样本点包含 2 个特征
# (3)centers=4:生成 4 个簇中心,数据将围绕这 4 个中心分布
# (4)cluster_std=0.5:设置簇中点分布的标准偏差为 0.5
# (标准偏差用来控制簇内样本点分布的离散程度,较小的标准偏差意味着样本点更紧密地围绕簇中心分布,而较大的标准偏差意味着样本点分布得更分散)
# (5)random_state=0:设置随机数生成器的种子为 0,这确保了每次生成的数据集都是一样的,保证结果的可重复性
X, y = make_blobs(n_samples=1000, n_features=2, centers=4, cluster_std=0.5, random_state=0)

# sklearn.cluster是 scikit-learn 中的一个子模块,它包含了多种用于执行聚类任务的算法,比如KMeans
# 所以这里需要对其进行引入
from sklearn.cluster import KMeans
# KMeans是一个类,所以应该用面向对象的思想对其进行使用(即:实例化对象)
# n_clusters=4,这告诉算法你想要将数据分为 4 个簇
# 此外,由于KMeams算法对于初始质心的选择是随机的,所以可以设置random_state随机数种子,确保结果的一致性
km = KMeans(n_clusters=4, random_state=0)
# 由于KMeans是无监督学习算法,所以训练时只需要传入X,通过特征来寻找标签
km.fit(X=X)

# 在训练完毕之后,可以得到两个非常关键的属性
# (1)cluster_centers_,表示质心
# (2)km.labels_,表示预测出来的簇标签
# 引入matplotlib的pyplot函数,画出质心和样本的关系图
from matplotlib import pyplot as plt
# 如果是用pycharm等后端工具绘图,需要指定图形用户界面工具包
# import matplotlib
# matplotlib.use('TkAgg')  # 设置绘图后端为 TkAgg
# 由于我们在make_blobs函数中设定了生成的数据只有两个特征,所以我们可以将第一个特征作为横坐标、第二个特征作为纵坐标,绘制对应的散点图,查看样本之间的位置关系
# 将make_blobs函数生成的标签y赋值给c,表示每个簇都有不同的颜色
plt.scatter(x=X[:, 0], y=X[:, 1], c=y)
# 由于质心也是只有两个特征,所以我们可以将第一个特征作为横坐标、第二个特征作为纵坐标,用大小为100的红色五角星来绘制对应的散点图
plt.scatter(x=km.cluster_centers_[:, 0], y=km.cluster_centers_[:, 1], c="red",marker="*",s=100)
# 用show方法显示图表
plt.show()
# 查看每个点的簇标签
print(km.labels_)

2 线性回归

2.1 定义

线性回归(Linear Regression)是一种预测连续数值的监督学习算法,它试图找到特征和目标变量之间的线性关系,并通过线性关系进行模型的训练和预测

2.2 原理

类似于我们在中学时学习的一元一次方程:y = k*x + b,线性回归旨在通过最小化误差的平方和来寻找特征和目标值之间的关系,其模型的表达式通常为:(n元一次方程)

其中: 是权重(能代表特征的重要程度), 是特征,b 是偏置项,y 是目标值

方法: 使用训练数据,通过梯度下降或其他优化算法来调整权重和偏置,来最小化损失函数(通常是均方误差)

2.3 应用场景

线性回归主要用于预测连续数值,如房价、温度、销售额等

2.4 算法实践

以波士顿房价数据集为例,分别用线性回归算法、KNN算法和决策树算法进行回归预测

# 引入pandas,提供一些数据分析的方法
import pandas as pd
# 读取波士顿房价数据集文件
data = pd.read_csv(filepath_or_buffer="./boston_house_prices.csv", skiprows=1)

# 将总的数据集划分成X特征和y标签
# 先根据Attrition_Flag字段,取出y标签
y = data["MEDV"].to_numpy()
# 再把Attrition_Flag字段删除,将剩余字段均作为X特征
X = data.drop(columns=["MEDV"]).to_numpy()

# 引入train_test_split,将X、y切分为训练集和测试集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)

# 从训练集中获取mu和sigma,然后带入到训练集和测试集这两个数据集中做标准化处理
mu = X_train.mean(axis=0)
sigma = X_train.std(axis=0)
X_train1 = (X_train - mu)  / sigma
X_test1 = (X_test - mu) / sigma

# 1、线性回归算法
# sklearn.linear_model 是 scikit-learn 库中的一个模块,它包含多种实现线性模型的类,而LinearRegression是该模块中一个用于实现线性回归算法的类,所以我们这里需要进行引入
from sklearn.linear_model import LinearRegression
# LinearRegression是一个类,所以应该用面向对象的思想对其进行使用(即:实例化对象)
lr = LinearRegression()
# 训练模型时,需要将训练集(X_train和y_train)作为参数传入fit方法中
lr.fit(X=X_train1, y=y_train)
w = lr.coef_
b = lr.intercept_
print(f"线性回归模型训练之后,得到的权重(w)为:{w},\n偏置项(b)为:{b}")
# 预测模型时,需要将测试集(X_test)作为参数传入predict方法中
y_pred1 = lr.predict(X=X_test1)
# 求预测结果的平均绝对误差
mae1 = abs(y_pred1 - y_test).mean()
# 求预测结果的平均平方误差
mse1 = ((y_pred1 - y_test) ** 2).mean()
print(f"线性回归模型预测之后,得到的平均绝对误差为{mae1},平均平方误差为{mse1}")

# 2、KNN算法
# 引入KNeighborsRegressor
from sklearn.neighbors import KNeighborsRegressor
# 实例化KNeighborsRegressor对象,并设置邻居数为5
knn = KNeighborsRegressor(n_neighbors=5)
# 模型训练
knn.fit(X=X_train1, y=y_train)
# 模型预测
y_pred2 = knn.predict(X=X_test1)
# 求预测结果的平均绝对误差
mae2 = abs(y_pred2 - y_test).mean()
# 求预测结果的平均平方误差
mse2 = ((y_pred2 - y_test) ** 2).mean()
print(f"KNN模型预测之后,得到的平均绝对误差为{mae2},平均平方误差为{mse2}")

# 3、决策树算法
# 引入DecisionTreeRegressor
from sklearn.tree import DecisionTreeRegressor
# 实例化DecisionTreeRegressor对象
dtr = DecisionTreeRegressor()
# 模型训练
dtr.fit(X=X_train1, y=y_train)
# 模型预测
y_pred3 = dtr.predict(X=X_test1)
# 求预测结果的平均绝对误差
mae3 = abs(y_pred3 - y_test).mean()
# 求预测结果的平均平方误差
mse3 = ((y_pred3 - y_test) ** 2).mean()
print(f"决策树模型预测之后,得到的平均绝对误差为{mae3},平均平方误差为{mse3}")

3 逻辑回归

3.1 定义

逻辑回归(Logistic Regression)虽然名字中有“回归”二字,但实际上它并不是一种回归算法,而是一种分类算法,它通常用于预测离散类别标签,特别是二分类问题

3.2 原理

逻辑回归使用 Sigmoid 函数将线性回归的输出映射到 0 和 1 之间,表示为属于某类的概率。模型的表达式为:

其中: 是权重, 是特征,b 是偏置项,e 是自然对数,y 是目标值

从上面公式可得:

的值越小,y 的值就越小

所以上面的公式其实还是可以化简为线性回归的公式:

方法: 使用训练数据,通过梯度下降或其他优化算法来调整权重和偏置,来最大化似然函数或最小化交叉熵损失

3.3 应用场景

逻辑回归主要用于解决二分类或多分类问题,如垃圾邮件检测、疾病诊断、图像识别等

3.4 算法实践

以《一、机器学习算法与实践_05项目实战——信用卡客户流失分析预测及PCA、SVD特征降维》中的数据集为例,用逻辑回归算法进行分类预测

# 引入joblib库,加载保存好的数据文件和模型实例文件
import joblib

_, [X_train, y_train, X_test, y_test] = joblib.load(filename="all_data.lxh")

# sklearn.linear_model是 scikit-learn 库中的一个模块,它包含多种实现线性模型的类,而LogisticRegression是该模块中一个用于实现逻辑回归算法的类,所以我们这里需要进行引入
from sklearn.linear_model import LogisticRegression
# LogisticRegression是一个类,所以应该用面向对象的思想对其进行使用(即:实例化对象)
lr = LogisticRegression()
# 训练模型时,需要将训练集(X_train和y_train)作为参数传入fit方法中
lr.fit(X=X_train, y=y_train)
# 预测模型时,需要将测试集(X_test)作为参数传入predict方法中
y_pred = lr.predict(X=X_test)

# 比较测试集的标签值和预测出来的标签值,计算预测的准确率
acc = (y_pred == y_test).mean()
print(f"预测的准确率为:{acc}")
# 计算权重和偏置项
w = lr.coef_
b = lr.intercept_
print(f"逻辑回归模型训练之后,得到的权重(w)为:{w},\n偏置项(b)为:{b}")

4 梯度下降法

4.1 基本概念

梯度下降法(Gradient Descent)是一种常用的优化算法,用于求解机器学习模型中的参数,目的是最小化损失函数(即误差函数),它广泛应用于训练线性回归、逻辑回归、神经网络等模型

4.2 基本思想

以线性回归和逻辑回归的公式为例:

评价一个模型的好坏,我们通常是需要衡量预测结果和真实结果之间的误差,使得误差最小

通常的操作步骤是:

Step1:从训练集中,取出一批样本 batch_X, batch_y

Step2:把特征 batch_X 带入模型,得到一个预测结果 y_pred(y_pred 是 w 和 b 的函数)

Step3:衡量预测结果和真实结果的误差(设误差函数为loss_fn)

  • 误差值:loss = loss_fn(y_pred, batch_y)

  • 由于loss 是 y_pred 的函数,而y_pred 又是 w 和 b 的函数,所以loss 是 w 和 b 的函数

  • 误差越大表示预测越差,误差越小表示预测越好,所以模型优化的问题就变成了函数求最小值的问题:当 w 和 b 是多少的时候,loss 可以取得最小值?

4.3 求函数最小值的方法

求一个函数的最小值,从理论数学和工程上,分别有着不同的方法

4.3.1 理论数学方法

(1)求导数/偏导

  • 对函数 F(x) 求导数,得到 F'(x)
  • 如果函数是多变量的,对每个变量求偏导数,得到 ▽F(x)

(2)令导数/偏导等于零

  • 将导数 F'(x) 或偏导数 ▽F(x) 设置为零
  • 解方程 F'(x)=0 或方程组 ▽F(x)= 0

(3)解方程/组,得到疑似结果,并进一步结果验证

  • 解这些方程,得到函数的临界点

  • 检查这些临界点是否为最小值,这通常涉及分析二阶导数或使用其他方法来确定临界点的性质(最小值、最大值或鞍点)

因为:

  • 数据量较大或函数复杂时,大量的数学求导及运算会十分繁琐

  • 样本实际上是分布在线性模型周围的,而不是正好在模型上

所以:我们一般不会采用理论数学去解决模型优化问题

4.3.2 工程方法

(1)迭代法:从函数的某个点开始,迭代地更新这个点,直到找到一个最小值

(2)随机梯度下降法:随机梯度下降法(Stochastic Gradient Descent,SGD)是一种用于优化机器学习算法的算法,特别是用于训练大规模数据集。它是梯度下降算法的一种变体,主要区别在于它是在每个训练步骤中使用单个训练样本或一小批样本来更新模型的参数,而不是整个训练集

【公式】假设损失函数为 L,参数为 θ(可以是权重 w 和偏置 b),学习率为 α,则 SGD的参数更新规则为:

其中,是损失函数在当前参数 下的梯度(也就是:函数在多维空间中每一点的方向导数)

4.4 算法流程

  • 初始化:首先随机初始化模型的参数(例如,权重和偏置)

  • 计算梯度:计算损失函数相对于每个参数的梯度(或偏导数),梯度是损失函数在当前参数值下的“斜率”,指向损失函数增加最快的方向

  • 更新参数:使用计算出的梯度和预先设定的学习率(步长)来更新每个参数,参数的更新遵循梯度的反方向,因为这是损失函数减少的方向

  • 迭代:重复步骤2和3,直到满足某个停止条件(例如:达到最大迭代次数,或损失函数下降到某个阈值以下)

4.4.1 手动求导,计算x为多少时, y=x² 达到最小值

根据数学知识,可得x²的导数为2x(常见初等函数的导数可见本文附页内容),因此计算方法如下:

# 引入numpy,提供一些科学计算的方法
import numpy as np
def fn(x):
    """
        原始函数
    """
    return x ** 2

def dfn(x):
    """
        求导函数
    """
    return 2 * x

# 设置迭代次数
steps = 1000
# 设置学习率(较大的学习率会使得算法更快地接近最小值,但也可能导致算法在最小值附近震荡或发散;较小的学习率会使得算法更慢地接近最小值,但更稳定)
# 不用numpy库和manth模块中的表示方法,直接写1e-2,代表的不是自然对数e,而是科学计数法,实际值就是0.01
learning_rate = 1e-2

# 1、随机的开始
# 从-1000到1000中,取出一个随机整数,并将其设置为一个一维数组
x = np.random.randint(low=-1000, high=1001, size=(1,))
# 2、迭代优化
for step in range(steps):
    # 梯度下降法,更新x的值
    x = x - learning_rate * dfn(x)
    print(f"优化{step+1}步后, x的值为: {x}")
print(f"迭代{steps}次之后,x的最终值为: {x}")

4.4.2 PyTorch自动求导,计算x为多少时, y=x² 达到最小值

PyTorch是目前主流的深度学习框架,提供了针对深度学习的科学计算库:

  • PyTorch基于 NumPy 扩展了更多功能,专门用于构建和训练深度学习模型(PyTorch中使用张量,NumPy中使用数组,张量和数组具备的方法和属性几乎一样)

  • 每一个张量都有3个重要的属性(data、grad、item()),其中:data表示张量的数据部分,grad表示张量的梯度部分,item()是直接取出张量的数据值

  • PyTorch支持自动求导(自动微分),方便定义梯度计算图(在 PyTorch 中,通过设置 requires_grad=True 来让张量支持进行梯度计算,计算的过程不是先求导函数,再带入变量求导数值,而是求导和带入一起完成)

  • PyTorch提供了丰富的神经网络层、优化器、激活函数等预定义模块

对于数据标准化中的标准差:

  • NumPy 默认求的是样本标准差:(sum((x - mu) ** 2) / len(x)) ** 0.5

    • import numpy as np

    • arr = np.array([1, 2, 3, 4, 5, 6])

    • 样本标准差:arr.std()

    • 总体标准差:arr.std(ddof=1)

  • PyTorch 默认求的是总体标准差(无偏估计):(sum((x - mu) ** 2) / (len(x) - 1)) ** 0.5

    • import torch

    • tensor = torch.tensor([1, 2, 3, 4, 5, 6], dtype=torch.float32)

    • 总体标准差:t.std()

    • 样本标准差:t.std(correction=0)

使用PyTorch自动求导的计算方法如下:

# 引入PyTorch,提供一些深度学习科学计算的方法
import torch
def fn(x):
    """
        原始函数
    """
    return x ** 2

# 走一个固定的步数
steps = 1000
# 设置学习率(较大的学习率会使得算法更快地接近最小值,但也可能导致算法在最小值附近震荡或发散;较小的学习率会使得算法更慢地接近最小值,但更稳定)
# 不用numpy库和manth模块中的表示方法,直接写1e-2,代表的不是自然对数e,而是科学计数法,实际值就是0.01
learning_rate = 1e-2

# 1、随机的开始
# 从-1000到1000中,取出一个随机整数,并将其设置为一个一维张量
# 设置requires_grad=True,表示x支持使用backward方法进行自动求导
x = torch.randint(low=-1000, high=1001, size=(1,), dtype=torch.float32, requires_grad=True)
# 2、迭代优化
for step in range(steps):
    # Step1:正向传播(原始函数)
    y = fn(x)
    # Step2:反向传播(自动求导,计算和存储梯度)
    y.backward()
    # Step3:梯度下降法,更新x的值
    x.data -= learning_rate * x.grad
    # Step4:清空梯度(如果不清空,则grad值会累加,这样就不是想要的效果了)
    x.grad.zero_()
    print(f"优化{step+1}步后, x的值为: {x}")
print(f"迭代{steps}次之后,x的最终值为: {x}")

4.5 算法实践

4.5.1 线性回归

以波士顿房价数据集为例,使用梯度下降法进行线性回归预测

Step1: 读取数据,使用PyTorch自动求导,结合梯度下降法计算w和b的值

# 引入pandas库,提供一些数据分析的方法
import pandas as pd
# 从csv文件中读取数据集
data = pd.read_csv(filepath_or_buffer="boston_house_prices.csv", skiprows=1)

# 通过索引,将数据集拆分为特征集和标签集
data = data.to_numpy()
X = data[:, :-1]
y = data[:, -1]

# 引入train_test_split,将数据集进一步切分为训练集和测试集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)

# 数据标准化处理
mu = X_train.mean(axis=0)
sigma = X_train.std(axis=0)
X_train = (X_train - mu) / sigma
X_test = (X_test - mu) / sigma

# 引入PyTorch,提供一些深度学习科学计算的方法
import torch
# 初始化权重和偏置
w = torch.randn(13, 1, requires_grad=True)
b = torch.randn(1, 1, requires_grad=True)

def model(X):
    """
        定义线性回归函数
    """
    return X @ w + b   

# 将数组数据转换为张量
X_train = torch.tensor(data=X_train, dtype=torch.float32)
y_train = torch.tensor(data=y_train.reshape(-1, 1), dtype=torch.float32)

# 设置迭代次数和学习率
steps = 3000
learning_rate = 1e-3

# 迭代优化
for step in range(steps):
    # 1、正向传播(原始函数)
    y_pred = model(X_train)
    # 2、计算损失(均方误差MSE)
    loss = ((y_train - y_pred) ** 2).mean()
    # 3、反向传播(自动求导,计算和存储梯度)
    loss.backward()
    # 4、梯度下降法,更新w、b的值
    w.data -= learning_rate * w.grad
    b.data -= learning_rate * b.grad
    # 5、清空梯度(如果不清空,则grad值会累加,这样就不是想要的效果了)
    w.grad.zero_()
    b.grad.zero_()
    # 每隔10次迭代,打印一次loss的值
    if step % 10 == 0:
        print(loss.item())

Step2: 定义预测方法,根据上面迭代出来的w和b,带入X_test进行预测

def predict(X_test):
    """
        定义预测方法
    """
    # 将数组数据转换为张量
    X_test = torch.tensor(data=X_test, dtype=torch.float32)

    # 在PyTorch中,当requires_grad=True的张量参与任何运算时,PyTorch都会构建一个计算图,并自动计算和存储这些张量的梯度
    # 然而,在某些情况(如模型评估或推理)下,我们并不需要计算梯度,此时即可使用 with torch.no_grad() 告诉 PyTorch ,在某个代码块内部不跟踪梯度信息,从而节省内存和计算资源
    with torch.no_grad():
        # 结合上面迭代出来的w和b,带入X_test进行预测
        y_pred = model(X_test)
    # 返回预测结果
    return y_pred

# 调用预测方法,获取预测结果
y_pred = predict(X_test)
# 将y_pred转换为一维数组,torch的view方法类似numpy的reshape方法
y_pred = y_pred.view(-1).numpy()
# 预测结果评估
mse = ((y_pred - y_test) ** 2).mean()
print(f"预测结果的均方误差(mse)值为:{mse}")

4.5.2 逻辑回归

以乳腺癌数据集为例,使用梯度下降法进行逻辑回归预测

Step1: 加载数据,使用PyTorch自动求导,结合梯度下降法计算w和b的值

# 引入load_breast_cancer,获取乳腺癌数据集
from sklearn.datasets import load_breast_cancer
X, y = load_breast_cancer(return_X_y=True)

# 引入train_test_split,将X、y切分为训练集和测试集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)

# 数据标准化处理
mu = X_train.mean(axis=0)
sigma = X_train.std(axis=0)
X_train = (X_train - mu) / sigma
X_test = (X_test - mu) / sigma

# 引入PyTorch,提供一些深度学习科学计算的方法
import torch

# 定义权重和偏置
# 30个特征、2个类别,所以w是(30,2)、b是(1,2)
w = torch.randn(30, 2, dtype=torch.float32, requires_grad=True)
b = torch.randn(1, 2, dtype=torch.float32, requires_grad=True)

def model(X):
    """
        定义逻辑回归函数
    """
    return X @ w + b

# 设置迭代次数和学习率
steps = 200
learning_rate = 1e-2

# 将数组数据转换为张量
X_train = torch.tensor(data=X_train, dtype=torch.float32)
X_test = torch.tensor(data=X_test, dtype=torch.float32)
y_train = torch.tensor(data=y_train, dtype=torch.long)
y_test = torch.tensor(data=y_test, dtype=torch.long)

def get_cross_entropy(y_pred, y_true):
    """
        每个样本的交叉熵ce = y_true @ np.log(1/y_pred)
        批量的交叉熵为每个样本交叉熵的平均值
    """
    # Step1:真实标签转 one-hot 编码
    # torch.eye(2)表示2×2的单位矩阵,y_true是0和1两种类型,中括号是切片,以此实现one-hot编码
    y_true = torch.eye(2)[y_true]
    # Step2:原始输出转概率
    # dim=1代表对每一行的各列值进行求和,keepdim=True表示求和后继续保持原始张量的维度
    # 因精度问题,可能会导致迭代多次后结果为0,所以这里加一个很小的数字(1e-9)
    y_pred = torch.exp(y_pred) / torch.exp(y_pred).sum(dim=1, keepdim=True) + 1e-9
    # Step3:求每个样本的交叉熵之和(dim=1代表对每一行的各列值进行求和)
    cross_entropy = (y_true * torch.log(1 / y_pred)).sum(dim=1)
    # Step4:求平均交叉熵
    cross_entropy = cross_entropy.mean()
    # 返回结果
    return cross_entropy

def get_acc(X, y):
    """
        计算准确率
    """
    with torch.no_grad():
        # 结合上面迭代出来的w和b,带入X进行预测
        y_pred = model(X=X)
        # 因为w,b是两组值,计算得出的y_pred分别是两个类别的概率,所以我们需要获得最大值对应的索引,代表预测出来的是哪种类型
        y_pred = y_pred.argmax(dim=1)
        # 实际值与y_pred进行对比,看有多少个数是相等的,就可以得到预测的准确率(用item方法获取acc这个张量的值)
        # pytorch不允许布尔值进行平均值计算,所以现将其转换为浮点数,再算均值
        acc = (y == y_pred).to(dtype=torch.float32).mean().item()
    return acc

def train():
    """
        训练过程
    """
    # 训练前,测试一下准确率
    train_acc = get_acc(X=X_train, y=y_train)
    test_acc = get_acc(X=X_test, y=y_test)
    print(f"开始训练之前,train_acc: {train_acc}, test_acc: {test_acc}")
    for step in range(steps):
        # 1、正向传播(原始函数)
        y_pred = model(X=X_train)
        # 2、计算损失(交叉熵)
        loss = get_cross_entropy(y_pred=y_pred, y_true=y_train)
        # 3、反向传播(自动求导,计算和存储梯度)
        loss.backward()
        # 4、梯度下降法,更新w、b的值
        w.data -= learning_rate * w.grad
        b.data -= learning_rate * b.grad
        # 5、清空梯度(如果不清空,则grad值会累加,这样就不是想要的效果了)
        w.grad.zero_()
        b.grad.zero_()
        # 6、模型评估
        train_acc = get_acc(X=X_train, y=y_train)
        test_acc = get_acc(X=X_test, y=y_test)
        print(f"训练了{step + 1}轮,train_acc: {train_acc}, test_acc: {test_acc}")

Step2: 调用训练函数,进行模型训练及准确率查看

train()

4.6 pytorch中封装好的线性模型实践

在上面4.5章节的算法实践中,为了理解梯度下降法的原理,我们自己定义了线性模型的函数,编写了求w和b的代码逻辑

但事实上,pytorch中也有已经封装好的现成方法(nn.Linear),下面来使用此方法,集合乳腺癌数据集来进行实践

Step1: 加载数据,引用nn模块

# 引入load_breast_cancer,获取乳腺癌数据集
from sklearn.datasets import load_breast_cancer
X, y = load_breast_cancer(return_X_y=True)

# 引入train_test_split,将X、y切分为训练集和测试集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)

# 数据标准化处理
mu = X_train.mean(axis=0)
sigma = X_train.std(axis=0)
X_train = (X_train - mu) / sigma
X_test = (X_test - mu) / sigma

# 引入PyTorch,提供一些深度学习科学计算的方法
import torch
# 从PyTorch中导入nn(神经网络)模块
from torch import nn
# Linear是nn模块中创建全连接线性层的类,自带w和b的属性及计算,这里我们用面向对象的思想对其进行使用(即:实例化对象)
# in_features=30, out_features=2表示输入特征有30种,输出标签为两类
model = nn.Linear(in_features=30, out_features=2)

# 设置迭代次数和学习率
steps = 200
# 优化器(可以帮助我们实现梯度下降和清空的方法)
# 将上面实例化的model参数传递给此优化器,并设置学习率lr为0.001
optimizer = torch.optim.SGD(params=model.parameters(), lr=1e-3)
# 损失函数(直接使用nn模块中计算交叉熵的CrossEntropyLoss方法)
loss_fn = nn.CrossEntropyLoss()

# 将数组数据转换为张量
X_train = torch.tensor(data=X_train, dtype=torch.float32)
X_test = torch.tensor(data=X_test, dtype=torch.float32)
y_train = torch.tensor(data=y_train, dtype=torch.long)
y_test = torch.tensor(data=y_test, dtype=torch.long)

def get_acc(X, y):
    """
        计算准确率
    """
    with torch.no_grad():
        # 结合上面迭代出来的w和b,带入X进行预测
        y_pred = model(X)
        # 因为w,b是两组值,计算得出的y_pred分别是两个类别的概率,所以我们需要获得最大值对应的索引,代表预测出来的是哪种类型
        y_pred = y_pred.argmax(dim=1)
        # 实际值与y_pred进行对比,看有多少个数是相等的,就可以得到预测的准确率(用item方法获取acc这个张量的值)
        # pytorch不允许布尔值进行平均值计算,所以现将其转换为浮点数,再算均值
        acc = (y == y_pred).to(dtype=torch.float32).mean().item()
    return acc

def train():
    """
        训练过程
    """
    # 训练前,测试一下准确率
    train_acc = get_acc(X=X_train, y=y_train)
    test_acc = get_acc(X=X_test, y=y_test)
    print(f"开始训练之前,train_acc: {train_acc}, test_acc: {test_acc}")
    for step in range(steps):
        # 1、正向传播(原始函数)
        y_pred = model(X_train)
        # 2、计算损失(交叉熵)
        loss = loss_fn(y_pred, y_train)
        # 3、反向传播(自动求导,计算和存储梯度)
        loss.backward()
        # 4、优化一步(自动计算和更新w、b)
        optimizer.step()
        # 5、清空梯度
        optimizer.zero_grad()
        # 6、模型评估
        train_acc = get_acc(X=X_train, y=y_train)
        test_acc = get_acc(X=X_test, y=y_test)
        print(f"训练了{step + 1}轮,train_acc: {train_acc}, test_acc: {test_acc}")

Step2: 调用训练函数,进行模型训练及准确率查看

train()

Step3: 保存模型权重和偏置

torch.save(obj=model.state_dict(), f="model.pt")

Step4: 加载模型权重和偏置

# 初始化模型
m = nn.Linear(in_features=30, out_features=2)
# 加载训练好的权重
# weights_only=True表示在加载模型状态字典(state dict)时仅加载模型的权重和偏置,而不加载模型的结构或其它非权重参数
m.load_state_dict(state_dict=torch.load(f="model.pt", weights_only=True))

Step5: 定义推理函数

def predict(X):
    """
        推理流程
    """
    # 类型校验:如果X不是张量,则将其转换为张量
    if not isinstance(X, torch.Tensor):
        X = torch.tensor(data=X, dtype=torch.float32)
    # 数据结构判断:如果特征不是30种,标签不是2类,则抛出异常
    if (X.size(1) != 30) or (X.ndim !=2):
        raise ValueError("输入数据有误!!!")
    # 通过模型进行预测
    y_pred = m(X)
    # w,b是两组值,计算得出的y_pred分别是两个类别的概率,所以我们需要获得最大值对应的索引,代表预测出来的是哪种类型
    y_pred = y_pred.argmax(dim=1)
    # 返回预测值
    return y_pred

Step6: 将测试集数据带入推理函数,进行结果预测

print(predict(X=X_test))

Step7: 查看实际值,比较其与预测值之间的差异

print(y_test)

5 附页

《常见初等函数的导数》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2189779.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开放式耳机哪个品牌好?适合运动的开放式蓝牙耳机分享

如今,开放式耳机的购买量呈现出持续上升的趋势,变得越来越多。而随着人们对音频设备需求的不断提升以及对舒适佩戴体验和自然聆听感受的日益追求,开放式耳机也以其独特的优势逐渐走进大众的视野,成为众多消费者的新宠。 在各大电…

中文llama3仿openai api实战

课程介绍 本次课属于【大模型本地部署应用】,基于Chinese-LLaMA-Alpaca-3(GitHub - ymcui/Chinese-LLaMA-Alpaca-3: 中文羊驼大模型三期项目 (Chinese Llama-3 LLMs) developed from Meta Llama 3)项目,介绍如何封装一个私有的兼容…

谷歌收录批量查询,谷歌收录批量查询的简单方法

谷歌收录批量查询是网站管理员和SEO优化人员常见的需求,以下提供几种简单且高效的批量查询方法: 一、使用Google Search Console(谷歌搜索控制台) 注册并验证网站: 首先,确保你已经在Google Search Conso…

常用正则匹配中国手机号码

正则表达式匹配中国的手机号码。 我可以提供一些匹配中国手机号码的常用正则表达式: 仅匹配11位数字的手机号码:^1[3456789]\d{9}$匹配以1开头的11位数字手机号码:^1\d{10}$更宽松的匹配规则,允许0开头的手机号码:^(?:0|86|+?86)?1[3-9]\d{9}$ 这些正则表达式可…

Java基础知识总结(超详细整理)

Java基础知识总结(超详细整理) Java语言的特点 1.面向对象 面向对象(OOP)就是Java语言的基础,也是Java语言的重要特性。面向对象的概念:生活中的一切事物都可以被称之为对象,生活中随处可见的事物就是一个对象&#…

CMSIS-RTOS V2封装层专题视频,一期视频将常用配置和用法梳理清楚,适用于RTX5和FreeRTOS(2024-09-28)

【前言】 本期视频就一个任务,通过ARM官方的CMSIS RTOS文档,将常用配置和用法给大家梳理清楚。 对于初次使用CMSIS-RTOS的用户来说,通过梳理官方文档,可以系统的了解各种用法,方便大家再进一步的自学或者应用&#x…

【人工智能深度学习应用】妙策API最佳实践

功能概述 在文章创作过程中,用户可以借助AI妙策来辅助创作。AI妙策主要集中在聚合热点话题榜和平台话题榜两个方面。 具体功能 话题分析 AI妙策中的话题分析可以作为创作的灵感来源,通过网页视角选题、热门视角选题、时效视角选题、新颖视角选题&…

网站建设中,如何处理多语言版本?

在全球化的今天,网站多语言版本的处理成为了一项基本要求,尤其是对于面向国际用户的企业来说。以下是一些关于网站建设中如何处理多语言版本问题的建议: 使用URL国际化:通过在URL中添加语言代码(如example.com/en/表示…

【文心智能体】猫咪用品购物指南搭建教程

前言 随着科技的飞速发展和人们生活水平的提高,越来越多的家庭开始养宠物,其中猫咪因其可爱、独立和温顺的性格而备受青睐。然而,面对市场上琳琅满目的猫咪用品,如何为自家的“喵星人”挑选最合适的商品,成为了许多宠…

vue3学习:axios输入城市名称查询该城市天气

说来惭愧,接触前端也有很长一段时间了,最近才学习axios与后端的交互。今天学习了一个查询城市天气的案例,只需输入城市名称,点击“查询”按钮便可以进行查询。运行效果如下: 案例只实现了基本的查询功能,没…

51单片机的金属探测器【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块金属检测传感器继电器LED、蜂鸣器等模块构成。适用于金属探测仪、检测金属、剔除金属等相似项目。 可实现功能: 1、LCD1602实时显示是否检测到金属 2、金属检测传感器(按键模拟)检测是否有…

利用 Python 爬虫采集 1688商品详情

1688是中国的一个大型B2B电子商务平台,主要用于批发和采购各种商品。对于需要从1688上获取商品详情数据、工程数据或店铺数据的用户来说,可以采用以下几种常见的方法: 官方API接口:如果1688提供了官方的API接口,那么可…

Java | Leetcode Java题解之第456题132模式

题目&#xff1a; 题解&#xff1a; class Solution {public boolean find132pattern(int[] nums) {int n nums.length;List<Integer> candidateI new ArrayList<Integer>();candidateI.add(nums[0]);List<Integer> candidateJ new ArrayList<Integer…

【华为HCIP实战课程四】OSPF邻居关系排错时间和区域问题,网络工程师

一、OSPF邻居关系排错 1、MA网络(默认的以太网、FR和ATM)要求掩码一致 2、相邻OSPF设备RID相同不能建立邻居-----上节已经详细演示说明 3、同一链路必须相同区域 4、Hello和死亡时间匹配 5、MTU检测 6、认证 7、Flag位的一致性 8、华为设备上网络类型不一致 二、同一…

红日靶场1学习笔记

一、准备工作 1、靶场搭建 靶场地址 靶场描述 靶场拓扑图 其他相关靶场搭建详情见靶场地址相关说明 2、靶场相关主机信息 后续打靶场的过程中&#xff0c;如果不是短时间内完成&#xff0c;可能ip会有变化 主机ip密码角色win7192.168.122.131hongrisec2019!边界服务器win…

华为OD机试 - 垃圾短信识别(Python/JS/C/C++ 2024 E卷 100分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

8645 归并排序(非递归算法)

### 思路 非递归归并排序通过逐步合并相邻的子数组来实现排序。每次合并后输出当前排序结果。 ### 伪代码 1. 读取输入的待排序关键字个数n。 2. 读取n个待排序关键字并存储在数组中。 3. 对数组进行非递归归并排序&#xff1a; - 初始化子数组的大小curr_size为1。 - 逐…

手机使用技巧:8 个 Android 锁屏移除工具 [解锁 Android]

有时候&#xff0c;您会被锁定在自己的 Android 设备之外&#xff0c;而且似乎不可能重新进入。 一个例子就是你买了一部二手手机&#xff0c;后来发现无法使用。另一种情况是你忘记了屏幕锁定密码和用于验证密码的 Google 帐户凭据。这种情况很少见&#xff0c;但确实会发生&…

vite学习教程01、vite构建vue2

文章目录 前言一、vite初始化项目二、修改配置文件2.1、修改main.js文件2.2、修改App.vue文件2.3、修改helloworld.vue2.4、修改vite.conf.js2.5、修改vue版本--修改package.json文件 三、安装vue2和vite插件四、启动服务资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝3W&…

网站建设中,静态网页和动态网页分别是什么,有什么区别

在现代网页设计和开发中&#xff0c;理解静态网页与动态网页的区别是至关重要的。 静态网页 定义&#xff1a; 静态网页是指内容固定不变&#xff0c;用户访问时服务器直接返回存储在服务器上的HTML文件。这些页面通常以.html、.htm等扩展名结尾&#xff0c;不包含特殊符号如“…