\documentclass[a4paper]{article}

% Basic packages
\usepackage[fontset=fandol]{ctex}
\usepackage{amsmath, amssymb}
\usepackage{graphicx}
\usepackage[margin=2.5cm]{geometry}
\usepackage[most]{tcolorbox}
\usepackage{etoolbox}
\usepackage{listings}
\usepackage{hyperref}
\usepackage{booktabs}
\usepackage{subcaption}
\usepackage{float}
\usepackage{tikz}
\usetikzlibrary{positioning, arrows.meta}
\IfFileExists{pgfplots.sty}{
    \usepackage{pgfplots}
    \pgfplotsset{compat=1.18}
}{}

% Highlight boxes
\newtcolorbox{knowledgebox}[1]{
    enhanced, colback=blue!5!white, colframe=blue!75!black, colbacktitle=blue!75!black,
    coltitle=white, fonttitle=\bfseries, title=#1, attach boxed title to top left={yshift=-2mm, xshift=2mm},
    boxrule=1pt, sharp corners
}

\newtcolorbox{importantbox}[1]{
    enhanced, colback=yellow!10!white, colframe=yellow!80!black, colbacktitle=yellow!80!black,
    coltitle=black, fonttitle=\bfseries, title=#1, sharp corners
}

\newtcolorbox{warningbox}[1]{
    enhanced, colback=red!5!white, colframe=red!75!black, colbacktitle=red!75!black,
    coltitle=white, fonttitle=\bfseries, title=#1, sharp corners
}

% Listings
\lstset{
    language=Python,
    basicstyle=\ttfamily\small,
    keywordstyle=\color{blue},
    stringstyle=\color{red!60!black},
    commentstyle=\color{green!60!black},
    breaklines=true,
    frame=single,
    numbers=left,
    numberstyle=\tiny\color{gray},
    captionpos=b,
    extendedchars=false
}

% Front-page metadata
\newcommand{\notetitle}{CS224N Lecture 5: Recurrent Neural Networks}
\newcommand{\noteauthors}{基于 Chris Manning 授课内容整理}
\newcommand{\notedate}{2024年}
\newcommand{\videochannel}{Stanford Online}
\newcommand{\videopublishdate}{2024}
\newcommand{\videoduration}{1:18:48}
\newcommand{\videourl}{https://www.youtube.com/watch?v=fyc0Jzr74y4}
\newcommand{\videocoverpath}{cover.jpg}
\newcommand{\repourl}{https://github.com/hqhq1025/ai-course-notes}


% Footer with repo info
\usepackage{fancyhdr}
\pagestyle{fancy}
\fancyhf{}
\fancyfoot[C]{\thepage}
\fancyfoot[R]{\footnotesize\color{gray}\href{\repourl}{AI Course Notes}}
\renewcommand{\headrulewidth}{0pt}
\renewcommand{\footrulewidth}{0pt}

\begin{document}

\begin{titlepage}
\centering
{\Large 课程笔记\par}
\vspace{1.2cm}
{\huge\bfseries \notetitle\par}
\vspace{0.8cm}
{\large \noteauthors\par}
\vspace{0.3cm}
{\large \notedate\par}
\vspace{1.2cm}

\ifdefempty{\videocoverpath}{
{\small 请在生成笔记时填入视频封面图的本地路径。\par}
}{
\includegraphics[width=0.82\textwidth,height=0.45\textheight,keepaspectratio]{\videocoverpath}\par
}

\vfill
\begin{tcolorbox}[width=0.9\textwidth, colback=black!2!white, colframe=black!60, sharp corners]
\textbf{视频作者/频道}：\videochannel\par
\textbf{发布日期}：\videopublishdate\par
\textbf{视频时长}：\videoduration\par
\ifdefempty{\videourl}{}{
\textbf{视频链接}：\href{\videourl}{\nolinkurl{\videourl}}\par
}
\par
\textbf{项目主页}：\href{\repourl}{\nolinkurl{\repourl}}\par
\end{tcolorbox}
\end{titlepage}

\tableofcontents
\newpage

%% ========== Part 1: 深度学习基础回顾 ==========

\section{引言与深度学习基础回顾}

本节课是 Stanford CS224N 的第五讲，由 Chris Manning 教授主讲。课程内容分为三个主要部分：（1）深度学习实用技巧的补充（正则化、参数初始化、优化器等）；（2）语言模型（Language Models）的概念与构建方法；（3）循环神经网络（Recurrent Neural Networks, RNN）的原理与应用。

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-000.jpg}
\caption{CS224N Lecture 5 封面：Language Models and Recurrent Neural Networks\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第1页。}

\subsection{从浅层到深层：深度学习的崛起}

Manning 教授首先回顾了神经网络的发展历史。在 1980--1990 年代，虽然反向传播算法已经被 Hinton 等人推广，但当时几乎所有的神经网络都只有\textbf{一个隐藏层}。原因在于，人们长期无法让深层网络比浅层网络表现更好。

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-005.jpg}
\caption{Bengio et al. (2006) 指出：训练深层神经网络曾被认为过于困难，深层网络的表现通常不如一两个隐藏层的浅层网络\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第6页。}

\begin{knowledgebox}{深度学习的历史转折}
直到 2006 年前后，Yoshua Bengio 等人提出逐层贪心预训练（Greedy Layer-Wise Training）等方法，深度网络才开始逐步展现出优于浅层网络的能力。Manning 指出，推动这一转变的并非某个惊天发现，而是若干``小想法''和``小技巧''的积累——正则化方法、激活函数选择、参数初始化策略、优化器改进等。这些看似不起眼的改进，让一个停滞了 15 年的领域焕发新生。
\end{knowledgebox}

\subsection{正则化：从经典到现代视角}

\subsubsection{经典正则化}

正则化（Regularization）是通过在损失函数中添加额外约束来防止过拟合的技术。最常见的形式是 L2 正则化：

$$J(\theta) = J_{\text{data}}(\theta) + \lambda \sum_i \theta_i^2$$

\begin{itemize}
    \item $J_{\text{data}}(\theta)$：数据拟合项（如交叉熵损失）
    \item $\lambda \sum_i \theta_i^2$：正则化项，鼓励参数保持较小的值
    \item $\lambda$：正则化强度超参数
\end{itemize}

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-003.jpg}
\caption{经典的过拟合视角：训练误差持续下降，但验证误差在某一点后开始上升\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第4页。}

\subsubsection{现代视角：Double Descent}

Manning 特别强调，现代大规模神经网络的行为与经典理论\textbf{截然不同}。在足够大的网络中，即使模型完全记住了训练数据（训练损失趋近于零），验证损失仍然会继续下降。

\begin{importantbox}{现代正则化的范式转变}
现代大型神经网络可以将训练数据几乎完美拟合（训练损失接近零），这在经典统计学看来是灾难性的过拟合。但实际上，只要正则化做得好，模型仍然能很好地泛化到新数据。这一发现被称为``Double Descent''现象，彻底改变了我们对模型复杂度和泛化的理解。
\end{importantbox}

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-004.jpg}
\caption{现代视角：大型网络中，验证损失先升后降，最终随着训练继续而改善\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第5页。}

\subsubsection{Dropout：现代神经网络的标配正则化}

传统的 L2/L1 正则化对大型神经网络往往不够强。Manning 介绍了 \textbf{Dropout}——一种专为神经网络设计的正则化方法。

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-006.jpg}
\caption{Dropout 示意图：训练时随机将部分神经元置零，测试时使用全部神经元但进行缩放\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第7页。}

\begin{importantbox}{Dropout 的核心机制}
\begin{enumerate}
    \item \textbf{训练时}：对每个训练样本，随机生成一个由 0 和 1 组成的掩码（mask），通过 Hadamard 积将部分神经元的输出置零。每次使用不同的掩码。
    \item \textbf{测试时}：不进行 Dropout，保留所有权重，但对输出进行缩放以补偿训练时的丢弃。
\end{enumerate}
\end{importantbox}

Dropout 为什么有效？Manning 给出了三种直觉解释：

\begin{itemize}
    \item \textbf{防止特征共适应}（Feature Co-adaptation）：模型不能过度依赖某几个特征的组合，因为它们随时可能被删除
    \item \textbf{隐式模型集成}：每次 Dropout 相当于训练一个不同的子网络，最终效果类似于指数级数量的模型集成
    \item \textbf{介于 Naive Bayes 和 Logistic Regression 之间}：Naive Bayes 独立地评估每个特征，Logistic Regression 在所有特征的上下文中设置权重，Dropout 则在随机子集的上下文中学习，提供了一种中间地带
\end{itemize}

\subsection{向量化：深度学习的性能基石}

\begin{warningbox}{永远不要写 for 循环}
深度学习的成功很大程度上依赖于向量化计算。在 Python 中，使用 for 循环逐元素处理数据比向量/矩阵操作慢一到两个数量级。在 GPU 上，这个差距更是达到两到三个数量级。如果你发现自己在写 for 循环处理数据（而不是最表层的输入预处理），几乎一定是做错了。
\end{warningbox}

\subsection{参数初始化}

神经网络的参数初始化至关重要。如果将所有参数初始化为零或同一常数，网络会陷入\textbf{对称性陷阱}——所有神经元学到的东西完全相同，相当于只有一个神经元。

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-010.jpg}
\caption{参数初始化：必须使用小的随机数初始化权重，避免对称性问题。Xavier 初始化根据层的输入输出维度自适应设置方差\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第11页。}

\begin{importantbox}{Xavier 初始化}
Xavier/Glorot 初始化设置权重的方差为：
$$\text{Var}(W_i) = \frac{2}{n_{\text{in}} + n_{\text{out}}}$$
其中 $n_{\text{in}}$ 是前一层的大小，$n_{\text{out}}$ 是后一层的大小。这确保信号在前向和反向传播过程中既不会消失也不会爆炸。Manning 指出，后来 Layer Normalization 的引入在很大程度上消除了对精确初始化的需求。
\end{importantbox}

\subsection{优化器：从 SGD 到 Adam}

Manning 简要介绍了优化器的发展：

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-011.jpg}
\caption{各种优化器及其关系\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第12页。}

\begin{itemize}
    \item \textbf{SGD}（随机梯度下降）：最基础的优化器，理论上能解决几乎所有问题，但对学习率和调度非常敏感
    \item \textbf{Adagrad}：为每个参数维护历史梯度平方和，自适应调整学习率。缺点是学习率会过早衰减
    \item \textbf{Adam}：结合动量和自适应学习率，是最常用的默认选择
\end{itemize}

\begin{knowledgebox}{如果只记住一个优化器}
Adam 是最安全的默认选择。它对超参数不敏感，几乎在所有场景下都能给出不错的结果。对于词向量等稀疏更新场景，带有权重衰减的变体（AdamW）可能更合适。
\end{knowledgebox}

\subsection{本章小结}

\begin{itemize}
    \item 深度学习的成功建立在若干关键技巧的积累之上：Dropout 正则化、合理的参数初始化、自适应优化器
    \item 现代大型网络的过拟合行为与经典理论不同——Double Descent 现象表明，足够大的模型即使完美拟合训练数据仍能良好泛化
    \item 向量化计算是深度学习效率的基石，应避免在数据处理中使用 for 循环
    \item Adam 优化器是实践中最可靠的默认选择
\end{itemize}

%% ========== Part 2: 语言模型 ==========

\section{语言模型}

\subsection{什么是语言模型}

\begin{importantbox}{语言模型的定义}
\textbf{语言模型}（Language Model, LM）是一个能够对文本序列中下一个词的出现概率进行预测的系统。形式化地，给定前文 $x^{(1)}, x^{(2)}, \ldots, x^{(t)}$，语言模型输出下一个词的条件概率分布：
$$P(x^{(t+1)} \mid x^{(1)}, x^{(2)}, \ldots, x^{(t)})$$
该概率分布满足 $\sum_{w \in V} P(w \mid x^{(1)}, \ldots, x^{(t)}) = 1$，其中 $V$ 是词汇表。
\end{importantbox}

等价地，语言模型可以为任意一段文本 $x^{(1)}, \ldots, x^{(T)}$ 赋予一个概率。通过链式法则：

$$P(x^{(1)}, \ldots, x^{(T)}) = P(x^{(1)}) \cdot P(x^{(2)} \mid x^{(1)}) \cdot P(x^{(3)} \mid x^{(1)}, x^{(2)}) \cdots P(x^{(T)} \mid x^{(1)}, \ldots, x^{(T-1)})$$

$$= \prod_{t=1}^{T} P(x^{(t)} \mid x^{(1)}, \ldots, x^{(t-1)})$$

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-013.jpg}
\caption{语言模型的基本任务：给定前文``the students opened their''，预测下一个词的概率分布\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第14页。}

\subsection{语言模型无处不在}

Manning 强调，语言模型并非 ChatGPT 时代的发明。它的历史可以追溯到 1950 年代 Claude Shannon 的信息论工作，至少从 1980 年代起就已经是 NLP 的核心技术。

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-015.jpg}
\caption{日常生活中的语言模型：手机输入法的下一词预测、Google 搜索的自动补全都是语言模型的应用\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第16页。}

\subsection{N-gram 语言模型}

在神经网络语言模型出现之前（约 1975--2012 年），\textbf{N-gram 语言模型}是主流方法。

\subsubsection{基本思想}

N-gram 模型做了一个\textbf{马尔可夫假设}（Markov Assumption）：预测下一个词时，只使用前 $n-1$ 个词作为上下文，丢弃更早的信息。

$$P(x^{(t+1)} \mid x^{(1)}, \ldots, x^{(t)}) \approx P(x^{(t+1)} \mid x^{(t-n+2)}, \ldots, x^{(t)})$$

概率估计通过计数得到：

$$P(w \mid \text{students opened their}) = \frac{\text{count}(\text{students opened their } w)}{\text{count}(\text{students opened their})}$$

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-017.jpg}
\caption{4-gram 语言模型示例：基于``students opened their''预测下一个词，books 的概率为 0.4，exams 为 0.1\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第18页。}

\subsubsection{N-gram 模型的问题}

\begin{warningbox}{N-gram 模型的两大致命缺陷}
\begin{enumerate}
    \item \textbf{稀疏性问题}（Sparsity）：很多合理的 n-gram 组合在训练语料中从未出现过，导致概率估计为零。而概率为零是灾难性的——任何涉及该 n-gram 的计算都会变为零。解决方法包括加法平滑（Laplace Smoothing）和回退（Backoff）策略。
    \item \textbf{存储问题}（Storage）：需要存储语料中所有观察到的 n-gram 的计数。增大 $n$ 或增大语料规模都会使存储需求指数级增长。实践中 $n$ 通常不超过 5。
\end{enumerate}
\end{warningbox}

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-020.jpg}
\caption{N-gram 模型的存储问题：增大 $n$ 或增大语料都会增加模型大小\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第21页。}

\subsubsection{用 N-gram 模型生成文本}

尽管 N-gram 模型很``粗糙''，但它已经能生成看起来有点像样的文本。Manning 展示了用 trigram 模型在 200 万词语料上生成的文本：

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-025.jpg}
\caption{Trigram 语言模型生成的文本：语法大致正确（surprisingly grammatical），但语义不连贯（incoherent）\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第26页。}

\begin{knowledgebox}{Scale 的故事古已有之}
Manning 有一个精彩的观察：今天人们说的``更多数据、更大模型就能解决一切''，与 2010 年代初人们对 N-gram 模型的态度如出一辙——当时人们也在说，只要有更大的语料和更高阶的 N-gram，性能就会持续提升。但事实证明，\textbf{更好的模型架构}同样重要。
\end{knowledgebox}

\subsection{固定窗口神经语言模型}

N-gram 模型的自然演进是使用神经网络来替代简单的计数。Yoshua Bengio 在 2003 年提出了\textbf{固定窗口神经语言模型}（Fixed-Window Neural Language Model）。

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-028.jpg}
\caption{固定窗口神经语言模型：将前 $n$ 个词的词嵌入拼接，通过隐藏层和 softmax 预测下一个词\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第29页。}

该模型的工作流程如下：

\begin{enumerate}
    \item 将窗口内的每个词通过词嵌入矩阵 $E$ 转换为向量 $e^{(i)}$
    \item 将所有词嵌入拼接为一个长向量
    \item 通过隐藏层：$h = f(W \cdot [e^{(1)}; e^{(2)}; \ldots; e^{(n)}] + b_1)$
    \item 通过输出层和 softmax：$\hat{y} = \text{softmax}(U \cdot h + b_2)$
\end{enumerate}

\begin{importantbox}{固定窗口模型的优势与局限}
\textbf{优势}：
\begin{itemize}
    \item 无稀疏性问题——不需要存储所有 n-gram 计数
    \item 存储成本更低——只需存储网络参数
    \item 通过词嵌入实现了一定的泛化能力
\end{itemize}
\textbf{局限}：
\begin{itemize}
    \item 仍然使用固定窗口，仍有马尔可夫假设的限制
    \item 不同位置的词使用权重矩阵 $W$ 的不同部分处理，没有参数共享——同一个词出现在不同位置时，学到的知识无法迁移
    \item 窗口大小的选择是个难题——没有任何固定窗口是``足够大''的
\end{itemize}
\end{importantbox}

\subsection{本章小结}

\begin{itemize}
    \item 语言模型是预测下一个词的概率分布的系统，是 NLP 的核心技术
    \item N-gram 语言模型通过计数和马尔可夫假设工作，简单但受限于稀疏性和存储问题
    \item 固定窗口神经语言模型解决了稀疏性问题，但仍受限于固定上下文长度和缺乏参数共享
    \item 我们需要一种能处理\textbf{任意长度}输入且\textbf{共享参数}的新架构——这就引出了循环神经网络
\end{itemize}

%% ========== Part 3: 循环神经网络 ==========

\section{循环神经网络（RNN）}

\subsection{RNN 的核心思想}

循环神经网络（Recurrent Neural Network, RNN）是一种专为处理序列数据设计的神经网络架构。其核心思想是：\textbf{在每个时间步应用相同的权重矩阵}，并维护一个\textbf{隐藏状态}（hidden state）来编码已处理过的所有历史信息。

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-030.jpg}
\caption{RNN 的核心思想：同一组权重 $W$ 在每个时间步重复应用。输入序列可以是任意长度\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第31页。}

\subsection{RNN 的数学定义}

在每个时间步 $t$，RNN 执行以下计算：

$$h^{(t)} = \tanh(W_h \cdot h^{(t-1)} + W_e \cdot e^{(t)} + b)$$

\begin{itemize}
    \item $h^{(t)} \in \mathbb{R}^{d_h}$：时间步 $t$ 的隐藏状态
    \item $h^{(t-1)} \in \mathbb{R}^{d_h}$：前一时间步的隐藏状态
    \item $W_h \in \mathbb{R}^{d_h \times d_h}$：隐藏状态到隐藏状态的权重矩阵
    \item $e^{(t)} \in \mathbb{R}^{d_e}$：时间步 $t$ 的词嵌入
    \item $W_e \in \mathbb{R}^{d_h \times d_e}$：输入到隐藏状态的权重矩阵
    \item $b \in \mathbb{R}^{d_h}$：偏置项
    \item $\tanh$：非线性激活函数（RNN 中最常用的选择）
\end{itemize}

初始隐藏状态 $h^{(0)}$ 通常设为全零向量。

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-032.jpg}
\caption{RNN 作为语言模型的完整架构：词经过嵌入层 $E$，通过 $W_e$ 和 $W_h$ 更新隐藏状态，最后通过 $U$ 和 softmax 预测下一个词\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第33页。}

要将 RNN 用作语言模型，需要在每个时间步将隐藏状态映射到词汇表上的概率分布：

$$\hat{y}^{(t)} = \text{softmax}(U \cdot h^{(t)} + b_2)$$

\begin{importantbox}{RNN 的关键特性}
\begin{enumerate}
    \item \textbf{可处理任意长度的输入}：隐藏状态可以无限累积信息，不受固定窗口限制
    \item \textbf{参数共享}：$W_h$ 和 $W_e$ 在每个时间步都相同——无论``student''出现在序列的什么位置，都会被同一套参数处理
    \item \textbf{模型大小不随上下文增长}：无论输入多长，参数数量固定。只是计算量与序列长度成正比
\end{enumerate}
\end{importantbox}

\begin{knowledgebox}{为什么用 $\tanh$ 而不是 ReLU}
传统 RNN 中通常使用 $\tanh$ 作为激活函数，而非在前馈网络中更常见的 ReLU。$\tanh$ 的输出范围在 $[-1, 1]$，这在正负两侧是平衡的，有助于隐藏状态在多次递推后保持数值稳定。如果使用 ReLU（输出非负），隐藏状态可能会不断累加正值而爆炸。
\end{knowledgebox}

\subsection{训练 RNN 语言模型}

\subsubsection{损失函数}

RNN 语言模型的训练目标是最小化每个时间步预测的交叉熵损失：

$$J^{(t)}(\theta) = -\log P(x^{(t+1)} \mid x^{(1)}, \ldots, x^{(t)}) = -\log \hat{y}^{(t)}_{x^{(t+1)}}$$

总体损失是所有时间步损失的平均：

$$J(\theta) = \frac{1}{T} \sum_{t=1}^{T} J^{(t)}(\theta)$$

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-035.jpg}
\caption{RNN 语言模型的训练过程：在每个时间步计算预测概率分布与实际下一个词之间的交叉熵损失\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第36页。}

\subsubsection{Teacher Forcing}

训练时采用\textbf{Teacher Forcing}策略：在每个时间步，无论模型预测了什么词，都将\textbf{实际的下一个词}（来自训练语料）作为下一时间步的输入。

\begin{knowledgebox}{Teacher Forcing vs 自由生成}
\textbf{Teacher Forcing}：训练时每步都``纠正''输入，使用真实的下一个词。优点是训练稳定高效；缺点是模型从未练习过从自己的错误中恢复。

\textbf{自由生成}（Free Generation/Rollout）：只在推理时使用——将模型自己生成的词作为下一步的输入，持续生成直到产生终止符 $\langle/s\rangle$。这也是 ChatGPT 等系统生成回复的方式。
\end{knowledgebox}

\subsubsection{实际训练中的分段处理}

理论上可以在整个语料上运行 RNN，但实际上这不可行——需要存储的中间状态太多。实际做法是将文本切分为固定长度的片段（如 100 个词），对每个片段独立计算损失和梯度。

\begin{warningbox}{分段引入了隐式马尔可夫假设}
虽然 RNN 理论上能处理任意长度的上下文，但分段训练意味着模型实际上只能利用片段长度以内的上下文。Manning 指出，这在某种程度上又回到了马尔可夫假设——只是窗口比 N-gram 大得多（100 词 vs 3--5 词）。现代大语言模型通过使用更长的上下文窗口（数千个 token）来缓解这一限制。
\end{warningbox}

\subsection{反向传播：BPTT}

\subsubsection{重复权重的梯度}

RNN 的一个关键特点是权重矩阵 $W_h$ 在每个时间步都被使用。那么如何计算损失对 $W_h$ 的梯度？

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-040.jpg}
\caption{RNN 的反向传播：重复权重 $W_h$ 的梯度等于它在每个时间步出现时的梯度之和\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第41页。}

$$\frac{\partial J^{(t)}}{\partial W_h} = \sum_{i=1}^{t} \left. \frac{\partial J^{(t)}}{\partial W_h} \right|_{(i)}$$

\begin{importantbox}{重复权重的梯度法则}
对于重复使用的权重，其梯度等于该权重在每个位置出现时的梯度之和。数学上，这可以理解为：将 $W_h$ 在每个时间步``虚拟地''视为不同的参数 $W_h^{(1)}, W_h^{(2)}, \ldots$，分别计算梯度，然后求和。这遵循多变量链式法则中的分支求和规则。
\end{importantbox}

\subsubsection{截断反向传播}

完整的反向传播通过时间（Backpropagation Through Time, BPTT）需要展开整个序列，计算代价很高。\textbf{截断 BPTT}（Truncated BPTT）是一种实用的近似：

\begin{itemize}
    \item 前向传播仍然使用完整的上下文来更新隐藏状态
    \item 但反向传播只回溯固定的步数（如 20 步），然后截断
    \item 这大大减少了计算和存储开销，实践中通常足够好
\end{itemize}

\subsection{用 RNN 生成文本}

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-043.jpg}
\caption{RNN 文本生成的 rollout 过程：从 $\langle s \rangle$ 开始，每步采样一个词并作为下一步的输入，直到生成 $\langle/s\rangle$\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第44页。}

文本生成的流程：

\begin{enumerate}
    \item 输入起始符号 $\langle s \rangle$，初始隐藏状态 $h^{(0)} = \mathbf{0}$
    \item 通过 RNN 得到下一个词的概率分布
    \item 从该分布中\textbf{采样}（sampling）一个词
    \item 将采样得到的词作为下一时间步的输入
    \item 重复步骤 2--4，直到采样到终止符 $\langle/s\rangle$
\end{enumerate}

Manning 展示了几个有趣的生成示例：

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-044.jpg}
\caption{RNN 在 Obama 演讲语料上训练后生成的文本：比 N-gram 更流畅，但仍不够连贯\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第45页。}

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-045.jpg}
\caption{RNN 在 Harry Potter 语料上训练后的生成结果：能捕捉到人物名称和叙事风格\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第46页。}

\begin{knowledgebox}{字符级 RNN 与条件生成}
RNN 不仅可以在词级别工作，还可以在\textbf{字符级别}逐字母生成文本。Manning 展示了一个有趣的应用：用颜色的 RGB 值初始化隐藏状态，然后让字符级 RNN 生成颜色名称（如``Ghostly Pink''、``Power Gray''等），效果出奇地好。这是\textbf{条件语言模型}（Conditional Language Model）的一个简单例子——用额外信息（颜色值）来引导生成。
\end{knowledgebox}

\subsection{RNN 的优势与缺陷}

\begin{importantbox}{RNN 相比前代模型的进步}
\begin{enumerate}
    \item 可以处理\textbf{任意长度}的输入序列
    \item \textbf{参数共享}：同一个词无论出现在什么位置，都被同样的权重处理
    \item 模型大小\textbf{不随上下文长度增长}
    \item 理论上能利用\textbf{任意远}的历史信息
\end{enumerate}
\end{importantbox}

\begin{warningbox}{RNN 的两大实际问题}
\begin{enumerate}
    \item \textbf{顺序计算，无法并行}：RNN 必须按时间步依次计算隐藏状态（本质上是一个 for 循环），无法利用 GPU 的并行计算能力。这使得 RNN 的训练速度远慢于后来的 Transformer。
    \item \textbf{长距离依赖难以学习}：虽然理论上隐藏状态包含了所有历史信息，但实际上，随着序列变长，早期的信息会在隐藏状态中逐渐``衰减''，最近的词总是支配隐藏状态。这就是所谓的\textbf{梯度消失问题}，我们将在下一节详细讨论。
\end{enumerate}
\end{warningbox}

\subsection{本章小结}

\begin{itemize}
    \item RNN 的核心思想是在每个时间步应用相同的权重，并通过隐藏状态编码历史信息
    \item 训练使用 Teacher Forcing 策略，损失函数为每步预测的交叉熵平均值
    \item 重复权重的梯度等于各时间步梯度之和；实践中常用截断 BPTT 加速
    \item RNN 能处理任意长度输入且参数共享，但受限于顺序计算和长距离依赖学习困难
\end{itemize}

%% ========== Part 4: 梯度消失与梯度爆炸 ==========

\section{RNN 的梯度问题}

\subsection{梯度消失与梯度爆炸}

RNN 面临的核心训练难题是\textbf{梯度消失}（Vanishing Gradient）和\textbf{梯度爆炸}（Exploding Gradient）。

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-050.jpg}
\caption{梯度消失与梯度爆炸问题的起源：损失 $J^{(4)}(\theta)$ 对早期隐藏状态的梯度需要经过多次矩阵相乘\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第51页。}

考虑损失 $J^{(t)}$ 对早期隐藏状态 $h^{(1)}$ 的偏导数：

$$\frac{\partial J^{(t)}}{\partial h^{(1)}} = \frac{\partial h^{(2)}}{\partial h^{(1)}} \times \frac{\partial h^{(3)}}{\partial h^{(2)}} \times \cdots \times \frac{\partial h^{(t)}}{\partial h^{(t-1)}} \times \frac{\partial J^{(t)}}{\partial h^{(t)}}$$

每个 $\frac{\partial h^{(i)}}{\partial h^{(i-1)}}$ 涉及对权重矩阵 $W_h$ 和激活函数导数的乘积。当这些矩阵的\textbf{谱范数}（最大奇异值）：

\begin{itemize}
    \item \textbf{小于 1}：连续相乘后梯度\textbf{指数级衰减} $\rightarrow$ \textbf{梯度消失}
    \item \textbf{大于 1}：连续相乘后梯度\textbf{指数级增长} $\rightarrow$ \textbf{梯度爆炸}
\end{itemize}

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-055.jpg}
\caption{梯度消失的直觉：当 $\frac{\partial h^{(i+1)}}{\partial h^{(i)}}$ 的值较小时，梯度信号在反向传播过程中越来越弱\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第56页。}

\subsection{梯度消失的后果}

\begin{warningbox}{梯度消失意味着无法学习长距离依赖}
当梯度消失时，来自远处时间步的梯度信号几乎为零。这意味着：
\begin{itemize}
    \item 模型无法学到``远处的词对当前预测很重要''这样的模式
    \item 隐藏状态虽然理论上包含所有历史信息，但梯度信号无法告诉模型如何更好地利用早期信息
    \item 模型的有效上下文窗口远小于序列长度
\end{itemize}
\end{warningbox}

\subsection{梯度爆炸的后果与解决方案}

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-060.jpg}
\caption{梯度爆炸导致 SGD 更新步长过大，使参数跳到损失曲面的``奇怪''位置，严重时产生 Inf 或 NaN\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第61页。}

梯度爆炸的解决方案相对简单——\textbf{梯度裁剪}（Gradient Clipping）：

$$\text{if } \|\nabla_\theta J\| > \tau: \quad \nabla_\theta J \leftarrow \frac{\tau}{\|\nabla_\theta J\|} \cdot \nabla_\theta J$$

\begin{itemize}
    \item $\|\nabla_\theta J\|$：梯度的范数
    \item $\tau$：裁剪阈值（超参数）
\end{itemize}

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-062.jpg}
\caption{梯度裁剪：当梯度范数超过阈值时，等比缩小梯度，保持方向不变但限制步长\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第63页。}

\begin{importantbox}{梯度裁剪的直觉}
梯度裁剪就像给 SGD 的更新步长设置了一个``速度限制''。它不改变梯度的方向（仍然朝着正确的方向走），只是在步子太大时把它缩小到安全范围内。这是一个简单但非常有效的技巧，在训练 RNN 时几乎是标配。
\end{importantbox}

\subsection{梯度消失的解决思路}

梯度消失比梯度爆炸更难解决。Manning 提到了几个方向：

\begin{enumerate}
    \item \textbf{LSTM}（Long Short-Term Memory）：通过门控机制（遗忘门、输入门、输出门）显式控制信息的保留和遗忘，是解决梯度消失最经典的方法
    \item \textbf{GRU}（Gated Recurrent Unit）：LSTM 的简化版本，用更少的参数实现类似效果
    \item \textbf{残差连接}（Residual Connections）：通过跳跃连接让梯度直接流过多层
\end{enumerate}

这些内容将在后续课程中详细讲解。

\subsection{本章小结}

\begin{itemize}
    \item 梯度消失和梯度爆炸源于反向传播中的矩阵连乘效应
    \item 梯度爆炸可以通过\textbf{梯度裁剪}有效解决
    \item 梯度消失更为棘手，导致 RNN 难以学习长距离依赖。主要解决方案包括 LSTM、GRU 等门控机制
    \item 这些问题最终推动了 Transformer 架构的诞生——通过自注意力机制直接建立任意位置之间的连接，彻底绕过了序列依赖问题
\end{itemize}

%% ========== Part 5: RNN 的其他应用 ==========

\section{RNN 的应用场景}

RNN 不仅可以用作语言模型，还广泛应用于各种序列处理任务。

\subsection{序列标注}

RNN 可以在每个时间步产生一个输出，用于\textbf{序列标注}（Sequence Tagging）任务：

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-065.jpg}
\caption{RNN 用于词性标注（POS Tagging）：输入一个句子，为每个词预测其词性标签（DT, JJ, NN, VBN 等）\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第66页。}

典型应用包括：
\begin{itemize}
    \item \textbf{词性标注}（Part-of-Speech Tagging）：为每个词标注名词、动词、形容词等语法类别
    \item \textbf{命名实体识别}（Named Entity Recognition, NER）：识别人名、地名、组织名等
\end{itemize}

\subsection{文本分类与情感分析}

对于需要对整个序列产生单一判断的任务，可以使用 RNN 最后一个时间步的隐藏状态（或所有隐藏状态的某种聚合）作为序列的表示，再接分类层。

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-066.jpg}
\caption{RNN 用于情感分类：处理完整个句子后，用最终隐藏状态进行分类\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第67页。}

\subsection{条件语言模型与编码器-解码器架构}

通过用不同的信息初始化 RNN 的隐藏状态，可以构建\textbf{条件语言模型}：

\begin{figure}[H]
\centering
\includegraphics[width=0.95\textwidth]{slides-images/slide-070.jpg}
\caption{条件语言模型：以音频特征初始化隐藏状态，RNN 生成对应的文字转录。这是语音识别和机器翻译的基础框架\protect\footnotemark}
\end{figure}
\footnotetext{Slides 第71页。}

\begin{knowledgebox}{从 RNN 到编码器-解码器}
条件语言模型的思想自然延伸为\textbf{编码器-解码器}（Encoder-Decoder）架构：一个 RNN（编码器）读取输入序列并产生一个固定长度的隐藏表示，另一个 RNN（解码器）以该表示为条件生成输出序列。这一架构在机器翻译（Seq2Seq）中取得了巨大成功，将在后续课程中详细讨论。
\end{knowledgebox}

\subsection{本章小结}

\begin{itemize}
    \item RNN 是一种通用的序列处理架构，适用于多种 NLP 任务
    \item 每步都有输出 $\rightarrow$ 序列标注（词性标注、命名实体识别）
    \item 只看最终输出 $\rightarrow$ 文本分类（情感分析）
    \item 用外部信息初始化 $\rightarrow$ 条件生成（语音识别、机器翻译）
\end{itemize}

%% ========== 总结与延伸 ==========

\section{总结与延伸}

\subsection{全课知识图谱}

本课建立了从传统方法到神经网络方法的完整语言建模认知链：

\begin{center}
\begin{tikzpicture}[node distance=1.0cm, every node/.style={draw, rounded corners, minimum width=4cm, minimum height=0.8cm, font=\small}]
\node (basics) {深度学习实用技巧};
\node (lm) [below=of basics] {语言模型概念};
\node (ngram) [below=of lm] {N-gram 语言模型};
\node (fwnn) [below=of ngram] {固定窗口神经语言模型};
\node (rnn) [below=of fwnn] {循环神经网络 (RNN)};
\node (grad) [below=of rnn] {梯度消失与爆炸};
\draw[-{Stealth}, thick] (basics) -- (lm);
\draw[-{Stealth}, thick] (lm) -- (ngram);
\draw[-{Stealth}, thick] (ngram) -- (fwnn);
\draw[-{Stealth}, thick] (fwnn) -- (rnn);
\draw[-{Stealth}, thick] (rnn) -- (grad);
\node[draw=none, right=0.8cm of basics, text width=5.5cm, font=\scriptsize] {Dropout、Xavier 初始化、Adam};
\node[draw=none, right=0.8cm of lm, text width=5.5cm, font=\scriptsize] {$P(w \mid \text{context})$、链式法则};
\node[draw=none, right=0.8cm of ngram, text width=5.5cm, font=\scriptsize] {计数、马尔可夫假设、稀疏性};
\node[draw=none, right=0.8cm of fwnn, text width=5.5cm, font=\scriptsize] {词嵌入拼接、无稀疏性、仍受限};
\node[draw=none, right=0.8cm of rnn, text width=5.5cm, font=\scriptsize] {参数共享、隐藏状态、BPTT};
\node[draw=none, right=0.8cm of grad, text width=5.5cm, font=\scriptsize] {梯度裁剪、LSTM/GRU $\rightarrow$ Transformer};
\end{tikzpicture}
\end{center}

\subsection{关键 Takeaways}

\begin{importantbox}{五条核心要点}
\begin{enumerate}
    \item \textbf{语言模型是 NLP 的基石}：从手机输入法到 ChatGPT，语言模型的核心任务始终是预测下一个词的概率分布
    \item \textbf{从计数到学习}：N-gram 靠统计计数，神经语言模型靠参数学习——后者在泛化能力上有质的飞跃
    \item \textbf{RNN 的核心创新是参数共享}：同一组权重在每个时间步重复应用，使模型能处理任意长度的输入
    \item \textbf{梯度问题是序列模型的命门}：梯度消失限制了 RNN 学习长距离依赖的能力，这一问题推动了 LSTM、GRU 乃至 Transformer 的诞生
    \item \textbf{实用技巧至关重要}：Dropout、合理初始化、Adam 优化器、梯度裁剪——这些``小技巧''的组合是深度学习成功的关键
\end{enumerate}
\end{importantbox}

\subsection{展望：从 RNN 到 Transformer}

Manning 在课程最后暗示了后续的发展方向：

\begin{itemize}
    \item \textbf{LSTM 和 GRU}：通过门控机制缓解梯度消失问题，使 RNN 能更好地捕捉长距离依赖（下一讲）
    \item \textbf{编码器-解码器架构}：将 RNN 组合为翻译模型、摘要模型等序列到序列系统
    \item \textbf{注意力机制}（Attention）：让解码器在每步直接``看到''编码器的所有隐藏状态，而非仅依赖最终状态
    \item \textbf{Transformer}：完全抛弃循环结构，用自注意力实现全并行计算，彻底解决 RNN 的顺序计算瓶颈和长距离依赖问题
\end{itemize}

\subsection{拓展阅读}

\begin{itemize}
    \item Bengio et al., ``A Neural Probabilistic Language Model'' (2003): \url{https://www.jmlr.org/papers/volume3/bengio03a/bengio03a.pdf} —— 首个神经语言模型
    \item Mikolov et al., ``Recurrent Neural Network based Language Model'' (2010) —— RNN 语言模型的经典论文
    \item Hochreiter \& Schmidhuber, ``Long Short-Term Memory'' (1997) —— LSTM 的原始论文
    \item Cho et al., ``Learning Phrase Representations using RNN Encoder-Decoder'' (2014) —— GRU 的提出
    \item Karpathy, ``The Unreasonable Effectiveness of Recurrent Neural Networks'' (2015): \url{http://karpathy.github.io/2015/05/21/rnn-effectiveness/} —— RNN 生成文本的有趣博客
    \item CS224N 课程主页：\url{https://web.stanford.edu/class/cs224n/}
\end{itemize}

\end{document}
