Неявные нейронные представления с периодическими функциями активации

    Знакомые с нейронными сетями читатели скорее всего слышали про термин «функция активации». Такие варианты функции активации, как сигмоида, гиперболический тангенс (TanH) и ReLU (линейный выпрямитель), активно применяются в нейронных сетях и широко известны энтузиастам, занимающимся экспериментами с нейронными архитектурами. Исследователи нейронных сетей не останавливаются на достигнутом и подбирают альтернативы, позволяющие расширить границы возможностей. Один из вариантов подхода, предложенного в 2020 году, показывает выдающиеся результаты по сравнению с классическими функциями активации. Про впечатляющие отличия и пойдет речь в этой статье: на основе материала Vincent Sitzmann, Julien N. P. Martel, Alexander Bergman, David B. Lindell, Gordon Wetzstein  и кода на нескольких наглядных примерах будет продемонстрировано превосходство нового метода.  


    Для работы с сигналами можно использовать различные формы их представления. Простой пример — сигнал в форме таблицы, где для каждого временного шага имеется определенное значение переменной (температуры или др.). Такой сигнал будет представлен в дискретной форме, что в ряде случаев неудобно (так, при воспроизведении звука необходимо перейти от дискретного представления к непрерывному). Другой формой представления сигнала может быть заданная функция зависимости переменной f от времени t, тогда можно говорить о функции f(t). Такая форма представления сигнала имеет ряд преимуществ, например, возможность экстраполяции. Сигнал, разложенный на ряд Фурье, также представляет из себя удобную форму, позволяющую рассчитать спектр мощности, выделить характерные частоты и др. Интересно, что нейронная сеть также является формой представления сигнала, и свойства такого представления напрямую связаны с функцией активации нейронов. 

    У функции активации есть ряд желательных свойств, которые позволяют расширить возможности использования нейронных сетей. Например, крайне желательна нелинейность функции активации, т.к. в этом случае можно доказать, что двухуровневая нейронная сеть будет универсальным аппроксиматором функции. Другое желательное свойство — непрерывная дифференцируемость для обеспечения методов оптимизации на основе градиентного спуска. При этом популярная функция RELU не является непрерывно дифференцируемой и имеет проблемы с оптимизацией на основе градиентного спуска. Еще одно полезное свойство — возможность аппроксимации тождественной функции около начала координат. Если функции активации имеют это свойство, то нейронная сеть будет обучаться эффективно в случае инициализации весов малыми случайными значениями. Если функция активации не аппроксимирует тождество около начала координат, то приходится быть осторожными при инициализации весов. 

    Для использования синусоидальной функции активации получаем следующую формулу:


    Здесь φi: RMi → RNi обозначает i-ый слой сети, Wi ∈ R Ni×Mi представляет собой матрицу весов, ответственную за аффинные преобразования сигнала, а bi ∈ RNi представляет собой смещение  к вектору xi ∈ RMi

    Интересно, что производная от предложенной функции активации также является синусоидальной функцией, т.к. производная от функции синуса является функция косинуса, которая в свою очередь является сдвинутой по фазе функцией синуса. Кроме того, синусоидальная функция активации позволяет аппроксимировать тождественную функцию около начала координат.  

    Таким образом, синусоидальная функция активации обладает рядом полезных свойств, таких, как нелинейность, непрерывная дифференцируемость и возможность аппроксимации тождественной функции около начала координат. Теперь давайте перейдем к теме статьи Vincent Sitzmann, Julien N. P. Martel, Alexander Bergman, David B. Lindell, Gordon Wetzstein.


    Слабой стороной современных нейронных сетей является неспособность моделировать сигналы с мелкими деталями. Кроме того, современные нейронные сети не могут представлять пространственные и временные производные сигнала, что ограничивает возможности для их применения в физическом моделировании: производные оказывается важным элементом, неявно определенным через уравнения в частных производных, описывающих физические процессы. 

    Форма представления сигнала, параметризованная с помощью нейронной сети, и обладающая свойствами неявного определения, непрерывности и дифференцируемости, представляет собой отдельное направление в развитии нейронных архитектур. Такой подход способен предложить ряд преимуществ по сравнению с традиционными представлениями и поэтому может быть выделен в отдельную парадигму. Авторы статьи, в рамках новой парадигмы, предлагают использовать функции периодической активации для неявных нейронных представлений. Новый подход, под названием Сети Синусоидального Представления (sinusoidal representation networks) или SIREN, в работе авторов демонстрирует впечатляющие результаты в приложении к  сложным сигналам физической природы и их производным.

    В основной работе авторы анализируют статистические свойства активации SIREN, чтобы предложить принципиальную новую схему задания начальных значений для весов сети, и демонстрируют работу с изображениями, волновыми полями, видео, звуком и их производными. Далее авторы показывают, как можно использовать SIREN для решения сложных краевых задач, таких, как точное уравнение Эйконала (с получением функций расстояния со знаком), уравнение Пуассона, а также уравнения Гельмгольца и уравнения волнового движения. Наконец, авторы в своей статье объединили SIREN с гиперсетями для изучения априорных вероятностей в пространстве функций SIREN, однако этот материал уже выходит за рамки нашего обзора.

    Baselines


    Следующие результаты сравнивают SIREN с различными сетевыми архитектурами. TanH, ReLU, Softplus и т. д. означает Multi Layer Perceptron одинакового размера с соответствующей функцией нелинейности. Авторы также сравнивают недавно предложенное позиционное кодирование (Partial Encoding) в сочетании с нелинейной функцией активации ReLU, обозначенной, как ReLU P.E. SIREN существенно превосходит все baseline результаты, сходится значительно быстрее и является единственной архитектурой, точно отражающей градиенты сигнала, позволяя тем самым использование в решении краевых задач.

    Представление изображений 


    SIREN, отображающая координаты 2D-пикселей в цвета, может быть использована для параметризации изображений. В данном случае авторы напрямую активируют SIREN, используя истинные значения пикселей. В данных примерах SIREN удается успешно аппроксимировать изображение, получая на 10 дБ более высокое значение PSNR в условиях меньшего количества итераций по сравнению с конкурентами. Кроме того, SIREN является единственным представителем Multi Layer Perceptron, которому удается точно отразить производные первого и второго порядка.



    Краткое демо


    (код взят из https://colab.research.google.com/github/vsitzmann/siren/blob/master/explore_siren.ipynb)

    Для начала, импортируем библиотеки и добавим вспомогательную функцию для визуализации 

    import torch
    from torch import nn
    import torch.nn.functional as F
    from torch.utils.data import DataLoader, Dataset
    import os
     
    from PIL import Image
    from torchvision.transforms import Resize, Compose, ToTensor, Normalize
    import numpy as np
    import skimage
    import matplotlib.pyplot as plt
     
    import time
     
    def get_mgrid(sidelen, dim=2):
       '''Generates a flattened grid of (x,y,...) coordinates in a range of -1 to 1.
       sidelen: int
       dim: int'''
       tensors = tuple(dim * [torch.linspace(-1, 1, steps=sidelen)])
       mgrid = torch.stack(torch.meshgrid(*tensors), dim=-1)
       mgrid = mgrid.reshape(-1, dim)
       return mgrid
    

    Теперь добавим реализацию синусоидального слоя, который будет основным строительным блоком SIREN. В данном случае используется гораздо более лаконичная реализация, чем в основном коде авторов, однако упрощение оправдано тем, что здесь не преследуется цель сравнения с baseline решениями.

    
    class SineLayer(nn.Module):
       # See paper sec. 3.2, final paragraph, and supplement Sec. 1.5 for discussion of omega_0.
      
       # If is_first=True, omega_0 is a frequency factor which simply multiplies the activations before the
       # nonlinearity. Different signals may require different omega_0 in the first layer - this is a
       # hyperparameter.
      
       # If is_first=False, then the weights will be divided by omega_0 so as to keep the magnitude of
       # activations constant, but boost gradients to the weight matrix (see supplement Sec. 1.5)
      
       def __init__(self, in_features, out_features, bias=True,
                    is_first=False, omega_0=30):
           super().__init__()
           self.omega_0 = omega_0
           self.is_first = is_first
          
           self.in_features = in_features
           self.linear = nn.Linear(in_features, out_features, bias=bias)
          
           self.init_weights()
      
       def init_weights(self):
           with torch.no_grad():
               if self.is_first:
                   self.linear.weight.uniform_(-1 / self.in_features,
                                                1 / self.in_features)     
               else:
                   self.linear.weight.uniform_(-np.sqrt(6 / self.in_features) / self.omega_0,
                                                np.sqrt(6 / self.in_features) / self.omega_0)
          
       def forward(self, input):
           return torch.sin(self.omega_0 * self.linear(input))
      
       def forward_with_intermediate(self, input):
           # For visualization of activation distributions
           intermediate = self.omega_0 * self.linear(input)
           return torch.sin(intermediate), intermediate
      
      
    class Siren(nn.Module):
       def __init__(self, in_features, hidden_features, hidden_layers, out_features, outermost_linear=False,
                    first_omega_0=30, hidden_omega_0=30.):
           super().__init__()
          
           self.net = []
           self.net.append(SineLayer(in_features, hidden_features,
                                     is_first=True, omega_0=first_omega_0))
     
           for i in range(hidden_layers):
               self.net.append(SineLayer(hidden_features, hidden_features,
                                         is_first=False, omega_0=hidden_omega_0))
     
           if outermost_linear:
               final_linear = nn.Linear(hidden_features, out_features)
              
               with torch.no_grad():
                   final_linear.weight.uniform_(-np.sqrt(6 / hidden_features) / hidden_omega_0,
                                                 np.sqrt(6 / hidden_features) / hidden_omega_0)
                  
               self.net.append(final_linear)
           else:
               self.net.append(SineLayer(hidden_features, out_features,
                                         is_first=False, omega_0=hidden_omega_0))
          
           self.net = nn.Sequential(*self.net)
      
       def forward(self, coords):
           coords = coords.clone().detach().requires_grad_(True) # allows to take derivative w.r.t. input
           output = self.net(coords)
           return output, coords       
     
       def forward_with_activations(self, coords, retain_grad=False):
           '''Returns not only model output, but also intermediate activations.
           Only used for visualizing activations later!'''
           activations = OrderedDict()
     
           activation_count = 0
           x = coords.clone().detach().requires_grad_(True)
           activations['input'] = x
           for i, layer in enumerate(self.net):
               if isinstance(layer, SineLayer):
                   x, intermed = layer.forward_with_intermediate(x)
                  
                   if retain_grad:
                       x.retain_grad()
                       intermed.retain_grad()
                      
                   activations['_'.join((str(layer.__class__), "%d" % activation_count))] = intermed
                   activation_count += 1
               else:
                   x = layer(x)
                  
                   if retain_grad:
                       x.retain_grad()
                      
               activations['_'.join((str(layer.__class__), "%d" % activation_count))] = x
               activation_count += 1
     
           return activations
    

    И, наконец, дифференциальные операторы, которые позволяют использовать torch.autograd для вычисления градиентов и лапласианов.

    
    def laplace(y, x):
       grad = gradient(y, x)
       return divergence(grad, x)
     
     
    def divergence(y, x):
       div = 0.
       for i in range(y.shape[-1]):
           div += torch.autograd.grad(y[..., i], x, torch.ones_like(y[..., i]), create_graph=True)[0][..., i:i+1]
       return div
     
     
    def gradient(y, x, grad_outputs=None):
       if grad_outputs is None:
           grad_outputs = torch.ones_like(y)
       grad = torch.autograd.grad(y, [x], grad_outputs=grad_outputs, create_graph=True)[0]
       return grad
    

    Для экспериментов используется классическое изображение оператора.

    
    def get_cameraman_tensor(sidelength):
       img = Image.fromarray(skimage.data.camera())       
       transform = Compose([
           Resize(sidelength),
           ToTensor(),
           Normalize(torch.Tensor([0.5]), torch.Tensor([0.5]))
       ])
       img = transform(img)
       return img
    

    Далее просто «фитируем» это изображение. В процессе аппроксимации мы стремимся параметризовать изображение f(x) в оттенках серого с пиксельными координатами x с помощью функции Φ(x). То есть мы ищем функцию Φ такую, что функционал L = ∫Ω∥Φ(x) − f(x)∥dx минимизируется. При этом Ω является областью изображения. Используем небольшой датасет, который вычисляет попиксельные координаты:

    
    class ImageFitting(Dataset):
       def __init__(self, sidelength):
           super().__init__()
           img = get_cameraman_tensor(sidelength)
           self.pixels = img.permute(1, 2, 0).view(-1, 1)
           self.coords = get_mgrid(sidelength, 2)
     
       def __len__(self):
           return 1
     
       def __getitem__(self, idx):   
           if idx > 0: raise IndexError
              
           return self.coords, self.pixels
    

    Далее создадим экземпляр датасета и SIREN. Поскольку пиксельные координаты являются двумерным вектором, SIREN получает на вход два признака, а поскольку изображение в оттенках серого, размерность выходного канала равна 1.

    
    cameraman = ImageFitting(256)
    dataloader = DataLoader(cameraman, batch_size=1, pin_memory=True, num_workers=0)
     
    img_siren = Siren(in_features=2, out_features=1, hidden_features=256,
                     hidden_layers=3, outermost_linear=True)
     
    img_siren.cuda()
    

    Теперь обучаем нейронную сеть в простом цикле. Всего за несколько сотен итераций хорошо аппроксимируются как изображение, так и его градиенты.

    
    total_steps = 500 # Since the whole image is our dataset, this just means 500 gradient descent steps.
    steps_til_summary = 10
     
    optim = torch.optim.Adam(lr=1e-4, params=img_siren.parameters())
     
    model_input, ground_truth = next(iter(dataloader))
    model_input, ground_truth = model_input.cuda(), ground_truth.cuda()
     
    for step in range(total_steps):
       model_output, coords = img_siren(model_input)   
       loss = ((model_output - ground_truth)**2).mean()
      
       if not step % steps_til_summary:
           print("Step %d, Total loss %0.6f" % (step, loss))
           img_grad = gradient(model_output, coords)
           img_laplacian = laplace(model_output, coords)
     
           fig, axes = plt.subplots(1,3, figsize=(18,6))
           axes[0].imshow(model_output.cpu().view(256,256).detach().numpy())
           axes[1].imshow(img_grad.norm(dim=-1).cpu().view(256,256).detach().numpy())
           axes[2].imshow(img_laplacian.cpu().view(256,256).detach().numpy())
           plt.show()
     
       optim.zero_grad()
       loss.backward()
       optim.step()
    






    Представление аудио


    Начнем с небольшого эксперимента.

    Будем использовать SIREN для параметризации аудиосигнала, то есть стремимся параметризовать звуковую волну f(t) в моменты времени t с помощью функции Φ. Для этого ищем функцию Φ такую, что: функция потерь L = ∫Ω∥Φ(t) −f(t)∥dt минимизируется, где Ω является звуковой волной. Для эксперимента будем использовать сонату Баха:

    
    import scipy.io.wavfile as wavfile
    import io
    from IPython.display import Audio
     
    if not os.path.exists('gt_bach.wav'):
       !wget https://vsitzmann.github.io/siren/img/audio/gt_bach.wav
    

    Создадим небольшой датасет, который позволяет перевести аудиофайл в более удобный для работы формат:

    
    class AudioFile(torch.utils.data.Dataset):
       def __init__(self, filename):
           self.rate, self.data = wavfile.read(filename)
           self.data = self.data.astype(np.float32)
           self.timepoints = get_mgrid(len(self.data), 1)
     
       def get_num_samples(self):
           return self.timepoints.shape[0]
     
       def __len__(self):
           return 1
     
       def __getitem__(self, idx):
           amplitude = self.data
           scale = np.max(np.abs(amplitude))
           amplitude = (amplitude / scale)
           amplitude = torch.Tensor(amplitude).view(-1, 1)
           return self.timepoints, amplitude
    

    Далее создадим экземпляр SIREN. Поскольку звуковой сигнал имеет гораздо более высокую пространственную частоту в диапазоне от -1 до 1, поэтому увеличиваем ω0 в первом слое SIREN.

    
    bach_audio = AudioFile('gt_bach.wav')
     
    dataloader = DataLoader(bach_audio, shuffle=True, batch_size=1, pin_memory=True, num_workers=0)
     
    # Note that we increase the frequency of the first layer to match the higher frequencies of the
    # audio signal. Equivalently, we could also increase the range of the input coordinates.
    audio_siren = Siren(in_features=1, out_features=1, hidden_features=256,
                       hidden_layers=3, first_omega_0=3000, outermost_linear=True)
    audio_siren.cuda()
    

    Давайте прослушаем исходные данные:

    
    rate, _ = wavfile.read('gt_bach.wav')
     
    model_input, ground_truth = next(iter(dataloader))
    Audio(ground_truth.squeeze().numpy(),rate=rate)
    

    Далее начнем обучение нейронной сети:

    
    total_steps = 1000
    steps_til_summary = 100
     
    optim = torch.optim.Adam(lr=1e-4, params=audio_siren.parameters())
     
    model_input, ground_truth = next(iter(dataloader))
    model_input, ground_truth = model_input.cuda(), ground_truth.cuda()
     
    for step in range(total_steps):
       model_output, coords = audio_siren(model_input)   
       loss = F.mse_loss(model_output, ground_truth)
      
       if not step % steps_til_summary:
           print("Step %d, Total loss %0.6f" % (step, loss))
      
           fig, axes = plt.subplots(1,2)
           axes[0].plot(coords.squeeze().detach().cpu().numpy(),model_output.squeeze().detach().cpu().numpy())
           axes[1].plot(coords.squeeze().detach().cpu().numpy(),ground_truth.squeeze().detach().cpu().numpy())
           plt.show()
     
       optim.zero_grad()
       loss.backward()
       optim.step()
    





    Послушаем, что получается в итоге:

    
    final_model_output, coords = audio_siren(model_input)
    Audio(final_model_output.cpu().detach().squeeze().numpy(),rate=rate)
    

    SIREN с одиночным входом, соответствующим координате времени, и скалярным выходом действительно может успешно параметризировать аудиосигналы. SIREN — единственная сетевая архитектура, которая успешно воспроизводит аудиосигнал как для музыки, так и для человеческого голоса.


    Представление видео


    Использование SIREN совместно на координатах пикселей и времени позволяет параметризовать видео. В данном случае SIREN непосредственно активируется на истинных значениях пикселей и позволяет параметризовать видео существенно лучше, чем ReLU Multi Layer Perceptron.


    Решение уравнения Пуассона


    Используя только производные в подходе SIREN, возможно решить уравнение Пуассона. SIREN — это опять-таки единственная архитектура, которая аппроксимирует изображения, градиенты и области лапласиан точно и быстро. 



    Представление фигур путем решения уравнения Эйконала Interactive 3D SDF Viewer — используйте мышь для навигации по сценам


    Решая краевую задачу в форме уравнений Эйконала, мы можем восстановить SDF из облака точек и нормалей поверхности. Подход SIREN позволяет восстановить сцену масштаба комнаты на основе только облака точек и нормалей поверхности, при этом удается точно воспроизвести мелкие детали, а для обучения требуется менее одного часа. В отличие от недавних работ по объединению воксельных сеток с нейронными неявными представлениями, в предлагаемом подходе полное представление хранится в весах одной пятислойной нейронной сети, без 2D или 3D-сверток, и поэтому требует гораздо меньшего количества параметров. Важно обратить внимание на то, что полученные SDF не обучаются на исходных значениях SDF, а скорее являются результатом решения вышеупомянутой эйкональной краевой задачи. Такая постановка задачи является существенно более сложной и требует обучения с учителем по градиентам (детали — в статье). В результате архитектуры сетей, градиенты которых хуже контролируются, показывают качество ниже, чем в подходе SIREN.



    Решение уравнения Гельмгольца


    В данном случае авторы используют подход SIREN для решения неоднородного уравнения Гельмгольца. Архитектуры на основе ReLU и Tanh не позволяют полностью решить эту задачу.


    Решение волнового уравнения


    При работе со временем, подход  SIREN позволяет успешно решить волновое уравнение, в отличие от архитектуры на основе Tanh, которая не смогла сойтись к правильному решению.


    * * *


    Предлагаемое авторами представление SIREN для глубокого обучения помогает в работе с такими данными, как изображения, аудио- и видеосигналы. Новый подход может быть полезным в таких областях, как классификация изображений или speech-to-text в рамках работы со звуком. За счет больших возможностей SIREN по точному воспроизведению сигнала, а также градиентов и лапласианов можно ожидать, что генеративные модели с использованием SIREN смогут совершить качественный скачок в своих возможностях. 

    В данный момент мы рассматриваем возможности проверить SIREN на наших задачах в Центре компетенции больших данных и искусственного интеллекта ЛАНИТ. Хочется надеяться, что предлагаемый подход покажет свою продуктивность не только на простых примерах, но и на нетривиальных прикладных задачах.
    ГК ЛАНИТ
    Ведущая многопрофильная группа ИТ-компаний в РФ

    Комментарии 12

      0
      Порт на C# + TensorFlow. Тренировка на картинке занимает около получаса на порядочном GPU.
        0
        Спасибо за ссылку. Согласен, в процессе экспериментов в collab c GPU/TPU тренировка занимала несколько минут. Безусловно, процесс более долгий по сравнению с тренировкой обычных свёрточных сетей, но результат существенно отличается по качеству.
          0
          А чем MLP с синусоидальной активацией принципиально отличается от разложения в ряд Фурье?
            0
            Можно переформулировать вопрос: получается, что у MLP с синусоидальной активацией и преобразования Фурье много общего? Да, оба подхода строятся на основе синусоидальной функции, помогают перевести сигнал в другой вид, позволяют восстановить сигнал с минимальными потерями (или без потерь в каких-то случаях). Синусоидальные базисные функции являются собственными функциями дифференцирования, что означает, что данное представление превращает линейные дифференциальные уравнения с постоянными коэффициентами в обычные алгебраические. Однако прямое сравнение преобразование Фурье и MLP с синусоидальной активацией не корректно: это разные методы с различными возможностями, различными временными затратами, с различной областью применения. MLP с синусоидальной функцией активации дает больше возможностей, чем MLP c функцией активации типа Relu, и такое сравнение в рамках одного семейства методов делают авторы изначальной исследовательской работы.
        0
        представляет собой байесову добавку к вектору
        почему вдруг смещение (bias) стал байесовой добавкой?
          0
          Еще докину занудства (не по сути, а по форме): эйконал — не имя собственное, не нужна заглавная буква :)
            0
            поправил термин, спасибо. Изначально был более жаргонный вариант, смещение — более точно с точки зрения терминологии.
          0
          О, ностальгия прямо ). Заменить обычную функцию активации на синус — это я пробовал лет так 15 назад примерно. Результаты на тестовых примерах выглядели интересно, но не сказать чтоб радикально прям лучше чем стандартные варианты. Т.е. сильно зависело от примера/задачи.
          Быстро стало понятно, что такой подход по существу является больше косметическим т.е. не решает основные грабли — а именно, во первЫх градиентный спуск по прежнему легко затыкается в локальных минимумах, и во вторых после настройки нейросети — нет ни малейшего понимания того, достаточно ли большой взята сетка (хватает ли в ней нейронов/частот), и единственный способ проверки это взять несколько сеток бОльшего размера и настроить — но это такой себе способ…
          После поломал голову над этим, и сформулировал/опробовал совершенно другой алгоритм настройки. Позволяющий вообще не фиксировать никакой структуры заранее, а начинать буквально с одного нейрона, и постепенно добавлять один за другим нейроны — при этом гарантируется сходимость к глобальному минимуму функции ошибки (но не гарантируется оптимальность этого процесса), имеется очень большая устойчивость к наличию шума во входных данных, и в случае добавления новых данных в обучающую выборку — не требуется производить перенастройки сети (а достаточно её один раз скорректировать по определенной процедуре линейного характера). В общем, получился действительно интересный алгоритм с широкими возможностями — но не пригодился, так и лежит «в столе». На «картинку» настройка займёт, в зависимости от требуемой ошибки, что-нибудь в районе нескольких минут (без GPU).

          Кстати, дарю идею ), если заменить синус на вейвлет-представление то можно ещё более интересный алгоритм реализовать — у меня тогда, 15 лет назад, до этого руки так и не дошли. А в простой замене стандартной функции активации на синус — нет ни новизны ни особой ценности, насколько я могу судить по моим давнешним экспериментам в этой области. В некоторых задачах будет получше, но не более того.
            0

            легко так написали, заменить синус на вейвлет. уже знаете как бэкпроп реализовать?..) может даже и можно.


            а вообще, вейвлеты образуют базис в пространстве функций, это не новость. тут не то чтобы за это зацепились. не факт что использование синусов в сетке это аналог преобразования Фурье и переход в частотное пространство напрямую.


            а идеи которые вы описываете, отчасти изучены, и изложены в книге С. Брантон и Дж. Куц "Анализ данных в науке и технике", сама книга недавно вышла, но все там описано, и с точки зрения низкоразмерных паттернов в данных.

              +1
              Да, примерно представляю как бэкпроп организовать — но делать его надо не напрямую в частотном пространстве, а с использованием приятных особенностей преобразования фурье (как-то гаусс-свёртке, т.е. размытию, частотного пространства соответствует перемножение на гаусс исходных данных, и игра вокруг этого — вначале бэкпроп в частотном пр-ве на самом крупном масштабе, потом на всё более мелком, последовательно). В варианте с синусом это отлично работает — но конечно простая замена функции активации на синус это даже не пол шага, именно потому что не используется вообще никак тот факт что правильно взятые тригонометрические функции образую базис. Незачем игнорировать полную амплитуду — т.е. и синус и косинус нужны. И незачем использовать частоты, имеющие в рамках исходного гиперкуба входных точек сколь нибудь значимую корреляцию. И настраивать влоб в частотном пр-ве — такая себе идея. И именно потому что не используется нормальный базис, или хотя-бы корреляционный анализ получаемых частот — авторы этой статьи проходят мимо множества очень приятных возможностей сеток с гармоническими функциями.

              Спасибо за ссылку, почитаю — посмотрю много ли из моих 15-ти летней давности наработок пришло в голову авторам этой книги. Любопытно, может быть и сам чего почерпну )
              0
              Спасибо за идею. Полностью согласен: использовать синусоидальную функцию активации пробовали и раньше. Есть ряд статей с упоминанием  Fourier neural networks, некоторые из них как раз относятся к периоду порядка 15 лет назад. Отличие в том, что Fourier neural networks активно использовали в первую очередь для временных рядов, однако авторы текущей работы сфокусировались на изображениях, звуке и решении физических уравнений.  

              Изменение функции активации не так сильно влияет на способность сети сойтись к глобальному оптимуму, в большей степени это зависит от архитектуры сети, learning rate и batch size, поэтому не удивительно, что ваша стратегия подбора архитектуры сети привела к желаемому результату. Надеюсь, у Вас дойдут руки до того, чтобы опубликовать Ваш метод, он может пригодиться другим исследователям и энтузиастам.

              Тем не менее, сложно спорить с тем, что развитие методов идет по спирали. Возможно, на текущем витке развития синусоидальная функция активации даст больше результатов в практических задачах, чем на прошлом. 

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое