ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

中文自然语言处理--基于双向GRU 生成各种古诗

2021-03-27 10:59:13  阅读:199  来源: 互联网

标签:layers GRU word keras -- words import 自然语言 counted


使用 LSTM 的变形 GRU 中文字符级建模,训练出一个能作诗的模型
数据集集为4000+古诗,一行一首,标题、作者已去掉:
https://download.csdn.net/download/fgg1234567890/16153813

import os
import random
import pandas as pd
import numpy as np
# 引入需要的模块
from keras.layers import Bidirectional
from keras.layers import GRU
import keras
from keras.callbacks import LambdaCallback
from sklearn.model_selection import train_test_split
from keras.layers import Dense, Input, Flatten, Dropout
from keras.layers import Embedding
from keras.models import Model
from keras.utils import plot_model
from keras.models import load_model
from keras.optimizers import Adam

# 步骤:
# 语料准备
# 语料预处理
# 模型参数配置
# 构建模型
# 训练模型
# 模型作诗
# 绘制模型网络结构图

puncs = [']', '[', '(', ')', '{', '}', ':', '《', '》']

# 预处理
def preprocess_file(Config):
    # 语料文本内容
    files_content = ''
    with open(Config.poetry_file, 'r', encoding='utf-8') as f:
        for line in f:
            # 每行的末尾加上"]"符号代表一首诗结束
            for char in puncs:
                line = line.replace(char, "")
            files_content += line.strip() + "]"

    words = sorted(list(files_content))
    words.remove(']')
    counted_words = {}
    for word in words:
        if word in counted_words:
            counted_words[word] += 1
        else:
            counted_words[word] = 1

    # 去掉低频的字
    erase = []
    for key in counted_words:
        if counted_words[key] <= 2:
            erase.append(key)
    for key in erase:
        del counted_words[key]
    del counted_words[']']
    wordPairs = sorted(counted_words.items(), key=lambda x: -x[1])

    words, _ = zip(*wordPairs)
    # word到id的映射
    word2num = dict((c, i+1) for i, c in enumerate(words))
    num2word = dict((i, c) for i, c in enumerate(words))
    word2numF = lambda x: word2num.get(x, 0)
    return word2numF, num2word, words, files_content

class Config(object):
    '''
    模型参数配置。预先定义模型参数和加载语料以及模型保存名称
    '''
    poetry_file = './qtais_tab2.txt'
    weight_file = 'poetry_model.h5'
    # 根据前六个字预测第七个字
    max_len = 6
    batch_size = 512
    learning_rate = 0.001

class PoetryModel(object):
    def __init__(self, config):
        '''
        init 函数定义,通过加载 Config 配置信息,进行语料预处理和模型加载,
        如果模型文件存在则直接加载模型,否则开始训练。
        '''
        self.model = None
        self.do_train = True
        self.loaded_model = False
        self.config = config

        # 文件预处理
        self.word2numF, self.num2word, self.words, self.files_content = preprocess_file(self.config)
        if os.path.exists(self.config.weight_file):
            self.model = load_model(self.config.weight_file)
            self.model.summary()
        else:
            self.train()
        self.do_train = False
        self.loaded_model = True

    def build_model(self):
        '''
        建立模型
        用 Keras 来构建网络模型,这里使用 LSTM 的 GRU 来实现,当然直接使用 LSTM 也没问题。
        '''
        input_tensor = Input(shape=(self.config.max_len,))
        embedd = Embedding(len(self.num2word)+1, 300, input_length=self.config.max_len)(input_tensor)
        lstm = Bidirectional(GRU(128, return_sequences=True))(embedd)
        dropout = Dropout(0.6)(lstm)
        # lstm = Bidirectional(GRU(128, return_sequences=True))(embedd)
        # dropout = Dropout(0.6)(lstm)
        flatten = Flatten()(dropout)
        dense = Dense(len(self.words), activation='softmax')(flatten)
        self.model = Model(inputs=input_tensor, outputs=dense)
        optimizer = Adam(lr=self.config.learning_rate)
        self.model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=['accuracy'])

    def sample(self, preds, temperature=1.0):
        '''
        当temperature=1.0时,模型输出正常
        当temperature=0.5时,模型输出比较open
        当temperature=1.5时,模型输出比较保守
        在训练的过程中可以看到temperature不同,结果也不同
        '''
        preds = np.asarray(preds).astype('float64')
        preds = np.log(preds) / temperature
        exp_preds = np.exp(preds)
        preds = exp_preds / np.sum(exp_preds)
        probas = np.random.multinomial(1, preds, 1)
        return np.argmax(probas)

    def generate_sample_result(self, epoch, logs):
        '''
        训练过程中,每个 epoch 打印出当前的学习情况
        '''
        print("\n==================Epoch {}=====================".format(epoch))
        for diversity in [0.5, 1.0, 1.5]:
            print("------------Diversity {}--------------".format(diversity))
            start_index = random.randint(0, len(self.files_content) - self.config.max_len - 1)
            generated = ''
            sentence = self.files_content[start_index: start_index + self.config.max_len]
            generated += sentence
            for i in range(20):
                x_pred = np.zeros((1, self.config.max_len))
                for t, char in enumerate(sentence[-6:]):
                    x_pred[0, t] = self.word2numF(char)

                preds = self.model.predict(x_pred, verbose=0)[0]
                next_index = self.sample(preds, diversity)
                next_char = self.num2word[next_index]
                generated += next_char
                sentence = sentence + next_char
            print(sentence)

    def predict(self, text):
        '''
        根据给定的提示,来进行预测。
        根据给出的文字,生成诗句,如果给的 text 不到四个字,则随机补全。
        '''
        if not self.loaded_model:
            return
        with open(self.config.poetry_file, 'r', encoding='utf-8') as f:
            file_list = f.readlines()
        random_line = random.choice(file_list)
        # 如果给的text不到四个字,则随机补全
        if not text or len(text) != 4:
            for _ in range(4 - len(text)):
                random_str_index = random.randrange(0, len(self.words))
                text += self.num2word.get(random_str_index) if self.num2word.get(random_str_index) not in [',', '。',
                                                                                                           ','] else self.num2word.get(
                    random_str_index + 1)
        seed = random_line[-(self.config.max_len):-1]
        res = ''
        seed = 'c' + seed
        for c in text:
            seed = seed[1:] + c
            for j in range(5):
                x_pred = np.zeros((1, self.config.max_len))
                for t, char in enumerate(seed):
                    x_pred[0, t] = self.word2numF(char)
                preds = self.model.predict(x_pred, verbose=0)[0]
                next_index = self.sample(preds, 1.0)
                next_char = self.num2word[next_index]
                seed = seed[1:] + next_char
            res += seed
        return res

    def data_generator(self):
        '''
        用于生成数据,提供给模型训练时使用。
        给定前六个字,生成第七个字,所以在后面生成训练数据的时候,会以6的跨度,1的步长截取文字,生成语料。
        如果出现了 ] 符号,说明 ] 符号之前的语句和之后的语句是两首诗里面的内容,
        两首诗之间是没有关联关系的,所以我们后面会舍弃掉包含 ] 符号的训练数据。
        '''
        print("len(self.files_content):", len(self.files_content))
        i = 0
        while 1:
            x = self.files_content[i: i + self.config.max_len]
            y = self.files_content[i + self.config.max_len]
            puncs = [']', '[', '(', ')', '{', '}', ':', '《', '》', ':']
            if len([i for i in puncs if i in x]) != 0:
                i += 1
                continue
            if len([i for i in puncs if i in y]) != 0:
                i += 1
                continue
            y_vec = np.zeros(
                shape=(1, len(self.words)),
                dtype=np.bool
            )
            y_vec[0, self.word2numF(y)] = 1.0
            x_vec = np.zeros(
                shape=(1, self.config.max_len),
                dtype=np.int32
            )
            for t, char in enumerate(x):
                x_vec[0, t] = self.word2numF(char)
            yield x_vec, y_vec
            i += 1

    def train(self):
        '''
        用来进行模型训练。
        TensorFlow回调函数:tf.keras.callbacks.LambdaCallback:
        用于动态创建简单的自定义回调的回调。
        此回调是使用将在适当时间调用的匿名函数构造的。请注意,回调需要位置参数,如下所示:
        on_epoch_begin和on_epoch_end要求两个位置参数: epoch,logs
        on_batch_begin和on_batch_end要求两个位置参数: batch,logs
        on_train_begin并on_train_end要求一个位置参数: logs
        参数:
        on_epoch_begin:在每个epoch开始时调用。
        on_epoch_end:在每个epoch结束时调用。
        on_batch_begin:在每个批处理开始时调用。
        on_batch_end:在每个批处理结束时调用。
        on_train_begin:在模型训练开始时调用。
        on_train_end:在模型训练结束时调用。
        '''
        # number_of_epoch = len(self.files_content) // self.config.batch_size
        number_of_epoch = 10
        if not self.model:
            self.build_model()
        self.model.summary()
        self.model.fit_generator(
            generator=self.data_generator(),
            verbose=True,
            steps_per_epoch=self.config.batch_size,
            epochs=number_of_epoch,
            callbacks=[
                # 该回调函数将在每个epoch后保存模型到filepath
                keras.callbacks.ModelCheckpoint(self.config.weight_file, save_weights_only=False),
                LambdaCallback(on_epoch_end=self.generate_sample_result)])

model = PoetryModel(Config)

text = input("text:")
sentence = model.predict(text)
print(sentence)
# 绘制网络结构图
plot_model(model.model, to_file='model.png')

原文:
https://soyoger.blog.csdn.net/article/details/108729402

标签:layers,GRU,word,keras,--,words,import,自然语言,counted
来源: https://blog.csdn.net/fgg1234567890/article/details/115261293

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有