Время прочтения: 4 мин.

Каждый раз, выполняя анализ данных методами машинного обучения традиционными средствами в виде Python, возникает вопрос: «Неужели это единственный рабочий инструмент для подобного рода задач, не имеющий аналогов?». Определённо нет. В данной статье рассмотрим ещё один пример использования функционального программирования для задач машинного обучения, в частности, реализацию применения нейронной сети на языке F# с использованием библиотеки numl.

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

У большинства начинающих аналитиков словосочетание «машинное обучение» сразу же ассоциируется с языком Python и его библиотеками (Pandas, SkLearn и т.д.). Происходит это, конечно же, из-за того, что данный набор программных средств стал массово применяться в Data Science одним из первых. И на фоне его популярности альтернативные разработки незаслуженно остаются незамеченными и малоиспользуемыми. Например, для .NET-разработчиков существует библиотека ML.NET, позволяющая пользоваться всеми преимуществами Python без необходимости изучения ещё одного языка программирования.

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

Создадим консольный проект и добавим нужные ссылки на библиотеки, в данном случае это библиотека numl. Также создадим тип (класс) Passenger, в который будут загружаться данные для обучения и предсказания:

open System
open numl
open numl.Model
open numl.Supervised.NeuralNetwork

let trainFile = "./Train.csv"

type Passenger = { 
              [<Label>] mutable Survived: bool;
              [<Feature>]Pclass: float;
              [<Feature>]Sex: float;
              [<Feature>]Age: float;
              [<Feature>]SibSp: float;
       [<Feature>]Parch: float}

В качестве признаков выбраны класс, пол, возраст, количество родственников на борту и количество детей. Следует отметить, что эти атрибуты имеют неизменяемый тип, а атрибут Survived (то, что нам нужно предсказать) имеет инструкцию mutable, что говорит о возможности изменения значения атрибута. 

Далее в вызываемой функции (main) пишем следующий код:

[<EntryPoint>]
let main argv = 
    let data = System.IO.File.ReadLines(trainFile)
    
    let data' = data |> Seq.map box
    
    let descriptor = Descriptor.Create<Passenger>()
    let generator = NeuralNetworkGenerator()
    generator.Descriptor <- descriptor
    let curd = DateTime.Now
    printfn "Start learning %A" curd    
    
    let model = Learner.Learn(data', 0.80, 100, generator)
    printfn "Learning complete %A" ed
    printfn "Learning time: %A" (ed-curd)
    printfn "Accuary %A" accuracy

В данном фрагменте происходит чтение тренировочного датасета из файла и создание на его основе массива экземпляров класса Passenger (Descriptor). После этого создаётся генератор нейронов и подстановка в него дескриптора. На основе этого генератора и происходит обучение модели. Также добавим вывод времени начала и окончания обучения для контроля процесса обучения.

Как только модель становится обученной, можно приступать к предсказанию на тестовом датасете. Ниже приведён пример предсказания по одному человеку. Поскольку указание всех атрибутов обязательно, предсказываемый атрибут Survived имеет значение true:

printfn "Start predict"
    let testData = {Survived=true; Pclass =3.0; Sex =1.0; Age =47.0; SibSp =1.0; Parch = 0.0}
    let predict = model.Model.Predict(testData)
    printfn "Predict complete"
    printfn "Predicted data: %A" predict
    Console.ReadLine() |> ignore

В результате выполнения предсказания видно, что Survived принял значение false:

Следует отметить, что данный результат был получен при условии, что объём обучающей выборки равен 80% и количество повторений 100. Если изменить эти настройки, например, увеличить количество повторов, то увеличится и время обучения. При этом метрика accuracy изменится в меньшую сторону.

Укажем в настройках модели 200 повторов вместо 100:

let model = Learner.Learn(data', 0.80, 200, generator)

Получаем результат, описанный выше:

Если же поменять объём выборки, то время обучения также уменьшится:

let model = Learner.Learn(data', 0.60, 200, generator)

Вот так выглядит простейший пример построения нейронной сети с помощью языка F#. Учитывая, небольшое количество строк кода, сопоставимое с количеством строк при решении той же задачи средствами Python, F# для машинного обучения достоин внимания и места в наборе инструментов аналитиков.