2013年,Tomas Mikolov领导的团队在Google发表了两篇看似简单的论文。他们用一个浅层神经网络,只做一件事情:根据词语的上下文来学习词语的向量表示。这个叫Word2Vec的方法,后来成为自然语言处理领域最具影响力的技术之一。
为什么一个简单的模型能产生如此深远的影响?答案藏在它的设计哲学里。Word2Vec不是试图让机器"理解"语言,而是让机器发现词语之间隐藏的数学关系。
分布式表示:一个词需要多少个数字
在Word2Vec之前,计算机表示词语最直接的方式是One-Hot编码。假设词表有10万个词,每个词就是一个10万维的向量,只有对应位置是1,其他位置都是0。这种方法有个致命问题:所有词之间的距离都相等。“猫"和"狗"的距离,跟"猫"和"汽车"的距离完全一样。
graph LR
subgraph One-Hot编码
A[猫] --> B["[1,0,0,0,...,0]"]
C[狗] --> D["[0,1,0,0,...,0]"]
E[汽车] --> F["[0,0,1,0,...,0]"]
end
G[距离计算] --> H["d(猫,狗) = √2"]
G --> I["d(猫,汽车) = √2"]
style B fill:#f9f,stroke:#333
style D fill:#9f9,stroke:#333
style F fill:#ff9,stroke:#333
这种表示方式忽略了词语之间的语义关系。更重要的是,当词表变大时,向量的维度会爆炸式增长,导致计算效率极低。
1986年,Geoffrey Hinton提出了分布式表示的概念:用一个低维稠密向量来表示每个词。比如用300个实数来表示一个词,而不是10万个二进制位。这些数字不是随意选取的,而是通过学习得到的。每个维度可能对应某种隐含的语义特征——虽然我们无法直接解释每个维度的含义,但这些维度共同编码了词语的语义信息。
graph TD
subgraph 分布式表示
A[猫] --> B["[0.23, -0.45, 0.78, ..., 0.12]"]
C[狗] --> D["[0.21, -0.42, 0.75, ..., 0.15]"]
E[汽车] --> F["[-0.56, 0.32, -0.18, ..., 0.89]"]
end
G[余弦相似度] --> H["sim(猫,狗) = 0.95"]
G --> I["sim(猫,汽车) = 0.12"]
style B fill:#f9f,stroke:#333
style D fill:#9f9,stroke:#333
style F fill:#f99,stroke:#333
用一个简单的类比来理解:如果用两个维度来描述一个人——外向程度和专业能力,那么[0.8, 0.9]和[0.75, 0.85]两个人会比较相似,因为他们在这两个维度上都很接近。同样,词向量中,相似含义的词会在向量空间中聚集在一起。
Word2Vec的核心思想
Word2Vec的核心假设来自语言学家J.R. Firth在1957年提出的观点:“你应该通过一个词周围的词来认识它。”
这个假设被称为分布式假设:出现在相似上下文中的词,往往具有相似的含义。比如"猫"和"狗"经常出现在"可爱”、“养”、“喂"这样的上下文中,所以它们的含义相近。而"汽车"的上下文通常是"开”、“买”、“油价”,与"猫"的上下文差异很大。
graph TD
subgraph 上下文相似性
A["'猫'的上下文"] --> B[可爱]
A --> C[养]
A --> D[喂]
A --> E[宠物]
F["'狗'的上下文"] --> G[可爱]
F --> H[养]
F --> I[喂]
F --> J[宠物]
K["'汽车'的上下文"] --> L[开]
K --> M[买]
K --> N[油价]
K --> O[交通]
end
P[结论] --> Q["猫 ≈ 狗 (上下文高度重叠)"]
P --> R["猫 ≠ 汽车 (上下文几乎不重叠)"]
style A fill:#f9f,stroke:#333
style F fill:#9f9,stroke:#333
style K fill:#f99,stroke:#333
Word2Vec把这个问题转化成一个预测任务:给定一个词的上下文,预测这个词是什么;或者给定一个词,预测它的上下文。通过解决这个预测任务,模型被迫学习词语之间的语义关系,这些关系最终编码在词向量中。
Skip-gram:从一个词预测多个词
Skip-gram是Word2Vec的第一种架构。它的逻辑是:给定中心词,预测它周围的上下文词。
假设有这样一句话:“The cat sits on the mat”。如果以"sits"为中心词,窗口大小为2,那么上下文词就是"The"、“cat”、“on”、“the”。Skip-gram会创建4个训练样本:(sits, The)、(sits, cat)、(sits, on)、(sits, the)。
Skip-gram的训练过程可以分解为几个步骤。首先,中心词通过Embedding矩阵转换成一个向量。然后,这个向量与Context矩阵中的每个上下文词向量做点积,得到相似度分数。最后,通过Softmax函数将这些分数转换成概率分布,目标是让真实的上下文词概率最大化。
用数学公式表示,给定中心词$w_t$,预测上下文词$w_{t+j}$的概率为:
$$p(w_{t+j}|w_t) = \frac{\exp(v'_{w_{t+j}}^T v_{w_t})}{\sum_{w \in V} \exp(v'_w^T v_{w_t})}$$其中$v_{w_t}$是中心词的输入向量,$v'_{w_{t+j}}$是上下文词的输出向量,$V$是整个词表。
Skip-gram的特点是每个上下文词都独立地与中心词配对。这意味着一个中心词可以产生多个训练样本,模型有更多机会学习低频词的表示。这也是Skip-gram对低频词效果更好的原因之一。
CBOW:从多个词预测一个词
CBOW(Continuous Bag of Words)是Word2Vec的第二种架构,它的逻辑与Skip-gram相反:给定上下文词,预测中心词。
还是以"The cat sits on the mat"为例,以"sits"为中心词,CBOW会把上下文词"The"、“cat”、“on”、“the"的向量取平均,然后用这个平均向量来预测中心词"sits”。
CBOW的数学公式可以表示为:
$$p(w_t|w_{t-c}, ..., w_{t+c}) = \frac{\exp(v'_{w_t}^T \bar{v})}{\sum_{w \in V} \exp(v'_w^T \bar{v})}$$其中$\bar{v} = \frac{1}{2c}\sum_{-c \le j \le c, j \ne 0} v_{w_{t+j}}$是上下文词向量的平均。
CBOW通过平均上下文词向量,减少了噪声的影响,训练速度比Skip-gram更快。但这种平均操作也意味着低频词的信息可能被高频词淹没。因此,CBOW对高频词效果更好,但对低频词的表示可能不够精细。
graph TD
subgraph Skip-gram
A1[中心词 sits] --> B1[Embedding向量]
B1 --> C1[预测 The]
B1 --> D1[预测 cat]
B1 --> E1[预测 on]
B1 --> F1[预测 the]
end
subgraph CBOW
G1[The] --> H1[向量平均]
I1[cat] --> H1
J1[on] --> H1
K1[the] --> H1
H1 --> L1[预测 sits]
end
两种架构的权衡
Skip-gram和CBOW各有优劣,选择哪种架构取决于具体任务和数据特点。
Skip-gram的优势在于对低频词的处理。因为每个上下文词都独立地与中心词配对,低频词即使出现次数少,也能获得足够多的训练信号。Skip-gram生成的词向量通常能捕捉更细致的语义信息。它的缺点是训练时间较长,因为每个窗口会产生多个训练样本。
CBOW的优势在于训练速度。通过平均上下文向量,模型能够更快地收敛。对于大规模语料库和主要关注高频词的任务,CBOW是更实用的选择。它的缺点是对低频词的表示可能不够准确。
从实际应用来看,当语料库较小或者需要捕捉低频词的语义时,Skip-gram是更好的选择。当语料库很大并且训练效率是首要考虑时,CBOW可能更合适。
2013年的原始论文中,Mikolov等人观察到Skip-gram生成的词向量在语义类比任务上表现更好。但2021年的一项研究发现,经过正确的调整,CBOW也能达到与Skip-gram相当的效果。这表明两种架构的差异可能部分来自于实现细节和超参数设置。
Softmax的计算困境
无论是Skip-gram还是CBOW,训练时都需要计算Softmax函数。这带来了一个严重的计算瓶颈。
Softmax的定义是:
$$\text{softmax}(z_i) = \frac{\exp(z_i)}{\sum_{j=1}^{V} \exp(z_j)}$$问题在于分母需要对整个词表$V$求和。当词表有几十万甚至上百万个词时,计算每个训练样本的Softmax都极其耗时。更糟糕的是,这个计算需要对每个训练样本都重复一遍。
graph LR
subgraph Softmax计算瓶颈
A[中心词向量] --> B[与所有词向量点积]
B --> C["计算 exp(v'·v)"]
C --> D["求和: Σexp(v'·v)"]
D --> E[归一化得到概率]
end
F[词表大小 V = 100,000] --> G[每个样本需要 O(V) 次计算]
G --> H[训练速度极慢]
style B fill:#f99,stroke:#333
style D fill:#f99,stroke:#333
2013年的论文中,Mikolov提出了两种优化方法来解决这个问题:层次Softmax和负采样。这两种方法都避免了对整个词表求和,将计算复杂度从$O(V)$降低到$O(\log V)$或更低。
负采样:用对比学习代替概率计算
负采样是最常用的优化技术,它的思想来自对比学习:不计算整个词表的概率分布,而是只区分"真实的上下文词"和"随机的非上下文词"。
具体来说,对于每个正样本(中心词,真实上下文词),模型会采样$K$个负样本(中心词,随机词)。训练目标是让正样本的相似度得分接近1,负样本的得分接近0。
graph TD
subgraph 负采样过程
A[中心词: sits] --> B[正样本: sits-cat]
A --> C[负样本1: sits-banana]
A --> D[负样本2: sits-computer]
A --> E[负样本3: sits-xyz]
end
F[训练目标] --> G["σ(v'cat·vsits) → 1"]
F --> H["σ(v'banana·vsits) → 0"]
F --> I["σ(v'computer·vsits) → 0"]
F --> J["σ(v'xyz·vsits) → 0"]
style B fill:#9f9,stroke:#333
style C fill:#f99,stroke:#333
style D fill:#f99,stroke:#333
style E fill:#f99,stroke:#333
负采样的损失函数是:
$$L = -\log \sigma(v'_{w_O}^T v_{w_I}) - \sum_{i=1}^{K} \mathbb{E}_{w_i \sim P_n(w)}[\log \sigma(-v'_{w_i}^T v_{w_I})]$$其中$\sigma(x) = \frac{1}{1+e^{-x}}$是Sigmoid函数,$P_n(w)$是负采样分布,通常取词频的3/4次方:$P_n(w) \propto f(w)^{0.75}$。
为什么取3/4次方?这有一个直觉解释:高频词本身出现概率就高,如果直接按词频采样,高频词会被过度采样。3/4次方相当于对高频词进行降权,让低频词有更多机会被采样。例如,如果一个词的频率是另一个词的100倍,那么经过3/4次方后,它的采样概率只提高约32倍。
负采样将计算复杂度从$O(V)$降低到$O(K)$,其中$K$通常取5-20。这意味着无论词表多大,每个训练样本的计算量都是固定的。这正是负采样被广泛采用的原因。
层次Softmax:用二叉树组织词表
层次Softmax采用了完全不同的策略:它把词表组织成一棵二叉树,每个词对应树的一个叶子节点。预测过程变成从树根到目标叶子的路径遍历。
Word2Vec使用Huffman树来组织词表。Huffman树的构造原则是让高频词离树根更近,低频词离树根更远。这样做的好处是:高频词的路径更短,计算更快。
具体来说,假设词表有$V$个词,Huffman树会有$V$个叶子节点和$V-1$个内部节点。每个内部节点存储一个向量,每个词的概率通过从树根到该词叶子节点的路径计算。
对于一条路径,每个内部节点做一个二分类决策:向左还是向右。如果向左走概率为$\sigma(v'_n^T v_{w_I})$,向右走概率为$1 - \sigma(v'_n^T v_{w_I})$,那么目标词的概率就是路径上所有决策概率的乘积。
层次Softmax的计算复杂度是$O(\log V)$,因为只需要遍历从树根到叶子节点的路径,路径长度等于词的编码长度。对于高频词,这个长度可能只有几步,远小于直接计算Softmax所需的$V$步。
graph TD
A[Root] -->|σ=0.8| B[Node1]
A -->|1-σ=0.2| C[Node2]
B -->|σ=0.7| D[the: 高频]
B -->|1-σ=0.3| E[cat: 中频]
C -->|σ=0.4| F[xyz: 低频]
C -->|1-σ=0.6| G[abc: 低频]
style A fill:#f9f,stroke:#333
style D fill:#9f9,stroke:#333
style E fill:#ff9,stroke:#333
style F fill:#f99,stroke:#333
style G fill:#f99,stroke:#333
负采样还是层次Softmax?
这两种优化方法各有特点。负采样的实现更简单,计算效率更高,特别适合大规模词表。它在实践中被更广泛地使用,尤其是在Gensim等工具的默认配置中。
层次Softmax的优势在于它保留了完整的概率模型,理论上更严谨。但由于实现复杂度较高,且需要预先知道所有词的频率来构建Huffman树,它的使用场景相对有限。
从原始论文的实验结果来看,负采样在大多数任务上的表现略优于层次Softmax。但具体选择取决于数据特点和任务需求。对于词表非常大(超过百万)的场景,负采样几乎是唯一实用的选择。
词向量的神奇性质
Word2Vec训练出的词向量有一个令人惊叹的性质:它们能通过向量运算来捕捉词语之间的语义关系。
最著名的例子是类比任务。给定"king"和"queen"、“man"和"woman"两组词,词向量满足这样的关系:
$$\vec{king} - \vec{man} + \vec{woman} \approx \vec{queen}$$这意味着词语之间的某种语义关系被编码成了向量空间中的线性变换。“king"到"queen"的方向,与"man"到"woman"的方向相似,都代表了"男性到女性"的性别变换。
graph TD
subgraph 词向量类比
A[king] -->|"− man"| B[中性向量]
B -->|"+ woman"| C[queen]
end
D[验证] --> E["king - man + woman ≈ queen"]
D --> F["Paris - France + Italy ≈ Rome"]
D --> G["bigger - big + small ≈ smaller"]
style A fill:#f9f,stroke:#333
style C fill:#9f9,stroke:#333
类似的关系还有很多:
- 国家与首都:$\vec{Paris} - \vec{France} + \vec{Italy} \approx \vec{Rome}$
- 比较级:$\vec{bigger} - \vec{big} + \vec{small} \approx \vec{smaller}$
- 动词时态:$\vec{walking} - \vec{walk} + \vec{swim} \approx \vec{swimming}$
这些类比关系的发现震惊了研究社区。它表明模型不仅仅是在记忆词语的共现统计,而是在某种程度上"学会"了语义概念。
词向量还能通过余弦相似度来衡量词语之间的相似性:
$$\text{similarity}(w_1, w_2) = \frac{\vec{w_1} \cdot \vec{w_2}}{|\vec{w_1}| |\vec{w_2}|}$$相似含义的词,如"cat"和"dog”,会有较高的余弦相似度。而语义无关的词,如"cat"和"computer”,相似度会很低。
这些性质让词向量成为下游任务的基础特征。文本分类、情感分析、命名实体识别等任务都能从预训练的词向量中获益。
Word2Vec的局限性
尽管Word2Vec取得了巨大成功,但它也存在一些根本性的局限。
第一个问题是一词多义。在Word2Vec中,每个词只有一个固定的向量表示。但很多词在不同上下文中有完全不同的含义。比如"bank"可以是"银行",也可以是"河岸";“apple"可以是"苹果公司”,也可以是"水果"。Word2Vec无法区分这些不同的含义,只能把所有含义混合在一个向量里。
第二个问题是词表外词(Out of Vocabulary,OOV)。Word2Vec只能为训练语料中出现过的词学习向量。如果遇到新词,模型无法处理。这在处理专业术语、人名地名、新兴词汇时特别成问题。
第三个问题是静态嵌入的本质。Word2Vec学习的是静态词向量,即无论词语出现在什么上下文中,它的向量表示都相同。这与人类对语言的理解方式不同——人类会根据上下文来理解词语的具体含义。
这些问题在后来的研究中得到了解决。2018年,ELMo首次提出了上下文相关的词向量,同一个词在不同句子中会有不同的向量表示。同年,BERT通过Transformer架构进一步提升了上下文建模能力。这些模型被称为动态词向量,它们解决了Word2Vec的一词多义问题。
对于OOV问题,后来的模型采用了子词(Subword)技术。比如BPE(Byte Pair Encoding)把词分解成更小的单元,这样即使遇到新词,也能通过已知的子词来推断其表示。
GloVe:全局统计与局部上下文的融合
2014年,斯坦福大学的Pennington等人提出了GloVe(Global Vectors),这是Word2Vec之后最重要的词嵌入方法之一。
GloVe的核心思想是结合全局统计和局部上下文。Word2Vec只关注局部窗口内的共现关系,而GloVe通过构建全局的词共现矩阵来捕捉整个语料库的统计信息。
具体来说,GloVe首先构建一个词共现矩阵$X$,其中$X_{ij}$表示词$i$和词$j$在某个窗口内共同出现的次数。然后定义目标函数:
$$J = \sum_{i,j=1}^{V} f(X_{ij})(w_i^T \tilde{w}_j + b_i + \tilde{b}_j - \log X_{ij})^2$$其中$f(x)$是权重函数,用于处理共现次数的巨大差异。这个函数让高频词对的权重不会过大,同时保证低频词对也能被有效训练。
GloVe的理论优势在于它明确地建模了词共现概率的比率。假设$P(i|j)$是词$i$在词$j$上下文中出现的概率,那么这些概率的比率能揭示词语之间的关系。例如,如果"ice"和"steam"的关系通过与"solid"、“gas”、“water”、“fashion"等词的共现概率比率来刻画。
从实践角度看,GloVe和Word2Vec的效果相当,各有优势。Word2Vec的分布式训练更简单,适合超大规模语料;GloVe的理论框架更清晰,适合理解词嵌入的本质。大多数情况下,两种方法产生的词向量可以互换使用。
从Word2Vec到现代大模型
Word2Vec的影响远远超出了它本身的技术贡献。它开创了预训练词向量的范式,为后来的ELMo、BERT、GPT等模型奠定了基础。
graph LR
A[Word2Vec 2013] -->|静态词向量| B[ELMo 2018]
B -->|上下文相关| C[BERT/GPT 2018]
C -->|大规模预训练| D[GPT-3 2020]
D -->|指令微调| E[ChatGPT 2022]
style A fill:#f9f,stroke:#333
style B fill:#ff9,stroke:#333
style C fill:#9f9,stroke:#333
style D fill:#9ff,stroke:#333
style E fill:#f99,stroke:#333
现代大模型的Embedding层本质上就是Word2Vec思想的延续。虽然这些模型采用了更复杂的架构(如Transformer)和更大的训练数据,但核心思想仍然是:通过大量无标注文本学习词语的分布式表示。
区别在于,Word2Vec学习的是静态词向量,而现代模型学习的是上下文相关的动态表示。在BERT中,每个词的表示不仅取决于词本身,还取决于整个句子的上下文。这通过Transformer的自注意力机制实现,模型可以"看到"句子中的所有词,并据此调整每个词的表示。
另一个重要的演进是词表粒度的变化。Word2Vec使用词级别的词表,这带来了OOV问题。现代大模型普遍采用子词级别的词表,如BPE、WordPiece、Unigram等。这些方法把罕见词分解成常见的子词单元,既解决了OOV问题,又减少了词表大小。
从计算效率的角度,Word2Vec的负采样思想也被现代模型继承。BERT等模型在预训练时采用的Masked Language Model,本质上也是一种对比学习:区分被Mask的真实词和随机采样的负样本。
工业界应用:从词语到物品
Word2Vec的应用不仅限于自然语言处理,它还被广泛用于推荐系统、广告系统等领域。
核心思想是:把用户的行为序列看作"句子”,把物品看作"词"。如果一个用户连续购买了A、B、C、D四种商品,那么这些商品的向量可以通过类似Word2Vec的方法学习。学习到的物品向量能够捕捉物品之间的相似性和关联性。
Airbnb在2018年发表了一篇论文,详细描述了他们如何用Word2Vec改进房源推荐。他们把用户的点击序列看作句子,每个房源看作一个词。通过Skip-gram模型学习房源向量,用于"相似房源"推荐。
Airbnb的实践中有几个值得关注的创新。首先,他们发现负采样的选择至关重要。如果负样本从全球房源池中随机采样,模型可能学会区分"巴黎"和"纽约",而不是区分巴黎内部的房源。因此,他们采用"同市场负采样",确保负样本来自同一城市。
其次,他们解决了冷启动问题。对于新上架的房源,没有用户点击数据怎么办?他们的解决方案是用地理位置最近的三个房源向量的平均值来初始化新房源的向量。这个简单的技巧在实践中效果很好。
Spotify在音乐推荐中使用了类似的方法。用户的播放序列被视为句子,每首歌曲被视为词。学习到的歌曲向量可以用于计算歌曲相似度,构建个性化推荐。
Yahoo在广告系统中应用了Word2Vec,把用户的搜索查询和广告点击序列视为句子。学习到的查询向量和广告向量可以用于广告匹配,提高广告相关性。据报道,这种方法带来了9%的点击率提升。
这些工业应用展示了一个重要观点:Word2Vec的核心思想——通过上下文关系学习分布式表示——具有极强的泛化能力。只要存在序列数据,都可以尝试用这种方法学习元素的向量表示。
实践指南:如何训练和使用词向量
如果需要训练自己的词向量,有几个关键参数需要注意。
窗口大小决定了上下文的范围。小窗口(2-5)更关注句法相似性,学到的词向量会让可互换的词更接近,比如同义词、反义词。大窗口(5-10或更大)更关注语义相关性,学到的词向量会让相关主题的词更接近。对于一般用途,窗口大小5是一个常用的默认值。
向量维度影响表示能力和计算效率。更小的维度(50-100)计算快,但可能损失信息;更大的维度(300-500)表示能力强,但需要更多数据和训练时间。300维是实践中常用的选择,Google发布的Word2Vec模型就是300维。
负采样数量通常设置为5。对于小数据集,可以增加到10-20;对于大数据集,2-5就够了。采样分布通常使用词频的3/4次方。
最小词频过滤掉低频词。出现次数少于这个阈值的词会被忽略,不参与训练。常用值是5-10,这可以有效过滤掉拼写错误和噪声词。
对于大多数应用场景,不需要从头训练词向量。使用预训练的词向量通常是更好的选择。Google发布的Google News词向量(300维,300万词)是一个常用选择。GloVe项目也提供了多个预训练模型,包括维基百科和Common Crawl数据集训练的模型。
使用预训练词向量时,可以直接加载向量矩阵,用于初始化下游模型的Embedding层。对于领域特定的任务,可以基于预训练向量进行微调,让词向量适应特定领域的语义。
技术遗产与未来展望
Word2Vec的历史意义在于它证明了:用简单的方法、大规模的数据,可以学到有意义的语义表示。这个发现启发了后来的预训练语言模型研究。
从技术角度看,Word2Vec的贡献可以总结为三点。第一,它提出了高效的训练方法(负采样、层次Softmax),使得在大规模语料上训练词向量成为可能。第二,它展示了词向量捕捉语义关系的能力,类比任务成为评估词嵌入质量的标准方法。第三,它开创了预训练词向量的范式,为后来的预训练语言模型奠定了基础。
Word2Vec的局限性也推动了后续研究。一词多义的问题催生了上下文相关的词向量(ELMo、BERT)。OOV问题促进了子词嵌入技术的发展(FastText、BPE)。静态表示的局限推动了动态表示的研究。
今天,当我们使用ChatGPT、Claude这样的大语言模型时,可能很少直接想到Word2Vec。但这些模型的Embedding层、预训练范式、分布式表示的思想,都能追溯到Word2Vec的工作。
技术的演进往往是渐进式的。Word2Vec站在前人工作的基础上——从Hinton的分布式表示,到Bengio的神经语言模型,再到分布式假设的长期研究。它用工程上的创新解决了计算效率问题,让词嵌入技术真正走向实用。
当2013年Mikolov在Google发表那些论文时,他可能没有预料到这个看似简单的模型会产生如此深远的影响。但技术的价值往往不在于复杂性,而在于它能否揭示问题的本质。Word2Vec揭示了语言的一个本质:词语的意义在于它与其他词语的关系。
参考文献:
- Mikolov, T., et al. (2013). Efficient Estimation of Word Representations in Vector Space. arXiv:1301.3781.
- Mikolov, T., et al. (2013). Distributed Representations of Words and Phrases and their Compositionality. NIPS 2013.
- Goldberg, Y., & Levy, O. (2014). word2vec Explained: deriving Mikolov et al.’s negative-sampling word-embedding method. arXiv:1402.3722.
- Pennington, J., Socher, R., & Manning, C. (2014). GloVe: Global Vectors for Word Representation. EMNLP 2014.
- Grbovic, M., et al. (2018). Real-time Personalization using Embeddings for Search Ranking at Airbnb. KDD 2018.
- Bernhardsson, E. (2015). Annoy: Approximate Nearest Neighbors in C++/Python. GitHub.
- Hinton, G. (1986). Learning distributed representations of concepts. Proceedings of the eighth annual conference of the cognitive science society.
- Bengio, Y., et al. (2003). A Neural Probabilistic Language Model. JMLR.
- Firth, J. R. (1957). A synopsis of linguistic theory, 1930-1955. Studies in Linguistic Analysis.
- Devlin, J., et al. (2019). BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding. NAACL 2019.
- Peters, M., et al. (2018). Deep contextualized word representations. NAACL 2018.
- Bojanowski, P., et al. (2017). Enriching Word Vectors with Subword Information. TACL.