我们能在Keras中使用tf.spectral傅里叶函数吗?

9

让我们从一个简单的时间序列开始,并尝试在keras中构建一个自动编码器,该编码器仅对数据进行傅里叶变换,然后再进行反变换。

如果我们尝试这样做:

inputs = Input(shape=(MAXLEN,1), name='main_input')
x = tf.spectral.rfft(inputs)
decoded = Lambda(tf.spectral.irfft)(x)

然后输入第三行代码时会产生一个错误:

>> ValueError: Tensor conversion requested dtype complex64 for Tensor with dtype float32

您看,tf.spectral.irfft的输出是float32类型,但Lambda似乎认为它是complex64类型?(Complex64是上一步的输入x)

我们可以通过以下方式在模型输入时修复该错误:

inputs = Input(shape=(MAXLEN,1), name='main_input')
x = tf.spectral.rfft(inputs)
decoded = Lambda(tf.cast(tf.spectral.irfft(x),dtype=tf.float32)))

这段文本的意思是:在输入时这是被接受的,但然后当我们尝试构建模型时出现了问题。
autoencoder = Model(inputs, decoded)

它生成以下错误:

TypeError: Output tensors to a Model must be Keras tensors. Found: <keras.layers.core.Lambda object at 0x7f24f0f7bbe0>

我想这是合理的,这也是我一开始不想投它的原因。

主要问题:如何成功地包装tf.spectral.irfft函数,该函数输出float32?

更一般的学习问题: 假设我实际上想在rfft和irfft之间执行某些操作,如何将这些虚数转换为绝对值而不破坏keras,以便可以应用各种卷积等操作?

3个回答

8

我认为你只需要更多的 Lambda 包装(使用 tf.keras,因为我安装了这个):

import numpy
import tensorflow as tf
K = tf.keras

inputs = K.Input(shape=(10, 8), name='main_input')
x = K.layers.Lambda(tf.spectral.rfft)(inputs)
decoded = K.layers.Lambda(tf.spectral.irfft)(x)
model = K.Model(inputs, decoded)
output = model(tf.ones([10, 8]))
with tf.Session():
  print(output.eval())
irfft的输出应该是实数,所以可能不需要进行类型转换。但如果您确实需要进行类型转换(或者通常在Lambda层中组合操作),我建议将其包装在Python lambda中:K.layers.Lambda(lambda v: tf.cast(tf.spectral.whatever(v), tf.float32)) 例如,如果您知道您的中间值(在rfftirfft之间)将具有零的虚数分量,则可以截断它:
import numpy
import tensorflow as tf
K = tf.keras

inputs = K.Input(shape=(10, 8), name='main_input')
x = K.layers.Lambda(lambda v: tf.real(tf.spectral.rfft(v)))(inputs)
decoded = K.layers.Lambda(
    lambda v: tf.spectral.irfft(tf.complex(real=v, imag=tf.zeros_like(v))))(x)
model = K.Model(inputs, decoded)
output = model(tf.reshape(tf.range(80, dtype=tf.float32), [10, 8]))
with tf.Session():
  print(output.eval())

请注意,对于一般序列而言,这并不适用,因为即使是实值输入,在变换后也可能有虚数分量。它适用于上面的tf.ones输入,但tf.range输入会被破坏:
[[ 0.  4.  4.  4.  4.  4.  4.  4.]
 [ 8. 12. 12. 12. 12. 12. 12. 12.]
 [16. 20. 20. 20. 20. 20. 20. 20.]
 [24. 28. 28. 28. 28. 28. 28. 28.]
 [32. 36. 36. 36. 36. 36. 36. 36.]
 [40. 44. 44. 44. 44. 44. 44. 44.]
 [48. 52. 52. 52. 52. 52. 52. 52.]
 [56. 60. 60. 60. 60. 60. 60. 60.]
 [64. 68. 68. 68. 68. 68. 68. 68.]
 [72. 76. 76. 76. 76. 76. 76. 76.]]
<不进行转换,我们得到的是0.到79.之间的重建完美结果>

我将其标记为正确,因为我无法否认它按照所示运行 :) 即使我使用tf.keras,它似乎也不接受rfft和irfft之间的其他层类型。例如,如果我尝试在这两个层之间放置K.layers.Conv1D,即使包装在Lambda中,我也会得到以下错误消息:“传递给参数'input'的值具有DataType complex64,而不在允许的值列表中:”,尽管rfft据说具有实际输出。您知道如何在rfft和irfft之间放置卷积吗? - Ezekiel Kruglick
2
rfft返回complex64,所以我认为dtype不是一个bug(虽然卷积应该能够处理复数?)。如果您知道它是实数,您可以将其转换/截断掉复杂的部分。我会添加一个示例。 - Allen Lavoie
4
一种选择是将实部/虚部分别拆分到不同的通道中(在一个通道中使用tf.real(),在另一个通道中使用tf.imag()),用于卷积处理。 - Allen Lavoie
这是一个FFT,所以我需要幅度,我会尝试使用数值运算。谢谢! - Ezekiel Kruglick
这个回答与 @Mark.F 的 https://datascience.stackexchange.com/a/42806/27662 结合起来帮助了我。 - Robert Lugg

2
我发现这篇文章时正试图解决同样的问题。您可以通过将tf.realtf.imag包装到Lambda层中(我正在使用stft因为没有实值等效物)来使转换无损:


x = tf.keras.layers.Lambda(
    lambda v: tf.signal.stft(
        v,
        frame_length=1024,
        frame_step=256,
        fft_length=1024,
    ), name='gen/FFTLayer')(inputs)
real = tf.keras.layers.Lambda(tf.real)(x)
imag = tf.keras.layers.Lambda(tf.imag)(x)
...
# transform real and imag either separately or by concatenating them in the feature space.
...
x = tf.keras.layers.Lambda(lambda x: tf.complex(x[0], x[1]))([real, imag])
x = tf.keras.layers.Lambda(
    lambda v: tf.signal.inverse_stft(
        v,
        frame_length=1024,
        frame_step=256,
        fft_length=1024,
    ))(x)

1

如果有人通过搜索引擎来到这里,我想要补充更多关于以上内容的信息。在这个谷歌讨论组中提供的以下内容将使用卷积和其他层运行rfft然后ifft:

inputs = Input(shape=(10, 8), name='main_input')
x = Lambda(lambda v: tf.to_float(tf.spectral.rfft(v)))(inputs)
x = Conv1D(filters=5, kernel_size=3, activation='relu', padding='same')(x)
x = Lambda(lambda v: tf.to_float(tf.spectral.irfft(tf.cast(v, dtype=tf.complex64))))(x)
x = Flatten()(x)
output = Dense(1)(x)
model = Model(inputs, output)
model.summary()

它使用了与Allen的答案相同的概念,但是轻微的差异允许与中间卷积兼容。

tf.to_float 会丢弃虚数部分吗? - endolith
rfft仅沿一个轴作用? - endolith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接