zoukankan      html  css  js  c++  java
  • 01-NLP-04-01用RNN做文本生成

    ---恢复内容开始---

    用RNN做文本生成

    举个小小的例子,来看看LSTM是怎么玩的

    我们这里用温斯顿丘吉尔的人物传记作为我们的学习语料。

    (各种中文语料可以自行网上查找, 英文的小说语料可以从古登堡计划网站下载txt平文本:https://www.gutenberg.org/wiki/Category:Bookshelf)

    第一步,一样,先导入各种库

    In [66]:
    import numpy
    from keras.models import Sequential
    from keras.layers import Dense
    from keras.layers import Dropout
    from keras.layers import LSTM
    from keras.callbacks import ModelCheckpoint
    from keras.utils import np_utils
    
     

    接下来,我们把文本读入

    In [67]:
    raw_text = open('../input/Winston_Churchil.txt').read()
    raw_text = raw_text.lower()
    
     

    既然我们是以每个字母为层级,字母总共才26个,所以我们可以很方便的用One-Hot来编码出所有的字母(当然,可能还有些标点符号和其他noise)

    In [68]:
    chars = sorted(list(set(raw_text)))    #set将raw——text中所有出现的元素组成一个集合(由于集合中元素没有重复),然后再将无顺序的集合转化为列表(有序)并排序
    char_to_int = dict((c, i) for i, c in enumerate(chars))   #生成字符和数字编号的对照表
    int_to_char = dict((i, c) for i, c in enumerate(chars))
    
     

    我们看到,全部的chars:

    In [69]:
    chars
    
    Out[69]:
    ['
    ',
     ' ',
     '!',
     '#',
     '$',
     '%',
     '(',
     ')',
     '*',
     ',',
     '-',
     '.',
     '/',
     '0',
     '1',
     '2',
     '3',
     '4',
     '5',
     '6',
     '7',
     '8',
     '9',
     ':',
     ';',
     '?',
     '@',
     '[',
     ']',
     '_',
     'a',
     'b',
     'c',
     'd',
     'e',
     'f',
     'g',
     'h',
     'i',
     'j',
     'k',
     'l',
     'm',
     'n',
     'o',
     'p',
     'q',
     'r',
     's',
     't',
     'u',
     'v',
     'w',
     'x',
     'y',
     'z',
     '‘',
     '’',
     '“',
     '”',
     'ufeff']
     

    一共有:

    In [70]:
    len(chars)
    
    Out[70]:
    61
     

    同时,我们的原文本一共有:

    In [71]:
    len(raw_text)
    
    Out[71]:
    276830

    我们这里简单的文本预测就是,给了前置的字母以后,下一个字母是谁?

    比如,Winsto, 给出 n Britai 给出 n;如果给Winston会给出空格。

    构造训练测试集

    我们需要把我们的raw text变成可以用来训练的x,y:

    x 是前置字母们 y 是后一个字母

    In [72]:
    seq_length = 100  #规定好前后语境的长度(100步)
    x = []
    y = []
    for i in range(0, len(raw_text) - seq_length):
        given = raw_text[i:i + seq_length]    # 待训练的已知量
        predict = raw_text[i + seq_length]  # 预测量
        x.append([char_to_int[char] for char in given])
        y.append(char_to_int[predict])
    
     

    我们可以看看我们做好的数据集的长相:

    In [73]:
    print(x[:3])   #前三个数组,每个小数组都为100维的
    print(y[:3])
    
     
    [[60, 45, 47, 44, 39, 34, 32, 49, 1, 36, 50, 49, 34, 43, 31, 34, 47, 36, 57, 48, 1, 47, 34, 30, 41, 1, 48, 44, 41, 33, 38, 34, 47, 48, 1, 44, 35, 1, 35, 44, 47, 49, 50, 43, 34, 9, 1, 31, 54, 1, 47, 38, 32, 37, 30, 47, 33, 1, 37, 30, 47, 33, 38, 43, 36, 1, 33, 30, 51, 38, 48, 0, 0, 49, 37, 38, 48, 1, 34, 31, 44, 44, 40, 1, 38, 48, 1, 35, 44, 47, 1, 49, 37, 34, 1, 50, 48, 34, 1, 44], [45, 47, 44, 39, 34, 32, 49, 1, 36, 50, 49, 34, 43, 31, 34, 47, 36, 57, 48, 1, 47, 34, 30, 41, 1, 48, 44, 41, 33, 38, 34, 47, 48, 1, 44, 35, 1, 35, 44, 47, 49, 50, 43, 34, 9, 1, 31, 54, 1, 47, 38, 32, 37, 30, 47, 33, 1, 37, 30, 47, 33, 38, 43, 36, 1, 33, 30, 51, 38, 48, 0, 0, 49, 37, 38, 48, 1, 34, 31, 44, 44, 40, 1, 38, 48, 1, 35, 44, 47, 1, 49, 37, 34, 1, 50, 48, 34, 1, 44, 35], [47, 44, 39, 34, 32, 49, 1, 36, 50, 49, 34, 43, 31, 34, 47, 36, 57, 48, 1, 47, 34, 30, 41, 1, 48, 44, 41, 33, 38, 34, 47, 48, 1, 44, 35, 1, 35, 44, 47, 49, 50, 43, 34, 9, 1, 31, 54, 1, 47, 38, 32, 37, 30, 47, 33, 1, 37, 30, 47, 33, 38, 43, 36, 1, 33, 30, 51, 38, 48, 0, 0, 49, 37, 38, 48, 1, 34, 31, 44, 44, 40, 1, 38, 48, 1, 35, 44, 47, 1, 49, 37, 34, 1, 50, 48, 34, 1, 44, 35, 1]]
    [35, 1, 30]
    
     

    此刻,楼上这些表达方式,类似就是一个词袋,或者说 index。

    接下来我们做两件事:

    1. 我们已经有了一个input的数字表达(index),我们要把它变成LSTM需要的数组格式: [样本数,时间步伐,特征]

    2. 第二,对于output,我们在Word2Vec里学过,用one-hot做output的预测可以给我们更好的效果,相对于直接预测一个准确的y数值的话。

    In [74]:
    n_patterns = len(x)
    n_vocab = len(chars)
    
    # 把x变成LSTM需要的样子
    x = numpy.reshape(x, (n_patterns, seq_length, 1))   #1表示将每个元素变成一维的特征向量。不能省去,否则x原来是100维的,没有办法放到LSTM中
    # 简单normal到0-1之间
    x = x / float(n_vocab)
    # output变成one-hot
    y = np_utils.to_categorical(y)   #否则,y就是一个0-61之间的实数,如果要输出31,此时要求ML得到的预测值应该锁定,30.5-31.5之间,而这对于61长条形的线性预测而言,很难预测而且还不一定能学会
    # one-hot表示的时候可以分别给出61个位置分别为1的概率,此时只需要将概率最大的one-hot向量对应的数值取出即可得到预测值,大大减少运算量,输出也更加简便 #注意:x没用one-hot的原因是我们会采用word2vec来实现同样的效果
    print(x[11]) #每个元素都是100个的一维数组 print(y[11]) #61维的one-hot表达式
     
    [[ 0.80327869]
     [ 0.55737705]
     [ 0.70491803]
     [ 0.50819672]
     [ 0.55737705]
     [ 0.7704918 ]
     [ 0.59016393]
     [ 0.93442623]
     [ 0.78688525]
     [ 0.01639344]
     [ 0.7704918 ]
     [ 0.55737705]
     [ 0.49180328]
     [ 0.67213115]
     [ 0.01639344]
     [ 0.78688525]
     [ 0.72131148]
     [ 0.67213115]
     [ 0.54098361]
     [ 0.62295082]
     [ 0.55737705]
     [ 0.7704918 ]
     [ 0.78688525]
     [ 0.01639344]
     [ 0.72131148]
     [ 0.57377049]
     [ 0.01639344]
     [ 0.57377049]
     [ 0.72131148]
     [ 0.7704918 ]
     [ 0.80327869]
     [ 0.81967213]
     [ 0.70491803]
     [ 0.55737705]
     [ 0.14754098]
     [ 0.01639344]
     [ 0.50819672]
     [ 0.8852459 ]
     [ 0.01639344]
     [ 0.7704918 ]
     [ 0.62295082]
     [ 0.52459016]
     [ 0.60655738]
     [ 0.49180328]
     [ 0.7704918 ]
     [ 0.54098361]
     [ 0.01639344]
     [ 0.60655738]
     [ 0.49180328]
     [ 0.7704918 ]
     [ 0.54098361]
     [ 0.62295082]
     [ 0.70491803]
     [ 0.59016393]
     [ 0.01639344]
     [ 0.54098361]
     [ 0.49180328]
     [ 0.83606557]
     [ 0.62295082]
     [ 0.78688525]
     [ 0.        ]
     [ 0.        ]
     [ 0.80327869]
     [ 0.60655738]
     [ 0.62295082]
     [ 0.78688525]
     [ 0.01639344]
     [ 0.55737705]
     [ 0.50819672]
     [ 0.72131148]
     [ 0.72131148]
     [ 0.6557377 ]
     [ 0.01639344]
     [ 0.62295082]
     [ 0.78688525]
     [ 0.01639344]
     [ 0.57377049]
     [ 0.72131148]
     [ 0.7704918 ]
     [ 0.01639344]
     [ 0.80327869]
     [ 0.60655738]
     [ 0.55737705]
     [ 0.01639344]
     [ 0.81967213]
     [ 0.78688525]
     [ 0.55737705]
     [ 0.01639344]
     [ 0.72131148]
     [ 0.57377049]
     [ 0.01639344]
     [ 0.49180328]
     [ 0.70491803]
     [ 0.8852459 ]
     [ 0.72131148]
     [ 0.70491803]
     [ 0.55737705]
     [ 0.01639344]
     [ 0.49180328]
     [ 0.70491803]]
    [ 0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.
      0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.
      0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.
      1.  0.  0.  0.  0.  0.]
    
     

    模型建造

    LSTM模型构建

    In [ ]:
    model = Sequential()  #前后关系用sequential(),还有一种是图片性质的graphic()
    model.add(LSTM(128, input_shape=(x.shape[1], x.shape[2])))    #第一层为LTSM,放128个神经元,x.shape[1]表示时间步伐100,x.shape[2]表每个中的小特征向量为1
    model.add(Dropout(0.2)) #防止数据集过拟合,每次随机取输出的80%的神经元来表示输出,这样让变化速率减缓,不会很快直接下降到极点
    model.add(Dense(y.shape[1], activation='softmax')) #dense为普通神经网络,在LSTM之后加一个普通神经网络做输出结果的计算。softmax来得出概率
    model.compile(loss='categorical_crossentropy', optimizer='adam') #多元制的交叉熵,adam的学习率可以按照差距来调整步伐,减少过拟合概率

    跑模型

    In [ ]:
    model.fit(x, y, nb_epoch=10, batch_size=32)   #nb_epoch=10表示跑多少次,每个batch中放多少集 
    Epoch 1/10
     21120/276730 [=>............................] - ETA: 1078s - loss: 3.0638
     

    我们来写个程序,看看我们训练出来的LSTM的效果:

    In [ ]:
    def predict_next(input_array):   #将处理后变成数字的数组放入
    # 预处理x x = numpy.reshape(input_array, (1, seq_length, 1)) x = x / float(n_vocab) y = model.predict(x) return y def string_to_index(raw_input): res = [] for c in raw_input[(len(raw_input)-seq_length):]: #利用查找表将输入的字符转化为数字 res.append(char_to_int[c]) return res def y_to_char(y): largest_index = y.argmax() #取y中最大数值的序列号(就是对照表中的数字顺序),来检索出预测的字符 c = int_to_char[largest_index] return c
     

    好,写成一个大程序:

    In [ ]:
    def generate_article(init, rounds=500):
        in_string = init.lower()
        for i in range(rounds):  #rounds需要往后生成多少个字符
            n = y_to_char(predict_next(string_to_index(in_string)))
            in_string += n    #将生成的附在之前的后面,当成下一次的输入,继续下一次的预测生成
        return in_string
    
    In [ ]:
    init = 'Professor Michael S. Hart is the originator of the Project'
    article = generate_article(init)
    print(article)
  • 相关阅读:
    超实用的 Nginx 极简教程,覆盖了常用场景(转)
    阿里云Redis开发规范(转)
    什么是 AQS ?
    缓存穿透、缓存并发、缓存失效之思路变迁(转)
    看不懂JDK8的流操作?5分钟带你入门(转)
    Redis 分布式锁的正确实现方式(转)
    urllib-Proxy
    基本urllib库
    Windows DOS 命令(持续更新...)
    java 位运算符
  • 原文地址:https://www.cnblogs.com/Josie-chen/p/9098604.html
Copyright © 2011-2022 走看看