Генерация кода для инкрементного обучения

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

Сгенерированный код выполняет следующие задачи, как определено в функциях точки входа:

  1. Загрузите сконфигурированный шаблон модели инкрементного обучения, созданный в командной строке.

  2. Отслеживайте показатели эффективности на входящем пакете данных из потока данных. Этот пример отслеживает скорость неправильной классификации и потери шарниров.

  3. Обновите модель, подгоняя инкрементальную модель к пакету данных.

  4. Прогнозирует метки для пакета данных.

Этот пример генерирует код из командной строки MATLAB ®, но можно сгенерировать код с помощью приложения MATLAB ® Coder™. Для получения дополнительной информации смотрите Генерацию кода для предсказания модели машинного обучения с использованием приложения MATLAB Coder.

Все функции объекта инкрементного обучения для двоичной линейной классификации (а также линейной регрессии) поддерживают генерацию кода. Чтобы подготовить код для генерации для инкрементного обучения, функции объекта требуют соответствующим образом сконфигурированного объекта модели инкрементного обучения, но -args опция codegen (MATLAB Coder) не принимает эти объекты. Чтобы обойти это ограничение, используйте saveLearnerForCoder и loadLearnerForCoder функций.

Этот график потока показывает рабочие процессы генерации кода для функций объекта инкрементного обучения для линейных моделей.

График потока предполагает два разных, но сливающихся рабочих процессов.

  • Рабочий процесс, начинающийся с Train Model > Convert Model, требует данных, в этом случае вы можете опционально выполнить выбор признаков или оптимизировать модель, выполнив перекрестную валидацию перед генерацией кода для инкрементного обучения.

  • Рабочий процесс, начинающийся с настройки модели, не требует данных. Вместо этого необходимо вручную сконфигурировать объект модели инкрементного обучения.

Для получения дополнительной информации о различиях между рабочими процессами и для помощи в определении того, какой из них использовать, смотрите Настройте Модель инкрементного обучения.

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

  • The NumPredictors свойство отражает количество предикторов в данных предиктора во время инкрементного обучения.

  • Для классификации, ClassNames свойство должно содержать все имена классов, ожидаемые во время инкрементного обучения.

Если вы выбираете рабочий процесс Train Model > Convert Model и подбираете модель к данным, содержащим все известные классы, модель конфигурируется для генерации кода.

После подготовки инкрементальной модели обучения сохраните объект модели при помощи saveLearnerForCoder. Затем задайте функцию точки входа, которая загружает сохраненную модель при помощи loadLearnerForCoder, и это выполняет инкрементальное обучение путем вызова функций объекта. Кроме того, можно задать несколько функций точки входа, которые выполняют этапы инкрементного обучения отдельно (этот пример использует этот рабочий процесс). Однако этот рабочий процесс требует специальной обработки, когда обновленный объект модели является входом в другую функцию точки входа. Для примера вы записываете следующие три функции точки входа:

  • Функция, которая принимает текущую модель и пакет данных, вызывает updateMetricsи возвращает модель с обновленными метриками эффективности.

  • Функция, которая принимает обновленную модель и пакет данных, вызывает fit, и возвращает модель с обновленными коэффициентами.

  • Функция, которая принимает дальнейшую обновленную модель и пакет данных предиктора, вызывает predict, и возвращает предсказанные метки.

Наконец, сгенерируйте код для функций точки входа при помощи codegen, и проверьте сгенерированный код.

Загрузка и предварительная обработка данных

Загрузите набор данных о деятельности человека. Случайным образом перетасуйте данные.

load humanactivity
rng(1); % For reproducibility
n = numel(actid);
p = size(feat,2);
idx = randsample(n,n);
X = feat(idx,:);
actid = actid(idx);

Для получения дополнительной информации о наборе данных введите Description в командной строке.

Ответы могут быть одним из пяти классов: Сидя, Стоя, Ходьба, Бег, или Танцы. Дихотомизируйте ответ путем определения, находится ли субъект в простое (actid <= 2). Сохраните уникальные имена классов. Создайте категориальные массивы.

classnames = categorical(["Idle" "NotIdle"]);
Y = repmat(classnames(1),n,1);
Y(actid > 2) = classnames(2);

Сконфигурируйте модель инкрементного обучения

Чтобы сгенерировать код для инкрементной классификации, необходимо соответствующим образом сконфигурировать двоичную классификационную линейную модель для инкрементного обучения incrementalClassificationLinear.

Создайте двоичную модель классификации (SVM) для инкрементного обучения. Полностью сконфигурируйте модель для генерации кода, задав все ожидаемые имена классов и количество переменных. Кроме того, укажите отслеживание скорости неправильной классификации и потерь шарниров. Для воспроизводимости этот пример отключает тасование наблюдений для решателя с инвариантной шкалой.

metrics = ["classiferror" "hinge"];
IncrementalMdl = incrementalClassificationLinear('ClassNames',classnames,'NumPredictors',p,...
    'Shuffle',false,'Metrics',metrics)
IncrementalMdl = 
  incrementalClassificationLinear

            IsWarm: 0
           Metrics: [2x2 table]
        ClassNames: [Idle    NotIdle]
    ScoreTransform: 'none'
              Beta: [60x1 double]
              Bias: 0
           Learner: 'svm'


  Properties, Methods

Mdl является incremenalClassificationLinear объект модели, сконфигурированный для генерации кода. Mdl холодный (Mdl.IsWarm является 0) потому что он не обработал данные - коэффициенты 0.

Кроме того, поскольку данные доступны, можно подгонять модель SVM к данным при помощи fitcsvm или fitclinear, а затем преобразовать полученную модель в модель инкрементного обучения путем передачи модели в incrementalLearner. Получившаяся модель теплая, потому что она обработала данные - ее коэффициенты, вероятно, ненулевые.

Сохраните модель с помощью saveLearnerForCoder

Сохраните модель инкрементного обучения в файл InitialMdl.mat при помощи saveLearnerForCoder.

saveLearnerForCoder(IncrementalMdl,'InitialMdl');

saveLearnerForCoder сохраняет модель инкрементного обучения в двоичном файле MATLAB SVMClassIncrLearner.mat как массивы структур в текущей папке.

Определите функции точки входа

Функция точки входа, также известная как функция верхнего уровня или первичная функция, является функцией, которую вы задаете для генерации кода. Потому что вы не можете вызвать какую-либо функцию на верхнем уровне, используя codegenнеобходимо задать функцию точки входа, которая вызывает функции с поддержкой генерации кода, и сгенерировать код C/C + + для функции точки входа при помощи codegen. Все функции в функции точки входа должны поддерживать генерацию кода.

Задайте в текущей папке четыре отдельные функции точки входа, которые выполняют следующие действия:

  • myInitialModelIncrLearn.m - Загрузка сохраненной модели при помощи loadLearnerForCoder, и возвращает модель той же формы для генерации кода. Эта функция точки входа облегчает использование модели, возвращаемой функцией точки входа, в качестве входа в другую функцию точки входа.

  • myUpdateMetricsIncrLearn.m - Измерьте эффективность текущей модели на входящем пакете данных и сохраните метрики эффективности в модели. Функция принимает текущую модель, и данные предиктора и отклика, и возвращает обновленную модель.

  • myFitIncrLearn.m - Подгонка текущей модели к входящему пакету данных и хранение обновленных коэффициентов в модели. Функция принимает текущую модель, и данные предиктора и отклика, и возвращает обновленную модель.

  • myPredictIncrLearn.m - Предсказанные метки для входящего пакета данных с использованием текущей модели. Функция принимает текущую модель и данные предиктора и возвращает метки и счета классов.

Для получения дополнительной информации о генерации кода для нескольких функций точки входа, смотрите Сгенерировать код для нескольких функций точки входа (MATLAB Coder).

Добавьте %#codegen директива компилятора (или прагма) к функции точки входа после сигнатуры функции, чтобы указать, что вы намерены сгенерировать код для алгоритма MATLAB. Добавление этой директивы предписывает анализатору кода MATLAB помочь вам диагностировать и исправить нарушения, которые могут привести к ошибкам во время генерации кода. Смотрите проверку кода with the Анализатор Кода (MATLAB Coder).

Кроме того, вы можете получить доступ к функциям в mlr/examples/stats/main, где mlr - значение matlabroot.

Отобразите тело каждой функции.

type myInitialModelIncrLearn.m
function incrementalModel = myInitialModelIncrLearn() %#codegen
% MYINITIALMODELINCRLEARN Load and return configured linear model for
% binary classification InitialMdl
    incrementalModel = loadLearnerForCoder('InitialMdl');
end
type myUpdateMetricsIncrLearn.m
function incrementalModel = myUpdateMetricsIncrLearn(incrementalModel,X,Y) %#codegen
% MYUPDATEMETRICSINCRLEARN Measure model performance metrics on new data
      incrementalModel = updateMetrics(incrementalModel,X,Y); 
end
type myFitIncrLearn.m
function incrementalModel = myFitIncrLearn(incrementalModel,X,Y) %#codegen
% MYFITINCRLEARN Fit model to new data
      incrementalModel = fit(incrementalModel,X,Y); 
end
type myPredictIncrLearn.m
function [labels,scores] = myPredictIncrLearn(incrementalModel,X) %#codegen
% MYPREDICTINCRLEARN Predict labels and classification scores on new data
      [labels,scores] = predict(incrementalModel,X); 
end

Сгенерируйте код

Настройка компилятора

Чтобы сгенерировать код C/C + +, вы должны иметь доступ к компилятору C/C + +, который настроен правильно. MATLAB Coder находит и использует поддерживаемый, установленный компилятор. Можно использовать mex -setup чтобы просмотреть и изменить компилятор по умолчанию. Для получения дополнительной информации см. раздел «Изменение компилятора по умолчанию».

Тип сборки

MATLAB Coder может сгенерировать код для следующих типов сборки:

  • Функция MEX (MATLAB Executable)

  • Автономный код C/C + +

  • Автономный код C/C + + скомпилирован в статическую библиотеку

  • Автономный код C/C + + скомпилирован в динамически связанную библиотеку

  • Автономный код C/C + + скомпилирован в исполняемый файл

Тип сборки можно задать с помощью -config опция codegen (MATLAB Coder). Для получения дополнительной информации об установке опций генерации кода смотрите -config опция codegen (MATLAB Coder) и настройте параметры сборки (MATLAB Coder).

По умолчанию codegen генерирует MEX-функцию. Функция MEX является программой на C/C + +, которая исполняется из MATLAB. Можно использовать MEX-функцию, чтобы ускорить алгоритмы MATLAB и протестировать сгенерированный код на функциональность и проблемы времени выполнения. Для получения дополнительной информации смотрите Алгоритм MATLAB Acceleration (MATLAB Coder) и Зачем тестировать MEX-функции в MATLAB? (MATLAB Coder).

Сгенерируйте код используя codegen

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

  • Типы данных входов функций точки входа при помощи coder.typeof (MATLAB Coder). Кроме того, поскольку количество наблюдений может варьироваться от партии к партии, укажите, что количество наблюдений (первая размерность) имеет переменный размер. Для получения дополнительной информации смотрите Specify Variable-Size Arguments for Генерация Кода и Specify Properties of Entry-Point Function Inputs (MATLAB Coder).

  • Поскольку несколько функций точки входа принимают объект инкрементальной модели в качестве входных данных и работают с ним, создайте представление объекта модели для генерации кода при помощи coder.OutputType (MATLAB Coder). Для получения дополнительной информации смотрите Передайте выход функции точки входа как вход (MATLAB Coder).

predictorData = coder.typeof(X,[],[true false]); 
responseData = coder.typeof(Y,[],true);
IncrMdlOutputType = coder.OutputType('myInitialModelIncrLearn');

Сгенерируйте код для функций точки входа с помощью codegen (MATLAB Coder). Для каждого аргумента функции точки входа используйте -args флаги для задания представлений переменных в кодере. Задайте выход MEX-функции myIncrLearn_mex.

 codegen -o myIncrLearn_mex ...
 myInitialModelIncrLearn ... 
 myUpdateMetricsIncrLearn -args {IncrMdlOutputType,predictorData,responseData} ...
 myFitIncrLearn -args {IncrMdlOutputType,predictorData,responseData} ...
 myPredictIncrLearn –args {IncrMdlOutputType,predictorData} -report
Code generation successful: To view the report, open('codegen/mex/myIncrLearn_mex/html/report.mldatx').

Для помощи с отладкой проблем генерации кода просмотрите сгенерированный код C/C + + нажав View report (см. Отчеты генерации кода (MATLAB Coder)).

Проверьте сгенерированный код

Протестируйте MEX-функцию, чтобы убедиться, что сгенерированный код обеспечивает ту же функциональность, что и оригинальный код MATLAB. Чтобы выполнить этот тест, запустите MEX-функцию с помощью тех же входов, которые вы использовали для запуска оригинального кода MATLAB, и затем сравните результаты. Выполнение MEX-функции в MATLAB перед генерацией автономного кода также позволяет вам обнаруживать и исправлять ошибки времени выполнения, которые намного сложнее диагностировать в сгенерированном автономном коде. Для получения дополнительной информации смотрите Зачем Тестировать MEX-функции в MATLAB? (MATLAB Coder).

Выполните инкрементальное обучение с помощью сгенерированных MEX-функций и непосредственно с помощью функций объекта. Задайте пакет

% Preallocation
numObsPerChunk = 50;
nchunk = floor(n/numObsPerChunk);
ce = array2table(zeros(nchunk,2),'VariableNames',["Cumulative" "Window"]);
hinge = ce;
ceCG = ce;
hingeCG = ce;
IncrementalMdlCG = myIncrLearn_mex('myInitialModelIncrLearn');
scores = zeros(n,2);
scoresCG = zeros(n,2);

% Incremental fitting
for j = 1:nchunk
    ibegin = min(n,numObsPerChunk*(j-1) + 1);
    iend   = min(n,numObsPerChunk*j);
    idx = ibegin:iend;

    IncrementalMdl = updateMetrics(IncrementalMdl,X(idx,:),Y(idx));
    ce{j,:} = IncrementalMdl.Metrics{"ClassificationError",:};
    hinge{j,:} = IncrementalMdl.Metrics{"HingeLoss",:};
    IncrementalMdlCG = myIncrLearn_mex('myUpdateMetricsIncrLearn',IncrementalMdlCG,...
        X(idx,:),Y(idx));
    ceCG{j,:} = IncrementalMdlCG.Metrics{"ClassificationError",:};
    hingeCG{j,:} = IncrementalMdlCG.Metrics{"HingeLoss",:};

    IncrementalMdl = fit(IncrementalMdl,X(idx,:),Y(idx));
    IncrementalMdlCG = myIncrLearn_mex('myFitIncrLearn',IncrementalMdlCG,X(idx,:),Y(idx));
    
    [~,scores(idx,:)] = predict(IncrementalMdl,X(idx,:));
    [~,scoresCG(idx,:)] = myIncrLearn_mex('myPredictIncrLearn',IncrementalMdlCG,X(idx,:));
end

Сравните совокупные метрики и счета для классификации Idle возвращенные функциями объекта и MEX-функциями.

idx = all(~isnan(ce.Variables),2);
areCEsEqual = norm(ce.Cumulative(idx) - ceCG.Cumulative(idx))
areCEsEqual = 8.9904e-18
idx = all(~isnan(hinge.Variables),2);
areHingeLossesEqual = norm(hinge.Cumulative(idx) - hingeCG.Cumulative(idx))
areHingeLossesEqual = 8.4704e-17
areScoresEqual = norm(scores(:,1) - scoresCG(:,1))
areScoresEqual = 8.8356e-13

Различия между возвращенными количествами незначительны.