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~