TensorFlow 最初于 2011 年以 Google 的内部封闭源代码项目 DisBelief
诞生,2015 年 11 月 9 日根据 Apache 2.0 开源许可证发布。2019 年推出了
TensorFlow 2.0,其对 API 进行了简化和优化,删除或改变了部分 1.x 的
API,并引入了多项新功能和特性
这篇文章将使用通俗易懂的语言介绍TensorFlow
2.0的使用方法,以及深度学习的具体流程
由于TensorFlow与PyTorch同为深度学习框架,之前的PyTorch文章讲过的与之相同的内容将不再赘述
请结合 TensorFlow中文文档
进行学习
张量和常用函数
Tensor:
深度学习的一种数据类型,又称为 张量 ,实质为多维矩阵
可以进行GPU计算的矩阵
包装了反向神经网络所需参数的数据类型
张量类型:
0阶:标量 scalar
1阶:向量 vector
2阶:矩阵 matrix
m = [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
n阶:张量 tensor
t = [[[ ... ]]] (有多少个中括号就是多少阶张量)
上述的 0、1、2 阶都是张量
在TensorFlow中,输入的数据需为2D,即[[...]]
数据类型
无论在PyTorch还是TensorFlow中,输入数据最好为 float 类型
tf.int:tf.int32
tf.float:tf.float32、tf.float64
tf.bool
tf.string
常用函数
导入TensorFlow:
创建张量
1 tf.constant(data, dtype=数据类型)
numpy转Tensor:
1 tf.convert_to_tensor(data, dtype=数据类型)
Tensor转numpy:
特殊张量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 tf.zeros(维度) tf.ones(维度) tf.fill(维度, 指定值) tf.random.normal(维度, mean=均值, stddev=标准差, seed=随机数种子) tf.random.truncated_normal(维度, mean=均值, stddev=标准差, seed=随机数种子) tf.random.uniform(维度, minval=最小值, maxval=最大值, seed=随机数种子)
其中的维度一般为:
一维:[数量] or (数量)
二维:[行,列] or (行,列)
三维:[...,...,...] or (...,...,...)
...
例如:
1 2 3 4 5 tf.zeros([2 , 3 ]) tf.fill((2 , 3 ), 3 ) tf.random.truncated_normal([4 , 3 ], stddev=0.1 , seed=1 )
其他函数
下列函数在手动编写网络时会用到,若要直接使用 Keras
编写可以选择性查看
强制类型转换
标记为可训练
被标记为可训练的数据,可以在反向传播中记录梯度信息
在手动编写网络,不使用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 34 35 36 37 tf.reduce_min(张量, axis=轴) tf.reduce_max(张量, axis=轴) tf.reduce_mean(张量, axis=轴) tf.reduce_sum(张量, axis=轴) tf.add(张量1 , 张量2 ) tf.substract(张量1 , 张量2 ) tf.multiply(张量1 , 张量2 ) tf.divide(张量1 , 张量2 ) tf.square(张量) tf.pow (张量, n) tf.sqrt(张量) tf.matmul(矩阵1 , 矩阵2 ) <=> 矩阵1 @ 矩阵2
在上述函数中,涉及两个不同张量的函数,需要两个张量维度相同才可以计算
手动编写网络相关
特征与标签配对:
1 2 tf.data.Dataset.from_tensor_slices((特征, 标签))
返回值为:含有(特征,标签)的数据对的列表
1 2 tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(32 ) tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(32 )
求梯度:
1 2 3 with tf.GradientTape() as tape: ... grad = tape.gradient(函数, 求导对象)
案例代码:
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)
上述 grad 的值为 loss 在 w=3 时的一阶导数值
枚举:
案例:
1 2 3 4 5 6 7 8 for i, element in enumerate (列表): print (i, element) 0 val01 val12 val2...
独热码:
1 2 tf.one_hot(标签, depth=几分类)
案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 labels = [1 , 0 , 2 ] class_num = 3 tf.one_hot(labels, depth=class_num) ''' 输出值: 0 1 2 label ([0, 1, 0], --> 1 [1, 0, 0], --> 0 [0, 0, 1]) --> 2 '''
概率分布转换:
softmax 在我们机器学习入门教程中介绍过原理,这里不再赘述
自更新:
其中的输入必须被定义为可训练
案例:
1 2 3 w = tf.Variable(4 ) w.assign_sum(1 ) print (w)
常用在参数更新公式中:
1 2 3 4 grad = tape.gradient(loss, w1) w1.assign_sub(lr * grad)
返回最大值的索引:
手动编写的网络我们不做赘述,这里提供一个手动编写鸢尾花识别的网络:
案例代码
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 import osimport tensorflow as tfimport numpy as npfrom sklearn.datasets import load_irisfrom sklearn import datasetsimport pandas as pdfrom matplotlib import pyplot as pltos.environ['TF_CPP_MIN_LOG_LEVEL' ] = '2' data = datasets.load_iris().data target = datasets.load_iris().target np.random.seed(0 ) np.random.shuffle(data) np.random.seed(0 ) np.random.shuffle(target) x_train = data[: -30 ] y_train = target[:-30 ] x_test = data[-30 :] y_test = target[-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 loss_data = [] acc_data = [] epoch = 500 loss_all = 0 for epoch in range (epoch): print ('-' *30 + '开始训练' + '-' *30 ) for step, (x_train, y_train) in enumerate (train_db): with tf.GradientTape() as tape: y = tf.matmul(x_train, w1) + b1 y = tf.nn.softmax(y) y_ = tf.one_hot(y_train, depth=3 ) loss = tf.reduce_mean(tf.square(y_-y)) loss_all += loss grad = tape.gradient(loss, [w1, b1]) w1.assign_sub(lr * grad[0 ]) b1.assign_sub(lr * grad[1 ]) average_loss = loss_all / len (train_db) loss_data.append(average_loss) loss_all = 0 print (f'Epoch:{epoch} , Loss:{average_loss} ' ) total_correct = 0 total_num = 0 print ('-' *30 + '开始测试' + '-' *30 ) 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 ) pred = tf.cast(pred, y_test.dtype) pred = tf.equal(pred, y_test) correct = tf.cast(pred, tf.int32) correct = tf.reduce_sum(correct) total_correct += int (correct) total_num += x_test.shape[0 ] acc = total_correct / total_num print (f'Epoch:{epoch} , Acc:{acc} ' ) acc_data.append(acc) plt.title('Loss' ) plt.plot(loss_data) plt.show() plt.title('Accuracy' ) plt.plot(acc_data) plt.show()
Keras搭建网络
使用Keras搭建网络比手动编写方便了许多
具体流程为:
导入相关包
加载数据集
搭建网络模型
定义模型训练方法
定义训练参数
输出参数
加载数据
TensorFlow有官方的数据集,具体请看官方文档
使用自带方法导入CSV文件
1 2 3 4 5 6 7 8 9 10 11 import tensorflow as tftitanic_file_path = tf.keras.utils.get_file("CSV文件路径" ) titanic_csv_ds = tf.data.experimental.make_csv_dataset( titanic_file_path, batch_size=5 , label_name='survived' , num_epochs=1 , ignore_errors=True )
1 2 3 4 from sklearn import datasetsdata = datasets.load_iris().data target = datasets.load_iris().target
网络结构
使用Sequential搭建网络
1 2 model = tf.keras.models.Sequential([网络结构])
具体网络结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 tf.keras.layers.Flatten() tf.keras.layers.Dense(神经元个数, activation='激活函数' , kerned_regularizer=正则化) ''' 激活函数: relu、softmax、sigmoid、tanh 正则化: tf.keras.regularizers.l1() —— L1正则化 tf.keras.regularizers.l2() —— L2正则化 '''
使用类搭建网络
使用类搭建网络和PyTorch类似:继承+重写方法
导库:
1 2 from tensorflow.keras import Modelfrom tensorflow.keras.layers import Dense
类结构:
1 2 3 4 5 6 7 8 9 10 11 12 class MyModel (Model ): def __init__ (self ): super (MyModel, self ).__init__() |...定义网络结构块...| def call (self, x ) |...调用网络结构块,前向传播...| return y 模型对象 = 网络类名() model = MyModel()
示例:
1 2 3 4 5 6 7 8 class IrisModel (Model ): def __init__ (self ): super (IrisModel, self ).__init__() self .d1 = Dense(3 , activation='sigmoid' , kernel_regularizer=tf.keras.regularizers.l2()) def call (self, x ): y = self .d1(x) return y
训练方法
1 model对象.compile (optimizer=优化器, loss=损失函数, metrics=['准确率' ])
参数:
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 ''' optimizer:优化器 'sgd' or tf.keras.optimizers.SGD(learning_rate=?, momentum=动量参数) 'adagrad' or tf.keras.optimizers.Adagrad(learning_rate=?) 'adadelta' or tf.keras.optimizers.Adadelta(learning_rate=?) 'adam' or tf.keras.optimizers.Adam(learning_rate=?, 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/True) True:输出更加精准,重新排列公式 ------------------------------------------------------------------------ metrics:准确率 'accuracy': y和y'都为数值 eg: y'=[1] y=[1] 'categorical_accuracy': y和y'都为独热码(概率分布) eg: y'=[0, 1, 0] y=[0.2, 0.6, 0.2] 'sparse_categorical_accuracy': y'为数值,y为独热码 eg: y'=[1] y=[0.2, 0.6, 0.2] '''
训练参数
1 2 3 4 5 6 7 model对象.fit(训练集输入特征, 训练集标签, batch_size=?, epochs=?, validation_data=(测试集输入特征, 测试集标签), validation_split=从训练集划分多少比例给测试集 validation_freq=多少次epoch测试一次)
打印网络参数和参数设计
其中还可以设置输出的参数:
1 2 3 4 5 6 7 8 9 10 import osos.environ['TF_CPP_MIN_LOG_LEVEL' ] = '2' ''' 0:显示所有日志信息(默认值),包括调试信息、警告信息、错误信息等。 1:仅显示警告信息和错误信息,忽略调试信息。 2:仅显示错误信息,忽略调试信息和警告信息。 3:不显示任何日志信息,仅在发生严重错误时可能输出少量信息。 '''
数据增强
这里我们介绍图像数据的数据增强,和PyTorch中的transform差不多
使用:
1 2 3 4 5 6 7 数据增强对象 = tf.keras,preprocessing.image.ImageDataGenerator(参数) 数据增强对象.fit(数据) 模型对象.fit(数据增强对象.flow(x_train, y_train, batch_size=?), ...其余参数...)
数据增强的参数有:
rescale:所有数值乘以该参数
rotation_range:随机度数旋转
width_shift_range:随机宽度偏移
height_shift_range:随机高度偏移
horizontal_flip:是否随机水平翻转
zoom_range:随机缩放范围(缩放[1-n, 1+n]倍)
断点续训
在一些情况下,不得不终止训练,下一次仍从上次结束的位置继续训练
1 2 3 4 5 6 7 8 9 10 11 12 13 14 checkpoint_save_path = '保存路径/名称.ckpt' if os.path.exists(checkpoint_save_path + '.index' ) print ('---load the model---' ) model对象.load_weights(checkpoint_save_path) 保存操作对象 = tf.keras.callbacks.ModelCheckpoint(参数) model.fit(...正常填入参数..., callbacks=[保存操作对象])
保存模型时的参数为:
filepath:路径名(和读取时的路径相同)
save_weights_only:是否只保留模型参数
save_best_only:是否只保留最好的模型参数
读取模型和保存模型的操作应该在 model.compile()和model.fit()之间
查看训练参数
查看训练参数一般在 model.summary() 之后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 np.set_printoptions(threshold=超过多少省略显示) print (模型对象.trainable_variables)file = open ('保存路径/文件名.txt' , 'w' ) for v in 模型对象.trainable_variables: file.write(str (v.name) + '\n' ) file.write(str (v.shape) + '\n' ) file.write(str (v.numpy()) + '\n' ) file.close()
Acc和Loss可视化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 history对象 = model对象.fit(...) acc = history对象.history['sparse_categorical_accuracy' ] loss = history对象.history['loss' ] val_acc = history对象.history['val_sparse_categorical_accuracy' ] val_loss = history对象.history['val_loss' ] plt.subplot(121 ) plt.plot(acc, label='Training Accuracy' ) plt.plot(val_acc, label='Validation Accuracy' ) plt.legend() plt.subplot(122 ) plt.plot(loss, label='Training Loss' ) plt.plot(val_loss, label='Validation Loss' ) plt.legend() plt.show()
应用模型
步骤:
复现模型
加载参数
对输入进行预处理
模型预测
(可选)对预测结果可视化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 model = tf.keras.models.Sequential([网络结构]) checkpoint_save_path = '模型路径/名称.ckpt' model.load_weights(checkpoint_save_path) ...各种操作... model.predict(输入) print (...)
可能用到的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 img对象.resize((H, W), Image.LANCZOS) np.array(img对象.convert('参数' )) ''' 参数: L:灰度图 RGB RGBA ''' 255 - img_arr img_arr / 255.0 img_arr.reshape((N, H, W))
案例代码
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 import tensorflow as tfimport osfrom PIL import Imageimport numpy as npmodel = tf.keras.models.Sequential([ tf.keras.layers.Flatten(), tf.keras.layers.Dense(128 , activation='relu' ), tf.keras.layers.Dense(10 , activation='softmax' ) ]) checkpoint_save_path = './checkpoint/mnist.ckpt' model.load_weights(checkpoint_save_path) for i in range (10 ): img_path = f'class4/MNIST_FC/{i} .png' img = Image.open (img_path) img = img.resize((28 , 28 ), Image.LANCZOS) img_arr = np.array(img.convert('L' )) for m in range (28 ): for n in range (28 ): if img_arr[m][n] <= 200 : img_arr[m][n] = 255 else : img_arr[m][n] = 0 img_arr = img_arr / 255.0 img_arr = img_arr.reshape((1 , 28 , 28 )) result = model.predict(img_arr) pred = tf.argmax(result, axis=1 ) print (f'输入的图片为数字:{i} 预测为:{int (pred.numpy())} ' )
TensorFlow描述卷积层
卷积
以二维卷积为例:Conv2D
1 tf.keras.layers.Conv2D(参数)
参数:
filters:卷积核个数
kernel_size:卷积核尺寸 单数 or (H, W)
strides:滑动步长 单数 or (H, W)
padding:填充
'same':进行0填充
'valid':不填充(默认)
activation:激活函数 'relu'、'sigmoid'、'tanh'、'softmax'等
input_shape:输入特征图维度(H, W, C)可省略
批标准化
具体原理之前的文章讲过,这里不再赘述
1 2 tf.keras.layers.BatchNormalization()
批标准化层应位于卷积层和激活层之间
激活
这里通常使用非线性激活
1 2 tf.keras.layers.Activation('激活函数' )
池化
具体原理之前的文章讲过,这里不再赘述
1 2 tf.keras.layers.MaxPool2D(参数) tf.keras.layers.AveragePool2D(参数)
参数:
pool_size:池化核大小 单数 or (H, W)
strides:步长 单数 or (H, W) 默认等于pool_size
padding:'same' or 'valid'
Dropout
在神经网络中,将神经元按照一定概率从网络中暂时舍弃,在使用时恢复
1 tf.keras.layers.Dropout(舍弃概率)
完整网络
卷积网络:
C:卷积
B:批标准化
A:激活
P:池化
D:Dropout
全连接
Flatten:tf.keras.layers.Flatten()
Dense
示例网络:
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 import tensorflow as tfimport tensorflow.keras.datasets as datasetsfrom matplotlib import pyplot as pltfrom tensorflow.keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D, BatchNormalization, Activationfrom tensorflow.keras import Modelimport os(x_train, y_train), (x_test, y_test) = datasets.cifar10.load_data() x_train, x_test = x_train / 255.0 , x_test / 255.0 class Baseline (Model ): def __init__ (self ): super (Baseline, self ).__init__() self .conv2 = Conv2D(filters=6 , kernel_size=5 , padding='same' ) self .bn = BatchNormalization() self .activation = Activation('relu' ) self .max_pool = MaxPooling2D(pool_size=(2 , 2 ), padding='same' ) self .drop = Dropout(0.2 ) self .flatten = Flatten() self .dense1 = Dense(units=128 , activation='relu' ) self .drop2 = Dropout(0.2 ) self .dense2 = Dense(units=10 , activation='softmax' ) def call (self, x ): x = self .conv2(x) x = self .bn(x) x = self .activation(x) x = self .max_pool(x) x = self .drop(x) x = self .flatten(x) x = self .dense1(x) x = self .drop2(x) y = self .dense2(x) return y model = Baseline() model.compile (optimizer='adam' , loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False ), metrics='sparse_categorical_accuracy' ) checkpoint_path = 'checkpoint/Baseline.ckpt' if os.path.exists(checkpoint_path + '.index' ): print ('-------------------Load Model---------------------' ) model.load_weights(checkpoint_path) cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_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()
总结
TensorFlow2.0 是会自动使用GPU的,不需要手动开启
学习 TensorFlow 2.0
时需要多多查看其官方文档,大多数还是用到什么就去找什么
深度学习框架也需要一些深度学习和机器学习的基础,如果没有了解过,那么也可以去了解,这会对学习框架有很大的帮助
如果文章中有哪些地方错误请留言或者邮箱联系我
其他内容持续更新中,敬请期待~~~