MXNet 相关函数详解

norm()

norm()
	1.Matlab函数中的 norm() 
		1.应用:norm()用于计算矩阵范数
		2.格式:n = norm(A);
		      n = norm(A,p);
		3.功能:norm函数可计算几种不同类型的矩阵范数,根据p的不同可得到不同的范数
		4.如果A为矩阵
			n=norm(A) 返回A的最大奇异值,即max(svd(A))
  			n=norm(A,p) 根据p的不同,返回不同的值
		5.如果A为向量
			norm(A,p) 返回向量A的p范数。即返回 sum(abs(A).^p)^(1/p),对任意 1<p<+∞.
  			norm(A) 返回向量A的2范数,即等价于norm(A,2)。
  			norm(A,inf) 返回max(abs(A))
  			norm(A,-inf) 返回min(abs(A))
		6.p值、返回值
			p值	返回值
			1	返回A中最大一列和,即max(sum(abs(A)))
			2	返回A的最大奇异值,和n=norm(A)用法一样
			inf	返回A中最大一行和,即max(sum(abs(A’)))
			'fro'	A和A‘的积的对角线和的平方根,即sqrt(sum(diag(A'*A)))

	2.例子:
		1.X.norm().asscalar() //asscalar函数将norm()求得的结果变换为Python中的标量
		2.sum():NDArray元素求和,结果中的数值虽为标量,但结果仍然为NDArray格式,可以通过norm().asscalar()函数转换为Python中的标量(实数)

		3.对Python的控制流求导
			对如下函数进行求导:
				def f(a):
					b = a * 2
					while b.norm().asscalar() < 1000:
						b = b * 2
					if b.sum().asscalar() > 0:
						c = b
					else:
						c = 100 * b
					return c

			函数f(a)最后的输出值c由输入值a决定,即c=xa,导数x=c/a。


MXNet 基础入门

1.如何使用NDArray来处理数据
	1.NDArray几种不同的创建方法
		1.第1条:从mxnet中导入nd
		2.第2条:使用arange()函数创建一个长度为12的行向量

			该NDArray包含12个元素(element),其值为arange(12)指定的0-11。在打印的结果中标注了属性<NDArray 12 @cpu(0)>。
			其中12指的是NDArray的形状,就是向量的长度。@cpu(0)表示默认情况下NDArray被创建在CPU上。
 
		3.第3条:使用reshape()函数修改x的形状,将x修改为一个3行4列的矩阵

		4.第4条:创建一个各元素为0,形状为(2,3,4)的张量。PS:矩阵和向量都是一种特殊的张量。

		5.第5条:同理,创建一个各元素为1的张量。

		6.第6条:通过Python的列表(list)指定NDArray中每个元素的值。

		7.第7条:通过nd.random.normal()方法,随机生成NDArray每个元素的值,创建一个形状为(3,4)的NDArray。
			每个元素随机采样于均值为0方差为1的正态分布。

		8.第8条:通过shape属性获取形状,通过size属性获取NDArray中元素的个数。


	2.NDArray的运算
		1.第1条:按元素加法

		2.第2条:按元素乘法

		3.第3条:按元素除法

		4.第4条:按元素指数运算,exp

		5.第5条:对矩阵b做转置,矩阵a、b做矩阵乘法操作,a为3行4列,b为4行3列,故其结果为一个3行3列的矩阵。dot

		6.第6条:NDArray元素求和(结果为标量,但仍然为NDArray格式,可以通过norm().asscalar()函数转换为Python中的数),sum()

	3.广播机制
		上面提到的两个NDArray之间元素级的运算都是基于两个NDArray形状相同,如果两个NDArray形状不同,在运算的过程中会触发广播(broadcasting)机制,
		即先把两个NDArray搞成形状相同,然后再进行运算。
		广播(broadcasting)机制简单理解就是行与列间复制,达到不同NDArray之间形状相同的目的。

	4.NDArray在进行运算的过程中产生的内存开销
		1.第1条:每一个操作都会新开辟一块内存空间用来存储操作后的运算结果。

		2.第2条:可以通过[:]将计算结果写入之前变量创建的内存空间中。nd.zeros_like(x)方法可以创建一个形状和x相同,但元素均为0的NDArray。

		3.第3条:在第2条的运算中,虽然变量z在计算前后的内存地址相同,在本质上其运行原理仍然是先将x+y的值放到一个新开辟的内存空间中,
			然后再将结果拷贝到z的内存中。
			为了避免这种计算过程中的内存开销,可以使用运算符全名函数中的out参数解决该问题。

			可以看到,前后的内存地址相同,这种开销也得以避免。
 
		4.第4条:现有NDArray的值在之后的程序中不会复用,可以直接使用如下方法来减少内存开销。x+=y,x[:]=x+y

	5.NDArray的索引
		类比Python中列表(list)的索引,NDArray的索引可以理解为每一个元素的位置。索引的值从0开始逐渐递加。
		举个栗子,一个3行2列的矩阵,其行索引为0,1,2,列索引为0,1。

		1.第1条:创建一个3行3列的矩阵x,通过x[1:3],根据Python的开闭原则,可知取的值为索引为1和2行的数据。

		2.第2条:通过x[1,2]这种形式可以取出指定的元素,可以对其重新赋值。

		3.第3条:可以通过[1:2,1:3]这种方式取出NDArray中的多个元素,可以对这些元素进行重新赋值。

	6.NDArray与NumPy格式的相互转换
		可以通过array()函数将numpy转换为ndarray,通过asnumpy()函数将ndarray转化为numpy。

	7.小结
		NDArray是MXNet中存储和转换数据的主要工具,可以将它理解为MXNet实现的一种数据结构。
		在这一节中可以了解到如何对NDArray进行创建、运算、制定索引,同时与numpy格式进行转换的方法。


2.简述MXNet提供的自动求导功能
	1.很多深度学习框架需要编译计算图进行求导,而MXNet不需要,使用自带的autograd包即可实现自动求导功能。
	2.下面来看两个例子。
		1.第一个:对简单的数学函数进行求导
			 对函数y=2x^2进行求导

			其中涉及的细节有一点:
				1.求变量x的导数,需要先调用x.attach_grad()函数创建需要的内存空间
				2.为了减少计算和内存的开销,默认情况下,MXNet不会记录用于求倒数的计算图,
				  我们需要需要调用autograd.record()函数来让MXNet记录有关的计算图。
				3.通过y.backward()函数求倒数,其结果为x.grad

		2.第二个:对Python的控制流求导
			 对如下函数进行求导:
				def f(a):
					b = a * 2
					while b.norm().asscalar() < 1000:
						b = b * 2
					if b.sum().asscalar() > 0:
						c = b
					else:
						c = 100 * b
					return c

			函数f(a)最后的输出值c由输入值a决定,即c=xa,导数x=c/a。

	3.小结
		通过MXNet自动求导总共分为3步:
			1.开辟存储导数的内存空间a.attach_grad()
			2.通过autograd.record()函数记录计算图,并实现相应的函数
			3.调用c.backward()函数进行求导



3.如何通过ndarray和autograd实现简单的线性回归
	线性回归是监督学习中的一种,是一个最简单,也是最有用的单层神经网络。
	我的理解是这样的给定一些数据集X,根据训练好的模型(将数据集X带入模型中),都有一个特定的y值与其对应。训练这个模型就是我们需要做的工作。
	那线性回归就是y=ax+b,我们要做的就是确定斜率a和位移b的值。

	1.第1步:数据集的创建
		在工业级的生产环境中,数据集往往来源于真事的业务场景(在Web日志中挖掘攻击行为呀,预测房价啊一类的),这里是演示,所以暂且使用随机生成的数据。
		在第一个例子中,作者使用了一套人工生成的数据,相应的生成公式如下:y[i] = 2*X[i][0] - 3.4*X[i][1] + 4.2 + noise
		noise服从均值为0方差为0.1的正态分布。
		相应的代码如下:
				>>> from mxnet import ndarray as nd
				>>> from mxnet import autograd
				>>> num_inputs = 2
				>>> num_examples = 1000
				>>> true_w = [2,-3.4]
				>>> true_b = 4.2
				>>> X = nd.random_normal(shape=(num_example,num_inputs))
				Traceback (most recent call last):
				  File "<stdin>", line 1, in <module>
				NameError: name 'num_example' is not defined
				>>> X = nd.random_normal(shape=(num_examples,num_inputs))
				>>> y = true_w[0]*X[:,0]+true_w[1]*X[:,1]+true_b
				>>> y += .01*nd.random_normal(shape=y.shape)

	2.第2步:数据读取
		当我们拥有了一定的数据集之后,我们要做的就是数据的读取。不断的读取这些数据块,进行神经网络的训练。
		相应的函数如下:
				>>> def date_iter():
				...     idx = list(range(num_examples))
				...     random.shuffle(idx)
				...     for i in range(0,num_examples,batch_size):
				...         j = nd.array(idx[i:min(i+batch_size,num_examples)])
				...         yield nd.take(X,j),nd.take(y,j)

		通过yield关键字来构造成迭代器,依次取出不同的样本数据(10个)。
		通过for loop不断的遍历将迭代器中的数据取出。
				>>> for date,label in adte_iter():
				...     print(date,label)
				...     break
 		接下来将读取到的数据,传入我们给定的算法中进行训练。

	3.第3步:定义模型
		先来随机初始化模型的参数。

		创建参数的梯度:

		参数初始化完成后我们就可以进行模型的定义:
			>>> def net(X):
			...     return nd.dot(X,w)+b

	4.第4步:定义损失函数
		通过损失函数衡量预测目标与真实目标之间的差距。
		def square_loss(yhat,y):
    			return (yhat - y.reshape(yhat.shape))**2

	5.第5步:优化
		使用梯度下降进行求解。
			def SGD(params,lr):
				for param in params:
					param[:] = param - lr * param.grad

	6.第6步:训练
				>>> epochs = 5
				>>> learning_rate = .001
				>>> 
				>>> for e in range(epochs):
				...     total_loss = 0
				...     for data,label in adte_iter():
				...         with autograd.record():
				...             output = net(data)
				...             loss = square_loss(output,label)
				...         loss.backward()
				...         SGD(params,learning_rate)
				...         total_loss += nd.sum(loss).asscalar()
				...     print("%d,loss: %f" % (e,total_loss/num_examples))
				... 
				0,loss: 0.130911
				1,loss: 0.002628
				2,loss: 0.000150
				3,loss: 0.000102
				4,loss: 0.000101
 
			查看训练结果(和我们的预期相同)

4.使用gluon的线性回归
	1.第1步:数据集的创建
	2.第2步:数据读取

	3.第3步:定义模型

	4.第4步:定义损失函数

	5.第5步:优化

	6.第6步:训练

5.总结
	1.确认需要训练的数据集(特征工程)
	2.将特征工程后的数据读取至内存中
	3.定义模型同时初始化模型参数
	4.定义损失函数、优化算法
	5.训练模型及验证结果

topk

1.mxnet官方文档topk函数介绍:http://mxnet.incubator.apache.org/api/python/docs/api/ndarray/ndarray.html?highlight=topk#mxnet.ndarray.topk
2.mxnet.ndarray.topk(data=None, axis=_Null, k=_Null, ret_typ=_Null, is_ascend=_Null, dtype=_Null, out=None, name=None, **kwargs) 
	1.topk:返回输入数组data中沿给定轴axis的顶部k个元素。返回的元素将被排序。
	  返回值:out函数的输出
	  返回类型:NDArray或NDArrays列表
	2.传入参数 data:输入数组
	3.传入参数 axis
		(int或无,可选,缺省值为-1)选择指定轴上的顶部前k个索引。如果没有给定,则展平数组。默认值为-1,即最后一个轴。
	4.传入参数 k
		(int,可选,缺省值=1)要选择的顶部元素的数量应该总是小于或等于给定轴中的元素编号。如果设置k<1,则执行全局排序。
	5.传入参数 ret_typ
		({'both','indices','mask','value'},可选,默认为'indices')–返回类型。
		“value”表示返回前k个值,“indices”表示返回前k个值的索引,“mask”表示返回包含0和1的掩码数组,1表示最大k值。
		“both”意味着返回前k个元素的值和索引的列表。
	6.传入参数 is_ascend
		(布尔型,可选,默认值为0)–选择k个最大元素还是k个最小元素。
		如果设置为false(默认为is_ascend=0),将选择最大的前k个元素。
		如果设置is_ascend=1,将选择最小的前k个元素。
	7.传入参数 dtype
		({'float16','float32','float64','int32','int64','uint8'},可选,默认值为'float32')
		ret_typ为“indices”或“both”时输出索引的数据类型。如果选定的数据类型不能精确表示索引,则会引发错误。
	8.传入参数 out:(ndarray,可选)保存结果的输出ndarray。
 
3.例子
	x = [[ 0.3,  0.2,  0.4],
     	     [ 0.1,  0.3,  0.2]]
	
	#返回最后一个轴上最大元素的索引值
	topk(x) = [[ 2.],
           	  [ 1.]]

	#返回最后一个轴上的Top2即前两个最大值的元素值,'value'返回类型表示返回元素值
	topk(x, ret_typ='value', k=2) = [[ 0.4,  0.3],
                                          [ 0.3,  0.2]]

	#返回最后一个轴上的Top2即前两个最小值的的元素值,'value'返回类型表示返回元素值
	topk(x, ret_typ='value', k=2, is_ascend=1) = [[ 0.2 ,  0.3],
                                                       [ 0.1 ,  0.2]]

	#返回轴axis为0上的Top2即前两个最大值的元素值,'value'返回类型表示返回元素值
	topk(x, axis=0, ret_typ='value', k=2) = [[ 0.3,  0.3,  0.4],
                                         	     [ 0.1,  0.2,  0.2]]

	#'both''返回类型表示返回“包含前k个元素的值和对应的索引的”列表
	topk(x, ret_typ='both', k=2) = [[[ 0.4,  0.3], [ 0.3,  0.2]] ,  [[ 2.,  0.], [ 1.,  2.]]]

gluon NLP 预训练词嵌入

1.官方文档:https://gluon-nlp.mxnet.io/examples/word_embedding/word_embedding.html
2.Counter词典、idx_to_token数组、token_to_idx词典
	#使用Counter词典容器封装分词数据,key为分词,value为该分词出现的次数
	counter = collections.Counter([tk for st in raw_dataset for tk in st])
	#只保留在数据集中至少出现5次的词
	counter = dict(filter(lambda x: x[1] >= 5, counter.items()))

	#将词映射到整数索引
	#数组:由索引和索引对应的元素值所组成
	idx_to_token = [tk for tk, _ in counter.items()] 
	#词典:key为数组idx_to_token中索引对应的元素值,value为数组idx_to_token中元素值对应的索引
	token_to_idx = {tk: idx for idx, tk in enumerate(idx_to_token)}

3.vocab词汇表、Counter词典、idx_to_token数组、token_to_idx词典
	from mxnet.contrib import text
	counter = collections.Counter([tk for st in tokenized_data for tk in st])
	#创建Vocab词汇表:传入Counter词典进行初始化,该Vocab词汇表对象同时包含了idx_to_token数组和token_to_idx词典
	#idx_to_token数组:元素值为分词,每个分词对应其索引位置
	#token_to_idx词典:key为idx_to_token数组中的分词,value为分词在idx_to_token数组中的对应的索引位置
	#min_freq:设置可过滤掉出现次数少于5的分词,即Counter词典中的value小于5的键值对都不会存储到Vocab词汇表中
	vocab = text.vocab.Vocabulary(counter, min_freq=5)

	#通过截断或者补0来将每条评论长度固定成500
	def pad(x):
		#将每条评论通过截断或者补0,使得每条评论长度变成500
		return x[:500] if len(x) > 500 else x + [0] * (500 - len(x))
		
	#对每条评论进行分词,然后通过词典vocab的to_indices函数把分词转换成词索引
	features = nd.array([pad(vocab.to_indices(x)) for x in tokenized_data])

4.vocab词汇表、Counter词典、idx_to_token数组、token_to_idx词典
	import warnings
	warnings.filterwarnings('ignore')
	from mxnet import gluon
	from mxnet import nd
	import gluonnlp as nlp
	import re

	#原文
	text = " hello world \n hello nice world \n hi world \n"
	 
	#token_delim记号分隔符(单词分隔符)	seq_delim序号分隔符(句子分隔符)
	def simple_tokenize(source_str, token_delim=' ', seq_delim='\n'):
		#re.split(' ' + '|' + '\n', " hello world \n hello nice world \n hi world \n")
		#split的结果:['', 'hello', 'world', '', '', 'hello', 'nice', 'world', '', '', 'hi', 'world', '', '']
		#filter的结果:['hello', 'world', 'hello', 'nice', 'world', 'hi', 'world']
		return filter(None, re.split(token_delim + '|' + seq_delim, source_str))

	#Counter词典容器:key为分词,value为分词出现的次数
	#Counter({'world': 3, 'hello': 2, 'nice': 1, 'hi': 1})
	counter = nlp.data.count_tokens(simple_tokenize(text))

	#创建Vocab词汇表:传入Counter词典进行初始化,该Vocab词汇表对象同时包含了idx_to_token数组和token_to_idx词典
	#idx_to_token数组:元素值为分词,每个分词对应其索引位置
	#token_to_idx词典:key为idx_to_token数组中的分词,value为分词在idx_to_token数组中的对应的索引位置
	#Vocab(size=8, unk="<unk>", reserved="['<pad>', '<bos>', '<eos>']")
	vocab = nlp.Vocab(counter)
	#vocab词汇表中包含4个特殊的未知标记,然后还包含4个分词
	len(vocab) #8
	#vocab中的idx_to_token:为数组,由索引和索引对应的元素值所组成,将词映射到整数索引
	#创建的vocab词汇表包含四个不同的单词和一个特殊的未知标记
	for word in vocab.idx_to_token:
		print(word)
	"""
	<unk>
	<pad>
	<bos>
	<eos>
	world
	hello
	hi
	nice
	"""

	#词典:key为数组idx_to_token中索引对应的元素值,value为数组idx_to_token中元素值对应的索引
	print(vocab.token_to_idx["<unk>"])	#0
	print(vocab.token_to_idx["world"])	#4

5.vocab词汇表、Counter词典、idx_to_token数组、token_to_idx词典
	#查看要使用的fasttext算法中预训练的单词嵌入源数据(需要联网下载),可以调用text.embedding.list_sources查看
	nlp.embedding.list_sources('fasttext')[:5] #['crawl-300d-2M', 'crawl-300d-2M-subword', 'wiki.aa', 'wiki.ab', 'wiki.ace']
	#创建fasttext算法的词嵌入层(需要联网下载'wiki.simple'源数据)
	fasttext_simple = nlp.embedding.create('fasttext', source='wiki.simple')
	#给Vocab词汇表中的词嵌入层添加'wiki.simple'源数据
	vocab.set_embedding(fasttext_simple)
	 
	#默认情况下,vocab词汇表中未知的不存在的分词的向量都是零向量。它的长度等于FastText单词嵌入的向量维数:(300,)
	#查看单词“beautiful”在vocab词汇表中的嵌入形状,但是单词“beautiful”实际不存在于vocab词汇表中
	vocab.embedding['beautiful'].shape
	#任何vocab词汇表中未知的不存在的分词的向量的前五个元素都是零。
	vocab.embedding['beautiful'][:5]
	#[0. 0. 0. 0. 0.]
	#<NDArray 5 @cpu(0)>
	#vocab词汇表中包含4个特殊的未知标记,然后还包含4个分词
	len(vocab) #8

	#单词“hello”和“world”在vocab中的嵌入形状
	vocab.embedding['hello', 'world'].shape #(2, 300)
	#嵌入“hello”和“world”的前五个元素,它们是非零的
	vocab.embedding['hello', 'world'][:, :5]
	#[[ 0.39567   0.21454  -0.035389 -0.24299  -0.095645]
	# [ 0.10444  -0.10858   0.27212   0.13299  -0.33165 ]]
	#<NDArray 2x5 @cpu(0)>

	#单词'hello', 'world'在vocab词汇表中的索引位置
	vocab['hello', 'world'] #[5, 4]
	#vocab['分词1', '分词2'] 等同于 vocab.to_indices(['分词1', '分词2']) 同时获取多个分词在vocab词汇表中的索引位置
	vocab.to_indices(['hello', 'world'])#[5, 4]
 
	#我们可以通过指定单词“hello”和“world”的索引(5和4)和权重或嵌入矩阵来获得向量,
	#这是在gluon.nn.Embedding中调用vocab.embedding.idx_to_vec得到的。
	#我们初始化一个新层并使用layer.weight.set_data方法设置权重。
	#随后,我们从权重向量中提取索引5和4,并检查它们的前五个条目。
	input_dim, output_dim = vocab.embedding.idx_to_vec.shape
	layer = gluon.nn.Embedding(input_dim, output_dim)
	layer.initialize()
	layer.weight.set_data(vocab.embedding.idx_to_vec)
	layer(nd.array([5, 4]))[:, :5]
	#根据索引(5和4)得到单词“hello”和“world”在嵌入矩阵中的权重向量
	#[[ 0.39567   0.21454  -0.035389 -0.24299  -0.095645]
	# [ 0.10444  -0.10858   0.27212   0.13299  -0.33165 ]]
	#<NDArray 2x5 @cpu(0)>

6.vocab词汇表、Counter词典、idx_to_token数组、token_to_idx词典
	#查看要使用的glove算法中预训练的单词嵌入源数据(需要联网下载),可以调用text.embedding.list_sources查看
	nlp.embedding.list_sources('glove')[:5] #['glove.42B.300d', 'glove.6B.100d', 'glove.6B.200d', 'glove.6B.300d', 'glove.6B.50d']
	#创建glove的词嵌入层(需要联网下载'glove.6B.50d'源数据)
	glove_6b50d = nlp.embedding.create('glove', source='glove.6B.50d')

	#1.使用Counter词典容器封装glove_6b50d数据集中idx_to_token数组中分词数据,Counter的key为idx_to_token数组中的分词,value为该分词出现的次数
	#2.创建Vocab词汇表:传入Counter词典进行初始化,该Vocab词汇表对象同时包含了idx_to_token数组和token_to_idx词典
	#  idx_to_token数组:元素值为分词,每个分词对应其索引位置
	#  token_to_idx词典:key为idx_to_token数组中的分词,value为分词在idx_to_token数组中的对应的索引位置
	vocab = nlp.Vocab(nlp.data.Counter(glove_6b50d.idx_to_token))
	#给Vocab词汇表中的词嵌入层添加'glove.6B.50d'源数据
	vocab.set_embedding(glove_6b50d)
	#vocab词汇表中包含4个特殊的未知标记,然后还包含400000个分词
	#vocab词汇表:同时包含了idx_to_token数组和token_to_idx词典
	len(vocab)	#400004
	len(vocab.idx_to_token)	#400004
	len(vocab.token_to_idx)	#00004
	#Vocab词汇表中的前4个为特殊的未知标记,后面才是分词
	print(vocab.idx_to_token[:7]) #['<unk>', '<pad>', '<bos>', '<eos>', '!', '!!', '!!!']
	#vocab词汇表:同时包含了idx_to_token数组和token_to_idx词典
	#vocab['分词'] 等同于 vocab.token_to_idx['分词'],均为把分词作为key从vocab词汇表中的token_to_idx词典获取value值(分词在idx_to_token数组中的索引位置)
	print(vocab['beautiful']) #71424
	print(vocab.token_to_idx['beautiful']) #71424
	print(vocab['fuck']) #154765
	#vocab['分词1', '分词2'] 等同于 vocab.to_indices(['分词1', '分词2']) 同时获取多个分词在vocab词汇表中的索引位置
	vocab.to_indices(['beautiful', 'fuck']) #[71424, 154765]
	#vocab.idx_to_token[索引值]:根据索引从vocab词汇表中的idx_to_token数组获取分词
	print(vocab.idx_to_token[71424]) #beautiful
	#vocab.idx_to_token[索引值] 等同于 vocab.to_tokens(索引值)
	vocab.to_tokens(71424) #'beautiful'
	#vocab.embedding['分词']:获取该分词在词嵌入层中的权重矩阵
	print(vocab.embedding['beautiful'])
	#[ 0.54623    1.2042    -1.1288    -0.1325     0.95529    0.040524
	# -0.47863   -0.3397    -0.28056    0.71761   -0.53691   -0.0045698
	#  0.73217    0.12101    0.28093   -0.088097   0.59733    0.55264
	#  0.056646  -0.50247   -0.63204    1.1439    -0.31053    0.1263
	#  1.3155    -0.52444   -1.5041     1.158      0.68795   -0.85051
	#  2.3236    -0.41789    0.44519   -0.019216   0.28969    0.53258
	# -0.023008   0.58958   -0.72397   -0.85216   -0.17761    0.14432
	#  0.40658   -0.52003    0.09081    0.082961  -0.021975  -1.6214
	#  0.34579   -0.010919 ]
	#<NDArray 50 @cpu(0)>

7.余弦相似性cos算法
	#为了应用单词嵌入,我们需要定义余弦相似度。余弦相似性决定了两个向量之间的相似性,即两个分词的相似度。
	from mxnet import nd
	#传入两个分词的权重矩阵,然后根据余弦相似性cos算法计算两个分词的相似值
	def cos_sim(x, y):
		return nd.dot(x, y) / (nd.norm(x) * nd.norm(y))

	#两个向量之间的余弦相似度范围可以在-1和1之间。值越大,两个向量之间的相似性就越大。

	x = nd.array([1, 2])
	y = nd.array([10, 20])
	z = nd.array([-1, -2])

	print(cos_sim(x, y))
	#[1.]
	#<NDArray 1 @cpu(0)>
	print(cos_sim(x, z))
	#[-1.]
	#<NDArray 1 @cpu(0)>

8.求近义词
	#求近义词
	#根据提供的一个词获取其对应意思相近的词,即求近义词
	#给定一个输入词,我们可以通过相似度从词汇表(400000个词,不包括4个未知标记)中找到最接近的前k个词。
	#任何一对词之间的相似度可以用向量的余弦相似度cos算法来表示。
	#我们首先必须规范化每一行,然后整个词汇表的嵌入矩阵和单个单词的嵌入矩阵做点积运算。
	#然后我们可以找到点积最大的索引(topk),它恰好是最相似单词的索引。

	#为了应用单词嵌入,我们需要定义余弦相似度。余弦相似性决定了两个向量之间的相似性,即两个分词的相似度。
	from mxnet import nd
	#传入两个分词的权重矩阵,然后根据余弦相似性cos算法计算两个分词的相似值
	def cos_sim(x, y):
		return nd.dot(x, y) / (nd.norm(x) * nd.norm(y))
	 
	def norm_vecs_by_row(x):
		#规范化vocab词汇表中词嵌入层中的权重矩阵的每一行
		return x / nd.sqrt(nd.sum(x * x, axis=1) + 1E-10).reshape((-1,1))

	#传入参数:vocab词汇表对象,取前k最相近的单词,word根据查找的单词
	def get_knn(vocab, k, word):
		#获取该分词在词嵌入层中的权重矩阵
		word_vec = vocab.embedding[word].reshape((-1, 1))
		#vocab.embedding.idx_to_vec.shape的维度大小为 (400004, 50),代表了400004个单词(包括4个未知标记),每个单词有50个权重值
		#规范化vocab词汇表中词嵌入层中的权重矩阵的每一行
		vocab_vecs = norm_vecs_by_row(vocab.embedding.idx_to_vec)
		#整个词汇表的嵌入矩阵和单个单词的嵌入矩阵做点积运算
		dot_prod = nd.dot(vocab_vecs, word_vec)
		#topk:从点积运算后的结果矩阵中取出前k个单词的权重矩阵
		#ret_typ='indices'表示返回的实际是这些前k个单词的权重矩阵所在结果矩阵中的索引位置,并封装为列表最后返回
		indices = nd.topk(dot_prod.reshape((len(vocab), )), k=k+1, ret_typ='indices')
		indices = [int(i.asscalar()) for i in indices]
		#移除第一个未知输入标记
		#vocab.idx_to_token[索引值] 等同于 vocab.to_tokens(索引值),根据前k个单词的权重矩阵对应的索引位置从idx_to_token数组中取出该单词
		return vocab.to_tokens(indices[1:])
		

	get_knn(vocab, 5, 'baby')
	#['babies', 'boy', 'girl', 'newborn', 'pregnant']
	get_knn(vocab, 5, 'computers')
	#['computer', 'phones', 'pcs', 'machines', 'devices']
	get_knn(vocab, 5, 'run')
	#['running', 'runs', 'went', 'start', 'ran']
	get_knn(vocab, 5, 'beautiful')
	#['lovely', 'gorgeous', 'wonderful', 'charming', 'beauty']

	#vocab.embedding['分词']:获取该分词在词嵌入层中的权重矩阵
	#根据余弦相似性cos算法计算两个分词的权重矩阵的相似值
	cos_sim(vocab.embedding['baby'], vocab.embedding['babies'])
	#[0.83871305]
	#<NDArray 1 @cpu(0)>

9.求类比词
	#求类比词
	#我们还可以将预训练的单词嵌入应用到单词类比问题中。例如,“男:女::子:女”就是一个比喻。
	#这句话也可以理解为“男人对女人就像儿子对女儿一样”。
	#单词类比完成问题具体定义为:对于类比“a:b::c:d”,给定前三个单词“a”、“b”、“c”,找到“d”。
	#其思想是根据 vec(‘c’) + (vec(‘b’) - vec(‘a’)) 找到最相似的词向量。
	#在本例中,我们将从vocab中的400000个索引单词中找到类似的单词。

	def norm_vecs_by_row(x):
		#规范化vocab词汇表中词嵌入层中的权重矩阵的每一行
		return x / nd.sqrt(nd.sum(x * x, axis=1) + 1E-10).reshape((-1,1))
		
	#传入参数:vocab词汇表对象,取前k最相近的单词,word1/word2/word3根据查找的单词
	def get_top_k_by_analogy(vocab, k, word1, word2, word3):
		#获取word1/word2/word3分词在词嵌入层中的权重矩阵
		word_vecs = vocab.embedding[word1, word2, word3]
		#根据 vec(‘c’) + (vec(‘b’) - vec(‘a’)) 找到最相似的词向量
		word_diff = (word_vecs[1] - word_vecs[0] + word_vecs[2]).reshape((-1, 1))
		#规范化vocab词汇表中词嵌入层中的权重矩阵的每一行
		vocab_vecs = norm_vecs_by_row(vocab.embedding.idx_to_vec)
		#整个词汇表的嵌入矩阵和包含多个单词的嵌入矩阵做点积运算
		dot_prod = nd.dot(vocab_vecs, word_diff)
		#topk:从点积运算后的结果矩阵中取出前k个单词的权重矩阵
		#ret_typ='indices'表示返回的实际是这些前k个单词的权重矩阵所在结果矩阵中的索引位置,并封装为列表最后返回
		indices = nd.topk(dot_prod.reshape((len(vocab), )), k=k, ret_typ='indices')
		indices = [int(i.asscalar()) for i in indices]
		#vocab.idx_to_token[索引值] 等同于 vocab.to_tokens(索引值),根据前k个单词的权重矩阵对应的索引位置从idx_to_token数组中取出该单词
		return vocab.to_tokens(indices)

	#为了应用单词嵌入,我们需要定义余弦相似度。余弦相似性决定了两个向量之间的相似性,即两个分词的相似度。
	from mxnet import nd
	#传入两个分词的权重矩阵,然后根据余弦相似性cos算法计算两个分词的相似值
	def cos_sim(x, y):
		return nd.dot(x, y) / (nd.norm(x) * nd.norm(y))
		
	def cos_sim_word_analogy(vocab, word1, word2, word3, word4):
		words = [word1, word2, word3, word4]
		vecs = vocab.embedding[words]
		#根据 vec(‘c’) + (vec(‘b’) - vec(‘a’)) 计算出的词向量和指定类比单词的权重矩阵进行余弦相似性cos计算相似值
		return cos_sim(vecs[1] - vecs[0] + vecs[2], vecs[3])

	#根据 vec(‘c’) + (vec(‘b’) - vec(‘a’)) 计算出的词向量和指定类比单词的权重矩阵进行余弦相似性cos计算相似值
	cos_sim_word_analogy(vocab, 'man', 'woman', 'son', 'daughter')
	#[0.9658341]
	#<NDArray 1 @cpu(0)>

	get_top_k_by_analogy(vocab, 1, 'man', 'woman', 'son')
	#['daughter']
	get_top_k_by_analogy(vocab, 1, 'beijing', 'china', 'tokyo')
	#['japan']
	get_top_k_by_analogy(vocab, 1, 'bad', 'worst', 'big')
	#['biggest']
	get_top_k_by_analogy(vocab, 1, 'do', 'did', 'go')
	#['went']

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

あずにゃん

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值