[智能体-284]:极简 Skip-Gram + 负采样 静态词向量训练 Demo(纯 Numpy,无第三方深度学习框架) 功能从原始短句→构建词表→随机初始化词向量表→逐样本迭代更新向量最关键的步骤→收敛得到静态词向量贴合前面通俗原理每次出错微调词向量表数值训练结束向量固定。python运行import numpy as np # 1. 原始语料预处理 # 实际的语料比这个多很多 corpus [ 苹果 很甜 香蕉 好吃, 苹果 水果 香蕉 水果, 汽车 行驶 轮胎 车子, 香蕉 果肉 苹果 果皮 ] # 分词、合并所有词语 all_words [] for sent in corpus: all_words.extend(sent.split()) # 构建词表 word-id字典单词索引号 vocab list(set(all_words)) word2id {w:i2 for i,w in enumerate(vocab)} # 补充特殊符号 word2id[[PAD]] 0 word2id[[UNK]] 1 # 向量矩阵 id2word {v:k for k,v in word2id.items()} # 训练参数 V len(word2id) # 词汇总数 emb_dim 3 # 词向量维度3方便查看 window 2 # 上下文窗口大小 neg_num 2 # 负采样数量1正2负 lr 0.02 # 学习率 epoch_total 80 # 总迭代轮数 # 初始化词向量表 V×emb_dim 随机小数训练前杂乱无意义 np.random.seed(1) emb_table np.random.uniform(-0.5,0.5,(V,emb_dim)) emb_table[0] np.zeros(emb_dim) # PAD固定全0 # 2. 生成SkipGram训练样本 def build_samples(word_list): samples [] n len(word_list) for cen_idx in range(n): center_w word_list[cen_idx] # 截取左右窗口上下文 start max(0,cen_idx-window) end min(n,cen_idxwindow1) for con_idx in range(start,end): if con_idx cen_idx: continue context_w word_list[con_idx] samples.append((center_w, context_w)) return samples all_sent_words [s.split() for s in corpus] train_samples [] for words in all_sent_words: train_samples build_samples(words) # 3. 负采样辅助函数 def get_neg_sample(pos_word_id): neg_ids [] while len(neg_ids)neg_num: rand_id np.random.randint(2,V) if rand_id ! pos_word_id: neg_ids.append(rand_id) return neg_ids # sigmoid激活 def sigmoid(x): return 1/(1np.exp(-x)) # 4. 迭代训练动态更新词向量表 for epoch in range(epoch_total): loss_sum 0.0 for cen_w, con_w in train_samples: cen_id word2id[cen_w] con_id word2id[con_w] # 取出中心词向量 cen_vec emb_table[cen_id].copy() # --------正样本更新目标预测接近1-------- score np.dot(cen_vec, emb_table[con_id]) pred sigmoid(score) grad pred - 1.0 # 反向更新上下文、中心词向量 emb_table[con_id] - lr * grad * cen_vec emb_table[cen_id] - lr * grad * emb_table[con_id] loss_sum -np.log(pred1e-8) # --------负样本更新目标预测接近0-------- neg_ids get_neg_sample(con_id) for nid in neg_ids: n_vec emb_table[nid] score_n np.dot(cen_vec, n_vec) pred_n sigmoid(score_n) grad_n pred_n emb_table[nid] - lr * grad_n * cen_vec emb_table[cen_id] - lr * grad_n * n_vec loss_sum -np.log(1-pred_n1e-8) # 每10轮打印损失 if epoch%10 0: print(fEpoch:{epoch:2d} | 平均损失:{loss_sum/len(train_samples):.4f}) # 5. 训练结束词向量固化静态词向量 print(\n训练完成最终静态词向量表) for wid in range(V): print(f{id2word[wid]:5} | {np.round(emb_table[wid],4)}) # 验证相似度水果类/交通工具向量距离 def cos_sim(v1,v2): return np.dot(v1,v2)/(np.linalg.norm(v1)*np.linalg.norm(v2)) print(\n相似度验证) print(苹果 vs 香蕉,round(cos_sim(emb_table[word2id[苹果]],emb_table[word2id[香蕉]]),3)) print(苹果 vs 汽车,round(cos_sim(emb_table[word2id[苹果]],emb_table[word2id[汽车]]),3))代码关键对应知识点初始化emb_table就是词向量表初始随机乱数一轮 Epoch遍历全部train_samples所有 (中心词 - 上下文)单步更新正例拉近向量、负例拉开向量直接原地修改 emb_table 数值训练结束不再更新emb_table固定 静态词向量表收敛表现loss 持续缓慢下降后期变化极小向量相对位置稳定。运行规律训练前苹果、香蕉、汽车向量随机相似度无规律训练后苹果↔香蕉相似度很高苹果↔汽车相似度极低符合语义。拓展工业真实使用真实项目不用手写 numpy用gensim.Word2Vec一行训练python运行from gensim.models import Word2Vec sent_list [s.split() for s in corpus] model Word2Vec(sentencessent_list,sg1,window2,vector_size3,negative2,epochs80,min_count1) # 取出静态词向量表 wv model.wv print(wv[苹果])