自然语言处理——02 文本预处理(上)

1 认识文本预处理

  • 概念:

    • 文本语料在输送给模型前 一般需要一系列的预处理工作,才能符合模型输入的要求;
    • 比如:将文本转化成模型需要的张量、规范张量的尺寸;
    • 比如:
      • 关于数据X:数据有没有脏数据、数据长度分布情况等;
      • 关于标签Y:分类问题查看标签是否均匀;
  • 作用:

    • 指导模型超参数的选择 、提升模型的评估指标;
    • 大白话:文本预处理的工作,就是准备出模型需要的x、y,然后送给模型;
  • 主要环节:

    1. 文本处理的基本方法(先拆分、标注文本)

      • 分词:把长句子切成词语(比如"我喜欢自然语言处理"→"我/喜欢/自然语言/处理");
      • 词性标注:给词语贴标签(动词、名词等,比如"喜欢"是动词);
      • 命名实体识别:找出人名、地名、机构名(比如"北京有个清华大学"→识别出"北京"是地名,"清华大学"是机构名);
    2. 文本张量表示方法(把文本转成模型能计算的数字)

      • one-hot编码:用0和1表示词语,简单但容易"词多爆内存";
      • Word2vec/Word Embedding:让意思相近的词数字更像(比如"猫"和"狗"的向量比"猫"和"桌子"更接近),能抓语义关系;
    3. 文本语料的数据分析(检查数据质量、找规律)

      • 统计标签数量、句子长度,画出词云(比如发现"垃圾邮件"标签太多/太少,或者句子太长模型难处理),帮你判断数据合不合理,需不需要调整;
    4. 文本特征处理(给模型加"额外信息")

      • 添加n-gram特征:不只看单个词,还看词语搭配(比如"机器学习"一起出现,比单独"机器"+"学习"信息更多);
      • 规范文本长度:把句子统一成差不多长度(太短补一补,太长砍一砍),方便模型处理;
    5. 数据增强方法(数据不够?"造"点新数据)

      • 回忆数据增强法(比如同义替换、回译):没太多数据时,把句子换种说法("我很开心"→"我非常高兴"),让模型见更多样的表达,避免学偏。

2 文本处理的基本方法

2.1 分词

2.1.1 概述

  • 分词:将连续的字序列 按照一定的规范 重新组合成词序列的过程;

  • 作用:

    • 词作为语言语义理解的最小单元,是人类理解文本语言的基础;
    • 是AI解决NLP领域高阶任务,如自动问答、机器翻译、文本生成的重要基础环节;
  • 英文是不需要分词的,因为英文中的空格是天然的分解符,而中文没有明显的分解符。

  • 例:

    复制代码
    腾讯是一家上市公司,旗下有微信、QQ等产品,我们平时主要通过微信联系。
    腾讯/是/一家/上市公司/,/旗下/有/微信/、/QQ/等/产品/,/我们/平时/主要/通过/微信/联系/。

2.1.2 jieba分词器

  • jieba------目前流行的中文分词工具

    python 复制代码
    import jieba
    • 支持三种分词模式(分词粒度):

      • 精确模式 :默认分词模式,试图将文本精确切分,减少冗余和歧义,适用于文本分析、文本挖掘等需要精准词语划分的任务,调用方法为jieba.cut(text, cut_all=False)

        cut_all=False:关闭全模式,开启精确模式;

      • 全模式 :会扫描出句子中所有可能成词的词语,分词速度快,但无法解决分词歧义问题,适用于需要尽可能多地提取词语,对准确性要求不高的场景 ,调用方法为jieba.cut(text, cut_all=True)

      • 搜索引擎模式 :在精确模式基础上,对长词再次进行切分,增加分词的粒度,提高召回率,适合用于搜索引擎构建索引、处理用户查询等场景,调用方法为jieba.cut_for_search(text)

    • 支持中文繁体分词,支持用户自定义词典;

  • jieba.cut VS jieba.lcut

    • jieba.cut
      • 返回的是一个生成器对象。生成器是一种特殊的迭代器,它并不会一次性将所有分词结果加载到内存中,而是在迭代过程中按需生成。这样在处理大量文本时,能显著减少内存占用,提升程序运行效率;
      • 适合处理文本量非常大的情况,比如处理大规模的文本语料库、流式读取文件中的文本并进行分词等,通过迭代方式逐个获取分词结果,能有效控制内存使用;
    • jieba.lcut
      • 返回的是一个列表,即一次性将所有分词结果以列表形式存储在内存中。在处理小量文本时,使用列表便于直接进行后续操作,如统计词频、筛选特定词语等;但处理大量文本时,若内存不足,可能导致程序崩溃;
      • 适用于文本量较小,需要立即对所有分词结果进行统一操作的场景,如对一篇短文章进行分词后,马上统计词频、进行词性标注等操作,因为可以直接通过索引等方式访问列表中的元素;
  • 用户自定义词典时,遵循以下格式:

    复制代码
    词语  词频  词性
    • 词语:希望 jieba 优先识别的自定义词汇(比如专业术语、新造词等)
    • 词频:数字表示 "建议的分词优先级"(数值越小,越优先被识别),主要用于调整分词权重
    • 词性 :可选的词性标注(如 n 名词、nz 其他专名),jieba 支持自定义词性,也可忽略(但格式上需保留空格占位)
  • jieba词性对照表

    复制代码
    a 形容词
    	ad 副词
    	ag 形容词性语素
    	an 名形词
    b 区别词
    c 连词
    d 副词
    	df
    	dg 副语素
    e 叹词
    f 方位词
    g 语素
    h 前接成分
    i 成语
    j 简称略称
    k 后接成分
    l 习用语
    m 数词
    	mg
    	mq 数量词
    n 名词
    	ng 名词性语素
    	nr 人名
    	nrfg
    	nrt
    	ns 地名
    	nt 机构团体名
    	nz 其他专名
    o 拟声词
    p 介词
    q 量词
    r 代词
    	rg 代词性语素
    	rr 人称代词
    	rz 指示代词
    s 处所词
    t 时间词
    	tg 时语素
    
    u 助词
    	ud 结构助词 得
    	ug 时态助词
    	uj 结构助词 的
    	ul 时态助词 了
    	uv 结构助词 地
    	uz 时态助词 着
    v 动词
    	vd 副动词
    	vg 动词性语素
    	vi 不及物动词
    	vn 名动词
    	vq
    x 非语素词
    y 语气词
    z 状态词
    	zg

2.1.3 例

  • 例:三种分词模式

    python 复制代码
    content = "腾讯是一家上市公司,旗下有微信、QQ等产品,我们平时主要通过微信联系。"
    python 复制代码
    # 精确模式
    myobj1 = jieba.cut(sentence=content, cut_all= False)
    print('myobj1-->', myobj1)
    mydata1 = jieba.lcut(sentence=content, cut_all=False)
    print('mydata1-->', mydata1)
    # 全模式
    myobj2 = jieba.cut(sentence=content, cut_all=True)
    print('myobj2-->', myobj2)
    mydata2 = jieba.lcut(sentence=content, cut_all=True)
    print('mydata2-->', mydata2)
    # 搜索引擎模式
    myobj3 = jieba.cut_for_search(sentence=content)
    print('myobj3-->', myobj3)
    mydata3 = jieba.lcut_for_search(sentence=content)
    print('mydata3-->', mydata3)
  • 例:支持繁体分词

    python 复制代码
    # 支持繁体分词
    content = "煩惱即是菩提,我暫且不提"
    mydata = jieba.lcut(content)
    print('mydata-->', mydata)
  • 例:支持用户自定义词典

    python 复制代码
    # 支持用户自定义词典
    # 不使用用户字典
    content = "腾讯是一家上市公司,旗下有王者荣耀、和平精英等游戏,但还是要少玩游戏"
    mydata1 = jieba.lcut(sentence=content, cut_all=False)
    print('mydata1-->', mydata1)
    
    # 使用用户字典
    """
    上市
    王者荣耀 1 n
    和平精英 2 n
    """
    jieba.load_userdict('data/userdict.txt')
    mydata2 = jieba.lcut(sentence=content, cut_all=False)
    print('mydata2-->', mydata2)

2.2 命名实体识别

  • 命名实体:将人名、地名、机构名等专有名词统称命名实体。如:周杰伦、黑山县、孔子学院、24辊方钢矫直机......

  • 命名实体识别(Named Entity Recognition,简称NER),识别出一段文本中可能存在的命名实体;

  • 命名实体也是人类理解文本的基础单元,是AI解决NLP领域高阶任务的重要基础环节;

  • 例:

    复制代码
    鲁迅,浙江绍兴人,五四新文化运动的重要参与者,代表作朝花夕拾。
    鲁迅(人名) / 浙江绍兴(地名)人 / 五四新文化运动(专有名词) / 重要参与者 / 代表作 / 朝花夕拾(专有名词)

2.3 词性标注

  • 词性:语言中对词的一种分类方法,以语法特征为主要依据、兼顾词汇意义对词进行划分的结果;

  • 词性标注(Part-Of-Speech tagging,简称POS),标注出一段文本中每个词汇的词性;

  • 词向标注作用:对文本语言的另一个角度的理解,AI解决NLP领域高阶任务的重要基础环节;

  • 例:

    复制代码
    我爱自然语言处理
    我/rr,爱/v,自然语言/n,处理/vn
    
    rr:人称代词
    v:动词
    n:名词
    vn:动名词
  • 例:

    python 复制代码
    # 词性标注
    import jieba.posseg as pseg
    mydata1 = pseg.lcut("我爱北京天安门")
    print('mydata1-->', mydata1)

3 文本张量的表示方式

3.1 概念

  • 文本张量表示:使用张量来表示一段文本,将计算机无法理解的文本转换成计算机可以计算的向量形式;

  • 表示词的向量成为词向量,那么表示一句话就是一个词向量矩阵;

  • 例:

    python 复制代码
    ["人生", "该", "如何", "起头"]
    # 上面每个词对应下面矩阵中的一个向量
    [[1.32, 4.32, 0.32, 5.2],
    [3.1, 5.43, 0.34, 3.2],
    [3.21, 5.32, 2, 4.32],
    [2.54, 7.32, 5.12, 9.54]]
  • NLP中文本词向量表示的常用方法:

    • One-Hot编码
    • Word2vec
    • Word Embedding

3.2 One-Hot编码

  • One-Hot编码(One-Hot Encoding),也叫稀疏词向量表示

    • One-Hot编码是将分类变量转换为数字格式的常用方法,通常用于AI任务中处理分类标签y数据
    • 在One-Hot编码中,对于一个具有n个不同类别的分类变量,将其表示为一个n维的向量。其中只有一个维度的值为1(代表该样本属于这个类别),其他维度的值均为0;
  • 例:

    py 复制代码
    ["红色"、"绿色"、"蓝色"]
    [[1, 0, 0],
    [0, 1, 0],
    [0, 0, 1]]
    
    ["人生", "该", "如何", "起头"]
    [[1, 0, 0, 0], 
    [0, 1, 0, 0], 
    [0, 0, 1, 0], 
    [0, 0, 0, 1]]
  • 例:One-Hot编码的生成

    python 复制代码
    import jieba
    from tensorflow.keras.preprocessing.text import Tokenizer
    import joblib

    pip install tensorflow

    python 复制代码
    # One-Hot编码的生成
    # 定义词汇表
    vocabs = ["周杰伦", "陈奕迅", "王力宏", "李宗盛", "吴亦凡", "鹿晗"]
    
    # 创建Tokenizer对象,用于文本处理和编码转换
    # Tokenizer是Keras中用于文本预处理的工具,能将文本转换为整数序列或one-hot编码
    mytokenizer = Tokenizer()
    print('mytokenizer-->', mytokenizer)
    
    # 使用词汇表训练Tokenizer,构建词汇索引
    # fit_on_texts()方法会遍历输入的文本列表,统计词频并生成词汇表
    mytokenizer.fit_on_texts(vocabs)
    # 打印index_word属性:键是整数索引,值是对应的词语(索引从1开始)
    print('mytokenizer.index_word-->', mytokenizer.index_word)
    # 打印word_index属性:键是词语,值是对应的整数索引(与index_word互为反向映射)
    print('mytokenizer.word_index-->', mytokenizer.word_index)
    
    # 遍历词汇表中的每个词语,手动生成One-Hot编码
    for vorcab in vocabs:
        # 创建一个全为0的列表,长度等于词汇表大小(即6个元素)
        zero_list = [0] * len(mytokenizer.index_word)
        # 获取当前词语在word_index中的索引,减1是为了对应列表的0开始索引
        idx = mytokenizer.word_index[vorcab] - 1
        # 将对应位置设为1,形成One-Hot编码(只有一个位置为1,其余为0)
        print(vorcab, '的Ont-Hot编码是:', zero_list)
    
    # 使用joblib保存训练好的Tokenizer对象到指定路径
    # 保存后可在其他地方通过joblib.load()加载复用,避免重复训练
    joblib.dump(value=mytokenizer, filename='data/mytokenizer' )
    print('保存mytokenizer END')
  • 例:One-Hot编码的使用

    python 复制代码
    mytokenizer = joblib.load('data/mytokenizer')
    # 定义要进行One-Hot编码的目标词语
    token1 = "李宗盛"
    # 创建一个全为0的列表,长度与词汇表(vocabs)的元素数量一致
    zero_list1 = [0] * len(vocabs)
    # 检查词汇是否在Tokenizer的词汇表中
    if token1 in mytokenizer.word_index:  
        # 通过加载好的Tokenizer,获取目标词语对应的索引,再减1转换为列表的0起始下标
        # mytokenizer.word_index 是一个字典,键是词语,值是该词语在Tokenizer中的索引(从1开始)
        idx1 = mytokenizer.word_index[token1] - 1
        # 将对应下标的位置置为1,完成One-Hot编码(只有对应位置为1,其余为0)
        zero_list1[idx1] = 1
        print(token1, '的One-Hot编码是:', zero_list1)
    else:
        print(f"{token1}不在词汇表中")
    
    token2 = "狗蛋儿"
    zero_list2 = [0] * len(vocabs)
    if token2 in mytokenizer.word_index:  
        idx2 = mytokenizer.word_index[token2] - 1
        zero_list2[idx2] = 1
        print(token2, '的One-Hot编码是:', zero_list2)
    else:
        print(f"{token2}不在词汇表中")
  • 优点:操作简单,容易理解;

  • 缺点:完全割裂了词与词之间的联系;如果在大语料集下,每个向量的长度过大,会占据大量内存;属于稀疏词向量表示;

  • 正因为One-Hot编码隔离了词和词的联系,又易浪费内存空间,所以出现了稠密向量的表示方法

    • Word2vec
    • Word Embedding

3.3 Word2vec模型

3.3.1 概述

  • Word2vec模型是一种将单词 转换为词向量的自然语言处理技术;

    • 是利用深度学习网络挖掘单词间语义关系,用网络里的权重参数表示词向量(比如"苹果"和"水果"的向量,在语义空间里距离近);
    • 是在无监督的语料(互联网大量文本,没人手动标标签)上构建了一个有监督的任务(预测单词这种带目标的事情),底成本利用海量数据;
  • Word2Vec有两种训练词向量方式:

    • **CBOW(Continuous Bag of Words)**方式训练词向量
      • 试图根据上下文中的周围单词来预测当前单词,即用周围词预测中间词
      • 它将周围单词的词向量求和或取平均作为上下文的表示,然后通过一个神经网络进行预测
    • Skip-Gram 方式训练词向量
      • 试图根据当前单词来预测上下文中的周围单词,即用中间词预测周围词
      • 它将当前单词的词向量作为输入,然后通过一个神经网络来预测周围单词

3.3.2 CBOW方式

  • 已知数据:有5个字母构成的文本序列"abcdeaaeddccbadae ...",其中a、b、c、d、e的One-Hot表示为见下表

    字母 One-Hot表示
    a 1 0 0 0 0
    b 0 1 0 0 0
    c 0 0 1 0 0
    d 0 0 0 1 0
    e 0 0 0 0 1
  • 需求

    • 构建神经网络,输入层数据要求5个特征,隐藏层有3个神经元,输出层5个神经元;
    • 使用已知数据训练这个神经网络;
    • 用隐藏层的权重参数充当 a、b、c、d、 e 这5个单词的词向量;
  • 需求分析

    • 两个问题:

      • 已知文本序列,如何构建神经网络,来探索 a、b、c、d、 e 这5个单词之间的语义关系?
      • a、b、c、d、 e 这5个单词之间的语义关系,又如何保存到神经网络中?
    • 思路:

      • 数据流:输入数据5个特征,经过隐藏层变成3个特征,经过输出层变成5个特征;
      • 数据形状:m*5 >> m*3 >> m*5(m是样本数量),即降维又升维;
      • 隐藏层参数矩阵w为:5*3(5是输入维度,3是隐藏层维度)、输出层参数矩阵w':3*5(3是隐藏层维度,5是输出维度);
      • 训练过程:数据经过前向传播得到预测值ŷ,预测值ŷ与真实值y比较得到损失,通过反向传播可以更新权重矩阵ww'参数;
  • CBOW 核心是用周围单词预测中间单词,过程如下:

    • 构建样本
      • 引入滑动窗口(此处规定滑动窗口大小为3)切分文本序列,构建样本。比如:
        • 样本1:x(a, c),y (b)。中间词是 b ,周围词就是 a 和 c
        • 样本2:x(b, d),y ©。中间词是 c ,周围词就是 b 和 d
        • ......
      • 将x中的两个字母的One-Hot编码拼在一起(或求和平均)当作输入,将y的One-Hot编码当作真实值;
    • 神经网络前向传播(降维+升维)
      • 降维(参数矩阵w):
        • 输入 x(a 和 c 的One-Hot编码拼在一起或者通过求和平均后得到的值)维度是 5 ,通过参数矩阵 w(5*3维度,5是输入维度,3是隐藏层维度) ,把 5 维 "拍扁" 成 3 维隐藏层输出;
        • 这一步叫 "降维",把高维、语义孤立的One-Hot,转成低维、带语义关联的向量(隐藏层输出就是 "词向量雏形");
      • 升维(参数矩阵w'):
        • 隐藏层 3 维输出,再通过参数矩阵 w'(3*5,3是隐藏层维度,5是输出维度) ,升回 5 维,得到预测值ŷ
        • 这一步是 "升维",让模型能输出和真实值同维度的预测结果,方便计算误差;
    • 计算损失(Loss) :拿预测值ŷ(模型猜的b^)和真实值y(实际的b)比,用损失函数算差距。差距越大,Loss 越高,模型越 "差";
    • 反向传播(更新ww' :根据 Loss 反向调整参数矩阵 ww' 的数值,让下次预测更准;
  • 训练结束后,参数矩阵 w 就存着单词的词向量

    • 此处 w 是5*3维度(5是输入维度,3是隐藏层维度),每一列对应一个单词的词向量;

    • 比如对于字母 a 的词向量:

      • 因为 a 的One-Hot是 [1,0,0,0,0],所以w 的 "第 0 列" 。假设w矩阵长这样

        复制代码
        [w11, w12, w13]
        [w21, w22, w23]
        [w31, w32, w33]
        [w41, w42, w43]
        [w51, w52, w53]
      • 那么 a 的词向量是 [w11, w21, w31, w41, w51]

    • 这样,原本孤立的One-Hot,就通过训练,在 w 里变成了"语义相关"的低维向量。

3.3.3 Skip-Gram方式

  • CBOW 核心是用中间单词预测周围单词,过程如下:

    • 构建样本

      • 引入滑动窗口(此处规定滑动窗口大小为3)切分文本序列,构建样本。比如:
        • 样本1:x(b),y (a, c)。中间词是 b ,周围词就是 a 和 c
        • 样本2:x(c),y (b, d)。中间词是 c ,周围词就是 b 和 d
        • ......
      • 将x中的字母的One-Hot编码当作输入,将y的One-Hot编码当作真实值;
    • 神经网络前向传播(降维+升维)

      • 降维(参数矩阵w):
        • 输入 x(b 的 One-hot)维度是 5 ,通过参数矩阵 w(5*3维度,5是输入维度,3是隐藏层维度) ,把 5 维 "拍扁" 成 3 维隐藏层输出;
        • 这一步提取 b 的"语义特征";
      • 升维(参数矩阵w'):
        • 隐藏层 3 维输出,再通过参数矩阵 w'(3*5,3是隐藏层维度,5是输出维度) ,升回 5 维,得到预测值ŷ
        • 这一步让模型能输出和真实值同维度的结果,方便算误差;
    • 计算损失(Loss) :拿预测值ŷ(模型猜的a^和c^)和真实值y(实际的a和c)比,用损失函数算差距。差距越大,Loss 越高,模型越 "差";

    • 反向传播(更新ww' :根据 Loss 反向调整参数矩阵 ww' 的数值,让下次预测更准;

  • 训练结束后,参数矩阵 w 就存着单词的词向量

    • 此处 w 是5*3维度(5是输入维度,3是隐藏层维度),每一行对应一个单词的词向量;

    • 比如对于字母 b 的词向量:

      • 因为 b 的One-Hot是 [0,1,0,0,0],所以w 的 "第 1 行" 。假设w矩阵长这样

        复制代码
        [w11, w12, w13]
        [w21, w22, w23]
        [w31, w32, w33]
        [w41, w42, w43]
        [w51, w52, w53]
      • 那么 b 的词向量是 [w21, w22, w23]

    • 这样,原本孤立的One-Hot,就通过训练,在 w 里变成了"语义相关"的低维向量。

3.3.4 Word2vec模型的训练和使用

3.3.4.1 概述
  • FastText 工具包

    • 背景:Facebook(现 Meta)开源的 NLP 工具;

    • 功能:文本分类、训练词向量;

  • Tomas Mikolov 是 Word2Vec 作者,后来又搞了 FastText ,相当于**"Word2Vec 升级版/拓展版"**:

    • Word2Vec 主要聚焦"单词级"词向量训练;

    • FastText 加入"字符级"信息(比如把"apple"拆成"app""ppl""ple" ),对小语种、生僻词更适配,功能也更丰富(文本分类 + 词向量)。

  • 不管用 Word2Vec 还是 FastText ,训练词向量都绕不开这些步骤:

    • 获取训练数据
    • 词向量的训练、保存、加载、查看
    • 模型效果检验
    • 模型超参数设定
3.3.4.2 获取训练数据
  • 数据来源:http://mattmahoney.net/dc/enwik9.zip,这是英语维基百科的部分网页信息,大小在300M左右(解压后1个G);

  • 原始数据中包含 XML/HTML 格式的内容,这些内容并不是我们需要的,所以接下来对原始数据做一些处理;

  • 编写wikifil.py脚本,用于清洗enwik9文件:

    python 复制代码
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    """
    Python版Wikipedia XML清洗脚本
    
    功能与原Perl脚本(wikifil.pl)完全相同:
    1. 只保留<text>...</text>之间的内容
    2. 移除#REDIRECT页面
    3. 清除所有XML/HTML标签
    4. 转换URL编码字符
    5. 移除参考文献、外部链接、图片标记等
    6. 保留图像说明文字
    7. 将链接转换为普通文本
    8. 将数字拼写出来(如1→one)
    9. 最终只保留小写字母和空格(无连续空格)
    """
    
    import re
    import sys
    
    
    def clean_wiki_text():
        text_mode = False
        with open("enwik9", "r", encoding="utf-8") as f:
            for line in f:
                # 检测是否进入<text>标签
                if "<text " in line:
                    text_mode = True
    
                # 忽略#REDIRECT页面
                if "#redirect" in line.lower():
                    text_mode = False
    
                if text_mode:
                    # 检测是否离开<text>标签
                    if "</text>" in line:
                        text_mode = False
                        continue
    
                    # 移除XML标签
                    line = re.sub(r'<.*?>', '', line)
    
                    # 解码URL编码字符
                    line = line.replace('&amp;', '&')
                    line = line.replace('&lt;', '<')
                    line = line.replace('&gt;', '>')
    
                    # 移除参考文献 <ref...>...</ref>
                    line = re.sub(r'<ref[^<]*<\/ref>', '', line)
    
                    # 移除XHTML标签
                    line = re.sub(r'<[^>]*>', '', line)
    
                    # 处理URL链接,保留可见文本
                    line = re.sub(r'\[http:[^] ]*', '[', line)
    
                    # 移除图片链接标记,保留说明文字
                    line = re.sub(r'\|thumb', '', line, flags=re.IGNORECASE)
                    line = re.sub(r'\|left', '', line, flags=re.IGNORECASE)
                    line = re.sub(r'\|right', '', line, flags=re.IGNORECASE)
                    line = re.sub(r'\|\d+px', '', line, flags=re.IGNORECASE)
                    line = re.sub(r'\[\[image:[^\[\]]*\|', '', line, flags=re.IGNORECASE)
    
                    # 简化分类标记
                    line = re.sub(r'\[\[category:([^|\]]*)[^]]*\]\]', '[[\\1]]', line, flags=re.IGNORECASE)
    
                    # 移除其他语言链接
                    line = re.sub(r'\[\[[a-z\-]*:[^\]]*\]\]', '', line, flags=re.IGNORECASE)
    
                    # 移除wiki URL,保留可见文本
                    line = re.sub(r'\[\[[^\|\]]*\|', '[[', line)
    
                    # 移除{{icons}}和{tables}
                    line = re.sub(r'\{\{[^\}]*\}\}', '', line)
                    line = re.sub(r'\{[^\}]*\}', '', line)
    
                    # 移除方括号
                    line = line.replace('[', '').replace(']', '')
    
                    # 移除其他URL编码字符
                    line = re.sub(r'&[^;]*;', ' ', line)
    
                    # 转换为小写
                    line = line.lower()
    
                    # 将数字拼写出来
                    line = line.replace('0', ' zero ')
                    line = line.replace('1', ' one ')
                    line = line.replace('2', ' two ')
                    line = line.replace('3', ' three ')
                    line = line.replace('4', ' four ')
                    line = line.replace('5', ' five ')
                    line = line.replace('6', ' six ')
                    line = line.replace('7', ' seven ')
                    line = line.replace('8', ' eight ')
                    line = line.replace('9', ' nine ')
    
                    # 移除非字母字符,合并连续空格
                    line = re.sub(r'[^a-z]', ' ', line)
                    line = re.sub(r' +', ' ', line).strip()
    
                    if line:
                        print(line)
    
    
    if __name__ == "__main__":
        clean_wiki_text()
  • 将下面这两个文件放在同一个目录下:

  • 打开 CMD 命令行,执行:

    sh 复制代码
    python wikifil.py > fil9
  • 可以查看生成的fil9文件中的前 20 行内容:

    sh 复制代码
    Get-Content fil9 -Head 20
3.3.4.3 词向量的训练、保存、加载、查看
  • 安装 fasttext:安装的方式具体还是见GitHub - facebookresearch/fastText: Library for fast text representation and classification.

    • 可能涉及到C++17的编译、由于Python版本过高不支持安装等问题,下面只给出示例代码;
    sh 复制代码
    pip install fasttext
  • 训练、保存、加载

    python 复制代码
    def fasttext_train_save_load():
        # 训练词向量
        mymodel = fasttext.train_unsupervised('data/fil9', epoch=1)
        # 保存词向量
        mymodel.save_model("data/mymodel.bin")
        # 加载词向量
        mymodel = fasttext.load_model("data/mymodel.bin")
  • 查看

    python 复制代码
    # 查看词向量
    def fasttext_get_word_vector():
        # 加载已经训练好的词向量模型
        mymodel = fasttext.load_model("data/mymodel.bin")
        # 查看词向量the
        myvector = mymodel.get_word_vector('the')
        print('myvector--->', type(myvector), myvector.shape, myvector)
3.3.4.4 模型效果检验
  • 检查单词向量质量的一种简单方法就是查看其邻近单词,然后主观来判断这些邻近单词是否与目标单词相关,进而来粗略评定模型效果的好坏;

  • 查看临近词:

    python 复制代码
    # 查看临近词
    def fasttext_get_nearest_neighbors():
        # 加载已经训练好的词向量模型
        mymodel = fasttext.load_model("data/mymodel.bin")
        # 查看词向量dog的临近词
        result = mymodel.get_nearest_neighbors('dog')
        print('result--->', result)
3.3.4.5 模型超参数设定
python 复制代码
# 模型参数设定
def fasttext_parm():
    ''' 
    unsupervised_default = {
        'model': "skipgram",    # 1 选择词向量的训练方式
        'lr': 0.05,             # 2 学习率
        'dim': 100,             # 3 词向量特征数
        'ws': 5,
        'epoch': 5,             # 4 训练轮次
        'minCount': 5,
        'minCountLabel': 0,
        'minn': 3,
        'maxn': 6,
        'neg': 5,
        'wordNgrams': 1,
        'loss': "ns",
        'bucket': 2000000,
        'thread': multiprocessing.cpu_count() - 1,  # 5 线程数
        'lrUpdateRate': 100,
        't': 1e-4,
        'label': "__label__",
        'verbose': 2,
        'pretrainedVectors': "",
        'seed': 0,
        'autotuneValidationFile': "",
        'autotuneMetric': "f1",
        'autotunePredictions': 1,
        'autotuneDuration': 60 * 5,  # 5 minutes
        'autotuneModelSize': ""
    }
    '''
    mymodel = fasttext.train_unsupervised('./data/fil9', epoch=1, model='cbow', lr=0.1, dim=300, thread=8)

3.3.5 CBOW VS Skip-Gram

  • 原理:CBOW用周围单词预测当前单词;Skip - Gram凭当前单词预测周围单;
  • 计算效率:CBOW因对多个上下文单词向量求和平均,计算高效;Skip - Gram要为每个单词生成上下文,训练速度慢、效率低;
  • 数据需求:CBOW需更多训练数据汇总整体信息;Skip - Gram在大规模数据中处理低频词更优,能生成丰富上下文信息;
  • 适用场景:CBOW适合训练数据大、高频词情况;Skip - Gram对低频词、复杂语义关系场景表现好,像大规模数据集中处理低频词。

3.4 Word Embedding

  • Word Embedding(词嵌入),通过一定的方式将词汇映射到指定维度(一般是更高维度)的空间;

    • 广义:只要是将单词用向量来表示的方法,都算(比如 Word2Vec、GloVe 这些),不管用不用深度学习;
    • 狭义 :在深度神经网络里,专门加一个 "嵌入层"(比如 PyTorch 的nn.Embedding),用网络训练出向量;
  • Word2Vec方式产生词向量和Word Embedding(nn. Embedding)方式有何异同?

    • 相同点:都是将单词用向量来表示,让计算机能处理文本;
    • 不同点
      • Word2Vec产生词向量后,某一个词向量比如单词"the"的词向量就固定下来了,是静态的
      • nn.Embedding层产生词向量后,词嵌入层作为整体神经网络的一部分,权重参数会参与更新,是动态的
      • Word2Vec使用起来一般需要两步:输入单词"the"拿到词向量 >> 再送给神经网络进行使用;
      • nn.Embedding层使用起来只有一步:直接嵌入到神经网络中。

4 使用tenserboard可视化嵌入的词向量

  • 需求:

    • 有下面两句话

      复制代码
      腾讯是一家上市公司,旗下有王者荣耀、和平精英等游戏,但还是要少玩游戏
      我爱自然语言处理
    • 对这两句话分词,并完成文本数值化、数值张量化

    • 然后对词向量进行可视化

python 复制代码
import torch
from tensorflow.keras.preprocessing.text import Tokenizer
from torch.utils.tensorboard import SummaryWriter
import jieba
import torch.nn as nn
import tensorflow as tf
import tensorboard as tb
tf.io.gfile = tb.compat.tensorflow_stub.io.gfile
python 复制代码
# 1 对句子分词,构建词列表word_list
# 定义两个示例句子
sentence1 = '腾讯是一家上市公司,旗下有王者荣耀、和平精英等游戏,但还是要少玩游戏'
sentence2 = '我爱自然语言处理'
sentences = [sentence1, sentence2]
word_list = []
# 用jieba对每个句子分词,结果存入word_list
for s in sentences:
    word_list.append(jieba.lcut(s))
print('word_list--->', word_list)
python 复制代码
# 2 对句子分词列表word_list做词表构建,得到每个词的索引映射
mytokenizer = Tokenizer()
mytokenizer.fit_on_texts(word_list)
print('mytokenizer.index_word--->', mytokenizer.index_word)
print('mytokenizer.word_index--->', mytokenizer.word_index)
my_token_list = list(mytokenizer.index_word.values())  # 获取词列表,用于后续可视化
print('my_token_list--->', my_token_list)
python 复制代码
# 3 创建nn.Embedding层,实现词嵌入
# num_embeddings是词表大小(不同词的数量),embedding_dim是词向量维度
embed = nn.Embedding(num_embeddings=len(mytokenizer.index_word), embedding_dim=8)
print('词嵌入层embed--->', embed)
# 设置打印精度,方便查看张量值
torch.set_printoptions(precision=4, sci_mode=False)
print('词嵌入层的矩阵参数(每个单词的词向量)embed--->', embed.weight.data)
python 复制代码
# 4 创建SummaryWriter对象,可视化词向量
# 用于把词嵌入矩阵和词列表写入TensorBoard,方便可视化查看
summarywriter = SummaryWriter()  
summarywriter.add_embedding(embed.weight.data, my_token_list)
summarywriter.close()
python 复制代码
# 5 通过索引获取词向量
# 遍历词表索引,获取对应词向量并打印
for idx in range(len(mytokenizer.index_word)):
    tmp_vec = embed(torch.tensor([idx]))
    print('tmp_vec--->', tmp_vec.detach().numpy())
  • 在程序的当前目录下运行:tensorboard --logdir=runs --host 0.0.0.0,再通过http://127.0.0.1:6006访问。
相关推荐
爱分享的飘哥32 分钟前
第七十章:告别“手写循环”噩梦!Trainer结构搭建:PyTorch Lightning让你“一键炼丹”!
人工智能·pytorch·分布式训练·lightning·accelerate·训练框架·trainer
阿里云大数据AI技术1 小时前
PAIFuser:面向图像视频的训练推理加速框架
人工智能·机器学习
盛世隐者1 小时前
【深度学习】pytorch深度学习框架的环境配置
人工智能·pytorch·深度学习
说私域1 小时前
基于开源链动2+1模式AI智能名片S2B2C商城小程序的流量转化策略研究
人工智能·小程序
funfan05171 小时前
GPT-5博士级AI使用教程及国内平替方案
人工智能·gpt
萤丰信息2 小时前
技术赋能安全:智慧工地构建城市建设新防线
java·大数据·开发语言·人工智能·智慧城市·智慧工地
AI视觉网奇2 小时前
音频分类模型笔记
人工智能·python·深度学习
Dante但丁2 小时前
手扒Github项目文档级知识图谱构建框架RAKG(保姆级)Day4
人工智能
用户5191495848452 小时前
使用JavaScript与CSS创建"移动高亮"导航栏
人工智能·aigc
Java中文社群2 小时前
淘宝首位程序员离职,竟投身AI新公司做这事!
人工智能·后端·程序员