一、引言
在NLP-统计语言模型中已经简要介绍过语言模型的相关知识,该文中已阐述语言模型的应用场景和一些传统的实现方式,本文接着演示n-gram的另一种实现方式-神经网络,那这样的实现方式就是神经语言模型吗?
按本渣的理解,答案是否定的,神经语言模型是一个类指,其本质是在统计语言模型上的一种延伸和扩展,我可以只考虑上文n个词,也可以考虑下文n个词,也可以基于上下文考虑,具体的情况需要根据需求而定。
二、文本生成实践
1、训练语料
为了和NLP-统计语言模型形成呼应,本文依旧使用文本生成作为案例,依旧照抄语料库(量少更能直观理解过程)。
另外强调一点,本文还是以理解为主,并没有将数据进行训练、验证和测试等拆分!
corpus = '''
这一生原本一个人,你坚持厮守成我们,却小小声牵着手在默认。
感动的眼神说愿意,走进我的人生。
进了门开了灯一家人,盼来生依然是一家人。
确认过眼神,我遇上对的人。
我挥剑转身,而鲜血如红唇。
前朝记忆渡红尘,伤人的不是刀刃,是你转世而来的魂。
青石板上的月光照进这山城,我一路的跟你轮回声,我对你用情极深。
谁在用琵琶弹奏一曲东风破,枫叶将故事染色,结局我看透。
篱笆外的古道我牵着你走过,荒烟漫草的年头,就连分手都很沉默。
'''
2、项目结构
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)
以‘确认过眼神’作为提示语句,测试结果如下:
'''
确认过眼神,
确认过眼神,我
确认过眼神,我遇
确认过眼神,我遇上
确认过眼神,我遇上对
确认过眼神,我遇上对的
确认过眼神,我遇上对的人
确认过眼神,我遇上对的人。
'''
从结果中发现,该模型已经完全记住了训练语料的连接关系,但也因为采样的时候贪婪地采样最大概率词作为预测结果,所以当提示语句在语料库中完全出现过时,预测结果的多样性就受到了抑制。