CNN在NLP领域的应用(2) 文本语义相似度计算
2016-12-20 13:50
1266 查看
本文的内容是紧接着上一篇文章的内容,上一篇文章讲到 CNN在文本分类领域的应用,本文将讨论其在文本相似度计算方面的应用,文本相似度可以用于搜索引擎、文本去重、文本挖掘、推荐系统等多个领域,也是NLP中需要处理的一类任务。
0.文本相似度计算
所谓文本相似度计算,是指给定两个文本(一般为字符串),并通过算法给出其相似度幅度的衡量,一般计算结果为0-1之间的值,下面简单介绍几种,较为传统和常见的文本相似度计算的方法,然后再姐好啊使用CNN的方法。
一.传统的文本相似度计算的方法:
1.1 最长公共子序列及最长公共子串:
该种方法思路较为简单,直接使用lcs(最长公共子序列)算法,设字符串A长度为L1,字符串长度为L2,它们的最长公共子串长度为lcs,那么这两个字符串相似度的度量为 sim = 2lcs/(L1+L2)。LCS算法的具体描述可参见LCS 。
1.2 余弦相似度:
该种方法思路一样非常简单,将两个字符串进行分词,并将其量化为多维空间上的点,计算两个文本的余弦相似度即可, 具体公式和LCS算法的方式类似,就不在赘述 。
1.3 其他
除了以上两种思路外,还有编辑距离 、 jaccard距离 、 simhash 等等,使用的方法也较为类似,因为不是本文的重点,因此也不在赘述。
二. 使用CNN的方法
2.1 使用CNN来进行文本相似度计算的原因:
首先这里文本相似度计算的输入是指 两个字符串,比如字符串A = ‘书写代码,改变世界’ 字符串B = ‘地产兴邦,称霸世界’,那么我们如何计算他们的文本相似度呢,一个是直接用本文中提到过的 传统的文本相似度计算方法来计算,如果任务的要求不高,直接拿字符串A和B计算即可。那么为什么又需要使用cnn和词向量的方法来计算文本相似度呢???
这是因为传统文本相似度算法,侧重于文本本身的相似度计算,因此需要大量的归一化的过程,例如中文数字和阿拉伯数字的归一化,中英文 单位的归一化(例如kg和千克)。但是即使通过归一化的方法,仍然有很多语义相似文本无法通过这种方法得到满意的结果。比如两道数学题, 题1 = '一个苹果+二个苹果等于多少个苹果?', 题2=‘一个香蕉+二个香蕉等于多少个香蕉’,显然这两道题语义是高度接近的,但是用传统的文本相似度计算方法,计算得到的相似度是非常低的,这不能满足现代互联网或其他领域对文本语义相似度任务的需要,于是需要使用cnn和词向量的方式来计算文本语义相似度,这是因为词向量的固有特性,如果对词向量有所不了解可参见我的上一篇文章文本分类
中关于词向量的介绍。
2.2 文本预处理的过程:
这里文本预处理过程和文本分类一文中文本预处理的内容大同小异,看过该文的可以直接跳过这一步。
首先文本相似度计算输入的 来源是两个字符串,比如字符串A = ‘书写代码,改变世界’ 字符串B = ‘地产兴邦,称霸世界’。
以字符串A的处理来说明问题,这部分主要是分3步,共4种状态。1.将原始文本分词并转换成以词的序列 2.将词序列转换成以词编号(每个词表中的词都有唯一编号)为元素的序列 3.将词的编号序列中的每个元素(某个词)展开为词向量的形式。下面通过一张图(本人手画简图。。。。囧)来表示这个过程,如下图所示:
上述图片,以'书写代码,改变世界' 这一文本字符串为例,介绍了将其转换成词向量为元素的序列表示,最后得到了一个2维矩阵,该矩阵可用于后续神经网络的训练等操作。
2.3 神经网络模块的设计
本文关于神经网络设计的思想来自于以下keras中文文档中的:泛型编程 这一届中微博相似度计算启发而来。并结合了实际需要而设计的。我下面结合一张神经网络设计图,来说明本文中所使用的神经网络,具体设计图(又是手画图,囧)如下:
简要介绍下上面的图,第一层数据输入层,将文本序列展开成词向量的序列,之后两个不同的输入流
,这里因为有两个输入(字符串A和字符串B),垂直方向则放置了2个完全一样的层组合(卷积层、激活层、池化层的组合)。之后连接全连接层和激活层,激活层采用sigmoid并输出该文本属于某类的概率,这个输出的值是个0-1之间的浮点数,该值代表 文本A和文本B的相似程度,值越大相似程度越大。
2.4 编程实现所需要的框架和数据集等
2.4.1 框架:本文采用keras框架来编写神经网络,关于keras的介绍请参见莫言大神翻译的 keras中文文档
2.4.2 数据集:采用的是1w条,标记好是否是语义相近的相似文本,比如一道数学题和另一道数学题,如果语义相似则标记为1,如果不同则标记为0.
2.4.3 词向量:虽然keras框架已经有embedding层,但是本文采用glove词向量作为预训练的词向量,glove的介绍和下载地址如下(打开会比较慢):
http://nlp.stanford.edu/projects/glove/
2.5 代码和相应的注释
在2.3部分已经通过一张图介绍了神经网络的设计部分,但是考虑到不够直观,这里还是把所使用的代码,罗列如下,采用keras编程,关键部分都已经罗列注释:
三.总结
本文描述了如何使用深度学习和keras框架构建一个文本分类器的全过程,并给出了相应的代码实现,为了方便大家使用,下面给出本文代码的github下载地址
。
0.文本相似度计算
所谓文本相似度计算,是指给定两个文本(一般为字符串),并通过算法给出其相似度幅度的衡量,一般计算结果为0-1之间的值,下面简单介绍几种,较为传统和常见的文本相似度计算的方法,然后再姐好啊使用CNN的方法。
一.传统的文本相似度计算的方法:
1.1 最长公共子序列及最长公共子串:
该种方法思路较为简单,直接使用lcs(最长公共子序列)算法,设字符串A长度为L1,字符串长度为L2,它们的最长公共子串长度为lcs,那么这两个字符串相似度的度量为 sim = 2lcs/(L1+L2)。LCS算法的具体描述可参见LCS 。
1.2 余弦相似度:
该种方法思路一样非常简单,将两个字符串进行分词,并将其量化为多维空间上的点,计算两个文本的余弦相似度即可, 具体公式和LCS算法的方式类似,就不在赘述 。
1.3 其他
除了以上两种思路外,还有编辑距离 、 jaccard距离 、 simhash 等等,使用的方法也较为类似,因为不是本文的重点,因此也不在赘述。
二. 使用CNN的方法
2.1 使用CNN来进行文本相似度计算的原因:
首先这里文本相似度计算的输入是指 两个字符串,比如字符串A = ‘书写代码,改变世界’ 字符串B = ‘地产兴邦,称霸世界’,那么我们如何计算他们的文本相似度呢,一个是直接用本文中提到过的 传统的文本相似度计算方法来计算,如果任务的要求不高,直接拿字符串A和B计算即可。那么为什么又需要使用cnn和词向量的方法来计算文本相似度呢???
这是因为传统文本相似度算法,侧重于文本本身的相似度计算,因此需要大量的归一化的过程,例如中文数字和阿拉伯数字的归一化,中英文 单位的归一化(例如kg和千克)。但是即使通过归一化的方法,仍然有很多语义相似文本无法通过这种方法得到满意的结果。比如两道数学题, 题1 = '一个苹果+二个苹果等于多少个苹果?', 题2=‘一个香蕉+二个香蕉等于多少个香蕉’,显然这两道题语义是高度接近的,但是用传统的文本相似度计算方法,计算得到的相似度是非常低的,这不能满足现代互联网或其他领域对文本语义相似度任务的需要,于是需要使用cnn和词向量的方式来计算文本语义相似度,这是因为词向量的固有特性,如果对词向量有所不了解可参见我的上一篇文章文本分类
中关于词向量的介绍。
2.2 文本预处理的过程:
这里文本预处理过程和文本分类一文中文本预处理的内容大同小异,看过该文的可以直接跳过这一步。
首先文本相似度计算输入的 来源是两个字符串,比如字符串A = ‘书写代码,改变世界’ 字符串B = ‘地产兴邦,称霸世界’。
以字符串A的处理来说明问题,这部分主要是分3步,共4种状态。1.将原始文本分词并转换成以词的序列 2.将词序列转换成以词编号(每个词表中的词都有唯一编号)为元素的序列 3.将词的编号序列中的每个元素(某个词)展开为词向量的形式。下面通过一张图(本人手画简图。。。。囧)来表示这个过程,如下图所示:
上述图片,以'书写代码,改变世界' 这一文本字符串为例,介绍了将其转换成词向量为元素的序列表示,最后得到了一个2维矩阵,该矩阵可用于后续神经网络的训练等操作。
2.3 神经网络模块的设计
本文关于神经网络设计的思想来自于以下keras中文文档中的:泛型编程 这一届中微博相似度计算启发而来。并结合了实际需要而设计的。我下面结合一张神经网络设计图,来说明本文中所使用的神经网络,具体设计图(又是手画图,囧)如下:
简要介绍下上面的图,第一层数据输入层,将文本序列展开成词向量的序列,之后两个不同的输入流
,这里因为有两个输入(字符串A和字符串B),垂直方向则放置了2个完全一样的层组合(卷积层、激活层、池化层的组合)。之后连接全连接层和激活层,激活层采用sigmoid并输出该文本属于某类的概率,这个输出的值是个0-1之间的浮点数,该值代表 文本A和文本B的相似程度,值越大相似程度越大。
2.4 编程实现所需要的框架和数据集等
2.4.1 框架:本文采用keras框架来编写神经网络,关于keras的介绍请参见莫言大神翻译的 keras中文文档
2.4.2 数据集:采用的是1w条,标记好是否是语义相近的相似文本,比如一道数学题和另一道数学题,如果语义相似则标记为1,如果不同则标记为0.
2.4.3 词向量:虽然keras框架已经有embedding层,但是本文采用glove词向量作为预训练的词向量,glove的介绍和下载地址如下(打开会比较慢):
http://nlp.stanford.edu/projects/glove/
2.5 代码和相应的注释
在2.3部分已经通过一张图介绍了神经网络的设计部分,但是考虑到不够直观,这里还是把所使用的代码,罗列如下,采用keras编程,关键部分都已经罗列注释:
from keras.layers import Input, LSTM, Dense, merge, Conv1D, MaxPooling1D, Flatten, Embedding, Dropout from keras.models import Model import numpy as np from keras.utils.np_utils import to_categorical from keras.preprocessing.text import Tokenizer from keras.preprocessing.sequence import pad_sequences import pdb from keras import backend as K import theano.tensor as T # tensor from theano import function # function from keras.engine.topology import Layer import os import sys import jieba jieba.load_userdict("./science") input_train = sys.argv[1] # s_label_zfli BASE_DIR = '.' GLOVE_DIR = BASE_DIR + '/wordvec/' # 预训练词向量的地址 MAX_SEQUENCE_LENGTH = 200 MAX_NB_WORDS = 200000 EMBEDDING_DIM = 200 VALIDATION_SPLIT = 0.2 print('Indexing word vectors.') embeddings_index = {} #f = open(os.path.join(GLOVE_DIR, '125_vec')) f = open(os.path.join(GLOVE_DIR, 'old_vec')) # 预训练的词向量,可使用Word2vec自行训练,下面几行就是依次读入词向量 for line in f: values = line.split() word = values[0] coefs = np.asarray(values[1:], dtype='float32') embeddings_index[word] = coefs f.close() print('Found %s word vectors.' % len(embeddings_index)) print('Processing text dataset') # good texts = [] # list of text samples labels_index = {} # dictionary mapping label name to numeric id labels = [] # list of label ids train_left = [] train_right = [] # 下面几行是读入训练集,训练集是 两个 字符串和一个这两个字符串是否相似的标记 for line in file(sys.argv[1]): # train,读入训练集 line = line.strip() tmp = line line = line.split('\1') if len(line)<5: continue label_id = line[0] tid = line[1] title = line[2] tid = line[3] title_right = line[4].strip() # need strip at this line seg_list = jieba.cut(title) seg_list_right = jieba.cut(title_right) text_left = (' '.join(seg_list)).encode('utf-8','ignore').strip() text_right = (' '.join(seg_list_right)).encode('utf-8','ignore').strip() #print text_left #print text_right texts.append(text_left) texts.append(text_right) labels.append(float(label_id)) train_left.append(text_left) train_right.append(text_right) print('Found %s left.' % len(train_left)) print('Found %s right.' % len(train_right)) print('Found %s labels.' % len(labels)) # finally, vectorize the text samples into a 2D integer tensor tokenizer = Tokenizer(nb_words=MAX_NB_WORDS) tokenizer.fit_on_texts(texts) sequences_left = tokenizer.texts_to_sequences(train_left) sequences_right = tokenizer.texts_to_sequences(train_right) #for item in sequences_left: # print item word_index = tokenizer.word_index print('Found %s unique tokens.' % len(word_index)) data_left = pad_sequences(sequences_left, maxlen=MAX_SEQUENCE_LENGTH,padding='pre', truncating='post') data_right = pad_sequences(sequences_right, maxlen=MAX_SEQUENCE_LENGTH, truncating='post') labels = np.array(labels) #labels = to_categorical(np.asarray(labels)) # split the data into a training set and a validation set indices = np.arange(data_left.shape[0]) np.random.shuffle(indices) data_left = data_left[indices] data_right = data_right[indices] labels = labels[indices] nb_validation_samples = int(VALIDATION_SPLIT * data_left.shape[0]) # create val and sp input_train_left = data_left[:-nb_validation_samples] input_train_right = data_right[:-nb_validation_samples] val_left = data_left[-nb_validation_samples:] val_right = data_right[-nb_validation_samples:] labels_train = labels[:-nb_validation_samples] labels_val = labels[-nb_validation_samples:] print('Preparing embedding matrix.') # prepare embedding matrix nb_words = min(MAX_NB_WORDS, len(word_index)) #print type(word_index) #for item in word_index: # print item + '\t' + str(word_index[item]) embedding_matrix = np.zeros((nb_words + 1, EMBEDDING_DIM)) for word, i in word_index.items(): if i > MAX_NB_WORDS: continue embedding_vector = embeddings_index.get(word) if embedding_vector is not None: # words not found in embedding index will be all-zeros. embedding_matrix[i] = embedding_vector # word_index to word_embedding_vector ,<20000(nb_words) # load pre-trained word embeddings into an Embedding layer # note that we set trainable = False so as to keep the embeddings fixed ''' embedding_layer = Embedding(nb_words + 1, EMBEDDING_DIM, input_length=MAX_SEQUENCE_LENGTH, weights=[embedding_matrix], trainable=True) ''' print('Training model.') # train a 1D convnet with global maxpoolinnb_wordsg #left model ''' data_1 = np.random.randint(low = 0, high = 200, size = (500, 140)) data_2 = np.random.randint(low = 0 ,high = 200, size = (500, 140)) labels = np.random.randint(low=0, high=2, size=(500, 1)) #labels = to_categorical(labels, 10) # to one-hot ''' tweet_a = Input(shape=(MAX_SEQUENCE_LENGTH,)) tweet_b = Input(shape=(MAX_SEQUENCE_LENGTH,)) tweet_input = Input(shape=(MAX_SEQUENCE_LENGTH,)) # 下面这些行是神经网络构造的内容,可参见上面的网络设计图 embedding_layer = Embedding(nb_words + 1, EMBEDDING_DIM, input_length=MAX_SEQUENCE_LENGTH, weights=[embedding_matrix], trainable=True)(tweet_input) conv1 = Conv1D(128, 3, activation='tanh')(embedding_layer) drop_1 = Dropout(0.2)(conv1) max_1 = MaxPooling1D(3)(drop_1) conv2 = Conv1D(128, 3, activation='tanh')(max_1) drop_2 = Dropout(0.2)(conv2) max_2 = MaxPooling1D(3)(drop_2) #conv2 = Conv1D(128, 3, activation='tanh')(max_1) #max_2 = MaxPooling1D(3)(conv2) out_1 = Flatten()(max_1) #out_1 = LSTM(128)(max_1) model_encode = Model(tweet_input, out_1) # 500(examples) * 5888 encoded_a = model_encode(tweet_a) encoded_b = model_encode(tweet_b) merged_vector = merge([encoded_a, encoded_b], mode='concat') # good dense_1 = Dense(128,activation='relu')(merged_vector) dense_2 = Dense(128,activation='relu')(dense_1) dense_3 = Dense(128,activation='relu')(dense_2) predictions = Dense(1, activation='sigmoid')(dense_3) #predictions = Dense(len(labels_index), activation='softmax')(merged_vector) model = Model(input=[tweet_a, tweet_b], output=predictions) model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy']) # 下面是训练程序 model.fit([input_train_left,input_train_right], labels_train, nb_epoch=5) json_string = model.to_json() # json_string = model.get_config() open('my_model_architecture.json','w').write(json_string) model.save_weights('my_model_weights.h5') # 下面是训练得到的神经网络进行评估 score = model.evaluate([input_train_left,input_train_right], labels_train, verbose=0) print('train score:', score[0]) # 训练集中的loss print('train accuracy:', score[1]) # 训练集中的准确率 score = model.evaluate([val_left, val_right], labels_val, verbose=0) print('Test score:', score[0])#测试集中的loss print('Test accuracy:', score[1]) #测试集中的准确率上述代码和注释较为详细的描述了该神经网络的结构,但是实际使用代码时最好去除中文注释部分,否则可能会有一些编码问题。由于上述代码和我上一篇文章有很多结构上的相似,如果遇到一些不懂得地方也可以参照上一篇的代码注释来学习。
三.总结
本文描述了如何使用深度学习和keras框架构建一个文本分类器的全过程,并给出了相应的代码实现,为了方便大家使用,下面给出本文代码的github下载地址
。
相关文章推荐
- pythonNLP-文本相似度计算-Demo
- LSF-SCNN:一种基于 CNN 的短文本表达模型及相似度计算的全新优化模型
- pythonNLP-文本相似度计算实验汇总
- 深度学习解决NLP问题:语义相似度计算——DSSM
- 深度学习解决NLP问题:语义相似度计算
- 余弦相似度的计算方法及在文本相似度方面的应用
- 8月3日云栖精选夜读:LSF-SCNN:一种基于CNN的短文本表达模型及相似度计算的全新优化模型
- 深度学习解决NLP问题:语义相似度计算
- Mapreduce 矩阵相乘应用--计算文本间的相似度
- TF-IDF在关键词自动提取、计算文本相似度和摘要自动生成上的应用
- 余弦定理的应用:基于文字的文本相似度计算
- NLP点滴——文本相似度,计算文本间的距离
- lucene计算文本相似度算法
- NLP Job - 专注自然语言处理&机器学习等领域的求职招聘 | 关注自然语言处理|机器学习|数据挖掘|搜索引擎|计算广告|推荐算法等相关领域的工作机会
- WordNet词网研究7——之JWS(Java Wordnet Similarity)语义相似度计算
- 文本相似度计算基本方法小结
- 文本相似度计算基本方法小结
- 文本比较算法Ⅲ——计算文本的相似度
- 海量数据相似度计算之simhash短文本查找
- 句子语义相似度计算