TextCNN模型结构
参考文章
卷积操作在文本上的应用
数据源定义
-
数据为 128 * 30 的文本转索引的数据 即 128行, 单个句子为30个词的文本
-
即 batch_size = 128 和 max_seq_len = 30
数据embedding
- num_embeddings : 文本中所有不重复单词的个数
- embedding层会生成 num_embeddings * embedding_dim 的二维文本向量矩阵
self.embedding = nn.Embedding(num_embeddings=num_embeddings, embedding_dim=64)
- 最终经过embedding层数据会变成 [batch_size, max_seq_len, embedding_dim] => [128, 30, 64]
卷积层操作
- 注意 : 卷积层的输入是 单个句子的长度 max_seq_len
- 卷积层输出out_channels为32, 卷积核kernel_size大小为3, 步长stride为2
self.conv1d = nn.Conv1d(in_channels=30, out_channels=32, kernel_size=3, stride=2)
- 卷积核输出大小 : [batch_size, out_channels, out]
- 计算公式如下 :
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲ L_{\text {out …
-
L i n L_in Lin 是 embedding_dim, 每个单词的向量维度
-
padding 是需要对数据填充的大小, 默认为 0
-
dilation : 默认为 1 目前不知道是干嘛的
-
输入数据 [128, 30, 64] 经过卷积层后 输出结果为 [128, 32, 31]
池化层操作
- 定义池化层卷积核大小为31, 步长为 1
- 池化层的作用是缩减特征, 取卷积核范围内最大的值作为新的特征
- MaxPool1d 一维池化, 对应卷积核就是 kernel_size * 1, 如果是 MaxPool2d 就是 kernel_size * kernel_size
self.pool = nn.MaxPool1d(kernel_size=31, stride=1)
- 最终池化结果为 [128, 32, 1]
全连接层
完整代码
# -*- coding: utf-8 -*-
# @Time : 2021/11/2 15:21
# @Author : 王天赐
# @Email : 15565946702@163.com
# @File : classification_model.py
# @Software: PyCharm
import torch
from torch import nn
import numpy as np
from torch.nn import functional
from torch.utils.data import DataLoader
from twitter_datasets import TwitterDatasets
class TextCNN(nn.Module):
def __init__(self, num_embeddings):
super(TextCNN, self).__init__()
# Embedding的输入是 : [batch_size, seq_len] = 128 * 30
# 输出 : [batch_size, seq_len, embedding_dim]
self.embedding = nn.Embedding(num_embeddings=num_embeddings, embedding_dim=64)
# in_channels 这个是单个句子的长度 seq_len
# in_channels : 30 , embedded = [128, 30, 64]
# out_channels : [128, 32, 62]
self.conv1d = nn.Conv1d(in_channels=30, out_channels=32, kernel_size=3, stride=2)
# kernel_size 对应的是最后一维, 获取每一行的特征中的最大值
self.pool = nn.MaxPool1d(kernel_size=31, stride=1)
self.decoder = nn.Linear(32, 2)
def forward(self, inputs):
# inputs : [128, 30] , embedded : [128, 30, 64]
embedded = self.embedding(inputs) # [batch_size, seq_len, embedding_dim]
# 输出通道宽 : embedding_dim - kernel_size + 1
# 输出 : [batch_size, out_channels, L_out]
# L_out = { (embedding_dim + 2 * padding(默认是0) - dilation(默认是1) * (kernel_size - 1) - 1 } / stride + 1
# out.size = (64 + 2 * 0 - 1 * (3 - 1) - 1) / 2 + 1 = 31
out = self.conv1d(embedded)
# torch.Size([128, 32, 31])
print(out.size())
out = self.pool(out)
# torch.Size([128, 32, 1])
print(out.size())
# 去除最后一列
out = torch.squeeze(out, -1)
# torch.Size([128, 32])
print(out.size())
out = self.decoder(out)
# [128, 32] * [32, 2] => [128, 2]
# torch.Size([128, 2])
print(out.size())
return out
if __name__ == '__main__':
datasets = TwitterDatasets()
train_loader = DataLoader(datasets, batch_size=128, shuffle=True)
model = TextCNN(len(datasets.vocab_dict))
for i, batch in enumerate(train_loader):
out = model.forward(batch[1])
print(out.size())