循环神经网络(RNN)入门指南:从原理到实践

目录

1. 循环神经网络的基本概念

2. 简单循环网络及其应用

3. 参数学习与优化

4. 基于门控的循环神经网络

4.1 长短期记忆网络(LSTM)

4.1.1 LSTM的核心组件:

4.2 门控循环单元(GRU)

5 实际应用中的优化技巧

5.1 变体和改进

5.2 注意力机制的结合

6 实现细节和最佳实践

6.1 初始化策略

6.1.1 梯度处理


1. 循环神经网络的基本概念

循环神经网络(Recurrent Neural Network,RNN)是一类具有短期记忆能 力的神经网络。在循环神经网络中,神经元不但可以接受其他神经元的信息,也 可以接受自身的信息,形成具有环路的网络结构。

循环神经网络是一类专门用于处理序列数据的神经网络。与传统的前馈神经网络不同,RNN引入了循环连接,使网络具备了处理时序信息的能力。在处理每个时间步的输入时,网络不仅考虑当前输入,还会利用之前的历史信息。

循环神经网络

从结构上看,RNN的核心是一个循环单元,它在每个时间步接收两个输入:当前时刻的输入数据和前一时刻的隐藏状态。这两个输入经过加权组合和非线性变换,生成当前时刻的新隐藏状态。具体来说,在每个时间步t,网络会执行以下计算:h_t = tanh(W_xh * x_t + W_hh * h_{t-1} + b_h),其中激活函数通常选择tanh或ReLU。

我们通过一个完整的Python实现来深入理解简单循环网络的工作机制:

import numpy as np

class SimpleRNN:
    def __init__(self, input_size, hidden_size, output_size):
        # 初始化网络参数
        self.hidden_size = hidden_size
        self.W_xh = np.random.randn(input_size, hidden_size) * 0.01
        self.W_hh = np.random.randn(hidden_size, hidden_size) * 0.01
        self.W_hy = np.random.randn(hidden_size, output_size) * 0.01
        self.b_h = np.zeros((1, hidden_size))
        self.b_y = np.zeros((1, output_size))
        
        # 用于存储反向传播所需的中间值
        self.hidden_states = []
        self.inputs = []
        
    def forward(self, input_sequence):
        # 初始化隐藏状态
        h = np.zeros((1, self.hidden_size))
        self.hidden_states = [h]
        self.inputs = input_sequence
        outputs = []
        
        # 前向传播
        for x in input_sequence:
            h = np.tanh(np.dot(x, self.W_xh) + 
                       np.dot(h, self.W_hh) + 
                       self.b_h)
            y = np.dot(h, self.W_hy) + self.b_y
            self.hidden_states.append(h)
            outputs.append(y)
            
        return outputs
    
    def backward(self, d_outputs, learning_rate=0.01):
        # 初始化梯度
        dW_xh = np.zeros_like(self.W_xh)
        dW_hh = np.zeros_like(self.W_hh)
        dW_hy = np.zeros_like(self.W_hy)
        db_h = np.zeros_like(self.b_h)
        db_y = np.zeros_like(self.b_y)
        
        # 反向传播
        dh_next = np.zeros((1, self.hidden_size))
        
        for t in reversed(range(len(self.inputs))):
            # 输出层的梯度
            dy = d_outputs[t]
            dW_hy += np.dot(self.hidden_states[t+1].T, dy)
            db_y += dy
            
            # 隐藏层的梯度
            dh = np.dot(dy, self.W_hy.T) + dh_next
            dh_raw = (1 - self.hidden_states[t+1] ** 2) * dh
            
            dW_xh += np.dot(self.inputs[t].T, dh_raw)
            dW_hh += np.dot(self.hidden_states[t].T, dh_raw)
            db_h += dh_raw
            dh_next = np.dot(dh_raw, self.W_hh.T)
            
        # 更新参数
        self.W_xh -= learning_rate * dW_xh
        self.W_hh -= learning_rate * dW_hh
        self.W_hy -= learning_rate * dW_hy
        self.b_h -= learning_rate * db_h
        self.b_y -= learning_rate * db_y

在自然语言处理中,它可以用于实现基础的语言模型我们可以训练网络预测句子中的下一个词:

def create_language_model():
    vocab_size = 5000  # 词汇表大小
    embedding_size = 128
    hidden_size = 256
    
    model = SimpleRNN(embedding_size, hidden_size, vocab_size)
    return model

def train_language_model(model, sentences, word_to_idx):
    for sentence in sentences:
        # 将句子转换为词嵌入序列
        input_sequence = [word_to_embedding[word_to_idx[word]] 
                         for word in sentence[:-1]]
        target_sequence = [word_to_idx[word] for word in sentence[1:]]
        
        # 前向传播
        outputs = model.forward(input_sequence)
        
        # 计算损失和梯度
        d_outputs = []
        for t, output in enumerate(outputs):
            target = np.zeros((1, vocab_size))
            target[0, target_sequence[t]] = 1
            d_outputs.append(output - target)
        
        # 反向传播
        model.backward(d_outputs)

在时间序列预测领域,简单循环网络可以用于预测股票价格、天气等连续值:

def time_series_prediction(data, sequence_length):
    model = SimpleRNN(input_size=1, hidden_size=32, output_size=1)
    
    # 准备训练数据
    sequences = []
    targets = []
    for i in range(len(data) - sequence_length):
        sequences.append(data[i:i+sequence_length])
        targets.append(data[i+sequence_length])
    
    # 训练模型
    for epoch in range(num_epochs):
        for seq, target in zip(sequences, targets):
            outputs = model.forward(seq)
            d_outputs = [output - target for output in outputs]
            model.backward(d_outputs)

虽然简单循环网络在这些应用中表现出了一定的能力,但它也存在明显的局限性。主要问题包括:

  1. 梯度消失和爆炸:在反向传播过程中,梯度会随着时间步的增加而衰减或爆炸。
  2. 长程依赖问题:网络难以捕捉距离较远的依赖关系。
  3. 信息瓶颈:所有历史信息都需要压缩在固定大小的隐藏状态中。

为了克服这些限制,后来发展出了LSTM和GRU等更复杂的RNN变体。但是,理解简单循环网络的原理和实现对于掌握这些高级模型仍然是必要的。

2. 简单循环网络及其应用

简单循环神经网络(Simple RNN)是循环神经网络家族中最基础的架构。它通过在传统神经网络的基础上引入循环连接,使网络具备了处理序列数据的能力。这种设计理念源于对人类认知过程的模拟:当我们阅读文本或听音乐时,总是会结合之前的内容来理解当前信息。简单循环网络正是通过这种方式,在处理序列数据的每个时间步都保持并更新一个内部状态,从而捕捉序列中的时序依赖关系。

从结构上看,简单循环网络的核心是循环层,它在每个时间步都执行相同的运算。具体来说,网络在处理当前输入时,会同时考虑两个因素:当前时间步的输入数据和上一时间步的隐藏状态。这两部分信息通过权重矩阵进行加权组合,然后经过非线性激活函数(通常是tanh或ReLU)得到当前时间步的新隐藏状态。

这个过程可以用数学表达式表示为:h_t = tanh(W_xh * x_t + W_hh * h_{t-1} + b_h),其中W_xh是输入到隐藏层的权重矩阵,W_hh是隐藏层到隐藏层的权重矩阵,b_h是偏置项。

在训练过程中,简单循环网络采用随时间反向传播(BPTT)算法。这种算法将网络在时间维度上展开,转化为一个深度前馈网络,然后应用标准的反向传播算法进行训练。值得注意的是,由于所有时间步共享相同的权重,网络的参数更新需要累积所有时间步的梯度。这种训练方式虽然直观,但在处理长序列时容易出现梯度消失或梯度爆炸的问题。

然而,简单循环网络也存在一些固有的局限性。最显著的问题是长程依赖问题,即网络难以捕捉序列中相距较远的元素之间的关系。这个问题的根源在于,随着序列长度的增加,早期的信息会在多次非线性变换中逐渐减弱,最终可能完全丧失。此外,简单循环网络还面临着训练不稳定的问题,特别是在处理长序列时,梯度的传播容易出现消失或爆炸。

为了提升模型性能,我们可以采取一些实用的策略。合适的权重初始化,可以使用正交初始化或者Xavier/He初始化方法来减缓梯度问题。使用梯度裁剪技术,防止梯度爆炸导致的训练不稳定。在优化器的选择上,Adam或RMSprop等自适应优化算法通常能够取得较好的效果。此外,批归一化等技术也可以帮助稳定训练过程。

在数据预处理方面,需要特别注意序列长度的处理。由于实际应用中的序列往往长度不一,我们通常需要通过截断或填充的方式将它们处理成固定长度。对输入数据进行适当的标准化或归一化处理也是提升模型性能的重要步骤。

尽管简单循环网络存在这些局限性,但它的设计思想启发了后续更复杂的RNN变体,如长短期记忆网络(LSTM)和门控循环单元(GRU)的发展。这些高级模型通过引入门控机制等创新设计,在很大程度上克服了简单循环网络的缺点,但其基本原理仍然源于简单循环网络的核心思想。

简单循环网络(Simple RNN)是最基础的RNN结构。在每个时间步,网络会:

  1. 接收当前时间步的输入
  2. 结合上一时间步的隐藏状态
  3. 通过非线性激活函数计算当前时间步的隐藏状态
  4. 输出预测结果

这种结构可以应用于多种机器学习任务,比如序列预测、序列标注等。在情感分析任务中,我们可以这样实现:

class SimpleRNN:
    def __init__(self, input_size, hidden_size, output_size):
        self.hidden_size = hidden_size
        # 初始化权重
        self.W_xh = np.random.randn(input_size, hidden_size) / np.sqrt(input_size)
        self.W_hh = np.random.randn(hidden_size, hidden_size) / np.sqrt(hidden_size)
        self.W_hy = np.random.randn(hidden_size, output_size) / np.sqrt(hidden_size)
        
    def forward(self, inputs):
        h = np.zeros((1, self.hidden_size))
        for x in inputs:
            h = np.tanh(np.dot(x, self.W_xh) + np.dot(h, self.W_hh))
        return np.dot(h, self.W_hy)

3. 参数学习与优化

参数学习是循环神经网络中最核心的环节,它直接决定了模型的性能。与传统神经网络相比,RNN的参数学习具有其特殊性,这主要源于其处理序列数据的特性。让我们深入探讨RNN的参数学习机制和优化策略。

随时间反向传播(BPTT)是RNN参数学习的基础算法。在前向传播过程中,RNN会按时间顺序处理输入序列,并在每个时间步保存必要的中间状态。当到达序列末尾时,网络会计算损失函数,然后开始反向传播过程。这个过程可以通过下面的数学表达式来描述:

对于时间步t的前向传播:

通过代码来详细展示这个过程:

class RNNWithOptimization:
    def __init__(self, input_size, hidden_size, output_size):
        # 初始化网络参数
        self.params = {
            'W_xh': np.random.randn(input_size, hidden_size) / np.sqrt(input_size),
            'W_hh': np.random.randn(hidden_size, hidden_size) / np.sqrt(hidden_size),
            'W_hy': np.random.randn(hidden_size, output_size) / np.sqrt(hidden_size),
            'b_h': np.zeros((1, hidden_size)),
            'b_y': np.zeros((1, output_size))
        }
        
        # 初始化Adam优化器的动量参数
        self.m = {key: np.zeros_like(value) for key, value in self.params.items()}
        self.v = {key: np.zeros_like(value) for key, value in self.params.items()}
        self.t = 0
        
    def forward_pass(self, inputs, targets):
        """前向传播并计算损失"""
        h = np.zeros((1, self.params['W_hh'].shape[0]))  # 初始化隐藏状态
        loss = 0
        cache = {'h': [h], 'y': [], 'inputs': inputs}
        
        # 前向传播through time
        for t, x in enumerate(inputs):
            # 计算隐藏状态
            h = np.tanh(np.dot(x, self.params['W_xh']) + 
                       np.dot(h, self.params['W_hh']) + 
                       self.params['b_h'])
            
            # 计算输出
            y = np.dot(h, self.params['W_hy']) + self.params['b_y']
            
            # 保存中间状态用于反向传播
            cache['h'].append(h)
            cache['y'].append(y)
            
            # 计算损失
            loss += 0.5 * np.sum((y - targets[t]) ** 2)
            
        return loss, cache
    
    def backward_pass(self, cache, targets, clip_threshold=5):
        """实现BPTT算法"""
        grads = {key: np.zeros_like(value) for key, value in self.params.items()}
        H = len(cache['h']) - 1  # 序列长度
        
        dh_next = np.zeros_like(cache['h'][0])
        
        for t in reversed(range(H)):
            # 计算输出层的梯度
            dy = cache['y'][t] - targets[t]
            grads['W_hy'] += np.dot(cache['h'][t+1].T, dy)
            grads['b_y'] += dy
            
            # 反向传播到隐藏层
            dh = np.dot(dy, self.params['W_hy'].T) + dh_next
            
            # 计算tanh的梯度
            dtanh = (1 - cache['h'][t+1] ** 2) * dh
            
            # 计算各参数的梯度
            grads['b_h'] += dtanh
            grads['W_xh'] += np.dot(cache['inputs'][t].T, dtanh)
            grads['W_hh'] += np.dot(cache['h'][t].T, dtanh)
            
            # 为下一个时间步准备梯度
            dh_next = np.dot(dtanh, self.params['W_hh'].T)
        
        # 梯度裁剪
        for key in grads:
            np.clip(grads[key], -clip_threshold, clip_threshold, out=grads[key])
            
        return grads
    
    def adam_optimize(self, grads, learning_rate=0.001, beta1=0.9, beta2=0.999, epsilon=1e-8):
        """实现Adam优化算法"""
        self.t += 1
        
        for key in self.params:
            # 更新动量
            self.m[key] = beta1 * self.m[key] + (1 - beta1) * grads[key]
            self.v[key] = beta2 * self.v[key] + (1 - beta2) * (grads[key] ** 2)
            
            # 偏差修正
            m_hat = self.m[key] / (1 - beta1 ** self.t)
            v_hat = self.v[key] / (1 - beta2 ** self.t)
            
            # 更新参数
            self.params[key] -= learning_rate * m_hat / (np.sqrt(v_hat) + epsilon)

在实际应用中,RNN的训练还需要考虑以下几个关键优化策略:

  • 梯度裁剪:防止梯度爆炸问题,通过设置梯度阈值来限制梯度的大小:
def clip_gradients(gradients, threshold=5.0):
    for grad in gradients.values():
        np.clip(grad, -threshold, threshold, out=grad)
  •  学习率调整:采用学习率衰减或自适应学习率策略:
def adjust_learning_rate(initial_lr, epoch, decay_rate=0.1):
    return initial_lr / (1 + decay_rate * epoch)
  • 正则化技术:包括权重衰减、dropout等:
def apply_dropout(h, dropout_rate=0.5):
    mask = (np.random.rand(*h.shape) > dropout_rate) / (1 - dropout_rate)
    return h * mask
  • 批量训练:使用小批量梯度下降来提高训练效率和稳定性:
def batch_generator(data, batch_size):
    n_batches = len(data) // batch_size
    for i in range(n_batches):
        yield data[i*batch_size:(i+1)*batch_size]
  • 初始化策略:采用适当的权重初始化方法:
def xavier_initialization(input_dim, output_dim):
    return np.random.randn(input_dim, output_dim) * np.sqrt(2.0/(input_dim + output_dim))

为了更好地监控训练过程,我们还需要实现验证和早停机制

class EarlyStopping:
    def __init__(self, patience=5, min_delta=0):
        self.patience = patience
        self.min_delta = min_delta
        self.counter = 0
        self.best_loss = None
        self.early_stop = False
        
    def __call__(self, val_loss):
        if self.best_loss is None:
            self.best_loss = val_loss
        elif val_loss > self.best_loss - self.min_delta:
            self.counter += 1
            if self.counter >= self.patience:
                self.early_stop = True
        else:
            self.best_loss = val_loss
            self.counter = 0

在训练循环中,我们需要综合运用这些优化策略

def train_rnn(model, train_data, val_data, epochs=100, batch_size=32):
    early_stopping = EarlyStopping(patience=5)
    
    for epoch in range(epochs):
        train_loss = 0
        for batch in batch_generator(train_data, batch_size):
            # 前向传播
            loss, cache = model.forward_pass(batch.inputs, batch.targets)
            
            # 反向传播
            grads = model.backward_pass(cache, batch.targets)
            
            # 应用优化策略
            clip_gradients(grads)
            model.adam_optimize(grads)
            
            train_loss += loss
            
        # 验证
        val_loss = evaluate(model, val_data)
        
        # 早停检查
        early_stopping(val_loss)
        if early_stopping.early_stop:
            print(f"Early stopping at epoch {epoch}")
            break

参数学习与优化是RNN成功应用的关键。通过合理的优化策略组合,我们可以显著提升模型的训练效果和泛化能力。在实践中,需要根据具体任务特点和数据特性,灵活调整这些优化策略的使用方式和参数设置。同时,良好的监控和调试机制也是确保训练过程顺利进行的重要保障。

4. 基于门控的循环神经网络

基于门控的循环神经网络是为了解决简单RNN在处理长序列时存在的梯度消失和长程依赖问题而提出的。通过引入门控机制,这些网络能够更好地控制信息的流动,从而在长序列处理任务中取得更好的效果。

4.1 长短期记忆网络(LSTM)

LSTM是最早提出且最为经典的门控RNN结构。它通过设计遗忘门、输入门和输出门三个门控单元,以及一个记忆单元,来控制信息的存储、更新和输出。

4.1.1 LSTM的核心组件:

class LSTM:
    def __init__(self, input_size, hidden_size):
        # 初始化权重矩阵
        # 输入门参数
        self.W_xi = np.random.randn(input_size, hidden_size) * 0.01
        self.W_hi = np.random.randn(hidden_size, hidden_size) * 0.01
        self.b_i = np.zeros((1, hidden_size))
        
        # 遗忘门参数
        self.W_xf = np.random.randn(input_size, hidden_size) * 0.01
        self.W_hf = np.random.randn(hidden_size, hidden_size) * 0.01
        self.b_f = np.zeros((1, hidden_size))
        
        # 输出门参数
        self.W_xo = np.random.randn(input_size, hidden_size) * 0.01
        self.W_ho = np.random.randn(hidden_size, hidden_size) * 0.01
        self.b_o = np.zeros((1, hidden_size))
        
        # 候选记忆单元参数
        self.W_xc = np.random.randn(input_size, hidden_size) * 0.01
        self.W_hc = np.random.randn(hidden_size, hidden_size) * 0.01
        self.b_c = np.zeros((1, hidden_size))
    
    def forward(self, x, prev_h, prev_c):
        # 输入门
        i = sigmoid(np.dot(x, self.W_xi) + np.dot(prev_h, self.W_hi) + self.b_i)
        
        # 遗忘门
        f = sigmoid(np.dot(x, self.W_xf) + np.dot(prev_h, self.W_hf) + self.b_f)
        
        # 输出门
        o = sigmoid(np.dot(x, self.W_xo) + np.dot(prev_h, self.W_ho) + self.b_o)
        
        # 候选记忆单元
        c_tilde = np.tanh(np.dot(x, self.W_xc) + np.dot(prev_h, self.W_hc) + self.b_c)
        
        # 更新记忆单元
        c = f * prev_c + i * c_tilde
        
        # 计算隐藏状态
        h = o * np.tanh(c)
        
        return h, c

LSTM的各个门控单元作用如下:

  1. 遗忘门(f):控制上一时刻记忆单元中的信息有多少需要保留
  2. 输入门(i):控制当前时刻新信息有多少需要写入记忆单元
  3. 输出门(o):控制记忆单元中的信息有多少需要输出到隐藏状态
  4. 记忆单元(c):存储长期记忆,通过门控机制进行更新

4.2 门控循环单元(GRU)

GRU是LSTM的简化版本,它将输入门和遗忘门合并为更新门,并引入重置门来控制历史信息的使用。

class GRU:
    def __init__(self, input_size, hidden_size):
        # 更新门参数
        self.W_xz = np.random.randn(input_size, hidden_size) * 0.01
        self.W_hz = np.random.randn(hidden_size, hidden_size) * 0.01
        self.b_z = np.zeros((1, hidden_size))
        
        # 重置门参数
        self.W_xr = np.random.randn(input_size, hidden_size) * 0.01
        self.W_hr = np.random.randn(hidden_size, hidden_size) * 0.01
        self.b_r = np.zeros((1, hidden_size))
        
        # 候选隐藏状态参数
        self.W_xh = np.random.randn(input_size, hidden_size) * 0.01
        self.W_hh = np.random.randn(hidden_size, hidden_size) * 0.01
        self.b_h = np.zeros((1, hidden_size))
    
    def forward(self, x, prev_h):
        # 更新门
        z = sigmoid(np.dot(x, self.W_xz) + np.dot(prev_h, self.W_hz) + self.b_z)
        
        # 重置门
        r = sigmoid(np.dot(x, self.W_xr) + np.dot(prev_h, self.W_hr) + self.b_r)
        
        # 候选隐藏状态
        h_tilde = np.tanh(np.dot(x, self.W_xh) + np.dot(r * prev_h, self.W_hh) + self.b_h)
        
        # 更新隐藏状态
        h = (1 - z) * prev_h + z * h_tilde
        
        return h

5 实际应用中的优化技巧

5.1 变体和改进

class PeepholeConnLSTM:
    def __init__(self, input_size, hidden_size):
        # 标准LSTM参数
        self.lstm = LSTM(input_size, hidden_size)
        
        # Peephole连接参数
        self.W_ci = np.random.randn(hidden_size, hidden_size) * 0.01
        self.W_cf = np.random.randn(hidden_size, hidden_size) * 0.01
        self.W_co = np.random.randn(hidden_size, hidden_size) * 0.01
    
    def forward(self, x, prev_h, prev_c):
        # 修改门控计算,加入记忆单元的直接连接
        i = sigmoid(np.dot(x, self.lstm.W_xi) + 
                   np.dot(prev_h, self.lstm.W_hi) + 
                   np.dot(prev_c, self.W_ci) + 
                   self.lstm.b_i)
        
        f = sigmoid(np.dot(x, self.lstm.W_xf) + 
                   np.dot(prev_h, self.lstm.W_hf) + 
                   np.dot(prev_c, self.W_cf) + 
                   self.lstm.b_f)
        
        # 其余计算与标准LSTM相同
        ...

5.2 注意力机制的结合

class AttentionLSTM:
    def __init__(self, input_size, hidden_size, attention_size):
        self.lstm = LSTM(input_size, hidden_size)
        self.attention = Attention(hidden_size, attention_size)
    
    def forward(self, x_sequence, prev_h, prev_c):
        # 存储所有隐藏状态
        all_hidden_states = []
        current_h, current_c = prev_h, prev_c
        
        # LSTM前向传播
        for x in x_sequence:
            current_h, current_c = self.lstm.forward(x, current_h, current_c)
            all_hidden_states.append(current_h)
        
        # 计算注意力权重
        context = self.attention(all_hidden_states)
        
        return context, current_h, current_c

6 实现细节和最佳实践

6.1 初始化策略

def initialize_lstm_params(input_size, hidden_size):
    # 使用正交初始化
    def orthogonal(shape):
        rand = np.random.randn(*shape)
        u, _, v = np.linalg.svd(rand)
        return u if u.shape == shape else v
    
    params = {}
    for gate in ['i', 'f', 'o', 'c']:
        params[f'W_x{gate}'] = orthogonal((input_size, hidden_size))
        params[f'W_h{gate}'] = orthogonal((hidden_size, hidden_size))
        params[f'b_{gate}'] = np.zeros((1, hidden_size))
        
        # 特殊处理遗忘门偏置
        if gate == 'f':
            params[f'b_{gate}'] += 1.0
    
    return params

6.1.1 梯度处理

def lstm_backward(dh_next, dc_next, cache):
    # 解包缓存的值
    x, prev_h, prev_c, i, f, o, c_tilde, c, h = cache
    
    # 计算各个门和状态的梯度
    do = dh_next * np.tanh(c)
    dc = dc_next + dh_next * o * (1 - np.tanh(c)**2)
    
    di = dc * c_tilde
    df = dc * prev_c
    dc_tilde = dc * i
    
    # 计算激活函数的梯度
    di_raw = di * i * (1 - i)
    df_raw = df * f * (1 - f)
    do_raw = do * o * (1 - o)
    dc_tilde_raw = dc_tilde * (1 - c_tilde**2)
    
    # 计算权重梯度
    dW_xi = np.dot(x.T, di_raw)
    dW_hi = np.dot(prev_h.T, di_raw)
    db_i = np.sum(di_raw, axis=0, keepdims=True)
    
    # ... 类似计算其他参数的梯度
    
    return dW_xi, dW_hi, db_i, ...

基于门控的循环神经网络通过其特殊的结构设计,很好地解决了简单RNN面临的问题。它们在各种序列处理任务中都展现出了优异的性能,成为了深度学习领域最重要的模型之一。理解这些模型的工作原理和实现细节,对于实际应用中选择合适的模型结构和优化策略具有重要的指导意义。

内容不全等,请各位理解支持!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

低代码开源项目Joget的研究——Joget8社区版安装部署

大纲 环境准备安装必要软件配置Java配置JAVA_HOME配置Java软链安装三方库 获取源码配置MySql数据库创建用户创建数据库导入初始数据 配置数据库连接配置sessionFactory(非必须,如果后续保存再配置)编译下载tomcat启动下载aspectjweaver移动jw…

赋能开发者 | 麒麟信安受邀参加2024开放原子开发者大会,以技术为引领,以人才创发展

12月20至21日,以“一切为了开发者”为主题的“2024开放原子开发者大会暨首届开源技术学术大会”在湖北武汉举办。本届大会由开放原子开源基金会、中国通信学会联合主办,旨在贯彻落实国家软件发展战略,加速培育壮大我国开源生态。工业和信息化…

HTML5实现好看的喜庆圣诞节网站源码

HTML5实现好看的喜庆圣诞节网站源码 前言一、设计来源1.1 主界面1.2 圣诞介绍界面1.3 圣诞象征界面1.4 圣诞活动界面1.5 圣诞热度界面1.6 圣诞纪念界面1.7 联系我们界面 二、效果和源码2.1 动态效果2.2 源代码 源码下载结束语 HTML5实现好看的喜庆圣诞节网站源码,圣…

1.微服务灰度发布落地实践(方案设计)

前言 微服务架构中的灰度发布(也称为金丝雀发布或渐进式发布)是一种在不影响现有用户的情况下,逐步将新版本的服务部署到生产环境的策略。通过灰度发布,你可以先将新版本的服务暴露给一小部分用户或特定的流量,观察其…

Vue中动态样式绑定+CSS变量实现切换明暗主题功能——从入门到进阶

1.直接借助Vue的动态绑定样式绑定 Vue动态样式绑定 在Vue中,动态样式绑定是一种强大的功能,它允许开发者根据数据的变化动态地更新元素的样式。以下是对Vue动态样式绑定的详细知识梳理与详解: 一、基础知识 Vue的动态样式绑定主要通过v-b…

华为管理变革之道:奋斗文化与活力

目录 企业文化是什么? 为什么活下去是华为的文化? 活下来,是华为公司的最低纲领,也是华为公司的最高纲领! 资源终会枯竭,唯有文化才能生生不息 企业文化之一:以客户为中心 企业文化之二&a…

强化数据治理能力,夯实数字政府建设基石!

当下,数字政府建设已成为推动国家治理体系和治理能力现代化的关键路径。数据作为数字化时代的关键生产要素,直接影响着数字政府建设的能效,关系着政府决策的科学性、公共服务的精准性以及社会治理的有效性。因此,通过数据治理来全…

NFC 碰一碰发视频源码搭建技术详解,支持OEM

一、引言 NFC(Near Field Communication)近场通信技术以其便捷性和安全性在现代移动应用中得到了广泛应用。结合视频播放功能,实现 NFC 碰一碰发视频的应用场景,能够为用户带来全新的交互体验,例如在商场的产品推广、景…

【论文阅读】AllMatch: Exploiting All Unlabeled Data for Semi-Supervised Learning

一、引言 在当今的机器学习领域,半监督学习(SSL)作为一种重要的学习范式,受到了广泛的关注。它旨在利用有限的标记数据和大量的未标记数据来提升模型的性能,从而在数据标记成本较高而未标记数据丰富的情况下发挥重要作…

光谱相机与普通相机的区别

一、成像目的 普通相机:主要目的是记录物体的外观形态,生成人眼可见的、直观的二维图像,重点在于还原物体的形状、颜色和纹理等视觉特征,以供人们进行观赏、记录场景或人物等用途。例如,拍摄旅游风景照片、人物肖像等…

基于单片机的蓄电池内阻检测系统设计(论文+源码)

1 系统的功能及方案设计 在本次设计中,考虑到整体设计难度。在此选择了上述的方法一来作为本次蓄电池内阻检测的方案。其系统整个框图如下图1所示。其主要的核心控制模块由LCD显示模块,负载电路模块,AD模数转换模块,继电器控制模…

Git核心概念

版本控制 什么是版本控制 版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。 除了项目源代码,你可以对任何类型的文件进行版本控制。 为什么要版本控制 有了它你就可以将某个文件回溯到之前的状态,甚至将整…

Kotlin 协程基础知识总结二 —— 启动与取消

协程启动与取消的主要内容: 启动协程:启动构建器、启动模式、作用域构建器、Job 生命周期取消协程:协程的取消、CPU 密集型任务取消、协程取消的副作用、超时任务 1、协程构建器 (P20)launch 与 aysnc 两种协程构建…

kong网关使用pre-function插件,改写接口的返回数据

一、背景 kong作为api网关,除了反向代理后端服务外,还可对接口进行预处理。 比如本文提及的一个小功能,根据http header某个字段的值,等于多少的时候,返回一个固定的报文。 使用到的kong插件是pre-function。 除了上…

群落生态学研究进展▌Hmsc包对于群落生态学假说的解读、Hmsc包开展单物种和多物种分析的技术细节及Hmsc包的实际应用

HMSC(Hierarchical Species Distribution Models)是一种用于预测物种分布的统计模型。它在群落生态学中的应用广泛,可以帮助科学家研究物种在不同环境条件下的分布规律,以及预测物种在未来环境变化下的潜在分布范围。 举例来说&a…

MacroSan 2500_24A配置

双控制器电源同时按下,切记/切记/切记 默认信息 默认地址:192.168.0.210 输入ODSP授权后设置密码## 配置端口 物理资源–>设备–>网口–>eth-1:0:0或eth-2:0:0 创建存储池 存储资源–>存储池 介质类型:混合(支持机械及SSD)全闪(仅支持SSD) RAID类型:CRAID-P(基于磁…

SQL-leetcode-180. 连续出现的数字

180. 连续出现的数字 表:Logs -------------------- | Column Name | Type | -------------------- | id | int | | num | varchar | -------------------- 在 SQL 中,id 是该表的主键。 id 是一个自增列。 找出所有至少连续出现三次的数字。 返回的…

ISDP010_基于DDD架构实现收银用例主成功场景

信息系统开发实践 | 系列文章传送门 ISDP001_课程概述 ISDP002_Maven上_创建Maven项目 ISDP003_Maven下_Maven项目依赖配置 ISDP004_创建SpringBoot3项目 ISDP005_Spring组件与自动装配 ISDP006_逻辑架构设计 ISDP007_Springboot日志配置与单元测试 ISDP008_SpringB…

ElementPlus 自定义封装 el-date-picker 的快捷功能

文章目录 需求分析 需求 分析 我们看到官网上给出的案例如下,但是不太满足我们用户想要的快捷功能,因为不太多,因此需要我们自己封装一些,方法如下 外部自定义该组件的快捷内容 export const getPickerOptions () > {cons…

怎么模仿磁盘 IO 慢的情况?并用于MySQL进行测试

今天给大家分享一篇在测试环境或者是自己想检验自己MySQL性能的文章 实验环境: Rocky Linux 8 镜像:Rocky-8.6-x86_64-dvd.iso 1. 创建一个大文件作为虚拟磁盘 [rootlocalhost ~] dd if/dev/zero of/tmp/slowdisk.img bs1M count100 记录了1000 的读入…