课程基本信息
Lecture 1 - Intro & Word Vectors
keyword: word2vec algorithm point: language is a social system, constructed and interpreted by people.
语言蕴含了太多信息,每个人又会用不同方式解读,我们真正做的事情是,试图更好地猜测语言表述能如何影响其他人。
类似的观点我还真的想过。。。
Language是个很新的东西。在地球生命进化的尺度去想。
AI需要人类的知识来学习,但是这些知识又蕴藏在人类的语言文字记录当中。我们能否在其中建立良性循环?
- 机器翻译
- GPT
What is “meaning”? denotational semantics 指称语义
Is-a关系 WordNet
a localist representation 对单词one-hot编码
这些向量是正交的,没有相似的概念,怎么办?
下面介绍:distributional semantics
利用context words 非常成功的想法
我们提到word时要注意我们是把它视作type还是token
word embeddings
可视化(二维投影)
算法:Word2vec (Mikolov, 2013)
固定尺寸窗口 遍历corpus 语料库
很好理解的损失函数,负的对数似然
改进:每个word对应到两个vector,一个是作为中心词的,一个作为上下文词的
现在的问题:怎么根据单词参数计算相似度的?
内积,然后SoftMax函数
下面训练模型 梯度下降最小化损失即可
NLP库:Gensim
Lecture 2 - Neural Classifier
上节课对Word2vec进行的是一个大概的描述,注意到他有一个特点是,一个词周围(窗口内)出现特定词的概率是确定的,对每个位置每次都按照相同的概率进行预测。
SGD的介绍
注意到,SGD每次处理一个窗口的损失函数,却要对所有参数求梯度,因此求出来的梯度是十分稀疏的。我们可以想到,每次只更新那些窗口内词的参数。Pytorch架中,词向量实际被表示为行向量,这样可以把整个词向量作为连续的内存范围访问,这样才能做到这一点,否则需要为所有词向量维护哈希表。
Word2vec让每个词对应到两个向量,单纯是为了好计算梯度进而容易优化。对应到一个向量效果可能还会更好。
实际模型可以分为两种实现:
Skip-grams(SG)
Continuous Bag of Words(CBOW)
计算损失时他们都没有使用SoftMax(计算开销太大了),而是使用Negative sampling
以SGNS为例
他们采样的时候,特定单词的概率从它在大语料库中的占比计算得到,特别的是分子上有一个3/4次方,这可以让罕见词概率更大一些。·
换一个思路:为什么不直接计数?count based方法。
co-occurrence matrix,十分容易想到的方法
问题也很容易想到:维数是词汇总数,稀疏。如何降低维数?
对co-occurrence matrix进行奇异值分解(SVD),但由于数据不服从正态分布,效果不会太好。可以先进行值的缩放。
这里符合自己之前学过的知识诶!维数和数据点数一样多,不能用特征值分解。
吴建鑫《模式识别》的PCA讲解真的好。
下面介绍一种融合了多方优点的方法:GloVe
下面介绍 如何评估word vector
- word vector analogy
实践中发现的规律: 数据源质量的重要性、维数的选择
-
人类对词相似度的评分,计算相关系数
-
词向量对named entity recognition(命名实体识别)的影响
多义词现象?可以对同一个词进行多种标记,不同意思的用不同向量表示
但这种词义的分割也是模糊的。实际应用中,还是让一个词用一个向量表示,该向量是多个不同含义的叠加。效果是好的。思考为什么?
Lecture 3 - Backprop and Neural Networks
从named entity recognition(命名实体识别,NER)引入
简单的利用神经网络的想法:每个待识别词作为中心词,考虑一个窗口内(比如大小为5)的5个词,用word2vec转换成词向量,连接起来成为长向量,作为分类器的输入。
然后梯度下降更新网络参数。这里特殊的是,我们还可以同时更新我们的词向量表示。
实践了下用链式法则求梯度的过程。梯度形状的复杂转换。
反向传播。
这里提到General Computation Graph还是很值得学习的,提到拓扑排序,提到正向和反向计算时间复杂度相同。
Lecture 4 - Syntactic Structure and Dependency Parsing
介绍了constituency grammars(成分语法),是一种context-free grammar
又介绍了dependency grammars / dependency structure(依存语法 / 依存结构),是对句法的分析。
老师用 look in the large crate in the kitchen by the door 作为例句。箭头从被修饰的词指向修饰词。这是Tesniere的惯例,当然也可以反过来。
介词短语附着歧义与卡特兰数
为什么歧义种数是卡特兰数?
一种二元非对称关系
例子:cuddly cat,箭头从cat指向cuddly,cuddly被称为cat的dependent,cat被称为cuddly的head
句子中的这些关系最终形成了连通无环有着单一根的有向图—从而形成了依存树
David Hayes在20世纪60年代发表了(也许是)第一个dependency parser.
为了方便算法实现,通常让真正的根(中心词)修饰一个假的ROOT。
人类标注,产生treebanks(树库)
特征:valency(配价) of the heads
个人理解是描述中心词左右通常出现什么词的先验事实?
可以专门研究“投射的”解析(projective parse)。即不会存在交叉的解析。
但英语中的介词悬空等现象通常允许非投射的解析
如何构建一个parser?
-
DP O(n^3)
-
Graph algorithms
-
Constraint Satisfaction
-
Transition-based parsing / deterministic dependency parsing
我们来关注最后一种方法。“类似于shift and reduce parser(移进-归约解析器)”
# Basic transition-based dependency parser
Start: σ = [ROOT], β = w₁, ..., wₙ, A = ∅
1. Shift σ, wᵢ|β, A → σ|wᵢ, β, A
2. Left-Arc σ|wᵢ|wⱼ, β, A → σ|wⱼ, β, A∪{r(wⱼ,wᵢ)}
3. Right-Arc σ|wᵢ|wⱼ, β, A → σ|wᵢ, β, A∪{r(wᵢ,wⱼ)}
Finish: σ = [w], β = ∅
例句:[root] I ate fish.
我草妙啊。然后训练使得模型能够预测下一个action即可。O(n)时间复杂度。
如果有20种依存关系,那么action维度就是2x20+1=41种。
可以贪婪方法,也可以加上beam search等搜索方法。
那么如何构建特征呢?可能看看栈顶、缓冲区顶的单词之类的。通常这些解析器有million级别的特征。
如何评估?Acc即可。
Lecture 5 - Recurrent Neural networks (RNNs)
神经网络的出现到深度学习的繁荣之间有一大段停滞,各种模型细节和训练技巧发挥着重要的作用。
-
“如今我们不再相信过拟合”,大型网络通过大量参数完美拟合训练集的同时实现泛化性能。
-
为了实现泛化性能,正则化是重要的——dropout 两个视角看待,一方面是避免部分特征依赖,增加鲁棒性;另一方面是可以认为dropout是一种集成学习。
-
深度学习讨厌for循环,用矩阵向量计算替代。
-
简单介绍了优化器问题。记住Adam这个词。
-
初始化问题(鞍点?),小范围随机数,后续Layer Norm可以减轻初始化的影响。
Language Model
n-gram
平滑方法(加一、回退、插值Kneser-Ney平滑)
A (fixed-window) Neural Language Model
RNN的出现使得我们可以处理可变长度的输入
RNN显著的问题:无法并行、无法解决长距离依赖(梯度消失)
(梯度爆炸可以用梯度裁剪简单解决)
训练细节:交叉熵损失(其实就是对数似然,因为真实标签只有一个)、teacher forcing的训练方法(每次用正确答案去预测下一个词)
,其中是真实标签的one-hot编码
RNN的BP:BPTT算法
RNN通常会限制最大上下文长度,把语料库截断若干片段,用片段训练
Lecture 6 - Sequence to Sequence Models
如何评估LM
perplexity 困惑度 预测概率倒数的几何平均值 现在不咋用了
困惑度其实就是交叉熵的指数
RNN的改进——LSTM
Hochreiter, Schmidhuber(1997)最早提出
但后续的Gers, Schmidhuber(2000)第二篇LSTM的论文也十分重要
当时没有得到太多重视
Schmidhuber后来有一个学生Alex Graves用LSTM做了更多工作,还去当了Hinton的博士后,让LSTM越来越火
LSTM能一定程度解决梯度消失的核心思想:加性机制,有点像Resnet、Densenet、HighwayNet的思想
RNN类模型的诸多应用:在各种其他信息的基础上生成文本(conditional language model)
表示信息可以所有h取均值,也可以直接取最后一个
BiLSTM 作为表示模型的改进
多层RNN(2、3层的居多)
机器翻译
统计机器翻译 是一个突破 但效果完全不够好
神经机器翻译 seq2seq model 伟大的进步
最简单的例子,用两个LSTM,一个编码一个解码,端到端训练
Lecture 7 - Attention, Final Projects and LLM intro
BLEU评估
早期的Attention:编码器每一步的隐藏状态都作为key。对解码器的每一步的隐藏装状态计算注意力得分,根据结果把编码器状态加权,与当前解码器状态连接起来用于输出。
这在当时大大改进了LSTM机器翻译。
注意力得分具体是如何计算的?直接点积的改进:bilinear attention(双线性注意力),,W可学习。W参数量太多怎么办?让W是两个低秩矩阵的乘积。这其实等价于让两个向量用对应的低秩矩阵投影后再点积。(基本和transformer的做法相同)
当然也可以用神经网络替代这个注意力得分的计算过程。但实践中还是上面的做法更好。
Lecture 8 - Self-Attention and Transformers
Self-Attention解决了RNN结构的诸多问题
位置编码
Transformer
这里用矩阵计算的方式讲解真的非常必要!
LayerNorm这里,下面同学们问的问题真好
Transformer的缺点 n^2的复杂度
TODO: Lecture 9 - Pretraining
Lecture 10 - Post-training
Lecture 11 - Natural Language Generation / Benchmarking
Lecture 12 - Neural Network
Lecture 13 - Interfaces
Lecture 14 - Reasoning and Agents
Lecture 15 - After DPO
Lecture 16 - ConvNets / Multimodal
Lecture 18 NLP, Linguistics, Philosophy
Assignment 1: Exploring Word Vectors
用到的库
-
Gensim
Gensim提供了Doc2Vec模型(训练一个神经网络,可以将任意长度的文本映射到固定长度的向量上) Gensim支持Word2Vec和FastText两种词向量模型。Word2Vec通过预测一个词周围的词来学习词向量,而FastText则进一步考虑了词内部的子结构(n-gram),对罕见词甚至未登录词(out-of-vocabulary words)有更好的表现。 Gensim还提供 LDA(此处指潜在迪利克雷分布,和线性判别分析无关)主题建模方法。LDA是一种用来识别大规模文档集中潜在主题的统计方法。
-
NLTK
常用于语料库下载、分词、词性标注、情感分析等简单任务
-
sklearn
场景 功能 主要模块 数据预处理 清洗、标准化、编码类别变量 sklearn.preprocessing
模型训练与评估 构建分类/回归模型(决策树、SVM、逻辑回归等),评估性能 sklearn.model_selection
,sklearn.linear_model
,sklearn.metrics
特征工程与降维 特征选择、降维、构造新特征 sklearn.decomposition
,sklearn.feature_selection
from sklearn.decomposition import TruncatedSVDfrom sklearn.decomposition import PCA
PCA和SVD的学习推荐学习 lectures 7, 8, and 9 of CS168.
CS168:The Modern Algorithmic Toolbox
numpy的广播机制建议学习
Computation on Arrays: Broadcasting | Python Data Science Handbook
PART 1
nltk.download('reuters')
下载一个英文新闻语料库
from nltk.corpus import reuters
reuters.fileids(category)
令category = crude,得到reuters中关于原油的新闻语料库即若干段新闻句子的list,处理后每个句子都是
利用python的set去重,得到完整词汇表。
通过计数得到共现矩阵:np.zeros初始化M,建立word2Ind字典,遍历每个句子计数。
SVD分解:
svd = TruncatedSVD(n_components=k, n_iter=n_iters)M_reduced=svd.fit_transform(M)
这里TruncatedSVD使用一种叫做随机化SVD的算法加速计算,n_iters控制迭代次数
这样每个词都被表示为一个二维向量,随便把几个词画在散点图上(plt.scatter
)
M_lengths = np.linalg.norm(M_reduced_co_occurrence, axis=1)M_normalized = M_reduced_co_occurrence / M_lengths[:, np.newaxis]
M_lengths是(N, )的, M_lengths[:, np.newaxis是(N, 1)的(这里也可以用reshape),M_normalized是N x 2的,因此这里的计算利用了numpy的广播机制。
注意不能直接除以M_lengths!否则会按行广播!
PART 2
import gensim.downloader as apiwv_from_bin = api.load("glove-wiki-gigaword-200")
下载并加载了一个训练好了的glove模型
wv_from_bin.vocab.keys()
该用法被gensim 4.0.0弃用了,应该改为
.index_to_key
、.key_to_index
进行和PART1一样的操作,得到散点图
使用 wv_from_bin.most_similar(word)
输出和给定word最相似的词,测试该glove模型的特点
使用wv_from_bin.distance(w1,w2)
测试两个词的相似度,yes和correct的相似度大于no和correct的
most_similar
还有更加实用的用法,例子:
pprint.pprint(wv_from_bin.most_similar(positive=['woman', 'king'], negative=['man']))
Assignment 2: word2vec
written
理解 the skip-gram word2vec algorithm
(a) 说明 Naive Softmax loss 实际等价于交叉熵损失
(b, c) 对特定的中心词和上下文词样本,计算损失函数,计算损失函数关于中心词向量的导数、关于窗口内每个词的上下文词向量导数
(d) 计算sigmoid函数的导数。
(e) 改用Negative Sampling loss,计算损失函数关于中心词向量的导数、关于窗口内每个词的上下文词向量导数
(f) 对给定的中心词,窗口内损失函数求和,计算总损失函数关于窗口内每个词的上下文/中心词向量导数
coding
(a)
在word2vec.py
中
实现sigmoid()
实现naiveSoftmaxLossAndGradient
,需要计算loss、梯度
实现negSamplingLossAndGradient
,需要计算loss、梯度
实现skipgram
(b)
在sgd.py
中
实现SGD
(c)
运行完整代码,在Stanford Sentiment Treebank (SST) dataset训练词向量
Assignment 3
Final Project
默认项目是实现一个BERT,当然也可以自定义项目
推荐学习Huggingface的transformers