WikiWiki
首页
Java开发
Java面试
Linux手册
  • AI相关
  • Python Flask
  • Pytorch
  • youlo8
SEO
uniapp小程序
Vue前端
work
数据库
软件设计师
入门指南
首页
Java开发
Java面试
Linux手册
  • AI相关
  • Python Flask
  • Pytorch
  • youlo8
SEO
uniapp小程序
Vue前端
work
数据库
软件设计师
入门指南

AI数学基础

引导

微积分

激活函数作图 展示二维图像切线 展示三维函数切面 神经网络反向传播 制作梯度下降求最小值的动画 实现三维平面的梯度下降

image-20250303201643407

线性代数

创建矩阵 将矩阵保存成图片 图像亮度增强 图像对比度增强 矩阵基本运算 计算特征值与特征向量 图像的SVD压缩

image-20250303201949095

概率论

使用Python模拟随机试验 概率质量函数示意图 求期望、方差、协方差 模拟常见的概率分布 模拟大数定律 模拟中心极限定理

image-20250303202353135

1.线性代数

①基本概念

  • 标量:标量是只有大小没有方向的量。在数学中,它通常是一个实数或复数。

    • 通俗:想象你有5个苹果。这个数字"5"就是一个标量。它只告诉你有多少苹果,但不告诉你这些苹果放在哪里。
  • 向量:向量是具有大小和方向的量。在数学中,它通常表示为一个有序的数字列表,如二维或三维空间中的点。

    • 通俗:想象你在一个大房间里,你想告诉你的朋友一个宝藏的位置。你可以说:“从门口开始,走5步向前,然后走3步向右。”这个“5步向前和3步向右”就像一个向量,它有方向和距离。
  • 矩阵:矩阵是一个二维数组,其中的每个元素都是一个标量。它可以表示线性变换。

    • 通俗:想象你有一个秘密地图,上面有很多宝藏的位置。为了整齐地记录所有的位置,你决定用一个网格或表格来写下所有的“向前和向右”的步数。这个表格就像一个矩阵,它帮助你整齐地记录信息。
  • 张量:张量是一个可以在多个方向上有分量的数学对象。标量是零阶张量,向量是一阶张量,矩阵是二阶张量。更高阶的张量可以看作是多维数组。

    • 通俗:想象你不仅在一个房间里找宝藏,而且在一个大楼里。这时,你不仅要告诉你的朋友向前走多少步、向右走多少步,还要告诉他上或下走多少楼梯。如果你用一个更大的表格来记录这些信息,其中包括向前、向右和上下的步数,这就像一个张量。张量就是一个更高级的表格,可以记录更多的信息。

案例实践1

  • 创建向量、矩阵、张量

    • 向量
    # 导入numpy库,并将其简称为np,方便后续调用
    import numpy as np
    
    # 创建一个一维数组(向量),使用np.array()函数
    vector = np.array([1, 2, 3, 4, 5])  # 定义一个包含5个元素的一维向量 [1, 2, 3, 4, 5]
    # 计算向量的L2范数(即欧几里得范数)
    # np.linalg.norm() 是 NumPy 提供的一个用于计算向量或矩阵范数的函数
    # 默认情况下,如果不指定参数 ord,它会计算 L2 范数(即向量各元素平方和的平方根)
    vector_norm = np.linalg.norm(vector)
    # 打印向量的内容
    print("Vector:\n", vector)  # 输出向量本身,\n 表示换行,使输出更清晰
    # 打印向量的L2范数
    print("Vector L2 Norm:", vector_norm)  # 输出向量的L2范数值
    
    • 矩阵
    # 导入 numpy 库,并将其简称为 np,方便后续调用
    import numpy as np
    
    # 创建一个二维数组(矩阵),使用 np.array() 函数
    matrix = np.array([[1, 2, 3],  # 定义一个 3x3 的矩阵
                       [4, 5, 6],
                       [7, 8, 9]])
    
    # 计算矩阵的 L2 范数(Frobenius 范数)
    # np.linalg.norm() 是 NumPy 提供的一个用于计算向量或矩阵范数的函数
    # Frobenius 范数是矩阵中所有元素平方和的平方根
    matrix_norm = np.linalg.norm(matrix)
    
    # 打印矩阵的内容
    print("Matrix:\n", matrix)  # 输出矩阵本身
    
    # 打印矩阵的 L2 范数(Frobenius 范数)
    print("Matrix L2 Norm (Frobenius Norm):", matrix_norm)  # 输出矩阵的 Frobenius 范数值
    
    • 张量
    # 导入 numpy 库,并将其简称为 np,方便后续调用
    import numpy as np
    
    # 创建一个三维张量(3D Tensor),使用 np.array() 函数
    # 这里我们创建一个 3x2x2 的三维张量
    tensor = np.array([
        [[1, 2], [3, 4]],  # 第一个 2x2 矩阵
        [[5, 6], [7, 8]],  # 第二个 2x2 矩阵
        [[9, 10], [11, 12]]  # 第三个 2x2 矩阵
    ])
    
    # 计算张量的 L2 范数(Frobenius 范数)
    # np.linalg.norm() 是 NumPy 提供的一个用于计算向量或矩阵范数的函数
    # 默认情况下,如果不指定参数 ord,对于多维数组来说它会计算 Frobenius 范数
    # Frobenius 范数是张量中所有元素平方和的平方根
    tensor_norm = np.linalg.norm(tensor)
    
    # 打印张量的内容
    print("Tensor:\n", tensor)  # 输出张量本身,
    
    # 打印张量的 L2 范数(Frobenius 范数)
    print("Tensor L2 Norm (Frobenius Norm):", tensor_norm)  # 输出张量的 Frobenius 范数值
    

案例实践2

  • 将Numpy矩阵保存成本地图像

    import numpy as np
    import cv2
    
    # 创建一个大小为 224x224 的二维矩阵,值在 0~255 之间
    # 使用 np.random.randint() 函数生成随机整数矩阵,dtype=np.uint8 表示数据类型为无符号 8 位整数
    two_d_matrix = np.random.randint(0, 256, (224, 224), dtype=np.uint8)
    
    # 创建一个大小为 3x224x224 的三维矩阵,值在 0~255 之间
    # 这个矩阵可以看作是一个 RGB 图像的三个通道(红、绿、蓝)
    three_d_matrix = np.random.randint(0, 256, (3, 224, 224), dtype=np.uint8)
    
    # 在 OpenCV 中,图像的通道顺序是 高 x 宽 x 通道数 (HxWxC),而我们创建的 three_d_matrix 是 通道数 x 高 x 宽 (CxHxW)
    # 因此需要使用 transpose() 方法调整维度顺序
    three_d_matrix_transposed = three_d_matrix.transpose(1, 2, 0)  # 调整为 HxWxC 的格式
    
    # 将二维矩阵作为灰度图像显示出来
    cv2.imshow(winname='two_d_matrix', mat=two_d_matrix)  # 使用 cv2.imshow() 显示图像,winname 是窗口名称,mat 是要显示的图像
    
    # 将三维矩阵作为彩色图像显示出来
    cv2.imshow(winname='three_d_matrix', mat=three_d_matrix_transposed)  # 同样使用 cv2.imshow() 显示图像
    
    # 等待用户按键,参数 0 表示无限等待,直到按下任意键
    cv2.waitKey(0)
    
    # 关闭所有 OpenCV 创建的窗口,防止资源泄漏
    cv2.destroyAllWindows()
    
    #使用0penCV将图片保存到本地
    cv2.imwrite("two_d_matrix.jpg",two_d_matrix)
    cv2.imwrite("three_d_matrix.jpg",three_d_matrix_transposed)
    

②矩阵基础运算

  • 标量与矩阵的乘法

    image-20250303205050067

  • 图像增强(调整对比度)

    image-20250303205157969

实践案例3

  • 图像增强(调整亮度)

    import cv2
    import numpy as np
    
    # 读取图像
    # 使用 cv2.imread() 函数读取图片文件,确保图片路径正确
    image = cv2.imread('222.png')
    
    # 检查图像是否成功加载
    if image is None:
        print("Error: Could not load image.")
    else:
        # 增加对比度
        # 使用 cv2.convertScaleAbs() 函数调整图像的对比度和亮度
        # alpha 控制对比度(>1 增加对比度,<1 减少对比度),beta 控制亮度(这里未使用)
        increased_contrast = cv2.convertScaleAbs(image, alpha=1.5, beta=0)
    
        # 减少对比度
        decreased_contrast = cv2.convertScaleAbs(image, alpha=0.5, beta=0)
    
        # 将图片进行横向拼接
        # 使用 cv2.hconcat() 函数将三张图片水平拼接在一起
        combined_image = cv2.hconcat([image, increased_contrast, decreased_contrast])
    
        # 显示图片
        # 使用 cv2.imshow() 显示拼接后的图片
        cv2.imshow(winname='Original - Increased Contrast - Decreased Contrast', mat=combined_image)
    
        # 等待用户按键
        # 参数 0 表示无限等待,直到按下任意键
        cv2.waitKey(0)
    
        # 关闭所有 OpenCV 创建的窗口,防止资源泄漏
        cv2.destroyAllWindows()
    

实践案例4

  • 用代码完成下面的操作(矩阵的基础运算)

    image-20250303210659185

    import numpy as np
    
    # 定义矩阵 A 和 B
    # 使用 np.array() 创建两个 2x2 的矩阵
    A = np.array([[1, 2],  # 矩阵 A
                  [3, 4]])
    
    B = np.array([[5, 6],  # 矩阵 B
                  [7, 8]])
    
    # 定义标量 C
    C = 3  # 标量值为 3
    
    # 标量与矩阵的乘法
    # 将标量 C 与矩阵 A 相乘,结果是矩阵 A 的每个元素都乘以标量 C
    scalar_matrix_multiplication = C * A
    
    # 矩阵的加法
    # 将矩阵 A 和矩阵 B 对应位置的元素相加,得到一个新的矩阵
    matrix_addition = A + B
    
    # 打印标量与矩阵乘法的结果和矩阵加法的结果
    print("Scalar Matrix Multiplication:\n", scalar_matrix_multiplication)
    print("Matrix Addition:\n", matrix_addition)
    

解方程组

image-20250303211158038

import numpy as np

# 创建系数矩阵 A
# 使用 np.array() 创建一个 3x3 的系数矩阵 A
A = np.array([[1, 2, 3],  # 矩阵 A 的第一行
              [1, 6, 7],  # 矩阵 A 的第二行
              [1, 10, 6]])  # 矩阵 A 的第三行

# 创建常数向量 b
# 使用 np.array() 创建一个长度为 3 的常数向量 b
b = np.array([5, 9, 8])  # 常数向量 b

# 解方程 Ax = b
# 使用 np.linalg.solve() 函数求解线性方程组
# 这里假设 A 是一个非奇异矩阵(即可逆矩阵),并且 A 和 b 的维度匹配
x = np.linalg.solve(A, b)

# 打印结果
print("解:", x)  # 输出方程组的解

特征向量与特征值

  • 特征值:与特征向量相对应的标量入称为特征值。它表示在特定的特征向量方向上,变换的缩放比例。

    • 想象一个拉伸机,你放入一根箭头,拉伸机的作用下,这根箭头可能会被拉长、缩短或者保持原长,但方向不变。这个“拉伸”或者“缩短”的倍数,就是特征值。特征值反映了这个变换增强或减弱了向量的程度。
  • 特征向量:如果一个非零向量v在一个线性变换下的效果仅仅是被伸缩(缩放或拉伸),而方向不变,那么这个向量被称为特征向量。

    • 那根被拉伸后方向不变的箭头,就是特征向量。它是一个特殊的向量,它在矩阵变换下只是被伸缩,但不改变其方向。可以指明变换,的主要方向
  • 求特征值与特征向量

    image-20250303212435570

    import numpy as np
    
    # 定义矩阵 A
    # 使用 np.array() 创建一个 2x2 的矩阵 A
    A = np.array([[4, 1],  # 矩阵 A 的第一行
                  [2, 3]])  # 矩阵 A 的第二行
    
    # 使用 NumPy 求特征值和特征向量
    # np.linalg.eig() 函数用于计算方阵的特征值和特征向量
    # 返回两个结果:
    # eigenvalues 是包含特征值的一维数组
    # eigenvectors 是包含特征向量的二维数组,每一列是一个特征向量
    eigenvalues, eigenvectors = np.linalg.eig(A)
    
    # 打印特征值
    print("特征值:", eigenvalues)
    
    # 如果需要,也可以打印特征向量
    print("特征向量:\n", eigenvectors)
    

    图像的SVD分解

    对一幅图像进行奇异值分解(SVD)可以将其分解为三个部分:U矩阵、Σ矩阵和V矩阵。每一部分都有其特定的意义和作用:

    1. U矩阵: U矩阵包含了“左奇异向量”,可以理解为一组基础的图像模式。这些模式类似于构建图像的基本组件,从某种角度描述了图像的空间特征。
    2. Σ矩阵: Σ矩阵是一个对角矩阵,包含了“奇异值”。这些奇异值表示每个图像模式的重要性或权重。较大的奇异值对应于图像中更重要的特征,而较小的奇异值则表示次要或细节信息。
    3. V矩阵: V矩阵包含了“右奇异向量”,与U矩阵类似,但它是从另一个角度描述图像的特征。它可以看作是图像在不同方向上的投影。

    通过SVD分解,图像被转化为一系列模式及其对应的权重。这种分解方式使得我们可以用较少的数据来描述一幅图像,同时保留其关键特征。这种方法在图像压缩、降噪和特征提取等领域具有广泛的应用价值。

    • 对图像进行SVD分解

      image-20250303213227780

    import cv2
    import numpy as np
    
    # 读取图像
    # 使用 cv2.imread() 函数读取图像,flags=0 表示以灰度模式读取
    image = cv2.imread(filename='robot.bmp', flags=0)
    
    # 检查图像是否成功加载
    if image is None:
        print("Error: Could not load image.")
    else:
        # 对图像进行 SVD 分解
        # 将图像转换为浮点数类型(np.float64),以避免精度损失
        # full_matrices=False 表示只返回缩小版的 U 和 V 矩阵
        U, s, V = np.linalg.svd(image.astype(np.float64), full_matrices=False)
    
        # 定义要保留的奇异值数量
        k = 10
    
        # 构造对角矩阵,仅保留前 k 个奇异值
        s_k = np.diag(s[:k])
    
        # 重构图像
        # 使用前 k 个奇异值和对应的左、右奇异向量重构图像
        compressed_image = np.dot(U[:, :k], np.dot(s_k, V[:k, :]))
    
        # 将像素值限制在 [0, 255] 范围内,并转换为无符号 8 位整数
        compressed_image = np.clip(compressed_image, a_min=0, a_max=255).astype(np.uint8)
    
        # 显示图像
        # 显示原始图像
        cv2.imshow(winname='Original Image', mat=image)
    
        # 显示压缩后的图像
        cv2.imshow(winname='Compressed Image', mat=compressed_image)
    
        # 等待用户按键
        cv2.waitKey(0)
    
        # 关闭所有窗口
        cv2.destroyAllWindows()
    

2.微积分

①导数

  • 导数的概念:导数是数学中的一个基本概念,主要用于研究函数随变量变化的快慢,即变化率。对于一个函数 f(x)f(x),若要计算其在某一点 x=ax=a 处的导数,可以考察函数在 xx 接近 aa 时的变化情况。

    更正式地,函数 f(x)f(x) 在点 x=ax=a 处的导数定义为:

    image-20250306210620005

    如果这个极限存在,我们就说函数在点a是可导的

  • 常见导数

    image-20250306210737173

  • 激活函数求导

    • 常见激活函数

    image-20250306210837526

image-20250306210910938

image-20250306211037667

image-20250306211109874

案例实践1

使用Python绘制出上述激活函数

image-20250306211650290

image-20250306212007998

image-20250306212509129

image-20250306212933019

image-20250306215126129

image-20250306215341375

# 导入必要的库
import numpy as np
import matplotlib
matplotlib.use('TkAgg')  # 设置matplotlib使用TkAgg作为绘图后端,确保在不支持默认后端的环境中正确显示图形
import matplotlib.pyplot as plt

# 定义Sigmoid激活函数
def sigmoid(x):
    """Sigmoid函数:1 / (1 + e^(-x))"""
    return 1 / (1 + np.exp(-x))

# 定义一个通用的函数来绘制激活函数图像
def plot_activation_function(func, x, title):
    """根据给定的激活函数和输入值绘制其图像"""
    y = func(x)  # 计算激活函数对应的输出值
    plt.plot(x, y)  # 绘制函数曲线
    plt.title(title)  # 设置图表标题
    plt.xlabel('x')  # 设置x轴标签
    plt.ylabel('f(x)')  # 设置y轴标签
    plt.grid(True)  # 显示网格线
    plt.show()  # 显示图表

# 定义tanh(双曲正切)激活函数
def tanh(x):
    """tanh函数:(e^x - e^(-x)) / (e^x + e^(-x))"""
    return np.tanh(x)

# 定义ReLU激活函数
def relu(x):
    """ReLU函数:max(0, x)"""
    return np.maximum(0, x)

# 定义Leaky ReLU激活函数
def leaky_relu(x, alpha=0.01):
    """Leaky ReLU函数:x > 0 ? x : alpha * x"""
    return np.where(x > 0, x, x * alpha)

# 定义Parametric ReLU激活函数
def parametric_relu(x, parm=0.5):
    """Parametric ReLU函数:x > 0 ? x : parm * x"""
    return np.where(x > 0, x, x * parm)

# 定义ELU(Exponential Linear Unit)激活函数
def elu(x, alpha=1):
    """ELU函数:x > 0 ? x : alpha * (e^x - 1)"""
    return np.where(x > 0, x, alpha * (np.exp(x) - 1))

# 创建从-10到10(包括10),步长为0.1的数组,用于作为各激活函数的输入
x_value = np.arange(-10, 10.1, 0.1)

# 分别绘制不同激活函数的图像
plot_activation_function(sigmoid, x_value, title='Sigmoid')
plot_activation_function(tanh, x_value, title='tanh')
plot_activation_function(relu, x_value, title='ReLU')
plot_activation_function(leaky_relu, x_value, title='Leaky ReLU')
plot_activation_function(parametric_relu, x_value, title='Parametric ReLU')
plot_activation_function(elu, x_value, title='ELU')

②微分

  • 微分的概念:微分是微积分中的一个基本概念,它描述了函数值随着自变量的微小变化而产生的变化率。具体来说,对于一个函数 f(x),在点 x 处的微分通常表示为 df 或 df(x),它在几何上对应于该函数图像在点 x 处切线的斜率与自变量一个微小变化 dx 的乘积。 换句话说,如果自变量 x 发生了微小的变化 dx,那么函数值 f(x) 相应的变化可以近似为这个微分值,数学上表达为:

​ $ df(x) = f'(x)dx $

​ 这里,f'(x) 代表函数 f 在点 x 处的导数,即切线的斜率。因此,微分实质上提供了函数在某一点附近行为的一个线性近似,并且是理解和计算变化率、斜率以及函数局部性质的关键工具。

  • 加法法则:如果有两个函数 $ u(x) $ 和 $ v(x) $,它们的和定义为一个新的函数 $ f(x) = u(x) + v(x) $,那么这个新函数 $ f(x) $ 的导数等于这两个函数导数的和。用公式表示就是:

    $ \frac{d}{dx} [u(x) + v(x)] = \frac{d}{dx} u(x) + \frac{d}{dx} v(x) $

  • 乘法法则:如果有两个函数 $ u(x) $ 和 $ v(x) $,它们的乘积为 $ f(x) = u(x) \cdot v(x) $,那么 $ f(x) $ 的导数是:

    $ \frac{d}{dx} [u(x) \cdot v(x)] = u(x) \frac{d}{dx} v(x) + v(x) \frac{d}{dx} u(x) $

  • 除法法则:如果有两个函数 $ u(x) $ 和 $ v(x) $,它们的商为 $ f(x) = \frac{u(x)}{v(x)} $,那么 $ f(x) $ 的导数是:

    $ \frac{d}{dx} \left[ \frac{u(x)}{v(x)} \right] = \frac{v(x) \frac{d}{dx} u(x) - u(x) \frac{d}{dx} v(x)}{[v(x)]^2} $

  • 积分示意图

    image-20250306220425447

案例实践2

  • 展示二维图像切线

    image-20250306221221601

    # 导入必要的库
    import numpy as np
    import matplotlib
    matplotlib.use('TkAgg')  # 设置matplotlib使用TkAgg作为绘图后端,确保图形在各种环境下都能正确显示
    import matplotlib.pyplot as plt
    
    # 定义原始函数 f(x) = x^2
    def f(x):
        return x**2
    
    # 定义函数的导数 df/dx = 2x,用于计算给定点处的斜率
    def df(x):
        return 2*x  
    
    # 使用numpy.linspace生成从-3到3的一系列x值,用于绘制函数曲线
    x = np.linspace(-3, 3, num=100)  
    y = f(x)  # 对应于每个x值的y值
    
    # 创建一个新的图形,指定尺寸为8*6英寸
    plt.figure(figsize=(8, 6))  
    # 绘制函数曲线,并为其添加标签
    plt.plot(x, y, label="f(x) = x^2")  
    
    # 在x=1时计算对应的y值和斜率,以绘制该点的切线
    x1 = 1
    y1 = f(x1)  # 当x=1时的f(x)值
    slope = df(x1)  # 当x=1时的斜率
    
    # 定义一个函数来表示切线方程,基于点斜式方程:y - y1 = m(x - x1)
    def tangent_line(x, x1, y1, slope):
        return slope * (x - x1) + y1
    
    # 在x=1附近的区间内生成一系列x值,用于绘制切线
    x_tangent = np.linspace(x1 - 1, x1 + 1, num=10) 
    y_tangent = tangent_line(x_tangent, x1, y1, slope)  # 计算切线上对应点的y值
    # 绘制切线,并设置颜色为红色,同时添加标签
    plt.plot(x_tangent, y_tangent, label="Tangent at x = 1", color='red')
    # 绘制切点,用黑色圆点标记
    plt.scatter([x1], [y1], color='black')  
    
    # 添加图例、坐标轴标签和标题,并启用网格
    plt.legend()
    plt.xlabel('x')
    plt.ylabel('f(x)')
    plt.title("Function and Tangent Line at a Point")
    plt.grid(True)
    
    # 显示图形
    plt.show()
    

案例实践3

  • 展示三维函数切面

    image-20250306222104952

    # 导入必要的库
    import numpy as np
    import matplotlib
    matplotlib.use('TkAgg')  # 设置matplotlib使用TkAgg作为绘图后端,确保在不支持默认后端的环境中正确显示图形
    import matplotlib.pyplot as plt
    
    # 创建x, y数据点。这里我们创建了两个等间距数列,分别代表x轴和y轴的数据点。
    # np.linspace生成从-5到5之间的100个等间隔数值。
    x = np.linspace(-5, 5, 100)
    y = np.linspace(-5, 5, 100)
    # np.meshgrid将两个一维数组转换为二维坐标矩阵,方便后续计算三维函数的值。
    x, y = np.meshgrid(x, y)
    
    # 定义三维函数f(x,y) = x^2 + y^2,这将在3D空间中绘制一个抛物面。
    def f(x, y): return x**2 + y**2
    
    # 根据定义的函数f计算每个(x, y)对应的z值。
    z = f(x, y)
    
    # 创建一个图形对象,并添加一个3D子图。
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    
    # 使用plot_surface方法绘制三维曲面,cmap参数设置颜色映射,alpha设置透明度。
    surf = ax.plot_surface(x, y, z, cmap='viridis', alpha=0.5)
    
    # 定义要突出显示的点的位置(point_x, point_y),并根据定义的函数计算该点的高度(point_z)。
    point_x, point_y = 1.0, 1.0
    point_z = f(point_x, point_y)
    
    # 在指定位置绘制一个红色的散点,以突出显示特定的点。
    ax.scatter(point_x, point_y, point_z, color='red', s=50)
    
    # 计算通过给定点的切平面的法向量。对于函数f(x,y)=x^2+y^2,在任意点(x,y)处的梯度即为切平面的法向量。
    normal = np.array([2*point_x, 2*point_y, -1])
    
    # 为切平面创建数据点。首先生成新的等间距数列用于切平面的数据点。
    x_plane = np.linspace(-5, 5, 10)
    y_plane = np.linspace(-5, 5, 10)
    # 使用np.meshgrid生成切平面的数据点网格。
    x_plane, y_plane = np.meshgrid(x_plane, y_plane)
    # 根据切平面的法向量和平面上的一个已知点计算切平面上所有点的高度。
    z_plane = (-normal[0] * (x_plane - point_x) - normal[1] * (y_plane - point_y)) / normal[2] + point_z
    
    # 绘制切平面,使用黄色填充且设置透明度。
    ax.plot_surface(x_plane, y_plane, z_plane, color='yellow', alpha=0.5)
    
    # 设置坐标轴标签和图表标题。
    ax.set_xlabel('X axis')
    ax.set_ylabel('Y axis')
    ax.set_zlabel('Z axis')
    ax.set_title('3D Surface Plot with Tangent Plane')
    
    # 显示图形。
    plt.show()
    

③链式求导法则

链式求导法则

设有两个函数 $ y = f(u) $ 和 $ u = g(x) $,它们都在相应的点可导。如果我们定义一个复合函数 $ y = f(g(x)) $,那么复合函数 $ y $ 关于 $ x $ 的导数是:

$$ \frac{dy}{dx} = \frac{dy}{du} \cdot \frac{du}{dx} $$

这里:

  • $$ \frac{dy}{du} $$ 是外函数 $ f(u) $ 关于其变量 $ u $ 的导数,
  • $$ \frac{du}{dx} $$ 是内函数 $ g(x) $ 关于 $ x $ 的导数。

复合函数求导示例

假设有复合函数 $ h(x) = (3x + 2)^2 $,对其进行求导。

令:

  • $ f(u) = u^2 $
  • $ g(x) = 3x + 2 $

则 $ h(x) = f(g(x)) $。

求导步骤:

  1. 求内函数 $ g(x) $ 的导数: $$ g'(x) = 3 $$

  2. 求外函数 $ f(u) $ 对 $ u $ 的导数: $$ f'(u) = 2u $$

  3. 将 $ g(x) $ 代入 $ f'(u) $,得到: $$ f'(g(x)) = 2(3x + 2) $$

  4. 应用链式法则,将 $ f'(g(x)) $ 与 $ g'(x) $ 相乘,得到 $ h'(x) $: $$ h'(x) = f'(g(x)) \cdot g'(x) = 2(3x + 2) \cdot 3 = 6(3x + 2) $$

最终结果: $$ h'(x) = 6(3x + 2) $$

④反向传播算法

求反向传播

假设:

  • 第一层参数全部初始化为0.5
  • 第二层参数全部初始化为1
  • 输入x1=40,x2=80,目标输出y=60
  • 学习率为1e-5

image-20250828081251463

前向传播

  • 第一层参数全部初始化为0.5
  • 第二层参数全部初始化为1
  • 输入x1=40,x2=80,目标y=60
  • 学习率为1e-5

image-20250828081854768

计算误差

  • 第一层参数全部初始化为0.5 第二层参数全部初始化为1
  • 输入x1=40,x2=80,目标y=60
  • 学习率为1e-5

image-20250828082216630

反向传播:计算损失函数的梯度

  • 第一层参数全部初始化为0.5
  • 第二层参数全部初始化为1
  • 输入x1=40,x2=80,目标y=60
  • 学习率为1e-5

image-20250828082350609

反向传播:计算第2层的梯度

  • 第一层参数全部初始化为0.5
  • 第二层参数全部初始化为1
  • 输入x1=40,x2=80,目标y=60
  • 学习率为1e-5

image-20250828082539250

反向传播:计算第1层的梯度

  • 第一层参数全部初始化为0.5
  • 第二层参数全部初始化为1
  • 输入x1=40,x2=80,目标y=60
  • 学习率为1e-5

image-20250828082754225

梯度更新

  • 第一层参数全部初始化为0.5
  • 第二层参数全部初始化为1
  • 输入x1=40,x2=80,目标y=60
  • 学习率为1e-5

image-20250828083004369

重新进行前向传播

  • 第一层参数全部初始化为0.5
  • 第二层参数全部初始化为1
  • 输入x1=40,x2=80,目标y=60
  • 学习率为1e-5

image-20250828083142114

重新计算误差

  • 第一层参数全部初始化为0.5
  • 第二层参数全部初始化为1
  • 输入x1=40,x2=80,目标y=60
  • 学习率为1e-5

image-20250828083228707

案例展示

代码实战:使用代码完成上述的神经网络反向传播

# 定义输入值和期望输出
x_1 = 40.0
x_2 = 80.0
expected_output = 60.0

# 初始化
# 定义权重
w_1_11 = 0.5
w_1_12 = 0.5
w_1_13 = 0.5
w_1_21 = 0.5
w_1_22 = 0.5
w_1_23 = 0.5

w_2_11 = 1.0
w_2_21 = 1.0
w_2_31 = 1.0

# 前向传播
z_1 = x_1 * w_1_11 + x_2 * w_1_21
z_2 = x_1 * w_1_12 + x_2 * w_1_22
z_3 = x_1 * w_1_13 + x_2 * w_1_23
y_pred = z_1 * w_2_11 + z_2 * w_2_21 + z_3 * w_2_31

print(f"前向传播测值为{y_pred}")

# 计算损失值(L2损失)
loss = 0.5 * (expected_output - y_pred) ** 2
print(f"当前Loss值为{loss}")

# 开始计算梯度
# 计算输出层关于损失函数的梯度
d_loss_predictied_output = -(expected_output - y_pred)

# 计算权重关于损失函数的梯度
d_loss_w_2_11 = d_loss_predictied_output * z_1
d_loss_w_2_21 = d_loss_predictied_output * z_2
d_loss_w_2_31 = d_loss_predictied_output * z_3

d_loss_w_1_11 = d_loss_predictied_output * w_2_11 * x_1
d_loss_w_1_21 = d_loss_predictied_output * w_2_11 * x_2
d_loss_w_1_12 = d_loss_predictied_output * w_2_21 * x_1
d_loss_w_1_22 = d_loss_predictied_output * w_2_21 * x_2
d_loss_w_1_13 = d_loss_predictied_output * w_2_31 * x_1
d_loss_w_1_23 = d_loss_predictied_output * w_2_31 * x_2

# 使用梯度下降法更新权重

# 使用梯度下降法更新权重
learning_rate = 1e-5
w_2_11 -= learning_rate * d_loss_w_2_11
w_2_21 -= learning_rate * d_loss_w_2_21
w_2_31 -= learning_rate * d_loss_w_2_31

w_1_11 -= learning_rate * d_loss_w_1_11
w_1_12 -= learning_rate * d_loss_w_1_12
w_1_13 -= learning_rate * d_loss_w_1_13
w_1_21 -= learning_rate * d_loss_w_1_21
w_1_22 -= learning_rate * d_loss_w_1_22
w_1_23 -= learning_rate * d_loss_w_1_23

# 前向传播
z_1 = x_1 * w_1_11 + x_2 * w_1_21
z_2 = x_1 * w_1_12 + x_2 * w_1_22
z_3 = x_1 * w_1_13 + x_2 * w_1_23
y_pred = z_1 * w_2_11 + z_2 * w_2_21 + z_3 * w_2_31

print(f"前向传播测值为{y_pred}")

# 计算损失值(L2损失)
loss = 0.5 * (expected_output - y_pred) ** 2
print(f"当前Loss值为{loss}")

封装为函数调用

def forward_propagation(layer_1_list, layer_2_list):
    w_1_11, w_1_12, w_1_13, w_1_21, w_1_22, w_1_23 = layer_1_list
    w_2_11, w_2_21, w_2_31 = layer_2_list
    z_1 = x_1 * w_1_11 + x_2 * w_1_21
    z_2 = x_1 * w_1_12 + x_2 * w_1_22
    z_3 = x_1 * w_1_13 + x_2 * w_1_23
    y_pred = z_1 * w_2_11 + z_2 * w_2_21 + z_3 * w_2_31
    return y_pred


def compute_loss(y_true, y_pred):
    loss = 0.5 * (y_true - y_pred) ** 2
    return loss

def backward_propagation(layer_1_list,layer_2_list,learning_rate):

    w_1_11, w_1_12, w_1_13, w_1_21, w_1_22, w_1_23 = layer_1_list
    w_2_11, w_2_21, w_2_31 = layer_2_list
    z_1 = x_1 * w_1_11 + x_2 * w_1_21
    z_2 = x_1 * w_1_12 + x_2 * w_1_22
    z_3 = x_1 * w_1_13 + x_2 * w_1_23

    # 计算输出层关于损失函数的梯度
    d_loss_predicted_output = -(y_true - y_pred)

    # 计算权重关于损失函数的梯度
    d_loss_w_2_11 = d_loss_predicted_output * z_1
    d_loss_w_2_21 = d_loss_predicted_output * z_2
    d_loss_w_2_31 = d_loss_predicted_output * z_3

    d_loss_w_1_11 = d_loss_predicted_output * w_2_11 * x_1
    d_loss_w_1_21 = d_loss_predicted_output * w_2_11 * x_2
    d_loss_w_1_12 = d_loss_predicted_output * w_2_21 * x_1
    d_loss_w_1_22 = d_loss_predicted_output * w_2_21 * x_2
    d_loss_w_1_13 = d_loss_predicted_output * w_2_31 * x_1
    d_loss_w_1_23 = d_loss_predicted_output * w_2_31 * x_2

    # 使用梯度下降法更新权重
    w_2_11 -= learning_rate * d_loss_w_2_11
    w_2_21 -= learning_rate * d_loss_w_2_21
    w_2_31 -= learning_rate * d_loss_w_2_31

    w_1_11 -= learning_rate * d_loss_w_1_11
    w_1_12 -= learning_rate * d_loss_w_1_12
    w_1_13 -= learning_rate * d_loss_w_1_13
    w_1_21 -= learning_rate * d_loss_w_1_21
    w_1_22 -= learning_rate * d_loss_w_1_22
    w_1_23 -= learning_rate * d_loss_w_1_23

    layer_1_list = [w_1_11, w_1_12, w_1_13, w_1_21, w_1_22, w_1_23]
    layer_2_list = [w_2_11, w_2_21, w_2_31]
    return layer_1_list,layer_2_list


def parm_init():
    # 初始化定义权重
    w_1_11 = 0.5
    w_1_12 = 0.5
    w_1_13 = 0.5
    w_1_21 = 0.5
    w_1_22 = 0.5
    w_1_23 = 0.5

    w_2_11 = 1.0
    w_2_21 = 1.0
    w_2_31 = 1.0

    layer_1_list = [w_1_11,w_1_12,w_1_13,w_1_21,w_1_22,w_1_23]
    layer_2_list = [w_2_11,w_2_21,w_2_31]
    return layer_1_list, layer_2_list

if __name__ == '__main__':
    # 定义输入值和期望输出
    x_1 = 40.0
    x_2 = 80.0
    y_true = 60.0
    learning_rate = 1e-5

    epoch = 100

    '''
    初始化
    '''
    # 初始化定义权重
    layer_1_list, layer_2_list = parm_init()

    for i in range(epoch):

        # 正向传播
        y_pred = forward_propagation(layer_1_list,layer_2_list)
        # 计算损失
        loss = compute_loss(y_true, y_pred)
        print(f"第{i}次 预测值为: ", y_pred, " 误差为: ",loss)
        # 反向传播
        layer_1_list,layer_2_list = backward_propagation(layer_1_list,layer_2_list,learning_rate)

梯度下降求最小值可视化

先完成第一步,使用梯度下降,求出函数最小值,实现梯度下降的静态显示:

import numpy as np
import matplotlib.pyplot as plt

# 定义函数
def f(x):
    return x ** 2

# 导数(梯度)
def df(x):
    return 2 * x

# 梯度下降参数
lr = 0.1  # 学习率
n_iterations = 10  # 迭代次数
# x1 = np.random.uniform(-3, 3)  # 随机初始化 x
x1 = 2.5

# 梯度下降算法
for i in range(n_iterations):
    gradient = df(x1)
    x1 = x1 - lr * gradient  # 更新 x

# 绘制原始函数
x = np.linspace(-3, 3, 100)
y = f(x)
plt.figure(figsize=(8, 6))
plt.plot(x, y, label="f(x) = x^2")

# 绘制梯度下降过程中 x 的位置
x_history = []
y_history = []

# 重新初始化 x1 用于演示
x1 = np.random.uniform(-3, 3)
for i in range(n_iterations):
    gradient = df(x1)
    x1 = x1 - lr * gradient  # 更新 x
    x_history.append(x1)
    y_history.append(f(x1))
    plt.scatter(x1, f(x1), color='red')

# 绘制梯度下降路径
plt.plot(x_history, y_history, label="Gradient Descent", color='red')

# 设置图形
plt.legend()
plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('Function and Gradient Descent Path')
plt.grid(True)
plt.show()

image-20250828092355661

再完成第二步,实现动画显示

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

# 定义函数
def f(x):
    return x ** 2

# 导数(梯度)
def df(x):
    return 2 * x

# 梯度下降参数
lr = 0.1  # 学习率
n_iterations = 10  # 迭代次数
x1 = np.random.uniform(-3, 3)  # 随机初始化 x

# 绘制原始函数
x = np.linspace(-3, 3, 100)
y = f(x)

fig, ax = plt.subplots(figsize=(8, 6))
ax.plot(x, y, label="f(x) = x^2")
point, = ax.plot([], [], 'ro', label="Gradient Descent")
value_display = ax.text(0.7, 0.02, '', transform=ax.transAxes)

# 初始化梯度下降过程的点
def init():
    point.set_data([], [])
    value_display.set_text('')
    return point, value_display

# 动画更新函数
def update(i):
    global x1
    gradient = df(x1)
    x1 -= lr * gradient  # 更新 x
    point.set_data(x1, f(x1))
    value_display.set_text('Min = ({:.2f}, {:.2f})'.format(x1, f(x1)))
    return point, value_display

# 创建动画
ani = FuncAnimation(fig, update, frames=np.arange(0, n_iterations), init_func=init, blit=True)

# 设置图形
ax.legend()
ax.set_xlabel('x')
ax.set_ylabel('f(x)')
ax.set_title('Function and Gradient Descent Animation')
ax.grid(True)

plt.show()

image-20250828093342337

实现三维平面的梯度下降

第一步:制作出三维平面

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np

# 创建x,y的数据点
x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
x, y = np.meshgrid(x, y)

# 定义三维函数
def f(x, y):
    return x**2 + y**2

# 计算z的值
z = f(x, y)

# 创建图形和轴
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

# 绘制表面
ax.plot_surface(x, y, z, cmap='viridis')

# 设置标签和标题
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
ax.set_title('3D Surface Plot')

# 显示图形
plt.show()

image-20250828093436748

第二步:实现三维平面的梯度下降

import matplotlib.pyplot as plt
import numpy as np

# 定义三维函数
def f(x, y):
    return x**2 + y**2

# 定义函数的梯度
def grad_f(x, y):
    return 2*x, 2*y

# 梯度下降函数
def gradient_descent(grad, start_x, start_y, learning_rate, num_iterations):
    x, y = start_x, start_y
    xs, ys, zs = [], [], []
    for i in range(num_iterations):
        dx, dy = grad(x, y)
        x, y = x - learning_rate * dx, y - learning_rate * dy
        z = f(x, y)
        xs.append(x)
        ys.append(y)
        zs.append(z)
    return xs, ys, zs

# 初始点
start_x, start_y = 3.0, 3.0

# 运行梯度下降
xs, ys, zs = gradient_descent(grad_f, start_x, start_y, 0.1, 50)

# 创建图形和轴
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

# 创建x,y的数据点
x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
x, y = np.meshgrid(x, y)
z = f(x, y)

# 绘制表面
ax.plot_surface(x, y, z, cmap='viridis', alpha=0.7)

# 绘制梯度下降的点
ax.scatter(xs, ys, zs, color='r', s=50)

# 绘制点的连接线
for i in range(len(xs)-1):
    ax.plot([xs[i], xs[i+1]], [ys[i], ys[i+1]], [zs[i], zs[i+1]], 'r-')

# 设置标签和标题·
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
ax.set_title('Gradient Descent on 3D Surface')

# 显示图形
plt.show()

image-20250828093537718

3.概率论

①基本概念

试验、事件与样本空间

  • 试验:本场游戏
  • 样本空间:整个方框
  • 事件:游戏中可能发生的事情

image-20250828095107011

  • 条件概率与独立事件
  • 条件概率是指在某个特定条件或情况已经发生的前提下,另一个事件发生的概率。
  • 如打雷下雨时,外出带伞的几率会增加
  • 独立事件是指两个(或更多)事件发生时,一个事件的结果不会影响另个事件的结果。
  • 如两次掷硬币的结果之间没有相互影响

案例实践1

使用Python模拟随机实验

import random

# 模拟掷硬币函数,'H'表示正面,'T'表示反面
def toss_coin():
    return 'H' if random.random() < 0.5 else 'T'

# 进行N次掷硬币试验
N = 10
results = [toss_coin() for _ in range(N)]

# 打印结果
print(results)

输出结果

['T', 'H', 'H', 'H', 'T', 'H', 'T', 'T', 'H', 'T']

②随机变量及其分布

  • 随机变量的定义

  • 随机变量是一个数值结果,它的值取决于随机现象的结果。

  • 随机变量可以是离散的,既有一个可数的值集合(如掷骰子的结果也可是连续的,既是一个实数区间的任何值(如测量一个人的身高)

  • 每个随机变量都有一个与之关联的概率分布,描述了随机变量取各种可能值的概率。

  • 离散随机变量与连续随机变量

  • 离散随机变量是指其可能的结果值是分开的,可以逐个列举的。例如,掷骰子的结果

  • 连续随机变量,顾名思义,它的可能结果是连续的,不能逐个列举。例如,一个人的身高就

  • 概率质量函数与概率密度函数

  • 概率质量函数(PMF)是离散随机变量的概率表示。它给出了这个随机变量取特定值的概率。例如,掷一个公平的六面骰子,每个面上的数字出现的概率都是1/6,这就可以通过一个概率质量函数来描述。

  • 概率密度函数(PDF)是连续随机变量的概率表示。由于连续随机变量取任何一个具体数值的概率都是0,所以概率密度函数不直接给出概率,而是给出了随机变量落在某一区间内的概率密度。PDF下某区间的面积代表随机变量落在这个区间的概率。例如,描述人的身高分布的随机变量就会用到概率密度函数。

案例实践2

  • 制作概率质量函数示意图
import random
import matplotlib.pyplot as plt

# 模拟掷硬币函数,'H'代表正面,'T'代表反面
def toss_coin():
    return 'H' if random.random() < 0.5 else 'T'

# 进行N次掷硬币实验
N = 1000
results = [toss_coin() for _ in range(N)]

print(results)


# 计算正面和反面的频率
heads = results.count('H')
tails = results.count('T')

# 概率质量函数的值
pmf = [heads/N, tails/N]

# 绘制概率质量函数
labels = ['Heads', 'Tails']
plt.bar(labels, pmf, color=['blue', 'orange'])
plt.title('Probability Mass Function of Coin Toss')
plt.ylabel('Probability')
plt.show()

image-20250828100340741

③期望、方差与协方差

  • 期望的定义与性质
  • 期望,又称为数学期望、均值,是指随机事件在多次重复实验中的平均值。在射击比赛中,如果一个射击运动员多次射击同一目标,他的子弹击中目标的平均位置就可以看作是期望值。期望值代表的是射手的准确度,一个射手的期望命中点越靠近靶心,说明他越准确。

方差与偏差

image-20250828100653352

  • 协方差与相关系数

image-20250828100744234

案例实践3

计算期望、方差与协方差

import numpy as np

# 生成两组随机数据
data1 = np.random.randn(1000)
data2 = np.random.randn(1000)

# 计算期望(平均值)
expectation1 = np.mean(data1)
expectation2 = np.mean(data2)

# 计算方差
variance1 = np.var(data1)
variance2 = np.var(data2)

# 计算协方差矩阵(注意:协方差是两个变量间的关系度量)
covariance_matrix = np.cov(data1, data2)

print(f"期望值 Data1: {expectation1}, Data2: {expectation2}")
print(f"方差 Data1: {variance1}, Data2: {variance2}")
print(f"协方差矩阵:\n{covariance_matrix}")

# 从协方差矩阵中提取data1和data2的协方差
covariance = covariance_matrix[0, 1]
print(f"Data1 和 Data2 的协方差: {covariance}")

输出

期望值 Data1: -0.027455405914977124, Data2: -0.019427600765748572
方差 Data1: 1.0287714668586663, Data2: 0.9960194474035864
协方差矩阵:
[[1.02980127 0.00939843]
 [0.00939843 0.99701646]]
Data1 和 Data2 的协方差: 0.009398426124711812

④常见的概率分布

  • 伯努利分布:描述只有两种可能结果的单次随机试验
  • 二项分布:描述固定n次的独立伯努利试验中成功的次数。

image-20250828101357029

  • 正态分布:描述自然和社会现象中出现的许多随机变量的分布情况
  • 指数分布:描述独立随机事件发生的时间间隔

image-20250828101749822

案例实践4

模拟常见的概率分布

import numpy as np
import matplotlib.pyplot as plt

# 设定随机种子,以便于结果的复现
np.random.seed(0)

# 伯努利分布(参数p为成功概率)
p = 0.5
bernoulli_dist = np.random.binomial(1, p, 1000)

# 二项分布(参数n为试验次数,p为每次成功概率)
n = 10
binomial_dist = np.random.binomial(n, p, 1000)

# 正态分布(参数mu为均值,sigma为标准差)
mu, sigma = 0, 0.1
normal_dist = np.random.normal(mu, sigma, 1000)

# 指数分布(参数lambda为率参数,其倒数为平均间隔时间)
lambd = 1.0
exponential_dist = np.random.exponential(1/lambd, 1000)

# Logistic分布(参数mu为位置参数,s为尺度参数)
mu, s = 0, 1
logistic_dist = np.random.logistic(mu, s, 1000)

# 绘制柱状图
fig, axs = plt.subplots(5, 1, figsize=(5, 8))

# 伯努利分布柱状图
axs[0].hist(bernoulli_dist, bins=2)
axs[0].set_title('Bernoulli Distribution')

# 二项分布柱状图
axs[1].hist(binomial_dist, bins=range(n+2))
axs[1].set_title('Binomial Distribution')

# 正态分布柱状图
axs[2].hist(normal_dist, bins=30)
axs[2].set_title('Normal Distribution')

# 指数分布柱状图
axs[3].hist(exponential_dist, bins=30)
axs[3].set_title('Exponential Distribution')

# Logistic分布柱状图
axs[4].hist(logistic_dist, bins=30)
axs[4].set_title('Logistic Distribution')

plt.tight_layout()
plt.show()

image-20250828102050369

⑤大数定律与中心极限定理

  • 大数定率 随着尝试次数的增加,实验的平均结果将趋近于期望值。
  • 例如抛硬币,随着你扔的次数越来越多,你会注意到,正面和反面出现的次数开始趋近于各占一半。如果你扔了成千上万次,你几乎可以肯定,正反面各自出现的次数会非常接近于总扔硬币次数的50%

案例实践5

模拟大数定律

import numpy as np
import matplotlib.pyplot as plt


def gamble_game(trials):
    # 抛硬币游戏,正面为1,反面为-1
    results = np.random.choice([1, -1], size=trials)
    return results


def simulate_gambling(trials):
    results = gamble_game(trials)

    # 计算累积盈亏
    cumulative_sum = np.cumsum(results)

    # 计算平均盈亏
    average_win_loss = cumulative_sum / np.arange(1, trials + 1)

    return cumulative_sum, average_win_loss


def plot_results(trials):
    cumulative_sum, average_win_loss = simulate_gambling(trials)

    # 绘制累积盈亏图
    plt.figure(figsize=(12, 6))
    plt.subplot(1, 2, 1)
    plt.plot(cumulative_sum)
    plt.title('Cumulative Win/Loss Over Trials')
    plt.xlabel('Trials')
    plt.ylabel('Cumulative Sum')

    # 绘制平均盈亏图
    plt.subplot(1, 2, 2)
    plt.plot(average_win_loss)
    plt.axhline(y=0, color='r', linestyle='-')  # 期望值线
    plt.title('Average Win/Loss Over Trials')
    plt.xlabel('Trials')
    plt.ylabel('Average Win/Loss')

    plt.tight_layout()
    plt.show()


# 模拟10000次游戏
plot_results(10000)

image-20250828102855936

⑥大数定律与中心极限定理

  • 中心极限定理
  • 当独立随机变量的数量足够大时,它们的总和将趋近于正态分布,无论原始随机变量的分布如何。
  • 即使单个骰子的结果是完全随机的,但是当许多随机结果加在一起时,总和的分布将趋向于形成一个特定的、可预测的形状,也就是正态分布。

实践案例6

中心极限定理

import numpy as np
import matplotlib.pyplot as plt

def roll_dice():
    """模拟掷一个六面骰子"""
    return np.random.randint(1, 7)

def experiment(num_rolls):
    """进行一次实验,实验包括num_rolls次掷骰子"""
    total = 0
    for _ in range(num_rolls):
        total += roll_dice()
    return total / num_rolls

def run_experiments(num_experiments, num_rolls):
    """运行多次实验,并记录每次实验的平均值"""
    averages = []
    for _ in range(num_experiments):
        averages.append(experiment(num_rolls))
    return averages

def plot_histogram(averages):
    """绘制实验平均值的直方图"""
    plt.hist(averages, bins=20, edgecolor='black', density=True)
    plt.xlabel('Average of Rolls')
    plt.ylabel('Frequency')
    plt.title('Central Limit Theorem Demonstration')
    plt.show()

# 参数设置
num_experiments = 10000  # 实验次数
num_rolls = 30          # 每次实验掷骰子的次数

# 运行实验并绘图
averages = run_experiments(num_experiments, num_rolls)
plot_histogram(averages)

image-20250828103522301

最近更新:: 2025/9/8 11:05
Contributors: yanpeng_