多模型特征融合分类模型:基于CNN和BiGRU的文本分类实战
多模型特征融合分类模型:基于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,以进一步提升模型性能。
- 可以将模型应用于其他文本分类任务,例如情感分析、主题分类等。
- 可以研究如何提高模型的可解释性,以便更好地理解模型的预测结果。
原文地址: https://www.cveoy.top/t/topic/lGO3 著作权归作者所有。请勿转载和采集!