Каким был бы идиоматический способ F# для масштабирования списка (n-кортежей или списка) с другим списком, а также массивами?

Дано:

let weights = [0.5;0.4;0.3]
let X = [[2;3;4];[7;3;2];[5;3;6]]

я хочу вот что:
wX = [(0.5)*[2;3;4];(0.4)*[7;3;2];(0.3)*[5;3;6]]
хотел бы знать, как это сделать со списками и массивами. Дополнительная информация по оптимизации приветствуется


person D.S.    schedule 01.02.2017    source источник
comment
Вы имели в виду 0,5 * [2 3 4]?   -  person VoronoiPotato    schedule 02.02.2017
comment
да, при форматировании сняли звездочки   -  person D.S.    schedule 02.02.2017
comment
'[2,3,4;7,3,2;5,3,6]' не является списком списков. Это список из 3-х кортежей, int*int*int   -  person Anton Schwaighofer    schedule 02.02.2017
comment
Это для нейросети? Если это так, вы можете рассмотреть использование матричных функций MathNet Numerics.   -  person Guy Coder    schedule 02.02.2017
comment
Я украдкой заглянул в ваш профиль и увидел, что вы работаете с машинным обучением. Имейте в виду, что многие нейронные сети создаются с помощью Python, а в Python есть утиная типизация, что делает использование с матрицами разного размера легко работать в Python, но сложнее в F#. Таким образом, вас может заинтересовать Возврат массивов разной размерности из одной функции; возможно ли это на F#?   -  person Guy Coder    schedule 02.02.2017
comment
Когда вы доберетесь до сигмовидной функции, вы можете посмотреть MathNet Raise Scalar by a Matrix   -  person Guy Coder    schedule 02.02.2017
comment
Когда вы загружаете необработанные данные, я обнаружил Array.blit, чтобы быть самым точным инструментом в наборе инструментов.   -  person Guy Coder    schedule 02.02.2017
comment
@GuyCoder спасибо за ваш вклад и дополнительное внимание к деталям, я только начинаю изучать F#, особенно потому, что я работаю в компании, которая непреклонна в отношении использования Python в производстве. Приятно изучать такие нюансы между тем, чем F# отличается от Python, похоже, мне нужно более глубокое чтение. И да, я пытался понять, смогу ли я написать базовую нейронную сеть на F#, чтобы лучше понять язык.   -  person D.S.    schedule 02.02.2017
comment
Как вы знаете, создание производственных нейронных сетей без графического процессора или чего-то подобного — безумие. Если вы используете библиотеку, чтобы помочь вам, убедитесь, что она может использовать графический процессор, если вам это нужно. Многие не упоминают об этом, и вы можете не узнать, пока не потратите драгоценное время.   -  person Guy Coder    schedule 02.02.2017
comment
Mathnet Numerics также имеет несколько хороших функций генератора случайных чисел. Я использовал F# только для того, чтобы узнать, как работают алгоритмы и концепции, и это того стоило. Теперь, когда TensorFlow работает в Windows с графическим процессором, я вернусь к его использованию.   -  person Guy Coder    schedule 02.02.2017


Ответы (4)


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

let weights = [0.5;0.4;0.3]
let X = [[2;3;4];[7;3;2];[5;3;6]]
X
|> List.map2 (fun w x -> 
    x 
    |> List.map (fun xi -> 
        (float xi) * w
    )
) weights

В зависимости от того, насколько вам удобен синтаксис, вы можете предпочесть однострочник, например

List.map2 (fun w x -> List.map (float >> (*) w) x) weights X

Одни и те же библиотечные функции существуют для последовательностей (Seq.map2, Seq.map) и массивов (в модуле Array).

person Anton Schwaighofer    schedule 01.02.2017
comment
List.map2 FTW! :) - person s952163; 02.02.2017
comment
спасибо за исправление моего вопроса, также приятно видеть двойные подходы, вы дважды ответили на мой вопрос :) - person D.S.; 02.02.2017
comment
кстати, мне было интересно, есть ли библиотека, которая упрощает выполнение стандартных матричных операций, таких как масштабирование, линейная сумма и т. д., очень похожих на R или Matlab, этот подход кажется очень сложным, отвлекает внимание от того, что и как. - person D.S.; 02.02.2017
comment
В MSDN есть обзорный список здесь post датирован 2010 годом, но некоторые библиотеки все еще существуют. Я слышал хорошие отзывы о Math.NET Numeric, но сам им не пользовался. - person Anton Schwaighofer; 02.02.2017

Это гораздо больше, чем ответ на конкретный вопрос, но после чата в комментариях и изучения того, что вопрос был конкретно частью нейронной сети в F #, я публикую это, которое охватывает вопрос и реализует часть нейронной сети с прямой связью. . Он использует MathNet Numerics.

Этот код представляет собой перевод на F# части кода Python из раздела Neural Networks and Deep Learning.

питон

def backprop(self, x, y):
    """Return a tuple ``(nabla_b, nabla_w)`` representing the
    gradient for the cost function C_x.  ``nabla_b`` and
    ``nabla_w`` are layer-by-layer lists of numpy arrays, similar
    to ``self.biases`` and ``self.weights``."""
    nabla_b = [np.zeros(b.shape) for b in self.biases]
    nabla_w = [np.zeros(w.shape) for w in self.weights]
    # feedforward
    activation = x
    activations = [x] # list to store all the activations, layer by layer
    zs = [] # list to store all the z vectors, layer by layer
    for b, w in zip(self.biases, self.weights):
        z = np.dot(w, activation)+b
        zs.append(z)
        activation = sigmoid(z)
        activations.append(activation)

F#

module NeuralNetwork1 =

    //# Third-party libraries
    open MathNet.Numerics.Distributions         // Normal.Sample
    open MathNet.Numerics.LinearAlgebra         // Matrix

    type Network(sizes : int array) = 

        let mutable (_biases : Matrix<double> list) = []
        let mutable (_weights : Matrix<double> list) = []    

        member __.Biases
            with get() = _biases
            and set value = 
                _biases <- value
        member __.Weights
            with get() = _weights
            and set value = 
                _weights <- value

        member __.Backprop (x : Matrix<double>) (y : Matrix<double>) =
            // Note: There is a separate member for feedforward. This one is only used within Backprop 
            // Note: In the text layers are numbered from 1 to n   with 1 being the input and n   being the output
            //       In the code layers are numbered from 0 to n-1 with 0 being the input and n-1 being the output
            //       Layers
            //         1     2     3    Text
            //         0     1     2    Code
            //       784 -> 30 -> 10
            let feedforward () : (Matrix<double> list * Matrix<double> list) =
                let (bw : (Matrix<double> * Matrix<double>) list) = List.zip __.Biases __.Weights
                let rec feedfowardInner layer activation zs activations =
                    match layer with
                    | x when x < (__.NumLayers - 1) ->
                        let (bias, weight) = bw.[layer]
                        let z = weight * activation + bias
                        let activation = __.Sigmoid z
                        feedfowardInner (layer + 1) activation (z :: zs) (activation :: activations)
                    | _ -> 
                        // Normally with recursive functions that build list for returning
                        // the final list(s) would be reversed before returning.
                        // However since the returned list will be accessed in reverse order
                        // for the backpropagation step, we leave them in the reverse order.
                        (zs, activations)
                feedfowardInner 0 x [] [x]

В weight * activation * есть перегруженный оператор, работающий с Matrix<double>

Связано с данными вашего примера и использованием MathNet Numerics Арифметика

let weights = [0.5;0.4;0.3]
let X = [[2;3;4];[7;3;2];[5;3;6]]

сначала значения для X необходимо преобразовать в число с плавающей запятой

let x1 = [[2.0;3.0;4.0];[7.0;3.0;2.0];[5.0;3;0;6;0]]

Теперь обратите внимание, что x1 — это матрица, а веса — вектор.

так что мы можем просто умножить

 let wx1 = weights * x1

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

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

Я знаю, что исходный код Python был действительным, и я смог успешно запустить его и получить такие же результаты, как указано в книге, а это означает, что первоначальные успехи были в пределах пары процентов от книги, а графики успеха были такими же. . Я сделал это для нескольких прогонов и нескольких конфигураций нейронной сети, как описано в книге. Затем я запустил код F# и получил такие же графики.

Я также скопировал начальные наборы случайных чисел из кода Python в код F#, чтобы, хотя генерируемые данные были случайными, и код Python, и код F# использовали одни и те же начальные числа, которых тысячи. Затем я пошагово выполнил код Python и F#, чтобы убедиться, что каждая отдельная функция возвращает сопоставимое значение с плавающей запятой, например Я поставил точку останова на каждой строке и убедился, что проверил каждую. На самом деле это заняло несколько дней, потому что мне нужно было написать код экспорта и импорта и передать данные из Python в F#.

См.: Как определить тип вложенных структур данных в Python. ?

Я также попробовал вариант, в котором я заменил список F # на связанный список, но не обнаружил увеличения скорости, например. LinkedList<Matrix<double>>. Было интересное упражнение.

person Guy Coder    schedule 02.02.2017
comment
Вы используете __.Foo как соглашение для чего-то? - person D.S.; 02.02.2017
comment
@Д.С. Нет, __.Foo — это экземпляр члена. См.: синтаксис экземпляра F#. Я редко использую члены с моим функциональным кодом, но я сделал это здесь, потому что при переводе мне нравится код максимально приближен к оригиналу. Я также избегаю изменчивых элементов, как чумы, но здесь тысячи изменчивых элементов являются частью матриц. Это как в поэзии, вам нельзя нарушать правила, пока вы их не поймете. - person Guy Coder; 02.02.2017
comment
@Д.С. Поскольку я сделал это год назад, и это живая книга, похоже, что часть книги изменилась. Мне придется перечитать его, чтобы увидеть, что было обновлено. - person Guy Coder; 02.02.2017
comment
@Д.С. Я добавил больше кода, чтобы поместить экземпляры в контекст. - person Guy Coder; 02.02.2017

Если я правильно понимаю,

let wX = weights |> List.map (fun w ->
    X |> List.map (fun (a, b, c) ->
        w * float a,
        w * float b,
        w * float c))
person ildjarn    schedule 01.02.2017
comment
Чтобы разделить X, вы можете использовать let chunkedWeights = weights |› Seq.chunkBySize 3 - person VoronoiPotato; 02.02.2017
comment
спасибо, но что, если X равно n x n, а не просто 3 x 3? - person D.S.; 02.02.2017
comment
Тогда это должен быть список, а не кортеж, к которому относится ответ @Anton. - person ildjarn; 02.02.2017

Это альтернативный способ добиться этого с помощью Math.Net: https://numerics.mathdotnet.com/Matrix.html#Arithmetics

person D.S.    schedule 02.02.2017
comment
Я знаю, что это трудно сказать, но если вы нажмете на * в моем ответе после weight * activation, вы увидите, что это ссылка на то, что вы только что опубликовали. - person Guy Coder; 02.02.2017
comment
Ааа чуть не пропустил это. Ваше расширение - удивительное объяснение, кстати. Большое спасибо. - person D.S.; 02.02.2017
comment
Спасибо. Я планирую опубликовать весь перевод книги на F# на GitHub, но мне все еще нужно выполнить сверточную нейронную сеть, а ее перевод на F# немного сложнее, чем первая часть. Поскольку упражнение заключалось в том, чтобы понять нейронные сети на низком уровне, и я достиг своей цели, я просто пошел дальше. Никогда по-настоящему не ценил утиную печать, пока не сделал эти переводы. Отложите его, и он был там больше года. - person Guy Coder; 02.02.2017
comment
Еще одна забавная вещь, которую я сделал с ним, заключалась в том, чтобы использовать символы для идентификаторов, let ``∇b`` = ``Δ`` :: ``∇b`` но обратная связь такова, что люди предпочли бы иметь имена. - person Guy Coder; 02.02.2017