机器学习带你横扫乐坛,你就是下一个方文山

xiaoxiaokeke 2020-08-04

我太爱北极猴子乐队了,但他们已经很久没有发行新单曲了。久久欠缺精神食粮的我某天晚上突然灵机一动,我可以自给自足呀!于是我写了个简单的代码,用Keras和TensorFlow训练了一个文本生成模型,写出一首全新的北极猴子的歌。

机器学习带你横扫乐坛,你就是下一个方文山

不过条件有限,这玩意儿无法跟真正的北极猴子的歌曲相提并论,但安慰一下长期缺新歌的自己还是可以的。

本文将简单介绍这个代码,完整的代码放在笔者的GitHub上:https://github.com/Rajwrita/Sequence-Models-for-Literature/blob/master/NLP-AM2.0.ipynb。

首先,你得建立一个几乎将全部北极猴子的歌曲包括在内的数据集(https://github.com/Rajwrita/Sequence-Models-for-Literature/blob/master/AM.txt),之后如果继续执行此代码,请尝试使用自己的数据集生成文本。

导入

首先要为深度学习模型导入通用的数据帧操作库以及TensorFlow和Keras库包:

import numpy as np 
from tensorflow.keras.preprocessing.sequence import pad_sequences 
from tensorflow.keras.layers import Embedding, LSTM, Dense, Dropout,Bidirectional 
from tensorflow.keras.preprocessing.text import Tokenizer 
from tensorflow.keras.models import Sequential 
from tensorflow.keras.optimizers import Adam 
from tensorflow.keras import regularizers 
import tensorflow.keras.utils as ku 

接着,导入数据:

data = open('AM.txt').read() 

再给文本装上一个分词器(tokenizer)。分词器可以生成覆盖整个语料库的单词词典,实质上就是键值对。键是单词,而值则是为该单词生成的标记。简言之,分词器将成句的字符串分解为独立的单词,然后赋予每个单词一个唯一整数值。这一步很关键,为后续嵌入层数据的准备打下基础。

获取单词索引的长度,可以得出语料库里单词的总量。在此基础上加1,就可以引入外部词汇。相应的代码如下:

tokenizer = Tokenizer()data = open('AM.txt').read() 
tokenizer.fit_on_texts(corpus) 
total_words = len(tokenizer.word_index) + 1 

再然后,使用token列表创建导入序列。导入序列说白了就是一个python列表,文本语料库的每一行都通过分词器生成一个token列表。是像这样的一行文本:

机器学习带你横扫乐坛,你就是下一个方文山

通过这道程序,就会将其转化为代表这些单词的一串token。数据集的每一行都会这样处理。其代码如下:

input_sequences = [] 
for line in corpus: 
   token_list =tokenizer.texts_to_sequences([line])[0] 
   for i in range(1, len(token_list)): 
      n_gram_sequence = token_list[:i+1] 
      input_sequences.append(n_gram_sequence) 

可以看出,导入序列只是被分解为短语的句子,紧接着要获取语料库中最长句子的长度。这一步很简单,只需要将所有句子遍历循环并找出最长那句即可。

max_sequence_len = max([len(x) for x in input_sequences]) 

现在填充所有序列使它们全部一样长。用零预填充序列,这样更容易提取到标签值,只要抓取最后一个标记就可以得到标签值了。

input_sequences = np.array(pad_sequences(input_sequences,maxlen=max_sequence_len, padding='pre')) 

填充之后,创建预测值和标签值,这样序列基本上就被分解为x数组和y数组了。这里用到的是python的切片属性。代码如下:

predictors, label = input_sequences[:,:-1],input_sequences[:,-1] 

现在,数据已经分为x数组和y数组,可以开始创建神经网络,对给定词组进行分类预测了。

从嵌入层开始

嵌入层是任何一种理解单词的深度学习模型不可或缺的一层,其实际作用是通过赋予同样含义词汇以同样数值,将较高维空间的向量投影到较低维空间,这样就可以直接对向量进行数学运算了。

在一行文本中,它处理了所有词汇并且赋予其在神经网络中的含义。第一个参数处理的是单词,第二个参数则是绘制单词矢量的维数,最后一个参数是输入维度的尺寸,其实就是最长序列的长度减1。减掉1是因为前面为了得到标签值,我们将每个序列的最后一个词砍掉了,所以得到的序列比最大序列长度要小1。

model.add(Embedding(total_words, 100, input_length=max_sequence_len-1)) 

添加LSTM(长短期记忆网络)层

机器学习带你横扫乐坛,你就是下一个方文山
图源:unsplash

LSTM层的细胞状态保存了整个上下文语境,从而保证了对下一个词汇产生影响的不止有相邻词汇。

除了单层的LSTM层,还可以使用堆叠多层的LSTM。使用双向LSTM层,我们可以从头到尾再从尾到头将原始数据导入学习算法中,这有助于神经网络更好地理解文本。双向LSTM还可以帮助神经网络更快收敛。

将返还序列标注设为True,这样就能将序列信息传递到第二层LSTM层,而不是直接到最终状态。

model.add(Bidirectional(LSTM(150, return_sequences = True))) 

接下来,运用密集层进一步捕获线性关系,以上各层的输出将转化为单词概率。softmax激活函数会将所有输入单词概率从(-∞,∞ ) 转化为(0,1)。

model.add(Dense(total_words/2, activation='relu',  
kernel_regularizer=regularizers.l2(0.01)))model.add(Dense(total_words 
,activation='softmax')) 

由于这里做的是categorical分类,所以要将定律设为分类交叉熵。至于优化器,这里选用adam优化器。

最后一步——Epochs

最后要花一点时间训练模型,数据集的数据不多,大概要训练模型500个epoch左右。

history = model.fit(predictors, label, epochs=100, verbose=1) 

要预测的单词越多,产生的乱码也会越多,因为每一个单词都要预测,其下一个和下下个单词也是,那么下一个单词永远比上一个有更多不确定性。来看看网络最后预测出来的文本!

seed_text = "I really like the Arctic Monkeys and  
机器学习带你横扫乐坛,你就是下一个方文山

相关推荐