《GPT图解—大模型是怎样构建的》学习(第二天)

第1课 高楼万丈平地起:语言模型的维形 N-Gram 和简单文本表示 Bag-of-Words

        语言模型是一个用来预估文本概率分布的数学模型,他可以帮助我们了解某个文本序列在自然语言中出现的概率,因此也就能够根据给定的文本预测下一个最可能出现的单词。语言模型关注的是上下文单词之间的相关性,以保证模型生成的文本序列是合理的语句。

1.1 N-Gram 模型

        信息论的奠基人香农提出来一个衡量信息量的方法叫做香农熵。他认为要衡量一句话的信息量就要了解每一个单词出现的概率。随后研究人员开始尝试统计的方法来预测文本中的词序列概率,这种为了预测词汇出现概率而使用的统计方法就是自然语言处理中的概率模型。概率模型的基本思想就是给定一个词序列,计算下一个词出现的概率。而且由于词序列可能非常长,计算整个序列的联合概率(联合概率指的是多个随机变量同时满足特定条件的概率,例如p(x,y)表示随机变量x取这个值,同时y取那个值的概率)会变得非常复杂。

        在N-Gram模型中,我们通过将文本分割成连续的N个词的组合(N-Gram)来近似的描述词序列的联合概率。假设一个词出现的概率仅依赖于他前面N-1个词。换句话说利用有限的上下文信息来近似预测下一词的概率。

        N-Gram模型的构建过程:

(1)将给定文本分割成连续的N个词的组合(N-Gram)

比如在二元模型中,将文本分割成相邻两个词构成的组合,称之为“二元组(2-Gram)”

(2)统计每一个N-Gram在文本中出现的次数,就是词频,如图二元组“我爱”词频为3

(3)为了得到一个词在给定上下文中出现的概率,可以利用条件概率公式计算。具体就是极速昂定前N-1个词时,下一次出现的概率。这个概率可以通过计算某个N-gram出现的次数和前N-1个词出现次数之比得到。比如二元组“我爱”出现了3次,二元组前缀“我”出现了10次,那么给定“我”,下一词为“爱”的概率为30%。

(4)可以使用这些概率来预测文本中下一个词出现的可能性。多次迭代这个过程,甚至可以生成整个句子,也可以算出每个句子在语料库中出现的概率。

1.2 “词”是什么,如何“分词”

        在“孙悟空三打白骨精”中,将“白骨精”作为一个词,也就是一个Gram来处理的,我们也可以进行粒度更细的划分,那么每一个Gram就代表的是一个字了。一般自然语言处理工具包都为我们提供好了分词工具,比如英文分词通常使用NLTK、spaCy等自然语言处理库,中文分词通常使用jieba库。

1.3 创建一个 Bigram 字符预测模型

一个小实验,Bigram字符预测模型程序的整体结构:

(1)构建实验语料库

# 构建一个玩具数据集
corpus = ["我特别特别喜欢看电影",
        "这部电影真的是很好看的电影",
        "今天天气真好是难得的好天气",
        "我今天去看了一部电影",
        "电影院的电影都很好看"]

(2)把句子分词N个gram(分词)

# 定义一个分词函数,将文本转换为单个字符的列表
def tokenize(text):
 return [char for char in text] # 将文本拆分为字符列表
# 对每个文本进行分词,并打印出对应的单字列表
print("单字列表:") 
for text in corpus:
    tokens = tokenize(text)
    print(tokens)

(3)计算每一个Bigram在词库中的词频

# 定义计算 N-Gram 词频的函数
from collections import defaultdict, Counter # 导入所需库
def count_ngrams(corpus, n):
    ngrams_count = defaultdict(Counter)  # 创建一个字典,存储 N-Gram 计数
    for text in corpus:  # 遍历语料库中的每个文本
        tokens = tokenize(text)  # 对文本进行分词
        for i in range(len(tokens) - n + 1):  # 遍历分词结果,生成 N-Gram
            ngram = tuple(tokens[i:i+n])  # 创建一个 N-Gram 元组
            prefix = ngram[:-1]  # 获取 N-Gram 的前缀
            token = ngram[-1]  # 获取 N-Gram 的目标单字
            ngrams_count[prefix][token] += 1  # 更新 N-Gram 计数
    return ngrams_count
bigram_counts = count_ngrams(corpus, 2) # 计算 bigram 词频
print("bigram 词频:") # 打印 bigram 词频
for prefix, counts in bigram_counts.items():
    print("{}: {}".format("".join(prefix), dict(counts))) 

(4)计算每个Bigram的出现概率

# 定义计算 N-Gram 出现概率的函数
def ngram_probabilities(ngram_counts):
 ngram_probs = defaultdict(Counter) # 创建一个字典,存储 N-Gram 出现的概率
 for prefix, tokens_count in ngram_counts.items(): # 遍历 N-Gram 前缀
     total_count = sum(tokens_count.values()) # 计算当前前缀的 N-Gram 计数
     for token, count in tokens_count.items(): # 遍历每个前缀的 N-Gram
         ngram_probs[prefix][token] = count / total_count # 计算每个 N-Gram 出现的概率
 return ngram_probs
bigram_probs = ngram_probabilities(bigram_counts) # 计算 bigram 出现的概率
print("\nbigram 出现的概率 :") # 打印 bigram 概率
for prefix, probs in bigram_probs.items():
 print("{}: {}".format("".join(prefix), dict(probs)))

(5)根据Bigram出现的概率,定义生成下一个词的函数

# 定义生成下一个词的函数
def generate_next_token(prefix, ngram_probs):
 if not prefix in ngram_probs: # 如果前缀不在 N-Gram 中,返回 None
    return None
 next_token_probs = ngram_probs[prefix] # 获取当前前缀的下一个词的概率
 next_token = max(next_token_probs, 
                    key=next_token_probs.get) # 选择概率最大的词作为下一个词
 return next_token

(6)输入一个前缀,生成连续文本

# 定义生成连续文本的函数
def generate_text(prefix, ngram_probs, n, length=6):
 tokens = list(prefix) # 将前缀转换为字符列表
 for _ in range(length - len(prefix)): # 根据指定长度生成文本 
     # 获取当前前缀的下一个词
     next_token = generate_next_token(tuple(tokens[-(n-1):]), ngram_probs) 
     if not next_token: # 如果下一个词为 None,跳出循环
         break
     tokens.append(next_token) # 将下一个词添加到生成的文本中
 return "".join(tokens) # 将字符列表连接成字符串

结果:

# 输入一个前缀,生成文本
generated_text = generate_text("我", bigram_probs, 2)
print("\n 生成的文本:", generated_text) # 打印生成的文本

N-gram模型中优点很明显就是计算简单,缺点也很明显,无法捕捉到距离较远的词之间的关系。

1.4 词袋模型

        词袋模型(Bag-of-Words)并不考虑哪个词和哪个词临近,而是通过把词看做一袋子元素的方式将文本转换为能统计的特征。

        词袋模型是一种简单的文本表示方法,也是自然语言处理的一个经典模型。她将文本中的词看做一个个独立的个体,并不考虑他们在句子中的顺序,只关心每个词出现的频次。

        词袋会将两个句子表示为如下向量,通过比较两个向量之间的相似度,我们就可以判断出它们之间关联性的强弱。

{“迦哥”:1,“喜欢”:1,“吃”:1,“苹果”:1}

{“苹果”:1,“是”:1,“迦哥”:1,“喜欢”:1,“的”:1,“水果”:1}

1.5 用词袋模型计算文本相似度

一个小实验,词袋模型比较一些文本相似度程序的整体结构:

(1)构建实验语料库

# 构建一个玩具数据集
corpus = ["我特别特别喜欢看电影",
        "这部电影真的是很好看的电影",
        "今天天气真好是难得的好天气",
        "我今天去看了一部电影",
        "电影院的电影都很好看"]

(2)给句子分词

# 对句子进行分词
import jieba # 导入 jieba 包
# 使用 jieba.cut 进行分词,并将结果转换为列表,存储在 corpus_tokenized 中
corpus_tokenized = [list(jieba.cut(sentence)) for sentence in corpus]

(3)创建词汇表

# 创建词汇表
word_dict = {} # 初始化词汇表
# 遍历分词后的语料库
for sentence in corpus_tokenized:
    for word in sentence:
        # 如果词汇表中没有该词,则将其添加到词汇表中
        if word not in word_dict:
            word_dict[word] = len(word_dict) # 分配当前词汇表索引
print(" 词汇表:", word_dict) # 打印词汇表

(4)生成词袋表示

# 根据词汇表将句子转换为词袋表示
bow_vectors = [] # 初始化词袋表示
# 遍历分词后的语料库
for sentence in corpus_tokenized:
    # 初始化一个全 0 向量,其长度等于词汇表大小
    sentence_vector = [0] * len(word_dict)
    for word in sentence:
        # 将对应词的索引位置加 1,表示该词在当前句子中出现了一次
        sentence_vector[word_dict[word]] += 1
    # 将当前句子的词袋向量添加到向量列表中
    bow_vectors.append(sentence_vector)
print(" 词袋表示:", bow_vectors) # 打印词袋表示

(5)计算余弦相似度,衡量两个文本向量的相似性,对于词袋表示的文本向量,使用余弦相似度计算文本之间的相似程度可以减少句子长度差异带来的影响,值越接近1越相似。

# 导入 numpy 库,用于计算余弦相似度
import numpy as np 
# 定义余弦相似度函数
def cosine_similarity(vec1, vec2):
    dot_product = np.dot(vec1, vec2) # 计算向量 vec1 和 vec2 的点积
    norm_a = np.linalg.norm(vec1) # 计算向量 vec1 的范数
    norm_b = np.linalg.norm(vec2) # 计算向量 vec2 的范数  
    return dot_product / (norm_a * norm_b) # 返回余弦相似度
# 初始化一个全 0 矩阵,用于存储余弦相似度
similarity_matrix = np.zeros((len(corpus), len(corpus)))
# 计算每两个句子之间的余弦相似度
for i in range(len(corpus)):
    for j in range(len(corpus)):
        similarity_matrix[i][j] = cosine_similarity(bow_vectors[i], 
                                                    bow_vectors[j])

(6)可视化余弦相似度

# 导入 matplotlib 库,用于可视化余弦相似度矩阵
import matplotlib.pyplot as plt
plt.rcParams["font.family"]=['SimHei'] # 用来设定字体样式
plt.rcParams['font.sans-serif']=['SimHei'] # 用来设定无衬线字体样式
plt.rcParams['axes.unicode_minus']=False # 用来正常显示负号
fig, ax = plt.subplots() # 创建一个绘图对象
# 使用 matshow 函数绘制余弦相似度矩阵,颜色使用蓝色调
cax = ax.matshow(similarity_matrix, cmap=plt.cm.Blues)
fig.colorbar(cax) # 条形图颜色映射
ax.set_xticks(range(len(corpus))) # x 轴刻度
ax.set_yticks(range(len(corpus))) # y 轴刻度
ax.set_xticklabels(corpus, rotation=45, ha='left') # 刻度标签 
ax.set_yticklabels(corpus) # 刻度标签为原始句子
plt.show() # 显示图形

        词袋模型相对简单,主要存在两个问题,第一:使用高维稀疏向量表示文本,导致模型更适用于高维空间,而且计算效率低。第二:词袋模型在表示单词时忽略了它们在文本中上下文信息,模型无法捕捉单词之间的语义关系,因为单词在空间中的相对位置没有意义。

小结

        N-Gram 和 Bag-of-Words 是两种非常基础但是仍然十分常用的自然语言处理技术,它们都用于表示文本数据,但具有不同的特点和适用场景。
1.使用给定的语料库,分别计算并比较二元组(N=2)、三元组(N=3)和四元组(N=4)出现的概率。观察不同的N对模型结果的影响,并分析原因。
N-Gram 是一种用于语言建模的技术,它用来估计文本中词序列的概率分布N-Gram 模型将文本看作一个由词序列构成的随机过程,根据已有的文本数据,计算出词序列出现的概率。因此,N-Gram 主要用于语言建模、文本生成、语音识别等自然语言处理任务中。
2.在词袋模型中,所有词的重要性是相同的。然而,在实际文本中,一些词(如停用词)可能出现频率高,但并不重要,而一些词出现频率低,但可能非常重要。请你自主学习 TF-IDF(词频-逆文档频率)表示,并用这种表示方式解决这个问题。
(3)随着 N的增加,模型可能会遇到数据稀疏性问题,导致模型性能下降。
        Bag-of-Words 则是一种用于文本表示的技术,它将文本看作由单词构成的无序集合,通过统计单词在文本中出现的频次来表示文本。因此,Bag-of-Words主要用于文本分类、情感分析、信息检索等自然语言处理任务中,
(1)Bag-of-Words 是基于词频将文本表示为一个向量,其中每个维度对应词汇表中的一个单词,其值为该单词在文本中出现的次数。
(2)Bag-of-Words 忽略了文本中的词序信息,只关注词频。这使得词袋模型在某些任务中表现出色,如主题建模和文本分类,但在需要捕捉词序信息的任务中表现较差,如机器翻译和命名实体识别。
(3)Bag-of-Words 可能会导致高维稀疏表示,因为文本向量的长度取决于词汇表的大小。为解决这个问题,可以使用降维技术,如主成分分析(PrincipalComponentAnalysis,PCA)或潜在语义分析(LatentSemantic Analysis,LSA)。

  • 15
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值