Код не работает после его перевода с Python на Julia

Я новичок в языке программирования Julia и все еще изучаю его, пишу код, который я уже написал на Python (или, по крайней мере, опробовал на Python).

Есть статья, в которой объясняется, как сделать очень простую нейронную сеть: https://medium.com/technology-invention-and-more/how-to-build-a-simple-neural-network-in-9-lines-of-python-code-cc8f23647ca1.

Я попробовал код из этой статьи на Python, и он работает нормально. Однако я раньше не использовал линейную алгебру в Python (например, точку). Сейчас пытаюсь перевести этот код на Джулию, но кое-чего не могу понять. Вот мой код Джулии:

using LinearAlgebra

synaptic_weights = [-0.16595599, 0.44064899, -0.99977125]::Vector{Float64}

sigmoid(x) = 1 / (1 + exp(-x))
sigmoid_derivative(x) = x * (1 -x)

function train(training_set_inputs, training_set_outputs, number_of_training_iterations)
    global synaptic_weights
    for (iteration) in 1:number_of_training_iterations
        output = think(training_set_inputs)

        error = training_set_outputs .- output

        adjustment = dot(transpose(training_set_inputs), error * sigmoid_derivative(output))

        synaptic_weights = synaptic_weights .+ adjustment
    end
end

think(inputs) = sigmoid(dot(inputs, synaptic_weights))

println("Random starting synaptic weights:")
println(synaptic_weights)

training_set_inputs = [0 0 1 ; 1 1 1 ; 1 0 1 ; 0 1 1]::Matrix{Int64}
training_set_outputs = [0, 1, 1, 0]::Vector{Int64}
train(training_set_inputs, training_set_outputs, 10000)

println("New synaptic weights after training:")
println(synaptic_weights)

println("Considering new situation [1, 0, 0] -> ?:")
println(think([1 0 0]))

Я уже пытался инициализировать векторы (например, synaptic_weights) как:

synaptic_weights = [-0.16595599 ; 0.44064899 ; -0.99977125]

Однако код не работает. Точнее, мне непонятны 3 вещи:

  1. Правильно ли я инициализирую векторы и матрицы (равно ли это тому, что исходный автор делает в Python)?
  2. В Python оригинальный автор использует операторы + и -, где один операнд является вектором, а другой — скаляром. Я не уверен, означает ли это поэлементное сложение или вычитание в Python. Например, равен ли (вектор + скаляр) в Python (вектор + скаляр) в Джулии?
  3. Когда я пытаюсь запустить приведенный выше код Julia, я получаю следующую ошибку:

    ERROR: LoadError: DimensionMismatch("first array has length 12 which does not match the length of the second, 3.")
    Stacktrace:
     [1] dot(::Array{Int64,2}, ::Array{Float64,1}) at C:\Users\julia\AppData\Local\Julia-1.0.3\share\julia\stdlib\v1.0\LinearAlgebra\src\generic.jl:702
     [2] think(::Array{Int64,2}) at C:\Users\Viktória\Documents\julia.jl:21
     [3] train(::Array{Int64,2}, ::Array{Int64,1}, ::Int64) at C:\Users\Viktória\Documents\julia.jl:11
     [4] top-level scope at none:0
    in expression starting at C:\Users\Viktória\Documents\julia.jl:28
    

    Эта ошибка возникает, когда функция think(inputs) пытается вычислить скалярное произведение входных данных и synaptic_weights. В этом случае входы представляют собой матрицу 4x3, а синаптические веса - матрицу 3x1 (вектор). Я знаю, что их можно перемножить, и в результате получится матрица (вектор) 4х1. Не означает ли это, что их скалярное произведение можно вычислить?

    В любом случае, этот точечный продукт можно вычислить в Python с помощью пакета numpy, поэтому я думаю, что есть определенный способ, которым его также можно вычислить в Julia.

Для скалярного произведения я также попытался создать функцию, которая принимает a и b в качестве аргументов и пытается вычислить их скалярное произведение: сначала вычисляет произведение a и b, а затем возвращает сумму результата. Я не уверен, что это хорошее решение, но код Julia не дал ожидаемого результата, когда я использовал эту функцию, поэтому я удалил его.

Не могли бы вы помочь мне с этим кодом, пожалуйста?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
0
175
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вот код, адаптированный для Джулии:

sigmoid(x) = 1 / (1 + exp(-x))
sigmoid_derivative(x) = x * (1 -x)
think(synaptic_weights, inputs) = sigmoid.(inputs * synaptic_weights)

function train!(synaptic_weights, training_set_inputs, training_set_outputs,
                number_of_training_iterations)
    for iteration in 1:number_of_training_iterations
        output = think(synaptic_weights, training_set_inputs)
        error = training_set_outputs .- output
        adjustment =  transpose(training_set_inputs) * (error .* sigmoid_derivative.(output))
        synaptic_weights .+= adjustment
    end
end

synaptic_weights = [-0.16595599, 0.44064899, -0.99977125]
println("Random starting synaptic weights:")
println(synaptic_weights)

training_set_inputs = Float64[0 0 1 ; 1 1 1 ; 1 0 1 ; 0 1 1]
training_set_outputs = Float64[0, 1, 1, 0]
train!(synaptic_weights, training_set_inputs, training_set_outputs, 10000)

println("New synaptic weights after training:")
println(synaptic_weights)

println("Considering new situation [1, 0, 0] -> ?:")
println(think(synaptic_weights, Float64[1 0 0]))

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

Самое главное, что я изменил:

  • не используйте глобальные переменные, так как они значительно замедлят работу
  • сделать так, чтобы все массивы имели тип элемента Float64
  • в нескольких местах вам нужно сделать трансляцию с помощью . (например, функции sigmoid и sigmoid_derivative определены таким образом, что они ожидают получить число в качестве аргумента, поэтому, когда мы их вызываем, после их имени добавляется . для запуска трансляции)
  • используйте стандартное матричное умножение * вместо dot

Код работает примерно в 30 раз быстрее, чем исходная реализация на Python. Я не выжимал максимальную производительность из этого кода (теперь он выполняет много аллокаций, которых можно избежать), так как это потребовало бы немного переписать его логику, и я предполагаю, что вы хотели прямую повторную реализацию.

Я хотел бы задать еще два вопроса. Почему нельзя использовать глобальные переменные в Джулии? Вы выполняете трансляцию при вызовах sigmoid и sigmoid_derivative с ., потому что предоставленные для них аргументы являются массивами (и эти трансляции означают выполнение заданных вычислений поэлементно)? В любом случае, я принял ваш ответ и спасибо за вашу четкую помощь!

Kerberos 13.04.2019 19:55

Использование глобальных переменных замедлит ваш код. Что касается вещания, обратите внимание, что выражение 1 / (1 + exp(-x)) обычно правильно оценивается Джулией, только если x является числом (не вектором). Поэтому, когда вы вызываете sigmoid, вы должны включить трансляцию, добавив . после имени функции. Я добавил еще несколько комментариев в ответ.

Bogumił Kamiński 13.04.2019 20:33

Другие вопросы по теме