人工智能(10)——————自然语言处理
声明
以下内容均来自B站吴恩达教授的视频以及西瓜书和众多前辈的学习成果总结,仅记录本人的大模型学习过程,如有侵权立马删除。言论仅代表自身理解,如有错误还请指正。
正文
简介
其实在现在的人工智能领域,很多东西都是相互关联,相互促进的。比如机器学习可以引入到自然语言处理,计算机视觉等多个类别当中,而自然语言处理中特有的seq2seq方法也可以用于机器学习当中。但是根本上这些类别都存在自己独有之处。
自然语言处理(Natural Language Processing,简称NLP)是指计算机科学与人工智能领域的一门学科,它研究和开发用于使计算机能够理解、处理和生成人类自然语言的技术和方法。比如我们编程开发,目的就是将人类说出来的语言,也就是要达到的目标,转化为计算机能识别的语言并运算。而NLP就是为了直接将人类语言传入计算机,计算机通过NLP训练好的模型可以直接分析人类语言,而不用经过编程语言的二次识别。
NLP涉及多个子领域,包括语音识别、语音合成、文本分析、机器翻译、情感分析等。其核心目标是建立能够理解人类语言的计算机系统,使计算机能够自动地从文本中提取和解析信息,并能够正确地理解和回应人类的自然语言输入。
在自然语言处理中,常见的任务包括:
- 词法分析:将文本切分成单词或者词汇单元。
- 句法分析:分析句子的结构以及词与词之间的关系。
- 语义分析:理解和解释句子的意义、语义角色标注等。
- 语音识别:将人类的语音转化为文本的过程。
- 机器翻译:将一种语言的文本转化为另一种语言的文本。
主要划分为了四大类:
- 类别到序列
- 序列到类别
- 同步的序列到序列
- 异步的序列到序列
其中类别到序列或者序列到类别就是等于从标签转为数据或者从数据转为标签。处于正常的文本处理范围。而最重要的是seq2seq,也就是序列到序列,将数据转为另一个数据,这就需要人工智能的帮助了。
在大规模发展的今天,怎么处理数据已经不再是NLP的重难点,而转变为以下语言的复杂度问题:
- 语言是没有规律的,或者说规律是错综复杂的。
- 语言是可以自由组合的,可以组合复杂的语言表达。
- 语言可以任意的发明创造一些新的表达方式。
- 语言需要联系到实践知识,有一定的知识依赖。
- 语言的使用要基于环境和上下文。
传统的语言处理
传统的语言处理过程是指在机器学习和自然语言处理领域中使用传统的方法来处理文本数据。这些方法通常基于规则和统计模型,而不是基于深度学习和神经网络。
传统的语言处理过程通常包括以下步骤:
-
分词(Tokenization):将文本分割成一个个词语或符号。
-
词性标注(Part-of-speech Tagging):为每个词语标注其词性,如名词、动词、形容词等。
-
句法分析(Parsing):分析句子的语法结构,如句子成分、依存关系等。
-
语法规则(Grammar Rules):使用预定义的语法规则来分析和处理文本数据。
-
语义分析(Semantic Analysis):理解文本的意义和上下文关系,如词义消歧、语义角色标注等。
-
文本分类(Text Classification):将文本分成不同类别,如情感分类、主题分类等。
-
命名实体识别(Named Entity Recognition):识别文本中的命名实体,如人名、地名、组织名等。
传统的语言处理方法通常需要人工参与构建规则和特征工程的过程,对于复杂的语言现象和大规模的文本数据来说,往往需要大量的时间和精力来进行开发和维护。随着深度学习和神经网络的发展,传统的语言处理方法逐渐被深度学习模型取代,因为深度学习在一定程度上能够自动学习和发现特征,并且具有更好的性能。
seq2seq模型
Seq2Seq是一种序列到序列(Sequence-to-Sequence)模型,也称为编码器-解码器(Encoder-Decoder)模型。它被广泛应用于机器翻译、文本摘要、对话生成等任务中。
Seq2Seq模型由两个重要的组件组成,即编码器和解码器。编码器将输入序列转化为一个固定长度的向量(上下文向量),解码器利用该向量生成输出序列。
编码器使用循环神经网络(RNN)或者卷积神经网络(CNN)来处理可变长度的输入序列。每个输入单词经过嵌入层后,被逐个输入到RNN或CNN中。RNN会保留和传递隐含状态,而CNN则会提取局部特征。最后一个时间步的隐含状态或者CNN的输出被用作上下文向量。
解码器也是一个RNN,它以编码器的上下文向量作为输入,生成输出序列。在训练过程中,解码器每个时间步的输入是上一个时间步的输出,通过在每个时间步预测下一个输出。
Seq2Seq模型的训练目标是最大化生成序列的概率。在训练过程中,解码器以目标序列的真实标签作为输入,通过最小化生成序列与目标序列之间的差异来调整模型参数。
在应用阶段,Seq2Seq模型可以通过贪婪搜索或束搜索等方法生成输出序列。贪婪搜索每个时间步选择概率最高的词汇作为输出,而束搜索则考虑多个潜在的输出序列,并根据一定的准则选择最优的序列。
Seq2Seq模型的优点是能够处理可变长度的输入输出序列,并且可以处理更复杂的语言结构和上下文依赖关系。然而,它也存在一些挑战,如处理长序列时可能存在信息丢失和模型训练困难等问题。因此,后续的改进模型如Transformer逐渐取代了Seq2Seq作为机器翻译等任务的主流模型。
transformer模型
这个在上一篇已经详细介绍过了,就是在seq2seq的基础上,基于深度学习的发展而衍生出来的新模型,在自然语言处理方面有着非常出色的效果。
具体处理步骤和代码流程
1、数据预处理和词法分析
首先利用爬虫或者自己的数据库等方式获取和清洗原始语言数据,包括文本、语料库或语音数据等,对于获取到的文本或者音频数据,我们要将其处理并转化为可被模型识别的向量形式。那么数据预处理阶段后,我们就可以自由选取处理方法对数据进行处理,如采样分析,去噪声,统一语干,去除停用词等。
比如我利用paddle库形成的tokennizer分词器,再结合统一语干,去除停用词等方法,就可以很好的对原始数据进行词法分析。
from spellchecker import SpellChecker
from sklearn.cluster import KMeans
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.preprocessing import LabelEncoder
nltk.download('wordnet')
# 数据预处理
# 加载停用词列表
def load_stopwords():
with open('stopwords.txt', 'r', encoding='utf-8') as f:
stopwords_list = [line.strip() for line in f.readlines()]
return set(stopwords_list)
# 纠正错别字
def correct_spelling(text):
spell = SpellChecker()
words = text.split()
corrected_words = []
for word in words:
corrected_word = spell.correction(word) if spell.unknown([word]) else word
corrected_words.append(corrected_word)
safe_list = [item if item is not None else "" for item in corrected_words]
return ' '.join(safe_list)
# 统一语干
def lemmatize_text(text):
lemmatizer = WordNetLemmatizer()
words = text.split()
lemmatized_words = [lemmatizer.lemmatize(word) for word in words]
return ' '.join(lemmatized_words)
2、特征提取
在自然语言处理中,特征提取是将文本数据转化为数值或向量表示的过程。特征提取的目的是捕捉文本中的有意义的信息,以便用于机器学习或深度学习模型的训练和预测。
下面是一些常用的特征提取方法:
-
词袋模型(Bag-of-words):将文本拆分为单词并统计每个单词的出现次数,生成一个单词的出现频率向量。这种方法忽略了单词的顺序和语义信息,被广泛用于文本分类等任务。
-
TF-IDF:TF-IDF(Term Frequency-Inverse Document Frequency)是一种用于衡量一个单词在文本中的重要性的方法。它结合了单词在文本中的频率和在整个语料库中的频率,通过计算单词的TF-IDF权重来表示文本。
-
词嵌入(Word Embeddings):词嵌入是将单词映射到一个低维向量空间的方法,将单词的语义信息编码为向量。常用的词嵌入方法有Word2Vec、GloVe和BERT等。
-
n-gram:n-gram是指连续的n个单词组成的片段。通过提取n-gram,可以捕捉到一定的语言上下文信息。
-
句法特征:句法特征基于句子的语法结构,可以提取句子中的词性、句法依存关系等信息,用于语义分析和语法分析等任务。
-
结构特征:结构特征是指基于文本的结构信息进行的特征提取,例如段落结构、句子长度、标点符号等。
-
主题模型特征:主题模型是一种用于从文本中发现主题的方法,可以将文本表示为一个主题分布向量。
下面是基于TF-IDF实现的分词器模型代码
def mask_tokens_by_contribution(input_ids, tokenizer, mlm_probability):
# 将输入ID转换为文本
text = tokenizer.decode(input_ids, skip_special_tokens=True)
# 使用TF-IDF或其他方法来评估每个词的贡献度
tfidf_vectorizer = TfidfVectorizer()
# 确保传递给 TfidfVectorizer 的是字符串列表
tfidf = tfidf_vectorizer.fit_transform([text]) # text 应该是一个字符串
feature_names = tfidf_vectorizer.get_feature_names_out()
contributions = tfidf.toarray()[0]
# 将贡献度分数转换为概率,较低的贡献度对应较高的掩码概率
mask_probabilities = 1 - contributions / np.sum(contributions)
# 确保 mask_probabilities 是一个一维数组,并且其长度与 input_ids 的长度一致
mask_probabilities = np.array(mask_probabilities, dtype='float32')
# 创建一个与 input_ids 相同形状的概率矩阵
# 这里我们使用 paddle 来创建一个与 input_ids 形状相同的概率矩阵
prob_matrix = paddle.full(input_ids.shape, mlm_probability, dtype='float32')
# 决定哪些标记被掩码
masked_indices = paddle.bernoulli(prob_matrix).astype('bool')
# 将被选择的标记替换为 [MASK]
input_ids = paddle.to_tensor(input_ids, dtype='int64')
mask_token_id = tokenizer.convert_tokens_to_ids('[MASK]')
# 确保只在 masked_indices 为 True 的位置上替换标记
input_ids = paddle.where(masked_indices, paddle.full_like(input_ids, mask_token_id), input_ids)
print("提取出来的mask掩码:",masked_indices)
return input_ids, masked_indices
def vectorize_text(data_list):
# 初始化 TF-IDF 向量化器
vectorizer = TfidfVectorizer()
# 提取所有文本数据
texts = [item[0] for item in data_list]
# 向量化文本数据
vectors = vectorizer.fit_transform(texts)
return vectors
3、建模调参
NLP 过程的最后一步是对通过步骤 1 和步骤 2 生成的向量,利用机器学习和深度学习方法执行计算,以产生期望的结果。许多来自非 NLP 领域的相同的机器学习技术,例如图像识别或欺诈检测,可用于该分析。
这一步就是完全参考机器学习或者深度学习的内容了,完整的示例代码如下:
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph.nn import FC, Embedding
# 数据预处理
def preprocess(data):
processed_data = []
for sentence in data:
# 进行分词、去除停用词等预处理操作
processed_sentence = preprocess_sentence(sentence)
processed_data.append(processed_sentence)
return processed_data
# 特征提取
def feature_extraction(data):
corpus = [" ".join(sentence) for sentence in data]
vectorizer = TfidfVectorizer()
features = vectorizer.fit_transform(corpus)
return features
# 建模和调参
def model_and_tuning(features, labels):
X_train, X_test, y_train, y_test = train_test_split(features, labels, test_size=0.2, random_state=42)
clf = RandomForestClassifier()
# 进行模型训练和调参
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
# 进行模型评估
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
return clf, accuracy, precision, recall
# 主函数
if __name__ == "__main__":
# 加载数据
data = load_data()
labels = load_labels()
# 数据预处理
processed_data = preprocess(data)
# 特征提取
features = feature_extraction(processed_data)
# 建模和调参
model, accuracy, precision, recall = model_and_tuning(features, labels)
# 输出结果
print("Accuracy: ", accuracy)
print("Precision: ", precision)
print("Recall: ", recall)
总结
本文基于人工智能(9)————————transformer模型及演化-CSDN博客的基础上,简述了NLP自然语言处理的发展历史,并对具体实现步骤进行了代码复现。在现在国家的大趋势下,基于NLP提出的数据分类分级越来越火热,关于数据付费的观念日益深刻,因此我有预感,关于这个方面的研究会越来多。