作者 |Shraddha Anala
编译 |VK
起源 |Towards Data Science
无论咱们是谁,浏览、了解、交换并最终产生新的内容是咱们在职业生存中都要做的事件。
当波及到从给定的文本体中提取有用的特色时,所波及的过程与间断整数向量(词袋)相比是基本不同的。这是因为句子或文本中的信息是以结构化的程序编码的,单词的语义地位传播了文本的意思。
因而,在放弃文本的上下文意义的同时,对数据进行适当示意的双重要求促使我学习并实现了两种不同的 NLP 模型来实现文本分类的工作。
词嵌入是文本中单个单词的密集示意,思考到上下文和其余与之相干的单词。
与简略的词袋模型相比,该实值向量能够更无效地抉择维数,更无效地捕获词与词之间的语义关系。
简略地说,具备类似含意或经常出现在类似上下文中的词,将具备类似的向量示意,这取决于这些词在其含意中的“靠近”或“相距”有多远。
在本文中,我将探讨两个词的嵌入 -
- 训练咱们本人的嵌入
- 预训练的GloVe 词嵌入
数据集
对于这个案例钻研,咱们将应用 Kaggle 的Stack Overflow 数据集(https://www.kaggle.com/imoore…。这个数据集蕴含了 6 万个用户在网站上提出的问题,次要工作是将问题分为 3 类。
当初让咱们看看这个多分类 NLP 我的项目的理论模型自身。
然而,在开始之前,请确保你曾经装置了这些包 / 库。
pip install gensim # 用于 NLP 预处理工作
pip install keras # 嵌入层
1. 训练词嵌入
如果你心愿跳过解释,请拜访第一个模型的残缺代码:https://github.com/shraddha-a…
1)数据预处理
在第一个模型中,咱们将训练一个神经网络来从咱们的文本语料库中学习嵌入。具体地说,咱们将应用 Keras 库为神经网络的嵌入层提供词标识及其索引。
在训练咱们的网络之前,必须确定一些要害参数。这些包含词汇的大小或语料库中惟一单词的数量以及嵌入向量的维数。
以下链接是用于训练和测试的数据集。当初咱们将导入它们,只保留问题和品质列以供剖析:https://www.kaggle.com/imoore…
我还更改了列名并定义了一个函数 text_clean 来清理问题。
# 导入库
# 数据操作 / 解决
import pandas as pd, numpy as np
# 可视化
import seaborn as sb, matplotlib.pyplot as plt
# NLP
import re
from nltk.corpus import stopwords
from gensim.utils import simple_preprocess
stop_words = set(stopwords.words('english'))
# 导入数据集
dataset = pd.read_csv('train.csv')[['Body', 'Y']].rename(columns = {'Body': 'question', 'Y': 'category'})
ds = pd.read_csv('valid.csv')[['Body', 'Y']].rename(columns = {'Body': 'question', 'Y': 'category'})
# 清理符号和 HTML 标签
symbols = re.compile(pattern = '[/<>(){}\[\]\|@,;]')
tags = ['href', 'http', 'https', 'www']
def text_clean(s: str) -> str:
s = symbols.sub(' ', s)
for i in tags:
s = s.replace(i, ' ')
return ' '.join(word for word in simple_preprocess(s) if not word in stop_words)
dataset.iloc[:, 0] = dataset.iloc[:, 0].apply(text_clean)
ds.iloc[:, 0] = ds.iloc[:, 0].apply(text_clean)
# 训练和测试集
X_train, y_train = dataset.iloc[:, 0].values, dataset.iloc[:, 1].values.reshape(-1, 1)
X_test, y_test = ds.iloc[:, 0].values, ds.iloc[:, 1].values.reshape(-1, 1)
# one-hot 编码
from sklearn.preprocessing import OneHotEncoder as ohe
from sklearn.compose import ColumnTransformer
ct = ColumnTransformer(transformers = [('one_hot_encoder', ohe(categories = 'auto'), [0])],
remainder = 'passthrough')
y_train = ct.fit_transform(y_train)
y_test = ct.transform(y_test)
# 设置参数
vocab_size = 2000
sequence_length = 100
如果你浏览原始数据集,你会发现 HTML 标记中蕴含的问题,例如,<p>…question</p>。此外,还有一些词,如 href,https 等,在整个文本中都有,所以我要确保从文本中删除这两组不须要的字符。
Gensim 的 simple_preprocess 办法返回一个小写的标记列表,去掉重音符号。
在这里应用 apply 办法将通过预处理函数迭代运行每一行,并在持续下一行之前返回输入。对训练和测试数据集利用文本预处理性能。
因为在因变量向量中有 3 个类别,咱们将利用 one-hot 编码并初始化一些参数以备当前应用。
2)标识化
接下来,咱们将应用 Keras Tokenizer 类将单词组成的问题转换成一个数组,用它们的索引示意单词。
因而,咱们首先必须应用 fit_on_texts 办法,从数据集中呈现的单词构建索引词汇表。
在建设词汇表之后,咱们应用 text_to_sequences 办法将句子转换成示意单词的数字列表。
pad_sequences 函数确保所有察看值的长度雷同,能够设置为任意数字或数据集中最长问题的长度。
咱们先前初始化的 vocab_size 参数只是咱们词汇表的大小(用于学习和索引)。
# Keras 的标识器
from keras.preprocessing.text import Tokenizer
tk = Tokenizer(num_words = vocab_size)
tk.fit_on_texts(X_train)
X_train = tk.texts_to_sequences(X_train)
X_test = tk.texts_to_sequences(X_test)
# 用 0 填充所有
from keras.preprocessing.sequence import pad_sequences
X_train_seq = pad_sequences(X_train, maxlen = sequence_length, padding = 'post')
X_test_seq = pad_sequences(X_test, maxlen = sequence_length, padding = 'post')
3)训练嵌入层
最初,在这一部分中,咱们将构建和训练咱们的模型,它由两个次要层组成,一个嵌入层将学习下面筹备的训练文档,以及一个密集的输入层来实现分类工作。
嵌入层将学习单词的示意,同时训练神经网络,须要大量的文本数据来提供精确的预测。在咱们的例子中,45000 个训练察看值足以无效地学习语料库并对问题的品质进行分类。咱们将从指标中看到。
# 训练嵌入层和神经网络
from keras.models import Sequential
from keras.layers import Embedding, Dense, Flatten
model = Sequential()
model.add(Embedding(input_dim = vocab_size, output_dim = 5, input_length = sequence_length))
model.add(Flatten())
model.add(Dense(units = 3, activation = 'softmax'))
model.compile(loss = 'categorical_crossentropy',
optimizer = 'rmsprop',
metrics = ['accuracy'])
model.summary()
history = model.fit(X_train_seq, y_train, epochs = 20, batch_size = 512, verbose = 1)
# 实现训练后保留模型
#model.save("model.h5")
4)评估和度量图
剩下的就是评估咱们的模型的性能,并绘制图来查看模型的准确性和损失指标是如何随工夫变动的。
咱们模型的性能指标显示在上面的屏幕截图中。
代码与上面显示的代码雷同。
# 在测试集上评估模型的性能
loss, accuracy = model.evaluate(X_test_seq, y_test, verbose = 1)
print("\nAccuracy: {}\nLoss: {}".format(accuracy, loss))
# 画出准确度和损失
sb.set_style('darkgrid')
# 1) 准确度
plt.plot(history.history['accuracy'], label = 'training', color = '#003399')
plt.legend(shadow = True, loc = 'lower right')
plt.title('Accuracy Plot over Epochs')
plt.show()
# 2) 损失
plt.plot(history.history['loss'], label = 'training loss', color = '#FF0033')
plt.legend(shadow = True, loc = 'upper right')
plt.title('Loss Plot over Epochs')
plt.show()
以下是训练中准确度的进步
20 个 epoch 的损失图
2. 预训练的 GloVe 词嵌入
如果你只想运行模型,这里有残缺的代码:https://github.com/shraddha-a…
代替训练你本人的嵌入,另一个抉择是应用预训练好的词嵌入,比方 GloVe 或 Word2Vec。在这一部分中,咱们将应用在 Wikipedia+gigaword5 上训练的 GloVe 词嵌入;从这里下载:https://nlp.stanford.edu/proj…
i)抉择一个预训练的词嵌入,如果
你的数据集是由更“通用”的语言组成的,一般来说你没有那么大的数据集。
因为这些嵌入曾经依据来自不同起源的大量单词进行了训练,如果你的数据也是通用的,那么预训练的模型可能会做得很好。
此外,通过预训练的嵌入,你能够节省时间和计算资源。
ii)抉择训练你本人的嵌入,如果
你的数据(和我的项目)是基于一个利基行业,如医药、金融或任何其余非通用和高度特定的畛域。
在这种状况下,个别的词嵌入表示法可能不适宜你,并且一些单词可能不在词汇表中。
须要大量的畛域数据来确保所学的词嵌入可能正确地示意不同的单词以及它们之间的语义关系
此外,它须要大量的计算资源来浏览你的语料库和建设词嵌入。
最终,是依据已有的数据训练你本人的嵌入,还是应用预训练好的嵌入,将取决于你的我的项目。
显然,你依然能够试验这两种模型,并抉择一种精度更高的模型,但下面的教程只是一个简化的教程,能够帮忙你做出决策。
过程
后面的局部曾经采取了所需的大部分步骤,只需进行一些调整。
咱们只须要构建一个单词及其向量的嵌入矩阵,而后用它来设置嵌入层的权重。
所以,放弃预处理、标识化和填充步骤不变。
一旦咱们导入了原始数据集并运行了后面的文本清理步骤,咱们将运行上面的代码来构建嵌入矩阵。
以下决定要嵌入多少个维度(50、100、200),并将其名称蕴含在上面的门路变量中。
# # 导入嵌入
path = 'Full path to your glove file (with the dimensions)'
embeddings = dict()
with open(path, 'r', encoding = 'utf-8') as f:
for line in f:
# 文件中的每一行都是一个单词外加 50 个数(示意这个单词的向量)values = line.split()
# 每一行的第一个元素是一个单词,其余的 50 个是它的向量
embeddings[values[0]] = np.array(values[1:], 'float32')
# 设置一些参数
vocab_size = 2100
glove_dim = 50
sequence_length = 200
# 从语料库中的单词构建嵌入矩阵
embedding_matrix = np.zeros((vocab_size, glove_dim))
for word, index in word_index.items():
if index < vocab_size:
try:
# 如果给定单词的嵌入存在,检索它并将其映射到单词。embedding_matrix[index] = embeddings[word]
except:
pass
构建和训练嵌入层和神经网络的代码应该稍作批改,以容许将嵌入矩阵用作权重。
# 神经网络
from keras.models import Sequential
from keras.layers import Embedding, Dense, Flatten
model = Sequential()
model.add(Embedding(input_dim = vocab_size,
output_dim = glove_dim,
input_length = sequence))
model.add(Flatten())
model.add(Dense(units = 3, activation = 'softmax'))
model.compile(optimizer = 'adam', metrics = ['accuracy'], loss = 'categorical_crossentropy')
# 加载咱们预训练好的嵌入矩阵到嵌入层
model.layers[0].set_weights([embedding_matrix])
model.layers[0].trainable = False # 训练时权重不会被更新
# 训练模型
history = model.fit(X_train_seq, y_train, epochs = 20, batch_size = 512, verbose = 1)
上面是咱们预训练的模型在测试集中的性能指标。
论断
从两个模型的性能指标来看,训练嵌入层仿佛更适宜这个数据集。
一些起因可能是
1)对于堆栈溢出的大多数问题都与 IT 和编程无关,也就是说,这是一个特定畛域的场景。
2)45000 个样本的大型训练数据集为咱们的嵌入层提供了一个很好的学习场景。
心愿本教程对你有帮忙,谢谢你的浏览,下一篇文章再见。
原文链接:https://towardsdatascience.co…
欢送关注磐创 AI 博客站:
http://panchuang.net/
sklearn 机器学习中文官网文档:
http://sklearn123.com/
欢送关注磐创博客资源汇总站:
http://docs.panchuang.net/