Tensorflow2.0建立神经网络

例一:我们有一个损失函数是(w+1)²,我们要找一个合适的w取值,让损失函数的值最小

import tensorflow as tf

w = tf.Variable(tf.constant(5, dtype=tf.float32))   #表明了哪个变量要进行优化
lr = 0.2  #梯度下降法中用到的α
epoch = 40  #表明要循环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))

一:基本概念和常见函数

(1)基本概念

  1. 张量:Tensor表示张量,是多维数组或者多维列表,用阶表示张量的维数。0阶张量叫做标量,1阶张量叫做向量,二阶张量叫做矩阵。
张量的创建
import tensorflow as tf
(1) a=tf.constant([1,5],dtype=tf.int64)  #这个张量是一阶张量,元素时152)将numpy格式转化为tensor格式
a=np.arange(0,5)
b=tf.convert_to_tensor(a,dtype=tf.int64)
 (3)采用不同函数创建不同值的张量
 tf.zeros([2.3])
 tf.ones(4)
 tf.fill([2,2],9)
 (4)采用不同函数创建符合不同分布的张量。
 tf.random.normal(维度,mean=均值,stddev=标准差)生成正态分布的随机数,默认均值为0,标准差为1.
 tf.random.truncated([2,2],mean=0.5,stddev=1) 生成截断式正态分布的随机数,能使生成的随机数更集中一些
 tf.random.uniform([2.2],minval=0,maxval=1)  生成指定维度的均匀分布随机数,前闭后开区间

(2)常用函数

1.tf.cast(x1,tf.int32)   强制将张量x1的数据类型转换为tf.int32

2.tf.reduce_mean(张量名,axis=操作轴) 计算张量沿着指定维度的平均值  经纬度--0为经度代表纵轴,1为维度代表横轴
  tf.reduce_sum(张量名,axis=操作轴) 不指明axis的话就是对所有的元素进行操作
  
3.w=tf.Variable(tf.random.normal([2,2],mean=0,stddev=1)) 表明先随机生成正态分布随机数,再将生成的随机数标记为课训练,那么在反向传播中就可以通过梯度下降更新参数w了

4.利用Tensorflow函数对张量进行四则运算(只有维度相同的张量才可以一起进行运算)
tf.add(a,b)
tf.subtract(a,b)
tf.multiply(a,b)
tf.divide(b,a)

5.利用Tensorflow函数对张量进行幂次运算
tf.pow(a,3) 计算张量的3次方
tf.square(a) 计算张量a的平方
tf.sqrt(a) 计算张量a的开方

6.利用函数实现两个矩阵的相乘
tf.matmul(a,b)

7.可利用tf.data.Dataset.from_tensor_slices((输入特征,标签)),切分传入张量的第一维度,生成输入特征/标签对,构建数据集,此函数对Tensor格式和numpy格式均适用,。
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)  #可以发现一个输入特征和对应的一个标签成一队了

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)  #求出损失函数对于w的梯度
print(grad)
  
9.利用enumerate(列表名)函数枚举出每一个元素,并在元素前配上对应的索引号
seq=['one','two','three']
for i,element in enumerate(seq):
   print(i,element)

10.可以用tf.one_hot(待转换数据,depth=几分类)函数实现用独热码表示标签。
classes=3
labels=tf.constant([1,0,2])
output=tf.one_hot(labels,depth=classes)  #将labels数据看做是表示的3类(102每一个数代表一类)1可能是代表红色,0是白色,2是黑色都有可能,我们将分类转换成向量[1,0,0],[0,1,0],[0,0,1]表示3类数据
print(output)  #此时输出是tf.Tensor([[0,1,0],[1,0,0],[0,0,1]])

11.可利用tf.nn.softmax()函数使前向传播的输出值符合概率分布,进而与独热码形式的标签作比较。比如说我们得到了前向传播的输出值 1.012.01-0.66.我们现在要通过softmax函数对这些数进行处理,使得这些数能够表示概率概率,而这些数加起来正好为1
y=tf.constant([1.01,2.01,-0.66])
y_pro=tf.nn.softmax(y)
print("After softmax,y_pro is",y_pro)

12.可利用assign_sub函数对参数实现自更新。使用此函数前需要利用tf.Variable定义变量w为可训练(可自更新)
w=tf.Variable(4)
w.assign_sub(1)
print(w) #会实现参数w自减1

13.可以利用tf.argmax(张量名,axis=操作轴)返回张量沿指定维度最大值的索引
import numpy as np
test=np.array([[1,2,3],[2,3,4],[5,4,3],[8,7,2]])
print(tf.argmax(test,axis=0)) #返回每一列最大值的索引
print(tf.argmax(test,axis=1)) #返回每一行最大值的索引


14.tf.where(条件语句,A,B) 条件语句真返回A,假返回B
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对应位置的元素

15. np.random.RandomState.rand(维度) 
会返回一个【01)之间的随机数
rdm=np.random.RandomState(seed=1)
a=rdm.rand()
b=rdm.rand(2,3) #这是一个两行三列的随机数矩阵

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

17.np.mgrid[起始值:结束值:步长,起始值:结束值:步长]
x.ravel() 将x变为一维数组
np.c_[数组1,数组2...] 使返回的间隔数值点配对
import numpy as np
x,y=np.mgrid[1:3:1,2:4:0.5]
grid=np.c_[x.ravel(),y.ravel()]
print("x:",x)
print("y:",y)
print("grid:\n",grid)  #通过这些函数可以完成网格点的设置

二:一些需要注意的地方
1.学习率:学习率是应用在梯度下降法中的一个超参数,设置的太大会引起震荡,太小会让优化进行的缓慢。这里我们有一个指数衰减学习率。可以先用较大的学习率来加快学习的速度,之后减少学习率,使模型在训练后期得以稳定。

指数衰减学习率=初始学习率*学习率衰减率的n次方            n=(当前轮数/多少轮衰减一次)
epoch=40
lr_base=0.2
lr_decay=0.99
lr_step=1

for epoch in range(epoch):
   lr=lr_base*lr_decay**(epoch/lr_step)
   with tf.GradientTape as tape:
      loss=tf.square(w+1)
   grads=tape.gradient(loss,w)
   w.assign_sub(lr*grads)
   print("After %s epoch,w is %f,loss is %f, lr is %f"%(epoch,w.numpy(),loss,lr))
  1. 激活函数 一般用relu函数即可
  2. 损失函数:可以使用均方误差(MSE),也可以是自定义损失函数,也可以用交叉熵损失函数来表示两个概率分布之间的距离。
  3. 欠拟合与过拟合
    欠拟合就是训练的模型准确率不过关就是说在训练集中表现得不好,过拟合就是在训练集中表现得很好,但是在测试集上不行
  4. 优化器
    就是各个梯度下降函数及相应的变种。

三:程序实现咎尾花数据集分类

神经网络实现花的分类只需要三步
1.准备数据:包括数据集读入,数据集乱序,把测试集和训练集中的数据配成输入特征和标签对,生成train和test不重合的训练集和测试集
2.搭建网络:定义神经网络中的所有可训练参数
3.优化这些可训练的参数,利用嵌套循环在with结构中求得损失函数loss对每个可训练参数的偏导数,并更新这些可训练参数,为了查看效果,程序可以加入每遍历一遍数据集就显示当前准确率,还可以画出准确率acc和损失函数loss的变化曲线图

我们有150组数据,每组包括花的花萼长,花萼宽,花瓣长,花瓣宽四个输入特征,同时给出了这一组特征对应的花的类别。类别分别用0,1,2表示。

建立神经网络并运行的各个步骤代码如下

# 导入所需模块
import tensorflow as tf
from sklearn import datasets
from matplotlib import pyplot as plt
import numpy as np

# 导入数据,分别为输入特征和标签
x_data = datasets.load_iris().data
y_data = datasets.load_iris().target

# 随机打乱数据(因为原始数据是顺序的,顺序不打乱会影响准确率)
# seed: 随机数种子,是一个整数,当设置之后,每次生成的随机数都一样(为方便教学,以保每位同学结果一致)
np.random.seed(116)  # 使用相同的seed,保证输入特征和标签一一对应
np.random.shuffle(x_data)
np.random.seed(116)
np.random.shuffle(y_data)
tf.random.set_seed(116)

# 将打乱后的数据集分割为训练集和测试集,训练集为前120行,测试集为后30行
x_train = x_data[:-30]
y_train = y_data[:-30]
x_test = x_data[-30:]
y_test = y_data[-30:]

# 转换x的数据类型,否则后面矩阵相乘时会因数据类型不一致报错
x_train = tf.cast(x_train, tf.float32)
x_test = tf.cast(x_test, tf.float32)

# from_tensor_slices函数使输入特征和标签值一一对应。(把数据集分批次,每个批次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)

# 生成神经网络的参数,4个输入特征故,输入层为4个输入节点;因为3分类,故输出层为3个神经元
# 用tf.Variable()标记参数可训练
# 使用seed使每次生成的随机数相同(方便教学,使大家结果都一致,在现实使用时不写seed)
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  # 学习率为0.1
train_loss_results = []  # 将每轮的loss记录在此列表中,为后续画loss曲线提供数据
test_acc = []  # 将每轮的acc记录在此列表中,为后续画acc曲线提供数据
epoch = 500  # 循环500轮
loss_all = 0  # 每轮分4个step,loss_all记录四个step生成的4个loss的和

# 训练部分
for epoch in range(epoch):  #数据集级别的循环,每个epoch循环一次数据集
    for step, (x_train, y_train) in enumerate(train_db):  #batch级别的循环 ,每个step循环一个batch
        with tf.GradientTape() as tape:  # with结构记录梯度信息
            y = tf.matmul(x_train, w1) + b1  # 神经网络乘加运算
            y = tf.nn.softmax(y)  # 使输出y符合概率分布(此操作后与独热码同量级,可相减求loss)
            y_ = tf.one_hot(y_train, depth=3)  # 将标签值转换为独热码格式,方便计算loss和accuracy
            loss = tf.reduce_mean(tf.square(y_ - y))  # 采用均方误差损失函数mse = mean(sum(y-out)^2)
            loss_all += loss.numpy()  # 将每个step计算出的loss累加,为后续求loss平均值提供数据,这样计算的loss更准确
        # 计算loss对各个参数的梯度
        grads = tape.gradient(loss, [w1, b1])
        # 实现梯度更新 w1 = w1 - lr * w1_grad    b = b - lr * b_grad
        w1.assign_sub(lr * grads[0])  # 参数w1自更新
        b1.assign_sub(lr * grads[1])  # 参数b自更新
        
    # 每个epoch,打印loss信息
    print("Epoch {}, loss: {}".format(epoch, loss_all/4))
    train_loss_results.append(loss_all / 4)  # 将4个step的loss求平均记录在此变量中
    loss_all = 0  # loss_all归零,为记录下一个epoch的loss做准备

        # 测试部分
    # total_correct为预测对的样本个数, total_number为测试的总样本数,将这两个变量都初始化为0
    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)
        pred = tf.argmax(y, axis=1)  # 返回y中最大值的索引,即预测的分类
        # 将pred转换为y_test的数据类型
        pred = tf.cast(pred, dtype=y_test.dtype)
        # 若分类正确,则correct=1,否则为0,将bool型的结果转换为int型
        correct = tf.cast(tf.equal(pred, y_test), dtype=tf.int32)
        # 将每个batch的correct数加起来
        correct = tf.reduce_sum(correct)
        # 将所有batch中的correct数加起来
        total_correct += int(correct)
        # total_number为测试的总样本数,也就是x_test的行数,shape[0]返回变量的行数
        total_number += x_test.shape[0]
    # 总的准确率等于total_correct/total_number
    acc = total_correct / total_number
    test_acc.append(acc)
    print("Test_acc:", acc)
    print("--------------------------")

# 绘制 loss 曲线
plt.title('Loss Function Curve')  # 图片标题
plt.xlabel('Epoch')  # x轴变量名称
plt.ylabel('Loss')  # y轴变量名称
plt.plot(train_loss_results, label="$Loss$")  # 逐点画出trian_loss_results值并连线,连线图标是Loss
plt.legend()  # 画出曲线图标
plt.show()  # 画出图像

# 绘制 Accuracy 曲线
plt.title('Acc Curve')  # 图片标题
plt.xlabel('Epoch')  # x轴变量名称
plt.ylabel('Acc')  # y轴变量名称
plt.plot(test_acc, label="$Accuracy$")  # 逐点画出test_acc值并连线,连线图标是Accuracy
plt.legend()
plt.show()

补充:如果花的数据集在本地文件中,我们还需要从本地中读取数据集的txt文件,并将其输入到神经网络进行训练
txt文件如下

"Sepal.Length""Sepal.Width""Petal.Length""Petal.Width""Species"
"1"5.1 3.5 1.4 0.2 "setosa"
"2"4.9 3 1.4 0.2 "setosa"

1.通过pandas函数读取,并处理成神经网络需要的数据结构
df=pd.read_csv('iris.txt',header=None,sep=',')
data=df.values
x_data=[lines[0:4]for lines in data]  #取输入特征
x_data=np.array(x_data,float)  #转换为numpy格式
y_data=[lines[4]for lines in data]  #取标签
for i in range(len(y_data)):
    if y_data[i]=='iris-setosa':
       y_data[i]=0
    elif y_data[i]=='iris-versicolor':
       y_data[i]=1
    ......
y_data=np.array(y_data)

2.通过open函数打开txt文件,并处理成神经网络需要的数据结构
f=open("iris.txt","r")
contents=f.readlines()
i=0
for content in contents: 
    temp=content.split(',')
    x_data[i]=np.array([temp[0:4]],dtype=float)
    if temp[4]=="Iris-setosa\n":
      y_data[i]=0
    elif temp[4]="Iris-versicolor\n":
      y_data[i]=1
    .....
    i=i+1
     

四:用tf.keras搭建网络八股
3.1 搭建网络的步骤如下
(1)import 相应的模块
(2)指定输入网络的训练集和测试集
(3)逐层搭建网络结构 mdoel=tf.keras.models.Sequential()
(4)在 model.compile()中配置训练方法,选择训练时使用的优化器,损失函数和最终评价指标
(5)在model.fit()中执行训练过程,告知训练集和测试集中的输入值和标签
(6)使用model.summary()打印网络结构,统计参数数目

3.2 涉及到的函数

1.tf.keras.models.Sequential
Sequential函数是一个容器,描述了神经网络的网络结构,在Sequential函数中的输入参数中描述从输入层到输出层中的网络结构
tf.keras.layers.Dense(神经元个数,activation="激活函数",kernel_regularizer="正则化方式")
activation可选relu,softmax,sigmoid,tanh等; kernel_regularizer可选 tf.keras.regularizers.l1()   tf.keras.regularizers.l2()

2.compile用来配置神经网络的训练方法,告知训练时使用的优化器,损失函数和准侧率评测标准
Model.compile(optimizer=优化器,loss=损失函数,metrics=["准确率"])

optimizer来设置优化器时可以用字符串来设定,也可以用函数形式来设定。用字符串的话一些超参数只能用默认设置,用函数形式的话可以自己手动设置
‘sgd’    tf.optimizers.SGD(lr=学习率,decay=学习率衰减率,momentum=动量参数)
‘adagrad’   tf.keras.optimizers.Adagrad(lr=学习率,decay=学习率衰减率)
‘adadelta’  tf.keras.optimizers.Adadelta(lr=学习率,decay=学习率衰减率)
‘adam’  tf.keras.optimizers.Adam(lr=学习率,decay=学习率衰减率)

loss可以是字符串也可以是函数形式
可选项包括如下:
‘mse’  or tf.keras.losses.MeanSquaredError()
‘sparse_categorical_crossentropy’ or tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False)
from_logits参数是说明是否要直接输出结果,False的话就要用softmax等函数将输出转化为概率分布的形式。

metrics 标注网格评测指标
可选项包括:
‘accrracy’:那么y_和y都是数值    y是指我们经过神经网络的输出,y_是指我们的标签数值
'categorical_accuracy'  :y_和y都是以独热码和概率分布表示
‘sparse_categorical_accuracy’:y_是以数值形式给出,y是以独热码形式给出


3.model.fit(训练集的输入特征,训练集的标签,batch_size,epochs,validation_data=(测试集的输入特征,测试集的标签),validation_split=从测试集中划分多少比例给训练集,validation_freq=测试的epoch间隔次数)
fit函数用来执行训练过程

4.model.summary()
summary函数用来打印网络结构和参数统计

3.3 用keras来实现之前的花的分类

第一步:import相关模块
import tensorflow as tf
from sklearn import datasets
import numpy as np

第二步:指定输入网络的训练集和测试集
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.12())])
#此时使用了单层全连接网络,第一个参数表示神经元个数,第二个参数表示网络所使用的激活函数,第三个参数表示选用的正则化方法
表示网络结构还可以用class来声明
class MyModel(Model):  #括号里的Model表示创建的类要继承TensorFlow库中的Model类。 
    def__init__(self):   #__init__函数定义所需的网络结构块
      super(MyModel,self).__init__()   #表示初始化父类的参数。之后就可以初始化网络结构,搭建出神经网络所需的各种网络结构块
      定义网络结构块
    def call(self,x):  #写出前向传播
      y=self.d1(x)   #call函数中调用__init__函数完成初始化的网络块,调用网络结构块实现前向传播
      return y
model=MyModel()
以上是class声明网络结构的基本格式,下面我们用class来声明识别花的网络的结构
class IrisModel(Model):
   def __init__(self):
      super(IrisModel,self).__init__()
      self.d1=Dense(3,activation='sigmoid',kernel_regularizer=tf.keras.regularizers.l2())  #d1是给这一层起的名字
   def call(self,x):
      y=self.d1(x)   
      return y
之后只要使用Model=MyModel()构建类的对象,就可以使用该模型了。


第四步:在model.compile()中配置训练方法
model.compile(optimizer=tf.keras.optimizers.SGD(lr=0.1),loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False,metrics=['sparse_categorical_accuracy']))
#使用了SGD优化器,学习率设置为0.1,选择SparseCategoricalCrossentrop作为损失函数。由于神经网络是用来softmax激活函数,使得输出是概率分布而不是原始输出,所以将from_logits参数设置为False。因为花的数据集的标签是012这样的数值,而网络前向传播的输出为概率分布,所以metrics需要设置为sparse_categorical_accuracy

第五步:model.fit()执行训练过程
model.fit(x_train,y_train,batch_size=32,epochs=500,validation_split=0.2,validation_freq=20)
#在fit中执行训练过程,validation_split表示数据集中测试集的划分比例,validation_freq表示每迭代20次在测试集中测试一次准确率。

第六步:使用model.summary()打印网络结构,统计参数数目
model.summary()

完整代码如下

import tensorflow as tf
from tensorflow.keras.layers import Dense
from tensorflow.keras import Model
from sklearn import datasets
import numpy as np

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 IrisModel(Model):
    def __init__(self):
        super(IrisModel,self).__init__()
        self.d1=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(lr=0.1),
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
              metrics=['sparse_categorical_accuracy'])

model.fit(x_train,y_train,batch_size=32,epochs=500,validation_split=0.2,validation_freq=20)
model.summary()

为了掌握六步法建立并训练神经网络,我们引入了两个数据集,运用神经网络去模拟数据
例一:MNIST数据集
这个数据集一共有7万张图片,28*28的0到9手写数字数据集,其中6万张用来训练,1万张用来测试。
我们建立一个神经网络用来识别图片并判断出来图片上的数字是啥

#1.导入模块
import tensorflow as tf

#2.指定训练集和测试集
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

#3.搭建网络结构
model = tf.keras.models.Sequential([
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(128, activation='relu'),
    tf.keras.layers.Dense(10, activation='softmax')
])

#4.配置训练方法
model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
              metrics=['sparse_categorical_accuracy'])
#5.执行训练
model.fit(x_train, y_train, batch_size=32, epochs=5, validation_data=(x_test, y_test), validation_freq=1)

#6.打印网络结构,统计参数数量
model.summary()

例二:Fashion_mnist数据集
包括6万张训练图片和1万张测试图片,图片被分为10类,每张图像为28*28的分辨率。(1代表T恤,2代表裤子等)

1.导入库
import tensorflow as tf
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras import Model

2.指定训练集和测试集
fashion = tf.keras.datasets.fashion_mnist
(x_train, y_train),(x_test, y_test) = fashion.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

3.设计网络结构
class MnistModel(Model):
    def __init__(self):
        super(MnistModel, self).__init__()
        self.flatten = Flatten()
        self.d1 = Dense(128, activation='relu')
        self.d2 = Dense(10, activation='softmax')
    def call(self, x):
        x = self.flatten(x)
        x = self.d1(x)
        y = self.d2(x)
        return y
model = MnistModel()

4.设置网络优化方式
model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
              metrics=['sparse_categorical_accuracy'])

5.进行训练
model.fit(x_train, y_train, batch_size=32, epochs=5, validation_data=(x_test, y_test), validation_freq=1)
6.打印网络结构
model.summary()

五:八股树的扩展
1.自制数据集,解决本领域的应用
2.数据增强,扩增数据集
3.断点续训,保存模型
4.参数提取,把参数存在文本中
5.acc、loss可视化,查看训练效果
6.应用程序,给物识图

5.1 自制数据集,解决本领域的应用
我们之前读取数据集是从自带的数据包中读取的所以代码很简单,(x_train,y_train),(x_test,y_test)=fashion.load_data()。那么如果我们的数据集是以文本文件的形式存放在电脑中,我们又该怎么读取呢。

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

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'  #这个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):  #path是txt文件所在的文件夹文职,txt是这个文件的名字
    f = open(txt, 'r')  # 以只读形式打开txt文件
    contents = f.readlines()  # 读取文件中所有行
    f.close()  # 关闭txt文件
    x, y_ = [], []  # 建立空列表
#txt文件中被空格分为两部分,图片名称和标签
    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_

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):   #如果我们曾经读取过文件并将读取结果以.npy文件的形式保存就执行以下代码
    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)

5.2 数据增强
如果数据集元素不够的话,可以进行数据增强从而实现数据集的扩充。

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)

eg:在之前识别花的类别的例子中用这个来扩增数据,有几个地方要在原来基础上修改

1.导入新的模块 from tensorflow.keras.preprocessing.image import ImageDataGenerator

2.规定对数据进行扩增的操作
image_gen_train=ImageDataGenerator( 
rescale=1./255,   #原像素值0-255归到0-1
rotation_range=45, #随机45度旋转
width_shift_range=.15,  #随机宽度偏移【-0.150.15)
height_shift_range=.15,  #随机高度偏移【-0.150.15)
horizontal_flip=True,   #随机水平翻转
zoom_range=0.5  #随机缩放到【1-50%1+50%)
image_gen_train.fit(x_train)

3.我们的image_gen_train.fit()里面只能放4维数据,所以我们要先将x_train进行reshape
x_train=x_train.reshape(x_train.shape[0],28,28,1)

4.此时我们的训练数据的代码也要修改
原来是 model.fit(x_train,y_train,batch_size=32,....)
应用了数据增强后要变成  model.fit(image_gen_train.flow(x_train,y_train,batch_size=32),.....)

5.3 断点续训,存取模型

(1)读取模型

checkpoint_save_path="./checkpoint/mnist.ckpt"   #我们将模型都会存在ckpt文件中
if os.path.exists(checkpoint_save_path+'.index'):  #我们如果存储过模型,那么会自动生成一个索引文件,所以我们通过判断这个文件是否存在来判断是否存储过模型
    model.load_weights(checkpoint_save_path)

(2)保存模型

#借助tensorflow提供的回调函数,直接保存参数和网络
tf.keras.callbacks.ModelCheckpoint(
filepath=路径文件名,
save_weights_only=True,
monitor="val_loss",
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])

5.4 参数提取,写至文本
在上一步中保存的参数,在这里我们将其提取出来
提取可训练参数:model.trainable_variables 模型中可训练的参数
设置print输出格式:np.set_printoptions(precision=小数点后按四舍五入保留几位,threshold=数组元素数量少于或等于门槛值,打印全部元素,否则打印门槛值+1个元素,中间用省略值补充) 我们给precision=np.inf时就会打印全部的小数位,给threshold=np.nan时就会打印全部的数组元素。
完整代码如下:

import tensorflow as tf
import os
import numpy as np
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'])
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)
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.5 acc/loss可视化,查看效果
其实在我们的history=model.fit()执行训练时中已经把训练集loss,测试集loss,训练集准确率,测试集准确率记录了下来。我们这时通过history将这些信息提取出来

import matplotlib.pyplot as plt
#history中读取所需的数据
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.figure(figsize=(8,8))
plt.subplot(1,2,1)
plt.plot(acc,label='Training Accuracy')
plt.plot(val_acc,label='Validatioin 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()

5.6 应用程序,绘物识图
我们之前训练好了神经网络可以进行识别图片中的数字。那么我们此时有个图片,如何给神经网络让它进行识别呢。

from PIL import Image
import numpy as np
import tensorflow as tf   #导入所需的模块

model_save_path = './checkpoint/mnist.ckpt'  #这是存放我们训练好的神经网络模型的文件位置

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)   #读取训练好的神经网络的参数值
preNum = int(input("input the number of test pictures:"))  #确定读取的图片数量

for i in range(preNum):
    image_path = input("the path of test picture:")
    img = Image.open(image_path) 
    img = img.resize((28, 28), Image.ANTIALIAS)  #训练模型时是28*28的灰度图,而现在是任意尺度的图片,所以我们要resize一下
    img_arr = np.array(img.convert('L'))
    
    for i in range(28):   #我们训练模型是用灰度图,而现在是用白底黑字的图,所以要对数据进行预处理
        for j in range(28):
            if img_arr[i][j] < 200:  #暗的地方调亮,变成纯白色
                img_arr[i][j] = 255
            else:
                img_arr[i][j] = 0    

    img_arr = img_arr / 255.0
    x_predict = img_arr[tf.newaxis, ...]  #神经网络训练时,都是一个batch进入网络的,所以我们要添加一个维度,从28*28---1*28*28
    result = model.predict(x_predict)  #使用predict函数执行前向传播从而得到计算结果
    
    pred = tf.argmax(result, axis=1)
    print('\n')
    tf.print(pred)    
    

你可能感兴趣的:(Tensorflow2.0建立神经网络)