TextGCN Graph Convolutional Networks for Text Classification

https://arxiv.org/abs/1809.05679

1.build a single text graph for a corpus based on word co-occurrence and document word relations,

2.then learn a Text Graph Convolutional Network (Text GCN) for the corpus. Our Text GCN is initialized with one-hot representation for word and document, it then jointly learns the embeddings for both words and documents, as supervised by the known class labels for documents.

HIERARCHICAL TRANSFORMERS FOR LONG DOCUMENT CLASSIFICATION

原版BERT的最大输入为512,为了使得BERT能解决超长文本的问题,作者在finetune阶段提出了两种策略来弥补这个问题,即利用BERT+LSTM或者BERT+transformer。

核心步骤:

1.split the input sequence into segments of a fixed size with overlap.

2.For each of these segments, we obtain H or P from BERT model.

3.We then stack these segment-level representations into a sequence, which serves as input to a small (100-dimensional) LSTM layer.//replacing the LSTM recurrent layer in favor of a small Transformer model

4.Finally, we use two fully connected layers with ReLU (30-dimensional) and softmax (the same dimensionality as the number of classes) activations to obtain the final predictions.

TextCNN TextRNN TextRCNN

1.TextCNN (Convolutional Neural Networks for Sentence Classification)

原文 https://arxiv.org/abs/1408.5882

调参论文 https://arxiv.org/abs/1510.03820

模型的整体结构如上所示。Feature Map是输入图像经过神经网络卷积产生的结果,filter是卷积核。

输入表示:

假设输入文本的长度为$n$,对于长度不够的需要做padding,任意一个单词可以用一个$k$维的向量表示,即$X_i \in \mathbb{R}^{k}$,那么一个句子可以表示为

其中$\oplus$是向量拼接操作,$X_{1:n} \in \mathbb{R}^{nk\times 1}$。

卷积

对于某个滑窗$X_{i,i+h-1}=\{X_i,X_{i+1},…,X_{i+h-1}\}$经过某个卷积核$W_j$可得

其中$f=tanh(\cdot)$,$W_j\in \mathbb{R}^{ 1\times hk},c_{i,j} $是标量

假设卷积通道数为$m$,在NLP中,卷积滑动步伐$k=1$,那么经过卷积层后得到的完整的特征矩阵为

其中$C \in \mathbb{R}^{(n-h+1)\times m}$

maxpooling

全连接

然后将$\hat{C}$接个全连接,就可以做分类或者回归任务了。

2.TextRNN (Recurrent Neural Network for Text Classification with Multi-Task Learning)

原文 https://www.ijcai.org/Proceedings/16/Papers/408.pdf

该文的场景为Recurrent Neural Network for Text Classification with Multi-Task Learning,就是论文的题目。文中给出了三种结构,如上图所示,图中的RNN单元为LSTM。

Model-I: Uniform-Layer Architecture

对于任务$m$,输入$\hat X_t$包含两个部分

其中$X_{t}^{(m)}$表示特定任务的词向量,$X_{t}^{(s)}$表示共享的词向量,$\oplus$表示向量拼接的操作。

Model-II: Coupled-Layer Architecture

Model-III: Shared-Layer Architecture

3.TextRCNN(Recurrent Convolutional Neural Networks for Text Classification)

原文 https://www.deeplearningitalia.com/wp-content/uploads/2018/03/Recurrent-Convolutional-Neural-Networks-for-Text-Classification.pdf

整体结构如上图所示,解释一下为啥叫RCNN,一般的 CNN 网络,都是卷积层 + 池化层,这里是将卷积层换成了双向 RNN,所以结果是,双向 RNN + 池化层。作者原话为:From the perspective of convolutional neural networks, the recurrent structure we previously mentioned is the convolutional layer.

词语表示

对于一个词语$w_i$,可以用一个三元组表示为

其中$e(w_i)$表示$w_i$的词向量,$c_l(w_i)$表示$w_i$句子左边的内容的向量表示,$c_r(w_i)$表示$w_i$句子右边的内容的向量表示,用式子表示如下

然后将$x_i$经过全连接得到$y_i^{(2)}$,$y_i^{(2)}$is a latent semantic vector

语句表示

获取众多的词语表示后,通过max-pooling得到句子表示

然后接全连接和softmax

参考

https://www.cnblogs.com/wangduo/p/6773601.html

fasttext

1、文本分类

1.1 n-gram

由于Bag of words不考虑词语的顺序,因此引入bag of n-gram。针对英文,词内的是char n-gram,用于词向量;词之间的是word n-gram,用于分类;对于中文,存在词粒度和字粒度。

举个例子,句子A为”今天天气真不错”,这里以词粒度举例,先分词为[“今天”,”天气”,”真“,”不错“]

uni-gram:今天 天气 真 不错

2-gram为:今天/天气 天气/真 真/不错

3-gram为:今天/天气/真 天气/真/不错

由于n-gram的量远比word大的多,完全存下所有的n-gram也不现实。FastText采用了hashing trick的方式,如下图所示:

用哈希的方式既能保证查找时O(1)的效率,又可能把内存消耗控制在O(buckets * dim)范围内。不过这种方法潜在的问题是存在哈希冲突,不同的n-gram可能会共享同一个embedding。如果buckets取的足够大,这种影响会很小。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
def build_dataset(config, ues_word):
if ues_word:
tokenizer = lambda x: x.split(' ') # word-level
else:
tokenizer = lambda x: [y for y in x] # char-level
if os.path.exists(config.vocab_path):
vocab = pkl.load(open(config.vocab_path, 'rb'))
else:
vocab = build_vocab(config.train_path, tokenizer=tokenizer, max_size=MAX_VOCAB_SIZE, min_freq=1)
pkl.dump(vocab, open(config.vocab_path, 'wb'))
print(f"Vocab size: {len(vocab)}")

def biGramHash(sequence, t, buckets):
t1 = sequence[t - 1] if t - 1 >= 0 else 0
return (t1 * 14918087) % buckets

def triGramHash(sequence, t, buckets):
t1 = sequence[t - 1] if t - 1 >= 0 else 0
t2 = sequence[t - 2] if t - 2 >= 0 else 0
return (t2 * 14918087 * 18408749 + t1 * 14918087) % buckets

def load_dataset(path, pad_size=32):
contents = []
with open(path, 'r', encoding='UTF-8') as f:
for line in tqdm(f):
lin = line.strip()
if not lin:
continue
content, label = lin.split('\t')
words_line = []
token = tokenizer(content)
seq_len = len(token)
if pad_size:
if len(token) < pad_size:
token.extend([PAD] * (pad_size - len(token)))
else:
token = token[:pad_size]
seq_len = pad_size
# word to id
for word in token:
words_line.append(vocab.get(word, vocab.get(UNK)))

# fasttext ngram
buckets = config.n_gram_vocab
bigram = []
trigram = []
# ------ngram------
for i in range(pad_size):
bigram.append(biGramHash(words_line, i, buckets))
trigram.append(triGramHash(words_line, i, buckets))
# -----------------
contents.append((words_line, int(label), seq_len, bigram, trigram))
return contents # [([...], 0), ([...], 1), ...]

train = load_dataset(config.train_path, config.pad_size)
dev = load_dataset(config.dev_path, config.pad_size)
test = load_dataset(config.test_path, config.pad_size)
return vocab, train, dev, test

1.2 网络结构

fasttext

模型结构上word2vec的cbow模型很像

输入层:举个例子,输入文本”今天天气真不错”,词粒度的2-gram为

中间层:线形层+relu作为激活函数

输出层:为简单的线形层

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Model(nn.Module):
def __init__(self, config):
super(Model, self).__init__()
if config.embedding_pretrained is not None:
self.embedding = nn.Embedding.from_pretrained(config.embedding_pretrained, freeze=False)
else:
self.embedding = nn.Embedding(config.n_vocab, config.embed, padding_idx=config.n_vocab - 1)
self.embedding_ngram2 = nn.Embedding(config.n_gram_vocab, config.embed)
self.embedding_ngram3 = nn.Embedding(config.n_gram_vocab, config.embed)
self.dropout = nn.Dropout(config.dropout)
self.fc1 = nn.Linear(config.embed * 3, config.hidden_size)
# self.dropout2 = nn.Dropout(config.dropout)
self.fc2 = nn.Linear(config.hidden_size, config.num_classes)

def forward(self, x):

out_word = self.embedding(x[0])
out_bigram = self.embedding_ngram2(x[2])
out_trigram = self.embedding_ngram3(x[3])
out = torch.cat((out_word, out_bigram, out_trigram), -1)

out = out.mean(dim=1)
out = self.dropout(out)
out = self.fc1(out)
out = F.relu(out)
out = self.fc2(out)
return out

1.3 分层softmax

对于分类问题,神经网络的输出结果需要经过softmax将其转为概率分布后才可以利用交叉熵计算loss

由于普通softmax的计算效率比较低,计算效率为$O(Kd)$使用分层的softmax时间复杂度可以达到$dlogK$,$K$为分类的数量,$d$为向量的维度

1.3.1 普通softmax

假设输出为$Y_{pred}=[y_1,y_2,…,y_K]$,则$P_{y_i}$为

其中$y_i$的维度为$d$,从公式可以看出计算效率为$O(Kd)$

1.3.2 分层softmax

霍夫曼树可以参考 https://zhuanlan.zhihu.com/p/154356949

为什么要霍夫曼,普通的不行?

分层softmax核心思想为利用训练样本构建霍夫曼树,如下

fasttext

树的结构是根据不同类在样本中出现的频次构造的,即频次越大的节点距离根节点越近。$K$个不同的类组成所有的叶子节点,$K-1个$内部节点作为参数。从根节点到某个叶子节点$y_i$经过的节点和边形成一条路径,路径长度表示为 $L_{y_i}$,$n_{(y_i,j)}$表示路径上的节点,那么

从公式可以看出时间复杂度降低至$dlogK$。

以图中$y_2$为例:

从根节点走到叶子节点 $y_2$ ,实际上是在做了3次逻辑回归。

2.训练词向量

https://arxiv.org/abs/1607.04606

参考

https://arxiv.org/abs/1607.01759

https://zhuanlan.zhihu.com/p/32965521

https://blog.csdn.net/qq_27009517/article/details/80676022

http://alex.smola.org/papers/2009/Weinbergeretal09.pdf

https://arxiv.org/abs/1607.04606

fasttext工具 https://github.com/facebookresearch/fastText


:D 一言句子获取中...