多模型特征融合分类模型:基于CNN和BiGRU的文本分类实战

1. 概述

本文将介绍一个使用CNN和BiGRU模型并行提取特征,并进行特征融合的文本分类方法。该方法利用了CNN的局部特征提取能力和BiGRU的序列信息捕捉能力,从而提高了分类模型的性能。我们将使用PyTorch框架实现该模型,并提供完整的代码示例。

2. 数据集

数据集存储在三个txt文件夹中:训练集、验证集和测试集。每个样本由23个特征值和一个类别标签组成,标签共有8个类别。样本数据格式如下:

7,7,183,233,10,10,3,10,3,10,0,25,21,0,0,2,78,2,1,0,0,86.6685638427734,1.25,4
7,7,183,233,10,10,3,10,3,10,0,25,21,90,80,20,10,2,1,0,0,86.4980087280273,1.10,0
7,0,183,0,9,0,3,10,3,0,0,25,123,90,80,20,10,0,1,0,1,0,1.00,7

3. 模型结构

该模型由四个部分组成:

  • CNN模型:用于提取局部特征。
  • BiGRU模型:用于提取序列特征。
  • 特征融合模型:用于将CNN和BiGRU提取的特征进行融合。
  • 分类模型:用于对融合后的特征进行分类。

4. 代码实现

import torch
import torch.nn as nn
import torch.optim as optim
import torch.utils.data as data
from torch.nn.utils.rnn import pad_sequence
from sklearn.model_selection import train_test_split
import numpy as np

# 定义CNN模型
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Conv2d(1, 16, kernel_size=3, stride=1, padding=1)
        self.relu = nn.ReLU(inplace=True)
        self.maxpool = nn.MaxPool2d(kernel_size=2, stride=2)
        self.conv2 = nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1)
        self.fc = nn.Linear(32 * 5 * 5, 64)

    def forward(self, x):
        x = x.unsqueeze(1)  # 转换为二维数据
        x = self.conv1(x)
        x = self.relu(x)
        x = self.maxpool(x)
        x = self.conv2(x)
        x = self.relu(x)
        x = self.maxpool(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x

# 定义BiGRU模型
class BiGRU(nn.Module):
    def __init__(self, input_size, hidden_size, num_layers):
        super(BiGRU, self).__init__()
        self.gru = nn.GRU(input_size, hidden_size, num_layers, bidirectional=True, batch_first=True)
        self.fc = nn.Linear(hidden_size * 2, 64)

    def forward(self, x):
        _, h = self.gru(x)
        x = torch.cat((h[-2, :, :], h[-1, :, :]), dim=1)
        x = self.fc(x)
        return x

# 定义特征融合模型
class FeatureFusion(nn.Module):
    def __init__(self):
        super(FeatureFusion, self).__init__()
        self.fc = nn.Linear(128, 64)

    def forward(self, cnn_features, gru_features):
        x = torch.cat((cnn_features, gru_features), dim=1)
        x = self.fc(x)
        return x

# 定义分类模型
class Classifier(nn.Module):
    def __init__(self):
        super(Classifier, self).__init__()
        self.fc1 = nn.Linear(64, 32)
        self.fc2 = nn.Linear(32, 16)
        self.fc3 = nn.Linear(16, 8)
        self.softmax = nn.Softmax(dim=1)

    def forward(self, x):
        x = self.fc1(x)
        x = self.fc2(x)
        x = self.fc3(x)
        x = self.softmax(x)
        return x

# 加载数据集
def load_dataset(file_path):
    with open(file_path, 'r') as f:
        lines = f.readlines()
    dataset = []
    for line in lines:
        sample = line.strip().split(',')
        features = [float(x) for x in sample[:-1]]
        label = int(sample[-1])
        dataset.append((features, label))
    return dataset

# 数据预处理
def preprocess(dataset):
    features, labels = zip(*dataset)
    features = np.array(features)
    labels = np.array(labels)
    return features, labels

# 数据集划分
def split_dataset(features, labels, test_size=0.2, valid_size=0.2):
    X_train, X_test, y_train, y_test = train_test_split(features, labels, test_size=test_size, random_state=42)
    X_train, X_valid, y_train, y_valid = train_test_split(X_train, y_train, test_size=valid_size, random_state=42)
    return X_train, y_train, X_valid, y_valid, X_test, y_test

# 数据集处理
def process_dataset(features, labels):
    features = torch.tensor(features, dtype=torch.float32)
    labels = torch.tensor(labels, dtype=torch.long)
    return features, labels

# 数据集加载器
def create_dataloader(features, labels, batch_size):
    dataset = data.TensorDataset(features, labels)
    dataloader = data.DataLoader(dataset, batch_size=batch_size, shuffle=True)
    return dataloader

# 训练函数
def train(model, dataloader, criterion, optimizer):
    model.train()
    total_loss = 0.0
    correct = 0
    total = 0
    for inputs, labels in dataloader:
        optimizer.zero_grad()
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        total_loss += loss.item()
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
    accuracy = correct / total
    return total_loss, accuracy

# 验证函数
def validate(model, dataloader, criterion):
    model.eval()
    total_loss = 0.0
    correct = 0
    total = 0
    with torch.no_grad():
        for inputs, labels in dataloader:
            outputs = model(inputs)
            loss = criterion(outputs, labels)
            total_loss += loss.item()
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    accuracy = correct / total
    return total_loss, accuracy

# 测试函数
def test(model, dataloader):
    model.eval()
    correct = 0
    total = 0
    with torch.no_grad():
        for inputs, labels in dataloader:
            outputs = model(inputs)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    accuracy = correct / total
    return accuracy

# 主函数
def main():
    # 加载数据集
    train_dataset = load_dataset('训练集.txt')
    valid_dataset = load_dataset('验证集.txt')
    test_dataset = load_dataset('测试集.txt')

    # 数据预处理
    train_features, train_labels = preprocess(train_dataset)
    valid_features, valid_labels = preprocess(valid_dataset)
    test_features, test_labels = preprocess(test_dataset)

    # 数据集划分
    X_train, y_train, X_valid, y_valid, X_test, y_test = split_dataset(train_features, train_labels)

    # 数据集处理
    X_train, y_train = process_dataset(X_train, y_train)
    X_valid, y_valid = process_dataset(X_valid, y_valid)
    X_test, y_test = process_dataset(X_test, y_test)

    # 创建数据加载器
    train_dataloader = create_dataloader(X_train, y_train, batch_size=64)
    valid_dataloader = create_dataloader(X_valid, y_valid, batch_size=64)
    test_dataloader = create_dataloader(X_test, y_test, batch_size=64)

    # 定义模型
    cnn_model = CNN()
    gru_model = BiGRU(input_size=23, hidden_size=32, num_layers=2)
    fusion_model = FeatureFusion()
    classifier_model = Classifier()

    # 定义损失函数和优化器
    criterion = nn.CrossEntropyLoss()
    optimizer = optim.Adam(list(cnn_model.parameters()) + list(gru_model.parameters()) + 
                           list(fusion_model.parameters()) + list(classifier_model.parameters()), lr=0.001)

    # 训练模型
    best_valid_loss = float('inf')
    for epoch in range(10):
        train_loss, train_acc = train(cnn_model, train_dataloader, criterion, optimizer)
        valid_loss, valid_acc = validate(cnn_model, valid_dataloader, criterion)
        print(f"Epoch: {epoch+1}")
        print(f"Train Loss: {train_loss:.4f} | Train Accuracy: {train_acc:.4f}")
        print(f"Valid Loss: {valid_loss:.4f} | Valid Accuracy: {valid_acc:.4f}")
        if valid_loss < best_valid_loss:
            best_valid_loss = valid_loss
            torch.save(cnn_model.state_dict(), 'best_model.pt')

    # 加载最好的模型
    cnn_model.load_state_dict(torch.load('best_model.pt'))

    # 提取特征
    cnn_features = []
    gru_features = []
    with torch.no_grad():
        for inputs, _ in train_dataloader:
            cnn_output = cnn_model(inputs)
            gru_output = gru_model(inputs)
            cnn_features.append(cnn_output)
            gru_features.append(gru_output)
    cnn_features = torch.cat(cnn_features, dim=0)
    gru_features = torch.cat(gru_features, dim=0)

    # 特征融合
    fused_features = fusion_model(cnn_features, gru_features)

    # 训练分类器
    classifier_optimizer = optim.Adam(classifier_model.parameters(), lr=0.001)
    classifier_criterion = nn.CrossEntropyLoss()

    for epoch in range(10):
        classifier_optimizer.zero_grad()
        outputs = classifier_model(fused_features)
        loss = classifier_criterion(outputs, y_train)
        loss.backward()
        classifier_optimizer.step()

    # 测试模型
    accuracy = test(classifier_model, test_dataloader)
    print(f"Test Accuracy: {accuracy:.4f}")

if __name__ == '__main__':
    main()

5. 总结

本文介绍了使用CNN和BiGRU模型并行提取特征,并进行特征融合的文本分类方法。该方法利用了两种模型的优势,并通过特征融合进一步提升了分类性能。代码实现部分提供了完整示例,方便读者理解和实践。

6. 注意事项

  • 数据集需要进行预处理,包括特征提取、标签转换等。
  • 模型参数需要根据具体数据集进行调整。
  • 训练过程需要进行验证,以选择最佳的模型参数。
  • 测试集用于评估模型的最终性能。

7. 未来展望

  • 可以尝试使用其他特征提取模型,例如Transformer,以进一步提升模型性能。
  • 可以将模型应用于其他文本分类任务,例如情感分析、主题分类等。
  • 可以研究如何提高模型的可解释性,以便更好地理解模型的预测结果。
多模型特征融合分类模型:基于CNN和BiGRU的文本分类实战

原文地址: https://www.cveoy.top/t/topic/lGO3 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录