Введение

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

Для обработки данных перед их предоставлением в качестве входных данных для модели довольно часто используются конвейеры, чтобы обрабатывать данные вместе с моделированием этих данных. Однако ужасная вещь о данных заключается в том, что они могут быть непредсказуемыми и часто грязными. Для этого у специалистов по данным действительно нет никаких удобных инструментов в любом месте, кроме кода, который они пишут, и эта задача не очень часто автоматизируется. Разрешите познакомить вас с новым типом маршрутизатора в Lathe.jl. Этот тип используется для обработки данных и маршрутизации их по конвейерам, и реализация этого довольно крутая.

"Ноутбук"

Основы маршрутизатора

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

Тип Lathe Router выполняет это действие, направляя данные в соответствующую модель, используя возвращаемые значения и переданную функцию. Эта функция по порядку возвращает то, что затем передается в качестве аргументов объектам, находящимся под ней. Как модель конвейера или токарного станка относится к типу LatheObject, так и маршрутизатор. Это означает, что мы можем поместить маршрутизаторы внутри конвейеров, а маршрутизаторы - в маршрутизаторы. Это создает бесконечные возможности для объединения данных в цепочку с помощью различных алгоритмов обработки данных и их маршрутизации по цепочке.

Это действительно здорово, потому что это может позволить нам начать с некоторых входных данных и отправить их по конвейеру маршрутизаторов и последующих конвейеров, которые работают вместе, чтобы обеспечить серию прогнозов. Итак, после объяснения всей концепции, давайте теперь опробуем тип Router и визуализируем, что он делает.

Моделирование

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

using Lathe.models: LinearRegression
using DataFrames
df = DataFrame(:A => [5, 10, 15, 20, 23, 17, 13, 15], :B => [10, 15, 20, 25, 27, 21, 20, 25],
    :C => [5, 10, 15, 20, 22, 19, 12, 13])

Затем я собираюсь извлечь нашу цель в виде массива.

y = Array(df[!, :A])

Теперь мы удалим этот столбец из нашего x, используя методы select! () И Not () вместе в следующем коде:

x = select!(df, Not(:A))

Теперь мы подгоним новую модель линейной регрессии с этим x в качестве характеристики:

m = LinearRegression(x, y)

Теперь давайте попробуем с его помощью предсказать:

m.predict(df)

Теперь мы сделаем то же самое, только на этот раз мы подгоним нашу модель только с одним массивом из нашего фрейма данных:

m2 = LinearRegression(Array(df[!, :B]), y)

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

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

function filter_X(x)
    df = nothing
    array = nothing

Теперь я просто собираюсь перебрать наш x и проверить тип значения, которое мы повторяем, используя простой условный оператор:

for data in x
        if typeof(data) == DataFrame
            df = data
        else
            array = data
        end
    end

На последнем этапе я верну наши данные и завершу функцию:

return(array, df)
end

Для окончательного результата, который будет выглядеть так:

function filter_X(x)
    df = nothing
    array = nothing
    for data in x
        if typeof(data) == DataFrame
            df = data
        else
            array = data
        end
    end
    return(array, df)
end

Обратите внимание на позиции возвратов, это жизненно важно для маршрутизатора, так как позиции в возврате - это то, что маршрутизатор использует, чтобы знать, куда поместить данные после запуска этой функции. Теперь мы создадим новый маршрутизатор, используя аргумент ключевого слова fn для обозначения нашей функции фильтрации и передав наши объекты Lathe.

router = Router(m2, m, fn = filter_X)

Теперь мы можем передавать повторяющиеся данные для обеих этих моделей в любом порядке, который мы хотим, с помощью функции predic () маршрутизатора. Проверить это:

result = router.predict([df[!, :B], x])

Это произвело новый массив, содержащий два массива прогнозов, по одному от каждой модели, проверьте это:

println("First model's prediction (Array): ", result[1], "\n\n")
println("Second model's prediction (DataFrame): ", result[2])

Ради интереса я также оценил точность обеих моделей:

using Lathe.stats: mae, r2
println("First model's mae: ", mae(y, result[1]))
println("Second model's mae: ", mae(y, result[2]))

println("First model's r2: ", r2(y, result[1]))
println("Second model's r2: ", r2(y, result[2]))

Я также визуализировал модели с Hone.jl. Поскольку Hone.jl был прекращен так долго, это было на удивление сложно и, вероятно, не стоило усилий. Тем не менее, я решил заняться этим и сделать именно это. Поскольку версия функции для фрейма данных была разбита по времени, я решил просто разбросать одно предсказание, а затем, благодаря модульной методологии Hone.jl, мы могли бы добавить точки для другого прогноза и исходного y, которое мы предсказали, на график.

using Hone: Points, Circle, Scatter
plt = Scatter(df[!, :B], result[1])
secondpredpoints = Points(df[!, :B], result[2], shape = Circle(.5, .5, 24, :purple))
plt.add(secondpredpoints)
original = Points(df[!, :B], y, shape = Circle(.5, .5, 24, :magenta))
plt.add(original)
plt.show()

Вывод

На мой взгляд, этот тип обязательно пригодится! Я считаю, что концепция автоматизации всего процесса управления данными и прогнозного моделирования просто потрясающая! Очень важная функция, которую я хотел бы добавить, - это возможность передавать только один фрагмент необходимых данных и при этом направлять их к соответствующему объекту Lathe. Однако из-за абстракции концепции маршрутизатора я действительно думаю, что это будет практически невозможно сделать. Однако, если это возможно, я могу гарантировать, что это будет реализовано до того, как скоро выйдет Lathe Gingerbread. Если вы хотите узнать больше о любом из трех пакетов, которые я использовал в этой статье (Lathe, Hone, DataFrames), я также оставлю их ссылки на Github ниже. Большое спасибо за то, что прочитали мою статью, и я надеюсь, что она была вам так же интересна, как и мне!