Заказ веса MPSCNN

Фреймворк Metal Performance Shader поддерживает создание собственных сверточных нейронных сетей. Например, при создании MSPCNNConvolution требуется четырехмерный тензор веса в качестве параметра init, который представлен в виде одномерного указателя с плавающей запятой.

init(device: MTLDevice,
  convolutionDescriptor: MPSCNNConvolutionDescriptor,
  kernelWeights: UnsafePointer<Float>,
  biasTerms: UnsafePointer<Float>?,
  flags: MPSCNNConvolutionFlags)

В документации есть что сказать о тензоре 4D

Макет веса фильтра устроен так, что его можно интерпретировать как четырехмерный тензор (массив) weight[outputChannels][kernelHeight][kernelWidth][inputChannels/groups]

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

Я попытался упорядочить веса, как того требует аналог BNNS, но безуспешно.

Как правильно представить четырехмерный тензор (массив) в виде одномерного указателя Float (массив)?

PS: я попытался упорядочить его как массив C и получить указатель на плоский массив, но это не сработало.

ОБНОВЛЕНИЕ

@RhythmicFistman: Вот как я сохранил его в простом массиве, который я могу преобразовать в UsafePointer<Float> (но не работает):

var output = Array<Float>(repeating: 0, count: weights.count)

for o in 0..<outputChannels {
    for ky in 0..<kernelHeight {
        for kx in 0..<kernelWidth {
            for i in 0..<inputChannels {
                let offset = ((o * kernelHeight + ky) * kernelWidth + kx) * inputChannels + i
                output[offset] = ...
            }
        }
    }
}

person Erik Aigner    schedule 10.11.2016    source источник
comment
Можете ли вы показать, как вы сделали попытку преобразования 4D в 1D? за исключением использования неправильного указателя, я думаю, что единственное, что может пойти не так, - это порядок индексов.   -  person Rhythmic Fistman    schedule 11.11.2016
comment
@RhythmicFistman Обновил вопрос, проблема даже не в том, что СОБСТВЕННЫЙ пример проекта Apple (с их собственными тренированными весами) работает правильно. Итак, как я должен знать, что вообще не так... мои данные или их реализация.   -  person Erik Aigner    schedule 11.11.2016
comment
Какой образец Apple вы имеете в виду? И MetalImageRecognition, и MPSCNNHelloWorld, кажется, работают для меня с бета-версией Xcode 8.2 и бета-версией iOS 10.2.   -  person warrenm    schedule 12.11.2016
comment
@warrenm попытайтесь запустить глубокую сеть на наборе MNIST в MPSCNNHelloWorld (переключите кнопку внизу, чтобы использовать глубокую сеть и начать тестовый запуск на наборе mnist). Это приведет к точности 7 или 8%, что не может быть правильным.   -  person Erik Aigner    schedule 12.11.2016
comment
См. пример tf.transpose здесь: mattrajca.com/2016/11/26/   -  person paiv    schedule 15.12.2016


Ответы (4)


Итак, я понял это. Вот две функции Python, которые я использую для преобразования своих сверток и полносвязных матриц.

# shape required for MPSCNN [oC kH kW iC]
# tensorflow order is [kH kW iC oC]
def convshape(a):
    a = np.swapaxes(a, 2, 3)
    a = np.swapaxes(a, 1, 2)
    a = np.swapaxes(a, 0, 1)
    return a

# fully connected only requires a x/y swap
def fullshape(a):
    a = np.swapaxes(a, 0, 1)
    return a
person Erik Aigner    schedule 16.12.2016

Это то, что мне недавно пришлось сделать для весов Caffe, поэтому я могу предоставить реализацию Swift для того, как я их переупорядочил. Следующая функция принимает массив Float весов Caffe для свертки (в порядке [c_o][c_i][h][w]) и переупорядочивает их в соответствии с ожиданиями Metal ([c_o][h][w][c_i] заказ):

public func convertCaffeWeightsToMPS(_ weights:[Float], kernelSize:(width:Int, height:Int), inputChannels:Int, outputChannels:Int, groups:Int) -> [Float] {

    var weightArray:[Float] = Array(repeating:0.0, count:weights.count)
    var outputIndex = 0

    let groupedInputChannels = inputChannels / groups
    let outputChannelWidth = groupedInputChannels * kernelSize.width * kernelSize.height

    // MPS ordering: [c_o][h][w][c_i]
    for outputChannel in 0..<outputChannels {
        for heightInKernel in 0..<kernelSize.height {
            for widthInKernel in 0..<kernelSize.width {
                for inputChannel in 0..<groupedInputChannels {
                    // Caffe ordering: [c_o][c_i][h][w]
                    let calculatedIndex = outputChannel * outputChannelWidth + inputChannel * kernelSize.width * kernelSize.height + heightInKernel * kernelSize.width + widthInKernel
                    weightArray[outputIndex] = weights[calculatedIndex]
                    outputIndex += 1
                }
            }
        }
    }

    return weightArray
}

Основываясь на моей визуализации слоя, кажется, что это дает правильные результаты свертки (соответствующие результатам, полученным Caffe). Я считаю, что он также правильно учитывает группировку, но мне нужно это проверить.

Tensorflow имеет другой порядок, чем Caffe, но вы должны иметь возможность изменить математику во внутренней части цикла, чтобы учесть это.

person Brad Larson    schedule 05.01.2017

Приведенная здесь документация предполагает некоторое знание C. В этом контексте a[x][y][z] обычно свертывается в одномерный массив, когда x, y и z являются константами, известными во время компиляции. Когда это происходит, компонент z меняется быстрее всего, за ним следует y, а затем x — снаружи внутрь.

Если у нас есть [2][2][2], он сворачивается в 1D как:

{ a[0][0][0], a[0][0][1], a[0][1][0], a[0][1][1], 
  a[1][0][0], a[1][0][1], a[1][1][0], a[1][1][1] }
person Ian Ollmann    schedule 14.01.2017

Я думаю, что у tensorflow уже есть удобный метод для такой задачи:

tf.transpose(aWeightTensor, perm=[3, 0, 1, 2])

Полная документация: https://www.tensorflow.org/api_docs/python/tf/transpose

person Tianyu Liu    schedule 15.06.2018