训练模型的时候,有的时候使用的是One-hot,有的时候使用Word2Vec,这两个有什么区别的,什么时候使用One-hot,什么时候使用Word2Vec呢,谈一下自己的理解。
One-hot Encoding
One-hot编码又被称为“一位有效编码”,采用N位对特征的N个状态或者候选值进行编码,每个候选值是否有效用0和1表示,任意时刻编码只有一位有效。举个简单的例子,如果要区分水果,假设水果包括:
大小:["大"、"中"、"小"] => [0, 1, 2]
颜色:[“红色”、“橙色”、“黄色”、“绿色”、“青色”、“蓝色”、“紫色”] => [0, 1, 2, 3, 4, 5, 6]
味道:[“酸”、“甜”、“苦”、“辣”] = > [0, 1, 2, 3]
只考虑表皮颜色,那么大的绿色甜西瓜对应的[0, 3, 1],小的紫色酸葡萄应该是[2, 6, 0]就可以了,但是这个数据放入到机器学习算法里,并不能很好的应用于计算。
这个时候我们把西瓜的特征转换成One-hot的数据,“大”对应[1, 0, 0],“绿色”对应[0, 0, 0, 1, 0, 0, 0],“甜”对应[0, 1, 0, 0],完整的数字化特征拼合三个维度变成[1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0],我们会发现整个向量非常的稀疏。
代码示例如下:
注意,代码中,one-hot的encoder自动帮我们做了编码,不是像我们例子中的大小、颜色、味道的顺序,编码后的顺序可以参考打印的feature name的顺序。
#!/usr/bin/python
# -*- coding: UTF-8 -*-
from sklearn import preprocessing
enc = preprocessing.OneHotEncoder()
# add sample into encode. we need care the samples
enc.fit([["大","红色","酸"],["中","橙色","甜"],["小","黄色","苦"],["大","绿色","辣"],["大","青色","酸"],["大","蓝色","甜"],["大","紫色","辣"]])
# encode one of test
array = enc.transform([["大","橙色","苦"]]).toarray()
print("econd of the test data:")
print(array)
print()
print("feature name:")
print(enc.get_feature_names())
输出如下:
econd of the test data:
[[0. 1. 0. 1. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]]feature name:
['x0_中' 'x0_大' 'x0_小' 'x1_橙色' 'x1_紫色' 'x1_红色' 'x1_绿色' 'x1_蓝色' 'x1_青色'
'x1_黄色' 'x2_甜' 'x2_苦' 'x2_辣' 'x2_酸']
Word2Vec
Word2Vec的模型是基于神经网络来训练词向量的工具,通过一系列的模型和框架对原有的NNLM(神经网络语言模型)进行优化,简化了计算的同时保持很好的准确度。一般word2vec应用于自然语言处理,训练出来的词向量可以应用于其他场景,比如可以用于聚类或者其他深度学习。当然word2vec也可以用于一些时序数据的挖掘,比如学生学习情况分析、用户浏览记录分析等等,当然也可以用于个性化推荐和搜索领域。
word2vec是基于NNLM及其他语言模型的基础上做了优化产生的,常见的有CBOW模型、Skip-Gram模型、Hierarchical Softmax和Negative Sampling两个降低复杂度的方法。
1. CBOW (Continuous Bag-of-Words Model) 是一种根据上下文的词语预测当前词语的出现概率的模型,已知上下文,估计当前词语的语言模型
2. Skip-Gram和CBOW有点相反,是已知当前词语,预测上下文的模型
3. Hierarchical Softmax是借助了分类的概念,假设所有的词都作为输出,Hierarchical Softmax则是把这些词按类别区分,二叉树上的每个顶点看做是用哈夫曼编码构造的二分类器。算法实现时,模型会赋予这些抽象的中间节点一个合适的向量,真正的词会公用这些向量,这种近似的处理会显著带来性能上的提升而且不会损失很大的准确性。
4. Negative Sampling也是用二分类模拟多分类,区别在于它采用一些负例,调整模型参数使得可以区分正例和负例,它不是把分母中的所有词都算一遍,只算其中的一部分。
腾讯AI实验室提供了基于800万个中文单词和短语训练的200维词向量,可以应用于中文处理任务,比如命名实体识别(NER)和文本分类。如果想自己训练一套Word2Vec也是比较简单的,我们写了一个代码来训练一个自己的Word2Vec,可以参考一下。这里采用的sg=1,用的是Skip-Gram;如果把sg设置为其他值,则是用的CBOW。
#!/usr/bin/python
#encoding: UTF-8
from gensim.models.word2vec import Word2Vec
import sys
import re
import jieba
class DocumentHandler:
def __init__(self, file_path):
self.full_text = ''
self.init_stop_words()
self.read_file(file_path)
# read stop words
def init_stop_words(self):
# copy from: https://blog.csdn.net/lwc5411117/article/details/84256874
with open("./stop_word_cn.txt", 'r+', encoding='UTF-8') as stop_f:
full_words = stop_f.read()
self.stop_words = full_words.split('\n')
# read data from file
def read_file(self, file_path):
fi = open(file_path, 'r+', encoding='UTF-8')
self.full_text = fi.read()
fi.close()
def clean_en_text(self, text):
# keep English, digital and space
comp = re.compile('[^A-Z^a-z^0-9^ ]')
return comp.sub('', text)
# make Chinese text clean
def clean_zh_text(self, text):
# keep English, digital and Chinese
comp = re.compile('[^A-Z^a-z^0-9^\u4e00-\u9fa5]')
return comp.sub('', text)
# split text as sentences
def split_sentence(self, full_text):
full_text = self.clean_zh_text(full_text)
sents = re.split(u'[\n。]', full_text)
sents = [sent for sent in sents if len(sent) > 0]
# split sentece to word list
sent_word_list = []
for sent in sents:
words = []
cut_res = jieba.cut(sent)
for cut in cut_res:
# remove stop words and single word
if not(cut in self.stop_words) and len(cut) > 1:
if (cut == '的'):
print(len(cut))
words.append(cut)
sent_word_list.append(words)
return sent_word_list
# calculate word2vec
def calculate_word2vec(self):
# split content of article
sent_word_list = self.split_sentence(self.full_text)
# generate word vector with 200
# consider 5 words before and after with 10 words
# use sg=1 which means "skip-gram"
model = Word2Vec(size=200, workers=5, sg=1)
model.build_vocab(sent_word_list)
model.train(sent_word_list, total_examples = model.corpus_count, epochs = model.epochs)
model.save('./gensim_w2v_sg1_model') # save model
# test model
def get_similirity_words(self, word):
model = Word2Vec.load('gensim_w2v_sg1_model') # load model
sim_words = model.wv.most_similar(positive=[word])
#print(model[word]) # output word vector of the word
for word,similarity in sim_words:
print(word, similarity) # ouput similarity of th word
# main processor
def main(file_path, word):
docHandler = DocumentHandler(file_path)
docHandler.calculate_word2vec()
docHandler.get_similirity_words(word)
if __name__ == '__main__':
if len(sys.argv) < 3:
print('Usage: python word2vec.py <file path> <word>')
sys.exit()
file_path = sys.argv[1]
word = sys.argv[2]
main(file_path, word)
One-hot vs. Word2Vec
1. One-hot向量维度比较高,而且非常的稀疏;然而word embedding却是低维度的稠密数据,一般50~600维左右。当你使用One-hot作为分类器的特征向量时,特征向量会随着词表的增长而增长;而word embedding更加具有计算效率。
2. word embedding具有更好的泛化能力,因为相同的语义相似性具有相同的词向量,但是在one-hot中却不具有这个特征。词向量的每一对wi和wj有cosine的相似度cos(wi,wj)=0。