当前位置: 首页 > news >正文

南京科技网站设计有特点网站搭建中114514

南京科技网站设计有特点,网站搭建中114514,肇庆专业网站建设服务,wordpress+防止采集文章目录 十一、二 模型选择与过拟合和欠拟合1、模型的选择2、过拟合和欠拟合3、估计模型容量4、线性分类器的VC维5、过拟合欠拟合的代码实现 :fire:①生成数据集②定义评估损失③定义训练函数④三阶多项式函数拟合⑤线性函数拟合(欠拟合)⑤高阶多项式函数拟合(过拟合) 十三、权… 文章目录 十一、二 模型选择与过拟合和欠拟合1、模型的选择2、过拟合和欠拟合3、估计模型容量4、线性分类器的VC维5、过拟合欠拟合的代码实现 :fire:①生成数据集②定义评估损失③定义训练函数④三阶多项式函数拟合⑤线性函数拟合(欠拟合)⑤高阶多项式函数拟合(过拟合) 十三、权重衰退Decay1、使用均方范数作为限制2、演示对最优解的情况3、权重衰减的实现 :fire:①造数据(合成数据集)②初始化模型参数和范数惩罚③定义训练代码实现④开始训练(1)忽略正则化直接训练(2)使用权重衰减 ⑤简洁实现 十四、丢弃法Dropout1、无差别加入噪音2、丢弃法的使用3、推理中的丢弃法5、丢弃法的代码实现 :fire:① 实现dropout_layer 函数② 测试dropout_layer 函数③开始定义模型 6、简洁实现 十五、数值稳定性1、衰减和爆炸2、梯度爆炸3、梯度消失 十六、模型初始化和激活函数1、随机初始化模型参数2、PyTorch的默认随机初始化3 、Xavier随机初始化4、线性激活函数 十七、PyTorch 神经网络基础1、模型构造2、参数管理3、读写文件 十八、Kaggle房价预测实战 :fire:1、数据集的获取2、查看数据便于后续处理3、数据预处理4、离散值的处理5、提取NumPy并转化为张量6、训练模型7 、折交叉验证8、在Kaggle 中实现并提交 :rocket: 十一、二 模型选择与过拟合和欠拟合 1、模型的选择 训练误差模型在训练数据上的误差 泛化误差模型在新数据上的误差 验证数据集一个用来评估模型好坏的数据集 测试数据集只用一次的数据集。 训练集是近10年的高考题验证集是最近一年的高考题测试集就是要考的高考题 K-折交叉验证K-Fold Cross-Validation是一种常用的模型验证技术主要用于评估统计分析或机器学习模型在独立数据集上的性能。其核心思想是将原始数据集分为K个大小相似的子集或“折”然后重复K次训练和验证过程。在每次迭代中选择一个子集作为验证集其余K-1个子集作为训练集。通过这K次迭代每个子集都充当过一次验证集从而可以得到K个评估指标。 在没有足够多数据时使用 K-折交叉验证的步骤大致如下 将原始数据集随机分成K个大小相等的子集或尽可能相等。对于每个子集ii1,2,…,K将其作为验证集其余K-1个子集作为训练集。在训练集上训练模型并在验证集上评估模型的性能例如计算分类准确率、回归误差等。重复步骤2和3直到每个子集都充当过一次验证集从而得到K个评估指标。计算这K个评估指标的平均值作为模型在该数据集上的最终性能评估结果。 训练数据集训练模型参数 验证数据集选择模型超参数 非大数据集上通常使用k-折交叉验证 2、过拟合和欠拟合 模型的容量Capacity或表达能力是指模型拟合复杂函数的能力它从本质上描述了整个模型的拟合能力的大小。这个能力体现在模型可以表示的函数集的大小即模型的假设空间的大小。 模型的容量不足它可能无法很好地表示数据这会导致欠拟合而如果模型的容量过大它可能会过分拟合数据 过拟合是指模型在训练数据上表现得非常好但在测试数据性能较差的现象 欠拟合是指模型在训练数据上的表现就很差更不用说在测试数据上的表现了 3、估计模型容量 难以在不同的种类算法之间比较,例如树模型和神经网络 给定一个模型种类将有两个主要因素参数的个数参数值的选择范围 为了解释模型复杂度我们以多项式函数拟合为例。给定一个由标量数据特征 x x x和对应的标量标签 y y y组成的训练数据集多项式函数拟合的目标是找一个 K K K阶多项式函数 y ^ b ∑ k 1 K x k w k \hat{y} b \sum_{k1}^K x^k w_k y^​bk1∑K​xkwk​ 来近似 y y y。在上式中 w k w_k wk​是模型的权重参数 b b b是偏差参数。与线性回归相同多项式函数拟合也使用平方损失函数。特别地一阶多项式函数拟合又叫线性函数拟合。 4、线性分类器的VC维 VC维用于衡量一个模型的复杂度和学习能力。 传统的定义是对一个指示函数集如果存在H个样本能够被函数集中的函数按所有可能的2的H次方种形式分开则称函数集能够把H个样本打散函数集的VC维就是它能打散的最大样本数目H。若对任意数目的样本都有函数能将它们打散则函数集的VC维是无穷大。 VC维反映了函数集的学习能力VC维越大则学习机器越复杂容量越大。在机器学习中VC维与偏差和方差之间存在关系模型的VC维越大其方差通常也越大。因此在选择合适的模型时需要考虑模型的复杂性对泛化性能的影响。 但是尚没有通用的关于任意函数集VC维计算的理论只对一些特殊的函数集知道其VC维。例如在N维空间中线性分类器和线性实函数的VC维是N1。 VC 维的用处 提供为什么一个模型好的理论依据它可以衡量训练误差和泛化误差之间的间隔。但深度学习中很少使用衡量不是很准确计算深度学习模型的VC维很困难 数据复杂度 多个重要因素样本个数每个样本的元素个数时间、空间结构多样性 总结①模型容量需要匹配数据复杂度否则可能导致欠拟合和过拟合②统计机器学习提供数学工具来衡量模型复杂度③实际中一般靠观察训练误差和验证误差 5、过拟合欠拟合的代码实现 ①生成数据集 max_degree 20 # 定义多项式的最大阶数但实际上我们可能不需要这么高的阶数 n_train, n_test 100, 100 # 训练和测试数据集的大小各100个样本 true_w np.zeros(max_degree) # 初始化一个长度为max_degree的零数组来存储多项式的系数 true_w[0:4] np.array([5, 1.2, -3.4, 5.6]) # 只设置了前四个系数其余为0# 生成一个(n_train n_test, 1)的二维数组其中元素来自正态分布 features np.random.normal(size(n_train n_test, 1)) # 打乱features数组的顺序但这一步通常在实际应用中不是必需的 np.random.shuffle(features)# 使用np.power生成多项式特征但这里可能存在问题因为我们没有按阶数来组织它们 poly_features np.power(features, np.arange(max_degree).reshape(1, -1))# 这里的问题在于gamma函数的使用。通常我们不需要用gamma函数来归一化多项式特征 for i in range(max_degree):poly_features[:, i] / math.gamma(i 1) # gamma(n)(n-1)!但这不是归一化的常见方法# 使用多项式特征和真实系数计算标签值 labels np.dot(poly_features, true_w) # 添加一些噪声到标签中使其更接近真实世界的数据 labels np.random.normal(scale0.1, sizelabels.shape)max_degree 20 定义多项式的最大阶数。但在实际应用中阶数可能不需要这么高因为这可能导致过拟合。 2-3. n_train, n_test 100, 100 定义训练和测试数据集的大小。 true_w np.zeros(max_degree) 初始化一个长度为max_degree的零数组用于存储多项式的系数。 true_w[0:4] np.array([5, 1.2, -3.4, 5.6]) 只设置了前四个多项式系数其余保持为零。这意味着我们实际上只关心四阶多项式。 features np.random.normal(size(n_train n_test, 1)) 生成一个(n_train n_test, 1)的二维数组其中元素来自标准正态分布。 np.random.shuffle(features) 打乱features数组的顺序。 poly_features np.power(features, np.arange(max_degree).reshape(1, -1)) 生成多项式特征。但是这样生成的特征没有按阶数组织即poly_features[:, 0]是所有x^0poly_features[:, 1]是所有x^1依此类推。 labels np.dot(poly_features, true_w) 使用多项式特征和真实系数计算标签值。但由于true_w中只有前四个系数是非零的其余的计算是多余的。 labels np.random.normal(scale0.1, sizelabels.shape) 向标签中添加噪声使其更接近真实世界的数据。 ②定义评估损失 def evaluate_loss(net, data_iter, loss): #save评估给定数据集上模型的损失metric d2l.Accumulator(2) # 损失的总和,样本数量for X, y in data_iter:out net(X)y y.reshape(out.shape)l loss(out, y)metric.add(l.sum(), l.numel())return metric[0] / metric[1]函数定义: def evaluate_loss(net, data_iter, loss):这个函数接受三个参数 net: 一个神经网络模型。data_iter: 一个迭代器用于遍历数据集。每次迭代返回一批输入数据 X 和对应的标签 y。loss: 损失函数用于计算预测输出 out 和真实标签 y 之间的差异。 初始化累加器: metric d2l.Accumulator(2)遍历数据集: for X, y in data_iter:对于数据迭代器 data_iter 中的每一批数据都会执行以下操作 4. 前向传播: out net(X)将输入数据 X 传递给神经网络模型 net得到预测输出 out。 5. 处理标签的形状: y y.reshape(out.shape)这里假设了 y 的形状可能需要调整以匹配 out 的形状。这在某些情况下是必要的尤其是当标签是多维的例如在多分类任务中标签可能是独热编码的并且模型输出了与这些标签形状相同的概率分布时。但请注意如果 y 和 out 本来就具有相同的形状这一步可能是不必要的。 计算损失: l loss(out, y)使用损失函数 loss 计算预测输出 out 和真实标签 y 之间的损失。 累加损失和样本数量: metric.add(l.sum(), l.numel())将当前批次的损失总和通过 l.sum() 计算添加到累加器的第一个值中并将当前批次的样本数量通过 l.numel() 计算它返回张量中元素的数量添加到累加器的第二个值中。 返回平均损失: return metric[0] / metric[1]最后返回累加器中的损失总和除以样本数量得到平均损失。 numel()函数返回数组中元素的个数 ③定义训练函数 def train(train_features, test_features, train_labels, test_labels,num_epochs400):loss nn.MSELoss(reductionnone)input_shape train_features.shape[-1]# 不设置偏置因为我们已经在多项式中实现了它net nn.Sequential(nn.Linear(input_shape, 1, biasFalse))batch_size min(10, train_labels.shape[0])train_iter d2l.load_array((train_features, train_labels.reshape(-1,1)),batch_size)test_iter d2l.load_array((test_features, test_labels.reshape(-1,1)),batch_size, is_trainFalse)trainer torch.optim.SGD(net.parameters(), lr0.01)animator d2l.Animator(xlabelepoch, ylabelloss, yscalelog,xlim[1, num_epochs], ylim[1e-3, 1e2],legend[train, test])for epoch in range(num_epochs):d2l.train_epoch_ch3(net, train_iter, loss, trainer)if epoch 0 or (epoch 1) % 20 0:animator.add(epoch 1, (evaluate_loss(net, train_iter, loss),evaluate_loss(net, test_iter, loss)))print(weight:, net[0].weight.data.numpy())定义 train 的函数用于训练一个简单的神经网络模型。该函数使用均方误差MSE作为损失函数并跟踪在训练集和测试集上的损失。 参数: train_features, test_features: 训练集和测试集的特征。train_labels, test_labels: 训练集和测试集的标签。num_epochs: 训练的轮数默认为 400。 初始化: loss: 初始化一个均方误差损失函数设置为 none这意味着返回的是每个元素的损失而不是批量中损失的平均值或总和。input_shape: 从训练特征中获取输入的形状最后一个维度。net: 创建一个简单的线性神经网络没有偏置项因为可能已经在多项式中实现了它。batch_size: 设置为训练标签数量的最小值或 10取两者中的较小值。train_iter, test_iter: 使用 d2l.load_array 函数创建训练集和测试集的数据迭代器。trainer: 初始化一个随机梯度下降SGD优化器学习率设置为 0.01。animator: 创建一个用于可视化损失随时间变化的动画对象。 训练循环: 使用 for 循环遍历每个训练轮次。在每个轮次中使用 d2l.train_epoch_ch3 函数进行一轮的训练该函数的具体实现未在此代码中给出但通常涉及前向传播、计算损失、反向传播和参数更新。如果当前轮次是 0 或每 20 轮就计算当前轮次在训练集和测试集上的损失并将损失添加到动画器中。 ④三阶多项式函数拟合 # 从多项式特征中选择前4个维度即1,x,x^2/2!,x^3/3! train(poly_features[:n_train, :4], poly_features[n_train:, :4],labels[:n_train], labels[n_train:])下面是我运行的拟合结果拟合结果只能说还行 ⑤线性函数拟合(欠拟合) 模拟下欠拟合的情况 # 从多项式特征中选择前2个维度即1和x train(poly_features[:n_train, :2], poly_features[n_train:, :2],labels[:n_train], labels[n_train:])欠拟合的情况运行的非常好达到预期 ⑤高阶多项式函数拟合(过拟合) 尝试使用一个阶数过高的多项式来训练模型。 在这种情况下没有足够的数据用于学到高阶系数应该具有接近于零的值。 因此这个过于复杂的模型会轻易受到训练数据中噪声的影响。 虽然训练损失可以有效地降低但测试损失仍然很高。 结果表明复杂模型对数据造成了过拟合。 # 从多项式特征中选取所有维度 train(poly_features[:n_train, :], poly_features[n_train:, :],labels[:n_train], labels[n_train:], num_epochs1500)下面是我执行的结果可以发现在epoch600左右开始发生过拟合 Tips 迭代次数有点多动图展示可能有点慢 十三、权重衰退Decay 1、使用均方范数作为限制 现在介绍解决过拟合的常见方法 权重衰减等价于 L 2 L_2 L2​ 范数正则化regularization。正则化通过为模型损失函数添加惩罚项使学出的模型参数值较小是应对过拟合的常用手段。我们先描述 L 2 L_2 L2​范数正则化再解释它为何又称权重衰减。 L 2 L_2 L2​范数正则化在模型原损失函数基础上添加 L 2 L_2 L2​范数惩罚项从而得到训练所需要最小化的函数。 L 2 L_2 L2​范数惩罚项指的是模型权重参数每个元素的平方和与一个正的常数的乘积。以3.1节线性回归中的线性回归损失函数 ℓ ( w 1 , w 2 , b ) 1 n ∑ i 1 n 1 2 ( x 1 ( i ) w 1 x 2 ( i ) w 2 b − y ( i ) ) 2 \ell(w_1, w_2, b) \frac{1}{n} \sum_{i1}^n \frac{1}{2}\left(x_1^{(i)} w_1 x_2^{(i)} w_2 b - y^{(i)}\right)^2 ℓ(w1​,w2​,b)n1​i1∑n​21​(x1(i)​w1​x2(i)​w2​b−y(i))2 为例其中 w 1 , w 2 w_1, w_2 w1​,w2​是权重参数 b b b是偏差参数样本 i i i的输入为 x 1 ( i ) , x 2 ( i ) x_1^{(i)}, x_2^{(i)} x1(i)​,x2(i)​标签为 y ( i ) y^{(i)} y(i)样本数为 n n n。将权重参数用向量 w [ w 1 , w 2 ] \boldsymbol{w} [w_1, w_2] w[w1​,w2​]表示带有 L 2 L_2 L2​范数惩罚项的新损失函数为 ℓ ( w 1 , w 2 , b ) λ 2 n ∥ w ∥ 2 , \ell(w_1, w_2, b) \frac{\lambda}{2n} \|\boldsymbol{w}\|^2, ℓ(w1​,w2​,b)2nλ​∥w∥2, 其中超参数 λ 0 \lambda 0 λ0。当权重参数均为0时惩罚项最小。当 λ \lambda λ较大时惩罚项在损失函数中的比重较大这通常会使学到的权重参数的元素较接近0。当 λ \lambda λ设为0时惩罚项完全不起作用。上式中 L 2 L_2 L2​范数平方 ∥ w ∥ 2 \|\boldsymbol{w}\|^2 ∥w∥2展开后得到 w 1 2 w 2 2 w_1^2 w_2^2 w12​w22​。有了 L 2 L_2 L2​范数惩罚项后在小批量随机梯度下降中我们将线性回归一节中权重 w 1 w_1 w1​和 w 2 w_2 w2​的迭代方式更改为 w 1 ← ( 1 − η λ ∣ B ∣ ) w 1 − η ∣ B ∣ ∑ i ∈ B x 1 ( i ) ( x 1 ( i ) w 1 x 2 ( i ) w 2 b − y ( i ) ) , w 2 ← ( 1 − η λ ∣ B ∣ ) w 2 − η ∣ B ∣ ∑ i ∈ B x 2 ( i ) ( x 1 ( i ) w 1 x 2 ( i ) w 2 b − y ( i ) ) . \begin{aligned} w_1 \leftarrow \left(1- \frac{\eta\lambda}{|\mathcal{B}|} \right)w_1 - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}}x_1^{(i)} \left(x_1^{(i)} w_1 x_2^{(i)} w_2 b - y^{(i)}\right),\\ w_2 \leftarrow \left(1- \frac{\eta\lambda}{|\mathcal{B}|} \right)w_2 - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}}x_2^{(i)} \left(x_1^{(i)} w_1 x_2^{(i)} w_2 b - y^{(i)}\right). \end{aligned} w1​w2​​←(1−∣B∣ηλ​)w1​−∣B∣η​i∈B∑​x1(i)​(x1(i)​w1​x2(i)​w2​b−y(i)),←(1−∣B∣ηλ​)w2​−∣B∣η​i∈B∑​x2(i)​(x1(i)​w1​x2(i)​w2​b−y(i)).​ 可见 L 2 L_2 L2​范数正则化令权重 w 1 w_1 w1​和 w 2 w_2 w2​先自乘小于1的数再减去不含惩罚项的梯度。因此 L 2 L_2 L2​范数正则化又叫权重衰减。权重衰减通过惩罚绝对值较大的模型参数为需要学习的模型增加了限制这可能对过拟合有效。实际场景中我们有时也在惩罚项中添加偏差元素的平方和。 绿线是损失函数的取值, 黄线是惩罚项的取值, 两者都是圈越大取值越大 2、演示对最优解的情况 新的损失函数由两项组成此时求导后梯度有两项了一项将w向绿线中心拉一项将w向原点拉进最后将在w*点达到一个平衡中心点是最优解但是容易过拟合 所以要拉动ta拉动的方法就是用这个平方项把ta往左下角拉 参数更新法则 知识串联这里就是吴恩达老师讲的正则化 总结 权重衰退通过L2正则项使得模型参数不会过大从而控制模型复杂度 正则项权重是控制模型复杂度的超参数 3、权重衰减的实现 ①造数据(合成数据集) 0.05为偏差x为随机的数, c为噪音 n_train, n_test, num_inputs, batch_size 20, 100, 200, 5 true_w, true_b torch.ones((num_inputs, 1)) * 0.01, 0.05 train_data d2l.synthetic_data(true_w, true_b, n_train) train_iter d2l.load_array(train_data, batch_size) test_data d2l.synthetic_data(true_w, true_b, n_test) test_iter d2l.load_array(test_data, batch_size, is_trainFalse)定义变量: n_train: 训练数据集中的样本数量设置为20。n_test: 测试数据集中的样本数量设置为100。num_inputs: 特征的数量设置为200。batch_size: 批量大小即每次迭代时从数据集中读取的样本数量设置为5。 生成真实权重和偏置: true_w: 真实的权重向量形状为(num_inputs, 1)每个元素的值都是0.01。true_b: 真实的偏置项值为0.05。 生成训练数据: 使用d2l.synthetic_data函数 基于给定的真实权重true_w和偏置true_b以及训练样本数量n_train生成一个线性关系的数据集。train_data包含了特征和标签。 创建训练迭代器: 使用d2l.load_array函数 加载训练数据并返回一个迭代器train_iter。这个迭代器可以在训练循环中使用以批量地读取数据。批量大小设置为batch_size。 生成测试数据: 同样使用d2l.synthetic_data函数但基于相同的真实权重和偏置以及测试样本数量n_test生成测试数据集。test_data也包含了特征和标签。 创建测试迭代器: 使用d2l.load_array函数加载测试数据并返回一个迭代器test_iter。与训练迭代器类似但注意这里is_trainFalse这通常用于指示该迭代器是在测试模式下使用可能影响数据的预处理或增强方式尽管在这个简单的线性回归例子中可能没有影响。 最终train_iter和test_iter可以用于训练循环和测试循环中以批量地读取数据并评估模型的性能。 ②初始化模型参数和范数惩罚 def init_params():w torch.normal(0, 1, size(num_inputs, 1), requires_gradTrue)b torch.zeros(1, requires_gradTrue)return [w, b]定义 2 范数惩罚 实现这一惩罚最方便的方法是对所有项求平方后并将它们求和。 def l2_penalty(w):return torch.sum(w.pow(2)) / 2③定义训练代码实现 def train(lambd):w, b init_params()net, loss lambda X: d2l.linreg(X, w, b), d2l.squared_lossnum_epochs, lr 100, 0.003animator d2l.Animator(xlabelepochs, ylabelloss, yscalelog,xlim[5, num_epochs], legend[train, test])for epoch in range(num_epochs):for X, y in train_iter:# 增加了L2范数惩罚项# 广播机制使l2_penalty(w)成为一个长度为batch_size的向量l loss(net(X), y) lambd * l2_penalty(w)l.sum().backward()d2l.sgd([w, b], lr, batch_size)if (epoch 1) % 5 0:animator.add(epoch 1, (d2l.evaluate_loss(net, train_iter, loss),d2l.evaluate_loss(net, test_iter, loss)))print(w的L2范数是, torch.norm(w).item())函数参数: lambd: L2正则化的系数也称为λ或lambda。作为超参数 初始化权重和偏置: 调用init_params()函数 初始化权重w和偏置b为一些随机值或零。 定义模型和网络损失: 使用lambda表达式定义了线性回归模型net该模型接受输入X、权重w和偏置b。定义损失函数为均方误差损失squared_loss。 设置超参数和动画器: num_epochs: 训练轮数设置为100。lr: 学习率设置为0.003。animator: 使用d2l.Animator类创建一个动画器用于可视化训练过程中训练和测试损失的变化。 训练循环: 遍历每一个训练轮次epoch。在每个轮次内遍历训练数据的每一个批次X, y。计算损失包括数据拟合损失和L2正则化项lambd * l2_penalty(w)。使用l.sum().backward()计算梯度并反向传播。使用随机梯度下降SGD更新权重和偏置。 评估和可视化: 每5个轮次计算并记录在训练和测试数据集上的损失。使用animator.add()将损失值添加到动画器中以便稍后可视化。 输出权重的L2范数: 在训练结束后打印出权重w的L2范数以检查正则化的效果。 ④开始训练 下面分三种情况进行描述 (1)忽略正则化直接训练 用lambd 0禁用权重衰减后运行这个代码。 注意这里训练误差有了减少但测试误差没有减少 这意味着出现了严重的过拟合。 w的L2范数是 13.71298885345459 (2)使用权重衰减 下面我们使用权重衰减来运行代码。 注意在这里训练误差增大但测试误差减小。 这正是我们期望从正则化中得到的效果 w的L2范数是 0.387514591217041 lambd往大了调是为了降低w的l2 norm达到避免过拟合的效果 ⑤简洁实现 下面给出简洁实现以便以后学习直接调用 def train_concise(wd):net nn.Sequential(nn.Linear(num_inputs, 1))for param in net.parameters():param.data.normal_()loss nn.MSELoss(reductionnone)num_epochs, lr 100, 0.003# 偏置参数没有衰减trainer torch.optim.SGD([{params:net[0].weight,weight_decay: wd},{params:net[0].bias}], lrlr)animator d2l.Animator(xlabelepochs, ylabelloss, yscalelog,xlim[5, num_epochs], legend[train, test])for epoch in range(num_epochs):for X, y in train_iter:trainer.zero_grad()l loss(net(X), y)l.mean().backward()trainer.step()if (epoch 1) % 5 0:animator.add(epoch 1,(d2l.evaluate_loss(net, train_iter, loss),d2l.evaluate_loss(net, test_iter, loss)))print(w的L2范数, net[0].weight.norm().item())net[0] 是 Linear(in_features100, out_features1, biasTrue) 使用PyTorch的nn神经网络和optim优化器模块来训练一个带有L2正则化的线性回归模型。 定义网络 使用nn.Sequential来定义一个简单的线性回归模型该模型包含一个nn.Linear层它将num_inputs个输入特征映射到一个输出。 初始化参数 使用for循环遍历网络中的所有参数权重和偏置。使用param.data.normal_()将权重参数初始化为正态分布均值为0标准差为1。注意偏置参数通常初始化为0但这里也使用了正态分布初始化不过因为正态分布关于均值对称所以初始化为0的效果和标准差很小的正态分布相近。 定义损失函数 使用nn.MSELoss定义均方误差损失函数并设置reductionnone这样损失函数会为每个样本输出一个损失值而不是它们的平均值。 设置优化器和超参数 使用torch.optim.SGD定义随机梯度下降优化器。为权重参数设置权重衰减即L2正则化的系数为wd。偏置参数没有设置权重衰减weight_decay: wd只应用于权重。设置学习率为lr。 设置动画器 使用d2l.Animator创建一个动画器用于可视化训练过程中的损失变化。 训练循环 遍历每一个训练轮次epoch。在每个轮次内遍历训练数据的每一个批次X, y。在每次迭代开始时使用trainer.zero_grad()清除之前累积的梯度。计算损失值l。对损失值的平均值进行反向传播使用l.mean().backward()。使用trainer.step()更新权重和偏置。 评估和可视化 每5个轮次计算并记录在训练和测试数据集上的损失。使用animator.add()将损失值添加到动画器中。 输出权重的L2范数 在训练结束后打印出第一个线性层net[0]的权重的L2范数。 十四、丢弃法Dropout 深度学习模型常常使用丢弃法dropout来应对过拟合问题 一个好的模型需要对输入数据的扰动鲁棒使用有噪音的数据等价于 Tikhonov正则 丢弃法在层之间加入噪音 1、无差别加入噪音 E ( X i ) 0 ∗ p X i / ( 1 − p ) ∗ ( 1 − p ) X i E(X_i) 0 * p X_i/(1-p) * (1-p) X _i E(Xi​)0∗pXi​/(1−p)∗(1−p)Xi​ 2、丢弃法的使用 举例说明 多层感知机描述了一个单隐藏层的多层感知机。其中输入个数为4隐藏单元个数为5且隐藏单元 h i h_i hi​ i 1 , … , 5 i1, \ldots, 5 i1,…,5的计算表达式为 h i ϕ ( x 1 w 1 i x 2 w 2 i x 3 w 3 i x 4 w 4 i b i ) h_i \phi\left(x_1 w_{1i} x_2 w_{2i} x_3 w_{3i} x_4 w_{4i} b_i\right) hi​ϕ(x1​w1i​x2​w2i​x3​w3i​x4​w4i​bi​) 这里 ϕ \phi ϕ是激活函数 x 1 , … , x 4 x_1, \ldots, x_4 x1​,…,x4​是输入隐藏单元 i i i的权重参数为 w 1 i , … , w 4 i w_{1i}, \ldots, w_{4i} w1i​,…,w4i​偏差参数为 b i b_i bi​。当对该隐藏层使用丢弃法时该层的隐藏单元将有一定概率被丢弃掉。设丢弃概率为 p p p那么有 p p p的概率 h i h_i hi​会被清零有 1 − p 1-p 1−p的概率 h i h_i hi​会除以 1 − p 1-p 1−p做拉伸。丢弃概率是丢弃法的超参数。具体来说设随机变量 ξ i \xi_i ξi​为0和1的概率分别为 p p p和 1 − p 1-p 1−p。使用丢弃法时我们计算新的隐藏单元 h i ′ h_i hi′​ h i ′ ξ i 1 − p h i h_i \frac{\xi_i}{1-p} h_i hi′​1−pξi​​hi​ 由于 E ( ξ i ) 1 − p E(\xi_i) 1-p E(ξi​)1−p因此 E ( h i ′ ) E ( ξ i ) 1 − p h i h i E(h_i) \frac{E(\xi_i)}{1-p}h_i h_i E(hi′​)1−pE(ξi​)​hi​hi​ 即丢弃法不改变其输入的期望值 红色箭头表示使用丢弃法后 3、推理中的丢弃法 推理中的丢弃法并不是直接应用于推理或预测阶段的技术。实际上丢弃法主要是在模型的训练阶段使用以防止过拟合现象。 丢弃法的基本原理是在每次训练迭代中随机丢弃一部分神经元的输出使得模型对输入数据有更好的鲁棒性。这通过减少每个神经元之间的依赖关系来增加模型的泛化能力。在训练时丢弃法可以看作是一种集成学习的方法因为每个训练迭代都在一个不同的、随机选择的神经网络子集上进行。 然而在推理或预测阶段我们通常不会使用丢弃法。相反我们会使用完整的、已经训练好的模型来进行预测。这是因为在推理时我们希望模型能够基于所有的神经元和连接来做出决策而不是仅仅依赖于一部分神经元。 因此虽然丢弃法在训练过程中是一个强大的工具但在推理或预测阶段并不适用。 总结 丢弃法将一些输出项随机置0来控制模型复杂度 常作用在多层感知机的隐藏层输出上 丢弃概率是控制模型复杂度的超参数 5、丢弃法的代码实现 要实现单层的暂退法函数 我们从均匀分布 [0,1]中抽取样本样本数与这层神经网络的维度一致。 然后我们保留那些对应样本大于 的节点把剩下的丢弃。 ① 实现dropout_layer 函数 def dropout_layer(X, dropout):assert 0 dropout 1# 在本情况中所有元素都被丢弃if dropout 1:return torch.zeros_like(X)# 在本情况中所有元素都被保留if dropout 0:return Xmask (torch.rand(X.shape) dropout).float()return mask * X / (1.0 - dropout)rand是产生0-1之间的均匀分布randn是产生均值为0方差为1的高斯分布 因为rand是在0到1均匀分布的所以dropout值的概率就是dropout dropout_layer 函数是一个实现丢弃法Dropout的自定义层。这个函数接收两个参数X输入数据和 dropout丢弃率然后基于丢弃率来随机丢弃X中的一些元素。 首先函数通过断言assert确保 dropout 的值在 [0, 1] 范围内。 如果 dropout 等于 1则函数返回与 X 形状相同且所有元素都为 0 的张量因为这种情况下所有的元素都会被丢弃。 如果 dropout 等于 0则函数直接返回 X因为没有任何元素需要被丢弃。 对于 dropout 在 (0, 1) 范围内的情况函数首先生成一个与 X 形状相同的随机数矩阵其中每个元素都是从 [0, 1) 范围内均匀采样的。然后通过比较这个随机数矩阵和 dropout 值生成一个掩码mask。这个掩码中值大于 dropout 的元素被置为 1保留而值小于或等于 dropout 的元素被置为 0丢弃。 最后函数将掩码与 X 相乘实现丢弃操作。但是为了保持层的输出在训练阶段和推理阶段的期望相同即不进行缩放需要将保留的元素除以 (1 - dropout)。这是因为在训练时由于一部分元素被丢弃了所以剩下的元素实际上被放大了 (1 / (1 - dropout)) 倍。而在推理时我们通常不使用丢弃法所以需要用这个缩放因子来确保输出的一致性。 注意在训练神经网络时通常会在每个训练迭代中调用此函数。然而在推理或测试时应该直接使用原始的、未经过丢弃法处理的模型或者使用一个等效的缩放模型即将每个神经元的权重乘以(1 - dropout)以避免在推理时引入额外的随机性。 ② 测试dropout_layer 函数 X torch.arange(16, dtype torch.float32).reshape((2, 8)) print(X) print(dropout_layer(X, 0.)) print(dropout_layer(X, 0.5)) print(dropout_layer(X, 1.))③开始定义模型 下面给出代码并逐行注释 # 设定两个dropout的比率 dropout1, dropout2 0.2, 0.5# 定义一个名为Net的神经网络类继承自nn.Module class Net(nn.Module):# 初始化函数当创建Net的实例时会自动调用def __init__(self, num_inputs, num_outputs, num_hiddens1, num_hiddens2, is_trainingTrue):# 调用父类nn.Module的初始化函数super(Net, self).__init__()# 存储输入特征的数量self.num_inputs num_inputs# 设置模型的训练模式标志但这种做法并不常见通常使用net.train()和net.eval()self.training is_training# 定义第一个全连接层从num_inputs到num_hiddens1个神经元self.lin1 nn.Linear(num_inputs, num_hiddens1)# 定义第一个dropout层但这里并没有直接使用nn.Dropout而是后面会调用的自定义函数# 注意这里没有实际的dropout层定义只是一个计划使用的变量名# 但通常我们会直接在forward方法中使用nn.Dropout# 定义第二个全连接层从num_hiddens1到num_hiddens2个神经元self.lin2 nn.Linear(num_hiddens1, num_hiddens2)# 同上这是计划使用的第二个dropout层的变量名# 定义第三个全连接层从num_hiddens2到num_outputs个神经元通常是输出层self.lin3 nn.Linear(num_hiddens2, num_outputs)# 定义ReLU激活函数通常用于隐藏层self.relu nn.ReLU()# 前向传播函数定义了数据通过网络的方式def forward(self, X):# 将输入数据X通过第一个全连接层并应用ReLU激活函数H1 self.relu(self.lin1(X.reshape((-1, self.num_inputs))))# 检查当前是否处于训练模式如果是则应用dropoutif self.training True:# 调用自定义的dropout_layer函数 H1 dropout_layer(H1, dropout1)# 将H1通过第二个全连接层并再次应用ReLU激活函数H2 self.relu(self.lin2(H1))# 如果在训练模式再次应用dropoutif self.training True:H2 dropout_layer(H2, dropout2)# 将H2通过第三个全连接层输出层得到最终的输出out self.lin3(H2)# 返回输出return out# 实例化Net类创建一个神经网络对象 # 注意这里并没有传递is_training参数尽管它在__init__方法中定义了所以它将默认为False net Net(num_inputs, num_outputs, num_hiddens1, num_hiddens2)6、简洁实现 为了方便后续调用给出简洁实现代码 net nn.Sequential(nn.Flatten(),nn.Linear(784, 256),nn.ReLU(),# 在第一个全连接层之后添加一个dropout层nn.Dropout(dropout1),nn.Linear(256, 256),nn.ReLU(),# 在第二个全连接层之后添加一个dropout层nn.Dropout(dropout2),nn.Linear(256, 10))def init_weights(m):if type(m) nn.Linear:nn.init.normal_(m.weight, std0.01)net.apply(init_weights);十五、数值稳定性 神经网络的梯度 反向传播 1、衰减和爆炸 当神经网络的层数较多时模型的数值稳定性容易变差。假设一个层数为 L L L的多层感知机的第 l l l层 H ( l ) \boldsymbol{H}^{(l)} H(l)的权重参数为 W ( l ) \boldsymbol{W}^{(l)} W(l)输出层 H ( L ) \boldsymbol{H}^{(L)} H(L)的权重参数为 W ( L ) \boldsymbol{W}^{(L)} W(L)。为了便于讨论不考虑偏差参数且设所有隐藏层的激活函数为恒等映射identity mapping ϕ ( x ) x \phi(x) x ϕ(x)x。给定输入 X \boldsymbol{X} X多层感知机的第 l l l层的输出 H ( l ) X W ( 1 ) W ( 2 ) … W ( l ) \boldsymbol{H}^{(l)} \boldsymbol{X} \boldsymbol{W}^{(1)} \boldsymbol{W}^{(2)} \ldots \boldsymbol{W}^{(l)} H(l)XW(1)W(2)…W(l)。此时如果层数 l l l较大 H ( l ) \boldsymbol{H}^{(l)} H(l)的计算可能会出现衰减或爆炸。举个例子假设输入和所有层的权重参数都是标量如权重参数为0.2和5多层感知机的第30层输出为输入 X \boldsymbol{X} X分别与 0. 2 30 ≈ 1 × 1 0 − 21 0.2^{30} \approx 1 \times 10^{-21} 0.230≈1×10−21衰减和 5 30 ≈ 9 × 1 0 20 5^{30} \approx 9 \times 10^{20} 530≈9×1020爆炸的乘积。类似地当层数较多时梯度的计算也更容易出现衰减或爆炸。 2、梯度爆炸 梯度爆炸的问题 值超出值域(infinity)对于16位浮点数尤为严重(数值区间6e-5-6e4)对学习率敏感如果学习率太大大参数值-更大的梯度如果学习率太小-训练无进展我们可能需要在训练过程不断调整学习率 3、梯度消失 梯度消失的问题 梯度值变成0对16位浮点数尤为严重训练没有进展不管如何选择学习率对于底部层尤为严重仅仅顶部层训练的较好无法让神经网络更深 当数值过大或者过小时会导致数值问题 · 常发生在深度模型中因为其会对个数累乘 十六、模型初始化和激活函数 目标 ①让梯度值在合理的范围内 例如[1e-6,1e3] ②将乘法变加法 ResNet,LSTM ③归一化梯度归一化梯度裁剪 ④合理的权重初始和激活函数 让每层的方差是一个常数 权重初始化 ①在合理值区间里随机初始参数 ②训练开始的时候更容易有数值不稳定 ③远离最优解的地方损失函数表面可能很复杂 ④最优解附近表面会比较平 ⑤使用W(0,0.01)来初始可能对小网络没问题但不能保证深度神经网络 在概率论中方差VarXEX2-E(X)2^, 而由于EX0所以这边直接EX^2)Var(X) 正向方差 反向方差 1、随机初始化模型参数 在神经网络中通常需要随机初始化模型参数。下面我们来解释这样做的原因。 上图多层感知机。为了方便解释假设输出层只保留一个输出单元 o 1 o_1 o1​删去 o 2 o_2 o2​和 o 3 o_3 o3​以及指向它们的箭头且隐藏层使用相同的激活函数。如果将每个隐藏单元的参数都初始化为相等的值那么在正向传播时每个隐藏单元将根据相同的输入计算出相同的值并传递至输出层。在反向传播中每个隐藏单元的参数梯度值相等。因此这些参数在使用基于梯度的优化算法迭代后值依然相等。之后的迭代也是如此。在这种情况下无论隐藏单元有多少隐藏层本质上只有1个隐藏单元在发挥作用。因此正如在前面的实验中所做的那样我们通常将神经网络的模型参数特别是权重参数进行随机初始化。 2、PyTorch的默认随机初始化 随机初始化模型参数的方法有很多。在3.3节线性回归的简洁实现中使用torch.nn.init.normal_()使模型net的权重参数采用正态分布的随机初始化方式。不过PyTorch中nn.Module的模块参数都采取了较为合理的初始化策略不同类型的layer具体采样的哪一种初始化方法的可参考源代码因此一般不用我们考虑。 3 、Xavier随机初始化 还有一种比较常用的随机初始化方法叫作Xavier随机初始化。 假设某全连接层的输入个数为 a a a输出个数为 b b bXavier随机初始化将使该层中权重参数的每个元素都随机采样于均匀分布 U ( − 6 a b , 6 a b ) . U\left(-\sqrt{\frac{6}{ab}}, \sqrt{\frac{6}{ab}}\right). U(−ab6​ ​,ab6​ ​). 它的设计主要考虑到模型参数初始化后每层输出的方差不该受该层输入个数影响且每层梯度的方差也不该受该层输出个数影响。 关于Xavier在吴恩达课程学习介绍过 4、线性激活函数 合理的权重初始值和激活函数的选取可以提升数值稳定性 十七、PyTorch 神经网络基础 1、模型构造 定义神经网络: net nn.Sequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))使用nn.Sequential 定义了一个简单的序贯模型它包含两个线性层nn.Linear和一个ReLU激活函数。第一个线性层将输入从20个特征转换到256个特征然后ReLU激活函数对这些特征进行非线性变换。最后第二个线性层将256个特征转换到10个输出。 生成随机输入数据: X torch.rand(2, 20)这里 使用torch.rand函数生成了一个形状为(2, 20)的张量其中包含了从均匀分布在0和1之间中随机抽取的浮点数。这表示我们有两个样本每个样本有20个特征。 通过神经网络传递输入数据: net(X)将输入数据X传递给神经网络net时它首先通过第一个线性层进行线性变换然后应用ReLU激活函数最后通过第二个线性层进行另一个线性变换。这个过程的结果是一个形状为(2, 10)的张量表示两个样本在10个输出类别上的得分或概率如果这是一个分类任务的话。 selfthis指针 nn.ReLU()是构造了一个ReLU对象并不是函数调用而F.ReLU()是函数调用 class MySequential(nn.Module):def __init__(self, *args):super().__init__()for idx, module in enumerate(args):# 这里module是Module子类的一个实例。我们把它保存在Module类的成员# 变量_modules中。_module的类型是OrderedDictself._modules[str(idx)] moduledef forward(self, X):# OrderedDict保证了按照成员添加的顺序遍历它们for block in self._modules.values():X block(X)return XMySequential 类试图模拟 PyTorch 内置的 nn.Sequential 的行为但有一个小的不同它使用模块在 args 中的索引转换为字符串作为其在 _modules 字典中的键。 下面是代码的详细解释 初始化方法 __init__: 调用父类 nn.Module 的初始化方法 super().__init__()。遍历传递给 __init__ 的所有参数这些参数应该是 nn.Module 的子类实例。使用 enumerate 函数获取每个模块的索引idx和模块本身module。将模块的索引转换为字符串并作为键将模块作为值存储在 _modules 字典中。在 PyTorch 中_modules 是一个 OrderedDict用于存储子模块并确保它们在 forward 方法中按照添加的顺序被遍历。 前向传播方法 forward: 这个方法定义了数据如何通过网络传递。遍历 _modules 字典中的所有值即模块。将输入 X 传递给当前模块并将输出重新赋值给 X以便它在下一个模块中使用。返回最终的输出 X。 注意虽然这段代码在功能上类似于 nn.Sequential但它使用索引作为键来存储模块这可能会导致在后续的代码或调试中难以理解和跟踪模块。相比之下nn.Sequential 使用简单的字符串如 0, 1, …作为键这更易于理解和跟踪。 X block(X) 表示将输入数据X传递给一个名为block的神经网络层进行处理并将处理后的结果重新赋值给X。 class FixedHiddenMLP(nn.Module):def __init__(self):super().__init__()# 不计算梯度的随机权重参数。因此其在训练期间保持不变self.rand_weight torch.rand((20, 20), requires_gradFalse)self.linear nn.Linear(20, 20)def forward(self, X):X self.linear(X)# 使用创建的常量参数以及relu和mm函数X F.relu(torch.mm(X, self.rand_weight) 1)# 复用全连接层。这相当于两个全连接层共享参数X self.linear(X)# 控制流while X.abs().sum() 1:X / 2return X.sum()2、参数管理 print(*[(name, param.shape) for name, param in net[0].named_parameters()])net[0]这假设net是一个可以索引的对象例如一个列表或元组并且它至少有一个元素这个元素是一个PyTorch的神经网络模型nn.Module的子类。net[0]取出这个列表或元组中的第一个模型。net[0].named_parameters()这是PyTorch中nn.Module类的一个方法它返回模型中所有可训练参数的名称name和参数本身param的迭代器。列表推导[(name, param.shape) for name, param in net[0].named_parameters()]对于模型net[0]中的每一个参数都创建一个元组其中包含参数的名称和参数的形状shape。print(*...)这里使用了*参数展开功能它可以将列表或元组中的元素作为独立的参数传递给print函数。所以这段代码会打印出模型net[0]中所有参数的名称和形状每个参数占一行。 套娃开始 从嵌套块收集参数 内置初始化 def init_normal(m):if type(m) nn.Linear:nn.init.normal_(m.weight, mean0, std0.01)nn.init.zeros_(m.bias) net.apply(init_normal) net[0].weight.data[0], net[0].bias.data[0]apply函数的功能是将传入的函数应用到指定的module上不只是初始化做什么都行 如果m的类型是nn.Linear即它是一个线性层那么使用nn.init.normal_函数来初始化权重m.weight。这里权重被初始化为均值为0、标准差为0.01的正态分布。使用nn.init.zeros_函数来初始化偏置m.bias。偏置被初始化为0。 net.apply(init_normal) 对net中的每个子模块如nn.Linear层、nn.Conv2d层等应用init_normal函数。这意味着如果net或其任何子模块包含nn.Linear层那么这些层的权重和偏置都会被上述方式初始化。 加下划线为原地操作不加为复制后修改 参数绑定 # 我们需要给共享层一个名称以便可以引用它的参数 shared nn.Linear(8, 8) net nn.Sequential(nn.Linear(4, 8), nn.ReLU(),shared, nn.ReLU(),shared, nn.ReLU(),nn.Linear(8, 1)) net(X) # 检查参数是否相同 print(net[2].weight.data[0] net[4].weight.data[0]) net[2].weight.data[0, 0] 100 # 确保它们实际上是同一个对象而不只是有相同的值 print(net[2].weight.data[0] net[4].weight.data[0])创建共享层并定义网络 shared nn.Linear(8, 8) net nn.Sequential(nn.Linear(4, 8), nn.ReLU(),shared, nn.ReLU(),shared, nn.ReLU(),nn.Linear(8, 1))这里shared被添加到net两次。 检查参数是否相同 print(net[2].weight.data[0] net[4].weight.data[0])由于net[2]和net[4]引用的是同一个nn.Linear实例它们的权重数据将是相同的所以上述打印语句将输出全为True的张量假设权重数据的第一个元素是一个向量。 修改共享层的权重并再次检查 net[2].weight.data[0, 0] 100 print(net[2].weight.data[0] net[4].weight.data[0])当 修改net[2].weight.data[0, 0]时由于net[2]和net[4]引用的是同一个对象所以net[4].weight.data[0, 0]也会被修改为100。 3、读写文件 加载和保存模型参数 首先定义MLP class MLP(nn.Module):def __init__(self):super().__init__()self.hidden nn.Linear(20, 256)self.output nn.Linear(256, 10)def forward(self, x):return self.output(F.relu(self.hidden(x)))net MLP() X torch.randn(size(2, 20)) Y net(X)存储文件 torch.save(net.state_dict(), mlp.params)开始加载 clone MLP() clone.load_state_dict(torch.load(mlp.params)) clone.eval()eval()方法是将模型设置为评估模式 train模式net.train())和eval模式net.eval())。一般的神经网络中这两种模式是一样的只有当模型中存在dropout和batchnorm的时候才有区别。 十八、Kaggle房价预测实战 https://www.kaggle.com/c/house-prices-advanced-regression-techniques 沐神个人版房价预测竞赛链接如下 https://www.kaggle.com/c/california-house-prices/discussion 1、数据集的获取 下载数据集 将数据集缓存在本地目录默认情况下为…/data中 并返回下载文件的名称。 如果缓存目录中已经存在此数据集文件并且其sha-1与存储在DATA_HUB中的相匹配 我们将使用缓存的文件以避免重复的下载。 def download(name, cache_diros.path.join(.., data)): #save下载一个DATA_HUB中的文件返回本地文件名assert name in DATA_HUB, f{name} 不存在于 {DATA_HUB}url, sha1_hash DATA_HUB[name]os.makedirs(cache_dir, exist_okTrue)fname os.path.join(cache_dir, url.split(/)[-1])if os.path.exists(fname):sha1 hashlib.sha1()with open(fname, rb) as f:while True:data f.read(1048576)if not data:breaksha1.update(data)if sha1.hexdigest() sha1_hash:return fname # 命中缓存print(f正在从{url}下载{fname}...)r requests.get(url, streamTrue, verifyTrue)with open(fname, wb) as f:f.write(r.content)return fname(1)断言使用 assert 语句确保 name 存在于 DATA_HUB 字典中。如果不存在程序将抛出一个异常。 (2)使用 os.makedirs 创建缓存目录如果它还不存在。exist_okTrue 意味着如果目录已经存在则不会抛出错误。 f.read(1048576)这是一个调用文件对象的 read 方法用于读取文件内容。传递给 read 方法的参数 1048576 是一个整数指定了读取的字节数。这个特定的数字是 1MB因为 1048576 字节等于 1MB 开始下载并解压如下 def download_extract(name, folderNone): #save下载并解压zip/tar文件fname download(name)base_dir os.path.dirname(fname)data_dir, ext os.path.splitext(fname)if ext .zip:fp zipfile.ZipFile(fname, r)elif ext in (.tar, .gz):fp tarfile.open(fname, r)else:assert False, 只有zip/tar文件可以被解压缩fp.extractall(base_dir)return os.path.join(base_dir, folder) if folder else data_dirdef download_all(): #save下载DATA_HUB中的所有文件for name in DATA_HUB:download(name)2、查看数据便于后续处理 train_data: 这是一个pandas DataFrame通常用于存储表格型的数据。iloc[]: 这是pandas DataFrame的一个基于整数位置的索引器用于基于行和列的整数位置来选择数据。0:4: 这选择了前4行包括第0行但不包括第4行。在Python的切片语法中结束索引是不包含的。[0, 1, 2, 3, -3, -2, -1]: 这选择了特定的列。具体来说它选择了第0列、第1列、第2列、第3列、倒数第3列、倒数第2列和最后一列。负索引是从后往前数的所以-3表示倒数第三列-2表示倒数第二列-1表示最后一列。 所以train_data.iloc[0:4, [0, 1, 2, 3, -3, -2, -1]]会返回一个新的DataFrame它包含train_data的前4行和指定的列第0、1、2、3、倒数第3、倒数第2和最后一列。 假设train_data是这样的 A B C D E F G 0 1 2 3 4 5 6 7 1 8 9 10 11 12 13 14 2 15 16 17 18 19 20 21 3 22 23 24 25 26 27 28 4 29 30 31 32 33 34 35那么train_data.iloc[0:4, [0, 1, 2, 3, -3, -2, -1]]会返回 A B C D F G 0 1 2 3 4 6 7 1 8 9 10 11 13 14 2 15 16 17 18 20 21 3 22 23 24 25 27 283、数据预处理 进行简化处理,删除第一例ID all_features pd.concat((train_data.iloc[:, 1:-1], test_data.iloc[:, 1:]))将train_data DataFrame中除了第一列和最后一列的所有列与test_data DataFrame中除了第一列的所有列合并在一起。 train_data.iloc[:, 1:-1]: 这选择了train_data DataFrame中从第二列开始到倒数第二列结束的所有列不包括第一列和最后一列。 : 表示选择所有的行。1:-1 是列的切片其中 1 表示从第二列开始因为索引是从0开始的-1 表示到倒数第二列结束。 test_data.iloc[:, 1:]: 这选择了test_data DataFrame中从第二列开始到最后一列的所有列不包括第一列。 : 表示选择所有的行。1: 是列的切片其中 1 表示从第二列开始: 表示到最后一列结束。 pd.concat(...): 这将上面选择出来的两个DataFrame在纵向上行方向上进行合并。默认情况下concat会按照原始顺序合并DataFrame但如果需要的话 也可以指定一个axis参数来改变合并的方向axis0表示纵向合并axis1表示横向合并。 所以all_features将会是一个新的DataFrame它包含了train_data和test_data中除了第一列之外的所有列并且这些列是按照它们在原始DataFrame中的顺序排列的。 处理残缺数据 将所有缺失的值替换为相应特征的平均值 .然后为了将所有特征放在一个共同的尺度上 通过将特征重新缩放到零均值和单位方差来标准化数据 # 若无法获得测试数据则可根据训练数据计算均值和标准差 numeric_features all_features.dtypes[all_features.dtypes ! object].index all_features[numeric_features] all_features[numeric_features].apply(lambda x: (x - x.mean()) / (x.std())) # 在标准化数据之后所有均值消失因此我们可以将缺失值设置为0 all_features[numeric_features] all_features[numeric_features].fillna(0)确定数值型特征 numeric_features all_features.dtypes[all_features.dtypes ! object].index首先通过 all_features.dtypes 获取DataFrame中每列的数据类型。然后它筛选出非object即非字符串或混合类型的列索引这些列通常包含数值型数据如整数、浮点数等。这些索引被存储在 numeric_features 变量中。 2. 标准化数值型特征 all_features[numeric_features] all_features[numeric_features].apply(lambda x: (x - x.mean()) / (x.std()))对 numeric_features 中指定的列进行标准化处理。对于每一列 x它计算该列的均值 x.mean() 和标准差 x.std()然后用列中的每个值减去均值并除以标准差从而实现标准化。这里使用了 apply 函数和 lambda 表达式来逐列应用标准化操作。 3. 处理缺失值 all_features[numeric_features] all_features[numeric_features].fillna(0)在标准化之后由于所有值都已经是相对于均值的偏移量即所有列的均值现在都是0因此可以将缺失值NaN安全地替换为0。这是因为在标准化之后0已经代表了原始数据的均值。使用 fillna(0) 方法可以将 numeric_features 中所有列的缺失值替换为0。 4、离散值的处理 # “Dummy_naTrue”将“na”缺失值视为有效的特征值并为其创建指示符特征 all_features pd.get_dummies(all_features, dummy_naTrue) all_features.shape使用独热编码进行替换 SalePrice - 房价这也是我们预测的对象 MSSubClass: 建筑类别 MSZoning: The general zoning classification空间分类 pd.get_dummies() 函数是一个用于将分类变量通常是字符串或布尔值转换为one-hot编码或称为独热编码的函数。 One-hot编码是机器学习中的一个常用技巧特别是当模型需要处理类别数据时。 dummy_naTrue 参数是一个特殊选项它告诉 pd.get_dummies() 函数将NaN即“缺失值”也视为一个有效的类别并为其创建一个额外的二进制列。 通常默认情况下pd.get_dummies() 会忽略NaN值但在设置 dummy_naTrue 后它会为每一个原本包含NaN值的列都创建一个新的列该列在原始列是NaN的位置为1否则为0。 首先pd.get_dummies(all_features, dummy_naTrue) 会将 all_features DataFrame 中的所有分类变量转换为one-hot编码并且对于原本包含NaN的列会为其创建一个新的列来表示这些NaN值。 然后all_features.shape 会返回处理后的DataFrame的维度即行数, 列数。 5、提取NumPy并转化为张量 n_train train_data.shape[0] 从train_data DataFrame 中获取训练数据的行数即样本数量并将其存储在n_train变量中。 train_features torch.tensor(all_features[:n_train].values.astype(float), dtypetorch.float32) 这行代码首先从all_features中选取前n_train个样本即训练数据。.values将这部分数据转换为NumPy数组。.astype(float)确保这些数据是浮点数类型。最后使用torch.tensor将这些数据转换为PyTorch张量并指定数据类型为torch.float32即32位浮点数。 test_features torch.tensor(all_features[n_train:].values.astype(float), dtypetorch.float32) 这行代码与上一行类似但它是从n_train之后选取all_features中的样本即测试数据。其余的处理步骤与训练特征相同。 train_labels torch.tensor(train_data.SalePrice.values.astype(float).reshape(-1, 1), dtypetorch.float32) 这行代码从train_data中提取SalePrice列作为训练标签。.values将其转换为NumPy数组。.astype(float)确保这些标签是浮点数类型。.reshape(-1, 1)将标签数组重新塑形为一个二维数组其中每个样本的标签都是一个单独的行尽管它可能只有一个元素。这样做常常是为了使标签的形状与特征矩阵的形状兼容特别是在进行某些类型的神经网络训练时。最后使用torch.tensor将这些标签转换为PyTorch张量并指定数据类型为torch.float32。 总结这段代码的目的是从原始数据集中提取特征和标签并将它们转换为PyTorch张量以便后续在PyTorch中进行模型训练。 6、训练模型 使用一个基本的线性回归模型和平方损失函数来训练模型。 loss torch.nn.MSELoss()def get_net(feature_num):net nn.Linear(feature_num, 1)for param in net.parameters():nn.init.normal_(param, mean0, std0.01)return net下面定义比赛用来评价模型的对数均方根误差。给定预测值 y ^ 1 , … , y ^ n \hat y_1, \ldots, \hat y_n y^​1​,…,y^​n​和对应的真实标签 y 1 , … , y n y_1,\ldots, y_n y1​,…,yn​它的定义为 1 n ∑ i 1 n ( log ⁡ ( y i ) − log ⁡ ( y ^ i ) ) 2 . \sqrt{\frac{1}{n}\sum_{i1}^n\left(\log(y_i)-\log(\hat y_i)\right)^2}. n1​i1∑n​(log(yi​)−log(y^​i​))2 ​. 对数均方根误差的实现如下。 def log_rmse(net, features, labels):with torch.no_grad():# 将小于1的值设成1使得取对数时数值更稳定clipped_preds torch.max(net(features), torch.tensor(1.0))rmse torch.sqrt(loss(clipped_preds.log(), labels.log()))return rmse.item()下面的训练函数跟本章中前几节的不同在于使用了Adam优化算法。相对之前使用的小批量随机梯度下降它对学习率相对不那么敏感。 def train(net, train_features, train_labels, test_features, test_labels,num_epochs, learning_rate, weight_decay, batch_size):train_ls, test_ls [], []train_iter d2l.load_array((train_features, train_labels), batch_size)# 这里使用的是Adam优化算法optimizer torch.optim.Adam(net.parameters(),lr learning_rate,weight_decay weight_decay)for epoch in range(num_epochs):for X, y in train_iter:optimizer.zero_grad()l loss(net(X), y)l.backward()optimizer.step()train_ls.append(log_rmse(net, train_features, train_labels))if test_labels is not None:test_ls.append(log_rmse(net, test_features, test_labels))return train_ls, test_ls参数 net: 要训练的神经网络模型。train_features, train_labels: 训练数据的特征和标签。test_features, test_labels: 测试数据的特征和标签可选如果提供则计算测试误差。num_epochs: 训练的总轮数epoch。learning_rate: 学习率。weight_decay: 权重衰减也称为L2正则化项。batch_size: 批处理大小。 初始化 train_ls, test_ls: 分别用于存储每一轮训练后的训练误差和测试误差如果提供了测试数据。train_iter: 使用d2l 的load_array函数将训练数据转换为可迭代的批处理数据。 优化器 使用PyTorch的torch.optim.Adam优化器。这个优化器使用了Adam算法来更新网络的权重。net.parameters(): 获取神经网络中所有可训练的参数。lr: 学习率。weight_decay: 权重衰减。 训练循环 对于每一个epoch即完整的训练数据集遍历一次 对于训练数据集中的每一个batchX, y optimizer.zero_grad(): 清空之前累积的梯度。l loss(net(X), y): 计算预测值与实际值之间的损失。这里假设loss是一个已定义的损失函数如均方误差。l.backward(): 反向传播计算损失相对于模型参数的梯度。optimizer.step(): 使用优化器更新模型的权重。 计算并记录当前epoch的训练误差。如果提供了测试数据则计算并记录当前epoch的测试误差。 返回值 train_ls, test_ls: 分别包含每一轮训练后的训练误差和测试误差的列表。 7 、折交叉验证 有助于模型选择和超参数调整。 我们首先需要定义一个函数在 折交叉验证过程中返回第 折的数据。 具体地说它选择第 个切片作为验证数据其余部分作为训练数据 def get_k_fold_data(k, i, X, y):assert k 1fold_size X.shape[0] // kX_train, y_train None, Nonefor j in range(k):idx slice(j * fold_size, (j 1) * fold_size)X_part, y_part X[idx, :], y[idx]if j i:X_valid, y_valid X_part, y_partelif X_train is None:X_train, y_train X_part, y_partelse:X_train torch.cat([X_train, X_part], 0)y_train torch.cat([y_train, y_part], 0)return X_train, y_train, X_valid, y_validdef k_fold(k, X_train, y_train, num_epochs, learning_rate, weight_decay,batch_size):train_l_sum, valid_l_sum 0, 0for i in range(k):data get_k_fold_data(k, i, X_train, y_train)net get_net()train_ls, valid_ls train(net, *data, num_epochs, learning_rate,weight_decay, batch_size)train_l_sum train_ls[-1]valid_l_sum valid_ls[-1]if i 0:d2l.plot(list(range(1, num_epochs 1)), [train_ls, valid_ls],xlabelepoch, ylabelrmse, xlim[1, num_epochs],legend[train, valid], yscalelog)print(f折{i 1}训练log rmse{float(train_ls[-1]):f}, f验证log rmse{float(valid_ls[-1]):f})return train_l_sum / k, valid_l_sum / k将数据集划分为k个大小相近的子集称为“折”并返回第i折作为验证集validation set其他k-1折作为训练集training set。 参数 k: 折数即要将数据集划分为多少个子集。i: 当前折的索引用于指定哪一折作为验证集。X: 特征数据通常是一个二维数组或张量。y: 标签数据通常是一个一维数组或张量。 断言 assert k 1: 确保k大于1因为k折交叉验证至少需要两个折。 计算每个折的大小 fold_size X.shape[0] // k: 计算每个折应包含多少样本。这里使用整数除法确保样本能够均匀分配到各个折中。 初始化训练集和验证集 X_train, y_train None, None: 初始化训练集的特征和标签为None。 循环遍历每个折 使用for循环遍历从0到k-1的索引j。idx slice(j * fold_size, (j 1) * fold_size): 计算当前折的索引范围。X_part, y_part X[idx, :], y[idx]: 从特征数据和标签数据中提取当前折的数据。 区分训练集和验证集 如果当前折的索引j等于i则将X_part和y_part分别赋值给X_valid和y_valid表示这一折是验证集。如果X_train和y_train尚未被初始化即它们仍然是None则将X_part和y_part分别赋值给X_train和y_train表示这是第一个折并且它将被用作训练集的开始部分。如果X_train和y_train已经被初始化即它们不是None则使用torch.cat函数将当前折的数据与已有的训练集数据合并。 返回结果 函数最后返回四个值训练集的特征X_train、训练集的标签y_train、验证集的特征X_valid和验证集的标签y_valid。 8、在Kaggle 中实现并提交
http://www.w-s-a.com/news/684760/

相关文章:

  • 公司网站建设的意义网易做网站
  • 网络营销案例分析与实践搜外seo
  • 手机建网站挣钱吗wordpress面包屑
  • 淘客做网站怎么备案网站开发工具的是什么
  • 提供大良网站建设郑州网站建设网站开发
  • 邢台做wap网站价格wordpress评论滑动
  • 绝味鸭脖网站建设规划书江苏建设人才网 官网
  • 网站源码授权破解centos wordpress 整站
  • 建设一个私人视频网站wordpress js
  • 手机企业网站制作流程3d建模自学
  • 网站优化方案和实施wordpress的归档
  • 建设事业单位网站多少钱集艾设计公司官网
  • 网站建设与管理方案书图片的制作方法
  • 中文建网站美发网站模板带手机版
  • 免费聊天不充值软件windows优化大师下载安装
  • 网站优化的关键词自己怎么做外贸网站空间
  • 现在建设的网站有什么劣势温州互联网公司
  • 重庆自助企业建站模板淘宝关键词top排行榜
  • 平邑网站制作买高端品牌网站
  • 深圳建网站三千网站安全代维
  • 西宁市精神文明建设网站装饰设计甲级资质
  • 做教育行业营销类型的网站徐州做网站多少钱
  • 临沂品牌网站制作企业网站建设搜集资料
  • wordpress注册验证码手机网站优化
  • 往建设厅网站上传东西做衣服的教程网站有哪些
  • 网上商城网站设计免费咨询口腔科医生回答在线
  • 南京网站c建设云世家 s浏览器
  • 如何做镜像别人网站wordpress菜单对齐修改
  • 长春网站建设net企业公示信息查询官网
  • 金鹏建设集团网站可在哪些网站做链接