鱼C论坛

 找回密码
 立即注册
查看: 194|回复: 9

lstm训练模型进行文本情感分析

[复制链接]
发表于 2024-10-15 15:07:59 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
def makeTheNet():
    class TweetDisasterRNN(nn.Module):
        def __init__(self, no_layers, vocab_size, hidden_dim, embedding_dim, output_dim):
            super().__init__()

            self.no_layers = no_layers
            self.hidden_dim = hidden_dim
            self.output_dim = output_dim

            # Embedding Layer
            self.embedding = nn.Embedding(vocab_size, embedding_dim)

            # LSTM Layers
            self.lstm = nn.LSTM(
                input_size=embedding_dim,
                hidden_size=hidden_dim,
                num_layers=no_layers,
                batch_first=True,
            )

            # Dropout to reduce overfitting
            self.dropout = nn.Dropout(0.5)

            # Fully Connected Layers with BatchNorm and Dropout
            self.fc1 = nn.Linear(hidden_dim, hidden_dim)  # Reduce size to decrease complexity
            self.bn1 = nn.BatchNorm1d(hidden_dim)  # BatchNorm
            self.fc2 = nn.Linear(hidden_dim, hidden_dim)  # Further reduction
            self.bn2 = nn.BatchNorm1d(hidden_dim)
            self.output = nn.Linear(hidden_dim, output_dim)  # Final output layer

        def forward(self, x, hidden):
            # Embedding and LSTM
            x = self.embedding(x)
            x, hidden = self.lstm(x, hidden)
            x = x[:, -1, :]  # Use last output for classification
            x = self.dropout(x)  # Apply dropout

            # Fully Connected Layers with ReLU, BatchNorm, and Dropout
            x = F.relu(self.fc1(x))
            x = self.bn1(x)  # Apply batch normalization
            x = F.dropout(x, 0.5)  # Apply dropout
            
            x = F.relu(self.fc2(x))
            x = self.bn2(x)
            x = F.dropout(x, 0.5)  # Additional dropout for regularization
            
            return self.output(x), hidden  # Output layer

        def init_hidden(self, batch_size):
            # Initialize hidden states
            h0 = torch.zeros((self.no_layers, batch_size, self.hidden_dim)).to(device)
            c0 = torch.zeros((self.no_layers, batch_size, self.hidden_dim)).to(device)
            hidden = (h0, c0)
            return hidden

    # Create the model with modified layer dimensions
    net = TweetDisasterRNN(
        no_layers=3,
        hidden_dim=256,
        output_dim=1,
        embedding_dim=128,
        vocab_size=len(vocab) + 1,
    )

    # Loss function and optimizer with L2 regularization
    lossfun = nn.BCEWithLogitsLoss()  # Binary classification
    optimizer = torch.optim.Adam(net.parameters(), lr=0.0001, weight_decay=0.01)  # L2 regularization
    
    return net, lossfun, optimizer
上述代码中,以下两个全连接层
# Fully Connected Layers with BatchNorm and Dropout
            self.fc1 = nn.Linear(hidden_dim, hidden_dim)  # Reduce size to decrease complexity
            self.bn1 = nn.BatchNorm1d(hidden_dim)  # BatchNorm
            self.fc2 = nn.Linear(hidden_dim, hidden_dim)  # Further reduction
            self.bn2 = nn.BatchNorm1d(hidden_dim)
            self.output = nn.Linear(hidden_dim, output_dim)  # Final output layer
 # Fully Connected Layers with ReLU, BatchNorm, and Dropout
            x = F.relu(self.fc1(x))
            x = self.bn1(x)  # Apply batch normalization
            x = F.dropout(x, 0.5)  # Apply dropout
            
            x = F.relu(self.fc2(x))
            x = self.bn2(x)
            x = F.dropout(x, 0.5)  # Additional dropout for regularization
有什么关系
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2024-10-15 15:16:58 | 显示全部楼层
看起来你正在实现一个用于文本情感分析的LSTM模型。以下是一些关于模型架构的建议和解释:

1. **Embedding Layer**:
   - 用于将输入的词索引转换为词向量,`vocab_size` 是词汇表的大小,`embedding_dim` 是词向量的维度。
   
2. **LSTM Layer**:
   - 由 `nn.LSTM` 实现的层,用于捕捉序列数据的时间依赖特性。`hidden_dim` 决定了隐藏状态的维度,`no_layers` 表示堆叠的LSTM层数。
   
3. **Dropout Layer**:
   - 用于在训练期间防止过拟合,通过丢弃一定比例的神经元输出。

4. **Batch Normalization and Fully Connected Layers**:
   - `BatchNorm1d` 是对特征维度进行标准化。全连接层用于将特征转化为最终的输出。
   - 设计了两层线性层(`fc1` 和 `fc2`)用于减少维度和复杂性。
   - 使用 ReLU 激活函数和 Dropout 来增加非线性和减少过拟合。

5. **输出层**:
   - 最后一层将隐藏状态映射到输出维度,用于预测情感类别(正面、负面等)。

6. **初始化隐藏状态**:
   - `init_hidden` 函数是用来初始化 LSTM 的隐藏状态和记忆单元的,通常使用零初始化可以在开始训练时多次尝试。

需要注意以下几点:
- `x = x` 这一行的注释提到“Use last output for classification”,但实际上,应该将最后一个时间步的输出提取出来以用于分类(例如 `x = x[:, -1, :]`)。
- 在 `forward` 函数中,应该确保在 BatchNorm 层之前应用 Dropout。

请确保在 `forward` 方法中适当地使用最后步骤的 LSTM 输出,并在完整的代码中完成 `init_hidden` 函数,以确保正确初始化 LSTM 的隐藏状态。根据需求调整 `output_dim` 以适配特定的情感类别数量。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-10-15 15:36:41 | 显示全部楼层
FishC 发表于 2024-10-15 15:16
看起来你正在实现一个用于文本情感分析的LSTM模型。以下是一些关于模型架构的建议和解释:

1. **Embeddi ...

应该将最后一个时间步的输出提取出来以用于分类,代码中为x = x[:, -1, :],怎么理解
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-10-15 15:36:51 | 显示全部楼层
Terence888 发表于 2024-10-15 15:36
应该将最后一个时间步的输出提取出来以用于分类,代码 ...


在LSTM模型中,通常你关心的是序列的最后一个时间步的输出,因为它总结了整个输入序列的信息,用于分类时特别重要。

在你的代码中,`x = x`这行没有完成这项任务。通常,我们需要从LSTM的输出中提取最后一个时间步的隐藏状态。假设`x`的形状是`(batch_size, seq_len, hidden_dim)`,那么可以用如下方式得到最后一个时间步的输出:
x = x[:, -1, :]  # 选择序列中的最后一个时间步

这行代码的作用是从每个批次中提取最后一个时间步对应的隐藏状态,从而为后续的全连接层提供输入。这样做有助于综合使用整个序列的上下文信息进行后续的分类或回归任务。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-10-16 22:07:10 | 显示全部楼层
FishC 发表于 2024-10-15 15:36
在LSTM模型中,通常你关心的是序列的最后一个时间步的输出,因为它总结了整个输入序列的信息,用于分类 ...
cm = confusion_matrix(yTrue, yPred)

plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, cmap=cmap, fmt='g')
plt.xlabel('Predicted')
plt.ylabel('Actual')
plt.title('Confusion Matrix')
plt.show()
绘制混淆矩阵热力图代码如上,其中
sns.heatmap(cm, annot=True, cmap=cmap, fmt='g')
这个函数的参数是什么意思
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-10-16 22:14:42 | 显示全部楼层
Terence888 发表于 2024-10-16 22:07
cm = confusion_matrix(yTrue, yPred)

plt.figure( ...


`sns.heatmap()` 是 Seaborn 库用于绘制热力图的函数,其中各个参数的含义如下:

- `cm`: 这是传入的混淆矩阵数据,一般是一个二维数组或矩阵,用于指定绘制热力图的数据源。

- `annot=True`: 这个参数表示在热力图每个方格内显示数值,`True` 表示会使用 `cm` 中的数值作为标注。

- `cmap`: 这用于设置热力图的颜色映射(colormap)。`cmap` 是一个字符串或 `matplotlib` 的 `Colormap` 对象,常用的有 `'viridis'`, `'coolwarm'`, `'Blues'` 等。如果你未提前定义 `cmap`,请确保它是一个有效的 colormap。

- `fmt='g'`: 决定标签的格式。`'g'` 代表常规格式(例如整数或浮点数)。如果你想要显示浮点数并控制小数位数,可以用像 `'0.2f'` 这样的格式。

这些参数的结合使得我们可以用直观的方式展示混淆矩阵,分析分类模型的性能。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-10-16 22:49:43 | 显示全部楼层
FishC 发表于 2024-10-16 22:14
`sns.heatmap()` 是 Seaborn 库用于绘制热力图的函数,其中各个参数的含义如下:

- `cm`: 这是传入 ...
def trainTheNet():
    clip = 5
    epochs = 100
    
    net, lossfun, optimizer = makeTheNet()
    net.to(device)
    
    trainAcc = []
    trainLoss = []
    devAcc = []
    devLoss = []
    
    yTrue, yPred = [], []
    
    for epochi in tqdm(range(epochs)):
        batchAcc = []
        batchLoss = []
        
        h = net.init_hidden(batch_size)
        
        net.train()#训练模型时
        
        for X, y in train_dataloader:
            X, y = X.to(device, dtype=torch.int), y.to(device)
            h = tuple([each.data for each in h])
            yHat, h = net(X, h)
            yHat = yHat.squeeze()
            loss = lossfun(yHat, y)
            
            batchAcc.append(100*torch.mean(((yHat.cpu() > .0) == y.cpu()).float()))
            batchLoss.append(loss.item())
            
            optimizer.zero_grad()
            loss.backward()
            
            nn.utils.clip_grad_norm_(net.parameters(), clip)#梯度裁剪,增强梯度下降的稳定性,防止梯度爆炸或梯度消失
            optimizer.step()
        
        trainAcc.append(np.mean(batchAcc))
        trainLoss.append(np.mean(batchLoss))
        
        net.eval()#训练模型时需要dropout和batch_normalization,预测时不需要,因此需要使用train()/eval()加以区分,eval为测试模型时使用
        
        with torch.no_grad():
            
            h = net.init_hidden(batch_size)
        
            batchAcc = []
            batchLoss = []

            for X, y in dev_dataloader:
                X, y = X.to(device, dtype=torch.int), y.to(device)
                h = tuple([each.data for each in h])
                yHat, h = net(X, h)
                yHat = yHat.squeeze()
                loss = lossfun(yHat, y)
                
                yPred.extend(yHat.cpu() > .0)
                yTrue.extend(y.cpu())

                batchAcc.append(100*torch.mean(((yHat.cpu() > .0) == y.cpu()).float()))
                batchLoss.append(loss.item())

            devAcc.append(np.mean(batchAcc))
            devLoss.append(np.mean(batchLoss))
        
    return trainAcc, devAcc, trainLoss, devLoss, net, yTrue, yPred
训练模型的代码如上,其中
h = tuple([each.data for each in h])
yHat = yHat.squeeze()
分别表示什么意思
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-10-16 22:54:17 | 显示全部楼层
Terence888 发表于 2024-10-16 22:49
def trainTheNet():
    clip = 5
    epochs = 100 ...


在您的代码中,以下两行的作用分别是:

1. **`h = tuple()`**:
   - 这行代码用于初始化一个空的元组 `h`。这个变量是用于存储LSTM的隐藏状态。通常,LSTM的隐藏状态 `h` 包含两个部分:`(h_n, c_n)`,分别是隐藏状态和细胞状态。空元组表示重置隐藏状态,因为每个新的批次通常都需要重新初始化。
   - 如果你希望在每个小批次的开始重置隐藏状态,这行可以保留,但要注意初始化的逻辑。这也可能是一个代码片段不完整的地方——LSTM的初始化应该用 `h = net.init_hidden(batch_size)` 这种方式来保持一致,而不是直接赋值为空元组。

2. **`yHat = yHat.squeeze()`**:
   - `squeeze()` 方法用于去除 `yHat` 中尺寸为1的维度。这在处理输出张量时很常用,因为LSTM的输出通常为形状 `(batch_size, seq_len, num_directions * hidden_dim)`。在二分类任务中,我们只对一维输出感兴趣,所以需要 `squeeze` 去掉多余的维度。
   - 如果 `yHat` 的形状是 `(batch_size, 1)`,`squeeze()` 会将其变为 `(batch_size,)`。这是为了确保损失函数计算时形状正确(即与 `y` 匹配)。

这些步骤是训练和评估中处理张量形状和初始化状态的重要部分。确保 `h` 在代码执行流中的正确设置和传递尤为重要,以便LSTM的隐藏状态在不同批次中按预期工作。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-10-17 11:28:33 | 显示全部楼层
FishC 发表于 2024-10-16 22:54
在您的代码中,以下两行的作用分别是:

1. **`h = tuple()`**:
def init_hidden(self, batch_size):
            # Initialize hidden states
            h0 = torch.zeros((self.no_layers, batch_size, self.hidden_dim)).to(device)
            c0 = torch.zeros((self.no_layers, batch_size, self.hidden_dim)).to(device)
            hidden = (h0, c0)
            
            return hidden
初始化隐藏状态的代码如上,为什么要把隐藏状态和记忆细胞放进一个元组
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-10-17 11:29:41 | 显示全部楼层
Terence888 发表于 2024-10-17 11:28
def init_hidden(self, batch_size):
            #  ...


在 LSTM 中,隐藏状态和记忆细胞共同驱动序列信息的存储和更新过程。具体原因如下:

1. **LSTM 的结构**:
   - LSTM(长短时记忆网络)由三个状态组成:输入、输出和保持信息的能力。相比普通的 RNN,它增加了细胞状态(cell state)用于长期信息的维持。
   - 其中隐藏状态 `h_t` 表示从当前要输出的部分,而记忆细胞 `c_t` 用于传递整个序列过程中重要的信息,不被频繁重置。

2. **元组的使用**:
   - `hidden = (h0, c0)` 将隐藏状态和细胞状态放入一个元组,这是因为 PyTorch 的 `nn.LSTM` 接受的输入需要同时包括隐藏状态和细胞状态。
   - 这对(h,c)元组使得在前向传播时显式传递和返回这两个状态更便捷,并区分它们的逻辑角色。

由于 LSTM 的更新过程中同时依赖于这两种状态,因此以元组形式传递可以保持代码简洁,提高可读性,且确保所有状态在传递给 LSTM 过程中不会搞混。这样在每个时间步进行更新和在下一批次初始化时,容易管理它们的状态和转移。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-23 15:59

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表