本文将以IMDB電影評論資料集為範例,介紹Keras對文本資料預處理并喂入神經網絡模型的方法。
IMDB資料集的目标是根據電影評論的文本内容預測評論的情感标簽。訓練集有20000條電影評論文本,測試集有5000條電影評論文本,其中正面評論和負面評論都各占一半。
文本資料預處理主要包括中文切詞(本示例不涉及),建構詞典,序列填充,定義資料管道等步驟。讓我們出發吧!
一,準備資料
1,擷取資料
在公衆号背景回複關鍵字:imdb,可以擷取IMDB資料集的下載下傳連結。資料大小約為13M,解壓後約為31M。
資料集結構如下所示。
直覺感受一下文本内容。
2,建構詞典
為了能夠将文本資料喂入模型,我們一般要建構詞典,以便将詞轉換成對應的token(即數字編碼)。
from keras.preprocessing.text import Tokenizer
from tqdm import tqdm
# 資料集路徑
train_data_path = 'imdb_datasets/xx_train_imdb'
test_data_path = 'imdb_datasets/xx_test_imdb'
train_samples = #訓練集樣本數量
test_samples = #測試集樣本數量
max_words = # 保留詞頻最高的前10000個詞
maxlen = # 每個樣本文本内容最多保留500個詞
# 建構訓練集文本生成器
def texts_gen():
with open(train_data_path,'r',encoding = 'utf-8') as f,\
tqdm(total = train_samples) as pbar:
while True:
text = (f.readline().rstrip('\n').split('\t')[-1])
if not text:
break
if len(text) > maxlen:
text = text[:maxlen]
pbar.update()
yield text
texts = texts_gen()
tokenizer = Tokenizer(num_words=max_words)
tokenizer.fit_on_texts(texts)
複制
看一下我們生成的詞典。
3,分割樣本
為了能夠像ImageDataGenerator那樣用資料管道多程序并行地讀取資料,我們需要将資料集按樣本分割成多個檔案。
import os
scatter_train_data_path = 'imdb_datasets/train/'
scatter_test_data_path = 'imdb_datasets/test/'
# 将資料按樣本打散到多個檔案
def scatter_data(data_file, scatter_data_path):
if not os.path.exists(scatter_data_path):
os.makedirs(scatter_data_path)
for idx,line in tqdm(enumerate(open(data_file,'r',encoding = 'utf-8'))):
with open(scatter_data_path + str(idx) + '.txt','w',
encoding = 'utf-8') as f:
f.write(line)
scatter_data(train_data_path,scatter_train_data_path)
scatter_data(test_data_path,scatter_test_data_path)
複制
4,定義管道
通過繼承keras.utils.Sequence類,我們可以建構像ImageDataGenerator那樣能夠并行讀取資料的生成器管道。盡管下面的代碼看起來有些長,但通常隻有__data_generation方法需要被修改。
# 定義Sequence資料管道, 可以多線程讀資料
import keras
import numpy as np
from keras.preprocessing.sequence import pad_sequences
batch_size =
class DataGenerator(keras.utils.Sequence):
def __init__(self,n_samples,data_path,batch_size=batch_size,shuffle=True):
self.data_path = data_path
self.n_samples = n_samples
self.batch_size = batch_size
self.shuffle = shuffle
self.on_epoch_end()
def __len__(self):
return int(np.ceil(self.n_samples/self.batch_size))
def __getitem__(self, index):
# Generate indexes of the batch
batch_indexes = self.indexes[index*self.batch_size:(index+)*self.batch_size]
# Generate data
datas, labels = self.__data_generation(batch_indexes)
return datas, labels
def on_epoch_end(self):
self.indexes = np.arange(self.n_samples)
if self.shuffle == True:
np.random.shuffle(self.indexes)
def __read_file(self,file_name):
with open(file_name,encoding = 'utf-8') as f:
line = f.readline()
return line
def __data_generation(self, batch_indexes):
lines = [self.__read_file(self.data_path + str(i) + '.txt') for i in batch_indexes]
labels = np.array([int(line.strip().split('\t')[]) for line in lines])
texts = [line.strip().split('\t')[-1] for line in lines]
sequences = tokenizer.texts_to_sequences(texts)
datas = pad_sequences(sequences,maxlen)
return datas,labels
train_gen = DataGenerator(train_samples,scatter_train_data_path)
test_gen = DataGenerator(test_samples,scatter_test_data_path)
複制
二,構模組化型
為了将文本token後的整數序列用神經網絡進行處理,我們在第一層使用了Embedding層,Embedding層從數學上等效為将輸入資料進行onehot編碼後的一個全連接配接層,在形式上以查表方式實作以提升效率。
from keras import models,layers
from keras import backend as K
K.clear_session()
embedding_dim =
model = models.Sequential()
model.add(layers.Embedding(max_words, embedding_dim, input_length=maxlen))
model.add(layers.Flatten())
model.add(layers.Dense(,activation = 'relu'))
model.add(layers.Dense(, activation = 'sigmoid'))
model.summary()
model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['acc'])
複制
三,訓練模型
epoch_num =
steps_per_epoch = int(np.ceil(train_samples/batch_size))
validation_steps = int(np.ceil(test_samples/batch_size))
history = model.fit_generator(train_gen,
steps_per_epoch = steps_per_epoch,
epochs = epoch_num,
validation_data= test_gen,
validation_steps = validation_steps,
workers=,
use_multiprocessing=False #linux上可使用多程序讀取資料
)
複制
四,評估模型
import os
import pandas as pd
# 儲存得分
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(, len(acc) + )
dfhistory = pd.DataFrame({'epoch':epochs,'train_loss':loss,'test_loss':val_loss,
'train_acc':acc,'test_acc':val_acc})
print(dfhistory)
複制
五,使用模型
六,儲存模型
model.save('imdb_model.h5')
複制