Python深度学习07——Keras实现纯文字转化为数组和张量

 参考书目:陈允杰.TensorFlow与Keras——Python深度学习应用实战.北京:中国水利水电出版社,2021

本系列基本不讲数学原理,只从代码角度去让读者们利用最简洁的Python代码实现深度学习方法。


虽然很多教材或很多视频都展示了怎么构建循环神经网络,怎么训练文本类型的时序数据,但是它们都没教我们怎么把文字变成可以运算的数据,本章就是在教大家怎么处理文本数据。采用的是IMDB网络电影情感分析的数据集。

首先我们得知道Keras里面的一些基础功能。


分割文字数据——断词

下面演示Keras将英文句子切割:

from keras.preprocessing.text import text_to_word_sequence
#  定义文件
doc = "Keras is an API designed for human beings, not machines."
# 将文件分割成单字
words = text_to_word_sequence(doc)
print(words)
#计算单词个数
vocab_size = len(words)
print(vocab_size)

 可以看出这句话切成了10个词,并且过滤了标点符号,text_to_word_sequence默认是用空白去分割句子的,英语句子单词之间都是有空白的。也可以使用逗号分割:

doc = "Pregnancies,Glucose,BloodPressure,SkinThickness,Insulin,Outcome"
# 将文件分割成单字  
words = text_to_word_sequence(doc, lower=False, split=",")
print(words)

对于中文,没有空白分割,可以试一试逗号切割:

jay='从前从前有个人爱你很久,但偏偏风渐渐把距离吹得好远,好不容易又能再多爱一天,但故事的最后你好像还是说了拜拜。'
words = text_to_word_sequence(jay, lower=False, split=",")
print(words)

好像切不出来,也不知道是不是不支持中文的逗号的原因。中文分词一般都采用——jieba库:

import jieba
jay='从前从前有个人爱你很久,但偏偏风渐渐把距离吹得好远,好不容易又能再多爱一天,但故事的最后你好像还是说了拜拜。'
sentence_depart = jieba.lcut(jay.strip())
print(sentence_depart)

可以看到分割效果还是不错的,就是没过滤掉标点符号,可以后面手动循环去除一下标点符号 

切割完后,就是对单词进行索引化,变为数字。使用tokenizer对象去实现

from keras.preprocessing.text import Tokenizer
# 定义3 份文件
docs = ["Keras is an API designed for human beings, not machines.",
        "Easy to learn and easy to use." ,
        "Keras makes it easy to turn models into products."]
# 建立 Tokenizer
tok = Tokenizer()
# 执行文字数据预处理  
tok.fit_on_texts(docs)
# 显示摘要信息
print(tok.document_count)#显示文件个数
print(tok.word_counts)#显示每个单词的个数
print(tok.word_index)#显示单词的索引
print(tok.word_docs)#显示单词在文件中出现的次数

 将文字都变为数字

#  建立序列数据
words = tok.texts_to_sequences(docs)
print(words)

可以看到三个文本变成了3个列表,然后使用numpy变为数组,嵌入神经网络里面就可以进行运算了。


IMDB网络电影数据预处理 

虽然这个数据以及内置在Kears里面,但是生活处理实际问题时我们都是从文本自己变为数字的,下面演示不使用内置数据集接口,从网站下载文字,然后把这个文本变成可以预算的数组。

下载地址为:https://ai.stanford.edu/~amaas/data/sentiment/

 下载这个,然后解压

解压出来为一个aclImdb的文件夹,里面目录是这样的:

训练集和测试集里面都有正面和负面两个文件夹,里面都是相对应的评论文本。每个目录下都是12500个,即训练集有12500个正类,12500个负类。测试集也一样。下面开始扫描目录,逐一读取。(由于文本爬虫下来的里面还有一个网页文件的符号,要定义一个函数去删除这些符号,然后手工构建标签y的列表)

import re
from os import listdir
from keras.preprocessing import sequence
from keras.preprocessing.text import Tokenizer

#IMDb数据所在目录  
path = "aclImdb/"
#建立档案清单 
fList = [path + "train/pos/" + x for x in listdir(path + "train/pos")] + \
        [path + "train/neg/" + x for x in listdir(path + "train/neg")] + \
        [path + "test/pos/" + x for x in listdir(path + "test/pos")] + \
        [path + "test/neg/" + x for x in listdir(path + "test/neg")]
#删除HTML标签的符号
def remove_tags(text):
    TAG = re.compile(r'<[^>]+>')
    return TAG.sub('', text)
# 读取文字档案的数据
input_label = ([1] * 12500 + [0] * 12500) * 2
input_text  = []

 下面开始读取5w个文件,并把文件写入imput_text这个列表中,展示一下第5个评论的内容和标签。

#  读取档案内容  
for fname in fList:
    with open(fname, encoding="utf8") as ff:
        input_text += [remove_tags(" ".join(ff.readlines()))]
print(input_text[5])
print(input_label[5])

 

 开始用训练集的数据构建索引字典,查看前10个索引是什么单词

# 将文件分割成单字, 建立词索引字典     
tok = Tokenizer(num_words=2000)
tok.fit_on_texts(input_text[:25000])
print("文件数 : ", tok.document_count)
print({k: tok.word_index[k] for k in list(tok.word_index)[:10]})

 将每个文本都变为数字列表:

# 建立训练和测试数据集 
X_train = tok.texts_to_sequences(input_text[:25000])
X_test  = tok.texts_to_sequences(input_text[25000:])
Y_train = input_label[:25000]
Y_test  = input_label[25000:]

由于每个评论的单词数量都不一样,变为数字的长度也不一样,使用运算时要弄成一样长的,然后将y也变为数组

# 将序列数据填充成相同长度 
X_train = sequence.pad_sequences(X_train, maxlen=100)
X_test  = sequence.pad_sequences(X_test,  maxlen=100)
print("X_train.shape: ", X_train.shape)
print("X_test.shape: ", X_test.shape)

Y_train=np.array(Y_train)
Y_test=np.array(Y_test)
print(Y_train.shape)
print(Y_test.shape)

            

 到这里,文本数据预处理就完了,变为了数值,就可以进行神经网络的运算了。


IMDB网络电影数据分类

我们采用六种神经网络进行分类,分别是MLP,一维CNN,RNN,LSTM,GRU,1DCNN+LSTM,首先导入包

import numpy as np
import pandas as pd 
import matplotlib.pyplot as plt
from keras.preprocessing import sequence
from keras.models import Sequential
from keras.layers import Dense, Dropout, Embedding, Flatten,MaxPooling1D,Conv1D,SimpleRNN,LSTM,GRU
seed = 10
np.random.seed(seed)  # 指定乱数种子  
#单词索引的最大个数2000,单句话最大长度100
top_words=2000  #tok = Tokenizer(num_words=2000)和这里这个2000是对应的
max_words=100   #sequence.pad_sequences(X_train, maxlen=100)和这里的100对应

构建模型(这里是六种模型一起构建的,方便代码的重复使用,但是对新手可能不好理解,新手想看一个一个的模型分别构建的可以看我前面的文章)

def build_model(top_words=top_words,max_words=max_words,mode='LSTM',hidden_dim=[32]):
    model = Sequential()
    model.add(Embedding(top_words, 32, input_length=max_words))
    model.add(Dropout(0.25))
    if mode=='RNN':
        #RNN
        model.add(SimpleRNN(32))  
    elif mode=='MLP':
        model.add(Flatten())
        model.add(Dense(256, activation="relu"))       
    elif mode=='LSTM':
        # LSTM
        model.add(LSTM(32))
    elif mode=='GRU':
        #GRU
        model.add(GRU(32))
    elif mode=='CNN':
        #一维卷积
        model.add(Conv1D(filters=32, kernel_size=3, padding="same",activation="relu"))
        model.add(MaxPooling1D(pool_size=2))
        model.add(Flatten())
        model.add(Dense(256, activation="relu"))
    elif mode=='CNN+LSTM':
        model.add(Conv1D(filters=32, kernel_size=3, padding="same",activation="relu"))
        model.add(MaxPooling1D(pool_size=2))
        model.add(LSTM(100))
        
    model.add(Dropout(0.25))   
    model.add(Dense(1, activation="sigmoid"))
    model.compile(loss="binary_crossentropy", optimizer="adam", metrics=["accuracy"])
    return model

每种神经网络的神经元个数,还有网络的层数在我模型里面都是固定的,当然也可以自己去改,看实际情况需求,下面定义画损失和精度随着训练周期变化的图的函数:

#定义损失和精度的图
def plot_loss(history):
    # 显示训练和验证损失图表 
    loss = history.history["loss"]
    epochs = range(1, len(loss)+1)
    val_loss = history.history["val_loss"]
    plt.plot(epochs, loss, "bo", label="Training Loss")
    plt.plot(epochs, val_loss, "r", label="Validation Loss")
    plt.title("Training and Validation Loss")
    plt.xlabel("Epochs")
    plt.ylabel("Loss")
    plt.legend()
    plt.show()
# 显示训练和验证准确度  
def plot_acc(history):
    acc = history.history["accuracy"]
    epochs = range(1, len(acc)+1)
    val_acc = history.history["val_accuracy"]
    plt.plot(epochs, acc, "b-", label="Training Acc")
    plt.plot(epochs, val_acc, "r--", label="Validation Acc")
    plt.title("Training and Validation Accuracy")
    plt.xlabel("Epochs")
    plt.ylabel("Accuracy")
    plt.legend()
    plt.show()

定义训练函数

#定义训练函数
def train_fuc(max_words=max_words,mode='LSTM',batch_size=32,epochs=10,hidden_dim=[32],show_acc=True,show_loss=True):
    #构建模型
    model=build_model(max_words=max_words,mode=mode)
    history=model.fit(X_train, Y_train,batch_size=batch_size,epochs=epochs,validation_split=0.2, verbose=1)
    print('——————训练完毕——————')
    # 评估模型
    loss, accuracy = model.evaluate(X_test, Y_test)
    print("测试数据集的准确度 = {:.4f}".format(accuracy))
    
    if show_loss:
        plot_loss(history)
    if show_acc:
        plot_acc(history)

设置参数,

max_words=100
batch_size=32
epochs=10
show_acc=True
show_loss=True
mode='MLP'  
top_words=2000

开始用不同的模型去训练了,由于已经定义好了,只需要把训练函数的mode参数改一下就是不同的模型,评估测试集精度,损失画图都一起出来,很方便


MLP训练

train_fuc(mode='MLP',batch_size=batch_size,epochs=epochs)

 

 上面是训练10轮的过程,中间是评估,下面是损失图和精度图。其他模型一样

CNN训练

#下面模型都是接受三维数据输入,先把X变个形状
X_train= X_train.reshape((X_train.shape[0], X_train.shape[1], 1))
X_test= X_test.reshape((X_test.shape[0], X_test.shape[1], 1))
train_fuc(mode='CNN',batch_size=batch_size,epochs=epochs)

 RNN(参数都是一样的,训练轮数和批量大小都没改,就不写到训练函数里面去了,只需要改一下mode 的名称就行)

mode='RNN' 
train_fuc(mode='RNN')

 

 

LSTM(一样,改一下mode参数就行,很方便简洁)

train_fuc(mode='LSTM')

LSTM精度还是很高的,比前面的模型都高 

 GRU

train_fuc(mode='GRU')

 训练结果也差不多,不展示了。精度是0.8345,略低于LSTM

 CNN+LSTM

train_fuc(mode='CNN+LSTM')

 

 

 精度最高,为 0.8478。组合模型还是有效的。

大家可以自己去搭建不同的模型,可以修改神经元个数,神经网络层数,使用不同网络层的组合,然后比较一下测试集精度大小,找到最优的模型。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阡之尘埃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值