LLM系列:4.transformer算法:3.Jieba

LLM系列:4.transformer算法:3.Jieba Jieba一.认识JiebaJieba 是目前表现较为不错的、立志于做最好的 Python 中文分词组件。它不仅支持多种分词模式还支持自定义词典和繁体分词且采用 MIT 授权协议。安装与导入 Jieba可以直接通过 pip 进行安装支持 Python 2/3。# 安装pip3 install jieba# 导入importjieba二.Jieba的分词原理与步骤jieba分词综合了基于字符串匹配的算法和基于统计的算法。其主要底层算法及分词步骤如下初始化与加载加载词典文件获取每个词语和它出现的词数。切分短语利用正则将文本切分为一个个语句之后对语句进行分词。构建DAG (有向无环图)基于前缀词典实现高效的词图扫描通过字符串匹配构建所有可能的分词情况的有向无环图 (DAG)。计算最大概率路径采用了动态规划查找最大概率路径找出基于词频的最大切分组合同时计算每个汉字节点到语句结尾的所有路径中的最大概率并记下对应位置。构建切分组合根据节点路径得到最终的词语切分结果。HMM新词处理对于未登录词 (dict.txt中没有的词语)采用基于汉字成词能力的 HMM (隐马尔科夫) 模型来处理并使用了 Viterbi 算法。返回结果通过yield将切分好的词语逐个返回相较于直接返回list这可以节约存储空间。三.Jieba 的核心类与属性在jieba的底层设计中大多数直接调用的模块级方法如jieba.cut实际上是代理给了默认实例化的核心类。1. 核心类 (Classes)jieba.Tokenizer: 这是jieba的核心分词器类。每次import jieba时模块会自动创建一个默认的Tokenizer实例即jieba.dt。如果你在多线程环境或需要隔离的词典环境可以自行实例化这个类例如my_tokenizer jieba.Tokenizer(dictionary...)。jieba.posseg.POSTokenizer: 专门用于词性标注的分词器类继承/组合了基础的分词能力并集成了词性标注模型。jieba.analyse.TFIDF/jieba.analyse.TextRank: 用于关键词提取的评估类。2. 核心属性 (Attributes)通常不建议直接操作底层属性但了解它们有助于理解原理jieba.dt: 默认的Tokenizer实例所有全局方法jieba.cut等都在操作这个实例。jieba.dt.FREQ: 一个 Python 字典存储了加载的词库中所有词语及其对应的词频包括前缀词。jieba.dt.total: 统计出的所有词汇的总词频用于计算概率分布。jieba.dt.initialized: 布尔值标识词典是否已经被加载和初始化。四.Jieba 的分词模式Jieba 库支持以下四种主要的分词模式分词模式描述与特点精确模式试图将文本精确地切分成若干个中文单词最精确地还原为之前的文本。适合文本分析其中不存在冗余单词。全模式将一段文本中所有可能的、可以成词的词语都扫描出来。速度非常快但不能解决歧义分词后的信息组合起来会有冗余。搜索引擎模式在精确模式的基础上对发现的长词再次进行切分从而提高召回率。适合用于搜索引擎分词和索引同样会存在冗余。paddle模式通过延迟加载方式在安装paddlepaddle-tiny后提供的模式。1. 精确模式 (Precise Mode)这是 Jieba 的默认模式也是日常开发中最常用的模式。核心机制它会结合基于词典的 DAG有向无环图最大概率路径算法以及基于 HMM隐马尔可夫模型的 Viterbi 算法来处理未登录词。系统会力求根据上下文找到一条唯一且最合理的切分路径。特点毫无冗余句子中的每个字只会属于一个词最终组合起来能完美还原原文。适用场景绝大多数常规的自然语言处理NLP任务如文本特征提取、情感分析、文本分类、词云生成等因为它能最大程度保留句子的正常语义。调用方式jieba.cut(text,cut_allFalse)# 默认就是 False2. 全模式 (Full Mode)顾名思义这是一种“地毯式搜索”的暴力模式。核心机制主要依赖纯词典匹配把句子中从每一个位置开始只要能和词典匹配上的词全部扫描出来。它不考虑上下文语境也不会开启 HMM 模型去猜测新词。特点切分速度极快因为跳过了复杂的概率计算但分词结果会有大量的重叠和冗余完全不具备解决词义歧义的能力。适用场景对处理速度要求极高且需要极高“召回率”宁可错杀一千不可放过一个的粗粒度文本扫描任务。通常不适合直接用于语义分析。调用方式jieba.cut(text,cut_allTrue)3. 搜索引擎模式 (Search Engine Mode)这是在“精确模式”基础上做加法的一种模式专门为了解决搜索场景痛点而生。核心机制属于“二次加工”。它首先在底层执行一次精确模式然后对精确切分出来的长词通常指长度大于 2 的词进行再次拆解提取出其中包含的更短的词汇。特点在保留基础语义环境的同时增加了细粒度词汇的输出。适用场景专门用于构建搜索引擎的倒排索引如配合 Elasticsearch 等使用。在搜索场景下用户经常只输入长词的一部分比如原文是“计算机科学”用户可能只搜“计算”这种模式能确保长短词汇都能被索引到显著提升搜索命中率。调用方式jieba.cut_for_search(text)4. Paddle模式 (Paddle Mode)引入了深度学习框架是 Jieba 拥抱神经网络架构的一种扩展模式。核心机制脱离了传统的词典统计与概率推算底层直接调用了百度飞桨PaddlePaddle预训练的深度学习序列标注模型通常基于 BiGRUCRF 架构。特点在处理长难句、解决复杂语境歧义、以及发现未登录词新词、专有名词、网络梗方面表现远超基础模式且可以同时输出高精度的词性标注。但缺点是依赖较重初次启动和运行速度比传统词典法慢资源消耗更大。适用场景对分词准确率要求极高、语料库包含大量非常规或新生词汇、且硬件资源允许的深度 AI 分析场景。调用方式# 必须先安装 paddlepaddle-tinyjieba.enable_paddle()# 开启并加载模型jieba.cut(text,use_paddleTrue)五.基础分词方法1. cut lcut - 精确与全模式分词作用对目标文本进行精确模式或全模式的切分。cut返回一个可迭代的生成器能够在处理长文本时节约内存而lcut则直接将全部分词结果转化为列表返回。jieba.cut(sentence,cut_allFalse,HMMTrue)jieba.lcut(sentence,cut_allFalse,HMMTrue)参数文本 (sentence) 需要分词的目标中文字符串(str)。全模式 (cut_all) 控制分词模式。设为False为精确模式(默认)设为True则为全模式(bool)。隐马尔可夫模型 (HMM) 是否开启HMM模型来识别未登录词(新词)。默认为True(bool)。关闭HMM(HMMFalse)Jieba 遇到不认识的词时就会极其生硬地把它们全部切碎成单独的汉字。开启HMM(HMMTrue)后即便词典里完全没有这个词算法也能利用统计学规律和上下文的概率“猜”出它可能是一个完整的词并将其作为一个整体切分出来。返回值成功cut返回生成器(generator)lcut返回由词汇字符串组成的列表(list[str])。示例importjieba text清华大学的计算机科学与技术专业# 1.精确模式 (返回生成器转化为list以便查看)seg_genjieba.cut(text,cut_allFalse)print(list(seg_gen))# [清华大学, 的, 计算机科学, 与, 技术, 专业]# 2.全模式 (直接返回列表)seg_listjieba.lcut(text,cut_allTrue)print(seg_list)# [清华, 清华大学, 华大, 大学, 的, 计算, 计算机, 计算机科学, 算机, 科学, 与, 技术, 专业]2. cut_for_search lcut_for_search - 搜索引擎模式分词作用在精确模式的基础上对发现的长词再次进行切分提取出更细粒度的词汇通常用于构建搜索引擎的倒排索引。cut_for_search返回一个可迭代的生成器lcut_for_search则直接将全部分词结果转化为列表返回。jieba.cut_for_search(sentence,HMMTrue)jieba.lcut_for_search(sentence,HMMTrue)参数文本 (sentence) 需要分词的目标字符串(str)。隐马尔可夫模型 (HMM) 是否开启 HMM 模型。默认为True(bool)。返回值成功cut_for_search返回生成器(generator)lcut_for_search返回列表(list[str])。示例importjieba text清华大学的计算机科学与技术专业seg_search_genjieba.cut_for_search(text)print(搜索引擎模式 (generator):,list(seg_search_gen))# [清华, 华大, 大学, 清华大学, 的, 计算, 算机, 科学, 计算机, 计算机科学, 与, 技术, 专业]search_listjieba.lcut_for_search(text)print(搜索引擎模式 (list):,search_list)# [清华, 华大, 大学, 清华大学, 的, 计算, 算机, 科学, 计算机, 计算机科学, 与, 技术, 专业]六.自定义词典与词频干预方法在垂直领域如医疗、法律、特定小说中系统自带的词典往往无法正确识别专业名词。此时需要介入干预 Jieba 的词库。1. load_userdict - 加载外部自定义词典作用批量加载本地的自定义 TXT 词典文件提升特定领域词汇的分词准确率。jieba.load_userdict(file_name)参数文件路径 (file_name) 包含自定义词汇的文本文件路径(str 或 pathlib.Path)。词典文件格式要求一词占一行每一行分为三部分词语、词频(可省略)、词性(可省略)各部分之间用空格隔开。示例创新工场 3 n返回值无返回值直接修改全局jieba.dt的内部状态。2. add_word del_word - 动态增删词汇作用在程序运行过程中通过代码动态地向内存中的词典添加或移除特定词汇而无需修改本地文件。jieba.add_word(word,freqNone,tagNone)jieba.del_word(word)参数词汇 (word) 需要添加或删除的目标词语(str)。词频 (freq) 可选指定该词的词频(int)。词性 (tag) 可选指定该词的词性(str)。示例text我来到了庆国京都print(jieba.lcut(text))# 可能被切成 [我, 来到, 了, 庆国, 京都]jieba.add_word(庆国京都)print(jieba.lcut(text))# 变为 [我, 来到, 了, 庆国京都]3. suggest_freq - 调节单个词语词频作用自动计算并调节单个词语的词频强制让系统能够或不能将某几个字作为一个词分出来。这是解决“误拆分”或“误合并”的最直接方法。jieba.suggest_freq(segment,tuneTrue)参数词段 (segment) 需要干预的词汇。(str 或 tuple)。传入元组代表促使拆分传入元组 (A, B) “把 A 和 B 拆开别连在一起”传入单个字符串代表促使合并传入字符串 “AB” “把 A 和 B 锁死这就是一个词”调节启用 (tune) 是否立刻在内部词典中生效此词频调节。默认为True(bool)。tuneTrue既计算又执行修改内存。tuneFalse只计算不执行只看结果。返回值成功 返回调节后的该词语(或字组)的新词频(int)。示例importjieba# 一.拆分场景强制将一个词拆开 (tuple)text如果放到post中将出错# --- 情况 A: tuneTrue (默认) ---# 强制拆分并立即在分词器中生效jieba.suggest_freq((中,将),tuneTrue)print(tuneTrue 拆分结果:,jieba.lcut(text))# 结果: [如果, 放到, post, 中, 将, 出错]# --- 情况 B: tuneFalse ---# 此时调用 suggest_freq 仅仅是计算了词频但没有强制覆盖内存中的模型# 因此分词效果可能不会发生改变或者需要依赖后续更新jieba.suggest_freq((中,将),tuneFalse)print(tuneFalse 拆分结果:,jieba.lcut(text))# 结果: [如果, 放到, post, 中将, 出错] (效果未生效)# 二.合并场景强制将两个字锁死为一个词 (str)text我爱北京天安门# 原始分词系统可能会把 天安门 识别出来但如果系统很笨可能切成 [天安, 门]print(原始分词:,jieba.lcut(text))# --- 情况 A: tuneTrue ---# 强制把 天安门 作为一个整体jieba.suggest_freq(天安门,tuneTrue)print(tuneTrue 合并结果:,jieba.lcut(text))# 结果: [我, 爱, 北京, 天安门]# --- 情况 B: tuneFalse ---# 调用了计算但未应用到当前分词器的动态词典中jieba.suggest_freq(天安门,tuneFalse)print(tuneFalse 合并结果:,jieba.lcut(text))# 结果: [我, 爱, 北京, 天安, 门] (可能保持原样取决于模型初始状态)七.关键词提取 -jieba.analyse子模块需要额外import jieba.analyse1. extract_tags - 基于 TF-IDF 提取关键词作用基于统计学中的 TF-IDF 算法从一段长文本中抽取出最具代表性的核心关键词。jieba.analyse.extract_tags(sentence,topK20,withWeightFalse,allowPOS())参数文本 (sentence) 待提取关键词的长文本字符串(str)。数量 (topK) 提取权重排名前 K 个的关键词。默认为 20(int)。权重返回 (withWeight) 是否一并返回每个关键词对应的 TF-IDF 权重值。默认为False(bool)。词性过滤 (allowPOS) 仅包含指定词性的词(如仅提取名词和人名(n, nr))。默认为空元组即不筛选(tuple)。返回值成功 默认返回关键词列表(list[str])如果withWeightTrue则返回包含(关键词, 权重)的元组列表(list[tuple])。示例importjieba.analyse text人工智能与深度学习在自然语言处理领域取得了巨大的突破tagsjieba.analyse.extract_tags(text,topK3,withWeightTrue)print(tags)# [(自然语言处理, 1.12...), (深度学习, 0.89...), (人工智能, 0.75...)]八.词性标注 -jieba.posseg子模块1. posseg.lcut - 词性标注作用在对文本进行分词的同时标注出每一个词语的词性(如n名词、v动词、a形容词等)。需要额外import jieba.posseg as psegpseg.cut(sentence)pseg.lcut(sentence)参数文本 (sentence) 需要标注词性的目标字符串(str)。返回值成功 返回包含pair(word, flag)对象的生成器或列表。可以通过.word和.flag提取。示例importjieba.possegaspseg wordspseg.lcut(我爱北京天安门)forwinwords:print(f{w.word}:{w.flag})# 我: r (代词)# 爱: v (动词)# 北京: ns (地名)# 天安门: ns (地名)九.位置获取方法1. tokenize - 获取词语位置索引作用进行切分并同时返回词语在原文中的起止位置(索引)。jieba.tokenize(unicode_sentence,modedefault)参数文本 (unicode_sentence) 需要处理的文本字符串。模式 (mode) 默认为default(精确模式)可设为search(搜索引擎模式)(str)。返回值成功 返回一个生成器每个元素是一个元组(词语, 起始索引, 结束索引)(Generator)。示例resultjieba.tokenize(自然语言处理)fortkinresult:print(f词汇:{tk[0]}, 起始:{tk[1]}, 结束:{tk[2]})# 词汇: 自然语言, 起始: 0, 结束: 4# 词汇: 处理, 起始: 4, 结束: 6十.结合Jieba的PyTorch Embedding示例重构使用您提供的《庆余年》文本作为语料库通过jieba构建真实的词汇表并利用这些真实数据来演示nn.Embedding的查表过程。importtorchimporttorch.nnasnnimportjieba# 1.准备原始文本text1朝堂是谁的朝堂天下又是谁的天下——庆帝的文武百官绝不敢轻易忤逆或直谏\ 夹缝里喘息贪污结党企图吞噬一点点这肮脏血腥的权力却落得个死无葬身之地\ 他想起赖明成陈萍萍那些一个个被处以极刑的大臣他与承乾自孩提时代就见识过权力只是一把刀子\ 一场流血一个个微不足道的死亡于是他们恐惧又愤恨他们开始认为阴谋诡计是一种力量\ 非要一刀见血才是一次胜利嬴的人才配活着与野兽何异他们在跟谁争\ 只有庆帝一个人把握着权柄把它高高挂起高于良知高于品德高于世间万物\ 他们竟然在争抢这样一个丑陋又卑鄙散发着腐烂恶臭的东西。text2人心那我亏大了自抬身价罢了。你觉得他们会感激我那都是一时的\ 他们求的是自己想要的东西我满足了一时片刻他们就想要有别的东西了\ 这点儿不长久的人心算什么人心。你看那些古来文人大家那庄墨韩人人追捧\ 高高在上不容亵渎一字换一城也毫不夸张是笔墨纸张值钱还是他们的名气\ 就像是最近给你送礼攀关系那些人一样的他们为的不是一时的东西我也不能做那一\ 时的玩意儿。”李承泽收回目光转过身来他握紧了自己的一双手直视着谢必安冰冷\ 宛如冬夜般的眼睛“必安你得明白东宫太子有父母有名头有朝臣陛下\ 拥有这个天下的人和财权我只有我自己但是我要让你知道的是这些他们有的\ 不见得一直有我依然有我自己。# 2.使用 jieba 构建全局词汇表 (Vocabulary)# 2.1对所有文本进行分词获取所有的 tokenall_wordsjieba.lcut(text1 text2)# 2.2去重并排序排序非必须只是为了结果稳定unique_wordssorted(list(set([wforwinall_wordsifw.strip()])))# 2.3构建 word - index 的映射字典word2idx{PAD:0}# 郑重声明 0 号索引是 PADfori,wordinenumerate(unique_words):word2idx[word]i1vocab_sizelen(word2idx)d_model16# 为了方便展示我们将向量维度设为16pad_index0print(f总词汇量 (Vocab Size):{vocab_size})# 总词汇量(Vocab Size): 204# 3. 实例化 Embedding 层embed_layernn.Embedding(num_embeddingsvocab_size,embedding_dimd_model,padding_idxpad_index)# 观察底层核心矩阵 weightprint(embed_layer.weight.shape)# torch.Size([204, 16])(行数对应词汇表大小)# 验证padding_idx第0行被强行锁定为全0print(embed_layer.weight[0])# tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], grad_fnSelectBackward0)# 4. 构造数据并映射为ID序列(支持动态Batch Size)raw_texts[text1,text2]# 将待处理文本放入列表中encoded_texts[]# 映射后ID序列列表max_len0# 最大句子长度确定时间步长度用# 4.1将文本分词、映射为ID序列并动态记录最大长度fortextinraw_texts:# 转换为ID序列(过滤掉不在词典中的词增加鲁棒性)encoded_sequence[word2idx[word]forwordinjieba.lcut(text)ifwordinword2idx]encoded_texts.append(encoded_sequence)# 确认并更新 max_leniflen(encoded_sequence)max_len:max_lenlen(encoded_sequence)# 4.2依据max_len对所有序列进行Padding填充padded_sequences[]forseqinencoded_texts:# 用PAD(0)补齐至最大长度padded_seqseq[pad_index]*(max_len-len(seq))padded_sequences.append(padded_seq)# 4.3 组装成Batch输入张量(必须是torch.long类型)input_indicestorch.tensor(padded_sequences,dtypetorch.long)# 输入张量(Batch, Seq_Len)映射关系print(input_indices)# tensor([[119, 116, ...(省略中间) , 0, 0],[ 42, 203, ...(省略中间) , 156, 4]])print(input_indices.shape)# torch.Size([2, 202])# 5. 前向传播执行查表操作embedded_featuresembed_layer(input_indices)# Embedding处理后的输出print(embedded_features.shape)# torch.Size([2, 202, 16])需要注意的是在实际构建Encoder时Embedding 层既可以写在 Encoder 的结构中也可以单独写。如果将Embedding层写在Encoder结构中代码会整洁所有与模型相关的部分都在同一个类中便于维护但这个操作相当于将整个Transformer结构的输入数据由(batch_size, seq_len, input_dimensions)结构修改为了(batch_size, seq_len)结构、从而会影响掩码的结构、影响掩码函数、这个Embedding层无法与Decoder或其他算法共用、甚至可能导致你的Encoder结构无法用于时间序列任务如果我们将Embedding层写在Encoder结构之外灵活性更高可以在多个模型中共享同一个 Embedding 层也可以对Embedding后的结果进行更加顺畅的独立处理但缺点是就需要额外的代码来管理Embedding层的初始化和传递。在实际进行编程时Embedding结构究竟写在Encoder内还是外与你的数据和实现的架构有很大的关系你需要根据具体情况进行具体的分析。