NLP-神经语言模型:文本生成

一、引言

NLP-统计语言模型中已经简要介绍过语言模型的相关知识,该文中已阐述语言模型的应用场景和一些传统的实现方式,本文接着演示n-gram的另一种实现方式-神经网络,那这样的实现方式就是神经语言模型吗?
按本渣的理解,答案是否定的,神经语言模型是一个类指,其本质是在统计语言模型上的一种延伸和扩展,我可以只考虑上文n个词,也可以考虑下文n个词,也可以基于上下文考虑,具体的情况需要根据需求而定。

二、文本生成实践

1、训练语料

为了和NLP-统计语言模型形成呼应,本文依旧使用文本生成作为案例,依旧照抄语料库(量少更能直观理解过程)。
另外强调一点,本文还是以理解为主,并没有将数据进行训练、验证和测试等拆分!

    corpus = '''
    这一生原本一个人,你坚持厮守成我们,却小小声牵着手在默认。
    感动的眼神说愿意,走进我的人生。
    进了门开了灯一家人,盼来生依然是一家人。
    确认过眼神,我遇上对的人。
    我挥剑转身,而鲜血如红唇。
    前朝记忆渡红尘,伤人的不是刀刃,是你转世而来的魂。
    青石板上的月光照进这山城,我一路的跟你轮回声,我对你用情极深。
    谁在用琵琶弹奏一曲东风破,枫叶将故事染色,结局我看透。
    篱笆外的古道我牵着你走过,荒烟漫草的年头,就连分手都很沉默。
    '''

2、项目结构

NLP-神经语言模型:文本生成

3、数据处理

本文假设第n个词的出现只与前面n-1个词相关,而与其它任何词都不相关,因此在构建训练数据时,将语料进行截取,如

这一生原本一个人,你坚持厮守成我们,却小小声牵着手在默认

以n为4进行切分的话即为

这一生->原
一生原->本
生原本->一
...以此类推

根据上述的切分方法,将数据进行切分并转为one-hot序列

   def __init__(self,window,corpus):
       self.window = window
       self.corpus = corpus
       self.char2id = None
       self.id2char = None
       self.char_length = 0


   def load_data(self):
       X = []
       Y = []
       # 将语料按照\n切分为句子
       corpus = self.corpus.strip().split('\n')
       # 获取所有的字符作为字典
       chrs = set(self.corpus.replace('\n',''))
       chrs.add('UNK')
       self.char_length = len(chrs)
       self.char2id = {c: i for i, c in enumerate(chrs)}
       self.id2char = {i: c for c, i in self.char2id.items()}
       for line in corpus:
           x = [[self.char2id[char] for char in line[i: i + self.window]] for i in range(len(line) - self.window)]
           y = [[self.char2id[line[i + self.window]]] for i in range(len(line) - self.window)]
           X.extend(x)
           Y.extend(y)
       # 转为one-hot
       X = to_categorical(X)
       Y = to_categorical(Y)
       return X,Y

4、模型构建

本文使用两层的LSTM进行模型搭建

    def build_model(self):
        model = Sequential()
        model.add(Bidirectional(LSTM(100,return_sequences=True)))
        model.add(Bidirectional(LSTM(200)))
        model.add(Dense(self.char_length, activation='softmax'))
        model.compile('adam', 'categorical_crossentropy')
        self.model = model

5、模型训练方法

    def train_model(self,X,Y,epochs):
        self.model.fit(X, Y, epochs=epochs, verbose=1)
        self.model.save('model.model')

6、模型测试方法

    def predict(self,sentence):
        input_sentence = [self.char2id.get(char,self.char2id['UNK']) for char in sentence]
        input_sentence = pad_sequences([input_sentence],maxlen=self.window)
        input_sentence = to_categorical(input_sentence,num_classes=self.char_length)
        predict = self.model.predict(input_sentence)
        # 本文为了方便 直接取使用最大概率的值,并非绝对,采样的方式有很多种,自行选择
        return self.id2char[np.argmax(predict)]

7、训练及测试

    # 以5切分
    window = 5
    text_generate = TextGenerate(window,corpus)
    X,Y = text_generate.load_data()
    text_generate.build_model()
    text_generate.train_model(X,Y,500)
    # text_generate.load_model()
    input_sentence = '确认过眼神'
    result = input_sentence
    #在构建语料的过程中,设置了每次只预测一个词,为了生成完成的一句话,需要进行循环预测
    while not result.endswith('。'):
        predict = text_generate.predict(input_sentence)
        result += predict
        input_sentence += predict
        input_sentence = input_sentence[len(input_sentence)-(window if len(input_sentence)>window else len(input_sentence)):]
        print(result)

以‘确认过眼神’作为提示语句,测试结果如下:

'''
确认过眼神,
确认过眼神,我
确认过眼神,我遇
确认过眼神,我遇上
确认过眼神,我遇上对
确认过眼神,我遇上对的
确认过眼神,我遇上对的人
确认过眼神,我遇上对的人。
'''

从结果中发现,该模型已经完全记住了训练语料的连接关系,但也因为采样的时候贪婪地采样最大概率词作为预测结果,所以当提示语句在语料库中完全出现过时,预测结果的多样性就受到了抑制。

上一篇:【原理】预训练模型之自然语言理解--KBERT


下一篇:Kenlm python接口用法详细介绍