AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


大语言模型的直觉理解:LLM 如何工作,无需数学即可解释
发布日期:2024-05-10 21:10:25 浏览次数: 2549 来源:大模型奇点说





在这个智能密集的时代,大型语言模型(LLMs)和生成式人工智能(GenAI)已经成为了公众讨论的热点话题。您可能已经在日常生活中体验过ChatGPT的魅力,甚至已经习惯于将其作为日常工作的智能伙伴。然而,在这些先进技术的背后,许多人心中都存在着一个共同的疑问:这些模型所展现出的智能,究竟源自何处?

在本文中,我将致力于揭开这层神秘的面纱,用浅显易懂的语言,不涉及繁琐的数学公式,来阐述生成式文本模型的工作原理。我的目标是帮助读者理解这些模型并非不可捉摸的魔法,而是基于科学原理构建的计算机算法。通过这篇文章,让我们一起探索GenAI的内在逻辑,以及它如何塑造我们与技术的互动方式。






大型语言模型(LLM)的作用是什么?



我将从澄清人们对大型语言模型工作方式的误解开始。大多数人认为这些模型可以回答问题或与您聊天,但实际上,它们所能做的只是接收您提供的文本作为输入,并猜测下一个词(或更准确地说,下一个token)将是什么。

让我们从token开始解开LLM的神秘面纱。



Tokens(词元)



token是大型语言模型理解文本的基本单位。将token视为单词是很方便的,但对于大型语言模型来说,其目标是尽可能高效地编码文本,因此在许多情况下,token代表的是比整个单词短或长的字符序列。标点符号和空格也作为token表示,可能是单独的,也可能是与其他字符组合在一起。

大型语言模型使用的完整token列表被称为LLM的词汇表,因为它可以用来表达任何可能的文本。字节对编码(Byte Pair Encoding,BPE)算法通常被LLM用于根据输入数据集生成token词汇表。为了给您一个大致的规模概念,开源的GPT-2语言模型,它可以被详细研究,使用了一个包含50,257个token的词汇表。

LLM的词汇表中的每个token都被赋予一个唯一的标识符,通常是一个数字。LLM使用一个标记器(tokenizer)来转换常规文本(作为字符串给出)和等价的token序列(作为token数字列表给出)。如果您熟悉Python并想要操作token,您可以安装从OpenAI安装:tiktoken

$ pip install tiktoken

然后在 Python 提示符中尝试以下操作:

>>> import tiktoken>>> encoding = tiktoken.encoding_for_model("gpt-2")
>>> encoding.encode("The quick brown fox jumps over the lazy dog.")[464, 2068, 7586, 21831, 18045, 625, 262, 16931, 3290, 13]
>>> encoding.decode([464, 2068, 7586, 21831, 18045, 625, 262, 16931, 3290, 13])'The quick brown fox jumps over the lazy dog.'
>>> encoding.decode([464])'The'>>> encoding.decode([2068])' quick'>>> encoding.decode([13])'.'

在这个实验中,您可以看到对于GPT-2语言模型,token464代表单词"The",token2068代表单词" quick",包括前面的空格。这个模型使用token13代表句号。

由于token是通过算法确定的,您可能会发现一些奇怪的事情,比如单词"the"的这三个变体,都被GPT-2编码为不同的token

>>> encoding.encode('The')[464]>>> encoding.encode('the')[1169]>>> encoding.encode(' the')[262]

BPE算法并不总是将整个单词映射到token。实际上,不经常使用的单词不会成为它们自己的token,而必须用多个token编码。这里有一个模型用两个token编码的单词的例子:

>>> encoding.encode("Payment")[19197, 434]
>>> encoding.decode([19197])'Pay'>>> encoding.decode([434])'ment'



下一个token的预测



如我上面所述,给定一些文本,语言模型会预测接下来会出现哪个token。如果通过Python伪代码来帮助理解这一点,以下是如何运行这些模型以获取下一个token的预测的方法:

predictions = get_token_predictions(['The', ' quick', ' brown', ' fox'])

该函数获取一个输入token列表,这些token是从用户提供的提示编码而来的。在这个例子中,我假设单词都是单独的token。为了简化,我使用每个token的文本表示,但正如您之前所看到的,实际上每个token将以数字的形式传递给模型。

这个函数的返回值是一个数据结构,它为词汇表中的每个token分配一个跟随输入文本的概率。如果这是基于GPT-2,函数的返回值将是一个包含50,257个浮点数的列表,每个数预测相应token接下来出现的概率。

在上面的例子中,您可以想象,一个训练有素的语言模型会为token"jumps"分配一个高的概率,以跟随我作为提示使用的部分短语"The quick brown fox"。再次假设模型训练得当,您也可以想象,一个随机单词如"potato"继续这个短语的概率将会低得多,接近于0。

为了能够产生合理的预测,语言模型必须经历一个训练过程。在训练过程中,它被呈现大量文本以供学习。训练结束时,模型能够使用它通过所有训练中看到的文本构建的数据结构来计算给定token序列的下一个token概率。

这是否与您的期望不同?我希望现在这开始看起来不那么神奇了。



生成长文本序列



由于模型只能预测下一个标记(token)会是什么,因此要使它生成完整的句子,唯一的方法是将模型在循环中多次运行。每次循环迭代都会生成一个新的token,这个token是根据返回的概率来选择的。然后,这个token会被添加到下一次循环中提供给模型的输入中,这个过程会一直持续到生成足够的文本为止。

让我们来看一个更完整的Python伪代码示例,展示这个过程是如何工作的:

def generate_text(prompt, num_tokens, hyperparameters):    tokens = tokenize(prompt)    for i in range(num_tokens):        predictions = get_token_predictions(tokens)        next_token = select_next_token(predictions, hyperparameters)        tokens.append(next_token)    return ''.join(tokens)

函数接受一个用户提示作为参数。这可以是,例如,一个问题:generate_text()

辅助函数将提示转换为等价的token列表,使用tokenize()或类似的库。在for循环内部,调用AI模型以获取下一个token的概率,如前例所示get_token_predictions()

函数的任务是接收下一个token的概率(或预测),并选择最佳token以继续输入序列。函数可以简单地选择概率最高的token,这在机器学习中被称为贪婪选择。更好的方法是,可以使用随机数生成器选择token,同时考虑模型返回的概率,这样就能在生成的文本中增加一些变化。这也将使得模型在多次给出相同提示时产生不同的响应select_next_token()

为了使token选择过程更加灵活,LLM返回的概率可以使用超参数进行修改,这些超参数作为参数传递给文本生成函数。超参数允许你控制token选择过程的“贪婪度”。如果你使用过LLM,你可能熟悉超参数temperature(生成多样性系数)temperature值越高,token概率就越平坦,这增加了选择不太可能token的机会,最终结果是使生成的文本看起来更有创造性或不寻常。你可能还使用过另外两个超参数top_ptop_k,它们控制考虑选择的最有概率token的数量。

一旦选择了一个token,循环就会迭代,现在模型接收的输入包括了末尾的新token,并生成一个跟随它的另一个token。参数num_tokens控制循环运行的次数,或者说,生成多少文本。生成的文本可以(并且经常是)在句子中间结束,因为LLM没有句子或段落的概念,因为它一次只处理一个token。为了防止生成的文本在句子中间结束,我们可以考虑将num_tokens参数视为最大值,而不是要生成的token的确切数量,在这种情况下,当生成一个句号token时,我们可以停止循环。

如果你已经理解了这一点,那么恭喜你,你现在知道LLM在高层次上是如何工作的了。你对更多细节感兴趣吗?在下一节中,我将更深入地介绍一些技术细节,同时仍然尽力避免引用支持这项技术的数学知识,这些知识相当高级。



模型训练



不幸的是,如果不使用数学,讨论模型是如何训练的实际上是很困难的。我将做的是首先向你展示一种非常简单的训练方法。

鉴于任务是预测跟随其他token的token,训练模型的一个简单方法是获取训练数据集中所有连续token对,并使用它们构建一个概率表。

让我们用一个简短的词汇表和数据集来完成这个任务。假设模型的词汇表包含以下五个token

['I', 'you', 'like', 'apples', 'bananas']

为了保持这个例子简短简单,我不会将空格或标点符号视为token。

让我们使用一个由三个句子组成的训练数据集:

  • I like apples

  • I like bananas

  • you like bananas

我们可以构建一个5x5的表格,在每个单元格中写上代表单元格行的token被代表单元格列的token跟随的次数。这是根据数据集中的三个句子构建的表格:

- I you like apples bananas
I

2

you

1

like


1 2
apples




bananas




希望这很清晰。数据集中有两例“I like”,一例“you like”,一例“like apples”和两例“like bananas”。

现在我们知道了每对token在训练数据集中出现了多少次,我们可以计算每个token跟随另一个token的概率。为此,我们将每行中的数字转换为概率。例如,表格中间行的token“like”被“apples”跟随了一次,被“bananas”跟随了两次。这意味着“apples”跟随“like”的概率是33.3%,而“bananas”跟随它的概率是剩余的66.7%。

这是计算了所有概率的完整表格。空白单元格的概率为0%。

- I you like apples bananas
I

100%

you

100%

like


33.3% 66.7%
apples 25% 25% 25%
25%
bananas 25% 25% 25% 25%

"I"、"you" 和 "like" 的行很容易计算,但是 "apples" 和 "bananas" 出现了问题,因为它们根本没有数据,因为数据集中没有这些token后面跟随其他token的例子。这里我们的训练存在一个“漏洞”,为了确保模型即使在缺乏训练的情况下也能产生预测,我决定将 "apples" 和 "bananas" 的后续token的概率平均分配到其他四个可能的token上,这显然可能会产生奇怪的结果,但至少当模型遇到这两个token中的任何一个时,它不会陷入僵局。

训练数据中存在的漏洞问题实际上是重要的。在真实的大型语言模型(LLM)中,训练数据集非常庞大,所以你不会发现像我上面那个微小例子中那样明显的训练漏洞。但是,由于训练数据覆盖范围低而产生的更小、更难以检测的漏洞确实存在,而且相当常见。LLM在这些训练不足的区域所做的token预测的质量可能很差,但通常以难以察觉的方式表现出来。这是LLM有时会产生幻觉的原因之一,当生成的文本读起来很通顺,但包含事实错误或不一致性时,就会发生这种情况。

使用上面的概率表,你现在可以想象 get_token_predictions() 函数的实现将如何工作。在Python伪代码中,它可能看起来像这样:

def get_token_predictions(input_tokens):    last_token = input_tokens[-1]    return probabilities_table[last_token]

比预期的简单,对吧?该函数接受一个来自用户提示的token序列。它取序列中的最后一个token,并返回对应于该token的概率表中的行。

如果你用token序列['you', 'like']调用这个函数,例如,该函数将返回"like"的行,这给予token "apples" 33.3%的概率来继续句子,而token "bananas"则是剩下的66.7%。有了这些概率,上面显示的函数应该会三分之一的概率选择"apples"。[‘you’, ‘like’]select_next_token()

当"apples" token被选作"you like"的延续时,就会形成句子"you like apples"。这是一个在训练数据集中不存在的原始句子,但它是完全合理的。希望你开始理解这些模型是如何通过重用模式并将它们在训练中学到的不同片段拼接在一起,来产生看似原创的想法或概念的。



上下文窗口



我在上一节中用来训练我的迷你语言模型的方法被称为马尔可夫链。

这种技术的一个问题是,只有一个token(输入中的最后一个)被用来进行预测。出现在最后一个token之前的任何文本在选择如何继续时都没有影响,因此我们可以说这个解决方案的上下文窗口等于一个token,这非常小。由于上下文窗口如此之小,模型不断地“忘记”其思路,并且从一个词跳到下一个词,没有太多的连贯性。

为了改善模型的预测,可以构建一个更大的概率表。为了使用两个token的上下文窗口,必须添加额外的表行,以表示所有可能的两个token的序列。使用我在示例中使用的五个token,概率表中将增加25个新行,每个token对一行,加上已经存在的5个单token行。模型必须再次训练,这次除了成对的token外,还要观察三个token的组。然后在函数的每次循环迭代中,将从输入中获取最后两个token(如果可用),以在更大的概率表中找到对应的行。get_token_predictions()

但是,两个token的上下文窗口仍然不够。为了使生成的文本与自身一致并且至少有些基本意义,需要一个更大的上下文窗口。如果没有足够大的上下文,新生成的token就无法与先前token中表达的概念或想法相关联。那么我们能做什么呢?将上下文窗口增加到3个token,将在概率表中添加125个额外的行,质量仍然会非常差。我们需要将上下文窗口设置得多大?

OpenAI的开源GPT-2模型使用1024个token的上下文窗口。为了能够使用马尔可夫链实现这种大小的上下文窗口,概率表的每一行都必须代表一个长度在1到1024个token之间的序列。使用上述示例中的5个token的词汇表,有51024种可能的1024个token长的序列。需要多少行来表示这个?我在Python会话中进行了计算(向右滚动以查看完整的数字):

>>> pow(5, 1024)55626846462680034577255817933310101605480399511558295763833185422180110870347954896357078975312775514101683493275895275128810854038836502721400309634442970528269449838300058261990253686064590901798039126173562593355209381270166265416453973718012279499214790991212515897719252957621869994522193843748736289511290126272884996414561770466127838448395124802899527144151299810833802858809753719892490239782222290074816037776586657834841586939662825734294051183140794537141608771803070715941051121170285190347786926570042246331102750604036185540464179153763503857127117918822547579033069472418242684328083352174724579376695971173152319349449321466491373527284227385153411689217559966957882267024615430273115634918212890625

这确实是很多行!而这只是表格的一部分,因为我们还需要能够处理1023个token长、1022个token长,等等,一直到1个token长的序列,因为我们希望确保在输入中没有足够的token时,也可以处理较短的序列。马尔可夫链很有趣,但它们确实有一个大的可扩展性问题。

而且,1024个token的上下文窗口甚至已经不再那么出色了。在GPT-3中,上下文窗口增加到了2048个token,然后在GPT-3.5中增加到了4096个token记。GPT-4开始时有8192个token,后来增加到32K,然后又增加到128K(没错,是128,000个标记!)。现在开始出现具有1M或更大上下文窗口的模型,这些模型在进行token预测时能够具有更好的连贯性和回忆能力。

总之,马尔可夫链让我们以正确的方式思考文本生成问题,但它们存在一些大问题,这些问题阻止我们将它们视为一种可行的解决方案。



从马尔可夫链到神经网络



显然,我们必须放弃拥有概率表的念头,因为对于一个合理的上下文窗口来说,这样的表将需要无法想象的大量RAM。我们可以做的是用一个函数来替换这个表,该函数返回对token概率的近似值,这些值是通过算法生成的,而不是存储在一个大表中。这正是神经网络可以做得很好的事情。

神经网络是一种特殊类型的函数,它接受一些输入,对它们进行一些计算,并返回一个输出。对于语言模型来说,输入是代表提示的token,输出是下一个token的预测概率列表。

我说神经网络是“特殊”的函数。使它们特殊的是,除了函数逻辑之外,它们对输入执行的计算由一些外部定义的参数控制。最初,网络的参数是未知的,因此,该函数产生一个完全无用的输出。神经网络的训练过程包括找到使函数在训练数据集上评估时表现最佳的参数,假设如果函数在训练数据上表现良好,它在其他数据上也会同样表现良好。

在训练过程中,使用一种称为反向传播的算法以小增量迭代调整参数,该算法数学量很大,所以我不会在本文中讨论。每次调整后,预期神经网络的预测会变得更好一点。参数更新后,网络再次针对训练数据集进行评估,结果将指导下一轮的调整。这个过程一直持续,直到函数在训练数据集上进行良好的下一个token预测。

为了帮助你了解神经网络工作的规模,考虑到GPT-2模型大约有15亿个参数,而GPT-3将参数数量增加到1750亿。GPT-4据说大约有1.76万亿个参数。使用当前一代硬件在这种规模上训练神经网络需要很长时间,通常是几周或几个月。

有趣的是,由于有如此多的参数,所有参数都是通过一个没有人类协助的漫长迭代过程计算出来的,因此很难理解模型是如何工作的。一个训练有素的大型语言模型就像一个极其难以调试的黑匣子,因为模型的大部分“思考”都隐藏在参数中。即使是训练它的人也难以解释其内部工作机制。



层、Transformer和注意力



您可能很好奇,在神经网络函数内部发生的神秘计算是什么,借助经过良好调整的参数,它可以接受一列表输入token,并以某种方式输出下一个token的合理概率。

神经网络被配置为执行一系列操作,每个操作称为一个层。第一层接收输入,并对其进行某种类型的转换。转换后的输入进入下一层,并再次进行转换。这个过程一直持续,直到数据到达最后一层并进行最后一次转换,生成输出或预测。

机器学习专家提出了不同类型的层,这些层对输入数据执行数学转换,并且他们还找出了组织和组合层的方法,以实现所需的结果。一些层是通用的,而其他层则设计为处理特定类型的输入数据,例如图像,或者像大型语言模型(LLM)一样,处理标记化文本。

今天在大型语言模型中用于文本生成的最流行的神经网络架构被称为Transformer。使用这种设计的LLM被称为GPT,或生成预训练Transformers

Transformer模型的独特特征是它们执行的一种称为注意力(Attention)的层计算,这使它们能够派生上下文窗口中token之间的关系和模式,这些关系和模式随后反映在下一个token的结果概率中。

注意力机制最初用于语言翻译器,作为一种找出输入序列中哪些token对于提取其含义最为重要。这种机制赋予现代翻译器在基本层面上“理解”句子的能力,通过关注(或引导“注意力”到)重要的单词或token



大型语言模型(LLM)是否具有智能?



到现在为止,您可能已经开始对 LLMs 在生成文本的方式中表现出某种形式的智能形成了自己的看法。

依我个人之见,LLM仅仅在一定程度上具备推理及激发原创思维的“联想”能力,但这并不意味着它们无用。由于它们对上下文窗口中的token进行了巧妙的计算,LLM能够捕捉到用户提示中存在的模式,并将它们与在训练期间学到的类似模式相匹配。它们生成的文本大部分是由训练数据的片段组成的,但它们将单词(实际上是token)拼接在一起的方式非常复杂,很多时候产生出感觉原创且有用的结果。

关于“原创思想”,这是一个更为复杂的话题。LLM并不具备人类意识或独立的创造性思维。它们产生的“思想”实际上是基于其训练数据的统计模式和组合。这意味着模型可以生成新颖的句段或未直接出现在训练数据中的观点,但这些仍然是基于对已有数据的学习和重新组合,而非传统意义上的原创思考。因此,尽管LLM在某些场景下可能表现出类似推理或创造性的行为,这些能力从根本上还是源自其对大量文本数据的分析和模式识别。

另一方面,鉴于LLM会产生幻觉,需要警惕任何LLM直接产生输出而未经人类验证就直接发送给最终用户的流程。

在未来几个月或几年出现的更大的LLM是否会实现任何类似于真正智能的东西?我觉得由于其许多限制,GPT架构不会实现这一点,但谁知道呢,也许随着技术突破与创新,我们也许会到达那里。



END



53AI,企业落地应用大模型首选服务商

产品:大模型应用平台+智能体定制开发+落地咨询服务

承诺:先做场景POC验证,看到效果再签署服务协议。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

与创始人交个朋友

回到顶部

 
扫码咨询