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。组合模型还是有效的。

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

### 回答1: 在深度学习训练过程中,当遇到 "list' object has no attribute 'shape'" 这个错误提示时,通常是因为将一个 Python 的列表(list)对象误以为是 NumPy 数组对象,并试图使用 shape 属性来获取数组的形状信息。 在深度学习中,数据通常以数组(或者矩阵)的形式输入到模型中进行训练。而在 Python 中,我们可以使用 NumPy 库来处理数组数据。NumPy 的数组对象有一个 shape 属性,用于获取数组的形状信息。 然而,Python 的列表(list)对象并没有 shape 属性。列表是一种用于保存多个元素的数据结构,而不是一个完整的数组对象。所以,当试图对列表使用 shape 属性时,Python 解释器会报错提示该对象没有 shape 属性。 解决该问题的方法有两种: 1. 将列表转换为 NumPy 数组:可以使用 NumPy 库中的 array() 函数将列表转换为数组对象。例如,将一个列表变量 `list_data` 转换为数组可以使用 `array_data = np.array(list_data)`。这样我们就可以通过 `array_data.shape` 获取数组的形状信息。 2. 检查数据类型:在深度学习训练中,应确保输入的数据是正确的数据类型,即数组对象。若在代码中遇到一个列表对象应是数组类型,要确保数据在传入模型之前进行正确的类型转换。 总之,对于 "list' object has no attribute 'shape'" 错误,我们需要确保正确地使用数组对象,并避免将列表对象误用为数组对象。使用 NumPy 库可以进行列表到数组的转换,使用正确的数据类型可以避免此类错误。 ### 回答2: 当出现 "list' object has no attribute 'shape'" 的错误提示时,意味着你正试图访问一个列表对象的 'shape' 属性,但实际上列表对象并没有 'shape' 属性。这个错误通常出现在深度学习训练中使用了错误的数据类型或错误的操作。 在深度学习中,通常使用的是 NumPy 或 TensorFlow 等库来进行数据处理和模型训练。这些库提供了一系列操作和数据结构,比如张量tensors)。 而张量是多维数组,可以有 'shape' 属性表示其维度大小。在这些库中,张量对象拥有 'shape' 属性,可以通过调用该属性来获取张量的维度大小。 但列表对象本身并不是张量,它只是一个简单的有序集合。因此,并不能直接调用列表对象的 'shape' 属性来获取其维度大小。 要解决这个错误,你需要确保使用正确的数据类型和操作。如果你想在深度学习中使用张量来进行训练,你可以使用相应的库来将列表数据转换为张量数据类型。比如,你可以使用 NumPy 的 array() 函数将列表转换为 NumPy 数组,或者使用 TensorFlow 的 convert_to_tensor() 函数将列表转换为张量对象。 以下是一个示例,展示了如何使用 NumPy 将列表转换为张量: ```python import numpy as np # 创建一个列表 my_list = [1, 2, 3, 4, 5] # 将列表转换为 NumPy 数组 my_array = np.array(my_list) # 打印数组的 shape 属性 print(my_array.shape) ``` 通过正确使用库提供的数据类型和操作,你就能够避免 "list' object has no attribute 'shape'" 错误,并顺利进行深度学习训练。 ### 回答3: "list' object has no attribute 'shape'" 这个错误是因为在深度学习训练过程中,尝试对一个列表对象使用了"shape"属性,但是列表对象并没有这个属性。 深度学习模型通常使用张量tensor)作为输入和输出的数据结构,而不是使用列表。张量是多维数组,可以方便地进行数值计算和操作。 要解决这个错误,您可以将您的输入数据转换为张量格式,然后再进行训练过程。可以使用库如NumPy或者TensorFlow提供的函数来完成这个转换。 首先,您需要将您的数据存储在一个NumPy数组中。确保数组的形状(shape)是正确的,以匹配您的训练模型的输入要求。 然后,可以使用NumPy的数组对象创建一个张量对象。您可以使用语句`tensor = tf.convert_to_tensor(numpy_array)`将NumPy数组转换为张量。在这个例子中,假设您使用的是TensorFlow库。 接下来,您就可以使用这个张量对象进行深度学习的训练过程了。在训练中,您可以使用其他TensorFlow提供的函数,如`tf.keras`进行模型的构建和训练。 请确保您在训练过程中使用适当的输入类型,以避免类似于"list' object has no attribute 'shape'"这样的错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阡之尘埃

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

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

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

打赏作者

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

抵扣说明:

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

余额充值