循环神经网络原理及实现(二):循环神经网络复现

news2024/12/24 0:23:47

专栏:神经网络复现目录

循环神经网络

循环神经网络(Recurrent Neural Network,RNN)是一种神经网络结构,其主要特点是网络中存在循环连接,使得网络具有记忆功能,可以处理序列数据。在传统神经网络中,每一层之间的连接是单向的,每一层的输入仅仅依赖于前一层的输出。而在循环神经网络中,除了输入层和输出层之外,每一层之间都存在循环连接,使得网络具有一定的记忆功能,可以处理序列数据。在循环神经网络中,每个时间步的输入数据不仅取决于当前时刻的输入数据,还取决于上一个时间步的网络状态。
在这里插入图片描述


文章目录

  • 循环神经网络
  • 困惑度
  • 循环神经网络的从零实现
    • 数据集和导包
    • 独热编码
    • 初始化模型参数
    • 循环神经网络模型
    • 预测
    • 梯度裁剪
    • 训练
  • 循环神经网络的简易实现
    • 数据集和导包
    • 定义模型
    • 训练和预测
  • 通过时间反向传播


困惑度

我们讨论如何度量语言模型的质量, 这将在后续部分中用于评估基于循环神经网络的模型。 一个好的语言模型能够用高度准确的词元来预测我们接下来会看到什么。 考虑一下由不同的语言模型给出的对“It is raining …”(“…下雨了”)的续写:

  1. “It is raining outside”(外面下雨了);

  2. “It is raining banana tree”(香蕉树下雨了);

  3. “It is raining piouw;kcj pwepoiut”(piouw;kcj pwepoiut下雨了)。

就质量而言,例1显然是最合乎情理、在逻辑上最连贯的。 虽然这个模型可能没有很准确地反映出后续词的语义, 比如,“It is raining in San Francisco”(旧金山下雨了) 和“It is raining in winter”(冬天下雨了) 可能才是更完美的合理扩展, 但该模型已经能够捕捉到跟在后面的是哪类单词。 例2则要糟糕得多,因为其产生了一个无意义的续写。 尽管如此,至少该模型已经学会了如何拼写单词, 以及单词之间的某种程度的相关性。 最后,例3表明了训练不足的模型是无法正确地拟合数据的。

我们可以通过计算序列的似然概率来度量模型的质量。 然而这是一个难以理解、难以比较的数字。 毕竟,较短的序列比较长的序列更有可能出现, 因此评估模型产生托尔斯泰的巨著《战争与和平》的可能性 不可避免地会比产生圣埃克苏佩里的中篇小说《小王子》可能性要小得多。 而缺少的可能性值相当于平均数。

在这里,信息论可以派上用场了。 我们在引入softmax回归时定义了熵、惊异和交叉熵。 如果想要压缩文本,我们可以根据当前词元集预测的下一个词元。 一个更好的语言模型应该能让我们更准确地预测下一个词元。 因此,它应该允许我们在压缩序列时花费更少的比特。 所以我们可以通过一个序列中所有的n个词元的交叉熵损失的平均值来衡量:
在这里插入图片描述
其中P由语言模型给出, 是在时间步t从该序列中观察到的实际词元。 这使得不同长度的文档的性能具有了可比性。 由于历史原因,自然语言处理的科学家更喜欢使用一个叫做困惑度(perplexity)的量。
在这里插入图片描述
困惑度的最好的理解是“下一个词元的实际选择数的调和平均数”。

在最好的情况下,模型总是完美地估计标签词元的概率为1。 在这种情况下,模型的困惑度为1。

在最坏的情况下,模型总是预测标签词元的概率为0。 在这种情况下,困惑度是正无穷大。

在基线上,该模型的预测是词表的所有可用词元上的均匀分布。 在这种情况下,困惑度等于词表中唯一词元的数量。 事实上,如果我们在没有任何压缩的情况下存储序列, 这将是我们能做的最好的编码方式。 因此,这种方式提供了一个重要的上限, 而任何实际模型都必须超越这个上限。

循环神经网络的从零实现

数据集和导包

%matplotlib inline
import math
import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l

batch_size, num_steps = 32, 35
train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)

独热编码

独热编码(One-Hot Encoding)是一种将分类数据转换为数字数据的技术。在机器学习和深度学习领域中,我们经常需要将非数字型的数据转化为数字型,以便计算机可以处理这些数据。而独热编码就是一种将分类数据转换为数字数据的方式之一。

独热编码的基本思想是将每个不同的分类值都转换成一个新的特征,这个特征的取值只能是0或1,其中1表示该样本属于这个分类,0则表示不属于。具体实现的过程中,首先需要确定所有的分类,然后为每个分类赋一个唯一的整数编码,接下来将每个整数编码表示成一个二进制数,最后将每个二进制数作为一个新的特征。

例如,假设有一个分类特征“颜色”,它有三个不同的值:“红色”、“绿色”和“蓝色”。首先,我们要为每个颜色分配一个唯一的整数编码:红色为1,绿色为2,蓝色为3。然后,我们将每个整数编码转换为二进制数:红色的编码为001,绿色的编码为010,蓝色的编码为100。最后,我们将这些二进制数作为新的特征,即“颜色_001”、“颜色_010”和“颜色_100”,并将它们添加到数据集中。

使用独热编码可以有效地解决分类特征不能直接用于计算的问题,同时也避免了将整数编码解释为有序特征的问题。

索引为0和2的独热向量如下所示:

F.one_hot(torch.tensor([0, 2]), len(vocab))

在这里插入图片描述
我们每次采样的小批量数据形状是二维张量: (批量大小,时间步数)。 one_hot函数将这样一个小批量数据转换成三维张量, 张量的最后一个维度等于词表大小(len(vocab))。 我们经常转换输入的维度,以便获得形状为 (时间步数,批量大小,词表大小)的输出。 这将使我们能够更方便地通过最外层的维度, 一步一步地更新小批量数据的隐状态。

初始化模型参数

接下来,我们初始化循环神经网络模型的模型参数。 隐藏单元数num_hiddens是一个可调的超参数。 当训练语言模型时,输入和输出来自相同的词表。 因此,它们具有相同的维度,即词表的大小。

def get_params(vocab_size, num_hiddens, device):
    num_inputs = num_outputs = vocab_size

    def normal(shape):
        return torch.randn(size=shape, device=device) * 0.01

    # 隐藏层参数
    W_xh = normal((num_inputs, num_hiddens))
    W_hh = normal((num_hiddens, num_hiddens))
    b_h = torch.zeros(num_hiddens, device=device)
    # 输出层参数
    W_hq = normal((num_hiddens, num_outputs))
    b_q = torch.zeros(num_outputs, device=device)
    # 附加梯度
    params = [W_xh, W_hh, b_h, W_hq, b_q]
    for param in params:
        param.requires_grad_(True)
    return params

这个函数是为了创建一个循环神经网络(RNN)模型,其参数包括词汇表大小、隐藏层的大小以及设备类型,返回一个包含五个张量的列表,其中包括输入到隐藏层的权重、隐藏层之间的权重、隐藏层的偏置、隐藏层到输出的权重以及输出层的偏置。

具体来说,这个函数通过调用normal函数来初始化网络中的权重和偏置,normal函数生成一个形状为shape的张量,每个元素都是从均值为0、标准差为0.01的正态分布中随机采样的。接下来,我们使用torch.zeros函数初始化了偏置项。其中,b_h是隐藏层的偏置,b_q是输出层的偏置。

在初始化完所有的参数后,我们将所有的参数设置为需要求梯度,以便在训练过程中更新这些参数。最后,我们将这些参数存储在一个列表中并返回。

RNN模型可以用于处理序列数据,因为它可以记忆之前的状态,根据之前的状态来计算当前的输出。这个函数返回的参数可以用于创建一个简单的RNN模型,该模型可以接受一个序列作为输入,并预测序列的下一个值。

循环神经网络模型

def init_rnn_state(batch_size, num_hiddens, device):
    return (torch.zeros((batch_size, num_hiddens), device=device), )
def rnn(inputs, state, params):
    # inputs的形状:(时间步数量,批量大小,词表大小)
    W_xh, W_hh, b_h, W_hq, b_q = params
    H, = state
    outputs = []
    # X的形状:(批量大小,词表大小)
    for X in inputs:
        H = torch.tanh(torch.mm(X, W_xh) + torch.mm(H, W_hh) + b_h)
        Y = torch.mm(H, W_hq) + b_q
        outputs.append(Y)
    return torch.cat(outputs, dim=0), (H,)
class RNNModelScratch: #@save
    """从零开始实现的循环神经网络模型"""
    def __init__(self, vocab_size, num_hiddens, device,
                 get_params, init_state, forward_fn):
        self.vocab_size, self.num_hiddens = vocab_size, num_hiddens
        self.params = get_params(vocab_size, num_hiddens, device)
        self.init_state, self.forward_fn = init_state, forward_fn

    def __call__(self, X, state):
        X = F.one_hot(X.T, self.vocab_size).type(torch.float32)
        return self.forward_fn(X, state, self.params)

    def begin_state(self, batch_size, device):
        return self.init_state(batch_size, self.num_hiddens, device)

让我们检查输出是否具有正确的形状。 例如,隐状态的维数是否保持不变。

num_hiddens = 512
net = RNNModelScratch(len(vocab), num_hiddens, d2l.try_gpu(), get_params,
                      init_rnn_state, rnn)
state = net.begin_state(X.shape[0], d2l.try_gpu())
Y, new_state = net(X.to(d2l.try_gpu()), state)
Y.shape, len(new_state), new_state[0].shape

在这里插入图片描述

预测

def predict(prefix, num_preds, net, vocab, device):  #@save
    """在prefix后面生成新字符"""
    state = net.begin_state(batch_size=1, device=device)
    outputs = [vocab[prefix[0]]]
    get_input = lambda: torch.tensor([outputs[-1]], device=device).reshape((1, 1))
    for y in prefix[1:]:  # 预热期
        _, state = net(get_input(), state)
        outputs.append(vocab[y])
    for _ in range(num_preds):  # 预测num_preds步
        y, state = net(get_input(), state)
        outputs.append(int(y.argmax(dim=1).reshape(1)))
    return ''.join([vocab.idx_to_token[i] for i in outputs])
predict('time traveller ', 10, net, vocab, d2l.try_gpu())

梯度裁剪

梯度裁剪是一种用于缓解梯度爆炸或梯度消失问题的技术,通常应用于深度学习中的循环神经网络(RNN)或长短期记忆(LSTM)等模型中。

在训练神经网络时,我们使用反向传播算法计算每个权重参数的梯度,并使用这些梯度来更新权重。但是,当网络非常深或训练数据非常复杂时,计算出的梯度可能会变得非常大或非常小,这可能导致梯度爆炸或梯度消失问题。梯度爆炸指的是梯度值过大,而梯度消失则指的是梯度值过小,因此,它们都可能影响模型的训练效果。

为了解决这些问题,我们可以使用梯度裁剪技术来限制梯度的范围。具体来说,梯度裁剪将所有梯度的范数限制在一个预先定义的阈值之内,如果梯度的范数超过了这个阈值,那么就将其裁剪到这个阈值。这样可以避免梯度爆炸问题,同时也能够保留足够的梯度信息,避免梯度消失问题。

梯度裁剪通常在模型的训练过程中应用,它可以使用不同的方式实现,如在反向传播过程中直接裁剪梯度值,或者在计算梯度之后再将其裁剪。裁剪阈值可以根据经验或交叉验证等技术进行调整,以找到最佳的值。

梯度裁剪的具体实现方式通常有两种:

  1. L2范数裁剪:将所有参数的梯度按照其L2范数进行缩放。具体来说,对于一个参数 w w w,其梯度 g w g_w gw的L2范数为 ∣ g w ∣ 2 |g_w|_2 gw2,如果它超过了一个预先设定的阈值 c c c,那么就将 g w g_w gw乘以 c / ∣ g w ∣ 2 c/|g_w|_2 c/∣gw2,以将其缩放到一个范围内。

  2. 全局范数裁剪:将所有参数的梯度的总体L2范数进行缩放。具体来说,对于所有参数的梯度向量 g g g,其总体L2范数为 ∣ g ∣ 2 |g|_2 g2,如果它超过了一个预先设定的阈值 c c c,那么就将 g g g乘以 c / ∣ g ∣ 2 c/|g|_2 c/∣g2,以将其缩放到一个范围内。

值得注意的是,梯度裁剪的阈值 c c c通常是一个超参数,需要通过实验来进行调整。一般来说,阈值的取值应该大于梯度范数的平均值,同时要小于梯度范数的最大值,以保证既能缓解梯度爆炸问题,又不会对梯度信息造成太大的损失。

def grad_clipping(net, theta):  #@save
    """裁剪梯度"""
    if isinstance(net, nn.Module):
        params = [p for p in net.parameters() if p.requires_grad]
    else:
        params = net.params
    norm = torch.sqrt(sum(torch.sum((p.grad ** 2)) for p in params))
    if norm > theta:
        for param in params:
            param.grad[:] *= theta / norm

训练

#@save
def train_epoch(net, train_iter, loss, updater, device, use_random_iter):
    state, timer = None, d2l.Timer()
    metric = d2l.Accumulator(2)  # 训练损失之和,词元数量
    for X, Y in train_iter:
        if state is None or use_random_iter:
            # 在第一次迭代或使用随机抽样时初始化state
            state = net.begin_state(batch_size=X.shape[0], device=device)
        else:
            if isinstance(net, nn.Module) and not isinstance(state, tuple):
                # state对于nn.GRU是个张量
                state.detach_()
            else:
                # state对于nn.LSTM或对于我们从零开始实现的模型是个张量
                for s in state:
                    s.detach_()
        y = Y.T.reshape(-1)
        X, y = X.to(device), y.to(device)
        y_hat, state = net(X, state)
        l = loss(y_hat, y.long()).mean()
        if isinstance(updater, torch.optim.Optimizer):
            updater.zero_grad()
            l.backward()
            grad_clipping(net, 1)
            updater.step()
        else:
            l.backward()
            grad_clipping(net, 1)
            # 因为已经调用了mean函数
            updater(batch_size=1)
        metric.add(l * y.numel(), y.numel())
    return math.exp(metric[0] / metric[1]), metric[1] / timer.stop()

这段代码主要实现了训练神经网络模型的迭代过程,其中涉及到梯度裁剪的操作。具体来说,代码中通过定义了一个名为 train_epoch_ch8 的函数来实现,该函数接受以下参数:

net:要训练的神经网络模型;
train_iter:训练数据集的迭代器;
loss:损失函数;
updater:更新函数,用于更新网络参数;
device:使用的设备;
use_random_iter:是否使用随机迭代器。
在训练过程中,代码使用一个名为 state 的变量来保存网络的状态,以便在下一次迭代中使用。当第一次迭代或使用随机抽样时,会初始化这个状态;在其他情况下,会将其从计算图中分离出来。

接下来,代码将当前的输入数据 X 和标签 Y 转移到设备上,然后将标签转换为一维向量 y。接着,代码通过调用神经网络模型 net 来得到网络的输出 y_hat 和状态 state,并使用损失函数 loss 计算出网络输出与真实标签之间的损失值 l。

在得到损失值后,代码使用 grad_clipping 函数对模型的所有参数的梯度进行裁剪,以防止梯度爆炸问题的出现。具体来说,该函数会将参数的梯度限制在一定范围内,以确保梯度的范数不会太大。在这里,给定的参数为 1,表示梯度的范数最大值为 1。

最后,代码根据更新函数的类型分别进行更新操作,并将当前的损失值和训练数据集中所有数据的词元数量累加到度量器 metric 中。最终,函数返回的指数形式的损失值和平均时间,用于衡量模型在训练集上的性能。

#@save
def train(net, train_iter, vocab, lr, num_epochs, device,
              use_random_iter=False):
    loss = nn.CrossEntropyLoss()
    animator = d2l.Animator(xlabel='epoch', ylabel='perplexity',
                            legend=['train'], xlim=[10, num_epochs])
    # 初始化
    if isinstance(net, nn.Module):
        updater = torch.optim.SGD(net.parameters(), lr)
    else:
        updater = lambda batch_size: d2l.sgd(net.params, lr, batch_size)
    predict = lambda prefix: predict_ch8(prefix, 50, net, vocab, device)
    # 训练和预测
    for epoch in range(num_epochs):
        ppl, speed = train_epoch_ch8(
            net, train_iter, loss, updater, device, use_random_iter)
        if (epoch + 1) % 10 == 0:
            print(predict('time traveller'))
            animator.add(epoch + 1, [ppl])
    print(f'困惑度 {ppl:.1f}, {speed:.1f} 词元/秒 {str(device)}')
    print(predict('time traveller'))
    print(predict('traveller'))
num_epochs, lr = 500, 1
train_ch8(net, train_iter, vocab, lr, num_epochs, d2l.try_gpu())

在这里插入图片描述
在这里插入图片描述

最后,让我们检查一下使用随机抽样方法的结果。

net = RNNModelScratch(len(vocab), num_hiddens, d2l.try_gpu(), get_params,
                      init_rnn_state, rnn)
train_ch8(net, train_iter, vocab, lr, num_epochs, d2l.try_gpu(),
          use_random_iter=True)

在这里插入图片描述

循环神经网络的简易实现

数据集和导包

import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l

batch_size, num_steps = 32, 35
train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)

定义模型

num_hiddens = 256
rnn_layer = nn.RNN(len(vocab), num_hiddens)

我们使用张量来初始化隐状态,它的形状是(隐藏层数,批量大小,隐藏单元数)。

state = torch.zeros((1, batch_size, num_hiddens))
state.shape

通过一个隐状态和一个输入,我们就可以用更新后的隐状态计算输出。 需要强调的是,rnn_layer的“输出”(Y)不涉及输出层的计算: 它是指每个时间步的隐状态,这些隐状态可以用作后续输出层的输入。

X = torch.rand(size=(num_steps, batch_size, len(vocab)))
Y, state_new = rnn_layer(X, state)
Y.shape, state_new.shape
#@save
class RNNModel(nn.Module):
    """循环神经网络模型"""
    def __init__(self, rnn_layer, vocab_size, **kwargs):
        super(RNNModel, self).__init__(**kwargs)
        self.rnn = rnn_layer
        self.vocab_size = vocab_size
        self.num_hiddens = self.rnn.hidden_size
        # 如果RNN是双向的(之后将介绍),num_directions应该是2,否则应该是1
        if not self.rnn.bidirectional:
            self.num_directions = 1
            self.linear = nn.Linear(self.num_hiddens, self.vocab_size)
        else:
            self.num_directions = 2
            self.linear = nn.Linear(self.num_hiddens * 2, self.vocab_size)

    def forward(self, inputs, state):
        X = F.one_hot(inputs.T.long(), self.vocab_size)
        X = X.to(torch.float32)
        Y, state = self.rnn(X, state)
        # 全连接层首先将Y的形状改为(时间步数*批量大小,隐藏单元数)
        # 它的输出形状是(时间步数*批量大小,词表大小)。
        output = self.linear(Y.reshape((-1, Y.shape[-1])))
        return output, state

    def begin_state(self, device, batch_size=1):
        if not isinstance(self.rnn, nn.LSTM):
            # nn.GRU以张量作为隐状态
            return  torch.zeros((self.num_directions * self.rnn.num_layers,
                                 batch_size, self.num_hiddens),
                                device=device)
        else:
            # nn.LSTM以元组作为隐状态
            return (torch.zeros((
                self.num_directions * self.rnn.num_layers,
                batch_size, self.num_hiddens), device=device),
                    torch.zeros((
                        self.num_directions * self.rnn.num_layers,
                        batch_size, self.num_hiddens), device=device))

这段代码定义了一个循环神经网络模型 RNNModel,包含以下方法:

init(self, rnn_layer, vocab_size, **kwargs): 初始化方法,接受三个参数,分别为:rnn_layer表示RNN的类型(可以是LSTM、GRU、RNN等),vocab_size表示词表的大小,**kwargs表示其他的一些可选参数。在初始化过程中,根据rnn_layer的类型,选择是否使用双向的RNN,并初始化一个全连接层linear。

forward(self, inputs, state): 前向传播方法,接受两个参数,分别为:inputs表示输入的数据(一个序列),state表示RNN的隐状态。在前向传播中,首先将inputs转化为one-hot向量表示,然后通过RNN模型得到输出Y和更新后的隐状态state。最后将Y通过全连接层linear得到最终的输出output。

begin_state(self, device, batch_size=1): 获取RNN的初始隐状态方法,接受两个参数,分别为:device表示使用的设备,batch_size表示批次大小。根据RNN的类型,返回相应形式的初始隐状态。

总体来说,这段代码定义了一个通用的循环神经网络模型,可以用于各种自然语言处理任务(如语言建模、文本分类等)。其中,需要根据具体的任务选择合适的RNN类型,并根据词表大小和隐状态大小设置相应的超参数。

训练和预测

device = d2l.try_gpu()
net = RNNModel(rnn_layer, vocab_size=len(vocab))
net = net.to(device)
d2l.predict_ch8('time traveller', 10, net, vocab, device)
num_epochs, lr = 500, 1
d2l.train_ch8(net, train_iter, vocab, lr, num_epochs, device)

在这里插入图片描述

通过时间反向传播

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/404029.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

autoxjs

文章目录autojs一、工具二、使用步骤1.手机设置开发模式并打开usb调试2.安装scrcpy3. 安装autoxjs4. vscode插件使用auto 入门语法总结autojs autojs 目前作者已经跑路了,转为用社区的autoxjs,官网地址:http://doc.autoxjs.com/#/ 一、工具 …

Echart的使用初体验,Echarts的基本使用及语法格式,简单图表绘制和使用及图例添加【学习笔记】

Echart? ECharts 是一个使用 JavaScript 实现的开源可视化库,涵盖各行业图表,满足各种需求。 ECharts 遵循 Apache-2.0 开源协议,免费商用。 ECharts 兼容当前绝大部分浏览器(IE8/9/10/11,Chrome&#xf…

【打造家庭服务器系列01】无桌面版Ubuntu 22.04 连接wifi

一、背景 最近有一台笔记本一直放在哪没用了,就想着拿来做个服务器用吧。 如何安装Ubuntu系统,大家可以百度搜索一下很多。 主要分三步: 制作U盘启动盘(推荐使用rufus工具,轻量方便)设置BIOS引导 &#x…

java——代理

什么是代理: 给目标对象一个代理对象,由代理对象控制着对目标对象的引用 为什么使用代理: ①:功能增强:通过代理业务对原有业务进行增强 ②:用户只能同行过代理对象间接访问目标对象,防止用…

About What Is a DBA?

1.Evaluating a DBA Job Offer Here are some useful questions to ask: • Does the company offer regular training for its DBAs to learn new DBMS features and functionality? What about training for related technologies such as programming, networking, e-bus…

[NIPS 2017] Improved Training of Wasserstein GANs (WGAN-GP)

Contents IntroductionDifficulties with weight constraintsCapacity underuseExploding and vanishing gradientsGradient penaltyReferencesIntroduction WGAN 增加了 GAN 模型训练的稳定性,但有时仍然会有生成质量不高或难以收敛的问题。作者发现上述问题经常是由 WGAN 中…

保障信息安全:使用PyZbar库识别二维码图片可以快速获取二维码中的信息,保障信息安全。

目录 简介: 源代码: 源代码说明: 效果如下所示: 简介: 不用摄像头识别二维码可以应用在以下场景: 批量处理二维码图片:可以在服务器上使用PyZbar等库来批量处理二维码图片,例如读…

Nginx 配置实例-负载均衡

一、实现效果 浏览器地址栏输入地址 http://192.168.137.129/edu/a.html,负载均衡效果,将请求平均分配到8080和8081两台服务器上。 二、准备工作 1. 准备两台tomcat服务器,一台8080,一台8081 (具体操作如下两个链接) Nginx配置实…

亚信科技新“三驾马车”再创佳绩,与数字经济同频共振

‍数据智能产业创新服务媒体——聚焦数智 改变商业近日,亚信科技公布了2022年财报。财报显示,2022年,亚信科技实现营收77.38亿元,同比上升12.2%;毛利润29.39亿元,同比上升11.1%,毛利率达38.0%&…

分布式链路追踪组件skywalking介绍

SkyWalking组件概念 一个开源的可观测平台, 用于从服务和云原生基础设施收集, 分析, 聚合及可视化数据。SkyWalking 提供了一种简便的方式来清晰地观测分布式系统, 甚至横跨多个云平台。SkyWalking 更是一个现代化的应用程序性能监控(Application Performance Monitoring)系统…

通过Session共享数据验证码进行用户登录

通过Session共享数据验证码进行用户登录 需求: 访问带有验证码的登录页面login.jsp。用户输入用户名,密码以及验证码。 ①。如果用户名和密码输入有误,跳转登陆页面,提示:用户名或密码错误。 ②。如果验证码输入有误…

核方法(kernel Method)

核方法 核方法定义 一种能够将在原始数据空间中的非线性数据映射到高维线性可分的方法。 核方法的用处 1、低维数据非线性,当其映射到高维空间(feature space)时,可以用线性方法对数据进行处理。 2、线性学习器相对于非线性学…

从MySQL innoDB的特性Doublewrite buffer谈起

文章目录前言什么是Doublewrite buffer为什么要叫它Doublewrite呢,双写分别是哪两次写,体现在了什么地方呢为什么需要Doublewrite bufferDoublewrite buffer的具体使用1.假如还没有进行第一次写的时候crash了,也就是Doublewrite buffer和磁盘…

最大值池化与均值池化比较分析

1 问题在深度学习的卷积网络过程中,神经网络有卷积层,池化层,全连接层。而池化层有最大值池化和均值池化两种情况,而我们组就在思考,最大值池化和均值池化有什么区别呢?两者的模型准确率是否有所不同&#…

RTP载荷H264(实战细节)

RTP包由两部分组成,RTP头和RTP载荷: RTP头 RTP头的 结构如下: 代码结构: typedef struct RtpHdr {uint8_t cc : 4, // CSRC countx : 1, // header extendp : 1, // padding flagversion : 2; // versionuint8_t …

【Oracle 19c】解决 Oracle EM(Enterprise Manager) Express 切换回旧版后无法访问的问题

文章目录问题描述解决方案解决过程1、按 Oracle EM Express 提示下载 Adobe Flash Player PPAPI 版1、按 F12 查看 HTTP 请求头2、找到问题后使用其他浏览器尝试问题描述 由于从 Oracle Database 19c 开始,Oracle EM(Enterprise Manager) Express(Oracl…

【论文阅读】注意力机制与二维 TSP 问题

前置知识 注意力机制 见 这篇 二维 TSP 问题 给定二维平面上 nnn 个点的坐标 S{xi}i1nS\{x_i\}_{i1}^nS{xi​}i1n​,其中 xi∈[0,1]2x_i\in [0,1]^2xi​∈[0,1]2,要找到一个 1∼n1\sim n1∼n 的排列 π\piπ ,使得目标函数 L(π∣s)∥xπ…

2023年中国高校计算机大赛-团队程序设计天梯赛(GPLT)上海理工大学校内选拔赛(同步赛) A — E

2023年中国高校计算机大赛-团队程序设计天梯赛(GPLT)上海理工大学校内选拔赛(同步赛) 文章目录A -- A Xor B Problem题目分析codeB -- 吃苹果题目分析codeC -- n皇后问题题目分析codeD -- 分苹果题目分析codeE -- 完型填空题目分析codeA – A…

单片机——矩阵按键模块

主要目的 学会按键扫描 1.延时函数 延时函数部分详见链接: 单片机控制一盏灯的亮与灭程序解释 void delay (uint k) //定义延时函数{uint i,j;for(i<0;i<k;i){for(j0;j<113;j){;}}}这个程序里面的延时函数的目的是按键消抖。 2.按键扫描模块 这是本次实验的重点&a…

JS的执行机制

javaScript的执行机制 JS是单线程 单线程&#xff1a;所有的任务执行时需要排队&#xff0c;前一个任务结束&#xff0c;才会执行后一个任务。缺点&#xff1a;如果前一个任务耗时很长&#xff0c;后一个任务就会一直等待执行。会导致JS 执行的时间过长&#xff0c;造成页面的…