02 - 神经网络的数据表示


1. 基本概念

张量(tensor):输入网络的数据存储对象

张量运算: 层的组成要素

梯度下降: 可以让网络从训练样本中进行学习

1.1 张量的概念

1.1.1 标量(0 Dimension)

仅包含一个数字的张量叫作标量。在numpy中,一个float32 或 float64 的数字就是一个标量张量。

使用ndim 属性查看张量的维度。

Python 3.8.1 (tags/v3.8.1:1b293b6, Dec 18 2019, 23:11:46) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> x = np.array(12)
>>> x
array(12)
>>> x.ndim
0

1.1.2 向量(1 Dimension)

数字组成的数组叫向量或一维张量。一维张量只有一个轴。

>>> x = np.array([12, 3, 6, 14, 7])
>>> x
array([12, 3, 6, 14, 7])
>>> x.ndim
1

1.1.3 矩阵(2 Dimension)

向量组成的数组叫作矩阵或二维张量。矩阵有两个轴, 行和列。

>>> x = np.array([[5, 78, 2, 34, 0],[6, 79, 3, 35, 1],[7, 80, 4, 36, 2]])
>>> x
array([[ 5, 78, 2, 34, 0],
[ 6, 79, 3, 35, 1],
[ 7, 80, 4, 36, 2]])
>>> x.ndim
2

现实生活中例如(samples, features) 组成的2D张量 。 

例如: 

人口统计数据集, 其中包括每个人的年龄、邮编、和收入。 每个人可以表示为包含3个值的向量

而整个数据集包含100000个人, 因此,可以存储在形状为(100000, 3) 的 2D张量中。

文本文档数据集, 每个文档表示为每个单词在其中出现的次数,每个文档可以被编码为包含20000个值的向量,

整个数据集包含500个文档, 可以存储在形状为(500, 20000)的张量中。

1.1.4 立方体 (3 Dimension) 

将多个矩阵组合成一个新的数组,可以得到一个3D张量,理解为一个数字立方体。

>>> x = np.array([[[5, 78, 2, 34, 0],
... [6, 79, 3, 35, 1],
... [7, 80, 4, 36, 2]],
... [[5, 78, 2, 34, 0],
... [6, 79, 3, 35, 1],
... [7, 80, 4, 36, 2]],
... [[5, 78, 2, 34, 0],
... [6, 79, 3, 35, 1],
... [7, 80, 4, 36, 2]]])
>>> x
array([[[ 5, 78, 2, 34, 0],
[ 6, 79, 3, 35, 1],
[ 7, 80, 4, 36, 2]],

[[ 5, 78, 2, 34, 0],
[ 6, 79, 3, 35, 1],
[ 7, 80, 4, 36, 2]],

[[ 5, 78, 2, 34, 0],
[ 6, 79, 3, 35, 1],
[ 7, 80, 4, 36, 2]]])
>>> x.ndim
3

3D 张量, 时间序列数据, 形状为(samples, timesteps, features)

例如股票价格数据集: 每一分钟,股票的当前价格、前一分钟最高价格和前一分钟的最低价格保存下来。

因此每分钟被编码为3D向量,整个交易日被编码为一个开关为(390, 3)的2D张量中,250 的数据则可以保存

在一个形状为(250, 390, 3)的3D张量中,这里每个样本是一天的股票数据。

将多个3D张量组合成一个数组, 可以创建一个4D张量,以此类推。

4D 张量,图像,形状为(samples, height, width, channels)

5D张量, 视频,形状为(samples, frames, height, width, channels)

1.2 张量的属性

张量有三个关键属性

* 维度(ndim):就是属于几D张量。

* 形状: 表示张量沿每个维度的大小(元素个数),所以是一个整数元组。 

           例如标量的形状是空,即()

                  向量的形状只包含一个元素(5,)

                  矩阵示例形状为3行5列(3, 5)。

                  立方体的形状为3高(层),3长(行),5宽(列) (3, 3, 5)

* 数据类型(dtype):  float32, uint8, float64 , char

查看mnist数据集中张量的属性值

from keras.datasets import mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

接下来,我们给出张量 train_images 的轴的个数,即 ndim 属性。
>>> print(train_images.ndim)
3

下面是它的形状。
>>> print(train_images.shape)
(60000, 28, 28)

下面是它的数据类型,即 dtype 属性。
>>> print(train_images.dtype)
uint8

使用Matplolib 显示

digit = train_images[4]
import matplotlib.pyplot as plt
plt.imshow(digit, cmap=plt.cm.binary)
plt.show()

2. 张量运算

所有计算机程序进入CPU运算时都只表示高低电平,运算只有AND,OR,NOR等。

与此类似,深度神经网络学到的所有变化都是张量运算。

2.1 逐元素运算

relu 运算与加法都是逐元素的运算,即该运算独立地应用于张量中的每个元素。

relu 的简单代码实现 

def naive_relu(x):
  assert len(x.shape) == 2
  x = x.copy()

  for i in range(x.shape[0]):
    for j in range(x.shape[1]):
       x[i, j] = max(x[i,j], 0)
  return x

加法运算的简单实现 

def naive_add(x, y):

  assert len(x.shape) == 2
  assert x.shape == y.shape

  x = x.copy()
  for i in range(x.shape[0]):
    for j in range(x.shape[1]):
      x[i, j] += y[i, j]
  return x

减法与乘法使用相同的方法实现,numpy 将这个都已经封装起来,交给了BLAS(Basic linear algebra subprograms)实现。

可以直接这么用

import numpy as np

z = x + y

z = np.maximum(z, 0)

2.2 广播

如果两个不同形状的张量数据进行加法操作,较小的张量会被广播,以匹配较大的张量的形状。

广播包含以下两步:

(1) 向较小的张量添加轴(叫作广播轴), 使其ndim 与 较大的张量相同

(2) 将较小的张量沿着新轴重复,使其形状与较大的张量相同。

具体例子

假设x是一个2D矩阵,形状为(32, 10), y是一个数组,形状为(10, ), 现在要执行 x + y 操作。

第一步: 给y添加空的第一个轴,这样y的形状变为(1, 10)。 

第二步:将 y 沿着新轴重复32次, 这样得到的张量 y 的形状为 (32, 10), 并且 y[i, :] == y 。

现在可以相加, 因为形状相同。

2.3 张量点积

点积运算,也叫张量积,与诸元素运算不相同。是最常见也是最有用的张量运算。

与逐元素的运算不相同,它将输入张量的元素合并在一起。

两个向量之间的点积是一个标量,而且只有元素个数相同的向量之间才能做点积。

import numpy as np

z = np.dot(x, y)

数学符号中的 . 表示点积去处

 z = x.y

def naive_vector_dot(x, y):
  assert len(x.shape) == 1
  assert len(y.shape) == 1
  assert x.shape[o] == y.shape[o]

  z = 0.
  for i in range(x.shape[0]):
    z += x[i] * y[i]
  return z

一个矩阵x 和 一个向量 y 做点积,返回值是一个向量,其中每个元素是y 和 x 的每一行之间的点积。

def naive_metric_vector_dot(x, y):
  assert len(x.shape) == 2
  assert len(y.shape) == 1
  assert x.shape[1] == y.shape[0]

  z = np.zeros(x.shape[0])
  for i in range(x.shape[0]):
    for j in range(x.shape[1]):
      z[i] += x[i, j] * y[j]
  return z

2.4 张量变形

张量变形是指改变张量的行和列,以得到想要的形状。 变形后的张量的元素总个数与初始张量相同。

>>> x = np.array([[0., 1.],
[2., 3.],
[4., 5.]])
>>> print(x.shape)
(3, 2)
>>> x = x.reshape((6, 1))
>>> x
array([[ 0.],
[ 1.],
[ 2.],
[ 3.],
[ 4.],
[ 5.]])
>>> x = x.reshape((2, 3))
>>> x
array([[ 0., 1., 2.],
[ 3., 4., 5.]])

经常遇到的一种特殊的张量变形是转置transposition)。对矩阵做转置是指将行和列互换,
使 x[i, :] 变为 x[:, i]
>>> x = np.zeros((300, 20))
>>> x = np.transpose(x)
>>> print(x.shape)
(20, 300)
创建一个形状为 (300, 20) 的零矩阵



相关