使用预训练模型学习判断imdb评论正负面模型
本节的模型与上节见过的那个类似:将句子嵌入到向量序列中,然后将其展平,最后在上面训练一个 Dense 层。但此处将使用预训练的词嵌入。此外,我们将从头开始,先下载IMDB 原始文本数据,而不是使用 Keras 内置的已经预先分词的 IMDB 数据。
首先,在 http://mng.bz/0tIo ,下载原始IMDB数据集并解压。
文件夹的结构如下:
aclImdb:
├─test
│ ├─neg
│ └─pos
└─train
├─neg
└─pos
随便打开一个neg文件夹里的文本:
想必,这就是评论的原生数据,并没有像之前keras内置的imdb数据集那样帮我们处理好了单词->序列的转化。
- 处理 IMDB 原始数据的标签
#导入要处理的数据
import os
imdb_dir = 'C:\\Users\\Administrator\\Desktop\\Keras_learn\\aclImdb'
train_dir = os.path.join(imdb_dir,'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):
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)
这里从训练集中,分别导入neg和pos文件夹里的文本信息,并在 labels 相应的索引处添加标签值,0表示负面评价,1表示正面评价。
让我们验证下导入是否成功:
print(len(texts))
print(texts[0])
输出:
正负面评价总共有25 000 条。
- 处理数据
keras内置了分词器(tokenizer)功能,可以在限制单词数的前提下,为需要的单词编号。
#处理数据
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
import numpy as np
maxlen = 100 #在100个单词后截断评论
training_samples = 200 #200个训练样本
validation_samples = 10000 #10 000个验证样本
max_words = 10000 #只考虑数据集中前10 000 个最常见的单词
tokenizer = Tokenizer(num_words=max_words)
tokenizer.fit_on_texts(texts)
sequences = tokenizer.texts_to_sequences(texts) #将单词转化为序列
word_index = tokenizer.word_index
print('Found %s unique tokens.'% len(word_index))
data = pad_sequences(sequences,maxlen=maxlen)
#不够maxlen用 0 填补
labels = np.asarray(labels)
print('Shape of data tensor:',data.shape)
print('Shape of label tensor:',labels.shape)
#打乱顺序
indices = np.arange(data.shape[0])
indices = np.random.choice(indices,indices.shape[0])
data = data[indices]
labels = labels[indices]
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]
输出:
-
下载GloVe词嵌入
打开https://nlp.stanford.edu/projects/glove,下载 2014 年英文维基百科的预计算嵌入。这是一个 822 MB 的压缩文件,文件名是 glove.6B.zip,里面包含 400 000 个单词(或非单词的标记)的 100 维嵌入向量。解压文件。 -
导入模型参数
我们用字典的方式,导入词——向量。
# 解析 GloVe 词嵌入文件
glove_dir = 'C:\\Users\\Administrator\\Desktop\\Keras_learn\\glove'
#文件里每行是一个单词,和该单词的词向量
embeddings_index = {}
f = open(os.path.join(glove_dir,'glove.6B.100d.txt'),encoding='utf-8')
#构建 词:向量 的字典
for line in f:
values = line.split()
word = values[0] #获得单词
coefs = np.asarray(values[1:],dtype='float32') #获得词向量
embeddings_index[word] = coefs #构建 词:向量 的字典
f.close()
print('Found %s word vectors.'%len(embeddings_index))
输出:
- 准备 GloVe 词嵌入矩阵
要让参数注入模型中,必须将这个字典转为矩阵的形式。
#准备 GloVe 词嵌入矩阵
embedding_dim = 100
embedding_matrix = np.zeros((max_words,embedding_dim))
for word, i in word_index.items():
if i < max_words:
embedding_vector = embeddings_index.get(word)
if embedding_vector is not None:
embedding_matrix[i] = embedding_vector
#在glove中找不到的词,其嵌入向量全为0
这里,值得注意的是,每个单词都转为一个100维向量。
- 模型定义
#模型定义
from keras.models import Sequential
from keras.layers import Embedding, Flatten, Dense
model = Sequential()
model.add(Embedding(max_words,embedding_dim,input_length=maxlen))
#max_words==10 000, embedding_dim==100, maxlen==100
model.add(Flatten())
model.add(Dense(32,activation='relu'))
model.add(Dense(1,activation='sigmoid'))
model.summary()
- 将预训练的词嵌入加载到 Embedding 层中
#model的第一层即为embedding
model.layers[0].set_weights([embedding_matrix])
model.layers[0].trainable = False
- 编译,训练模型
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')
输出:
- 绘制结果
#绘制结果
import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1,len(acc)+1)
plt.figure("acc")
plt.plot(epochs,acc,'bo',label="Training acc")
plt.plot(epochs,val_acc,'b',label="Validation acc")
plt.title("Traning and validation accuracy")
plt.legend()
plt.figure("loss")
plt.plot(epochs,loss,'bo',label="Training loss")
plt.plot(epochs,val_loss,'b',label="Validation loss")
plt.title("Traning and validation loss")
plt.legend()
plt.show()
可以明显看出,由于训练样本只有 200,会严重依赖样本,训练集接近1,而验证集的准确度只有0.56。但换个角度想想,只用了200个数据集就可以达到超过一半的准确度,也是不容易的。