机器学习与人工智能:NLP分词与文本相似度分析

news2025/5/18 3:24:41

自然语言处理

你有没有想过,生成式 AI 工具或大型语言模型背后究竟发生了什么?自然语言处理(NLP)是这些工具的核心,它使计算机能够理解人类语言。换句话说,NLP 是连接人类交流和机器(如计算机)的桥梁。因此,它是任何生成式 AI 工具的重要组成部分。

在本文中,我们将构建一个简单的 Python NLP 对象,它接受一组文档,执行各种标准的 NLP 预处理技术,最后,允许终端用户输入新的文档或文本,并从之前上传的文档组中提取最相似的文本。

分词与预处理

每个机器学习管道、数据科学项目和探索性数据分析任务都需要数据清洗或预处理步骤,自然语言处理也不例外。当我开始学习 NLP 时,我记得被从表格数据转换为实际单词的格式所淹没。了解这一点后,让我们逐步分解 NLP 任务中清理文本数据的各个部分。

分词将文本分解为更小的单元,如单个单词或字符。让我们来看一个例子,我们在一个句子上执行分词:

"Medium is a great place for writing content."

分解成单个单词或 tokens 后,它看起来像这样:

"Medium", “is”, “a”, “great”, “place”, “for”, “writing”, "content."

开始将这段文本视为数据集中的一个观测值,并将每个单词视为一个二进制列。因此,对于这个句子,所有这些单词的列都将为 1True,对于所有其他单词或 tokens,它将是 0 或 False。我有点超前了,但如果你是 NLP 新手,早点这样思考会很有帮助。

现在分词步骤完成了,让我们学习如何预处理文本数据。假设我们还有另一个句子,内容如下:

"A great place for writing content is Medium."

分词后,它将读作:

"A", “great”, “place”, “for”, “writing”, “content”, “is”, "Medium."

从人类交流的角度来看,这两个文本几乎完全相同,并且传达了相同的信息。然而,NLP 是关于构建人类交流和计算机之间的桥梁,计算机可能会将这些句子视为完全不同。你正在构建的 NLP 应用程序或工具的最终产品将决定你需要对文本进行何种预处理。我们将在本文中创建一个相似度模型,因此我们要确保用于训练该模型的文本是经过适当预处理的。

第一步是将所有内容转换为小写。注意单词 “a” 在每个陈述中都被用作一个单词。然而,在一个句子中它被大写,而在另一个句子中是小写。通过将所有内容转换为小写,计算机或 NLP 模型将理解这些是同一个标记的两个实例,而不是两个不同的标记。

None

作者提供的图片

None

作者提供的图片

你是否已经开始像 NLP 实践者那样思考了?换句话说,你是否已经开始理解我们所看到的与计算机所看到的之间的联系,以及它如何适用于我们的相似性引擎?想想我们可以在数据中去除的噪声。对于 NLP 来说,去除噪声的最常见技术是去除在大多数文本中出现的填充词或常见词。我们称这些为“停用词”。注意,这一步可能非常主观。然而,有许多开源库允许你下载一个停用词列表,你可以在你的 NLP 应用程序中使用。本着从头开始构建我们的对象的精神,我们将创建我们自己的停用词列表。为什么我们应该关心去除停用词?将停用词视为在数据集中每个观测值中都出现且没有明显模式的数据点。如果数据集中的每个观测值都有这样一个没有明显模式的目标的数据点,那么它只会为我们的模型增加噪声,甚至可能阻碍它。在 NLP 应用程序中,停用词也不例外。

None

作者提供的图片

让我们继续一个更高级的文本数据预处理方法:创建 n-grams。回到停用词,你应该总是去除它们吗?这取决于你的数据上下文和你的 NLP 模型。有时,停用词可能会根据它们在文本行中的位置提供必要的上下文。

目前,我们的数据将是顺序无关的。换句话说,将我们的分词文本输入模型会将多个句子中的同一个单词的多个实例视为相同,不管它在句子中的使用方式和位置如何。为了捕捉这些细微差别,我们可以将标记转换为 n-grams。例如,假设我们想要捕捉我们正在处理的句子中的每个两个单词的短语,更正式地说,就是二元组(bigrams)。我添加了停用词,看看包含它们的分词文本在应用二元组后会是什么样子。

None

作者提供的图片

现在,单词的顺序很重要了!这种增加的复杂性可以捕捉文本数据中的细微差别,使我们的相似性引擎更加精确。我们甚至可以将文本转换为三元组(trigrams)、四元组(quadragrams)等。注意,这可能会使你的 NLP 应用程序或模型变得复杂、计算成本高昂,甚至冗余。因此,一些 NLP 库中有内置逻辑,规定只有当 n-gram 在一定比例的文档中存在时,才能创建它。这种效率确保你不会执行任何冗余的预处理。我在这里谈论去除停用词和创建 n-grams 也不是巧合。这两种技术的预处理顺序将显著影响你的模型。

词袋模型(Bag-of-Words Model, BOW)

我们可以开始构建人类语言和计算机之间的桥梁了。我们回顾了清理文本数据的技术,现在将把它转换成计算机可以理解的格式。

词袋模型 是将文本建模成计算机或应用程序可以理解的最简单方式。它只是查看文档集合(也称为 语料库)中的所有唯一标记,并将每个文档转换为语料库中每个标记的计数集合。让我们来看一个假设的例子,语料库包含三个文档。

None

作者提供的图片

首先,让我们从这些文档中提取所有唯一的标记,这也就是所谓的 词汇表

None

接下来,我们将每个文档转换为一个 词袋 向量。为此,我们取词汇表,并为文档和词汇表中都出现的每个单词标记 1,否则为 0 。这个过程应该与对分类数据进行虚拟编码或独热编码非常相似。

None

仅此而已。这应该与对分类数据进行虚拟编码或独热编码非常相似。

余弦相似度

在创建我们的对象之前,是时候讨论我们相似性引擎的核心数学原理了,即 余弦相似度。它通过计算两个向量之间的夹角的余弦值来衡量两个向量之间的相似度。在我们将文本转换为 词袋 向量之前,这听起来可能会更疯狂一些。余弦相似度 是衡量文本相似度的绝佳选择,因为它可以让我们感受到文档之间的相似性,而不管一个文档是否比另一个大得多。它有助于捕捉提供相似上下文或总体信息的相似文档。余弦相似度 的分数范围从 -1 到 1,其中 1 表示最相似,0 表示没有相似性,-1 表示两个文档完全相反,我应该指出,使用 TF-IDF 分数计算的词袋向量中这种情况很少见。让我们来看看两个向量 A 和 B 之间的余弦相似度公式。

cos ⁡ ( θ ) = A ⋅ B ∥ A ∥ ∥ B ∥ \cos(\theta) = \frac{A \cdot B}{\|A\| \|B\|} cos(θ)=ABAB

作者提供的图片

在分子中,我们取两个向量的点积,在分母中,我们取两个向量的模的乘积。

None

在这个假设的例子中,我们可以看到,根据余弦相似度指标,两个向量 AB 非常相似;换句话说,它们的方向几乎相同。

DIYSimilarityEngine 类

尽管这个对象的名字如此,但大多数方法将致力于分词、预处理文本数据以及构建袋装词向量。通常,这些步骤是在构建一些基于 NLP 的模型之前通过其他库和框架完成的。然而,将这些步骤整合到同一个对象中是实用的。我应该指出,常见的 NLP 预处理框架,如 nltk 和 spacy,将提供比你在这里看到的更高级的技术,因为我的目标是在基础水平上展示这些,以便读者能够获得坚实的基础。

import pandas as pd
import re
import math
from collections import Counter

class DIYSimilarityEngine:
    def __init__(self, ngram_n=1, stopwords=None):
        self.ngram_n = ngram_n
        self.stopwords = set(stopwords) if stopwords else set([
            'the', 'is', 'at', 'which', 'on', 'a', 'an', 'and', 'in', 'it', 'of', 'to', 'with', 'as', 'for', 'by'])
        self.documents = []
        self.vocabulary = set()
        self.bow_vectors = []

_preprocess_generate_ngrams 方法

我们的前两个方法对文档列表中的每个文档进行预处理和分词。注意,创建 n-grams 的方法是在预处理方法中调用的。注意调用的顺序。首先,将文本转换为小写,去除标点符号,执行分词,最后,将标记转换为 n-grams,它们仍然是标记。始终在去除你认为是噪声的文本(如停用词)之后,将文本转换为 n-grams。

def _preprocess(self, text):
    # 将文本转换为小写
    text = text.lower()
    # 去除标点符号
    text = re.sub(r'[^\w\s]', '', text)
    # 分词
    tokens = text.split()
    # 去除停用词
    tokens = [t for t in tokens if t not in self.stopwords]

    if self.ngram_n > 1:
        # 如果需要,生成 n-grams
        tokens = self._generate_ngrams(tokens, self.ngram_n)

    return tokens

def _generate_ngrams(self, tokens, n):
    # 生成 n-grams
    return ['_'.join(tokens[i:i+n]) for i in range(len(tokens)-n+1)]

_build_vocabulary_vectorizefit 方法

这两个方法基于现在分词后的文档列表构建一个唯一的标记集合;同样,这也就是我们袋装词模型的 词汇表。有了 词汇表_vectorize 方法利用 collections 库中的 Counter 方法为分词后的文档构建一个袋装词向量,该文档以 1 和 0 的列表形式表示。

这两个方法和 _preprocess 方法一起在 fit 方法中使用,以将模型的词汇表分配给词汇表属性,并将所有的袋装词向量分配给其属性。

def _build_vocabulary(self, tokenized_docs):
    vocab = set()
    for tokens in tokenized_docs:
        vocab.update(tokens)
    return sorted(vocab)

def _vectorize(self, tokens, vocab):
    tf = Counter(tokens)
    return [tf[word] for word in vocab]

def fit(self, documents):
    self.documents = documents
    tokenized = [self._preprocess(doc) for doc in documents]
    self.vocabulary = self._build_vocabulary(tokenized)
    self.bow_vectors = [self._vectorize(tokens, self.vocabulary) for tokens in tokenized]

_cosine_similaritymost_similar 方法

这两个方法是我们对象的核心,但如果没有之前的预处理,它将一无是处。记住,在调用 most_similar 方法之前,必须先将语料库拟合到对象中。这个方法接受一个新文档,对其进行预处理,然后利用 _cosine_similarity 方法来确定语料库中哪些文档最相似,通过返回一个列表,其中包含最相似的前三个文档及其相关的余弦相似度分数,以便用户能够了解它们的相似程度。

def _cosine_similarity(self, vec1, vec2):
    # 计算两个向量的点积
    dot_product = sum(a*b for a, b in zip(vec1, vec2))
    # 计算向量的模
    norm1 = math.sqrt(sum(a*a for a in vec1))
    norm2 = math.sqrt(sum(b*b for b in vec2))
    if norm1 == 0 or norm2 == 0:
        return 0.0
    return dot_product / (norm1 * norm2)

def most_similar(self, query, top_n=3):
    # 对查询文本进行预处理
    query_tokens = self._preprocess(query)
    # 将查询文本转换为袋装词向量
    query_vector = self._vectorize(query_tokens, self.vocabulary)
    # 计算查询向量与语料库中每个文档向量的余弦相似度
    similarities = [
        (i, self._cosine_similarity(query_vector, bow_vector))
        for i, bow_vector in enumerate(self.bow_vectors)
    ]
    # 按相似度降序排序
    similarities.sort(key=lambda x: x[1], reverse=True)
    # 返回最相似的前 top_n 个文档及其相似度分数
    return [(self.documents[i], sim) for i, sim in similarities[:top_n]]

现实世界应用:寻找最佳工作

无论当前就业市场的状况如何,了解一个人凭借其技能和经验最有可能获得哪些工作机会总是有益的,特别是对于数据科学家和类似职业。使用我们的相似性引擎对象,我们将对其进行测试,通过创建一个概述个人经验和技能集的假设文档,并使用来自 Kaggle 的 2023 数据科学家职位描述数据集 来查看哪个工作最匹配。

让我们先为一个有五年数据分析师经验的候选人编写描述和技能集:

经验丰富的数据分析师,拥有 5 年通过数据驱动的决策制定提供可操作见解的经验。熟练掌握 SQL、Excel 和 Tableau、Power BI 等可视化工具,具有扎实的 Python 高级分析基础。证明了设计和自动化报告仪表板、进行统计分析以及跨职能合作以支持业务战略的能力。擅长识别趋势、优化流程以及以清晰、有影响力的方式传达复杂发现。

让我们先初始化对象的一个实例,并拟合 Jobs 数据集中的职位描述。

nlp = DIYSimilarityEngine(ngram_n=3)
data = pd.read_csv('Jobs.csv')
docs = list(data['description'])
nlp.fit(docs)

现在,让我们找出候选人最有可能获得面试或录用的工作,理论上。

summary = """
经验丰富的数据分析师,拥有 5 年通过数据驱动的决策制定提供可操作见解的经验。
熟练掌握 SQL、Excel 和 Tableau、Power BI 等可视化工具,具有扎实的 Python 高级分析基础。
证明了设计和自动化报告仪表板、进行统计分析以及跨职能合作以支持业务战略的能力。
擅长识别趋势、优化流程以及以清晰、有影响力的方式传达复杂发现。
"""
_ = nlp.most_similar(summary)
most_similar_doc = _[0][0]
data[data['description'] == most_similar_doc]

None

这个结果得出了 0.06 的相似度分数!说实话,这并不理想,如果你仔细阅读职位描述并与候选人的总结进行比较,你会发现许多技能是匹配的。然而,一旦你看到这份工作需要很多小众的职责和背景,差异就相当明显了。如何改进呢?我们很容易说模型或预处理需要更多的细节,但也可以认为候选人的总结越详细越好。、

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2378190.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

记录一下seata后端数据库由mariadb10切换到mysql8遇到的SQLException问题

文章目录 前言一、问题记录二、参考帖子三、记录store.db.driverClassName 前言 记录一下seata后端数据库由mariadb10切换到mysql8遇到的SQLException问题。 一、问题记录 17:39:23.709 ERROR --- [ionPool-Create-1134013833] com.alibaba.druid.pool.DruidDataSource : …

CUDA学习笔记

CUDA入门笔记 总览 CUDA是NVIDIA公司对其GPU产品提供的一个编程模型,在2006年提出,近年随着深度学习的广泛应用,CUDA已成为针对加速深度学习算法的并行计算工具。 以下是维基百科的定义:一种专有的并行计算平台和应用程序编程接…

c++ 类的语法3

测试下默认构造函数。demo1&#xff1a; void testClass3() {class Demo { // 没显示提供默认构造函数&#xff0c;会有默认构造函数。public:int x; // 普通成员变量&#xff0c;可默认构造};Demo demo1;//cout << "demo1.x: " << demo1.x << en…

Python 之类型注解

类型注解允许开发者显式地声明变量、函数参数和返回值的类型。但是加不加注解对于程序的运行没任何影响&#xff08;是非强制的&#xff0c;且类型注解不影响运行时行为&#xff09;&#xff0c;属于 有了挺好&#xff0c;没有也行。但是大型项目按照规范添加注解的话&#xff…

【linux】Web服务—搭建nginx+ssl的加密认证web服务器

准备工作 步骤&#xff1a; 一、 新建存储网站数据文件的目录 二、创建一个该目录下的默认页面&#xff0c;index.html 三、使用算法进行加密 四、制作证书 五、编辑配置文件&#xff0c;可以选择修改主配置文件&#xff0c;但是不建议 原因如下&#xff1a; 自定义一个配置文…

基于HTTP头部字段的SQL注入:SQLi-labs第17-20关

前置知识&#xff1a;HTTP头部介绍 HTTP&#xff08;超文本传输协议&#xff09;头部&#xff08;Headers&#xff09;是客户端和服务器在通信时传递的元数据&#xff0c;用于控制请求和响应的行为、传递附加信息或定义内容类型等。它们分为请求头&#xff08;Request Headers&…

实战解析MCP-使用本地的Qwen-2.5模型-AI协议的未来?

文章目录 目录 文章目录 前言 一、MCP是什么&#xff1f; 1.1MCP定义 1.2工作原理 二、为什么要MCP&#xff1f; 2.1 打破碎片化的困局 2.2 实时双向通信&#xff0c;提升交互效率 2.3 提高安全性与数据隐私保护 三、MCP 与 LangChain 的区别 3.1 目标定位不同 3.…

RabbitMQ高级篇-MQ的可靠性

目录 MQ的可靠性 1.如何设置数据持久化 1.1.交换机持久化 1.2.队列持久化 1.3.消息持久化 2.消息持久化 队列持久化&#xff1a; 消息持久化&#xff1a; 3.非消息持久化 非持久化队列&#xff1a; 非持久化消息&#xff1a; 4.消息的存储机制 4.1持久化消息&…

fpga系列 HDL : Microchip FPGA开发软件 Libero Soc 项目仿真示例

新建项目 项目初始界面中创建或导入设计文件&#xff1a; 新建HDL文件 module test (input [3:0] a,input [3:0] b,output reg [3:0] sum,output reg carry_out );always (*) begin{carry_out, sum} a b; endendmodule点击此按钮可进行项目信息的重新…

DeepSearch:WebThinker开启AI搜索研究新纪元!

1&#xff0c;项目简介 WebThinker 是一个深度研究智能体&#xff0c;使 LRMs 能够在推理过程中自主搜索网络、导航网页&#xff0c;并撰写研究报告。这种技术的目标是革命性的&#xff1a;让用户通过简单的查询就能在互联网的海量信息中进行深度搜索、挖掘和整合&#xff0c;从…

springCloud/Alibaba常用中间件之Setinel实现熔断降级

文章目录 SpringCloud Alibaba:依赖版本补充Sentinel:1、下载-运行&#xff1a;Sentinel(1.8.6)下载sentinel&#xff1a;运行&#xff1a;Sentinel <br> 2、流控规则① 公共的测试代码以及需要使用的测试Jmeter①、流控模式1. 直接:2. 并联:3. 链路: ②、流控效果1. 快速…

Deeper and Wider Siamese Networks for Real-Time Visual Tracking

现象&#xff1a; the backbone networks used in Siamese trackers are relatively shallow, such as AlexNet , which does not fully take advantage of the capability of modern deep neural networks. direct replacement of backbones with existing powerful archite…

黑马程序员C++2024版笔记 第0章 C++入门

1.C代码的基础结构 以hello_world代码为例&#xff1a; 预处理指令 #include<iostream> using namespace std; 代码前2行是预处理指令&#xff0c;即代码编译前的准备工作。&#xff08;编译是将源代码转化为可执行程序.exe文件的过程&#xff09; 主函数 主函数是…

foxmail - foxmail 启用超大附件提示密码与帐号不匹配

foxmail 启用超大附件提示密码与帐号不匹配 问题描述 在 foxmail 客户端中&#xff0c;启用超大附件功能&#xff0c;输入了正确的账号&#xff08;邮箱&#xff09;与密码&#xff0c;但是提示密码与帐号不匹配 处理策略 找到 foxmail 客户端目录/Global 目录下的 domain.i…

Crowdfund Insider聚焦:CertiK联创顾荣辉解析Web3.0创新与安全平衡之术

近日&#xff0c;权威金融科技媒体Crowdfund Insider发布报道&#xff0c;聚焦CertiK联合创始人兼CEO顾荣辉教授在Unchained Summit的主题演讲。报道指出&#xff0c;顾教授的观点揭示了Web3.0生态当前面临的挑战&#xff0c;以及合规与技术在推动行业可持续发展中的关键作用。…

PowerBI链接EXCEL实现自动化报表

PowerBI链接EXCEL实现自动化报表 曾经我将工作中一天的工作缩短至2个小时&#xff0c;其中最关键的一步就是使用PowerBI链接Excel做成一个自动化报表&#xff0c;PowerBI更新源数据&#xff0c;Excel更新报表并且保留报表格式。 以制作一个超市销售报表为例&#xff0c;简单叙…

腾讯云MCP数据智能处理:简化数据探索与分析的全流程指南

引言 在当今数据驱动的商业环境中&#xff0c;企业面临着海量数据处理和分析的挑战。腾讯云MCP(Managed Cloud Platform)提供的数据智能处理解决方案&#xff0c;为数据科学家和分析师提供了强大的工具集&#xff0c;能够显著简化数据探索、分析流程&#xff0c;并增强数据科学…

Android framework 中间件开发(一)

在Android开发中,经常会调用到一些系统服务,这些系统服务简化了上层应用的开发,这便是中间件的作用,中间件是介于系统和应用之间的桥梁,将复杂的底层逻辑进行一层封装,供上层APP直接调用,或者将一些APP没有权限一些操作放到中间件里面来实施. 假设一个需求,通过中间件调节系统亮…

MATLAB中的概率分布生成:从理论到实践

MATLAB中的概率分布生成&#xff1a;从理论到实践 引言 MATLAB作为一款强大的科学计算软件&#xff0c;在统计分析、数据模拟和概率建模方面提供了丰富的功能。本文将介绍如何使用MATLAB生成各种常见的概率分布&#xff0c;包括均匀分布、正态分布、泊松分布等&#xff0c;并…

C# 面向对象 构造函数带参无参细节解析

继承类构造时会先调用基类构造函数&#xff0c;不显式调用基类构造函数时&#xff0c;默认调用基类无参构造函数&#xff0c;但如果基类没有写无参构造函数&#xff0c;会无法调用从而报错&#xff1b;此时&#xff0c;要么显式的调用基类构造函数&#xff0c;并按其格式带上参…