ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

人工智能中 相关的术语概念知识

2020-01-26 12:06:08  阅读:447  来源: 互联网

标签:... 术语 批量 人工智能 知识 state 隐藏 向量 size


1.预训练模型
	1.现在有很多在Imagenet等数据集上训练的很好的模型,当我们使用相同的网络结构时可以直接拿来调用,而不必花很多时间你去从头造轮子。
	2.下面对预训练模型的使用场景做一个简单的理解:
		1.当数据集小的时候:
			1.相似度高:
				如果训练数据和预训练模型所用的数据相似度较高的时候,我们不需要从头造轮子,只需要修改最后的输出的softmax即可,
				采用已经训练好的结构来提取特征。
			2.相似度低:
				如果训练数据和预训练模型所用的数据相似度较低,假设网络一共有n层,我们可以冻结预训练模型中的前k个层中的权重,
				然后重新训练后面的“n减k”个层,并修改最后一层的分类器的输出类即可。因为数据的相似度不高,重新训练的过程就变得非常关键。
				而新数据集大小的不足,则是通过冻结预训练模型的前k层进行弥补。相似度不高的时候重新训练是很有必要的,
				而冻结前k层的原因则就是为了弥补训练数据量的不足,当然了数据量不足可以采取数据增强方法,比如图片的 对称、旋转、随机切、扭曲等等)
		2.当数据集大的时候:
			1.相似度高:
				这个是非常好也非常难得的情况,此时只要采用预训练模型不需要改变任何参数即可,即保持模型原有的结构和初始权重不变,
				随后在新数据集的基础上重新训练。
			2.相似度低:
				因为我们有一个很大的数据集,所以神经网络的训练过程将会比较有效率。然而,因为实际数据与预训练模型的训练数据之间存在很大差异,
				采用预训练模型将不会是一种高效的方式。因此最好的方法还是将预处理模型中的权重全都初始化后,再在新数据集的基础上重头开始训练。

特征图、感受野


L1、L2权重正则化


L1范数损失

1.L1范数损失,即预测值与真实值之间差的绝对值。
2.平滑L1范数损失:它在零点附近使⽤平⽅函数从而更加平滑,这是通过⼀个超参数σ来控制平滑区域的。


L2范数


L2范数 和 格拉姆矩阵(内积) 在神经风格迁移中的应用


双线性插值

双线性插值
	1.转置卷积层可以放大特征图。
	2.在图像处理中,我们有时需要将图像放大,即上采样(upsample)。
	 上采样的方法有很多,常用的有双线性插值。简单来说,为了得到输出图像在坐标(x, y)上的像素,
	 先将该坐标映射到输入图像的坐标(x′, y′),例如,根据输入与输出的尺寸之比来映射。
	 映射后的x′和y′通常是实数。然后,在输入图像上找到与坐标(x′, y′)最近的4个像素。
	 最后,输出图像在坐标(x, y)上的像素依据输入图像上这4个像素及其与(x′, y′)的相对距离来计算。
	 双线性插值的上采样可以通过由以下bilinear_kernel函数构造的卷积核的转置卷积层来实现。
	3.数学里factor是因数的意思。2和3是6的因数。
	  假如a*b=c(a、b、c都是整数),那么我们称a和b就是c的因数。 
	  需要注意的是,唯有被除数,除数,商皆为整数,余数为零时,此关系才成立。 
	  反过来说,我们称c为a、b的倍数。在研究因数和倍数时,不考虑0。


# 构造一个将输入的高和宽放大2倍的转置卷积层:设置步长strides=2
# Conv2DTranspose转置卷积层:输出通道数为3,卷积核大小4x4,填充1,步长2
conv_trans = nn.Conv2DTranspose(3, kernel_size=4, padding=1, strides=2)
# bilinear_kernel()函数返回初始化好的kernel卷积核
conv_trans.initialize(init.Constant(bilinear_kernel(3, 3, 4)))
 
def bilinear_kernel(in_channels, out_channels, kernel_size):
	# 因数2 = (4 + 1) // 2
	factor = (kernel_size + 1) // 2
	# 奇数%2==1,偶数%2==0。4%2==0
	if kernel_size % 2 == 1:
		center = factor - 1
	else:
		# center = 1.5 = 2- 0.5
		center = factor - 0.5
	# og[0].shape = (4, 1) 。og[1].shape = (1, 4)
	og = np.ogrid[:kernel_size, :kernel_size]
	# filt.shape = (4, 4)
	filt = (1 - abs(og[0] - center) / factor) * (1 - abs(og[1] - center) / factor)
	# weight.shape = (3, 3, 4, 4)
	weight = np.zeros((in_channels, out_channels, kernel_size, kernel_size), dtype='float32')
	weight[range(in_channels), range(out_channels), :, :] = filt
	# 返回的数组shape为(3, 3, 4, 4)
	return nd.array(weight)

下面为og、filt、weight的打印值情况:
		og = [array([[0],
			    [1],
			    [2],
			    [3]]), 
		       array([[0, 1, 2, 3]])]
			
		filt = array([[0.0625, 0.1875, 0.1875, 0.0625],
			     [0.1875, 0.5625, 0.5625, 0.1875],
			     [0.1875, 0.5625, 0.5625, 0.1875],
			     [0.0625, 0.1875, 0.1875, 0.0625]])
		 
		weight = array([[[[0.0625, 0.1875, 0.1875, 0.0625],
				[0.1875, 0.5625, 0.5625, 0.1875],
				[0.1875, 0.5625, 0.5625, 0.1875],
				[0.0625, 0.1875, 0.1875, 0.0625]],
				[[0.    , 0.    , 0.    , 0.    ],
				[0.    , 0.    , 0.    , 0.    ],
				[0.    , 0.    , 0.    , 0.    ],
				[0.    , 0.    , 0.    , 0.    ]],
				[[0.    , 0.    , 0.    , 0.    ],
				[0.    , 0.    , 0.    , 0.    ],
				[0.    , 0.    , 0.    , 0.    ],
				[0.    , 0.    , 0.    , 0.    ]]],
			
				[[[0.    , 0.    , 0.    , 0.    ],
				[0.    , 0.    , 0.    , 0.    ],
				[0.    , 0.    , 0.    , 0.    ],
				[0.    , 0.    , 0.    , 0.    ]],
				[[0.0625, 0.1875, 0.1875, 0.0625],
				[0.1875, 0.5625, 0.5625, 0.1875],
				[0.1875, 0.5625, 0.5625, 0.1875],
				[0.0625, 0.1875, 0.1875, 0.0625]],
				[[0.    , 0.    , 0.    , 0.    ],
				[0.    , 0.    , 0.    , 0.    ],
				[0.    , 0.    , 0.    , 0.    ],
				[0.    , 0.    , 0.    , 0.    ]]],
				
				[[[0.    , 0.    , 0.    , 0.    ],
				[0.    , 0.    , 0.    , 0.    ],
				[0.    , 0.    , 0.    , 0.    ],
				[0.    , 0.    , 0.    , 0.    ]],
				[[0.    , 0.    , 0.    , 0.    ],
				[0.    , 0.    , 0.    , 0.    ],
				[0.    , 0.    , 0.    , 0.    ],
				[0.    , 0.    , 0.    , 0.    ]],
				[[0.0625, 0.1875, 0.1875, 0.0625],
				[0.1875, 0.5625, 0.5625, 0.1875],
				[0.1875, 0.5625, 0.5625, 0.1875],
				[0.0625, 0.1875, 0.1875, 0.0625]]]], dtype=float32) 
		 

						   
# 读取图像X,将上采样的结果记作Y。为了打印图像,我们需要调整通道维的位置。	
img = image.imread('../img/catdog.jpg') 
# transpose((2, 0, 1)) 表示把读取图像X的 高x宽x通道数 变换为 通道数x高x宽
X = img.astype('float32').transpose((2, 0, 1)).expand_dims(axis=0) / 255
Y = conv_trans(X)
# 上采样的结果记作Y。transpose((1, 2, 0))表示把上采样的结果的 通道数x高x宽 变换为 高x宽x通道数
out_img = Y[0].transpose((1, 2, 0))

# 可以看到,转置卷积层将图像的高和宽分别放大2倍。值得一提的是,除了坐标刻度不同,
# 双线性插值放大的图像和“目标检测和边界框”一节中打印出的原图看上去没什么两样。
d2l.set_figsize()
print('input image shape:', img.shape)
d2l.plt.imshow(img.asnumpy());
print('output image shape:', out_img.shape)
d2l.plt.imshow(out_img.asnumpy());

input image shape: (561, 728, 3)
output image shape: (1122, 1456, 3)

 


全卷积网络

下⾯我们创建全卷积⽹络实例net。它复制了pretrained_net实例成员变量features⾥除去最后两层的所有层以及预训练得到的模型参数。
net = nn.HybridSequential()
for layer in pretrained_net.features[:-2]:
	net.add(layer)

给定⾼和宽分别为320和480的输⼊,net的前向计算将输⼊的⾼和宽减小⾄原来的1/32,即10和15。
X = nd.random.uniform(shape=(1, 3, 320, 480))
net(X).shape # (1, 512, 10, 15)

接下来,我们通过1×1卷积层将输出通道数变换为Pascal VOC2012数据集的类别个数21。
最后,我们需要将特征图的⾼和宽放⼤32倍,从而变回输⼊图像的⾼和宽。
回忆⼀下“填充和步幅”⼀节中描述的卷积层输出形状的计算⽅法。由于(3200 64+16×2+32)/32 = 10且(4800 64+16×2+32)/32 =15,
我们构造⼀个步幅为32的转置卷积层,并将卷积核的⾼和宽设为64、填充设为16。
不难发现,如果步幅为s、填充为s/2(假设s/2为整数)、卷积核的⾼和宽为2s,转置卷积核将输⼊的⾼和宽分别放⼤s倍。
num_classes = 21
net.add(nn.Conv2D(num_classes, kernel_size=1), nn.Conv2DTranspose(num_classes, kernel_size=64, padding=16, strides=32))

初始化转置卷积层我们使用双线性插值的方式。在全卷积⽹络中,我们将转置卷积层初始化为双线性插值的上采样。
对于1×1卷积层,我们采⽤Xavier随机初始化。
net[-1].initialize(init.Constant(bilinear_kernel(num_classes, num_classes, 64)))
net[-2].initialize(init=init.Xavier())

# 参数 kernel_size = 64。num_classes = 21 得出 in_channels = 21 和 out_channels = 21。
def bilinear_kernel(in_channels, out_channels, kernel_size):
	# 因数32 = (64 + 1) // 2
	factor = (kernel_size + 1) // 2
	# 奇数%2==1,偶数%2==0。64%2==0
	if kernel_size % 2 == 1:
		center = factor - 1
	else:
		# center = 31.5 = 32 - 0.5
		center = factor - 0.5
	# og[0].shape = (64, 1) 。og[1].shape = (1, 64)
	og = np.ogrid[:kernel_size, :kernel_size]
	# filt.shape = (64, 64)
	filt = (1 - abs(og[0] - center) / factor) * (1 - abs(og[1] - center) / factor)
	# weight.shape = (21, 21, 64, 64)
	weight = np.zeros((in_channels, out_channels, kernel_size, kernel_size), dtype='float32')
	weight[range(in_channels), range(out_channels), :, :] = filt
	# 返回的数组shape为 (21, 21, 64, 64)
	return nd.array(weight)

下面为og、filt、nd.array(weight)的打印值情况:
>>> og
[array([[ 0],
       [ 1],
       [ 2],
       [ 3],
       [ 4],
       [ 5],
       [ 6],
       [ 7],
       [ 8],
       [ 9],
       [10],
       [11],
       [12],
       [13],
       [14],
       [15],
       [16],
       [17],
       [18],
       [19],
       [20],
       [21],
       [22],
       [23],
       [24],
       [25],
       [26],
       [27],
       [28],
       [29],
       [30],
       [31],
       [32],
       [33],
       [34],
       [35],
       [36],
       [37],
       [38],
       [39],
       [40],
       [41],
       [42],
       [43],
       [44],
       [45],
       [46],
       [47],
       [48],
       [49],
       [50],
       [51],
       [52],
       [53],
       [54],
       [55],
       [56],
       [57],
       [58],
       [59],
       [60],
       [61],
       [62],
       [63]]), array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
        16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
        32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
        48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]])]

>>> filt
array([[0.00024414, 0.00073242, 0.0012207 , ..., 0.0012207 , 0.00073242,
        0.00024414],
       [0.00073242, 0.00219727, 0.00366211, ..., 0.00366211, 0.00219727,
        0.00073242],
       [0.0012207 , 0.00366211, 0.00610352, ..., 0.00610352, 0.00366211,
        0.0012207 ],
       ...,
       [0.0012207 , 0.00366211, 0.00610352, ..., 0.00610352, 0.00366211,
        0.0012207 ],
       [0.00073242, 0.00219727, 0.00366211, ..., 0.00366211, 0.00219727,
        0.00073242],
       [0.00024414, 0.00073242, 0.0012207 , ..., 0.0012207 , 0.00073242,
        0.00024414]])

>>> nd.array(weight)
array([[[[0.00024414, 0.00073242, 0.0012207 , ..., 0.0012207 ,
          0.00073242, 0.00024414],
         [0.00073242, 0.00219727, 0.00366211, ..., 0.00366211,
          0.00219727, 0.00073242],
         [0.0012207 , 0.00366211, 0.00610352, ..., 0.00610352,
          0.00366211, 0.0012207 ],
         ...,
         [0.0012207 , 0.00366211, 0.00610352, ..., 0.00610352,
          0.00366211, 0.0012207 ],
         [0.00073242, 0.00219727, 0.00366211, ..., 0.00366211,
          0.00219727, 0.00073242],
         [0.00024414, 0.00073242, 0.0012207 , ..., 0.0012207 ,
          0.00073242, 0.00024414]],

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        ...,

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]]],


       [[[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        [[0.00024414, 0.00073242, 0.0012207 , ..., 0.0012207 ,
          0.00073242, 0.00024414],
         [0.00073242, 0.00219727, 0.00366211, ..., 0.00366211,
          0.00219727, 0.00073242],
         [0.0012207 , 0.00366211, 0.00610352, ..., 0.00610352,
          0.00366211, 0.0012207 ],
         ...,
         [0.0012207 , 0.00366211, 0.00610352, ..., 0.00610352,
          0.00366211, 0.0012207 ],
         [0.00073242, 0.00219727, 0.00366211, ..., 0.00366211,
          0.00219727, 0.00073242],
         [0.00024414, 0.00073242, 0.0012207 , ..., 0.0012207 ,
          0.00073242, 0.00024414]],

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        ...,

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]]],


       [[[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        [[0.00024414, 0.00073242, 0.0012207 , ..., 0.0012207 ,
          0.00073242, 0.00024414],
         [0.00073242, 0.00219727, 0.00366211, ..., 0.00366211,
          0.00219727, 0.00073242],
         [0.0012207 , 0.00366211, 0.00610352, ..., 0.00610352,
          0.00366211, 0.0012207 ],
         ...,
         [0.0012207 , 0.00366211, 0.00610352, ..., 0.00610352,
          0.00366211, 0.0012207 ],
         [0.00073242, 0.00219727, 0.00366211, ..., 0.00366211,
          0.00219727, 0.00073242],
         [0.00024414, 0.00073242, 0.0012207 , ..., 0.0012207 ,
          0.00073242, 0.00024414]],

        ...,

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]]],


       ...,


       [[[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        ...,

        [[0.00024414, 0.00073242, 0.0012207 , ..., 0.0012207 ,
          0.00073242, 0.00024414],
         [0.00073242, 0.00219727, 0.00366211, ..., 0.00366211,
          0.00219727, 0.00073242],
         [0.0012207 , 0.00366211, 0.00610352, ..., 0.00610352,
          0.00366211, 0.0012207 ],
         ...,
         [0.0012207 , 0.00366211, 0.00610352, ..., 0.00610352,
          0.00366211, 0.0012207 ],
         [0.00073242, 0.00219727, 0.00366211, ..., 0.00366211,
          0.00219727, 0.00073242],
         [0.00024414, 0.00073242, 0.0012207 , ..., 0.0012207 ,
          0.00073242, 0.00024414]],

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]]],


       [[[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        ...,

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        [[0.00024414, 0.00073242, 0.0012207 , ..., 0.0012207 ,
          0.00073242, 0.00024414],
         [0.00073242, 0.00219727, 0.00366211, ..., 0.00366211,
          0.00219727, 0.00073242],
         [0.0012207 , 0.00366211, 0.00610352, ..., 0.00610352,
          0.00366211, 0.0012207 ],
         ...,
         [0.0012207 , 0.00366211, 0.00610352, ..., 0.00610352,
          0.00366211, 0.0012207 ],
         [0.00073242, 0.00219727, 0.00366211, ..., 0.00366211,
          0.00219727, 0.00073242],
         [0.00024414, 0.00073242, 0.0012207 , ..., 0.0012207 ,
          0.00073242, 0.00024414]],

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]]],


       [[[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        ...,

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        [[0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]],

        [[0.00024414, 0.00073242, 0.0012207 , ..., 0.0012207 ,
          0.00073242, 0.00024414],
         [0.00073242, 0.00219727, 0.00366211, ..., 0.00366211,
          0.00219727, 0.00073242],
         [0.0012207 , 0.00366211, 0.00610352, ..., 0.00610352,
          0.00366211, 0.0012207 ],
         ...,
         [0.0012207 , 0.00366211, 0.00610352, ..., 0.00610352,
          0.00366211, 0.0012207 ],
         [0.00073242, 0.00219727, 0.00366211, ..., 0.00366211,
          0.00219727, 0.00073242],
         [0.00024414, 0.00073242, 0.0012207 , ..., 0.0012207 ,
          0.00073242, 0.00024414]]]], dtype=float32)

矩阵乘法之内积、格拉姆矩阵(Gram matrix)、小批量矩阵乘法

1.内积
	内积指的是两个矩阵(向量)的乘积和,相当于卷积层中的卷积核和输入矩阵(向量)的两个矩阵相乘的乘积和。
	比如有两个向量:
		A矩阵:(x1,x2,...,xn)。B矩阵:(y1,y2,...,yn)。
		那么A矩阵和B矩阵的内积为(两个矩阵(向量)的乘积和):x1*y1+x2*y2+...+xn*yn

2.矩阵X(向量X)的格拉姆矩阵(Gram matrix)XX⊤∈Ra×a中的i行j列的元素值Xij实际为矩阵Xi(向量Xi)与矩阵Xj(向量Xj)的内积
	1.X⊤表示矩阵X(向量X)的转置
	2.矩阵X(向量X)的格拉姆矩阵(Gram matrix)的表达式为XX⊤∈Ra×a。
		1.XX⊤表示矩阵X(向量X)和转置后的矩阵X(向量X)进行两个矩阵(向量)之间的相乘,求内积(乘积和)
		2.矩阵Y为转置后的矩阵X,其中矩阵X的行a等于矩阵Y的列a,矩阵X的列b等于矩阵Y的行b。
		  那么矩阵X*矩阵Y得出的结果矩阵的shape计算规则:“作为被乘数的矩阵X的”行值变为结果矩阵的行值,“作为乘数的矩阵Y的”列值变为结果矩阵的列值。
		  即结果矩阵的shape为(作为被乘数的矩阵X的行a,作为乘数的矩阵Y的列a)。
		  乘法里(被乘数*乘数),前面的是被乘数,后面是乘数。
		  除法里(被除数/除数),前面的是被除数,后面是除数。
		3.Ra×a表示此处的矩阵X(向量X)的行a*列b通过转置操作后变成列b*行a,那么矩阵X(向量X)和转置后的矩阵X(向量X)进行相乘的话,
		  结果矩阵的表达式写为∈R行a×行a。

3.矩阵X(向量X)的格拉姆矩阵的示例
	矩阵X的行a等于矩阵Y的列a,矩阵X的列b等于矩阵Y的行b,矩阵X乘以矩阵Y得出的结果矩阵shape为(矩阵X的行a,矩阵Y的列a)。
	结果矩阵shape的计算规是行值必须为“作为被乘数的矩阵X的”行值,而列值必须为“作为乘数的矩阵Y的”列值。
	乘法里(被乘数*乘数),前面的是被乘数,后面是乘数。
	除法里(被除数/除数),前面的是被除数,后面是除数。
		1.例子1
			>>> import numpy as nd
			>>> x = nd.arange(12)
			>>> X1 = x.reshape((3, 4))
			>>> X1
			array([[ 0,  1,  2,  3],
				   [ 4,  5,  6,  7],
				   [ 8,  9, 10, 11]])
			>>> X1.T
			array([[ 0,  4,  8],
				   [ 1,  5,  9],
				   [ 2,  6, 10],
				   [ 3,  7, 11]])
			>>> X1.shape
			(3, 4)
			>>> X1.T.shape
			(4, 3)
			>>> Y = nd.dot(X1, X1.T)
			>>> Y
			array([[ 14,  38,  62],
				   [ 38, 126, 214],
				   [ 62, 214, 366]])
			>>> Y.shape
			(3, 3)

		2.例子2
			>>> import numpy as nd
			>>> x = nd.arange(12)
			>>> X1 = x.reshape((4, 3))
			>>> X1
			array([[ 0,  1,  2],
				   [ 3,  4,  5],
				   [ 6,  7,  8],
				   [ 9, 10, 11]])
			>>> X1.T
			array([[ 0,  3,  6,  9],
				   [ 1,  4,  7, 10],
				   [ 2,  5,  8, 11]])
			>>> X1.shape
			(4, 3)
			>>> X1.T.shape
			(3, 4)
			>>> Y = nd.dot(X1, X1.T)
			>>> Y
			array([[  5,  14,  23,  32],
				   [ 14,  50,  86, 122],
				   [ 23,  86, 149, 212],
				   [ 32, 122, 212, 302]])
			>>> Y.shape
			(4, 4)
1.gram(X)函数传入的矩阵X的shape为样本数(批量大小)*通道数*高*宽
2.num_channels = X.shape[1] 表示num_channels为通道数
  n = X.size // X.shape[1] 表示n为样本数(批量大小)*通道数*高*宽 / 通道数,因为样本数(批量大小)为1,因此n为高*宽
3.X = X.reshape((num_channels, n)) 表示矩阵X的shape变换为(通道数,高*宽),通道数为行数,高*宽为列数
4.nd.dot(X, X.T) / (num_channels * n) 
	1.其中的 nd.dot(X, X.T)表示矩阵X与转置后的矩阵X相乘,计算乘积和(内积),计算得出的结果矩阵的shape为(通道数,通道数),通道数为行数,通道数为列数。
	2.矩阵X*转置后的矩阵X得出的结果矩阵的shape计算规则:“作为被乘数的矩阵X的”行值变为结果矩阵的行值,“作为乘数的转置后的矩阵X的”列值变为结果矩阵的列值。
	3.num_channels * n 表示 矩阵X中元素的个数,即通道数*高*宽


通过预训练模型从样本数据中抽取特征值

output_features = net.features(X.as_in_context(ctx)):
	把样本数据X传入net.features(X.as_in_context(ctx))中,
	同样通过成员变量features可以获取预训练模型的原输出层的输⼊,即抽取的特征output_features。
outputs = net.output_new(output_features):
	把抽取的特征output_features输入到net.output_new⾃定义的可训练的小规模输出⽹络中,


softmax运算

1.softmax 常用于预测于伯努利分布相关联的概率,具体的数学表达为:

   也就是在机器学习中著名的S型曲线,对数据下溢和上溢有很好的作用,具体的实现也就是如下的代码
		import numpy as np

		def softmax(x):
		"""
		input:x
		output:softmax result
		function:softmax
		"""
			exp_x = np.exp(x)
			softmax_x = exp_x / np.sum(exp_x)
			return softmax_x  

  如果是在tensorflow中使用的话可以直接使用如下
		import tensorflow as tf
		def softmax(A):
			with tf.Session() as sess: 
			print sess.run(tf.nn.softmax(A))

2.在深度学习中,softmax 是一个常用的函数,计算公式如下:
	softmax[x(i)] = exp[x(i)] / sum[exp[x(j)],{i,j = 1...N}
	比如当这个函数的输入序列 {x0,x1,x2,x3...} 为非常小的负数时,分母下溢,结果异常。

3.softmax 是神经网络的一种激活函数。
  本文给出一种使用 tensorflow 计算 softmax 和直接使用 python 计算softmax 的例子。
		#tensorflow 计算 softmax
		>>> import tensorflow as tf
		>>> import math
		>>> a = tf.Variable( [1.0,2.0,3.0,4.0] )
		>>> a
		<tf.Variable 'Variable:0' shape=(4,) dtype=float32_ref>
		>>> y = tf.nn.softmax( a )
		>>> y
		<tf.Tensor 'Reshape_1:0' shape=(4,) dtype=float32>
		>>> se = tf.Session()
		#global_variables_initializer:全局变量初始值设定项
		>>> init = tf.global_variables_initializer()
		>>> se.run( init )
		>>> r = se.run( y )
		>>> r
		array([0.0320586 , 0.08714432, 0.23688284, 0.6439143 ], dtype=float32)
		>>> print( r[0] +  r[1] + r[2] + r[3] )
		1.0

		#python 计算softmax
		>>> import math
		>>> all =  math.exp(1.0) + math.exp(2.0) + math.exp(3.0) + math.exp(4.0)
		>>> a1 = math.exp(1.0)/all
		>>> a2 = math.exp(2.0)/all
		>>> a3 = math.exp(3.0)/all
		>>> a4 = math.exp(4.0)/all
		>>> a1
		0.03205860328008499
		>>> a2
		0.08714431874203257
		>>> a3
		0.23688281808991013
		>>> a4
		0.6439142598879722
		>>> a1 + a2 + a3 + a4
		1.0

4.softmax计算公式:
	Softmax是机器学习中一个非常重要的工具,他可以兼容 logistics 算法,
	可以独立作为机器学习的模型进行建模训练,还可以作为深度学习的激励函数。
	softmax的作用简单的说就计算一组数值中每个值的占比,公式一般性描述为:
    	设一共有n个用数值表示的分类 

,其中n表示分类的个数。那么softmax公式的代码实现:
		import numpy as np
		import math
		 
		def softmax(inMatrix):
			"""
			softmax计算公式函数
			:param inMatrix: 矩阵数据
			:return:
			"""
			m, n = np.shape(inMatrix)  # 得到m,n(行,列) 1行 7列
			outMatrix = np.mat(np.zeros((m, n)))  # mat生成数组
			soft_sum = 0
			for idx in range(0, n):
				#inMatrix[0, idx] 表示取矩阵中的第1行的第idx列元素值
				outMatrix[0, idx] = math.exp(inMatrix[0, idx])  # exp求幂运算,取e为底的指数计算变成非负
				#表示对outMatrix矩阵中的第一行中的每列元素值求和
				soft_sum += outMatrix[0, idx]  # 求和运算
			for idx in range(0, n):
				outMatrix[0, idx] = outMatrix[0, idx] / soft_sum  # 然后除以所有项之后进行归一化
			return outMatrix

		a = np.array([[1, 2, 1, 2, 1, 1, 3]])
		result = softmax(a)
		#[[0.05943317 0.16155612 0.05943317 0.16155612 0.05943317 0.05943317 0.43915506]]
		print(result) # <class 'numpy.matrix'>
		#1.0000000000000002
		print(result[0, 0]+result[0, 1]+result[0, 2]+result[0, 3]+result[0, 4]+result[0, 5]+result[0, 6])

5.其他分享softmax计算博客文章的链接
	https://www.jianshu.com/p/222a41c3f04e
	https://www.jianshu.com/p/695136c5647b
	https://www.chkui.com/article/tensorflow/math_in_machine_learning_softmax

6.现有如下的分类问题,可以通过动物的4个特征来区分此动物到底是什么种类(猫,狗,人 3个类别)

	在开始讲解之前,首先定义一下此文的统一符号命名,(很重要)
		红色的是Input 简写为X
 		蓝色的是Output 简写为 O
 		红色和蓝色之间的是权重矩阵 简写为 W
 		绿色的是Output经过Softmax之后的结果 简写为 yhat

	1.正向传播:
 		O = X*W
 		yhat = Softmax(O)

		此时softmax 有三个神经元输出 m = 3
 		X (Shape 为(1,4)的向量)
 		W (Shape为(4,3)的矩阵)
 		yhat (Shape 为(1,3)的向量)

	2.反向传播
		1) 求损失
			因为是分类问题,我们采用交叉熵作为 loss function, 如下:

		2) 求梯度
			我先给出梯度公式也就是项目中的error-term误差项:

			想必各位想知道这个公式到底怎么推导得到的呢?
			接下来我会讲解此文的重点
			求权重的梯度需要运用链式求导法则,如下:

			我们来分别计算后面的三项:
			第一项:

			第二项:
				稍微有点复杂。
				为什么yhat下标为i 而O下标为j 呢?
				这是由于softmax 函数的分母有多个输出,但是针对yhat_i 需要计算所有的softmax
				由于softmax有多个输出,所以需要考虑 i = j 与 i != j 两种情形。
				当 i != j 时:

				所以可得结果为:

				第三项:

				第三项非常简单,我们先来算前两项

				到这里别忘了 我们经过softmax 的输出只有一个类别也就是 yi = 1, 其他的都等于0
				所以前两项的结果为:

				最终我们就得到了结果:

				但是error-term误差项为

				这里为什么有个负号?
				是因为模型应该朝着负梯度的方向优化,所以加了一个负号。

余弦相似度/余弦相似性

1.余弦相似度
	通过计算两个向量的夹角余弦值来评估他们之间的相似度。
	夹角余弦值的取值范围在[-1,1],任何角度的余弦值都在-1到1之间。
	两个向量之间的夹角角度的余弦值(余弦相似度的值)确定两个向量是否大致指向相同的方向,与向量的的长度无关,仅仅与向量的指向方向相关。
	两个向量之间夹角为0°的话,余弦值(余弦相似度的值)为1,代表两个向量的指向方向相同。
	两个向量之间夹角为90°的话,余弦值(余弦相似度的值)为0,代表两个向量的指向方向垂直。
	两个向量之间夹角为180°的话,余弦值(余弦相似度的值)为-1,代表两个向量的指向方向相反。
	余弦相似度通常用于正空间,因此给出的值为0到1之间,通常用于文本挖掘中的文件比较。
	一个向量空间中两个向量夹角间的余弦值作为衡量两个个体之间差异的大小,余弦值接近1,夹角趋于0,表明两个向量越相似;
	余弦值接近于0,夹角趋于90度,表明两个向量越不相似。

2.余弦相似度,又称为余弦相似性,是通过计算两个向量的夹角余弦值来评估他们的相似度。
  余弦相似度将向量根据坐标值,绘制到向量空间中,如最常见的二维空间。
  余弦相似度用于评估两个向量的夹角的相似度。
  余弦值的范围在[-1,1]之间,值越趋近于1,代表两个向量的方向越接近;越趋近于-1,他们的方向越相反;接近于0,表示两个向量近乎于正交。
  最常见的应用就是计算文本相似度。将两个文本根据他们词,建立两个向量,计算这两个向量的余弦值,就可以知道两个文本在统计学方法中他们的相似度情况。
  实践证明,这是一个非常有效的方法。

3.余弦相似性通过测量两个向量的夹角的余弦值来度量它们之间的相似性。0度角的余弦值是1,而其他任何角度的余弦值都不大于1;并且其最小值是-1。
  从而两个向量之间的角度的余弦值确定两个向量是否大致指向相同的方向。两个向量有相同的指向时,余弦相似度的值为1;
  两个向量夹角为90°时,余弦相似度的值为0;两个向量指向完全相反的方向时,余弦相似度的值为-1。
  这结果是与向量的长度无关的,仅仅与向量的指向方向相关。余弦相似度通常用于正空间,因此给出的值为-1到1之间。
  注意这上下界对任何维度的向量空间中都适用,而且余弦相似性最常用于高维正空间。
  例如在信息检索中,每个词项被赋予不同的维度,而一个维度由一个向量表示,其各个维度上的值对应于该词项在文档中出现的频率。
  余弦相似度因此可以给出两篇文档在其主题方面的相似度。
  另外,它通常用于文本挖掘中的文件比较。此外,在数据挖掘领域中,会用到它来度量集群内部的凝聚力。
  
4.两个向量间的余弦值可以通过使用欧几里得点积公式求出:

	给定两个属性向量,A和B,其余弦相似性θ由点积和向量长度给出,如下所示:

	给出的相似性范围从-1到1:-1意味着两个向量指向的方向正好截然相反,1表示它们的指向是完全相同的,0通常表示它们之间是独立的,
	而在这之间的值则表示中间的相似性或相异性。
	对于文本匹配,属性向量A和B通常是文档中的词频向量。余弦相似性,可以被看作是在比较过程中把文件长度正规化的方法。
	在信息检索的情况下,由于一个词的频率(TF-IDF权)不能为负数,所以这两个文档的余弦相似性范围从0到1。并且,两个词的频率向量之间的角度不能大于90°。

5.角相似性
	“余弦相似性”一词有时也被用来表示另一个系数,尽管最常见的是像上述定义那样的。透过使用相同计算方式得到的相似性,
	向量之间的规范化角度可以作为一个范围在[0,1]上的有界相似性函数,从上述定义的相似性计算如下:

	这式子适用于向量系数可以为正或负的情况。或者,用以下式子计算

	这式子适用于向量系数总为正的情况。
	虽然“余弦相似性”一词有时会用来表示这个角距离,但实际上很少这样说,因为角度的余弦值只是作为一种计算角度的简便方法而被用到,
	本身并不是意思的一部分。角相似系数的优点是,当作为一个差异系数(从1减去它)时,产生的函数是一个严格距离度量,
	而对于第一种意义的“余弦相似性”则不然。然而,对于大多数的用途,这不是一个重要的性质。若对于某些情况下,
	只有一组向量之间的相似性或距离的相对顺序是重要的,那么不管是使用哪个函数,所得出的顺序都是一样的。

6.与“Tanimoto”系数的混淆
	有时,余弦相似性会跟一种特殊形式的、有着类似代数形式的相似系数相混淆:

	事实上,这个代数形式是首先被Tanimoto定义,作为在所比较集合由位元向量表示时计算其Jaccard系数的方法。
	虽然这公式也可以扩展到向量,它具有和余弦相似性颇为不同的性质,并且除了形式相似外便没有什么关系。

7.Ochiai系数
	这个系数在生物学中也叫Ochiai系数,或Ochiai-Barkman系数:

	这里A和B是集合,n(A)是A的元素个数。如果集合由位元向量所代表,那么可看到Ochiai系数跟余弦相似性是等同的。

8.计算余弦值推导
	1.余弦函数在三角形中的计算公式为

		a和b代表两个向量(向量是在空间中具有大小和方向的量,在数据计量中表示带箭头的线段)

	2.二维空间的余弦函数的公式:将a、b两个向量二维化,通过余弦定理与二维空间结合,即可推导出来二维空间下计算两个向量的余弦相似性公式。

		上图中向量a用坐标(x1,y1)表示,向量b用坐标(x2,y2)表示。向量a和向量b在直角坐标中的长度分别为

		向量a和向量b之间的距离我们用向量c表示,那么向量c在直角坐标系中的长度为

		将a,b,c带入三角函数的公式中便得到了直角坐标系中向量表示的三角形的余弦函数。
		二维向量图和余弦定理的结合:

	3.多维空间的余弦函数的公式

9.计算余弦相似度例子一
	1.余弦相似度量:计算个体间的相似度。
		相似度越小,距离越大。相似度越大,距离越小。
	2.假设有3个物品,item1,item2和item3,用向量表示分别为:item1[1,1,0,0,1],item2[0,0,1,2,1],item3[0,0,1,2,0]。
	3.用欧式距离公式计算item1、itme2之间的距离,以及item2和item3之间的距离,分别是:

	4.用余弦函数计算item1和item2夹角间的余弦值为:

	5.用余弦函数计算item2和item3夹角间的余弦值为:

	6.由此可得出item1和item2相似度小,两个之间的距离大(距离为7),item2和itme3相似度大,两者之间的距离小(距离为1)。
	7.余弦相似度算法:
		一个向量空间中两个向量夹角间的余弦值作为衡量两个个体之间差异的大小,余弦值接近1,夹角趋于0,表明两个向量越相似;
	  	余弦值接近于0,夹角趋于90度,表明两个向量越不相似。
 
10.计算余弦相似度例子二
	1.使用余弦相似度计算两段文本的相似度。
		思路:1、对原文进行jieba中文分词;
		      2、set中去重后存储所有词,并使用dict字段存储set中元素值(key)和索引值(value);
		      3、中文分词进行编码:每个中文分词转换为出现在set中的索引值;
		      4、词频向量化:即计算每个分词出现的次数,通过oneHot编码对分词进行编码来计算词频,从而得到两个句子分别的词频向量;
		      5、根据余弦相似度的公式分别计算出两个句子的的相似度,即计算两个句子分别的词频向量之间夹角的余弦值,值越大相似度越高。
	2.原文句子A:这只皮靴号码大了。那只号码合适。
	  原文句子B:这只皮靴号码不小,那只更合适。
	3.分词
		使用jieba分词对上面两个原文句子分词后,分别得到两个列表:
		listA=['这', '只', '皮靴', '号码', '大', '了', '那', '只', '号码', '合适']
		listB=['这', '只', '皮靴', '号码', '不小', '那', '只','更合', '合适']
	3.列出所有词,将listA和listB放在一个set中,set中去重后得到:
		set={'不小', '了', '合适', '那', '只', '皮靴', '更合', '号码', '这', '大'}
	  将上述set转换为dict,key为set中的词,value为set中词出现的索引位置。
		dict1={'不小': 0, '了': 1, '合适': 2, '那': 3, '只': 4, '皮靴': 5, '更合': 6, '号码': 7, '这': 8, '大': 9},
	  可以看出“不小”这个词在set中排第1,下标为0。
	4.将listA和listB中的中文分词进行编码,将每个中文分词转换为出现在set中的索引值,转换后为:
		listAcode=[8, 4, 5, 7, 9, 1, 3, 4, 7, 2]
		listBcode=[8, 4, 5, 7, 0, 3, 4, 6, 2]
	  我们来分析listAcode,结合dict1,得知元素值8对应的字是“这”,元素值4对应的字是“只”,元素值9对应的字是“大”,就是句子A和句子B转换为用set中的索引值来表示。
	5.对listAcode和listBcode进行oneHot编码,就是计算每个分词出现的次数。
	  词频向量化:即计算每个分词出现的次数,通过oneHot编码对分词进行编码来计算词频,从而得到两个句子分别的词频向量。
	  oneHot编号后得到的结果如下:
		listAcodeOneHot = [0, 1, 1, 1, 2, 1, 0, 2, 1, 1]
		listBcodeOneHot = [1, 0, 1, 1, 2, 1, 1, 1, 1, 0]
 	6.根据余弦相似度的公式分别计算出两个句子的的相似度,即计算两个句子分别的词频向量之间夹角的余弦值,值越大相似度越高。

	7.流程图

11.实现余弦相似度的代码
                import jieba
                import math
                
                s1 = '这只皮靴号码大了。那只号码合适'
                s1_cut = [i for i in jieba.cut(s1, cut_all=True) if i != '']
                s2 = '这只皮靴号码不小,那只更合适'
                s2_cut = [i for i in jieba.cut(s2, cut_all=True) if i != '']
                print(s1_cut) #['这', '只', '皮靴', '号码', '大', '了', '那', '只', '号码', '合适']
                print(s2_cut)#['这', '只', '皮靴', '号码', '不小', '那', '只', '更合', '合适']
                
                #两个set分别对中文切割后的数组元素进行去重,然后两个set联合union放到一起组成一个新的set
                word_set = set(s1_cut).union(set(s2_cut))
                print(word_set)#{'只', '合适', '不小', '更合', '号码', '皮靴', '这', '了', '大', '那'}
                
                word_dict = dict()
                i = 0
                #构建dict字典中的key为set中的每个中文单词,value为set中的该中文单词的索引值
                for word in word_set:
                    word_dict[word] = i
                    i += 1
                print(word_dict) #{'只': 0, '合适': 1, '不小': 2, '更合': 3, '号码': 4, '皮靴': 5, '这': 6, '了': 7, '大': 8, '那': 9}
                
                #把原文中每个中文单词作为key对应的从dict字典中取出对应的value,值为set中的该中文单词的索引值
                s1_cut_code = [word_dict[word] for word in s1_cut]
                print(s1_cut_code) #[6, 0, 5, 4, 8, 7, 9, 0, 4, 1]
                
                #把原文中每个中文单词作为key对应的从dict字典中取出对应的value,值为set中的该中文单词的索引值
                s2_cut_code = [word_dict[word] for word in s2_cut]
                print(s2_cut_code) #[6, 0, 5, 4, 2, 9, 0, 3, 1]
                
                #重新创建一个新的和dict字典键值对数量相同的初始化值为0的数组
                s1_cut_code = [0]*len(word_dict)
                #oneHot编码:计算原文中每个中文分词出现的次数,即能得到原文句子的词频向量
                #根据原文中相同中文单词出现的重复次数记录到新数组中,出现次数要记录到的索引位置为该中文单词在set中索引值,或者说dict字典中的该中文单词作为key对应的value
                for word in s1_cut:
                    s1_cut_code[word_dict[word]]+=1
                print(s1_cut_code)#[2, 1, 0, 0, 2, 1, 1, 1, 1, 1]
                
                #重新创建一个新的和dict字典键值对数量相同的初始化值为0的数组
                s2_cut_code = [0]*len(word_dict)
                #oneHot编码:计算原文中每个中文分词出现的次数,即能得到原文句子的词频向量
                #根据原文中相同中文单词出现的重复次数记录到新数组中,出现次数要记录到的索引位置为该中文单词在set中索引值,或者说dict字典中的该中文单词作为key对应的value
                for word in s2_cut:
                    s2_cut_code[word_dict[word]]+=1
                print(s2_cut_code)#[2, 1, 1, 1, 1, 1, 1, 0, 0, 1]
                
                
                # 计算余弦相似度
                sum = 0
                sq1 = 0
                sq2 = 0
                #通过计算余弦值的公式cos(θ)= (x1*x2 + y1*y2) / sqrt(pow(x1,2)+pow(y1,2)) * sqrt(pow(x2,2)+pow(y2,2))
                #pow(x,y)函数用于求 x 的 y 次方
                for i in range(len(s1_cut_code)):
                    #计算(x1*x2 + y1*y2):x1*x2或y1*y2均代表词频向量s1_cut_code中的元素值 乘以 词频向量s2_cut_code中的元素值
                    sum += s1_cut_code[i] * s2_cut_code[i]
                    #计算pow(x1,2)+pow(y1,2):代表求词频向量s1_cut_code中的每个元素值的2次方
                    sq1 += pow(s1_cut_code[i], 2)
                    #计算pow(x2,2)+pow(y2,2):代表求词频向量s2_cut_code中的每个元素值的2次方
                    sq2 += pow(s2_cut_code[i], 2)
                
                try:
                    #sqrt() 计算平方根,即开平方
                    #round(浮点数x,四舍五入要保留的小数位) 方法返回浮点数x的四舍五入值。
                    #计算出余弦值的结果cos(θ)= (x1*x2 + y1*y2) / sqrt(pow(x1,2)+pow(y1,2)) * sqrt(pow(x2,2)+pow(y2,2))
                    result = round(float(sum) / (math.sqrt(sq1) * math.sqrt(sq2)), 2)
                except ZeroDivisionError:
                    result = 0.0
                print(result) #0.81


含注意力机制的解码器进行⽮量化计算

>>> from mxnet.gluon import nn 
>>> from mxnet import nd
#时间步数seq_len, 批量⼤小batch_size, 隐藏单元个数num_hiddens
>>> seq_len, batch_size, num_hiddens = 10, 4, 8
#键项和值项均为编码器在所有时间步的隐藏状态enc_states,形状为(时间步数, 批量⼤小, 隐藏单元个数)
>>> enc_states = nd.zeros((seq_len, batch_size, num_hiddens))
#解码器在上⼀时间步的隐藏状态dec_state,形状为(批量⼤小, 隐藏单元个数)
>>> dec_state = nd.zeros((batch_size, num_hiddens))
#形状为(时间步数, 批量⼤小, 隐藏单元个数)
>>> enc_states.shape
(10, 4, 8) 
#形状为(批量⼤小, 隐藏单元个数)
>>> dec_state.shape
(4, 8) 
#给形状为(批量⼤小, 隐藏单元个数)的解码器在上⼀时间步的隐藏状态dec_state中 增加多第一维,变成“和编码器在所有时间步的隐藏状态enc_states的”形状相同
>>> dec_state.expand_dims(0).shape
(1, 4, 8)
#编码器在所有时间步的隐藏状态enc_states的第一维
>>> enc_states.shape[0]
10
#将解码器在上⼀时间步的隐藏状态dec_state ⼴播到“和编码器在所有时间步的隐藏状态enc_states的”形状相同
#axis=0 表示在隐藏状态dec_state第一维(axis轴为0)上进行广播大小
#目的把隐藏状态dec_state第一维为1的大小值拓展到“和隐藏状态enc_states”形状相同第一维为10的大小值相同
>>> dec_states = nd.broadcast_axis(dec_state.expand_dims(0), axis=0, size=enc_states.shape[0])
>>> dec_states.shape
(10, 4, 8)
#解码的隐藏状态器dec_states与编码器在所有时间步上隐藏状态enc_states的一一连结
#dim=2 表示在第三维度上连结,enc_states形状(10, 4, 8)和dec_statesenc_states形状(10, 4, 8)两者在第三维度上连结为(10, 4, 16)
>>> enc_and_dec_states = nd.concat(enc_states, dec_states, dim=2)
>>> enc_and_dec_states.shape
(10, 4, 16)
 
#1.Dense实例的flatten选项。
#  当输入的维度⼤于2时,默认情况下,Dense实例会将除了第⼀维(样本维)以外的维度均视作需要仿射变换的特征维,并将输入自动转成行为样本数、列为特征值的二维矩阵。
#  计算后,输出矩阵的形状为(样本数, 输出个数)。如果我们希望全连接层只对输⼊的最后⼀维做仿射变换,而保持其他维度上的形状不变,便需要将Dense实例的flatten选项设为False。
#  在下⾯例⼦中,全连接层只对输⼊的最后⼀维做仿射变换,因此输出形状中只有最后⼀维变为全连接层的输出个数2。
#2.这⾥函数a有多种选择,如果两个输⼊向量⻓度相同,⼀个简单的选择是计算它们的内积a(s,h) = s⊤*h。
#  而最早提出注意⼒机制的论⽂则将输⼊连结后通过含单隐藏层的多层感知机变换:a(S, H) = v⊤*tanh(WsS+ WhH),其中v、Ws、Wh都是可以学习的模型参数。
#3.实现“注意⼒机制”⼀节中定义的函数a:将输⼊连结后通过含单隐藏层的多层感知机变换。
#  其中隐藏层的输⼊是解码器的隐藏状态与编码器在所有时间步上隐藏状态的⼀⼀连结,且使⽤tanh函数作为激活函数。
#  输出层的输出个数为1。两个Dense实例均不使⽤偏差use_bias=False,且设flatten=False。
#  其中函数a定义⾥向量v的⻓度是⼀个超参数,即attention_size。
>>> model = nn.Sequential()
>>> attention_size = 10
>>> model.add(nn.Dense(attention_size, activation='tanh', use_bias=False, flatten=False), nn.Dense(1, use_bias=False, flatten=False))
>>> model.initialize()

#做前向计算,输出形状为(时间步数, 批量大小, 1)
>>> e = model(enc_and_dec_states)
>>> e.shape #形状为(时间步数, 批量大小, 1)
(10, 4, 1)

#softmax(e, axis=0) 表示在时间步维度做softmax运算
>>> alpha = nd.softmax(e, axis=0) 
>>> alpha.shape
(10, 4, 1)

>>> e
[[[0.]
  [0.]
  [0.]
  [0.]]

 [[0.]
  [0.]
  [0.]
  [0.]]

 [[0.]
  [0.]
  [0.]
  [0.]]

 [[0.]
  [0.]
  [0.]
  [0.]]

 [[0.]
  [0.]
  [0.]
  [0.]]

 [[0.]
  [0.]
  [0.]
  [0.]]

 [[0.]
  [0.]
  [0.]
  [0.]]

 [[0.]
  [0.]
  [0.]
  [0.]]

 [[0.]
  [0.]
  [0.]
  [0.]]

 [[0.]
  [0.]
  [0.]
  [0.]]]
<NDArray 10x4x1 @cpu(0)>

>>> alpha
[[[0.1]
  [0.1]
  [0.1]
  [0.1]]

 [[0.1]
  [0.1]
  [0.1]
  [0.1]]

 [[0.1]
  [0.1]
  [0.1]
  [0.1]]

 [[0.1]
  [0.1]
  [0.1]
  [0.1]]

 [[0.1]
  [0.1]
  [0.1]
  [0.1]]

 [[0.1]
  [0.1]
  [0.1]
  [0.1]]

 [[0.1]
  [0.1]
  [0.1]
  [0.1]]

 [[0.1]
  [0.1]
  [0.1]
  [0.1]]

 [[0.1]
  [0.1]
  [0.1]
  [0.1]]

 [[0.1]
  [0.1]
  [0.1]
  [0.1]]]
<NDArray 10x4x1 @cpu(0)>

#最终计算背景变量,注意⼒机制采⽤更⾼效的⽮量化计算(softmax(QK⊤)V):编码器在所有时间步的隐藏状态enc_states和softmax运算输出的值相乘
#alpha.shape为 (10, 4, 1),enc_states.shape为 (10, 4, 8),(alpha * enc_states).shape为 (10, 4, 8),sum(axis=0)表示在第一维上进行相加和
>>> result = (alpha * enc_states).sum(axis=0)
>>> result.shape
(4, 8)

机器翻译(使用含注意力机制的编码器—解码器)

one-hot向量化中实现的循环神经网络:
	1.输入数据inputs:
		输入数据数据为List列表,列表的元素数量为时间步数,每个元素(时间步t)为(批量⼤小,词典⼤小)形状的矩阵。
		inputs和outputs皆为num_steps个形状为(batch_size, vocab_size)的矩阵。
	2.输入隐藏状态state:
		初始化的隐藏状态为元组,元祖中可包含多个(批量大小, 隐藏单元个数)形状的NDArray。
		定义init_rnn_state函数来返回初始化的隐藏状态。由一个形状为(批量大小, 隐藏单元个数)的值为0的NDArray组成的元组。
		使用元组是为了更便于处理隐藏状态含有多个NDArray的情况。
	3.输出数据outputs:
		输入数据和输出数据的形状相同。
		输出数据为List列表,列表的元素数量为时间步数,每个元素(时间步t)为(批量⼤小,词典⼤小)形状的矩阵。
		inputs和outputs皆为num_steps个形状为(batch_size, vocab_size)的矩阵
	4.输出隐藏状态state:
		输入隐藏状态和输出隐藏状态的形状相同。
		输出隐藏状态为元组,元祖中可包含多个(批量大小, 隐藏单元个数)形状的NDArray。
		由一个形状为(批量大小, 隐藏单元个数)的值为0的NDArray组成的元组。使用元组是为了更便于处理隐藏状态含有多个NDArray的情况。
		对于门控循环单元来说,state列表中只含一个元素,即隐藏状态;如果使用长短期记忆,state列表中还将包含另一个元素,即记忆细胞。

1.最终inputs变量为一个List列表,列表中元素数量即为时间步数。
2.列表中的一个元素,实际是这个批量中所有多个样本的相同时间步t都构建到同一个矩阵(一维数组)中,
  列表中的每个元素代表这个批量中所有多个样本的每个相同时间步t。
3.我们一开始取样的数据的形状为(批量大小,时间步),批量大小即一个批量中的样本数,此处每个样本都有相同的时间步数。
  把(批量大小,时间步)转置为(时间步,批量大小)的形状之后,然后把每个样本的相同的时间步t都构建到同一个矩阵(一维数组)中,
  然后这个矩阵(一维数组)中的每个元素进行one-hot向量化,即一个元素值转换为一个一维数组,这个一维数组的大小为词典大小,
  然后把一维数组中“和这个元素值值相同”的索引值位置上的元素置为1,其他的都置为0。
  最终(批量大小,时间步)形状的批量数据经过one-hot向量化之后,变成了一个List列表,列表中元素总数等于样本中的时间步数,
  每个元素为(批量⼤小,词典⼤小)形状的矩阵。
4.例子:对批量中每个样本的多个时间步进行one-hot向量化
	>>> from mxnet import nd
	>>> X = nd.arange(10).reshape((2, 5))
	>>> X
		[[0. 1. 2. 3. 4.]
		 [5. 6. 7. 8. 9.]]
		<NDArray 2x5 @cpu(0)>
	>>> X.T
		[[0. 5.]
		 [1. 6.]
		 [2. 7.]
		 [3. 8.]
		 [4. 9.]]
		<NDArray 5x2 @cpu(0)>
	>>> for x in X.T:
	...     x
		[0. 5.]
		<NDArray 2 @cpu(0)>
		[1. 6.]
		<NDArray 2 @cpu(0)>
		[2. 7.]
		<NDArray 2 @cpu(0)>
		[3. 8.]
		<NDArray 2 @cpu(0)>
		[4. 9.]
		<NDArray 2 @cpu(0)>
		
	>>> for x in X.T:
	...     nd.one_hot(x, 10)
		[[1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
		 [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]]
		<NDArray 2x10 @cpu(0)>
		[[0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
		 [0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]]
		<NDArray 2x10 @cpu(0)>
		[[0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]
		 [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]]
		<NDArray 2x10 @cpu(0)>
		[[0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]
		 [0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]]
		<NDArray 2x10 @cpu(0)>
		[[0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
		 [0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]]
		<NDArray 2x10 @cpu(0)>
#含单隐藏层、num_hiddens隐藏单元个数为256
rnn_layer = rnn.RNN(num_hiddens=256) 

#1.batch_size批量大小即是指单个批量中的样本数
#2.成员函数begin_state返回initialize()初始化后的隐藏状态,返回的变量是一个List列表,此时的List列表中只有一个元素。
#3.List列表中第一个元素为初始化的隐藏状态,形状为(隐藏层个数, 批量⼤小, 隐藏单元个数)
#  对于门控循环单元来说,state列表中只含一个元素,即隐藏状态;如果使用长短期记忆,state列表中还将包含另一个元素,即记忆细胞。
state = rnn_layer.begin_state(batch_size=2)
#state隐藏状态为列表,state[0]即列表中第一个元素的形状(1, 2, 256) 代表 (隐藏层个数, 批量⼤小, 隐藏单元个数)
#对于门控循环单元来说,state列表中只含一个元素,即隐藏状态;如果使用长短期记忆,state列表中还将包含另一个元素,即记忆细胞。
state[0].shape #(1, 2, 256) 

#1.上一节one-hot向量化中实现的循环神经网络中,inputs和outputs皆为num_steps个形状为(batch_size, vocab_size)的矩阵,
#  即inputs和outputs均为List列表,列表的元素数量为时间步数,每个元素(时间步t)为(批量⼤小,词典⼤小)形状的矩阵。
#2.与上一节中实现的循环神经网络不同,这里rnn_layer的输入形状为(时间步数, 批量大小, 输入个数)。其中输入个数即one-hot向量长度(词典大小),
#  即(时间步数, 批量大小, 词典⼤小)。state输入隐藏状态为列表,state[0]即列表中第一个元素的形状为(隐藏层个数, 批量⼤小, 隐藏单元个数)。
#  对于门控循环单元来说,state列表中只含一个元素,即隐藏状态;如果使用长短期记忆,state列表中还将包含另一个元素,即记忆细胞。
#3.此外,rnn_layer作为Gluon的rnn.RNN实例,在通过rnn_layer(输入数据, 隐藏状态)前向计算后会分别返回输出outputs和隐藏状态state_new,
#  其中输出outputs指的是隐藏层在各个时间步上计算并输出的隐藏状态,它们通常作为后续输出层的输入。
#  需要强调的是,该“输出”本身并不涉及输出层计算,形状为(时间步数, 批量大小, 隐藏单元个数),该前向计算并不涉及输出层计算。
#  而rnn.RNN实例在前向计算返回的隐藏状态指的是隐藏层在最后时间步的可用于初始化下一时间步的隐藏状态:当隐藏层有多层时,每一层的隐藏状态都会记录在该变量中;
#  对于像长短期记忆这样的循环神经网络,该变量还会包含其他信息。我们会在本章的后面介绍长短期记忆和深度循环神经网络。

num_steps = 35 #时间步数
X = nd.random.uniform(shape=(num_steps, batch_size, vocab_size)) #(时间步数, 批量大小, 词典⼤小)
#state隐藏状态为列表,state[0]即列表中第一个元素的形状(1, 2, 256)代表(隐藏层个数, 批量⼤小, 隐藏单元个数)
#rnn_layer(输入数据, 隐藏状态)前向计算后会分别返回输出outputs和隐藏状态state_new
Y, state_new = rnn_layer(X, state) 
#输出outputs形状为(时间步数, 批量大小, 隐藏单元个数)
#输出隐藏状态state_new为列表,state[0]即列表中第一个元素的形状为(隐藏层个数, 批量⼤小, 隐藏单元个数)
#对于门控循环单元来说,state列表中只含一个元素,即隐藏状态;如果使用长短期记忆,state列表中还将包含另一个元素,即记忆细胞。
Y.shape, len(state_new), state_new[0].shape #((35, 2, 256), 1, (1, 2, 256))

in_vocab:输入法语词汇表Vocabulary对象
out_vocab:输出英语词汇表Vocabulary对象
dataset:包含每个法语样本中的词索引序列和每个英语样本中的词索引序列
dataset[0]:同时获取第一个法语样本和第一个英语样本中的词索引序列

 

#嵌入层的权重是一个矩阵,其行数为词典大小vocab_size,列数为每个词向量的维度embed_size
self.embedding = nn.Embedding(vocab_size, embed_size)
#设门控循环单元的隐藏层num_layers个数为2,隐藏单元num_hiddens个数为16
self.rnn = rnn.GRU(num_hiddens, num_layers, dropout=drop_prob)

def forward(self, inputs, state):
	#将法语输入每个样本的词索引通过词嵌入层Embedding得到词的表征
	#inputs输入形状是(批量大小, 时间步数),输出形状为(批量大小, 词数, 词向量维度),批量大小即样本数/样本维,时间步数即词数
	#swapaxes(0, 1)将输出互换样本维和时间步维,输出形状变换为(词数, 批量大小, 词向量维度)
        	embedding = self.embedding(inputs).swapaxes(0, 1)
	#1.将提取的词的表征(词数, 批量大小, 词向量维度)输入到一个多层门控循环单元中
	#  同时还输入初始化后的隐藏状态List列表,第一个元素形状为(隐藏层个数, 批量⼤小, 隐藏单元个数)
	#2.编码器对该输入执行前向计算后返回的输出output和最终时间步的多层隐藏状态state。
	#  输出output形状为(时间步数, 批量大小, 隐藏单元个数)。门控循环单元在最终时间步的多层隐藏状态的形状为(隐藏层个数, 批量大小, 隐藏单元个数)。
	#  输出指的是最后一层的隐藏层在各个时间步的隐藏状态,并不涉及输出层计算。注意力机制中将输出output作为键项和值项。
	#  对于门控循环单元来说,前向计算返回的最终时间步的多层隐藏状态state实际为列表,并且列表中只含一个元素,该元素才是真正的多层隐藏状态;
	#  最终时间步的多层隐藏状态可用作为初始化下一时间步的隐藏状态。如果使用长短期记忆,state列表中还将包含另一个元素,即记忆细胞。
        	return self.rnn(embedding, state)

def begin_state(self, *args, **kwargs):
	#传入batch_size批量大小,返回initialize()初始化后的隐藏状态是一个List列表。
	#List列表中第一个元素为初始化的隐藏状态,形状为(隐藏层个数, 批量⼤小, 隐藏单元个数)
	#对于门控循环单元来说,state列表中只含一个元素,即隐藏状态。如果使用长短期记忆,state列表中还将包含另一个元素,即记忆细胞。
	return self.rnn.begin_state(*args, **kwargs)

1.c = attention_forward(self.attention, enc_states, state[0][-1])
  在解码器的前向计算attention_forward中,先通过注意⼒机制计算得到当前时间步的背景向量c。
  attention:其中函数a定义⾥向量v的⻓度是⼀个超参数
  enc_states:键项和值项均为编码器在所有时间步的隐藏状态,形状为(时间步数, 批量⼤小, 隐藏单元个数)
  state[0][-1]:查询项为解码器在上⼀时间步的隐藏状态,形状为(批量⼤小, 隐藏单元个数)
2.input_and_c = nd.concat(self.embedding(cur_input), c, dim=1)
  由于解码器的输⼊cur_input来⾃输出语⾔的词索引,我们将输⼊通过词嵌⼊层embedding(cur_input)得到表征,
  然后和背景向量c在特征维dim=1连结得到input_and_c。
3.output, state = self.rnn(input_and_c.expand_dims(0), state)
  为输⼊和背景向量的连结input_and_c增加时间步维expand_dims(0),时间步个数为1。
  我们将连结后的结果input_and_c与上⼀时间步的隐藏状态state,
  通过⻔控循环单元计算出当前时间步的输出output与隐藏状态state。
4.output = self.out(output).squeeze(axis=0)
  将输出output通过全连接层out=nn.Dense变换为有关各个输出词的预测,
  移除时间步维squeeze(axis=0),输出形状为(批量⼤⼩, 输出词典⼤⼩)

import collections
import io
import math
from mxnet import autograd, gluon, init, nd
from mxnet.contrib import text
from mxnet.gluon import data as gdata, loss as gloss, nn, rnn

PAD, BOS, EOS = '<pad>', '<bos>', '<eos>'


# 将一个序列中所有的词记录在all_tokens中以便之后构造词典,然后在该序列后面添加PAD直到序列
# 长度变为max_seq_len,然后将序列保存在all_seqs中
def process_one_seq(seq_tokens, all_tokens, all_seqs, max_seq_len):
    all_tokens.extend(seq_tokens)
    seq_tokens += [EOS] + [PAD] * (max_seq_len - len(seq_tokens) - 1)
    all_seqs.append(seq_tokens)

	
# 使用所有的词来构造词典。并将所有序列中的词变换为词索引后构造NDArray实例
def build_data(all_tokens, all_seqs):
    vocab = text.vocab.Vocabulary(collections.Counter(all_tokens),
                                  reserved_tokens=[PAD, BOS, EOS])
    indices = [vocab.to_indices(seq) for seq in all_seqs]
    return vocab, nd.array(indices)
	
	
def read_data(max_seq_len):
    # in和out分别是input和output的缩写
    in_tokens, out_tokens, in_seqs, out_seqs = [], [], [], []
    with io.open('fr-en-small.txt') as f:
        lines = f.readlines()
    for line in lines:
        in_seq, out_seq = line.rstrip().split('\t')
        in_seq_tokens, out_seq_tokens = in_seq.split(' '), out_seq.split(' ')
        if max(len(in_seq_tokens), len(out_seq_tokens)) > max_seq_len - 1:
            continue  # 如果加上EOS后长于max_seq_len,则忽略掉此样本
        process_one_seq(in_seq_tokens, in_tokens, in_seqs, max_seq_len)
        process_one_seq(out_seq_tokens, out_tokens, out_seqs, max_seq_len)
    in_vocab, in_data = build_data(in_tokens, in_seqs)
    out_vocab, out_data = build_data(out_tokens, out_seqs)
    return in_vocab, out_vocab, gdata.ArrayDataset(in_data, out_data)
	
	
max_seq_len = 7
in_vocab, out_vocab, dataset = read_data(max_seq_len)
#print(out_vocab.token_to_idx[EOS]) #3
#print(out_vocab.token_to_idx[PAD]) #1
dataset[0]
#(
# [ 6.  5. 46.  4.  3.  1.  1.]
# <NDArray 7 @cpu(0)>, 
# [ 9.  5. 28.  4.  3.  1.  1.]
# <NDArray 7 @cpu(0)>)


class Encoder(nn.Block):
    def __init__(self, vocab_size, embed_size, num_hiddens, num_layers,
                 drop_prob=0, **kwargs):
        super(Encoder, self).__init__(**kwargs)
        self.embedding = nn.Embedding(vocab_size, embed_size)
        self.rnn = rnn.GRU(num_hiddens, num_layers, dropout=drop_prob)

    def forward(self, inputs, state):
        # 输入形状是(批量大小, 时间步数)。将输出互换样本维和时间步维
        #将法语输入每个样本的词索引通过词嵌入层Embedding得到词的表征
        #inputs输入形状是(批量大小, 时间步数),输出形状为(批量大小, 词数, 词向量维度),批量大小即样本数/样本维,时间步数即词数
        #swapaxes(0, 1)将输出互换样本维和时间步维,输出形状变换为(词数, 批量大小, 词向量维度)
        embedding = self.embedding(inputs).swapaxes(0, 1)
        #1.将提取的词的表征(词数, 批量大小, 词向量维度)输入到一个多层门控循环单元中
        #  同时还输入初始化后的隐藏状态List列表,第一个元素形状为(隐藏层个数, 批量⼤小, 隐藏单元个数)
        #2.编码器对该输入执行前向计算后返回的输出output和最终时间步的多层隐藏状态state。
        #  输出output形状为(时间步数, 批量大小, 隐藏单元个数)。门控循环单元在最终时间步的多层隐藏状态的形状为(隐藏层个数, 批量大小, 隐藏单元个数)。
        #  输出指的是最后一层的隐藏层在各个时间步的隐藏状态,并不涉及输出层计算。注意力机制中将输出output作为键项和值项。
        #  对于门控循环单元来说,前向计算返回的最终时间步的多层隐藏状态state实际为列表,并且列表中只含一个元素,该元素才是真正的多层隐藏状态;
        #  最终时间步的多层隐藏状态可用作为初始化下一时间步的隐藏状态。如果使用长短期记忆,state列表中还将包含另一个元素,即记忆细胞。
        #3.门控循环单元执行前向计算后返回的输出output和最终时间步的多层隐藏状态state
        return self.rnn(embedding, state)

    def begin_state(self, *args, **kwargs):
        #传入batch_size批量大小,返回initialize()初始化后的隐藏状态是一个List列表。
        #List列表中第一个元素为初始化的隐藏状态,形状为(隐藏层个数, 批量⼤小, 隐藏单元个数)
        #对于门控循环单元来说,state列表中只含一个元素,即隐藏状态。如果使用长短期记忆,state列表中还将包含另一个元素,即记忆细胞。
        return self.rnn.begin_state(*args, **kwargs)
		
		
encoder = Encoder(vocab_size=10, embed_size=8, num_hiddens=16, num_layers=2)
encoder.initialize()
output, state = encoder(nd.zeros((4, 7)), encoder.begin_state(batch_size=4))
output.shape, state[0].shape #((7, 4, 16), (2, 4, 16))


dense = nn.Dense(2, flatten=False)
dense.initialize()
dense(nd.zeros((3, 5, 7))).shape #(3, 5, 2)
 
 
def attention_model(attention_size):
    model = nn.Sequential()
    model.add(nn.Dense(attention_size, activation='tanh', use_bias=False,flatten=False),
              nn.Dense(1, use_bias=False, flatten=False))
    return model


def attention_forward(model, enc_states, dec_state):
    #将解码器隐藏状态广播到和编码器隐藏状态形状相同后进行连结。
    #把代表查询项的“为解码器在上一时间步的”隐藏状态的形状(批量大小, 隐藏单元个数)变换为(时间步数, 批量大小, 隐藏单元个数)。
    dec_states = nd.broadcast_axis(dec_state.expand_dims(0), axis=0, size=enc_states.shape[0])
    #print("dec_states:",dec_states.shape) #(7, 2, 64)
    
    #键项和值项均为编码器在所有时间步的隐藏状态enc_states,形状为(时间步数, 批量大小, 隐藏单元个数)
    #enc_states和dec_states均为(7, 2, 64),因此concat第三维之后变成(7, 2, 128)
    enc_and_dec_states = nd.concat(enc_states, dec_states, dim=2)
    #print("enc_and_dec_states:",enc_and_dec_states.shape) #(7, 2, 128)
    
    #使用了Dense,设置输出层的输出个数为1,因为flatten=False,所以全连接层只对输入的最后一维做仿射变换,(7, 2, 128)变成(7, 2, 1)
    e = model(enc_and_dec_states)  # 形状为(时间步数, 批量大小, 1)
    #print("e:",e.shape) #(7, 2, 1)
    
    alpha = nd.softmax(e, axis=0)  # 在时间步维度做softmax运算
    #print("alpha:",alpha.shape) #(7, 2, 1)
    #print("enc_states:",enc_states.shape) #(7, 2, 64)
    #print("alpha * enc_states:",(alpha * enc_states).shape) #(7, 2, 64)
    
    #(时间步数, 批量大小, 1) * (时间步数, 批量大小, 隐藏单元个数) = (时间步数, 批量大小, 隐藏单元个数)
    #(时间步数, 批量大小, 隐藏单元个数).sum(axis=0) 变成 (批量大小, 隐藏单元个数)
    return (alpha * enc_states).sum(axis=0)  # 返回背景变量



seq_len, batch_size, num_hiddens = 10, 4, 8
model = attention_model(10)
model.initialize()
enc_states = nd.zeros((seq_len, batch_size, num_hiddens))
dec_state = nd.zeros((batch_size, num_hiddens))
attention_forward(model, enc_states, dec_state).shape #(4, 8)


class Decoder(nn.Block):
    def __init__(self, vocab_size, embed_size, num_hiddens, num_layers,
                 attention_size, drop_prob=0, **kwargs):
        super(Decoder, self).__init__(**kwargs)
        self.embedding = nn.Embedding(vocab_size, embed_size)
        self.attention = attention_model(attention_size)
        self.rnn = rnn.GRU(num_hiddens, num_layers, dropout=drop_prob)
        self.out = nn.Dense(vocab_size, flatten=False)

    def forward(self, cur_input, state, enc_states):
        #print("state.class:",type(state)) # <class 'list'>
        #print("state[0].class:",type(state[0])) #<class 'mxnet.ndarray.ndarray.NDArray'>
        #print("state[0]:",state[0].shape) #(2, 2, 64)
        #print("state[0][-1]:",state[0][-1].shape) #(2, 64)。[-1]取三维数组中的后两维。
 
        #1.使用注意力机制计算背景向量。注意力机制返回当前时间步的背景变量c,形状为(批量大小, 隐藏单元个数),即(2, 64)。
        #2.传入 注意力超参数attention、形状为(时间步数, 批量大小, 隐藏单元个数)的编码器在所有时间步的隐藏状态enc_states、
        #  state[0][-1]代表查询项的“为解码器在上一时间步的”隐藏状态,形状为(批量大小, 隐藏单元个数)
        c = attention_forward(self.attention, enc_states, state[0][-1])
        
        #1.将嵌入后的输入和背景向量c在特征维连结。
        #2.输入数据cur_input为真实标签序列的词索引,形状为(2,),即(时间步,)。
        #  由于解码器的输入cur_input来自输出语言的词索引,我们将输入通过词嵌入层embedding得到表征(2, 64),即(批量大小,词向量维度)
        #3.然后形状为(2, 64)的词表征和形状为(2, 64)的背景向量c在dim=1的特征维上连结 得出形状为(2, 128)。
        #print("embedding(cur_input).shape:",self.embedding(cur_input).shape) # (2, 64)
        #print("c:",c) # (2, 64)
        
        input_and_c = nd.concat(self.embedding(cur_input), c, dim=1)
        #print("input_and_c:",input_and_c.shape) #(2, 128)
        #print("input_and_c.expand_dims(0):",input_and_c.expand_dims(0).shape) #(1, 2, 128)
        
        #1.为输入和背景向量的连结input_and_c增加时间步维,时间步个数为1,时间步个数即词数,即变为(词数/时间步数, 批量大小, 词向量维度+隐藏单元个数)
        #  我们将连结后的结果与上一时间步的隐藏状态state 通过门控循环单元 计算出当前时间步的输出output与隐藏状态state。
        #2.解码器的前向计算输出的output形状为(时间步数, 批量大小, 隐藏单元个数),即(1, 2, 64)。
        #3.解码器的前向计算输出的隐藏状态state为List列表,列表中的第一个元素才是真正的隐藏状态,形状为(隐藏层个数, 批量⼤小, 隐藏单元个数),即(2, 2, 64)
        #  解码器的当前时间步的的前向计算输出的隐藏状态可作为下一个时间步前向计算的隐藏状态的输入。
        output, state = self.rnn(input_and_c.expand_dims(0), state)
        #print("output:",output.shape) #(1, 2, 64)
        #print("state:",len(state)) #1
        #print("state.class:",type(state)) #<class 'list'>
 
        #移除时间步维,输出形状为(批量大小, 输出词典大小)
        #因为最终还要将输出通过全连接层变换为输出词典中有关各个输出词的预测,形状为(批量大小, 输出词典大小)。
        #所以然后再经过Dense(vocab_size, flatten=False),(时间步数, 批量大小, 隐藏单元个数) 变成了 (时间步数, 批量大小, 输出词典大小),
        #最终再经过squeeze(axis=0)移除了时间步维,因此输出形状变为(批量大小, 输出词典大小)。
        output = self.out(output).squeeze(axis=0)
        return output, state

    def begin_state(self, enc_state):
        # 直接将编码器最终时间步的隐藏状态作为解码器的初始隐藏状态
        return enc_state



def batch_loss(encoder, decoder, X, Y, loss):
    #X和Y的形状都均为(批量大小,时间步数),批量大小为样本数,时间步数为每个样本中的单词数量
    #print("X:",X.shape) #(2, 7)
    #print("Y:",Y.shape) #(2, 7)
    
    batch_size = X.shape[0] #批量大小
    #传入batch_size批量大小,返回initialize()初始化后的隐藏状态是一个List列表
    #List列表中第一个元素为初始化的隐藏状态,形状为(隐藏层个数, 批量⼤小, 隐藏单元个数)
    #对于门控循环单元来说,state列表中只含一个元素,即隐藏状态。如果使用长短期记忆,state列表中还将包含另一个元素,即记忆细胞。
    enc_state = encoder.begin_state(batch_size=batch_size)
    #print("enc_state1:",len(enc_state)) #1
    
    #调用forward函数进行前向计算,最终门控循环单元执行前向计算后返回的输出output和最终时间步的多层隐藏状态state
    enc_outputs, enc_state = encoder(X, enc_state)
    #print("enc_state2:",len(enc_state)) #1
    #print("enc_state2.class:",type(enc_state)) #<class 'list'>
    #print("enc_outputs:",enc_outputs.shape) #(7, 2, 64) 即 (时间步数, 批量大小, 隐藏单元个数)

    #直接将编码器在最终时间步的隐藏状态作为解码器的初始隐藏状态,但这编码器和解码器的循环神经网络使用相同的隐藏层个数和隐藏单元个数
    #解码器的初始隐藏状态 同样为List列表,封装到第一个元素才是真正的隐藏状态,形状为(隐藏层个数, 批量⼤小, 隐藏单元个数),即(2, 2, 64)
    dec_state = decoder.begin_state(enc_state)
    #print("dec_state1:",len(dec_state)) #1
    
    # 解码器在最初时间步的输入是BOS,形状为(2,)
    dec_input = nd.array([out_vocab.token_to_idx[BOS]] * batch_size)
    # 我们将使用掩码变量mask来忽略掉标签为填充项PAD的损失
    mask, num_not_pad_tokens = nd.ones(shape=(batch_size,)), 0
    l = nd.array([0])
    
    #Y的形状为(2,7),Y.T的形状为(7,2)。2代表批量大小,7代表每个样本的元素值数量,此处每个样本的元素值为单词的索引值
    #Y为真实标签序列的批量样本数据,根据for y in Y.T循环每次同时遍历出2个样本中的相同时间步上的索引值,一共遍历7次。
    for y in Y.T:
        print("y:",y.shape) #(2,) 
        #1.dec_input:输入数据为真实标签序列的词索引,形状为(2,),即(批量大小,)。
        #  因为此处使用的是强制教学,所以每次遍历出的词索引是批量中的每个样本的同一个时间步的词索引。
        #2.注意⼒机制的输⼊包括查询项、键项和值项。设编码器和解码器的隐藏单元个数相同。
        #  注意力机制返回当前时间步的背景变量,形状为(批量大小, 隐藏单元个数)。
        #    1.dec_state:第一次是以编码器在最终时间步的隐藏状态List列表作为解码器的初始隐藏状态,之后每次都是使用解码器在上一时间步的隐藏状态List列表。
        #      但在每次的注意力计算之前,不是直接使用隐藏状态List列表,因此还要执行state[0][-1],state[0]表示获取List列表中第一个隐藏状态元素,
        #      即为(隐藏层个数, 批量⼤小, 隐藏单元个数),即(2, 2, 64),而state[0][-1]表示获取出的隐藏状态形状为(批量⼤小, 隐藏单元个数),
        #      最后再执行state.expand_dims(0)将解码器隐藏状态广播到和编码器隐藏状态形状相同,形状变为(时间步数, 批量大小, 隐藏单元个数)。
        #      这里的查询项为解码器在上一时间步的隐藏状态,形状为(批量大小, 隐藏单元个数);
        #    2.enc_outputs:编码器在所有时间步的隐藏状态,形状为(时间步数, 批量大小, 隐藏单元个数)。
        #      键项和值项均为编码器在所有时间步的隐藏状态,形状都均为(时间步数, 批量大小, 隐藏单元个数)。
        #3.dec_output:
        #  解码器的前向计算输出的output形状为(时间步数, 批量大小, 隐藏单元个数),即(1, 2, 64)。
        #  因为最终还要将输出通过全连接层变换为输出词典中有关各个输出词的预测,形状为(批量大小, 输出词典大小)。
        #  所以然后再经过Dense(vocab_size, flatten=False),(时间步数, 批量大小, 隐藏单元个数) 变成了 (时间步数, 批量大小, 输出词典大小),
        #  最终再经过squeeze(axis=0)移除了时间步维,因此输出形状变为(批量大小, 输出词典大小)。
        #4.dec_state:解码器的前向计算输出的隐藏状态dec_state为List列表,列表中的第一个元素才是真正的隐藏状态,形状为(隐藏层个数, 批量⼤小, 隐藏单元个数),即(2, 2, 64)
        #  解码器的当前时间步的的前向计算输出的隐藏状态可作为下一个时间步前向计算的隐藏状态的输入。
        dec_output, dec_state = decoder(dec_input, dec_state, enc_outputs)
        #print("dec_state2:",len(dec_state))   #1
        #print("dec_output:",dec_output.shape) #(2, 39)

        #输出词典中有关各个输出词的预测dec_output 和 样本输出序列(真实标签序列)中的对应真实时间步的单词y 放到一起通过交叉熵损失函数计算损失值
        l = l + (mask * loss(dec_output, y)).sum()
        #1.在模型训练中,所有输出序列损失的均值通常作为需要最小化的损失函数。
        #  在图10.8所描述的模型预测中,我们需要将解码器在上一个时间步的输出作为当前时间步的输入。
        #  与此不同的是,在训练中我们也可以将标签序列(训练集的真实输出标签序列)在上一个时间步的真实标签作为解码器在当前时间步的输入,
        #  这叫作强制教学(teacher forcing)。
        #2.解码器在某时间步的输入为样本输出序列(真实标签序列)在上一时间步的词,即强制教学。在编码器—解码器的训练中,可以采用强制教学。
        #3.问题:在训练中,将强制教学替换为使用解码器在上一时间步的输出作为解码器在当前时间步的输入,结果有什么变化吗?
        dec_input = y  # 使用强制教学
        num_not_pad_tokens += mask.sum().asscalar()
        #1.当遇到EOS时,序列后面的词将均为PAD,相应位置的掩码设成0
        #2.token_to_idx[EOS]的索引值实际会与矩阵大小为(2,)的y中的两个索引值逐个比较,如果token_to_idx[EOS]的索引值不等于y中的某个索引值的话,
        #  该索引值返回1,如果token_to_idx[EOS]的索引值不等于y中的某个索引值的话,该索引值返回0,即表示此时y中的该索引值代表EOS,
        #  因此该判断表达式实际返回一个大小为(2,)的矩阵,但矩阵中元素只为1或0。
        #3.如果其中包含0的大小为(2,)的矩阵跟大小为(2,)的矩阵mask相乘的话,那么mask对应位置上的元素值即为0。
        #4.只要遇到EOS的话,mask对应位置上值变为0,那么即使遍历到后面为填充项PAD的话,便会开始继续使用带有0的mask来计算,
        #  从而达到使用掩码变量mask来忽略掉标签为填充项PAD的损失。
        mask = mask * (y != out_vocab.token_to_idx[EOS])
        
    return l / num_not_pad_tokens



def train(encoder, decoder, dataset, lr, batch_size, num_epochs):
    #强制Xavier初始化
    encoder.initialize(init.Xavier(), force_reinit=True)
    decoder.initialize(init.Xavier(), force_reinit=True)
    #设置Trainer训练器,通过adam算法进行优化调整权重/偏差等超参数,,学习率为lr
    enc_trainer = gluon.Trainer(encoder.collect_params(), 'adam',{'learning_rate': lr})
    dec_trainer = gluon.Trainer(decoder.collect_params(), 'adam',{'learning_rate': lr})
    #交叉熵损失函数 softmax 和 cross-entropy 放在一起使用, 可以大大减少梯度求解的计算量
    loss = gloss.SoftmaxCrossEntropyLoss()
    data_iter = gdata.DataLoader(dataset, batch_size, shuffle=True)
    for epoch in range(num_epochs):
        l_sum = 0.0
        for X, Y in data_iter:
            with autograd.record():
                #X和Y的形状都均为(批量大小,时间步数),批量大小为样本数,时间步数为每个样本中的单词数量
                l = batch_loss(encoder, decoder, X, Y, loss)
            l.backward()
            enc_trainer.step(1)
            dec_trainer.step(1)
            l_sum += l.asscalar()
        if (epoch + 1) % 10 == 0:
            print("epoch %d, loss %.3f" % (epoch + 1, l_sum / len(data_iter)))



#每个词向量的维度embed_size、隐藏层个数num_layers、隐藏单元个数num_hiddens
embed_size, num_hiddens, num_layers = 64, 64, 2
#注意力超参数attention_size、丢弃率drop_prob、学习率lr、批量大小(单个批量中的样本数)batch_size、训练周期num_epochs
attention_size, drop_prob, lr, batch_size, num_epochs = 10, 0.5, 0.01, 2, 50
#print("in_vocab",len(in_vocab)) #输入词典大小,即47个单词
#print("out_vocab",len(out_vocab))#输出词典大小,即39个单词
#print("BOS",out_vocab.token_to_idx[BOS])#词索引为2
#print("EOS",out_vocab.token_to_idx[EOS])#词索引为3
#print("PAD",out_vocab.token_to_idx[PAD])#词索引为1

#传入 输入词典大小、每个词向量的维度、隐藏单元个数、隐藏层个数、丢弃率
encoder = Encoder(len(in_vocab), embed_size, num_hiddens, num_layers,drop_prob)
#传入 输出词典大小、每个词向量的维度、隐藏单元个数、隐藏层个数、注意力超参数、丢弃率
decoder = Decoder(len(out_vocab), embed_size, num_hiddens, num_layers,attention_size, drop_prob)
train(encoder, decoder, dataset, lr, batch_size, num_epochs)



def translate(encoder, decoder, input_seq, max_seq_len):
    in_tokens = input_seq.split(' ')
    in_tokens += [EOS] + [PAD] * (max_seq_len - len(in_tokens) - 1)
    enc_input = nd.array([in_vocab.to_indices(in_tokens)])
    enc_state = encoder.begin_state(batch_size=1)
    enc_output, enc_state = encoder(enc_input, enc_state)
    dec_input = nd.array([out_vocab.token_to_idx[BOS]])
    dec_state = decoder.begin_state(enc_state)
    output_tokens = []
    for _ in range(max_seq_len):
        #1.len(out_vocab))为39,代表Vocabulary词汇表out_vocab有39个单词,包括PAD/BOS/EOS,索引分别为1/2/3
        #2.dec_output形状为1x39,代表Vocabulary词汇表39个单词每个单词所对应的概率值大小,模型则预测出每个单词的概率值,
        #  获取最大概率值所在的索引,可根据该索引获取Vocabulary词汇表相同索引上的单词。
        dec_output, dec_state = decoder(dec_input, dec_state, enc_output)
        print("dec_output:",dec_output)
        #argmax(axis=1):代表取出形状为1x39的dec_output的列值,argmax获取出最大概率值所在的索引,可根据该索引获取Vocabulary词汇表相同索引上的单词。
        #argmax的方式:代表的是贪婪搜索,而贪婪搜索不是每次都能得到最优输出序列,而本书另外最推荐的是束搜索。
        pred = dec_output.argmax(axis=1)
        print("pred:",pred)
        #根据模型预测出的Vocabulary词汇表39个单词每个单词的概率值输出,根据最大概率值所在的索引获取Vocabulary词汇表相同索引上的单词。
        pred_token = out_vocab.idx_to_token[int(pred.asscalar())]
        if pred_token == EOS:  # 当任一时间步搜索出EOS时,输出序列即完成
            break
        else:
            output_tokens.append(pred_token)
            dec_input = pred
    return output_tokens
	
	
input_seq = 'ils regardent .'
translate(encoder, decoder, input_seq, max_seq_len)
		

BLEU计算的逐步分析

import math
import collections

pred_tokens = 'they are watching .'
label_tokens = 'they are watching .'
k=2

len_pred, len_label = len(pred_tokens), len(label_tokens)
len_pred, len_label #(19, 19)
1 - len_label / len_pred #0.0

#math.exp(0)的值 1.0
score = math.exp(min(0, 1 - len_label / len_pred))
score #1.0


#1.双层for循环
#	for n in range(1, k+1): #k+1=3,遍历的n为1、2
#		for i in range(len_label - n + 1):
#			#n为1时,len_label - n + 1 = 19,遍历的i为0~18;
#			#n为2时,len_label - n + 1 = 18,遍历的i为0~17;
#			label_tokens[i: i + n] 
#			#n为1时,从label_tokens[0:1]到label_tokens[18:19] 
#			#n为2时,从label_tokens[0:2]到label_tokens[17:19] 
#		for i in range(len_pred - n + 1):
#			#n为1时,len_label - n + 1 = 19,遍历的i为0~18;
#			#n为2时,len_label - n + 1 = 18,遍历的i为0~17;


#2.collections.defaultdict(int) 输出的为 defaultdict(<class 'int'>, {})
# 返回一个新的类似字典的对象,类似于Counter字典,两者都可用于统计某值出现的次数。 
# defaultdict 是内置 dict 类的子类。它重载了一个方法并添加了一个可写的实例变量,其余的功能与 dict 类相同。
# >>> s = 'mississippi'
# >>> d = defaultdict(int)
# >>> for k in s:
# ...     d[k] += 1
# >>> sorted(d.items())
# [('i', 4), ('m', 1), ('p', 2), ('s', 4)]


#遍历的n为1、2
for n in range(1, k + 1):
	#defaultdict字典类似于Counter字典,可用于统计某值出现的次数
	num_matches, label_subs = 0, collections.defaultdict(int)
	
	for i in range(len_label - n + 1):
		#计算标签序列中字母组合出现的次数
		label_subs[''.join(label_tokens[i: i + n])] += 1
	sorted(label_subs.items())
	#n为1时,打印[(' ', 3), ('.', 1), ('a', 2), ('c', 1), ('e', 2), ('g', 1), ('h', 2), ('i', 1), ('n', 1), ('r', 1), ('t', 2), ('w', 1), ('y', 1)]
	#n为2时,打印[(' .', 1), (' a', 1), (' w', 1), ('ar', 1), ('at', 1), ('ch', 1), ('e ', 1), ('ey', 1), ('g ', 1), ('he', 1), ('hi', 1), ('in', 1), ('ng', 1), ('re', 1), ('tc', 1), ('th', 1), ('wa', 1), ('y ', 1)]
 
	for i in range(len_pred - n + 1):
		#根据预测序列中字母组合与标签序列中字母组合进行匹配,如果能匹配出字母组合并且出现次数大于0则执行继续执行下面操作
		if label_subs[''.join(pred_tokens[i: i + n])] > 0:
			#预测序列中字母组合能匹配标签序列中字母组合,则给统计相似度num_matches加1
			num_matches += 1
			#给defaultdict字典中统计的标签序列中字母组合的出现次数减1
			label_subs[''.join(pred_tokens[i: i + n])] -= 1
	
	#1.math.pow(x, y) 将返回 x 的 y 次幂。 
	#  pow(1.0, x) 和 pow(x, 0.0) 总是返回 1.0 ,即使 x 是零或NaN。 
	#  如果 x 和 y 都是有限的, x 是负数, y 不是整数那么 pow(x, y) 是未定义的,并且引发 ValueError。
	#  与内置的 ** 运算符不同, math.pow() 将其参数转换为 float类型的。而使用 ** 或内置自带的pow()函数是用来计算精确的整数幂的。
	#2.下面的公式代码实质为Pn**(0.5**n)的推导:Pn为预测序列与标签序列匹配词数为n的子序列的数量与预测序列中词数为n的子序列的数量之比,
	#  可简单理解为“预测序列与标签序列所匹配出来的”词数数量与预测序列中词数的数量之比。
	#3.n为1时,num_matches为19,math.pow(19 / (19 - 1 + 1), math.pow(0.5, 1)),实际为pow(1,0.5),结果为1.0
	#  n为2时,num_matches为18,math.pow(18 / (19 - 2 + 1), math.pow(0.5, 2)),实际为pow(1,0.25),结果为1.0
	#  因为pow(1.0, x) 总是返回 1.0
	score *= math.pow(num_matches / (len_pred - n + 1), math.pow(0.5, n))
	print(score) #n为1或2时,score都为1.0

深度学习基础

1.深度学习基本概念
	监督学习:所有输入数据都有确定的对应输出数据,在各种网络架构中,输入数据和输出数据的节点层都位于网络的两端,训练过程就是不断地调整它们之间的网络连接权重。
 	左上:列出了各种不同网络架构的监督学习,比如标准的神经网络(NN)可用于训练房子特征和房价之间的函数,卷积神经网络(CNN)可用于训练图像和类别之间的函数,
	      循环神经网络(RNN)可用于训练语音和文本之间的函数。
 	左下:分别展示了 NN、CNN 和 RNN 的简化架构。这三种架构的前向过程各不相同,NN 使用的是权重矩阵(连接)和节点值相乘并陆续传播至下一层节点的方式;
	      CNN 使用矩形卷积核在图像输入上依次进行卷积操作、滑动,得到下一层输入的方式;RNN 记忆或遗忘先前时间步的信息以为当前计算过程提供长期记忆。
 	右上:NN 可以处理结构化数据(表格、数据库等)和非结构化数据(图像、音频等)。
 	右下:深度学习能发展起来主要是由于大数据的出现,神经网络的训练需要大量的数据;而大数据本身也反过来促进了更大型网络的出现。
	      深度学习研究的一大突破是新型激活函数的出现,用 ReLU 函数替换sigmoid 函数可以在反向传播中保持快速的梯度下降过程,
	      sigmoid 函数在正无穷处和负无穷处会出现趋于零的导数,这正是梯度消失导致训练缓慢甚至失败的主要原因。要研究深度学习,
	      需要学会「idea—代码—实验—idea」的良性循环。

2.logistic 回归
	左上:logistic 回归主要用于二分类问题,如图中所示,logistic 回归可以求解一张图像是不是猫的问题,其中图像是输入(x),猫(1)或非猫(0)是输出。
	      我们可以将 logistic 回归看成将两组数据点分离的问题,如果仅有线性回归(激活函数为线性),则对于非线性边界的数据点(例如,一组数据点被另一组包围)
	      是无法有效分离的,因此在这里需要用非线性激活函数替换线性激活函数。在这个案例中,我们使用的是 sigmoid 激活函数,它是值域为(0, 1)的平滑函数,
	      可以使神经网络的输出得到连续、归一(概率值)的结果,例如当输出节点为(0.2, 0.8)时,判定该图像是非猫(0)。
	左下:神经网络的训练目标是确定最合适的权重 w 和偏置项 b,那这个过程是怎么样的呢?
	      这个分类其实就是一个优化问题,优化过程的目的是使预测值 y hat 和真实值 y 之间的差距最小,形式上可以通过寻找目标函数的最小值来实现。
	      所以我们首先确定目标函数(损失函数、代价函数)的形式,然后用梯度下降逐步更新 w、b,当损失函数达到最小值或者足够小时,我们就能获得很好的预测结果。
	右上:损失函数值在参数曲面上变化的简图,使用梯度可以找到最快的下降路径,学习率的大小可以决定收敛的速度和最终结果。学习率较大时,初期收敛很快,
	      不易停留在局部极小值,但后期难以收敛到稳定的值;学习率较小时,情况刚好相反。一般而言,我们希望训练初期学习率较大,后期学习率较小,
	      之后会介绍变化学习率的训练方法。
	右下:总结整个训练过程,从输入节点 x 开始,通过前向传播得到预测输出 y hat,用 y hat 和 y 得到损失函数值,开始执行反向传播,
	      更新 w 和 b,重复迭代该过程,直到收敛。

3.浅层网络的特点
	左上:浅层网络即隐藏层数较少,如图所示,这里仅有一个隐藏层。
	左下:这里介绍了不同激活函数的特点:
		sigmoid:sigmoid 函数常用于二分分类问题,或者多分类问题的最后一层,主要是由于其归一化特性。sigmoid 函数在两侧会出现梯度趋于零的情况,会导致训练缓慢。
		tanh:相对于 sigmoid,tanh 函数的优点是梯度值更大,可以使训练速度变快。
		ReLU:可以理解为阈值激活(spiking model 的特例,类似生物神经的工作方式),该函数很常用,基本是默认选择的激活函数,优点是不会导致训练缓慢的问题,
		      并且由于激活值为零的节点不会参与反向传播,该函数还有稀疏化网络的效果。
		Leaky ReLU:避免了零激活值的结果,使得反向传播过程始终执行,但在实践中很少用。

	右上:为什么要使用激活函数呢?更准确地说是,为什么要使用非线性激活函数呢?
	      上图中的实例可以看出,没有激活函数的神经网络经过两层的传播,最终得到的结果和单层的线性运算是一样的,也就是说,没有使用非线性激活函数的话,
	      无论多少层的神经网络都等价于单层神经网络(不包含输入层)。
	右下:如何初始化参数 w、b 的值?
	      当将所有参数初始化为零的时候,会使所有的节点变得相同,在训练过程中只能学到相同的特征,而无法学到多层级、多样化的特征。
	      解决办法是随机初始化所有参数,但仅需少量的方差就行,因此使用 Rand(0.01)进行初始化,其中 0.01 也是超参数之一。

4.深度神经网络的特点
	左上:神经网络的参数化容量随层数增加而指数式地增长,即某些深度神经网络能解决的问题,浅层神经网络需要相对的指数量级的计算才能解决。
	左下:CNN 的深度网络可以将底层的简单特征逐层组合成越来越复杂的特征,深度越大,其能分类的图像的复杂度和多样性就越大。
	      RNN 的深度网络也是同样的道理,可以将语音分解为音素,再逐渐组合成字母、单词、句子,执行复杂的语音到文本任务。
	右边:深度网络的特点是需要大量的训练数据和计算资源,其中涉及大量的矩阵运算,可以在 GPU 上并行执行,还包含了大量的超参数,
	      例如学习率、迭代次数、隐藏层数、激活函数选择、学习率调整方案、批尺寸大小、正则化方法等。

5.偏差与方差
	1.那么部署你的机器学习模型需要注意些什么?下图展示了构建 ML 应用所需要的数据集分割、偏差与方差等问题。
	2.如上所示,经典机器学习和深度学习模型所需要的样本数有非常大的差别,深度学习的样本数是经典 ML 的成千上万倍。
	  因此训练集、开发集和测试集的分配也有很大的区别,当然我们假设这些不同的数据集都服从同分布。
	3.偏差与方差问题同样是机器学习模型中常见的挑战,上图依次展示了由高偏差带来的欠拟合和由高方差带来的过拟合。
	  一般而言,解决高偏差的问题是选择更复杂的网络或不同的神经网络架构,而解决高方差的问题可以添加正则化、减少模型冗余或使用更多的数据进行训练。
	4.当然,机器学习模型需要注意的问题远不止这些,但在配置我们的 ML 应用中,它们是最基础和最重要的部分。
	  其它如数据预处理、数据归一化、超参数的选择等都在后面的信息图中有所体现。

6.正则化
	1.正则化是解决高方差或模型过拟合的主要手段,过去数年,研究者提出和开发了多种适合机器学习算法的正则化方法,
	  如数据增强、L2 正则化(权重衰减)、L1 正则化、Dropout、Drop Connect、随机池化和提前终止等。
	2.如上图左列所示,L1 和 L2 正则化也是是机器学习中使用最广泛的正则化方法。L1 正则化向目标函数添加正则化项,以减少参数的绝对值总和;
	  而 L2 正则化中,添加正则化项的目的在于减少参数平方的总和。根据之前的研究,L1 正则化中的很多参数向量是稀疏向量,因为很多模型导致参数趋近于 0,
	  因此它常用于特征选择设置中。此外,参数范数惩罚 L2 正则化能让深度学习算法「感知」到具有较高方差的输入 x,
	  因此与输出目标的协方差较小(相对增加方差)的特征权重将会收缩。
	3.在中间列中,上图展示了 Dropout 技术,即暂时丢弃一部分神经元及其连接的方法。随机丢弃神经元可以防止过拟合,同时指数级、高效地连接不同网络架构。
	  一般使用了 Dropout 技术的神经网络会设定一个保留率 p,然后每一个神经元在一个批量的训练中以概率 1-p 随机选择是否去掉。
	  在最后进行推断时所有神经元都需要保留,因而有更高的准确度。
	4.Bagging 是通过结合多个模型降低泛化误差的技术,主要的做法是分别训练几个不同的模型,然后让所有模型表决测试样例的输出。
	  而 Dropout 可以被认为是集成了大量深层神经网络的 Bagging 方法,因此它提供了一种廉价的 Bagging 集成近似方法,能够训练和评估值数据数量的神经网络。
	5.最后,上图还描述了数据增强与提前终止等正则化方法。数据增强通过向训练数据添加转换或扰动来人工增加训练数据集。
	  数据增强技术如水平或垂直翻转图像、裁剪、色彩变换、扩展和旋转通常应用在视觉表象和图像分类中。
	  而提前终止通常用于防止训练中过度表达的模型泛化性能差。如果迭代次数太少,算法容易欠拟合(方差较小,偏差较大),
	  而迭代次数太多,算法容易过拟合(方差较大,偏差较小)。因此,提前终止通过确定迭代次数解决这个问题。

7.最优化
	1.最优化是机器学习模型中非常非常重要的模块,它不仅主导了整个训练过程,同时还决定了最后模型性能的好坏和收敛需要的时长。
	  以下两张信息图都展示了最优化方法需要关注的知识点,包括最优化的预备和具体的最优化方法。
	2.以上展示了最优化常常出现的问题和所需要的操作。首先在执行最优化前,我们需要归一化输入数据,而且开发集与测试集归一化的常数(均值与方差)与训练集是相同的。
	  上图也展示了归一化的原因,因为如果特征之间的量级相差太大,那么损失函数的表面就是一张狭长的椭圆形,而梯度下降或最速下降法会因为「锯齿」现象而很难收敛,
	  因此归一化为圆形有助于减少下降方向的震荡。
	3.后面的梯度消失与梯度爆炸问题也是十分常见的现象。「梯度消失」指的是随着网络深度增加,参数的梯度范数指数式减小的现象。梯度很小,意味着参数的变化很缓慢,
	  从而使得学习过程停滞。梯度爆炸指神经网络训练过程中大的误差梯度不断累积,导致模型权重出现很大的更新,在极端情况下,权重的值变得非常大以至于出现 NaN 值。
	4.梯度检验现在可能用的比较少,因为我们在 TensorFlow 或其它框架上执行最优化算法只需要调用优化器就行。梯度检验一般是使用数值的方法计算近似的导数并传播,
	  因此它能检验我们基于解析式算出来的梯度是否正确。

	5.下面就是具体的最优化算法了,包括最基本的小批量随机梯度下降、带动量的随机梯度下降和 RMSProp 等适应性学习率算法。
	6.小批量随机梯度下降(通常 SGD 指的就是这种)使用一个批量的数据更新参数,因此大大降低了一次迭代所需的计算量。这种方法降低了更新参数的方差,
	  使得收敛过程更为稳定;它也能利用流行深度学习框架中高度优化的矩阵运算器,从而高效地求出每个小批数据的梯度。
	  通常一个小批数据含有的样本数量在 50 至 256 之间,但对于不同的用途也会有所变化。
	7.动量策略旨在加速 SGD 的学习过程,特别是在具有较高曲率的情况下。一般而言,动量算法利用先前梯度的指数衰减滑动平均值在该方向上进行修正,
	  从而更好地利用历史梯度的信息。该算法引入了变量 v 作为参数在参数空间中持续移动的速度向量,速度一般可以设置为负梯度的指数衰减滑动平均值。
	8.上图后面所述的 RMSProp 和 Adam 等适应性学习率算法是目前我们最常用的最优化方法。RMSProp 算法(Hinton,2012)修改 AdaGrad 以在非凸情况下表现更好,
	  它改变梯度累积为指数加权的移动平均值,从而丢弃距离较远的历史梯度信息。RMSProp 是 Hinton 在公开课上提出的最优化算法,
	  其实它可以视为 AdaDelta 的特例。但实践证明 RMSProp 有非常好的性能,它目前在深度学习中有非常广泛的应用。
	9.Adam 算法同时获得了 AdaGrad 和 RMSProp 算法的优点。Adam 不仅如 RMSProp 算法那样基于一阶矩均值计算适应性参数学习率,
	  它同时还充分利用了梯度的二阶矩均值(即有偏方差/uncentered variance)。

8.超参数
	1.以下是介绍超参数的信息图,它在神经网络中占据了重要的作用,因为它们可以直接提升模型的性能。
	2.众所周知学习率、神经网络隐藏单元数、批量大小、层级数和正则化系数等超参数可以直接影响模型的性能,而怎么调就显得非常重要。
	  目前最常见的还是手动调参,开发者会根据自身建模经验选择「合理」的超参数,然后再根据模型性能做一些小的调整。
	  而自动化调参如随机过程或贝叶斯优化等仍需要非常大的计算量,且效率比较低。不过近来关于使用强化学习、遗传算法和神经网络等方法搜索超参数有很大的进步,
	  研究者都在寻找一种高效而准确的方法。
	  目前的超参数搜索方法有:
		依靠经验:聆听自己的直觉,设置感觉上应该对的参数然后看看它是否工作,不断尝试直到累趴。
		网格搜索:让计算机尝试一些在一定范围内均匀分布的数值。
		随机搜索:让计算机尝试一些随机值,看看它们是否好用。
		贝叶斯优化:使用类似 MATLAB bayesopt 的工具自动选取最佳参数——结果发现贝叶斯优化的超参数比你自己的机器学习算法还要多,
			   累觉不爱,回到依靠经验和网格搜索方法上去。
	3.因为篇幅有限,后面的展示将只简要介绍信息图,相信它们对各位读者都十分有帮助。

9.结构化机器学习过程
	我们需要按过程或结构来设定我们的机器学习系统,首先需要设定模型要达到的目标,例如它的预期性能是多少、度量方法是什么等。
	然后分割训练、开发和测试集,并预期可能到达的优化水平。随后再构建模型并训练,在开发集和测试集完成验证后就可以用于推断了。

10.误差分析
	在完成训练后,我们可以分析误差的来源而改进性能,包括发现错误的标注、不正确的损失函数等。

11.训练集、开发集与测试集
	上图展示了三个分割数据集及其表现所需要注意的地方,也就是说如果它们间有不同的正确率,那么我们该如何修正这些「差别」。
	例如训练集的正确率明显高于验证集与测试集表明模型过拟合,三个数据集的正确率都明显低于可接受水平可能是因为欠拟合。

12.其它学习方法
	机器学习和深度学习当然不止监督学习方法,还有如迁移学习、多任务学习和端到端的学习等。

13.卷积神经网络基础
	1.计算机视觉任务涉及的数据体量是特别大的,一张图像就有上千个数据点,更别提高分辨率图像和视频了。
	  这时用全连接网络的话,参数数量太大,因而改用卷积神经网络(CNN),参数数量可以极大地减小。
	  CNN 的工作原理就像用检测特定特征的过滤器扫描整张图像,进行特征提取,并逐层组合成越来越复杂的特征。
	  这种「扫描」的工作方式使其有很好的参数共享特性,从而能检测不同位置的相同目标(平移对称)。
	2.卷积核对应的检测特征可以从其参数分布简单地判断,例如,权重从左到右变小的卷积核可以检测到黑白竖条纹的边界,并显示为中间亮,
	  两边暗的特征图,具体的相对亮暗结果取决于图像像素分布和卷积核的相对关系。卷积核权重可以直接硬编码,但为了让相同的架构适应不同的任务,
	  通过训练得到卷积核权重是更好的办法。

	3.卷积运算的主要参数:
		1.padding:直接的卷积运算会使得到的特征图越来越小,padding 操作会在图像周围添加 0 像素值的边缘,
		  使卷积后得到的特征图大小和原图像(长宽,不包括通道数)相同。
		2.常用的两个选项是:『VALID』,不执行 padding;『SAME』,使输出特征图的长宽和原图像相同。
		3.stride:两次卷积操作之间的步长大小。
		4.一个卷积层上可以有多个卷积核,每个卷积核运算得到的结果是一个通道,每个通道的特征图的长宽相同,可以堆叠起来构成多通道特征图,作为下一个卷积层的输入。

	4.深度卷积神经网络的架构:
		深度卷积神经网络的架构主要以卷积层、池化层的多级堆叠,最后是全连接层执行分类。
		池化层的主要作用是减少特征图尺寸,进而减少参数数量,加速运算,使其目标检测表现更加鲁棒。

14.经典卷积神经网络
	LeNet·5:手写识别分类网络,这是第一个卷积神经网络,由 Yann LeCun 提出。
	AlexNet:图像分类网络,首次在 CNN 引入 ReLU 激活函数。
	VGG-16:图像分类网络,深度较大。

15.特殊卷积神经网络
	ResNet:引入残差连接,缓解梯度消失和梯度爆炸问题,可以训练非常深的网络。
	Network in Network:使用 1x1 卷积核,可以将卷积运算变成类似于全连接网络的形式,还可以减少特征图的通道数,从而减少参数数量。
	Inception Network:使用了多种尺寸卷积核的并行操作,再堆叠成多个通道,可以捕捉多种规模的特征,但缺点是计算量太大,可以通过 1x1 卷积减少通道数。

16.实践建议
	使用开源实现:从零开始实现时非常困难的,利用别人的实现可以快速探索更复杂有趣的任务。
	数据增强:通过对原图像进行镜像、随机裁剪、旋转、颜色变化等操作,增加训练数据量和多样性。
	迁移学习:针对当前任务的训练数据太少时,可以将充分训练过的模型用少量数据微调获得足够好的性能。
	基准测试和竞赛中表现良好的诀窍:使用模型集成,使用多模型输出的平均结果;在测试阶段,将图像裁剪成多个副本分别测试,并将测试结果取平均。

17.目标检测算法
	目标检测即使用边界框检测图像中物体的位置,Faster R-CNN、R-FCN 和 SSD 是三种目前最优且应用最广泛的目标检测模型,上图也展示了 YOLO 的基本过程。

18.人脸识别
	人脸识别有两大类应用:人脸验证(二分分类)和人脸识别(多人分类)。
	当样本量不足时,或者不断有新样本加入时,需要使用 one-shot learning,解决办法是学习相似性函数,即确定两张图像的相似性。
	比如在 Siamese Network 中学习人脸识别时,就是利用两个网络的输出,减少同一个人的两个输出的差别,增大不同人的两个输出之间的差别。

19.风格迁移
	风格迁移是一个热门话题,它会在视觉上给人耳目一新的感觉。例如你有一副图,然后将另一幅图的风格特征应用到这幅图上,
	比如用一位著名画家或某一副名画的风格来修改你的图像,因此我们可以获得独特风格的作品。

20.循环神经网络基础
	1.如上所示,命名实体识别等序列问题在现实生活中占了很大的比例,而隐马尔可夫链等传统机器学习算法只能作出很强的假设而处理部分序列问题。
	  但近来循环神经网络在这些问题上有非常大的突破,RNN 隐藏状态的结构以循环形的形式成记忆,每一时刻的隐藏层的状态取决于它的过去状态,
	  这种结构使得 RNN 可以保存、记住和处理长时期的过去复杂信号。
	2.循环神经网络(RNN)能够从序列和时序数据中学习特征和长期依赖关系。RNN 具备非线性单元的堆叠,其中单元之间至少有一个连接形成有向循环。
	  训练好的 RNN 可以建模任何动态系统;但是,训练 RNN 主要受到学习长期依赖性问题的影响。
	3.以下展示了 RNN 的应用、问题以及变体等:
		循环神经网络在语言建模等序列问题上有非常强大的力量,但同时它也存在很严重的梯度消失问题。
		因此像 LSTM 和 GRU 等基于门控的 RNN 有非常大的潜力,它们使用门控机制保留或遗忘前面时间步的信息,并形成记忆以提供给当前的计算过程。

21.NLP 中的词表征
	1.词嵌入在自然语言处理中非常重要,因为不论执行怎样的任务,将词表征出来都是必须的。上图展示了词嵌入的方法,
	  我们可以将词汇库映射到一个 200 或 300 维的向量,从而大大减少表征词的空间。此外,这种词表征的方法还能表示词的语义,因为词义相近的词在嵌入空间中距离相近。

	2.除了以上所述的 Skip Grams,以下还展示了学习词嵌入的常见方法:
		GloVe 词向量是很常见的词向量学习方法,它学到的词表征可进一步用于语句分类等任务。

22.序列到序列
	序列到序列的方法使用最多的就是编码器解码器框架,其它还有束搜索等模块的介绍。
	编码器解码器架构加上注意力机制可以解决非常多的自然语言处理问题,以下介绍了 BLEU 分值和注意力机制。它们在机器翻译的架构和评估中都是不能缺少的部分。

 


批标准化/批归一化

 

 

 

 

 

 

 

 

 

 

 

 

 

 

日萌社 发布了224 篇原创文章 · 获赞 111 · 访问量 16万+ 私信 关注

标签:...,术语,批量,人工智能,知识,state,隐藏,向量,size
来源: https://blog.csdn.net/zimiao552147572/article/details/104084989

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有