这次的比赛在词向量的处理上使用了很多方法和分析手段,包括之前分析词向量对文本的覆盖的文章,针对覆盖程度对原始数据进行文本的预处理。到后面的使用tfidf和embedding结合的方法表示文本。这些方法都是在丰富和完整文本的表示。也都在效果上得到了提升。
但是在比赛中有一点我一直很困惑,就是使用Google提供的Word2Vec表示文本会降低分类准确率,讨论区也有参赛这对这个问题提出了疑问,但是没有解答,大家都默认用了效果好的。几乎所有的kernel使用的是另外的两个训练好的词向量glove和paragram或者是wiki的词向量。
后来我想,既然使用训练好的词向量会出现效果降低的问题,那我们如果使用针对这个数据集的训练出来的词向量会不会效果好一点,(就是也不用训练好的词向量+fine_tune模式,直接训练生成词向量),而且这个比赛训练集合测试集不变,这就给这个方法的实施提供了条件。
所以就去查了一下相关的实现方法和接口,发现自训练的word2vec一般都会用gensim提供的接口。这篇文章讲了这个接口常用的方法和一个小例子,可以从这里入手,我就不多做说明了。这篇文章讲了Word2Vec的原理,感兴趣的同学可以看一下。
下面说一下针对这个比赛的训练集和测试集训练word2vec的过程。使用的是gensim的Word2Vec接口
首先读入数据并对数据进行预处理
train_df = pd.read_csv('../input/train.csv')
test_df = pd.read_csv('../input/test.csv')
train_df["question_text"] = train_df["question_text"].str.lower()
test_df["question_text"] = test_df["question_text"].str.lower()
train_df["question_text"] = train_df["question_text"].apply(lambda x: clean_text(x))
test_df["question_text"] = test_df["question_text"].apply(lambda x: clean_text(x))
train_df["question_text"] = train_df["question_text"].apply(lambda x: replace_typical_misspell(x))
test_df["question_text"] = test_df["question_text"].apply(lambda x: replace_typical_misspell(x))
train_X = train_df["question_text"].fillna("_##_").values
test_X = test_df["question_text"].fillna("_##_").values
train_y = train_df['target'].values
然后使用tokenizer对数据进行序列化并padding到等长
tokenizer = Tokenizer(num_words=max_features)
tokenizer.fit_on_texts(list(train_X)+list(test_X))
train_X = tokenizer.texts_to_sequences(train_X)
test_X = tokenizer.texts_to_sequences(test_X)
train_X = pad_sequences(train_X, maxlen=maxlen)
test_X = pad_sequences(test_X, maxlen=maxlen)
word_index = tokenizer.word_index
这时候我们可以下数据集里一共有多少单词
nb_words = len(word_index) # 200593
print(nb_words)
200593个单词,我们接下来就要对这些单词训练他们的word2vec词向量。做法是调用gensim.models的Word2Vec接口得到词向量模型
all_data=pd.concat([train_df["question_text"],test_df["question_text"]])
model = Word2Vec([[word for word in sentence.split(' ')] for sentence in all_data.values],
size=200, window=5, iter=10, workers=11, seed=2018, min_count=2)
这个时候我们可以看一下训练好词向量的效果如何。来看看与一个单词相近的词向量有哪些,比如我们看一下和good这个词的词向量相近的词是谁:
req_count = 10
for key in model.wv.similar_by_word('good', topn =100):
req_count -= 1
print(key[0], key[1])
if req_count == 0:
break;
decent 0.5868578553199768
great 0.5853617191314697
better 0.5842558145523071
best 0.5734809041023254
perfect 0.5464102029800415
helpful 0.5342572927474976
recommended 0.5127782821655273
suitable 0.5072467923164368
nice 0.5057531595230103
legit 0.48905688524246216
可以看到效果还可以。最后我们就建立embedding矩阵就行了。
nb_words = min(max_features, len(word_index))
embedding_word2vec_matrix = np.zeros((nb_words, 200)) # (95000, 200)
for word, i in word_index.items():
if i >= max_features: continue
embedding_vector = model[word] if word in model else None
if embedding_vector is not None:
count += 1
embedding_word2vec_matrix[i] = embedding_vector
else:
unk_vec = np.random.random(200) * 0.5
unk_vec = unk_vec - unk_vec.mean()
embedding_word2vec_matrix[i] = unk_vec