一、开篇:从“词”到“向量”的跨越
昨天我们用一整天的完成了文本预处理,把脏乱的原始文本变成了干净的词序列:
原始:I absolutely LOVED the movie!!! It was soooo amazing 😍😍😍 #mustwatch处理后:['absolutely', 'love', 'movie', 'amazing', 'mustwatch']
现在的问题是:这些词怎么输入给模型?
神经网络只能处理数字,不能直接处理“movie”、“love”这样的字符串。
最直接的想法:独热编码(One-hot encoding)
movie → [0,0,0,1,0,0,...] (假设词表大小10000,movie是第4个词)love → [0,1,0,0,0,0,...]
独热编码的致命问题:
维度灾难:词表越大,向量越长(10000维)
语义鸿沟:任何两个词的距离都是相同的,无法表达语义相似性(“猫”和“狗”的距离与“猫”和“汽车”一样远)
解决方案:词嵌入(Word Embedding)
将词映射到低维稠密向量(如300维),使得:
二、词嵌入的核心思想:分布假设
“You shall know a word by the company it keeps.”
—— J.R. Firth (1957)
分布假设:词语的含义由其上下文决定。如果两个词经常出现在相似的上下文中,它们的语义应该相似。
例:
“咖啡”和“茶”都出现在“我喝了一杯”后面,所以它们的向量应该相近。
三、主流词嵌入模型
3.1 Word2Vec (2013) —— 词嵌入的奠基者
Google发布的Word2Vec有两种训练方式:CBOW和Skip-gram。
Skip-gram示意图:
输入:中心词 "coffee" → 预测上下文 ["I", "drank", "a", "cup", "of"]
from gensim.models import Word2Vecfrom nltk.tokenize import word_tokenizeimport nltknltk.download('punkt')# 准备语料(已经预处理的句子列表)sentences = [ "I love natural language processing".split(), "Word embeddings are amazing".split(), "I love coffee and tea".split(), "Coffee and tea are both beverages".split()]# 训练Word2Vec模型model = Word2Vec(sentences, vector_size=100, window=5, min_count=1, workers=4)# 查看词向量vector = model.wv['coffee']print(f"'coffee'的向量(前10维):{vector[:10]}")# 找相似词similar = model.wv.most_similar('coffee')print("与'coffee'最相似的词:", similar)# 词类比:国王 - 男人 + 女人 ≈ 王后result = model.wv.most_similar(positive=['woman', 'king'], negative=['man'])print("国王 - 男人 + 女人 ≈", result[0][0])
3.2 GloVe (2014) —— 全局共现统计
斯坦福提出的GloVe(Global Vectors)结合了全局矩阵分解和局部上下文窗口的优点。
原理:利用词-词共现矩阵,通过加权最小二乘学习词向量。
特点:
from gensim.scripts.glove2word2vec import glove2word2vecfrom gensim.models import KeyedVectors# 将GloVe格式转为Word2Vec格式glove_input_file = 'glove.6B.100d.txt'word2vec_output_file = 'glove.6B.100d.word2vec.txt'glove2word2vec(glove_input_file, word2vec_output_file)# 加载模型model = KeyedVectors.load_word2vec_format(word2vec_output_file, binary=False)# 使用similar = model.most_similar('coffee')print("GloVe中与'coffee'最相似的词:", similar)
3.3 FastText (2016) —— 子词信息的引入
Facebook提出的FastText在Word2Vec的基础上引入了子词信息(subword)。
核心创新:将词拆分成字符级别的n-gram,每个n-gram有自己的向量,词的向量由所有n-gram向量求和得到。
"coffee" → <co, coff, offe, ffee, fee> 等字符n-gram
优势:
from gensim.models import FastText# 训练FastTextmodel = FastText(sentences, vector_size=100, window=5, min_count=1, workers=4)# 对于未登录词,仍能得到向量vector_oov = model.wv['never_seen_word']print("未登录词向量(前10维):", vector_oov[:10])# 相似词查找similar = model.wv.most_similar('coffee')print("FastText中与'coffee'最相似的词:", similar)
四、词嵌入的评估
4.1 内在评估
4.2 外在评估
五、词嵌入的应用场景
六、实战项目:基于词向量的文本分类
6.1 用预训练词向量做文本分类
import numpy as npfrom sklearn.model_selection import train_test_splitfrom sklearn.linear_model import LogisticRegressionfrom sklearn.metrics import accuracy_scoreimport gensim.downloader as api# 加载预训练的GloVe词向量(第一次运行会下载)print("加载GloVe模型...")glove = api.load("glove-twitter-25") # 25维,Twitter语料# 示例数据:简单情感句子texts = [ "I love this movie", "This film is great", "Terrible waste of time", "I hate it", "Absolutely wonderful", "Worst movie ever", "Pretty good actually", "Not bad at all"]labels = [1, 1, 0, 0, 1, 0, 1, 1] # 1:正面 0:负面def sentence_to_vector(sentence, model): """将句子中所有词的词向量平均,作为句子向量""" words = sentence.lower().split() vectors = [model[word] for word in words if word in model] if len(vectors) == 0: return np.zeros(model.vector_size) return np.mean(vectors, axis=0)# 转换为特征向量X = np.array([sentence_to_vector(s, glove) for s in texts])y = np.array(labels)# 划分训练集和测试集X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)# 训练分类器clf = LogisticRegression()clf.fit(X_train, y_train)# 评估y_pred = clf.predict(X_test)print("准确率:", accuracy_score(y_test, y_pred))
6.2 可视化词向量
import matplotlib.pyplot as pltfrom sklearn.decomposition import PCA# 选择一些词words = ['coffee', 'tea', 'milk', 'water', 'juice', 'dog', 'cat', 'fish', 'bird', 'rabbit']# 获取向量vectors = np.array([glove[word] for word in words if word in glove])# PCA降维到2维pca = PCA(n_components=2)vectors_2d = pca.fit_transform(vectors)# 绘制plt.figure(figsize=(8,6))for i, word in enumerate(words): if word in glove: plt.scatter(vectors_2d[i,0], vectors_2d[i,1]) plt.annotate(word, (vectors_2d[i,0], vectors_2d[i,1]))plt.title("词向量PCA可视化")plt.show()
七、词向量的局限与发展
7.1 固有局限
7.2 下一代:上下文相关的词向量
2018年之后,BERT、ELMo等模型通过深度网络生成了动态词向量——同一个词在不同语境下有不同的向量。
句子1:"He deposited money in the bank."句子2:"He sat on the river bank."
BERT能根据上下文给两个“bank”不同的向量表示。总结:向量,让词语有了“数学意义”
词嵌入是NLP从符号主义走向连接主义的关键一步。它让词语变成了向量,使得我们可以用数学运算来处理语义——这是深度学习革命的前奏。
今天你学会了: