Transformer与大语言模型:从自注意力到ChatGPT

1 阅读1分钟

在前面几节中,我们学习了传统的机器学习方法、强化学习以及各种神经网络架构。今天,我们将深入学习当前最热门的AI技术——Transformer架构和大语言模型,包括自注意力机制、BERT、GPT等,这些技术推动了现代AI的快速发展。

Transformer架构概览

Transformer架构由Vaswani等人在2017年提出,彻底改变了自然语言处理领域。它完全基于注意力机制,摒弃了传统的循环和卷积结构。

graph TD
    A[Transformer] --> B[自注意力]
    A --> C[编码器-解码器]
    A --> D[位置编码]
    B --> E[Query]
    B --> F[Key]
    B --> G[Value]
    C --> H[Encoder]
    C --> I[Decoder]
    H --> J[多头注意力]
    H --> K[前馈网络]
    I --> L[掩码注意力]
    I --> M[编码器-解码器注意力]

自注意力机制详解

自注意力机制是Transformer的核心,它允许模型在处理序列时关注序列中的不同位置。

注意力机制原理

注意力机制的计算公式为: Attention(Q, K, V) = softmax(QK^T / √d_k) V

其中Q是查询矩阵,K是键矩阵,V是值矩阵,d_k是键向量的维度。

import numpy as np
import matplotlib.pyplot as plt

# 简单的自注意力实现
class SimpleSelfAttention:
    """简化版自注意力机制"""
    
    def __init__(self, d_model=64):
        self.d_model = d_model
        # 初始化权重矩阵
        self.W_q = np.random.randn(d_model, d_model) * 0.1
        self.W_k = np.random.randn(d_model, d_model) * 0.1
        self.W_v = np.random.randn(d_model, d_model) * 0.1
    
    def softmax(self, x):
        """Softmax函数"""
        exp_x = np.exp(x - np.max(x, axis=-1, keepdims=True))
        return exp_x / np.sum(exp_x, axis=-1, keepdims=True)
    
    def scaled_dot_product_attention(self, Q, K, V):
        """缩放点积注意力"""
        # 计算注意力分数
        d_k = Q.shape[-1]
        scores = np.dot(Q, K.T) / np.sqrt(d_k)
        
        # 应用softmax
        attention_weights = self.softmax(scores)
        
        # 计算输出
        output = np.dot(attention_weights, V)
        
        return output, attention_weights
    
    def forward(self, X):
        """前向传播"""
        # 线性变换生成Q, K, V
        Q = np.dot(X, self.W_q)
        K = np.dot(X, self.W_k)
        V = np.dot(X, self.W_v)
        
        # 计算注意力
        output, attention_weights = self.scaled_dot_product_attention(Q, K, V)
        
        return output, attention_weights

# 可视化注意力机制
def visualize_attention():
    """可视化注意力机制"""
    # 创建示例序列数据
    seq_length = 5
    d_model = 8
    np.random.seed(42)
    X = np.random.randn(seq_length, d_model)
    
    # 创建注意力机制
    attention = SimpleSelfAttention(d_model=d_model)
    
    # 计算注意力
    output, attention_weights = attention.forward(X)
    
    print("输入序列形状:", X.shape)
    print("注意力权重形状:", attention_weights.shape)
    print("输出序列形状:", output.shape)
    
    # 可视化注意力权重
    plt.figure(figsize=(12, 5))
    
    plt.subplot(1, 2, 1)
    plt.imshow(attention_weights, cmap='viridis', aspect='auto')
    plt.xlabel('Key位置')
    plt.ylabel('Query位置')
    plt.title('注意力权重矩阵')
    plt.colorbar(label='注意力权重')
    
    # 添加数值标签
    for i in range(seq_length):
        for j in range(seq_length):
            plt.text(j, i, f'{attention_weights[i, j]:.2f}', 
                    ha='center', va='center', color='white' if attention_weights[i, j] < 0.5 else 'black')
    
    plt.subplot(1, 2, 2)
    # 可视化输入和输出序列
    plt.plot(range(seq_length), X[:, 0], 'bo-', label='输入序列(第1维)')
    plt.plot(range(seq_length), output[:, 0], 'ro-', label='输出序列(第1维)')
    plt.xlabel('序列位置')
    plt.ylabel('值')
    plt.title('输入与输出序列对比')
    plt.legend()
    plt.grid(True, alpha=0.3)
    
    plt.tight_layout()
    plt.show()
    
    # 分析注意力模式
    print("\n注意力权重分析:")
    for i in range(min(3, seq_length)):
        max_attention_idx = np.argmax(attention_weights[i])
        print(f"位置{i}最关注位置{max_attention_idx} (权重: {attention_weights[i, max_attention_idx]:.3f})")

visualize_attention()

多头注意力机制

多头注意力允许模型在不同表示子空间中并行关注信息,增强了模型的表达能力。

# 多头注意力实现
class MultiHeadAttention:
    """多头注意力机制"""
    
    def __init__(self, d_model=64, num_heads=8):
        self.d_model = d_model
        self.num_heads = num_heads
        self.d_k = d_model // num_heads
        
        # 为每个头初始化权重矩阵
        self.W_q = np.random.randn(num_heads, d_model, self.d_k) * 0.1
        self.W_k = np.random.randn(num_heads, d_model, self.d_k) * 0.1
        self.W_v = np.random.randn(num_heads, d_model, self.d_k) * 0.1
        self.W_o = np.random.randn(num_heads * self.d_k, d_model) * 0.1
    
    def scaled_dot_product_attention(self, Q, K, V):
        """缩放点积注意力"""
        d_k = Q.shape[-1]
        scores = np.dot(Q, K.T) / np.sqrt(d_k)
        
        # 应用softmax
        exp_scores = np.exp(scores - np.max(scores, axis=-1, keepdims=True))
        attention_weights = exp_scores / np.sum(exp_scores, axis=-1, keepdims=True)
        
        # 计算输出
        output = np.dot(attention_weights, V)
        
        return output, attention_weights
    
    def forward(self, X):
        """前向传播"""
        batch_size, seq_length, d_model = X.shape
        heads_output = []
        all_attention_weights = []
        
        # 对每个头计算注意力
        for i in range(self.num_heads):
            Q = np.dot(X, self.W_q[i])
            K = np.dot(X, self.W_k[i])
            V = np.dot(X, self.W_v[i])
            
            head_output, attention_weights = self.scaled_dot_product_attention(Q, K, V)
            heads_output.append(head_output)
            all_attention_weights.append(attention_weights)
        
        # 拼接所有头的输出
        multi_head_output = np.concatenate(heads_output, axis=-1)
        
        # 线性变换
        final_output = np.dot(multi_head_output, self.W_o)
        
        return final_output, all_attention_weights

# 演示多头注意力
def multihead_attention_demo():
    """多头注意力演示"""
    # 创建示例数据
    batch_size = 2
    seq_length = 4
    d_model = 16
    num_heads = 4
    
    np.random.seed(42)
    X = np.random.randn(batch_size, seq_length, d_model)
    
    # 创建多头注意力
    mha = MultiHeadAttention(d_model=d_model, num_heads=num_heads)
    
    # 计算注意力
    output, attention_weights = mha.forward(X)
    
    print("多头注意力演示:")
    print(f"输入形状: {X.shape}")
    print(f"输出形状: {output.shape}")
    print(f"注意力头数: {len(attention_weights)}")
    print(f"每个头的注意力权重形状: {attention_weights[0].shape}")
    
    # 可视化第一个样本的第一个头的注意力权重
    plt.figure(figsize=(10, 8))
    
    for i in range(min(4, num_heads)):
        plt.subplot(2, 2, i+1)
        plt.imshow(attention_weights[i][0], cmap='viridis', aspect='auto')
        plt.xlabel('Key位置')
        plt.ylabel('Query位置')
        plt.title(f'头 {i+1} 注意力权重')
        plt.colorbar(label='权重')
    
    plt.tight_layout()
    plt.show()

multihead_attention_demo()

Transformer编码器结构

Transformer编码器由多层组成,每层包含多头注意力和前馈神经网络。

# 简化的Transformer编码器层
class TransformerEncoderLayer:
    """Transformer编码器层"""
    
    def __init__(self, d_model=64, num_heads=8, d_ff=256, dropout=0.1):
        self.d_model = d_model
        self.num_heads = num_heads
        self.d_ff = d_ff
        self.dropout = dropout
        
        # 多头注意力
        self.multihead_attention = MultiHeadAttention(d_model, num_heads)
        
        # 前馈神经网络
        self.W1 = np.random.randn(d_model, d_ff) * 0.1
        self.b1 = np.zeros(d_ff)
        self.W2 = np.random.randn(d_ff, d_model) * 0.1
        self.b2 = np.zeros(d_model)
        
        # Layer Normalization参数
        self.ln1_gamma = np.ones(d_model)
        self.ln1_beta = np.zeros(d_model)
        self.ln2_gamma = np.ones(d_model)
        self.ln2_beta = np.zeros(d_model)
    
    def layer_norm(self, x, gamma, beta, eps=1e-5):
        """Layer Normalization"""
        mean = np.mean(x, axis=-1, keepdims=True)
        std = np.std(x, axis=-1, keepdims=True)
        return gamma * (x - mean) / (std + eps) + beta
    
    def feed_forward(self, x):
        """前馈神经网络"""
        # 第一层
        hidden = np.dot(x, self.W1) + self.b1
        # ReLU激活
        hidden = np.maximum(0, hidden)
        # 第二层
        output = np.dot(hidden, self.W2) + self.b2
        return output
    
    def forward(self, x):
        """前向传播"""
        # 多头注意力 + 残差连接 + LayerNorm
        attn_output, _ = self.multihead_attention.forward(x)
        x = x + attn_output  # 残差连接
        x = self.layer_norm(x, self.ln1_gamma, self.ln1_beta)  # LayerNorm
        
        # 前馈网络 + 残差连接 + LayerNorm
        ff_output = self.feed_forward(x)
        x = x + ff_output  # 残差连接
        x = self.layer_norm(x, self.ln2_gamma, self.ln2_beta)  # LayerNorm
        
        return x

# 演示Transformer编码器
def transformer_encoder_demo():
    """Transformer编码器演示"""
    # 创建示例数据
    batch_size = 1
    seq_length = 5
    d_model = 16
    
    np.random.seed(42)
    X = np.random.randn(batch_size, seq_length, d_model)
    
    # 创建编码器层
    encoder_layer = TransformerEncoderLayer(d_model=d_model, num_heads=4)
    
    # 前向传播
    output = encoder_layer.forward(X)
    
    print("Transformer编码器演示:")
    print(f"输入形状: {X.shape}")
    print(f"输出形状: {output.shape}")
    
    # 可视化输入和输出
    plt.figure(figsize=(12, 5))
    
    plt.subplot(1, 2, 1)
    plt.imshow(X[0].T, cmap='viridis', aspect='auto')
    plt.xlabel('序列位置')
    plt.ylabel('特征维度')
    plt.title('输入序列')
    plt.colorbar(label='值')
    
    plt.subplot(1, 2, 2)
    plt.imshow(output[0].T, cmap='viridis', aspect='auto')
    plt.xlabel('序列位置')
    plt.ylabel('特征维度')
    plt.title('编码器输出')
    plt.colorbar(label='值')
    
    plt.tight_layout()
    plt.show()

transformer_encoder_demo()

BERT与GPT模型

BERT和GPT是基于Transformer的两种重要大语言模型,分别代表了双向和单向语言建模。

# BERT与GPT对比
def bert_gpt_comparison():
    """BERT与GPT对比"""
    
    comparison = {
        '特征': ['训练方式', '注意力机制', '主要任务', '输入处理', '应用场景'],
        'BERT': ['双向编码器', '双向自注意力', '掩码语言建模', '完整句子输入', '理解任务(NLU)'],
        'GPT': ['单向解码器', '因果注意力', '自回归语言建模', '从左到右生成', '生成任务(NLG)']
    }
    
    fig, ax = plt.subplots(figsize=(12, 6))
    
    # 隐藏坐标轴
    ax.axis('tight')
    ax.axis('off')
    
    # 创建表格
    table_data = []
    for i in range(len(comparison['特征'])):
        table_data.append([
            comparison['特征'][i],
            comparison['BERT'][i],
            comparison['GPT'][i]
        ])
    
    table = ax.table(cellText=table_data,
                     colLabels=['特征', 'BERT', 'GPT'],
                     cellLoc='center',
                     loc='center')
    
    table.auto_set_font_size(False)
    table.set_fontsize(12)
    table.scale(1.2, 1.5)
    
    # 设置表头样式
    for i in range(3):
        table[(0, i)].set_facecolor('#4CAF50')
        table[(0, i)].set_text_props(weight='bold', color='white')
    
    plt.title('BERT与GPT模型对比', fontsize=16, pad=20)
    plt.tight_layout()
    plt.show()

bert_gpt_comparison()

# 大语言模型发展时间线
def llm_timeline():
    """大语言模型发展时间线"""
    models = {
        '2017': ['Transformer'],
        '2018': ['BERT', 'GPT-1'],
        '2019': ['GPT-2', 'XLNet'],
        '2020': ['GPT-3', 'T5'],
        '2021': ['BERT-Large', 'T5-XXL'],
        '2022': ['ChatGPT', 'PaLM'],
        '2023': ['GPT-4', 'Claude', '通义千问']
    }
    
    plt.figure(figsize=(14, 8))
    
    years = list(models.keys())
    year_nums = range(len(years))
    
    plt.hlines(1, 0, len(years)-1, alpha=0.3, linewidth=2)
    plt.scatter(year_nums, [1]*len(year_nums), s=150, color='red', zorder=5)
    
    for i, (year, model_list) in enumerate(models.items()):
        models_text = '\n'.join(model_list)
        plt.annotate(f"{year}\n{models_text}", (i, 1), 
                    xytext=(0, 40 if i % 2 == 0 else -60), 
                    textcoords='offset points',
                    ha='center', va='bottom' if i % 2 == 0 else 'top',
                    bbox=dict(boxstyle='round,pad=0.5', fc='lightblue', alpha=0.8),
                    arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0'),
                    fontsize=9)
    
    plt.xlim(-0.5, len(years)-0.5)
    plt.ylim(0.3, 1.7)
    plt.yticks([])
    plt.xlabel('年份', fontsize=12)
    plt.title('大语言模型发展时间线', fontsize=16, pad=20)
    plt.tight_layout()
    plt.show()

llm_timeline()

位置编码

由于Transformer没有循环或卷积结构,需要位置编码来为模型提供序列顺序信息。

# 正弦位置编码
class PositionalEncoding:
    """正弦位置编码"""
    
    def __init__(self, d_model, max_len=5000):
        self.d_model = d_model
        self.max_len = max_len
        
        # 创建位置编码矩阵
        pe = np.zeros((max_len, d_model))
        position = np.arange(0, max_len, dtype=np.float32).reshape(-1, 1)
        
        # 计算分母项
        div_term = np.exp(np.arange(0, d_model, 2, dtype=np.float32) * 
                         -(np.log(10000.0) / d_model))
        
        # 填充位置编码
        pe[:, 0::2] = np.sin(position * div_term)  # 偶数位置
        pe[:, 1::2] = np.cos(position * div_term)  # 奇数位置
        
        self.pe = pe
    
    def forward(self, x):
        """添加位置编码"""
        seq_len = x.shape[1]
        return x + self.pe[:seq_len, :]

# 可视化位置编码
def visualize_positional_encoding():
    """可视化位置编码"""
    d_model = 128
    max_len = 100
    
    pos_encoding = PositionalEncoding(d_model, max_len)
    pe_matrix = pos_encoding.pe[:50, :]  # 只显示前50个位置
    
    plt.figure(figsize=(14, 6))
    
    plt.subplot(1, 2, 1)
    plt.imshow(pe_matrix, cmap='RdBu', aspect='auto')
    plt.xlabel('特征维度')
    plt.ylabel('序列位置')
    plt.title('位置编码矩阵')
    plt.colorbar(label='编码值')
    
    plt.subplot(1, 2, 2)
    # 绘制几个维度的位置编码
    dimensions = [0, 1, 10, 20, 50]
    for dim in dimensions:
        if dim < d_model:
            plt.plot(pos_encoding.pe[:50, dim], label=f'维度 {dim}')
    
    plt.xlabel('序列位置')
    plt.ylabel('编码值')
    plt.title('不同维度的位置编码')
    plt.legend()
    plt.grid(True, alpha=0.3)
    
    plt.tight_layout()
    plt.show()
    
    print("位置编码特点:")
    print("1. 使用正弦和余弦函数生成,确保不同位置有不同的编码")
    print("2. 具有周期性,能够处理比训练时更长的序列")
    print("3. 为模型提供序列顺序信息")

visualize_positional_encoding()

现代大语言模型应用

# 大语言模型应用场景
def llm_applications():
    """大语言模型应用场景"""
    
    applications = {
        '文本生成': ['文章写作', '故事创作', '诗歌生成'],
        '问答系统': ['智能客服', '知识问答', '教育辅导'],
        '语言翻译': ['多语言翻译', '实时翻译', '文档翻译'],
        '代码生成': ['代码补全', '代码解释', '调试辅助'],
        '创意辅助': ['头脑风暴', '方案设计', '内容策划']
    }
    
    fig, axes = plt.subplots(2, 3, figsize=(15, 10))
    axes = axes.flatten()
    
    colors = plt.cm.Set3(np.linspace(0, 1, len(applications)))
    
    for i, (category, examples) in enumerate(applications.items()):
        if i < len(axes):
            ax = axes[i]
            y_pos = np.arange(len(examples))
            ax.barh(y_pos, [1]*len(examples), color=colors[i])
            ax.set_yticks(y_pos)
            ax.set_yticklabels(examples)
            ax.set_title(category, fontsize=12, fontweight='bold')
            ax.set_xlabel('应用程度')
            ax.set_xlim(0, 1.2)
            ax.grid(True, alpha=0.3)
    
    # 隐藏多余的子图
    for i in range(len(applications), len(axes)):
        axes[i].set_visible(False)
    
    plt.suptitle('大语言模型应用场景', fontsize=16)
    plt.tight_layout()
    plt.show()

llm_applications()

# 模拟ChatGPT对话示例
def chatgpt_demo():
    """模拟ChatGPT对话示例"""
    print("ChatGPT对话示例:")
    print("=" * 50)
    
    conversation = [
        ("用户", "你好,你能帮我写一个Python函数来计算斐波那契数列吗?"),
        ("ChatGPT", "当然可以!以下是一个计算斐波那契数列的Python函数:\n\n```python\ndef fibonacci(n):\n    if n <= 0:\n        return []\n    elif n == 1:\n        return [0]\n    elif n == 2:\n        return [0, 1]\n    \n    fib_seq = [0, 1]\n    for i in range(2, n):\n        fib_seq.append(fib_seq[i-1] + fib_seq[i-2])\n    \n    return fib_seq\n\n# 使用示例\nprint(fibonacci(10))  # 输出: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]\n```"),
        ("用户", "这个函数的时间复杂度是多少?"),
        ("ChatGPT", "这个函数的时间复杂度是 O(n),空间复杂度也是 O(n)。\n\n分析:\n- 时间复杂度:函数中有一个从2到n-1的循环,执行n-2次,所以时间复杂度是O(n)\n- 空间复杂度:我们创建了一个长度为n的列表来存储结果,所以空间复杂度是O(n)\n\n如果只需要计算第n个斐波那契数而不保存整个序列,可以优化到O(1)空间复杂度。")
    ]
    
    for speaker, message in conversation:
        print(f"\n{speaker}:")
        print(message)
        print("-" * 30)

chatgpt_demo()

本周学习总结

今天我们深入学习了Transformer架构和大语言模型的核心技术:

  1. 自注意力机制

    • 理解了注意力机制的数学原理
    • 实现了简单的自注意力和多头注意力
  2. Transformer架构

    • 学习了编码器-解码器结构
    • 掌握了位置编码的重要性
  3. 大语言模型

    • 对比了BERT和GPT的特点
    • 了解了现代大语言模型的发展历程
  4. 实际应用

    • 探讨了大语言模型的各种应用场景
    • 模拟了ChatGPT的对话过程
graph TD
    A[Transformer与大语言模型] --> B[自注意力机制]
    A --> C[Transformer架构]
    A --> D[大语言模型]
    B --> E[Query-Key-Value]
    B --> F[多头注意力]
    C --> G[编码器]
    C --> H[解码器]
    C --> I[位置编码]
    D --> J[BERT]
    D --> K[GPT]
    D --> L[ChatGPT]

课后练习

  1. 运行本节所有代码示例,理解Transformer的工作原理
  2. 修改自注意力机制,尝试不同的缩放因子
  3. 实现一个简单的文本分类器,使用Transformer编码器
  4. 研究RoBERTa、ALBERT等BERT的变体模型

下节预告

下一节我们将学习深度强化学习进阶内容,包括DQN改进、策略梯度方法和PPO算法,这些是现代强化学习的重要技术,敬请期待!


有任何疑问请在讨论区留言,我们会定期回复大家的问题。