前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Tensorflow 2.0 Reimagine Plutarch

使用Tensorflow 2.0 Reimagine Plutarch

作者头像
代码医生工作室
发布2019-09-19 10:37:20
1.2K0
发布2019-09-19 10:37:20
举报
文章被收录于专栏:相约机器人相约机器人
作者 | Almis Povilaitis

来源 | Medium

编辑 | 代码医生团队

前言

普鲁塔克的贵族希腊人和罗马人的生活,也被称为平行生活或只是普鲁塔克的生活,是一系列着名的古希腊人和罗马人的传记,从忒修斯和Lycurgus到马库斯安东尼斯。

研究了使用gensim库训练自己的单词嵌入。在这里将主要关注利用TensorFlow 2.0平台的嵌入层一词; 目的是更好地了解该层如何工作以及它如何为更大的NLP模型的成功做出贡献。

为了帮助轻松复制,已将代码改编为Google Colab,并突出显示了该平台的独特之处 - 否则整个代码可以使用Python 3.6+和相关软件包在本地计算机上运行。代码在整篇文章中介绍,但将跳过一些补充或次要代码 - 整个代码可以在Github存储库中找到。

本分析中使用的文本已由Project Gutenberg提供。

https://colab.research.google.com/notebooks/welcome.ipynb

https://github.com/mlai-demo/TextExplore

https://www.gutenberg.org/ebooks/674

把事情搞定

在Colab上,运行时类型更改为GPU,然后导入最新的TensorFlow版本 - 下面的代码片段仅适用于Colab,否则只需使用pip或conda install命令在机器上上传最新的TensorFlow。

代码语言:javascript
复制
from __future__ import absolute_import, division, print_function, unicode_literals
try:
  # %tensorflow_version only exists in Colab.
  %tensorflow_version 2.x
except Exception:
  pass
 
import tensorflow as tf
print(tf.__version__)

还需要操作系统和正则表达式库,然后保存并打印文件路径以供将来参考:

代码语言:javascript
复制
import os
import re
fpath = os.getcwd(); fpath

将文本(Plutarch.txt)导入到Google Colab驱动器中 - 需要记住,文件是短暂的,需要在每次使用平台后更长时间上传它们:

代码语言:javascript
复制
from google.colab import files
 
uploaded = files.upload()
 
for fn in uploaded.keys():
    print('User uploaded file "{name}" with length {length} bytes'.format(
      name=fn, length=len(uploaded[fn])))
  
# Click Files tab - the uploaded file(s) will be there

上面的代码也可以在Colab的Code Snippets选项卡下找到 - 除了许多其他非常有用的代码之外。执行此代码时,将看到Colab上传文件,然后可以单击左侧的Colab Files选项卡以确保该文件与Google的默认Sample Data目录一起存在。

阅读文本并做一些基本的正则表达式操作:

代码语言:javascript
复制
import re
corpus = open(fpath + '/Plutarch.txt',  'rb').read().lower().decode(encoding='utf-8')
corpus = re.sub('\n', ' ', corpus) #remove new line
corpus = re.sub('\r', ' ', corpus) #remove "return"

由于将文本分成句子,因此新行对分析没有意义。此外在使用文本标记器时,注意到“\ r”(表示回车)会创建错误的唯一单词,例如“us”和“us\ r” - 再次,在案例中并不重要。因此,“\ n”和“\ r”都需要去。

建立字典

当向实际的单词嵌入方向前进时,将文本标记为句子:

代码语言:javascript
复制
import nltk
from nltk.tokenize import sent_tokenize
nltk.download('punkt') #need in Colab upon resetting the runtime
 
# tokenize at sentence level
sentences = nltk.sent_tokenize(corpus)
print("The number of sentences is {}".format(len(sentences)))

将看到该文本总共有16,989个句子。接下来需要计算最长句子中的单词数量 - 原因将在后面的教程中变得明显:

代码语言:javascript
复制
from nltk.tokenize import word_tokenize
word_count = lambda sentence: len(word_tokenize(sentence))
longest_sentence = max(sentences, key=word_count)
length_longest_sentence = len(word_tokenize(longest_sentence))
print("The longest sentence has {} words".format(length_longest_sentence))

事实证明,最长的句子是370字长。接下来将整个文本转换为正数,以便可以开始使用TensorFlow讲一种通用语言:

代码语言:javascript
复制
from tensorflow.keras.preprocessing.text import Tokenizer
tokenizer = Tokenizer()
tokenizer.fit_on_texts(sentences)
sent_numeric = tokenizer.texts_to_sequences(sentences)
 
len(tokenizer.word_index.items())

从上面还发现该文本有20241个唯一单词,因为tokenizer每个相同的单词只分配一个数字。为了标准化所有句子的长度(即将输入数据制作成单个,相同的形状张量以使其可处理/更容易为模型 - 在这里满足机器的需求),需要转换表示单词(sent_numeric)到实际字典(word_index)中的数字列表,并添加填充。还可以将截断非常长的句子与填充短句子结合起来,但在这种情况下,只需填充最长句子的长度。

代码语言:javascript
复制
word_index = {k:v for k,v in tokenizer.word_index.items()}
word_index["<PAD>"] = 0
 
vocab_size = len(word_index)
 
maxLen = length_longest_sentence
data = tf.keras.preprocessing.sequence.pad_sequences(sent_numeric,
                                                        value=word_index["<PAD>"],
                                                        padding='post',
                                                        maxlen=maxLen)

由于为填充添加0,词汇量大小(也就是唯一词的数量)将增加1,达到20,242。键入“data [0]”(即第一个句子)以查看填充的第一个句子的样子。

为了能够在单词及其数字表示之间来回转换,需要为查找添加反向单词索引:

代码语言:javascript
复制
reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])
def decode_data(text):
return ' '.join([reverse_word_index.get(i, '?') for i in text])

仔细检查单词索引和转换是有意义的 - 一个错误可能会抛弃整个数据集,使其难以理解。交叉检查的例子 - 转换之前和之后 - 在Github存储库中可用。

模型

最后,构建并运行模型。TensorFlow提供了一个很好的教程,正在适应需求。

https://www.tensorflow.org/beta/tutorials/text/word_embeddings

但首先,只需运行嵌入层,这将产生一个嵌入的数组。已经读过这样的数组可以保存并在另一个模型中使用 - 是的它可以,但是在跳过新模型中的嵌入步骤之外,不太确定实用程序,因为为每个单词生成的向量是对待解决的问题不可知:

代码语言:javascript
复制
import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding
 
embedding_dim = 100
model_justembed = Sequential()
model_justembed.add(Embedding(vocab_size, embedding_dim, input_length=maxLen))
model_justembed.compile('adam', 'mse')
model_justembed.summary()
 
output_array = model.predict(data)

不会花太多时间在上面,而是专注于嵌入只是第一部分的模型。

在导入相关库之后,继续构建新的,非常基本的模型架构:

代码语言:javascript
复制
from tensorflow.keras import layers
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Embedding
 
embedding_dim=100
model = tf.keras.Sequential([
  layers.Embedding(vocab_size, embedding_dim, input_length=maxLen, mask_zero=TRUE),
  layers.GlobalAveragePooling1D(),
  layers.Dense(1, activation='sigmoid')
])
model.summary()

嵌入层 - 通常可以用作模型中的第一层 - 将数字编码的唯一字序列(作为提醒,其中20,241个加上填充编码为零)转换为向量序列,后者被学习为模型训练。每个向量将有100个维度(embedding_dim = 100),因此将得到一个20242 x 100的矩阵。输入长度将固定为最长句子的长度,即370个单词,就像每个单词一样模型认为由于填充而具有相同的大小。Mask_zero通知模型输入值0是否是应该被屏蔽掉的特殊填充值,这在模型可以处理变量输入长度的循环层中特别有用。

在训练之后,具有相似含义的足够有意义的数据词可能具有相似的向量。

这是模型摘要(具有额外密集层的模型位于github存储库中):

在模型摘要中,将看到嵌入层的参数数量是2,024,200,这是嵌入维度100的20,242个字。

前面提到的TensorFlow教程使用评论数据集,每个评论标记为1或0,具体取决于积极或消极的情绪。没有标签的奢侈品,但仍然想要试驾这个模型,所以只需创建一个0的数组并附加到每个句子; 该模型需要这样的结构。这不会是机器智能遭遇无法解决的任务的第一次或最后一次,但仍然需要提供解决方案。训练这个模型:

代码语言:javascript
复制
import numpy as np
adam = tf.keras.optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.0, amsgrad=False)
model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])
batch_size = 16989  #number of sentences
data_labels = np.zeros([batch_size, 1])
history = model.fit(
    data,
    data_labels,
    epochs=200,
    batch_size=batch_size,
verbose = 0)

嵌入式训练。在转向可视化之前,快速检查gensim的单词相似度。首先,需要创建矢量文件 - 将其暂时保存在Colab中或下载到本地机器:

代码语言:javascript
复制
f = open('vectors.tsv' ,'w')
f.write('{} {}\n'.format(vocab_size-1, embedding_dim))
vectors = model.get_weights()[0]
for words, i in tokenizer.word_index.items():
    str_vec = ' '.join(map(str, list(vectors[i, :])))
    f.write('{} {}\n'.format(words, str_vec))
f.close()
# download the file to the local machine by double-clicking the Colab file or using this:
try:
  from google.colab import files
except ImportError:
   pass
else:
  files.download('vectors.tsv')

其次

代码语言:javascript
复制
import gensim
w2v = gensim.models.KeyedVectors.load_word2vec_format('./vectors.tsv', binary=False)
w2v.most_similar('rome')

最后,检查Pompey和Caesar之间的相似性,它们在之前训练过的CBOW模型中显示出很高的相似性:

代码语言:javascript
复制
round(w2v.similarity('pompey', 'caesar'),4)

单词之间的关系很高。此外,正如人们所预料的那样,凯撒与罗马高度相似。

对于那些对更复杂模型感兴趣的人,Github文件中提供了其他变体,包括Recurrent Neural Networks(长短期记忆),但请记住,它们的训练速度比上面的简单模型慢得多。

https://github.com/mlai-demo/TextExplore/blob/master/RePlutarch_TFembPub.ipynb

可视化

对于嵌入的可视化,很难击败TensorFlow投影仪,所以创建矢量和元(即对应于这些矢量的文字)文件供其使用:

https://projector.tensorflow.org/

代码语言:javascript
复制
import io
 
out_v = io.open('vecs.tsv', 'w', encoding='utf-8')
out_m = io.open('meta.tsv', 'w', encoding='utf-8')
for word_num in range(vocab_size): #had vocab_size-2 before
    word = reverse_word_index[word_num]
    embeddings = weights[word_num]
    out_m.write(word + "\n")
    out_v.write('\t'.join([str(x) for x in embeddings]) + "\n")
out_v.close()
out_m.close()

在本地导入文件,然后可以转到TensorFlow的投影仪,上传文件以替换默认数据,并尝试网站上提供的各种选项。以下是文本的整个向量空间的Principal Components Analysis视图:

这里只是100个单词的向量空间,与“罗马”最相似。

结论

在本文中,简要介绍了嵌入层一词在深度学习模型中的作用。在这种模型的上下文中,该层支持解决特定的NLP任务 - 例如文本分类 - 并且通过迭代训练单词向量以最有利于最小化模型损失。一旦模型被训练,就可以通过相似性计算和可视化来检查嵌入层输出。

嵌入层也可用于加载预训练的字嵌入(例如GloVe,BERT,FastText,ELMo),认为这通常是一种更有效的方式来利用需要这种嵌入的模型 - 部分归因于“工业级” “生成它们所需的工作量和数据大小。然而在专门文本的情况下,特别是如果可以训练单词嵌入的语料库相当大,训练自己的嵌入仍然可以更有效。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 相约机器人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档