当前位置:   article > 正文

NLP学习笔记(四):关于keras的Input层与embedding层全解析_tokenizer embedding layer input dim=4

tokenizer embedding layer input dim=4

这里解析的主要是关于NLP搭建网络中遇到的常见的一个关于维度的问题,为什么我们模型中Input layer的输出维度和embedding的维度明明看上去对不上,模型却能好好运行?搞懂了它能帮助我们更进一步的理解词向量。

一、一些预处理:

  1. ## 一、需要设置的值
  2. embed_size = 300 # 词向量维度
  3. max_features = 50000 # 字典内的单词数/特征数
  4. maxlen = 100 # 每句话的最大长度
  5. ## 二、填充缺失值
  6. train_X = train_df["question_text"].fillna("_na_").values
  7. ## 三、对句子进行分词
  8. tokenizer = Tokenizer(num_words=max_features)
  9. tokenizer.fit_on_texts(list(train_X))
  10. train_X = tokenizer.texts_to_sequences(train_X)
  11. ## 四、填充
  12. train_X = pad_sequences(train_X, maxlen=maxlen)
  13. ## 五、标签
  14. train_y = train_df['target'].values

首先对“三”和“四”进行解释,我们将训练集(语料)中出现频次最高的前50000个词选出,构成我们的字典(同时也是特征数),此时我们将每个句子分词,并用这个0~49999个下标对其进行表示,maxlen就是我们设置的最大句子长度(一般可以对所有句子的词数进行统计取平均值)不够的进行(往左)补零,多余的截断。于是我们的train_X就变成了:

其shape为。前面的数字是训练集中的句子数。

在此之前我们的训练数据是类似这样的:

二、搭建一个简单的网络

  1. inp = Input(shape=(maxlen,))
  2. x = Embedding(max_features, embed_size)(inp)
  3. x = Bidirectional(CuDNNGRU(64, return_sequences=True))(x)
  4. x = GlobalMaxPool1D()(x)
  5. x = Dense(16, activation="relu")(x)
  6. x = Dropout(0.1)(x)
  7. x = Dense(1, activation="sigmoid")(x)
  8. model = Model(inputs=inp, outputs=x)
  9. model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
  10. print(model.summary())

我们搭建的网络没有运用到预训练的词向量。这里我们仅仅关注前两层,也就是Input层Embedding层

我们看一看网络参数:

  1. _________________________________________________________________
  2. Layer (type) Output Shape Param #
  3. =================================================================
  4. input_2 (InputLayer) (None, 100) 0
  5. _________________________________________________________________
  6. embedding_2 (Embedding) (None, 100, 300) 15000000
  7. _________________________________________________________________
  8. bidirectional_2 (Bidirection (None, 100, 128) 140544
  9. _________________________________________________________________
  10. global_max_pooling1d_1 (Glob (None, 128) 0
  11. _________________________________________________________________
  12. dense_1 (Dense) (None, 16) 2064
  13. _________________________________________________________________
  14. dropout_1 (Dropout) (None, 16) 0
  15. _________________________________________________________________
  16. dense_2 (Dense) (None, 1) 17
  17. =================================================================
  18. Total params: 15,142,625
  19. Trainable params: 15,142,625
  20. Non-trainable params: 0
  21. _________________________________________________________________
  22. None

其中的None指的是我们之后会设置的batch_size,这里不必理会。那么问题来了,这个(None, 100)是怎么通过我们的EmbeddingMatrix(50000, 300)得到这样一个维度(None, 100, 300)的???

聪明的你肯定已经发现了,我们的输入必须也得包含50000这样一个维度啊,不然怎么乘得掉呢?

这里将Input层的官方说明引出:

  • shape: A shape tuple (integer), not including the batch size. For instance, shape=(32,)indicates that the expected input will be batches of 32-dimensional vectors.

也就是说,不看batch_size的话,其实我们输入的是一个100维度的向量,正好对应了上面的maxlen对吧。

那我们embedding层的作用,是将正整数下标转换为具有固定大小的向量。

如[[4], [20]]->[[0.25,0.1], [0.6,-0.2]]。一定要注意到一个下标对应一个向量。

所以embedding层,其本质就是,我们用下标去寻找对应的映射!

为什么这么说,因为我们的每个词,都对应的是一个OneHot encoder,例如字典为50000的大小,单词Ant在其中下标为4,就是一个[0, 0, 0, 1, 0, 0, 0, 0...]维度为50000的这样的一个向量。

而我们的embedding_matrix有50000 x 300这么大,其中每一行都对应着字典中每个词对应的词向量!因为是onehot(假设还是Ant),所以用其去乘的话就相当于直接去找矩阵中第4行就完了(读者在草稿纸上一画就明晰了),这就是一个查表映射的过程。查找得到的向量就是我们之前设置的300这么大。

于是就可以解释了,为什么100个词输进去,得到的是(100, 300)这么大的一个矩阵,而加上batch_size后就是上面看到的(None, 100, 300),keras中没有使用乘法(虽然本质是乘法),而是直接使用的通过下标去查找映射,所以维度很容易让人迷惑!

三、关于词向量

我们在NLP的过程中可能会遇到很多预训练的embedding,比如word2vec啊,glove啊,fasttext啊。它们都是经过不同的语料以及不同的训练方法训练而成的,接着可能就有一些问题了:

  • 前面我们没有使用预训练的词向量,所以刚刚生成的Embedding矩阵是通过一些方法随机初始化的,在官方文档内有embeddings_initializer我们可以去设置其初始化的方法。
  • 我们如何训练词向量?这个就涉及到skip-gram、C-bow等建立词向量的模型了,这里就不过多阐述了,更详细的解释可见个人总结:自然语言处理 word2vec(skip-gram/CBOW以及优化算法Hierarchical Softmax和Negative Sampling)
  • 之前也说了,embedding的本质是一个字典,每一个词以及它对应的词向量。而往往会想,这么多词,怎么运用到自己的训练集上呢?答案是我们针对自己的训练集建立自己的字典,然后依据字典去提取我们需要的词向量进行了,然后就可以建立我们的词向量矩阵,因为是别人训练好的,所有一般不需要我们再训练,直接用就可以了。(最下我附上了一个比较完整的使用预训练的词向量的代码)比如将刚刚的第二层换为:
x = Embedding(max_features, embed_size, weights=[embedding_matrix], trainable = False)(inp)

 

PS : 附上一个比较完整的使用预训练的词向量的方法,里面没有提到的变量都和之前涉及的操作一样

  1. ### 得到语料中所有词向量
  2. EMBEDDING_FILE = '../glove.840B.300d/glove.840B.300d.txt'
  3. def get_coefs(word,*arr):
  4. return word, np.asarray(arr, dtype='float32')
  5. embeddings_index = dict(get_coefs(*o.split(" ")) for o in open(EMBEDDING_FILE, 'r', encoding = 'UTF-8'))
  6. all_embs = np.stack(embeddings_index.values())
  7. emb_mean,emb_std = all_embs.mean(), all_embs.std()
  8. embed_size = 300
  9. word_index = tokenizer.word_index
  10. ### 初始化矩阵以及通过字典去提取词向量构建矩阵
  11. num_words = min(max_features, len(word_index))
  12. embedding_matrix = np.random.normal(emb_mean, emb_std, (nb_words, embed_size))
  13. for word, i in word_index.items():
  14. if i >= max_features:
  15. continue
  16. embedding_vector = embeddings_index.get(word)
  17. if embedding_vector is not None:
  18. embedding_matrix[i] = embedding_vector
  19. ### 构建模型
  20. inp = Input(shape=(maxlen,))
  21. x = Embedding(max_features, embed_size, weights=[embedding_matrix], trainable = False)(inp)
  22. x = Bidirectional(CuDNNGRU(64, return_sequences=True))(x)
  23. x = GlobalMaxPool1D()(x)
  24. x = Dense(16, activation="relu")(x)
  25. x = Dropout(0.1)(x)
  26. x = Dense(1, activation="sigmoid")(x)
  27. model = Model(inputs=inp, outputs=x)
  28. model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
  29. print(model.summary())
本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号