首页 > 技术文章 > 中文情感识别 5

Howbin 2020-04-01 14:29 原文

中文情感识别 5

文本生成实例:爱丽丝梦游仙境

参考文献:

  • 深度学习:基于 Keras 的 Python 实践 / 魏贞原著. —北京:电子工业出版社, 2018.5

学习笔记


循环神经网络不仅可以用于预测模型,也可以用作生成模型。也就是说,可以使用循环神经网络学习序列问题,并为问题域生成全新的合理序列。生成模型不仅需要对问题本身进行深入研究,还需要了解问题相关的知识领域的信息。

我主要学习这个项目的文本处理技术

问题描述

《爱丽丝梦游仙境》的文本可以到 https://www.gutenberg.org/ebooks/11 下载

导入数据

下载后的文本文件需要进行预处理,才能用于神经网络的训练。首先将文本导入
Python,然后按照标点符号分割成不同的句子,并将其中无意义的字符(如换行符等)和每个章节的标题删除.

关键问题

nltk 资源问题 路径配置

参考

这里面有很多的坑,这个字典不好下载。 然后下载以后不好配置。 需要好好的根据报错信息去调整NLTK Data 的结构目录。

比如说:

  • 压缩包解压
  • 要求PY3目录
    报错信息
    这个你自己建一个就好了。然后把文件复制到PY3

tokenizers 目录结构如下

│  
└─punkt
    │  czech.pickle
    │  danish.pickle
    │  dutch.pickle
    │  english.pickle
    │  estonian.pickle
    │  finnish.pickle
    │  french.pickle
    │  german.pickle
    │  greek.pickle
    │  italian.pickle
    │  norwegian.pickle
    │  polish.pickle
    │  portuguese.pickle
    │  README
    │  slovene.pickle
    │  spanish.pickle
    │  swedish.pickle
    │  turkish.pickle
    │  
    └─PY3
            czech.pickle
            danish.pickle
            dutch.pickle
            english.pickle
            estonian.pickle
            finnish.pickle
            french.pickle
            german.pickle
            greek.pickle
            italian.pickle
            norwegian.pickle
            polish.pickle
            portuguese.pickle
            README
            slovene.pickle
            spanish.pickle
            swedish.pickle
            turkish.pickle

当然这个解决方法不能解除根本错误,本质上应该是NLTK字典目录太久了。

corpora.Dictionary([document])

就是生成字典,注意这个字典的输入要求。为[[ ]]

词云生成

关键问题

这里有很多的坑。 主要式因为版本不一样,参数发生了一些变换。具体见代码。

关于导入库
书上为:

from pyecharts import WordCloud

应该为:

from pyecharts.charts import WordCloud

参考:

关于列表与元组嵌套

a=('name',1)

b=list()

b.append(a)

a[0]
Out[59]: 'name'

b[0]
Out[60]: ('name', 1)

代码

def show_word_cloud(document):
    # 需要清除的标点符号
    left_words = ['.', ',', '?', '!', ';', ':', '\'', '(', ')']
    # 生成字典
    dic = corpora.Dictionary([document])
    # 计算得到每个单词的使用频率
    words_set = dic.doc2bow(document)
    # 生成单词列表和使用频率列表
    my_words=list()
    for item in words_set:
        key = item[0]
        frequence = item[1]
        word = dic.get(key=key)
        if word not in left_words:
            my_words.append((word,frequence))
            
    # 使用 pyecharts 生成词云
    word_cloud = WordCloud()
    word_cloud.add(series_name='Alice\'s word cloud', data_pair=my_words, shape='circle', word_size_range=[20, 100], width=1000, height=620)
    word_cloud.render("wordcloud_custom_mask_image.html")

结果

词云图
词云图

简单 LSTM

首先通过一个简单的长短期 记忆网络来生成对《爱丽丝梦游仙境》的文本分析模型,这个模型中包含一个词嵌入层、一个 LSTM 层、一个 Dropout 层,以及一个使用 softmax 激活函数的输出层。然后将准备的数据按照固定长度拆分成训练数据集,并使用其训练模型

关键问题

关于dict_len的数值
这个数值要根据自己的字典长度+1
否则训练就会有问题

代码

'''
文本生成实例:爱丽丝梦游仙境
LSTM
'''
from nltk import data
data.path.append(r"Y:\python\NLTK\nltk_data")
from nltk import word_tokenize
from gensim import corpora
from pyecharts.charts import WordCloud
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers.embeddings import Embedding
from keras.layers.convolutional import Conv1D, MaxPooling1D
import numpy as np
from keras.utils import np_utils

filename='Alice.txt'
dict_file = 'dict_file.txt'
model_json_file = 'simple_model.json'
model_hd5_file = 'simple_model.hd5'

batch_size = 128
epochs = 200
dict_len = 3281
max_len = 20
document_max_len = 33200


def clear_data(str):
    '''
        删除字符串中的特殊字符或换行符
    '''
    value = str.replace('\ufeff', '').replace('\n', '')
    return value
    
def load_dataset(): 
    '''
        导入数据
    '''
    with open(filename, mode='r') as file:
        document = []
        lines = file.readlines()
        for line in lines:
            # 删除非内容字符
            value = clear_data(line)
            if value != '':
                # 对一行文本进行分词
                for str in word_tokenize(value):
                    # 跳过章节标题
                    if str == 'CHAPTER':
                        break
                    else:
                        document.append(str.lower())
    return document

def word_to_integer(document):
    '''
    :return 单词向量
    '''
    dic = corpora.Dictionary([document])
    dic.save_as_text(dict_file)
    dic_set = dic.token2id
    # 将单词转换为整数
    values = []
    for word in document:
    # 查找每个单词在字典中的编码
        values.append(dic_set[word])
    return values

def show_word_cloud(document):
    # 需要清除的标点符号
    left_words = ['.', ',', '?', '!', ';', ':', '\'', '(', ')']
    # 生成字典
    dic = corpora.Dictionary([document])
    # 计算得到每个单词的使用频率
    words_set = dic.doc2bow(document)
    # 生成单词列表和使用频率列表
    my_words=list()
    for item in words_set:
        key = item[0]
        frequence = item[1]
        word = dic.get(key=key)
        if word not in left_words:
            my_words.append((word,frequence))
            
    # 使用 pyecharts 生成词云
    word_cloud = WordCloud()
    word_cloud.add(series_name='Alice\'s word cloud', data_pair=my_words, shape='circle', word_size_range=[20, 100], width=1000, height=620)
    word_cloud.render("wordcloud_custom_mask_image.html")

def build_model():
    model = Sequential()
    model.add(
        Embedding(input_dim=dict_len, output_dim=32, input_length=max_len)
    )
    model.add(
        Conv1D(
            filters=32,
            kernel_size=3,
            padding='same',
            activation='relu'
        )
    )
    model.add(MaxPooling1D(pool_size=2))
    model.add(LSTM(units=256))
    model.add(Dropout(0.2))
    model.add(Dense(units=dict_len, activation='softmax'))
    model.compile(loss='categorical_crossentropy', optimizer='adam')
    model.summary()
    return model

def  make_dataset(document):
    '''
        :return 按照固定长度拆分文本
    '''
    dataset =  np.array(document[0:document_max_len])
    dataset=dataset.reshape(int(document_max_len / max_len), max_len)
    return dataset

def make_y(document):
    dataset = make_dataset(document)
    y = dataset[1:dataset.shape[0], 0]
    return y

def make_x(document):
    dataset = make_dataset(document)
    x = dataset[0: dataset.shape[0] - 1, :]
    return x

document = load_dataset()
values = word_to_integer(document)

x_train = make_x(values)
y_train = make_y(values)
y_train = np_utils.to_categorical(y_train, dict_len)
model = build_model()
model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs,verbose=2)
# 保存模型到 json 文件中
model_json = model.to_json()
with open(model_json_file, 'w') as file:
    file.write(model_json)
model_json = model.to_json()
# 保存权重数值到文件中
model.save_weights(model_hd5_file)

结果

这么模型,的输入输出,我至今处于懵逼状态。
懂了 利用 一行 20个单词,生成一个单词

poch 199/200
 - 2s - loss: 0.4299
Epoch 200/200
 - 1s - loss: 0.4314

生成文本

用训练完成的 LSTM 模型生成文本相对简单。首先,设定一段作为开始的句子,
长度为训练模型时的输入长度。然后加载上节中生成的字典文件、模型配置文件及模型权重文件,并编译生成 LSTM 模型,这个模型不再需要进行训练。最后,利用这个加载的 LSTM 模型做出预测。使用 LSTM 模型做出预测的最简单方法是首先使用给定序列作为输入,然后生成下一个单词,再更新给定序列,将生成的单词添加到末尾并修剪第一个单词。在这里重复这个过程 200 次,就可以生成一篇文章。

关键问题

代码

# -*- coding: utf-8 -*-
'''
文本生成实例:爱丽丝梦游仙境
利用上节生成的LSTM经行文本生成
'''
from nltk import word_tokenize
from gensim import corpora
from keras.models import model_from_json
import numpy as np

model_json_file = 'simple_model.json'
model_hd5_file = 'simple_model.hd5'
dict_file = 'dict_file.txt'
words = 200
max_len = 20
myfile = 'myfile.txt'

def load_dict():
    # 从文本导入字典
    dic = corpora.Dictionary.load_from_text(dict_file)
    return dic

def load_model():
    # 从 json 加载模型
    with open(model_json_file, 'r') as file:
        model_json = file.read()
    # 加载模型
    model = model_from_json(model_json)
    model.load_weights(model_hd5_file)
    model.compile(loss='categorical_crossentropy', optimizer='adam')
    return model

def word_to_integer(document):
    # 导入字典
    dic = load_dict()
    dic_set = dic.token2id
    # 将单词转换为整数
    values = []
    for word in document:
    # 查找每个单词在字典中的编码
        values.append(dic_set[word])
    return values

def make_dataset(document):
    dataset = np.array(document)
    dataset = dataset.reshape(1, max_len)
    return dataset

def reverse_document(values):
    # 导入字典
    dic = load_dict()
    # 将编码转换为单词
    document = ''
    for value in values:
        word = dic.get(value)
        document = document + word + ' '
    return document

model = load_model()
start_doc = 'Alice is a little girl, who has a dream to go to visit the land in the time.'
document = word_tokenize(start_doc.lower())
new_document = []
values = word_to_integer(document)
new_document = [] + values


for i in range(words):
    x = make_dataset(values)
    prediction = model.predict(x, verbose=0)
    prediction =  np.argmax(prediction)
    values.append(prediction)
    new_document.append(prediction)
    values = values[1: ]

new_document = reverse_document(new_document)
with open(myfile, 'w') as file:
    file.write(new_document)

推荐阅读