A1 · Exploring Word Vectors
| 年份 | 2026(W26)—— 往年版本可能不同,见作业说明 |
| 分值 | 25 分(W26 参与评分) |
| 截止 | 1/13(周二)4:30pm |
| 算力 | CPU 即可(numpy / matplotlib / sklearn,无需 GPU) |
| 配哪几讲 | 候选(机器按术语匹配,供参考):第1讲 §7. 词向量:把意义表示为数字;第1讲 §5. 大语言模型与生成式 AI 的崛起;第2讲 §四、现场演示:用真实词向量玩类比;第2讲 §七、如何评估词向量? |
做什么
题面来自官方材料(本地原件在
_local/,官方链接见「材料」)。 编号是原件的编号(notebook 是 cell 序号、PDF 是页码),方便跟官方题面对号。 本项目不提供答案(D53)。
cell 0
CS224N Assignment 1: Exploring Word Vectors(25 分)
截止时间:2026 年 1 月 13 日(周二)下午 4:30
欢迎来到 CS224N!
开始之前,请务必阅读与本 notebook 同一目录下的 README.md,里面有关键的环境配置信息。你需要先安装一些 Python 库,才能顺利做完这份作业。本 notebook 中已经提供了大量代码,我们非常鼓励你把它读一遍并理解它 —— 这也是学习的一部分 :)
如果你对 Python、Numpy 或 Matplotlib 还不太熟悉,我们建议你参加周五的 review session。该课程会有录像,材料也会发布在我们的课程网站上。CS231N 的 Python/Numpy 教程也是一份很好的资料。
Assignment Notes: 请务必边做边保存 notebook。提交说明位于本 notebook 的末尾。
cell 1|代码(原件里的脚手架,原样保留)
# All Import Statements Defined Here
# Note: Do not add to this list.
# ----------------
import sys
assert sys.version_info[0] == 3
assert sys.version_info[1] >= 8
from platform import python_version
assert int(python_version().split(".")[1]) >= 5, "Please upgrade your Python version following the instructions in \
the README.md file found in the same directory as this notebook. Your Python version is " + python_version()
from gensim.models import KeyedVectors
from gensim.test.utils import datapath
import pprint
import matplotlib.pyplot as plt
plt.rcParams['figure.figsize'] = [10, 5]
from datasets import load_dataset
imdb_dataset = load_dataset("stanfordnlp/imdb", name="plain_text")
import re
import numpy as np
import random
import scipy as sp
from sklearn.decomposition import TruncatedSVD
from sklearn.decomposition import PCA
START_TOKEN = '<START>'
END_TOKEN = '<END>'
NUM_SAMPLES = 150
np.random.seed(0)
random.seed(0)
# ----------------
cell 2
Word Vectors
Word Vectors 常被用作下游 NLP 任务(例如 question answering、文本生成、翻译等)的基础组件,因此建立对其优势与劣势的直觉很重要。在这里,你将探索两类 word vectors:由 co-occurrence matrices(共现矩阵)导出的词向量,以及由 GloVe 导出的词向量。
术语说明: “word vectors” 和 “word embeddings” 这两个说法经常混用。“embedding” 一词指的是:我们把一个词意义的某些方面编码到一个更低维的空间中。正如 Wikipedia 所说:“conceptually it involves a mathematical embedding from a space with one dimension per word to a continuous vector space with a much lower dimension”(概念上,它涉及一个从“每个词一维”的空间到一个维度低得多的连续向量空间的数学嵌入)。
cell 3
Part 1: Count-Based Word Vectors(10 分)
大多数 word vector 模型都源自下面这个想法:
你可以通过一个词结交的伙伴来认识这个词(Firth, J. R. 1957:11)
许多 word vector 实现都基于这样一个想法:相似的词,即(近)同义词,会出现在相似的上下文中。因此,相似的词常常会与同一个词子集(即上下文)一起被说出或写出。通过考察这些上下文,我们就可以尝试为词构建 embeddings。基于这一直觉,许多“老派”的 word vector 构造方法都依赖于词频统计。这里我们详细讲其中一种策略,即 co-occurrence matrices(共现矩阵)(更多信息见这里或这里)。
cell 4
Co-Occurrence(共现)
共现矩阵统计的是:某些东西在某个环境中共同出现的频率。给定文档中出现的某个词 $w_i$,我们考虑 $w_i$ 周围的 context window(上下文窗口)。假设我们固定的窗口大小为 $n$,那么这就是该文档中 $w_i$ 之前的 $n$ 个词和之后的 $n$ 个词,即词 $w_{i-n} \dots w_{i-1}$ 与 $w_{i+1} \dots w_{i+n}$。我们构建一个 co-occurrence matrix(共现矩阵)$M$,它是一个对称的词×词矩阵,其中 $M_{ij}$ 表示在所有文档中,$w_j$ 出现在 $w_i$ 窗口内的次数。
例子:固定窗口 n=1 的共现:
Document 1: "all that glitters is not gold"
Document 2: "all is well that ends well"
| * | <START> |
all | that | glitters | is | not | gold | well | ends | <END> |
|---|---|---|---|---|---|---|---|---|---|---|
<START> |
0 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| all | 2 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| that | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 1 | 0 |
| glitters | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| is | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
| not | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 |
| gold | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 |
| well | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 1 |
| ends | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
<END> |
0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 |
在 NLP 中,我们通常用 <START> 和 <END> token 来标记句子、段落或文档的开头与结尾。这些 token 也被计入共现统计,从而把每个文档“包”起来,例如:"<START> All that glitters is not gold <END>"。
矩阵的行(或列)给出了基于词-词共现的 word vectors,但它们可能非常大。为了降维,我们采用 Singular Value Decomposition(SVD,奇异值分解),这类似于 PCA,选出前 $k$ 个主成分。SVD 过程把共现矩阵 $A$ 分解为对角 $S$ 矩阵中的奇异值,以及 $U_k$ 中新的、更短的 word vectors。
这种降维会保持语义关系;例如,doctor 与 hospital 的距离会比 doctor 与 dog 更近。
如果你不熟悉特征值与 SVD,这里有一份适合初学者的 SVD 介绍。想更深入了解,还可以参考 CS168 的第 7、8、9 讲,它们对这些算法做了高层面的讲解。在实际实现时,建议使用 numpy、scipy 或 sklearn 等 Python 包中现成的函数。虽然对大规模语料做完整 SVD 可能很占内存,但存在 Truncated SVD 这类可扩展的技术,可以高效地提取前 $k$ 个向量分量。
cell 5
绘制 Co-Occurrence Word Embeddings
这里我们将使用 Large Movie Review Dataset(大型影评数据集)。这是一个用于二分类情感分类的数据集,数据量远超前人的基准数据集。我们提供了 25,000 条情感极性很强的影评用于训练,25,000 条用于测试。此外还有额外的无标签数据可用。下面我们提供一个 read_corpus 函数,它从数据集中取出影评文本。该函数还会给每篇文档加上 <START> 和 <END> token,并把词转成小写。你不需要再做任何其他形式的预处理。
cell 6|代码(原件里的脚手架,原样保留)
def read_corpus():
""" Read files from the Large Movie Review Dataset.
Params:
category (string): category name
Return:
list of lists, with words from each of the processed files
"""
files = imdb_dataset["train"]["text"][:NUM_SAMPLES]
return [[START_TOKEN] + [re.sub(r'[^\w]', '', w.lower()) for w in f.split(" ")] + [END_TOKEN] for f in files]
cell 7
我们来看看这些文档长什么样……
cell 8|代码(原件里的脚手架,原样保留)
imdb_corpus = read_corpus()
pprint.pprint(imdb_corpus[:3], compact=True, width=100)
print("corpus size: ", len(imdb_corpus[0]))
cell 9
Question 1.1:实现 distinct_words [code](2 分)
编写一个方法,找出语料库中出现的不同单词(词类型)。
你可以使用 for 循环来处理输入 corpus(一个由字符串列表组成的列表),但请尝试使用 Python list comprehensions(它们通常更快)。特别地,这个链接可能有助于展平一个由列表组成的列表。如果你对 Python list comprehensions 整体不熟悉,这里有更多信息。
你返回的 corpus_words 应当是有序的。你可以使用 Python 的 sorted 函数来完成。
你可能发现使用 Python sets 来去除重复单词会很有用。
cell 10|代码(原件里的脚手架,原样保留)
def distinct_words(corpus):
""" Determine a list of distinct words for the corpus.
Params:
corpus (list of list of strings): corpus of documents
Return:
corpus_words (list of strings): sorted list of distinct words across the corpus
n_corpus_words (integer): number of distinct words across the corpus
"""
corpus_words = []
n_corpus_words = -1
# ------------------
# Write your implementation here.
# ------------------
return corpus_words, n_corpus_words
cell 11|代码(原件里的脚手架,原样保留)
# ---------------------
# Run this sanity check
# Note that this not an exhaustive check for correctness.
# ---------------------
# Define toy corpus
test_corpus = ["{} All that glitters isn't gold {}".format(START_TOKEN, END_TOKEN).split(" "), "{} All's well that ends well {}".format(START_TOKEN, END_TOKEN).split(" ")]
test_corpus_words, num_corpus_words = distinct_words(test_corpus)
# Correct answers
ans_test_corpus_words = sorted([START_TOKEN, "All", "ends", "that", "gold", "All's", "glitters", "isn't", "well", END_TOKEN])
ans_num_corpus_words = len(ans_test_corpus_words)
# Test correct number of words
assert(num_corpus_words == ans_num_corpus_words), "Incorrect number of distinct words. Correct: {}. Yours: {}".format(ans_num_corpus_words, num_corpus_words)
# Test correct words
assert (test_corpus_words == ans_test_corpus_words), "Incorrect corpus_words.\nCorrect: {}\nYours: {}".format(str(ans_test_corpus_words), str(test_corpus_words))
# Print Success
print ("-" * 80)
print("Passed All Tests!")
print ("-" * 80)
cell 12
Question 1.2:实现 compute_co_occurrence_matrix [code](3 分)
编写一个方法,为某个窗口大小 $n$(默认值为 4)构造共现矩阵,考虑窗口中中心词之前 $n$ 个词和之后 $n$ 个词。这里我们开始使用 numpy (np) 来表示向量、矩阵和张量。如果你不熟悉 NumPy,这个 cs231n 的 Python NumPy tutorial 后半部分有一个 NumPy 教程。
cell 13|代码(原件里的脚手架,原样保留)
def compute_co_occurrence_matrix(corpus, window_size=4):
""" Compute co-occurrence matrix for the given corpus and window_size (default of 4).
Note: Each word in a document should be at the center of a window. Words near edges will have a smaller
number of co-occurring words.
For example, if we take the document "<START> All that glitters is not gold <END>" with window size of 4,
"All" will co-occur with "<START>", "that", "glitters", "is", and "not".
Params:
corpus (list of list of strings): corpus of documents
window_size (int): size of context window
Return:
M (a symmetric numpy matrix of shape (number of unique words in the corpus , number of unique words in the corpus)):
Co-occurence matrix of word counts.
The ordering of the words in the rows/columns should be the same as the ordering of the words given by the distinct_words function.
word2ind (dict): dictionary that maps word to index (i.e. row/column number) for matrix M.
"""
words, n_words = distinct_words(corpus)
M = None
word2ind = {}
# ------------------
# Write your implementation here.
# ------------------
return M, word2ind
cell 14|代码(原件里的脚手架,原样保留)
# ---------------------
# Run this sanity check
# Note that this is not an exhaustive check for correctness.
# ---------------------
# Define toy corpus and get student's co-occurrence matrix
test_corpus = ["{} All that glitters isn't gold {}".format(START_TOKEN, END_TOKEN).split(" "), "{} All's well that ends well {}".format(START_TOKEN, END_TOKEN).split(" ")]
M_test, word2ind_test = compute_co_occurrence_matrix(test_corpus, window_size=1)
# Correct M and word2ind
M_test_ans = np.array(
[[0., 0., 0., 0., 0., 0., 1., 0., 0., 1.,],
[0., 0., 1., 1., 0., 0., 0., 0., 0., 0.,],
[0., 1., 0., 0., 0., 0., 0., 0., 1., 0.,],
[0., 1., 0., 0., 0., 0., 0., 0., 0., 1.,],
[0., 0., 0., 0., 0., 0., 0., 0., 1., 1.,],
[0., 0., 0., 0., 0., 0., 0., 1., 1., 0.,],
[1., 0., 0., 0., 0., 0., 0., 1., 0., 0.,],
[0., 0., 0., 0., 0., 1., 1., 0., 0., 0.,],
[0., 0., 1., 0., 1., 1., 0., 0., 0., 1.,],
[1., 0., 0., 1., 1., 0., 0., 0., 1., 0.,]]
)
ans_test_corpus_words = sorted([START_TOKEN, "All", "ends", "that", "gold", "All's", "glitters", "isn't", "well", END_TOKEN])
word2ind_ans = dict(zip(ans_test_corpus_words, range(len(ans_test_corpus_words))))
# Test correct word2ind
assert (word2ind_ans == word2ind_test), "Your word2ind is incorrect:\nCorrect: {}\nYours: {}".format(word2ind_ans, word2ind_test)
# Test correct M shape
assert (M_test.shape == M_test_ans.shape), "M matrix has incorrect shape.\nCorrect: {}\nYours: {}".format(M_test.shape, M_test_ans.shape)
# Test correct M values
for w1 in word2ind_ans.keys():
idx1 = word2ind_ans[w1]
for w2 in word2ind_ans.keys():
idx2 = word2ind_ans[w2]
student = M_test[idx1, idx2]
correct = M_test_ans[idx1, idx2]
if student != correct:
print("Correct M:")
print(M_test_ans)
print("Your M: ")
print(M_test)
raise AssertionError("Incorrect count at index ({}, {})=({}, {}) in matrix M. Yours has {} but should have {}.".format(idx1, idx2, w1, w2, student, correct))
# Print Success
print ("-" * 80)
print("Passed All Tests!")
print ("-" * 80)
cell 15
Question 1.3:实现 reduce_to_k_dim [code](1 分)
构造一个方法,对矩阵执行降维,以生成 k 维 embeddings。使用 SVD 取前 k 个分量,并生成一个由 k 维 embeddings 组成的新矩阵。
注意: numpy、scipy 和 scikit-learn(sklearn)都提供了 SVD 的某种实现,但只有 scipy 和 sklearn 提供了 Truncated SVD 的实现,并且只有 sklearn 提供了用于计算大规模 Truncated SVD 的高效随机化算法。因此请使用 sklearn.decomposition.TruncatedSVD。
cell 16|代码(原件里的脚手架,原样保留)
def reduce_to_k_dim(M, k=2):
""" Reduce a co-occurence count matrix of dimensionality (num_corpus_words, num_corpus_words)
to a matrix of dimensionality (num_corpus_words, k) using the following SVD function from Scikit-Learn:
- http://scikit-learn.org/stable/modules/generated/sklearn.decomposition.TruncatedSVD.html
Params:
M (numpy matrix of shape (number of unique words in the corpus , number of unique words in the corpus)): co-occurence matrix of word counts
k (int): embedding size of each word after dimension reduction
Return:
M_reduced (numpy matrix of shape (number of corpus words, k)): matrix of k-dimensioal word embeddings.
In terms of the SVD from math class, this actually returns U * S
"""
n_iters = 10 # Use this parameter in your call to `TruncatedSVD`
M_reduced = None
print("Running Truncated SVD over %i words..." % (M.shape[0]))
# ------------------
# Write your implementation here.
# ------------------
print("Done.")
return M_reduced
cell 17|代码(原件里的脚手架,原样保留)
# ---------------------
# Run this sanity check
# Note that this is not an exhaustive check for correctness
# In fact we only check that your M_reduced has the right dimensions.
# ---------------------
# Define toy corpus and run student code
test_corpus = ["{} All that glitters isn't gold {}".format(START_TOKEN, END_TOKEN).split(" "), "{} All's well that ends well {}".format(START_TOKEN, END_TOKEN).split(" ")]
M_test, word2ind_test = compute_co_occurrence_matrix(test_corpus, window_size=1)
M_test_reduced = reduce_to_k_dim(M_test, k=2)
# Test proper dimensions
assert (M_test_reduced.shape[0] == 10), "M_reduced has {} rows; should have {}".format(M_test_reduced.shape[0], 10)
assert (M_test_reduced.shape[1] == 2), "M_reduced has {} columns; should have {}".format(M_test_reduced.shape[1], 2)
# Print Success
print ("-" * 80)
print("Passed All Tests!")
print ("-" * 80)
cell 18
Question 1.4: 实现 plot_embeddings [code](1 分)
这里你要写一个函数,把一组二维向量画在二维空间里。画图我们会用 Matplotlib(plt)。
作为例子,你可能会觉得改写这段代码很有用。以后想要画好一张图,一个好办法是去看Matplotlib gallery,找一张大致像你想要的效果的图,然后改写他们给的代码。
cell 19|代码(原件里的脚手架,原样保留)
def plot_embeddings(M_reduced, word2ind, words):
""" Plot in a scatterplot the embeddings of the words specified in the list "words".
NOTE: do not plot all the words listed in M_reduced / word2ind.
Include a label next to each point.
Params:
M_reduced (numpy matrix of shape (number of unique words in the corpus , 2)): matrix of 2-dimensioal word embeddings
word2ind (dict): dictionary that maps word to indices for matrix M
words (list of strings): words whose embeddings we want to visualize
"""
# ------------------
# Write your implementation here.
# ------------------
cell 20|代码(原件里的脚手架,原样保留)
# ---------------------
# Run this sanity check
# Note that this is not an exhaustive check for correctness.
# The plot produced should look like the included file question_1.4_test.png
# ---------------------
print ("-" * 80)
print ("Outputted Plot:")
M_reduced_plot_test = np.array([[1, 1], [-1, -1], [1, -1], [-1, 1], [0, 0]])
word2ind_plot_test = {'test1': 0, 'test2': 1, 'test3': 2, 'test4': 3, 'test5': 4}
words = ['test1', 'test2', 'test3', 'test4', 'test5']
plot_embeddings(M_reduced_plot_test, word2ind_plot_test, words)
print ("-" * 80)
cell 21
Question 1.5: 共现矩阵绘图分析 [written](3 分)
现在我们要把你写好的所有部分拼起来用!我们会在 Large Movie Review 语料上,用固定窗口 4(默认窗口大小)计算 co-occurrence matrix。然后我们用 TruncatedSVD 计算每个词的二维 embedding。TruncatedSVD 返回的是 $U*S$,所以我们需要把返回的向量归一化,让所有向量都落在单位圆附近(因此接近程度就是方向上的接近程度)。注意:下面做归一化的那行代码用到了 NumPy 的广播(broadcasting)概念。如果你不了解 broadcasting,去看看 Computation on Arrays: Broadcasting by Jake VanderPlas。
运行下面这一格来生成图。运行可能要花上几分钟。
cell 22|代码(原件里的脚手架,原样保留)
# -----------------------------
# Run This Cell to Produce Your Plot
# ------------------------------
imdb_corpus = read_corpus()
M_co_occurrence, word2ind_co_occurrence = compute_co_occurrence_matrix(imdb_corpus)
M_reduced_co_occurrence = reduce_to_k_dim(M_co_occurrence, k=2)
# Rescale (normalize) the rows to make them each of unit-length
M_lengths = np.linalg.norm(M_reduced_co_occurrence, axis=1)
M_normalized = M_reduced_co_occurrence / M_lengths[:, np.newaxis] # broadcasting
words = ['movie', 'book', 'mysterious', 'story', 'fascinating', 'good', 'interesting', 'large', 'massive', 'huge']
plot_embeddings(M_normalized, word2ind_co_occurrence, words)
cell 23
确认你的图与作业压缩包中的 "question_1.5.png" 一致。如果不一致,请用 "question_1.5.png" 中的图来回答接下来两个问题。
cell 24
a. 找出至少两组在二维 embedding 空间中聚在一起的词。对你观察到的每一个 cluster 给出解释。
cell 25
在这里写下你的答案。
cell 26
b. 有哪些词你觉得应该聚在一起、实际却没有?至少描述两个例子。
cell 27
在这里写下你的答案。
cell 28
Part 2: 基于预测的词向量(15 分)
正如课上讨论过的,近年来基于预测(prediction-based)的词向量表现出了更好的性能,例如 word2vec 和 GloVe(后者也利用了计数带来的好处)。这里我们将探索 GloVe 生成的 embedding。关于 word2vec 和 GloVe 算法的更多细节,请回看课堂笔记和讲义。如果你比较有冒险精神,可以挑战一下,试着读一读 GloVe 的原始论文。
接下来运行下面几格,把 GloVe 向量载入内存。注意:如果这是你第一次运行这几格(也就是要下载 embedding 模型),运行需要几分钟。如果你之前运行过,重新运行会直接加载模型而不会重新下载,大约需要 1 到 2 分钟。
cell 29|代码(原件里的脚手架,原样保留)
def load_embedding_model():
""" Load GloVe Vectors
Return:
wv_from_bin: All 400000 embeddings, each length 200
"""
import gensim.downloader as api
wv_from_bin = api.load("glove-wiki-gigaword-200")
print("Loaded vocab size %i" % len(list(wv_from_bin.index_to_key)))
return wv_from_bin
wv_from_bin = load_embedding_model()
cell 30
注意:如果你遇到 "reset by peer" 错误,重新运行这一格即可重新开始下载。
cell 31
降低词 embedding 的维度
我们把 GloVe embedding 和共现矩阵得到的 embedding 直接做个比较。为了避免内存不够用,我们会改用 40000 个 GloVe 向量的样本来做。
运行下面几格,依次做:
- 把 40000 个 GloVe 向量放进一个矩阵 M。
- 运行
reduce_to_k_dim(你写的 Truncated SVD 函数),把这些向量从 200 维降到 2 维。
cell 32|代码(原件里的脚手架,原样保留)
def get_matrix_of_vectors(wv_from_bin, required_words):
""" Put the GloVe vectors into a matrix M.
Param:
wv_from_bin: KeyedVectors object; the 400000 GloVe vectors loaded from file
Return:
M: numpy matrix shape (num words, 200) containing the vectors
word2ind: dictionary mapping each word to its row number in M
"""
import random
words = list(wv_from_bin.index_to_key)
print("Shuffling words ...")
random.seed(225)
random.shuffle(words)
print("Putting %i words into word2ind and matrix M..." % len(words))
word2ind = {}
M = []
curInd = 0
for w in words:
try:
M.append(wv_from_bin.get_vector(w))
word2ind[w] = curInd
curInd += 1
except KeyError:
continue
for w in required_words:
if w in words:
continue
try:
M.append(wv_from_bin.get_vector(w))
word2ind[w] = curInd
curInd += 1
except KeyError:
continue
M = np.stack(M)
print("Done.")
return M, word2ind
cell 33|代码(原件里的脚手架,原样保留)
# -----------------------------------------------------------------
# Run Cell to Reduce 200-Dimensional Word Embeddings to k Dimensions
# Note: This should be quick to run
# -----------------------------------------------------------------
M, word2ind = get_matrix_of_vectors(wv_from_bin, words)
M_reduced = reduce_to_k_dim(M, k=2)
# Rescale (normalize) the rows to make them each of unit-length
M_lengths = np.linalg.norm(M_reduced, axis=1)
M_reduced_normalized = M_reduced / M_lengths[:, np.newaxis] # broadcasting
cell 34
注意:如果你在本地机器上遇到内存不足的问题,可以试试关掉其他应用,给你的设备腾出更多内存。你也可以试试重启机器,好腾出额外的内存。然后立刻运行 jupyter notebook,看看能不能正常加载词向量。如果这样做之后你在本地机器上加载 embedding 仍然有问题,请到 office hours 或联系课程 staff。
cell 35
Question 2.1: GloVe 绘图分析 [written](3 分)
运行下面这一格,画出 ['movie', 'book', 'mysterious', 'story', 'fascinating', 'good', 'interesting', 'large', 'massive', 'huge'] 的二维 GloVe embedding。
cell 36|代码(原件里的脚手架,原样保留)
words = ['movie', 'book', 'mysterious', 'story', 'fascinating', 'good', 'interesting', 'large', 'massive', 'huge']
plot_embeddings(M_reduced_normalized, word2ind, words)
cell 37
请核对你生成的图与作业压缩包中 "question_2.1.png" 是否一致。如果不一致,请改用 "question_2.1.png" 中的图(若适用,也使用 "question_1.5.png" 中的图)来回答接下来的两个问题。
cell 38
a. 这张图与之前由 co-occurrence matrix 生成的那张图,在一个方面有什么不同?在一个方面又有什么相似之处?
cell 39
在此写下你的答案。
cell 40
b. 为什么 GloVe 的图(question_2.1.png)可能与之前由 co-occurrence matrix 生成的图(question_1.5.png)不同?
cell 41
在此写下你的答案。
cell 42
余弦相似度(Cosine Similarity)
现在我们已经有了词向量,就需要一种方法来量化各个词之间依据这些向量的相似程度。其中一种度量就是 cosine-similarity(余弦相似度)。我们将用它来找出彼此「接近」和「远离」的词。
我们可以把 n 维向量看作 n 维空间中的点。从这个视角看,L1 和 L2 距离有助于量化在这些点之间「需要走过」的空间量。另一种方法是考察两个向量之间的夹角。由三角学我们知道:

我们可以不计算实际的角度,而是把相似度保留为 $similarity = cos(\Theta)$ 的形式。形式上,两个向量 $p$ 和 $q$ 之间的 Cosine Similarity $s$ 定义为:
$$s = \frac{p \cdot q}{||p|| ||q||}, \textrm{ where } s \in [-1, 1] $$
cell 43
Question 2.2: Words with Multiple Meanings(多义词)(1.5 分)[code + written]
Polyseme(多义词)和 homonym(同形/同音异义词)是指具有不止一个含义的词(关于 polyseme 与 homonym 区别的更多内容,见这个 wiki page)。请找一个词,它要有至少两个不同的含义,且其 top-10 最相似词(按 cosine similarity)中同时包含来自两个含义的相关词。例如,"leaves" 的 top 10 里既有 "go_away" 的含义,也有 "a_structure_of_a_plant" 的含义;"scoop" 的 top 10 里既有 "handed_waffle_cone",也有 "lowdown"。你可能要试好几个多义词或同形异义词,才能找到一个。
请写出你找到的词,以及在 top 10 中出现的多个含义。你认为为什么你试过的许多多义词/同形异义词都不奏效(即 top-10 最相似词只包含该词其中一个含义)?
Note:你应当使用 wv_from_bin.most_similar(word) 函数来获得 top 10 最相似的词。该函数按照与给定词的 cosine similarity 对词表中的所有其他词进行排序。如需更多帮助,请查阅 GenSim documentation。
cell 44|代码(原件里的脚手架,原样保留)
# ------------------
# Write your implementation here.
# ------------------
cell 45
在此写下你的答案。
cell 46
Question 2.3: Synonyms & Antonyms(同义词与反义词)(2 分)[code + written]
在考虑 Cosine Similarity 时,通常更方便的是考虑 Cosine Distance,它就是 1 - Cosine Similarity。
请找三个词 $(w_1,w_2,w_3)$,其中 $w_1$ 与 $w_2$ 是同义词,$w_1$ 与 $w_3$ 是反义词,但 Cosine Distance $(w_1,w_3) <$ Cosine Distance $(w_1,w_2)$。
举例来说,$w_1$="happy" 与 $w_3$="sad" 的距离比与 $w_2$="cheerful" 更近。请另找一个满足上述条件的例子。找到例子后,请给出一个可能的解释,说明为什么会出现这种反直觉的结果。
你应当在这里使用 wv_from_bin.distance(w1, w2) 函数来计算两个词之间的 cosine distance。如需更多帮助,请参阅 GenSim documentation。
cell 47|代码(原件里的脚手架,原样保留)
# ------------------
# Write your implementation here.
# ------------------
cell 48
在此写下你的答案。
cell 49
Question 2.4: Analogies with Word Vectors(用词向量做类比)[written](1.5 分)
词向量已被证明有时能展现出求解类比的能力。
例如,对于类比 "man : grandfather :: woman : x"(读作:man 之于 grandfather,如同 woman 之于 x),x 是什么?
在下面的 cell 中,我们向你展示如何使用 GenSim documentation 中的 most_similar 函数,用词向量求出 x。该函数会找出与 positive 列表中的词最相似、且与 negative 列表中的词最不相似的词(同时会略去输入词本身,因为它们往往就是最相似的;见 this paper)。类比的答案将具有最高的 cosine similarity(即返回的数值最大)。
cell 50|代码(原件里的脚手架,原样保留)
# Run this cell to answer the analogy -- man : grandfather :: woman : x
pprint.pprint(wv_from_bin.most_similar(positive=['woman', 'grandfather'], negative=['man']))
cell 51
令 $m$、$g$、$w$、$x$ 分别表示 man、grandfather、woman 和答案的词向量。只使用向量 $m$、$g$、$w$ 以及向量算术运算符 $+$ 和 $-$,我们要最大化其与 $x$ 的 cosine similarity 的那个表达式是什么?
提示:回忆一下,词向量就是表示一个词的多维向量。用各向量任意摆放的位置画一个 2D 的例子,可能会有帮助。在坐标平面上,man 和 woman 相对于 grandfather 和答案会位于哪里?
cell 52
在此写下你的答案。
cell 53
Question 2.5: Finding Analogies(寻找类比)[code + written](1.5 分)
a. 对于上一个例子,显然 "grandmother" 才能补全这个类比。但请给出一个直观的解释,说明为什么 most_similar 函数会给出像 "granddaughter"、"daughter" 或 "mother" 这样的词?
cell 54
在此写下你的答案。
cell 55
b. 找一个按这些向量成立的类比例子(即目标词排在第一位)。在你的解答中,请按 x:y :: a:b 的形式写出完整的类比。如果你认为这个类比比较复杂,请用一两句话解释它为什么成立。
Note:你可能需要试很多个类比,才能找到一个可行的!
cell 56|代码(原件里的脚手架,原样保留)
# For example: x, y, a, b = ("", "", "", "")
# ------------------
# Write your implementation here.
# ------------------
# Test the solution
assert wv_from_bin.most_similar(positive=[a, y], negative=[x])[0][0] == b
cell 57
在此写下你的答案。
cell 58
Question 2.6: Incorrect Analogy(错误的类比)[code + written](1.5 分)
a. 下面,我们期望看到预期的类比 "hand : glove :: foot : sock",但实际看到的却是一个意外的结果。请给出一个可能的理由,解释为什么这个类比会得出这样的结果?
cell 59|代码(原件里的脚手架,原样保留)
pprint.pprint(wv_from_bin.most_similar(positive=['foot', 'glove'], negative=['hand']))
cell 60
在此写下你的答案。
cell 61
b. 再找一个按这些向量不成立的类比例子。在你的解答中,按 x:y :: a:b 的形式写出预期的类比,并根据词向量写出 b 的错误取值(在上一个例子中,这个值会是 '45,000-square')。
cell 62|代码(原件里的脚手架,原样保留)
# For example: x, y, a, b = ("", "", "", "")
# ------------------
# Write your implementation here.
# ------------------
pprint.pprint(wv_from_bin.most_similar(positive=[a, y], negative=[x]))
assert wv_from_bin.most_similar(positive=[a, y], negative=[x])[0][0] != b
cell 63
在此写下你的答案。
cell 64
Question 2.7: Guided Analysis of Bias in Word Vectors(词向量中偏见的引导性分析)[written](1 分)
意识到我们的 word embeddings 中隐含的偏见(性别、种族、性取向等)是很重要的。偏见可能很危险,因为使用这些模型的应用会借此强化刻板印象。
运行下面的 cell,考察 (a) 哪些词与 "man" 和 "profession" 最相似、且与 "woman" 最不相似,以及 (b) 哪些词与 "woman" 和 "profession" 最相似、且与 "man" 最不相似。指出女性关联词列表与男性关联词列表之间的差异,并解释它如何反映了性别偏见。
cell 65|代码(原件里的脚手架,原样保留)
# Run this cell
# Here `positive` indicates the list of words to be similar to and `negative` indicates the list of words to be
# most dissimilar from.
pprint.pprint(wv_from_bin.most_similar(positive=['man', 'profession'], negative=['woman']))
print()
pprint.pprint(wv_from_bin.most_similar(positive=['woman', 'profession'], negative=['man']))
cell 66
在此写下你的答案。
cell 67
Question 2.8: Independent Analysis of Bias in Word Vectors [code + written] (1 point)
使用 most_similar 函数,另找出一对能够说明这些向量体现出某种偏见的类比(analogy)。请简要解释你所发现的偏见示例。
cell 68|代码(原件里的脚手架,原样保留)
# ------------------
# Write your implementation here.
# ------------------
cell 69
在此写下你的答案。
cell 70
Question 2.9: Thinking About Bias [written] (2 points)
a. 给出一种可能的解释:偏见(bias)是如何进入 word vectors 的。你的解释应聚焦于 word vectors,而不是其他 AI 系统(例如 ChatGPT)中的偏见。如有必要,你可以用具体的历史事例来支撑你的解释。
cell 71
在此写下你的答案。
cell 72
b. 你可以用哪一种可能的方法来减轻 word vectors 所体现的偏见?请简要说明该方法,以及该方法的目标是什么。
cell 73
在此写下你的答案。
cell 74
Submission Instructions
- 点击 Jupyter Notebook 顶部的 Save 按钮。
- 选择 Edit -> Clear Outputs of All Cells。这会清除所有 cell 的输出(但会保留所有 cell 的内容)。
- 选择 Run -> Run All Cells。这会按顺序运行所有 cell,并需要几分钟时间。
- 重新运行完所有内容后,选择 File -> Save and Export Notebook as -> PDF(如果你看到类似 "nbconvert failed: Pandoc wasn't found" 的错误,可以先将它保存为 HTML)。选择 File -> Save and Export Notebook as -> HTML。这会把 notebook 保存为你电脑上的一个 HTML 文件。在网页浏览器中打开下载的 HTML 文件。在浏览器中按 Ctrl + P(Windows/Linux)或 Cmd + P(Mac)打开打印对话框。在打印对话框中,把目标(destination)改为 Save as PDF,然后点击 Save。 确保你所有的解答、尤其是编程部分都显示在 pdf 中,如果提供的代码因为代码 cell 中行不换行而被截断,那没关系。
- 查看 PDF 文件,确认你的所有解答都在、且显示正确。PDF 是评分者唯一会看到的东西!
- 在 Gradescope 上提交你的 PDF。
材料
官方地址(W26 ✓ 全部验证过可下载 ✓)。本地副本放
_local/(只服务本地、不外传)。
| 材料 | 链接 |
|---|---|
| 官方 notebook | a1.zip(0.1 MB) |
怎么自查
- 官方仓库自带的测试(
pytest/ sanity check)—— ⚠️ Gradescope 只对注册学生开放, 自学者以仓库自带测试为准; - 提交脚本:A3 / A4 提到
bash create_submission.sh。
这份作业和书的关系
见上表「配哪几讲」。等题面中英对照做完,这里会换成章节标题原文,并加定位校验
(指引指向一个不存在的标题会被 nav_check 报出来)。