【NLP】预训练模型中获取词嵌入 or Embedding层?

0.前言

        该博客写于投递完杭州一个中厂的NLP算法实习岗位后,面试的过程非常难受,被技术一个接一个的问题,问的很懵逼,于是痛并思痛,决定再沉淀几天再好好找工作。投递之前,没有做充足的准备,《深入浅出Embedding》是一本好书,拿到手里后,却没有怎么好好看过,写完这篇博客,我又很幸运的接到了一个公司的面试,希望这次可以准备好了再上战场。

1.项目简介

        该项目基于数据集IMDB进行情感分析,在做预测之前先对数据进行预处理,进行词嵌入操作,在项目中采用两种方法:1.利用预训练模型进行迁移学习;2.直接使用embedding层。第一种方法使用2014年英文维基百科的预计算词嵌入数据集:golve.6B.100d.txt,该文件包含了400000个单词的100维嵌入向量

        IMDB数据集:啊,这个太大了,我上传不了,有需要的请私我。

        词嵌入数据集:链接:https://pan.baidu.com/s/12v5rF_cVTm2txngVT02--w?pwd=35gr 提取码:35gr

2.读取IMDB数据集

        将下载好的IMDB数据集保存在本地,并转换成list从文件中读出来。

import os


imbd_dir = "D:\\973325230\\Python\\pythonProject\\word embedding\\data\\aclImdb" #你自己的文件路径
train_dir = os.path.join(imbd_dir,"train") #将文件夹里的"train"文件取出来

labels = [] #用于存放标签
texts = []  #用于存放文本内容分

for label_type in ["neg","pos"]:
    dir_name = os.path.join(train_dir,label_type)
    for fname in os.listdir(dir_name):  #fname是取出的文件名
        if fname[-4:] == ".txt":
            f = open(os.path.join(dir_name,fname),encoding="utf-8")
            texts.append(f.read())
            f.close()
            if label_type == "neg":
                labels.append(0)
            else:
                labels.append(1)

3.数据基本处理

3.1分词

        在这里我们利用TensorFlow2.0提供的Tokenizer函数进行分词操作。

        首先,导入我们本次所需要的包。

from tensorflow.keras.preprocessing.text import Tokenizer  #用于分词操作
from tensorflow.keras.preprocessing.sequence import pad_sequences #用于处理序列长度
import numpy as np

         然后,我们实例化一个分词器,用于分词处理,分词后构造一个{单词:序号}样式的字典

maxlen = 100      #只保留每个文本的前100个词
max_words = 10000 #只考虑数据集中最常见的10000个单词

#实例化一个分词器
tokenizer = Tokenizer(num_words=max_words) #num_words:默认是处理所有字词,在这里处理最常见的、出现频率最高的10000个字词。
tokenizer.fit_on_texts(texts) #利用tokenizer和texts构造一个字典(字典里包含了,单词与数字的映射关系)
sequences = tokenizer.texts_to_sequences(texts) #将texts的文字内容转换成数字

#从文本中一共抽取了88582个独一无二的单词
word_index = tokenizer.word_index
print("Found %s unique tokens." % len(word_index))

data = pad_sequences(sequences,maxlen) #只保留文本的前maxlen个单词
labels = np.asarray(labels) #将结构数据转化为ndarray,且不占用新的内存、
print("shape of data tensor:", data.shape)
print("shape of labels tensor:", labels.shape)

        我们可以看一下结果,现在我们的训练数据变成了一个一共有25000个样本,每个样本的文字长度为100的,25000个对应标签的数据集。

3.2训练集与验证集的划分

        我们在15000个样本上进行训练,然后在10000个样本上进行验证。

training_samples = 200 #在200个样本上训练
validation_samples = 10000 #对10000个样本进行验证

##将数据划分为训练集和验证集
##首先打乱数据,因为一开始数据集是排好序的,neg在前面,act在后面
indices = np.arange(data.shape[0]) #shape[0] = 25000
np.random.shuffle(indices) #打乱
data = data[indices]     #用新的编号排列data
labels = labels[indices] #用新的编号排列labels

x_train = data[:training_samples]
y_train = labels[:training_samples]
x_val = data[training_samples:training_samples+validation_samples]
y_val = labels[training_samples:training_samples+validation_samples]

4.处理GloVe词嵌入数据集

        1.对golve.6B.100d.txt文件进行解析构建一个{单词:向量表示}

样式的词典(embedding_index)。

##预处理GloVe
glove_dir = "D:\\973325230\\Python\\pythonProject\\word embedding\\data\\glove.6B"
embedding_index = {}
f = open(os.path.join(glove_dir,"glove.6B.100d.txt"),encoding="utf-8")
for line in f:  #将字典里的每一行取出来(就是一个单词,对应一个已经训练好的词向量)
    values = line.split() #按空格划分,存成list
    word = values[0] #每一行的第一个就是单词
    coefs = np.asarray(values[1:],dtype="float32") #这个单词所对应的词向量
    embedding_index[word] = coefs #将键值对存入词典
f.close() 

        我们可以打印看看这个字典的第一个样例。

##查看字典样例
for key,value in embedding_index.items():
    print(key,value)
    print(value.shape)
    break

        2.我们上一步只是针对glove,构建好了词典,这一步,我们需要利用上一步构建好的词典,为我们的训练集文本构建一个矩阵,其形状为10000×100。(其实,我这里有一点想不清楚,我以为它会做成一个字典然后传入Embedding层,但这里似乎是一个权重矩阵,我也不太清楚它在训练的过程中该如何对应相关的词)

###利用Glove对要处理的文本重新建立一个字典,字典大小为10000
embedding_dim = 100
embedding_matrix = np.zeros((max_words,embedding_dim))
for word,i in word_index.items():
    embedding_vector = embedding_index.get(word) #get()函数返回指定键的值,如果值不存在字典中,则返回默认值
    if i < max_words: #选取了88582中前10000个单词换成glove的词向量
        if embedding_vector is not None:
            ##embedding_index里找得到对应词嵌入就填上,找不到就是0
            embedding_matrix[i] = embedding_vector

5.构建模型

        我们采用Keras的序列(Sequential)构建模型,首先导入我们需要的包。

from tensorflow.keras.models import Sequential 
from tensorflow.keras.layers import Embedding, Flatten, Dense

         我们讲第一层设为Embedding层,第二层用Faltten()进行降维处理,第三、四层是全连接层,因为是二分类问题,最后一层采用Sigmoid函数,进行归一化处理。

##构建模型
model = Sequential() #建立一个容器
model.add(Embedding(max_words,embedding_dim,input_length=maxlen))
model.add(Flatten()) #将三维张量展平为二维
model.add(Dense(32,activation="relu"))
model.add(Dense(1,activation="sigmoid"))
model.summary()

6.训练模型

6.1使用预训练模型的词嵌入进行训练

        在刚刚建立好的模型中,我们在第一层,也就是Embedding层加载我们刚才用GloVe建立的embedding_matrix,并将第一层冻结,不参与训练。

#在模型中加载之前设定好的词嵌入,并冻结embedding layer
model.layers[0].set_weights([embedding_matrix])
model.layers[0].trainable = False

        这里,我们设置训练大小批次为32,一共迭代10次。 

##训练模型
model.compile(optimizer="rmsprop",loss="binary_crossentropy",metrics=["acc"])
history = model.fit(x_train,y_train,epochs=10,batch_size=32,validation_data=(x_val,y_val))
##保存模型权重
model.save_weights("pre_trained_glove_model.h5")

6.2Embedding层进行词嵌入训练

        这里模型的建立预训练过程都跟6.1是一样的,只不过,我们不再加载GloVe词嵌入,也不再将Embedding层冻结,让其参与训练。

##构建模型
model = Sequential() #建立一个容器
model.add(Embedding(max_words,embedding_dim,input_length=maxlen))
model.add(Flatten())
model.add(Dense(32,activation="relu"))
model.add(Dense(1,activation="sigmoid"))
model.summary()

##训练模型
model.compile(optimizer="rmsprop",loss="binary_crossentropy",metrics=["acc"])
history = model.fit(x_train,y_train,epochs=10,batch_size=32,validation_data=(x_val,y_val))

7.可视化训练过程及结果

        我们对训练的过程及结果进行一个可视化处理,这里利用matplotlib进行画图。

import matplotlib.pyplot as plt
##可视化训练结果
plt.plot(history.history["acc"])
plt.plot(history.history["val_acc"])
plt.title('Model accuracy')
plt.ylabel('Acc')
plt.xlabel('Epoch')
plt.legend(['train_accuracy', 'val_accuracy'], loc='upper left')
plt.show()

        1.使用预训练的词嵌入进行训练的过程及结果。 

         我们可以看到训练的准确率与验证的准确率相差较大,不过这里只使用了较少的训练数据,并且没有进行调参,但是验证准确率基本达到60%左右。

 

        2.只使用Embedding词嵌入进行训练的过程及结果。

        由图可知,不适用预训练模型的验证集准确率只在50%左右,使用预训练词嵌入模型的性能优于未使用预训练的模型性能。

8.总结

        该项目参考于《深入浅出Embedding》第2章内容,上述实验在训练集数据增加之后,采用Embedding层的效果反而优于了引入预训练模型的词嵌入方法,希望各大读者,也包括我可以进一步探究其中的原因。

        接offer~ 接offer~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值