Tensorflow 入门教程(1)

视频教程链接:

【国家精品课程】北京大学人工智能实践-TensorFlow2.0

一、张量以及常用函数

1.创建张量

tf.constant()函数进行创建

1
2
3
4
5
6
import tensorflow as tf

a = tf.constant([[1,2]],dtype=tf.int32)
print(a)
print(a.dtype)
print(a.shape)

运行结果:

image-20230501145049017

numpy格式转换为tensor格式

1
2
a = numpy.range(0,5)
b = tf.convert_to_tensor(a,dtype=tf.int64)

创建特殊张量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建全部为0的张量
tf.zeros(维度)

# 创建全部为1的张量
tf.ones(维度)

# 创建全为指定值的张量
tf.fill(维度,指定值) # tf.fill([2,2],9)

# 生成正太分布的随机数 默认均值为 0 标准差为1
tf.random.normal(维度,mean=均值,stddev=标准差)

# 生成截断式正太分布的随机数 生成的随机数据取值在(均值-2*标准差,均值+2*标准差)
tf.random.truncated_normal(维度,mean=均值,stddev=标准差)

# 生成均匀分布随机数 在[minval,maxval]之间
tf.random.uniform(维度,minval=最小值,maxval=最大值)

2.常用函数

1
2
3
4
5
6
7
8
# 强制tensor转换该数据
tf.cast(张量名,dtype=数据类型)

# 计算张量维度上元素的最大值
tf.reduce_min(张量名)

# 计算张量维度上元素的最大值
tf.reduce_max(张量名)

指定axis

image-20230501150803267

1
2
3
4
5
# 计算张量沿着指定维度的平均值
tf.reduce_mean(张量名,axis=操作轴)

# 计算张量沿着指定维度的和
tf.reduce_sum(张量名,axis=操作轴)

示例code:

1
2
3
4
5
6
7
8
9
import tensorflow as tf

a = tf.constant([[1,2,3],[2,2,3]],dtype=tf.int32)

print(a)

print(tf.reduce_mean(a))

print(tf.reduce_sum(a,axis=1))

运行结果:

image-20230501151534626

(1)tf.Varible()
1
2
# 将变量标记为可训练,被标记的变量会在反向传播中记录梯度信息
tf.Varible(初始值)
(2)Tensorflow中的数学运算

image-20230501152015863

image-20230501152046019

image-20230501152232661

image-20230501152255367

(3)输入特征与标签配对的函数

numpy与tensor格式都可以用该语句输入数据

1
2
# 切分传入张量的第一维度,生成输入特征与标签对,构建数据集
tf.data.Dataset.from_tensor_slices((输入特征,标签))

示例code:

1
2
3
4
5
6
7
8
9
10
import tensorflow as tf

feature = tf.constant([12,23,10,17])
labels = tf.constant([0,1,1,0])

dataset = tf.data.Dataset.from_tensor_slices((feature,labels))
print(dataset)

for els in dataset:
print(els)

运行结果:

image-20230501153406054

(4)tf.GradientTape()
1
2
3
4
# 函数对参数的求导运算,with结构记录计算过程,gradient求出张量的梯度
with tf.GradientTape() as tape:
若干计算过程
grad = tape.gradient(函数,对谁求导)

示例:

image-20230501154342092

1
2
3
4
5
with tf.GradientTape() as tape:
w = tf.Variable(tf.constant(3.0))
loss = tf.pow(w, 2)
grad = tape.gradient(loss, w)
print(grad)

运行:

image-20230501154356793

(5)tf.one_hot()

独热编码:在分类问题中,常用独热编码作为标签,标记类别:1表示是,0表示非

1
2
# 将待转换的数据 转换为one-hot形式的数据输出
tf.one_hot(待转换的数据,depth=几分类)

示例:

1
2
3
4
5
#tf.one_hot
classes=3
labels=tf.constant([1,0,2]) # 输入的元素值最小为0 最大为2
output=tf.one_hot(labels,depth=classes)
print(output)

运行结果:

image-20230501155233232

(6)tf.nn.softmax()

image-20230501155629072

image-20230501155656570

(7) assign_sub()
1
2
3
# 赋值操作 更新参数的值并且返回
# 调用assign_sub之前,先用tf.Varible定义w为可以训练(可自更新)
w.assign(w要自减的内容)

示例:

1
2
3
4
#assign_sub
w=tf.Variable(4)
w.assign_sub(1)
print(w)

运行:

image-20230501160203099

(8)tf.argmax()
1
2
# 返回张量沿指定维度最大值的索引
tf.argmax(张量名,axis=操作轴)

image-20230501160348245

示例:

1
2
3
4
5
6
#tf.argmax
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))#返回每一行最大值的索引

运行:

1
2
3
4
5
6
[[1 2 3]
[2 3 4]
[5 4 3]
[8 7 2]]
tf.Tensor([3 3 1], shape=(3,), dtype=int64)
tf.Tensor([2 2 0 0], shape=(4,), dtype=int64)

二、鸢尾花分类

数据集介绍:

image-20230501160800120

1.准备数据

(1)数据集读入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from sklearn import datasets
import pandas as pd


x_data = datasets.load_iris().data # 返回iris数据集所有数据集
y_data = datasets.load_iris().target # 返回iris数据集所有标签
# print("x_data:\n", x_data)
# print("y_data:\n", y_data)


# 将数据变成表格的形式
x_data = pd.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
print('x_data add a column:\n', x_data)

运行:

image-20230501161709190

(2)数据集乱序
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
from sklearn import datasets
from pandas import DataFrame
import pandas as pd
import tensorflow as tf
import numpy as np
from matplotlib import pyplot as plt

# 读入数据
x_data = datasets.load_iris().data # 返回iris数据集所有数据集
y_data = datasets.load_iris().target # 返回iris数据集所有标签

# 数据集乱序
np.random.seed(116) # 使用相同的seed,使输入特征/标签一一对应
np.random.shuffle(x_data) # 打乱
np.random.seed(116)
np.random.shuffle(y_data)

# 数据集分出永不相见的训练集和测试集
x_train = x_data[:-30]
y_train = y_data[:-30]
x_test = x_data[-30:]
y_test = y_data[-30:]

# 转换数据类型
x_train = tf.cast(x_train, tf.float32)
x_test = tf.cast(x_test, tf.float32)

# 配成输入特征,标签对,每次喂入一小撮(32个样本为一个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)

2.训练

两层网络,输入层以及输出层(4特征输入 3特征输出)

1
2
3
# 定义神经网络中所有可训练参数(初始化的参数权值)
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))

整体code:

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
92
93
94
95
96
97
98
99
100
101
from sklearn import datasets
from pandas import DataFrame
import pandas as pd
import tensorflow as tf
import numpy as np
from matplotlib import pyplot as plt

x_data = datasets.load_iris().data # 返回iris数据集所有数据集
y_data = datasets.load_iris().target # 返回iris数据集所有标签

np.random.seed(116) # 使用相同的seed,使输入特征/标签一一对应
np.random.shuffle(x_data) # 打乱
np.random.seed(116)
np.random.shuffle(y_data)

# 数据集分出永不相见的训练集和测试集
x_train = x_data[:-30]
y_train = y_data[:-30]
x_test = x_data[-30:]
y_test = y_data[-30:]

# 转换数据类型
x_train = tf.cast(x_train, tf.float32)
x_test = tf.cast(x_test, tf.float32)

# 配成输入特征,标签对,每次喂入一小撮
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)

# 定义神经网络中所有可训练参数
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))

lr = 0.1
train_loss_result = [] # 将每轮loss记录在此列表中,为后续画loss曲线提供数据
test_acc = []
epoch = 500
loss_all = 0 # 每轮分4个step,loss_all记录四个step生成4个loss值



"""----------- 嵌套循环迭代,with结构更新参数,显示当前loss -----------"""
for epoch in range(epoch):
"""-------训练部分--------"""
for step, (x_train, y_train) in enumerate(train_db):
with tf.GradientTape() as tape: # 记录梯度信息
# 前向传播过程记录y
y = tf.matmul(x_train, w1) + b1 # 前向传播计算预测值
y = tf.nn.softmax(y) # 使得预测y得分符合概率分布
# 对预测结果使用softmax()函数之后才可以与实际标签的one_hot进行比较
y_ = tf.one_hot(y_train, depth=3) # 将标签值独热编码
# 记录总loss
loss = tf.reduce_mean(tf.square(y_ - y)) # 采用均方误差损失值mse
loss_all += loss.numpy()
# 计算loss对各个参数的梯度
grads = tape.gradient(loss, [w1, b1])
# 实现梯度更新,w1=w1-lr*w1_grad b=b-lr*b_grad
w1.assign_sub(lr * grads[0])
b1.assign_sub(lr * grads[1])
# 每个epoch打印loss信息
print("epoch{},loss{}".format(epoch, loss_all / 4)) # (训练集有120组数据,每个step只能喂入32组数据,需要batch级别循环4次,求每个step的平均loss)
train_loss_result.append(loss_all / 4) # 将四个step的loss求平均值记录在此变量中
loss_all = 0 # loss_all值归0,为下一个epoch的loss做准备


"""-------测试部分--------"""
# 计算当前参数前向传播后的准确率,显示当前acc
total_correct, total_number = 0, 0
for x_test, y_test in test_db: # 遍历测试集中所有数据
y = tf.matmul(x_test, w1) + b1 # 前向传播计算预测值
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) # equal,相等的意思。顾名思义,就是判断,x, y 是不是相等,它的判断方法不是整体判断,而是逐个元素进行判断,如果相等就是True,不相等,就是False。由于是逐个元素判断,所以x,y 的维度要一致。
correct = tf.reduce_sum(correct) # 将所有batch中的correct数加起来
total_correct += int(correct) # 将所有batch中的correct数加起来
total_number += x_test.shape[0] # 在矩阵中,[0]就表示行数(样本数),[1]则表示列数,[2]代表通道数
acc = total_correct / total_number
test_acc.append(acc)
print("test_acc", acc)
print("------------------------------")


"""---------------可视化------------"""
# acc/loss可视化
# 绘制loss曲线
plt.title('loss Curve') # 图片隐私
plt.xlabel('Epoch') # x轴名称
plt.ylabel('loss')
plt.plot(train_loss_result, label="$loss$") # 逐点画出test_acc值并连线,?epoch?
plt.legend()
plt.show()

# 绘制accuracy曲线
plt.title('Acc Curve') # 图片隐私
plt.xlabel('Epoch') # x轴名称
plt.ylabel('Acc')
plt.plot(test_acc, label="$Accuracy$") # 逐点画出test_acc值并连线,?epoch?
plt.legend()
plt.show()

三、神经网络的优化过程

1.预备知识

(1)tf.where()

image-20230501170511960

示例code:

1
2
3
4
a = tf.constant([1, 2, 3, 1, 1])
b = tf.constant([0, 1, 3, 4, 5])
c = tf.where(tf.greater(a, b), a, b) # 若a>b,返回a对应位置的元素,否则返回b对应位置的元素
print("c:", c)

运行:

image-20230501170624125

(2)np.random.RandomState.rand()

产生一个0-1之间的随机数

1
np.random.RandomState.rand(维度)

示例:

1
2
3
4
5
6
7
import numpy as np

rdm = np.random.RandomState(seed=1) # 随机数种子 seed为常数导致每一次生成的随机数相同
a = rdm.rand()
b = rdm.rand(2, 3)
print("a:", a)
print("b:", b)

运行:

image-20230501171105924

(3)np.vstack()

将两个数组按照垂直方向叠加

1
np.vstack(数组1,数组2)

示例:

1
2
3
4
5
6
import numpy as np

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = np.vstack((a, b))
print("c:\n", c)

运行:

image-20230501171401103

(4)np.mgrid[] .ravel() np.c_[]

np.mgrid[] 返回若干组维度相同的等差数组

x.ravel() 将x变为一维数组 把.前的变量拉直

np.c_[] 使返回的间隔数组点配对

`np.c_[数组1,数组2,…]

示例;

1
2
3
4
5
6
7
8
9
10
11
12
import numpy as np
import tensorflow as tf

# 生成等间隔数值点
x, y = np.mgrid[1:3:1, 2:4:0.5]
# 将x, y拉直,并合并配对为二维张量,生成二维坐标点
grid = np.c_[x.ravel(), y.ravel()]
print("x:\n", x)
print("y:\n", y)
print("x.ravel():\n", x.ravel())
print("y.ravel():\n", y.ravel())
print('grid:\n', grid)

示例;

image-20230501172726637

2.损失函数

(1)均方损失函数

image-20230502100229116

1
LOSS_MSE = tf.reduce_mean(tf.square(y_ - y))   # y 模型前向传播值    y_ 样本标签
(2)交叉熵损失函数

image-20230502100543147

y_与y的距离越近(越接近)其交叉熵就越小

1
LOSS_fn = tf.loss.categorical_crossentropy(y_ - y)   # y 模型前向传播值     
softmax 与交叉熵结合

输出先过softmax函数,再计算yy_的交叉熵损失函数

分步计算

1
2
3
# y已经经过前向传播得到的预测值
y_pro = tf.nn.softmax(y) # 经过 softmax函数,得到概率分布
loss_ce1 = tf.losses.categorical_crossentropy(y_,y_pro) # y_ 样本标签

结合计算

1
loss_ce2 = tf.softmax_cross_entropy_with_logits(y_,y)

3.优化器

image-20230502102645614

(1)SGD

image-20230502103018837

(2)SGDM

image-20230502103205345

对于单层网络

1
2
3
4
5
6
7
m_w,m_b = 0,0
beta = 0.9 # 动量超参数

m_w = beta * m_w + (1 - beta) * grads[0]
m_b = beta * m_b + (1 - beta) * grads[1]
w1.assign_sub(lr * m_w) # 参数自更新
b1.assign_sub(lr * m_b)
(3)Adagrad

image-20230502103919433

Adagrad的一阶动量mt就是当前时刻的梯度,二阶动量是梯度平方的累积和

1
2
3
4
5
6
v_w,v_b = 0,0

v_w += tf.square(grads[0])
v_b += tf.square(grads[1])
w1.assign_sub = (lr *grads[0] / tf.sqrt(v_w))
b1.ssign_sub = (lr *grads[1] /tf.sqrt(v_b))
(4)RMSProp

在SGD的基础上增加二阶动量,二阶动量vt使用指数滑动平均值计算,表征过去一段时间的平均值

image-20230502105046419

一阶动量是当前时刻梯度

1
2
3
4
5
6
7
v_w,v_b = 0,0
beta = 0.9

v_w = beta * v_w + (1-beta) * tf.square(grads[0])
v_b = beta * v_b + (1-beta) * tf.square(grads[1])
w1.assign_sub = (lr *grads[0] / tf.sqrt(v_w))
b1.assign_sub = (lr *grads[1]/ tf.sqrt(v_b))
(5)Adam

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

image-20230502105816067

四、使用八股搭建神经网络

1.使用Tensorflow API : tf.keras搭建网络八股

image-20230502110121291

(1)tf.keras.models.Sequential()
1
tf.keras.models.Sequential([网络结构])   # 描述各层网络
拉直层

image-20230502110427660

全连接层

image-20230502110453675

卷积层

image-20230502110506668

LSTM层

image-20230502110522107

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

image-20230502110921320

from_logits询问是否是原始输出,若神经网络的预测结果经过了softmax概率分布则填写False

image-20230502110938173

image-20230502111246499

(3)model.fit()
1
2
3
4
5
model.fit(训练集的属土特征,训练集标签,batch_size= , epochs = ,
validation_data = (测试集的输入特征,测试集的标签),
validation_split=从训练集划分多少比例给测试集,
validation_freq = 多少次epoch测试一次
)
(4)model.summary()
1
model.summary() # 打印出网络的结构

2.使用tf.keras复现鸢尾花分类

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
"""
使用tf.keras实现鸢尾花分类
"""
import numpy as np
import tensorflow as tf
from sklearn import datasets


# 读入鸢尾花数据
x_train = datasets.load_iris().data
y_train = datasets.load_iris().target

# 数据集乱序
np.random.seed(116) # 随机种子一致导致后文 标签与对应样本的特征乱序顺序一致
np.random.shuffle(x_train)
np.random.seed(116)
np.random.shuffle(y_train)
tf.random.set_seed(116)

# 搭建模型(三分类--单层网络)
model = tf.keras.models.Sequential([
tf.keras.layers.Dense(3,activation='softmax',kernel_regularizer=tf.keras.regularizers.l2())
])

# 编译
model.compile(optimizer=tf.keras.optimizers.SGD(learning_rate=0.1),
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy'])

# 训练拟合(validation_split=0.2 从训练集划分20%比例给测试集,validation_freq=20 20次epoch测试一次)
model.fit(x_train,y_train,batch_size=32,epochs=500,validation_split=0.2,validation_freq=20)

model.summary()

运行:

image-20230502113808342

3.自定义class 类搭建网络结构

Sequential搭建出上层输出就是下层输入的顺序网络结构

但是无法写出带有跳连的非顺序网络结构(此时类class可以)—类似于pytorch定义自己的网络结构

image-20230502114506383

示例;

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
"""
使用class实现鸢尾花分类
"""
import numpy as np
import tensorflow as tf
from sklearn import datasets



# 读入鸢尾花数据
x_train = datasets.load_iris().data
y_train = datasets.load_iris().target

# 数据集乱序
np.random.seed(116) # 随机种子一致导致后文 标签与对应样本的特征乱序顺序一致
np.random.shuffle(x_train)
np.random.seed(116)
np.random.shuffle(y_train)
tf.random.set_seed(116)

# 自定义类class搭建模型(三分类--单层网络)
class IrisModel(tf.keras.Model):
def __init__(self):
super(IrisModel,self).__init__()
self.d1 = tf.keras.layers.Dense(3,activation='softmax',kernel_regularizer=tf.keras.regularizers.l2())

def call(self,x):
y = self.d1(x)
return y

model = IrisModel()


# 编译
model.compile(optimizer=tf.keras.optimizers.SGD(learning_rate=0.1),
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy'])

# 训练拟合(validation_split=0.2 从训练集划分20%比例给测试集,validation_freq=20 20次epoch测试一次)
model.fit(x_train,y_train,batch_size=32,epochs=500,validation_split=0.2,validation_freq=20)

model.summary()

4.MNIST数据集

image-20230502121024663

image-20230502121037702

image-20230502121050845

可视化:

image-20230502121213615

image-20230502121233830

(1)手写数字识别(Sequential())
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
"""
手写识别MNIST
"""
import numpy as np
import tensorflow as tf
from sklearn import datasets



# 读入MNIST数据
mnist = tf.keras.datasets.mnist
(x_train,y_train),(x_test,y_test) = mnist.load_data()
# 将特征皈依化到0-1 之间 加快模型收敛
x_train,x_test = x_train / 255.0 , x_test / 255.0


model = tf.keras.models.Sequential([
# 将输入特征展平 28 * 28
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128,activation='relu'),
# 输出 10 分类
tf.keras.layers.Dense(10,activation='softmax'),
])

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

# 训练拟合(validation_split=0.2 从训练集划分20%比例给测试集,validation_freq=20 20次epoch测试一次)
model.fit(x_train,y_train,batch_size=32,epochs=5,validation_data=(x_test,y_test),validation_freq=1)

model.summary()

运行:

image-20230502122241785

(2)手写数字识别(自定义类class)
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
"""
手写识别MNIST---自定义类class
"""
import numpy as np
import tensorflow as tf
from sklearn import datasets



# 读入MNIST数据
mnist = tf.keras.datasets.mnist
(x_train,y_train),(x_test,y_test) = mnist.load_data()
# 将特征皈依化到0-1 之间 加快模型收敛
x_train,x_test = x_train / 255.0 , x_test / 255.0

# 搭建模型
class MnistModel(tf.keras.Model):
def __init__(self):
super(MnistModel,self).__init__()
self.flatten = tf.keras.layers.Flatten(),
self.d1 = tf.keras.layers.Dense(128,activation='relu')
self.d2 = tf.keras.layers.Dense(10,activation='softmax')

def call(self,x):
x = self.flatten(x)
x = self.d1(x)
y = self.d2(x)
return y

model = MnistModel()

model = tf.keras.models.Sequential([
# 将输入特征展平 28 * 28
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128,activation='relu'),
# 输出 10 分类
tf.keras.layers.Dense(10,activation='softmax'),
])

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

# 训练拟合(validation_split=0.2 从训练集划分20%比例给测试集,validation_freq=20 20次epoch测试一次)
model.fit(x_train,y_train,batch_size=32,epochs=5,validation_data=(x_test,y_test),validation_freq=1)

model.summary()

5.FASHION数据集

image-20230502122907656

五、八股进阶

image-20230502123140865

1.自制数据集

数据结构如下:(图片名 最后的数字即为其标签)

image-20230502204829177

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
"""
自制数据集

"""
import tensorflow as tf
from PIL import Image
import numpy as np
import os

train_path = "./data/mnist_image_label/mnist_train_jpg_60000/"
train_txt = "./data/mnist_image_label/mnist_train_jpg_60000.txt"
x_train_savepath = "./data/mnist_image_label/mnist_x_train.npy"
y_train_savepath = "./data/mnist_image_label/mnist_y_train.npy"

test_path = "./data/mnist_image_label/mnist_test_jpg_10000/"
test_txt = "./data/mnist_image_label/mnist_train_jpg_10000.txt"
x_test_savepath = "./data/mnist_image_label/mnist_x_test.npy"
y_test_savepath = "./data/mnist_image_label/mnist_y_test.npy"


def generate(path,txt):
f = open(txt,'r')
contents = f.readlines() # 读取txt文件中所有行
f.close()
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)
y_.append(value[1])
print('loading : ' + content)


x = np.array(x) # 变为np.array形式
y_ = np.array(y_)

y_ = y_.astype(np.int64) # 变为64位整型
return x,y_


"""---数据集---"""
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 = generate(train_path,train_txt)
x_test,y_test = generate(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)


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

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

# 训练拟合(validation_split=0.2 从训练集划分20%比例给测试集,validation_freq=20 20次epoch测试一次)
model.fit(x_train,y_train,batch_size=32,epochs=5,validation_data=(x_test,y_test),validation_freq=1)

model.summary()

2.数据增强

image-20230502213605177

image-20230502213622414

(1)注意
1
image_gen_teain.fit(x_train)

fit()函数需要参数为4D参数,因此需要对数据进行reshape()

image-20230502213949787

6000为样本数量,28*28为样本尺寸大小,1为通道数

image-20230502214156936

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
import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator

# 加载mnist 数据
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
x_train = x_train.reshape(x_train.shape[0], 28, 28, 1) # 给数据增加一个维度,从(60000, 28, 28)reshape为(60000, 28, 28, 1)

# 数据增强
image_gen_train = ImageDataGenerator(
rescale=1. / 1., # 如为图像,分母为255时,可归至01
rotation_range=45, # 随机45度旋转
width_shift_range=.15, # 宽度偏移
height_shift_range=.15, # 高度偏移
horizontal_flip=False, # 水平翻转
zoom_range=0.5 # 将图像随机缩放阈量50
)
image_gen_train.fit(x_train)

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

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

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

3.读取保存模型

(1)读取模型
1
load_weights(路径文件名)
1
2
3
4
5
checkpoint_save_path = "./mnist.ckpt"
# 生成ckpt文件时,会同步生成索引表'.index'
if os.path.exists(checkpoint_save_path + '.index')
print('-------load the model--------')
model.load_weights(checkpoint_save_path)
(2)保存模型

image-20230502215429327

save_weights_only为是否只保留模型参数、save_best_only为是否只保留最优结果

执行训练时加入cp_callback选项记录至history中

image-20230502215806987

示例:

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
import tensorflow as tf
import os

mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

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

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

# 读取模型
checkpoint_save_path = "./checkpoint/mnist.ckpt"
if os.path.exists(checkpoint_save_path + '.index'):
print('-------------load the model-----------------')
model.load_weights(checkpoint_save_path)

# 创建callback选项
cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,
save_weights_only=True,
save_best_only=True)

# 执行训练时加入cp_callback选项记录至history中
history = model.fit(x_train, y_train, batch_size=32, epochs=5, validation_data=(x_test, y_test), validation_freq=1,
callbacks=[cp_callback])
model.summary()

4.参数提取读入文本

1
model.trainable_varibles   #  返回模型中可训练的参数

设置print输出格式

1
2
3
4
np.set_printoptions(threshold = 超过多少省略显示)

# 示例 (这样可以将参数全部显示打印出来,而不会出现省略号)
np.set_printoptions(threshold = np.inf) # np.inf 表示无限大

将参数写文本

1
2
3
4
5
6
7
prinr(model.trainable_varibles)
file = open('./weights.txt','w')
for v in model.trainable_varibles:
file.write(str(v.name) + '\n')
file.write(str(v.shape) + '\n')
file.write(str(v.numpy()) + '\n')
file.close()

示例:

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
"""
参数提取读入文本
"""
import tensorflow as tf
import os
import numpy as np

# 设置参数打印方式 (threshold=np.inf 不出现省略全部打印出来)
np.set_printoptions(threshold=np.inf)

# 读取数据集
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

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

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


# 检查是否存在模型训练参数保存的ckpt文件
checkpoint_save_path = "./checkpoint/mnist.ckpt"
if os.path.exists(checkpoint_save_path + '.index'):
print('-------------load the model-----------------')
# 若存在则直接加载权重文件
model.load_weights(checkpoint_save_path)

# 若不存在模型训练参数的权重文件---创建回调
cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,
save_weights_only=True,
save_best_only=True)

# 训练并且将模型训练参数权重文件进行保存
history = model.fit(x_train, y_train, batch_size=32, epochs=5, validation_data=(x_test, y_test), validation_freq=1,
callbacks=[cp_callback])
model.summary()

# 打印训练权重文件的参数
print(model.trainable_variables)

# 将参数提取写入txt文件中
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()

5.acc 以及 loss 的可视化

image-20230502224907025

在训练model.fit()的过程中同步记录了以下信息:

image-20230502224952084

可用以下代码进行提取:

image-20230502225038439

可视化代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
###############################################    show   ###############################################
from matplotlib import pyplot as plt


# 显示训练集和验证集的acc和loss曲线
acc = history.history['sparse_categorical_accuracy']
val_acc = history.history['val_sparse_categorical_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']

plt.subplot(1, 2, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.title('Training and Validation Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.title('Training and Validation Loss')
plt.legend()
plt.show()

运行:

image-20230502225230915

6.使用训练后的模型进行预测

输入一张手写数字图片 —-> 输出识别结果

前向传播执行应用

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

下面是预测过程:(复现模型—> 加载参数—> 预测结果)

image-20230502225644100

但是输入的数据需要满足训练的神经网络对于输入数据的要求

六、CNN

卷积神经网络的主要模块

image-20230504104500795

卷积就是特征提取器:CBAPD

image-20230504104556710

1.感受野

卷积神经网络各输出特征图中的每个像素点,在原始输入图片上映射区域的大小

image-20230503093858057

上图中1是原始输入数据,为5x51通过一个3x3的卷积核变为2,则2的感受野是3.

在对2经过一个3x3的卷积核变为3,则3的感受野是5(原始输入数据为1

4的感受野也是5

2.卷积层

image-20230504100730776

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
import numpy as np
import tensorflow as tf

model = tf.keras.models.Sequential([
tf.keras.layers.Conv2D(6,5,padding='valid',activation='sigmoid'),
tf.keras.layers.MaxPool2D(2,2),
tf.keras.layers.Conv2D(6,(5,5),padding='valid',activation='sigmoid'),
tf.keras.layers.MaxPool2D(2,(2,2)),
tf.keras.layers.Conv2D(filters=6,kernel_size=(5,5),padding='valid',activation='sigmoid'),
tf.keras.layers.MaxPool2D(pool_size=(2,2),strides=2),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(10,activation='softmax')
])

3.批量标准化(BN)

神经网络对于0附近的数据更加敏感,但是随着网络层数的增加,特征数据会出现偏离0均值的情况

标准化:使得数据符合0均值,1为标准差的分布(将偏移的数据重新拉回到0附近)

批标准化:对一个batch数据,做标准化处理

image-20230504101708450

image-20230504101739435

批标准化操作会让每个像素点进行减均值除以标准差的自更新计算

image-20230504101846539

image-20230504102055604

在反向传播时,缩放因子,与偏移因子会与其他待训练的参数一同被训练优化,使得标准正太分布后的特征数据,通过缩放因子与偏移因子优化了投入特征数据的宽窄与偏移量,保证了网络的非线性表达力

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

image-20230504102525166

(1)BN操作函数
1
tf.keras.layers.BatchNormalization(),   # BN层操作

示例:

1
2
3
4
5
6
7
8
9
10
import numpy as np
import tensorflow as tf

model = tf.keras.models.Sequential([
tf.keras.layers.Conv2D(6,5,padding='valid',activation='sigmoid'),
tf.keras.layers.BatchNormalization(), # BN层操作
tf.keras.layers.Activation('relu'), # 激活层
tf.keras.layers.MaxPool2D(2,2),
tf.keras.layers.Dropout(0.2), # Dropout()层
])

4.池化操作

池化用于减少特征的数据量

最大池化:可以提取图片纹理

均值池化:可以保留背景特征

(1)池化函数

image-20230504103811461

示例:

1
2
3
4
5
6
7
8
9
10
import numpy as np
import tensorflow as tf

model = tf.keras.models.Sequential([
tf.keras.layers.Conv2D(6,5,padding='valid',activation='sigmoid'),
tf.keras.layers.BatchNormalization(), # BN层操作
tf.keras.layers.Activation('relu'), # 激活层
tf.keras.layers.MaxPool2D(pool_size=(2,2),strides=2,padding='same'), # 池化
tf.keras.layers.Dropout(0.2), # Dropout()层
])

5.舍弃

舍弃(Dropout())是一种正则化操作,也是为了防止神经网络过拟合

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

(1)Dropout函数
1
tf.keras.layers.Dropout(舍弃的概率)         # Dropout()层

示例:

1
2
3
4
5
6
7
model = tf.keras.models.Sequential([
tf.keras.layers.Conv2D(6,5,padding='valid',activation='sigmoid'),
tf.keras.layers.BatchNormalization(), # BN层操作
tf.keras.layers.Activation('relu'), # 激活层
tf.keras.layers.MaxPool2D(pool_size=(2,2),strides=2,padding='same'), # 池化
tf.keras.layers.Dropout(0.2), # Dropout()层
])

七、CIFAR10数据集

image-20230504104838723

image-20230504104901853

1.导入数据集

1
2
3
4
import tensorflow as tf

cifar10 = tf.keras.datasets.cifar10
(x_train,y_train),(x_test,y_test) = cifar10.load_data()
(1)可视化数据集

image-20230504105141527

下载的数据集到下面目录(ubuntu下/home/用户名/.kera.kera是隐藏用户名

image-20230504111637223

2.使用卷积神经网络训练CIFAR10数据集

卷积模型如下:(卷积就是CBAPD—卷积–批量归一化—激活—池化—舍弃)

image-20230504105534244

image-20230504105722853

示例:

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
92
93
94
95
96
97
"""
搭建卷积神经网络训练 CIFAR10
"""
import tensorflow as tf
import os
import numpy as np
from matplotlib import pyplot as plt


np.set_printoptions(threshold=np.inf)

cifar10 = tf.keras.datasets.cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

# 搭建网络模型
class Baseline(tf.keras.Model):
def __init__(self):
super(Baseline, self).__init__()
self.c1 = tf.keras.layers.Conv2D(filters=6, kernel_size=(5, 5), padding='same') # C卷积层
self.b1 = tf.keras.layers.BatchNormalization() # B BN层
self.a1 = tf.keras.layers.Activation('relu') # A 激活层
self.p1 = tf.keras.layers.MaxPool2D(pool_size=(2, 2), strides=2, padding='same') # P 池化层
self.d1 = tf.keras.layers.Dropout(0.2) # D dropout层

self.flatten = tf.keras.layers.Flatten() # 展平层
self.f1 = tf.keras.layers.Dense(128, activation='relu') # 线性层
self.d2 = tf.keras.layers.Dropout(0.2)
self.f2 = tf.keras.layers.Dense(10, activation='softmax') # 最后10分类,经过softmax变为概率分布

def call(self, x):
x = self.c1(x)
x = self.b1(x)
x = self.a1(x)
x = self.p1(x)
x = self.d1(x)

x = self.flatten(x)
x = self.f1(x)
x = self.d2(x)
y = self.f2(x)
return y

# 创建模型示例
model = Baseline()

# 编译
model.compile(optimizer='adam',
# 损失函数 传入的非原始数据 from_logits=False (经过了softmax)
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy'])

# 加载模型权重参数
checkpoint_save_path = "./checkpoint/Baseline.ckpt"
if os.path.exists(checkpoint_save_path + '.index'):
print('-------------load the model-----------------')
model.load_weights(checkpoint_save_path)

# 创建回调(用于训练时,保存模型权重参数)
cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,
save_weights_only=True,
save_best_only=True)

# 训练
history = model.fit(x_train, y_train, batch_size=32, epochs=5, validation_data=(x_test, y_test), validation_freq=1,
callbacks=[cp_callback])
model.summary()

# 读取模型训练的权重参数,并进行保存至文本
# 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()

############################################### show ###############################################

# 显示训练集和验证集的acc和loss曲线
acc = history.history['sparse_categorical_accuracy']
val_acc = history.history['val_sparse_categorical_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']

plt.subplot(1, 2, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.title('Training and Validation Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.title('Training and Validation Loss')
plt.legend()
plt.show()