给你一篇文章或者一个句子,人们在理解这些句子时,头脑中会进行上下文的搜索和知识联想。对于一篇有思想的文章,作者的创作构思以及核心思想,我们人脑基本的处理逻辑都是结合过去学习过的规则和概念进行抽象总结。作者的创作过程可以形成一个图模型,清晰地展示出路径。目前的nlp根本达不到,都是浅层次的操作,因为目前的nlp基本上借鉴了图像处理机制,根本没有触及nlp的本质。nlp的进展将会十分艰难,因为机器永远不能理解字符串背后的涵义以及说话者到底发生了什么。目前来看,只有知识图谱算是当今Ai最有前景,道路最正确的一个研究方向。逻辑推理还有很长的路,吴文俊公式在理论上已经给出了支持,但是应用在计算机中困难重重。
通常情况下,人在理解语义时头脑会搜寻与之相关的知识。知识图谱的创始人人为,构成这个世界的是实体,而不是字符串,这从根本上改变了过去的搜索体系。语义理解其实是基于知识、概念以及这些概念间的关系。人们在解答问题时,往往会讲述与这个问题相关的知识,这是语义理解的过程。这种机制完全不同于人对图像或者语音的认识。CNN在图像或者语音领域取得成果是不足为奇的,因为生物学家已经对人脑神经元在图像识别过程中的机制非常熟悉,但是对于人脑如何理解文字的神经元机制却知之甚少,所以导致了目前nlp语义理解方面进展非常缓慢。然而有的学者研究规则专家系统已经长达20多年,本人坚信这是正确的方向,最起码比单纯研究深度学习更有实际意义。研究nlp,语言逻辑,语言学是必修课,目前深度学习的瓶颈早就已经显现出来了,而且门槛儿远低于知识图谱,规则专家系统。
单就深度学习来看的话,学院派的人花费大量精力在NLG,seq2seq,多任务自然语言生成等等上面。这些在知识图谱专家或者语言学家眼中就是学术界的玩票儿。本人在刚开始介入nlp学习的过程中,对深度学习很疯狂,尤其是seq2seq,后来请教王昊奋专家,王就很坦白地跟我说seq2seq是玩票儿,坑了很多人,包括在校研究生。所以本人从那以后重新反思之前的学习,重新确立方向,先把数学专业的数学进修好,包括6本数学著作涵盖泛函数,微积分,线代,统计学,矩阵论,凸优化。然后重点转向图模型推理,知识图谱的研究学习。在学习的过程中,前期走了很多弯路,幸亏及时修正了方向,要不然将会陷入深度学习的陷阱之中。当然深度学习并不是一无是处,最起码在图像中取得了非常好的效果,是成功的。在nlp中,深度学习与图模型结合初步解决推理问题也是成功的,谷歌大脑和DeepMind在18年已经做了尝试并且开源了代码。也就说深度学习引入到nlp中需要改进,一方面要改进自身的算法,比如CNN的动态池化层改进,输入层embedding的知识图谱嵌入扩展,另一方面又要与图模型,统计学进行融合。现在Ai界比较热的两大研究方向:一是AutoML,二是神经规则推理。在神经网络参数优化方面,遗传算法是重点。在联结主义和符号主义融合方面,国内的吕正东博士是先锋,他的深度好奇公司已经迈出来第一步。
之前在网上看到很多介绍word2vector的博客,本人发现其中有很多错误。论述很多篇幅并没有抓住他的本质。还有一部分人质疑word2vector不是深度学习,说层数太浅达不到深度的级别,这是一种误解。word2vector是地地道道的深度学习,能够抽取出词的高阶特征。他的成功,关键是基于他的核心思想:相同语境出现的词语义相近。这在nlp中是非常重要的一个思想,利用上下文。除去上下文的影响因素,单纯从数学公式来看,w2v的skip_gram是logistic regression的升级版。所以w2v其实是很简单的,但是背后的想法不简单。这也是国内的研究人员与美国的研究人员的差距,国内人员习惯于复杂的公式推导,但是想法很low,美国正好相反。这一点从数学教育就可以看出来,国内热衷于感知层面的数学学习,和机器差不多。而国外主要从认知层面来学习数学。物理学,数学,神经生物学,生活常识都可以是想法的来源,比如上个世纪的pagerank好像很简单,但是欧洲的学者利用随机游走模型能够模拟出成功和运气之间存在很强的相关性。即使是简单的算法,国内的人也想不出来,原因是人天性里的好奇心和创造力从小被解题和刷题给灭掉了。
再比如,给你一篇文章,抽取出其中的中心句或者总结出中心思想(当然了,只有中国的语文教学这样教学生,把学生教傻了,当前的在线教育利用Ai助纣为虐就是个妖魔化方向)。首先你得研究作为一个人本身的处理逻辑,然后从中抽象出数学模型然后再与Ai结合。
这就是上层的灵感,尤其是幼儿时期的非经验主义的学习,不需要海量数据的灌输。这一点国内非常落伍,基本都是从google扒美国的论文,拿过来研究一下改改。去年百度副总闭关几个月阅读《脑科学导论》反思当下的深度学习,请问AI创业公司有几个能做到?除了吹牛还有别的本事吗?更进一步说,人本身就是十分复杂精密的机器,从DNA指导蛋白质合成过程就可以看出,人一定是上帝造出来的机器,不可能是随机组合,概率真的真的太太太低了。只有修正了认知才会进步。回到刚提出的问题,抽出中心句的过程,人脑逻辑是衡量上下文和假设中心句的语义相似度。所以才会有另一篇本人写的博客。
目前来看,LSTM以及Attention Model是比较成功的,但是仍然基于形式化的,对于深层语义仍然没有解决,必须依靠规则专家系统!目前来看,深度学习算法LSTM,Attention Model等在nlp中的应用,仅限于上下文和词,句子向量。计算一下句子相似度,聚类之类的,要想真正让机器理解文字,还达不到。也就是说只在语义表示层做文章是远远不够的,底层的知识图谱是关键。Google提出的知识图谱是一种变革,nlp是一个完整的生态圈,从最底层的存储,GDB三元组(entry,relation,entry),到上层的语义表示(这个阶段可以借助深度学习直接在语义层进行训练),比如(head,relation,tail)三元组表示的图结构,表达了实体与实体间的关系,可以用深度学习训练出一个模型:h + r = t,获取语义表示。这样在预测时,得到了两个实体的语义表示,进行减法运算就可以知道两者的关系。这个不同于word2vector,但是还是有共性的。word2vector的模型训练和kg的向量化,两者其实存在一定的关联。前者可以看成是kg图结构碾平之后的序列化的向量表示。目前trans系列和基于语义相似度模型都可以解决kg的向量化。
实体,关系和规则是深度学习引入到nlp中必须考虑的,也是逻辑推理的基本要素。深度学习获取语义表示(不仅限于文字,也可是一个有多维度的图节点),必须与规则交互,规则表明了实体之间如何交互。语义表示和规则是深度学习两大构成要素,二者相互交互。
语义表示是深度学习在nlp文字应用中的方向。之前在词embedding上word2vector获取了巨大成功,现在主要方向是由词embedding迁移到句子或者文章embedding。获取句子的embedding,之前的博客,siamese lstm已经有论述了,在2014~2015年间,国外的学者探索了各种方法,比如tree-lstm,convnet,skip-thougt,基于ma机构的siamese lstm来计算句子或者文章的相似度。目前从数据来看,基于ma结构的siamese lstm效果最好,最适应nlp的规律。在github上已经有了siamese lstm的实验,进一步改进是基于BiLSTM+self_attention(本人提出),至于增加层数是否能够带来准确率的提升,有待于进一步论证,个人持中立态度。现在上传用tensorflow实现的word2vector代码(negative sampleing)作为结尾:
data-helper.py:
import collections
import os
import random
import zipfile
import numpy as np
import urllib.request as request
import tensorflow as tf
url = 'http://mattmahoney.net/dc/'
def maybe_download(filename,expected_bytes):
if not os.path.exists(filename):
filename,_ = request.urlretrieve(url+filename,filename)
statinfo = os.stat(filename)
if statinfo.st_size == expected_bytes:
print('Found and verified',filename)
else:
print(statinfo.st_size)
raise Exception('Failed to verify' + filename + '.Can you get to it with a browser?')
return filename
def read_data(filename):
with zipfile.ZipFile(filename) as f:
data = tf.compat.as_str(f.read(f.namelist()[0])).split()
return data
vocabulary_size = 50000
def build_dataset(words):
count = [['UNK',-1]]
count.extend(collections.Counter(words).most_common(vocabulary_size - 1))
dictionary = dict(zip(list(zip(*count))[0],range(len(list(zip(*count))[0]))))
data = list()
un_count = 0
for word in words:
if word in dictionary:
index = dictionary[word]
else:
index = 0
un_count += 1
data.append(index)
count[0][1] = un_count
reverse_dictionary = dict(zip(dictionary.values(),dictionary.keys()))
return data,reverse_dictionary,dictionary,count
data_index = 0
def generate_batch(data,batch_size,num_skips,skip_window):
filename = maybe_download('text8.zip', 31344016)
words = read_data(filename)
global data_index
assert num_skips <= 2 * skip_window
assert batch_size % num_skips == 0
span = 2 * skip_window + 1
batch = np.ndarray(shape=[batch_size],dtype=np.int32)
labels = np.ndarray(shape=[batch_size,1],dtype=np.int32)
buffer = collections.deque(maxlen=span)
#初始化
for i in range(span):
buffer.append(data[data_index])
data_index = (data_index + 1) % len(data)
#移动窗口,获取批量数据
for i in range(batch_size // num_skips):
target = skip_window
avoid_target = [skip_window]
for j in range(num_skips):
while target in avoid_target:
target = np.random.randint(0,span - 1)
avoid_target.append(target)
batch[i * num_skips + j] = buffer[skip_window]
labels[i * num_skips + j,0] = buffer[target]
buffer.append(data[data_index])
data_index = (data_index + 1) % len(data)
return batch,labels
w2vModel.py
import tensorflow as tf
import w2v.data_helper as da
import numpy as np
import math
#filename = da.maybe_download('text8.zip', 31344016)
words = da.read_data("text8.zip")
assert words is not None
data,reverse_dictionary,dictionary,count = da.build_dataset(words)
class config(object):
batch_size = 128
embedding_size = 128
skip_window = 1
num_skips = 2
valid_size = 16
valid_window = 100
valid_examples = np.random.choice(valid_window, valid_size, replace=False)
num_sampled = 64
vocabulary_size = 50000
num_steps = 10001
class w2vModel(object):
def __init__(self,config):
self.train_inputs = train_inputs = tf.placeholder(tf.int32, shape=[config.batch_size])
self.train_labels = train_labels = tf.placeholder(tf.int32, shape=[config.batch_size, 1])
self.valid_dataset = valid_dataset = tf.constant(config.valid_examples, dtype=tf.int32)
with tf.device('/cpu:0'):
embeddings = tf.Variable(
tf.random_uniform(shape=[config.vocabulary_size, config.embedding_size], minval=-1.0, maxval=1.0))
embed = tf.nn.embedding_lookup(embeddings, train_inputs)
nce_weights = tf.Variable(
tf.truncated_normal([config.vocabulary_size, config.embedding_size], stddev=1.0 / math.sqrt(config.embedding_size)))
nce_bias = tf.Variable(tf.zeros([config.vocabulary_size]))
loss = tf.reduce_mean(
tf.nn.nce_loss(weights=nce_weights, biases=nce_bias, labels=train_labels, inputs=embed,
num_sampled=config.num_sampled, num_classes=config.vocabulary_size))
optimizer = tf.train.GradientDescentOptimizer(1.0).minimize(loss)
norm = tf.sqrt(tf.reduce_sum(tf.square(embeddings), 1, keep_dims=True))
normalized_embeddings = embeddings / norm
valid_embeddings = tf.nn.embedding_lookup(normalized_embeddings, valid_dataset)
similarity = tf.matmul(valid_embeddings, normalized_embeddings, transpose_b=True)
tf.add_to_collection("embedding",embeddings)
self.saver = saver = tf.train.Saver(tf.global_variables())
train.py:
import tensorflow as tf
import w2v.w2vmodel as model
import w2v.data_helper as da
config = model.config()
with tf.Graph().as_default() as g:
Model = model.w2vModel(config)
with tf.Session(graph=g) as session:
tf.global_variables_initializer().run()
print("initialized")
average_loss = 0.0
for step in range(config.num_steps):
batch_inputs,batch_labels = da.generate_batch(model.data,config.batch_size,config.num_skips,config.skip_window)
feed_dict = {Model.train_inputs:batch_inputs,Model.train_labels:batch_labels}
_,loss_val = session.run([Model.optimizer,Model.loss],feed_dict=feed_dict)
average_loss += loss_val
if step % 2000 == 0:
if step > 0:
average_loss /= 2000
print("Average loss at step",step,":",average_loss)
average_loss = 0
if step % 10000 == 0:
sim = Model.similarity.eval()
for i in range(config.valid_size):
valid_word = model.reverse_dictionary[config.valid_examples[i]]
top_k = 8
nearest = (-sim[i,:]).argsort()[1:top_k+1]
log_str = "Nearest to %s:" % valid_word
for k in range(top_k):
close_word = model.reverse_dictionary[nearest[k]]
log_str = "%s %s," % (log_str,close_word)
print(log_str)
Model.saver.save(session, "E:/word2vector/models/model.ckpt")
#final_embeddings = model.normalized_embeddings.eval()
期待nlp语义理解出现变革……