24.8.6学习笔记

Batch Normalization的关键点包括:

  1. 归一化:批量归一化对每一层的输入进行归一化处理,通常是通过调整和缩放激活值来实现。这有助于减少内部协变量偏移,即由于训练过程中网络参数的变化而导致的网络激活分布的变化。

  2. 缩放和平移:归一化之后,输出会通过学习得到的参数(γ 和 β)进行缩放和平移,允许模型取消归一化可能带来的任何不期望的效果。

  3. 实现

    • 在训练过程中,用于归一化的均值和方差是从每个小批量中计算出来的。
    • 在测试阶段,使用指数移动平均法从训练过程中收集的小批量统计量来估计总体统计量(均值和方差)。
  4. 好处

    • 更快收敛:批量归一化可以通过减少梯度消失/爆炸问题来加速训练。
    • 正则化效果:它具有轻微的正则化效果,减少了对dropout或其他正则化技术的需求。
    • 超参数鲁棒性:它使得网络对超参数的选择(如学习率)更加鲁棒。
  5. 缺点

    • 额外计算:批量归一化增加了前向和反向传播的计算负担。
    • 内存开销:需要存储每一层的运行均值和方差。
    • 对批大小敏感:批量归一化的有效性可能会受到批大小的影响,非常小的批可能无法提供良好的归一化估计。

权值衰减(Weight Decay)是机器学习和深度学习中常用的一种正则化技术,用于防止模型过拟合。下面我将详细解释权值衰减的概念和作用。

  1. 基本概念

    • 权值(Weights):在机器学习模型中,尤其是神经网络,权值是模型参数的一部分,它们决定了输入数据如何影响输出结果。
    • 衰减(Decay):这里指的是随着时间或训练过程的进行,逐渐减少或降低某些量的影响。
  2. 权值衰减的作用

    • 防止过拟合:过拟合是指模型在训练数据上表现很好,但在新的、未见过的数据上表现差。权值衰减通过限制模型的复杂度,帮助模型更好地泛化到新数据上。
  3. 实现方式

    • 在损失函数中添加惩罚项:权值衰减通过在模型的损失函数中添加一个额外的惩罚项来实现。这个惩罚项是所有权值的平方和乘以一个正的衰减系数(通常用λ表示)。
    • 损失函数的修改形式:原本的损失函数是 LL,添加权值衰减后的损失函数变为 L+λ∑iwi2L​,其中 wi 是模型中的权值,∑ 表示求和。
  4. 优化过程中的影响

    • 在训练过程中,优化算法(如梯度下降)不仅会尝试最小化原始损失 LL,同时也会考虑到权值的平方和。这导致模型在更新权值时会倾向于选择更小的值,从而减少了模型的复杂度。
  5. 如何选择衰减系数

    • 衰减系数 λ 是一个超参数,需要根据具体问题和数据集进行调整。如果 λλ 太大,可能会导致模型欠拟合(模型太简单,不能捕捉数据的复杂性)。如果 λ 太小,可能不足以防止过拟合。

Dropout是一种在深度学习中广泛使用的正则化技术,由Geoffrey Hinton和他的同事们提出。它主要用于防止神经网络在训练过程中出现过拟合现象。下面是对Dropout的详细解释:

  1. 基本概念

    • Dropout是一种随机丢弃(deactivate)神经网络中一部分神经元(或其连接)的技术。在训练过程中,每个神经元都有一定概率(通常是一个超参数,如0.5)被丢弃。
  2. 工作原理

    • 在每个训练迭代中,Dropout随机选择一些神经元,并将其输出暂时设置为零。这意味着这些神经元在当前迭代中对网络的输出没有贡献。
    • 丢弃是随机的,并且每次迭代都可能不同,这迫使网络学习更加鲁棒的特征表示,而不是依赖于任何单一的神经元。
  3. 防止过拟合

    • 由于神经元的随机丢弃,网络不能过度依赖于任何一个特定的神经元或小组成员,这有助于减少过拟合。
    • Dropout可以被视为训练多个不同的模型,并在测试时平均它们的预测结果,即使实际上并没有存储这些模型。
  4. 实现方式

    • 在神经网络的层之间添加Dropout层,或者在全连接层之后直接应用Dropout。
    • Dropout层在训练时执行丢弃操作,在测试时则不执行丢弃,但会乘以一个缩放因子(通常为丢弃率的倒数),以保持网络权重的期望值不变。
  5. 超参数

    • Dropout的丢弃率是一个重要的超参数,需要根据具体问题进行调整。太高的丢弃率可能导致欠拟合,而太低的丢弃率可能不足以防止过拟合。

超参数

是机器学习模型训练过程中需要设置的参数,不同于模型训练得到的参数(通常称为模型参数或权重)。超参数通常需要在模型训练之前设定,并且对模型的性能有显著影响。以下是关于超参数的一些详细解释:

  1. 定义

    • 超参数是在模型训练之前设置的参数,用于控制学习过程中的某些方面,例如模型的复杂度、学习能力等。
  2. 作用

    • 超参数用于指导模型的训练过程,帮助模型找到最佳的参数设置,以提高模型在新数据上的泛化能力。
  3. 类型

    • 常见的超参数包括学习率(如优化算法中的步长大小)、正则化参数(如L1或L2正则化的强度)、网络结构参数(如神经网络的层数和每层的神经元数量)、Dropout率等。
  4. 选择

    • 超参数的选择通常依赖于问题的性质、数据集的大小和特征、以及模型的类型。没有固定的规则来选择超参数,通常需要通过实验和经验来确定。
  5. 调整方法

    • 超参数的调整通常通过网格搜索(Grid Search)、随机搜索(Random Search)或更高级的贝叶斯优化等方法进行。这些方法通过在超参数空间内进行搜索,找到最佳的超参数组合。
  6. 影响

    • 超参数的选择对模型的性能有显著影响。例如,学习率太高可能导致模型训练不稳定,而学习率太低可能导致训练过程缓慢或陷入局部最优。

超参数优化的基本概念

首先,我们需要了解什么是超参数。超参数是在训练模型之前设定的一些参数,它们不能通过训练直接学习得到。例如,学习率、批次大小、隐藏层数量等都属于超参数。

超参数优化的目标

超参数优化的目标是找到一组超参数的组合,使得模型在验证集上的性能达到最优。这是因为我们希望模型不仅在训练数据上表现好,而且在新数据上也能有好的泛化能力。

常见的超参数优化方法

以下是几种常用的超参数优化方法及其详细过程:

1. 网格搜索 (Grid Search)
  • 定义:网格搜索是最简单的超参数优化方法之一。它通过遍历预定义的超参数值组合来寻找最佳的超参数配置。
  • 过程
    1. 定义范围:首先定义每个超参数的候选值集合。
    2. 遍历组合:对于每个超参数组合,训练模型并评估其在验证集上的性能。
    3. 选择最佳:记录每次训练的结果,并最终选择性能最好的超参数组合作为最终结果。
2. 随机搜索 (Random Search)
  • 定义:随机搜索也是一种简单的超参数优化方法,它随机选取超参数的组合进行训练和评估。
  • 过程
    1. 定义范围:与网格搜索类似,先定义每个超参数的候选值集合。
    2. 随机采样:从每个超参数的候选值集合中随机选取值,组成不同的超参数组合。
    3. 评估性能:对于每个随机选取的超参数组合,训练模型并评估其在验证集上的性能。
    4. 选择最佳:记录每次训练的结果,并最终选择性能最好的超参数组合作为最终结果。
3. 贝叶斯优化 (Bayesian Optimization)
  • 定义:贝叶斯优化是一种基于概率模型的方法,它利用已有的试验结果来预测未知的超参数配置的性能。
  • 过程
    1. 初始化:开始时,随机选择几个超参数配置并训练模型。
    2. 构建代理模型:根据已有的训练结果,构建一个代理模型(如高斯过程)来预测不同超参数配置下的模型性能。
    3. 选择下一个配置:基于代理模型的预测结果,使用某种策略(如预期改进准则)来选择下一个要尝试的超参数配置。
    4. 迭代:重复步骤2和3,直到达到预定的停止条件(比如迭代次数或时间限制)。
    5. 选择最佳:记录每次训练的结果,并最终选择性能最好的超参数组合作为最终结果。
4. 遗传算法 (Genetic Algorithms)
  • 定义:遗传算法是一种模拟自然选择和遗传学原理的优化方法。
  • 过程
    1. 初始化种群:随机生成一个初始超参数配置的种群。
    2. 评估适应度:对于种群中的每个个体,训练模型并评估其在验证集上的性能。
    3. 选择操作:根据适应度选择优秀的个体作为父代。
    4. 遗传操作:通过交叉(组合两个父代产生新的子代)和变异(随机改变某些超参数的值)来产生新一代的种群。
    5. 迭代:重复步骤2至4,直到达到预定的停止条件。
    6. 选择最佳:记录每次训练的结果,并最终选择性能最好的超参数组合作为最终结果。

总结

每种方法都有其特点和适用场景,选择哪种方法取决于你的问题规模、资源约束以及你愿意投入的时间。例如,如果你的超参数空间很小,你可以考虑使用网格搜索;如果超参数空间很大,随机搜索可能是更好的选择;而对于复杂的优化问题,贝叶斯优化和遗传算法通常能够找到更优的解决方案。

Best parameters set found on development set:
{'batch_size': 46, 'epochs': 39, 'hidden_size': 73, 'learning_rate': 0.1}   最优超参数
Test Accuracy: 97.55% 采用了随机搜索的超参数优化方法,使预测准确率提高了10%左右

以下是MNIST手写数字识别,并且加入了随机搜索的超参数优化方法。:

import numpy as np
# 导入NumPy库,用于数值计算。

from sklearn.datasets import fetch_openml
# 从sklearn.datasets模块导入fetch_openml函数,用于加载MNIST数据集。

from sklearn.model_selection import train_test_split, RandomizedSearchCV
# 从sklearn.model_selection模块导入train_test_split函数,用于划分训练集和测试集。
# 从同一个模块导入RandomizedSearchCV类,用于执行超参数随机搜索。

from sklearn.base import BaseEstimator, ClassifierMixin
# 从sklearn.base模块导入BaseEstimator和ClassifierMixin类,用于构建自定义的估计器。

from scipy.stats import randint as sp_randint


# 从scipy.stats模块导入randint类,用于生成随机整数,这里别名为sp_randint。

# 加载 MNIST 数据集
def load_mnist():
    """从 OpenML 加载 MNIST 数据集"""
    mnist = fetch_openml('mnist_784', version=1)
    # 使用fetch_openml函数加载MNIST数据集,'mnist_784'是数据集的标识符。

    X = mnist.data.to_numpy() / 255.0  # 归一化到 [0, 1] 并转换为 NumPy 数组
    # 将数据集中的图像数据转换为NumPy数组,并归一化到0到1之间。

    y = mnist.target.astype(np.int).to_numpy()  # 转换标签为整数并转换为 NumPy 数组
    # 将数据集中的标签转换为整数,并转换为NumPy数组。

    return X, y


# 返回图像数据和对应的标签。

# 定义神经网络类
class NeuralNetwork(BaseEstimator, ClassifierMixin):
    def __init__(self, input_size, hidden_size=128, output_size=10, learning_rate=0.01, epochs=20, batch_size=64):
        """初始化神经网络参数"""
        # 使用 He 初始化方法来初始化权重
        self.W1 = self.he_normal((input_size, hidden_size))
        # 初始化第一个全连接层的权重矩阵。

        self.b1 = np.zeros((1, hidden_size))
        # 初始化第一个全连接层的偏置向量。

        self.W2 = self.he_normal((hidden_size, output_size))
        # 初始化第二个全连接层的权重矩阵。

        self.b2 = np.zeros((1, output_size))
        # 初始化第二个全连接层的偏置向量。

        self.input_size = input_size
        # 设置输入层大小。

        self.hidden_size = hidden_size
        # 设置隐藏层大小。

        self.output_size = output_size
        # 设置输出层大小。

        self.learning_rate = learning_rate
        # 设置学习率。

        self.epochs = epochs
        # 设置训练轮数。

        self.batch_size = batch_size
        # 设置批量大小。

    def he_normal(self, shape, fan_in=None):
        """He 正态分布初始化"""
        if fan_in is None:
            fan_in = shape[0]
        std = np.sqrt(2.0 / fan_in)
        return np.random.normal(loc=0.0, scale=std, size=shape)
        # 使用He正态分布初始化方法来初始化权重矩阵。

    def softmax(self, z):
        """计算 softmax 函数"""
        exp_z = np.exp(z - np.max(z))  # 防止溢出
        # 计算指数函数,减去最大值以防止数值溢出。

        return exp_z / exp_z.sum(axis=1, keepdims=True)
        # 计算softmax函数,返回概率分布。

    def forward(self, X):
        """前向传播"""
        self.z1 = np.dot(X, self.W1) + self.b1  # 隐藏层的线性组合
        # 第一个全连接层的线性变换。

        self.a1 = np.maximum(0, self.z1)  # ReLU 激活函数
        # 应用ReLU激活函数。

        self.z2 = np.dot(self.a1, self.W2) + self.b2  # 输出层的线性组合
        # 第二个全连接层的线性变换。

        self.a2 = self.softmax(self.z2)  # 输出层的激活函数
        # 应用softmax激活函数。

        return self.a2
        # 返回输出层的激活结果。

    def compute_loss(self, y_true, y_pred):
        """计算交叉熵损失"""
        m = y_true.shape[0]  # 样本数量
        # 获取样本的数量。

        log_likelihood = -np.log(y_pred[range(m), y_true])  # 计算对数似然
        # 计算对数似然。

        loss = np.sum(log_likelihood) / m  # 平均损失
        # 计算平均交叉熵损失。

        return loss
        # 返回平均交叉熵损失。

    def backward(self, X, y_true):
        """反向传播"""
        m = y_true.shape[0]  # 样本数量
        # 获取样本的数量。

        delta2 = self.a2
        delta2[range(m), y_true] -= 1  # 计算输出层的误差
        # 计算输出层的误差。

        delta2 /= m  # 平均化
        # 对误差进行平均化处理。

        dW2 = np.dot(self.a1.T, delta2)  # 输出层权重的梯度
        # 计算第二个全连接层权重的梯度。

        db2 = np.sum(delta2, axis=0, keepdims=True)  # 输出层偏置的梯度
        # 计算第二个全连接层偏置的梯度。

        delta1 = np.dot(delta2, self.W2.T)  # 隐藏层的误差
        # 计算第一个全连接层的误差。

        delta1[self.z1 <= 0] = 0  # ReLU 的导数
        # 应用ReLU激活函数的导数。

        dW1 = np.dot(X.T, delta1)  # 隐藏层权重的梯度
        # 计算第一个全连接层权重的梯度。

        db1 = np.sum(delta1, axis=0, keepdims=True)  # 隐藏层偏置的梯度
        # 计算第一个全连接层偏置的梯度。

        # 更新权重和偏置
        self.W1 -= self.learning_rate * dW1
        # 更新第一个全连接层的权重。

        self.b1 -= self.learning_rate * db1
        # 更新第一个全连接层的偏置。

        self.W2 -= self.learning_rate * dW2
        # 更新第二个全连接层的权重。

        self.b2 -= self.learning_rate * db2
        # 更新第二个全连接层的偏置。

        def fit(self, X, y):

            for epoch in range(self.epochs):  # 迭代self.epochs次,self.epochs是训练的轮数
                indices = np.arange(X.shape[0])  # 创建一个索引数组,包含从0到X的行数-1的整数
                np.random.shuffle(indices)  # 随机打乱索引数组,实现数据的随机化,有助于模型的泛化

                X_shuffled = X[indices]  # 根据打乱的索引重新排列X数据,实现数据的随机化
                y_shuffled = y[indices]  # 根据打乱的索引重新排列y标签

                for i in range(0, X.shape[0], self.batch_size):  # 以self.batch_size为步长遍历整个数据集
                    X_batch = X_shuffled[i:i + self.batch_size]  # 取出当前批次(batch)的数据
                    y_batch = y_shuffled[i:i + self.batch_size]  # 取出当前批次的标签

                    y_pred = self.forward(X_batch)  # 调用forward方法进行前向传播,计算当前批次数据的预测值
                    loss = self.compute_loss(y_batch, y_pred)  # 计算当前批次的损失值,使用预测值和真实标签
                    self.backward(X_batch, y_batch)  # 调用backward方法进行反向传播,根据损失计算梯度并更新权重



        return self
        # 返回训练完成的模型实例。

    def predict(self, X):
        """预测函数"""
        y_pred = self.forward(X)
        return np.argmax(y_pred, axis=1)  # 返回预测的类别
        # 返回预测结果的类别标签。

    def score(self, X, y):
        """计算准确率"""
        y_pred = self.predict(X)
        return np.mean(y_pred == y)
        # 计算预测准确率。


# 主程序
if __name__ == "__main__":
    # 加载数据
    X, y = load_mnist()
    # 加载MNIST数据集。

    # 划分训练集和测试集
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
    # 使用train_test_split函数划分训练集和测试集。

    # 定义超参数搜索空间
    param_dist = {
        'hidden_size': sp_randint(32, 256),
        # 定义隐藏层大小的随机整数分布。

        'learning_rate': [0.001, 0.01, 0.1],
        # 定义学习率的选择列表。

        'epochs': sp_randint(10, 50),
        # 定义训练轮数的随机整数分布。

        'batch_size': sp_randint(32, 128)
        # 定义批量大小的随机整数分布。
    }

    # 初始化神经网络
    input_size = 784  # 输入层大小(28x28像素)
    # 设置输入层大小。

    output_size = 10  # 输出层大小(10个数字)
    # 设置输出层大小。

    nn = NeuralNetwork(input_size, output_size=output_size)
    # 初始化神经网络实例。

    # 创建随机搜索对象
    random_search = RandomizedSearchCV(
        estimator=nn, # 所以random这个实例可以调用构建的神经网络里的方法
        # 指定要优化的估计器。

        param_distributions=param_dist,
        # 指定超参数的分布。

        n_iter=10,  # 进行10次随机搜索
        # 设置搜索的迭代次数。

        scoring='accuracy',
        # 设置评估指标为准确率。

        cv=5,  # 5折交叉验证
        # 设置交叉验证的折叠数。

        verbose=2,
        # 设置日志的详细级别。

        n_jobs=-1  # 使用所有可用的核心
        # 设置并行任务的数量。
    )

    # 执行随机搜索,这个方法结束后回会返回最佳超参数并且重新训练模型,
    # 当它执行好之后,模型也被最佳超参数训练好了。很厉害
    random_search.fit(X_train, y_train)
    # 使用训练数据执行随机搜索。

    # 输出最佳参数
    print("Best parameters set found on development set:")
    print(random_search.best_params_)
    # 输出最佳的超参数配置。

    # 在测试集上进行预测
    y_test_pred = random_search.predict(X_test)
    # 使用最佳参数训练的模型在测试集上进行预测。

    # 计算测试准确率
    test_accuracy = random_search.score(X_test, y_test) * 100
    print(f'Test Accuracy: {test_accuracy:.2f}%')
    # 计算并输出测试集上的准确率。

 

么是卷积

想象一下你有一张照片,你想从中找出一些特定的模式或者特征。比如,你想找出照片中的边缘或者线条。卷积就是一种可以帮助你做到这一点的技术。

举例说明

假设你有一张黑白照片,这张照片由许多像素组成,每个像素都有一个灰度值(从0到255)。你可以把这张照片想象成一个二维网格,每个格子代表一个像素。

现在,假设你想找出这张照片中的水平边缘。为了做到这一点,你可以使用一个叫做“卷积核”(也叫滤波器)的小矩阵。这个矩阵通常比原图小很多,比如说是一个3x3的矩阵。

卷积核

卷积核是一个小小的矩阵,它用来扫描整张图片。卷积核里的数字代表了一种模式,比如它可以代表一个水平边缘的样子。卷积核里的数字可以是正数也可以是负数,这些数字决定了我们要找的模式。

卷积过程
  1. 定位
    • 把卷积核放在图片的一个位置上,通常是从左上角开始。
  2. 乘法
    • 卷积核覆盖的区域内的像素值与卷积核里的对应位置的数字相乘。
  3. 求和
    • 把所有乘积的结果加起来,得到一个值。
  4. 移动
    • 把卷积核向右或向下移动一个像素的位置,重复上面的过程。
  5. 重复
    • 直到卷积核覆盖了图片中的每一个位置。
结果

通过这个过程,你会得到一个新的图片,这个新图片的每个像素值都是通过卷积核与原图对应位置的像素值进行计算得到的。如果你使用的是一个代表水平边缘的卷积核,那么新图片就会突出显示那些有水平边缘的地方。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值