https://www.bilibili.com/video/av89601743
哔哩哔哩上面有一位道友专门针对《简单粗暴》分享了他的个人经验,我也跟着复习了一遍,挑一些重点的记录一下
这是一个工程师的角度,从开发的角度理解t f的构建
其实求梯度、导数、斜率都差不多的概念 然后是求他们的损失函数,我们的目标就是减少损失函数
tf里面主要就是张量的运算,costant和varieble
with。。。as。。其实相当于一个记录器,之后就可以直接用这个记录器里面的内容
对于线性的来说,x和y是已经知道的部分,求的是w和b(也可能是多个w),也可以说是分别对做w、b偏导
w、b是损失函数中的未知数
reduce_sum就是求和
归一化还是比较有用滴,但是最后一定要记得返归一化
权重偏置一般一开始是随机给予或者为0
梯度下降要慢慢来,也就是学习率的大小不能过大,但是也不能太小
zip(grads,variable)就是将[a_grad,b_grad]和[a,b]配对,[(a_grad,a),(b_grad,b)]
optimizer的作用就是a= a-a*a_grad*learning_rate,b也同理
我们不是求y_pred=a*X+b的最小值,而是求loss=0.5*tf.reduce_sum(tf.square(y_pred-y))最优化,所以损失函数一定要放在tape里面
真正神经网络函数肯定不是一维的,所以会是y = w1*x1+w2*x2+w3*x3+b等
下图是对于损失函数求偏导的过程 (视频中讲解时把2忽略了不做考虑,但博主认为并没有消去,所以最后结算的时候应该是和学习率相乘了)
super().__init__()的作用是定义基类(鸡肋??)
class :
def init:
def call:
在这里不单单是一层的结果作为下一层的输入,也可以是不同的值输入到不同的层里面,得到的输出再输入到同一个层里面(比如有一个很浅的层,然后有一个很深的层,两个结果结合到一个层以后这样子泛化能力会比较强)
output
自后就会用这个output去做损失的计算和梯度计算
y_pred = w1*x1+w2*x2+w3*x3+b
这里给一个多元的训练样本,x=tf.constant( [[1,2,3],[4,5,6]])那这里其实就是相当于是放入了三个元(1,4)(2,5)(3,6)
units= 1 神经元的个数
activition激活函数 kernel_initializer权重系数 bias_initializer偏置系数
optimizer对权重系数和偏执做梯度下降
tape记录用来求y_pred和loss
grads计算tape.gradient(loss,model.variable) model.variable直接获得所有变量,也就是[[w1,w2,w3],b]
optimizer.apply_gradient(grads_and_vars=zip(grads,model.variable)) 绑定grad和相对应变量交给优化器
以上三个loss、grads、optimizer不断循环epoch
偏置向量bias,即b是默认有的
batch的作用就是批次下取平均值,比较有助于避免一些奇异的离异点,避免得到离群样本的干扰
flattern的作用就是打平,类似于将一张皱巴巴的纸铺展开来(28,28,1)变成(784,)
也就是相当于y=w1x1+w2x2+...+W783x783+b kernel = [w1,w2,w3...w783],bias=b
softmax(x)的作用就是将x个元素分布在[0,1]之间(归一化),求导是一组概率,所有元素最后的和为1.通俗的讲就是x属于某一类的概率分别是多少
num_batches = int(data_loader.num_train//batch_ssize*num_epochs)总的训练数除以训练批次再乘以训练次数,前面相当于按照批大小把大蛋糕分成了好几份,后一个则是说重复吃这个蛋糕epochs次
for....in range(num_batches):
x,y=每次取batchsize大小的蛋糕(所以循环的总次数=蛋糕的块数*迭代次数)
with ...as tape:
y_pred = model(x)
loss=交叉熵得到的损失函数(batchsize个元素)
loss=tf.reduce_mean(loss) 样本的平均损失函数,计算出来的值只有一个浮点数
grads=
optimizer=
交叉熵就是将y_pred[0.1,0.5,0.,0.,0.,...] 和 y_real[0,1,0,0,0,0,...]计算得出一个总的概率,分布越接近则得到的浮点数越小,在正常的计算中,得到的应为batchsize个概率「」
可以看到每一次循环中仅得到一个loss的浮点数,然后计算梯度,再用优化器优化
metric模型评估也可以批量地进行
sparse_前缀的意义就是允许我们传入标量5——》[0,0,0,0,1,0,0,0,..]
CNN= 卷积层+池化层+全联接层
eg.input->卷+卷+池+池+平+全+全->output
卷积层相当于用一个方框扫描这一个层(filters卷积层神经元树木【聚集核,可以理解为一共有多少个窗口/kernel去滑动,每次完成后会对这几个窗口的结果求平均值】、kernel_size=[n,n]感受野大小、 padding=‘same‘给最后的输出的部分补0,让输出的大小和输入的保持一致),用一个窗口滑动,每次滑动之后对于窗口内计算的各个元素求和再加上偏置项
tf.squeeze(output)
tf.squeeze(input, squeeze_dims=None, name=None)从tensor中删除所有大小是1的维度,如果不想删除所有尺寸1尺寸,可以通过指定squeeze_dims来删除特定尺寸1尺寸。
strides参数(默认1)用于设置步长
池化层 也是滑动,可以理解为降采样,对之前卷积结束以后得到的新的层,再用窗口滑动取其中的最大值,[14,14]的图经过大小[2,2]的池化(四个里面选一个最大的)后变成[7,7]
[28,28,1] 其中1代表灰度图像,只有一层,[ [[],[],[]..28个,[],[]] ,[....],[...],.28组..]
[28,28,3] 其中3代表3个通道,[ [[a,b,c],[a,b,c],[a,b,c]..28个,[],[]] ,[....],[...],.28组..]
三个数里面最后一个你可以理解为图层的概念,所以也是会变的,主要是看你在选择卷积层的时候核(filter)选了多大的
RNN
训练
i am a stud->t
i have a boyf->r
...
预测(每一次输入的大小要一样)
we have a boy->f
e have a boyf->r
have a boyfr->i(注意这里为保证数量大小,最前面必有一个空格)
class...:
def init:
读取文本,大写改为小写
去重还加了排序
给每一个内容标注下标
内容和下标反正标注了一下(与上面一行一起用就可以很方便地在单词和编号之间互换查找了)
对于原文,利用上面的编码标注,将其转化成编号形式的数字列表
def ...(self,seq_length,batch_size):
seq = []
next_char=[]
for i in range(batch_size):
index = np.random.randint(0,len(self.text)-seq_length)随机选一个编号
seq.append(self.text[index:index+seq_length])将这个编号和它后面 滑动窗口大小 个组合成一个list元素
next_char.append(self.text[index+seq_length])加入上一行代码取到的数列之后的那个数据
return np.array(seq),np.array(next_char) 类型是[batch_size,seq_length],[batch_size]
onehot独热
[45,7,1]->46会转化为很多0 和一个1的模式
[[0,0,0,0,0,.....1,0,0,0,...,0] [0,0,0,0,0,0,1,0,...,0] [0,1,0,0,...]]->[0,0,0,0,0,...1,0,0,0,0,...]
class RNN(tf.keras.Model): def __init__(self, num_chars总共有多少种不同的字符, batch_size, seq_length): super().__init__() self.num_chars = num_chars(onehot编码的长度) self.seq_length = seq_length self.batch_size = batch_size self.cell = tf.keras.layers.LSTMCell(units=256) self.dense = tf.keras.layers.Dense(units=self.num_chars)(这里一定是onehot的长度) def call(self, inputs, from_logits=False): inputs = tf.one_hot(inputs, depth=self.num_chars) # [batch_size, seq_length, num_chars] state = self.cell.get_initial_state(batch_size=self.batch_size, dtype=tf.float32) 【给设置的神经元设置一个初始化的状态】 for t in range(self.seq_length): output, state = self.cell(inputs[:, t, :]【所有的大list里面的所有小list,第t个list,这个list所有的值,也就是相当于最小的那个list单元里的所有元素(即独热的那个)】, state) 这里的output可以理解为已经预测了,也就是它认为下一个应该是什么 logits = self.dense(output) if from_logits: return logits 原样输出 else: return tf.nn.softmax(logits) 输出对应的概率
对于batch_size应该理解为将这个蛋糕切成多少份,而不是每一份蛋糕的大小?
在RNN环境下直接用model.predict()效果不会很好(默认其实是调用call方法),会找最大的那个值,因此重新定义
def predict(self, inputs, temperature=1.): batch_size, _ = tf.shape(inputs) logits = self(inputs, from_logits=True) prob = tf.nn.softmax(logits / temperature).numpy() return np.array([np.random.choice(self.num_chars, p=prob[i, :]) for i in range(batch_size.numpy())])
之前用tf.argmax()取最大的作为预测值,对于文本生成太过于绝对,因此使用np.random.choice()函数按照生成的概率分布取样(这样概率小的也可能被取到)
同时加入temperature参数控制发布大小参数越大分布越平缓(最大、小值的差值于诶奥,生成的文本越丰富)
X_, _ = data_loader.get_batch(seq_length, 1) for diversity in [0.2, 0.5, 1.0, 1.2]: diversity(也就是predict里面传进去的temperature)给的比较小的时候就生成的比较死板,越大越丰富 X = X_ print("diversity %f:" % diversity) for t in range(400): 往后面再生成400个字 y_pred = model.predict(X, diversity) print(data_loader.indices_char[y_pred[0]], end=‘‘, flush=True) 还记得一开始定义的那个字和编号组队的字典吗,对,现在用上面一行给出的数字去找对应的字 X = np.concatenate([X[:, 1:]【这里1:很妙,相当于抛去第一个字,然后后面再连接新的字】, np.expand_dims(y_pred, axis=1)]【连上我们新预测的那个字】, axis=-1) print("\n")
np.expand_dims:用于扩展数组的形状
np.argmax:返回沿轴最大值的索引值
DRL强化学习部分暂缺
###############
接下来的方法和上面的构建模型的方法有所不同,比如模型需要compile和fit
###############
Keras 的 Sequential API通过向 tf.keras.models.Sequential()
提供一个层的列表,由 Keras 将它们自动首尾相连
model = tf.keras.models.Sequential([ tf.keras.layers.Flatten(), tf.keras.layers.Dense(100, activation=tf.nn.relu), tf.keras.layers.Dense(10), tf.keras.layers.Softmax() ])
Keras 提供 Functional API 多输入 / 输出或存在参数共享的模型。将层作为可调用的对象并返回张量
inputs = tf.keras.Input(shape=(28, 28, 1)) x = tf.keras.layers.Flatten()(inputs) x = tf.keras.layers.Dense(units=100, activation=tf.nn.relu)(x) x = tf.keras.layers.Dense(units=10)(x) outputs = tf.keras.layers.Softmax()(x) model = tf.keras.Model(inputs=inputs, outputs=outputs)
compile
方法配置训练过程:
model.compile( optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), loss=tf.keras.losses.sparse_categorical_crossentropy, metrics=[tf.keras.metrics.sparse_categorical_accuracy] )
fit
方法训练模型
model.fit(data_loader.train_data【训练数据】, data_loader.train_label【目标数据(数据标签)】, epochs=num_epochs, batch_size=batch_size)【应该还可以加一个validation_data
:验证数据,可用于在训练过程中监控模型的性能】
evaluate
评估训练效果
print(model.evaluate(data_loader.test_data, data_loader.test_label))
所以归纳一下构建模型的方法
1.继承类的keras.Model,call()定义任意的复杂的网络结构
2.keras.Model 传入任意个inputs和outputs定义任意的复杂的网络结构
3..keras.Model.Sequential,只能定义简单的一层传一层的方式
自定义层 继承 tf.keras.layers.Layer
类,并重写 __init__
、 build
和 call
三个方法
class MyLayer(tf.keras.layers.Layer): def __init__(self): super().__init__() # 初始化代码 def build(self, input_shape): # input_shape 是一个 TensorShape 类型对象,提供输入的形状 # 在第一次使用该层的时候调用该部分代码,在这里创建变量可以使得变量的形状自适应输入的形状 # 而不需要使用者额外指定变量形状。 # 如果已经可以完全确定变量的形状,也可以在__init__部分创建变量 self.variable_0 = self.add_weight(...) self.variable_1 = self.add_weight(...) def call(self, inputs): # 模型调用的代码(处理输入并返回输出) return output
比如这里先自定义一个层
class LinearLayer(tf.keras.layers.Layer): def __init__(self, units): super().__init__() self.units = units def build(self, input_shape): # 这里 input_shape 是第一次运行call()时参数inputs的形状 self.w = self.add_variable(name=‘w‘,【一定要用add的方式而不能直接variable】 shape=[input_shape[-1]【取到最里面那一层】, self.units], initializer=tf.zeros_initializer())【建议改成随机的赋值,tf.random_normal_initializer()】
self.b = self.add_variable(name=‘b‘, shape=[self.units], initializer=tf.zeros_initializer())【建议改成随机的赋值,tf.random_normal_initializer()】 def call(self, inputs): y_pred = tf.matmul(inputs, self.w) + self.b return y_pred
再自定义一个模型调用上面的层
class LinearModel(tf.keras.Model): def __init__(self): super().__init__() self.layer = LinearLayer(units=1) def call(self, inputs): output = self.layer(inputs) return output
自定义损失函数,【可以在compile里面修改loss这一块】class MeanSquaredError(tf.keras.losses.Loss):
def call(self, y_true, y_pred): 【在这里面因为原来的数据中y_ture和y_pred的结果格式不一样所以下面这一行对格式做了onehot处理】
y_true = tf.squeeze(y_true,axis = -1)【因为keras在自定义的时候会自己升维,所以这里加上这一行代码用来降维处理,把最里面的一个list剥除】
y_true = tf.one_hot(tf,cast(y_true,dtype=‘int32),depth = 10) return tf.reduce_mean(tf.square{tf.reduce_sum(y_pred - y_true)))【这里加一个reduce_sum就是未来保证开方里面只有一个值】
自定义评估指标【可以在compile里面修改metric这一块】【自定义的时候有时候结果会升高维度(即每个元素会自己套上一个list)】
class SparseCategoricalAccuracy(tf.keras.metrics.Metric): def __init__(self): super().__init__() self.total = self.add_weight(name=‘total‘, dtype=tf.int32, initializer=tf.zeros_initializer()) self.count = self.add_weight(name=‘count‘, dtype=tf.int32, initializer=tf.zeros_initializer()) def update_state(self, y_true, y_pred, sample_weight=None): y_true = tf.squeeze(y_true,axis = -1)【因为keras在自定义的时候会自己升维,所以这里加上这一行代码用来降维处理,把最里面的一个list剥除】 values = tf.cast【类型转化】(tf.equal【返回true/false】(y_true, tf.argmax【返回概率最大的那个下标】(y_pred, axis=-1, output_type=tf.int32)), tf.int32) self.total.assign_add(tf.shape(y_true)[0])【实际上就是batch_size】 self.count.assign_add(tf.reduce_sum(values))【每一次答对的次数】 def result(self): return self.count / self.total
使用tf.config.experimental_run_functions_eagerly(True)之后可以在代码运行计算中print你想要的,否则是不会实时计算的,他会自己进入优化
变量的保存与恢复
tf.train.Checkpoint
这一强大的变量保存与恢复类
声明
checkpoint = tf.train.Checkpoint(model=model) 或者自己命名checkpoint = tf.train.Checkpoint(myAwesomeModel=model, myAwesomeOptimizer=optimizer)
保存
checkpoint.save(保存文件的目录 + 前缀)
重新载入
model_to_be_restored = MyModel() # 待恢复参数的同一模型 checkpoint = tf.train.Checkpoint(myAwesomeModel=model_to_be_restored) # 键名保持为“myAwesomeModel” checkpoint.restore(之前保存的文件的目录 + 前缀 + 编号save_path_with_prefix_and_index) 总结:
# train.py 模型训练阶段 model = MyModel() # 实例化Checkpoint,指定保存对象为model(如果需要保存Optimizer的参数也可加入) checkpoint = tf.train.Checkpoint(myModel=model) # ...(模型训练代码) # 模型训练完毕后将参数保存到文件(也可以在模型训练过程中每隔一段时间就保存一次) checkpoint.save(‘./save/model.ckpt‘)
# test.py 模型使用阶段 model = MyModel() checkpoint = tf.train.Checkpoint(myModel=model) # 实例化Checkpoint,指定恢复对象为model checkpoint.restore(tf.train.latest_checkpoint(‘./save‘)) # 从文件恢复模型参数 # 模型使用代码
只想保留最后的几个 Checkpoint;
checkpoint = tf.train.Checkpoint(model=model) manager = tf.train.CheckpointManager(checkpoint, directory=‘./save‘, checkpoint_name=‘model.ckpt‘, max_to_keep=k)
自行指定保存的 Checkpoint 的编号,则可以在保存时加入 checkpoint_number
参数。例如 manager.save(checkpoint_number=100)
TensorBoard:类似于一种埋点保存
定义
summary_writer = tf.summary.create_file_writer(‘./tensorboard‘) # 参数为记录文件所保存的目录
记录器(不只是scalar)
with summary_writer.as_default(): # 希望使用的记录器 tf.summary.scalar("loss", loss, step=batch_index) tf.summary.scalar("MyScalar", my_scalar, step=batch_index) # 还可以添加其他自定义的变量
终端运行,【使用浏览器访问命令行程序所输出的网址(一般是 http:// 计算机名称:6006)】【如果要监听所有端口就在后面打赏--bind_all】
tensorboard --logdir=./tensorboard
Graph看结构Profile看耗时
tf.summary.trace_on(graph=True, profiler=True) # 开启Trace,可以记录图结构和profile信息 ########## 进行训练的代码######### with summary_writer.as_default(): tf.summary.trace_export(name="model_trace", step=0, profiler_outdir=log_dir) # 保存Trace信息到文件【建议将log_dir改为./tensorboard这样子就不会单独放到另外的文件夹里面了】
【一些坑:1、每一次训练用这个记得把之前的记录的删掉!!!!!2、trace_on直接放在大的循环里面会持续地占有大内存,事实上只要记录一次就行了,采用个位数的batch意思意思就能出图了3、自定义的层之间没有图的联系,或者说是不是强连接,所以在def call()上面加上@tf.function这样会自动编程图的结构,这个是即时模式Eager Execution的弊端,当然,如果是用pipeline/sequential的那种因为有compile那一步就会保存为图来执行了】
@tf.function
修饰符,将模型以图执行模式运行。【在原有的函数外围再包一层函数】【函数参数只包括 TensorFlow 张量或 NumPy 数组】【相当于再翻译一遍代码】【会事先先运行一遍代码,之后运行都会执行翻译后的代码,但是如果传入的参数类型转变了,那它就会再一次执行原来的代码再翻译一遍】
使用TensorFlow内置的tf.print()。@tf.function不支持Python内置的print方法
t f.data
dataset = tf.data.Dataset.from_tensor_slices((X, Y))#X输入Y输出
mnist_dataset = tf.data.Dataset.from_tensor_slices((train_data, train_label))
dataset = dataset.map(函数)
mnist_dataset = mnist_dataset.batch(4)划分批次
mnist_dataset = mnist_dataset.shuffle(buffer_size=10000).batch(4)数据打散后再设置批次,缓存大小设置为 10000:
mnist_dataset = mnist_dataset.prefetch(buffer_size=tf.data.experimental.AUTOTUNE)开启预加载数据
mnist_dataset = mnist_dataset.map(map_func=函数, num_parallel_calls=【CPU个数】)利用多核心的优势对数据进行并行化变换
数据集元素的获取与使用
dataset = tf.data.Dataset.from_tensor_slices((A, B, C, ...)) for a, b, c, ... in dataset: # 对张量a, b, c等进行操作,例如送入模型进行训练
it = iter(dataset) a_0, b_0, c_0, ... = next(it) a_1, b_1, c_1, ... = next(it)#使用iter()
显式创建一个 Python 迭代器并使用next()
获取下一个元素
model.fit(mnist_dataset, epochs=num_epochs)#使用tf.data.Dataset
后,我们可以直接传入Dataset
多输入和多特征的概念
多输入是多组特征,进入不同的inputs,(比如k 线图特征和开盘价特征两种数据集一个进入CNN一个进入RNN,最后输出到一个全联接层?)
TFRecord数据集存储格式(不吃内存,数据在内存里面了)
整理为 TFRecord 格式
- 读取到内存;
- 转换为
tf.train.Example
对象(每一个 由若干个tf.train.Feature
的字典组成,需要先建立 Feature 的字典); - 将该 对象序列化为字符串,并通过一个预先定义的
tf.io.TFRecordWriter
写入 TFRecord 文件。
with tf.io.TFRecordWriter(tfrecord_file) as writer: for filename, label in zip(train_filenames, train_labels): image = open(filename, ‘rb‘).read() # 读取数据集图片到内存,image 为一个 Byte 类型的字符串 feature = { # 建立 tf.train.Feature 字典 ‘image‘: tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])), # 图片是一个 Bytes 对象 ‘label‘: tf.train.Feature(int64_list=tf.train.Int64List(value=[label])) # 标签是一个 Int 对象 } example = tf.train.Example(features=tf.train.Features(feature=feature)) # 通过字典建立 Example writer.write(example.SerializeToString()) # 将Example序列化并写入 TFRecord 文件
读取 TFRecord 数据
- 通过
tf.data.TFRecordDataset
读入原始的 TFRecord 文件(此时文件中的tf.train.Example
对象尚未被反序列化),获得一个tf.data.Dataset
数据集对象;
- 通过
Dataset.map
方法,对该数据集对象中的每一个序列化的tf.train.Example
字符串执行tf.io.parse_single_example
函数,从而实现反序列化
raw_dataset = tf.data.TFRecordDataset(tfrecord_file) # 读取 TFRecord 文件 feature_description = { # 定义Feature结构,告诉解码器每个Feature的类型是什么 ‘image‘: tf.io.FixedLenFeature([], tf.string), ‘label‘: tf.io.FixedLenFeature([], tf.int64), } def _parse_example(example_string): # 将 TFRecord 文件中的每一个序列化的 tf.train.Example 解码 feature_dict = tf.io.parse_single_example(example_string, feature_description) feature_dict[‘image‘] = tf.io.decode_jpeg(feature_dict[‘image‘]) # 解码JPEG图片 return feature_dict[‘image‘], feature_dict[‘label‘] dataset = raw_dataset.map(_parse_example)
tf.TensorArray
时间序列的结构中,我们可能需要将一系列张量以数组的方式依次存放起来,以供进一步处理。
在即时执行模式下,你可以直接使用一个 Python 列表(List)存放数组。不过,如果你需要基于计算图的特性(例如使用 @tf.function
加速模型运行或者使用 SavedModel 导出模型),就无法使用这种方式了
import tensorflow as tf @tf.function def array_write_and_read(): arr = tf.TensorArray(dtype=tf.float32, size=3) #声明一个大小为size
,类型为dtype
的 TensorArrayarr
。如果将dynamic_size
参数设置为True
,则该数组会自动增长空间。 arr = arr.write(0, tf.constant(0.0))#写入 arr = arr.write(1, tf.constant(1.0)) arr = arr.write(2, tf.constant(2.0)) arr_0 = arr.read(0)#读取 arr_1 = arr.read(1) arr_2 = arr.read(2) return arr_0, arr_1, arr_2 a, b, c = array_write_and_read() print(a, b, c) 还包括stack()
、unstack()
等常用操作
tf.config
gpus = tf.config.experimental.list_physical_devices(device_type=‘GPU‘) cpus = tf.config.experimental.list_physical_devices(device_type=‘CPU‘) print(gpus, cpus)
gpus = tf.config.experimental.list_physical_devices(device_type=‘GPU‘) tf.config.experimental.set_visible_devices(devices=gpus[0:2], device_type=‘GPU‘)#只使用下标为 0、1 的两块显卡
import os os.environ[‘CUDA_VISIBLE_DEVICES‘] = "2,3"即可指定程序只在显卡 2,3 上运行。
gpus = tf.config.experimental.list_physical_devices(device_type=‘GPU‘) tf.config.experimental.set_virtual_device_configuration( gpus[0], [tf.config.experimental.VirtualDeviceConfiguration(memory_limit=1024)])#建立了一个显存大小为 1GB 的 “虚拟 GPU”
gpus = tf.config.experimental.list_physical_devices(‘GPU‘)
tf.config.experimental.set_virtual_device_configuration(
gpus[0],
[tf.config.experimental.VirtualDeviceConfiguration(memory_limit=2048),
tf.config.experimental.VirtualDeviceConfiguration(memory_limit=2048)])#在实体 GPU GPU:0
的基础上建立了两个显存均为 2GB 的虚拟 GPU。
导出为 SavedModel 文件时,无需建立模型的源代码即可再次运行模型,适用于模型的分享和部署。后文的 TensorFlow Serving(服务器端部署模型)、TensorFlow Lite(移动端部署模型)以及 TensorFlow.js 都会用到这一格式。
tf.saved_model.save(model, "保存的目标文件夹名称") #导出
model = tf.saved_model.load("保存的目标文件夹名称") #载入
注意??
继承 tf.keras.Model
类建立的 Keras 模型,其需要导出到 SavedModel 格式的方法(比如 call
)都需要使用 @tf.function
修饰
对于使用继承 tf.keras.Model
类建立的 Keras 模型 model
,使用 SavedModel 载入后将无法使用 model()
直接进行推断,而需要使用 model.call()
。
——————————分割————
Sequential 下的保存和ke ra s的保存不太一样,自己带了save的格式
model.save(‘mnist_cnn.h5‘) #保存模型在服务器端,可以直接通过 keras.models.load_model("mnist_cnn.h5")
#加载模型然后进行推理;在移动设备需要将 HDF5 模型文件转换为 TensorFlow Lite 的格式,然后通过相应平台的 Interpreter 加载,然后进行推理。这个时候model后面带不带call都是可以的
TensorFlow Serving部署模型
Keras Sequential mode直接用命令
tensorflow_model_server --rest_api_port=8501 --model_name=MLP --model_base_path="/home/.../.../saved" # 文件夹绝对地址根据自身情况填写,无需加入版本号
Custom Keras models
不仅需要使用 @tf.function
修饰,还要在修饰时指定 input_signature
参数
@tf.function(input_signature=[tf.TensorSpec([None, 28, 28, 1], tf.float32)])
#输入是一个 [None, 28, 28, 1]
的四维张量( None
表示第一维即 Batch Size 的大小不固定)
tf.saved_model.save(model, "saved_with_signature/1", signatures={"call": model.call}) #在调用模型时使用 call
这一签名来调用 model.call
方法时,我们可以在导出时传入 signature
参数,以 dict
的键值对形式告知导出的方法对应的签名
而后可以使用命令
tensorflow_model_server --rest_api_port=8501 --model_name=MLP --model_base_path="/home/.../.../saved_with_signature" # 修改为自己模型的绝对地址
在客户端调用以 TensorFlow Serving 部署的模型
在客户端向服务器发送以下格式的请求:
服务器 URI: http://服务器地址:端口号/v1/models/模型名:predict
请求内容:
{ "signature_name": "需要调用的函数签名(Sequential模式不需要)", "instances": 输入数据 }
回复为:
{ "predictions": 返回值 }
import json import numpy as np import requests from zh.model.utils import MNISTLoader data_loader = MNISTLoader() data = json.dumps({ "instances": data_loader.test_data[0:3].tolist() }) headers = {"content-type": "application/json"} json_response = requests.post( ‘http://localhost:8501/v1/models/MLP:predict‘, data=data, headers=headers) predictions = np.array(json.loads(json_response.text)[‘predictions‘]) print(np.argmax(predictions, axis=-1)) print(data_loader.test_label[0:10])
python的格式调用
jason 只认python的数组,所以要用.tolist()的方法
??调用ke ra s自定义的模型时不要忘记了传
"signature_name": "call",
【返回的json可以在bejason.com这个网站上面查看】
tensorflow sever 可以穿一个model_config_file的文件同时运行多个模型
拓展:
多输入,两路输入,最后合到一个输入。再一开始定义的时候那个@tf.function(input_signature=[tf.TensorSpec([None, 28, 28, 1],name=‘a‘,dtype=tf.float32),tf.TensorSpec([None, 28, 28, 1],name=‘b‘,detype=tf.float32)])
后面requests的时候不能用instance去post,而是要改为"inputs":{‘a‘:[1,2,3],‘b‘:[4,5,6]}【a b相当于特征的名字】,请求返回的是outputs的Jason
对于视频中还存在一些我感觉不是很明白的地方,如果有大佬知道请多指正!
Q:p6那里的783、784之间少了一个1不是很懂
Q:tf.squeeze(input, squeeze_dims=None, name=None)从tensor中删除所有大小是1的维度,如果不想删除所有尺寸1尺寸,可以通过指定squeeze_dims来删除特定尺寸1尺寸。
p9里面池化层取【2,2】的大小是取最大值,那要是取大一点的窗口也是取最大值吗?
Q:
p11
07:51这里cha r s不仅仅是去重还将大写改为小写,还加了排序
11:48这里我觉得应该是【从0开始 到 倒数seq_length】里面选取一个编号?
Q:
p12
9:13是否能理解为【所有的大list里面的所有小list,第t个list,这个list所有的值,也就是相当于最小的那个list单元里的所有元素(即独热的那个)】什么时候from_logits可以为true?
Q:
p13
对于batch_size应该理解为将这个蛋糕切成多少份,而不是每一份蛋糕的大小?
12:32 temperature = 1.时候为什么除一下以后会压扁?
19:35 np.expand_dims:用于扩展数组的形状
Q:
p16
6:37为什么都要用到tf.cast来转换类型,直接用int()的方法不行吗?
Q:
p20
7:56 @tf.function 会执行一遍代码,如果在运行的代码里面有print也会输出,那这时候的那个数据在总的运行次数里面有加入到训练当中吗
A:只是跑一次生成计算图代码,忽略输出,然后再重新开始跑计算吧。
Q:
p22
6:39多输入和多特征的概念
多输入是多组特征,进入不同的inputs,(比如k 线图特征和开盘价特征两种数据集一个进入CNN一个进入RNN,最后输出到一个全联接层?)
1.这个时候在构建模型的时候是不是sequential的那种构建方式不能用了,而model和自定义里面可以?
2.多特征应该怎么样呢将(x,y)变成(x,y,z)?fit的时候如何判断哪个是输入的x1x2x3哪个是输出y1y2呢
3.那是不是((A,B),(C),(D))这样是不可能的
10:23 from_tenor_slices((输入,输出)),多输入的情况是from_tenor_slices((输入,输入,输入,输出))吗?还是还需要嵌套一层元组
Q:
p24 write(index, value) :将 value 写入数组的第 index 个位置;
6:06 对于时间序列的数据,是不是写的时候就是
arr = tf.TensorArray(dtype=tf.float32, size=3)【这个size是代表arr里面放几种编号吗】
arr = arr.write(1, tf.variable(x))【这里x假设是代表着日期,那前面只要写1就行了还是说要用循环把1替换成1~日期总数?】
10:56 如果只有CPU的情况下,应该采取怎么样的优化方式
Q:
p25
15:28 input里面(none,28,28,1)none是batch的个数,什么时候是必须要加上的呢,batch不是在fit里面设定的吗?
A:自定义的ke ra s需要部署到serving上时;
Q:
p26
10:36
一个模型如果我有多组特征
1合并放在一个输入层里面
2分开放在不同输入层里面
如果输入层的配置相同的情况下,效果也是相同的吗?(感觉应该如果各特征没有相关性的话效果相同)
Q:
p28
11:37 讲得是模型的多输入的
对于多特征来说要是想输入到一个层里面怎么做呢,现在网上关于tf2的都是输入一个特征的,很少输入多个特征的,讲得也不清晰。那我同一个数据集里面的多个特征只能按照多输入的方式每一个单独弄一套最后划到同一层上面吗
原文地址:https://www.cnblogs.com/xingnie/p/12483126.html