一、数据集的加载
任务描述
本关任务:keras 环境搭建。
相关知识
为了完成本关任务,你需要掌握:1. python 语言基础; 2. 机器学习。
实验目的
学会搭建Keras开发环境,掌握基于TensorFlow的高级API框架Keras的基本用法,通过MNIST手写数字体数据集,学会搭建基于Keras API的神经网络,并用来识别手写数字体。
实验开发环境和工具
可以在Ubuntu18.04操作系统上搭建开发环境,所使用的开发工具包括Anaconda、Tensorflow、Keras,使用Python语言。因为Keras是基于Tensorflow的高层API,所以需要先安装Tensorflow再安装Keras。
实验内容
1. 安装Anaconda和TensorFlow
本实验的开发环境是在Ubuntu18.04环境下安装Anaconda并安装TensorFlow1.10。首先来看一下如何安装Anaconda。 (1) 安装Anaconda 请打开网站 https://www.anaconda.com/download/
下载适合的Anaconda安装包,因为本实验所采用的开发环境是基于64位的Ubuntu18.04操作系统(Linux的一种版本),所以下载Linux版本的的安装包Anaconda3-5.2.0-Linux-x86_64。 然后请打开系统终端,并在终端下输入以下命令进入所下载的安装包目录Downloads(学员请使用谷歌浏览器,自动保存的文件夹即为Downloads): cd Downloads
接着,请在该目录下输入以下命令并回车运行: bash Anaconda3-5.2.0-Linux-x86_64.sh
。
最后,在终端输入“ipython”验证Anaconda是否安装成功。
检测条件:没有报错即安装成功。
(2) 安装TensorFlow 2.1) 在终端输入以下命令查看TensorFlow各个版本 anaconda search -t conda tensorflow
执行结果如下:
检测条件:控制台出现以上结果。
2.2) 在终端输入以下命令查看可安装的版本地址 anaconda show anaconda/tensorflow
执行结果如下:
检测条件:控制台出现以上结果。
2.3) 如上所示,最后一步会提供一个下载地址,请使用以下命令安装TensorFlow: conda install --channel https://conda.anaconda.org/anaconda tensorflow
遇到终端输入提示“Proceed([y]/n)?”,输入“y”即可。
代码执行结果如下:
检测条件:控制台出现以上结果。
2.4) 最后,在终端下先输入“python”,然后再输入“import tensorflow”
执行结果如下:
检测条件: 不报错则安装成功。
2.安装Keras
(1) 装依赖包 numpy scipy h5py这三个包都是除了tensorflow以为的kearas依赖包,除了h5py以外其他包在前面实验过程中都已经安装,我们此处只需安装h5py即可。
首先用组合键Ctrl+Alt+T打开命令行终端,然后输入pip install h5py完成安装。
安装完成后在终端下输入pip list(或者conda list)查看已安装的包,发现有h5py则安装成功。
检测条件:终端软件安装列表有h5py则安装成功。
(2) 安装Keras
请学员通过快捷键Ctrl+Alt+T
打开命令行终端,使用命令pip install keras
来安装
安装完成后在终端下输入pip list
(或者conda list
)查看已安装的包,发现有keras则安装成功。
检测条件:终端软件安装列表有keras则安装成功
(3) 导入模块
本教程需要导入Keras里的模块。请学员编码实现从Keras的datasets模块中导入数据集mnist模块,从layer.core模块导入Dense和Activation模块,从optimizers 模块导入SGD模块,从utils模块导入np_utils模块。
检测条件:导入模块没有报错
数据预处理
(1) 加载训练集和测试集
请学员通过mnist模块的load_data方法编程实现如下功能:把数据集中的训练数据和测试数据分别赋值给(X_train,y_train)
和(X_test,yest)
两组变量,并通过shape方法查看四个变量的维度是否正确
执行结果如下:
(60000,28,28)
(60000,)
(10000,28,28)
(10000)
检测条件:是否输出以上结果。
(2) 重塑训练集和测试集的形状X_train
和X_test
如上所示分别是60000×28×28
和10000×28×28
的形状的张量,请通过numpy
模块的reshape方法编程实现将其形状重塑为60000×784,10000×784,并使用numpy模块的astype方法将X_train和X_test的元素数据类型改为float32。
然后请学员通过print方法结合X_train/X_test的shape和dtype属性编程实现查看形状和数据类型是否正确。
提示1:numpy模块的reshape方法形式如下:
a.reshape(shape) : 不改变numpy数组a的元素,返回一个shape形状的数组,原数组不变 例:
a = np.arange(20)
#原数组不变
In [1]: a.reshape([4,5])
Out[1]:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]])
提示2: numpy模块的数组对象的astype方法形式如下:
astype(dtype,order ='K',cast ='unsafe',subok = True,copy = True)
参数dytpe表示需要转化的数据类型,其他参数都有缺省值,这里用不到,可以不用管。
代码执行结果:
(60000,784)
(10000,784)
float32
float32
检测条件:是否输出以上结果
(3) 归一化
上面我们已经重塑了X_train和X_test的形状,但是mnist图片是通过矩阵存储在计算机里面,矩阵里的每一个元素都是一个像素点,每个像素都转换成了0~255的值,其中0代表白色,255代表黑色。为了方便神经网络的计算,我们需要对像素点进行归一化。
请学员通过编程把X_train和X_test里面的像素点归一化到0-1之间的值,并查看X_train的第2个例子的第100个到150个像素点的值。
代码执行结果:
array([0. , 0. , 0. , 0. , 0. ,
0. , 0. , 0. , 0. , 0. ,
0. , 0. , 0. , 0. , 0. ,
0. , 0. , 0. , 0. , 0. ,
0. , 0. , 0. , 0. , 0. ,
0. , 0. , 0.2 , 0.62352943, 0.99215686,
0.62352943, 0.19607843, 0. , 0. , 0. ,
0. , 0. , 0. , 0. , 0. ,
0. , 0. , 0. , 0. , 0. ,
0. , 0. , 0. , 0. , 0. ,
0. ], dtype=float32)
检测条件:输出以上结果则正确。
(4) one-hot编码
请学员基于Keras的utils模块的to_categorical方法编程实现对Y_train和Y_test的one-hot编码,并打印Y_train前五行看编码是否正确
提示:to_categorical方法的形式如下:to_categorical(y, num_classes=None)
其中,参数y表示要转换为one-hot编码的类向量,而参数num_classes表示类别的总数。该方法返回的是输入的类向量中所有类别所对应的one-hot编码构成的矩阵。例如,以下代码将类别1和类别3分别转换成对应的one-hot编码。
示例代码:
ohl=keras.utils.to_categorical([1,3],num_classes=5)
print(ohl)
对应的输出结果
[[0. 1. 0. 0. 0.]
[0. 0. 0. 1. 0.]]
代码执行结果:
array([[0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
[1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],
[0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]], dtype=float32
检测条件:输出以上结果则正确
编程要求
根据提示,在右侧编辑器/** Begin **/
到 /** End **/
处补充代码,完成数据的加载、重塑数据集、数据归一化、one-hot 编码。
测试说明
完成对mnist
数据集的加载、数据重塑、归一化、转one-hot编码。
平台会对你编写的代码进行测试:
预期输出:
(60000, 28, 28)
(60000,)
(10000, 28, 28)
(10000,)
(60000, 784)
(10000, 784)
float32
float32
[0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0.2 0.62352943 0.99215686
0.62352943 0.19607843 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. 0. ]
[[0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
[0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]
[0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]
[0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]]
[[0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]
[0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]
[0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
[0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]]
开始你的任务吧,祝你成功!
代码部分
import numpy as np
from keras.datasets import mnist
from keras.utils import np_utils
from keras import models
from keras import layers
# (X_train, y_train), (X_test, y_test) = mnist.load_data()
path = '/data/workspace/myshixun/step1/mnist.npz' # mnist数据集的文件路径
# --------------- Begin --------------- #
f = np.load(path)
# X_train, y_train =
# X_test, y_test =
(X_train, y_train), (X_test, y_test) = mnist.load_data()
f.close()
# --------------- End --------------- #
print(X_train.shape)
print(y_train.shape)
print(X_test.shape)
print(y_test.shape)
# 重塑数据集 转成(-1,784)
# --------------- Begin --------------- #
X_train = X_train.reshape([60000, 784]).astype(dtype='float32')
X_test = X_test.reshape([10000, 784]).astype(dtype='float32')
# --------------- End --------------- #
print(X_train.shape)
print(X_test.shape)
print(X_train.dtype)
print(X_test.dtype)
# 归一化
# --------------- Begin --------------- #
X_train = X_train / 255
X_test = X_test / 255
print(X_train[1][100:150])
# --------------- End --------------- #
# one-hot编码
# --------------- Begin --------------- #
y_train = np_utils.to_categorical([5,0,4,1,9,2,1,3,1,4], num_classes=10)
y_test = np_utils.to_categorical([7,2,1,0,4,1,4,9,5,9], num_classes=10)
# --------------- End --------------- #
print(y_train[:10])
print(y_test[:10])
二、数据集的加载
任务描述
本关任务:神经网络的搭建及训练。
相关知识
为了完成本关任务,你需要掌握:1. 神经网络的搭建 2.神经网络的训练
实验目的
学会搭建Keras开发环境,掌握基于TensorFlow的高级API框架Keras的基本用法,通过MNIST手写数字体数据集,学会搭建基于Keras API的神经网络,并用来识别手写数字体。
实验开发环境和工具
可以在 Ubuntu18.04 操作系统上搭建开发环境,所使用的开发工具包括 Anaconda、Tensorflow、Keras,使用 Python 语言。因为Keras是基于 Tensorflow 的高层 API,所以需要先安装 Tensorflow 再安装 Keras。
实验内容
搭建神经网络
添加层
Keras的原始构造模块就是模型,最简单的模型称为序列模型,即Sequential模型。接下来,我们来通过序列模型构建最基本的神经网络----感知机。感知机的模型如下:
请学员编程实现如下功能:首先创建一个序列模型对象 model;并调用 model 对象的add方法添加一个全连接层(全连接层在Keras中用Dense类来表示),其输出维度是10,输入维度是784;然后再通过add方法给输出层添加一个激活层(激活层在Keras中用Activation类来表示)得到最后输出;最后通过model的summary方法查看模型是否正确。
提示1:可以通过 Sequential类的构造方法来构建一个Keras序列模型对象。model = Sequential()
提示2: 可通过如下add方法的形式来将一个个layer加入模型model中model.add(layer)
其中Model是我们通过Sequential类构造的序列模型对象,然后调用add()方法添加层来搭建神经网络,其中layer可以是全连接层也可以是其它常用的神经网络层结构,层的具体定义后续会讲解。
提示3: Dense类的构造方法形式如下:Dense(units,activation=None,use_bias=True, kernel_initializer='glorot_uniform',bias_initializer='zeros',kernel_regularizer=None,bias_regularizer=None,activity_regularizer=None,kernel_constraint=None,bias_constraint=None)
虽然参数很多,但是我们实际中使用只需要注明几个显著的参数即可。
其中参数units表示该层的输出维度,为大于0的整数,其余的参数都有缺省值,在这里用不到。
- use_bias:布尔值,是否使用偏置项
注意如果第一层就是全连接层需要使用参数input_shape指定输入大小。
提示4:Activation类的构造方法形式如下:Activation(activation)
其中参数activation表示用于神经网络的激活函数
代码执行结果:
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_2 (Dense) (None, 10) 7850
_________________________________________________________________
activation_2 (Activation) (None, 10) 0
=================================================================
Total params: 7,850
Trainable params: 7,850
Non-trainable params: 0
________________________
检测条件:输出以上内容则正确
编译神经网络
在定义好神经网络模型后,需要对模型进行编译,已有经过编译,模型才能由Keras的后端(Theao或Tensorflow)来执行。
请学员编程实现以下功能:通过model对象的compile方法来对模型进行编译。
提示:compile 方法有三个参数,其中参数 loss 表示损失函数,这里应该用categorical_crossentropy作为损失函数;参数 optimizer表示所采用的优化算法,这里可以用SGD方法;参数metrics表示性能评估指标,这里可以用accuracy作为性能评估指标。
检测条件:需对代码进行判断
训练神经网络
一旦编译完模型,就可以用model对象的fit方法进行模型的训练。
请学员编程实现以下功能:用fit方法来训练神经网络。
提示:fit方法的形式如下:
fit(self, x=None, y=None, batch_size=None, epochs=1, verbose=1, callbacks=None, validation_split=0.0, validation_data=None, shuffle=True, class_weight=None, sample_weight=None, initial_epoch=0, steps_per_epoch=None, validation_steps=None)
虽然参数很多,但是我们实际中使用只需要注明几个显著的参数即可。
- x:输入数据。如果模型只有一个输入,那么x的类型是numpy数组,如果模型有多个输入,那么x的类型应当为list,list的元素是对应于各个输入的numpy array。
- y:标签,numpy 数组。如果模型有多个输出,可以传入一个numpy 数组的list。
- batch_size:整数,指定进行梯度下降时每个batch包含的样本数。训练时一个
- batch的样本会被计算一次梯度下降,使目标函数优化一步。
- epochs:整数,训练终止时的epoch值,训练将在达到该epoch值时停止
- verbose:日志显示,0为不在标准输出流输出日志信息,1为输出进度条记录,2为每个epoch输出一行记录
- validation_split:0~1之间的浮点数,用来指定训练集的一定比例数据作为验证集。验证集将不参与训练,并在每个epoch结束后测试的模型的指标,如损失函数、精确度等。
其中参数x,y分别对应X_train, Y_train,参数batch_sze可设置为128,参数epochs可设置为200,参数verbose设置为1,参数validation_split设置为0.2
代码执行结果:
Train on 48000 samples, validate on 12000 samples
Epoch 1/200
48000/48000 [==============================] - 1s 19us/step - loss: 1.3633 - acc: 0.6796 - val_loss: 0.8904 - val_acc: 0.8246
Epoch 2/200
48000/48000 [==============================] - 1s 15us/step - loss: 0.7913 - acc: 0.8272 - val_loss: 0.6572 - val_acc: 0.8546
Epoch 3/200
48000/48000 [==============================] - 1s 15us/step - loss: 0.6436 - acc: 0.8497 - val_loss: 0.5625 - val_acc: 0.8681
Epoch 4/200
48000/48000 [==============================] - 1s 15us/step - loss: 0.5717 - acc: 0.8602 - val_loss: 0.5098 - val_acc: 0.8765
Epoch 5/200
48000/48000 [==============================] - 1s 15us/step - loss: 0.5276 - acc: 0.8678 - val_loss: 0.4758 - val_acc: 0.8826
.
.
Epoch 195/200
48000/48000 [==============================] - 1s 19us/step - loss: 0.2767 - acc: 0.9231 - val_loss: 0.2760 - val_acc: 0.9239
Epoch 196/200
48000/48000 [==============================] - 1s 18us/step - loss: 0.2766 - acc: 0.9226 - val_loss: 0.2758 - val_acc: 0.9241
Epoch 197/200
48000/48000 [==============================] - 1s 14us/step - loss: 0.2765 - acc: 0.9229 - val_loss: 0.2758 - val_acc: 0.9242
Epoch 198/200
48000/48000 [==============================] - 1s 14us/step - loss: 0.2763 - acc: 0.9231 - val_loss: 0.2758 - val_acc: 0.9236
Epoch 199/200
48000/48000 [==============================] - 1s 14us/step - loss: 0.2762 - acc: 0.9229 - val_loss: 0.2757 - val_acc: 0.9241
Epoch 200/200
48000/48000 [==============================] - 1s 15us/step - loss: 0.2761 - acc: 0.9230 - val_loss: 0.2756 - val_acc: 0.9241
检测条件:输出的结果准确率acc和loss与上面接近即正确。
评估神经网络
一旦模型训练完成,我们就可以在全新的样本测试集上进行评估,来观察模型的好坏。
请学员编程实现以下功能:
1)使用model对象的evaluate方法来评估模型,并将evaluate的返回值保存到变量score中
2)然后打印score的第一项和第二项值,查看总的损失值和准确率。
提示:evaluate()定义形式如下: evaluate(self, x, y, batch_size=32, verbose=1, sample_weight=None)
- x:输入数据
- y:标签
- batch_size:整数,含义同fit的同名参数
- verbose:含义同fit的同名参数,但只能取0或1
- evaluate方法前2个参数是测试集特征和标签(可以分别用X_test和Y_test),verbose ,可设置为1,其它参数可以不用考虑。
代码执行结果:
10000/10000 [==============================] - 0s 24us/step
Test score 0.27740466350317
Test accuracy 0.9213
检测条件:总的损失和准确率接近即正确。
优化神经网络
模型改进
之前构建了一个最简单的神经网络。实际上,构建神经网络的时候可以同时添加层和激活函数,如下所示:model.add(Dense(10,input_shape = (784,)),activation = ’softmax’)
请学员编程实现以下功能:在上述最基本的神经网络基础上通过add方法加一个隐藏层,隐藏层的输出维度是128,激活函数是’relu’,同时输出层不再是一个单一的激活函数,而是用Dense定义全连接输出层,其中输出维度是10,激活函数是’softmax’,注意,此时我们的输入层的的输出的激活函数是’relu’.然后通过summary.model()方法查看构建是否正确
代码执行结果:
Layer (type) Output Shape Param #
=================================================================
dense_3 (Dense) (None, 128) 100480
_________________________________________________________________
dense_4 (Dense) (None, 128) 16512
_________________________________________________________________
dense_5 (Dense) (None, 10) 1290
=================================================================
Total params: 118,282
Trainable params: 118,282
Non-trainable params: 0
检测条件:得到如上左右结果则代表正确:
编译神经网络
请学员编程实现以下功能:通过model对象的compile方法来对模型编译,其中参数loss用categorical_crossentropy,参数optimizer用SGD,参数metrics选用’accuracy’
检测条件:需对代码进行判断
训练神经网络
请学员编程实现以下功能:用fit方法来训练神经网络,其中batch_sze设置为128,epochs设置为20,verbose设置为1,validation_split设置为0.2。
代码执行结果:
Train on 48000 samples, validate on 12000 samples
Epoch 1/20
48000/48000 [==============================] - 6s 131us/step - loss: 1.4590 - acc: 0.6352 - val_loss: 0.7348 - val_acc: 0.8351
Epoch 2/20
48000/48000 [==============================] - 1s 29us/step - loss: 0.5887 - acc: 0.8514 - val_loss: 0.4486 - val_acc: 0.8847
Epoch 3/20
48000/48000 [==============================] - 2s 34us/step - loss: 0.4353 - acc: 0.8807 - val_loss: 0.3711 - val_acc: 0.9003
Epoch 4/20
48000/48000 [==============================] - 1s 30us/step - loss: 0.3767 - acc: 0.8947 - val_loss: 0.3339 - val_acc: 0.9069
Epoch 5/20
48000/48000 [==============================] - 1s 29us/step - loss: 0.3430 - acc: 0.9035 - val_loss: 0.3088 - val_acc: 0.9136
Epoch 16/20
48000/48000 [==============================] - 2s 31us/step - loss: 0.2140 - acc: 0.9385 - val_loss: 0.2063 - val_acc: 0.9410
Epoch 17/20
48000/48000 [==============================] - 2s 31us/step - loss: 0.2074 - acc: 0.9406 - val_loss: 0.2010 - val_acc: 0.9434
Epoch 18/20
48000/48000 [==============================] - 2s 32us/step - loss: 0.2016 - acc: 0.9420 - val_loss: 0.1973 - val_acc: 0.9442
Epoch 19/20
48000/48000 [==============================] - 2s 32us/step - loss: 0.1961 - acc: 0.9440 - val_loss: 0.1929 - val_acc: 0.9447
Epoch 20/20
48000/48000 [==============================] - 2s 32us/step - loss: 0.1906 - acc: 0.9453 - val_loss: 0.1885 - val_acc: 0.9476
10000/10000 [==============================] - 0s 30us/step
检测条件:输出的结果准确率acc和loss与上面接近即正确。
评估神经网络
请学员编程实现以下功能:使用evaluate函数来评估模型。
其中参数x,y分别对应测试集的数据和标签,verbose设置为1,其余参数可以忽略。并将evaluate的返回值赋值给score,然后打印score的第一项和第二项值,查看总的损失值和准确率。
代码执行结果:
10000/10000 [==============================] - 0s 24us/step
Test score: 0.18817616567984224
Test accuracy: 0.9452
检测条件:输出的结果准确率acc和loss与上面接近即正确。
总结
我们通过增加2个隐藏层,让准确率提升了2.2%,然而我们的迭代次数从200显著减少到了20,说明了我们的神经网络得到了很好的改善。
编程要求
根据提示,在右侧编辑器 / Begin /
到 / End /
处补充代码,完成模型的构建、模型的编译、模型的训练以及模型的预测。
测试说明
搭建神经网络,并利用神经网络对mnist
数据集进行训练。
平台会对你编写的代码进行测试:
预期输出:通关成功!
开始你的任务吧,祝你成功!
代码部分
import numpy as np
from keras.datasets import mnist
from keras.utils import np_utils
from keras import models
from keras import layers
def mnist():
# (X_train, y_train), (X_test, y_test) = mnist.load_data()
path = '/data/workspace/myshixun/step1/mnist.npz' # mnist数据集的文件路径
f = np.load(path)
X_train, y_train = f['x_train'], f['y_train']
X_test, y_test = f['x_test'], f['y_test']
f.close()
# 重塑数据集
X_train = X_train.reshape([60000, 784])
X_test = X_test.reshape([10000, 784])
X_train = X_train.astype(np.float32)
X_test = X_test.astype(np.float32)
# 归一化
X_train = X_train / 255
X_test = X_test / 255
# one-hot编码
y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)
# build model
# --------------- Begin --------------- #
from keras import models, Sequential
from keras.layers import Dense, Activation
model = Sequential()
# model.add(Dense(10,input_shape = (784,)))
model.add(Dense(units=10, activation='softmax', input_shape=(784,)))
model.add(Activation('relu'))
model.build()
model.summary()
# train model
model.compile (optimizer='SGD', loss='categorical_crossentropy', metrics= ['accuracy'])
# model.fit(x=X_train, y=y_train, batch_size=128, epochs=200, verbose=1, validation_split=0.2)
model.fit(x=X_train, y=y_train, batch_size=128, epochs=6, verbose=1, validation_split=0.2)
score =model.evaluate(x=X_test, y=y_test,verbose=1)
test_loss = score[0]
test_acc = score[1]
# --------------- End --------------- #
# print("test_loss:", test_loss, "\ntest_acc:", test_acc)
return test_loss,test_acc,model