这篇主要讲述从文本处理到词嵌入,输出的一个流程,从一个电影评论的情感分析数据集来展开
分词需要做大量的处理-----注意事项
是否要将大写字母转化为小写字母
去除停顿词. the a of
拼写错误
step1:分词
step2:建立词典
step3:one-hot encoding
电影评论有长有短,这说明数据没有对齐
step4:对齐句子
假定w=7,一个句子如果超过了7,将前面的截断,保留后面w个词,同时也可以保留前面词,截断后面的词,如果这个句子比w还短,则在前面pad it with zeros,这样都可以存储在一个矩阵里面
d是词向量的维度,自己设置的,v是词汇表的大小
我们的任务是学习训练出来的词向量会带有感情色彩,就比如好的感情色彩的词应该在一块,则它们的词向量应该相似
Logistic Regression for Binary Classification
from keras.models import Sequential
from keras.layers import Flatten,Dense,Embedding
embedding_dim=8
model=Sequential()# 把神经网络的层按顺序搭起来
model.add(Embedding(vocabulary,embedding_dim,input_length=word_num)) # 第一层为embedding层
model.add(Flatten()) # 将上面的20*8的矩阵压平成一维向量,变成160维的向量
model.add(Dense(1,activation='sigmoid'))# 全连接层输出的是一维,用sigmoid的激活函数
model.summary()# 打印出模型的概要
利用word Embedding层将每个单词映射到低维向量,所以每个长度为20的电影评论都可以被Flatten成160维向量,最后用线形分类回归器分成0和1的数,0代表负类评论,1代表正类评论
可以看到分类器有160个参数,加上偏置参数,总共有161个参数
环境配置:我的版本是tensorflow1.5.0+keras2.1.4, 其它版本可能会遇到点问题
数据集:
链接: https://pan.baidu.com/s/1us7AXURRpCoxgQY65YOylg
提取码: data
使用自带的IMDB数据库实现如下:
from keras.preprocessing.text import Tokenizer
from keras.preprocessing import sequence
import os
from keras.datasets import imdb
from keras import preprocessing
'举例操作:将词典和index对应'
samples = ['He is an engineer.', 'He uses PC to work.']
tokenizer = Tokenizer(num_words=1000) # 创建分词器,设置为只考虑前1000个最常见的单词
# num_words设置成 vocabulary,最后返回的是最常见的、出现频率最高的num_words个字词
# 需要保留的最大词数,基于词频。只有最常出现的 num_words 词会被保留,
tokenizer.fit_on_texts(samples) # 构建单词索引,为每个单词分配一个整数(根据词典标签)
# 只有前1000个词频最高的词才会被保留,相当于根据samples的内容,分配索引(每个词对应一个整数)
# fit_on_texts使用一系列文档来生成token词典,texts为list类,每个元素为一个文档。
# #num_words:处理的最大单词数
# print(tokenizer.fit_on_texts(samples))
sequences = tokenizer.texts_to_sequences(samples) # 将字符串转换为整数索引组成的列表
# 也就是得到词索引列表
# texts_to_sequences(texts) 将多个文档转换为word下标的向量形式,
# shape为[len(texts),len(text)] -- (文档数,每条文档的长度)
print(sequences)
one_hot_results = tokenizer.texts_to_matrix(samples, mode='binary')
# 不仅能使用二进制,也支持one-hot外的向量化编码模式,矩阵化=one_hot编码操作
word_index = tokenizer.word_index # 查看单词索引,此处为给samples标记的内容
print('独立标签数:', len(word_index))
print(word_index)
print(one_hot_results)
'嵌入层(Embeding)实现'
# 1.利用Embedding层学习词嵌入
# imdb数据集,使用keras的embedding层处理文字数据
##将评论限制为10000个常见单词,评论长度限制为20个单词,每个单词学习一个8维嵌入
from keras.datasets import imdb
from keras import preprocessing
max_features = 10000 # 作为特征的单词个数
maxlen = 20 # 在20个单词后截断文本,这些单词都属于上一行的最常见单词
(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=max_features)
# x_train表示的是训练数据(评论列表,每条评论由数字组成,对应单词在词典中出现的位置下标),
# y_train表示的是训练的标签,标签0表示负面评论,1表示正面评论
# num_words定义的是大于该词频的单词会被读取,词频越高的单词,其在单词表中的位置越靠前
# 将整数序列进行对齐,也就是对列表进行填充或者裁切
# imdb数据集中,单词的索引是从1开始的,不是从0开始的,所以可以使用0填充序列
x_train = preprocessing.sequence.pad_sequences(x_train, maxlen=maxlen)
x_test = preprocessing.sequence.pad_sequences(x_test, maxlen=maxlen)
# 只使用了前20个单词作为判断依据,之前imdb的划分是基于全部评论
'利用Embedding层学习词嵌入(定义模型)'
from keras.models import Sequential # Sequential为将神经网络的层按顺序搭起来
from keras.layers import Dense, Embedding, Flatten
model = Sequential()
# 第一层是Embedding层,设定字典里10000个单词,Embedding层的输出是个20×8的矩阵,
# 只考虑每条电影评论中最后的20个单词,每个单词用8维的向量来表示
# 参数矩阵在此的维度是80000,矩阵的参数根据设定的每个单词表示的向量(8)*字典词个数10000得到
model.add(Embedding(10000, 8, input_length=maxlen))
model.add(Flatten()) ##将三维的嵌入张量展平为形状为(samples,maxlen*8)的二维张量
model.add(Dense(1, activation='sigmoid')) # 添加分类器
model.compile(
optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['accuracy']
)
history = model.fit(x_train, y_train,
epochs=10,
batch_size=32,
validation_split=0.2)
model.summary()
从原始数据分析:
'从零开始动手实现'
# 从IMDB的原始文本开始学习,而不使用keras自带的imdb数据库。
'处理IMDB原始数据的内容和标签对应'
# imdb_dir = r'E:\python\data\aclImdb\aclImdb' # 根据自己的情况改进
imdb_dir = r'/Users/liuqian/PycharmProjects/pythonProject/data/aclImdb'
train_dir = os.path.join(imdb_dir, 'train') # 因为训练数据集分为train和test,所以需要进行分类
# train中又分为积极pos和消极neg两类型的评价,因此需要进行区分
labels = []
texts = []
for label_type in ['neg', 'pos']: # 划分评论的正反倾向,并存到不同的文件
# 遍历两个类别的数据集
dir_name = os.path.join(train_dir, label_type)
# print(dir_name)
# /Users/liuqian/PycharmProjects/pythonProject/data/aclImdb/train/neg
# /Users/liuqian/PycharmProjects/pythonProject/data/aclImdb/train/pos
for fname in os.listdir(dir_name):
# os.listdir() 方法用于返回指定的文件夹包含的文件或文件夹的名字的列表(返回文件名)
if fname[-4:] == '.txt': # 判断后四位是不是.txt
f = open(os.path.join(dir_name, fname), encoding='utf-8')
texts.append(f.read())
f.close()
if label_type == 'neg': # 此处进行标签的工作(对应txt附上不同标签)
labels.append(0)
else:
labels.append(1)
# 1表示积极评论,0表示消极评论
# 对imdb原始数据的文本进行分类
from keras.preprocessing.text import Tokenizer # 分词器
from keras.preprocessing.sequence import pad_sequences # 补位0
import numpy as np
max_len = 500 # 100个单词后截断评论,这个参数非常影响准确度
training_samples = 2000 # 在2000个样本中训练
validation_samples = 10000 # 在10000个样本上验证
max_words = 10000 # 只考虑数据集中前10000个最常见的单词
tokenizer = Tokenizer(num_words=max_words)
tokenizer.fit_on_texts(texts)
sequences = tokenizer.texts_to_sequences(texts)
word_index = tokenizer.word_index
data = pad_sequences(sequences, maxlen=max_len)
labels = np.asarray(labels) # 结构数据转化为ndarray
print('shape of data tensor:', data.shape)
print('shape of label tensor:', labels.shape)
# 将数据划分为训练集和验证集
# 注意要打乱数据,因为原始数据是分类排好序的
indices = np.arange(data.shape[0])
np.random.shuffle(indices) # 重新排序返回一个随机序列
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词嵌入文件,构建一个将单词(字符格式)映射为其向量表示(数值向量)的索引
#glov_dir = r'\data\imdb'
glov_dir = r'/Users/liuqian/PycharmProjects/pythonProject'
embeddings_index = {}
f = open(os.path.join(glov_dir, 'data/glove.6B.100d.txt'), encoding='utf8')
for line in f:
values = line.split()
word = values[0]
coefs = np.asarray(values[1:], dtype='float32')
embeddings_index[word] = coefs
f.close()
print('find %s word vectors' % len(embeddings_index))
'准备GloVe词嵌入矩阵:'
# 构造可以加载到embedding层中的嵌入矩阵
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
'定义模型:'
from keras.models import Sequential
from keras.layers import Embedding, Dense, Flatten
model = Sequential()
model.add(Embedding(max_words, embedding_dim, input_length=max_len)) # 100维
model.add(Flatten())
model.add(Dense(32, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
model.summary()
##将准备好的glove矩阵加载到Embedding层,同时冻结Embedding层,道理和预处理方法都是一样的
model.layers[0].set_weights([embedding_matrix])
model.layers[0].trainable = False # 冻结embedding层
'编译模型'
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)
)
loss_and_acc = model.evaluate(x_val, y_val)
print('loss=' + str(loss_and_acc[0]))
print('acc=' + str(loss_and_acc[1]))
model.save_weights('pre_trained_glove_model.h5')
# loss=0.8123553492546082
# acc=0.6564