在采用TensorFlow是,尽量采用TensorFlow自带的api进行数据处理,可以减少采用训练好的模型进行推理阶段的预处理或后处理步骤。
一、Tensor 之间的运算规则
- 相同大小 Tensor 之间的任何算术运算都会将运算应用到元素级
- 不同大小 Tensor(要求dimension 0 必须相同) 之间的运算叫做广播(broadcasting)
- Tensor 与 Scalar(0维 tensor) 间的算术运算会将那个标量值传播到各个元素
- Note: TensorFlow 在进行数学运算时,一定要求各个 Tensor 数据类型一致
二、常用操作符和基本数学函数
大多数运算符都进行了重载操作,使我们可以快速使用 (+ - * /) 等,但是有一点不好的是使用重载操作符后就不能为每个操作命名了。
# 算术操作符:+ - * / %
tf.add(x, y, name=None) # 加法(支持 broadcasting)
tf.subtract(x, y, name=None) # 减法
tf.multiply(x, y, name=None) # 乘法
tf.divide(x, y, name=None) # 浮点除法, 返回浮点数(python3 除法)
tf.mod(x, y, name=None) # 取余
# 幂指对数操作符:^ ^2 ^0.5 e^ ln
tf.pow(x, y, name=None) # 幂次方
tf.square(x, name=None) # 平方
tf.sqrt(x, name=None) # 开根号,必须传入浮点数或复数
tf.exp(x, name=None) # 计算 e 的次方
tf.log(x, name=None) # 以 e 为底,必须传入浮点数或复数
# 取符号、负、倒数、绝对值、近似、两数中较大/小的
tf.negative(x, name=None) # 取负(y = -x).
tf.sign(x, name=None) # 返回 x 的符号
tf.reciprocal(x, name=None) # 取倒数
tf.abs(x, name=None) # 求绝对值
tf.round(x, name=None) # 四舍五入
tf.ceil(x, name=None) # 向上取整
tf.floor(x, name=None) # 向下取整
tf.rint(x, name=None) # 取最接近的整数
tf.maximum(x, y, name=None) # 返回两tensor中的最大值 (x > y ? x : y)
tf.minimum(x, y, name=None) # 返回两tensor中的最小值 (x < y ? x : y)
# 三角函数和反三角函数
tf.cos(x, name=None)
tf.sin(x, name=None)
tf.tan(x, name=None)
tf.acos(x, name=None)
tf.asin(x, name=None)
tf.atan(x, name=None)
# 其它
tf.div(x, y, name=None) # python 2.7 除法, x/y-->int or x/float(y)-->float
tf.truediv(x, y, name=None) # python 3 除法, x/y-->float
tf.floordiv(x, y, name=None) # python 3 除法, x//y-->int
tf.realdiv(x, y, name=None)
tf.truncatediv(x, y, name=None)
tf.floor_div(x, y, name=None)
tf.truncatemod(x, y, name=None)
tf.floormod(x, y, name=None)
tf.cross(x, y, name=None)
tf.add_n(inputs, name=None) # inputs: A list of Tensor objects, each with same shape and type
tf.squared_difference(x, y, name=None)
三、矩阵数学函数
# 矩阵乘法(tensors of rank >= 2)
tf.matmul(a, b, transpose_a=False, transpose_b=False, adjoint_a=False, adjoint_b=False, a_is_sparse=False, b_is_sparse=False, name=None)
# 转置,可以通过指定 perm=[1, 0] 来进行轴变换
tf.transpose(a, perm=None, name='transpose')
# 在张量 a 的最后两个维度上进行转置
tf.matrix_transpose(a, name='matrix_transpose')
# Matrix with two batch dimensions, x.shape is [1, 2, 3, 4]
# tf.matrix_transpose(x) is shape [1, 2, 4, 3]
# 求矩阵的迹
tf.trace(x, name=None)
# 计算方阵行列式的值
tf.matrix_determinant(input, name=None)
# 求解可逆方阵的逆,input 必须为浮点型或复数
tf.matrix_inverse(input, adjoint=None, name=None)
# 奇异值分解
tf.svd(tensor, full_matrices=False, compute_uv=True, name=None)
# QR 分解
tf.qr(input, full_matrices=None, name=None)
# 求张量的范数(默认2)
tf.norm(tensor, ord='euclidean', axis=None, keep_dims=False, name=None)
# 构建一个单位矩阵, 或者 batch 个矩阵,batch_shape 以 list 的形式传入
tf.eye(num_rows, num_columns=None, batch_shape=None, dtype=tf.float32, name=None)
# Construct one identity matrix.
tf.eye(2)
==> [[1., 0.],
[0., 1.]]
# Construct a batch of 3 identity matricies, each 2 x 2.
# batch_identity[i, :, :] is a 2 x 2 identity matrix, i = 0, 1, 2.
batch_identity = tf.eye(2, batch_shape=[3])
# Construct one 2 x 3 "identity" matrix
tf.eye(2, num_columns=3)
==> [[ 1., 0., 0.],
[ 0., 1., 0.]]
# 构建一个对角矩阵,rank = 2*rank(diagonal)
tf.diag(diagonal, name=None)
# 'diagonal' is [1, 2, 3, 4]
tf.diag(diagonal) ==> [[1, 0, 0, 0]
[0, 2, 0, 0]
[0, 0, 3, 0]
[0, 0, 0, 4]]
其它
tf.diag_part
tf.matrix_diag
tf.matrix_diag_part
tf.matrix_band_part
tf.matrix_set_diag
tf.cholesky
tf.cholesky_solve
tf.matrix_solve
tf.matrix_triangular_solve
tf.matrix_solve_ls
tf.self_adjoint_eig
tf.self_adjoint_eigvals
四、Reduction:reduce various dimensions of a tensor
# 计算输入 tensor 所有元素的和,或者计算指定的轴所有元素的和
tf.reduce_sum(input_tensor, axis=None, keep_dims=False, name=None)
# 'x' is [[1, 1, 1]
# [1, 1, 1]]
tf.reduce_sum(x) ==> 6
tf.reduce_sum(x, 0) ==> [2, 2, 2]
tf.reduce_sum(x, 1) ==> [3, 3]
tf.reduce_sum(x, 1, keep_dims=True) ==> [[3], [3]] # 维度不缩减
tf.reduce_sum(x, [0, 1]) ==> 6
# 计算输入 tensor 所有元素的均值/最大值/最小值/积/逻辑与/或
# 或者计算指定的轴所有元素的均值/最大值/最小值/积/逻辑与/或(just like reduce_sum)
tf.reduce_mean(input_tensor, axis=None, keep_dims=False, name=None)
tf.reduce_max(input_tensor, axis=None, keep_dims=False, name=None)
tf.reduce_min(input_tensor, axis=None, keep_dims=False, name=None)
tf.reduce_prod(input_tensor, axis=None, keep_dims=False, name=None)
tf.reduce_all(input_tensor, axis=None, keep_dims=False, name=None) # 全部满足条件
tf.reduce_any(input_tensor, axis=None, keep_dims=False, name=None) #至少有一个满足条件
-------------------------------------------
# 分界线以上和 Numpy 中相应的用法完全一致
-------------------------------------------
# inputs 为一 list, 计算 list 中所有元素的累计和,
# tf.add(x, y, name=None)只能计算两个元素的和,此函数相当于扩展了其功能
tf.accumulate_n(inputs, shape=None, tensor_dtype=None, name=None)
# Computes log(sum(exp(elements across dimensions of a tensor)))
tf.reduce_logsumexp(input_tensor, axis=None, keep_dims=False, name=None)
# Computes number of nonzero elements across dimensions of a tensor
tf.count_nonzero(input_tensor, axis=None, keep_dims=False, name=None)
https://www.cnblogs.com/pythonzc/p/12270992.html, 可参考
五:损失函数loss汇总
回归问题:
tf.losses.mean_squared_error
:均方根误差(MSE)
优点是便于梯度下降,误差大时下降快,误差小时下降慢,有利于函数收敛。
缺点是受明显偏离正常范围的离群样本的影响较大
mse = tf.losses.mean_squared_error(y_true, y_pred)
# 手动实现
mse = tf.reduce_mean(tf.square(y_true - y_pred))
tf.losses.absolute_difference
:平均绝对误差(MAE) : 想格外增强对离群样本的健壮性时使用
优点是其克服了 MSE 的缺点,受偏离正常范围的离群样本影响较小。
缺点是收敛速度比 MSE 慢,因为当误差大或小时其都保持同等速度下降,而且在某一点处还不可导,计算机求导比较困难
maes = tf.losses.absolute_difference(y_true, y_pred)
maes_loss = tf.reduce_sum(maes)
tf.losses.huber_loss
:Huber loss —— 集合 MSE 和 MAE 的优点,但是需要手动调超参数
检测真实值(y_true)和预测值(y_pred)之差的绝对值在超参数 δ 内时,使用 MSE 来计算 loss, 在 δ 外时使用类 MAE 计算 loss。sklearn 关于 huber 回归的文档中建议将 δ=1.35 以达到 95% 的有效性
hubers = tf.losses.huber_loss(y_true, y_pred)
hubers_loss = tf.reduce_sum(hubers)
处理分类问题
tf.nn.sigmoid_cross_entropy_with_logits
:先 sigmoid 再求交叉熵 —— 二分类问题首选
使用时,一定不要将预测值(y_pred)进行 sigmoid 处理,否则会影响训练的准确性,因为函数内部已经包含了 sigmoid 激活(若已先行 sigmoid 处理过了,则 tensorflow 提供了另外的函数) 。真实值(y_true)则要求是 One-hot 编码形式。函数求得的结果是一组向量,是每个维度单独的交叉熵,如果想求总的交叉熵,使用 tf.reduce_sum() 相加即可;如果想求 loss ,则使用 tf.reduce_mean() 进行平均。
sigmoids = tf.nn.sigmoid_cross_entropy_with_logits(labels=y, logits=y_pred)
sigmoids_loss = tf.reduce_mean(sigmoids)
# 基础函数实现
y_pred_si = 1.0/(1+tf.exp(-y_pred))
sigmoids = -y_true*tf.log(y_pred_si) - (1-y_true)*tf.log(1-y_pred_si)
sigmoids_loss = tf.reduce_mean(sigmoids)
tf.losses.log_loss
:交叉熵 —— 效果同上,预测值格式略有不同
预测值(y_pred)计算完成后,若已先行进行了 sigmoid 处理,则使用此函数求 loss ,若还没经过 sigmoid 处理,可直接使用 sigmoid_cross_entropy_with_logits。
logs = tf.losses.log_loss(labels=y, logits=y_pred)
logs_loss = tf.reduce_mean(logs)
#基础函数实现
logs = -y_true*tf.log(y_pred) - (1-y_true)*tf.log(1-y_pred)
logs_loss = tf.reduce_mean(logs)
tf.nn.softmax_cross_entropy_with_logits_v2
:先 softmax 再求交叉熵 —— 多分类问题首选
预测值(y_pred)同样是没有经过 softmax 处理过的值,真实值(y_true)要求是 One-hot 编码形式
softmaxs = tf.nn.softmax_cross_entropy_with_logits_v2(labels=y, logits=y_pred)
softmaxs_loss = tf.reduce_mean(softmaxs)
tf.nn.sparse_softmax_cross_entropy_with_logits
:效果同上,真实值格式略有不同
若真实值(y_true)不是 One-hot 格式的,可以使用此函数,可省略一步转换
softmaxs_sparse = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=y_pred)
softmaxs_sparse_loss = tf.reduce_mean(softmaxs_sparse)
tf.nn.weighted_cross_entropy_with_logits
:带权重的 sigmoid 交叉熵 —— 适用于正、负样本数量差距过大时
增加了一个权重的系数,用来平衡正、负样本差距,可在一定程度上解决差距过大时训练结果严重偏向大样本的情况。
sigmoids_weighted = tf.nn.weighted_cross_entropy_with_logits(targets=y, logits=y_pred, pos_weight)
sigmoids_weighted_loss = tf.reduce_mean(sigmoids_weighted)
# 基础函数实现
sigmoids_weighted = -y_true*tf.log(y_pred) * weight - (1-y_true)*tf.log(1-y_pred)
sigmoids_loss = tf.reduce_mean(sigmoids)
tf.losses.hinge_loss:铰链损失函数 —— SVM 中使用
hing_loss 是为了求出不同类别间的“最大间隔”,此特性尤其适用于 SVM(支持向量机)。使用 SVM 做分类,与 LR(Logistic Regression 对数几率回归)相比,其优点是小样本量有不错效果、对噪点包容性强,缺点是样本量大时效率低、有时很难找到合适的区分方法
hings = tf.losses.hinge_loss(labels=y, logits=y_pred, weights)
hings_loss = tf.reduce_mean(hings)
自定义函数:
标准的损失函数并不合适所有场景,有些实际的背景需要采用自己构造的损失函数,Tensorflow 也提供了丰富的基础函数供自行构建。
例如下面的例子:当预测值(y_pred)比真实值(y_true)大时,使用 (y_pred-y_true)*loss_more 作为 loss,反之,使用 (y_true-y_pred)*loss_less
loss = tf.reduce_sum(tf.where(tf.greater(y_pred, y_true), (y_pred-y_true)*loss_more,(y_true-y_pred)*loss_less))
tf.greater(x, y):判断 x 是否大于 y,当维度不一致时广播后比较
tf.where(condition, x, y):当 condition 为 true 时返回 x,否则返回 y
tf.reduce_mean():沿维度求平均
tf.reduce_sum():沿维度相加
tf.reduce_prod():沿维度相乘
tf.reduce_min():沿维度找最小
tf.reduce_max():沿维度找最大
六:具体应用
tf.nn.top_k
import tensorflow as tf
t = [1,2,3,4,5,6,7,8,7,6,5,4,3,2]
t = tf.constant(t)
with tf.Session() as sess:
n = tf.size(t)
print sess.run(tf.nn.top_k(t,n))
print sess.run(t)
输出:TopKV2(values=array([8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1], dtype=int32), indices=array([ 7, 6, 8, 5, 9, 4, 10, 3, 11, 2, 12, 1, 13, 0],
dtype=int32))
可以看出返回的是从大到小的排序,和rank值。和numpy的argsort(a)操作方式类似,但是argsort是升序。
tf.gather_nd(),tf.where(),tf.greater(),tf.boolean_mask()
import numpy as np
import tensorflow as tf
a = range(15,30)
a = np.reshape(a,[3,5])
a = tf.constant(a)
with tf.Session() as sess:
print sess.run(a)
x = tf.where(a > 20)
tt = tf.gather_nd(a ,x)
print sess.run(tt)
print sess.run(tf.shape(tt))
print sess.run(x)
print sess.run(tf.shape(x))
rrr = tf.greater(a,20)
print('rrr:')
print sess.run(rrr)
res = tf.boolean_mask(a,rrr)
print sess.run(res)
print:
a:
[[15 16 17 18 19]
[20 21 22 23 24]
[25 26 27 28 29]]
x:
[[1 1]
[1 2]
[1 3]
[1 4]
[2 0]
[2 1]
[2 2]
[2 3]
[2 4]]
x.shape
[9 2]
tt
[21 22 23 24 25 26 27 28 29]
tt.shape
[9]
rrr:
[[False False False False False]
[False True True True True]
[ True True True True True]]
[21 22 23 24 25 26 27 28 29]
可以看出tf.where()返回的是符合条件的tensor的坐标。而tf.gather_nd()是返回对应坐标的元素值。在这里打印出的shape可以看出,tf.where返回的是两维坐标,其实第二维的长度表示源tensor的‘深度’。而tf.gather_nd返回的是一维。这个在tensorflow中应当注意,可能要对里面的每个元素进行扩维度。,另外通过tf.boolean.mask也可以实现类似的功能,tf.greater()和tf.less()是大于和小鱼的判别
tf.logical_and(), tf.logical_not(), tf.equal()
很简单的用法,直接看代码:
b = tf.constant(5)
with tf.Session() as sess:
print sess.run(tf.logical_and(True, False))
print sess.run(tf.logical_or(True, False))
print sess.run(tf.equal(tf.subtract(b,5),0))
print:
False
True
True
就是逻辑与和或和等与的操作
tf.while_loop(),tf.cond
tf.while_loop(cond, body, loop_vars, shape_invariants=None,
parallel_iterations=10, back_prop=True,
swap_memory=False, name=None)
其中,cond返回一个布尔型张量,body是循环体,loop_vars是运行cond和body需要的输入变量列表。
即loop_vars先传入cond 判断条件是否成立,成立之后,把loop_vars传入body 执行操作, 然后返回操作后的loop_vars,即loop_vars已被更新,再把更新后的参数传入cond, 依次循环,直到不满足条件。
logits = [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]
level = config.level_num
dis = []
for k in range(0, config.train_batch_size):
for i in range(k*level, level*(k+1)):
for j in range(i+1, level*(k+1)):
dis.append(logits[i] - logits[j])
print(dis)
# 采用 tf.while_loop的实现方法如下
batch_size = tf.constant(config.train_batch_size, tf.int32) # 4
k_begin = tf.constant(0, tf.int32)
level = tf.constant(config.level_num, tf.int32) # 6
dis = tf.Variable([], dtype=tf.float32)
margin = tf.constant(3, tf.float32)
def cond_1(dis_1, k_going):
# tf.less(zero, block_y)
return k_going < batch_size
def body_1(dis_1, k_going):
def cond_2(dis_2, i_going, i_end):
return i_going < i_end
def body_2(dis_2, i_going, i_end):
def cond_3(dis_3, j_going, j_end, ij_going, logits):
return j_going < j_end
def body_3(dis_3, j_going, j_end, ij_going, logits):
result_value = logits[ij_going] - logits[j_going]
dis_3 = tf.concat([dis_3, [result_value]], axis=0)
j_going = j_going + 1
return dis_3, j_going, j_end, ij_going, logits
j_begin = i_going + 1
j_end = i_end
dis_res2_result, _, _, _, _ = tf.while_loop(cond_3, body_3, (dis_2, j_begin, j_end, i_going, logits))
i_going = i_going + 1
return dis_res2_result, i_going, i_end
i_begin = k_going*level
i_end = (k_going+1)*level
dis_1_result, i_begin, i_end = tf.while_loop(cond_2, body_2, (dis_1, i_begin, i_end))
k_going = k_going + 1
return dis_1_result, k_going
dis_result, k_begin_result = tf.while_loop(cond_1,
body_1,
[dis, k_begin],
shape_invariants=[tf.TensorShape([None]), k_begin.get_shape()])
tf.expand_dims()
给定张量输入,此操作在输入形状的维度索引轴处插入1的尺寸。 尺寸索引轴从零开始; 如果您指定轴的负数,则从最后向后计数
# 't' is a tensor of shape [2]
shape(expand_dims(t, 0)) ==> [1, 2]
shape(expand_dims(t, 1)) ==> [2, 1]
shape(expand_dims(t, -1)) ==> [2, 1]
# 't2' is a tensor of shape [2, 3, 5]
shape(expand_dims(t2, 0)) ==> [1, 2, 3, 5]
shape(expand_dims(t2, 2)) ==> [2, 3, 1, 5]
shape(expand_dims(t2, 3)) ==> [2, 3, 5, 1]
tf.squeeze()
给定张量输入,此操作返回相同类型的张量,并删除所有尺寸为1的尺寸。 如果不想删除所有尺寸1尺寸,可以通过指定squeeze_dims来删除特定尺寸1尺寸。如果不想删除所有大小是1的维度,可以通过squeeze_dims指定。
# 't' is a tensor of shape [1, 2, 1, 3, 1, 1]
shape(squeeze(t)) ==> [2, 3]
Or, to remove specific size 1 dimensions:
# 't' is a tensor of shape [1, 2, 1, 3, 1, 1]
shape(squeeze(t, [2, 4])) ==> [1, 2, 3, 1]
tf.layers.batch_normalization()
应当特别注意该api结构的实现部分,因为在使用时产生了moving_mean和moving_variance这两个参数。如果不刻意保存这两个变量,那么训练train阶段和推理inference阶段的结果会相差很多。具体用法如下:
BN操作:y=γ(x-μ)/σ+β
其中x是输入,y是输出,μ是均值,σ是方差,γ和β是缩放(scale)、偏移(offset)系数。其详细原理可参考该博客
其他额外不常用的的关于归一化的操作,可参考该文章
1,使用阶段,卷积->bn->激活函数。
conv1 = tf.layers.conv2d
bn = tf.layers.batch_normalization(conv1, training=True) // inference 时training为False
relu = tf.nn.relu(bn)
2. 训练时需要添加如下代码:
update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(update_ops):
optimizer = tf.train.AdamOptimizer(learning_rate).minimize(loss)
3.保存时需要注意,
预测时比较特别,因为这一步一般都是从checkpoint文件中读取模型参数,然后做预测。一般来说,保存checkpoint的时候,不会把所有模型参数都保存下来,因为一些无关数据会增大模型的尺寸,常见的方法是只保存那些训练时更新的参数(可训练参数),如下:
var_list = tf.trainable_variables()
saver = tf.train.Saver(var_list=var_list, max_to_keep=5)
但使用了batch_normalization,γ和β是可训练参数没错,μ和σ不是,它们仅仅是通过滑动平均计算出的,如果按照上面的方法保存模型,在读取模型预测时,会报错找不到μ和σ。不过好在所有的参数都在
tf.global_variables()中,因此可以这么写,并且训练和推理阶段都需要添加
var_list = tf.trainable_variables()
g_list = tf.global_variables()
bn_moving_vars = [g for g in g_list if 'moving_mean' in g.name]
bn_moving_vars += [g for g in g_list if 'moving_variance' in g.name]
var_list += bn_moving_vars
saver = tf.train.Saver(var_list=var_list, max_to_keep=5)
# 或者直接全部保存
# saver = tf.train.Saver(tf.global_variables())
4. training参数设置问题
可以将该参数设置为placeholder,在train或inference时依据其情况添加。
training = tf.placeholder(tf.bool)
tf.control_dependencies和tf.GraphKeys.UPDATE_OPS
在使用bn时提到了这两个函数,在这里解释一下这两个函数的功能:具体参开了该博客
tf.control_dependencies,该函数保证其辖域中的操作必须要在该函数所传递的参数中的操作完成后再进行。请看下面一个例子。
import tensorflow as tf
a_1 = tf.Variable(1)
b_1 = tf.Variable(2)
update_op = tf.assign(a_1, 10)
add = tf.add(a_1, b_1)
a_2 = tf.Variable(1)
b_2 = tf.Variable(2)
update_op = tf.assign(a_2, 10)
with tf.control_dependencies([update_op]):
add_with_dependencies = tf.add(a_2, b_2)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
ans_1, ans_2 = sess.run([add, add_with_dependencies])
print("Add: ", ans_1)
print("Add_with_dependency: ", ans_2)
输出:
Add: 3
Add_with_dependency: 12
可以看到两组加法进行的对比,正常的计算图在计算add时是不会经过update_op操作的,因此在加法时a的值为1,但是采用tf.control_dependencies函数,可以控制在进行add前先完成update_op的操作,因此在加法时a的值为10,因此最后两种加法的结果不同。
关于tf.GraphKeys.UPDATE_OPS,这是一个tensorflow的计算图中内置的一个集合,其中会保存一些需要在训练操作之前完成的操作,并配合tf.control_dependencies函数使用。
关于在batch_norm中,即为更新mean和variance的操作。通过下面一个例子可以看到tf.layers.batch_normalization中是如何实现的。
import tensorflow as tf
is_traing = tf.placeholder(dtype=tf.bool)
input = tf.ones([1, 2, 2, 3])
output = tf.layers.batch_normalization(input, training=is_traing)
update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
print(update_ops)
# with tf.control_dependencies(update_ops):
# train_op = optimizer.minimize(loss)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
saver = tf.train.Saver()
saver.save(sess, "batch_norm_layer/Model")
输出:
[<tf.Tensor 'batch_normalization/AssignMovingAvg:0' shape=(3,) dtype=float32_ref>, <tf.Tensor 'batch_normalization/AssignMovingAvg_1:0' shape=(3,) dtype=float32_ref>]
可以看到输出的即为两个batch_normalization中更新mean和variance的操作,需要保证它们在train_op前完成。
这两个操作是在tensorflow的内部实现中自动被加入tf.GraphKeys.UPDATE_OPS这个集合的,在tf.contrib.layers.batch_norm的参数中可以看到有一项updates_collections的默认值即为tf.GraphKeys.UPDATE_OPS,而在tf.layers.batch_normalization中则是直接将两个更新操作放入了上述集合。
tf.layers.dense 和 tf.layers.flatten
flatten的作用在于,相当于保留第0轴的维度,其他的全部归集到第1轴上,例如:
x=tf.placeholder(shape=(None,4,4),dtype='float32')
y=tf.layers.Flatten()(x)
print(y)
# 结果
Tensor("flatten/Reshape:0", shape=(?, 16), dtype=float32)
dense相当于一个全连接层,其中有两个必填参数:inputs,units。
inputs是输入,units时输出的维度。,假如inputs的shape=[n, m],intis=t,那么经过dense的结果的维度为:[n,t]
from tensorflow.contrib.framework import arg_scope
对于一些OP,可能存在重复行的操作,添加arg_scope可以使相同的OP使用同一种初始化。以下两步的卷积的初始化以及正则化的初始值都相同。当然你也可以用其他操作,比如BN,池化pool等
with arg_scope([tf.layers.conv2d], padding='SAME',
initializer=layers.variance_scaling_initializer(),
regularizer=layers.l2_regularizer(0.01)):
net = tf.layers.conv2d(inputs, 64, [3, 3], 4, padding='VALID', scope='conv1')
net = tf.layers.conv2d(net, 256, [5, 5], scope='conv2')
tf.trainable_variables(), tf.all_variables(), tf.global_variables()
tf.trainable_variables(), tf.all_variables(), 类似,都是保存可训练参数,tf.global_variables()保存所有参数。
tf_config = tf.ConfigProto()
该函数时用来在创建session的时候,进行一个参数上的配置。
config = tf.ConfigProto(allow_soft_placement=True, allow_soft_placement=True)
config.gpu_options.per_process_gpu_memory_fraction = 0.4 #设置显存最大内存使用率
# config.gpu_options.allow_growth = True,对显存使用率不限制
sess = tf.Session(config=config)
log_device_placement = True ,可以获取到 operations 和 Tensor 被指派到哪个设备(几号CPU或几号GPU)上运行,会在终端打印出各项操作是在哪个设备上运行的。在tf中,通过命令 "with tf.device('/cpu:0'):",允许手动设置操作运行的设备。如果手动设置的设备不存在或者不可用,就会导致tf程序等待或异常,为了防止这种情况,可以设置tf.ConfigProto()中参数allow_soft_placement=True,允许tf自动选择一个存在并且可用的设备来运行操作
可变学习率
now_step = tf.placeholder(tf.int32)
lr = tf.train.cosine_decay_restarts(0.0005, self.now_step, 20, t_mul=1.0, m_mul=1.0, alpha=0.0) # 0.001
optimizer=tf.train.AdamOptimizer(lr).minimize(cost)
tf.pad
def test_pad():
vct = tf.Variable(tf.ones([3, 4]), name="vct")
# 指定填充方式,
pad1 = np.array([[1, 2], [3, 4]])
pad2 = np.array([[2, 2], [2, 2]])
# tf.pad进行填充
vct_pad1 = tf.pad(vct, pad1, name='pad_1')
vct_pad2 = tf.pad(vct, pad2, name='pad_2')
# 创建会话
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print(sess.run(vct))
print(sess.run(vct_pad1))
print(sess.run(vct_pad2))
得出的结果为:
[[1. 1. 1. 1.]
[1. 1. 1. 1.]
[1. 1. 1. 1.]]
[[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 1. 1. 1. 1. 0. 0. 0. 0.]
[0. 0. 0. 1. 1. 1. 1. 0. 0. 0. 0.]
[0. 0. 0. 1. 1. 1. 1. 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. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 1. 1. 1. 1. 0. 0.]
[0. 0. 1. 1. 1. 1. 0. 0.]
[0. 0. 1. 1. 1. 1. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0.]]
这里的cvt是两维数据,那么对应的pad1里面的元素也是两个,而且pad里面的元素,是一个1*2的数组。可以简单的理解成左右pading的数目。如果cvt的shape是[2,3,4],那么pad得是[[a,b],[m,n],[q,p]]这种形式。一个元素对应cvt的一个维度的pading方式。最后一个参数为pading的数值该怎么定义,可以参考该博客
tensorflow 运行时内存持续增长
tensorflow 训练部分代码几乎都采用循环,如果在循环中用到tf的op操作,就会导致节点越来越多,图的大小也不停的增大,故而内存会持续增长
常见情况有几种
1、sess.run(a+b)
写成sess.run(add_a_b)最好
2、tf.train.Saver()
3.不要使用额外的图
原因是在对每一张图片正向传播结束后,都会在留下中间信息。具体地说是在我将正向传播的代码与模型的代码分离了,在每次遍历图片时都会正向传播,在tensorflow中新增加了很多的计算节点(如tf.matmul等等),导致内存中遗留了大量的过期信息。
纠正的做法就是一个前提:避免在循环训练图片时额外使用tf计算资源。
使用placeholder作为输入数据的入口,在模型中定义需要使用的函数,包括正向传播。不要在遍历图片时额外使用tf计算。
遇到这种问题一定要回头检查代码,尤其是在别人写的代码基础上改时,引用自传送门