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提供了丰富的神经网络层、优化器、激活函数等预定义模块
使用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 附页
5.1 常见初等函数的导数
5.2 总体方差和样本方差
人工智能(AI)通常被认为是“以小博大”的过程,与统计学项目类似,由于总体可能非常大或难以直接访问,研究者通常无法直接研究总体,因此,一般会采取抽样的方法,从总体中选取一部分样本进行研究,使用样本来估计总体
(1)对总体的统计
- 均值:mu = sum(x) / len(x)
- 方差:sum((x - mu) ** 2) / len(x)
- 标准差:(sum((x - mu) ** 2) / len(x)) ** 0.5
(2)对样本的统计(无偏估计,n-1的自由度)
- 均值:mu = sum(x) / len(x)
- 方差:sum((x - mu) ** 2) / (len(x) - 1)
- 标准差:(sum((x - mu) ** 2) / (len(x) - 1)) ** 0.5
(3)NumPy和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)
-