自学内容网 自学内容网

24.9.19学习笔记

  1. 隐藏状态(Hidden State)

    • 想象一下你在和朋友聊天,你们聊了很多话题,你的大脑会记住你们聊过的内容,这样你就能在对话中引用之前的内容。在RNN这类神经网络中,隐藏状态就像是你的大脑记忆,它记录了到目前为止处理的所有信息,这样网络就能在下一步的计算中使用这些信息。
  2. 细胞状态(Cell State)

    • 还是用聊天的例子,细胞状态就像是你们聊天的笔记,它详细记录了每个话题的细节。在LSTM这种特殊类型的神经网络中,细胞状态是一种特殊的记忆,它不仅记得你们聊了什么,还记得每个话题的具体内容。这样,即使你们聊了很长时间,LSTM也能回忆起很久以前的细节。

在LSTM中,隐藏状态和细胞状态一起工作,隐藏状态帮助网络决定下一步应该输出什么而细胞状态则确保网络不会忘记任何重要的信息。这就像是你在聊天时,既能回应朋友的话(隐藏状态),又能记住你们之前聊过的所有细节(细胞状态)。这种机制使得LSTM非常适合处理需要记忆的信息,比如语音识别或者翻译句子,因为它能够理解和记住整个对话的历史。

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
import numpy as np
import random

# 读取数据
with open('eng.txt', 'r', encoding='utf-8') as f:
    text = f.read()  # 从文件中读取整个文本内容

# 创建字符到索引的映射
chars = sorted(list(set(text)))  # 获取文本中所有独特的字符,并排序
char_to_idx = {ch: i for i, ch in enumerate(chars)}  # 创建字符到索引的字典
idx_to_char = {i: ch for i, ch in enumerate(chars)}  # 创建索引到字符的字典

# 转换为索引序列
data = [char_to_idx[ch] for ch in text]  # 将文本中的每个字符转换为其对应的索引


class CharDataset(Dataset):
    def __init__(self, data, seq_length=64):
        self.data = data  # 存储数据
        self.seq_length = seq_length  # 序列长度

        # 检查数据长度是否足够
        if len(self.data) <= self.seq_length:
            raise ValueError(
                f"Data length ({len(self.data)}) is not sufficient for the sequence length ({self.seq_length}).")

    def __len__(self):
        # 返回不小于0的长度
        return max(0, len(self.data) - self.seq_length)

    def __getitem__(self, idx):
        if idx >= len(self):  # 检查索引是否超出范围
            raise IndexError(f"Index {idx} out of range for dataset of length {len(self)}.")

        input_seq = self.data[idx:idx + self.seq_length]  # 获取输入序列
        target_seq = self.data[idx + 1:idx + self.seq_length + 1]  # 获取目标序列
        return torch.tensor(input_seq, dtype=torch.long), torch.tensor(target_seq, dtype=torch.long)  # 返回输入和目标序列


seq_length = 64  # 定义序列长度
batch_size = 32  # 定义批次大小

# 检查数据长度是否符合要求
if len(data) <= seq_length:
    raise ValueError(f"Data length ({len(data)}) is too short for the given sequence length ({seq_length}).")

dataset = CharDataset(data, seq_length)  # 创建数据集实例
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)  # 创建数据加载器,打乱数据


class CharLSTM(nn.Module):
    def __init__(self, vocab_size, embed_dim, hidden_dim, num_layers=1):
        super(CharLSTM, self).__init__()  # 初始化父类
        self.embed = nn.Embedding(vocab_size, embed_dim)  # 嵌入层
        self.lstm = nn.LSTM(embed_dim, hidden_dim, num_layers, batch_first=True)  # LSTM 层
        self.fc = nn.Linear(hidden_dim, vocab_size)  # 全连接层

    def forward(self, x, h0=None, c0=None):
        if h0 is None or c0 is None:
            h0 = torch.zeros(self.lstm.num_layers, x.size(0), self.lstm.hidden_size).to(x.device)
            c0 = torch.zeros(self.lstm.num_layers, x.size(0), self.lstm.hidden_size).to(x.device)

        x = self.embed(x)  # 将输入通过嵌入层
        out, (h, c) = self.lstm(x, (h0, c0))  # 通过 LSTM 层
        out = self.fc(out)  # 通过全连接层
        return out, (h, c)  # 返回输出和隐藏状态


vocab_size = len(chars)  # 字符词汇表大小
embed_dim = 128  # 嵌入维度
hidden_dim = 256  # 隐藏层维度
num_layers = 2  # LSTM 层数

model = CharLSTM(vocab_size, embed_dim, hidden_dim, num_layers)  # 创建模型实例
criterion = nn.CrossEntropyLoss()  # 定义损失函数
optimizer = optim.Adam(model.parameters(), lr=0.001)  # 定义优化器

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")  # 选择计算设备
model.to(device)  # 将模型移动到选定的设备

num_epochs = 10  # 训练轮数
for epoch in range(num_epochs):
    model.train()  # 设置模型为训练模式
    total_loss = 0  # 初始化总损失
    for inputs, targets in dataloader:  # 迭代数据加载器
        inputs, targets = inputs.to(device), targets.to(device)  # 将数据移动到设备

        # 初始化隐藏状态和细胞状态
        h0 = torch.zeros(num_layers, inputs.size(0), hidden_dim).to(device)
        c0 = torch.zeros(num_layers, inputs.size(0), hidden_dim).to(device)

        optimizer.zero_grad()  # 清零梯度
        outputs, _ = model(inputs, h0, c0)  # 前向传播,传入隐藏状态
        loss = criterion(outputs.view(-1, vocab_size), targets.view(-1))  # 计算损失
        loss.backward()  # 反向传播
        optimizer.step()  # 更新参数

        total_loss += loss.item()  # 累加损失

    avg_loss = total_loss / len(dataloader)  # 计算平均损失
    print(f'Epoch {epoch + 1}/{num_epochs}, Loss: {avg_loss:.4f}')  # 打印当前 epoch 的损失


def generate_text(model, start_string, length=1000, temperature=1.0):
    model.eval()  # 设置模型为评估模式
    with torch.no_grad():  # 关闭梯度计算
        # 将起始字符串转换为索引
        input_seq = [char_to_idx[ch] for ch in start_string]
        input_seq = torch.tensor(input_seq, dtype=torch.long).unsqueeze(0).to(device)  # 转换为张量并移动到设备

        # 初始化隐藏状态和细胞状态
        h, c = torch.zeros(num_layers, input_seq.size(0), hidden_dim).to(device), torch.zeros(num_layers,
                                                                                              input_seq.size(0),
                                                                                              hidden_dim).to(device)

        # 生成的文本
        generated_text = start_string

        for i in range(length):  # 生成指定长度的文本
            # 模型前向传播
            output, (h, c) = model(input_seq, h, c)

            # 获取最后一个时间步的输出
            output = output[:, -1, :]

            # 应用温度调整
            output = output / temperature
            probs = torch.softmax(output, dim=-1)

            # 采样下一个字符
            next_idx = torch.multinomial(probs, 1).item()
            next_char = idx_to_char[next_idx]

            # 添加生成的字符到生成的文本中
            generated_text += next_char

            # 更新输入张量为新的字符
            input_seq = torch.tensor([[next_idx]], dtype=torch.long).to(device)

        return generated_text  # 返回生成的文本


# 生成文本
start_string = "The quick brown fox"  # 起始字符串
generated_text = generate_text(model, start_string, length=1000)  # 生成 500 个字符的文本

print(generated_text)  # 打印生成的文本

想象你和你的朋友正在计划一个大型聚会。你们每天都在讨论不同的细节,比如时间、地点、邀请哪些人等等。每次讨论后,你们都会总结到目前为止的决定,并记下还需要解决的问题。

在这个例子中:

  1. 嵌入后的向量序列(x):就像是你们每天讨论的笔记,里面包含了所有的细节和决策。

  2. LSTM 层:就像是你们用来记录和更新聚会计划的超级笔记本。它不仅能记住你们已经决定的事情,还能记住你们需要考虑的事情。

  3. 输出(out):在每次讨论后,你们会有一个更新后的计划,这就是 LSTM 层的输出。它代表了基于到目前为止所有讨论的信息,你们对聚会计划的最新理解。

  4. 隐藏状态(h):这就像是你们总结的当前决定。比如,你们可能已经决定了聚会的日期和时间,但还没有决定地点。这个隐藏状态就是你们在当前时间点上认为最重要的信息。

  5. 细胞状态(c):这就像是你们的“待办事项”列表,记录了所有你们还需要讨论和决定的事情。这个细胞状态帮助你们在下次讨论时能够快速回忆起还有哪些事情没有解决。

所以,out, (h, c) = self.lstm(x, (h0, c0)) 这行代码的意思是:你们把每天的讨论笔记(嵌入后的向量序列)输入到超级笔记本(LSTM 层)中,得到了最新的聚会计划(输出),并且更新了你们的当前决定(隐藏状态)和待办事项(细胞状态)。这样,无论讨论进行到哪一天,你们都能保持信息的最新状态,并准备好进行下一次讨论。

 


原文地址:https://blog.csdn.net/m0_64006292/article/details/142361505

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!