LLM模型:代码讲解Transformer运行原理

news2025/1/6 9:20:31

视频讲解、获取源码:LLM模型:代码讲解Transformer运行原理(1)_哔哩哔哩_bilibili

1 训练保存模型文件

2 模型推理

3 推理代码


import torch
import tiktoken
from wutenglan_model import WutenglanModel

import pyttsx3

# 设置设备为CUDA(如果可用),否则使用CPU
# 这是因为许多深度学习算法在GPU上运行更高效
device = torch.device('cpu')
encoding = tiktoken.get_encoding("cl100k_base")

# 加载模型
model = WutenglanModel(99849)
model_state_dict = torch.load('model\model-wutenglan-base.pt', map_location=device)
model.load_state_dict(model_state_dict)
model.eval()
model.to(device)

start = '武'
# 对起始点进行编码,将其转换为模型可接受的输入格式
start_ids = encoding.encode(start)

# 为编码的起始点创建一个张量,并且通过unsqueeze操作增加一个新的维度,以适应模型的输入要求
input_content = (torch.tensor(start_ids, dtype=torch.long, device=device)[None, ...])

def text_to_speech(text):
    # 初始化引擎
    engine = pyttsx3.init()
    
    # 设置语速,可选参数,根据需要调整
    rate = engine.getProperty('rate')
    engine.setProperty('rate', rate - 50)  # 减少语速
    
    # 设置音量,范围 0.0 到 1.0
    volume = engine.getProperty('volume')
    engine.setProperty('volume', volume + 100)  # 增加音量
    
    # 获取可用的声音列表
    voices = engine.getProperty('voices')
    # 设置声音,例如选择第一个声音
    engine.setProperty('voice', voices[0].id)
    
    # 朗读文本
    engine.say(text)
    
    # 运行引擎,等待完成
    engine.runAndWait()

# 在不进行梯度计算的上下文中生成文本,以减少内存占用并提高效率
with torch.no_grad():
    # 使用预训练模型生成文本,基于输入`input_content`,最大新生成的token数量为5
    output = model.inference(input_content, max_new_tokens=5)
    # 将生成的token序列转换为字符串文本
    text = encoding.decode(output[0].tolist())
    # 输出生成的文本到控制台
    print(text)
    # 将生成的文本转换为语音输出
    text_to_speech(text)



4 推理重点代码详解

4.1 截取上下文长度

last_context = input_content[:, -context_length:]

这行代码的主要作用是从输入序列 input_content 中截取最后 context_length 个token作为当前的上下文。这一操作对于许多基于Transformer架构的语言模型来说是非常重要的,因为这些模型通常有一个固定的输入长度限制(即 context_length),超过这个长度的输入需要被裁剪,以适应模型的处理能力。

如果删除这一行代码,那么在每次迭代中,你将会使用整个 input_content 作为模型的输入,这可能导致以下问题:

  1. 超出模型处理能力:如果 input_content 的长度超过了模型的设计长度(context_length),那么模型可能无法正确处理这么长的序列,导致错误或次优的结果。

  2. 内存问题:处理过长的序列可能会导致内存不足的问题,特别是在GPU上运行大型模型时。

  3. 计算效率低下:即使模型能够处理更长的序列,这样做也可能导致计算资源的浪费,因为模型只需要最新的上下文信息来进行下一步的预测。

方括号内的两个冒号是用来进行数组切片(slice)操作的符号。具体来说,在表达式 input_content[:, -context_length:] 中,第一个冒号和第二个冒号分别有不同的含义:

  1. 第一个冒号 (:)

    • 第一个冒号表示的是“所有”的意思。在这个上下文中,它意味着选择所有行。例如,在二维数组(矩阵)中,如果我们想要选择所有的行,我们可以使用 :。这意味着我们会保留所有的行。
  2. 第二个冒号 (:)

    • 第二个冒号同样用于指定切片范围,但它跟在负数索引 -context_length 后面,表示从 -context_length 到结尾(end of the sequence)。这里的 -context_length 是一个负数索引,它指向列表或数组的末尾往前数第 context_length 个位置。所以 input_content[:, -context_length:] 意味着选择每行的最后 context_length 个元素。

 4.2 线性层计算结果说明

【线性变换结果】

# 使用前向传播方法得到linear_predictions和损失
linear_predictions, loss = self.forward(last_context, None)

以上代码输出的linear_predictions形状如下: 

【线性变换说明】

linear_predictions 的形状 torch.Size([1, 2, 99850]) 中,每个维度都有特定的意义:

  1. 第一个维度 1

    • 这个维度通常表示批次大小(batch size)。在这里,1 表示当前批次只有一个样本。这意味着在这一时刻,模型正在处理一个单独的序列。
  2. 第二个维度 2

    • 这个维度通常表示时间步的数量(time steps)。在这里,2 表示当前批次中有两个时间步。这可能是因为 input_content 在进入模型之前已经包含了一个或多个先前的时间步,并且当前批次包含这些先前的时间步加上当前的时间步。
  3. 第三个维度 99850

    • 这个维度通常表示词汇表大小(vocabulary size)或输出特征的数量。在这里,99850 表示模型的输出空间大小,也就是模型可以预测的词汇数量。换句话说,模型在每个时间步上可以预测 99850 个不同的token之一。

总结一下:

  • [1]:批次大小,当前批次包含一个样本。
  • [2]:时间步的数量,当前批次包含两个时间步。
  • [99850]:输出特征的数量,模型可以预测 99850 个不同的token。

在文本生成任务中,模型的输出通常是一个概率分布,表示在当前上下文下下一个token的可能性。因此,linear_predictions 的形状表明模型在一个样本上有两个时间步,并且在每个时间步上输出了 99850 个可能的下一个token的概率值。

4.3 只关注最后一个时间步的linear_predictions

为什么要取最后一个时间步,而不取所有的。

假设我们有一个简单的文本生成任务,目的是根据前面的文本生成下一个词。为了简化讨论,我们假设词汇表只有几个词,比如 ['hello', 'world', 'how', 'are', 'you']

实际数据示例

假设我们的输入序列是 ['hello', 'world'],我们希望模型根据这个序列生成下一个词。为了说明这一点,我们可以构造一个简单的示例。

构造示例数据

  1. 词汇表

    • 假设词汇表为 ['hello', 'world', 'how', 'are', 'you']
  2. 编码映射

    • 我们需要将词汇表中的每个词映射到一个整数。例如:
      • 'hello' -> 0
      • 'world' -> 1
      • 'how' -> 2
      • 'are' -> 3
      • 'you' -> 4
  3. 输入序列

    • 输入序列 input_sequence 为 ['hello', 'world'],对应的编码序列为 [0, 1]

构造 linear_predictions 张量

假设模型已经处理了输入序列,并生成了 linear_predictions 张量,其形状为 [1, 2, 5]。这意味着:

  • 第一维 [1] 表示批次大小,当前批次包含一个样本。
  • 第二维 [2] 表示时间步的数量,当前批次包含两个时间步。
  • 第三维 [5] 表示词汇表大小。

假设 linear_predictions 的具体数值如下:

1linear_predictions = torch.tensor([
2    [
3        [0.1, 0.2, 0.3, 0.4, 0.5],  # 第一个时间步的预测值
4        [0.2, 0.3, 0.4, 0.5, 0.6]   # 第二个时间步的预测值
5    ]
6])

为什么取最后一个时间步

在文本生成任务中,我们通常只关心当前上下文的下一个词的预测。因此,我们通常取最后一个时间步的预测值来生成下一个词。

  1. 取最后一个时间步

    • linear_predictions[:, -1, :] 取的是最后一个时间步的预测值。
    • 在这个例子中,linear_predictions[:, -1, :] 将得到:
      tensor([[0.2, 0.3, 0.4, 0.5, 0.6]])
  2. 计算概率分布

    • 接下来,我们使用 F.softmax 将这些预测值转换为概率分布。
      import torch.nn.functional as F
      
      last_timestep_predictions = linear_predictions[:, -1, :]
      probs = F.softmax(input=last_timestep_predictions, dim=-1)
  3. 采样下一个词

    • 使用 torch.multinomial 从概率分布中采样下一个词。
      idx_next = torch.multinomial(input=probs, num_samples=1)

为什么不取所有时间步

如果我们取所有的时间步,即 linear_predictions[:, :, :],我们将得到:

1tensor([
2    [
3        [0.1, 0.2, 0.3, 0.4, 0.5],
4        [0.2, 0.3, 0.4, 0.5, 0.6]
5    ]
6])

这将导致以下问题:

  1. 形状不匹配

    • 在计算 softmax 时,需要一个二维张量作为输入。如果我们取所有的时间步,形状为 [1, 2, 5],需要进一步处理才能进行 softmax 计算。
  2. 逻辑不一致

    • 在文本生成任务中,我们通常只关心当前上下文的下一个词的预测。取所有时间步的信息可能会引入不必要的复杂度,并且不符合逐词生成的逻辑。

示例代码

1import torch
2import torch.nn.functional as F
3
4# 构造示例数据
5vocab = ['hello', 'world', 'how', 'are', 'you']
6word_to_idx = {word: idx for idx, word in enumerate(vocab)}
7
8# 输入序列
9input_sequence = ['hello', 'world']
10encoded_sequence = [word_to_idx[word] for word in input_sequence]
11
12# 构造 linear_predictions 张量
13linear_predictions = torch.tensor([
14    [
15        [0.1, 0.2, 0.3, 0.4, 0.5],  # 第一个时间步的预测值
16        [0.2, 0.3, 0.4, 0.5, 0.6]   # 第二个时间步的预测值
17    ]
18])
19
20# 取最后一个时间步
21last_timestep_predictions = linear_predictions[:, -1, :]
22
23# 计算概率分布
24probs = F.softmax(input=last_timestep_predictions, dim=-1)
25
26# 采样下一个词
27idx_next = torch.multinomial(input=probs, num_samples=1)
28
29# 解码下一个词
30next_word = vocab[idx_next.item()]
31print("Next word:", next_word)

通过这种方式,我们只关注当前上下文的下一个词的预测。

4.4 SoftMax激活函数

Softmax 函数是一种常用的激活函数,在机器学习尤其是深度学习中具有重要作用。它的主要用途包括:

概率化输出:

Softmax 函数将一个 K 维的实数向量转换为另一个 K 维的向量,其中每个元素都是 [0, 1] 区间内的值,并且所有元素的和为 1。因此,经过 Softmax 处理后的输出可以解释为概率分布,这使得模型能够输出每个类别的预测概率。
6.2 激活函数的计算过程
假设我们有一个未归一化的得分向量 z=[1.0,2.0,3.0]z=[1.0,2.0,3.0],代表三个类别的得分。

4.5 torch.multinomial 进行随机采样

4.5.1 torch.multinomial作用

idx_next = torch.multinomial(input=probs, num_samples=1) 这段代码的作用是从一个概率分布中随机采样一个样本。具体来说,这段代码在文本生成任务中用于从当前时间步的预测概率分布中选择下一个 token 的索引。下面详细介绍这段代码的具体作用和实现细节。

代码详解

  1. probs 参数

    • probs 是一个一维张量,表示一个概率分布。在文本生成任务中,这个概率分布通常是对下一个可能生成的 token 的概率估计。例如,在你的例子中,probs 的形状是 [1, 99850],表示一个样本在最后一个时间步上的 99850 个可能的下一个 token 的概率分布。
  2. num_samples 参数

    • num_samples 表示从概率分布中采样的样本数量。在这个例子中,num_samples=1 表示我们只采样一个样本。
  3. torch.multinomial 函数

    • torch.multinomial 是 PyTorch 中的一个函数,用于从一个多维概率分布中进行随机采样。它根据提供的概率分布进行采样,返回一个索引,该索引指示采样出的 token 在词汇表中的位置。

具体实现

假设我们有一个概率分布 probs,其形状为 [1, 99850]。我们希望从这个概率分布中采样一个 token 的索引。

示例代码

1import torch
2import torch.nn.functional as F
3
4# 构造示例数据
5probs = torch.tensor([
6    [0.1, 0.2, 0.3, 0.15, 0.1, 0.05, 0.1]
7])  # 示例概率分布,形状为 [1, 7]
8
9# 从概率分布中采样一个 token 的索引
10idx_next = torch.multinomial(input=probs, num_samples=1)
11
12print("Sampled index:", idx_next.item())

代码流程

  1. 构造概率分布

    • probs 是一个形状为 [1, 99850] 的张量,表示一个样本在最后一个时间步上的 99850 个可能的下一个 token 的概率分布。
  2. 采样

    • 使用 torch.multinomial 从概率分布 probs 中采样一个索引。num_samples=1 表示我们只采样一个样本。
  3. 结果

    • idx_next 是一个形状为 [1, 1] 的张量,表示采样出的下一个 token 的索引。

详细解释

  1. 概率分布

    • probs 张量中的每个元素都是一个概率值,表示相应 token 被选中的概率。所有概率值之和为 1。
  2. 采样过程

    • torch.multinomial 根据提供的概率分布进行采样。它从 [0, 99849] 范围内随机选择一个索引,这个索引是基于概率分布 probs 进行的加权选择。也就是说,概率值较高的 token 被选中的可能性更大。
  3. 结果索引

    • idx_next 是一个形状为 [1, 1] 的张量,表示采样出的下一个 token 的索引。例如,如果 idx_next 的值为 3,则表示下一个 token 的索引为 3

示例代码

假设 probs 的具体值如下:

1probs = torch.tensor([
2    [0.1, 0.2, 0.3, 0.15, 0.1, 0.05, 0.1]
3])  # 示例概率分布,形状为 [1, 7]

我们从这个概率分布中采样一个 token 的索引:

1idx_next = torch.multinomial(input=probs, num_samples=1)
2print("Sampled index:", idx_next.item())

输出结果可能是:

1Sampled index: 2

这意味着根据给定的概率分布,采样出的下一个 token 的索引为 2

总结

idx_next = torch.multinomial(input=probs, num_samples=1) 这段代码用于从一个概率分布中采样一个 token 的索引。在文本生成任务中,这一步骤用于确定下一个生成的 token。通过这种方式,模型可以根据当前上下文的概率分布生成下一个 token,从而逐步构建出完整的文本序列。

4.5.2 torch.multinomial随机采样原因

在文本生成任务中,使用 torch.multinomial 进行随机采样而不是直接选择概率最大的 token 主要有以下几个原因:

1. 探索性(Exploration)

在生成文本时,如果每次都选择概率最大的 token,模型的行为会变得非常确定性和单调。这可能会导致生成的文本缺乏多样性和创造性。通过随机采样,模型可以在一定程度上探索不同的生成路径,从而产生更加丰富和多样的文本。

2. 平滑峰值(Smoothing Peaks)

在某些情况下,模型可能会过于自信地选择某个 token,即使这个 token 不一定是最佳选择。通过随机采样,可以平衡模型的置信度,避免过度依赖某个特定的选择。这有助于模型生成更加自然和流畅的文本。

3. 避免局部最优(Avoiding Local Optima)

直接选择概率最大的 token 容易让模型陷入局部最优解。通过随机采样,模型有机会跳出局部最优解,探索更多的可能性,从而找到全局最优解。

4. 更真实的模拟人类行为

人类在写作或说话时,并不总是选择最有可能的词,而是会根据上下文和个人风格做出选择。随机采样可以更好地模拟这种行为,使得生成的文本更加自然和真实。

5. 多样性(Diversity)

随机采样可以增加生成文本的多样性。如果总是选择概率最大的 token,生成的文本可能会非常相似,缺乏变化。通过随机采样,可以引入更多的变化,使得生成的文本更加多样化。

6. 控制生成过程

在某些应用场景中,可以通过调整采样过程中的温度参数(temperature parameter)来控制生成过程的随机程度。温度参数是一个正数,可以调节模型生成文本的多样性:

  • 温度较高(例如,大于1):模型的生成更加随机,可以探索更多的可能性。
  • 温度较低(例如,小于1但大于0):模型的生成更加确定,倾向于选择高概率的 token。

示例代码

假设我们有一个概率分布 probs,我们可以调整温度参数来控制随机性:

1import torch
2import torch.nn.functional as F
3
4# 构造示例数据
5probs = torch.tensor([
6    [0.1, 0.2, 0.3, 0.15, 0.1, 0.05, 0.1]
7])  # 示例概率分布,形状为 [1, 7]
8
9# 调整温度参数
10temperature = 0.7  # 可以调整此参数
11
12# 应用温度参数
13probs = probs / temperature
14
15# 归一化概率分布
16probs = F.softmax(probs, dim=-1)
17
18# 从概率分布中采样一个 token 的索引
19idx_next = torch.multinomial(input=probs, num_samples=1)
20
21print("Sampled index:", idx_next.item())

总结

通过使用 torch.multinomial 进行随机采样,而不是直接选择概率最大的 token,可以带来更多的多样性和探索性,使得生成的文本更加自然和丰富。这种做法在实践中已经被证明能够提高生成文本的质量和多样性。

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

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

相关文章

javaWeb【day04】--(MavenSpringBootWeb入门)

01. Maven课程介绍 1.1 课程安排 学习完前端Web开发技术后,我们即将开始学习后端Web开发技术。做为一名Java开发工程师,后端Web开发技术是我们学习的重点。 1.2 初识Maven 1.2.1 什么是Maven Maven是Apache旗下的一个开源项目,是一款用于…

数据治理:企业数字化转型的关键环节

数据治理:企业数字化转型的关键环节 前言数据治理:企业数字化转型的关键环节 前言 在当今数字化时代,企业的发展与数据紧密相连。数据已成为企业的重要资产,而如何有效地治理数据,使其发挥最大价值,成为企…

Python安装llama库出错“metadata-generation-failed”

Python安装llama库出错“metadata-generation-failed” 1. 安装llama库时出错2. 定位问题1. 去官网下载llama包 2.修改配置文件2.1 解压文件2.2 修改配置文件 3. 本地安装文件 1. 安装llama库时出错 2. 定位问题 根据查到的资料,发现时llama包中的execfile函数已经…

旋翼无人机的应用场景和用途!!!

1. 航拍摄影 全景拍摄:旋翼无人机可以携带摄像装置进行大规模航拍,广泛应用于影视制作、广告拍摄、城市规划、房地产宣传等领域。其独特的视角和高度,能够拍摄到地面难以捕捉的壮丽景色,为观众带来震撼的视觉效果。 测绘与地理信…

USB通信协议基础概念

文章目录 一、什么是USB1. **标准化接口**2. **热插拔**3. **即插即用**4. **电源供给**5. **数据传输速度**6. **连接类型**7. **协议和功能** 二、USB的三个部分1. **USB Host(主机)**2. **USB Device(设备)**3. **USB Hub&…

Ubuntu 22.04 make menuconfig 失败原因

先 安装一些配置 linux下使用menuconfig需要安装如下库_menuconfig 安装-CSDN博客 然后 cd 到指定源代码 需要在内核文件目录下编译 Linux 内核源码(kernel source)路径_--kernel-source-path-CSDN博客 make menuconfig 又报错 说是gcc 12什么什么&…

Linux概述、远程连接、常用命令

Linux介绍 Linux操作系统介绍 Linux操作系统的特点 开源免费安全稳定可移植性好 Linux可以安装在不同的设备上 高性能 Linux的使用领域 应用服务器数据库服务器网络服务器虚拟化云计算嵌入式领域个人PC移动手机 Linux文件系统和目录 /:根目录,唯一/h…

RT-Thread(Nano版本)的快速移植(基于NUCLEO-F446RE)

目录 概述 1 RT-Thread 1.1 RT-Thread的版本 1.2 认识Nano版本 2 STM32F446U上移植RT-Thread 2.1 STM32Cube创建工程 2.2 移植RT-Thread 2.2.1 安装RT-Thread Packet 2.2.2 加载RT-Thread 2.2.3 匹配相关接口 2.2.3.1 初次编译代码 2.2.3.2 匹配端口 2.2.4 移植FinSH…

基于飞腾平台的Hive的安装配置

【写在前面】 飞腾开发者平台是基于飞腾自身强大的技术基础和开放能力,聚合行业内优秀资源而打造的。该平台覆盖了操作系统、算法、数据库、安全、平台工具、虚拟化、存储、网络、固件等多个前沿技术领域,包含了应用使能套件、软件仓库、软件支持、软件适…

Matplotlib 颜色设置详解

在使用matplotlib进行颜色绘制的时候,如绘制图表、背景色或者对文字设置的时候都可以配置颜色, 以下说明主流的三种颜色使用方法 颜色名称 可以是直接使用颜色名称的字符串对color进行赋值,包括可以使用首字母缩写或者完整拼写的形式,以下为部分颜色的书写形式 缩写版 • …

机器学习课程学习周报十一

机器学习课程学习周报十一 文章目录 机器学习课程学习周报十一摘要Abstract一、机器学习部分1.1 再探GAN的数学原理1.1.1 似然与概率1.1.2 GAN和最大似然估计1.1.3 最大后验概率 1.2 WGAN1.3 GAN的性能评估方法1.4 条件型生成 总结 摘要 本周的学习主要围绕生成对抗网络&#…

详细介绍msvcr120.dll文件以及修复msvcr120.dll丢失的几种方法

遇到“msvcr120.dll丢失”错误通常会在尝试运行某些程序时发生,这类错误提示“程序无法启动,因为您的计算机缺少msvcr120.dll文件。”这可能导致许多用户感到困扰和不便。有几个步骤可以帮助你轻松修复“msvcr120.dll丢失”错误,保证应用程序…

Linux新建虚拟机Ubuntu详解

1. 打开软件,点击新建虚拟机; 2. 选择典型; 3. 点击稍后安装操作系统后,点击下一步; 4. 选择客户机操作系统以及版本,这里我们选择Ubuntu 64位; 5. 给虚拟机命名以及新建文件夹存放虚拟机位置&…

bat批处理实现从特定文件夹中提取文件内容并以父文件夹名存储

1、需求分析 标题是bat批处理实现从特定文件夹中提取文件内容并以父文件夹名存储。这里面我们要做的工作是: ①、批处理脚本使用的是bat文件; ②、文件夹下面有很多子文件夹,然后子文件夹下仍然有相同的文件结构,我们需要从三级…

创业者必读!选择拍卖源码还是自建开发,哪种方案更安全?

在当今数字化时代,拍卖平台作为一种独特的电子商务模式,正逐渐成为人们关注的焦点。随着互联网技术的发展,网络安全问题变得越来越突出。如何保障用户数据安全,防止信息泄露及攻击事件的发生,已经成为拍卖软件开发者面…

面试必备:接口自动化测试精选面试干货

一、 请问你是如何做接口测试的? 大体来说,经历以下过程:接口需求调研、接口测试工具选择、接口测试用例编写、接口测试执行、接口测试回归、接口测试自动化持续集成。具体来说,接口测试流程分成以下九步: 第一步&am…

HMI设计:嵌入式设备和电脑的差异化,工控领域首选。

嵌入式设备属于专机专用,电脑是通用,从性能、用途、特殊能力、成本、通信上嵌入式设备完全优于电脑,是工控领域的首选。 嵌入式设备和电脑在很多方面有着显著的差异,主要体现在以下几个方面: 1. 设计用途&#xff1a…

算法练习题17——leetcode54螺旋矩阵

题目描述 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 代码 import java.util.*;class Solution {public List<Integer> spiralOrder(int[][] matrix) {// 用于存储螺旋顺序遍历的结果List<Integer>…

神经网络训练不起来怎么办(零)| General Guidance

摘要&#xff1a;模型性能不理想时&#xff0c;如何判断 Model Bias&#xff0c; Optimization&#xff0c; Overfitting 等问题&#xff0c;并以此着手优化模型。在这个分析过程中&#xff0c;我们可以对Function Set&#xff0c;模型弹性有直观的理解。关键词&#xff1a;模型…

SnapGene 2.3.2软件下载安装教程百度网盘分享链接地址

SnapGene简介&#xff1a;SnapGene 2.3.2软件下载安装教程百度网盘分享链接地址&#xff0c;SnapGene 是一款超厉害的分子生物学软件。它最初由美国公司开发&#xff0c;后来换了东家。 这款软件功能多样&#xff0c;在序列编辑分析方面表现出色&#xff0c;能轻松搞定 DNA 序列…