嵩县网站建设,什么样的口罩才具有防疫功能,为公司做的图可以上传网站吗,专业分销网站建设专栏#xff1a;神经网络复现目录
深度学习神经网络基础知识(二)
本文讲述神经网络基础知识#xff0c;具体细节讲述前向传播#xff0c;反向传播和计算图#xff0c;同时讲解神经网络优化方法#xff1a;权重衰减#xff0c;Dropout等方法#xff0c;最后进行Kaggle实…专栏神经网络复现目录
深度学习神经网络基础知识(二)
本文讲述神经网络基础知识具体细节讲述前向传播反向传播和计算图同时讲解神经网络优化方法权重衰减Dropout等方法最后进行Kaggle实战具体用一个预测房价的例子使用上述方法。
文章部分文字和代码来自《动手学深度学习》 文章目录深度学习神经网络基础知识(二)范数权重衰减定义权重衰减的从零实现运行结果权重衰减的简洁实现暂退法Dropout定义暂退法的从零实现运行结果暂退法的简洁实现范数
LpL_pLp范数是一种向量范数定义如下
∣x∣p(∣x1∣p∣x2∣p⋯∣xn∣p)1p\left|\boldsymbol{x}\right|{p}\left(\left|x{1}\right|^{p}\left|x_{2}\right|^{p}\cdots\left|x_{n}\right|^{p}\right)^{\frac{1}{p}}∣x∣p(∣x1∣p∣x2∣p⋯∣xn∣p)p1
其中p≥1p \geq 1p≥1x(x1,x2,⋯,xn)\boldsymbol{x}(x_1, x_2, \cdots, x_n)x(x1,x2,⋯,xn) 是一个 nnn 维向量。当 p2p2p2 时LpL_pLp范数也称为欧几里得范数Euclidean norm常用于表达向量的长度或者大小。当 p1p1p1 时LpL_pLp范数也称为曼哈顿范数Manhattan norm或者 ℓ1\ell_1ℓ1范数常用于表达向量中各个元素的绝对值之和。当 p→∞p \rightarrow \inftyp→∞ 时LpL_pLp范数也称为切比雪夫范数Chebyshev norm或者 ℓ∞\ell_\inftyℓ∞ 范数常用于表达向量中绝对值最大的元素。
L0L_0L0范数不是向量范数因为它并不满足向量范数的三个条件之一即正定性。通常把向量 x\boldsymbol{x}x 中非零元素的个数称为 x\boldsymbol{x}x 的 L0L_0L0 范数但这并不是一个数学上合理的定义。
常见的范数有以下几种
L1L^1L1 范数∣∣x∣∣1∑i1n∣xi∣||x||1 \sum{i1}^n |x_i|∣∣x∣∣1∑i1n∣xi∣
L2L^2L2 范数∣∣x∣∣2∑i1nxi2||x||2 \sqrt{\sum{i1}^n x_i^2}∣∣x∣∣2∑i1nxi2
权重衰减
定义
权重衰减是一种用于降低过拟合的正则化技术。其原理是通过在模型训练过程中增加一个惩罚项也称作正则化项来抑制模型的复杂度从而达到减小过拟合的效果。
具体来说在损失函数中添加一个正则化项一般会对模型的参数进行L2L_2L2范数的约束也就是让模型的参数尽量小。这样在模型训练过程中不仅会尽量减小训练数据的损失还会尽量让模型参数的平方和小从而达到抑制模型过拟合的效果。
权重衰减的损失函数为 其中 L(w,b)\mathcal{L}(\boldsymbol{w}, b)L(w,b) 是原始的无正则化项的损失函数∣w∣2|\boldsymbol{w}|^2∣w∣2 表示模型参数的L2L_2L2范数λ\lambdaλ 是正则化强度nnn 是训练样本数。
在优化算法中我们需要对这个损失函数进行梯度下降。由于正则化项的梯度为 λnw\frac{\lambda}{n}\boldsymbol{w}nλw因此我们需要对原始的梯度加上这个正则化项的梯度
w←(1−ηλ∣B∣)w−η∣B∣∑i∈B∂∂wl(i)(w,b)w \leftarrow (1 - \frac{\eta \lambda}{|B|})w - \frac{\eta}{|B|} \sum_{i \in B} \frac{\partial}{\partial w} l^{(i)}(w, b) w←(1−∣B∣ηλ)w−∣B∣ηi∈B∑∂w∂l(i)(w,b)
其中www是待更新的权重参数η\etaη是学习率λ\lambdaλ是正则化超参数即权重衰减超参数∣B∣|B|∣B∣是当前小批量中的样本数l(i)(w,b)l^{(i)}(w, b)l(i)(w,b)是第iii个样本的损失函数∂∂wl(i)(w,b)\frac{\partial}{\partial w} l^{(i)}(w, b)∂w∂l(i)(w,b)是对权重参数的损失函数梯度。
权重衰减的从零实现
构造生成数据集的函数
%matplotlib inline
import torch
from torch import nn
from d2l import torch as d2l
#生成数据集
def synthetic_data(w,b,num):#x通过正态分布生成xtorch.normal(0,1,(num,len(w)))ytorch.matmul(x,w)b#数据集中加入噪声ytorch.normal(0,0.01,y.shape)return x,y.reshape(-1,1)构造一个数据迭代器
def load_array(data_arrays, batch_size, is_trainTrue): #save构造一个PyTorch数据迭代器dataset data.TensorDataset(*data_arrays)return data.DataLoader(dataset, batch_size, shuffleis_train)生成数据集
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 synthetic_data(true_w, true_b, n_train)
train_iter load_array(train_data, batch_size)
test_data synthetic_data(true_w, true_b, n_test)
test_iter load_array(test_data, batch_size, is_trainFalse)初始化模型参数
def init_params():w torch.normal(0, 1, size(num_inputs, 1), requires_gradTrue)b torch.zeros(1, requires_gradTrue)return [w, b]定义L2范数惩罚
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())运行结果
未使用权重衰减 使用权重衰减
权重衰减的简洁实现
def train_concise(weight_decay):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 optim.SGD(model.parameters(), lrlr, weight_decayweight_decay)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())关注这行代码
trainer optim.SGD(model.parameters(), lrlr, weight_decayweight_decay)其中weight_decay参数即为lambda
暂退法Dropout
定义
Dropout是一种用于神经网络的正则化技术旨在减少模型的过拟合。该算法的核心思想是在网络的训练过程中随机“丢弃”一部分神经元从而强制模型学习更加鲁棒和通用的特征。在测试时所有神经元都保留但是输出值需要乘以一个固定比例以保持期望输出不变。
具体来说假设我们有一个包含LLL个层的神经网络。对于第iii层它的输出为h(i)h^{(i)}h(i)。在训练时我们按照一定的概率ppp来随机选择一部分神经元将它们的输出值设置为0。因此第iii层的输出为
h~(i)r(i)⊙h(i)\tilde{h}^{(i)}r^{(i)}\odot h^{(i)}h~(i)r(i)⊙h(i)
其中r(i)r^{(i)}r(i)是一个与h(i)h^{(i)}h(i)具有相同形状的二进制向量其中元素值为1的概率为ppp值为0的概率为1−p1-p1−p⊙\odot⊙表示按元素相乘。在前向传播过程中我们使用h~(i)\tilde{h}^{(i)}h~(i)代替h(i)h^{(i)}h(i)进行计算。在反向传播过程中由于某些神经元的输出被设置为0我们只需要将其对应的梯度清零即可。
在测试时我们需要保留所有神经元的输出但是为了保持期望输出不变我们需要将所有神经元的输出值乘以ppp即
htest(i)p⋅h(i)h^{(i)}_{test}p\cdot h^{(i)}htest(i)p⋅h(i)
下图形象的展示了暂退法的效果
暂退法的从零实现
这是一个实现dropout算法的函数它接受一个输入张量X和一个dropout概率dropout然后返回一个应用了dropout的输出张量。
具体来说该函数会生成一个与X形状相同的掩码张量其中每个元素都是随机生成的0或1生成方式是根据概率dropout与0比较如果大于dropout则为1否则为0。然后将掩码张量与X相乘并除以(1 - dropout)这个操作相当于将保留下来的元素值除以它们的概率。最后返回应用了dropout的输出张量。
import torch
from torch import nn
from d2l import torch as d2ldef 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)具体关注一下
mask (torch.rand(X.shape) dropout).float()这一行代码的作用是生成一个与X形状相同的张量mask并且其中的每个元素都是0或1。这里的0和1表示相应的X元素是否被保留而生成这些0和1的方式是随机的因为我们用torch.rand()函数生成一个形状与X相同的随机张量并将其中的每个元素与dropout做比较。
比较的结果是一个布尔类型的张量即对于X中的每个元素如果随机生成的相应元素的值大于dropout那么在mask中相应位置的值为1表示保留反之如果随机生成的值小于等于dropout那么在mask中相应位置的值为0表示丢弃。
最后为了保持期望的值不变我们将所有保留的元素的值除以 1- dropout这是因为被保留的概率是1- dropout。所以最终得到的输出是一个X的掩码版本其中的一些元素被随机置为零。
测试一下我们写的dropout层
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.))定义模型参数
num_inputs, num_outputs, num_hiddens1, num_hiddens2 784, 10, 256, 256定义模型 这次我们使用了和以往不同、面向对象的模型定义方式需要重写__init__和forward函数
init 方法用于定义网络结构包括网络层、激活函数、损失函数等并初始化权重、偏差等参数。这些网络参数在训练过程中会不断地更新。
forward 方法用于定义数据在网络中的正向传播也就是模型从输入到输出的计算过程即输入数据经过网络的各层计算最终得到输出。在该方法中我们可以任意组合各种网络层及其参数实现自己所需要的网络结构和计算过程。
在下面的代码中Net 类继承自 nn.Module其中 init 方法用于定义网络的结构包括三个全连接层和一个 ReLU 激活函数。forward 方法用于实现数据在网络中的正向传播计算包括将输入 X 经过全连接层和激活函数得到输出 out。在训练模式中还会在第一个全连接层和第二个全连接层后面添加 dropout 层。
dropout1, dropout2 0.2, 0.5class Net(nn.Module):def __init__(self, num_inputs, num_outputs, num_hiddens1, num_hiddens2,is_training True):super(Net, self).__init__()self.num_inputs num_inputsself.training is_trainingself.lin1 nn.Linear(num_inputs, num_hiddens1)self.lin2 nn.Linear(num_hiddens1, num_hiddens2)self.lin3 nn.Linear(num_hiddens2, num_outputs)self.relu nn.ReLU()def forward(self, X):H1 self.relu(self.lin1(X.reshape((-1, self.num_inputs))))# 只有在训练模型时才使用dropoutif self.training True:# 在第一个全连接层之后添加一个dropout层H1 dropout_layer(H1, dropout1)H2 self.relu(self.lin2(H1))if self.training True:# 在第二个全连接层之后添加一个dropout层H2 dropout_layer(H2, dropout2)out self.lin3(H2)return outnet Net(num_inputs, num_outputs, num_hiddens1, num_hiddens2)训练和测试
num_epochs, lr, batch_size 10, 0.5, 256
loss nn.CrossEntropyLoss(reductionnone)
train_iter, test_iter d2l.load_data_fashion_mnist(batch_size)
trainer torch.optim.SGD(net.parameters(), lrlr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)运行结果 暂退法的简洁实现
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);或者是
class Net(nn.Module):def __init__(self, input_size, hidden_size, output_size, dropout_prob):super(Net, self).__init__()self.fc1 nn.Linear(input_size, hidden_size)self.fc2 nn.Linear(hidden_size, hidden_size)self.fc3 nn.Linear(hidden_size, output_size)self.dropout nn.Dropout(pdropout_prob)def forward(self, x):x torch.relu(self.fc1(x))x self.dropout(x)x torch.relu(self.fc2(x))x self.dropout(x)x self.fc3(x)return x