2-工具与软件-5-TensorFlow

环境的安装

首先去conda官网下载 conda

选择合适的版本

linux系统先使用bash安装

1
sudo bash xxx.sh

安装后在pycharm配置conda环境,然后新建AI项目,选择conda,然后在所选择的解释器中安装tensorflow

选择pycharm自动安装(会自动安装其他依赖,十分方便)

tensorflow安装

所需安装:

  • conda
  • tensorflow

如果你是N卡,可继续在项目终端中输入

1
conda install cudatoolkit
1
conda install cudnn

安装GUP加速

1.1 人工智能三学派

三学派

行为主义:机器人的摔倒预测

符号主义:用公式描述的人工智能,让PC具有了理性思维

连接主义:仿造人的感性思维

1.2 神经网络的设计过程

用神经网络实现鸢尾花的分类:梯度下降

目的:找到一组参数w和b,使得损失函数最小。

梯度:函数对各参数求偏导后的向量。 梯度下降的方向是函数减小的方向

梯度下降法:沿损失函数梯度下降的方向,寻找损失函数的最小值,得到最优参数的方法

学习率

学习率(lr):设置过小,收敛缓慢;设置过大,无法收敛(找不到最小值)

反向传播:从后向前,逐层求损失函数对每层神经元参数的偏导数,迭代更新所有参数。

损失函数:
$$
loss = (w + 1 )^2
$$

$$
\frac{\part loss}{\part w} = 2w +2
$$

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import tensorflow as tf

w = tf.Variable(tf.constant(5, dtype=tf.float32))
lr = 0.2 # 学习率
epoch = 40 # 循环迭代数

for epoch in range(epoch): # for epoch 定义顶层循环,表示对数据集循环epoch次,此例数据集数据仅有1个w,初始化时候constant赋值为5,循环40次迭代。
with tf.GradientTape() as tape: # with结构到grads框起了梯度的计算过程。
loss = tf.square(w + 1)
grads = tape.gradient(loss, w) # .gradient函数告知谁对谁求导

w.assign_sub(lr * grads) # .assign_sub 对变量做自减 即:w -= lr*grads 即 w = w - lr*grads
print("After %s epoch,w is %f,loss is %f" % (epoch, w.numpy(), loss))

# lr初始值:0.2 请自改学习率 0.001 0.999 看收敛过程
# 最终目的:找到 loss 最小 即 w = -1 的最优参数w

Output:

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
After 0 epoch,w is 2.600000,loss is 36.000000
After 1 epoch,w is 1.160000,loss is 12.959999
After 2 epoch,w is 0.296000,loss is 4.665599
After 3 epoch,w is -0.222400,loss is 1.679616
After 4 epoch,w is -0.533440,loss is 0.604662
After 5 epoch,w is -0.720064,loss is 0.217678
After 6 epoch,w is -0.832038,loss is 0.078364
After 7 epoch,w is -0.899223,loss is 0.028211
After 8 epoch,w is -0.939534,loss is 0.010156
After 9 epoch,w is -0.963720,loss is 0.003656
After 10 epoch,w is -0.978232,loss is 0.001316
After 11 epoch,w is -0.986939,loss is 0.000474
After 12 epoch,w is -0.992164,loss is 0.000171
After 13 epoch,w is -0.995298,loss is 0.000061
After 14 epoch,w is -0.997179,loss is 0.000022
After 15 epoch,w is -0.998307,loss is 0.000008
After 16 epoch,w is -0.998984,loss is 0.000003
After 17 epoch,w is -0.999391,loss is 0.000001
After 18 epoch,w is -0.999634,loss is 0.000000
After 19 epoch,w is -0.999781,loss is 0.000000
After 20 epoch,w is -0.999868,loss is 0.000000
After 21 epoch,w is -0.999921,loss is 0.000000
After 22 epoch,w is -0.999953,loss is 0.000000
After 23 epoch,w is -0.999972,loss is 0.000000
After 24 epoch,w is -0.999983,loss is 0.000000
After 25 epoch,w is -0.999990,loss is 0.000000
After 26 epoch,w is -0.999994,loss is 0.000000
After 27 epoch,w is -0.999996,loss is 0.000000
After 28 epoch,w is -0.999998,loss is 0.000000
After 29 epoch,w is -0.999999,loss is 0.000000
After 30 epoch,w is -0.999999,loss is 0.000000
After 31 epoch,w is -1.000000,loss is 0.000000
After 32 epoch,w is -1.000000,loss is 0.000000
After 33 epoch,w is -1.000000,loss is 0.000000
After 34 epoch,w is -1.000000,loss is 0.000000
After 35 epoch,w is -1.000000,loss is 0.000000
After 36 epoch,w is -1.000000,loss is 0.000000
After 37 epoch,w is -1.000000,loss is 0.000000
After 38 epoch,w is -1.000000,loss is 0.000000
After 39 epoch,w is -1.000000,loss is 0.000000

1.3 张量生成

张量(Tensor:多维数组 /列表 ) 阶 :张量的维数

维数
0-D 0 标量 scalar s=1
1-D 1 向量 vector v=[1,2,3]
2-D 2 矩阵 matrix m=[[1,2],[3,4],[5,6]]
n-D n 张量 tensor t=[[[[……]]]] (n个)

数据类型

1
2
3
4
5
6
·tf.int   tf.float ...
tf.int 32 , tf.float 32 , tf.float 64
·tf.bool
tf.constant([true, false])
·tf.string
tf.constant("Hello world!")

创建Tensor

tf.constant(张量内容,dtype=数据类型(可选))

创建全为0的张量 tf.zeros(维度)

​ 纬度:一维直接写个数;二维[行,列];多维[n,m,j,k,…..]

创建全为1的张量 tf.ones(纬度)

创建全为指定值的张量 tf.fill(维度,指定值)

正态分部的随机数,默认值为0,标准差为1

tf.random.normal(纬度,mean=均值,stddev=标准差)

生成截断式正态分布的随机数

tf.random.truncated_normal(纬度,mean=均值,stddev=标准差)

在正态分布中如果随机生成的数据的取值在($\mu\pm2\sigma$)

生成均匀分布的随机数

tf.random.uniform(纬度,minval=最小值,maxval=最大值)

1.4 TF2常用函数

强制tensor转换为该数据类型
tf.cast (张量名,dtype=数据类型)
计算张量维度上元素的最小值
tf.reduce_min (张量名)
计算张量维度上元素的最大值
tf.reduce_max (张量名)

理解axis
在一个二维张量或数组中,可以通过调整 axis 等于0或1 控制执行维度。
axis=0代表跨行(经度,down),而axis=1代表跨列(纬度,across)
如果不指定axis,则所有元素参与计算。

理解axis

计算张量沿着指定维度的平均值
tf.reduce_mean (张量名,axis=操作轴) (不指定axis,则对所有元素进行操作)
计算张量沿着指定维度的和
tf.reduce_sum (张量名,axis=操作轴)

tf.Variable () 将变量标记为“可训练”,被标记的变量会在反向传播
中记录梯度信息。神经网络训练中,常用该函数标记待训练参数。
tf.Variable(初始值)
w = tf.Variable(tf.random.normal([2, 2], mean=0, stddev=1))

TensorFlow中的数学运算
对应元素的四则运算:tf.addtf.subtracttf.multiplytf.divide

只有纬度相同的张量才能做四则运算。
平方、次方与开方: tf.squaretf.powtf.sqrt
矩阵乘:tf.matmul

切分传入张量的第一维度,生成输入特征/标签对,构建数据集
data = tf.data.Dataset.from_tensor_slices((输入特征, 标签))
(Numpy和Tensor格式都可用该语句读入数据)

tf.GradientTape
with结构记录计算过程,gradient求出张量的梯度

1
2
3
with tf.GradientTape( ) as tape:
若干个计算过程
grad=tape.gradient(函数,对谁求导)

enumerate是python的内建函数,它可遍历每个元素(如列表、元组
或字符串),组合为:索引 元素,常在for循环中使用。
enumerate(列表名)

独热编码:在分类问题中,常用独热码做标签,标记类别:1表示是,0表示非。 tf.one_hot (待转换数据, depth=几分类)

当n分类的n个输出 (y0 ,y1, …… yn-1)通过softmax( ) 函数,
便符合概率分布了。也就是说,将多个权重占比划分归为1。
$$
\forall x \ \ P(X = x) \in [0,1] 且 \sum_{x}P(X = x) = 1
$$

assign_sub 赋值操作,更新参数的值并返回。
调用assign_sub前,先用 tf.Variable 定义变量 w 为可训练(可自更新)。
w.assign_sub (w要自减的内容)

返回张量沿指定维度最大值的索引
tf.argmax (张量名,axis=操作轴) numpy中也有类似函数

1.5 鸢尾花数据集的读入

Setosa Iris(狗尾草鸢尾),Versicolour Iris(杂色鸢尾),Virginica Iris(弗吉尼亚鸢尾)

鸢尾花数据来源:sklearn框架

3种鸢尾花

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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.8 神经网络实现鸢尾花的分类

1.准备数据

​ 数据集读入

1
2
3
4
# 从sklearn包datasets 读入数据集:
from sklearn.datasets import datasets
x_data = datasets.load_iris().data # 返回iris数据集所有输入特征
y_data = datasets.load_iris().target # 返回iris数据集所有标签

​ 数据集乱序

1
2
3
4
5
np.random.seed(116) # 使用相同的seed,使输入特征/标签一一对应
np.random.shuffle(x_data)
np.random.seed(116)
np.random.shuffle(y_data)
tf.random.set_seed(116)

​ 分成用不相见的训练集和测试集

1
2
3
4
x_train = x_data[:-30]
y_train = y_data[:-30]
x_test = x_data[-30:]
y_test = y_data[-30:]

​ 配成【输入特征,标签】对,每次喂入一个batch

1
2
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)

2.搭建网络

​ 定义神经网络中的所有可训练参数

1
2
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))

输入层与输出层

3.参数优化

​ 嵌套循环迭代,with结构更新参数,显示当前loss

1
2
3
4
5
6
7
8
9
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))

4.测试效果

​ 计算当前参数前向传播后的准确率,显示当前acc

1
2
3
4
5
6
7
8
9
10
11
12
for x_test, y_test in test_db:
y = tf.matmul(h, w) + b # y为预测结果
y = tf.nn.softmax(y)
# y符合概率分布
pred = tf.argmax(y, axis=1) # 返回y中最大值的索引,即预测的分类
pred = tf.cast(pred, dtype=y_test.dtype) #调整数据类型与标签一致
correct = tf.cast(tf.equal(pred, y_test), dtype=tf.int32)
correct = tf.reduce_sum (correct) # 将每个batch的correct数加起来
total_correct += int (correct) # 将所有batch中的correct数加起来
total_number += x_test.shape [0]
acc = total_correct / total_number
print("test_acc:", acc)

5.acc / loss 可视化(查看效果)

1
2
3
4
5
6
plt.title('Acc Curve') # 图片标题
plt.xlabel('Epoch') # x轴名称
plt.ylabel('Acc') # y轴名称
plt.plot(test_acc, label="$Accuracy$") # 逐点画出test_acc值并连线
plt.legend()
plt.show()

2.1 预备知识

函数:

tf.where() 条件语句真返回A,条件语句假返回B
tf.where(条件语句,真返回A,假返回B)

np.random.RandomState.rand()返回一个[0,1)之间的随机数
np.random.RandomState.rand(维度) #维度为空,返回标量

np.vstack()将两个数组按垂直方向叠加
np.vstack(数组1,数组2)

np.mgrid[ ] 返回间隔数值点,可同时返回多组, [起始值 结束值)
np.mgrid[ 起始值 : 结束值 : 步长 ,起始值 : 结束值 : 步长 , … ]

x.ravel( ) 将x变为一维数组,“把. 前变量拉直”
np.c\_[ ] 使返回的间隔数值点配对
np.c\_[ 数组1,数组2, … ]

2.2 复杂度学习率

NN复杂度:多用NN层数和NN参数的个数表示

复杂度

空间复杂度:
层数 = 隐藏层的层数 + 1个输出层
图为2层NN
总参数 = 总w + 总b
图中 3x4+4 + 4x2+2 = 26

时间复杂度:
乘加运算次数
左图 3x4 + 4x2 = 20

学习率:

学习率

指数衰减学习率:
可以先用较大的学习率,快速得到较优解,然后逐步减小学习率,使
模型在训练后期稳定。
指数衰减学习率 = 初始学习率 * 学习率衰减率( 当前轮数 / 多少轮衰减一次 )

2.3 激活函数

优秀的激活函数:
• 非线性: 激活函数非线性时,多层神经网络可逼近所有函数
• 可微性: 优化器大多用梯度下降更新参数
• 单调性: 当激活函数是单调的,能保证单层网络的损失函数是凸函数
• 近似恒等性: f(x)≈x当参数初始化为随机小值时,神经网络更稳定

激活函数输出值的范围:
• 激活函数输出为有限值时,基于梯度的优化方法更稳定
• 激活函数输出为无限值时,建议调小学习率

Sigmoid函数:
$$
f(x) = \frac{1}{1 + e ^ {-x}}
$$
Sigmoid函数

特点
(1)易造成梯度消失
(2)输出非0均值,收敛慢
(3)幂运算复杂,训练时间长
目前Sigmoid函数因计算复杂,已接近弃用。

Tanh函数:
$$
f(x) = \frac{1-e^{-2x}}{1+e^{-2x}}
$$
Tanh函数

特点
(1)输出是0均值
(2)易造成梯度消失
(3)幂运算复杂,训练时间长

Relu函数:
$$
f(x) = max(x , 0) = \begin{cases}0 \quad x<0 \\ x \quad x\geq0 \end{cases}
$$
Relu函数

优点:
(1) 解决了梯度消失问题 (在正区间)
(2) 只需判断输入是否大于0,计算速度快
(3) 收敛速度远快于sigmoid和tanh

缺点:
(1) 输出非0均值,收敛慢
(2) Dead RelU问题:某些神经元可能永远不会被激活,导致相应的参数永远不能被更新。(神经元死亡)

Leaky Relu函数:
$$
f(x) = max (ax,x)
$$
Leaky Relu函数

理论上来讲,Leaky Relu有Relu的所有优点,外加不会有Dead Relu问题,但是在实际操作当中,并没有完全证明Leaky Relu总是好于Relu。

对于初学者的建议:
首选relu激活函数;
学习率设置较小值;
输入特征标准化,即让输入特征满足以0为均值,1为标准差的正态分布;
初始参数中心化,即让随机生成的参数满足以0为均值,$\sqrt{\frac{2}{当前层输入特征个数}}$为标准差的正态分布。

2.4 损失函数

预测值(y)与已知答案(_y)的差距

主流的三种计算方法

均方误差mse:
$$
MSE(y_,y)=\frac{\sum_{i=1}^n (y-y_)^2}{n}
$$
lost_mse = tf.reduce_mean(tf.square(y_-y))

自定义函数:

可在一定程度上优化实际问题中的预测误差。

交叉熵CE:

表明了两个概率分布之间的距离,交叉熵越大,表明两个概率分布越远
$$
H(y_,y) = - \sum y_ \times ln\ y
$$
交叉熵越小,证明数据距离真实越准确。

softmax与交叉熵的结合:

在TensorFlow中提供了函数tf.nn.softmax_cross_entropy_with_logits(y_,y)输出先过softmax函数,再计算y与y_的交叉熵损失函数。

2.5 缓解过拟合

欠拟合与过拟合

欠拟合与过拟合

欠拟合的解决方法:
增加输入特征项
增加网络参数
减少正则化参数
过拟合的解决方法:
数据清洗
增大训练集
采用正则化
增大正则化参数

正则化缓解过拟合:

正则化在损失函数中引入模型复杂度指标,利用给W加权值,弱化了训练
数据的噪声(一般不正则化b)

正则化

正则化的选择
L1正则化大概率会使很多参数变为零,因此该方法可通过稀疏参数,即减少参数的数量,降低复杂度。
L2正则化会使参数很接近零但不为零,因此该方法可通过减小参数值的大小降低复杂度。

使用L2正则化缓解过拟合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
with tf.GradientTape() as tape:  # 记录梯度信息

h1 = tf.matmul(x_train, w1) + b1 # 记录神经网络乘加运算
h1 = tf.nn.relu(h1)
y = tf.matmul(h1, w2) + b2

# 采用均方误差损失函数mse = mean(sum(y-out)^2)
loss_mse = tf.reduce_mean(tf.square(y_train - y))
# 添加l2正则化
loss_regularization = []
# tf.nn.l2_loss(w)=sum(w ** 2) / 2
loss_regularization.append(tf.nn.l2_loss(w1))
loss_regularization.append(tf.nn.l2_loss(w2))
# 求和
# 例:x=tf.constant(([1,1,1],[1,1,1]))
# tf.reduce_sum(x)
# >>>6
loss_regularization = tf.reduce_sum(loss_regularization)
loss = loss_mse + 0.03 * loss_regularization # REGULARIZER = 0.03

# 计算loss对各个参数的梯度
variables = [w1, b1, w2, b2]
grads = tape.gradient(loss, variables)

下表可以看出,L2正则化函数可有效的缓解过饱和现象

未填加L2正则化 加入了L2正则化

2.6 优化器

是引导神经网络更新参数的工具。

神经网络参数优化器:
待优化参数𝒘,损失函数loss,学习率lr,每次迭代一个batch,t表示当前batch迭代的总次数:

  1. 计算t时刻损失函数关于当前参数的梯度 $g_t=\triangledown loss = \frac{\partial loss}{\partial (w_t)} $
  2. 计算t时刻一阶动量 $m_t$ 和二阶动量$V_t$
  3. 计算t时刻下降梯度:$\eta_t =\frac{lr·m_t}{\sqrt{V_t}}$
  4. 计算t+1时刻参数:$w_{t+1} = w_t - \eta_t = w_t - \frac{lr·m_t}{\sqrt{V_t}}$

一阶动量:与梯度相关的函数
二阶动量:与梯度平方相关的函数

SGD(无momentum),常用的梯度下降算法:

$m_t = g_t $ $V_t = 1$

$\eta_t = \frac{lr·m_t}{\sqrt{V_t}} $

$w_{t+1} = w_t -\eta_t = w_t - \frac{lr·m_t}{\sqrt{V_t}} \ =w_t - lr·g_t $

$w_{t+1} = w_t -lr \ast \frac{\partial loss}{\partial w_t} \ 参数更新公式$

SGDM(含momentum的SGD),在SGD的基础上增加了一阶动量:

$m_{t-1}$表示上一时刻的一阶动量。

$m_t = \beta · m_{t-1} + (1-\beta )·g_t $ $V_t=1$

$\eta_t= \frac{lr·m_t}{\sqrt{V_t}} = lr· m_t =lr·(\beta · m_{t-1}+(1-\beta)·g_t)$

$w_{t+1}=w_t -\eta_t = w_t - lr · (\beta · m_{t-1}+(1-\beta)·g_t)$

Adagrad,在SGD基础上增加二阶动量:

$m_t=g_t$ $V_t=\sum_{\tau=1}^t g_\tau^2$

$\eta_t=\frac{lr·m_t}{\sqrt{V_t}} =\frac{lr·g_t}{\sqrt{\sum_{\tau=1}^t g_\tau^2}} $

$w_{t+1}=w_t-\eta_t=w_t-\frac{lr·g_t}{\sqrt{\sum_{\tau=1}^t g_\tau^2}}$

RMSProp,SGD基础上增加二阶动量:

$m_t=g_t$ $V_t = \beta · V_{t-1} + (1-\beta)·g_t^2 $

$\eta_t=\frac{lr·m_t}{\sqrt{V_t}} =\frac{lr·g_t}{\sqrt{ \beta · V_{t-1} + (1-\beta)·g_t^2}} $

$w_{t+1}=w_t-\eta_t=w_t-\frac{lr·g_t}{\sqrt{ \beta · V_{t-1} + (1-\beta)·g_t^2}}$

Adam, 同时结合SGDM一阶动量和RMSProp二阶动量:

$m_t=\beta_1 ·m_{t-1}+(1-\beta_1)·g_t$

修正一阶动量的偏差:$\widehat{m_t}=\frac{m_t}{1-\beta_1^t}$

$V_t=\beta_2 · V_{step-1}+(1-\beta_2)·g_t^2$

修正二阶动量的偏差:$ \widehat{V_t}= \frac{V_t} {1-\beta_2^t} $

$\eta_t=\frac{lr·\widehat{m_t} }{\sqrt{\widehat{V_t} } } = lr \cdot \frac{lr{\frac{m_t}{1-\beta_1^t} } }{\sqrt{ {\frac{V_t} {1-\beta_2^t} } } } $

$w_{t+1}=w_t-\eta_t=w_t-\frac{lr\cdot{\frac{m_t}{1-\beta_1^t} } }{\sqrt{ {\frac{V_t}{1-\beta_2^t} } } } $

3.1 搭建网络八股Sequential

用Tensorflow API:tf.keras搭建网络八股
六步法

1
2
3
4
5
6
import    #引入相关模块
train, test #告知喂入网络的训练集和测试集 特征x_train和标签y_train
model = tf.keras.models.Sequential #搭建网络解构
model.compile # 配置训练方法(选择优化器、损失函数、评测指标)
model.fit # 训练过程,告知train、test,告知batch、迭代次数
model.summary # 打印网络解构、参数统计

model = tf.keras.models.Sequential ([ 网络结构 ]) #描述各层网络
Sequential是容器,给出从输入层到输出层的各层网络解构

拉直层: tf.keras.layers.Flatten( )

全连接层: tf.keras.layers.Dense(神经元个数, activation= "激活函数“ ,kernel_regularizer=哪种正则化)
activation(字符串给出)可选: relu、 softmax、 sigmoid 、 tanh
kernel_regularizer可选:tf.keras.regularizers.l1()tf.keras.regularizers.l2()

卷积层: tf.keras.layers.Conv2D(filters = 卷积核个数, kernel_size = 卷积核尺寸,
strides = 卷积步长, padding = “ valid” or “same”)

LSTM层: tf.keras.layers.LSTM()

model.compile(optimizer = 优化器,loss = 损失函数 metrics = [“准确率”] )

1
2
3
4
5
6
7
8
9
10
11
12
# Optimizer可选:
‘sgd’ or tf.keras.optimizers.SGD (lr=学习率,momentum=动量参数)
‘adagrad’ or tf.keras.optimizers.Adagrad (lr=学习率)
‘adadelta’ or tf.keras.optimizers.Adadelta (lr=学习率)
‘adam’ or tf.keras.optimizers.Adam (lr=学习率, beta_1=0.9, beta_2=0.999)
# loss可选:
‘mse’ or tf.keras.losses.MeanSquaredError()
‘sparse_categorical_crossentropy’ or tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False)
# Metrics可选:
‘accuracy’ :y_和y都是数值,如y_=[1] y=[1]
‘categorical_accuracy’ :y_和y都是独热码(概率分布),如y_=[0,1,0] y=[0.256,0.695,0.048]
‘sparse_categorical_accuracy’ :y_是数值,y是独热码(概率分布),如y_=[1] y=[0.256,0.695,0.048]

model.fit (训练集的输入特征, 训练集的标签, batch_size= , epochs= , validation_data=(测试集的输入特征,测试集的标签), validation_split=从训练集划分多少比例给测试集, validation_freq = 多少次epoch测试一次)

使用sequential可以搭建出上层输出就是下层输入的下层网络机构,但无法写出一些带有跳连的非顺序网络结构,这时候可以选择用类Class搭建神经网络解构。

3.2 搭建网络八股Class

在六步法中,将第三部的Model改为class MyModel(Model) model=MyModel

1
2
3
4
5
6
7
8
9
10
11
12
13
class MyModel(Model): # 继承了Tensorflow的model类
def __init__(self):
super(MyModel, self).__init__()
定义网络结构块
def call(self, x):
调用网络结构块,实现前向传播
return y
model = MyModel()


#__init__( ) 定义所需网络结构块
#call( ) 写出前向传播 实现钱前向传播

3.3 MNIST数据集

手写数字的数据集-上万张

导入MNIST数据集:

1
2
 mnist = tf.keras.datasets.mnist
(x_train, y_train) , (x_test, y_test) = mnist.load_data()

为输入特征,输入神经网络时,将数据拉伸为一维数组:
tf.keras.layers.Flatten( )

3.4 FASHION数据集

提供 6万张 28X28 像素点的衣裤等图片和标签,用于训练。
提供 1万张 28X28 像素点的衣裤等图片和标签,用于测试。

导入FASHION数据集:

1
2
fashion = tf.keras.datasets.fashion_mnist
(x_train, y_train),(x_test, y_test) = fashion.load_data()

4.1 搭建网络八股总览

① 自制数据集,解决本领域应用
② 数据增强,扩充数据集
③ 断点续训,存取模型
④ 参数提取,把参数存入文本
⑤ acc/loss可视化,查看训练效果
⑥ 应用程序,给图识物

六步法八股总览

4.2 自制数据集

使用Py,目的是将文件夹内的图片读入,返回输入特征、标签。

标签文件txt

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import tensorflow as tf
from PIL import Image
import numpy as np
import os
### import


train_path = './mnist_image_label/mnist_train_jpg_60000/'
train_txt = './mnist_image_label/mnist_train_jpg_60000.txt'
x_train_savepath = './mnist_image_label/mnist_x_train.npy'
y_train_savepath = './mnist_image_label/mnist_y_train.npy'
### 训练集


test_path = './mnist_image_label/mnist_test_jpg_10000/'
test_txt = './mnist_image_label/mnist_test_jpg_10000.txt'
x_test_savepath = './mnist_image_label/mnist_x_test.npy'
y_test_savepath = './mnist_image_label/mnist_y_test.npy'

### 测试集

def generateds(path, txt):
f = open(txt, 'r') # 以只读形式打开txt文件
contents = f.readlines() # 读取文件中所有行
f.close() # 关闭txt文件
x, y_ = [], [] # 建立空列表
for content in contents: # 逐行取出
value = content.split() # 以空格分开,图片路径为value[0] , 标签为value[1] , 存入列表
img_path = path + value[0] # 拼出图片路径和文件名
img = Image.open(img_path) # 读入图片
img = np.array(img.convert('L')) # 图片变为8位宽灰度值的np.array格式
img = img / 255. # 数据归一化 (实现预处理)
x.append(img) # 归一化后的数据,贴到列表x
y_.append(value[1]) # 标签贴到列表y_
print('loading : ' + content) # 打印状态提示

x = np.array(x) # 变为np.array格式
y_ = np.array(y_) # 变为np.array格式
y_ = y_.astype(np.int64) # 变为64位整型
return x, y_ # 返回输入特征x,返回标签y_

### generateds函数


if os.path.exists(x_train_savepath) and os.path.exists(y_train_savepath) and os.path.exists(
x_test_savepath) and os.path.exists(y_test_savepath):
print('-------------Load Datasets-----------------')
x_train_save = np.load(x_train_savepath)
y_train = np.load(y_train_savepath)
x_test_save = np.load(x_test_savepath)
y_test = np.load(y_test_savepath)
x_train = np.reshape(x_train_save, (len(x_train_save), 28, 28))
x_test = np.reshape(x_test_save, (len(x_test_save), 28, 28))
else:
print('-------------Generate Datasets-----------------')
x_train, y_train = generateds(train_path, train_txt)
x_test, y_test = generateds(test_path, test_txt)

print('-------------Save Datasets-----------------')
x_train_save = np.reshape(x_train, (len(x_train), -1))
x_test_save = np.reshape(x_test, (len(x_test), -1))
np.save(x_train_savepath, x_train_save)
np.save(y_train_savepath, y_train)
np.save(x_test_savepath, x_test_save)
np.save(y_test_savepath, y_test)

### train test



model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')
])

### models.sequential

model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy'])

### model.compile

model.fit(x_train, y_train, batch_size=32, epochs=5, validation_data=(x_test, y_test), validation_freq=1)

### model.fit

model.summary()

### model.summary

第一次运行生成了npy格式的数据集。

第二次会加载数据集,执行训练过程。

4.3 数据增强

增大数据量,扩充数据集

1
2
3
4
5
6
7
8
image_gen_train =tf.keras.preprocessing.image.ImageDataGenerator(
rescale = 所有数据将乘以该数值
rotation_range = 随机旋转角度数范围
width_shift_range = 随机宽度偏移量
height_shift_range = 随机高度偏移量
水平翻转:horizontal_flip = 是否随机水平翻转
随机缩放:zoom_range = 随机缩放的范围 [1-n,1+n] )
image_gen_train.fit(x_train)

4.4 断点续训

可以存取模型

读取模型:
load_weights(路径文件名)

可以先检查是否存在断点,如有则加载模型。

1
2
3
4
cheakpoint_save_path= "./cheakpoint/mnist.ckpt"
if os.path.exists(checkpoint_save_path + '.index'):
print('--------load the model-------')
model.load_weights(checkpoint_save_path)

保存模型: tf.keras.callbacks.ModelCheckpoint(filepath=路径文件名,save_weights_only=True/False,save_best_only=True/False) history = model.fit( callbacks=[cp_callback] )

4.5 参数提取

把参数存入文本

提取可训练参数
model.trainable_variables 返回模型中可训练的参数
设置print输出格式
np.set_printoptions(threshold=超过多少省略显示)

1
2
3
4
5
6
7
8
9
10
11
np.set_printoptions(threshold=np.inf)
# np.inf表示无限大


print(model.trainable_variables)
file = open('./weights.txt', 'w')
for v in model.trainable_variables:
file.write(str(v.name) + '\n')
file.write(str(v.shape) + '\n')
file.write(str(v.numpy()) + '\n')
file.close()

4.6 acc/loss可视化

acc曲线与loss曲线

history=model.fit(训练集数据, 训练集标签, batch_size=, epochs=,validation_split=用作测试数据的比例,validation_data=测试集,validation_freq=测试频率)

只是一段画图程序代码。

添加的画图代码

4.7 图片识别

给图识物

predict(输入特征,batch_size=整数)返回向前传播的计算结果

复现模型(前向传播):

model = tf.keras.models.Sequential([ tf.keras.layers.Flatten(), tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Dense(10, activation='softmax’)])

加载参数:

model.load_weights(model_save_path

预测结果:

result = model.predict(x_predict)

5.1 卷积的计算过程 Convolutional

全连接 NN 特点:每个神经元与前后相邻层的每一个神经元都有连接关系。(可以实
现分类和预测)

全连接网络参数的个数为:$\sum(前层\times 后层 + 后层)$

卷积的概念:卷积可以认为是一种有效提取图像特征的方法。一般会用一个正方形的
卷积核,按指定步长,在输入特征图上滑动,遍历输入特征图中的每个像素点。每一个步长,
卷积核会与输入特征图出现重合区域,重合区域对应元素相乘、求和再加上偏置项得到输出
特征的一个像素点。

对于彩色图像(多通道)来说,卷积核通道数与输入特征一致,套接后在对应位置上进行乘加和操作,如果是彩色图片(RGB)利用三通道卷积核对三通道的彩色特征图做卷积计算。

输出特征尺寸计算

5.2 感受野

感受野(Receptive Field):卷积神经网络各输出特征图中的每个像素点,在原始输入图片上映射区域的大小。

黄-绿为两次3*3 蓝色为一次5*5

通常用两层3*3卷积核替换一层5*5卷积核

5.3 全零填充 Padding

将图的四周加上一圈零填充。

padding

TF描述全零填充
用参数padding = ‘SAME’ 或 padding = ‘VALID’表示

SAME:5X5X1 –> 5X5X1 VALID:5X5X1–>3X3X1

可以让输出特征图和输出特征图的尺寸不变。

5.4 TF描述卷积计算层

1
2
3
4
5
6
7
8
tf.keras.layers.Conv2D (
filters = 卷积核个数,
kernel_size = 卷积核尺寸, #正方形写核长整数,或(核高h,核宽w)
strides = 滑动步长, #横纵向相同写步长整数,或(纵向步长h,横向步长w),默认1
padding = “same” or “valid”, #使用全零填充是“same”,不使用是“valid”(默认)
activation = “ relu ” or “ sigmoid ” or “ tanh ” or “ softmax”等 , #如有BN此处不写
input_shape = (高, 宽 , 通道数) #输入特征图维度,可省略
)

TF描述卷积层

例如可以使用关键字传递参数的方法。

5.5 批标准化 BN

标准化:使数据符合0均值,1为标准差的分布。

批标准化:对一小批数据(batch),做标准化处理 。

批标准化后,第 k个卷积核的输出特征图(feature map)中第 i 个像素点

BN层位于卷积层之后,激活层之前。

批标准化

5.6 池化 Pooling

池化用于减少特征数据量。平均池化和最大池化

1
2
3
4
5
6
7
8
9
10
tf.keras.layers.MaxPool2D(
pool_size=池化核尺寸,#正方形写核长整数,或(核高h,核宽w)
strides=池化步长,#步长整数, 或(纵向步长h,横向步长w),默认为pool_size
padding=‘valid’or‘same’ #使用全零填充是“same”,不使用是“valid”(默认)
)
tf.keras.layers.AveragePooling2D(
pool_size=池化核尺寸,#正方形写核长整数,或(核高h,核宽w)
strides=池化步长,#步长整数, 或(纵向步长h,横向步长w),默认为pool_size
padding=‘valid’or‘same’ #使用全零填充是“same”,不使用是“valid”(默认)
)

池化

5.7 舍弃 Dropout

在神经网络训练时,将一部分神经元按照一定概率从神经网络中暂时舍弃。神经网络使用时,被舍弃的神经元恢复链接。

舍弃

5.8 卷积神经网络

卷积是什么? 卷积就是特征提取器,就是CBAPD

CBAPD

CNN

5.9 Cifar10数据集 卷积神经网络搭建示例

提供 5万张 32*32 像素点的十分类彩色图片和标签,用于训练。
提供 1万张 32*32 像素点的十分类彩色图片和标签,用于测试。

1
2
3
4
5
6
7
8
9
10
#导入cifar10数据集:
cifar10 = tf.keras.datasets.cifar10
(x_train, y_train),(x_test, y_test) = cifar10.load_data()

plt.imshow(x_train[0])
#绘制图片
plt.show()
print("x_train[0]:\n" , x_train[0])
print("y_train[0]:", y_train[0])
print("x_test.shape:", x_test.shape)

卷积神经网络搭建示例

搭建示例

5.10 LeNet AlexNet VGGNet InceptionNet ResNet

LeNet由Yann LeCun于1998年提出,卷积网络开篇之作。

AlexNet网络诞生于2012年,当年ImageNet竞赛的冠军,Top5错误率为16.4%。

VGGNet诞生于2014年,当年ImageNet竞赛的亚军,Top5错误率减小到7.3%。

InceptionNet诞生于2014年,当年ImageNet竞赛冠军,Top5错误率为6.67%

ResNet诞生于2015年,当年ImageNet竞赛冠军,Top5错误率为3.57%

经典卷积网络

6.1 循环核

循环核:参数时间共享,循环层提取时间信息。

循环核

6.2 循环核按时间步展开

循环神经网络:借助循环核提取时间特征后,送入全连接网络。

循环神经网络

循环计算层:向输出方向生长。

循环计算层

6.3 TF描述循环计算层

1
2
3
4
5
6
tf.keras.layers.SimpleRNN(记忆体个数,activation=‘激活函数’ ,
return_sequences=是否每个时刻输出ht到下一层)
activation=‘激活函数’ (不写,默认使用tanh)
return_sequences=True 各时间步输出ht
return_sequences=False 仅最后时间步输出ht(默认)
例:SimpleRNN(3, return_sequences=True)

入RNN时, x_train维度:
[送入样本数, 循环核时间展开步数, 每个时间步输入特征个数]

6.4 循环计算过程-字母输入预测

字母预测:输入a预测出b,输入b预测出c,
输入c预测出d,输入d预测出e,输入e预测出a

用RNN实现输入一个字母,预测下一个字母
(One hot 编码)独热码

用RNN实现输入连续四个字母,预测下一个字母
(One hot 编码)

用RNN实现输入一个字母,预测下一个字母
(Embedding 编码)

用RNN实现输入连续四个字母,预测下一个字母
(Embedding 编码)

6.5 股票预测

用RNN实现股票预测

用LSTM实现股票预测

LSTM 由Hochreiter & Schmidhuber 于1997年提出,通过门控单元改善了RNN长期依赖问题。

用GRU实现股票预测

GRU由Cho等人于2014年提出,优化LSTM结构。

更新于:2024