RNN模型与NLP应用(2):文本处理与词嵌入

前文-电影评论情感分析

这篇主要讲述从文本处理到词嵌入,输出的一个流程,从一个电影评论的情感分析数据集来展开
请添加图片描述

文本处理

分词需要做大量的处理-----注意事项
是否要将大写字母转化为小写字母
去除停顿词. the a of
拼写错误
step1:分词
请添加图片描述
step2:建立词典
在这里插入图片描述
step3:one-hot encoding
电影评论有长有短,这说明数据没有对齐
在这里插入图片描述
step4:对齐句子
假定w=7,一个句子如果超过了7,将前面的截断,保留后面w个词,同时也可以保留前面词,截断后面的词,如果这个句子比w还短,则在前面pad it with zeros,这样都可以存储在一个矩阵里面请添加图片描述

Word Embedding:Word to Vector

请添加图片描述
请添加图片描述
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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值