作者:GjZero

标签:Python, Keras, 语言模型, 日语

本文约2400字,建议阅读10分钟

本文介绍了语言模型,并介绍如何用MeCab和Keras实现一个日文的神经网络语言模型。(为什么是日文呢?纯属作者兴趣)

基于神经网络的语言模型

依据Wikepedia,语言模型的定义是“句子们的概率分布”。给定一个长度为m的句子,则可以有概率

P(w_1,...,w_m)

由条件概率公式有

P(w_1,...w_m) = \prod_{i=1}^mP(w_i|w_1,...w_{i-1})

n-gram模型假设,第i个词语的概率分布只和前面固定的n个词有关(Markov性),那么就有

P(w_1,...w_m) = \prod_{i=1}^mP(w_i|w_1,...w_{i-1}) \approx \prod_{i=1}^mP(w_i|w_{i-(n-1)},...,w_{i-1})

所以估计

P(w_1,...w_m)

的任务变成了估计

P(w_i|w_{i-(n-1)},...,w_{i-1})

用传统的统计方法面临着

  • 维度灾难(当n变大,存储空间不够)

  • n元组并不会在语料库中全部出现

所以这里使用神经网络近似函数

P(w_i|w_{i-(n-1)},...,w_{i-1})

神经网络方法解决了如上两个困难

  • 当n变大,神经网络的参数以线性级别增长

  • n元组虽然没有全部出现,但词向量可以捕捉到不同的词可能代表的相似的含义

一个传统的基于神经网络的模型结构如下图所示:

用MeCab实现日语分词

MeCab(めかぶ)是一款日语分词工具。Linux用户可以用如下指令安装MeCab:

sudo apt-get install mecab mecab-ipadic-utf8 libmecab-dev swig

pip install mecab-python3

MeCab可以对一个句子进行分词,并分析各词的词性。对于句子“すもももももももものうち”有

すもももももももものうち

すもも  名詞,一般,*,*,*,*,すもも,スモモ,スモモ

も      助詞,係助詞,*,*,*,*,も,モ,モ

もも    名詞,一般,*,*,*,*,もも,モモ,モモ

も      助詞,係助詞,*,*,*,*,も,モ,モ

もも    名詞,一般,*,*,*,*,もも,モモ,モモ

の      助詞,連体化,*,*,*,*,の,ノ,ノ

うち    名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ

EOS

为了将分析的结果转化为分词结果,可用如下的`mecab_to_text`函数,则会输出“すもも も もも も もも の うち”。

Python

def mecab_to_text(sentence_list):

"""

:param sentence_list: A list of sentences or one single sentence.

:return: A list of segmented sentences.

:note: Use mecab to segment a list of sentences or one single sentence in Japanese.

"""

import MeCab

mecab = MeCab.Tagger("-Ochasen")

single_flag = False

if isinstance(sentence_list, str):

sentence_list = [sentence_list]

single_flag = True

ret_list = []

for sentence in sentence_list:

text_list = []

m = mecab.parseToNode(sentence)

while m:

text_list.append(m.surface)

m = m.next

seg_sentence = " ".join(text_list).strip()

ret_list.append(seg_sentence)

if single_flag:

return ret_list[0]

return ret_list

模型构建

我们需要先构建我们的训练样本,语料库来自日语小说。对语料库中的句子用MeCab进行分词之后,用给定的窗宽k分割出训练集。训练集中的词和词向量进行对应为300维的向量。这样训练集中的每一个x(特征)对应一个(k-1)×300维的矩阵,每一个y(结果)对应一个one-hot的向量。

语料库

语料库是来自于网络上的日语小说,因为版权因素这里不提供下载。用什么样的小说并不会太影响我们后续的过程。在这里实现了`load_text`,`make_word_dictionary`,`clear_dictionary`;分别用来读入语料库,从分好词的语料库中生成词典,清理词典中在词向量里没有出现的词。

Python

def load_text(use_length=-1, min_len=10):

start = time.clock()

japanese_text_path = "H:\\Work\\JapaneseModel\\Japanese_book\\"

text_list = []

if use_length == -1:

for file in os.listdir(japanese_text_path):

with open(japanese_text_path + file, 'r', encoding='utf-8') as f:

for line in f.readlines():

line_use = line.strip()

if len(line_use) > min_len:

text_list.append(line_use)

else:

counter = 0

for file in os.listdir(japanese_text_path):

with open(japanese_text_path + file, 'r', encoding='utf-8') as f:

for line in f.readlines():

line_use = line.strip()

if len(line_use) > min_len:

text_list.append(line_use)

counter += 1

if counter == use_length:

print("Japanese text loaded %d lines."%use_length)

elapsed = time.clock() - start

print("Time used:", round(elapsed, 3))

return text_list

print("Japanese text loaded all lines.")

elapsed = time.clock() - start

print("Time used:", round(elapsed, 3))

return text_list

def make_word_dictionary(split_text_list, lower_bound=100):

start = time.clock()

word_dictionary = dict()

for sentence in split_text_list:

sentence_use = sentence.split(" ")

for word in sentence_use:

if not word in word_dictionary:

word_dictionary[word] = 1

else:

word_dictionary[word] += 1

print("Word dictionary established.")

elapsed = time.clock() - start

print("Time used:", round(elapsed, 3))

if lower_bound > 0:

pop_list = []

for word in word_dictionary:

if word_dictionary[word] < lower_bound:

pop_list.append(word)

for word in pop_list:

word_dictionary.pop(word)

word_list = []

for word in word_dictionary:

word_list.append(word)

return word_list

def clear_dictionary(dictionary, embedding_dictionary):

ret_list = []

for word in dictionary:

if word in embedding_dictionary:

ret_list.append(word)

return ret_list

实现了这几个函数以后,就可以用如下的方式读入语料库。

Python

japanese_text = load_text(use_text_length)

split_japanese_text = mecab_to_text(japanese_text)

dictionary = make_word_dictionary(split_japanese_text, lower_bound=10)

dictionary = clear_dictionary(dictionary, embeddings_index)

词向量

我们使用facebook在fastText项目中预训练好的日语300维词向量,下载地址点击[这里](https://s3-us-west-1.amazonaws.com/fasttext-vectors/word-vectors-v2/cc.ja.300.vec.gz)。因为该文件的第一行保存了词向量文件的信息,你应该手动删除该行,然后用`load_embedding`函数来读取词向量。

Python

def load_embedding():

start = time.clock()

"""

Total 2000000 words in this embedding file, 300-d. It is float16 type.

The first line is "2000000 300".

You should delete this line.

"""

EMBEDDING_FILE = 'H:\\Work\\cc.ja.300.vec'

def get_coefs(word, *arr): return word, np.asarray(arr, dtype='float16')

embeddings_index = dict(get_coefs(*o.strip().split(" ")) for o in open(EMBEDDING_FILE, 'r', encoding="utf-8"))

elapsed = time.clock() - start

print("Word vectors loaded.")

print("Time used:", round(elapsed, 3))

return embeddings_index

生成训练集

假设我们的窗宽为k,那么我们的训练集由k-1个词组成x_train,由之后连接的词组成y_train。如果k=3,我们语料库中的一个句子为“a bb ccc d”, 其中a、bb、ccc、d分别是4个词。那么我们将这个句子前面连接k-1=2个“space”,结尾连接一个“eol”,扩充为“space space a bb ccc d eof”。这样可以得到如下的训练样本:

x1|x2|y

:- | :- | :-

space|space|a

space|a|bb

a|bb|ccc

bb|ccc|d

ccc|d|eol

“generate_train”函数实现了上述生成训练集的算法

Python

def generate_train(window, end_index, text_seq):

prefix = [0] * (window - 1)

suffix = [end_index]

x_list = []

y_list = []

for seq in text_seq:

if len(seq) > 1:

seq_use = prefix + seq + suffix

# print(seq_use)

for i in range(len(seq_use) - window + 1):

x_list.append(seq_use[i: i + window - 1])

y_list.append(seq_use[i + window - 1])

# print(seq_use[i: i + window])

return x_list, y_list

构建神经网络模型

和传统的神经网络语言模型有所不同:先将x映射为词向量,连接双层BiLSTM作为隐藏层,再连接一个Softmax来预测下一个词是什么。在Keras中,实现BiLSTM非常容易。因为`CuDNNLSTM`的实现比`LSTM`要快很多,推荐安装cudnn来使用这个函数。加入了一些`Dropout`层来避免过拟合。

Python

# Model

inp = Input(shape=(window - 1,))

x = Embedding(nb_words, 300, trainable = True, weights=[embedding_matrix])(inp)

x = Bidirectional(CuDNNLSTM(128, return_sequences=True))(x)

x = Dropout(0.1)(x)

x = Bidirectional(CuDNNLSTM(128, return_sequences=False))(x)

x = Dropout(0.1)(x)

x = Dense(128, activation="relu")(x)

x = Dropout(0.1)(x)

x = Dense(nb_words, activation="softmax")(x)

model = Model(inputs=inp, outputs=x)

opt = keras.optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999,

epsilon=None, decay=0.0, amsgrad=False)

model.compile(loss='categorical_crossentropy',

optimizer=opt, metrics=['accuracy'])

history = LossHistory()

epoch_nb = 80 # 40 is enough

batch = 64

model.fit(x_train, y_train, batch_size=batch, epochs=epoch_nb, verbose=1,

validation_data=(x_test, y_test), callbacks=[history])

随机生成句子

用`predict_random_sentence`函数来生成随机句子,其中的`reverse_index`保存了从语料库生成的词典中的词和序号的一一对应。若将[0,0,0,0]更改为其他数字,即可生成给定开头的句子。

Python

def predict_random_sentence(new=[0] * (window - 1)):

sentence = reverse_index[new[0]] + reverse_index[new[1]] + reverse_index[new[2]] + reverse_index[new[3]]

while new[-1] != end_index:

prob = model.predict(np.asarray([new]))[0]

new_predict = int(random.choices(word_ind, weights=prob)[0])

sentence += reverse_index[new_predict]

new = new[1:] + [new_predict]

return sentence

predict_random_sentence([0,0,0,0])

保存模型

保存模型到本地,以后就可以直接调用,避免重复训练。上文中提到的tokenizer和神经网络模型都需要保存。

Python

with open("../result/tokenizer.pkl", "wb") as handle:

pickle.dump(tokenizer, handle, protocol=pickle.HIGHEST_PROTOCOL)

model.save('../model/language_model.model')

效果展示

我们训练了80个epoch,使用了20000句话进行训练,选择的窗宽为5。以下是从日文语言模型中随机生成的一些句子。

'「なんだろう。僕が仕事を休みになり、でもまあ……見てた」'

'アグライアはグラスをじっと見つめた。'

'それにしても、それを使って、ジークをから表情になって猫のように《さ》がを受けた。'

'森そうだ、そんなことか」'

'真剣で命をように、そのの人は、辻宮氏はだいたい邸にてあげた《と?》みをうとした。「そんな顔だって今?」'

'佳澄ちゃんが……俺とさっきに言わせて下さい。'

'沙耶「まあ、沙耶ねえ先に戻ることにになってきます?」'

'「最近はどうしてそういうつもりじゃないでしょうね」'

简单的翻译一下生成的句子(日语水平比较烂,可能翻译错了)

'怎么说呢。我虽然下班了,但还是……看到了'

'Agria凝视着玻璃杯'

'即使如此,使用它,Sieg看来像猫一样的表情接受了さ'

'像树林啊,是这样吗'

这句话实在不太通顺……

'佳澄酱,请给我说下刚才的事情'

'沙耶:“嘛,沙耶先回去了啊?”'

'最近为什么不打算这样做了呢'

总体来说,该语言模型可以生成出一些通顺的话语。以上都是从空句子开始生成的,也可以改变生成句子的开头。

项目地址及参考文献

完整的项目代码见

[GitHub]

(https://github.com/GanjinZero/DeepLearningPlayground/tree/master/code/Language%20Model)

[Language_model]

(https://en.wikipedia.org/wiki/Language_model)

[MeCab]

(http://taku910.github.io/mecab/)

[fastText]

(https://github.com/facebookresearch/fastText/blob/master/docs/crawl-vectors.md)

【作者简介】

GjZero,清华大学统计中心博士二年级在读。研究方向是医学信息学中的自然语言处理。兴趣是扑克、麻将等和博弈论有关的运动。

编辑:王菁

校对:林亦霖

手把手教 | 使用Keras构造日文的神经网络语言模型(内附源码)相关推荐

  1. 手把手教 | 使用Bert预训练模型文本分类(内附源码)

    作者:GjZero 标签:Bert, 中文分类, 句子向量 本文约1500字,建议阅读8分钟. 本文从实践入手,带领大家进行Bert的中文文本分类和作为句子向量进行使用的教程. Bert介绍 Bert ...

  2. paddle 标注_一看就会,手把手教你编程,批量文章标注拼音(附源码)

    文/IT可达鸭 图/IT可达鸭.网络 前言 是不是学了Python之后,苦于没有项目练手?是不是看了很多关于编程视频,等到自己动手时,却怎么也做不出一个项目? 工作在一线的老程序员告诉你,别慌,让我手 ...

  3. 百度收录批量查询_峰少课堂 手把手教你操作百度霸屏!(内附详细操作笔记!)...

    今天峰少课堂给大家讲解的是操作百度霸屏,一个月赚6000块!(内附详细操作笔记!) 看完可以直接拿去实操,没有效果你来找我啊哈哈哈哈哈!!之前我就是在公司摸索出了这套方法,然后专门找了一个文案做百度霸 ...

  4. 手把手教你使用LabVIEW OpenCV dnn实现图像分类(含源码)

    文章目录 前言 一.什么是图像分类? 1.图像分类的概念 2.MobileNet简介 二.使用python实现图像分类(py_to_py_ssd_mobilenet.py) 1.获取预训练模型 2.使 ...

  5. 前向型神经网络之BPNN(附源码)

    BPNN 人工神经网络   我们知道,人的脑袋具有很强的学习.记忆.联想等功能,虽然人类还没有完全搞明白人类的大脑,但是我们已经知道它的基本单位就是一个个神经元,即一个神经细胞,人的神级细胞个数大约为 ...

  6. 手把手教你使用热敏电阻NTC,产品级精度±0.1℃以内,简单明了,内附源码详解,方便移植

    NTC Author:家有仙妻谢掌柜 Date:2021/1/19 一.背景 前一段疫情期间,就考虑到用NTC来做测温功能,写在这里记录自己的成长历程,也分享出去供大家参考! NTC(Negative ...

  7. 手把手教你调试构建一个Vue/小程序商城项目源码

    下面将详细的介绍weiphp5.0商城项目的调试打包上线的流程: 安装NodeJs/NPM 安装CNPM(可忽略) 运行项目 打包上线项目 1. 安装NodeJs 推荐到NodeJS的官网下载安装包 ...

  8. c语言中strtok函数详解,手把手教你自主实现字符串切割函数,内附详细代码。

    函数功能简介: 对字符串str进行切割,切割的标志为字符指针q指向的这两个字符 "# *":. 但是在对这个函数进行调用时,只有在第一次调用时,才会将str这个字符串的首地址传递过 ...

  9. AI预测彩票,使用chatgpt和lstm神经网络(文末附源码)

    提示:经过2个月的使用AI预测彩票的测试写一篇文章记录下心路历程 文章目录 前言 一.什么是lstm和chatgpt? 二.chat使用步骤 1. wetab浏览器插件 2. 整理的训练话术如下(重点 ...

最新文章

  1. Chrome 浏览器跨域和安全访问问题 使用 chrome的命令行标记:disable-web-security 参数联调线上数据...
  2. 第十六届智能车竞赛浙江赛区比赛胜利结束
  3. 线程:synchronized
  4. 多图 | 600岁“网红”的10亿+营收变现(结尾有彩蛋)
  5. 设计模式 分类和原则
  6. c语言 将点同时保证x坐标从小到大,y坐标从小到大地排序,C语言第五六次作业.ppt...
  7. python爬虫自动更换ip_Python 爬虫使用动态切换ip防止封杀
  8. python3.7.2怎么用不了pillow_python怎么加载Pillow包
  9. Atitit 个人 企业 政府 等组织 财政收入分类与提升途径attilax总结 1.1. 国家财政收入分类 1 1.2. 企业收入分类 1 1.3. 个人收入分类 1 1.1.国家财政收入分类
  10. 梨花带雨html音乐播放器源码,梨花带雨 - 雨陌文化传媒 - 5SING中国原创音乐基地...
  11. 基于MATLAB/GUI的自组网仿真平台,对比leach,ADOV协议
  12. 浅谈:Java和C语言各自的学习难度
  13. QT5+zint库实现条形码条形码(一)
  14. Anaconda 安装及使用
  15. OC5038内置 MOS 开关降压型 LED 恒流驱动器
  16. Tomcat环境变量配置(转载)
  17. [B站视频]Python爬虫技术5天速成
  18. 架构师接龙 岑文初VS. 杨海朝_系统架构
  19. 《LeetCode零基础指南》导读
  20. Fully-Convolutional Siamese Networks for Object Tracking基于全卷积孪生网络的目标跟踪算法SiameseFC

热门文章

  1. [转] composer - 文档 - 命令行
  2. github 项目绑定自己的域名
  3. html从入门到卖电脑(六)
  4. node-webkit教程(16)调试typescript
  5. 添物 不花钱学计算机及编程(预备篇)— 编译原理
  6. 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(32)-swfupload多文件上传[附源码]...
  7. 用鼠标拖动图片的JS代码
  8. javascript 中的eval方法 小窍门
  9. easyui datagrid b表格中的内容显示null_扫盲 | 实际工作中,B 端设计都在做什么?...
  10. java流的写法_java IO-过滤流类的写法