tensorflow频域操作及梯度求取

tensorflow频域操作及梯度求取

最近尝试使用tensorflow中的傅立叶变换操作,主要涉及的op有tf.complextf.fft, tf.fft2d,tf.angle, 涉及的数据类型为tf.complex64, 这里主要介绍以下几个部分:

  • 一维离散傅立叶变换函数:tf.fft使用示例
  • 复数梯度求取算子:tf.gradients使用示例
  • 阻挡梯度的传播:tf.stop_gradient()使用示例
  • 高阶导数的求取:tf.gradients高阶示例

部分内容节选自tensorflow学习笔记(三十):tf.gradients 与 tf.stop_gradient() 与 高阶导数


一、一维离散傅立叶变换函数:tf.fft使用示例

import tensorflow as tf
# 两种初始化复数的办法
# 方法一:tf.cast():弊病,tf.cast()无法回传梯度
input_real=tf.constant([2,3])
input_complex=tf.cast(input_real ,tf.complex64)

#方法二:分别设置实部和虚部,用complex拼接
real=tf.constant([2.25, 3.25])
imag=tf.constant([4.75, 5.75])
complex=tf.complex(real, imag)

#方法三:初始化一个跟实部相同shape的虚部,用complex拼接:可以回传梯度
tf.complex(sqrtmag, tf.zeros(sqrtmag.shape))

fft=tf.fft(complex)
angle=tf.angle(fft)

二、复数梯度求取算子:tf.gradients使用示例

1. grad_ys的测试:

这里要注意两点:

  1. grad_ys的shape应该与tf.gradients的第一个参数保持一致,[tf.convert_to_tensor([2.,2.,3.]),tf.convert_to_tensor([3.,2.,4.])][z1, z2]的shape是一致的
  2. 这里的数字实际上是给每一个维度的梯度增加一个乘法因子,如果都设置为1,则直接按照公式求取梯度即可,作用应该是在第三部分回传梯度时,可以自由设置不同维度的梯度值,但如果单纯只是需要求取表达式对于某一个变量的梯度,设置为1即可。
import tensorflow as tf
w1 = tf.get_variable('w1', shape=[3])
w2 = tf.get_variable('w2', shape=[3])
w3 = tf.get_variable('w3', shape=[3])
w4 = tf.get_variable('w4', shape=[3])

z1 = w1 + w2+ w3
z2 = w3 + w4
grads = tf.gradients([z1, z2], [w1, w2, w3, w4], grad_ys=[tf.convert_to_tensor([2.,2.,3.]),
                                                          tf.convert_to_tensor([3.,2.,4.])])
with tf.Session() as sess:
    tf.global_variables_initializer().run()
    print(sess.run(grads))

2. 复数tf.gradients和grad_ys的使用

使用示例

import tensorflow as tf
real=tf.constant([2.25, 3.25])
imag=tf.constant([4.75, 5.75])
complex=tf.complex(real, imag)

fft=tf.fft(complex)
angle=tf.angle(fft)
# complex对real的实部求梯度
grads_complex_real=tf.gradients(complex,real,grad_ys=tf.convert_to_tensor([1,1],dtype=tf.complex64))
# fft对real求梯度
grads_fft_real=tf.gradients(fft,real,grad_ys=tf.convert_to_tensor([1,1],dtype=tf.complex64))    
# fft对complex求梯
度
grads_fft_complex=tf.gradients(fft,complex,grad_ys=tf.convert_to_tensor([1,1],dtype=tf.complex64))  
# angle对fft求梯度
grads_angle_fft=tf.gradients(angle,fft,grad_ys=tf.convert_to_tensor([1,1],dtype=tf.complex64))
# angle对complex求梯度
grads_angle_complex=tf.gradients(angle,complex,grad_ys=tf.convert_to_tensor([1,1],dtype=tf.float32)
# angle对real求梯度
grads_angle_real=tf.gradients(angle,real,grad_ys=tf.convert_to_tensor([1,1],dtype=tf.float32))

sess=tf.InteractiveSession()
print(sess.run([angle,grads_fft_complex])

3. 频域loss函数注意事项:

  1. 使用频域fft或者angle作为loss函数的输入时,由于离散傅立叶变换公式中(参见网页),某一个频率点的值由complex中每一个值和其对应的指数项,求和之后得到,其中指数项根据欧拉公式就是一个复数。公式总结如下:
    fft=n1i=0(complex×fftcomplexmnuv) f f t = ∑ i = 0 n − 1 ( c o m p l e x 中 每 个 离 散 点 的 值 ( 复 数 ) × 欧 拉 复 数 因 子 ( 由 f f t 和 c o m p l e x 的 坐 标 决 定 , m , n 和 u , v ) )
    因此,fft值的数量级相当于是 255×width×height 255 × w i d t h × h e i g h t ,因此将fft作为loss函数的一部分时,fft的值会非常的大,在梯度回传是,根据离散傅立叶反变换,complex中每一个点的梯度,同样由 (fft×) ( f f t 中 每 个 离 散 点 的 值 × 欧 拉 复 数 因 子 ) 决定,所回传的实际梯度值的数量级应该是 (fftlabel)×width×height △ ( f f t − l a b e l ) × w i d t h × h e i g h t , 而 (fftlabel) △ ( f f t − l a b e l ) fft和标签label之间的差值,其数量级和fft的数量级相同为 255×width×height 255 × w i d t h × h e i g h t , 最终回传到complex中每一个点的梯度的数量级应该是 255×width2×height2 255 × w i d t h 2 × h e i g h t 2
  2. 使用tf.angle进行梯度回传到real或者imag的适合,由于tf.angle = tf.atan(tf.imag/tf.real)), 当中存在除法运算,在训练过程中可能会出现NAN的error。具体原因应该就是这个除法,有待进一步确定,解决办法暂时还没想到,也有待进一步研究。

三、阻挡梯度的传播:tf.stop_gradient()使用示例

阻挡节点BP的梯度

import tensorflow as tf
a = tf.Variable(1.0)
b = tf.Variable(1.0)
c = tf.add(a, b)
c_stoped = tf.stop_gradient(c)
d = tf.add(a, b)
e = tf.add(c_stoped, d)
gradients = tf.gradients(e, xs=[a, b])
with tf.Session() as sess:
    tf.global_variables_initializer().run()
    print(sess.run(gradients))
#输出 [1.0, 1.0]

虽然c节点被stop了,但是ab还有从d传回的梯度,所以还是可以输出梯度值的。

import tensorflow as tf
w1 = tf.Variable(2.0)
w2 = tf.Variable(2.0)
a = tf.multiply(w1, 3.0)
a_stoped = tf.stop_gradient(a)

# b=w1*3.0*w2
b = tf.multiply(a_stoped, w2)

opt = tf.train.GradientDescentOptimizer(0.1)
gradients = tf.gradients(b, xs=tf.trainable_variables())

tf.summary.histogram(gradients[0].name, gradients[0])# 这里会报错,因为gradients[0]是None
#其它地方都会运行正常,无论是梯度的计算还是变量的更新。总觉着tensorflow这么设计有点不好,
#不如改成流过去的梯度为0
train_op = opt.apply_gradients(zip(gradients, tf.trainable_variables()))

print(gradients)
with tf.Session() as sess:
    tf.global_variables_initializer().run()
    print(sess.run(train_op))
    print(sess.run([w1, w2])

四、高阶导数的求取:tf.gradients高阶示例

tensorflow 求 高阶导数可以使用tf.gradients来实现。
Note: 有些optf没有实现其高阶导的计算,例如 tf.add …, 如果计算了一个没有实现 高阶导的 op的高阶导, gradients 会返回 None

import tensorflow as tf

with tf.device('/cpu:0'):
    a = tf.constant(1.)
    b = tf.pow(a, 2)
    grad = tf.gradients(ys=b, xs=a) # 一阶导
    print(grad[0])
    grad_2 = tf.gradients(ys=grad[0], xs=a) # 二阶导
    grad_3 = tf.gradients(ys=grad_2[0], xs=a) # 三阶导
    print(grad_3)

with tf.Session() as sess:
    print(sess.run(grad_3))
  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值