基于电影字幕的Genre预测:Word2Vec替代BOW的实现方案
当然可行!用Word2Vec替代BOW做字幕的Genre预测不仅完全可行,还能解决BOW的核心短板——BOW只统计词频,完全忽略了词与词之间的语义关联,而Word2Vec能把语义相似的词映射到向量空间中的相近位置,这对理解字幕里的对话语境、提升Genre预测的准确性帮助很大。下面我就一步步给你讲具体怎么实现:
一、核心思路梳理
之前你用BOW时,每个字幕样本是一个一维的词频向量;换成Word2Vec后,每个样本会变成由多个词向量组成的序列(因为字幕是一串连续的文本),或者你也可以把序列里的词向量做平均,得到一个固定维度的句向量。两种方式都能适配Keras模型,看你更关注序列上下文还是想简化模型。
二、具体实现步骤
1. 先把字幕文本重新预处理
你之前用的是已经转好的BOW向量,现在需要回到原始字幕文本,做基础的文本预处理:
- 分词:英文用
nltk.word_tokenize,中文用jieba这类工具,把每段字幕拆成单个词的序列 - 清洗:去除停用词、标点、数字,把所有词统一成小写(英文)
- 最终输出:每段字幕变成类似
["hello", "there", "this", "is", "a", "thriller"]的词序列列表
2. 训练或加载Word2Vec模型
你有两种选择,根据你的数据量来选:
选项1:用自己的字幕语料训练Word2Vec
如果你的字幕数据量足够(比如几千段以上),自己训练的模型会更贴合电影领域的语境,用gensim库就能轻松实现:
from gensim.models import Word2Vec # corpus是所有字幕样本的词序列列表,比如corpus = [["i", "love", "action"], ["scary", "scene", "horror"], ...] model = Word2Vec( sentences=corpus, vector_size=128, # 词向量维度,一般选50-300,根据数据量调整 window=5, # 上下文窗口大小,即每个词会参考前后5个词的语义 min_count=2, # 忽略出现次数少于2的生僻词 workers=4 # 多线程加速训练 ) # 保存模型,方便后续调用 model.save("movie_subtitle_word2vec.model")
选项2:用预训练的Word2Vec模型
如果你的字幕数据量很小,可以直接用公开的预训练模型(比如Google News的Word2Vec模型),加载后就能直接获取词向量,不用自己训练。
3. 把字幕样本转换成Word2Vec向量序列
现在要把每个词序列转换成对应的词向量序列,同时处理长度不一致的问题:
from keras.preprocessing.sequence import pad_sequences import numpy as np # 加载训练好的模型 model = Word2Vec.load("movie_subtitle_word2vec.model") word_vec_dim = model.vector_size # 比如128 def word_seq_to_vec_seq(word_seq): # 把每个词转换成向量,不在词表里的词用全0向量替代 vec_seq = [] for word in word_seq: if word in model.wv: vec_seq.append(model.wv[word]) else: vec_seq.append(np.zeros(word_vec_dim)) return vec_seq # 假设所有预处理后的字幕词序列存在texts列表里 X_vec_seqs = [word_seq_to_vec_seq(seq) for seq in texts] # 统一序列长度:取所有字幕长度的95分位数,或者固定一个最大值 max_seq_len = 100 X_padded = pad_sequences(X_vec_seqs, maxlen=max_seq_len, dtype="float32")
4. 重构Keras模型
现在输入变成了三维张量(样本数 × 序列长度 × 词向量维度),你可以选两种模型结构:
结构1:用LSTM捕捉序列上下文(推荐)
因为字幕是连续的对话,用循环神经网络(LSTM/GRU)能捕捉上下文的语义关联,更适合Genre预测:
from keras.models import Sequential from keras.layers import LSTM, Dense, Dropout model = Sequential() # 输入形状是(None, max_seq_len, word_vec_dim) model.add(LSTM(64, input_shape=(max_seq_len, word_vec_dim))) model.add(Dropout(0.3)) # 防止过拟合 model.add(Dense(32, activation="relu")) # 输出是4种Genre的独热编码,用softmax激活 model.add(Dense(4, activation="softmax")) # 编译模型,和你之前用BOW时的配置一致即可 model.compile(optimizer="adam", loss="categorical_crossentropy", metrics=["accuracy"])
结构2:词向量平均成句向量,用全连接层(简化版)
如果不想用复杂的序列模型,可以把每个字幕的所有词向量做平均,得到一个固定维度的句向量,然后用全连接层训练:
# 把每个样本的词向量做平均 X_avg_vecs = np.array([np.mean(vec_seq, axis=0) for vec_seq in X_vec_seqs]) # 构建全连接模型 model = Sequential() model.add(Dense(64, activation="relu", input_shape=(word_vec_dim,))) model.add(Dropout(0.3)) model.add(Dense(32, activation="relu")) model.add(Dense(4, activation="softmax")) model.compile(optimizer="adam", loss="categorical_crossentropy", metrics=["accuracy"])
5. 训练模型
这部分和你之前用BOW的流程完全一致,把处理好的输入(X_padded或X_avg_vecs)和独热编码标签y输入模型即可:
# 假设y是你的独热编码标签数组,形状为(样本数, 4) history = model.fit( X_padded, y, epochs=100, batch_size=32, validation_split=0.2 # 用20%的数据做验证 )
三、额外小贴士
- 如果自己训练Word2Vec,语料量越大效果越好,要是你的字幕数据不多,可以找一些公开的电影剧本语料来扩充训练集。
- 对比BOW和Word2Vec的效果时,尽量保持其他参数(比如epochs、batch_size、全连接层结构)一致,这样对比结果才公平。
- 也可以试试预训练词向量微调:把预训练的词向量作为Keras Embedding层的初始权重,设置
trainable=True,让模型在训练时根据你的数据微调词向量,这往往能得到更好的效果。
内容的提问来源于stack exchange,提问作者Arun Prakash

