【课程总结】Day7:深度学习概述

2024-06-12 02:04

本文主要是介绍【课程总结】Day7:深度学习概述,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

本篇文章,我们将通过示例来逐步学习理解导数、求函数最小值、深度学习的本质、以及使用numpy和pytorch实操深度学习训练过程。

线性回归

线性回归内容回顾

在《【课程总结】Day5(下):PCA降维、SVD分解、聚类算法和集成学习》中,我们已经了解到线性回归以及线性回归可以表示为:

y = f ( x ) = x 1 w 1 + x 2 w 2 + . . . + x 13 w 13 + b y=f(x)=x_1w_1 + x_2w_2 + ... + x_{13}w_{13} + b y=f(x)=x1w1+x2w2+...+x13w13+b

其中:

  • ( x*1, x2, …, x_{13} ):输入特征向量 ( x ) 的各个特征值,代表输入数据的各个特征。*
  • ( w_1, w2, …, w{13} ):权重向量 ( w ) 的各个权重值,用来衡量每个特征对输出的影响程度。
  • ( b ):偏置项,也称为截距项,用来调整模型的输出值,即在没有特征输入时的输出值。
  • ( y ):模型的输出值,即线性回归模型对输入特征的预测值。

该公式也可以表示为内积相乘的方式,如下:

y = f ( x ) = x @ w + b y=f(x)=x@w+b y=f(x)=x@w+b

其中:

x@w:特征向量 ( x ) 与 权重向量( w ) 的内积

如果有多个样本的话,那么上面的公示可以进一步表示为:

y = f ( X ) = X @ w + b y=f(X)=X@w+b y=f(X)=X@w+b

其中:

X代表特征矩阵,矩阵的行为一条一条的样本,矩阵的列为多个特征向量。

线性回归方程的解析

  • 在训练时,xy是训练集中的特征和标签,看作是常量w和b是待优化的参数值,看作是变量
  • 在推理时,wb已经找到了比较合适的值固定下来,看作常量;此时x是待预测的样本的特征,是变量
  • 预测的本质:把x带入,求解y。

线性回归=求损失loss函数的最小值

训练过程

由上图可知,训练的大致过程是:

  1. 从训练集中取出一对x 和y
  2. 把x带入模型,求解预测结果y_pred
  3. 找到一种方法,度量y和y_pred的误差loss
  4. 由此推导:
    • loss是y和y_pred的函数;
    • y_pred是模型预测的结果,是w和b的函数;
    • 所以简单来说,loss也是w和b的函数
训练的本质

由上图推导结果可知,训练的本质:求解loss什么时候是最小值。

数学表达:当w和b取得什么值的时候,loss最小

通俗表达:求loss函数的最小值

如何求函数的最小值?

一个例子

y = 2 x 2 y = 2x^2 y=2x2

上述这个示例中,求y最小值是比较简单的,从图形中可以看到x=0时,y=0为最小值。但是实际工程中,并不是所有的函数y=f(x)都能画出来,简单地找到最小值,此时就需要使用导数求最小值。

如果你和我一样忘了导数相关的知识,可以查看《【重拾数学知识】导数、极值和最值》回顾一下。

求解方法(理论方法)

通过回归导数求极值的方法,我们知道大致步骤如下:

  • 第一步:求函数的导数
  • 第二步:令导数等于零
  • 第三步:解方程,求出疑似极值点
  • 第四步:验证该点是否是极值点以及是什么极值点

求解的问题

上述的方法是有一定前提条件的,即:

  • 第一步的求(偏)导数是可以求得的;
  • 第三步(偏)导数为零后,方程(组)是可以解的。

实际工程中,上述方法是不可行的。以Llama3-8B模型为例,其有80亿个输入参数x,按照上述的求解方法是无法求得最小值的!

由此可知,通过推导公式期望一次性求得最小值是不现实的;而我们可以借鉴人工智能中一个重要的思想:迭代法来逐步求解最小值。

求解方法(迭代法)

仍然以 y = 2 x 2 y = 2x^2 y=2x2为例,我们可以通过以下方法求得最小值。

随机选择一个出生点 x 0 x_0 x0

  • x 0 x_0 x0在最小值的左侧时, x 0 x_0 x0 + 正数(一个非常小的正数)向右侧移动;
  • x 0 x_0 x0在最小值的右侧时, x 0 x_0 x0 - 正数(一个非常小的正数)向左侧移动;
  • x 0 x_0 x0在最小值的时候,不用移动,此时就是最小值。

由导数的单调性可知:

  • x 0 x_0 x0在左侧时,由于函数是单调递减的,所以导数<0
  • x 0 x_0 x0在右侧时,由于函数是单调递增的,所以导数>0

因此上述的计算方法可以推导得到:

  • x 0 x_0 x0在0的左侧时, x 0 x_0 x0 + 正数 → x 0 x_0 x0 + 导数 → x 0 x_0 x0 - 导数

    因为导数<0,加上一个小于的导数相当于减去导数

  • x 0 x_0 x0在0的右侧时, x 0 x_0 x0 - 正数 → x 0 x_0 x0 - 导数

    因为导数>0,减去一个大于的导数相当于减去导数

  • x 0 x_0 x0=0时,也可以看作是 x 0 x_0 x0 - 导数

由此,我们可以得到结论:不管$$x_0$$在何处,求最小值时减去导数即向极值逼近。

概念补充
  • 在一元函数中,求函数f(x)在某一点的斜率为导数;在多元函数中,称为偏导数,也就是梯度。
  • 减去导数也就是减去梯度,这就是梯度下降法!

备注:深度学习在兴起之前,人工智能只能靠支持向量机撑门面;伴随着互联网+GPU芯片的兴起,梯度下降法拥有了使用的土壤,以此人工智能才真正兴起。

代码实现(手动求函数最小值)

y = 2 x 2 y = 2x^2 y=2x2为例

import numpy as npdef fn(x):"""原始函数"""return 2 * x ** 2def dfn(x):"""导函数"""return 4 * xdef gradient_descent(x0, learning_rate, dfn, epochs):"""使用梯度下降法求函数的最小值Parameters:x0 (float): 初始点的位置learning_rate (float): 学习率dfn (function): 导函数epochs (int): 迭代次数Returns:x_min (float): 最小值点的位置"""for _ in range(epochs):x0 = x0 - learning_rate * dfn(x0)return x0# 随机选择一个出生点
x0 = np.random.randint(low=-1000, high=1000, size=1)# 迭代次数
epochs = 1000# 学习率
learning_rate = 1e-2# 使用梯度下降法求最小值
x_min = gradient_descent(x0, learning_rate, dfn, epochs)# 输出最小值
print("最小值点的位置:", x_min)

运行结果:

f ( x , y , z ) = x 2 + y 2 + z 2 f(x,y,z) = x^2 + y^2 + z^2 f(x,y,z)=x2+y2+z2为例

import numpy as npdef df_x(x, y, z):"""f 对 x 求偏导"""return 2 * xdef df_y(x, y, z):"""f 对 y 求偏导"""return 2 * ydef df_z(x, y, z):"""f 对 z 求偏导"""return 2 * z# 随机选择出生点
x0 = np.random.randint(low=-1000, high=1000, size=(1,))
y0 = np.random.randint(low=-1000, high=1000, size=(1,))
z0 = np.random.randint(low=-1000, high=1000, size=(1,))# 迭代次数
epochs = 1000# 学习率
learning_rate = 1e-2for _ in range(epochs):# 求解每个变量的偏导fx = df_x(x0, y0, z0)fy = df_y(x0, y0, z0)fz = df_z(x0, y0, z0)# 每个变量都减去自己的偏导x0 = x0 - learning_rate * fxy0 = y0 - learning_rate * fyz0 = z0 - learning_rate * fz# 输出更新后的变量值
print("更新后的 x 值:", x0)
print("更新后的 y 值:", y0)
print("更新后的 z 值:", z0)

运行结果:

代码实现(使用pytorch求函数最小值)

上述通过求导数得到函数最小值的方法,也可以通过pytorch来实现,具体代码如下:

y = 2 x 2 y = 2x^2 y=2x2为例

import torch# 定义原始函数和导函数
def fn(x):return 2 * x ** 2# 说明:pytorch可以通过grad函数求导,所以可以省去写导函数
# def dfn(x):
#     return 4 * x# 随机选择出生点
# requires_grad=True用来告诉框架该变量是一个张量,需要计算梯度。
x0 = torch.randint(low=-1000, high=1001, size=(1,), dtype=torch.float32, requires_grad=True)# 迭代次数
epochs = 1000# 学习率
learning_rate = 1e-2# 使用 PyTorch 进行梯度下降
for _ in range(epochs):# 正向传播计算损失loss = fn(x0)# 反向传播计算梯度loss.backward()# 获取梯度并更新参数with torch.no_grad():grad = x0.gradx0 -= learning_rate * grad# 梯度清零x0.grad.zero_()# 输出最小值点的位置
print("最小值点的位置:", x0.item())

运行结果:

f ( x , y , z ) = x 2 + y 2 + z 2 f(x,y,z) = x^2 + y^2 + z^2 f(x,y,z)=x2+y2+z2为例

import torchdef fn(x, y, z):"""函数定义"""return x**2 + y**2 + z**2# 说明:pytorch可以通过grad函数求导,所以可以省去写导函数
# def df_x(x, y, z):
#     return 2 * x# def df_y(x, y, z):
#     return 2 * y# def df_z(x, y, z):
#     return 2 * z# 随机选择出生点
x0 = torch.randint(low=-1000, high=1001, size=(1,), dtype=torch.float32, requires_grad=True)
y0 = torch.randint(low=-1000, high=1001, size=(1,), dtype=torch.float32, requires_grad=True)
z0 = torch.randint(low=-1000, high=1001, size=(1,), dtype=torch.float32, requires_grad=True)# 迭代次数
epochs = 1000# 学习率
learning_rate = 1e-2# 使用 PyTorch 进行梯度下降
for _ in range(epochs):# 正向传播计算损失loss = fn(x0, y0, z0)# 反向传播计算梯度loss.backward()# 获取梯度并更新参数# 在测试阶段或者不需要计算梯度的情况下使用 torch.no_grad()# 以提高计算效率并避免不必要的梯度计算。with torch.no_grad():x0 -= learning_rate * x0.grady0 -= learning_rate * y0.gradz0 -= learning_rate * z0.grad# 梯度清零x0.grad.zero_()y0.grad.zero_()z0.grad.zero_()# 输出更新后的变量值
print("更新后的 x 值:", x0.item())
print("更新后的 y 值:", y0.item())
print("更新后的 z 值:", z0.item())

运行结果:

内容小结

  • 线性回归

    • 在训练时,xy是训练集中的特征和标签,看作是常量w和b是待优化的参数值,看作是变量
    • 在推理时,wb已经找到了比较合适的值固定下来,看作常量;此时x是待预测的样本的特征,是变量
    • 预测的本质:把x带入,求解y。
  • 求损失loss函数

    • 由训练的过程可知:损失函数loss也是w和b的函数
    • 训练的本质:求损失loss函数的最小值
  • 求函数最小值

    • 理论的求解方法,在现实工程中由于参数巨大,实际不可行。
    • 实际的求解方式是使用迭代思想逐步求解。
    • 不管 x 0 x_0 x0在何处,求最小值时减去导数即向极值逼近,所以我们可以通过迭代法+迭代中减去导数求最小值,这就是梯度下降法。
  • 求导即可使用numpy方法,也可以使用pytorch

    • 梯度下降法使用过程中,一般需要定义epochs迭代次数、learning_rate学习率
    • 梯度下降法的一般过程为:正向传播计算损失→反向传播计算梯度→获取梯度并更新参数→梯度清零
    • 在循环减去梯度的过程中,需要记得使用.grad.zero_()进行梯度清零

这篇关于【课程总结】Day7:深度学习概述的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1052890

相关文章

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

Python函数作用域与闭包举例深度解析

《Python函数作用域与闭包举例深度解析》Python函数的作用域规则和闭包是编程中的关键概念,它们决定了变量的访问和生命周期,:本文主要介绍Python函数作用域与闭包的相关资料,文中通过代码... 目录1. 基础作用域访问示例1:访问全局变量示例2:访问外层函数变量2. 闭包基础示例3:简单闭包示例4

Python版本与package版本兼容性检查方法总结

《Python版本与package版本兼容性检查方法总结》:本文主要介绍Python版本与package版本兼容性检查方法的相关资料,文中提供四种检查方法,分别是pip查询、conda管理、PyP... 目录引言为什么会出现兼容性问题方法一:用 pip 官方命令查询可用版本方法二:conda 管理包环境方法

pycharm跑python项目易出错的问题总结

《pycharm跑python项目易出错的问题总结》:本文主要介绍pycharm跑python项目易出错问题的相关资料,当你在PyCharm中运行Python程序时遇到报错,可以按照以下步骤进行排... 1. 一定不要在pycharm终端里面创建环境安装别人的项目子模块等,有可能出现的问题就是你不报错都安装

Java中最全最基础的IO流概述和简介案例分析

《Java中最全最基础的IO流概述和简介案例分析》JavaIO流用于程序与外部设备的数据交互,分为字节流(InputStream/OutputStream)和字符流(Reader/Writer),处理... 目录IO流简介IO是什么应用场景IO流的分类流的超类类型字节文件流应用简介核心API文件输出流应用文

Python中logging模块用法示例总结

《Python中logging模块用法示例总结》在Python中logging模块是一个强大的日志记录工具,它允许用户将程序运行期间产生的日志信息输出到控制台或者写入到文件中,:本文主要介绍Pyt... 目录前言一. 基本使用1. 五种日志等级2.  设置报告等级3. 自定义格式4. C语言风格的格式化方法

深度解析Python中递归下降解析器的原理与实现

《深度解析Python中递归下降解析器的原理与实现》在编译器设计、配置文件处理和数据转换领域,递归下降解析器是最常用且最直观的解析技术,本文将详细介绍递归下降解析器的原理与实现,感兴趣的小伙伴可以跟随... 目录引言:解析器的核心价值一、递归下降解析器基础1.1 核心概念解析1.2 基本架构二、简单算术表达

深度解析Java @Serial 注解及常见错误案例

《深度解析Java@Serial注解及常见错误案例》Java14引入@Serial注解,用于编译时校验序列化成员,替代传统方式解决运行时错误,适用于Serializable类的方法/字段,需注意签... 目录Java @Serial 注解深度解析1. 注解本质2. 核心作用(1) 主要用途(2) 适用位置3