深度学习RootKai

一、线性代数

1.标量

就是只有大小没有方向的数字

2.向量

里面有多个值如

C = {c1,c2,c3,c4,c5,c6,......,cn}

向量没有行列之分,列向量只有在矩阵中才有的

向量本身并没有行列之分,向量的表示可以是行向量或列向量,这只是一个约定。在矩阵中,列向量和行向量的区别更加明显:

  • 列向量是一个只有一列的矩阵,通常表示为垂直排列的数值。
  • 行向量是一个只有一行的矩阵,通常表示为水平排列的数值。

 

 

 

点乘就是点积

3.矩阵

 

矩阵乘法直观上来看是一个扭曲空间的,把一个向量变成另一个向量

AB矩阵,B的每一列都与A的每一行相乘得到一个标量放在一个位置上,最终构成一个二维数组

矩阵也有长度:我们称为范数

 

Frobenius范数就是把一个矩阵拉长,做向量的范数

 范数是用来衡量向量或矩阵大小的一种数学工具。通俗来说,它可以看作是“长度”或“大小”的概念。例如,在二维空间中,一个向量的范数就是从原点到这个点的直线距离。在机器学习和数学中,范数帮助我们理解和比较不同数据的大小或距离。常见的范数有 L1 范数、L2 范数等。

 4.特殊矩阵

不是每个矩阵都有特征向量

二、线性代数的实现

1.标量

标量可以做一些基本的运算

2.向量

于一维张量 tensor([1., 1., 1.]),其本身没有行和列的概念

标量组成的列表

  • 通过张量的索引来访问任意一个元素,计算机中的索引从0开始,而数学中的索引从1开始
  • 访问张量的长度
    • x = torch.arange(4)
      print(len(x))

      结果:

    • 4
  •  只有一个轴(维度)的张量。形状只有一个元素

3.矩阵

矩阵:shape是一个list所以它的轴向编号是从零开始的

按照轴向进行求和,就是按照顺序累加,比如axis=[1,2]就是先按行累加一起,在把按行累加后的结果,按列累加

这里比如一个tensor的shape是[3,4,5],如果我们对这个tensor求和,而且是在1和2的轴向求和,那么求和后的矩阵的shape就是在原来shape[3,4,5]的基础上去掉4,5结果的shape就是[3]就是长度为三的向量

 二维数组

多维数组

 

通过指定两个分量m和n来创建一个形状为m x n的矩阵

  • 矩阵的转置
    • A = torch.randint(1,10,(3,5))
      print(A)
      print(A.T)

      结果:

    • tensor([[9, 3, 6, 4, 6],
              [3, 8, 9, 3, 4],
              [1, 8, 9, 5, 7]])
      tensor([[9, 3, 1],
              [3, 8, 8],
              [6, 9, 9],
              [4, 3, 5],
              [6, 4, 7]])
  •  对称矩阵
    • A = torch.tensor([
          [1,2,3],
          [2,0,4],
          [3,4,5]
      ])
      print(A)
      print(A == A.T)
      

      结果:

    • 这里矩阵的比较是维数的比较和每个对应元素的比较

    • tensor([[1, 2, 3],
              [2, 0, 4],
              [3, 4, 5]])
      tensor([[True, True, True],
              [True, True, True],
              [True, True, True]])

      向量是标量的推广,矩阵是向量的推广,我们可以构建具有更多轴的数据结构

a = torch.arange(24).reshape(2,3,4)
print(a)

结果:

tensor([[[ 0,  1,  2,  3],
         [ 4,  5,  6,  7],
         [ 8,  9, 10, 11]],

        [[12, 13, 14, 15],
         [16, 17, 18, 19],
         [20, 21, 22, 23]]])

这里的reshape()里面的参数是第一维、第二维、第三维

  •  给定具有相同形状的任何两个张量,任何按元素二元运算的结果都将是相同形状的张量
    • A = torch.arange(20).reshape(5,4)
      B = A.clone()
      
      print(A, A + B)
      

      结果:

    • tensor([[ 0,  1,  2,  3],
              [ 4,  5,  6,  7],
              [ 8,  9, 10, 11],
              [12, 13, 14, 15],
              [16, 17, 18, 19]]) tensor([[ 0,  2,  4,  6],
              [ 8, 10, 12, 14],
              [16, 18, 20, 22],
              [24, 26, 28, 30],
              [32, 34, 36, 38]])
  •  两个矩阵的按元素乘法称为哈达玛积,数学符号是\bigodot
    • a = torch.randint(1,10,(3,3))
      b = torch.randint(1,10,(3,3))
      print(f"{a}\n{b}")
      print(a*b)

      结果:

    • tensor([[2, 2, 4],
              [9, 3, 4],
              [9, 2, 2]])
      tensor([[5, 5, 4],
              [9, 7, 5],
              [4, 3, 4]])
      tensor([[10, 10, 16],
              [81, 21, 20],
              [36,  6,  8]])
      
  • 标量与矩阵的加法和乘法
    • a = torch.randint(1,10,(3,3))
      b = 2
      print("---a---")
      print(a)
      print("---a+b---")
      print(a+b)
      

                标量加矩阵:就是把标量与矩阵的每个元素都相加

  • ---a---
    tensor([[5, 9, 1],
            [4, 8, 2],
            [5, 1, 6]])
    ---a+b---
    tensor([[ 7, 11,  3],
            [ 6, 10,  4],
            [ 7,  3,  8]])
    
    • 标量与矩阵的乘法
    • a = torch.randint(1,10,(3,3))
      b = 2
      print("---a---")
      print(a)
      print("---a+b---")
      print(a*b)
            结果:
    • ---a---
      tensor([[5, 9, 1],
              [4, 8, 2],
              [5, 1, 6]])
      ---a+b---
      tensor([[10, 18,  2],
              [ 8, 16,  4],
              [10,  2, 12]])

      标量与矩阵相乘,就是标量与矩阵的每个元素相乘

  • 计算矩阵元素的和
    • a = torch.randint(1,10,(3,3))
      print("---a---")
      print(a)
      print("---sum---")
      print(a.sum())

      结果:就是把每个元素加起来

    • ---a---
      tensor([[1, 4, 6],
              [5, 8, 8],
              [3, 5, 5]])
      ---sum---
      tensor(45)
  • 表示任意形状张量的元素的和
    • a = torch.randint(1,10,(3,7))
      print("---shape---")
      print(a.shape)
      print("---sum---")
      print(a.sum())

      结果:

    • ---shape---
      torch.Size([3, 7])
      ---sum---
      tensor(117)
  •  指定求和汇总张量的轴
    • a = torch.randint(1,10,(3,3))
      print(a)
      s1 = a.sum(axis = 0)
      print(s1)
      结果:
    • tensor([[6, 4, 2],
              [8, 6, 2],
              [4, 2, 5]])
      tensor([18, 12,  9])
    • 指定两个轴的求和
    • a = torch.randint(1,10,(3,3,3))
      print(a)
      print(a.sum(axis=[1,0]))
    • 结果:
    • tensor([[[3, 8, 3],
               [5, 7, 2],
               [9, 2, 7]],
      
              [[8, 6, 3],
               [7, 1, 1],
               [8, 9, 2]],
      
              [[6, 2, 1],
               [9, 9, 5],
               [4, 7, 6]]])
      tensor([59, 51, 30])
      
  • 一个与求和相关的量是平均值(mean或average)
    • a = torch.randint(1,10,(3,3),dtype=torch.float32)
      print(a.numel())
      print(a.mean())
      print(a.sum()/a.numel())

      结果:

    • 9
      tensor(5.3333)
      tensor(5.3333)
    • 指定轴向求平均值
    • a = torch.randint(1,10,(3,3),dtype=torch.float32)
      print(a.mean(axis=0))
      print(a.sum(axis=0)/a.shape[0])

      结果:

    • tensor([3.0000, 2.6667, 5.0000])
      tensor([3.0000, 2.6667, 5.0000])
  • 计算总和或均值时保持轴数不变
    • a = torch.randint(1,10,(3,3),dtype=torch.float32)
      print("---没有开启保持轴数不变---")
      print(a.sum(axis=0))
      print("---开启保持轴数不变---")
      print(a.sum(axis=0,keepdims=True))
      print("---第二维---")
      print("---没有开启保持轴数不变---")
      print(a.sum(axis=1))
      print("---开启保持轴数不变---")
      print(a.sum(axis=1,keepdims=True))
      print("---三维---")
      a = torch.randint(1,10,(3,3,3),dtype=torch.float32)
      print("---没有开启保持轴数不变---")
      print(a.sum(axis=0))
      print("---开启保持轴数不变---")
      print(a.sum(axis=0,keepdims=True))

      结果:

    • ---没有开启保持轴数不变---
      tensor([14., 18., 17.])
      ---开启保持轴数不变---
      tensor([[14., 18., 17.]])
      ---第二维---
      ---没有开启保持轴数不变---
      tensor([11., 19., 19.])
      ---开启保持轴数不变---
      tensor([[11.],
              [19.],
              [19.]])
      ---三维---
      ---没有开启保持轴数不变---
      tensor([[17., 18.,  7.],
              [ 9., 16., 18.],
              [12., 13., 14.]])
      ---开启保持轴数不变---
      tensor([[[17., 18.,  7.],
               [ 9., 16., 18.],
               [12., 13., 14.]]])

      这样保持轴数不变,可以利用广播机制

    • 通过广播将A除以sum_A
    • a = torch.randint(1,10,(3,3),dtype=torch.float32)
      sum_A = a.sum(axis=0,keepdims=True)
      print(a/sum_A)

      结果:

    • tensor([[0.2174, 0.5000, 0.6667],
              [0.3913, 0.3000, 0.0833],
              [0.3913, 0.2000, 0.2500]])
  • 某个轴计算a元素的累积求和
    • a = torch.randint(1,10,(3,3))
      print(a)
      print(a.cumsum(axis=0))

      结果:

    • tensor([[7, 7, 8],
              [7, 5, 3],
              [6, 9, 6]])
      tensor([[ 7,  7,  8],
              [14, 12, 11],
              [20, 21, 17]])

      就是一行一行的往下加

  • 点积是相同位置的按元素乘积的和
    • a = torch.ones(3,dtype=torch.float32)
      print(a)
      b = torch.arange(1,4,dtype=torch.float32)
      print(b)
      
      print(torch.dot(a,b))

      结果:

    • tensor([1., 1., 1.])
      tensor([1., 2., 3.])
      tensor(6.)

      点积(dot product)通常只能用于一维数组(1D tensors)。如果你想对多维数组进行操作,可以考虑使用矩阵乘法(如 torch.matmul

      torch.dot()注意事项:
        维度要求:a 和 b 必须都是一维张量,且长度相同。
        类型:确保张量的数据类型一致,以避免不必要的类型转换。
        性能:对于大张量,可以考虑使用 torch.matmul() 或 torch.mm(),以便进行更复的      矩阵运算。
  • 我们可以通过执行按元素乘法(哈达玛积),然后进行求和来表示两个向量的点积
    • a = torch.ones(3,dtype=torch.float32)
      print(a)
      b = torch.arange(1,4,dtype=torch.float32)
      print(b)
      print(a*b)
      print(torch.sum(a*b)

      结果:

    • tensor([1., 1., 1.])
      tensor([1., 2., 3.])
      tensor([1., 2., 3.])
      tensor(6.)
  • 矩阵向量积Ax是一个长度为m的列向量,其ii^{th}元素是点积a_{i}^{T}X
    •  

      在 PyTorch 中,torch.mm()torch.matmul()torch.mv() 都是用于矩阵运算的,但它们有不同的用途和适用情况:

    • torch.mm(a, b):

      • 用于两个二维张量(矩阵)的矩阵乘法。
      • 输入必须是二维张量,输出也是二维张量。
      • 示例:
      • result = torch.mm(a, b)  # a 和 b 必须是 2D 张量
        
    • torch.matmul(a, b):

      • 支持更广泛的输入,包括二维、三维及更高维张量。
      • 进行矩阵乘法或矩阵与向量的乘法(根据输入的维度自动判断)。
      • 示例:
      • result = torch.matmul(a, b)  # a 和 b 可以是 1D 或 2D 张量
        
    • torch.mv(a, b):

      • 用于矩阵与向量的乘法。
      • a 是二维张量(矩阵),b 是一维张量(向量)。
      • 示例:
      • result = torch.mv(a, b)  # a 是 2D 张量,b 是 1D 张量
        
    • 总结

    • 使用 torch.mm() 进行矩阵与矩阵的乘法。
    • 使用 torch.matmul() 进行更灵活的矩阵和向量的乘法。
    • 使用 torch.mv() 专门进行矩阵与向量的乘法。
    •  
      a = torch.randint(1,10,(5,4))
      print(a)
      b = torch.arange(4)
      print(b.size())
      print(torch.mv(a,b))

      结果:

    • tensor([[8, 4, 5, 4],
              [5, 8, 9, 7],
              [9, 1, 6, 8],
              [1, 3, 8, 1],
              [4, 6, 1, 5]])
      torch.Size([4])
      tensor([26, 47, 37, 22, 23])
      
  •  我们可以将矩阵-矩阵的乘法AB看成是简单的执行m次矩阵-向量积,并将结果拼接在一起,形成一个nxm矩阵
    • a = torch.randint(1,10,(4,3))
      b = torch.randint(1,10,(3,4))
      print(torch.mm(a,b))

      结果:

    • a = torch.randint(1,10,(4,3))
      b = torch.randint(1,10,(3,4))
      print(torch.mm(a,b))
  • L_{2}范数是向量元素平方和的平方根
    •  
    •  
      a = torch.tensor([1.0,2.0])
      print(torch.norm(a))

      结果:

    • tensor(2.2361)
  • L_{1} 范数。它表示为向量元素的绝对值之和
    • a = torch.tensor([1.0,2.0])
      print(torch.abs(a).sum())
       结果:
    • tensor(3.)
  • 矩阵的佛罗贝尼乌斯范数(Frobenius norm)是矩阵元素的平方和的平方根
    • print(torch.norm(torch.ones((4,9))))
       结果:
    • tensor(6.)

 三、矩阵计算:导数

1.标量导数

导数就是切线的斜率

2.亚导数

亚导数:是一个用于处理非光滑函数的概念,类似于导数,但适用于在某些点上不可导的情况。通俗地说,亚导数可以看作是函数在某一点的“最陡”上升方向的斜率。它定义了函数在该点的局部行为,帮助我们理解优化问题或变化率,即使函数本身在某些地方不平滑或不连续。

 光滑函数:是指那些在其定义域内具有连续导数的函数,简单来说,就是没有尖角或断裂的平滑曲线,比如抛物线。

非光滑函数:则是那些在某些点上可能不连续或没有导数的函数,比如绝对值函数(在原点有尖角),或者分段定义的函数。光滑函数的图形看起来很柔和,而非光滑函数的图形则可能有明显的折角或不连贯的地方。

 3.梯度(拓展到向量上)

什么是梯度:简单来说,它表示函数在某一点的“上升方向”和“上升速度”。想象一下,如果你在一座山上,梯度就告诉你哪个方向最陡,往哪个方向走能最快到达山顶。

具体来说,梯度是一个向量,它的每个分量表示函数在该方向上的变化率。在二维空间中,梯度指向函数值上升最快的方向,而其大小则表示在这个方向上的变化有多快。梯度在优化和机器学习中非常重要,因为它帮助我们找到最优解。

 为什么叫梯度:“梯度”这个词源于拉丁语“gradus”,意为“阶梯”或“步骤”。这个名字反映了它与变化和上升的关系。就像走上楼梯一样,梯度告诉你在某一点上,函数的值如何变化,在哪个方向上升得最快。因此,叫“梯度”是因为它与高低变化、上升路径的概念紧密相关。这个名字很形象,能够直观地传达出它的含义。

梯度大小的意义:梯度越大,说明函数在那个方向上的变化速度越快。梯度的大小表示了在该方向上函数值变化的快慢。因此,如果梯度的值很大,意味着你沿着这个方向走,函数值会迅速上升(或下降)。反之,梯度小则表示变化较慢。这个特性在优化问题中非常重要,因为它帮助我们找到最优解的路径。 

 对向量求导

1.标量对向量求导\frac{\partial y }{\partial \textbf{X}}

 列向量与标量的导数变成行向量

求导方法就是用标量与向量里面的所有元素进行求导

理解:

 注意:梯度是和等高线正交的,也就是梯度指向的你是函数值变化最大的那个方向

例子:

2.向量对标量求导\frac{\partial \mathbf{y} }{\partial x}

3.向量对向量求导\frac{\partial \textbf{y} }{\partial \mathbf{x}}

 先用y向量的元素对x向量求导变成标量对向量求导,然后在根据标量对向量的求导来计算

例子:

4.拓展到矩阵上

5.向量的链式法则

1.标量的链式求导法则

2.拓展到向量:链式求导法则

补充:〈x,y〉 表示向量 xx 和 yy 的内积(或点积)。内积计算结果是一个标量,反映了两个向量之间的相似度和夹角关系

 例子1:

例子2:

 6.自动求导

给我们一个函数,我们可以根据链式求导法则进行求导,但是,在神经网络里面,几百层,手动求导有点困难,所以我们需要自动求导会好一些

 1.相关概念

符号求导:就是给我一个显式的函数,我能把导数求出来

数值求导:我给你任何一个f(x)我不用知道f(x)本身长什么样子,我能够通过数值来拟合这个导数

通俗的解释:

符号求导:是通过数学公式和规则直接对函数进行微分,得到一个表达式,表示函数的变化率;比如,如果你有 f(x)=x2f(x)=x2,符号求导后得到 f′(x)=2xf′(x)=2x。

数值求导则:是通过具体数值的近似计算来估算函数的导数,常用差商方法,例如利用函数在某点附近的值来近似导数。这种方法在实际应用中常用于无法或不易求出导数的情况。

简单来说,符号求导给出精确公式,数值求导给出近似结果。

自动求导:

自动求导是一种利用计算机程序自动计算函数导数的方法。它结合了符号求导和数值求导的优点,通过追踪计算过程中变量的变化,精确地得到导数值。简单来说,就是让计算机在执行运算时,自动记录下每一步的导数信息,从而快速而准确地得到函数的导数,常用于机器学习和优化中。

2.自动求导的原理(在pytorch内部如何进行的)

1.将代码分解成操作子

2.将计算表示成一个无环图

3.计算图的构造

 3.自动求导的两种模式

链式法则:

链式法则的计算有两种:

1.正向积累:

2.反向积累、又称反向传递

复杂度:

7.自动求导的实现

前言:认识自动求导的过程

 y.backward()是说明对y开始求导而且是采用反向求导

对y求导的每步结果都存放在了x.grad里面,如果你对某个张量 xx求导,结果会存放在 x.grad 中,所以打印最后的求导结果是print(x.grad)

疑问:

问:为什么使用反向传播求导而不采用正向求导?

答:是因为在深度学习的模型中,反向求导更有优势

 假设我们想对函数y=2X^{T}X关于列向量X求导

x = torch.arange(4.0)
print(x)

结果:

tensor([0., 1., 2., 3.])

第一步:在我们计算关于X的梯度之前,我们需要一个地方来存储梯度

x = torch.randint(1,10,(5,6),dtype=torch.float32)
x.requires_grad_(True) #这句话等价于x = torch.arange(4.0,requieres_grad=True)
print(x.grad) #默认值为None

x.requires_grad(True) 是在深度学习库(比如 PyTorch)中使用的一个命令,用于告诉系统要对变量 xx 进行自动求导。通俗来说,这句话的意思是:

  • 开启梯度计算:让系统知道你希望在后续的计算中,能够追踪 xx 这个变量的变化,从而在训练模型时计算出它对损失函数的影响。

换句话说,这样设置后,任何通过 x进行的运算都会被记录,以便后续计算导数(梯度)。这对优化算法(比如反向传播)非常重要。

这一步的目的就是为了知道Y是怎么得到的,然后可以才开Y进行求导

 x.grad :是用来获取变量 x 的梯度(导数)值。在深度学习中,这个梯度表示的是损失函数相对于 x 的变化率,通俗来说,就是 x 对模型性能的影响程度。通过查看 x.grad,你可以了解调整 x的值将如何影响模型的输出,从而指导优化过程。

第二步:我们计算y

x = torch.arange(4.0)
x.requires_grad_(True) #这句话等价于x = torch.arange(4.0,requieres_grad_=True)

y = 2 *torch.dot(x,x)
print(y)

结果:

tensor(28., grad_fn=<MulBackward0>)

grad_fn=<MulBackward0> 是在深度学习框架(如 PyTorch)中,表示一个张量(tensor)的梯度计算历史。具体来说,它指的是这个张量是通过某个操作(在这个例子中是乘法)生成的,并且可以追踪这个操作的反向传播信息。

通俗来说:

  • grad_fn:这个属性记录了生成当前张量的操作(如加法、乘法等)。
  • <MulBackward0>:表示这是一个乘法操作的反向传播函数,系统可以用它来计算导数。

简单地说,这让框架能够在需要时自动计算梯度,以支持反向传播

总之一句话:grad_fn=<MulBackward0>这句话说明我这个张量是通过乘法得到的,并且我这个张量支持(可以)通过反向传播来追溯这个张量产生的每一步

1.为什么要追溯张量产生的每一步?

深度学习框架能够记录和管理张量生成过程中涉及的操作。这些信息在反向传播过程中非常重要,因为它们用于计算梯度(导数)。

具体来说:

  • 追踪操作:当你对张量进行运算(例如加法或乘法)时,框架会记录这些运算的类型和顺序。
  • 反向传播:在训练过程中,模型会通过反向传播算法更新参数。反向传播需要计算损失函数相对于模型参数的梯度,框架利用之前记录的操作信息来有效地计算这些梯度。

这样,框架能够在需要时自动处理复杂的梯度计算,而你不需要手动去实现每一步的导数计算。

2.这对求导有什么帮助?

这些信息在反向传播过程中非常重要,因为它们决定了如何计算每个参数的梯度,从而指导模型的优化过程。

例如,假设你有一个简单的神经网络,包含输入层、隐藏层和输出层。假设在某一步,你通过乘法和加法生成了一个中间张量。如果没有记录这些操作的历史,框架就无法知道在计算梯度时,应该如何拆解这些操作来逐步求出每个参数的导数。

如果你用链式法则进行反向传播,框架需要知道:如果某个中间张量是通过两个张量相乘得来的,那么在求导时需要分别考虑这两个张量的影响。没有这些操作信息,反向传播的过程将无法正确进行,导致模型无法有效学习。

第三步:通过调用反   

x = torch.arange(4.0)
print(x)
x.requires_grad_(True) #这句话等价于x = torch.arange(4.0,requieres_grad_=True)
y = 2*torch.dot(x, x)
print(y)
y.backward()
print(x.grad)

结果:

tensor([0., 1., 2., 3.])
tensor(28., grad_fn=<MulBackward0>)
tensor([ 0.,  4.,  8., 12.])

对应的数学分析过程:

 注意:在默认情况下Pytorch会累积梯度,我们需要清除之前的值

x.grad.zero_()
y.backward()
x.grad

深度学习中,我们的目的不是计算微分矩阵,而是批量中每个样本单独计算的偏导数之和

暂时不太明白????????????????????????????????

就是我们基本上都是对标量求导,而不是对矩阵求导

 将某些计算移动到记录的计算图之外

 将某些计算移动到记录的计算图之外:意味着在执行某些操作时,我们不希望它们被纳入自动求导的计算图中。这通常是为了提高效率或避免不必要的梯度计算。

具体来说:

  1. 计算图:在深度学习框架(如 PyTorch)中,计算图用于跟踪张量操作,以便在反向传播时自动计算梯度。

  2. 记录与不记录:有些计算可能不需要进行梯度计算(例如,数据预处理、某些中间计算结果等)。如果将这些计算移动到计算图之外,框架就不会跟踪这些操作,从而节省内存和计算资源。

  3. 方法:在 PyTorch 中,可以使用 with torch.no_grad() 上下文管理器,或者使用 .detach() 方法,来明确表示某些操作不应影响计算图。

这样做可以使模型训练更加高效,同时也可以避免计算不必要的梯度。

下面的代码里u = y.detach(),就是这个操作,因为u没有被求导

x.grad.zero_()
y = x*x
u = y.detach() #将y变成一个标量
z = u * x
z.sum().backward()
print(x.grad)

即使构建函数的计算图需要通过Python控制流(例如:条件,循环,或者任意函数调用),我们仍然可以计算得到的变量的梯度

def f(a):
    b = a * 2
    while b.norm() < 100:
        b = b*2
    if b.sum() > 0:
        c = b
    else:
        c = 100 * b
    return c
a = torch.randn(size=(),requires_grad=True)
d = f(a)
d.backward()

这段代码的意思就是,我的b,c都是用python的函数控制流来控制生成的,所以,我们在后面对d求导的时候,就需要知道我的d是怎么来的,所以,这段代码就验证了,我的d通过python的控制流生成的,同样可以使用a向量记录生成过程,用来自动求导

四、深度学习基础

1.线性回归

正式迈入深度学习的大门,前面的是游戏教程,我们结束了游戏教程,下面我们开始操作这个好玩的游戏吧。-----RootKai

线性回归是机器学习最基础的一个模型,也是我们理解后面所有深度学习模型的基础

 1.一个简化模型:

假设1:影响房价的关键因素是卧室个数、卫生间个数、居住面积,记为:x1,x2,x3

假设2:成交价是关键因素的加权和

权重和偏差的实际值在后面决定

2.拓展到一般化的线性模型

3.线性模型可以看作是单层的神经网络

 输入的维度是d,输出的维度是1只有一个O1,每一个箭头代表一个权重,这里我们没有画偏差,所以我们的这个神经网络,是一个输入层和一个输出层,它之所以叫作单层神经网络,是因为它带权重的层为一,我们不把输出层当作一个层,因为我们把权重和输入层放在了一起。

4.为什么这个就是叫做神经网络,而不是其他的

神经网络源于神经科学

真实的神经元

5.衡量预估质量(损失)

  • 比较真实值和预估值,例如房屋售价和估价
  • 假设y是真实值,是估计值,我们可以比较

 这个叫做平方(均方)损失

损失函数:

损失函数是一个数学公式,用于衡量模型预测值与真实值之间的差异。它提供了一个量化的标准,以便在训练过程中优化模型参数。

具体来说:

  1. 损失函数:是一个定义好的数学公式,用来计算模型预测值与真实值之间的差距。

  2. 训练损失:是将损失函数应用于训练数据后的结果,表示模型在训练集上的具体表现。

所以,可以说它们在本质上都是损失函数,只是在不同的上下文中使用。

使用平方损失的原因主要有:

  1. 容易计算:平方损失是平滑的,计算起来简单,适合用来找到最佳参数。

  2. 重视大错误:它对大的预测错误惩罚更重,能帮助模型更好地调整,避免大偏差。

  3. 统计支持:在一些假设下,使用平方损失可以帮助我们找到最可信的结果。

这样一来,平方损失在训练模型时更有效、更可靠。如果还有其他问题,请随时问!

 前面有1/2是方便我们在求导的时候可以把2给消掉

 6.训练数据

在前面我们定义了模型、定义了损失之后,下面我们可以来学习我们的参数了(也就是权重和偏差),我们是基于数据来学习

  • 我们搜集一些数据点来决定参数值(权重和偏差),例如过去6个月卖的房子
  • 这被称为训练数据
  • 通常越多越好
  • 假设我们有n个样本,记为

这里的y向量是我们测试数据的真实值

样本是包含特征值和真实值(类别标记)

 7.参数学习

有了上面的这些之后,我们就可以求解我们的模型了,我们根据我们之前定义的损失、我们给定数据来评估说对于我们的模型,在每一个数据上的损失求均值就会得到我们的一个损失函数

疑问:

问:为什么要求均值?

答:均值让我们更准确地评估模型的表现,确保我们的优化过程是合理和有效的。

损失函数:

这个函数就是关于,我们的数据X,y,关于我们的权重,偏差,1/2来自我们定义的损失

我们的目标:我们要找到一个w和b使得我们的损失函数最小,也就是下面我们的最小化损失函数来学习参数

这个公式的意思:我们选取一个w和一个b使得这一项最小,把是的这个损失函数最小化的w和b记作我们的解记为w*和b*

求解:

因为这个是线性模型有显示解:

将偏差加入到权重里,就是在X向量里加一个1即可·

知识点补充:在数学和机器学习中,符号 arg⁡通常与优化问题相关,表示“使得...最小化(或最大化)的参数值”。在优化问题的上下文中,arg⁡min⁡或 arg⁡max 用于找到使目标函数(如损失函数)达到最小值或最大值的参数。

总结:只有线性回归有显示解,其他都没有

2.基础优化方法

1.常见优化算法:梯度下降

1.当我们的模型没有显示解的时候我们该怎么做?

第一步:我先挑选一个参数的随机初始值w0

第二步:在接下来的时候,我们不断的去更新w0使得它接近我们的最优解,我们的更新法则:如下:

负梯度:就是函数值下降最快的方向 ,沿着梯度方向每次走多远

梯度下降算法的核心思想就是通过计算损失函数的梯度,找到损失值下降最快的方向

实际的例子就是:我下山的时候,沿着最陡的方向下山,很快就能到达山地(因为这里是负梯度)

2.选择学习率

首先,不能太小,如果太小,我们每次走的步长有限,那么我们到达某一个点的时候,要走很多的步骤,这样会计算很多次梯度,而梯度是我们计算模型里,最贵的那一步

 学习率低了,结果变坏了:

学习率过低可能导致模型训练缓慢,出现以下问题:

  1. 收敛缓慢:模型更新幅度小,可能需要很长时间才能接近最优解,导致训练时间延长。

  2. 局部最优:更新幅度小可能让模型停留在局部最优,而无法找到更好的全局最优解。

  3. 振荡问题:如果学习率太低,优化过程可能在一些区域内震荡,而无法有效地向目标收敛。

学习率就像你走路时的步伐大小:

  1. 步伐太小:如果你每一步走得太小,你可能会走得很慢,花很长时间才能到达目的地。训练也一样,学习率低会让模型学习得很慢。

  2. 停在错误的地方:如果你走得太小,可能在某个地方徘徊,没办法找到更好的路径。训练时,模型可能卡在一个不太好的解上,无法继续进步。

  3. 不够有效:小步伐在大风景里可能会让你迷失方向,而不容易找到正确的路。类似地,学习率低会使得模型在学习过程中无法有效调整。

其次,我们也不能选太大,因为一下子步子迈太大了,就买过了我们下降的地方,就迈到了很远很远的地方,导致我们一直在震荡,并没有真正的在下降

 总之学习率,不能太大也不能太小

在实际中我们很少使用梯度下降,深度学习最常见的梯度下降版本是小批量随机梯度下降,我们每次计算梯度,要对整个损失函数求导,这样我们要把所有样本重新算一下,我们使用一个近似的办法

 3.线性回归从零实现(不使用深度学习框架)

 %matplotlib inline 是一个 IPython 魔法命令,用于在 Jupyter Notebook 中设置 Matplotlib 的绘图显示方式。

具体来说,它的作用是:

  • 内联显示:使得所有生成的图像直接嵌入到 Notebook 的输出单元中,而不是弹出一个单独的窗口。
  • 方便可视化:这样可以更方便地查看和分析生成的图形,特别是在交互式数据分析时。

总之,使用 "%matplotlib inline" 后,绘图命令的输出会直接显示在 Notebook 中。

 第一步:首先,构造一个数据集,

代码解释:

噪声:是指在信号或数据中干扰我们想要的信息的随机或无关的成分。通俗来说,可以把噪声想象成在听音乐时,周围的杂音,比如人说话的声音、交通的喧嚣等,这些杂音会影响你对音乐的感受。

在数据和信号处理中,噪声会使得我们更难准确地理解和分析数据。例如,在图像中,噪声可能表现为随机的亮点或斑点,使得图像变得模糊或不清晰。总之,噪声是一种无用的干扰,通常需要通过各种方法进行消除或减少。

 完整代码:

import torch
import random
from torch.onnx.symbolic_opset9 import detach

"""注意:这里的#注释是每一步的标题,在三个引号里的注释是是知识点的补充"""

#生成模拟数据
def synthetic_data(w,b,num_examples):
    """生成y = Xw + b + 噪声"""
    x = torch.normal(0,1,(num_examples, len(w)))
    y = torch.matmul(x,w) + b
    y += torch.normal(0,0.01,y.shape)
    return x,y.reshape((-1,1))

true_w = torch.tensor([2, -3.4])
true_b = 4.2

features, labels = synthetic_data(true_w, true_b, 1000)

#打印这些数据
# print(features[0:10,:])
# print(labels[0:10,:])
# print(len(features))
# num_examples = len(features)
# indices = list(range(num_examples))
# random.shuffle(indices)
# print(indices)


#创建一个迭代器,进行从数据集里选取小批量的数据,进行模型的训练

"""
range() 是 Python 中用于生成一个整数序列的内置函数。它常用于循环中,以指定迭代的范围。
语法

python

range(start, stop, step)
    start:序列的起始值(默认为 0)。
    stop:序列的结束值(不包含该值)。
    step:步长,默认为 1。
"""

"""
yield 是 Python 中用于定义生成器的关键字。当在一个函数中使用 yield 时,该函数会返回一个生成器对象,而不是一次性返回一个值。这允许你逐步生成值,从而节省内存。

获取生成的值:

    在循环中,第一次调用 next()(隐含在 for 循环中),函数开始执行:
        count 是 1,满足条件,执行 yield count,返回 1,并暂停。
    print(number) 打印 1。

继续执行:

    再次调用 next(),恢复执行到上一个 yield 的地方:
        count 现在变成 2,继续循环。
        再次执行 yield count,返回 2,并暂停。
    print(number) 打印 2。
"""

def data_iter(batch_size, features, labels):
    num_examples = len(features)
    indices = list(range(num_examples))
    random.shuffle(indices) #打乱列表的下标
    for i in range(0, num_examples, batch_size):
        batch_indices = torch.tensor(
            indices[i: min((i + batch_size,num_examples))] #min()是为了不能完全被平分后,下标过大,导致报错,这样可以把下标控制在列表的最后一个位置
        )
        yield features[batch_indices], labels[batch_indices]
batch_size = 10
for x,y in data_iter(batch_size, features, labels):
    # print(x,'\n',y)
    break


#定义初始化模型参数
w = torch.normal(0, 0.01, size=(2,1), requires_grad=True)
b = torch.zeros(1, requires_grad=True)

#定义模型
def linreg(x,w,b):
    """线性回归模型"""
    return torch.matmul(x,w) + b
#定义损失函数
def squared_loss(y_hat, y):
    """均方损失"""
    return (y_hat - y.reshape(y_hat.shape))**2 / 2
#定义优化算法gradient descent梯度下降
"""参数解释:params一个列表,里面是w,b;lr是学习率;"""
def sgd(params,lr,batch_seize):
    """小批量随机梯度下降"""
    with torch.no_grad():
        for param in params:
            """param.grad / batch_seize:默认情况下,PyTorch 在计算梯度时,针对每个样本的损失进行反向传播,得到的 param.grad 是对该批次样本的梯度累加。"""
            param -= lr * param.grad / batch_seize
            """手动把梯度设置为0,下一次计算梯度的时候,就不会和上一次相关了"""
            param.grad.zero_()

#训练过程
lr = 0.0256
"""把整个数据扫三遍"""
"""
num_epochs = 3 的意思是整个训练过程将对数据集进行三轮(或三遍)遍历。具体来说:

    一个 epoch:一次完整的前向传播和后向传播,使用整个训练数据集来更新模型的参数。

    多轮训练:通过多次遍历数据集,模型可以更好地学习数据的特征,逐渐优化参数,以提高性能。

    收敛:通常模型需要多个 epochs 来逐步收敛,找到更好的权重,使得损失最小化。
"""
num_epochs = 10
"""模型"""
net = linreg
"""损失函数"""
loss = squared_loss

"""第一层,每次对数据扫一遍"""
for epoch in range(num_epochs):
    """第二层每次拿出一个小批次数据"""
    for x,y in data_iter(batch_size, features, labels):
        """计算损失函数"""
        l = loss(net(x,w,b), y)
        """求和算梯度"""
        """
        当你计算损失时,l 可能是一个包含多个元素的张量,比如批量中的每个样本的损失。如果直接调用 l.backward(),PyTorch 会对这个张量的每个元素分别计算梯度,结果是每个元素都得到梯度,而不是一个总的梯度。
        通过 l.sum().backward(),你首先将所有损失求和,得到一个单一的标量,这样反向传播时只计算这个总损失的梯度,从而简化了计算过程并避免了维度问题。
        """
        l.sum().backward()
        """更新我们的w检验一下效果"""
        sgd([w,b],lr,batch_size)
    with torch.no_grad():
        train_l = loss(net(features,w,b), labels)
        print(f'epoch {epoch + 1} train loss: {float(train_l.mean()):f}')

4.线性回归的简洁实现

使用深度学习框架

完整代码:

import numpy as np
import torch
import d21
"""nn是神经网络的缩写"""
from torch import nn

"""处理数据的模块"""
from torch.utils import data

#创建模拟数据
true_w = torch.tensor([2,-3.4])
true_b = 4.2
features, labels = d21.synthetic_data(true_w, true_b,1000)
#小批量取出数据
"""这里调用框架中现有的API来读取数据"""
def load_array(data_arrays, batch_size, is_train=True):
    """构造一个Pytorch数据迭代器"""
    """把我们的数据传进来获得一个dataset"""
    dataset = data.TensorDataset(*data_arrays)
    """有了dataset我们调用DataLoader每次随机从里面挑选batch_size个样本,shuffle就是是不是要随机打乱他们的顺序"""
    return data.DataLoader(dataset, batch_size=batch_size, shuffle=is_train)
batch_size = 10
"""迭代器"""
data_iter = load_array((features, labels), batch_size)
#定义模型
"""nn.Linear(2,1)输入维度和输出的维度"""
"""这里的Sequential是一个容器,用来放各个层的,这里因为是线性回归,所以只有单层,所以只放的linear"""
net = nn.Sequential(nn.Linear(2,1))
#定义初始化模型参数
"""net[0]用于访问容器中的第一个层"""
"""data是Linear里面原来的数据,我们给他转换成了正太分布的"""
net[0].weight.data.normal_(0,0.01)
net[0].bias.data.fill_(0)
#定义损失函数
loss = nn.MSELoss()
#定义优化算法
trainer = torch.optim.SGD(net.parameters(), lr=0.03)
#训练
num_epochs = 3
for epoch in range(num_epochs):
    for x,y in data_iter:
        l = loss(net(x),y)
        trainer.zero_grad()
        l.backward()
        """进行模型的更新"""
        trainer.step()
    l = loss(net(features),labels)
    print(f'epoch {epoch} loss {l:f}')

五、深度学习基础(二)

1.Softmax回归

这是一个分类问题

分类vs回归

回归:估计一个连续值

分类:预测一个离散类别

回归:

单连续数值输出

自然区间R

跟真实值的区别作为损失

分类:

通常多个输出

输出i是预测为第i类的置信度

2.从不回归到多类分类----均方(平方)损失

一位有效编码:

一位有效编码(One-Hot Encoding)是将分类标签转换为二进制向量的过程,其中每个类别都对应一个唯一的二进制向量。

假设我们有一个关于动物类别的数据集,其中有三种动物:猫、狗和鸟。我们想要对这些类别进行一位有效编码。

原始标签:

进行一位有效编码后,我们可以将这些类别转换为以下二进制向量:

  • 猫:[1, 0, 0](表示第一个类别)
  • 狗:[0, 1, 0](表示第二个类别)
  • 鸟:[0, 0, 1](表示第三个类别)

现在,让我们用一个具体的例子来说明这个过程:

假设我们有5个样本,它们的原始类别标签如下:

原始标签:   猫  狗  鸟  猫  狗

经过一位有效编码后,这些标签将转换为以下形式:

编码后的标签:
   [1, 0, 0]  [0, 1, 0]  [0, 0, 1]  [1, 0, 0]  [0, 1, 0]

在这个编码中:

  • 第一个样本是“猫”,所以第一个位置是1,其余是0。
  • 第二个样本是“狗”,所以第二个位置是1,其余是0。
  • 第三个样本是“鸟”,所以第三个位置是1,其余是0。
  • 第四个样本又是“猫”,所以再次将第一个位置设为1。
  • 第五个样本是“狗”,所以再次将第二个位置设为1。

这种方式允许模型能够以数值形式处理类别数据,并且可以轻松地区分不同的类别。在机器学习模型中,这种编码特别有用,因为它可以确保每个类别都有一个清晰的、唯一的表示,而不会混淆。

最大值作为预测”:

这句话可能是在描述一种特定的预测策略,即在分类问题中,模型可能会选择概率最高的类别作为预测结果。例如,如果你的模型是一个分类器,它可能会为每个类别分配一个概率分数,然后选择分数最高的那个类别作为最终预测。

 在机器学习中,"使用均方训练"通常指的是使用均方误差(Mean Squared Error, MSE)作为损失函数来训练模型。均方误差是一种常见的损失函数,用于衡量模型预测值与实际值之间的差异。它计算了预测误差的平方的平均值。

通俗解释:

假设你在做投篮练习,你的目标是让篮球每次都准确地投入篮筐。但是,每次投篮都可能因为力度、角度或其他因素而偏离目标。均方误差就像是一个评估你投篮准确性的工具:

  1. 每次投篮的“误差”:如果你投偏了,这个“误差”就是篮球落点与篮筐中心点之间的距离。

  2. 平方误差:为了更重视大的误差(比如,投到篮筐后面比擦框而出更严重),每次误差不是简单地计算距离,而是计算距离的平方。

  3. 平均误差:假设你连续投了10次篮,每次的误差平方后加起来,然后除以10(投篮次数),得到一个平均值。这个平均值就是均方误差。

在机器学习中:

  • 预测值:你的每次投篮尝试。
  • 实际值:篮筐中心点,也就是你想要篮球达到的确切位置。
  • 误差:预测值与实际值之间的差异。
  • 平方误差:为了避免正负误差相互抵消,将误差平方。
  • 平均:计算所有样本的平方误差的平均值。

置信度:

置信度是指对某个预测结果或结论的信心程度。可以把置信度想象成一个百分比。例如,如果你说“我有80%的信心这个天气预报是正确的”,那就是你对这个预报的置信度是80%。如果置信度越高,说明你越相信这个结果是准确的;反之,置信度低则表示你对结果的不确定性更大。

(与上面的一样的)是你对某个判断或猜测有多确定的感觉。

想象一下,你参加了一个猜谜游戏,主持人给你一些线索,让你猜一个名人的名字。根据线索,你可能会有不同的把握程度:

  1. 低置信度

    • 如果线索很少或者很模糊,你可能不太确定自己的猜测是否正确。这时候,你的置信度就比较低,比如说只有30%,意味着你只有30%的把握猜中。
  2. 高置信度

    • 如果线索非常明显,你几乎可以确定自己的猜测是正确的。这时候,你的置信度就很高,比如说有90%,意味着你有90%的把握猜中。

在机器学习中,模型也会对它的预测给出一个“把握程度”。例如,一个图像识别模型可能需要判断一张照片中的物体是猫还是狗。如果模型非常确定这张照片是猫,它可能会给出一个高置信度,比如95%。如果模型不太确定,可能只给出一个低置信度,比如60%。

这个置信度通常是基于模型内部的计算得出的,比如它会考虑所有支持“是猫”的证据和所有支持“是狗”的证据,然后比较两者的强度。在实际应用中,我们可能会设置一个阈值,只有当置信度超过这个阈值时,我们才认为模型的预测是可靠的。

我们希望我们的输出是一个概率(非负,和为1)但是我们现在输出的O是一个向量我们引入一个操作子:softmax(O)得到一个长为n的向量,但是它的每个元素都是非负,和为1

 其实y-hat就是一个概率了

 softmax和交叉熵损失:

交叉熵:

想象你在参加一个考试,考题是选择正确的答案。你有一个“信念”或者“预测”,说某个答案是对的,比如你认为答案A是正确的。

  • 真实情况:实际上,答案是B。
  • 你的预测:你认为答案A的正确性有70%的信心,B的正确性有30%的信心。

交叉熵的例子:

比较真实概率和预测概率之间的损失作为区别:(一般我们用交叉熵来衡量区别)

3.损失函数

1.均方损失(L2 Loss)

定义:真实值减去预测值平方除以2,乘以1/2为了求导消掉2

2.绝对值损失函数(L1 Loss)

定义:

3.Huber‘s Robust Loss

定义:

似然函数:

似然函数是统计学中的一个重要概念,用来衡量在给定参数下,观察到的数据出现的可能性。

通俗解释

想象你在掷硬币,硬币可能是公平的(即正反面概率各50%),也可能是偏的(比如正面70%,反面30%)。如果你掷了硬币多次,并记录下结果(比如你得到了7次正面和3次反面),那么你可以用似然函数来评估不同硬币的“好坏”。

  1. 数据:你观察到的数据(例如:7次正面和3次反面)。
  2. 参数:硬币正面朝上的概率(比如0.5、0.7等)。
  3. 似然函数:对于每一个可能的概率值,你计算在这个概率下,观察到的结果出现的可能性有多大。

例子

  • 假设你认为硬币是公平的(概率为0.5),那么似然函数可以告诉你,在这个假设下,得到7次正面和3次反面的概率是多少。
  • 如果你试着计算概率,比如发现0.5的概率下结果出现的概率很低,而0.7下的概率很高,那么就说明硬币可能是偏的。

总结

似然函数可以简单理解为一个工具,用来评估在特定参数假设下,观察到的数据有多“合理”。通过调整参数,你可以找到最符合数据的参数值,这就是最大似然估计。

结束:======================后面的笔记就是类似疑问,探究======================

一、图像数据集

Fashion-MNIST 的数据集的label是用数字表示的,而不是用文字,我们要自己进行映射

补充:列表推导式:

列表推导式(List Comprehension)是 Python 中用于创建新列表的一种简洁而优雅的方式。它允许你通过对已有列表进行操作,快速生成新的列表。列表推导式的基本语法如下:

python

new_list = [expression for item in iterable if condition]

组成部分

  1. expression:这是要添加到新列表中的元素,通常是对 item 的某种处理。
  2. for item in iterable:这是迭代的部分,iterable 是你要遍历的集合(如列表、元组、字符串等)。
  3. if condition(可选):这是一个可选的条件,只有满足这个条件的 item 才会被包含在新列表中。

示例

  1. 基本示例:创建一个包含数字的平方的列表。

python

squares = [x**2 for x in range(10)]
print(squares)

输出:

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
  1. 带条件的列表推导式:仅包括偶数的平方。

python

even_squares = [x**2 for x in range(10) if x % 2 == 0]
print(even_squares)

输出:

[0, 4, 16, 36, 64]
  1. 与字符串操作结合:将字符串列表中的每个字符串转换为大写。

python

fruits = ['apple', 'banana', 'cherry']
uppercase_fruits = [fruit.upper() for fruit in fruits]
print(uppercase_fruits)

输出:

['APPLE', 'BANANA', 'CHERRY']

使用场景

列表推导式非常适合用于以下情况:

  • 需要从一个列表生成另一个列表。
  • 需要对列表中的每个元素进行处理或过滤。

使用列表推导式可以使代码更简洁、更易读。

注意:在pycharm中显示图片要用

plt.tight_layout()  # 自动调整子图参数
plt.show()  # 显示图像

六、感知机

1.什么是感知机?

感知机就是模仿人类的神经元产生的,进行对输入的结果进行二分类

可以实现一个二分类:
 

 使用感知机进行二分类,就要找到一个能将样本全部分开的决策函数:WX + b = 0,因此我们需要一个学习策略,就是定义一个损失函数,通过训练样本,通过减小损失值,不断迭代模型的参数最终找到最优参数W和b,损失函数的作用是用来衡量模型的输出结果和真实结果之间的偏差,然后根据偏差修正模型。对于线性回归的问题结果是连续的值,能衡量差异,但是二分类,怎么衡量?为了通过可导来更新模型,感知机通过选择用误分类样本到决策函数的距离来衡量模型的偏差感知机只关心怎么能把样本区分开

七、模型的选择

1.训练误差

2.泛化误差

3.计算训练误差和泛化误差

1.数据集:

训练数据集:训练模型参数

验证数据集:选择模型超参数

非大数据集上通常使用K-折交叉验证

2.K-则交叉验证

八、过拟合和欠拟合

拟合是指使用一个数学模型或函数来表示数据中的关系,使得该模型尽可能精确地通过或接近给定的样

1.模型容量

 2.模型容量的影响

随着模型的复杂度变高,模型会过拟合

3.估计模型容量

4.VC维

用一个简单的比喻来形象地解释 VC 维。

想象一个学校运动会

想象你在学校运动会上,有一个裁判要决定参赛者的表现。裁判有一个“规则”系统,可以根据参赛者的表现来给他们打分。这个规则系统就像我们的模型。

VC维的比喻
  1. 样本点:想象有几个运动员在比赛。每个运动员代表一个样本点。

  2. 区分能力:如果裁判能够根据自己的“规则”来对这些运动员进行评分,并且可以对所有可能的组合(如每个运动员得分或不得分)进行完美的区分,那就意味着这个规则系统非常灵活,能处理很多情况。

  3. VC维:假设裁判可以对最多3个运动员的表现进行完美分类,比如:

    • 所有3个人都得分。
    • 其中1人得分,其他2人不得分。
    • 2人得分,1人不得分。

    但如果有4个运动员,裁判就无法做到这一点,无法为他们的所有组合打分。这时,我们说这个规则系统的 VC 维就是 3,因为它只能完美区分最多3个运动员的表现。

总结

  • VC维就像裁判的评判能力:越高的 VC 维意味着裁判能处理越多的运动员和表现组合。
  • 重要性:了解裁判的能力(VC维)可以帮我们决定在比赛中选择哪个裁判(模型),以确保他们能正确评判运动员的表现。

 5.数据复杂度

 6.处理过拟合的方法

1.权重衰退

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值