震惊!更换GPU会改变LLM的行为

2024-09-01 03:36
文章标签 行为 gpu llm 更换 改变 震惊

本文主要是介绍震惊!更换GPU会改变LLM的行为,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 新发现
    • 前言
    • 1. Why this article ?
    • 2. Setup the experimentation
    • 3. The experiment results:A100/A10/3090
    • 4. Why is it different?
    • 5. Why do the calculation differ depending on the GPU ?
    • 结论

新发现

  最近在做RAG相关的工作,偶然间发现,生产环境(A100)与测试环境(A10)大模型推理的结果不太一致,后来做了进一步实验发现,相同的数据,相同的推理代码,相同的模型以及相同的软件版本环境,不同的GPU下,推理结果不完全一致。具体如下:
  A100

在这里插入图片描述

  A10

在这里插入图片描述

  3090

在这里插入图片描述

  然后在Medium上发现了一篇博客,博主也分享了他的发现,与我的情况一模一样,在此做个记录。

前言

  大多数技术人员都知道,依赖项的不同版本可能会导致行为不同。然而,在大型语言模型领域,由于我们需要大量的计算资源,因此在训练和推理任务中我们严重依赖于GPU。然而,很少有人真正意识到更换GPU也会影响LLM的输出。
  当然,你可以创造两个完全相同的环境,可以设置依赖项版本,可以使用Dockerization,可以将LLM的温度系数设置为0,也可以设定任何你想要的seed。归根结底,除非你没有使用完全相同的GPU模型,否则这些都不会起作用。
  在这篇文章中,我将通过一个实验来强调这一现象,该实验显示了差异发生的位置和原因。

1. Why this article ?

  有一天,作者和一些人讨论为什么OpenAIAnthropic模型在设计上不是确定性的。作者解释说,他们可能会使用混合专家(MoE, Mixture of Experts)方法,偶尔不会将token路由给最优专家,因为这些专家忙于处理其他token,这会导致答案不一致。
  另一个因素可能是OpenAI的批量查询效率。这些批处理的大小可以根据传入查询的数量而变化,这可以改变GPU计算策略,从而导致不同的结果。
  但也有人指出:“不同的GPU也可能导致不同的结果,不是吗?”
  当你使用OpenAI API时,在某个地方有一台远程机器代表你运行计算并返回结果。现在,如果机器并不总是运行在相同的硬件上,那么最终得到的模型响应可能就不会相同。
  考虑到这一点,可能就会出现其他问题:
  (1)如果我在生产环境中有一个LLM应用程序,并且我需要扩展到具有不同GPU的其他生产环境上,这是否会出现很严重的问题?
  (2)如果开发环境中的GPU与生产环境中的GPU不同,会怎么样?
  基于这些问题,作者做了一个实验,看看它的影响有多大。

  Note: 以下是博主的真实实验。

2. Setup the experimentation

  主要环境库版本及其它参数:

# 依赖库
python        3.10.12
cuda          12.1
torch		  2.0.1
transformers  4.40.2
accelerate    0.26.1# GPU型号
NVIDIA A100 40GB
NVIDIA A10  24GB
NVIDIA GeForce RTX 3090 24GB# LLM
InternLM2.5-7B-Chat# pip镜像
-i https://pypi.tuna.tsinghua.edu.cn/simple

  与作者的一致,这里也统一设置随机数:

# Set seeds for reproducibility
random_seed = 42
np_seed = 42
torch_seed = 42
transformers_seed = 42random.seed(random_seed)
np.random.seed(np_seed)
torch.manual_seed(torch_seed)
set_seed(transformers_seed)

3. The experiment results:A100/A10/3090

  模型使用的是书生·浦语InternLM2.5-7B-Chat,测试代码如下:

# -*- coding: utf-8 -*-
# Author  : liyanpeng
# Email   : yanpeng.li@cumt.edu.cn
# Datetime: 2024/8/31 13:08
# Filename: llm_prob_test.py
import os
import random
import numpy as np
import torch
from transformers import set_seed
from transformers import AutoTokenizer, AutoModelForCausalLM
from prettytable import PrettyTable# Set seeds for reproducibility
random_seed = 42
np_seed = 42
torch_seed = 42
transformers_seed = 42random.seed(random_seed)
np.random.seed(np_seed)
torch.manual_seed(torch_seed)
set_seed(transformers_seed)os.environ['CUDA_VISIBLE_DEVICES'] = '0'
device = "cuda"
model_path = '/data/liyanpeng/huggingface/internlm/internlm2-chat-7b'tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_path, device_map="auto", trust_remote_code=True,torch_dtype=torch.float16)
model = model.eval()query = '你好'
prompt = """<|im_start|>user\n{query}<|im_end|>\n<|im_start|>assistant\n"""
prompt = prompt.format(query=query)response, _ = model.chat(tokenizer, query, do_sample=False, temperature=0,history=[], meta_instruction='')
print(response)

  我们看下不同型号GPU下,模型的输出:

# A100
你好!很高兴为你服务。有什么我可以帮助你的吗?# A10
你好!很高兴为你服务。有什么我可以帮助你的吗?# 3090
你好!很高兴为你服务。有什么我可以帮助你的吗?

  嗯哼,看着没啥问题,模型在三个不同型号的输出是一致的。现在将输入字符长度增加一些,再看看模型的输出,具体如下:

query = '2012年Hinton团队提出了AlexNet,它是首个充分利用GPU的并行能力训练出的卷积神经网络,并在ImageNet比赛一举夺魁,获得了85%的准确率,其准确分辨相似图像的能力惊艳了世界。如今,大部分的神经网络训练都离不开了GPU,对此,你有什么看法?(请注意,回复的内容长度不要超过300字,观点总结在一起,不要分开表述)'

  由于输出的文本很长,为了方便对比,直接放在了表格中:

在这里插入图片描述
  可以看到,相同的模型,相同的输入,在不同的硬件上,输出结果是有差异的。

4. Why is it different?

  为什么相同的输入和相同的LLM在不同的GPU上生成的内容是不同的?
  很容易回答,由于LLM的自回归性质,下一个token是基于前一个token选择的,任何微小的变化都会引起级联反应,从而导致蝴蝶效应,最终生成的内容是不同的。
  而token的选择是基于概率来选择的,比如贪婪采样,在每一步,模型会选择概率最高的token作为下一个输出。
  为了让模型输出时可以看到每个token的概率,不在使用官方的chat函数,对代码做了更改,增加了return_dict_in_generate参数和output_scores参数,具体如下:

inputs = tokenizer([prompt], return_tensors="pt").to(device)
eos_token_id = [tokenizer.eos_token_id, tokenizer.convert_tokens_to_ids(["<|im_end|>"])[0]]
outputs = model.generate(**inputs, max_new_tokens=512, do_sample=False, temperature=0,eos_token_id=eos_token_id, return_dict_in_generate=True, output_scores=True)input_length = inputs.input_ids.shape[1]
generated_tokens = outputs.sequences[:, input_length:]
output_ids = generated_tokens[0].cpu().tolist()
response = tokenizer.decode(output_ids, skip_special_tokens=True)
response = response.split("<|im_end|>")[0]
print(response)transition_scores = model.compute_transition_scores(outputs.sequences, outputs.scores, normalize_logits=True)
table = PrettyTable(["token id", "token str", "probability"])
for tok, score in zip(generated_tokens[0], transition_scores[0]):table.add_row([f'{tok:d}', tokenizer.decode(tok), f'{np.exp(score.cpu().numpy()):.2%}'])
print(table)

  以A100A10的对比结果为例,这里截取了部分内容,可以看下每个token的输出概率:

# A100
+----------+-------------+-------------+
| token id |  token str  | probability |
+----------+-------------+-------------+
|   ...    |     ...     |     ...     |
|  70939   |     推动    |    52.30%   |
|  60362   ||    99.34%   |
|  49145   |     GPU     |    75.64%   |
|  71071   |     硬件    |    27.03%   | <-- here
|  60381   ||    35.73%   |
|  68367   |     软件    |    64.54%   |
|  74657   |    技术的   |    69.70%   |
|  68705   |     不断    |    50.39%   |
|  70212   |     进步    |    54.20%   |
|  60355   ||    96.46%   |
|   ...    |     ...     |     ...     |
+----------+-------------+-------------+# A10
+----------+-------------+-------------+
| token id |  token str  | probability |
+----------+-------------+-------------+
|   ...    |     ...     |     ...     |
|  70939   |     推动    |    51.97%   |
|  60362   ||    99.35%   |
|  49145   |     GPU     |    75.36%   |
|  75075   |     架构    |    31.85%   | <-- here
|  60354   ||    44.99%   |
|  68705   |     不断    |    36.70%   |
|  70386   |     优化    |    84.05%   |
|  60353   ||    95.13%   |
|  60367   ||    42.60%   |
|  70188   |     适应    |    70.76%   |
|  70907   |     深度    |    25.57%   |
|  68352   |     学习    |    81.61%   |
|  71370   |    的需求   |    75.34%   |
|  60355   ||    99.96%   |
|   ...    |     ...     |     ...     |
+----------+-------------+-------------+

  可以看到,A100A10输出的概率并不完全相同。通常情况下,这不会影响token的顺序,但在某些情况下有影响。例如,在A100"硬件"的概率为27.03%,而在A10上的"架构"的概率为31.85%,由于大模型自回归的性质,从这个token开始,输出的内容就不太一样了。

5. Why do the calculation differ depending on the GPU ?

  为什么不同GPU的计算会有所不同?
  GPU之间的不同计算可以归因于以下几个因素:
  (1)并行计算处理
  GPU都是关于高效并行处理大量计算的。但是,不同的GPU在管理并行任务时可能会有所不同,从而影响操作顺序和内存访问。这很重要,因为在编程中,数量级相差很大的数字即使是简单相加也可能是非结合的(non-associative),从而导致精确计算中的潜在不准确性。
在这里插入图片描述

  non-associative,通俗的理解为不满足结合律。

  比如下面的这个例子:

import torch# Define three floating-point numbers in bfloat16 with a large difference in magnitude
a = torch.tensor(1e10, dtype=torch.bfloat16)
b = torch.tensor(-1e10, dtype=torch.bfloat16)
c = torch.tensor(1.0, dtype=torch.bfloat16)# Calculate the sums in different orders
sum1 = (a + b) + c
sum2 = a + (b + c)
# Print the results in bfloat16
print(f"(a + b) + c in bfloat16: {sum1}")
# >>> 1.0
print(f"a + (b + c) in bfloat16: {sum2}")
# >>> 0.0

  对于LLM来说,数百万次的计算可能会导致由于小的重复不准确而产生偏差,这会影响序列生成过程中的词汇选择。
  (2)硬件架构
  不同的GPU硬件,如NVIDIA Tesla T4NVIDIA A10,具有不同的硬件架构。这些架构旨在优化性能的各个方面,包括并行处理能力、内存带宽和计算单元。例如,T4使用的是图灵架构,而A10基于安培架构。不同的架构意味着浮点运算、内存访问模式和其他低级操作的不同实现方式。即使这些实现中存在微小的差异,也可能导致计算结果的变化。例如,针对更高精度优化的架构可能与针对速度优化的架构产生不同的结果,即使两者都执行相同的浮点运算。

  A100A103090都是Ampere架构。

  (3)量化影响
  量化后的模型可以减少其精度以节省内存和计算资源,但它也引入了额外的误差来源。这些误差的影响可能会根据GPU处理较低精度算术的方式而有所不同。由于量化涉及对数字的近似处理,不同的GPU可能会以不同的方式处理这些近似值,从而导致token预测概率的变化。

结论

  很有趣的发现,博主本人对CUDA的具体计算原理了解的不多,但让我想起了以前写算子时,对数据进行不同的切片计算效率不一,结果一致,所以对上述的发现更倾向于第一种,即不同硬件上计算时,由于LLM的层数很多,导致在传递时,某些数值不满足了结合律,从而出现了计算的偏差,然后偏差继续向前传播,导致结果差别越来越大。
  对这一现象,我觉得还需要再研究一下。

这篇关于震惊!更换GPU会改变LLM的行为的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1125826

相关文章

AI Toolkit + H100 GPU,一小时内微调最新热门文生图模型 FLUX

上个月,FLUX 席卷了互联网,这并非没有原因。他们声称优于 DALLE 3、Ideogram 和 Stable Diffusion 3 等模型,而这一点已被证明是有依据的。随着越来越多的流行图像生成工具(如 Stable Diffusion Web UI Forge 和 ComyUI)开始支持这些模型,FLUX 在 Stable Diffusion 领域的扩展将会持续下去。 自 FLU

如何用GPU算力卡P100玩黑神话悟空?

精力有限,只记录关键信息,希望未来能够有助于其他人。 文章目录 综述背景评估游戏性能需求显卡需求CPU和内存系统需求主机需求显式需求 实操硬件安装安装操作系统Win11安装驱动修改注册表选择程序使用什么GPU 安装黑神话悟空其他 综述 用P100 + PCIe Gen3.0 + Dell720服务器(32C64G),运行黑神话悟空画质中等流畅运行。 背景 假设有一张P100-

秒变高手:玩转CentOS 7软件更换的方法大全

在 CentOS 7 中更换软件源可以通过以下步骤完成。更换源可以加快软件包的下载速度,特别是当默认源速度较慢时。以下是详细步骤: 前言 为了帮助您解决在使用CentOS 7安装不了软件速度慢的问题,我们推出了这份由浪浪云赞助的教程——“CentOS7如何更换软件源加快下载速度”。 浪浪云,以他们卓越的弹性计算、云存储和网络服务受到广泛好评,他们的支持和帮助使得我们可以将最前沿的技术知识分

[论文笔记]LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale

引言 今天带来第一篇量化论文LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale笔记。 为了简单,下文中以翻译的口吻记录,比如替换"作者"为"我们"。 大语言模型已被广泛采用,但推理时需要大量的GPU内存。我们开发了一种Int8矩阵乘法的过程,用于Transformer中的前馈和注意力投影层,这可以将推理所需

GPU 计算 CMPS224 2021 学习笔记 02

并行类型 (1)任务并行 (2)数据并行 CPU & GPU CPU和GPU拥有相互独立的内存空间,需要在两者之间相互传输数据。 (1)分配GPU内存 (2)将CPU上的数据复制到GPU上 (3)在GPU上对数据进行计算操作 (4)将计算结果从GPU复制到CPU上 (5)释放GPU内存 CUDA内存管理API (1)分配内存 cudaErro

LLM系列 | 38:解读阿里开源语音多模态模型Qwen2-Audio

引言 模型概述 模型架构 训练方法 性能评估 实战演示 总结 引言 金山挂月窥禅径,沙鸟听经恋法门。 小伙伴们好,我是微信公众号《小窗幽记机器学习》的小编:卖铁观音的小男孩,今天这篇小作文主要是介绍阿里巴巴的语音多模态大模型Qwen2-Audio。近日,阿里巴巴Qwen团队发布了最新的大规模音频-语言模型Qwen2-Audio及其技术报告。该模型在音频理解和多模态交互

react笔记 8-18 事件 方法 定义方法 获取/改变数据 传值

1、定义方法并绑定 class News extends React.Component {constructor(props) {super(props)this.state = {msg:'home组件'}}run(){alert("我是一个run") //方法写在类中}render() {return (<div><h2>{this.state.msg}</h2><button onCli

PyInstaller问题解决 onnxruntime-gpu 使用GPU和CUDA加速模型推理

前言 在模型推理时,需要使用GPU加速,相关的CUDA和CUDNN安装好后,通过onnxruntime-gpu实现。 直接运行python程序是正常使用GPU的,如果使用PyInstaller将.py文件打包为.exe,发现只能使用CPU推理了。 本文分析这个问题和提供解决方案,供大家参考。 问题分析——找不到ONNX Runtime GPU 动态库 首先直接运行python程序

『功能项目』更换URP场景【32】

上一章已经将项目从普通管线升级到了URP管线 现在我们打开上一篇31项目优化 - 默认管线转URP的项目, 进入战斗场景 将Land的子级全部隐藏 将新的URP场景预制体拖拽至Land子级 对场景预制体完全解压缩 将Terrain拖拽至Land的直接子级 将Terrain设置为Land 与 静态Static 清除烘培 重新烘培 修改脚本:LoadRe