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

news2024/11/25 12:43:14

零、预知识

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.7
    tmp = np.sum(w*x) + b
    if tmp <= 0:
        return 0
    else:
        return 1

或门 OR 代码实现

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

与非门 NAND 代码实现

def NAND(x1, x2):
    x = np.array([x1, x2])
    w = np.array([-0.5, -0.5])
    b = 0.7
    tmp = np.sum(w*x) + b
    if tmp <= 0:
        return 0
    else:
        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 > 0
        return 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 x
    
    W3 = 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) + b1
        z1 = sigmoid(a1)
        a2 = np.dot(z1, W2) + b2
        z2 = sigmoid(a2)
        a3 = np.dot(z2, W3) + b3
        y = identity_function(a3)
    
        return y
    
    network = 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_a
    
        return 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_a

    return 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 np
    
    def 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.0001
  return (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.0001
  return (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-4
    grad = np.zeros_like(x)

    for idx in range(x.size):
        tem_val = x[idx]
        # f(x + h) 的计算
        x[idx] = tem_val + h
        fxh1 = f(x)

        # f(x - h) 的计算
        x[idx] = tem_val - h
        fxh2 = 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_x
    for i in range(step_num):
        grad = numerical_gradient(f, x)
        x -= lr * grad
        
    return 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.coloradmin.cn/o/849586.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关于C++静态代码扫描工具及基于jenkins流水线搭建vs报告分析工具开发的小总结

静态代码扫描&#xff09; 主要内容&#xff1a;Tscancode 报告解析插件使用1.Tscancode linux使用命令介绍2.插件jar包3.tscancode扫描生成的文件4.解析报告插件执行方式与参数说明5.解析后生成报告样式 CPPCHECK报告解析1.cppcheck 代码扫描linux命令2.解析报告插件使用 报告…

ArduPilot开源飞控之Companion Computers简单分析

ArduPilot开源飞控之Companion Computers简单分析 1. 源由2. 伴机系统2.1 APSync2.2 DroneKit2.3 FlytOS2.4 Maverick2.5 ROS2.6 Rpanion-server 3. 总结4. 参考资料 1. 源由 从稳定性&#xff0c;社区群体&#xff0c;以及开源方式的角度看&#xff0c;Ardupilot是不错的选择…

【Vue】使用print.js插件实现打印预览功能,超简单

目录 一、实现效果 二、实现步骤 【1】安装插件 【2】在需要打印的页面导入 【3】在vue文件中需要打印的部分外层套一层div&#xff0c;给div设置id。作为打印的区域 【4】在打印按钮上添加打印事件 【5】在methods中添加点击事件 三、完整代码 一、实现效果 二、实现步…

647. 回文子串

目录 一、题目 二、代码 一、题目 二、代码 双指针&#xff01;&#xff01;&#xff01; class Solution { public:bool Palindrome(string s,int i,int j){int begini;int end j;while(begin<end){if(s[begin]!s[end]){return false;}begin;end--;}return true;}int …

IPWorks S3 Delphi Edition Crack

IPWorks S3 Delphi Edition Crack IPWorksS3使集成基于云的文件存储变得容易。易于使用的组件可用于与任何S3兼容的存储提供商集成&#xff0c;如Amazon S3、Digital Ocean Spaces、Wasabi、Backblaze B2、IBM Cloud Object storage、Oracle Cloud、Linode等。强大的客户端加密…

解决Vue+Element-UI 进行From表单校验时出现了英文提示问题

说明&#xff1a;该篇博客是博主一字一码编写的&#xff0c;实属不易&#xff0c;请尊重原创&#xff0c;谢谢大家&#xff01; 问题描述 在使用form表单时&#xff0c;往往会对表单字段进行校验&#xff0c;字段为必填项时会添加required属性&#xff0c;此时自定义rules规则…

vue中全局状态存储 pinia和vuex对比 pinia比vuex更香 Pinia数据持久化及数据加密

前言 毕竟尤大佬都推荐使用pinia&#xff0c;支持vue2和vue3&#xff01; 如果熟悉vuex&#xff0c;花个把小时把pinia看一下&#xff0c;就不想用vuex了 支持选项式api和组合式api写法pinia没有mutations&#xff0c;只有&#xff1a;state、getters、actionspinia分模块不…

20个程序员接单平台分享

这题我会&#xff01;接单软件那么多&#xff0c;找到适合自己的最重要&#xff01; V2EX https://www.v2ex.com/ 先给一个“非正常选项”&#xff0c;v2ex上有一个“酷工作”板块&#xff0c;运气好的话可以在这里找到不错的单子&#xff0c;最重要的是带你开启新世界的大门…

新型声学攻击通过键盘击键窃取数据,准确率高达 95%

来自英国大学的一组研究人员训练了一种深度学习模型&#xff0c;该模型可利用麦克风记录并分析键盘击键的声音&#xff0c;以此来窃取目标设备中的数据&#xff0c;准确率高达 95%。 不同于其他需要特殊条件并受到数据速率和距离限制的旁道攻击&#xff0c;由于现有大量场景都拥…

LeetCode:Hot100的python版本

94. 二叉树的中序遍历

Python爬虫的Selenium(学习于b站尚硅谷)

目录 一、Selenium  1.为什么要学习Selenium  &#xff08;1&#xff09;什么是Selenium  &#xff08;2&#xff09;为什么使用selenium?  &#xff08;3&#xff09;代码演示 2. selenium的基本使用  &#xff08;1&#xff09;如何安装selenium  &#xff08;2…

jmeter如何压测和存储

一、存储过程准备&#xff1a; 1、建立一个空表&#xff1a; 1 CREATE TABLE test_data ( id NUMBER, name VARCHAR2(50), age NUMBER ); 2、建立一个存储过程&#xff1a; 1 2 3 4 5 6 7 8 9 CREATE OR REPLACE PROCEDURE insert_test_data (n IN NUMBER) AS BEGIN --E…

【工程优化问题】基于多种智能优化算法的压力容器设计问题研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Linux内核应该怎么去学习?

通过阅读源码来学习操作系统要注意区分共性与平台特性。 1. 中断响应是共性&#xff0c;8259 中断控制器和 IDT 是 x86 的特性。 2. 虚拟内存管理是共性&#xff0c;x86 的 GDT 和 LDT 是特性&#xff0c;而且现在的系统也只是走个过场而已。 3. 任务调度与上下文切换是共性&am…

44.实现爱尔兰B公式计算并输出表格(matlab程序)

1.简述 1.话务量定义 话务量指在一特定时间内呼叫次数与每次呼叫平均占用时间的乘积。 话务量反映了电话负荷的大小&#xff0c;与呼叫强度和呼叫保持时间有关。呼叫强度是单位时间内发生的呼叫次数&#xff0c;呼叫保持时间也就是占用时间。 话务量计算方法 话务量公式为…

智安网络|恶意软件在网络安全中的危害与应对策略

恶意软件是指一类具有恶意目的的软件程序&#xff0c;恶意软件是网络安全领域中的一个严重威胁&#xff0c;给个人用户、企业和整个网络生态带来巨大的危害。通过潜伏于合法软件、邮件附件、下载链接等途径传播&#xff0c;破坏用户计算机系统、窃取敏感信息、进行勒索等不法行…

C语言经典小游戏之扫雷(超详解释+源码)

“少年气&#xff0c;是历尽千帆举重若轻的沉淀&#xff0c;也是乐观淡然笑对生活的豁达&#xff01;” 今天我们学习一下扫雷游戏怎么用C语言来实现&#xff01; 扫雷小游戏 1.游戏介绍2.游戏准备3.游戏实现3.1生成菜单3.2游戏的具体实现3.2.1初始化棋盘3.2打印棋盘3.3布置雷…

Linux root用户执行修改密码命令,提示 Permission denied

问题 linux系统中&#xff08;ubuntu20&#xff09;&#xff0c;root用户下执行passwd命令&#xff0c;提示 passwd: Permission denied &#xff0c;如下图&#xff1a; 排查 1.执行 ll /usr/bin/passwd &#xff0c;查看文件权限是否正确&#xff0c;正常情况是 -rwsr-xr…

VAE、 EM、KL散度

文章目录 VAEVAE额外的损失函数 EMKL散度 VAE 左图相当于变量x&#xff0c;右图相当于z 假如在AE中&#xff0c;一张满月的图片作为输入&#xff0c;模型得到的输出是一张满月的图片&#xff1b;一张弦月的图片作为输入&#xff0c;模型得到的是一张弦月的图片。当从满月的code…

SpringBoot复习:(22)ConfigurationProperties和@PropertySource配合使用及JSR303校验

一、配置类 package cn.edu.tju.config;import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component;Component ConfigurationPropertie…