第2章 大模型预备知识
大语言模型(LLM)学习需要掌握三大核心技能:Python数据处理、数学基础和文本预处理。首先需熟练使用Numpy、Pandas和Matplotlib三大Python库进行数据操作与可视化。其次要理解线性代数、微积分和概率论等数学原理,以及深度学习的神经网络、反向传播等核心概念。最后需掌握文本数据的完整处理流程,包括清洗、分词(Tokenization)和向量化(Embedding)。现代LLM普
核心目标
本模块旨在为学习大语言模型(LLM)奠定坚实的工程与理论基础。我们将深入探讨数据处理、数学原理和深度学习核心概念,这些都是理解和应用LLM不可或缺的知识。学完本模块,你将能够:
- 熟练使用
Numpy
、Pandas
和Matplotlib
等Python核心库进行高效的数据操作和可视化。 - 掌握LLM所需的关键数学基础,包括线性代数、微积分和概率论的核心思想。
- 理解深度学习的基本原理,包括神经网络、反向传播和激活函数的作用。
- 掌握从数据清洗到文本向量化(Embedding)和
Tokenization
的完整文本预处理流程。
2.1 Python核心库实践
详细文字讲解
在AI领域,Python是事实上的标准语言,其强大的生态系统为我们提供了高效的工具。Numpy
、Pandas
和 Matplotlib
被誉为数据科学的“三驾马车”,是处理和分析数据的基础。
- Numpy (Numerical Python):是Python科学计算的核心库。它提供了高性能的多维数组对象(
ndarray
)和用于处理这些数组的工具。在深度学习中,所有的数据,如文本向量、图片像素,最终都会被表示为Numpy数组(或框架内置的Tensor,其底层与Numpy高度兼容)。 - Pandas:建立在Numpy之上,提供了更高级的数据结构和数据分析工具。其核心是
DataFrame
,一个二维的、大小可变的、可以包含异构类型列的表格型数据结构。它非常适合用来读取、清洗、转换和分析结构化数据,如CSV文件或数据库表。 - Matplotlib:是Python最著名的绘图库,提供了广泛的静态、动态和交互式可视化功能。无论是绘制损失函数曲线、展示数据分布,还是可视化模型注意力,
Matplotlib
都是不可或缺的工具。
代码示例:数据处理与可视化流程
下面的代码演示了一个完整的工作流程:使用 Pandas
读取和处理数据,利用 Numpy
进行数值计算,最后通过 Matplotlib
将结果可视化。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 1. 使用 Pandas 创建和处理数据
# 创建一个模拟数据集:包含学生ID、学习小时数和考试分数
data = {
'student_id': range(1, 11),
'hours_studied': [2.5, 5.1, 3.2, 8.5, 3.5, 1.5, 9.2, 5.5, 7.3, 4.8],
'exam_score': [55, 76, 65, 92, 68, 45, 98, 78, 88, 72]
}
df = pd.DataFrame(data)
print("--- 原始数据 (Pandas DataFrame) ---")
print(df)
# 数据清洗:假设分数低于50的是无效数据,将其移除
df_cleaned = df[df['exam_score'] >= 50]
print("\n--- 清洗后的数据 ---")
print(df_cleaned)
# 2. 使用 Numpy 进行数值计算
# 将Pandas Series转换为Numpy数组以进行高效计算
hours_np = df_cleaned['hours_studied'].to_numpy()
scores_np = df_cleaned['exam_score'].to_numpy()
# 计算学习小时数和分数的平均值和标准差
mean_hours = np.mean(hours_np)
std_hours = np.std(hours_np)
mean_score = np.mean(scores_np)
std_score = np.std(scores_np)
print(f"\n--- Numpy 计算结果 ---")
print(f"平均学习小时: {mean_hours:.2f} (标准差: {std_hours:.2f})")
print(f"平均分数: {mean_score:.2f} (标准差: {std_score:.2f})")
# 计算两者之间的相关系数
correlation_matrix = np.corrcoef(hours_np, scores_np)
correlation = correlation_matrix[0, 1]
print(f"学习小时与分数的相关系数: {correlation:.2f}")
# 3. 使用 Matplotlib 进行可视化
plt.figure(figsize=(10, 5))
# 创建一个散点图来观察学习小时和分数的关系
plt.scatter(hours_np, scores_np, color='blue', label='学生数据')
# 添加标题和标签
plt.title('学习小时 vs. 考试分数')
plt.xlabel('学习小时数')
plt.ylabel('考试分数')
plt.grid(True)
plt.legend()
# 显示图表
plt.show()
本节总结
Numpy
是数值计算的基础,提供高效的ndarray
对象。Pandas
是数据处理和分析的利器,核心是DataFrame
。Matplotlib
是数据可视化的标准工具,能将数据和分析结果直观地呈现出来。- 熟练掌握这“三驾马车”是进行任何AI和数据科学项目的前提。
2.2 数学与深度学习基础
详细文字讲解
大语言模型本质上是复杂的数学函数,其背后由线性代数、微积分和概率论等数学分支驱动。同时,它也是深度学习发展到极致的产物。
- 线性代数 (Linear Algebra):是描述和操作向量与空间的语言。在LLM中,词嵌入(Word Embeddings)、注意力权重(Attention Weights)、神经网络的层(Layers)等所有核心组件都是以向量、矩阵和张量(高维矩阵)的形式存在和计算的。矩阵乘法是神经网络中最频繁的运算。
- 微积分 (Calculus):核心是梯度(Gradient) 的概念。模型训练的过程就是通过反向传播(Backpropagation) 算法计算损失函数相对于模型参数(权重)的梯度,然后利用梯度下降(Gradient Descent) 算法沿着梯度的反方向小步更新参数,从而使损失函数最小化。这个过程就像一个盲人下山,每一步都朝着最陡峭的下坡方向走。
- 概率论 (Probability Theory):为处理不确定性提供了框架。LLM的输出本质上是一个概率分布,它预测的是下一个词(Token)出现的可能性。Softmax函数就是将模型的原始输出(logits)转换成一个合法的概率分布。
架构图:深度学习训练流程
深度学习核心概念
- 神经网络 (Neural Network):由许多相互连接的“神经元”(计算单元)组成的层状结构。每个连接都有一个权重,模型学习的目标就是找到最优的权重组合。
- 激活函数 (Activation Function):如
ReLU
、GeLU
等。它们为神经网络引入了非线性,这是模型能够学习复杂模式的关键。如果没有激活函数,无论神经网络有多少层,它本质上都只是一个线性模型,无法捕捉现实世界中的复杂关系。 - 损失函数 (Loss Function):如交叉熵损失(Cross-Entropy Loss),用于衡量模型预测结果与真实标签之间的差距。训练的目标就是最小化这个差距。
- 优化器 (Optimizer):如
Adam
、SGD
,是梯度下降算法的具体实现,负责根据计算出的梯度来更新模型的参数。
本节总结
- 线性代数提供了数据表示和变换的工具(向量、矩阵)。
- 微积分提供了模型优化的核心方法(梯度下降)。
- 概率论提供了理解模型输出的框架(概率分布)。
- 深度学习通过前向传播、计算损失、反向传播和参数更新的循环来完成训练,而激活函数是其学习复杂模式的关键。
2.3 文本数据的处理与表示
详细文字讲解
计算机无法直接理解文本,必须将其转化为数字形式。这个过程包括数据清洗、Tokenization和文本向量化三个主要步骤。
-
数据清洗 (Data Cleaning):从原始文本中移除无关或有害的信息,如HTML标签、广告、乱码、重复内容等。这是保证模型训练质量的第一步,遵循“垃圾进,垃圾出”的原则。
-
Tokenization:将干净的文本分割成更小的单元,称为Token。Token可以是词(Word)、子词(Subword)或字符(Character)。现代LLM普遍采用子词(Subword) Tokenization算法,如
BPE (Byte-Pair Encoding)
或WordPiece
。这样做的好处是:- 有效处理未登录词 (Out-of-Vocabulary, OOV):任何新词都可以被拆解成已知的子词组合,例如
“unhappiness”
->“un”
+“happi”
+“ness”
。 - 平衡词典大小和序列长度:相比于词级别,子词的词典更小;相比于字符级别,生成的序列长度更短,计算效率更高。
- 有效处理未登录词 (Out-of-Vocabulary, OOV):任何新词都可以被拆解成已知的子词组合,例如
-
文本向量化 (Text Vectorization / Embedding):将每个Token映射到一个高维、稠密的浮点数向量。这个向量被称为词嵌入(Word Embedding)。与传统的One-Hot编码不同,好的词嵌入能在向量空间中捕捉语义关系。例如,“国王”的向量减去“男人”的向量,再加上“女人”的向量,其结果在向量空间中会非常接近“女王”的向量。
代码示例:使用Hugging Face tokenizers
和 transformers
from transformers import AutoTokenizer
import torch
# 加载一个预训练好的分词器,以BERT为例
# 这会自动下载并缓存模型和配置文件
model_name = 'bert-base-uncased'
tokenizer = AutoTokenizer.from_pretrained(model_name)
text = "Tokenization is a core concept in NLP."
# 1. Tokenization
# 将文本转换为Token ID
token_ids = tokenizer.encode(text)
# 也可以直接调用分词器,获取更详细的输出
encoding = tokenizer(text)
print(f"--- Tokenization 示例 ---")
print(f"原始文本: {text}")
print(f"Token 列表: {tokenizer.tokenize(text)}")
print(f"Token ID 列表: {encoding['input_ids']}")
print(f"Attention Mask: {encoding['attention_mask']}") # 用于标识哪些是真实Token,哪些是Padding
# 将ID转换回Token
reconstructed_tokens = tokenizer.convert_ids_to_tokens(encoding['input_ids'])
print(f"从ID重构的Token: {reconstructed_tokens}")
# 2. Embedding (需要加载模型本身)
from transformers import AutoModel
# 加载预训练的BERT模型
model = AutoModel.from_pretrained(model_name)
# 将Token ID转换为PyTorch张量
# 需要在外面再加一个维度,因为模型期望的是一个批次(batch)的数据
input_ids_tensor = torch.tensor([encoding['input_ids']])
# 模型推理,关闭梯度计算以节省内存和计算
with torch.no_grad():
outputs = model(input_ids=input_ids_tensor)
# 获取最后一层的隐藏状态,这就是每个Token的Embedding
last_hidden_states = outputs.last_hidden_state
print(f"\n--- Embedding 示例 ---")
# 输出的维度是 (batch_size, sequence_length, hidden_size)
print(f"输出Embedding的形状: {last_hidden_states.shape}")
# BERT-base的隐藏层大小是768
# 第一个Token [CLS] 的Embedding
cls_embedding = last_hidden_states[0, 0, :]
print(f"[CLS] Token的Embedding (前10个值): {cls_embedding[:10]}")
本节总结
- 文本预处理是LLM数据流水线的核心,包括清洗、Tokenization、向量化。
- Tokenization 将文本分割成模型能理解的单元,现代LLM多采用子词方案来平衡词典大小和序列长度。
- Embedding 将每个Token映射到一个稠密的语义向量,这是模型能够理解和推理语言的基础。
- Hugging Face的
transformers
和tokenizers
库为我们提供了方便、强大的工具来执行这些操作。
-
分布式表示 (Distributed Representation) - 静态词向量的黎明
- 核心思想:一个词的意义由其上下文决定(语言学家J.R. Firth的名言:“You shall know a word by the company it keeps”)。我们不再孤立地看一个词,而是通过它周围的词来学习它的表示。
- Word2Vec (Google, 2013):这是一个里程碑式的工作。它通过训练一个简单的浅层神经网络,来完成一个代理任务:根据上下文词预测中心词(CBOW模型),或者根据中心词预测上下文词(Skip-gram模型)。训练完成后,神经网络的隐藏层权重就成为了每个词的低维(如300维)、稠密的向量表示(即词嵌入)。这些向量神奇地捕捉到了语义关系,例如著名的
vector('King') - vector('Man') + vector('Woman')
在向量空间中非常接近vector('Queen')
。 - GloVe (Stanford, 2014):它认为Word2Vec只利用了局部上下文信息,而GloVe则试图结合全局统计信息。它通过对一个巨大的、全局的词-词共现矩阵进行分解,来直接优化学习词向量。
- 共同局限性:静态性。在这些模型中,一个词(如“bank”)无论出现在什么语境下,其向量表示都是唯一的、固定不变的。这显然无法解决“一词多义”的问题。例如,在“river bank”(河岸)和“investment bank”(投资银行)中,“bank”的含义截然不同,但它们的向量表示却是完全相同的。
-
动态词向量 / 上下文相关的嵌入 (Contextualized Embeddings) - LLM的前夜
- 核心思想:一个词的表示应该根据其所在的具体句子动态地生成。
- ELMo (2018):首次成功地实现了这个思想。它使用一个深度的、双向的LSTM(长短期记忆网络)来处理整个句子。一个词的最终嵌入不再是一个固定的向量,而是LSTM网络在不同层的内部状态的加权组合。因此,同一个词在不同句子中的嵌入是不同的。
- BERT (Google, 2018):革命性的里程碑,它用更强大的Transformer Encoder取代了LSTM。BERT通过两个巧妙的预训练任务(“掩码语言模型”和“下一句预测”)来学习深度的、双向的上下文信息。要获取一个词的嵌入,必须将整个句子输入到BERT模型中,模型会同时考虑该词左右两边的所有上下文,然后输出该词在当前语境下的动态向量。这极大地提升了模型对语言细微差别的理解能力。
代码示例:获取静态与动态词向量
import torch
from transformers import BertTokenizer, BertModel
from gensim.models import KeyedVectors
from gensim.test.utils import datapath
import numpy as np
# --- 动态词向量 (BERT) ---
def get_bert_embeddings(text):
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')
# 准备输入
inputs = tokenizer(text, return_tensors='pt', padding=True, truncation=True)
# 获取模型输出
with torch.no_grad():
outputs = model(**inputs)
# outputs.last_hidden_state 的形状是 (batch_size, sequence_length, hidden_size)
return outputs.last_hidden_state
# 示例
sentence1 = "The bank will not approve the loan."
sentence2 = "The boat was moored by the bank of the river."
embeddings1 = get_bert_embeddings(sentence1)
embeddings2 = get_bert_embeddings(sentence2)
# 获取 'bank' 在两个句子中的向量
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
tokens1 = tokenizer.tokenize(sentence1)
tokens2 = tokenizer.tokenize(sentence2)
bank_index1 = tokens1.index('bank') + 1 # +1 for [CLS] token
bank_index2 = tokens2.index('bank') + 1
vec_bank1 = embeddings1[0, bank_index1, :]
vec_bank2 = embeddings2[0, bank_index2, :]
# 比较两个向量的相似度 (余弦相似度)
cos_sim = torch.nn.functional.cosine_similarity(vec_bank1.unsqueeze(0), vec_bank2.unsqueeze(0))
print("--- BERT 动态词向量 ---")
print(f"'bank' in sentence 1 (financial):", vec_bank1[:5])
print(f"'bank' in sentence 2 (geography):", vec_bank2[:5])
print(f"Cosine similarity between the two 'bank' vectors: {cos_sim.item():.4f}")
# 预期结果:相似度不会很高,因为上下文不同
# --- 静态词向量 (Word2Vec) ---
# 注意:需要先下载预训练的Word2Vec模型文件,例如 'GoogleNews-vectors-negative300.bin'
# 这里我们用一个gensim的测试模型代替,实际效果不如完整模型
# try:
# wv = KeyedVectors.load_word2vec_format(datapath('word2vec_pre_kv_c'), binary=False)
# vec_bank_static = wv['bank']
# print("\n--- Word2Vec 静态词向量 ---")
# print(f"Static vector for 'bank':", vec_bank_static[:5])
# except Exception as e:
# print("\nCould not load Word2Vec model. Skipping static vector example.")
本节总结
- 文本向量化是将文字转换为机器可处理的数字的关键步骤。
- 其发展历程反映了对语义理解的深化:从无语义的One-Hot,到捕捉词间关系的静态词向量 (Word2Vec),再到理解上下文、解决一词多义的动态词向量 (BERT)。
- 动态上下文嵌入是现代LLM的基石,它使得模型能够根据具体语境来理解词语的精确含义。
- 理解向量化技术的演进,有助于我们理解LLM为何如此强大。
模块总结
在本模块中,我们为进入大模型的世界铺平了道路。
- 我们学习了数据标注的重要性、方法和质量控制策略,理解了高质量数据是模型成功的基石。
- 我们掌握了数据清洗与预处理的常用技术,能够将原始的、嘈杂的文本转化为干净、规范的训练数据。
- 我们回顾了文本向量化的发展,从简单的One-Hot,到捕捉语义关系的Word2Vec,再到能够理解上下文的BERT嵌入,为理解LLM的输入层打下了基础。
现在,我们已经准备好深入探索大语言模型的核心引擎——Transformer架构。
更多推荐
所有评论(0)