第一章神经网络计算
1.1张量生成
Tensorflow中的tensor表示张量,是多维数据,多维列表,用阶表示张量的维度。0 阶张量叫做标量,表示的是一个单独的数,如 123;1 阶张量叫作向量,表示的是一个一维数组如[1,2,3];2 阶张量叫作矩阵,表示的是一个二维数组,它可以有 i 行 j 列个元素,每个元素用它的行号和列号共同索引到,如在[[1,2,3],[4,5,6],[7,8,9]]中,2 的索引即为第 0 行第 1 列。张量的阶数与方括号的数量相同,0 个方括号即为 0 阶张量,1 个方括号即为 1 阶张量。故张量可以表示0 阶到 n 阶的数组。也可通过 reshape 的方式得到更高维度数组。
创建张量的方法:
1.利用tf.constant(张量内容,dtype=数据类型(可选))。举例:
import tensorflow as tf
a = tf.constant([1, 5], dtype = tf.int64)
输出结果为:
<tf.Tensor([1,5], shape=(2 , ) , dtype=int64)>
shape中数字为2,表示一维张量里有2个元素。
2.很多时候数据以numpy格式给出,通过一下函数可以将numpy格式转化为Tensor格式:tf.convert_to_tensor(数据名,dtype=数据类型(可选))。举例:
import tensorflow as tf
import numpy as np
a = np.arange(0, 5)
b = tf.comvert_to_tensor(a, dtype = tf.int64)
print(a)
print(b)
输出结果为:
[0 1 2 3 4]
<tf.Tensor([0 1 2 3 4], shape=( 5 , ), dtype=int64)>
3.可采用不同函数创建不同值的张量。tf.zeros(维度)创建全为0的张量,tf.ones(维度)创建全为1的张量,tf.fill(维度,指定值)创建全为指定值的张量。其中维度参数部分,如一维则直接写个数,二维用[行,列]表示,多维用[n,m,j..] 表示。举例:
import tensorflow as tf
a = tf.zeros([2, 3])
b = tf.ones(4)
c = tf.fill([2, 2], 9) print(a)
print(b)
print(c)
输出结果为:
tf.Tensor([[0. 0. 0.] [0. 0. 0.]], shape=(2, 3), dtype=float32)
tf.Tensor([1. 1. 1. 1.], shape=(4, ), dtype=float32)
tf.Tensor([[9 9] [9 9]], shape=(2, 2), dtype=int32)
4.可采用不同函数创建符合不同分布的张量。如用 tf. random.normal (维度, mean=均值,stddev=标准差)生成正态分布的随机数,默认均值为 0,标准差为 1; 用 tf. random.truncated_normal (维度,mean=均值,stddev=标准差)生成截断式正态分布的随机数,能使生成的这些随机数更集中一些,如果随机生成数据的取值在 (μ - 2σ,u + 2σ ) 之外则重新进行生成,保证了生成值在均值附近;利用 tf.random. uniform(维度,minval=最小值,maxval=最大值),生成指定维度的均匀 分布随机数,用 minval 给定随机数的最小值,用maxval 给定随机数的最大值,最小、最大值是前闭后开区间。举例:
import tensorflow as tf
d = tf.random.normal ([2, 2], mean=0.5, stddev=1)
print(d)
e = tf.random.truncated_normal ([2, 2], mean=0.5, stddev=1)
print(e)
f = tf.random.uniform([2, 2], minval=0, maxval=1)
print(f)
输出结果为:
tf.Tensor([[0.7925745 0.643315 ]
[1.4752257 0.2533372]], shape=(2, 2), dtype=float32)
tf.Tensor([[ 1.3688478 1.0125661 ]
[ 0.17475659 -0.02224463]], shape=(2, 2), dtype=float32)
tf.Tensor([[0.28219545 0.15581512]
[0.77972126 0.47817433]], shape=(2, 2), dtype=float32)
1.2常用函数
1.利用tf.cast (张量名,dtype=数据类型)强制将Tensor转换为该数据类型;利用tf.reduce_min (张量名)计算张量维度上元素的最小值;利用tf.reduce_max (张 量名)计算张量维度上元素的最大值。举例如下:
x1 = tf.constant ([1., 2., 3.], dtype=tf.float64)
print(x1)
x2 = tf.cast (x1, tf.int32)
print(x2)
print (tf.reduce_min(x2), tf.reduce_max(x2))
输出结果为:
tf.Tensor([1. 2. 3.], shape=(3,), dtype=float64)
tf.Tensor([1 2 3], shape=(3,), dtype=int32)
tf.Tensor(1, shape=(), dtype=int32)
tf.Tensor(3, shape=(), dtype=intt32)
2.可用 tf.reduce_mean (张量名,axis=操作轴)计算张量沿着指定维度的平均值;tf.reduce_sum (张量名,axis=操作轴)计算张量沿着指定维度的和,如不指定 axis,则表示对所有元素进行操作。其中维度可按图理解:
举例如下:
x=tf.constant( [ [ 1, 2,3],[2, 2, 3] ] ) print(x)
print(tf.reduce_mean( x ))
print(tf.reduce_sum( x, axis=1 ))
输出结果:
tf.Tensor([[1 2 3] [2 2 3]], shape=(2, 3), dtype=int32)
tf.Tensor(2, shape=(), dtype=int32) (对所有元素求均值)
tf.Tensor([6 7], shape=(2,), dtype=int32) (横向求和,两行分别为 6 和 7)
3.可利用 tf.Variable(initial_value,trainable,validate_shape,name)函数可以将变量标记为“可训练”的,被它标记了的变量,会在反向传播中记录自己的梯度信息。其中 initial_value 默认为 None,可以搭配 tensorflow 随机生成函数来初始化参数;trainable 默认为 True,表示可以后期被算法优化的,如果不想该变量被优化,即改为 False;validate_shape 默认为 True,形状不接受更改,如果需要更改,validate_shape=False;name 默认为 None,给变量确定名称。举例如下:
w = tf.Variable(tf.random.normal([2, 2], mean=0, stddev=1))
表示首先随机 生成正态分布随机数,再给生成的随机数标记为可训练,这样在反向传播中就可 以通过梯度下降更新参数 w 了。
4.利用 TensorFlow 中函数对张量进行四则运算。利用 tf.add (张量 1,张量 2)实现两个张量的对应元素相加;利用 tf.subtract (张量 1,张量 2)实现两个张量的 对应元素相减;利用tf.multiply (张量1,张量2)实现两个张量的对应元素相乘; 利用tf.divide (张量1,张量2)实现两个张量的对应元素相除。注:只有维度相同的张量才可以做四则运算,举例如下:
a = tf.ones([1, 3])
b = tf.fill([1, 3], 3.)
print(a)
print(b)
print(tf.add(a,b))
print(tf.subtract(a,b))
print(tf.multiply(a,b))
print(tf.divide(b,a))
输出结果:
tf.Tensor([[1. 1. 1.]], shape=(1, 3), dtype=float32)
tf.Tensor([[3. 3. 3.]], shape=(1, 3), dtype=float32)
tf.Tensor([[4. 4. 4.]], shape=(1, 3), dtype=float32)
tf.Tensor([[-2. -2. -2.]], shape=(1, 3), dtype=float32)
tf.Tensor([[3. 3. 3.]], shape=(1, 3), dtype=float32)
tf.Tensor([[3. 3. 3.]], shape=(1, 3), dtype=float32)
5.利用TensorFlow中函数对张量进行幂次运算。可用tf.square (张量名)计算某个张量的平方;利用 tf.pow (张量名,n 次方数)计算某个张量的 n 次方;利用 tf.sqrt (张量名)计算某个张量的开方。举例如下:
a = tf.fill([1, 2], 3.)
print(a)
print(tf.pow(a, 3))
print(tf.square(a))
print(tf.sqrt(a))
输出结果:
tf.Tensor([[3. 3.]], shape=(1, 2), dtype=float32)
tf.Tensor([[27. 27.]], shape=(1, 2), dtype=float32)
tf.Tensor([[9. 9.]], shape=(1, 2), dtype=float32)
tf.Tensor([[1.7320508 1.7320508]], shape=(1, 2), dtype=float32)
6.可利用tf.matmul(矩阵1,矩阵2)实现两个矩阵的相乘。举例如下:
a = tf.ones([3, 2])
b = tf.fill([2, 3], 3.)
print(tf.matmul(a, b))
输出结果:
tf.Tensor([[6. 6. 6.] [6. 6. 6.] [6. 6. 6.]], shape=(3, 3), dtype=float32)
即 a 为一个3行2列的全1矩阵,b为2行3列的全3矩阵,二者进行矩阵相乘。
7.可利用 tf.data.Dataset.from_tensor_slices((输入特征, 标签))切分传入张量的第一维度,生成输入特征/标签对,构建数据集,此函数对 Tensor 格式与 Numpy 格式均适用,其切分的是第一维度,表征数据集中数据的数量,之后切分 batch 等操作都以第一维为基础。举例如下:
features = tf.constant([12,23,10,17])
labels = tf.constant([0, 1, 1, 0])
dataset = tf.data.Dataset.from_tensor_slices((features, labels))
print(dataset)
for element in dataset:
print(element)
输出结果:
<TensorSliceDataset shapes: ((),()), types: (tf.int32, tf.int32))>
(<tf.Tensor: id=9, shape=(), dtype=int32, numpy=12>, <tf.Tensor: id=10, shape=(),
dtype=int32, numpy=0>)
(<tf.Tensor: id=11, shape=(), dtype=int32, numpy=23>, <tf.Tensor: id=12, shape=(), dtype=int32, numpy=1>)
(<tf.Tensor: id=13, shape=(), dtype=int32, numpy=10>, <tf.Tensor: id=14, shape=(), dtype=int32, numpy=1>)
(<tf.Tensor: id=15, shape=(), dtype=int32, numpy=17>, <tf.Tensor: id=16, shape=(),
dtype=int32, numpy=0>)
即将输入特征 12 和标签 0 对应,产生配对;将输入特征 23 和标签 1 对应,产生配对......
8.可利用 tf.GradientTape( )函数搭配 with 结构计算损失函数在某一张量处的梯度,举例如下:
with tf.GradientTape( ) as tape:
w = tf.Variable(tf.constant(3.0))
loss = tf.pow(w,2)
grad = tape.gradient(loss,w)
print(grad)
输出结果:
tf.Tensor(6.0, shape=(), dtype=float32)
9.可利用 enumerate(列表名)函数枚举出每一个元素,并在元素前配上对应的索引号,常在 for 循环中使用。举例如下:
seq = [one, two, three]
for i, element in enumerate(seq):
print(i, element)
输出结果:
0 one
1 two
2 three
10.可用 tf.one_hot(待转换数据,depth=几分类)函数实现用独热码表示标签,在分类问题中很常见。标记类别为为 1 和 0,其中 1 表示是,0 表示非。如在鸢尾花分类任务中,如果标签是 1,表示分类结果是 1 杂色鸢尾,把它用独热码表示就是 0,1,0,这样可以表示出每个分类的概率:也就是百分之 0 的可能是 0 狗尾草鸢尾,百分百的可能是 1 杂色鸢尾,百分之 0 的可能是弗吉尼亚鸢尾。举例如下:
classes = 3
labels = tf.constant([1,0,2])
output = tf.one_hot( labels, depth=classes )
print(output)
输出结果:
tf.Tensor([[0. 1. 0.] [1. 0. 0.] [0. 0. 1.]], shape=(3, 3), dtype=float32)
索引从 0 开始,待转换数据中各元素值应小于 depth,若带转换元素值大于等于 depth,则该元素输出编码为 [0, 0 ... 0, 0]。即 depth 确定列数,待转换元素的 个数确定行数。举例如下:
classes = 3
labels = tf.constant([1,4,2]) # 输入的元素值 4 超出 depth-1
output = tf.one_hot(labels,depth=classes)
print(output)
输出结果:
tf.Tensor([[0. 1. 0.] [0. 0. 0.] [0. 0. 1.]], shape=(3, 3), dtype=float32)
即元素 4 对应的输出编码为[0. 0. 0.]。
11.可利用 tf.nn.softmax( )函数使前向传播的输出值符合概率分布,进而与独热码形式的标签作比较,程序实现如下:
y = tf.constant ( [1.01, 2.01, -0.66] )
y_pro = tf.nn.softmax(y)
print("After softmax, y_pro is:", y_pro)
输出结果:
After softmax, y_pro is:
tf.Tensor([0.25598174 0.69583046 0.0481878], shape=(3,), dtype=float32)
12.可利用 assign_sub 对参数实现自更新。使用此函数前需利用 tf.Variable 定义变量 w 为可训练(可自更新),举例如下:
w = tf.Variable(4)
w.assign_sub(1)
print(w)
输出结果:
<tf.VariableVariable:0shape=()dtype=int32,numpy=3>
即实现了参数 w 自减 1。注:直接调用 tf.assign_sub 会报错,要用 w.assign_sub。
13.可利用 tf.argmax (张量名,axis=操作轴)返回张量沿指定维度最大值的索引,维度定义与图一致。举例如下:
import numpy as np
test = np.array([[1, 2, 3], [2, 3, 4], [5, 4, 3], [8, 7, 2]])
print(test)
print( tf.argmax (test, axis=0)) # 返回每一列(经度)最大值的索引
print( tf.argmax (test, axis=1)) # 返回每一行(纬度)最大值的索引
输出结果:
tf.Tensor([3 3 1], shape=(3,), dtype=int64)
tf.Tensor([2 2 0 0], shape=(4,), dtype=int64)
到目前为止,基本的tf2函数就学完了,下面举出一个实例进行学习。
1.3鸢尾花数据集读入
鸢尾花数据集,其提供了 150 组鸢尾花数据,每组包括鸢尾花的花萼 长、花萼宽、花瓣长、花瓣宽 4 个输入特征,同时还给出了这一组特征对应的 鸢尾花类别。类别包括狗尾鸢尾、杂色鸢尾、弗吉尼亚鸢尾三类, 分别用数字 0、1、2 表示。代码实现如下:
from sklearn import datasets
from pandas import DataFrame
import pandas as pd
x_data = datasets.load_iris().data # .data返回iris数据集所有输入特征
y_data = datasets.load_iris().target # .target返回iris数据集所有标签
print("x_data from datasets: \n", x_data)
print("y_data from datasets: \n", y_data)
x_data = DataFrame(x_data, columns=['花萼长度', '花萼宽度', '花瓣长度', '花瓣宽度'])
# 为表格增加行索引(左侧)和列标签(上方)
pd.set_option('display.unicode.east_asian_width', True) # 设置列名对齐
print("x_data add index: \n", x_data)
x_data['类别'] = y_data # 新加一列,列标签为‘类别’,数据为y_data
print("x_data add a column: \n", x_data)
#类型维度不确定时,建议用print函数打印出来确认效果
1.4神经网络实现鸢尾花分类
我们用神经网络实现鸢尾花分类仅需要三步:
1.准备数据,包括数据集读入、数据集乱序,把训练集和测试集中的数据配成输入特征和标签对,生成 train 和 test 即永不相见的训练集和测试集;
(1) 数据集读入:
from sklearn.datasets import datasets
x_data = datasets.load_iris().data # 返回 iris 数据集所有输入特征 y_data = datasets.load_iris().target # 返回 iris 数据集所有标签
(2) 数据集乱序:
np.random.seed(116) # 使用相同的 seed,使输入特征/标签一一对应 np.random.shuffle(x_data)
np.random.seed(116)
np.random.shuffle(y_data)
tf.random.set_seed(116)
(3) 数据集分割成永不相见的训练集和测试集:
x_train = x_data[:-30]
y_train = y_data[:-30]
x_test = x_data[-30:]
y_test = y_data[-30:]
(4) 配成[输入特征,标签]对,每次喂入一小撮(batch):
train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(32)
test_db = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(32)
使用 from_tensor_slices 把训练集的输入特征 和标签配对打包,将每 32 组输入特征标签对打包为一个 batch,在喂入神经网络 时会以 batch 为单位喂入。
2.搭建网络,定义神经网络中的所有可训练参数;
(5) 定义神经网路中所有可训练参数:
w1 = tf.Variable(tf.random.truncated_normal([ 4, 3 ], stddev=0.1, seed=1))
b1 = tf.Variable(tf.random.truncated_normal([ 3 ], stddev=0.1, seed=1))
(6) 嵌套循环迭代,with结构更新参数,显示当前loss:
for epoch in range(epoch): #数据集级别迭代
for step, (x_train, y_train) in enumerate(train_db): #batch 级别迭代
with tf.GradientTape() as tape: # 记录梯度信息
(前向传播过程计算 y)
(计算总 loss)
grads = tape.gradient(loss, [ w1, b1 ])
w1.assign_sub(lr * grads[0]) #参数自更新
b1.assign_sub(lr * grads[1])
print("Epoch {}, loss: {}".format(epoch, loss_all/4))
(5) 部分定义了神经网络的所有可训练参数。只用了一层网络,因为输入特征是 4个,输出节点数等于分类数,是 3 分类,故参数 w1 为 4 行 3 列的张量,b1 必须与w1 的维度一致,所以是 3。(6)部分用两层 for 循环进行更新参数:第一层 for 循 环是针对整个数据集进行循环,故用 epoch 表示;第二层 for 循环是针对 batch 的,用step表示。在with 结构中计算前向传播的预测结果y,计算损失函数loss,损失函数 loss分别对参数 w1 和参数 b1 计算偏导数,更新参数 w1 和参数 b1 的值, 打印出这一轮 epoch 后的损失函数值。因为训练集有 120 组数据,batch 是 32,每个 step 只能喂入 32 组数据,需要 batch 级别循环 4 次,所以 loss 除以 4,求得每次 step 迭代的平均 loss。
3.优化这些可训练的参数,利用嵌套循环在 with 结构中求得损失函数 loss 对每个可训练参数的偏导数,更改这些可训练参数,为了查看效果,程序中可以加入每遍历一次数据集显示当前准确率,还可以画出准确率 acc 和损失函数 loss 的变化曲线图。
(7) 计算当前参数前向传播后的准确率,显示当前准确率acc:
for x_test, y_test in test_db:
y = tf.matmul(h, w) + b
y = tf.nn.softmax(y)
pred = tf.argmax(y, axis=1)
pred = tf.cast(pred, dtype=y_test.dtype) # 调整数据类型与标签一致
correct = tf.cast(tf.equal(pred, y_test), dtype=tf.int32)
correct = tf.reduce_sum (correct)
total_correct += int (correct)
total_number += x_test.shape [0]
acc = total_correct / total_number
print("test_acc:", acc)
(8) acc/loss可视化:
plt.title(Acc Curve)# 图片标题
plt.xlabel(Epoch) # x 轴名称
plt.ylabel(Acc)# y 轴名称
plt.plot(test_acc, label="$Accuracy$") plt.legend()# 逐点画出 test_acc 值并连线
plt.show()
上述两部分完成了对准确率的计算并可视化准确率与 loss。(7)部分前向传播计算出 y ,使其符合概率分布并找到最大的概率值对应的索引号,调整数据类型与标签一致,如果预测值和标签相等则 correct 变量自加一,准确率即预测对了的数量除以测试集中的数据总数。(9)部分可将计算出的准确率画成曲线图,通过设置图标题、设置 x 轴名称、设置 y 轴名称,标出每个 epoch 时的准确率并画出曲线,可用同样方法画出 loss 曲线。