机器学习鱼书笔记(自用更新)

零、预知识

1.Numpy

使用

  1. 介绍:高效的操作多维数组的函数库。

  2. 安装:(前提已经安装了python)

    pip install numpy
    
  3. 导入

    import numpy as np
    
  4. 创建数组

    Numpy最重要的数据结构是多维数组(ndarray)。通过Numpy,你可以轻松创建数组:

    # 从Python列表创建一维数组
    arr1d = np.array([1, 2, 3, 4, 5])
    >[1, 2, 3, 4, 5]# 从Python嵌套列表创建二维数组
    arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
    >[[1, 2, 3],[4, 5, 6],[7, 8, 9]]# 创建全零数组
    zeros = np.zeros((3, 4))
    >[[0., 0., 0., 0.],[0., 0., 0., 0.],[0., 0., 0., 0.]]# 创建全一数组
    ones = np.ones((2, 3))
    >[[1., 1., 1.],[1., 1., 1.]]# 创建指定范围内的数组
    range_arr = np.arange(0, 10, 2)
    >[0, 2, 4, 6, 8]# 创建线性间隔的数组
    linspace_arr = np.linspace(0, 1, 5)
    >[0.  , 0.25, 0.5 , 0.75, 1.  ]
    
  5. 数组属性

    Numpy数组有许多属性,你可以通过它们来了解数组的维度、形状和元素类型:

    arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])print(arr.shape)        # 获取数组的形状 n行m列 输出:(3, 3)
    print(arr.ndim)         # 获取数组的维度 输出:2
    print(arr.size)         # 获取数组的长度 输出:9
    print(arr.dtype)        # 获取数组的元素类型 输出:int64
    
  6. 数组操作

    Numpy提供了许多数组操作函数,使得数组的操作和计算变得简单高效:

    # 数组加法
    arr1 = np.array([1, 2, 3])
    arr2 = np.array([4, 5, 6])
    result = arr1 + arr2
    >[5, 7, 9]# 数组乘法
    arr = np.array([1, 2, 3])
    result = arr * 2
    >[2, 4, 6]# 二维数组乘法
    x = np.array([[1, 2], [3, 4]])
    y = np.array([[2, 1], [3, 4]])
    >[[1, 2],   [[2, 1],[3, 4]]    [4, 3]]
    >[[2, 2],[12, 12]]# 矩阵乘法 点乘运算
    mat1 = np.array([[1, 2], [3, 4]])
    mat2 = np.array([[5, 6], [7, 8]])
    result = np.dot(mat1, mat2)
    >[[1, 2],     [[5, 6],[3, 4]]			 [7, 8]]
    >输出: [[19, 22],[43, 50]]# 数组索引和切片 与python内置数组操作一致
    arr = np.array([1, 2, 3, 4, 5])
    print(arr[0])         # 输出:1
    print(arr[1:4])       # 输出:[2, 3, 4]# 数组形状变换
    arr = np.array([1, 2, 3, 4, 5, 6])
    reshaped_arr = arr.reshape(2, 3)
    >[[1, 2, 3],[4, 5, 6]]
    
  7. 常用数学函数

    Numpy提供了许多常用的数学函数,可以直接应用于数组:

    arr = np.array([1, 2, 3, 4, 5])print(np.sum(arr))          # 输出:15
    print(np.mean(arr))         # 输出:3.0
    print(np.max(arr))          # 输出:5
    print(np.min(arr))          # 输出:1
    print(np.sin(arr))          # 输出:[0.84147098 0.90929743 0.14112001 -0.7568025  -0.95892427]
    print(np.cos(arr))          # 输出:[0.54030231 -0.41614684 -0.9899925 -0.65364362 0.28366219 0.96017029]
    print(np.power(arr, 2))     # 输出:[1,  4,  9, 16, 25]
    print(np.exp(arr))          # 输出:[2.71828183, 7.3890561, 20.08553692, 54.59815003, 148.4131591 ]
    

广播机制

广播是numpy中一种强大的机制,允许对不同形状的数组进行运算,而不需要显式地进行形状匹配或复制数据。

  1. 广播标量,下图将10当做2x2的矩阵来运算
    请添加图片描述

  2. 数组广播

    请添加图片描述
    通过以上的例子可以看到广播的原则都是低纬度向高纬度看齐,然后补全数据,再进行运算。

2.Matplotlib

Matplotlib是Python中最流行的数据可视化库之一,可以用来绘制图表内容。

安装Matplotlib

在开始之前,确保你已经安装了Python和Matplotlib。如果还没有安装Matplotlib,可以通过以下命令使用pip进行安装:

pip install matplotlib

导入Matplotlib

在使用Matplotlib之前,首先需要导入它。习惯上,我们使用以下方式导入Matplotlib并简写为plt

import matplotlib.pyplot as plt

1. 绘制简单的折线图

折线图是Matplotlib中最简单的图表类型之一,它用于显示数据随着变量的变化而变化的趋势。下面是一个简单的绘制折线图的例子:

# 示例数据
x = [1, 2, 3, 4, 5]
y = [2, 4, 6, 8, 10]# 绘制折线图
plt.plot(x, y)# 添加标题和标签
plt.title('简单折线图')
plt.xlabel('X轴')
plt.ylabel('Y轴')# 显示图形
plt.show()

图形绘制如下

请添加图片描述

2.绘制散点图

散点图常用于显示两个变量之间的关系。下面是一个绘制散点图的例子:

# 示例数据
x = [1, 2, 3, 4, 5]
y = [2, 4, 6, 8, 10]# 绘制散点图
plt.scatter(x, y)# 添加标题和标签
plt.title('简单散点图')
plt.xlabel('X轴')
plt.ylabel('Y轴')# 显示图形
plt.show()

请添加图片描述

3. 绘制柱状图

柱状图常用于比较不同类别的数据。下面是一个绘制柱状图的例子:

# 示例数据
categories = ['A', 'B', 'C', 'D', 'E']
values = [10, 25, 15, 30, 20]# 绘制柱状图
plt.bar(categories, values)# 添加标题和标签
plt.title('简单柱状图')
plt.xlabel('类别')
plt.ylabel('值')# 显示图形
plt.show()

请添加图片描述

4. 绘制饼图

饼图常用于显示不同类别占总量的比例。下面是一个绘制饼图的例子:

# 示例数据
categories = ['A', 'B', 'C', 'D', 'E']
values = [10, 25, 15, 30, 20]# 绘制饼图
plt.pie(values, labels=categories, autopct='%1.1f%%')# 添加标题
plt.title('简单饼图')# 显示图形
plt.show()

请添加图片描述

5. 自定义图形样式

Matplotlib允许我们自定义图形的样式,包括线条颜色、标记类型、图例等。例如:

x = np.arange(0,6, 0.1)
# 绘制sin图像
y1 = np.sin(x)
# 绘制cos图像
y2 = np.cos(x)plt.plot(x, y1, label="sin", color='blue')
# 设置图线样式
plt.plot(x, y2, linestyle="--", color='red', label="cos")
plt.xlabel("x")
plt.ylabel("y")
plt.title("sin & cos")
plt.legend()
plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9jJZ8AKc-1691499932714)(鱼书笔记.assets/image-20230728194801266.png)]

6.绘制其他图形的方法(总结于chatGPT)

plt.plot() # 绘制折线图。plt.scatter() # 绘制散点图。plt.bar() # 绘制柱状图。plt.barh() # 绘制水平柱状图。plt.hist() # 绘制直方图。plt.pie() # 绘制饼图。plt.boxplot() # 绘制箱线图。plt.errorbar() # 绘制误差条形图。plt.contour() # 绘制等高线图。plt.imshow() # 绘制图像。plt.polar() # 绘制极坐标图。plt.stem() # 绘制离散序列的线型图。plt.fill() 和 plt.fill_between() # 绘制填充图。plt.stackplot() # 绘制堆叠区域图。plt.barbs() # 绘制风羽图。plt.quiver() # 绘制场矢量图。plt.streamplot() # 绘制流线图。plt.hexbin() # 绘制六边形二维直方图。

一、感知机

1.感知机原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yayXtuvz-1691499932714)(鱼书笔记.assets/image-20230726224334363.png)]

x1,x2是输入,y是输出,w1,w2是权值,x*w之和超过阀值θ时才会激活y
y = { 0 ( ω 1 x 1 + ω 2 x 2 ≤ θ ) 1 ( ω 1 x 1 + ω 2 x 2 > θ ) y = \begin{cases} 0 \,\,( \omega 1x1 + \omega2x2 \le \theta )\\ 1 \,\,( \omega 1x1 + \omega2x2 > \theta )\\ \end{cases} y={0(ω1x1+ω2x2θ)1(ω1x1+ω2x2>θ)

可将 θ \theta θ 变为-b移到不等式左边,变换为如下表达式。其中 ω 1 \omega1 ω1 ω 2 \omega2 ω2 表示权重(用于控制各个信号的重要性),b表示偏置(用于控制神经元被激活的容易程度)。
y = { 0 ( b + ω 1 x 1 + ω 2 x 2 ≤ 0 ) 1 ( b + ω 1 x 1 + ω 2 x 2 > 0 ) y = \begin{cases} 0 \, \,( b+ \omega 1x1 + \omega2x2 \le 0 )\\ 1 \, \,( b+ \omega 1x1 + \omega2x2 > 0 )\\ \end{cases} y={0(b+ω1x1+ω2x20)1(b+ω1x1+ω2x2>0)

2.简单逻辑电路

与门 AND 代码实现

def AND(x1, x2):x = np.array([x1, x2])w = np.array([0.5, 0.5])b = -0.7tmp = np.sum(w*x) + bif tmp <= 0:return 0else:return 1

或门 OR 代码实现

def OR(x1, x2):x = np.array([x1, x2])w = np.array([0.5, 0.5])b = -0.2tmp = np.sum(w*x) + bif tmp <= 0:return 0else:return 1

与非门 NAND 代码实现

def NAND(x1, x2):x = np.array([x1, x2])w = np.array([-0.5, -0.5])b = 0.7tmp = np.sum(w*x) + bif tmp <= 0:return 0else:return 1

3.多层感知机的实现

单层的感知机,只能划分线性空间,想要实现异或门仅靠单层感知机无法实现,所以借助多层感知机进行非线性的空间划分可以解决异或门无法实现的问题。如下图所示,通过一个与非门,一个或门,一个与门相互连接实现了异或门的功能

请添加图片描述
请添加图片描述

  1. 第0层的两个神经元接收输入信号,并将信号发送至第1层的神经元。
  2. 第1层的神经元将信号发送至第2层的神经元,第2层的神经元输出y。

异或门 代码实现

def XOR(x1, x2):s1 = NAND(x1, x2)s2 = OR(x1, x2)y = AND(s1, s2)return y

二、神经网络

前面设计与或非门的权重值是人工设计的,后续通过学习神经网络,利用已有的数据学习合适的权重作为参数解决上面的权重问题。

请添加图片描述

1.激活函数

请添加图片描述

根据上图的函数转换,我们就能转换为h(x),这就是激活函数

激活函数类型

激活函数分为阶跃函数和sigmoid函数,其中阶跃函数就是当输入值超过某一阀值时就换转变输出。

  1. 阶跃函数

    定义如下
    h ( x ) = { 0 ( x ≤ 0 ) 1 ( x > 0 ) h(x) = \begin{cases} 0 \,\,( x \le 0 )\\ 1 \,\,( x > 0 )\\ \end{cases} h(x)={0(x0)1(x>0)
    代码实现

    import numpy as np
    import matplotlib.pylab as plt# 定义阶跃函数
    def step_function(x):y = x > 0return y.astype(int)X = np.arange(-5.0, 5.0, 0.1)
    Y = step_function(X)
    plt.plot(X, Y)
    plt.ylim(-0.1, 1.1)  # 指定图中绘制的y轴的范围
    plt.show()
    

请添加图片描述

  1. sigmoid函数

    定义如下
    h ( x ) = 1 1 + e x p ( − x ) h(x) = \frac{1}{1 + exp(-x)} h(x)=1+exp(x)1

    代码实现

    # coding: utf-8
    import numpy as np
    import matplotlib.pylab as plt# sigmoid函数
    def sigmoid(x):return 1 / (1 + np.exp(-x))    X = np.arange(-5.0, 5.0, 0.1)
    Y = sigmoid(X)
    plt.plot(X, Y)
    plt.ylim(-0.1, 1.1)
    plt.show()

请添加图片描述

两个激活函数对比

请添加图片描述

  • 共同点:有相似的形状、输入小时输出接近(等于)0,输入大时输出接近(等于)1、输出信号都在0到1之间。
  • 不同点:sigmoid函数是光滑的曲线,阶跃函数是跳跃的折线。

ReLU函数

大于0时直接输出x,小于等于0时输出0
h ( x ) = { x ( x > 0 ) 0 ( x ≤ 0 ) h(x) = \begin{cases} x \,\,( x > 0 )\\ 0 \,\,( x \le 0 )\\ \end{cases} h(x)={x(x>0)0(x0)
代码实现

import numpy as np
import matplotlib.pylab as plt# 定义reLU函数
def relu(x):return np.maximum(0, x)x = np.arange(-5.0, 5.0, 0.1)
y = relu(x)
plt.plot(x, y)
plt.ylim(-1.0, 5.5)
plt.show()

请添加图片描述

2.神经网络的内积

多维数组的运算

二维数组点乘二维数组的运算法则等同于线性代数中学习的矩阵相乘的结果。

使用二维数组点乘一维数组的运算过程中我发现与想象的不太一致。像如下两个数组进行点乘运算,按照线性代数中所学,b矩阵应该要求为2行1列。但使用np.array进行点乘运算结果没有问题。
请添加图片描述

以下总结了二维点乘一维数组的运算规律

请添加图片描述
请添加图片描述

神经网络的内积

请添加图片描述

实现该神经网络时,要注意X、W、Y的形状,特别是X和W的对应维度的元素个数是否一致。

代码实现

请添加图片描述

3层神经网络的实现

请添加图片描述

其中符号的含义
请添加图片描述

  1. 实现第0层到第一层,在上图x1和x2的基础上加上了b1
    请添加图片描述

    用数学式表示 a 1 a_1 a1 如下
    a 1 ( 1 ) = ω 11 ( 1 ) x 1 + ω 12 ( 1 ) x 2 + b 1 ( 1 ) a^{(1)}_1 = \omega^{(1)}_{11}x_1 + \omega^{(1)}_{12}x2 + b^{(1)}_1 a1(1)=ω11(1)x1+ω12(1)x2+b1(1)
    根据矩阵点乘算法规则,那么可以将第一层的加权表示成下面的数学式
    A ( 1 ) = X W ( 1 ) + B ( 1 ) A^{(1)} = XW^{(1)} + B^{(1)} A(1)=XW(1)+B(1)
    请添加图片描述

    代码实现

    X = np.array([1.0, 0.5])
    W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
    B1 = np.array([0.1, 0.2, 0.3])
    A1 = np.dot(X, W1) + B1
    
  2. 实现下图a1到z1激活函数的转变(sigmoid函数)

请添加图片描述

代码实现

Z1 = sigmoid(A1)
print(A1) # [0.3 0.7 1.1]
print(Z1) # [0.57444252 0.66818777 0.75026011]
  1. 同理实现第一层到第二层的传递

请添加图片描述

代码实现

W2 = np.array([[0.1, 0.4],[0.2, 0.5], [0.3, 0.6]])
B2 = np.array([0.1, 0.2])
print(Z1.shape) # (3,)
print(W2.shape) # (3, 2)
print(B2.shape) # (2,)
A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid(A2)
  1. 第2层到第3层(输出层)也跟上面步骤基本一致,但激活函数不同
    请添加图片描述

    代码实现

    # 定义恒等函数
    def identity_function(x):return xW3 = np.array([[0.1, 0.3], [0.2, 0.4]])
    B3 = np.array([0.1, 0.2])
    A3 = np.dot(Z2, W3) + B3
    Y = identity_function(A3)
    

    这里定义的恒等函数,会将输入按照原样输出,这里用恒等函数是为了和前面第0层到第1层和第1层到第2层的处理流程保持一致

  2. 总体代码实现

    def identity_function(x):return x# 权重和偏置的初始化  
    def init_network():network = {}network['W1'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])network['b1'] = np.array([0.1, 0.2, 0.3])network['W2'] = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])network['b2'] = np.array([0.1, 0.2])network['W3'] = np.array([[0.1, 0.3], [0.2, 0.4]])network['b3'] = np.array([0.1, 0.2])return network# 将输入信号转换为输出信号的方法
    def forward(network, x):W1, W2, W3 = network['W1'], network['W2'], network['W3']b1, b2, b3 = network['b1'], network['b2'], network['b3']a1 = np.dot(x, W1) + b1z1 = sigmoid(a1)a2 = np.dot(z1, W2) + b2z2 = sigmoid(a2)a3 = np.dot(z2, W3) + b3y = identity_function(a3)return ynetwork = init_network()
    # 定义两个输入x的初值
    x = np.array([1.0, 0.5])
    y = forward(network, x)
    print(y)  # [0.31682708 0.69627909]
    

感知机中神经元流动的是0或1的二元信号,而神经网络中流动的是连续的实数值信号。

神经网络的激活函数必须使用非线性函数。因为使用线性函数的话,加深神经网络的层数将没有意义

一般而言,对于输出层的激活函数,回归问题用恒等函数,分类问题用softmax函数。

3.输出层的设计

1.三种输出函数的类型

  1. 恒等函数,常用在回归问题上

    def identity_function(x):return x
    
  2. sigmoid函数,用在二元分类问题上
    h ( x ) = 1 1 + e x p ( − x ) h(x) = \frac{1}{1 + exp(-x)} h(x)=1+exp(x)1

    def sigmoid(x):return 1 / (1 + np.exp(-x))    
    
  3. softmax函数,用在多元分类问题上
    y k = e x p ( a k ) ∑ i = 1 n e x p ( a i ) y_k = \frac{exp(a_k)}{\sum_{i=1}^n exp(a_i)} yk=i=1nexp(ai)exp(ak)

    def softmax(a):exp_a = np.exp(a)sum_exp_a = np.sum(exp_a)y = exp_a / sum_exp_areturn y
    

    其中softmax函数表示在各输出之间都有收到输入信号的影响,如图

    请添加图片描述

2.softmax函数溢出改进

之所以要改进softmax函数,是因为计算机所表示的数字是有界限的,比如32位或64位,而 e x e^x ex 可以可以很大,会超过64位所表示数字的最大值,于是对softmax函数进行如下的改进:(1)分子分母同乘以一个常数(2)将常数移到指数函数内部,记为 l o g C logC logC (3)用另一个常数替换 l o g C logC logC(4)实例中常用0减去a数组中的最大值C: $ -C $替换这个 C ′ C' C
请添加图片描述

例子:

请添加图片描述

代码实现改进后的softmax函数

def softmax(a):c = np.max(a)exp_a = np.exp(a - c)sum_exp_a = np.sum(exp_a)y = exp_a / sum_exp_areturn y

3.softmax函数特征

请添加图片描述

我们可以看到输出的y都在0-1之间,且它们的和为1,所以我们可以把他转为概率问题,也就是说输出的越大,他的概率越高,从上图可以看出,输入的a数组元素越大,输出的数组对应元素(即概览)也越大;另外e^x是一个单调递增函数,所以上例中a元素的大小关系和y的大小关系不变,y[2]最大,所以我们在实际上根本不需要softmax函数,直接看a元素就能知道哪个概率最大了(因为softmax需要指数运算,计算量挺大的)

求解机器学习问题的步骤可以分为“学习”和“推理”两个阶段。在学习阶段进行模型的学习,然后,在推理阶段,用学到的模型对未知的数据进行推理(分类)。如前所述,推理阶段一般会省略输出层的softmax函数。在输出层使用softmax函数是因为它和神经网络的学习有关系

4.输出神经元数量

请添加图片描述

由上图可以知道,输出神经元数量由类别数量决定,如输出结果为0-9这10个类别,那么神经元输出则为10个。

4.手写数字识别

三、神经网络的学习

神经网络的学习指的是根据训练数据找出相关权重参数的过程

1.从数据中学习

  1. 数字识别的方案

请添加图片描述

  1. 训练数据和测试数据

    1.训练数据和测试数据:训练数据为监督数据,就是用来训练模型的,而测试数据就是不包含在训练模型内的数据,用来评判训练后模型好坏的数据。

    2.泛化能力:泛化能力其实就是先训练数据训练模型,然后用测试数据进行测试模型,如果测试的成绩好那么他的泛化能力就好。

    3.过拟合:根据训练数据训练出来的模型,他可以很好的处理测试已经训练过的数据,但是对没有测试过的测试数据却无法处理,所以模型和训练数据太过拟合以至于没有很好的泛化能力

2.损失函数

损失函数是用来评判神经网络好坏的一个重要指标,越低越好,一般有2种评判方法均方误差交叉熵误差

one-hot表示法:仅正确标签为1,其余为0

1.均方误差

  • 数学表达式:
    E = 1 2 ∑ k ( y k − t k ) 2 E = \frac{1}{2}\sum_{k}(y_k - t_k)^2 E=21k(yktk)2

    y k y_k yk表示神经网络的输出, t k t_k tk表示监督数据,k表示数据的维数。

  • 代码:

    import numpy as npdef mean_squared_error(y, t):return 0.5 * np.sum((y - t) ** 2)
    
  • 实例:

请添加图片描述

2.交叉熵误差

  • 数学表达式:
    E = − ∑ k ( t k l o g e y k ) E = -\sum_{k} (t_klog_ey_k) E=k(tklogeyk)
    其中log表示以e为底的自然对数, y k y_k yk是神经网络的输出, t k t_k tk是正确解标签。并且 t k t_k tk中只有正确解的索引标签为1,其余为0(one-hot表示)

  • 代码实现:

    def corss_entropy_error(y, t):# 在Python中,1e-7 是一个表示科学计数法的数值,也称为浮点数。它表示的是数字 1 乘以 10 的负7次方,即 0.0000001。科学计数法用于表示非常大或非常小的数值,以便简化表示和处理。在这种情况下,1e-7 表示一个非常接近零的小数值。delta = 1e-7# log表示以e为底数的自然对数return -np.sum(t * np.log(y + delta))
    

    代码如下代码中加上了一个微小值delta,因为当出现np.log(0)时会变为负无限大的-inf,这样会导致后续计算无法进行。添加微小值可以防止负无限大的发生。

    因为只有t为1时才计算,所以计算量比均方误差小,同时log是个负数的单调递增函数,趋向于0,所以y越大则E的结果越趋向于0,那么其误差结果就越小。

  • 实例:

请添加图片描述

从上图可以看到第一个例子正确时概率高,损失函数的结果低,所以他的神经网络模型好。

3.mini-batch学习

E = − 1 N ∑ n ∑ k ( t n k l o g e ( y n k ) ) E=-\frac{1}{N}\sum_n\sum_k(t_{nk} log_e(y_{nk})) E=N1nk(tnkloge(ynk))

这里,假设数据有N个, t n k t_{nk} tnk表示第n个数据的第k个元素的值( y n k y_{nk} ynk是神经网络的输出, t n k t_{nk} tnk是监督数据)。以上表达式就是将N个数据的损失函数的值取平均值。

# 改良交叉熵误差函数的实现
def cross_entropy_error_improved(y, t):# y的维度为1时if y.ndim == 1:t = t.reshape(1, t.size)y = y.reshape(1, y.size)batch_size= y.shape[0]# 以下是t为one-hot表示形式的实现return -np.sum(t * np.log(y + 1e-7)) / batch_size# 以下是标签表示法,np.arange(batch_size)会生成一个0到batch_size-1的数组# return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size

mini-batch简单说就是采取部分样本计算出的结果近似看为整体的计算结果。

在进行神经网络的学习时,不能将识别精度作为指标。因为如果以识别精度为指标,则参数的导数在绝大多数地方都会变为0。

得益于sigmoid函数的斜率不为0,神经网络的学习才得以正确进行。

3.数值微分

1.导数

导数的定义:表示函数某一点的瞬间变化率,数学表达式如下
d f ( x ) d x = lim ⁡ h → 0 f ( x + h ) − f ( x ) h \frac{df(x)}{dx} = \lim_{h\rightarrow0} \frac{f(x+h) - f(x)}{h} dxdf(x)=h0limhf(x+h)f(x)
考虑代码实现求函数的导数,可以将h设置为非常非常小的值,如 1 0 − 50 10^{-50} 1050,则代码如下:

def numerical_diff(f, x):h = 1e-50 # 0.0001return (f(x+h) - f(x)) / h

需改进点:

  • 1 0 − 50 10^{-50} 1050在Python中会产生舍入误差(rounding error)。如下运行的结果

    >>> np.float32(1e-50)
    0.0
    

    使用float32类型的浮点数表示 1 0 − 50 10^{-50} 1050则直接变成了0.0,无法正确表示。所以需要改进这个微小值。这里考虑使用 1 0 − 4 10^{-4} 1041e-4

  • f(x+h)-f(x)/h(向前差分)这个误差也很大,因为根据1的改变,h不是一个趋近于0的数,所以误差变大,应该用中心法改成f(x+h)-f(x-h)/2h(中心差分)
    请添加图片描述

改进后代码:

def numerical_diff(f, x):h = 1e-4 # 0.0001return (f(x+h) - f(x-h)) / (2*h)

注意:这种利用微小差分的导数过程为数值微分,而用数学公式推导的如y=x²导数为y=2x这种交解析性求导,这种叫做真导数

2.一个微分的例子

如:y=0.01x²+0.1x的导数实现

运行结果如下:

请添加图片描述

可以发现改进后的微分代码误差非常小

3.偏导数

一个函数有多个自变量时的导数成为偏导数,表达式 ∂ f ∂ x 0 \frac{\partial f}{\partial x_0} x0f ∂ f ∂ x 1 \frac{\partial f}{\partial x_1} x1f


f ( x 0 , x 1 ) = x 0 2 + x 1 2 f(x_0, x_1) = x_0^2 + x_1^2 f(x0,x1)=x02+x12
使用matplotlib绘制的图像如下

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D# 创建数据点
x0 = np.linspace(-10, 10, 100)
x1 = np.linspace(-10, 10, 100)
# 使用 np.meshgrid 函数可以将这两个一维数组转换为两个二维数组 x0 和 x1,这将构成我们的网格。
x0, x1 = np.meshgrid(x0, x1)
f = x0**2 + x1**2# 创建 3D 图像
fig = plt.figure()
# projection='3d' 指定这是一个三维图像
ax = fig.add_subplot(111, projection='3d')# 绘制曲面
# cmap='viridis' 指定了颜色映射,这里使用了 Viridis 颜色映射
ax.plot_surface(x0, x1, f, cmap='viridis')# 设置轴标签
ax.set_xlabel('x0')
ax.set_ylabel('x1')
ax.set_zlabel('f(x0, x1)')# 显示图像
plt.show()

请添加图片描述

偏导数实现:原理其实跟一元导数一样,就是带入一个真值消除一个变量而已

请添加图片描述

4.梯度

由全部变量的偏导数汇总而成的向量( ∂ f ∂ x 0 \frac{\partial f}{\partial x_0} x0f ∂ f ∂ x 1 \frac{\partial f}{\partial x_1} x1f)称为梯度

比如我们求一个函数y=x0²+x1²变量有x0,x1,当我们对他全部变量(这里最多只有2个)进行偏导汇总而成的变量叫梯度。

实现梯度的代码如下

# 实现梯度
def numerical_gradient(f, x):h = 1e-4grad = np.zeros_like(x)for idx in range(x.size):tem_val = x[idx]# f(x + h) 的计算x[idx] = tem_val + hfxh1 = f(x)# f(x - h) 的计算x[idx] = tem_val - hfxh2 = f(x)grad[idx] = (fxh1 - fxh2) / (2 * h)x[idx] = tem_val # 还原倍数return grad

请添加图片描述

从这个图可以看出,梯度指向函数 f ( x 0 , x 1 ) f(x_0,x_1) f(x0,x1)的最低处(最小值),就像指南针一样,所有的箭头都指向同一点。其次我们发现,离“最低处”越远,箭头越大。梯度指示的方向是各点处的函数值减小最多的方向,这是一个重要的性质

1.梯度法

在梯度法中,函数的取值从当前位置沿着梯度方向前进一段距离,然后在新的地方重新求梯度,再沿着新梯度方向前进,如此反复,不断地沿梯度方向前进。像这样不断的沿梯度方向前进,逐渐减小函数值的过程就是梯度法,用数学表达式来表示则如下所示

x 0 = x 0 − η ∂ f ∂ x 0 x_0 = x_0 - \eta\frac{\partial f}{\partial x_0} x0=x0ηx0f

x 1 = x 1 − η ∂ f ∂ x 1 x_1 = x_1 - \eta\frac{\partial f}{\partial x_1} x1=x1ηx1f

其中, η \eta η表示更新量,在神经网络的学习中,称为学习率。学习率决定在一次更新中更新的程度。

梯度下降算法代码实现:

# 梯度下降法找最小值
def gradient_descent(f, init_x, lr=0.01, step_num=100):x = init_xfor i in range(step_num):grad = numerical_gradient(f, x)x -= lr * gradreturn x

用梯度法求函数 f ( x 0 , x 1 ) = x 0 2 + x 1 2 f(x_0, x_1) = x_0^2 + x_1^2 f(x0,x1)=x02+x12的最小值如下

请添加图片描述

最终结果为(-6.11110793e-10 8.14814391e-10),非常接近(0,0)。实际上,真的最小值就是(0, 0)。所以说通过梯度法我们基本得到了正确结果。用图示来表示梯度法的更新过程则如下:

请添加图片描述

学习率 η \eta η不可过大也不可过小,太大时结果会发散成很大的数,太小的话结果几乎没更新就结束了

像学习率这样的参数称为超参数。这是一种和神经网络的参数(权重和偏置)性质不同的参数。相对于神经网络的权重参数是通过训练数据和学习算法自动获得的,学习率这样的超参数则是人工设定的。一般来说,超参数需要尝试多个值,以便找到一种可以使学习顺利进行的设定。

2.神经网络的梯度

我们有2*3的W权重参数,L为损失函数,梯度用 ∂ L ∂ W \frac{\partial L}{\partial W} WL表示,如下所示
W = { ω 11 ω 12 ω 13 ω 21 ω 22 ω 23 } W = \begin{Bmatrix} \omega_{11}&\omega_{12}&\omega_{13}\\ \omega_{21}&\omega_{22}&\omega_{23}\\ \end{Bmatrix} W={ω11ω21ω12ω22ω13ω23}

∂ L ∂ W = { ∂ L ∂ ω 11 ∂ L ∂ ω 12 ∂ L ∂ ω 13 ∂ L ∂ ω 21 ∂ L ∂ ω 22 ∂ L ∂ ω 23 } \frac{\partial L}{\partial W} = \begin{Bmatrix} \frac{\partial L}{\partial \omega_{11}}&\frac{\partial L}{\partial \omega_{12}}&\frac{\partial L}{\partial \omega_{13}}\\ \frac{\partial L}{\partial \omega_{21}}&\frac{\partial L}{\partial \omega_{22}}&\frac{\partial L}{\partial \omega_{23}}\\ \end{Bmatrix} WL={ω11Lω21Lω12Lω22Lω13Lω23L}

∂ L ∂ W \frac{\partial L}{\partial W} WL的元素由各个元素关于W的偏导数构成。比如,第一行第一列的元素 ∂ L ∂ ω 11 \frac{\partial L}{\partial \omega_{11}} ω11L表示当 ω 11 \omega_{11} ω11稍微变化时,损失函数L会发生多大变化。这里的重点是 ∂ L ∂ W \frac{\partial L}{\partial W} WL的形状和W相同。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Go语言】Golang保姆级入门教程 Go初学者chapter2

【Go语言】变量 VSCode插件 setting的首选项 一个程序就是一个世界 变量是程序的基本组成单位 变量的使用步骤 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zuxG8imp-1691479164956)(https://cdn.staticaly.com/gh/hudiework/imgmain/image-20…

解决MySQL与Redis缓存一致性的问题

背景 考试系统中&#xff0c;教师会在后台发布一场考试&#xff0c;考试会存储在MySQL和Redis里面&#xff0c;考试有时候是会出错的&#xff0c;我们需要后台修改&#xff0c;如果多个教师在后台并发修改&#xff08;概率不大&#xff09;&#xff0c;可能会出现数据库缓存不…

Linux shell yes命令(不停输出换行的y)(不停输出换行的指定字符串)(脚本自动确认y)

文章目录 yes命令功能doc文档英文中文翻译完整文档 示例应用案例自动为脚本多次确认y yes命令功能 yes命令可以不断地输出换行的指定字符串&#xff0c;不加参数时&#xff0c;不断输出换行的“y”&#xff0c;有时我们需要执行一些需要用户键入“y”确认的脚本&#xff0c;但…

Mysql中如果建立了索引,索引所占的空间随着数据量增长而变大,这样无论写入还是查询,性能都会有所下降,怎么处理?

索引所占空间的增长确实会对MySQL数据库的写入性能和查询性能造成影响&#xff0c;这主要是由于索引数据过多时会导致磁盘I/O操作变得非常频繁&#xff0c;从而使性能下降。为此&#xff0c;可以采取以下几种方式来减缓这种影响&#xff1a; 1. 限制索引的大小&#xff1a;可以…

Oracle-创建PDB

Oracle-创建PDB 创建PDB的方式 从PDB$SEED新建PDB克隆已存在的PDB 本地PDB克隆到同一个CDB中将远程PDB克隆到CDB中将非CDB插入或克隆到CDB中通过插拔的方式创建PDB sql 命令语法 条件 CDB必须open并且read write模式连接CDB$ROOT 用户并且具有CREATEPLUGGABLEDATABASE系统权…

Linux 使用gdb调试C程序

一、gdb的一些基础命令 l&#xff1a;显示代码 l n&#xff1a;跳转到当前代码页的第n行的代码 l filename.c &#xff1a;n&#xff1a;跳转到filename.c文件的第n行代码 b 行号&#xff1a;加断点 info break&#xff1a;查看断点信息 delete 断点编号&#xff1a;删除断点 …

IoTDB原理剖析

一、介绍 IoTDB&#xff08;物联网数据库&#xff09;是一体化收集、存储、管理与分析物联网时序数据的软件系统。 Apache IoTDB采用轻量式架构&#xff0c;具有高性能和丰富的功能。 IoTDB从存储上对时间序列进行排序&#xff0c;索引和chunk块存储&#xff0c;大大的提升时序…

Python爬虫如何更换ip防封

作为一名长期扎根在爬虫行业动态ip解决方案的技术员&#xff0c;我发现很多人常常在使用Python爬虫时遇到一个困扰&#xff0c;那就是如何更换IP地址。别担心&#xff0c;今天我就来教你如何在Python爬虫中更换IP&#xff0c;让你的爬虫不再受到IP封锁的困扰。废话不多说&#…

linux安装wkhtmltopdf(清晰明了)

概述 在公司项目中使用到 wkhtmltopdf 转换PDF&#xff0c;由于 wkhtmltox-0.12.5 版本 echarts 图形虚线样式&#xff0c;需要升级 wkhtmltox-0.12.6 版本来解决。 官网地址 wkhtmltopdf &#xff1a;https://wkhtmltopdf.org/ windows 安装 下载流程及安装流程 进入官…

gazebo 导入从blender导出的dae等文件

背景&#xff1a; gazebo 模型库里的模型在我需要完成的任务中不够用&#xff0c;还是得从 solidworks、3DMax, blender这种建模软件里面在手动画一些&#xff0c;或者去他们的库里面在挖一挖。 目录 1 blender 1-1 blender 相关links 1-2 install 2 gazebo导入模型 2-1 g…

开封Geotrust单域名https证书推荐

Geotrust作为全球领先的数字证书颁发机构之一&#xff0c;拥有多年的数字证书颁发经验&#xff0c;其数字证书被广泛应用于电子商务、在线支付、企业通讯、云计算等领域&#xff0c;为用户提供了安全可靠的保障。而Geotrust旗下的单域名https证书是大多数客户创建网站时的选择之…

技术应用:Docker安全性的最佳实验|聊聊工程化Docker

&#x1f525; 技术相关&#xff1a;《技术应用》 ⛺️ I Love you, like a fire! 文章目录 首先&#xff0c;使用Docker Hub控制访问其次&#xff0c;保护密钥写在最后 不可否认&#xff0c;能生存在互联网上的软件都是相互关联的&#xff0c;当我们开发一款应用程序时&#x…

如何设计一个高性能/高并发/高可用/高可靠/可扩展的系统?

作者&#xff1a;阿秀 校招八股文学习网站&#xff1a;https://interviewguide.cn 这是阿秀的第「293」篇原创 小伙伴们大家好&#xff0c;我是阿秀。 面试者和求职者的关系就好像是矛与盾&#xff0c;一个拼命堆自己的防装&#xff0c;反伤刺甲、魔女斗篷都往身上穿&#xff1…

java获取到heapdump文件后,如何快速分析?

简介 在之前的OOM问题复盘之后&#xff0c;本周&#xff0c;又一Java服务出现了内存问题&#xff0c;这次问题不严重&#xff0c;只会触发堆内存占用高报警&#xff0c;没有触发OOM&#xff0c;但好在之前的复盘中总结了dump脚本&#xff0c;会在堆占用高时自动执行jstack与jm…

MD-MTSP:星雀优化算法NOA求解多仓库多旅行商问题MATLAB(可更改数据集,旅行商的数量和起点)

一、星雀优化算法NOA 星雀优化算法(Nutcracker optimizer algorithm,NOA)由Mohamed Abdel-Basset等人于2023年提出&#xff0c;该算法模拟星雀的两种行为&#xff0c;即&#xff1a;在夏秋季节收集并储存食物&#xff0c;在春冬季节搜索食物的存储位置。星雀优化算法(Nutcrack…

WPS Office 代码执行漏洞(QVD-2023-17241)

目录 本地利用弹计算器&#xff08;自娱自乐&#xff09; 原理分析 msf的利用 1.修改win11中的hosts文件 2.MSF生成一个C#后门 3.shellcode替换 4.在创建html的目录&#xff0c;用python打开http服务来捕获请求 5.开启监听 6.在win11中点击poc文档&#xff0c;可以看到k…

LeetCode算法递归类—验证二叉搜索树

目录 98. 验证二叉搜索树 题解&#xff1a; 代码&#xff1a; 运行结果&#xff1a;​编辑 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含…

​LeetCode解法汇总1572. 矩阵对角线元素的和

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 描述&#xff1a; 给你一个正…

nodejs实现解析chm文件列表,无需转换为PDF文件格式,在线预览chm文件以及目录,不依赖任何网页端插件

特性&#xff1a; 1、支持任意深度的chm文件解析 2、解析后内容结构转换为tree数据呈现 3、点击树节点可以在html实时查看数据 4、不依赖任何浏览器端插件&#xff0c;兼容性较好 nodejs端核心代码 const $g global.SG.$g, fs global.SG.fs, router global.SG.router, xl…

【资料分享】全志科技T507-H工业核心板规格书

1 核心板简介 创龙科技SOM-TLT507是一款基于全志科技T507-H处理器设计的4核ARM Cortex-A53全国产工业核心板&#xff0c;主频高达1.416GHz。核心板CPU、ROM、RAM、电源、晶振等所有元器件均采用国产工业级方案&#xff0c;国产化率100%。 核心板通过邮票孔连接方式引出MIPI C…