核心目标

本模块旨在为学习大语言模型(LLM)奠定坚实的工程与理论基础。我们将深入探讨数据处理、数学原理和深度学习核心概念,这些都是理解和应用LLM不可或缺的知识。学完本模块,你将能够:

  • 熟练使用 NumpyPandasMatplotlib 等Python核心库进行高效的数据操作和可视化。
  • 掌握LLM所需的关键数学基础,包括线性代数、微积分和概率论的核心思想。
  • 理解深度学习的基本原理,包括神经网络、反向传播和激活函数的作用。
  • 掌握从数据清洗到文本向量化(Embedding)和 Tokenization 的完整文本预处理流程。

2.1 Python核心库实践

详细文字讲解

在AI领域,Python是事实上的标准语言,其强大的生态系统为我们提供了高效的工具。NumpyPandasMatplotlib 被誉为数据科学的“三驾马车”,是处理和分析数据的基础。

  • 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)转换成一个合法的概率分布。

架构图:深度学习训练流程

模型训练循环
计算
Softmax
与真实标签比较
微积分求导
梯度下降
模型前向传播
模型输出 Logits
概率分布
计算损失 Loss
反向传播计算梯度
更新模型参数
输入数据

深度学习核心概念

  • 神经网络 (Neural Network):由许多相互连接的“神经元”(计算单元)组成的层状结构。每个连接都有一个权重,模型学习的目标就是找到最优的权重组合。
  • 激活函数 (Activation Function):如 ReLUGeLU 等。它们为神经网络引入了非线性,这是模型能够学习复杂模式的关键。如果没有激活函数,无论神经网络有多少层,它本质上都只是一个线性模型,无法捕捉现实世界中的复杂关系。
  • 损失函数 (Loss Function):如交叉熵损失(Cross-Entropy Loss),用于衡量模型预测结果与真实标签之间的差距。训练的目标就是最小化这个差距。
  • 优化器 (Optimizer):如 AdamSGD,是梯度下降算法的具体实现,负责根据计算出的梯度来更新模型的参数。

本节总结

  • 线性代数提供了数据表示和变换的工具(向量、矩阵)。
  • 微积分提供了模型优化的核心方法(梯度下降)。
  • 概率论提供了理解模型输出的框架(概率分布)。
  • 深度学习通过前向传播计算损失反向传播参数更新的循环来完成训练,而激活函数是其学习复杂模式的关键。

2.3 文本数据的处理与表示

详细文字讲解

计算机无法直接理解文本,必须将其转化为数字形式。这个过程包括数据清洗、Tokenization和文本向量化三个主要步骤。

  1. 数据清洗 (Data Cleaning):从原始文本中移除无关或有害的信息,如HTML标签、广告、乱码、重复内容等。这是保证模型训练质量的第一步,遵循“垃圾进,垃圾出”的原则。

  2. Tokenization:将干净的文本分割成更小的单元,称为Token。Token可以是词(Word)、子词(Subword)或字符(Character)。现代LLM普遍采用子词(Subword) Tokenization算法,如 BPE (Byte-Pair Encoding)WordPiece。这样做的好处是:

    • 有效处理未登录词 (Out-of-Vocabulary, OOV):任何新词都可以被拆解成已知的子词组合,例如 “unhappiness” -> “un” + “happi” + “ness”
    • 平衡词典大小和序列长度:相比于词级别,子词的词典更小;相比于字符级别,生成的序列长度更短,计算效率更高。
  3. 文本向量化 (Text Vectorization / Embedding):将每个Token映射到一个高维、稠密的浮点数向量。这个向量被称为词嵌入(Word Embedding)。与传统的One-Hot编码不同,好的词嵌入能在向量空间中捕捉语义关系。例如,“国王”的向量减去“男人”的向量,再加上“女人”的向量,其结果在向量空间中会非常接近“女王”的向量。

代码示例:使用Hugging Face tokenizerstransformers

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的 transformerstokenizers 库为我们提供了方便、强大的工具来执行这些操作。
  1. 分布式表示 (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”的含义截然不同,但它们的向量表示却是完全相同的。
  2. 动态词向量 / 上下文相关的嵌入 (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架构。

Logo

更多推荐