Pytorch

发布于:2024-06-30 ⋅ 阅读:(127) ⋅ 点赞:(0)

Pytorch

1. 基础

1.1 名词

  • 特征

    • 特征是数据中的各个属性或变量,它们用于描述数据的不同方面。在机器学习中,每个特征都是一个输入变量,可以是数值型、类别型、文本型等。
    • 比如:对于一个房价预测模型,特征可能包括房屋面积、房间数量、地理位置等。
  • 纬度

    • 纬度是指数据集中特征的数量。每个特征代表一个维度。
    • 比如:一个具有3个特征的数据集可以被视为在3维空间中,每个数据点都有3个坐标(即3个特征)。
  • 纬度诅咒

    • 特征向量很多的数据,会引发纬度诅咒。在高纬度空间中,会导致数据稀疏、计算复杂度增加、模型过拟合等。

    • 例如,假设我们有1000个特征,数据在1000维空间中会非常稀疏,距离度量也变得不再有意义。

    • 在高维空间中数据分析和建模面临的一系列问题和挑战。随着数据维度的增加,数据在高维空间中的稀疏性也增加,导致许多传统方法在高维空间中表现不佳。

      根本问题:数据量(有特征的)少,导致在高纬空间稀疏性增加。

  • 特征选择和降维

    • 可以通过特征选择(去除不相关或冗余的特征)和降维技术(如主成分分析PCA)来降低纬度。

      案例

      数据集:鸢尾花数据集(Iris Dataset)

      • 特征:花萼长度、花萼宽度、花瓣长度、花瓣宽度。
      • 纬度:4(因为有4个特征)。

      在这个数据集中,每朵花的数据点位于一个4维空间中。假设我们使用这些特征来训练一个分类模型来区分不同种类的鸢尾花:

      • 如果我们引入更多特征(如花的颜色、花朵数量等),纬度将增加。
      • 通过特征选择,我们可以选择对分类效果影响较大的特征来简化模型。
      • 如果特征数量过多,可能需要使用降维技术来减少纬度,同时保留数据的主要信息。
  • 向量和坐标

    在向量空间中,每个数据点可以被表示为一个向量。

    一个向量的每一个分量对应数据点的一个特征。例如:

1.2 人工智能系统的演变

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. Rule-based systems(基于规则的系统):基于规则,手动设计多个规则,输入可以根据规则来输出。

    基于规则的系统是最早的人工智能方法之一,这种系统通过人工编写的规则来处理输入并生成输出。

    工作流程

    1. 输入:系统接收输入数据。
    2. 手工设计的程序:专家编写的规则程序处理输入。
    3. 输出:生成结果。

    特点

    • 依赖专家知识:规则由领域专家编写。
    • 缺乏适应性:难以应对复杂、多变的任务。
    • 可解释性强:由于规则是手工编写的,系统的决策过程容易理解。

    应用场景

    • 简单决策系统:如税务计算、简单的诊断系统。
  2. Classic machine learning(经典机器学习):手动从输入中提取特征(最终变成一个向量 ),把输入和输出建立一个映射函数,f(x)=y的公式,最后输出

    经典机器学习方法通过使用预定义的特征和算法从数据中学习模型。

    工作流程

    1. 输入:系统接收输入数据。
    2. 手工设计的特征:专家从数据中提取重要特征(feature engineering)。
    3. 特征映射:使用机器学习算法(如线性回归、决策树等)对特征进行映射,从而生成输出。
    4. 输出:生成结果。

    特点

    • 特征工程:特征的选择和设计对模型性能影响很大。
    • 依赖领域知识:特征设计需要专家知识。
    • 灵活性较强:可应用于各种类型的数据和任务。

    应用场景

    • 图像识别、文本分类、预测分析等。
  3. Representation learning(表示学习):把非结构化的输入,通过复杂的算法,提取出特征(向量)。然后根据映射函数得到输出。

    通过自动学习特征表示,将输入映射到输出。

    输入:系统接收输入数据。

    初始特征提取:从输入数据中提取初始特征,这些特征可能是手工设计的,也可能是从数据中自动学习得到的。

    特征转换:通过不同的方法(如降维、聚类、编码等)将初始特征转换为新的特征表示。

    特征映射:使用这些新的特征表示进行映射,生成输出。

    输出:生成结果。

  4. Depping Learning(深度学习):表征学习的一种实现方式,通过多层神经网络来学习数据的多层次表示,具有强大的特征提取和任务优化能力。

    1. 输入:系统接收输入数据。
    2. 简单特征:初始层提取简单特征。
    3. 更多抽象特征的附加层:通过多层神经网络提取 更加抽象和复杂的特征。
    4. 特征映射:最终使用这些抽象特征进行映射,生成输出。
    5. 输出:生成结果。

1.3 反向传播

实际上就是求偏导数

用计算图表示,每一步都是原子计算。

从下到上求e,是前推的过程。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2. 线型模型

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 有一些数据集。

  2. 先用线性模型,y=wx+b。权重w。

  3. 随机一个权重,得到一个线型模型。

  4. 根据随机的线型模型,用输入获取输出y’。计算真实输出和y’的距离。

    也就是说,通过某个方法找到当前模型和真实数据之间的误差。 也就是Loss

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    计算均方差 。w=3时

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    w=2时。

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

注意:

对所有的样本:平均平方误差Mean Sequare ErrorMSE

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

import numpy as np
import matplotlib.pyplot as plt

# 数据样本
x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]


# 线型模型
def forward(x):
    return x * w


# 损失函数
def loss(x, y):
    y_pred = forward(x)
    return (y_pred - y) * (y_pred - y)


# 预测的权重w,作为图的x轴
w_list = []
# 每个权重w对应的损失值,作为图的y轴
mse_list = []

# 在某个范围内,穷举权重w
for w in np.arange(0.0, 4.1, 0.1):
    print('w=', w)
    l_sum = 0
    # 遍历每个样本,计算损失值
    for x_val, y_val in zip(x_data, y_data):
        # 预测值
        y_pred_val = forward(x_val)
        # 损失值
        loss_val = loss(x_val, y_val)
        # 累加损失值
        l_sum += loss_val
        print(f"\t{x_val} {y_val} {y_pred_val} {loss_val}")
    #     三个样本的平均损失值
    print(f"MSE = {l_sum / 3}")
    w_list.append(w)
    mse_list.append(l_sum / 3)

plt.plot(w_list, mse_list)
plt.ylabel('Loss')
plt.xlabel('w')
plt.show()

加上常数:

import numpy as np
import matplotlib.pyplot as plt

x_data = [1.0, 2.0, 3.0]
y_data = [3.0, 5.0, 7.0]


def forward(x):
    return x * w + b

def loss(x, y):
    y_pred = forward(x)
    return (y_pred - y) * (y_pred - y)


w_list = []
b_list = []
mse_list = []
for w in np.arange(0.0, 4.1, 0.1):
    for b in np.arange(0.0, 4.1, 0.1):
        print('w', w, 'b', b)
        l_sum = 0
        for x_val, y_val in zip(x_data, y_data):
            y_pred_val = forward(x_val)
            loss_val = loss(x_val, y_val)
            l_sum += loss_val
            print('\t', x_val, y_val, y_pred_val, loss_val)
        print('MSE=', l_sum / 3)
        w_list.append(w)
        b_list.append(b)
        mse_list.append((l_sum) / 3)

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# 绘制线框图
ax.plot(w_list, b_list, mse_list, 'o-')
print(w_list)
# 设置标签
ax.set_xlabel('w')
ax.set_ylabel('b')
ax.set_zlabel('Loss')
# 显示图形
plt.show()

3. 梯度下降

3.1 意义

从线型模型,大概了解到了数据集、模型、Loss之间的关系。

那么模型的权重w,怎么快速求得,是一个问题。根本问题:如何最小化损失函数

假设Loss是光滑凹凸曲线,在这个曲线上,对某个w求偏导,可以知道当前w的方向。(导数>0,增函数;导数<0,减函数),那么沿着这个方向不断的测试w,可以得到最小Loss的w。(理想状态下)

事实上,Loss曲线是不光滑的非凸函数,只能找到局部最优。

  • cost(cost function,损失函数)
  • 学习率:相当于w移动的距离
  • 每次迭代,都向着下降最快的方向走,典型的贪心。这是局部最优结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 鞍点:不能继续迭代的点(梯度是0 ,走不动了),在一个水平线 或 从一个面上看是最低从另一个面看最高
    • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.2 公式

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

import numpy as np
import matplotlib.pyplot as plt

x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]

w = 1
# 学习率
rate = 0.01


def forward(x):
    return x * w


# 损失函数
def cost(xs, ys):
    cost = 0
    for x, y in zip(xs, ys):
        y_pred = forward(x)
        cost += (y_pred - y) ** 2
    return cost / len(xs)


# 求梯度
def gradient(xs, ys):
    grad = 0
    for x, y in zip(xs, ys):
        grad += 2 * x * (x * w - y)
    return grad / len(xs)


l_list = []

for epoch in range(100):
    cost_val = cost(x_data, y_data)
    grad_val = gradient(x_data, y_data)
    w = w - grad_val * rate
    print('Epoch:', epoch, 'w=', w, 'loss=', cost_val)
    l_list.append(cost_val)

plt.plot(range(100), l_list)
plt.show()

image-20240629214637378

3.3 随机梯度下降 Stochastic

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 梯度下降:cost,用平均损失作为梯度下降更新的依据。比如N个样本,就需要计算N样本的损失
  • 随机梯度下降:从N个样本中随机选一个,就成了拿单个样本的损失作为梯度下降的更新依据。

随机梯度下降的好处

加入在鞍点的情况下。使用cost会停止不同。但是如果只用一个样本,样本都是有噪声的,也就有了一个随机噪声,这个噪声可能就会帮助向前推动,有可能就会跳过鞍点,找到最优值

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

全部样本都计算损失(样本独立算损失,不是想之前那个求平均)

import numpy as np
import matplotlib.pyplot as plt

x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]

w = 1
# 学习率
rate = 0.01


def forward(x):
    return x * w


# 损失函数
def loss(x, y):
    return (forward(x) - y) ** 2


# 求梯度
def gradient(x, y):
    return 2 * x * (x * w - y)


l_list = []
for epoch in range(100):
    for x, y in zip(x_data, y_data):
        grad_val = gradient(x, y)
        w = w - grad_val * rate
        print('grad:', x, y, grad_val)
        loss_val = loss(x, y)
    print("Epoch: ", epoch, "w=", w, "loss=", loss_val)
    l_list.append(loss_val)

# 因为每个样本都做了一次,所以是100*3
plt.plot(range(100), l_list)
plt.show()

存在的问题:

  • 梯度下降:cost一次计算时,样本之间的损失没有关联(最后是损失的和),可以并行计算。
  • 随机梯度下降:样本之间的损失是有关系的,因为当前样本计算的权重w,依赖上一个样本的w。这时的计算是不能并行的

梯度下降:效率高、性能低

随机梯度下降:效率低、性能高(比梯度下降拿到的最优值更好)

3.4 Mini-Batch

小批量梯度下降

一种常用的梯度下降优化算法,它结合了批量梯度下降(Batch Gradient Descent)和随机梯度下降(Stochastic Gradient Descent, SGD)的优点,提供了一种在速度和稳定性之间折中的方法。

每次不对全部的N个点求损失的均值,而对某些点M(M<N)

注意:这里老师【刘二大人】和其他博主讲的是相反,其他博主是说梯度下降(即批量梯度下降)效率最慢,结果好。随机梯度下降是从N个点中选择一个点,效率最好,结果差(实际上刘二老师也说的是选一个,但是代码中,实际上还是都选了。。)。

这里等之后有时间或其他机会,再研究。

5. 反向传播

  1. 拿之前线性方程的举例。现在的XN纬矩阵,WM✖️N纬矩阵,H=W*X,是M纬矩阵。

  2. H就是中间结果(隐藏层),H也是下一层的输入X’,再下一层H’=W’ * X’,最后得到loss。这是一个前推的过程

  3. 得到loss之后,就需要求梯度、更新参数。

  4. 最终需要求的梯度是:loss对w对偏导(对于中间产生的H即X’, 也需要求loss对X’的偏导,因为他是中间结果,他的上一层,需要这个结果)

  5. loss对W对偏导,可以链式拆分:loss对H的偏导*H对W对偏导。

  6. 就这样逐层计算。当前层的链式计算表达式,其中可以用到后一层的结果,这是反向传播的过程

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

解决了下面的问题:

梯度计算:

  • 问题: 神经网络中包含多个隐藏层,每一层都有大量的参数(权重和偏置)。要调整这些参数使得网络的输出与期望输出尽可能接近,需要计算损失函数关于每个参数的梯度。参数x是一个N纬矩阵,如果按照一纬线型的方式求loss、梯度下降,那么计算量很庞大。
  • 解决: 反向传播算法使用链式法则(链式求导法则)来高效地计算损失函数关于每个参数的梯度。这些梯度用于更新参数,使得损失函数的值在训练过程中逐渐减小。

权重更新:

  • 问题: 一旦计算了梯度,需要使用这些梯度来更新网络中的权重,以减少预测误差。

效率问题:

  • 问题: 直接计算每个参数的梯度在深度神经网络中可能会非常耗时且复杂,特别是当网络层数较多时。
  • 解决: 反向传播通过逐层计算梯度,利用前向传播时保存的中间结果,可以有效地减少计算量。具体来说,通过逐层计算误差的传播,从输出层到输入层反向传播误差,使得梯度计算更加高效。

反向传播的核心步骤

  1. 前向传播:
  • 输入数据通过网络,从输入层经过各个隐藏层,到达输出层,计算出网络的预测值。
  1. 损失计算:
  • 根据网络的预测值和真实值,计算损失函数值。
  1. 反向传播:
  • 从输出层开始,逐层计算损失函数关于每个参数的梯度,反向传播到输入层。
  1. 权重更新:
  • 使用梯度和优化算法(如梯度下降),更新每个参数的值。

前向传播目的是计算损失,反向传播目的是计算梯度来更新参数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

5. Pytorch

Pytorch基础:

  • Tensor存放数据,可以是标量、向量、矩阵、高阶矩阵。就说任何类似的数值都行
  • w是Tensor(张量类型),Tensor中包含data和grad,data和grad也是Tensor。grad初始为None,调用l.backward()方法后w.grad为Tensor,故更新w.data时需使用w.grad.data
  • 如果w需要计算梯度,那构建的计算图中,跟w相关的tensor都默认需要计算梯度。
import os
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"

import torch
a = torch.tensor([1.0])
a.requires_grad = True # 或者 a.requires_grad_()
print(a)
print(a.data)
print(a.type())             # a的类型是tensor
print(a.data.type())        # a.data的类型是tensor
print(a.grad)
print(type(a.grad))
  • 反向传播主要体现在,l.backward()。调用该方法后w.grad由None更新为Tensor类型,且w.grad.data的值用于后续w.data的更新。
  • l.backward()会把计算图中所有需要梯度(grad)的地方都会求出来,然后把梯度都存在对应的待求的参数中,最终计算图被释放。
  • 取tensor中的data是不会构建计算图的。
import os
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
import matplotlib.pyplot as plt
import torch

x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]

w = torch.tensor([0.1])
# 需要计算梯度
w.requires_grad = True


# 注意这里的w是一个tensor,所以x也要是一个tensor(如果不是,那么会自动转换类型)
# 并且,这不是一个乘法,而代表一个计算图
def forward(x):
    return x * w


# 这也是一个计算图
def loss(x, y):
    y_pred = forward(x)
    return (y_pred - y) ** 2


l_list = []
for epoch in range(100):
    for x, y in zip(x_data, y_data):
        l = loss(x, y)
        # 反向传播。backward自动根据计算图,把所有需要梯度的地方都求出来。梯度会存到w中
        # 执行完毕,计算图会被释放。下次loss会创建一个新的计算图。
        l.backward()
        print('\tgrad:', x, y, w.grad.item())
        # 这里我们需要的是值,所以用data。如果用w,那么会得到一个tensor,而不是一个值
        w.data = w.data - 0.01 * w.grad.data
        w.grad.data.zero_()


    print("Epoch: ", epoch, "w=", w, "loss=", l.data)
    l_list.append(l.data)

# 因为每个样本都做了一次,所以是100*3
plt.plot(range(100), l_list)
plt.show()


网站公告

今日签到

点亮在社区的每一天
去签到