赞
踩
该博客写于投递完杭州一个中厂的NLP算法实习岗位后,面试的过程非常难受,被技术一个接一个的问题,问的很懵逼,于是痛并思痛,决定再沉淀几天再好好找工作。投递之前,没有做充足的准备,《深入浅出Embedding》是一本好书,拿到手里后,却没有怎么好好看过,写完这篇博客,我又很幸运的接到了一个公司的面试,希望这次可以准备好了再上战场。
该项目基于数据集IMDB进行情感分析,在做预测之前先对数据进行预处理,进行词嵌入操作,在项目中采用两种方法:1.利用预训练模型进行迁移学习;2.直接使用embedding层。第一种方法使用2014年英文维基百科的预计算词嵌入数据集:golve.6B.100d.txt,该文件包含了400000个单词的100维嵌入向量。
IMDB数据集:啊,这个太大了,我上传不了,有需要的请私我。
词嵌入数据集:链接:https://pan.baidu.com/s/12v5rF_cVTm2txngVT02--w?pwd=35gr 提取码:35gr
将下载好的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)
在这里我们利用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个对应标签的数据集。
我们在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]
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
我们采用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()
在刚刚建立好的模型中,我们在第一层,也就是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.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))
我们对训练的过程及结果进行一个可视化处理,这里利用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%左右,使用预训练词嵌入模型的性能优于未使用预训练的模型性能。
该项目参考于《深入浅出Embedding》第2章内容,上述实验在训练集数据增加之后,采用Embedding层的效果反而优于了引入预训练模型的词嵌入方法,希望各大读者,也包括我可以进一步探究其中的原因。
接offer~ 接offer~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。