阿里云个人网站建设书,南通网站建设方案开发,免费手机网站,wordpress 添加 links继续填坑#xff0c;本篇介绍深度学习中的长短期记忆网络~~~~ 目录
3.3. 长短期记忆网络#xff08;LSTM#xff09;
3.3.1. 什么是长短期记忆网络
3.3.2. 形成过程与运行原理
3.3.2.1. 细胞状态与门结构
3.3.2.2. 遗忘门
3.3.2.3. 输入门
3.3.2.4. 细胞状态更新
3.…继续填坑本篇介绍深度学习中的长短期记忆网络~~~~ 目录
3.3. 长短期记忆网络LSTM
3.3.1. 什么是长短期记忆网络
3.3.2. 形成过程与运行原理
3.3.2.1. 细胞状态与门结构
3.3.2.2. 遗忘门
3.3.2.3. 输入门
3.3.2.4. 细胞状态更新
3.3.2.5. 输出门
3.3.2.6. 以上各步骤的示例代码
3.3.3. 优缺点
3.3.4. 存在的问题及解决方法
3.3.5. 示例代码 3.3. 长短期记忆网络LSTM
3.3.1. 什么是长短期记忆网络
长短期记忆网络LSTMLong Short-Term Memory算法是一种特殊的循环神经网络RNN它旨在解决传统RNN在处理长序列数据时遇到的梯度消失和梯度爆炸问题从而更有效地学习序列中的长期依赖关系。 为了最小化训练误差通常使用梯度下降法如应用时序性倒传递算法来依据错误修改每次的权重。此外LSTM有多种变体其中一个重要的版本是门控循环单元GRU。 LSTM适合于处理和预测时间序列中间隔和延迟非常长的重要事件。其表现通常比时间递归神经网络及隐马尔科夫模型HMM更好。例如在不分段连续手写识别上LSTM模型曾赢得过ICDAR手写识别比赛冠军。此外LSTM还广泛应用于自主语音识别并在2013年使用TIMIT自然演讲数据库达成了17.7%的错误率纪录。LSTM的成功在很大程度上促进了深度学习和人工智能领域的发展。尽管近年来出现了新的模型结构如基于注意力机制的Transformer但LSTM仍然是许多序列建模任务的可靠选择。随着时间的推移LSTM被广泛应用于自然语言处理、语音识别、文本生成、视频分析等多个领域 3.3.2. 形成过程与运行原理
LSTM通过引入“门”结构和“细胞状态”来更好地捕捉序列中的长期依赖关系。通过借鉴脑神经学的知识来组建序列中的长期依赖关系
3.3.2.1. 细胞状态与门结构
LSTM的核心是细胞状态它像一条传送带在整个链上运行只有一些小的线性操作作用其上信息在上面流传保持不变会很容易。LSTM通过精心设计的门结构来去除或增加信息到细胞状态这些门结构包括遗忘门、输入门和输出门。
3.3.2.2. 遗忘门
决定从细胞状态中丢弃什么信息。它查看当前的输入和前一个时间步的隐藏状态并为细胞状态中的每个数字输出一个在0到1之间的数字1表示“完全保留”0表示“完全舍弃”。
遗忘门决定了从上一个时间步的细胞状态中丢弃哪些信息。其计算公式为 其中表示输入门在时刻的值是时刻 ( t ) 的输入是前一个时刻的隐藏状态和 是对应的权重矩阵而是偏置项。函数表示sigmoid激活函数。 3.3.2.3. 输入门
决定什么新信息将被存储在细胞状态中。这包括两部分一部分是输入门决定我们将更新哪些部分另一部分是tanh层创建一个新的候选值向量这个向量可能会被添加到细胞状态中。 类似地表示遗忘门在时刻的值其他符号的含义与输入门公式中的相同只是权重和偏置项是针对遗忘门的。 3.3.2.4. 细胞状态更新
首先旧细胞状态与遗忘门相乘丢弃掉需要丢弃的信息。然后将输入门的输出与tanh层的输出相乘得出新的候选细胞状态。最后将这两个值相加形成新的细胞状态。
旧细胞状态与遗忘门相乘 这里表示经过遗忘门处理后的旧细胞状态是前一个时刻的细胞状态 是遗忘门在时刻的输出而表示逐元素相乘Hadamard乘积。这一步的目的是丢弃掉不需要的信息。
计算新的候选细胞状态 其中是新的候选细胞状态是时刻 的输入 是前一个时刻的隐藏状态 和 是对应的权重矩阵是偏置项。函数 是双曲正切激活函数它将输入值压缩到 ( -1 ) 到 ( 1 ) 的范围内。
将候选细胞状态与输入门相乘 这里是输入门在时刻的输出表示逐元素相乘。这一步的目的是根据输入门的选择来决定哪些新的信息被加入到细胞状态中。
更新细胞状态 最终新的细胞状态是经过遗忘门处理后的旧细胞状态 与经过输入门处理后的新候选细胞状态 之和。这一步完成了细胞状态的更新使得LSTM能够记住长期依赖关系。 3.3.2.5. 输出门
基于细胞状态来决定输出什么。首先运行一个sigmoid层来确定细胞状态的哪个部分将输出然后将细胞状态通过tanh进行处理得到一个在-1到1之间的值并将其与sigmoid门的输出相乘最终得到输出。 在这里是输出门在时刻的值其他参数和符号的意义与前面公式中的一致但针对输出门。
3.3.2.6. 以上各步骤的示例代码
Python代码示例
import numpy as np def sigmoid(x): return 1 / (1 np.exp(-x)) def tanh(x): return np.tanh(x) # LSTM Cell 参数初始化
input_size 10
hidden_size 20 Wf np.random.randn(hidden_size, hidden_size input_size) # 遗忘门权重
Wi np.random.randn(hidden_size, hidden_size input_size) # 输入门权重
Wc np.random.randn(hidden_size, hidden_size input_size) # 候选细胞状态权重
Wo np.random.randn(hidden_size, hidden_size input_size) # 输出门权重 # LSTM Cell 前向传播
def lstm_cell_forward(xt, ht_prev, ct_prev, Wf, Wi, Wc, Wo): # 拼接前一个隐藏状态和当前输入 concat np.concatenate((ht_prev, xt), axis0) # 计算遗忘门 ft sigmoid(np.dot(Wf, concat)) # 计算输入门 it sigmoid(np.dot(Wi, concat)) # 计算候选细胞状态 cct tanh(np.dot(Wc, concat)) # 细胞状态更新 ct ft * ct_prev it * cct # 计算输出门 ot sigmoid(np.dot(Wo, concat)) # 计算隐藏状态 ht ot * tanh(ct) return ht, ct # 示例使用
xt np.random.randn(input_size) # 当前输入
ht_prev np.zeros(hidden_size) # 前一个隐藏状态
ct_prev np.zeros(hidden_size) # 前一个细胞状态 ht, ct lstm_cell_forward(xt, ht_prev, ct_prev, Wf, Wi, Wc, Wo)C代码示例
#include Eigen/Dense
#include cmath using namespace Eigen; // 激活函数
double sigmoid(double x) { return 1.0 / (1.0 std::exp(-x));
} double tanh(double x) { return std::tanh(x);
} // LSTM单元前向传播
void LSTMCellForward(const VectorXd xt, const VectorXd ht_prev, const VectorXd ct_prev, const MatrixXd Wf, const MatrixXd Wi, const MatrixXd Wc, const MatrixXd Wo, VectorXd ht, VectorXd ct) { int input_size xt.size(); int hidden_size ht_prev.size(); VectorXd concat(input_size hidden_size); concat ht_prev, xt; // 计算遗忘门 VectorXd ft concat.unaryExpr([](double elem) { return sigmoid(elem); }) * Wf.transpose(); // 计算输入门 VectorXd it concat.unaryExpr([](double elem) { return sigmoid(elem); }) * Wi.transpose(); // 计算候选细胞状态 VectorXd cct concat.unaryExpr([](double elem) { return tanh(elem); }) * Wc.transpose(); // 细胞状态更新 ct ft.array() * ct_prev.array() it.array() * cct.array(); // 计算输出门 VectorXd ot concat.unaryExpr([](double elem) { return sigmoid(elem); }) * Wo.transpose(); // 计算隐藏状态 ht ot.array() * ct.array().unaryExpr([](double elem) { return tanh(elem); });
} int main() { int input_size 10; int hidden_size 20; MatrixXd Wf MatrixXd::Random(hidden_size, hidden_size input_size); // 遗忘门权重 MatrixXd Wi MatrixXd::Random(hidden_size, hidden_size input_size); // 输入门权重 MatrixXd Wc MatrixXd::Random(hidden_size, hidden_size input_size); // 候选细胞状态权重 MatrixXd Wo MatrixXd::Random(hidden_size, hidden_size input_size); // 输出门权重 VectorXd xt VectorXd::Random(input_size); // 当前输入 VectorXd ht_prev VectorXd::Zero(hidden_size); // 前一个隐藏状态 VectorXd ct_prev VectorXd::Zero(hidden_size); // 前一个细胞状态 VectorXd ht(hidden_size), ct(hidden_size); LSTMCellForward(xt, ht_prev, ct_prev, Wf, Wi, Wc, Wo, ht, ct); // Do something with ht and ct... return 0;
}
这些代码是简化示例实际应用中LSTM的实现会更加复杂包括多个时间步的迭代、批处理支持、梯度计算和权重更新等。
在生产环境中建议使用成熟的深度学习框架如TensorFlow或PyTorch来实现LSTM哦。 3.3.3. 优缺点
优点 能够有效地解决传统RNN中的梯度消失和梯度爆炸问题。能够更好地捕捉序列中的长期依赖关系。在处理长序列数据时具有优势。 缺点 LSTM模型相对复杂计算成本较高。对于输入序列长度较长时可能会出现过拟合现象导致泛化能力下降。 3.3.4. 存在的问题及解决方法 过拟合问题可以通过正则化、dropout等技术来减轻过拟合现象。 无法有效捕捉时间上下文关系可以引入双向LSTMBidirectional LSTM结构来提高对于时间上下文之间关系的建模能力。 对输入数据序列顺序敏感在实际应用中可以通过数据增强、序列颠倒等方法来减轻模型对输入数据序列顺序的敏感性。 3.3.5. 示例代码
Python代码
由于篇幅限制这里提供一个简化的Python示例使用PyTorch库实现LSTM
import torch
import torch.nn as nn # 定义一个简单的LSTM模型
class SimpleLSTM(nn.Module): def __init__(self, input_size, hidden_size, output_size): super(SimpleLSTM, self).__init__() self.hidden_size hidden_size self.lstm nn.LSTM(input_size, hidden_size, batch_firstTrue) self.fc nn.Linear(hidden_size, output_size) def forward(self, x, hidden): lstm_out, hidden self.lstm(x, hidden) output self.fc(lstm_out[:, -1, :]) # 取最后一个时间步的输出进行分类 return output, hidden def init_hidden(self, batch_size): return (torch.zeros(1, batch_size, self.hidden_size), torch.zeros(1, batch_size, self.hidden_size)) # 模型参数
input_size 10
hidden_size 20
output_size 2
batch_size 1
sequence_length 5 # 创建模型实例
model SimpleLSTM(input_size, hidden_size, output_size) # 创建虚拟输入数据和初始隐藏状态
x torch.randn(batch_size, sequence_length, input_size)
hidden model.init_hidden(batch_size) # 前向传播
output, hidden model(x, hidden)
print(output)
C代码
在C中使用LSTM我们通常会借助PyTorch的C API也称为LibTorch。以下是一个简单的示例
#include torch/script.h // 包含TorchScript的头文件
#include iostream int main() { // 加载一个预先训练好的LSTM模型这里假设你已经有一个用PyTorch训练的模型并导出了TorchScript torch::jit::script::Module module; try { module torch::jit::load(lstm_model.pt); // 加载模型 } catch (const c10::Error e) { std::cerr 模型加载错误\n; return -1; } // 创建一个输入张量假设输入大小为[1, 5, 10]batch_size, sequence_length, input_size torch::Tensor input torch::randn({1, 5, 10}); // 执行模型前向传播 std::vectortorch::jit::IValue inputs; inputs.push_back(input); torch::Tensor output module.forward(inputs).toTensor(); std::cout output std::endl; return 0;
}
请注意C 示例中的模型需要是预先训练好并导出为TorchScript的模型。TorchScript是PyTorch的一个子集允许模型在没有Python运行时的环境中执行。
在C中直接使用LSTM而不依赖预先训练的模型会更复杂因为你需要手动实现LSTM的所有细节。这通常不是推荐的做法除非你有特定的性能要求或需要深度定制LSTM的行为。
在大多数情况下使用PyTorch等高级库会更加方便和高效。