de7a47db9d
上次修复了 ''...'' 模式,遗漏了 ``...'' 包裹中文的情况。 补充修复 ch02-ch08 及 ap01-ap02 中的混合引号。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1655 lines
61 KiB
TeX
1655 lines
61 KiB
TeX
\chapter{一维数据——序列与文本}
|
||
|
||
\section{篇章导读}
|
||
|
||
一维数据(1D Data)是人工智能处理的最基础、也是最重要的数据形态。你正在阅读的文字、你说出的一句话、股票市场的价格走势、建筑室内温度随时间的变化——这些都是一维序列数据。
|
||
|
||
与图像(二维)和三维模型(三维)不同,一维数据的核心特征是\textbf{有序性}:每一个元素的含义不仅取决于它本身,还取决于它在序列中的位置以及与前后元素的关系。“我吃饭了“和”饭我吃了”虽然包含相同的字,但含义截然不同——这就是序列的魅力与挑战。
|
||
|
||
本章将带你踏上一段激动人心的技术演进之旅:从早期的小型循环神经网络(RNN),到注意力机制的革命性突破,再到 Transformer 架构的横空出世,最终见证大语言模型(LLM)如何改变整个 AI 格局。这条从小模型到大模型的技术脉络,不仅是深度学习最精彩的故事之一,也直接关系到今天每一位设计师如何使用 AI 工具。
|
||
|
||
\textbf{核心线索}:序列数据的模型演进——RNN(小模型)→ 注意力机制 → Transformer → 大语言模型(LLM)
|
||
|
||
|
||
\section{学习目标}
|
||
|
||
\begin{itemize}
|
||
\tightlist
|
||
\item
|
||
理解序列数据的特点与建模难点
|
||
\item
|
||
掌握 RNN、LSTM、GRU 的基本原理
|
||
\item
|
||
理解 Self-Attention 机制和 Transformer 架构
|
||
\item
|
||
了解大语言模型的发展历程与关键技术
|
||
\item
|
||
学会在设计场景中应用文本处理技术
|
||
\end{itemize}
|
||
|
||
|
||
\section{序列建模}
|
||
|
||
\section{为什么序列数据如此重要?}
|
||
|
||
在设计领域,序列数据无处不在:
|
||
|
||
{\def\LTcaptype{none} % do not increment counter
|
||
\begin{longtable}[]{@{}lll@{}}
|
||
\toprule\noalign{}
|
||
数据类型 & 示例 & 设计场景 \\
|
||
\midrule\noalign{}
|
||
\endhead
|
||
\bottomrule\noalign{}
|
||
\endlastfoot
|
||
文本序列 & 设计说明、用户评论 & 需求分析、情感分析 \\
|
||
时间序列 & 温度、人流量、能耗 & 环境监测、建筑性能 \\
|
||
操作序列 & 用户点击、浏览路径 & 交互设计优化 \\
|
||
空间序列 & 路径规划、动线分析 & 室内导航设计 \\
|
||
\end{longtable}
|
||
}
|
||
|
||
\subsection{序列数据的两大挑战}
|
||
|
||
\textbf{挑战一:变长问题}
|
||
|
||
图像的尺寸可以统一缩放,但文本的长度千变万化:
|
||
|
||
\begin{lstlisting}
|
||
"好" → 长度 1
|
||
"这个设计方案不错" → 长度 7
|
||
"请将客厅的主色调调整为暖色系,并增加自然采光" → 长度 19
|
||
\end{lstlisting}
|
||
|
||
传统的全连接网络(MLP)要求固定大小的输入,无法直接处理变长序列。
|
||
|
||
\textbf{挑战二:时序依赖}
|
||
|
||
序列中元素的含义依赖上下文:
|
||
|
||
\begin{lstlisting}
|
||
"这个设计 很好" → 正面评价
|
||
"这个设计 很好笑" → 负面/调侃评价
|
||
|
||
"光线 不足" → 设计问题
|
||
"光线 充足" → 设计优点
|
||
\end{lstlisting}
|
||
|
||
模型需要“记住”之前看到的内容,才能理解当前内容。这正是\textbf{循环神经网络}要解决的核心问题。
|
||
|
||
|
||
\section{RNN:循环神经网络}
|
||
|
||
\subsection{核心思想}
|
||
|
||
RNN(Recurrent Neural Network)通过\textbf{隐藏状态}(Hidden State)在时间步之间传递信息,实现“记忆”功能。
|
||
|
||
\begin{lstlisting}
|
||
时间展开视图:
|
||
|
||
x₁ x₂ x₃ x₄
|
||
│ │ │ │
|
||
▼ ▼ ▼ ▼
|
||
┌───┐ ┌───┐ ┌───┐ ┌───┐
|
||
│h₁ │──→│h₂ │──→│h₃ │──→│h₄ │
|
||
└───┘ └───┘ └───┘ └───┘
|
||
│ │ │ │
|
||
▼ ▼ ▼ ▼
|
||
y₁ y₂ y₃ y₄
|
||
\end{lstlisting}
|
||
|
||
\subsection{数学表达}
|
||
|
||
在每个时间步 t,RNN 执行以下计算:
|
||
|
||
\begin{lstlisting}
|
||
隐藏状态更新:
|
||
h_t = tanh(W_hh · h_{t-1} + W_xh · x_t + b_h)
|
||
|
||
输出计算:
|
||
y_t = W_hy · h_t + b_y
|
||
\end{lstlisting}
|
||
|
||
其中: - \passthrough{\lstinline!x\_t!}:时刻 t 的输入 - \passthrough{\lstinline!h\_\{t-1\}!}:上一时刻的隐藏状态("记忆") - \passthrough{\lstinline!h\_t!}:当前时刻的隐藏状态 - \passthrough{\lstinline!W\_hh, W\_xh, W\_hy!}:可学习的权重矩阵
|
||
|
||
\subsection{直观理解}
|
||
|
||
想象你在阅读一份设计说明书:
|
||
|
||
\begin{lstlisting}
|
||
输入序列:"客厅 采用 开放式 布局 以 增加 空间感"
|
||
|
||
时间步1:看到"客厅" → h₁ = "在讨论一个房间"
|
||
时间步2:看到"采用" → h₂ = "在讨论客厅的某个选择"
|
||
时间步3:看到"开放式" → h₃ = "客厅选择了开放式布局"
|
||
时间步4:看到"布局" → h₄ = "确认:客厅开放式布局方案"
|
||
时间步5:看到"以" → h₅ = "接下来要说目的"
|
||
时间步6:看到"增加" → h₆ = "目的是增加某个属性"
|
||
时间步7:看到"空间感" → h₇ = "完整理解:客厅用开放式布局来增加空间感"
|
||
\end{lstlisting}
|
||
|
||
每一步,RNN 都在累积和更新对文本的理解。这就是“记忆”的力量。
|
||
|
||
\subsection{PyTorch 代码示例}
|
||
|
||
\begin{lstlisting}[language=Python]
|
||
import torch
|
||
import torch.nn as nn
|
||
|
||
# 定义一个简单的 RNN
|
||
class SimpleRNN(nn.Module):
|
||
def __init__(self, input_size, hidden_size, output_size):
|
||
super().__init__()
|
||
self.rnn = nn.RNN(input_size, hidden_size, batch_first=True)
|
||
self.fc = nn.Linear(hidden_size, output_size)
|
||
|
||
def forward(self, x):
|
||
# x 形状: (batch, seq_len, input_size)
|
||
out, h_n = self.rnn(x) # out: 所有时间步的输出
|
||
out = self.fc(out[:, -1, :]) # 取最后一个时间步
|
||
return out
|
||
|
||
# 创建模型
|
||
model = SimpleRNN(input_size=100, hidden_size=64, output_size=2)
|
||
# 用于文本分类(如:正面/负面评价)
|
||
\end{lstlisting}
|
||
|
||
|
||
\section{RNN 的致命问题:梯度消失与梯度爆炸}
|
||
|
||
\subsection{问题根源}
|
||
|
||
RNN 需要通过反向传播跨越多个时间步来更新参数。当序列较长时,梯度要经过多次乘法运算:
|
||
|
||
\begin{lstlisting}
|
||
梯度回传路径(以4步为例):
|
||
|
||
∂L/∂W = (∂L/∂h₄) · (∂h₄/∂h₃) · (∂h₃/∂h₂) · (∂h₂/∂h₁) · (∂h₁/∂W)
|
||
└───────── 多次连乘 ──────────┘
|
||
\end{lstlisting}
|
||
|
||
\begin{itemize}
|
||
\tightlist
|
||
\item
|
||
如果每步的梯度 \textless{} 1 → 连乘后趋近于 0 → \textbf{梯度消失}(学不到远距离依赖)
|
||
\item
|
||
如果每步的梯度 \textgreater{} 1 → 连乘后趋近于 ∞ → \textbf{梯度爆炸}(训练不稳定)
|
||
\end{itemize}
|
||
|
||
\subsection{直观类比}
|
||
|
||
\begin{lstlisting}
|
||
传话游戏:
|
||
|
||
第1个人:"把客厅漆成温暖的米黄色"
|
||
第2个人:"把客厅漆成温暖的米……什么?" ← 信息开始丢失
|
||
第3个人:"客厅……漆成……?" ← 大量信息丢失
|
||
第4个人:"什么客厅?" ← 几乎全忘了
|
||
|
||
RNN 处理长序列时遇到同样的问题!
|
||
\end{lstlisting}
|
||
|
||
在实际应用中,标准 RNN 通常只能“记住”5-10步之前的信息。对于“这个设计方案虽然外观简洁大方,功能分区合理,但是整体造价偏高”这样跨越多个分句的评价,RNN 可能会遗忘前半部分的内容。
|
||
|
||
|
||
\section{LSTM:长短期记忆网络}
|
||
|
||
\subsection{设计哲学}
|
||
|
||
LSTM(Long Short-Term Memory)由 Hochreiter 和 Schmidhuber 于1997年提出,核心创新是引入\textbf{细胞状态}(Cell State)——一条贯穿整个序列的“信息高速公路”,让信息可以长距离传递而不被稀释。
|
||
|
||
\begin{lstlisting}
|
||
LSTM 单元内部结构:
|
||
|
||
┌──────────────────────────────────────┐
|
||
│ LSTM 单元 │
|
||
│ │
|
||
c_{t-1} ───────→ │ ─── 遗忘门 ──→ ── 输入门 ──→ ──────→ │ ──→ c_t
|
||
│ ↓ ↓ ↓ │ (细胞状态)
|
||
h_{t-1} ──┐ │ σ σ+σ̃ tanh │
|
||
│ │ ↓ ↓ ↓ │
|
||
x_t ──────┤ │ f_t i_t c̃_t │
|
||
│ │ ↓ │
|
||
│ │ c_t = f_t⊙c_{t-1} + i_t⊙c̃_t
|
||
│ │ ↓ │
|
||
└─────→│ ────────────── 输出门 ──────→│ │
|
||
│ σ → o_t ↓ │
|
||
│ h_t = o_t ⊙ tanh(c_t) │
|
||
└──────────────────────────────────────┘
|
||
\end{lstlisting}
|
||
|
||
\subsection{三道门:遗忘、输入、输出}
|
||
|
||
LSTM 通过三个“门”(Gate)来精确控制信息的流动:
|
||
|
||
\textbf{1. 遗忘门(Forget Gate)}:决定从细胞状态中丢弃什么信息
|
||
|
||
\begin{lstlisting}
|
||
f_t = σ(W_f · [h_{t-1}, x_t] + b_f)
|
||
|
||
输出范围 (0, 1):0 = 全部遗忘,1 = 全部保留
|
||
\end{lstlisting}
|
||
|
||
\textbf{2. 输入门(Input Gate)}:决定将什么新信息存入细胞状态
|
||
|
||
\begin{lstlisting}
|
||
i_t = σ(W_i · [h_{t-1}, x_t] + b_i) # 哪些信息需要更新
|
||
c̃_t = tanh(W_c · [h_{t-1}, x_t] + b_c) # 新的候选值
|
||
\end{lstlisting}
|
||
|
||
\textbf{3. 输出门(Output Gate)}:决定输出什么信息
|
||
|
||
\begin{lstlisting}
|
||
o_t = σ(W_o · [h_{t-1}, x_t] + b_o)
|
||
h_t = o_t ⊙ tanh(c_t)
|
||
\end{lstlisting}
|
||
|
||
\subsection{细胞状态更新}
|
||
|
||
\begin{lstlisting}
|
||
c_t = f_t ⊙ c_{t-1} + i_t ⊙ c̃_t
|
||
└────────┘ └────────┘
|
||
保留旧信息 加入新信息
|
||
\end{lstlisting}
|
||
|
||
\subsection{设计类比}
|
||
|
||
\begin{lstlisting}
|
||
LSTM 就像一位优秀的设计项目管理者:
|
||
|
||
遗忘门 → "之前的方案中哪些思路已经过时了?"
|
||
输入门 → "客户提出了什么新需求?"
|
||
细胞状态 → "项目的核心理念和关键约束"(长期记忆)
|
||
隐藏状态 → "当前正在讨论的具体话题"(短期记忆)
|
||
输出门 → "现在应该向团队传达什么信息?"
|
||
\end{lstlisting}
|
||
|
||
\subsection{代码示例}
|
||
|
||
\begin{lstlisting}[language=Python]
|
||
import torch
|
||
import torch.nn as nn
|
||
|
||
class TextClassifier(nn.Module):
|
||
"""基于 LSTM 的文本分类模型"""
|
||
def __init__(self, vocab_size, embed_dim, hidden_dim, num_classes):
|
||
super().__init__()
|
||
self.embedding = nn.Embedding(vocab_size, embed_dim)
|
||
self.lstm = nn.LSTM(embed_dim, hidden_dim, batch_first=True)
|
||
self.fc = nn.Linear(hidden_dim, num_classes)
|
||
|
||
def forward(self, x):
|
||
# x: (batch, seq_len) 整数索引
|
||
embedded = self.embedding(x) # (batch, seq_len, embed_dim)
|
||
output, (h_n, c_n) = self.lstm(embedded)
|
||
logits = self.fc(h_n.squeeze(0)) # 取最后隐藏状态
|
||
return logits
|
||
|
||
# 使用示例
|
||
model = TextClassifier(vocab_size=10000, embed_dim=128,
|
||
hidden_dim=256, num_classes=3)
|
||
# 3分类:正面/中性/负面(设计评论情感分析)
|
||
\end{lstlisting}
|
||
|
||
|
||
\section{GRU:简化版 LSTM}
|
||
|
||
GRU(Gated Recurrent Unit)由 Cho 等人于2014年提出,将遗忘门和输入门合并为\textbf{更新门},参数更少,训练更快。
|
||
|
||
\begin{lstlisting}
|
||
GRU 结构:
|
||
|
||
h_{t-1} ──→ ┌─────────────────┐ ──→ h_t
|
||
│ 重置门 r_t │
|
||
x_t ──────→ │ 更新门 z_t │
|
||
│ h̃_t = tanh(...) │
|
||
│ h_t = (1-z_t)⊙h_{t-1} + z_t⊙h̃_t
|
||
└─────────────────┘
|
||
\end{lstlisting}
|
||
|
||
\subsection{LSTM vs GRU 对比}
|
||
|
||
{\def\LTcaptype{none} % do not increment counter
|
||
\begin{longtable}[]{@{}lll@{}}
|
||
\toprule\noalign{}
|
||
特性 & LSTM & GRU \\
|
||
\midrule\noalign{}
|
||
\endhead
|
||
\bottomrule\noalign{}
|
||
\endlastfoot
|
||
门数量 & 3个(遗忘、输入、输出) & 2个(重置、更新) \\
|
||
细胞状态 & 有独立的细胞状态 c\_t & 无,直接用隐藏状态 \\
|
||
参数量 & 较多 & 较少(约少1/3) \\
|
||
训练速度 & 较慢 & 较快 \\
|
||
适用场景 & 复杂长序列 & 中等长度序列 \\
|
||
\end{longtable}
|
||
}
|
||
|
||
\subsection{如何选择?}
|
||
|
||
\begin{lstlisting}
|
||
经验法则:
|
||
├── 数据量充足、序列很长 → LSTM(更强的表达能力)
|
||
├── 数据量有限、序列中等 → GRU(更快收敛,不易过拟合)
|
||
└── 都试试,看验证集表现 → 实践出真知
|
||
\end{lstlisting}
|
||
|
||
|
||
\section{设计应用:文本分类与时间序列预测}
|
||
|
||
\subsection{应用一:设计文档自动分类}
|
||
|
||
\begin{lstlisting}[language=Python]
|
||
import torch
|
||
import torch.nn as nn
|
||
|
||
class DesignDocClassifier(nn.Module):
|
||
"""将设计文档自动分类为:建筑设计/景观设计/室内设计/平面设计"""
|
||
def __init__(self, vocab_size=20000, embed_dim=128, hidden_dim=256):
|
||
super().__init__()
|
||
self.embedding = nn.Embedding(vocab_size, embed_dim)
|
||
self.lstm = nn.LSTM(embed_dim, hidden_dim,
|
||
num_layers=2, # 2层 LSTM
|
||
bidirectional=True, # 双向:同时看前后文
|
||
dropout=0.3,
|
||
batch_first=True)
|
||
self.fc = nn.Linear(hidden_dim * 2, 4) # 4个设计类别
|
||
|
||
def forward(self, x):
|
||
embedded = self.embedding(x)
|
||
output, _ = self.lstm(embedded)
|
||
# 取正反向最后隐藏状态拼接
|
||
logits = self.fc(output[:, -1, :])
|
||
return logits
|
||
\end{lstlisting}
|
||
|
||
\subsection{应用二:建筑能耗时间序列预测}
|
||
|
||
\begin{lstlisting}[language=Python]
|
||
import torch
|
||
import torch.nn as nn
|
||
|
||
class EnergyPredictor(nn.Module):
|
||
"""基于 LSTM 的建筑能耗预测模型"""
|
||
def __init__(self, input_dim=6, hidden_dim=64, pred_steps=24):
|
||
super().__init__()
|
||
# input_dim: 温度、湿度、光照、人流量、时间(时)、星期几
|
||
self.lstm = nn.LSTM(input_dim, hidden_dim, num_layers=2,
|
||
dropout=0.2, batch_first=True)
|
||
self.fc = nn.Linear(hidden_dim, pred_steps) # 预测未来24小时
|
||
|
||
def forward(self, x):
|
||
# x: (batch, past_hours, features) 过去若干小时的数据
|
||
_, (h_n, _) = self.lstm(x)
|
||
prediction = self.fc(h_n[-1]) # 最后一层的隐藏状态
|
||
return prediction
|
||
\end{lstlisting}
|
||
|
||
|
||
\section{注意力革命}
|
||
|
||
\section{RNN 的瓶颈:串行计算的困境}
|
||
|
||
尽管 LSTM 和 GRU 缓解了梯度消失问题,但 RNN 的根本架构限制依然存在:
|
||
|
||
\begin{lstlisting}
|
||
RNN 的串行瓶颈:
|
||
|
||
时间步: t=1 t=2 t=3 t=4 t=5
|
||
│ │ │ │ │
|
||
▼ ▼ ▼ ▼ ▼
|
||
┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐
|
||
x₁ → │h₁│→ │h₂│→ │h₃│→ │h₃│→ │h₅│ → 输出
|
||
└───┘ └───┘ └───┘ └───┘ └───┘
|
||
|
||
问题1:必须等 t=1 算完才能算 t=2,无法并行!
|
||
问题2:h₅ 能充分了解 x₁ 的信息吗?(远距离依赖)
|
||
\end{lstlisting}
|
||
|
||
{\def\LTcaptype{none} % do not increment counter
|
||
\begin{longtable}[]{@{}ll@{}}
|
||
\toprule\noalign{}
|
||
RNN 的局限 & 具体影响 \\
|
||
\midrule\noalign{}
|
||
\endhead
|
||
\bottomrule\noalign{}
|
||
\endlastfoot
|
||
串行计算 & 训练速度慢,无法利用 GPU 并行能力 \\
|
||
长距离依赖衰减 & 即使是 LSTM,50步以上的依赖也会衰减 \\
|
||
固定的信息压缩 & 无论序列多长,都压缩到一个固定大小的 h\_t \\
|
||
单向信息流 & 后面的信息无法影响前面的理解(除非用双向) \\
|
||
\end{longtable}
|
||
}
|
||
|
||
2017年,一篇名为《Attention Is All You Need》的论文彻底改变了这一局面。
|
||
|
||
|
||
\section{Self-Attention:自注意力机制}
|
||
|
||
\subsection{核心思想}
|
||
|
||
自注意力让序列中的每个位置都能\textbf{直接}与所有其他位置“交流”,一步到位地获取全局信息。
|
||
|
||
\begin{lstlisting}
|
||
传统 RNN(串行传递信息):
|
||
|
||
"设计" → "这个" → "客厅" → "的" → "风格"
|
||
↑
|
||
需要回溯4步才能看到"设计"
|
||
|
||
Self-Attention(直接连接):
|
||
|
||
"设计" ─────────────────────→ "风格"
|
||
↘ ↗
|
||
"这个" ──→ "客厅"
|
||
↗ ↘
|
||
"的" ──────────────────→ "风格"
|
||
|
||
每个词都可以直接"看到"所有其他词!
|
||
\end{lstlisting}
|
||
|
||
\subsection{Q、K、V 机制}
|
||
|
||
Self-Attention 借鉴了信息检索的思想:
|
||
|
||
\begin{lstlisting}
|
||
类比:在图书馆找书
|
||
|
||
Q(Query/查询) = "我想找关于北欧风格室内设计的书"
|
||
K(Key/键) = 每本书的标签:"现代设计""北欧风格""古典建筑"...
|
||
V(Value/值) = 每本书的实际内容
|
||
|
||
计算过程:
|
||
1. Q 和每个 K 比较相似度 → 得到"关注程度"
|
||
2. 按关注程度加权求和 V → 得到最终结果
|
||
\end{lstlisting}
|
||
|
||
在序列建模中,每个位置同时扮演 Q、K、V 三个角色:
|
||
|
||
\begin{lstlisting}
|
||
输入序列 X:[x₁, x₂, x₃, x₄]
|
||
|
||
线性变换:
|
||
Q = X · W_Q # 查询矩阵:我想找什么?
|
||
K = X · W_K # 键矩阵:我包含什么信息?
|
||
V = X · W_V # 值矩阵:我的具体内容是什么?
|
||
\end{lstlisting}
|
||
|
||
\subsection{Scaled Dot-Product Attention}\label{scaled-dot-product-attention}
|
||
|
||
完整的注意力计算公式:
|
||
|
||
\begin{lstlisting}
|
||
Q · Kᵀ
|
||
Attention(Q,K,V) = softmax(─────) · V
|
||
√d_k
|
||
|
||
步骤分解:
|
||
|
||
步骤1:计算注意力分数
|
||
scores = Q · Kᵀ # 矩阵乘法,得到每对位置的相似度
|
||
|
||
步骤2:缩放
|
||
scores = scores / √d_k # 除以维度平方根,防止梯度消失
|
||
|
||
步骤3:归一化
|
||
weights = softmax(scores) # 转为概率分布,和为1
|
||
|
||
步骤4:加权求和
|
||
output = weights · V # 按注意力权重聚合信息
|
||
\end{lstlisting}
|
||
|
||
\subsection{为什么要缩放(Scale)?}
|
||
|
||
\begin{lstlisting}
|
||
当 d_k 较大时,Q·Kᵀ 的值也会很大
|
||
→ softmax 输入很大时会进入"饱和区"
|
||
→ 梯度变得极小,训练困难
|
||
→ 除以 √d_k 将值拉回合理范围
|
||
|
||
示例(d_k = 64):
|
||
缩放前:softmax([10, 8, 3, -5]) → [0.87, 0.12, 0.001, 0.000001]
|
||
缩放后:softmax([1.25, 1.0, 0.375, -0.625]) → [0.38, 0.30, 0.16, 0.07]
|
||
\end{lstlisting}
|
||
|
||
\subsection{代码实现}
|
||
|
||
\begin{lstlisting}[language=Python]
|
||
import torch
|
||
import torch.nn.functional as F
|
||
|
||
def scaled_dot_product_attention(Q, K, V):
|
||
"""
|
||
Q: (batch, seq_len, d_k)
|
||
K: (batch, seq_len, d_k)
|
||
V: (batch, seq_len, d_v)
|
||
"""
|
||
d_k = Q.size(-1)
|
||
|
||
# 步骤1 & 2: 计算缩放后的注意力分数
|
||
scores = torch.matmul(Q, K.transpose(-2, -1)) / (d_k ** 0.5)
|
||
|
||
# 步骤3: softmax 归一化
|
||
weights = F.softmax(scores, dim=-1)
|
||
|
||
# 步骤4: 加权求和
|
||
output = torch.matmul(weights, V)
|
||
|
||
return output, weights
|
||
|
||
# 示例:对句子"客厅 采用 开放式 布局"做自注意力
|
||
seq_len, d_k = 4, 64
|
||
Q = torch.randn(1, seq_len, d_k)
|
||
K = torch.randn(1, seq_len, d_k)
|
||
V = torch.randn(1, seq_len, d_k)
|
||
|
||
output, attn_weights = scaled_dot_product_attention(Q, K, V)
|
||
print(f"注意力权重形状: {attn_weights.shape}") # (1, 4, 4) - 每个词对其他词的关注度
|
||
print(f"输出形状: {output.shape}") # (1, 4, 64)
|
||
\end{lstlisting}
|
||
|
||
|
||
\section{Multi-Head Attention:多头注意力}
|
||
|
||
\subsection{为什么需要多头?}
|
||
|
||
单个注意力头只能学习一种“关注模式”。现实中的语言理解需要同时关注多种关系:
|
||
|
||
\begin{lstlisting}
|
||
句子:"设计师用木质材料打造了温馨的北欧风格客厅"
|
||
|
||
头1 关注:语法关系 → "设计师"是主语,"打造"是谓语
|
||
头2 关注:修饰关系 → "温馨的"修饰"客厅","北欧风格"修饰"客厅"
|
||
头3 关注:语义关系 → "木质材料"与"北欧风格"语义关联
|
||
头4 关注:设计属性 → "温馨"是情感描述,"北欧风格"是风格类别
|
||
\end{lstlisting}
|
||
|
||
\subsection{结构示意}
|
||
|
||
\begin{lstlisting}
|
||
输入 X
|
||
│
|
||
├──→ Head 1: Q₁=X·W₁ᵠ, K₁=X·W₁ᵏ, V₁=X·W₁ᵛ → Attention(Q₁,K₁,V₁) → head₁
|
||
├──→ Head 2: Q₂=X·W₂ᵠ, K₂=X·W₂ᵏ, V₂=X·W₂ᵛ → Attention(Q₂,K₂,V₂) → head₂
|
||
├──→ Head 3: Q₃=X·W₃ᵠ, K₃=X·W₃ᵏ, V₃=X·W₃ᵛ → Attention(Q₃,K₃,V₃) → head₃
|
||
│ ...
|
||
└──→ Head h: Qₕ=X·Wₕᵠ, Kₕ=X·Wₕᵏ, Vₕ=X·Wₕᵛ → Attention(Qₕ,Kₕ,Vₕ) → headₕ
|
||
│
|
||
Concat(head₁, ..., headₕ) → Linear → Output │
|
||
\end{lstlisting}
|
||
|
||
\subsection{代码实现}
|
||
|
||
\begin{lstlisting}[language=Python]
|
||
class MultiHeadAttention(nn.Module):
|
||
def __init__(self, d_model, num_heads):
|
||
super().__init__()
|
||
assert d_model % num_heads == 0
|
||
|
||
self.d_k = d_model // num_heads
|
||
self.num_heads = num_heads
|
||
|
||
# 为每个头创建 Q, K, V 的线性变换
|
||
self.W_q = nn.Linear(d_model, d_model)
|
||
self.W_k = nn.Linear(d_model, d_model)
|
||
self.W_v = nn.Linear(d_model, d_model)
|
||
self.W_o = nn.Linear(d_model, d_model)
|
||
|
||
def forward(self, x):
|
||
batch_size = x.size(0)
|
||
|
||
# 线性变换并分割为多头
|
||
Q = self.W_q(x).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
|
||
K = self.W_k(x).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
|
||
V = self.W_v(x).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
|
||
|
||
# 计算注意力
|
||
attn_output, _ = scaled_dot_product_attention(Q, K, V)
|
||
|
||
# 合并多头
|
||
attn_output = attn_output.transpose(1, 2).contiguous()
|
||
attn_output = attn_output.view(batch_size, -1, self.num_heads * self.d_k)
|
||
|
||
# 最终线性变换
|
||
return self.W_o(attn_output)
|
||
\end{lstlisting}
|
||
|
||
|
||
\section{Positional Encoding:位置编码}
|
||
|
||
\subsection{为什么需要位置信息?}
|
||
|
||
Self-Attention 本身是“位置无关”的——它把输入看作一个集合(Set),而不是序列(Sequence):
|
||
|
||
\begin{lstlisting}
|
||
输入 A:"客厅 在 北面"
|
||
输入 B:"北面 在 客厅"
|
||
|
||
Self-Attention 不加位置信息时,会把两者视为完全相同!
|
||
(因为词相同,只是顺序不同,但 Attention 是排列不变的)
|
||
\end{lstlisting}
|
||
|
||
\subsection{正弦/余弦位置编码}
|
||
|
||
Transformer 使用正弦和余弦函数为每个位置生成独特的编码:
|
||
|
||
\begin{lstlisting}
|
||
PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
|
||
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
|
||
|
||
其中:
|
||
pos = 位置索引(第几个词)
|
||
i = 维度索引
|
||
d_model = 模型维度
|
||
\end{lstlisting}
|
||
|
||
\begin{lstlisting}
|
||
位置编码示意(d_model=4 简化版):
|
||
|
||
位置0: [sin(0), cos(0), sin(0), cos(0) ]
|
||
位置1: [sin(1), cos(1), sin(0.0001), cos(0.0001) ]
|
||
位置2: [sin(2), cos(2), sin(0.0002), cos(0.0002) ]
|
||
位置3: [sin(3), cos(3), sin(0.0003), cos(0.0003) ]
|
||
...
|
||
\end{lstlisting}
|
||
|
||
\subsection{代码实现}
|
||
|
||
\begin{lstlisting}[language=Python]
|
||
import torch
|
||
import math
|
||
|
||
def positional_encoding(seq_len, d_model):
|
||
"""生成位置编码矩阵"""
|
||
pe = torch.zeros(seq_len, d_model)
|
||
position = torch.arange(0, seq_len).unsqueeze(1).float()
|
||
|
||
# 计算频率
|
||
div_term = torch.exp(
|
||
torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model)
|
||
)
|
||
|
||
pe[:, 0::2] = torch.sin(position * div_term) # 偶数维度
|
||
pe[:, 1::2] = torch.cos(position * div_term) # 奇数维度
|
||
|
||
return pe
|
||
|
||
# 示例:为长度50的序列生成64维位置编码
|
||
pe = positional_encoding(50, 64)
|
||
print(f"位置编码形状: {pe.shape}") # (50, 64)
|
||
\end{lstlisting}
|
||
|
||
|
||
\section{Self-Attention vs RNN:全面对比}
|
||
|
||
{\def\LTcaptype{none} % do not increment counter
|
||
\begin{longtable}[]{@{}lll@{}}
|
||
\toprule\noalign{}
|
||
特性 & RNN/LSTM & Self-Attention \\
|
||
\midrule\noalign{}
|
||
\endhead
|
||
\bottomrule\noalign{}
|
||
\endlastfoot
|
||
计算方式 & 串行(逐步) & 并行(一步) \\
|
||
长距离依赖 & 需要传递多步,信息衰减 & 任意距离,一步直达 \\
|
||
训练速度 & 慢(无法充分并行) & 快(GPU 友好) \\
|
||
位置信息 & 天然有序(按时间步处理) & 需要额外位置编码 \\
|
||
内存消耗 & O(1) 每步 & O(n²) 需存储 n×n 注意力矩阵 \\
|
||
适用长度 & 中短序列(\textless100) & 中长序列(\textless 数万,受内存限制) \\
|
||
\end{longtable}
|
||
}
|
||
|
||
\begin{lstlisting}
|
||
计算复杂度对比:
|
||
|
||
RNN: O(n · d²) - 线性于序列长度
|
||
Attention: O(n² · d) - 二次于序列长度
|
||
|
||
当 n(序列长度)远大于 d(维度)时,Attention 更慢
|
||
但 Attention 的并行性弥补了这一劣势
|
||
\end{lstlisting}
|
||
|
||
|
||
\section{Transformer 架构}
|
||
|
||
\section{整体结构}
|
||
|
||
Transformer 由 Vaswani 等人在2017年提出,采用 \textbf{Encoder-Decoder}(编码器-解码器)结构:
|
||
|
||
\begin{lstlisting}
|
||
┌──────────────────────────────────────────────────────────────┐
|
||
│ Transformer 架构 │
|
||
│ │
|
||
│ 输入序列 输出序列 │
|
||
│ "设计一个客厅" "现代简约风格客厅..." │
|
||
│ │ ↑ │
|
||
│ ▼ │ │
|
||
│ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ Encoder │ │ Decoder │ │
|
||
│ │ ┌────────┐ │ │ ┌────────┐ │ │
|
||
│ │ │ Enc │ │ │ │ Dec │ │ │
|
||
│ │ │ Layer │ │ ────────→ │ │ Layer │ │ │
|
||
│ │ │ ×N │ │ 编码信息 │ │ ×N │ │ │
|
||
│ │ └────────┘ │ │ └────────┘ │ │
|
||
│ └──────────────┘ └──────────────┘ │
|
||
│ ↑ ↑ │
|
||
│ 输入嵌入 输出嵌入 │
|
||
│ + 位置编码 + 位置编码 │
|
||
└──────────────────────────────────────────────────────────────┘
|
||
\end{lstlisting}
|
||
|
||
|
||
\section{Encoder Layer(编码器层)}
|
||
|
||
每一层编码器包含两个子层:
|
||
|
||
\begin{lstlisting}
|
||
输入
|
||
│
|
||
▼
|
||
┌──────────────────────────┐
|
||
│ Multi-Head Attention │ ← 子层1:自注意力
|
||
│ (Self-Attention) │
|
||
└──────────────────────────┘
|
||
│ ↑
|
||
├──── Add & Norm ────┘ ← 残差连接 + 层归一化
|
||
│
|
||
▼
|
||
┌──────────────────────────┐
|
||
│ Feed-Forward Network │ ← 子层2:前馈网络
|
||
│ (FFN) │
|
||
└──────────────────────────┘
|
||
│ ↑
|
||
├──── Add & Norm ────┘ ← 残差连接 + 层归一化
|
||
│
|
||
▼
|
||
输出 → 传递给下一层或 Decoder
|
||
\end{lstlisting}
|
||
|
||
\subsection{子层详解}
|
||
|
||
\textbf{子层1:多头自注意力}
|
||
|
||
\begin{lstlisting}[language=Python]
|
||
# Encoder 中的 Self-Attention
|
||
# Q, K, V 都来自同一个输入
|
||
attn_output = MultiHeadAttention(x, x, x)
|
||
x = LayerNorm(x + attn_output) # 残差连接 + 层归一化
|
||
\end{lstlisting}
|
||
|
||
\textbf{子层2:前馈网络(FFN)}
|
||
|
||
\begin{lstlisting}
|
||
FFN(x) = W₂ · ReLU(W₁ · x + b₁) + b₂
|
||
|
||
# 两层线性变换,中间用 ReLU 激活
|
||
# 通常 d_ff = 4 × d_model(先升维再降维)
|
||
\end{lstlisting}
|
||
|
||
\begin{lstlisting}[language=Python]
|
||
class FeedForward(nn.Module):
|
||
def __init__(self, d_model, d_ff=2048):
|
||
super().__init__()
|
||
self.linear1 = nn.Linear(d_model, d_ff)
|
||
self.linear2 = nn.Linear(d_ff, d_model)
|
||
|
||
def forward(self, x):
|
||
return self.linear2(F.relu(self.linear1(x)))
|
||
\end{lstlisting}
|
||
|
||
|
||
\section{Decoder Layer(解码器层)}
|
||
|
||
解码器比编码器多了一个\textbf{交叉注意力}(Cross-Attention)子层:
|
||
|
||
\begin{lstlisting}
|
||
输入(已生成的输出)
|
||
│
|
||
▼
|
||
┌──────────────────────────┐
|
||
│ Masked Multi-Head │ ← 子层1:掩码自注意力
|
||
│ Self-Attention │ (只能看到已生成的词)
|
||
└──────────────────────────┘
|
||
│ ↑
|
||
├──── Add & Norm ────┘
|
||
│
|
||
▼
|
||
┌──────────────────────────┐
|
||
│ Multi-Head │ ← 子层2:交叉注意力
|
||
│ Cross-Attention │ Q来自Decoder,K/V来自Encoder
|
||
│ (Q: Dec, K,V: Enc) │
|
||
└──────────────────────────┘
|
||
│ ↑
|
||
├──── Add & Norm ────┘
|
||
│
|
||
▼
|
||
┌──────────────────────────┐
|
||
│ Feed-Forward Network │ ← 子层3:前馈网络
|
||
└──────────────────────────┘
|
||
│ ↑
|
||
├──── Add & Norm ────┘
|
||
│
|
||
▼
|
||
输出 → 传递给下一层或最终线性层
|
||
\end{lstlisting}
|
||
|
||
\subsection{Masked Self-Attention:为什么要“掩码”?}
|
||
|
||
在生成文本时,模型不能“偷看”未来的词:
|
||
|
||
\begin{lstlisting}
|
||
生成 "现代 简约 风格 客厅":
|
||
|
||
时刻1:生成 "现代" → 只能看到起始符 [BOS]
|
||
时刻2:生成 "简约" → 只能看到 [BOS, 现代]
|
||
时刻3:生成 "风格" → 只能看到 [BOS, 现代, 简约]
|
||
时刻4:生成 "客厅" → 只能看到 [BOS, 现代, 简约, 风格]
|
||
|
||
掩码矩阵(上三角遮住未来信息):
|
||
|
||
现代 简约 风格 客厅
|
||
现代 [ 1 0 0 0 ] ← 只看自己
|
||
简约 [ 1 1 0 0 ] ← 看自己和"现代"
|
||
风格 [ 1 1 1 0 ] ← 看前面三个
|
||
客厅 [ 1 1 1 1 ] ← 看所有
|
||
|
||
0 = 被遮住(设为 -inf,softmax 后变为 0)
|
||
\end{lstlisting}
|
||
|
||
|
||
\section{残差连接与层归一化}
|
||
|
||
\subsection{残差连接(Residual Connection)}
|
||
|
||
\begin{lstlisting}
|
||
核心公式:output = LayerNorm(x + Sublayer(x))
|
||
|
||
为什么重要?
|
||
┌──────────────────────────────────────────────┐
|
||
│ 没有残差连接:梯度要穿过每一层 │
|
||
│ x → Layer1 → Layer2 → ... → Layer12 → Loss │
|
||
│ │
|
||
│ 有残差连接:梯度有"捷径"可以直接回传 │
|
||
│ x → Layer1 → (+x) → Layer2 → (+x) → ... │
|
||
│ ↑ │
|
||
│ └── 梯度可以直接沿加法路径回传 ──→ │
|
||
└──────────────────────────────────────────────┘
|
||
\end{lstlisting}
|
||
|
||
\subsection{层归一化(Layer Normalization)}
|
||
|
||
\begin{lstlisting}
|
||
对每个样本的所有特征进行归一化:
|
||
|
||
LayerNorm(x) = (x - μ) / σ · γ + β
|
||
|
||
其中 μ, σ 是该样本所有特征的均值和标准差
|
||
γ, β 是可学习的缩放和偏移参数
|
||
|
||
作用:稳定训练过程,加速收敛
|
||
\end{lstlisting}
|
||
|
||
|
||
\section{Transformer 三大变体}
|
||
|
||
\begin{lstlisting}
|
||
Transformer 家族树
|
||
|
||
Original Transformer
|
||
(Encoder + Decoder)
|
||
┌────────┴────────┐
|
||
│ │
|
||
┌──────┴──────┐ ┌─────┴─────┐
|
||
│ │ │ │
|
||
BERT GPT T5/BART
|
||
(Encoder-only) (Decoder-only) (Encoder-Decoder)
|
||
│ │ │
|
||
理解为主 生成为主 理解+生成
|
||
\end{lstlisting}
|
||
|
||
\subsection{BERT(Encoder-only)}\label{bertencoder-only}
|
||
|
||
\begin{lstlisting}
|
||
特点:双向理解,擅长"阅读理解"
|
||
|
||
训练方式:
|
||
1. MLM(Masked Language Model):随机遮住15%的词,让模型预测
|
||
"客厅采用了[MASK]风格的设计" → 预测 [MASK] = "北欧"
|
||
|
||
2. NSP(Next Sentence Prediction):判断两句话是否相邻
|
||
|
||
擅长任务:
|
||
- 文本分类(设计文档分类)
|
||
- 命名实体识别(提取设计要素)
|
||
- 问答系统(设计知识库问答)
|
||
- 情感分析(用户评价分析)
|
||
\end{lstlisting}
|
||
|
||
\subsection{GPT(Decoder-only)}\label{gptdecoder-only}
|
||
|
||
\begin{lstlisting}
|
||
特点:自回归生成,擅长"续写"
|
||
|
||
训练方式:
|
||
给定前面的词,预测下一个词:
|
||
"设计师选择的" → 预测下一个词(可能是"材料"/"色彩"/"风格")
|
||
|
||
擅长任务:
|
||
- 文本生成(设计描述生成)
|
||
- 对话系统(设计咨询助手)
|
||
- 代码生成(设计自动化)
|
||
- 创意构思(头脑风暴)
|
||
\end{lstlisting}
|
||
|
||
\subsection{T5(Encoder-Decoder)}\label{t5encoder-decoder}
|
||
|
||
\begin{lstlisting}
|
||
特点:用统一的"文本到文本"框架处理所有NLP任务
|
||
|
||
设计理念:
|
||
所有NLP任务都转化为:输入文本 → 输出文本
|
||
|
||
翻译: "translate English to Chinese: Hello" → "你好"
|
||
摘要: "summarize: 很长的设计文档..." → "核心要点..."
|
||
分类: "classify: 这是一篇关于景观的文章" → "景观设计"
|
||
\end{lstlisting}
|
||
|
||
\subsection{三大变体对比}
|
||
|
||
{\def\LTcaptype{none} % do not increment counter
|
||
\begin{longtable}[]{@{}llll@{}}
|
||
\toprule\noalign{}
|
||
特性 & BERT & GPT & T5 \\
|
||
\midrule\noalign{}
|
||
\endhead
|
||
\bottomrule\noalign{}
|
||
\endlastfoot
|
||
架构 & Encoder & Decoder & Encoder-Decoder \\
|
||
方向 & 双向 & 单向(从左到右) & 编码双向,解码单向 \\
|
||
核心能力 & 理解 & 生成 & 理解+生成 \\
|
||
典型规模 & 340M & 117M \textasciitilde{} 1.8T & 60M \textasciitilde{} 11B \\
|
||
设计应用 & 分析评价 & 生成方案 & 转换/翻译 \\
|
||
\end{longtable}
|
||
}
|
||
|
||
|
||
\section{Token 处理流水线}
|
||
|
||
文本进入 Transformer 之前,需要经过完整的预处理流水线:
|
||
|
||
\begin{lstlisting}
|
||
原始文本 Tokenization Embedding
|
||
"设计一个温馨的客厅" → [设,计,一,个,温,馨,的,客,厅] → 每个token → 768维向量
|
||
↓
|
||
+ Positional Encoding
|
||
↓
|
||
输入 Transformer
|
||
\end{lstlisting}
|
||
|
||
\subsection{Tokenization:分词}
|
||
|
||
\begin{lstlisting}
|
||
三种分词策略:
|
||
|
||
1. 字符级(Character-level)
|
||
"设计" → ["设", "计"]
|
||
✗ 优点:词表小 ✗ 缺点:序列太长,丢失词义
|
||
|
||
2. 词级(Word-level)
|
||
"设计一个客厅" → ["设计", "一个", "客厅"]
|
||
✓ 优点:保留语义 ✗ 缺点:词表巨大,无法处理新词
|
||
|
||
3. 子词级(Subword-level) ← BPE/GPT 使用的方式
|
||
"uncomfortable" → ["un", "comfort", "able"]
|
||
✓ 优点:平衡词表大小和语义 ✓ 缺点:无明显缺点
|
||
\end{lstlisting}
|
||
|
||
\begin{lstlisting}[language=Python]
|
||
# 使用 HuggingFace 的分词器
|
||
from transformers import AutoTokenizer
|
||
|
||
tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese")
|
||
tokens = tokenizer("设计一个温馨的客厅")
|
||
print(f"Token IDs: {tokens['input_ids']}")
|
||
print(f"Token 文本: {tokenizer.convert_ids_to_tokens(tokens['input_ids'])}")
|
||
|
||
# 输出示例:
|
||
# Token IDs: [101, 6371, 2552, 671, 702, 3649, 5889, 4638, 2642, 1079, 102]
|
||
# Token 文本: ['[CLS]', '设', '计', '一', '个', '温', '馨', '的', '客', '厅', '[SEP]']
|
||
\end{lstlisting}
|
||
|
||
|
||
\section{大语言模型}
|
||
|
||
\section{从 Transformer 到大语言模型}
|
||
|
||
Transformer 论文发表后的几年里,研究者们发现了一个关键规律:\textbf{只要持续增大模型的规模(参数量、数据量、计算量),模型的能力就会不断提升}。这就是 Scaling Law 的威力。
|
||
|
||
大语言模型(Large Language Model, LLM)的演进时间线:
|
||
|
||
\begin{lstlisting}
|
||
模型规模指数级增长:
|
||
|
||
参数量
|
||
│
|
||
│ GPT-4
|
||
│ (~1.8T)
|
||
│ GPT-3
|
||
│ (175B) ────────╮
|
||
│ GPT-2 │
|
||
│ (1.5B) │
|
||
│ GPT-1 │
|
||
│ (117M) │
|
||
│ BERT │
|
||
│ (340M) │
|
||
│ │
|
||
└──┴─────┴──────┴──────┴──────┴──────┴───────┴──── 时间
|
||
2018 2018 2019 2020 2022 2023
|
||
\end{lstlisting}
|
||
|
||
|
||
\section{GPT 系列演进}
|
||
|
||
\subsection{GPT-1(2018):奠定基础}
|
||
|
||
\begin{lstlisting}
|
||
参数量:117M
|
||
训练数据:约5GB文本
|
||
核心贡献:证明了"预训练 + 微调"范式的有效性
|
||
|
||
预训练阶段:在大规模无标注文本上学习语言规律
|
||
微调阶段:在小规模有标注数据上适应特定任务
|
||
\end{lstlisting}
|
||
|
||
\subsection{GPT-2(2019):规模的力量}
|
||
|
||
\begin{lstlisting}
|
||
参数量:1.5B(15亿)
|
||
训练数据:约40GB文本(WebText)
|
||
核心发现:模型足够大时,无需微调就能完成多种任务
|
||
|
||
"零样本学习"(Zero-shot)能力初现:
|
||
直接输入任务描述,模型就能理解并执行
|
||
\end{lstlisting}
|
||
|
||
\subsection{GPT-3(2020):涌现能力}
|
||
|
||
\begin{lstlisting}
|
||
参数量:175B(1750亿)
|
||
训练数据:约570GB文本
|
||
核心突破:少样本学习(Few-shot Learning)
|
||
|
||
无需更新参数,只需给几个例子:
|
||
┌──────────────────────────────────────────────────────┐
|
||
│ Prompt: │
|
||
│ │
|
||
│ 将设计风格翻译为英文: │
|
||
│ 北欧风格 → Nordic style │
|
||
│ 日式风格 → Japanese style │
|
||
│ 工业风格 → │
|
||
│ │
|
||
│ GPT-3 输出:Industrial style │
|
||
└──────────────────────────────────────────────────────┘
|
||
\end{lstlisting}
|
||
|
||
\subsection{ChatGPT(2022):对话能力的飞跃}
|
||
|
||
\begin{lstlisting}
|
||
核心创新:RLHF(基于人类反馈的强化学习)
|
||
意义:让 AI 学会了"对话"
|
||
|
||
GPT-3(之前):
|
||
用户:"帮我设计一个客厅"
|
||
GPT-3:"客厅是家庭活动的中心空间..." ← 像百科全书
|
||
|
||
ChatGPT:
|
||
用户:"帮我设计一个客厅"
|
||
ChatGPT:"很乐意帮忙!请问:
|
||
1. 客厅面积多大?
|
||
2. 预算范围?
|
||
3. 喜欢什么风格?
|
||
我可以根据这些信息给你更具体的建议。"
|
||
← 像真正的设计师在对话
|
||
\end{lstlisting}
|
||
|
||
\subsection{GPT-4(2023):多模态能力}
|
||
|
||
\begin{lstlisting}
|
||
核心突破:不仅能理解文本,还能理解图像
|
||
|
||
输入:一张客厅照片 + "如何改善这个空间?"
|
||
输出:"根据照片分析:
|
||
1. 自然光不足 → 建议增加镜面元素反射光线
|
||
2. 家具尺度偏大 → 建议换用低矮家具增加空间感
|
||
3. 色调单一 → 建议添加暖色软装点缀..."
|
||
|
||
模型规模的飞跃:
|
||
|
||
┌──────────────────────────────────────┐
|
||
│ GPT-1 GPT-2 GPT-3 GPT-4 │
|
||
│ 117M → 1.5B → 175B → ~1.8T │
|
||
│ │
|
||
│ ×13 ×117 ×15,000 ×15,000 │
|
||
│ (相对GPT-1) │
|
||
└──────────────────────────────────────┘
|
||
\end{lstlisting}
|
||
|
||
|
||
\section{训练范式:预训练 → 微调 → 指令调优}
|
||
|
||
大语言模型的训练分为三个阶段:
|
||
|
||
\begin{lstlisting}
|
||
阶段1:预训练(Pre-training)
|
||
目标:学习语言的基础知识
|
||
数据:互联网海量文本(TB级别)
|
||
方法:预测下一个词
|
||
成本:极高(数百万美元)
|
||
|
||
"这个客厅采用了" → "现代简约" ← 正确答案
|
||
|
||
↓
|
||
|
||
阶段2:监督微调(Supervised Fine-Tuning, SFT)
|
||
目标:学会遵循指令
|
||
数据:人工编写的(指令, 回答)对
|
||
方法:继续训练,但用有标注数据
|
||
|
||
指令:"描述一下北欧风格的特点"
|
||
回答:"北欧风格以简洁、自然、功能性为核心..."
|
||
|
||
↓
|
||
|
||
阶段3:对齐优化(Alignment)
|
||
目标:让回答更符合人类偏好
|
||
数据:人类对多个回答的排序
|
||
方法:RLHF / DPO
|
||
|
||
问题:"设计一个50平米的客厅"
|
||
回答A:"以下是设计方案..." ← 较好
|
||
回答B:"我无法帮您..." ← 较差
|
||
人类标注:A > B → 模型学习人类偏好
|
||
\end{lstlisting}
|
||
|
||
\subsection{中国的大语言模型}
|
||
|
||
\begin{lstlisting}
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ 模型名称 开发机构 特点 │
|
||
├─────────────────────────────────────────────────────┤
|
||
│ 文心一言 百度 中文理解能力强 │
|
||
│ 通义千问 阿里巴巴 多模态能力突出 │
|
||
│ 智谱清言 智谱AI 开源生态活跃 │
|
||
│ Kimi 月之暗面 长文本处理能力 │
|
||
│ DeepSeek 深度求索 推理能力突出 │
|
||
│ 讯飞星火 科大讯飞 教育领域深耕 │
|
||
└─────────────────────────────────────────────────────┘
|
||
\end{lstlisting}
|
||
|
||
|
||
\section{LLM 关键技术}
|
||
|
||
\section{RAG:检索增强生成}
|
||
|
||
\subsection{问题:大模型的“幻觉”}
|
||
|
||
LLM 会自信地编造不存在的事实:
|
||
|
||
\begin{lstlisting}
|
||
用户:"北京故宫的建筑面积是多少?"
|
||
LLM(幻觉):"北京故宫的建筑面积约为25万平方米。"
|
||
(实际约15万平方米)
|
||
|
||
原因:LLM 的知识来自训练数据,无法实时更新
|
||
它生成的是"最可能"的答案,而非"最正确"的答案
|
||
\end{lstlisting}
|
||
|
||
\subsection{RAG 架构}
|
||
|
||
RAG(Retrieval-Augmented Generation)通过引入外部知识库来解决幻觉问题:
|
||
|
||
\begin{lstlisting}
|
||
用户提问
|
||
│
|
||
▼
|
||
┌──────────────┐
|
||
│ 1. 检索阶段 │ 在知识库中搜索相关文档
|
||
│ (Retrieve) │
|
||
└──────────────┘
|
||
│
|
||
│ 检索到的相关文档
|
||
▼
|
||
┌──────────────┐
|
||
│ 2. 增强阶段 │ 将问题 + 相关文档拼接为增强提示
|
||
│ (Augment) │
|
||
└──────────────┘
|
||
│
|
||
│ 增强后的 Prompt
|
||
▼
|
||
┌──────────────┐
|
||
│ 3. 生成阶段 │ LLM 基于增强信息生成回答
|
||
│ (Generate) │
|
||
└──────────────┘
|
||
│
|
||
▼
|
||
准确、有据可查的回答
|
||
\end{lstlisting}
|
||
|
||
\subsection{设计领域 RAG 示例}
|
||
|
||
\begin{lstlisting}[language=Python]
|
||
# 概念代码:设计知识库 RAG 系统
|
||
|
||
# 步骤1:建立知识库(将设计规范文档分块并编码)
|
||
documents = [
|
||
"住宅设计规范:客厅净高不应低于2.4米...",
|
||
"建筑采光标准:居住空间采光系数不低于2%...",
|
||
"无障碍设计规范:门道净宽不小于0.8米...",
|
||
# ... 更多设计规范文档
|
||
]
|
||
|
||
# 步骤2:用户提问
|
||
query = "办公空间的人均面积标准是多少?"
|
||
|
||
# 步骤3:检索相关文档
|
||
relevant_docs = retrieve(query, knowledge_base)
|
||
# 返回: ["办公建筑设计标准:普通办公空间人均使用面积不小于6平方米..."]
|
||
|
||
# 步骤4:构建增强提示
|
||
augmented_prompt = f"""
|
||
根据以下参考资料回答问题。如果资料中没有相关信息,请说明。
|
||
|
||
参考资料:
|
||
{relevant_docs}
|
||
|
||
问题:{query}
|
||
"""
|
||
|
||
# 步骤5:LLM 生成回答
|
||
answer = llm.generate(augmented_prompt)
|
||
# 输出: "根据《办公建筑设计标准》,普通办公空间人均使用面积不应小于6平方米。"
|
||
\end{lstlisting}
|
||
|
||
\subsection{RAG 的优势}
|
||
|
||
{\def\LTcaptype{none} % do not increment counter
|
||
\begin{longtable}[]{@{}ll@{}}
|
||
\toprule\noalign{}
|
||
优势 & 说明 \\
|
||
\midrule\noalign{}
|
||
\endhead
|
||
\bottomrule\noalign{}
|
||
\endlastfoot
|
||
减少幻觉 & 回答基于真实文档,有据可查 \\
|
||
知识更新 & 只需更新知识库,无需重新训练模型 \\
|
||
领域适配 & 轻松添加专业设计知识 \\
|
||
可追溯性 & 可以提供答案来源 \\
|
||
\end{longtable}
|
||
}
|
||
|
||
|
||
\section{RLHF:基于人类反馈的强化学习}
|
||
|
||
\subsection{三个训练阶段}
|
||
|
||
\begin{lstlisting}
|
||
阶段1:监督微调(SFT)
|
||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
人类编写示范回答 → 训练模型模仿
|
||
|
||
示例:
|
||
指令:"设计一个50人的办公空间"
|
||
示范回答:"建议采用开放式布局..."
|
||
|
||
阶段2:训练奖励模型(Reward Model)
|
||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
模型生成多个回答 → 人工排序 → 训练打分模型
|
||
|
||
问题:"设计一个50人的办公空间"
|
||
回答A: ████████████ 评分: 8 ← 人工标注
|
||
回答B: ██████████ 评分: 6
|
||
回答C: ██████ 评分: 3
|
||
|
||
→ 训练一个能自动评分的 Reward Model
|
||
|
||
阶段3:强化学习优化(RL)
|
||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
用 PPO 算法让模型生成高分回答
|
||
|
||
模型生成回答 → Reward Model 打分 → 更新模型参数
|
||
重复这个过程,让模型学会生成人类偏好的回答
|
||
\end{lstlisting}
|
||
|
||
\subsection{RLHF 的设计意义}
|
||
|
||
\begin{lstlisting}
|
||
通过 RLHF,大模型学会了:
|
||
|
||
✓ 遵循设计约束(面积、预算、规范)
|
||
✓ 结构化输出(分点描述,层次清晰)
|
||
✓ 适度创意(在合理范围内创新)
|
||
✓ 安全意识(遵守安全规范和伦理标准)
|
||
✗ 避免不切实际的方案
|
||
✗ 避免忽略用户的核心需求
|
||
\end{lstlisting}
|
||
|
||
|
||
\section{Prompt Engineering:提示工程}
|
||
|
||
\subsection{核心原则}
|
||
|
||
Prompt Engineering 是与大语言模型有效沟通的“艺术与科学”:
|
||
|
||
\begin{lstlisting}
|
||
原则一:明确任务
|
||
━━━━━━━━━━━━━━━━
|
||
✗ 模糊:"帮我设计一个空间"
|
||
✓ 明确:"请为一个30平米的客厅设计一个简约风格的家具布局方案"
|
||
|
||
原则二:提供示例(Few-shot)
|
||
━━━━━━━━━━━━━━━━
|
||
在提示中给出1-3个示例,让模型理解期望的输出格式
|
||
|
||
原则三:指定格式
|
||
━━━━━━━━━━━━━━━━
|
||
"请按以下格式输出:
|
||
1. 设计理念(50字以内)
|
||
2. 空间布局(分点描述)
|
||
3. 材料建议(列表形式)
|
||
4. 预估预算(表格形式)"
|
||
|
||
原则四:角色设定
|
||
━━━━━━━━━━━━━━━━
|
||
"你是一位有15年经验的室内设计师,擅长小户型改造..."
|
||
\end{lstlisting}
|
||
|
||
\subsection{设计应用示例}
|
||
|
||
\begin{lstlisting}
|
||
Prompt: 设计一个50人的办公空间
|
||
|
||
角色设定:
|
||
你是一位资深办公空间设计师,熟悉人体工程学和现代办公理念。
|
||
|
||
任务要求:
|
||
- 面积:约200平方米
|
||
- 人数:50人
|
||
- 类型:科技创业公司
|
||
- 风格:现代简约
|
||
- 需要考虑:开放工位、会议室、休息区、茶水间
|
||
|
||
请按以下结构输出:
|
||
1. 空间规划方案
|
||
2. 动线设计要点
|
||
3. 家具选型建议
|
||
4. 照明设计方案
|
||
5. 预估造价
|
||
|
||
---
|
||
|
||
LLM 输出(示例):
|
||
|
||
1. 空间规划方案
|
||
├── 开放工位区(120㎡):40个工位,2人/组
|
||
├── 独立办公区(20㎡):2间,各容纳5人
|
||
├── 会议室(30㎡):1间大会议室 + 2间小会议室
|
||
├── 休息区(15㎡):沙发、茶水台
|
||
└── 茶水间(15㎡):吧台、储物柜
|
||
|
||
2. 动线设计要点
|
||
├── 主通道宽度 ≥ 1.2m
|
||
├── 入口直通开放区,减少干扰
|
||
└── 会议室靠近入口,方便访客
|
||
...
|
||
\end{lstlisting}
|
||
|
||
\subsection{Prompt 设计模式}
|
||
|
||
\begin{lstlisting}
|
||
┌─────────────────────────────────────────────────┐
|
||
│ 模式 适用场景 示例 │
|
||
├─────────────────────────────────────────────────┤
|
||
│ Zero-shot 简单任务 "翻译:你好" │
|
||
│ Few-shot 格式化输出 给2-3个范例 │
|
||
│ Chain-of 复杂推理 "让我们一步步 │
|
||
│ Thought 来思考..." │
|
||
│ Role-play 专业领域 "你是一名建筑 │
|
||
│ 设计师..." │
|
||
│ Template 结构化输出 固定输出模板 │
|
||
└─────────────────────────────────────────────────┘
|
||
\end{lstlisting}
|
||
|
||
|
||
\section{设计应用}
|
||
|
||
\section{应用一:设计文档文本分类}
|
||
|
||
\begin{lstlisting}
|
||
场景:设计公司收到大量项目文档,需要自动分类归档
|
||
|
||
输入文档 → 文本分类模型 → 类别标签
|
||
"本项目采用新中式园林风格..." → "景观设计"
|
||
"室内空间以简约风格为主..." → "室内设计"
|
||
"建筑主体采用框架剪力墙..." → "建筑设计"
|
||
\end{lstlisting}
|
||
|
||
\begin{lstlisting}[language=Python]
|
||
from transformers import AutoModelForSequenceClassification, AutoTokenizer
|
||
import torch
|
||
|
||
# 加载预训练的中文文本分类模型
|
||
model_name = "bert-base-chinese"
|
||
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
||
model = AutoModelForSequenceClassification.from_pretrained(
|
||
model_name, num_labels=4 # 4个设计类别
|
||
)
|
||
|
||
# 分类推理
|
||
text = "本方案采用开放式布局,以白色和原木色为主色调"
|
||
inputs = tokenizer(text, return_tensors="pt", padding=True, truncation=True)
|
||
outputs = model(**inputs)
|
||
predicted_class = torch.argmax(outputs.logits, dim=1)
|
||
|
||
categories = ["建筑设计", "景观设计", "室内设计", "平面设计"]
|
||
print(f"分类结果: {categories[predicted_class]}")
|
||
# 输出: 分类结果: 室内设计
|
||
\end{lstlisting}
|
||
|
||
\section{应用二:用户评价情感分析}
|
||
|
||
\begin{lstlisting}
|
||
场景:分析用户对设计方案的反馈
|
||
|
||
"新方案的空间利用很合理,采光也不错" → 正面
|
||
"走廊太窄了,家具颜色也不搭配" → 负面
|
||
"基本满足需求,但细节还有改进空间" → 中性
|
||
\end{lstlisting}
|
||
|
||
\begin{lstlisting}[language=Python]
|
||
# 使用预训练模型进行情感分析
|
||
from transformers import pipeline
|
||
|
||
sentiment_analyzer = pipeline(
|
||
"sentiment-analysis",
|
||
model="uer/roberta-base-finetuned-jd-binary-chinese"
|
||
)
|
||
|
||
# 批量分析设计评价
|
||
reviews = [
|
||
"这个设计方案很有创意,空间利用率很高",
|
||
"配色方案不太协调,建议调整",
|
||
"整体还不错,就是预算偏高"
|
||
]
|
||
|
||
for review in reviews:
|
||
result = sentiment_analyzer(review)
|
||
print(f"评价: {review}")
|
||
print(f"情感: {result[0]['label']} (置信度: {result[0]['score']:.2%})")
|
||
print()
|
||
\end{lstlisting}
|
||
|
||
\section{应用三:设计描述自动生成}
|
||
|
||
\begin{lstlisting}
|
||
场景:根据设计参数自动生成方案描述文本
|
||
|
||
输入参数:
|
||
- 空间类型:客厅
|
||
- 面积:35平方米
|
||
- 风格:现代简约
|
||
- 主色:白色 + 原木色
|
||
- 需求:家庭聚会、阅读角
|
||
|
||
生成描述:
|
||
"本方案为35平方米的客厅空间设计,采用现代简约风格。
|
||
空间以白色和原木色为主色调,营造明亮温馨的氛围。
|
||
布局上,沙发区面向景观窗,可满足家庭聚会需求;
|
||
窗边设置阅读角,配备落地灯和单人沙发。
|
||
整体动线流畅,兼顾功能性与美感。"
|
||
\end{lstlisting}
|
||
|
||
\section{应用四:对话式设计助手}
|
||
|
||
\begin{lstlisting}
|
||
场景:设计师通过对话形式获取设计建议
|
||
|
||
┌──────────────────────────────────────────────────────┐
|
||
│ 设计师:我想为一个80平米的小户型做室内设计, │
|
||
│ 业主要求现代简约风格,预算15万。 │
|
||
│ │
|
||
│ AI助手:好的,我来帮您规划。80平米做现代简约是 │
|
||
│ 很好的选择。我有几个问题: │
|
||
│ │
|
||
│ 1. 户型是几室几厅? │
|
||
│ 2. 家庭成员构成?(是否有老人/儿童) │
|
||
│ 3. 有没有特别的空间需求?(书房/衣帽间等) │
|
||
│ │
|
||
│ 设计师:两室一厅,一家三口,孩子5岁,需要一个小书房 │
|
||
│ │
|
||
│ AI助手:明白了。以下是我的初步建议: │
|
||
│ │
|
||
│ 【空间规划】 │
|
||
│ - 主卧:约15㎡(含衣柜区) │
|
||
│ - 儿童房:约10㎡(考虑成长性设计) │
|
||
│ - 客厅+餐厅:约25㎡(开放式布局) │
|
||
│ - 书房:约6㎡(可做半开放式) │
|
||
│ - 厨卫:约12㎡ │
|
||
│ - 阳台:约6㎡(兼顾洗衣和休闲) │
|
||
│ ... │
|
||
└──────────────────────────────────────────────────────┘
|
||
\end{lstlisting}
|
||
|
||
\subsection{技术实现思路}
|
||
|
||
\begin{lstlisting}[language=Python]
|
||
# 对话式设计助手的基本架构
|
||
|
||
class DesignAssistant:
|
||
def __init__(self, llm, knowledge_base):
|
||
self.llm = llm # 大语言模型
|
||
self.knowledge_base = knowledge_base # 设计知识库
|
||
self.conversation_history = []
|
||
|
||
def chat(self, user_input):
|
||
# 1. 从知识库检索相关设计规范
|
||
relevant_docs = self.knowledge_base.retrieve(user_input)
|
||
|
||
# 2. 构建增强提示
|
||
system_prompt = """
|
||
你是一位专业的室内设计顾问。请根据用户需求和
|
||
参考的设计规范,提供专业、实用的设计建议。
|
||
回答要有条理,必要时提出追问以明确需求。
|
||
"""
|
||
|
||
# 3. 组装完整提示
|
||
messages = [
|
||
{"role": "system", "content": system_prompt},
|
||
*self.conversation_history,
|
||
{"role": "user", "content": f"参考资料:{relevant_docs}\n\n用户问题:{user_input}"}
|
||
]
|
||
|
||
# 4. 调用 LLM 生成回答
|
||
response = self.llm.chat(messages)
|
||
|
||
# 5. 更新对话历史
|
||
self.conversation_history.append({"role": "user", "content": user_input})
|
||
self.conversation_history.append({"role": "assistant", "content": response})
|
||
|
||
return response
|
||
\end{lstlisting}
|
||
|
||
|
||
\section{思考与练习}
|
||
|
||
\begin{enumerate}
|
||
\def\labelenumi{\arabic{enumi}.}
|
||
\item
|
||
\textbf{模型选择}:在设计项目文档分类任务中,你会选择 BERT 还是 GPT 架构?为什么?考虑模型规模、训练成本、任务特点等因素。
|
||
\item
|
||
\textbf{RAG vs 微调}:你正在为一家建筑设计院开发 AI 设计助手。设计规范经常更新,且需要提供条文来源。你会选择 RAG 还是模型微调?请分析两种方案的优劣。
|
||
\item
|
||
\textbf{Prompt 设计}:尝试设计一个高质量的 Prompt,让大语言模型帮助分析一份景观设计方案的优缺点。你的 Prompt 中应该包含哪些要素?考虑角色设定、输出格式、评估维度等。
|
||
\end{enumerate}
|
||
|
||
|
||
\section{关键术语}
|
||
|
||
{\def\LTcaptype{none} % do not increment counter
|
||
\begin{longtable}[]{@{}
|
||
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.3333}}
|
||
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.3333}}
|
||
>{\raggedright\arraybackslash}p{(\linewidth - 4\tabcolsep) * \real{0.3333}}@{}}
|
||
\toprule\noalign{}
|
||
\begin{minipage}[b]{\linewidth}\raggedright
|
||
中文
|
||
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
|
||
英文
|
||
\end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright
|
||
说明
|
||
\end{minipage} \\
|
||
\midrule\noalign{}
|
||
\endhead
|
||
\bottomrule\noalign{}
|
||
\endlastfoot
|
||
循环神经网络 & RNN (Recurrent Neural Network) & 处理序列数据的基础网络结构 \\
|
||
长短期记忆网络 & LSTM (Long Short-Term Memory) & 解决 RNN 梯度消失问题的改进结构 \\
|
||
门控循环单元 & GRU (Gated Recurrent Unit) & LSTM 的简化版本,参数更少 \\
|
||
自注意力机制 & Self-Attention & 序列内部元素之间直接计算关联的机制 \\
|
||
缩放点积注意力 & Scaled Dot-Product Attention & 带缩放的注意力计算公式 \\
|
||
多头注意力 & Multi-Head Attention & 多组并行的注意力,捕获多种关系模式 \\
|
||
位置编码 & Positional Encoding & 为序列中的位置信息编码的方法 \\
|
||
Transformer & Transformer & 基于注意力机制的序列模型架构 \\
|
||
掩码注意力 & Masked Attention & 防止解码器看到未来信息的注意力机制 \\
|
||
大语言模型 & LLM (Large Language Model) & 参数量巨大的预训练语言模型 \\
|
||
检索增强生成 & RAG (Retrieval-Augmented Generation) & 结合外部知识库的生成方法 \\
|
||
人类反馈强化学习 & RLHF (Reinforcement Learning from Human Feedback) & 基于人类偏好优化模型的方法 \\
|
||
提示工程 & Prompt Engineering & 设计有效提示以引导模型输出的技术 \\
|
||
分词 & Tokenization & 将文本拆分为最小处理单元的过程 \\
|
||
词嵌入 & Embedding & 将离散的词映射为连续向量表示 \\
|
||
预训练 & Pre-training & 在大规模数据上的初始训练阶段 \\
|
||
微调 & Fine-tuning & 在特定任务数据上的调整训练 \\
|
||
自回归 & Autoregressive & 逐步生成,每步依赖之前输出 \\
|
||
幻觉 & Hallucination & 模型生成看似合理但实际错误的内容 \\
|
||
缩放定律 & Scaling Law & 模型性能与规模之间的关系规律 \\
|
||
\end{longtable}
|
||
}
|
||
|
||
|
||
\section{延伸阅读}
|
||
|
||
\begin{itemize}
|
||
\tightlist
|
||
\item
|
||
\href{https://arxiv.org/abs/1706.03762}{Attention Is All You Need} --- Transformer 原始论文
|
||
\item
|
||
\href{https://jalammar.github.io/illustrated-transformer/}{The Illustrated Transformer} --- Transformer 可视化解读
|
||
\item
|
||
\href{https://arxiv.org/abs/1810.04805}{BERT: Pre-training of Deep Bidirectional Transformers} --- BERT 论文
|
||
\item
|
||
\href{https://arxiv.org/abs/2005.14165}{Language Models are Few-Shot Learners (GPT-3)} --- GPT-3 论文
|
||
\item
|
||
\href{https://arxiv.org/abs/2203.02155}{Training language models to follow instructions with human feedback} --- InstructGPT/RLHF 论文
|
||
\item
|
||
\href{https://arxiv.org/abs/2005.11401}{Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks} --- RAG 原始论文
|
||
\end{itemize}
|