对输入的标准化(浅层模型)
处理后的任意一个特征在数据集中所有样本上的均值为0、标准差为1。
标准化处理输入数据使各个特征的分布相近
批量归一化(深度模型)
利用小批量上的均值和标准差,不断调整神经网络中间输出,从而使整个神经网络在各层的中间输出的数值更稳定。
1.对全连接层做批量归一化
位置:全连接层中的仿射变换和激活函数之间。
全连接:
批量归一化:
这⾥ϵ > 0是个很小的常数,保证分母大于0
引入可学习参数:拉伸参数γ和偏移参数β。若和,批量归一化无效。
2.对卷积层做批量归⼀化
位置:卷积计算之后、应⽤激活函数之前。
如果卷积计算输出多个通道,我们需要对这些通道的输出分别做批量归一化,且每个通道都拥有独立的拉伸和偏移参数。 计算:对单通道,batchsize=m,卷积计算输出=pxq 对该通道中m×p×q个元素同时做批量归一化,使用相同的均值和方差。
3.预测时的批量归⼀化
训练:以batch为单位,对每个batch计算均值和方差。
预测:用移动平均估算整个训练数据集的样本均值和方差。
只考虑在函数为凸函数, 且最小值点上 时的收敛速度:
令 为第 次迭代后 的值, 表示 到最小值点 的距离,由 :
两边除以 , 有:
代入更新方程 , 得到:
当 时,有:
预处理 (Heissan阵辅助梯度下降)
梯度下降与线性搜索(共轭梯度法)
随机梯度下降
随机梯度下降参数更新
对于有 个样本对训练数据集,设 是第 个样本的损失函数, 则目标函数为:
其梯度为:
使用该梯度的一次更新的时间复杂度为
随机梯度下降更新公式 :
代码:
def train_ch7(optimizer_fn, states, hyperparams, features, labels,
batch_size=10, num_epochs=2):
# 初始化模型
net, loss = d2l.linreg, d2l.squared_loss
w = torch.nn.Parameter(torch.tensor(np.random.normal(0, 0.01, size=(features.shape[1], 1)), dtype=torch.float32),
requires_grad=True)
b = torch.nn.Parameter(torch.zeros(1, dtype=torch.float32), requires_grad=True)
def eval_loss():
return loss(net(features, w, b), labels).mean().item()
ls = [eval_loss()]
data_iter = torch.utils.data.DataLoader(
torch.utils.data.TensorDataset(features, labels), batch_size, shuffle=True)
for _ in range(num_epochs):
start = time.time()
for batch_i, (X, y) in enumerate(data_iter):
l = loss(net(X, w, b), y).mean() # 使用平均损失
# 梯度清零
if w.grad is not None:
w.grad.data.zero_()
b.grad.data.zero_()
l.backward()
optimizer_fn([w, b], states, hyperparams) # 迭代模型参数
if (batch_i + 1) * batch_size % 100 == 0:
ls.append(eval_loss()) # 每100个样本记录下当前训练误差
# 打印结果和作图
print('loss: %f, %f sec per epoch' % (ls[-1], time.time() - start))
d2l.set_figsize()
d2l.plt.plot(np.linspace(0, num_epochs, len(ls)), ls)
d2l.plt.xlabel('epoch')
d2l.plt.ylabel('loss')
class BiRNN(nn.Module):
def init(self, vocab, embed_size, num_hiddens, num_layers):
‘’’
@params:
vocab: 在数据集上创建的词典,用于获取词典大小
embed_size: 嵌入维度大小
num_hiddens: 隐藏状态维度大小
num_layers: 隐藏层个数
‘’’
super(BiRNN, self).init()
self.embedding = nn.Embedding(len(vocab), embed_size)
# encoder-decoder framework
# bidirectional设为True即得到双向循环神经网络
self.encoder = nn.LSTM(input_size=embed_size,
hidden_size=num_hiddens,
num_layers=num_layers,
bidirectional=True)
self.decoder = nn.Linear(4*num_hiddens, 2) # 初始时间步和最终时间步的隐藏状态作为全连接层输入
def forward(self, inputs):
'''
@params:
inputs: 词语下标序列,形状为 (batch_size, seq_len) 的整数张量
@return:
outs: 对文本情感的预测,形状为 (batch_size, 2) 的张量
'''
# 因为LSTM需要将序列长度(seq_len)作为第一维,所以需要将输入转置
embeddings = self.embedding(inputs.permute(1, 0)) # (seq_len, batch_size, d)
# rnn.LSTM 返回输出、隐藏状态和记忆单元,格式如 outputs, (h, c)
outputs, _ = self.encoder(embeddings) # (seq_len, batch_size, 2*h)
encoding = torch.cat((outputs[0], outputs[-1]), -1) # (batch_size, 4*h)
outs = self.decoder(encoding) # (batch_size, 2)
return outs
embed_size, num_hiddens, num_layers = 100, 100, 2
net = BiRNN(vocab, embed_size, num_hiddens, num_layers)
在训练集中,我们将每个锚框视为一个训练样本。为了训练目标检测模型,我们需要为每个锚框标注两类标签:一是锚框所含目标的类别,简称类别;二是真实边界框相对锚框的偏移量,简称偏移量(offset)。在目标检测时,我们首先生成多个锚框,然后为每个锚框预测类别以及偏移量,接着根据预测的偏移量调整锚框位置从而得到预测边界框,最后筛选需要输出的预测边界框。
我们知道,在目标检测的训练集中,每个图像已标注了真实边界框的位置以及所含目标的类别。在生成锚框之后,我们主要依据与锚框相似的真实边界框的位置和类别信息为锚框标注。那么,该如何为锚框分配与其相似的真实边界框呢?
假设图像中锚框分别为,真实边界框分别为,且。定义矩阵,其中第行第列的元素为锚框与真实边界框的交并比。 首先,我们找出矩阵中最大元素,并将该元素的行索引与列索引分别记为。我们为锚框分配真实边界框。显然,锚框和真实边界框在所有的“锚框—真实边界框”的配对中相似度最高。接下来,将矩阵中第行和第列上的所有元素丢弃。找出矩阵中剩余的最大元素,并将该元素的行索引与列索引分别记为。我们为锚框分配真实边界框,再将矩阵中第行和第列上的所有元素丢弃。此时矩阵中已有两行两列的元素被丢弃。 依此类推,直到矩阵中所有列元素全部被丢弃。这个时候,我们已为个锚框各分配了一个真实边界框。 接下来,我们只遍历剩余的个锚框:给定其中的锚框,根据矩阵的第行找到与交并比最大的真实边界框,且只有当该交并比大于预先设定的阈值时,才为锚框分配真实边界框。
如图9.3(左)所示,假设矩阵中最大值为,我们将为锚框分配真实边界框。然后,丢弃矩阵中第2行和第3列的所有元素,找出剩余阴影部分的最大元素,为锚框分配真实边界框。接着如图9.3(中)所示,丢弃矩阵中第7行和第1列的所有元素,找出剩余阴影部分的最大元素,为锚框分配真实边界框。最后如图9.3(右)所示,丢弃矩阵中第5行和第4列的所有元素,找出剩余阴影部分的最大元素,为锚框分配真实边界框。之后,我们只需遍历除去的剩余锚框,并根据阈值判断是否为剩余锚框分配真实边界框。
def MultiBoxPrior(feature_map, sizes=[0.75, 0.5, 0.25], ratios=[1, 2, 0.5]):
“”"
# 按照「9.4.1. 生成多个锚框」所讲的实现, anchor表示成(xmin, ymin, xmax, ymax).
https://zh.d2l.ai/chapter_computer-vision/anchor.html
Args:
feature_map: torch tensor, Shape: [N, C, H, W].
sizes: List of sizes (0~1) of generated MultiBoxPriores.
ratios: List of aspect ratios (non-negative) of generated MultiBoxPriores.
Returns:
anchors of shape (1, num_anchors, 4). 由于batch里每个都一样, 所以第一维为1
“”"
pairs = [] # pair of (size, sqrt(ration))
# 生成n + m -1个框
for r in ratios:
pairs.append([sizes[0], math.sqrt(r)])
for s in sizes[1:]:
pairs.append([s, math.sqrt(ratios[0])])
pairs = np.array(pairs)
# 生成相对于坐标中心点的框(x,y,x,y)
ss1 = pairs[:, 0] * pairs[:, 1] # size * sqrt(ration)
ss2 = pairs[:, 0] / pairs[:, 1] # size / sqrt(ration)
base_anchors = np.stack([-ss1, -ss2, ss1, ss2], axis=1) / 2
#将坐标点和anchor组合起来生成hw(n+m-1)个框输出
h, w = feature_map.shape[-2:]
shifts_x = np.arange(0, w) / w
shifts_y = np.arange(0, h) / h
shift_x, shift_y = np.meshgrid(shifts_x, shifts_y)
shift_x = shift_x.reshape(-1)
shift_y = shift_y.reshape(-1)
shifts = np.stack((shift_x, shift_y, shift_x, shift_y), axis=1)
anchors = shifts.reshape((-1, 1, 4)) + base_anchors.reshape((1, -1, 4))
return torch.tensor(anchors, dtype=torch.float32).view(1, -1, 4)